Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/zohar/linux...
[cascardo/linux.git] / drivers / memory / fsl-corenet-cf.c
1 /*
2  * CoreNet Coherency Fabric error reporting
3  *
4  * Copyright 2014 Freescale Semiconductor Inc.
5  *
6  * This program is free software; you can redistribute  it and/or modify it
7  * under  the terms of  the GNU General  Public License as published by the
8  * Free Software Foundation;  either version 2 of the  License, or (at your
9  * option) any later version.
10  */
11
12 #include <linux/interrupt.h>
13 #include <linux/io.h>
14 #include <linux/irq.h>
15 #include <linux/module.h>
16 #include <linux/of.h>
17 #include <linux/of_address.h>
18 #include <linux/of_device.h>
19 #include <linux/of_irq.h>
20 #include <linux/platform_device.h>
21
22 enum ccf_version {
23         CCF1,
24         CCF2,
25 };
26
27 struct ccf_info {
28         enum ccf_version version;
29         int err_reg_offs;
30 };
31
32 static const struct ccf_info ccf1_info = {
33         .version = CCF1,
34         .err_reg_offs = 0xa00,
35 };
36
37 static const struct ccf_info ccf2_info = {
38         .version = CCF2,
39         .err_reg_offs = 0xe40,
40 };
41
42 static const struct of_device_id ccf_matches[] = {
43         {
44                 .compatible = "fsl,corenet1-cf",
45                 .data = &ccf1_info,
46         },
47         {
48                 .compatible = "fsl,corenet2-cf",
49                 .data = &ccf2_info,
50         },
51         {}
52 };
53
54 struct ccf_err_regs {
55         u32 errdet;             /* 0x00 Error Detect Register */
56         /* 0x04 Error Enable (ccf1)/Disable (ccf2) Register */
57         u32 errdis;
58         /* 0x08 Error Interrupt Enable Register (ccf2 only) */
59         u32 errinten;
60         u32 cecar;              /* 0x0c Error Capture Attribute Register */
61         u32 cecaddrh;           /* 0x10 Error Capture Address High */
62         u32 cecaddrl;           /* 0x14 Error Capture Address Low */
63         u32 cecar2;             /* 0x18 Error Capture Attribute Register 2 */
64 };
65
66 /* LAE/CV also valid for errdis and errinten */
67 #define ERRDET_LAE              (1 << 0)  /* Local Access Error */
68 #define ERRDET_CV               (1 << 1)  /* Coherency Violation */
69 #define ERRDET_CTYPE_SHIFT      26        /* Capture Type (ccf2 only) */
70 #define ERRDET_CTYPE_MASK       (0x1f << ERRDET_CTYPE_SHIFT)
71 #define ERRDET_CAP              (1 << 31) /* Capture Valid (ccf2 only) */
72
73 #define CECAR_VAL               (1 << 0)  /* Valid (ccf1 only) */
74 #define CECAR_UVT               (1 << 15) /* Unavailable target ID (ccf1) */
75 #define CECAR_SRCID_SHIFT_CCF1  24
76 #define CECAR_SRCID_MASK_CCF1   (0xff << CECAR_SRCID_SHIFT_CCF1)
77 #define CECAR_SRCID_SHIFT_CCF2  18
78 #define CECAR_SRCID_MASK_CCF2   (0xff << CECAR_SRCID_SHIFT_CCF2)
79
80 #define CECADDRH_ADDRH          0xff
81
82 struct ccf_private {
83         const struct ccf_info *info;
84         struct device *dev;
85         void __iomem *regs;
86         struct ccf_err_regs __iomem *err_regs;
87 };
88
89 static irqreturn_t ccf_irq(int irq, void *dev_id)
90 {
91         struct ccf_private *ccf = dev_id;
92         static DEFINE_RATELIMIT_STATE(ratelimit, DEFAULT_RATELIMIT_INTERVAL,
93                                       DEFAULT_RATELIMIT_BURST);
94         u32 errdet, cecar, cecar2;
95         u64 addr;
96         u32 src_id;
97         bool uvt = false;
98         bool cap_valid = false;
99
100         errdet = ioread32be(&ccf->err_regs->errdet);
101         cecar = ioread32be(&ccf->err_regs->cecar);
102         cecar2 = ioread32be(&ccf->err_regs->cecar2);
103         addr = ioread32be(&ccf->err_regs->cecaddrl);
104         addr |= ((u64)(ioread32be(&ccf->err_regs->cecaddrh) &
105                        CECADDRH_ADDRH)) << 32;
106
107         if (!__ratelimit(&ratelimit))
108                 goto out;
109
110         switch (ccf->info->version) {
111         case CCF1:
112                 if (cecar & CECAR_VAL) {
113                         if (cecar & CECAR_UVT)
114                                 uvt = true;
115
116                         src_id = (cecar & CECAR_SRCID_MASK_CCF1) >>
117                                  CECAR_SRCID_SHIFT_CCF1;
118                         cap_valid = true;
119                 }
120
121                 break;
122         case CCF2:
123                 if (errdet & ERRDET_CAP) {
124                         src_id = (cecar & CECAR_SRCID_MASK_CCF2) >>
125                                  CECAR_SRCID_SHIFT_CCF2;
126                         cap_valid = true;
127                 }
128
129                 break;
130         }
131
132         dev_crit(ccf->dev, "errdet 0x%08x cecar 0x%08x cecar2 0x%08x\n",
133                  errdet, cecar, cecar2);
134
135         if (errdet & ERRDET_LAE) {
136                 if (uvt)
137                         dev_crit(ccf->dev, "LAW Unavailable Target ID\n");
138                 else
139                         dev_crit(ccf->dev, "Local Access Window Error\n");
140         }
141
142         if (errdet & ERRDET_CV)
143                 dev_crit(ccf->dev, "Coherency Violation\n");
144
145         if (cap_valid) {
146                 dev_crit(ccf->dev, "address 0x%09llx, src id 0x%x\n",
147                          addr, src_id);
148         }
149
150 out:
151         iowrite32be(errdet, &ccf->err_regs->errdet);
152         return errdet ? IRQ_HANDLED : IRQ_NONE;
153 }
154
155 static int ccf_probe(struct platform_device *pdev)
156 {
157         struct ccf_private *ccf;
158         struct resource *r;
159         const struct of_device_id *match;
160         int ret, irq;
161
162         match = of_match_device(ccf_matches, &pdev->dev);
163         if (WARN_ON(!match))
164                 return -ENODEV;
165
166         ccf = devm_kzalloc(&pdev->dev, sizeof(*ccf), GFP_KERNEL);
167         if (!ccf)
168                 return -ENOMEM;
169
170         r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
171         if (!r) {
172                 dev_err(&pdev->dev, "%s: no mem resource\n", __func__);
173                 return -ENXIO;
174         }
175
176         ccf->regs = devm_ioremap_resource(&pdev->dev, r);
177         if (IS_ERR(ccf->regs)) {
178                 dev_err(&pdev->dev, "%s: can't map mem resource\n", __func__);
179                 return PTR_ERR(ccf->regs);
180         }
181
182         ccf->dev = &pdev->dev;
183         ccf->info = match->data;
184         ccf->err_regs = ccf->regs + ccf->info->err_reg_offs;
185
186         dev_set_drvdata(&pdev->dev, ccf);
187
188         irq = platform_get_irq(pdev, 0);
189         if (!irq) {
190                 dev_err(&pdev->dev, "%s: no irq\n", __func__);
191                 return -ENXIO;
192         }
193
194         ret = devm_request_irq(&pdev->dev, irq, ccf_irq, 0, pdev->name, ccf);
195         if (ret) {
196                 dev_err(&pdev->dev, "%s: can't request irq\n", __func__);
197                 return ret;
198         }
199
200         switch (ccf->info->version) {
201         case CCF1:
202                 /* On CCF1 this register enables rather than disables. */
203                 iowrite32be(ERRDET_LAE | ERRDET_CV, &ccf->err_regs->errdis);
204                 break;
205
206         case CCF2:
207                 iowrite32be(0, &ccf->err_regs->errdis);
208                 iowrite32be(ERRDET_LAE | ERRDET_CV, &ccf->err_regs->errinten);
209                 break;
210         }
211
212         return 0;
213 }
214
215 static int ccf_remove(struct platform_device *pdev)
216 {
217         struct ccf_private *ccf = dev_get_drvdata(&pdev->dev);
218
219         switch (ccf->info->version) {
220         case CCF1:
221                 iowrite32be(0, &ccf->err_regs->errdis);
222                 break;
223
224         case CCF2:
225                 /*
226                  * We clear errdis on ccf1 because that's the only way to
227                  * disable interrupts, but on ccf2 there's no need to disable
228                  * detection.
229                  */
230                 iowrite32be(0, &ccf->err_regs->errinten);
231                 break;
232         }
233
234         return 0;
235 }
236
237 static struct platform_driver ccf_driver = {
238         .driver = {
239                 .name = KBUILD_MODNAME,
240                 .of_match_table = ccf_matches,
241         },
242         .probe = ccf_probe,
243         .remove = ccf_remove,
244 };
245
246 module_platform_driver(ccf_driver);
247
248 MODULE_LICENSE("GPL");
249 MODULE_AUTHOR("Freescale Semiconductor");
250 MODULE_DESCRIPTION("Freescale CoreNet Coherency Fabric error reporting");