diff options
Diffstat (limited to 'arch/arm/mach-imx')
-rw-r--r-- | arch/arm/mach-imx/Kconfig | 10 | ||||
-rw-r--r-- | arch/arm/mach-imx/Makefile | 19 | ||||
-rw-r--r-- | arch/arm/mach-imx/Makefile.boot | 2 | ||||
-rw-r--r-- | arch/arm/mach-imx/dma.c | 203 | ||||
-rw-r--r-- | arch/arm/mach-imx/generic.c | 274 | ||||
-rw-r--r-- | arch/arm/mach-imx/generic.h | 16 | ||||
-rw-r--r-- | arch/arm/mach-imx/irq.c | 252 | ||||
-rw-r--r-- | arch/arm/mach-imx/leds-mx1ads.c | 54 | ||||
-rw-r--r-- | arch/arm/mach-imx/leds.c | 31 | ||||
-rw-r--r-- | arch/arm/mach-imx/leds.h | 9 | ||||
-rw-r--r-- | arch/arm/mach-imx/mx1ads.c | 88 | ||||
-rw-r--r-- | arch/arm/mach-imx/time.c | 101 |
12 files changed, 1059 insertions, 0 deletions
diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig new file mode 100644 index 00000000000..ec85813ee5d --- /dev/null +++ b/arch/arm/mach-imx/Kconfig @@ -0,0 +1,10 @@ +menu "IMX Implementations" + depends on ARCH_IMX + +config ARCH_MX1ADS + bool "mx1ads" + depends on ARCH_IMX + help + Say Y here if you are using the Motorola MX1ADS board + +endmenu diff --git a/arch/arm/mach-imx/Makefile b/arch/arm/mach-imx/Makefile new file mode 100644 index 00000000000..0b27d79f2ef --- /dev/null +++ b/arch/arm/mach-imx/Makefile @@ -0,0 +1,19 @@ +# +# Makefile for the linux kernel. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). + +# Object file lists. + +obj-y += irq.o time.o dma.o generic.o + +# Specific board support +obj-$(CONFIG_ARCH_MX1ADS) += mx1ads.o + +# Support for blinky lights +led-y := leds.o + +obj-$(CONFIG_LEDS) += $(led-y) +led-$(CONFIG_ARCH_MX1ADS) += leds-mx1ads.o diff --git a/arch/arm/mach-imx/Makefile.boot b/arch/arm/mach-imx/Makefile.boot new file mode 100644 index 00000000000..fd72ce5b808 --- /dev/null +++ b/arch/arm/mach-imx/Makefile.boot @@ -0,0 +1,2 @@ + zreladdr-$(CONFIG_ARCH_MX1ADS) := 0x08008000 + diff --git a/arch/arm/mach-imx/dma.c b/arch/arm/mach-imx/dma.c new file mode 100644 index 00000000000..71a59e19616 --- /dev/null +++ b/arch/arm/mach-imx/dma.c @@ -0,0 +1,203 @@ +/* + * linux/arch/arm/mach-imx/dma.c + * + * imx DMA registration and IRQ dispatching + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 03/03/2004 Sascha Hauer <sascha@saschahauer.de> + * initial version heavily inspired by + * linux/arch/arm/mach-pxa/dma.c + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/interrupt.h> +#include <linux/errno.h> + +#include <asm/system.h> +#include <asm/irq.h> +#include <asm/hardware.h> +#include <asm/dma.h> + +static struct dma_channel { + char *name; + void (*irq_handler) (int, void *, struct pt_regs *); + void (*err_handler) (int, void *, struct pt_regs *); + void *data; +} dma_channels[11]; + +/* set err_handler to NULL to have the standard info-only error handler */ +int +imx_request_dma(char *name, imx_dma_prio prio, + void (*irq_handler) (int, void *, struct pt_regs *), + void (*err_handler) (int, void *, struct pt_regs *), void *data) +{ + unsigned long flags; + int i, found = 0; + + /* basic sanity checks */ + if (!name || !irq_handler) + return -EINVAL; + + local_irq_save(flags); + + /* try grabbing a DMA channel with the requested priority */ + for (i = prio; i < prio + (prio == DMA_PRIO_LOW) ? 8 : 4; i++) { + if (!dma_channels[i].name) { + found = 1; + break; + } + } + + if (!found) { + /* requested prio group is full, try hier priorities */ + for (i = prio - 1; i >= 0; i--) { + if (!dma_channels[i].name) { + found = 1; + break; + } + } + } + + if (found) { + DIMR &= ~(1 << i); + dma_channels[i].name = name; + dma_channels[i].irq_handler = irq_handler; + dma_channels[i].err_handler = err_handler; + dma_channels[i].data = data; + } else { + printk(KERN_WARNING "No more available DMA channels for %s\n", + name); + i = -ENODEV; + } + + local_irq_restore(flags); + return i; +} + +void +imx_free_dma(int dma_ch) +{ + unsigned long flags; + + if (!dma_channels[dma_ch].name) { + printk(KERN_CRIT + "%s: trying to free channel %d which is already freed\n", + __FUNCTION__, dma_ch); + return; + } + + local_irq_save(flags); + DIMR &= ~(1 << dma_ch); + dma_channels[dma_ch].name = NULL; + local_irq_restore(flags); +} + +static irqreturn_t +dma_err_handler(int irq, void *dev_id, struct pt_regs *regs) +{ + int i, disr = DISR; + struct dma_channel *channel; + unsigned int err_mask = DBTOSR | DRTOSR | DSESR | DBOSR; + + DISR = disr; + for (i = 0; i < 11; i++) { + channel = &dma_channels[i]; + + if ( (err_mask & 1<<i) && channel->name && channel->err_handler) { + channel->err_handler(i, channel->data, regs); + continue; + } + + if (DBTOSR & (1 << i)) { + printk(KERN_WARNING + "Burst timeout on channel %d (%s)\n", + i, channel->name); + DBTOSR |= (1 << i); + } + if (DRTOSR & (1 << i)) { + printk(KERN_WARNING + "Request timeout on channel %d (%s)\n", + i, channel->name); + DRTOSR |= (1 << i); + } + if (DSESR & (1 << i)) { + printk(KERN_WARNING + "Transfer timeout on channel %d (%s)\n", + i, channel->name); + DSESR |= (1 << i); + } + if (DBOSR & (1 << i)) { + printk(KERN_WARNING + "Buffer overflow timeout on channel %d (%s)\n", + i, channel->name); + DBOSR |= (1 << i); + } + } + return IRQ_HANDLED; +} + +static irqreturn_t +dma_irq_handler(int irq, void *dev_id, struct pt_regs *regs) +{ + int i, disr = DISR; + + DISR = disr; + for (i = 0; i < 11; i++) { + if (disr & (1 << i)) { + struct dma_channel *channel = &dma_channels[i]; + if (channel->name && channel->irq_handler) { + channel->irq_handler(i, channel->data, regs); + } else { + /* + * IRQ for an unregistered DMA channel: + * let's clear the interrupts and disable it. + */ + printk(KERN_WARNING + "spurious IRQ for DMA channel %d\n", i); + } + } + } + return IRQ_HANDLED; +} + +static int __init +imx_dma_init(void) +{ + int ret; + + /* reset DMA module */ + DCR = DCR_DRST; + + ret = request_irq(DMA_INT, dma_irq_handler, 0, "DMA", NULL); + if (ret) { + printk(KERN_CRIT "Wow! Can't register IRQ for DMA\n"); + return ret; + } + + ret = request_irq(DMA_ERR, dma_err_handler, 0, "DMA", NULL); + if (ret) { + printk(KERN_CRIT "Wow! Can't register ERRIRQ for DMA\n"); + free_irq(DMA_INT, NULL); + } + + /* enable DMA module */ + DCR = DCR_DEN; + + /* clear all interrupts */ + DISR = 0x3ff; + + /* enable interrupts */ + DIMR = 0; + + return ret; +} + +arch_initcall(imx_dma_init); + +EXPORT_SYMBOL(imx_request_dma); +EXPORT_SYMBOL(imx_free_dma); diff --git a/arch/arm/mach-imx/generic.c b/arch/arm/mach-imx/generic.c new file mode 100644 index 00000000000..54377d0f578 --- /dev/null +++ b/arch/arm/mach-imx/generic.c @@ -0,0 +1,274 @@ +/* + * arch/arm/mach-imx/generic.c + * + * author: Sascha Hauer + * Created: april 20th, 2004 + * Copyright: Synertronixx GmbH + * + * Common code for i.MX machines + * + * 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 <linux/device.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <asm/hardware.h> + +#include <asm/mach/map.h> + +void imx_gpio_mode(int gpio_mode) +{ + unsigned int pin = gpio_mode & GPIO_PIN_MASK; + unsigned int port = (gpio_mode & GPIO_PORT_MASK) >> 5; + unsigned int ocr = (gpio_mode & GPIO_OCR_MASK) >> 10; + unsigned int tmp; + + /* Pullup enable */ + if(gpio_mode & GPIO_PUEN) + PUEN(port) |= (1<<pin); + else + PUEN(port) &= ~(1<<pin); + + /* Data direction */ + if(gpio_mode & GPIO_OUT) + DDIR(port) |= 1<<pin; + else + DDIR(port) &= ~(1<<pin); + + /* Primary / alternate function */ + if(gpio_mode & GPIO_AF) + GPR(port) |= (1<<pin); + else + GPR(port) &= ~(1<<pin); + + /* use as gpio? */ + if( ocr == 3 ) + GIUS(port) |= (1<<pin); + else + GIUS(port) &= ~(1<<pin); + + /* Output / input configuration */ + /* FIXME: I'm not very sure about OCR and ICONF, someone + * should have a look over it + */ + if(pin<16) { + tmp = OCR1(port); + tmp &= ~( 3<<(pin*2)); + tmp |= (ocr << (pin*2)); + OCR1(port) = tmp; + + if( gpio_mode & GPIO_AOUT ) + ICONFA1(port) &= ~( 3<<(pin*2)); + if( gpio_mode & GPIO_BOUT ) + ICONFB1(port) &= ~( 3<<(pin*2)); + } else { + tmp = OCR2(port); + tmp &= ~( 3<<((pin-16)*2)); + tmp |= (ocr << ((pin-16)*2)); + OCR2(port) = tmp; + + if( gpio_mode & GPIO_AOUT ) + ICONFA2(port) &= ~( 3<<((pin-16)*2)); + if( gpio_mode & GPIO_BOUT ) + ICONFB2(port) &= ~( 3<<((pin-16)*2)); + } +} + +EXPORT_SYMBOL(imx_gpio_mode); + +/* + * get the system pll clock in Hz + * + * mfi + mfn / (mfd +1) + * f = 2 * f_ref * -------------------- + * pd + 1 + */ +static unsigned int imx_decode_pll(unsigned int pll) +{ + u32 mfi = (pll >> 10) & 0xf; + u32 mfn = pll & 0x3ff; + u32 mfd = (pll >> 16) & 0x3ff; + u32 pd = (pll >> 26) & 0xf; + u32 f_ref = (CSCR & CSCR_SYSTEM_SEL) ? 16000000 : (CLK32 * 512); + + mfi = mfi <= 5 ? 5 : mfi; + + return (2 * (f_ref>>10) * ( (mfi<<10) + (mfn<<10) / (mfd+1) )) / (pd+1); +} + +unsigned int imx_get_system_clk(void) +{ + return imx_decode_pll(SPCTL0); +} +EXPORT_SYMBOL(imx_get_system_clk); + +unsigned int imx_get_mcu_clk(void) +{ + return imx_decode_pll(MPCTL0); +} +EXPORT_SYMBOL(imx_get_mcu_clk); + +/* + * get peripheral clock 1 ( UART[12], Timer[12], PWM ) + */ +unsigned int imx_get_perclk1(void) +{ + return imx_get_system_clk() / (((PCDR) & 0xf)+1); +} +EXPORT_SYMBOL(imx_get_perclk1); + +/* + * get peripheral clock 2 ( LCD, SD, SPI[12] ) + */ +unsigned int imx_get_perclk2(void) +{ + return imx_get_system_clk() / (((PCDR>>4) & 0xf)+1); +} +EXPORT_SYMBOL(imx_get_perclk2); + +/* + * get peripheral clock 3 ( SSI ) + */ +unsigned int imx_get_perclk3(void) +{ + return imx_get_system_clk() / (((PCDR>>16) & 0x7f)+1); +} +EXPORT_SYMBOL(imx_get_perclk3); + +/* + * get hclk ( SDRAM, CSI, Memory Stick, I2C, DMA ) + */ +unsigned int imx_get_hclk(void) +{ + return imx_get_system_clk() / (((CSCR>>10) & 0xf)+1); +} +EXPORT_SYMBOL(imx_get_hclk); + +static struct resource imx_mmc_resources[] = { + [0] = { + .start = 0x00214000, + .end = 0x002140FF, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = (SDHC_INT), + .end = (SDHC_INT), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device imx_mmc_device = { + .name = "imx-mmc", + .id = 0, + .num_resources = ARRAY_SIZE(imx_mmc_resources), + .resource = imx_mmc_resources, +}; + +static struct resource imx_uart1_resources[] = { + [0] = { + .start = 0x00206000, + .end = 0x002060FF, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = (UART1_MINT_RX), + .end = (UART1_MINT_RX), + .flags = IORESOURCE_IRQ, + }, + [2] = { + .start = (UART1_MINT_TX), + .end = (UART1_MINT_TX), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device imx_uart1_device = { + .name = "imx-uart", + .id = 0, + .num_resources = ARRAY_SIZE(imx_uart1_resources), + .resource = imx_uart1_resources, +}; + +static struct resource imx_uart2_resources[] = { + [0] = { + .start = 0x00207000, + .end = 0x002070FF, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = (UART2_MINT_RX), + .end = (UART2_MINT_RX), + .flags = IORESOURCE_IRQ, + }, + [2] = { + .start = (UART2_MINT_TX), + .end = (UART2_MINT_TX), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device imx_uart2_device = { + .name = "imx-uart", + .id = 1, + .num_resources = ARRAY_SIZE(imx_uart2_resources), + .resource = imx_uart2_resources, +}; + +static struct resource imxfb_resources[] = { + [0] = { + .start = 0x00205000, + .end = 0x002050FF, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = LCDC_INT, + .end = LCDC_INT, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device imxfb_device = { + .name = "imx-fb", + .id = 0, + .num_resources = ARRAY_SIZE(imxfb_resources), + .resource = imxfb_resources, +}; + +static struct platform_device *devices[] __initdata = { + &imx_mmc_device, + &imxfb_device, + &imx_uart1_device, + &imx_uart2_device, +}; + +static struct map_desc imx_io_desc[] __initdata = { + /* virtual physical length type */ + {IMX_IO_BASE, IMX_IO_PHYS, IMX_IO_SIZE, MT_DEVICE}, +}; + +void __init +imx_map_io(void) +{ + iotable_init(imx_io_desc, ARRAY_SIZE(imx_io_desc)); +} + +static int __init imx_init(void) +{ + return platform_add_devices(devices, ARRAY_SIZE(devices)); +} + +subsys_initcall(imx_init); diff --git a/arch/arm/mach-imx/generic.h b/arch/arm/mach-imx/generic.h new file mode 100644 index 00000000000..e91003e4bef --- /dev/null +++ b/arch/arm/mach-imx/generic.h @@ -0,0 +1,16 @@ +/* + * linux/arch/arm/mach-imx/generic.h + * + * Author: Sascha Hauer <sascha@saschahauer.de> + * Copyright: Synertronixx GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +extern void __init imx_map_io(void); +extern void __init imx_init_irq(void); + +struct sys_timer; +extern struct sys_timer imx_timer; diff --git a/arch/arm/mach-imx/irq.c b/arch/arm/mach-imx/irq.c new file mode 100644 index 00000000000..0c2713426df --- /dev/null +++ b/arch/arm/mach-imx/irq.c @@ -0,0 +1,252 @@ +/* + * linux/arch/arm/mach-imx/irq.c + * + * Copyright (C) 1999 ARM Limited + * Copyright (C) 2002 Shane Nay (shane@minirl.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. + * + * 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 + * + * 03/03/2004 Sascha Hauer <sascha@saschahauer.de> + * Copied from the motorola bsp package and added gpio demux + * interrupt handler + */ + +#include <linux/init.h> +#include <linux/list.h> +#include <linux/timer.h> + +#include <asm/hardware.h> +#include <asm/irq.h> +#include <asm/io.h> + +#include <asm/mach/irq.h> + +/* + * + * We simply use the ENABLE DISABLE registers inside of the IMX + * to turn on/off specific interrupts. FIXME- We should + * also add support for the accelerated interrupt controller + * by putting offets to irq jump code in the appropriate + * places. + * + */ + +#define INTENNUM_OFF 0x8 +#define INTDISNUM_OFF 0xC + +#define VA_AITC_BASE IO_ADDRESS(IMX_AITC_BASE) +#define IMX_AITC_INTDISNUM (VA_AITC_BASE + INTDISNUM_OFF) +#define IMX_AITC_INTENNUM (VA_AITC_BASE + INTENNUM_OFF) + +#if 0 +#define DEBUG_IRQ(fmt...) printk(fmt) +#else +#define DEBUG_IRQ(fmt...) do { } while (0) +#endif + +static void +imx_mask_irq(unsigned int irq) +{ + __raw_writel(irq, IMX_AITC_INTDISNUM); +} + +static void +imx_unmask_irq(unsigned int irq) +{ + __raw_writel(irq, IMX_AITC_INTENNUM); +} + +static int +imx_gpio_irq_type(unsigned int _irq, unsigned int type) +{ + unsigned int irq_type = 0, irq, reg, bit; + + irq = _irq - IRQ_GPIOA(0); + reg = irq >> 5; + bit = 1 << (irq % 32); + + if (type == IRQT_PROBE) { + /* Don't mess with enabled GPIOs using preconfigured edges or + GPIOs set to alternate function during probe */ + /* TODO: support probe */ +// if ((GPIO_IRQ_rising_edge[idx] | GPIO_IRQ_falling_edge[idx]) & +// GPIO_bit(gpio)) +// return 0; +// if (GAFR(gpio) & (0x3 << (((gpio) & 0xf)*2))) +// return 0; +// type = __IRQT_RISEDGE | __IRQT_FALEDGE; + } + + GIUS(reg) |= bit; + DDIR(reg) &= ~(bit); + + DEBUG_IRQ("setting type of irq %d to ", _irq); + + if (type & __IRQT_RISEDGE) { + DEBUG_IRQ("rising edges\n"); + irq_type = 0x0; + } + if (type & __IRQT_FALEDGE) { + DEBUG_IRQ("falling edges\n"); + irq_type = 0x1; + } + if (type & __IRQT_LOWLVL) { + DEBUG_IRQ("low level\n"); + irq_type = 0x3; + } + if (type & __IRQT_HIGHLVL) { + DEBUG_IRQ("high level\n"); + irq_type = 0x2; + } + + if (irq % 32 < 16) { + ICR1(reg) = (ICR1(reg) & ~(0x3 << ((irq % 16) * 2))) | + (irq_type << ((irq % 16) * 2)); + } else { + ICR2(reg) = (ICR2(reg) & ~(0x3 << ((irq % 16) * 2))) | + (irq_type << ((irq % 16) * 2)); + } + + return 0; + +} + +static void +imx_gpio_ack_irq(unsigned int irq) +{ + DEBUG_IRQ("%s: irq %d\n", __FUNCTION__, irq); + ISR(IRQ_TO_REG(irq)) |= 1 << ((irq - IRQ_GPIOA(0)) % 32); +} + +static void +imx_gpio_mask_irq(unsigned int irq) +{ + DEBUG_IRQ("%s: irq %d\n", __FUNCTION__, irq); + IMR(IRQ_TO_REG(irq)) &= ~( 1 << ((irq - IRQ_GPIOA(0)) % 32)); +} + +static void +imx_gpio_unmask_irq(unsigned int irq) +{ + DEBUG_IRQ("%s: irq %d\n", __FUNCTION__, irq); + IMR(IRQ_TO_REG(irq)) |= 1 << ((irq - IRQ_GPIOA(0)) % 32); +} + +static void +imx_gpio_handler(unsigned int mask, unsigned int irq, + struct irqdesc *desc, struct pt_regs *regs) +{ + desc = irq_desc + irq; + while (mask) { + if (mask & 1) { + DEBUG_IRQ("handling irq %d\n", irq); + desc->handle(irq, desc, regs); + } + irq++; + desc++; + mask >>= 1; + } +} + +static void +imx_gpioa_demux_handler(unsigned int irq_unused, struct irqdesc *desc, + struct pt_regs *regs) +{ + unsigned int mask, irq; + + mask = ISR(0); + irq = IRQ_GPIOA(0); + imx_gpio_handler(mask, irq, desc, regs); +} + +static void +imx_gpiob_demux_handler(unsigned int irq_unused, struct irqdesc *desc, + struct pt_regs *regs) +{ + unsigned int mask, irq; + + mask = ISR(1); + irq = IRQ_GPIOB(0); + imx_gpio_handler(mask, irq, desc, regs); +} + +static void +imx_gpioc_demux_handler(unsigned int irq_unused, struct irqdesc *desc, + struct pt_regs *regs) +{ + unsigned int mask, irq; + + mask = ISR(2); + irq = IRQ_GPIOC(0); + imx_gpio_handler(mask, irq, desc, regs); +} + +static void +imx_gpiod_demux_handler(unsigned int irq_unused, struct irqdesc *desc, + struct pt_regs *regs) +{ + unsigned int mask, irq; + + mask = ISR(3); + irq = IRQ_GPIOD(0); + imx_gpio_handler(mask, irq, desc, regs); +} + +static struct irqchip imx_internal_chip = { + .ack = imx_mask_irq, + .mask = imx_mask_irq, + .unmask = imx_unmask_irq, +}; + +static struct irqchip imx_gpio_chip = { + .ack = imx_gpio_ack_irq, + .mask = imx_gpio_mask_irq, + .unmask = imx_gpio_unmask_irq, + .type = imx_gpio_irq_type, +}; + +void __init +imx_init_irq(void) +{ + unsigned int irq; + + DEBUG_IRQ("Initializing imx interrupts\n"); + + /* Mask all interrupts initially */ + IMR(0) = 0; + IMR(1) = 0; + IMR(2) = 0; + IMR(3) = 0; + + for (irq = 0; irq < IMX_IRQS; irq++) { + set_irq_chip(irq, &imx_internal_chip); + set_irq_handler(irq, do_level_IRQ); + set_irq_flags(irq, IRQF_VALID); + } + + for (irq = IRQ_GPIOA(0); irq < IRQ_GPIOD(32); irq++) { + set_irq_chip(irq, &imx_gpio_chip); + set_irq_handler(irq, do_edge_IRQ); + set_irq_flags(irq, IRQF_VALID); + } + + set_irq_chained_handler(GPIO_INT_PORTA, imx_gpioa_demux_handler); + set_irq_chained_handler(GPIO_INT_PORTB, imx_gpiob_demux_handler); + set_irq_chained_handler(GPIO_INT_PORTC, imx_gpioc_demux_handler); + set_irq_chained_handler(GPIO_INT_PORTD, imx_gpiod_demux_handler); + + /* Disable all interrupts initially. */ + /* In IMX this is done in the bootloader. */ +} diff --git a/arch/arm/mach-imx/leds-mx1ads.c b/arch/arm/mach-imx/leds-mx1ads.c new file mode 100644 index 00000000000..e6399b06e4a --- /dev/null +++ b/arch/arm/mach-imx/leds-mx1ads.c @@ -0,0 +1,54 @@ +/* + * linux/arch/arm/mach-imx/leds-mx1ads.c + * + * Copyright (c) 2004 Sascha Hauer <sascha@saschahauer.de> + * + * Original (leds-footbridge.c) by Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <asm/hardware.h> +#include <asm/system.h> +#include <asm/io.h> +#include <asm/leds.h> +#include <asm/mach-types.h> +#include "leds.h" + +/* + * The MX1ADS Board has only one usable LED, + * so select only the timer led or the + * cpu usage led + */ +void +mx1ads_leds_event(led_event_t ledevt) +{ + unsigned long flags; + + local_irq_save(flags); + + switch (ledevt) { +#ifdef CONFIG_LEDS_CPU + case led_idle_start: + DR(0) &= ~(1<<2); + break; + + case led_idle_end: + DR(0) |= 1<<2; + break; +#endif + +#ifdef CONFIG_LEDS_TIMER + case led_timer: + DR(0) ^= 1<<2; +#endif + default: + break; + } + local_irq_restore(flags); +} diff --git a/arch/arm/mach-imx/leds.c b/arch/arm/mach-imx/leds.c new file mode 100644 index 00000000000..471c1db7c57 --- /dev/null +++ b/arch/arm/mach-imx/leds.c @@ -0,0 +1,31 @@ +/* + * linux/arch/arm/mach-imx/leds.h + * + * Copyright (C) 2004 Sascha Hauer <sascha@saschahauer.de> + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/kernel.h> +#include <linux/init.h> + +#include <asm/leds.h> +#include <asm/mach-types.h> + +#include "leds.h" + +static int __init +leds_init(void) +{ + if (machine_is_mx1ads()) { + leds_event = mx1ads_leds_event; + } + + return 0; +} + +__initcall(leds_init); diff --git a/arch/arm/mach-imx/leds.h b/arch/arm/mach-imx/leds.h new file mode 100644 index 00000000000..83fa21e795a --- /dev/null +++ b/arch/arm/mach-imx/leds.h @@ -0,0 +1,9 @@ +/* + * include/asm-arm/arch-imx/leds.h + * + * Copyright (c) 2004 Sascha Hauer <sascha@saschahauer.de> + * + * blinky lights for IMX-based systems + * + */ +extern void mx1ads_leds_event(led_event_t evt); diff --git a/arch/arm/mach-imx/mx1ads.c b/arch/arm/mach-imx/mx1ads.c new file mode 100644 index 00000000000..625dd01c257 --- /dev/null +++ b/arch/arm/mach-imx/mx1ads.c @@ -0,0 +1,88 @@ +/* + * arch/arm/mach-imx/mx1ads.c + * + * Initially based on: + * linux-2.6.7-imx/arch/arm/mach-imx/scb9328.c + * Copyright (c) 2004 Sascha Hauer <sascha@saschahauer.de> + * + * 2004 (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/device.h> +#include <linux/init.h> +#include <asm/system.h> +#include <asm/hardware.h> +#include <asm/irq.h> +#include <asm/pgtable.h> +#include <asm/page.h> + +#include <asm/mach/map.h> +#include <asm/mach-types.h> + +#include <asm/mach/arch.h> +#include <linux/interrupt.h> +#include "generic.h" +#include <asm/serial.h> + +static struct resource mx1ads_resources[] = { + [0] = { + .start = IMX_CS4_VIRT, + .end = IMX_CS4_VIRT + 16, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = 13, + .end = 13, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device mx1ads_device = { + .name = "mx1ads", + .num_resources = ARRAY_SIZE(mx1ads_resources), + .resource = mx1ads_resources, +}; + +static struct platform_device *devices[] __initdata = { + &mx1ads_device, +}; + +static void __init +mx1ads_init(void) +{ +#ifdef CONFIG_LEDS + imx_gpio_mode(GPIO_PORTA | GPIO_OUT | GPIO_GPIO | 2); +#endif + platform_add_devices(devices, ARRAY_SIZE(devices)); +} + +static struct map_desc mx1ads_io_desc[] __initdata = { + /* virtual physical length type */ + {IMX_CS0_VIRT, IMX_CS0_PHYS, IMX_CS0_SIZE, MT_DEVICE}, + {IMX_CS1_VIRT, IMX_CS1_PHYS, IMX_CS1_SIZE, MT_DEVICE}, + {IMX_CS2_VIRT, IMX_CS2_PHYS, IMX_CS2_SIZE, MT_DEVICE}, + {IMX_CS3_VIRT, IMX_CS3_PHYS, IMX_CS3_SIZE, MT_DEVICE}, + {IMX_CS4_VIRT, IMX_CS4_PHYS, IMX_CS4_SIZE, MT_DEVICE}, + {IMX_CS5_VIRT, IMX_CS5_PHYS, IMX_CS5_SIZE, MT_DEVICE}, +}; + +static void __init +mx1ads_map_io(void) +{ + imx_map_io(); + iotable_init(mx1ads_io_desc, ARRAY_SIZE(mx1ads_io_desc)); +} + +MACHINE_START(MX1ADS, "Motorola MX1ADS") + MAINTAINER("Sascha Hauer, Pengutronix") + BOOT_MEM(0x08000000, 0x00200000, 0xe0200000) + BOOT_PARAMS(0x08000100) + MAPIO(mx1ads_map_io) + INITIRQ(imx_init_irq) + .timer = &imx_timer, + INIT_MACHINE(mx1ads_init) +MACHINE_END diff --git a/arch/arm/mach-imx/time.c b/arch/arm/mach-imx/time.c new file mode 100644 index 00000000000..11f1e56c36b --- /dev/null +++ b/arch/arm/mach-imx/time.c @@ -0,0 +1,101 @@ +/* + * linux/arch/arm/mach-imx/time.c + * + * Copyright (C) 2000-2001 Deep Blue Solutions + * Copyright (C) 2002 Shane Nay (shane@minirl.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/time.h> + +#include <asm/hardware.h> +#include <asm/io.h> +#include <asm/leds.h> +#include <asm/irq.h> +#include <asm/mach/time.h> + +/* Use timer 1 as system timer */ +#define TIMER_BASE IMX_TIM1_BASE + +/* + * Returns number of us since last clock interrupt. Note that interrupts + * will have been disabled by do_gettimeoffset() + */ +static unsigned long imx_gettimeoffset(void) +{ + unsigned long ticks; + + /* + * Get the current number of ticks. Note that there is a race + * condition between us reading the timer and checking for + * an interrupt. We get around this by ensuring that the + * counter has not reloaded between our two reads. + */ + ticks = IMX_TCN(TIMER_BASE); + + /* + * Interrupt pending? If so, we've reloaded once already. + */ + if (IMX_TSTAT(TIMER_BASE) & TSTAT_COMP) + ticks += LATCH; + + /* + * Convert the ticks to usecs + */ + return (1000000 / CLK32) * ticks; +} + +/* + * IRQ handler for the timer + */ +static irqreturn_t +imx_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + write_seqlock(&xtime_lock); + + /* clear the interrupt */ + if (IMX_TSTAT(TIMER_BASE)) + IMX_TSTAT(TIMER_BASE) = 0; + + timer_tick(regs); + write_sequnlock(&xtime_lock); + + return IRQ_HANDLED; +} + +static struct irqaction imx_timer_irq = { + .name = "i.MX Timer Tick", + .flags = SA_INTERRUPT, + .handler = imx_timer_interrupt +}; + +/* + * Set up timer interrupt, and return the current time in seconds. + */ +static void __init imx_timer_init(void) +{ + /* + * Initialise to a known state (all timers off, and timing reset) + */ + IMX_TCTL(TIMER_BASE) = 0; + IMX_TPRER(TIMER_BASE) = 0; + IMX_TCMP(TIMER_BASE) = LATCH - 1; + IMX_TCTL(TIMER_BASE) = TCTL_CLK_32 | TCTL_IRQEN | TCTL_TEN; + + /* + * Make irqs happen for the system timer + */ + setup_irq(TIM1_INT, &imx_timer_irq); +} + +struct sys_timer imx_timer = { + .init = imx_timer_init, + .offset = imx_gettimeoffset, +}; |