diff options
author | Gary King <gking@nvidia.com> | 2010-03-22 21:41:47 -0700 |
---|---|---|
committer | Gary King <gking@nvidia.com> | 2010-03-24 16:14:24 -0800 |
commit | 339232a58f8e29f832d2baa4679e7e4aa31c9e4c (patch) | |
tree | c72a8892b0b0d5312f2125119a10ed3f74875b03 /arch/arm/mach-tegra | |
parent | 21f99c1a2bbc26b57035a09f6bd02ed2737f47b3 (diff) |
tegra: implement native timer support
replace the existing timer code with a kernel-style implementation
reset the lp2 spare trigger to 0 following an lp2 wakeup, to prevent
a spurious interrupt from triggering if the wakeup source was not
the lp2 timer.
Change-Id: I4af642b15f024e43cf88f19751bdaad5279ebd9b
Reviewed-on: http://git-master/r/936
Reviewed-by: Gary King <gking@nvidia.com>
Tested-by: Gary King <gking@nvidia.com>
Diffstat (limited to 'arch/arm/mach-tegra')
-rw-r--r-- | arch/arm/mach-tegra/idle-t2.c | 5 | ||||
-rw-r--r-- | arch/arm/mach-tegra/include/mach/iomap.h | 214 | ||||
-rw-r--r-- | arch/arm/mach-tegra/include/mach/irqs.h | 2 | ||||
-rw-r--r-- | arch/arm/mach-tegra/timer.c | 364 |
4 files changed, 367 insertions, 218 deletions
diff --git a/arch/arm/mach-tegra/idle-t2.c b/arch/arm/mach-tegra/idle-t2.c index 2fa7457fc4d2..d9c4ea398b9f 100644 --- a/arch/arm/mach-tegra/idle-t2.c +++ b/arch/arm/mach-tegra/idle-t2.c @@ -65,7 +65,7 @@ extern void enter_lp2(NvU32, NvU32); extern void exit_power_state(void); extern void module_context_init(void); extern void power_lp0_init(void); -extern void NvSpareTimerTrigger(unsigned long); /* timer.c */ +extern void tegra_lp2_set_trigger(unsigned long); NvRmMemHandle s_hWarmboot = NULL; NvU32 g_AvpWarmbootEntry; NvU32 g_IramPA = 0; @@ -312,8 +312,9 @@ void mach_tegra_idle(void) if (lp2_ok) { sleep_time -= LP2_ROUNDTRIP_TIME_US; - NvSpareTimerTrigger(sleep_time); + tegra_lp2_set_trigger(sleep_time); cpu_ap20_do_lp2(); + tegra_lp2_set_trigger(0); /* add the actual amount of time spent in lp2 to the timers */ sleep_time = NV_REGR(s_hRmGlobal, NvRmModuleID_Pmif, 0, APBDEV_PMC_SCRATCH1_0); diff --git a/arch/arm/mach-tegra/include/mach/iomap.h b/arch/arm/mach-tegra/include/mach/iomap.h new file mode 100644 index 000000000000..3e3df252c41a --- /dev/null +++ b/arch/arm/mach-tegra/include/mach/iomap.h @@ -0,0 +1,214 @@ +/* + * arch/arm/mach-tegra/include/mach/iomap.h + * + * Copyright (C) 2010 Google, Inc. + * + * Author: + * Colin Cross <ccross@google.com> + * Erik Gilling <konkers@google.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#ifndef __MACH_TEGRA_IOMAP_H +#define __MACH_TEGRA_IOMAP_H + +#include <mach/io.h> +#include <asm/sizes.h> + +#ifdef CONFIG_ARCH_TEGRA_2x_SOC + +#define TEGRA_ARM_PERIF_BASE 0x50040000 +#define TEGRA_ARM_PERIF_SIZE SZ_8K + +#define TEGRA_SCU_BASE 0x50040000 +#define TEGRA_SCU_SIZE SZ_256 + +#define TEGRA_GIC_PROC_IF_BASE 0x50040100 +#define TEGRA_GIC_PROC_IF_SIZE SZ_256 + +#define TEGRA_ARM_INT_DIST_BASE 0x50041000 +#define TEGRA_ARM_INT_DIST_SIZE SZ_4K + +#define TEGRA_DISPLAY_BASE 0x54200000 +#define TEGRA_DISPLAY_SIZE SZ_256K + +#define TEGRA_DISPLAY2_BASE 0x54240000 +#define TEGRA_DISPLAY2_SIZE SZ_256K + +#define TEGRA_PRIMARY_ICTLR_BASE 0x60004000 +#define TEGRA_PRIMARY_ICTLR_SIZE SZ_64 + +#define TEGRA_SECONDARY_ICTLR_BASE 0x60004100 +#define TEGRA_SECONDARY_ICTLR_SIZE SZ_64 + +#define TEGRA_TERTIARY_ICTLR_BASE 0x60004200 +#define TEGRA_TERTIARY_ICTLR_SIZE SZ_64 + +#define TEGRA_QUATERNARY_ICTLR_BASE 0x60004300 +#define TEGRA_QUATERNARY_ICTLR_SIZE SZ_64 + +#define TEGRA_TMR1_BASE 0x60005000 +#define TEGRA_TMR1_SIZE SZ_8 + +#define TEGRA_TMR2_BASE 0x60005008 +#define TEGRA_TMR2_SIZE SZ_8 + +#define TEGRA_TMRUS_BASE 0x60005010 +#define TEGRA_TMRUS_SIZE SZ_64 + +#define TEGRA_TMR3_BASE 0x60005050 +#define TEGRA_TMR3_SIZE SZ_8 + +#define TEGRA_TMR4_BASE 0x60005058 +#define TEGRA_TMR4_SIZE SZ_8 + +#define TEGRA_CLK_RESET_BASE 0x60006000 +#define TEGRA_CLK_RESET_SIZE SZ_4K + +#define TEGRA_FLOW_CTRL_BASE 0x60007000 +#define TEGRA_FLOW_CTRL_SIZE 20 + +#define TEGRA_STATMON_BASE 0x6000C4000 +#define TEGRA_STATMON_SIZE SZ_1K + +#define TEGRA_GPIO_BASE 0x6000D000 +#define TEGRA_GPIO_SIZE SZ_4K + +#define TEGRA_EXCEPTION_VECTORS_BASE 0x6000F000 +#define TEGRA_EXCEPTION_VECTORS_SIZE SZ_4K + +#define TEGRA_APB_MISC_BASE 0x70000000 +#define TEGRA_APB_MISC_SIZE SZ_4K + +#define TEGRA_AC97_BASE 0x70002000 +#define TEGRA_AC97_SIZE SZ_512 + +#define TEGRA_SPDIF_BASE 0x70002400 +#define TEGRA_SPDIF_SIZE SZ_512 + +#define TEGRA_I2S1_BASE 0x70002800 +#define TEGRA_I2S1_SIZE SZ_256 + +#define TEGRA_I2S2_BASE 0x70002A00 +#define TEGRA_I2S2_SIZE SZ_256 + +#define TEGRA_UARTA_BASE 0x70006000 +#define TEGRA_UARTA_SIZE SZ_64 + +#define TEGRA_UARTB_BASE 0x70006040 +#define TEGRA_UARTB_SIZE SZ_64 + +#define TEGRA_UARTC_BASE 0x70006200 +#define TEGRA_UARTC_SIZE SZ_256 + +#define TEGRA_UARTD_BASE 0x70006300 +#define TEGRA_UARTD_SIZE SZ_256 + +#define TEGRA_UARTE_BASE 0x70006400 +#define TEGRA_UARTE_SIZE SZ_256 + +#define TEGRA_NAND_BASE 0x70008000 +#define TEGRA_NAND_SIZE SZ_256 + +#define TEGRA_HSMMC_BASE 0x70008500 +#define TEGRA_HSMMC_SIZE SZ_256 + +#define TEGRA_SNOR_BASE 0x70009000 +#define TEGRA_SNOR_SIZE SZ_4K + +#define TEGRA_PWFM_BASE 0x7000A000 +#define TEGRA_PWFM_SIZE SZ_256 + +#define TEGRA_MIPI_BASE 0x7000B000 +#define TEGRA_MIPI_SIZE SZ_256 + +#define TEGRA_I2C_BASE 0x7000C000 +#define TEGRA_I2C_SIZE SZ_256 + +#define TEGRA_TWC_BASE 0x7000C100 +#define TEGRA_TWC_SIZE SZ_256 + +#define TEGRA_SPI_BASE 0x7000C380 +#define TEGRA_SPI_SIZE 48 + +#define TEGRA_I2C2_BASE 0x7000C400 +#define TEGRA_I2C2_SIZE SZ_256 + +#define TEGRA_I2C3_BASE 0x7000C500 +#define TEGRA_I2C3_SIZE SZ_256 + +#define TEGRA_OWR_BASE 0x7000D000 +#define TEGRA_OWR_SIZE 80 + +#define TEGRA_DVC_BASE 0x7000D000 +#define TEGRA_DVC_SIZE SZ_512 + +#define TEGRA_SPI1_BASE 0x7000D400 +#define TEGRA_SPI1_SIZE SZ_512 + +#define TEGRA_SPI2_BASE 0x7000D600 +#define TEGRA_SPI2_SIZE SZ_512 + +#define TEGRA_SPI3_BASE 0x7000D800 +#define TEGRA_SPI3_SIZE SZ_512 + +#define TEGRA_SPI4_BASE 0x7000DA00 +#define TEGRA_SPI4_SIZE SZ_512 + +#define TEGRA_RTC_BASE 0x7000E000 +#define TEGRA_RTC_SIZE SZ_256 + +#define TEGRA_KBC_BASE 0x7000E200 +#define TEGRA_KBC_SIZE SZ_256 + +#define TEGRA_PMC_BASE 0x7000E400 +#define TEGRA_PMC_SIZE SZ_256 + +#define TEGRA_MC_BASE 0x7000F000 +#define TEGRA_MC_SIZE SZ_1K + +#define TEGRA_EMC_BASE 0x7000F400 +#define TEGRA_EMC_SIZE SZ_1K + +#define TEGRA_FUSE_BASE 0x7000F800 +#define TEGRA_FUSE_SIZE SZ_1K + +#define TEGRA_KFUSE_BASE 0x7000FC00 +#define TEGRA_KFUSE_SIZE SZ_1K + +#define TEGRA_CSITE_BASE 0x70040000 +#define TEGRA_CSITE_SIZE SZ_256K + +#define TEGRA_USB_BASE 0xC5000000 +#define TEGRA_USB_SIZE SZ_16K + +#define TEGRA_USB1_BASE 0xC5004000 +#define TEGRA_USB1_SIZE SZ_16K + +#define TEGRA_USB2_BASE 0xC5008000 +#define TEGRA_USB2_SIZE SZ_16K + +#define TEGRA_SDMMC1_BASE 0xC8000000 +#define TEGRA_SDMMC1_SIZE SZ_512 + +#define TEGRA_SDMMC2_BASE 0xC8000200 +#define TEGRA_SDMMC2_SIZE SZ_512 + +#define TEGRA_SDMMC3_BASE 0xC8000400 +#define TEGRA_SDMMC3_SIZE SZ_512 + +#define TEGRA_SDMMC4_BASE 0xC8000600 +#define TEGRA_SDMMC4_SIZE SZ_512 + +#endif /* CONFIG_ARCH_TEGRA_2x_SOC */ + +#endif diff --git a/arch/arm/mach-tegra/include/mach/irqs.h b/arch/arm/mach-tegra/include/mach/irqs.h index 0851d73947b9..b97456f73aa8 100644 --- a/arch/arm/mach-tegra/include/mach/irqs.h +++ b/arch/arm/mach-tegra/include/mach/irqs.h @@ -35,6 +35,8 @@ #define INT_GPIO2 (INT_SEC_BASE + 1) #define INT_GPIO3 (INT_SEC_BASE + 2) #define INT_GPIO4 (INT_SEC_BASE + 3) +#define INT_TMR3 (INT_SEC_BASE + 9) +#define INT_TMR4 (INT_SEC_BASE + 10) #define INT_SYS_STATS_MON (INT_SEC_BASE + 22) #define INT_GPIO5 (INT_SEC_BASE + 23) diff --git a/arch/arm/mach-tegra/timer.c b/arch/arm/mach-tegra/timer.c index 8ccec6551f53..f69dee4bf1ff 100644 --- a/arch/arm/mach-tegra/timer.c +++ b/arch/arm/mach-tegra/timer.c @@ -27,257 +27,189 @@ #include <linux/clockchips.h> #include <asm/mach/time.h> #include <asm/io.h> +#include <mach/iomap.h> -#include "ap15/artimer.h" -#include "ap15/artimerus.h" -#include "ap15/arclk_rst.h" -#include "nvcommon.h" -#include "nvrm_drf.h" -#include "nvrm_init.h" -#include "nvrm_module.h" -#include "nvrm_interrupt.h" -#include "nvrm_hardware_access.h" -#include "mach/nvrm_linux.h" -#include "nvos.h" -#include "nvassert.h" - -/* CPU complex uses timer instance 2 */ -#define NV_CPU_TIMER_INSTANCE 2 -#define NV_CPU_SPARE_TIMER_INSTANCE 3 - -static volatile NvU8 *s_NvTimerTick; -static volatile NvU8 *s_NvTimerUsec; - -static irqreturn_t NvTimerIntrHandler( - int irq, - void *dev_id) -{ - struct clock_event_device *dev = - (struct clock_event_device *)dev_id; - NV_WRITE32(s_NvTimerTick + TIMER_TMR_PCR_0, - NV_DRF_NUM(TIMER, TMR_PCR, INTR_CLR, 1)); - BUG_ON(dev == NULL); - dev->event_handler(dev); - return IRQ_HANDLED; -} +struct tegra_timer { + void __iomem *mmio; + struct clock_event_device event; +}; -static inline void NvTimerUpdateHardware( - unsigned long cycles, - int periodic) -{ - NvU32 v; - v = NV_DRF_NUM(TIMER, TMR_PTV, TMR_PTV, cycles); - if (periodic) { - v |= NV_DRF_DEF(TIMER, TMR_PTV, PER, ENABLE); - } - v |= NV_DRF_DEF(TIMER, TMR_PTV, EN, ENABLE); - NV_WRITE32(s_NvTimerTick + TIMER_TMR_PTV_0, v); -} +#define CLK_RST_CONTROLLER_OSC_CTRL_0 0x50 + +#define TIMER1_OFFS 0x00 /* reserved for AVP */ +#define TIMER2_OFFS 0x08 /* reserved for AVP */ +#define TIMER3_OFFS 0x50 /* used as OS CPU event timer */ +#define TIMER4_OFFS 0x58 /* reserved as LP2 wakeup trigger */ -static inline int NvTimerSetEvent( - unsigned long cycles, - struct clock_event_device *dev) +#define TIMER_TMR_PTV_0 0x0 +#define TIMER_TMR_PCR_0 0x4 + +#define TIMERUS_OFFS 0x10 +#define TIMERUS_CNTR_1US_0 0x0 +#define TIMERUS_USEC_CFG_0 0x4 + +static int tegra_event_set_next(unsigned long cycles, + struct clock_event_device *dev) { - NvTimerUpdateHardware(cycles, 0); - return 0; + struct tegra_timer *tmr = container_of(dev, struct tegra_timer, event); + u32 reg; + + reg = 0x80000000 | ((1000000/HZ)*(cycles+1)-1); + writel(reg, tmr->mmio + TIMER_TMR_PTV_0); + + return 0; } -static inline void NvTimerSetMode( - enum clock_event_mode mode, - struct clock_event_device *dev) +static void tegra_event_set_mode(enum clock_event_mode mode, + struct clock_event_device *dev) { - - NV_WRITE32(s_NvTimerTick + TIMER_TMR_PTV_0, 0); - - switch (mode) { - case CLOCK_EVT_MODE_PERIODIC: - NvTimerUpdateHardware((1000000/HZ)-1, 1); - break; - case CLOCK_EVT_MODE_ONESHOT: - break; - case CLOCK_EVT_MODE_RESUME: - case CLOCK_EVT_MODE_SHUTDOWN: - case CLOCK_EVT_MODE_UNUSED: - break; + struct tegra_timer *tmr = container_of(dev, struct tegra_timer, event); + u32 reg; + + writel(0, tmr->mmio + TIMER_TMR_PTV_0); + + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + reg = 0xc0000000ul | ((1000000/HZ)-1); + writel(reg, tmr->mmio + TIMER_TMR_PTV_0); + break; + case CLOCK_EVT_MODE_ONESHOT: + break; + case CLOCK_EVT_MODE_RESUME: + case CLOCK_EVT_MODE_SHUTDOWN: + case CLOCK_EVT_MODE_UNUSED: + break; } } -static cycle_t NvReadTimerUs(void) +static struct tegra_timer tegra_clockevent = { + .mmio = IO_ADDRESS(TEGRA_TMR1_BASE + TIMER3_OFFS), + .event = { + .name = "timer_event", + .rating = 300, + .features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC, + .irq = INT_TMR3, + .mult = 16777, + .shift = 24, + .set_next_event = tegra_event_set_next, + .set_mode = tegra_event_set_mode, + }, +}; + +static irqreturn_t tegra_clockevent_interrupt(int irq, void *dev_id) { - return (cycle_t) NV_READ32(s_NvTimerUsec + TIMERUS_CNTR_1US_0); + struct tegra_timer *tmr = (struct tegra_timer *)dev_id; + + writel(1<<30, tmr->mmio + TIMER_TMR_PCR_0); + tmr->event.event_handler(&tmr->event); + return IRQ_HANDLED; } -/* Timers on Tegra run at microsecond resolution, so the - * best way to approximate a divide by 1000 (ns-to-cycle) - * and maintain a decent amount of precision is to multiply - * by 16777 and shift right by 24. This causes 1000.01ns - * in kernel time to represent 1000ns in actual time. */ - -static struct clock_event_device s_NvTimer = { - .name = "timer0", - .rating = 300, - .features = CLOCK_EVT_FEAT_ONESHOT, - .mult = 16777, - .shift = 24, - .set_next_event = NvTimerSetEvent, - .set_mode = NvTimerSetMode, +static struct irqaction tegra_clockevent_irq = { + .name = "timer_event", + .irq = INT_TMR3, + .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_TRIGGER_HIGH, + .handler = tegra_clockevent_interrupt, + .dev_id = &tegra_clockevent, }; -static struct irqaction s_NvTimerIrq = { - .name = "timer0", - .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_TRIGGER_HIGH, - .handler = NvTimerIntrHandler, - .dev_id = &s_NvTimer, -}; +static cycle_t tegra_clocksource_read(void) +{ + void __iomem *tmr = IO_ADDRESS(TEGRA_TMR1_BASE + TIMERUS_OFFS); + return (cycle_t) readl(tmr + TIMERUS_CNTR_1US_0); +} -/* Converting from clock-cycles to nanoseconds is trivial - - * just multiply by 1000 */ -static struct clocksource s_NvClockUs = +static struct clocksource tegra_clocksource = { - .name = "timer_us", - .rating = 300, - .read = NvReadTimerUs, - .mask = 0xFFFFFFFFUL, - .mult = 1000, - .shift = 0, - .flags = CLOCK_SOURCE_IS_CONTINUOUS, + .name = "timer_us", + .rating = 300, + .read = tegra_clocksource_read, + .mask = 0xFFFFFFFFUL, + .mult = 1000, + .shift = 0, + .flags = CLOCK_SOURCE_IS_CONTINUOUS, }; -static void NvSpareTimerInit(void); - -static void __init tegra_timer_init(void) +static irqreturn_t tegra_lp2wake_interrupt(int irq, void *dev_id) { - NvRmPhysAddr Phys; - NvU32 Len; - volatile NvU8 *pCar = NULL; - NvU32 OscFreq; - - NvRmModuleGetBaseAddress(s_hRmGlobal, - NVRM_MODULE_ID(NvRmPrivModuleID_ClockAndReset, 0), &Phys, &Len); - if (NvRmPhysicalMemMap(Phys, Len, NVOS_MEM_READ_WRITE, - NvOsMemAttribute_Uncached, (void **)&pCar)!=NvSuccess) - { - NV_ASSERT(!"Error: Unable to get clock controller base address\n"); - } - - OscFreq = NV_READ32(pCar + CLK_RST_CONTROLLER_OSC_CTRL_0); - - NvRmModuleGetBaseAddress(s_hRmGlobal, - NVRM_MODULE_ID(NvRmModuleID_TimerUs, 0), &Phys, &Len); - - if (NvRmPhysicalMemMap(Phys, Len, NVOS_MEM_READ_WRITE, - NvOsMemAttribute_Uncached, (void **)&s_NvTimerUsec)!=NvSuccess) - { - NV_ASSERT(!"ERROR: Unable to get microsecod timer base address\n"); - } - - switch (NV_DRF_VAL(CLK_RST_CONTROLLER, OSC_CTRL, OSC_FREQ, OscFreq)) - { - case 0: // 13MHz - NV_WRITE32(s_NvTimerUsec + TIMERUS_USEC_CFG_0, - NV_DRF_NUM(TIMERUS, USEC_CFG, USEC_DIVIDEND, 0) | - NV_DRF_NUM(TIMERUS, USEC_CFG, USEC_DIVISOR, 12)); - break; - case 1: // 19.2 MHz - NV_WRITE32(s_NvTimerUsec + TIMERUS_USEC_CFG_0, - NV_DRF_NUM(TIMERUS, USEC_CFG, USEC_DIVIDEND, 4) | - NV_DRF_NUM(TIMERUS, USEC_CFG, USEC_DIVISOR, 95)); - break; - case 2: // 12 MHz - NV_WRITE32(s_NvTimerUsec + TIMERUS_USEC_CFG_0, - NV_DRF_NUM(TIMERUS, USEC_CFG, USEC_DIVIDEND, 0) | - NV_DRF_NUM(TIMERUS, USEC_CFG, USEC_DIVISOR, 11)); - break; - case 3: // 26 MHz - default: - NV_WRITE32(s_NvTimerUsec + TIMERUS_USEC_CFG_0, - NV_DRF_NUM(TIMERUS, USEC_CFG, USEC_DIVIDEND, 0) | - NV_DRF_NUM(TIMERUS, USEC_CFG, USEC_DIVISOR, 25)); - break; - } - if (clocksource_register(&s_NvClockUs)) { - NV_ASSERT(!"ERROR: Could not register microsecond timer\n"); - } - - NvRmModuleGetBaseAddress(s_hRmGlobal, - NVRM_MODULE_ID(NvRmModuleID_Timer, NV_CPU_TIMER_INSTANCE), &Phys, &Len); - if (NvRmPhysicalMemMap(Phys, Len, NVOS_MEM_READ_WRITE, - NvOsMemAttribute_Uncached, - (void **)&s_NvTimerTick)!=NvSuccess) { - NV_ASSERT(!"ERROR: Unable to get tick-timer base address\n"); - } - - s_NvTimerIrq.irq = NvRmGetIrqForLogicalInterrupt(s_hRmGlobal, - NVRM_MODULE_ID(NvRmModuleID_Timer, NV_CPU_TIMER_INSTANCE), 0); - - if (setup_irq(s_NvTimerIrq.irq, &s_NvTimerIrq)) { - NV_ASSERT(!"ERROR: Could not configure timer IRQ\n"); - } - - - s_NvTimer.max_delta_ns = - clockevent_delta2ns(TIMER_TMR_PTV_0_TMR_PTV_DEFAULT_MASK, - &s_NvTimer); - s_NvTimer.min_delta_ns = clockevent_delta2ns(1, &s_NvTimer); - s_NvTimer.cpumask = cpu_all_mask; - s_NvTimer.irq = s_NvTimerIrq.irq; - clockevents_register_device(&s_NvTimer); - - NvSpareTimerInit(); + void __iomem *tmr = (void __iomem *)dev_id; + writel(1<<30, tmr + TIMER_TMR_PCR_0); + return IRQ_HANDLED; } -struct sys_timer tegra_timer = -{ - .init = tegra_timer_init, +static struct irqaction tegra_lp2wake_irq = { + .name = "timer_lp2wake", + .irq = INT_TMR4, + .flags = IRQF_DISABLED, + .handler = tegra_lp2wake_interrupt, + .dev_id = IO_ADDRESS(TEGRA_TMR1_BASE + TIMER4_OFFS), }; -static volatile NvU8 *s_NvSpareTimerTick; - -static irqreturn_t NvSpareTimerIntrHandler( - int irq, - void *driver_data) +void tegra_lp2_set_trigger(unsigned long cycles) { - NV_WRITE32(s_NvSpareTimerTick + TIMER_TMR_PCR_0, - NV_DRF_NUM(TIMER, TMR_PCR, INTR_CLR, 1)); + void __iomem *tmr = (void __iomem*)tegra_lp2wake_irq.dev_id; - return IRQ_HANDLED; + writel(0, tmr + TIMER_TMR_PTV_0); + if (cycles) { + u32 reg = 0x80000000ul | min(0x1ffffffful, cycles); + writel(reg, tmr + TIMER_TMR_PTV_0); + } } -static struct irqaction s_SpareTimerIrq = { - .name = "spare_timer", - .flags = IRQF_DISABLED, - .handler = NvSpareTimerIntrHandler, - .dev_id = &s_NvSpareTimerTick, -}; - -void NvSpareTimerTrigger(unsigned long cycles) +static unsigned long measure_input_freq(unsigned int *m, unsigned int *n) { - NvU32 v; - v = NV_DRF_NUM(TIMER, TMR_PTV, TMR_PTV, cycles); - v |= NV_DRF_DEF(TIMER, TMR_PTV, EN, ENABLE); - NV_WRITE32(s_NvSpareTimerTick + TIMER_TMR_PTV_0, v); + void __iomem *clk_rst = IO_ADDRESS(TEGRA_CLK_RESET_BASE); + unsigned long osc = readl(clk_rst + CLK_RST_CONTROLLER_OSC_CTRL_0); + osc >>= 30; + + switch (osc) { + case 0: if (m && n) { *m=1; *n=13; } return 13000; + case 1: if (m && n) { *m=5; *n=96; } return 19200; + case 2: if (m && n) { *m=1; *n=12; } return 12000; + case 3: if (m && n) { *m=1; *n=26; } return 26000; + } + return 0; } -static void NvSpareTimerInit(void) +static void __init tegra_timer_init(void) { - int irq; - NvRmPhysAddr Phys; - NvU32 Len; + void __iomem *tmr; + unsigned int m, n; + unsigned long val; - NvRmModuleGetBaseAddress(s_hRmGlobal, - NVRM_MODULE_ID(NvRmModuleID_Timer, NV_CPU_SPARE_TIMER_INSTANCE), &Phys, &Len); + tmr = IO_ADDRESS(TEGRA_TMR1_BASE + TIMERUS_OFFS); + val = measure_input_freq(&m, &n); - if (NvRmPhysicalMemMap(Phys, Len, NVOS_MEM_READ_WRITE, - NvOsMemAttribute_Uncached, - (void **)&s_NvSpareTimerTick)!=NvSuccess) { - NV_ASSERT(!"ERROR: Unable to get tick-timer base address\n"); - } + val = ((m-1)<<8) | (n-1); - irq = NvRmGetIrqForLogicalInterrupt(s_hRmGlobal, - NVRM_MODULE_ID(NvRmModuleID_Timer, NV_CPU_SPARE_TIMER_INSTANCE), 0); + writel(val, tmr + TIMERUS_USEC_CFG_0); - if (setup_irq(irq, &s_SpareTimerIrq)) { - NV_ASSERT(!"ERROR: Could not configure timer IRQ\n"); - } + if (clocksource_register(&tegra_clocksource)) { + pr_err("Failed to register clocksource\n"); + BUG(); + } + + tegra_clockevent.event.max_delta_ns = + clockevent_delta2ns(0x1ffffffful, &tegra_clockevent.event); + + tegra_clockevent.event.min_delta_ns = + clockevent_delta2ns(1, &tegra_clockevent.event); + + tegra_clockevent.event.cpumask = cpu_all_mask; + + if (setup_irq(tegra_clockevent_irq.irq, &tegra_clockevent_irq)) { + pr_err("Failed to register clockevent IRQ\n"); + BUG(); + } + if (setup_irq(tegra_lp2wake_irq.irq, &tegra_lp2wake_irq)) { + pr_err("Failed to register LP2 wakeup timer IRQ\n"); + BUG(); + } + + clockevents_register_device(&tegra_clockevent.event); } +struct sys_timer tegra_timer = { + .init = tegra_timer_init, +}; + |