#include <linux/regmap.h>
 #include <linux/types.h>
 
+enum {
+       BD957X_REGULATOR_CELL,
+       BD957X_WDT_CELL,
+};
+
+/*
+ * Due to the BD9576MUF nasty IRQ behaiour we don't always populate IRQs.
+ * These will be added to regulator resources only if IRQ information for the
+ * PMIC is populated in device-tree.
+ */
+static const struct resource bd9576_regulator_irqs[] = {
+       DEFINE_RES_IRQ_NAMED(BD9576_INT_THERM, "bd9576-temp"),
+       DEFINE_RES_IRQ_NAMED(BD9576_INT_OVD, "bd9576-ovd"),
+       DEFINE_RES_IRQ_NAMED(BD9576_INT_UVD, "bd9576-uvd"),
+};
+
 static struct mfd_cell bd9573_mfd_cells[] = {
-       { .name = "bd9573-regulator", },
-       { .name = "bd9576-wdt", },
+       [BD957X_REGULATOR_CELL] = { .name = "bd9573-regulator", },
+       [BD957X_WDT_CELL]       = { .name = "bd9576-wdt", },
 };
 
 static struct mfd_cell bd9576_mfd_cells[] = {
-       { .name = "bd9576-regulator", },
-       { .name = "bd9576-wdt", },
+       [BD957X_REGULATOR_CELL] = { .name = "bd9576-regulator", },
+       [BD957X_WDT_CELL]       = { .name = "bd9576-wdt", },
 };
 
 static const struct regmap_range volatile_ranges[] = {
        .cache_type = REGCACHE_RBTREE,
 };
 
+static struct regmap_irq bd9576_irqs[] = {
+       REGMAP_IRQ_REG(BD9576_INT_THERM, 0, BD957X_MASK_INT_MAIN_THERM),
+       REGMAP_IRQ_REG(BD9576_INT_OVP, 0, BD957X_MASK_INT_MAIN_OVP),
+       REGMAP_IRQ_REG(BD9576_INT_SCP, 0, BD957X_MASK_INT_MAIN_SCP),
+       REGMAP_IRQ_REG(BD9576_INT_OCP, 0, BD957X_MASK_INT_MAIN_OCP),
+       REGMAP_IRQ_REG(BD9576_INT_OVD, 0, BD957X_MASK_INT_MAIN_OVD),
+       REGMAP_IRQ_REG(BD9576_INT_UVD, 0, BD957X_MASK_INT_MAIN_UVD),
+       REGMAP_IRQ_REG(BD9576_INT_UVP, 0, BD957X_MASK_INT_MAIN_UVP),
+       REGMAP_IRQ_REG(BD9576_INT_SYS, 0, BD957X_MASK_INT_MAIN_SYS),
+};
+
+static struct regmap_irq_chip bd9576_irq_chip = {
+       .name = "bd9576_irq",
+       .irqs = &bd9576_irqs[0],
+       .num_irqs = ARRAY_SIZE(bd9576_irqs),
+       .status_base = BD957X_REG_INT_MAIN_STAT,
+       .mask_base = BD957X_REG_INT_MAIN_MASK,
+       .ack_base = BD957X_REG_INT_MAIN_STAT,
+       .init_ack_masked = true,
+       .num_regs = 1,
+       .irq_reg_stride = 1,
+};
+
 static int bd957x_i2c_probe(struct i2c_client *i2c,
                             const struct i2c_device_id *id)
 {
        struct mfd_cell *cells;
        int num_cells;
        unsigned long chip_type;
+       struct irq_domain *domain;
+       bool usable_irqs;
 
        chip_type = (unsigned long)of_device_get_match_data(&i2c->dev);
 
        case ROHM_CHIP_TYPE_BD9576:
                cells = bd9576_mfd_cells;
                num_cells = ARRAY_SIZE(bd9576_mfd_cells);
+               usable_irqs = !!i2c->irq;
                break;
        case ROHM_CHIP_TYPE_BD9573:
                cells = bd9573_mfd_cells;
                num_cells = ARRAY_SIZE(bd9573_mfd_cells);
+               /*
+                * BD9573 only supports fatal IRQs which we can not handle
+                * because SoC is going to lose the power.
+                */
+               usable_irqs = false;
                break;
        default:
                dev_err(&i2c->dev, "Unknown device type");
                return PTR_ERR(regmap);
        }
 
+       /*
+        * BD9576 behaves badly. It kepts IRQ line asserted for the whole
+        * duration of detected HW condition (like over temperature). So we
+        * don't require IRQ to be populated.
+        * If IRQ information is not given, then we mask all IRQs and do not
+        * provide IRQ resources to regulator driver - which then just omits
+        * the notifiers.
+        */
+       if (usable_irqs) {
+               struct regmap_irq_chip_data *irq_data;
+               struct mfd_cell *regulators;
+
+               regulators = &bd9576_mfd_cells[BD957X_REGULATOR_CELL];
+               regulators->resources = bd9576_regulator_irqs;
+               regulators->num_resources = ARRAY_SIZE(bd9576_regulator_irqs);
+
+               ret = devm_regmap_add_irq_chip(&i2c->dev, regmap, i2c->irq,
+                                              IRQF_ONESHOT, 0,
+                                              &bd9576_irq_chip, &irq_data);
+               if (ret) {
+                       dev_err(&i2c->dev, "Failed to add IRQ chip\n");
+                       return ret;
+               }
+               domain = regmap_irq_get_domain(irq_data);
+       } else {
+               ret = regmap_update_bits(regmap, BD957X_REG_INT_MAIN_MASK,
+                                        BD957X_MASK_INT_ALL,
+                                        BD957X_MASK_INT_ALL);
+               if (ret)
+                       return ret;
+               domain = NULL;
+       }
+
        ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_AUTO, cells,
-                                  num_cells, NULL, 0, NULL);
+                                  num_cells, NULL, 0, domain);
        if (ret)
                dev_err(&i2c->dev, "Failed to create subdevices\n");
 
 
        BD957X_VOUTS1,
 };
 
+/*
+ * The BD9576 has own IRQ 'blocks' for:
+ *  - I2C/thermal,
+ *  - Over voltage protection
+ *  - Short-circuit protection
+ *  - Over current protection
+ *  - Over voltage detection
+ *  - Under voltage detection
+ *  - Under voltage protection
+ *  - 'system interrupt'.
+ *
+ * Each of the blocks have a status register giving more accurate IRQ source
+ * information - for example which of the regulators have over-voltage.
+ *
+ * On top of this, there is "main IRQ" status register where each bit indicates
+ * which of sub-blocks have active IRQs. Fine. That would fit regmap-irq main
+ * status handling. Except that:
+ *  - Only some sub-IRQs can be masked.
+ *  - The IRQ informs us about fault-condition, not when fault state changes.
+ *    The IRQ line it is kept asserted until the detected condition is acked
+ *    AND cleared in HW. This is annoying for IRQs like the one informing high
+ *    temperature because if IRQ is not disabled it keeps the CPU in IRQ
+ *    handling loop.
+ *
+ * For now we do just use the main-IRQ register as source for our IRQ
+ * information and bind the regmap-irq to this. We leave fine-grained sub-IRQ
+ * register handling to handlers in sub-devices. The regulator driver shall
+ * read which regulators are source for problem - or if the detected error is
+ * regulator temperature error. The sub-drivers do also handle masking of "sub-
+ * IRQs" if this is supported/needed.
+ *
+ * To overcome the problem with HW keeping IRQ asserted we do call
+ * disable_irq_nosync() from sub-device handler and add a delayed work to
+ * re-enable IRQ roughly 1 second later. This should keep our CPU out of
+ * busy-loop.
+ */
+#define IRQS_SILENT_MS                 1000
+
+enum {
+       BD9576_INT_THERM,
+       BD9576_INT_OVP,
+       BD9576_INT_SCP,
+       BD9576_INT_OCP,
+       BD9576_INT_OVD,
+       BD9576_INT_UVD,
+       BD9576_INT_UVP,
+       BD9576_INT_SYS,
+};
+
 #define BD957X_REG_SMRB_ASSERT         0x15
 #define BD957X_REG_PMIC_INTERNAL_STAT  0x20
 #define BD957X_REG_INT_THERM_STAT      0x23
 #define BD957X_REG_INT_MAIN_STAT       0x30
 #define BD957X_REG_INT_MAIN_MASK       0x31
 
+#define UVD_IRQ_VALID_MASK             0x6F
+#define OVD_IRQ_VALID_MASK             0x2F
+
+#define BD957X_MASK_INT_MAIN_THERM     BIT(0)
+#define BD957X_MASK_INT_MAIN_OVP       BIT(1)
+#define BD957X_MASK_INT_MAIN_SCP       BIT(2)
+#define BD957X_MASK_INT_MAIN_OCP       BIT(3)
+#define BD957X_MASK_INT_MAIN_OVD       BIT(4)
+#define BD957X_MASK_INT_MAIN_UVD       BIT(5)
+#define BD957X_MASK_INT_MAIN_UVP       BIT(6)
+#define BD957X_MASK_INT_MAIN_SYS       BIT(7)
+#define BD957X_MASK_INT_ALL            0xff
+
 #define BD957X_REG_WDT_CONF            0x16
 
 #define BD957X_REG_POW_TRIGGER1                0x41