#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>
        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);
 }
 
 }
 
 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;
 
 }
 
 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;
 
        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)
 }
 #endif /* CONFIG_GPIOLIB */
 
+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 = 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
 mt7530_setup(struct dsa_switch *ds)
 {
 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,
        .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,
                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);
 }
 
 #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
 #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)
        }
 }
 
+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
        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,
  *                     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;
        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 {