/** * mic_ack_interrupt - Device specific interrupt handling. * @mdev: pointer to mic_device instance * * Returns: bitmask of doorbell events triggered. */ static u32 mic_x100_ack_interrupt(struct mic_device *mdev) { u32 reg = 0; struct mic_mw *mw = &mdev->mmio; u32 sicr0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SICR0; /* Clear pending bit array. */ if (MIC_A0_STEP == mdev->stepping) mic_mmio_write(mw, 1, MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_MSIXPBACR); if (mdev->irq_info.num_vectors <= 1) { reg = mic_mmio_read(mw, sicr0); if (unlikely(!reg)) goto done; mic_mmio_write(mw, reg, sicr0); } if (mdev->stepping >= MIC_B0_STEP) mdev->intr_ops->enable_interrupts(mdev); done: return reg; }
/** * mic_x100_ack_interrupt - Read the interrupt sources register and * clear it. This function will be called in the MSI/INTx case. * @mdev: Pointer to mic_device instance. * * Returns: bitmask of interrupt sources triggered. */ static u32 mic_x100_ack_interrupt(struct mic_device *mdev) { u32 sicr0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SICR0; u32 reg = mic_mmio_read(&mdev->mmio, sicr0); mic_mmio_write(&mdev->mmio, reg, sicr0); return reg; }
/** * mic_x100_write_spad - write to the scratchpad register * @mdev: pointer to mic_device instance * @idx: index to the scratchpad register, 0 based * @val: the data value to put into the register * * This function allows writing of a 32bit value to the indexed scratchpad * register. * * RETURNS: none. */ static void mic_x100_write_spad(struct mic_device *mdev, unsigned int idx, u32 val) { dev_dbg(mdev->sdev->parent, "Writing 0x%x to scratch pad index %d\n", val, idx); mic_mmio_write(&mdev->mmio, val, MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SPAD0 + idx * 4); }
/** * mic_x100_send_rdmasr_intr - Send an RDMASR interrupt to MIC. * @mdev: pointer to mic_device instance */ static void mic_x100_send_rdmasr_intr(struct mic_device *mdev, int doorbell) { int rdmasr_offset = MIC_X100_SBOX_RDMASR0 + (doorbell << 2); /* Ensure that the interrupt is ordered w.r.t. previous stores. */ wmb(); mic_mmio_write(&mdev->mmio, 0, MIC_X100_SBOX_BASE_ADDRESS + rdmasr_offset); }
/** * mic_x100_disable_interrupts - Disable interrupts. * @mdev: pointer to mic_device instance */ static void mic_x100_disable_interrupts(struct mic_device *mdev) { u32 reg; struct mic_mw *mw = &mdev->mmio; u32 sice0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SICE0; u32 siac0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SIAC0; u32 sicc0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SICC0; reg = mic_mmio_read(mw, sice0); mic_mmio_write(mw, reg, sicc0); if (mdev->irq_info.num_vectors > 1) { reg = mic_mmio_read(mw, siac0); reg &= ~(MIC_X100_SBOX_DBR_BITS(0xf) | MIC_X100_SBOX_DMA_BITS(0xff)); mic_mmio_write(mw, reg, siac0); } }
/** * mic_x100_send_firmware_intr - Send an interrupt to the firmware on MIC. * @mdev: pointer to mic_device instance */ static void mic_x100_send_firmware_intr(struct mic_device *mdev) { u32 apicicr_low; u64 apic_icr_offset = MIC_X100_SBOX_APICICR7; int vector = MIC_X100_BSP_INTERRUPT_VECTOR; struct mic_mw *mw = &mdev->mmio; /* * For MIC we need to make sure we "hit" * the send_icr bit (13). */ apicicr_low = (vector | (1 << 13)); mic_mmio_write(mw, mic_x100_get_apic_id(mdev), MIC_X100_SBOX_BASE_ADDRESS + apic_icr_offset + 4); /* Ensure that the interrupt is ordered w.r.t. previous stores. */ wmb(); mic_mmio_write(mw, apicicr_low, MIC_X100_SBOX_BASE_ADDRESS + apic_icr_offset); }
/** * mic_x100_intr_workarounds - These hardware specific workarounds are * to be invoked everytime an interrupt is handled. * @mdev: Pointer to mic_device instance. * * Returns: none */ static void mic_x100_intr_workarounds(struct mic_device *mdev) { struct mic_mw *mw = &mdev->mmio; /* Clear pending bit array. */ if (MIC_A0_STEP == mdev->stepping) mic_mmio_write(mw, 1, MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_MSIXPBACR); if (mdev->stepping >= MIC_B0_STEP) mdev->intr_ops->enable_interrupts(mdev); }
static void mic_x100_send_rdmasr_intr(struct mic_mw *mw, int doorbell) { int rdmasr_offset = MIC_X100_SBOX_RDMASR0 + (doorbell << 2); /* * Ensure that the interrupt is ordered w.r.t. previous stores * to main memory. Fence instructions are not implemented in X100 * since execution is in order but a compiler barrier is still * required. */ wmb(); mic_mmio_write(mw, 0, MIC_X100_SBOX_BASE_ADDRESS + rdmasr_offset); }
/** * __mic_send_intr - Send interrupt to Host. * @mdev: pointer to mic_device instance * @doorbell: Doorbell number. */ void mic_send_intr(struct mic_device *mdev, int doorbell) { struct mic_mw *mw = &mdev->mmio; if (doorbell > MIC_X100_MAX_DOORBELL_IDX) return; /* Ensure that the interrupt is ordered w.r.t previous stores. */ wmb(); mic_mmio_write(mw, MIC_X100_SBOX_SDBIC0_DBREQ_BIT, MIC_X100_SBOX_BASE_ADDRESS + (MIC_X100_SBOX_SDBIC0 + (4 * doorbell))); }
/** * mic_x100_enable_interrupts - Enable interrupts. * @mdev: pointer to mic_device instance */ static void mic_x100_enable_interrupts(struct mic_device *mdev) { u32 reg; struct mic_mw *mw = &mdev->mmio; u32 sice0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SICE0; u32 siac0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SIAC0; reg = mic_mmio_read(mw, sice0); reg |= MIC_X100_SBOX_DBR_BITS(0xf) | MIC_X100_SBOX_DMA_BITS(0xff); mic_mmio_write(mw, reg, sice0); /* * Enable auto-clear when enabling interrupts. Applicable only for * MSI-x. Legacy and MSI mode cannot have auto-clear enabled. */ if (mdev->irq_info.num_vectors > 1) { reg = mic_mmio_read(mw, siac0); reg |= MIC_X100_SBOX_DBR_BITS(0xf) | MIC_X100_SBOX_DMA_BITS(0xff); mic_mmio_write(mw, reg, siac0); } }
/** * mic_x100_program_msi_to_src_map - program the MSI mapping registers * @mdev: pointer to mic_device instance * @idx: index to the mapping register, 0 based * @offset: The bit offset in the register that needs to be updated. * @set: boolean specifying if the bit in the specified offset needs * to be set or cleared. * * RETURNS: None. */ static void mic_x100_program_msi_to_src_map(struct mic_device *mdev, int idx, int offset, bool set) { unsigned long reg; struct mic_mw *mw = &mdev->mmio; u32 mxar = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_MXAR0 + idx * 4; reg = mic_mmio_read(mw, mxar); if (set) __set_bit(offset, ®); else __clear_bit(offset, ®); mic_mmio_write(mw, reg, mxar); }
/** * mic_x100_send_sbox_intr - Send an MIC_X100_SBOX interrupt to MIC. * @mdev: pointer to mic_device instance */ static void mic_x100_send_sbox_intr(struct mic_device *mdev, int doorbell) { struct mic_mw *mw = &mdev->mmio; u64 apic_icr_offset = MIC_X100_SBOX_APICICR0 + doorbell * 8; u32 apicicr_low = mic_mmio_read(mw, MIC_X100_SBOX_BASE_ADDRESS + apic_icr_offset); /* for MIC we need to make sure we "hit" the send_icr bit (13) */ apicicr_low = (apicicr_low | (1 << 13)); /* Ensure that the interrupt is ordered w.r.t. previous stores. */ wmb(); mic_mmio_write(mw, apicicr_low, MIC_X100_SBOX_BASE_ADDRESS + apic_icr_offset); }
/** * mic_x100_hw_reset - Reset the MIC device. * @mdev: pointer to mic_device instance */ static void mic_x100_hw_reset(struct mic_device *mdev) { u32 reset_reg; u32 rgcr = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_RGCR; struct mic_mw *mw = &mdev->mmio; /* Ensure that the reset is ordered w.r.t. previous loads and stores */ mb(); /* Trigger reset */ reset_reg = mic_mmio_read(mw, rgcr); reset_reg |= 0x1; mic_mmio_write(mw, reset_reg, rgcr); /* * It seems we really want to delay at least 1 second * after touching reset to prevent a lot of problems. */ msleep(1000); }
/* * mic_x100_send_sbox_intr - Send an MIC_X100_SBOX interrupt to MIC. */ static void mic_x100_send_sbox_intr(struct mic_mw *mw, int doorbell) { u64 apic_icr_offset = MIC_X100_SBOX_APICICR0 + doorbell * 8; u32 apicicr_low = mic_mmio_read(mw, MIC_X100_SBOX_BASE_ADDRESS + apic_icr_offset); /* for MIC we need to make sure we "hit" the send_icr bit (13) */ apicicr_low = (apicicr_low | (1 << 13)); /* * Ensure that the interrupt is ordered w.r.t. previous stores * to main memory. Fence instructions are not implemented in X100 * since execution is in order but a compiler barrier is still * required. */ wmb(); mic_mmio_write(mw, apicicr_low, MIC_X100_SBOX_BASE_ADDRESS + apic_icr_offset); }