drivers/pci/controller/pci-xgene-msi.c

Source file repositories/reference/linux-study-clean/drivers/pci/controller/pci-xgene-msi.c

File Facts

System
Linux kernel
Corpus path
drivers/pci/controller/pci-xgene-msi.c
Extension
.c
Size
11076 bytes
Lines
400
Domain
Representative Device Path
Bucket
PCIe NVMe Storage Path
Inferred role
Representative Device Path: implementation source
Status
source implementation candidate

Why This File Exists

Part of the selected hardware vertical slice: PCI discovery, driver binding, NVMe queues, block requests, DMA, interrupts, and completion.

Dependency Surface

Detected Declarations

Annotated Snippet

struct xgene_msi {
	struct irq_domain	*inner_domain;
	u64			msi_addr;
	void __iomem		*msi_regs;
	unsigned long		*bitmap;
	struct mutex		bitmap_lock;
	unsigned int		gic_irq[NR_HW_IRQS];
};

/* Global data */
static struct xgene_msi *xgene_msi_ctrl;

/*
 * X-Gene v1 has 16 frames of MSI termination registers MSInIRx, where n is
 * frame number (0..15), x is index of registers in each frame (0..7).  Each
 * 32b register is at the beginning of a 64kB region, each frame occupying
 * 512kB (and the whole thing 8MB of PA space).
 *
 * Each register supports 16 MSI vectors (0..15) to generate interrupts. A
 * write to the MSInIRx from the PCI side generates an interrupt. A read
 * from the MSInRx on the CPU side returns a bitmap of the pending MSIs in
 * the lower 16 bits. A side effect of this read is that all pending
 * interrupts are acknowledged and cleared).
 *
 * Additionally, each MSI termination frame has 1 MSIINTn register (n is
 * 0..15) to indicate the MSI pending status caused by any of its 8
 * termination registers, reported as a bitmap in the lower 8 bits. Each 32b
 * register is at the beginning of a 64kB region (and overall occupying an
 * extra 1MB).
 *
 * There is one GIC IRQ assigned for each MSI termination frame, 16 in
 * total.
 *
 * The register layout is as follows:
 * MSI0IR0			base_addr
 * MSI0IR1			base_addr +  0x10000
 * ...				...
 * MSI0IR6			base_addr +  0x60000
 * MSI0IR7			base_addr +  0x70000
 * MSI1IR0			base_addr +  0x80000
 * MSI1IR1			base_addr +  0x90000
 * ...				...
 * MSI1IR7			base_addr +  0xF0000
 * MSI2IR0			base_addr + 0x100000
 * ...				...
 * MSIFIR0			base_addr + 0x780000
 * MSIFIR1			base_addr + 0x790000
 * ...				...
 * MSIFIR7			base_addr + 0x7F0000
 * MSIINT0			base_addr + 0x800000
 * MSIINT1			base_addr + 0x810000
 * ...				...
 * MSIINTF			base_addr + 0x8F0000
 */

/* MSInIRx read helper */
static u32 xgene_msi_ir_read(struct xgene_msi *msi, u32 msi_grp, u32 msir_idx)
{
	return readl_relaxed(msi->msi_regs + MSI_IR0 +
			     (FIELD_PREP(MSI_GROUP_MASK, msi_grp) |
			      FIELD_PREP(MSI_INDEX_MASK, msir_idx)));
}

/* MSIINTn read helper */
static u32 xgene_msi_int_read(struct xgene_msi *msi, u32 msi_grp)
{
	return readl_relaxed(msi->msi_regs + MSI_INT0 +
			     FIELD_PREP(MSI_INTR_MASK, msi_grp));
}

/*
 * In order to allow an MSI to be moved from one CPU to another without
 * having to repaint both the address and the data (which cannot be done
 * atomically), we statically partitions the MSI frames between CPUs. Given
 * that XGene-1 has 8 CPUs, each CPU gets two frames assigned to it
 *
 * We adopt the convention that when an MSI is moved, it is configured to
 * target the same register number in the congruent frame assigned to the
 * new target CPU. This reserves a given MSI across all CPUs, and reduces
 * the MSI capacity from 2048 to 256.
 *
 * Effectively, this amounts to:
 * - hwirq[7]::cpu[2:0] is the target frame number (n in MSInIRx)
 * - hwirq[6:4] is the register index in any given frame (x in MSInIRx)
 * - hwirq[3:0] is the MSI data
 */
static irq_hw_number_t compute_hwirq(u8 frame, u8 index, u8 data)
{
	return (FIELD_PREP(BIT(7), FIELD_GET(BIT(3), frame))	|
		FIELD_PREP(MSInRx_HWIRQ_MASK, index)		|

Annotation

Implementation Notes