1 | /* Copyright (c) 2009 - 2016 Freescale Semiconductor, Inc. |
2 | * |
3 | * Redistribution and use in source and binary forms, with or without |
4 | * modification, are permitted provided that the following conditions are met: |
5 | * * Redistributions of source code must retain the above copyright |
6 | * notice, this list of conditions and the following disclaimer. |
7 | * * Redistributions in binary form must reproduce the above copyright |
8 | * notice, this list of conditions and the following disclaimer in the |
9 | * documentation and/or other materials provided with the distribution. |
10 | * * Neither the name of Freescale Semiconductor nor the |
11 | * names of its contributors may be used to endorse or promote products |
12 | * derived from this software without specific prior written permission. |
13 | * |
14 | * ALTERNATIVELY, this software may be distributed under the terms of the |
15 | * GNU General Public License ("GPL") as published by the Free Software |
16 | * Foundation, either version 2 of that License or (at your option) any |
17 | * later version. |
18 | * |
19 | * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY |
20 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
21 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
22 | * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY |
23 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
24 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
25 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
26 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
28 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
29 | */ |
30 | |
31 | #include "bman_priv.h" |
32 | |
33 | u16 bman_ip_rev; |
34 | EXPORT_SYMBOL(bman_ip_rev); |
35 | |
36 | /* Register offsets */ |
37 | #define REG_FBPR_FPC 0x0800 |
38 | #define REG_ECSR 0x0a00 |
39 | #define REG_ECIR 0x0a04 |
40 | #define REG_EADR 0x0a08 |
41 | #define REG_EDATA(n) (0x0a10 + ((n) * 0x04)) |
42 | #define REG_SBEC(n) (0x0a80 + ((n) * 0x04)) |
43 | #define REG_IP_REV_1 0x0bf8 |
44 | #define REG_IP_REV_2 0x0bfc |
45 | #define REG_FBPR_BARE 0x0c00 |
46 | #define REG_FBPR_BAR 0x0c04 |
47 | #define REG_FBPR_AR 0x0c10 |
48 | #define REG_SRCIDR 0x0d04 |
49 | #define REG_LIODNR 0x0d08 |
50 | #define REG_ERR_ISR 0x0e00 |
51 | #define REG_ERR_IER 0x0e04 |
52 | #define REG_ERR_ISDR 0x0e08 |
53 | |
54 | /* Used by all error interrupt registers except 'inhibit' */ |
55 | #define BM_EIRQ_IVCI 0x00000010 /* Invalid Command Verb */ |
56 | #define BM_EIRQ_FLWI 0x00000008 /* FBPR Low Watermark */ |
57 | #define BM_EIRQ_MBEI 0x00000004 /* Multi-bit ECC Error */ |
58 | #define BM_EIRQ_SBEI 0x00000002 /* Single-bit ECC Error */ |
59 | #define BM_EIRQ_BSCN 0x00000001 /* pool State Change Notification */ |
60 | |
61 | struct bman_hwerr_txt { |
62 | u32 mask; |
63 | const char *txt; |
64 | }; |
65 | |
66 | static const struct bman_hwerr_txt bman_hwerr_txts[] = { |
67 | { BM_EIRQ_IVCI, "Invalid Command Verb" }, |
68 | { BM_EIRQ_FLWI, "FBPR Low Watermark" }, |
69 | { BM_EIRQ_MBEI, "Multi-bit ECC Error" }, |
70 | { BM_EIRQ_SBEI, "Single-bit ECC Error" }, |
71 | { BM_EIRQ_BSCN, "Pool State Change Notification" }, |
72 | }; |
73 | |
74 | /* Only trigger low water mark interrupt once only */ |
75 | #define BMAN_ERRS_TO_DISABLE BM_EIRQ_FLWI |
76 | |
77 | /* Pointer to the start of the BMan's CCSR space */ |
78 | static u32 __iomem *bm_ccsr_start; |
79 | |
80 | static inline u32 bm_ccsr_in(u32 offset) |
81 | { |
82 | return ioread32be(bm_ccsr_start + offset/4); |
83 | } |
84 | static inline void bm_ccsr_out(u32 offset, u32 val) |
85 | { |
86 | iowrite32be(val, bm_ccsr_start + offset/4); |
87 | } |
88 | |
89 | static void bm_get_version(u16 *id, u8 *major, u8 *minor) |
90 | { |
91 | u32 v = bm_ccsr_in(REG_IP_REV_1); |
92 | *id = (v >> 16); |
93 | *major = (v >> 8) & 0xff; |
94 | *minor = v & 0xff; |
95 | } |
96 | |
97 | /* signal transactions for FBPRs with higher priority */ |
98 | #define FBPR_AR_RPRIO_HI BIT(30) |
99 | |
100 | /* Track if probe has occurred and if cleanup is required */ |
101 | static int __bman_probed; |
102 | static int __bman_requires_cleanup; |
103 | |
104 | |
105 | static int bm_set_memory(u64 ba, u32 size) |
106 | { |
107 | u32 bar, bare; |
108 | u32 exp = ilog2(size); |
109 | /* choke if size isn't within range */ |
110 | DPAA_ASSERT(size >= 4096 && size <= 1024*1024*1024 && |
111 | is_power_of_2(size)); |
112 | /* choke if '[e]ba' has lower-alignment than 'size' */ |
113 | DPAA_ASSERT(!(ba & (size - 1))); |
114 | |
115 | /* Check to see if BMan has already been initialized */ |
116 | bar = bm_ccsr_in(REG_FBPR_BAR); |
117 | if (bar) { |
118 | /* Maker sure ba == what was programmed) */ |
119 | bare = bm_ccsr_in(REG_FBPR_BARE); |
120 | if (bare != upper_32_bits(ba) || bar != lower_32_bits(ba)) { |
121 | pr_err("Attempted to reinitialize BMan with different BAR, got 0x%llx read BARE=0x%x BAR=0x%x\n" , |
122 | ba, bare, bar); |
123 | return -ENOMEM; |
124 | } |
125 | pr_info("BMan BAR already configured\n" ); |
126 | __bman_requires_cleanup = 1; |
127 | return 1; |
128 | } |
129 | |
130 | bm_ccsr_out(REG_FBPR_BARE, upper_32_bits(ba)); |
131 | bm_ccsr_out(REG_FBPR_BAR, lower_32_bits(ba)); |
132 | bm_ccsr_out(REG_FBPR_AR, val: exp - 1); |
133 | return 0; |
134 | } |
135 | |
136 | /* |
137 | * Location and size of BMan private memory |
138 | * |
139 | * Ideally we would use the DMA API to turn rmem->base into a DMA address |
140 | * (especially if iommu translations ever get involved). Unfortunately, the |
141 | * DMA API currently does not allow mapping anything that is not backed with |
142 | * a struct page. |
143 | */ |
144 | static dma_addr_t fbpr_a; |
145 | static size_t fbpr_sz; |
146 | |
147 | static irqreturn_t bman_isr(int irq, void *ptr) |
148 | { |
149 | u32 isr_val, ier_val, ecsr_val, isr_mask, i; |
150 | struct device *dev = ptr; |
151 | |
152 | ier_val = bm_ccsr_in(REG_ERR_IER); |
153 | isr_val = bm_ccsr_in(REG_ERR_ISR); |
154 | ecsr_val = bm_ccsr_in(REG_ECSR); |
155 | isr_mask = isr_val & ier_val; |
156 | |
157 | if (!isr_mask) |
158 | return IRQ_NONE; |
159 | |
160 | for (i = 0; i < ARRAY_SIZE(bman_hwerr_txts); i++) { |
161 | if (bman_hwerr_txts[i].mask & isr_mask) { |
162 | dev_err_ratelimited(dev, "ErrInt: %s\n" , |
163 | bman_hwerr_txts[i].txt); |
164 | if (bman_hwerr_txts[i].mask & ecsr_val) { |
165 | /* Re-arm error capture registers */ |
166 | bm_ccsr_out(REG_ECSR, val: ecsr_val); |
167 | } |
168 | if (bman_hwerr_txts[i].mask & BMAN_ERRS_TO_DISABLE) { |
169 | dev_dbg(dev, "Disabling error 0x%x\n" , |
170 | bman_hwerr_txts[i].mask); |
171 | ier_val &= ~bman_hwerr_txts[i].mask; |
172 | bm_ccsr_out(REG_ERR_IER, val: ier_val); |
173 | } |
174 | } |
175 | } |
176 | bm_ccsr_out(REG_ERR_ISR, val: isr_val); |
177 | |
178 | return IRQ_HANDLED; |
179 | } |
180 | |
181 | int bman_is_probed(void) |
182 | { |
183 | return __bman_probed; |
184 | } |
185 | EXPORT_SYMBOL_GPL(bman_is_probed); |
186 | |
187 | int bman_requires_cleanup(void) |
188 | { |
189 | return __bman_requires_cleanup; |
190 | } |
191 | |
192 | void bman_done_cleanup(void) |
193 | { |
194 | __bman_requires_cleanup = 0; |
195 | } |
196 | |
197 | static int fsl_bman_probe(struct platform_device *pdev) |
198 | { |
199 | int ret, err_irq; |
200 | struct device *dev = &pdev->dev; |
201 | struct device_node *node = dev->of_node; |
202 | struct resource *res; |
203 | u16 id, bm_pool_cnt; |
204 | u8 major, minor; |
205 | |
206 | __bman_probed = -1; |
207 | |
208 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
209 | if (!res) { |
210 | dev_err(dev, "Can't get %pOF property 'IORESOURCE_MEM'\n" , |
211 | node); |
212 | return -ENXIO; |
213 | } |
214 | bm_ccsr_start = devm_ioremap(dev, offset: res->start, size: resource_size(res)); |
215 | if (!bm_ccsr_start) |
216 | return -ENXIO; |
217 | |
218 | bm_get_version(id: &id, major: &major, minor: &minor); |
219 | if (major == 1 && minor == 0) { |
220 | bman_ip_rev = BMAN_REV10; |
221 | bm_pool_cnt = BM_POOL_MAX; |
222 | } else if (major == 2 && minor == 0) { |
223 | bman_ip_rev = BMAN_REV20; |
224 | bm_pool_cnt = 8; |
225 | } else if (major == 2 && minor == 1) { |
226 | bman_ip_rev = BMAN_REV21; |
227 | bm_pool_cnt = BM_POOL_MAX; |
228 | } else { |
229 | dev_err(dev, "Unknown Bman version:%04x,%02x,%02x\n" , |
230 | id, major, minor); |
231 | return -ENODEV; |
232 | } |
233 | |
234 | ret = qbman_init_private_mem(dev, idx: 0, compat: "fsl,bman-fbpr" , addr: &fbpr_a, size: &fbpr_sz); |
235 | if (ret) { |
236 | dev_err(dev, "qbman_init_private_mem() failed 0x%x\n" , |
237 | ret); |
238 | return -ENODEV; |
239 | } |
240 | |
241 | dev_dbg(dev, "Allocated FBPR 0x%llx 0x%zx\n" , fbpr_a, fbpr_sz); |
242 | |
243 | bm_set_memory(ba: fbpr_a, size: fbpr_sz); |
244 | |
245 | err_irq = platform_get_irq(pdev, 0); |
246 | if (err_irq <= 0) { |
247 | dev_info(dev, "Can't get %pOF IRQ\n" , node); |
248 | return -ENODEV; |
249 | } |
250 | ret = devm_request_irq(dev, irq: err_irq, handler: bman_isr, IRQF_SHARED, devname: "bman-err" , |
251 | dev_id: dev); |
252 | if (ret) { |
253 | dev_err(dev, "devm_request_irq() failed %d for '%pOF'\n" , |
254 | ret, node); |
255 | return ret; |
256 | } |
257 | /* Disable Buffer Pool State Change */ |
258 | bm_ccsr_out(REG_ERR_ISDR, BM_EIRQ_BSCN); |
259 | /* |
260 | * Write-to-clear any stale bits, (eg. starvation being asserted prior |
261 | * to resource allocation during driver init). |
262 | */ |
263 | bm_ccsr_out(REG_ERR_ISR, val: 0xffffffff); |
264 | /* Enable Error Interrupts */ |
265 | bm_ccsr_out(REG_ERR_IER, val: 0xffffffff); |
266 | |
267 | bm_bpalloc = devm_gen_pool_create(dev, min_alloc_order: 0, nid: -1, name: "bman-bpalloc" ); |
268 | if (IS_ERR(ptr: bm_bpalloc)) { |
269 | ret = PTR_ERR(ptr: bm_bpalloc); |
270 | dev_err(dev, "bman-bpalloc pool init failed (%d)\n" , ret); |
271 | return ret; |
272 | } |
273 | |
274 | /* seed BMan resource pool */ |
275 | ret = gen_pool_add(pool: bm_bpalloc, DPAA_GENALLOC_OFF, size: bm_pool_cnt, nid: -1); |
276 | if (ret) { |
277 | dev_err(dev, "Failed to seed BPID range [%d..%d] (%d)\n" , |
278 | 0, bm_pool_cnt - 1, ret); |
279 | return ret; |
280 | } |
281 | |
282 | __bman_probed = 1; |
283 | |
284 | return 0; |
285 | }; |
286 | |
287 | static const struct of_device_id fsl_bman_ids[] = { |
288 | { |
289 | .compatible = "fsl,bman" , |
290 | }, |
291 | {} |
292 | }; |
293 | |
294 | static struct platform_driver fsl_bman_driver = { |
295 | .driver = { |
296 | .name = KBUILD_MODNAME, |
297 | .of_match_table = fsl_bman_ids, |
298 | .suppress_bind_attrs = true, |
299 | }, |
300 | .probe = fsl_bman_probe, |
301 | }; |
302 | |
303 | builtin_platform_driver(fsl_bman_driver); |
304 | |