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