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