[PATCH] ARM: NUC900: Add PCI driver support for NUC960
Wan ZongShun
mcuos.com at gmail.com
Sat Nov 12 11:04:12 EST 2011
Add pci driver support for Nuvoton arm nuc960.
Signed-off-by: Wan ZongShun <mcuos.com at gmail.com>
---
arch/arm/mach-w90x900/Kconfig | 1 +
arch/arm/mach-w90x900/Makefile | 3 +
arch/arm/mach-w90x900/include/mach/map.h | 4 +
arch/arm/mach-w90x900/include/mach/regs-pci.h | 37 ++++
arch/arm/mach-w90x900/nuc960.c | 2 +
arch/arm/mach-w90x900/pci.c | 231
+++++++++++++++++++++++++
6 files changed, 278 insertions(+), 0 deletions(-)
create mode 100644 arch/arm/mach-w90x900/include/mach/regs-pci.h
create mode 100644 arch/arm/mach-w90x900/pci.c
diff --git a/arch/arm/mach-w90x900/Kconfig b/arch/arm/mach-w90x900/Kconfig
index 69bab32..17be262 100644
--- a/arch/arm/mach-w90x900/Kconfig
+++ b/arch/arm/mach-w90x900/Kconfig
@@ -41,6 +41,7 @@ menu "NUC960 Machines"
config MACH_W90N960EVB
bool "Nuvoton NUC960 Evaluation Board"
select CPU_NUC960
+ select PCI
help
Say Y here if you are using the Nuvoton NUC960EVB
diff --git a/arch/arm/mach-w90x900/Makefile b/arch/arm/mach-w90x900/Makefile
index 828c032..0c35800 100644
--- a/arch/arm/mach-w90x900/Makefile
+++ b/arch/arm/mach-w90x900/Makefile
@@ -17,3 +17,6 @@ obj-$(CONFIG_CPU_NUC960) += nuc960.o
obj-$(CONFIG_MACH_W90P910EVB) += mach-nuc910evb.o
obj-$(CONFIG_MACH_W90P950EVB) += mach-nuc950evb.o
obj-$(CONFIG_MACH_W90N960EVB) += mach-nuc960evb.o
+
+# Add pci support for nuc960, nuc920
+obj-$(CONFIG_PCI) += pci.o
diff --git a/arch/arm/mach-w90x900/include/mach/map.h
b/arch/arm/mach-w90x900/include/mach/map.h
index 1a20953..69f6c73 100644
--- a/arch/arm/mach-w90x900/include/mach/map.h
+++ b/arch/arm/mach-w90x900/include/mach/map.h
@@ -154,4 +154,8 @@
#define W90X900_PA_EMC (0xB0003000)
#define W90X900_SZ_EMC SZ_4K
+/* PCI interface controller */
+#define W90X900_VA_PCI W90X900_ADDR(0x00002000)
+#define W90X900_PA_PCI (0xB0002000)
+#define W90X900_SZ_PCI SZ_4K
#endif /* __ASM_ARCH_MAP_H */
diff --git a/arch/arm/mach-w90x900/include/mach/regs-pci.h
b/arch/arm/mach-w90x900/include/mach/regs-pci.h
new file mode 100644
index 0000000..d1975eb
--- /dev/null
+++ b/arch/arm/mach-w90x900/include/mach/regs-pci.h
@@ -0,0 +1,37 @@
+/*
+ * arch/arm/mach-w90x900/include/mach/regs-pci.h
+ *
+ * Copyright (c) 2011 Nuvoton technology corporation.
+ *
+ * Wan ZongShun <mcuos.com at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation;version 2 of the License.
+ *
+ */
+
+#ifndef __ASM_ARCH_REGS_PCI_H
+#define __ASM_ARCH_REGS_PCI_H
+
+#define PCI_BA W90X900_VA_PCI /* PCI Control */
+/* PCI Control Registers */
+#define REG_PCICTR (PCI_BA + 0x000)
+#define REG_PCISTR (PCI_BA + 0x004)
+#define REG_PCILATIMER (PCI_BA + 0x008)
+#define REG_PCIINTEN (PCI_BA + 0x010)
+#define REG_PCIINT (PCI_BA + 0x014)
+#define REG_CFGADDR (PCI_BA + 0x020)
+#define REG_CFGDATA (PCI_BA + 0x024)
+#define REG_PCIARB (PCI_BA + 0x04C)
+#define REG_PCIBIST (PCI_BA + 0x050)
+
+#define NUC900_PCI_IO_BASE 0xE0000000
+#define NUC900_PCI_IO_END 0xE000FFFF
+#define NUC900_PCI_IO_SIZE 0x10000
+
+#define NUC900_PCI_MEM_BASE 0xC0000000
+#define NUC900_PCI_MEM_END 0xDFFFEFFFF
+#define NUC900_PCI_MEM_SIZE 0x20000000
+
+#endif /* ___ASM_ARCH_REGS_PCI_H */
diff --git a/arch/arm/mach-w90x900/nuc960.c b/arch/arm/mach-w90x900/nuc960.c
index 8851a3a..0212964 100644
--- a/arch/arm/mach-w90x900/nuc960.c
+++ b/arch/arm/mach-w90x900/nuc960.c
@@ -19,6 +19,7 @@
#include <asm/mach/map.h>
#include <mach/hardware.h>
#include "cpu.h"
+#include "clock.h"
/* define specific CPU platform device */
@@ -30,6 +31,7 @@ static struct platform_device *nuc960_dev[] __initdata = {
/* define specific CPU platform io map */
static struct map_desc nuc960evb_iodesc[] __initdata = {
+ IODESC_ENT(PCI),
};
/*Init NUC960 evb io*/
diff --git a/arch/arm/mach-w90x900/pci.c b/arch/arm/mach-w90x900/pci.c
new file mode 100644
index 0000000..493515e
--- /dev/null
+++ b/arch/arm/mach-w90x900/pci.c
@@ -0,0 +1,231 @@
+/*
+ * linux/arch/arm/mach-w90x900/pci.c
+ *
+ * Copyright (c) 2008 Nuvoton technology corporation.
+ *
+ * Wan ZongShun <mcuos.com at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation;version 2 of the License.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+
+#include <mach/hardware.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/mach/pci.h>
+
+#include <mach/regs-pci.h>
+#include <mach/regs-clock.h>
+#include <mach/regs-gcr.h>
+
+#define EXTAL15M (0x03 << 2)
+#define CK33DIV5 (0x05 << 4)
+#define RESET_VAL1 0x20C0
+#define RESET_VAL2 0x20CF
+
+static int nuc900_read_config(struct pci_bus *bus, unsigned int devfn,
+ int where, int size, unsigned int *val)
+{
+ unsigned int v;
+
+ __raw_writel(devfn * 0x100 + (where & 0xfffffffc), REG_CFGADDR);
+ v = __raw_readl(REG_CFGDATA);
+ switch (size) {
+ case 1:
+ *val = (v >> ((where % 4) * 8)) & 0xff;
+ break;
+ case 2:
+ *val = (v >> ((where % 4) * 8)) & 0xffff;
+ break;
+ default:
+ *val = v;
+ break;
+ }
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+
+static int nuc900_write_config(struct pci_bus *bus, unsigned int devfn,
+ int where, int size, unsigned int val)
+{
+ unsigned int v;
+
+ __raw_writel(devfn * 0x100 + where, REG_CFGADDR);
+
+ v = __raw_readl(REG_CFGDATA);
+
+ __raw_writel(devfn * 0x100 + where, REG_CFGADDR);
+
+ switch (size) {
+ case 1:
+ v &= ~(0xff << (where % 4) * 8);
+ v |= (val << (where % 4) * 8);
+ __raw_writel(val, REG_CFGDATA);
+ break;
+ case 2:
+ v &= ~(0xffff << (where % 4) * 8);
+ v |= (val << (where % 4) * 8);
+ __raw_writel(val, REG_CFGDATA);
+ break;
+ case 4:
+ __raw_writel(val, REG_CFGDATA);
+ break;
+ }
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static struct pci_ops pci_nuc900_ops = {
+ .read = nuc900_read_config,
+ .write = nuc900_write_config,
+};
+
+static struct resource pci_io = {
+ .name = "NUC900 PCI IO",
+ .start = NUC900_PCI_IO_BASE,
+ .end = NUC900_PCI_IO_BASE + NUC900_PCI_IO_SIZE - 1,
+ .flags = IORESOURCE_IO,
+};
+
+static struct resource pci_mem = {
+ .name = "nuc900 PCI Memory",
+ .start = NUC900_PCI_MEM_BASE,
+ .end = NUC900_PCI_MEM_BASE + NUC900_PCI_MEM_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+};
+
+static int __init pci_nuc900_setup_resources(struct resource **resource)
+{
+ int ret = 0;
+
+ ret = request_resource(&iomem_resource, &pci_io);
+ if (ret) {
+ printk(KERN_ERR "PCI: unable to allocate I/O "
+ "memory region (%d)\n", ret);
+ goto out;
+ }
+ ret = request_resource(&iomem_resource, &pci_mem);
+ if (ret) {
+ printk(KERN_ERR "PCI: unable to allocate non-prefetchable "
+ "memory region (%d)\n", ret);
+ goto release_io_mem;
+ }
+
+ /*
+ * bus->resource[0] is the IO resource for this bus
+ * bus->resource[1] is the mem resource for this bus
+ * bus->resource[2] is the prefetch mem resource for this bus
+ */
+ resource[0] = &pci_io;
+ resource[1] = &pci_mem;
+ resource[2] = NULL;
+
+ goto out;
+
+ release_io_mem:
+ release_resource(&pci_io);
+ out:
+ return ret;
+}
+int __init pci_nuc900_setup(int nr, struct pci_sys_data *sys)
+{
+ int ret = 0;
+
+ if (nr == 0) {
+ sys->mem_offset = 0;
+ sys->io_offset = 0;
+ ret = pci_nuc900_setup_resources(sys->resource);
+ if (ret) {
+ printk(KERN_ERR "pci_versatile_setup:\
+ resources... oops?\n");
+ goto out;
+ }
+ } else {
+ printk(KERN_ERR "pci_versatile_setup:\
+ resources... nr == 0??\n");
+ goto out;
+ }
+ ret = 1;
+out:
+ return ret;
+}
+
+struct pci_bus *pci_nuc900_scan_bus(int nr, struct pci_sys_data *sys)
+{
+ return pci_scan_bus(sys->busnr, &pci_nuc900_ops, sys);
+}
+
+void __init pci_nuc900_preinit(void)
+{
+ /* CK33 from PLL0 */
+ __raw_writel(__raw_readl(REG_CLKSEL) & ~EXTAL15M, REG_CLKSEL);
+ /* PCI CLOCK = 200/6 = 33Mhz */
+ __raw_writel(((__raw_readl(REG_CLKDIV) &
+ (~(0xf<<4))) | CK33DIV5), REG_CLKDIV);
+
+ /* enable PCI clock */
+ __raw_writel(__raw_readl(REG_CLKEN) | 0x4, REG_CLKEN);
+
+ __raw_writel(RESET_VAL1, REG_PCICTR);
+
+ mdelay(100);
+
+ __raw_writel(RESET_VAL2, REG_PCICTR);
+
+ mdelay(200);
+}
+
+static inline int bridge_swizzle(int pin, unsigned int slot)
+{
+ return (pin + slot) & 3;
+}
+
+/*
+ * This routine handles multiple bridges.
+ */
+static u8 __init nuc900_swizzle(struct pci_dev *dev, u8 *pinp)
+{
+ int pin = *pinp;
+
+ if (pin == 0)
+ pin = 1;
+
+ pin -= 1;
+ while (dev->bus->self) {
+ pin = bridge_swizzle(pin, PCI_SLOT(dev->devfn));
+ /*
+ * move up the chain of bridges, swizzling as we go.
+ */
+ dev = dev->bus->self;
+ }
+ *pinp = pin + 1;
+
+ return PCI_SLOT(dev->devfn);
+}
+
+static struct hw_pci nuc900_pci __initdata = {
+ .swizzle = nuc900_swizzle,
+ .setup = pci_nuc900_setup,
+ .nr_controllers = 1,
+ .scan = pci_nuc900_scan_bus,
+ .preinit = pci_nuc900_preinit,
+};
+
+static int __init nuc900_pci_init(void)
+{
+ pci_common_init(&nuc900_pci);
+ return 0;
+}
+
+subsys_initcall(nuc900_pci_init);
--
1.7.0.4
More information about the linux-arm-kernel
mailing list