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
33u16 bman_ip_rev;
34EXPORT_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
61struct bman_hwerr_txt {
62 u32 mask;
63 const char *txt;
64};
65
66static 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 */
78static u32 __iomem *bm_ccsr_start;
79
80static inline u32 bm_ccsr_in(u32 offset)
81{
82 return ioread32be(bm_ccsr_start + offset/4);
83}
84static inline void bm_ccsr_out(u32 offset, u32 val)
85{
86 iowrite32be(val, bm_ccsr_start + offset/4);
87}
88
89static 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 */
101static int __bman_probed;
102static int __bman_requires_cleanup;
103
104
105static 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 */
144static dma_addr_t fbpr_a;
145static size_t fbpr_sz;
146
147static 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
181int bman_is_probed(void)
182{
183 return __bman_probed;
184}
185EXPORT_SYMBOL_GPL(bman_is_probed);
186
187int bman_requires_cleanup(void)
188{
189 return __bman_requires_cleanup;
190}
191
192void bman_done_cleanup(void)
193{
194 __bman_requires_cleanup = 0;
195}
196
197static 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
287static const struct of_device_id fsl_bman_ids[] = {
288 {
289 .compatible = "fsl,bman",
290 },
291 {}
292};
293
294static 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
303builtin_platform_driver(fsl_bman_driver);
304

source code of linux/drivers/soc/fsl/qbman/bman_ccsr.c