From 8569bc5e0d1bdc4b252bf3f4e7c893ea2e17c98f Mon Sep 17 00:00:00 2001 From: Alexander Couzens Date: Wed, 9 Jun 2021 23:46:35 +0200 Subject: [PATCH] ramips: ethernet: ralink: rewrite esw_rt3050 to support link states Ensure the esw is initialized before the ethernet device is sending packets. Further implement carrier detection similar to mt7620. If any port has a link, the ethernet device will detect a carrier. Signed-off-by: Alexander Couzens --- .../drivers/net/ethernet/ralink/esw_rt3050.c | 142 ++++++++++++------ .../drivers/net/ethernet/ralink/esw_rt3050.h | 3 + .../drivers/net/ethernet/ralink/soc_rt3050.c | 3 + 3 files changed, 103 insertions(+), 45 deletions(-) diff --git a/target/linux/ramips/files/drivers/net/ethernet/ralink/esw_rt3050.c b/target/linux/ramips/files/drivers/net/ethernet/ralink/esw_rt3050.c index 4a7dbb2d03952..75b6707be2b55 100644 --- a/target/linux/ramips/files/drivers/net/ethernet/ralink/esw_rt3050.c +++ b/target/linux/ramips/files/drivers/net/ethernet/ralink/esw_rt3050.c @@ -24,6 +24,7 @@ #include #include "mtk_eth_soc.h" +#include "esw_rt3050.h" /* HW limitations for this switch: * - No large frame support (PKT_MAX_LEN at most 1536) @@ -216,6 +217,7 @@ struct rt305x_esw { struct device *dev; void __iomem *base; int irq; + struct fe_priv *priv; /* Protects against concurrent register r/w operations. */ spinlock_t reg_rw_lock; @@ -736,19 +738,44 @@ static void esw_hw_init(struct rt305x_esw *esw) esw_w32(esw, ~RT305X_ESW_PORT_ST_CHG, RT305X_ESW_REG_IMR); } + +int rt3050_esw_has_carrier(struct fe_priv *priv) +{ + struct rt305x_esw *esw = priv->soc->swpriv; + u32 link; + int i; + bool cpuport; + + link = esw_r32(esw, RT305X_ESW_REG_POA); + link >>= RT305X_ESW_POA_LINK_SHIFT; + cpuport = link & BIT(RT305X_ESW_PORT6); + link &= RT305X_ESW_POA_LINK_MASK; + for (i = 0; i <= RT305X_ESW_PORT5; i++) { + if (priv->link[i] != (link & BIT(i))) + dev_info(esw->dev, "port %d link %s\n", i, link & BIT(i) ? "up" : "down"); + priv->link[i] = link & BIT(i); + } + + return !!link && cpuport; +} + static irqreturn_t esw_interrupt(int irq, void *_esw) { - struct rt305x_esw *esw = (struct rt305x_esw *)_esw; + struct rt305x_esw *esw = (struct rt305x_esw *) _esw; u32 status; + int i; status = esw_r32(esw, RT305X_ESW_REG_ISR); if (status & RT305X_ESW_PORT_ST_CHG) { - u32 link = esw_r32(esw, RT305X_ESW_REG_POA); - - link >>= RT305X_ESW_POA_LINK_SHIFT; - link &= RT305X_ESW_POA_LINK_MASK; - dev_info(esw->dev, "link changed 0x%02X\n", link); + if (!esw->priv) + goto out; + if (rt3050_esw_has_carrier(esw->priv)) + netif_carrier_on(esw->priv->netdev); + else + netif_carrier_off(esw->priv->netdev); } + +out: esw_w32(esw, status, RT305X_ESW_REG_ISR); return IRQ_HANDLED; @@ -1376,9 +1403,7 @@ static int esw_probe(struct platform_device *pdev) struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); struct device_node *np = pdev->dev.of_node; const __be32 *port_map, *port_disable, *reg_init; - struct switch_dev *swdev; struct rt305x_esw *esw; - int ret; esw = devm_kzalloc(&pdev->dev, sizeof(*esw), GFP_KERNEL); if (!esw) @@ -1417,6 +1442,67 @@ static int esw_probe(struct platform_device *pdev) if (IS_ERR(esw->rst_ephy)) esw->rst_ephy = NULL; + spin_lock_init(&esw->reg_rw_lock); + platform_set_drvdata(pdev, esw); + + return 0; +} + +static int esw_remove(struct platform_device *pdev) +{ + struct rt305x_esw *esw = platform_get_drvdata(pdev); + + if (esw) { + esw_w32(esw, ~0, RT305X_ESW_REG_IMR); + platform_set_drvdata(pdev, NULL); + } + + return 0; +} + +static const struct of_device_id ralink_esw_match[] = { + { .compatible = "ralink,rt3050-esw" }, + {}, +}; +MODULE_DEVICE_TABLE(of, ralink_esw_match); + +/* called by the ethernet driver to bound with the switch driver */ +int rt3050_esw_init(struct fe_priv *priv) +{ + struct device_node *np = priv->switch_np; + struct platform_device *pdev = of_find_device_by_node(np); + struct switch_dev *swdev; + struct rt305x_esw *esw; + const __be32 *rgmii; + int ret; + + if (!pdev) + return -ENODEV; + + if (!of_device_is_compatible(np, ralink_esw_match->compatible)) + return -EINVAL; + + esw = platform_get_drvdata(pdev); + if (!esw) + return -EPROBE_DEFER; + + priv->soc->swpriv = esw; + esw->priv = priv; + + esw_hw_init(esw); + + rgmii = of_get_property(np, "ralink,rgmii", NULL); + if (rgmii && be32_to_cpu(*rgmii) == 1) { + /* + * External switch connected to RGMII interface. + * Unregister the switch device after initialization. + */ + dev_err(&pdev->dev, "RGMII mode, not exporting switch device.\n"); + unregister_switch(&esw->swdev); + platform_set_drvdata(pdev, NULL); + return -ENODEV; + } + swdev = &esw->swdev; swdev->of_node = pdev->dev.of_node; swdev->name = "rt305x-esw"; @@ -1432,53 +1518,19 @@ static int esw_probe(struct platform_device *pdev) return ret; } - platform_set_drvdata(pdev, esw); - - spin_lock_init(&esw->reg_rw_lock); - - esw_hw_init(esw); - - reg_init = of_get_property(np, "ralink,rgmii", NULL); - if (reg_init && be32_to_cpu(*reg_init) == 1) { - /* - * External switch connected to RGMII interface. - * Unregister the switch device after initialization. - */ - dev_err(&pdev->dev, "RGMII mode, not exporting switch device.\n"); - unregister_switch(&esw->swdev); - platform_set_drvdata(pdev, NULL); - return -ENODEV; - } - ret = devm_request_irq(&pdev->dev, esw->irq, esw_interrupt, 0, "esw", - esw); - + esw); if (!ret) { esw_w32(esw, RT305X_ESW_PORT_ST_CHG, RT305X_ESW_REG_ISR); esw_w32(esw, ~RT305X_ESW_PORT_ST_CHG, RT305X_ESW_REG_IMR); } - return ret; -} - -static int esw_remove(struct platform_device *pdev) -{ - struct rt305x_esw *esw = platform_get_drvdata(pdev); - - if (esw) { - esw_w32(esw, ~0, RT305X_ESW_REG_IMR); - platform_set_drvdata(pdev, NULL); - } + dev_info(&pdev->dev, "mediatek esw at 0x%08lx, irq %d initialized\n", + esw->base, esw->irq); return 0; } -static const struct of_device_id ralink_esw_match[] = { - { .compatible = "ralink,rt3050-esw" }, - {}, -}; -MODULE_DEVICE_TABLE(of, ralink_esw_match); - static struct platform_driver esw_driver = { .probe = esw_probe, .remove = esw_remove, diff --git a/target/linux/ramips/files/drivers/net/ethernet/ralink/esw_rt3050.h b/target/linux/ramips/files/drivers/net/ethernet/ralink/esw_rt3050.h index bbc8fbd52a40f..c9650209cfe12 100644 --- a/target/linux/ramips/files/drivers/net/ethernet/ralink/esw_rt3050.h +++ b/target/linux/ramips/files/drivers/net/ethernet/ralink/esw_rt3050.h @@ -26,4 +26,7 @@ static inline int __init mtk_switch_init(void) { return 0; } static inline void mtk_switch_exit(void) { } #endif + +int rt3050_esw_init(struct fe_priv *priv); +int rt3050_esw_has_carrier(struct fe_priv *priv); #endif diff --git a/target/linux/ramips/files/drivers/net/ethernet/ralink/soc_rt3050.c b/target/linux/ramips/files/drivers/net/ethernet/ralink/soc_rt3050.c index f98d7ec3da68f..435f007477883 100644 --- a/target/linux/ramips/files/drivers/net/ethernet/ralink/soc_rt3050.c +++ b/target/linux/ramips/files/drivers/net/ethernet/ralink/soc_rt3050.c @@ -17,6 +17,7 @@ #include #include "mtk_eth_soc.h" +#include "esw_rt3050.h" #include "mdio_rt2880.h" static const u16 rt5350_reg_table[FE_REG_COUNT] = { @@ -115,6 +116,7 @@ static void rt5350_tx_dma(struct fe_tx_dma *txd) static struct fe_soc_data rt3050_data = { .init_data = rt305x_init_data, .fwd_config = rt3050_fwd_config, + .switch_init = rt3050_esw_init, .pdma_glo_cfg = FE_PDMA_SIZE_8DWORDS, .checksum_bit = RX_DMA_L4VALID, .rx_int = FE_RX_DONE_INT, @@ -127,6 +129,7 @@ static struct fe_soc_data rt5350_data = { .reg_table = rt5350_reg_table, .set_mac = rt5350_set_mac, .fwd_config = rt5350_fwd_config, + .switch_init = rt3050_esw_init, .tx_dma = rt5350_tx_dma, .pdma_glo_cfg = FE_PDMA_SIZE_8DWORDS, .checksum_bit = RX_DMA_L4VALID,