drivers/pci/controller/pci-ixp4xx.c

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

File Facts

System
Linux kernel
Corpus path
drivers/pci/controller/pci-ixp4xx.c
Extension
.c
Size
18135 bytes
Lines
679
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 ixp4xx_pci {
	struct device *dev;
	void __iomem *base;
	bool errata_hammer;
	bool host_mode;
};

/*
 * The IXP4xx has a peculiar address bus that will change the
 * byte order on SoC peripherals depending on whether the device
 * operates in big-endian or little-endian mode. That means that
 * readl() and writel() that always use little-endian access
 * will not work for SoC peripherals such as the PCI controller
 * when used in big-endian mode. The accesses to the individual
 * PCI devices on the other hand, are always little-endian and
 * can use readl() and writel().
 *
 * For local AHB bus access we need to use __raw_[readl|writel]()
 * to make sure that we access the SoC devices in the CPU native
 * endianness.
 */
static inline u32 ixp4xx_readl(struct ixp4xx_pci *p, u32 reg)
{
	return __raw_readl(p->base + reg);
}

static inline void ixp4xx_writel(struct ixp4xx_pci *p, u32 reg, u32 val)
{
	__raw_writel(val, p->base + reg);
}

static int ixp4xx_pci_check_master_abort(struct ixp4xx_pci *p)
{
	u32 isr = ixp4xx_readl(p, IXP4XX_PCI_ISR);

	if (isr & IXP4XX_PCI_ISR_PFE) {
		/* Make sure the master abort bit is reset */
		ixp4xx_writel(p, IXP4XX_PCI_ISR, IXP4XX_PCI_ISR_PFE);
		dev_dbg(p->dev, "master abort detected\n");
		return -EINVAL;
	}

	return 0;
}

static int ixp4xx_pci_read_indirect(struct ixp4xx_pci *p, u32 addr, u32 cmd, u32 *data)
{
	ixp4xx_writel(p, IXP4XX_PCI_NP_AD, addr);

	if (p->errata_hammer) {
		int i;

		/*
		 * PCI workaround - only works if NP PCI space reads have
		 * no side effects. Hammer the register and read twice 8
		 * times. last one will be good.
		 */
		for (i = 0; i < 8; i++) {
			ixp4xx_writel(p, IXP4XX_PCI_NP_CBE, cmd);
			*data = ixp4xx_readl(p, IXP4XX_PCI_NP_RDATA);
			*data = ixp4xx_readl(p, IXP4XX_PCI_NP_RDATA);
		}
	} else {
		ixp4xx_writel(p, IXP4XX_PCI_NP_CBE, cmd);
		*data = ixp4xx_readl(p, IXP4XX_PCI_NP_RDATA);
	}

	return ixp4xx_pci_check_master_abort(p);
}

static int ixp4xx_pci_write_indirect(struct ixp4xx_pci *p, u32 addr, u32 cmd, u32 data)
{
	ixp4xx_writel(p, IXP4XX_PCI_NP_AD, addr);

	/* Set up the write */
	ixp4xx_writel(p, IXP4XX_PCI_NP_CBE, cmd);

	/* Execute the write by writing to NP_WDATA */
	ixp4xx_writel(p, IXP4XX_PCI_NP_WDATA, data);

	return ixp4xx_pci_check_master_abort(p);
}

static u32 ixp4xx_config_addr(u8 bus_num, u16 devfn, int where)
{
	/* Root bus is always 0 in this hardware */
	if (bus_num == 0) {
		/* type 0 */
		return (PCI_CONF1_ADDRESS(0, 0, PCI_FUNC(devfn), where) &
			~PCI_CONF1_ENABLE) | BIT(32-PCI_SLOT(devfn));

Annotation

Implementation Notes