From c231d6976a161ae7b5b3aba4a1821b2e9c00c02c Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Tue, 27 Jul 2010 21:34:38 -0700 Subject: [ARM] tegra: update iomap Add missing io address map entries from datasheet. Add the IRAM area to the statically mapped io regions. Correct the onewire, USB, and statmon addresses Signed-off-by: Colin Cross --- arch/arm/mach-tegra/include/mach/io.h | 6 ++++++ arch/arm/mach-tegra/include/mach/iomap.h | 33 ++++++++++++++++++++++++++------ arch/arm/mach-tegra/io.c | 6 ++++++ 3 files changed, 39 insertions(+), 6 deletions(-) (limited to 'arch') diff --git a/arch/arm/mach-tegra/include/mach/io.h b/arch/arm/mach-tegra/include/mach/io.h index 35edfc32ffc9..16f16189b5eb 100644 --- a/arch/arm/mach-tegra/include/mach/io.h +++ b/arch/arm/mach-tegra/include/mach/io.h @@ -33,6 +33,10 @@ * */ +#define IO_IRAM_PHYS 0x40000000 +#define IO_IRAM_VIRT 0xFE400000 +#define IO_IRAM_SIZE SZ_256K + #define IO_CPU_PHYS 0x50040000 #define IO_CPU_VIRT 0xFE000000 #define IO_CPU_SIZE SZ_16K @@ -55,6 +59,8 @@ IO_TO_VIRT_XLATE((n), IO_APB_PHYS, IO_APB_VIRT) : \ IO_TO_VIRT_BETWEEN((n), IO_CPU_PHYS, IO_CPU_SIZE) ? \ IO_TO_VIRT_XLATE((n), IO_CPU_PHYS, IO_CPU_VIRT) : \ + IO_TO_VIRT_BETWEEN((n), IO_IRAM_PHYS, IO_IRAM_SIZE) ? \ + IO_TO_VIRT_XLATE((n), IO_IRAM_PHYS, IO_IRAM_VIRT) : \ 0) #ifndef __ASSEMBLER__ diff --git a/arch/arm/mach-tegra/include/mach/iomap.h b/arch/arm/mach-tegra/include/mach/iomap.h index 1741f7dd7a9b..44a4f4bcf91f 100644 --- a/arch/arm/mach-tegra/include/mach/iomap.h +++ b/arch/arm/mach-tegra/include/mach/iomap.h @@ -23,9 +23,15 @@ #include +#define TEGRA_IRAM_BASE 0x40000000 +#define TEGRA_IRAM_SIZE SZ_256K + #define TEGRA_ARM_PERIF_BASE 0x50040000 #define TEGRA_ARM_PERIF_SIZE SZ_8K +#define TEGRA_ARM_PL310_BASE 0x50043000 +#define TEGRA_ARM_PL310_SIZE SZ_4K + #define TEGRA_ARM_INT_DIST_BASE 0x50041000 #define TEGRA_ARM_INT_DIST_SIZE SZ_4K @@ -68,7 +74,22 @@ #define TEGRA_FLOW_CTRL_BASE 0x60007000 #define TEGRA_FLOW_CTRL_SIZE 20 -#define TEGRA_STATMON_BASE 0x6000C4000 +#define TEGRA_AHB_DMA_BASE 0x60008000 +#define TEGRA_AHB_DMA_SIZE SZ_4K + +#define TEGRA_AHB_DMA_CH0_BASE 0x60009000 +#define TEGRA_AHB_DMA_CH0_SIZE 32 + +#define TEGRA_APB_DMA_BASE 0x6000A000 +#define TEGRA_APB_DMA_SIZE SZ_4K + +#define TEGRA_APB_DMA_CH0_BASE 0x6000B000 +#define TEGRA_APB_DMA_CH0_SIZE 32 + +#define TEGRA_AHB_GIZMO_BASE 0x6000C004 +#define TEGRA_AHB_GIZMO_SIZE 0x10C + +#define TEGRA_STATMON_BASE 0x6000C400 #define TEGRA_STATMON_SIZE SZ_1K #define TEGRA_GPIO_BASE 0x6000D000 @@ -137,7 +158,7 @@ #define TEGRA_I2C3_BASE 0x7000C500 #define TEGRA_I2C3_SIZE SZ_256 -#define TEGRA_OWR_BASE 0x7000D000 +#define TEGRA_OWR_BASE 0x7000C600 #define TEGRA_OWR_SIZE 80 #define TEGRA_DVC_BASE 0x7000D000 @@ -182,12 +203,12 @@ #define TEGRA_USB_BASE 0xC5000000 #define TEGRA_USB_SIZE SZ_16K -#define TEGRA_USB1_BASE 0xC5004000 -#define TEGRA_USB1_SIZE SZ_16K - -#define TEGRA_USB2_BASE 0xC5008000 +#define TEGRA_USB2_BASE 0xC5004000 #define TEGRA_USB2_SIZE SZ_16K +#define TEGRA_USB3_BASE 0xC5008000 +#define TEGRA_USB3_SIZE SZ_16K + #define TEGRA_SDMMC1_BASE 0xC8000000 #define TEGRA_SDMMC1_SIZE SZ_512 diff --git a/arch/arm/mach-tegra/io.c b/arch/arm/mach-tegra/io.c index 9fe2c5c683d4..31848a9592f8 100644 --- a/arch/arm/mach-tegra/io.c +++ b/arch/arm/mach-tegra/io.c @@ -49,6 +49,12 @@ static struct map_desc tegra_io_desc[] __initdata = { .length = IO_CPU_SIZE, .type = MT_DEVICE, }, + { + .virtual = IO_IRAM_VIRT, + .pfn = __phys_to_pfn(IO_IRAM_PHYS), + .length = IO_IRAM_SIZE, + .type = MT_DEVICE, + }, }; void __init tegra_map_common_io(void) -- cgit v1.2.3 From 8726e4f50e3f445601c19a851c62586f5dc7dd49 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Mon, 5 Apr 2010 15:22:21 -0700 Subject: [ARM] tegra: Add legacy irq support The "legacy irq controller" duplicates the functionality of the GIC, but remains powered during the cpu suspend and idle modes that power down the CPU and the GIC. Signed-off-by: Colin Cross --- arch/arm/mach-tegra/Makefile | 2 +- arch/arm/mach-tegra/include/mach/legacy_irq.h | 31 +++++++ arch/arm/mach-tegra/legacy_irq.c | 114 ++++++++++++++++++++++++++ 3 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 arch/arm/mach-tegra/include/mach/legacy_irq.h create mode 100644 arch/arm/mach-tegra/legacy_irq.c (limited to 'arch') diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile index 51e9370eed99..0a975b5fe493 100644 --- a/arch/arm/mach-tegra/Makefile +++ b/arch/arm/mach-tegra/Makefile @@ -1,6 +1,6 @@ obj-y += common.o obj-y += io.o -obj-y += irq.o +obj-y += irq.o legacy_irq.o obj-y += clock.o obj-y += timer.o obj-y += gpio.o diff --git a/arch/arm/mach-tegra/include/mach/legacy_irq.h b/arch/arm/mach-tegra/include/mach/legacy_irq.h new file mode 100644 index 000000000000..db1eb3dd04c8 --- /dev/null +++ b/arch/arm/mach-tegra/include/mach/legacy_irq.h @@ -0,0 +1,31 @@ +/* + * arch/arm/mach-tegra/include/mach/legacy_irq.h + * + * Copyright (C) 2010 Google, Inc. + * Author: Colin Cross + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that 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. + * + */ + +#ifndef _ARCH_ARM_MACH_TEGRA_LEGARY_IRQ_H +#define _ARCH_ARM_MACH_TEGRA_LEGARY_IRQ_H + +void tegra_legacy_mask_irq(unsigned int irq); +void tegra_legacy_unmask_irq(unsigned int irq); +void tegra_legacy_select_fiq(unsigned int irq, bool fiq); +void tegra_legacy_force_irq_set(unsigned int irq); +void tegra_legacy_force_irq_clr(unsigned int irq); +int tegra_legacy_force_irq_status(unsigned int irq); +void tegra_legacy_select_fiq(unsigned int irq, bool fiq); +unsigned long tegra_legacy_vfiq(int nr); +unsigned long tegra_legacy_class(int nr); + +#endif diff --git a/arch/arm/mach-tegra/legacy_irq.c b/arch/arm/mach-tegra/legacy_irq.c new file mode 100644 index 000000000000..7cc8601c19ff --- /dev/null +++ b/arch/arm/mach-tegra/legacy_irq.c @@ -0,0 +1,114 @@ +/* + * arch/arm/mach-tegra/legacy_irq.c + * + * Copyright (C) 2010 Google, Inc. + * Author: Colin Cross + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that 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. + * + */ + +#include +#include +#include +#include + +#define ICTLR_CPU_IER 0x20 +#define ICTLR_CPU_IER_SET 0x24 +#define ICTLR_CPU_IER_CLR 0x28 +#define ICTLR_CPU_IEP_CLASS 0x2C +#define ICTLR_CPU_IEP_VFIQ 0x08 +#define ICTLR_CPU_IEP_FIR 0x14 +#define ICTLR_CPU_IEP_FIR_SET 0x18 +#define ICTLR_CPU_IEP_FIR_CLR 0x1c + +static void __iomem *ictlr_reg_base[] = { + IO_ADDRESS(TEGRA_PRIMARY_ICTLR_BASE), + IO_ADDRESS(TEGRA_SECONDARY_ICTLR_BASE), + IO_ADDRESS(TEGRA_TERTIARY_ICTLR_BASE), + IO_ADDRESS(TEGRA_QUATERNARY_ICTLR_BASE), +}; + +/* When going into deep sleep, the CPU is powered down, taking the GIC with it + In order to wake, the wake interrupts need to be enabled in the legacy + interrupt controller. */ +void tegra_legacy_unmask_irq(unsigned int irq) +{ + void __iomem *base; + pr_debug("%s: %d\n", __func__, irq); + + irq -= 32; + base = ictlr_reg_base[irq>>5]; + writel(1 << (irq & 31), base + ICTLR_CPU_IER_SET); +} + +void tegra_legacy_mask_irq(unsigned int irq) +{ + void __iomem *base; + pr_debug("%s: %d\n", __func__, irq); + + irq -= 32; + base = ictlr_reg_base[irq>>5]; + writel(1 << (irq & 31), base + ICTLR_CPU_IER_CLR); +} + +void tegra_legacy_force_irq_set(unsigned int irq) +{ + void __iomem *base; + pr_debug("%s: %d\n", __func__, irq); + + irq -= 32; + base = ictlr_reg_base[irq>>5]; + writel(1 << (irq & 31), base + ICTLR_CPU_IEP_FIR_SET); +} + +void tegra_legacy_force_irq_clr(unsigned int irq) +{ + void __iomem *base; + pr_debug("%s: %d\n", __func__, irq); + + irq -= 32; + base = ictlr_reg_base[irq>>5]; + writel(1 << (irq & 31), base + ICTLR_CPU_IEP_FIR_CLR); +} + +int tegra_legacy_force_irq_status(unsigned int irq) +{ + void __iomem *base; + pr_debug("%s: %d\n", __func__, irq); + + irq -= 32; + base = ictlr_reg_base[irq>>5]; + return !!(readl(base + ICTLR_CPU_IEP_FIR) & (1 << (irq & 31))); +} + +void tegra_legacy_select_fiq(unsigned int irq, bool fiq) +{ + void __iomem *base; + pr_debug("%s: %d\n", __func__, irq); + + irq -= 32; + base = ictlr_reg_base[irq>>5]; + writel(fiq << (irq & 31), base + ICTLR_CPU_IEP_CLASS); +} + +unsigned long tegra_legacy_vfiq(int nr) +{ + void __iomem *base; + base = ictlr_reg_base[nr]; + return readl(base + ICTLR_CPU_IEP_VFIQ); +} + +unsigned long tegra_legacy_class(int nr) +{ + void __iomem *base; + base = ictlr_reg_base[nr]; + return readl(base + ICTLR_CPU_IEP_CLASS); +} -- cgit v1.2.3 From 460907bc26f5565aced3cf35a10e3579c671ef50 Mon Sep 17 00:00:00 2001 From: Gary King Date: Mon, 5 Apr 2010 20:30:59 -0700 Subject: [ARM] tegra: add suspend and mirror irqs to legacy controller mirror IRQ enable and disable operations on the legacy PPI system interrupt controller, since the legacy controller is responsible for responding to wakeup interrupts when the CPU is in LP2 idle mode save the irq controller state on suspend and restore on resume Signed-off-by: Gary King --- arch/arm/mach-tegra/include/mach/irqs.h | 2 + arch/arm/mach-tegra/irq.c | 137 ++++++++++++++++++++++++++++++++ 2 files changed, 139 insertions(+) (limited to 'arch') diff --git a/arch/arm/mach-tegra/include/mach/irqs.h b/arch/arm/mach-tegra/include/mach/irqs.h index 20f640edaa0d..71bbf3422953 100644 --- a/arch/arm/mach-tegra/include/mach/irqs.h +++ b/arch/arm/mach-tegra/include/mach/irqs.h @@ -25,6 +25,7 @@ #define IRQ_LOCALTIMER 29 +#ifdef CONFIG_ARCH_TEGRA_2x_SOC /* Primary Interrupt Controller */ #define INT_PRI_BASE (INT_GIC_BASE + 32) #define INT_TMR1 (INT_PRI_BASE + 0) @@ -169,5 +170,6 @@ #define INT_GPIO_NR (28 * 8) #define NR_IRQS (INT_GPIO_BASE + INT_GPIO_NR) +#endif #endif diff --git a/arch/arm/mach-tegra/irq.c b/arch/arm/mach-tegra/irq.c index 1fdbe708d43d..50a8dfb9a0cf 100644 --- a/arch/arm/mach-tegra/irq.c +++ b/arch/arm/mach-tegra/irq.c @@ -4,6 +4,8 @@ * Author: * Colin Cross * + * Copyright (C) 2010, NVIDIA Corporation + * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. @@ -27,8 +29,143 @@ #include "board.h" +#define INT_SYS_NR (INT_GPIO_BASE - INT_PRI_BASE) +#define INT_SYS_SZ (INT_SEC_BASE - INT_PRI_BASE) +#define PPI_NR ((INT_SYS_NR+INT_SYS_SZ-1)/INT_SYS_SZ) + +#define APBDMA_IRQ_STA_CPU 0x14 +#define APBDMA_IRQ_MASK_SET 0x20 +#define APBDMA_IRQ_MASK_CLR 0x24 + +#define ICTLR_CPU_IER 0x20 +#define ICTLR_CPU_IER_SET 0x24 +#define ICTLR_CPU_IER_CLR 0x28 +#define ICTLR_CPU_IEP_CLASS 0x2c +#define ICTLR_COP_IER 0x30 +#define ICTLR_COP_IER_SET 0x34 +#define ICTLR_COP_IER_CLR 0x38 +#define ICTLR_COP_IEP_CLASS 0x3c + +static void (*gic_mask_irq)(unsigned int irq); +static void (*gic_unmask_irq)(unsigned int irq); + +#define irq_to_ictlr(irq) (((irq)-32) >> 5) +static void __iomem *tegra_ictlr_base = IO_ADDRESS(TEGRA_PRIMARY_ICTLR_BASE); +#define ictlr_to_virt(ictlr) (tegra_ictlr_base + (ictlr)*0x100) + +static void tegra_mask(unsigned int irq) +{ + void __iomem *addr = ictlr_to_virt(irq_to_ictlr(irq)); + gic_mask_irq(irq); + writel(1<<(irq&31), addr+ICTLR_CPU_IER_CLR); +} + +static void tegra_unmask(unsigned int irq) +{ + void __iomem *addr = ictlr_to_virt(irq_to_ictlr(irq)); + gic_unmask_irq(irq); + writel(1<<(irq&31), addr+ICTLR_CPU_IER_SET); +} + +#ifdef CONFIG_PM + +static int tegra_set_wake(unsigned int irq, unsigned int on) +{ + return 0; +} +#endif + +static struct irq_chip tegra_irq = { + .name = "PPI", + .mask = tegra_mask, + .unmask = tegra_unmask, +#ifdef CONFIG_PM + .set_wake = tegra_set_wake, +#endif +}; + void __init tegra_init_irq(void) { + struct irq_chip *gic; + unsigned int i; + + for (i = 0; i < PPI_NR; i++) { + writel(~0, ictlr_to_virt(i) + ICTLR_CPU_IER_CLR); + writel(0, ictlr_to_virt(i) + ICTLR_CPU_IEP_CLASS); + } + gic_dist_init(0, IO_ADDRESS(TEGRA_ARM_INT_DIST_BASE), 29); gic_cpu_init(0, IO_ADDRESS(TEGRA_ARM_PERIF_BASE + 0x100)); + + gic = get_irq_chip(29); + gic_unmask_irq = gic->unmask; + gic_mask_irq = gic->mask; + tegra_irq.ack = gic->ack; +#ifdef CONFIG_SMP + tegra_irq.set_affinity = gic->set_affinity; +#endif + + for (i = INT_PRI_BASE; i < INT_GPIO_BASE; i++) { + set_irq_chip(i, &tegra_irq); + set_irq_handler(i, handle_level_irq); + set_irq_flags(i, IRQF_VALID); + } +} + +#ifdef CONFIG_PM +static u32 cop_ier[PPI_NR]; +static u32 cpu_ier[PPI_NR]; +static u32 cpu_iep[PPI_NR]; + +void tegra_irq_suspend(void) +{ + unsigned long flags; + int i; + + for (i = INT_PRI_BASE; i < INT_GPIO_BASE; i++) { + struct irq_desc *desc = irq_to_desc(i); + if (!desc) + continue; + if (desc->status & IRQ_WAKEUP) { + pr_debug("irq %d is wakeup\n", i); + continue; + } + disable_irq(i); + } + + local_irq_save(flags); + for (i = 0; i < PPI_NR; i++) { + void __iomem *ictlr = ictlr_to_virt(i); + cpu_ier[i] = readl(ictlr + ICTLR_CPU_IER); + cpu_iep[i] = readl(ictlr + ICTLR_CPU_IEP_CLASS); + cop_ier[i] = readl(ictlr + ICTLR_COP_IER); + writel(~0, ictlr + ICTLR_COP_IER_CLR); + } + local_irq_restore(flags); +} + +void tegra_irq_resume(void) +{ + unsigned long flags; + int i; + + local_irq_save(flags); + for (i = 0; i < PPI_NR; i++) { + void __iomem *ictlr = ictlr_to_virt(i); + writel(cpu_iep[i], ictlr + ICTLR_CPU_IEP_CLASS); + writel(~0ul, ictlr + ICTLR_CPU_IER_CLR); + writel(cpu_ier[i], ictlr + ICTLR_CPU_IER_SET); + writel(0, ictlr + ICTLR_COP_IEP_CLASS); + writel(~0ul, ictlr + ICTLR_COP_IER_CLR); + writel(cop_ier[i], ictlr + ICTLR_COP_IER_SET); + } + local_irq_restore(flags); + + for (i = INT_PRI_BASE; i < INT_GPIO_BASE; i++) { + struct irq_desc *desc = irq_to_desc(i); + if (!desc || (desc->status & IRQ_WAKEUP)) + continue; + enable_irq(i); + } } +#endif -- cgit v1.2.3 From c5f04b8d10a73f2a016d6815d64dd5eebc734097 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Fri, 9 Jul 2010 15:13:52 -0700 Subject: [ARM] tegra: pinmux: add safe values, move tegra2, add suspend - the reset values for some pin groups in the tegra pin mux can result in functional errors due to conflicting with actively-configured pin groups muxing from the same controller. this change adds a known safe, non- conflicting mux for every pin group, which can be used on platforms where the pin group is not routed to any peripheral - also add each pin group's I/O voltage rail, to enable platform code to map from the pin groups used by each interface to the regulators used for dynamic voltage control - add routines to individually configure the tristate, pin mux and pull- ups for a pingroup_config array, so that it is possible to program individual values at run-time without modifying other values. this allows driver power-management code to reprogram individual interfaces into lower power states during idle / suspend, or to reprogram the pin mux to support multiple physical busses per internal controller (e.g., sharing a single I2C or SPI controller across multiple pin groups) - move chip-specific data like pingroups and drive-pingroups out of the common code and into chip-specific code - fix debug output for group with no pullups - add a TEGRA_MUX_SAFE function. Setting a pingroup to TEGRA_MUX_SAFE will automatically select a mux setting that is guaranteed not to conflict with any of the hardware blocks. Signed-off-by: Gary King --- arch/arm/mach-tegra/Makefile | 1 + arch/arm/mach-tegra/include/mach/pinmux-t2.h | 174 +++++++++++++ arch/arm/mach-tegra/include/mach/pinmux.h | 210 ++++------------ arch/arm/mach-tegra/pinmux-t2-tables.c | 260 ++++++++++++++++++++ arch/arm/mach-tegra/pinmux.c | 354 +++++++++------------------ 5 files changed, 603 insertions(+), 396 deletions(-) create mode 100644 arch/arm/mach-tegra/include/mach/pinmux-t2.h create mode 100644 arch/arm/mach-tegra/pinmux-t2-tables.c (limited to 'arch') diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile index 0a975b5fe493..87d065e65113 100644 --- a/arch/arm/mach-tegra/Makefile +++ b/arch/arm/mach-tegra/Makefile @@ -7,6 +7,7 @@ obj-y += gpio.o obj-y += pinmux.o obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += clock.o obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_clocks.o +obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += pinmux-t2-tables.o obj-$(CONFIG_SMP) += platsmp.o localtimer.o headsmp.o obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o diff --git a/arch/arm/mach-tegra/include/mach/pinmux-t2.h b/arch/arm/mach-tegra/include/mach/pinmux-t2.h new file mode 100644 index 000000000000..e5b9d740f973 --- /dev/null +++ b/arch/arm/mach-tegra/include/mach/pinmux-t2.h @@ -0,0 +1,174 @@ +/* + * linux/arch/arm/mach-tegra/include/mach/pinmux-t2.h + * + * Copyright (C) 2010 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that 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. + * + */ + +#ifndef __MACH_TEGRA_PINMUX_T2_H +#define __MACH_TEGRA_PINMUX_T2_H + +enum tegra_pingroup { + TEGRA_PINGROUP_ATA = 0, + TEGRA_PINGROUP_ATB, + TEGRA_PINGROUP_ATC, + TEGRA_PINGROUP_ATD, + TEGRA_PINGROUP_ATE, + TEGRA_PINGROUP_CDEV1, + TEGRA_PINGROUP_CDEV2, + TEGRA_PINGROUP_CRTP, + TEGRA_PINGROUP_CSUS, + TEGRA_PINGROUP_DAP1, + TEGRA_PINGROUP_DAP2, + TEGRA_PINGROUP_DAP3, + TEGRA_PINGROUP_DAP4, + TEGRA_PINGROUP_DDC, + TEGRA_PINGROUP_DTA, + TEGRA_PINGROUP_DTB, + TEGRA_PINGROUP_DTC, + TEGRA_PINGROUP_DTD, + TEGRA_PINGROUP_DTE, + TEGRA_PINGROUP_DTF, + TEGRA_PINGROUP_GMA, + TEGRA_PINGROUP_GMB, + TEGRA_PINGROUP_GMC, + TEGRA_PINGROUP_GMD, + TEGRA_PINGROUP_GME, + TEGRA_PINGROUP_GPU, + TEGRA_PINGROUP_GPU7, + TEGRA_PINGROUP_GPV, + TEGRA_PINGROUP_HDINT, + TEGRA_PINGROUP_I2CP, + TEGRA_PINGROUP_IRRX, + TEGRA_PINGROUP_IRTX, + TEGRA_PINGROUP_KBCA, + TEGRA_PINGROUP_KBCB, + TEGRA_PINGROUP_KBCC, + TEGRA_PINGROUP_KBCD, + TEGRA_PINGROUP_KBCE, + TEGRA_PINGROUP_KBCF, + TEGRA_PINGROUP_LCSN, + TEGRA_PINGROUP_LD0, + TEGRA_PINGROUP_LD1, + TEGRA_PINGROUP_LD10, + TEGRA_PINGROUP_LD11, + TEGRA_PINGROUP_LD12, + TEGRA_PINGROUP_LD13, + TEGRA_PINGROUP_LD14, + TEGRA_PINGROUP_LD15, + TEGRA_PINGROUP_LD16, + TEGRA_PINGROUP_LD17, + TEGRA_PINGROUP_LD2, + TEGRA_PINGROUP_LD3, + TEGRA_PINGROUP_LD4, + TEGRA_PINGROUP_LD5, + TEGRA_PINGROUP_LD6, + TEGRA_PINGROUP_LD7, + TEGRA_PINGROUP_LD8, + TEGRA_PINGROUP_LD9, + TEGRA_PINGROUP_LDC, + TEGRA_PINGROUP_LDI, + TEGRA_PINGROUP_LHP0, + TEGRA_PINGROUP_LHP1, + TEGRA_PINGROUP_LHP2, + TEGRA_PINGROUP_LHS, + TEGRA_PINGROUP_LM0, + TEGRA_PINGROUP_LM1, + TEGRA_PINGROUP_LPP, + TEGRA_PINGROUP_LPW0, + TEGRA_PINGROUP_LPW1, + TEGRA_PINGROUP_LPW2, + TEGRA_PINGROUP_LSC0, + TEGRA_PINGROUP_LSC1, + TEGRA_PINGROUP_LSCK, + TEGRA_PINGROUP_LSDA, + TEGRA_PINGROUP_LSDI, + TEGRA_PINGROUP_LSPI, + TEGRA_PINGROUP_LVP0, + TEGRA_PINGROUP_LVP1, + TEGRA_PINGROUP_LVS, + TEGRA_PINGROUP_OWC, + TEGRA_PINGROUP_PMC, + TEGRA_PINGROUP_PTA, + TEGRA_PINGROUP_RM, + TEGRA_PINGROUP_SDB, + TEGRA_PINGROUP_SDC, + TEGRA_PINGROUP_SDD, + TEGRA_PINGROUP_SDIO1, + TEGRA_PINGROUP_SLXA, + TEGRA_PINGROUP_SLXC, + TEGRA_PINGROUP_SLXD, + TEGRA_PINGROUP_SLXK, + TEGRA_PINGROUP_SPDI, + TEGRA_PINGROUP_SPDO, + TEGRA_PINGROUP_SPIA, + TEGRA_PINGROUP_SPIB, + TEGRA_PINGROUP_SPIC, + TEGRA_PINGROUP_SPID, + TEGRA_PINGROUP_SPIE, + TEGRA_PINGROUP_SPIF, + TEGRA_PINGROUP_SPIG, + TEGRA_PINGROUP_SPIH, + TEGRA_PINGROUP_UAA, + TEGRA_PINGROUP_UAB, + TEGRA_PINGROUP_UAC, + TEGRA_PINGROUP_UAD, + TEGRA_PINGROUP_UCA, + TEGRA_PINGROUP_UCB, + TEGRA_PINGROUP_UDA, + /* these pin groups only have pullup and pull down control */ + TEGRA_PINGROUP_CK32, + TEGRA_PINGROUP_DDRC, + TEGRA_PINGROUP_PMCA, + TEGRA_PINGROUP_PMCB, + TEGRA_PINGROUP_PMCC, + TEGRA_PINGROUP_PMCD, + TEGRA_PINGROUP_PMCE, + TEGRA_PINGROUP_XM2C, + TEGRA_PINGROUP_XM2D, + TEGRA_MAX_PINGROUP, +}; + +enum tegra_drive_pingroup { + TEGRA_DRIVE_PINGROUP_AO1 = 0, + TEGRA_DRIVE_PINGROUP_AO2, + TEGRA_DRIVE_PINGROUP_AT1, + TEGRA_DRIVE_PINGROUP_AT2, + TEGRA_DRIVE_PINGROUP_CDEV1, + TEGRA_DRIVE_PINGROUP_CDEV2, + TEGRA_DRIVE_PINGROUP_CSUS, + TEGRA_DRIVE_PINGROUP_DAP1, + TEGRA_DRIVE_PINGROUP_DAP2, + TEGRA_DRIVE_PINGROUP_DAP3, + TEGRA_DRIVE_PINGROUP_DAP4, + TEGRA_DRIVE_PINGROUP_DBG, + TEGRA_DRIVE_PINGROUP_LCD1, + TEGRA_DRIVE_PINGROUP_LCD2, + TEGRA_DRIVE_PINGROUP_SDMMC2, + TEGRA_DRIVE_PINGROUP_SDMMC3, + TEGRA_DRIVE_PINGROUP_SPI, + TEGRA_DRIVE_PINGROUP_UAA, + TEGRA_DRIVE_PINGROUP_UAB, + TEGRA_DRIVE_PINGROUP_UART2, + TEGRA_DRIVE_PINGROUP_UART3, + TEGRA_DRIVE_PINGROUP_VI1, + TEGRA_DRIVE_PINGROUP_VI2, + TEGRA_DRIVE_PINGROUP_XM2A, + TEGRA_DRIVE_PINGROUP_XM2C, + TEGRA_DRIVE_PINGROUP_XM2D, + TEGRA_DRIVE_PINGROUP_XM2CLK, + TEGRA_DRIVE_PINGROUP_MEMCOMP, + TEGRA_MAX_DRIVE_PINGROUP, +}; + +#endif + diff --git a/arch/arm/mach-tegra/include/mach/pinmux.h b/arch/arm/mach-tegra/include/mach/pinmux.h index 41c8ce5b7c27..defd8775defa 100644 --- a/arch/arm/mach-tegra/include/mach/pinmux.h +++ b/arch/arm/mach-tegra/include/mach/pinmux.h @@ -17,126 +17,11 @@ #ifndef __MACH_TEGRA_PINMUX_H #define __MACH_TEGRA_PINMUX_H -enum tegra_pingroup { - TEGRA_PINGROUP_ATA = 0, - TEGRA_PINGROUP_ATB, - TEGRA_PINGROUP_ATC, - TEGRA_PINGROUP_ATD, - TEGRA_PINGROUP_ATE, - TEGRA_PINGROUP_CDEV1, - TEGRA_PINGROUP_CDEV2, - TEGRA_PINGROUP_CRTP, - TEGRA_PINGROUP_CSUS, - TEGRA_PINGROUP_DAP1, - TEGRA_PINGROUP_DAP2, - TEGRA_PINGROUP_DAP3, - TEGRA_PINGROUP_DAP4, - TEGRA_PINGROUP_DDC, - TEGRA_PINGROUP_DTA, - TEGRA_PINGROUP_DTB, - TEGRA_PINGROUP_DTC, - TEGRA_PINGROUP_DTD, - TEGRA_PINGROUP_DTE, - TEGRA_PINGROUP_DTF, - TEGRA_PINGROUP_GMA, - TEGRA_PINGROUP_GMB, - TEGRA_PINGROUP_GMC, - TEGRA_PINGROUP_GMD, - TEGRA_PINGROUP_GME, - TEGRA_PINGROUP_GPU, - TEGRA_PINGROUP_GPU7, - TEGRA_PINGROUP_GPV, - TEGRA_PINGROUP_HDINT, - TEGRA_PINGROUP_I2CP, - TEGRA_PINGROUP_IRRX, - TEGRA_PINGROUP_IRTX, - TEGRA_PINGROUP_KBCA, - TEGRA_PINGROUP_KBCB, - TEGRA_PINGROUP_KBCC, - TEGRA_PINGROUP_KBCD, - TEGRA_PINGROUP_KBCE, - TEGRA_PINGROUP_KBCF, - TEGRA_PINGROUP_LCSN, - TEGRA_PINGROUP_LD0, - TEGRA_PINGROUP_LD1, - TEGRA_PINGROUP_LD10, - TEGRA_PINGROUP_LD11, - TEGRA_PINGROUP_LD12, - TEGRA_PINGROUP_LD13, - TEGRA_PINGROUP_LD14, - TEGRA_PINGROUP_LD15, - TEGRA_PINGROUP_LD16, - TEGRA_PINGROUP_LD17, - TEGRA_PINGROUP_LD2, - TEGRA_PINGROUP_LD3, - TEGRA_PINGROUP_LD4, - TEGRA_PINGROUP_LD5, - TEGRA_PINGROUP_LD6, - TEGRA_PINGROUP_LD7, - TEGRA_PINGROUP_LD8, - TEGRA_PINGROUP_LD9, - TEGRA_PINGROUP_LDC, - TEGRA_PINGROUP_LDI, - TEGRA_PINGROUP_LHP0, - TEGRA_PINGROUP_LHP1, - TEGRA_PINGROUP_LHP2, - TEGRA_PINGROUP_LHS, - TEGRA_PINGROUP_LM0, - TEGRA_PINGROUP_LM1, - TEGRA_PINGROUP_LPP, - TEGRA_PINGROUP_LPW0, - TEGRA_PINGROUP_LPW1, - TEGRA_PINGROUP_LPW2, - TEGRA_PINGROUP_LSC0, - TEGRA_PINGROUP_LSC1, - TEGRA_PINGROUP_LSCK, - TEGRA_PINGROUP_LSDA, - TEGRA_PINGROUP_LSDI, - TEGRA_PINGROUP_LSPI, - TEGRA_PINGROUP_LVP0, - TEGRA_PINGROUP_LVP1, - TEGRA_PINGROUP_LVS, - TEGRA_PINGROUP_OWC, - TEGRA_PINGROUP_PMC, - TEGRA_PINGROUP_PTA, - TEGRA_PINGROUP_RM, - TEGRA_PINGROUP_SDB, - TEGRA_PINGROUP_SDC, - TEGRA_PINGROUP_SDD, - TEGRA_PINGROUP_SDIO1, - TEGRA_PINGROUP_SLXA, - TEGRA_PINGROUP_SLXC, - TEGRA_PINGROUP_SLXD, - TEGRA_PINGROUP_SLXK, - TEGRA_PINGROUP_SPDI, - TEGRA_PINGROUP_SPDO, - TEGRA_PINGROUP_SPIA, - TEGRA_PINGROUP_SPIB, - TEGRA_PINGROUP_SPIC, - TEGRA_PINGROUP_SPID, - TEGRA_PINGROUP_SPIE, - TEGRA_PINGROUP_SPIF, - TEGRA_PINGROUP_SPIG, - TEGRA_PINGROUP_SPIH, - TEGRA_PINGROUP_UAA, - TEGRA_PINGROUP_UAB, - TEGRA_PINGROUP_UAC, - TEGRA_PINGROUP_UAD, - TEGRA_PINGROUP_UCA, - TEGRA_PINGROUP_UCB, - TEGRA_PINGROUP_UDA, - /* these pin groups only have pullup and pull down control */ - TEGRA_PINGROUP_CK32, - TEGRA_PINGROUP_DDRC, - TEGRA_PINGROUP_PMCA, - TEGRA_PINGROUP_PMCB, - TEGRA_PINGROUP_PMCC, - TEGRA_PINGROUP_PMCD, - TEGRA_PINGROUP_PMCE, - TEGRA_PINGROUP_XM2C, - TEGRA_PINGROUP_XM2D, - TEGRA_MAX_PINGROUP, -}; +#if defined(CONFIG_ARCH_TEGRA_2x_SOC) +#include "pinmux-t2.h" +#else +#error "Undefined Tegra architecture" +#endif enum tegra_mux_func { TEGRA_MUX_RSVD = 0x8000, @@ -205,6 +90,7 @@ enum tegra_mux_func { TEGRA_MUX_VI, TEGRA_MUX_VI_SENSOR_CLK, TEGRA_MUX_XIO, + TEGRA_MUX_SAFE, TEGRA_MAX_MUX, }; @@ -219,6 +105,18 @@ enum tegra_tristate { TEGRA_TRI_TRISTATE = 1, }; +enum tegra_vddio { + TEGRA_VDDIO_BB = 0, + TEGRA_VDDIO_LCD, + TEGRA_VDDIO_VI, + TEGRA_VDDIO_UART, + TEGRA_VDDIO_DDR, + TEGRA_VDDIO_NAND, + TEGRA_VDDIO_SYS, + TEGRA_VDDIO_AUDIO, + TEGRA_VDDIO_SD, +}; + struct tegra_pingroup_config { enum tegra_pingroup pingroup; enum tegra_mux_func func; @@ -270,38 +168,6 @@ enum tegra_pull_strength { TEGRA_MAX_PULL, }; -enum tegra_drive_pingroup { - TEGRA_DRIVE_PINGROUP_AO1 = 0, - TEGRA_DRIVE_PINGROUP_AO2, - TEGRA_DRIVE_PINGROUP_AT1, - TEGRA_DRIVE_PINGROUP_AT2, - TEGRA_DRIVE_PINGROUP_CDEV1, - TEGRA_DRIVE_PINGROUP_CDEV2, - TEGRA_DRIVE_PINGROUP_CSUS, - TEGRA_DRIVE_PINGROUP_DAP1, - TEGRA_DRIVE_PINGROUP_DAP2, - TEGRA_DRIVE_PINGROUP_DAP3, - TEGRA_DRIVE_PINGROUP_DAP4, - TEGRA_DRIVE_PINGROUP_DBG, - TEGRA_DRIVE_PINGROUP_LCD1, - TEGRA_DRIVE_PINGROUP_LCD2, - TEGRA_DRIVE_PINGROUP_SDMMC2, - TEGRA_DRIVE_PINGROUP_SDMMC3, - TEGRA_DRIVE_PINGROUP_SPI, - TEGRA_DRIVE_PINGROUP_UAA, - TEGRA_DRIVE_PINGROUP_UAB, - TEGRA_DRIVE_PINGROUP_UART2, - TEGRA_DRIVE_PINGROUP_UART3, - TEGRA_DRIVE_PINGROUP_VI1, - TEGRA_DRIVE_PINGROUP_VI2, - TEGRA_DRIVE_PINGROUP_XM2A, - TEGRA_DRIVE_PINGROUP_XM2C, - TEGRA_DRIVE_PINGROUP_XM2D, - TEGRA_DRIVE_PINGROUP_XM2CLK, - TEGRA_DRIVE_PINGROUP_MEMCOMP, - TEGRA_MAX_DRIVE_PINGROUP, -}; - enum tegra_drive { TEGRA_DRIVE_DIV_8 = 0, TEGRA_DRIVE_DIV_4, @@ -331,18 +197,44 @@ struct tegra_drive_pingroup_config { enum tegra_slew slew_falling; }; -int tegra_pinmux_set_func(enum tegra_pingroup pg, enum tegra_mux_func func); -int tegra_pinmux_set_tristate(enum tegra_pingroup pg, enum tegra_tristate tristate); -int tegra_pinmux_set_pullupdown(enum tegra_pingroup pg, enum tegra_pullupdown pupd); +struct tegra_drive_pingroup_desc { + const char *name; + s16 reg; +}; + +struct tegra_pingroup_desc { + const char *name; + int funcs[4]; + int func_safe; + int vddio; + s16 tri_reg; /* offset into the TRISTATE_REG_* register bank */ + s16 mux_reg; /* offset into the PIN_MUX_CTL_* register bank */ + s16 pupd_reg; /* offset into the PULL_UPDOWN_REG_* register bank */ + s8 tri_bit; /* offset into the TRISTATE_REG_* register bit */ + s8 mux_bit; /* offset into the PIN_MUX_CTL_* register bit */ + s8 pupd_bit; /* offset into the PULL_UPDOWN_REG_* register bit */ +}; + +extern const struct tegra_pingroup_desc tegra_soc_pingroups[]; +extern const struct tegra_drive_pingroup_desc tegra_soc_drive_pingroups[]; -void tegra_pinmux_config_pingroup(enum tegra_pingroup pingroup, - enum tegra_mux_func func, enum tegra_pullupdown pupd, +int tegra_pinmux_set_tristate(enum tegra_pingroup pg, enum tegra_tristate tristate); +int tegra_pinmux_set_pullupdown(enum tegra_pingroup pg, + enum tegra_pullupdown pupd); -void tegra_pinmux_config_table(struct tegra_pingroup_config *config, int len); +void tegra_pinmux_config_table(const struct tegra_pingroup_config *config, + int len); void tegra_drive_pinmux_config_table(struct tegra_drive_pingroup_config *config, int len); - +void tegra_pinmux_set_safe_pinmux_table(const struct tegra_pingroup_config *config, + int len); +void tegra_pinmux_config_pinmux_table(const struct tegra_pingroup_config *config, + int len); +void tegra_pinmux_config_tristate_table(const struct tegra_pingroup_config *config, + int len, enum tegra_tristate tristate); +void tegra_pinmux_config_pullupdown_table(const struct tegra_pingroup_config *config, + int len, enum tegra_pullupdown pupd); #endif diff --git a/arch/arm/mach-tegra/pinmux-t2-tables.c b/arch/arm/mach-tegra/pinmux-t2-tables.c new file mode 100644 index 000000000000..a6ea34e782dc --- /dev/null +++ b/arch/arm/mach-tegra/pinmux-t2-tables.c @@ -0,0 +1,260 @@ +/* + * linux/arch/arm/mach-tegra/pinmux-t2-tables.c + * + * Common pinmux configurations for Tegra 2 SoCs + * + * Copyright (C) 2010 NVIDIA Corporation + * + * 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. + * + * This program is distributed in the hope that 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#define DRIVE_PINGROUP(pg_name, r) \ + [TEGRA_DRIVE_PINGROUP_ ## pg_name] = { \ + .name = #pg_name, \ + .reg = r \ + } + +const struct tegra_drive_pingroup_desc tegra_soc_drive_pingroups[TEGRA_MAX_DRIVE_PINGROUP] = { + DRIVE_PINGROUP(AO1, 0x868), + DRIVE_PINGROUP(AO2, 0x86c), + DRIVE_PINGROUP(AT1, 0x870), + DRIVE_PINGROUP(AT2, 0x874), + DRIVE_PINGROUP(CDEV1, 0x878), + DRIVE_PINGROUP(CDEV2, 0x87c), + DRIVE_PINGROUP(CSUS, 0x880), + DRIVE_PINGROUP(DAP1, 0x884), + DRIVE_PINGROUP(DAP2, 0x888), + DRIVE_PINGROUP(DAP3, 0x88c), + DRIVE_PINGROUP(DAP4, 0x890), + DRIVE_PINGROUP(DBG, 0x894), + DRIVE_PINGROUP(LCD1, 0x898), + DRIVE_PINGROUP(LCD2, 0x89c), + DRIVE_PINGROUP(SDMMC2, 0x8a0), + DRIVE_PINGROUP(SDMMC3, 0x8a4), + DRIVE_PINGROUP(SPI, 0x8a8), + DRIVE_PINGROUP(UAA, 0x8ac), + DRIVE_PINGROUP(UAB, 0x8b0), + DRIVE_PINGROUP(UART2, 0x8b4), + DRIVE_PINGROUP(UART3, 0x8b8), + DRIVE_PINGROUP(VI1, 0x8bc), + DRIVE_PINGROUP(VI2, 0x8c0), + DRIVE_PINGROUP(XM2A, 0x8c4), + DRIVE_PINGROUP(XM2C, 0x8c8), + DRIVE_PINGROUP(XM2D, 0x8cc), + DRIVE_PINGROUP(XM2CLK, 0x8d0), + DRIVE_PINGROUP(MEMCOMP, 0x8d4), +}; + +#define PINGROUP(pg_name, vdd, f0, f1, f2, f3, f_safe, \ + tri_r, tri_b, mux_r, mux_b, pupd_r, pupd_b) \ + [TEGRA_PINGROUP_ ## pg_name] = { \ + .name = #pg_name, \ + .vddio = TEGRA_VDDIO_ ## vdd, \ + .funcs = { \ + TEGRA_MUX_ ## f0, \ + TEGRA_MUX_ ## f1, \ + TEGRA_MUX_ ## f2, \ + TEGRA_MUX_ ## f3, \ + }, \ + .func_safe = TEGRA_MUX_ ## f_safe, \ + .tri_reg = tri_r, \ + .tri_bit = tri_b, \ + .mux_reg = mux_r, \ + .mux_bit = mux_b, \ + .pupd_reg = pupd_r, \ + .pupd_bit = pupd_b, \ + } + +const struct tegra_pingroup_desc tegra_soc_pingroups[TEGRA_MAX_PINGROUP] = { + PINGROUP(ATA, NAND, IDE, NAND, GMI, RSVD, IDE, 0x14, 0, 0x80, 24, 0xA0, 0), + PINGROUP(ATB, NAND, IDE, NAND, GMI, SDIO4, IDE, 0x14, 1, 0x80, 16, 0xA0, 2), + PINGROUP(ATC, NAND, IDE, NAND, GMI, SDIO4, IDE, 0x14, 2, 0x80, 22, 0xA0, 4), + PINGROUP(ATD, NAND, IDE, NAND, GMI, SDIO4, IDE, 0x14, 3, 0x80, 20, 0xA0, 6), + PINGROUP(ATE, NAND, IDE, NAND, GMI, RSVD, IDE, 0x18, 25, 0x80, 12, 0xA0, 8), + PINGROUP(CDEV1, AUDIO, OSC, PLLA_OUT, PLLM_OUT1, AUDIO_SYNC, OSC, 0x14, 4, 0x88, 2, 0xA8, 0), + PINGROUP(CDEV2, AUDIO, OSC, AHB_CLK, APB_CLK, PLLP_OUT4, OSC, 0x14, 5, 0x88, 4, 0xA8, 2), + PINGROUP(CRTP, LCD, CRT, RSVD, RSVD, RSVD, RSVD, 0x20, 14, 0x98, 20, 0xA4, 24), + PINGROUP(CSUS, VI, PLLC_OUT1, PLLP_OUT2, PLLP_OUT3, VI_SENSOR_CLK, PLLC_OUT1, 0x14, 6, 0x88, 6, 0xAC, 24), + PINGROUP(DAP1, AUDIO, DAP1, RSVD, GMI, SDIO2, DAP1, 0x14, 7, 0x88, 20, 0xA0, 10), + PINGROUP(DAP2, AUDIO, DAP2, TWC, RSVD, GMI, DAP2, 0x14, 8, 0x88, 22, 0xA0, 12), + PINGROUP(DAP3, BB, DAP3, RSVD, RSVD, RSVD, DAP3, 0x14, 9, 0x88, 24, 0xA0, 14), + PINGROUP(DAP4, UART, DAP4, RSVD, GMI, RSVD, DAP4, 0x14, 10, 0x88, 26, 0xA0, 16), + PINGROUP(DDC, LCD, I2C2, RSVD, RSVD, RSVD, RSVD4, 0x18, 31, 0x88, 0, 0xB0, 28), + PINGROUP(DTA, VI, RSVD, SDIO2, VI, RSVD, RSVD4, 0x14, 11, 0x84, 20, 0xA0, 18), + PINGROUP(DTB, VI, RSVD, RSVD, VI, SPI1, RSVD1, 0x14, 12, 0x84, 22, 0xA0, 20), + PINGROUP(DTC, VI, RSVD, RSVD, VI, RSVD, RSVD1, 0x14, 13, 0x84, 26, 0xA0, 22), + PINGROUP(DTD, VI, RSVD, SDIO2, VI, RSVD, RSVD1, 0x14, 14, 0x84, 28, 0xA0, 24), + PINGROUP(DTE, VI, RSVD, RSVD, VI, SPI1, RSVD1, 0x14, 15, 0x84, 30, 0xA0, 26), + PINGROUP(DTF, VI, I2C3, RSVD, VI, RSVD, RSVD4, 0x20, 12, 0x98, 30, 0xA0, 28), + PINGROUP(GMA, NAND, UARTE, SPI3, GMI, SDIO4, SPI3, 0x14, 28, 0x84, 0, 0xB0, 20), + PINGROUP(GMB, NAND, IDE, NAND, GMI, GMI_INT, GMI, 0x18, 29, 0x88, 28, 0xB0, 22), + PINGROUP(GMC, NAND, UARTD, SPI4, GMI, SFLASH, SPI4, 0x14, 29, 0x84, 2, 0xB0, 24), + PINGROUP(GMD, NAND, RSVD, NAND, GMI, SFLASH, GMI, 0x18, 30, 0x88, 30, 0xB0, 26), + PINGROUP(GME, NAND, RSVD, DAP5, GMI, SDIO4, GMI, 0x18, 0, 0x8C, 0, 0xA8, 24), + PINGROUP(GPU, UART, PWM, UARTA, GMI, RSVD, RSVD4, 0x14, 16, 0x8C, 4, 0xA4, 20), + PINGROUP(GPU7, SYS, RTCK, RSVD, RSVD, RSVD, RTCK, 0x20, 11, 0x98, 28, 0xA4, 6), + PINGROUP(GPV, SD, PCIE, RSVD, RSVD, RSVD, PCIE, 0x14, 17, 0x8C, 2, 0xA0, 30), + PINGROUP(HDINT, LCD, HDMI, RSVD, RSVD, RSVD, HDMI, 0x1C, 23, 0x84, 4, 0xAC, 22), + PINGROUP(I2CP, SYS, I2C, RSVD, RSVD, RSVD, RSVD4, 0x14, 18, 0x88, 8, 0xA4, 2), + PINGROUP(IRRX, UART, UARTA, UARTB, GMI, SPI4, UARTB, 0x14, 20, 0x88, 18, 0xA8, 22), + PINGROUP(IRTX, UART, UARTA, UARTB, GMI, SPI4, UARTB, 0x14, 19, 0x88, 16, 0xA8, 20), + PINGROUP(KBCA, SYS, KBC, NAND, SDIO2, EMC_TEST0_DLL, KBC, 0x14, 22, 0x88, 10, 0xA4, 8), + PINGROUP(KBCB, SYS, KBC, NAND, SDIO2, MIO, KBC, 0x14, 21, 0x88, 12, 0xA4, 10), + PINGROUP(KBCC, SYS, KBC, NAND, TRACE, EMC_TEST1_DLL, KBC, 0x18, 26, 0x88, 14, 0xA4, 12), + PINGROUP(KBCD, SYS, KBC, NAND, SDIO2, MIO, KBC, 0x20, 10, 0x98, 26, 0xA4, 14), + PINGROUP(KBCE, SYS, KBC, NAND, OWR, RSVD, KBC, 0x14, 26, 0x80, 28, 0xB0, 2), + PINGROUP(KBCF, SYS, KBC, NAND, TRACE, MIO, KBC, 0x14, 27, 0x80, 26, 0xB0, 0), + PINGROUP(LCSN, LCD, DISPLAYA, DISPLAYB, SPI3, RSVD, RSVD4, 0x1C, 31, 0x90, 12, 0xAC, 20), + PINGROUP(LD0, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 0, 0x94, 0, 0xAC, 12), + PINGROUP(LD1, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 1, 0x94, 2, 0xAC, 12), + PINGROUP(LD10, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 10, 0x94, 20, 0xAC, 12), + PINGROUP(LD11, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 11, 0x94, 22, 0xAC, 12), + PINGROUP(LD12, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 12, 0x94, 24, 0xAC, 12), + PINGROUP(LD13, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 13, 0x94, 26, 0xAC, 12), + PINGROUP(LD14, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 14, 0x94, 28, 0xAC, 12), + PINGROUP(LD15, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 15, 0x94, 30, 0xAC, 12), + PINGROUP(LD16, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 16, 0x98, 0, 0xAC, 12), + PINGROUP(LD17, LCD, DISPLAYA, DISPLAYB, RSVD, RSVD, RSVD4, 0x1C, 17, 0x98, 2, 0xAC, 12), + PINGROUP(LD2, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 2, 0x94, 4, 0xAC, 12), + PINGROUP(LD3, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 3, 0x94, 6, 0xAC, 12), + PINGROUP(LD4, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 4, 0x94, 8, 0xAC, 12), + PINGROUP(LD5, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 5, 0x94, 10, 0xAC, 12), + PINGROUP(LD6, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 6, 0x94, 12, 0xAC, 12), + PINGROUP(LD7, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 7, 0x94, 14, 0xAC, 12), + PINGROUP(LD8, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 8, 0x94, 16, 0xAC, 12), + PINGROUP(LD9, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 9, 0x94, 18, 0xAC, 12), + PINGROUP(LDC, LCD, DISPLAYA, DISPLAYB, RSVD, RSVD, RSVD4, 0x1C, 30, 0x90, 14, 0xAC, 20), + PINGROUP(LDI, LCD, DISPLAYA, DISPLAYB, RSVD, RSVD, RSVD4, 0x20, 6, 0x98, 16, 0xAC, 18), + PINGROUP(LHP0, LCD, DISPLAYA, DISPLAYB, RSVD, RSVD, RSVD4, 0x1C, 18, 0x98, 10, 0xAC, 16), + PINGROUP(LHP1, LCD, DISPLAYA, DISPLAYB, RSVD, RSVD, RSVD4, 0x1C, 19, 0x98, 4, 0xAC, 14), + PINGROUP(LHP2, LCD, DISPLAYA, DISPLAYB, RSVD, RSVD, RSVD4, 0x1C, 20, 0x98, 6, 0xAC, 14), + PINGROUP(LHS, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x20, 7, 0x90, 22, 0xAC, 22), + PINGROUP(LM0, LCD, DISPLAYA, DISPLAYB, SPI3, RSVD, RSVD4, 0x1C, 24, 0x90, 26, 0xAC, 22), + PINGROUP(LM1, LCD, DISPLAYA, DISPLAYB, RSVD, CRT, RSVD3, 0x1C, 25, 0x90, 28, 0xAC, 22), + PINGROUP(LPP, LCD, DISPLAYA, DISPLAYB, RSVD, RSVD, RSVD4, 0x20, 8, 0x98, 14, 0xAC, 18), + PINGROUP(LPW0, LCD, DISPLAYA, DISPLAYB, SPI3, HDMI, DISPLAYA, 0x20, 3, 0x90, 0, 0xAC, 20), + PINGROUP(LPW1, LCD, DISPLAYA, DISPLAYB, RSVD, RSVD, RSVD4, 0x20, 4, 0x90, 2, 0xAC, 20), + PINGROUP(LPW2, LCD, DISPLAYA, DISPLAYB, SPI3, HDMI, DISPLAYA, 0x20, 5, 0x90, 4, 0xAC, 20), + PINGROUP(LSC0, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 27, 0x90, 18, 0xAC, 22), + PINGROUP(LSC1, LCD, DISPLAYA, DISPLAYB, SPI3, HDMI, DISPLAYA, 0x1C, 28, 0x90, 20, 0xAC, 20), + PINGROUP(LSCK, LCD, DISPLAYA, DISPLAYB, SPI3, HDMI, DISPLAYA, 0x1C, 29, 0x90, 16, 0xAC, 20), + PINGROUP(LSDA, LCD, DISPLAYA, DISPLAYB, SPI3, HDMI, DISPLAYA, 0x20, 1, 0x90, 8, 0xAC, 20), + PINGROUP(LSDI, LCD, DISPLAYA, DISPLAYB, SPI3, RSVD, DISPLAYA, 0x20, 2, 0x90, 6, 0xAC, 20), + PINGROUP(LSPI, LCD, DISPLAYA, DISPLAYB, XIO, HDMI, DISPLAYA, 0x20, 0, 0x90, 10, 0xAC, 22), + PINGROUP(LVP0, LCD, DISPLAYA, DISPLAYB, RSVD, RSVD, RSVD4, 0x1C, 21, 0x90, 30, 0xAC, 22), + PINGROUP(LVP1, LCD, DISPLAYA, DISPLAYB, RSVD, RSVD, RSVD4, 0x1C, 22, 0x98, 8, 0xAC, 16), + PINGROUP(LVS, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 26, 0x90, 24, 0xAC, 22), + PINGROUP(OWC, SYS, OWR, RSVD, RSVD, RSVD, OWR, 0x14, 31, 0x84, 8, 0xB0, 30), + PINGROUP(PMC, SYS, PWR_ON, PWR_INTR, RSVD, RSVD, PWR_ON, 0x14, 23, 0x98, 18, -1, -1), + PINGROUP(PTA, NAND, I2C2, HDMI, GMI, RSVD, RSVD4, 0x14, 24, 0x98, 22, 0xA4, 4), + PINGROUP(RM, UART, I2C, RSVD, RSVD, RSVD, RSVD4, 0x14, 25, 0x80, 14, 0xA4, 0), + PINGROUP(SDB, SD, UARTA, PWM, SDIO3, SPI2, PWM, 0x20, 15, 0x8C, 10, -1, -1), + PINGROUP(SDC, SD, PWM, TWC, SDIO3, SPI3, TWC, 0x18, 1, 0x8C, 12, 0xAC, 28), + PINGROUP(SDD, SD, UARTA, PWM, SDIO3, SPI3, PWM, 0x18, 2, 0x8C, 14, 0xAC, 30), + PINGROUP(SDIO1, BB, SDIO1, RSVD, UARTE, UARTA, RSVD2, 0x14, 30, 0x80, 30, 0xB0, 18), + PINGROUP(SLXA, SD, PCIE, SPI4, SDIO3, SPI2, PCIE, 0x18, 3, 0x84, 6, 0xA4, 22), + PINGROUP(SLXC, SD, SPDIF, SPI4, SDIO3, SPI2, SPI4, 0x18, 5, 0x84, 10, 0xA4, 26), + PINGROUP(SLXD, SD, SPDIF, SPI4, SDIO3, SPI2, SPI4, 0x18, 6, 0x84, 12, 0xA4, 28), + PINGROUP(SLXK, SD, PCIE, SPI4, SDIO3, SPI2, PCIE, 0x18, 7, 0x84, 14, 0xA4, 30), + PINGROUP(SPDI, AUDIO, SPDIF, RSVD, I2C, SDIO2, RSVD2, 0x18, 8, 0x8C, 8, 0xA4, 16), + PINGROUP(SPDO, AUDIO, SPDIF, RSVD, I2C, SDIO2, RSVD2, 0x18, 9, 0x8C, 6, 0xA4, 18), + PINGROUP(SPIA, AUDIO, SPI1, SPI2, SPI3, GMI, GMI, 0x18, 10, 0x8C, 30, 0xA8, 4), + PINGROUP(SPIB, AUDIO, SPI1, SPI2, SPI3, GMI, GMI, 0x18, 11, 0x8C, 28, 0xA8, 6), + PINGROUP(SPIC, AUDIO, SPI1, SPI2, SPI3, GMI, GMI, 0x18, 12, 0x8C, 26, 0xA8, 8), + PINGROUP(SPID, AUDIO, SPI2, SPI1, SPI2_ALT, GMI, GMI, 0x18, 13, 0x8C, 24, 0xA8, 10), + PINGROUP(SPIE, AUDIO, SPI2, SPI1, SPI2_ALT, GMI, GMI, 0x18, 14, 0x8C, 22, 0xA8, 12), + PINGROUP(SPIF, AUDIO, SPI3, SPI1, SPI2, RSVD, RSVD4, 0x18, 15, 0x8C, 20, 0xA8, 14), + PINGROUP(SPIG, AUDIO, SPI3, SPI2, SPI2_ALT, I2C, SPI2_ALT, 0x18, 16, 0x8C, 18, 0xA8, 16), + PINGROUP(SPIH, AUDIO, SPI3, SPI2, SPI2_ALT, I2C, SPI2_ALT, 0x18, 17, 0x8C, 16, 0xA8, 18), + PINGROUP(UAA, BB, SPI3, MIPI_HS, UARTA, ULPI, MIPI_HS, 0x18, 18, 0x80, 0, 0xAC, 0), + PINGROUP(UAB, BB, SPI2, MIPI_HS, UARTA, ULPI, MIPI_HS, 0x18, 19, 0x80, 2, 0xAC, 2), + PINGROUP(UAC, BB, OWR, RSVD, RSVD, RSVD, RSVD4, 0x18, 20, 0x80, 4, 0xAC, 4), + PINGROUP(UAD, UART, IRDA, SPDIF, UARTA, SPI4, SPDIF, 0x18, 21, 0x80, 6, 0xAC, 6), + PINGROUP(UCA, UART, UARTC, RSVD, GMI, RSVD, RSVD4, 0x18, 22, 0x84, 16, 0xAC, 8), + PINGROUP(UCB, UART, UARTC, PWM, GMI, RSVD, RSVD4, 0x18, 23, 0x84, 18, 0xAC, 10), + PINGROUP(UDA, BB, SPI1, RSVD, UARTD, ULPI, RSVD2, 0x20, 13, 0x80, 8, 0xB0, 16), + /* these pin groups only have pullup and pull down control */ + PINGROUP(CK32, SYS, RSVD, RSVD, RSVD, RSVD, RSVD, -1, -1, -1, -1, 0xB0, 14), + PINGROUP(DDRC, DDR, RSVD, RSVD, RSVD, RSVD, RSVD, -1, -1, -1, -1, 0xAC, 26), + PINGROUP(PMCA, SYS, RSVD, RSVD, RSVD, RSVD, RSVD, -1, -1, -1, -1, 0xB0, 4), + PINGROUP(PMCB, SYS, RSVD, RSVD, RSVD, RSVD, RSVD, -1, -1, -1, -1, 0xB0, 6), + PINGROUP(PMCC, SYS, RSVD, RSVD, RSVD, RSVD, RSVD, -1, -1, -1, -1, 0xB0, 8), + PINGROUP(PMCD, SYS, RSVD, RSVD, RSVD, RSVD, RSVD, -1, -1, -1, -1, 0xB0, 10), + PINGROUP(PMCE, SYS, RSVD, RSVD, RSVD, RSVD, RSVD, -1, -1, -1, -1, 0xB0, 12), + PINGROUP(XM2C, DDR, RSVD, RSVD, RSVD, RSVD, RSVD, -1, -1, -1, -1, 0xA8, 30), + PINGROUP(XM2D, DDR, RSVD, RSVD, RSVD, RSVD, RSVD, -1, -1, -1, -1, 0xA8, 28), +}; + +#ifdef CONFIG_PM +#define TRISTATE_REG_A 0x14 +#define TRISTATE_REG_NUM 4 +#define PIN_MUX_CTL_REG_A 0x80 +#define PIN_MUX_CTL_REG_NUM 8 +#define PULLUPDOWN_REG_A 0xa0 +#define PULLUPDOWN_REG_NUM 5 + +static u32 pinmux_reg[TRISTATE_REG_NUM + PIN_MUX_CTL_REG_NUM + + PULLUPDOWN_REG_NUM]; + +static inline unsigned long pg_readl(unsigned long offset) +{ + return readl(IO_TO_VIRT(TEGRA_APB_MISC_BASE + offset)); +} + +static inline void pg_writel(unsigned long value, unsigned long offset) +{ + writel(value, IO_TO_VIRT(TEGRA_APB_MISC_BASE + offset)); +} + +void tegra_pinmux_suspend(void) +{ + unsigned int i; + u32 *ctx = pinmux_reg; + + for (i = 0; i < TRISTATE_REG_NUM; i++) + *ctx++ = pg_readl(TRISTATE_REG_A + i*4); + + for (i = 0; i < PIN_MUX_CTL_REG_NUM; i++) + *ctx++ = pg_readl(PIN_MUX_CTL_REG_A + i*4); + + for (i = 0; i < PULLUPDOWN_REG_NUM; i++) + *ctx++ = pg_readl(PULLUPDOWN_REG_A + i*4); +} + +void tegra_pinmux_resume(void) +{ + unsigned int i; + u32 *ctx = pinmux_reg; + + for (i = 0; i < PIN_MUX_CTL_REG_NUM; i++) + pg_writel(*ctx++, PIN_MUX_CTL_REG_A + i*4); + + for (i = 0; i < PULLUPDOWN_REG_NUM; i++) + pg_writel(*ctx++, PULLUPDOWN_REG_A + i*4); + + for (i = 0; i < TRISTATE_REG_NUM; i++) + pg_writel(*ctx++, TRISTATE_REG_A + i*4); +} +#endif diff --git a/arch/arm/mach-tegra/pinmux.c b/arch/arm/mach-tegra/pinmux.c index 13ae10237e84..f80d507671bc 100644 --- a/arch/arm/mach-tegra/pinmux.c +++ b/arch/arm/mach-tegra/pinmux.c @@ -14,7 +14,8 @@ * */ - +#include +#include #include #include #include @@ -23,21 +24,6 @@ #include #include - -#define TEGRA_TRI_STATE(x) (0x14 + (4 * (x))) -#define TEGRA_PP_MUX_CTL(x) (0x80 + (4 * (x))) -#define TEGRA_PP_PU_PD(x) (0xa0 + (4 * (x))) - -#define REG_A 0 -#define REG_B 1 -#define REG_C 2 -#define REG_D 3 -#define REG_E 4 -#define REG_F 5 -#define REG_G 6 - -#define REG_N -1 - #define HSM_EN(reg) (((reg) >> 2) & 0x1) #define SCHMT_EN(reg) (((reg) >> 3) & 0x1) #define LPMD(reg) (((reg) >> 4) & 0x3) @@ -46,154 +32,8 @@ #define SLWR(reg) (((reg) >> 28) & 0x3) #define SLWF(reg) (((reg) >> 30) & 0x3) -struct tegra_pingroup_desc { - const char *name; - int funcs[4]; - s8 tri_reg; /* offset into the TRISTATE_REG_* register bank */ - s8 tri_bit; /* offset into the TRISTATE_REG_* register bit */ - s8 mux_reg; /* offset into the PIN_MUX_CTL_* register bank */ - s8 mux_bit; /* offset into the PIN_MUX_CTL_* register bit */ - s8 pupd_reg; /* offset into the PULL_UPDOWN_REG_* register bank */ - s8 pupd_bit; /* offset into the PULL_UPDOWN_REG_* register bit */ -}; - -#define PINGROUP(pg_name, f0, f1, f2, f3, \ - tri_r, tri_b, mux_r, mux_b, pupd_r, pupd_b) \ - [TEGRA_PINGROUP_ ## pg_name] = { \ - .name = #pg_name, \ - .funcs = { \ - TEGRA_MUX_ ## f0, \ - TEGRA_MUX_ ## f1, \ - TEGRA_MUX_ ## f2, \ - TEGRA_MUX_ ## f3, \ - }, \ - .tri_reg = REG_ ## tri_r, \ - .tri_bit = tri_b, \ - .mux_reg = REG_ ## mux_r, \ - .mux_bit = mux_b, \ - .pupd_reg = REG_ ## pupd_r, \ - .pupd_bit = pupd_b, \ - } - -static const struct tegra_pingroup_desc pingroups[TEGRA_MAX_PINGROUP] = { - PINGROUP(ATA, IDE, NAND, GMI, RSVD, A, 0, A, 24, A, 0), - PINGROUP(ATB, IDE, NAND, GMI, SDIO4, A, 1, A, 16, A, 2), - PINGROUP(ATC, IDE, NAND, GMI, SDIO4, A, 2, A, 22, A, 4), - PINGROUP(ATD, IDE, NAND, GMI, SDIO4, A, 3, A, 20, A, 6), - PINGROUP(ATE, IDE, NAND, GMI, RSVD, B, 25, A, 12, A, 8), - PINGROUP(CDEV1, OSC, PLLA_OUT, PLLM_OUT1, AUDIO_SYNC, A, 4, C, 2, C, 0), - PINGROUP(CDEV2, OSC, AHB_CLK, APB_CLK, PLLP_OUT4, A, 5, C, 4, C, 2), - PINGROUP(CRTP, CRT, RSVD, RSVD, RSVD, D, 14, G, 20, B, 24), - PINGROUP(CSUS, PLLC_OUT1, PLLP_OUT2, PLLP_OUT3, VI_SENSOR_CLK, A, 6, C, 6, D, 24), - PINGROUP(DAP1, DAP1, RSVD, GMI, SDIO2, A, 7, C, 20, A, 10), - PINGROUP(DAP2, DAP2, TWC, RSVD, GMI, A, 8, C, 22, A, 12), - PINGROUP(DAP3, DAP3, RSVD, RSVD, RSVD, A, 9, C, 24, A, 14), - PINGROUP(DAP4, DAP4, RSVD, GMI, RSVD, A, 10, C, 26, A, 16), - PINGROUP(DDC, I2C2, RSVD, RSVD, RSVD, B, 31, C, 0, E, 28), - PINGROUP(DTA, RSVD, SDIO2, VI, RSVD, A, 11, B, 20, A, 18), - PINGROUP(DTB, RSVD, RSVD, VI, SPI1, A, 12, B, 22, A, 20), - PINGROUP(DTC, RSVD, RSVD, VI, RSVD, A, 13, B, 26, A, 22), - PINGROUP(DTD, RSVD, SDIO2, VI, RSVD, A, 14, B, 28, A, 24), - PINGROUP(DTE, RSVD, RSVD, VI, SPI1, A, 15, B, 30, A, 26), - PINGROUP(DTF, I2C3, RSVD, VI, RSVD, D, 12, G, 30, A, 28), - PINGROUP(GMA, UARTE, SPI3, GMI, SDIO4, A, 28, B, 0, E, 20), - PINGROUP(GMB, IDE, NAND, GMI, GMI_INT, B, 29, C, 28, E, 22), - PINGROUP(GMC, UARTD, SPI4, GMI, SFLASH, A, 29, B, 2, E, 24), - PINGROUP(GMD, RSVD, NAND, GMI, SFLASH, B, 30, C, 30, E, 26), - PINGROUP(GME, RSVD, DAP5, GMI, SDIO4, B, 0, D, 0, C, 24), - PINGROUP(GPU, PWM, UARTA, GMI, RSVD, A, 16, D, 4, B, 20), - PINGROUP(GPU7, RTCK, RSVD, RSVD, RSVD, D, 11, G, 28, B, 6), - PINGROUP(GPV, PCIE, RSVD, RSVD, RSVD, A, 17, D, 2, A, 30), - PINGROUP(HDINT, HDMI, RSVD, RSVD, RSVD, C, 23, B, 4, D, 22), - PINGROUP(I2CP, I2C, RSVD, RSVD, RSVD, A, 18, C, 8, B, 2), - PINGROUP(IRRX, UARTA, UARTB, GMI, SPI4, A, 20, C, 18, C, 22), - PINGROUP(IRTX, UARTA, UARTB, GMI, SPI4, A, 19, C, 16, C, 20), - PINGROUP(KBCA, KBC, NAND, SDIO2, EMC_TEST0_DLL, A, 22, C, 10, B, 8), - PINGROUP(KBCB, KBC, NAND, SDIO2, MIO, A, 21, C, 12, B, 10), - PINGROUP(KBCC, KBC, NAND, TRACE, EMC_TEST1_DLL, B, 26, C, 14, B, 12), - PINGROUP(KBCD, KBC, NAND, SDIO2, MIO, D, 10, G, 26, B, 14), - PINGROUP(KBCE, KBC, NAND, OWR, RSVD, A, 26, A, 28, E, 2), - PINGROUP(KBCF, KBC, NAND, TRACE, MIO, A, 27, A, 26, E, 0), - PINGROUP(LCSN, DISPLAYA, DISPLAYB, SPI3, RSVD, C, 31, E, 12, D, 20), - PINGROUP(LD0, DISPLAYA, DISPLAYB, XIO, RSVD, C, 0, F, 0, D, 12), - PINGROUP(LD1, DISPLAYA, DISPLAYB, XIO, RSVD, C, 1, F, 2, D, 12), - PINGROUP(LD10, DISPLAYA, DISPLAYB, XIO, RSVD, C, 10, F, 20, D, 12), - PINGROUP(LD11, DISPLAYA, DISPLAYB, XIO, RSVD, C, 11, F, 22, D, 12), - PINGROUP(LD12, DISPLAYA, DISPLAYB, XIO, RSVD, C, 12, F, 24, D, 12), - PINGROUP(LD13, DISPLAYA, DISPLAYB, XIO, RSVD, C, 13, F, 26, D, 12), - PINGROUP(LD14, DISPLAYA, DISPLAYB, XIO, RSVD, C, 14, F, 28, D, 12), - PINGROUP(LD15, DISPLAYA, DISPLAYB, XIO, RSVD, C, 15, F, 30, D, 12), - PINGROUP(LD16, DISPLAYA, DISPLAYB, XIO, RSVD, C, 16, G, 0, D, 12), - PINGROUP(LD17, DISPLAYA, DISPLAYB, RSVD, RSVD, C, 17, G, 2, D, 12), - PINGROUP(LD2, DISPLAYA, DISPLAYB, XIO, RSVD, C, 2, F, 4, D, 12), - PINGROUP(LD3, DISPLAYA, DISPLAYB, XIO, RSVD, C, 3, F, 6, D, 12), - PINGROUP(LD4, DISPLAYA, DISPLAYB, XIO, RSVD, C, 4, F, 8, D, 12), - PINGROUP(LD5, DISPLAYA, DISPLAYB, XIO, RSVD, C, 5, F, 10, D, 12), - PINGROUP(LD6, DISPLAYA, DISPLAYB, XIO, RSVD, C, 6, F, 12, D, 12), - PINGROUP(LD7, DISPLAYA, DISPLAYB, XIO, RSVD, C, 7, F, 14, D, 12), - PINGROUP(LD8, DISPLAYA, DISPLAYB, XIO, RSVD, C, 8, F, 16, D, 12), - PINGROUP(LD9, DISPLAYA, DISPLAYB, XIO, RSVD, C, 9, F, 18, D, 12), - PINGROUP(LDC, DISPLAYA, DISPLAYB, RSVD, RSVD, C, 30, E, 14, D, 20), - PINGROUP(LDI, DISPLAYA, DISPLAYB, RSVD, RSVD, D, 6, G, 16, D, 18), - PINGROUP(LHP0, DISPLAYA, DISPLAYB, RSVD, RSVD, C, 18, G, 10, D, 16), - PINGROUP(LHP1, DISPLAYA, DISPLAYB, RSVD, RSVD, C, 19, G, 4, D, 14), - PINGROUP(LHP2, DISPLAYA, DISPLAYB, RSVD, RSVD, C, 20, G, 6, D, 14), - PINGROUP(LHS, DISPLAYA, DISPLAYB, XIO, RSVD, D, 7, E, 22, D, 22), - PINGROUP(LM0, DISPLAYA, DISPLAYB, SPI3, RSVD, C, 24, E, 26, D, 22), - PINGROUP(LM1, DISPLAYA, DISPLAYB, RSVD, CRT, C, 25, E, 28, D, 22), - PINGROUP(LPP, DISPLAYA, DISPLAYB, RSVD, RSVD, D, 8, G, 14, D, 18), - PINGROUP(LPW0, DISPLAYA, DISPLAYB, SPI3, HDMI, D, 3, E, 0, D, 20), - PINGROUP(LPW1, DISPLAYA, DISPLAYB, RSVD, RSVD, D, 4, E, 2, D, 20), - PINGROUP(LPW2, DISPLAYA, DISPLAYB, SPI3, HDMI, D, 5, E, 4, D, 20), - PINGROUP(LSC0, DISPLAYA, DISPLAYB, XIO, RSVD, C, 27, E, 18, D, 22), - PINGROUP(LSC1, DISPLAYA, DISPLAYB, SPI3, HDMI, C, 28, E, 20, D, 20), - PINGROUP(LSCK, DISPLAYA, DISPLAYB, SPI3, HDMI, C, 29, E, 16, D, 20), - PINGROUP(LSDA, DISPLAYA, DISPLAYB, SPI3, HDMI, D, 1, E, 8, D, 20), - PINGROUP(LSDI, DISPLAYA, DISPLAYB, SPI3, RSVD, D, 2, E, 6, D, 20), - PINGROUP(LSPI, DISPLAYA, DISPLAYB, XIO, HDMI, D, 0, E, 10, D, 22), - PINGROUP(LVP0, DISPLAYA, DISPLAYB, RSVD, RSVD, C, 21, E, 30, D, 22), - PINGROUP(LVP1, DISPLAYA, DISPLAYB, RSVD, RSVD, C, 22, G, 8, D, 16), - PINGROUP(LVS, DISPLAYA, DISPLAYB, XIO, RSVD, C, 26, E, 24, D, 22), - PINGROUP(OWC, OWR, RSVD, RSVD, RSVD, A, 31, B, 8, E, 30), - PINGROUP(PMC, PWR_ON, PWR_INTR, RSVD, RSVD, A, 23, G, 18, N, -1), - PINGROUP(PTA, I2C2, HDMI, GMI, RSVD, A, 24, G, 22, B, 4), - PINGROUP(RM, I2C, RSVD, RSVD, RSVD, A, 25, A, 14, B, 0), - PINGROUP(SDB, UARTA, PWM, SDIO3, SPI2, D, 15, D, 10, N, -1), - PINGROUP(SDC, PWM, TWC, SDIO3, SPI3, B, 1, D, 12, D, 28), - PINGROUP(SDD, UARTA, PWM, SDIO3, SPI3, B, 2, D, 14, D, 30), - PINGROUP(SDIO1, SDIO1, RSVD, UARTE, UARTA, A, 30, A, 30, E, 18), - PINGROUP(SLXA, PCIE, SPI4, SDIO3, SPI2, B, 3, B, 6, B, 22), - PINGROUP(SLXC, SPDIF, SPI4, SDIO3, SPI2, B, 5, B, 10, B, 26), - PINGROUP(SLXD, SPDIF, SPI4, SDIO3, SPI2, B, 6, B, 12, B, 28), - PINGROUP(SLXK, PCIE, SPI4, SDIO3, SPI2, B, 7, B, 14, B, 30), - PINGROUP(SPDI, SPDIF, RSVD, I2C, SDIO2, B, 8, D, 8, B, 16), - PINGROUP(SPDO, SPDIF, RSVD, I2C, SDIO2, B, 9, D, 6, B, 18), - PINGROUP(SPIA, SPI1, SPI2, SPI3, GMI, B, 10, D, 30, C, 4), - PINGROUP(SPIB, SPI1, SPI2, SPI3, GMI, B, 11, D, 28, C, 6), - PINGROUP(SPIC, SPI1, SPI2, SPI3, GMI, B, 12, D, 26, C, 8), - PINGROUP(SPID, SPI2, SPI1, SPI2_ALT, GMI, B, 13, D, 24, C, 10), - PINGROUP(SPIE, SPI2, SPI1, SPI2_ALT, GMI, B, 14, D, 22, C, 12), - PINGROUP(SPIF, SPI3, SPI1, SPI2, RSVD, B, 15, D, 20, C, 14), - PINGROUP(SPIG, SPI3, SPI2, SPI2_ALT, I2C, B, 16, D, 18, C, 16), - PINGROUP(SPIH, SPI3, SPI2, SPI2_ALT, I2C, B, 17, D, 16, C, 18), - PINGROUP(UAA, SPI3, MIPI_HS, UARTA, ULPI, B, 18, A, 0, D, 0), - PINGROUP(UAB, SPI2, MIPI_HS, UARTA, ULPI, B, 19, A, 2, D, 2), - PINGROUP(UAC, OWR, RSVD, RSVD, RSVD, B, 20, A, 4, D, 4), - PINGROUP(UAD, IRDA, SPDIF, UARTA, SPI4, B, 21, A, 6, D, 6), - PINGROUP(UCA, UARTC, RSVD, GMI, RSVD, B, 22, B, 16, D, 8), - PINGROUP(UCB, UARTC, PWM, GMI, RSVD, B, 23, B, 18, D, 10), - PINGROUP(UDA, SPI1, RSVD, UARTD, ULPI, D, 13, A, 8, E, 16), - /* these pin groups only have pullup and pull down control */ - PINGROUP(CK32, RSVD, RSVD, RSVD, RSVD, N, -1, N, -1, E, 14), - PINGROUP(DDRC, RSVD, RSVD, RSVD, RSVD, N, -1, N, -1, D, 26), - PINGROUP(PMCA, RSVD, RSVD, RSVD, RSVD, N, -1, N, -1, E, 4), - PINGROUP(PMCB, RSVD, RSVD, RSVD, RSVD, N, -1, N, -1, E, 6), - PINGROUP(PMCC, RSVD, RSVD, RSVD, RSVD, N, -1, N, -1, E, 8), - PINGROUP(PMCD, RSVD, RSVD, RSVD, RSVD, N, -1, N, -1, E, 10), - PINGROUP(PMCE, RSVD, RSVD, RSVD, RSVD, N, -1, N, -1, E, 12), - PINGROUP(XM2C, RSVD, RSVD, RSVD, RSVD, N, -1, N, -1, C, 30), - PINGROUP(XM2D, RSVD, RSVD, RSVD, RSVD, N, -1, N, -1, C, 28), -}; +static const struct tegra_pingroup_desc *const pingroups = tegra_soc_pingroups; +static const struct tegra_drive_pingroup_desc *const drive_pingroups = tegra_soc_drive_pingroups; static char *tegra_mux_names[TEGRA_MAX_MUX] = { [TEGRA_MUX_AHB_CLK] = "AHB_CLK", @@ -256,48 +96,7 @@ static char *tegra_mux_names[TEGRA_MAX_MUX] = { [TEGRA_MUX_VI] = "VI", [TEGRA_MUX_VI_SENSOR_CLK] = "VI_SENSOR_CLK", [TEGRA_MUX_XIO] = "XIO", -}; - -struct tegra_drive_pingroup_desc { - const char *name; - s16 reg; -}; - -#define DRIVE_PINGROUP(pg_name, r) \ - [TEGRA_DRIVE_PINGROUP_ ## pg_name] = { \ - .name = #pg_name, \ - .reg = r \ - } - -static const struct tegra_drive_pingroup_desc drive_pingroups[TEGRA_MAX_PINGROUP] = { - DRIVE_PINGROUP(AO1, 0x868), - DRIVE_PINGROUP(AO2, 0x86c), - DRIVE_PINGROUP(AT1, 0x870), - DRIVE_PINGROUP(AT2, 0x874), - DRIVE_PINGROUP(CDEV1, 0x878), - DRIVE_PINGROUP(CDEV2, 0x87c), - DRIVE_PINGROUP(CSUS, 0x880), - DRIVE_PINGROUP(DAP1, 0x884), - DRIVE_PINGROUP(DAP2, 0x888), - DRIVE_PINGROUP(DAP3, 0x88c), - DRIVE_PINGROUP(DAP4, 0x890), - DRIVE_PINGROUP(DBG, 0x894), - DRIVE_PINGROUP(LCD1, 0x898), - DRIVE_PINGROUP(LCD2, 0x89c), - DRIVE_PINGROUP(SDMMC2, 0x8a0), - DRIVE_PINGROUP(SDMMC3, 0x8a4), - DRIVE_PINGROUP(SPI, 0x8a8), - DRIVE_PINGROUP(UAA, 0x8ac), - DRIVE_PINGROUP(UAB, 0x8b0), - DRIVE_PINGROUP(UART2, 0x8b4), - DRIVE_PINGROUP(UART3, 0x8b8), - DRIVE_PINGROUP(VI1, 0x8bc), - DRIVE_PINGROUP(VI2, 0x8c0), - DRIVE_PINGROUP(XM2A, 0x8c4), - DRIVE_PINGROUP(XM2C, 0x8c8), - DRIVE_PINGROUP(XM2D, 0x8cc), - DRIVE_PINGROUP(XM2CLK, 0x8d0), - DRIVE_PINGROUP(MEMCOMP, 0x8d4), + [TEGRA_MUX_SAFE] = "", }; static const char *tegra_drive_names[TEGRA_MAX_DRIVE] = { @@ -381,22 +180,27 @@ static inline void pg_writel(unsigned long value, unsigned long offset) writel(value, IO_TO_VIRT(TEGRA_APB_MISC_BASE + offset)); } -int tegra_pinmux_set_func(enum tegra_pingroup pg, enum tegra_mux_func func) +static int tegra_pinmux_set_func(const struct tegra_pingroup_config *config) { int mux = -1; int i; unsigned long reg; unsigned long flags; + enum tegra_pingroup pg = config->pingroup; + enum tegra_mux_func func = config->func; if (pg < 0 || pg >= TEGRA_MAX_PINGROUP) return -ERANGE; - if (pingroups[pg].mux_reg == REG_N) + if (pingroups[pg].mux_reg < 0) return -EINVAL; if (func < 0) return -ERANGE; + if (func == TEGRA_MUX_SAFE) + func = pingroups[pg].func_safe; + if (func & TEGRA_MUX_RSVD) { mux = func & 0x3; } else { @@ -413,10 +217,10 @@ int tegra_pinmux_set_func(enum tegra_pingroup pg, enum tegra_mux_func func) spin_lock_irqsave(&mux_lock, flags); - reg = pg_readl(TEGRA_PP_MUX_CTL(pingroups[pg].mux_reg)); + reg = pg_readl(pingroups[pg].mux_reg); reg &= ~(0x3 << pingroups[pg].mux_bit); reg |= mux << pingroups[pg].mux_bit; - pg_writel(reg, TEGRA_PP_MUX_CTL(pingroups[pg].mux_reg)); + pg_writel(reg, pingroups[pg].mux_reg); spin_unlock_irqrestore(&mux_lock, flags); @@ -432,16 +236,16 @@ int tegra_pinmux_set_tristate(enum tegra_pingroup pg, if (pg < 0 || pg >= TEGRA_MAX_PINGROUP) return -ERANGE; - if (pingroups[pg].tri_reg == REG_N) + if (pingroups[pg].tri_reg < 0) return -EINVAL; spin_lock_irqsave(&mux_lock, flags); - reg = pg_readl(TEGRA_TRI_STATE(pingroups[pg].tri_reg)); + reg = pg_readl(pingroups[pg].tri_reg); reg &= ~(0x1 << pingroups[pg].tri_bit); if (tristate) reg |= 1 << pingroups[pg].tri_bit; - pg_writel(reg, TEGRA_TRI_STATE(pingroups[pg].tri_reg)); + pg_writel(reg, pingroups[pg].tri_reg); spin_unlock_irqrestore(&mux_lock, flags); @@ -457,7 +261,7 @@ int tegra_pinmux_set_pullupdown(enum tegra_pingroup pg, if (pg < 0 || pg >= TEGRA_MAX_PINGROUP) return -ERANGE; - if (pingroups[pg].pupd_reg == REG_N) + if (pingroups[pg].pupd_reg < 0) return -EINVAL; if (pupd != TEGRA_PUPD_NORMAL && @@ -468,38 +272,39 @@ int tegra_pinmux_set_pullupdown(enum tegra_pingroup pg, spin_lock_irqsave(&mux_lock, flags); - reg = pg_readl(TEGRA_PP_PU_PD(pingroups[pg].pupd_reg)); + reg = pg_readl(pingroups[pg].pupd_reg); reg &= ~(0x3 << pingroups[pg].pupd_bit); reg |= pupd << pingroups[pg].pupd_bit; - pg_writel(reg, TEGRA_PP_PU_PD(pingroups[pg].pupd_reg)); + pg_writel(reg, pingroups[pg].pupd_reg); spin_unlock_irqrestore(&mux_lock, flags); return 0; } -void tegra_pinmux_config_pingroup(enum tegra_pingroup pingroup, - enum tegra_mux_func func, - enum tegra_pullupdown pupd, - enum tegra_tristate tristate) +static void tegra_pinmux_config_pingroup(const struct tegra_pingroup_config *config) { + enum tegra_pingroup pingroup = config->pingroup; + enum tegra_mux_func func = config->func; + enum tegra_pullupdown pupd = config->pupd; + enum tegra_tristate tristate = config->tristate; int err; - if (pingroups[pingroup].mux_reg != REG_N) { - err = tegra_pinmux_set_func(pingroup, func); + if (pingroups[pingroup].mux_reg >= 0) { + err = tegra_pinmux_set_func(config); if (err < 0) pr_err("pinmux: can't set pingroup %s func to %s: %d\n", pingroup_name(pingroup), func_name(func), err); } - if (pingroups[pingroup].pupd_reg != REG_N) { + if (pingroups[pingroup].pupd_reg >= 0) { err = tegra_pinmux_set_pullupdown(pingroup, pupd); if (err < 0) pr_err("pinmux: can't set pingroup %s pullupdown to %s: %d\n", pingroup_name(pingroup), pupd_name(pupd), err); } - if (pingroups[pingroup].tri_reg != REG_N) { + if (pingroups[pingroup].tri_reg >= 0) { err = tegra_pinmux_set_tristate(pingroup, tristate); if (err < 0) pr_err("pinmux: can't set pingroup %s tristate to %s: %d\n", @@ -507,17 +312,12 @@ void tegra_pinmux_config_pingroup(enum tegra_pingroup pingroup, } } - - -void tegra_pinmux_config_table(struct tegra_pingroup_config *config, int len) +void tegra_pinmux_config_table(const struct tegra_pingroup_config *config, int len) { int i; for (i = 0; i < len; i++) - tegra_pinmux_config_pingroup(config[i].pingroup, - config[i].func, - config[i].pupd, - config[i].tristate); + tegra_pinmux_config_pingroup(&config[i]); } static const char *drive_pinmux_name(enum tegra_drive_pingroup pg) @@ -784,6 +584,86 @@ void tegra_drive_pinmux_config_table(struct tegra_drive_pingroup_config *config, config[i].slew_falling); } +void tegra_pinmux_set_safe_pinmux_table(const struct tegra_pingroup_config *config, + int len) +{ + int i; + struct tegra_pingroup_config c; + + for (i = 0; i < len; i++) { + int err; + c = config[i]; + if (c.pingroup < 0 || c.pingroup >= TEGRA_MAX_PINGROUP) { + WARN_ON(1); + continue; + } + c.func = pingroups[c.pingroup].func_safe; + err = tegra_pinmux_set_func(&c); + if (err < 0) + pr_err("%s: tegra_pinmux_set_func returned %d setting " + "%s to %s\n", __func__, err, + pingroup_name(c.pingroup), func_name(c.func)); + } +} + +void tegra_pinmux_config_pinmux_table(const struct tegra_pingroup_config *config, + int len) +{ + int i; + + for (i = 0; i < len; i++) { + int err; + if (config[i].pingroup < 0 || + config[i].pingroup >= TEGRA_MAX_PINGROUP) { + WARN_ON(1); + continue; + } + err = tegra_pinmux_set_func(&config[i]); + if (err < 0) + pr_err("%s: tegra_pinmux_set_func returned %d setting " + "%s to %s\n", __func__, err, + pingroup_name(config[i].pingroup), + func_name(config[i].func)); + } +} + +void tegra_pinmux_config_tristate_table(const struct tegra_pingroup_config *config, + int len, enum tegra_tristate tristate) +{ + int i; + int err; + enum tegra_pingroup pingroup; + + for (i = 0; i < len; i++) { + pingroup = config[i].pingroup; + if (pingroups[pingroup].tri_reg >= 0) { + err = tegra_pinmux_set_tristate(pingroup, tristate); + if (err < 0) + pr_err("pinmux: can't set pingroup %s tristate" + " to %s: %d\n", pingroup_name(pingroup), + tri_name(tristate), err); + } + } +} + +void tegra_pinmux_config_pullupdown_table(const struct tegra_pingroup_config *config, + int len, enum tegra_pullupdown pupd) +{ + int i; + int err; + enum tegra_pingroup pingroup; + + for (i = 0; i < len; i++) { + pingroup = config[i].pingroup; + if (pingroups[pingroup].pupd_reg >= 0) { + err = tegra_pinmux_set_pullupdown(pingroup, pupd); + if (err < 0) + pr_err("pinmux: can't set pingroup %s pullupdown" + " to %s: %d\n", pingroup_name(pingroup), + pupd_name(pupd), err); + } + } +} #ifdef CONFIG_DEBUG_FS @@ -812,11 +692,11 @@ static int dbg_pinmux_show(struct seq_file *s, void *unused) len = strlen(pingroups[i].name); dbg_pad_field(s, 5 - len); - if (pingroups[i].mux_reg == REG_N) { + if (pingroups[i].mux_reg < 0) { seq_printf(s, "TEGRA_MUX_NONE"); len = strlen("NONE"); } else { - mux = (pg_readl(TEGRA_PP_MUX_CTL(pingroups[i].mux_reg)) >> + mux = (pg_readl(pingroups[i].mux_reg) >> pingroups[i].mux_bit) & 0x3; if (pingroups[i].funcs[mux] == TEGRA_MUX_RSVD) { seq_printf(s, "TEGRA_MUX_RSVD%1lu", mux+1); @@ -829,21 +709,21 @@ static int dbg_pinmux_show(struct seq_file *s, void *unused) } dbg_pad_field(s, 13-len); - if (pingroups[i].mux_reg == REG_N) { + if (pingroups[i].pupd_reg < 0) { seq_printf(s, "TEGRA_PUPD_NORMAL"); len = strlen("NORMAL"); } else { - pupd = (pg_readl(TEGRA_PP_PU_PD(pingroups[i].pupd_reg)) >> + pupd = (pg_readl(pingroups[i].pupd_reg) >> pingroups[i].pupd_bit) & 0x3; seq_printf(s, "TEGRA_PUPD_%s", pupd_name(pupd)); len = strlen(pupd_name(pupd)); } dbg_pad_field(s, 9 - len); - if (pingroups[i].tri_reg == REG_N) { + if (pingroups[i].tri_reg < 0) { seq_printf(s, "TEGRA_TRI_NORMAL"); } else { - tri = (pg_readl(TEGRA_TRI_STATE(pingroups[i].tri_reg)) >> + tri = (pg_readl(pingroups[i].tri_reg) >> pingroups[i].tri_bit) & 0x1; seq_printf(s, "TEGRA_TRI_%s", tri_name(tri)); -- cgit v1.2.3 From 2e47b8b3c2dc0f9f0651ee0f20e163da09b20fb9 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Wed, 7 Apr 2010 12:59:42 -0700 Subject: [ARM] tegra: gpio: Add suspend and wake support Includes checkpatch fixes and TEGRA_NR_GPIOS changes from Mike Rapoport Signed-off-by: Colin Cross --- arch/arm/mach-tegra/gpio.c | 104 ++++++++++++++++++++++++++++---- arch/arm/mach-tegra/include/mach/gpio.h | 4 +- 2 files changed, 95 insertions(+), 13 deletions(-) (limited to 'arch') diff --git a/arch/arm/mach-tegra/gpio.c b/arch/arm/mach-tegra/gpio.c index fe78fba25f3c..0775265e69f5 100644 --- a/arch/arm/mach-tegra/gpio.c +++ b/arch/arm/mach-tegra/gpio.c @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -60,6 +61,13 @@ struct tegra_gpio_bank { int bank; int irq; spinlock_t lvl_lock[4]; +#ifdef CONFIG_PM + u32 cnf[4]; + u32 out[4]; + u32 oe[4]; + u32 int_enb[4]; + u32 int_lvl[4]; +#endif }; @@ -131,7 +139,7 @@ static struct gpio_chip tegra_gpio_chip = { .direction_output = tegra_gpio_direction_output, .set = tegra_gpio_set, .base = 0, - .ngpio = ARCH_NR_GPIOS, + .ngpio = TEGRA_NR_GPIOS, }; static void tegra_gpio_irq_ack(unsigned int irq) @@ -244,6 +252,76 @@ static void tegra_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) } +#ifdef CONFIG_PM +void tegra_gpio_resume(void) +{ + unsigned long flags; + int b, p, i; + + local_irq_save(flags); + + for (b = 0; b < ARRAY_SIZE(tegra_gpio_banks); b++) { + struct tegra_gpio_bank *bank = &tegra_gpio_banks[b]; + + for (p = 0; p < ARRAY_SIZE(bank->oe); p++) { + unsigned int gpio = (b<<5) | (p<<3); + __raw_writel(bank->cnf[p], GPIO_CNF(gpio)); + __raw_writel(bank->out[p], GPIO_OUT(gpio)); + __raw_writel(bank->oe[p], GPIO_OE(gpio)); + __raw_writel(bank->int_lvl[p], GPIO_INT_LVL(gpio)); + __raw_writel(bank->int_enb[p], GPIO_INT_ENB(gpio)); + } + } + + local_irq_restore(flags); + + for (i = INT_GPIO_BASE; i < (INT_GPIO_BASE + TEGRA_NR_GPIOS); i++) { + struct irq_desc *desc = irq_to_desc(i); + if (!desc || (desc->status & IRQ_WAKEUP)) + continue; + enable_irq(i); + } +} + +void tegra_gpio_suspend(void) +{ + unsigned long flags; + int b, p, i; + + for (i = INT_GPIO_BASE; i < (INT_GPIO_BASE + TEGRA_NR_GPIOS); i++) { + struct irq_desc *desc = irq_to_desc(i); + if (!desc) + continue; + if (desc->status & IRQ_WAKEUP) { + int gpio = i - INT_GPIO_BASE; + pr_debug("gpio %d.%d is wakeup\n", gpio/8, gpio&7); + continue; + } + disable_irq(i); + } + + local_irq_save(flags); + for (b = 0; b < ARRAY_SIZE(tegra_gpio_banks); b++) { + struct tegra_gpio_bank *bank = &tegra_gpio_banks[b]; + + for (p = 0; p < ARRAY_SIZE(bank->oe); p++) { + unsigned int gpio = (b<<5) | (p<<3); + bank->cnf[p] = __raw_readl(GPIO_CNF(gpio)); + bank->out[p] = __raw_readl(GPIO_OUT(gpio)); + bank->oe[p] = __raw_readl(GPIO_OE(gpio)); + bank->int_enb[p] = __raw_readl(GPIO_INT_ENB(gpio)); + bank->int_lvl[p] = __raw_readl(GPIO_INT_LVL(gpio)); + } + } + local_irq_restore(flags); +} + +static int tegra_gpio_wake_enable(unsigned int irq, unsigned int enable) +{ + struct tegra_gpio_bank *bank = get_irq_chip_data(irq); + return set_irq_wake(bank->irq, enable); +} +#endif static struct irq_chip tegra_gpio_irq_chip = { .name = "GPIO", @@ -251,6 +329,9 @@ static struct irq_chip tegra_gpio_irq_chip = { .mask = tegra_gpio_irq_mask, .unmask = tegra_gpio_irq_unmask, .set_type = tegra_gpio_irq_set_type, +#ifdef CONFIG_PM + .set_wake = tegra_gpio_wake_enable, +#endif }; @@ -274,7 +355,7 @@ static int __init tegra_gpio_init(void) gpiochip_add(&tegra_gpio_chip); - for (i = INT_GPIO_BASE; i < (INT_GPIO_BASE + ARCH_NR_GPIOS); i++) { + for (i = INT_GPIO_BASE; i < (INT_GPIO_BASE + TEGRA_NR_GPIOS); i++) { bank = &tegra_gpio_banks[GPIO_BANK(irq_to_gpio(i))]; lockdep_set_class(&irq_desc[i].lock, &gpio_lock_class); @@ -312,15 +393,16 @@ static int dbg_gpio_show(struct seq_file *s, void *unused) for (i = 0; i < 7; i++) { for (j = 0; j < 4; j++) { int gpio = tegra_gpio_compose(i, j, 0); - seq_printf(s, "%d:%d %02x %02x %02x %02x %02x %02x %06x\n", - i, j, - __raw_readl(GPIO_CNF(gpio)), - __raw_readl(GPIO_OE(gpio)), - __raw_readl(GPIO_OUT(gpio)), - __raw_readl(GPIO_IN(gpio)), - __raw_readl(GPIO_INT_STA(gpio)), - __raw_readl(GPIO_INT_ENB(gpio)), - __raw_readl(GPIO_INT_LVL(gpio))); + seq_printf(s, + "%d:%d %02x %02x %02x %02x %02x %02x %06x\n", + i, j, + __raw_readl(GPIO_CNF(gpio)), + __raw_readl(GPIO_OE(gpio)), + __raw_readl(GPIO_OUT(gpio)), + __raw_readl(GPIO_IN(gpio)), + __raw_readl(GPIO_INT_STA(gpio)), + __raw_readl(GPIO_INT_ENB(gpio)), + __raw_readl(GPIO_INT_LVL(gpio))); } } return 0; diff --git a/arch/arm/mach-tegra/include/mach/gpio.h b/arch/arm/mach-tegra/include/mach/gpio.h index 540e822e50f7..e31f486d69a2 100644 --- a/arch/arm/mach-tegra/include/mach/gpio.h +++ b/arch/arm/mach-tegra/include/mach/gpio.h @@ -22,7 +22,7 @@ #include -#define ARCH_NR_GPIOS INT_GPIO_NR +#define TEGRA_NR_GPIOS INT_GPIO_NR #include @@ -35,7 +35,7 @@ static inline int gpio_to_irq(unsigned int gpio) { - if (gpio < ARCH_NR_GPIOS) + if (gpio < TEGRA_NR_GPIOS) return INT_GPIO_BASE + gpio; return -EINVAL; } -- cgit v1.2.3 From 73625e3e2e2bc36198f5b43e0f32d9dfb8e3b77c Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Wed, 23 Jun 2010 15:49:17 -0700 Subject: [ARM] tegra: Add support for reading fuses The Tegra SOC contains fuses to identify the CPU type and bin, and a unique id. The CPU info is required to determine the correct voltages for each cpu and core frequency. Signed-off-by: Colin Cross --- arch/arm/mach-tegra/Makefile | 1 + arch/arm/mach-tegra/common.c | 2 ++ arch/arm/mach-tegra/fuse.c | 84 ++++++++++++++++++++++++++++++++++++++++++++ arch/arm/mach-tegra/fuse.h | 24 +++++++++++++ 4 files changed, 111 insertions(+) create mode 100644 arch/arm/mach-tegra/fuse.c create mode 100644 arch/arm/mach-tegra/fuse.h (limited to 'arch') diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile index 87d065e65113..620347525272 100644 --- a/arch/arm/mach-tegra/Makefile +++ b/arch/arm/mach-tegra/Makefile @@ -5,6 +5,7 @@ obj-y += clock.o obj-y += timer.o obj-y += gpio.o obj-y += pinmux.o +obj-y += fuse.o obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += clock.o obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_clocks.o obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += pinmux-t2-tables.o diff --git a/arch/arm/mach-tegra/common.c b/arch/arm/mach-tegra/common.c index 039a514b61ef..9aedaf77013c 100644 --- a/arch/arm/mach-tegra/common.c +++ b/arch/arm/mach-tegra/common.c @@ -26,6 +26,7 @@ #include "board.h" #include "clock.h" +#include "fuse.h" static __initdata struct tegra_clk_init_table common_clk_init_table[] = { /* name parent rate enabled */ @@ -55,6 +56,7 @@ void __init tegra_init_cache(void) void __init tegra_common_init(void) { + tegra_init_fuse(); tegra_init_clock(); tegra_clk_init_from_table(common_clk_init_table); tegra_init_cache(); diff --git a/arch/arm/mach-tegra/fuse.c b/arch/arm/mach-tegra/fuse.c new file mode 100644 index 000000000000..1fa26d9a1a68 --- /dev/null +++ b/arch/arm/mach-tegra/fuse.c @@ -0,0 +1,84 @@ +/* + * arch/arm/mach-tegra/fuse.c + * + * Copyright (C) 2010 Google, Inc. + * + * Author: + * Colin Cross + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that 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. + * + */ + +#include +#include + +#include + +#include "fuse.h" + +#define FUSE_UID_LOW 0x108 +#define FUSE_UID_HIGH 0x10c +#define FUSE_SKU_INFO 0x110 +#define FUSE_SPARE_BIT 0x200 + +static inline u32 fuse_readl(unsigned long offset) +{ + return readl(IO_TO_VIRT(TEGRA_FUSE_BASE + offset)); +} + +static inline void fuse_writel(u32 value, unsigned long offset) +{ + writel(value, IO_TO_VIRT(TEGRA_FUSE_BASE + offset)); +} + +void tegra_init_fuse(void) +{ + u32 reg = readl(IO_TO_VIRT(TEGRA_CLK_RESET_BASE + 0x48)); + reg |= 1 << 28; + writel(reg, IO_TO_VIRT(TEGRA_CLK_RESET_BASE + 0x48)); + + pr_info("Tegra SKU: %d CPU Process: %d Core Process: %d\n", + tegra_sku_id(), tegra_cpu_process_id(), + tegra_core_process_id()); +} + +unsigned long long tegra_chip_uid(void) +{ + unsigned long long lo, hi; + + lo = fuse_readl(FUSE_UID_LOW); + hi = fuse_readl(FUSE_UID_HIGH); + return (hi << 32ull) | lo; +} + +int tegra_sku_id(void) +{ + int sku_id; + u32 reg = fuse_readl(FUSE_SKU_INFO); + sku_id = reg & 0xFF; + return sku_id; +} + +int tegra_cpu_process_id(void) +{ + int cpu_process_id; + u32 reg = fuse_readl(FUSE_SPARE_BIT); + cpu_process_id = (reg >> 6) & 3; + return cpu_process_id; +} + +int tegra_core_process_id(void) +{ + int core_process_id; + u32 reg = fuse_readl(FUSE_SPARE_BIT); + core_process_id = (reg >> 12) & 3; + return core_process_id; +} diff --git a/arch/arm/mach-tegra/fuse.h b/arch/arm/mach-tegra/fuse.h new file mode 100644 index 000000000000..584b2e27dbda --- /dev/null +++ b/arch/arm/mach-tegra/fuse.h @@ -0,0 +1,24 @@ +/* + * arch/arm/mach-tegra/fuse.c + * + * Copyright (C) 2010 Google, Inc. + * + * Author: + * Colin Cross + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that 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. + * + */ + +unsigned long long tegra_chip_uid(void); +int tegra_sku_id(void); +int tegra_cpu_process_id(void); +int tegra_core_process_id(void); +void tegra_init_fuse(void); -- cgit v1.2.3 From 71fc84cc35ee05913306bfe6e2454cdfc5bf7081 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Mon, 7 Jun 2010 20:49:46 -0700 Subject: [ARM] tegra: clock: Add dvfs support, bug fixes, and cleanups - Add drivers to clock lookup table - Add new pll_m entries - Support I2C U16 divider - Fix rate reporting on 32.768kHz clock - Call propagate rate only if set_rate succeeds - Add support for audio_sync clock - Add 24MHz to PLLA frequency list - Correct i2s1/2/spdifout mux - Add suspend support - Fix enable/disable parent clocks in set_parent - Add max_rate parameter to all clocks - DVFS support - Add virtual cpu clock with dvfs - Support clk_round_rate - Fix requesting very high periph frequencies - Add quirks for PLLU: PLLU is slightly different from the rest of the PLLs. The lock enable bit is at bit 22 instead of 18 in the MISC register, and the post divider field is a single bit with reversed values from other PLLs. - Simplify recalculating clock rates - Fix UART divider flags - Remove unused clock ops Signed-off-by: Colin Cross --- arch/arm/mach-tegra/Makefile | 1 + arch/arm/mach-tegra/clock.c | 267 ++++++++++- arch/arm/mach-tegra/clock.h | 58 ++- arch/arm/mach-tegra/include/mach/clk.h | 5 + arch/arm/mach-tegra/tegra2_clocks.c | 778 +++++++++++++++++++++++++-------- arch/arm/mach-tegra/tegra2_dvfs.c | 86 ++++ arch/arm/mach-tegra/tegra2_dvfs.h | 20 + 7 files changed, 1005 insertions(+), 210 deletions(-) create mode 100644 arch/arm/mach-tegra/tegra2_dvfs.c create mode 100644 arch/arm/mach-tegra/tegra2_dvfs.h (limited to 'arch') diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile index 620347525272..3642e58e5073 100644 --- a/arch/arm/mach-tegra/Makefile +++ b/arch/arm/mach-tegra/Makefile @@ -8,6 +8,7 @@ obj-y += pinmux.o obj-y += fuse.o obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += clock.o obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_clocks.o +obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_dvfs.o obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += pinmux-t2-tables.o obj-$(CONFIG_SMP) += platsmp.o localtimer.o headsmp.o obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o diff --git a/arch/arm/mach-tegra/clock.c b/arch/arm/mach-tegra/clock.c index 03ad578349b9..ae19f95585be 100644 --- a/arch/arm/mach-tegra/clock.c +++ b/arch/arm/mach-tegra/clock.c @@ -24,13 +24,80 @@ #include #include #include +#include #include #include "clock.h" +#include "board.h" +#include "fuse.h" static LIST_HEAD(clocks); static DEFINE_SPINLOCK(clock_lock); +static DEFINE_MUTEX(dvfs_lock); + +static int clk_is_dvfs(struct clk *c) +{ + return (c->dvfs != NULL); +}; + +static int dvfs_set_rate(struct dvfs *d, unsigned long rate) +{ + struct dvfs_table *t; + + if (d->table == NULL) + return -ENODEV; + + for (t = d->table; t->rate != 0; t++) { + if (rate <= t->rate) { + if (!d->reg) + return 0; + + return regulator_set_voltage(d->reg, + t->millivolts * 1000, + d->max_millivolts * 1000); + } + } + + return -EINVAL; +} + +static void dvfs_init(struct clk *c) +{ + int process_id; + int i; + struct dvfs_table *table; + + process_id = c->dvfs->cpu ? tegra_core_process_id() : + tegra_cpu_process_id(); + + for (i = 0; i < c->dvfs->process_id_table_length; i++) + if (process_id == c->dvfs->process_id_table[i].process_id) + c->dvfs->table = c->dvfs->process_id_table[i].table; + + if (c->dvfs->table == NULL) { + pr_err("Failed to find dvfs table for clock %s process %d\n", + c->name, process_id); + return; + } + + c->dvfs->max_millivolts = 0; + for (table = c->dvfs->table; table->rate != 0; table++) + if (c->dvfs->max_millivolts < table->millivolts) + c->dvfs->max_millivolts = table->millivolts; + + c->dvfs->reg = regulator_get(NULL, c->dvfs->reg_id); + + if (IS_ERR(c->dvfs->reg)) { + pr_err("Failed to get regulator %s for clock %s\n", + c->dvfs->reg_id, c->name); + c->dvfs->reg = NULL; + return; + } + + if (c->refcnt > 0) + dvfs_set_rate(c->dvfs, c->rate); +} struct clk *tegra_get_clock_by_name(const char *name) { @@ -48,14 +115,31 @@ struct clk *tegra_get_clock_by_name(const char *name) return ret; } +static void clk_recalculate_rate(struct clk *c) +{ + u64 rate; + + if (!c->parent) + return; + + rate = c->parent->rate; + + if (c->mul != 0 && c->div != 0) { + rate = rate * c->mul; + do_div(rate, c->div); + } + + if (rate > c->max_rate) + pr_warn("clocks: Set clock %s to rate %llu, max is %lu\n", + c->name, rate, c->max_rate); + + c->rate = rate; +} + int clk_reparent(struct clk *c, struct clk *parent) { pr_debug("%s: %s\n", __func__, c->name); - if (c->refcnt && c->parent) - clk_disable_locked(c->parent); c->parent = parent; - if (c->refcnt && c->parent) - clk_enable_locked(c->parent); list_del(&c->sibling); list_add_tail(&c->sibling, &parent->children); return 0; @@ -67,8 +151,7 @@ static void propagate_rate(struct clk *c) pr_debug("%s: %s\n", __func__, c->name); list_for_each_entry(clkp, &c->children, sibling) { pr_debug(" %s\n", clkp->name); - if (clkp->ops->recalculate_rate) - clkp->ops->recalculate_rate(clkp); + clk_recalculate_rate(clkp); propagate_rate(clkp); } } @@ -77,6 +160,8 @@ void clk_init(struct clk *c) { unsigned long flags; + pr_debug("%s: %s\n", __func__, c->name); + spin_lock_irqsave(&clock_lock, flags); INIT_LIST_HEAD(&c->children); @@ -85,6 +170,8 @@ void clk_init(struct clk *c) if (c->ops && c->ops->init) c->ops->init(c); + clk_recalculate_rate(c); + list_add(&c->node, &clocks); if (c->parent) @@ -122,13 +209,38 @@ int clk_enable_locked(struct clk *c) return 0; } +int clk_enable_cansleep(struct clk *c) +{ + int ret; + unsigned long flags; + + mutex_lock(&dvfs_lock); + + if (clk_is_dvfs(c) && c->refcnt > 0) + dvfs_set_rate(c->dvfs, c->rate); + + spin_lock_irqsave(&clock_lock, flags); + ret = clk_enable_locked(c); + spin_unlock_irqrestore(&clock_lock, flags); + + mutex_unlock(&dvfs_lock); + + return ret; +} +EXPORT_SYMBOL(clk_enable_cansleep); + int clk_enable(struct clk *c) { int ret; unsigned long flags; + + if (clk_is_dvfs(c)) + BUG(); + spin_lock_irqsave(&clock_lock, flags); ret = clk_enable_locked(c); spin_unlock_irqrestore(&clock_lock, flags); + return ret; } EXPORT_SYMBOL(clk_enable); @@ -152,9 +264,30 @@ void clk_disable_locked(struct clk *c) c->refcnt--; } +void clk_disable_cansleep(struct clk *c) +{ + unsigned long flags; + + mutex_lock(&dvfs_lock); + + spin_lock_irqsave(&clock_lock, flags); + clk_disable_locked(c); + spin_unlock_irqrestore(&clock_lock, flags); + + if (clk_is_dvfs(c) && c->refcnt == 0) + dvfs_set_rate(c->dvfs, c->rate); + + mutex_unlock(&dvfs_lock); +} +EXPORT_SYMBOL(clk_disable_cansleep); + void clk_disable(struct clk *c) { unsigned long flags; + + if (clk_is_dvfs(c)) + BUG(); + spin_lock_irqsave(&clock_lock, flags); clk_disable_locked(c); spin_unlock_irqrestore(&clock_lock, flags); @@ -175,6 +308,8 @@ int clk_set_parent_locked(struct clk *c, struct clk *parent) if (ret) return ret; + clk_recalculate_rate(c); + propagate_rate(c); return 0; @@ -197,22 +332,69 @@ struct clk *clk_get_parent(struct clk *c) } EXPORT_SYMBOL(clk_get_parent); -int clk_set_rate(struct clk *c, unsigned long rate) +int clk_set_rate_locked(struct clk *c, unsigned long rate) +{ + int ret; + + if (rate > c->max_rate) + rate = c->max_rate; + + if (!c->ops || !c->ops->set_rate) + return -ENOSYS; + + ret = c->ops->set_rate(c, rate); + + if (ret) + return ret; + + clk_recalculate_rate(c); + + propagate_rate(c); + + return 0; +} + +int clk_set_rate_cansleep(struct clk *c, unsigned long rate) { int ret = 0; unsigned long flags; + pr_debug("%s: %s\n", __func__, c->name); + + mutex_lock(&dvfs_lock); + + if (rate > c->rate) + ret = dvfs_set_rate(c->dvfs, rate); + if (ret) + goto out; + spin_lock_irqsave(&clock_lock, flags); + ret = clk_set_rate_locked(c, rate); + spin_unlock_irqrestore(&clock_lock, flags); - pr_debug("%s: %s\n", __func__, c->name); + if (ret) + goto out; - if (c->ops && c->ops->set_rate) - ret = c->ops->set_rate(c, rate); - else - ret = -ENOSYS; + ret = dvfs_set_rate(c->dvfs, rate); - propagate_rate(c); +out: + mutex_unlock(&dvfs_lock); + return ret; +} +EXPORT_SYMBOL(clk_set_rate_cansleep); + +int clk_set_rate(struct clk *c, unsigned long rate) +{ + int ret = 0; + unsigned long flags; + + pr_debug("%s: %s\n", __func__, c->name); + + if (clk_is_dvfs(c)) + BUG(); + spin_lock_irqsave(&clock_lock, flags); + ret = clk_set_rate_locked(c, rate); spin_unlock_irqrestore(&clock_lock, flags); return ret; @@ -235,6 +417,20 @@ unsigned long clk_get_rate(struct clk *c) } EXPORT_SYMBOL(clk_get_rate); +long clk_round_rate(struct clk *c, unsigned long rate) +{ + pr_debug("%s: %s\n", __func__, c->name); + + if (!c->ops || !c->ops->round_rate) + return -ENOSYS; + + if (rate > c->max_rate) + rate = c->max_rate; + + return c->ops->round_rate(c, rate); +} +EXPORT_SYMBOL(clk_round_rate); + static int tegra_clk_init_one_from_table(struct tegra_clk_init_table *table) { struct clk *c; @@ -308,13 +504,28 @@ void tegra_periph_reset_assert(struct clk *c) } EXPORT_SYMBOL(tegra_periph_reset_assert); -int __init tegra_init_clock(void) +void __init tegra_init_clock(void) { tegra2_init_clocks(); +} + +int __init tegra_init_dvfs(void) +{ + struct clk *c, *safe; + + mutex_lock(&dvfs_lock); + + list_for_each_entry_safe(c, safe, &clocks, node) + if (c->dvfs) + dvfs_init(c); + + mutex_unlock(&dvfs_lock); return 0; } +late_initcall(tegra_init_dvfs); + #ifdef CONFIG_DEBUG_FS static struct dentry *clk_debugfs_root; @@ -324,7 +535,7 @@ static void clock_tree_show_one(struct seq_file *s, struct clk *c, int level) struct clk *child; struct clk *safe; const char *state = "uninit"; - char div[5] = {0}; + char div[8] = {0}; if (c->state == ON) state = "on"; @@ -332,16 +543,26 @@ static void clock_tree_show_one(struct seq_file *s, struct clk *c, int level) state = "off"; if (c->mul != 0 && c->div != 0) { - BUG_ON(c->mul > 2); - if (c->mul > c->div) - snprintf(div, sizeof(div), "x%d", c->mul / c->div); - else + if (c->mul > c->div) { + int mul = c->mul / c->div; + int mul2 = (c->mul * 10 / c->div) % 10; + int mul3 = (c->mul * 10) % c->div; + if (mul2 == 0 && mul3 == 0) + snprintf(div, sizeof(div), "x%d", mul); + else if (mul3 == 0) + snprintf(div, sizeof(div), "x%d.%d", mul, mul2); + else + snprintf(div, sizeof(div), "x%d.%d..", mul, mul2); + } else { snprintf(div, sizeof(div), "%d%s", c->div / c->mul, (c->div % c->mul) ? ".5" : ""); + } } - seq_printf(s, "%*s%-*s %-6s %-3d %-5s %-10lu\n", - level * 3 + 1, c->set ? "" : "*", + seq_printf(s, "%*s%c%c%-*s %-6s %-3d %-8s %-10lu\n", + level * 3 + 1, "", + c->rate > c->max_rate ? '!' : ' ', + !c->set ? '*' : ' ', 30 - level * 3, c->name, state, c->refcnt, div, c->rate); list_for_each_entry_safe(child, safe, &c->children, sibling) { @@ -353,8 +574,8 @@ static int clock_tree_show(struct seq_file *s, void *data) { struct clk *c; unsigned long flags; - seq_printf(s, " clock state ref div rate \n"); - seq_printf(s, "-----------------------------------------------------------\n"); + seq_printf(s, " clock state ref div rate\n"); + seq_printf(s, "--------------------------------------------------------------\n"); spin_lock_irqsave(&clock_lock, flags); list_for_each_entry(c, &clocks, node) if (c->parent == NULL) diff --git a/arch/arm/mach-tegra/clock.h b/arch/arm/mach-tegra/clock.h index af7c70e2a3ba..94fd859770f1 100644 --- a/arch/arm/mach-tegra/clock.h +++ b/arch/arm/mach-tegra/clock.h @@ -27,18 +27,43 @@ #define DIV_U71 (1 << 1) #define DIV_U71_FIXED (1 << 2) #define DIV_2 (1 << 3) -#define PLL_FIXED (1 << 4) -#define PLL_HAS_CPCON (1 << 5) -#define MUX (1 << 6) -#define PLLD (1 << 7) -#define PERIPH_NO_RESET (1 << 8) -#define PERIPH_NO_ENB (1 << 9) -#define PERIPH_EMC_ENB (1 << 10) -#define PERIPH_MANUAL_RESET (1 << 11) -#define PLL_ALT_MISC_REG (1 << 12) +#define DIV_U16 (1 << 4) +#define PLL_FIXED (1 << 5) +#define PLL_HAS_CPCON (1 << 6) +#define MUX (1 << 7) +#define PLLD (1 << 8) +#define PERIPH_NO_RESET (1 << 9) +#define PERIPH_NO_ENB (1 << 10) +#define PERIPH_EMC_ENB (1 << 11) +#define PERIPH_MANUAL_RESET (1 << 12) +#define PLL_ALT_MISC_REG (1 << 13) +#define PLLU (1 << 14) #define ENABLE_ON_INIT (1 << 28) struct clk; +struct regulator; + +struct dvfs_table { + unsigned long rate; + int millivolts; +}; + +struct dvfs_process_id_table { + int process_id; + struct dvfs_table *table; +}; + + +struct dvfs { + struct regulator *reg; + struct dvfs_table *table; + int max_millivolts; + + int process_id_table_length; + const char *reg_id; + bool cpu; + struct dvfs_process_id_table process_id_table[]; +}; struct clk_mux_sel { struct clk *input; @@ -58,12 +83,9 @@ struct clk_ops { void (*init)(struct clk *); int (*enable)(struct clk *); void (*disable)(struct clk *); - void (*recalc)(struct clk *); int (*set_parent)(struct clk *, struct clk *); int (*set_rate)(struct clk *, unsigned long); - unsigned long (*get_rate)(struct clk *); long (*round_rate)(struct clk *, unsigned long); - unsigned long (*recalculate_rate)(struct clk *); }; enum clk_state { @@ -85,6 +107,7 @@ struct clk { struct clk *parent; struct clk_lookup lookup; unsigned long rate; + unsigned long max_rate; u32 flags; u32 refcnt; const char *name; @@ -103,10 +126,6 @@ struct clk { unsigned long cf_max; unsigned long vco_min; unsigned long vco_max; - u32 m; - u32 n; - u32 p; - u32 cpcon; const struct clk_pll_table *pll_table; /* DIV */ @@ -117,6 +136,12 @@ struct clk { const struct clk_mux_sel *inputs; u32 sel; u32 reg_mask; + + /* Virtual cpu clock */ + struct clk *main; + struct clk *backup; + + struct dvfs *dvfs; }; @@ -141,6 +166,7 @@ unsigned long clk_measure_input_freq(void); void clk_disable_locked(struct clk *c); int clk_enable_locked(struct clk *c); int clk_set_parent_locked(struct clk *c, struct clk *parent); +int clk_set_rate_locked(struct clk *c, unsigned long rate); int clk_reparent(struct clk *c, struct clk *parent); void tegra_clk_init_from_table(struct tegra_clk_init_table *table); diff --git a/arch/arm/mach-tegra/include/mach/clk.h b/arch/arm/mach-tegra/include/mach/clk.h index 2896f25ebfb5..d7723955dac7 100644 --- a/arch/arm/mach-tegra/include/mach/clk.h +++ b/arch/arm/mach-tegra/include/mach/clk.h @@ -23,4 +23,9 @@ void tegra_periph_reset_deassert(struct clk *c); void tegra_periph_reset_assert(struct clk *c); +int clk_enable_cansleep(struct clk *clk); +void clk_disable_cansleep(struct clk *clk); +int clk_set_rate_cansleep(struct clk *clk, unsigned long rate); +int clk_set_parent_cansleep(struct clk *clk, struct clk *parent); + #endif diff --git a/arch/arm/mach-tegra/tegra2_clocks.c b/arch/arm/mach-tegra/tegra2_clocks.c index 426163231fff..cf1c9d0ef7e1 100644 --- a/arch/arm/mach-tegra/tegra2_clocks.c +++ b/arch/arm/mach-tegra/tegra2_clocks.c @@ -30,14 +30,21 @@ #include #include "clock.h" +#include "fuse.h" +#include "tegra2_dvfs.h" #define RST_DEVICES 0x004 #define RST_DEVICES_SET 0x300 #define RST_DEVICES_CLR 0x304 +#define RST_DEVICES_NUM 3 #define CLK_OUT_ENB 0x010 #define CLK_OUT_ENB_SET 0x320 #define CLK_OUT_ENB_CLR 0x324 +#define CLK_OUT_ENB_NUM 3 + +#define CLK_MASK_ARM 0x44 +#define MISC_CLK_ENB 0x48 #define OSC_CTRL 0x50 #define OSC_CTRL_OSC_FREQ_MASK (3<<30) @@ -45,6 +52,7 @@ #define OSC_CTRL_OSC_FREQ_19_2MHZ (1<<30) #define OSC_CTRL_OSC_FREQ_12MHZ (2<<30) #define OSC_CTRL_OSC_FREQ_26MHZ (3<<30) +#define OSC_CTRL_MASK 0x3f2 #define OSC_FREQ_DET 0x58 #define OSC_FREQ_DET_TRIG (1<<31) @@ -53,10 +61,17 @@ #define OSC_FREQ_DET_BUSY (1<<31) #define OSC_FREQ_DET_CNT_MASK 0xFFFF +#define PERIPH_CLK_SOURCE_I2S1 0x100 +#define PERIPH_CLK_SOURCE_EMC 0x19c +#define PERIPH_CLK_SOURCE_OSC 0x1fc +#define PERIPH_CLK_SOURCE_NUM \ + ((PERIPH_CLK_SOURCE_OSC - PERIPH_CLK_SOURCE_I2S1) / 4) + #define PERIPH_CLK_SOURCE_MASK (3<<30) #define PERIPH_CLK_SOURCE_SHIFT 30 #define PERIPH_CLK_SOURCE_ENABLE (1<<28) -#define PERIPH_CLK_SOURCE_DIV_MASK 0xFF +#define PERIPH_CLK_SOURCE_DIVU71_MASK 0xFF +#define PERIPH_CLK_SOURCE_DIVU16_MASK 0xFFFF #define PERIPH_CLK_SOURCE_DIV_SHIFT 0 #define PLL_BASE 0x0 @@ -79,8 +94,9 @@ #define PLL_OUT_RESET_DISABLE (1<<0) #define PLL_MISC(c) (((c)->flags & PLL_ALT_MISC_REG) ? 0x4 : 0xc) +#define PLL_MISC_LOCK_ENABLE(c) (((c)->flags & PLLU) ? (1<<22) : (1<<18)) + #define PLL_MISC_DCCON_SHIFT 20 -#define PLL_MISC_LOCK_ENABLE (1<<18) #define PLL_MISC_CPCON_SHIFT 8 #define PLL_MISC_CPCON_MASK (0xF<rate * 2, rate); + if (divider_u71 - 2 < 0) + return 0; - if (divider_u71 - 2 > 255 || divider_u71 - 2 < 0) + if (divider_u71 - 2 > 255) return -EINVAL; return divider_u71 - 2; } -static unsigned long tegra2_clk_recalculate_rate(struct clk *c) +static int clk_div16_get_divider(unsigned long parent_rate, unsigned long rate) { - unsigned long rate; - rate = c->parent->rate; + s64 divider_u16; - if (c->mul != 0 && c->div != 0) - c->rate = rate * c->mul / c->div; - else - c->rate = rate; - return c->rate; -} + divider_u16 = parent_rate; + divider_u16 += rate - 1; + do_div(divider_u16, rate); + if (divider_u16 - 1 < 0) + return 0; + + if (divider_u16 - 1 > 255) + return -EINVAL; + + return divider_u16 - 1; +} /* clk_m functions */ static unsigned long tegra2_clk_m_autodetect_rate(struct clk *c) @@ -244,7 +269,6 @@ static void tegra2_super_clk_init(struct clk *c) } BUG_ON(sel->input == NULL); c->parent = sel->input; - tegra2_clk_recalculate_rate(c); } static int tegra2_super_clk_enable(struct clk *c) @@ -266,6 +290,7 @@ static int tegra2_super_clk_set_parent(struct clk *c, struct clk *p) u32 val; const struct clk_mux_sel *sel; int shift; + val = clk_readl(c->reg + SUPER_CLK_MUX);; BUG_ON(((val & SUPER_STATE_MASK) != SUPER_STATE_RUN) && ((val & SUPER_STATE_MASK) != SUPER_STATE_IDLE)); @@ -273,11 +298,18 @@ static int tegra2_super_clk_set_parent(struct clk *c, struct clk *p) SUPER_IDLE_SOURCE_SHIFT : SUPER_RUN_SOURCE_SHIFT; for (sel = c->inputs; sel->input != NULL; sel++) { if (sel->input == p) { - clk_reparent(c, p); val &= ~(SUPER_SOURCE_MASK << shift); val |= sel->value << shift; + + if (c->refcnt) + clk_enable_locked(p); + clk_writel(val, c->reg); - c->rate = c->parent->rate; + + if (c->refcnt && c->parent) + clk_disable_locked(c->parent); + + clk_reparent(c, p); return 0; } } @@ -289,7 +321,61 @@ static struct clk_ops tegra_super_ops = { .enable = tegra2_super_clk_enable, .disable = tegra2_super_clk_disable, .set_parent = tegra2_super_clk_set_parent, - .recalculate_rate = tegra2_clk_recalculate_rate, +}; + +/* virtual cpu clock functions */ +/* some clocks can not be stopped (cpu, memory bus) while the SoC is running. + To change the frequency of these clocks, the parent pll may need to be + reprogrammed, so the clock must be moved off the pll, the pll reprogrammed, + and then the clock moved back to the pll. To hide this sequence, a virtual + clock handles it. + */ +static void tegra2_cpu_clk_init(struct clk *c) +{ +} + +static int tegra2_cpu_clk_enable(struct clk *c) +{ + return 0; +} + +static void tegra2_cpu_clk_disable(struct clk *c) +{ + pr_debug("%s on clock %s\n", __func__, c->name); + + /* oops - don't disable the CPU clock! */ + BUG(); +} + +static int tegra2_cpu_clk_set_rate(struct clk *c, unsigned long rate) +{ + int ret; + ret = clk_set_parent_locked(c->parent, c->backup); + if (ret) { + pr_err("Failed to switch cpu to clock %s\n", c->backup->name); + return ret; + } + + ret = clk_set_rate_locked(c->main, rate); + if (ret) { + pr_err("Failed to change cpu pll to %lu\n", rate); + return ret; + } + + ret = clk_set_parent_locked(c->parent, c->main); + if (ret) { + pr_err("Failed to switch cpu to clock %s\n", c->main->name); + return ret; + } + + return 0; +} + +static struct clk_ops tegra_cpu_ops = { + .init = tegra2_cpu_clk_init, + .enable = tegra2_cpu_clk_enable, + .disable = tegra2_cpu_clk_disable, + .set_rate = tegra2_cpu_clk_set_rate, }; /* bus clock functions */ @@ -299,7 +385,6 @@ static void tegra2_bus_clk_init(struct clk *c) c->state = ((val >> c->reg_shift) & BUS_CLK_DISABLE) ? OFF : ON; c->div = ((val >> c->reg_shift) & BUS_CLK_DIV_MASK) + 1; c->mul = 1; - tegra2_clk_recalculate_rate(c); } static int tegra2_bus_clk_enable(struct clk *c) @@ -340,27 +425,15 @@ static struct clk_ops tegra_bus_ops = { .enable = tegra2_bus_clk_enable, .disable = tegra2_bus_clk_disable, .set_rate = tegra2_bus_clk_set_rate, - .recalculate_rate = tegra2_clk_recalculate_rate, }; /* PLL Functions */ -static unsigned long tegra2_pll_clk_recalculate_rate(struct clk *c) -{ - u64 rate; - rate = c->parent->rate; - rate *= c->n; - do_div(rate, c->m); - if (c->p == 2) - rate >>= 1; - c->rate = rate; - return c->rate; -} - static int tegra2_pll_clk_wait_for_lock(struct clk *c) { ktime_t before; before = ktime_get(); + while (!(clk_readl(c->reg + PLL_BASE) & PLL_BASE_LOCK)) { if (ktime_us_delta(ktime_get(), before) > 5000) { pr_err("Timed out waiting for lock bit on pll %s", @@ -380,24 +453,19 @@ static void tegra2_pll_clk_init(struct clk *c) if (c->flags & PLL_FIXED && !(val & PLL_BASE_OVERRIDE)) { pr_warning("Clock %s has unknown fixed frequency\n", c->name); - c->n = 1; - c->m = 0; - c->p = 1; + c->mul = 1; + c->div = 1; } else if (val & PLL_BASE_BYPASS) { - c->n = 1; - c->m = 1; - c->p = 1; + c->mul = 1; + c->div = 1; } else { - c->n = (val & PLL_BASE_DIVN_MASK) >> PLL_BASE_DIVN_SHIFT; - c->m = (val & PLL_BASE_DIVM_MASK) >> PLL_BASE_DIVM_SHIFT; - c->p = (val & PLL_BASE_DIVP_MASK) ? 2 : 1; + c->mul = (val & PLL_BASE_DIVN_MASK) >> PLL_BASE_DIVN_SHIFT; + c->div = (val & PLL_BASE_DIVM_MASK) >> PLL_BASE_DIVM_SHIFT; + if (c->flags & PLLU) + c->div *= (val & PLLU_BASE_POST_DIV) ? 1 : 2; + else + c->div *= (val & PLL_BASE_DIVP_MASK) ? 2 : 1; } - - val = clk_readl(c->reg + PLL_MISC(c)); - if (c->flags & PLL_HAS_CPCON) - c->cpcon = (val & PLL_MISC_CPCON_MASK) >> PLL_MISC_CPCON_SHIFT; - - tegra2_pll_clk_recalculate_rate(c); } static int tegra2_pll_clk_enable(struct clk *c) @@ -411,7 +479,7 @@ static int tegra2_pll_clk_enable(struct clk *c) clk_writel(val, c->reg + PLL_BASE); val = clk_readl(c->reg + PLL_MISC(c)); - val |= PLL_MISC_LOCK_ENABLE; + val |= PLL_MISC_LOCK_ENABLE(c); clk_writel(val, c->reg + PLL_MISC(c)); tegra2_pll_clk_wait_for_lock(c); @@ -441,33 +509,36 @@ static int tegra2_pll_clk_set_rate(struct clk *c, unsigned long rate) input_rate = c->parent->rate; for (sel = c->pll_table; sel->input_rate != 0; sel++) { if (sel->input_rate == input_rate && sel->output_rate == rate) { - c->n = sel->n; - c->m = sel->m; - c->p = sel->p; - c->cpcon = sel->cpcon; + c->mul = sel->n; + c->div = sel->m * sel->p; val = clk_readl(c->reg + PLL_BASE); if (c->flags & PLL_FIXED) val |= PLL_BASE_OVERRIDE; val &= ~(PLL_BASE_DIVP_MASK | PLL_BASE_DIVN_MASK | PLL_BASE_DIVM_MASK); - val |= (c->m << PLL_BASE_DIVM_SHIFT) | - (c->n << PLL_BASE_DIVN_SHIFT); - BUG_ON(c->p > 2); - if (c->p == 2) - val |= 1 << PLL_BASE_DIVP_SHIFT; + val |= (sel->m << PLL_BASE_DIVM_SHIFT) | + (sel->n << PLL_BASE_DIVN_SHIFT); + BUG_ON(sel->p < 1 || sel->p > 2); + if (c->flags & PLLU) { + if (sel->p == 1) + val |= PLLU_BASE_POST_DIV; + } else { + if (sel->p == 2) + val |= 1 << PLL_BASE_DIVP_SHIFT; + } clk_writel(val, c->reg + PLL_BASE); if (c->flags & PLL_HAS_CPCON) { - val = c->cpcon << PLL_MISC_CPCON_SHIFT; - val |= PLL_MISC_LOCK_ENABLE; + val = clk_readl(c->reg + PLL_MISC(c)); + val &= ~PLL_MISC_CPCON_MASK; + val |= sel->cpcon << PLL_MISC_CPCON_SHIFT; clk_writel(val, c->reg + PLL_MISC(c)); } if (c->state == ON) tegra2_pll_clk_enable(c); - c->rate = rate; return 0; } } @@ -479,7 +550,21 @@ static struct clk_ops tegra_pll_ops = { .enable = tegra2_pll_clk_enable, .disable = tegra2_pll_clk_disable, .set_rate = tegra2_pll_clk_set_rate, - .recalculate_rate = tegra2_pll_clk_recalculate_rate, +}; + +static void tegra2_pllx_clk_init(struct clk *c) +{ + tegra2_pll_clk_init(c); + + if (tegra_sku_id() == 7) + c->max_rate = 750000000; +} + +static struct clk_ops tegra_pllx_ops = { + .init = tegra2_pllx_clk_init, + .enable = tegra2_pll_clk_enable, + .disable = tegra2_pll_clk_disable, + .set_rate = tegra2_pll_clk_set_rate, }; /* Clock divider ops */ @@ -503,8 +588,6 @@ static void tegra2_pll_div_clk_init(struct clk *c) c->div = 1; c->mul = 1; } - - tegra2_clk_recalculate_rate(c); } static int tegra2_pll_div_clk_enable(struct clk *c) @@ -565,7 +648,7 @@ static int tegra2_pll_div_clk_set_rate(struct clk *c, unsigned long rate) int divider_u71; pr_debug("%s: %s %lu\n", __func__, c->name, rate); if (c->flags & DIV_U71) { - divider_u71 = clk_div71_get_divider(c->parent, rate); + divider_u71 = clk_div71_get_divider(c->parent->rate, rate); if (divider_u71 >= 0) { val = clk_readl(c->reg); new_val = val >> c->reg_shift; @@ -580,25 +663,37 @@ static int tegra2_pll_div_clk_set_rate(struct clk *c, unsigned long rate) clk_writel(val, c->reg); c->div = divider_u71 + 2; c->mul = 2; - tegra2_clk_recalculate_rate(c); return 0; } } else if (c->flags & DIV_2) { - if (c->parent->rate == rate * 2) { - c->rate = rate; + if (c->parent->rate == rate * 2) return 0; - } } return -EINVAL; } +static long tegra2_pll_div_clk_round_rate(struct clk *c, unsigned long rate) +{ + int divider; + pr_debug("%s: %s %lu\n", __func__, c->name, rate); + + if (c->flags & DIV_U71) { + divider = clk_div71_get_divider(c->parent->rate, rate); + if (divider < 0) + return divider; + return c->parent->rate * 2 / (divider + 2); + } else if (c->flags & DIV_2) { + return c->parent->rate / 2; + } + return -EINVAL; +} static struct clk_ops tegra_pll_div_ops = { .init = tegra2_pll_div_clk_init, .enable = tegra2_pll_div_clk_enable, .disable = tegra2_pll_div_clk_disable, .set_rate = tegra2_pll_div_clk_set_rate, - .recalculate_rate = tegra2_clk_recalculate_rate, + .round_rate = tegra2_pll_div_clk_round_rate, }; /* Periph clk ops */ @@ -621,9 +716,13 @@ static void tegra2_periph_clk_init(struct clk *c) } if (c->flags & DIV_U71) { - u32 divu71 = val & PERIPH_CLK_SOURCE_DIV_MASK; + u32 divu71 = val & PERIPH_CLK_SOURCE_DIVU71_MASK; c->div = divu71 + 2; c->mul = 2; + } else if (c->flags & DIV_U16) { + u32 divu16 = val & PERIPH_CLK_SOURCE_DIVU16_MASK; + c->div = divu16 + 1; + c->mul = 1; } else { c->div = 1; c->mul = 1; @@ -637,7 +736,6 @@ static void tegra2_periph_clk_init(struct clk *c) if (clk_readl(RST_DEVICES + PERIPH_CLK_TO_ENB_REG(c)) & PERIPH_CLK_TO_ENB_BIT(c)) c->state = OFF; - tegra2_clk_recalculate_rate(c); } static int tegra2_periph_clk_enable(struct clk *c) @@ -692,12 +790,19 @@ static int tegra2_periph_clk_set_parent(struct clk *c, struct clk *p) pr_debug("%s: %s %s\n", __func__, c->name, p->name); for (sel = c->inputs; sel->input != NULL; sel++) { if (sel->input == p) { - clk_reparent(c, p); val = clk_readl(c->reg); val &= ~PERIPH_CLK_SOURCE_MASK; val |= (sel->value) << PERIPH_CLK_SOURCE_SHIFT; + + if (c->refcnt) + clk_enable_locked(p); + clk_writel(val, c->reg); - c->rate = c->parent->rate; + + if (c->refcnt && c->parent) + clk_disable_locked(c->parent); + + clk_reparent(c, p); return 0; } } @@ -708,20 +813,55 @@ static int tegra2_periph_clk_set_parent(struct clk *c, struct clk *p) static int tegra2_periph_clk_set_rate(struct clk *c, unsigned long rate) { u32 val; - int divider_u71; + int divider; pr_debug("%s: %lu\n", __func__, rate); if (c->flags & DIV_U71) { - divider_u71 = clk_div71_get_divider(c->parent, rate); - if (divider_u71 >= 0) { + divider = clk_div71_get_divider(c->parent->rate, rate); + if (divider >= 0) { val = clk_readl(c->reg); - val &= ~PERIPH_CLK_SOURCE_DIV_MASK; - val |= divider_u71; + val &= ~PERIPH_CLK_SOURCE_DIVU71_MASK; + val |= divider; clk_writel(val, c->reg); - c->div = divider_u71 + 2; + c->div = divider + 2; c->mul = 2; - tegra2_clk_recalculate_rate(c); return 0; } + } else if (c->flags & DIV_U16) { + divider = clk_div16_get_divider(c->parent->rate, rate); + if (divider >= 0) { + val = clk_readl(c->reg); + val &= ~PERIPH_CLK_SOURCE_DIVU16_MASK; + val |= divider; + clk_writel(val, c->reg); + c->div = divider + 1; + c->mul = 1; + return 0; + } + } else if (c->parent->rate <= rate) { + c->div = 1; + c->mul = 1; + return 0; + } + return -EINVAL; +} + +static long tegra2_periph_clk_round_rate(struct clk *c, + unsigned long rate) +{ + int divider; + pr_debug("%s: %s %lu\n", __func__, c->name, rate); + + if (c->flags & DIV_U71) { + divider = clk_div71_get_divider(c->parent->rate, rate); + if (divider < 0) + return divider; + + return c->parent->rate * 2 / (divider + 2); + } else if (c->flags & DIV_U16) { + divider = clk_div16_get_divider(c->parent->rate, rate); + if (divider < 0) + return divider; + return c->parent->rate / (divider + 1); } return -EINVAL; } @@ -732,7 +872,7 @@ static struct clk_ops tegra_periph_clk_ops = { .disable = &tegra2_periph_clk_disable, .set_parent = &tegra2_periph_clk_set_parent, .set_rate = &tegra2_periph_clk_set_rate, - .recalculate_rate = &tegra2_clk_recalculate_rate, + .round_rate = &tegra2_periph_clk_round_rate, }; /* Clock doubler ops */ @@ -744,21 +884,108 @@ static void tegra2_clk_double_init(struct clk *c) if (!(clk_readl(CLK_OUT_ENB + PERIPH_CLK_TO_ENB_REG(c)) & PERIPH_CLK_TO_ENB_BIT(c))) c->state = OFF; - tegra2_clk_recalculate_rate(c); }; +static int tegra2_clk_double_set_rate(struct clk *c, unsigned long rate) +{ + if (rate != 2 * c->parent->rate) + return -EINVAL; + c->mul = 2; + c->div = 1; + return 0; +} + static struct clk_ops tegra_clk_double_ops = { .init = &tegra2_clk_double_init, .enable = &tegra2_periph_clk_enable, .disable = &tegra2_periph_clk_disable, - .recalculate_rate = &tegra2_clk_recalculate_rate, + .set_rate = &tegra2_clk_double_set_rate, +}; + +static void tegra2_audio_sync_clk_init(struct clk *c) +{ + int source; + const struct clk_mux_sel *sel; + u32 val = clk_readl(c->reg); + c->state = (val & (1<<4)) ? OFF : ON; + source = val & 0xf; + for (sel = c->inputs; sel->input != NULL; sel++) + if (sel->value == source) + break; + BUG_ON(sel->input == NULL); + c->parent = sel->input; +} + +static int tegra2_audio_sync_clk_enable(struct clk *c) +{ + clk_writel(0, c->reg); + return 0; +} + +static void tegra2_audio_sync_clk_disable(struct clk *c) +{ + clk_writel(1, c->reg); +} + +static int tegra2_audio_sync_clk_set_parent(struct clk *c, struct clk *p) +{ + u32 val; + const struct clk_mux_sel *sel; + for (sel = c->inputs; sel->input != NULL; sel++) { + if (sel->input == p) { + val = clk_readl(c->reg); + val &= ~0xf; + val |= sel->value; + + if (c->refcnt) + clk_enable_locked(p); + + clk_writel(val, c->reg); + + if (c->refcnt && c->parent) + clk_disable_locked(c->parent); + + clk_reparent(c, p); + return 0; + } + } + + return -EINVAL; +} + +static int tegra2_audio_sync_clk_set_rate(struct clk *c, unsigned long rate) +{ + unsigned long parent_rate; + if (!c->parent) { + pr_err("%s: clock has no parent\n", __func__); + return -EINVAL; + } + parent_rate = c->parent->rate; + if (rate != parent_rate) { + pr_err("%s: %s/%ld differs from parent %s/%ld\n", + __func__, + c->name, rate, + c->parent->name, parent_rate); + return -EINVAL; + } + c->rate = parent_rate; + return 0; +} + +static struct clk_ops tegra_audio_sync_clk_ops = { + .init = tegra2_audio_sync_clk_init, + .enable = tegra2_audio_sync_clk_enable, + .disable = tegra2_audio_sync_clk_disable, + .set_rate = tegra2_audio_sync_clk_set_rate, + .set_parent = tegra2_audio_sync_clk_set_parent, }; /* Clock definitions */ static struct clk tegra_clk_32k = { .name = "clk_32k", - .rate = 32678, + .rate = 32768, .ops = NULL, + .max_rate = 32768, }; static struct clk_pll_table tegra_pll_s_table[] = { @@ -782,6 +1009,7 @@ static struct clk tegra_pll_s = { .vco_min = 12000000, .vco_max = 26000000, .pll_table = tegra_pll_s_table, + .max_rate = 26000000, }; static struct clk_mux_sel tegra_clk_m_sel[] = { @@ -797,6 +1025,7 @@ static struct clk tegra_clk_m = { .reg = 0x1fc, .reg_mask = (1<<28), .reg_shift = 28, + .max_rate = 26000000, }; static struct clk_pll_table tegra_pll_c_table[] = { @@ -816,6 +1045,7 @@ static struct clk tegra_pll_c = { .vco_min = 20000000, .vco_max = 1400000000, .pll_table = tegra_pll_c_table, + .max_rate = 600000000, }; static struct clk tegra_pll_c_out1 = { @@ -825,9 +1055,18 @@ static struct clk tegra_pll_c_out1 = { .parent = &tegra_pll_c, .reg = 0x84, .reg_shift = 0, + .max_rate = 600000000, }; static struct clk_pll_table tegra_pll_m_table[] = { + { 12000000, 666000000, 666, 12, 1, 8}, + { 13000000, 666000000, 666, 13, 1, 8}, + { 19200000, 666000000, 555, 16, 1, 8}, + { 26000000, 666000000, 666, 26, 1, 8}, + { 12000000, 600000000, 600, 12, 1, 8}, + { 13000000, 600000000, 600, 13, 1, 8}, + { 19200000, 600000000, 375, 12, 1, 6}, + { 26000000, 600000000, 600, 26, 1, 8}, { 0, 0, 0, 0, 0, 0 }, }; @@ -844,6 +1083,7 @@ static struct clk tegra_pll_m = { .vco_min = 20000000, .vco_max = 1200000000, .pll_table = tegra_pll_m_table, + .max_rate = 800000000, }; static struct clk tegra_pll_m_out1 = { @@ -853,6 +1093,7 @@ static struct clk tegra_pll_m_out1 = { .parent = &tegra_pll_m, .reg = 0x94, .reg_shift = 0, + .max_rate = 600000000, }; static struct clk_pll_table tegra_pll_p_table[] = { @@ -880,6 +1121,7 @@ static struct clk tegra_pll_p = { .vco_min = 20000000, .vco_max = 1400000000, .pll_table = tegra_pll_p_table, + .max_rate = 432000000, }; static struct clk tegra_pll_p_out1 = { @@ -889,6 +1131,7 @@ static struct clk tegra_pll_p_out1 = { .parent = &tegra_pll_p, .reg = 0xa4, .reg_shift = 0, + .max_rate = 432000000, }; static struct clk tegra_pll_p_out2 = { @@ -898,6 +1141,7 @@ static struct clk tegra_pll_p_out2 = { .parent = &tegra_pll_p, .reg = 0xa4, .reg_shift = 16, + .max_rate = 432000000, }; static struct clk tegra_pll_p_out3 = { @@ -907,6 +1151,7 @@ static struct clk tegra_pll_p_out3 = { .parent = &tegra_pll_p, .reg = 0xa8, .reg_shift = 0, + .max_rate = 432000000, }; static struct clk tegra_pll_p_out4 = { @@ -916,6 +1161,7 @@ static struct clk tegra_pll_p_out4 = { .parent = &tegra_pll_p, .reg = 0xa8, .reg_shift = 16, + .max_rate = 432000000, }; static struct clk_pll_table tegra_pll_a_table[] = { @@ -923,6 +1169,7 @@ static struct clk_pll_table tegra_pll_a_table[] = { { 28800000, 73728000, 64, 25, 1, 1}, { 28800000, 11289600, 49, 25, 1, 1}, { 28800000, 12288000, 64, 25, 1, 1}, + { 28800000, 24000000, 5, 6, 1, 1}, { 0, 0, 0, 0, 0, 0 }, }; @@ -939,6 +1186,7 @@ static struct clk tegra_pll_a = { .vco_min = 20000000, .vco_max = 1400000000, .pll_table = tegra_pll_a_table, + .max_rate = 56448000, }; static struct clk tegra_pll_a_out0 = { @@ -948,6 +1196,7 @@ static struct clk tegra_pll_a_out0 = { .parent = &tegra_pll_a, .reg = 0xb4, .reg_shift = 0, + .max_rate = 56448000, }; static struct clk_pll_table tegra_pll_d_table[] = { @@ -971,6 +1220,7 @@ static struct clk tegra_pll_d = { .vco_min = 40000000, .vco_max = 1000000000, .pll_table = tegra_pll_d_table, + .max_rate = 1000000000, }; static struct clk tegra_pll_d_out0 = { @@ -978,19 +1228,20 @@ static struct clk tegra_pll_d_out0 = { .ops = &tegra_pll_div_ops, .flags = DIV_2 | PLLD, .parent = &tegra_pll_d, + .max_rate = 500000000, }; static struct clk_pll_table tegra_pll_u_table[] = { - { 12000000, 480000000, 960, 12, 1, 0}, - { 13000000, 480000000, 960, 13, 1, 0}, - { 19200000, 480000000, 200, 4, 1, 0}, - { 26000000, 480000000, 960, 26, 1, 0}, + { 12000000, 480000000, 960, 12, 2, 0}, + { 13000000, 480000000, 960, 13, 2, 0}, + { 19200000, 480000000, 200, 4, 2, 0}, + { 26000000, 480000000, 960, 26, 2, 0}, { 0, 0, 0, 0, 0, 0 }, }; static struct clk tegra_pll_u = { .name = "pll_u", - .flags = 0, + .flags = PLLU, .ops = &tegra_pll_ops, .reg = 0xc0, .input_min = 2000000, @@ -1001,24 +1252,59 @@ static struct clk tegra_pll_u = { .vco_min = 480000000, .vco_max = 960000000, .pll_table = tegra_pll_u_table, + .max_rate = 480000000, }; static struct clk_pll_table tegra_pll_x_table[] = { + /* 1 GHz */ { 12000000, 1000000000, 1000, 12, 1, 12}, { 13000000, 1000000000, 1000, 13, 1, 12}, { 19200000, 1000000000, 625, 12, 1, 8}, { 26000000, 1000000000, 1000, 26, 1, 12}, - { 12000000, 750000000, 750, 12, 1, 12}, - { 13000000, 750000000, 750, 13, 1, 12}, - { 19200000, 750000000, 625, 16, 1, 8}, - { 26000000, 750000000, 750, 26, 1, 12}, + + /* 912 MHz */ + { 12000000, 912000000, 912, 12, 1, 12}, + { 13000000, 912000000, 912, 13, 1, 12}, + { 19200000, 912000000, 760, 16, 1, 8}, + { 26000000, 912000000, 912, 26, 1, 12}, + + /* 816 MHz */ + { 12000000, 816000000, 816, 12, 1, 12}, + { 13000000, 816000000, 816, 13, 1, 12}, + { 19200000, 816000000, 680, 16, 1, 8}, + { 26000000, 816000000, 816, 26, 1, 12}, + + /* 760 MHz */ + { 12000000, 760000000, 760, 12, 1, 12}, + { 13000000, 760000000, 760, 13, 1, 12}, + { 19200000, 760000000, 950, 24, 1, 8}, + { 26000000, 760000000, 760, 26, 1, 12}, + + /* 608 MHz */ + { 12000000, 608000000, 760, 12, 1, 12}, + { 13000000, 608000000, 760, 13, 1, 12}, + { 19200000, 608000000, 380, 12, 1, 8}, + { 26000000, 608000000, 760, 26, 1, 12}, + + /* 456 MHz */ + { 12000000, 456000000, 456, 12, 1, 12}, + { 13000000, 456000000, 456, 13, 1, 12}, + { 19200000, 456000000, 380, 16, 1, 8}, + { 26000000, 456000000, 456, 26, 1, 12}, + + /* 312 MHz */ + { 12000000, 312000000, 312, 12, 1, 12}, + { 13000000, 312000000, 312, 13, 1, 12}, + { 19200000, 312000000, 260, 16, 1, 8}, + { 26000000, 312000000, 312, 26, 1, 12}, + { 0, 0, 0, 0, 0, 0 }, }; static struct clk tegra_pll_x = { .name = "pll_x", .flags = PLL_HAS_CPCON | PLL_ALT_MISC_REG, - .ops = &tegra_pll_ops, + .ops = &tegra_pllx_ops, .reg = 0xe0, .input_min = 2000000, .input_max = 31000000, @@ -1028,6 +1314,7 @@ static struct clk tegra_pll_x = { .vco_min = 20000000, .vco_max = 1200000000, .pll_table = tegra_pll_x_table, + .max_rate = 1000000000, }; static struct clk tegra_clk_d = { @@ -1038,19 +1325,77 @@ static struct clk tegra_clk_d = { .reg = 0x34, .reg_shift = 12, .parent = &tegra_clk_m, + .max_rate = 52000000, +}; + +/* initialized before peripheral clocks */ +static struct clk_mux_sel mux_audio_sync_clk[8+1]; +static const struct audio_sources { + const char *name; + int value; +} mux_audio_sync_clk_sources[] = { + { .name = "spdif_in", .value = 0 }, + { .name = "i2s1", .value = 1 }, + { .name = "i2s2", .value = 2 }, + { .name = "pll_a_out0", .value = 4 }, +#if 0 /* FIXME: not implemented */ + { .name = "ac97", .value = 3 }, + { .name = "ext_audio_clk2", .value = 5 }, + { .name = "ext_audio_clk1", .value = 6 }, + { .name = "ext_vimclk", .value = 7 }, +#endif + { 0, 0 } +}; + +static struct clk tegra_clk_audio = { + .name = "audio", + .inputs = mux_audio_sync_clk, + .reg = 0x38, + .max_rate = 24000000, + .ops = &tegra_audio_sync_clk_ops }; -/* FIXME: need tegra_audio static struct clk tegra_clk_audio_2x = { - .name = "clk_d", + .name = "audio_2x", .flags = PERIPH_NO_RESET, + .max_rate = 48000000, .ops = &tegra_clk_double_ops, .clk_num = 89, .reg = 0x34, .reg_shift = 8, - .parent = &tegra_audio, + .parent = &tegra_clk_audio, +}; + +struct clk_lookup tegra_audio_clk_lookups[] = { + { .con_id = "audio", .clk = &tegra_clk_audio }, + { .con_id = "audio_2x", .clk = &tegra_clk_audio_2x } +}; + +/* This is called after peripheral clocks are initialized, as the + * audio_sync clock depends on some of the peripheral clocks. + */ + +static void init_audio_sync_clock_mux(void) +{ + int i; + struct clk_mux_sel *sel = mux_audio_sync_clk; + const struct audio_sources *src = mux_audio_sync_clk_sources; + struct clk_lookup *lookup; + + for (i = 0; src->name; i++, sel++, src++) { + sel->input = tegra_get_clock_by_name(src->name); + if (!sel->input) + pr_err("%s: could not find clk %s\n", __func__, + src->name); + sel->value = src->value; + } + + lookup = tegra_audio_clk_lookups; + for (i = 0; i < ARRAY_SIZE(tegra_audio_clk_lookups); i++, lookup++) { + clk_init(lookup->clk); + clkdev_add(lookup); + } } -*/ static struct clk_mux_sel mux_cclk[] = { { .input = &tegra_clk_m, .value = 0}, @@ -1077,27 +1422,40 @@ static struct clk_mux_sel mux_sclk[] = { { 0, 0}, }; -static struct clk tegra_clk_cpu = { - .name = "cpu", +static struct clk tegra_clk_cclk = { + .name = "cclk", .inputs = mux_cclk, .reg = 0x20, .ops = &tegra_super_ops, + .max_rate = 1000000000, }; -static struct clk tegra_clk_sys = { - .name = "sys", +static struct clk tegra_clk_sclk = { + .name = "sclk", .inputs = mux_sclk, .reg = 0x28, .ops = &tegra_super_ops, + .max_rate = 600000000, +}; + +static struct clk tegra_clk_virtual_cpu = { + .name = "cpu", + .parent = &tegra_clk_cclk, + .main = &tegra_pll_x, + .backup = &tegra_clk_m, + .ops = &tegra_cpu_ops, + .max_rate = 1000000000, + .dvfs = &tegra_dvfs_virtual_cpu_dvfs, }; static struct clk tegra_clk_hclk = { .name = "hclk", .flags = DIV_BUS, - .parent = &tegra_clk_sys, + .parent = &tegra_clk_sclk, .reg = 0x30, .reg_shift = 4, .ops = &tegra_bus_ops, + .max_rate = 240000000, }; static struct clk tegra_clk_pclk = { @@ -1107,6 +1465,7 @@ static struct clk tegra_clk_pclk = { .reg = 0x30, .reg_shift = 0, .ops = &tegra_bus_ops, + .max_rate = 108000000, }; static struct clk_mux_sel mux_pllm_pllc_pllp_plla[] = { @@ -1133,10 +1492,9 @@ static struct clk_mux_sel mux_pllp_pllc_pllm_clkm[] = { { 0, 0}, }; -static struct clk_mux_sel mux_plla_audio_pllp_clkm[] = { - {.input = &tegra_pll_a, .value = 0}, - /* FIXME: no mux defined for tegra_audio - {.input = &tegra_audio, .value = 1},*/ +static struct clk_mux_sel mux_pllaout0_audio2x_pllp_clkm[] = { + {.input = &tegra_pll_a_out0, .value = 0}, + {.input = &tegra_clk_audio_2x, .value = 1}, {.input = &tegra_pll_p, .value = 2}, {.input = &tegra_clk_m, .value = 3}, { 0, 0}, @@ -1153,8 +1511,7 @@ static struct clk_mux_sel mux_pllp_plld_pllc_clkm[] = { static struct clk_mux_sel mux_pllp_pllc_audio_clkm_clk32[] = { {.input = &tegra_pll_p, .value = 0}, {.input = &tegra_pll_c, .value = 1}, - /* FIXME: no mux defined for tegra_audio - {.input = &tegra_audio, .value = 2},*/ + {.input = &tegra_clk_audio, .value = 2}, {.input = &tegra_clk_m, .value = 3}, {.input = &tegra_clk_32k, .value = 4}, { 0, 0}, @@ -1187,7 +1544,7 @@ static struct clk_mux_sel mux_clk_32k[] = { { 0, 0}, }; -#define PERIPH_CLK(_name, _dev, _con, _clk_num, _reg, _inputs, _flags) \ +#define PERIPH_CLK(_name, _dev, _con, _clk_num, _reg, _max, _inputs, _flags) \ { \ .name = _name, \ .lookup = { \ @@ -1199,72 +1556,76 @@ static struct clk_mux_sel mux_clk_32k[] = { .reg = _reg, \ .inputs = _inputs, \ .flags = _flags, \ + .max_rate = _max, \ } struct clk tegra_periph_clks[] = { - PERIPH_CLK("rtc", "rtc-tegra", NULL, 4, 0, mux_clk_32k, PERIPH_NO_RESET), - PERIPH_CLK("timer", "timer", NULL, 5, 0, mux_clk_m, 0), - PERIPH_CLK("i2s1", "i2s.0", NULL, 11, 0x100, mux_plla_audio_pllp_clkm, MUX | DIV_U71), - PERIPH_CLK("i2s2", "i2s.1", NULL, 18, 0x104, mux_plla_audio_pllp_clkm, MUX | DIV_U71), + PERIPH_CLK("rtc", "rtc-tegra", NULL, 4, 0, 32768, mux_clk_32k, PERIPH_NO_RESET), + PERIPH_CLK("timer", "timer", NULL, 5, 0, 26000000, mux_clk_m, 0), + PERIPH_CLK("i2s1", "i2s.0", NULL, 11, 0x100, 26000000, mux_pllaout0_audio2x_pllp_clkm, MUX | DIV_U71), + PERIPH_CLK("i2s2", "i2s.1", NULL, 18, 0x104, 26000000, mux_pllaout0_audio2x_pllp_clkm, MUX | DIV_U71), /* FIXME: spdif has 2 clocks but 1 enable */ - PERIPH_CLK("spdif_out", "spdif_out", NULL, 10, 0x108, mux_plla_audio_pllp_clkm, MUX | DIV_U71), - PERIPH_CLK("spdif_in", "spdif_in", NULL, 10, 0x10c, mux_pllp_pllc_pllm, MUX | DIV_U71), - PERIPH_CLK("pwm", "pwm", NULL, 17, 0x110, mux_pllp_pllc_audio_clkm_clk32, MUX | DIV_U71), - PERIPH_CLK("spi", "spi", NULL, 43, 0x114, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), - PERIPH_CLK("xio", "xio", NULL, 45, 0x120, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), - PERIPH_CLK("twc", "twc", NULL, 16, 0x12c, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), - PERIPH_CLK("sbc1", "spi_tegra.0", NULL, 41, 0x134, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), - PERIPH_CLK("sbc2", "spi_tegra.1", NULL, 44, 0x118, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), - PERIPH_CLK("sbc3", "spi_tegra.2", NULL, 46, 0x11c, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), - PERIPH_CLK("sbc4", "spi_tegra.3", NULL, 68, 0x1b4, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), - PERIPH_CLK("ide", "ide", NULL, 25, 0x144, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), - PERIPH_CLK("ndflash", "tegra_nand", NULL, 13, 0x160, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), + PERIPH_CLK("spdif_out", "spdif_out", NULL, 10, 0x108, 100000000, mux_pllaout0_audio2x_pllp_clkm, MUX | DIV_U71), + PERIPH_CLK("spdif_in", "spdif_in", NULL, 10, 0x10c, 100000000, mux_pllp_pllc_pllm, MUX | DIV_U71), + PERIPH_CLK("pwm", "pwm", NULL, 17, 0x110, 432000000, mux_pllp_pllc_audio_clkm_clk32, MUX | DIV_U71), + PERIPH_CLK("spi", "spi", NULL, 43, 0x114, 40000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), + PERIPH_CLK("xio", "xio", NULL, 45, 0x120, 150000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), + PERIPH_CLK("twc", "twc", NULL, 16, 0x12c, 150000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), + PERIPH_CLK("sbc1", "spi_tegra.0", NULL, 41, 0x134, 160000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), + PERIPH_CLK("sbc2", "spi_tegra.1", NULL, 44, 0x118, 160000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), + PERIPH_CLK("sbc3", "spi_tegra.2", NULL, 46, 0x11c, 160000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), + PERIPH_CLK("sbc4", "spi_tegra.3", NULL, 68, 0x1b4, 160000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), + PERIPH_CLK("ide", "ide", NULL, 25, 0x144, 100000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* requires min voltage */ + PERIPH_CLK("ndflash", "tegra_nand", NULL, 13, 0x160, 164000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* scales with voltage */ /* FIXME: vfir shares an enable with uartb */ - PERIPH_CLK("vfir", "vfir", NULL, 7, 0x168, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), - PERIPH_CLK("sdmmc1", "sdhci-tegra.0", NULL, 14, 0x150, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), - PERIPH_CLK("sdmmc2", "sdhci-tegra.1", NULL, 9, 0x154, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), - PERIPH_CLK("sdmmc3", "sdhci-tegra.2", NULL, 69, 0x1bc, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), - PERIPH_CLK("sdmmc4", "sdhci-tegra.3", NULL, 15, 0x160, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), - PERIPH_CLK("vde", "vde", NULL, 61, 0x1c8, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), - PERIPH_CLK("csite", "csite", NULL, 73, 0x1d4, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), + PERIPH_CLK("vfir", "vfir", NULL, 7, 0x168, 72000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), + PERIPH_CLK("sdmmc1", "sdhci-tegra.0", NULL, 14, 0x150, 52000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* scales with voltage */ + PERIPH_CLK("sdmmc2", "sdhci-tegra.1", NULL, 9, 0x154, 52000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* scales with voltage */ + PERIPH_CLK("sdmmc3", "sdhci-tegra.2", NULL, 69, 0x1bc, 52000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* scales with voltage */ + PERIPH_CLK("sdmmc4", "sdhci-tegra.3", NULL, 15, 0x160, 52000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* scales with voltage */ + PERIPH_CLK("vde", "vde", NULL, 61, 0x1c8, 250000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* scales with voltage and process_id */ + PERIPH_CLK("csite", "csite", NULL, 73, 0x1d4, 144000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* max rate ??? */ /* FIXME: what is la? */ - PERIPH_CLK("la", "la", NULL, 76, 0x1f8, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), - PERIPH_CLK("owr", "owr", NULL, 71, 0x1cc, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), - PERIPH_CLK("nor", "nor", NULL, 42, 0x1d0, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), - PERIPH_CLK("mipi", "mipi", NULL, 50, 0x174, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), - PERIPH_CLK("i2c1", "tegra-i2c.0", NULL, 12, 0x124, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), - PERIPH_CLK("i2c2", "tegra-i2c.1", NULL, 54, 0x198, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), - PERIPH_CLK("i2c3", "tegra-i2c.2", NULL, 67, 0x1b8, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), - PERIPH_CLK("dvc", "tegra-i2c.3", NULL, 47, 0x128, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), - PERIPH_CLK("i2c1_i2c", "tegra-i2c.0", "i2c", 0, 0, mux_pllp_out3, 0), - PERIPH_CLK("i2c2_i2c", "tegra-i2c.1", "i2c", 0, 0, mux_pllp_out3, 0), - PERIPH_CLK("i2c3_i2c", "tegra-i2c.2", "i2c", 0, 0, mux_pllp_out3, 0), - PERIPH_CLK("dvc_i2c", "tegra-i2c.3", "i2c", 0, 0, mux_pllp_out3, 0), - PERIPH_CLK("uarta", "uart.0", NULL, 6, 0x178, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), - PERIPH_CLK("uartb", "uart.1", NULL, 7, 0x17c, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), - PERIPH_CLK("uartc", "uart.2", NULL, 55, 0x1a0, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), - PERIPH_CLK("uartd", "uart.3", NULL, 65, 0x1c0, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), - PERIPH_CLK("uarte", "uart.4", NULL, 66, 0x1c4, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), - PERIPH_CLK("3d", "3d", NULL, 24, 0x158, mux_pllm_pllc_pllp_plla, MUX | DIV_U71 | PERIPH_MANUAL_RESET), - PERIPH_CLK("2d", "2d", NULL, 21, 0x15c, mux_pllm_pllc_pllp_plla, MUX | DIV_U71), + PERIPH_CLK("la", "la", NULL, 76, 0x1f8, 26000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), + PERIPH_CLK("owr", "tegra_w1", NULL, 71, 0x1cc, 26000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), + PERIPH_CLK("nor", "nor", NULL, 42, 0x1d0, 92000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* requires min voltage */ + PERIPH_CLK("mipi", "mipi", NULL, 50, 0x174, 60000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* scales with voltage */ + PERIPH_CLK("i2c1", "tegra-i2c.0", NULL, 12, 0x124, 26000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U16), + PERIPH_CLK("i2c2", "tegra-i2c.1", NULL, 54, 0x198, 26000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U16), + PERIPH_CLK("i2c3", "tegra-i2c.2", NULL, 67, 0x1b8, 26000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U16), + PERIPH_CLK("dvc", "tegra-i2c.3", NULL, 47, 0x128, 26000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U16), + PERIPH_CLK("i2c1_i2c", "tegra-i2c.0", "i2c", 0, 0, 72000000, mux_pllp_out3, 0), + PERIPH_CLK("i2c2_i2c", "tegra-i2c.1", "i2c", 0, 0, 72000000, mux_pllp_out3, 0), + PERIPH_CLK("i2c3_i2c", "tegra-i2c.2", "i2c", 0, 0, 72000000, mux_pllp_out3, 0), + PERIPH_CLK("dvc_i2c", "tegra-i2c.3", "i2c", 0, 0, 72000000, mux_pllp_out3, 0), + PERIPH_CLK("uarta", "uart.0", NULL, 6, 0x178, 216000000, mux_pllp_pllc_pllm_clkm, MUX), + PERIPH_CLK("uartb", "uart.1", NULL, 7, 0x17c, 216000000, mux_pllp_pllc_pllm_clkm, MUX), + PERIPH_CLK("uartc", "uart.2", NULL, 55, 0x1a0, 216000000, mux_pllp_pllc_pllm_clkm, MUX), + PERIPH_CLK("uartd", "uart.3", NULL, 65, 0x1c0, 216000000, mux_pllp_pllc_pllm_clkm, MUX), + PERIPH_CLK("uarte", "uart.4", NULL, 66, 0x1c4, 216000000, mux_pllp_pllc_pllm_clkm, MUX), + PERIPH_CLK("3d", "3d", NULL, 24, 0x158, 300000000, mux_pllm_pllc_pllp_plla, MUX | DIV_U71 | PERIPH_MANUAL_RESET), /* scales with voltage and process_id */ + PERIPH_CLK("2d", "2d", NULL, 21, 0x15c, 300000000, mux_pllm_pllc_pllp_plla, MUX | DIV_U71), /* scales with voltage and process_id */ /* FIXME: vi and vi_sensor share an enable */ - PERIPH_CLK("vi", "vi", NULL, 20, 0x148, mux_pllm_pllc_pllp_plla, MUX | DIV_U71), - PERIPH_CLK("vi_sensor", "vi_sensor", NULL, 20, 0x1a8, mux_pllm_pllc_pllp_plla, MUX | DIV_U71), - PERIPH_CLK("epp", "epp", NULL, 19, 0x16c, mux_pllm_pllc_pllp_plla, MUX | DIV_U71), - PERIPH_CLK("mpe", "mpe", NULL, 60, 0x170, mux_pllm_pllc_pllp_plla, MUX | DIV_U71), - PERIPH_CLK("host1x", "host1x", NULL, 28, 0x180, mux_pllm_pllc_pllp_plla, MUX | DIV_U71), + PERIPH_CLK("vi", "vi", NULL, 20, 0x148, 150000000, mux_pllm_pllc_pllp_plla, MUX | DIV_U71), /* scales with voltage and process_id */ + PERIPH_CLK("vi_sensor", "vi_sensor", NULL, 20, 0x1a8, 150000000, mux_pllm_pllc_pllp_plla, MUX | DIV_U71 | PERIPH_NO_RESET), /* scales with voltage and process_id */ + PERIPH_CLK("epp", "epp", NULL, 19, 0x16c, 300000000, mux_pllm_pllc_pllp_plla, MUX | DIV_U71), /* scales with voltage and process_id */ + PERIPH_CLK("mpe", "mpe", NULL, 60, 0x170, 250000000, mux_pllm_pllc_pllp_plla, MUX | DIV_U71), /* scales with voltage and process_id */ + PERIPH_CLK("host1x", "host1x", NULL, 28, 0x180, 166000000, mux_pllm_pllc_pllp_plla, MUX | DIV_U71), /* scales with voltage and process_id */ /* FIXME: cve and tvo share an enable */ - PERIPH_CLK("cve", "cve", NULL, 49, 0x140, mux_pllp_plld_pllc_clkm, MUX | DIV_U71), - PERIPH_CLK("tvo", "tvo", NULL, 49, 0x188, mux_pllp_plld_pllc_clkm, MUX | DIV_U71), - PERIPH_CLK("hdmi", "hdmi", NULL, 51, 0x18c, mux_pllp_plld_pllc_clkm, MUX | DIV_U71), - PERIPH_CLK("tvdac", "tvdac", NULL, 53, 0x194, mux_pllp_plld_pllc_clkm, MUX | DIV_U71), - PERIPH_CLK("disp1", "tegrafb.0", NULL, 27, 0x138, mux_pllp_plld_pllc_clkm, MUX | DIV_U71), - PERIPH_CLK("disp2", "tegrafb.1", NULL, 26, 0x13c, mux_pllp_plld_pllc_clkm, MUX | DIV_U71), - PERIPH_CLK("usbd", "fsl-tegra-udc", NULL, 22, 0, mux_clk_m, 0), - PERIPH_CLK("usb2", "usb.1", NULL, 58, 0, mux_clk_m, 0), - PERIPH_CLK("usb3", "usb.2", NULL, 59, 0, mux_clk_m, 0), - PERIPH_CLK("emc", "emc", NULL, 57, 0x19c, mux_pllm_pllc_pllp_clkm, MUX | DIV_U71 | PERIPH_EMC_ENB), - PERIPH_CLK("dsi", "dsi", NULL, 48, 0, mux_plld, 0), + PERIPH_CLK("cve", "cve", NULL, 49, 0x140, 250000000, mux_pllp_plld_pllc_clkm, MUX | DIV_U71), /* requires min voltage */ + PERIPH_CLK("tvo", "tvo", NULL, 49, 0x188, 250000000, mux_pllp_plld_pllc_clkm, MUX | DIV_U71), /* requires min voltage */ + PERIPH_CLK("hdmi", "hdmi", NULL, 51, 0x18c, 148500000, mux_pllp_plld_pllc_clkm, MUX | DIV_U71), /* requires min voltage */ + PERIPH_CLK("tvdac", "tvdac", NULL, 53, 0x194, 250000000, mux_pllp_plld_pllc_clkm, MUX | DIV_U71), /* requires min voltage */ + PERIPH_CLK("disp1", "tegrafb.0", NULL, 27, 0x138, 190000000, mux_pllp_plld_pllc_clkm, MUX | DIV_U71), /* scales with voltage and process_id */ + PERIPH_CLK("disp2", "tegrafb.1", NULL, 26, 0x13c, 190000000, mux_pllp_plld_pllc_clkm, MUX | DIV_U71), /* scales with voltage and process_id */ + PERIPH_CLK("usbd", "fsl-tegra-udc", NULL, 22, 0, 480000000, mux_clk_m, 0), /* requires min voltage */ + PERIPH_CLK("usb2", "tegra-ehci.1", NULL, 58, 0, 480000000, mux_clk_m, 0), /* requires min voltage */ + PERIPH_CLK("usb3", "tegra-ehci.2", NULL, 59, 0, 480000000, mux_clk_m, 0), /* requires min voltage */ + PERIPH_CLK("emc", "emc", NULL, 57, 0x19c, 800000000, mux_pllm_pllc_pllp_clkm, MUX | DIV_U71 | PERIPH_EMC_ENB), + PERIPH_CLK("dsi", "dsi", NULL, 48, 0, 500000000, mux_plld, 0), /* scales with voltage */ + PERIPH_CLK("csi", "csi", NULL, 52, 0, 72000000, mux_pllp_out3, 0), + PERIPH_CLK("isp", "isp", NULL, 23, 0, 150000000, mux_clk_m, 0), /* same frequency as VI */ + PERIPH_CLK("csus", "csus", NULL, 92, 0, 150000000, mux_clk_m, PERIPH_NO_RESET), }; #define CLK_DUPLICATE(_name, _dev, _con) \ @@ -1286,6 +1647,9 @@ struct clk_duplicate tegra_clk_duplicates[] = { CLK_DUPLICATE("uartc", "tegra_uart.2", NULL), CLK_DUPLICATE("uartd", "tegra_uart.3", NULL), CLK_DUPLICATE("uarte", "tegra_uart.4", NULL), + CLK_DUPLICATE("host1x", "tegrafb.0", "host1x"), + CLK_DUPLICATE("host1x", "tegrafb.1", "host1x"), + CLK_DUPLICATE("usbd", "tegra-ehci.0", NULL), }; #define CLK(dev, con, ck) \ @@ -1315,11 +1679,12 @@ struct clk_lookup tegra_clk_lookups[] = { CLK(NULL, "pll_d_out0", &tegra_pll_d_out0), CLK(NULL, "pll_u", &tegra_pll_u), CLK(NULL, "pll_x", &tegra_pll_x), - CLK(NULL, "cpu", &tegra_clk_cpu), - CLK(NULL, "sys", &tegra_clk_sys), + CLK(NULL, "cclk", &tegra_clk_cclk), + CLK(NULL, "sclk", &tegra_clk_sclk), CLK(NULL, "hclk", &tegra_clk_hclk), CLK(NULL, "pclk", &tegra_clk_pclk), CLK(NULL, "clk_d", &tegra_clk_d), + CLK(NULL, "cpu", &tegra_clk_virtual_cpu), }; void __init tegra2_init_clocks(void) @@ -1356,4 +1721,75 @@ void __init tegra2_init_clocks(void) cd->name); } } + + init_audio_sync_clock_mux(); +} + +#ifdef CONFIG_PM +static u32 clk_rst_suspend[RST_DEVICES_NUM + CLK_OUT_ENB_NUM + + PERIPH_CLK_SOURCE_NUM + 3]; + +void tegra_clk_suspend(void) +{ + unsigned long off, i; + u32 *ctx = clk_rst_suspend; + + *ctx++ = clk_readl(OSC_CTRL) & OSC_CTRL_MASK; + + for (off = PERIPH_CLK_SOURCE_I2S1; off <= PERIPH_CLK_SOURCE_OSC; + off += 4) { + if (off == PERIPH_CLK_SOURCE_EMC) + continue; + *ctx++ = clk_readl(off); + } + + off = RST_DEVICES; + for (i = 0; i < RST_DEVICES_NUM; i++, off += 4) + *ctx++ = clk_readl(off); + + off = CLK_OUT_ENB; + for (i = 0; i < CLK_OUT_ENB_NUM; i++, off += 4) + *ctx++ = clk_readl(off); + + *ctx++ = clk_readl(MISC_CLK_ENB); + *ctx++ = clk_readl(CLK_MASK_ARM); +} + +void tegra_clk_resume(void) +{ + unsigned long off, i; + const u32 *ctx = clk_rst_suspend; + u32 val; + + val = clk_readl(OSC_CTRL) & ~OSC_CTRL_MASK; + val |= *ctx++; + clk_writel(val, OSC_CTRL); + + /* enable all clocks before configuring clock sources */ + clk_writel(0xbffffff9ul, CLK_OUT_ENB); + clk_writel(0xfefffff7ul, CLK_OUT_ENB + 4); + clk_writel(0x77f01bfful, CLK_OUT_ENB + 8); + wmb(); + + for (off = PERIPH_CLK_SOURCE_I2S1; off <= PERIPH_CLK_SOURCE_OSC; + off += 4) { + if (off == PERIPH_CLK_SOURCE_EMC) + continue; + clk_writel(*ctx++, off); + } + wmb(); + + off = RST_DEVICES; + for (i = 0; i < RST_DEVICES_NUM; i++, off += 4) + clk_writel(*ctx++, off); + wmb(); + + off = CLK_OUT_ENB; + for (i = 0; i < CLK_OUT_ENB_NUM; i++, off += 4) + clk_writel(*ctx++, off); + wmb(); + + clk_writel(*ctx++, MISC_CLK_ENB); + clk_writel(*ctx++, CLK_MASK_ARM); } +#endif diff --git a/arch/arm/mach-tegra/tegra2_dvfs.c b/arch/arm/mach-tegra/tegra2_dvfs.c new file mode 100644 index 000000000000..5529c238dd77 --- /dev/null +++ b/arch/arm/mach-tegra/tegra2_dvfs.c @@ -0,0 +1,86 @@ +/* + * arch/arm/mach-tegra/tegra2_dvfs.c + * + * Copyright (C) 2010 Google, Inc. + * + * Author: + * Colin Cross + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that 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. + * + */ + +#include + +#include "clock.h" +#include "tegra2_dvfs.h" + +static struct dvfs_table virtual_cpu_process_0[] = { + {314000000, 750}, + {456000000, 825}, + {608000000, 900}, + {760000000, 975}, + {817000000, 1000}, + {912000000, 1050}, + {1000000000, 1100}, + {0, 0}, +}; + +static struct dvfs_table virtual_cpu_process_1[] = { + {314000000, 750}, + {456000000, 825}, + {618000000, 900}, + {770000000, 975}, + {827000000, 1000}, + {922000000, 1050}, + {1000000000, 1100}, + {0, 0}, +}; + +static struct dvfs_table virtual_cpu_process_2[] = { + {494000000, 750}, + {675000000, 825}, + {817000000, 875}, + {922000000, 925}, + {1000000000, 975}, + {0, 0}, +}; + +static struct dvfs_table virtual_cpu_process_3[] = { + {730000000, 750}, + {760000000, 775}, + {845000000, 800}, + {1000000000, 875}, + {0, 0}, +}; + +struct dvfs tegra_dvfs_virtual_cpu_dvfs = { + .reg_id = "vdd_cpu", + .process_id_table = { + { + .process_id = 0, + .table = virtual_cpu_process_0, + }, + { + .process_id = 1, + .table = virtual_cpu_process_1, + }, + { + .process_id = 2, + .table = virtual_cpu_process_2, + }, + { + .process_id = 3, + .table = virtual_cpu_process_3, + }, + }, + .process_id_table_length = 4, + .cpu = 1, +}; diff --git a/arch/arm/mach-tegra/tegra2_dvfs.h b/arch/arm/mach-tegra/tegra2_dvfs.h new file mode 100644 index 000000000000..f8c1adba96a6 --- /dev/null +++ b/arch/arm/mach-tegra/tegra2_dvfs.h @@ -0,0 +1,20 @@ +/* + * arch/arm/mach-tegra/tegra2_dvfs.h + * + * Copyright (C) 2010 Google, Inc. + * + * Author: + * Colin Cross + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that 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. + * + */ + +extern struct dvfs tegra_dvfs_virtual_cpu_dvfs; -- cgit v1.2.3 From 8486bddc09c84606fb22cf30c6282335275d4dfa Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Thu, 24 Jun 2010 18:57:00 -0700 Subject: [ARM] tegra: common: Update common clock init table Renames clocks in the clock init table to match the datasheet names Signed-off-by: Colin Cross --- arch/arm/mach-tegra/common.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'arch') diff --git a/arch/arm/mach-tegra/common.c b/arch/arm/mach-tegra/common.c index 9aedaf77013c..445104a993ba 100644 --- a/arch/arm/mach-tegra/common.c +++ b/arch/arm/mach-tegra/common.c @@ -36,8 +36,8 @@ static __initdata struct tegra_clk_init_table common_clk_init_table[] = { { "pll_p_out2", "pll_p", 48000000, true }, { "pll_p_out3", "pll_p", 72000000, true }, { "pll_p_out4", "pll_p", 108000000, true }, - { "sys", "pll_p_out4", 108000000, true }, - { "hclk", "sys", 108000000, true }, + { "sclk", "pll_p_out4", 108000000, true }, + { "hclk", "sclk", 108000000, true }, { "pclk", "hclk", 54000000, true }, { NULL, NULL, 0, 0}, }; -- cgit v1.2.3 From 7056d423f16103f6700569f60ca842d91bfaabab Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Thu, 22 Apr 2010 20:30:13 -0700 Subject: [ARM] tegra: Add cpufreq support Implement cpufreq support for the Tegra SOC. DVFS is handled by the core virtual cpu clock. The frequencies of the two cores are tied together, the highest frequency requested by either core determines the actual frequency. Signed-off-by: Colin Cross --- arch/arm/Kconfig | 1 + arch/arm/mach-tegra/Makefile | 1 + arch/arm/mach-tegra/cpu-tegra.c | 185 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 187 insertions(+) create mode 100644 arch/arm/mach-tegra/cpu-tegra.c (limited to 'arch') diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 3849887157e7..fc6573d17a46 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -573,6 +573,7 @@ config ARCH_TEGRA select HAVE_CLK select COMMON_CLKDEV select ARCH_HAS_BARRIERS if CACHE_L2X0 + select ARCH_HAS_CPUFREQ help This enables support for NVIDIA Tegra based systems (Tegra APX, Tegra 6xx and Tegra 2 series). diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile index 3642e58e5073..2a3762179ccf 100644 --- a/arch/arm/mach-tegra/Makefile +++ b/arch/arm/mach-tegra/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_dvfs.o obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += pinmux-t2-tables.o obj-$(CONFIG_SMP) += platsmp.o localtimer.o headsmp.o obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o +obj-$(CONFIG_CPU_FREQ) += cpu-tegra.o obj-${CONFIG_MACH_HARMONY} += board-harmony.o obj-${CONFIG_MACH_HARMONY} += board-harmony-pinmux.o diff --git a/arch/arm/mach-tegra/cpu-tegra.c b/arch/arm/mach-tegra/cpu-tegra.c new file mode 100644 index 000000000000..fea5719c7072 --- /dev/null +++ b/arch/arm/mach-tegra/cpu-tegra.c @@ -0,0 +1,185 @@ +/* + * arch/arm/mach-tegra/cpu-tegra.c + * + * Copyright (C) 2010 Google, Inc. + * + * Author: + * Colin Cross + * Based on arch/arm/plat-omap/cpu-omap.c, (C) 2005 Nokia Corporation + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +/* Frequency table index must be sequential starting at 0 */ +static struct cpufreq_frequency_table freq_table[] = { + { 0, 312000 }, + { 1, 456000 }, + { 2, 608000 }, + { 3, 760000 }, + { 4, 816000 }, + { 5, 912000 }, + { 6, 1000000 }, + { 7, CPUFREQ_TABLE_END }, +}; + +#define NUM_CPUS 2 + +static struct clk *cpu_clk; + +static unsigned long target_cpu_speed[NUM_CPUS]; + +int tegra_verify_speed(struct cpufreq_policy *policy) +{ + return cpufreq_frequency_table_verify(policy, freq_table); +} + +unsigned int tegra_getspeed(unsigned int cpu) +{ + unsigned long rate; + + if (cpu >= NUM_CPUS) + return 0; + + rate = clk_get_rate(cpu_clk) / 1000; + return rate; +} + +static int tegra_update_cpu_speed(void) +{ + int i; + unsigned long rate = 0; + int ret = 0; + struct cpufreq_freqs freqs; + + for_each_online_cpu(i) + rate = max(rate, target_cpu_speed[i]); + + freqs.old = tegra_getspeed(0); + freqs.new = rate; + + if (freqs.old == freqs.new) + return ret; + + for_each_online_cpu(freqs.cpu) + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + +#ifdef CONFIG_CPU_FREQ_DEBUG + printk(KERN_DEBUG "cpufreq-tegra: transition: %u --> %u\n", + freqs.old, freqs.new); +#endif + + ret = clk_set_rate_cansleep(cpu_clk, freqs.new * 1000); + if (ret) { + pr_err("cpu-tegra: Failed to set cpu frequency to %d kHz\n", + freqs.new); + return ret; + } + + for_each_online_cpu(freqs.cpu) + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + + return 0; +} + +static int tegra_target(struct cpufreq_policy *policy, + unsigned int target_freq, + unsigned int relation) +{ + int idx; + unsigned int freq; + + cpufreq_frequency_table_target(policy, freq_table, target_freq, + relation, &idx); + + freq = freq_table[idx].frequency; + + target_cpu_speed[policy->cpu] = freq; + + return tegra_update_cpu_speed(); +} + +static int tegra_cpu_init(struct cpufreq_policy *policy) +{ + if (policy->cpu >= NUM_CPUS) + return -EINVAL; + + cpu_clk = clk_get_sys(NULL, "cpu"); + if (IS_ERR(cpu_clk)) + return PTR_ERR(cpu_clk); + + cpufreq_frequency_table_cpuinfo(policy, freq_table); + cpufreq_frequency_table_get_attr(freq_table, policy->cpu); + policy->cur = tegra_getspeed(policy->cpu); + target_cpu_speed[policy->cpu] = policy->cur; + + /* FIXME: what's the actual transition time? */ + policy->cpuinfo.transition_latency = 300 * 1000; + + policy->shared_type = CPUFREQ_SHARED_TYPE_ALL; + cpumask_copy(policy->related_cpus, cpu_possible_mask); + + return 0; +} + +static int tegra_cpu_exit(struct cpufreq_policy *policy) +{ + cpufreq_frequency_table_cpuinfo(policy, freq_table); + clk_put(cpu_clk); + return 0; +} + +static struct freq_attr *tegra_cpufreq_attr[] = { + &cpufreq_freq_attr_scaling_available_freqs, + NULL, +}; + +static struct cpufreq_driver tegra_cpufreq_driver = { + .verify = tegra_verify_speed, + .target = tegra_target, + .get = tegra_getspeed, + .init = tegra_cpu_init, + .exit = tegra_cpu_exit, + .name = "tegra", + .attr = tegra_cpufreq_attr, +}; + +static int __init tegra_cpufreq_init(void) +{ + return cpufreq_register_driver(&tegra_cpufreq_driver); +} + +static void __exit tegra_cpufreq_exit(void) +{ + cpufreq_unregister_driver(&tegra_cpufreq_driver); +} + + +MODULE_AUTHOR("Colin Cross "); +MODULE_DESCRIPTION("cpufreq driver for Nvidia Tegra2"); +MODULE_LICENSE("GPL"); +module_init(tegra_cpufreq_init); +module_exit(tegra_cpufreq_exit); -- cgit v1.2.3 From 4de3a8fa334851e642d4889d6afa6e5d3daea10a Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Mon, 5 Apr 2010 13:16:42 -0700 Subject: [ARM] tegra: Add APB DMA support The APB DMA block handles DMA transfers to and from some peripherals in the Tegra SOC. It reads from sequential addresses on the memory bus, and writes repeatedly to the same address on the APB bus. Two transfer modes are supported, oneshot for transferring a known size to or from a peripheral, and continuous for streaming data. In continuous mode, a callback occurs when the buffer is half full to allow the existing data to be handled and a new request queued.x v2 changes: dma API no longer uses PTR_ERR Signed-off-by: Erik Gilling Signed-off-by: Colin Cross --- arch/arm/mach-tegra/Kconfig | 7 + arch/arm/mach-tegra/Makefile | 1 + arch/arm/mach-tegra/common.c | 7 + arch/arm/mach-tegra/dma.c | 752 +++++++++++++++++++++++++++++++++ arch/arm/mach-tegra/include/mach/dma.h | 155 +++++++ 5 files changed, 922 insertions(+) create mode 100644 arch/arm/mach-tegra/dma.c create mode 100644 arch/arm/mach-tegra/include/mach/dma.h (limited to 'arch') diff --git a/arch/arm/mach-tegra/Kconfig b/arch/arm/mach-tegra/Kconfig index a57713c1954a..8e32d9d7ec8d 100644 --- a/arch/arm/mach-tegra/Kconfig +++ b/arch/arm/mach-tegra/Kconfig @@ -47,4 +47,11 @@ config TEGRA_DEBUG_UARTE endchoice +config TEGRA_SYSTEM_DMA + bool "Enable system DMA driver for NVIDIA Tegra SoCs" + default y + help + Adds system DMA functionality for NVIDIA Tegra SoCs, used by + several Tegra device drivers + endif diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile index 2a3762179ccf..fc069a9e0f6b 100644 --- a/arch/arm/mach-tegra/Makefile +++ b/arch/arm/mach-tegra/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_dvfs.o obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += pinmux-t2-tables.o obj-$(CONFIG_SMP) += platsmp.o localtimer.o headsmp.o obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o +obj-$(CONFIG_TEGRA_SYSTEM_DMA) += dma.o obj-$(CONFIG_CPU_FREQ) += cpu-tegra.o obj-${CONFIG_MACH_HARMONY} += board-harmony.o diff --git a/arch/arm/mach-tegra/common.c b/arch/arm/mach-tegra/common.c index 445104a993ba..7c91e2b9d643 100644 --- a/arch/arm/mach-tegra/common.c +++ b/arch/arm/mach-tegra/common.c @@ -19,10 +19,13 @@ #include #include +#include +#include #include #include +#include #include "board.h" #include "clock.h" @@ -52,6 +55,7 @@ void __init tegra_init_cache(void) l2x0_init(p, 0x6C080001, 0x8200c3fe); #endif + } void __init tegra_common_init(void) @@ -60,4 +64,7 @@ void __init tegra_common_init(void) tegra_init_clock(); tegra_clk_init_from_table(common_clk_init_table); tegra_init_cache(); +#ifdef CONFIG_TEGRA_SYSTEM_DMA + tegra_dma_init(); +#endif } diff --git a/arch/arm/mach-tegra/dma.c b/arch/arm/mach-tegra/dma.c new file mode 100644 index 000000000000..edda6ec5e925 --- /dev/null +++ b/arch/arm/mach-tegra/dma.c @@ -0,0 +1,752 @@ +/* + * arch/arm/mach-tegra/dma.c + * + * System DMA driver for NVIDIA Tegra SoCs + * + * Copyright (c) 2008-2009, NVIDIA Corporation. + * + * 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. + * + * This program is distributed in the hope that 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define APB_DMA_GEN 0x000 +#define GEN_ENABLE (1<<31) + +#define APB_DMA_CNTRL 0x010 + +#define APB_DMA_IRQ_MASK 0x01c + +#define APB_DMA_IRQ_MASK_SET 0x020 + +#define APB_DMA_CHAN_CSR 0x000 +#define CSR_ENB (1<<31) +#define CSR_IE_EOC (1<<30) +#define CSR_HOLD (1<<29) +#define CSR_DIR (1<<28) +#define CSR_ONCE (1<<27) +#define CSR_FLOW (1<<21) +#define CSR_REQ_SEL_SHIFT 16 +#define CSR_REQ_SEL_MASK (0x1F<list.next, typeof(*req), node); + + tegra_dma_dequeue_req(ch, req); + return; +} + +void tegra_dma_stop(struct tegra_dma_channel *ch) +{ + unsigned int csr; + unsigned int status; + + csr = ch->csr; + csr &= ~CSR_IE_EOC; + writel(csr, ch->addr + APB_DMA_CHAN_CSR); + + csr &= ~CSR_ENB; + writel(csr, ch->addr + APB_DMA_CHAN_CSR); + + status = readl(ch->addr + APB_DMA_CHAN_STA); + if (status & STA_ISE_EOC) + writel(status, ch->addr + APB_DMA_CHAN_STA); +} + +int tegra_dma_cancel(struct tegra_dma_channel *ch) +{ + unsigned int csr; + unsigned long irq_flags; + + spin_lock_irqsave(&ch->lock, irq_flags); + while (!list_empty(&ch->list)) + list_del(ch->list.next); + + csr = ch->csr; + csr &= ~CSR_REQ_SEL_MASK; + csr |= CSR_REQ_SEL_INVALID; + + /* Set the enable as that is not shadowed */ + csr |= CSR_ENB; + writel(csr, ch->addr + APB_DMA_CHAN_CSR); + + tegra_dma_stop(ch); + + spin_unlock_irqrestore(&ch->lock, irq_flags); + return 0; +} + +int tegra_dma_dequeue_req(struct tegra_dma_channel *ch, + struct tegra_dma_req *_req) +{ + unsigned int csr; + unsigned int status; + struct tegra_dma_req *req = NULL; + int found = 0; + unsigned long irq_flags; + int to_transfer; + int req_transfer_count; + + spin_lock_irqsave(&ch->lock, irq_flags); + list_for_each_entry(req, &ch->list, node) { + if (req == _req) { + list_del(&req->node); + found = 1; + break; + } + } + if (!found) { + spin_unlock_irqrestore(&ch->lock, irq_flags); + return 0; + } + + /* STOP the DMA and get the transfer count. + * Getting the transfer count is tricky. + * - Change the source selector to invalid to stop the DMA from + * FIFO to memory. + * - Read the status register to know the number of pending + * bytes to be transfered. + * - Finally stop or program the DMA to the next buffer in the + * list. + */ + csr = ch->csr; + csr &= ~CSR_REQ_SEL_MASK; + csr |= CSR_REQ_SEL_INVALID; + + /* Set the enable as that is not shadowed */ + csr |= CSR_ENB; + writel(csr, ch->addr + APB_DMA_CHAN_CSR); + + /* Get the transfer count */ + status = readl(ch->addr + APB_DMA_CHAN_STA); + to_transfer = (status & STA_COUNT_MASK) >> STA_COUNT_SHIFT; + req_transfer_count = (ch->csr & CSR_WCOUNT_MASK) >> CSR_WCOUNT_SHIFT; + req_transfer_count += 1; + to_transfer += 1; + + req->bytes_transferred = req_transfer_count; + + if (status & STA_BUSY) + req->bytes_transferred -= to_transfer; + + /* In continous transfer mode, DMA only tracks the count of the + * half DMA buffer. So, if the DMA already finished half the DMA + * then add the half buffer to the completed count. + * + * FIXME: There can be a race here. What if the req to + * dequue happens at the same time as the DMA just moved to + * the new buffer and SW didn't yet received the interrupt? + */ + if (ch->mode & TEGRA_DMA_MODE_CONTINOUS) + if (req->buffer_status == TEGRA_DMA_REQ_BUF_STATUS_HALF_FULL) + req->bytes_transferred += req_transfer_count; + + req->bytes_transferred *= 4; + + tegra_dma_stop(ch); + if (!list_empty(&ch->list)) { + /* if the list is not empty, queue the next request */ + struct tegra_dma_req *next_req; + next_req = list_entry(ch->list.next, + typeof(*next_req), node); + tegra_dma_update_hw(ch, next_req); + } + req->status = -TEGRA_DMA_REQ_ERROR_ABORTED; + + spin_unlock_irqrestore(&ch->lock, irq_flags); + + /* Callback should be called without any lock */ + req->complete(req); + return 0; +} +EXPORT_SYMBOL(tegra_dma_dequeue_req); + +bool tegra_dma_is_empty(struct tegra_dma_channel *ch) +{ + unsigned long irq_flags; + bool is_empty; + + spin_lock_irqsave(&ch->lock, irq_flags); + if (list_empty(&ch->list)) + is_empty = true; + else + is_empty = false; + spin_unlock_irqrestore(&ch->lock, irq_flags); + return is_empty; +} +EXPORT_SYMBOL(tegra_dma_is_empty); + +bool tegra_dma_is_req_inflight(struct tegra_dma_channel *ch, + struct tegra_dma_req *_req) +{ + unsigned long irq_flags; + struct tegra_dma_req *req; + + spin_lock_irqsave(&ch->lock, irq_flags); + list_for_each_entry(req, &ch->list, node) { + if (req == _req) { + spin_unlock_irqrestore(&ch->lock, irq_flags); + return true; + } + } + spin_unlock_irqrestore(&ch->lock, irq_flags); + return false; +} +EXPORT_SYMBOL(tegra_dma_is_req_inflight); + +int tegra_dma_enqueue_req(struct tegra_dma_channel *ch, + struct tegra_dma_req *req) +{ + unsigned long irq_flags; + int start_dma = 0; + + if (req->size > NV_DMA_MAX_TRASFER_SIZE || + req->source_addr & 0x3 || req->dest_addr & 0x3) { + pr_err("Invalid DMA request for channel %d\n", ch->id); + return -EINVAL; + } + + spin_lock_irqsave(&ch->lock, irq_flags); + + req->bytes_transferred = 0; + req->status = 0; + req->buffer_status = 0; + if (list_empty(&ch->list)) + start_dma = 1; + + list_add_tail(&req->node, &ch->list); + + if (start_dma) + tegra_dma_update_hw(ch, req); + + spin_unlock_irqrestore(&ch->lock, irq_flags); + + return 0; +} +EXPORT_SYMBOL(tegra_dma_enqueue_req); + +struct tegra_dma_channel *tegra_dma_allocate_channel(int mode) +{ + int channel; + struct tegra_dma_channel *ch; + + /* first channel is the shared channel */ + if (mode & TEGRA_DMA_SHARED) { + channel = TEGRA_SYSTEM_DMA_CH_MIN; + } else { + channel = find_first_zero_bit(channel_usage, + ARRAY_SIZE(dma_channels)); + if (channel >= ARRAY_SIZE(dma_channels)) + return NULL; + } + __set_bit(channel, channel_usage); + ch = &dma_channels[channel]; + ch->mode = mode; + return ch; +} +EXPORT_SYMBOL(tegra_dma_allocate_channel); + +void tegra_dma_free_channel(struct tegra_dma_channel *ch) +{ + if (ch->mode & TEGRA_DMA_SHARED) + return; + tegra_dma_cancel(ch); + __clear_bit(ch->id, channel_usage); +} +EXPORT_SYMBOL(tegra_dma_free_channel); + +static void tegra_dma_update_hw_partial(struct tegra_dma_channel *ch, + struct tegra_dma_req *req) +{ + if (req->to_memory) { + ch->apb_ptr = req->source_addr; + ch->ahb_ptr = req->dest_addr; + } else { + ch->apb_ptr = req->dest_addr; + ch->ahb_ptr = req->source_addr; + } + writel(ch->apb_ptr, ch->addr + APB_DMA_CHAN_APB_PTR); + writel(ch->ahb_ptr, ch->addr + APB_DMA_CHAN_AHB_PTR); + + req->status = TEGRA_DMA_REQ_INFLIGHT; + return; +} + +static void tegra_dma_update_hw(struct tegra_dma_channel *ch, + struct tegra_dma_req *req) +{ + int ahb_addr_wrap; + int apb_addr_wrap; + int ahb_bus_width; + int apb_bus_width; + int index; + unsigned long csr; + + + ch->csr |= CSR_FLOW; + ch->csr &= ~CSR_REQ_SEL_MASK; + ch->csr |= req->req_sel << CSR_REQ_SEL_SHIFT; + ch->ahb_seq &= ~AHB_SEQ_BURST_MASK; + ch->ahb_seq |= AHB_SEQ_BURST_1; + + /* One shot mode is always single buffered, + * continuous mode is always double buffered + * */ + if (ch->mode & TEGRA_DMA_MODE_ONESHOT) { + ch->csr |= CSR_ONCE; + ch->ahb_seq &= ~AHB_SEQ_DBL_BUF; + ch->csr &= ~CSR_WCOUNT_MASK; + ch->csr |= ((req->size>>2) - 1) << CSR_WCOUNT_SHIFT; + } else { + ch->csr &= ~CSR_ONCE; + ch->ahb_seq |= AHB_SEQ_DBL_BUF; + + /* In double buffered mode, we set the size to half the + * requested size and interrupt when half the buffer + * is full */ + ch->csr &= ~CSR_WCOUNT_MASK; + ch->csr |= ((req->size>>3) - 1) << CSR_WCOUNT_SHIFT; + } + + if (req->to_memory) { + ch->csr &= ~CSR_DIR; + ch->apb_ptr = req->source_addr; + ch->ahb_ptr = req->dest_addr; + + apb_addr_wrap = req->source_wrap; + ahb_addr_wrap = req->dest_wrap; + apb_bus_width = req->source_bus_width; + ahb_bus_width = req->dest_bus_width; + + } else { + ch->csr |= CSR_DIR; + ch->apb_ptr = req->dest_addr; + ch->ahb_ptr = req->source_addr; + + apb_addr_wrap = req->dest_wrap; + ahb_addr_wrap = req->source_wrap; + apb_bus_width = req->dest_bus_width; + ahb_bus_width = req->source_bus_width; + } + + apb_addr_wrap >>= 2; + ahb_addr_wrap >>= 2; + + /* set address wrap for APB size */ + index = 0; + do { + if (apb_addr_wrap_table[index] == apb_addr_wrap) + break; + index++; + } while (index < ARRAY_SIZE(apb_addr_wrap_table)); + BUG_ON(index == ARRAY_SIZE(apb_addr_wrap_table)); + ch->apb_seq &= ~APB_SEQ_WRAP_MASK; + ch->apb_seq |= index << APB_SEQ_WRAP_SHIFT; + + /* set address wrap for AHB size */ + index = 0; + do { + if (ahb_addr_wrap_table[index] == ahb_addr_wrap) + break; + index++; + } while (index < ARRAY_SIZE(ahb_addr_wrap_table)); + BUG_ON(index == ARRAY_SIZE(ahb_addr_wrap_table)); + ch->ahb_seq &= ~AHB_SEQ_WRAP_MASK; + ch->ahb_seq |= index << AHB_SEQ_WRAP_SHIFT; + + for (index = 0; index < ARRAY_SIZE(bus_width_table); index++) { + if (bus_width_table[index] == ahb_bus_width) + break; + } + BUG_ON(index == ARRAY_SIZE(bus_width_table)); + ch->ahb_seq &= ~AHB_SEQ_BUS_WIDTH_MASK; + ch->ahb_seq |= index << AHB_SEQ_BUS_WIDTH_SHIFT; + + for (index = 0; index < ARRAY_SIZE(bus_width_table); index++) { + if (bus_width_table[index] == apb_bus_width) + break; + } + BUG_ON(index == ARRAY_SIZE(bus_width_table)); + ch->apb_seq &= ~APB_SEQ_BUS_WIDTH_MASK; + ch->apb_seq |= index << APB_SEQ_BUS_WIDTH_SHIFT; + + ch->csr |= CSR_IE_EOC; + + /* update hw registers with the shadow */ + writel(ch->csr, ch->addr + APB_DMA_CHAN_CSR); + writel(ch->apb_seq, ch->addr + APB_DMA_CHAN_APB_SEQ); + writel(ch->apb_ptr, ch->addr + APB_DMA_CHAN_APB_PTR); + writel(ch->ahb_seq, ch->addr + APB_DMA_CHAN_AHB_SEQ); + writel(ch->ahb_ptr, ch->addr + APB_DMA_CHAN_AHB_PTR); + + csr = ch->csr | CSR_ENB; + writel(csr, ch->addr + APB_DMA_CHAN_CSR); + + req->status = TEGRA_DMA_REQ_INFLIGHT; +} + +static void tegra_dma_init_hw(struct tegra_dma_channel *ch) +{ + /* One shot with an interrupt to CPU after transfer */ + ch->csr = CSR_ONCE | CSR_IE_EOC; + ch->ahb_seq = AHB_SEQ_BUS_WIDTH_32 | AHB_SEQ_INTR_ENB; + ch->apb_seq = APB_SEQ_BUS_WIDTH_32 | 1 << APB_SEQ_WRAP_SHIFT; +} + +static void handle_oneshot_dma(struct tegra_dma_channel *ch) +{ + struct tegra_dma_req *req; + + spin_lock(&ch->lock); + if (list_empty(&ch->list)) { + spin_unlock(&ch->lock); + return; + } + + req = list_entry(ch->list.next, typeof(*req), node); + if (req) { + int bytes_transferred; + + bytes_transferred = + (ch->csr & CSR_WCOUNT_MASK) >> CSR_WCOUNT_SHIFT; + bytes_transferred += 1; + bytes_transferred <<= 2; + + list_del(&req->node); + req->bytes_transferred = bytes_transferred; + req->status = TEGRA_DMA_REQ_SUCCESS; + + spin_unlock(&ch->lock); + /* Callback should be called without any lock */ + pr_debug("%s: transferred %d bytes\n", __func__, + req->bytes_transferred); + req->complete(req); + spin_lock(&ch->lock); + } + + if (!list_empty(&ch->list)) { + req = list_entry(ch->list.next, typeof(*req), node); + /* the complete function we just called may have enqueued + another req, in which case dma has already started */ + if (req->status != TEGRA_DMA_REQ_INFLIGHT) + tegra_dma_update_hw(ch, req); + } + spin_unlock(&ch->lock); +} + +static void handle_continuous_dma(struct tegra_dma_channel *ch) +{ + struct tegra_dma_req *req; + + spin_lock(&ch->lock); + if (list_empty(&ch->list)) { + spin_unlock(&ch->lock); + return; + } + + req = list_entry(ch->list.next, typeof(*req), node); + if (req) { + if (req->buffer_status == TEGRA_DMA_REQ_BUF_STATUS_EMPTY) { + /* Load the next request into the hardware, if available + * */ + if (!list_is_last(&req->node, &ch->list)) { + struct tegra_dma_req *next_req; + + next_req = list_entry(req->node.next, + typeof(*next_req), node); + tegra_dma_update_hw_partial(ch, next_req); + } + req->buffer_status = TEGRA_DMA_REQ_BUF_STATUS_HALF_FULL; + req->status = TEGRA_DMA_REQ_SUCCESS; + /* DMA lock is NOT held when callback is called */ + spin_unlock(&ch->lock); + if (likely(req->threshold)) + req->threshold(req); + return; + + } else if (req->buffer_status == + TEGRA_DMA_REQ_BUF_STATUS_HALF_FULL) { + /* Callback when the buffer is completely full (i.e on + * the second interrupt */ + int bytes_transferred; + + bytes_transferred = + (ch->csr & CSR_WCOUNT_MASK) >> CSR_WCOUNT_SHIFT; + bytes_transferred += 1; + bytes_transferred <<= 3; + + req->buffer_status = TEGRA_DMA_REQ_BUF_STATUS_FULL; + req->bytes_transferred = bytes_transferred; + req->status = TEGRA_DMA_REQ_SUCCESS; + list_del(&req->node); + + /* DMA lock is NOT held when callbak is called */ + spin_unlock(&ch->lock); + req->complete(req); + return; + + } else { + BUG(); + } + } + spin_unlock(&ch->lock); +} + +static irqreturn_t dma_isr(int irq, void *data) +{ + struct tegra_dma_channel *ch = data; + unsigned long status; + + status = readl(ch->addr + APB_DMA_CHAN_STA); + if (status & STA_ISE_EOC) + writel(status, ch->addr + APB_DMA_CHAN_STA); + else { + pr_warning("Got a spurious ISR for DMA channel %d\n", ch->id); + return IRQ_HANDLED; + } + return IRQ_WAKE_THREAD; +} + +static irqreturn_t dma_thread_fn(int irq, void *data) +{ + struct tegra_dma_channel *ch = data; + + if (ch->mode & TEGRA_DMA_MODE_ONESHOT) + handle_oneshot_dma(ch); + else + handle_continuous_dma(ch); + + + return IRQ_HANDLED; +} + +int __init tegra_dma_init(void) +{ + int ret = 0; + int i; + unsigned int irq; + void __iomem *addr; + + addr = IO_ADDRESS(TEGRA_APB_DMA_BASE); + writel(GEN_ENABLE, addr + APB_DMA_GEN); + writel(0, addr + APB_DMA_CNTRL); + writel(0xFFFFFFFFul >> (31 - TEGRA_SYSTEM_DMA_CH_MAX), + addr + APB_DMA_IRQ_MASK_SET); + + memset(channel_usage, 0, sizeof(channel_usage)); + memset(dma_channels, 0, sizeof(dma_channels)); + + /* Reserve all the channels we are not supposed to touch */ + for (i = 0; i < TEGRA_SYSTEM_DMA_CH_MIN; i++) + __set_bit(i, channel_usage); + + for (i = TEGRA_SYSTEM_DMA_CH_MIN; i <= TEGRA_SYSTEM_DMA_CH_MAX; i++) { + struct tegra_dma_channel *ch = &dma_channels[i]; + + __clear_bit(i, channel_usage); + + ch->id = i; + snprintf(ch->name, TEGRA_DMA_NAME_SIZE, "dma_channel_%d", i); + + ch->addr = IO_ADDRESS(TEGRA_APB_DMA_CH0_BASE + + TEGRA_APB_DMA_CH0_SIZE * i); + + spin_lock_init(&ch->lock); + INIT_LIST_HEAD(&ch->list); + tegra_dma_init_hw(ch); + + irq = INT_APB_DMA_CH0 + i; + ret = request_threaded_irq(irq, dma_isr, dma_thread_fn, 0, + dma_channels[i].name, ch); + if (ret) { + pr_err("Failed to register IRQ %d for DMA %d\n", + irq, i); + goto fail; + } + ch->irq = irq; + } + /* mark the shared channel allocated */ + __set_bit(TEGRA_SYSTEM_DMA_CH_MIN, channel_usage); + + for (i = TEGRA_SYSTEM_DMA_CH_MAX+1; i < NV_DMA_MAX_CHANNELS; i++) + __set_bit(i, channel_usage); + + return ret; +fail: + writel(0, addr + APB_DMA_GEN); + for (i = TEGRA_SYSTEM_DMA_CH_MIN; i <= TEGRA_SYSTEM_DMA_CH_MAX; i++) { + struct tegra_dma_channel *ch = &dma_channels[i]; + if (ch->irq) + free_irq(ch->irq, ch); + } + return ret; +} + +#ifdef CONFIG_PM +static u32 apb_dma[5*TEGRA_SYSTEM_DMA_CH_NR + 3]; + +void tegra_dma_suspend(void) +{ + void __iomem *addr = IO_ADDRESS(TEGRA_APB_DMA_BASE); + u32 *ctx = apb_dma; + int i; + + *ctx++ = readl(addr + APB_DMA_GEN); + *ctx++ = readl(addr + APB_DMA_CNTRL); + *ctx++ = readl(addr + APB_DMA_IRQ_MASK); + + for (i = 0; i < TEGRA_SYSTEM_DMA_CH_NR; i++) { + addr = IO_ADDRESS(TEGRA_APB_DMA_CH0_BASE + + TEGRA_APB_DMA_CH0_SIZE * i); + + *ctx++ = readl(addr + APB_DMA_CHAN_CSR); + *ctx++ = readl(addr + APB_DMA_CHAN_AHB_PTR); + *ctx++ = readl(addr + APB_DMA_CHAN_AHB_SEQ); + *ctx++ = readl(addr + APB_DMA_CHAN_APB_PTR); + *ctx++ = readl(addr + APB_DMA_CHAN_APB_SEQ); + } +} + +void tegra_dma_resume(void) +{ + void __iomem *addr = IO_ADDRESS(TEGRA_APB_DMA_BASE); + u32 *ctx = apb_dma; + int i; + + writel(*ctx++, addr + APB_DMA_GEN); + writel(*ctx++, addr + APB_DMA_CNTRL); + writel(*ctx++, addr + APB_DMA_IRQ_MASK); + + for (i = 0; i < TEGRA_SYSTEM_DMA_CH_NR; i++) { + addr = IO_ADDRESS(TEGRA_APB_DMA_CH0_BASE + + TEGRA_APB_DMA_CH0_SIZE * i); + + writel(*ctx++, addr + APB_DMA_CHAN_CSR); + writel(*ctx++, addr + APB_DMA_CHAN_AHB_PTR); + writel(*ctx++, addr + APB_DMA_CHAN_AHB_SEQ); + writel(*ctx++, addr + APB_DMA_CHAN_APB_PTR); + writel(*ctx++, addr + APB_DMA_CHAN_APB_SEQ); + } +} + +#endif diff --git a/arch/arm/mach-tegra/include/mach/dma.h b/arch/arm/mach-tegra/include/mach/dma.h new file mode 100644 index 000000000000..39011bd9a925 --- /dev/null +++ b/arch/arm/mach-tegra/include/mach/dma.h @@ -0,0 +1,155 @@ +/* + * arch/arm/mach-tegra/include/mach/dma.h + * + * Copyright (c) 2008-2009, NVIDIA Corporation. + * + * 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. + * + * This program is distributed in the hope that 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MACH_TEGRA_DMA_H +#define __MACH_TEGRA_DMA_H + +#include + +#if defined(CONFIG_TEGRA_SYSTEM_DMA) + +struct tegra_dma_req; +struct tegra_dma_channel; + +#define TEGRA_DMA_REQ_SEL_CNTR 0 +#define TEGRA_DMA_REQ_SEL_I2S_2 1 +#define TEGRA_DMA_REQ_SEL_I2S_1 2 +#define TEGRA_DMA_REQ_SEL_SPD_I 3 +#define TEGRA_DMA_REQ_SEL_UI_I 4 +#define TEGRA_DMA_REQ_SEL_MIPI 5 +#define TEGRA_DMA_REQ_SEL_I2S2_2 6 +#define TEGRA_DMA_REQ_SEL_I2S2_1 7 +#define TEGRA_DMA_REQ_SEL_UARTA 8 +#define TEGRA_DMA_REQ_SEL_UARTB 9 +#define TEGRA_DMA_REQ_SEL_UARTC 10 +#define TEGRA_DMA_REQ_SEL_SPI 11 +#define TEGRA_DMA_REQ_SEL_AC97 12 +#define TEGRA_DMA_REQ_SEL_ACMODEM 13 +#define TEGRA_DMA_REQ_SEL_SL4B 14 +#define TEGRA_DMA_REQ_SEL_SL2B1 15 +#define TEGRA_DMA_REQ_SEL_SL2B2 16 +#define TEGRA_DMA_REQ_SEL_SL2B3 17 +#define TEGRA_DMA_REQ_SEL_SL2B4 18 +#define TEGRA_DMA_REQ_SEL_UARTD 19 +#define TEGRA_DMA_REQ_SEL_UARTE 20 +#define TEGRA_DMA_REQ_SEL_I2C 21 +#define TEGRA_DMA_REQ_SEL_I2C2 22 +#define TEGRA_DMA_REQ_SEL_I2C3 23 +#define TEGRA_DMA_REQ_SEL_DVC_I2C 24 +#define TEGRA_DMA_REQ_SEL_OWR 25 +#define TEGRA_DMA_REQ_SEL_INVALID 31 + +enum tegra_dma_mode { + TEGRA_DMA_SHARED = 1, + TEGRA_DMA_MODE_CONTINOUS = 2, + TEGRA_DMA_MODE_ONESHOT = 4, +}; + +enum tegra_dma_req_error { + TEGRA_DMA_REQ_SUCCESS = 0, + TEGRA_DMA_REQ_ERROR_ABORTED, + TEGRA_DMA_REQ_INFLIGHT, +}; + +enum tegra_dma_req_buff_status { + TEGRA_DMA_REQ_BUF_STATUS_EMPTY = 0, + TEGRA_DMA_REQ_BUF_STATUS_HALF_FULL, + TEGRA_DMA_REQ_BUF_STATUS_FULL, +}; + +struct tegra_dma_req { + struct list_head node; + unsigned int modid; + int instance; + + /* Called when the req is complete and from the DMA ISR context. + * When this is called the req structure is no longer queued by + * the DMA channel. + * + * State of the DMA depends on the number of req it has. If there are + * no DMA requests queued up, then it will STOP the DMA. It there are + * more requests in the DMA, then it will queue the next request. + */ + void (*complete)(struct tegra_dma_req *req); + + /* This is a called from the DMA ISR context when the DMA is still in + * progress and is actively filling same buffer. + * + * In case of continous mode receive, this threshold is 1/2 the buffer + * size. In other cases, this will not even be called as there is no + * hardware support for it. + * + * In the case of continous mode receive, if there is next req already + * queued, DMA programs the HW to use that req when this req is + * completed. If there is no "next req" queued, then DMA ISR doesn't do + * anything before calling this callback. + * + * This is mainly used by the cases, where the clients has queued + * only one req and want to get some sort of DMA threshold + * callback to program the next buffer. + * + */ + void (*threshold)(struct tegra_dma_req *req); + + /* 1 to copy to memory. + * 0 to copy from the memory to device FIFO */ + int to_memory; + + void *virt_addr; + + unsigned long source_addr; + unsigned long dest_addr; + unsigned long dest_wrap; + unsigned long source_wrap; + unsigned long source_bus_width; + unsigned long dest_bus_width; + unsigned long req_sel; + unsigned int size; + + /* Updated by the DMA driver on the conpletion of the request. */ + int bytes_transferred; + int status; + + /* DMA completion tracking information */ + int buffer_status; + + /* Client specific data */ + void *dev; +}; + +int tegra_dma_enqueue_req(struct tegra_dma_channel *ch, + struct tegra_dma_req *req); +int tegra_dma_dequeue_req(struct tegra_dma_channel *ch, + struct tegra_dma_req *req); +void tegra_dma_dequeue(struct tegra_dma_channel *ch); +void tegra_dma_flush(struct tegra_dma_channel *ch); + +bool tegra_dma_is_req_inflight(struct tegra_dma_channel *ch, + struct tegra_dma_req *req); +bool tegra_dma_is_empty(struct tegra_dma_channel *ch); + +struct tegra_dma_channel *tegra_dma_allocate_channel(int mode); +void tegra_dma_free_channel(struct tegra_dma_channel *ch); + +int __init tegra_dma_init(void); + +#endif + +#endif -- cgit v1.2.3 From 8d685bc5e5cad76c05c9986c23a821c5bcef7c16 Mon Sep 17 00:00:00 2001 From: Mike Rapoport Date: Mon, 27 Sep 2010 11:26:32 +0200 Subject: tegra: add PCI Express clocks Signed-off-by: Mike Rapoport CC: Gary King Signed-off-by: Colin Cross --- arch/arm/mach-tegra/tegra2_clocks.c | 48 +++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) (limited to 'arch') diff --git a/arch/arm/mach-tegra/tegra2_clocks.c b/arch/arm/mach-tegra/tegra2_clocks.c index cf1c9d0ef7e1..ae3b308e22a4 100644 --- a/arch/arm/mach-tegra/tegra2_clocks.c +++ b/arch/arm/mach-tegra/tegra2_clocks.c @@ -110,6 +110,8 @@ #define PLLD_MISC_DIV_RST (1<<23) #define PLLD_MISC_DCCON_SHIFT 12 +#define PLLE_MISC_READY (1 << 15) + #define PERIPH_CLK_TO_ENB_REG(c) ((c->clk_num / 32) * 4) #define PERIPH_CLK_TO_ENB_SET_REG(c) ((c->clk_num / 32) * 8) #define PERIPH_CLK_TO_ENB_BIT(c) (1 << (c->clk_num % 32)) @@ -567,6 +569,31 @@ static struct clk_ops tegra_pllx_ops = { .set_rate = tegra2_pll_clk_set_rate, }; +static int tegra2_plle_clk_enable(struct clk *c) +{ + u32 val; + + pr_debug("%s on clock %s\n", __func__, c->name); + + mdelay(1); + + val = clk_readl(c->reg + PLL_BASE); + if (!(val & PLLE_MISC_READY)) + return -EBUSY; + + val = clk_readl(c->reg + PLL_BASE); + val |= PLL_BASE_ENABLE | PLL_BASE_BYPASS; + clk_writel(val, c->reg + PLL_BASE); + + return 0; +} + +static struct clk_ops tegra_plle_ops = { + .init = tegra2_pll_clk_init, + .enable = tegra2_plle_clk_enable, + .set_rate = tegra2_pll_clk_set_rate, +}; + /* Clock divider ops */ static void tegra2_pll_div_clk_init(struct clk *c) { @@ -1317,6 +1344,23 @@ static struct clk tegra_pll_x = { .max_rate = 1000000000, }; +static struct clk_pll_table tegra_pll_e_table[] = { + { 12000000, 100000000, 200, 24, 1, 0 }, + { 0, 0, 0, 0, 0, 0 }, +}; + +static struct clk tegra_pll_e = { + .name = "pll_e", + .flags = PLL_ALT_MISC_REG, + .ops = &tegra_plle_ops, + .input_min = 12000000, + .input_max = 12000000, + .max_rate = 100000000, + .parent = &tegra_clk_m, + .reg = 0xe8, + .pll_table = tegra_pll_e_table, +}; + static struct clk tegra_clk_d = { .name = "clk_d", .flags = PERIPH_NO_RESET, @@ -1626,6 +1670,9 @@ struct clk tegra_periph_clks[] = { PERIPH_CLK("csi", "csi", NULL, 52, 0, 72000000, mux_pllp_out3, 0), PERIPH_CLK("isp", "isp", NULL, 23, 0, 150000000, mux_clk_m, 0), /* same frequency as VI */ PERIPH_CLK("csus", "csus", NULL, 92, 0, 150000000, mux_clk_m, PERIPH_NO_RESET), + PERIPH_CLK("pex", NULL, "pex", 70, 0, 26000000, mux_clk_m, PERIPH_MANUAL_RESET), + PERIPH_CLK("afi", NULL, "afi", 72, 0, 26000000, mux_clk_m, PERIPH_MANUAL_RESET), + PERIPH_CLK("pcie_xclk", NULL, "pcie_xclk", 74, 0, 26000000, mux_clk_m, PERIPH_MANUAL_RESET), }; #define CLK_DUPLICATE(_name, _dev, _con) \ @@ -1679,6 +1726,7 @@ struct clk_lookup tegra_clk_lookups[] = { CLK(NULL, "pll_d_out0", &tegra_pll_d_out0), CLK(NULL, "pll_u", &tegra_pll_u), CLK(NULL, "pll_x", &tegra_pll_x), + CLK(NULL, "pll_e", &tegra_pll_e), CLK(NULL, "cclk", &tegra_clk_cclk), CLK(NULL, "sclk", &tegra_clk_sclk), CLK(NULL, "hclk", &tegra_clk_hclk), -- cgit v1.2.3 From 77ffc1465cec32489889d6bc9c288b7b0d2ce9fb Mon Sep 17 00:00:00 2001 From: Mike Rapoport Date: Mon, 27 Sep 2010 11:26:33 +0200 Subject: tegra: add PCI Express support Change-Id: Ibd0bcd46895eb88952b9db29e1f68572d39aae01 Signed-off-by: Mike Rapoport Acked-by: Arnd Bergmann CC: Russell King CC: Gary King Signed-off-by: Colin Cross --- arch/arm/mach-tegra/Kconfig | 4 + arch/arm/mach-tegra/Makefile | 1 + arch/arm/mach-tegra/board.h | 1 + arch/arm/mach-tegra/include/mach/hardware.h | 4 + arch/arm/mach-tegra/include/mach/io.h | 12 +- arch/arm/mach-tegra/pcie.c | 915 ++++++++++++++++++++++++++++ 6 files changed, 936 insertions(+), 1 deletion(-) create mode 100644 arch/arm/mach-tegra/pcie.c (limited to 'arch') diff --git a/arch/arm/mach-tegra/Kconfig b/arch/arm/mach-tegra/Kconfig index 8e32d9d7ec8d..acd9552f8ada 100644 --- a/arch/arm/mach-tegra/Kconfig +++ b/arch/arm/mach-tegra/Kconfig @@ -16,6 +16,10 @@ config ARCH_TEGRA_2x_SOC endchoice +config TEGRA_PCI + bool "PCI Express support" + select PCI + comment "Tegra board type" config MACH_HARMONY diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile index fc069a9e0f6b..b4c07e67a1fb 100644 --- a/arch/arm/mach-tegra/Makefile +++ b/arch/arm/mach-tegra/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_SMP) += platsmp.o localtimer.o headsmp.o obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o obj-$(CONFIG_TEGRA_SYSTEM_DMA) += dma.o obj-$(CONFIG_CPU_FREQ) += cpu-tegra.o +obj-$(CONFIG_TEGRA_PCI) += pcie.o obj-${CONFIG_MACH_HARMONY} += board-harmony.o obj-${CONFIG_MACH_HARMONY} += board-harmony-pinmux.o diff --git a/arch/arm/mach-tegra/board.h b/arch/arm/mach-tegra/board.h index 3d06354136f2..0de565ca37c5 100644 --- a/arch/arm/mach-tegra/board.h +++ b/arch/arm/mach-tegra/board.h @@ -27,6 +27,7 @@ void __init tegra_common_init(void); void __init tegra_map_common_io(void); void __init tegra_init_irq(void); void __init tegra_init_clock(void); +int __init tegra_pcie_init(bool init_port0, bool init_port1); extern struct sys_timer tegra_timer; #endif diff --git a/arch/arm/mach-tegra/include/mach/hardware.h b/arch/arm/mach-tegra/include/mach/hardware.h index 6014edf60d93..56e43b3a5b97 100644 --- a/arch/arm/mach-tegra/include/mach/hardware.h +++ b/arch/arm/mach-tegra/include/mach/hardware.h @@ -21,4 +21,8 @@ #ifndef __MACH_TEGRA_HARDWARE_H #define __MACH_TEGRA_HARDWARE_H +#define PCIBIOS_MIN_IO 0x1000 +#define PCIBIOS_MIN_MEM 0 +#define pcibios_assign_all_busses() 1 + #endif diff --git a/arch/arm/mach-tegra/include/mach/io.h b/arch/arm/mach-tegra/include/mach/io.h index 16f16189b5eb..f0981b1ac59e 100644 --- a/arch/arm/mach-tegra/include/mach/io.h +++ b/arch/arm/mach-tegra/include/mach/io.h @@ -21,7 +21,7 @@ #ifndef __MACH_TEGRA_IO_H #define __MACH_TEGRA_IO_H -#define IO_SPACE_LIMIT 0xffffffff +#define IO_SPACE_LIMIT 0xffff /* On TEGRA, many peripherals are very closely packed in * two 256MB io windows (that actually only use about 64KB @@ -73,10 +73,20 @@ void tegra_iounmap(volatile void __iomem *addr); #define IO_ADDRESS(n) ((void __iomem *) IO_TO_VIRT(n)) +#ifdef CONFIG_TEGRA_PCI +extern void __iomem *tegra_pcie_io_base; + +static inline void __iomem *__io(unsigned long addr) +{ + return tegra_pcie_io_base + (addr & IO_SPACE_LIMIT); +} +#else static inline void __iomem *__io(unsigned long addr) { return (void __iomem *)addr; } +#endif + #define __io(a) __io(a) #define __mem_pci(a) (a) diff --git a/arch/arm/mach-tegra/pcie.c b/arch/arm/mach-tegra/pcie.c new file mode 100644 index 000000000000..53f5fa37014a --- /dev/null +++ b/arch/arm/mach-tegra/pcie.c @@ -0,0 +1,915 @@ +/* + * arch/arm/mach-tegra/pci.c + * + * PCIe host controller driver for TEGRA(2) SOCs + * + * Copyright (c) 2010, CompuLab, Ltd. + * Author: Mike Rapoport + * + * Based on NVIDIA PCIe driver + * Copyright (c) 2008-2009, NVIDIA Corporation. + * + * Bits taken from arch/arm/mach-dove/pcie.c + * + * 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. + * + * This program is distributed in the hope that 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +/* register definitions */ +#define AFI_OFFSET 0x3800 +#define PADS_OFFSET 0x3000 +#define RP0_OFFSET 0x0000 +#define RP1_OFFSET 0x1000 + +#define AFI_AXI_BAR0_SZ 0x00 +#define AFI_AXI_BAR1_SZ 0x04 +#define AFI_AXI_BAR2_SZ 0x08 +#define AFI_AXI_BAR3_SZ 0x0c +#define AFI_AXI_BAR4_SZ 0x10 +#define AFI_AXI_BAR5_SZ 0x14 + +#define AFI_AXI_BAR0_START 0x18 +#define AFI_AXI_BAR1_START 0x1c +#define AFI_AXI_BAR2_START 0x20 +#define AFI_AXI_BAR3_START 0x24 +#define AFI_AXI_BAR4_START 0x28 +#define AFI_AXI_BAR5_START 0x2c + +#define AFI_FPCI_BAR0 0x30 +#define AFI_FPCI_BAR1 0x34 +#define AFI_FPCI_BAR2 0x38 +#define AFI_FPCI_BAR3 0x3c +#define AFI_FPCI_BAR4 0x40 +#define AFI_FPCI_BAR5 0x44 + +#define AFI_CACHE_BAR0_SZ 0x48 +#define AFI_CACHE_BAR0_ST 0x4c +#define AFI_CACHE_BAR1_SZ 0x50 +#define AFI_CACHE_BAR1_ST 0x54 + +#define AFI_MSI_BAR_SZ 0x60 +#define AFI_MSI_FPCI_BAR_ST 0x64 +#define AFI_MSI_AXI_BAR_ST 0x68 + +#define AFI_CONFIGURATION 0xac +#define AFI_CONFIGURATION_EN_FPCI (1 << 0) + +#define AFI_FPCI_ERROR_MASKS 0xb0 + +#define AFI_INTR_MASK 0xb4 +#define AFI_INTR_MASK_INT_MASK (1 << 0) +#define AFI_INTR_MASK_MSI_MASK (1 << 8) + +#define AFI_INTR_CODE 0xb8 +#define AFI_INTR_CODE_MASK 0xf +#define AFI_INTR_MASTER_ABORT 4 +#define AFI_INTR_LEGACY 6 + +#define AFI_INTR_SIGNATURE 0xbc +#define AFI_SM_INTR_ENABLE 0xc4 + +#define AFI_AFI_INTR_ENABLE 0xc8 +#define AFI_INTR_EN_INI_SLVERR (1 << 0) +#define AFI_INTR_EN_INI_DECERR (1 << 1) +#define AFI_INTR_EN_TGT_SLVERR (1 << 2) +#define AFI_INTR_EN_TGT_DECERR (1 << 3) +#define AFI_INTR_EN_TGT_WRERR (1 << 4) +#define AFI_INTR_EN_DFPCI_DECERR (1 << 5) +#define AFI_INTR_EN_AXI_DECERR (1 << 6) +#define AFI_INTR_EN_FPCI_TIMEOUT (1 << 7) + +#define AFI_PCIE_CONFIG 0x0f8 +#define AFI_PCIE_CONFIG_PCIEC0_DISABLE_DEVICE (1 << 1) +#define AFI_PCIE_CONFIG_PCIEC1_DISABLE_DEVICE (1 << 2) +#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_MASK (0xf << 20) +#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_SINGLE (0x0 << 20) +#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_DUAL (0x1 << 20) + +#define AFI_FUSE 0x104 +#define AFI_FUSE_PCIE_T0_GEN2_DIS (1 << 2) + +#define AFI_PEX0_CTRL 0x110 +#define AFI_PEX1_CTRL 0x118 +#define AFI_PEX_CTRL_RST (1 << 0) +#define AFI_PEX_CTRL_REFCLK_EN (1 << 3) + +#define RP_VEND_XP 0x00000F00 +#define RP_VEND_XP_DL_UP (1 << 30) + +#define RP_LINK_CONTROL_STATUS 0x00000090 +#define RP_LINK_CONTROL_STATUS_LINKSTAT_MASK 0x3fff0000 + +#define PADS_CTL_SEL 0x0000009C + +#define PADS_CTL 0x000000A0 +#define PADS_CTL_IDDQ_1L (1 << 0) +#define PADS_CTL_TX_DATA_EN_1L (1 << 6) +#define PADS_CTL_RX_DATA_EN_1L (1 << 10) + +#define PADS_PLL_CTL 0x000000B8 +#define PADS_PLL_CTL_RST_B4SM (1 << 1) +#define PADS_PLL_CTL_LOCKDET (1 << 8) +#define PADS_PLL_CTL_REFCLK_MASK (0x3 << 16) +#define PADS_PLL_CTL_REFCLK_INTERNAL_CML (0 << 16) +#define PADS_PLL_CTL_REFCLK_INTERNAL_CMOS (1 << 16) +#define PADS_PLL_CTL_REFCLK_EXTERNAL (2 << 16) +#define PADS_PLL_CTL_TXCLKREF_MASK (0x1 << 20) +#define PADS_PLL_CTL_TXCLKREF_DIV10 (0 << 20) +#define PADS_PLL_CTL_TXCLKREF_DIV5 (1 << 20) + +/* PMC access is required for PCIE xclk (un)clamping */ +#define PMC_SCRATCH42 0x144 +#define PMC_SCRATCH42_PCX_CLAMP (1 << 0) + +static void __iomem *reg_pmc_base = IO_ADDRESS(TEGRA_PMC_BASE); + +#define pmc_writel(value, reg) \ + __raw_writel(value, (u32)reg_pmc_base + (reg)) +#define pmc_readl(reg) \ + __raw_readl((u32)reg_pmc_base + (reg)) + +/* + * Tegra2 defines 1GB in the AXI address map for PCIe. + * + * That address space is split into different regions, with sizes and + * offsets as follows: + * + * 0x80000000 - 0x80003fff - PCI controller registers + * 0x80004000 - 0x80103fff - PCI configuration space + * 0x80104000 - 0x80203fff - PCI extended configuration space + * 0x80203fff - 0x803fffff - unused + * 0x80400000 - 0x8040ffff - downstream IO + * 0x80410000 - 0x8fffffff - unused + * 0x90000000 - 0x9fffffff - non-prefetchable memory + * 0xa0000000 - 0xbfffffff - prefetchable memory + */ +#define TEGRA_PCIE_BASE 0x80000000 + +#define PCIE_REGS_SZ SZ_16K +#define PCIE_CFG_OFF PCIE_REGS_SZ +#define PCIE_CFG_SZ SZ_1M +#define PCIE_EXT_CFG_OFF (PCIE_CFG_SZ + PCIE_CFG_OFF) +#define PCIE_EXT_CFG_SZ SZ_1M +#define PCIE_IOMAP_SZ (PCIE_REGS_SZ + PCIE_CFG_SZ + PCIE_EXT_CFG_SZ) + +#define MMIO_BASE (TEGRA_PCIE_BASE + SZ_4M) +#define MMIO_SIZE SZ_64K +#define MEM_BASE_0 (TEGRA_PCIE_BASE + SZ_256M) +#define MEM_SIZE_0 SZ_128M +#define MEM_BASE_1 (MEM_BASE_0 + MEM_SIZE_0) +#define MEM_SIZE_1 SZ_128M +#define PREFETCH_MEM_BASE_0 (MEM_BASE_1 + MEM_SIZE_1) +#define PREFETCH_MEM_SIZE_0 SZ_128M +#define PREFETCH_MEM_BASE_1 (PREFETCH_MEM_BASE_0 + PREFETCH_MEM_SIZE_0) +#define PREFETCH_MEM_SIZE_1 SZ_128M + +#define PCIE_CONF_BUS(b) ((b) << 16) +#define PCIE_CONF_DEV(d) ((d) << 11) +#define PCIE_CONF_FUNC(f) ((f) << 8) +#define PCIE_CONF_REG(r) \ + (((r) & ~0x3) | (((r) < 256) ? PCIE_CFG_OFF : PCIE_EXT_CFG_OFF)) + +struct tegra_pcie_port { + int index; + u8 root_bus_nr; + void __iomem *base; + + bool link_up; + + char io_space_name[16]; + char mem_space_name[16]; + char prefetch_space_name[20]; + struct resource res[3]; +}; + +struct tegra_pcie_info { + struct tegra_pcie_port port[2]; + int num_ports; + + void __iomem *regs; + struct resource res_mmio; + + struct clk *pex_clk; + struct clk *afi_clk; + struct clk *pcie_xclk; + struct clk *pll_e; +}; + +static struct tegra_pcie_info tegra_pcie = { + .res_mmio = { + .name = "PCI IO", + .start = MMIO_BASE, + .end = MMIO_BASE + MMIO_SIZE - 1, + .flags = IORESOURCE_MEM, + }, +}; + +void __iomem *tegra_pcie_io_base; +EXPORT_SYMBOL(tegra_pcie_io_base); + +static inline void afi_writel(u32 value, unsigned long offset) +{ + writel(value, offset + AFI_OFFSET + tegra_pcie.regs); +} + +static inline u32 afi_readl(unsigned long offset) +{ + return readl(offset + AFI_OFFSET + tegra_pcie.regs); +} + +static inline void pads_writel(u32 value, unsigned long offset) +{ + writel(value, offset + PADS_OFFSET + tegra_pcie.regs); +} + +static inline u32 pads_readl(unsigned long offset) +{ + return readl(offset + PADS_OFFSET + tegra_pcie.regs); +} + +static struct tegra_pcie_port *bus_to_port(int bus) +{ + int i; + + for (i = tegra_pcie.num_ports - 1; i >= 0; i--) { + int rbus = tegra_pcie.port[i].root_bus_nr; + if (rbus != -1 && rbus == bus) + break; + } + + return i >= 0 ? tegra_pcie.port + i : NULL; +} + +static int tegra_pcie_read_conf(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *val) +{ + struct tegra_pcie_port *pp = bus_to_port(bus->number); + void __iomem *addr; + + if (pp) { + if (devfn != 0) { + *val = 0xffffffff; + return PCIBIOS_DEVICE_NOT_FOUND; + } + + addr = pp->base + (where & ~0x3); + } else { + addr = tegra_pcie.regs + (PCIE_CONF_BUS(bus->number) + + PCIE_CONF_DEV(PCI_SLOT(devfn)) + + PCIE_CONF_FUNC(PCI_FUNC(devfn)) + + PCIE_CONF_REG(where)); + } + + *val = readl(addr); + + if (size == 1) + *val = (*val >> (8 * (where & 3))) & 0xff; + else if (size == 2) + *val = (*val >> (8 * (where & 3))) & 0xffff; + + return PCIBIOS_SUCCESSFUL; +} + +static int tegra_pcie_write_conf(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 val) +{ + struct tegra_pcie_port *pp = bus_to_port(bus->number); + void __iomem *addr; + + u32 mask; + u32 tmp; + + if (pp) { + if (devfn != 0) + return PCIBIOS_DEVICE_NOT_FOUND; + + addr = pp->base + (where & ~0x3); + } else { + addr = tegra_pcie.regs + (PCIE_CONF_BUS(bus->number) + + PCIE_CONF_DEV(PCI_SLOT(devfn)) + + PCIE_CONF_FUNC(PCI_FUNC(devfn)) + + PCIE_CONF_REG(where)); + } + + if (size == 4) { + writel(val, addr); + return PCIBIOS_SUCCESSFUL; + } + + if (size == 2) + mask = ~(0xffff << ((where & 0x3) * 8)); + else if (size == 1) + mask = ~(0xff << ((where & 0x3) * 8)); + else + return PCIBIOS_BAD_REGISTER_NUMBER; + + tmp = readl(addr) & mask; + tmp |= val << ((where & 0x3) * 8); + writel(tmp, addr); + + return PCIBIOS_SUCCESSFUL; +} + +static struct pci_ops tegra_pcie_ops = { + .read = tegra_pcie_read_conf, + .write = tegra_pcie_write_conf, +}; + +static void __devinit tegra_pcie_fixup_bridge(struct pci_dev *dev) +{ + u16 reg; + + if ((dev->class >> 16) == PCI_BASE_CLASS_BRIDGE) { + pci_read_config_word(dev, PCI_COMMAND, ®); + reg |= (PCI_COMMAND_IO | PCI_COMMAND_MEMORY | + PCI_COMMAND_MASTER | PCI_COMMAND_SERR); + pci_write_config_word(dev, PCI_COMMAND, reg); + } +} +DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, tegra_pcie_fixup_bridge); + +/* Tegra PCIE root complex wrongly reports device class */ +static void __devinit tegra_pcie_fixup_class(struct pci_dev *dev) +{ + dev->class = PCI_CLASS_BRIDGE_PCI << 8; +} +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA, 0x0bf0, tegra_pcie_fixup_class); +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA, 0x0bf1, tegra_pcie_fixup_class); + +/* Tegra PCIE requires relaxed ordering */ +static void __devinit tegra_pcie_relax_enable(struct pci_dev *dev) +{ + u16 val16; + int pos = pci_find_capability(dev, PCI_CAP_ID_EXP); + + if (pos <= 0) { + dev_err(&dev->dev, "skipping relaxed ordering fixup\n"); + return; + } + + pci_read_config_word(dev, pos + PCI_EXP_DEVCTL, &val16); + val16 |= PCI_EXP_DEVCTL_RELAX_EN; + pci_write_config_word(dev, pos + PCI_EXP_DEVCTL, val16); +} +DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, tegra_pcie_relax_enable); + +static int tegra_pcie_setup(int nr, struct pci_sys_data *sys) +{ + struct tegra_pcie_port *pp; + + if (nr >= tegra_pcie.num_ports) + return 0; + + pp = tegra_pcie.port + nr; + pp->root_bus_nr = sys->busnr; + + /* + * IORESOURCE_IO + */ + snprintf(pp->io_space_name, sizeof(pp->io_space_name), + "PCIe %d I/O", pp->index); + pp->io_space_name[sizeof(pp->io_space_name) - 1] = 0; + pp->res[0].name = pp->io_space_name; + if (pp->index == 0) { + pp->res[0].start = PCIBIOS_MIN_IO; + pp->res[0].end = pp->res[0].start + SZ_32K - 1; + } else { + pp->res[0].start = PCIBIOS_MIN_IO + SZ_32K; + pp->res[0].end = IO_SPACE_LIMIT; + } + pp->res[0].flags = IORESOURCE_IO; + if (request_resource(&ioport_resource, &pp->res[0])) + panic("Request PCIe IO resource failed\n"); + sys->resource[0] = &pp->res[0]; + + /* + * IORESOURCE_MEM + */ + snprintf(pp->mem_space_name, sizeof(pp->mem_space_name), + "PCIe %d MEM", pp->index); + pp->mem_space_name[sizeof(pp->mem_space_name) - 1] = 0; + pp->res[1].name = pp->mem_space_name; + if (pp->index == 0) { + pp->res[1].start = MEM_BASE_0; + pp->res[1].end = pp->res[1].start + MEM_SIZE_0 - 1; + } else { + pp->res[1].start = MEM_BASE_1; + pp->res[1].end = pp->res[1].start + MEM_SIZE_1 - 1; + } + pp->res[1].flags = IORESOURCE_MEM; + if (request_resource(&iomem_resource, &pp->res[1])) + panic("Request PCIe Memory resource failed\n"); + sys->resource[1] = &pp->res[1]; + + /* + * IORESOURCE_MEM | IORESOURCE_PREFETCH + */ + snprintf(pp->prefetch_space_name, sizeof(pp->prefetch_space_name), + "PCIe %d PREFETCH MEM", pp->index); + pp->prefetch_space_name[sizeof(pp->prefetch_space_name) - 1] = 0; + pp->res[2].name = pp->prefetch_space_name; + if (pp->index == 0) { + pp->res[2].start = PREFETCH_MEM_BASE_0; + pp->res[2].end = pp->res[2].start + PREFETCH_MEM_SIZE_0 - 1; + } else { + pp->res[2].start = PREFETCH_MEM_BASE_1; + pp->res[2].end = pp->res[2].start + PREFETCH_MEM_SIZE_1 - 1; + } + pp->res[2].flags = IORESOURCE_MEM | IORESOURCE_PREFETCH; + if (request_resource(&iomem_resource, &pp->res[2])) + panic("Request PCIe Prefetch Memory resource failed\n"); + sys->resource[2] = &pp->res[2]; + + return 1; +} + +static int tegra_pcie_map_irq(struct pci_dev *dev, u8 slot, u8 pin) +{ + return INT_PCIE_INTR; +} + +static struct pci_bus __init *tegra_pcie_scan_bus(int nr, + struct pci_sys_data *sys) +{ + struct tegra_pcie_port *pp; + + if (nr >= tegra_pcie.num_ports) + return 0; + + pp = tegra_pcie.port + nr; + pp->root_bus_nr = sys->busnr; + + return pci_scan_bus(sys->busnr, &tegra_pcie_ops, sys); +} + +static struct hw_pci tegra_pcie_hw __initdata = { + .nr_controllers = 2, + .setup = tegra_pcie_setup, + .scan = tegra_pcie_scan_bus, + .swizzle = pci_std_swizzle, + .map_irq = tegra_pcie_map_irq, +}; + + +static irqreturn_t tegra_pcie_isr(int irq, void *arg) +{ + const char *err_msg[] = { + "Unknown", + "AXI slave error", + "AXI decode error", + "Target abort", + "Master abort", + "Invalid write", + "Response decoding error", + "AXI response decoding error", + "Transcation timeout", + }; + + u32 code, signature; + + code = afi_readl(AFI_INTR_CODE) & AFI_INTR_CODE_MASK; + signature = afi_readl(AFI_INTR_SIGNATURE); + afi_writel(0, AFI_INTR_CODE); + + if (code == AFI_INTR_LEGACY) + return IRQ_NONE; + + if (code >= ARRAY_SIZE(err_msg)) + code = 0; + + /* + * do not pollute kernel log with master abort reports since they + * happen a lot during enumeration + */ + if (code == AFI_INTR_MASTER_ABORT) + pr_debug("PCIE: %s, signature: %08x\n", err_msg[code], signature); + else + pr_err("PCIE: %s, signature: %08x\n", err_msg[code], signature); + + return IRQ_HANDLED; +} + +static void tegra_pcie_setup_translations(void) +{ + u32 fpci_bar; + u32 size; + u32 axi_address; + + /* Bar 0: config Bar */ + fpci_bar = ((u32)0xfdff << 16); + size = PCIE_CFG_SZ; + axi_address = TEGRA_PCIE_BASE + PCIE_CFG_OFF; + afi_writel(axi_address, AFI_AXI_BAR0_START); + afi_writel(size >> 12, AFI_AXI_BAR0_SZ); + afi_writel(fpci_bar, AFI_FPCI_BAR0); + + /* Bar 1: extended config Bar */ + fpci_bar = ((u32)0xfe1 << 20); + size = PCIE_EXT_CFG_SZ; + axi_address = TEGRA_PCIE_BASE + PCIE_EXT_CFG_OFF; + afi_writel(axi_address, AFI_AXI_BAR1_START); + afi_writel(size >> 12, AFI_AXI_BAR1_SZ); + afi_writel(fpci_bar, AFI_FPCI_BAR1); + + /* Bar 2: downstream IO bar */ + fpci_bar = ((__u32)0xfdfc << 16); + size = MMIO_SIZE; + axi_address = MMIO_BASE; + afi_writel(axi_address, AFI_AXI_BAR2_START); + afi_writel(size >> 12, AFI_AXI_BAR2_SZ); + afi_writel(fpci_bar, AFI_FPCI_BAR2); + + /* Bar 3: prefetchable memory BAR */ + fpci_bar = (((PREFETCH_MEM_BASE_0 >> 12) & 0x0fffffff) << 4) | 0x1; + size = PREFETCH_MEM_SIZE_0 + PREFETCH_MEM_SIZE_1; + axi_address = PREFETCH_MEM_BASE_0; + afi_writel(axi_address, AFI_AXI_BAR3_START); + afi_writel(size >> 12, AFI_AXI_BAR3_SZ); + afi_writel(fpci_bar, AFI_FPCI_BAR3); + + /* Bar 4: non prefetchable memory BAR */ + fpci_bar = (((MEM_BASE_0 >> 12) & 0x0FFFFFFF) << 4) | 0x1; + size = MEM_SIZE_0 + MEM_SIZE_1; + axi_address = MEM_BASE_0; + afi_writel(axi_address, AFI_AXI_BAR4_START); + afi_writel(size >> 12, AFI_AXI_BAR4_SZ); + afi_writel(fpci_bar, AFI_FPCI_BAR4); + + /* Bar 5: NULL out the remaining BAR as it is not used */ + fpci_bar = 0; + size = 0; + axi_address = 0; + afi_writel(axi_address, AFI_AXI_BAR5_START); + afi_writel(size >> 12, AFI_AXI_BAR5_SZ); + afi_writel(fpci_bar, AFI_FPCI_BAR5); + + /* map all upstream transactions as uncached */ + afi_writel(PHYS_OFFSET, AFI_CACHE_BAR0_ST); + afi_writel(0, AFI_CACHE_BAR0_SZ); + afi_writel(0, AFI_CACHE_BAR1_ST); + afi_writel(0, AFI_CACHE_BAR1_SZ); + + /* No MSI */ + afi_writel(0, AFI_MSI_FPCI_BAR_ST); + afi_writel(0, AFI_MSI_BAR_SZ); + afi_writel(0, AFI_MSI_AXI_BAR_ST); + afi_writel(0, AFI_MSI_BAR_SZ); +} + +static void tegra_pcie_enable_controller(void) +{ + u32 val, reg; + int i; + + /* Enable slot clock and pulse the reset signals */ + for (i = 0, reg = AFI_PEX0_CTRL; i < 2; i++, reg += 0x8) { + val = afi_readl(reg) | AFI_PEX_CTRL_REFCLK_EN; + afi_writel(val, reg); + val &= ~AFI_PEX_CTRL_RST; + afi_writel(val, reg); + + val = afi_readl(reg) | AFI_PEX_CTRL_RST; + afi_writel(val, reg); + } + + /* Enable dual controller and both ports */ + val = afi_readl(AFI_PCIE_CONFIG); + val &= ~(AFI_PCIE_CONFIG_PCIEC0_DISABLE_DEVICE | + AFI_PCIE_CONFIG_PCIEC1_DISABLE_DEVICE | + AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_MASK); + val |= AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_DUAL; + afi_writel(val, AFI_PCIE_CONFIG); + + val = afi_readl(AFI_FUSE) & ~AFI_FUSE_PCIE_T0_GEN2_DIS; + afi_writel(val, AFI_FUSE); + + /* Initialze internal PHY, enable up to 16 PCIE lanes */ + pads_writel(0x0, PADS_CTL_SEL); + + /* override IDDQ to 1 on all 4 lanes */ + val = pads_readl(PADS_CTL) | PADS_CTL_IDDQ_1L; + pads_writel(val, PADS_CTL); + + /* + * set up PHY PLL inputs select PLLE output as refclock, + * set TX ref sel to div10 (not div5) + */ + val = pads_readl(PADS_PLL_CTL); + val &= ~(PADS_PLL_CTL_REFCLK_MASK | PADS_PLL_CTL_TXCLKREF_MASK); + val |= (PADS_PLL_CTL_REFCLK_INTERNAL_CML | PADS_PLL_CTL_TXCLKREF_DIV10); + pads_writel(val, PADS_PLL_CTL); + + /* take PLL out of reset */ + val = pads_readl(PADS_PLL_CTL) | PADS_PLL_CTL_RST_B4SM; + pads_writel(val, PADS_PLL_CTL); + + /* + * Hack, set the clock voltage to the DEFAULT provided by hw folks. + * This doesn't exist in the documentation + */ + pads_writel(0xfa5cfa5c, 0xc8); + + /* Wait for the PLL to lock */ + do { + val = pads_readl(PADS_PLL_CTL); + } while (!(val & PADS_PLL_CTL_LOCKDET)); + + /* turn off IDDQ override */ + val = pads_readl(PADS_CTL) & ~PADS_CTL_IDDQ_1L; + pads_writel(val, PADS_CTL); + + /* enable TX/RX data */ + val = pads_readl(PADS_CTL); + val |= (PADS_CTL_TX_DATA_EN_1L | PADS_CTL_RX_DATA_EN_1L); + pads_writel(val, PADS_CTL); + + /* Take the PCIe interface module out of reset */ + tegra_periph_reset_deassert(tegra_pcie.pcie_xclk); + + /* Finally enable PCIe */ + val = afi_readl(AFI_CONFIGURATION) | AFI_CONFIGURATION_EN_FPCI; + afi_writel(val, AFI_CONFIGURATION); + + val = (AFI_INTR_EN_INI_SLVERR | AFI_INTR_EN_INI_DECERR | + AFI_INTR_EN_TGT_SLVERR | AFI_INTR_EN_TGT_DECERR | + AFI_INTR_EN_TGT_WRERR | AFI_INTR_EN_DFPCI_DECERR); + afi_writel(val, AFI_AFI_INTR_ENABLE); + afi_writel(0xffffffff, AFI_SM_INTR_ENABLE); + + /* FIXME: No MSI for now, only INT */ + afi_writel(AFI_INTR_MASK_INT_MASK, AFI_INTR_MASK); + + /* Disable all execptions */ + afi_writel(0, AFI_FPCI_ERROR_MASKS); + + return; +} + +static void tegra_pcie_xclk_clamp(bool clamp) +{ + u32 reg; + + reg = pmc_readl(PMC_SCRATCH42) & ~PMC_SCRATCH42_PCX_CLAMP; + + if (clamp) + reg |= PMC_SCRATCH42_PCX_CLAMP; + + pmc_writel(reg, PMC_SCRATCH42); +} + +static int tegra_pcie_power_on(void) +{ + tegra_pcie_xclk_clamp(true); + tegra_periph_reset_assert(tegra_pcie.pcie_xclk); + tegra_pcie_xclk_clamp(false); + + clk_enable(tegra_pcie.afi_clk); + clk_enable(tegra_pcie.pex_clk); + return clk_enable(tegra_pcie.pll_e); +} + +static void tegra_pcie_power_off(void) +{ + tegra_periph_reset_assert(tegra_pcie.pcie_xclk); + tegra_periph_reset_assert(tegra_pcie.afi_clk); + tegra_periph_reset_assert(tegra_pcie.pex_clk); + + tegra_pcie_xclk_clamp(true); +} + +static int tegra_pcie_clocks_get(void) +{ + int err; + + tegra_pcie.pex_clk = clk_get(NULL, "pex"); + if (IS_ERR(tegra_pcie.pex_clk)) + return PTR_ERR(tegra_pcie.pex_clk); + + tegra_pcie.afi_clk = clk_get(NULL, "afi"); + if (IS_ERR(tegra_pcie.afi_clk)) { + err = PTR_ERR(tegra_pcie.afi_clk); + goto err_afi_clk; + } + + tegra_pcie.pcie_xclk = clk_get(NULL, "pcie_xclk"); + if (IS_ERR(tegra_pcie.pcie_xclk)) { + err = PTR_ERR(tegra_pcie.pcie_xclk); + goto err_pcie_xclk; + } + + tegra_pcie.pll_e = clk_get_sys(NULL, "pll_e"); + if (IS_ERR(tegra_pcie.pll_e)) { + err = PTR_ERR(tegra_pcie.pll_e); + goto err_pll_e; + } + + return 0; + +err_pll_e: + clk_put(tegra_pcie.pcie_xclk); +err_pcie_xclk: + clk_put(tegra_pcie.afi_clk); +err_afi_clk: + clk_put(tegra_pcie.pex_clk); + + return err; +} + +static void tegra_pcie_clocks_put(void) +{ + clk_put(tegra_pcie.pll_e); + clk_put(tegra_pcie.pcie_xclk); + clk_put(tegra_pcie.afi_clk); + clk_put(tegra_pcie.pex_clk); +} + +static int __init tegra_pcie_get_resources(void) +{ + struct resource *res_mmio = &tegra_pcie.res_mmio; + int err; + + err = tegra_pcie_clocks_get(); + if (err) { + pr_err("PCIE: failed to get clocks: %d\n", err); + return err; + } + + err = tegra_pcie_power_on(); + if (err) { + pr_err("PCIE: failed to power up: %d\n", err); + goto err_pwr_on; + } + + tegra_pcie.regs = ioremap_nocache(TEGRA_PCIE_BASE, PCIE_IOMAP_SZ); + if (tegra_pcie.regs == NULL) { + pr_err("PCIE: Failed to map PCI/AFI registers\n"); + err = -ENOMEM; + goto err_map_reg; + } + + err = request_resource(&iomem_resource, res_mmio); + if (err) { + pr_err("PCIE: Failed to request resources: %d\n", err); + goto err_req_io; + } + + tegra_pcie_io_base = ioremap_nocache(res_mmio->start, + resource_size(res_mmio)); + if (tegra_pcie_io_base == NULL) { + pr_err("PCIE: Failed to map IO\n"); + err = -ENOMEM; + goto err_map_io; + } + + err = request_irq(INT_PCIE_INTR, tegra_pcie_isr, + IRQF_SHARED, "PCIE", &tegra_pcie); + if (err) { + pr_err("PCIE: Failed to register IRQ: %d\n", err); + goto err_irq; + } + set_irq_flags(INT_PCIE_INTR, IRQF_VALID); + + return 0; + +err_irq: + iounmap(tegra_pcie_io_base); +err_map_io: + release_resource(&tegra_pcie.res_mmio); +err_req_io: + iounmap(tegra_pcie.regs); +err_map_reg: + tegra_pcie_power_off(); +err_pwr_on: + tegra_pcie_clocks_put(); + + return err; +} + +/* + * FIXME: If there are no PCIe cards attached, then calling this function + * can result in the increase of the bootup time as there are big timeout + * loops. + */ +#define TEGRA_PCIE_LINKUP_TIMEOUT 200 /* up to 1.2 seconds */ +static bool tegra_pcie_check_link(struct tegra_pcie_port *pp, int idx, + u32 reset_reg) +{ + u32 reg; + int retries = 3; + int timeout; + + do { + timeout = TEGRA_PCIE_LINKUP_TIMEOUT; + while (timeout) { + reg = readl(pp->base + RP_VEND_XP); + + if (reg & RP_VEND_XP_DL_UP) + break; + + mdelay(1); + timeout--; + } + + if (!timeout) { + pr_err("PCIE: port %d: link down, retrying\n", idx); + goto retry; + } + + timeout = TEGRA_PCIE_LINKUP_TIMEOUT; + while (timeout) { + reg = readl(pp->base + RP_LINK_CONTROL_STATUS); + + if (reg & 0x20000000) + return true; + + mdelay(1); + timeout--; + } + +retry: + /* Pulse the PEX reset */ + reg = afi_readl(reset_reg) | AFI_PEX_CTRL_RST; + afi_writel(reg, reset_reg); + mdelay(1); + reg = afi_readl(reset_reg) & ~AFI_PEX_CTRL_RST; + afi_writel(reg, reset_reg); + + retries--; + } while (retries); + + return false; +} + +static void __init tegra_pcie_add_port(int index, u32 offset, u32 reset_reg) +{ + struct tegra_pcie_port *pp; + + pp = tegra_pcie.port + tegra_pcie.num_ports; + + pp->index = -1; + pp->base = tegra_pcie.regs + offset; + pp->link_up = tegra_pcie_check_link(pp, index, reset_reg); + + if (!pp->link_up) { + pp->base = NULL; + printk(KERN_INFO "PCIE: port %d: link down, ignoring\n", index); + return; + } + + tegra_pcie.num_ports++; + pp->index = index; + pp->root_bus_nr = -1; + memset(pp->res, 0, sizeof(pp->res)); +} + +int __init tegra_pcie_init(bool init_port0, bool init_port1) +{ + int err; + + if (!(init_port0 || init_port1)) + return -ENODEV; + + err = tegra_pcie_get_resources(); + if (err) + return err; + + tegra_pcie_enable_controller(); + + /* setup the AFI address translations */ + tegra_pcie_setup_translations(); + + if (init_port0) + tegra_pcie_add_port(0, RP0_OFFSET, AFI_PEX0_CTRL); + + if (init_port1) + tegra_pcie_add_port(1, RP1_OFFSET, AFI_PEX1_CTRL); + + pci_common_init(&tegra_pcie_hw); + + return 0; +} -- cgit v1.2.3 From f2a443931e6c7616aa023f55ddfdbca8943ab0e9 Mon Sep 17 00:00:00 2001 From: Mike Rapoport Date: Mon, 27 Sep 2010 11:26:34 +0200 Subject: tegra: harmony: enable PCI Express Signed-off-by: Mike Rapoport CC: Olof Johansson CC: Gary King Signed-off-by: Colin Cross --- arch/arm/mach-tegra/Makefile | 1 + arch/arm/mach-tegra/board-harmony-pcie.c | 57 ++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 arch/arm/mach-tegra/board-harmony-pcie.c (limited to 'arch') diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile index b4c07e67a1fb..cdbc68e4c0ca 100644 --- a/arch/arm/mach-tegra/Makefile +++ b/arch/arm/mach-tegra/Makefile @@ -18,3 +18,4 @@ obj-$(CONFIG_TEGRA_PCI) += pcie.o obj-${CONFIG_MACH_HARMONY} += board-harmony.o obj-${CONFIG_MACH_HARMONY} += board-harmony-pinmux.o +obj-${CONFIG_MACH_HARMONY} += board-harmony-pcie.o diff --git a/arch/arm/mach-tegra/board-harmony-pcie.c b/arch/arm/mach-tegra/board-harmony-pcie.c new file mode 100644 index 000000000000..f7e7d4514b6a --- /dev/null +++ b/arch/arm/mach-tegra/board-harmony-pcie.c @@ -0,0 +1,57 @@ +/* + * arch/arm/mach-tegra/board-harmony-pcie.c + * + * Copyright (C) 2010 CompuLab, Ltd. + * Mike Rapoport + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that 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. + * + */ + +#include +#include +#include +#include + +#include + +#include +#include "board.h" + +#ifdef CONFIG_TEGRA_PCI + +static int __init harmony_pcie_init(void) +{ + int err; + + if (!machine_is_harmony()) + return 0; + + tegra_pinmux_set_tristate(TEGRA_PINGROUP_GPV, TEGRA_TRI_NORMAL); + tegra_pinmux_set_tristate(TEGRA_PINGROUP_SLXA, TEGRA_TRI_NORMAL); + tegra_pinmux_set_tristate(TEGRA_PINGROUP_SLXK, TEGRA_TRI_NORMAL); + + err = tegra_pcie_init(true, true); + if (err) + goto err_pcie; + + return 0; + +err_pcie: + tegra_pinmux_set_tristate(TEGRA_PINGROUP_GPV, TEGRA_TRI_TRISTATE); + tegra_pinmux_set_tristate(TEGRA_PINGROUP_SLXA, TEGRA_TRI_TRISTATE); + tegra_pinmux_set_tristate(TEGRA_PINGROUP_SLXK, TEGRA_TRI_TRISTATE); + + return err; +} + +subsys_initcall(harmony_pcie_init); + +#endif -- cgit v1.2.3