Skip to content

Commit

Permalink
net: dsa: sja1105: register the MDIO buses for 100base-T1 and 100base-TX
Browse files Browse the repository at this point in the history
The SJA1110 contains two types of integrated PHYs: one 100base-TX PHY
and multiple 100base-T1 PHYs.

The access procedure for the 100base-T1 PHYs is also different than it
is for the 100base-TX one. So we register 2 MDIO buses, one for the
base-TX and the other for the base-T1. Each bus has an OF node which is
a child of the "mdio" subnode of the switch, and they are recognized by
compatible string.

Cc: Russell King <linux@armlinux.org.uk>
Cc: Heiner Kallweit <hkallweit1@gmail.com>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: devicetree@vger.kernel.org
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Vladimir Oltean authored and David S. Miller committed Jun 8, 2021
1 parent ceec8bc commit 5a8f097
Show file tree
Hide file tree
Showing 5 changed files with 358 additions and 1 deletion.
1 change: 1 addition & 0 deletions drivers/net/dsa/sja1105/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ obj-$(CONFIG_NET_DSA_SJA1105) += sja1105.o
sja1105-objs := \
sja1105_spi.o \
sja1105_main.o \
sja1105_mdio.o \
sja1105_flower.o \
sja1105_ethtool.o \
sja1105_devlink.o \
Expand Down
19 changes: 19 additions & 0 deletions drivers/net/dsa/sja1105/sja1105.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@ struct sja1105_regs {
u64 rmii_ref_clk[SJA1105_MAX_NUM_PORTS];
u64 rmii_ext_tx_clk[SJA1105_MAX_NUM_PORTS];
u64 stats[__MAX_SJA1105_STATS_AREA][SJA1105_MAX_NUM_PORTS];
u64 mdio_100base_tx;
u64 mdio_100base_t1;
};

struct sja1105_mdio_private {
struct sja1105_private *priv;
};

enum {
Expand All @@ -78,6 +84,12 @@ enum {
SJA1105_SPEED_MAX,
};

enum sja1105_internal_phy_t {
SJA1105_NO_PHY = 0,
SJA1105_PHY_BASE_TX,
SJA1105_PHY_BASE_T1,
};

struct sja1105_info {
u64 device_id;
/* Needed for distinction between P and R, and between Q and S
Expand Down Expand Up @@ -123,6 +135,7 @@ struct sja1105_info {
bool supports_rgmii[SJA1105_MAX_NUM_PORTS];
bool supports_sgmii[SJA1105_MAX_NUM_PORTS];
bool supports_2500basex[SJA1105_MAX_NUM_PORTS];
enum sja1105_internal_phy_t internal_phy[SJA1105_MAX_NUM_PORTS];
const u64 port_speed[SJA1105_SPEED_MAX];
};

Expand Down Expand Up @@ -246,6 +259,8 @@ struct sja1105_private {
enum sja1105_vlan_state vlan_state;
struct devlink_region **regions;
struct sja1105_cbs_entry *cbs;
struct mii_bus *mdio_base_t1;
struct mii_bus *mdio_base_tx;
struct sja1105_tagger_data tagger_data;
struct sja1105_ptp_data ptp_data;
struct sja1105_tas_data tas_data;
Expand Down Expand Up @@ -275,6 +290,10 @@ int sja1105_vlan_filtering(struct dsa_switch *ds, int port, bool enabled,
struct netlink_ext_ack *extack);
void sja1105_frame_memory_partitioning(struct sja1105_private *priv);

/* From sja1105_mdio.c */
int sja1105_mdiobus_register(struct dsa_switch *ds);
void sja1105_mdiobus_unregister(struct dsa_switch *ds);

/* From sja1105_devlink.c */
int sja1105_devlink_setup(struct dsa_switch *ds);
void sja1105_devlink_teardown(struct dsa_switch *ds);
Expand Down
21 changes: 20 additions & 1 deletion drivers/net/dsa/sja1105/sja1105_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,15 @@ static int sja1105_init_mii_settings(struct sja1105_private *priv)
continue;

switch (priv->phy_mode[i]) {
case PHY_INTERFACE_MODE_INTERNAL:
if (priv->info->internal_phy[i] == SJA1105_NO_PHY)
goto unsupported;

mii->xmii_mode[i] = XMII_MODE_MII;
if (priv->info->internal_phy[i] == SJA1105_PHY_BASE_TX)
mii->special[i] = true;

break;
case PHY_INTERFACE_MODE_REVMII:
role = XMII_PHY;
fallthrough;
Expand Down Expand Up @@ -3109,11 +3118,19 @@ static int sja1105_setup(struct dsa_switch *ds)
dev_err(ds->dev, "Failed to register PTP clock: %d\n", rc);
return rc;
}

rc = sja1105_mdiobus_register(ds);
if (rc < 0) {
dev_err(ds->dev, "Failed to register MDIO bus: %pe\n",
ERR_PTR(rc));
goto out_ptp_clock_unregister;
}

/* Create and send configuration down to device */
rc = sja1105_static_config_load(priv);
if (rc < 0) {
dev_err(ds->dev, "Failed to load static config: %d\n", rc);
goto out_ptp_clock_unregister;
goto out_mdiobus_unregister;
}
/* Configure the CGU (PHY link modes and speeds) */
rc = priv->info->clocking_setup(priv);
Expand Down Expand Up @@ -3156,6 +3173,8 @@ static int sja1105_setup(struct dsa_switch *ds)

out_devlink_teardown:
sja1105_devlink_teardown(ds);
out_mdiobus_unregister:
sja1105_mdiobus_unregister(ds);
out_ptp_clock_unregister:
sja1105_ptp_clock_unregister(ds);
out_static_config_free:
Expand Down
288 changes: 288 additions & 0 deletions drivers/net/dsa/sja1105/sja1105_mdio.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,288 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright 2021, NXP Semiconductors
*/
#include <linux/of_mdio.h>
#include "sja1105.h"

enum sja1105_mdio_opcode {
SJA1105_C45_ADDR = 0,
SJA1105_C22 = 1,
SJA1105_C45_DATA = 2,
SJA1105_C45_DATA_AUTOINC = 3,
};

static u64 sja1105_base_t1_encode_addr(struct sja1105_private *priv,
int phy, enum sja1105_mdio_opcode op,
int xad)
{
const struct sja1105_regs *regs = priv->info->regs;

return regs->mdio_100base_t1 | (phy << 7) | (op << 5) | (xad << 0);
}

static int sja1105_base_t1_mdio_read(struct mii_bus *bus, int phy, int reg)
{
struct sja1105_mdio_private *mdio_priv = bus->priv;
struct sja1105_private *priv = mdio_priv->priv;
u64 addr;
u32 tmp;
int rc;

if (reg & MII_ADDR_C45) {
u16 mmd = (reg >> MII_DEVADDR_C45_SHIFT) & 0x1f;

addr = sja1105_base_t1_encode_addr(priv, phy, SJA1105_C45_ADDR,
mmd);

tmp = reg & MII_REGADDR_C45_MASK;

rc = sja1105_xfer_u32(priv, SPI_WRITE, addr, &tmp, NULL);
if (rc < 0)
return rc;

addr = sja1105_base_t1_encode_addr(priv, phy, SJA1105_C45_DATA,
mmd);

rc = sja1105_xfer_u32(priv, SPI_READ, addr, &tmp, NULL);
if (rc < 0)
return rc;

return tmp & 0xffff;
}

/* Clause 22 read */
addr = sja1105_base_t1_encode_addr(priv, phy, SJA1105_C22, reg & 0x1f);

rc = sja1105_xfer_u32(priv, SPI_READ, addr, &tmp, NULL);
if (rc < 0)
return rc;

return tmp & 0xffff;
}

static int sja1105_base_t1_mdio_write(struct mii_bus *bus, int phy, int reg,
u16 val)
{
struct sja1105_mdio_private *mdio_priv = bus->priv;
struct sja1105_private *priv = mdio_priv->priv;
u64 addr;
u32 tmp;
int rc;

if (reg & MII_ADDR_C45) {
u16 mmd = (reg >> MII_DEVADDR_C45_SHIFT) & 0x1f;

addr = sja1105_base_t1_encode_addr(priv, phy, SJA1105_C45_ADDR,
mmd);

tmp = reg & MII_REGADDR_C45_MASK;

rc = sja1105_xfer_u32(priv, SPI_WRITE, addr, &tmp, NULL);
if (rc < 0)
return rc;

addr = sja1105_base_t1_encode_addr(priv, phy, SJA1105_C45_DATA,
mmd);

tmp = val & 0xffff;

rc = sja1105_xfer_u32(priv, SPI_WRITE, addr, &tmp, NULL);
if (rc < 0)
return rc;

return 0;
}

/* Clause 22 write */
addr = sja1105_base_t1_encode_addr(priv, phy, SJA1105_C22, reg & 0x1f);

tmp = val & 0xffff;

return sja1105_xfer_u32(priv, SPI_WRITE, addr, &tmp, NULL);
}

static int sja1105_base_tx_mdio_read(struct mii_bus *bus, int phy, int reg)
{
struct sja1105_mdio_private *mdio_priv = bus->priv;
struct sja1105_private *priv = mdio_priv->priv;
const struct sja1105_regs *regs = priv->info->regs;
u32 tmp;
int rc;

rc = sja1105_xfer_u32(priv, SPI_READ, regs->mdio_100base_tx + reg,
&tmp, NULL);
if (rc < 0)
return rc;

return tmp & 0xffff;
}

static int sja1105_base_tx_mdio_write(struct mii_bus *bus, int phy, int reg,
u16 val)
{
struct sja1105_mdio_private *mdio_priv = bus->priv;
struct sja1105_private *priv = mdio_priv->priv;
const struct sja1105_regs *regs = priv->info->regs;
u32 tmp = val;

return sja1105_xfer_u32(priv, SPI_WRITE, regs->mdio_100base_tx + reg,
&tmp, NULL);
}

static int sja1105_mdiobus_base_tx_register(struct sja1105_private *priv,
struct device_node *mdio_node)
{
struct sja1105_mdio_private *mdio_priv;
struct device_node *np;
struct mii_bus *bus;
int rc = 0;

np = of_find_compatible_node(mdio_node, NULL,
"nxp,sja1110-base-tx-mdio");
if (!np)
return 0;

if (!of_device_is_available(np))
goto out_put_np;

bus = mdiobus_alloc_size(sizeof(*mdio_priv));
if (!bus) {
rc = -ENOMEM;
goto out_put_np;
}

bus->name = "SJA1110 100base-TX MDIO bus";
snprintf(bus->id, MII_BUS_ID_SIZE, "%s-base-tx",
dev_name(priv->ds->dev));
bus->read = sja1105_base_tx_mdio_read;
bus->write = sja1105_base_tx_mdio_write;
bus->parent = priv->ds->dev;
mdio_priv = bus->priv;
mdio_priv->priv = priv;

rc = of_mdiobus_register(bus, np);
if (rc) {
mdiobus_free(bus);
goto out_put_np;
}

priv->mdio_base_tx = bus;

out_put_np:
of_node_put(np);

return 0;
}

static void sja1105_mdiobus_base_tx_unregister(struct sja1105_private *priv)
{
if (!priv->mdio_base_tx)
return;

mdiobus_unregister(priv->mdio_base_tx);
mdiobus_free(priv->mdio_base_tx);
priv->mdio_base_tx = NULL;
}

static int sja1105_mdiobus_base_t1_register(struct sja1105_private *priv,
struct device_node *mdio_node)
{
struct sja1105_mdio_private *mdio_priv;
struct device_node *np;
struct mii_bus *bus;
int rc = 0;

np = of_find_compatible_node(mdio_node, NULL,
"nxp,sja1110-base-t1-mdio");
if (!np)
return 0;

if (!of_device_is_available(np))
goto out_put_np;

bus = mdiobus_alloc_size(sizeof(*mdio_priv));
if (!bus) {
rc = -ENOMEM;
goto out_put_np;
}

bus->name = "SJA1110 100base-T1 MDIO bus";
snprintf(bus->id, MII_BUS_ID_SIZE, "%s-base-t1",
dev_name(priv->ds->dev));
bus->read = sja1105_base_t1_mdio_read;
bus->write = sja1105_base_t1_mdio_write;
bus->parent = priv->ds->dev;
mdio_priv = bus->priv;
mdio_priv->priv = priv;

rc = of_mdiobus_register(bus, np);
if (rc) {
mdiobus_free(bus);
goto out_put_np;
}

priv->mdio_base_t1 = bus;

out_put_np:
of_node_put(np);

return rc;
}

static void sja1105_mdiobus_base_t1_unregister(struct sja1105_private *priv)
{
if (!priv->mdio_base_t1)
return;

mdiobus_unregister(priv->mdio_base_t1);
mdiobus_free(priv->mdio_base_t1);
priv->mdio_base_t1 = NULL;
}

int sja1105_mdiobus_register(struct dsa_switch *ds)
{
struct sja1105_private *priv = ds->priv;
const struct sja1105_regs *regs = priv->info->regs;
struct device_node *switch_node = ds->dev->of_node;
struct device_node *mdio_node;
int rc;

mdio_node = of_get_child_by_name(switch_node, "mdios");
if (!mdio_node)
return 0;

if (!of_device_is_available(mdio_node))
goto out_put_mdio_node;

if (regs->mdio_100base_tx != SJA1105_RSV_ADDR) {
rc = sja1105_mdiobus_base_tx_register(priv, mdio_node);
if (rc)
goto err_put_mdio_node;
}

if (regs->mdio_100base_t1 != SJA1105_RSV_ADDR) {
rc = sja1105_mdiobus_base_t1_register(priv, mdio_node);
if (rc)
goto err_free_base_tx_mdiobus;
}

out_put_mdio_node:
of_node_put(mdio_node);

return 0;

err_free_base_tx_mdiobus:
sja1105_mdiobus_base_tx_unregister(priv);
err_put_mdio_node:
of_node_put(mdio_node);

return rc;
}

void sja1105_mdiobus_unregister(struct dsa_switch *ds)
{
struct sja1105_private *priv = ds->priv;

sja1105_mdiobus_base_t1_unregister(priv);
sja1105_mdiobus_base_tx_unregister(priv);
}
Loading

0 comments on commit 5a8f097

Please sign in to comment.