arch/mips/pci/pci-alchemy.c

Source file repositories/reference/linux-study-clean/arch/mips/pci/pci-alchemy.c

File Facts

System
Linux kernel
Corpus path
arch/mips/pci/pci-alchemy.c
Extension
.c
Size
14574 bytes
Lines
541
Domain
Architecture Layer
Bucket
arch/mips
Inferred role
Architecture Layer: implementation source
Status
source implementation candidate

Why This File Exists

CPU and platform-specific kernel glue: boot entry, traps, syscall entry, interrupts, page tables, context switch, and low-level barriers.

Dependency Surface

Detected Declarations

Annotated Snippet

struct alchemy_pci_context {
	struct pci_controller alchemy_pci_ctrl; /* leave as first member! */
	void __iomem *regs;			/* ctrl base */
	/* tools for wired entry for config space access */
	unsigned long last_elo0;
	unsigned long last_elo1;
	int wired_entry;
	struct vm_struct *pci_cfg_vm;

	unsigned long pm[12];

	int (*board_map_irq)(const struct pci_dev *d, u8 slot, u8 pin);
	int (*board_pci_idsel)(unsigned int devsel, int assert);
};

/* for syscore_ops. There's only one PCI controller on Alchemy chips, so this
 * should suffice for now.
 */
static struct alchemy_pci_context *__alchemy_pci_ctx;


/* IO/MEM resources for PCI. Keep the memres in sync with fixup_bigphys_addr
 * in arch/mips/alchemy/common/setup.c
 */
static struct resource alchemy_pci_def_memres = {
	.start	= ALCHEMY_PCI_MEMWIN_START,
	.end	= ALCHEMY_PCI_MEMWIN_END,
	.name	= "PCI memory space",
	.flags	= IORESOURCE_MEM
};

static struct resource alchemy_pci_def_iores = {
	.start	= ALCHEMY_PCI_IOWIN_START,
	.end	= ALCHEMY_PCI_IOWIN_END,
	.name	= "PCI IO space",
	.flags	= IORESOURCE_IO
};

static void mod_wired_entry(int entry, unsigned long entrylo0,
		unsigned long entrylo1, unsigned long entryhi,
		unsigned long pagemask)
{
	unsigned long old_pagemask;
	unsigned long old_ctx;

	/* Save old context and create impossible VPN2 value */
	old_ctx = read_c0_entryhi() & MIPS_ENTRYHI_ASID;
	old_pagemask = read_c0_pagemask();
	write_c0_index(entry);
	write_c0_pagemask(pagemask);
	write_c0_entryhi(entryhi);
	write_c0_entrylo0(entrylo0);
	write_c0_entrylo1(entrylo1);
	tlb_write_indexed();
	write_c0_entryhi(old_ctx);
	write_c0_pagemask(old_pagemask);
}

static void alchemy_pci_wired_entry(struct alchemy_pci_context *ctx)
{
	ctx->wired_entry = read_c0_wired();
	add_wired_entry(0, 0, (unsigned long)ctx->pci_cfg_vm->addr, PM_4K);
	ctx->last_elo0 = ctx->last_elo1 = ~0;
}

static int config_access(unsigned char access_type, struct pci_bus *bus,
			 unsigned int dev_fn, unsigned char where, u32 *data)
{
	struct alchemy_pci_context *ctx = bus->sysdata;
	unsigned int device = PCI_SLOT(dev_fn);
	unsigned int function = PCI_FUNC(dev_fn);
	unsigned long offset, status, cfg_base, flags, entryLo0, entryLo1, r;
	int error = PCIBIOS_SUCCESSFUL;

	if (device > 19) {
		*data = 0xffffffff;
		return -1;
	}

	local_irq_save(flags);
	r = __raw_readl(ctx->regs + PCI_REG_STATCMD) & 0x0000ffff;
	r |= PCI_STATCMD_STATUS(0x2000);
	__raw_writel(r, ctx->regs + PCI_REG_STATCMD);
	wmb();

	/* Allow board vendors to implement their own off-chip IDSEL.
	 * If it doesn't succeed, may as well bail out at this point.
	 */
	if (ctx->board_pci_idsel(device, 1) == 0) {
		*data = 0xffffffff;

Annotation

Implementation Notes