History

i2c: smbus: add SMBus Host Notify support

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.

Benjamin Tissoires committed 10 years ago

Commit e456cd31 parent b4f21055 files · +162 −5Browse files

Unified · Split

5 files
  1. Documentation/i2c/smbus-protocol +6 −0
  2. drivers/i2c/i2c-smbus.c +108 −5
  3. include/linux/i2c-smbus.h +44 −0
  4. include/linux/i2c.h +3 −0
  5. include/uapi/linux/i2c.h +1 −0
Documentation/i2c/smbus-protocol+6−0
@@ -199,6 +199,12 @@ alerting device's address. · 198 unmodified lines
199199
200200 [S] [HostAddr] [Wr] A [DevAddr] A [DataLow] A [DataHigh] A [P]
201201
202+This is implemented in the following way in the Linux kernel:
203+* I2C bus drivers which support SMBus Host Notify should call
204+ i2c_setup_smbus_host_notify() to setup SMBus Host Notify support.
205+* I2C drivers for devices which can trigger SMBus Host Notify should implement
206+ the optional alert() callback.
207+
202208
203209 Packet Error Checking (PEC)
204210 ===========================
drivers/i2c/i2c-smbus.c+108−5
@@ -33,7 +33,8 @@ struct i2c_smbus_alert { · 32 unmodified lines
3333
3434 struct alert_data {
3535 unsigned short addr;
36− u8 flag:1;
36+ enum i2c_alert_protocol type;
37+ unsigned int data;
3738 };
3839
3940 /* If this is the alerting device, notify its driver */
@@ -56,8 +57,7 @@ static int smbus_do_alert(struct device *dev, void *addrp) · 16 unmodified lines
5657 if (client->dev.driver) {
5758 driver = to_i2c_driver(client->dev.driver);
5859 if (driver->alert)
59− driver->alert(client, I2C_PROTOCOL_SMBUS_ALERT,
60− data->flag);
60+ driver->alert(client, data->type, data->data);
6161 else
6262 dev_warn(&client->dev, "no driver alert()!\n");
6363 } else
@@ -97,8 +97,9 @@ static void smbus_alert(struct work_struct *work) · 33 unmodified lines
9797 if (status < 0)
9898 break;
9999
100− data.flag = status & 1;
100+ data.data = status & 1;
101101 data.addr = status >> 1;
102+ data.type = I2C_PROTOCOL_SMBUS_ALERT;
102103
103104 if (data.addr == prev_addr) {
104105 dev_warn(&ara->dev, "Duplicate SMBALERT# from dev "
@@ -106,7 +107,7 @@ static void smbus_alert(struct work_struct *work) · 1 unmodified line
106107 break;
107108 }
108109 dev_dbg(&ara->dev, "SMBALERT# from dev 0x%02x, flag %d\n",
109− data.addr, data.flag);
110+ data.addr, data.data);
110111
111112 /* Notify driver for the device which issued the alert */
112113 device_for_each_child(&ara->adapter->dev, &data,
@@ -240,6 +241,108 @@ int i2c_handle_smbus_alert(struct i2c_client *ara) · 127 unmodified lines
240241 }
241242 EXPORT_SYMBOL_GPL(i2c_handle_smbus_alert);
242243
244+static void smbus_host_notify_work(struct work_struct *work)
245+{
246+ struct alert_data alert;
247+ struct i2c_adapter *adapter;
248+ unsigned long flags;
249+ u16 payload;
250+ u8 addr;
251+ struct smbus_host_notify *data;
252+
253+ data = container_of(work, struct smbus_host_notify, work);
254+
255+ spin_lock_irqsave(&data->lock, flags);
256+ payload = data->payload;
257+ addr = data->addr;
258+ adapter = data->adapter;
259+
260+ /* clear the pending bit and release the spinlock */
261+ data->pending = false;
262+ spin_unlock_irqrestore(&data->lock, flags);
263+
264+ if (!adapter || !addr)
265+ return;
266+
267+ alert.type = I2C_PROTOCOL_SMBUS_HOST_NOTIFY;
268+ alert.addr = addr;
269+ alert.data = payload;
270+
271+ device_for_each_child(&adapter->dev, &alert, smbus_do_alert);
272+}
273+
274+/**
275+ * i2c_setup_smbus_host_notify - Allocate a new smbus_host_notify for the given
276+ * I2C adapter.
277+ * @adapter: the adapter we want to associate a Host Notify function
278+ *
279+ * Returns a struct smbus_host_notify pointer on success, and NULL on failure.
280+ * The resulting smbus_host_notify must not be freed afterwards, it is a
281+ * managed resource already.
282+ */
283+struct smbus_host_notify *i2c_setup_smbus_host_notify(struct i2c_adapter *adap)
284+{
285+ struct smbus_host_notify *host_notify;
286+
287+ host_notify = devm_kzalloc(&adap->dev, sizeof(struct smbus_host_notify),
288+ GFP_KERNEL);
289+ if (!host_notify)
290+ return NULL;
291+
292+ host_notify->adapter = adap;
293+
294+ spin_lock_init(&host_notify->lock);
295+ INIT_WORK(&host_notify->work, smbus_host_notify_work);
296+
297+ return host_notify;
298+}
299+EXPORT_SYMBOL_GPL(i2c_setup_smbus_host_notify);
300+
301+/**
302+ * i2c_handle_smbus_host_notify - Forward a Host Notify event to the correct
303+ * I2C client.
304+ * @host_notify: the struct host_notify attached to the relevant adapter
305+ * @data: the Host Notify data which contains the payload and address of the
306+ * client
307+ * Context: can't sleep
308+ *
309+ * Helper function to be called from an I2C bus driver's interrupt
310+ * handler. It will schedule the Host Notify work, in turn calling the
311+ * corresponding I2C device driver's alert function.
312+ *
313+ * host_notify should be a valid pointer previously returned by
314+ * i2c_setup_smbus_host_notify().
315+ */
316+int i2c_handle_smbus_host_notify(struct smbus_host_notify *host_notify,
317+ unsigned short addr, unsigned int data)
318+{
319+ unsigned long flags;
320+ struct i2c_adapter *adapter;
321+
322+ if (!host_notify || !host_notify->adapter)
323+ return -EINVAL;
324+
325+ adapter = host_notify->adapter;
326+
327+ spin_lock_irqsave(&host_notify->lock, flags);
328+
329+ if (host_notify->pending) {
330+ spin_unlock_irqrestore(&host_notify->lock, flags);
331+ dev_warn(&adapter->dev, "Host Notify already scheduled.\n");
332+ return -EBUSY;
333+ }
334+
335+ host_notify->payload = data;
336+ host_notify->addr = addr;
337+
338+ /* Mark that there is a pending notification and release the lock */
339+ host_notify->pending = true;
340+ spin_unlock_irqrestore(&host_notify->lock, flags);
341+
342+ return schedule_work(&host_notify->work);
343+}
344+EXPORT_SYMBOL_GPL(i2c_handle_smbus_host_notify);
345+
243346 module_i2c_driver(smbalert_driver);
244347
245348 MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>");
include/linux/i2c-smbus.h+44−0
@@ -23,6 +23,8 @@ · 22 unmodified lines
2323 #define _LINUX_I2C_SMBUS_H
2424
2525 #include <linux/i2c.h>
26+#include <linux/spinlock.h>
27+#include <linux/workqueue.h>
2628
2729
2830 /**
@@ -48,4 +50,46 @@ struct i2c_client *i2c_setup_smbus_alert(struct i2c_adapter *adapter, · 19 unmodified lines
4850 struct i2c_smbus_alert_setup *setup);
4951 int i2c_handle_smbus_alert(struct i2c_client *ara);
5052
53+/**
54+ * smbus_host_notify - internal structure used by the Host Notify mechanism.
55+ * @adapter: the I2C adapter associated with this struct
56+ * @work: worker used to schedule the IRQ in the slave device
57+ * @lock: spinlock to check if a notification is already pending
58+ * @pending: flag set when a notification is pending (any new notification will
59+ * be rejected if pending is true)
60+ * @payload: the actual payload of the Host Notify event
61+ * @addr: the address of the slave device which raised the notification
62+ *
63+ * This struct needs to be allocated by i2c_setup_smbus_host_notify() and does
64+ * not need to be freed. Internally, i2c_setup_smbus_host_notify() uses a
65+ * managed resource to clean this up when the adapter get released.
66+ */
67+struct smbus_host_notify {
68+ struct i2c_adapter *adapter;
69+ struct work_struct work;
70+ spinlock_t lock;
71+ bool pending;
72+ u16 payload;
73+ u8 addr;
74+};
75+
76+#if IS_ENABLED(CONFIG_I2C_SMBUS)
77+struct smbus_host_notify *i2c_setup_smbus_host_notify(struct i2c_adapter *adap);
78+int i2c_handle_smbus_host_notify(struct smbus_host_notify *host_notify,
79+ unsigned short addr, unsigned int data);
80+#else
81+static inline struct smbus_host_notify *
82+i2c_setup_smbus_host_notify(struct i2c_adapter *adap)
83+{
84+ return NULL;
85+}
86+
87+static inline int
88+i2c_handle_smbus_host_notify(struct smbus_host_notify *host_notify,
89+ unsigned short addr, unsigned int data)
90+{
91+ return 0;
92+}
93+#endif /* I2C_SMBUS */
94+
5195 #endif /* _LINUX_I2C_SMBUS_H */
include/linux/i2c.h+3−0
@@ -128,6 +128,7 @@ i2c_smbus_read_i2c_block_data_or_emulated(const struct i2c_client *client, · 127 unmodified lines
128128
129129 enum i2c_alert_protocol {
130130 I2C_PROTOCOL_SMBUS_ALERT,
131+ I2C_PROTOCOL_SMBUS_HOST_NOTIFY,
131132 };
132133
133134 /**
@@ -184,6 +185,8 @@ struct i2c_driver { · 50 unmodified lines
184185 * The format and meaning of the data value depends on the protocol.
185186 * For the SMBus alert protocol, there is a single bit of data passed
186187 * as the alert response's low bit ("event flag").
188+ * For the SMBus Host Notify protocol, the data corresponds to the
189+ * 16-bit payload data reported by the slave device acting as master.
187190 */
188191 void (*alert)(struct i2c_client *, enum i2c_alert_protocol protocol,
189192 unsigned int data);
include/uapi/linux/i2c.h+1−0
@@ -102,6 +102,7 @@ struct i2c_msg { · 101 unmodified lines
102102 #define I2C_FUNC_SMBUS_WRITE_BLOCK_DATA 0x02000000
103103 #define I2C_FUNC_SMBUS_READ_I2C_BLOCK 0x04000000 /* I2C-like block xfer */
104104 #define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK 0x08000000 /* w/ 1-byte reg. addr. */
105+#define I2C_FUNC_SMBUS_HOST_NOTIFY 0x10000000
105106
106107 #define I2C_FUNC_SMBUS_BYTE (I2C_FUNC_SMBUS_READ_BYTE | \
107108 I2C_FUNC_SMBUS_WRITE_BYTE)

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t