1 | /* |
2 | * B53 register access through memory mapped registers |
3 | * |
4 | * Copyright (C) 2012-2013 Jonas Gorski <jogo@openwrt.org> |
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/bits.h> |
20 | #include <linux/kernel.h> |
21 | #include <linux/module.h> |
22 | #include <linux/of.h> |
23 | #include <linux/io.h> |
24 | #include <linux/platform_device.h> |
25 | #include <linux/platform_data/b53.h> |
26 | |
27 | #include "b53_priv.h" |
28 | |
29 | struct b53_mmap_priv { |
30 | void __iomem *regs; |
31 | }; |
32 | |
33 | static int b53_mmap_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val) |
34 | { |
35 | struct b53_mmap_priv *priv = dev->priv; |
36 | void __iomem *regs = priv->regs; |
37 | |
38 | *val = readb(addr: regs + (page << 8) + reg); |
39 | |
40 | return 0; |
41 | } |
42 | |
43 | static int b53_mmap_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val) |
44 | { |
45 | struct b53_mmap_priv *priv = dev->priv; |
46 | void __iomem *regs = priv->regs; |
47 | |
48 | if (WARN_ON(reg % 2)) |
49 | return -EINVAL; |
50 | |
51 | if (dev->pdata && dev->pdata->big_endian) |
52 | *val = ioread16be(regs + (page << 8) + reg); |
53 | else |
54 | *val = readw(addr: regs + (page << 8) + reg); |
55 | |
56 | return 0; |
57 | } |
58 | |
59 | static int b53_mmap_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val) |
60 | { |
61 | struct b53_mmap_priv *priv = dev->priv; |
62 | void __iomem *regs = priv->regs; |
63 | |
64 | if (WARN_ON(reg % 4)) |
65 | return -EINVAL; |
66 | |
67 | if (dev->pdata && dev->pdata->big_endian) |
68 | *val = ioread32be(regs + (page << 8) + reg); |
69 | else |
70 | *val = readl(addr: regs + (page << 8) + reg); |
71 | |
72 | return 0; |
73 | } |
74 | |
75 | static int b53_mmap_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val) |
76 | { |
77 | struct b53_mmap_priv *priv = dev->priv; |
78 | void __iomem *regs = priv->regs; |
79 | |
80 | if (WARN_ON(reg % 2)) |
81 | return -EINVAL; |
82 | |
83 | if (reg % 4) { |
84 | u16 lo; |
85 | u32 hi; |
86 | |
87 | if (dev->pdata && dev->pdata->big_endian) { |
88 | lo = ioread16be(regs + (page << 8) + reg); |
89 | hi = ioread32be(regs + (page << 8) + reg + 2); |
90 | } else { |
91 | lo = readw(addr: regs + (page << 8) + reg); |
92 | hi = readl(addr: regs + (page << 8) + reg + 2); |
93 | } |
94 | |
95 | *val = ((u64)hi << 16) | lo; |
96 | } else { |
97 | u32 lo; |
98 | u16 hi; |
99 | |
100 | if (dev->pdata && dev->pdata->big_endian) { |
101 | lo = ioread32be(regs + (page << 8) + reg); |
102 | hi = ioread16be(regs + (page << 8) + reg + 4); |
103 | } else { |
104 | lo = readl(addr: regs + (page << 8) + reg); |
105 | hi = readw(addr: regs + (page << 8) + reg + 4); |
106 | } |
107 | |
108 | *val = ((u64)hi << 32) | lo; |
109 | } |
110 | |
111 | return 0; |
112 | } |
113 | |
114 | static int b53_mmap_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val) |
115 | { |
116 | struct b53_mmap_priv *priv = dev->priv; |
117 | void __iomem *regs = priv->regs; |
118 | u32 hi, lo; |
119 | |
120 | if (WARN_ON(reg % 4)) |
121 | return -EINVAL; |
122 | |
123 | if (dev->pdata && dev->pdata->big_endian) { |
124 | lo = ioread32be(regs + (page << 8) + reg); |
125 | hi = ioread32be(regs + (page << 8) + reg + 4); |
126 | } else { |
127 | lo = readl(addr: regs + (page << 8) + reg); |
128 | hi = readl(addr: regs + (page << 8) + reg + 4); |
129 | } |
130 | |
131 | *val = ((u64)hi << 32) | lo; |
132 | |
133 | return 0; |
134 | } |
135 | |
136 | static int b53_mmap_write8(struct b53_device *dev, u8 page, u8 reg, u8 value) |
137 | { |
138 | struct b53_mmap_priv *priv = dev->priv; |
139 | void __iomem *regs = priv->regs; |
140 | |
141 | writeb(val: value, addr: regs + (page << 8) + reg); |
142 | |
143 | return 0; |
144 | } |
145 | |
146 | static int b53_mmap_write16(struct b53_device *dev, u8 page, u8 reg, |
147 | u16 value) |
148 | { |
149 | struct b53_mmap_priv *priv = dev->priv; |
150 | void __iomem *regs = priv->regs; |
151 | |
152 | if (WARN_ON(reg % 2)) |
153 | return -EINVAL; |
154 | |
155 | if (dev->pdata && dev->pdata->big_endian) |
156 | iowrite16be(value, regs + (page << 8) + reg); |
157 | else |
158 | writew(val: value, addr: regs + (page << 8) + reg); |
159 | |
160 | return 0; |
161 | } |
162 | |
163 | static int b53_mmap_write32(struct b53_device *dev, u8 page, u8 reg, |
164 | u32 value) |
165 | { |
166 | struct b53_mmap_priv *priv = dev->priv; |
167 | void __iomem *regs = priv->regs; |
168 | |
169 | if (WARN_ON(reg % 4)) |
170 | return -EINVAL; |
171 | |
172 | if (dev->pdata && dev->pdata->big_endian) |
173 | iowrite32be(value, regs + (page << 8) + reg); |
174 | else |
175 | writel(val: value, addr: regs + (page << 8) + reg); |
176 | |
177 | return 0; |
178 | } |
179 | |
180 | static int b53_mmap_write48(struct b53_device *dev, u8 page, u8 reg, |
181 | u64 value) |
182 | { |
183 | if (WARN_ON(reg % 2)) |
184 | return -EINVAL; |
185 | |
186 | if (reg % 4) { |
187 | u32 hi = (u32)(value >> 16); |
188 | u16 lo = (u16)value; |
189 | |
190 | b53_mmap_write16(dev, page, reg, value: lo); |
191 | b53_mmap_write32(dev, page, reg: reg + 2, value: hi); |
192 | } else { |
193 | u16 hi = (u16)(value >> 32); |
194 | u32 lo = (u32)value; |
195 | |
196 | b53_mmap_write32(dev, page, reg, value: lo); |
197 | b53_mmap_write16(dev, page, reg: reg + 4, value: hi); |
198 | } |
199 | |
200 | return 0; |
201 | } |
202 | |
203 | static int b53_mmap_write64(struct b53_device *dev, u8 page, u8 reg, |
204 | u64 value) |
205 | { |
206 | u32 hi, lo; |
207 | |
208 | hi = upper_32_bits(value); |
209 | lo = lower_32_bits(value); |
210 | |
211 | if (WARN_ON(reg % 4)) |
212 | return -EINVAL; |
213 | |
214 | b53_mmap_write32(dev, page, reg, value: lo); |
215 | b53_mmap_write32(dev, page, reg: reg + 4, value: hi); |
216 | |
217 | return 0; |
218 | } |
219 | |
220 | static int b53_mmap_phy_read16(struct b53_device *dev, int addr, int reg, |
221 | u16 *value) |
222 | { |
223 | return -EIO; |
224 | } |
225 | |
226 | static int b53_mmap_phy_write16(struct b53_device *dev, int addr, int reg, |
227 | u16 value) |
228 | { |
229 | return -EIO; |
230 | } |
231 | |
232 | static const struct b53_io_ops b53_mmap_ops = { |
233 | .read8 = b53_mmap_read8, |
234 | .read16 = b53_mmap_read16, |
235 | .read32 = b53_mmap_read32, |
236 | .read48 = b53_mmap_read48, |
237 | .read64 = b53_mmap_read64, |
238 | .write8 = b53_mmap_write8, |
239 | .write16 = b53_mmap_write16, |
240 | .write32 = b53_mmap_write32, |
241 | .write48 = b53_mmap_write48, |
242 | .write64 = b53_mmap_write64, |
243 | .phy_read16 = b53_mmap_phy_read16, |
244 | .phy_write16 = b53_mmap_phy_write16, |
245 | }; |
246 | |
247 | static int b53_mmap_probe_of(struct platform_device *pdev, |
248 | struct b53_platform_data **ppdata) |
249 | { |
250 | struct device_node *np = pdev->dev.of_node; |
251 | struct device_node *of_ports, *of_port; |
252 | struct device *dev = &pdev->dev; |
253 | struct b53_platform_data *pdata; |
254 | void __iomem *mem; |
255 | |
256 | mem = devm_platform_ioremap_resource(pdev, index: 0); |
257 | if (IS_ERR(ptr: mem)) |
258 | return PTR_ERR(ptr: mem); |
259 | |
260 | pdata = devm_kzalloc(dev, size: sizeof(struct b53_platform_data), |
261 | GFP_KERNEL); |
262 | if (!pdata) |
263 | return -ENOMEM; |
264 | |
265 | pdata->regs = mem; |
266 | pdata->chip_id = (u32)(unsigned long)device_get_match_data(dev); |
267 | pdata->big_endian = of_property_read_bool(np, propname: "big-endian" ); |
268 | |
269 | of_ports = of_get_child_by_name(node: np, name: "ports" ); |
270 | if (!of_ports) { |
271 | dev_err(dev, "no ports child node found\n" ); |
272 | return -EINVAL; |
273 | } |
274 | |
275 | for_each_available_child_of_node(of_ports, of_port) { |
276 | u32 reg; |
277 | |
278 | if (of_property_read_u32(np: of_port, propname: "reg" , out_value: ®)) |
279 | continue; |
280 | |
281 | if (reg < B53_N_PORTS) |
282 | pdata->enabled_ports |= BIT(reg); |
283 | } |
284 | |
285 | of_node_put(node: of_ports); |
286 | *ppdata = pdata; |
287 | |
288 | return 0; |
289 | } |
290 | |
291 | static int b53_mmap_probe(struct platform_device *pdev) |
292 | { |
293 | struct device_node *np = pdev->dev.of_node; |
294 | struct b53_platform_data *pdata = pdev->dev.platform_data; |
295 | struct b53_mmap_priv *priv; |
296 | struct b53_device *dev; |
297 | int ret; |
298 | |
299 | if (!pdata && np) { |
300 | ret = b53_mmap_probe_of(pdev, ppdata: &pdata); |
301 | if (ret) { |
302 | dev_err(&pdev->dev, "OF probe error\n" ); |
303 | return ret; |
304 | } |
305 | } |
306 | |
307 | if (!pdata) |
308 | return -EINVAL; |
309 | |
310 | priv = devm_kzalloc(dev: &pdev->dev, size: sizeof(*priv), GFP_KERNEL); |
311 | if (!priv) |
312 | return -ENOMEM; |
313 | |
314 | priv->regs = pdata->regs; |
315 | |
316 | dev = b53_switch_alloc(base: &pdev->dev, ops: &b53_mmap_ops, priv); |
317 | if (!dev) |
318 | return -ENOMEM; |
319 | |
320 | dev->pdata = pdata; |
321 | |
322 | platform_set_drvdata(pdev, data: dev); |
323 | |
324 | return b53_switch_register(dev); |
325 | } |
326 | |
327 | static void b53_mmap_remove(struct platform_device *pdev) |
328 | { |
329 | struct b53_device *dev = platform_get_drvdata(pdev); |
330 | |
331 | if (dev) |
332 | b53_switch_remove(dev); |
333 | } |
334 | |
335 | static void b53_mmap_shutdown(struct platform_device *pdev) |
336 | { |
337 | struct b53_device *dev = platform_get_drvdata(pdev); |
338 | |
339 | if (dev) |
340 | b53_switch_shutdown(dev); |
341 | |
342 | platform_set_drvdata(pdev, NULL); |
343 | } |
344 | |
345 | static const struct of_device_id b53_mmap_of_table[] = { |
346 | { |
347 | .compatible = "brcm,bcm3384-switch" , |
348 | .data = (void *)BCM63XX_DEVICE_ID, |
349 | }, { |
350 | .compatible = "brcm,bcm6318-switch" , |
351 | .data = (void *)BCM63268_DEVICE_ID, |
352 | }, { |
353 | .compatible = "brcm,bcm6328-switch" , |
354 | .data = (void *)BCM63XX_DEVICE_ID, |
355 | }, { |
356 | .compatible = "brcm,bcm6362-switch" , |
357 | .data = (void *)BCM63XX_DEVICE_ID, |
358 | }, { |
359 | .compatible = "brcm,bcm6368-switch" , |
360 | .data = (void *)BCM63XX_DEVICE_ID, |
361 | }, { |
362 | .compatible = "brcm,bcm63268-switch" , |
363 | .data = (void *)BCM63268_DEVICE_ID, |
364 | }, { |
365 | .compatible = "brcm,bcm63xx-switch" , |
366 | .data = (void *)BCM63XX_DEVICE_ID, |
367 | }, { /* sentinel */ } |
368 | }; |
369 | MODULE_DEVICE_TABLE(of, b53_mmap_of_table); |
370 | |
371 | static struct platform_driver b53_mmap_driver = { |
372 | .probe = b53_mmap_probe, |
373 | .remove_new = b53_mmap_remove, |
374 | .shutdown = b53_mmap_shutdown, |
375 | .driver = { |
376 | .name = "b53-switch" , |
377 | .of_match_table = b53_mmap_of_table, |
378 | }, |
379 | }; |
380 | |
381 | module_platform_driver(b53_mmap_driver); |
382 | MODULE_AUTHOR("Jonas Gorski <jogo@openwrt.org>" ); |
383 | MODULE_DESCRIPTION("B53 MMAP access driver" ); |
384 | MODULE_LICENSE("Dual BSD/GPL" ); |
385 | |