]> git.proxmox.com Git - mirror_ubuntu-kernels.git/commitdiff
net: lan743x: Add support for EEPROM
authorRaju Lakkaraju <Raju.Lakkaraju@microchip.com>
Thu, 17 Mar 2022 10:43:07 +0000 (16:13 +0530)
committerDavid S. Miller <davem@davemloft.net>
Fri, 18 Mar 2022 12:53:23 +0000 (12:53 +0000)
Add new the EEPROM read and write access functions and system lock
protection to access by devices for PCI11010/PCI11414 chips

Signed-off-by: Raju Lakkaraju <Raju.Lakkaraju@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/microchip/lan743x_ethtool.c
drivers/net/ethernet/microchip/lan743x_main.c
drivers/net/ethernet/microchip/lan743x_main.h

index d13d284cdaea5dad4768e6b3757ef5f80681c097..af64ec8c344d51ec7cefcedee4ee0abf21e1768b 100644 (file)
@@ -7,6 +7,8 @@
 #include <linux/phy.h>
 #include "lan743x_main.h"
 #include "lan743x_ethtool.h"
+#include <linux/sched.h>
+#include <linux/iopoll.h>
 
 /* eeprom */
 #define LAN743X_EEPROM_MAGIC               (0x74A5)
 #define OTP_INDICATOR_1                            (0xF3)
 #define OTP_INDICATOR_2                            (0xF7)
 
+#define LOCK_TIMEOUT_MAX_CNT               (100) // 1 sec (10 msce * 100)
+
+#define LAN743X_CSR_READ_OP(offset)         lan743x_csr_read(adapter, offset)
+
 static int lan743x_otp_power_up(struct lan743x_adapter *adapter)
 {
        u32 reg_value;
@@ -149,6 +155,63 @@ static int lan743x_otp_write(struct lan743x_adapter *adapter, u32 offset,
        return 0;
 }
 
+static int lan743x_hs_syslock_acquire(struct lan743x_adapter *adapter,
+                                     u16 timeout)
+{
+       u16 timeout_cnt = 0;
+       u32 val;
+
+       do {
+               spin_lock(&adapter->eth_syslock_spinlock);
+               if (adapter->eth_syslock_acquire_cnt == 0) {
+                       lan743x_csr_write(adapter, ETH_SYSTEM_SYS_LOCK_REG,
+                                         SYS_LOCK_REG_ENET_SS_LOCK_);
+                       val = lan743x_csr_read(adapter,
+                                              ETH_SYSTEM_SYS_LOCK_REG);
+                       if (val & SYS_LOCK_REG_ENET_SS_LOCK_) {
+                               adapter->eth_syslock_acquire_cnt++;
+                               WARN_ON(adapter->eth_syslock_acquire_cnt == 0);
+                               spin_unlock(&adapter->eth_syslock_spinlock);
+                               break;
+                       }
+               } else {
+                       adapter->eth_syslock_acquire_cnt++;
+                       WARN_ON(adapter->eth_syslock_acquire_cnt == 0);
+                       spin_unlock(&adapter->eth_syslock_spinlock);
+                       break;
+               }
+
+               spin_unlock(&adapter->eth_syslock_spinlock);
+
+               if (timeout_cnt++ < timeout)
+                       usleep_range(10000, 11000);
+               else
+                       return -ETIMEDOUT;
+       } while (true);
+
+       return 0;
+}
+
+static void lan743x_hs_syslock_release(struct lan743x_adapter *adapter)
+{
+       u32 val;
+
+       spin_lock(&adapter->eth_syslock_spinlock);
+       WARN_ON(adapter->eth_syslock_acquire_cnt == 0);
+
+       if (adapter->eth_syslock_acquire_cnt) {
+               adapter->eth_syslock_acquire_cnt--;
+               if (adapter->eth_syslock_acquire_cnt == 0) {
+                       lan743x_csr_write(adapter, ETH_SYSTEM_SYS_LOCK_REG, 0);
+                       val = lan743x_csr_read(adapter,
+                                              ETH_SYSTEM_SYS_LOCK_REG);
+                       WARN_ON((val & SYS_LOCK_REG_ENET_SS_LOCK_) != 0);
+               }
+       }
+
+       spin_unlock(&adapter->eth_syslock_spinlock);
+}
+
 static int lan743x_eeprom_wait(struct lan743x_adapter *adapter)
 {
        unsigned long start_time = jiffies;
@@ -263,6 +326,100 @@ static int lan743x_eeprom_write(struct lan743x_adapter *adapter,
        return 0;
 }
 
+static int lan743x_hs_eeprom_cmd_cmplt_chk(struct lan743x_adapter *adapter)
+{
+       u32 val;
+
+       return readx_poll_timeout(LAN743X_CSR_READ_OP, HS_E2P_CMD, val,
+                                 (!(val & HS_E2P_CMD_EPC_BUSY_) ||
+                                   (val & HS_E2P_CMD_EPC_TIMEOUT_)),
+                                 50, 10000);
+}
+
+static int lan743x_hs_eeprom_read(struct lan743x_adapter *adapter,
+                                 u32 offset, u32 length, u8 *data)
+{
+       int retval;
+       u32 val;
+       int i;
+
+       retval = lan743x_hs_syslock_acquire(adapter, LOCK_TIMEOUT_MAX_CNT);
+       if (retval < 0)
+               return retval;
+
+       retval = lan743x_hs_eeprom_cmd_cmplt_chk(adapter);
+       lan743x_hs_syslock_release(adapter);
+       if (retval < 0)
+               return retval;
+
+       for (i = 0; i < length; i++) {
+               retval = lan743x_hs_syslock_acquire(adapter,
+                                                   LOCK_TIMEOUT_MAX_CNT);
+               if (retval < 0)
+                       return retval;
+
+               val = HS_E2P_CMD_EPC_BUSY_ | HS_E2P_CMD_EPC_CMD_READ_;
+               val |= (offset & HS_E2P_CMD_EPC_ADDR_MASK_);
+               lan743x_csr_write(adapter, HS_E2P_CMD, val);
+               retval = lan743x_hs_eeprom_cmd_cmplt_chk(adapter);
+               if (retval < 0) {
+                       lan743x_hs_syslock_release(adapter);
+                       return retval;
+               }
+
+               val = lan743x_csr_read(adapter, HS_E2P_DATA);
+
+               lan743x_hs_syslock_release(adapter);
+
+               data[i] = val & 0xFF;
+               offset++;
+       }
+
+       return 0;
+}
+
+static int lan743x_hs_eeprom_write(struct lan743x_adapter *adapter,
+                                  u32 offset, u32 length, u8 *data)
+{
+       int retval;
+       u32 val;
+       int i;
+
+       retval = lan743x_hs_syslock_acquire(adapter, LOCK_TIMEOUT_MAX_CNT);
+       if (retval < 0)
+               return retval;
+
+       retval = lan743x_hs_eeprom_cmd_cmplt_chk(adapter);
+       lan743x_hs_syslock_release(adapter);
+       if (retval < 0)
+               return retval;
+
+       for (i = 0; i < length; i++) {
+               retval = lan743x_hs_syslock_acquire(adapter,
+                                                   LOCK_TIMEOUT_MAX_CNT);
+               if (retval < 0)
+                       return retval;
+
+               /* Fill data register */
+               val = data[i];
+               lan743x_csr_write(adapter, HS_E2P_DATA, val);
+
+               /* Send "write" command */
+               val = HS_E2P_CMD_EPC_BUSY_ | HS_E2P_CMD_EPC_CMD_WRITE_;
+               val |= (offset & HS_E2P_CMD_EPC_ADDR_MASK_);
+               lan743x_csr_write(adapter, HS_E2P_CMD, val);
+
+               retval = lan743x_hs_eeprom_cmd_cmplt_chk(adapter);
+               lan743x_hs_syslock_release(adapter);
+               if (retval < 0)
+                       return retval;
+
+               offset++;
+       }
+
+       return 0;
+}
+
 static void lan743x_ethtool_get_drvinfo(struct net_device *netdev,
                                        struct ethtool_drvinfo *info)
 {
@@ -304,10 +461,16 @@ static int lan743x_ethtool_get_eeprom(struct net_device *netdev,
        struct lan743x_adapter *adapter = netdev_priv(netdev);
        int ret = 0;
 
-       if (adapter->flags & LAN743X_ADAPTER_FLAG_OTP)
+       if (adapter->flags & LAN743X_ADAPTER_FLAG_OTP) {
                ret = lan743x_otp_read(adapter, ee->offset, ee->len, data);
-       else
-               ret = lan743x_eeprom_read(adapter, ee->offset, ee->len, data);
+       } else {
+               if (adapter->is_pci11x1x)
+                       ret = lan743x_hs_eeprom_read(adapter, ee->offset,
+                                                    ee->len, data);
+               else
+                       ret = lan743x_eeprom_read(adapter, ee->offset,
+                                                 ee->len, data);
+       }
 
        return ret;
 }
@@ -326,8 +489,13 @@ static int lan743x_ethtool_set_eeprom(struct net_device *netdev,
                }
        } else {
                if (ee->magic == LAN743X_EEPROM_MAGIC) {
-                       ret = lan743x_eeprom_write(adapter, ee->offset,
-                                                  ee->len, data);
+                       if (adapter->is_pci11x1x)
+                               ret = lan743x_hs_eeprom_write(adapter,
+                                                             ee->offset,
+                                                             ee->len, data);
+                       else
+                               ret = lan743x_eeprom_write(adapter, ee->offset,
+                                                          ee->len, data);
                }
        }
 
index 7ace3ed357788ca835f86f8463bd4aec20753319..9ac0c2b96a15bebf815c46ecd6378a30c7facb60 100644 (file)
@@ -2869,6 +2869,7 @@ static int lan743x_hardware_init(struct lan743x_adapter *adapter,
                adapter->used_tx_channels = PCI11X1X_USED_TX_CHANNELS;
                adapter->max_vector_count = PCI11X1X_MAX_VECTOR_COUNT;
                pci11x1x_strap_get_status(adapter);
+               spin_lock_init(&adapter->eth_syslock_spinlock);
        } else {
                adapter->max_tx_channels = LAN743X_MAX_TX_CHANNELS;
                adapter->used_tx_channels = LAN743X_USED_TX_CHANNELS;
index bca9f105900c0f2e5e1187ce4287f0000a7d8810..5ae3420340f3d82acd95a2138e639b3ae6eb53ab 100644 (file)
 
 #define E2P_DATA                       (0x044)
 
+/* Hearthstone top level & System Reg Addresses */
+#define ETH_CTRL_REG_ADDR_BASE         (0x0000)
+#define ETH_SYS_REG_ADDR_BASE          (0x4000)
+#define CONFIG_REG_ADDR_BASE           (0x0000)
+#define ETH_EEPROM_REG_ADDR_BASE       (0x0E00)
+#define ETH_OTP_REG_ADDR_BASE          (0x1000)
+#define SYS_LOCK_REG                   (0x00A0)
+#define SYS_LOCK_REG_MAIN_LOCK_                BIT(7)
+#define SYS_LOCK_REG_GEN_PERI_LOCK_    BIT(5)
+#define SYS_LOCK_REG_SPI_PERI_LOCK_    BIT(4)
+#define SYS_LOCK_REG_SMBUS_PERI_LOCK_  BIT(3)
+#define SYS_LOCK_REG_UART_SS_LOCK_     BIT(2)
+#define SYS_LOCK_REG_ENET_SS_LOCK_     BIT(1)
+#define SYS_LOCK_REG_USB_SS_LOCK_      BIT(0)
+#define ETH_SYSTEM_SYS_LOCK_REG                (ETH_SYS_REG_ADDR_BASE + \
+                                        CONFIG_REG_ADDR_BASE + \
+                                        SYS_LOCK_REG)
+#define HS_EEPROM_REG_ADDR_BASE                (ETH_SYS_REG_ADDR_BASE + \
+                                        ETH_EEPROM_REG_ADDR_BASE)
+#define HS_E2P_CMD                     (HS_EEPROM_REG_ADDR_BASE + 0x0000)
+#define HS_E2P_CMD_EPC_BUSY_           BIT(31)
+#define HS_E2P_CMD_EPC_CMD_WRITE_      GENMASK(29, 28)
+#define HS_E2P_CMD_EPC_CMD_READ_       (0x0)
+#define HS_E2P_CMD_EPC_TIMEOUT_                BIT(17)
+#define HS_E2P_CMD_EPC_ADDR_MASK_      GENMASK(15, 0)
+#define HS_E2P_DATA                    (HS_EEPROM_REG_ADDR_BASE + 0x0004)
+#define HS_E2P_DATA_MASK_              GENMASK(7, 0)
+#define HS_E2P_CFG                     (HS_EEPROM_REG_ADDR_BASE + 0x0008)
+#define HS_E2P_CFG_I2C_PULSE_MASK_     GENMASK(19, 16)
+#define HS_E2P_CFG_EEPROM_SIZE_SEL_    BIT(12)
+#define HS_E2P_CFG_I2C_BAUD_RATE_MASK_ GENMASK(9, 8)
+#define HS_E2P_CFG_TEST_EEPR_TO_BYP_   BIT(0)
+#define HS_E2P_PAD_CTL                 (HS_EEPROM_REG_ADDR_BASE + 0x000C)
+
 #define GPIO_CFG0                      (0x050)
 #define GPIO_CFG0_GPIO_DIR_BIT_(bit)   BIT(16 + (bit))
 #define GPIO_CFG0_GPIO_DATA_BIT_(bit)  BIT(0 + (bit))
@@ -773,6 +807,10 @@ struct lan743x_adapter {
        struct lan743x_rx       rx[LAN743X_USED_RX_CHANNELS];
        bool                    is_pci11x1x;
        bool                    is_sgmii_en;
+       /* protect ethernet syslock */
+       spinlock_t              eth_syslock_spinlock;
+       bool                    eth_syslock_en;
+       u32                     eth_syslock_acquire_cnt;
        u8                      max_tx_channels;
        u8                      used_tx_channels;
        u8                      max_vector_count;