summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/i2c/smbus-protocol6
-rw-r--r--drivers/i2c/i2c-smbus.c113
-rw-r--r--include/linux/i2c-smbus.h44
-rw-r--r--include/linux/i2c.h3
-rw-r--r--include/uapi/linux/i2c.h1
5 files changed, 162 insertions, 5 deletions
diff --git a/Documentation/i2c/smbus-protocol b/Documentation/i2c/smbus-protocol
index 6012b12b3510..14d4ec1be245 100644
--- a/Documentation/i2c/smbus-protocol
+++ b/Documentation/i2c/smbus-protocol
@@ -199,6 +199,12 @@ alerting device's address.
199 199
200[S] [HostAddr] [Wr] A [DevAddr] A [DataLow] A [DataHigh] A [P] 200[S] [HostAddr] [Wr] A [DevAddr] A [DataLow] A [DataHigh] A [P]
201 201
202This 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
202 208
203Packet Error Checking (PEC) 209Packet Error Checking (PEC)
204=========================== 210===========================
diff --git a/drivers/i2c/i2c-smbus.c b/drivers/i2c/i2c-smbus.c
index 3b6765a4ebe9..f574995b41c1 100644
--- a/drivers/i2c/i2c-smbus.c
+++ b/drivers/i2c/i2c-smbus.c
@@ -33,7 +33,8 @@ struct i2c_smbus_alert {
33 33
34struct alert_data { 34struct alert_data {
35 unsigned short addr; 35 unsigned short addr;
36 u8 flag:1; 36 enum i2c_alert_protocol type;
37 unsigned int data;
37}; 38};
38 39
39/* If this is the alerting device, notify its driver */ 40/* If this is the alerting device, notify its driver */
@@ -56,8 +57,7 @@ static int smbus_do_alert(struct device *dev, void *addrp)
56 if (client->dev.driver) { 57 if (client->dev.driver) {
57 driver = to_i2c_driver(client->dev.driver); 58 driver = to_i2c_driver(client->dev.driver);
58 if (driver->alert) 59 if (driver->alert)
59 driver->alert(client, I2C_PROTOCOL_SMBUS_ALERT, 60 driver->alert(client, data->type, data->data);
60 data->flag);
61 else 61 else
62 dev_warn(&client->dev, "no driver alert()!\n"); 62 dev_warn(&client->dev, "no driver alert()!\n");
63 } else 63 } else
@@ -97,8 +97,9 @@ static void smbus_alert(struct work_struct *work)
97 if (status < 0) 97 if (status < 0)
98 break; 98 break;
99 99
100 data.flag = status & 1; 100 data.data = status & 1;
101 data.addr = status >> 1; 101 data.addr = status >> 1;
102 data.type = I2C_PROTOCOL_SMBUS_ALERT;
102 103
103 if (data.addr == prev_addr) { 104 if (data.addr == prev_addr) {
104 dev_warn(&ara->dev, "Duplicate SMBALERT# from dev " 105 dev_warn(&ara->dev, "Duplicate SMBALERT# from dev "
@@ -106,7 +107,7 @@ static void smbus_alert(struct work_struct *work)
106 break; 107 break;
107 } 108 }
108 dev_dbg(&ara->dev, "SMBALERT# from dev 0x%02x, flag %d\n", 109 dev_dbg(&ara->dev, "SMBALERT# from dev 0x%02x, flag %d\n",
109 data.addr, data.flag); 110 data.addr, data.data);
110 111
111 /* Notify driver for the device which issued the alert */ 112 /* Notify driver for the device which issued the alert */
112 device_for_each_child(&ara->adapter->dev, &data, 113 device_for_each_child(&ara->adapter->dev, &data,
@@ -240,6 +241,108 @@ int i2c_handle_smbus_alert(struct i2c_client *ara)
240} 241}
241EXPORT_SYMBOL_GPL(i2c_handle_smbus_alert); 242EXPORT_SYMBOL_GPL(i2c_handle_smbus_alert);
242 243
244static 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 */
283struct 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}
299EXPORT_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 */
316int 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}
344EXPORT_SYMBOL_GPL(i2c_handle_smbus_host_notify);
345
243module_i2c_driver(smbalert_driver); 346module_i2c_driver(smbalert_driver);
244 347
245MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>"); 348MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>");
diff --git a/include/linux/i2c-smbus.h b/include/linux/i2c-smbus.h
index 8f1b086ca5bc..4ac95bbe53ef 100644
--- a/include/linux/i2c-smbus.h
+++ b/include/linux/i2c-smbus.h
@@ -23,6 +23,8 @@
23#define _LINUX_I2C_SMBUS_H 23#define _LINUX_I2C_SMBUS_H
24 24
25#include <linux/i2c.h> 25#include <linux/i2c.h>
26#include <linux/spinlock.h>
27#include <linux/workqueue.h>
26 28
27 29
28/** 30/**
@@ -48,4 +50,46 @@ struct i2c_client *i2c_setup_smbus_alert(struct i2c_adapter *adapter,
48 struct i2c_smbus_alert_setup *setup); 50 struct i2c_smbus_alert_setup *setup);
49int i2c_handle_smbus_alert(struct i2c_client *ara); 51int i2c_handle_smbus_alert(struct i2c_client *ara);
50 52
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 */
67struct 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)
77struct smbus_host_notify *i2c_setup_smbus_host_notify(struct i2c_adapter *adap);
78int i2c_handle_smbus_host_notify(struct smbus_host_notify *host_notify,
79 unsigned short addr, unsigned int data);
80#else
81static inline struct smbus_host_notify *
82i2c_setup_smbus_host_notify(struct i2c_adapter *adap)
83{
84 return NULL;
85}
86
87static inline int
88i2c_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
51#endif /* _LINUX_I2C_SMBUS_H */ 95#endif /* _LINUX_I2C_SMBUS_H */
diff --git a/include/linux/i2c.h b/include/linux/i2c.h
index 37a45dcd6592..fffdc270ca18 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,
128 128
129enum i2c_alert_protocol { 129enum i2c_alert_protocol {
130 I2C_PROTOCOL_SMBUS_ALERT, 130 I2C_PROTOCOL_SMBUS_ALERT,
131 I2C_PROTOCOL_SMBUS_HOST_NOTIFY,
131}; 132};
132 133
133/** 134/**
@@ -184,6 +185,8 @@ struct i2c_driver {
184 * The format and meaning of the data value depends on the protocol. 185 * The format and meaning of the data value depends on the protocol.
185 * For the SMBus alert protocol, there is a single bit of data passed 186 * For the SMBus alert protocol, there is a single bit of data passed
186 * as the alert response's low bit ("event flag"). 187 * 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.
187 */ 190 */
188 void (*alert)(struct i2c_client *, enum i2c_alert_protocol protocol, 191 void (*alert)(struct i2c_client *, enum i2c_alert_protocol protocol,
189 unsigned int data); 192 unsigned int data);
diff --git a/include/uapi/linux/i2c.h b/include/uapi/linux/i2c.h
index adcbef4bff61..009e27bb9abe 100644
--- a/include/uapi/linux/i2c.h
+++ b/include/uapi/linux/i2c.h
@@ -102,6 +102,7 @@ struct i2c_msg {
102#define I2C_FUNC_SMBUS_WRITE_BLOCK_DATA 0x02000000 102#define I2C_FUNC_SMBUS_WRITE_BLOCK_DATA 0x02000000
103#define I2C_FUNC_SMBUS_READ_I2C_BLOCK 0x04000000 /* I2C-like block xfer */ 103#define I2C_FUNC_SMBUS_READ_I2C_BLOCK 0x04000000 /* I2C-like block xfer */
104#define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK 0x08000000 /* w/ 1-byte reg. addr. */ 104#define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK 0x08000000 /* w/ 1-byte reg. addr. */
105#define I2C_FUNC_SMBUS_HOST_NOTIFY 0x10000000
105 106
106#define I2C_FUNC_SMBUS_BYTE (I2C_FUNC_SMBUS_READ_BYTE | \ 107#define I2C_FUNC_SMBUS_BYTE (I2C_FUNC_SMBUS_READ_BYTE | \
107 I2C_FUNC_SMBUS_WRITE_BYTE) 108 I2C_FUNC_SMBUS_WRITE_BYTE)