diff options
Diffstat (limited to 'arch/mips/pci')
-rw-r--r-- | arch/mips/pci/Makefile | 6 | ||||
-rw-r--r-- | arch/mips/pci/fixup-lantiq.c | 40 | ||||
-rw-r--r-- | arch/mips/pci/ops-loongson2.c | 1 | ||||
-rw-r--r-- | arch/mips/pci/pci-ar71xx.c | 375 | ||||
-rw-r--r-- | arch/mips/pci/pci-ar724x.c | 292 | ||||
-rw-r--r-- | arch/mips/pci/pci-ath724x.c | 174 | ||||
-rw-r--r-- | arch/mips/pci/pci-lantiq.c | 219 | ||||
-rw-r--r-- | arch/mips/pci/pci.c | 55 |
8 files changed, 852 insertions, 310 deletions
diff --git a/arch/mips/pci/Makefile b/arch/mips/pci/Makefile index c3ac4b086eb2..c703f43a9914 100644 --- a/arch/mips/pci/Makefile +++ b/arch/mips/pci/Makefile @@ -19,7 +19,8 @@ obj-$(CONFIG_BCM47XX) += pci-bcm47xx.o obj-$(CONFIG_BCM63XX) += pci-bcm63xx.o fixup-bcm63xx.o \ ops-bcm63xx.o obj-$(CONFIG_MIPS_ALCHEMY) += pci-alchemy.o -obj-$(CONFIG_SOC_AR724X) += pci-ath724x.o +obj-$(CONFIG_SOC_AR71XX) += pci-ar71xx.o +obj-$(CONFIG_PCI_AR724X) += pci-ar724x.o # # These are still pretty much in the old state, watch, go blind. @@ -41,7 +42,8 @@ obj-$(CONFIG_SIBYTE_SB1250) += fixup-sb1250.o pci-sb1250.o obj-$(CONFIG_SIBYTE_BCM112X) += fixup-sb1250.o pci-sb1250.o obj-$(CONFIG_SIBYTE_BCM1x80) += pci-bcm1480.o pci-bcm1480ht.o obj-$(CONFIG_SNI_RM) += fixup-sni.o ops-sni.o -obj-$(CONFIG_SOC_XWAY) += pci-lantiq.o ops-lantiq.o +obj-$(CONFIG_LANTIQ) += fixup-lantiq.o +obj-$(CONFIG_PCI_LANTIQ) += pci-lantiq.o ops-lantiq.o obj-$(CONFIG_TANBAC_TB0219) += fixup-tb0219.o obj-$(CONFIG_TANBAC_TB0226) += fixup-tb0226.o obj-$(CONFIG_TANBAC_TB0287) += fixup-tb0287.o diff --git a/arch/mips/pci/fixup-lantiq.c b/arch/mips/pci/fixup-lantiq.c new file mode 100644 index 000000000000..6c829df28dc7 --- /dev/null +++ b/arch/mips/pci/fixup-lantiq.c @@ -0,0 +1,40 @@ +/* + * 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. + * + * Copyright (C) 2012 John Crispin <blogic@openwrt.org> + */ + +#include <linux/of_irq.h> +#include <linux/of_pci.h> + +int (*ltq_pci_plat_arch_init)(struct pci_dev *dev) = NULL; +int (*ltq_pci_plat_dev_init)(struct pci_dev *dev) = NULL; + +int pcibios_plat_dev_init(struct pci_dev *dev) +{ + if (ltq_pci_plat_arch_init) + return ltq_pci_plat_arch_init(dev); + + if (ltq_pci_plat_dev_init) + return ltq_pci_plat_dev_init(dev); + + return 0; +} + +int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) +{ + struct of_irq dev_irq; + int irq; + + if (of_irq_map_pci(dev, &dev_irq)) { + dev_err(&dev->dev, "trying to map irq for unknown slot:%d pin:%d\n", + slot, pin); + return 0; + } + irq = irq_create_of_mapping(dev_irq.controller, dev_irq.specifier, + dev_irq.size); + dev_info(&dev->dev, "SLOT:%d PIN:%d IRQ:%d\n", slot, pin, irq); + return irq; +} diff --git a/arch/mips/pci/ops-loongson2.c b/arch/mips/pci/ops-loongson2.c index d657ee0bc131..afd221122d22 100644 --- a/arch/mips/pci/ops-loongson2.c +++ b/arch/mips/pci/ops-loongson2.c @@ -15,6 +15,7 @@ #include <linux/pci.h> #include <linux/kernel.h> #include <linux/init.h> +#include <linux/export.h> #include <loongson.h> diff --git a/arch/mips/pci/pci-ar71xx.c b/arch/mips/pci/pci-ar71xx.c new file mode 100644 index 000000000000..1552522b8718 --- /dev/null +++ b/arch/mips/pci/pci-ar71xx.c @@ -0,0 +1,375 @@ +/* + * Atheros AR71xx PCI host controller driver + * + * Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org> + * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> + * + * Parts of this file are based on Atheros' 2.6.15 BSP + * + * 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/resource.h> +#include <linux/types.h> +#include <linux/delay.h> +#include <linux/bitops.h> +#include <linux/pci.h> +#include <linux/pci_regs.h> +#include <linux/interrupt.h> + +#include <asm/mach-ath79/ar71xx_regs.h> +#include <asm/mach-ath79/ath79.h> +#include <asm/mach-ath79/pci.h> + +#define AR71XX_PCI_MEM_BASE 0x10000000 +#define AR71XX_PCI_MEM_SIZE 0x08000000 + +#define AR71XX_PCI_WIN0_OFFS 0x10000000 +#define AR71XX_PCI_WIN1_OFFS 0x11000000 +#define AR71XX_PCI_WIN2_OFFS 0x12000000 +#define AR71XX_PCI_WIN3_OFFS 0x13000000 +#define AR71XX_PCI_WIN4_OFFS 0x14000000 +#define AR71XX_PCI_WIN5_OFFS 0x15000000 +#define AR71XX_PCI_WIN6_OFFS 0x16000000 +#define AR71XX_PCI_WIN7_OFFS 0x07000000 + +#define AR71XX_PCI_CFG_BASE \ + (AR71XX_PCI_MEM_BASE + AR71XX_PCI_WIN7_OFFS + 0x10000) +#define AR71XX_PCI_CFG_SIZE 0x100 + +#define AR71XX_PCI_REG_CRP_AD_CBE 0x00 +#define AR71XX_PCI_REG_CRP_WRDATA 0x04 +#define AR71XX_PCI_REG_CRP_RDDATA 0x08 +#define AR71XX_PCI_REG_CFG_AD 0x0c +#define AR71XX_PCI_REG_CFG_CBE 0x10 +#define AR71XX_PCI_REG_CFG_WRDATA 0x14 +#define AR71XX_PCI_REG_CFG_RDDATA 0x18 +#define AR71XX_PCI_REG_PCI_ERR 0x1c +#define AR71XX_PCI_REG_PCI_ERR_ADDR 0x20 +#define AR71XX_PCI_REG_AHB_ERR 0x24 +#define AR71XX_PCI_REG_AHB_ERR_ADDR 0x28 + +#define AR71XX_PCI_CRP_CMD_WRITE 0x00010000 +#define AR71XX_PCI_CRP_CMD_READ 0x00000000 +#define AR71XX_PCI_CFG_CMD_READ 0x0000000a +#define AR71XX_PCI_CFG_CMD_WRITE 0x0000000b + +#define AR71XX_PCI_INT_CORE BIT(4) +#define AR71XX_PCI_INT_DEV2 BIT(2) +#define AR71XX_PCI_INT_DEV1 BIT(1) +#define AR71XX_PCI_INT_DEV0 BIT(0) + +#define AR71XX_PCI_IRQ_COUNT 5 + +static DEFINE_SPINLOCK(ar71xx_pci_lock); +static void __iomem *ar71xx_pcicfg_base; + +/* Byte lane enable bits */ +static const u8 ar71xx_pci_ble_table[4][4] = { + {0x0, 0xf, 0xf, 0xf}, + {0xe, 0xd, 0xb, 0x7}, + {0xc, 0xf, 0x3, 0xf}, + {0xf, 0xf, 0xf, 0xf}, +}; + +static const u32 ar71xx_pci_read_mask[8] = { + 0, 0xff, 0xffff, 0, 0xffffffff, 0, 0, 0 +}; + +static inline u32 ar71xx_pci_get_ble(int where, int size, int local) +{ + u32 t; + + t = ar71xx_pci_ble_table[size & 3][where & 3]; + BUG_ON(t == 0xf); + t <<= (local) ? 20 : 4; + + return t; +} + +static inline u32 ar71xx_pci_bus_addr(struct pci_bus *bus, unsigned int devfn, + int where) +{ + u32 ret; + + if (!bus->number) { + /* type 0 */ + ret = (1 << PCI_SLOT(devfn)) | (PCI_FUNC(devfn) << 8) | + (where & ~3); + } else { + /* type 1 */ + ret = (bus->number << 16) | (PCI_SLOT(devfn) << 11) | + (PCI_FUNC(devfn) << 8) | (where & ~3) | 1; + } + + return ret; +} + +static int ar71xx_pci_check_error(int quiet) +{ + void __iomem *base = ar71xx_pcicfg_base; + u32 pci_err; + u32 ahb_err; + + pci_err = __raw_readl(base + AR71XX_PCI_REG_PCI_ERR) & 3; + if (pci_err) { + if (!quiet) { + u32 addr; + + addr = __raw_readl(base + AR71XX_PCI_REG_PCI_ERR_ADDR); + pr_crit("ar71xx: %s bus error %d at addr 0x%x\n", + "PCI", pci_err, addr); + } + + /* clear PCI error status */ + __raw_writel(pci_err, base + AR71XX_PCI_REG_PCI_ERR); + } + + ahb_err = __raw_readl(base + AR71XX_PCI_REG_AHB_ERR) & 1; + if (ahb_err) { + if (!quiet) { + u32 addr; + + addr = __raw_readl(base + AR71XX_PCI_REG_AHB_ERR_ADDR); + pr_crit("ar71xx: %s bus error %d at addr 0x%x\n", + "AHB", ahb_err, addr); + } + + /* clear AHB error status */ + __raw_writel(ahb_err, base + AR71XX_PCI_REG_AHB_ERR); + } + + return !!(ahb_err | pci_err); +} + +static inline void ar71xx_pci_local_write(int where, int size, u32 value) +{ + void __iomem *base = ar71xx_pcicfg_base; + u32 ad_cbe; + + value = value << (8 * (where & 3)); + + ad_cbe = AR71XX_PCI_CRP_CMD_WRITE | (where & ~3); + ad_cbe |= ar71xx_pci_get_ble(where, size, 1); + + __raw_writel(ad_cbe, base + AR71XX_PCI_REG_CRP_AD_CBE); + __raw_writel(value, base + AR71XX_PCI_REG_CRP_WRDATA); +} + +static inline int ar71xx_pci_set_cfgaddr(struct pci_bus *bus, + unsigned int devfn, + int where, int size, u32 cmd) +{ + void __iomem *base = ar71xx_pcicfg_base; + u32 addr; + + addr = ar71xx_pci_bus_addr(bus, devfn, where); + + __raw_writel(addr, base + AR71XX_PCI_REG_CFG_AD); + __raw_writel(cmd | ar71xx_pci_get_ble(where, size, 0), + base + AR71XX_PCI_REG_CFG_CBE); + + return ar71xx_pci_check_error(1); +} + +static int ar71xx_pci_read_config(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *value) +{ + void __iomem *base = ar71xx_pcicfg_base; + unsigned long flags; + u32 data; + int err; + int ret; + + ret = PCIBIOS_SUCCESSFUL; + data = ~0; + + spin_lock_irqsave(&ar71xx_pci_lock, flags); + + err = ar71xx_pci_set_cfgaddr(bus, devfn, where, size, + AR71XX_PCI_CFG_CMD_READ); + if (err) + ret = PCIBIOS_DEVICE_NOT_FOUND; + else + data = __raw_readl(base + AR71XX_PCI_REG_CFG_RDDATA); + + spin_unlock_irqrestore(&ar71xx_pci_lock, flags); + + *value = (data >> (8 * (where & 3))) & ar71xx_pci_read_mask[size & 7]; + + return ret; +} + +static int ar71xx_pci_write_config(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 value) +{ + void __iomem *base = ar71xx_pcicfg_base; + unsigned long flags; + int err; + int ret; + + value = value << (8 * (where & 3)); + ret = PCIBIOS_SUCCESSFUL; + + spin_lock_irqsave(&ar71xx_pci_lock, flags); + + err = ar71xx_pci_set_cfgaddr(bus, devfn, where, size, + AR71XX_PCI_CFG_CMD_WRITE); + if (err) + ret = PCIBIOS_DEVICE_NOT_FOUND; + else + __raw_writel(value, base + AR71XX_PCI_REG_CFG_WRDATA); + + spin_unlock_irqrestore(&ar71xx_pci_lock, flags); + + return ret; +} + +static struct pci_ops ar71xx_pci_ops = { + .read = ar71xx_pci_read_config, + .write = ar71xx_pci_write_config, +}; + +static struct resource ar71xx_pci_io_resource = { + .name = "PCI IO space", + .start = 0, + .end = 0, + .flags = IORESOURCE_IO, +}; + +static struct resource ar71xx_pci_mem_resource = { + .name = "PCI memory space", + .start = AR71XX_PCI_MEM_BASE, + .end = AR71XX_PCI_MEM_BASE + AR71XX_PCI_MEM_SIZE - 1, + .flags = IORESOURCE_MEM +}; + +static struct pci_controller ar71xx_pci_controller = { + .pci_ops = &ar71xx_pci_ops, + .mem_resource = &ar71xx_pci_mem_resource, + .io_resource = &ar71xx_pci_io_resource, +}; + +static void ar71xx_pci_irq_handler(unsigned int irq, struct irq_desc *desc) +{ + void __iomem *base = ath79_reset_base; + u32 pending; + + pending = __raw_readl(base + AR71XX_RESET_REG_PCI_INT_STATUS) & + __raw_readl(base + AR71XX_RESET_REG_PCI_INT_ENABLE); + + if (pending & AR71XX_PCI_INT_DEV0) + generic_handle_irq(ATH79_PCI_IRQ(0)); + + else if (pending & AR71XX_PCI_INT_DEV1) + generic_handle_irq(ATH79_PCI_IRQ(1)); + + else if (pending & AR71XX_PCI_INT_DEV2) + generic_handle_irq(ATH79_PCI_IRQ(2)); + + else if (pending & AR71XX_PCI_INT_CORE) + generic_handle_irq(ATH79_PCI_IRQ(4)); + + else + spurious_interrupt(); +} + +static void ar71xx_pci_irq_unmask(struct irq_data *d) +{ + unsigned int irq = d->irq - ATH79_PCI_IRQ_BASE; + void __iomem *base = ath79_reset_base; + u32 t; + + t = __raw_readl(base + AR71XX_RESET_REG_PCI_INT_ENABLE); + __raw_writel(t | (1 << irq), base + AR71XX_RESET_REG_PCI_INT_ENABLE); + + /* flush write */ + __raw_readl(base + AR71XX_RESET_REG_PCI_INT_ENABLE); +} + +static void ar71xx_pci_irq_mask(struct irq_data *d) +{ + unsigned int irq = d->irq - ATH79_PCI_IRQ_BASE; + void __iomem *base = ath79_reset_base; + u32 t; + + t = __raw_readl(base + AR71XX_RESET_REG_PCI_INT_ENABLE); + __raw_writel(t & ~(1 << irq), base + AR71XX_RESET_REG_PCI_INT_ENABLE); + + /* flush write */ + __raw_readl(base + AR71XX_RESET_REG_PCI_INT_ENABLE); +} + +static struct irq_chip ar71xx_pci_irq_chip = { + .name = "AR71XX PCI", + .irq_mask = ar71xx_pci_irq_mask, + .irq_unmask = ar71xx_pci_irq_unmask, + .irq_mask_ack = ar71xx_pci_irq_mask, +}; + +static __init void ar71xx_pci_irq_init(void) +{ + void __iomem *base = ath79_reset_base; + int i; + + __raw_writel(0, base + AR71XX_RESET_REG_PCI_INT_ENABLE); + __raw_writel(0, base + AR71XX_RESET_REG_PCI_INT_STATUS); + + BUILD_BUG_ON(ATH79_PCI_IRQ_COUNT < AR71XX_PCI_IRQ_COUNT); + + for (i = ATH79_PCI_IRQ_BASE; + i < ATH79_PCI_IRQ_BASE + AR71XX_PCI_IRQ_COUNT; i++) + irq_set_chip_and_handler(i, &ar71xx_pci_irq_chip, + handle_level_irq); + + irq_set_chained_handler(ATH79_CPU_IRQ_IP2, ar71xx_pci_irq_handler); +} + +static __init void ar71xx_pci_reset(void) +{ + void __iomem *ddr_base = ath79_ddr_base; + + ath79_device_reset_set(AR71XX_RESET_PCI_BUS | AR71XX_RESET_PCI_CORE); + mdelay(100); + + ath79_device_reset_clear(AR71XX_RESET_PCI_BUS | AR71XX_RESET_PCI_CORE); + mdelay(100); + + __raw_writel(AR71XX_PCI_WIN0_OFFS, ddr_base + AR71XX_DDR_REG_PCI_WIN0); + __raw_writel(AR71XX_PCI_WIN1_OFFS, ddr_base + AR71XX_DDR_REG_PCI_WIN1); + __raw_writel(AR71XX_PCI_WIN2_OFFS, ddr_base + AR71XX_DDR_REG_PCI_WIN2); + __raw_writel(AR71XX_PCI_WIN3_OFFS, ddr_base + AR71XX_DDR_REG_PCI_WIN3); + __raw_writel(AR71XX_PCI_WIN4_OFFS, ddr_base + AR71XX_DDR_REG_PCI_WIN4); + __raw_writel(AR71XX_PCI_WIN5_OFFS, ddr_base + AR71XX_DDR_REG_PCI_WIN5); + __raw_writel(AR71XX_PCI_WIN6_OFFS, ddr_base + AR71XX_DDR_REG_PCI_WIN6); + __raw_writel(AR71XX_PCI_WIN7_OFFS, ddr_base + AR71XX_DDR_REG_PCI_WIN7); + + mdelay(100); +} + +__init int ar71xx_pcibios_init(void) +{ + u32 t; + + ar71xx_pcicfg_base = ioremap(AR71XX_PCI_CFG_BASE, AR71XX_PCI_CFG_SIZE); + if (ar71xx_pcicfg_base == NULL) + return -ENOMEM; + + ar71xx_pci_reset(); + + /* setup COMMAND register */ + t = PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE + | PCI_COMMAND_PARITY | PCI_COMMAND_SERR | PCI_COMMAND_FAST_BACK; + ar71xx_pci_local_write(PCI_COMMAND, 4, t); + + /* clear bus errors */ + ar71xx_pci_check_error(1); + + ar71xx_pci_irq_init(); + + register_pci_controller(&ar71xx_pci_controller); + + return 0; +} diff --git a/arch/mips/pci/pci-ar724x.c b/arch/mips/pci/pci-ar724x.c new file mode 100644 index 000000000000..414a7459858d --- /dev/null +++ b/arch/mips/pci/pci-ar724x.c @@ -0,0 +1,292 @@ +/* + * Atheros AR724X PCI host controller driver + * + * Copyright (C) 2011 René Bolldorf <xsecute@googlemail.com> + * Copyright (C) 2009-2011 Gabor Juhos <juhosg@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/irq.h> +#include <linux/pci.h> +#include <asm/mach-ath79/ath79.h> +#include <asm/mach-ath79/ar71xx_regs.h> +#include <asm/mach-ath79/pci.h> + +#define AR724X_PCI_CFG_BASE 0x14000000 +#define AR724X_PCI_CFG_SIZE 0x1000 +#define AR724X_PCI_CTRL_BASE (AR71XX_APB_BASE + 0x000f0000) +#define AR724X_PCI_CTRL_SIZE 0x100 + +#define AR724X_PCI_MEM_BASE 0x10000000 +#define AR724X_PCI_MEM_SIZE 0x08000000 + +#define AR724X_PCI_REG_INT_STATUS 0x4c +#define AR724X_PCI_REG_INT_MASK 0x50 + +#define AR724X_PCI_INT_DEV0 BIT(14) + +#define AR724X_PCI_IRQ_COUNT 1 + +#define AR7240_BAR0_WAR_VALUE 0xffff + +static DEFINE_SPINLOCK(ar724x_pci_lock); +static void __iomem *ar724x_pci_devcfg_base; +static void __iomem *ar724x_pci_ctrl_base; + +static u32 ar724x_pci_bar0_value; +static bool ar724x_pci_bar0_is_cached; + +static int ar724x_pci_read(struct pci_bus *bus, unsigned int devfn, int where, + int size, uint32_t *value) +{ + unsigned long flags; + void __iomem *base; + u32 data; + + if (devfn) + return PCIBIOS_DEVICE_NOT_FOUND; + + base = ar724x_pci_devcfg_base; + + spin_lock_irqsave(&ar724x_pci_lock, flags); + data = __raw_readl(base + (where & ~3)); + + switch (size) { + case 1: + if (where & 1) + data >>= 8; + if (where & 2) + data >>= 16; + data &= 0xff; + break; + case 2: + if (where & 2) + data >>= 16; + data &= 0xffff; + break; + case 4: + break; + default: + spin_unlock_irqrestore(&ar724x_pci_lock, flags); + + return PCIBIOS_BAD_REGISTER_NUMBER; + } + + spin_unlock_irqrestore(&ar724x_pci_lock, flags); + + if (where == PCI_BASE_ADDRESS_0 && size == 4 && + ar724x_pci_bar0_is_cached) { + /* use the cached value */ + *value = ar724x_pci_bar0_value; + } else { + *value = data; + } + + return PCIBIOS_SUCCESSFUL; +} + +static int ar724x_pci_write(struct pci_bus *bus, unsigned int devfn, int where, + int size, uint32_t value) +{ + unsigned long flags; + void __iomem *base; + u32 data; + int s; + + if (devfn) + return PCIBIOS_DEVICE_NOT_FOUND; + + if (soc_is_ar7240() && where == PCI_BASE_ADDRESS_0 && size == 4) { + if (value != 0xffffffff) { + /* + * WAR for a hw issue. If the BAR0 register of the + * device is set to the proper base address, the + * memory space of the device is not accessible. + * + * Cache the intended value so it can be read back, + * and write a SoC specific constant value to the + * BAR0 register in order to make the device memory + * accessible. + */ + ar724x_pci_bar0_is_cached = true; + ar724x_pci_bar0_value = value; + + value = AR7240_BAR0_WAR_VALUE; + } else { + ar724x_pci_bar0_is_cached = false; + } + } + + base = ar724x_pci_devcfg_base; + + spin_lock_irqsave(&ar724x_pci_lock, flags); + data = __raw_readl(base + (where & ~3)); + + switch (size) { + case 1: + s = ((where & 3) * 8); + data &= ~(0xff << s); + data |= ((value & 0xff) << s); + break; + case 2: + s = ((where & 2) * 8); + data &= ~(0xffff << s); + data |= ((value & 0xffff) << s); + break; + case 4: + data = value; + break; + default: + spin_unlock_irqrestore(&ar724x_pci_lock, flags); + + return PCIBIOS_BAD_REGISTER_NUMBER; + } + + __raw_writel(data, base + (where & ~3)); + /* flush write */ + __raw_readl(base + (where & ~3)); + spin_unlock_irqrestore(&ar724x_pci_lock, flags); + + return PCIBIOS_SUCCESSFUL; +} + +static struct pci_ops ar724x_pci_ops = { + .read = ar724x_pci_read, + .write = ar724x_pci_write, +}; + +static struct resource ar724x_io_resource = { + .name = "PCI IO space", + .start = 0, + .end = 0, + .flags = IORESOURCE_IO, +}; + +static struct resource ar724x_mem_resource = { + .name = "PCI memory space", + .start = AR724X_PCI_MEM_BASE, + .end = AR724X_PCI_MEM_BASE + AR724X_PCI_MEM_SIZE - 1, + .flags = IORESOURCE_MEM, +}; + +static struct pci_controller ar724x_pci_controller = { + .pci_ops = &ar724x_pci_ops, + .io_resource = &ar724x_io_resource, + .mem_resource = &ar724x_mem_resource, +}; + +static void ar724x_pci_irq_handler(unsigned int irq, struct irq_desc *desc) +{ + void __iomem *base; + u32 pending; + + base = ar724x_pci_ctrl_base; + + pending = __raw_readl(base + AR724X_PCI_REG_INT_STATUS) & + __raw_readl(base + AR724X_PCI_REG_INT_MASK); + + if (pending & AR724X_PCI_INT_DEV0) + generic_handle_irq(ATH79_PCI_IRQ(0)); + + else + spurious_interrupt(); +} + +static void ar724x_pci_irq_unmask(struct irq_data *d) +{ + void __iomem *base; + u32 t; + + base = ar724x_pci_ctrl_base; + + switch (d->irq) { + case ATH79_PCI_IRQ(0): + t = __raw_readl(base + AR724X_PCI_REG_INT_MASK); + __raw_writel(t | AR724X_PCI_INT_DEV0, + base + AR724X_PCI_REG_INT_MASK); + /* flush write */ + __raw_readl(base + AR724X_PCI_REG_INT_MASK); + } +} + +static void ar724x_pci_irq_mask(struct irq_data *d) +{ + void __iomem *base; + u32 t; + + base = ar724x_pci_ctrl_base; + + switch (d->irq) { + case ATH79_PCI_IRQ(0): + t = __raw_readl(base + AR724X_PCI_REG_INT_MASK); + __raw_writel(t & ~AR724X_PCI_INT_DEV0, + base + AR724X_PCI_REG_INT_MASK); + + /* flush write */ + __raw_readl(base + AR724X_PCI_REG_INT_MASK); + + t = __raw_readl(base + AR724X_PCI_REG_INT_STATUS); + __raw_writel(t | AR724X_PCI_INT_DEV0, + base + AR724X_PCI_REG_INT_STATUS); + + /* flush write */ + __raw_readl(base + AR724X_PCI_REG_INT_STATUS); + } +} + +static struct irq_chip ar724x_pci_irq_chip = { + .name = "AR724X PCI ", + .irq_mask = ar724x_pci_irq_mask, + .irq_unmask = ar724x_pci_irq_unmask, + .irq_mask_ack = ar724x_pci_irq_mask, +}; + +static void __init ar724x_pci_irq_init(int irq) +{ + void __iomem *base; + int i; + + base = ar724x_pci_ctrl_base; + + __raw_writel(0, base + AR724X_PCI_REG_INT_MASK); + __raw_writel(0, base + AR724X_PCI_REG_INT_STATUS); + + BUILD_BUG_ON(ATH79_PCI_IRQ_COUNT < AR724X_PCI_IRQ_COUNT); + + for (i = ATH79_PCI_IRQ_BASE; + i < ATH79_PCI_IRQ_BASE + AR724X_PCI_IRQ_COUNT; i++) + irq_set_chip_and_handler(i, &ar724x_pci_irq_chip, + handle_level_irq); + + irq_set_chained_handler(irq, ar724x_pci_irq_handler); +} + +int __init ar724x_pcibios_init(int irq) +{ + int ret; + + ret = -ENOMEM; + + ar724x_pci_devcfg_base = ioremap(AR724X_PCI_CFG_BASE, + AR724X_PCI_CFG_SIZE); + if (ar724x_pci_devcfg_base == NULL) + goto err; + + ar724x_pci_ctrl_base = ioremap(AR724X_PCI_CTRL_BASE, + AR724X_PCI_CTRL_SIZE); + if (ar724x_pci_ctrl_base == NULL) + goto err_unmap_devcfg; + + ar724x_pci_irq_init(irq); + register_pci_controller(&ar724x_pci_controller); + + return PCIBIOS_SUCCESSFUL; + +err_unmap_devcfg: + iounmap(ar724x_pci_devcfg_base); +err: + return ret; +} diff --git a/arch/mips/pci/pci-ath724x.c b/arch/mips/pci/pci-ath724x.c deleted file mode 100644 index a4dd24a4130b..000000000000 --- a/arch/mips/pci/pci-ath724x.c +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Atheros 724x PCI support - * - * Copyright (C) 2011 René Bolldorf <xsecute@googlemail.com> - * - * 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/pci.h> -#include <asm/mach-ath79/pci-ath724x.h> - -#define reg_read(_phys) (*(unsigned int *) KSEG1ADDR(_phys)) -#define reg_write(_phys, _val) ((*(unsigned int *) KSEG1ADDR(_phys)) = (_val)) - -#define ATH724X_PCI_DEV_BASE 0x14000000 -#define ATH724X_PCI_MEM_BASE 0x10000000 -#define ATH724X_PCI_MEM_SIZE 0x08000000 - -static DEFINE_SPINLOCK(ath724x_pci_lock); -static struct ath724x_pci_data *pci_data; -static int pci_data_size; - -static int ath724x_pci_read(struct pci_bus *bus, unsigned int devfn, int where, - int size, uint32_t *value) -{ - unsigned long flags, addr, tval, mask; - - if (devfn) - return PCIBIOS_DEVICE_NOT_FOUND; - - if (where & (size - 1)) - return PCIBIOS_BAD_REGISTER_NUMBER; - - spin_lock_irqsave(&ath724x_pci_lock, flags); - - switch (size) { - case 1: - addr = where & ~3; - mask = 0xff000000 >> ((where % 4) * 8); - tval = reg_read(ATH724X_PCI_DEV_BASE + addr); - tval = tval & ~mask; - *value = (tval >> ((4 - (where % 4))*8)); - break; - case 2: - addr = where & ~3; - mask = 0xffff0000 >> ((where % 4)*8); - tval = reg_read(ATH724X_PCI_DEV_BASE + addr); - tval = tval & ~mask; - *value = (tval >> ((4 - (where % 4))*8)); - break; - case 4: - *value = reg_read(ATH724X_PCI_DEV_BASE + where); - break; - default: - spin_unlock_irqrestore(&ath724x_pci_lock, flags); - - return PCIBIOS_BAD_REGISTER_NUMBER; - } - - spin_unlock_irqrestore(&ath724x_pci_lock, flags); - - return PCIBIOS_SUCCESSFUL; -} - -static int ath724x_pci_write(struct pci_bus *bus, unsigned int devfn, int where, - int size, uint32_t value) -{ - unsigned long flags, tval, addr, mask; - - if (devfn) - return PCIBIOS_DEVICE_NOT_FOUND; - - if (where & (size - 1)) - return PCIBIOS_BAD_REGISTER_NUMBER; - - spin_lock_irqsave(&ath724x_pci_lock, flags); - - switch (size) { - case 1: - addr = (ATH724X_PCI_DEV_BASE + where) & ~3; - mask = 0xff000000 >> ((where % 4)*8); - tval = reg_read(addr); - tval = tval & ~mask; - tval |= (value << ((4 - (where % 4))*8)) & mask; - reg_write(addr, tval); - break; - case 2: - addr = (ATH724X_PCI_DEV_BASE + where) & ~3; - mask = 0xffff0000 >> ((where % 4)*8); - tval = reg_read(addr); - tval = tval & ~mask; - tval |= (value << ((4 - (where % 4))*8)) & mask; - reg_write(addr, tval); - break; - case 4: - reg_write((ATH724X_PCI_DEV_BASE + where), value); - break; - default: - spin_unlock_irqrestore(&ath724x_pci_lock, flags); - - return PCIBIOS_BAD_REGISTER_NUMBER; - } - - spin_unlock_irqrestore(&ath724x_pci_lock, flags); - - return PCIBIOS_SUCCESSFUL; -} - -static struct pci_ops ath724x_pci_ops = { - .read = ath724x_pci_read, - .write = ath724x_pci_write, -}; - -static struct resource ath724x_io_resource = { - .name = "PCI IO space", - .start = 0, - .end = 0, - .flags = IORESOURCE_IO, -}; - -static struct resource ath724x_mem_resource = { - .name = "PCI memory space", - .start = ATH724X_PCI_MEM_BASE, - .end = ATH724X_PCI_MEM_BASE + ATH724X_PCI_MEM_SIZE - 1, - .flags = IORESOURCE_MEM, -}; - -static struct pci_controller ath724x_pci_controller = { - .pci_ops = &ath724x_pci_ops, - .io_resource = &ath724x_io_resource, - .mem_resource = &ath724x_mem_resource, -}; - -void ath724x_pci_add_data(struct ath724x_pci_data *data, int size) -{ - pci_data = data; - pci_data_size = size; -} - -int __init pcibios_map_irq(const struct pci_dev *dev, uint8_t slot, uint8_t pin) -{ - unsigned int devfn = dev->devfn; - int irq = -1; - - if (devfn > pci_data_size - 1) - return irq; - - irq = pci_data[devfn].irq; - - return irq; -} - -int pcibios_plat_dev_init(struct pci_dev *dev) -{ - unsigned int devfn = dev->devfn; - - if (devfn > pci_data_size - 1) - return PCIBIOS_DEVICE_NOT_FOUND; - - dev->dev.platform_data = pci_data[devfn].pdata; - - return PCIBIOS_SUCCESSFUL; -} - -static int __init ath724x_pcibios_init(void) -{ - register_pci_controller(&ath724x_pci_controller); - - return PCIBIOS_SUCCESSFUL; -} - -arch_initcall(ath724x_pcibios_init); diff --git a/arch/mips/pci/pci-lantiq.c b/arch/mips/pci/pci-lantiq.c index 030c77e7926e..ea453532a33c 100644 --- a/arch/mips/pci/pci-lantiq.c +++ b/arch/mips/pci/pci-lantiq.c @@ -13,8 +13,12 @@ #include <linux/delay.h> #include <linux/mm.h> #include <linux/vmalloc.h> -#include <linux/export.h> -#include <linux/platform_device.h> +#include <linux/module.h> +#include <linux/clk.h> +#include <linux/of_platform.h> +#include <linux/of_gpio.h> +#include <linux/of_irq.h> +#include <linux/of_pci.h> #include <asm/pci.h> #include <asm/gpio.h> @@ -22,17 +26,9 @@ #include <lantiq_soc.h> #include <lantiq_irq.h> -#include <lantiq_platform.h> #include "pci-lantiq.h" -#define LTQ_PCI_CFG_BASE 0x17000000 -#define LTQ_PCI_CFG_SIZE 0x00008000 -#define LTQ_PCI_MEM_BASE 0x18000000 -#define LTQ_PCI_MEM_SIZE 0x02000000 -#define LTQ_PCI_IO_BASE 0x1AE00000 -#define LTQ_PCI_IO_SIZE 0x00200000 - #define PCI_CR_FCI_ADDR_MAP0 0x00C0 #define PCI_CR_FCI_ADDR_MAP1 0x00C4 #define PCI_CR_FCI_ADDR_MAP2 0x00C8 @@ -68,79 +64,27 @@ #define ltq_pci_cfg_w32(x, y) ltq_w32((x), ltq_pci_mapped_cfg + (y)) #define ltq_pci_cfg_r32(x) ltq_r32(ltq_pci_mapped_cfg + (x)) -struct ltq_pci_gpio_map { - int pin; - int alt0; - int alt1; - int dir; - char *name; -}; - -/* the pci core can make use of the following gpios */ -static struct ltq_pci_gpio_map ltq_pci_gpio_map[] = { - { 0, 1, 0, 0, "pci-exin0" }, - { 1, 1, 0, 0, "pci-exin1" }, - { 2, 1, 0, 0, "pci-exin2" }, - { 39, 1, 0, 0, "pci-exin3" }, - { 10, 1, 0, 0, "pci-exin4" }, - { 9, 1, 0, 0, "pci-exin5" }, - { 30, 1, 0, 1, "pci-gnt1" }, - { 23, 1, 0, 1, "pci-gnt2" }, - { 19, 1, 0, 1, "pci-gnt3" }, - { 38, 1, 0, 1, "pci-gnt4" }, - { 29, 1, 0, 0, "pci-req1" }, - { 31, 1, 0, 0, "pci-req2" }, - { 3, 1, 0, 0, "pci-req3" }, - { 37, 1, 0, 0, "pci-req4" }, -}; - __iomem void *ltq_pci_mapped_cfg; static __iomem void *ltq_pci_membase; -int (*ltqpci_plat_dev_init)(struct pci_dev *dev) = NULL; - -/* Since the PCI REQ pins can be reused for other functionality, make it - possible to exclude those from interpretation by the PCI controller */ -static int ltq_pci_req_mask = 0xf; - -static int *ltq_pci_irq_map; - -struct pci_ops ltq_pci_ops = { +static int reset_gpio; +static struct clk *clk_pci, *clk_external; +static struct resource pci_io_resource; +static struct resource pci_mem_resource; +static struct pci_ops pci_ops = { .read = ltq_pci_read_config_dword, .write = ltq_pci_write_config_dword }; -static struct resource pci_io_resource = { - .name = "pci io space", - .start = LTQ_PCI_IO_BASE, - .end = LTQ_PCI_IO_BASE + LTQ_PCI_IO_SIZE - 1, - .flags = IORESOURCE_IO -}; - -static struct resource pci_mem_resource = { - .name = "pci memory space", - .start = LTQ_PCI_MEM_BASE, - .end = LTQ_PCI_MEM_BASE + LTQ_PCI_MEM_SIZE - 1, - .flags = IORESOURCE_MEM -}; - -static struct pci_controller ltq_pci_controller = { - .pci_ops = <q_pci_ops, +static struct pci_controller pci_controller = { + .pci_ops = &pci_ops, .mem_resource = &pci_mem_resource, .mem_offset = 0x00000000UL, .io_resource = &pci_io_resource, .io_offset = 0x00000000UL, }; -int pcibios_plat_dev_init(struct pci_dev *dev) -{ - if (ltqpci_plat_dev_init) - return ltqpci_plat_dev_init(dev); - - return 0; -} - -static u32 ltq_calc_bar11mask(void) +static inline u32 ltq_calc_bar11mask(void) { u32 mem, bar11mask; @@ -151,48 +95,42 @@ static u32 ltq_calc_bar11mask(void) return bar11mask; } -static void ltq_pci_setup_gpio(int gpio) -{ - int i; - for (i = 0; i < ARRAY_SIZE(ltq_pci_gpio_map); i++) { - if (gpio & (1 << i)) { - ltq_gpio_request(ltq_pci_gpio_map[i].pin, - ltq_pci_gpio_map[i].alt0, - ltq_pci_gpio_map[i].alt1, - ltq_pci_gpio_map[i].dir, - ltq_pci_gpio_map[i].name); - } - } - ltq_gpio_request(21, 0, 0, 1, "pci-reset"); - ltq_pci_req_mask = (gpio >> PCI_REQ_SHIFT) & PCI_REQ_MASK; -} - -static int __devinit ltq_pci_startup(struct ltq_pci_data *conf) +static int __devinit ltq_pci_startup(struct platform_device *pdev) { + struct device_node *node = pdev->dev.of_node; + const __be32 *req_mask, *bus_clk; u32 temp_buffer; - /* set clock to 33Mhz */ - if (ltq_is_ar9()) { - ltq_cgu_w32(ltq_cgu_r32(LTQ_CGU_IFCCR) & ~0x1f00000, LTQ_CGU_IFCCR); - ltq_cgu_w32(ltq_cgu_r32(LTQ_CGU_IFCCR) | 0xe00000, LTQ_CGU_IFCCR); - } else { - ltq_cgu_w32(ltq_cgu_r32(LTQ_CGU_IFCCR) & ~0xf00000, LTQ_CGU_IFCCR); - ltq_cgu_w32(ltq_cgu_r32(LTQ_CGU_IFCCR) | 0x800000, LTQ_CGU_IFCCR); + /* get our clocks */ + clk_pci = clk_get(&pdev->dev, NULL); + if (IS_ERR(clk_pci)) { + dev_err(&pdev->dev, "failed to get pci clock\n"); + return PTR_ERR(clk_pci); } - /* external or internal clock ? */ - if (conf->clock) { - ltq_cgu_w32(ltq_cgu_r32(LTQ_CGU_IFCCR) & ~(1 << 16), - LTQ_CGU_IFCCR); - ltq_cgu_w32((1 << 30), LTQ_CGU_PCICR); - } else { - ltq_cgu_w32(ltq_cgu_r32(LTQ_CGU_IFCCR) | (1 << 16), - LTQ_CGU_IFCCR); - ltq_cgu_w32((1 << 31) | (1 << 30), LTQ_CGU_PCICR); + clk_external = clk_get(&pdev->dev, "external"); + if (IS_ERR(clk_external)) { + clk_put(clk_pci); + dev_err(&pdev->dev, "failed to get external pci clock\n"); + return PTR_ERR(clk_external); } - /* setup pci clock and gpis used by pci */ - ltq_pci_setup_gpio(conf->gpio); + /* read the bus speed that we want */ + bus_clk = of_get_property(node, "lantiq,bus-clock", NULL); + if (bus_clk) + clk_set_rate(clk_pci, *bus_clk); + + /* and enable the clocks */ + clk_enable(clk_pci); + if (of_find_property(node, "lantiq,external-clock", NULL)) + clk_enable(clk_external); + else + clk_disable(clk_external); + + /* setup reset gpio used by pci */ + reset_gpio = of_get_named_gpio(node, "gpio-reset", 0); + if (reset_gpio > 0) + devm_gpio_request(&pdev->dev, reset_gpio, "pci-reset"); /* enable auto-switching between PCI and EBU */ ltq_pci_w32(0xa, PCI_CR_CLK_CTRL); @@ -205,7 +143,12 @@ static int __devinit ltq_pci_startup(struct ltq_pci_data *conf) /* enable external 2 PCI masters */ temp_buffer = ltq_pci_r32(PCI_CR_PC_ARB); - temp_buffer &= (~(ltq_pci_req_mask << 16)); + /* setup the request mask */ + req_mask = of_get_property(node, "req-mask", NULL); + if (req_mask) + temp_buffer &= ~((*req_mask & 0xf) << 16); + else + temp_buffer &= ~0xf0000; /* enable internal arbiter */ temp_buffer |= (1 << INTERNAL_ARB_ENABLE_BIT); /* enable internal PCI master reqest */ @@ -249,47 +192,55 @@ static int __devinit ltq_pci_startup(struct ltq_pci_data *conf) ltq_ebu_w32(ltq_ebu_r32(LTQ_EBU_PCC_IEN) | 0x10, LTQ_EBU_PCC_IEN); /* toggle reset pin */ - __gpio_set_value(21, 0); - wmb(); - mdelay(1); - __gpio_set_value(21, 1); - return 0; -} - -int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) -{ - if (ltq_pci_irq_map[slot]) - return ltq_pci_irq_map[slot]; - printk(KERN_ERR "lq_pci: trying to map irq for unknown slot %d\n", - slot); - + if (reset_gpio > 0) { + __gpio_set_value(reset_gpio, 0); + wmb(); + mdelay(1); + __gpio_set_value(reset_gpio, 1); + } return 0; } static int __devinit ltq_pci_probe(struct platform_device *pdev) { - struct ltq_pci_data *ltq_pci_data = - (struct ltq_pci_data *) pdev->dev.platform_data; + struct resource *res_cfg, *res_bridge; pci_clear_flags(PCI_PROBE_ONLY); - ltq_pci_irq_map = ltq_pci_data->irq; - ltq_pci_membase = ioremap_nocache(PCI_CR_BASE_ADDR, PCI_CR_SIZE); - ltq_pci_mapped_cfg = - ioremap_nocache(LTQ_PCI_CFG_BASE, LTQ_PCI_CFG_BASE); - ltq_pci_controller.io_map_base = - (unsigned long)ioremap(LTQ_PCI_IO_BASE, LTQ_PCI_IO_SIZE - 1); - ltq_pci_startup(ltq_pci_data); - register_pci_controller(<q_pci_controller); + res_cfg = platform_get_resource(pdev, IORESOURCE_MEM, 0); + res_bridge = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res_cfg || !res_bridge) { + dev_err(&pdev->dev, "missing memory reources\n"); + return -EINVAL; + } + + ltq_pci_membase = devm_request_and_ioremap(&pdev->dev, res_bridge); + ltq_pci_mapped_cfg = devm_request_and_ioremap(&pdev->dev, res_cfg); + + if (!ltq_pci_membase || !ltq_pci_mapped_cfg) { + dev_err(&pdev->dev, "failed to remap resources\n"); + return -ENOMEM; + } + + ltq_pci_startup(pdev); + + pci_load_of_ranges(&pci_controller, pdev->dev.of_node); + register_pci_controller(&pci_controller); return 0; } -static struct platform_driver -ltq_pci_driver = { +static const struct of_device_id ltq_pci_match[] = { + { .compatible = "lantiq,pci-xway" }, + {}, +}; +MODULE_DEVICE_TABLE(of, ltq_pci_match); + +static struct platform_driver ltq_pci_driver = { .probe = ltq_pci_probe, .driver = { - .name = "ltq_pci", + .name = "pci-xway", .owner = THIS_MODULE, + .of_match_table = ltq_pci_match, }, }; @@ -297,7 +248,7 @@ int __init pcibios_init(void) { int ret = platform_driver_register(<q_pci_driver); if (ret) - printk(KERN_INFO "ltq_pci: Error registering platfom driver!"); + pr_info("pci-xway: Error registering platform driver!"); return ret; } diff --git a/arch/mips/pci/pci.c b/arch/mips/pci/pci.c index 0514866fa925..271e8c4a54c7 100644 --- a/arch/mips/pci/pci.c +++ b/arch/mips/pci/pci.c @@ -16,6 +16,7 @@ #include <linux/init.h> #include <linux/types.h> #include <linux/pci.h> +#include <linux/of_address.h> #include <asm/cpu-info.h> @@ -114,9 +115,63 @@ static void __devinit pcibios_scanbus(struct pci_controller *hose) pci_bus_assign_resources(bus); pci_enable_bridges(bus); } + bus->dev.of_node = hose->of_node; } } +#ifdef CONFIG_OF +void __devinit pci_load_of_ranges(struct pci_controller *hose, + struct device_node *node) +{ + const __be32 *ranges; + int rlen; + int pna = of_n_addr_cells(node); + int np = pna + 5; + + pr_info("PCI host bridge %s ranges:\n", node->full_name); + ranges = of_get_property(node, "ranges", &rlen); + if (ranges == NULL) + return; + hose->of_node = node; + + while ((rlen -= np * 4) >= 0) { + u32 pci_space; + struct resource *res = NULL; + u64 addr, size; + + pci_space = be32_to_cpup(&ranges[0]); + addr = of_translate_address(node, ranges + 3); + size = of_read_number(ranges + pna + 3, 2); + ranges += np; + switch ((pci_space >> 24) & 0x3) { + case 1: /* PCI IO space */ + pr_info(" IO 0x%016llx..0x%016llx\n", + addr, addr + size - 1); + hose->io_map_base = + (unsigned long)ioremap(addr, size); + res = hose->io_resource; + res->flags = IORESOURCE_IO; + break; + case 2: /* PCI Memory space */ + case 3: /* PCI 64 bits Memory space */ + pr_info(" MEM 0x%016llx..0x%016llx\n", + addr, addr + size - 1); + res = hose->mem_resource; + res->flags = IORESOURCE_MEM; + break; + } + if (res != NULL) { + res->start = addr; + res->name = node->full_name; + res->end = res->start + size - 1; + res->parent = NULL; + res->sibling = NULL; + res->child = NULL; + } + } +} +#endif + static DEFINE_MUTEX(pci_scan_mutex); void __devinit register_pci_controller(struct pci_controller *hose) |