]> git.baikalelectronics.ru Git - kernel.git/commitdiff
net: dsa: microchip: lan937x: add phy read and write support
authorArun Ramadoss <arun.ramadoss@microchip.com>
Fri, 1 Jul 2022 15:06:29 +0000 (20:36 +0530)
committerDavid S. Miller <davem@davemloft.net>
Sat, 2 Jul 2022 15:34:05 +0000 (16:34 +0100)
This patch add support for the writing and reading of the phy registers.
LAN937x uses the Vphy indirect addressing method for accessing the phys.

Signed-off-by: Arun Ramadoss <arun.ramadoss@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/dsa/microchip/ksz_common.c
drivers/net/dsa/microchip/lan937x.h
drivers/net/dsa/microchip/lan937x_main.c
drivers/net/dsa/microchip/lan937x_reg.h

index 07b6f34a437ef627ccc34328c877bd9329681bb8..67bb4bff4d9b1fbdf827f29a7854e9ec6c34b432 100644 (file)
@@ -207,6 +207,8 @@ static const struct ksz_dev_ops lan937x_dev_ops = {
        .get_port_addr = ksz9477_get_port_addr,
        .cfg_port_member = ksz9477_cfg_port_member,
        .port_setup = lan937x_port_setup,
+       .r_phy = lan937x_r_phy,
+       .w_phy = lan937x_w_phy,
        .r_mib_cnt = ksz9477_r_mib_cnt,
        .r_mib_pkt = ksz9477_r_mib_pkt,
        .r_mib_stat64 = ksz_r_mib_stats64,
index 534f5a7a1129c39e258c70251c27d798675dc3b1..370203406a057d10b1a87624b415c211c2dd2ee6 100644 (file)
@@ -12,4 +12,6 @@ void lan937x_port_setup(struct ksz_device *dev, int port, bool cpu_port);
 void lan937x_config_cpu_port(struct dsa_switch *ds);
 int lan937x_switch_init(struct ksz_device *dev);
 void lan937x_switch_exit(struct ksz_device *dev);
+void lan937x_r_phy(struct ksz_device *dev, u16 addr, u16 reg, u16 *data);
+void lan937x_w_phy(struct ksz_device *dev, u16 addr, u16 reg, u16 val);
 #endif
index e167a0c1ff85b084b8602e09c7ef67ce570dc7dd..5a2e14fe3cf32be366f55663a7496d92fb81febd 100644 (file)
@@ -28,6 +28,114 @@ static int lan937x_port_cfg(struct ksz_device *dev, int port, int offset,
                                  bits, set ? bits : 0);
 }
 
+static int lan937x_enable_spi_indirect_access(struct ksz_device *dev)
+{
+       u16 data16;
+       int ret;
+
+       /* Enable Phy access through SPI */
+       ret = lan937x_cfg(dev, REG_GLOBAL_CTRL_0, SW_PHY_REG_BLOCK, false);
+       if (ret < 0)
+               return ret;
+
+       ret = ksz_read16(dev, REG_VPHY_SPECIAL_CTRL__2, &data16);
+       if (ret < 0)
+               return ret;
+
+       /* Allow SPI access */
+       data16 |= VPHY_SPI_INDIRECT_ENABLE;
+
+       return ksz_write16(dev, REG_VPHY_SPECIAL_CTRL__2, data16);
+}
+
+static int lan937x_vphy_ind_addr_wr(struct ksz_device *dev, int addr, int reg)
+{
+       u16 addr_base = REG_PORT_T1_PHY_CTRL_BASE;
+       u16 temp;
+
+       /* get register address based on the logical port */
+       temp = PORT_CTRL_ADDR(addr, (addr_base + (reg << 2)));
+
+       return ksz_write16(dev, REG_VPHY_IND_ADDR__2, temp);
+}
+
+static int lan937x_internal_phy_write(struct ksz_device *dev, int addr, int reg,
+                                     u16 val)
+{
+       unsigned int value;
+       int ret;
+
+       /* Check for internal phy port */
+       if (!dev->info->internal_phy[addr])
+               return -EOPNOTSUPP;
+
+       ret = lan937x_vphy_ind_addr_wr(dev, addr, reg);
+       if (ret < 0)
+               return ret;
+
+       /* Write the data to be written to the VPHY reg */
+       ret = ksz_write16(dev, REG_VPHY_IND_DATA__2, val);
+       if (ret < 0)
+               return ret;
+
+       /* Write the Write En and Busy bit */
+       ret = ksz_write16(dev, REG_VPHY_IND_CTRL__2,
+                         (VPHY_IND_WRITE | VPHY_IND_BUSY));
+       if (ret < 0)
+               return ret;
+
+       ret = regmap_read_poll_timeout(dev->regmap[1], REG_VPHY_IND_CTRL__2,
+                                      value, !(value & VPHY_IND_BUSY), 10,
+                                      1000);
+       if (ret < 0) {
+               dev_err(dev->dev, "Failed to write phy register\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static int lan937x_internal_phy_read(struct ksz_device *dev, int addr, int reg,
+                                    u16 *val)
+{
+       unsigned int value;
+       int ret;
+
+       /* Check for internal phy port, return 0xffff for non-existent phy */
+       if (!dev->info->internal_phy[addr])
+               return 0xffff;
+
+       ret = lan937x_vphy_ind_addr_wr(dev, addr, reg);
+       if (ret < 0)
+               return ret;
+
+       /* Write Read and Busy bit to start the transaction */
+       ret = ksz_write16(dev, REG_VPHY_IND_CTRL__2, VPHY_IND_BUSY);
+       if (ret < 0)
+               return ret;
+
+       ret = regmap_read_poll_timeout(dev->regmap[1], REG_VPHY_IND_CTRL__2,
+                                      value, !(value & VPHY_IND_BUSY), 10,
+                                      1000);
+       if (ret < 0) {
+               dev_err(dev->dev, "Failed to read phy register\n");
+               return ret;
+       }
+
+       /* Read the VPHY register which has the PHY data */
+       return ksz_read16(dev, REG_VPHY_IND_DATA__2, val);
+}
+
+void lan937x_r_phy(struct ksz_device *dev, u16 addr, u16 reg, u16 *data)
+{
+       lan937x_internal_phy_read(dev, addr, reg, data);
+}
+
+void lan937x_w_phy(struct ksz_device *dev, u16 addr, u16 reg, u16 val)
+{
+       lan937x_internal_phy_write(dev, addr, reg, val);
+}
+
 int lan937x_reset_switch(struct ksz_device *dev)
 {
        u32 data32;
@@ -111,6 +219,14 @@ void lan937x_config_cpu_port(struct dsa_switch *ds)
 int lan937x_setup(struct dsa_switch *ds)
 {
        struct ksz_device *dev = ds->priv;
+       int ret;
+
+       /* enable Indirect Access from SPI to the VPHY registers */
+       ret = lan937x_enable_spi_indirect_access(dev);
+       if (ret < 0) {
+               dev_err(dev->dev, "failed to enable spi indirect access");
+               return ret;
+       }
 
        /* The VLAN aware is a global setting. Mixed vlan
         * filterings are not supported.
index 5e27b2bd2d866b9a6dad46c0456c5eb15e4f4730..7a0fa25959506efe2fcf58b02f80ae3fc590260c 100644 (file)
@@ -8,6 +8,12 @@
 #define PORT_CTRL_ADDR(port, addr)     ((addr) | (((port) + 1)  << 12))
 
 /* 0 - Operation */
+#define REG_GLOBAL_CTRL_0              0x0007
+
+#define SW_PHY_REG_BLOCK               BIT(7)
+#define SW_FAST_MODE                   BIT(3)
+#define SW_FAST_MODE_OVERRIDE          BIT(2)
+
 #define REG_SW_INT_STATUS__4           0x0010
 #define REG_SW_INT_MASK__4             0x0014
 
 #define ALU_V_USE_FID                  BIT(30)
 #define ALU_V_PORT_MAP                 0xFF
 
+/* 7 - VPhy */
+#define REG_VPHY_IND_ADDR__2           0x075C
+#define REG_VPHY_IND_DATA__2           0x0760
+
+#define REG_VPHY_IND_CTRL__2           0x0768
+
+#define VPHY_IND_WRITE                 BIT(1)
+#define VPHY_IND_BUSY                  BIT(0)
+
+#define REG_VPHY_SPECIAL_CTRL__2       0x077C
+#define VPHY_SMI_INDIRECT_ENABLE       BIT(15)
+#define VPHY_SW_LOOPBACK               BIT(14)
+#define VPHY_MDIO_INTERNAL_ENABLE      BIT(13)
+#define VPHY_SPI_INDIRECT_ENABLE       BIT(12)
+#define VPHY_PORT_MODE_M               0x3
+#define VPHY_PORT_MODE_S               8
+#define VPHY_MODE_RGMII                        0
+#define VPHY_MODE_MII_PHY              1
+#define VPHY_MODE_SGMII                        2
+#define VPHY_MODE_RMII_PHY             3
+#define VPHY_SW_COLLISION_TEST         BIT(7)
+#define VPHY_SPEED_DUPLEX_STAT_M       0x7
+#define VPHY_SPEED_DUPLEX_STAT_S       2
+#define VPHY_SPEED_1000                        BIT(4)
+#define VPHY_SPEED_100                 BIT(3)
+#define VPHY_FULL_DUPLEX               BIT(2)
+
 /* Port Registers */
 
 /* 0 - Operation */
 #define PORT_TAIL_TAG_ENABLE           BIT(2)
 #define PORT_QUEUE_SPLIT_ENABLE                0x3
 
+/* 1 - Phy */
+#define REG_PORT_T1_PHY_CTRL_BASE      0x0100
+
 /* 3 - xMII */
 #define REG_PORT_XMII_CTRL_0           0x0300
 #define PORT_SGMII_SEL                 BIT(7)