From patchwork Fri Jul 19 09:48:55 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Peter Wu X-Patchwork-Id: 260232 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 1E2992C008C for ; Fri, 19 Jul 2013 19:49:08 +1000 (EST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1759970Ab3GSJtB (ORCPT ); Fri, 19 Jul 2013 05:49:01 -0400 Received: from mail-wg0-f49.google.com ([74.125.82.49]:44930 "EHLO mail-wg0-f49.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1759915Ab3GSJtA (ORCPT ); Fri, 19 Jul 2013 05:49:00 -0400 Received: by mail-wg0-f49.google.com with SMTP id a12so3868742wgh.16 for ; Fri, 19 Jul 2013 02:48:58 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:user-agent:mime-version :content-transfer-encoding:content-type; bh=ulvJuBqPjatLxUIGSoG6OmzIN0Vnift3TcK3Ipe2XcE=; b=FQCOtj8KA/7HUEJOZwaSbiM6Hhp2LVllGIDTWoC9iXfRHRn6K+7kIbmyXkJ3BKZWYm 9nhAuApO2VVsF+0hTwmwECx3OMl87gKXqRC0c6U0CFqBblZ0AaTpbosQAE78fA/mr0Eo SMwKbSoRfkQZoyr2HX1wdvMK36dvwNvAaVlOi+RC3Lkz1WstBYD5C7+D2cRaR26sX3NC oknczegGcJV+u56/492SDPQCNWAg8op+9vHpxxI58OvZ8NXO/jnKjsm6FJUc6NTMz/H+ ezb/tpAS08hPaOBvE+01wMnqaNOpy2obVH89PaoOLZt58sS2S+gmHmHRiqVmP205wlwB NSSQ== X-Received: by 10.180.12.45 with SMTP id v13mr22003114wib.7.1374227338842; Fri, 19 Jul 2013 02:48:58 -0700 (PDT) Received: from al.localnet (al.lekensteyn.nl. [2001:470:1f15:b83::c0d1:f1ed]) by mx.google.com with ESMTPSA id mb20sm33974393wic.1.2013.07.19.02.48.56 for (version=TLSv1.2 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Fri, 19 Jul 2013 02:48:57 -0700 (PDT) From: Peter Wu To: Francois Romieu , netdev@vger.kernel.org Cc: Realtek linux nic maintainers Subject: [RFC] EEPROM dumping/changing code for r8169 driver Date: Fri, 19 Jul 2013 11:48:55 +0200 Message-ID: <1906856.2MoFjQXLhS@al> User-Agent: KMail/4.10.5 (Linux/3.10.0-1-custom; KDE/4.10.5; x86_64; ; ) MIME-Version: 1.0 Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Hi, As noted in another mail, I have been working on adding EEPROM reading writing support for the r8169 driver. The below diff adds basic support, but it is not completely finished yet: - debug code is still present - Kconfig help text might be reworded - No check for EEPROM size, currently it assumed 1K bits, but 2K bits also exist. This is low-hanging fruit, but as I did not have hardware with 93c56, I did not bother to add it right now. (according to the Realtek r8168 driver, there are also configurations without these 93cx6 chips, but an EEPROM with "TWSI" which these changes currently do not account for at all) There is an issue with reading though, the initial read fails until a certain action has happened[1]. In the case of an external RTL8169sb GBe PCI card (RTL869SC according to the text on the chip), a write to the EEPROM has to happen first. Could it be a timing issue? I have several PDFs available, but the most detailed one is a PDF with the title "xRTL8169spec_1.21.DOC" (from 2002) which also has a section "11.3.2 Serial EEPROM Interface Timing". Regards, Peter [1]: http://marc.info/?l=linux-netdev&m=137422584013574 --- -- To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html diff --git a/drivers/net/ethernet/realtek/Kconfig b/drivers/net/ethernet/realtek/Kconfig index ae5d027..ee70de2 100644 --- a/drivers/net/ethernet/realtek/Kconfig +++ b/drivers/net/ethernet/realtek/Kconfig @@ -112,4 +112,13 @@ config R8169 To compile this driver as a module, choose M here: the module will be called r8169. This is recommended. +config R8169_93CX6 + bool "Realtek 8169 external 93CX6 EEPROM support" + depends on R8169 + select EEPROM_93CX6 + ---help--- + Select this if your want to access EEPROM of the Realtek 8169 PCI + Gigabit Ethernet adapter. This is necessary for dumping or changing + EEPROM using ethtool. If unsure, say N. + endif # NET_VENDOR_REALTEK diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c index 4106a74..dc5d11c 100644 --- a/drivers/net/ethernet/realtek/r8169.c +++ b/drivers/net/ethernet/realtek/r8169.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -412,7 +413,8 @@ enum rtl8168_8101_registers { DBG_REG = 0xd1, #define FIX_NAK_1 (1 << 4) #define FIX_NAK_2 (1 << 3) - TWSI = 0xd2, + TWSI = 0xd2, /* Two Wire Serial Interface */ +#define TWSI_TYPE_EEPROM (1 << 2) MCU = 0xd3, #define NOW_IS_OOB (1 << 7) #define TX_EMPTY (1 << 5) @@ -504,8 +506,14 @@ enum rtl_register_content { FSWInt = 0x01, /* Forced software interrupt */ /* Cfg9346Bits */ + Cfg9346_mask = 0xc0, Cfg9346_Lock = 0x00, Cfg9346_Unlock = 0xc0, + Cfg9346_Program = 0x80, /* Programming mode */ + Cfg9346_EECS = 0x08, /* Chip select */ + Cfg9346_EESK = 0x04, /* Serial data clock */ + Cfg9346_EEDI = 0x02, /* Data input */ + Cfg9346_EEDO = 0x01, /* Data output */ /* rx_mode_bits */ AcceptErr = 0x20, @@ -1643,6 +1651,172 @@ static int rtl8169_get_regs_len(struct net_device *dev) return R8169_REGS_SIZE; } +#ifdef CONFIG_R8169_93CX6 +static int rtl8169_get_eeprom_len(struct net_device *dev) { + /* TODO do not hard code EEPROM size, detect it! */ + /* 93c46 -> 1K bit, 93c56 -> 2K bit */ + return 1024 / 8; /* must be ONE byte! */ +} + +static void rtl_eeprom_read(struct eeprom_93cx6 *eeprom) +{ + struct rtl8169_private *tp = eeprom->data; + void __iomem *ioaddr = tp->mmio_addr; + u8 reg; + + reg = RTL_R8(Cfg9346); + + eeprom->reg_data_in = reg & Cfg9346_EEDI; + eeprom->reg_data_out = reg & Cfg9346_EEDO; + eeprom->reg_data_clock = reg & Cfg9346_EESK; + eeprom->reg_chip_select = reg & Cfg9346_EECS; + if (0 && !(reg & Cfg9346_EECS)) { /* DEBUG - remove me! */ + struct net_device *dev; + dev = (struct net_device *)((char *) tp - ALIGN(sizeof(struct net_device), NETDEV_ALIGN)); + netif_warn(tp, link, dev, "CS is disabled, reg=0x%02x\n", reg); + } +} + +static void rtl_eeprom_write(struct eeprom_93cx6 *eeprom) +{ + struct rtl8169_private *tp = eeprom->data; + void __iomem *ioaddr = tp->mmio_addr; + u8 reg; + + reg = RTL_R8(Cfg9346) & ~Cfg9346_mask; + reg |= Cfg9346_Program; + reg &= ~(Cfg9346_EEDO | Cfg9346_EEDI | Cfg9346_EESK | Cfg9346_EECS); + + if (eeprom->reg_data_in) + reg |= Cfg9346_EEDI; + if (eeprom->reg_data_clock) + reg |= Cfg9346_EESK; + if (eeprom->reg_chip_select) + reg |= Cfg9346_EECS; + + RTL_W8(Cfg9346, reg); + RTL_R8(Cfg9346); /* sync? */ + udelay(4); /* SK Clock Cycle Time */ + //udelay(3); /* DO Setup time is min 2 us for 9346, DO hold time max 2us */ +} + +static void rtl_init_93cx6(struct net_device *dev, struct eeprom_93cx6 *eeprom) +{ + struct rtl8169_private *tp = netdev_priv(dev); + + eeprom->data = tp; + eeprom->register_read = rtl_eeprom_read; + eeprom->register_write = rtl_eeprom_write; + /* TODO: check EEPROM type; note TWSI_TYPE_EEPROM is not supported */ + eeprom->width = PCI_EEPROM_WIDTH_93C46; +} + +/* semi-randomly chosen key for ethtool --change-eeprom option */ +#define R8169_EEPROM_MAGIC (0x00008169) + +static int rtl8169_get_eeprom(struct net_device *dev, + struct ethtool_eeprom *ee_eeprom, u8 *data) +{ + struct rtl8169_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + struct eeprom_93cx6 eeprom; + int i = 0; + u8 offset = ee_eeprom->offset >> 1; + u16 val; + + ee_eeprom->magic = R8169_EEPROM_MAGIC; + + rtl_lock_work(tp); + rtl_init_93cx6(dev, &eeprom); + /* something odd is happening, I must write *at least once* to the + * eeprom (say, 00 to offset 0) before read returns something else than + * 00. IOTW, if no write is performed, all values read as 00. Attempted + * solutions that failed: + * eeprom_93cx6_wren(&eeprom, false); // also tried wr(1) before wren(0) + * W(Cfg9346, Cfg9346_Program);R(Cfg9346);udelay(10); // tried |CS too + * + * The oddiest thing is that this seems to survive a shutdown (even 15 + * minutes). Next attempt wa taking this PCI card out of the machine and + * leave it disconnected for 20 minutes. This did not work either. The + * capacitors are likely still charged. When the machine was left + * powered off (not disconnected) for 9.5 hours, the eeprom read empty + * though. auto-loading from eeprom (Cfg9346 = 0x40) also seems to work? + * (or was it caused by writing 2981 to EEPROM[0:1] before autoload?) + */ + + /* this does not work (from rtl8180): */ + //RTL_W8(Cfg9346, Cfg9346_Program); + //RTL_R8(Cfg9346); + //udelay(10); + + /* Do not use eeprom_93cx6_multiread, that returns data in an array of + * little endian words which is not compatible with BE arches. */ + + if (ee_eeprom->offset & 1) { + eeprom_93cx6_read(&eeprom, offset++, &val); + data[i++] = val >> 8; + } + + while (i < ee_eeprom->len - 1) { + eeprom_93cx6_read(&eeprom, offset++, &val); + data[i++] = val & 0xFF; + data[i++] = val >> 8; + } + + if (i < ee_eeprom->len) { + eeprom_93cx6_read(&eeprom, offset, &val); + data[i] = val & 0xFF; + } + + RTL_W8(Cfg9346, Cfg9346_Lock); + rtl_unlock_work(tp); + return 0; +} + +static int rtl8169_set_eeprom(struct net_device *dev, + struct ethtool_eeprom *ee_eeprom, u8 *data) +{ + struct rtl8169_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + struct eeprom_93cx6 eeprom; + int i = 0; + u8 offset = ee_eeprom->offset >> 1; + u16 val; + + if (ee_eeprom->magic != R8169_EEPROM_MAGIC) + return -EINVAL; + + rtl_lock_work(tp); + rtl_init_93cx6(dev, &eeprom); + eeprom_93cx6_wren(&eeprom, true); + + if (ee_eeprom->offset & 1) { + eeprom_93cx6_read(&eeprom, offset, &val); + val &= 0xFF; + val |= ((u16)data[i++]) << 8; + eeprom_93cx6_write(&eeprom, offset++, val); + } + + while (i < ee_eeprom->len - 1) { + val = data[i++]; + val |= ((u16)data[i++]) << 8; + eeprom_93cx6_write(&eeprom, offset++, val); + } + + if (i < ee_eeprom->len) { + eeprom_93cx6_read(&eeprom, offset, &val); + val &= 0xFF00; + val |= data[i++]; + eeprom_93cx6_write(&eeprom, offset, val); + } + + eeprom_93cx6_wren(&eeprom, false); + RTL_W8(Cfg9346, Cfg9346_Lock); + rtl_unlock_work(tp); + return 0; +} +#endif + static int rtl8169_set_speed_tbi(struct net_device *dev, u8 autoneg, u16 speed, u8 duplex, u32 ignored) { @@ -2024,6 +2198,11 @@ static const struct ethtool_ops rtl8169_ethtool_ops = { .get_drvinfo = rtl8169_get_drvinfo, .get_regs_len = rtl8169_get_regs_len, .get_link = ethtool_op_get_link, +#ifdef CONFIG_R8169_93CX6 + .get_eeprom_len = rtl8169_get_eeprom_len, + .get_eeprom = rtl8169_get_eeprom, + .set_eeprom = rtl8169_set_eeprom, +#endif .get_settings = rtl8169_get_settings, .set_settings = rtl8169_set_settings, .get_msglevel = rtl8169_get_msglevel, @@ -7114,6 +7293,19 @@ rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) mutex_init(&tp->wk.mutex); + if (0) { /* DEBUG - remove me! */ + u16 val = 0xAB; + struct eeprom_93cx6 eeprom = { + .data = tp, + .register_read = rtl_eeprom_read, + .register_write = rtl_eeprom_write, + .width = PCI_EEPROM_WIDTH_93C46 + }; + eeprom_93cx6_read(&eeprom, 0, &val); + pr_info("Read something: 0x%04x\n", val); + RTL_W8(Cfg9346, Cfg9346_Lock); + } + /* Get MAC address */ for (i = 0; i < ETH_ALEN; i++) dev->dev_addr[i] = RTL_R8(MAC0 + i);