diff mbox

[v8,2/4] i2c-smbus: add SMBus Host Notify support

Message ID 1465484030-28838-3-git-send-email-benjamin.tissoires@redhat.com
State Accepted
Headers show

Commit Message

Benjamin Tissoires June 9, 2016, 2:53 p.m. UTC
SMBus Host Notify allows a slave device to act as a master on a bus to
notify the host of an interrupt. On Intel chipsets, the functionality
is directly implemented in the firmware. We just need to export a
function to call .alert() on the proper device driver.

i2c_handle_smbus_host_notify() behaves like i2c_handle_smbus_alert().
When called, it schedules a task that will be able to sleep to go through
the list of devices attached to the adapter.

The current implementation allows one Host Notification to be scheduled
while an other is running.

Tested-by: Andrew Duggan <aduggan@synaptics.com>
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---
changes in v2:
- do the actual processing of finding the device in i2c-smbus.c
- remove the i2c-core implementations
- remove the manual toggle of SMBus Host Notify

no changes in v3

changes in v4:
- schedule the worker in i2c_handle_smbus_host_notify() -> it can now be called
  in an interrupt context.
- introduce i2c_setup_smbus_host_notify()

no changes in v5

changes in v6:
- fix the parameters of the inlined functions when the module is not compiled

changes in v7:
- fixed typo in i2c_handle_smbus_host_notify() warning message ('.' and '\n'
  were inverted)

changes in v8:
- Documentation updated to reflect the code
- s/-EFAULT/-EBUSY
- use of IS_ENABLED(CONFIG_I2C_SMBUS) instead of a custom made test :)

 Documentation/i2c/smbus-protocol |   6 +++
 drivers/i2c/i2c-smbus.c          | 113 +++++++++++++++++++++++++++++++++++++--
 include/linux/i2c-smbus.h        |  44 +++++++++++++++
 include/linux/i2c.h              |   3 ++
 include/uapi/linux/i2c.h         |   1 +
 5 files changed, 162 insertions(+), 5 deletions(-)

Comments

Wolfram Sang June 17, 2016, 11:26 a.m. UTC | #1
On Thu, Jun 09, 2016 at 04:53:48PM +0200, Benjamin Tissoires wrote:
> SMBus Host Notify allows a slave device to act as a master on a bus to
> notify the host of an interrupt. On Intel chipsets, the functionality
> is directly implemented in the firmware. We just need to export a
> function to call .alert() on the proper device driver.
> 
> i2c_handle_smbus_host_notify() behaves like i2c_handle_smbus_alert().
> When called, it schedules a task that will be able to sleep to go through
> the list of devices attached to the adapter.
> 
> The current implementation allows one Host Notification to be scheduled
> while an other is running.
> 
> Tested-by: Andrew Duggan <aduggan@synaptics.com>
> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>

Applied to for-next, thanks!
Jean Delvare July 18, 2016, 9:37 a.m. UTC | #2
[As it doesn't look like this message was delivered, I am sending it
again. I apologize if this is a duplicate for some of you.]

Hi Benjamin, Wolfram,

Sorry for being late to the party. I finally found some time to look at
the patches. Looks good overall, with just two minor comments:

On jeu., 2016-06-09 at 16:53 +0200, Benjamin Tissoires wrote:
> SMBus Host Notify allows a slave device to act as a master on a bus to
> notify the host of an interrupt. On Intel chipsets, the functionality
> is directly implemented in the firmware. We just need to export a
> function to call .alert() on the proper device driver.
> 
> i2c_handle_smbus_host_notify() behaves like i2c_handle_smbus_alert().
> When called, it schedules a task that will be able to sleep to go through
> the list of devices attached to the adapter.
> 
> The current implementation allows one Host Notification to be scheduled
> while an other is running.
> 
> Tested-by: Andrew Duggan <aduggan@synaptics.com>
> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
> ---
> (...)
> diff --git a/drivers/i2c/i2c-smbus.c b/drivers/i2c/i2c-smbus.c
> index 3b6765a..f574995 100644
> --- a/drivers/i2c/i2c-smbus.c
> +++ b/drivers/i2c/i2c-smbus.c
> (...)
> +/**
> + * 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
> + * @data: the Host Notify data which contains the payload and address of the
> + * client

Doesn't look correct. The data parameter doesn't contain the address,
that would be in the (undocumented) address parameter. I'll send a
patch.

> diff --git a/include/linux/i2c-smbus.h b/include/linux/i2c-smbus.h
> index 8f1b086..4ac95bb 100644
> --- a/include/linux/i2c-smbus.h
> +++ b/include/linux/i2c-smbus.h
> (...)
> +#if IS_ENABLED(CONFIG_I2C_SMBUS)
> +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);
> +#else
> +static inline struct smbus_host_notify *
> +i2c_setup_smbus_host_notify(struct i2c_adapter *adap)
> +{
> +	return NULL;
> +}
> +
> +static inline int
> +i2c_handle_smbus_host_notify(struct smbus_host_notify *host_notify,
> +			     unsigned short addr, unsigned int data)
> +{
> +	return 0;
> +}
> +#endif /* I2C_SMBUS */

You provide stubs for SMBus Host Notify support if CONFIG_I2C_SMBUS is
not selected. There are no such stubs for SMBus Alert support, for which
I assumed drivers would select I2C_SMBUS if they have support. Which is
what you are actually doing for i2c-i801 in a latter patch.

Is there any reason for this difference? For consistency I'd rather
provide stubs for all or none. My preference being for none, unless you
have a use case which requires them.
Jean Delvare July 18, 2016, 2:31 p.m. UTC | #3
Hi Benjamin, Wolfram,

Now that I have reviewed the i2c-i801 part of the implementation, I'm
wondering...

On Thu,  9 Jun 2016 16:53:48 +0200, Benjamin Tissoires wrote:
> +/**
> + * 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);

Here we initialize a workqueue.

> +
> +	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
> + * @data: the Host Notify data which contains the payload and address of the
> + * client
> + * 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);

And here we use it.

> +}
> +EXPORT_SYMBOL_GPL(i2c_handle_smbus_host_notify);

But what happens on i2c_adapter removal? What prevents the following
sequence from happening?

1* A Host Notify event happens.
2* The event is handled and queued by i2c_handle_smbus_host_notify().
3* Someone tears down the underlying i2c_adapter (for example "rmmod
   i2c-i801".)
4* The workqueue is processed, accessing memory which has already been
   freed.

Of course it would be back luck, but that's pretty much the definition
of a race condition ;-)

To be on the safe side, don't we need a teardown function in i2c-smbus,
that could be called before i2c_del_adapter, which would remove the
host notify handle and flush the workqueue?
Benjamin Tissoires July 18, 2016, 3:59 p.m. UTC | #4
On Jul 18 2016 or thereabouts, Jean Delvare wrote:
> [As it doesn't look like this message was delivered, I am sending it
> again. I apologize if this is a duplicate for some of you.]
> 
> Hi Benjamin, Wolfram,
> 
> Sorry for being late to the party. I finally found some time to look at
> the patches. Looks good overall, with just two minor comments:
> 
> On jeu., 2016-06-09 at 16:53 +0200, Benjamin Tissoires wrote:
> > SMBus Host Notify allows a slave device to act as a master on a bus to
> > notify the host of an interrupt. On Intel chipsets, the functionality
> > is directly implemented in the firmware. We just need to export a
> > function to call .alert() on the proper device driver.
> > 
> > i2c_handle_smbus_host_notify() behaves like i2c_handle_smbus_alert().
> > When called, it schedules a task that will be able to sleep to go through
> > the list of devices attached to the adapter.
> > 
> > The current implementation allows one Host Notification to be scheduled
> > while an other is running.
> > 
> > Tested-by: Andrew Duggan <aduggan@synaptics.com>
> > Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
> > ---
> > (...)
> > diff --git a/drivers/i2c/i2c-smbus.c b/drivers/i2c/i2c-smbus.c
> > index 3b6765a..f574995 100644
> > --- a/drivers/i2c/i2c-smbus.c
> > +++ b/drivers/i2c/i2c-smbus.c
> > (...)
> > +/**
> > + * 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
> > + * @data: the Host Notify data which contains the payload and address of the
> > + * client
> 
> Doesn't look correct. The data parameter doesn't contain the address,
> that would be in the (undocumented) address parameter. I'll send a
> patch.

Thanks for the fixup patch already :)

> 
> > diff --git a/include/linux/i2c-smbus.h b/include/linux/i2c-smbus.h
> > index 8f1b086..4ac95bb 100644
> > --- a/include/linux/i2c-smbus.h
> > +++ b/include/linux/i2c-smbus.h
> > (...)
> > +#if IS_ENABLED(CONFIG_I2C_SMBUS)
> > +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);
> > +#else
> > +static inline struct smbus_host_notify *
> > +i2c_setup_smbus_host_notify(struct i2c_adapter *adap)
> > +{
> > +	return NULL;
> > +}
> > +
> > +static inline int
> > +i2c_handle_smbus_host_notify(struct smbus_host_notify *host_notify,
> > +			     unsigned short addr, unsigned int data)
> > +{
> > +	return 0;
> > +}
> > +#endif /* I2C_SMBUS */
> 
> You provide stubs for SMBus Host Notify support if CONFIG_I2C_SMBUS is
> not selected. There are no such stubs for SMBus Alert support, for which
> I assumed drivers would select I2C_SMBUS if they have support. Which is
> what you are actually doing for i2c-i801 in a latter patch.
> 
> Is there any reason for this difference? For consistency I'd rather
> provide stubs for all or none. My preference being for none, unless you
> have a use case which requires them.

Looks like you are right. There is no need for the stubs and they can be
dropped. I think I had them in the first place for a previous
implementation, and they just stayed here.

Given that you already sent a few cleanup patches, do you want to send
this fix also, or do you expect me to send it? (I don't think there will
be a conflict, so either is fine).


Cheers,
Benjamin

> 
> -- 
> Jean Delvare
> SUSE L3 Support
--
To unsubscribe from this list: send the line "unsubscribe linux-i2c" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Benjamin Tissoires July 18, 2016, 4:35 p.m. UTC | #5
On Jul 18 2016 or thereabouts, Jean Delvare wrote:
> Hi Benjamin, Wolfram,
> 
> Now that I have reviewed the i2c-i801 part of the implementation, I'm
> wondering...
> 
> On Thu,  9 Jun 2016 16:53:48 +0200, Benjamin Tissoires wrote:
> > +/**
> > + * 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);
> 
> Here we initialize a workqueue.
> 
> > +
> > +	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
> > + * @data: the Host Notify data which contains the payload and address of the
> > + * client
> > + * 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);
> 
> And here we use it.
> 
> > +}
> > +EXPORT_SYMBOL_GPL(i2c_handle_smbus_host_notify);
> 
> But what happens on i2c_adapter removal? What prevents the following
> sequence from happening?
> 
> 1* A Host Notify event happens.
> 2* The event is handled and queued by i2c_handle_smbus_host_notify().
> 3* Someone tears down the underlying i2c_adapter (for example "rmmod
>    i2c-i801".)
> 4* The workqueue is processed, accessing memory which has already been
>    freed.
> 
> Of course it would be back luck, but that's pretty much the definition
> of a race condition ;-)

Yes, you are right :(
Sorry for not doing things properly :/

> 
> To be on the safe side, don't we need a teardown function in i2c-smbus,
> that could be called before i2c_del_adapter, which would remove the
> host notify handle and flush the workqueue?

I was thinking at adding a devm action on the release of the struct
smbus_host_notify, but it's actually a bad idea because some other
resources (children moslty) might already be released when the devres
action will be called.

I think it might be easier to add a i2c_remove_host_notify() (or such)
which would make sure we call the cancel_work_sync() function. It would
be the responsibility of the caller to call it once
i2c_setup_smbus_host_notify() has been called. I'd say it has the
advantage of not adding any hidden data in the adapter to the cost of a
small pain in the adapter driver.

Cheers,
Benjamin

> 
> -- 
> Jean Delvare
> SUSE L3 Support
--
To unsubscribe from this list: send the line "unsubscribe linux-i2c" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Jean Delvare July 18, 2016, 4:47 p.m. UTC | #6
On Mon, 18 Jul 2016 17:59:02 +0200, Benjamin Tissoires wrote:
> On Jul 18 2016 or thereabouts, Jean Delvare wrote:
> > You provide stubs for SMBus Host Notify support if CONFIG_I2C_SMBUS is
> > not selected. There are no such stubs for SMBus Alert support, for which
> > I assumed drivers would select I2C_SMBUS if they have support. Which is
> > what you are actually doing for i2c-i801 in a latter patch.
> > 
> > Is there any reason for this difference? For consistency I'd rather
> > provide stubs for all or none. My preference being for none, unless you
> > have a use case which requires them.
> 
> Looks like you are right. There is no need for the stubs and they can be
> dropped. I think I had them in the first place for a previous
> implementation, and they just stayed here.
> 
> Given that you already sent a few cleanup patches, do you want to send
> this fix also, or do you expect me to send it? (I don't think there will
> be a conflict, so either is fine).

I can do it, I just wanted to make sure it was OK with you first.
Jean Delvare July 18, 2016, 8:47 p.m. UTC | #7
On Mon, 18 Jul 2016 18:35:19 +0200, Benjamin Tissoires wrote:
> On Jul 18 2016 or thereabouts, Jean Delvare wrote:
> > But what happens on i2c_adapter removal? What prevents the following
> > sequence from happening?
> > 
> > 1* A Host Notify event happens.
> > 2* The event is handled and queued by i2c_handle_smbus_host_notify().
> > 3* Someone tears down the underlying i2c_adapter (for example "rmmod
> >    i2c-i801".)
> > 4* The workqueue is processed, accessing memory which has already been
> >    freed.
> > 
> > Of course it would be back luck, but that's pretty much the definition
> > of a race condition ;-)
> 
> Yes, you are right :(
> Sorry for not doing things properly :/

No worry. Bugs happen everywhere, we find them and fix them. That's
part of the process. If we only submit patches which we are 100%
certain are perfect, we never submit anything. I know something about
that...

> > To be on the safe side, don't we need a teardown function in i2c-smbus,
> > that could be called before i2c_del_adapter, which would remove the
> > host notify handle and flush the workqueue?
> 
> I was thinking at adding a devm action on the release of the struct
> smbus_host_notify, but it's actually a bad idea because some other
> resources (children moslty) might already be released when the devres
> action will be called.
> 
> I think it might be easier to add a i2c_remove_host_notify() (or such)
> which would make sure we call the cancel_work_sync() function. It would
> be the responsibility of the caller to call it once
> i2c_setup_smbus_host_notify() has been called. I'd say it has the
> advantage of not adding any hidden data in the adapter to the cost of a
> small pain in the adapter driver.

That's what I had in mind as well, but I'm open to any option which
solves the problem really.
diff mbox

Patch

diff --git a/Documentation/i2c/smbus-protocol b/Documentation/i2c/smbus-protocol
index 6012b12..14d4ec1 100644
--- a/Documentation/i2c/smbus-protocol
+++ b/Documentation/i2c/smbus-protocol
@@ -199,6 +199,12 @@  alerting device's address.
 
 [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.
+
 
 Packet Error Checking (PEC)
 ===========================
diff --git a/drivers/i2c/i2c-smbus.c b/drivers/i2c/i2c-smbus.c
index 3b6765a..f574995 100644
--- a/drivers/i2c/i2c-smbus.c
+++ b/drivers/i2c/i2c-smbus.c
@@ -33,7 +33,8 @@  struct i2c_smbus_alert {
 
 struct alert_data {
 	unsigned short		addr;
-	u8			flag:1;
+	enum i2c_alert_protocol	type;
+	unsigned int		data;
 };
 
 /* If this is the alerting device, notify its driver */
@@ -56,8 +57,7 @@  static int smbus_do_alert(struct device *dev, void *addrp)
 	if (client->dev.driver) {
 		driver = to_i2c_driver(client->dev.driver);
 		if (driver->alert)
-			driver->alert(client, I2C_PROTOCOL_SMBUS_ALERT,
-				      data->flag);
+			driver->alert(client, data->type, data->data);
 		else
 			dev_warn(&client->dev, "no driver alert()!\n");
 	} else
@@ -97,8 +97,9 @@  static void smbus_alert(struct work_struct *work)
 		if (status < 0)
 			break;
 
-		data.flag = status & 1;
+		data.data = status & 1;
 		data.addr = status >> 1;
+		data.type = I2C_PROTOCOL_SMBUS_ALERT;
 
 		if (data.addr == prev_addr) {
 			dev_warn(&ara->dev, "Duplicate SMBALERT# from dev "
@@ -106,7 +107,7 @@  static void smbus_alert(struct work_struct *work)
 			break;
 		}
 		dev_dbg(&ara->dev, "SMBALERT# from dev 0x%02x, flag %d\n",
-			data.addr, data.flag);
+			data.addr, data.data);
 
 		/* Notify driver for the device which issued the alert */
 		device_for_each_child(&ara->adapter->dev, &data,
@@ -240,6 +241,108 @@  int i2c_handle_smbus_alert(struct i2c_client *ara)
 }
 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
+ * @data: the Host Notify data which contains the payload and address of the
+ * client
+ * 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>");
diff --git a/include/linux/i2c-smbus.h b/include/linux/i2c-smbus.h
index 8f1b086..4ac95bb 100644
--- a/include/linux/i2c-smbus.h
+++ b/include/linux/i2c-smbus.h
@@ -23,6 +23,8 @@ 
 #define _LINUX_I2C_SMBUS_H
 
 #include <linux/i2c.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
 
 
 /**
@@ -48,4 +50,46 @@  struct i2c_client *i2c_setup_smbus_alert(struct i2c_adapter *adapter,
 					 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;
+};
+
+#if IS_ENABLED(CONFIG_I2C_SMBUS)
+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);
+#else
+static inline struct smbus_host_notify *
+i2c_setup_smbus_host_notify(struct i2c_adapter *adap)
+{
+	return NULL;
+}
+
+static inline int
+i2c_handle_smbus_host_notify(struct smbus_host_notify *host_notify,
+			     unsigned short addr, unsigned int data)
+{
+	return 0;
+}
+#endif /* I2C_SMBUS */
+
 #endif /* _LINUX_I2C_SMBUS_H */
diff --git a/include/linux/i2c.h b/include/linux/i2c.h
index 29a2d3b7c..e8ccdfe 100644
--- a/include/linux/i2c.h
+++ b/include/linux/i2c.h
@@ -128,6 +128,7 @@  i2c_smbus_read_i2c_block_data_or_emulated(const struct i2c_client *client,
 
 enum i2c_alert_protocol {
 	I2C_PROTOCOL_SMBUS_ALERT,
+	I2C_PROTOCOL_SMBUS_HOST_NOTIFY,
 };
 
 /**
@@ -184,6 +185,8 @@  struct i2c_driver {
 	 * The format and meaning of the data value depends on the protocol.
 	 * For the SMBus alert protocol, there is a single bit of data passed
 	 * as the alert response's low bit ("event flag").
+	 * For the SMBus Host Notify protocol, the data corresponds to the
+	 * 16-bit payload data reported by the slave device acting as master.
 	 */
 	void (*alert)(struct i2c_client *, enum i2c_alert_protocol protocol,
 		      unsigned int data);
diff --git a/include/uapi/linux/i2c.h b/include/uapi/linux/i2c.h
index adcbef4..009e27b 100644
--- a/include/uapi/linux/i2c.h
+++ b/include/uapi/linux/i2c.h
@@ -102,6 +102,7 @@  struct i2c_msg {
 #define I2C_FUNC_SMBUS_WRITE_BLOCK_DATA 0x02000000
 #define I2C_FUNC_SMBUS_READ_I2C_BLOCK	0x04000000 /* I2C-like block xfer  */
 #define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK	0x08000000 /* w/ 1-byte reg. addr. */
+#define I2C_FUNC_SMBUS_HOST_NOTIFY	0x10000000
 
 #define I2C_FUNC_SMBUS_BYTE		(I2C_FUNC_SMBUS_READ_BYTE | \
 					 I2C_FUNC_SMBUS_WRITE_BYTE)