diff options
author | Timo Teras <timo.teras@solidboot.com> | 2006-06-26 16:16:12 -0700 |
---|---|---|
committer | Tony Lindgren <tony@atomide.com> | 2006-06-26 16:16:12 -0700 |
commit | 77900a2fc3bfb1eb6eaa6d43eef4591e1f7c600d (patch) | |
tree | 6d6278f833a6057b1b951e4e9bded234aa6459e2 /arch/arm/plat-omap/dmtimer.c | |
parent | 6e711ec6d249c0d3f8bc225066738d064ed90032 (diff) | |
download | linux-77900a2fc3bfb1eb6eaa6d43eef4591e1f7c600d.tar.bz2 |
ARM: OMAP: Port dmtimers to OMAP2 and implement PWM support
Port dmtimer framework to OMAP2.
Modify the dmtimers API to support setting of PWM configuration and prescaler.
Convert 32 kHz timer and GP timer to use the dmtimer framework.
Signed-off-by: Timo Teras <timo.teras@solidboot.com>
Signed-off-by: Juha Yrjola <juha.yrjola@solidboot.com>
Signed-off-by: Tony Lindgren <tony@atomide.com>
Diffstat (limited to 'arch/arm/plat-omap/dmtimer.c')
-rw-r--r-- | arch/arm/plat-omap/dmtimer.c | 413 |
1 files changed, 295 insertions, 118 deletions
diff --git a/arch/arm/plat-omap/dmtimer.c b/arch/arm/plat-omap/dmtimer.c index eba3cb52ad87..c25a1a6d2b03 100644 --- a/arch/arm/plat-omap/dmtimer.c +++ b/arch/arm/plat-omap/dmtimer.c @@ -4,7 +4,8 @@ * OMAP Dual-Mode Timers * * Copyright (C) 2005 Nokia Corporation - * Author: Lauri Leukkunen <lauri.leukkunen@nokia.com> + * OMAP2 support by Juha Yrjola + * API improvements and OMAP2 clock framework support by Timo Teras * * 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 @@ -26,15 +27,17 @@ */ #include <linux/init.h> +#include <linux/spinlock.h> +#include <linux/errno.h> +#include <linux/list.h> +#include <linux/clk.h> +#include <linux/delay.h> #include <asm/hardware.h> #include <asm/arch/dmtimer.h> #include <asm/io.h> #include <asm/arch/irqs.h> -#include <linux/spinlock.h> -#include <linux/list.h> - -#define OMAP_TIMER_COUNT 8 +/* register offsets */ #define OMAP_TIMER_ID_REG 0x00 #define OMAP_TIMER_OCP_CFG_REG 0x10 #define OMAP_TIMER_SYS_STAT_REG 0x14 @@ -50,52 +53,184 @@ #define OMAP_TIMER_CAPTURE_REG 0x3c #define OMAP_TIMER_IF_CTRL_REG 0x40 +/* timer control reg bits */ +#define OMAP_TIMER_CTRL_GPOCFG (1 << 14) +#define OMAP_TIMER_CTRL_CAPTMODE (1 << 13) +#define OMAP_TIMER_CTRL_PT (1 << 12) +#define OMAP_TIMER_CTRL_TCM_LOWTOHIGH (0x1 << 8) +#define OMAP_TIMER_CTRL_TCM_HIGHTOLOW (0x2 << 8) +#define OMAP_TIMER_CTRL_TCM_BOTHEDGES (0x3 << 8) +#define OMAP_TIMER_CTRL_SCPWM (1 << 7) +#define OMAP_TIMER_CTRL_CE (1 << 6) /* compare enable */ +#define OMAP_TIMER_CTRL_PRE (1 << 5) /* prescaler enable */ +#define OMAP_TIMER_CTRL_PTV_SHIFT 2 /* how much to shift the prescaler value */ +#define OMAP_TIMER_CTRL_AR (1 << 1) /* auto-reload enable */ +#define OMAP_TIMER_CTRL_ST (1 << 0) /* start timer */ + +struct omap_dm_timer { + unsigned long phys_base; + int irq; +#ifdef CONFIG_ARCH_OMAP2 + struct clk *iclk, *fclk; +#endif + void __iomem *io_base; + unsigned reserved:1; +}; + +#ifdef CONFIG_ARCH_OMAP1 + +static struct omap_dm_timer dm_timers[] = { + { .phys_base = 0xfffb1400, .irq = INT_1610_GPTIMER1 }, + { .phys_base = 0xfffb1c00, .irq = INT_1610_GPTIMER2 }, + { .phys_base = 0xfffb2400, .irq = INT_1610_GPTIMER3 }, + { .phys_base = 0xfffb2c00, .irq = INT_1610_GPTIMER4 }, + { .phys_base = 0xfffb3400, .irq = INT_1610_GPTIMER5 }, + { .phys_base = 0xfffb3c00, .irq = INT_1610_GPTIMER6 }, + { .phys_base = 0xfffb4400, .irq = INT_1610_GPTIMER7 }, + { .phys_base = 0xfffb4c00, .irq = INT_1610_GPTIMER8 }, +}; -static struct dmtimer_info_struct { - struct list_head unused_timers; - struct list_head reserved_timers; -} dm_timer_info; +#elif defined(CONFIG_ARCH_OMAP2) static struct omap_dm_timer dm_timers[] = { - { .base=0xfffb1400, .irq=INT_1610_GPTIMER1 }, - { .base=0xfffb1c00, .irq=INT_1610_GPTIMER2 }, - { .base=0xfffb2400, .irq=INT_1610_GPTIMER3 }, - { .base=0xfffb2c00, .irq=INT_1610_GPTIMER4 }, - { .base=0xfffb3400, .irq=INT_1610_GPTIMER5 }, - { .base=0xfffb3c00, .irq=INT_1610_GPTIMER6 }, - { .base=0xfffb4400, .irq=INT_1610_GPTIMER7 }, - { .base=0xfffb4c00, .irq=INT_1610_GPTIMER8 }, - { .base=0x0 }, + { .phys_base = 0x48028000, .irq = INT_24XX_GPTIMER1 }, + { .phys_base = 0x4802a000, .irq = INT_24XX_GPTIMER2 }, + { .phys_base = 0x48078000, .irq = INT_24XX_GPTIMER3 }, + { .phys_base = 0x4807a000, .irq = INT_24XX_GPTIMER4 }, + { .phys_base = 0x4807c000, .irq = INT_24XX_GPTIMER5 }, + { .phys_base = 0x4807e000, .irq = INT_24XX_GPTIMER6 }, + { .phys_base = 0x48080000, .irq = INT_24XX_GPTIMER7 }, + { .phys_base = 0x48082000, .irq = INT_24XX_GPTIMER8 }, + { .phys_base = 0x48084000, .irq = INT_24XX_GPTIMER9 }, + { .phys_base = 0x48086000, .irq = INT_24XX_GPTIMER10 }, + { .phys_base = 0x48088000, .irq = INT_24XX_GPTIMER11 }, + { .phys_base = 0x4808a000, .irq = INT_24XX_GPTIMER12 }, }; +#else + +#error OMAP architecture not supported! + +#endif + +static const int dm_timer_count = ARRAY_SIZE(dm_timers); static spinlock_t dm_timer_lock; +static inline u32 omap_dm_timer_read_reg(struct omap_dm_timer *timer, int reg) +{ + return readl(timer->io_base + reg); +} -inline void omap_dm_timer_write_reg(struct omap_dm_timer *timer, int reg, u32 value) +static void omap_dm_timer_write_reg(struct omap_dm_timer *timer, int reg, u32 value) { - omap_writel(value, timer->base + reg); + writel(value, timer->io_base + reg); while (omap_dm_timer_read_reg(timer, OMAP_TIMER_WRITE_PEND_REG)) ; } -u32 omap_dm_timer_read_reg(struct omap_dm_timer *timer, int reg) +static void omap_dm_timer_wait_for_reset(struct omap_dm_timer *timer) { - return omap_readl(timer->base + reg); + int c; + + c = 0; + while (!(omap_dm_timer_read_reg(timer, OMAP_TIMER_SYS_STAT_REG) & 1)) { + c++; + if (c > 100000) { + printk(KERN_ERR "Timer failed to reset\n"); + return; + } + } } -int omap_dm_timers_active(void) +static void omap_dm_timer_reset(struct omap_dm_timer *timer) +{ + u32 l; + + omap_dm_timer_write_reg(timer, OMAP_TIMER_IF_CTRL_REG, 0x06); + omap_dm_timer_wait_for_reset(timer); + + omap_dm_timer_set_source(timer, OMAP_TIMER_SRC_SYS_CLK); + + /* Set to smart-idle mode */ + l = omap_dm_timer_read_reg(timer, OMAP_TIMER_OCP_CFG_REG); + l |= 0x02 << 3; + omap_dm_timer_write_reg(timer, OMAP_TIMER_OCP_CFG_REG, l); +} + +static void omap_dm_timer_reserve(struct omap_dm_timer *timer) +{ + timer->reserved = 1; +#ifdef CONFIG_ARCH_OMAP2 + clk_enable(timer->iclk); + clk_enable(timer->fclk); +#endif + omap_dm_timer_reset(timer); +} + +struct omap_dm_timer *omap_dm_timer_request(void) +{ + struct omap_dm_timer *timer = NULL; + unsigned long flags; + int i; + + spin_lock_irqsave(&dm_timer_lock, flags); + for (i = 0; i < dm_timer_count; i++) { + if (dm_timers[i].reserved) + continue; + + timer = &dm_timers[i]; + omap_dm_timer_reserve(timer); + break; + } + spin_unlock_irqrestore(&dm_timer_lock, flags); + + return timer; +} + +struct omap_dm_timer *omap_dm_timer_request_specific(int id) { struct omap_dm_timer *timer; + unsigned long flags; - for (timer = &dm_timers[0]; timer->base; ++timer) - if (omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG) & - OMAP_TIMER_CTRL_ST) - return 1; + spin_lock_irqsave(&dm_timer_lock, flags); + if (id <= 0 || id > dm_timer_count || dm_timers[id-1].reserved) { + spin_unlock_irqrestore(&dm_timer_lock, flags); + printk("BUG: warning at %s:%d/%s(): unable to get timer %d\n", + __FILE__, __LINE__, __FUNCTION__, id); + dump_stack(); + return NULL; + } - return 0; + timer = &dm_timers[id-1]; + omap_dm_timer_reserve(timer); + spin_unlock_irqrestore(&dm_timer_lock, flags); + + return timer; } +void omap_dm_timer_free(struct omap_dm_timer *timer) +{ + omap_dm_timer_reset(timer); +#ifdef CONFIG_ARCH_OMAP2 + clk_disable(timer->iclk); + clk_disable(timer->fclk); +#endif + WARN_ON(!timer->reserved); + timer->reserved = 0; +} + +int omap_dm_timer_get_irq(struct omap_dm_timer *timer) +{ + return timer->irq; +} + +#if defined(CONFIG_ARCH_OMAP1) + +struct clk *omap_dm_timer_get_fclk(struct omap_dm_timer *timer) +{ + BUG(); +} /** * omap_dm_timer_modify_idlect_mask - Check if any running timers use ARMXOR @@ -103,184 +238,226 @@ int omap_dm_timers_active(void) */ __u32 omap_dm_timer_modify_idlect_mask(__u32 inputmask) { - int n; + int i; /* If ARMXOR cannot be idled this function call is unnecessary */ if (!(inputmask & (1 << 1))) return inputmask; /* If any active timer is using ARMXOR return modified mask */ - for (n = 0; dm_timers[n].base; ++n) - if (omap_dm_timer_read_reg(&dm_timers[n], OMAP_TIMER_CTRL_REG)& - OMAP_TIMER_CTRL_ST) { - if (((omap_readl(MOD_CONF_CTRL_1)>>(n*2)) & 0x03) == 0) + for (i = 0; i < dm_timer_count; i++) { + u32 l; + + l = omap_dm_timer_read_reg(&dm_timers[n], OMAP_TIMER_CTRL_REG); + if (l & OMAP_TIMER_CTRL_ST) { + if (((omap_readl(MOD_CONF_CTRL_1) >> (i * 2)) & 0x03) == 0) inputmask &= ~(1 << 1); else inputmask &= ~(1 << 2); } + } return inputmask; } +#elif defined(CONFIG_ARCH_OMAP2) -void omap_dm_timer_set_source(struct omap_dm_timer *timer, int source) +struct clk *omap_dm_timer_get_fclk(struct omap_dm_timer *timer) { - int n = (timer - dm_timers) << 1; - u32 l; + return timer->fclk; +} - l = omap_readl(MOD_CONF_CTRL_1) & ~(0x03 << n); - l |= source << n; - omap_writel(l, MOD_CONF_CTRL_1); +__u32 omap_dm_timer_modify_idlect_mask(__u32 inputmask) +{ + BUG(); } +#endif -static void omap_dm_timer_reset(struct omap_dm_timer *timer) +void omap_dm_timer_trigger(struct omap_dm_timer *timer) { - /* Reset and set posted mode */ - omap_dm_timer_write_reg(timer, OMAP_TIMER_IF_CTRL_REG, 0x06); - omap_dm_timer_write_reg(timer, OMAP_TIMER_OCP_CFG_REG, 0x02); - - omap_dm_timer_set_source(timer, OMAP_TIMER_SRC_ARMXOR); + omap_dm_timer_write_reg(timer, OMAP_TIMER_TRIGGER_REG, 0); } +void omap_dm_timer_start(struct omap_dm_timer *timer) +{ + u32 l; + l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); + if (!(l & OMAP_TIMER_CTRL_ST)) { + l |= OMAP_TIMER_CTRL_ST; + omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l); + } +} -struct omap_dm_timer * omap_dm_timer_request(void) +void omap_dm_timer_stop(struct omap_dm_timer *timer) { - struct omap_dm_timer *timer = NULL; - unsigned long flags; + u32 l; - spin_lock_irqsave(&dm_timer_lock, flags); - if (!list_empty(&dm_timer_info.unused_timers)) { - timer = (struct omap_dm_timer *) - dm_timer_info.unused_timers.next; - list_move_tail((struct list_head *)timer, - &dm_timer_info.reserved_timers); + l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); + if (l & OMAP_TIMER_CTRL_ST) { + l &= ~0x1; + omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l); } - spin_unlock_irqrestore(&dm_timer_lock, flags); - - return timer; } +#ifdef CONFIG_ARCH_OMAP1 -void omap_dm_timer_free(struct omap_dm_timer *timer) +void omap_dm_timer_set_source(struct omap_dm_timer *timer, int source) { - unsigned long flags; - - omap_dm_timer_reset(timer); + int n = (timer - dm_timers) << 1; + u32 l; - spin_lock_irqsave(&dm_timer_lock, flags); - list_move_tail((struct list_head *)timer, &dm_timer_info.unused_timers); - spin_unlock_irqrestore(&dm_timer_lock, flags); + l = omap_readl(MOD_CONF_CTRL_1) & ~(0x03 << n); + l |= source << n; + omap_writel(l, MOD_CONF_CTRL_1); } -void omap_dm_timer_set_int_enable(struct omap_dm_timer *timer, - unsigned int value) -{ - omap_dm_timer_write_reg(timer, OMAP_TIMER_INT_EN_REG, value); -} +#else -unsigned int omap_dm_timer_read_status(struct omap_dm_timer *timer) +void omap_dm_timer_set_source(struct omap_dm_timer *timer, int source) { - return omap_dm_timer_read_reg(timer, OMAP_TIMER_STAT_REG); + static const char *source_timers[] = { + "sys_ck", + "func_32k_ck", + "alt_ck" + }; + struct clk *parent; + + if (source < 0 || source >= 3) + return; + + parent = clk_get(NULL, source_timers[source]); + clk_disable(timer->fclk); + clk_set_parent(timer->fclk, parent); + clk_enable(timer->fclk); + clk_put(parent); + + /* When the functional clock disappears, too quick writes seem to + * cause an abort. */ + udelay(50); } -void omap_dm_timer_write_status(struct omap_dm_timer *timer, unsigned int value) -{ - omap_dm_timer_write_reg(timer, OMAP_TIMER_STAT_REG, value); -} +#endif -void omap_dm_timer_enable_autoreload(struct omap_dm_timer *timer) +void omap_dm_timer_set_load(struct omap_dm_timer *timer, int autoreload, + unsigned int load) { u32 l; + l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); - l |= OMAP_TIMER_CTRL_AR; + if (autoreload) + l |= OMAP_TIMER_CTRL_AR; + else + l &= ~OMAP_TIMER_CTRL_AR; omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l); + omap_dm_timer_write_reg(timer, OMAP_TIMER_LOAD_REG, load); + omap_dm_timer_write_reg(timer, OMAP_TIMER_TRIGGER_REG, 0); } -void omap_dm_timer_trigger(struct omap_dm_timer *timer) -{ - omap_dm_timer_write_reg(timer, OMAP_TIMER_TRIGGER_REG, 1); -} - -void omap_dm_timer_set_trigger(struct omap_dm_timer *timer, unsigned int value) +void omap_dm_timer_set_match(struct omap_dm_timer *timer, int enable, + unsigned int match) { u32 l; l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); - l |= value & 0x3; + if (enable) + l |= OMAP_TIMER_CTRL_CE; + else + l &= ~OMAP_TIMER_CTRL_CE; omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l); + omap_dm_timer_write_reg(timer, OMAP_TIMER_MATCH_REG, match); } -void omap_dm_timer_start(struct omap_dm_timer *timer) + +void omap_dm_timer_set_pwm(struct omap_dm_timer *timer, int def_on, + int toggle, int trigger) { u32 l; l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); - l |= OMAP_TIMER_CTRL_ST; + l &= ~(OMAP_TIMER_CTRL_GPOCFG | OMAP_TIMER_CTRL_SCPWM | + OMAP_TIMER_CTRL_PT | (0x03 << 10)); + if (def_on) + l |= OMAP_TIMER_CTRL_SCPWM; + if (toggle) + l |= OMAP_TIMER_CTRL_PT; + l |= trigger << 10; omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l); } -void omap_dm_timer_stop(struct omap_dm_timer *timer) +void omap_dm_timer_set_prescaler(struct omap_dm_timer *timer, int prescaler) { u32 l; l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); - l &= ~0x1; + l &= ~(OMAP_TIMER_CTRL_PRE | (0x07 << 2)); + if (prescaler >= 0x00 && prescaler <= 0x07) { + l |= OMAP_TIMER_CTRL_PRE; + l |= prescaler << 2; + } omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l); } -unsigned int omap_dm_timer_read_counter(struct omap_dm_timer *timer) +void omap_dm_timer_set_int_enable(struct omap_dm_timer *timer, + unsigned int value) { - return omap_dm_timer_read_reg(timer, OMAP_TIMER_COUNTER_REG); + omap_dm_timer_write_reg(timer, OMAP_TIMER_INT_EN_REG, value); } -void omap_dm_timer_reset_counter(struct omap_dm_timer *timer) +unsigned int omap_dm_timer_read_status(struct omap_dm_timer *timer) { - omap_dm_timer_write_reg(timer, OMAP_TIMER_COUNTER_REG, 0); + return omap_dm_timer_read_reg(timer, OMAP_TIMER_STAT_REG); } -void omap_dm_timer_set_load(struct omap_dm_timer *timer, unsigned int load) +void omap_dm_timer_write_status(struct omap_dm_timer *timer, unsigned int value) { - omap_dm_timer_write_reg(timer, OMAP_TIMER_LOAD_REG, load); + omap_dm_timer_write_reg(timer, OMAP_TIMER_STAT_REG, value); } -void omap_dm_timer_set_match(struct omap_dm_timer *timer, unsigned int match) +unsigned int omap_dm_timer_read_counter(struct omap_dm_timer *timer) { - omap_dm_timer_write_reg(timer, OMAP_TIMER_MATCH_REG, match); + return omap_dm_timer_read_reg(timer, OMAP_TIMER_COUNTER_REG); } -void omap_dm_timer_enable_compare(struct omap_dm_timer *timer) +int omap_dm_timers_active(void) { - u32 l; + int i; - l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); - l |= OMAP_TIMER_CTRL_CE; - omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l); -} + for (i = 0; i < dm_timer_count; i++) { + struct omap_dm_timer *timer; + timer = &dm_timers[i]; + if (omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG) & + OMAP_TIMER_CTRL_ST) + return 1; + } + return 0; +} -static inline void __dm_timer_init(void) +int omap_dm_timer_init(void) { struct omap_dm_timer *timer; + int i; + + if (!(cpu_is_omap16xx() || cpu_is_omap24xx())) + return -ENODEV; spin_lock_init(&dm_timer_lock); - INIT_LIST_HEAD(&dm_timer_info.unused_timers); - INIT_LIST_HEAD(&dm_timer_info.reserved_timers); - - timer = &dm_timers[0]; - while (timer->base) { - list_add_tail((struct list_head *)timer, &dm_timer_info.unused_timers); - omap_dm_timer_reset(timer); - timer++; + for (i = 0; i < dm_timer_count; i++) { +#ifdef CONFIG_ARCH_OMAP2 + char clk_name[16]; +#endif + + timer = &dm_timers[i]; + timer->io_base = (void __iomem *) io_p2v(timer->phys_base); +#ifdef CONFIG_ARCH_OMAP2 + sprintf(clk_name, "gpt%d_ick", i + 1); + timer->iclk = clk_get(NULL, clk_name); + sprintf(clk_name, "gpt%d_fck", i + 1); + timer->fclk = clk_get(NULL, clk_name); +#endif } -} -static int __init omap_dm_timer_init(void) -{ - if (cpu_is_omap16xx()) - __dm_timer_init(); return 0; } - -arch_initcall(omap_dm_timer_init); |