Message ID | 20220210040025.487863-1-dqfext@gmail.com |
---|---|
State | Superseded |
Headers | show |
Series | [v3] kernel: backport MT7530 IRQ support | expand |
On Thu, Feb 10, 2022 at 12:00:25PM +0800, DENG Qingfang wrote: > 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. I've tried building this and it breaks. See comments inline: > > Signed-off-by: DENG Qingfang <dqfext@gmail.com> > --- > ...net-dsa-mt7530-add-interrupt-support.patch | 417 ++++++++++++++++++ > target/linux/ramips/dts/mt7621.dtsi | 3 + > 2 files changed, 420 insertions(+) > create mode 100644 target/linux/generic/backport-5.10/772-v5.14-net-dsa-mt7530-add-interrupt-support.patch > > 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..47ac779efa > --- /dev/null > +++ b/target/linux/generic/backport-5.10/772-v5.14-net-dsa-mt7530-add-interrupt-support.patch > @@ -0,0 +1,417 @@ > +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 | 263 +++++++++++++++++++++++++++++++++++---- > + drivers/net/dsa/mt7530.h | 21 +++- > + 2 files changed, 255 insertions(+), 29 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) > +@@ -1991,6 +2002,211 @@ mt7530_setup(struct dsa_switch *ds) > + return 0; > + } > + > ++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) This function appears to be unused. At top level: drivers/net/dsa/mt7530.c:2169:1: warning: 'mt7530_free_irq' defined but not used [-Wunused-function] 2169 | 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(bus); Did you mean mdiobus_register(bus); ? Or devm_mdiobus_register(dev, bus); ? drivers/net/dsa/mt7530.c: In function 'mt7530_setup_mdio': drivers/net/dsa/mt7530.c:2200:40: error: macro "devm_mdiobus_register" requires 2 arguments, but only 1 given 2200 | ret = devm_mdiobus_register(bus); | ^ In file included from ./include/linux/of_mdio.h:12, from drivers/net/dsa/mt7530.c:14: ./include/linux/phy.h:394: note: macro "devm_mdiobus_register" defined here 394 | #define devm_mdiobus_register(dev, bus) \ | drivers/net/dsa/mt7530.c:2200:15: error: 'devm_mdiobus_register' undeclared (first use in this function); did you mean 'devm_of_mdiobus_register'? 2200 | ret = devm_mdiobus_register(bus); | ^~~~~~~~~~~~~~~~~~~~~ | devm_of_mdiobus_register drivers/net/dsa/mt7530.c:2200:15: note: each undeclared identifier is reported only once for each function it appears in At top level: > ++ 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 > + mt7531_setup(struct dsa_switch *ds) > + { > +@@ -2798,24 +3014,19 @@ static int > + mt753x_setup(struct dsa_switch *ds) > + { > + struct mt7530_priv *priv = ds->priv; > ++ int ret = priv->info->sw_setup(ds); > ++ if (ret) > ++ return ret; > + > +- 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; > +- > +- 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, > +--- 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,9 @@ static const char *p5_intf_modes(unsigne > + } > + } > + > ++/* Forward declaration */ > ++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 +756,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 +791,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 +816,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 7992f47993..7bd3b0f871 100644 > --- a/target/linux/ramips/dts/mt7621.dtsi > +++ b/target/linux/ramips/dts/mt7621.dtsi > @@ -514,6 +514,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>; > -- > 2.25.1 >
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..47ac779efa --- /dev/null +++ b/target/linux/generic/backport-5.10/772-v5.14-net-dsa-mt7530-add-interrupt-support.patch @@ -0,0 +1,417 @@ +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 | 263 +++++++++++++++++++++++++++++++++++---- + drivers/net/dsa/mt7530.h | 21 +++- + 2 files changed, 255 insertions(+), 29 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) +@@ -1991,6 +2002,211 @@ mt7530_setup(struct dsa_switch *ds) + return 0; + } + ++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(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 + mt7531_setup(struct dsa_switch *ds) + { +@@ -2798,24 +3014,19 @@ static int + mt753x_setup(struct dsa_switch *ds) + { + struct mt7530_priv *priv = ds->priv; ++ int ret = priv->info->sw_setup(ds); ++ if (ret) ++ return ret; + +- 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; +- +- 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, +--- 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,9 @@ static const char *p5_intf_modes(unsigne + } + } + ++/* Forward declaration */ ++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 +756,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 +791,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 +816,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 7992f47993..7bd3b0f871 100644 --- a/target/linux/ramips/dts/mt7621.dtsi +++ b/target/linux/ramips/dts/mt7621.dtsi @@ -514,6 +514,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>;
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> --- ...net-dsa-mt7530-add-interrupt-support.patch | 417 ++++++++++++++++++ target/linux/ramips/dts/mt7621.dtsi | 3 + 2 files changed, 420 insertions(+) create mode 100644 target/linux/generic/backport-5.10/772-v5.14-net-dsa-mt7530-add-interrupt-support.patch