aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexander Usyskin <alexander.usyskin@intel.com>2016-12-04 08:22:59 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2016-12-06 05:03:22 -0500
commita2eb0fc07f4d4f3b2eb2b1b1b72de738d4b9e003 (patch)
tree05772491468eb98273ed6dac8cf99e22a2745cb8
parent4a8efd4a1a9593a11c808da94e6609f6d4ee7276 (diff)
mei: fix the back to back interrupt handling
Since the newer HW sports two interrupts causes we cannot just simply acknowledge the interrupts directly in the quick handler and store the cause in the member variable, as the cause will be overridden upon next interrupt while the interrupt thread was not yet scheduled handling the previous interrupt. The simple fix is to disable interrupts in quick handler and acknowledge and enabled them in the interrupt thread. Signed-off-by: Alexander Usyskin <alexander.usyskin@intel.com> Signed-off-by: Tomas Winkler <tomas.winkler@intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/misc/mei/hw-me.c67
-rw-r--r--drivers/misc/mei/hw-me.h2
2 files changed, 50 insertions, 19 deletions
diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c
index e2b56e8cf745..a05375a3338a 100644
--- a/drivers/misc/mei/hw-me.c
+++ b/drivers/misc/mei/hw-me.c
@@ -246,6 +246,36 @@ static inline enum mei_pg_state mei_me_pg_state(struct mei_device *dev)
246 return hw->pg_state; 246 return hw->pg_state;
247} 247}
248 248
249static inline u32 me_intr_src(u32 hcsr)
250{
251 return hcsr & H_CSR_IS_MASK;
252}
253
254/**
255 * me_intr_disable - disables mei device interrupts
256 * using supplied hcsr register value.
257 *
258 * @dev: the device structure
259 * @hcsr: supplied hcsr register value
260 */
261static inline void me_intr_disable(struct mei_device *dev, u32 hcsr)
262{
263 hcsr &= ~H_CSR_IE_MASK;
264 mei_hcsr_set(dev, hcsr);
265}
266
267/**
268 * mei_me_intr_clear - clear and stop interrupts
269 *
270 * @dev: the device structure
271 * @hcsr: supplied hcsr register value
272 */
273static inline void me_intr_clear(struct mei_device *dev, u32 hcsr)
274{
275 if (me_intr_src(hcsr))
276 mei_hcsr_write(dev, hcsr);
277}
278
249/** 279/**
250 * mei_me_intr_clear - clear and stop interrupts 280 * mei_me_intr_clear - clear and stop interrupts
251 * 281 *
@@ -255,8 +285,7 @@ static void mei_me_intr_clear(struct mei_device *dev)
255{ 285{
256 u32 hcsr = mei_hcsr_read(dev); 286 u32 hcsr = mei_hcsr_read(dev);
257 287
258 if (hcsr & H_CSR_IS_MASK) 288 me_intr_clear(dev, hcsr);
259 mei_hcsr_write(dev, hcsr);
260} 289}
261/** 290/**
262 * mei_me_intr_enable - enables mei device interrupts 291 * mei_me_intr_enable - enables mei device interrupts
@@ -280,8 +309,7 @@ static void mei_me_intr_disable(struct mei_device *dev)
280{ 309{
281 u32 hcsr = mei_hcsr_read(dev); 310 u32 hcsr = mei_hcsr_read(dev);
282 311
283 hcsr &= ~H_CSR_IE_MASK; 312 me_intr_disable(dev, hcsr);
284 mei_hcsr_set(dev, hcsr);
285} 313}
286 314
287/** 315/**
@@ -968,13 +996,14 @@ static void mei_me_pg_legacy_intr(struct mei_device *dev)
968 * mei_me_d0i3_intr - perform d0i3 processing in interrupt thread handler 996 * mei_me_d0i3_intr - perform d0i3 processing in interrupt thread handler
969 * 997 *
970 * @dev: the device structure 998 * @dev: the device structure
999 * @intr_source: interrupt source
971 */ 1000 */
972static void mei_me_d0i3_intr(struct mei_device *dev) 1001static void mei_me_d0i3_intr(struct mei_device *dev, u32 intr_source)
973{ 1002{
974 struct mei_me_hw *hw = to_me_hw(dev); 1003 struct mei_me_hw *hw = to_me_hw(dev);
975 1004
976 if (dev->pg_event == MEI_PG_EVENT_INTR_WAIT && 1005 if (dev->pg_event == MEI_PG_EVENT_INTR_WAIT &&
977 (hw->intr_source & H_D0I3C_IS)) { 1006 (intr_source & H_D0I3C_IS)) {
978 dev->pg_event = MEI_PG_EVENT_INTR_RECEIVED; 1007 dev->pg_event = MEI_PG_EVENT_INTR_RECEIVED;
979 if (hw->pg_state == MEI_PG_ON) { 1008 if (hw->pg_state == MEI_PG_ON) {
980 hw->pg_state = MEI_PG_OFF; 1009 hw->pg_state = MEI_PG_OFF;
@@ -993,7 +1022,7 @@ static void mei_me_d0i3_intr(struct mei_device *dev)
993 wake_up(&dev->wait_pg); 1022 wake_up(&dev->wait_pg);
994 } 1023 }
995 1024
996 if (hw->pg_state == MEI_PG_ON && (hw->intr_source & H_IS)) { 1025 if (hw->pg_state == MEI_PG_ON && (intr_source & H_IS)) {
997 /* 1026 /*
998 * HW sent some data and we are in D0i3, so 1027 * HW sent some data and we are in D0i3, so
999 * we got here because of HW initiated exit from D0i3. 1028 * we got here because of HW initiated exit from D0i3.
@@ -1008,13 +1037,14 @@ static void mei_me_d0i3_intr(struct mei_device *dev)
1008 * mei_me_pg_intr - perform pg processing in interrupt thread handler 1037 * mei_me_pg_intr - perform pg processing in interrupt thread handler
1009 * 1038 *
1010 * @dev: the device structure 1039 * @dev: the device structure
1040 * @intr_source: interrupt source
1011 */ 1041 */
1012static void mei_me_pg_intr(struct mei_device *dev) 1042static void mei_me_pg_intr(struct mei_device *dev, u32 intr_source)
1013{ 1043{
1014 struct mei_me_hw *hw = to_me_hw(dev); 1044 struct mei_me_hw *hw = to_me_hw(dev);
1015 1045
1016 if (hw->d0i3_supported) 1046 if (hw->d0i3_supported)
1017 mei_me_d0i3_intr(dev); 1047 mei_me_d0i3_intr(dev, intr_source);
1018 else 1048 else
1019 mei_me_pg_legacy_intr(dev); 1049 mei_me_pg_legacy_intr(dev);
1020} 1050}
@@ -1133,19 +1163,16 @@ static int mei_me_hw_reset(struct mei_device *dev, bool intr_enable)
1133irqreturn_t mei_me_irq_quick_handler(int irq, void *dev_id) 1163irqreturn_t mei_me_irq_quick_handler(int irq, void *dev_id)
1134{ 1164{
1135 struct mei_device *dev = (struct mei_device *)dev_id; 1165 struct mei_device *dev = (struct mei_device *)dev_id;
1136 struct mei_me_hw *hw = to_me_hw(dev);
1137 u32 hcsr; 1166 u32 hcsr;
1138 1167
1139 hcsr = mei_hcsr_read(dev); 1168 hcsr = mei_hcsr_read(dev);
1140 if (!(hcsr & H_CSR_IS_MASK)) 1169 if (!me_intr_src(hcsr))
1141 return IRQ_NONE; 1170 return IRQ_NONE;
1142 1171
1143 hw->intr_source = hcsr & H_CSR_IS_MASK; 1172 dev_dbg(dev->dev, "interrupt source 0x%08X\n", me_intr_src(hcsr));
1144 dev_dbg(dev->dev, "interrupt source 0x%08X.\n", hw->intr_source);
1145
1146 /* clear H_IS and H_D0I3C_IS bits in H_CSR to clear the interrupts */
1147 mei_hcsr_write(dev, hcsr);
1148 1173
1174 /* disable interrupts on device */
1175 me_intr_disable(dev, hcsr);
1149 return IRQ_WAKE_THREAD; 1176 return IRQ_WAKE_THREAD;
1150} 1177}
1151 1178
@@ -1164,11 +1191,16 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id)
1164 struct mei_device *dev = (struct mei_device *) dev_id; 1191 struct mei_device *dev = (struct mei_device *) dev_id;
1165 struct mei_cl_cb complete_list; 1192 struct mei_cl_cb complete_list;
1166 s32 slots; 1193 s32 slots;
1194 u32 hcsr;
1167 int rets = 0; 1195 int rets = 0;
1168 1196
1169 dev_dbg(dev->dev, "function called after ISR to handle the interrupt processing.\n"); 1197 dev_dbg(dev->dev, "function called after ISR to handle the interrupt processing.\n");
1170 /* initialize our complete list */ 1198 /* initialize our complete list */
1171 mutex_lock(&dev->device_lock); 1199 mutex_lock(&dev->device_lock);
1200
1201 hcsr = mei_hcsr_read(dev);
1202 me_intr_clear(dev, hcsr);
1203
1172 mei_io_list_init(&complete_list); 1204 mei_io_list_init(&complete_list);
1173 1205
1174 /* check if ME wants a reset */ 1206 /* check if ME wants a reset */
@@ -1178,7 +1210,7 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id)
1178 goto end; 1210 goto end;
1179 } 1211 }
1180 1212
1181 mei_me_pg_intr(dev); 1213 mei_me_pg_intr(dev, me_intr_src(hcsr));
1182 1214
1183 /* check if we need to start the dev */ 1215 /* check if we need to start the dev */
1184 if (!mei_host_is_ready(dev)) { 1216 if (!mei_host_is_ready(dev)) {
@@ -1228,6 +1260,7 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id)
1228 1260
1229end: 1261end:
1230 dev_dbg(dev->dev, "interrupt thread end ret = %d\n", rets); 1262 dev_dbg(dev->dev, "interrupt thread end ret = %d\n", rets);
1263 mei_me_intr_enable(dev);
1231 mutex_unlock(&dev->device_lock); 1264 mutex_unlock(&dev->device_lock);
1232 return IRQ_HANDLED; 1265 return IRQ_HANDLED;
1233} 1266}
diff --git a/drivers/misc/mei/hw-me.h b/drivers/misc/mei/hw-me.h
index 2ee14dc1b2ea..cf64847a35b9 100644
--- a/drivers/misc/mei/hw-me.h
+++ b/drivers/misc/mei/hw-me.h
@@ -51,14 +51,12 @@ struct mei_cfg {
51 * 51 *
52 * @cfg: per device generation config and ops 52 * @cfg: per device generation config and ops
53 * @mem_addr: io memory address 53 * @mem_addr: io memory address
54 * @intr_source: interrupt source
55 * @pg_state: power gating state 54 * @pg_state: power gating state
56 * @d0i3_supported: di03 support 55 * @d0i3_supported: di03 support
57 */ 56 */
58struct mei_me_hw { 57struct mei_me_hw {
59 const struct mei_cfg *cfg; 58 const struct mei_cfg *cfg;
60 void __iomem *mem_addr; 59 void __iomem *mem_addr;
61 u32 intr_source;
62 enum mei_pg_state pg_state; 60 enum mei_pg_state pg_state;
63 bool d0i3_supported; 61 bool d0i3_supported;
64}; 62};