Signed-off-by: Bryan Whitehead <bryan.whiteh...@microchip.com>
---
 drivers/net/ethernet/microchip/lan743x_ethtool.c | 209 +++++++++++++++++++++++
 drivers/net/ethernet/microchip/lan743x_main.h    |  33 ++++
 2 files changed, 242 insertions(+)

diff --git a/drivers/net/ethernet/microchip/lan743x_ethtool.c 
b/drivers/net/ethernet/microchip/lan743x_ethtool.c
index addd628..0d0c997 100644
--- a/drivers/net/ethernet/microchip/lan743x_ethtool.c
+++ b/drivers/net/ethernet/microchip/lan743x_ethtool.c
@@ -7,6 +7,178 @@
 #include <linux/pci.h>
 #include <linux/phy.h>
 
+/* eeprom */
+#define LAN743X_EEPROM_MAGIC               (0x74A5)
+#define LAN743X_OTP_MAGIC                  (0x74F3)
+#define EEPROM_INDICATOR_1                 (0xA5)
+#define EEPROM_INDICATOR_2                 (0xAA)
+#define EEPROM_MAC_OFFSET                  (0x01)
+#define MAX_EEPROM_SIZE                            512
+#define OTP_INDICATOR_1                            (0xF3)
+#define OTP_INDICATOR_2                            (0xF7)
+
+static int lan743x_otp_write(struct lan743x_adapter *adapter, u32 offset,
+                            u32 length, u8 *data)
+{
+       unsigned long timeout;
+       u32 buf;
+       int i;
+
+       buf = lan743x_csr_read(adapter, OTP_PWR_DN);
+
+       if (buf & OTP_PWR_DN_PWRDN_N_) {
+               /* clear it and wait to be cleared */
+               lan743x_csr_write(adapter, OTP_PWR_DN, 0);
+
+               timeout = jiffies + HZ;
+               do {
+                       udelay(1);
+                       buf = lan743x_csr_read(adapter, OTP_PWR_DN);
+                       if (time_after(jiffies, timeout)) {
+                               netif_warn(adapter, drv, adapter->netdev,
+                                          "timeout on OTP_PWR_DN 
completion\n");
+                               return -EIO;
+                       }
+               } while (buf & OTP_PWR_DN_PWRDN_N_);
+       }
+
+       /* set to BYTE program mode */
+       lan743x_csr_write(adapter, OTP_PRGM_MODE, OTP_PRGM_MODE_BYTE_);
+
+       for (i = 0; i < length; i++) {
+               lan743x_csr_write(adapter, OTP_ADDR1,
+                                 ((offset + i) >> 8) &
+                                 OTP_ADDR1_15_11_MASK_);
+               lan743x_csr_write(adapter, OTP_ADDR2,
+                                 ((offset + i) &
+                                 OTP_ADDR2_10_3_MASK_));
+               lan743x_csr_write(adapter, OTP_PRGM_DATA, data[i]);
+               lan743x_csr_write(adapter, OTP_TST_CMD, OTP_TST_CMD_PRGVRFY_);
+               lan743x_csr_write(adapter, OTP_CMD_GO, OTP_CMD_GO_GO_);
+
+               timeout = jiffies + HZ;
+               do {
+                       udelay(1);
+                       buf = lan743x_csr_read(adapter, OTP_STATUS);
+                       if (time_after(jiffies, timeout)) {
+                               netif_warn(adapter, drv, adapter->netdev,
+                                          "Timeout on OTP_STATUS 
completion\n");
+                               return -EIO;
+                       }
+               } while (buf & OTP_STATUS_BUSY_);
+       }
+
+       return 0;
+}
+
+static int lan743x_eeprom_wait(struct lan743x_adapter *adapter)
+{
+       unsigned long start_time = jiffies;
+       u32 val;
+
+       do {
+               val = lan743x_csr_read(adapter, E2P_CMD);
+
+               if (!(val & E2P_CMD_EPC_BUSY_) ||
+                   (val & E2P_CMD_EPC_TIMEOUT_))
+                       break;
+               usleep_range(40, 100);
+       } while (!time_after(jiffies, start_time + HZ));
+
+       if (val & (E2P_CMD_EPC_TIMEOUT_ | E2P_CMD_EPC_BUSY_)) {
+               netif_warn(adapter, drv, adapter->netdev,
+                          "EEPROM read operation timeout\n");
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static int lan743x_eeprom_confirm_not_busy(struct lan743x_adapter *adapter)
+{
+       unsigned long start_time = jiffies;
+       u32 val;
+
+       do {
+               val = lan743x_csr_read(adapter, E2P_CMD);
+
+               if (!(val & E2P_CMD_EPC_BUSY_))
+                       return 0;
+
+               usleep_range(40, 100);
+       } while (!time_after(jiffies, start_time + HZ));
+
+       netif_warn(adapter, drv, adapter->netdev, "EEPROM is busy\n");
+       return -EIO;
+}
+
+static int lan743x_eeprom_read(struct lan743x_adapter *adapter,
+                              u32 offset, u32 length, u8 *data)
+{
+       int retval;
+       u32 val;
+       int i;
+
+       retval = lan743x_eeprom_confirm_not_busy(adapter);
+       if (retval)
+               return retval;
+
+       for (i = 0; i < length; i++) {
+               val = E2P_CMD_EPC_BUSY_ | E2P_CMD_EPC_CMD_READ_;
+               val |= (offset & E2P_CMD_EPC_ADDR_MASK_);
+               lan743x_csr_write(adapter, E2P_CMD, val);
+
+               retval = lan743x_eeprom_wait(adapter);
+               if (retval < 0)
+                       return retval;
+
+               val = lan743x_csr_read(adapter, E2P_DATA);
+               data[i] = val & 0xFF;
+               offset++;
+       }
+
+       return 0;
+}
+
+static int lan743x_eeprom_write(struct lan743x_adapter *adapter,
+                               u32 offset, u32 length, u8 *data)
+{
+       int retval;
+       u32 val;
+       int i;
+
+       retval = lan743x_eeprom_confirm_not_busy(adapter);
+       if (retval)
+               return retval;
+
+       /* Issue write/erase enable command */
+       val = E2P_CMD_EPC_BUSY_ | E2P_CMD_EPC_CMD_EWEN_;
+       lan743x_csr_write(adapter, E2P_CMD, val);
+
+       retval = lan743x_eeprom_wait(adapter);
+       if (retval < 0)
+               return retval;
+
+       for (i = 0; i < length; i++) {
+               /* Fill data register */
+               val = data[i];
+               lan743x_csr_write(adapter, E2P_DATA, val);
+
+               /* Send "write" command */
+               val = E2P_CMD_EPC_BUSY_ | E2P_CMD_EPC_CMD_WRITE_;
+               val |= (offset & E2P_CMD_EPC_ADDR_MASK_);
+               lan743x_csr_write(adapter, E2P_CMD, val);
+
+               retval = lan743x_eeprom_wait(adapter);
+               if (retval < 0)
+                       return retval;
+
+               offset++;
+       }
+
+       return 0;
+}
+
 static void lan743x_ethtool_get_drvinfo(struct net_device *netdev,
                                        struct ethtool_drvinfo *info)
 {
@@ -32,6 +204,40 @@ static void lan743x_ethtool_set_msglevel(struct net_device 
*netdev,
        adapter->msg_enable = msglevel;
 }
 
+static int lan743x_ethtool_get_eeprom_len(struct net_device *netdev)
+{
+       return MAX_EEPROM_SIZE;
+}
+
+static int lan743x_ethtool_get_eeprom(struct net_device *netdev,
+                                     struct ethtool_eeprom *ee, u8 *data)
+{
+       struct lan743x_adapter *adapter = netdev_priv(netdev);
+
+       return lan743x_eeprom_read(adapter, ee->offset, ee->len, data);
+}
+
+static int lan743x_ethtool_set_eeprom(struct net_device *netdev,
+                                     struct ethtool_eeprom *ee, u8 *data)
+{
+       struct lan743x_adapter *adapter = netdev_priv(netdev);
+       int ret = -EINVAL;
+
+       if (ee->magic == LAN743X_EEPROM_MAGIC)
+               ret = lan743x_eeprom_write(adapter, ee->offset, ee->len,
+                                          data);
+       /* Beware!  OTP is One Time Programming ONLY!
+        * So do some strict condition check before messing up
+        */
+       else if ((ee->magic == LAN743X_OTP_MAGIC) &&
+                (ee->offset == 0) &&
+                (ee->len == 512) &&
+                (data[0] == OTP_INDICATOR_1))
+               ret = lan743x_otp_write(adapter, ee->offset, ee->len, data);
+
+       return ret;
+}
+
 static const char lan743x_set0_hw_cnt_strings[][ETH_GSTRING_LEN] = {
        "RX FCS Errors",
        "RX Alignment Errors",
@@ -217,6 +423,9 @@ const struct ethtool_ops lan743x_ethtool_ops = {
        .set_msglevel = lan743x_ethtool_set_msglevel,
        .get_link = ethtool_op_get_link,
 
+       .get_eeprom_len = lan743x_ethtool_get_eeprom_len,
+       .get_eeprom = lan743x_ethtool_get_eeprom,
+       .set_eeprom = lan743x_ethtool_set_eeprom,
        .get_strings = lan743x_ethtool_get_strings,
        .get_ethtool_stats = lan743x_ethtool_get_ethtool_stats,
        .get_sset_count = lan743x_ethtool_get_sset_count,
diff --git a/drivers/net/ethernet/microchip/lan743x_main.h 
b/drivers/net/ethernet/microchip/lan743x_main.h
index de4f2cc..c026b8d 100644
--- a/drivers/net/ethernet/microchip/lan743x_main.h
+++ b/drivers/net/ethernet/microchip/lan743x_main.h
@@ -42,6 +42,16 @@
 
 #define DP_DATA_0                      (0x030)
 
+#define E2P_CMD                                (0x040)
+#define E2P_CMD_EPC_BUSY_              BIT(31)
+#define E2P_CMD_EPC_CMD_WRITE_         (0x30000000)
+#define E2P_CMD_EPC_CMD_EWEN_          (0x20000000)
+#define E2P_CMD_EPC_CMD_READ_          (0x00000000)
+#define E2P_CMD_EPC_TIMEOUT_           BIT(10)
+#define E2P_CMD_EPC_ADDR_MASK_         (0x000001FF)
+
+#define E2P_DATA                       (0x044)
+
 #define FCT_RX_CTL                     (0xAC)
 #define FCT_RX_CTL_EN_(channel)                BIT(28 + (channel))
 #define FCT_RX_CTL_DIS_(channel)       BIT(24 + (channel))
@@ -288,6 +298,29 @@
 #define TX_CFG_C_TX_DMA_INT_STS_AUTO_CLR_      BIT(3)
 #define TX_CFG_C_TX_INT_STS_R2C_MODE_MASK_     (0x00000007)
 
+#define OTP_PWR_DN                             (0x1000)
+#define OTP_PWR_DN_PWRDN_N_                    BIT(0)
+
+#define OTP_ADDR1                              (0x1004)
+#define OTP_ADDR1_15_11_MASK_                  (0x1F)
+
+#define OTP_ADDR2                              (0x1008)
+#define OTP_ADDR2_10_3_MASK_                   (0xFF)
+
+#define OTP_PRGM_DATA                          (0x1010)
+
+#define OTP_PRGM_MODE                          (0x1014)
+#define OTP_PRGM_MODE_BYTE_                    BIT(0)
+
+#define OTP_TST_CMD                            (0x1024)
+#define OTP_TST_CMD_PRGVRFY_                   BIT(3)
+
+#define OTP_CMD_GO                             (0x1028)
+#define OTP_CMD_GO_GO_                         BIT(0)
+
+#define OTP_STATUS                             (0x1030)
+#define OTP_STATUS_BUSY_                       BIT(0)
+
 /* MAC statistics registers */
 #define STAT_RX_FCS_ERRORS                     (0x1200)
 #define STAT_RX_ALIGNMENT_ERRORS               (0x1204)
-- 
2.7.4

Reply via email to