diff options
Diffstat (limited to 'arch/arm/mach-omap1')
-rw-r--r-- | arch/arm/mach-omap1/Makefile | 3 | ||||
-rw-r--r-- | arch/arm/mach-omap1/board-osk.c | 138 | ||||
-rw-r--r-- | arch/arm/mach-omap1/leds-osk.c | 80 | ||||
-rw-r--r-- | arch/arm/mach-omap1/mux.c | 146 | ||||
-rw-r--r-- | arch/arm/mach-omap1/time.c | 49 | ||||
-rw-r--r-- | arch/arm/mach-omap1/timer32k.c | 209 |
6 files changed, 432 insertions, 193 deletions
diff --git a/arch/arm/mach-omap1/Makefile b/arch/arm/mach-omap1/Makefile index 015a66b3ca8e..c06f5254c0f3 100644 --- a/arch/arm/mach-omap1/Makefile +++ b/arch/arm/mach-omap1/Makefile @@ -5,7 +5,8 @@ # Common support obj-y := io.o id.o clock.o irq.o mux.o serial.o devices.o -obj-$(CONFIG_OMAP_MPU_TIMER) += time.o +obj-$(CONFIG_OMAP_MPU_TIMER) += time.o +obj-$(CONFIG_OMAP_32K_TIMER) += timer32k.o # Power Management obj-$(CONFIG_PM) += pm.o sleep.o diff --git a/arch/arm/mach-omap1/board-osk.c b/arch/arm/mach-omap1/board-osk.c index 5279e35a8aec..4f9baba7d893 100644 --- a/arch/arm/mach-omap1/board-osk.c +++ b/arch/arm/mach-omap1/board-osk.c @@ -32,6 +32,7 @@ #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/i2c.h> +#include <linux/leds.h> #include <linux/mtd/mtd.h> #include <linux/mtd/partitions.h> @@ -183,11 +184,80 @@ static struct platform_device *osk5912_devices[] __initdata = { &osk5912_mcbsp1_device, }; +static struct gpio_led tps_leds[] = { + /* NOTE: D9 and D2 have hardware blink support. + * Also, D9 requires non-battery power. + */ + { .gpio = OSK_TPS_GPIO_LED_D9, .name = "d9", }, + { .gpio = OSK_TPS_GPIO_LED_D2, .name = "d2", }, + { .gpio = OSK_TPS_GPIO_LED_D3, .name = "d3", .active_low = 1, + .default_trigger = "heartbeat", }, +}; + +static struct gpio_led_platform_data tps_leds_data = { + .num_leds = 3, + .leds = tps_leds, +}; + +static struct platform_device osk5912_tps_leds = { + .name = "leds-gpio", + .id = 0, + .dev.platform_data = &tps_leds_data, +}; + +static int osk_tps_setup(struct i2c_client *client, void *context) +{ + /* Set GPIO 1 HIGH to disable VBUS power supply; + * OHCI driver powers it up/down as needed. + */ + gpio_request(OSK_TPS_GPIO_USB_PWR_EN, "n_vbus_en"); + gpio_direction_output(OSK_TPS_GPIO_USB_PWR_EN, 1); + + /* Set GPIO 2 high so LED D3 is off by default */ + tps65010_set_gpio_out_value(GPIO2, HIGH); + + /* Set GPIO 3 low to take ethernet out of reset */ + gpio_request(OSK_TPS_GPIO_LAN_RESET, "smc_reset"); + gpio_direction_output(OSK_TPS_GPIO_LAN_RESET, 0); + + /* GPIO4 is VDD_DSP */ + gpio_request(OSK_TPS_GPIO_DSP_PWR_EN, "dsp_power"); + gpio_direction_output(OSK_TPS_GPIO_DSP_PWR_EN, 1); + /* REVISIT if DSP support isn't configured, power it off ... */ + + /* Let LED1 (D9) blink; leds-gpio may override it */ + tps65010_set_led(LED1, BLINK); + + /* Set LED2 off by default */ + tps65010_set_led(LED2, OFF); + + /* Enable LOW_PWR handshake */ + tps65010_set_low_pwr(ON); + + /* Switch VLDO2 to 3.0V for AIC23 */ + tps65010_config_vregs1(TPS_LDO2_ENABLE | TPS_VLDO2_3_0V + | TPS_LDO1_ENABLE); + + /* register these three LEDs */ + osk5912_tps_leds.dev.parent = &client->dev; + platform_device_register(&osk5912_tps_leds); + + return 0; +} + +static struct tps65010_board tps_board = { + .base = OSK_TPS_GPIO_BASE, + .outmask = 0x0f, + .setup = osk_tps_setup, +}; + static struct i2c_board_info __initdata osk_i2c_board_info[] = { { I2C_BOARD_INFO("tps65010", 0x48), .type = "tps65010", .irq = OMAP_GPIO_IRQ(OMAP_MPUIO(1)), + .platform_data = &tps_board, + }, /* TODO when driver support is ready: * - aic23 audio chip at 0x1a @@ -198,7 +268,7 @@ static struct i2c_board_info __initdata osk_i2c_board_info[] = { static void __init osk_init_smc91x(void) { - if ((omap_request_gpio(0)) < 0) { + if ((gpio_request(0, "smc_irq")) < 0) { printk("Error requesting gpio 0 for smc91x irq\n"); return; } @@ -210,7 +280,7 @@ static void __init osk_init_smc91x(void) static void __init osk_init_cf(void) { omap_cfg_reg(M7_1610_GPIO62); - if ((omap_request_gpio(62)) < 0) { + if ((gpio_request(62, "cf_irq")) < 0) { printk("Error requesting gpio 62 for CF irq\n"); return; } @@ -334,7 +404,7 @@ static struct platform_device *mistral_devices[] __initdata = { static int mistral_get_pendown_state(void) { - return !omap_get_gpio_datain(4); + return !gpio_get_value(4); } static const struct ads7846_platform_data mistral_ts_info = { @@ -396,25 +466,31 @@ static void __init osk_mistral_init(void) omap_cfg_reg(W14_1610_CCP_DATAP); /* CAM_PWDN */ - if (omap_request_gpio(11) == 0) { + if (gpio_request(11, "cam_pwdn") == 0) { omap_cfg_reg(N20_1610_GPIO11); - omap_set_gpio_direction(11, 0 /* out */); - omap_set_gpio_dataout(11, 0 /* off */); + gpio_direction_output(11, 0); } else pr_debug("OSK+Mistral: CAM_PWDN is awol\n"); /* omap_cfg_reg(P19_1610_GPIO6); */ /* BUSY */ + gpio_request(6, "ts_busy"); + gpio_direction_input(6); + omap_cfg_reg(P20_1610_GPIO4); /* PENIRQ */ + gpio_request(4, "ts_int"); + gpio_direction_input(4); set_irq_type(OMAP_GPIO_IRQ(4), IRQT_FALLING); + spi_register_board_info(mistral_boardinfo, ARRAY_SIZE(mistral_boardinfo)); /* the sideways button (SW1) is for use as a "wakeup" button */ omap_cfg_reg(N15_1610_MPUIO2); - if (omap_request_gpio(OMAP_MPUIO(2)) == 0) { + if (gpio_request(OMAP_MPUIO(2), "wakeup") == 0) { int ret = 0; - omap_set_gpio_direction(OMAP_MPUIO(2), 1); + + gpio_direction_input(OMAP_MPUIO(2)); set_irq_type(OMAP_GPIO_IRQ(OMAP_MPUIO(2)), IRQT_RISING); #ifdef CONFIG_PM /* share the IRQ in case someone wants to use the @@ -425,7 +501,7 @@ static void __init osk_mistral_init(void) IRQF_SHARED, "mistral_wakeup", &osk_mistral_wake_interrupt); if (ret != 0) { - omap_free_gpio(OMAP_MPUIO(2)); + gpio_free(OMAP_MPUIO(2)); printk(KERN_ERR "OSK+Mistral: no wakeup irq, %d?\n", ret); } else @@ -438,10 +514,8 @@ static void __init osk_mistral_init(void) * board, like the touchscreen, EEPROM, and wakeup (!) switch. */ omap_cfg_reg(PWL); - if (omap_request_gpio(2) == 0) { - omap_set_gpio_direction(2, 0 /* out */); - omap_set_gpio_dataout(2, 1 /* on */); - } + if (gpio_request(2, "lcd_pwr") == 0) + gpio_direction_output(2, 1); platform_add_devices(mistral_devices, ARRAY_SIZE(mistral_devices)); } @@ -484,44 +558,6 @@ static void __init osk_map_io(void) omap1_map_common_io(); } -#ifdef CONFIG_TPS65010 -static int __init osk_tps_init(void) -{ - if (!machine_is_omap_osk()) - return 0; - - /* Let LED1 (D9) blink */ - tps65010_set_led(LED1, BLINK); - - /* Disable LED 2 (D2) */ - tps65010_set_led(LED2, OFF); - - /* Set GPIO 1 HIGH to disable VBUS power supply; - * OHCI driver powers it up/down as needed. - */ - tps65010_set_gpio_out_value(GPIO1, HIGH); - - /* Set GPIO 2 low to turn on LED D3 */ - tps65010_set_gpio_out_value(GPIO2, HIGH); - - /* Set GPIO 3 low to take ethernet out of reset */ - tps65010_set_gpio_out_value(GPIO3, LOW); - - /* gpio4 for VDD_DSP */ - /* FIXME send power to DSP iff it's configured */ - - /* Enable LOW_PWR */ - tps65010_set_low_pwr(ON); - - /* Switch VLDO2 to 3.0V for AIC23 */ - tps65010_config_vregs1(TPS_LDO2_ENABLE | TPS_VLDO2_3_0V - | TPS_LDO1_ENABLE); - - return 0; -} -fs_initcall(osk_tps_init); -#endif - MACHINE_START(OMAP_OSK, "TI-OSK") /* Maintainer: Dirk Behme <dirk.behme@de.bosch.com> */ .phys_io = 0xfff00000, diff --git a/arch/arm/mach-omap1/leds-osk.c b/arch/arm/mach-omap1/leds-osk.c index 026685ed461a..754383dde807 100644 --- a/arch/arm/mach-omap1/leds-osk.c +++ b/arch/arm/mach-omap1/leds-osk.c @@ -1,11 +1,9 @@ /* * linux/arch/arm/mach-omap1/leds-osk.c * - * LED driver for OSK, and optionally Mistral QVGA, boards + * LED driver for OSK with optional Mistral QVGA board */ #include <linux/init.h> -#include <linux/workqueue.h> -#include <linux/i2c/tps65010.h> #include <asm/hardware.h> #include <asm/leds.h> @@ -20,49 +18,11 @@ #define LED_STATE_CLAIMED (1 << 1) static u8 led_state; -#define GREEN_LED (1 << 0) /* TPS65010 LED1 */ -#define AMBER_LED (1 << 1) /* TPS65010 LED2 */ -#define RED_LED (1 << 2) /* TPS65010 GPIO2 */ #define TIMER_LED (1 << 3) /* Mistral board */ #define IDLE_LED (1 << 4) /* Mistral board */ static u8 hw_led_state; -/* TPS65010 leds are changed using i2c -- from a task context. - * Using one of these for the "idle" LED would be impractical... - */ -#define TPS_LEDS (GREEN_LED | RED_LED | AMBER_LED) - -static u8 tps_leds_change; - -static void tps_work(struct work_struct *unused) -{ - for (;;) { - u8 leds; - - local_irq_disable(); - leds = tps_leds_change; - tps_leds_change = 0; - local_irq_enable(); - - if (!leds) - break; - - /* careful: the set_led() value is on/off/blink */ - if (leds & GREEN_LED) - tps65010_set_led(LED1, !!(hw_led_state & GREEN_LED)); - if (leds & AMBER_LED) - tps65010_set_led(LED2, !!(hw_led_state & AMBER_LED)); - - /* the gpio led doesn't have that issue */ - if (leds & RED_LED) - tps65010_set_gpio_out_value(GPIO2, - !(hw_led_state & RED_LED)); - } -} - -static DECLARE_WORK(work, tps_work); - #ifdef CONFIG_OMAP_OSK_MISTRAL /* For now, all system indicators require the Mistral board, since that @@ -112,7 +72,6 @@ void osk_leds_event(led_event_t evt) case led_stop: led_state &= ~LED_STATE_ENABLED; hw_led_state = 0; - /* NOTE: work may still be pending!! */ break; case led_claim: @@ -145,48 +104,11 @@ void osk_leds_event(led_event_t evt) #endif /* CONFIG_OMAP_OSK_MISTRAL */ - /* "green" == tps LED1 (leftmost, normally power-good) - * works only with DC adapter, not on battery power! - */ - case led_green_on: - if (led_state & LED_STATE_CLAIMED) - hw_led_state |= GREEN_LED; - break; - case led_green_off: - if (led_state & LED_STATE_CLAIMED) - hw_led_state &= ~GREEN_LED; - break; - - /* "amber" == tps LED2 (middle) */ - case led_amber_on: - if (led_state & LED_STATE_CLAIMED) - hw_led_state |= AMBER_LED; - break; - case led_amber_off: - if (led_state & LED_STATE_CLAIMED) - hw_led_state &= ~AMBER_LED; - break; - - /* "red" == LED on tps gpio3 (rightmost) */ - case led_red_on: - if (led_state & LED_STATE_CLAIMED) - hw_led_state |= RED_LED; - break; - case led_red_off: - if (led_state & LED_STATE_CLAIMED) - hw_led_state &= ~RED_LED; - break; - default: break; } leds ^= hw_led_state; - leds &= TPS_LEDS; - if (leds && (led_state & LED_STATE_CLAIMED)) { - tps_leds_change |= leds; - schedule_work(&work); - } done: local_irq_restore(flags); diff --git a/arch/arm/mach-omap1/mux.c b/arch/arm/mach-omap1/mux.c index 52c70e5fcf65..e207bf7cb853 100644 --- a/arch/arm/mach-omap1/mux.c +++ b/arch/arm/mach-omap1/mux.c @@ -3,9 +3,9 @@ * * OMAP1 pin multiplexing configurations * - * Copyright (C) 2003 - 2005 Nokia Corporation + * Copyright (C) 2003 - 2008 Nokia Corporation * - * Written by Tony Lindgren <tony.lindgren@nokia.com> + * Written by Tony Lindgren * * 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 @@ -32,8 +32,10 @@ #ifdef CONFIG_OMAP_MUX +static struct omap_mux_cfg arch_mux_cfg; + #ifdef CONFIG_ARCH_OMAP730 -struct pin_config __initdata_or_module omap730_pins[] = { +static struct pin_config __initdata_or_module omap730_pins[] = { MUX_CFG_730("E2_730_KBR0", 12, 21, 0, 20, 1, 0) MUX_CFG_730("J7_730_KBR1", 12, 25, 0, 24, 1, 0) MUX_CFG_730("E1_730_KBR2", 12, 29, 0, 28, 1, 0) @@ -49,10 +51,14 @@ MUX_CFG_730("AA17_730_USB_DM", 2, 21, 0, 20, 0, 0) MUX_CFG_730("W16_730_USB_PU_EN", 2, 25, 0, 24, 0, 0) MUX_CFG_730("W17_730_USB_VBUSI", 2, 29, 0, 28, 0, 0) }; -#endif +#define OMAP730_PINS_SZ ARRAY_SIZE(omap730_pins) +#else +#define omap730_pins NULL +#define OMAP730_PINS_SZ 0 +#endif /* CONFIG_ARCH_OMAP730 */ #if defined(CONFIG_ARCH_OMAP15XX) || defined(CONFIG_ARCH_OMAP16XX) -struct pin_config __initdata_or_module omap1xxx_pins[] = { +static struct pin_config __initdata_or_module omap1xxx_pins[] = { /* * description mux mode mux pull pull pull pu_pd pu dbg * reg offset mode reg bit ena reg @@ -306,22 +312,136 @@ MUX_CFG("Y12_1610_CCP_CLKP", 8, 18, 6, 1, 24, 1, 1, 0, 0) MUX_CFG("W13_1610_CCP_CLKM", 9, 0, 6, 1, 28, 1, 1, 0, 0) MUX_CFG("W14_1610_CCP_DATAP", 9, 24, 6, 2, 4, 1, 2, 0, 0) MUX_CFG("Y14_1610_CCP_DATAM", 9, 21, 6, 2, 3, 1, 2, 0, 0) - }; +#define OMAP1XXX_PINS_SZ ARRAY_SIZE(omap1xxx_pins) +#else +#define omap1xxx_pins NULL +#define OMAP1XXX_PINS_SZ 0 #endif /* CONFIG_ARCH_OMAP15XX || CONFIG_ARCH_OMAP16XX */ -int __init omap1_mux_init(void) +int __init_or_module omap1_cfg_reg(const struct pin_config *cfg) { - -#ifdef CONFIG_ARCH_OMAP730 - omap_mux_register(omap730_pins, ARRAY_SIZE(omap730_pins)); + static DEFINE_SPINLOCK(mux_spin_lock); + unsigned long flags; + unsigned int reg_orig = 0, reg = 0, pu_pd_orig = 0, pu_pd = 0, + pull_orig = 0, pull = 0; + unsigned int mask, warn = 0; + + /* Check the mux register in question */ + if (cfg->mux_reg) { + unsigned tmp1, tmp2; + + spin_lock_irqsave(&mux_spin_lock, flags); + reg_orig = omap_readl(cfg->mux_reg); + + /* The mux registers always seem to be 3 bits long */ + mask = (0x7 << cfg->mask_offset); + tmp1 = reg_orig & mask; + reg = reg_orig & ~mask; + + tmp2 = (cfg->mask << cfg->mask_offset); + reg |= tmp2; + + if (tmp1 != tmp2) + warn = 1; + + omap_writel(reg, cfg->mux_reg); + spin_unlock_irqrestore(&mux_spin_lock, flags); + } + + /* Check for pull up or pull down selection on 1610 */ + if (!cpu_is_omap15xx()) { + if (cfg->pu_pd_reg && cfg->pull_val) { + spin_lock_irqsave(&mux_spin_lock, flags); + pu_pd_orig = omap_readl(cfg->pu_pd_reg); + mask = 1 << cfg->pull_bit; + + if (cfg->pu_pd_val) { + if (!(pu_pd_orig & mask)) + warn = 1; + /* Use pull up */ + pu_pd = pu_pd_orig | mask; + } else { + if (pu_pd_orig & mask) + warn = 1; + /* Use pull down */ + pu_pd = pu_pd_orig & ~mask; + } + omap_writel(pu_pd, cfg->pu_pd_reg); + spin_unlock_irqrestore(&mux_spin_lock, flags); + } + } + + /* Check for an associated pull down register */ + if (cfg->pull_reg) { + spin_lock_irqsave(&mux_spin_lock, flags); + pull_orig = omap_readl(cfg->pull_reg); + mask = 1 << cfg->pull_bit; + + if (cfg->pull_val) { + if (pull_orig & mask) + warn = 1; + /* Low bit = pull enabled */ + pull = pull_orig & ~mask; + } else { + if (!(pull_orig & mask)) + warn = 1; + /* High bit = pull disabled */ + pull = pull_orig | mask; + } + + omap_writel(pull, cfg->pull_reg); + spin_unlock_irqrestore(&mux_spin_lock, flags); + } + + if (warn) { +#ifdef CONFIG_OMAP_MUX_WARNINGS + printk(KERN_WARNING "MUX: initialized %s\n", cfg->name); #endif - -#if defined(CONFIG_ARCH_OMAP15XX) || defined(CONFIG_ARCH_OMAP16XX) - omap_mux_register(omap1xxx_pins, ARRAY_SIZE(omap1xxx_pins)); + } + +#ifdef CONFIG_OMAP_MUX_DEBUG + if (cfg->debug || warn) { + printk("MUX: Setting register %s\n", cfg->name); + printk(" %s (0x%08x) = 0x%08x -> 0x%08x\n", + cfg->mux_reg_name, cfg->mux_reg, reg_orig, reg); + + if (!cpu_is_omap15xx()) { + if (cfg->pu_pd_reg && cfg->pull_val) { + printk(" %s (0x%08x) = 0x%08x -> 0x%08x\n", + cfg->pu_pd_name, cfg->pu_pd_reg, + pu_pd_orig, pu_pd); + } + } + + if (cfg->pull_reg) + printk(" %s (0x%08x) = 0x%08x -> 0x%08x\n", + cfg->pull_name, cfg->pull_reg, pull_orig, pull); + } #endif +#ifdef CONFIG_OMAP_MUX_ERRORS + return warn ? -ETXTBSY : 0; +#else return 0; +#endif +} + +int __init omap1_mux_init(void) +{ + if (cpu_is_omap730()) { + arch_mux_cfg.pins = omap730_pins; + arch_mux_cfg.size = OMAP730_PINS_SZ; + arch_mux_cfg.cfg_reg = omap1_cfg_reg; + } + + if (cpu_is_omap15xx() || cpu_is_omap16xx()) { + arch_mux_cfg.pins = omap1xxx_pins; + arch_mux_cfg.size = OMAP1XXX_PINS_SZ; + arch_mux_cfg.cfg_reg = omap1_cfg_reg; + } + + return omap_mux_register(&arch_mux_cfg); } #endif diff --git a/arch/arm/mach-omap1/time.c b/arch/arm/mach-omap1/time.c index a4f8b2055437..5d2b270935a2 100644 --- a/arch/arm/mach-omap1/time.c +++ b/arch/arm/mach-omap1/time.c @@ -56,37 +56,6 @@ #define OMAP_MPU_TIMER_BASE OMAP_MPU_TIMER1_BASE #define OMAP_MPU_TIMER_OFFSET 0x100 -/* cycles to nsec conversions taken from arch/i386/kernel/timers/timer_tsc.c, - * converted to use kHz by Kevin Hilman */ -/* convert from cycles(64bits) => nanoseconds (64bits) - * basic equation: - * ns = cycles / (freq / ns_per_sec) - * ns = cycles * (ns_per_sec / freq) - * ns = cycles * (10^9 / (cpu_khz * 10^3)) - * ns = cycles * (10^6 / cpu_khz) - * - * Then we use scaling math (suggested by george at mvista.com) to get: - * ns = cycles * (10^6 * SC / cpu_khz / SC - * ns = cycles * cyc2ns_scale / SC - * - * And since SC is a constant power of two, we can convert the div - * into a shift. - * -johnstul at us.ibm.com "math is hard, lets go shopping!" - */ -static unsigned long cyc2ns_scale; -#define CYC2NS_SCALE_FACTOR 10 /* 2^10, carefully chosen */ - -static inline void set_cyc2ns_scale(unsigned long cpu_khz) -{ - cyc2ns_scale = (1000000 << CYC2NS_SCALE_FACTOR)/cpu_khz; -} - -static inline unsigned long long cycles_2_ns(unsigned long long cyc) -{ - return (cyc * cyc2ns_scale) >> CYC2NS_SCALE_FACTOR; -} - - typedef struct { u32 cntl; /* CNTL_TIMER, R/W */ u32 load_tim; /* LOAD_TIM, W */ @@ -194,8 +163,6 @@ static struct irqaction omap_mpu_timer1_irq = { static __init void omap_init_mpu_timer(unsigned long rate) { - set_cyc2ns_scale(rate / 1000); - setup_irq(INT_TIMER1, &omap_mpu_timer1_irq); omap_mpu_timer_start(0, (rate / HZ) - 1, 1); @@ -260,22 +227,6 @@ static void __init omap_init_clocksource(unsigned long rate) printk(err, clocksource_mpu.name); } - -/* - * Scheduler clock - returns current time in nanosec units. - */ -unsigned long long sched_clock(void) -{ - unsigned long ticks = 0 - omap_mpu_timer_read(1); - unsigned long long ticks64; - - ticks64 = omap_mpu_timer2_overflows; - ticks64 <<= 32; - ticks64 |= ticks; - - return cycles_2_ns(ticks64); -} - /* * --------------------------------------------------------------------------- * Timer initialization diff --git a/arch/arm/mach-omap1/timer32k.c b/arch/arm/mach-omap1/timer32k.c new file mode 100644 index 000000000000..fbbdb806c95a --- /dev/null +++ b/arch/arm/mach-omap1/timer32k.c @@ -0,0 +1,209 @@ +/* + * linux/arch/arm/mach-omap1/timer32k.c + * + * OMAP 32K Timer + * + * Copyright (C) 2004 - 2005 Nokia Corporation + * Partial timer rewrite and additional dynamic tick timer support by + * Tony Lindgen <tony@atomide.com> and + * Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com> + * OMAP Dual-mode timer framework support by Timo Teras + * + * MPU timer code based on the older MPU timer code for OMAP + * Copyright (C) 2000 RidgeRun, Inc. + * Author: Greg Lonnon <glonnon@ridgerun.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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * 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., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/sched.h> +#include <linux/spinlock.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/clocksource.h> +#include <linux/clockchips.h> + +#include <asm/system.h> +#include <asm/hardware.h> +#include <asm/io.h> +#include <asm/leds.h> +#include <asm/irq.h> +#include <asm/mach/irq.h> +#include <asm/mach/time.h> +#include <asm/arch/dmtimer.h> + +struct sys_timer omap_timer; + +/* + * --------------------------------------------------------------------------- + * 32KHz OS timer + * + * This currently works only on 16xx, as 1510 does not have the continuous + * 32KHz synchronous timer. The 32KHz synchronous timer is used to keep track + * of time in addition to the 32KHz OS timer. Using only the 32KHz OS timer + * on 1510 would be possible, but the timer would not be as accurate as + * with the 32KHz synchronized timer. + * --------------------------------------------------------------------------- + */ + +#if defined(CONFIG_ARCH_OMAP16XX) +#define TIMER_32K_SYNCHRONIZED 0xfffbc410 +#else +#error OMAP 32KHz timer does not currently work on 15XX! +#endif + +/* 16xx specific defines */ +#define OMAP1_32K_TIMER_BASE 0xfffb9000 +#define OMAP1_32K_TIMER_CR 0x08 +#define OMAP1_32K_TIMER_TVR 0x00 +#define OMAP1_32K_TIMER_TCR 0x04 + +#define OMAP_32K_TICKS_PER_SEC (32768) + +/* + * TRM says 1 / HZ = ( TVR + 1) / 32768, so TRV = (32768 / HZ) - 1 + * so with HZ = 128, TVR = 255. + */ +#define OMAP_32K_TIMER_TICK_PERIOD ((OMAP_32K_TICKS_PER_SEC / HZ) - 1) + +#define JIFFIES_TO_HW_TICKS(nr_jiffies, clock_rate) \ + (((nr_jiffies) * (clock_rate)) / HZ) + +static inline void omap_32k_timer_write(int val, int reg) +{ + omap_writew(val, OMAP1_32K_TIMER_BASE + reg); +} + +static inline unsigned long omap_32k_timer_read(int reg) +{ + return omap_readl(OMAP1_32K_TIMER_BASE + reg) & 0xffffff; +} + +static inline void omap_32k_timer_start(unsigned long load_val) +{ + if (!load_val) + load_val = 1; + omap_32k_timer_write(load_val, OMAP1_32K_TIMER_TVR); + omap_32k_timer_write(0x0f, OMAP1_32K_TIMER_CR); +} + +static inline void omap_32k_timer_stop(void) +{ + omap_32k_timer_write(0x0, OMAP1_32K_TIMER_CR); +} + +#define omap_32k_timer_ack_irq() + +static int omap_32k_timer_set_next_event(unsigned long delta, + struct clock_event_device *dev) +{ + omap_32k_timer_start(delta); + + return 0; +} + +static void omap_32k_timer_set_mode(enum clock_event_mode mode, + struct clock_event_device *evt) +{ + omap_32k_timer_stop(); + + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + omap_32k_timer_start(OMAP_32K_TIMER_TICK_PERIOD); + break; + case CLOCK_EVT_MODE_ONESHOT: + case CLOCK_EVT_MODE_UNUSED: + case CLOCK_EVT_MODE_SHUTDOWN: + break; + case CLOCK_EVT_MODE_RESUME: + break; + } +} + +static struct clock_event_device clockevent_32k_timer = { + .name = "32k-timer", + .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, + .shift = 32, + .set_next_event = omap_32k_timer_set_next_event, + .set_mode = omap_32k_timer_set_mode, +}; + +/* + * The 32KHz synchronized timer is an additional timer on 16xx. + * It is always running. + */ +static inline unsigned long omap_32k_sync_timer_read(void) +{ + return omap_readl(TIMER_32K_SYNCHRONIZED); +} + +static irqreturn_t omap_32k_timer_interrupt(int irq, void *dev_id) +{ + struct clock_event_device *evt = &clockevent_32k_timer; + omap_32k_timer_ack_irq(); + + evt->event_handler(evt); + + return IRQ_HANDLED; +} + +static struct irqaction omap_32k_timer_irq = { + .name = "32KHz timer", + .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, + .handler = omap_32k_timer_interrupt, +}; + +static __init void omap_init_32k_timer(void) +{ + setup_irq(INT_OS_TIMER, &omap_32k_timer_irq); + + clockevent_32k_timer.mult = div_sc(OMAP_32K_TICKS_PER_SEC, + NSEC_PER_SEC, + clockevent_32k_timer.shift); + clockevent_32k_timer.max_delta_ns = + clockevent_delta2ns(0xfffffffe, &clockevent_32k_timer); + clockevent_32k_timer.min_delta_ns = + clockevent_delta2ns(1, &clockevent_32k_timer); + + clockevent_32k_timer.cpumask = cpumask_of_cpu(0); + clockevents_register_device(&clockevent_32k_timer); +} + +/* + * --------------------------------------------------------------------------- + * Timer initialization + * --------------------------------------------------------------------------- + */ +static void __init omap_timer_init(void) +{ +#ifdef CONFIG_OMAP_DM_TIMER + omap_dm_timer_init(); +#endif + omap_init_32k_timer(); +} + +struct sys_timer omap_timer = { + .init = omap_timer_init, +}; |