Commit | Line | Data |
---|---|---|
967dd82f FF |
1 | /* |
2 | * B53 register access through Switch Register Access Bridge Registers | |
3 | * | |
4 | * Copyright (C) 2013 Hauke Mehrtens <hauke@hauke-m.de> | |
5 | * | |
6 | * Permission to use, copy, modify, and/or distribute this software for any | |
7 | * purpose with or without fee is hereby granted, provided that the above | |
8 | * copyright notice and this permission notice appear in all copies. | |
9 | * | |
10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
17 | */ | |
18 | ||
19 | #include <linux/kernel.h> | |
20 | #include <linux/module.h> | |
21 | #include <linux/delay.h> | |
22 | #include <linux/platform_device.h> | |
23 | #include <linux/platform_data/b53.h> | |
fefae690 | 24 | #include <linux/of.h> |
967dd82f FF |
25 | |
26 | #include "b53_priv.h" | |
27 | ||
28 | /* command and status register of the SRAB */ | |
29 | #define B53_SRAB_CMDSTAT 0x2c | |
30 | #define B53_SRAB_CMDSTAT_RST BIT(2) | |
31 | #define B53_SRAB_CMDSTAT_WRITE BIT(1) | |
32 | #define B53_SRAB_CMDSTAT_GORDYN BIT(0) | |
33 | #define B53_SRAB_CMDSTAT_PAGE 24 | |
34 | #define B53_SRAB_CMDSTAT_REG 16 | |
35 | ||
36 | /* high order word of write data to switch registe */ | |
37 | #define B53_SRAB_WD_H 0x30 | |
38 | ||
39 | /* low order word of write data to switch registe */ | |
40 | #define B53_SRAB_WD_L 0x34 | |
41 | ||
42 | /* high order word of read data from switch register */ | |
43 | #define B53_SRAB_RD_H 0x38 | |
44 | ||
45 | /* low order word of read data from switch register */ | |
46 | #define B53_SRAB_RD_L 0x3c | |
47 | ||
48 | /* command and status register of the SRAB */ | |
49 | #define B53_SRAB_CTRLS 0x40 | |
50 | #define B53_SRAB_CTRLS_RCAREQ BIT(3) | |
51 | #define B53_SRAB_CTRLS_RCAGNT BIT(4) | |
52 | #define B53_SRAB_CTRLS_SW_INIT_DONE BIT(6) | |
53 | ||
54 | /* the register captures interrupt pulses from the switch */ | |
55 | #define B53_SRAB_INTR 0x44 | |
56 | #define B53_SRAB_INTR_P(x) BIT(x) | |
57 | #define B53_SRAB_SWITCH_PHY BIT(8) | |
58 | #define B53_SRAB_1588_SYNC BIT(9) | |
59 | #define B53_SRAB_IMP1_SLEEP_TIMER BIT(10) | |
60 | #define B53_SRAB_P7_SLEEP_TIMER BIT(11) | |
61 | #define B53_SRAB_IMP0_SLEEP_TIMER BIT(12) | |
62 | ||
63 | struct b53_srab_priv { | |
64 | void __iomem *regs; | |
65 | }; | |
66 | ||
67 | static int b53_srab_request_grant(struct b53_device *dev) | |
68 | { | |
69 | struct b53_srab_priv *priv = dev->priv; | |
70 | u8 __iomem *regs = priv->regs; | |
71 | u32 ctrls; | |
72 | int i; | |
73 | ||
74 | ctrls = readl(regs + B53_SRAB_CTRLS); | |
75 | ctrls |= B53_SRAB_CTRLS_RCAREQ; | |
76 | writel(ctrls, regs + B53_SRAB_CTRLS); | |
77 | ||
78 | for (i = 0; i < 20; i++) { | |
79 | ctrls = readl(regs + B53_SRAB_CTRLS); | |
80 | if (ctrls & B53_SRAB_CTRLS_RCAGNT) | |
81 | break; | |
82 | usleep_range(10, 100); | |
83 | } | |
84 | if (WARN_ON(i == 5)) | |
85 | return -EIO; | |
86 | ||
87 | return 0; | |
88 | } | |
89 | ||
90 | static void b53_srab_release_grant(struct b53_device *dev) | |
91 | { | |
92 | struct b53_srab_priv *priv = dev->priv; | |
93 | u8 __iomem *regs = priv->regs; | |
94 | u32 ctrls; | |
95 | ||
96 | ctrls = readl(regs + B53_SRAB_CTRLS); | |
97 | ctrls &= ~B53_SRAB_CTRLS_RCAREQ; | |
98 | writel(ctrls, regs + B53_SRAB_CTRLS); | |
99 | } | |
100 | ||
101 | static int b53_srab_op(struct b53_device *dev, u8 page, u8 reg, u32 op) | |
102 | { | |
103 | struct b53_srab_priv *priv = dev->priv; | |
104 | u8 __iomem *regs = priv->regs; | |
105 | int i; | |
106 | u32 cmdstat; | |
107 | ||
108 | /* set register address */ | |
109 | cmdstat = (page << B53_SRAB_CMDSTAT_PAGE) | | |
110 | (reg << B53_SRAB_CMDSTAT_REG) | | |
111 | B53_SRAB_CMDSTAT_GORDYN | | |
112 | op; | |
113 | writel(cmdstat, regs + B53_SRAB_CMDSTAT); | |
114 | ||
115 | /* check if operation completed */ | |
116 | for (i = 0; i < 5; ++i) { | |
117 | cmdstat = readl(regs + B53_SRAB_CMDSTAT); | |
118 | if (!(cmdstat & B53_SRAB_CMDSTAT_GORDYN)) | |
119 | break; | |
120 | usleep_range(10, 100); | |
121 | } | |
122 | ||
123 | if (WARN_ON(i == 5)) | |
124 | return -EIO; | |
125 | ||
126 | return 0; | |
127 | } | |
128 | ||
129 | static int b53_srab_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val) | |
130 | { | |
131 | struct b53_srab_priv *priv = dev->priv; | |
132 | u8 __iomem *regs = priv->regs; | |
133 | int ret = 0; | |
134 | ||
135 | ret = b53_srab_request_grant(dev); | |
136 | if (ret) | |
137 | goto err; | |
138 | ||
139 | ret = b53_srab_op(dev, page, reg, 0); | |
140 | if (ret) | |
141 | goto err; | |
142 | ||
143 | *val = readl(regs + B53_SRAB_RD_L) & 0xff; | |
144 | ||
145 | err: | |
146 | b53_srab_release_grant(dev); | |
147 | ||
148 | return ret; | |
149 | } | |
150 | ||
151 | static int b53_srab_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val) | |
152 | { | |
153 | struct b53_srab_priv *priv = dev->priv; | |
154 | u8 __iomem *regs = priv->regs; | |
155 | int ret = 0; | |
156 | ||
157 | ret = b53_srab_request_grant(dev); | |
158 | if (ret) | |
159 | goto err; | |
160 | ||
161 | ret = b53_srab_op(dev, page, reg, 0); | |
162 | if (ret) | |
163 | goto err; | |
164 | ||
165 | *val = readl(regs + B53_SRAB_RD_L) & 0xffff; | |
166 | ||
167 | err: | |
168 | b53_srab_release_grant(dev); | |
169 | ||
170 | return ret; | |
171 | } | |
172 | ||
173 | static int b53_srab_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val) | |
174 | { | |
175 | struct b53_srab_priv *priv = dev->priv; | |
176 | u8 __iomem *regs = priv->regs; | |
177 | int ret = 0; | |
178 | ||
179 | ret = b53_srab_request_grant(dev); | |
180 | if (ret) | |
181 | goto err; | |
182 | ||
183 | ret = b53_srab_op(dev, page, reg, 0); | |
184 | if (ret) | |
185 | goto err; | |
186 | ||
187 | *val = readl(regs + B53_SRAB_RD_L); | |
188 | ||
189 | err: | |
190 | b53_srab_release_grant(dev); | |
191 | ||
192 | return ret; | |
193 | } | |
194 | ||
195 | static int b53_srab_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val) | |
196 | { | |
197 | struct b53_srab_priv *priv = dev->priv; | |
198 | u8 __iomem *regs = priv->regs; | |
199 | int ret = 0; | |
200 | ||
201 | ret = b53_srab_request_grant(dev); | |
202 | if (ret) | |
203 | goto err; | |
204 | ||
205 | ret = b53_srab_op(dev, page, reg, 0); | |
206 | if (ret) | |
207 | goto err; | |
208 | ||
209 | *val = readl(regs + B53_SRAB_RD_L); | |
210 | *val += ((u64)readl(regs + B53_SRAB_RD_H) & 0xffff) << 32; | |
211 | ||
212 | err: | |
213 | b53_srab_release_grant(dev); | |
214 | ||
215 | return ret; | |
216 | } | |
217 | ||
218 | static int b53_srab_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val) | |
219 | { | |
220 | struct b53_srab_priv *priv = dev->priv; | |
221 | u8 __iomem *regs = priv->regs; | |
222 | int ret = 0; | |
223 | ||
224 | ret = b53_srab_request_grant(dev); | |
225 | if (ret) | |
226 | goto err; | |
227 | ||
228 | ret = b53_srab_op(dev, page, reg, 0); | |
229 | if (ret) | |
230 | goto err; | |
231 | ||
232 | *val = readl(regs + B53_SRAB_RD_L); | |
233 | *val += (u64)readl(regs + B53_SRAB_RD_H) << 32; | |
234 | ||
235 | err: | |
236 | b53_srab_release_grant(dev); | |
237 | ||
238 | return ret; | |
239 | } | |
240 | ||
241 | static int b53_srab_write8(struct b53_device *dev, u8 page, u8 reg, u8 value) | |
242 | { | |
243 | struct b53_srab_priv *priv = dev->priv; | |
244 | u8 __iomem *regs = priv->regs; | |
245 | int ret = 0; | |
246 | ||
247 | ret = b53_srab_request_grant(dev); | |
248 | if (ret) | |
249 | goto err; | |
250 | ||
251 | writel(value, regs + B53_SRAB_WD_L); | |
252 | ||
253 | ret = b53_srab_op(dev, page, reg, B53_SRAB_CMDSTAT_WRITE); | |
254 | ||
255 | err: | |
256 | b53_srab_release_grant(dev); | |
257 | ||
258 | return ret; | |
259 | } | |
260 | ||
261 | static int b53_srab_write16(struct b53_device *dev, u8 page, u8 reg, | |
262 | u16 value) | |
263 | { | |
264 | struct b53_srab_priv *priv = dev->priv; | |
265 | u8 __iomem *regs = priv->regs; | |
266 | int ret = 0; | |
267 | ||
268 | ret = b53_srab_request_grant(dev); | |
269 | if (ret) | |
270 | goto err; | |
271 | ||
272 | writel(value, regs + B53_SRAB_WD_L); | |
273 | ||
274 | ret = b53_srab_op(dev, page, reg, B53_SRAB_CMDSTAT_WRITE); | |
275 | ||
276 | err: | |
277 | b53_srab_release_grant(dev); | |
278 | ||
279 | return ret; | |
280 | } | |
281 | ||
282 | static int b53_srab_write32(struct b53_device *dev, u8 page, u8 reg, | |
283 | u32 value) | |
284 | { | |
285 | struct b53_srab_priv *priv = dev->priv; | |
286 | u8 __iomem *regs = priv->regs; | |
287 | int ret = 0; | |
288 | ||
289 | ret = b53_srab_request_grant(dev); | |
290 | if (ret) | |
291 | goto err; | |
292 | ||
293 | writel(value, regs + B53_SRAB_WD_L); | |
294 | ||
295 | ret = b53_srab_op(dev, page, reg, B53_SRAB_CMDSTAT_WRITE); | |
296 | ||
297 | err: | |
298 | b53_srab_release_grant(dev); | |
299 | ||
300 | return ret; | |
301 | } | |
302 | ||
303 | static int b53_srab_write48(struct b53_device *dev, u8 page, u8 reg, | |
304 | u64 value) | |
305 | { | |
306 | struct b53_srab_priv *priv = dev->priv; | |
307 | u8 __iomem *regs = priv->regs; | |
308 | int ret = 0; | |
309 | ||
310 | ret = b53_srab_request_grant(dev); | |
311 | if (ret) | |
312 | goto err; | |
313 | ||
314 | writel((u32)value, regs + B53_SRAB_WD_L); | |
315 | writel((u16)(value >> 32), regs + B53_SRAB_WD_H); | |
316 | ||
317 | ret = b53_srab_op(dev, page, reg, B53_SRAB_CMDSTAT_WRITE); | |
318 | ||
319 | err: | |
320 | b53_srab_release_grant(dev); | |
321 | ||
322 | return ret; | |
323 | } | |
324 | ||
325 | static int b53_srab_write64(struct b53_device *dev, u8 page, u8 reg, | |
326 | u64 value) | |
327 | { | |
328 | struct b53_srab_priv *priv = dev->priv; | |
329 | u8 __iomem *regs = priv->regs; | |
330 | int ret = 0; | |
331 | ||
332 | ret = b53_srab_request_grant(dev); | |
333 | if (ret) | |
334 | goto err; | |
335 | ||
336 | writel((u32)value, regs + B53_SRAB_WD_L); | |
337 | writel((u32)(value >> 32), regs + B53_SRAB_WD_H); | |
338 | ||
339 | ret = b53_srab_op(dev, page, reg, B53_SRAB_CMDSTAT_WRITE); | |
340 | ||
341 | err: | |
342 | b53_srab_release_grant(dev); | |
343 | ||
344 | return ret; | |
345 | } | |
346 | ||
347 | static struct b53_io_ops b53_srab_ops = { | |
348 | .read8 = b53_srab_read8, | |
349 | .read16 = b53_srab_read16, | |
350 | .read32 = b53_srab_read32, | |
351 | .read48 = b53_srab_read48, | |
352 | .read64 = b53_srab_read64, | |
353 | .write8 = b53_srab_write8, | |
354 | .write16 = b53_srab_write16, | |
355 | .write32 = b53_srab_write32, | |
356 | .write48 = b53_srab_write48, | |
357 | .write64 = b53_srab_write64, | |
358 | }; | |
359 | ||
fefae690 FF |
360 | static const struct of_device_id b53_srab_of_match[] = { |
361 | { .compatible = "brcm,bcm53010-srab" }, | |
362 | { .compatible = "brcm,bcm53011-srab" }, | |
363 | { .compatible = "brcm,bcm53012-srab" }, | |
364 | { .compatible = "brcm,bcm53018-srab" }, | |
365 | { .compatible = "brcm,bcm53019-srab" }, | |
366 | { .compatible = "brcm,bcm5301x-srab" }, | |
367 | { /* sentinel */ }, | |
368 | }; | |
369 | MODULE_DEVICE_TABLE(of, b53_srab_of_match); | |
370 | ||
967dd82f FF |
371 | static int b53_srab_probe(struct platform_device *pdev) |
372 | { | |
fefae690 FF |
373 | struct b53_platform_data *pdata = pdev->dev.platform_data; |
374 | struct device_node *dn = pdev->dev.of_node; | |
375 | const struct of_device_id *of_id = NULL; | |
967dd82f FF |
376 | struct b53_srab_priv *priv; |
377 | struct b53_device *dev; | |
378 | struct resource *r; | |
379 | ||
fefae690 FF |
380 | if (dn) |
381 | of_id = of_match_node(b53_srab_of_match, dn); | |
382 | ||
383 | if (of_id) { | |
384 | pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); | |
385 | if (!pdata) | |
386 | return -ENOMEM; | |
387 | ||
388 | pdata->chip_id = (u32)of_id->data; | |
389 | } | |
390 | ||
967dd82f FF |
391 | priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); |
392 | if (!priv) | |
393 | return -ENOMEM; | |
394 | ||
395 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
396 | priv->regs = devm_ioremap_resource(&pdev->dev, r); | |
397 | if (IS_ERR(priv->regs)) | |
398 | return -ENOMEM; | |
399 | ||
400 | dev = b53_switch_alloc(&pdev->dev, &b53_srab_ops, priv); | |
401 | if (!dev) | |
402 | return -ENOMEM; | |
403 | ||
fefae690 FF |
404 | if (pdata) |
405 | dev->pdata = pdata; | |
406 | ||
967dd82f FF |
407 | platform_set_drvdata(pdev, dev); |
408 | ||
409 | return b53_switch_register(dev); | |
410 | } | |
411 | ||
412 | static int b53_srab_remove(struct platform_device *pdev) | |
413 | { | |
414 | struct b53_device *dev = platform_get_drvdata(pdev); | |
415 | ||
416 | if (dev) | |
417 | b53_switch_remove(dev); | |
418 | ||
419 | return 0; | |
420 | } | |
421 | ||
967dd82f FF |
422 | static struct platform_driver b53_srab_driver = { |
423 | .probe = b53_srab_probe, | |
424 | .remove = b53_srab_remove, | |
425 | .driver = { | |
426 | .name = "b53-srab-switch", | |
427 | .of_match_table = b53_srab_of_match, | |
428 | }, | |
429 | }; | |
430 | ||
431 | module_platform_driver(b53_srab_driver); | |
432 | MODULE_AUTHOR("Hauke Mehrtens <hauke@hauke-m.de>"); | |
433 | MODULE_DESCRIPTION("B53 Switch Register Access Bridge Registers (SRAB) access driver"); | |
434 | MODULE_LICENSE("Dual BSD/GPL"); |