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>
This commit is contained in:
		 DENG Qingfang
					DENG Qingfang
				
			
				
					committed by
					
						 Daniel Golle
						Daniel Golle
					
				
			
			
				
	
			
			
			 Daniel Golle
						Daniel Golle
					
				
			
						parent
						
							8d8d26ba42
						
					
				
				
					commit
					f9cfe7af1f
				
			| @@ -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 { | ||||
| @@ -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>; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user