From df33ec7097216f888d1311a6d3fd42ba259524ca Mon Sep 17 00:00:00 2001 From: Sam Edwards Date: Wed, 28 Feb 2024 15:34:56 -0700 Subject: [PATCH 01/11] local: hack in RTL8370MB-CG to dsa driver --- drivers/net/dsa/realtek/rtl8365mb.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/net/dsa/realtek/rtl8365mb.c b/drivers/net/dsa/realtek/rtl8365mb.c index 41ea3b5a42b1..f8c7b2bca205 100644 --- a/drivers/net/dsa/realtek/rtl8365mb.c +++ b/drivers/net/dsa/realtek/rtl8365mb.c @@ -554,6 +554,19 @@ static const struct rtl8365mb_chip_info rtl8365mb_chip_infos[] = { .jam_table = rtl8365mb_init_jam_8365mb_vc, .jam_size = ARRAY_SIZE(rtl8365mb_init_jam_8365mb_vc), }, + { + .name = "RTL8370MB-CG", + .chip_id = 0x6368, + .chip_ver = 0x0010, + .extints = { + { 8, 1, PHY_INTF(MII) | PHY_INTF(TMII) | + PHY_INTF(RMII) | PHY_INTF(RGMII) }, + { 9, 2, PHY_INTF(MII) | PHY_INTF(TMII) | + PHY_INTF(RMII) | PHY_INTF(RGMII) }, + }, + .jam_table = rtl8365mb_init_jam_8365mb_vc, + .jam_size = ARRAY_SIZE(rtl8365mb_init_jam_8365mb_vc), + }, }; enum rtl8365mb_stp_state { -- 2.43.2 From e061ef217a9977a9a044018eb6eeede7be9a00c6 Mon Sep 17 00:00:00 2001 From: Sam Edwards Date: Wed, 6 Mar 2024 17:42:37 -0700 Subject: [PATCH 02/11] local: realtek: cmd_{read,write} -> i2c_addr --- drivers/net/dsa/realtek/realtek-mdio.c | 2 -- drivers/net/dsa/realtek/realtek-smi.c | 7 +++---- drivers/net/dsa/realtek/realtek.h | 6 ++---- drivers/net/dsa/realtek/rtl8365mb.c | 3 +-- drivers/net/dsa/realtek/rtl8366rb.c | 3 +-- 5 files changed, 7 insertions(+), 14 deletions(-) diff --git a/drivers/net/dsa/realtek/realtek-mdio.c b/drivers/net/dsa/realtek/realtek-mdio.c index 292e6d087e8b..0f29e6273b4a 100644 --- a/drivers/net/dsa/realtek/realtek-mdio.c +++ b/drivers/net/dsa/realtek/realtek-mdio.c @@ -184,8 +184,6 @@ static int realtek_mdio_probe(struct mdio_device *mdiodev) priv->chip_data = (void *)priv + sizeof(*priv); priv->clk_delay = var->clk_delay; - priv->cmd_read = var->cmd_read; - priv->cmd_write = var->cmd_write; priv->ops = var->ops; priv->write_reg_noack = realtek_mdio_write; diff --git a/drivers/net/dsa/realtek/realtek-smi.c b/drivers/net/dsa/realtek/realtek-smi.c index ff13563059c5..ca808ac52ebc 100644 --- a/drivers/net/dsa/realtek/realtek-smi.c +++ b/drivers/net/dsa/realtek/realtek-smi.c @@ -208,7 +208,7 @@ static int realtek_smi_read_reg(struct realtek_priv *priv, u32 addr, u32 *data) realtek_smi_start(priv); /* Send READ command */ - ret = realtek_smi_write_byte(priv, priv->cmd_read); + ret = realtek_smi_write_byte(priv, (priv->i2c_addr << 1) | 1); if (ret) goto out; @@ -249,7 +249,7 @@ static int realtek_smi_write_reg(struct realtek_priv *priv, realtek_smi_start(priv); /* Send WRITE command */ - ret = realtek_smi_write_byte(priv, priv->cmd_write); + ret = realtek_smi_write_byte(priv, (priv->i2c_addr << 1)); if (ret) goto out; @@ -447,8 +447,7 @@ static int realtek_smi_probe(struct platform_device *pdev) /* Link forward and backward */ priv->dev = dev; priv->clk_delay = var->clk_delay; - priv->cmd_read = var->cmd_read; - priv->cmd_write = var->cmd_write; + priv->i2c_addr = var->i2c_addr; priv->ops = var->ops; priv->setup_interface = realtek_smi_setup_mdio; diff --git a/drivers/net/dsa/realtek/realtek.h b/drivers/net/dsa/realtek/realtek.h index 4fa7c6ba874a..9fce9fc1a4a7 100644 --- a/drivers/net/dsa/realtek/realtek.h +++ b/drivers/net/dsa/realtek/realtek.h @@ -59,8 +59,7 @@ struct realtek_priv { int mdio_addr; unsigned int clk_delay; - u8 cmd_read; - u8 cmd_write; + u8 i2c_addr; spinlock_t lock; /* Locks around command writes */ struct dsa_switch *ds; struct irq_domain *irqdomain; @@ -120,8 +119,7 @@ struct realtek_variant { const struct dsa_switch_ops *ds_ops_mdio; const struct realtek_ops *ops; unsigned int clk_delay; - u8 cmd_read; - u8 cmd_write; + u8 i2c_addr; size_t chip_data_sz; }; diff --git a/drivers/net/dsa/realtek/rtl8365mb.c b/drivers/net/dsa/realtek/rtl8365mb.c index f8c7b2bca205..f07763ff4f59 100644 --- a/drivers/net/dsa/realtek/rtl8365mb.c +++ b/drivers/net/dsa/realtek/rtl8365mb.c @@ -2182,8 +2182,7 @@ const struct realtek_variant rtl8365mb_variant = { .ds_ops_mdio = &rtl8365mb_switch_ops_mdio, .ops = &rtl8365mb_ops, .clk_delay = 10, - .cmd_read = 0xb9, - .cmd_write = 0xb8, + .i2c_addr = 0x5c, .chip_data_sz = sizeof(struct rtl8365mb), }; EXPORT_SYMBOL_GPL(rtl8365mb_variant); diff --git a/drivers/net/dsa/realtek/rtl8366rb.c b/drivers/net/dsa/realtek/rtl8366rb.c index 7868ef237f6c..de06505299da 100644 --- a/drivers/net/dsa/realtek/rtl8366rb.c +++ b/drivers/net/dsa/realtek/rtl8366rb.c @@ -1890,8 +1890,7 @@ const struct realtek_variant rtl8366rb_variant = { .ds_ops_mdio = &rtl8366rb_switch_ops_mdio, .ops = &rtl8366rb_ops, .clk_delay = 10, - .cmd_read = 0xa9, - .cmd_write = 0xa8, + .i2c_addr = 0x54, .chip_data_sz = sizeof(struct rtl8366rb), }; EXPORT_SYMBOL_GPL(rtl8366rb_variant); -- 2.43.2 From 3ce670fc2ce09cabd6f18c9c2028a11c167081dd Mon Sep 17 00:00:00 2001 From: Sam Edwards Date: Fri, 8 Mar 2024 22:38:34 -0700 Subject: [PATCH 03/11] local: realtek: clean up `chip_data` --- drivers/net/dsa/realtek/realtek-mdio.c | 1 - drivers/net/dsa/realtek/realtek-smi.c | 1 - drivers/net/dsa/realtek/realtek.h | 2 +- drivers/net/dsa/realtek/rtl8365mb.c | 38 +++++++++++++------------- drivers/net/dsa/realtek/rtl8366rb.c | 8 +++--- 5 files changed, 24 insertions(+), 26 deletions(-) diff --git a/drivers/net/dsa/realtek/realtek-mdio.c b/drivers/net/dsa/realtek/realtek-mdio.c index 0f29e6273b4a..cde06e1ffa34 100644 --- a/drivers/net/dsa/realtek/realtek-mdio.c +++ b/drivers/net/dsa/realtek/realtek-mdio.c @@ -181,7 +181,6 @@ static int realtek_mdio_probe(struct mdio_device *mdiodev) priv->mdio_addr = mdiodev->addr; priv->bus = mdiodev->bus; priv->dev = &mdiodev->dev; - priv->chip_data = (void *)priv + sizeof(*priv); priv->clk_delay = var->clk_delay; priv->ops = var->ops; diff --git a/drivers/net/dsa/realtek/realtek-smi.c b/drivers/net/dsa/realtek/realtek-smi.c index ca808ac52ebc..b6dc3798600c 100644 --- a/drivers/net/dsa/realtek/realtek-smi.c +++ b/drivers/net/dsa/realtek/realtek-smi.c @@ -423,7 +423,6 @@ static int realtek_smi_probe(struct platform_device *pdev) priv = devm_kzalloc(dev, sizeof(*priv) + var->chip_data_sz, GFP_KERNEL); if (!priv) return -ENOMEM; - priv->chip_data = (void *)priv + sizeof(*priv); mutex_init(&priv->map_lock); diff --git a/drivers/net/dsa/realtek/realtek.h b/drivers/net/dsa/realtek/realtek.h index 9fce9fc1a4a7..61adcd66703f 100644 --- a/drivers/net/dsa/realtek/realtek.h +++ b/drivers/net/dsa/realtek/realtek.h @@ -79,7 +79,7 @@ struct realtek_priv { int vlan4k_enabled; char buf[4096]; - void *chip_data; /* Per-chip extra variant data */ + char chip_data[]; /* Per-chip extra variant data */ }; /* diff --git a/drivers/net/dsa/realtek/rtl8365mb.c b/drivers/net/dsa/realtek/rtl8365mb.c index f07763ff4f59..205645ded149 100644 --- a/drivers/net/dsa/realtek/rtl8365mb.c +++ b/drivers/net/dsa/realtek/rtl8365mb.c @@ -852,7 +852,7 @@ static int rtl8365mb_dsa_phy_write(struct dsa_switch *ds, int phy, int regnum, static const struct rtl8365mb_extint * rtl8365mb_get_port_extint(struct realtek_priv *priv, int port) { - struct rtl8365mb *mb = priv->chip_data; + struct rtl8365mb *mb = (struct rtl8365mb *)priv->chip_data; int i; for (i = 0; i < RTL8365MB_MAX_NUM_EXTINTS; i++) { @@ -877,7 +877,7 @@ rtl8365mb_get_tag_protocol(struct dsa_switch *ds, int port, struct rtl8365mb_cpu *cpu; struct rtl8365mb *mb; - mb = priv->chip_data; + mb = (struct rtl8365mb *)priv->chip_data; cpu = &mb->cpu; if (cpu->position == RTL8365MB_CPU_POS_BEFORE_CRC) @@ -1105,7 +1105,7 @@ static void rtl8365mb_phylink_mac_link_down(struct dsa_switch *ds, int port, struct rtl8365mb *mb; int ret; - mb = priv->chip_data; + mb = (struct rtl8365mb *)priv->chip_data; p = &mb->ports[port]; cancel_delayed_work_sync(&p->mib_work); @@ -1133,7 +1133,7 @@ static void rtl8365mb_phylink_mac_link_up(struct dsa_switch *ds, int port, struct rtl8365mb *mb; int ret; - mb = priv->chip_data; + mb = (struct rtl8365mb *)priv->chip_data; p = &mb->ports[port]; schedule_delayed_work(&p->mib_work, 0); @@ -1289,7 +1289,7 @@ static void rtl8365mb_get_ethtool_stats(struct dsa_switch *ds, int port, u64 *da int ret; int i; - mb = priv->chip_data; + mb = (struct rtl8365mb *)priv->chip_data; mutex_lock(&mb->mib_lock); for (i = 0; i < RTL8365MB_MIB_END; i++) { @@ -1336,7 +1336,7 @@ static void rtl8365mb_get_phy_stats(struct dsa_switch *ds, int port, struct rtl8365mb_mib_counter *mib; struct rtl8365mb *mb; - mb = priv->chip_data; + mb = (struct rtl8365mb *)priv->chip_data; mib = &rtl8365mb_mib_counters[RTL8365MB_MIB_dot3StatsSymbolErrors]; mutex_lock(&mb->mib_lock); @@ -1373,7 +1373,7 @@ static void rtl8365mb_get_mac_stats(struct dsa_switch *ds, int port, int ret; int i; - mb = priv->chip_data; + mb = (struct rtl8365mb *)priv->chip_data; mutex_lock(&mb->mib_lock); for (i = 0; i < RTL8365MB_MIB_END; i++) { @@ -1437,7 +1437,7 @@ static void rtl8365mb_get_ctrl_stats(struct dsa_switch *ds, int port, struct rtl8365mb_mib_counter *mib; struct rtl8365mb *mb; - mb = priv->chip_data; + mb = (struct rtl8365mb *)priv->chip_data; mib = &rtl8365mb_mib_counters[RTL8365MB_MIB_dot3ControlInUnknownOpcodes]; mutex_lock(&mb->mib_lock); @@ -1465,7 +1465,7 @@ static void rtl8365mb_stats_update(struct realtek_priv *priv, int port) [RTL8365MB_MIB_dot3StatsFCSErrors] = 1, [RTL8365MB_MIB_dot3StatsLateCollisions] = 1, }; - struct rtl8365mb *mb = priv->chip_data; + struct rtl8365mb *mb = (struct rtl8365mb *)priv->chip_data; struct rtnl_link_stats64 *stats; int ret; int i; @@ -1544,7 +1544,7 @@ static void rtl8365mb_get_stats64(struct dsa_switch *ds, int port, struct rtl8365mb_port *p; struct rtl8365mb *mb; - mb = priv->chip_data; + mb = (struct rtl8365mb *)priv->chip_data; p = &mb->ports[port]; spin_lock(&p->stats_lock); @@ -1554,7 +1554,7 @@ static void rtl8365mb_get_stats64(struct dsa_switch *ds, int port, static void rtl8365mb_stats_setup(struct realtek_priv *priv) { - struct rtl8365mb *mb = priv->chip_data; + struct rtl8365mb *mb = (struct rtl8365mb *)priv->chip_data; int i; /* Per-chip global mutex to protect MIB counter access, since doing @@ -1580,7 +1580,7 @@ static void rtl8365mb_stats_setup(struct realtek_priv *priv) static void rtl8365mb_stats_teardown(struct realtek_priv *priv) { - struct rtl8365mb *mb = priv->chip_data; + struct rtl8365mb *mb = (struct rtl8365mb *)priv->chip_data; int i; for (i = 0; i < priv->num_ports; i++) { @@ -1707,7 +1707,7 @@ static int rtl8365mb_irq_disable(struct realtek_priv *priv) static int rtl8365mb_irq_setup(struct realtek_priv *priv) { - struct rtl8365mb *mb = priv->chip_data; + struct rtl8365mb *mb = (struct rtl8365mb *)priv->chip_data; struct device_node *intc; u32 irq_trig; int virq; @@ -1826,7 +1826,7 @@ static int rtl8365mb_irq_setup(struct realtek_priv *priv) static void rtl8365mb_irq_teardown(struct realtek_priv *priv) { - struct rtl8365mb *mb = priv->chip_data; + struct rtl8365mb *mb = (struct rtl8365mb *)priv->chip_data; int virq; int i; @@ -1848,7 +1848,7 @@ static void rtl8365mb_irq_teardown(struct realtek_priv *priv) static int rtl8365mb_cpu_config(struct realtek_priv *priv) { - struct rtl8365mb *mb = priv->chip_data; + struct rtl8365mb *mb = (struct rtl8365mb *)priv->chip_data; struct rtl8365mb_cpu *cpu = &mb->cpu; u32 val; int ret; @@ -1882,7 +1882,7 @@ static int rtl8365mb_change_tag_protocol(struct dsa_switch *ds, struct rtl8365mb_cpu *cpu; struct rtl8365mb *mb; - mb = priv->chip_data; + mb = (struct rtl8365mb *)priv->chip_data; cpu = &mb->cpu; switch (proto) { @@ -1908,7 +1908,7 @@ static int rtl8365mb_change_tag_protocol(struct dsa_switch *ds, static int rtl8365mb_switch_init(struct realtek_priv *priv) { - struct rtl8365mb *mb = priv->chip_data; + struct rtl8365mb *mb = (struct rtl8365mb *)priv->chip_data; const struct rtl8365mb_chip_info *ci; int ret; int i; @@ -1961,7 +1961,7 @@ static int rtl8365mb_setup(struct dsa_switch *ds) int ret; int i; - mb = priv->chip_data; + mb = (struct rtl8365mb *)priv->chip_data; cpu = &mb->cpu; ret = rtl8365mb_reset_chip(priv); @@ -2085,7 +2085,7 @@ static int rtl8365mb_get_chip_id_and_ver(struct regmap *map, u32 *id, u32 *ver) static int rtl8365mb_detect(struct realtek_priv *priv) { - struct rtl8365mb *mb = priv->chip_data; + struct rtl8365mb *mb = (struct rtl8365mb *)priv->chip_data; u32 chip_id; u32 chip_ver; int ret; diff --git a/drivers/net/dsa/realtek/rtl8366rb.c b/drivers/net/dsa/realtek/rtl8366rb.c index de06505299da..23dee1ce1d4a 100644 --- a/drivers/net/dsa/realtek/rtl8366rb.c +++ b/drivers/net/dsa/realtek/rtl8366rb.c @@ -812,7 +812,7 @@ static int rtl8366rb_setup(struct dsa_switch *ds) int ret; int i; - rb = priv->chip_data; + rb = (struct rtl8366rb *)priv->chip_data; ret = regmap_read(priv->map, RTL8366RB_CHIP_ID_REG, &chip_id); if (ret) { @@ -1299,7 +1299,7 @@ static int rtl8366rb_vlan_filtering(struct dsa_switch *ds, int port, struct rtl8366rb *rb; int ret; - rb = priv->chip_data; + rb = (struct rtl8366rb *)priv->chip_data; dev_dbg(priv->dev, "port %d: %s VLAN filtering\n", port, vlan_filtering ? "enable" : "disable"); @@ -1409,7 +1409,7 @@ static int rtl8366rb_change_mtu(struct dsa_switch *ds, int port, int new_mtu) int i; /* Cache the per-port MTU setting */ - rb = priv->chip_data; + rb = (struct rtl8366rb *)priv->chip_data; rb->max_mtu[port] = new_mtu; /* Roof out the MTU for the entire switch to the greatest @@ -1615,7 +1615,7 @@ static int rtl8366rb_set_mc_index(struct realtek_priv *priv, int port, int index bool pvid_enabled; int ret; - rb = priv->chip_data; + rb = (struct rtl8366rb *)priv->chip_data; pvid_enabled = !!index; if (port >= priv->num_ports || index >= RTL8366RB_NUM_VLANS) -- 2.43.2 From 1bd0e9b71f9b092592db87488920be09a2a35bc2 Mon Sep 17 00:00:00 2001 From: Sam Edwards Date: Fri, 8 Mar 2024 22:56:42 -0700 Subject: [PATCH 04/11] local: realtek: simplify/clean up regmap config --- drivers/net/dsa/realtek/realtek-mdio.c | 21 ++------------------- drivers/net/dsa/realtek/realtek-smi.c | 20 ++------------------ 2 files changed, 4 insertions(+), 37 deletions(-) diff --git a/drivers/net/dsa/realtek/realtek-mdio.c b/drivers/net/dsa/realtek/realtek-mdio.c index cde06e1ffa34..8622d9c0833d 100644 --- a/drivers/net/dsa/realtek/realtek-mdio.c +++ b/drivers/net/dsa/realtek/realtek-mdio.c @@ -114,12 +114,8 @@ static void realtek_mdio_unlock(void *ctx) } static const struct regmap_config realtek_mdio_regmap_config = { - .reg_bits = 10, /* A4..A0 R4..R0 */ + .reg_bits = 16, .val_bits = 16, - .reg_stride = 1, - /* PHY regs are at 0x8000 */ - .max_register = 0xffff, - .reg_format_endian = REGMAP_ENDIAN_BIG, .reg_read = realtek_mdio_read, .reg_write = realtek_mdio_write, .cache_type = REGCACHE_NONE, @@ -127,19 +123,6 @@ static const struct regmap_config realtek_mdio_regmap_config = { .unlock = realtek_mdio_unlock, }; -static const struct regmap_config realtek_mdio_nolock_regmap_config = { - .reg_bits = 10, /* A4..A0 R4..R0 */ - .val_bits = 16, - .reg_stride = 1, - /* PHY regs are at 0x8000 */ - .max_register = 0xffff, - .reg_format_endian = REGMAP_ENDIAN_BIG, - .reg_read = realtek_mdio_read, - .reg_write = realtek_mdio_write, - .cache_type = REGCACHE_NONE, - .disable_locking = true, -}; - static int realtek_mdio_probe(struct mdio_device *mdiodev) { struct realtek_priv *priv; @@ -170,7 +153,7 @@ static int realtek_mdio_probe(struct mdio_device *mdiodev) return ret; } - rc = realtek_mdio_nolock_regmap_config; + rc.disable_locking = true; priv->map_nolock = devm_regmap_init(dev, NULL, priv, &rc); if (IS_ERR(priv->map_nolock)) { ret = PTR_ERR(priv->map_nolock); diff --git a/drivers/net/dsa/realtek/realtek-smi.c b/drivers/net/dsa/realtek/realtek-smi.c index b6dc3798600c..26d60dbf01bd 100644 --- a/drivers/net/dsa/realtek/realtek-smi.c +++ b/drivers/net/dsa/realtek/realtek-smi.c @@ -325,11 +325,8 @@ static void realtek_smi_unlock(void *ctx) } static const struct regmap_config realtek_smi_regmap_config = { - .reg_bits = 10, /* A4..A0 R4..R0 */ + .reg_bits = 16, .val_bits = 16, - .reg_stride = 1, - /* PHY regs are at 0x8000 */ - .max_register = 0xffff, .reg_format_endian = REGMAP_ENDIAN_BIG, .reg_read = realtek_smi_read, .reg_write = realtek_smi_write, @@ -338,19 +335,6 @@ static const struct regmap_config realtek_smi_regmap_config = { .unlock = realtek_smi_unlock, }; -static const struct regmap_config realtek_smi_nolock_regmap_config = { - .reg_bits = 10, /* A4..A0 R4..R0 */ - .val_bits = 16, - .reg_stride = 1, - /* PHY regs are at 0x8000 */ - .max_register = 0xffff, - .reg_format_endian = REGMAP_ENDIAN_BIG, - .reg_read = realtek_smi_read, - .reg_write = realtek_smi_write, - .cache_type = REGCACHE_NONE, - .disable_locking = true, -}; - static int realtek_smi_mdio_read(struct mii_bus *bus, int addr, int regnum) { struct realtek_priv *priv = bus->priv; @@ -435,7 +419,7 @@ static int realtek_smi_probe(struct platform_device *pdev) return ret; } - rc = realtek_smi_nolock_regmap_config; + rc.disable_locking = true; priv->map_nolock = devm_regmap_init(dev, NULL, priv, &rc); if (IS_ERR(priv->map_nolock)) { ret = PTR_ERR(priv->map_nolock); -- 2.43.2 From 42670d4213d6c03b52707408810e309691d01b5b Mon Sep 17 00:00:00 2001 From: Sam Edwards Date: Mon, 11 Mar 2024 23:11:55 -0600 Subject: [PATCH 05/11] local: realtek: kill off `realtek_smi_write_reg_noack` --- drivers/net/dsa/realtek/realtek-mdio.c | 2 -- drivers/net/dsa/realtek/realtek-smi.c | 26 +++----------------------- drivers/net/dsa/realtek/realtek.h | 1 - drivers/net/dsa/realtek/rtl8365mb.c | 4 ++-- drivers/net/dsa/realtek/rtl8366rb.c | 4 ++-- 5 files changed, 7 insertions(+), 30 deletions(-) diff --git a/drivers/net/dsa/realtek/realtek-mdio.c b/drivers/net/dsa/realtek/realtek-mdio.c index 8622d9c0833d..f8c479d0e5a1 100644 --- a/drivers/net/dsa/realtek/realtek-mdio.c +++ b/drivers/net/dsa/realtek/realtek-mdio.c @@ -168,8 +168,6 @@ static int realtek_mdio_probe(struct mdio_device *mdiodev) priv->clk_delay = var->clk_delay; priv->ops = var->ops; - priv->write_reg_noack = realtek_mdio_write; - np = dev->of_node; dev_set_drvdata(dev, priv); diff --git a/drivers/net/dsa/realtek/realtek-smi.c b/drivers/net/dsa/realtek/realtek-smi.c index 26d60dbf01bd..53a6018d54b8 100644 --- a/drivers/net/dsa/realtek/realtek-smi.c +++ b/drivers/net/dsa/realtek/realtek-smi.c @@ -162,12 +162,6 @@ static int realtek_smi_write_byte(struct realtek_priv *priv, u8 data) return realtek_smi_wait_for_ack(priv); } -static int realtek_smi_write_byte_noack(struct realtek_priv *priv, u8 data) -{ - realtek_smi_write_bits(priv, data, 8); - return 0; -} - static int realtek_smi_read_byte0(struct realtek_priv *priv, u8 *data) { u32 t; @@ -238,8 +232,7 @@ static int realtek_smi_read_reg(struct realtek_priv *priv, u32 addr, u32 *data) return ret; } -static int realtek_smi_write_reg(struct realtek_priv *priv, - u32 addr, u32 data, bool ack) +static int realtek_smi_write_reg(struct realtek_priv *priv, u32 addr, u32 data) { unsigned long flags; int ret; @@ -269,10 +262,7 @@ static int realtek_smi_write_reg(struct realtek_priv *priv, goto out; /* Write DATA[15:8] */ - if (ack) - ret = realtek_smi_write_byte(priv, data >> 8); - else - ret = realtek_smi_write_byte_noack(priv, data >> 8); + ret = realtek_smi_write_byte(priv, data >> 8); if (ret) goto out; @@ -285,22 +275,13 @@ static int realtek_smi_write_reg(struct realtek_priv *priv, return ret; } -/* There is one single case when we need to use this accessor and that - * is when issueing soft reset. Since the device reset as soon as we write - * that bit, no ACK will come back for natural reasons. - */ -static int realtek_smi_write_reg_noack(void *ctx, u32 reg, u32 val) -{ - return realtek_smi_write_reg(ctx, reg, val, false); -} - /* Regmap accessors */ static int realtek_smi_write(void *ctx, u32 reg, u32 val) { struct realtek_priv *priv = ctx; - return realtek_smi_write_reg(priv, reg, val, true); + return realtek_smi_write_reg(priv, reg, val); } static int realtek_smi_read(void *ctx, u32 reg, u32 *val) @@ -434,7 +415,6 @@ static int realtek_smi_probe(struct platform_device *pdev) priv->ops = var->ops; priv->setup_interface = realtek_smi_setup_mdio; - priv->write_reg_noack = realtek_smi_write_reg_noack; dev_set_drvdata(dev, priv); spin_lock_init(&priv->lock); diff --git a/drivers/net/dsa/realtek/realtek.h b/drivers/net/dsa/realtek/realtek.h index 61adcd66703f..eed9bb3ed10a 100644 --- a/drivers/net/dsa/realtek/realtek.h +++ b/drivers/net/dsa/realtek/realtek.h @@ -73,7 +73,6 @@ struct realtek_priv { const struct realtek_ops *ops; int (*setup_interface)(struct dsa_switch *ds); - int (*write_reg_noack)(void *ctx, u32 addr, u32 data); int vlan_enabled; int vlan4k_enabled; diff --git a/drivers/net/dsa/realtek/rtl8365mb.c b/drivers/net/dsa/realtek/rtl8365mb.c index 205645ded149..b723ade207c9 100644 --- a/drivers/net/dsa/realtek/rtl8365mb.c +++ b/drivers/net/dsa/realtek/rtl8365mb.c @@ -1940,8 +1940,8 @@ static int rtl8365mb_reset_chip(struct realtek_priv *priv) { u32 val; - priv->write_reg_noack(priv, RTL8365MB_CHIP_RESET_REG, - FIELD_PREP(RTL8365MB_CHIP_RESET_HW_MASK, 1)); + regmap_write(priv->map, RTL8365MB_CHIP_RESET_REG, + FIELD_PREP(RTL8365MB_CHIP_RESET_HW_MASK, 1)); /* Realtek documentation says the chip needs 1 second to reset. Sleep * for 100 ms before accessing any registers to prevent ACK timeouts. diff --git a/drivers/net/dsa/realtek/rtl8366rb.c b/drivers/net/dsa/realtek/rtl8366rb.c index 23dee1ce1d4a..3f2084bc0b02 100644 --- a/drivers/net/dsa/realtek/rtl8366rb.c +++ b/drivers/net/dsa/realtek/rtl8366rb.c @@ -1760,8 +1760,8 @@ static int rtl8366rb_reset_chip(struct realtek_priv *priv) u32 val; int ret; - priv->write_reg_noack(priv, RTL8366RB_RESET_CTRL_REG, - RTL8366RB_CHIP_CTRL_RESET_HW); + regmap_write(priv->map, RTL8366RB_RESET_CTRL_REG, + RTL8366RB_CHIP_CTRL_RESET_HW); do { usleep_range(20000, 25000); ret = regmap_read(priv->map, RTL8366RB_RESET_CTRL_REG, &val); -- 2.43.2 From 53bf34da29f84eba84a0c22387f476a0a13d6b61 Mon Sep 17 00:00:00 2001 From: Sam Edwards Date: Mon, 11 Mar 2024 23:19:50 -0600 Subject: [PATCH 06/11] local: realtek: become an i2c driver, not platform --- drivers/net/dsa/realtek/Kconfig | 1 + drivers/net/dsa/realtek/realtek-mdio.c | 1 - drivers/net/dsa/realtek/realtek-smi.c | 293 ++----------------------- drivers/net/dsa/realtek/realtek.h | 4 - 4 files changed, 17 insertions(+), 282 deletions(-) diff --git a/drivers/net/dsa/realtek/Kconfig b/drivers/net/dsa/realtek/Kconfig index 060165a85fb7..ee74ef09511d 100644 --- a/drivers/net/dsa/realtek/Kconfig +++ b/drivers/net/dsa/realtek/Kconfig @@ -31,6 +31,7 @@ config NET_DSA_REALTEK_SMI depends on NET_DSA_REALTEK_RTL8365MB || NET_DSA_REALTEK_RTL8366RB depends on NET_DSA_REALTEK_RTL8365MB || !NET_DSA_REALTEK_RTL8365MB depends on NET_DSA_REALTEK_RTL8366RB || !NET_DSA_REALTEK_RTL8366RB + select REGMAP_I2C help Select to enable support for registering switches connected through SMI. diff --git a/drivers/net/dsa/realtek/realtek-mdio.c b/drivers/net/dsa/realtek/realtek-mdio.c index f8c479d0e5a1..d9bd65842fb9 100644 --- a/drivers/net/dsa/realtek/realtek-mdio.c +++ b/drivers/net/dsa/realtek/realtek-mdio.c @@ -165,7 +165,6 @@ static int realtek_mdio_probe(struct mdio_device *mdiodev) priv->bus = mdiodev->bus; priv->dev = &mdiodev->dev; - priv->clk_delay = var->clk_delay; priv->ops = var->ops; np = dev->of_node; diff --git a/drivers/net/dsa/realtek/realtek-smi.c b/drivers/net/dsa/realtek/realtek-smi.c index 53a6018d54b8..1aeb32a1c068 100644 --- a/drivers/net/dsa/realtek/realtek-smi.c +++ b/drivers/net/dsa/realtek/realtek-smi.c @@ -34,263 +34,13 @@ #include #include #include -#include +#include #include #include #include #include "realtek.h" -#define REALTEK_SMI_ACK_RETRY_COUNT 5 - -static inline void realtek_smi_clk_delay(struct realtek_priv *priv) -{ - ndelay(priv->clk_delay); -} - -static void realtek_smi_start(struct realtek_priv *priv) -{ - /* Set GPIO pins to output mode, with initial state: - * SCK = 0, SDA = 1 - */ - gpiod_direction_output(priv->mdc, 0); - gpiod_direction_output(priv->mdio, 1); - realtek_smi_clk_delay(priv); - - /* CLK 1: 0 -> 1, 1 -> 0 */ - gpiod_set_value(priv->mdc, 1); - realtek_smi_clk_delay(priv); - gpiod_set_value(priv->mdc, 0); - realtek_smi_clk_delay(priv); - - /* CLK 2: */ - gpiod_set_value(priv->mdc, 1); - realtek_smi_clk_delay(priv); - gpiod_set_value(priv->mdio, 0); - realtek_smi_clk_delay(priv); - gpiod_set_value(priv->mdc, 0); - realtek_smi_clk_delay(priv); - gpiod_set_value(priv->mdio, 1); -} - -static void realtek_smi_stop(struct realtek_priv *priv) -{ - realtek_smi_clk_delay(priv); - gpiod_set_value(priv->mdio, 0); - gpiod_set_value(priv->mdc, 1); - realtek_smi_clk_delay(priv); - gpiod_set_value(priv->mdio, 1); - realtek_smi_clk_delay(priv); - gpiod_set_value(priv->mdc, 1); - realtek_smi_clk_delay(priv); - gpiod_set_value(priv->mdc, 0); - realtek_smi_clk_delay(priv); - gpiod_set_value(priv->mdc, 1); - - /* Add a click */ - realtek_smi_clk_delay(priv); - gpiod_set_value(priv->mdc, 0); - realtek_smi_clk_delay(priv); - gpiod_set_value(priv->mdc, 1); - - /* Set GPIO pins to input mode */ - gpiod_direction_input(priv->mdio); - gpiod_direction_input(priv->mdc); -} - -static void realtek_smi_write_bits(struct realtek_priv *priv, u32 data, u32 len) -{ - for (; len > 0; len--) { - realtek_smi_clk_delay(priv); - - /* Prepare data */ - gpiod_set_value(priv->mdio, !!(data & (1 << (len - 1)))); - realtek_smi_clk_delay(priv); - - /* Clocking */ - gpiod_set_value(priv->mdc, 1); - realtek_smi_clk_delay(priv); - gpiod_set_value(priv->mdc, 0); - } -} - -static void realtek_smi_read_bits(struct realtek_priv *priv, u32 len, u32 *data) -{ - gpiod_direction_input(priv->mdio); - - for (*data = 0; len > 0; len--) { - u32 u; - - realtek_smi_clk_delay(priv); - - /* Clocking */ - gpiod_set_value(priv->mdc, 1); - realtek_smi_clk_delay(priv); - u = !!gpiod_get_value(priv->mdio); - gpiod_set_value(priv->mdc, 0); - - *data |= (u << (len - 1)); - } - - gpiod_direction_output(priv->mdio, 0); -} - -static int realtek_smi_wait_for_ack(struct realtek_priv *priv) -{ - int retry_cnt; - - retry_cnt = 0; - do { - u32 ack; - - realtek_smi_read_bits(priv, 1, &ack); - if (ack == 0) - break; - - if (++retry_cnt > REALTEK_SMI_ACK_RETRY_COUNT) { - dev_err(priv->dev, "ACK timeout\n"); - return -ETIMEDOUT; - } - } while (1); - - return 0; -} - -static int realtek_smi_write_byte(struct realtek_priv *priv, u8 data) -{ - realtek_smi_write_bits(priv, data, 8); - return realtek_smi_wait_for_ack(priv); -} - -static int realtek_smi_read_byte0(struct realtek_priv *priv, u8 *data) -{ - u32 t; - - /* Read data */ - realtek_smi_read_bits(priv, 8, &t); - *data = (t & 0xff); - - /* Send an ACK */ - realtek_smi_write_bits(priv, 0x00, 1); - - return 0; -} - -static int realtek_smi_read_byte1(struct realtek_priv *priv, u8 *data) -{ - u32 t; - - /* Read data */ - realtek_smi_read_bits(priv, 8, &t); - *data = (t & 0xff); - - /* Send an ACK */ - realtek_smi_write_bits(priv, 0x01, 1); - - return 0; -} - -static int realtek_smi_read_reg(struct realtek_priv *priv, u32 addr, u32 *data) -{ - unsigned long flags; - u8 lo = 0; - u8 hi = 0; - int ret; - - spin_lock_irqsave(&priv->lock, flags); - - realtek_smi_start(priv); - - /* Send READ command */ - ret = realtek_smi_write_byte(priv, (priv->i2c_addr << 1) | 1); - if (ret) - goto out; - - /* Set ADDR[7:0] */ - ret = realtek_smi_write_byte(priv, addr & 0xff); - if (ret) - goto out; - - /* Set ADDR[15:8] */ - ret = realtek_smi_write_byte(priv, addr >> 8); - if (ret) - goto out; - - /* Read DATA[7:0] */ - realtek_smi_read_byte0(priv, &lo); - /* Read DATA[15:8] */ - realtek_smi_read_byte1(priv, &hi); - - *data = ((u32)lo) | (((u32)hi) << 8); - - ret = 0; - - out: - realtek_smi_stop(priv); - spin_unlock_irqrestore(&priv->lock, flags); - - return ret; -} - -static int realtek_smi_write_reg(struct realtek_priv *priv, u32 addr, u32 data) -{ - unsigned long flags; - int ret; - - spin_lock_irqsave(&priv->lock, flags); - - realtek_smi_start(priv); - - /* Send WRITE command */ - ret = realtek_smi_write_byte(priv, (priv->i2c_addr << 1)); - if (ret) - goto out; - - /* Set ADDR[7:0] */ - ret = realtek_smi_write_byte(priv, addr & 0xff); - if (ret) - goto out; - - /* Set ADDR[15:8] */ - ret = realtek_smi_write_byte(priv, addr >> 8); - if (ret) - goto out; - - /* Write DATA[7:0] */ - ret = realtek_smi_write_byte(priv, data & 0xff); - if (ret) - goto out; - - /* Write DATA[15:8] */ - ret = realtek_smi_write_byte(priv, data >> 8); - if (ret) - goto out; - - ret = 0; - - out: - realtek_smi_stop(priv); - spin_unlock_irqrestore(&priv->lock, flags); - - return ret; -} - -/* Regmap accessors */ - -static int realtek_smi_write(void *ctx, u32 reg, u32 val) -{ - struct realtek_priv *priv = ctx; - - return realtek_smi_write_reg(priv, reg, val); -} - -static int realtek_smi_read(void *ctx, u32 reg, u32 *val) -{ - struct realtek_priv *priv = ctx; - - return realtek_smi_read_reg(priv, reg, val); -} - static void realtek_smi_lock(void *ctx) { struct realtek_priv *priv = ctx; @@ -309,8 +59,7 @@ static const struct regmap_config realtek_smi_regmap_config = { .reg_bits = 16, .val_bits = 16, .reg_format_endian = REGMAP_ENDIAN_BIG, - .reg_read = realtek_smi_read, - .reg_write = realtek_smi_write, + .val_format_endian = REGMAP_ENDIAN_LITTLE, .cache_type = REGCACHE_NONE, .lock = realtek_smi_lock, .unlock = realtek_smi_unlock, @@ -373,10 +122,10 @@ static int realtek_smi_setup_mdio(struct dsa_switch *ds) return ret; } -static int realtek_smi_probe(struct platform_device *pdev) +static int realtek_smi_probe(struct i2c_client *client) { const struct realtek_variant *var; - struct device *dev = &pdev->dev; + struct device *dev = &client->dev; struct realtek_priv *priv; struct regmap_config rc; struct device_node *np; @@ -393,7 +142,7 @@ static int realtek_smi_probe(struct platform_device *pdev) rc = realtek_smi_regmap_config; rc.lock_arg = priv; - priv->map = devm_regmap_init(dev, NULL, priv, &rc); + priv->map = devm_regmap_init_i2c(client, &rc); if (IS_ERR(priv->map)) { ret = PTR_ERR(priv->map); dev_err(dev, "regmap init failed: %d\n", ret); @@ -401,7 +150,7 @@ static int realtek_smi_probe(struct platform_device *pdev) } rc.disable_locking = true; - priv->map_nolock = devm_regmap_init(dev, NULL, priv, &rc); + priv->map_nolock = devm_regmap_init_i2c(client, &rc); if (IS_ERR(priv->map_nolock)) { ret = PTR_ERR(priv->map_nolock); dev_err(dev, "regmap init failed: %d\n", ret); @@ -410,13 +159,11 @@ static int realtek_smi_probe(struct platform_device *pdev) /* Link forward and backward */ priv->dev = dev; - priv->clk_delay = var->clk_delay; - priv->i2c_addr = var->i2c_addr; priv->ops = var->ops; priv->setup_interface = realtek_smi_setup_mdio; - dev_set_drvdata(dev, priv); + i2c_set_clientdata(client, priv); spin_lock_init(&priv->lock); /* TODO: if power is software controlled, set up any regulators here */ @@ -435,14 +182,6 @@ static int realtek_smi_probe(struct platform_device *pdev) dev_dbg(dev, "deasserted RESET\n"); } - /* Fetch MDIO pins */ - priv->mdc = devm_gpiod_get_optional(dev, "mdc", GPIOD_OUT_LOW); - if (IS_ERR(priv->mdc)) - return PTR_ERR(priv->mdc); - priv->mdio = devm_gpiod_get_optional(dev, "mdio", GPIOD_OUT_LOW); - if (IS_ERR(priv->mdio)) - return PTR_ERR(priv->mdio); - priv->leds_disabled = of_property_read_bool(np, "realtek,disable-leds"); ret = priv->ops->detect(priv); @@ -468,12 +207,12 @@ static int realtek_smi_probe(struct platform_device *pdev) return 0; } -static int realtek_smi_remove(struct platform_device *pdev) +static void realtek_smi_remove(struct i2c_client *client) { - struct realtek_priv *priv = platform_get_drvdata(pdev); + struct realtek_priv *priv = i2c_get_clientdata(client); if (!priv) - return 0; + return; dsa_unregister_switch(priv->ds); if (priv->slave_mii_bus) @@ -483,19 +222,19 @@ static int realtek_smi_remove(struct platform_device *pdev) if (priv->reset) gpiod_set_value(priv->reset, 1); - return 0; + return; } -static void realtek_smi_shutdown(struct platform_device *pdev) +static void realtek_smi_shutdown(struct i2c_client *client) { - struct realtek_priv *priv = platform_get_drvdata(pdev); + struct realtek_priv *priv = i2c_get_clientdata(client); if (!priv) return; dsa_switch_shutdown(priv->ds); - platform_set_drvdata(pdev, NULL); + i2c_set_clientdata(client, NULL); } static const struct of_device_id realtek_smi_of_match[] = { @@ -515,7 +254,7 @@ static const struct of_device_id realtek_smi_of_match[] = { }; MODULE_DEVICE_TABLE(of, realtek_smi_of_match); -static struct platform_driver realtek_smi_driver = { +static struct i2c_driver realtek_smi_driver = { .driver = { .name = "realtek-smi", .of_match_table = realtek_smi_of_match, @@ -524,7 +263,7 @@ static struct platform_driver realtek_smi_driver = { .remove = realtek_smi_remove, .shutdown = realtek_smi_shutdown, }; -module_platform_driver(realtek_smi_driver); +module_i2c_driver(realtek_smi_driver); MODULE_AUTHOR("Linus Walleij "); MODULE_DESCRIPTION("Driver for Realtek ethernet switch connected via SMI interface"); diff --git a/drivers/net/dsa/realtek/realtek.h b/drivers/net/dsa/realtek/realtek.h index eed9bb3ed10a..cd2b9e774044 100644 --- a/drivers/net/dsa/realtek/realtek.h +++ b/drivers/net/dsa/realtek/realtek.h @@ -49,8 +49,6 @@ struct rtl8366_vlan_4k { struct realtek_priv { struct device *dev; struct gpio_desc *reset; - struct gpio_desc *mdc; - struct gpio_desc *mdio; struct regmap *map; struct regmap *map_nolock; struct mutex map_lock; @@ -58,8 +56,6 @@ struct realtek_priv { struct mii_bus *bus; int mdio_addr; - unsigned int clk_delay; - u8 i2c_addr; spinlock_t lock; /* Locks around command writes */ struct dsa_switch *ds; struct irq_domain *irqdomain; -- 2.43.2 From 8f6ff7666048234af439687962ed8fc72209c542 Mon Sep 17 00:00:00 2001 From: Sam Edwards Date: Wed, 13 Mar 2024 19:48:18 -0600 Subject: [PATCH 07/11] local: realtek: Implement "Realtek"/"RTK" I2C mode --- drivers/net/dsa/realtek/realtek-smi.c | 79 ++++++++++++++++++++++++++- 1 file changed, 78 insertions(+), 1 deletion(-) diff --git a/drivers/net/dsa/realtek/realtek-smi.c b/drivers/net/dsa/realtek/realtek-smi.c index 1aeb32a1c068..01e0634ce999 100644 --- a/drivers/net/dsa/realtek/realtek-smi.c +++ b/drivers/net/dsa/realtek/realtek-smi.c @@ -41,6 +41,75 @@ #include "realtek.h" +static int realtek_smi_rtk_read(void *ctx, const void *reg, size_t reg_size, + void *val, size_t val_size) +{ + struct device *dev = ctx; + struct i2c_client *i2c = to_i2c_client(dev); + int ret; + + /* In "Realtek" I2C mode, read transfers aren't quite the same as those + * of standard I2C. A conventional I2C read involves a write transfer + * to specify the register, followed by a read transfer to retrieve the + * value. The "Realtek" mode uses a single transfer, but the adapter + * sends 2 extra bytes after the address byte to specify the register + * before the bus turns around to complete the read operation. + * + * This can be accomplished through prudent use of the NOSTART flag to + * insert the register bytes into the middle of a read transfer without + * the device being any the wiser. + */ + struct i2c_msg xfer[] = { + /* Start a transfer and send only the start byte + * (address and R/W̅ bit) */ + { + .addr = i2c->addr, + .flags = I2C_M_RD, + .len = 0 + }, + + /* Send 2 extra bytes to specify the register */ + { + .addr = i2c->addr, + .flags = I2C_M_NOSTART, + .len = reg_size, + .buf = (void *)reg + }, + + /* Now turn the bus around and finish the read */ + { + .addr = i2c->addr, + .flags = I2C_M_RD | I2C_M_NOSTART, + .len = val_size, + .buf = val + } + }; + + ret = i2c_transfer(i2c->adapter, xfer, ARRAY_SIZE(xfer)); + if (ret == ARRAY_SIZE(xfer)) + return 0; + else if (ret < 0) + return ret; + else + return -EIO; +} + +static int realtek_smi_rtk_write(void *ctx, const void *data, size_t count) +{ + struct device *dev = ctx; + struct i2c_client *i2c = to_i2c_client(dev); + int ret; + + /* Writes, on the other hand, are no different in 'rtk' mode. */ + ret = i2c_master_send(i2c, data, count); + if (ret == count) + return 0; + else if (ret < 0) + return ret; + else + return -EIO; +} + static void realtek_smi_lock(void *ctx) { struct realtek_priv *priv = ctx; @@ -58,8 +127,10 @@ static void realtek_smi_unlock(void *ctx) static const struct regmap_config realtek_smi_regmap_config = { .reg_bits = 16, .val_bits = 16, - .reg_format_endian = REGMAP_ENDIAN_BIG, + .reg_format_endian = REGMAP_ENDIAN_LITTLE, .val_format_endian = REGMAP_ENDIAN_LITTLE, + .read = realtek_smi_rtk_read, + .write = realtek_smi_rtk_write, .cache_type = REGCACHE_NONE, .lock = realtek_smi_lock, .unlock = realtek_smi_unlock, @@ -134,6 +205,12 @@ static int realtek_smi_probe(struct i2c_client *client) var = of_device_get_match_data(dev); np = dev->of_node; + /* The "Realtek" protocol variant requires nonstandard functionality */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_NOSTART)) { + dev_err(dev, "'rtk' mode needs bus with NOSTART support\n"); + return -ENODEV; + } + priv = devm_kzalloc(dev, sizeof(*priv) + var->chip_data_sz, GFP_KERNEL); if (!priv) return -ENOMEM; -- 2.43.2 From 675d9d3af6a2e07dd71577876f2b2bffb422f794 Mon Sep 17 00:00:00 2001 From: Sam Edwards Date: Tue, 12 Mar 2024 03:41:04 -0600 Subject: [PATCH 08/11] local: realtek: add back GPIO support --- drivers/net/dsa/realtek/Kconfig | 1 + drivers/net/dsa/realtek/realtek-smi.c | 114 ++++++++++++++++++++++++++ 2 files changed, 115 insertions(+) diff --git a/drivers/net/dsa/realtek/Kconfig b/drivers/net/dsa/realtek/Kconfig index ee74ef09511d..81a0e0270557 100644 --- a/drivers/net/dsa/realtek/Kconfig +++ b/drivers/net/dsa/realtek/Kconfig @@ -31,6 +31,7 @@ config NET_DSA_REALTEK_SMI depends on NET_DSA_REALTEK_RTL8365MB || NET_DSA_REALTEK_RTL8366RB depends on NET_DSA_REALTEK_RTL8365MB || !NET_DSA_REALTEK_RTL8365MB depends on NET_DSA_REALTEK_RTL8366RB || !NET_DSA_REALTEK_RTL8366RB + select I2C_GPIO select REGMAP_I2C help Select to enable support for registering switches connected diff --git a/drivers/net/dsa/realtek/realtek-smi.c b/drivers/net/dsa/realtek/realtek-smi.c index 01e0634ce999..fce1daac3557 100644 --- a/drivers/net/dsa/realtek/realtek-smi.c +++ b/drivers/net/dsa/realtek/realtek-smi.c @@ -31,10 +31,13 @@ #include #include #include +#include #include #include #include +#include #include +#include #include #include #include @@ -342,6 +345,117 @@ static struct i2c_driver realtek_smi_driver = { }; module_i2c_driver(realtek_smi_driver); +struct realtek_smi_plat_priv { + struct i2c_adapter adap; + struct i2c_algo_bit_data algo; + struct gpio_desc *sda, *scl; + struct i2c_client *client; +}; + +static void realtek_smi_setsda(void *data, int state) +{ + struct realtek_smi_plat_priv *priv = data; + + gpiod_set_value_cansleep(priv->sda, state); +} + +static void realtek_smi_setscl(void *data, int state) +{ + struct realtek_smi_plat_priv *priv = data; + + gpiod_set_value_cansleep(priv->scl, state); +} + +static int realtek_smi_getsda(void *data) +{ + struct realtek_smi_plat_priv *priv = data; + + return gpiod_get_value_cansleep(priv->sda); +} + +static int realtek_smi_getscl(void *data) +{ + struct realtek_smi_plat_priv *priv = data; + + return gpiod_get_value_cansleep(priv->scl); +} + +static int realtek_smi_plat_probe(struct platform_device *pdev) +{ + const struct of_device_id *match; + const struct realtek_variant *var; + struct device *dev = &pdev->dev; + struct realtek_smi_plat_priv *priv; + struct i2c_board_info binfo; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + platform_set_drvdata(pdev, priv); + + match = of_match_device(dev->driver->of_match_table, dev); + var = match->data; + + /* Fetch SMI pins */ + priv->sda = devm_gpiod_get(dev, "mdio", GPIOD_OUT_HIGH_OPEN_DRAIN); + if (IS_ERR(priv->sda)) + return PTR_ERR(priv->sda); + priv->scl = devm_gpiod_get(dev, "mdc", GPIOD_OUT_HIGH_OPEN_DRAIN); + if (IS_ERR(priv->scl)) + return PTR_ERR(priv->scl); + + /* Initialize I2C adapter */ + strscpy(priv->adap.name, dev_name(dev), sizeof(priv->adap.name)); + priv->adap.owner = THIS_MODULE; + priv->adap.algo_data = &priv->algo; + priv->adap.dev.parent = dev; + priv->algo.setsda = realtek_smi_setsda; + priv->algo.setscl = realtek_smi_setscl; + priv->algo.getsda = realtek_smi_getsda; + priv->algo.getscl = realtek_smi_getscl; + priv->algo.udelay = var->clk_delay; + priv->algo.timeout = HZ / 10; /* 100 ms */ + priv->algo.data = priv; + + ret = i2c_bit_add_bus(&priv->adap); + if (ret) { + dev_err(dev, "Failed to create I2C adapter\n"); + return ret; + } + + memset(&binfo, 0, sizeof(binfo)); + strscpy(binfo.type, match->compatible, I2C_NAME_SIZE); + binfo.addr = var->i2c_addr; + binfo.of_node = dev->of_node; + + priv->client = i2c_new_client_device(&priv->adap, &binfo); + if (IS_ERR(priv->client)) { + dev_err(dev, "Failed to create I2C device\n"); + return PTR_ERR(priv->client); + } + + return 0; +} + +static void realtek_smi_plat_remove(struct platform_device *pdev) +{ + struct realtek_smi_plat_priv *priv = platform_get_drvdata(pdev); + + i2c_unregister_device(priv->client); + i2c_del_adapter(&priv->adap); +} + +static struct platform_driver realtek_smi_plat_driver = { + .driver = { + .name = "realtek-smi", + .of_match_table = realtek_smi_of_match, + }, + .probe = realtek_smi_plat_probe, + .remove_new = realtek_smi_plat_remove, +}; +module_platform_driver(realtek_smi_plat_driver); + MODULE_AUTHOR("Linus Walleij "); MODULE_DESCRIPTION("Driver for Realtek ethernet switch connected via SMI interface"); MODULE_LICENSE("GPL"); -- 2.43.2 From e2474c1d276b790b55b9518df4e0f1959221db65 Mon Sep 17 00:00:00 2001 From: Sam Edwards Date: Thu, 21 Mar 2024 16:39:46 -0600 Subject: [PATCH 09/11] local: begin improving tagging decoder --- net/dsa/tag_rtl8_4.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/net/dsa/tag_rtl8_4.c b/net/dsa/tag_rtl8_4.c index 4f67834fd121..f9181cff5f0e 100644 --- a/net/dsa/tag_rtl8_4.c +++ b/net/dsa/tag_rtl8_4.c @@ -35,8 +35,13 @@ * X | reserved * ------------+------------- * REASON | reason for forwarding packet to CPU - * | 0: packet was forwarded or flooded to CPU - * | 80: packet was trapped to CPU + * | 0x00: packet was forwarded or flooded to CPU + * | 0x41: dot1x unauthorized + * | 0x4B: port learning limit exceeded + * | 0x50: unknown unicast destination MAC + * | 0x51: unknown multicast destination MAC + * | 0x54: unknown source MAC + * | 0x55: unmatched source MAC * FID_EN | 1: packet has an FID * | 0: no FID * FID | FID of packet (if FID_EN=1) @@ -92,8 +97,13 @@ #define RTL8_4_PROTOCOL GENMASK(15, 8) #define RTL8_4_PROTOCOL_RTL8365MB 0x04 #define RTL8_4_REASON GENMASK(7, 0) -#define RTL8_4_REASON_FORWARD 0 -#define RTL8_4_REASON_TRAP 80 +#define RTL8_4_REASON_FORWARD 0x00 +#define RTL8_4_REASON_1XUNAUTH 0x41 +#define RTL8_4_REASON_LUT_OVER 0x4B +#define RTL8_4_REASON_UNK_UNICAST 0x50 +#define RTL8_4_REASON_UNK_MULTICAST 0x51 +#define RTL8_4_REASON_UNK_SOURCE 0x54 +#define RTL8_4_REASON_UNMATCH_SOURCE 0x55 #define RTL8_4_LEARN_DIS BIT(5) @@ -188,7 +198,7 @@ static int rtl8_4_read_tag(struct sk_buff *skb, struct net_device *dev, return -ENOENT; } - if (reason != RTL8_4_REASON_TRAP) + if (reason == RTL8_4_REASON_FORWARD) dsa_default_offload_fwd_mark(skb); return 0; -- 2.43.2 From b7c86d836c03ba9a8254d564697792612aed9e90 Mon Sep 17 00:00:00 2001 From: Sam Edwards Date: Thu, 21 Mar 2024 19:10:46 -0600 Subject: [PATCH 10/11] local: realtek: trap standalone ports rather than use the isolation matrix --- drivers/net/dsa/realtek/rtl8365mb.c | 63 +++++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 4 deletions(-) diff --git a/drivers/net/dsa/realtek/rtl8365mb.c b/drivers/net/dsa/realtek/rtl8365mb.c index b723ade207c9..d4f0374d9dac 100644 --- a/drivers/net/dsa/realtek/rtl8365mb.c +++ b/drivers/net/dsa/realtek/rtl8365mb.c @@ -281,6 +281,18 @@ (RTL8365MB_PORT_ISOLATION_REG_BASE + (_physport)) #define RTL8365MB_PORT_ISOLATION_MASK 0x07FF +/* Dot1x registers; to put ports in UNAUTH mode so they trap on ingress */ +#define RTL8365MB_DOT1X_PORT_ENABLE_REG 0x0A80 +#define RTL8365MB_DOT1X_PORT_AUTH_REG 0x0A82 +#define RTL8365MB_DOT1X_PORT_OPDIR_REG 0x0A83 +#define RTL8365MB_DOT1X_UNAUTH_ACT_BASE 0x0A84 +#define RTL8365MB_DOT1X_UNAUTH_ACT_REG(_physport) \ + (RTL8365MB_DOT1X_UNAUTH_ACT_BASE + ((_physport) >> 3)) +#define RTL8365MB_DOT1X_UNAUTH_ACT_OFFSET(_physport) \ + (((_physport) & 0x7) << 1) +#define RTL8365MB_DOT1X_UNAUTH_ACT_MASK(_physport) \ + (0x3 << RTL8365MB_DOT1X_UNAUTH_ACT_OFFSET((_physport))) + /* MSTP port state registers - indexed by tree instance */ #define RTL8365MB_MSTI_CTRL_BASE 0x0A00 #define RTL8365MB_MSTI_CTRL_REG(_msti, _physport) \ @@ -597,6 +609,12 @@ enum rtl8365mb_cpu_rxlen { RTL8365MB_CPU_RXLEN_64BYTES = 1, }; +enum rtl8365mb_dot1x_action { + RTL8365MB_DOT1X_ACTION_DROP = 0, + RTL8365MB_DOT1X_ACTION_TRAP = 1, + RTL8365MB_DOT1X_ACTION_VLAN = 2, +}; + /** * struct rtl8365mb_cpu - CPU port configuration * @enable: enable/disable hardware insertion of CPU tag in switch->CPU frames @@ -1228,6 +1246,43 @@ static int rtl8365mb_port_set_isolation(struct realtek_priv *priv, int port, return regmap_write(priv->map, RTL8365MB_PORT_ISOLATION_REG(port), mask); } +static int rtl8365mb_port_set_trapping(struct realtek_priv *priv, int port, + bool enable) +{ + int ret; + + /* Prevent ingress traffic from being handled by the switch fabric by + * using dot1x mode. The dot1x functionality isn't used elsewhere in + * the driver, so is a convenient method for trapping ingress traffic. + */ + + ret = regmap_update_bits(priv->map, RTL8365MB_DOT1X_PORT_ENABLE_REG, + BIT(port), enable ? BIT(port) : 0); + if (ret) + return ret; + + if (!enable) + return 0; + + ret = regmap_update_bits(priv->map, RTL8365MB_DOT1X_PORT_AUTH_REG, + BIT(port), 0); + if (ret) + return ret; + + /* Match on input only */ + ret = regmap_update_bits(priv->map, RTL8365MB_DOT1X_PORT_OPDIR_REG, + BIT(port), BIT(port)); + if (ret) + return ret; + + /* Set action to TRAP */ + return regmap_update_bits(priv->map, + RTL8365MB_DOT1X_UNAUTH_ACT_REG(port), + RTL8365MB_DOT1X_UNAUTH_ACT_MASK(port), + RTL8365MB_DOT1X_ACTION_TRAP << + RTL8365MB_DOT1X_UNAUTH_ACT_OFFSET(port)); +} + static int rtl8365mb_mib_counter_read(struct realtek_priv *priv, int port, u32 offset, u32 length, u64 *mibvalue) { @@ -2003,13 +2058,13 @@ static int rtl8365mb_setup(struct dsa_switch *ds) if (dsa_is_unused_port(priv->ds, i)) continue; - /* Forward only to the CPU */ - ret = rtl8365mb_port_set_isolation(priv, i, cpu->mask); + /* Clear the port's isolation mask */ + ret = rtl8365mb_port_set_isolation(priv, i, 0); if (ret) goto out_teardown_irq; - /* Disable learning */ - ret = rtl8365mb_port_set_learning(priv, i, false); + /* Trap all ingress traffic on this port */ + ret = rtl8365mb_port_set_trapping(priv, i, true); if (ret) goto out_teardown_irq; -- 2.43.2 From e88bfa1bf5804123596c625c5bb18b40e60c2246 Mon Sep 17 00:00:00 2001 From: Sam Edwards Date: Fri, 22 Mar 2024 17:43:16 -0600 Subject: [PATCH 11/11] local: realtek: implement bridge offload --- drivers/net/dsa/realtek/rtl8365mb.c | 80 +++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/drivers/net/dsa/realtek/rtl8365mb.c b/drivers/net/dsa/realtek/rtl8365mb.c index d4f0374d9dac..61f8dac3c914 100644 --- a/drivers/net/dsa/realtek/rtl8365mb.c +++ b/drivers/net/dsa/realtek/rtl8365mb.c @@ -275,6 +275,15 @@ #define RTL8365MB_LUT_PORT_LEARN_LIMIT_REG(_physport) \ (RTL8365MB_LUT_PORT_LEARN_LIMIT_BASE + (_physport)) +/* Port-based EFID registers */ +#define RTL8365MB_PORT_EFID_BASE 0x0A32 +#define RTL8365MB_PORT_EFID_REG(_physport) \ + (RTL8365MB_PORT_EFID_BASE + ((_physport) >> 2)) +#define RTL8365MB_PORT_EFID_OFFSET(_physport) \ + (((_physport) & 0x3) << 2) +#define RTL8365MB_PORT_EFID_MASK(_physport) \ + (0x7 << RTL8365MB_PORT_EFID_OFFSET((_physport))) + /* Port isolation (forwarding mask) registers */ #define RTL8365MB_PORT_ISOLATION_REG_BASE 0x08A2 #define RTL8365MB_PORT_ISOLATION_REG(_physport) \ @@ -1283,6 +1292,70 @@ static int rtl8365mb_port_set_trapping(struct realtek_priv *priv, int port, RTL8365MB_DOT1X_UNAUTH_ACT_OFFSET(port)); } +static int rtl8365mb_port_set_efid(struct realtek_priv *priv, int port, u8 efid) +{ + return regmap_update_bits(priv->map, RTL8365MB_PORT_EFID_REG(port), + RTL8365MB_PORT_EFID_MASK(port), + efid << RTL8365MB_PORT_EFID_OFFSET(port)); +} + +static int rtl8365mb_bridge_apply_isolation(struct realtek_priv *priv, + const struct dsa_bridge *bridge) +{ + struct rtl8365mb *mb = (struct rtl8365mb *)priv->chip_data; + struct rtl8365mb_cpu *cpu = &mb->cpu; + unsigned long cpu_mask = cpu->mask; + unsigned long bridge_mask; + int ret; + int port; + + /* Make sure the CPU is included in every bridge */ + bridge_mask = cpu_mask; + + for (int i = 0; i < priv->num_ports; i++) { + if (dsa_port_offloads_bridge(dsa_to_port(priv->ds, i), bridge)) + bridge_mask |= BIT(i); + } + + /* Don't set the CPU port's isolation; it's bypassed anyway */ + for_each_andnot_bit(port, &bridge_mask, &cpu_mask, priv->num_ports) { + ret = rtl8365mb_port_set_isolation(priv, port, bridge_mask); + if (ret) + return ret; + } + + return 0; +} + +static int rtl8365mb_port_bridge_join(struct dsa_switch *ds, int port, + struct dsa_bridge bridge, + bool *tx_fwd_offload, + struct netlink_ext_ack *extack) +{ + struct realtek_priv *priv = ds->priv; + u8 efid = bridge.num; + int ret; + + ret = rtl8365mb_port_set_efid(priv, port, efid); + if (ret) + return ret; + + ret = rtl8365mb_bridge_apply_isolation(priv, &bridge); + if (ret) + return ret; + + return rtl8365mb_port_set_trapping(priv, port, false); +} + +static void rtl8365mb_port_bridge_leave(struct dsa_switch *ds, int port, + struct dsa_bridge bridge) +{ + struct realtek_priv *priv = ds->priv; + + rtl8365mb_port_set_trapping(priv, port, true); + rtl8365mb_bridge_apply_isolation(priv, &bridge); +} + static int rtl8365mb_mib_counter_read(struct realtek_priv *priv, int port, u32 offset, u32 length, u64 *mibvalue) { @@ -2016,6 +2089,9 @@ static int rtl8365mb_setup(struct dsa_switch *ds) int ret; int i; + ds->fdb_isolation = true; + ds->max_num_bridges = 7; + mb = (struct rtl8365mb *)priv->chip_data; cpu = &mb->cpu; @@ -2201,6 +2277,8 @@ static const struct dsa_switch_ops rtl8365mb_switch_ops_smi = { .get_stats64 = rtl8365mb_get_stats64, .port_change_mtu = rtl8365mb_port_change_mtu, .port_max_mtu = rtl8365mb_port_max_mtu, + .port_bridge_join = rtl8365mb_port_bridge_join, + .port_bridge_leave = rtl8365mb_port_bridge_leave, }; static const struct dsa_switch_ops rtl8365mb_switch_ops_mdio = { @@ -2224,6 +2302,8 @@ static const struct dsa_switch_ops rtl8365mb_switch_ops_mdio = { .get_stats64 = rtl8365mb_get_stats64, .port_change_mtu = rtl8365mb_port_change_mtu, .port_max_mtu = rtl8365mb_port_max_mtu, + .port_bridge_join = rtl8365mb_port_bridge_join, + .port_bridge_leave = rtl8365mb_port_bridge_leave, }; static const struct realtek_ops rtl8365mb_ops = { -- 2.43.2