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

misc: mic: bug fix for interrupt acknowledgement in MSI/INTx case.

The interrupt handler (mic_interrupt), called in the MSI/INTx mode,
writes to the interrupt sources register to acknowledge the
interrupt and then calls the corresponding callback handlers to handle
the same. These callback handlers acknowledge the interrupts again
leading to missed interrupts. This patch fixes the issue by removing
the interrupt acknowlegment code from the callback handlers.

Reviewed-by: Sudeep Dutt <sudeep.dutt@intel.com>
Reviewed-by: Nikhil Rao <nikhil.rao@intel.com>
Reviewed-by: Siva Krishna Kumar Reddy Yerramreddy <siva.krishna.kumar.reddy.yerramreddy@intel.com>
Signed-off-by: Dasaratharaman Chandramouli <dasaratharaman.chandramouli@intel.com>
Cc: stable <stable@vger.kernel.org> # 3.13
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Dasaratharaman Chandramouli and committed by
Greg Kroah-Hartman
df5e4e8b d6a48452

+25 -18
+3
drivers/misc/mic/host/mic_device.h
··· 134 134 * @send_intr: Send an interrupt for a particular doorbell on the card. 135 135 * @ack_interrupt: Hardware specific operations to ack the h/w on 136 136 * receipt of an interrupt. 137 + * @intr_workarounds: Hardware specific workarounds needed after 138 + * handling an interrupt. 137 139 * @reset: Reset the remote processor. 138 140 * @reset_fw_ready: Reset firmware ready field. 139 141 * @is_fw_ready: Check if firmware is ready for OS download. ··· 151 149 void (*write_spad)(struct mic_device *mdev, unsigned int idx, u32 val); 152 150 void (*send_intr)(struct mic_device *mdev, int doorbell); 153 151 u32 (*ack_interrupt)(struct mic_device *mdev); 152 + void (*intr_workarounds)(struct mic_device *mdev); 154 153 void (*reset)(struct mic_device *mdev); 155 154 void (*reset_fw_ready)(struct mic_device *mdev); 156 155 bool (*is_fw_ready)(struct mic_device *mdev);
+1 -1
drivers/misc/mic/host/mic_main.c
··· 115 115 struct mic_device *mdev = data; 116 116 struct mic_bootparam *bootparam = mdev->dp; 117 117 118 - mdev->ops->ack_interrupt(mdev); 118 + mdev->ops->intr_workarounds(mdev); 119 119 120 120 switch (bootparam->shutdown_status) { 121 121 case MIC_HALTED:
+1 -1
drivers/misc/mic/host/mic_virtio.c
··· 369 369 struct mic_vdev *mvdev = data; 370 370 struct mic_device *mdev = mvdev->mdev; 371 371 372 - mdev->ops->ack_interrupt(mdev); 372 + mdev->ops->intr_workarounds(mdev); 373 373 schedule_work(&mvdev->virtio_bh_work); 374 374 return IRQ_HANDLED; 375 375 }
+20 -16
drivers/misc/mic/host/mic_x100.c
··· 174 174 } 175 175 176 176 /** 177 - * mic_ack_interrupt - Device specific interrupt handling. 178 - * @mdev: pointer to mic_device instance 177 + * mic_x100_ack_interrupt - Read the interrupt sources register and 178 + * clear it. This function will be called in the MSI/INTx case. 179 + * @mdev: Pointer to mic_device instance. 179 180 * 180 - * Returns: bitmask of doorbell events triggered. 181 + * Returns: bitmask of interrupt sources triggered. 181 182 */ 182 183 static u32 mic_x100_ack_interrupt(struct mic_device *mdev) 183 184 { 184 - u32 reg = 0; 185 - struct mic_mw *mw = &mdev->mmio; 186 185 u32 sicr0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SICR0; 186 + u32 reg = mic_mmio_read(&mdev->mmio, sicr0); 187 + mic_mmio_write(&mdev->mmio, reg, sicr0); 188 + return reg; 189 + } 190 + 191 + /** 192 + * mic_x100_intr_workarounds - These hardware specific workarounds are 193 + * to be invoked everytime an interrupt is handled. 194 + * @mdev: Pointer to mic_device instance. 195 + * 196 + * Returns: none 197 + */ 198 + static void mic_x100_intr_workarounds(struct mic_device *mdev) 199 + { 200 + struct mic_mw *mw = &mdev->mmio; 187 201 188 202 /* Clear pending bit array. */ 189 203 if (MIC_A0_STEP == mdev->stepping) 190 204 mic_mmio_write(mw, 1, MIC_X100_SBOX_BASE_ADDRESS + 191 205 MIC_X100_SBOX_MSIXPBACR); 192 206 193 - if (mdev->irq_info.num_vectors <= 1) { 194 - reg = mic_mmio_read(mw, sicr0); 195 - 196 - if (unlikely(!reg)) 197 - goto done; 198 - 199 - mic_mmio_write(mw, reg, sicr0); 200 - } 201 - 202 207 if (mdev->stepping >= MIC_B0_STEP) 203 208 mdev->intr_ops->enable_interrupts(mdev); 204 - done: 205 - return reg; 206 209 } 207 210 208 211 /** ··· 556 553 .write_spad = mic_x100_write_spad, 557 554 .send_intr = mic_x100_send_intr, 558 555 .ack_interrupt = mic_x100_ack_interrupt, 556 + .intr_workarounds = mic_x100_intr_workarounds, 559 557 .reset = mic_x100_hw_reset, 560 558 .reset_fw_ready = mic_x100_reset_fw_ready, 561 559 .is_fw_ready = mic_x100_is_fw_ready,