GIT 35583f73c45763931d01543c089a27dd51bcc3ae master.kernel.org:/pub/scm/linux/kernel/git/jgarzik/netdev-2.6.git#smc91x-eeprom

---
Index: drivers/net/smc91x.c
===================================================================
--- 3e84d1d21b77d739af7bca35a0da7ef481f7e6d6/drivers/net/smc91x.c  (mode:100644)
+++ 425d7be63270056d62d3d561048c48e992f91d87/drivers/net/smc91x.c  (mode:100644)
@@ -1722,6 +1722,160 @@
 	lp->msg_enable = level;
 }
 
+/*
+ * Must be called with lp->lock locked. Caller must select bank 2 again.
+ */
+static int smc_read_eeprom_reg(void __iomem *ioaddr, unsigned int reg)
+{
+	unsigned int timeout;
+
+	SMC_SELECT_BANK(2);
+	SMC_SET_PTR(reg);
+
+	SMC_SELECT_BANK(1);
+	SMC_SET_CTL(SMC_GET_CTL() | CTL_EEPROM_SELECT | CTL_RELOAD);
+	timeout = 100;
+	while ((SMC_GET_CTL() & CTL_RELOAD) && --timeout)
+		udelay(100);
+	if (timeout == 0) {
+		printk(KERN_INFO "%s: timeout reading EEPROM register %02x\n",
+			CARDNAME, reg);
+		return -EIO;
+	}
+
+	return SMC_GET_GP();
+}
+
+/*
+ * Must be called with lp->lock locked. Caller must select bank 2 again.
+ */
+static int smc_write_eeprom_reg(void __iomem *ioaddr, unsigned int reg,
+				unsigned int val)
+{
+	unsigned int timeout;
+
+	SMC_SELECT_BANK(2);
+	SMC_SET_PTR(reg);
+
+	SMC_SELECT_BANK(1);
+	SMC_SET_GP(val);
+	SMC_SET_CTL(SMC_GET_CTL() | CTL_EEPROM_SELECT | CTL_STORE);
+	timeout = 100;
+	while ((SMC_GET_CTL() & CTL_STORE) && --timeout)
+		udelay(100);
+	if (timeout == 0) {
+		printk(KERN_INFO "%s: timeout writing EEPROM register %02x\n",
+			CARDNAME, reg);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int smc_ethtool_geteepromlen(struct net_device *dev)
+{
+	return SMC_EEPROM_SIZE;
+}
+
+static int smc_ethtool_geteeprom(struct net_device *dev,
+				 struct ethtool_eeprom *eeprom, u8 *data)
+{
+
+	struct smc_local *lp = netdev_priv(dev);
+	void __iomem *ioaddr = lp->base;
+	unsigned reg;
+	int ret, len;
+
+	len = eeprom->len;
+	reg = eeprom->offset >> 1;
+	eeprom->len = 0;
+	eeprom->magic = SMC_EEPROM_MAGIC;
+
+	spin_lock_irq(&lp->lock);
+
+	if (eeprom->offset & 1) {
+		ret = smc_read_eeprom_reg(ioaddr, reg++);
+		if (ret < 0)
+			goto out;
+		*data++ = ret >> 8;
+	        eeprom->len++;
+		len--;
+	}
+	while (len) {
+		len -= 2;
+		ret = smc_read_eeprom_reg(ioaddr, reg++);
+		if (ret < 0)
+			goto out;
+		*data++ = ret & 0xff;
+		if (len < 0) {
+			eeprom->len++;
+			break;
+		}
+		*data++ = ret >> 8;
+		eeprom->len += 2;
+	}
+
+	ret = 0;
+out:
+	SMC_SELECT_BANK(2);
+	spin_unlock_irq(&lp->lock);
+
+	return ret;
+}
+
+static int smc_ethtool_seteeprom(struct net_device *dev,
+				 struct ethtool_eeprom *eeprom, u8 *data)
+{
+	struct smc_local *lp = netdev_priv(dev);
+	void __iomem *ioaddr = lp->base;
+	unsigned reg;
+	int ret, len;
+
+	if (eeprom->magic != SMC_EEPROM_MAGIC)
+		return -EINVAL;
+
+	reg = eeprom->offset >> 1;
+	len = eeprom->len;
+
+	spin_lock_irq(&lp->lock);
+
+	if (eeprom->offset & 1) {
+		ret = smc_read_eeprom_reg(ioaddr, reg);
+		if (ret < 0)
+			goto out;
+		ret = (ret & 0xff) | ((int)*data++ << 8);
+		ret = smc_write_eeprom_reg(ioaddr, reg++, ret);
+		if (ret < 0)
+			goto out;
+		len--;
+	}
+	while (len) {
+		len -= 2;
+		if (len < 0) {
+			ret = smc_read_eeprom_reg(ioaddr, reg);
+			if (ret < 0)
+				goto out;
+			ret = (ret & 0xff) | *data;
+			ret = smc_write_eeprom_reg(ioaddr, reg, ret);
+			if (ret < 0)
+				goto out;
+			break;
+		}
+		ret = *data++;
+		ret |= (int)*data++ << 8;
+		ret = smc_write_eeprom_reg(ioaddr, reg++, ret);
+		if (ret < 0)
+			goto out;
+	}
+
+	ret = 0;
+out:
+	SMC_SELECT_BANK(2);
+	spin_unlock_irq(&lp->lock);
+
+	return ret;
+}
+
 static struct ethtool_ops smc_ethtool_ops = {
 	.get_settings	= smc_ethtool_getsettings,
 	.set_settings	= smc_ethtool_setsettings,
@@ -1731,8 +1885,9 @@
 	.set_msglevel	= smc_ethtool_setmsglevel,
 	.nway_reset	= smc_ethtool_nwayreset,
 	.get_link	= ethtool_op_get_link,
-//	.get_eeprom	= smc_ethtool_geteeprom,
-//	.set_eeprom	= smc_ethtool_seteeprom,
+	.get_eeprom_len	= smc_ethtool_geteepromlen,
+	.get_eeprom	= smc_ethtool_geteeprom,
+	.set_eeprom	= smc_ethtool_seteeprom,
 };
 
 /*
Index: drivers/net/smc91x.h
===================================================================
--- 3e84d1d21b77d739af7bca35a0da7ef481f7e6d6/drivers/net/smc91x.h  (mode:100644)
+++ 425d7be63270056d62d3d561048c48e992f91d87/drivers/net/smc91x.h  (mode:100644)
@@ -404,6 +404,13 @@
 #define SMC_DATA_EXTENT (4)
 
 /*
+ * 128 bytes serial EEPROM
+ */
+#define SMC_EEPROM_SIZE		128
+
+#define SMC_EEPROM_MAGIC	0x3A8EBEEF
+
+/*
  . Bank Select Register:
  .
  .		yyyy yyyy 0000 00xx
@@ -834,6 +841,8 @@
 #define SMC_GET_CONFIG()	SMC_inw( ioaddr, CONFIG_REG )
 #define SMC_SET_CONFIG(x)	SMC_outw( x, ioaddr, CONFIG_REG )
 #define SMC_GET_COUNTER()	SMC_inw( ioaddr, COUNTER_REG )
+#define SMC_GET_GP()		SMC_inw( ioaddr, GP_REG )
+#define SMC_SET_GP(x)		SMC_outw( x, ioaddr, GP_REG )
 #define SMC_GET_CTL()		SMC_inw( ioaddr, CTL_REG )
 #define SMC_SET_CTL(x)		SMC_outw( x, ioaddr, CTL_REG )
 #define SMC_GET_MII()		SMC_inw( ioaddr, MII_REG )