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 <lynxis@fe80.eu>
This commit is contained in:
parent
74c58c9d58
commit
8569bc5e0d
|
@ -24,6 +24,7 @@
|
||||||
#include <linux/reset.h>
|
#include <linux/reset.h>
|
||||||
|
|
||||||
#include "mtk_eth_soc.h"
|
#include "mtk_eth_soc.h"
|
||||||
|
#include "esw_rt3050.h"
|
||||||
|
|
||||||
/* HW limitations for this switch:
|
/* HW limitations for this switch:
|
||||||
* - No large frame support (PKT_MAX_LEN at most 1536)
|
* - No large frame support (PKT_MAX_LEN at most 1536)
|
||||||
|
@ -216,6 +217,7 @@ struct rt305x_esw {
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
int irq;
|
int irq;
|
||||||
|
struct fe_priv *priv;
|
||||||
|
|
||||||
/* Protects against concurrent register r/w operations. */
|
/* Protects against concurrent register r/w operations. */
|
||||||
spinlock_t reg_rw_lock;
|
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);
|
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)
|
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;
|
u32 status;
|
||||||
|
int i;
|
||||||
|
|
||||||
status = esw_r32(esw, RT305X_ESW_REG_ISR);
|
status = esw_r32(esw, RT305X_ESW_REG_ISR);
|
||||||
if (status & RT305X_ESW_PORT_ST_CHG) {
|
if (status & RT305X_ESW_PORT_ST_CHG) {
|
||||||
u32 link = esw_r32(esw, RT305X_ESW_REG_POA);
|
if (!esw->priv)
|
||||||
|
goto out;
|
||||||
link >>= RT305X_ESW_POA_LINK_SHIFT;
|
if (rt3050_esw_has_carrier(esw->priv))
|
||||||
link &= RT305X_ESW_POA_LINK_MASK;
|
netif_carrier_on(esw->priv->netdev);
|
||||||
dev_info(esw->dev, "link changed 0x%02X\n", link);
|
else
|
||||||
|
netif_carrier_off(esw->priv->netdev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
esw_w32(esw, status, RT305X_ESW_REG_ISR);
|
esw_w32(esw, status, RT305X_ESW_REG_ISR);
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
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 resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
struct device_node *np = pdev->dev.of_node;
|
struct device_node *np = pdev->dev.of_node;
|
||||||
const __be32 *port_map, *port_disable, *reg_init;
|
const __be32 *port_map, *port_disable, *reg_init;
|
||||||
struct switch_dev *swdev;
|
|
||||||
struct rt305x_esw *esw;
|
struct rt305x_esw *esw;
|
||||||
int ret;
|
|
||||||
|
|
||||||
esw = devm_kzalloc(&pdev->dev, sizeof(*esw), GFP_KERNEL);
|
esw = devm_kzalloc(&pdev->dev, sizeof(*esw), GFP_KERNEL);
|
||||||
if (!esw)
|
if (!esw)
|
||||||
|
@ -1417,48 +1442,10 @@ static int esw_probe(struct platform_device *pdev)
|
||||||
if (IS_ERR(esw->rst_ephy))
|
if (IS_ERR(esw->rst_ephy))
|
||||||
esw->rst_ephy = NULL;
|
esw->rst_ephy = NULL;
|
||||||
|
|
||||||
swdev = &esw->swdev;
|
spin_lock_init(&esw->reg_rw_lock);
|
||||||
swdev->of_node = pdev->dev.of_node;
|
|
||||||
swdev->name = "rt305x-esw";
|
|
||||||
swdev->alias = "rt305x";
|
|
||||||
swdev->cpu_port = RT305X_ESW_PORT6;
|
|
||||||
swdev->ports = RT305X_ESW_NUM_PORTS;
|
|
||||||
swdev->vlans = RT305X_ESW_NUM_VIDS;
|
|
||||||
swdev->ops = &esw_ops;
|
|
||||||
|
|
||||||
ret = register_switch(swdev, NULL);
|
|
||||||
if (ret < 0) {
|
|
||||||
dev_err(&pdev->dev, "register_switch failed\n");
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
platform_set_drvdata(pdev, esw);
|
platform_set_drvdata(pdev, esw);
|
||||||
|
|
||||||
spin_lock_init(&esw->reg_rw_lock);
|
return 0;
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
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)
|
static int esw_remove(struct platform_device *pdev)
|
||||||
|
@ -1479,6 +1466,71 @@ static const struct of_device_id ralink_esw_match[] = {
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, ralink_esw_match);
|
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";
|
||||||
|
swdev->alias = "rt305x";
|
||||||
|
swdev->cpu_port = RT305X_ESW_PORT6;
|
||||||
|
swdev->ports = RT305X_ESW_NUM_PORTS;
|
||||||
|
swdev->vlans = RT305X_ESW_NUM_VIDS;
|
||||||
|
swdev->ops = &esw_ops;
|
||||||
|
|
||||||
|
ret = register_switch(swdev, NULL);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(&pdev->dev, "register_switch failed\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = devm_request_irq(&pdev->dev, esw->irq, esw_interrupt, 0, "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);
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_info(&pdev->dev, "mediatek esw at 0x%08lx, irq %d initialized\n",
|
||||||
|
esw->base, esw->irq);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static struct platform_driver esw_driver = {
|
static struct platform_driver esw_driver = {
|
||||||
.probe = esw_probe,
|
.probe = esw_probe,
|
||||||
.remove = esw_remove,
|
.remove = esw_remove,
|
||||||
|
|
|
@ -26,4 +26,7 @@ static inline int __init mtk_switch_init(void) { return 0; }
|
||||||
static inline void mtk_switch_exit(void) { }
|
static inline void mtk_switch_exit(void) { }
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
int rt3050_esw_init(struct fe_priv *priv);
|
||||||
|
int rt3050_esw_has_carrier(struct fe_priv *priv);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include <asm/mach-ralink/ralink_regs.h>
|
#include <asm/mach-ralink/ralink_regs.h>
|
||||||
|
|
||||||
#include "mtk_eth_soc.h"
|
#include "mtk_eth_soc.h"
|
||||||
|
#include "esw_rt3050.h"
|
||||||
#include "mdio_rt2880.h"
|
#include "mdio_rt2880.h"
|
||||||
|
|
||||||
static const u16 rt5350_reg_table[FE_REG_COUNT] = {
|
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 = {
|
static struct fe_soc_data rt3050_data = {
|
||||||
.init_data = rt305x_init_data,
|
.init_data = rt305x_init_data,
|
||||||
.fwd_config = rt3050_fwd_config,
|
.fwd_config = rt3050_fwd_config,
|
||||||
|
.switch_init = rt3050_esw_init,
|
||||||
.pdma_glo_cfg = FE_PDMA_SIZE_8DWORDS,
|
.pdma_glo_cfg = FE_PDMA_SIZE_8DWORDS,
|
||||||
.checksum_bit = RX_DMA_L4VALID,
|
.checksum_bit = RX_DMA_L4VALID,
|
||||||
.rx_int = FE_RX_DONE_INT,
|
.rx_int = FE_RX_DONE_INT,
|
||||||
|
@ -127,6 +129,7 @@ static struct fe_soc_data rt5350_data = {
|
||||||
.reg_table = rt5350_reg_table,
|
.reg_table = rt5350_reg_table,
|
||||||
.set_mac = rt5350_set_mac,
|
.set_mac = rt5350_set_mac,
|
||||||
.fwd_config = rt5350_fwd_config,
|
.fwd_config = rt5350_fwd_config,
|
||||||
|
.switch_init = rt3050_esw_init,
|
||||||
.tx_dma = rt5350_tx_dma,
|
.tx_dma = rt5350_tx_dma,
|
||||||
.pdma_glo_cfg = FE_PDMA_SIZE_8DWORDS,
|
.pdma_glo_cfg = FE_PDMA_SIZE_8DWORDS,
|
||||||
.checksum_bit = RX_DMA_L4VALID,
|
.checksum_bit = RX_DMA_L4VALID,
|
||||||
|
|
Loading…
Reference in New Issue