Commit | Line | Data |
---|---|---|
1d3bb996 | 1 | /* |
3396c782 | 2 | * drivers/net/ethernet/ibm/emac/rgmii.c |
1d3bb996 DG |
3 | * |
4 | * Driver for PowerPC 4xx on-chip ethernet controller, RGMII bridge support. | |
5 | * | |
17cf803a BH |
6 | * Copyright 2007 Benjamin Herrenschmidt, IBM Corp. |
7 | * <benh@kernel.crashing.org> | |
8 | * | |
9 | * Based on the arch/ppc version of the driver: | |
10 | * | |
1d3bb996 DG |
11 | * Copyright (c) 2004, 2005 Zultys Technologies. |
12 | * Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net> | |
13 | * | |
14 | * Based on original work by | |
15 | * Matt Porter <mporter@kernel.crashing.org> | |
16 | * Copyright 2004 MontaVista Software, Inc. | |
17 | * | |
18 | * This program is free software; you can redistribute it and/or modify it | |
19 | * under the terms of the GNU General Public License as published by the | |
20 | * Free Software Foundation; either version 2 of the License, or (at your | |
21 | * option) any later version. | |
22 | * | |
23 | */ | |
5a0e3ad6 | 24 | #include <linux/slab.h> |
1d3bb996 DG |
25 | #include <linux/kernel.h> |
26 | #include <linux/ethtool.h> | |
5af50730 | 27 | #include <linux/of_address.h> |
1d3bb996 DG |
28 | #include <asm/io.h> |
29 | ||
30 | #include "emac.h" | |
31 | #include "debug.h" | |
32 | ||
33 | // XXX FIXME: Axon seems to support a subset of the RGMII, we | |
34 | // thus need to take that into account and possibly change some | |
35 | // of the bit settings below that don't seem to quite match the | |
36 | // AXON spec | |
37 | ||
38 | /* RGMIIx_FER */ | |
39 | #define RGMII_FER_MASK(idx) (0x7 << ((idx) * 4)) | |
40 | #define RGMII_FER_RTBI(idx) (0x4 << ((idx) * 4)) | |
41 | #define RGMII_FER_RGMII(idx) (0x5 << ((idx) * 4)) | |
42 | #define RGMII_FER_TBI(idx) (0x6 << ((idx) * 4)) | |
43 | #define RGMII_FER_GMII(idx) (0x7 << ((idx) * 4)) | |
004ea683 | 44 | #define RGMII_FER_MII(idx) RGMII_FER_GMII(idx) |
1d3bb996 DG |
45 | |
46 | /* RGMIIx_SSR */ | |
47 | #define RGMII_SSR_MASK(idx) (0x7 << ((idx) * 8)) | |
48 | #define RGMII_SSR_100(idx) (0x2 << ((idx) * 8)) | |
49 | #define RGMII_SSR_1000(idx) (0x4 << ((idx) * 8)) | |
50 | ||
51 | /* RGMII bridge supports only GMII/TBI and RGMII/RTBI PHYs */ | |
52 | static inline int rgmii_valid_mode(int phy_mode) | |
53 | { | |
54 | return phy_mode == PHY_MODE_GMII || | |
004ea683 | 55 | phy_mode == PHY_MODE_MII || |
1d3bb996 DG |
56 | phy_mode == PHY_MODE_RGMII || |
57 | phy_mode == PHY_MODE_TBI || | |
58 | phy_mode == PHY_MODE_RTBI; | |
59 | } | |
60 | ||
61 | static inline const char *rgmii_mode_name(int mode) | |
62 | { | |
63 | switch (mode) { | |
64 | case PHY_MODE_RGMII: | |
65 | return "RGMII"; | |
66 | case PHY_MODE_TBI: | |
67 | return "TBI"; | |
68 | case PHY_MODE_GMII: | |
69 | return "GMII"; | |
004ea683 GE |
70 | case PHY_MODE_MII: |
71 | return "MII"; | |
1d3bb996 DG |
72 | case PHY_MODE_RTBI: |
73 | return "RTBI"; | |
74 | default: | |
75 | BUG(); | |
76 | } | |
77 | } | |
78 | ||
79 | static inline u32 rgmii_mode_mask(int mode, int input) | |
80 | { | |
81 | switch (mode) { | |
82 | case PHY_MODE_RGMII: | |
83 | return RGMII_FER_RGMII(input); | |
84 | case PHY_MODE_TBI: | |
85 | return RGMII_FER_TBI(input); | |
86 | case PHY_MODE_GMII: | |
87 | return RGMII_FER_GMII(input); | |
004ea683 GE |
88 | case PHY_MODE_MII: |
89 | return RGMII_FER_MII(input); | |
1d3bb996 DG |
90 | case PHY_MODE_RTBI: |
91 | return RGMII_FER_RTBI(input); | |
92 | default: | |
93 | BUG(); | |
94 | } | |
95 | } | |
96 | ||
fe17dc1e | 97 | int rgmii_attach(struct platform_device *ofdev, int input, int mode) |
1d3bb996 | 98 | { |
8513fbd8 | 99 | struct rgmii_instance *dev = platform_get_drvdata(ofdev); |
eb4d84f1 | 100 | struct rgmii_regs __iomem *p = dev->base; |
1d3bb996 DG |
101 | |
102 | RGMII_DBG(dev, "attach(%d)" NL, input); | |
103 | ||
104 | /* Check if we need to attach to a RGMII */ | |
105 | if (input < 0 || !rgmii_valid_mode(mode)) { | |
106 | printk(KERN_ERR "%s: unsupported settings !\n", | |
61c7a080 | 107 | ofdev->dev.of_node->full_name); |
1d3bb996 DG |
108 | return -ENODEV; |
109 | } | |
110 | ||
111 | mutex_lock(&dev->lock); | |
112 | ||
113 | /* Enable this input */ | |
114 | out_be32(&p->fer, in_be32(&p->fer) | rgmii_mode_mask(mode, input)); | |
115 | ||
116 | printk(KERN_NOTICE "%s: input %d in %s mode\n", | |
61c7a080 | 117 | ofdev->dev.of_node->full_name, input, rgmii_mode_name(mode)); |
1d3bb996 DG |
118 | |
119 | ++dev->users; | |
120 | ||
121 | mutex_unlock(&dev->lock); | |
122 | ||
123 | return 0; | |
124 | } | |
125 | ||
2dc11581 | 126 | void rgmii_set_speed(struct platform_device *ofdev, int input, int speed) |
1d3bb996 | 127 | { |
8513fbd8 | 128 | struct rgmii_instance *dev = platform_get_drvdata(ofdev); |
eb4d84f1 | 129 | struct rgmii_regs __iomem *p = dev->base; |
1d3bb996 DG |
130 | u32 ssr; |
131 | ||
132 | mutex_lock(&dev->lock); | |
133 | ||
134 | ssr = in_be32(&p->ssr) & ~RGMII_SSR_MASK(input); | |
135 | ||
136 | RGMII_DBG(dev, "speed(%d, %d)" NL, input, speed); | |
137 | ||
138 | if (speed == SPEED_1000) | |
139 | ssr |= RGMII_SSR_1000(input); | |
140 | else if (speed == SPEED_100) | |
141 | ssr |= RGMII_SSR_100(input); | |
142 | ||
143 | out_be32(&p->ssr, ssr); | |
144 | ||
145 | mutex_unlock(&dev->lock); | |
146 | } | |
147 | ||
2dc11581 | 148 | void rgmii_get_mdio(struct platform_device *ofdev, int input) |
1d3bb996 | 149 | { |
8513fbd8 | 150 | struct rgmii_instance *dev = platform_get_drvdata(ofdev); |
eb4d84f1 | 151 | struct rgmii_regs __iomem *p = dev->base; |
1d3bb996 DG |
152 | u32 fer; |
153 | ||
154 | RGMII_DBG2(dev, "get_mdio(%d)" NL, input); | |
155 | ||
1f57877a | 156 | if (!(dev->flags & EMAC_RGMII_FLAG_HAS_MDIO)) |
1d3bb996 DG |
157 | return; |
158 | ||
159 | mutex_lock(&dev->lock); | |
160 | ||
161 | fer = in_be32(&p->fer); | |
162 | fer |= 0x00080000u >> input; | |
163 | out_be32(&p->fer, fer); | |
164 | (void)in_be32(&p->fer); | |
165 | ||
166 | DBG2(dev, " fer = 0x%08x\n", fer); | |
167 | } | |
168 | ||
2dc11581 | 169 | void rgmii_put_mdio(struct platform_device *ofdev, int input) |
1d3bb996 | 170 | { |
8513fbd8 | 171 | struct rgmii_instance *dev = platform_get_drvdata(ofdev); |
eb4d84f1 | 172 | struct rgmii_regs __iomem *p = dev->base; |
1d3bb996 DG |
173 | u32 fer; |
174 | ||
175 | RGMII_DBG2(dev, "put_mdio(%d)" NL, input); | |
176 | ||
1f57877a | 177 | if (!(dev->flags & EMAC_RGMII_FLAG_HAS_MDIO)) |
1d3bb996 DG |
178 | return; |
179 | ||
180 | fer = in_be32(&p->fer); | |
181 | fer &= ~(0x00080000u >> input); | |
182 | out_be32(&p->fer, fer); | |
183 | (void)in_be32(&p->fer); | |
184 | ||
185 | DBG2(dev, " fer = 0x%08x\n", fer); | |
186 | ||
187 | mutex_unlock(&dev->lock); | |
188 | } | |
189 | ||
2dc11581 | 190 | void rgmii_detach(struct platform_device *ofdev, int input) |
1d3bb996 | 191 | { |
8513fbd8 | 192 | struct rgmii_instance *dev = platform_get_drvdata(ofdev); |
0021195c | 193 | struct rgmii_regs __iomem *p; |
1d3bb996 DG |
194 | |
195 | BUG_ON(!dev || dev->users == 0); | |
0021195c JL |
196 | p = dev->base; |
197 | ||
198 | mutex_lock(&dev->lock); | |
1d3bb996 DG |
199 | |
200 | RGMII_DBG(dev, "detach(%d)" NL, input); | |
201 | ||
202 | /* Disable this input */ | |
203 | out_be32(&p->fer, in_be32(&p->fer) & ~RGMII_FER_MASK(input)); | |
204 | ||
205 | --dev->users; | |
206 | ||
207 | mutex_unlock(&dev->lock); | |
208 | } | |
209 | ||
2dc11581 | 210 | int rgmii_get_regs_len(struct platform_device *ofdev) |
1d3bb996 DG |
211 | { |
212 | return sizeof(struct emac_ethtool_regs_subhdr) + | |
213 | sizeof(struct rgmii_regs); | |
214 | } | |
215 | ||
2dc11581 | 216 | void *rgmii_dump_regs(struct platform_device *ofdev, void *buf) |
1d3bb996 | 217 | { |
8513fbd8 | 218 | struct rgmii_instance *dev = platform_get_drvdata(ofdev); |
1d3bb996 DG |
219 | struct emac_ethtool_regs_subhdr *hdr = buf; |
220 | struct rgmii_regs *regs = (struct rgmii_regs *)(hdr + 1); | |
221 | ||
222 | hdr->version = 0; | |
223 | hdr->index = 0; /* for now, are there chips with more than one | |
224 | * rgmii ? if yes, then we'll add a cell_index | |
225 | * like we do for emac | |
226 | */ | |
227 | memcpy_fromio(regs, dev->base, sizeof(struct rgmii_regs)); | |
228 | return regs + 1; | |
229 | } | |
230 | ||
231 | ||
fe17dc1e | 232 | static int rgmii_probe(struct platform_device *ofdev) |
1d3bb996 | 233 | { |
61c7a080 | 234 | struct device_node *np = ofdev->dev.of_node; |
1d3bb996 DG |
235 | struct rgmii_instance *dev; |
236 | struct resource regs; | |
237 | int rc; | |
238 | ||
239 | rc = -ENOMEM; | |
240 | dev = kzalloc(sizeof(struct rgmii_instance), GFP_KERNEL); | |
e404decb | 241 | if (dev == NULL) |
1d3bb996 | 242 | goto err_gone; |
1d3bb996 DG |
243 | |
244 | mutex_init(&dev->lock); | |
245 | dev->ofdev = ofdev; | |
246 | ||
247 | rc = -ENXIO; | |
248 | if (of_address_to_resource(np, 0, ®s)) { | |
249 | printk(KERN_ERR "%s: Can't get registers address\n", | |
250 | np->full_name); | |
251 | goto err_free; | |
252 | } | |
253 | ||
254 | rc = -ENOMEM; | |
eb4d84f1 | 255 | dev->base = (struct rgmii_regs __iomem *)ioremap(regs.start, |
1d3bb996 DG |
256 | sizeof(struct rgmii_regs)); |
257 | if (dev->base == NULL) { | |
258 | printk(KERN_ERR "%s: Can't map device registers!\n", | |
259 | np->full_name); | |
260 | goto err_free; | |
261 | } | |
262 | ||
1f57877a | 263 | /* Check for RGMII flags */ |
61c7a080 | 264 | if (of_get_property(ofdev->dev.of_node, "has-mdio", NULL)) |
1f57877a BH |
265 | dev->flags |= EMAC_RGMII_FLAG_HAS_MDIO; |
266 | ||
267 | /* CAB lacks the right properties, fix this up */ | |
61c7a080 | 268 | if (of_device_is_compatible(ofdev->dev.of_node, "ibm,rgmii-axon")) |
1f57877a | 269 | dev->flags |= EMAC_RGMII_FLAG_HAS_MDIO; |
1d3bb996 DG |
270 | |
271 | DBG2(dev, " Boot FER = 0x%08x, SSR = 0x%08x\n", | |
272 | in_be32(&dev->base->fer), in_be32(&dev->base->ssr)); | |
273 | ||
274 | /* Disable all inputs by default */ | |
275 | out_be32(&dev->base->fer, 0); | |
276 | ||
277 | printk(KERN_INFO | |
1f57877a | 278 | "RGMII %s initialized with%s MDIO support\n", |
61c7a080 | 279 | ofdev->dev.of_node->full_name, |
1f57877a | 280 | (dev->flags & EMAC_RGMII_FLAG_HAS_MDIO) ? "" : "out"); |
1d3bb996 DG |
281 | |
282 | wmb(); | |
8513fbd8 | 283 | platform_set_drvdata(ofdev, dev); |
1d3bb996 DG |
284 | |
285 | return 0; | |
286 | ||
287 | err_free: | |
288 | kfree(dev); | |
289 | err_gone: | |
290 | return rc; | |
291 | } | |
292 | ||
fe17dc1e | 293 | static int rgmii_remove(struct platform_device *ofdev) |
1d3bb996 | 294 | { |
8513fbd8 | 295 | struct rgmii_instance *dev = platform_get_drvdata(ofdev); |
1d3bb996 DG |
296 | |
297 | WARN_ON(dev->users != 0); | |
298 | ||
299 | iounmap(dev->base); | |
300 | kfree(dev); | |
301 | ||
302 | return 0; | |
303 | } | |
304 | ||
305 | static struct of_device_id rgmii_match[] = | |
306 | { | |
307 | { | |
1d3bb996 DG |
308 | .compatible = "ibm,rgmii", |
309 | }, | |
310 | { | |
311 | .type = "emac-rgmii", | |
312 | }, | |
313 | {}, | |
314 | }; | |
315 | ||
74888760 | 316 | static struct platform_driver rgmii_driver = { |
4018294b GL |
317 | .driver = { |
318 | .name = "emac-rgmii", | |
319 | .owner = THIS_MODULE, | |
320 | .of_match_table = rgmii_match, | |
321 | }, | |
1d3bb996 DG |
322 | .probe = rgmii_probe, |
323 | .remove = rgmii_remove, | |
324 | }; | |
325 | ||
326 | int __init rgmii_init(void) | |
327 | { | |
74888760 | 328 | return platform_driver_register(&rgmii_driver); |
1d3bb996 DG |
329 | } |
330 | ||
331 | void rgmii_exit(void) | |
332 | { | |
74888760 | 333 | platform_driver_unregister(&rgmii_driver); |
1d3bb996 | 334 | } |