From 13ae3d5bdf737d6078a562154ff4fef0ba5d0de1 Mon Sep 17 00:00:00 2001 From: Olof Johansson Date: Thu, 22 Dec 2011 14:17:40 +0000 Subject: ARM: tegra: Don't WARN_ON() for too early dma channel allocations Since we'll do opportunistic allocations before the dma subsystem is enabled we want just silent failures and retries instead. Signed-off-by: Olof Johansson --- arch/arm/mach-tegra/dma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'arch/arm/mach-tegra') diff --git a/arch/arm/mach-tegra/dma.c b/arch/arm/mach-tegra/dma.c index c0cf967e47d3..98b33c89b2c5 100644 --- a/arch/arm/mach-tegra/dma.c +++ b/arch/arm/mach-tegra/dma.c @@ -357,7 +357,7 @@ struct tegra_dma_channel *tegra_dma_allocate_channel(int mode) int channel; struct tegra_dma_channel *ch = NULL; - if (WARN_ON(!tegra_dma_initialized)) + if (!tegra_dma_initialized) return NULL; mutex_lock(&tegra_dma_lock); -- cgit v1.2.3-55-g7522 From e2f91578b35347341482f6af9e4fcf3174531efd Mon Sep 17 00:00:00 2001 From: Olof Johansson Date: Wed, 12 Oct 2011 23:52:29 -0700 Subject: ARM: tegra: use APB DMA for accessing APB devices Tegra2 hangs if APB registers are accessed from the cpu during an apb dma operation. The workaround is to use apb dma to read/write the registers instead. There is a dependency loop between fuses, clocks, and APBDMA. If dma is enabled, fuse reads must go through APBDMA to avoid corruption due to a hw bug. APBDMA requires a clock to be enabled. Clocks must read a fuse to determine allowable cpu frequencies. Separate out the fuse DMA initialization, and allow the fuse read and write functions to be called without using DMA before the DMA initialization has been completed. Access to the fuses before APBDMA is initialized won't hit the hardware bug because nothing else can be using DMA. Original fuse registar access code from Varun Wadekar , improved by Colin Cross and later moved to separate driver by Jon Mayo . Major refactoring/cleanup by Olof Johansson . Changes since v1: * fix 'return false' on error condition * dequeue dma ops in case of timeout From: Jon Mayo . Signed-off-by: Jon Mayo . Signed-off-by: Olof Johansson Acked-by: Stephen Warren --- arch/arm/mach-tegra/Makefile | 2 +- arch/arm/mach-tegra/apbio.c | 145 +++++++++++++++++++++++++++++++++++++++++++ arch/arm/mach-tegra/apbio.h | 39 ++++++++++++ arch/arm/mach-tegra/dma.c | 2 + 4 files changed, 187 insertions(+), 1 deletion(-) create mode 100644 arch/arm/mach-tegra/apbio.c create mode 100644 arch/arm/mach-tegra/apbio.h (limited to 'arch/arm/mach-tegra') diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile index e120ff54f663..23d15fba3843 100644 --- a/arch/arm/mach-tegra/Makefile +++ b/arch/arm/mach-tegra/Makefile @@ -15,7 +15,7 @@ obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += pinmux-tegra30-tables.o obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += board-dt-tegra30.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_TEGRA_SYSTEM_DMA) += dma.o apbio.o obj-$(CONFIG_CPU_FREQ) += cpu-tegra.o obj-$(CONFIG_TEGRA_PCI) += pcie.o obj-$(CONFIG_USB_SUPPORT) += usb_phy.o diff --git a/arch/arm/mach-tegra/apbio.c b/arch/arm/mach-tegra/apbio.c new file mode 100644 index 000000000000..e75451e517bd --- /dev/null +++ b/arch/arm/mach-tegra/apbio.c @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2010 NVIDIA Corporation. + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "apbio.h" + +static DEFINE_MUTEX(tegra_apb_dma_lock); + +static struct tegra_dma_channel *tegra_apb_dma; +static u32 *tegra_apb_bb; +static dma_addr_t tegra_apb_bb_phys; +static DECLARE_COMPLETION(tegra_apb_wait); + +bool tegra_apb_init(void) +{ + struct tegra_dma_channel *ch; + + mutex_lock(&tegra_apb_dma_lock); + + /* Check to see if we raced to setup */ + if (tegra_apb_dma) + goto out; + + ch = tegra_dma_allocate_channel(TEGRA_DMA_MODE_ONESHOT | + TEGRA_DMA_SHARED); + + if (!ch) + goto out_fail; + + tegra_apb_bb = dma_alloc_coherent(NULL, sizeof(u32), + &tegra_apb_bb_phys, GFP_KERNEL); + if (!tegra_apb_bb) { + pr_err("%s: can not allocate bounce buffer\n", __func__); + tegra_dma_free_channel(ch); + goto out_fail; + } + + tegra_apb_dma = ch; +out: + mutex_unlock(&tegra_apb_dma_lock); + return true; + +out_fail: + mutex_unlock(&tegra_apb_dma_lock); + return false; +} + +static void apb_dma_complete(struct tegra_dma_req *req) +{ + complete(&tegra_apb_wait); +} + +u32 tegra_apb_readl(unsigned long offset) +{ + struct tegra_dma_req req; + int ret; + + if (!tegra_apb_dma && !tegra_apb_init()) + return readl(IO_TO_VIRT(offset)); + + mutex_lock(&tegra_apb_dma_lock); + req.complete = apb_dma_complete; + req.to_memory = 1; + req.dest_addr = tegra_apb_bb_phys; + req.dest_bus_width = 32; + req.dest_wrap = 1; + req.source_addr = offset; + req.source_bus_width = 32; + req.source_wrap = 4; + req.req_sel = TEGRA_DMA_REQ_SEL_CNTR; + req.size = 4; + + INIT_COMPLETION(tegra_apb_wait); + + tegra_dma_enqueue_req(tegra_apb_dma, &req); + + ret = wait_for_completion_timeout(&tegra_apb_wait, + msecs_to_jiffies(50)); + + if (WARN(ret == 0, "apb read dma timed out")) { + tegra_dma_dequeue_req(tegra_apb_dma, &req); + *(u32 *)tegra_apb_bb = 0; + } + + mutex_unlock(&tegra_apb_dma_lock); + return *((u32 *)tegra_apb_bb); +} + +void tegra_apb_writel(u32 value, unsigned long offset) +{ + struct tegra_dma_req req; + int ret; + + if (!tegra_apb_dma && !tegra_apb_init()) { + writel(value, IO_TO_VIRT(offset)); + return; + } + + mutex_lock(&tegra_apb_dma_lock); + *((u32 *)tegra_apb_bb) = value; + req.complete = apb_dma_complete; + req.to_memory = 0; + req.dest_addr = offset; + req.dest_wrap = 4; + req.dest_bus_width = 32; + req.source_addr = tegra_apb_bb_phys; + req.source_bus_width = 32; + req.source_wrap = 1; + req.req_sel = TEGRA_DMA_REQ_SEL_CNTR; + req.size = 4; + + INIT_COMPLETION(tegra_apb_wait); + + tegra_dma_enqueue_req(tegra_apb_dma, &req); + + ret = wait_for_completion_timeout(&tegra_apb_wait, + msecs_to_jiffies(50)); + + if (WARN(ret == 0, "apb write dma timed out")) + tegra_dma_dequeue_req(tegra_apb_dma, &req); + + mutex_unlock(&tegra_apb_dma_lock); +} diff --git a/arch/arm/mach-tegra/apbio.h b/arch/arm/mach-tegra/apbio.h new file mode 100644 index 000000000000..8b49e8c89a64 --- /dev/null +++ b/arch/arm/mach-tegra/apbio.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2010 NVIDIA Corporation. + * 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_APBIO_H +#define __MACH_TEGRA_APBIO_H + +#ifdef CONFIG_TEGRA_SYSTEM_DMA + +u32 tegra_apb_readl(unsigned long offset); +void tegra_apb_writel(u32 value, unsigned long offset); + +#else +#include +#include + +static inline u32 tegra_apb_readl(unsigned long offset) +{ + return readl(IO_TO_VIRT(offset)); +} + +static inline void tegra_apb_writel(u32 value, unsigned long offset) +{ + writel(value, IO_TO_VIRT(offset)); +} +#endif + +#endif diff --git a/arch/arm/mach-tegra/dma.c b/arch/arm/mach-tegra/dma.c index 98b33c89b2c5..122e46770d93 100644 --- a/arch/arm/mach-tegra/dma.c +++ b/arch/arm/mach-tegra/dma.c @@ -33,6 +33,8 @@ #include #include +#include "apbio.h" + #define APB_DMA_GEN 0x000 #define GEN_ENABLE (1<<31) -- cgit v1.2.3-55-g7522 From d262f49d10554ae2908e6d1d0e93fa736c4c0d06 Mon Sep 17 00:00:00 2001 From: Olof Johansson Date: Thu, 13 Oct 2011 00:14:08 -0700 Subject: ARM: tegra: fuse: use apbio dma for register access Use the apbio dma functions for accessing the fuse registers. Signed-off-by: Olof Johansson --- arch/arm/mach-tegra/fuse.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) (limited to 'arch/arm/mach-tegra') diff --git a/arch/arm/mach-tegra/fuse.c b/arch/arm/mach-tegra/fuse.c index 1fa26d9a1a68..daf3f572acc8 100644 --- a/arch/arm/mach-tegra/fuse.c +++ b/arch/arm/mach-tegra/fuse.c @@ -23,20 +23,16 @@ #include #include "fuse.h" +#include "apbio.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) +static inline u32 tegra_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)); + return tegra_apb_readl(TEGRA_FUSE_BASE + offset); } void tegra_init_fuse(void) @@ -54,15 +50,15 @@ unsigned long long tegra_chip_uid(void) { unsigned long long lo, hi; - lo = fuse_readl(FUSE_UID_LOW); - hi = fuse_readl(FUSE_UID_HIGH); + lo = tegra_fuse_readl(FUSE_UID_LOW); + hi = tegra_fuse_readl(FUSE_UID_HIGH); return (hi << 32ull) | lo; } int tegra_sku_id(void) { int sku_id; - u32 reg = fuse_readl(FUSE_SKU_INFO); + u32 reg = tegra_fuse_readl(FUSE_SKU_INFO); sku_id = reg & 0xFF; return sku_id; } @@ -70,7 +66,7 @@ int tegra_sku_id(void) int tegra_cpu_process_id(void) { int cpu_process_id; - u32 reg = fuse_readl(FUSE_SPARE_BIT); + u32 reg = tegra_fuse_readl(FUSE_SPARE_BIT); cpu_process_id = (reg >> 6) & 3; return cpu_process_id; } @@ -78,7 +74,7 @@ int tegra_cpu_process_id(void) int tegra_core_process_id(void) { int core_process_id; - u32 reg = fuse_readl(FUSE_SPARE_BIT); + u32 reg = tegra_fuse_readl(FUSE_SPARE_BIT); core_process_id = (reg >> 12) & 3; return core_process_id; } -- cgit v1.2.3-55-g7522 From 9a1086da345cea8b2d1f01b47e5bbd81d640d642 Mon Sep 17 00:00:00 2001 From: Olof Johansson Date: Thu, 13 Oct 2011 00:31:20 -0700 Subject: ARM: tegra: fuse: add functions to access chip revision Add function to get chip revision, and print it out at boot time. Restructure the fuse access to just use cached variables instead of always reading the fuses, and export those variables directly instead of using accessor functions. Add a SKU ID table of currently known values. Based on code originally by Colin Cross . Changes since v1: * Add A01 minor rev support * Don't decode for A03p on anything but T2x Signed-off-by: Olof Johansson Acked-by: Stephen Warren --- arch/arm/mach-tegra/fuse.c | 85 +++++++++++++++++++++++++------------ arch/arm/mach-tegra/fuse.h | 32 +++++++++++--- arch/arm/mach-tegra/tegra2_clocks.c | 2 +- 3 files changed, 86 insertions(+), 33 deletions(-) (limited to 'arch/arm/mach-tegra') diff --git a/arch/arm/mach-tegra/fuse.c b/arch/arm/mach-tegra/fuse.c index daf3f572acc8..b1895c53ed60 100644 --- a/arch/arm/mach-tegra/fuse.c +++ b/arch/arm/mach-tegra/fuse.c @@ -30,20 +30,75 @@ #define FUSE_SKU_INFO 0x110 #define FUSE_SPARE_BIT 0x200 +int tegra_sku_id; +int tegra_cpu_process_id; +int tegra_core_process_id; +enum tegra_revision tegra_revision; + +static const char *tegra_revision_name[TEGRA_REVISION_MAX] = { + [TEGRA_REVISION_UNKNOWN] = "unknown", + [TEGRA_REVISION_A01] = "A01", + [TEGRA_REVISION_A02] = "A02", + [TEGRA_REVISION_A03] = "A03", + [TEGRA_REVISION_A03p] = "A03 prime", + [TEGRA_REVISION_A04] = "A04", +}; + static inline u32 tegra_fuse_readl(unsigned long offset) { return tegra_apb_readl(TEGRA_FUSE_BASE + offset); } +static inline bool get_spare_fuse(int bit) +{ + return tegra_fuse_readl(FUSE_SPARE_BIT + bit * 4); +} + +static enum tegra_revision tegra_get_revision(void) +{ + void __iomem *chip_id = IO_ADDRESS(TEGRA_APB_MISC_BASE) + 0x804; + u32 id = readl(chip_id); + u32 minor_rev = (id >> 16) & 0xf; + u32 chipid = (id >> 8) & 0xff; + + switch (minor_rev) { + case 1: + return TEGRA_REVISION_A01; + case 2: + return TEGRA_REVISION_A02; + case 3: + if (chipid == 0x20 && (get_spare_fuse(18) || get_spare_fuse(19))) + return TEGRA_REVISION_A03p; + else + return TEGRA_REVISION_A03; + case 4: + return TEGRA_REVISION_A04; + default: + return TEGRA_REVISION_UNKNOWN; + } +} + 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()); + reg = tegra_fuse_readl(FUSE_SKU_INFO); + tegra_sku_id = reg & 0xFF; + + reg = tegra_fuse_readl(FUSE_SPARE_BIT); + tegra_cpu_process_id = (reg >> 6) & 3; + + reg = tegra_fuse_readl(FUSE_SPARE_BIT); + tegra_core_process_id = (reg >> 12) & 3; + + tegra_revision = tegra_get_revision(); + + pr_info("Tegra Revision: %s SKU: %d CPU Process: %d Core Process: %d\n", + tegra_revision_name[tegra_get_revision()], + tegra_sku_id, tegra_cpu_process_id, + tegra_core_process_id); } unsigned long long tegra_chip_uid(void) @@ -54,27 +109,3 @@ unsigned long long tegra_chip_uid(void) hi = tegra_fuse_readl(FUSE_UID_HIGH); return (hi << 32ull) | lo; } - -int tegra_sku_id(void) -{ - int sku_id; - u32 reg = tegra_fuse_readl(FUSE_SKU_INFO); - sku_id = reg & 0xFF; - return sku_id; -} - -int tegra_cpu_process_id(void) -{ - int cpu_process_id; - u32 reg = tegra_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 = tegra_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 index 584b2e27dbda..7576aaf6865c 100644 --- a/arch/arm/mach-tegra/fuse.h +++ b/arch/arm/mach-tegra/fuse.h @@ -1,6 +1,4 @@ /* - * arch/arm/mach-tegra/fuse.c - * * Copyright (C) 2010 Google, Inc. * * Author: @@ -17,8 +15,32 @@ * */ +#ifndef __MACH_TEGRA_FUSE_H +#define __MACH_TEGRA_FUSE_H + +enum tegra_revision { + TEGRA_REVISION_UNKNOWN = 0, + TEGRA_REVISION_A01, + TEGRA_REVISION_A02, + TEGRA_REVISION_A03, + TEGRA_REVISION_A03p, + TEGRA_REVISION_A04, + TEGRA_REVISION_MAX, +}; + +#define SKU_ID_T20 8 +#define SKU_ID_T25SE 20 +#define SKU_ID_AP25 23 +#define SKU_ID_T25 24 +#define SKU_ID_AP25E 27 +#define SKU_ID_T25E 28 + +extern int tegra_sku_id; +extern int tegra_cpu_process_id; +extern int tegra_core_process_id; +extern enum tegra_revision tegra_revision; + 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); + +#endif diff --git a/arch/arm/mach-tegra/tegra2_clocks.c b/arch/arm/mach-tegra/tegra2_clocks.c index ff9e6b6c0460..74d314fdf2f9 100644 --- a/arch/arm/mach-tegra/tegra2_clocks.c +++ b/arch/arm/mach-tegra/tegra2_clocks.c @@ -720,7 +720,7 @@ static void tegra2_pllx_clk_init(struct clk *c) { tegra2_pll_clk_init(c); - if (tegra_sku_id() == 7) + if (tegra_sku_id == 7) c->max_rate = 750000000; } -- cgit v1.2.3-55-g7522 From dee47183301983139fd0ed784d0defe0ba08f8f6 Mon Sep 17 00:00:00 2001 From: Olof Johansson Date: Mon, 17 Oct 2011 16:39:24 -0700 Subject: ARM: tegra: fuse: add bct strapping reading This is used by the memory setup code to pick the right memory timing table, if needed. Signed-off-by: Olof Johansson --- arch/arm/mach-tegra/fuse.c | 14 ++++++++++++++ arch/arm/mach-tegra/fuse.h | 2 ++ 2 files changed, 16 insertions(+) (limited to 'arch/arm/mach-tegra') diff --git a/arch/arm/mach-tegra/fuse.c b/arch/arm/mach-tegra/fuse.c index b1895c53ed60..17fdd4086e6f 100644 --- a/arch/arm/mach-tegra/fuse.c +++ b/arch/arm/mach-tegra/fuse.c @@ -35,6 +35,17 @@ int tegra_cpu_process_id; int tegra_core_process_id; enum tegra_revision tegra_revision; +/* The BCT to use at boot is specified by board straps that can be read + * through a APB misc register and decoded. 2 bits, i.e. 4 possible BCTs. + */ +int tegra_bct_strapping; + +#define STRAP_OPT 0x008 +#define GMI_AD0 (1 << 4) +#define GMI_AD1 (1 << 5) +#define RAM_ID_MASK (GMI_AD0 | GMI_AD1) +#define RAM_CODE_SHIFT 4 + static const char *tegra_revision_name[TEGRA_REVISION_MAX] = { [TEGRA_REVISION_UNKNOWN] = "unknown", [TEGRA_REVISION_A01] = "A01", @@ -93,6 +104,9 @@ void tegra_init_fuse(void) reg = tegra_fuse_readl(FUSE_SPARE_BIT); tegra_core_process_id = (reg >> 12) & 3; + reg = tegra_apb_readl(TEGRA_APB_MISC_BASE + STRAP_OPT); + tegra_bct_strapping = (reg & RAM_ID_MASK) >> RAM_CODE_SHIFT; + tegra_revision = tegra_get_revision(); pr_info("Tegra Revision: %s SKU: %d CPU Process: %d Core Process: %d\n", diff --git a/arch/arm/mach-tegra/fuse.h b/arch/arm/mach-tegra/fuse.h index 7576aaf6865c..d65d2abf803b 100644 --- a/arch/arm/mach-tegra/fuse.h +++ b/arch/arm/mach-tegra/fuse.h @@ -40,6 +40,8 @@ extern int tegra_cpu_process_id; extern int tegra_core_process_id; extern enum tegra_revision tegra_revision; +extern int tegra_bct_strapping; + unsigned long long tegra_chip_uid(void); void tegra_init_fuse(void); -- cgit v1.2.3-55-g7522 From 17711dbf4788ded84470941ff63a7029f73ca654 Mon Sep 17 00:00:00 2001 From: Olof Johansson Date: Sun, 16 Oct 2011 16:54:51 -0700 Subject: ARM: tegra: emc: convert tegra2_emc to a platform driver This is the first step in making it device-tree aware and get rid of the in-kernel EMC tables (of which there are none in mainline, thankfully). Changes since v3: * moved to devm_request_and_ioremap() in probe() Changes since v2: * D'oh -- missed a couple of variables that were added, never used and then later removed in a later patch. Changes since v1: * Fixed messed up indentation * Removed code that should be gone (was added here and removed later in series) Signed-off-by: Olof Johansson Acked-by: Stephen Warren --- arch/arm/mach-tegra/tegra2_emc.c | 92 ++++++++++++++++++++++++--------- arch/arm/mach-tegra/tegra2_emc.h | 11 ++-- include/linux/platform_data/tegra_emc.h | 34 ++++++++++++ 3 files changed, 107 insertions(+), 30 deletions(-) create mode 100644 include/linux/platform_data/tegra_emc.h (limited to 'arch/arm/mach-tegra') diff --git a/arch/arm/mach-tegra/tegra2_emc.c b/arch/arm/mach-tegra/tegra2_emc.c index 0f7ae6e90b55..e6229bbbb83a 100644 --- a/arch/arm/mach-tegra/tegra2_emc.c +++ b/arch/arm/mach-tegra/tegra2_emc.c @@ -16,10 +16,13 @@ */ #include +#include #include #include #include #include +#include +#include #include @@ -32,18 +35,17 @@ static bool emc_enable; #endif module_param(emc_enable, bool, 0644); -static void __iomem *emc = IO_ADDRESS(TEGRA_EMC_BASE); -static const struct tegra_emc_table *tegra_emc_table; -static int tegra_emc_table_size; +static struct platform_device *emc_pdev; +static void __iomem *emc_regbase; static inline void emc_writel(u32 val, unsigned long addr) { - writel(val, emc + addr); + writel(val, emc_regbase + addr); } static inline u32 emc_readl(unsigned long addr) { - return readl(emc + addr); + return readl(emc_regbase + addr); } static const unsigned long emc_reg_addr[TEGRA_EMC_NUM_REGS] = { @@ -98,15 +100,15 @@ static const unsigned long emc_reg_addr[TEGRA_EMC_NUM_REGS] = { /* Select the closest EMC rate that is higher than the requested rate */ long tegra_emc_round_rate(unsigned long rate) { + struct tegra_emc_pdata *pdata; int i; int best = -1; unsigned long distance = ULONG_MAX; - if (!tegra_emc_table) + if (!emc_pdev) return -EINVAL; - if (!emc_enable) - return -EINVAL; + pdata = emc_pdev->dev.platform_data; pr_debug("%s: %lu\n", __func__, rate); @@ -116,10 +118,10 @@ long tegra_emc_round_rate(unsigned long rate) */ rate = rate / 2 / 1000; - for (i = 0; i < tegra_emc_table_size; i++) { - if (tegra_emc_table[i].rate >= rate && - (tegra_emc_table[i].rate - rate) < distance) { - distance = tegra_emc_table[i].rate - rate; + for (i = 0; i < pdata->num_tables; i++) { + if (pdata->tables[i].rate >= rate && + (pdata->tables[i].rate - rate) < distance) { + distance = pdata->tables[i].rate - rate; best = i; } } @@ -127,9 +129,9 @@ long tegra_emc_round_rate(unsigned long rate) if (best < 0) return -EINVAL; - pr_debug("%s: using %lu\n", __func__, tegra_emc_table[best].rate); + pr_debug("%s: using %lu\n", __func__, pdata->tables[best].rate); - return tegra_emc_table[best].rate * 2 * 1000; + return pdata->tables[best].rate * 2 * 1000; } /* @@ -142,37 +144,81 @@ long tegra_emc_round_rate(unsigned long rate) */ int tegra_emc_set_rate(unsigned long rate) { + struct tegra_emc_pdata *pdata; int i; int j; - if (!tegra_emc_table) + if (!emc_pdev) return -EINVAL; + pdata = emc_pdev->dev.platform_data; + /* * The EMC clock rate is twice the bus rate, and the bus rate is * measured in kHz */ rate = rate / 2 / 1000; - for (i = 0; i < tegra_emc_table_size; i++) - if (tegra_emc_table[i].rate == rate) + for (i = 0; i < pdata->num_tables; i++) + if (pdata->tables[i].rate == rate) break; - if (i >= tegra_emc_table_size) + if (i >= pdata->num_tables) return -EINVAL; pr_debug("%s: setting to %lu\n", __func__, rate); for (j = 0; j < TEGRA_EMC_NUM_REGS; j++) - emc_writel(tegra_emc_table[i].regs[j], emc_reg_addr[j]); + emc_writel(pdata->tables[i].regs[j], emc_reg_addr[j]); + + emc_readl(pdata->tables[i].regs[TEGRA_EMC_NUM_REGS - 1]); + + return 0; +} + +static int __devinit tegra_emc_probe(struct platform_device *pdev) +{ + struct tegra_emc_pdata *pdata; + struct resource *res; + + if (!emc_enable) { + dev_err(&pdev->dev, "disabled per module parameter\n"); + return -ENODEV; + } + + pdata = pdev->dev.platform_data; - emc_readl(tegra_emc_table[i].regs[TEGRA_EMC_NUM_REGS - 1]); + if (!pdata) { + dev_err(&pdev->dev, "missing platform data\n"); + return -ENXIO; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "missing register base\n"); + return -ENOMEM; + } + + emc_regbase = devm_request_and_ioremap(&pdev->dev, res); + if (!emc_regbase) { + dev_err(&pdev->dev, "failed to remap registers\n"); + return -ENOMEM; + } + emc_pdev = pdev; return 0; } -void tegra_init_emc(const struct tegra_emc_table *table, int table_size) +static struct platform_driver tegra_emc_driver = { + .driver = { + .name = "tegra-emc", + .owner = THIS_MODULE, + }, + .probe = tegra_emc_probe, +}; + +static int __init tegra_emc_init(void) { - tegra_emc_table = table; - tegra_emc_table_size = table_size; + return platform_driver_register(&tegra_emc_driver); } +device_initcall(tegra_emc_init); diff --git a/arch/arm/mach-tegra/tegra2_emc.h b/arch/arm/mach-tegra/tegra2_emc.h index 19f08cb31603..f61409b54cb7 100644 --- a/arch/arm/mach-tegra/tegra2_emc.h +++ b/arch/arm/mach-tegra/tegra2_emc.h @@ -15,13 +15,10 @@ * */ -#define TEGRA_EMC_NUM_REGS 46 - -struct tegra_emc_table { - unsigned long rate; - u32 regs[TEGRA_EMC_NUM_REGS]; -}; +#ifndef __MACH_TEGRA_TEGRA2_EMC_H_ +#define __MACH_TEGRA_TEGRA2_EMC_H int tegra_emc_set_rate(unsigned long rate); long tegra_emc_round_rate(unsigned long rate); -void tegra_init_emc(const struct tegra_emc_table *table, int table_size); + +#endif diff --git a/include/linux/platform_data/tegra_emc.h b/include/linux/platform_data/tegra_emc.h new file mode 100644 index 000000000000..df67505e98f8 --- /dev/null +++ b/include/linux/platform_data/tegra_emc.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2011 Google, Inc. + * + * Author: + * Colin Cross + * Olof Johansson + * + * 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 __TEGRA_EMC_H_ +#define __TEGRA_EMC_H_ + +#define TEGRA_EMC_NUM_REGS 46 + +struct tegra_emc_table { + unsigned long rate; + u32 regs[TEGRA_EMC_NUM_REGS]; +}; + +struct tegra_emc_pdata { + int num_tables; + struct tegra_emc_table *tables; +}; + +#endif -- cgit v1.2.3-55-g7522 From 941b8db1df8bfc29a88fc8e3e203289d84a3f64d Mon Sep 17 00:00:00 2001 From: Olof Johansson Date: Mon, 17 Oct 2011 16:05:22 -0700 Subject: ARM: tegra: emc: device tree support Add device tree support to the emc driver, filling in the platform data based on the DT bindings. Changes since v1: * Unmangled some messed up patch squashes, moving changes to earlier patches * Flipped an of_property_read_u32 return value test * Clarified clock settings message on case where no table is provided Signed-off-by: Olof Johansson Acked-by: Stephen Warren --- arch/arm/mach-tegra/tegra2_emc.c | 146 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 139 insertions(+), 7 deletions(-) (limited to 'arch/arm/mach-tegra') diff --git a/arch/arm/mach-tegra/tegra2_emc.c b/arch/arm/mach-tegra/tegra2_emc.c index e6229bbbb83a..52df6ca36292 100644 --- a/arch/arm/mach-tegra/tegra2_emc.c +++ b/arch/arm/mach-tegra/tegra2_emc.c @@ -21,12 +21,14 @@ #include #include #include +#include #include #include #include #include "tegra2_emc.h" +#include "fuse.h" #ifdef CONFIG_TEGRA_EMC_SCALING_ENABLE static bool emc_enable = true; @@ -176,6 +178,126 @@ int tegra_emc_set_rate(unsigned long rate) return 0; } +#ifdef CONFIG_OF +static struct device_node *tegra_emc_ramcode_devnode(struct device_node *np) +{ + struct device_node *iter; + u32 reg; + + for_each_child_of_node(np, iter) { + if (of_property_read_u32(np, "nvidia,ram-code", ®)) + continue; + if (reg == tegra_bct_strapping) + return of_node_get(iter); + } + + return NULL; +} + +static struct tegra_emc_pdata *tegra_emc_dt_parse_pdata( + struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct device_node *tnp, *iter; + struct tegra_emc_pdata *pdata; + int ret, i, num_tables; + + if (!np) + return NULL; + + if (of_find_property(np, "nvidia,use-ram-code", NULL)) { + tnp = tegra_emc_ramcode_devnode(np); + if (!tnp) + dev_warn(&pdev->dev, + "can't find emc table for ram-code 0x%02x\n", + tegra_bct_strapping); + } else + tnp = of_node_get(np); + + if (!tnp) + return NULL; + + num_tables = 0; + for_each_child_of_node(tnp, iter) + if (of_device_is_compatible(iter, "nvidia,tegra20-emc-table")) + num_tables++; + + if (!num_tables) { + pdata = NULL; + goto out; + } + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + pdata->tables = devm_kzalloc(&pdev->dev, + sizeof(*pdata->tables) * num_tables, + GFP_KERNEL); + + i = 0; + for_each_child_of_node(tnp, iter) { + u32 prop; + + ret = of_property_read_u32(iter, "clock-frequency", &prop); + if (ret) { + dev_err(&pdev->dev, "no clock-frequency in %s\n", + iter->full_name); + continue; + } + pdata->tables[i].rate = prop; + + ret = of_property_read_u32_array(iter, "nvidia,emc-registers", + pdata->tables[i].regs, + TEGRA_EMC_NUM_REGS); + if (ret) { + dev_err(&pdev->dev, + "malformed emc-registers property in %s\n", + iter->full_name); + continue; + } + + i++; + } + pdata->num_tables = i; + +out: + of_node_put(tnp); + return pdata; +} +#else +static struct tegra_emc_pdata *tegra_emc_dt_parse_pdata( + struct platform_device *pdev) +{ + return NULL; +} +#endif + +static struct tegra_emc_pdata __devinit *tegra_emc_fill_pdata(struct platform_device *pdev) +{ + struct clk *c = clk_get_sys(NULL, "emc"); + struct tegra_emc_pdata *pdata; + unsigned long khz; + int i; + + WARN_ON(pdev->dev.platform_data); + BUG_ON(IS_ERR_OR_NULL(c)); + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + pdata->tables = devm_kzalloc(&pdev->dev, sizeof(*pdata->tables), + GFP_KERNEL); + + pdata->tables[0].rate = clk_get_rate(c); + + for (i = 0; i < TEGRA_EMC_NUM_REGS; i++) + pdata->tables[0].regs[i] = emc_readl(emc_reg_addr[i]); + + pdata->num_tables = 1; + + khz = pdata->tables[0].rate / 1000; + dev_info(&pdev->dev, "no tables provided, using %ld kHz emc, " + "%ld kHz mem\n", khz, khz/2); + + return pdata; +} + static int __devinit tegra_emc_probe(struct platform_device *pdev) { struct tegra_emc_pdata *pdata; @@ -186,13 +308,6 @@ static int __devinit tegra_emc_probe(struct platform_device *pdev) return -ENODEV; } - pdata = pdev->dev.platform_data; - - if (!pdata) { - dev_err(&pdev->dev, "missing platform data\n"); - return -ENXIO; - } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(&pdev->dev, "missing register base\n"); @@ -204,15 +319,32 @@ static int __devinit tegra_emc_probe(struct platform_device *pdev) dev_err(&pdev->dev, "failed to remap registers\n"); return -ENOMEM; } + + pdata = pdev->dev.platform_data; + + if (!pdata) + pdata = tegra_emc_dt_parse_pdata(pdev); + + if (!pdata) + pdata = tegra_emc_fill_pdata(pdev); + + pdev->dev.platform_data = pdata; + emc_pdev = pdev; return 0; } +static struct of_device_id tegra_emc_of_match[] __devinitdata = { + { .compatible = "nvidia,tegra20-emc", }, + { }, +}; + static struct platform_driver tegra_emc_driver = { .driver = { .name = "tegra-emc", .owner = THIS_MODULE, + .of_match_table = tegra_emc_of_match, }, .probe = tegra_emc_probe, }; -- cgit v1.2.3-55-g7522 From cb3732d0dc9df198c889a26210b6b27bc51a1c4a Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Mon, 9 Jan 2012 20:05:11 +0000 Subject: ARM: tegra: Pause DMA when reading transfer count In order to read an accurate channel transfer count from the APB DMA engine, the DMA controller must be paused first. Signed-off-by: Laxman Dewangan Acked-by: Stephen Warren Tested-by: Stephen Warren Signed-off-by: Olof Johansson --- arch/arm/mach-tegra/dma.c | 116 +++++++++++++++++++++++++++++----------------- 1 file changed, 74 insertions(+), 42 deletions(-) (limited to 'arch/arm/mach-tegra') diff --git a/arch/arm/mach-tegra/dma.c b/arch/arm/mach-tegra/dma.c index 122e46770d93..998c55ddca42 100644 --- a/arch/arm/mach-tegra/dma.c +++ b/arch/arm/mach-tegra/dma.c @@ -135,6 +135,7 @@ struct tegra_dma_channel { static bool tegra_dma_initialized; static DEFINE_MUTEX(tegra_dma_lock); +static DEFINE_SPINLOCK(enable_lock); static DECLARE_BITMAP(channel_usage, NV_DMA_MAX_CHANNELS); static struct tegra_dma_channel dma_channels[NV_DMA_MAX_CHANNELS]; @@ -200,18 +201,82 @@ static int tegra_dma_cancel(struct tegra_dma_channel *ch) return 0; } +static unsigned int get_channel_status(struct tegra_dma_channel *ch, + struct tegra_dma_req *req, bool is_stop_dma) +{ + void __iomem *addr = IO_ADDRESS(TEGRA_APB_DMA_BASE); + unsigned int status; + + if (is_stop_dma) { + /* + * STOP the DMA and get the transfer count. + * Getting the transfer count is tricky. + * - Globally disable DMA on all channels + * - Read the channel's status register to know the number + * of pending bytes to be transfered. + * - Stop the dma channel + * - Globally re-enable DMA to resume other transfers + */ + spin_lock(&enable_lock); + writel(0, addr + APB_DMA_GEN); + udelay(20); + status = readl(ch->addr + APB_DMA_CHAN_STA); + tegra_dma_stop(ch); + writel(GEN_ENABLE, addr + APB_DMA_GEN); + spin_unlock(&enable_lock); + if (status & STA_ISE_EOC) { + pr_err("Got Dma Int here clearing"); + writel(status, ch->addr + APB_DMA_CHAN_STA); + } + req->status = TEGRA_DMA_REQ_ERROR_ABORTED; + } else { + status = readl(ch->addr + APB_DMA_CHAN_STA); + } + return status; +} + +/* should be called with the channel lock held */ +static unsigned int dma_active_count(struct tegra_dma_channel *ch, + struct tegra_dma_req *req, unsigned int status) +{ + unsigned int to_transfer; + unsigned int req_transfer_count; + unsigned int bytes_transferred; + + to_transfer = ((status & STA_COUNT_MASK) >> STA_COUNT_SHIFT) + 1; + req_transfer_count = ch->req_transfer_count + 1; + bytes_transferred = req_transfer_count; + if (status & STA_BUSY) + bytes_transferred -= to_transfer; + /* + * In continuous 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. + */ + if (ch->mode & TEGRA_DMA_MODE_CONTINOUS) { + if (req->buffer_status == TEGRA_DMA_REQ_BUF_STATUS_HALF_FULL) + bytes_transferred += req_transfer_count; + if (status & STA_ISE_EOC) + bytes_transferred += req_transfer_count; + } + bytes_transferred *= 4; + return bytes_transferred; +} + 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; + int stop = 0; spin_lock_irqsave(&ch->lock, irq_flags); + + if (list_entry(ch->list.next, struct tegra_dma_req, node) == _req) + stop = 1; + list_for_each_entry(req, &ch->list, node) { if (req == _req) { list_del(&req->node); @@ -224,47 +289,12 @@ int tegra_dma_dequeue_req(struct tegra_dma_channel *ch, 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 transferred. - * - Finally stop or program the DMA to the next buffer in the - * list. - */ - csr = readl(ch->addr + APB_DMA_CHAN_CSR); - csr &= ~CSR_REQ_SEL_MASK; - csr |= CSR_REQ_SEL_INVALID; - 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->req_transfer_count; - req_transfer_count += 1; - to_transfer += 1; + if (!stop) + goto skip_stop_dma; - req->bytes_transferred = req_transfer_count; + status = get_channel_status(ch, req, true); + req->bytes_transferred = dma_active_count(ch, req, status); - if (status & STA_BUSY) - req->bytes_transferred -= to_transfer; - - /* In continuous 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; @@ -272,6 +302,8 @@ int tegra_dma_dequeue_req(struct tegra_dma_channel *ch, typeof(*next_req), node); tegra_dma_update_hw(ch, next_req); } + +skip_stop_dma: req->status = -TEGRA_DMA_REQ_ERROR_ABORTED; spin_unlock_irqrestore(&ch->lock, irq_flags); -- cgit v1.2.3-55-g7522 From e53b7d87cc375fbe428551651094fb676764aae3 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Tue, 3 Jan 2012 12:05:47 +0000 Subject: ARM: tegra: Support Tegra30 in decompressor UART setup On Tegra20, the UART clock runs at 216MHz, whereas on Tegra30 it runs at 408MHz. Modify arch_decomp_setup() to detect Tegra20-vs-Tegra30 at run- time, and program the correct divisor. This makes uncompressor messages work correctly on Tegra30. This also fixes early printk, assuming zImage is used and this setup code runs. v2: Use CHIPID register to differentiate between chips, rather than a GIC register. This should be more future-proof. Volatile is required to prevent the compiler transforming the 32-bit apb_misc register read into an 8-bit read of address 1 higher, since the HW only supports 32- bit accesses, and will hang on an 8-bit access. Signed-off-by: Stephen Warren Signed-off-by: Olof Johansson --- arch/arm/mach-tegra/include/mach/uncompress.h | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'arch/arm/mach-tegra') diff --git a/arch/arm/mach-tegra/include/mach/uncompress.h b/arch/arm/mach-tegra/include/mach/uncompress.h index 4e8323770c79..39bd5e5a1afd 100644 --- a/arch/arm/mach-tegra/include/mach/uncompress.h +++ b/arch/arm/mach-tegra/include/mach/uncompress.h @@ -45,15 +45,23 @@ static inline void flush(void) static inline void arch_decomp_setup(void) { + volatile u32 *apb_misc = (volatile u32 *)TEGRA_APB_MISC_BASE; + u32 chip, div; volatile u8 *uart = (volatile u8 *)TEGRA_DEBUG_UART_BASE; int shift = 2; if (uart == NULL) return; + chip = (apb_misc[0x804 / 4] >> 8) & 0xff; + if (chip == 0x20) + div = 0x0075; + else + div = 0x00dd; + uart[UART_LCR << shift] |= UART_LCR_DLAB; - uart[UART_DLL << shift] = 0x75; - uart[UART_DLM << shift] = 0x0; + uart[UART_DLL << shift] = div & 0xff; + uart[UART_DLM << shift] = div >> 8; uart[UART_LCR << shift] = 3; } -- cgit v1.2.3-55-g7522 From 229c7b22a24b9996e3a3eb1a2748e255e16bc323 Mon Sep 17 00:00:00 2001 From: Doug Anderson Date: Fri, 6 Jan 2012 10:43:19 +0000 Subject: ARM: tegra: Introduce define DEBUG_UART_SHIFT This removes the need for the variable "shift" in all functions in uncompress.h. Signed-off-by: Doug Anderson [swarren: Extracted from a larger patch by Doug] Signed-off-by: Stephen Warren Tested-by: Doug Anderson Acked-by: Doug Anderson Signed-off-by: Olof Johansson --- arch/arm/mach-tegra/include/mach/uncompress.h | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'arch/arm/mach-tegra') diff --git a/arch/arm/mach-tegra/include/mach/uncompress.h b/arch/arm/mach-tegra/include/mach/uncompress.h index 39bd5e5a1afd..9797279e94de 100644 --- a/arch/arm/mach-tegra/include/mach/uncompress.h +++ b/arch/arm/mach-tegra/include/mach/uncompress.h @@ -2,10 +2,12 @@ * arch/arm/mach-tegra/include/mach/uncompress.h * * Copyright (C) 2010 Google, Inc. + * Copyright (C) 2011 Google, Inc. * * Author: * Colin Cross * Erik Gilling + * Doug Anderson * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -26,17 +28,18 @@ #include +#define DEBUG_UART_SHIFT 2 + static void putc(int c) { volatile u8 *uart = (volatile u8 *)TEGRA_DEBUG_UART_BASE; - int shift = 2; if (uart == NULL) return; - while (!(uart[UART_LSR << shift] & UART_LSR_THRE)) + while (!(uart[UART_LSR << DEBUG_UART_SHIFT] & UART_LSR_THRE)) barrier(); - uart[UART_TX << shift] = c; + uart[UART_TX << DEBUG_UART_SHIFT] = c; } static inline void flush(void) @@ -48,7 +51,6 @@ static inline void arch_decomp_setup(void) volatile u32 *apb_misc = (volatile u32 *)TEGRA_APB_MISC_BASE; u32 chip, div; volatile u8 *uart = (volatile u8 *)TEGRA_DEBUG_UART_BASE; - int shift = 2; if (uart == NULL) return; @@ -59,10 +61,10 @@ static inline void arch_decomp_setup(void) else div = 0x00dd; - uart[UART_LCR << shift] |= UART_LCR_DLAB; - uart[UART_DLL << shift] = div & 0xff; - uart[UART_DLM << shift] = div >> 8; - uart[UART_LCR << shift] = 3; + uart[UART_LCR << DEBUG_UART_SHIFT] |= UART_LCR_DLAB; + uart[UART_DLL << DEBUG_UART_SHIFT] = div & 0xff; + uart[UART_DLM << DEBUG_UART_SHIFT] = div >> 8; + uart[UART_LCR << DEBUG_UART_SHIFT] = 3; } static inline void arch_decomp_wdog(void) -- cgit v1.2.3-55-g7522 From 31bac1375bda9787f18b2f60e0e1ca62258ea09c Mon Sep 17 00:00:00 2001 From: Doug Anderson Date: Fri, 6 Jan 2012 10:43:20 +0000 Subject: ARM: tegra: uncompress.h: Store UART address in a variable This will allow a future change to auto-detect which UART to use. Signed-off-by: Doug Anderson [swarren: Extracted from a larger patch by Doug] Signed-off-by: Stephen Warren Tested-by: Doug Anderson Acked-by: Doug Anderson Signed-off-by: Olof Johansson --- arch/arm/mach-tegra/include/mach/uncompress.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'arch/arm/mach-tegra') diff --git a/arch/arm/mach-tegra/include/mach/uncompress.h b/arch/arm/mach-tegra/include/mach/uncompress.h index 9797279e94de..bb3fd359f9fa 100644 --- a/arch/arm/mach-tegra/include/mach/uncompress.h +++ b/arch/arm/mach-tegra/include/mach/uncompress.h @@ -30,10 +30,10 @@ #define DEBUG_UART_SHIFT 2 +volatile u8 *uart; + static void putc(int c) { - volatile u8 *uart = (volatile u8 *)TEGRA_DEBUG_UART_BASE; - if (uart == NULL) return; @@ -50,8 +50,8 @@ static inline void arch_decomp_setup(void) { volatile u32 *apb_misc = (volatile u32 *)TEGRA_APB_MISC_BASE; u32 chip, div; - volatile u8 *uart = (volatile u8 *)TEGRA_DEBUG_UART_BASE; + uart = (volatile u8 *)TEGRA_DEBUG_UART_BASE; if (uart == NULL) return; -- cgit v1.2.3-55-g7522 From fe2639892cb618d5c42ea4570feea8dc497d0487 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Fri, 6 Jan 2012 10:43:21 +0000 Subject: ARM: tegra: uncompress.h: Choose a UART at runtime With this change we automatically detect which UART to use for for printing during decompression. The detection involves coordination with the bootloader: it's expected that the bootloader will leave a 'D' (for [D]ebug) in the UART scratchpad register for whichever UART we should use for debugging. If we don't find any such UART, we fall back to the UART that was specified during config time: CONFIG_TEGRA_DEBUG_UART_XXX. As a side effect of this change, uncompress debug messages will work if you've specified CONFIG_TEGRA_DEBUG_UART_NONE, provided the bootloader obeys the protocol. This change is in line with what is documented in Documentation/arm/Booting. Other approaches considered: * Hardcode based on machine ID (as many other ARM boards do). OK, but nice to not have yet another place to add per-board code. Better to have bootloader parse device tree and pass us this info. * Check for TXE bit (like SA1110). Nice (and doesn't require a bootloader change), but a little less explicit. Also: if bootloader (for some reason) uses another UART, it needs to remember to turn it off before jumping to the kernel or we may print to it. NOTE: adapting this patch to check TXE too would be easy if desired. Signed-off-by: Doug Anderson [swarren: Added clock/reset condition checks] Signed-off-by: Stephen Warren Tested-by: Doug Anderson Acked-by: Doug Anderson Signed-off-by: Olof Johansson --- arch/arm/mach-tegra/include/mach/uncompress.h | 75 ++++++++++++++++++++++++++- 1 file changed, 74 insertions(+), 1 deletion(-) (limited to 'arch/arm/mach-tegra') diff --git a/arch/arm/mach-tegra/include/mach/uncompress.h b/arch/arm/mach-tegra/include/mach/uncompress.h index bb3fd359f9fa..6c087b6974b2 100644 --- a/arch/arm/mach-tegra/include/mach/uncompress.h +++ b/arch/arm/mach-tegra/include/mach/uncompress.h @@ -3,11 +3,13 @@ * * Copyright (C) 2010 Google, Inc. * Copyright (C) 2011 Google, Inc. + * Copyright (C) 2011 NVIDIA CORPORATION. All Rights Reserved. * * Author: * Colin Cross * Erik Gilling * Doug Anderson + * Stephen Warren * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -23,6 +25,7 @@ #ifndef __MACH_TEGRA_UNCOMPRESS_H #define __MACH_TEGRA_UNCOMPRESS_H +#include #include #include @@ -46,12 +49,82 @@ static inline void flush(void) { } +/* + * Setup before decompression. This is where we do UART selection for + * earlyprintk and init the uart_base register. + */ static inline void arch_decomp_setup(void) { + static const struct { + u32 base; + u32 reset_reg; + u32 clock_reg; + u32 bit; + } uarts[] = { + { + TEGRA_UARTA_BASE, + TEGRA_CLK_RESET_BASE + 0x04, + TEGRA_CLK_RESET_BASE + 0x10, + 6, + }, + { + TEGRA_UARTB_BASE, + TEGRA_CLK_RESET_BASE + 0x04, + TEGRA_CLK_RESET_BASE + 0x10, + 7, + }, + { + TEGRA_UARTC_BASE, + TEGRA_CLK_RESET_BASE + 0x08, + TEGRA_CLK_RESET_BASE + 0x14, + 23, + }, + { + TEGRA_UARTD_BASE, + TEGRA_CLK_RESET_BASE + 0x0c, + TEGRA_CLK_RESET_BASE + 0x18, + 1, + }, + { + TEGRA_UARTE_BASE, + TEGRA_CLK_RESET_BASE + 0x0c, + TEGRA_CLK_RESET_BASE + 0x18, + 2, + }, + }; + int i; volatile u32 *apb_misc = (volatile u32 *)TEGRA_APB_MISC_BASE; u32 chip, div; - uart = (volatile u8 *)TEGRA_DEBUG_UART_BASE; + /* + * Look for the first UART that: + * a) Is not in reset. + * b) Is clocked. + * c) Has a 'D' in the scratchpad register. + * + * Note that on Tegra30, the first two conditions are required, since + * if not true, accesses to the UART scratch register will hang. + * Tegra20 doesn't have this issue. + * + * The intent is that the bootloader will tell the kernel which UART + * to use by setting up those conditions. If nothing found, we'll fall + * back to what's specified in TEGRA_DEBUG_UART_BASE. + */ + for (i = 0; i < ARRAY_SIZE(uarts); i++) { + if (*(u8 *)uarts[i].reset_reg & BIT(uarts[i].bit)) + continue; + + if (!(*(u8 *)uarts[i].clock_reg & BIT(uarts[i].bit))) + continue; + + uart = (volatile u8 *)uarts[i].base; + if (uart[UART_SCR << DEBUG_UART_SHIFT] != 'D') + continue; + + break; + } + if (i == ARRAY_SIZE(uarts)) + uart = (volatile u8 *)TEGRA_DEBUG_UART_BASE; if (uart == NULL) return; -- cgit v1.2.3-55-g7522 From 6d7d7b3ecd20a0fbcebdbdffe7b25d94cfa37d93 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Fri, 6 Jan 2012 10:43:22 +0000 Subject: ARM: tegra: Pass uncompress.h UART selection to DEBUG_LL uncompress.h now saves the selected UART's physical address in Tegra's IRAM, along with a cookie to indicate validity. The first time it's run, macro addruart in debug-macro.S looks for this cookie, and if it's present, uses the UART address stored there. If not, the static value TEGRA_DEBUG_UART_BASE is used, as was previous behaviour. The static behaviour will thus be used when not booting using a zImage. This work was inspired by work by Doug Anderson ; see http://lkml.org/lkml/2011/9/26/284. However, this patch relies on the data passing describe above, rather than duplicating the UART selection logic in debug-macro.S; the latest selection logic is more complex due to the need to check reset/clock bits too. Signed-off-by: Stephen Warren Tested-by: Doug Anderson Acked-by: Doug Anderson Signed-off-by: Olof Johansson --- arch/arm/mach-tegra/common.c | 17 +++++ arch/arm/mach-tegra/include/mach/debug-macro.S | 88 ++++++++++++++++++++++---- arch/arm/mach-tegra/include/mach/irammap.h | 35 ++++++++++ arch/arm/mach-tegra/include/mach/uncompress.h | 15 ++++- 4 files changed, 143 insertions(+), 12 deletions(-) create mode 100644 arch/arm/mach-tegra/include/mach/irammap.h (limited to 'arch/arm/mach-tegra') diff --git a/arch/arm/mach-tegra/common.c b/arch/arm/mach-tegra/common.c index a2eb90169aed..76210e5df561 100644 --- a/arch/arm/mach-tegra/common.c +++ b/arch/arm/mach-tegra/common.c @@ -33,6 +33,23 @@ #include "clock.h" #include "fuse.h" +/* + * Storage for debug-macro.S's state. + * + * This must be in .data not .bss so that it gets initialized each time the + * kernel is loaded. The data is declared here rather than debug-macro.S so + * that multiple inclusions of debug-macro.S point at the same data. + */ +#define TEGRA_DEBUG_UART_OFFSET (TEGRA_DEBUG_UART_BASE & 0xFFFF) +u32 tegra_uart_config[3] = { + /* Debug UART initialization required */ + 1, + /* Debug UART physical address */ + (u32)(IO_APB_PHYS + TEGRA_DEBUG_UART_OFFSET), + /* Debug UART virtual address */ + (u32)(IO_APB_VIRT + TEGRA_DEBUG_UART_OFFSET), +}; + #ifdef CONFIG_OF static const struct of_device_id tegra_dt_irq_match[] __initconst = { { .compatible = "arm,cortex-a9-gic", .data = gic_of_init }, diff --git a/arch/arm/mach-tegra/include/mach/debug-macro.S b/arch/arm/mach-tegra/include/mach/debug-macro.S index 619abc63aee8..90069abd37bd 100644 --- a/arch/arm/mach-tegra/include/mach/debug-macro.S +++ b/arch/arm/mach-tegra/include/mach/debug-macro.S @@ -1,11 +1,17 @@ /* * arch/arm/mach-tegra/include/mach/debug-macro.S * - * Copyright (C) 2010 Google, Inc. + * Copyright (C) 2010,2011 Google, Inc. + * Copyright (C) 2011-2012 NVIDIA CORPORATION. All Rights Reserved. * * Author: * Colin Cross * Erik Gilling + * Doug Anderson + * Stephen Warren + * + * Portions based on mach-omap2's debug-macro.S + * Copyright (C) 1994-1999 Russell King * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -18,18 +24,78 @@ * */ +#include + #include #include +#include + + .macro addruart, rp, rv, tmp + adr \rp, 99f @ actual addr of 99f + ldr \rv, [\rp] @ linked addr is stored there + sub \rv, \rv, \rp @ offset between the two + ldr \rp, [\rp, #4] @ linked tegra_uart_config + sub \tmp, \rp, \rv @ actual tegra_uart_config + ldr \rp, [\tmp] @ Load tegra_uart_config + cmp \rp, #1 @ needs intitialization? + bne 100f @ no; go load the addresses + mov \rv, #0 @ yes; record init is done + str \rv, [\tmp] + mov \rp, #TEGRA_IRAM_BASE @ See if cookie is in IRAM + ldr \rv, [\rp, #TEGRA_IRAM_DEBUG_UART_OFFSET] + movw \rp, #TEGRA_IRAM_DEBUG_UART_COOKIE & 0xffff + movt \rp, #TEGRA_IRAM_DEBUG_UART_COOKIE >> 16 + cmp \rv, \rp @ Cookie present? + bne 100f @ No, use default UART + mov \rp, #TEGRA_IRAM_BASE @ Load UART address from IRAM + ldr \rv, [\rp, #TEGRA_IRAM_DEBUG_UART_OFFSET + 4] + str \rv, [\tmp, #4] @ Store in tegra_uart_phys + sub \rv, \rv, #IO_APB_PHYS @ Calculate virt address + add \rv, \rv, #IO_APB_VIRT + str \rv, [\tmp, #8] @ Store in tegra_uart_virt + b 100f + + .align +99: .word . + .word tegra_uart_config + .ltorg + +100: ldr \rp, [\tmp, #4] @ Load tegra_uart_phys + ldr \rv, [\tmp, #8] @ Load tegra_uart_virt + .endm + +#define UART_SHIFT 2 + +/* + * Code below is swiped from , but add an extra + * check to make sure that we aren't in the CONFIG_TEGRA_DEBUG_UART_NONE case. + * We use the fact that all 5 valid UART addresses all have something in the + * 2nd-to-lowest byte. + */ - .macro addruart, rp, rv, tmp - ldr \rp, =IO_APB_PHYS @ physical - ldr \rv, =IO_APB_VIRT @ virtual - orr \rp, \rp, #(TEGRA_DEBUG_UART_BASE & 0xFF) - orr \rp, \rp, #(TEGRA_DEBUG_UART_BASE & 0xFF00) - orr \rv, \rv, #(TEGRA_DEBUG_UART_BASE & 0xFF) - orr \rv, \rv, #(TEGRA_DEBUG_UART_BASE & 0xFF00) - .endm + .macro senduart, rd, rx + tst \rx, #0x0000ff00 + strneb \rd, [\rx, #UART_TX << UART_SHIFT] +1001: + .endm -#define UART_SHIFT 2 -#include + .macro busyuart, rd, rx + tst \rx, #0x0000ff00 + beq 1002f +1001: ldrb \rd, [\rx, #UART_LSR << UART_SHIFT] + and \rd, \rd, #UART_LSR_TEMT | UART_LSR_THRE + teq \rd, #UART_LSR_TEMT | UART_LSR_THRE + bne 1001b +1002: + .endm + .macro waituart, rd, rx +#ifdef FLOW_CONTROL + tst \rx, #0x0000ff00 + beq 1002f +1001: ldrb \rd, [\rx, #UART_MSR << UART_SHIFT] + tst \rd, #UART_MSR_CTS + beq 1001b +1002: +#endif + .endm diff --git a/arch/arm/mach-tegra/include/mach/irammap.h b/arch/arm/mach-tegra/include/mach/irammap.h new file mode 100644 index 000000000000..0cbe63261854 --- /dev/null +++ b/arch/arm/mach-tegra/include/mach/irammap.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __MACH_TEGRA_IRAMMAP_H +#define __MACH_TEGRA_IRAMMAP_H + +#include + +/* The first 1K of IRAM is permanently reserved for the CPU reset handler */ +#define TEGRA_IRAM_RESET_HANDLER_OFFSET 0 +#define TEGRA_IRAM_RESET_HANDLER_SIZE SZ_1K + +/* + * These locations are written to by uncompress.h, and read by debug-macro.S. + * The first word holds the cookie value if the data is valid. The second + * word holds the UART physical address. + */ +#define TEGRA_IRAM_DEBUG_UART_OFFSET SZ_1K +#define TEGRA_IRAM_DEBUG_UART_SIZE 8 +#define TEGRA_IRAM_DEBUG_UART_COOKIE 0x55415254 + +#endif diff --git a/arch/arm/mach-tegra/include/mach/uncompress.h b/arch/arm/mach-tegra/include/mach/uncompress.h index 6c087b6974b2..b066ba0ee3c3 100644 --- a/arch/arm/mach-tegra/include/mach/uncompress.h +++ b/arch/arm/mach-tegra/include/mach/uncompress.h @@ -3,7 +3,7 @@ * * Copyright (C) 2010 Google, Inc. * Copyright (C) 2011 Google, Inc. - * Copyright (C) 2011 NVIDIA CORPORATION. All Rights Reserved. + * Copyright (C) 2011-2012 NVIDIA CORPORATION. All Rights Reserved. * * Author: * Colin Cross @@ -30,6 +30,7 @@ #include #include +#include #define DEBUG_UART_SHIFT 2 @@ -49,6 +50,17 @@ static inline void flush(void) { } +static inline void save_uart_address(void) +{ + u32 *buf = (u32 *)(TEGRA_IRAM_BASE + TEGRA_IRAM_DEBUG_UART_OFFSET); + + if (uart) { + buf[0] = TEGRA_IRAM_DEBUG_UART_COOKIE; + buf[1] = (u32)uart; + } else + buf[0] = 0; +} + /* * Setup before decompression. This is where we do UART selection for * earlyprintk and init the uart_base register. @@ -125,6 +137,7 @@ static inline void arch_decomp_setup(void) } if (i == ARRAY_SIZE(uarts)) uart = (volatile u8 *)TEGRA_DEBUG_UART_BASE; + save_uart_address(); if (uart == NULL) return; -- cgit v1.2.3-55-g7522 From 2123552d12168e744271aaf206e5826760fbd857 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 4 Jan 2012 08:39:33 +0000 Subject: ARM: tegra: Remove use of TEGRA_GPIO_TO_IRQ Replace compile-time usage of TEGRA_GPIO_TO_IRQ with run-time calls to gpio_to_irq(). This will allow the base IRQ number for the Tegra GPIO driver to be dynamically allocated in a later patch. Signed-off-by: Stephen Warren Acked-by: Grant Likely Signed-off-by: Olof Johansson --- arch/arm/mach-tegra/board-harmony.c | 2 +- arch/arm/mach-tegra/board-seaboard.c | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'arch/arm/mach-tegra') diff --git a/arch/arm/mach-tegra/board-harmony.c b/arch/arm/mach-tegra/board-harmony.c index 789bdc9e8f91..c00aadb01e09 100644 --- a/arch/arm/mach-tegra/board-harmony.c +++ b/arch/arm/mach-tegra/board-harmony.c @@ -101,7 +101,6 @@ static struct wm8903_platform_data harmony_wm8903_pdata = { static struct i2c_board_info __initdata wm8903_board_info = { I2C_BOARD_INFO("wm8903", 0x1a), .platform_data = &harmony_wm8903_pdata, - .irq = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_CDC_IRQ), }; static void __init harmony_i2c_init(void) @@ -111,6 +110,7 @@ static void __init harmony_i2c_init(void) platform_device_register(&tegra_i2c_device3); platform_device_register(&tegra_i2c_device4); + wm8903_board_info.irq = gpio_to_irq(TEGRA_GPIO_CDC_IRQ); i2c_register_board_info(0, &wm8903_board_info, 1); } diff --git a/arch/arm/mach-tegra/board-seaboard.c b/arch/arm/mach-tegra/board-seaboard.c index ebac65f52510..d669847f0485 100644 --- a/arch/arm/mach-tegra/board-seaboard.c +++ b/arch/arm/mach-tegra/board-seaboard.c @@ -159,7 +159,6 @@ static struct platform_device *seaboard_devices[] __initdata = { static struct i2c_board_info __initdata isl29018_device = { I2C_BOARD_INFO("isl29018", 0x44), - .irq = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_ISL29018_IRQ), }; static struct i2c_board_info __initdata adt7461_device = { @@ -183,7 +182,6 @@ static struct wm8903_platform_data wm8903_pdata = { static struct i2c_board_info __initdata wm8903_device = { I2C_BOARD_INFO("wm8903", 0x1a), .platform_data = &wm8903_pdata, - .irq = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_CDC_IRQ), }; static int seaboard_ehci_init(void) @@ -214,7 +212,10 @@ static void __init seaboard_i2c_init(void) gpio_request(TEGRA_GPIO_ISL29018_IRQ, "isl29018"); gpio_direction_input(TEGRA_GPIO_ISL29018_IRQ); + isl29018_device.irq = gpio_to_irq(TEGRA_GPIO_ISL29018_IRQ); i2c_register_board_info(0, &isl29018_device, 1); + + wm8903_device.irq = gpio_to_irq(TEGRA_GPIO_CDC_IRQ); i2c_register_board_info(0, &wm8903_device, 1); i2c_register_board_info(3, &adt7461_device, 1); -- cgit v1.2.3-55-g7522 From 6f74dc9bc8de41f3de474a7269a70921e773c40f Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 4 Jan 2012 08:39:37 +0000 Subject: gpio: tegra: Dynamically allocate IRQ base, and support DT Enhance the driver to dynamically allocate the base IRQ number, and create an IRQ domain for itself. The use of an IRQ domain ensures that any device tree node interrupts properties are correctly parsed. Describe interrupt-related properties in the device tree binding docs, and the contents of "child" node interrupts property. Update tegra*.dtsi to specify the required interrupt-related properties. Finally, remove the definition of TEGRA_GPIO_TO_IRQ; this macro no longer gives correct results since the IRQ numbers for GPIOs are dynamically allocated. Signed-off-by: Stephen Warren Acked-by: Grant Likely Signed-off-by: Olof Johansson --- .../devicetree/bindings/gpio/gpio_nvidia.txt | 12 +++++++++++ arch/arm/boot/dts/tegra20.dtsi | 2 ++ arch/arm/boot/dts/tegra30.dtsi | 2 ++ arch/arm/mach-tegra/include/mach/gpio-tegra.h | 2 -- drivers/gpio/gpio-tegra.c | 25 ++++++++++++++++------ 5 files changed, 34 insertions(+), 9 deletions(-) (limited to 'arch/arm/mach-tegra') diff --git a/Documentation/devicetree/bindings/gpio/gpio_nvidia.txt b/Documentation/devicetree/bindings/gpio/gpio_nvidia.txt index 50b363c5b884..d114e1997d39 100644 --- a/Documentation/devicetree/bindings/gpio/gpio_nvidia.txt +++ b/Documentation/devicetree/bindings/gpio/gpio_nvidia.txt @@ -8,6 +8,16 @@ Required properties: second cell is used to specify optional parameters: - bit 0 specifies polarity (0 for normal, 1 for inverted) - gpio-controller : Marks the device node as a GPIO controller. +- #interrupt-cells : Should be 2. + The first cell is the GPIO number. + The second cell is used to specify flags: + bits[3:0] trigger type and level flags: + 1 = low-to-high edge triggered. + 2 = high-to-low edge triggered. + 4 = active high level-sensitive. + 8 = active low level-sensitive. + Valid combinations are 1, 2, 3, 4, 8. +- interrupt-controller : Marks the device node as an interrupt controller. Example: @@ -23,4 +33,6 @@ gpio: gpio@6000d000 { 0 89 0x04 >; #gpio-cells = <2>; gpio-controller; + #interrupt-cells = <2>; + interrupt-controller; }; diff --git a/arch/arm/boot/dts/tegra20.dtsi b/arch/arm/boot/dts/tegra20.dtsi index 3195ad5562d3..ec1f0101c79c 100644 --- a/arch/arm/boot/dts/tegra20.dtsi +++ b/arch/arm/boot/dts/tegra20.dtsi @@ -101,6 +101,8 @@ 0 89 0x04 >; #gpio-cells = <2>; gpio-controller; + #interrupt-cells = <2>; + interrupt-controller; }; pinmux: pinmux@70000000 { diff --git a/arch/arm/boot/dts/tegra30.dtsi b/arch/arm/boot/dts/tegra30.dtsi index fd25e8e9ffd9..ac4b75cb26c0 100644 --- a/arch/arm/boot/dts/tegra30.dtsi +++ b/arch/arm/boot/dts/tegra30.dtsi @@ -107,6 +107,8 @@ 0 125 0x04 >; #gpio-cells = <2>; gpio-controller; + #interrupt-cells = <2>; + interrupt-controller; }; serial@70006000 { diff --git a/arch/arm/mach-tegra/include/mach/gpio-tegra.h b/arch/arm/mach-tegra/include/mach/gpio-tegra.h index 87d37fdf5084..6140820555e1 100644 --- a/arch/arm/mach-tegra/include/mach/gpio-tegra.h +++ b/arch/arm/mach-tegra/include/mach/gpio-tegra.h @@ -25,8 +25,6 @@ #define TEGRA_NR_GPIOS INT_GPIO_NR -#define TEGRA_GPIO_TO_IRQ(gpio) (INT_GPIO_BASE + (gpio)) - struct tegra_gpio_table { int gpio; /* GPIO number */ bool enable; /* Enable for GPIO at init? */ diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c index bdc293791590..bc923c7acce9 100644 --- a/drivers/gpio/gpio-tegra.c +++ b/drivers/gpio/gpio-tegra.c @@ -25,6 +25,7 @@ #include #include #include +#include #include @@ -74,7 +75,7 @@ struct tegra_gpio_bank { #endif }; - +static struct irq_domain irq_domain; static void __iomem *regs; static struct tegra_gpio_bank tegra_gpio_banks[7]; @@ -139,7 +140,7 @@ static int tegra_gpio_direction_output(struct gpio_chip *chip, unsigned offset, static int tegra_gpio_to_irq(struct gpio_chip *chip, unsigned offset) { - return TEGRA_GPIO_TO_IRQ(offset); + return irq_domain_to_irq(&irq_domain, offset); } static struct gpio_chip tegra_gpio_chip = { @@ -155,28 +156,28 @@ static struct gpio_chip tegra_gpio_chip = { static void tegra_gpio_irq_ack(struct irq_data *d) { - int gpio = d->irq - INT_GPIO_BASE; + int gpio = d->hwirq; tegra_gpio_writel(1 << GPIO_BIT(gpio), GPIO_INT_CLR(gpio)); } static void tegra_gpio_irq_mask(struct irq_data *d) { - int gpio = d->irq - INT_GPIO_BASE; + int gpio = d->hwirq; tegra_gpio_mask_write(GPIO_MSK_INT_ENB(gpio), gpio, 0); } static void tegra_gpio_irq_unmask(struct irq_data *d) { - int gpio = d->irq - INT_GPIO_BASE; + int gpio = d->hwirq; tegra_gpio_mask_write(GPIO_MSK_INT_ENB(gpio), gpio, 1); } static int tegra_gpio_irq_set_type(struct irq_data *d, unsigned int type) { - int gpio = d->irq - INT_GPIO_BASE; + int gpio = d->hwirq; struct tegra_gpio_bank *bank = irq_data_get_irq_chip_data(d); int port = GPIO_PORT(gpio); int lvl_type; @@ -343,6 +344,16 @@ static int __devinit tegra_gpio_probe(struct platform_device *pdev) int i; int j; + irq_domain.irq_base = irq_alloc_descs(-1, 0, TEGRA_NR_GPIOS, 0); + if (irq_domain.irq_base < 0) { + dev_err(&pdev->dev, "Couldn't allocate IRQ numbers\n"); + return -ENODEV; + } + irq_domain.nr_irq = TEGRA_NR_GPIOS; + irq_domain.ops = &irq_domain_simple_ops; + irq_domain.of_node = pdev->dev.of_node; + irq_domain_add(&irq_domain); + for (i = 0; i < ARRAY_SIZE(tegra_gpio_banks); i++) { res = platform_get_resource(pdev, IORESOURCE_IRQ, i); if (!res) { @@ -381,7 +392,7 @@ static int __devinit tegra_gpio_probe(struct platform_device *pdev) gpiochip_add(&tegra_gpio_chip); for (gpio = 0; gpio < TEGRA_NR_GPIOS; gpio++) { - int irq = TEGRA_GPIO_TO_IRQ(gpio); + int irq = irq_domain_to_irq(&irq_domain, gpio); /* No validity check; all Tegra GPIOs are valid IRQs */ bank = &tegra_gpio_banks[GPIO_BANK(gpio)]; -- cgit v1.2.3-55-g7522 From 76c2f6e513b3df8031dda383838da2a3820adbe3 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Thu, 26 Jan 2012 15:12:23 +0000 Subject: ARM: tegra: Fix EMC pdata initialization from registers Commit d91eeb0 "ARM: tegra: emc: device tree support" modified the EMC driver to create an EMC table from existing register settings when none was provided through platform data or device tree. This code wrote the wrong clock rate into the table; the actual rate in Hz, rather than the expected half-rate in KHz. This caused the BUG_ON in tegra2_emc_clk_round_rate() to fire, since that enormous rate could not be generated. Fixes: [ 2.425921] kernel BUG at arch/arm/mach-tegra/tegra2_clocks.c:1158! ... [ 2.618766] [] (tegra2_emc_clk_round_rate+0x58/0x70) from [] (clk_round_rate+0x48/0x68) [ 2.628494] [] (clk_round_rate+0x48/0x68) from [] (clk_set_rate_locked+0x40/0x68) [ 2.637707] [] (clk_set_rate_locked+0x40/0x68) from [] (clk_set_rate+0x28/0x40) [ 2.646754] [] (clk_set_rate+0x28/0x40) from [] (tegra_update_cpu_speed+0x54/0x144) [ 2.656144] [] (tegra_update_cpu_speed+0x54/0x144) from [] (tegra_target+0xb4/0xe0) [ 2.665538] [] (tegra_target+0xb4/0xe0) from [] (__cpufreq_driver_target+0x88/0xa4) [ 2.674931] [] (__cpufreq_driver_target+0x88/0xa4) from [] (dbs_check_cpu+0x324/0x340) [ 2.684582] [] (dbs_check_cpu+0x324/0x340) from [] (do_dbs_timer+0x54/0xf4) [ 2.693277] [] (do_dbs_timer+0x54/0xf4) from [] (process_one_work+0x1d4/0x320) [ 2.702225] [] (process_one_work+0x1d4/0x320) from [] (worker_thread+0x134/0x230) [ 2.711437] [] (worker_thread+0x134/0x230) from [] (kthread+0x80/0x8c) [ 2.719700] [] (kthread+0x80/0x8c) from [] (kernel_thread_exit+0x0/0x8) Reported-by: Marc Dietrich Signed-off-by: Stephen Warren [olof: fixed calculation of printed values] Signed-off-by: Olof Johansson --- arch/arm/mach-tegra/tegra2_emc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'arch/arm/mach-tegra') diff --git a/arch/arm/mach-tegra/tegra2_emc.c b/arch/arm/mach-tegra/tegra2_emc.c index 52df6ca36292..5070d833bdd1 100644 --- a/arch/arm/mach-tegra/tegra2_emc.c +++ b/arch/arm/mach-tegra/tegra2_emc.c @@ -284,16 +284,16 @@ static struct tegra_emc_pdata __devinit *tegra_emc_fill_pdata(struct platform_de pdata->tables = devm_kzalloc(&pdev->dev, sizeof(*pdata->tables), GFP_KERNEL); - pdata->tables[0].rate = clk_get_rate(c); + pdata->tables[0].rate = clk_get_rate(c) / 2 / 1000; for (i = 0; i < TEGRA_EMC_NUM_REGS; i++) pdata->tables[0].regs[i] = emc_readl(emc_reg_addr[i]); pdata->num_tables = 1; - khz = pdata->tables[0].rate / 1000; + khz = pdata->tables[0].rate; dev_info(&pdev->dev, "no tables provided, using %ld kHz emc, " - "%ld kHz mem\n", khz, khz/2); + "%ld kHz mem\n", khz * 2, khz); return pdata; } -- cgit v1.2.3-55-g7522 From 8bc4f556bd740789cf6fab36d1776d6d4d8bd375 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Tue, 24 Jan 2012 13:40:49 +0530 Subject: ARM: tegra: dma: not required to move requestor when stopping. It is not require to move the requestor of dma to INVALID option before stopping dma. Signed-off-by: Laxman Dewangan Acked-by: Stephen Warren Tested-by: Stephen Warren Signed-off-by: Olof Johansson --- arch/arm/mach-tegra/dma.c | 8 -------- 1 file changed, 8 deletions(-) (limited to 'arch/arm/mach-tegra') diff --git a/arch/arm/mach-tegra/dma.c b/arch/arm/mach-tegra/dma.c index 998c55ddca42..abea4f6e2dd5 100644 --- a/arch/arm/mach-tegra/dma.c +++ b/arch/arm/mach-tegra/dma.c @@ -52,8 +52,6 @@ #define CSR_ONCE (1<<27) #define CSR_FLOW (1<<21) #define CSR_REQ_SEL_SHIFT 16 -#define CSR_REQ_SEL_MASK (0x1F<lock, irq_flags); while (!list_empty(&ch->list)) list_del(ch->list.next); - csr = readl(ch->addr + APB_DMA_CHAN_CSR); - csr &= ~CSR_REQ_SEL_MASK; - csr |= CSR_REQ_SEL_INVALID; - writel(csr, ch->addr + APB_DMA_CHAN_CSR); - tegra_dma_stop(ch); spin_unlock_irqrestore(&ch->lock, irq_flags); -- cgit v1.2.3-55-g7522 From d3b8bdd5f9bc538fb17466cbb7af43209b55cb93 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 25 Jan 2012 14:43:28 -0700 Subject: ARM: tegra: Add a simple PMC driver This PMC driver is enough to parse the nvidia,invert-interrupt property from device tree, and configure the PMC's to honor that. In the future, this file could expand to centralize all other PMC accesses within the mach-tegra code. Signed-off-by: Stephen Warren Signed-off-by: Olof Johansson --- arch/arm/mach-tegra/Makefile | 1 + arch/arm/mach-tegra/common.c | 3 ++ arch/arm/mach-tegra/pmc.c | 76 ++++++++++++++++++++++++++++++++++++++++++++ arch/arm/mach-tegra/pmc.h | 23 ++++++++++++++ 4 files changed, 103 insertions(+) create mode 100644 arch/arm/mach-tegra/pmc.c create mode 100644 arch/arm/mach-tegra/pmc.h (limited to 'arch/arm/mach-tegra') diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile index 23d15fba3843..e0b7a4d32599 100644 --- a/arch/arm/mach-tegra/Makefile +++ b/arch/arm/mach-tegra/Makefile @@ -7,6 +7,7 @@ obj-y += clock.o obj-y += timer.o obj-y += pinmux.o obj-y += fuse.o +obj-y += pmc.o obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += powergate.o obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_clocks.o obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_emc.o diff --git a/arch/arm/mach-tegra/common.c b/arch/arm/mach-tegra/common.c index 76210e5df561..43da4284d862 100644 --- a/arch/arm/mach-tegra/common.c +++ b/arch/arm/mach-tegra/common.c @@ -32,6 +32,7 @@ #include "board.h" #include "clock.h" #include "fuse.h" +#include "pmc.h" /* * Storage for debug-macro.S's state. @@ -117,11 +118,13 @@ void __init tegra20_init_early(void) tegra2_init_clocks(); tegra_clk_init_from_table(tegra20_clk_init_table); tegra_init_cache(0x331, 0x441); + tegra_pmc_init(); } #endif #ifdef CONFIG_ARCH_TEGRA_3x_SOC void __init tegra30_init_early(void) { tegra_init_cache(0x441, 0x551); + tegra_pmc_init(); } #endif diff --git a/arch/arm/mach-tegra/pmc.c b/arch/arm/mach-tegra/pmc.c new file mode 100644 index 000000000000..7af6a54404be --- /dev/null +++ b/arch/arm/mach-tegra/pmc.c @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include + +#include + +#define PMC_CTRL 0x0 +#define PMC_CTRL_INTR_LOW (1 << 17) + +static inline u32 tegra_pmc_readl(u32 reg) +{ + return readl(IO_ADDRESS(TEGRA_PMC_BASE + reg)); +} + +static inline void tegra_pmc_writel(u32 val, u32 reg) +{ + writel(val, IO_ADDRESS(TEGRA_PMC_BASE + reg)); +} + +#ifdef CONFIG_OF +static const struct of_device_id matches[] __initconst = { + { .compatible = "nvidia,tegra20-pmc" }, + { } +}; +#endif + +void __init tegra_pmc_init(void) +{ + /* + * For now, Harmony is the only board that uses the PMC, and it wants + * the signal inverted. Seaboard would too if it used the PMC. + * Hopefully by the time other boards want to use the PMC, everything + * will be device-tree, or they also want it inverted. + */ + bool invert_interrupt = true; + u32 val; + +#ifdef CONFIG_OF + if (of_have_populated_dt()) { + struct device_node *np; + + invert_interrupt = false; + + np = of_find_matching_node(NULL, matches); + if (np) { + if (of_find_property(np, "nvidia,invert-interrupt", + NULL)) + invert_interrupt = true; + } + } +#endif + + val = tegra_pmc_readl(PMC_CTRL); + if (invert_interrupt) + val |= PMC_CTRL_INTR_LOW; + else + val &= ~PMC_CTRL_INTR_LOW; + tegra_pmc_writel(val, PMC_CTRL); +} diff --git a/arch/arm/mach-tegra/pmc.h b/arch/arm/mach-tegra/pmc.h new file mode 100644 index 000000000000..8995ee4a8768 --- /dev/null +++ b/arch/arm/mach-tegra/pmc.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef __MACH_TEGRA_PMC_H +#define __MACH_TEGRA_PMC_H + +void tegra_pmc_init(void); + +#endif -- cgit v1.2.3-55-g7522 From 129cee1020d8031df1e3986673c64ca9eb7a2617 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 25 Jan 2012 11:43:29 +0000 Subject: ARM: tegra: Remove duplicate PMU interrupt inversion code The new PMC driver now configures the PMU interrupt inversion, so board files don't need to poke the PMC registers directly to achieve this. Signed-off-by: Stephen Warren Signed-off-by: Olof Johansson --- arch/arm/mach-tegra/board-harmony-power.c | 15 --------------- 1 file changed, 15 deletions(-) (limited to 'arch/arm/mach-tegra') diff --git a/arch/arm/mach-tegra/board-harmony-power.c b/arch/arm/mach-tegra/board-harmony-power.c index 21d1285731b3..976edfb05912 100644 --- a/arch/arm/mach-tegra/board-harmony-power.c +++ b/arch/arm/mach-tegra/board-harmony-power.c @@ -18,18 +18,13 @@ #include #include #include -#include #include #include -#include #include #include "board-harmony.h" -#define PMC_CTRL 0x0 -#define PMC_CTRL_INTR_LOW (1 << 17) - static struct regulator_consumer_supply tps658621_ldo0_supply[] = { REGULATOR_SUPPLY("pex_clk", NULL), }; @@ -114,16 +109,6 @@ static struct i2c_board_info __initdata harmony_regulators[] = { int __init harmony_regulator_init(void) { - void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE); - u32 pmc_ctrl; - - /* - * Configure the power management controller to trigger PMU - * interrupts when low - */ - pmc_ctrl = readl(pmc + PMC_CTRL); - writel(pmc_ctrl | PMC_CTRL_INTR_LOW, pmc + PMC_CTRL); - i2c_register_board_info(3, harmony_regulators, 1); return 0; -- cgit v1.2.3-55-g7522 From e77a6b313fdfe4faa8f9a8edf919c7eb8d095fb5 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Mon, 27 Feb 2012 14:27:19 -0700 Subject: ARM: tegra: uncompress.h: Don't depend on kernel headers This fixes the following compile error: CC arch/arm/boot/compressed/misc.o In file included from arch/arm/boot/compressed/misc.c:28:0: arch/arm/mach-tegra/include/mach/uncompress.h: In function 'arch_decomp_setup': arch/arm/mach-tegra/include/mach/uncompress.h:125:2: error: implicit declaration of function 'BUILD_BUG_ON_ZERO' [-Werror=implicit-function-declaration] cc1: some warnings being treated as errors This is due to use of the ARRAY_SIZE() macro. Typically, this would be solved by including , but the compressor code isn't part of the kernel, and so should not include kernel headers. Instead, define the few macros the code uses directly, and in a way that doesn't depend on . Signed-off-by: Stephen Warren Signed-off-by: Olof Johansson --- arch/arm/mach-tegra/include/mach/uncompress.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'arch/arm/mach-tegra') diff --git a/arch/arm/mach-tegra/include/mach/uncompress.h b/arch/arm/mach-tegra/include/mach/uncompress.h index b066ba0ee3c3..5a440f315e57 100644 --- a/arch/arm/mach-tegra/include/mach/uncompress.h +++ b/arch/arm/mach-tegra/include/mach/uncompress.h @@ -25,13 +25,15 @@ #ifndef __MACH_TEGRA_UNCOMPRESS_H #define __MACH_TEGRA_UNCOMPRESS_H -#include #include #include #include #include +#define BIT(x) (1 << (x)) +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) + #define DEBUG_UART_SHIFT 2 volatile u8 *uart; -- cgit v1.2.3-55-g7522