diff options
Diffstat (limited to 'arch/arm/plat-nomadik')
-rw-r--r-- | arch/arm/plat-nomadik/Kconfig | 1 | ||||
-rw-r--r-- | arch/arm/plat-nomadik/gpio.c | 52 | ||||
-rw-r--r-- | arch/arm/plat-nomadik/include/plat/pincfg.h | 70 | ||||
-rw-r--r-- | arch/arm/plat-nomadik/timer.c | 80 |
4 files changed, 112 insertions, 91 deletions
diff --git a/arch/arm/plat-nomadik/Kconfig b/arch/arm/plat-nomadik/Kconfig index 5da3f97c537..187f4e84bb2 100644 --- a/arch/arm/plat-nomadik/Kconfig +++ b/arch/arm/plat-nomadik/Kconfig @@ -14,6 +14,7 @@ if PLAT_NOMADIK config HAS_MTU bool + select HAVE_SCHED_CLOCK help Support for Multi Timer Unit. MTU provides access to multiple interrupt generating programmable diff --git a/arch/arm/plat-nomadik/gpio.c b/arch/arm/plat-nomadik/gpio.c index 85e6fd212a4..eda4e3a11a3 100644 --- a/arch/arm/plat-nomadik/gpio.c +++ b/arch/arm/plat-nomadik/gpio.c @@ -119,7 +119,7 @@ static void __nmk_gpio_make_output(struct nmk_gpio_chip *nmk_chip, } static void __nmk_config_pin(struct nmk_gpio_chip *nmk_chip, unsigned offset, - pin_cfg_t cfg) + pin_cfg_t cfg, bool sleep) { static const char *afnames[] = { [NMK_GPIO_ALT_GPIO] = "GPIO", @@ -145,11 +145,34 @@ static void __nmk_config_pin(struct nmk_gpio_chip *nmk_chip, unsigned offset, int output = PIN_DIR(cfg); int val = PIN_VAL(cfg); - dev_dbg(nmk_chip->chip.dev, "pin %d: af %s, pull %s, slpm %s (%s%s)\n", - pin, afnames[af], pullnames[pull], slpmnames[slpm], + dev_dbg(nmk_chip->chip.dev, "pin %d [%#lx]: af %s, pull %s, slpm %s (%s%s)\n", + pin, cfg, afnames[af], pullnames[pull], slpmnames[slpm], output ? "output " : "input", output ? (val ? "high" : "low") : ""); + if (sleep) { + int slpm_pull = PIN_SLPM_PULL(cfg); + int slpm_output = PIN_SLPM_DIR(cfg); + int slpm_val = PIN_SLPM_VAL(cfg); + + /* + * The SLPM_* values are normal values + 1 to allow zero to + * mean "same as normal". + */ + if (slpm_pull) + pull = slpm_pull - 1; + if (slpm_output) + output = slpm_output - 1; + if (slpm_val) + val = slpm_val - 1; + + dev_dbg(nmk_chip->chip.dev, "pin %d: sleep pull %s, dir %s, val %s\n", + pin, + slpm_pull ? pullnames[pull] : "same", + slpm_output ? (output ? "output" : "input") : "same", + slpm_val ? (val ? "high" : "low") : "same"); + } + if (output) __nmk_gpio_make_output(nmk_chip, offset, val); else { @@ -175,7 +198,7 @@ static void __nmk_config_pin(struct nmk_gpio_chip *nmk_chip, unsigned offset, * side-effects. The gpio can be manipulated later using standard GPIO API * calls. */ -int nmk_config_pin(pin_cfg_t cfg) +int nmk_config_pin(pin_cfg_t cfg, bool sleep) { struct nmk_gpio_chip *nmk_chip; int gpio = PIN_NUM(cfg); @@ -186,7 +209,7 @@ int nmk_config_pin(pin_cfg_t cfg) return -EINVAL; spin_lock_irqsave(&nmk_chip->lock, flags); - __nmk_config_pin(nmk_chip, gpio - nmk_chip->chip.base, cfg); + __nmk_config_pin(nmk_chip, gpio - nmk_chip->chip.base, cfg, sleep); spin_unlock_irqrestore(&nmk_chip->lock, flags); return 0; @@ -207,7 +230,7 @@ int nmk_config_pins(pin_cfg_t *cfgs, int num) int i; for (i = 0; i < num; i++) { - int ret = nmk_config_pin(cfgs[i]); + ret = nmk_config_pin(cfgs[i], false); if (ret) break; } @@ -216,6 +239,21 @@ int nmk_config_pins(pin_cfg_t *cfgs, int num) } EXPORT_SYMBOL(nmk_config_pins); +int nmk_config_pins_sleep(pin_cfg_t *cfgs, int num) +{ + int ret = 0; + int i; + + for (i = 0; i < num; i++) { + ret = nmk_config_pin(cfgs[i], true); + if (ret) + break; + } + + return ret; +} +EXPORT_SYMBOL(nmk_config_pins_sleep); + /** * nmk_gpio_set_slpm() - configure the sleep mode of a pin * @gpio: pin number @@ -634,7 +672,7 @@ static int __devinit nmk_gpio_probe(struct platform_device *dev) chip = &nmk_chip->chip; chip->base = pdata->first_gpio; - chip->label = pdata->name; + chip->label = pdata->name ?: dev_name(&dev->dev); chip->dev = &dev->dev; chip->owner = THIS_MODULE; diff --git a/arch/arm/plat-nomadik/include/plat/pincfg.h b/arch/arm/plat-nomadik/include/plat/pincfg.h index 8c5ae3f2acf..05a3936ae6d 100644 --- a/arch/arm/plat-nomadik/include/plat/pincfg.h +++ b/arch/arm/plat-nomadik/include/plat/pincfg.h @@ -19,16 +19,22 @@ * bit 9..10 - Alternate Function Selection * bit 11..12 - Pull up/down state * bit 13 - Sleep mode behaviour - * bit 14 - (sleep mode) Direction - * bit 15 - (sleep mode) Value (if output) + * bit 14 - Direction + * bit 15 - Value (if output) + * bit 16..18 - SLPM pull up/down state + * bit 19..20 - SLPM direction + * bit 21..22 - SLPM Value (if output) * * to facilitate the definition, the following macros are provided * * PIN_CFG_DEFAULT - default config (0): * pull up/down = disabled * sleep mode = input/wakeup - * (sleep mode) direction = input - * (sleep mode) value = low + * direction = input + * value = low + * SLPM direction = same as normal + * SLPM pull = same as normal + * SLPM value = same as normal * * PIN_CFG - default config with alternate function * PIN_CFG_PULL - default config with alternate function and pull up/down @@ -75,30 +81,64 @@ typedef unsigned long pin_cfg_t; #define PIN_VAL_LOW (0 << PIN_VAL_SHIFT) #define PIN_VAL_HIGH (1 << PIN_VAL_SHIFT) -/* Shortcuts. Use these instead of separate DIR and VAL. */ -#define PIN_INPUT PIN_DIR_INPUT +#define PIN_SLPM_PULL_SHIFT 16 +#define PIN_SLPM_PULL_MASK (0x7 << PIN_SLPM_PULL_SHIFT) +#define PIN_SLPM_PULL(x) \ + (((x) & PIN_SLPM_PULL_MASK) >> PIN_SLPM_PULL_SHIFT) +#define PIN_SLPM_PULL_NONE \ + ((1 + NMK_GPIO_PULL_NONE) << PIN_SLPM_PULL_SHIFT) +#define PIN_SLPM_PULL_UP \ + ((1 + NMK_GPIO_PULL_UP) << PIN_SLPM_PULL_SHIFT) +#define PIN_SLPM_PULL_DOWN \ + ((1 + NMK_GPIO_PULL_DOWN) << PIN_SLPM_PULL_SHIFT) + +#define PIN_SLPM_DIR_SHIFT 19 +#define PIN_SLPM_DIR_MASK (0x3 << PIN_SLPM_DIR_SHIFT) +#define PIN_SLPM_DIR(x) \ + (((x) & PIN_SLPM_DIR_MASK) >> PIN_SLPM_DIR_SHIFT) +#define PIN_SLPM_DIR_INPUT ((1 + 0) << PIN_SLPM_DIR_SHIFT) +#define PIN_SLPM_DIR_OUTPUT ((1 + 1) << PIN_SLPM_DIR_SHIFT) + +#define PIN_SLPM_VAL_SHIFT 21 +#define PIN_SLPM_VAL_MASK (0x3 << PIN_SLPM_VAL_SHIFT) +#define PIN_SLPM_VAL(x) \ + (((x) & PIN_SLPM_VAL_MASK) >> PIN_SLPM_VAL_SHIFT) +#define PIN_SLPM_VAL_LOW ((1 + 0) << PIN_SLPM_VAL_SHIFT) +#define PIN_SLPM_VAL_HIGH ((1 + 1) << PIN_SLPM_VAL_SHIFT) + +/* Shortcuts. Use these instead of separate DIR, PULL, and VAL. */ +#define PIN_INPUT_PULLDOWN (PIN_DIR_INPUT | PIN_PULL_DOWN) +#define PIN_INPUT_PULLUP (PIN_DIR_INPUT | PIN_PULL_UP) +#define PIN_INPUT_NOPULL (PIN_DIR_INPUT | PIN_PULL_NONE) #define PIN_OUTPUT_LOW (PIN_DIR_OUTPUT | PIN_VAL_LOW) #define PIN_OUTPUT_HIGH (PIN_DIR_OUTPUT | PIN_VAL_HIGH) -/* - * These are the same as the ones above, but should make more sense to the - * reader when seen along with a setting a pin to AF mode. - */ -#define PIN_SLPM_INPUT PIN_INPUT -#define PIN_SLPM_OUTPUT_LOW PIN_OUTPUT_LOW -#define PIN_SLPM_OUTPUT_HIGH PIN_OUTPUT_HIGH +#define PIN_SLPM_INPUT_PULLDOWN (PIN_SLPM_DIR_INPUT | PIN_SLPM_PULL_DOWN) +#define PIN_SLPM_INPUT_PULLUP (PIN_SLPM_DIR_INPUT | PIN_SLPM_PULL_UP) +#define PIN_SLPM_INPUT_NOPULL (PIN_SLPM_DIR_INPUT | PIN_SLPM_PULL_NONE) +#define PIN_SLPM_OUTPUT_LOW (PIN_SLPM_DIR_OUTPUT | PIN_SLPM_VAL_LOW) +#define PIN_SLPM_OUTPUT_HIGH (PIN_SLPM_DIR_OUTPUT | PIN_SLPM_VAL_HIGH) -#define PIN_CFG_DEFAULT (PIN_PULL_NONE | PIN_SLPM_INPUT) +#define PIN_CFG_DEFAULT (0) #define PIN_CFG(num, alt) \ (PIN_CFG_DEFAULT |\ (PIN_NUM(num) | PIN_##alt)) +#define PIN_CFG_INPUT(num, alt, pull) \ + (PIN_CFG_DEFAULT |\ + (PIN_NUM(num) | PIN_##alt | PIN_INPUT_##pull)) + +#define PIN_CFG_OUTPUT(num, alt, val) \ + (PIN_CFG_DEFAULT |\ + (PIN_NUM(num) | PIN_##alt | PIN_OUTPUT_##val)) + #define PIN_CFG_PULL(num, alt, pull) \ ((PIN_CFG_DEFAULT & ~PIN_PULL_MASK) |\ (PIN_NUM(num) | PIN_##alt | PIN_PULL_##pull)) -extern int nmk_config_pin(pin_cfg_t cfg); +extern int nmk_config_pin(pin_cfg_t cfg, bool sleep); extern int nmk_config_pins(pin_cfg_t *cfgs, int num); +extern int nmk_config_pins_sleep(pin_cfg_t *cfgs, int num); #endif diff --git a/arch/arm/plat-nomadik/timer.c b/arch/arm/plat-nomadik/timer.c index 63cdc6025bd..41723402006 100644 --- a/arch/arm/plat-nomadik/timer.c +++ b/arch/arm/plat-nomadik/timer.c @@ -17,9 +17,9 @@ #include <linux/clk.h> #include <linux/jiffies.h> #include <linux/err.h> -#include <linux/cnt32_to_63.h> -#include <linux/timer.h> +#include <linux/sched.h> #include <asm/mach/time.h> +#include <asm/sched_clock.h> #include <plat/mtu.h> @@ -52,81 +52,24 @@ static struct clocksource nmdk_clksrc = { * Override the global weak sched_clock symbol with this * local implementation which uses the clocksource to get some * better resolution when scheduling the kernel. - * - * Because the hardware timer period may be quite short - * (32.3 secs on the 133 MHz MTU timer selection on ux500) - * and because cnt32_to_63() needs to be called at least once per - * half period to work properly, a kernel keepwarm() timer is set up - * to ensure this requirement is always met. - * - * Also the sched_clock timer will wrap around at some point, - * here we set it to run continously for a year. */ -#define SCHED_CLOCK_MIN_WRAP 3600*24*365 -static struct timer_list cnt32_to_63_keepwarm_timer; -static u32 sched_mult; -static u32 sched_shift; +static DEFINE_CLOCK_DATA(cd); unsigned long long notrace sched_clock(void) { - u64 cycles; + u32 cyc; if (unlikely(!mtu_base)) return 0; - cycles = cnt32_to_63(-readl(mtu_base + MTU_VAL(0))); - /* - * sched_mult is guaranteed to be even so will - * shift out bit 63 - */ - return (cycles * sched_mult) >> sched_shift; + cyc = -readl(mtu_base + MTU_VAL(0)); + return cyc_to_sched_clock(&cd, cyc, (u32)~0); } -/* Just kick sched_clock every so often */ -static void cnt32_to_63_keepwarm(unsigned long data) +static void notrace nomadik_update_sched_clock(void) { - mod_timer(&cnt32_to_63_keepwarm_timer, round_jiffies(jiffies + data)); - (void) sched_clock(); -} - -/* - * Set up a timer to keep sched_clock():s 32_to_63 algorithm warm - * once in half a 32bit timer wrap interval. - */ -static void __init nmdk_sched_clock_init(unsigned long rate) -{ - u32 v; - unsigned long delta; - u64 days; - - /* Find the apropriate mult and shift factors */ - clocks_calc_mult_shift(&sched_mult, &sched_shift, - rate, NSEC_PER_SEC, SCHED_CLOCK_MIN_WRAP); - /* We need to multiply by an even number to get rid of bit 63 */ - if (sched_mult & 1) - sched_mult++; - - /* Let's see what we get, take max counter and scale it */ - days = (0xFFFFFFFFFFFFFFFFLLU * sched_mult) >> sched_shift; - do_div(days, NSEC_PER_SEC); - do_div(days, (3600*24)); - - pr_info("sched_clock: using %d bits @ %lu Hz wrap in %lu days\n", - (64 - sched_shift), rate, (unsigned long) days); - - /* - * Program a timer to kick us at half 32bit wraparound - * Formula: seconds per wrap = (2^32) / f - */ - v = 0xFFFFFFFFUL / rate; - /* We want half of the wrap time to keep cnt32_to_63 warm */ - v /= 2; - pr_debug("sched_clock: prescaled timer rate: %lu Hz, " - "initialize keepwarm timer every %d seconds\n", rate, v); - /* Convert seconds to jiffies */ - delta = msecs_to_jiffies(v*1000); - setup_timer(&cnt32_to_63_keepwarm_timer, cnt32_to_63_keepwarm, delta); - mod_timer(&cnt32_to_63_keepwarm_timer, round_jiffies(jiffies + delta)); + u32 cyc = -readl(mtu_base + MTU_VAL(0)); + update_sched_clock(&cd, cyc, (u32)~0); } /* Clockevent device: use one-shot mode */ @@ -222,7 +165,6 @@ void __init nmdk_timer_init(void) } else { cr |= MTU_CRn_PRESCALE_1; } - clocksource_calc_mult_shift(&nmdk_clksrc, rate, MTU_MIN_RANGE); /* Timer 0 is the free running clocksource */ writel(cr, mtu_base + MTU_CR(0)); @@ -233,11 +175,11 @@ void __init nmdk_timer_init(void) /* Now the clock source is ready */ nmdk_clksrc.read = nmdk_read_timer; - if (clocksource_register(&nmdk_clksrc)) + if (clocksource_register_hz(&nmdk_clksrc, rate)) pr_err("timer: failed to initialize clock source %s\n", nmdk_clksrc.name); - nmdk_sched_clock_init(rate); + init_sched_clock(&cd, nomadik_update_sched_clock, 32, rate); /* Timer 1 is used for events */ |