diff options
Diffstat (limited to 'drivers/net/smc91111.c')
-rw-r--r-- | drivers/net/smc91111.c | 254 |
1 files changed, 176 insertions, 78 deletions
diff --git a/drivers/net/smc91111.c b/drivers/net/smc91111.c index 100688ccf8..698c74add1 100644 --- a/drivers/net/smc91111.c +++ b/drivers/net/smc91111.c @@ -435,14 +435,14 @@ #define MEMORY_WAIT_TIME 16 struct accessors { - void (*ob)(unsigned, void __iomem *); - void (*ow)(unsigned, void __iomem *); - void (*ol)(unsigned long, void __iomem *); - void (*osl)(void __iomem *, const void *, int); - unsigned (*ib)(void __iomem *); - unsigned (*iw)(void __iomem *); - unsigned long (*il)(void __iomem *); - void (*isl)(void __iomem *, void*, int); + void (*ob)(unsigned, void __iomem *, unsigned, unsigned); + void (*ow)(unsigned, void __iomem *, unsigned, unsigned); + void (*ol)(unsigned long, void __iomem *, unsigned, unsigned); + void (*osl)(void __iomem *, unsigned, const void *, int, unsigned); + unsigned (*ib)(void __iomem *, unsigned, unsigned); + unsigned (*iw)(void __iomem *, unsigned, unsigned); + unsigned long (*il)(void __iomem *, unsigned, unsigned); + void (*isl)(void __iomem *, unsigned, void*, int, unsigned); }; struct smc91c111_priv { @@ -450,6 +450,11 @@ struct smc91c111_priv { struct accessors a; void __iomem *base; int qemu_fixup; + unsigned shift; + int version; + int revision; + unsigned int control_setup; + unsigned int config_setup; }; #if (SMC_DEBUG > 2 ) @@ -479,109 +484,176 @@ struct smc91c111_priv { #define ETH_ZLEN 60 -static void a_outb(unsigned value, void __iomem *offset) +static void a8_outb(unsigned value, void __iomem *base, unsigned int offset, + unsigned shift) { - writeb(value, offset); + writeb(value, base + (offset << shift)); } -static void a_outw(unsigned value, void __iomem *offset) +static void a16_outw(unsigned value, void __iomem *base, unsigned int offset, + unsigned shift) { - writew(value, offset); + writew(value, base + (offset << shift)); } -static void a_outl(unsigned long value, void __iomem *offset) +static void a32_outl(unsigned long value, void __iomem *base, + unsigned int offset, unsigned shift) { - writel(value, offset); + writel(value, base + (offset << shift)); } -static void a_outsl(void __iomem *offset, const void *data, int count) +static void a16_outsl(void __iomem *base, unsigned int offset, + const void *data, int count, unsigned shift) { - writesl(offset, data, count); + writesw(base + (offset << shift), data, count * 2); } -static unsigned a_inb(void __iomem *offset) +static void a16_outl(unsigned long value, void __iomem *base, + unsigned int offset, unsigned shift) { - return readb(offset); + writew(value & 0xffff, base + (offset << shift)); + writew(value >> 16, base + ((offset + 2) << shift)); } -static unsigned a_inw(void __iomem *offset) +static void a32_outsl(void __iomem *base, unsigned int offset, + const void *data, int count, unsigned shift) { - return readw(offset); + writesw(base + (offset << shift), data, count * 2); } -static unsigned long a_inl(void __iomem *offset) +static unsigned a8_inb(void __iomem *base, unsigned int offset, unsigned shift) { - return readl(offset); + return readb(base + (offset << shift)); } -static inline void a_insl(void __iomem *offset, void *data, int count) +static unsigned a16_inw(void __iomem *base, unsigned int offset, unsigned shift) { - readsl(offset, data, count); + return readw(base + (offset << shift)); } -/* access happens via a 32 bit bus */ -static const struct accessors access_via_32bit = { - .ob = a_outb, - .ow = a_outw, - .ol = a_outl, - .osl = a_outsl, - .ib = a_inb, - .iw = a_inw, - .il = a_inl, - .isl = a_insl, -}; - -/* ------------------------------------------------------------------------ */ - -static inline void SMC_outb(struct smc91c111_priv *p, unsigned value, - unsigned offset) +static unsigned long a16_inl(void __iomem *base, unsigned int offset, + unsigned shift) { - (p->a.ob)(value, p->base + offset); -} + u32 value; -static inline void SMC_outw(struct smc91c111_priv *p, unsigned value, - unsigned offset) -{ - (p->a.ow)(value, p->base + offset); -} + value = readw(base + (offset << shift)); + value |= readw(base + (offset << shift)) << 16; -static inline void SMC_outl(struct smc91c111_priv *p, unsigned long value, - unsigned offset) -{ - (p->a.ol)(value, p->base + offset); + return value; } -static inline void SMC_outsl(struct smc91c111_priv *p, unsigned offset, - const void *data, int count) +static inline void a16_insl(void __iomem *base, unsigned int offset, void *data, + int count, unsigned shift) { - (p->a.osl)(p->base + offset, data, count); + readsw(base + (offset << shift), data, count * 2); } -static inline unsigned SMC_inb(struct smc91c111_priv *p, unsigned offset) +static unsigned long a32_inl(void __iomem *base, unsigned int offset, + unsigned shift) { - return (p->a.ib)(p->base + offset); + return readl(base + (offset << shift)); } -static inline unsigned SMC_inw(struct smc91c111_priv *p, unsigned offset) +static inline void a32_insl(void __iomem *base, unsigned int offset, void *data, + int count, unsigned shift) { - return (p->a.iw)(p->base + offset); + readsl(base + (offset << shift), data, count); } -static inline unsigned long SMC_inl(struct smc91c111_priv *p, unsigned offset) -{ - return (p->a.il)(p->base + offset); -} +static const struct accessors access_via_16bit = { + .ob = a8_outb, + .ow = a16_outw, + .ol = a16_outl, + .osl = a16_outsl, + .ib = a8_inb, + .iw = a16_inw, + .il = a16_inl, + .isl = a16_insl, +}; -static inline void SMC_insl(struct smc91c111_priv *p, unsigned offset, - void *data, int count) -{ - (p->a.isl)(p->base + offset, data, count); -} +/* access happens via a 32 bit bus */ +static const struct accessors access_via_32bit = { + .ob = a8_outb, + .ow = a16_outw, + .ol = a32_outl, + .osl = a32_outsl, + .ib = a8_inb, + .iw = a16_inw, + .il = a32_inl, + .isl = a32_insl, +}; -static inline void SMC_SELECT_BANK(struct smc91c111_priv *p, int bank) -{ - SMC_outw(p, bank, BANK_SELECT); -} +/* ------------------------------------------------------------------------ */ + +static unsigned last_bank; +#define SMC_outb(p, value, offset) \ + do { \ + PRINTK3("\t%s:%d outb: %s[%1d:0x%04x] = 0x%02x\n", \ + __func__, __LINE__, #offset, last_bank, \ + (offset), (value)); \ + ((p)->a.ob)((value), (p)->base, (offset), (p)->shift); \ + } while (0) + +#define SMC_outw(p, value, offset) \ + do { \ + PRINTK3("\t%s:%d outw: %s[%1d:0x%04x] = 0x%04x\n", \ + __func__, __LINE__, #offset, last_bank, \ + (offset), (value)); \ + ((p)->a.ow)((value), (p)->base, (offset), (p)->shift); \ + } while (0) + +#define SMC_outl(p, value, offset) \ + do { \ + PRINTK3("\t%s:%d outl: %s[%1d:0x%04x] = 0x%08lx\n", \ + __func__, __LINE__, #offset, last_bank, \ + (offset), (unsigned long)(value)); \ + ((p)->a.ol)((value), (p)->base, (offset), (p)->shift); \ + } while (0) + +#define SMC_outsl(p, offset, data, count)\ + do { \ + PRINTK3("\t%s:%d outsl: %5d@%p -> [%1d:0x%04x]\n", \ + __func__, __LINE__, (count) * 4, data, \ + last_bank, (offset)); \ + ((p)->a.osl)((p)->base, (offset), data, (count), \ + (p)->shift); \ + } while (0) + +#define SMC_inb(p, offset) \ + ({ \ + unsigned _v = ((p)->a.ib)((p)->base, (offset), \ + (p)->shift); \ + PRINTK3("\t%s:%d inb: %s[%1d:0x%04x] -> 0x%02x\n", \ + __func__, __LINE__, #offset, last_bank, \ + (offset), _v); \ + _v; }) + +#define SMC_inw(p, offset) \ + ({ \ + unsigned _v = ((p)->a.iw)((p)->base, (offset), \ + (p)->shift); \ + PRINTK3("\t%s:%d inw: %s[%1d:0x%04x] -> 0x%04x\n", \ + __func__, __LINE__, #offset, last_bank, \ + (offset), _v); \ + _v; }) + +#define SMC_inl(p, offset) \ + ({ \ + unsigned long _v = ((p)->a.il)((p)->base, (offset), \ + (p)->shift); \ + PRINTK3("\t%s:%d inl: %s[%1d:0x%04x] -> 0x%08lx\n", \ + __func__, __LINE__, #offset, last_bank, \ + (offset), _v); \ + _v; }) + +#define SMC_insl(p, offset, data, count) \ + ((p)->a.isl)((p)->base, (offset), data, (count), (p)->shift) + +#define SMC_SELECT_BANK(p, bank) \ + do { \ + SMC_outw(p, bank & 0xf, BANK_SELECT); \ + last_bank = bank & 0xf; \ + } while (0) #if SMC_DEBUG > 2 static void print_packet( unsigned char * buf, int length ) @@ -863,6 +935,7 @@ static int smc91c111_phy_read(struct mii_bus *bus, int phyaddr, int phyreg) static void smc91c111_reset(struct eth_device *edev) { struct smc91c111_priv *priv = (struct smc91c111_priv *)edev->priv; + int rev_vers; /* This resets the registers mostly to defaults, but doesn't affect EEPROM. That seems unnecessary */ @@ -878,8 +951,11 @@ static void smc91c111_reset(struct eth_device *edev) /* Release from possible power-down state */ /* Configuration register is not affected by Soft Reset */ - SMC_outw(priv, SMC_inw(priv, CONFIG_REG) | CONFIG_EPH_POWER_EN, - CONFIG_REG); + if (priv->config_setup) + SMC_outw(priv, priv->config_setup, CONFIG_REG); + else + SMC_outw(priv, SMC_inw(priv, CONFIG_REG) | CONFIG_EPH_POWER_EN, + CONFIG_REG); SMC_SELECT_BANK(priv, 0); @@ -892,7 +968,10 @@ static void smc91c111_reset(struct eth_device *edev) /* set the control register */ SMC_SELECT_BANK(priv, 1); - SMC_outw(priv, CTL_DEFAULT, CTL_REG); + if (priv->control_setup) + SMC_outw(priv, priv->control_setup, CTL_REG); + else + SMC_outw(priv, CTL_DEFAULT, CTL_REG); /* Reset the MMU */ SMC_SELECT_BANK(priv, 2); @@ -908,6 +987,14 @@ static void smc91c111_reset(struct eth_device *edev) /* Disable all interrupts */ SMC_outb(priv, 0, IM_REG); + + /* Check chip revision (91c94, 91c96, 91c100, ...) */ + SMC_SELECT_BANK(priv, 3); + rev_vers = SMC_inb(priv, REV_REG); + priv->revision = (rev_vers >> 4) & 0xf; + priv->version = rev_vers & 0xf; + dev_info(edev->parent, "chip is revision=%2d, version=%2d\n", + priv->revision, priv->version); } static void smc91c111_enable(struct eth_device *edev) @@ -927,10 +1014,16 @@ static int smc91c111_eth_open(struct eth_device *edev) /* Configure the Receive/Phy Control register */ SMC_SELECT_BANK(priv, 0); - SMC_outw(priv, RPC_DEFAULT, RPC_REG); + if (priv->revision > 4) + SMC_outw(priv, RPC_DEFAULT, RPC_REG); smc91c111_enable(edev); + if (priv->revision <= 4) { + dev_info(edev->parent, "force link at 10Mpbs on internal phy\n"); + return 0; + } + ret = phy_device_connect(edev, &priv->miibus, 0, NULL, 0, PHY_INTERFACE_MODE_NA); @@ -1333,15 +1426,19 @@ static int smc91c111_probe(struct device_d *dev) edev->priv = (struct smc91c111_priv *)(edev + 1); priv = edev->priv; + priv->a = access_via_32bit; if (dev->platform_data) { struct smc91c111_pdata *pdata = dev->platform_data; priv->qemu_fixup = pdata->qemu_fixup; + priv->shift = pdata->addr_shift; + if (pdata->bus_width == 16) + priv->a = access_via_16bit; + pdata->config_setup = pdata->config_setup; + pdata->control_setup = pdata->control_setup; } - priv->a = access_via_32bit; - edev->init = smc91c111_init_dev; edev->open = smc91c111_eth_open; edev->send = smc91c111_eth_send; @@ -1361,7 +1458,8 @@ static int smc91c111_probe(struct device_d *dev) smc91c111_reset(edev); - mdiobus_register(&priv->miibus); + if (priv->revision > 4) + mdiobus_register(&priv->miibus); eth_register(edev); return 0; |