aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPhil Reid <preid@electromag.com.au>2017-08-24 05:31:01 -0400
committerWolfram Sang <wsa@the-dreams.de>2017-10-28 17:42:26 -0400
commit9b9f2b8bc2ac98d91da714660c53d1cdac999e09 (patch)
tree6d04e6cf60b2b730af973daca623fa524df939b1
parentbb176f67090ca54869fc1262c913aa69d2ede070 (diff)
i2c: i2c-smbus: Use threaded irq for smbalert
Prior to this commit the smbalert_irq was handling in the hard irq context. This change switch to using a thread irq which avoids the need for the work thread. Using threaded irq also removes the need for the edge_triggered flag as the enabling / disabling of the hard irq for level triggered interrupts will be handled by the irq core. Without this change have an irq connected to something like an i2c gpio resulted in a null ptr deferences. Specifically handle_nested_irq calls the threaded irq handler. There are currently 3 in tree drivers affected by this change. i2c-parport driver calls i2c_handle_smbus_alert in a hard irq context. This driver use edge trigger interrupts which skip the enable / disable calls. But it still need to handle the smbus transaction on a thread. So the work thread is kept for this driver. i2c-parport-light & i2c-thunderx-pcidrv provide the irq number in the setup which will result in the thread irq being used. i2c-parport-light is edge trigger so the enable / disable call was skipped as well. i2c-thunderx-pcidrv is getting the edge / level trigger setting from of data and was setting the flag as required. However the irq core should handle this automatically. Signed-off-by: Phil Reid <preid@electromag.com.au> Reviewed-by: Benjamin Tissoires <benjamin.tissoires@redhat.com> Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
-rw-r--r--drivers/i2c/busses/i2c-parport-light.c1
-rw-r--r--drivers/i2c/busses/i2c-parport.c1
-rw-r--r--drivers/i2c/busses/i2c-thunderx-pcidrv.c6
-rw-r--r--drivers/i2c/i2c-smbus.c41
-rw-r--r--include/linux/i2c-smbus.h1
5 files changed, 17 insertions, 33 deletions
diff --git a/drivers/i2c/busses/i2c-parport-light.c b/drivers/i2c/busses/i2c-parport-light.c
index faa8fb8f2b8f..fa41ff799533 100644
--- a/drivers/i2c/busses/i2c-parport-light.c
+++ b/drivers/i2c/busses/i2c-parport-light.c
@@ -123,7 +123,6 @@ static struct i2c_adapter parport_adapter = {
123 123
124/* SMBus alert support */ 124/* SMBus alert support */
125static struct i2c_smbus_alert_setup alert_data = { 125static struct i2c_smbus_alert_setup alert_data = {
126 .alert_edge_triggered = 1,
127}; 126};
128static struct i2c_client *ara; 127static struct i2c_client *ara;
129static struct lineop parport_ctrl_irq = { 128static struct lineop parport_ctrl_irq = {
diff --git a/drivers/i2c/busses/i2c-parport.c b/drivers/i2c/busses/i2c-parport.c
index a8e54df4aed6..319209a07353 100644
--- a/drivers/i2c/busses/i2c-parport.c
+++ b/drivers/i2c/busses/i2c-parport.c
@@ -237,7 +237,6 @@ static void i2c_parport_attach(struct parport *port)
237 237
238 /* Setup SMBus alert if supported */ 238 /* Setup SMBus alert if supported */
239 if (adapter_parm[type].smbus_alert) { 239 if (adapter_parm[type].smbus_alert) {
240 adapter->alert_data.alert_edge_triggered = 1;
241 adapter->ara = i2c_setup_smbus_alert(&adapter->adapter, 240 adapter->ara = i2c_setup_smbus_alert(&adapter->adapter,
242 &adapter->alert_data); 241 &adapter->alert_data);
243 if (adapter->ara) 242 if (adapter->ara)
diff --git a/drivers/i2c/busses/i2c-thunderx-pcidrv.c b/drivers/i2c/busses/i2c-thunderx-pcidrv.c
index df0976f4432a..1a7cad874756 100644
--- a/drivers/i2c/busses/i2c-thunderx-pcidrv.c
+++ b/drivers/i2c/busses/i2c-thunderx-pcidrv.c
@@ -118,8 +118,6 @@ static void thunder_i2c_clock_disable(struct device *dev, struct clk *clk)
118static int thunder_i2c_smbus_setup_of(struct octeon_i2c *i2c, 118static int thunder_i2c_smbus_setup_of(struct octeon_i2c *i2c,
119 struct device_node *node) 119 struct device_node *node)
120{ 120{
121 u32 type;
122
123 if (!node) 121 if (!node)
124 return -EINVAL; 122 return -EINVAL;
125 123
@@ -127,10 +125,6 @@ static int thunder_i2c_smbus_setup_of(struct octeon_i2c *i2c,
127 if (!i2c->alert_data.irq) 125 if (!i2c->alert_data.irq)
128 return -EINVAL; 126 return -EINVAL;
129 127
130 type = irqd_get_trigger_type(irq_get_irq_data(i2c->alert_data.irq));
131 i2c->alert_data.alert_edge_triggered =
132 (type & IRQ_TYPE_LEVEL_MASK) ? 1 : 0;
133
134 i2c->ara = i2c_setup_smbus_alert(&i2c->adap, &i2c->alert_data); 128 i2c->ara = i2c_setup_smbus_alert(&i2c->adap, &i2c->alert_data);
135 if (!i2c->ara) 129 if (!i2c->ara)
136 return -ENODEV; 130 return -ENODEV;
diff --git a/drivers/i2c/i2c-smbus.c b/drivers/i2c/i2c-smbus.c
index f9271c713d20..d4af2701ac6e 100644
--- a/drivers/i2c/i2c-smbus.c
+++ b/drivers/i2c/i2c-smbus.c
@@ -25,8 +25,6 @@
25#include <linux/workqueue.h> 25#include <linux/workqueue.h>
26 26
27struct i2c_smbus_alert { 27struct i2c_smbus_alert {
28 unsigned int alert_edge_triggered:1;
29 int irq;
30 struct work_struct alert; 28 struct work_struct alert;
31 struct i2c_client *ara; /* Alert response address */ 29 struct i2c_client *ara; /* Alert response address */
32}; 30};
@@ -72,13 +70,12 @@ static int smbus_do_alert(struct device *dev, void *addrp)
72 * The alert IRQ handler needs to hand work off to a task which can issue 70 * The alert IRQ handler needs to hand work off to a task which can issue
73 * SMBus calls, because those sleeping calls can't be made in IRQ context. 71 * SMBus calls, because those sleeping calls can't be made in IRQ context.
74 */ 72 */
75static void smbus_alert(struct work_struct *work) 73static irqreturn_t smbus_alert(int irq, void *d)
76{ 74{
77 struct i2c_smbus_alert *alert; 75 struct i2c_smbus_alert *alert = d;
78 struct i2c_client *ara; 76 struct i2c_client *ara;
79 unsigned short prev_addr = 0; /* Not a valid address */ 77 unsigned short prev_addr = 0; /* Not a valid address */
80 78
81 alert = container_of(work, struct i2c_smbus_alert, alert);
82 ara = alert->ara; 79 ara = alert->ara;
83 80
84 for (;;) { 81 for (;;) {
@@ -115,21 +112,17 @@ static void smbus_alert(struct work_struct *work)
115 prev_addr = data.addr; 112 prev_addr = data.addr;
116 } 113 }
117 114
118 /* We handled all alerts; re-enable level-triggered IRQs */ 115 return IRQ_HANDLED;
119 if (!alert->alert_edge_triggered)
120 enable_irq(alert->irq);
121} 116}
122 117
123static irqreturn_t smbalert_irq(int irq, void *d) 118static void smbalert_work(struct work_struct *work)
124{ 119{
125 struct i2c_smbus_alert *alert = d; 120 struct i2c_smbus_alert *alert;
121
122 alert = container_of(work, struct i2c_smbus_alert, alert);
126 123
127 /* Disable level-triggered IRQs until we handle them */ 124 smbus_alert(0, alert);
128 if (!alert->alert_edge_triggered)
129 disable_irq_nosync(irq);
130 125
131 schedule_work(&alert->alert);
132 return IRQ_HANDLED;
133} 126}
134 127
135/* Setup SMBALERT# infrastructure */ 128/* Setup SMBALERT# infrastructure */
@@ -139,28 +132,28 @@ static int smbalert_probe(struct i2c_client *ara,
139 struct i2c_smbus_alert_setup *setup = dev_get_platdata(&ara->dev); 132 struct i2c_smbus_alert_setup *setup = dev_get_platdata(&ara->dev);
140 struct i2c_smbus_alert *alert; 133 struct i2c_smbus_alert *alert;
141 struct i2c_adapter *adapter = ara->adapter; 134 struct i2c_adapter *adapter = ara->adapter;
142 int res; 135 int res, irq;
143 136
144 alert = devm_kzalloc(&ara->dev, sizeof(struct i2c_smbus_alert), 137 alert = devm_kzalloc(&ara->dev, sizeof(struct i2c_smbus_alert),
145 GFP_KERNEL); 138 GFP_KERNEL);
146 if (!alert) 139 if (!alert)
147 return -ENOMEM; 140 return -ENOMEM;
148 141
149 alert->alert_edge_triggered = setup->alert_edge_triggered; 142 irq = setup->irq;
150 alert->irq = setup->irq; 143 INIT_WORK(&alert->alert, smbalert_work);
151 INIT_WORK(&alert->alert, smbus_alert);
152 alert->ara = ara; 144 alert->ara = ara;
153 145
154 if (setup->irq > 0) { 146 if (irq > 0) {
155 res = devm_request_irq(&ara->dev, setup->irq, smbalert_irq, 147 res = devm_request_threaded_irq(&ara->dev, irq,
156 0, "smbus_alert", alert); 148 NULL, smbus_alert,
149 IRQF_SHARED | IRQF_ONESHOT,
150 "smbus_alert", alert);
157 if (res) 151 if (res)
158 return res; 152 return res;
159 } 153 }
160 154
161 i2c_set_clientdata(ara, alert); 155 i2c_set_clientdata(ara, alert);
162 dev_info(&adapter->dev, "supports SMBALERT#, %s trigger\n", 156 dev_info(&adapter->dev, "supports SMBALERT#\n");
163 setup->alert_edge_triggered ? "edge" : "level");
164 157
165 return 0; 158 return 0;
166} 159}
diff --git a/include/linux/i2c-smbus.h b/include/linux/i2c-smbus.h
index a1385023a29b..19efbd14e812 100644
--- a/include/linux/i2c-smbus.h
+++ b/include/linux/i2c-smbus.h
@@ -42,7 +42,6 @@
42 * properly set. 42 * properly set.
43 */ 43 */
44struct i2c_smbus_alert_setup { 44struct i2c_smbus_alert_setup {
45 unsigned int alert_edge_triggered:1;
46 int irq; 45 int irq;
47}; 46};
48 47