18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Alchemy PCI host mode support. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2001-2003, 2007-2008 MontaVista Software Inc. 68c2ecf20Sopenharmony_ci * Author: MontaVista Software, Inc. <source@mvista.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Support for all devices (greater than 16) added by David Gathright. 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/clk.h> 128c2ecf20Sopenharmony_ci#include <linux/export.h> 138c2ecf20Sopenharmony_ci#include <linux/types.h> 148c2ecf20Sopenharmony_ci#include <linux/pci.h> 158c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 168c2ecf20Sopenharmony_ci#include <linux/kernel.h> 178c2ecf20Sopenharmony_ci#include <linux/init.h> 188c2ecf20Sopenharmony_ci#include <linux/syscore_ops.h> 198c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include <asm/dma-coherence.h> 228c2ecf20Sopenharmony_ci#include <asm/mach-au1x00/au1000.h> 238c2ecf20Sopenharmony_ci#include <asm/tlbmisc.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI_DEBUG 268c2ecf20Sopenharmony_ci#define DBG(x...) printk(KERN_DEBUG x) 278c2ecf20Sopenharmony_ci#else 288c2ecf20Sopenharmony_ci#define DBG(x...) do {} while (0) 298c2ecf20Sopenharmony_ci#endif 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define PCI_ACCESS_READ 0 328c2ecf20Sopenharmony_ci#define PCI_ACCESS_WRITE 1 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistruct alchemy_pci_context { 358c2ecf20Sopenharmony_ci struct pci_controller alchemy_pci_ctrl; /* leave as first member! */ 368c2ecf20Sopenharmony_ci void __iomem *regs; /* ctrl base */ 378c2ecf20Sopenharmony_ci /* tools for wired entry for config space access */ 388c2ecf20Sopenharmony_ci unsigned long last_elo0; 398c2ecf20Sopenharmony_ci unsigned long last_elo1; 408c2ecf20Sopenharmony_ci int wired_entry; 418c2ecf20Sopenharmony_ci struct vm_struct *pci_cfg_vm; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci unsigned long pm[12]; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci int (*board_map_irq)(const struct pci_dev *d, u8 slot, u8 pin); 468c2ecf20Sopenharmony_ci int (*board_pci_idsel)(unsigned int devsel, int assert); 478c2ecf20Sopenharmony_ci}; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci/* for syscore_ops. There's only one PCI controller on Alchemy chips, so this 508c2ecf20Sopenharmony_ci * should suffice for now. 518c2ecf20Sopenharmony_ci */ 528c2ecf20Sopenharmony_cistatic struct alchemy_pci_context *__alchemy_pci_ctx; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci/* IO/MEM resources for PCI. Keep the memres in sync with fixup_bigphys_addr 568c2ecf20Sopenharmony_ci * in arch/mips/alchemy/common/setup.c 578c2ecf20Sopenharmony_ci */ 588c2ecf20Sopenharmony_cistatic struct resource alchemy_pci_def_memres = { 598c2ecf20Sopenharmony_ci .start = ALCHEMY_PCI_MEMWIN_START, 608c2ecf20Sopenharmony_ci .end = ALCHEMY_PCI_MEMWIN_END, 618c2ecf20Sopenharmony_ci .name = "PCI memory space", 628c2ecf20Sopenharmony_ci .flags = IORESOURCE_MEM 638c2ecf20Sopenharmony_ci}; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic struct resource alchemy_pci_def_iores = { 668c2ecf20Sopenharmony_ci .start = ALCHEMY_PCI_IOWIN_START, 678c2ecf20Sopenharmony_ci .end = ALCHEMY_PCI_IOWIN_END, 688c2ecf20Sopenharmony_ci .name = "PCI IO space", 698c2ecf20Sopenharmony_ci .flags = IORESOURCE_IO 708c2ecf20Sopenharmony_ci}; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic void mod_wired_entry(int entry, unsigned long entrylo0, 738c2ecf20Sopenharmony_ci unsigned long entrylo1, unsigned long entryhi, 748c2ecf20Sopenharmony_ci unsigned long pagemask) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci unsigned long old_pagemask; 778c2ecf20Sopenharmony_ci unsigned long old_ctx; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci /* Save old context and create impossible VPN2 value */ 808c2ecf20Sopenharmony_ci old_ctx = read_c0_entryhi() & MIPS_ENTRYHI_ASID; 818c2ecf20Sopenharmony_ci old_pagemask = read_c0_pagemask(); 828c2ecf20Sopenharmony_ci write_c0_index(entry); 838c2ecf20Sopenharmony_ci write_c0_pagemask(pagemask); 848c2ecf20Sopenharmony_ci write_c0_entryhi(entryhi); 858c2ecf20Sopenharmony_ci write_c0_entrylo0(entrylo0); 868c2ecf20Sopenharmony_ci write_c0_entrylo1(entrylo1); 878c2ecf20Sopenharmony_ci tlb_write_indexed(); 888c2ecf20Sopenharmony_ci write_c0_entryhi(old_ctx); 898c2ecf20Sopenharmony_ci write_c0_pagemask(old_pagemask); 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic void alchemy_pci_wired_entry(struct alchemy_pci_context *ctx) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci ctx->wired_entry = read_c0_wired(); 958c2ecf20Sopenharmony_ci add_wired_entry(0, 0, (unsigned long)ctx->pci_cfg_vm->addr, PM_4K); 968c2ecf20Sopenharmony_ci ctx->last_elo0 = ctx->last_elo1 = ~0; 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic int config_access(unsigned char access_type, struct pci_bus *bus, 1008c2ecf20Sopenharmony_ci unsigned int dev_fn, unsigned char where, u32 *data) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci struct alchemy_pci_context *ctx = bus->sysdata; 1038c2ecf20Sopenharmony_ci unsigned int device = PCI_SLOT(dev_fn); 1048c2ecf20Sopenharmony_ci unsigned int function = PCI_FUNC(dev_fn); 1058c2ecf20Sopenharmony_ci unsigned long offset, status, cfg_base, flags, entryLo0, entryLo1, r; 1068c2ecf20Sopenharmony_ci int error = PCIBIOS_SUCCESSFUL; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci if (device > 19) { 1098c2ecf20Sopenharmony_ci *data = 0xffffffff; 1108c2ecf20Sopenharmony_ci return -1; 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci local_irq_save(flags); 1148c2ecf20Sopenharmony_ci r = __raw_readl(ctx->regs + PCI_REG_STATCMD) & 0x0000ffff; 1158c2ecf20Sopenharmony_ci r |= PCI_STATCMD_STATUS(0x2000); 1168c2ecf20Sopenharmony_ci __raw_writel(r, ctx->regs + PCI_REG_STATCMD); 1178c2ecf20Sopenharmony_ci wmb(); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci /* Allow board vendors to implement their own off-chip IDSEL. 1208c2ecf20Sopenharmony_ci * If it doesn't succeed, may as well bail out at this point. 1218c2ecf20Sopenharmony_ci */ 1228c2ecf20Sopenharmony_ci if (ctx->board_pci_idsel(device, 1) == 0) { 1238c2ecf20Sopenharmony_ci *data = 0xffffffff; 1248c2ecf20Sopenharmony_ci local_irq_restore(flags); 1258c2ecf20Sopenharmony_ci return -1; 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci /* Setup the config window */ 1298c2ecf20Sopenharmony_ci if (bus->number == 0) 1308c2ecf20Sopenharmony_ci cfg_base = (1 << device) << 11; 1318c2ecf20Sopenharmony_ci else 1328c2ecf20Sopenharmony_ci cfg_base = 0x80000000 | (bus->number << 16) | (device << 11); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci /* Setup the lower bits of the 36-bit address */ 1358c2ecf20Sopenharmony_ci offset = (function << 8) | (where & ~0x3); 1368c2ecf20Sopenharmony_ci /* Pick up any address that falls below the page mask */ 1378c2ecf20Sopenharmony_ci offset |= cfg_base & ~PAGE_MASK; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci /* Page boundary */ 1408c2ecf20Sopenharmony_ci cfg_base = cfg_base & PAGE_MASK; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci /* To improve performance, if the current device is the same as 1438c2ecf20Sopenharmony_ci * the last device accessed, we don't touch the TLB. 1448c2ecf20Sopenharmony_ci */ 1458c2ecf20Sopenharmony_ci entryLo0 = (6 << 26) | (cfg_base >> 6) | (2 << 3) | 7; 1468c2ecf20Sopenharmony_ci entryLo1 = (6 << 26) | (cfg_base >> 6) | (0x1000 >> 6) | (2 << 3) | 7; 1478c2ecf20Sopenharmony_ci if ((entryLo0 != ctx->last_elo0) || (entryLo1 != ctx->last_elo1)) { 1488c2ecf20Sopenharmony_ci mod_wired_entry(ctx->wired_entry, entryLo0, entryLo1, 1498c2ecf20Sopenharmony_ci (unsigned long)ctx->pci_cfg_vm->addr, PM_4K); 1508c2ecf20Sopenharmony_ci ctx->last_elo0 = entryLo0; 1518c2ecf20Sopenharmony_ci ctx->last_elo1 = entryLo1; 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci if (access_type == PCI_ACCESS_WRITE) 1558c2ecf20Sopenharmony_ci __raw_writel(*data, ctx->pci_cfg_vm->addr + offset); 1568c2ecf20Sopenharmony_ci else 1578c2ecf20Sopenharmony_ci *data = __raw_readl(ctx->pci_cfg_vm->addr + offset); 1588c2ecf20Sopenharmony_ci wmb(); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci DBG("alchemy-pci: cfg access %d bus %u dev %u at %x dat %x conf %lx\n", 1618c2ecf20Sopenharmony_ci access_type, bus->number, device, where, *data, offset); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci /* check for errors, master abort */ 1648c2ecf20Sopenharmony_ci status = __raw_readl(ctx->regs + PCI_REG_STATCMD); 1658c2ecf20Sopenharmony_ci if (status & (1 << 29)) { 1668c2ecf20Sopenharmony_ci *data = 0xffffffff; 1678c2ecf20Sopenharmony_ci error = -1; 1688c2ecf20Sopenharmony_ci DBG("alchemy-pci: master abort on cfg access %d bus %d dev %d\n", 1698c2ecf20Sopenharmony_ci access_type, bus->number, device); 1708c2ecf20Sopenharmony_ci } else if ((status >> 28) & 0xf) { 1718c2ecf20Sopenharmony_ci DBG("alchemy-pci: PCI ERR detected: dev %d, status %lx\n", 1728c2ecf20Sopenharmony_ci device, (status >> 28) & 0xf); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci /* clear errors */ 1758c2ecf20Sopenharmony_ci __raw_writel(status & 0xf000ffff, ctx->regs + PCI_REG_STATCMD); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci *data = 0xffffffff; 1788c2ecf20Sopenharmony_ci error = -1; 1798c2ecf20Sopenharmony_ci } 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci /* Take away the IDSEL. */ 1828c2ecf20Sopenharmony_ci (void)ctx->board_pci_idsel(device, 0); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci local_irq_restore(flags); 1858c2ecf20Sopenharmony_ci return error; 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cistatic int read_config_byte(struct pci_bus *bus, unsigned int devfn, 1898c2ecf20Sopenharmony_ci int where, u8 *val) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci u32 data; 1928c2ecf20Sopenharmony_ci int ret = config_access(PCI_ACCESS_READ, bus, devfn, where, &data); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci if (where & 1) 1958c2ecf20Sopenharmony_ci data >>= 8; 1968c2ecf20Sopenharmony_ci if (where & 2) 1978c2ecf20Sopenharmony_ci data >>= 16; 1988c2ecf20Sopenharmony_ci *val = data & 0xff; 1998c2ecf20Sopenharmony_ci return ret; 2008c2ecf20Sopenharmony_ci} 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_cistatic int read_config_word(struct pci_bus *bus, unsigned int devfn, 2038c2ecf20Sopenharmony_ci int where, u16 *val) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci u32 data; 2068c2ecf20Sopenharmony_ci int ret = config_access(PCI_ACCESS_READ, bus, devfn, where, &data); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci if (where & 2) 2098c2ecf20Sopenharmony_ci data >>= 16; 2108c2ecf20Sopenharmony_ci *val = data & 0xffff; 2118c2ecf20Sopenharmony_ci return ret; 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_cistatic int read_config_dword(struct pci_bus *bus, unsigned int devfn, 2158c2ecf20Sopenharmony_ci int where, u32 *val) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci return config_access(PCI_ACCESS_READ, bus, devfn, where, val); 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cistatic int write_config_byte(struct pci_bus *bus, unsigned int devfn, 2218c2ecf20Sopenharmony_ci int where, u8 val) 2228c2ecf20Sopenharmony_ci{ 2238c2ecf20Sopenharmony_ci u32 data = 0; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci if (config_access(PCI_ACCESS_READ, bus, devfn, where, &data)) 2268c2ecf20Sopenharmony_ci return -1; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci data = (data & ~(0xff << ((where & 3) << 3))) | 2298c2ecf20Sopenharmony_ci (val << ((where & 3) << 3)); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci if (config_access(PCI_ACCESS_WRITE, bus, devfn, where, &data)) 2328c2ecf20Sopenharmony_ci return -1; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci return PCIBIOS_SUCCESSFUL; 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_cistatic int write_config_word(struct pci_bus *bus, unsigned int devfn, 2388c2ecf20Sopenharmony_ci int where, u16 val) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci u32 data = 0; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci if (config_access(PCI_ACCESS_READ, bus, devfn, where, &data)) 2438c2ecf20Sopenharmony_ci return -1; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci data = (data & ~(0xffff << ((where & 3) << 3))) | 2468c2ecf20Sopenharmony_ci (val << ((where & 3) << 3)); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci if (config_access(PCI_ACCESS_WRITE, bus, devfn, where, &data)) 2498c2ecf20Sopenharmony_ci return -1; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci return PCIBIOS_SUCCESSFUL; 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_cistatic int write_config_dword(struct pci_bus *bus, unsigned int devfn, 2558c2ecf20Sopenharmony_ci int where, u32 val) 2568c2ecf20Sopenharmony_ci{ 2578c2ecf20Sopenharmony_ci return config_access(PCI_ACCESS_WRITE, bus, devfn, where, &val); 2588c2ecf20Sopenharmony_ci} 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_cistatic int alchemy_pci_read(struct pci_bus *bus, unsigned int devfn, 2618c2ecf20Sopenharmony_ci int where, int size, u32 *val) 2628c2ecf20Sopenharmony_ci{ 2638c2ecf20Sopenharmony_ci switch (size) { 2648c2ecf20Sopenharmony_ci case 1: { 2658c2ecf20Sopenharmony_ci u8 _val; 2668c2ecf20Sopenharmony_ci int rc = read_config_byte(bus, devfn, where, &_val); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci *val = _val; 2698c2ecf20Sopenharmony_ci return rc; 2708c2ecf20Sopenharmony_ci } 2718c2ecf20Sopenharmony_ci case 2: { 2728c2ecf20Sopenharmony_ci u16 _val; 2738c2ecf20Sopenharmony_ci int rc = read_config_word(bus, devfn, where, &_val); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci *val = _val; 2768c2ecf20Sopenharmony_ci return rc; 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci default: 2798c2ecf20Sopenharmony_ci return read_config_dword(bus, devfn, where, val); 2808c2ecf20Sopenharmony_ci } 2818c2ecf20Sopenharmony_ci} 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_cistatic int alchemy_pci_write(struct pci_bus *bus, unsigned int devfn, 2848c2ecf20Sopenharmony_ci int where, int size, u32 val) 2858c2ecf20Sopenharmony_ci{ 2868c2ecf20Sopenharmony_ci switch (size) { 2878c2ecf20Sopenharmony_ci case 1: 2888c2ecf20Sopenharmony_ci return write_config_byte(bus, devfn, where, (u8) val); 2898c2ecf20Sopenharmony_ci case 2: 2908c2ecf20Sopenharmony_ci return write_config_word(bus, devfn, where, (u16) val); 2918c2ecf20Sopenharmony_ci default: 2928c2ecf20Sopenharmony_ci return write_config_dword(bus, devfn, where, val); 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci} 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_cistatic struct pci_ops alchemy_pci_ops = { 2978c2ecf20Sopenharmony_ci .read = alchemy_pci_read, 2988c2ecf20Sopenharmony_ci .write = alchemy_pci_write, 2998c2ecf20Sopenharmony_ci}; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_cistatic int alchemy_pci_def_idsel(unsigned int devsel, int assert) 3028c2ecf20Sopenharmony_ci{ 3038c2ecf20Sopenharmony_ci return 1; /* success */ 3048c2ecf20Sopenharmony_ci} 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci/* save PCI controller register contents. */ 3078c2ecf20Sopenharmony_cistatic int alchemy_pci_suspend(void) 3088c2ecf20Sopenharmony_ci{ 3098c2ecf20Sopenharmony_ci struct alchemy_pci_context *ctx = __alchemy_pci_ctx; 3108c2ecf20Sopenharmony_ci if (!ctx) 3118c2ecf20Sopenharmony_ci return 0; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci ctx->pm[0] = __raw_readl(ctx->regs + PCI_REG_CMEM); 3148c2ecf20Sopenharmony_ci ctx->pm[1] = __raw_readl(ctx->regs + PCI_REG_CONFIG) & 0x0009ffff; 3158c2ecf20Sopenharmony_ci ctx->pm[2] = __raw_readl(ctx->regs + PCI_REG_B2BMASK_CCH); 3168c2ecf20Sopenharmony_ci ctx->pm[3] = __raw_readl(ctx->regs + PCI_REG_B2BBASE0_VID); 3178c2ecf20Sopenharmony_ci ctx->pm[4] = __raw_readl(ctx->regs + PCI_REG_B2BBASE1_SID); 3188c2ecf20Sopenharmony_ci ctx->pm[5] = __raw_readl(ctx->regs + PCI_REG_MWMASK_DEV); 3198c2ecf20Sopenharmony_ci ctx->pm[6] = __raw_readl(ctx->regs + PCI_REG_MWBASE_REV_CCL); 3208c2ecf20Sopenharmony_ci ctx->pm[7] = __raw_readl(ctx->regs + PCI_REG_ID); 3218c2ecf20Sopenharmony_ci ctx->pm[8] = __raw_readl(ctx->regs + PCI_REG_CLASSREV); 3228c2ecf20Sopenharmony_ci ctx->pm[9] = __raw_readl(ctx->regs + PCI_REG_PARAM); 3238c2ecf20Sopenharmony_ci ctx->pm[10] = __raw_readl(ctx->regs + PCI_REG_MBAR); 3248c2ecf20Sopenharmony_ci ctx->pm[11] = __raw_readl(ctx->regs + PCI_REG_TIMEOUT); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci return 0; 3278c2ecf20Sopenharmony_ci} 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_cistatic void alchemy_pci_resume(void) 3308c2ecf20Sopenharmony_ci{ 3318c2ecf20Sopenharmony_ci struct alchemy_pci_context *ctx = __alchemy_pci_ctx; 3328c2ecf20Sopenharmony_ci if (!ctx) 3338c2ecf20Sopenharmony_ci return; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci __raw_writel(ctx->pm[0], ctx->regs + PCI_REG_CMEM); 3368c2ecf20Sopenharmony_ci __raw_writel(ctx->pm[2], ctx->regs + PCI_REG_B2BMASK_CCH); 3378c2ecf20Sopenharmony_ci __raw_writel(ctx->pm[3], ctx->regs + PCI_REG_B2BBASE0_VID); 3388c2ecf20Sopenharmony_ci __raw_writel(ctx->pm[4], ctx->regs + PCI_REG_B2BBASE1_SID); 3398c2ecf20Sopenharmony_ci __raw_writel(ctx->pm[5], ctx->regs + PCI_REG_MWMASK_DEV); 3408c2ecf20Sopenharmony_ci __raw_writel(ctx->pm[6], ctx->regs + PCI_REG_MWBASE_REV_CCL); 3418c2ecf20Sopenharmony_ci __raw_writel(ctx->pm[7], ctx->regs + PCI_REG_ID); 3428c2ecf20Sopenharmony_ci __raw_writel(ctx->pm[8], ctx->regs + PCI_REG_CLASSREV); 3438c2ecf20Sopenharmony_ci __raw_writel(ctx->pm[9], ctx->regs + PCI_REG_PARAM); 3448c2ecf20Sopenharmony_ci __raw_writel(ctx->pm[10], ctx->regs + PCI_REG_MBAR); 3458c2ecf20Sopenharmony_ci __raw_writel(ctx->pm[11], ctx->regs + PCI_REG_TIMEOUT); 3468c2ecf20Sopenharmony_ci wmb(); 3478c2ecf20Sopenharmony_ci __raw_writel(ctx->pm[1], ctx->regs + PCI_REG_CONFIG); 3488c2ecf20Sopenharmony_ci wmb(); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci /* YAMON on all db1xxx boards wipes the TLB and writes zero to C0_wired 3518c2ecf20Sopenharmony_ci * on resume, making it necessary to recreate it as soon as possible. 3528c2ecf20Sopenharmony_ci */ 3538c2ecf20Sopenharmony_ci ctx->wired_entry = 8191; /* impossibly high value */ 3548c2ecf20Sopenharmony_ci alchemy_pci_wired_entry(ctx); /* install it */ 3558c2ecf20Sopenharmony_ci} 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_cistatic struct syscore_ops alchemy_pci_pmops = { 3588c2ecf20Sopenharmony_ci .suspend = alchemy_pci_suspend, 3598c2ecf20Sopenharmony_ci .resume = alchemy_pci_resume, 3608c2ecf20Sopenharmony_ci}; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_cistatic int alchemy_pci_probe(struct platform_device *pdev) 3638c2ecf20Sopenharmony_ci{ 3648c2ecf20Sopenharmony_ci struct alchemy_pci_platdata *pd = pdev->dev.platform_data; 3658c2ecf20Sopenharmony_ci struct alchemy_pci_context *ctx; 3668c2ecf20Sopenharmony_ci void __iomem *virt_io; 3678c2ecf20Sopenharmony_ci unsigned long val; 3688c2ecf20Sopenharmony_ci struct resource *r; 3698c2ecf20Sopenharmony_ci struct clk *c; 3708c2ecf20Sopenharmony_ci int ret; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci /* need at least PCI IRQ mapping table */ 3738c2ecf20Sopenharmony_ci if (!pd) { 3748c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "need platform data for PCI setup\n"); 3758c2ecf20Sopenharmony_ci ret = -ENODEV; 3768c2ecf20Sopenharmony_ci goto out; 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 3808c2ecf20Sopenharmony_ci if (!ctx) { 3818c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "no memory for pcictl context\n"); 3828c2ecf20Sopenharmony_ci ret = -ENOMEM; 3838c2ecf20Sopenharmony_ci goto out; 3848c2ecf20Sopenharmony_ci } 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 3878c2ecf20Sopenharmony_ci if (!r) { 3888c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "no pcictl ctrl regs resource\n"); 3898c2ecf20Sopenharmony_ci ret = -ENODEV; 3908c2ecf20Sopenharmony_ci goto out1; 3918c2ecf20Sopenharmony_ci } 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci if (!request_mem_region(r->start, resource_size(r), pdev->name)) { 3948c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "cannot claim pci regs\n"); 3958c2ecf20Sopenharmony_ci ret = -ENODEV; 3968c2ecf20Sopenharmony_ci goto out1; 3978c2ecf20Sopenharmony_ci } 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci c = clk_get(&pdev->dev, "pci_clko"); 4008c2ecf20Sopenharmony_ci if (IS_ERR(c)) { 4018c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "unable to find PCI clock\n"); 4028c2ecf20Sopenharmony_ci ret = PTR_ERR(c); 4038c2ecf20Sopenharmony_ci goto out2; 4048c2ecf20Sopenharmony_ci } 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci ret = clk_prepare_enable(c); 4078c2ecf20Sopenharmony_ci if (ret) { 4088c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "cannot enable PCI clock\n"); 4098c2ecf20Sopenharmony_ci goto out6; 4108c2ecf20Sopenharmony_ci } 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci ctx->regs = ioremap(r->start, resource_size(r)); 4138c2ecf20Sopenharmony_ci if (!ctx->regs) { 4148c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "cannot map pci regs\n"); 4158c2ecf20Sopenharmony_ci ret = -ENODEV; 4168c2ecf20Sopenharmony_ci goto out5; 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci /* map parts of the PCI IO area */ 4208c2ecf20Sopenharmony_ci /* REVISIT: if this changes with a newer variant (doubt it) make this 4218c2ecf20Sopenharmony_ci * a platform resource. 4228c2ecf20Sopenharmony_ci */ 4238c2ecf20Sopenharmony_ci virt_io = ioremap(AU1500_PCI_IO_PHYS_ADDR, 0x00100000); 4248c2ecf20Sopenharmony_ci if (!virt_io) { 4258c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "cannot remap pci io space\n"); 4268c2ecf20Sopenharmony_ci ret = -ENODEV; 4278c2ecf20Sopenharmony_ci goto out3; 4288c2ecf20Sopenharmony_ci } 4298c2ecf20Sopenharmony_ci ctx->alchemy_pci_ctrl.io_map_base = (unsigned long)virt_io; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci /* Au1500 revisions older than AD have borked coherent PCI */ 4328c2ecf20Sopenharmony_ci if ((alchemy_get_cputype() == ALCHEMY_CPU_AU1500) && 4338c2ecf20Sopenharmony_ci (read_c0_prid() < 0x01030202) && 4348c2ecf20Sopenharmony_ci (coherentio == IO_COHERENCE_DISABLED)) { 4358c2ecf20Sopenharmony_ci val = __raw_readl(ctx->regs + PCI_REG_CONFIG); 4368c2ecf20Sopenharmony_ci val |= PCI_CONFIG_NC; 4378c2ecf20Sopenharmony_ci __raw_writel(val, ctx->regs + PCI_REG_CONFIG); 4388c2ecf20Sopenharmony_ci wmb(); 4398c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "non-coherent PCI on Au1500 AA/AB/AC\n"); 4408c2ecf20Sopenharmony_ci } 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci if (pd->board_map_irq) 4438c2ecf20Sopenharmony_ci ctx->board_map_irq = pd->board_map_irq; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci if (pd->board_pci_idsel) 4468c2ecf20Sopenharmony_ci ctx->board_pci_idsel = pd->board_pci_idsel; 4478c2ecf20Sopenharmony_ci else 4488c2ecf20Sopenharmony_ci ctx->board_pci_idsel = alchemy_pci_def_idsel; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci /* fill in relevant pci_controller members */ 4518c2ecf20Sopenharmony_ci ctx->alchemy_pci_ctrl.pci_ops = &alchemy_pci_ops; 4528c2ecf20Sopenharmony_ci ctx->alchemy_pci_ctrl.mem_resource = &alchemy_pci_def_memres; 4538c2ecf20Sopenharmony_ci ctx->alchemy_pci_ctrl.io_resource = &alchemy_pci_def_iores; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci /* we can't ioremap the entire pci config space because it's too large, 4568c2ecf20Sopenharmony_ci * nor can we dynamically ioremap it because some drivers use the 4578c2ecf20Sopenharmony_ci * PCI config routines from within atomic contex and that becomes a 4588c2ecf20Sopenharmony_ci * problem in get_vm_area(). Instead we use one wired TLB entry to 4598c2ecf20Sopenharmony_ci * handle all config accesses for all busses. 4608c2ecf20Sopenharmony_ci */ 4618c2ecf20Sopenharmony_ci ctx->pci_cfg_vm = get_vm_area(0x2000, VM_IOREMAP); 4628c2ecf20Sopenharmony_ci if (!ctx->pci_cfg_vm) { 4638c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "unable to get vm area\n"); 4648c2ecf20Sopenharmony_ci ret = -ENOMEM; 4658c2ecf20Sopenharmony_ci goto out4; 4668c2ecf20Sopenharmony_ci } 4678c2ecf20Sopenharmony_ci ctx->wired_entry = 8191; /* impossibly high value */ 4688c2ecf20Sopenharmony_ci alchemy_pci_wired_entry(ctx); /* install it */ 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci set_io_port_base((unsigned long)ctx->alchemy_pci_ctrl.io_map_base); 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci /* board may want to modify bits in the config register, do it now */ 4738c2ecf20Sopenharmony_ci val = __raw_readl(ctx->regs + PCI_REG_CONFIG); 4748c2ecf20Sopenharmony_ci val &= ~pd->pci_cfg_clr; 4758c2ecf20Sopenharmony_ci val |= pd->pci_cfg_set; 4768c2ecf20Sopenharmony_ci val &= ~PCI_CONFIG_PD; /* clear disable bit */ 4778c2ecf20Sopenharmony_ci __raw_writel(val, ctx->regs + PCI_REG_CONFIG); 4788c2ecf20Sopenharmony_ci wmb(); 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci __alchemy_pci_ctx = ctx; 4818c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, ctx); 4828c2ecf20Sopenharmony_ci register_syscore_ops(&alchemy_pci_pmops); 4838c2ecf20Sopenharmony_ci register_pci_controller(&ctx->alchemy_pci_ctrl); 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "PCI controller at %ld MHz\n", 4868c2ecf20Sopenharmony_ci clk_get_rate(c) / 1000000); 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci return 0; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ciout4: 4918c2ecf20Sopenharmony_ci iounmap(virt_io); 4928c2ecf20Sopenharmony_ciout3: 4938c2ecf20Sopenharmony_ci iounmap(ctx->regs); 4948c2ecf20Sopenharmony_ciout5: 4958c2ecf20Sopenharmony_ci clk_disable_unprepare(c); 4968c2ecf20Sopenharmony_ciout6: 4978c2ecf20Sopenharmony_ci clk_put(c); 4988c2ecf20Sopenharmony_ciout2: 4998c2ecf20Sopenharmony_ci release_mem_region(r->start, resource_size(r)); 5008c2ecf20Sopenharmony_ciout1: 5018c2ecf20Sopenharmony_ci kfree(ctx); 5028c2ecf20Sopenharmony_ciout: 5038c2ecf20Sopenharmony_ci return ret; 5048c2ecf20Sopenharmony_ci} 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_cistatic struct platform_driver alchemy_pcictl_driver = { 5078c2ecf20Sopenharmony_ci .probe = alchemy_pci_probe, 5088c2ecf20Sopenharmony_ci .driver = { 5098c2ecf20Sopenharmony_ci .name = "alchemy-pci", 5108c2ecf20Sopenharmony_ci }, 5118c2ecf20Sopenharmony_ci}; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_cistatic int __init alchemy_pci_init(void) 5148c2ecf20Sopenharmony_ci{ 5158c2ecf20Sopenharmony_ci /* Au1500/Au1550 have PCI */ 5168c2ecf20Sopenharmony_ci switch (alchemy_get_cputype()) { 5178c2ecf20Sopenharmony_ci case ALCHEMY_CPU_AU1500: 5188c2ecf20Sopenharmony_ci case ALCHEMY_CPU_AU1550: 5198c2ecf20Sopenharmony_ci return platform_driver_register(&alchemy_pcictl_driver); 5208c2ecf20Sopenharmony_ci } 5218c2ecf20Sopenharmony_ci return 0; 5228c2ecf20Sopenharmony_ci} 5238c2ecf20Sopenharmony_ciarch_initcall(alchemy_pci_init); 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ciint pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) 5278c2ecf20Sopenharmony_ci{ 5288c2ecf20Sopenharmony_ci struct alchemy_pci_context *ctx = dev->sysdata; 5298c2ecf20Sopenharmony_ci if (ctx && ctx->board_map_irq) 5308c2ecf20Sopenharmony_ci return ctx->board_map_irq(dev, slot, pin); 5318c2ecf20Sopenharmony_ci return -1; 5328c2ecf20Sopenharmony_ci} 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ciint pcibios_plat_dev_init(struct pci_dev *dev) 5358c2ecf20Sopenharmony_ci{ 5368c2ecf20Sopenharmony_ci return 0; 5378c2ecf20Sopenharmony_ci} 538