Commit ffaf1de2 authored by Arun Ramadoss's avatar Arun Ramadoss Committed by David S. Miller

net: dsa: microchip: lan937x: add phy read and write support

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: default avatarArun Ramadoss <arun.ramadoss@microchip.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 99b16df0
......@@ -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,
......
......@@ -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
......@@ -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.
......
......@@ -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
......@@ -82,6 +88,33 @@
#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 */
......@@ -94,6 +127,9 @@
#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)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment