143 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright 2011-2012 Calxeda, Inc.4 */5#include <linux/types.h>6#include <linux/kernel.h>7#include <linux/ctype.h>8#include <linux/edac.h>9#include <linux/interrupt.h>10#include <linux/of.h>11#include <linux/of_device.h>12#include <linux/platform_device.h>13 14#include "edac_module.h"15 16#define SR_CLR_SB_ECC_INTR 0x017#define SR_CLR_DB_ECC_INTR 0x418 19struct hb_l2_drvdata {20 void __iomem *base;21 int sb_irq;22 int db_irq;23};24 25static irqreturn_t highbank_l2_err_handler(int irq, void *dev_id)26{27 struct edac_device_ctl_info *dci = dev_id;28 struct hb_l2_drvdata *drvdata = dci->pvt_info;29 30 if (irq == drvdata->sb_irq) {31 writel(1, drvdata->base + SR_CLR_SB_ECC_INTR);32 edac_device_handle_ce(dci, 0, 0, dci->ctl_name);33 }34 if (irq == drvdata->db_irq) {35 writel(1, drvdata->base + SR_CLR_DB_ECC_INTR);36 edac_device_handle_ue(dci, 0, 0, dci->ctl_name);37 }38 39 return IRQ_HANDLED;40}41 42static const struct of_device_id hb_l2_err_of_match[] = {43 { .compatible = "calxeda,hb-sregs-l2-ecc", },44 {},45};46MODULE_DEVICE_TABLE(of, hb_l2_err_of_match);47 48static int highbank_l2_err_probe(struct platform_device *pdev)49{50 const struct of_device_id *id;51 struct edac_device_ctl_info *dci;52 struct hb_l2_drvdata *drvdata;53 struct resource *r;54 int res = 0;55 56 dci = edac_device_alloc_ctl_info(sizeof(*drvdata), "cpu",57 1, "L", 1, 2, 0);58 if (!dci)59 return -ENOMEM;60 61 drvdata = dci->pvt_info;62 dci->dev = &pdev->dev;63 platform_set_drvdata(pdev, dci);64 65 if (!devres_open_group(&pdev->dev, NULL, GFP_KERNEL))66 return -ENOMEM;67 68 r = platform_get_resource(pdev, IORESOURCE_MEM, 0);69 if (!r) {70 dev_err(&pdev->dev, "Unable to get mem resource\n");71 res = -ENODEV;72 goto err;73 }74 75 if (!devm_request_mem_region(&pdev->dev, r->start,76 resource_size(r), dev_name(&pdev->dev))) {77 dev_err(&pdev->dev, "Error while requesting mem region\n");78 res = -EBUSY;79 goto err;80 }81 82 drvdata->base = devm_ioremap(&pdev->dev, r->start, resource_size(r));83 if (!drvdata->base) {84 dev_err(&pdev->dev, "Unable to map regs\n");85 res = -ENOMEM;86 goto err;87 }88 89 id = of_match_device(hb_l2_err_of_match, &pdev->dev);90 dci->mod_name = pdev->dev.driver->name;91 dci->ctl_name = id ? id->compatible : "unknown";92 dci->dev_name = dev_name(&pdev->dev);93 94 if (edac_device_add_device(dci))95 goto err;96 97 drvdata->db_irq = platform_get_irq(pdev, 0);98 res = devm_request_irq(&pdev->dev, drvdata->db_irq,99 highbank_l2_err_handler,100 0, dev_name(&pdev->dev), dci);101 if (res < 0)102 goto err2;103 104 drvdata->sb_irq = platform_get_irq(pdev, 1);105 res = devm_request_irq(&pdev->dev, drvdata->sb_irq,106 highbank_l2_err_handler,107 0, dev_name(&pdev->dev), dci);108 if (res < 0)109 goto err2;110 111 devres_close_group(&pdev->dev, NULL);112 return 0;113err2:114 edac_device_del_device(&pdev->dev);115err:116 devres_release_group(&pdev->dev, NULL);117 edac_device_free_ctl_info(dci);118 return res;119}120 121static void highbank_l2_err_remove(struct platform_device *pdev)122{123 struct edac_device_ctl_info *dci = platform_get_drvdata(pdev);124 125 edac_device_del_device(&pdev->dev);126 edac_device_free_ctl_info(dci);127}128 129static struct platform_driver highbank_l2_edac_driver = {130 .probe = highbank_l2_err_probe,131 .remove_new = highbank_l2_err_remove,132 .driver = {133 .name = "hb_l2_edac",134 .of_match_table = hb_l2_err_of_match,135 },136};137 138module_platform_driver(highbank_l2_edac_driver);139 140MODULE_LICENSE("GPL v2");141MODULE_AUTHOR("Calxeda, Inc.");142MODULE_DESCRIPTION("EDAC Driver for Calxeda Highbank L2 Cache");143