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

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
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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,
+2 −0
Original line number Diff line number Diff line
@@ -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
+116 −0
Original line number Diff line number Diff line
@@ -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.
+36 −0
Original line number Diff line number Diff line
@@ -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)