diff options
author | Kim Phillips <kim.phillips@freescale.com> | 2007-04-13 02:26:03 -0400 |
---|---|---|
committer | Jeff Garzik <jeff@garzik.org> | 2007-04-28 11:01:04 -0400 |
commit | 728de4c927a3544b6d3da331b634035d4c75ca17 (patch) | |
tree | 5f05e0b318de9c1d517bd7bbca5964249c7cc885 /drivers/net/ucc_geth_mii.c | |
parent | a999589ccaae16472531e0616f23826ad097aa40 (diff) |
ucc_geth: migrate ucc_geth to phylib
migrate ucc_geth to use the common phylib code.
There are several side effects from doing this:
o deprecate 'interface' property specification present
in some old device tree source files in
favour of a split 'max-speed' and 'interface-type'
description to appropriately match definitions
in include/linux/phy.h. Note that 'interface' property
is still honoured if max-speed or interface-type
are not present (backward compatible).
o compile-time CONFIG_UGETH_HAS_GIGA is eliminated
in favour of probe time speed derivation logic.
o adjust_link streamlined to only operate on maccfg2
and upsmr.r10m, instead of reapplying static initial
values related to the interface-type.
o Addition of UEC MDIO of_platform driver requires
platform code add 'mdio' type to id list
prior to calling of_platform_bus_probe (separate patch).
o ucc_struct_init introduced to reduce ucc_geth_startup
complexity.
Signed-off-by: Li Yang <leoli@freescale.com>
Signed-off-by: Kim Phillips <kim.phillips@freescale.com>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
Diffstat (limited to 'drivers/net/ucc_geth_mii.c')
-rw-r--r-- | drivers/net/ucc_geth_mii.c | 279 |
1 files changed, 279 insertions, 0 deletions
diff --git a/drivers/net/ucc_geth_mii.c b/drivers/net/ucc_geth_mii.c new file mode 100644 index 000000000000..73b5a538e8f4 --- /dev/null +++ b/drivers/net/ucc_geth_mii.c | |||
@@ -0,0 +1,279 @@ | |||
1 | /* | ||
2 | * drivers/net/ucc_geth_mii.c | ||
3 | * | ||
4 | * Gianfar Ethernet Driver -- MIIM bus implementation | ||
5 | * Provides Bus interface for MIIM regs | ||
6 | * | ||
7 | * Author: Li Yang | ||
8 | * | ||
9 | * Copyright (c) 2002-2004 Freescale Semiconductor, Inc. | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify it | ||
12 | * under the terms of the GNU General Public License as published by the | ||
13 | * Free Software Foundation; either version 2 of the License, or (at your | ||
14 | * option) any later version. | ||
15 | * | ||
16 | */ | ||
17 | |||
18 | #include <linux/kernel.h> | ||
19 | #include <linux/sched.h> | ||
20 | #include <linux/string.h> | ||
21 | #include <linux/errno.h> | ||
22 | #include <linux/unistd.h> | ||
23 | #include <linux/slab.h> | ||
24 | #include <linux/interrupt.h> | ||
25 | #include <linux/init.h> | ||
26 | #include <linux/delay.h> | ||
27 | #include <linux/netdevice.h> | ||
28 | #include <linux/etherdevice.h> | ||
29 | #include <linux/skbuff.h> | ||
30 | #include <linux/spinlock.h> | ||
31 | #include <linux/mm.h> | ||
32 | #include <linux/module.h> | ||
33 | #include <linux/platform_device.h> | ||
34 | #include <asm/ocp.h> | ||
35 | #include <linux/crc32.h> | ||
36 | #include <linux/mii.h> | ||
37 | #include <linux/phy.h> | ||
38 | #include <linux/fsl_devices.h> | ||
39 | |||
40 | #include <asm/of_platform.h> | ||
41 | #include <asm/io.h> | ||
42 | #include <asm/irq.h> | ||
43 | #include <asm/uaccess.h> | ||
44 | #include <asm/ucc.h> | ||
45 | |||
46 | #include "ucc_geth_mii.h" | ||
47 | #include "ucc_geth.h" | ||
48 | |||
49 | #define DEBUG | ||
50 | #ifdef DEBUG | ||
51 | #define vdbg(format, arg...) printk(KERN_DEBUG , format "\n" , ## arg) | ||
52 | #else | ||
53 | #define vdbg(format, arg...) do {} while(0) | ||
54 | #endif | ||
55 | |||
56 | #define DRV_DESC "QE UCC Ethernet Controller MII Bus" | ||
57 | #define DRV_NAME "fsl-uec_mdio" | ||
58 | |||
59 | /* Write value to the PHY for this device to the register at regnum, */ | ||
60 | /* waiting until the write is done before it returns. All PHY */ | ||
61 | /* configuration has to be done through the master UEC MIIM regs */ | ||
62 | int uec_mdio_write(struct mii_bus *bus, int mii_id, int regnum, u16 value) | ||
63 | { | ||
64 | struct ucc_mii_mng __iomem *regs = (void __iomem *)bus->priv; | ||
65 | |||
66 | /* Setting up the MII Mangement Address Register */ | ||
67 | out_be32(®s->miimadd, | ||
68 | (mii_id << MIIMADD_PHY_ADDRESS_SHIFT) | regnum); | ||
69 | |||
70 | /* Setting up the MII Mangement Control Register with the value */ | ||
71 | out_be32(®s->miimcon, value); | ||
72 | |||
73 | /* Wait till MII management write is complete */ | ||
74 | while ((in_be32(®s->miimind)) & MIIMIND_BUSY) | ||
75 | cpu_relax(); | ||
76 | |||
77 | return 0; | ||
78 | } | ||
79 | |||
80 | /* Reads from register regnum in the PHY for device dev, */ | ||
81 | /* returning the value. Clears miimcom first. All PHY */ | ||
82 | /* configuration has to be done through the TSEC1 MIIM regs */ | ||
83 | int uec_mdio_read(struct mii_bus *bus, int mii_id, int regnum) | ||
84 | { | ||
85 | struct ucc_mii_mng __iomem *regs = (void __iomem *)bus->priv; | ||
86 | u16 value; | ||
87 | |||
88 | /* Setting up the MII Mangement Address Register */ | ||
89 | out_be32(®s->miimadd, | ||
90 | (mii_id << MIIMADD_PHY_ADDRESS_SHIFT) | regnum); | ||
91 | |||
92 | /* Clear miimcom, perform an MII management read cycle */ | ||
93 | out_be32(®s->miimcom, 0); | ||
94 | out_be32(®s->miimcom, MIIMCOM_READ_CYCLE); | ||
95 | |||
96 | /* Wait till MII management write is complete */ | ||
97 | while ((in_be32(®s->miimind)) & (MIIMIND_BUSY | MIIMIND_NOT_VALID)) | ||
98 | cpu_relax(); | ||
99 | |||
100 | /* Read MII management status */ | ||
101 | value = in_be32(®s->miimstat); | ||
102 | |||
103 | return value; | ||
104 | } | ||
105 | |||
106 | /* Reset the MIIM registers, and wait for the bus to free */ | ||
107 | int uec_mdio_reset(struct mii_bus *bus) | ||
108 | { | ||
109 | struct ucc_mii_mng __iomem *regs = (void __iomem *)bus->priv; | ||
110 | unsigned int timeout = PHY_INIT_TIMEOUT; | ||
111 | |||
112 | spin_lock_bh(&bus->mdio_lock); | ||
113 | |||
114 | /* Reset the management interface */ | ||
115 | out_be32(®s->miimcfg, MIIMCFG_RESET_MANAGEMENT); | ||
116 | |||
117 | /* Setup the MII Mgmt clock speed */ | ||
118 | out_be32(®s->miimcfg, MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_112); | ||
119 | |||
120 | /* Wait until the bus is free */ | ||
121 | while ((in_be32(®s->miimind) & MIIMIND_BUSY) && timeout--) | ||
122 | cpu_relax(); | ||
123 | |||
124 | spin_unlock_bh(&bus->mdio_lock); | ||
125 | |||
126 | if (timeout <= 0) { | ||
127 | printk(KERN_ERR "%s: The MII Bus is stuck!\n", bus->name); | ||
128 | return -EBUSY; | ||
129 | } | ||
130 | |||
131 | return 0; | ||
132 | } | ||
133 | |||
134 | static int uec_mdio_probe(struct of_device *ofdev, const struct of_device_id *match) | ||
135 | { | ||
136 | struct device *device = &ofdev->dev; | ||
137 | struct device_node *np = ofdev->node, *tempnp = NULL; | ||
138 | struct device_node *child = NULL; | ||
139 | struct ucc_mii_mng __iomem *regs; | ||
140 | struct mii_bus *new_bus; | ||
141 | struct resource res; | ||
142 | int k, err = 0; | ||
143 | |||
144 | new_bus = kzalloc(sizeof(struct mii_bus), GFP_KERNEL); | ||
145 | |||
146 | if (NULL == new_bus) | ||
147 | return -ENOMEM; | ||
148 | |||
149 | new_bus->name = "UCC Ethernet Controller MII Bus"; | ||
150 | new_bus->read = &uec_mdio_read; | ||
151 | new_bus->write = &uec_mdio_write; | ||
152 | new_bus->reset = &uec_mdio_reset; | ||
153 | |||
154 | memset(&res, 0, sizeof(res)); | ||
155 | |||
156 | err = of_address_to_resource(np, 0, &res); | ||
157 | if (err) | ||
158 | goto reg_map_fail; | ||
159 | |||
160 | new_bus->id = res.start; | ||
161 | |||
162 | new_bus->irq = kmalloc(32 * sizeof(int), GFP_KERNEL); | ||
163 | |||
164 | if (NULL == new_bus->irq) { | ||
165 | err = -ENOMEM; | ||
166 | goto reg_map_fail; | ||
167 | } | ||
168 | |||
169 | for (k = 0; k < 32; k++) | ||
170 | new_bus->irq[k] = PHY_POLL; | ||
171 | |||
172 | while ((child = of_get_next_child(np, child)) != NULL) { | ||
173 | int irq = irq_of_parse_and_map(child, 0); | ||
174 | if (irq != NO_IRQ) { | ||
175 | const u32 *id = get_property(child, "reg", NULL); | ||
176 | new_bus->irq[*id] = irq; | ||
177 | } | ||
178 | } | ||
179 | |||
180 | /* Set the base address */ | ||
181 | regs = ioremap(res.start, sizeof(struct ucc_mii_mng)); | ||
182 | |||
183 | if (NULL == regs) { | ||
184 | err = -ENOMEM; | ||
185 | goto ioremap_fail; | ||
186 | } | ||
187 | |||
188 | new_bus->priv = (void __force *)regs; | ||
189 | |||
190 | new_bus->dev = device; | ||
191 | dev_set_drvdata(device, new_bus); | ||
192 | |||
193 | /* Read MII management master from device tree */ | ||
194 | while ((tempnp = of_find_compatible_node(tempnp, "network", "ucc_geth")) | ||
195 | != NULL) { | ||
196 | struct resource tempres; | ||
197 | |||
198 | err = of_address_to_resource(tempnp, 0, &tempres); | ||
199 | if (err) | ||
200 | goto bus_register_fail; | ||
201 | |||
202 | /* if our mdio regs fall within this UCC regs range */ | ||
203 | if ((res.start >= tempres.start) && | ||
204 | (res.end <= tempres.end)) { | ||
205 | /* set this UCC to be the MII master */ | ||
206 | const u32 *id = get_property(tempnp, "device-id", NULL); | ||
207 | if (id == NULL) | ||
208 | goto bus_register_fail; | ||
209 | |||
210 | ucc_set_qe_mux_mii_mng(*id - 1); | ||
211 | |||
212 | /* assign the TBI an address which won't | ||
213 | * conflict with the PHYs */ | ||
214 | out_be32(®s->utbipar, UTBIPAR_INIT_TBIPA); | ||
215 | break; | ||
216 | } | ||
217 | } | ||
218 | |||
219 | err = mdiobus_register(new_bus); | ||
220 | if (0 != err) { | ||
221 | printk(KERN_ERR "%s: Cannot register as MDIO bus\n", | ||
222 | new_bus->name); | ||
223 | goto bus_register_fail; | ||
224 | } | ||
225 | |||
226 | return 0; | ||
227 | |||
228 | bus_register_fail: | ||
229 | iounmap(regs); | ||
230 | ioremap_fail: | ||
231 | kfree(new_bus->irq); | ||
232 | reg_map_fail: | ||
233 | kfree(new_bus); | ||
234 | |||
235 | return err; | ||
236 | } | ||
237 | |||
238 | int uec_mdio_remove(struct of_device *ofdev) | ||
239 | { | ||
240 | struct device *device = &ofdev->dev; | ||
241 | struct mii_bus *bus = dev_get_drvdata(device); | ||
242 | |||
243 | mdiobus_unregister(bus); | ||
244 | |||
245 | dev_set_drvdata(device, NULL); | ||
246 | |||
247 | iounmap((void __iomem *)bus->priv); | ||
248 | bus->priv = NULL; | ||
249 | kfree(bus); | ||
250 | |||
251 | return 0; | ||
252 | } | ||
253 | |||
254 | static struct of_device_id uec_mdio_match[] = { | ||
255 | { | ||
256 | .type = "mdio", | ||
257 | .compatible = "ucc_geth_phy", | ||
258 | }, | ||
259 | {}, | ||
260 | }; | ||
261 | |||
262 | MODULE_DEVICE_TABLE(of, uec_mdio_match); | ||
263 | |||
264 | static struct of_platform_driver uec_mdio_driver = { | ||
265 | .name = DRV_NAME, | ||
266 | .probe = uec_mdio_probe, | ||
267 | .remove = uec_mdio_remove, | ||
268 | .match_table = uec_mdio_match, | ||
269 | }; | ||
270 | |||
271 | int __init uec_mdio_init(void) | ||
272 | { | ||
273 | return of_register_platform_driver(&uec_mdio_driver); | ||
274 | } | ||
275 | |||
276 | void __exit uec_mdio_exit(void) | ||
277 | { | ||
278 | of_unregister_platform_driver(&uec_mdio_driver); | ||
279 | } | ||