GIT bc3c2b0f50d05e606f3eebe368fa5bc45328e766 git://electric-eye.fr.zoreil.com/home/romieu/linux-2.6.git#r8169 commit Author: Francois Romieu Date: Sun May 27 22:20:51 2007 +0200 r8169: eeprom read support No need for literacy. Ugo's API is clear. I have added a size member to the eeprom struct for convenience and filled it according to the National Semiconductor NM93C06/C46/C56/C66 documentation. Signed-off-by: Francois Romieu Cc: Michael Wu Cc: John W. Linville Cc: Edward Hsu commit 9615c1540eff93b6cb2a41390307c181da167a47 Author: Francois Romieu Date: Wed Jul 4 23:24:55 2007 +0100 r8169: MSI support It is currently limited to 0x8136 and 0x8168. 8169sb/8110sb ought to handle it as well where they support MSI. Signed-off-by: Francois Romieu Cc: Edward Hsu Tester-Cc: Rolf Eike Beer drivers/net/Kconfig | 1 drivers/net/r8169.c | 180 ++++++++++++++++++++++++++++++++++++++++-- include/linux/eeprom_93cx6.h | 4 + 3 files changed, 174 insertions(+), 11 deletions(-) diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index f8a602c..365bc89 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -2111,6 +2111,7 @@ config R8169 tristate "Realtek 8169 gigabit ethernet support" depends on PCI select CRC32 + select EEPROM_93CX6 ---help--- Say Y here if you have a Realtek 8169 PCI Gigabit Ethernet adapter. diff --git a/drivers/net/r8169.c b/drivers/net/r8169.c index bb6896a..d886f7a 100644 --- a/drivers/net/r8169.c +++ b/drivers/net/r8169.c @@ -23,6 +23,7 @@ #include #include #include #include +#include #include #include @@ -256,9 +257,16 @@ enum rtl_register_content { NPQ = 0x40, /* Poll cmd on the low prio queue */ FSWInt = 0x01, /* Forced software interrupt */ - /* Cfg9346Bits */ - Cfg9346_Lock = 0x00, + /* Cfg9346 operating mode register p.23 */ Cfg9346_Unlock = 0xc0, + Cfg9346_Prog = 0x80, + Cfg9346_Auto = 0x80, + Cfg9346_Lock = 0x00, + /* Sub-mode bits in Programming or Auto-load mode. */ + Cfg9346_CS = 0x08, /* Chip Select */ + Cfg9346_SK = 0x04, /* Serial Data Clock */ + Cfg9346_DI = 0x02, /* Data In (going into the eeprom) */ + Cfg9346_DO = 0x01, /* Data Out (coming from the eeprom) */ /* rx_mode_bits */ AcceptErr = 0x20, @@ -268,15 +276,17 @@ enum rtl_register_content { AcceptMyPhys = 0x02, AcceptAllPhys = 0x01, - /* RxConfigBits */ + /* RxConfigBits p.21 */ RxCfgFIFOShift = 13, RxCfgDMAShift = 8, + Cfg9356Select = 6, /* TxConfigBits */ TxInterFrameGapShift = 24, TxDMAShift = 8, /* DMA burst value (0-7) is shift this many bits */ /* Config1 register p.24 */ + MSIEnable = (1 << 5), /* Enable Message Signaled Interrupt */ PMEnable = (1 << 0), /* Power Management Enable */ /* Config2 register p. 25 */ @@ -418,8 +428,10 @@ #endif void (*hw_start)(struct net_device *); unsigned int (*phy_reset_pending)(void __iomem *); unsigned int (*link_ok)(void __iomem *); + struct eeprom_93cx6 eeprom; struct delayed_work task; unsigned wol_enabled : 1; + unsigned msi : 1; }; MODULE_AUTHOR("Realtek and the Linux r8169 crew "); @@ -647,6 +659,58 @@ static int rtl8169_get_regs_len(struct n return R8169_REGS_SIZE; } +static int rtl_get_eeprom_len(struct net_device *dev) +{ + struct rtl8169_private *tp = netdev_priv(dev); + + return tp->eeprom.size; +} + +static void eeprom_cmd_start(void __iomem *ioaddr) +{ + RTL_W8(Cfg9346, Cfg9346_Prog); +} + +static void eeprom_cmd_end(void __iomem *ioaddr) +{ + RTL_W8(Cfg9346, Cfg9346_Lock); +} + +static int rtl_get_eeprom(struct net_device *dev, struct ethtool_eeprom *ee, + u8 *data) +{ + struct rtl8169_private *tp = netdev_priv(dev); + struct eeprom_93cx6 *eeprom = &tp->eeprom; + void __iomem *ioaddr = tp->mmio_addr; + u32 offset = ee->offset; + u32 len = ee->len; + u16 reg; + + spin_lock_irq(&tp->lock); + + eeprom_cmd_start(ioaddr); + + if (offset & 0x1) { + eeprom_93cx6_read(eeprom, offset >> 1, ®); + *data++ = cpu_to_le16(reg) >> 8; + offset++; + len--; + } + + eeprom_93cx6_multiread(eeprom, offset >> 1, (__le16 *)data, len >> 1); + + if (len & 0x1) { + eeprom_93cx6_read(eeprom, (offset >> 1) + (len >> 1) + 1, ®); + data[len] = cpu_to_le16(reg); + } + + eeprom_cmd_end(ioaddr); + + spin_unlock_irq(&tp->lock); + + return 0; +} + static int rtl8169_set_speed_tbi(struct net_device *dev, u8 autoneg, u16 speed, u8 duplex) { @@ -1048,6 +1112,8 @@ static const struct ethtool_ops rtl8169_ .get_drvinfo = rtl8169_get_drvinfo, .get_regs_len = rtl8169_get_regs_len, .get_link = ethtool_op_get_link, + .get_eeprom_len = rtl_get_eeprom_len, + .get_eeprom = rtl_get_eeprom, .get_settings = rtl8169_get_settings, .set_settings = rtl8169_set_settings, .get_msglevel = rtl8169_get_msglevel, @@ -1380,6 +1446,63 @@ static void rtl8169_init_phy(struct net_ printk(KERN_INFO PFX "%s: TBI auto-negotiating\n", dev->name); } +static void rtl_93cx6_register_read(struct eeprom_93cx6 *eeprom) +{ + struct net_device *dev = eeprom->data; + struct rtl8169_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + u8 reg; + + reg = RTL_R8(Cfg9346); + + eeprom->reg_data_in = reg & Cfg9346_DI; + eeprom->reg_data_out = reg & Cfg9346_DO; + eeprom->reg_data_clock = reg & Cfg9346_SK; + eeprom->reg_chip_select = reg & Cfg9346_CS; +} + +static void rtl_93cx6_register_write(struct eeprom_93cx6 *eeprom) +{ + struct net_device *dev = eeprom->data; + struct rtl8169_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + u8 reg = Cfg9346_Prog; + + if (eeprom->reg_data_in) + reg |= Cfg9346_DI; + if (eeprom->reg_data_out) + reg |= Cfg9346_DO; + if (eeprom->reg_data_clock) + reg |= Cfg9346_SK; + if (eeprom->reg_chip_select) + reg |= Cfg9346_CS; + + RTL_W8(Cfg9346, reg); + /* PCI commit */ + RTL_R8(ChipCmd); + /* This is not a posting bug band-aid: the eeprom wants ~250 ns. */ + ndelay(250); +} + +static void rtl_init_eeprom(struct net_device *dev, struct rtl8169_private *tp) +{ + struct eeprom_93cx6 *eeprom = &tp->eeprom; + void __iomem *ioaddr = tp->mmio_addr; + + eeprom->data = dev; + + eeprom->register_read = rtl_93cx6_register_read; + eeprom->register_write = rtl_93cx6_register_write; + + if (RTL_R32(RxConfig) & Cfg9356Select) { + eeprom->width = PCI_EEPROM_WIDTH_93C56; + eeprom->size = 256; + } else { + eeprom->width = PCI_EEPROM_WIDTH_93C46; + eeprom->size = 128; + } +} + static void rtl_rar_set(struct rtl8169_private *tp, u8 *addr) { void __iomem *ioaddr = tp->mmio_addr; @@ -1446,6 +1569,7 @@ static const struct rtl_cfg_info { unsigned int align; u16 intr_event; u16 napi_event; + unsigned msi : 1; } rtl_cfg_infos [] = { [RTL_CFG_0] = { .hw_start = rtl_hw_start_8169, @@ -1453,7 +1577,8 @@ static const struct rtl_cfg_info { .align = 0, .intr_event = SYSErr | LinkChg | RxOverflow | RxFIFOOver | TxErr | TxOK | RxOK | RxErr, - .napi_event = RxFIFOOver | TxErr | TxOK | RxOK | RxOverflow + .napi_event = RxFIFOOver | TxErr | TxOK | RxOK | RxOverflow, + .msi = 0 }, [RTL_CFG_1] = { .hw_start = rtl_hw_start_8168, @@ -1461,7 +1586,8 @@ static const struct rtl_cfg_info { .align = 8, .intr_event = SYSErr | LinkChg | RxOverflow | TxErr | TxOK | RxOK | RxErr, - .napi_event = TxErr | TxOK | RxOK | RxOverflow + .napi_event = TxErr | TxOK | RxOK | RxOverflow, + .msi = 1 }, [RTL_CFG_2] = { .hw_start = rtl_hw_start_8101, @@ -1469,10 +1595,39 @@ static const struct rtl_cfg_info { .align = 8, .intr_event = SYSErr | LinkChg | RxOverflow | PCSTimeout | RxFIFOOver | TxErr | TxOK | RxOK | RxErr, - .napi_event = RxFIFOOver | TxErr | TxOK | RxOK | RxOverflow + .napi_event = RxFIFOOver | TxErr | TxOK | RxOK | RxOverflow, + .msi = 1 } }; +/* Cfg9346_Unlock assumed. */ +static bool rtl_try_msi(struct pci_dev *pdev, void __iomem *ioaddr, + const struct rtl_cfg_info *cfg) +{ + bool msi = false; + u8 cfg2; + + cfg2 = RTL_R8(Config2) & ~MSIEnable; + if (cfg->msi) { + if (pci_enable_msi(pdev)) { + dev_info(&pdev->dev, "no MSI. Back to INTx.\n"); + } else { + cfg2 |= MSIEnable; + msi = true; + } + } + RTL_W8(Config2, cfg2); + return msi; +} + +static void rtl_disable_msi(struct pci_dev *pdev, struct rtl8169_private *tp) +{ + if (tp->msi) { + pci_disable_msi(pdev); + tp->msi = 0; + } +} + static int __devinit rtl8169_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { @@ -1609,6 +1764,7 @@ rtl8169_init_one(struct pci_dev *pdev, c RTL_W8(Cfg9346, Cfg9346_Unlock); RTL_W8(Config1, RTL_R8(Config1) | PMEnable); RTL_W8(Config5, RTL_R8(Config5) & PMEStatus); + tp->msi = rtl_try_msi(pdev, ioaddr, cfg); RTL_W8(Cfg9346, Cfg9346_Lock); if (RTL_R8(PHYstatus) & TBI_Enable) { @@ -1669,6 +1825,8 @@ #endif tp->intr_event = cfg->intr_event; tp->napi_event = cfg->napi_event; + rtl_init_eeprom(dev, tp); + init_timer(&tp->timer); tp->timer.data = (unsigned long) dev; tp->timer.function = rtl8169_phy_timer; @@ -1677,7 +1835,7 @@ #endif rc = register_netdev(dev); if (rc < 0) - goto err_out_unmap_5; + goto err_out_msi_5; pci_set_drvdata(pdev, dev); @@ -1700,7 +1858,8 @@ #endif out: return rc; -err_out_unmap_5: +err_out_msi_5: + rtl_disable_msi(pdev, tp); iounmap(ioaddr); err_out_free_res_4: pci_release_regions(pdev); @@ -1721,6 +1880,7 @@ static void __devexit rtl8169_remove_one flush_scheduled_work(); unregister_netdev(dev); + rtl_disable_msi(pdev, tp); rtl8169_release_board(pdev, dev, tp->mmio_addr); pci_set_drvdata(pdev, NULL); } @@ -1764,8 +1924,8 @@ static int rtl8169_open(struct net_devic smp_mb(); - retval = request_irq(dev->irq, rtl8169_interrupt, IRQF_SHARED, - dev->name, dev); + retval = request_irq(dev->irq, rtl8169_interrupt, + tp->msi ? 0 : IRQF_SHARED, dev->name, dev); if (retval < 0) goto err_release_ring_2; diff --git a/include/linux/eeprom_93cx6.h b/include/linux/eeprom_93cx6.h index d774b77..ced7730 100644 --- a/include/linux/eeprom_93cx6.h +++ b/include/linux/eeprom_93cx6.h @@ -21,13 +21,14 @@ /* Module: eeprom_93cx6 Abstract: EEPROM reader datastructures for 93cx6 chipsets. - Supported chipsets: 93c46 & 93c66. + Supported chipsets: 93c46/93c56/93c66. */ /* * EEPROM operation defines. */ #define PCI_EEPROM_WIDTH_93C46 6 +#define PCI_EEPROM_WIDTH_93C56 8 #define PCI_EEPROM_WIDTH_93C66 8 #define PCI_EEPROM_WIDTH_OPCODE 3 #define PCI_EEPROM_WRITE_OPCODE 0x05 @@ -59,6 +60,7 @@ struct eeprom_93cx6 { void (*register_write)(struct eeprom_93cx6 *eeprom); int width; + int size; char reg_data_in; char reg_data_out;