| /* SPDX-License-Identifier: (GPL-2.0 OR Linux-OpenIB) OR BSD-2-Clause */ |
| /* Copyright(c) 2023 Advanced Micro Devices, Inc. */ |
| |
| #ifndef _PDS_INTR_H_ |
| #define _PDS_INTR_H_ |
| |
| /* |
| * Interrupt control register |
| * @coal_init: Coalescing timer initial value, in |
| * device units. Use @identity->intr_coal_mult |
| * and @identity->intr_coal_div to convert from |
| * usecs to device units: |
| * |
| * coal_init = coal_usecs * coal_mutl / coal_div |
| * |
| * When an interrupt is sent the interrupt |
| * coalescing timer current value |
| * (@coalescing_curr) is initialized with this |
| * value and begins counting down. No more |
| * interrupts are sent until the coalescing |
| * timer reaches 0. When @coalescing_init=0 |
| * interrupt coalescing is effectively disabled |
| * and every interrupt assert results in an |
| * interrupt. Reset value: 0 |
| * @mask: Interrupt mask. When @mask=1 the interrupt |
| * resource will not send an interrupt. When |
| * @mask=0 the interrupt resource will send an |
| * interrupt if an interrupt event is pending |
| * or on the next interrupt assertion event. |
| * Reset value: 1 |
| * @credits: Interrupt credits. This register indicates |
| * how many interrupt events the hardware has |
| * sent. When written by software this |
| * register atomically decrements @int_credits |
| * by the value written. When @int_credits |
| * becomes 0 then the "pending interrupt" bit |
| * in the Interrupt Status register is cleared |
| * by the hardware and any pending but unsent |
| * interrupts are cleared. |
| * !!!IMPORTANT!!! This is a signed register. |
| * @flags: Interrupt control flags |
| * @unmask -- When this bit is written with a 1 |
| * the interrupt resource will set mask=0. |
| * @coal_timer_reset -- When this |
| * bit is written with a 1 the |
| * @coalescing_curr will be reloaded with |
| * @coalescing_init to reset the coalescing |
| * timer. |
| * @mask_on_assert: Automatically mask on assertion. When |
| * @mask_on_assert=1 the interrupt resource |
| * will set @mask=1 whenever an interrupt is |
| * sent. When using interrupts in Legacy |
| * Interrupt mode the driver must select |
| * @mask_on_assert=0 for proper interrupt |
| * operation. |
| * @coalescing_curr: Coalescing timer current value, in |
| * microseconds. When this value reaches 0 |
| * the interrupt resource is again eligible to |
| * send an interrupt. If an interrupt event |
| * is already pending when @coalescing_curr |
| * reaches 0 the pending interrupt will be |
| * sent, otherwise an interrupt will be sent |
| * on the next interrupt assertion event. |
| */ |
| struct pds_core_intr { |
| u32 coal_init; |
| u32 mask; |
| u16 credits; |
| u16 flags; |
| #define PDS_CORE_INTR_F_UNMASK 0x0001 |
| #define PDS_CORE_INTR_F_TIMER_RESET 0x0002 |
| u32 mask_on_assert; |
| u32 coalescing_curr; |
| u32 rsvd6[3]; |
| }; |
| |
| #ifndef __CHECKER__ |
| static_assert(sizeof(struct pds_core_intr) == 32); |
| #endif /* __CHECKER__ */ |
| |
| #define PDS_CORE_INTR_CTRL_REGS_MAX 2048 |
| #define PDS_CORE_INTR_CTRL_COAL_MAX 0x3F |
| #define PDS_CORE_INTR_INDEX_NOT_ASSIGNED -1 |
| |
| struct pds_core_intr_status { |
| u32 status[2]; |
| }; |
| |
| /** |
| * enum pds_core_intr_mask_vals - valid values for mask and mask_assert. |
| * @PDS_CORE_INTR_MASK_CLEAR: unmask interrupt. |
| * @PDS_CORE_INTR_MASK_SET: mask interrupt. |
| */ |
| enum pds_core_intr_mask_vals { |
| PDS_CORE_INTR_MASK_CLEAR = 0, |
| PDS_CORE_INTR_MASK_SET = 1, |
| }; |
| |
| /** |
| * enum pds_core_intr_credits_bits - Bitwise composition of credits values. |
| * @PDS_CORE_INTR_CRED_COUNT: bit mask of credit count, no shift needed. |
| * @PDS_CORE_INTR_CRED_COUNT_SIGNED: bit mask of credit count, including sign bit. |
| * @PDS_CORE_INTR_CRED_UNMASK: unmask the interrupt. |
| * @PDS_CORE_INTR_CRED_RESET_COALESCE: reset the coalesce timer. |
| * @PDS_CORE_INTR_CRED_REARM: unmask the and reset the timer. |
| */ |
| enum pds_core_intr_credits_bits { |
| PDS_CORE_INTR_CRED_COUNT = 0x7fffu, |
| PDS_CORE_INTR_CRED_COUNT_SIGNED = 0xffffu, |
| PDS_CORE_INTR_CRED_UNMASK = 0x10000u, |
| PDS_CORE_INTR_CRED_RESET_COALESCE = 0x20000u, |
| PDS_CORE_INTR_CRED_REARM = (PDS_CORE_INTR_CRED_UNMASK | |
| PDS_CORE_INTR_CRED_RESET_COALESCE), |
| }; |
| |
| static inline void |
| pds_core_intr_coal_init(struct pds_core_intr __iomem *intr_ctrl, u32 coal) |
| { |
| iowrite32(coal, &intr_ctrl->coal_init); |
| } |
| |
| static inline void |
| pds_core_intr_mask(struct pds_core_intr __iomem *intr_ctrl, u32 mask) |
| { |
| iowrite32(mask, &intr_ctrl->mask); |
| } |
| |
| static inline void |
| pds_core_intr_credits(struct pds_core_intr __iomem *intr_ctrl, |
| u32 cred, u32 flags) |
| { |
| if (WARN_ON_ONCE(cred > PDS_CORE_INTR_CRED_COUNT)) { |
| cred = ioread32(&intr_ctrl->credits); |
| cred &= PDS_CORE_INTR_CRED_COUNT_SIGNED; |
| } |
| |
| iowrite32(cred | flags, &intr_ctrl->credits); |
| } |
| |
| static inline void |
| pds_core_intr_clean_flags(struct pds_core_intr __iomem *intr_ctrl, u32 flags) |
| { |
| u32 cred; |
| |
| cred = ioread32(&intr_ctrl->credits); |
| cred &= PDS_CORE_INTR_CRED_COUNT_SIGNED; |
| cred |= flags; |
| iowrite32(cred, &intr_ctrl->credits); |
| } |
| |
| static inline void |
| pds_core_intr_clean(struct pds_core_intr __iomem *intr_ctrl) |
| { |
| pds_core_intr_clean_flags(intr_ctrl, PDS_CORE_INTR_CRED_RESET_COALESCE); |
| } |
| |
| static inline void |
| pds_core_intr_mask_assert(struct pds_core_intr __iomem *intr_ctrl, u32 mask) |
| { |
| iowrite32(mask, &intr_ctrl->mask_on_assert); |
| } |
| |
| #endif /* _PDS_INTR_H_ */ |