summaryrefslogtreecommitdiffstats
path: root/drivers/net/smc91111.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/smc91111.c')
-rw-r--r--drivers/net/smc91111.c254
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;