Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Alexander Usyskin and committed by
Greg Kroah-Hartman
a2eb0fc0 4a8efd4a

+50 -19
+50 -17
drivers/misc/mei/hw-me.c
··· 246 246 return hw->pg_state; 247 247 } 248 248 249 + static 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 + */ 261 + static 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 + */ 273 + static 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 280 * mei_me_intr_clear - clear and stop interrupts 251 281 * ··· 285 255 { 286 256 u32 hcsr = mei_hcsr_read(dev); 287 257 288 - if (hcsr & H_CSR_IS_MASK) 289 - mei_hcsr_write(dev, hcsr); 258 + me_intr_clear(dev, hcsr); 290 259 } 291 260 /** 292 261 * mei_me_intr_enable - enables mei device interrupts ··· 309 280 { 310 281 u32 hcsr = mei_hcsr_read(dev); 311 282 312 - hcsr &= ~H_CSR_IE_MASK; 313 - mei_hcsr_set(dev, hcsr); 283 + me_intr_disable(dev, hcsr); 314 284 } 315 285 316 286 /** ··· 996 968 * mei_me_d0i3_intr - perform d0i3 processing in interrupt thread handler 997 969 * 998 970 * @dev: the device structure 971 + * @intr_source: interrupt source 999 972 */ 1000 - static void mei_me_d0i3_intr(struct mei_device *dev) 973 + static void mei_me_d0i3_intr(struct mei_device *dev, u32 intr_source) 1001 974 { 1002 975 struct mei_me_hw *hw = to_me_hw(dev); 1003 976 1004 977 if (dev->pg_event == MEI_PG_EVENT_INTR_WAIT && 1005 - (hw->intr_source & H_D0I3C_IS)) { 978 + (intr_source & H_D0I3C_IS)) { 1006 979 dev->pg_event = MEI_PG_EVENT_INTR_RECEIVED; 1007 980 if (hw->pg_state == MEI_PG_ON) { 1008 981 hw->pg_state = MEI_PG_OFF; ··· 1022 993 wake_up(&dev->wait_pg); 1023 994 } 1024 995 1025 - if (hw->pg_state == MEI_PG_ON && (hw->intr_source & H_IS)) { 996 + if (hw->pg_state == MEI_PG_ON && (intr_source & H_IS)) { 1026 997 /* 1027 998 * HW sent some data and we are in D0i3, so 1028 999 * we got here because of HW initiated exit from D0i3. ··· 1037 1008 * mei_me_pg_intr - perform pg processing in interrupt thread handler 1038 1009 * 1039 1010 * @dev: the device structure 1011 + * @intr_source: interrupt source 1040 1012 */ 1041 - static void mei_me_pg_intr(struct mei_device *dev) 1013 + static void mei_me_pg_intr(struct mei_device *dev, u32 intr_source) 1042 1014 { 1043 1015 struct mei_me_hw *hw = to_me_hw(dev); 1044 1016 1045 1017 if (hw->d0i3_supported) 1046 - mei_me_d0i3_intr(dev); 1018 + mei_me_d0i3_intr(dev, intr_source); 1047 1019 else 1048 1020 mei_me_pg_legacy_intr(dev); 1049 1021 } ··· 1163 1133 irqreturn_t mei_me_irq_quick_handler(int irq, void *dev_id) 1164 1134 { 1165 1135 struct mei_device *dev = (struct mei_device *)dev_id; 1166 - struct mei_me_hw *hw = to_me_hw(dev); 1167 1136 u32 hcsr; 1168 1137 1169 1138 hcsr = mei_hcsr_read(dev); 1170 - if (!(hcsr & H_CSR_IS_MASK)) 1139 + if (!me_intr_src(hcsr)) 1171 1140 return IRQ_NONE; 1172 1141 1173 - hw->intr_source = hcsr & H_CSR_IS_MASK; 1174 - dev_dbg(dev->dev, "interrupt source 0x%08X.\n", hw->intr_source); 1142 + dev_dbg(dev->dev, "interrupt source 0x%08X\n", me_intr_src(hcsr)); 1175 1143 1176 - /* clear H_IS and H_D0I3C_IS bits in H_CSR to clear the interrupts */ 1177 - mei_hcsr_write(dev, hcsr); 1178 - 1144 + /* disable interrupts on device */ 1145 + me_intr_disable(dev, hcsr); 1179 1146 return IRQ_WAKE_THREAD; 1180 1147 } 1181 1148 ··· 1191 1164 struct mei_device *dev = (struct mei_device *) dev_id; 1192 1165 struct mei_cl_cb complete_list; 1193 1166 s32 slots; 1167 + u32 hcsr; 1194 1168 int rets = 0; 1195 1169 1196 1170 dev_dbg(dev->dev, "function called after ISR to handle the interrupt processing.\n"); 1197 1171 /* initialize our complete list */ 1198 1172 mutex_lock(&dev->device_lock); 1173 + 1174 + hcsr = mei_hcsr_read(dev); 1175 + me_intr_clear(dev, hcsr); 1176 + 1199 1177 mei_io_list_init(&complete_list); 1200 1178 1201 1179 /* check if ME wants a reset */ ··· 1210 1178 goto end; 1211 1179 } 1212 1180 1213 - mei_me_pg_intr(dev); 1181 + mei_me_pg_intr(dev, me_intr_src(hcsr)); 1214 1182 1215 1183 /* check if we need to start the dev */ 1216 1184 if (!mei_host_is_ready(dev)) { ··· 1260 1228 1261 1229 end: 1262 1230 dev_dbg(dev->dev, "interrupt thread end ret = %d\n", rets); 1231 + mei_me_intr_enable(dev); 1263 1232 mutex_unlock(&dev->device_lock); 1264 1233 return IRQ_HANDLED; 1265 1234 }
-2
drivers/misc/mei/hw-me.h
··· 51 51 * 52 52 * @cfg: per device generation config and ops 53 53 * @mem_addr: io memory address 54 - * @intr_source: interrupt source 55 54 * @pg_state: power gating state 56 55 * @d0i3_supported: di03 support 57 56 */ 58 57 struct mei_me_hw { 59 58 const struct mei_cfg *cfg; 60 59 void __iomem *mem_addr; 61 - u32 intr_source; 62 60 enum mei_pg_state pg_state; 63 61 bool d0i3_supported; 64 62 };