Freescale Ethernet combined driver
PATCH 4/4 Freescale Ethernet combined driver
Hi
The following patch is a combined FCC/FEC ethernet driver for the Freescale line of PowerQUICCs.
FECs on 8xx and FCCs on 82xx are supported.
This part of the patch contains the mii drivers.
Regards
Pantelis
drivers/net/fs_enet/mii-bitbang.c new file +394 -0
@@ -0,0 +1,394 @@ +/* +* XXX blah blah +*/ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/string.h> +#include <linux/ptrace.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <linux/spinlock.h> +#include <linux/mii.h> +#include <linux/ethtool.h> +#include <linux/bitops.h> + +#include <asm/pgtable.h> +#include <asm/irq.h> +#include <asm/uaccess.h> + +#include "fs_enet.h" + +#ifdef CONFIG_8xx +static int bitbang_prep_bit (__u8 **dirp,__u8 **datp,__u8 *mskp,int port,int bit) +{ +immap_t *im = (immap_t *)IMAP_ADDR ; +void *dir, *dat, *ppar; +int adv; +__u8 msk; + +switch (port) { +case fsiop_porta: + dir = &im->im_ioport.iop_padir; + dat = &im->im_ioport.iop_padat; + ppar = &im->im_ioport.iop_papar; +break ; + +case fsiop_portb: + dir = &im->im_cpm.cp_pbdir; + dat = &im->im_cpm.cp_pbdat; + ppar = &im->im_cpm.cp_pbpar; +break ; + +case fsiop_portc: + dir = &im->im_ioport.iop_pcdir; + dat = &im->im_ioport.iop_pcdat; + ppar = &im->im_ioport.iop_pcpar; +break ; + +case fsiop_portd: + dir = &im->im_ioport.iop_pddir; + dat = &im->im_ioport.iop_pddat; + ppar = &im->im_ioport.iop_pdpar; +break ; + +case fsiop_porte: + dir = &im->im_cpm.cp_pedir; + dat = &im->im_cpm.cp_pedat; + ppar = &im->im_cpm.cp_pepar; +break ; + +default : + printk(KERN_ERR DRV_MODULE_NAME + "Illegal port value %d!\n", port); +return -EINVAL ; + } + + adv = bit >>3 ; + dir = (char *)dir + adv; + dat = (char *)dat + adv; + ppar = (char *)ppar + adv; + + msk =1 << (7 - (bit &7 )); +if ((in_8 (ppar) & msk) !=0 ) { + printk(KERN_ERR DRV_MODULE_NAME + "pin %d on port %d is not general purpose!\n", bit, port); +return -EINVAL ; + } + + *dirp = dir; + *datp = dat; + *mskp = msk; + +return 0 ; +} +#endif + +#ifdef CONFIG_8260 +static int bitbang_prep_bit (__u8 **dirp,__u8 **datp,__u8 *mskp,int port,int bit) +{ +iop_cpm2_t *io = &((cpm2_map_t *)CPM_MAP_ADDR )->im_ioport; +void *dir, *dat, *ppar; +int adv; +__u8 msk; + +switch (port) { +case fsiop_porta: + dir = &io->iop_pdira; + dat = &io->iop_pdata; + ppar = &io->iop_ppara; +break ; + +case fsiop_portb: + dir = &io->iop_pdirb; + dat = &io->iop_pdatb; + ppar = &io->iop_pparb; +break ; + +case fsiop_portc: + dir = &io->iop_pdirc; + dat = &io->iop_pdatc; + ppar = &io->iop_pparc; +break ; + +case fsiop_portd: + dir = &io->iop_pdird; + dat = &io->iop_pdatd; + ppar = &io->iop_ppard; +break ; + +default : + printk(KERN_ERR DRV_MODULE_NAME + "Illegal port value %d!\n", port); +return -EINVAL ; + } + + adv = bit >>3 ; + dir = (char *)dir + adv; + dat = (char *)dat + adv; + ppar = (char *)ppar + adv; + + msk =1 << (7 - (bit &7 )); +if ((in_8 (ppar) & msk) !=0 ) { + printk(KERN_ERR DRV_MODULE_NAME + "pin %d on port %d is not general purpose!\n", bit, port); +return -EINVAL ; + } + + *dirp = dir; + *datp = dat; + *mskp = msk; + +return 0 ; +} +#endif + +static inline void bb_set (__u8 *p,__u8 m) +{ +out_8 (p,in_8 (p) | m); +} + +static inline void bb_clr (__u8 *p,__u8 m) +{ +out_8 (p,in_8 (p) & ~m); +} + +static inline int bb_read (__u8 *p,__u8 m) +{ +return (in_8 (p) & m) !=0 ; +} + +static inline void mdio_active (struct fs_enet_mii_bus *bus) +{ +bb_set (bus->bitbang.mdio_dir, bus->bitbang.mdio_msk); +} + +static inline void mdio_tristate (struct fs_enet_mii_bus *bus) +{ +bb_clr (bus->bitbang.mdio_dir, bus->bitbang.mdio_msk); +} + +static inline int mdio_read (struct fs_enet_mii_bus *bus) +{ +return bb_read (bus->bitbang.mdio_dat, bus->bitbang.mdio_msk); +} + +static inline void mdio (struct fs_enet_mii_bus *bus,int what) +{ +if (what) +bb_set (bus->bitbang.mdio_dat, bus->bitbang.mdio_msk); +else +bb_clr (bus->bitbang.mdio_dat, bus->bitbang.mdio_msk); +} + +static inline void mdc (struct fs_enet_mii_bus *bus,int what) +{ +if (what) +bb_set (bus->bitbang.mdc_dat, bus->bitbang.mdc_msk); +else +bb_clr (bus->bitbang.mdc_dat, bus->bitbang.mdc_msk); +} + +static inline void mii_delay (struct fs_enet_mii_bus *bus) +{ +udelay (bus->bus_info->i.bitbang.delay); +} + +/* Utility to send the preamble, address, and register (common to read and write). */ +static void bitbang_pre (struct fs_enet_mii_bus *bus,int read,__u8 addr,__u8 reg) +{ +int j; + +/* +* Send a 32 bit preamble ('1's) with an extra '1' bit for good measure. +* The IEEE spec says this is a PHY optional requirement. The AMD +* 79C874 requires one after power up and one after a MII communications +* error. This means that we are doing more preambles than we need, +* but it is safer and will be much more robust. +*/ + +mdio_active (bus); +mdio (bus,1 ); +for (j =0 ; j <32 ; j++) { +mdc (bus,0 ); +mii_delay (bus); +mdc (bus,1 ); +mii_delay (bus); + } + +/* send the start bit (01) and the read opcode (10) or write (10) */ +mdc (bus,0 ); +mdio (bus,0 ); +mii_delay (bus); +mdc (bus,1 ); +mii_delay (bus); +mdc (bus,0 ); +mdio (bus,1 ); +mii_delay (bus); +mdc (bus,1 ); +mii_delay (bus); +mdc (bus,0 ); +mdio (bus, read); +mii_delay (bus); +mdc (bus,1 ); +mii_delay (bus); +mdc (bus,0 ); +mdio (bus, !read); +mii_delay (bus); +mdc (bus,1 ); +mii_delay (bus); + +/* send the PHY address */ +for (j =0 ; j <5 ; j++) { +mdc (bus,0 ); +mdio (bus, (addr &0x10 ) !=0 ); +mii_delay (bus); +mdc (bus,1 ); +mii_delay (bus); + addr <<=1 ; + } + +/* send the register address */ +for (j =0 ; j <5 ; j++) { +mdc (bus,0 ); +mdio (bus, (reg &0x10 ) !=0 ); +mii_delay (bus); +mdc (bus,1 ); +mii_delay (bus); + reg <<=1 ; + } +} + +static int mii_read (struct fs_enet_mii_bus *bus,int phy_id,int location) +{ +__u16 rdreg; +int ret, j; +__u8 addr = phy_id &0xff ; +__u8 reg = location &0xff ; + +bitbang_pre (bus,1 , addr, reg); + +/* tri-state our MDIO I/O pin so we can read */ +mdc (bus,0 ); +mdio_tristate (bus); +mii_delay (bus); +mdc (bus,1 ); +mii_delay (bus); + +/* check the turnaround bit: the PHY should be driving it to zero */ +if (mdio_read (bus) !=0 ) { +/* puts ("PHY didn't drive TA low\n"); */ +for (j =0 ; j <32 ; j++) { +mdc (bus,0 ); +mii_delay (bus); +mdc (bus,1 ); +mii_delay (bus); + } + ret =-1 ; + goto out; + } + +mdc (bus,0 ); +mii_delay (bus); + +/* read 16 bits of register data, MSB first */ + rdreg =0 ; +for (j =0 ; j <16 ; j++) { +mdc (bus,1 ); +mii_delay (bus); + rdreg <<=1 ; + rdreg |=mdio_read (bus); +mdc (bus,0 ); +mii_delay (bus); + } + +mdc (bus,1 ); +mii_delay (bus); +mdc (bus,0 ); +mii_delay (bus); +mdc (bus,1 ); +mii_delay (bus); + + ret = rdreg; +out: +return ret; +} + +static void mii_write (struct fs_enet_mii_bus *bus,int phy_id,int location,int val) +{ +int j; +__u8 addr = phy_id &0xff ; +__u8 reg = location &0xff ; +__u16 value = val &0xffff ; + +bitbang_pre (bus,0 , addr, reg); + +/* send the turnaround (10) */ +mdc (bus,0 ); +mdio (bus,1 ); +mii_delay (bus); +mdc (bus,1 ); +mii_delay (bus); +mdc (bus,0 ); +mdio (bus,0 ); +mii_delay (bus); +mdc (bus,1 ); +mii_delay (bus); + +/* write 16 bits of register data, MSB first */ +for (j =0 ; j <16 ; j++) { +mdc (bus,0 ); +mdio (bus, (value &0x8000 ) !=0 ); +mii_delay (bus); +mdc (bus,1 ); +mii_delay (bus); + value <<=1 ; + } + +/* +* Tri-state the MDIO line. +*/ +mdio_tristate (bus); +mdc (bus,0 ); +mii_delay (bus); +mdc (bus,1 ); +mii_delay (bus); +} + +int fs_mii_bitbang_init (struct fs_enet_mii_bus *bus) +{ +const struct fs_mii_bus_info *bi = bus->bus_info; +int r; + + r =bitbang_prep_bit (&bus->bitbang.mdio_dir, + &bus->bitbang.mdio_dat, + &bus->bitbang.mdio_msk, + bi->i.bitbang.mdio_port, + bi->i.bitbang.mdio_bit); +if (r !=0 ) +return r; + + r =bitbang_prep_bit (&bus->bitbang.mdc_dir, + &bus->bitbang.mdc_dat, + &bus->bitbang.mdc_msk, + bi->i.bitbang.mdc_port, + bi->i.bitbang.mdc_bit); +if (r !=0 ) +return r; + + bus->mii_read = mii_read; + bus->mii_write = mii_write; + +return 0 ; +}
drivers/net/fs_enet/mii-fixed.c new file +81 -0
81 lines not on this page · raw message
Hi,
May be a little late (I'm trying to catch up on all mailing list mail) but I was wondering if the io port handling functions I see now in these patches should not be 'atomic'. So toggling MII pins does not interfere with toggling e.g. I2C bit-bang interface pins when those pins happen to be on the same port (A/B/C/D/E).
best regards,
Jaap-Jan
Quoted 8 lines · Jaap-Jan Boor
Hi,
May be a little late (I'm trying to catch up on all mailing list mail) but I was wondering if the io port handling functions I see now in these patches should not be 'atomic'. So toggling MII pins does not interfere with toggling e.g. I2C bit-bang interface pins when those pins happen to be on the same port (A/B/C/D/E).
Quoted 6 lines
best regards,
Jaap-Jan
Jaap-Jan
All mii operations are done under a spinlock with interrupts disabled.
Regards
Pantelis
Ah, very good, thanks
Jaap-Jan
Quoted 7 lines · Pantelis Antoniou
Jaap-Jan
All mii operations are done under a spinlock with interrupts disabled.
Regards
Pantelis