diff options
author | DENG Qingfang <dqfext@gmail.com> | 2022-02-10 13:59:46 +0800 |
---|---|---|
committer | Daniel Golle <daniel@makrotopia.org> | 2022-02-10 13:44:06 +0000 |
commit | f9cfe7af1f1f16082eb1fdda89f0331948517f22 (patch) | |
tree | 8877d004179e2b3b163757167c56145dfb11303c /target | |
parent | 8d8d26ba428d6f45c456b25622397e6ff75b1d47 (diff) | |
download | upstream-f9cfe7af1f1f16082eb1fdda89f0331948517f22.tar.gz upstream-f9cfe7af1f1f16082eb1fdda89f0331948517f22.tar.bz2 upstream-f9cfe7af1f1f16082eb1fdda89f0331948517f22.zip |
kernel: backport MT7530 IRQ support
Support MT7530 PHY link change interrupts, and enable for MT7621.
For external MT7530, a GPIO IRQ line is required, which is
board-specific, so it should be added to each DTS. In case the
interrupt-controller property is missing, it will fall back to
polling mode.
Signed-off-by: DENG Qingfang <dqfext@gmail.com>
Diffstat (limited to 'target')
-rw-r--r-- | target/linux/generic/backport-5.10/772-v5.14-net-dsa-mt7530-add-interrupt-support.patch | 425 | ||||
-rw-r--r-- | target/linux/ramips/dts/mt7621.dtsi | 3 |
2 files changed, 428 insertions, 0 deletions
diff --git a/target/linux/generic/backport-5.10/772-v5.14-net-dsa-mt7530-add-interrupt-support.patch b/target/linux/generic/backport-5.10/772-v5.14-net-dsa-mt7530-add-interrupt-support.patch new file mode 100644 index 0000000000..9890015301 --- /dev/null +++ b/target/linux/generic/backport-5.10/772-v5.14-net-dsa-mt7530-add-interrupt-support.patch @@ -0,0 +1,425 @@ +From ba751e28d44255744a30190faad0ca09b455c44d Mon Sep 17 00:00:00 2001 +From: DENG Qingfang <dqfext@gmail.com> +Date: Wed, 19 May 2021 11:32:00 +0800 +Subject: [PATCH] net: dsa: mt7530: add interrupt support + +Add support for MT7530 interrupt controller to handle internal PHYs. +In order to assign an IRQ number to each PHY, the registration of MDIO bus +is also done in this driver. + +Signed-off-by: DENG Qingfang <dqfext@gmail.com> +Reviewed-by: Andrew Lunn <andrew@lunn.ch> +Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Reviewed-by: Vladimir Oltean <olteanv@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/mt7530.c | 264 +++++++++++++++++++++++++++++++++++---- + drivers/net/dsa/mt7530.h | 20 ++- + 2 files changed, 256 insertions(+), 28 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -10,6 +10,7 @@ + #include <linux/mfd/syscon.h> + #include <linux/module.h> + #include <linux/netdevice.h> ++#include <linux/of_irq.h> + #include <linux/of_mdio.h> + #include <linux/of_net.h> + #include <linux/of_platform.h> +@@ -602,18 +603,14 @@ mt7530_mib_reset(struct dsa_switch *ds) + mt7530_write(priv, MT7530_MIB_CCR, CCR_MIB_ACTIVATE); + } + +-static int mt7530_phy_read(struct dsa_switch *ds, int port, int regnum) ++static int mt7530_phy_read(struct mt7530_priv *priv, int port, int regnum) + { +- struct mt7530_priv *priv = ds->priv; +- + return mdiobus_read_nested(priv->bus, port, regnum); + } + +-static int mt7530_phy_write(struct dsa_switch *ds, int port, int regnum, ++static int mt7530_phy_write(struct mt7530_priv *priv, int port, int regnum, + u16 val) + { +- struct mt7530_priv *priv = ds->priv; +- + return mdiobus_write_nested(priv->bus, port, regnum, val); + } + +@@ -791,9 +788,8 @@ out: + } + + static int +-mt7531_ind_phy_read(struct dsa_switch *ds, int port, int regnum) ++mt7531_ind_phy_read(struct mt7530_priv *priv, int port, int regnum) + { +- struct mt7530_priv *priv = ds->priv; + int devad; + int ret; + +@@ -809,10 +805,9 @@ mt7531_ind_phy_read(struct dsa_switch *d + } + + static int +-mt7531_ind_phy_write(struct dsa_switch *ds, int port, int regnum, ++mt7531_ind_phy_write(struct mt7530_priv *priv, int port, int regnum, + u16 data) + { +- struct mt7530_priv *priv = ds->priv; + int devad; + int ret; + +@@ -828,6 +823,22 @@ mt7531_ind_phy_write(struct dsa_switch * + return ret; + } + ++static int ++mt753x_phy_read(struct mii_bus *bus, int port, int regnum) ++{ ++ struct mt7530_priv *priv = bus->priv; ++ ++ return priv->info->phy_read(priv, port, regnum); ++} ++ ++static int ++mt753x_phy_write(struct mii_bus *bus, int port, int regnum, u16 val) ++{ ++ struct mt7530_priv *priv = bus->priv; ++ ++ return priv->info->phy_write(priv, port, regnum, val); ++} ++ + static void + mt7530_get_strings(struct dsa_switch *ds, int port, u32 stringset, + uint8_t *data) +@@ -1824,6 +1835,210 @@ mt7530_setup_gpio(struct mt7530_priv *pr + return devm_gpiochip_add_data(dev, gc, priv); + } + ++static irqreturn_t ++mt7530_irq_thread_fn(int irq, void *dev_id) ++{ ++ struct mt7530_priv *priv = dev_id; ++ bool handled = false; ++ u32 val; ++ int p; ++ ++ mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED); ++ val = mt7530_mii_read(priv, MT7530_SYS_INT_STS); ++ mt7530_mii_write(priv, MT7530_SYS_INT_STS, val); ++ mutex_unlock(&priv->bus->mdio_lock); ++ ++ for (p = 0; p < MT7530_NUM_PHYS; p++) { ++ if (BIT(p) & val) { ++ unsigned int irq; ++ ++ irq = irq_find_mapping(priv->irq_domain, p); ++ handle_nested_irq(irq); ++ handled = true; ++ } ++ } ++ ++ return IRQ_RETVAL(handled); ++} ++ ++static void ++mt7530_irq_mask(struct irq_data *d) ++{ ++ struct mt7530_priv *priv = irq_data_get_irq_chip_data(d); ++ ++ priv->irq_enable &= ~BIT(d->hwirq); ++} ++ ++static void ++mt7530_irq_unmask(struct irq_data *d) ++{ ++ struct mt7530_priv *priv = irq_data_get_irq_chip_data(d); ++ ++ priv->irq_enable |= BIT(d->hwirq); ++} ++ ++static void ++mt7530_irq_bus_lock(struct irq_data *d) ++{ ++ struct mt7530_priv *priv = irq_data_get_irq_chip_data(d); ++ ++ mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED); ++} ++ ++static void ++mt7530_irq_bus_sync_unlock(struct irq_data *d) ++{ ++ struct mt7530_priv *priv = irq_data_get_irq_chip_data(d); ++ ++ mt7530_mii_write(priv, MT7530_SYS_INT_EN, priv->irq_enable); ++ mutex_unlock(&priv->bus->mdio_lock); ++} ++ ++static struct irq_chip mt7530_irq_chip = { ++ .name = KBUILD_MODNAME, ++ .irq_mask = mt7530_irq_mask, ++ .irq_unmask = mt7530_irq_unmask, ++ .irq_bus_lock = mt7530_irq_bus_lock, ++ .irq_bus_sync_unlock = mt7530_irq_bus_sync_unlock, ++}; ++ ++static int ++mt7530_irq_map(struct irq_domain *domain, unsigned int irq, ++ irq_hw_number_t hwirq) ++{ ++ irq_set_chip_data(irq, domain->host_data); ++ irq_set_chip_and_handler(irq, &mt7530_irq_chip, handle_simple_irq); ++ irq_set_nested_thread(irq, true); ++ irq_set_noprobe(irq); ++ ++ return 0; ++} ++ ++static const struct irq_domain_ops mt7530_irq_domain_ops = { ++ .map = mt7530_irq_map, ++ .xlate = irq_domain_xlate_onecell, ++}; ++ ++static void ++mt7530_setup_mdio_irq(struct mt7530_priv *priv) ++{ ++ struct dsa_switch *ds = priv->ds; ++ int p; ++ ++ for (p = 0; p < MT7530_NUM_PHYS; p++) { ++ if (BIT(p) & ds->phys_mii_mask) { ++ unsigned int irq; ++ ++ irq = irq_create_mapping(priv->irq_domain, p); ++ ds->slave_mii_bus->irq[p] = irq; ++ } ++ } ++} ++ ++static int ++mt7530_setup_irq(struct mt7530_priv *priv) ++{ ++ struct device *dev = priv->dev; ++ struct device_node *np = dev->of_node; ++ int ret; ++ ++ if (!of_property_read_bool(np, "interrupt-controller")) { ++ dev_info(dev, "no interrupt support\n"); ++ return 0; ++ } ++ ++ priv->irq = of_irq_get(np, 0); ++ if (priv->irq <= 0) { ++ dev_err(dev, "failed to get parent IRQ: %d\n", priv->irq); ++ return priv->irq ? : -EINVAL; ++ } ++ ++ priv->irq_domain = irq_domain_add_linear(np, MT7530_NUM_PHYS, ++ &mt7530_irq_domain_ops, priv); ++ if (!priv->irq_domain) { ++ dev_err(dev, "failed to create IRQ domain\n"); ++ return -ENOMEM; ++ } ++ ++ /* This register must be set for MT7530 to properly fire interrupts */ ++ if (priv->id != ID_MT7531) ++ mt7530_set(priv, MT7530_TOP_SIG_CTRL, TOP_SIG_CTRL_NORMAL); ++ ++ ret = request_threaded_irq(priv->irq, NULL, mt7530_irq_thread_fn, ++ IRQF_ONESHOT, KBUILD_MODNAME, priv); ++ if (ret) { ++ irq_domain_remove(priv->irq_domain); ++ dev_err(dev, "failed to request IRQ: %d\n", ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static void ++mt7530_free_mdio_irq(struct mt7530_priv *priv) ++{ ++ int p; ++ ++ for (p = 0; p < MT7530_NUM_PHYS; p++) { ++ if (BIT(p) & priv->ds->phys_mii_mask) { ++ unsigned int irq; ++ ++ irq = irq_find_mapping(priv->irq_domain, p); ++ irq_dispose_mapping(irq); ++ } ++ } ++} ++ ++static void ++mt7530_free_irq_common(struct mt7530_priv *priv) ++{ ++ free_irq(priv->irq, priv); ++ irq_domain_remove(priv->irq_domain); ++} ++ ++static void ++mt7530_free_irq(struct mt7530_priv *priv) ++{ ++ mt7530_free_mdio_irq(priv); ++ mt7530_free_irq_common(priv); ++} ++ ++static int ++mt7530_setup_mdio(struct mt7530_priv *priv) ++{ ++ struct dsa_switch *ds = priv->ds; ++ struct device *dev = priv->dev; ++ struct mii_bus *bus; ++ static int idx; ++ int ret; ++ ++ bus = devm_mdiobus_alloc(dev); ++ if (!bus) ++ return -ENOMEM; ++ ++ ds->slave_mii_bus = bus; ++ bus->priv = priv; ++ bus->name = KBUILD_MODNAME "-mii"; ++ snprintf(bus->id, MII_BUS_ID_SIZE, KBUILD_MODNAME "-%d", idx++); ++ bus->read = mt753x_phy_read; ++ bus->write = mt753x_phy_write; ++ bus->parent = dev; ++ bus->phy_mask = ~ds->phys_mii_mask; ++ ++ if (priv->irq) ++ mt7530_setup_mdio_irq(priv); ++ ++ ret = devm_mdiobus_register(dev, bus); ++ if (ret) { ++ dev_err(dev, "failed to register MDIO bus: %d\n", ret); ++ if (priv->irq) ++ mt7530_free_mdio_irq(priv); ++ } ++ ++ return ret; ++} ++ + static int + mt7530_setup(struct dsa_switch *ds) + { +@@ -2798,24 +3013,20 @@ static int + mt753x_setup(struct dsa_switch *ds) + { + struct mt7530_priv *priv = ds->priv; ++ int ret = priv->info->sw_setup(ds); + +- return priv->info->sw_setup(ds); +-} +- +-static int +-mt753x_phy_read(struct dsa_switch *ds, int port, int regnum) +-{ +- struct mt7530_priv *priv = ds->priv; ++ if (ret) ++ return ret; + +- return priv->info->phy_read(ds, port, regnum); +-} ++ ret = mt7530_setup_irq(priv); ++ if (ret) ++ return ret; + +-static int +-mt753x_phy_write(struct dsa_switch *ds, int port, int regnum, u16 val) +-{ +- struct mt7530_priv *priv = ds->priv; ++ ret = mt7530_setup_mdio(priv); ++ if (ret && priv->irq) ++ mt7530_free_irq_common(priv); + +- return priv->info->phy_write(ds, port, regnum, val); ++ return ret; + } + + static int mt753x_get_mac_eee(struct dsa_switch *ds, int port, +@@ -2852,8 +3063,6 @@ static const struct dsa_switch_ops mt753 + .get_tag_protocol = mtk_get_tag_protocol, + .setup = mt753x_setup, + .get_strings = mt7530_get_strings, +- .phy_read = mt753x_phy_read, +- .phy_write = mt753x_phy_write, + .get_ethtool_stats = mt7530_get_ethtool_stats, + .get_sset_count = mt7530_get_sset_count, + .set_ageing_time = mt7530_set_ageing_time, +@@ -3036,6 +3245,9 @@ mt7530_remove(struct mdio_device *mdiode + dev_err(priv->dev, "Failed to disable io pwr: %d\n", + ret); + ++ if (priv->irq) ++ mt7530_free_irq(priv); ++ + dsa_unregister_switch(priv->ds); + mutex_destroy(&priv->reg_mutex); + } +--- a/drivers/net/dsa/mt7530.h ++++ b/drivers/net/dsa/mt7530.h +@@ -7,6 +7,7 @@ + #define __MT7530_H + + #define MT7530_NUM_PORTS 7 ++#define MT7530_NUM_PHYS 5 + #define MT7530_CPU_PORT 6 + #define MT7530_NUM_FDB_RECORDS 2048 + #define MT7530_ALL_MEMBERS 0xff +@@ -401,6 +402,12 @@ enum mt7531_sgmii_force_duplex { + #define SYS_CTRL_SW_RST BIT(1) + #define SYS_CTRL_REG_RST BIT(0) + ++/* Register for system interrupt */ ++#define MT7530_SYS_INT_EN 0x7008 ++ ++/* Register for system interrupt status */ ++#define MT7530_SYS_INT_STS 0x700c ++ + /* Register for PHY Indirect Access Control */ + #define MT7531_PHY_IAC 0x701C + #define MT7531_PHY_ACS_ST BIT(31) +@@ -722,6 +729,8 @@ static const char *p5_intf_modes(unsigne + } + } + ++struct mt7530_priv; ++ + /* struct mt753x_info - This is the main data structure for holding the specific + * part for each supported device + * @sw_setup: Holding the handler to a device initialization +@@ -746,8 +755,8 @@ struct mt753x_info { + enum mt753x_id id; + + int (*sw_setup)(struct dsa_switch *ds); +- int (*phy_read)(struct dsa_switch *ds, int port, int regnum); +- int (*phy_write)(struct dsa_switch *ds, int port, int regnum, u16 val); ++ int (*phy_read)(struct mt7530_priv *priv, int port, int regnum); ++ int (*phy_write)(struct mt7530_priv *priv, int port, int regnum, u16 val); + int (*pad_setup)(struct dsa_switch *ds, phy_interface_t interface); + int (*cpu_port_config)(struct dsa_switch *ds, int port); + bool (*phy_mode_supported)(struct dsa_switch *ds, int port, +@@ -781,6 +790,10 @@ struct mt753x_info { + * registers + * @p6_interface Holding the current port 6 interface + * @p5_intf_sel: Holding the current port 5 interface select ++ * ++ * @irq: IRQ number of the switch ++ * @irq_domain: IRQ domain of the switch irq_chip ++ * @irq_enable: IRQ enable bits, synced to SYS_INT_EN + */ + struct mt7530_priv { + struct device *dev; +@@ -802,6 +815,9 @@ struct mt7530_priv { + struct mt7530_port ports[MT7530_NUM_PORTS]; + /* protect among processes for registers access*/ + struct mutex reg_mutex; ++ int irq; ++ struct irq_domain *irq_domain; ++ u32 irq_enable; + }; + + struct mt7530_hw_vlan_entry { diff --git a/target/linux/ramips/dts/mt7621.dtsi b/target/linux/ramips/dts/mt7621.dtsi index 4a3327a364..b784a9d60c 100644 --- a/target/linux/ramips/dts/mt7621.dtsi +++ b/target/linux/ramips/dts/mt7621.dtsi @@ -487,6 +487,9 @@ mediatek,mcm; resets = <&rstctrl 2>; reset-names = "mcm"; + interrupt-controller; + #interrupt-cells = <1>; + interrupts = <GIC_SHARED 23 IRQ_TYPE_LEVEL_HIGH>; ports { #address-cells = <1>; |