diff options
Diffstat (limited to 'arch')
-rw-r--r-- | arch/arm/Kconfig | 8 | ||||
-rw-r--r-- | arch/arm/Makefile | 1 | ||||
-rw-r--r-- | arch/arm/mach-iop13xx/Kconfig | 20 | ||||
-rw-r--r-- | arch/arm/mach-iop13xx/Makefile | 12 | ||||
-rw-r--r-- | arch/arm/mach-iop13xx/Makefile.boot | 3 | ||||
-rw-r--r-- | arch/arm/mach-iop13xx/io.c | 93 | ||||
-rw-r--r-- | arch/arm/mach-iop13xx/iq81340mc.c | 98 | ||||
-rw-r--r-- | arch/arm/mach-iop13xx/iq81340sc.c | 100 | ||||
-rw-r--r-- | arch/arm/mach-iop13xx/irq.c | 286 | ||||
-rw-r--r-- | arch/arm/mach-iop13xx/pci.c | 1113 | ||||
-rw-r--r-- | arch/arm/mach-iop13xx/setup.c | 406 | ||||
-rw-r--r-- | arch/arm/mach-iop13xx/time.c | 102 | ||||
-rw-r--r-- | arch/arm/mm/Kconfig | 2 |
13 files changed, 2243 insertions, 1 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index ce00c570459..d654c4f87d0 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -223,6 +223,12 @@ config ARCH_IOP33X help Support for Intel's IOP33X (XScale) family of processors. +config ARCH_IOP13XX + bool "IOP13xx-based" + select PCI + help + Support for Intel's IOP13XX (XScale) family of processors. + config ARCH_IXP4XX bool "IXP4xx-based" depends on MMU @@ -331,6 +337,8 @@ source "arch/arm/mach-iop32x/Kconfig" source "arch/arm/mach-iop33x/Kconfig" +source "arch/arm/mach-iop13xx/Kconfig" + source "arch/arm/mach-ixp4xx/Kconfig" source "arch/arm/mach-ixp2000/Kconfig" diff --git a/arch/arm/Makefile b/arch/arm/Makefile index 6f4f8bf3607..5f6f9a58570 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -108,6 +108,7 @@ endif machine-$(CONFIG_ARCH_CLPS711X) := clps711x machine-$(CONFIG_ARCH_IOP32X) := iop32x machine-$(CONFIG_ARCH_IOP33X) := iop33x + machine-$(CONFIG_ARCH_IOP13XX) := iop13xx machine-$(CONFIG_ARCH_IXP4XX) := ixp4xx machine-$(CONFIG_ARCH_IXP2000) := ixp2000 machine-$(CONFIG_ARCH_IXP23XX) := ixp23xx diff --git a/arch/arm/mach-iop13xx/Kconfig b/arch/arm/mach-iop13xx/Kconfig new file mode 100644 index 00000000000..40c2d689f2e --- /dev/null +++ b/arch/arm/mach-iop13xx/Kconfig @@ -0,0 +1,20 @@ +if ARCH_IOP13XX + +menu "IOP13XX Implementation Options" + +comment "IOP13XX Platform Support" + +config MACH_IQ81340SC + bool "Enable IQ81340SC Hardware Support" + help + Say Y here if you want to support running on the Intel IQ81340SC + evaluation kit. + +config MACH_IQ81340MC + bool "Enable IQ81340MC Hardware Support" + help + Say Y here if you want to support running on the Intel IQ81340MC + evaluation kit. + +endmenu +endif diff --git a/arch/arm/mach-iop13xx/Makefile b/arch/arm/mach-iop13xx/Makefile new file mode 100644 index 00000000000..c3d6c08f2d4 --- /dev/null +++ b/arch/arm/mach-iop13xx/Makefile @@ -0,0 +1,12 @@ +obj-y := +obj-m := +obj-n := +obj- := + +obj-$(CONFIG_ARCH_IOP13XX) += setup.o +obj-$(CONFIG_ARCH_IOP13XX) += irq.o +obj-$(CONFIG_ARCH_IOP13XX) += time.o +obj-$(CONFIG_ARCH_IOP13XX) += pci.o +obj-$(CONFIG_ARCH_IOP13XX) += io.o +obj-$(CONFIG_MACH_IQ81340SC) += iq81340sc.o +obj-$(CONFIG_MACH_IQ81340MC) += iq81340mc.o diff --git a/arch/arm/mach-iop13xx/Makefile.boot b/arch/arm/mach-iop13xx/Makefile.boot new file mode 100644 index 00000000000..0b0e19fdfe6 --- /dev/null +++ b/arch/arm/mach-iop13xx/Makefile.boot @@ -0,0 +1,3 @@ + zreladdr-y := 0x00008000 +params_phys-y := 0x00000100 +initrd_phys-y := 0x00800000 diff --git a/arch/arm/mach-iop13xx/io.c b/arch/arm/mach-iop13xx/io.c new file mode 100644 index 00000000000..fbf9f88e46e --- /dev/null +++ b/arch/arm/mach-iop13xx/io.c @@ -0,0 +1,93 @@ +/* + * iop13xx custom ioremap implementation + * Copyright (c) 2005-2006, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + * + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <asm/hardware.h> +#include <asm/io.h> + +void * __iomem __iop13xx_ioremap(unsigned long cookie, size_t size, + unsigned long flags) +{ + void __iomem * retval; + + switch (cookie) { + case IOP13XX_PCIX_LOWER_MEM_RA ... IOP13XX_PCIX_UPPER_MEM_RA: + if (unlikely(!iop13xx_atux_mem_base)) + retval = NULL; + else + retval = (void *)(iop13xx_atux_mem_base + + (cookie - IOP13XX_PCIX_LOWER_MEM_RA)); + break; + case IOP13XX_PCIE_LOWER_MEM_RA ... IOP13XX_PCIE_UPPER_MEM_RA: + if (unlikely(!iop13xx_atue_mem_base)) + retval = NULL; + else + retval = (void *)(iop13xx_atue_mem_base + + (cookie - IOP13XX_PCIE_LOWER_MEM_RA)); + break; + case IOP13XX_PBI_LOWER_MEM_RA ... IOP13XX_PBI_UPPER_MEM_RA: + retval = __ioremap(IOP13XX_PBI_LOWER_MEM_PA + + (cookie - IOP13XX_PBI_LOWER_MEM_RA), + size, flags); + break; + case IOP13XX_PCIE_LOWER_IO_PA ... IOP13XX_PCIE_UPPER_IO_PA: + retval = (void *) IOP13XX_PCIE_IO_PHYS_TO_VIRT(cookie); + break; + case IOP13XX_PCIX_LOWER_IO_PA ... IOP13XX_PCIX_UPPER_IO_PA: + retval = (void *) IOP13XX_PCIX_IO_PHYS_TO_VIRT(cookie); + break; + case IOP13XX_PMMR_PHYS_MEM_BASE ... IOP13XX_PMMR_UPPER_MEM_PA: + retval = (void *) IOP13XX_PMMR_PHYS_TO_VIRT(cookie); + break; + default: + retval = __ioremap(cookie, size, flags); + } + + return retval; +} +EXPORT_SYMBOL(__iop13xx_ioremap); + +void __iop13xx_iounmap(void __iomem *addr) +{ + extern void __iounmap(volatile void __iomem *addr); + + if (iop13xx_atue_mem_base) + if (addr >= (void __iomem *) iop13xx_atue_mem_base && + addr < (void __iomem *) (iop13xx_atue_mem_base + + iop13xx_atue_mem_size)) + goto skip; + + if (iop13xx_atux_mem_base) + if (addr >= (void __iomem *) iop13xx_atux_mem_base && + addr < (void __iomem *) (iop13xx_atux_mem_base + + iop13xx_atux_mem_size)) + goto skip; + + switch ((u32) addr) { + case IOP13XX_PCIE_LOWER_IO_VA ... IOP13XX_PCIE_UPPER_IO_VA: + case IOP13XX_PCIX_LOWER_IO_VA ... IOP13XX_PCIX_UPPER_IO_VA: + case IOP13XX_PMMR_VIRT_MEM_BASE ... IOP13XX_PMMR_UPPER_MEM_VA: + goto skip; + } + __iounmap(addr); + +skip: + return; +} +EXPORT_SYMBOL(__iop13xx_iounmap); diff --git a/arch/arm/mach-iop13xx/iq81340mc.c b/arch/arm/mach-iop13xx/iq81340mc.c new file mode 100644 index 00000000000..ee595786cd2 --- /dev/null +++ b/arch/arm/mach-iop13xx/iq81340mc.c @@ -0,0 +1,98 @@ +/* + * iq81340mc board support + * Copyright (c) 2005-2006, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + * + */ +#include <linux/pci.h> + +#include <asm/hardware.h> +#include <asm/irq.h> +#include <asm/mach/pci.h> +#include <asm/mach-types.h> +#include <asm/mach/arch.h> +#include <asm/arch/pci.h> +#include <asm/mach/time.h> + +extern int init_atu; /* Flag to select which ATU(s) to initialize / disable */ + +static int __init +iq81340mc_pcix_map_irq(struct pci_dev *dev, u8 idsel, u8 pin) +{ + switch (idsel) { + case 1: + switch (pin) { + case 1: return ATUX_INTB; + case 2: return ATUX_INTC; + case 3: return ATUX_INTD; + case 4: return ATUX_INTA; + default: return -1; + } + case 2: + switch (pin) { + case 1: return ATUX_INTC; + case 2: return ATUX_INTD; + case 3: return ATUX_INTC; + case 4: return ATUX_INTD; + default: return -1; + } + default: return -1; + } +} + +static struct hw_pci iq81340mc_pci __initdata = { + .swizzle = pci_std_swizzle, + .nr_controllers = 0, + .setup = iop13xx_pci_setup, + .map_irq = iq81340mc_pcix_map_irq, + .scan = iop13xx_scan_bus, + .preinit = iop13xx_pci_init, +}; + +static int __init iq81340mc_pci_init(void) +{ + iop13xx_atu_select(&iq81340mc_pci); + pci_common_init(&iq81340mc_pci); + iop13xx_map_pci_memory(); + + return 0; +} + +static void __init iq81340mc_init(void) +{ + iop13xx_platform_init(); + iq81340mc_pci_init(); +} + +static void __init iq81340mc_timer_init(void) +{ + iop13xx_init_time(400000000); +} + +static struct sys_timer iq81340mc_timer = { + .init = iq81340mc_timer_init, + .offset = iop13xx_gettimeoffset, +}; + +MACHINE_START(IQ81340MC, "Intel IQ81340MC") + /* Maintainer: Dan Williams <dan.j.williams@intel.com> */ + .phys_io = PHYS_IO, + .io_pg_offst = IO_PG_OFFSET, + .map_io = iop13xx_map_io, + .init_irq = iop13xx_init_irq, + .timer = &iq81340mc_timer, + .boot_params = BOOT_PARAM_OFFSET, + .init_machine = iq81340mc_init, +MACHINE_END diff --git a/arch/arm/mach-iop13xx/iq81340sc.c b/arch/arm/mach-iop13xx/iq81340sc.c new file mode 100644 index 00000000000..6677e14b61b --- /dev/null +++ b/arch/arm/mach-iop13xx/iq81340sc.c @@ -0,0 +1,100 @@ +/* + * iq81340sc board support + * Copyright (c) 2005-2006, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + * + */ +#include <linux/pci.h> + +#include <asm/hardware.h> +#include <asm/irq.h> +#include <asm/mach/pci.h> +#include <asm/mach-types.h> +#include <asm/mach/arch.h> +#include <asm/arch/pci.h> +#include <asm/mach/time.h> + +extern int init_atu; + +static int __init +iq81340sc_atux_map_irq(struct pci_dev *dev, u8 idsel, u8 pin) +{ + WARN_ON(idsel < 1 || idsel > 2); + + switch (idsel) { + case 1: + switch (pin) { + case 1: return ATUX_INTB; + case 2: return ATUX_INTC; + case 3: return ATUX_INTD; + case 4: return ATUX_INTA; + default: return -1; + } + case 2: + switch (pin) { + case 1: return ATUX_INTC; + case 2: return ATUX_INTC; + case 3: return ATUX_INTC; + case 4: return ATUX_INTC; + default: return -1; + } + default: return -1; + } +} + +static struct hw_pci iq81340sc_pci __initdata = { + .swizzle = pci_std_swizzle, + .nr_controllers = 0, + .setup = iop13xx_pci_setup, + .scan = iop13xx_scan_bus, + .map_irq = iq81340sc_atux_map_irq, + .preinit = iop13xx_pci_init +}; + +static int __init iq81340sc_pci_init(void) +{ + iop13xx_atu_select(&iq81340sc_pci); + pci_common_init(&iq81340sc_pci); + iop13xx_map_pci_memory(); + + return 0; +} + +static void __init iq81340sc_init(void) +{ + iop13xx_platform_init(); + iq81340sc_pci_init(); +} + +static void __init iq81340sc_timer_init(void) +{ + iop13xx_init_time(400000000); +} + +static struct sys_timer iq81340sc_timer = { + .init = iq81340sc_timer_init, + .offset = iop13xx_gettimeoffset, +}; + +MACHINE_START(IQ81340SC, "Intel IQ81340SC") + /* Maintainer: Dan Williams <dan.j.williams@intel.com> */ + .phys_io = PHYS_IO, + .io_pg_offst = IO_PG_OFFSET, + .map_io = iop13xx_map_io, + .init_irq = iop13xx_init_irq, + .timer = &iq81340sc_timer, + .boot_params = BOOT_PARAM_OFFSET, + .init_machine = iq81340sc_init, +MACHINE_END diff --git a/arch/arm/mach-iop13xx/irq.c b/arch/arm/mach-iop13xx/irq.c new file mode 100644 index 00000000000..c4d9c8c5579 --- /dev/null +++ b/arch/arm/mach-iop13xx/irq.c @@ -0,0 +1,286 @@ +/* + * iop13xx IRQ handling / support functions + * Copyright (c) 2005-2006, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + * + */ +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/list.h> +#include <linux/sysctl.h> +#include <asm/uaccess.h> +#include <asm/mach/irq.h> +#include <asm/irq.h> +#include <asm/hardware.h> +#include <asm/mach-types.h> +#include <asm/arch/irqs.h> + +/* INTCTL0 CP6 R0 Page 4 + */ +static inline u32 read_intctl_0(void) +{ + u32 val; + asm volatile("mrc p6, 0, %0, c0, c4, 0":"=r" (val)); + return val; +} +static inline void write_intctl_0(u32 val) +{ + asm volatile("mcr p6, 0, %0, c0, c4, 0"::"r" (val)); +} + +/* INTCTL1 CP6 R1 Page 4 + */ +static inline u32 read_intctl_1(void) +{ + u32 val; + asm volatile("mrc p6, 0, %0, c1, c4, 0":"=r" (val)); + return val; +} +static inline void write_intctl_1(u32 val) +{ + asm volatile("mcr p6, 0, %0, c1, c4, 0"::"r" (val)); +} + +/* INTCTL2 CP6 R2 Page 4 + */ +static inline u32 read_intctl_2(void) +{ + u32 val; + asm volatile("mrc p6, 0, %0, c2, c4, 0":"=r" (val)); + return val; +} +static inline void write_intctl_2(u32 val) +{ + asm volatile("mcr p6, 0, %0, c2, c4, 0"::"r" (val)); +} + +/* INTCTL3 CP6 R3 Page 4 + */ +static inline u32 read_intctl_3(void) +{ + u32 val; + asm volatile("mrc p6, 0, %0, c3, c4, 0":"=r" (val)); + return val; +} +static inline void write_intctl_3(u32 val) +{ + asm volatile("mcr p6, 0, %0, c3, c4, 0"::"r" (val)); +} + +/* INTSTR0 CP6 R0 Page 5 + */ +static inline u32 read_intstr_0(void) +{ + u32 val; + asm volatile("mrc p6, 0, %0, c0, c5, 0":"=r" (val)); + return val; +} +static inline void write_intstr_0(u32 val) +{ + asm volatile("mcr p6, 0, %0, c0, c5, 0"::"r" (val)); +} + +/* INTSTR1 CP6 R1 Page 5 + */ +static inline u32 read_intstr_1(void) +{ + u32 val; + asm volatile("mrc p6, 0, %0, c1, c5, 0":"=r" (val)); + return val; +} +static void write_intstr_1(u32 val) +{ + asm volatile("mcr p6, 0, %0, c1, c5, 0"::"r" (val)); +} + +/* INTSTR2 CP6 R2 Page 5 + */ +static inline u32 read_intstr_2(void) +{ + u32 val; + asm volatile("mrc p6, 0, %0, c2, c5, 0":"=r" (val)); + return val; +} +static void write_intstr_2(u32 val) +{ + asm volatile("mcr p6, 0, %0, c2, c5, 0"::"r" (val)); +} + +/* INTSTR3 CP6 R3 Page 5 + */ +static inline u32 read_intstr_3(void) +{ + u32 val; + asm volatile("mrc p6, 0, %0, c3, c5, 0":"=r" (val)); + return val; +} +static void write_intstr_3(u32 val) +{ + asm volatile("mcr p6, 0, %0, c3, c5, 0"::"r" (val)); +} + +/* INTBASE CP6 R0 Page 2 + */ +static inline u32 read_intbase(void) +{ + u32 val; + asm volatile("mrc p6, 0, %0, c0, c2, 0":"=r" (val)); + return val; +} +static void write_intbase(u32 val) +{ + asm volatile("mcr p6, 0, %0, c0, c2, 0"::"r" (val)); +} + +/* INTSIZE CP6 R2 Page 2 + */ +static inline u32 read_intsize(void) +{ + u32 val; + asm volatile("mrc p6, 0, %0, c2, c2, 0":"=r" (val)); + return val; +} +static void write_intsize(u32 val) +{ + asm volatile("mcr p6, 0, %0, c2, c2, 0"::"r" (val)); +} + +/* 0 = Interrupt Masked and 1 = Interrupt not masked */ +static void +iop13xx_irq_mask0 (unsigned int irq) +{ + u32 cp_flags = iop13xx_cp6_save(); + write_intctl_0(read_intctl_0() & ~(1 << (irq - 0))); + iop13xx_cp6_restore(cp_flags); +} + +static void +iop13xx_irq_mask1 (unsigned int irq) +{ + u32 cp_flags = iop13xx_cp6_save(); + write_intctl_1(read_intctl_1() & ~(1 << (irq - 32))); + iop13xx_cp6_restore(cp_flags); +} + +static void +iop13xx_irq_mask2 (unsigned int irq) +{ + u32 cp_flags = iop13xx_cp6_save(); + write_intctl_2(read_intctl_2() & ~(1 << (irq - 64))); + iop13xx_cp6_restore(cp_flags); +} + +static void +iop13xx_irq_mask3 (unsigned int irq) +{ + u32 cp_flags = iop13xx_cp6_save(); + write_intctl_3(read_intctl_3() & ~(1 << (irq - 96))); + iop13xx_cp6_restore(cp_flags); +} + +static void +iop13xx_irq_unmask0(unsigned int irq) +{ + u32 cp_flags = iop13xx_cp6_save(); + write_intctl_0(read_intctl_0() | (1 << (irq - 0))); + iop13xx_cp6_restore(cp_flags); +} + +static void +iop13xx_irq_unmask1(unsigned int irq) +{ + u32 cp_flags = iop13xx_cp6_save(); + write_intctl_1(read_intctl_1() | (1 << (irq - 32))); + iop13xx_cp6_restore(cp_flags); +} + +static void +iop13xx_irq_unmask2(unsigned int irq) +{ + u32 cp_flags = iop13xx_cp6_save(); + write_intctl_2(read_intctl_2() | (1 << (irq - 64))); + iop13xx_cp6_restore(cp_flags); +} + +static void +iop13xx_irq_unmask3(unsigned int irq) +{ + u32 cp_flags = iop13xx_cp6_save(); + write_intctl_3(read_intctl_3() | (1 << (irq - 96))); + iop13xx_cp6_restore(cp_flags); +} + +static struct irqchip iop13xx_irqchip0 = { + .ack = iop13xx_irq_mask0, + .mask = iop13xx_irq_mask0, + .unmask = iop13xx_irq_unmask0, +}; + +static struct irqchip iop13xx_irqchip1 = { + .ack = iop13xx_irq_mask1, + .mask = iop13xx_irq_mask1, + .unmask = iop13xx_irq_unmask1, +}; + +static struct irqchip iop13xx_irqchip2 = { + .ack = iop13xx_irq_mask2, + .mask = iop13xx_irq_mask2, + .unmask = iop13xx_irq_unmask2, +}; + +static struct irqchip iop13xx_irqchip3 = { + .ack = iop13xx_irq_mask3, + .mask = iop13xx_irq_mask3, + .unmask = iop13xx_irq_unmask3, +}; + +void __init iop13xx_init_irq(void) +{ + unsigned int i; + + u32 cp_flags = iop13xx_cp6_save(); + + /* disable all interrupts */ + write_intctl_0(0); + write_intctl_1(0); + write_intctl_2(0); + write_intctl_3(0); + + /* treat all as IRQ */ + write_intstr_0(0); + write_intstr_1(0); + write_intstr_2(0); + write_intstr_3(0); + + /* initialize the interrupt vector generator */ + write_intbase(INTBASE); + write_intsize(INTSIZE_4); + + for(i = 0; i < NR_IOP13XX_IRQS; i++) { + if (i < 32) + set_irq_chip(i, &iop13xx_irqchip0); + else if (i < 64) + set_irq_chip(i, &iop13xx_irqchip1); + else if (i < 96) + set_irq_chip(i, &iop13xx_irqchip2); + else + set_irq_chip(i, &iop13xx_irqchip3); + + set_irq_handler(i, do_level_IRQ); + set_irq_flags(i, IRQF_VALID | IRQF_PROBE); + } + + iop13xx_cp6_restore(cp_flags); +} diff --git a/arch/arm/mach-iop13xx/pci.c b/arch/arm/mach-iop13xx/pci.c new file mode 100644 index 00000000000..89ec70ea318 --- /dev/null +++ b/arch/arm/mach-iop13xx/pci.c @@ -0,0 +1,1113 @@ +/* + * iop13xx PCI support + * Copyright (c) 2005-2006, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + * + */ + +#include <linux/pci.h> +#include <linux/delay.h> + +#include <asm/irq.h> +#include <asm/hardware.h> +#include <asm/sizes.h> +#include <asm/mach/pci.h> +#include <asm/arch/pci.h> + +#define IOP13XX_PCI_DEBUG 0 +#define PRINTK(x...) ((void)(IOP13XX_PCI_DEBUG && printk(x))) + +u32 iop13xx_atux_pmmr_offset; /* This offset can change based on strapping */ +u32 iop13xx_atue_pmmr_offset; /* This offset can change based on strapping */ +static struct pci_bus *pci_bus_atux = 0; +static struct pci_bus *pci_bus_atue = 0; +u32 iop13xx_atue_mem_base; +u32 iop13xx_atux_mem_base; +size_t iop13xx_atue_mem_size; +size_t iop13xx_atux_mem_size; +unsigned long iop13xx_pcibios_min_io = 0; +unsigned long iop13xx_pcibios_min_mem = 0; + +EXPORT_SYMBOL(iop13xx_atue_mem_base); +EXPORT_SYMBOL(iop13xx_atux_mem_base); +EXPORT_SYMBOL(iop13xx_atue_mem_size); +EXPORT_SYMBOL(iop13xx_atux_mem_size); + +int init_atu = 0; /* Flag to select which ATU(s) to initialize / disable */ +static unsigned long atux_trhfa_timeout = 0; /* Trhfa = RST# high to first + access */ + +/* Scan the initialized busses and ioremap the requested memory range + */ +void iop13xx_map_pci_memory(void) +{ + int atu; + struct pci_bus *bus; + struct pci_dev *dev; + resource_size_t end = 0; + + for (atu = 0; atu < 2; atu++) { + bus = atu ? pci_bus_atue : pci_bus_atux; + if (bus) { + list_for_each_entry(dev, &bus->devices, bus_list) { + int i; + int max = 7; + + if (dev->subordinate) + max = DEVICE_COUNT_RESOURCE; + + for (i = 0; i < max; i++) { + struct resource *res = &dev->resource[i]; + if (res->flags & IORESOURCE_MEM) + end = max(res->end, end); + } + } + + switch(atu) { + case 0: + iop13xx_atux_mem_size = + (end - IOP13XX_PCIX_LOWER_MEM_RA) + 1; + + /* 16MB align the request */ + if (iop13xx_atux_mem_size & (SZ_16M - 1)) { + iop13xx_atux_mem_size &= ~(SZ_16M - 1); + iop13xx_atux_mem_size += SZ_16M; + } + + if (end) { + iop13xx_atux_mem_base = + (u32) __ioremap_pfn( + __phys_to_pfn(IOP13XX_PCIX_LOWER_MEM_PA) + , 0, iop13xx_atux_mem_size, 0); + if (!iop13xx_atux_mem_base) { + printk("%s: atux allocation " + "failed\n", __FUNCTION__); + BUG(); + } + } else + iop13xx_atux_mem_size = 0; + PRINTK("%s: atu: %d bus_size: %d mem_base: %x\n", + __FUNCTION__, atu, iop13xx_atux_mem_size, + iop13xx_atux_mem_base); + break; + case 1: + iop13xx_atue_mem_size = + (end - IOP13XX_PCIE_LOWER_MEM_RA) + 1; + + /* 16MB align the request */ + if (iop13xx_atue_mem_size & (SZ_16M - 1)) { + iop13xx_atue_mem_size &= ~(SZ_16M - 1); + iop13xx_atue_mem_size += SZ_16M; + } + + if (end) { + iop13xx_atue_mem_base = + (u32) __ioremap_pfn( + __phys_to_pfn(IOP13XX_PCIE_LOWER_MEM_PA) + , 0, iop13xx_atue_mem_size, 0); + if (!iop13xx_atue_mem_base) { + printk("%s: atue allocation " + "failed\n", __FUNCTION__); + BUG(); + } + } else + iop13xx_atue_mem_size = 0; + PRINTK("%s: atu: %d bus_size: %d mem_base: %x\n", + __FUNCTION__, atu, iop13xx_atue_mem_size, + iop13xx_atue_mem_base); + break; + } + + printk("%s: Initialized (%uM @ resource/virtual: %08lx/%08x)\n", + atu ? "ATUE" : "ATUX", + (atu ? iop13xx_atue_mem_size : iop13xx_atux_mem_size) / + SZ_1M, + atu ? IOP13XX_PCIE_LOWER_MEM_RA : + IOP13XX_PCIX_LOWER_MEM_RA, + atu ? iop13xx_atue_mem_base : + iop13xx_atux_mem_base); + end = 0; + } + + } +} + +static inline int iop13xx_atu_function(int atu) +{ + int func = 0; + /* the function number depends on the value of the + * IOP13XX_INTERFACE_SEL_PCIX reset strap + * see C-Spec section 3.17 + */ + switch(atu) { + case IOP13XX_INIT_ATU_ATUX: + if (__raw_readl(IOP13XX_ESSR0) & IOP13XX_INTERFACE_SEL_PCIX) + func = 5; + else + func = 0; + break; + case IOP13XX_INIT_ATU_ATUE: + if (__raw_readl(IOP13XX_ESSR0) & IOP13XX_INTERFACE_SEL_PCIX) + func = 0; + else + func = 5; + break; + default: + BUG(); + } + + return func; +} + +/* iop13xx_atux_cfg_address - format a configuration address for atux + * @bus: Target bus to access + * @devfn: Combined device number and function number + * @where: Desired register's address offset + * + * Convert the parameters to a configuration address formatted + * according the PCI-X 2.0 specification + */ +static u32 iop13xx_atux_cfg_address(struct pci_bus *bus, int devfn, int where) +{ + struct pci_sys_data *sys = bus->sysdata; + u32 addr; + + if (sys->busnr == bus->number) + addr = 1 << (PCI_SLOT(devfn) + 16) | (PCI_SLOT(devfn) << 11); + else + addr = bus->number << 16 | PCI_SLOT(devfn) << 11 | 1; + + addr |= PCI_FUNC(devfn) << 8 | ((where & 0xff) & ~3); + addr |= ((where & 0xf00) >> 8) << 24; /* upper register number */ + + return addr; +} + +/* iop13xx_atue_cfg_address - format a configuration address for atue + * @bus: Target bus to access + * @devfn: Combined device number and function number + * @where: Desired register's address offset + * + * Convert the parameters to an address usable by the ATUE_OCCAR + */ +static u32 iop13xx_atue_cfg_address(struct pci_bus *bus, int devfn, int where) +{ + struct pci_sys_data *sys = bus->sysdata; + u32 addr; + + PRINTK("iop13xx_atue_cfg_address: bus: %d dev: %d func: %d", + bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn)); + addr = ((u32) bus->number) << IOP13XX_ATUE_OCCAR_BUS_NUM | + ((u32) PCI_SLOT(devfn)) << IOP13XX_ATUE_OCCAR_DEV_NUM | + ((u32) PCI_FUNC(devfn)) << IOP13XX_ATUE_OCCAR_FUNC_NUM | + (where & ~0x3); + + if (sys->busnr != bus->number) + addr |= 1; /* type 1 access */ + + return addr; +} + +/* This routine checks the status of the last configuration cycle. If an error + * was detected it returns >0, else it returns a 0. The errors being checked + * are parity, master abort, target abort (master and target). These types of + * errors occure during a config cycle where there is no device, like during + * the discovery stage. + */ +static int iop13xx_atux_pci_status(int clear) +{ + unsigned int status; + int err = 0; + + /* + * Check the status registers. + */ + status = __raw_readw(IOP13XX_ATUX_ATUSR); + if (status & IOP_PCI_STATUS_ERROR) + { + PRINTK("\t\t\tPCI error: ATUSR %#08x", status); + if(clear) + __raw_writew(status & IOP_PCI_STATUS_ERROR, + IOP13XX_ATUX_ATUSR); + err = 1; + } + status = __raw_readl(IOP13XX_ATUX_ATUISR); + if (status & IOP13XX_ATUX_ATUISR_ERROR) + { + PRINTK("\t\t\tPCI error interrupt: ATUISR %#08x", status); + if(clear) + __raw_writel(status & IOP13XX_ATUX_ATUISR_ERROR, + IOP13XX_ATUX_ATUISR); + err = 1; + } + return err; +} + +/* Simply write the address register and read the configuration + * data. Note that the data dependency on %0 encourages an abort + * to be detected before we return. + */ +static inline u32 iop13xx_atux_read(unsigned long addr) +{ + u32 val; + + __asm__ __volatile__( + "str %1, [%2]\n\t" + "ldr %0, [%3]\n\t" + "mov %0, %0\n\t" + : "=r" (val) + : "r" (addr), "r" (IOP13XX_ATUX_OCCAR), "r" (IOP13XX_ATUX_OCCDR)); + + return val; +} + +/* The read routines must check the error status of the last configuration + * cycle. If there was an error, the routine returns all hex f's. + */ +static int +iop13xx_atux_read_config(struct pci_bus *bus, unsigned int devfn, int where, + int size, u32 *value) +{ + unsigned long addr = iop13xx_atux_cfg_address(bus, devfn, where); + u32 val = iop13xx_atux_read(addr) >> ((where & 3) * 8); + + if (iop13xx_atux_pci_status(1) || is_atux_occdr_error()) { + __raw_writel(__raw_readl(IOP13XX_XBG_BECSR) & 3, + IOP13XX_XBG_BECSR); + val = 0xffffffff; + } + + *value = val; + + return PCIBIOS_SUCCESSFUL; +} + +static int +iop13xx_atux_write_config(struct pci_bus *bus, unsigned int devfn, int where, + int size, u32 value) +{ + unsigned long addr = iop13xx_atux_cfg_address(bus, devfn, where); + u32 val; + + if (size != 4) { + val = iop13xx_atux_read(addr); + if (!iop13xx_atux_pci_status(1) == 0) + return PCIBIOS_SUCCESSFUL; + + where = (where & 3) * 8; + + if (size == 1) + val &= ~(0xff << where); + else + val &= ~(0xffff << where); + + __raw_writel(val | value << where, IOP13XX_ATUX_OCCDR); + } else { + __raw_writel(addr, IOP13XX_ATUX_OCCAR); + __raw_writel(value, IOP13XX_ATUX_OCCDR); + } + + return PCIBIOS_SUCCESSFUL; +} + +static struct pci_ops iop13xx_atux_ops = { + .read = iop13xx_atux_read_config, + .write = iop13xx_atux_write_config, +}; + +/* This routine checks the status of the last configuration cycle. If an error + * was detected it returns >0, else it returns a 0. The errors being checked + * are parity, master abort, target abort (master and target). These types of + * errors occure during a config cycle where there is no device, like during + * the discovery stage. + */ +static int iop13xx_atue_pci_status(int clear) +{ + unsigned int status; + int err = 0; + + /* + * Check the status registers. + */ + + /* standard pci status register */ + status = __raw_readw(IOP13XX_ATUE_ATUSR); + if (status & IOP_PCI_STATUS_ERROR) { + PRINTK("\t\t\tPCI error: ATUSR %#08x", status); + if(clear) + __raw_writew(status & IOP_PCI_STATUS_ERROR, + IOP13XX_ATUE_ATUSR); + err++; + } + + /* check the normal status bits in the ATUISR */ + status = __raw_readl(IOP13XX_ATUE_ATUISR); + if (status & IOP13XX_ATUE_ATUISR_ERROR) { + PRINTK("\t\t\tPCI error: ATUISR %#08x", status); + if (clear) + __raw_writew(status & IOP13XX_ATUE_ATUISR_ERROR, + IOP13XX_ATUE_ATUISR); + err++; + + /* check the PCI-E status if the ATUISR reports an interface error */ + if (status & IOP13XX_ATUE_STAT_PCI_IFACE_ERR) { + /* get the unmasked errors */ + status = __raw_readl(IOP13XX_ATUE_PIE_STS) & + ~(__raw_readl(IOP13XX_ATUE_PIE_MSK)); + + if (status) { + PRINTK("\t\t\tPCI-E error: ATUE_PIE_STS %#08x", + __raw_readl(IOP13XX_ATUE_PIE_STS)); + err++; + } else { + PRINTK("\t\t\tPCI-E error: ATUE_PIE_STS %#08x", + __raw_readl(IOP13XX_ATUE_PIE_STS)); + PRINTK("\t\t\tPCI-E error: ATUE_PIE_MSK %#08x", + __raw_readl(IOP13XX_ATUE_PIE_MSK)); + BUG(); + } + + if(clear) + __raw_writel(status, IOP13XX_ATUE_PIE_STS); + } + } + + return err; +} + +static inline int __init +iop13xx_pcie_map_irq(struct pci_dev *dev, u8 idsel, u8 pin) +{ + WARN_ON(idsel != 0); + + switch (pin) { + case 1: return ATUE_INTA; + case 2: return ATUE_INTB; + case 3: return ATUE_INTC; + case 4: return ATUE_INTD; + default: return -1; + } +} + +static inline u32 iop13xx_atue_read(unsigned long addr) +{ + u32 val; + + __raw_writel(addr, IOP13XX_ATUE_OCCAR); + val = __raw_readl(IOP13XX_ATUE_OCCDR); + + rmb(); + + return val; +} + +/* The read routines must check the error status of the last configuration + * cycle. If there was an error, the routine returns all hex f's. + */ +static int +iop13xx_atue_read_config(struct pci_bus *bus, unsigned int devfn, int where, + int size, u32 *value) +{ + u32 val; + unsigned long addr = iop13xx_atue_cfg_address(bus, devfn, where); + + /* Hide device numbers > 0 on the local PCI-E bus (Type 0 access) */ + if (!PCI_SLOT(devfn) || (addr & 1)) { + val = iop13xx_atue_read(addr) >> ((where & 3) * 8); + if( iop13xx_atue_pci_status(1) || is_atue_occdr_error() ) { + __raw_writel(__raw_readl(IOP13XX_XBG_BECSR) & 3, + IOP13XX_XBG_BECSR); + val = 0xffffffff; + } + + PRINTK("addr=%#0lx, val=%#010x", addr, val); + } else + val = 0xffffffff; + + *value = val; + + return PCIBIOS_SUCCESSFUL; +} + +static int +iop13xx_atue_write_config(struct pci_bus *bus, unsigned int devfn, int where, + int size, u32 value) +{ + unsigned long addr = iop13xx_atue_cfg_address(bus, devfn, where); + u32 val; + + if (size != 4) { + val = iop13xx_atue_read(addr); + if (!iop13xx_atue_pci_status(1) == 0) + return PCIBIOS_SUCCESSFUL; + + where = (where & 3) * 8; + + if (size == 1) + val &= ~(0xff << where); + else + val &= ~(0xffff << where); + + __raw_writel(val | value << where, IOP13XX_ATUE_OCCDR); + } else { + __raw_writel(addr, IOP13XX_ATUE_OCCAR); + __raw_writel(value, IOP13XX_ATUE_OCCDR); + } + + return PCIBIOS_SUCCESSFUL; +} + +static struct pci_ops iop13xx_atue_ops = { + .read = iop13xx_atue_read_config, + .write = iop13xx_atue_write_config, +}; + +/* When a PCI device does not exist during config cycles, the XScale gets a + * bus error instead of returning 0xffffffff. We can't rely on the ATU status + * bits to tell us that it was indeed a configuration cycle that caused this + * error especially in the case when the ATUE link is down. Instead we rely + * on data from the south XSI bridge to validate the abort + */ +int +iop13xx_pci_abort(unsigned long addr, unsigned int fsr, struct pt_regs *regs) +{ + PRINTK("Data abort: address = 0x%08lx " + "fsr = 0x%03x PC = 0x%08lx LR = 0x%08lx", + addr, fsr, regs->ARM_pc, regs->ARM_lr); + + PRINTK("IOP13XX_XBG_BECSR: %#10x", __raw_readl(IOP13XX_XBG_BECSR)); + PRINTK("IOP13XX_XBG_BERAR: %#10x", __raw_readl(IOP13XX_XBG_BERAR)); + PRINTK("IOP13XX_XBG_BERUAR: %#10x", __raw_readl(IOP13XX_XBG_BERUAR)); + + /* If it was an imprecise abort, then we need to correct the + * return address to be _after_ the instruction. + */ + if (fsr & (1 << 10)) + regs->ARM_pc += 4; + + if (is_atue_occdr_error() || is_atux_occdr_error()) + return 0; + else + return 1; +} + +/* Scan an IOP13XX PCI bus. nr selects which ATU we use. + */ +struct pci_bus *iop13xx_scan_bus(int nr, struct pci_sys_data *sys) +{ + int which_atu; + struct pci_bus *bus = NULL; + + switch (init_atu) { + case IOP13XX_INIT_ATU_ATUX: + which_atu = nr ? 0 : IOP13XX_INIT_ATU_ATUX; + break; + case IOP13XX_INIT_ATU_ATUE: + which_atu = nr ? 0 : IOP13XX_INIT_ATU_ATUE; + break; + case (IOP13XX_INIT_ATU_ATUX | IOP13XX_INIT_ATU_ATUE): + which_atu = nr ? IOP13XX_INIT_ATU_ATUE : IOP13XX_INIT_ATU_ATUX; + break; + default: + which_atu = 0; + } + + if (!which_atu) { + BUG(); + return NULL; + } + + switch (which_atu) { + case IOP13XX_INIT_ATU_ATUX: + if (time_after_eq(jiffies + msecs_to_jiffies(1000), + atux_trhfa_timeout)) /* ensure not wrap */ + while(time_before(jiffies, atux_trhfa_timeout)) + udelay(100); + + bus = pci_bus_atux = pci_scan_bus(sys->busnr, + &iop13xx_atux_ops, + sys); + break; + case IOP13XX_INIT_ATU_ATUE: + bus = pci_bus_atue = pci_scan_bus(sys->busnr, + &iop13xx_atue_ops, + sys); + break; + } + + return bus; +} + +/* This function is called from iop13xx_pci_init() after assigning valid + * values to iop13xx_atue_pmmr_offset. This is the location for common + * setup of ATUE for all IOP13XX implementations. + */ +void __init iop13xx_atue_setup(void) +{ + int func = iop13xx_atu_function(IOP13XX_INIT_ATU_ATUE); + u32 reg_val; + + /* BAR 1 (1:1 mapping with Physical RAM) */ + /* Set limit and enable */ + __raw_writel(~(IOP13XX_MAX_RAM_SIZE - PHYS_OFFSET - 1) & ~0x1, + IOP13XX_ATUE_IALR1); + __raw_writel(0x0, IOP13XX_ATUE_IAUBAR1); + + /* Set base at the top of the reserved address space */ + __raw_writel(PHYS_OFFSET | PCI_BASE_ADDRESS_MEM_TYPE_64 | + PCI_BASE_ADDRESS_MEM_PREFETCH, IOP13XX_ATUE_IABAR1); + + /* 1:1 mapping with physical ram + * (leave big endian byte swap disabled) + */ + __raw_writel(0x0, IOP13XX_ATUE_IAUTVR1); + __raw_writel(PHYS_OFFSET, IOP13XX_ATUE_IATVR1); + + /* Outbound window 1 (PCIX/PCIE memory window) */ + /* 32 bit Address Space */ + __raw_writel(0x0, IOP13XX_ATUE_OUMWTVR1); + /* PA[35:32] */ + __raw_writel(IOP13XX_ATUE_OUMBAR_ENABLE | + (IOP13XX_PCIE_MEM_PHYS_OFFSET >> 32), + IOP13XX_ATUE_OUMBAR1); + + /* Setup the I/O Bar + * A[35-16] in 31-12 + */ + __raw_writel(((IOP13XX_PCIE_LOWER_IO_PA >> 0x4) & 0xfffff000), + IOP13XX_ATUE_OIOBAR); + __raw_writel(IOP13XX_PCIE_LOWER_IO_BA, IOP13XX_ATUE_OIOWTVR); + + /* clear startup errors */ + iop13xx_atue_pci_status(1); + + /* OIOBAR function number + */ + reg_val = __raw_readl(IOP13XX_ATUE_OIOBAR); + reg_val &= ~0x7; + reg_val |= func; + __raw_writel(reg_val, IOP13XX_ATUE_OIOBAR); + + /* OUMBAR function numbers + */ + reg_val = __raw_readl(IOP13XX_ATUE_OUMBAR0); + reg_val &= ~(IOP13XX_ATU_OUMBAR_FUNC_NUM_MASK << + IOP13XX_ATU_OUMBAR_FUNC_NUM); + reg_val |= func << IOP13XX_ATU_OUMBAR_FUNC_NUM; + __raw_writel(reg_val, IOP13XX_ATUE_OUMBAR0); + + reg_val = __raw_readl(IOP13XX_ATUE_OUMBAR1); + reg_val &= ~(IOP13XX_ATU_OUMBAR_FUNC_NUM_MASK << + IOP13XX_ATU_OUMBAR_FUNC_NUM); + reg_val |= func << IOP13XX_ATU_OUMBAR_FUNC_NUM; + __raw_writel(reg_val, IOP13XX_ATUE_OUMBAR1); + + reg_val = __raw_readl(IOP13XX_ATUE_OUMBAR2); + reg_val &= ~(IOP13XX_ATU_OUMBAR_FUNC_NUM_MASK << + IOP13XX_ATU_OUMBAR_FUNC_NUM); + reg_val |= func << IOP13XX_ATU_OUMBAR_FUNC_NUM; + __raw_writel(reg_val, IOP13XX_ATUE_OUMBAR2); + + reg_val = __raw_readl(IOP13XX_ATUE_OUMBAR3); + reg_val &= ~(IOP13XX_ATU_OUMBAR_FUNC_NUM_MASK << + IOP13XX_ATU_OUMBAR_FUNC_NUM); + reg_val |= func << IOP13XX_ATU_OUMBAR_FUNC_NUM; + __raw_writel(reg_val, IOP13XX_ATUE_OUMBAR3); + + /* Enable inbound and outbound cycles + */ + reg_val = __raw_readw(IOP13XX_ATUE_ATUCMD); + reg_val |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | + PCI_COMMAND_PARITY | PCI_COMMAND_SERR; + __raw_writew(reg_val, IOP13XX_ATUE_ATUCMD); + + reg_val = __raw_readl(IOP13XX_ATUE_ATUCR); + reg_val |= IOP13XX_ATUE_ATUCR_OUT_EN | + IOP13XX_ATUE_ATUCR_IVM; + __raw_writel(reg_val, IOP13XX_ATUE_ATUCR); +} + +void __init iop13xx_atue_disable(void) +{ + u32 reg_val; + + __raw_writew(0x0, IOP13XX_ATUE_ATUCMD); + __raw_writel(IOP13XX_ATUE_ATUCR_IVM, IOP13XX_ATUE_ATUCR); + + /* wait for cycles to quiesce */ + while (__raw_readl(IOP13XX_ATUE_PCSR) & (IOP13XX_ATUE_PCSR_OUT_Q_BUSY | + IOP13XX_ATUE_PCSR_IN_Q_BUSY | + IOP13XX_ATUE_PCSR_LLRB_BUSY)) + cpu_relax(); + + /* BAR 0 ( Disabled ) */ + __raw_writel(0x0, IOP13XX_ATUE_IAUBAR0); + __raw_writel(0x0, IOP13XX_ATUE_IABAR0); + __raw_writel(0x0, IOP13XX_ATUE_IAUTVR0); + __raw_writel(0x0, IOP13XX_ATUE_IATVR0); + __raw_writel(0x0, IOP13XX_ATUE_IALR0); + reg_val = __raw_readl(IOP13XX_ATUE_OUMBAR0); + reg_val &= ~IOP13XX_ATUE_OUMBAR_ENABLE; + __raw_writel(reg_val, IOP13XX_ATUE_OUMBAR0); + + /* BAR 1 ( Disabled ) */ + __raw_writel(0x0, IOP13XX_ATUE_IAUBAR1); + __raw_writel(0x0, IOP13XX_ATUE_IABAR1); + __raw_writel(0x0, IOP13XX_ATUE_IAUTVR1); + __raw_writel(0x0, IOP13XX_ATUE_IATVR1); + __raw_writel(0x0, IOP13XX_ATUE_IALR1); + reg_val = __raw_readl(IOP13XX_ATUE_OUMBAR1); + reg_val &= ~IOP13XX_ATUE_OUMBAR_ENABLE; + __raw_writel(reg_val, IOP13XX_ATUE_OUMBAR1); + + /* BAR 2 ( Disabled ) */ + __raw_writel(0x0, IOP13XX_ATUE_IAUBAR2); + __raw_writel(0x0, IOP13XX_ATUE_IABAR2); + __raw_writel(0x0, IOP13XX_ATUE_IAUTVR2); + __raw_writel(0x0, IOP13XX_ATUE_IATVR2); + __raw_writel(0x0, IOP13XX_ATUE_IALR2); + reg_val = __raw_readl(IOP13XX_ATUE_OUMBAR2); + reg_val &= ~IOP13XX_ATUE_OUMBAR_ENABLE; + __raw_writel(reg_val, IOP13XX_ATUE_OUMBAR2); + + /* BAR 3 ( Disabled ) */ + reg_val = __raw_readl(IOP13XX_ATUE_OUMBAR3); + reg_val &= ~IOP13XX_ATUE_OUMBAR_ENABLE; + __raw_writel(reg_val, IOP13XX_ATUE_OUMBAR3); + + /* Setup the I/O Bar + * A[35-16] in 31-12 + */ + __raw_writel((IOP13XX_PCIE_LOWER_IO_PA >> 0x4) & 0xfffff000, + IOP13XX_ATUE_OIOBAR); + __raw_writel(IOP13XX_PCIE_LOWER_IO_BA, IOP13XX_ATUE_OIOWTVR); +} + +/* This function is called from iop13xx_pci_init() after assigning valid + * values to iop13xx_atux_pmmr_offset. This is the location for common + * setup of ATUX for all IOP13XX implementations. + */ +void __init iop13xx_atux_setup(void) +{ + u32 reg_val; + int func = iop13xx_atu_function(IOP13XX_INIT_ATU_ATUX); + + /* Take PCI-X bus out of reset if bootloader hasn't already. + * According to spec, we should wait for 2^25 PCI clocks to meet + * the PCI timing parameter Trhfa (RST# high to first access). + * This is rarely necessary and often ignored. + */ + reg_val = __raw_readl(IOP13XX_ATUX_PCSR); + if (reg_val & IOP13XX_ATUX_PCSR_P_RSTOUT) { + int msec = (reg_val >> IOP13XX_ATUX_PCSR_FREQ_OFFSET) & 0x7; + msec = 1000 / (8-msec); /* bits 100=133MHz, 111=>33MHz */ + __raw_writel(reg_val & ~IOP13XX_ATUX_PCSR_P_RSTOUT, + IOP13XX_ATUX_PCSR); + atux_trhfa_timeout = jiffies + msecs_to_jiffies(msec); + } + else + atux_trhfa_timeout = jiffies; + + /* BAR 1 (1:1 mapping with Physical RAM) */ + /* Set limit and enable */ + __raw_writel(~(IOP13XX_MAX_RAM_SIZE - PHYS_OFFSET - 1) & ~0x1, + IOP13XX_ATUX_IALR1); + __raw_writel(0x0, IOP13XX_ATUX_IAUBAR1); + + /* Set base at the top of the reserved address space */ + __raw_writel(PHYS_OFFSET | PCI_BASE_ADDRESS_MEM_TYPE_64 | + PCI_BASE_ADDRESS_MEM_PREFETCH, IOP13XX_ATUX_IABAR1); + + /* 1:1 mapping with physical ram + * (leave big endian byte swap disabled) + */ + __raw_writel(0x0, IOP13XX_ATUX_IAUTVR1); + __raw_writel(PHYS_OFFSET, IOP13XX_ATUX_IATVR1); + + /* Outbound window 1 (PCIX/PCIE memory window) */ + /* 32 bit Address Space */ + __raw_writel(0x0, IOP13XX_ATUX_OUMWTVR1); + /* PA[35:32] */ + __raw_writel(IOP13XX_ATUX_OUMBAR_ENABLE | + IOP13XX_PCIX_MEM_PHYS_OFFSET >> 32, + IOP13XX_ATUX_OUMBAR1); + + /* Setup the I/O Bar + * A[35-16] in 31-12 + */ + __raw_writel((IOP13XX_PCIX_LOWER_IO_PA >> 0x4) & 0xfffff000, + IOP13XX_ATUX_OIOBAR); + __raw_writel(IOP13XX_PCIX_LOWER_IO_BA, IOP13XX_ATUX_OIOWTVR); + + /* clear startup errors */ + iop13xx_atux_pci_status(1); + + /* OIOBAR function number + */ + reg_val = __raw_readl(IOP13XX_ATUX_OIOBAR); + reg_val &= ~0x7; + reg_val |= func; + __raw_writel(reg_val, IOP13XX_ATUX_OIOBAR); + + /* OUMBAR function numbers + */ + reg_val = __raw_readl(IOP13XX_ATUX_OUMBAR0); + reg_val &= ~(IOP13XX_ATU_OUMBAR_FUNC_NUM_MASK << + IOP13XX_ATU_OUMBAR_FUNC_NUM); + reg_val |= func << IOP13XX_ATU_OUMBAR_FUNC_NUM; + __raw_writel(reg_val, IOP13XX_ATUX_OUMBAR0); + + reg_val = __raw_readl(IOP13XX_ATUX_OUMBAR1); + reg_val &= ~(IOP13XX_ATU_OUMBAR_FUNC_NUM_MASK << + IOP13XX_ATU_OUMBAR_FUNC_NUM); + reg_val |= func << IOP13XX_ATU_OUMBAR_FUNC_NUM; + __raw_writel(reg_val, IOP13XX_ATUX_OUMBAR1); + + reg_val = __raw_readl(IOP13XX_ATUX_OUMBAR2); + reg_val &= ~(IOP13XX_ATU_OUMBAR_FUNC_NUM_MASK << + IOP13XX_ATU_OUMBAR_FUNC_NUM); + reg_val |= func << IOP13XX_ATU_OUMBAR_FUNC_NUM; + __raw_writel(reg_val, IOP13XX_ATUX_OUMBAR2); + + reg_val = __raw_readl(IOP13XX_ATUX_OUMBAR3); + reg_val &= ~(IOP13XX_ATU_OUMBAR_FUNC_NUM_MASK << + IOP13XX_ATU_OUMBAR_FUNC_NUM); + reg_val |= func << IOP13XX_ATU_OUMBAR_FUNC_NUM; + __raw_writel(reg_val, IOP13XX_ATUX_OUMBAR3); + + /* Enable inbound and outbound cycles + */ + reg_val = __raw_readw(IOP13XX_ATUX_ATUCMD); + reg_val |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | + PCI_COMMAND_PARITY | PCI_COMMAND_SERR; + __raw_writew(reg_val, IOP13XX_ATUX_ATUCMD); + + reg_val = __raw_readl(IOP13XX_ATUX_ATUCR); + reg_val |= IOP13XX_ATUX_ATUCR_OUT_EN; + __raw_writel(reg_val, IOP13XX_ATUX_ATUCR); +} + +void __init iop13xx_atux_disable(void) +{ + u32 reg_val; + + __raw_writew(0x0, IOP13XX_ATUX_ATUCMD); + __raw_writel(0x0, IOP13XX_ATUX_ATUCR); + + /* wait for cycles to quiesce */ + while (__raw_readl(IOP13XX_ATUX_PCSR) & (IOP13XX_ATUX_PCSR_OUT_Q_BUSY | + IOP13XX_ATUX_PCSR_IN_Q_BUSY)) + cpu_relax(); + + /* BAR 0 ( Disabled ) */ + __raw_writel(0x0, IOP13XX_ATUX_IAUBAR0); + __raw_writel(0x0, IOP13XX_ATUX_IABAR0); + __raw_writel(0x0, IOP13XX_ATUX_IAUTVR0); + __raw_writel(0x0, IOP13XX_ATUX_IATVR0); + __raw_writel(0x0, IOP13XX_ATUX_IALR0); + reg_val = __raw_readl(IOP13XX_ATUX_OUMBAR0); + reg_val &= ~IOP13XX_ATUX_OUMBAR_ENABLE; + __raw_writel(reg_val, IOP13XX_ATUX_OUMBAR0); + + /* BAR 1 ( Disabled ) */ + __raw_writel(0x0, IOP13XX_ATUX_IAUBAR1); + __raw_writel(0x0, IOP13XX_ATUX_IABAR1); + __raw_writel(0x0, IOP13XX_ATUX_IAUTVR1); + __raw_writel(0x0, IOP13XX_ATUX_IATVR1); + __raw_writel(0x0, IOP13XX_ATUX_IALR1); + reg_val = __raw_readl(IOP13XX_ATUX_OUMBAR1); + reg_val &= ~IOP13XX_ATUX_OUMBAR_ENABLE; + __raw_writel(reg_val, IOP13XX_ATUX_OUMBAR1); + + /* BAR 2 ( Disabled ) */ + __raw_writel(0x0, IOP13XX_ATUX_IAUBAR2); + __raw_writel(0x0, IOP13XX_ATUX_IABAR2); + __raw_writel(0x0, IOP13XX_ATUX_IAUTVR2); + __raw_writel(0x0, IOP13XX_ATUX_IATVR2); + __raw_writel(0x0, IOP13XX_ATUX_IALR2); + reg_val = __raw_readl(IOP13XX_ATUX_OUMBAR2); + reg_val &= ~IOP13XX_ATUX_OUMBAR_ENABLE; + __raw_writel(reg_val, IOP13XX_ATUX_OUMBAR2); + + /* BAR 3 ( Disabled ) */ + __raw_writel(0x0, IOP13XX_ATUX_IAUBAR3); + __raw_writel(0x0, IOP13XX_ATUX_IABAR3); + __raw_writel(0x0, IOP13XX_ATUX_IAUTVR3); + __raw_writel(0x0, IOP13XX_ATUX_IATVR3); + __raw_writel(0x0, IOP13XX_ATUX_IALR3); + reg_val = __raw_readl(IOP13XX_ATUX_OUMBAR3); + reg_val &= ~IOP13XX_ATUX_OUMBAR_ENABLE; + __raw_writel(reg_val, IOP13XX_ATUX_OUMBAR3); + + /* Setup the I/O Bar + * A[35-16] in 31-12 + */ + __raw_writel((IOP13XX_PCIX_LOWER_IO_PA >> 0x4) & 0xfffff000, + IOP13XX_ATUX_OIOBAR); + __raw_writel(IOP13XX_PCIX_LOWER_IO_BA, IOP13XX_ATUX_OIOWTVR); +} + +void __init iop13xx_set_atu_mmr_bases(void) +{ + /* Based on ESSR0, determine the ATU X/E offsets */ + switch(__raw_readl(IOP13XX_ESSR0) & + (IOP13XX_CONTROLLER_ONLY | IOP13XX_INTERFACE_SEL_PCIX)) { + /* both asserted */ + case 0: + iop13xx_atux_pmmr_offset = IOP13XX_ATU1_PMMR_OFFSET; + iop13xx_atue_pmmr_offset = IOP13XX_ATU2_PMMR_OFFSET; + break; + /* IOP13XX_CONTROLLER_ONLY = deasserted + * IOP13XX_INTERFACE_SEL_PCIX = asserted + */ + case IOP13XX_CONTROLLER_ONLY: + iop13xx_atux_pmmr_offset = IOP13XX_ATU0_PMMR_OFFSET; + iop13xx_atue_pmmr_offset = IOP13XX_ATU2_PMMR_OFFSET; + break; + /* IOP13XX_CONTROLLER_ONLY = asserted + * IOP13XX_INTERFACE_SEL_PCIX = deasserted + */ + case IOP13XX_INTERFACE_SEL_PCIX: + iop13xx_atux_pmmr_offset = IOP13XX_ATU1_PMMR_OFFSET; + iop13xx_atue_pmmr_offset = IOP13XX_ATU2_PMMR_OFFSET; + break; + /* both deasserted */ + case IOP13XX_CONTROLLER_ONLY | IOP13XX_INTERFACE_SEL_PCIX: + iop13xx_atux_pmmr_offset = IOP13XX_ATU2_PMMR_OFFSET; + iop13xx_atue_pmmr_offset = IOP13XX_ATU0_PMMR_OFFSET; + break; + default: + BUG(); + } +} + +void __init iop13xx_atu_select(struct hw_pci *plat_pci) +{ + int i; + + /* set system defaults + * note: if "iop13xx_init_atu=" is specified this autodetect + * sequence will be bypassed + */ + if (init_atu == IOP13XX_INIT_ATU_DEFAULT) { + /* check for single/dual interface */ + if (__raw_readl(IOP13XX_ESSR0) & IOP13XX_INTERFACE_SEL_PCIX) { + /* ATUE must be present check the device id + * to see if ATUX is present. + */ + init_atu |= IOP13XX_INIT_ATU_ATUE; + switch (__raw_readw(IOP13XX_ATUE_DID) & 0xf0) { + case 0x70: + case 0x80: + case 0xc0: + init_atu |= IOP13XX_INIT_ATU_ATUX; + break; + } + } else { + /* ATUX must be present check the device id + * to see if ATUE is present. + */ + init_atu |= IOP13XX_INIT_ATU_ATUX; + switch (__raw_readw(IOP13XX_ATUX_DID) & 0xf0) { + case 0x70: + case 0x80: + case 0xc0: + init_atu |= IOP13XX_INIT_ATU_ATUE; + break; + } + } + + /* check central resource and root complex capability */ + if (init_atu & IOP13XX_INIT_ATU_ATUX) + if (!(__raw_readl(IOP13XX_ATUX_PCSR) & + IOP13XX_ATUX_PCSR_CENTRAL_RES)) + init_atu &= ~IOP13XX_INIT_ATU_ATUX; + + if (init_atu & IOP13XX_INIT_ATU_ATUE) + if (__raw_readl(IOP13XX_ATUE_PCSR) & + IOP13XX_ATUE_PCSR_END_POINT) + init_atu &= ~IOP13XX_INIT_ATU_ATUE; + } + + for (i = 0; i < 2; i++) { + if((init_atu & (1 << i)) == (1 << i)) + plat_pci->nr_controllers++; + } +} + +void __init iop13xx_pci_init(void) +{ + /* clear pre-existing south bridge errors */ + __raw_writel(__raw_readl(IOP13XX_XBG_BECSR) & 3, IOP13XX_XBG_BECSR); + + /* Setup the Min Address for PCI memory... */ + iop13xx_pcibios_min_mem = IOP13XX_PCIX_LOWER_MEM_BA; + + /* if Linux is given control of an ATU + * clear out its prior configuration, + * otherwise do not touch the registers + */ + if (init_atu & IOP13XX_INIT_ATU_ATUE) { + iop13xx_atue_disable(); + iop13xx_atue_setup(); + } + + if (init_atu & IOP13XX_INIT_ATU_ATUX) { + iop13xx_atux_disable(); + iop13xx_atux_setup(); + } + + hook_fault_code(16+6, iop13xx_pci_abort, SIGBUS, + "imprecise external abort"); +} + +/* intialize the pci memory space. handle any combination of + * atue and atux enabled/disabled + */ +int iop13xx_pci_setup(int nr, struct pci_sys_data *sys) +{ + struct resource *res; + int which_atu; + u32 pcixsr, pcsr; + + if (nr > 1) + return 0; + + res = kmalloc(sizeof(struct resource) * 2, GFP_KERNEL); + if (!res) + panic("PCI: unable to alloc resources"); + + memset(res, 0, sizeof(struct resource) * 2); + + /* 'nr' assumptions: + * ATUX is always 0 + * ATUE is 1 when ATUX is also enabled + * ATUE is 0 when ATUX is disabled + */ + switch(init_atu) { + case IOP13XX_INIT_ATU_ATUX: + which_atu = nr ? 0 : IOP13XX_INIT_ATU_ATUX; + break; + case IOP13XX_INIT_ATU_ATUE: + which_atu = nr ? 0 : IOP13XX_INIT_ATU_ATUE; + break; + case (IOP13XX_INIT_ATU_ATUX | IOP13XX_INIT_ATU_ATUE): + which_atu = nr ? IOP13XX_INIT_ATU_ATUE : IOP13XX_INIT_ATU_ATUX; + break; + default: + which_atu = 0; + } + + if (!which_atu) + return 0; + + switch(which_atu) { + case IOP13XX_INIT_ATU_ATUX: + pcixsr = __raw_readl(IOP13XX_ATUX_PCIXSR); + pcixsr &= ~0xffff; + pcixsr |= sys->busnr << IOP13XX_ATUX_PCIXSR_BUS_NUM | + 0 << IOP13XX_ATUX_PCIXSR_DEV_NUM | + iop13xx_atu_function(IOP13XX_INIT_ATU_ATUX) + << IOP13XX_ATUX_PCIXSR_FUNC_NUM; + __raw_writel(pcixsr, IOP13XX_ATUX_PCIXSR); + + res[0].start = IOP13XX_PCIX_LOWER_IO_PA; + res[0].end = IOP13XX_PCIX_UPPER_IO_PA; + res[0].name = "IQ81340 ATUX PCI I/O Space"; + res[0].flags = IORESOURCE_IO; + + res[1].start = IOP13XX_PCIX_LOWER_MEM_RA; + res[1].end = IOP13XX_PCIX_UPPER_MEM_RA; + res[1].name = "IQ81340 ATUX PCI Memory Space"; + res[1].flags = IORESOURCE_MEM; + sys->mem_offset = IOP13XX_PCIX_MEM_OFFSET; + sys->io_offset = IOP13XX_PCIX_IO_OFFSET; + break; + case IOP13XX_INIT_ATU_ATUE: + /* Note: the function number field in the PCSR is ro */ + pcsr = __raw_readl(IOP13XX_ATUE_PCSR); + pcsr &= ~(0xfff8 << 16); + pcsr |= sys->busnr << IOP13XX_ATUE_PCSR_BUS_NUM | + 0 << IOP13XX_ATUE_PCSR_DEV_NUM; + + __raw_writel(pcsr, IOP13XX_ATUE_PCSR); + + res[0].start = IOP13XX_PCIE_LOWER_IO_PA; + res[0].end = IOP13XX_PCIE_UPPER_IO_PA; + res[0].name = "IQ81340 ATUE PCI I/O Space"; + res[0].flags = IORESOURCE_IO; + + res[1].start = IOP13XX_PCIE_LOWER_MEM_RA; + res[1].end = IOP13XX_PCIE_UPPER_MEM_RA; + res[1].name = "IQ81340 ATUE PCI Memory Space"; + res[1].flags = IORESOURCE_MEM; + sys->mem_offset = IOP13XX_PCIE_MEM_OFFSET; + sys->io_offset = IOP13XX_PCIE_IO_OFFSET; + sys->map_irq = iop13xx_pcie_map_irq; + break; + default: + return 0; + } + + request_resource(&ioport_resource, &res[0]); + request_resource(&iomem_resource, &res[1]); + + sys->resource[0] = &res[0]; + sys->resource[1] = &res[1]; + sys->resource[2] = NULL; + + return 1; +} + +u16 iop13xx_dev_id(void) +{ + if (__raw_readl(IOP13XX_ESSR0) & IOP13XX_INTERFACE_SEL_PCIX) + return __raw_readw(IOP13XX_ATUE_DID); + else + return __raw_readw(IOP13XX_ATUX_DID); +} + +static int __init iop13xx_init_atu_setup(char *str) +{ + init_atu = IOP13XX_INIT_ATU_NONE; + if (str) { + while (*str != '\0') { + switch (*str) { + case 'x': + case 'X': + init_atu |= IOP13XX_INIT_ATU_ATUX; + init_atu &= ~IOP13XX_INIT_ATU_NONE; + break; + case 'e': + case 'E': + init_atu |= IOP13XX_INIT_ATU_ATUE; + init_atu &= ~IOP13XX_INIT_ATU_NONE; + break; + case ',': + case '=': + break; + default: + PRINTK("\"iop13xx_init_atu\" malformed at " + "character: \'%c\'", *str); + *(str + 1) = '\0'; + init_atu = IOP13XX_INIT_ATU_DEFAULT; + } + str++; + } + } + return 1; +} + +__setup("iop13xx_init_atu", iop13xx_init_atu_setup); diff --git a/arch/arm/mach-iop13xx/setup.c b/arch/arm/mach-iop13xx/setup.c new file mode 100644 index 00000000000..3756d2ccb1a --- /dev/null +++ b/arch/arm/mach-iop13xx/setup.c @@ -0,0 +1,406 @@ +/* + * iop13xx platform Initialization + * Copyright (c) 2005-2006, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + * + */ + +#include <linux/serial_8250.h> +#ifdef CONFIG_MTD_PHYSMAP +#include <linux/mtd/physmap.h> +#endif +#include <asm/mach/map.h> +#include <asm/hardware.h> +#include <asm/irq.h> + +#define IOP13XX_UART_XTAL 33334000 +#define IOP13XX_SETUP_DEBUG 0 +#define PRINTK(x...) ((void)(IOP13XX_SETUP_DEBUG && printk(x))) + +/* Standard IO mapping for all IOP13XX based systems + */ +static struct map_desc iop13xx_std_desc[] __initdata = { + { /* mem mapped registers */ + .virtual = IOP13XX_PMMR_VIRT_MEM_BASE, + .pfn = __phys_to_pfn(IOP13XX_PMMR_PHYS_MEM_BASE), + .length = IOP13XX_PMMR_SIZE, + .type = MT_DEVICE, + }, { /* PCIE IO space */ + .virtual = IOP13XX_PCIE_LOWER_IO_VA, + .pfn = __phys_to_pfn(IOP13XX_PCIE_LOWER_IO_PA), + .length = IOP13XX_PCIX_IO_WINDOW_SIZE, + .type = MT_DEVICE, + }, { /* PCIX IO space */ + .virtual = IOP13XX_PCIX_LOWER_IO_VA, + .pfn = __phys_to_pfn(IOP13XX_PCIX_LOWER_IO_PA), + .length = IOP13XX_PCIX_IO_WINDOW_SIZE, + .type = MT_DEVICE, + }, +}; + +static struct resource iop13xx_uart0_resources[] = { + [0] = { + .start = IOP13XX_UART0_PHYS, + .end = IOP13XX_UART0_PHYS + 0x3f, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_IOP13XX_UART0, + .end = IRQ_IOP13XX_UART0, + .flags = IORESOURCE_IRQ + } +}; + +static struct resource iop13xx_uart1_resources[] = { + [0] = { + .start = IOP13XX_UART1_PHYS, + .end = IOP13XX_UART1_PHYS + 0x3f, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_IOP13XX_UART1, + .end = IRQ_IOP13XX_UART1, + .flags = IORESOURCE_IRQ + } +}; + +static struct plat_serial8250_port iop13xx_uart0_data[] = { + { + .membase = (char*)(IOP13XX_UART0_VIRT), + .mapbase = (IOP13XX_UART0_PHYS), + .irq = IRQ_IOP13XX_UART0, + .uartclk = IOP13XX_UART_XTAL, + .regshift = 2, + .iotype = UPIO_MEM, + .flags = UPF_SKIP_TEST, + }, + { }, +}; + +static struct plat_serial8250_port iop13xx_uart1_data[] = { + { + .membase = (char*)(IOP13XX_UART1_VIRT), + .mapbase = (IOP13XX_UART1_PHYS), + .irq = IRQ_IOP13XX_UART1, + .uartclk = IOP13XX_UART_XTAL, + .regshift = 2, + .iotype = UPIO_MEM, + .flags = UPF_SKIP_TEST, + }, + { }, +}; + +/* The ids are fixed up later in iop13xx_platform_init */ +static struct platform_device iop13xx_uart0 = { + .name = "serial8250", + .id = 0, + .dev.platform_data = iop13xx_uart0_data, + .num_resources = 2, + .resource = iop13xx_uart0_resources, +}; + +static struct platform_device iop13xx_uart1 = { + .name = "serial8250", + .id = 0, + .dev.platform_data = iop13xx_uart1_data, + .num_resources = 2, + .resource = iop13xx_uart1_resources +}; + +static struct resource iop13xx_i2c_0_resources[] = { + [0] = { + .start = IOP13XX_I2C0_PHYS, + .end = IOP13XX_I2C0_PHYS + 0x18, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_IOP13XX_I2C_0, + .end = IRQ_IOP13XX_I2C_0, + .flags = IORESOURCE_IRQ + } +}; + +static struct resource iop13xx_i2c_1_resources[] = { + [0] = { + .start = IOP13XX_I2C1_PHYS, + .end = IOP13XX_I2C1_PHYS + 0x18, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_IOP13XX_I2C_1, + .end = IRQ_IOP13XX_I2C_1, + .flags = IORESOURCE_IRQ + } +}; + +static struct resource iop13xx_i2c_2_resources[] = { + [0] = { + .start = IOP13XX_I2C2_PHYS, + .end = IOP13XX_I2C2_PHYS + 0x18, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_IOP13XX_I2C_2, + .end = IRQ_IOP13XX_I2C_2, + .flags = IORESOURCE_IRQ + } +}; + +/* I2C controllers. The IOP13XX uses the same block as the IOP3xx, so + * we just use the same device name. + */ + +/* The ids are fixed up later in iop13xx_platform_init */ +static struct platform_device iop13xx_i2c_0_controller = { + .name = "IOP3xx-I2C", + .id = 0, + .num_resources = 2, + .resource = iop13xx_i2c_0_resources +}; + +static struct platform_device iop13xx_i2c_1_controller = { + .name = "IOP3xx-I2C", + .id = 0, + .num_resources = 2, + .resource = iop13xx_i2c_1_resources +}; + +static struct platform_device iop13xx_i2c_2_controller = { + .name = "IOP3xx-I2C", + .id = 0, + .num_resources = 2, + .resource = iop13xx_i2c_2_resources +}; + +#ifdef CONFIG_MTD_PHYSMAP +/* PBI Flash Device + */ +static struct physmap_flash_data iq8134x_flash_data = { + .width = 2, +}; + +static struct resource iq8134x_flash_resource = { + .start = IQ81340_FLASHBASE, + .end = 0, + .flags = IORESOURCE_MEM, +}; + +static struct platform_device iq8134x_flash = { + .name = "physmap-flash", + .id = 0, + .dev = { .platform_data = &iq8134x_flash_data, }, + .num_resources = 1, + .resource = &iq8134x_flash_resource, +}; + +static unsigned long iq8134x_probe_flash_size(void) +{ + uint8_t __iomem *flash_addr = ioremap(IQ81340_FLASHBASE, PAGE_SIZE); + int i; + char query[3]; + unsigned long size = 0; + int width = iq8134x_flash_data.width; + + if (flash_addr) { + /* send CFI 'query' command */ + writew(0x98, flash_addr); + + /* check for CFI compliance */ + for (i = 0; i < 3 * width; i += width) + query[i / width] = readb(flash_addr + (0x10 * width) + i); + + /* read the size */ + if (memcmp(query, "QRY", 3) == 0) + size = 1 << readb(flash_addr + (0x27 * width)); + + /* send CFI 'read array' command */ + writew(0xff, flash_addr); + + iounmap(flash_addr); + } + + return size; +} +#endif + +void __init iop13xx_map_io(void) +{ + /* Initialize the Static Page Table maps */ + iotable_init(iop13xx_std_desc, ARRAY_SIZE(iop13xx_std_desc)); +} + +static int init_uart = 0; +static int init_i2c = 0; + +void __init iop13xx_platform_init(void) +{ + int i; + u32 uart_idx, i2c_idx, plat_idx; + struct platform_device *iop13xx_devices[IQ81340_MAX_PLAT_DEVICES]; + + /* set the bases so we can read the device id */ + iop13xx_set_atu_mmr_bases(); + + memset(iop13xx_devices, 0, sizeof(iop13xx_devices)); + + if (init_uart == IOP13XX_INIT_UART_DEFAULT) { + switch (iop13xx_dev_id()) { + /* enable both uarts on iop341 and iop342 */ + case 0x3380: + case 0x3384: + case 0x3388: + case 0x338c: + case 0x3382: + case 0x3386: + case 0x338a: + case 0x338e: + init_uart |= IOP13XX_INIT_UART_0; + init_uart |= IOP13XX_INIT_UART_1; + break; + /* only enable uart 1 */ + default: + init_uart |= IOP13XX_INIT_UART_1; + } + } + + if (init_i2c == IOP13XX_INIT_I2C_DEFAULT) { + switch (iop13xx_dev_id()) { + /* enable all i2c units on iop341 and iop342 */ + case 0x3380: + case 0x3384: + case 0x3388: + case 0x338c: + case 0x3382: + case 0x3386: + case 0x338a: + case 0x338e: + init_i2c |= IOP13XX_INIT_I2C_0; + init_i2c |= IOP13XX_INIT_I2C_1; + init_i2c |= IOP13XX_INIT_I2C_2; + break; + /* only enable i2c 1 and 2 */ + default: + init_i2c |= IOP13XX_INIT_I2C_1; + init_i2c |= IOP13XX_INIT_I2C_2; + } + } + + plat_idx = 0; + uart_idx = 0; + i2c_idx = 0; + + /* uart 1 (if enabled) is ttyS0 */ + if (init_uart & IOP13XX_INIT_UART_1) { + PRINTK("Adding uart1 to platform device list\n"); + iop13xx_uart1.id = uart_idx++; + iop13xx_devices[plat_idx++] = &iop13xx_uart1; + } + if (init_uart & IOP13XX_INIT_UART_0) { + PRINTK("Adding uart0 to platform device list\n"); + iop13xx_uart0.id = uart_idx++; + iop13xx_devices[plat_idx++] = &iop13xx_uart0; + } + + for(i = 0; i < IQ81340_NUM_I2C; i++) { + if ((init_i2c & (1 << i)) && IOP13XX_SETUP_DEBUG) + printk("Adding i2c%d to platform device list\n", i); + switch(init_i2c & (1 << i)) { + case IOP13XX_INIT_I2C_0: + iop13xx_i2c_0_controller.id = i2c_idx++; + iop13xx_devices[plat_idx++] = + &iop13xx_i2c_0_controller; + break; + case IOP13XX_INIT_I2C_1: + iop13xx_i2c_1_controller.id = i2c_idx++; + iop13xx_devices[plat_idx++] = + &iop13xx_i2c_1_controller; + break; + case IOP13XX_INIT_I2C_2: + iop13xx_i2c_2_controller.id = i2c_idx++; + iop13xx_devices[plat_idx++] = + &iop13xx_i2c_2_controller; + break; + } + } + +#ifdef CONFIG_MTD_PHYSMAP + iq8134x_flash_resource.end = iq8134x_flash_resource.start + + iq8134x_probe_flash_size(); + if (iq8134x_flash_resource.end > iq8134x_flash_resource.start) + iop13xx_devices[plat_idx++] = &iq8134x_flash; + else + printk(KERN_ERR "%s: Failed to probe flash size\n", __FUNCTION__); +#endif + + platform_add_devices(iop13xx_devices, plat_idx); +} + +static int __init iop13xx_init_uart_setup(char *str) +{ + if (str) { + while (*str != '\0') { + switch(*str) { + case '0': + init_uart |= IOP13XX_INIT_UART_0; + break; + case '1': + init_uart |= IOP13XX_INIT_UART_1; + break; + case ',': + case '=': + break; + default: + PRINTK("\"iop13xx_init_uart\" malformed" + " at character: \'%c\'", *str); + *(str + 1) = '\0'; + init_uart = IOP13XX_INIT_UART_DEFAULT; + } + str++; + } + } + return 1; +} + +static int __init iop13xx_init_i2c_setup(char *str) +{ + if (str) { + while (*str != '\0') { + switch(*str) { + case '0': + init_i2c |= IOP13XX_INIT_I2C_0; + break; + case '1': + init_i2c |= IOP13XX_INIT_I2C_1; + break; + case '2': + init_i2c |= IOP13XX_INIT_I2C_2; + break; + case ',': + case '=': + break; + default: + PRINTK("\"iop13xx_init_i2c\" malformed" + " at character: \'%c\'", *str); + *(str + 1) = '\0'; + init_i2c = IOP13XX_INIT_I2C_DEFAULT; + } + str++; + } + } + return 1; +} + +__setup("iop13xx_init_uart", iop13xx_init_uart_setup); +__setup("iop13xx_init_i2c", iop13xx_init_i2c_setup); diff --git a/arch/arm/mach-iop13xx/time.c b/arch/arm/mach-iop13xx/time.c new file mode 100644 index 00000000000..8b21365f653 --- /dev/null +++ b/arch/arm/mach-iop13xx/time.c @@ -0,0 +1,102 @@ +/* + * arch/arm/mach-iop13xx/time.c + * + * Timer code for IOP13xx (copied from IOP32x/IOP33x implementation) + * + * Author: Deepak Saxena <dsaxena@mvista.com> + * + * Copyright 2002-2003 MontaVista Software Inc. + * + * 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; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/kernel.h> +#include <linux/interrupt.h> +#include <linux/time.h> +#include <linux/init.h> +#include <linux/timex.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/uaccess.h> +#include <asm/mach/irq.h> +#include <asm/mach/time.h> + +static unsigned long ticks_per_jiffy; +static unsigned long ticks_per_usec; +static unsigned long next_jiffy_time; + +static inline u32 read_tcr1(void) +{ + u32 val; + asm volatile("mrc p6, 0, %0, c3, c9, 0" : "=r" (val)); + return val; +} + +unsigned long iop13xx_gettimeoffset(void) +{ + unsigned long offset; + u32 cp_flags; + + cp_flags = iop13xx_cp6_save(); + offset = next_jiffy_time - read_tcr1(); + iop13xx_cp6_restore(cp_flags); + + return offset / ticks_per_usec; +} + +static irqreturn_t +iop13xx_timer_interrupt(int irq, void *dev_id) +{ + u32 cp_flags = iop13xx_cp6_save(); + + write_seqlock(&xtime_lock); + + asm volatile("mcr p6, 0, %0, c6, c9, 0" : : "r" (1)); + + while ((signed long)(next_jiffy_time - read_tcr1()) + >= ticks_per_jiffy) { + timer_tick(); + next_jiffy_time -= ticks_per_jiffy; + } + + write_sequnlock(&xtime_lock); + + iop13xx_cp6_restore(cp_flags); + + return IRQ_HANDLED; +} + +static struct irqaction iop13xx_timer_irq = { + .name = "IOP13XX Timer Tick", + .handler = iop13xx_timer_interrupt, + .flags = IRQF_DISABLED | IRQF_TIMER, +}; + +void __init iop13xx_init_time(unsigned long tick_rate) +{ + u32 timer_ctl; + u32 cp_flags; + + ticks_per_jiffy = (tick_rate + HZ/2) / HZ; + ticks_per_usec = tick_rate / 1000000; + next_jiffy_time = 0xffffffff; + + timer_ctl = IOP13XX_TMR_EN | IOP13XX_TMR_PRIVILEGED | + IOP13XX_TMR_RELOAD | IOP13XX_TMR_RATIO_1_1; + + /* + * We use timer 0 for our timer interrupt, and timer 1 as + * monotonic counter for tracking missed jiffies. + */ + cp_flags = iop13xx_cp6_save(); + asm volatile("mcr p6, 0, %0, c4, c9, 0" : : "r" (ticks_per_jiffy - 1)); + asm volatile("mcr p6, 0, %0, c0, c9, 0" : : "r" (timer_ctl)); + asm volatile("mcr p6, 0, %0, c5, c9, 0" : : "r" (0xffffffff)); + asm volatile("mcr p6, 0, %0, c1, c9, 0" : : "r" (timer_ctl)); + iop13xx_cp6_restore(cp_flags); + + setup_irq(IRQ_IOP13XX_TIMER0, &iop13xx_timer_irq); +} diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig index c0bfb8212b7..4c4dae6901f 100644 --- a/arch/arm/mm/Kconfig +++ b/arch/arm/mm/Kconfig @@ -333,7 +333,7 @@ config CPU_XSCALE # XScale Core Version 3 config CPU_XSC3 bool - depends on ARCH_IXP23XX + depends on ARCH_IXP23XX || ARCH_IOP13XX default y select CPU_32v5 select CPU_ABRT_EV5T |