From mboxrd@z Thu Jan 1 00:00:00 1970 From: wellsk40@gmail.com (Kevin Wells) Date: Tue, 26 Jan 2010 16:19:51 -0800 Subject: [PATCH 03/10] ARM: LPC32XX: core architecture files Message-ID: <1264551591.6528.6@usb10132> To: linux-arm-kernel@lists.infradead.org List-Id: linux-arm-kernel.lists.infradead.org LPC32XX core architecture files Signed-off-by: Kevin Wells --- arch/arm/mach-lpc32xx/gpiolib.c | 455 +++++++++++++++++++++++++++++++++++++++ arch/arm/mach-lpc32xx/irq.c | 237 ++++++++++++++++++++ arch/arm/mach-lpc32xx/serial.c | 200 +++++++++++++++++ arch/arm/mach-lpc32xx/timer.c | 187 ++++++++++++++++ 4 files changed, 1079 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-lpc32xx/gpiolib.c b/arch/arm/mach-lpc32xx/gpiolib.c new file mode 100644 index 0000000..615b72f --- /dev/null +++ b/arch/arm/mach-lpc32xx/gpiolib.c @@ -0,0 +1,455 @@ +/* + * arch/arm/mach-lpc32xx/gpiolib.c + * + * Author: Kevin Wells + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include + +#include +#include + +#define GPIO_IOBASE io_p2v(GPIO_BASE) + +struct gpio_regs { + u32 inp_state; + u32 outp_set; + u32 outp_clr; + u32 dir_set; + u32 dir_clr; + int pgroup; +}; + +/* + * GPIO names + */ +static char *gpio_p0_names[GPIO_P0_MAX] = { + "p0.0", "p0.1", "p0.2", "p0.3", + "p0.4", "p0.5", "p0.6", "p0.7" +}; + +static char *gpio_p1_names[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 char *gpio_p2_names[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 char *gpio_p3_names[GPIO_P3_MAX] = { + "gpi000", "gpio01", "gpio02", "gpio03", + "gpio04", "gpio05" +}; + +static char *gpi_p3_names[GPI_P3_MAX] = { + "gpi00", "gpi01", "gpi02", "gpi03", + "gpi04", "gpi05", "gpi06", "gpi07", + "gpi08", "gpi09", "na", "na", + "na", "na", "na", "gpi15", + "gpi16", "gpi17", "gpi18", "gpi19", + "gpi20", "gpi21", "gpi22", "gpi23", + "gpi24", "gpi25", "gpi26", "gpi27" +}; + +static char *gpo_p3_names[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[] = { + { + .inp_state = GPIO_P0_INP_STATE(GPIO_IOBASE), + .outp_set = GPIO_P0_OUTP_SET(GPIO_IOBASE), + .outp_clr = GPIO_P0_OUTP_CLR(GPIO_IOBASE), + .dir_set = GPIO_P0_DIR_SET(GPIO_IOBASE), + .dir_clr = GPIO_P0_DIR_CLR(GPIO_IOBASE), + .pgroup = 0, + }, + { + .inp_state = GPIO_P1_INP_STATE(GPIO_IOBASE), + .outp_set = GPIO_P1_OUTP_SET(GPIO_IOBASE), + .outp_clr = GPIO_P1_OUTP_CLR(GPIO_IOBASE), + .dir_set = GPIO_P1_DIR_SET(GPIO_IOBASE), + .dir_clr = GPIO_P1_DIR_CLR(GPIO_IOBASE), + .pgroup = 1, + }, + { + .inp_state = GPIO_P2_INP_STATE(GPIO_IOBASE), + .outp_set = GPIO_P2_OUTP_SET(GPIO_IOBASE), + .outp_clr = GPIO_P2_OUTP_CLR(GPIO_IOBASE), + .dir_set = GPIO_P2_DIR_SET(GPIO_IOBASE), + .dir_clr = GPIO_P2_DIR_CLR(GPIO_IOBASE), + .pgroup = 2, + }, + { + .inp_state = GPIO_P3_INP_STATE(GPIO_IOBASE), + .outp_set = GPIO_P3_OUTP_SET(GPIO_IOBASE), + .outp_clr = GPIO_P3_OUTP_CLR(GPIO_IOBASE), + .dir_set = GPIO_P2_DIR_SET(GPIO_IOBASE), + .dir_clr = GPIO_P2_DIR_CLR(GPIO_IOBASE), + .pgroup = 3, + }, +}; + +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) + writel(1 << pin, group->gpio_grp->dir_clr); + else + writel(1 << pin, group->gpio_grp->dir_set); +} + +static void __set_gpio_dir_p3(struct lpc32xx_gpio_chip *group, + unsigned pin, int input) +{ + u32 u; + + /* P3 GPIO pins are offset in the register to pin mapping */ + u = (1 << (pin + 25)); + + if (input) + writel(u, group->gpio_grp->dir_clr); + else + writel(u, group->gpio_grp->dir_set); +} + +static void __set_gpio_level_p012(struct lpc32xx_gpio_chip *group, + unsigned pin, int high) +{ + if (high) + writel(1 << pin, group->gpio_grp->outp_set); + else + writel(1 << pin, group->gpio_grp->outp_clr); +} + +static void __set_gpio_level_p3(struct lpc32xx_gpio_chip *group, + unsigned pin, int high) +{ + u32 u; + + /* P3 GPIO pins are offset in the register to pin mapping */ + u = (1 << (pin + 25)); + + if (high) + writel(u, group->gpio_grp->outp_set); + else + writel(u, group->gpio_grp->outp_clr); +} + +static void __set_gpo_level_p3(struct lpc32xx_gpio_chip *group, + unsigned pin, int high) +{ + if (high) + writel(1 << pin, group->gpio_grp->outp_set); + else + writel(1 << pin, group->gpio_grp->outp_clr); +} + +static int __get_gpio_state_p012(struct lpc32xx_gpio_chip *group, + unsigned pin) +{ + int state; + + state = (int) readl(group->gpio_grp->inp_state); + state = (state >> pin) & 1; + + return state; +} + +static int __get_gpio_state_p3(struct lpc32xx_gpio_chip *group, + unsigned pin) +{ + int state; + + state = (int) readl(group->gpio_grp->inp_state); + + /* P3 GPIO pin input mapping is not contiguous */ + if (pin == 5) + state = (state >> 24) & 1; + else + state = (state >> (10 + pin)) & 1; + + return state; +} + +static int __get_gpi_state_p3(struct lpc32xx_gpio_chip *group, + unsigned pin) +{ + int state; + + state = (int) readl(group->gpio_grp->inp_state); + state = (state >> pin) & 1; + + return state; +} + +/* + * GENERIC_GPIO primitives. + */ +static int lpc32xx_gpio_dir_input(struct gpio_chip *chip, + unsigned pin) +{ + struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip); + + if (pin >= chip->ngpio) + return -EINVAL; + + if (group->gpio_grp->pgroup <= 2) + __set_gpio_dir_p012(group, pin, 1); + else + __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_dir_in_none(struct gpio_chip *chip, + unsigned pin) +{ + return -EINVAL; +} + +static int lpc32xx_gpio_get_value(struct gpio_chip *chip, unsigned pin) +{ + int val = 0; + struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip); + + if (pin < chip->ngpio) { + if (group->gpio_grp->pgroup <= 2) + val = __get_gpio_state_p012(group, pin); + else + val = __get_gpio_state_p3(group, pin); + } + + return val; +} + +static int lpc32xx_gpio_get_none(struct gpio_chip *chip, unsigned pin) +{ + return 0; +} + +static int lpc32xx_gpi_get_value(struct gpio_chip *chip, unsigned pin) +{ + int val = 0; + struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip); + + if (pin < chip->ngpio) + val = __get_gpi_state_p3(group, pin); + + return val; +} + +static int lpc32xx_gpio_dir_output(struct gpio_chip *chip, unsigned pin, + int value) +{ + struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip); + + if (pin >= chip->ngpio) + return -EINVAL; + + if (group->gpio_grp->pgroup <= 2) + __set_gpio_dir_p012(group, pin, 0); + else + __set_gpio_dir_p3(group, pin, 0); + + return 0; +} + +static int lpc32xx_gpio_dir_out_none(struct gpio_chip *chip, unsigned pin, + int value) +{ + return -EINVAL; +} + +static int lpc32xx_gpio_dir_out_always(struct gpio_chip *chip, unsigned pin, + int value) +{ + return 0; +} + +static void lpc32xx_gpio_set_value(struct gpio_chip *chip, unsigned pin, + int value) +{ + struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip); + + if (pin < chip->ngpio) { + if (group->gpio_grp->pgroup <= 2) + __set_gpio_level_p012(group, pin, value); + else + __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); + + if (pin < chip->ngpio) + __set_gpo_level_p3(group, pin, value); +} + +static void lpc32xx_gpo_set_none(struct gpio_chip *chip, unsigned pin, + int value) +{ + /* NULL function for GPI only pins */ +} + +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, + .get = lpc32xx_gpio_get_value, + .direction_output = lpc32xx_gpio_dir_output, + .set = lpc32xx_gpio_set_value, + .request = lpc32xx_gpio_request, + .base = GPIO_P0_GRP, + .ngpio = GPIO_P0_MAX, + .names = gpio_p0_names, + .can_sleep = 0, + }, + .gpio_grp = &gpio_grp_regs[0], + }, + { + .chip = { + .label = "gpio_p1", + .direction_input = lpc32xx_gpio_dir_input, + .get = lpc32xx_gpio_get_value, + .direction_output = lpc32xx_gpio_dir_output, + .set = lpc32xx_gpio_set_value, + .request = lpc32xx_gpio_request, + .base = GPIO_P1_GRP, + .ngpio = GPIO_P1_MAX, + .names = gpio_p1_names, + .can_sleep = 0, + }, + .gpio_grp = &gpio_grp_regs[1], + }, + { + .chip = { + .label = "gpio_p2", + .direction_input = lpc32xx_gpio_dir_input, + .get = lpc32xx_gpio_get_value, + .direction_output = lpc32xx_gpio_dir_output, + .set = lpc32xx_gpio_set_value, + .request = lpc32xx_gpio_request, + .base = GPIO_P2_GRP, + .ngpio = GPIO_P2_MAX, + .names = gpio_p2_names, + .can_sleep = 0, + }, + .gpio_grp = &gpio_grp_regs[2], + }, + { + .chip = { + .label = "gpio_p3", + .direction_input = lpc32xx_gpio_dir_input, + .get = lpc32xx_gpio_get_value, + .direction_output = lpc32xx_gpio_dir_output, + .set = lpc32xx_gpio_set_value, + .request = lpc32xx_gpio_request, + .base = GPIO_P3_GRP, + .ngpio = GPIO_P3_MAX, + .names = gpio_p3_names, + .can_sleep = 0, + }, + .gpio_grp = &gpio_grp_regs[3], + }, + { + .chip = { + .label = "gpi_p3", + .direction_input = lpc32xx_gpio_dir_in_always, + .get = lpc32xx_gpi_get_value, + .direction_output = lpc32xx_gpio_dir_out_none, + .set = lpc32xx_gpo_set_none, + .request = lpc32xx_gpio_request, + .base = GPI_P3_GRP, + .ngpio = GPI_P3_MAX, + .names = gpi_p3_names, + .can_sleep = 0, + }, + .gpio_grp = &gpio_grp_regs[3], + }, + { + .chip = { + .label = "gpo_p3", + .direction_input = lpc32xx_gpio_dir_in_none, + .get = lpc32xx_gpio_get_none, + .direction_output = lpc32xx_gpio_dir_out_always, + .set = lpc32xx_gpo_set_value, + .request = lpc32xx_gpio_request, + .base = GPO_P3_GRP, + .ngpio = GPO_P3_MAX, + .names = gpo_p3_names, + .can_sleep = 0, + }, + .gpio_grp = &gpio_grp_regs[3], + }, +}; + +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/irq.c b/arch/arm/mach-lpc32xx/irq.c new file mode 100644 index 0000000..8a1b57d --- /dev/null +++ b/arch/arm/mach-lpc32xx/irq.c @@ -0,0 +1,237 @@ +/* + * arch/arm/mach-lpc32xx/irq.c + * + * Author: Kevin Wells + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* + * Default value represeting 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 represeting the Activation Type of all internal + * interrupt sources. All are level senesitive. + */ +#define MIC_ATR_DEFAULT 0x00000000 +#define SIC1_ATR_DEFAULT 0x00026000 +#define SIC2_ATR_DEFAULT 0x00000000 + +static void get_controller(unsigned int irq, unsigned int *base, + unsigned int *irqbit) +{ + if (irq < 32) { + *base = io_p2v(MIC_BASE); + *irqbit = 1 << irq; + } else if (irq < 64) { + *base = io_p2v(SIC1_BASE); + *irqbit = 1 << (irq - 32); + } else { + *base = io_p2v(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 = readl(ctrl + INTC_MASK); + reg &= ~mask; + writel(reg, (ctrl + INTC_MASK)); +} + +static void lpc32xx_unmask_irq(unsigned int irq) +{ + unsigned int reg, ctrl, mask; + + get_controller(irq, &ctrl, &mask); + + reg = readl(ctrl + INTC_MASK); + reg |= mask; + writel(reg, (ctrl + INTC_MASK)); +} + +static void lpc32xx_mask_ack_irq(unsigned int irq) +{ + unsigned int ctrl, mask; + + get_controller(irq, &ctrl, &mask); + + writel(mask, (ctrl + INTC_RAW_STAT)); +} + +static int lpc32xx_set_irq_type(unsigned int irq, unsigned int type) +{ + unsigned int reg, ctrl, mask; + + get_controller(irq, &ctrl, &mask); + + switch (type) { + case IRQ_TYPE_EDGE_RISING: + /* Rising edge sensitive */ + reg = readl(ctrl + INTC_POLAR); + reg |= mask; + writel(reg, (ctrl + INTC_POLAR)); + reg = readl(ctrl + INTC_ACT_TYPE); + reg |= mask; + writel(reg, (ctrl + INTC_ACT_TYPE)); + set_irq_handler(irq, handle_edge_irq); + break; + + case IRQ_TYPE_EDGE_FALLING: + /* Falling edge sensitive */ + reg = readl(ctrl + INTC_POLAR); + reg &= ~mask; + writel(reg, (ctrl + INTC_POLAR)); + reg = readl(ctrl + INTC_ACT_TYPE); + reg |= mask; + writel(reg, (ctrl + INTC_ACT_TYPE)); + set_irq_handler(irq, handle_edge_irq); + break; + + case IRQ_TYPE_LEVEL_LOW: + /* Low level sensitive */ + reg = readl(ctrl + INTC_POLAR); + reg &= ~mask; + writel(reg, (ctrl + INTC_POLAR)); + reg = readl(ctrl + INTC_ACT_TYPE); + reg &= ~mask; + writel(reg, (ctrl + INTC_ACT_TYPE)); + set_irq_handler(irq, handle_level_irq); + break; + + case IRQ_TYPE_LEVEL_HIGH: + /* High level sensitive */ + reg = readl(ctrl + INTC_POLAR); + reg |= mask; + writel(reg, (ctrl + INTC_POLAR)); + reg = readl(ctrl + INTC_ACT_TYPE); + reg &= ~mask; + writel(reg, (ctrl + INTC_ACT_TYPE)); + set_irq_handler(irq, handle_level_irq); + break; + + /* Other modes are not supported */ + default: + return -ENOTSUPP; + } + + return 0; +} + +static void __init lpc32xx_set_default_mappings(unsigned int base, + unsigned int apr, unsigned int atr, unsigned int offset) +{ + unsigned int i, lvl, type; + + /* Set activation levels for each interrupt */ + i = 0; + while (i < 32) { + lvl = ((apr >> i) & 0x1) | (((atr >> i) & 0x1) << 1); + switch (lvl) { + case 0x0: /* Low polarity and level operation */ + type = IRQ_TYPE_LEVEL_LOW; + break; + + case 0x1: /* High polarity and level operation */ + type = IRQ_TYPE_LEVEL_HIGH; + break; + + case 0x2: /* Low polarity and edge operation */ + type = IRQ_TYPE_EDGE_FALLING; + break; + + case 0x3: /* High polarity and edge operation */ + default: + type = IRQ_TYPE_EDGE_RISING; + break; + } + + lpc32xx_set_irq_type((offset + i), type); + i++; + } +} + +static struct irq_chip lpc32xx_irq_chip = { + .ack = lpc32xx_mask_ack_irq, + .mask = lpc32xx_mask_irq, + .unmask = lpc32xx_unmask_irq, + .set_type = lpc32xx_set_irq_type, +}; + +void __init lpc32xx_init_irq(void) +{ + unsigned int i, vloc; + + /* Setup MIC */ + vloc = io_p2v(MIC_BASE); + writel(0, (vloc + INTC_MASK)); + writel(MIC_APR_DEFAULT, (vloc + INTC_POLAR)); + writel(MIC_ATR_DEFAULT, (vloc + INTC_ACT_TYPE)); + + /* Setup SIC1 */ + vloc = io_p2v(SIC1_BASE); + writel(0, (vloc + INTC_MASK)); + writel(SIC1_APR_DEFAULT, (vloc + INTC_POLAR)); + writel(SIC1_ATR_DEFAULT, (vloc + INTC_ACT_TYPE)); + + /* Setup SIC2 */ + vloc = io_p2v(SIC2_BASE); + writel(0, (vloc + INTC_MASK)); + writel(SIC2_APR_DEFAULT, (vloc + INTC_POLAR)); + writel(SIC2_ATR_DEFAULT, (vloc + INTC_ACT_TYPE)); + + /* Configure supported IRQ's */ + for (i = 0; i < NR_IRQS; i++) { + set_irq_flags(i, IRQF_VALID); + set_irq_chip(i, &lpc32xx_irq_chip); + } + + /* Set default mappings */ + lpc32xx_set_default_mappings(io_p2v(MIC_BASE), MIC_APR_DEFAULT, + MIC_ATR_DEFAULT, 0); + lpc32xx_set_default_mappings(io_p2v(SIC1_BASE), SIC1_APR_DEFAULT, + SIC1_ATR_DEFAULT, 32); + lpc32xx_set_default_mappings(io_p2v(SIC2_BASE), SIC2_APR_DEFAULT, + SIC2_ATR_DEFAULT, 64); + + /* mask all interrupts except SUBIRQA and SUBFIQ */ + writel((1 << IRQ_SUB1IRQ) | (1 << IRQ_SUB2IRQ) | + (1 << IRQ_SUB1FIQ) | (1 << IRQ_SUB2FIQ), + (io_p2v(MIC_BASE) + INTC_MASK)); + writel(0, (io_p2v(SIC1_BASE) + INTC_MASK)); + writel(0, (io_p2v(SIC2_BASE) + INTC_MASK)); +} diff --git a/arch/arm/mach-lpc32xx/serial.c b/arch/arm/mach-lpc32xx/serial.c new file mode 100644 index 0000000..1c15121 --- /dev/null +++ b/arch/arm/mach-lpc32xx/serial.c @@ -0,0 +1,200 @@ +/* + * arch/arm/mach-lpc32xx/serial.c + * + * Author: Kevin Wells + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "common.h" + +/* Standard 8250/16550 compatible serial ports */ +static struct plat_serial8250_port serial_std_platform_data[] = { +#ifdef CONFIG_ARCH_LPC32XX_UART5_ENABLE + { + .membase = (void *) io_p2v(UART5_BASE), + .mapbase = UART5_BASE, + .irq = IRQ_UART_IIR5, + .uartclk = MAIN_OSC_FREQ, + .regshift = 2, + .iotype = UPIO_MEM32, + .flags = UPF_BOOT_AUTOCONF | UPF_BUGGY_UART | + UPF_SKIP_TEST, + }, +#endif +#ifdef CONFIG_ARCH_LPC32XX_UART3_ENABLE + { + .membase = (void *) io_p2v(UART3_BASE), + .mapbase = UART3_BASE, + .irq = IRQ_UART_IIR3, + .uartclk = MAIN_OSC_FREQ, + .regshift = 2, + .iotype = UPIO_MEM32, + .flags = UPF_BOOT_AUTOCONF | UPF_BUGGY_UART | + UPF_SKIP_TEST, + }, +#endif +#ifdef CONFIG_ARCH_LPC32XX_UART4_ENABLE + { + .membase = (void *) io_p2v(UART4_BASE), + .mapbase = UART4_BASE, + .irq = IRQ_UART_IIR4, + .uartclk = MAIN_OSC_FREQ, + .regshift = 2, + .iotype = UPIO_MEM32, + .flags = UPF_BOOT_AUTOCONF | UPF_BUGGY_UART | + UPF_SKIP_TEST, + }, +#endif +#ifdef CONFIG_ARCH_LPC32XX_UART6_ENABLE + { + .membase = (void *) io_p2v(UART6_BASE), + .mapbase = UART6_BASE, + .irq = IRQ_UART_IIR6, + .uartclk = 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; + u32 pdiv_clk_reg; +}; + +static struct uartinit uartinit_data[] __initdata = { +#ifdef CONFIG_ARCH_LPC32XX_UART5_ENABLE + { + .uart_ck_name = "uart5_ck", + .ck_mode_mask = UART_CLKMODE_LOAD(UART_CLKMODE_ON, 5), + .pdiv_clk_reg = CLKPWR_UART5_CLK_CTRL(CLKPWR_IOBASE), + }, +#endif +#ifdef CONFIG_ARCH_LPC32XX_UART3_ENABLE + { + .uart_ck_name = "uart3_ck", + .ck_mode_mask = UART_CLKMODE_LOAD(UART_CLKMODE_ON, 3), + .pdiv_clk_reg = CLKPWR_UART3_CLK_CTRL(CLKPWR_IOBASE), + }, +#endif +#ifdef CONFIG_ARCH_LPC32XX_UART4_ENABLE + { + .uart_ck_name = "uart4_ck", + .ck_mode_mask = UART_CLKMODE_LOAD(UART_CLKMODE_ON, 4), + .pdiv_clk_reg = CLKPWR_UART4_CLK_CTRL(CLKPWR_IOBASE), + }, +#endif +#ifdef CONFIG_ARCH_LPC32XX_UART6_ENABLE + { + .uart_ck_name = "uart6_ck", + .ck_mode_mask = UART_CLKMODE_LOAD(UART_CLKMODE_ON, 6), + .pdiv_clk_reg = CLKPWR_UART6_CLK_CTRL(CLKPWR_IOBASE), + }, +#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; + void *puart; + int i; + + /* UART clocks are off, let clock driver manage them */ + __raw_writel(0, CLKPWR_UART_CLK_CTRL(CLKPWR_IOBASE)); + + for (i = 0; i < ARRAY_SIZE(uartinit_data); i++) { + clk = clk_get(NULL, uartinit_data[i].uart_ck_name); + if (IS_ERR(clk)) { +#ifdef CONFIG_DEBUG_LL + /* A clock get failure can mean the console might + not work. It's possible this might not even + work. */ + printascii("Serial port clock get failure!\n"); +#endif + } else { + clk_enable(clk); + serial_std_platform_data[i].uartclk = + clk_get_rate(clk); + } + + /* Setup UART clock modes for all UARTs, disable autoclock */ + clkmodes |= uartinit_data[i].ck_mode_mask; + + /* pre-UART clock divider set to 1 */ + writel(0x0101, uartinit_data[i].pdiv_clk_reg); + } + + /* This needs to be done after all UART clocks are setup */ + writel(clkmodes, UARTCTL_CLKMODE(io_p2v(UART_CTRL_BASE))); + 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].membase; + writel(0xC1, UART_IIR_FCR(puart)); + writel(0x00, UART_DLL_FIFO(puart)); + clkmodes = 64; + while (clkmodes--) + tmp = readl(UART_DLL_FIFO(puart)); + writel(0, UART_IIR_FCR(puart)); + } + + /* IrDA pulsing support on UART6. This only enables the IrDA mux */ + tmp = readl(UARTCTL_CTRL(io_p2v(UART_CTRL_BASE))); +#ifdef CONFIG_ARCH_LPC32XX_UART6_IRDAMODE + tmp &= ~UART_UART6_IRDAMOD_BYPASS; +#else + tmp |= UART_UART6_IRDAMOD_BYPASS; +#endif + writel(tmp, UARTCTL_CTRL(io_p2v(UART_CTRL_BASE))); + + /* Disable UART5->USB transparent mode or USB won't work */ + tmp = readl(UARTCTL_CTRL(io_p2v(UART_CTRL_BASE))); + tmp &= ~UART_U5_ROUTE_TO_USB; + writel(tmp, UARTCTL_CTRL(io_p2v(UART_CTRL_BASE))); + + platform_add_devices(lpc32xx_serial_devs, + ARRAY_SIZE(lpc32xx_serial_devs)); +} diff --git a/arch/arm/mach-lpc32xx/timer.c b/arch/arm/mach-lpc32xx/timer.c new file mode 100644 index 0000000..9c06346 --- /dev/null +++ b/arch/arm/mach-lpc32xx/timer.c @@ -0,0 +1,187 @@ +/* + * arch/arm/mach-lpc32xx/timer.c + * + * Author: Kevin Wells + * + * Copyright (C) 2009 - 2010 NXP Semiconductors + * Copyright (C) 2009 Fontys University of Applied Sciences, Eindhoven + * Ed Schouten + * Laurens Timmermans + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include "common.h" + +#define TIMER0_IOBASE io_p2v(TIMER0_BASE) +#define TIMER1_IOBASE io_p2v(TIMER1_BASE) + +static cycle_t lpc32xx_clksrc_read(struct clocksource *cs) +{ + return (cycle_t)readl(TIMER_TC(TIMER1_IOBASE)); +} + +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) +{ + unsigned long flags; + + if (delta < 1) + return -ETIME; + + local_irq_save(flags); + + writel(TIMER_CNTR_TCR_RESET, TIMER_TCR(TIMER0_IOBASE)); + writel(delta, TIMER_PR(TIMER0_IOBASE)); + writel(TIMER_CNTR_TCR_EN, TIMER_TCR(TIMER0_IOBASE)); + + local_irq_restore(flags); + + 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(). + */ + writel(0, TIMER_TCR(TIMER0_IOBASE)); + 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 */ + writel(TIMER_CNTR_MTCH_BIT(0), TIMER_IR(TIMER0_IOBASE)); + + 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 */ + writel( + (CLKPWR_TMRPWMCLK_TIMER0_EN | CLKPWR_TMRPWMCLK_TIMER1_EN), + CLKPWR_TIMERS_PWMS_CLK_CTRL_1(CLKPWR_IOBASE)); + + /* 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 = MAIN_OSC_FREQ; + else + clkrate = 397 * CLOCK_OSC_FREQ; + + /* Get ARM HCLKPLL register and convert it into a frequency*/ + pllreg = readl(CLKPWR_HCLKPLL_CTRL(CLKPWR_IOBASE)) & 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 */ + writel(0, TIMER_TCR(TIMER0_IOBASE)); + writel(TIMER_CNTR_MTCH_BIT(0), TIMER_IR(TIMER0_IOBASE)); + writel(1, TIMER_MR0(TIMER0_IOBASE)); + writel(TIMER_CNTR_MCR_MTCH(0) | TIMER_CNTR_MCR_STOP(0) | + TIMER_CNTR_MCR_RESET(0), TIMER_MCR(TIMER0_IOBASE)); + + /* Setup tick interrupt */ + setup_irq(IRQ_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); + lpc32xx_clkevt.cpumask = cpumask_of(0); + clockevents_register_device(&lpc32xx_clkevt); + + /* Use timer1 as clock source. */ + writel(TIMER_CNTR_TCR_RESET, TIMER_TCR(TIMER1_IOBASE)); + writel(0, TIMER_PR(TIMER1_IOBASE)); + writel(0, TIMER_MCR(TIMER1_IOBASE)); + writel(TIMER_CNTR_TCR_EN, TIMER_TCR(TIMER1_IOBASE)); + lpc32xx_clksrc.mult = clocksource_hz2mult(clkrate, + lpc32xx_clksrc.shift); + clocksource_register(&lpc32xx_clksrc); +} + +struct sys_timer lpc32xx_timer = { + .init = &lpc32xx_timer_init, +}; + -- 1.6.6