[PATCH] 8139cp: add ethtool eeprom support
Implement the ethtool eeprom operations for the 8139cp driver. Tested on x86 and big-endian ARM. Signed-off-by: Philip Craig <philipc@snapgear.com> Signed-off-by: Jeff Garzik <jeff@garzik.org>
This commit is contained in:
parent
d73f1e3c3b
commit
722fdb3359
1 changed files with 167 additions and 12 deletions
|
@ -401,6 +401,11 @@ static void cp_clean_rings (struct cp_private *cp);
|
|||
#ifdef CONFIG_NET_POLL_CONTROLLER
|
||||
static void cp_poll_controller(struct net_device *dev);
|
||||
#endif
|
||||
static int cp_get_eeprom_len(struct net_device *dev);
|
||||
static int cp_get_eeprom(struct net_device *dev,
|
||||
struct ethtool_eeprom *eeprom, u8 *data);
|
||||
static int cp_set_eeprom(struct net_device *dev,
|
||||
struct ethtool_eeprom *eeprom, u8 *data);
|
||||
|
||||
static struct pci_device_id cp_pci_tbl[] = {
|
||||
{ PCI_VENDOR_ID_REALTEK, PCI_DEVICE_ID_REALTEK_8139,
|
||||
|
@ -1577,6 +1582,9 @@ static struct ethtool_ops cp_ethtool_ops = {
|
|||
.get_strings = cp_get_strings,
|
||||
.get_ethtool_stats = cp_get_ethtool_stats,
|
||||
.get_perm_addr = ethtool_op_get_perm_addr,
|
||||
.get_eeprom_len = cp_get_eeprom_len,
|
||||
.get_eeprom = cp_get_eeprom,
|
||||
.set_eeprom = cp_set_eeprom,
|
||||
};
|
||||
|
||||
static int cp_ioctl (struct net_device *dev, struct ifreq *rq, int cmd)
|
||||
|
@ -1612,24 +1620,32 @@ static int cp_ioctl (struct net_device *dev, struct ifreq *rq, int cmd)
|
|||
#define eeprom_delay() readl(ee_addr)
|
||||
|
||||
/* The EEPROM commands include the alway-set leading bit. */
|
||||
#define EE_EXTEND_CMD (4)
|
||||
#define EE_WRITE_CMD (5)
|
||||
#define EE_READ_CMD (6)
|
||||
#define EE_ERASE_CMD (7)
|
||||
|
||||
static int read_eeprom (void __iomem *ioaddr, int location, int addr_len)
|
||||
{
|
||||
int i;
|
||||
unsigned retval = 0;
|
||||
void __iomem *ee_addr = ioaddr + Cfg9346;
|
||||
int read_cmd = location | (EE_READ_CMD << addr_len);
|
||||
#define EE_EWDS_ADDR (0)
|
||||
#define EE_WRAL_ADDR (1)
|
||||
#define EE_ERAL_ADDR (2)
|
||||
#define EE_EWEN_ADDR (3)
|
||||
|
||||
#define CP_EEPROM_MAGIC PCI_DEVICE_ID_REALTEK_8139
|
||||
|
||||
static void eeprom_cmd_start(void __iomem *ee_addr)
|
||||
{
|
||||
writeb (EE_ENB & ~EE_CS, ee_addr);
|
||||
writeb (EE_ENB, ee_addr);
|
||||
eeprom_delay ();
|
||||
}
|
||||
|
||||
/* Shift the read command bits out. */
|
||||
for (i = 3 + addr_len - 1; i >= 0; i--) {
|
||||
int dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
|
||||
static void eeprom_cmd(void __iomem *ee_addr, int cmd, int cmd_len)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Shift the command bits out. */
|
||||
for (i = cmd_len - 1; i >= 0; i--) {
|
||||
int dataval = (cmd & (1 << i)) ? EE_DATA_WRITE : 0;
|
||||
writeb (EE_ENB | dataval, ee_addr);
|
||||
eeprom_delay ();
|
||||
writeb (EE_ENB | dataval | EE_SHIFT_CLK, ee_addr);
|
||||
|
@ -1637,6 +1653,33 @@ static int read_eeprom (void __iomem *ioaddr, int location, int addr_len)
|
|||
}
|
||||
writeb (EE_ENB, ee_addr);
|
||||
eeprom_delay ();
|
||||
}
|
||||
|
||||
static void eeprom_cmd_end(void __iomem *ee_addr)
|
||||
{
|
||||
writeb (~EE_CS, ee_addr);
|
||||
eeprom_delay ();
|
||||
}
|
||||
|
||||
static void eeprom_extend_cmd(void __iomem *ee_addr, int extend_cmd,
|
||||
int addr_len)
|
||||
{
|
||||
int cmd = (EE_EXTEND_CMD << addr_len) | (extend_cmd << (addr_len - 2));
|
||||
|
||||
eeprom_cmd_start(ee_addr);
|
||||
eeprom_cmd(ee_addr, cmd, 3 + addr_len);
|
||||
eeprom_cmd_end(ee_addr);
|
||||
}
|
||||
|
||||
static u16 read_eeprom (void __iomem *ioaddr, int location, int addr_len)
|
||||
{
|
||||
int i;
|
||||
u16 retval = 0;
|
||||
void __iomem *ee_addr = ioaddr + Cfg9346;
|
||||
int read_cmd = location | (EE_READ_CMD << addr_len);
|
||||
|
||||
eeprom_cmd_start(ee_addr);
|
||||
eeprom_cmd(ee_addr, read_cmd, 3 + addr_len);
|
||||
|
||||
for (i = 16; i > 0; i--) {
|
||||
writeb (EE_ENB | EE_SHIFT_CLK, ee_addr);
|
||||
|
@ -1648,13 +1691,125 @@ static int read_eeprom (void __iomem *ioaddr, int location, int addr_len)
|
|||
eeprom_delay ();
|
||||
}
|
||||
|
||||
/* Terminate the EEPROM access. */
|
||||
writeb (~EE_CS, ee_addr);
|
||||
eeprom_delay ();
|
||||
eeprom_cmd_end(ee_addr);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void write_eeprom(void __iomem *ioaddr, int location, u16 val,
|
||||
int addr_len)
|
||||
{
|
||||
int i;
|
||||
void __iomem *ee_addr = ioaddr + Cfg9346;
|
||||
int write_cmd = location | (EE_WRITE_CMD << addr_len);
|
||||
|
||||
eeprom_extend_cmd(ee_addr, EE_EWEN_ADDR, addr_len);
|
||||
|
||||
eeprom_cmd_start(ee_addr);
|
||||
eeprom_cmd(ee_addr, write_cmd, 3 + addr_len);
|
||||
eeprom_cmd(ee_addr, val, 16);
|
||||
eeprom_cmd_end(ee_addr);
|
||||
|
||||
eeprom_cmd_start(ee_addr);
|
||||
for (i = 0; i < 20000; i++)
|
||||
if (readb(ee_addr) & EE_DATA_READ)
|
||||
break;
|
||||
eeprom_cmd_end(ee_addr);
|
||||
|
||||
eeprom_extend_cmd(ee_addr, EE_EWDS_ADDR, addr_len);
|
||||
}
|
||||
|
||||
static int cp_get_eeprom_len(struct net_device *dev)
|
||||
{
|
||||
struct cp_private *cp = netdev_priv(dev);
|
||||
int size;
|
||||
|
||||
spin_lock_irq(&cp->lock);
|
||||
size = read_eeprom(cp->regs, 0, 8) == 0x8129 ? 256 : 128;
|
||||
spin_unlock_irq(&cp->lock);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static int cp_get_eeprom(struct net_device *dev,
|
||||
struct ethtool_eeprom *eeprom, u8 *data)
|
||||
{
|
||||
struct cp_private *cp = netdev_priv(dev);
|
||||
unsigned int addr_len;
|
||||
u16 val;
|
||||
u32 offset = eeprom->offset >> 1;
|
||||
u32 len = eeprom->len;
|
||||
u32 i = 0;
|
||||
|
||||
eeprom->magic = CP_EEPROM_MAGIC;
|
||||
|
||||
spin_lock_irq(&cp->lock);
|
||||
|
||||
addr_len = read_eeprom(cp->regs, 0, 8) == 0x8129 ? 8 : 6;
|
||||
|
||||
if (eeprom->offset & 1) {
|
||||
val = read_eeprom(cp->regs, offset, addr_len);
|
||||
data[i++] = (u8)(val >> 8);
|
||||
offset++;
|
||||
}
|
||||
|
||||
while (i < len - 1) {
|
||||
val = read_eeprom(cp->regs, offset, addr_len);
|
||||
data[i++] = (u8)val;
|
||||
data[i++] = (u8)(val >> 8);
|
||||
offset++;
|
||||
}
|
||||
|
||||
if (i < len) {
|
||||
val = read_eeprom(cp->regs, offset, addr_len);
|
||||
data[i] = (u8)val;
|
||||
}
|
||||
|
||||
spin_unlock_irq(&cp->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cp_set_eeprom(struct net_device *dev,
|
||||
struct ethtool_eeprom *eeprom, u8 *data)
|
||||
{
|
||||
struct cp_private *cp = netdev_priv(dev);
|
||||
unsigned int addr_len;
|
||||
u16 val;
|
||||
u32 offset = eeprom->offset >> 1;
|
||||
u32 len = eeprom->len;
|
||||
u32 i = 0;
|
||||
|
||||
if (eeprom->magic != CP_EEPROM_MAGIC)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irq(&cp->lock);
|
||||
|
||||
addr_len = read_eeprom(cp->regs, 0, 8) == 0x8129 ? 8 : 6;
|
||||
|
||||
if (eeprom->offset & 1) {
|
||||
val = read_eeprom(cp->regs, offset, addr_len) & 0xff;
|
||||
val |= (u16)data[i++] << 8;
|
||||
write_eeprom(cp->regs, offset, val, addr_len);
|
||||
offset++;
|
||||
}
|
||||
|
||||
while (i < len - 1) {
|
||||
val = (u16)data[i++];
|
||||
val |= (u16)data[i++] << 8;
|
||||
write_eeprom(cp->regs, offset, val, addr_len);
|
||||
offset++;
|
||||
}
|
||||
|
||||
if (i < len) {
|
||||
val = read_eeprom(cp->regs, offset, addr_len) & 0xff00;
|
||||
val |= (u16)data[i];
|
||||
write_eeprom(cp->regs, offset, val, addr_len);
|
||||
}
|
||||
|
||||
spin_unlock_irq(&cp->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Put the board into D3cold state and wait for WakeUp signal */
|
||||
static void cp_set_d3_state (struct cp_private *cp)
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue