[S] [HostAddr] [Wr] A [DevAddr] A [DataLow] A [DataHigh] A [P]
 
 This is implemented in the following way in the Linux kernel:
-* I2C bus drivers which support SMBus Host Notify should call
-  i2c_setup_smbus_host_notify() to setup SMBus Host Notify support.
-* I2C drivers for devices which can trigger SMBus Host Notify should implement
-  the optional alert() callback.
+* I2C bus drivers which support SMBus Host Notify should report
+  I2C_FUNC_SMBUS_HOST_NOTIFY.
+* I2C bus drivers trigger SMBus Host Notify by a call to
+  i2c_handle_smbus_host_notify().
+* I2C drivers for devices which can trigger SMBus Host Notify will have
+  client->irq assigned to a Host Notify IRQ if noone else specified an other.
+
+There is currently no way to retrieve the data parameter from the client.
 
 
 Packet Error Checking (PEC)
 
 config I2C
        tristate "I2C support"
        select RT_MUTEXES
+       select IRQ_DOMAIN
        ---help---
          I2C (pronounce: I-squared-C) is a slow serial bus protocol used in
          many micro controller applications and developed by Philips.  SMBus,
 
         */
        bool acpi_reserved;
        struct mutex acpi_lock;
-       struct smbus_host_notify *host_notify;
 };
 
 #define FEATURE_SMBUS_PEC      BIT(0)
 
        /*
         * With the tested platforms, reading SMBNTFDDAT (22 + (p)->smba)
-        * always returns 0 and is safe to read.
-        * We just use 0 given we have no use of the data right now.
+        * always returns 0. Our current implementation doesn't provide
+        * data, so we just ignore it.
         */
-       i2c_handle_smbus_host_notify(priv->host_notify, addr, 0);
+       i2c_handle_smbus_host_notify(&priv->adapter, addr);
 
        /* clear Host Notify bit and return */
        outb_p(SMBSLVSTS_HST_NTFY_STS, SMBSLVSTS(priv));
                I2C_FUNC_SMBUS_HOST_NOTIFY : 0);
 }
 
-static int i801_enable_host_notify(struct i2c_adapter *adapter)
+static void i801_enable_host_notify(struct i2c_adapter *adapter)
 {
        struct i801_priv *priv = i2c_get_adapdata(adapter);
 
        if (!(priv->features & FEATURE_HOST_NOTIFY))
-               return -ENOTSUPP;
-
-       if (!priv->host_notify)
-               priv->host_notify = i2c_setup_smbus_host_notify(adapter);
-       if (!priv->host_notify)
-               return -ENOMEM;
+               return;
 
        priv->original_slvcmd = inb_p(SMBSLVCMD(priv));
 
 
        /* clear Host Notify bit to allow a new notification */
        outb_p(SMBSLVSTS_HST_NTFY_STS, SMBSLVSTS(priv));
-
-       return 0;
 }
 
 static void i801_disable_host_notify(struct i801_priv *priv)
                return err;
        }
 
-       /*
-        * Enable Host Notify for chips that supports it.
-        * It is done after i2c_add_adapter() so that we are sure the work queue
-        * is not used if i2c_add_adapter() fails.
-        */
-       err = i801_enable_host_notify(&priv->adapter);
-       if (err && err != -ENOTSUPP)
-               dev_warn(&dev->dev, "Unable to enable SMBus Host Notify\n");
+       i801_enable_host_notify(&priv->adapter);
 
        i801_probe_optional_slaves(priv);
        /* We ignore errors - multiplexing is optional */
 {
        struct pci_dev *pci_dev = to_pci_dev(dev);
        struct i801_priv *priv = pci_get_drvdata(pci_dev);
-       int err;
 
-       err = i801_enable_host_notify(&priv->adapter);
-       if (err && err != -ENOTSUPP)
-               dev_warn(dev, "Unable to enable SMBus Host Notify\n");
+       i801_enable_host_notify(&priv->adapter);
 
        return 0;
 }
 
 #define I2C_ADDR_OFFSET_TEN_BIT        0xa000
 #define I2C_ADDR_OFFSET_SLAVE  0x1000
 
+#define I2C_ADDR_7BITS_MAX     0x77
+#define I2C_ADDR_7BITS_COUNT   (I2C_ADDR_7BITS_MAX + 1)
+
 /* core_lock protects i2c_adapter_idr, and guarantees
    that device detection, deletion of detected devices, and attach_adapter
    calls are serialized */
        adap->bus_recovery_info = NULL;
 }
 
+static int i2c_smbus_host_notify_to_irq(const struct i2c_client *client)
+{
+       struct i2c_adapter *adap = client->adapter;
+       unsigned int irq;
+
+       if (!adap->host_notify_domain)
+               return -ENXIO;
+
+       if (client->flags & I2C_CLIENT_TEN)
+               return -EINVAL;
+
+       irq = irq_find_mapping(adap->host_notify_domain, client->addr);
+       if (!irq)
+               irq = irq_create_mapping(adap->host_notify_domain,
+                                        client->addr);
+
+       return irq > 0 ? irq : -ENXIO;
+}
+
 static int i2c_device_probe(struct device *dev)
 {
        struct i2c_client       *client = i2c_verify_client(dev);
                }
                if (irq == -EPROBE_DEFER)
                        return irq;
+               /*
+                * ACPI and OF did not find any useful IRQ, try to see
+                * if Host Notify can be used.
+                */
+               if (irq < 0) {
+                       dev_dbg(dev, "Using Host Notify IRQ\n");
+                       irq = i2c_smbus_host_notify_to_irq(client);
+               }
                if (irq < 0)
                        irq = 0;
 
        .unlock_bus =  i2c_adapter_unlock_bus,
 };
 
+static void i2c_host_notify_irq_teardown(struct i2c_adapter *adap)
+{
+       struct irq_domain *domain = adap->host_notify_domain;
+       irq_hw_number_t hwirq;
+
+       if (!domain)
+               return;
+
+       for (hwirq = 0 ; hwirq < I2C_ADDR_7BITS_COUNT ; hwirq++)
+               irq_dispose_mapping(irq_find_mapping(domain, hwirq));
+
+       irq_domain_remove(domain);
+       adap->host_notify_domain = NULL;
+}
+
+static int i2c_host_notify_irq_map(struct irq_domain *h,
+                                         unsigned int virq,
+                                         irq_hw_number_t hw_irq_num)
+{
+       irq_set_chip_and_handler(virq, &dummy_irq_chip, handle_simple_irq);
+
+       return 0;
+}
+
+static const struct irq_domain_ops i2c_host_notify_irq_ops = {
+       .map = i2c_host_notify_irq_map,
+};
+
+static int i2c_setup_host_notify_irq_domain(struct i2c_adapter *adap)
+{
+       struct irq_domain *domain;
+
+       if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_HOST_NOTIFY))
+               return 0;
+
+       domain = irq_domain_create_linear(adap->dev.fwnode,
+                                         I2C_ADDR_7BITS_COUNT,
+                                         &i2c_host_notify_irq_ops, adap);
+       if (!domain)
+               return -ENOMEM;
+
+       adap->host_notify_domain = domain;
+
+       return 0;
+}
+
+/**
+ * i2c_handle_smbus_host_notify - Forward a Host Notify event to the correct
+ * I2C client.
+ * @adap: the adapter
+ * @addr: the I2C address of the notifying device
+ * Context: can't sleep
+ *
+ * Helper function to be called from an I2C bus driver's interrupt
+ * handler. It will schedule the Host Notify IRQ.
+ */
+int i2c_handle_smbus_host_notify(struct i2c_adapter *adap, unsigned short addr)
+{
+       int irq;
+
+       if (!adap)
+               return -EINVAL;
+
+       irq = irq_find_mapping(adap->host_notify_domain, addr);
+       if (irq <= 0)
+               return -ENXIO;
+
+       generic_handle_irq(irq);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(i2c_handle_smbus_host_notify);
+
 static int i2c_register_adapter(struct i2c_adapter *adap)
 {
        int res = -EINVAL;
        if (adap->timeout == 0)
                adap->timeout = HZ;
 
+       /* register soft irqs for Host Notify */
+       res = i2c_setup_host_notify_irq_domain(adap);
+       if (res) {
+               pr_err("adapter '%s': can't create Host Notify IRQs (%d)\n",
+                      adap->name, res);
+               goto out_list;
+       }
+
        dev_set_name(&adap->dev, "i2c-%d", adap->nr);
        adap->dev.bus = &i2c_bus_type;
        adap->dev.type = &i2c_adapter_type;
 
        pm_runtime_disable(&adap->dev);
 
+       i2c_host_notify_irq_teardown(adap);
+
        /* wait until all references to the device are gone
         *
         * FIXME: This is old code and should ideally be replaced by an
 
 }
 EXPORT_SYMBOL_GPL(i2c_handle_smbus_alert);
 
-static void smbus_host_notify_work(struct work_struct *work)
-{
-       struct alert_data alert;
-       struct i2c_adapter *adapter;
-       unsigned long flags;
-       u16 payload;
-       u8 addr;
-       struct smbus_host_notify *data;
-
-       data = container_of(work, struct smbus_host_notify, work);
-
-       spin_lock_irqsave(&data->lock, flags);
-       payload = data->payload;
-       addr = data->addr;
-       adapter = data->adapter;
-
-       /* clear the pending bit and release the spinlock */
-       data->pending = false;
-       spin_unlock_irqrestore(&data->lock, flags);
-
-       if (!adapter || !addr)
-               return;
-
-       alert.type = I2C_PROTOCOL_SMBUS_HOST_NOTIFY;
-       alert.addr = addr;
-       alert.data = payload;
-
-       device_for_each_child(&adapter->dev, &alert, smbus_do_alert);
-}
-
-/**
- * i2c_setup_smbus_host_notify - Allocate a new smbus_host_notify for the given
- * I2C adapter.
- * @adapter: the adapter we want to associate a Host Notify function
- *
- * Returns a struct smbus_host_notify pointer on success, and NULL on failure.
- * The resulting smbus_host_notify must not be freed afterwards, it is a
- * managed resource already.
- */
-struct smbus_host_notify *i2c_setup_smbus_host_notify(struct i2c_adapter *adap)
-{
-       struct smbus_host_notify *host_notify;
-
-       host_notify = devm_kzalloc(&adap->dev, sizeof(struct smbus_host_notify),
-                                  GFP_KERNEL);
-       if (!host_notify)
-               return NULL;
-
-       host_notify->adapter = adap;
-
-       spin_lock_init(&host_notify->lock);
-       INIT_WORK(&host_notify->work, smbus_host_notify_work);
-
-       return host_notify;
-}
-EXPORT_SYMBOL_GPL(i2c_setup_smbus_host_notify);
-
-/**
- * i2c_handle_smbus_host_notify - Forward a Host Notify event to the correct
- * I2C client.
- * @host_notify: the struct host_notify attached to the relevant adapter
- * @addr: the I2C address of the notifying device
- * @data: the payload of the notification
- * Context: can't sleep
- *
- * Helper function to be called from an I2C bus driver's interrupt
- * handler. It will schedule the Host Notify work, in turn calling the
- * corresponding I2C device driver's alert function.
- *
- * host_notify should be a valid pointer previously returned by
- * i2c_setup_smbus_host_notify().
- */
-int i2c_handle_smbus_host_notify(struct smbus_host_notify *host_notify,
-                                unsigned short addr, unsigned int data)
-{
-       unsigned long flags;
-       struct i2c_adapter *adapter;
-
-       if (!host_notify || !host_notify->adapter)
-               return -EINVAL;
-
-       adapter = host_notify->adapter;
-
-       spin_lock_irqsave(&host_notify->lock, flags);
-
-       if (host_notify->pending) {
-               spin_unlock_irqrestore(&host_notify->lock, flags);
-               dev_warn(&adapter->dev, "Host Notify already scheduled.\n");
-               return -EBUSY;
-       }
-
-       host_notify->payload = data;
-       host_notify->addr = addr;
-
-       /* Mark that there is a pending notification and release the lock */
-       host_notify->pending = true;
-       spin_unlock_irqrestore(&host_notify->lock, flags);
-
-       return schedule_work(&host_notify->work);
-}
-EXPORT_SYMBOL_GPL(i2c_handle_smbus_host_notify);
-
 module_i2c_driver(smbalert_driver);
 
 MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>");
 
                                         struct i2c_smbus_alert_setup *setup);
 int i2c_handle_smbus_alert(struct i2c_client *ara);
 
-/**
- * smbus_host_notify - internal structure used by the Host Notify mechanism.
- * @adapter: the I2C adapter associated with this struct
- * @work: worker used to schedule the IRQ in the slave device
- * @lock: spinlock to check if a notification is already pending
- * @pending: flag set when a notification is pending (any new notification will
- *             be rejected if pending is true)
- * @payload: the actual payload of the Host Notify event
- * @addr: the address of the slave device which raised the notification
- *
- * This struct needs to be allocated by i2c_setup_smbus_host_notify() and does
- * not need to be freed. Internally, i2c_setup_smbus_host_notify() uses a
- * managed resource to clean this up when the adapter get released.
- */
-struct smbus_host_notify {
-       struct i2c_adapter      *adapter;
-       struct work_struct      work;
-       spinlock_t              lock;
-       bool                    pending;
-       u16                     payload;
-       u8                      addr;
-};
-
-struct smbus_host_notify *i2c_setup_smbus_host_notify(struct i2c_adapter *adap);
-int i2c_handle_smbus_host_notify(struct smbus_host_notify *host_notify,
-                                unsigned short addr, unsigned int data);
-
 #endif /* _LINUX_I2C_SMBUS_H */
 
 #include <linux/device.h>      /* for struct device */
 #include <linux/sched.h>       /* for completion */
 #include <linux/mutex.h>
+#include <linux/irqdomain.h>           /* for Host Notify IRQ */
 #include <linux/of.h>          /* for struct device_node */
 #include <linux/swab.h>                /* for swab16 */
 #include <uapi/linux/i2c.h>
 
        struct i2c_bus_recovery_info *bus_recovery_info;
        const struct i2c_adapter_quirks *quirks;
+
+       struct irq_domain *host_notify_domain;
 };
 #define to_i2c_adapter(d) container_of(d, struct i2c_adapter, dev)
 
        return (msg->addr << 1) | (msg->flags & I2C_M_RD ? 1 : 0);
 }
 
+int i2c_handle_smbus_host_notify(struct i2c_adapter *adap, unsigned short addr);
 /**
  * module_i2c_driver() - Helper macro for registering a modular I2C driver
  * @__i2c_driver: i2c_driver struct