diff options
author | blogic <blogic@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2012-10-05 10:12:53 +0000 |
---|---|---|
committer | blogic <blogic@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2012-10-05 10:12:53 +0000 |
commit | 5c105d9f3fd086aff195d3849dcf847d6b0bd927 (patch) | |
tree | 1229a11f725bfa58aa7c57a76898553bb5f6654a /target/linux/mcs814x/files-3.3/arch/arm/mach-mcs814x/pci.c | |
download | openwrt-5c105d9f3fd086aff195d3849dcf847d6b0bd927.tar.gz openwrt-5c105d9f3fd086aff195d3849dcf847d6b0bd927.zip |
branch Attitude Adjustment
git-svn-id: svn://svn.openwrt.org/openwrt/branches/attitude_adjustment@33625 3c298f89-4303-0410-b956-a3cf2f4a3e73
Diffstat (limited to 'target/linux/mcs814x/files-3.3/arch/arm/mach-mcs814x/pci.c')
-rw-r--r-- | target/linux/mcs814x/files-3.3/arch/arm/mach-mcs814x/pci.c | 453 |
1 files changed, 453 insertions, 0 deletions
diff --git a/target/linux/mcs814x/files-3.3/arch/arm/mach-mcs814x/pci.c b/target/linux/mcs814x/files-3.3/arch/arm/mach-mcs814x/pci.c new file mode 100644 index 000000000..9ac5c46d6 --- /dev/null +++ b/target/linux/mcs814x/files-3.3/arch/arm/mach-mcs814x/pci.c @@ -0,0 +1,453 @@ +/* + * Moschip MCS8140 PCI support + * + * Copyright (C) 2003 Moschip Semiconductors Ltd. + * Copyright (C) 2003 Artec Design Ltd. + * Copyright (C) 2012 Florian Fainelli <florian@openwrt.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/ptrace.h> +#include <linux/slab.h> +#include <linux/ioport.h> +#include <linux/interrupt.h> +#include <linux/spinlock.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/io.h> + +#include <asm/irq.h> +#include <asm/system.h> +#include <asm/mach/pci.h> +#include <asm/mach/map.h> +#include <mach/mcs814x.h> +#include <mach/irqs.h> + +#define MCS8140_PCI_CONFIG_SIZE SZ_64M +#define MCS8140_PCI_IOMISC_SIZE SZ_64M + +#define MCS8140_PCI_HOST_BASE 0x80000000 +#define MCS8140_PCI_IOMISC_BASE 0x00000000 +#define MCS8140_PCI_PRE_BASE 0x10000000 +#define MCS8140_PCI_NONPRE_BASE 0x30000000 + +#define MCS8140_PCI_CFG_BASE (MCS8140_PCI_HOST_BASE + 0x04000000) +#define MCS8140_PCI_IO_BASE (MCS8140_PCI_HOST_BASE) + +#define MCS8140_PCI_IO_VIRT_BASE (MCS814X_IO_BASE - MCS8140_PCI_CONFIG_SIZE - \ + MCS8140_PCI_IOMISC_SIZE) +#define MCS8140_PCI_CFG_VIRT_BASE (MCS814X_IO_BASE - MCS8140_PCI_CONFIG_SIZE) + +#define PCI_FATAL_ERROR 1 +#define EXTERNAL_ABORT_NON_LINE_FETCH 8 +#define EPRM_DONE 0x80 +#define EPRM_SDRAM_FUNC0 0xAC +#define PCI_INTD 4 +#define MCS8140_PCI_DEVICE_ID 0xA0009710 +#define MCS8140_PCI_CLASS_ID 0x02000011 /* Host-Class id :0x0600 */ +#define PCI_IF_CONFIG 0x200 + +static void __iomem *mcs8140_pci_master_base; +static void __iomem *mcs8140_eeprom_emu_base; + +static unsigned long __pci_addr(struct pci_bus *bus, + unsigned int devfn, int offset) +{ + unsigned int busnr = bus->number; + unsigned int slot; + + /* we only support bus 0 */ + if (busnr != 0) + return 0; + + /* + * Trap out illegal values + */ + BUG_ON(devfn > 255 || busnr > 255 || devfn > 255); + + /* Scan 3 slots */ + slot = PCI_SLOT(devfn); + switch (slot) { + case 1: + case 2: + case 3: + if (PCI_FUNC(devfn) >= 4) + return 0; + + return MCS8140_PCI_CFG_VIRT_BASE | (PCI_SLOT(devfn) << 11) | + (PCI_FUNC(devfn) << 8) | offset; + default: + pr_warn("Ignoring: PCI Slot is %x\n", PCI_SLOT(devfn)); + return 0; + } +} + +static int mcs8140_pci_host_status(void) +{ + u32 host_status; + + host_status = readl_relaxed(mcs8140_pci_master_base + PCI_IF_CONFIG); + if (host_status & PCI_FATAL_ERROR) { + writel_relaxed(host_status & 0xfffffff0, + mcs8140_pci_master_base + PCI_IF_CONFIG); + /* flush write */ + host_status = + readl_relaxed(mcs8140_pci_master_base + PCI_IF_CONFIG); + return 1; + } + + return 0; +} + +static int mcs8140_pci_read_config(struct pci_bus *bus, + unsigned int devfn, int where, + int size, u32 *val) +{ + unsigned long v = 0xFFFFFFFF; + unsigned long addr = __pci_addr(bus, devfn, where); + + if (addr != 0) { + switch (size) { + case 1: + v = readb_relaxed(addr); + break; + case 2: + addr &= ~1; + v = readw_relaxed(addr); + break; + default: + addr &= ~3; + v = readl_relaxed(addr); + break; + } + } else + v = 0xffffffff; + + if (mcs8140_pci_host_status()) + v = 0xffffffff; + + *val = v; + + return PCIBIOS_SUCCESSFUL; +} + +static void mcs8140_eeprom_emu_init(void) +{ + writel_relaxed(0x0000000F, mcs8140_eeprom_emu_base + EPRM_SDRAM_FUNC0); + writel_relaxed(0x08000000, MCS8140_PCI_CFG_VIRT_BASE + 0x10); + /* Set the DONE bit of the EEPROM emulator */ + writel_relaxed(0x01, mcs8140_eeprom_emu_base + EPRM_DONE); +} + +static int mcs8140_pci_write_config(struct pci_bus *bus, + unsigned int devfn, int where, + int size, u32 val) +{ + unsigned long addr = __pci_addr(bus, devfn, where); + + if (addr != 0) { + switch (size) { + case 1: + writeb_relaxed((u8)val, addr); + break; + case 2: + writew_relaxed((u16)val, addr); + break; + case 4: + writel_relaxed(val, addr); + break; + } + } + + return PCIBIOS_SUCCESSFUL; +} + +static struct pci_ops pci_mcs8140_ops = { + .read = mcs8140_pci_read_config, + .write = mcs8140_pci_write_config, +}; + + +static struct resource io_mem = { + .name = "PCI I/O space", + .start = MCS8140_PCI_HOST_BASE + MCS8140_PCI_IOMISC_BASE, + .end = MCS8140_PCI_HOST_BASE + MCS8140_PCI_IOMISC_BASE + SZ_64M, + .flags = IORESOURCE_IO, +}; + +static struct resource pre_mem = { + .name = "PCI prefetchable", + .start = MCS8140_PCI_HOST_BASE + MCS8140_PCI_PRE_BASE, + .end = MCS8140_PCI_HOST_BASE + MCS8140_PCI_PRE_BASE + SZ_512M, + .flags = IORESOURCE_MEM | IORESOURCE_PREFETCH, +}; + +static struct resource non_mem = { + .name = "PCI non-prefetchable", + .start = MCS8140_PCI_HOST_BASE + MCS8140_PCI_NONPRE_BASE, + .end = MCS8140_PCI_HOST_BASE + MCS8140_PCI_NONPRE_BASE + SZ_256M, + .flags = IORESOURCE_MEM, +}; + +int __init pci_mcs8140_setup_resources(struct pci_sys_data *sys) +{ + int ret = 0; + + ret = request_resource(&iomem_resource, &io_mem); + if (ret) { + pr_err("PCI: unable to allocate I/O " + "memory region (%d)\n", ret); + goto out; + } + + ret = request_resource(&iomem_resource, &non_mem); + if (ret) { + pr_err("PCI: unable to allocate non-prefetchable " + "memory region (%d)\n", ret); + goto release_io_mem; + } + + ret = request_resource(&iomem_resource, &pre_mem); + if (ret) { + pr_err("PCI: unable to allocate prefetchable " + "memory region (%d)\n", ret); + goto release_non_mem; + } + + mcs8140_eeprom_emu_init(); + + pci_add_resource(&sys->resources, &io_mem); + pci_add_resource(&sys->resources, &non_mem); + pci_add_resource(&sys->resources, &pre_mem); + + return ret; + +release_non_mem: + release_resource(&non_mem); +release_io_mem: + release_resource(&io_mem); +out: + return ret; +} + +struct pci_bus *pci_mcs8140_scan_bus(int nr, struct pci_sys_data *sys) +{ + return pci_scan_bus(sys->busnr, &pci_mcs8140_ops, sys); +} + + +int __init pci_mcs8140_setup(int nr, struct pci_sys_data *sys) +{ + int ret = 0; + u32 val; + + if (nr > 0) + return 0; + + sys->mem_offset = MCS8140_PCI_IO_VIRT_BASE - MCS8140_PCI_IO_BASE; + sys->io_offset = 0; + + ret = pci_mcs8140_setup_resources(sys); + if (ret < 0) { + pr_err("unable to setup mcs8140 resources\n"); + goto out; + } + + val = readl_relaxed(MCS8140_PCI_CFG_VIRT_BASE); + if (val != MCS8140_PCI_DEVICE_ID) { + pr_err("cannot find MCS8140 PCI Core: %08x\n", val); + ret = -EIO; + goto out; + } + + pr_info("MCS8140 PCI core found\n"); + + val = readl_relaxed(MCS8140_PCI_CFG_VIRT_BASE + PCI_COMMAND); + /* Added to support wireless cards */ + writel_relaxed(0, MCS8140_PCI_CFG_VIRT_BASE + 0x40); + writel_relaxed(val | 0x147, MCS8140_PCI_CFG_VIRT_BASE + PCI_COMMAND); + val = readl_relaxed(MCS8140_PCI_CFG_VIRT_BASE + PCI_COMMAND); + ret = 1; +out: + return ret; +} + + +static int __init mcs8140_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) +{ + int line = IRQ_PCI_INTA; + + if (pin != 0) { + /* IRQ_PCIA - 22 */ + if (pin == PCI_INTD) + line = IRQ_PCI_INTA + pin; /* IRQ_PCIA - 22 */ + else + line = IRQ_PCI_INTA + pin - 1; /* IRQ_PCIA - 22 */ + } + + pr_info("PCI: Map interrupt slot 0x%02x pin 0x%02x line 0x%02x\n", + slot, pin, line); + + return line; +} + +static irqreturn_t mcs8140_pci_abort_interrupt(int irq, void *dummy) +{ + u32 word; + + word = readl_relaxed(mcs8140_pci_master_base + PCI_IF_CONFIG); + if (!(word & (1 << 24))) + return IRQ_NONE; + + writel_relaxed(word & 0xfffffff0, + mcs8140_pci_master_base + PCI_IF_CONFIG); + /* flush write */ + word = readl_relaxed(mcs8140_pci_master_base + PCI_IF_CONFIG); + + return IRQ_HANDLED; +} + +static int mcs8140_pci_abort_irq_init(int irq) +{ + u32 word; + + /* Enable Interrupt in PCI Master Core */ + word = readl_relaxed(mcs8140_pci_master_base + PCI_IF_CONFIG); + word |= (1 << 24); + writel_relaxed(word, mcs8140_pci_master_base + PCI_IF_CONFIG); + + /* flush write */ + word = readl_relaxed(mcs8140_pci_master_base + PCI_IF_CONFIG); + + return request_irq(irq, mcs8140_pci_abort_interrupt, 0, + "PCI abort", NULL); +} + +static int mcs8140_pci_host_abort(unsigned long addr, + unsigned int fsr, struct pt_regs *regs) +{ + pr_warn("PCI Data abort: address = 0x%08lx fsr = 0x%03x" + "PC = 0x%08lx LR = 0x%08lx\n", + addr, fsr, regs->ARM_pc, regs->ARM_lr); + + /* + * If it was an imprecise abort, then we need to correct the + * return address to be _after_ the instruction. + */ + if (fsr & (1 << 10) || mcs8140_pci_host_status()) + regs->ARM_pc += 4; + + return 0; +} + +static void mcs8140_data_abort_init(void) +{ + hook_fault_code(EXTERNAL_ABORT_NON_LINE_FETCH, + mcs8140_pci_host_abort, SIGBUS, + 0, "external abort on non-line fetch"); +} + +static struct hw_pci mcs8140_pci __initdata = { + .map_irq = mcs8140_map_irq, + .nr_controllers = 1, + .setup = pci_mcs8140_setup, + .scan = pci_mcs8140_scan_bus, +}; + +static struct map_desc mcs8140_pci_io_desc[] __initdata = { + { + .virtual = MCS8140_PCI_CFG_VIRT_BASE, + .pfn = __phys_to_pfn(MCS8140_PCI_CFG_BASE), + .length = MCS8140_PCI_CONFIG_SIZE, + .type = MT_DEVICE + }, + { + .virtual = MCS8140_PCI_IO_VIRT_BASE, + .pfn = __phys_to_pfn(MCS8140_PCI_IO_BASE), + .length = MCS8140_PCI_IOMISC_SIZE, + .type = MT_DEVICE + }, +}; + +static int __devinit mcs8140_pci_probe(struct platform_device *pdev) +{ + struct resource *res; + int ret, irq; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "failed to get mem resource 0\n"); + return -ENODEV; + } + + mcs8140_pci_master_base = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + if (!mcs8140_pci_master_base) { + dev_err(&pdev->dev, "failed to remap PCI master regs\n"); + return -ENODEV; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res) { + dev_err(&pdev->dev, "failed to get mem resource 1\n"); + return -ENOMEM; + } + + mcs8140_eeprom_emu_base = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + if (!mcs8140_eeprom_emu_base) { + dev_err(&pdev->dev, "failed to remap EEPROM regs\n"); + return -ENOMEM; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "failed to get pci abort irq\n"); + return -ENODEV; + } + + /* Setup static mappins for PCI CFG space */ + iotable_init(mcs8140_pci_io_desc, ARRAY_SIZE(mcs8140_pci_io_desc)); + + pcibios_min_io = MCS8140_PCI_HOST_BASE; + pcibios_min_mem = MCS8140_PCI_HOST_BASE + MCS8140_PCI_PRE_BASE; + + mcs8140_data_abort_init(); + ret = mcs8140_pci_abort_irq_init(irq); + if (ret) { + dev_err(&pdev->dev, "failed to setup abort irq\n"); + return ret; + } + + pci_common_init(&mcs8140_pci); + + return 0; +} + +static struct of_device_id mcs8140_of_ids[] __devinitdata = { + { .compatible = "moschip,mcs8140-pci" }, + { .compatible = "moschip,mcs814x-pci" }, + { /* sentinel */ }, +}; + +static struct platform_driver mcs8140_pci_driver = { + .driver = { + .name = "mcs8140-pci", + .of_match_table = mcs8140_of_ids, + }, + .probe = mcs8140_pci_probe, +}; + +static int __init mcs8140_pci_init(void) +{ + return platform_driver_register(&mcs8140_pci_driver); +} +subsys_initcall(mcs8140_pci_init); + |