diff options
Diffstat (limited to 'drivers/net/fs_enet/mii-fec.c')
-rw-r--r-- | drivers/net/fs_enet/mii-fec.c | 251 |
1 files changed, 251 insertions, 0 deletions
diff --git a/drivers/net/fs_enet/mii-fec.c b/drivers/net/fs_enet/mii-fec.c new file mode 100644 index 00000000000..e0e9d6c35d8 --- /dev/null +++ b/drivers/net/fs_enet/mii-fec.c | |||
@@ -0,0 +1,251 @@ | |||
1 | /* | ||
2 | * Combined Ethernet driver for Motorola MPC8xx and MPC82xx. | ||
3 | * | ||
4 | * Copyright (c) 2003 Intracom S.A. | ||
5 | * by Pantelis Antoniou <panto@intracom.gr> | ||
6 | * | ||
7 | * 2005 (c) MontaVista Software, Inc. | ||
8 | * Vitaly Bordug <vbordug@ru.mvista.com> | ||
9 | * | ||
10 | * This file is licensed under the terms of the GNU General Public License | ||
11 | * version 2. This program is licensed "as is" without any warranty of any | ||
12 | * kind, whether express or implied. | ||
13 | */ | ||
14 | |||
15 | #include <linux/module.h> | ||
16 | #include <linux/types.h> | ||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/string.h> | ||
19 | #include <linux/ptrace.h> | ||
20 | #include <linux/errno.h> | ||
21 | #include <linux/ioport.h> | ||
22 | #include <linux/slab.h> | ||
23 | #include <linux/interrupt.h> | ||
24 | #include <linux/init.h> | ||
25 | #include <linux/delay.h> | ||
26 | #include <linux/netdevice.h> | ||
27 | #include <linux/etherdevice.h> | ||
28 | #include <linux/skbuff.h> | ||
29 | #include <linux/spinlock.h> | ||
30 | #include <linux/mii.h> | ||
31 | #include <linux/ethtool.h> | ||
32 | #include <linux/bitops.h> | ||
33 | #include <linux/platform_device.h> | ||
34 | #include <linux/of_platform.h> | ||
35 | |||
36 | #include <asm/pgtable.h> | ||
37 | #include <asm/irq.h> | ||
38 | #include <asm/uaccess.h> | ||
39 | #include <asm/mpc5xxx.h> | ||
40 | |||
41 | #include "fs_enet.h" | ||
42 | #include "fec.h" | ||
43 | |||
44 | /* Make MII read/write commands for the FEC. | ||
45 | */ | ||
46 | #define mk_mii_read(REG) (0x60020000 | ((REG & 0x1f) << 18)) | ||
47 | #define mk_mii_write(REG, VAL) (0x50020000 | ((REG & 0x1f) << 18) | (VAL & 0xffff)) | ||
48 | #define mk_mii_end 0 | ||
49 | |||
50 | #define FEC_MII_LOOPS 10000 | ||
51 | |||
52 | static int fs_enet_fec_mii_read(struct mii_bus *bus , int phy_id, int location) | ||
53 | { | ||
54 | struct fec_info* fec = bus->priv; | ||
55 | struct fec __iomem *fecp = fec->fecp; | ||
56 | int i, ret = -1; | ||
57 | |||
58 | BUG_ON((in_be32(&fecp->fec_r_cntrl) & FEC_RCNTRL_MII_MODE) == 0); | ||
59 | |||
60 | /* Add PHY address to register command. */ | ||
61 | out_be32(&fecp->fec_mii_data, (phy_id << 23) | mk_mii_read(location)); | ||
62 | |||
63 | for (i = 0; i < FEC_MII_LOOPS; i++) | ||
64 | if ((in_be32(&fecp->fec_ievent) & FEC_ENET_MII) != 0) | ||
65 | break; | ||
66 | |||
67 | if (i < FEC_MII_LOOPS) { | ||
68 | out_be32(&fecp->fec_ievent, FEC_ENET_MII); | ||
69 | ret = in_be32(&fecp->fec_mii_data) & 0xffff; | ||
70 | } | ||
71 | |||
72 | return ret; | ||
73 | } | ||
74 | |||
75 | static int fs_enet_fec_mii_write(struct mii_bus *bus, int phy_id, int location, u16 val) | ||
76 | { | ||
77 | struct fec_info* fec = bus->priv; | ||
78 | struct fec __iomem *fecp = fec->fecp; | ||
79 | int i; | ||
80 | |||
81 | /* this must never happen */ | ||
82 | BUG_ON((in_be32(&fecp->fec_r_cntrl) & FEC_RCNTRL_MII_MODE) == 0); | ||
83 | |||
84 | /* Add PHY address to register command. */ | ||
85 | out_be32(&fecp->fec_mii_data, (phy_id << 23) | mk_mii_write(location, val)); | ||
86 | |||
87 | for (i = 0; i < FEC_MII_LOOPS; i++) | ||
88 | if ((in_be32(&fecp->fec_ievent) & FEC_ENET_MII) != 0) | ||
89 | break; | ||
90 | |||
91 | if (i < FEC_MII_LOOPS) | ||
92 | out_be32(&fecp->fec_ievent, FEC_ENET_MII); | ||
93 | |||
94 | return 0; | ||
95 | |||
96 | } | ||
97 | |||
98 | static int fs_enet_fec_mii_reset(struct mii_bus *bus) | ||
99 | { | ||
100 | /* nothing here - for now */ | ||
101 | return 0; | ||
102 | } | ||
103 | |||
104 | static struct of_device_id fs_enet_mdio_fec_match[]; | ||
105 | static int __devinit fs_enet_mdio_probe(struct platform_device *ofdev) | ||
106 | { | ||
107 | const struct of_device_id *match; | ||
108 | struct resource res; | ||
109 | struct mii_bus *new_bus; | ||
110 | struct fec_info *fec; | ||
111 | int (*get_bus_freq)(struct device_node *); | ||
112 | int ret = -ENOMEM, clock, speed; | ||
113 | |||
114 | match = of_match_device(fs_enet_mdio_fec_match, &ofdev->dev); | ||
115 | if (!match) | ||
116 | return -EINVAL; | ||
117 | get_bus_freq = match->data; | ||
118 | |||
119 | new_bus = mdiobus_alloc(); | ||
120 | if (!new_bus) | ||
121 | goto out; | ||
122 | |||
123 | fec = kzalloc(sizeof(struct fec_info), GFP_KERNEL); | ||
124 | if (!fec) | ||
125 | goto out_mii; | ||
126 | |||
127 | new_bus->priv = fec; | ||
128 | new_bus->name = "FEC MII Bus"; | ||
129 | new_bus->read = &fs_enet_fec_mii_read; | ||
130 | new_bus->write = &fs_enet_fec_mii_write; | ||
131 | new_bus->reset = &fs_enet_fec_mii_reset; | ||
132 | |||
133 | ret = of_address_to_resource(ofdev->dev.of_node, 0, &res); | ||
134 | if (ret) | ||
135 | goto out_res; | ||
136 | |||
137 | snprintf(new_bus->id, MII_BUS_ID_SIZE, "%x", res.start); | ||
138 | |||
139 | fec->fecp = ioremap(res.start, resource_size(&res)); | ||
140 | if (!fec->fecp) | ||
141 | goto out_fec; | ||
142 | |||
143 | if (get_bus_freq) { | ||
144 | clock = get_bus_freq(ofdev->dev.of_node); | ||
145 | if (!clock) { | ||
146 | /* Use maximum divider if clock is unknown */ | ||
147 | dev_warn(&ofdev->dev, "could not determine IPS clock\n"); | ||
148 | clock = 0x3F * 5000000; | ||
149 | } | ||
150 | } else | ||
151 | clock = ppc_proc_freq; | ||
152 | |||
153 | /* | ||
154 | * Scale for a MII clock <= 2.5 MHz | ||
155 | * Note that only 6 bits (25:30) are available for MII speed. | ||
156 | */ | ||
157 | speed = (clock + 4999999) / 5000000; | ||
158 | if (speed > 0x3F) { | ||
159 | speed = 0x3F; | ||
160 | dev_err(&ofdev->dev, | ||
161 | "MII clock (%d Hz) exceeds max (2.5 MHz)\n", | ||
162 | clock / speed); | ||
163 | } | ||
164 | |||
165 | fec->mii_speed = speed << 1; | ||
166 | |||
167 | setbits32(&fec->fecp->fec_r_cntrl, FEC_RCNTRL_MII_MODE); | ||
168 | setbits32(&fec->fecp->fec_ecntrl, FEC_ECNTRL_PINMUX | | ||
169 | FEC_ECNTRL_ETHER_EN); | ||
170 | out_be32(&fec->fecp->fec_ievent, FEC_ENET_MII); | ||
171 | clrsetbits_be32(&fec->fecp->fec_mii_speed, 0x7E, fec->mii_speed); | ||
172 | |||
173 | new_bus->phy_mask = ~0; | ||
174 | new_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL); | ||
175 | if (!new_bus->irq) | ||
176 | goto out_unmap_regs; | ||
177 | |||
178 | new_bus->parent = &ofdev->dev; | ||
179 | dev_set_drvdata(&ofdev->dev, new_bus); | ||
180 | |||
181 | ret = of_mdiobus_register(new_bus, ofdev->dev.of_node); | ||
182 | if (ret) | ||
183 | goto out_free_irqs; | ||
184 | |||
185 | return 0; | ||
186 | |||
187 | out_free_irqs: | ||
188 | dev_set_drvdata(&ofdev->dev, NULL); | ||
189 | kfree(new_bus->irq); | ||
190 | out_unmap_regs: | ||
191 | iounmap(fec->fecp); | ||
192 | out_res: | ||
193 | out_fec: | ||
194 | kfree(fec); | ||
195 | out_mii: | ||
196 | mdiobus_free(new_bus); | ||
197 | out: | ||
198 | return ret; | ||
199 | } | ||
200 | |||
201 | static int fs_enet_mdio_remove(struct platform_device *ofdev) | ||
202 | { | ||
203 | struct mii_bus *bus = dev_get_drvdata(&ofdev->dev); | ||
204 | struct fec_info *fec = bus->priv; | ||
205 | |||
206 | mdiobus_unregister(bus); | ||
207 | dev_set_drvdata(&ofdev->dev, NULL); | ||
208 | kfree(bus->irq); | ||
209 | iounmap(fec->fecp); | ||
210 | kfree(fec); | ||
211 | mdiobus_free(bus); | ||
212 | |||
213 | return 0; | ||
214 | } | ||
215 | |||
216 | static struct of_device_id fs_enet_mdio_fec_match[] = { | ||
217 | { | ||
218 | .compatible = "fsl,pq1-fec-mdio", | ||
219 | }, | ||
220 | #if defined(CONFIG_PPC_MPC512x) | ||
221 | { | ||
222 | .compatible = "fsl,mpc5121-fec-mdio", | ||
223 | .data = mpc5xxx_get_bus_frequency, | ||
224 | }, | ||
225 | #endif | ||
226 | {}, | ||
227 | }; | ||
228 | MODULE_DEVICE_TABLE(of, fs_enet_mdio_fec_match); | ||
229 | |||
230 | static struct platform_driver fs_enet_fec_mdio_driver = { | ||
231 | .driver = { | ||
232 | .name = "fsl-fec-mdio", | ||
233 | .owner = THIS_MODULE, | ||
234 | .of_match_table = fs_enet_mdio_fec_match, | ||
235 | }, | ||
236 | .probe = fs_enet_mdio_probe, | ||
237 | .remove = fs_enet_mdio_remove, | ||
238 | }; | ||
239 | |||
240 | static int fs_enet_mdio_fec_init(void) | ||
241 | { | ||
242 | return platform_driver_register(&fs_enet_fec_mdio_driver); | ||
243 | } | ||
244 | |||
245 | static void fs_enet_mdio_fec_exit(void) | ||
246 | { | ||
247 | platform_driver_unregister(&fs_enet_fec_mdio_driver); | ||
248 | } | ||
249 | |||
250 | module_init(fs_enet_mdio_fec_init); | ||
251 | module_exit(fs_enet_mdio_fec_exit); | ||