Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0-or-later
2 : : /*
3 : : * i2c-smbus.c - SMBus extensions to the I2C protocol
4 : : *
5 : : * Copyright (C) 2008 David Brownell
6 : : * Copyright (C) 2010 Jean Delvare <jdelvare@suse.de>
7 : : */
8 : :
9 : : #include <linux/device.h>
10 : : #include <linux/i2c.h>
11 : : #include <linux/i2c-smbus.h>
12 : : #include <linux/interrupt.h>
13 : : #include <linux/kernel.h>
14 : : #include <linux/module.h>
15 : : #include <linux/of_irq.h>
16 : : #include <linux/slab.h>
17 : : #include <linux/workqueue.h>
18 : :
19 : : struct i2c_smbus_alert {
20 : : struct work_struct alert;
21 : : struct i2c_client *ara; /* Alert response address */
22 : : };
23 : :
24 : : struct alert_data {
25 : : unsigned short addr;
26 : : enum i2c_alert_protocol type;
27 : : unsigned int data;
28 : : };
29 : :
30 : : /* If this is the alerting device, notify its driver */
31 : 0 : static int smbus_do_alert(struct device *dev, void *addrp)
32 : : {
33 : 0 : struct i2c_client *client = i2c_verify_client(dev);
34 : 0 : struct alert_data *data = addrp;
35 : 0 : struct i2c_driver *driver;
36 : :
37 [ # # # # ]: 0 : if (!client || client->addr != data->addr)
38 : : return 0;
39 [ # # ]: 0 : if (client->flags & I2C_CLIENT_TEN)
40 : : return 0;
41 : :
42 : : /*
43 : : * Drivers should either disable alerts, or provide at least
44 : : * a minimal handler. Lock so the driver won't change.
45 : : */
46 : 0 : device_lock(dev);
47 [ # # ]: 0 : if (client->dev.driver) {
48 : 0 : driver = to_i2c_driver(client->dev.driver);
49 [ # # ]: 0 : if (driver->alert)
50 : 0 : driver->alert(client, data->type, data->data);
51 : : else
52 : 0 : dev_warn(&client->dev, "no driver alert()!\n");
53 : : } else
54 : : dev_dbg(&client->dev, "alert with no driver\n");
55 : 0 : device_unlock(dev);
56 : :
57 : : /* Stop iterating after we find the device */
58 : 0 : return -EBUSY;
59 : : }
60 : :
61 : : /*
62 : : * The alert IRQ handler needs to hand work off to a task which can issue
63 : : * SMBus calls, because those sleeping calls can't be made in IRQ context.
64 : : */
65 : 0 : static irqreturn_t smbus_alert(int irq, void *d)
66 : : {
67 : 0 : struct i2c_smbus_alert *alert = d;
68 : 0 : struct i2c_client *ara;
69 : :
70 : 0 : ara = alert->ara;
71 : :
72 : 0 : for (;;) {
73 : 0 : s32 status;
74 : 0 : struct alert_data data;
75 : :
76 : : /*
77 : : * Devices with pending alerts reply in address order, low
78 : : * to high, because of slave transmit arbitration. After
79 : : * responding, an SMBus device stops asserting SMBALERT#.
80 : : *
81 : : * Note that SMBus 2.0 reserves 10-bit addresses for future
82 : : * use. We neither handle them, nor try to use PEC here.
83 : : */
84 : 0 : status = i2c_smbus_read_byte(ara);
85 [ # # ]: 0 : if (status < 0)
86 : : break;
87 : :
88 : 0 : data.data = status & 1;
89 : 0 : data.addr = status >> 1;
90 : 0 : data.type = I2C_PROTOCOL_SMBUS_ALERT;
91 : :
92 : 0 : dev_dbg(&ara->dev, "SMBALERT# from dev 0x%02x, flag %d\n",
93 : : data.addr, data.data);
94 : :
95 : : /* Notify driver for the device which issued the alert */
96 : 0 : device_for_each_child(&ara->adapter->dev, &data,
97 : : smbus_do_alert);
98 : : }
99 : :
100 : 0 : return IRQ_HANDLED;
101 : : }
102 : :
103 : 0 : static void smbalert_work(struct work_struct *work)
104 : : {
105 : 0 : struct i2c_smbus_alert *alert;
106 : :
107 : 0 : alert = container_of(work, struct i2c_smbus_alert, alert);
108 : :
109 : 0 : smbus_alert(0, alert);
110 : :
111 : 0 : }
112 : :
113 : : /* Setup SMBALERT# infrastructure */
114 : 0 : static int smbalert_probe(struct i2c_client *ara,
115 : : const struct i2c_device_id *id)
116 : : {
117 : 0 : struct i2c_smbus_alert_setup *setup = dev_get_platdata(&ara->dev);
118 : 0 : struct i2c_smbus_alert *alert;
119 : 0 : struct i2c_adapter *adapter = ara->adapter;
120 : 0 : int res, irq;
121 : :
122 : 0 : alert = devm_kzalloc(&ara->dev, sizeof(struct i2c_smbus_alert),
123 : : GFP_KERNEL);
124 [ # # ]: 0 : if (!alert)
125 : : return -ENOMEM;
126 : :
127 [ # # ]: 0 : if (setup) {
128 : 0 : irq = setup->irq;
129 : : } else {
130 : : irq = of_irq_get_byname(adapter->dev.of_node, "smbus_alert");
131 : : if (irq <= 0)
132 : : return irq;
133 : : }
134 : :
135 [ # # ]: 0 : INIT_WORK(&alert->alert, smbalert_work);
136 : 0 : alert->ara = ara;
137 : :
138 [ # # ]: 0 : if (irq > 0) {
139 : 0 : res = devm_request_threaded_irq(&ara->dev, irq,
140 : : NULL, smbus_alert,
141 : : IRQF_SHARED | IRQF_ONESHOT,
142 : : "smbus_alert", alert);
143 [ # # ]: 0 : if (res)
144 : : return res;
145 : : }
146 : :
147 : 0 : i2c_set_clientdata(ara, alert);
148 : 0 : dev_info(&adapter->dev, "supports SMBALERT#\n");
149 : :
150 : 0 : return 0;
151 : : }
152 : :
153 : : /* IRQ and memory resources are managed so they are freed automatically */
154 : 0 : static int smbalert_remove(struct i2c_client *ara)
155 : : {
156 : 0 : struct i2c_smbus_alert *alert = i2c_get_clientdata(ara);
157 : :
158 : 0 : cancel_work_sync(&alert->alert);
159 : 0 : return 0;
160 : : }
161 : :
162 : : static const struct i2c_device_id smbalert_ids[] = {
163 : : { "smbus_alert", 0 },
164 : : { /* LIST END */ }
165 : : };
166 : : MODULE_DEVICE_TABLE(i2c, smbalert_ids);
167 : :
168 : : static struct i2c_driver smbalert_driver = {
169 : : .driver = {
170 : : .name = "smbus_alert",
171 : : },
172 : : .probe = smbalert_probe,
173 : : .remove = smbalert_remove,
174 : : .id_table = smbalert_ids,
175 : : };
176 : :
177 : : /**
178 : : * i2c_handle_smbus_alert - Handle an SMBus alert
179 : : * @ara: the ARA client on the relevant adapter
180 : : * Context: can't sleep
181 : : *
182 : : * Helper function to be called from an I2C bus driver's interrupt
183 : : * handler. It will schedule the alert work, in turn calling the
184 : : * corresponding I2C device driver's alert function.
185 : : *
186 : : * It is assumed that ara is a valid i2c client previously returned by
187 : : * i2c_setup_smbus_alert().
188 : : */
189 : 0 : int i2c_handle_smbus_alert(struct i2c_client *ara)
190 : : {
191 : 0 : struct i2c_smbus_alert *alert = i2c_get_clientdata(ara);
192 : :
193 : 0 : return schedule_work(&alert->alert);
194 : : }
195 : : EXPORT_SYMBOL_GPL(i2c_handle_smbus_alert);
196 : :
197 : 3 : module_i2c_driver(smbalert_driver);
198 : :
199 : : MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>");
200 : : MODULE_DESCRIPTION("SMBus protocol extensions support");
201 : : MODULE_LICENSE("GPL");
|