]>
Commit | Line | Data |
---|---|---|
48257c4f PA |
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 | ||
48257c4f PA |
16 | #include <linux/module.h> |
17 | #include <linux/types.h> | |
18 | #include <linux/kernel.h> | |
19 | #include <linux/sched.h> | |
20 | #include <linux/string.h> | |
21 | #include <linux/ptrace.h> | |
22 | #include <linux/errno.h> | |
23 | #include <linux/ioport.h> | |
24 | #include <linux/slab.h> | |
25 | #include <linux/interrupt.h> | |
26 | #include <linux/pci.h> | |
27 | #include <linux/init.h> | |
28 | #include <linux/delay.h> | |
29 | #include <linux/netdevice.h> | |
30 | #include <linux/etherdevice.h> | |
31 | #include <linux/skbuff.h> | |
32 | #include <linux/spinlock.h> | |
33 | #include <linux/mii.h> | |
34 | #include <linux/ethtool.h> | |
35 | #include <linux/bitops.h> | |
5b4b8454 | 36 | #include <linux/platform_device.h> |
48257c4f PA |
37 | |
38 | #include <asm/pgtable.h> | |
39 | #include <asm/irq.h> | |
40 | #include <asm/uaccess.h> | |
41 | ||
42 | #include "fs_enet.h" | |
43 | ||
5b4b8454 VB |
44 | static int bitbang_prep_bit(u8 **datp, u8 *mskp, |
45 | struct fs_mii_bit *mii_bit) | |
48257c4f | 46 | { |
5b4b8454 | 47 | void *dat; |
48257c4f PA |
48 | int adv; |
49 | u8 msk; | |
50 | ||
5b4b8454 | 51 | dat = (void*) mii_bit->offset; |
48257c4f | 52 | |
5b4b8454 | 53 | adv = mii_bit->bit >> 3; |
48257c4f | 54 | dat = (char *)dat + adv; |
48257c4f | 55 | |
5b4b8454 | 56 | msk = 1 << (7 - (mii_bit->bit & 7)); |
48257c4f | 57 | |
48257c4f PA |
58 | *datp = dat; |
59 | *mskp = msk; | |
60 | ||
61 | return 0; | |
62 | } | |
48257c4f PA |
63 | |
64 | static inline void bb_set(u8 *p, u8 m) | |
65 | { | |
66 | out_8(p, in_8(p) | m); | |
67 | } | |
68 | ||
69 | static inline void bb_clr(u8 *p, u8 m) | |
70 | { | |
71 | out_8(p, in_8(p) & ~m); | |
72 | } | |
73 | ||
74 | static inline int bb_read(u8 *p, u8 m) | |
75 | { | |
76 | return (in_8(p) & m) != 0; | |
77 | } | |
78 | ||
5b4b8454 | 79 | static inline void mdio_active(struct bb_info *bitbang) |
48257c4f | 80 | { |
5b4b8454 | 81 | bb_set(bitbang->mdio_dir, bitbang->mdio_dir_msk); |
48257c4f PA |
82 | } |
83 | ||
5b4b8454 | 84 | static inline void mdio_tristate(struct bb_info *bitbang ) |
48257c4f | 85 | { |
5b4b8454 | 86 | bb_clr(bitbang->mdio_dir, bitbang->mdio_dir_msk); |
48257c4f PA |
87 | } |
88 | ||
5b4b8454 | 89 | static inline int mdio_read(struct bb_info *bitbang ) |
48257c4f | 90 | { |
5b4b8454 | 91 | return bb_read(bitbang->mdio_dat, bitbang->mdio_dat_msk); |
48257c4f PA |
92 | } |
93 | ||
5b4b8454 | 94 | static inline void mdio(struct bb_info *bitbang , int what) |
48257c4f PA |
95 | { |
96 | if (what) | |
5b4b8454 | 97 | bb_set(bitbang->mdio_dat, bitbang->mdio_dat_msk); |
48257c4f | 98 | else |
5b4b8454 | 99 | bb_clr(bitbang->mdio_dat, bitbang->mdio_dat_msk); |
48257c4f PA |
100 | } |
101 | ||
5b4b8454 | 102 | static inline void mdc(struct bb_info *bitbang , int what) |
48257c4f PA |
103 | { |
104 | if (what) | |
5b4b8454 | 105 | bb_set(bitbang->mdc_dat, bitbang->mdc_msk); |
48257c4f | 106 | else |
5b4b8454 | 107 | bb_clr(bitbang->mdc_dat, bitbang->mdc_msk); |
48257c4f PA |
108 | } |
109 | ||
5b4b8454 | 110 | static inline void mii_delay(struct bb_info *bitbang ) |
48257c4f | 111 | { |
5b4b8454 | 112 | udelay(bitbang->delay); |
48257c4f PA |
113 | } |
114 | ||
115 | /* Utility to send the preamble, address, and register (common to read and write). */ | |
5b4b8454 | 116 | static void bitbang_pre(struct bb_info *bitbang , int read, u8 addr, u8 reg) |
48257c4f PA |
117 | { |
118 | int j; | |
119 | ||
120 | /* | |
121 | * Send a 32 bit preamble ('1's) with an extra '1' bit for good measure. | |
122 | * The IEEE spec says this is a PHY optional requirement. The AMD | |
123 | * 79C874 requires one after power up and one after a MII communications | |
124 | * error. This means that we are doing more preambles than we need, | |
125 | * but it is safer and will be much more robust. | |
126 | */ | |
127 | ||
5b4b8454 VB |
128 | mdio_active(bitbang); |
129 | mdio(bitbang, 1); | |
48257c4f | 130 | for (j = 0; j < 32; j++) { |
5b4b8454 VB |
131 | mdc(bitbang, 0); |
132 | mii_delay(bitbang); | |
133 | mdc(bitbang, 1); | |
134 | mii_delay(bitbang); | |
48257c4f PA |
135 | } |
136 | ||
137 | /* send the start bit (01) and the read opcode (10) or write (10) */ | |
5b4b8454 VB |
138 | mdc(bitbang, 0); |
139 | mdio(bitbang, 0); | |
140 | mii_delay(bitbang); | |
141 | mdc(bitbang, 1); | |
142 | mii_delay(bitbang); | |
143 | mdc(bitbang, 0); | |
144 | mdio(bitbang, 1); | |
145 | mii_delay(bitbang); | |
146 | mdc(bitbang, 1); | |
147 | mii_delay(bitbang); | |
148 | mdc(bitbang, 0); | |
149 | mdio(bitbang, read); | |
150 | mii_delay(bitbang); | |
151 | mdc(bitbang, 1); | |
152 | mii_delay(bitbang); | |
153 | mdc(bitbang, 0); | |
154 | mdio(bitbang, !read); | |
155 | mii_delay(bitbang); | |
156 | mdc(bitbang, 1); | |
157 | mii_delay(bitbang); | |
48257c4f PA |
158 | |
159 | /* send the PHY address */ | |
160 | for (j = 0; j < 5; j++) { | |
5b4b8454 VB |
161 | mdc(bitbang, 0); |
162 | mdio(bitbang, (addr & 0x10) != 0); | |
163 | mii_delay(bitbang); | |
164 | mdc(bitbang, 1); | |
165 | mii_delay(bitbang); | |
48257c4f PA |
166 | addr <<= 1; |
167 | } | |
168 | ||
169 | /* send the register address */ | |
170 | for (j = 0; j < 5; j++) { | |
5b4b8454 VB |
171 | mdc(bitbang, 0); |
172 | mdio(bitbang, (reg & 0x10) != 0); | |
173 | mii_delay(bitbang); | |
174 | mdc(bitbang, 1); | |
175 | mii_delay(bitbang); | |
48257c4f PA |
176 | reg <<= 1; |
177 | } | |
178 | } | |
179 | ||
5b4b8454 | 180 | static int fs_enet_mii_bb_read(struct mii_bus *bus , int phy_id, int location) |
48257c4f PA |
181 | { |
182 | u16 rdreg; | |
183 | int ret, j; | |
184 | u8 addr = phy_id & 0xff; | |
185 | u8 reg = location & 0xff; | |
5b4b8454 | 186 | struct bb_info* bitbang = bus->priv; |
48257c4f | 187 | |
5b4b8454 | 188 | bitbang_pre(bitbang, 1, addr, reg); |
48257c4f PA |
189 | |
190 | /* tri-state our MDIO I/O pin so we can read */ | |
5b4b8454 VB |
191 | mdc(bitbang, 0); |
192 | mdio_tristate(bitbang); | |
193 | mii_delay(bitbang); | |
194 | mdc(bitbang, 1); | |
195 | mii_delay(bitbang); | |
48257c4f PA |
196 | |
197 | /* check the turnaround bit: the PHY should be driving it to zero */ | |
5b4b8454 | 198 | if (mdio_read(bitbang) != 0) { |
48257c4f PA |
199 | /* PHY didn't drive TA low */ |
200 | for (j = 0; j < 32; j++) { | |
5b4b8454 VB |
201 | mdc(bitbang, 0); |
202 | mii_delay(bitbang); | |
203 | mdc(bitbang, 1); | |
204 | mii_delay(bitbang); | |
48257c4f PA |
205 | } |
206 | ret = -1; | |
207 | goto out; | |
208 | } | |
209 | ||
5b4b8454 VB |
210 | mdc(bitbang, 0); |
211 | mii_delay(bitbang); | |
48257c4f PA |
212 | |
213 | /* read 16 bits of register data, MSB first */ | |
214 | rdreg = 0; | |
215 | for (j = 0; j < 16; j++) { | |
5b4b8454 VB |
216 | mdc(bitbang, 1); |
217 | mii_delay(bitbang); | |
48257c4f | 218 | rdreg <<= 1; |
5b4b8454 VB |
219 | rdreg |= mdio_read(bitbang); |
220 | mdc(bitbang, 0); | |
221 | mii_delay(bitbang); | |
48257c4f PA |
222 | } |
223 | ||
5b4b8454 VB |
224 | mdc(bitbang, 1); |
225 | mii_delay(bitbang); | |
226 | mdc(bitbang, 0); | |
227 | mii_delay(bitbang); | |
228 | mdc(bitbang, 1); | |
229 | mii_delay(bitbang); | |
48257c4f PA |
230 | |
231 | ret = rdreg; | |
232 | out: | |
233 | return ret; | |
234 | } | |
235 | ||
5b4b8454 | 236 | static int fs_enet_mii_bb_write(struct mii_bus *bus, int phy_id, int location, u16 val) |
48257c4f PA |
237 | { |
238 | int j; | |
5b4b8454 VB |
239 | struct bb_info* bitbang = bus->priv; |
240 | ||
48257c4f PA |
241 | u8 addr = phy_id & 0xff; |
242 | u8 reg = location & 0xff; | |
243 | u16 value = val & 0xffff; | |
244 | ||
5b4b8454 | 245 | bitbang_pre(bitbang, 0, addr, reg); |
48257c4f PA |
246 | |
247 | /* send the turnaround (10) */ | |
5b4b8454 VB |
248 | mdc(bitbang, 0); |
249 | mdio(bitbang, 1); | |
250 | mii_delay(bitbang); | |
251 | mdc(bitbang, 1); | |
252 | mii_delay(bitbang); | |
253 | mdc(bitbang, 0); | |
254 | mdio(bitbang, 0); | |
255 | mii_delay(bitbang); | |
256 | mdc(bitbang, 1); | |
257 | mii_delay(bitbang); | |
48257c4f PA |
258 | |
259 | /* write 16 bits of register data, MSB first */ | |
260 | for (j = 0; j < 16; j++) { | |
5b4b8454 VB |
261 | mdc(bitbang, 0); |
262 | mdio(bitbang, (value & 0x8000) != 0); | |
263 | mii_delay(bitbang); | |
264 | mdc(bitbang, 1); | |
265 | mii_delay(bitbang); | |
48257c4f PA |
266 | value <<= 1; |
267 | } | |
268 | ||
269 | /* | |
270 | * Tri-state the MDIO line. | |
271 | */ | |
5b4b8454 VB |
272 | mdio_tristate(bitbang); |
273 | mdc(bitbang, 0); | |
274 | mii_delay(bitbang); | |
275 | mdc(bitbang, 1); | |
276 | mii_delay(bitbang); | |
277 | return 0; | |
48257c4f PA |
278 | } |
279 | ||
5b4b8454 VB |
280 | static int fs_enet_mii_bb_reset(struct mii_bus *bus) |
281 | { | |
282 | /*nothing here - dunno how to reset it*/ | |
283 | return 0; | |
284 | } | |
285 | ||
286 | static int fs_mii_bitbang_init(struct bb_info *bitbang, struct fs_mii_bb_platform_info* fmpi) | |
48257c4f | 287 | { |
48257c4f PA |
288 | int r; |
289 | ||
5b4b8454 VB |
290 | bitbang->delay = fmpi->delay; |
291 | ||
292 | r = bitbang_prep_bit(&bitbang->mdio_dir, | |
293 | &bitbang->mdio_dir_msk, | |
294 | &fmpi->mdio_dir); | |
48257c4f PA |
295 | if (r != 0) |
296 | return r; | |
297 | ||
5b4b8454 VB |
298 | r = bitbang_prep_bit(&bitbang->mdio_dat, |
299 | &bitbang->mdio_dat_msk, | |
300 | &fmpi->mdio_dat); | |
48257c4f PA |
301 | if (r != 0) |
302 | return r; | |
303 | ||
5b4b8454 VB |
304 | r = bitbang_prep_bit(&bitbang->mdc_dat, |
305 | &bitbang->mdc_msk, | |
306 | &fmpi->mdc_dat); | |
307 | if (r != 0) | |
308 | return r; | |
48257c4f PA |
309 | |
310 | return 0; | |
311 | } | |
5b4b8454 VB |
312 | |
313 | ||
314 | static int __devinit fs_enet_mdio_probe(struct device *dev) | |
315 | { | |
316 | struct platform_device *pdev = to_platform_device(dev); | |
317 | struct fs_mii_bb_platform_info *pdata; | |
318 | struct mii_bus *new_bus; | |
319 | struct bb_info *bitbang; | |
320 | int err = 0; | |
321 | ||
322 | if (NULL == dev) | |
323 | return -EINVAL; | |
324 | ||
325 | new_bus = kzalloc(sizeof(struct mii_bus), GFP_KERNEL); | |
326 | ||
327 | if (NULL == new_bus) | |
328 | return -ENOMEM; | |
329 | ||
330 | bitbang = kzalloc(sizeof(struct bb_info), GFP_KERNEL); | |
331 | ||
332 | if (NULL == bitbang) | |
333 | return -ENOMEM; | |
334 | ||
335 | new_bus->name = "BB MII Bus", | |
336 | new_bus->read = &fs_enet_mii_bb_read, | |
337 | new_bus->write = &fs_enet_mii_bb_write, | |
338 | new_bus->reset = &fs_enet_mii_bb_reset, | |
339 | new_bus->id = pdev->id; | |
340 | ||
341 | new_bus->phy_mask = ~0x9; | |
342 | pdata = (struct fs_mii_bb_platform_info *)pdev->dev.platform_data; | |
343 | ||
344 | if (NULL == pdata) { | |
345 | printk(KERN_ERR "gfar mdio %d: Missing platform data!\n", pdev->id); | |
346 | return -ENODEV; | |
347 | } | |
348 | ||
349 | /*set up workspace*/ | |
350 | fs_mii_bitbang_init(bitbang, pdata); | |
351 | ||
352 | new_bus->priv = bitbang; | |
353 | ||
354 | new_bus->irq = pdata->irq; | |
355 | ||
356 | new_bus->dev = dev; | |
357 | dev_set_drvdata(dev, new_bus); | |
358 | ||
359 | err = mdiobus_register(new_bus); | |
360 | ||
361 | if (0 != err) { | |
362 | printk (KERN_ERR "%s: Cannot register as MDIO bus\n", | |
363 | new_bus->name); | |
364 | goto bus_register_fail; | |
365 | } | |
366 | ||
367 | return 0; | |
368 | ||
369 | bus_register_fail: | |
370 | kfree(bitbang); | |
371 | kfree(new_bus); | |
372 | ||
373 | return err; | |
374 | } | |
375 | ||
376 | ||
377 | static int fs_enet_mdio_remove(struct device *dev) | |
378 | { | |
379 | struct mii_bus *bus = dev_get_drvdata(dev); | |
380 | ||
381 | mdiobus_unregister(bus); | |
382 | ||
383 | dev_set_drvdata(dev, NULL); | |
384 | ||
385 | iounmap((void *) (&bus->priv)); | |
386 | bus->priv = NULL; | |
387 | kfree(bus); | |
388 | ||
389 | return 0; | |
390 | } | |
391 | ||
392 | static struct device_driver fs_enet_bb_mdio_driver = { | |
393 | .name = "fsl-bb-mdio", | |
394 | .bus = &platform_bus_type, | |
395 | .probe = fs_enet_mdio_probe, | |
396 | .remove = fs_enet_mdio_remove, | |
397 | }; | |
398 | ||
399 | int fs_enet_mdio_bb_init(void) | |
400 | { | |
401 | return driver_register(&fs_enet_bb_mdio_driver); | |
402 | } | |
403 | ||
404 | void fs_enet_mdio_bb_exit(void) | |
405 | { | |
406 | driver_unregister(&fs_enet_bb_mdio_driver); | |
407 | } | |
408 |