diff options
author | Kevin Wells <wellsk40@gmail.com> | 2010-07-28 03:38:52 -0700 |
---|---|---|
committer | Kevin Wells <wellsk40@gmail.com> | 2010-07-28 03:38:52 -0700 |
commit | cccf59abea4e1c36322e365d6e61ff7de1f3ee07 (patch) | |
tree | 376b61082713ca866c85318c034c8155124a07af /arch | |
parent | fc0f5ac8fe693d1b05f5a928cc48135d1c8b7f2e (diff) | |
parent | 408059495a372f1a9845672e58608fb6d8ec0c40 (diff) |
Merge branch 'test' of ../test/linux-2.6-lpc2 into wells/lpc32xx-arch_v2
Diffstat (limited to 'arch')
30 files changed, 4827 insertions, 0 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 98922f7d2d1..9a189f757c9 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -482,6 +482,19 @@ config ARCH_LOKI help Support for the Marvell Loki (88RC8480) SoC. +config ARCH_LPC32XX + bool "NXP LPC32XX" + select CPU_ARM926T + select ARCH_REQUIRE_GPIOLIB + select HAVE_IDE + select ARM_AMBA + select USB_ARCH_HAS_OHCI + select COMMON_CLKDEV + select GENERIC_TIME + select GENERIC_CLOCKEVENTS + help + Support for the NXP LPC32XX family of processors + config ARCH_MV78XX0 bool "Marvell MV78xx0" select CPU_FEROCEON @@ -845,6 +858,8 @@ source "arch/arm/mach-lh7a40x/Kconfig" source "arch/arm/mach-loki/Kconfig" +source "arch/arm/mach-lpc32xx/Kconfig" + source "arch/arm/mach-msm/Kconfig" source "arch/arm/mach-mv78xx0/Kconfig" diff --git a/arch/arm/Makefile b/arch/arm/Makefile index 64ba313724d..6a612c5483c 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -142,6 +142,7 @@ machine-$(CONFIG_ARCH_KS8695) := ks8695 machine-$(CONFIG_ARCH_L7200) := l7200 machine-$(CONFIG_ARCH_LH7A40X) := lh7a40x machine-$(CONFIG_ARCH_LOKI) := loki +machine-$(CONFIG_ARCH_LPC32XX) := lpc32xx machine-$(CONFIG_ARCH_MMP) := mmp machine-$(CONFIG_ARCH_MSM) := msm machine-$(CONFIG_ARCH_MV78XX0) := mv78xx0 diff --git a/arch/arm/mach-lpc32xx/Kconfig b/arch/arm/mach-lpc32xx/Kconfig new file mode 100644 index 00000000000..fde66350869 --- /dev/null +++ b/arch/arm/mach-lpc32xx/Kconfig @@ -0,0 +1,33 @@ +if ARCH_LPC32XX + +menu "Individual UART enable selections" + +config ARCH_LPC32XX_UART3_SELECT + bool "Add support for standard UART3" + help + Adds support for standard UART 3 when the 8250 serial support + is enabled. + +config ARCH_LPC32XX_UART4_SELECT + bool "Add support for standard UART4" + help + Adds support for standard UART 4 when the 8250 serial support + is enabled. + +config ARCH_LPC32XX_UART5_SELECT + bool "Add support for standard UART5" + default y + help + Adds support for standard UART 5 when the 8250 serial support + is enabled. + +config ARCH_LPC32XX_UART6_SELECT + bool "Add support for standard UART6" + help + Adds support for standard UART 6 when the 8250 serial support + is enabled. + +endmenu + +endif + diff --git a/arch/arm/mach-lpc32xx/Makefile b/arch/arm/mach-lpc32xx/Makefile new file mode 100644 index 00000000000..a5fc5d0eeae --- /dev/null +++ b/arch/arm/mach-lpc32xx/Makefile @@ -0,0 +1,8 @@ +# +# Makefile for the linux kernel. +# + +obj-y := timer.o irq.o common.o serial.o clock.o +obj-y += gpiolib.o pm.o suspend.o +obj-y += phy3250.o + diff --git a/arch/arm/mach-lpc32xx/Makefile.boot b/arch/arm/mach-lpc32xx/Makefile.boot new file mode 100644 index 00000000000..b796b41ebf8 --- /dev/null +++ b/arch/arm/mach-lpc32xx/Makefile.boot @@ -0,0 +1,4 @@ + zreladdr-y := 0x80008000 +params_phys-y := 0x80000100 +initrd_phys-y := 0x82000000 + diff --git a/arch/arm/mach-lpc32xx/clock.c b/arch/arm/mach-lpc32xx/clock.c new file mode 100644 index 00000000000..32d63796430 --- /dev/null +++ b/arch/arm/mach-lpc32xx/clock.c @@ -0,0 +1,1137 @@ +/* + * arch/arm/mach-lpc32xx/clock.c + * + * Author: Kevin Wells <kevin.wells@nxp.com> + * + * Copyright (C) 2010 NXP Semiconductors + * + * 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. + */ + +/* + * LPC32xx clock management driver overview + * + * The LPC32XX contains a number of high level system clocks that can be + * generated from different sources. These system clocks are used to + * generate the CPU and bus rates and the individual peripheral clocks in + * the system. When Linux is started by the boot loader, the system + * clocks are already running. Stopping a system clock during normal + * Linux operation should never be attempted, as peripherals that require + * those clocks will quit working (ie, DRAM). + * + * The LPC32xx high level clock tree looks as follows. Clocks marked with + * an asterisk are always on and cannot be disabled. Clocks marked with + * an ampersand can only be disabled in CPU suspend mode. Clocks marked + * with a caret are always on if it is the selected clock for the SYSCLK + * source. The clock that isn't used for SYSCLK can be enabled and + * disabled normally. + * 32KHz oscillator* + * / | \ + * RTC* PLL397^ TOUCH + * / + * Main oscillator^ / + * | \ / + * | SYSCLK& + * | \ + * | \ + * USB_PLL HCLK_PLL& + * | | | + * USB host/device PCLK& | + * | | + * Peripherals + * + * The CPU and chip bus rates are derived from the HCLK PLL, which can + * generate various clock rates up to 266MHz and beyond. The internal bus + * rates (PCLK and HCLK) are generated from dividers based on the HCLK + * PLL rate. HCLK can be a ratio of 1:1, 1:2, or 1:4 or HCLK PLL rate, + * while PCLK can be 1:1 to 1:32 of HCLK PLL rate. Most peripherals high + * level clocks are based on either HCLK or PCLK, but have their own + * dividers as part of the IP itself. Because of this, the system clock + * rates should not be changed. + * + * The HCLK PLL is clocked from SYSCLK, which can be derived from the + * main oscillator or PLL397. PLL397 generates a rate that is 397 times + * the 32KHz oscillator rate. The main oscillator runs at the selected + * oscillator/crystal rate on the mosc_in pin of the LPC32xx. This rate + * is normally 13MHz, but depends on the selection of external crystals + * or oscillators. If USB operation is required, the main oscillator must + * be used in the system. + * + * Switching SYSCLK between sources during normal Linux operation is not + * supported. SYSCLK is preset in the bootloader. Because of the + * complexities of clock management during clock frequency changes, + * there are some limitations to the clock driver explained below: + * - The PLL397 and main oscillator can be enabled and disabled by the + * clk_enable() and clk_disable() functions unless SYSCLK is based + * on that clock. This allows the other oscillator that isn't driving + * the HCLK PLL to be used as another system clock that can be routed + * to an external pin. + * - The muxed SYSCLK input and HCLK_PLL rate cannot be changed with + * this driver. + * - HCLK and PCLK rates cannot be changed as part of this driver. + * - Most peripherals have their own dividers are part of the peripheral + * block. Changing SYSCLK, HCLK PLL, HCLK, or PCLK sources or rates + * will also impact the individual peripheral rates. + */ + +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/errno.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/amba/bus.h> +#include <linux/amba/clcd.h> + +#include <mach/hardware.h> +#include <asm/clkdev.h> +#include <mach/clkdev.h> +#include <mach/platform.h> +#include "clock.h" +#include "common.h" + +static struct clk clk_armpll; +static struct clk clk_usbpll; +static DEFINE_MUTEX(clkm_lock); + +/* + * Post divider values for PLLs based on selected register value + */ +static const u32 pll_postdivs[4] = {1, 2, 4, 8}; + +static unsigned long local_return_parent_rate(struct clk *clk) +{ + /* + * If a clock has a rate of 0, then it inherits it's parent + * clock rate + */ + while (clk->rate == 0) + clk = clk->parent; + + return clk->rate; +} + +/* 32KHz clock has a fixed rate and is not stoppable */ +static struct clk osc_32KHz = { + .rate = LPC32XX_CLOCK_OSC_FREQ, + .get_rate = local_return_parent_rate, +}; + +static int local_pll397_enable(struct clk *clk, int enable) +{ + u32 reg; + unsigned long timeout = 1 + msecs_to_jiffies(10); + + reg = __raw_readl(LPC32XX_CLKPWR_PLL397_CTRL); + + if (enable == 0) { + reg |= LPC32XX_CLKPWR_SYSCTRL_PLL397_DIS; + __raw_writel(reg, LPC32XX_CLKPWR_PLL397_CTRL); + } else { + /* Enable PLL397 */ + reg &= ~LPC32XX_CLKPWR_SYSCTRL_PLL397_DIS; + __raw_writel(reg, LPC32XX_CLKPWR_PLL397_CTRL); + + /* Wait for PLL397 lock */ + while (((__raw_readl(LPC32XX_CLKPWR_PLL397_CTRL) & + LPC32XX_CLKPWR_SYSCTRL_PLL397_STS) == 0) && + (timeout > jiffies)) + cpu_relax(); + + if ((__raw_readl(LPC32XX_CLKPWR_PLL397_CTRL) & + LPC32XX_CLKPWR_SYSCTRL_PLL397_STS) == 0) + return -ENODEV; + } + + return 0; +} + +static int local_oscmain_enable(struct clk *clk, int enable) +{ + u32 reg; + unsigned long timeout = 1 + msecs_to_jiffies(10); + + reg = __raw_readl(LPC32XX_CLKPWR_MAIN_OSC_CTRL); + + if (enable == 0) { + reg |= LPC32XX_CLKPWR_MOSC_DISABLE; + __raw_writel(reg, LPC32XX_CLKPWR_MAIN_OSC_CTRL); + } else { + /* Enable main oscillator */ + reg &= ~LPC32XX_CLKPWR_MOSC_DISABLE; + __raw_writel(reg, LPC32XX_CLKPWR_MAIN_OSC_CTRL); + + /* Wait for main oscillator to start */ + while (((__raw_readl(LPC32XX_CLKPWR_MAIN_OSC_CTRL) & + LPC32XX_CLKPWR_MOSC_DISABLE) != 0) && + (timeout > jiffies)) + cpu_relax(); + + if ((__raw_readl(LPC32XX_CLKPWR_MAIN_OSC_CTRL) & + LPC32XX_CLKPWR_MOSC_DISABLE) != 0) + return -ENODEV; + } + + return 0; +} + +static struct clk osc_pll397 = { + .parent = &osc_32KHz, + .enable = local_pll397_enable, + .rate = LPC32XX_CLOCK_OSC_FREQ * 397, + .get_rate = local_return_parent_rate, +}; + +static struct clk osc_main = { + .enable = local_oscmain_enable, + .rate = LPC32XX_MAIN_OSC_FREQ, + .get_rate = local_return_parent_rate, +}; + +static struct clk clk_sys; + +/* + * Convert a PLL register value to a PLL output frequency + */ +u32 clk_get_pllrate_from_reg(u32 inputclk, u32 regval) +{ + struct clk_pll_setup pllcfg; + + pllcfg.cco_bypass_b15 = 0; + pllcfg.direct_output_b14 = 0; + pllcfg.fdbk_div_ctrl_b13 = 0; + if ((regval & LPC32XX_CLKPWR_HCLKPLL_CCO_BYPASS) != 0) + pllcfg.cco_bypass_b15 = 1; + if ((regval & LPC32XX_CLKPWR_HCLKPLL_POSTDIV_BYPASS) != 0) + pllcfg.direct_output_b14 = 1; + if ((regval & LPC32XX_CLKPWR_HCLKPLL_FDBK_SEL_FCLK) != 0) + pllcfg.fdbk_div_ctrl_b13 = 1; + pllcfg.pll_m = 1 + ((regval >> 1) & 0xFF); + pllcfg.pll_n = 1 + ((regval >> 9) & 0x3); + pllcfg.pll_p = pll_postdivs[((regval >> 11) & 0x3)]; + + return clk_check_pll_setup(inputclk, &pllcfg); +} + +/* + * Setup the HCLK PLL with a PLL structure + */ +static u32 local_clk_pll_setup(struct clk_pll_setup *PllSetup) +{ + u32 tv, tmp = 0; + + if (PllSetup->analog_on != 0) + tmp |= LPC32XX_CLKPWR_HCLKPLL_POWER_UP; + if (PllSetup->cco_bypass_b15 != 0) + tmp |= LPC32XX_CLKPWR_HCLKPLL_CCO_BYPASS; + if (PllSetup->direct_output_b14 != 0) + tmp |= LPC32XX_CLKPWR_HCLKPLL_POSTDIV_BYPASS; + if (PllSetup->fdbk_div_ctrl_b13 != 0) + tmp |= LPC32XX_CLKPWR_HCLKPLL_FDBK_SEL_FCLK; + + tv = ffs(PllSetup->pll_p) - 1; + if ((!is_power_of_2(PllSetup->pll_p)) || (tv > 3)) + return 0; + + tmp |= LPC32XX_CLKPWR_HCLKPLL_POSTDIV_2POW(tv); + tmp |= LPC32XX_CLKPWR_HCLKPLL_PREDIV_PLUS1(PllSetup->pll_n - 1); + tmp |= LPC32XX_CLKPWR_HCLKPLL_PLLM(PllSetup->pll_m - 1); + + return tmp; +} + +/* + * Update the ARM core PLL frequency rate variable from the actual PLL setting + */ +static void local_update_armpll_rate(void) +{ + u32 clkin, pllreg; + + clkin = clk_armpll.parent->rate; + pllreg = __raw_readl(LPC32XX_CLKPWR_HCLKPLL_CTRL) & 0x1FFFF; + + clk_armpll.rate = clk_get_pllrate_from_reg(clkin, pllreg); +} + +/* + * Find a PLL configuration for the selected input frequency + */ +static u32 local_clk_find_pll_cfg(u32 pllin_freq, u32 target_freq, + struct clk_pll_setup *pllsetup) +{ + u32 ifreq, freqtol, m, n, p, fclkout; + + /* Determine frequency tolerance limits */ + freqtol = target_freq / 250; + ifreq = pllin_freq; + + /* Is direct bypass mode possible? */ + if (abs(pllin_freq - target_freq) <= freqtol) { + pllsetup->analog_on = 0; + pllsetup->cco_bypass_b15 = 1; + pllsetup->direct_output_b14 = 1; + pllsetup->fdbk_div_ctrl_b13 = 1; + pllsetup->pll_p = pll_postdivs[0]; + pllsetup->pll_n = 1; + pllsetup->pll_m = 1; + return clk_check_pll_setup(ifreq, pllsetup); + } else if (target_freq <= ifreq) { + pllsetup->analog_on = 0; + pllsetup->cco_bypass_b15 = 1; + pllsetup->direct_output_b14 = 0; + pllsetup->fdbk_div_ctrl_b13 = 1; + pllsetup->pll_n = 1; + pllsetup->pll_m = 1; + for (p = 0; p <= 3; p++) { + pllsetup->pll_p = pll_postdivs[p]; + fclkout = clk_check_pll_setup(ifreq, pllsetup); + if (abs(target_freq - fclkout) <= freqtol) + return fclkout; + } + } + + /* Is direct mode possible? */ + pllsetup->analog_on = 1; + pllsetup->cco_bypass_b15 = 0; + pllsetup->direct_output_b14 = 1; + pllsetup->fdbk_div_ctrl_b13 = 0; + pllsetup->pll_p = pll_postdivs[0]; + for (m = 1; m <= 256; m++) { + for (n = 1; n <= 4; n++) { + /* Compute output frequency for this value */ + pllsetup->pll_n = n; + pllsetup->pll_m = m; + fclkout = clk_check_pll_setup(ifreq, + pllsetup); + if (abs(target_freq - fclkout) <= + freqtol) + return fclkout; + } + } + + /* Is integer mode possible? */ + pllsetup->analog_on = 1; + pllsetup->cco_bypass_b15 = 0; + pllsetup->direct_output_b14 = 0; + pllsetup->fdbk_div_ctrl_b13 = 1; + for (m = 1; m <= 256; m++) { + for (n = 1; n <= 4; n++) { + for (p = 0; p < 4; p++) { + /* Compute output frequency */ + pllsetup->pll_p = pll_postdivs[p]; + pllsetup->pll_n = n; + pllsetup->pll_m = m; + fclkout = clk_check_pll_setup( + ifreq, pllsetup); + if (abs(target_freq - fclkout) <= freqtol) + return fclkout; + } + } + } + + /* Try non-integer mode */ + pllsetup->analog_on = 1; + pllsetup->cco_bypass_b15 = 0; + pllsetup->direct_output_b14 = 0; + pllsetup->fdbk_div_ctrl_b13 = 0; + for (m = 1; m <= 256; m++) { + for (n = 1; n <= 4; n++) { + for (p = 0; p < 4; p++) { + /* Compute output frequency */ + pllsetup->pll_p = pll_postdivs[p]; + pllsetup->pll_n = n; + pllsetup->pll_m = m; + fclkout = clk_check_pll_setup( + ifreq, pllsetup); + if (abs(target_freq - fclkout) <= freqtol) + return fclkout; + } + } + } + + return 0; +} + +static struct clk clk_armpll = { + .parent = &clk_sys, + .get_rate = local_return_parent_rate, +}; + +/* + * Setup the USB PLL with a PLL structure + */ +static u32 local_clk_usbpll_setup(struct clk_pll_setup *pHCLKPllSetup) +{ + u32 reg, tmp = local_clk_pll_setup(pHCLKPllSetup); + + reg = __raw_readl(LPC32XX_CLKPWR_USB_CTRL) & ~0x1FFFF; + reg |= tmp; + __raw_writel(reg, LPC32XX_CLKPWR_USB_CTRL); + + return clk_check_pll_setup(clk_usbpll.parent->rate, + pHCLKPllSetup); +} + +static int local_usbpll_enable(struct clk *clk, int enable) +{ + u32 reg; + int ret = -ENODEV; + unsigned long timeout = 1 + msecs_to_jiffies(10); + + reg = __raw_readl(LPC32XX_CLKPWR_USB_CTRL); + + if (enable == 0) { + reg &= ~(LPC32XX_CLKPWR_USBCTRL_CLK_EN1 | + LPC32XX_CLKPWR_USBCTRL_CLK_EN2); + __raw_writel(reg, LPC32XX_CLKPWR_USB_CTRL); + } else if (reg & LPC32XX_CLKPWR_USBCTRL_PLL_PWRUP) { + reg |= LPC32XX_CLKPWR_USBCTRL_CLK_EN1; + __raw_writel(reg, LPC32XX_CLKPWR_USB_CTRL); + + /* Wait for PLL lock */ + while ((timeout > jiffies) & (ret == -ENODEV)) { + reg = __raw_readl(LPC32XX_CLKPWR_USB_CTRL); + if (reg & LPC32XX_CLKPWR_USBCTRL_PLL_STS) + ret = 0; + } + + if (ret == 0) { + reg |= LPC32XX_CLKPWR_USBCTRL_CLK_EN2; + __raw_writel(reg, LPC32XX_CLKPWR_USB_CTRL); + } + } + + return ret; +} + +static unsigned long local_usbpll_round_rate(struct clk *clk, + unsigned long rate) +{ + u32 clkin, usbdiv; + struct clk_pll_setup pllsetup; + + /* + * Unlike other clocks, this clock has a KHz input rate, so bump + * it up to work with the PLL function + */ + rate = rate * 1000; + + clkin = clk->parent->rate; + usbdiv = (__raw_readl(LPC32XX_CLKPWR_USBCLK_PDIV) & + LPC32XX_CLKPWR_USBPDIV_PLL_MASK) + 1; + clkin = clkin / usbdiv; + + /* Try to find a good rate setup */ + if (local_clk_find_pll_cfg(clkin, rate, &pllsetup) == 0) + return 0; + + return clk_check_pll_setup(clkin, &pllsetup); +} + +static int local_usbpll_set_rate(struct clk *clk, unsigned long rate) +{ + u32 clkin, reg, usbdiv; + struct clk_pll_setup pllsetup; + + /* + * Unlike other clocks, this clock has a KHz input rate, so bump + * it up to work with the PLL function + */ + rate = rate * 1000; + + clkin = clk->get_rate(clk); + usbdiv = (__raw_readl(LPC32XX_CLKPWR_USBCLK_PDIV) & + LPC32XX_CLKPWR_USBPDIV_PLL_MASK) + 1; + clkin = clkin / usbdiv; + + /* Try to find a good rate setup */ + if (local_clk_find_pll_cfg(clkin, rate, &pllsetup) == 0) + return -EINVAL; + + local_usbpll_enable(clk, 0); + + reg = __raw_readl(LPC32XX_CLKPWR_USB_CTRL); + reg |= LPC32XX_CLKPWR_USBCTRL_CLK_EN1; + __raw_writel(reg, LPC32XX_CLKPWR_USB_CTRL); + + pllsetup.analog_on = 1; + local_clk_usbpll_setup(&pllsetup); + + clk->rate = clk_check_pll_setup(clkin, &pllsetup); + + reg = __raw_readl(LPC32XX_CLKPWR_USB_CTRL); + reg |= LPC32XX_CLKPWR_USBCTRL_CLK_EN2; + __raw_writel(reg, LPC32XX_CLKPWR_USB_CTRL); + + return 0; +} + +static struct clk clk_usbpll = { + .parent = &osc_main, + .set_rate = local_usbpll_set_rate, + .enable = local_usbpll_enable, + .rate = 48000, /* In KHz */ + .get_rate = local_return_parent_rate, + .round_rate = local_usbpll_round_rate, +}; + +static u32 clk_get_hclk_div(void) +{ + static const u32 hclkdivs[4] = {1, 2, 4, 4}; + return hclkdivs[LPC32XX_CLKPWR_HCLKDIV_DIV_2POW( + __raw_readl(LPC32XX_CLKPWR_HCLK_DIV))]; +} + +static struct clk clk_hclk = { + .parent = &clk_armpll, + .get_rate = local_return_parent_rate, +}; + +static struct clk clk_pclk = { + .parent = &clk_armpll, + .get_rate = local_return_parent_rate, +}; + +static int local_onoff_enable(struct clk *clk, int enable) +{ + u32 tmp; + + tmp = __raw_readl(clk->enable_reg); + + if (enable == 0) + tmp &= ~clk->enable_mask; + else + tmp |= clk->enable_mask; + + __raw_writel(tmp, clk->enable_reg); + + return 0; +} + +/* Peripheral clock sources */ +static struct clk clk_timer0 = { + .parent = &clk_pclk, + .enable = local_onoff_enable, + .enable_reg = LPC32XX_CLKPWR_TIMERS_PWMS_CLK_CTRL_1, + .enable_mask = LPC32XX_CLKPWR_TMRPWMCLK_TIMER0_EN, + .get_rate = local_return_parent_rate, +}; +static struct clk clk_timer1 = { + .parent = &clk_pclk, + .enable = local_onoff_enable, + .enable_reg = LPC32XX_CLKPWR_TIMERS_PWMS_CLK_CTRL_1, + .enable_mask = LPC32XX_CLKPWR_TMRPWMCLK_TIMER1_EN, + .get_rate = local_return_parent_rate, +}; +static struct clk clk_timer2 = { + .parent = &clk_pclk, + .enable = local_onoff_enable, + .enable_reg = LPC32XX_CLKPWR_TIMERS_PWMS_CLK_CTRL_1, + .enable_mask = LPC32XX_CLKPWR_TMRPWMCLK_TIMER2_EN, + .get_rate = local_return_parent_rate, +}; +static struct clk clk_timer3 = { + .parent = &clk_pclk, + .enable = local_onoff_enable, + .enable_reg = LPC32XX_CLKPWR_TIMERS_PWMS_CLK_CTRL_1, + .enable_mask = LPC32XX_CLKPWR_TMRPWMCLK_TIMER3_EN, + .get_rate = local_return_parent_rate, +}; +static struct clk clk_wdt = { + .parent = &clk_pclk, + .enable = local_onoff_enable, + .enable_reg = LPC32XX_CLKPWR_TIMER_CLK_CTRL, + .enable_mask = LPC32XX_CLKPWR_PWMCLK_WDOG_EN, + .get_rate = local_return_parent_rate, +}; +static struct clk clk_vfp9 = { + .parent = &clk_pclk, + .enable = local_onoff_enable, + .enable_reg = LPC32XX_CLKPWR_DEBUG_CTRL, + .enable_mask = LPC32XX_CLKPWR_VFP_CLOCK_ENABLE_BIT, + .get_rate = local_return_parent_rate, +}; +static struct clk clk_dma = { + .parent = &clk_hclk, + .enable = local_onoff_enable, + .enable_reg = LPC32XX_CLKPWR_DMA_CLK_CTRL, + .enable_mask = LPC32XX_CLKPWR_DMACLKCTRL_CLK_EN, + .get_rate = local_return_parent_rate, +}; + +static struct clk clk_uart3 = { + .parent = &clk_pclk, + .enable = local_onoff_enable, + .enable_reg = LPC32XX_CLKPWR_UART_CLK_CTRL, + .enable_mask = LPC32XX_CLKPWR_UARTCLKCTRL_UART3_EN, + .get_rate = local_return_parent_rate, +}; + +static struct clk clk_uart4 = { + .parent = &clk_pclk, + .enable = local_onoff_enable, + .enable_reg = LPC32XX_CLKPWR_UART_CLK_CTRL, + .enable_mask = LPC32XX_CLKPWR_UARTCLKCTRL_UART4_EN, + .get_rate = local_return_parent_rate, +}; + +static struct clk clk_uart5 = { + .parent = &clk_pclk, + .enable = local_onoff_enable, + .enable_reg = LPC32XX_CLKPWR_UART_CLK_CTRL, + .enable_mask = LPC32XX_CLKPWR_UARTCLKCTRL_UART5_EN, + .get_rate = local_return_parent_rate, +}; + +static struct clk clk_uart6 = { + .parent = &clk_pclk, + .enable = local_onoff_enable, + .enable_reg = LPC32XX_CLKPWR_UART_CLK_CTRL, + .enable_mask = LPC32XX_CLKPWR_UARTCLKCTRL_UART6_EN, + .get_rate = local_return_parent_rate, +}; + +static struct clk clk_i2c0 = { + .parent = &clk_hclk, + .enable = local_onoff_enable, + .enable_reg = LPC32XX_CLKPWR_I2C_CLK_CTRL, + .enable_mask = LPC32XX_CLKPWR_I2CCLK_I2C1CLK_EN, + .get_rate = local_return_parent_rate, +}; + +static struct clk clk_i2c1 = { + .parent = &clk_hclk, + .enable = local_onoff_enable, + .enable_reg = LPC32XX_CLKPWR_I2C_CLK_CTRL, + .enable_mask = LPC32XX_CLKPWR_I2CCLK_I2C2CLK_EN, + .get_rate = local_return_parent_rate, +}; + +static struct clk clk_i2c2 = { + .parent = &clk_pclk, + .enable = local_onoff_enable, + .enable_reg = io_p2v(LPC32XX_USB_BASE + 0xFF4), + .enable_mask = 0x4, + .get_rate = local_return_parent_rate, +}; + +static struct clk clk_ssp0 = { + .parent = &clk_hclk, + .enable = local_onoff_enable, + .enable_reg = LPC32XX_CLKPWR_SSP_CLK_CTRL, + .enable_mask = LPC32XX_CLKPWR_SSPCTRL_SSPCLK0_EN, + .get_rate = local_return_parent_rate, +}; + +static struct clk clk_ssp1 = { + .parent = &clk_hclk, + .enable = local_onoff_enable, + .enable_reg = LPC32XX_CLKPWR_SSP_CLK_CTRL, + .enable_mask = LPC32XX_CLKPWR_SSPCTRL_SSPCLK1_EN, + .get_rate = local_return_parent_rate, +}; + +static struct clk clk_kscan = { + .parent = &osc_32KHz, + .enable = local_onoff_enable, + .enable_reg = LPC32XX_CLKPWR_KEY_CLK_CTRL, + .enable_mask = LPC32XX_CLKPWR_KEYCLKCTRL_CLK_EN, + .get_rate = local_return_parent_rate, +}; + +static struct clk clk_nand = { + .parent = &clk_hclk, + .enable = local_onoff_enable, + .enable_reg = LPC32XX_CLKPWR_NAND_CLK_CTRL, + .enable_mask = LPC32XX_CLKPWR_NANDCLK_SLCCLK_EN, + .get_rate = local_return_parent_rate, +}; + +static struct clk clk_i2s0 = { + .parent = &clk_hclk, + .enable = local_onoff_enable, + .enable_reg = LPC32XX_CLKPWR_I2S_CLK_CTRL, + .enable_mask = LPC32XX_CLKPWR_I2SCTRL_I2SCLK0_EN, + .get_rate = local_return_parent_rate, +}; + +static struct clk clk_i2s1 = { + .parent = &clk_hclk, + .enable = local_onoff_enable, + .enable_reg = LPC32XX_CLKPWR_I2S_CLK_CTRL, + .enable_mask = LPC32XX_CLKPWR_I2SCTRL_I2SCLK1_EN, + .get_rate = local_return_parent_rate, +}; + +static struct clk clk_net = { + .parent = &clk_hclk, + .enable = local_onoff_enable, + .enable_reg = LPC32XX_CLKPWR_MACCLK_CTRL, + .enable_mask = (LPC32XX_CLKPWR_MACCTRL_DMACLK_EN | + LPC32XX_CLKPWR_MACCTRL_MMIOCLK_EN | + LPC32XX_CLKPWR_MACCTRL_HRCCLK_EN), + .get_rate = local_return_parent_rate, +}; + +static struct clk clk_rtc = { + .parent = &osc_32KHz, + .rate = 1, /* 1 Hz */ + .get_rate = local_return_parent_rate, +}; + +static struct clk clk_usbd = { + .parent = &clk_usbpll, + .enable = local_onoff_enable, + .enable_reg = LPC32XX_CLKPWR_USB_CTRL, + .enable_mask = LPC32XX_CLKPWR_USBCTRL_HCLK_EN, + .get_rate = local_return_parent_rate, +}; + +static int tsc_onoff_enable(struct clk *clk, int enable) +{ + u32 tmp; + + /* Make sure 32KHz clock is the selected clock */ + tmp = __raw_readl(LPC32XX_CLKPWR_ADC_CLK_CTRL_1); + tmp &= ~LPC32XX_CLKPWR_ADCCTRL1_PCLK_SEL; + __raw_writel(tmp, LPC32XX_CLKPWR_ADC_CLK_CTRL_1); + + if (enable == 0) + __raw_writel(0, clk->enable_reg); + else + __raw_writel(clk->enable_mask, clk->enable_reg); + + return 0; +} + +static struct clk clk_tsc = { + .parent = &osc_32KHz, + .enable = tsc_onoff_enable, + .enable_reg = LPC32XX_CLKPWR_ADC_CLK_CTRL, + .enable_mask = LPC32XX_CLKPWR_ADC32CLKCTRL_CLK_EN, + .get_rate = local_return_parent_rate, +}; + +static int mmc_onoff_enable(struct clk *clk, int enable) +{ + u32 tmp; + + tmp = __raw_readl(LPC32XX_CLKPWR_MS_CTRL) & + ~LPC32XX_CLKPWR_MSCARD_SDCARD_EN; + + /* If rate is 0, disable clock */ + if (enable != 0) + tmp |= LPC32XX_CLKPWR_MSCARD_SDCARD_EN; + + __raw_writel(tmp, LPC32XX_CLKPWR_MS_CTRL); + + return 0; +} + +static unsigned long mmc_get_rate(struct clk *clk) +{ + u32 div, rate, oldclk; + + /* The MMC clock must be on when accessing an MMC register */ + oldclk = __raw_readl(LPC32XX_CLKPWR_MS_CTRL); + __raw_writel(oldclk | LPC32XX_CLKPWR_MSCARD_SDCARD_EN, + LPC32XX_CLKPWR_MS_CTRL); + div = __raw_readl(LPC32XX_CLKPWR_MS_CTRL); + __raw_writel(oldclk, LPC32XX_CLKPWR_MS_CTRL); + + /* Get the parent clock rate */ + rate = clk->parent->get_rate(clk->parent); + + /* Get the MMC controller clock divider value */ + div = div & LPC32XX_CLKPWR_MSCARD_SDCARD_DIV(0xf); + + if (!div) + div = 1; + + return rate / div; +} + +static unsigned long mmc_round_rate(struct clk *clk, unsigned long rate) +{ + unsigned long div, prate; + + /* Get the parent clock rate */ + prate = clk->parent->get_rate(clk->parent); + + if (rate >= prate) + return prate; + + div = prate / rate; + if (div > 0xf) + div = 0xf; + + return prate / div; +} + +static int mmc_set_rate(struct clk *clk, unsigned long rate) +{ + u32 oldclk, tmp; + unsigned long prate, div, crate = mmc_round_rate(clk, rate); + + prate = clk->parent->get_rate(clk->parent); + + div = prate / crate; + + /* The MMC clock must be on when accessing an MMC register */ + oldclk = __raw_readl(LPC32XX_CLKPWR_MS_CTRL); + __raw_writel(oldclk | LPC32XX_CLKPWR_MSCARD_SDCARD_EN, + LPC32XX_CLKPWR_MS_CTRL); + tmp = __raw_readl(LPC32XX_CLKPWR_MS_CTRL) & + ~LPC32XX_CLKPWR_MSCARD_SDCARD_DIV(0xf); + tmp |= LPC32XX_CLKPWR_MSCARD_SDCARD_DIV(div); + __raw_writel(tmp, LPC32XX_CLKPWR_MS_CTRL); + + __raw_writel(oldclk, LPC32XX_CLKPWR_MS_CTRL); + + return 0; +} + +static struct clk clk_mmc = { + .parent = &clk_armpll, + .set_rate = mmc_set_rate, + .get_rate = mmc_get_rate, + .round_rate = mmc_round_rate, + .enable = mmc_onoff_enable, + .enable_reg = LPC32XX_CLKPWR_MS_CTRL, + .enable_mask = LPC32XX_CLKPWR_MSCARD_SDCARD_EN, +}; + +static unsigned long clcd_get_rate(struct clk *clk) +{ + u32 tmp, div, rate, oldclk; + + /* The LCD clock must be on when accessing an LCD register */ + oldclk = __raw_readl(LPC32XX_CLKPWR_LCDCLK_CTRL); + __raw_writel(oldclk | LPC32XX_CLKPWR_LCDCTRL_CLK_EN, + LPC32XX_CLKPWR_LCDCLK_CTRL); + tmp = __raw_readl(io_p2v(LPC32XX_LCD_BASE + CLCD_TIM2)); + __raw_writel(oldclk, LPC32XX_CLKPWR_LCDCLK_CTRL); + + rate = clk->parent->get_rate(clk->parent); + + /* Only supports internal clocking */ + if (tmp & TIM2_BCD) + return rate; + + div = (tmp & 0x1F) | ((tmp & 0xF8) >> 22); + tmp = rate / (2 + div); + + return tmp; +} + +static int clcd_set_rate(struct clk *clk, unsigned long rate) +{ + u32 tmp, prate, div, oldclk; + + /* The LCD clock must be on when accessing an LCD register */ + oldclk = __raw_readl(LPC32XX_CLKPWR_LCDCLK_CTRL); + __raw_writel(oldclk | LPC32XX_CLKPWR_LCDCTRL_CLK_EN, + LPC32XX_CLKPWR_LCDCLK_CTRL); + + tmp = __raw_readl(io_p2v(LPC32XX_LCD_BASE + CLCD_TIM2)) | TIM2_BCD; + prate = clk->parent->get_rate(clk->parent); + + if (rate < prate) { + /* Find closest divider */ + div = prate / rate; + if (div >= 2) { + div -= 2; + tmp &= ~TIM2_BCD; + } + + tmp &= ~(0xF800001F); + tmp |= (div & 0x1F); + tmp |= (((div >> 5) & 0x1F) << 27); + } + + __raw_writel(tmp, io_p2v(LPC32XX_LCD_BASE + CLCD_TIM2)); + __raw_writel(oldclk, LPC32XX_CLKPWR_LCDCLK_CTRL); + + return 0; +} + +static unsigned long clcd_round_rate(struct clk *clk, unsigned long rate) +{ + u32 prate, div; + + prate = clk->parent->get_rate(clk->parent); + + if (rate >= prate) + rate = prate; + else { + div = prate / rate; + if (div > 0x3ff) + div = 0x3ff; + + rate = prate / div; + } + + return rate; +} + +static struct clk clk_lcd = { + .parent = &clk_hclk, + .set_rate = clcd_set_rate, + .get_rate = clcd_get_rate, + .round_rate = clcd_round_rate, + .enable = local_onoff_enable, + .enable_reg = LPC32XX_CLKPWR_LCDCLK_CTRL, + .enable_mask = LPC32XX_CLKPWR_LCDCTRL_CLK_EN, +}; + +static inline void clk_lock(void) +{ + mutex_lock(&clkm_lock); +} + +static inline void clk_unlock(void) +{ + mutex_unlock(&clkm_lock); +} + +static void local_clk_disable(struct clk *clk) +{ + WARN_ON(clk->usecount == 0); + + /* Don't attempt to disable clock if it has no users */ + if (clk->usecount > 0) { + clk->usecount--; + + /* Only disable clock when it has no more users */ + if ((clk->usecount == 0) && (clk->enable)) + clk->enable(clk, 0); + + /* Check parent clocks, they may need to be disabled too */ + if (clk->parent) + local_clk_disable(clk->parent); + } +} + +static int local_clk_enable(struct clk *clk) +{ + int ret = 0; + + /* Enable parent clocks first and update use counts */ + if (clk->parent) + ret = local_clk_enable(clk->parent); + + if (!ret) { + /* Only enable clock if it's currently disabled */ + if ((clk->usecount == 0) && (clk->enable)) + ret = clk->enable(clk, 1); + + if (!ret) + clk->usecount++; + else if (clk->parent) + local_clk_disable(clk->parent); + } + + return ret; +} + +/* + * clk_enable - inform the system when the clock source should be running. + */ +int clk_enable(struct clk *clk) +{ + int ret; + + clk_lock(); + ret = local_clk_enable(clk); + clk_unlock(); + + return ret; +} +EXPORT_SYMBOL(clk_enable); + +/* + * clk_disable - inform the system when the clock source is no longer required + */ +void clk_disable(struct clk *clk) +{ + clk_lock(); + local_clk_disable(clk); + clk_unlock(); +} +EXPORT_SYMBOL(clk_disable); + +/* + * clk_get_rate - obtain the current clock rate (in Hz) for a clock source + */ +unsigned long clk_get_rate(struct clk *clk) +{ + unsigned long rate; + + clk_lock(); + rate = clk->get_rate(clk); + clk_unlock(); + + return rate; +} +EXPORT_SYMBOL(clk_get_rate); + +/* + * clk_set_rate - set the clock rate for a clock source + */ +int clk_set_rate(struct clk *clk, unsigned long rate) +{ + int ret = -EINVAL; + + /* + * Most system clocks can only be enabled or disabled, with + * the actual rate set as part of the peripheral dividers + * instead of high level clock control + */ + if (clk->set_rate) { + clk_lock(); + ret = clk->set_rate(clk, rate); + clk_unlock(); + } + + return ret; +} +EXPORT_SYMBOL(clk_set_rate); + +/* + * clk_round_rate - adjust a rate to the exact rate a clock can provide + */ +long clk_round_rate(struct clk *clk, unsigned long rate) +{ + clk_lock(); + + if (clk->round_rate) + rate = clk->round_rate(clk, rate); + else + rate = clk->get_rate(clk); + + clk_unlock(); + + return rate; +} +EXPORT_SYMBOL(clk_round_rate); + +/* + * clk_set_parent - set the parent clock source for this clock + */ +int clk_set_parent(struct clk *clk, struct clk *parent) +{ + /* Clock re-parenting is not supported */ + return -EINVAL; +} +EXPORT_SYMBOL(clk_set_parent); + +/* + * clk_get_parent - get the parent clock source for this clock + */ +struct clk *clk_get_parent(struct clk *clk) +{ + return clk->parent; +} +EXPORT_SYMBOL(clk_get_parent); + +#define _REGISTER_CLOCK(d, n, c) \ + { \ + .dev_id = (d), \ + .con_id = (n), \ + .clk = &(c), \ + }, + +static struct clk_lookup lookups[] = { + _REGISTER_CLOCK(NULL, "osc_32KHz", osc_32KHz) + _REGISTER_CLOCK(NULL, "osc_pll397", osc_pll397) + _REGISTER_CLOCK(NULL, "osc_main", osc_main) + _REGISTER_CLOCK(NULL, "sys_ck", clk_sys) + _REGISTER_CLOCK(NULL, "arm_pll_ck", clk_armpll) + _REGISTER_CLOCK(NULL, "ck_pll5", clk_usbpll) + _REGISTER_CLOCK(NULL, "hclk_ck", clk_hclk) + _REGISTER_CLOCK(NULL, "pclk_ck", clk_pclk) + _REGISTER_CLOCK(NULL, "timer0_ck", clk_timer0) + _REGISTER_CLOCK(NULL, "timer1_ck", clk_timer1) + _REGISTER_CLOCK(NULL, "timer2_ck", clk_timer2) + _REGISTER_CLOCK(NULL, "timer3_ck", clk_timer3) + _REGISTER_CLOCK(NULL, "vfp9_ck", clk_vfp9) + _REGISTER_CLOCK(NULL, "clk_dmac", clk_dma) + _REGISTER_CLOCK("pnx4008-watchdog", NULL, clk_wdt) + _REGISTER_CLOCK(NULL, "uart3_ck", clk_uart3) + _REGISTER_CLOCK(NULL, "uart4_ck", clk_uart4) + _REGISTER_CLOCK(NULL, "uart5_ck", clk_uart5) + _REGISTER_CLOCK(NULL, "uart6_ck", clk_uart6) + _REGISTER_CLOCK("pnx-i2c.0", NULL, clk_i2c0) + _REGISTER_CLOCK("pnx-i2c.1", NULL, clk_i2c1) + _REGISTER_CLOCK("pnx-i2c.2", NULL, clk_i2c2) + _REGISTER_CLOCK("dev:ssp0", NULL, clk_ssp0) + _REGISTER_CLOCK("dev:ssp1", NULL, clk_ssp1) + _REGISTER_CLOCK("lpc32xx_keys.0", NULL, clk_kscan) + _REGISTER_CLOCK("lpc32xx-nand.0", "nand_ck", clk_nand) + _REGISTER_CLOCK("tbd", "i2s0_ck", clk_i2s0) + _REGISTER_CLOCK("tbd", "i2s1_ck", clk_i2s1) + _REGISTER_CLOCK("lpc32xx-ts", NULL, clk_tsc) + _REGISTER_CLOCK("dev:mmc0", "MCLK", clk_mmc) + _REGISTER_CLOCK("lpc-net.0", NULL, clk_net) + _REGISTER_CLOCK("dev:clcd", NULL, clk_lcd) + _REGISTER_CLOCK("lpc32xx_udc", "ck_usbd", clk_usbd) + _REGISTER_CLOCK("lpc32xx_rtc", NULL, clk_rtc) +}; + +static int __init clk_init(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(lookups); i++) + clkdev_add(&lookups[i]); + + /* + * Setup muxed SYSCLK for HCLK PLL base -this selects the + * parent clock used for the ARM PLL and is used to derive + * the many system clock rates in the device. + */ + if (clk_is_sysclk_mainosc() != 0) + clk_sys.parent = &osc_main; + else + clk_sys.parent = &osc_pll397; + + clk_sys.rate = clk_sys.parent->rate; + + /* Compute the current ARM PLL and USB PLL frequencies */ + local_update_armpll_rate(); + + /* Compute HCLK and PCLK bus rates */ + clk_hclk.rate = clk_hclk.parent->rate / clk_get_hclk_div(); + clk_pclk.rate = clk_pclk.parent->rate / clk_get_pclk_div(); + + /* + * Enable system clocks - this step is somewhat formal, as the + * clocks are already running, but it does get the clock data + * inline with the actual system state. Never disable these + * clocks as they will only stop if the system is going to sleep. + * In that case, the chip/system power management functions will + * handle clock gating. + */ + if (clk_enable(&clk_hclk) || clk_enable(&clk_pclk)) + printk(KERN_ERR "Error enabling system HCLK and PCLK\n"); + + /* + * Timers 0 and 1 were enabled and are being used by the high + * resolution tick function prior to this driver being initialized. + * Tag them now as used. + */ + if (clk_enable(&clk_timer0) || clk_enable(&clk_timer1)) + printk(KERN_ERR "Error enabling timer tick clocks\n"); + + return 0; +} +core_initcall(clk_init); + diff --git a/arch/arm/mach-lpc32xx/clock.h b/arch/arm/mach-lpc32xx/clock.h new file mode 100644 index 00000000000..c0a8434307f --- /dev/null +++ b/arch/arm/mach-lpc32xx/clock.h @@ -0,0 +1,38 @@ +/* + * arch/arm/mach-lpc32xx/clock.h + * + * Author: Kevin Wells <kevin.wells@nxp.com> + * + * Copyright (C) 2010 NXP Semiconductors + * + * 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. + */ + +#ifndef __LPC32XX_CLOCK_H +#define __LPC32XX_CLOCK_H + +struct clk { + struct list_head node; + struct clk *parent; + u32 rate; + u32 usecount; + + int (*set_rate) (struct clk *, unsigned long); + unsigned long (*round_rate) (struct clk *, unsigned long); + unsigned long (*get_rate) (struct clk *clk); + int (*enable) (struct clk *, int); + + /* Register address and bit mask for simple clocks */ + void __iomem *enable_reg; + u32 enable_mask; +}; + +#endif diff --git a/arch/arm/mach-lpc32xx/common.c b/arch/arm/mach-lpc32xx/common.c new file mode 100644 index 00000000000..ee24dc28e93 --- /dev/null +++ b/arch/arm/mach-lpc32xx/common.c @@ -0,0 +1,271 @@ +/* + * arch/arm/mach-lpc32xx/common.c + * + * Author: Kevin Wells <kevin.wells@nxp.com> + * + * Copyright (C) 2010 NXP Semiconductors + * + * 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. + */ + +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/i2c-pnx.h> +#include <linux/io.h> + +#include <asm/mach/map.h> + +#include <mach/i2c.h> +#include <mach/hardware.h> +#include <mach/platform.h> +#include "common.h" + +/* + * Watchdog timer + */ +static struct resource watchdog_resources[] = { + [0] = { + .start = LPC32XX_WDTIM_BASE, + .end = LPC32XX_WDTIM_BASE + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, +}; + +struct platform_device lpc32xx_watchdog_device = { + .name = "pnx4008-watchdog", + .id = -1, + .num_resources = ARRAY_SIZE(watchdog_resources), + .resource = watchdog_resources, +}; + +/* + * I2C busses + */ +static struct i2c_pnx_data i2c0_data = { + .name = I2C_CHIP_NAME "1", + .base = LPC32XX_I2C1_BASE, + .irq = IRQ_LPC32XX_I2C_1, +}; + +static struct i2c_pnx_data i2c1_data = { + .name = I2C_CHIP_NAME "2", + .base = LPC32XX_I2C2_BASE, + .irq = IRQ_LPC32XX_I2C_2, +}; + +static struct i2c_pnx_data i2c2_data = { + .name = "USB-I2C", + .base = LPC32XX_OTG_I2C_BASE, + .irq = IRQ_LPC32XX_USB_I2C, +}; + +struct platform_device lpc32xx_i2c0_device = { + .name = "pnx-i2c", + .id = 0, + .dev = { + .platform_data = &i2c0_data, + }, +}; + +struct platform_device lpc32xx_i2c1_device = { + .name = "pnx-i2c", + .id = 1, + .dev = { + .platform_data = &i2c1_data, + }, +}; + +struct platform_device lpc32xx_i2c2_device = { + .name = "pnx-i2c", + .id = 2, + .dev = { + .platform_data = &i2c2_data, + }, +}; + +/* + * Returns the unique ID for the device + */ +void lpc32xx_get_uid(u32 devid[4]) +{ + int i; + + for (i = 0; i < 4; i++) + devid[i] = __raw_readl(LPC32XX_CLKPWR_DEVID(i << 2)); +} + +/* + * Returns SYSCLK source + * 0 = PLL397, 1 = main oscillator + */ +int clk_is_sysclk_mainosc(void) +{ + if ((__raw_readl(LPC32XX_CLKPWR_SYSCLK_CTRL) & + LPC32XX_CLKPWR_SYSCTRL_SYSCLKMUX) == 0) + return 1; + + return 0; +} + +/* + * System reset via the watchdog timer + */ +void lpc32xx_watchdog_reset(void) +{ + /* Make sure WDT clocks are enabled */ + __raw_writel(LPC32XX_CLKPWR_PWMCLK_WDOG_EN, + LPC32XX_CLKPWR_TIMER_CLK_CTRL); + + /* Instant assert of RESETOUT_N with pulse length 1mS */ + __raw_writel(13000, io_p2v(LPC32XX_WDTIM_BASE + 0x18)); + __raw_writel(0x70, io_p2v(LPC32XX_WDTIM_BASE + 0xC)); +} + +/* + * Detects and returns IRAM size for the device variation + */ +#define LPC32XX_IRAM_BANK_SIZE SZ_128K +static u32 iram_size; +u32 lpc32xx_return_iram_size(void) +{ + if (iram_size == 0) { + u32 savedval1, savedval2; + void __iomem *iramptr1, *iramptr2; + + iramptr1 = io_p2v(LPC32XX_IRAM_BASE); + iramptr2 = io_p2v(LPC32XX_IRAM_BASE + LPC32XX_IRAM_BANK_SIZE); + savedval1 = __raw_readl(iramptr1); + savedval2 = __raw_readl(iramptr2); + + if (savedval1 == savedval2) { + __raw_writel(savedval2 + 1, iramptr2); + if (__raw_readl(iramptr1) == savedval2 + 1) + iram_size = LPC32XX_IRAM_BANK_SIZE; + else + iram_size = LPC32XX_IRAM_BANK_SIZE * 2; + __raw_writel(savedval2, iramptr2); + } else + iram_size = LPC32XX_IRAM_BANK_SIZE * 2; + } + + return iram_size; +} + +/* + * Computes PLL rate from PLL register and input clock + */ +u32 clk_check_pll_setup(u32 ifreq, struct clk_pll_setup *pllsetup) +{ + u32 ilfreq, p, m, n, fcco, fref, cfreq; + int mode; + + /* + * PLL requirements + * ifreq must be >= 1MHz and <= 20MHz + * FCCO must be >= 156MHz and <= 320MHz + * FREF must be >= 1MHz and <= 27MHz + * Assume the passed input data is not valid + */ + + ilfreq = ifreq; + m = pllsetup->pll_m; + n = pllsetup->pll_n; + p = pllsetup->pll_p; + + mode = (pllsetup->cco_bypass_b15 << 2) | + (pllsetup->direct_output_b14 << 1) | + pllsetup->fdbk_div_ctrl_b13; + + switch (mode) { + case 0x0: /* Non-integer mode */ + cfreq = (m * ilfreq) / (2 * p * n); + fcco = (m * ilfreq) / n; + fref = ilfreq / n; + break; + + case 0x1: /* integer mode */ + cfreq = (m * ilfreq) / n; + fcco = (m * ilfreq) / (n * 2 * p); + fref = ilfreq / n; + break; + + case 0x2: + case 0x3: /* Direct mode */ + cfreq = (m * ilfreq) / n; + fcco = cfreq; + fref = ilfreq / n; + break; + + case 0x4: + case 0x5: /* Bypass mode */ + cfreq = ilfreq / (2 * p); + fcco = 156000000; + fref = 1000000; + break; + + case 0x6: + case 0x7: /* Direct bypass mode */ + default: + cfreq = ilfreq; + fcco = 156000000; + fref = 1000000; + break; + } + + if (fcco < 156000000 || fcco > 320000000) + cfreq = 0; + + if (fref < 1000000 || fref > 27000000) + cfreq = 0; + + return (u32) cfreq; +} + +u32 clk_get_pclk_div(void) +{ + return 1 + ((__raw_readl(LPC32XX_CLKPWR_HCLK_DIV) >> 2) & 0x1F); +} + +static struct map_desc lpc32xx_io_desc[] __initdata = { + { + .virtual = IO_ADDRESS(LPC32XX_AHB0_START), + .pfn = __phys_to_pfn(LPC32XX_AHB0_START), + .length = LPC32XX_AHB0_SIZE, + .type = MT_DEVICE + }, + { + .virtual = IO_ADDRESS(LPC32XX_AHB1_START), + .pfn = __phys_to_pfn(LPC32XX_AHB1_START), + .length = LPC32XX_AHB1_SIZE, + .type = MT_DEVICE + }, + { + .virtual = IO_ADDRESS(LPC32XX_FABAPB_START), + .pfn = __phys_to_pfn(LPC32XX_FABAPB_START), + .length = LPC32XX_FABAPB_SIZE, + .type = MT_DEVICE + }, + { + .virtual = IO_ADDRESS(LPC32XX_IRAM_BASE), + .pfn = __phys_to_pfn(LPC32XX_IRAM_BASE), + .length = (LPC32XX_IRAM_BANK_SIZE * 2), + .type = MT_DEVICE + }, +}; + +void __init lpc32xx_map_io(void) +{ + iotable_init(lpc32xx_io_desc, ARRAY_SIZE(lpc32xx_io_desc)); +} diff --git a/arch/arm/mach-lpc32xx/common.h b/arch/arm/mach-lpc32xx/common.h new file mode 100644 index 00000000000..f82211fd80c --- /dev/null +++ b/arch/arm/mach-lpc32xx/common.h @@ -0,0 +1,73 @@ +/* + * arch/arm/mach-lpc32xx/common.h + * + * Author: Kevin Wells <kevin.wells@nxp.com> + * + * Copyright (C) 2009-2010 NXP Semiconductors + * + * 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. + */ + +#ifndef __LPC32XX_COMMON_H +#define __LPC32XX_COMMON_H + +#include <linux/platform_device.h> + +/* + * Arch specific platform device structures + */ +extern struct platform_device lpc32xx_watchdog_device; +extern struct platform_device lpc32xx_i2c0_device; +extern struct platform_device lpc32xx_i2c1_device; +extern struct platform_device lpc32xx_i2c2_device; + +/* + * Other arch specific structures and functions + */ +extern struct sys_timer lpc32xx_timer; +extern void __init lpc32xx_init_irq(void); +extern void __init lpc32xx_map_io(void); +extern void __init lpc32xx_serial_init(void); +extern void __init lpc32xx_gpio_init(void); + +/* + * Structure used for setting up and querying the PLLS + */ +struct clk_pll_setup { + int analog_on; + int cco_bypass_b15; + int direct_output_b14; + int fdbk_div_ctrl_b13; + int pll_p; + int pll_n; + u32 pll_m; +}; + +extern int clk_is_sysclk_mainosc(void); +extern u32 clk_check_pll_setup(u32 ifreq, struct clk_pll_setup *pllsetup); +extern u32 clk_get_pllrate_from_reg(u32 inputclk, u32 regval); +extern u32 clk_get_pclk_div(void); + +/* + * Returns the LPC32xx unique 128-bit chip ID + */ +extern void lpc32xx_get_uid(u32 devid[4]); + +extern void lpc32xx_watchdog_reset(void); +extern u32 lpc32xx_return_iram_size(void); + +/* + * Pointers used for sizing and copying suspend function data + */ +extern int lpc32xx_sys_suspend(void); +extern int lpc32xx_sys_suspend_sz; + +#endif diff --git a/arch/arm/mach-lpc32xx/gpiolib.c b/arch/arm/mach-lpc32xx/gpiolib.c new file mode 100644 index 00000000000..69061ea8997 --- /dev/null +++ b/arch/arm/mach-lpc32xx/gpiolib.c @@ -0,0 +1,446 @@ +/* + * arch/arm/mach-lpc32xx/gpiolib.c + * + * Author: Kevin Wells <kevin.wells@nxp.com> + * + * Copyright (C) 2010 NXP Semiconductors + * + * 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. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/errno.h> +#include <linux/gpio.h> + +#include <mach/hardware.h> +#include <mach/platform.h> +#include "common.h" + +#define LPC32XX_GPIO_P3_INP_STATE _GPREG(0x000) +#define LPC32XX_GPIO_P3_OUTP_SET _GPREG(0x004) +#define LPC32XX_GPIO_P3_OUTP_CLR _GPREG(0x008) +#define LPC32XX_GPIO_P3_OUTP_STATE _GPREG(0x00C) +#define LPC32XX_GPIO_P2_DIR_SET _GPREG(0x010) +#define LPC32XX_GPIO_P2_DIR_CLR _GPREG(0x014) +#define LPC32XX_GPIO_P2_DIR_STATE _GPREG(0x018) +#define LPC32XX_GPIO_P2_INP_STATE _GPREG(0x01C) +#define LPC32XX_GPIO_P2_OUTP_SET _GPREG(0x020) +#define LPC32XX_GPIO_P2_OUTP_CLR _GPREG(0x024) +#define LPC32XX_GPIO_P2_MUX_SET _GPREG(0x028) +#define LPC32XX_GPIO_P2_MUX_CLR _GPREG(0x02C) +#define LPC32XX_GPIO_P2_MUX_STATE _GPREG(0x030) +#define LPC32XX_GPIO_P0_INP_STATE _GPREG(0x040) +#define LPC32XX_GPIO_P0_OUTP_SET _GPREG(0x044) +#define LPC32XX_GPIO_P0_OUTP_CLR _GPREG(0x048) +#define LPC32XX_GPIO_P0_OUTP_STATE _GPREG(0x04C) +#define LPC32XX_GPIO_P0_DIR_SET _GPREG(0x050) +#define LPC32XX_GPIO_P0_DIR_CLR _GPREG(0x054) +#define LPC32XX_GPIO_P0_DIR_STATE _GPREG(0x058) +#define LPC32XX_GPIO_P1_INP_STATE _GPREG(0x060) +#define LPC32XX_GPIO_P1_OUTP_SET _GPREG(0x064) +#define LPC32XX_GPIO_P1_OUTP_CLR _GPREG(0x068) +#define LPC32XX_GPIO_P1_OUTP_STATE _GPREG(0x06C) +#define LPC32XX_GPIO_P1_DIR_SET _GPREG(0x070) +#define LPC32XX_GPIO_P1_DIR_CLR _GPREG(0x074) +#define LPC32XX_GPIO_P1_DIR_STATE _GPREG(0x078) + +#define GPIO012_PIN_TO_BIT(x) (1 << (x)) +#define GPIO3_PIN_TO_BIT(x) (1 << ((x) + 25)) +#define GPO3_PIN_TO_BIT(x) (1 << (x)) +#define GPIO012_PIN_IN_SEL(x, y) (((x) >> (y)) & 1) +#define GPIO3_PIN_IN_SHIFT(x) ((x) == 5 ? 24 : 10 + (x)) +#define GPIO3_PIN_IN_SEL(x, y) ((x) >> GPIO3_PIN_IN_SHIFT(y)) +#define GPIO3_PIN5_IN_SEL(x) (((x) >> 24) & 1) +#define GPI3_PIN_IN_SEL(x, y) (((x) >> (y)) & 1) + +struct gpio_regs { + void __iomem *inp_state; + void __iomem *outp_set; + void __iomem *outp_clr; + void __iomem *dir_set; + void __iomem *dir_clr; +}; + +/* + * GPIO names + */ +static const char *gpio_p0_names[LPC32XX_GPIO_P0_MAX] = { + "p0.0", "p0.1", "p0.2", "p0.3", + "p0.4", "p0.5", "p0.6", "p0.7" +}; + +static const char *gpio_p1_names[LPC32XX_GPIO_P1_MAX] = { + "p1.0", "p1.1", "p1.2", "p1.3", + "p1.4", "p1.5", "p1.6", "p1.7", + "p1.8", "p1.9", "p1.10", "p1.11", + "p1.12", "p1.13", "p1.14", "p1.15", + "p1.16", "p1.17", "p1.18", "p1.19", + "p1.20", "p1.21", "p1.22", "p1.23", +}; + +static const char *gpio_p2_names[LPC32XX_GPIO_P2_MAX] = { + "p2.0", "p2.1", "p2.2", "p2.3", + "p2.4", "p2.5", "p2.6", "p2.7", + "p2.8", "p2.9", "p2.10", "p2.11", + "p2.12" +}; + +static const char *gpio_p3_names[LPC32XX_GPIO_P3_MAX] = { + "gpi000", "gpio01", "gpio02", "gpio03", + "gpio04", "gpio05" +}; + +static const char *gpi_p3_names[LPC32XX_GPI_P3_MAX] = { + "gpi00", "gpi01", "gpi02", "gpi03", + "gpi04", "gpi05", "gpi06", "gpi07", + "gpi08", "gpi09", NULL, NULL, + NULL, NULL, NULL, "gpi15", + "gpi16", "gpi17", "gpi18", "gpi19", + "gpi20", "gpi21", "gpi22", "gpi23", + "gpi24", "gpi25", "gpi26", "gpi27" +}; + +static const char *gpo_p3_names[LPC32XX_GPO_P3_MAX] = { + "gpo00", "gpo01", "gpo02", "gpo03", + "gpo04", "gpo05", "gpo06", "gpo07", + "gpo08", "gpo09", "gpo10", "gpo11", + "gpo12", "gpo13", "gpo14", "gpo15", + "gpo16", "gpo17", "gpo18", "gpo19", + "gpo20", "gpo21", "gpo22", "gpo23" +}; + +static struct gpio_regs gpio_grp_regs_p0 = { + .inp_state = LPC32XX_GPIO_P0_INP_STATE, + .outp_set = LPC32XX_GPIO_P0_OUTP_SET, + .outp_clr = LPC32XX_GPIO_P0_OUTP_CLR, + .dir_set = LPC32XX_GPIO_P0_DIR_SET, + .dir_clr = LPC32XX_GPIO_P0_DIR_CLR, +}; + +static struct gpio_regs gpio_grp_regs_p1 = { + .inp_state = LPC32XX_GPIO_P1_INP_STATE, + .outp_set = LPC32XX_GPIO_P1_OUTP_SET, + .outp_clr = LPC32XX_GPIO_P1_OUTP_CLR, + .dir_set = LPC32XX_GPIO_P1_DIR_SET, + .dir_clr = LPC32XX_GPIO_P1_DIR_CLR, +}; + +static struct gpio_regs gpio_grp_regs_p2 = { + .inp_state = LPC32XX_GPIO_P2_INP_STATE, + .outp_set = LPC32XX_GPIO_P2_OUTP_SET, + .outp_clr = LPC32XX_GPIO_P2_OUTP_CLR, + .dir_set = LPC32XX_GPIO_P2_DIR_SET, + .dir_clr = LPC32XX_GPIO_P2_DIR_CLR, +}; + +static struct gpio_regs gpio_grp_regs_p3 = { + .inp_state = LPC32XX_GPIO_P3_INP_STATE, + .outp_set = LPC32XX_GPIO_P3_OUTP_SET, + .outp_clr = LPC32XX_GPIO_P3_OUTP_CLR, + .dir_set = LPC32XX_GPIO_P2_DIR_SET, + .dir_clr = LPC32XX_GPIO_P2_DIR_CLR, +}; + +struct lpc32xx_gpio_chip { + struct gpio_chip chip; + struct gpio_regs *gpio_grp; +}; + +static inline struct lpc32xx_gpio_chip *to_lpc32xx_gpio( + struct gpio_chip *gpc) +{ + return container_of(gpc, struct lpc32xx_gpio_chip, chip); +} + +static void __set_gpio_dir_p012(struct lpc32xx_gpio_chip *group, + unsigned pin, int input) +{ + if (input) + __raw_writel(GPIO012_PIN_TO_BIT(pin), + group->gpio_grp->dir_clr); + else + __raw_writel(GPIO012_PIN_TO_BIT(pin), + group->gpio_grp->dir_set); +} + +static void __set_gpio_dir_p3(struct lpc32xx_gpio_chip *group, + unsigned pin, int input) +{ + u32 u = GPIO3_PIN_TO_BIT(pin); + + if (input) + __raw_writel(u, group->gpio_grp->dir_clr); + else + __raw_writel(u, group->gpio_grp->dir_set); +} + +static void __set_gpio_level_p012(struct lpc32xx_gpio_chip *group, + unsigned pin, int high) +{ + if (high) + __raw_writel(GPIO012_PIN_TO_BIT(pin), + group->gpio_grp->outp_set); + else + __raw_writel(GPIO012_PIN_TO_BIT(pin), + group->gpio_grp->outp_clr); +} + +static void __set_gpio_level_p3(struct lpc32xx_gpio_chip *group, + unsigned pin, int high) +{ + u32 u = GPIO3_PIN_TO_BIT(pin); + + if (high) + __raw_writel(u, group->gpio_grp->outp_set); + else + __raw_writel(u, group->gpio_grp->outp_clr); +} + +static void __set_gpo_level_p3(struct lpc32xx_gpio_chip *group, + unsigned pin, int high) +{ + if (high) + __raw_writel(GPO3_PIN_TO_BIT(pin), group->gpio_grp->outp_set); + else + __raw_writel(GPO3_PIN_TO_BIT(pin), group->gpio_grp->outp_clr); +} + +static int __get_gpio_state_p012(struct lpc32xx_gpio_chip *group, + unsigned pin) +{ + return GPIO012_PIN_IN_SEL(__raw_readl(group->gpio_grp->inp_state), + pin); +} + +static int __get_gpio_state_p3(struct lpc32xx_gpio_chip *group, + unsigned pin) +{ + int state = __raw_readl(group->gpio_grp->inp_state); + + /* + * P3 GPIO pin input mapping is not contiguous, GPIOP3-0..4 is mapped + * to bits 10..14, while GPIOP3-5 is mapped to bit 24. + */ + return GPIO3_PIN_IN_SEL(state, pin); +} + +static int __get_gpi_state_p3(struct lpc32xx_gpio_chip *group, + unsigned pin) +{ + return GPI3_PIN_IN_SEL(__raw_readl(group->gpio_grp->inp_state), pin); +} + +/* + * GENERIC_GPIO primitives. + */ +static int lpc32xx_gpio_dir_input_p012(struct gpio_chip *chip, + unsigned pin) +{ + struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip); + + __set_gpio_dir_p012(group, pin, 1); + + return 0; +} + +static int lpc32xx_gpio_dir_input_p3(struct gpio_chip *chip, + unsigned pin) +{ + struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip); + + __set_gpio_dir_p3(group, pin, 1); + + return 0; +} + +static int lpc32xx_gpio_dir_in_always(struct gpio_chip *chip, + unsigned pin) +{ + return 0; +} + +static int lpc32xx_gpio_get_value_p012(struct gpio_chip *chip, unsigned pin) +{ + struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip); + + return __get_gpio_state_p012(group, pin); +} + +static int lpc32xx_gpio_get_value_p3(struct gpio_chip *chip, unsigned pin) +{ + struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip); + + return __get_gpio_state_p3(group, pin); +} + +static int lpc32xx_gpi_get_value(struct gpio_chip *chip, unsigned pin) +{ + struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip); + + return __get_gpi_state_p3(group, pin); +} + +static int lpc32xx_gpio_dir_output_p012(struct gpio_chip *chip, unsigned pin, + int value) +{ + struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip); + + __set_gpio_dir_p012(group, pin, 0); + + return 0; +} + +static int lpc32xx_gpio_dir_output_p3(struct gpio_chip *chip, unsigned pin, + int value) +{ + struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip); + + __set_gpio_dir_p3(group, pin, 0); + + return 0; +} + +static int lpc32xx_gpio_dir_out_always(struct gpio_chip *chip, unsigned pin, + int value) +{ + return 0; +} + +static void lpc32xx_gpio_set_value_p012(struct gpio_chip *chip, unsigned pin, + int value) +{ + struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip); + + __set_gpio_level_p012(group, pin, value); +} + +static void lpc32xx_gpio_set_value_p3(struct gpio_chip *chip, unsigned pin, + int value) +{ + struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip); + + __set_gpio_level_p3(group, pin, value); +} + +static void lpc32xx_gpo_set_value(struct gpio_chip *chip, unsigned pin, + int value) +{ + struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip); + + __set_gpo_level_p3(group, pin, value); +} + +static int lpc32xx_gpio_request(struct gpio_chip *chip, unsigned pin) +{ + if (pin < chip->ngpio) + return 0; + + return -EINVAL; +} + +static struct lpc32xx_gpio_chip lpc32xx_gpiochip[] = { + { + .chip = { + .label = "gpio_p0", + .direction_input = lpc32xx_gpio_dir_input_p012, + .get = lpc32xx_gpio_get_value_p012, + .direction_output = lpc32xx_gpio_dir_output_p012, + .set = lpc32xx_gpio_set_value_p012, + .request = lpc32xx_gpio_request, + .base = LPC32XX_GPIO_P0_GRP, + .ngpio = LPC32XX_GPIO_P0_MAX, + .names = gpio_p0_names, + .can_sleep = 0, + }, + .gpio_grp = &gpio_grp_regs_p0, + }, + { + .chip = { + .label = "gpio_p1", + .direction_input = lpc32xx_gpio_dir_input_p012, + .get = lpc32xx_gpio_get_value_p012, + .direction_output = lpc32xx_gpio_dir_output_p012, + .set = lpc32xx_gpio_set_value_p012, + .request = lpc32xx_gpio_request, + .base = LPC32XX_GPIO_P1_GRP, + .ngpio = LPC32XX_GPIO_P1_MAX, + .names = gpio_p1_names, + .can_sleep = 0, + }, + .gpio_grp = &gpio_grp_regs_p1, + }, + { + .chip = { + .label = "gpio_p2", + .direction_input = lpc32xx_gpio_dir_input_p012, + .get = lpc32xx_gpio_get_value_p012, + .direction_output = lpc32xx_gpio_dir_output_p012, + .set = lpc32xx_gpio_set_value_p012, + .request = lpc32xx_gpio_request, + .base = LPC32XX_GPIO_P2_GRP, + .ngpio = LPC32XX_GPIO_P2_MAX, + .names = gpio_p2_names, + .can_sleep = 0, + }, + .gpio_grp = &gpio_grp_regs_p2, + }, + { + .chip = { + .label = "gpio_p3", + .direction_input = lpc32xx_gpio_dir_input_p3, + .get = lpc32xx_gpio_get_value_p3, + .direction_output = lpc32xx_gpio_dir_output_p3, + .set = lpc32xx_gpio_set_value_p3, + .request = lpc32xx_gpio_request, + .base = LPC32XX_GPIO_P3_GRP, + .ngpio = LPC32XX_GPIO_P3_MAX, + .names = gpio_p3_names, + .can_sleep = 0, + }, + .gpio_grp = &gpio_grp_regs_p3, + }, + { + .chip = { + .label = "gpi_p3", + .direction_input = lpc32xx_gpio_dir_in_always, + .get = lpc32xx_gpi_get_value, + .request = lpc32xx_gpio_request, + .base = LPC32XX_GPI_P3_GRP, + .ngpio = LPC32XX_GPI_P3_MAX, + .names = gpi_p3_names, + .can_sleep = 0, + }, + .gpio_grp = &gpio_grp_regs_p3, + }, + { + .chip = { + .label = "gpo_p3", + .direction_output = lpc32xx_gpio_dir_out_always, + .set = lpc32xx_gpo_set_value, + .request = lpc32xx_gpio_request, + .base = LPC32XX_GPO_P3_GRP, + .ngpio = LPC32XX_GPO_P3_MAX, + .names = gpo_p3_names, + .can_sleep = 0, + }, + .gpio_grp = &gpio_grp_regs_p3, + }, +}; + +void __init lpc32xx_gpio_init(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(lpc32xx_gpiochip); i++) + gpiochip_add(&lpc32xx_gpiochip[i].chip); +} diff --git a/arch/arm/mach-lpc32xx/include/mach/clkdev.h b/arch/arm/mach-lpc32xx/include/mach/clkdev.h new file mode 100644 index 00000000000..9bf0637e29c --- /dev/null +++ b/arch/arm/mach-lpc32xx/include/mach/clkdev.h @@ -0,0 +1,25 @@ +/* + * arch/arm/mach-lpc32xx/include/mach/clkdev.h + * + * Author: Kevin Wells <kevin.wells@nxp.com> + * + * Copyright (C) 2010 NXP Semiconductors + * + * 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. + */ + +#ifndef __ASM_ARCH_CLKDEV_H +#define __ASM_ARCH_CLKDEV_H + +#define __clk_get(clk) ({ 1; }) +#define __clk_put(clk) do { } while (0) + +#endif diff --git a/arch/arm/mach-lpc32xx/include/mach/debug-macro.S b/arch/arm/mach-lpc32xx/include/mach/debug-macro.S new file mode 100644 index 00000000000..621744d6b15 --- /dev/null +++ b/arch/arm/mach-lpc32xx/include/mach/debug-macro.S @@ -0,0 +1,31 @@ +/* + * arch/arm/mach-lpc32xx/include/mach/debug-macro.S + * + * Author: Kevin Wells <kevin.wells@nxp.com> + * + * Copyright (C) 2010 NXP Semiconductors + * + * 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. + */ + +/* + * Debug output is hardcoded to standard UART 5 +*/ + + .macro addruart,rx, tmp + mrc p15, 0, \rx, c1, c0 + tst \rx, #1 @ MMU enabled? + ldreq \rx, =0x40090000 + ldrne \rx, =0xF4090000 + .endm + +#define UART_SHIFT 2 +#include <asm/hardware/debug-8250.S> diff --git a/arch/arm/mach-lpc32xx/include/mach/entry-macro.S b/arch/arm/mach-lpc32xx/include/mach/entry-macro.S new file mode 100644 index 00000000000..870227c9660 --- /dev/null +++ b/arch/arm/mach-lpc32xx/include/mach/entry-macro.S @@ -0,0 +1,47 @@ +/* + * arch/arm/mach-lpc32xx/include/mach/entry-macro.S + * + * Author: Kevin Wells <kevin.wells@nxp.com> + * + * Copyright (C) 2010 NXP Semiconductors + * + * 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. + */ + +#include <mach/hardware.h> +#include <mach/platform.h> + +#define LPC32XX_INTC_MASKED_STATUS_OFS 0x8 + + .macro disable_fiq + .endm + + .macro get_irqnr_preamble, base, tmp + ldr \base, =IO_ADDRESS(LPC32XX_MIC_BASE) + .endm + + .macro arch_ret_to_user, tmp1, tmp2 + .endm + +/* + * Return IRQ number in irqnr. Also return processor Z flag status in CPSR + * as set if an interrupt is pending. + */ + .macro get_irqnr_and_base, irqnr, irqstat, base, tmp + ldr \irqstat, [\base, #LPC32XX_INTC_MASKED_STATUS_OFS] + clz \irqnr, \irqstat + rsb \irqnr, \irqnr, #31 + teq \irqstat, #0 + .endm + + .macro irq_prio_table + .endm + diff --git a/arch/arm/mach-lpc32xx/include/mach/gpio.h b/arch/arm/mach-lpc32xx/include/mach/gpio.h new file mode 100644 index 00000000000..67d03da1eee --- /dev/null +++ b/arch/arm/mach-lpc32xx/include/mach/gpio.h @@ -0,0 +1,74 @@ +/* + * arch/arm/mach-lpc32xx/include/mach/gpio.h + * + * Author: Kevin Wells <kevin.wells@nxp.com> + * + * Copyright (C) 2010 NXP Semiconductors + * + * 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. + */ + +#ifndef __ASM_ARCH_GPIO_H +#define __ASM_ARCH_GPIO_H + +#include <asm-generic/gpio.h> + +/* + * Note! + * Muxed GP pins need to be setup to the GP state in the board level + * code prior to using this driver. + * GPI pins : 28xP3 group + * GPO pins : 24xP3 group + * GPIO pins: 8xP0 group, 24xP1 group, 13xP2 group, 6xP3 group + */ + +#define LPC32XX_GPIO_P0_MAX 8 +#define LPC32XX_GPIO_P1_MAX 24 +#define LPC32XX_GPIO_P2_MAX 13 +#define LPC32XX_GPIO_P3_MAX 6 +#define LPC32XX_GPI_P3_MAX 28 +#define LPC32XX_GPO_P3_MAX 24 + +#define LPC32XX_GPIO_P0_GRP 0 +#define LPC32XX_GPIO_P1_GRP (LPC32XX_GPIO_P0_GRP + LPC32XX_GPIO_P0_MAX) +#define LPC32XX_GPIO_P2_GRP (LPC32XX_GPIO_P1_GRP + LPC32XX_GPIO_P1_MAX) +#define LPC32XX_GPIO_P3_GRP (LPC32XX_GPIO_P2_GRP + LPC32XX_GPIO_P2_MAX) +#define LPC32XX_GPI_P3_GRP (LPC32XX_GPIO_P3_GRP + LPC32XX_GPIO_P3_MAX) +#define LPC32XX_GPO_P3_GRP (LPC32XX_GPI_P3_GRP + LPC32XX_GPI_P3_MAX) + +/* + * A specific GPIO can be selected with this macro + * ie, GPIO_05 can be selected with LPC32XX_GPIO(LPC32XX_GPIO_P3_GRP, 5) + * See the LPC32x0 User's guide for GPIO group numbers + */ +#define LPC32XX_GPIO(x, y) ((x) + (y)) + +static inline int gpio_get_value(unsigned gpio) +{ + return __gpio_get_value(gpio); +} + +static inline void gpio_set_value(unsigned gpio, int value) +{ + __gpio_set_value(gpio, value); +} + +static inline int gpio_cansleep(unsigned gpio) +{ + return __gpio_cansleep(gpio); +} + +static inline int gpio_to_irq(unsigned gpio) +{ + return __gpio_to_irq(gpio); +} + +#endif diff --git a/arch/arm/mach-lpc32xx/include/mach/hardware.h b/arch/arm/mach-lpc32xx/include/mach/hardware.h new file mode 100644 index 00000000000..33e1dde37bd --- /dev/null +++ b/arch/arm/mach-lpc32xx/include/mach/hardware.h @@ -0,0 +1,34 @@ +/* + * arch/arm/mach-lpc32xx/include/mach/hardware.h + * + * Copyright (c) 2005 MontaVista Software, Inc. <source@mvista.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; 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. + */ + +#ifndef __ASM_ARCH_HARDWARE_H +#define __ASM_ARCH_HARDWARE_H + +/* + * Start of virtual addresses for IO devices + */ +#define IO_BASE 0xF0000000 + +/* + * This macro relies on fact that for all HW i/o addresses bits 20-23 are 0 + */ +#define IO_ADDRESS(x) (((((x) & 0xff000000) >> 4) | ((x) & 0xfffff)) |\ + IO_BASE) + +#define io_p2v(x) ((void __iomem *) (unsigned long) IO_ADDRESS(x)) +#define io_v2p(x) ((((x) & 0x0ff00000) << 4) | ((x) & 0x000fffff)) + +#endif diff --git a/arch/arm/mach-lpc32xx/include/mach/i2c.h b/arch/arm/mach-lpc32xx/include/mach/i2c.h new file mode 100644 index 00000000000..034dc9286bc --- /dev/null +++ b/arch/arm/mach-lpc32xx/include/mach/i2c.h @@ -0,0 +1,63 @@ +/* + * PNX4008-specific tweaks for I2C IP3204 block + * + * Author: Vitaly Wool <vwool@ru.mvista.com> + * + * 2005 (c) MontaVista Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#ifndef __ASM_ARCH_I2C_H +#define __ASM_ARCH_I2C_H + +enum { + mstatus_tdi = 0x00000001, + mstatus_afi = 0x00000002, + mstatus_nai = 0x00000004, + mstatus_drmi = 0x00000008, + mstatus_active = 0x00000020, + mstatus_scl = 0x00000040, + mstatus_sda = 0x00000080, + mstatus_rff = 0x00000100, + mstatus_rfe = 0x00000200, + mstatus_tff = 0x00000400, + mstatus_tfe = 0x00000800, +}; + +enum { + mcntrl_tdie = 0x00000001, + mcntrl_afie = 0x00000002, + mcntrl_naie = 0x00000004, + mcntrl_drmie = 0x00000008, + mcntrl_daie = 0x00000020, + mcntrl_rffie = 0x00000040, + mcntrl_tffie = 0x00000080, + mcntrl_reset = 0x00000100, + mcntrl_cdbmode = 0x00000400, +}; + +enum { + rw_bit = 1 << 0, + start_bit = 1 << 8, + stop_bit = 1 << 9, +}; + +#define I2C_REG_RX(a) ((a)->ioaddr) /* Rx FIFO reg (RO) */ +#define I2C_REG_TX(a) ((a)->ioaddr) /* Tx FIFO reg (WO) */ +#define I2C_REG_STS(a) ((a)->ioaddr + 0x04) /* Status reg (RO) */ +#define I2C_REG_CTL(a) ((a)->ioaddr + 0x08) /* Ctl reg */ +#define I2C_REG_CKL(a) ((a)->ioaddr + 0x0c) /* Clock divider low */ +#define I2C_REG_CKH(a) ((a)->ioaddr + 0x10) /* Clock divider high */ +#define I2C_REG_ADR(a) ((a)->ioaddr + 0x14) /* I2C address */ +#define I2C_REG_RFL(a) ((a)->ioaddr + 0x18) /* Rx FIFO level (RO) */ +#define I2C_REG_TFL(a) ((a)->ioaddr + 0x1c) /* Tx FIFO level (RO) */ +#define I2C_REG_RXB(a) ((a)->ioaddr + 0x20) /* Num of bytes Rx-ed (RO) */ +#define I2C_REG_TXB(a) ((a)->ioaddr + 0x24) /* Num of bytes Tx-ed (RO) */ +#define I2C_REG_TXS(a) ((a)->ioaddr + 0x28) /* Tx slave FIFO (RO) */ +#define I2C_REG_STFL(a) ((a)->ioaddr + 0x2c) /* Tx slave FIFO level (RO) */ + +#define I2C_CHIP_NAME "PNX4008-I2C" + +#endif /* __ASM_ARCH_I2C_H */ diff --git a/arch/arm/mach-lpc32xx/include/mach/io.h b/arch/arm/mach-lpc32xx/include/mach/io.h new file mode 100644 index 00000000000..9b59ab5cef8 --- /dev/null +++ b/arch/arm/mach-lpc32xx/include/mach/io.h @@ -0,0 +1,27 @@ +/* + * arch/arm/mach-lpc32xx/include/mach/io.h + * + * Author: Kevin Wells <kevin.wells@nxp.com> + * + * Copyright (C) 2010 NXP Semiconductors + * + * 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. + */ + +#ifndef __ASM_ARM_ARCH_IO_H +#define __ASM_ARM_ARCH_IO_H + +#define IO_SPACE_LIMIT 0xffffffff + +#define __io(a) __typesafe_io(a) +#define __mem_pci(a) (a) + +#endif diff --git a/arch/arm/mach-lpc32xx/include/mach/irqs.h b/arch/arm/mach-lpc32xx/include/mach/irqs.h new file mode 100644 index 00000000000..2667f52e3b0 --- /dev/null +++ b/arch/arm/mach-lpc32xx/include/mach/irqs.h @@ -0,0 +1,117 @@ +/* + * arch/arm/mach-lpc32xx/include/mach/irqs.h + * + * Author: Kevin Wells <kevin.wells@nxp.com> + * + * Copyright (C) 2010 NXP Semiconductors + * + * 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. + */ + +#ifndef __ASM_ARM_ARCH_IRQS_H +#define __ASM_ARM_ARCH_IRQS_H + +#define LPC32XX_SIC1_IRQ(n) (32 + (n)) +#define LPC32XX_SIC2_IRQ(n) (64 + (n)) + +/* + * MIC interrupts + */ +#define IRQ_LPC32XX_SUB1IRQ 0 +#define IRQ_LPC32XX_SUB2IRQ 1 +#define IRQ_LPC32XX_PWM3 3 +#define IRQ_LPC32XX_PWM4 4 +#define IRQ_LPC32XX_HSTIMER 5 +#define IRQ_LPC32XX_WATCH 6 +#define IRQ_LPC32XX_UART_IIR3 7 +#define IRQ_LPC32XX_UART_IIR4 8 +#define IRQ_LPC32XX_UART_IIR5 9 +#define IRQ_LPC32XX_UART_IIR6 10 +#define IRQ_LPC32XX_FLASH 11 +#define IRQ_LPC32XX_SD1 13 +#define IRQ_LPC32XX_LCD 14 +#define IRQ_LPC32XX_SD0 15 +#define IRQ_LPC32XX_TIMER0 16 +#define IRQ_LPC32XX_TIMER1 17 +#define IRQ_LPC32XX_TIMER2 18 +#define IRQ_LPC32XX_TIMER3 19 +#define IRQ_LPC32XX_SSP0 20 +#define IRQ_LPC32XX_SSP1 21 +#define IRQ_LPC32XX_I2S0 22 +#define IRQ_LPC32XX_I2S1 23 +#define IRQ_LPC32XX_UART_IIR7 24 +#define IRQ_LPC32XX_UART_IIR2 25 +#define IRQ_LPC32XX_UART_IIR1 26 +#define IRQ_LPC32XX_MSTIMER 27 +#define IRQ_LPC32XX_DMA 28 +#define IRQ_LPC32XX_ETHERNET 29 +#define IRQ_LPC32XX_SUB1FIQ 30 +#define IRQ_LPC32XX_SUB2FIQ 31 + +/* + * SIC1 interrupts start at offset 32 + */ +#define IRQ_LPC32XX_JTAG_COMM_TX LPC32XX_SIC1_IRQ(1) +#define IRQ_LPC32XX_JTAG_COMM_RX LPC32XX_SIC1_IRQ(2) +#define IRQ_LPC32XX_GPI_11 LPC32XX_SIC1_IRQ(4) +#define IRQ_LPC32XX_TS_P LPC32XX_SIC1_IRQ(6) +#define IRQ_LPC32XX_TS_IRQ LPC32XX_SIC1_IRQ(7) +#define IRQ_LPC32XX_TS_AUX LPC32XX_SIC1_IRQ(8) +#define IRQ_LPC32XX_SPI2 LPC32XX_SIC1_IRQ(12) +#define IRQ_LPC32XX_PLLUSB LPC32XX_SIC1_IRQ(13) +#define IRQ_LPC32XX_PLLHCLK LPC32XX_SIC1_IRQ(14) +#define IRQ_LPC32XX_PLL397 LPC32XX_SIC1_IRQ(17) +#define IRQ_LPC32XX_I2C_2 LPC32XX_SIC1_IRQ(18) +#define IRQ_LPC32XX_I2C_1 LPC32XX_SIC1_IRQ(19) +#define IRQ_LPC32XX_RTC LPC32XX_SIC1_IRQ(20) +#define IRQ_LPC32XX_KEY LPC32XX_SIC1_IRQ(22) +#define IRQ_LPC32XX_SPI1 LPC32XX_SIC1_IRQ(23) +#define IRQ_LPC32XX_SW LPC32XX_SIC1_IRQ(24) +#define IRQ_LPC32XX_USB_OTG_TIMER LPC32XX_SIC1_IRQ(25) +#define IRQ_LPC32XX_USB_OTG_ATX LPC32XX_SIC1_IRQ(26) +#define IRQ_LPC32XX_USB_HOST LPC32XX_SIC1_IRQ(27) +#define IRQ_LPC32XX_USB_DEV_DMA LPC32XX_SIC1_IRQ(28) +#define IRQ_LPC32XX_USB_DEV_LP LPC32XX_SIC1_IRQ(29) +#define IRQ_LPC32XX_USB_DEV_HP LPC32XX_SIC1_IRQ(30) +#define IRQ_LPC32XX_USB_I2C LPC32XX_SIC1_IRQ(31) + +/* + * SIC2 interrupts start at offset 64 + */ +#define IRQ_LPC32XX_GPIO_00 LPC32XX_SIC2_IRQ(0) +#define IRQ_LPC32XX_GPIO_01 LPC32XX_SIC2_IRQ(1) +#define IRQ_LPC32XX_GPIO_02 LPC32XX_SIC2_IRQ(2) +#define IRQ_LPC32XX_GPIO_03 LPC32XX_SIC2_IRQ(3) +#define IRQ_LPC32XX_GPIO_04 LPC32XX_SIC2_IRQ(4) +#define IRQ_LPC32XX_GPIO_05 LPC32XX_SIC2_IRQ(5) +#define IRQ_LPC32XX_SPI2_DATAIN LPC32XX_SIC2_IRQ(6) +#define IRQ_LPC32XX_U2_HCTS LPC32XX_SIC2_IRQ(7) +#define IRQ_LPC32XX_P0_P1_IRQ LPC32XX_SIC2_IRQ(8) +#define IRQ_LPC32XX_GPI_08 LPC32XX_SIC2_IRQ(9) +#define IRQ_LPC32XX_GPI_09 LPC32XX_SIC2_IRQ(10) +#define IRQ_LPC32XX_GPI_19 LPC32XX_SIC2_IRQ(11) +#define IRQ_LPC32XX_U7_HCTS LPC32XX_SIC2_IRQ(12) +#define IRQ_LPC32XX_GPI_07 LPC32XX_SIC2_IRQ(15) +#define IRQ_LPC32XX_SDIO LPC32XX_SIC2_IRQ(18) +#define IRQ_LPC32XX_U5_RX LPC32XX_SIC2_IRQ(19) +#define IRQ_LPC32XX_SPI1_DATAIN LPC32XX_SIC2_IRQ(20) +#define IRQ_LPC32XX_GPI_00 LPC32XX_SIC2_IRQ(22) +#define IRQ_LPC32XX_GPI_01 LPC32XX_SIC2_IRQ(23) +#define IRQ_LPC32XX_GPI_02 LPC32XX_SIC2_IRQ(24) +#define IRQ_LPC32XX_GPI_03 LPC32XX_SIC2_IRQ(25) +#define IRQ_LPC32XX_GPI_04 LPC32XX_SIC2_IRQ(26) +#define IRQ_LPC32XX_GPI_05 LPC32XX_SIC2_IRQ(27) +#define IRQ_LPC32XX_GPI_06 LPC32XX_SIC2_IRQ(28) +#define IRQ_LPC32XX_SYSCLK LPC32XX_SIC2_IRQ(31) + +#define NR_IRQS 96 + +#endif diff --git a/arch/arm/mach-lpc32xx/include/mach/memory.h b/arch/arm/mach-lpc32xx/include/mach/memory.h new file mode 100644 index 00000000000..044e1acecbe --- /dev/null +++ b/arch/arm/mach-lpc32xx/include/mach/memory.h @@ -0,0 +1,27 @@ +/* + * arch/arm/mach-lpc32xx/include/mach/memory.h + * + * Author: Kevin Wells <kevin.wells@nxp.com> + * + * Copyright (C) 2010 NXP Semiconductors + * + * 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. + */ + +#ifndef __ASM_ARCH_MEMORY_H +#define __ASM_ARCH_MEMORY_H + +/* + * Physical DRAM offset of bank 0 + */ +#define PHYS_OFFSET UL(0x80000000) + +#endif diff --git a/arch/arm/mach-lpc32xx/include/mach/platform.h b/arch/arm/mach-lpc32xx/include/mach/platform.h new file mode 100644 index 00000000000..14ea8d1aadb --- /dev/null +++ b/arch/arm/mach-lpc32xx/include/mach/platform.h @@ -0,0 +1,694 @@ +/* + * arch/arm/mach-lpc32xx/include/mach/platform.h + * + * Author: Kevin Wells <kevin.wells@nxp.com> + * + * Copyright (C) 2010 NXP Semiconductors + * + * 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. + */ + +#ifndef __ASM_ARCH_PLATFORM_H +#define __ASM_ARCH_PLATFORM_H + +#define _SBF(f, v) ((v) << (f)) +#define _BIT(n) _SBF(n, 1) + +/* + * AHB 0 physical base addresses + */ +#define LPC32XX_SLC_BASE 0x20020000 +#define LPC32XX_SSP0_BASE 0x20084000 +#define LPC32XX_SPI1_BASE 0x20088000 +#define LPC32XX_SSP1_BASE 0x2008C000 +#define LPC32XX_SPI2_BASE 0x20090000 +#define LPC32XX_I2S0_BASE 0x20094000 +#define LPC32XX_SD_BASE 0x20098000 +#define LPC32XX_I2S1_BASE 0x2009C000 +#define LPC32XX_MLC_BASE 0x200A8000 +#define LPC32XX_AHB0_START LPC32XX_SLC_BASE +#define LPC32XX_AHB0_SIZE 0x00089000 + +/* + * AHB 1 physical base addresses + */ +#define LPC32XX_DMA_BASE 0x31000000 +#define LPC32XX_USB_BASE 0x31020000 +#define LPC32XX_USBH_BASE 0x31020000 +#define LPC32XX_USB_OTG_BASE 0x31020000 +#define LPC32XX_OTG_I2C_BASE 0x31020300 +#define LPC32XX_LCD_BASE 0x31040000 +#define LPC32XX_ETHERNET_BASE 0x31060000 +#define LPC32XX_EMC_BASE 0x31080000 +#define LPC32XX_ETB_CFG_BASE 0x310C0000 +#define LPC32XX_ETB_DATA_BASE 0x310E0000 +#define LPC32XX_AHB1_START LPC32XX_DMA_BASE +#define LPC32XX_AHB1_SIZE 0x000E1000 + +/* + * FAB physical base addresses + */ +#define LPC32XX_CLK_PM_BASE 0x40004000 +#define LPC32XX_MIC_BASE 0x40008000 +#define LPC32XX_SIC1_BASE 0x4000C000 +#define LPC32XX_SIC2_BASE 0x40010000 +#define LPC32XX_HS_UART1_BASE 0x40014000 +#define LPC32XX_HS_UART2_BASE 0x40018000 +#define LPC32XX_HS_UART7_BASE 0x4001C000 +#define LPC32XX_RTC_BASE 0x40024000 +#define LPC32XX_RTC_RAM_BASE 0x40024080 +#define LPC32XX_GPIO_BASE 0x40028000 +#define LPC32XX_PWM3_BASE 0x4002C000 +#define LPC32XX_PWM4_BASE 0x40030000 +#define LPC32XX_MSTIM_BASE 0x40034000 +#define LPC32XX_HSTIM_BASE 0x40038000 +#define LPC32XX_WDTIM_BASE 0x4003C000 +#define LPC32XX_DEBUG_CTRL_BASE 0x40040000 +#define LPC32XX_TIMER0_BASE 0x40044000 +#define LPC32XX_ADC_BASE 0x40048000 +#define LPC32XX_TIMER1_BASE 0x4004C000 +#define LPC32XX_KSCAN_BASE 0x40050000 +#define LPC32XX_UART_CTRL_BASE 0x40054000 +#define LPC32XX_TIMER2_BASE 0x40058000 +#define LPC32XX_PWM1_BASE 0x4005C000 +#define LPC32XX_PWM2_BASE 0x4005C004 +#define LPC32XX_TIMER3_BASE 0x40060000 + +/* + * APB physical base addresses + */ +#define LPC32XX_UART3_BASE 0x40080000 +#define LPC32XX_UART4_BASE 0x40088000 +#define LPC32XX_UART5_BASE 0x40090000 +#define LPC32XX_UART6_BASE 0x40098000 +#define LPC32XX_I2C1_BASE 0x400A0000 +#define LPC32XX_I2C2_BASE 0x400A8000 + +/* + * FAB and APB base and sizing + */ +#define LPC32XX_FABAPB_START LPC32XX_CLK_PM_BASE +#define LPC32XX_FABAPB_SIZE 0x000A5000 + +/* + * Internal memory bases and sizes + */ +#define LPC32XX_IRAM_BASE 0x08000000 +#define LPC32XX_IROM_BASE 0x0C000000 + +/* + * External Static Memory Bank Address Space Bases + */ +#define LPC32XX_EMC_CS0_BASE 0xE0000000 +#define LPC32XX_EMC_CS1_BASE 0xE1000000 +#define LPC32XX_EMC_CS2_BASE 0xE2000000 +#define LPC32XX_EMC_CS3_BASE 0xE3000000 + +/* + * External SDRAM Memory Bank Address Space Bases + */ +#define LPC32XX_EMC_DYCS0_BASE 0x80000000 +#define LPC32XX_EMC_DYCS1_BASE 0xA0000000 + +/* + * Clock and crystal information + */ +#define LPC32XX_MAIN_OSC_FREQ 13000000 +#define LPC32XX_CLOCK_OSC_FREQ 32768 + +/* + * Clock and Power control register offsets + */ +#define _PMREG(x) io_p2v(LPC32XX_CLK_PM_BASE +\ + (x)) +#define LPC32XX_CLKPWR_DEBUG_CTRL _PMREG(0x000) +#define LPC32XX_CLKPWR_BOOTMAP _PMREG(0x014) +#define LPC32XX_CLKPWR_P01_ER _PMREG(0x018) +#define LPC32XX_CLKPWR_USBCLK_PDIV _PMREG(0x01C) +#define LPC32XX_CLKPWR_INT_ER _PMREG(0x020) +#define LPC32XX_CLKPWR_INT_RS _PMREG(0x024) +#define LPC32XX_CLKPWR_INT_SR _PMREG(0x028) +#define LPC32XX_CLKPWR_INT_AP _PMREG(0x02C) +#define LPC32XX_CLKPWR_PIN_ER _PMREG(0x030) +#define LPC32XX_CLKPWR_PIN_RS _PMREG(0x034) +#define LPC32XX_CLKPWR_PIN_SR _PMREG(0x038) +#define LPC32XX_CLKPWR_PIN_AP _PMREG(0x03C) +#define LPC32XX_CLKPWR_HCLK_DIV _PMREG(0x040) +#define LPC32XX_CLKPWR_PWR_CTRL _PMREG(0x044) +#define LPC32XX_CLKPWR_PLL397_CTRL _PMREG(0x048) +#define LPC32XX_CLKPWR_MAIN_OSC_CTRL _PMREG(0x04C) +#define LPC32XX_CLKPWR_SYSCLK_CTRL _PMREG(0x050) +#define LPC32XX_CLKPWR_LCDCLK_CTRL _PMREG(0x054) +#define LPC32XX_CLKPWR_HCLKPLL_CTRL _PMREG(0x058) +#define LPC32XX_CLKPWR_ADC_CLK_CTRL_1 _PMREG(0x060) +#define LPC32XX_CLKPWR_USB_CTRL _PMREG(0x064) +#define LPC32XX_CLKPWR_SDRAMCLK_CTRL _PMREG(0x068) +#define LPC32XX_CLKPWR_DDR_LAP_NOM _PMREG(0x06C) +#define LPC32XX_CLKPWR_DDR_LAP_COUNT _PMREG(0x070) +#define LPC32XX_CLKPWR_DDR_LAP_DELAY _PMREG(0x074) +#define LPC32XX_CLKPWR_SSP_CLK_CTRL _PMREG(0x078) +#define LPC32XX_CLKPWR_I2S_CLK_CTRL _PMREG(0x07C) +#define LPC32XX_CLKPWR_MS_CTRL _PMREG(0x080) +#define LPC32XX_CLKPWR_MACCLK_CTRL _PMREG(0x090) +#define LPC32XX_CLKPWR_TEST_CLK_SEL _PMREG(0x0A4) +#define LPC32XX_CLKPWR_SFW_INT _PMREG(0x0A8) +#define LPC32XX_CLKPWR_I2C_CLK_CTRL _PMREG(0x0AC) +#define LPC32XX_CLKPWR_KEY_CLK_CTRL _PMREG(0x0B0) +#define LPC32XX_CLKPWR_ADC_CLK_CTRL _PMREG(0x0B4) +#define LPC32XX_CLKPWR_PWM_CLK_CTRL _PMREG(0x0B8) +#define LPC32XX_CLKPWR_TIMER_CLK_CTRL _PMREG(0x0BC) +#define LPC32XX_CLKPWR_TIMERS_PWMS_CLK_CTRL_1 _PMREG(0x0C0) +#define LPC32XX_CLKPWR_SPI_CLK_CTRL _PMREG(0x0C4) +#define LPC32XX_CLKPWR_NAND_CLK_CTRL _PMREG(0x0C8) +#define LPC32XX_CLKPWR_UART3_CLK_CTRL _PMREG(0x0D0) +#define LPC32XX_CLKPWR_UART4_CLK_CTRL _PMREG(0x0D4) +#define LPC32XX_CLKPWR_UART5_CLK_CTRL _PMREG(0x0D8) +#define LPC32XX_CLKPWR_UART6_CLK_CTRL _PMREG(0x0DC) +#define LPC32XX_CLKPWR_IRDA_CLK_CTRL _PMREG(0x0E0) +#define LPC32XX_CLKPWR_UART_CLK_CTRL _PMREG(0x0E4) +#define LPC32XX_CLKPWR_DMA_CLK_CTRL _PMREG(0x0E8) +#define LPC32XX_CLKPWR_AUTOCLOCK _PMREG(0x0EC) +#define LPC32XX_CLKPWR_DEVID(x) _PMREG(0x130 + (x)) + +/* + * clkpwr_debug_ctrl register definitions +*/ +#define LPC32XX_CLKPWR_VFP_CLOCK_ENABLE_BIT _BIT(4) + +/* + * clkpwr_bootmap register definitions + */ +#define LPC32XX_CLKPWR_BOOTMAP_SEL_BIT _BIT(1) + +/* + * clkpwr_start_gpio register bit definitions + */ +#define LPC32XX_CLKPWR_GPIOSRC_P1IO23_BIT _BIT(31) +#define LPC32XX_CLKPWR_GPIOSRC_P1IO22_BIT _BIT(30) +#define LPC32XX_CLKPWR_GPIOSRC_P1IO21_BIT _BIT(29) +#define LPC32XX_CLKPWR_GPIOSRC_P1IO20_BIT _BIT(28) +#define LPC32XX_CLKPWR_GPIOSRC_P1IO19_BIT _BIT(27) +#define LPC32XX_CLKPWR_GPIOSRC_P1IO18_BIT _BIT(26) +#define LPC32XX_CLKPWR_GPIOSRC_P1IO17_BIT _BIT(25) +#define LPC32XX_CLKPWR_GPIOSRC_P1IO16_BIT _BIT(24) +#define LPC32XX_CLKPWR_GPIOSRC_P1IO15_BIT _BIT(23) +#define LPC32XX_CLKPWR_GPIOSRC_P1IO14_BIT _BIT(22) +#define LPC32XX_CLKPWR_GPIOSRC_P1IO13_BIT _BIT(21) +#define LPC32XX_CLKPWR_GPIOSRC_P1IO12_BIT _BIT(20) +#define LPC32XX_CLKPWR_GPIOSRC_P1IO11_BIT _BIT(19) +#define LPC32XX_CLKPWR_GPIOSRC_P1IO10_BIT _BIT(18) +#define LPC32XX_CLKPWR_GPIOSRC_P1IO9_BIT _BIT(17) +#define LPC32XX_CLKPWR_GPIOSRC_P1IO8_BIT _BIT(16) +#define LPC32XX_CLKPWR_GPIOSRC_P1IO7_BIT _BIT(15) +#define LPC32XX_CLKPWR_GPIOSRC_P1IO6_BIT _BIT(14) +#define LPC32XX_CLKPWR_GPIOSRC_P1IO5_BIT _BIT(13) +#define LPC32XX_CLKPWR_GPIOSRC_P1IO4_BIT _BIT(12) +#define LPC32XX_CLKPWR_GPIOSRC_P1IO3_BIT _BIT(11) +#define LPC32XX_CLKPWR_GPIOSRC_P1IO2_BIT _BIT(10) +#define LPC32XX_CLKPWR_GPIOSRC_P1IO1_BIT _BIT(9) +#define LPC32XX_CLKPWR_GPIOSRC_P1IO0_BIT _BIT(8) +#define LPC32XX_CLKPWR_GPIOSRC_P0IO7_BIT _BIT(7) +#define LPC32XX_CLKPWR_GPIOSRC_P0IO6_BIT _BIT(6) +#define LPC32XX_CLKPWR_GPIOSRC_P0IO5_BIT _BIT(5) +#define LPC32XX_CLKPWR_GPIOSRC_P0IO4_BIT _BIT(4) +#define LPC32XX_CLKPWR_GPIOSRC_P0IO3_BIT _BIT(3) +#define LPC32XX_CLKPWR_GPIOSRC_P0IO2_BIT _BIT(2) +#define LPC32XX_CLKPWR_GPIOSRC_P0IO1_BIT _BIT(1) +#define LPC32XX_CLKPWR_GPIOSRC_P0IO0_BIT _BIT(0) + +/* + * clkpwr_usbclk_pdiv register definitions + */ +#define LPC32XX_CLKPWR_USBPDIV_PLL_MASK 0xF + +/* + * clkpwr_start_int, clkpwr_start_raw_sts_int, clkpwr_start_sts_int, + * clkpwr_start_pol_int, register bit definitions + */ +#define LPC32XX_CLKPWR_INTSRC_ADC_BIT _BIT(31) +#define LPC32XX_CLKPWR_INTSRC_TS_P_BIT _BIT(30) +#define LPC32XX_CLKPWR_INTSRC_TS_AUX_BIT _BIT(29) +#define LPC32XX_CLKPWR_INTSRC_USBAHNEEDCLK_BIT _BIT(26) +#define LPC32XX_CLKPWR_INTSRC_MSTIMER_BIT _BIT(25) +#define LPC32XX_CLKPWR_INTSRC_RTC_BIT _BIT(24) +#define LPC32XX_CLKPWR_INTSRC_USBNEEDCLK_BIT _BIT(23) +#define LPC32XX_CLKPWR_INTSRC_USB_BIT _BIT(22) +#define LPC32XX_CLKPWR_INTSRC_I2C_BIT _BIT(21) +#define LPC32XX_CLKPWR_INTSRC_USBOTGTIMER_BIT _BIT(20) +#define LPC32XX_CLKPWR_INTSRC_USBATXINT_BIT _BIT(19) +#define LPC32XX_CLKPWR_INTSRC_KEY_BIT _BIT(16) +#define LPC32XX_CLKPWR_INTSRC_MAC_BIT _BIT(7) +#define LPC32XX_CLKPWR_INTSRC_P0P1_BIT _BIT(6) +#define LPC32XX_CLKPWR_INTSRC_GPIO_05_BIT _BIT(5) +#define LPC32XX_CLKPWR_INTSRC_GPIO_04_BIT _BIT(4) +#define LPC32XX_CLKPWR_INTSRC_GPIO_03_BIT _BIT(3) +#define LPC32XX_CLKPWR_INTSRC_GPIO_02_BIT _BIT(2) +#define LPC32XX_CLKPWR_INTSRC_GPIO_01_BIT _BIT(1) +#define LPC32XX_CLKPWR_INTSRC_GPIO_00_BIT _BIT(0) + +/* + * clkpwr_start_pin, clkpwr_start_raw_sts_pin, clkpwr_start_sts_pin, + * clkpwr_start_pol_pin register bit definitions + */ +#define LPC32XX_CLKPWR_EXTSRC_U7_RX_BIT _BIT(31) +#define LPC32XX_CLKPWR_EXTSRC_U7_HCTS_BIT _BIT(30) +#define LPC32XX_CLKPWR_EXTSRC_U6_IRRX_BIT _BIT(28) +#define LPC32XX_CLKPWR_EXTSRC_U5_RX_BIT _BIT(26) +#define LPC32XX_CLKPWR_EXTSRC_GPI_28_BIT _BIT(25) +#define LPC32XX_CLKPWR_EXTSRC_U3_RX_BIT _BIT(24) +#define LPC32XX_CLKPWR_EXTSRC_U2_HCTS_BIT _BIT(23) +#define LPC32XX_CLKPWR_EXTSRC_U2_RX_BIT _BIT(22) +#define LPC32XX_CLKPWR_EXTSRC_U1_RX_BIT _BIT(21) +#define LPC32XX_CLKPWR_EXTSRC_MSDIO_INT_BIT _BIT(18) +#define LPC32XX_CLKPWR_EXTSRC_MSDIO_SRT_BIT _BIT(17) +#define LPC32XX_CLKPWR_EXTSRC_GPI_06_BIT _BIT(16) +#define LPC32XX_CLKPWR_EXTSRC_GPI_05_BIT _BIT(15) +#define LPC32XX_CLKPWR_EXTSRC_GPI_04_BIT _BIT(14) +#define LPC32XX_CLKPWR_EXTSRC_GPI_03_BIT _BIT(13) +#define LPC32XX_CLKPWR_EXTSRC_GPI_02_BIT _BIT(12) +#define LPC32XX_CLKPWR_EXTSRC_GPI_01_BIT _BIT(11) +#define LPC32XX_CLKPWR_EXTSRC_GPI_00_BIT _BIT(10) +#define LPC32XX_CLKPWR_EXTSRC_SYSCLKEN_BIT _BIT(9) +#define LPC32XX_CLKPWR_EXTSRC_SPI1_DATIN_BIT _BIT(8) +#define LPC32XX_CLKPWR_EXTSRC_GPI_07_BIT _BIT(7) +#define LPC32XX_CLKPWR_EXTSRC_SPI2_DATIN_BIT _BIT(6) +#define LPC32XX_CLKPWR_EXTSRC_GPI_19_BIT _BIT(5) +#define LPC32XX_CLKPWR_EXTSRC_GPI_09_BIT _BIT(4) +#define LPC32XX_CLKPWR_EXTSRC_GPI_08_BIT _BIT(3) + +/* + * clkpwr_hclk_div register definitions + */ +#define LPC32XX_CLKPWR_HCLKDIV_DDRCLK_STOP (0x0 << 7) +#define LPC32XX_CLKPWR_HCLKDIV_DDRCLK_NORM (0x1 << 7) +#define LPC32XX_CLKPWR_HCLKDIV_DDRCLK_HALF (0x2 << 7) +#define LPC32XX_CLKPWR_HCLKDIV_PCLK_DIV(n) (((n) & 0x1F) << 2) +#define LPC32XX_CLKPWR_HCLKDIV_DIV_2POW(n) ((n) & 0x3) + +/* + * clkpwr_pwr_ctrl register definitions + */ +#define LPC32XX_CLKPWR_CTRL_FORCE_PCLK _BIT(10) +#define LPC32XX_CLKPWR_SDRAM_SELF_RFSH _BIT(9) +#define LPC32XX_CLKPWR_UPD_SDRAM_SELF_RFSH _BIT(8) +#define LPC32XX_CLKPWR_AUTO_SDRAM_SELF_RFSH _BIT(7) +#define LPC32XX_CLKPWR_HIGHCORE_STATE_BIT _BIT(5) +#define LPC32XX_CLKPWR_SYSCLKEN_STATE_BIT _BIT(4) +#define LPC32XX_CLKPWR_SYSCLKEN_GPIO_EN _BIT(3) +#define LPC32XX_CLKPWR_SELECT_RUN_MODE _BIT(2) +#define LPC32XX_CLKPWR_HIGHCORE_GPIO_EN _BIT(1) +#define LPC32XX_CLKPWR_STOP_MODE_CTRL _BIT(0) + +/* + * clkpwr_pll397_ctrl register definitions + */ +#define LPC32XX_CLKPWR_PLL397_MSLOCK_STS _BIT(10) +#define LPC32XX_CLKPWR_PLL397_BYPASS _BIT(9) +#define LPC32XX_CLKPWR_PLL397_BIAS_NORM 0x000 +#define LPC32XX_CLKPWR_PLL397_BIAS_N12_5 0x040 +#define LPC32XX_CLKPWR_PLL397_BIAS_N25 0x080 +#define LPC32XX_CLKPWR_PLL397_BIAS_N37_5 0x0C0 +#define LPC32XX_CLKPWR_PLL397_BIAS_P12_5 0x100 +#define LPC32XX_CLKPWR_PLL397_BIAS_P25 0x140 +#define LPC32XX_CLKPWR_PLL397_BIAS_P37_5 0x180 +#define LPC32XX_CLKPWR_PLL397_BIAS_P50 0x1C0 +#define LPC32XX_CLKPWR_PLL397_BIAS_MASK 0x1C0 +#define LPC32XX_CLKPWR_SYSCTRL_PLL397_DIS _BIT(1) +#define LPC32XX_CLKPWR_SYSCTRL_PLL397_STS _BIT(0) + +/* + * clkpwr_main_osc_ctrl register definitions + */ +#define LPC32XX_CLKPWR_MOSC_ADD_CAP(n) (((n) & 0x7F) << 2) +#define LPC32XX_CLKPWR_MOSC_CAP_MASK (0x7F << 2) +#define LPC32XX_CLKPWR_TEST_MODE _BIT(1) +#define LPC32XX_CLKPWR_MOSC_DISABLE _BIT(0) + +/* + * clkpwr_sysclk_ctrl register definitions + */ +#define LPC32XX_CLKPWR_SYSCTRL_BP_TRIG(n) (((n) & 0x3FF) << 2) +#define LPC32XX_CLKPWR_SYSCTRL_BP_MASK (0x3FF << 2) +#define LPC32XX_CLKPWR_SYSCTRL_USEPLL397 _BIT(1) +#define LPC32XX_CLKPWR_SYSCTRL_SYSCLKMUX _BIT(0) + +/* + * clkpwr_lcdclk_ctrl register definitions + */ +#define LPC32XX_CLKPWR_LCDCTRL_LCDTYPE_TFT12 0x000 +#define LPC32XX_CLKPWR_LCDCTRL_LCDTYPE_TFT16 0x040 +#define LPC32XX_CLKPWR_LCDCTRL_LCDTYPE_TFT15 0x080 +#define LPC32XX_CLKPWR_LCDCTRL_LCDTYPE_TFT24 0x0C0 +#define LPC32XX_CLKPWR_LCDCTRL_LCDTYPE_STN4M 0x100 +#define LPC32XX_CLKPWR_LCDCTRL_LCDTYPE_STN8C 0x140 +#define LPC32XX_CLKPWR_LCDCTRL_LCDTYPE_DSTN4M 0x180 +#define LPC32XX_CLKPWR_LCDCTRL_LCDTYPE_DSTN8C 0x1C0 +#define LPC32XX_CLKPWR_LCDCTRL_LCDTYPE_MSK 0x01C0 +#define LPC32XX_CLKPWR_LCDCTRL_CLK_EN 0x020 +#define LPC32XX_CLKPWR_LCDCTRL_SET_PSCALE(n) ((n - 1) & 0x1F) +#define LPC32XX_CLKPWR_LCDCTRL_PSCALE_MSK 0x001F + +/* + * clkpwr_hclkpll_ctrl register definitions + */ +#define LPC32XX_CLKPWR_HCLKPLL_POWER_UP _BIT(16) +#define LPC32XX_CLKPWR_HCLKPLL_CCO_BYPASS _BIT(15) +#define LPC32XX_CLKPWR_HCLKPLL_POSTDIV_BYPASS _BIT(14) +#define LPC32XX_CLKPWR_HCLKPLL_FDBK_SEL_FCLK _BIT(13) +#define LPC32XX_CLKPWR_HCLKPLL_POSTDIV_2POW(n) (((n) & 0x3) << 11) +#define LPC32XX_CLKPWR_HCLKPLL_PREDIV_PLUS1(n) (((n) & 0x3) << 9) +#define LPC32XX_CLKPWR_HCLKPLL_PLLM(n) (((n) & 0xFF) << 1) +#define LPC32XX_CLKPWR_HCLKPLL_PLL_STS _BIT(0) + +/* + * clkpwr_adc_clk_ctrl_1 register definitions + */ +#define LPC32XX_CLKPWR_ADCCTRL1_RTDIV(n) (((n) & 0xFF) << 0) +#define LPC32XX_CLKPWR_ADCCTRL1_PCLK_SEL _BIT(8) + +/* + * clkpwr_usb_ctrl register definitions + */ +#define LPC32XX_CLKPWR_USBCTRL_HCLK_EN _BIT(24) +#define LPC32XX_CLKPWR_USBCTRL_USBI2C_EN _BIT(23) +#define LPC32XX_CLKPWR_USBCTRL_USBDVND_EN _BIT(22) +#define LPC32XX_CLKPWR_USBCTRL_USBHSTND_EN _BIT(21) +#define LPC32XX_CLKPWR_USBCTRL_PU_ADD (0x0 << 19) +#define LPC32XX_CLKPWR_USBCTRL_BUS_KEEPER (0x1 << 19) +#define LPC32XX_CLKPWR_USBCTRL_PD_ADD (0x3 << 19) +#define LPC32XX_CLKPWR_USBCTRL_CLK_EN2 _BIT(18) +#define LPC32XX_CLKPWR_USBCTRL_CLK_EN1 _BIT(17) +#define LPC32XX_CLKPWR_USBCTRL_PLL_PWRUP _BIT(16) +#define LPC32XX_CLKPWR_USBCTRL_CCO_BYPASS _BIT(15) +#define LPC32XX_CLKPWR_USBCTRL_POSTDIV_BYPASS _BIT(14) +#define LPC32XX_CLKPWR_USBCTRL_FDBK_SEL_FCLK _BIT(13) +#define LPC32XX_CLKPWR_USBCTRL_POSTDIV_2POW(n) (((n) & 0x3) << 11) +#define LPC32XX_CLKPWR_USBCTRL_PREDIV_PLUS1(n) (((n) & 0x3) << 9) +#define LPC32XX_CLKPWR_USBCTRL_FDBK_PLUS1(n) (((n) & 0xFF) << 1) +#define LPC32XX_CLKPWR_USBCTRL_PLL_STS _BIT(0) + +/* + * clkpwr_sdramclk_ctrl register definitions + */ +#define LPC32XX_CLKPWR_SDRCLK_FASTSLEW_CLK _BIT(22) +#define LPC32XX_CLKPWR_SDRCLK_FASTSLEW _BIT(21) +#define LPC32XX_CLKPWR_SDRCLK_FASTSLEW_DAT _BIT(20) +#define LPC32XX_CLKPWR_SDRCLK_SW_DDR_RESET _BIT(19) +#define LPC32XX_CLKPWR_SDRCLK_HCLK_DLY(n) (((n) & 0x1F) << 14) +#define LPC32XX_CLKPWR_SDRCLK_DLY_ADDR_STS _BIT(13) +#define LPC32XX_CLKPWR_SDRCLK_SENS_FACT(n) (((n) & 0x7) << 10) +#define LPC32XX_CLKPWR_SDRCLK_USE_CAL _BIT(9) +#define LPC32XX_CLKPWR_SDRCLK_DO_CAL _BIT(8) +#define LPC32XX_CLKPWR_SDRCLK_CAL_ON_RTC _BIT(7) +#define LPC32XX_CLKPWR_SDRCLK_DQS_DLY(n) (((n) & 0x1F) << 2) +#define LPC32XX_CLKPWR_SDRCLK_USE_DDR _BIT(1) +#define LPC32XX_CLKPWR_SDRCLK_CLK_DIS _BIT(0) + +/* + * clkpwr_ssp_blk_ctrl register definitions + */ +#define LPC32XX_CLKPWR_SSPCTRL_DMA_SSP1RX _BIT(5) +#define LPC32XX_CLKPWR_SSPCTRL_DMA_SSP1TX _BIT(4) +#define LPC32XX_CLKPWR_SSPCTRL_DMA_SSP0RX _BIT(3) +#define LPC32XX_CLKPWR_SSPCTRL_DMA_SSP0TX _BIT(2) +#define LPC32XX_CLKPWR_SSPCTRL_SSPCLK1_EN _BIT(1) +#define LPC32XX_CLKPWR_SSPCTRL_SSPCLK0_EN _BIT(0) + +/* + * clkpwr_i2s_clk_ctrl register definitions + */ +#define LPC32XX_CLKPWR_I2SCTRL_I2S1_RX_FOR_TX _BIT(6) +#define LPC32XX_CLKPWR_I2SCTRL_I2S1_TX_FOR_RX _BIT(5) +#define LPC32XX_CLKPWR_I2SCTRL_I2S1_USE_DMA _BIT(4) +#define LPC32XX_CLKPWR_I2SCTRL_I2S0_RX_FOR_TX _BIT(3) +#define LPC32XX_CLKPWR_I2SCTRL_I2S0_TX_FOR_RX _BIT(2) +#define LPC32XX_CLKPWR_I2SCTRL_I2SCLK1_EN _BIT(1) +#define LPC32XX_CLKPWR_I2SCTRL_I2SCLK0_EN _BIT(0) + +/* + * clkpwr_ms_ctrl register definitions + */ +#define LPC32XX_CLKPWR_MSCARD_MSDIO_PIN_DIS _BIT(10) +#define LPC32XX_CLKPWR_MSCARD_MSDIO_PU_EN _BIT(9) +#define LPC32XX_CLKPWR_MSCARD_MSDIO23_DIS _BIT(8) +#define LPC32XX_CLKPWR_MSCARD_MSDIO1_DIS _BIT(7) +#define LPC32XX_CLKPWR_MSCARD_MSDIO0_DIS _BIT(6) +#define LPC32XX_CLKPWR_MSCARD_SDCARD_EN _BIT(5) +#define LPC32XX_CLKPWR_MSCARD_SDCARD_DIV(n) ((n) & 0xF) + +/* + * clkpwr_macclk_ctrl register definitions + */ +#define LPC32XX_CLKPWR_MACCTRL_NO_ENET_PIS 0x00 +#define LPC32XX_CLKPWR_MACCTRL_USE_MII_PINS 0x08 +#define LPC32XX_CLKPWR_MACCTRL_USE_RMII_PINS 0x18 +#define LPC32XX_CLKPWR_MACCTRL_PINS_MSK 0x18 +#define LPC32XX_CLKPWR_MACCTRL_DMACLK_EN _BIT(2) +#define LPC32XX_CLKPWR_MACCTRL_MMIOCLK_EN _BIT(1) +#define LPC32XX_CLKPWR_MACCTRL_HRCCLK_EN _BIT(0) + +/* + * clkpwr_test_clk_sel register definitions + */ +#define LPC32XX_CLKPWR_TESTCLK1_SEL_PERCLK (0x0 << 5) +#define LPC32XX_CLKPWR_TESTCLK1_SEL_RTC (0x1 << 5) +#define LPC32XX_CLKPWR_TESTCLK1_SEL_MOSC (0x2 << 5) +#define LPC32XX_CLKPWR_TESTCLK1_SEL_MASK (0x3 << 5) +#define LPC32XX_CLKPWR_TESTCLK_TESTCLK1_EN _BIT(4) +#define LPC32XX_CLKPWR_TESTCLK2_SEL_HCLK (0x0 << 1) +#define LPC32XX_CLKPWR_TESTCLK2_SEL_PERCLK (0x1 << 1) +#define LPC32XX_CLKPWR_TESTCLK2_SEL_USBCLK (0x2 << 1) +#define LPC32XX_CLKPWR_TESTCLK2_SEL_MOSC (0x5 << 1) +#define LPC32XX_CLKPWR_TESTCLK2_SEL_PLL397 (0x7 << 1) +#define LPC32XX_CLKPWR_TESTCLK2_SEL_MASK (0x7 << 1) +#define LPC32XX_CLKPWR_TESTCLK_TESTCLK2_EN _BIT(0) + +/* + * clkpwr_sw_int register definitions + */ +#define LPC32XX_CLKPWR_SW_INT(n) (_BIT(0) | (((n) & 0x7F) << 1)) +#define LPC32XX_CLKPWR_SW_GET_ARG(n) (((n) & 0xFE) >> 1) + +/* + * clkpwr_i2c_clk_ctrl register definitions + */ +#define LPC32XX_CLKPWR_I2CCLK_USBI2CHI_DRIVE _BIT(4) +#define LPC32XX_CLKPWR_I2CCLK_I2C2HI_DRIVE _BIT(3) +#define LPC32XX_CLKPWR_I2CCLK_I2C1HI_DRIVE _BIT(2) +#define LPC32XX_CLKPWR_I2CCLK_I2C2CLK_EN _BIT(1) +#define LPC32XX_CLKPWR_I2CCLK_I2C1CLK_EN _BIT(0) + +/* + * clkpwr_key_clk_ctrl register definitions + */ +#define LPC32XX_CLKPWR_KEYCLKCTRL_CLK_EN 0x1 + +/* + * clkpwr_adc_clk_ctrl register definitions + */ +#define LPC32XX_CLKPWR_ADC32CLKCTRL_CLK_EN 0x1 + +/* + * clkpwr_pwm_clk_ctrl register definitions + */ +#define LPC32XX_CLKPWR_PWMCLK_PWM2_DIV(n) (((n) & 0xF) << 8) +#define LPC32XX_CLKPWR_PWMCLK_PWM1_DIV(n) (((n) & 0xF) << 4) +#define LPC32XX_CLKPWR_PWMCLK_PWM2SEL_PCLK 0x8 +#define LPC32XX_CLKPWR_PWMCLK_PWM2CLK_EN 0x4 +#define LPC32XX_CLKPWR_PWMCLK_PWM1SEL_PCLK 0x2 +#define LPC32XX_CLKPWR_PWMCLK_PWM1CLK_EN 0x1 + +/* + * clkpwr_timer_clk_ctrl register definitions + */ +#define LPC32XX_CLKPWR_PWMCLK_HSTIMER_EN 0x2 +#define LPC32XX_CLKPWR_PWMCLK_WDOG_EN 0x1 + +/* + * clkpwr_timers_pwms_clk_ctrl_1 register definitions + */ +#define LPC32XX_CLKPWR_TMRPWMCLK_TIMER3_EN 0x20 +#define LPC32XX_CLKPWR_TMRPWMCLK_TIMER2_EN 0x10 +#define LPC32XX_CLKPWR_TMRPWMCLK_TIMER1_EN 0x08 +#define LPC32XX_CLKPWR_TMRPWMCLK_TIMER0_EN 0x04 +#define LPC32XX_CLKPWR_TMRPWMCLK_PWM4_EN 0x02 +#define LPC32XX_CLKPWR_TMRPWMCLK_PWM3_EN 0x01 + +/* + * clkpwr_spi_clk_ctrl register definitions + */ +#define LPC32XX_CLKPWR_SPICLK_SET_SPI2DATIO 0x80 +#define LPC32XX_CLKPWR_SPICLK_SET_SPI2CLK 0x40 +#define LPC32XX_CLKPWR_SPICLK_USE_SPI2 0x20 +#define LPC32XX_CLKPWR_SPICLK_SPI2CLK_EN 0x10 +#define LPC32XX_CLKPWR_SPICLK_SET_SPI1DATIO 0x08 +#define LPC32XX_CLKPWR_SPICLK_SET_SPI1CLK 0x04 +#define LPC32XX_CLKPWR_SPICLK_USE_SPI1 0x02 +#define LPC32XX_CLKPWR_SPICLK_SPI1CLK_EN 0x01 + +/* + * clkpwr_nand_clk_ctrl register definitions + */ +#define LPC32XX_CLKPWR_NANDCLK_INTSEL_MLC 0x20 +#define LPC32XX_CLKPWR_NANDCLK_DMA_RNB 0x10 +#define LPC32XX_CLKPWR_NANDCLK_DMA_INT 0x08 +#define LPC32XX_CLKPWR_NANDCLK_SEL_SLC 0x04 +#define LPC32XX_CLKPWR_NANDCLK_MLCCLK_EN 0x02 +#define LPC32XX_CLKPWR_NANDCLK_SLCCLK_EN 0x01 + +/* + * clkpwr_uart3_clk_ctrl, clkpwr_uart4_clk_ctrl, clkpwr_uart5_clk_ctrl + * and clkpwr_uart6_clk_ctrl register definitions + */ +#define LPC32XX_CLKPWR_UART_Y_DIV(y) ((y) & 0xFF) +#define LPC32XX_CLKPWR_UART_X_DIV(x) (((x) & 0xFF) << 8) +#define LPC32XX_CLKPWR_UART_USE_HCLK _BIT(16) + +/* + * clkpwr_irda_clk_ctrl register definitions + */ +#define LPC32XX_CLKPWR_IRDA_Y_DIV(y) ((y) & 0xFF) +#define LPC32XX_CLKPWR_IRDA_X_DIV(x) (((x) & 0xFF) << 8) + +/* + * clkpwr_uart_clk_ctrl register definitions + */ +#define LPC32XX_CLKPWR_UARTCLKCTRL_UART6_EN _BIT(3) +#define LPC32XX_CLKPWR_UARTCLKCTRL_UART5_EN _BIT(2) +#define LPC32XX_CLKPWR_UARTCLKCTRL_UART4_EN _BIT(1) +#define LPC32XX_CLKPWR_UARTCLKCTRL_UART3_EN _BIT(0) + +/* + * clkpwr_dmaclk_ctrl register definitions + */ +#define LPC32XX_CLKPWR_DMACLKCTRL_CLK_EN 0x1 + +/* + * clkpwr_autoclock register definitions + */ +#define LPC32XX_CLKPWR_AUTOCLK_USB_EN 0x40 +#define LPC32XX_CLKPWR_AUTOCLK_IRAM_EN 0x02 +#define LPC32XX_CLKPWR_AUTOCLK_IROM_EN 0x01 + +/* + * Interrupt controller register offsets + */ +#define LPC32XX_INTC_MASK(x) io_p2v((x) + 0x00) +#define LPC32XX_INTC_RAW_STAT(x) io_p2v((x) + 0x04) +#define LPC32XX_INTC_STAT(x) io_p2v((x) + 0x08) +#define LPC32XX_INTC_POLAR(x) io_p2v((x) + 0x0C) +#define LPC32XX_INTC_ACT_TYPE(x) io_p2v((x) + 0x10) +#define LPC32XX_INTC_TYPE(x) io_p2v((x) + 0x14) + +/* + * Timer/counter register offsets + */ +#define LCP32XX_TIMER_IR(x) io_p2v((x) + 0x00) +#define LCP32XX_TIMER_TCR(x) io_p2v((x) + 0x04) +#define LCP32XX_TIMER_TC(x) io_p2v((x) + 0x08) +#define LCP32XX_TIMER_PR(x) io_p2v((x) + 0x0C) +#define LCP32XX_TIMER_PC(x) io_p2v((x) + 0x10) +#define LCP32XX_TIMER_MCR(x) io_p2v((x) + 0x14) +#define LCP32XX_TIMER_MR0(x) io_p2v((x) + 0x18) +#define LCP32XX_TIMER_MR1(x) io_p2v((x) + 0x1C) +#define LCP32XX_TIMER_MR2(x) io_p2v((x) + 0x20) +#define LCP32XX_TIMER_MR3(x) io_p2v((x) + 0x24) +#define LCP32XX_TIMER_CCR(x) io_p2v((x) + 0x28) +#define LCP32XX_TIMER_CR0(x) io_p2v((x) + 0x2C) +#define LCP32XX_TIMER_CR1(x) io_p2v((x) + 0x30) +#define LCP32XX_TIMER_CR2(x) io_p2v((x) + 0x34) +#define LCP32XX_TIMER_CR3(x) io_p2v((x) + 0x38) +#define LCP32XX_TIMER_EMR(x) io_p2v((x) + 0x3C) +#define LCP32XX_TIMER_CTCR(x) io_p2v((x) + 0x70) + +/* + * ir register definitions + */ +#define LCP32XX_TIMER_CNTR_MTCH_BIT(n) (1 << ((n) & 0x3)) +#define LCP32XX_TIMER_CNTR_CAPT_BIT(n) (1 << (4 + ((n) & 0x3))) + +/* + * tcr register definitions + */ +#define LCP32XX_TIMER_CNTR_TCR_EN 0x1 +#define LCP32XX_TIMER_CNTR_TCR_RESET 0x2 + +/* + * mcr register definitions + */ +#define LCP32XX_TIMER_CNTR_MCR_MTCH(n) (0x1 << ((n) * 3)) +#define LCP32XX_TIMER_CNTR_MCR_RESET(n) (0x1 << (((n) * 3) + 1)) +#define LCP32XX_TIMER_CNTR_MCR_STOP(n) (0x1 << (((n) * 3) + 2)) + +/* + * Standard UART register offsets + */ +#define LPC32XX_UART_DLL_FIFO(x) io_p2v((x) + 0x00) +#define LPC32XX_UART_DLM_IER(x) io_p2v((x) + 0x04) +#define LPC32XX_UART_IIR_FCR(x) io_p2v((x) + 0x08) +#define LPC32XX_UART_LCR(x) io_p2v((x) + 0x0C) +#define LPC32XX_UART_MODEM_CTRL(x) io_p2v((x) + 0x10) +#define LPC32XX_UART_LSR(x) io_p2v((x) + 0x14) +#define LPC32XX_UART_MODEM_STATUS(x) io_p2v((x) + 0x18) +#define LPC32XX_UART_RXLEV(x) io_p2v((x) + 0x1C) + +/* + * UART control structure offsets + */ +#define _UCREG(x) io_p2v(\ + LPC32XX_UART_CTRL_BASE + (x)) +#define LPC32XX_UARTCTL_CTRL _UCREG(0x00) +#define LPC32XX_UARTCTL_CLKMODE _UCREG(0x04) +#define LPC32XX_UARTCTL_CLOOP _UCREG(0x08) + +/* + * ctrl register definitions + */ +#define LPC32XX_UART_U3_MD_CTRL_EN _BIT(11) +#define LPC32XX_UART_IRRX6_INV_EN _BIT(10) +#define LPC32XX_UART_HDPX_EN _BIT(9) +#define LPC32XX_UART_UART6_IRDAMOD_BYPASS _BIT(5) +#define LPC32XX_RT_IRTX6_INV_EN _BIT(4) +#define LPC32XX_RT_IRTX6_INV_MIR_EN _BIT(3) +#define LPC32XX_RT_RX_IRPULSE_3_16_115K _BIT(2) +#define LPC32XX_RT_TX_IRPULSE_3_16_115K _BIT(1) +#define LPC32XX_UART_U5_ROUTE_TO_USB _BIT(0) + +/* + * clkmode register definitions + */ +#define LPC32XX_UART_ENABLED_CLOCKS(n) (((n) >> 16) & 0x7F) +#define LPC32XX_UART_ENABLED_CLOCK(n, u) (((n) >> (16 + (u))) & 0x1) +#define LPC32XX_UART_ENABLED_CLKS_ANY _BIT(14) +#define LPC32XX_UART_CLKMODE_OFF 0x0 +#define LPC32XX_UART_CLKMODE_ON 0x1 +#define LPC32XX_UART_CLKMODE_AUTO 0x2 +#define LPC32XX_UART_CLKMODE_MASK(u) (0x3 << ((((u) - 3) * 2) + 4)) +#define LPC32XX_UART_CLKMODE_LOAD(m, u) ((m) << ((((u) - 3) * 2) + 4)) + +/* + * GPIO Module Register offsets + */ +#define _GPREG(x) io_p2v(LPC32XX_GPIO_BASE + (x)) +#define LPC32XX_GPIO_P_MUX_SET _GPREG(0x100) +#define LPC32XX_GPIO_P_MUX_CLR _GPREG(0x104) +#define LPC32XX_GPIO_P_MUX_STATE _GPREG(0x108) +#define LPC32XX_GPIO_P3_MUX_SET _GPREG(0x110) +#define LPC32XX_GPIO_P3_MUX_CLR _GPREG(0x114) +#define LPC32XX_GPIO_P3_MUX_STATE _GPREG(0x118) +#define LPC32XX_GPIO_P0_MUX_SET _GPREG(0x120) +#define LPC32XX_GPIO_P0_MUX_CLR _GPREG(0x124) +#define LPC32XX_GPIO_P0_MUX_STATE _GPREG(0x128) +#define LPC32XX_GPIO_P1_MUX_SET _GPREG(0x130) +#define LPC32XX_GPIO_P1_MUX_CLR _GPREG(0x134) +#define LPC32XX_GPIO_P1_MUX_STATE _GPREG(0x138) + +#endif diff --git a/arch/arm/mach-lpc32xx/include/mach/system.h b/arch/arm/mach-lpc32xx/include/mach/system.h new file mode 100644 index 00000000000..df3b0dea4d7 --- /dev/null +++ b/arch/arm/mach-lpc32xx/include/mach/system.h @@ -0,0 +1,52 @@ +/* + * arch/arm/mach-lpc32xx/include/mach/system.h + * + * Author: Kevin Wells <kevin.wells@nxp.com> + * + * Copyright (C) 2010 NXP Semiconductors + * + * 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. + */ + +#ifndef __ASM_ARCH_SYSTEM_H +#define __ASM_ARCH_SYSTEM_H + +static void arch_idle(void) +{ + cpu_do_idle(); +} + +static inline void arch_reset(char mode, const char *cmd) +{ + extern void lpc32xx_watchdog_reset(void); + + switch (mode) { + case 's': + case 'h': + printk(KERN_CRIT "RESET: Rebooting system\n"); + + /* Disable interrupts */ + local_irq_disable(); + + lpc32xx_watchdog_reset(); + break; + + default: + /* Do nothing */ + break; + } + + /* Wait for watchdog to reset system */ + while (1) + ; +} + +#endif diff --git a/arch/arm/mach-lpc32xx/include/mach/timex.h b/arch/arm/mach-lpc32xx/include/mach/timex.h new file mode 100644 index 00000000000..8d4066b16b3 --- /dev/null +++ b/arch/arm/mach-lpc32xx/include/mach/timex.h @@ -0,0 +1,28 @@ +/* + * arch/arm/mach-lpc32xx/include/mach/timex.h + * + * Author: Kevin Wells <kevin.wells@nxp.com> + * + * Copyright (C) 2010 NXP Semiconductors + * + * 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. + */ + +#ifndef __ASM_ARCH_TIMEX_H +#define __ASM_ARCH_TIMEX_H + +/* + * Rate in Hz of the main system oscillator. This value should match + * the value 'MAIN_OSC_FREQ' in platform.h + */ +#define CLOCK_TICK_RATE 13000000 + +#endif diff --git a/arch/arm/mach-lpc32xx/include/mach/uncompress.h b/arch/arm/mach-lpc32xx/include/mach/uncompress.h new file mode 100644 index 00000000000..c142487d299 --- /dev/null +++ b/arch/arm/mach-lpc32xx/include/mach/uncompress.h @@ -0,0 +1,60 @@ +/* + * arch/arm/mach-lpc32xx/include/mach/uncompress.h + * + * Author: Kevin Wells <kevin.wells@nxp.com> + * + * Copyright (C) 2010 NXP Semiconductors + * + * 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. + */ + +#ifndef __ASM_ARM_ARCH_UNCOMPRESS_H +#define __ASM_ARM_ARCH_UNCOMPRESS_H + +#include <linux/io.h> + +#include <mach/hardware.h> +#include <mach/platform.h> + +/* + * Uncompress output is hardcoded to standard UART 5 + */ + +#define UART_FIFO_CTL_TX_RESET (1 << 2) +#define UART_STATUS_TX_MT (1 << 6) + +#define _UARTREG(x) (void __iomem *)(LPC32XX_UART5_BASE + (x)) + +#define LPC32XX_UART_DLLFIFO_O 0x00 +#define LPC32XX_UART_IIRFCR_O 0x08 +#define LPC32XX_UART_LSR_O 0x14 + +static inline void putc(int ch) +{ + /* Wait for transmit FIFO to empty */ + while ((__raw_readl(_UARTREG(LPC32XX_UART_LSR_O)) & + UART_STATUS_TX_MT) == 0) + ; + + __raw_writel((u32) ch, _UARTREG(LPC32XX_UART_DLLFIFO_O)); +} + +static inline void flush(void) +{ + __raw_writel(__raw_readl(_UARTREG(LPC32XX_UART_IIRFCR_O)) | + UART_FIFO_CTL_TX_RESET, _UARTREG(LPC32XX_UART_IIRFCR_O)); +} + +/* NULL functions; we don't presently need them */ +#define arch_decomp_setup() +#define arch_decomp_wdog() + +#endif diff --git a/arch/arm/mach-lpc32xx/include/mach/vmalloc.h b/arch/arm/mach-lpc32xx/include/mach/vmalloc.h new file mode 100644 index 00000000000..d1d936c7236 --- /dev/null +++ b/arch/arm/mach-lpc32xx/include/mach/vmalloc.h @@ -0,0 +1,24 @@ +/* + * arch/arm/mach-lpc32xx/include/mach/vmalloc.h + * + * Author: Kevin Wells <kevin.wells@nxp.com> + * + * Copyright (C) 2010 NXP Semiconductors + * + * 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. + */ + +#ifndef __ASM_ARCH_VMALLOC_H +#define __ASM_ARCH_VMALLOC_H + +#define VMALLOC_END 0xF0000000 + +#endif diff --git a/arch/arm/mach-lpc32xx/irq.c b/arch/arm/mach-lpc32xx/irq.c new file mode 100644 index 00000000000..bd0df26c415 --- /dev/null +++ b/arch/arm/mach-lpc32xx/irq.c @@ -0,0 +1,432 @@ +/* + * arch/arm/mach-lpc32xx/irq.c + * + * Author: Kevin Wells <kevin.wells@nxp.com> + * + * Copyright (C) 2010 NXP Semiconductors + * + * 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. + */ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/err.h> +#include <linux/io.h> + +#include <mach/irqs.h> +#include <mach/hardware.h> +#include <mach/platform.h> +#include "common.h" + +/* + * Default value representing the Activation polarity of all internal + * interrupt sources + */ +#define MIC_APR_DEFAULT 0x3FF0EFE0 +#define SIC1_APR_DEFAULT 0xFBD27186 +#define SIC2_APR_DEFAULT 0x801810C0 + +/* + * Default value representing the Activation Type of all internal + * interrupt sources. All are level sensitive. + */ +#define MIC_ATR_DEFAULT 0x00000000 +#define SIC1_ATR_DEFAULT 0x00026000 +#define SIC2_ATR_DEFAULT 0x00000000 + +struct lpc32xx_event_group_regs { + void __iomem *enab_reg; + void __iomem *edge_reg; + void __iomem *maskstat_reg; + void __iomem *rawstat_reg; +}; + +static const struct lpc32xx_event_group_regs lpc32xx_event_int_regs = { + .enab_reg = LPC32XX_CLKPWR_INT_ER, + .edge_reg = LPC32XX_CLKPWR_INT_AP, + .maskstat_reg = LPC32XX_CLKPWR_INT_SR, + .rawstat_reg = LPC32XX_CLKPWR_INT_RS, +}; + +static const struct lpc32xx_event_group_regs lpc32xx_event_pin_regs = { + .enab_reg = LPC32XX_CLKPWR_PIN_ER, + .edge_reg = LPC32XX_CLKPWR_PIN_AP, + .maskstat_reg = LPC32XX_CLKPWR_PIN_SR, + .rawstat_reg = LPC32XX_CLKPWR_PIN_RS, +}; + +struct lpc32xx_event_info { + const struct lpc32xx_event_group_regs *event_group; + u32 mask; +}; + +/* + * Maps an IRQ number to and event mask and register + */ +static const struct lpc32xx_event_info lpc32xx_events[NR_IRQS] = { + [IRQ_LPC32XX_GPI_08] = { + .event_group = &lpc32xx_event_pin_regs, + .mask = LPC32XX_CLKPWR_EXTSRC_GPI_08_BIT, + }, + [IRQ_LPC32XX_GPI_09] = { + .event_group = &lpc32xx_event_pin_regs, + .mask = LPC32XX_CLKPWR_EXTSRC_GPI_09_BIT, + }, + [IRQ_LPC32XX_GPI_19] = { + .event_group = &lpc32xx_event_pin_regs, + .mask = LPC32XX_CLKPWR_EXTSRC_GPI_19_BIT, + }, + [IRQ_LPC32XX_GPI_07] = { + .event_group = &lpc32xx_event_pin_regs, + .mask = LPC32XX_CLKPWR_EXTSRC_GPI_07_BIT, + }, + [IRQ_LPC32XX_GPI_00] = { + .event_group = &lpc32xx_event_pin_regs, + .mask = LPC32XX_CLKPWR_EXTSRC_GPI_00_BIT, + }, + [IRQ_LPC32XX_GPI_01] = { + .event_group = &lpc32xx_event_pin_regs, + .mask = LPC32XX_CLKPWR_EXTSRC_GPI_01_BIT, + }, + [IRQ_LPC32XX_GPI_02] = { + .event_group = &lpc32xx_event_pin_regs, + .mask = LPC32XX_CLKPWR_EXTSRC_GPI_02_BIT, + }, + [IRQ_LPC32XX_GPI_03] = { + .event_group = &lpc32xx_event_pin_regs, + .mask = LPC32XX_CLKPWR_EXTSRC_GPI_03_BIT, + }, + [IRQ_LPC32XX_GPI_04] = { + .event_group = &lpc32xx_event_pin_regs, + .mask = LPC32XX_CLKPWR_EXTSRC_GPI_04_BIT, + }, + [IRQ_LPC32XX_GPI_05] = { + .event_group = &lpc32xx_event_pin_regs, + .mask = LPC32XX_CLKPWR_EXTSRC_GPI_05_BIT, + }, + [IRQ_LPC32XX_GPI_06] = { + .event_group = &lpc32xx_event_pin_regs, + .mask = LPC32XX_CLKPWR_EXTSRC_GPI_06_BIT, + }, + [IRQ_LPC32XX_GPIO_00] = { + .event_group = &lpc32xx_event_int_regs, + .mask = LPC32XX_CLKPWR_INTSRC_GPIO_00_BIT, + }, + [IRQ_LPC32XX_GPIO_01] = { + .event_group = &lpc32xx_event_int_regs, + .mask = LPC32XX_CLKPWR_INTSRC_GPIO_01_BIT, + }, + [IRQ_LPC32XX_GPIO_02] = { + .event_group = &lpc32xx_event_int_regs, + .mask = LPC32XX_CLKPWR_INTSRC_GPIO_02_BIT, + }, + [IRQ_LPC32XX_GPIO_03] = { + .event_group = &lpc32xx_event_int_regs, + .mask = LPC32XX_CLKPWR_INTSRC_GPIO_03_BIT, + }, + [IRQ_LPC32XX_GPIO_04] = { + .event_group = &lpc32xx_event_int_regs, + .mask = LPC32XX_CLKPWR_INTSRC_GPIO_04_BIT, + }, + [IRQ_LPC32XX_GPIO_05] = { + .event_group = &lpc32xx_event_int_regs, + .mask = LPC32XX_CLKPWR_INTSRC_GPIO_05_BIT, + }, + [IRQ_LPC32XX_KEY] = { + .event_group = &lpc32xx_event_int_regs, + .mask = LPC32XX_CLKPWR_INTSRC_KEY_BIT, + }, + [IRQ_LPC32XX_USB_OTG_ATX] = { + .event_group = &lpc32xx_event_int_regs, + .mask = LPC32XX_CLKPWR_INTSRC_USBATXINT_BIT, + }, + [IRQ_LPC32XX_USB_HOST] = { + .event_group = &lpc32xx_event_int_regs, + .mask = LPC32XX_CLKPWR_INTSRC_USB_BIT, + }, + [IRQ_LPC32XX_RTC] = { + .event_group = &lpc32xx_event_int_regs, + .mask = LPC32XX_CLKPWR_INTSRC_RTC_BIT, + }, + [IRQ_LPC32XX_MSTIMER] = { + .event_group = &lpc32xx_event_int_regs, + .mask = LPC32XX_CLKPWR_INTSRC_MSTIMER_BIT, + }, + [IRQ_LPC32XX_TS_AUX] = { + .event_group = &lpc32xx_event_int_regs, + .mask = LPC32XX_CLKPWR_INTSRC_TS_AUX_BIT, + }, + [IRQ_LPC32XX_TS_P] = { + .event_group = &lpc32xx_event_int_regs, + .mask = LPC32XX_CLKPWR_INTSRC_TS_P_BIT, + }, + [IRQ_LPC32XX_TS_IRQ] = { + .event_group = &lpc32xx_event_int_regs, + .mask = LPC32XX_CLKPWR_INTSRC_ADC_BIT, + }, +}; + +static void get_controller(unsigned int irq, unsigned int *base, + unsigned int *irqbit) +{ + if (irq < 32) { + *base = LPC32XX_MIC_BASE; + *irqbit = 1 << irq; + } else if (irq < 64) { + *base = LPC32XX_SIC1_BASE; + *irqbit = 1 << (irq - 32); + } else { + *base = LPC32XX_SIC2_BASE; + *irqbit = 1 << (irq - 64); + } +} + +static void lpc32xx_mask_irq(unsigned int irq) +{ + unsigned int reg, ctrl, mask; + + get_controller(irq, &ctrl, &mask); + + reg = __raw_readl(LPC32XX_INTC_MASK(ctrl)) & ~mask; + __raw_writel(reg, LPC32XX_INTC_MASK(ctrl)); +} + +static void lpc32xx_unmask_irq(unsigned int irq) +{ + unsigned int reg, ctrl, mask; + + get_controller(irq, &ctrl, &mask); + + reg = __raw_readl(LPC32XX_INTC_MASK(ctrl)) | mask; + __raw_writel(reg, LPC32XX_INTC_MASK(ctrl)); +} + +static void lpc32xx_ack_irq(unsigned int irq) +{ + unsigned int ctrl, mask; + + get_controller(irq, &ctrl, &mask); + + __raw_writel(mask, LPC32XX_INTC_RAW_STAT(ctrl)); + + /* Also need to clear pending wake event */ + if (lpc32xx_events[irq].mask != 0) + __raw_writel(lpc32xx_events[irq].mask, + lpc32xx_events[irq].event_group->rawstat_reg); +} + +static void __lpc32xx_set_irq_type(unsigned int irq, int use_high_level, + int use_edge) +{ + unsigned int reg, ctrl, mask; + + get_controller(irq, &ctrl, &mask); + + /* Activation level, high or low */ + reg = __raw_readl(LPC32XX_INTC_POLAR(ctrl)); + if (use_high_level) + reg |= mask; + else + reg &= ~mask; + __raw_writel(reg, LPC32XX_INTC_POLAR(ctrl)); + + /* Activation type, edge or level */ + reg = __raw_readl(LPC32XX_INTC_ACT_TYPE(ctrl)); + if (use_edge) + reg |= mask; + else + reg &= ~mask; + __raw_writel(reg, LPC32XX_INTC_ACT_TYPE(ctrl)); + + /* Use same polarity for the wake events */ + if (lpc32xx_events[irq].mask != 0) { + reg = __raw_readl(lpc32xx_events[irq].event_group->edge_reg); + + if (use_high_level) + reg |= lpc32xx_events[irq].mask; + else + reg &= ~lpc32xx_events[irq].mask; + + __raw_writel(reg, lpc32xx_events[irq].event_group->edge_reg); + } +} + +static int lpc32xx_set_irq_type(unsigned int irq, unsigned int type) +{ + switch (type) { + case IRQ_TYPE_EDGE_RISING: + /* Rising edge sensitive */ + __lpc32xx_set_irq_type(irq, 1, 1); + break; + + case IRQ_TYPE_EDGE_FALLING: + /* Falling edge sensitive */ + __lpc32xx_set_irq_type(irq, 0, 1); + break; + + case IRQ_TYPE_LEVEL_LOW: + /* Low level sensitive */ + __lpc32xx_set_irq_type(irq, 0, 0); + break; + + case IRQ_TYPE_LEVEL_HIGH: + /* High level sensitive */ + __lpc32xx_set_irq_type(irq, 1, 0); + break; + + /* Other modes are not supported */ + default: + return -EINVAL; + } + + /* Ok to use the level handler for all types */ + set_irq_handler(irq, handle_level_irq); + + return 0; +} + +static int lpc32xx_irq_wake(unsigned int irqno, unsigned int state) +{ + unsigned long eventreg; + + if (lpc32xx_events[irqno].mask != 0) { + eventreg = __raw_readl(lpc32xx_events[irqno]. + event_group->enab_reg); + + if (state) + eventreg |= lpc32xx_events[irqno].mask; + else + eventreg &= ~lpc32xx_events[irqno].mask; + + __raw_writel(eventreg, + lpc32xx_events[irqno].event_group->enab_reg); + + return 0; + } + + /* Clear event */ + __raw_writel(lpc32xx_events[irqno].mask, + lpc32xx_events[irqno].event_group->rawstat_reg); + + return -ENODEV; +} + +static void __init lpc32xx_set_default_mappings(unsigned int apr, + unsigned int atr, unsigned int offset) +{ + unsigned int i; + + /* Set activation levels for each interrupt */ + i = 0; + while (i < 32) { + __lpc32xx_set_irq_type(offset + i, ((apr >> i) & 0x1), + ((atr >> i) & 0x1)); + i++; + } +} + +static struct irq_chip lpc32xx_irq_chip = { + .ack = lpc32xx_ack_irq, + .mask = lpc32xx_mask_irq, + .unmask = lpc32xx_unmask_irq, + .set_type = lpc32xx_set_irq_type, + .set_wake = lpc32xx_irq_wake +}; + +static void lpc32xx_sic1_handler(unsigned int irq, struct irq_desc *desc) +{ + unsigned long ints = __raw_readl(LPC32XX_INTC_STAT(LPC32XX_SIC1_BASE)); + + while (ints != 0) { + int irqno = fls(ints) - 1; + + ints &= ~(1 << irqno); + + generic_handle_irq(LPC32XX_SIC1_IRQ(irqno)); + } +} + +static void lpc32xx_sic2_handler(unsigned int irq, struct irq_desc *desc) +{ + unsigned long ints = __raw_readl(LPC32XX_INTC_STAT(LPC32XX_SIC2_BASE)); + + while (ints != 0) { + int irqno = fls(ints) - 1; + + ints &= ~(1 << irqno); + + generic_handle_irq(LPC32XX_SIC2_IRQ(irqno)); + } +} + +void __init lpc32xx_init_irq(void) +{ + unsigned int i; + + /* Setup MIC */ + __raw_writel(0, LPC32XX_INTC_MASK(LPC32XX_MIC_BASE)); + __raw_writel(MIC_APR_DEFAULT, LPC32XX_INTC_POLAR(LPC32XX_MIC_BASE)); + __raw_writel(MIC_ATR_DEFAULT, LPC32XX_INTC_ACT_TYPE(LPC32XX_MIC_BASE)); + + /* Setup SIC1 */ + __raw_writel(0, LPC32XX_INTC_MASK(LPC32XX_SIC1_BASE)); + __raw_writel(MIC_APR_DEFAULT, LPC32XX_INTC_POLAR(LPC32XX_SIC1_BASE)); + __raw_writel(MIC_ATR_DEFAULT, LPC32XX_INTC_ACT_TYPE(LPC32XX_SIC1_BASE)); + + /* Setup SIC2 */ + __raw_writel(0, LPC32XX_INTC_MASK(LPC32XX_SIC2_BASE)); + __raw_writel(MIC_APR_DEFAULT, LPC32XX_INTC_POLAR(LPC32XX_SIC2_BASE)); + __raw_writel(MIC_ATR_DEFAULT, LPC32XX_INTC_ACT_TYPE(LPC32XX_SIC2_BASE)); + + /* Configure supported IRQ's */ + for (i = 0; i < NR_IRQS; i++) { + set_irq_chip(i, &lpc32xx_irq_chip); + set_irq_handler(i, handle_level_irq); + set_irq_flags(i, IRQF_VALID); + } + + /* Set default mappings */ + lpc32xx_set_default_mappings(MIC_APR_DEFAULT, MIC_ATR_DEFAULT, 0); + lpc32xx_set_default_mappings(SIC1_APR_DEFAULT, SIC1_ATR_DEFAULT, 32); + lpc32xx_set_default_mappings(SIC2_APR_DEFAULT, SIC2_ATR_DEFAULT, 64); + + /* mask all interrupts except SUBIRQ */ + __raw_writel(0, LPC32XX_INTC_MASK(LPC32XX_MIC_BASE)); + __raw_writel(0, LPC32XX_INTC_MASK(LPC32XX_SIC1_BASE)); + __raw_writel(0, LPC32XX_INTC_MASK(LPC32XX_SIC2_BASE)); + + /* MIC SUBIRQx interrupts will route handling to the chain handlers */ + set_irq_chained_handler(IRQ_LPC32XX_SUB1IRQ, lpc32xx_sic1_handler); + set_irq_chained_handler(IRQ_LPC32XX_SUB2IRQ, lpc32xx_sic2_handler); + + /* Initially disable all wake events */ + __raw_writel(0, LPC32XX_CLKPWR_P01_ER); + __raw_writel(0, LPC32XX_CLKPWR_INT_ER); + __raw_writel(0, LPC32XX_CLKPWR_PIN_ER); + + /* + * Default wake activation polarities, all pin sources are low edge + * triggered + */ + __raw_writel(LPC32XX_CLKPWR_INTSRC_TS_P_BIT | + LPC32XX_CLKPWR_INTSRC_MSTIMER_BIT | + LPC32XX_CLKPWR_INTSRC_RTC_BIT, + LPC32XX_CLKPWR_INT_AP); + __raw_writel(0, LPC32XX_CLKPWR_PIN_AP); + + /* Clear latched wake event states */ + __raw_writel(__raw_readl(LPC32XX_CLKPWR_PIN_RS), + LPC32XX_CLKPWR_PIN_RS); + __raw_writel(__raw_readl(LPC32XX_CLKPWR_INT_RS), + LPC32XX_CLKPWR_INT_RS); +} diff --git a/arch/arm/mach-lpc32xx/phy3250.c b/arch/arm/mach-lpc32xx/phy3250.c new file mode 100644 index 00000000000..bc9a42da214 --- /dev/null +++ b/arch/arm/mach-lpc32xx/phy3250.c @@ -0,0 +1,397 @@ +/* + * arch/arm/mach-lpc32xx/phy3250.c + * + * Author: Kevin Wells <kevin.wells@nxp.com> + * + * Copyright (C) 2010 NXP Semiconductors + * + * 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. + */ + +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/sysdev.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/dma-mapping.h> +#include <linux/device.h> +#include <linux/spi/spi.h> +#include <linux/spi/eeprom.h> +#include <linux/leds.h> +#include <linux/gpio.h> +#include <linux/amba/bus.h> +#include <linux/amba/clcd.h> +#include <linux/amba/pl022.h> + +#include <asm/setup.h> +#include <asm/mach-types.h> +#include <asm/mach/arch.h> + +#include <mach/hardware.h> +#include <mach/platform.h> +#include "common.h" + +/* + * Mapped GPIOLIB GPIOs + */ +#define SPI0_CS_GPIO LPC32XX_GPIO(LPC32XX_GPIO_P3_GRP, 5) +#define LCD_POWER_GPIO LPC32XX_GPIO(LPC32XX_GPO_P3_GRP, 0) +#define BKL_POWER_GPIO LPC32XX_GPIO(LPC32XX_GPO_P3_GRP, 4) +#define LED_GPIO LPC32XX_GPIO(LPC32XX_GPO_P3_GRP, 1) + +/* + * AMBA LCD controller + */ +static struct clcd_panel conn_lcd_panel = { + .mode = { + .name = "QVGA portrait", + .refresh = 60, + .xres = 240, + .yres = 320, + .pixclock = 191828, + .left_margin = 22, + .right_margin = 11, + .upper_margin = 2, + .lower_margin = 1, + .hsync_len = 5, + .vsync_len = 2, + .sync = 0, + .vmode = FB_VMODE_NONINTERLACED, + }, + .width = -1, + .height = -1, + .tim2 = (TIM2_IVS | TIM2_IHS), + .cntl = (CNTL_BGR | CNTL_LCDTFT | CNTL_LCDVCOMP(1) | + CNTL_LCDBPP16_565), + .bpp = 16, +}; +#define PANEL_SIZE (3 * SZ_64K) + +static int lpc32xx_clcd_setup(struct clcd_fb *fb) +{ + dma_addr_t dma; + + fb->fb.screen_base = dma_alloc_writecombine(&fb->dev->dev, + PANEL_SIZE, &dma, GFP_KERNEL); + if (!fb->fb.screen_base) { + printk(KERN_ERR "CLCD: unable to map framebuffer\n"); + return -ENOMEM; + } + + fb->fb.fix.smem_start = dma; + fb->fb.fix.smem_len = PANEL_SIZE; + fb->panel = &conn_lcd_panel; + + if (gpio_request(LCD_POWER_GPIO, "LCD power")) + printk(KERN_ERR "Error requesting gpio %u", + LCD_POWER_GPIO); + else if (gpio_direction_output(LCD_POWER_GPIO, 1)) + printk(KERN_ERR "Error setting gpio %u to output", + LCD_POWER_GPIO); + + if (gpio_request(BKL_POWER_GPIO, "LCD backlight power")) + printk(KERN_ERR "Error requesting gpio %u", + BKL_POWER_GPIO); + else if (gpio_direction_output(BKL_POWER_GPIO, 1)) + printk(KERN_ERR "Error setting gpio %u to output", + BKL_POWER_GPIO); + + return 0; +} + +static int lpc32xx_clcd_mmap(struct clcd_fb *fb, struct vm_area_struct *vma) +{ + return dma_mmap_writecombine(&fb->dev->dev, vma, + fb->fb.screen_base, fb->fb.fix.smem_start, + fb->fb.fix.smem_len); +} + +static void lpc32xx_clcd_remove(struct clcd_fb *fb) +{ + dma_free_writecombine(&fb->dev->dev, fb->fb.fix.smem_len, + fb->fb.screen_base, fb->fb.fix.smem_start); +} + +/* + * On some early LCD modules (1307.0), the backlight logic is inverted. + * For those board variants, swap the disable and enable states for + * BKL_POWER_GPIO. +*/ +static void clcd_disable(struct clcd_fb *fb) +{ + gpio_set_value(BKL_POWER_GPIO, 0); + gpio_set_value(LCD_POWER_GPIO, 0); +} + +static void clcd_enable(struct clcd_fb *fb) +{ + gpio_set_value(BKL_POWER_GPIO, 1); + gpio_set_value(LCD_POWER_GPIO, 1); +} + +static struct clcd_board lpc32xx_clcd_data = { + .name = "Phytec LCD", + .check = clcdfb_check, + .decode = clcdfb_decode, + .disable = clcd_disable, + .enable = clcd_enable, + .setup = lpc32xx_clcd_setup, + .mmap = lpc32xx_clcd_mmap, + .remove = lpc32xx_clcd_remove, +}; + +static struct amba_device lpc32xx_clcd_device = { + .dev = { + .coherent_dma_mask = ~0, + .init_name = "dev:clcd", + .platform_data = &lpc32xx_clcd_data, + }, + .res = { + .start = LPC32XX_LCD_BASE, + .end = (LPC32XX_LCD_BASE + SZ_4K - 1), + .flags = IORESOURCE_MEM, + }, + .dma_mask = ~0, + .irq = {IRQ_LPC32XX_LCD, NO_IRQ}, +}; + +/* + * AMBA SSP (SPI) + */ +static void phy3250_spi_cs_set(u32 control) +{ + gpio_set_value(SPI0_CS_GPIO, (int) control); +} + +static struct pl022_config_chip spi0_chip_info = { + .lbm = LOOPBACK_DISABLED, + .com_mode = INTERRUPT_TRANSFER, + .iface = SSP_INTERFACE_MOTOROLA_SPI, + .hierarchy = SSP_MASTER, + .slave_tx_disable = 0, + .endian_tx = SSP_TX_LSB, + .endian_rx = SSP_RX_LSB, + .data_size = SSP_DATA_BITS_8, + .rx_lev_trig = SSP_RX_4_OR_MORE_ELEM, + .tx_lev_trig = SSP_TX_4_OR_MORE_EMPTY_LOC, + .clk_phase = SSP_CLK_FIRST_EDGE, + .clk_pol = SSP_CLK_POL_IDLE_LOW, + .ctrl_len = SSP_BITS_8, + .wait_state = SSP_MWIRE_WAIT_ZERO, + .duplex = SSP_MICROWIRE_CHANNEL_FULL_DUPLEX, + .cs_control = phy3250_spi_cs_set, +}; + +static struct pl022_ssp_controller lpc32xx_ssp0_data = { + .bus_id = 0, + .num_chipselect = 1, + .enable_dma = 0, +}; + +static struct amba_device lpc32xx_ssp0_device = { + .dev = { + .coherent_dma_mask = ~0, + .init_name = "dev:ssp0", + .platform_data = &lpc32xx_ssp0_data, + }, + .res = { + .start = LPC32XX_SSP0_BASE, + .end = (LPC32XX_SSP0_BASE + SZ_4K - 1), + .flags = IORESOURCE_MEM, + }, + .dma_mask = ~0, + .irq = {IRQ_LPC32XX_SSP0, NO_IRQ}, +}; + +/* AT25 driver registration */ +static int __init phy3250_spi_board_register(void) +{ +#if defined(CONFIG_SPI_SPIDEV) || defined(CONFIG_SPI_SPIDEV_MODULE) + static struct spi_board_info info[] = { + { + .modalias = "spidev", + .max_speed_hz = 5000000, + .bus_num = 0, + .chip_select = 0, + .controller_data = &spi0_chip_info, + }, + }; + +#else + static struct spi_eeprom eeprom = { + .name = "at25256a", + .byte_len = 0x8000, + .page_size = 64, + .flags = EE_ADDR2, + }; + + static struct spi_board_info info[] = { + { + .modalias = "at25", + .max_speed_hz = 5000000, + .bus_num = 0, + .chip_select = 0, + .platform_data = &eeprom, + .controller_data = &spi0_chip_info, + }, + }; +#endif + return spi_register_board_info(info, ARRAY_SIZE(info)); +} +arch_initcall(phy3250_spi_board_register); + +static struct i2c_board_info __initdata phy3250_i2c_board_info[] = { + { + I2C_BOARD_INFO("pcf8563", 0x51), + }, +}; + +static struct gpio_led phy_leds[] = { + { + .name = "led0", + .gpio = LED_GPIO, + .active_low = 1, + .default_trigger = "heartbeat", + }, +}; + +static struct gpio_led_platform_data led_data = { + .leds = phy_leds, + .num_leds = ARRAY_SIZE(phy_leds), +}; + +static struct platform_device lpc32xx_gpio_led_device = { + .name = "leds-gpio", + .id = -1, + .dev.platform_data = &led_data, +}; + +static struct platform_device *phy3250_devs[] __initdata = { + &lpc32xx_i2c0_device, + &lpc32xx_i2c1_device, + &lpc32xx_i2c2_device, + &lpc32xx_watchdog_device, + &lpc32xx_gpio_led_device, +}; + +static struct amba_device *amba_devs[] __initdata = { + &lpc32xx_clcd_device, + &lpc32xx_ssp0_device, +}; + +/* + * Board specific functions + */ +static void __init phy3250_board_init(void) +{ + u32 tmp; + int i; + + lpc32xx_gpio_init(); + + /* Register GPIOs used on this board */ + if (gpio_request(SPI0_CS_GPIO, "spi0 cs")) + printk(KERN_ERR "Error requesting gpio %u", + SPI0_CS_GPIO); + else if (gpio_direction_output(SPI0_CS_GPIO, 1)) + printk(KERN_ERR "Error setting gpio %u to output", + SPI0_CS_GPIO); + + /* Setup network interface for RMII mode */ + tmp = __raw_readl(LPC32XX_CLKPWR_MACCLK_CTRL); + tmp &= ~LPC32XX_CLKPWR_MACCTRL_PINS_MSK; + tmp |= LPC32XX_CLKPWR_MACCTRL_USE_RMII_PINS; + __raw_writel(tmp, LPC32XX_CLKPWR_MACCLK_CTRL); + + /* Setup SLC NAND controller muxing */ + __raw_writel(LPC32XX_CLKPWR_NANDCLK_SEL_SLC, + LPC32XX_CLKPWR_NAND_CLK_CTRL); + + /* Setup LCD muxing to RGB565 */ + tmp = __raw_readl(LPC32XX_CLKPWR_LCDCLK_CTRL) & + ~(LPC32XX_CLKPWR_LCDCTRL_LCDTYPE_MSK | + LPC32XX_CLKPWR_LCDCTRL_PSCALE_MSK); + tmp |= LPC32XX_CLKPWR_LCDCTRL_LCDTYPE_TFT16; + __raw_writel(tmp, LPC32XX_CLKPWR_LCDCLK_CTRL); + + /* Set up I2C pull levels */ + tmp = __raw_readl(LPC32XX_CLKPWR_I2C_CLK_CTRL); + tmp |= LPC32XX_CLKPWR_I2CCLK_USBI2CHI_DRIVE | + LPC32XX_CLKPWR_I2CCLK_I2C2HI_DRIVE; + __raw_writel(tmp, LPC32XX_CLKPWR_I2C_CLK_CTRL); + + /* Disable IrDA pulsing support on UART6 */ + tmp = __raw_readl(LPC32XX_UARTCTL_CTRL); + tmp |= LPC32XX_UART_UART6_IRDAMOD_BYPASS; + __raw_writel(tmp, LPC32XX_UARTCTL_CTRL); + + /* Enable DMA for I2S1 channel */ + tmp = __raw_readl(LPC32XX_CLKPWR_I2S_CLK_CTRL); + tmp = LPC32XX_CLKPWR_I2SCTRL_I2S1_USE_DMA; + __raw_writel(tmp, LPC32XX_CLKPWR_I2S_CLK_CTRL); + + lpc32xx_serial_init(); + + /* + * AMBA peripheral clocks need to be enabled prior to AMBA device + * detection or a data fault will occur, so enable the clocks + * here. However, we don't want to enable them if the peripheral + * isn't included in the image + */ +#ifdef CONFIG_FB_ARMCLCD + tmp = __raw_readl(LPC32XX_CLKPWR_LCDCLK_CTRL); + __raw_writel((tmp | LPC32XX_CLKPWR_LCDCTRL_CLK_EN), + LPC32XX_CLKPWR_LCDCLK_CTRL); +#endif +#ifdef CONFIG_SPI_PL022 + tmp = __raw_readl(LPC32XX_CLKPWR_SSP_CLK_CTRL); + __raw_writel((tmp | LPC32XX_CLKPWR_SSPCTRL_SSPCLK0_EN), + LPC32XX_CLKPWR_SSP_CLK_CTRL); +#endif + + platform_add_devices(phy3250_devs, ARRAY_SIZE(phy3250_devs)); + for (i = 0; i < ARRAY_SIZE(amba_devs); i++) { + struct amba_device *d = amba_devs[i]; + amba_device_register(d, &iomem_resource); + } + + /* Test clock needed for UDA1380 initial init */ + __raw_writel(LPC32XX_CLKPWR_TESTCLK2_SEL_MOSC | + LPC32XX_CLKPWR_TESTCLK_TESTCLK2_EN, + LPC32XX_CLKPWR_TEST_CLK_SEL); + + i2c_register_board_info(0, phy3250_i2c_board_info, + ARRAY_SIZE(phy3250_i2c_board_info)); +} + +static int __init lpc32xx_display_uid(void) +{ + u32 uid[4]; + + lpc32xx_get_uid(uid); + + printk(KERN_INFO "LPC32XX unique ID: %08x%08x%08x%08x\n", + uid[3], uid[2], uid[1], uid[0]); + + return 1; +} +arch_initcall(lpc32xx_display_uid); + +MACHINE_START(PHY3250, "Phytec 3250 board with the LPC3250 Microcontroller") + /* Maintainer: Kevin Wells, NXP Semiconductors */ + .phys_io = LPC32XX_UART5_BASE, + .io_pg_offst = ((IO_ADDRESS(LPC32XX_UART5_BASE))>>18) & 0xfffc, + .boot_params = 0x80000100, + .map_io = lpc32xx_map_io, + .init_irq = lpc32xx_init_irq, + .timer = &lpc32xx_timer, + .init_machine = phy3250_board_init, +MACHINE_END diff --git a/arch/arm/mach-lpc32xx/pm.c b/arch/arm/mach-lpc32xx/pm.c new file mode 100644 index 00000000000..a6e2aed9a49 --- /dev/null +++ b/arch/arm/mach-lpc32xx/pm.c @@ -0,0 +1,146 @@ +/* + * arch/arm/mach-lpc32xx/pm.c + * + * Original authors: Vitaly Wool, Dmitry Chigirev <source@mvista.com> + * Modified by Kevin Wells <kevin.wells@nxp.com> + * + * 2005 (c) MontaVista Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +/* + * LPC32XX CPU and system power management + * + * The LCP32XX has three CPU modes for controlling system power: run, + * direct-run, and halt modes. When switching between halt and run modes, + * the CPU transistions through direct-run mode. For Linux, direct-run + * mode is not used in normal operation. Halt mode is used when the + * system is fully suspended. + * + * Run mode: + * The ARM CPU clock (HCLK_PLL), HCLK bus clock, and PCLK bus clocks are + * derived from the HCLK PLL. The HCLK and PCLK bus rates are divided from + * the HCLK_PLL rate. Linux runs in this mode. + * + * Direct-run mode: + * The ARM CPU clock, HCLK bus clock, and PCLK bus clocks are driven from + * SYSCLK. SYSCLK is usually around 13MHz, but may vary based on SYSCLK + * source or the frequency of the main oscillator. In this mode, the + * HCLK_PLL can be safely enabled, changed, or disabled. + * + * Halt mode: + * SYSCLK is gated off and the CPU and system clocks are halted. + * Peripherals based on the 32KHz oscillator clock (ie, RTC, touch, + * key scanner, etc.) still operate if enabled. In this state, an enabled + * system event (ie, GPIO state change, RTC match, key press, etc.) will + * wake the system up back into direct-run mode. + * + * DRAM refresh + * DRAM clocking and refresh are slightly different for systems with DDR + * DRAM or regular SDRAM devices. If SDRAM is used in the system, the + * SDRAM will still be accessible in direct-run mode. In DDR based systems, + * a transistion to direct-run mode will stop all DDR accesses (no clocks). + * Because of this, the code to switch power modes and the code to enter + * and exit DRAM self-refresh modes must not be executed in DRAM. A small + * section of IRAM is used instead for this. + * + * Suspend is handled with the following logic: + * Backup a small area of IRAM used for the suspend code + * Copy suspend code to IRAM + * Transfer control to code in IRAM + * Places DRAMs in self-refresh mode + * Enter direct-run mode + * Save state of HCLK_PLL PLL + * Disable HCLK_PLL PLL + * Enter halt mode - CPU and buses will stop + * System enters direct-run mode when an enabled event occurs + * HCLK PLL state is restored + * Run mode is entered + * DRAMS are placed back into normal mode + * Code execution returns from IRAM + * IRAM code are used for suspend is restored + * Suspend mode is exited + */ + +#include <linux/suspend.h> +#include <linux/io.h> +#include <linux/slab.h> + +#include <asm/cacheflush.h> + +#include <mach/hardware.h> +#include <mach/platform.h> +#include "common.h" +#include "clock.h" + +#define TEMP_IRAM_AREA IO_ADDRESS(LPC32XX_IRAM_BASE) + +/* + * Both STANDBY and MEM suspend states are handled the same with no + * loss of CPU or memory state + */ +static int lpc32xx_pm_enter(suspend_state_t state) +{ + int (*lpc32xx_suspend_ptr) (void); + void *iram_swap_area; + + /* Allocate some space for temporary IRAM storage */ + iram_swap_area = kmalloc(lpc32xx_sys_suspend_sz, GFP_KERNEL); + if (!iram_swap_area) { + printk(KERN_ERR + "PM Suspend: cannot allocate memory to save portion " + "of SRAM\n"); + return -ENOMEM; + } + + /* Backup a small area of IRAM used for the suspend code */ + memcpy(iram_swap_area, (void *) TEMP_IRAM_AREA, + lpc32xx_sys_suspend_sz); + + /* + * Copy code to suspend system into IRAM. The suspend code + * needs to run from IRAM as DRAM may no longer be available + * when the PLL is stopped. + */ + memcpy((void *) TEMP_IRAM_AREA, &lpc32xx_sys_suspend, + lpc32xx_sys_suspend_sz); + flush_icache_range((unsigned long)TEMP_IRAM_AREA, + (unsigned long)(TEMP_IRAM_AREA) + lpc32xx_sys_suspend_sz); + + /* Transfer to suspend code in IRAM */ + lpc32xx_suspend_ptr = (void *) TEMP_IRAM_AREA; + flush_cache_all(); + (void) lpc32xx_suspend_ptr(); + + /* Restore original IRAM contents */ + memcpy((void *) TEMP_IRAM_AREA, iram_swap_area, + lpc32xx_sys_suspend_sz); + + kfree(iram_swap_area); + + return 0; +} + +static struct platform_suspend_ops lpc32xx_pm_ops = { + .valid = suspend_valid_only_mem, + .enter = lpc32xx_pm_enter, +}; + +#define EMC_DYN_MEM_CTRL_OFS 0x20 +#define EMC_SRMMC (1 << 3) +#define EMC_CTRL_REG io_p2v(LPC32XX_EMC_BASE + EMC_DYN_MEM_CTRL_OFS) +static int __init lpc32xx_pm_init(void) +{ + /* + * Setup SDRAM self-refresh clock to automatically disable o + * start of self-refresh. This only needs to be done once. + */ + __raw_writel(__raw_readl(EMC_CTRL_REG) | EMC_SRMMC, EMC_CTRL_REG); + + suspend_set_ops(&lpc32xx_pm_ops); + + return 0; +} +arch_initcall(lpc32xx_pm_init); diff --git a/arch/arm/mach-lpc32xx/serial.c b/arch/arm/mach-lpc32xx/serial.c new file mode 100644 index 00000000000..429cfdbb2b3 --- /dev/null +++ b/arch/arm/mach-lpc32xx/serial.c @@ -0,0 +1,190 @@ +/* + * arch/arm/mach-lpc32xx/serial.c + * + * Author: Kevin Wells <kevin.wells@nxp.com> + * + * Copyright (C) 2010 NXP Semiconductors + * + * 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. + */ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/serial.h> +#include <linux/serial_core.h> +#include <linux/serial_reg.h> +#include <linux/serial_8250.h> +#include <linux/clk.h> +#include <linux/io.h> + +#include <mach/hardware.h> +#include <mach/platform.h> +#include "common.h" + +#define LPC32XX_SUART_FIFO_SIZE 64 + +/* Standard 8250/16550 compatible serial ports */ +static struct plat_serial8250_port serial_std_platform_data[] = { +#ifdef CONFIG_ARCH_LPC32XX_UART5_SELECT + { + .membase = io_p2v(LPC32XX_UART5_BASE), + .mapbase = LPC32XX_UART5_BASE, + .irq = IRQ_LPC32XX_UART_IIR5, + .uartclk = LPC32XX_MAIN_OSC_FREQ, + .regshift = 2, + .iotype = UPIO_MEM32, + .flags = UPF_BOOT_AUTOCONF | UPF_BUGGY_UART | + UPF_SKIP_TEST, + }, +#endif +#ifdef CONFIG_ARCH_LPC32XX_UART3_SELECT + { + .membase = io_p2v(LPC32XX_UART3_BASE), + .mapbase = LPC32XX_UART3_BASE, + .irq = IRQ_LPC32XX_UART_IIR3, + .uartclk = LPC32XX_MAIN_OSC_FREQ, + .regshift = 2, + .iotype = UPIO_MEM32, + .flags = UPF_BOOT_AUTOCONF | UPF_BUGGY_UART | + UPF_SKIP_TEST, + }, +#endif +#ifdef CONFIG_ARCH_LPC32XX_UART4_SELECT + { + .membase = io_p2v(LPC32XX_UART4_BASE), + .mapbase = LPC32XX_UART4_BASE, + .irq = IRQ_LPC32XX_UART_IIR4, + .uartclk = LPC32XX_MAIN_OSC_FREQ, + .regshift = 2, + .iotype = UPIO_MEM32, + .flags = UPF_BOOT_AUTOCONF | UPF_BUGGY_UART | + UPF_SKIP_TEST, + }, +#endif +#ifdef CONFIG_ARCH_LPC32XX_UART6_SELECT + { + .membase = io_p2v(LPC32XX_UART6_BASE), + .mapbase = LPC32XX_UART6_BASE, + .irq = IRQ_LPC32XX_UART_IIR6, + .uartclk = LPC32XX_MAIN_OSC_FREQ, + .regshift = 2, + .iotype = UPIO_MEM32, + .flags = UPF_BOOT_AUTOCONF | UPF_BUGGY_UART | + UPF_SKIP_TEST, + }, +#endif + { }, +}; + +struct uartinit { + char *uart_ck_name; + u32 ck_mode_mask; + void __iomem *pdiv_clk_reg; +}; + +static struct uartinit uartinit_data[] __initdata = { +#ifdef CONFIG_ARCH_LPC32XX_UART5_SELECT + { + .uart_ck_name = "uart5_ck", + .ck_mode_mask = + LPC32XX_UART_CLKMODE_LOAD(LPC32XX_UART_CLKMODE_ON, 5), + .pdiv_clk_reg = LPC32XX_CLKPWR_UART5_CLK_CTRL, + }, +#endif +#ifdef CONFIG_ARCH_LPC32XX_UART3_SELECT + { + .uart_ck_name = "uart3_ck", + .ck_mode_mask = + LPC32XX_UART_CLKMODE_LOAD(LPC32XX_UART_CLKMODE_ON, 3), + .pdiv_clk_reg = LPC32XX_CLKPWR_UART3_CLK_CTRL, + }, +#endif +#ifdef CONFIG_ARCH_LPC32XX_UART4_SELECT + { + .uart_ck_name = "uart4_ck", + .ck_mode_mask = + LPC32XX_UART_CLKMODE_LOAD(LPC32XX_UART_CLKMODE_ON, 4), + .pdiv_clk_reg = LPC32XX_CLKPWR_UART4_CLK_CTRL, + }, +#endif +#ifdef CONFIG_ARCH_LPC32XX_UART6_SELECT + { + .uart_ck_name = "uart6_ck", + .ck_mode_mask = + LPC32XX_UART_CLKMODE_LOAD(LPC32XX_UART_CLKMODE_ON, 6), + .pdiv_clk_reg = LPC32XX_CLKPWR_UART6_CLK_CTRL, + }, +#endif +}; + +static struct platform_device serial_std_platform_device = { + .name = "serial8250", + .id = 0, + .dev = { + .platform_data = serial_std_platform_data, + }, +}; + +static struct platform_device *lpc32xx_serial_devs[] __initdata = { + &serial_std_platform_device, +}; + +void __init lpc32xx_serial_init(void) +{ + u32 tmp, clkmodes = 0; + struct clk *clk; + unsigned int puart; + int i, j; + + /* UART clocks are off, let clock driver manage them */ + __raw_writel(0, LPC32XX_CLKPWR_UART_CLK_CTRL); + + for (i = 0; i < ARRAY_SIZE(uartinit_data); i++) { + clk = clk_get(NULL, uartinit_data[i].uart_ck_name); + if (!IS_ERR(clk)) { + clk_enable(clk); + serial_std_platform_data[i].uartclk = + clk_get_rate(clk); + } + + /* Fall back on main osc rate if clock rate return fails */ + if (serial_std_platform_data[i].uartclk == 0) + serial_std_platform_data[i].uartclk = + LPC32XX_MAIN_OSC_FREQ; + + /* Setup UART clock modes for all UARTs, disable autoclock */ + clkmodes |= uartinit_data[i].ck_mode_mask; + + /* pre-UART clock divider set to 1 */ + __raw_writel(0x0101, uartinit_data[i].pdiv_clk_reg); + } + + /* This needs to be done after all UART clocks are setup */ + __raw_writel(clkmodes, LPC32XX_UARTCTL_CLKMODE); + for (i = 0; i < ARRAY_SIZE(uartinit_data) - 1; i++) { + /* Force a flush of the RX FIFOs to work around a HW bug */ + puart = serial_std_platform_data[i].mapbase; + __raw_writel(0xC1, LPC32XX_UART_IIR_FCR(puart)); + __raw_writel(0x00, LPC32XX_UART_DLL_FIFO(puart)); + j = LPC32XX_SUART_FIFO_SIZE; + while (j--) + tmp = __raw_readl(LPC32XX_UART_DLL_FIFO(puart)); + __raw_writel(0, LPC32XX_UART_IIR_FCR(puart)); + } + + /* Disable UART5->USB transparent mode or USB won't work */ + tmp = __raw_readl(LPC32XX_UARTCTL_CTRL); + tmp &= ~LPC32XX_UART_U5_ROUTE_TO_USB; + __raw_writel(tmp, LPC32XX_UARTCTL_CTRL); + + platform_add_devices(lpc32xx_serial_devs, + ARRAY_SIZE(lpc32xx_serial_devs)); +} diff --git a/arch/arm/mach-lpc32xx/suspend.S b/arch/arm/mach-lpc32xx/suspend.S new file mode 100644 index 00000000000..374f9f07fe4 --- /dev/null +++ b/arch/arm/mach-lpc32xx/suspend.S @@ -0,0 +1,151 @@ +/* + * arch/arm/mach-lpc32xx/suspend.S + * + * Original authors: Dmitry Chigirev, Vitaly Wool <source@mvista.com> + * Modified by Kevin Wells <kevin.wells@nxp.com> + * + * 2005 (c) MontaVista Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ +#include <linux/linkage.h> +#include <asm/assembler.h> +#include <mach/platform.h> +#include <mach/hardware.h> + +/* Using named register defines makes the code easier to follow */ +#define WORK1_REG r0 +#define WORK2_REG r1 +#define SAVED_HCLK_DIV_REG r2 +#define SAVED_HCLK_PLL_REG r3 +#define SAVED_DRAM_CLKCTRL_REG r4 +#define SAVED_PWR_CTRL_REG r5 +#define CLKPWRBASE_REG r6 +#define EMCBASE_REG r7 + +#define LPC32XX_EMC_STATUS_OFFS 0x04 +#define LPC32XX_EMC_STATUS_BUSY 0x1 +#define LPC32XX_EMC_STATUS_SELF_RFSH 0x4 + +#define LPC32XX_CLKPWR_PWR_CTRL_OFFS 0x44 +#define LPC32XX_CLKPWR_HCLK_DIV_OFFS 0x40 +#define LPC32XX_CLKPWR_HCLKPLL_CTRL_OFFS 0x58 + +#define CLKPWR_PCLK_DIV_MASK 0xFFFFFE7F + + .text + +ENTRY(lpc32xx_sys_suspend) + @ Save a copy of the used registers in IRAM, r0 is corrupted + adr r0, tmp_stack_end + stmfd r0!, {r3 - r7, sp, lr} + + @ Load a few common register addresses + adr WORK1_REG, reg_bases + ldr CLKPWRBASE_REG, [WORK1_REG, #0] + ldr EMCBASE_REG, [WORK1_REG, #4] + + ldr SAVED_PWR_CTRL_REG, [CLKPWRBASE_REG,\ + #LPC32XX_CLKPWR_PWR_CTRL_OFFS] + orr WORK1_REG, SAVED_PWR_CTRL_REG, #LPC32XX_CLKPWR_SDRAM_SELF_RFSH + + @ Wait for SDRAM busy status to go busy and then idle + @ This guarantees a small windows where DRAM isn't busy +1: + ldr WORK2_REG, [EMCBASE_REG, #LPC32XX_EMC_STATUS_OFFS] + and WORK2_REG, WORK2_REG, #LPC32XX_EMC_STATUS_BUSY + cmp WORK2_REG, #LPC32XX_EMC_STATUS_BUSY + bne 1b @ Branch while idle +2: + ldr WORK2_REG, [EMCBASE_REG, #LPC32XX_EMC_STATUS_OFFS] + and WORK2_REG, WORK2_REG, #LPC32XX_EMC_STATUS_BUSY + cmp WORK2_REG, #LPC32XX_EMC_STATUS_BUSY + beq 2b @ Branch until idle + + @ Setup self-refresh with support for manual exit of + @ self-refresh mode + str WORK1_REG, [CLKPWRBASE_REG, #LPC32XX_CLKPWR_PWR_CTRL_OFFS] + orr WORK2_REG, WORK1_REG, #LPC32XX_CLKPWR_UPD_SDRAM_SELF_RFSH + str WORK2_REG, [CLKPWRBASE_REG, #LPC32XX_CLKPWR_PWR_CTRL_OFFS] + str WORK1_REG, [CLKPWRBASE_REG, #LPC32XX_CLKPWR_PWR_CTRL_OFFS] + + @ Wait for self-refresh acknowledge, clocks to the DRAM device + @ will automatically stop on start of self-refresh +3: + ldr WORK2_REG, [EMCBASE_REG, #LPC32XX_EMC_STATUS_OFFS] + and WORK2_REG, WORK2_REG, #LPC32XX_EMC_STATUS_SELF_RFSH + cmp WORK2_REG, #LPC32XX_EMC_STATUS_SELF_RFSH + bne 3b @ Branch until self-refresh mode starts + + @ Enter direct-run mode from run mode + bic WORK1_REG, WORK1_REG, #LPC32XX_CLKPWR_SELECT_RUN_MODE + str WORK1_REG, [CLKPWRBASE_REG, #LPC32XX_CLKPWR_PWR_CTRL_OFFS] + + @ Safe disable of DRAM clock in EMC block, prevents DDR sync + @ issues on restart + ldr SAVED_HCLK_DIV_REG, [CLKPWRBASE_REG,\ + #LPC32XX_CLKPWR_HCLK_DIV_OFFS] + and WORK2_REG, SAVED_HCLK_DIV_REG, #CLKPWR_PCLK_DIV_MASK + str WORK2_REG, [CLKPWRBASE_REG, #LPC32XX_CLKPWR_HCLK_DIV_OFFS] + + @ Save HCLK PLL state and disable HCLK PLL + ldr SAVED_HCLK_PLL_REG, [CLKPWRBASE_REG,\ + #LPC32XX_CLKPWR_HCLKPLL_CTRL_OFFS] + bic WORK2_REG, SAVED_HCLK_PLL_REG, #LPC32XX_CLKPWR_HCLKPLL_POWER_UP + str WORK2_REG, [CLKPWRBASE_REG, #LPC32XX_CLKPWR_HCLKPLL_CTRL_OFFS] + + @ Enter stop mode until an enabled event occurs + orr WORK1_REG, WORK1_REG, #LPC32XX_CLKPWR_STOP_MODE_CTRL + str WORK1_REG, [CLKPWRBASE_REG, #LPC32XX_CLKPWR_PWR_CTRL_OFFS] + .rept 9 + nop + .endr + + @ Clear stop status + bic WORK1_REG, WORK1_REG, #LPC32XX_CLKPWR_STOP_MODE_CTRL + + @ Restore original HCLK PLL value and wait for PLL lock + str SAVED_HCLK_PLL_REG, [CLKPWRBASE_REG,\ + #LPC32XX_CLKPWR_HCLKPLL_CTRL_OFFS] +4: + ldr WORK2_REG, [CLKPWRBASE_REG, #LPC32XX_CLKPWR_HCLKPLL_CTRL_OFFS] + and WORK2_REG, WORK2_REG, #LPC32XX_CLKPWR_HCLKPLL_PLL_STS + bne 4b + + @ Re-enter run mode with self-refresh flag cleared, but no DRAM + @ update yet. DRAM is still in self-refresh + str SAVED_PWR_CTRL_REG, [CLKPWRBASE_REG,\ + #LPC32XX_CLKPWR_PWR_CTRL_OFFS] + + @ Restore original DRAM clock mode to restore DRAM clocks + str SAVED_HCLK_DIV_REG, [CLKPWRBASE_REG,\ + #LPC32XX_CLKPWR_HCLK_DIV_OFFS] + + @ Clear self-refresh mode + orr WORK1_REG, SAVED_PWR_CTRL_REG,\ + #LPC32XX_CLKPWR_UPD_SDRAM_SELF_RFSH + str WORK1_REG, [CLKPWRBASE_REG, #LPC32XX_CLKPWR_PWR_CTRL_OFFS] + str SAVED_PWR_CTRL_REG, [CLKPWRBASE_REG,\ + #LPC32XX_CLKPWR_PWR_CTRL_OFFS] + + @ Wait for EMC to clear self-refresh mode +5: + ldr WORK2_REG, [EMCBASE_REG, #LPC32XX_EMC_STATUS_OFFS] + and WORK2_REG, WORK2_REG, #LPC32XX_EMC_STATUS_SELF_RFSH + bne 5b @ Branch until self-refresh has exited + + @ restore regs and return + adr r0, tmp_stack + ldmfd r0!, {r3 - r7, sp, pc} + +reg_bases: + .long IO_ADDRESS(LPC32XX_CLK_PM_BASE) + .long IO_ADDRESS(LPC32XX_EMC_BASE) + +tmp_stack: + .long 0, 0, 0, 0, 0, 0, 0 +tmp_stack_end: + +ENTRY(lpc32xx_sys_suspend_sz) + .word . - lpc32xx_sys_suspend diff --git a/arch/arm/mach-lpc32xx/timer.c b/arch/arm/mach-lpc32xx/timer.c new file mode 100644 index 00000000000..630dd4a74b2 --- /dev/null +++ b/arch/arm/mach-lpc32xx/timer.c @@ -0,0 +1,182 @@ +/* + * arch/arm/mach-lpc32xx/timer.c + * + * Author: Kevin Wells <kevin.wells@nxp.com> + * + * Copyright (C) 2009 - 2010 NXP Semiconductors + * Copyright (C) 2009 Fontys University of Applied Sciences, Eindhoven + * Ed Schouten <e.schouten@fontys.nl> + * Laurens Timmermans <l.timmermans@fontys.nl> + * + * 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. + */ + +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/time.h> +#include <linux/err.h> +#include <linux/clockchips.h> + +#include <asm/mach/time.h> + +#include <mach/hardware.h> +#include <mach/platform.h> +#include "common.h" + +static cycle_t lpc32xx_clksrc_read(struct clocksource *cs) +{ + return (cycle_t)__raw_readl(LCP32XX_TIMER_TC(LPC32XX_TIMER1_BASE)); +} + +static struct clocksource lpc32xx_clksrc = { + .name = "lpc32xx_clksrc", + .shift = 24, + .rating = 300, + .read = lpc32xx_clksrc_read, + .mask = CLOCKSOURCE_MASK(32), + .flags = CLOCK_SOURCE_IS_CONTINUOUS, +}; + +static int lpc32xx_clkevt_next_event(unsigned long delta, + struct clock_event_device *dev) +{ + __raw_writel(LCP32XX_TIMER_CNTR_TCR_RESET, + LCP32XX_TIMER_TCR(LPC32XX_TIMER0_BASE)); + __raw_writel(delta, LCP32XX_TIMER_PR(LPC32XX_TIMER0_BASE)); + __raw_writel(LCP32XX_TIMER_CNTR_TCR_EN, + LCP32XX_TIMER_TCR(LPC32XX_TIMER0_BASE)); + + return 0; +} + +static void lpc32xx_clkevt_mode(enum clock_event_mode mode, + struct clock_event_device *dev) +{ + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + WARN_ON(1); + break; + + case CLOCK_EVT_MODE_ONESHOT: + case CLOCK_EVT_MODE_SHUTDOWN: + /* + * Disable the timer. When using oneshot, we must also + * disable the timer to wait for the first call to + * set_next_event(). + */ + __raw_writel(0, LCP32XX_TIMER_TCR(LPC32XX_TIMER0_BASE)); + break; + + case CLOCK_EVT_MODE_UNUSED: + case CLOCK_EVT_MODE_RESUME: + break; + } +} + +static struct clock_event_device lpc32xx_clkevt = { + .name = "lpc32xx_clkevt", + .features = CLOCK_EVT_FEAT_ONESHOT, + .shift = 32, + .rating = 300, + .set_next_event = lpc32xx_clkevt_next_event, + .set_mode = lpc32xx_clkevt_mode, +}; + +static irqreturn_t lpc32xx_timer_interrupt(int irq, void *dev_id) +{ + struct clock_event_device *evt = &lpc32xx_clkevt; + + /* Clear match */ + __raw_writel(LCP32XX_TIMER_CNTR_MTCH_BIT(0), + LCP32XX_TIMER_IR(LPC32XX_TIMER0_BASE)); + + evt->event_handler(evt); + + return IRQ_HANDLED; +} + +static struct irqaction lpc32xx_timer_irq = { + .name = "LPC32XX Timer Tick", + .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, + .handler = lpc32xx_timer_interrupt, +}; + +/* + * The clock management driver isn't initialized at this point, so the + * clocks need to be enabled here manually and then tagged as used in + * the clock driver initialization + */ +static void __init lpc32xx_timer_init(void) +{ + u32 clkrate, pllreg; + + /* Enable timer clock */ + __raw_writel(LPC32XX_CLKPWR_TMRPWMCLK_TIMER0_EN | + LPC32XX_CLKPWR_TMRPWMCLK_TIMER1_EN, + LPC32XX_CLKPWR_TIMERS_PWMS_CLK_CTRL_1); + + /* + * The clock driver isn't initialized at this point. So determine if + * the SYSCLK is driven from the PLL397 or main oscillator and then use + * it to compute the PLL frequency and the PCLK divider to get the base + * timer rates. This rate is needed to compute the tick rate. + */ + if (clk_is_sysclk_mainosc() != 0) + clkrate = LPC32XX_MAIN_OSC_FREQ; + else + clkrate = 397 * LPC32XX_CLOCK_OSC_FREQ; + + /* Get ARM HCLKPLL register and convert it into a frequency */ + pllreg = __raw_readl(LPC32XX_CLKPWR_HCLKPLL_CTRL) & 0x1FFFF; + clkrate = clk_get_pllrate_from_reg(clkrate, pllreg); + + /* Get PCLK divider and divide ARM PLL clock by it to get timer rate */ + clkrate = clkrate / clk_get_pclk_div(); + + /* Initial timer setup */ + __raw_writel(0, LCP32XX_TIMER_TCR(LPC32XX_TIMER0_BASE)); + __raw_writel(LCP32XX_TIMER_CNTR_MTCH_BIT(0), + LCP32XX_TIMER_IR(LPC32XX_TIMER0_BASE)); + __raw_writel(1, LCP32XX_TIMER_MR0(LPC32XX_TIMER0_BASE)); + __raw_writel(LCP32XX_TIMER_CNTR_MCR_MTCH(0) | + LCP32XX_TIMER_CNTR_MCR_STOP(0) | + LCP32XX_TIMER_CNTR_MCR_RESET(0), + LCP32XX_TIMER_MCR(LPC32XX_TIMER0_BASE)); + + /* Setup tick interrupt */ + setup_irq(IRQ_LPC32XX_TIMER0, &lpc32xx_timer_irq); + + /* Setup the clockevent structure. */ + lpc32xx_clkevt.mult = div_sc(clkrate, NSEC_PER_SEC, + lpc32xx_clkevt.shift); + lpc32xx_clkevt.max_delta_ns = clockevent_delta2ns(-1, + &lpc32xx_clkevt); + lpc32xx_clkevt.min_delta_ns = clockevent_delta2ns(1, + &lpc32xx_clkevt) + 1; + lpc32xx_clkevt.cpumask = cpumask_of(0); + clockevents_register_device(&lpc32xx_clkevt); + + /* Use timer1 as clock source. */ + __raw_writel(LCP32XX_TIMER_CNTR_TCR_RESET, + LCP32XX_TIMER_TCR(LPC32XX_TIMER1_BASE)); + __raw_writel(0, LCP32XX_TIMER_PR(LPC32XX_TIMER1_BASE)); + __raw_writel(0, LCP32XX_TIMER_MCR(LPC32XX_TIMER1_BASE)); + __raw_writel(LCP32XX_TIMER_CNTR_TCR_EN, + LCP32XX_TIMER_TCR(LPC32XX_TIMER1_BASE)); + lpc32xx_clksrc.mult = clocksource_hz2mult(clkrate, + lpc32xx_clksrc.shift); + clocksource_register(&lpc32xx_clksrc); +} + +struct sys_timer lpc32xx_timer = { + .init = &lpc32xx_timer_init, +}; + |