@@ -121,6 +121,36 @@ static void lan937x_switch_exit(struct ksz_device *dev)
lan937x_reset_switch(dev);
}
+static int lan937x_enable_spi_indirect_access(struct ksz_device *dev)
+{
+ u16 data16;
+ u8 data8;
+ int ret;
+
+ ret = ksz_read8(dev, REG_GLOBAL_CTRL_0, &data8);
+ if (ret < 0)
+ return ret;
+
+ /* Check if PHY register is blocked */
+ if (data8 & SW_PHY_REG_BLOCK) {
+ /* Enable Phy access through SPI */
+ data8 &= ~SW_PHY_REG_BLOCK;
+
+ ret = ksz_write8(dev, REG_GLOBAL_CTRL_0, data8);
+ 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 u32 lan937x_get_port_addr(int port, int offset)
{
return PORT_CTRL_ADDR(port, offset);
@@ -171,6 +201,88 @@ bool lan937x_is_internal_base_t1_phy_port(struct ksz_device *dev, int port)
return false;
}
+static int lan937x_vphy_ind_addr_wr(struct ksz_device *dev, int addr, int reg)
+{
+ u16 temp, addr_base;
+
+ if (lan937x_is_internal_base_tx_phy_port(dev, addr))
+ addr_base = REG_PORT_TX_PHY_CTRL_BASE;
+ else
+ addr_base = REG_PORT_T1_PHY_CTRL_BASE;
+
+ /* 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);
+}
+
+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 (!lan937x_is_internal_phy_port(dev, 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;
+}
+
+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 (!lan937x_is_internal_phy_port(dev, 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_port_setup(struct ksz_device *dev, int port, bool cpu_port)
{
struct dsa_switch *ds = dev->ds;
@@ -205,6 +317,73 @@ void lan937x_port_setup(struct ksz_device *dev, int port, bool cpu_port)
lan937x_cfg_port_member(dev, port, member);
}
+static int lan937x_sw_mdio_read(struct mii_bus *bus, int addr, int regnum)
+{
+ struct ksz_device *dev = bus->priv;
+ u16 val;
+ int ret;
+
+ if (regnum & MII_ADDR_C45)
+ return -EOPNOTSUPP;
+
+ ret = lan937x_internal_phy_read(dev, addr, regnum, &val);
+ if (ret < 0)
+ return ret;
+
+ return val;
+}
+
+static int lan937x_sw_mdio_write(struct mii_bus *bus, int addr, int regnum,
+ u16 val)
+{
+ struct ksz_device *dev = bus->priv;
+
+ if (regnum & MII_ADDR_C45)
+ return -EOPNOTSUPP;
+
+ return lan937x_internal_phy_write(dev, addr, regnum, val);
+}
+
+static int lan937x_mdio_register(struct ksz_device *dev)
+{
+ struct dsa_switch *ds = dev->ds;
+ struct device_node *mdio_np;
+ struct mii_bus *bus;
+ int ret;
+
+ mdio_np = of_get_child_by_name(dev->dev->of_node, "mdio");
+ if (!mdio_np) {
+ dev_err(ds->dev, "no MDIO bus node\n");
+ return -ENODEV;
+ }
+
+ bus = devm_mdiobus_alloc(ds->dev);
+ if (!bus) {
+ of_node_put(mdio_np);
+ return -ENOMEM;
+ }
+
+ bus->priv = dev;
+ bus->read = lan937x_sw_mdio_read;
+ bus->write = lan937x_sw_mdio_write;
+ bus->name = "lan937x slave smi";
+ snprintf(bus->id, MII_BUS_ID_SIZE, "SMI-%d", ds->index);
+ bus->parent = ds->dev;
+ bus->phy_mask = ~ds->phys_mii_mask;
+
+ ds->slave_mii_bus = bus;
+
+ ret = devm_of_mdiobus_register(ds->dev, bus, mdio_np);
+ if (ret) {
+ dev_err(ds->dev, "unable to register MDIO bus %s\n",
+ bus->id);
+ }
+
+ of_node_put(mdio_np);
+
+ return ret;
+}
+
static int lan937x_switch_init(struct ksz_device *dev)
{
int ret;
@@ -234,10 +413,25 @@ static int lan937x_init(struct ksz_device *dev)
int ret;
ret = lan937x_switch_init(dev);
- if (ret < 0)
+ if (ret < 0) {
dev_err(dev->dev, "failed to initialize the switch");
+ return ret;
+ }
- return 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;
+ }
+
+ ret = lan937x_mdio_register(dev);
+ if (ret < 0) {
+ dev_err(dev->dev, "failed to register the mdio");
+ return ret;
+ }
+
+ return 0;
}
const struct ksz_dev_ops lan937x_dev_ops = {
@@ -21,6 +21,10 @@ int lan937x_pwrite16(struct ksz_device *dev, int port,
int offset, u16 data);
int lan937x_pwrite32(struct ksz_device *dev, int port,
int offset, u32 data);
+int lan937x_internal_phy_write(struct ksz_device *dev, int addr,
+ int reg, u16 val);
+int lan937x_internal_phy_read(struct ksz_device *dev, int addr,
+ int reg, u16 *val);
bool lan937x_is_internal_phy_port(struct ksz_device *dev, int port);
bool lan937x_is_internal_base_tx_phy_port(struct ksz_device *dev, int port);
bool lan937x_is_internal_base_t1_phy_port(struct ksz_device *dev, int port);
@@ -23,6 +23,27 @@ static enum dsa_tag_protocol lan937x_get_tag_protocol(struct dsa_switch *ds,
return DSA_TAG_PROTO_LAN937X_VALUE;
}
+static int lan937x_phy_read16(struct dsa_switch *ds, int addr, int reg)
+{
+ struct ksz_device *dev = ds->priv;
+ u16 val;
+ int ret;
+
+ ret = lan937x_internal_phy_read(dev, addr, reg, &val);
+ if (ret < 0)
+ return ret;
+
+ return val;
+}
+
+static int lan937x_phy_write16(struct dsa_switch *ds, int addr, int reg,
+ u16 val)
+{
+ struct ksz_device *dev = ds->priv;
+
+ return lan937x_internal_phy_write(dev, addr, reg, val);
+}
+
static void lan937x_port_stp_state_set(struct dsa_switch *ds, int port,
u8 state)
{
@@ -199,6 +220,8 @@ static int lan937x_setup(struct dsa_switch *ds)
const struct dsa_switch_ops lan937x_switch_ops = {
.get_tag_protocol = lan937x_get_tag_protocol,
.setup = lan937x_setup,
+ .phy_read = lan937x_phy_read16,
+ .phy_write = lan937x_phy_write16,
.port_enable = ksz_enable_port,
.port_bridge_join = ksz_port_bridge_join,
.port_bridge_leave = ksz_port_bridge_leave,