blob: cd9119d335dd109183f48827f45d79dd740ac259 [file] [log] [blame]
Stepan Moskovchenko57884842012-06-07 17:35:49 -07001/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#include <linux/interrupt.h>
14#include <linux/io.h>
15#include <linux/irq.h>
16#include <linux/platform_device.h>
17#include <linux/module.h>
18#include <linux/errno.h>
19#include <linux/proc_fs.h>
20#include <linux/cpu.h>
21
22#define MODULE_NAME "msm_ebi_erp"
23
24#define EBI_ERR_ADDR 0x100
25#define SLV_ERR_APACKET_0 0x108
26#define SLV_ERR_APACKET_1 0x10C
27#define SLV_ERR_CNTL 0x114
28
29#define CNTL_ERR_OCCURRED BIT(4)
30#define CNTL_CLEAR_ERR BIT(8)
31#define CNTL_IRQ_EN BIT(12)
32
33#define AMID_MASK 0xFFFF
34#define ERR_AWRITE BIT(0)
35#define ERR_AOOOWR BIT(1)
36#define ERR_AOOORD BIT(2)
37#define ERR_APTORNS BIT(3)
38#define ERR_ALOCK_SHIFT 6
39#define ERR_ALOCK_MASK 0x3
40#define ERR_ATYPE_SHIFT 8
41#define ERR_ATYPE_MASK 0xF
42#define ERR_ABURST BIT(12)
43#define ERR_ASIZE_SHIFT 13
44#define ERR_ASIZE_MASK 0x7
45#define ERR_ATID_SHIFT 16
46#define ERR_ATID_MASK 0xFF
47#define ERR_ALEN_SHIFT 24
48#define ERR_ALEN_MASK 0xF
49
50#define ERR_CODE_DECODE_ERROR BIT(0)
51#define ERR_CODE_MPU_ERROR BIT(1)
52
53struct msm_ebi_erp_data {
54 void __iomem *base;
55 struct device *dev;
56};
57
58static const char *err_lock_types[4] = {
59 "normal",
60 "exclusive",
61 "locked",
62 "barrier",
63};
64
65static const char *err_sizes[8] = {
66 "byte",
67 "half word",
68 "word",
69 "double word",
70 "reserved_4",
71 "reserved_5",
72 "reserved_6",
73 "reserved_7",
74};
75
76static irqreturn_t msm_ebi_irq(int irq, void *dev_id)
77{
78 struct msm_ebi_erp_data *drvdata = dev_id;
79 void __iomem *base = drvdata->base;
80 unsigned int err_addr, err_apacket0, err_apacket1, err_cntl;
81
82 err_addr = readl_relaxed(base + EBI_ERR_ADDR);
83 err_apacket0 = readl_relaxed(base + SLV_ERR_APACKET_0);
84 err_apacket1 = readl_relaxed(base + SLV_ERR_APACKET_1);
85 err_cntl = readl_relaxed(base + SLV_ERR_CNTL);
86
87 if (!(err_cntl & CNTL_ERR_OCCURRED))
88 return IRQ_NONE;
89
90 pr_alert("EBI error detected!\n");
91 pr_alert("\tDevice = %s\n", dev_name(drvdata->dev));
92 pr_alert("\tERR_ADDR = 0x%08x\n", err_addr);
93 pr_alert("\tAPACKET0 = 0x%08x\n", err_apacket0);
94 pr_alert("\tAPACKET1 = 0x%08x\n", err_apacket1);
95 pr_alert("\tERR_CNTL = 0x%08x\n", err_cntl);
96
97 pr_alert("\tAMID = 0x%08x\n", err_apacket0 & AMID_MASK);
98 pr_alert("\tType = %s, %s, %s\n",
99 err_apacket1 & ERR_AWRITE ? "write" : "read",
100 err_sizes[(err_apacket1 >> ERR_ASIZE_SHIFT) & ERR_ASIZE_MASK],
101 err_apacket1 & ERR_APTORNS ? "non-secure" : "secure");
102
103 pr_alert("\tALOCK = %s\n",
104 err_lock_types[(err_apacket1 >> ERR_ALOCK_SHIFT) & ERR_ALOCK_MASK]);
105
106 pr_alert("\tABURST = %s\n", err_apacket1 & ERR_ABURST ?
107 "increment" : "wrap");
108
109 pr_alert("\tCODE = %s %s\n", err_cntl & ERR_CODE_DECODE_ERROR ?
110 "decode error" : "",
111 err_cntl & ERR_CODE_MPU_ERROR ?
112 "mpu error" : "");
113 err_cntl |= CNTL_CLEAR_ERR;
114 writel_relaxed(err_cntl, base + SLV_ERR_CNTL);
115 mb(); /* Ensure interrupt is cleared before returning */
116 return IRQ_HANDLED;
117}
118
119static int __devinit msm_ebi_erp_probe(struct platform_device *pdev)
120{
121 struct resource *r;
122 struct msm_ebi_erp_data *drvdata;
123 int ret, irq;
124 unsigned int err_cntl;
125
126 drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL);
127 if (!drvdata)
128 return -ENOMEM;
129
130 drvdata->dev = &pdev->dev;
131 platform_set_drvdata(pdev, drvdata);
132
133 r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
134 if (!r)
135 return -EINVAL;
136
137 drvdata->base = devm_ioremap(&pdev->dev, r->start, resource_size(r));
138 if (!drvdata->base)
139 return -ENOMEM;
140
141 irq = platform_get_irq(pdev, 0);
142 if (irq < 0)
143 return irq;
144
145 ret = devm_request_irq(&pdev->dev, irq, msm_ebi_irq, IRQF_TRIGGER_HIGH,
146 dev_name(&pdev->dev), drvdata);
147 if (ret)
148 return ret;
149
150 /* Enable the interrupt */
151 err_cntl = readl_relaxed(drvdata->base + SLV_ERR_CNTL);
152 err_cntl |= CNTL_IRQ_EN;
153 writel_relaxed(err_cntl, drvdata->base + SLV_ERR_CNTL);
154 mb(); /* Ensure interrupt is enabled before returning */
155 return 0;
156}
157
158static int msm_ebi_erp_remove(struct platform_device *pdev)
159{
160 struct msm_ebi_erp_data *drvdata = platform_get_drvdata(pdev);
161 unsigned int err_cntl;
162
163 /* Disable the interrupt */
164 err_cntl = readl_relaxed(drvdata->base + SLV_ERR_CNTL);
165 err_cntl &= ~CNTL_IRQ_EN;
166 writel_relaxed(err_cntl, drvdata->base + SLV_ERR_CNTL);
167 mb(); /* Ensure interrupt is disabled before returning */
168 return 0;
169}
170
171static struct platform_driver msm_ebi_erp_driver = {
172 .probe = msm_ebi_erp_probe,
173 .remove = __devexit_p(msm_ebi_erp_remove),
174 .driver = {
175 .name = MODULE_NAME,
176 .owner = THIS_MODULE,
177 },
178};
179
180static int __init msm_ebi_erp_init(void)
181{
182 return platform_driver_register(&msm_ebi_erp_driver);
183}
184
185static void __exit msm_ebi_erp_exit(void)
186{
187 platform_driver_unregister(&msm_ebi_erp_driver);
188}
189
190
191module_init(msm_ebi_erp_init);
192module_exit(msm_ebi_erp_exit);
193MODULE_LICENSE("GPL v2");
194MODULE_DESCRIPTION("MSM cache error reporting driver");