diff options
author | Srikant Patnaik | 2015-01-11 19:32:00 +0530 |
---|---|---|
committer | Srikant Patnaik | 2015-01-11 19:32:00 +0530 |
commit | b9ae4882794bcdc8d26671dfdbc406173d76c739 (patch) | |
tree | c38ab3721cf114958a1ba383ffa2a787cabe650f /arch/arm/mach-wmt/wmt_time.c | |
parent | 9269889243fe99dcec8609a7225cf3b55f879069 (diff) | |
download | FOSSEE-netbook-kernel-source-b9ae4882794bcdc8d26671dfdbc406173d76c739.tar.gz FOSSEE-netbook-kernel-source-b9ae4882794bcdc8d26671dfdbc406173d76c739.tar.bz2 FOSSEE-netbook-kernel-source-b9ae4882794bcdc8d26671dfdbc406173d76c739.zip |
source and header files added from another kernel source (not available in present repository)
Diffstat (limited to 'arch/arm/mach-wmt/wmt_time.c')
-rw-r--r-- | arch/arm/mach-wmt/wmt_time.c | 460 |
1 files changed, 460 insertions, 0 deletions
diff --git a/arch/arm/mach-wmt/wmt_time.c b/arch/arm/mach-wmt/wmt_time.c new file mode 100644 index 00000000..e4f6093a --- /dev/null +++ b/arch/arm/mach-wmt/wmt_time.c @@ -0,0 +1,460 @@ +/*++ +linux/arch/arm/mach-wmt/wmt_time.c + +Copyright (c) 2008 WonderMedia Technologies, Inc. + +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, see <http://www.gnu.org/licenses/>. + +WonderMedia Technologies, Inc. +10F, 529, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C. +--*/ + +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/clocksource.h> +#include <linux/clockchips.h> +#include <linux/delay.h> +#include <linux/export.h> + +#include <asm/mach/time.h> +#include <asm/sched_clock.h> +#include <mach/hardware.h> +#include <mach/wmt_mmap.h> +#include <mach/wmt_env.h> +#include <asm/smp_twd.h> + +//#define DEBUG +#ifdef DEBUG +#define fq_dbg(fmt, args...) printk(KERN_ERR "[%s]_%d: " fmt, __func__ , __LINE__, ## args) +#define fq_trace() printk(KERN_ERR "trace in %s %d\n", __func__, __LINE__) +#else +#define fq_dbg(fmt, args...) +#define fq_trace() +#endif + +#define MIN_OSCR_DELTA 16 +#define MIN_HRTMR_CYC_DELTA 64 +#define WMT_CLOCK_TICK_RATE 3000000 +#define WMT_CLOCK_TICK_RATE1 5000000 +#define WMT_CLOCK_TICK_RATE2 6000000 +#define WMT_CLOCK_TICK_RATE3 6000000 + +/* Clear OS Timer1 irq */ +static inline void wmt_os_timer_clear_irq(void) +{ + OSTS_VAL = OSTS_M1; +} + +/* disable OS Timer1 irq */ +static inline void wmt_os_timer_disable_irq(void) +{ + OSTI_VAL &= ~OSTI_E1; +} + +/* Enable OS timer1 irq */ +static inline void wmt_os_timer_enable_irq(void) +{ + OSTI_VAL |= OSTI_E1; +} + +/* Stop ostimer, counter stop */ +static inline void wmt_os_timer_freeze_counter(void) +{ + OSTC_VAL = 0; +} + +/* Let OS Timer free run, counter increase now */ +static inline void wmt_os_timer_restart_counter(void) +{ + OSTC_VAL = OSTC_ENABLE; +} + +static inline void wmt_os_timer_set_counter(u32 new_cnt) +{ + OSCR_VAL = new_cnt; +} + +static inline u32 wmt_os_timer_read_counter(void) +{ + OSTC_VAL |= OSTC_RDREQ; + while (OSTA_VAL & OSTA_RCA) + ; + + return (u32)OSCR_VAL; +} + +static inline void wmt_os_timer_set_match(u32 new_match) +{ + /* check if can write OS Timer1 match register nows */ + while (OSTA_VAL & OSTA_MWA1) + ; + + OSM1_VAL = new_match; +} + +/* OS timer hardware initializing routine */ +/* TODO: Here we let os timer run, but disable interrupt, + when clcokevent registed, then enable interrupt +*/ +static void __init wmt_os_timer_init(void) +{ + wmt_os_timer_disable_irq(); + wmt_os_timer_clear_irq(); + wmt_os_timer_freeze_counter(); + wmt_os_timer_set_match(0); + wmt_os_timer_restart_counter(); +} + +/* for clocksource */ +static cycle_t wmt_timer_read_cycles(struct clocksource *cs) +{ + return (cycle_t)wmt_os_timer_read_counter(); +} + +struct clocksource wmt_clocksource = { + .name = "wmt_clocksource", + .rating = 200, + .read = wmt_timer_read_cycles, + .mask = CLOCKSOURCE_MASK(32), + .flags = CLOCK_SOURCE_IS_CONTINUOUS, +}; + +static void __init wmt_clocksource_init(struct clocksource *cs) +{ + clocksource_register_hz(cs, WMT_CLOCK_TICK_RATE); + fq_dbg("%s mult:%u, shift:%u, max_idle_ns:%llu\n\n", cs->name, + cs->mult, cs->shift, cs->max_idle_ns); +} + +struct clocksource wmt_clocksource1 = { + .name = "wmt_clocksource1", + .rating = 150, + .read = wmt_timer_read_cycles, + .mask = CLOCKSOURCE_MASK(32), + .flags = CLOCK_SOURCE_IS_CONTINUOUS, +}; + +static void __init wmt_clocksource_init1(struct clocksource *cs) +{ + clocksource_register_hz(cs, WMT_CLOCK_TICK_RATE1); + fq_dbg("%s mult:%u, shift:%u, max_idle_ns:%llu\n\n", cs->name, + cs->mult, cs->shift, cs->max_idle_ns); +} + + +struct clocksource wmt_clocksource2 = { + .name = "wmt_clocksource2", + .rating = 150, + .read = wmt_timer_read_cycles, + .mask = CLOCKSOURCE_MASK(32), + .flags = CLOCK_SOURCE_IS_CONTINUOUS, +}; + +static void __init wmt_clocksource_init2(struct clocksource *cs) +{ + clocksource_register_hz(cs, WMT_CLOCK_TICK_RATE2); + fq_dbg("%s mult:%u, shift:%u, max_idle_ns:%llu\n\n", cs->name, + cs->mult, cs->shift, cs->max_idle_ns); +} + +struct clocksource wmt_clocksource3 = { + .name = "wmt_clocksource3", + .rating = 150, + .read = wmt_timer_read_cycles, + .mask = CLOCKSOURCE_MASK(32), + .flags = CLOCK_SOURCE_IS_CONTINUOUS, +}; + +static void __init wmt_clocksource_init3(struct clocksource *cs) +{ + clocksource_register_hz(cs, WMT_CLOCK_TICK_RATE3); + fq_dbg("%s mult:%u, shift:%u, max_idle_ns:%llu\n\n", cs->name, + cs->mult, cs->shift, cs->max_idle_ns); +} + + +static int +wmt_timer_set_next_event(unsigned long cycles, struct clock_event_device *evt) +{ + unsigned long next = 0; + unsigned long oscr = 0; + + oscr = wmt_os_timer_read_counter(); + next = oscr + cycles; + /* set new value to os time1 match register */ + wmt_os_timer_set_match(next); + /* Enable match on timer 1 to cause interrupts. */ + wmt_os_timer_enable_irq(); + /* check if overflow */ + if ((signed long)(next - wmt_os_timer_read_counter()) <= MIN_OSCR_DELTA) { + fq_dbg("set os timer overflow!, set_cyc:%lu, now_cyc:%lu," + " next_cyc:%lu\n\n\n", cycles, oscr, next); + return -ETIME; + } + + return 0; +} + +static void +wmt_timer_set_mode(enum clock_event_mode mode, struct clock_event_device *evt) +{ + switch (mode) { + case CLOCK_EVT_MODE_UNUSED: + case CLOCK_EVT_MODE_SHUTDOWN: + case CLOCK_EVT_MODE_ONESHOT: + /* disable OS Timer irq here */ + wmt_os_timer_disable_irq(); + /* Clear match on OS Timer 1 */ + wmt_os_timer_clear_irq(); + break; + case CLOCK_EVT_MODE_RESUME: + case CLOCK_EVT_MODE_PERIODIC: + default: + break; + } + + return ; +} + +struct clock_event_device wmt_clockevent = { + .name = "wmt_clockevent", + .features = CLOCK_EVT_FEAT_ONESHOT, + .rating = 200, + .set_next_event = wmt_timer_set_next_event, + .set_mode = wmt_timer_set_mode, + .shift = 32, +}; + +static irqreturn_t wmt_timer_interrupt(int irq, void *dev_id) +{ + struct clock_event_device *evt = dev_id; + + /* Clear match on OS Timer 1 irq */ + wmt_os_timer_clear_irq(); + evt->event_handler(evt); + + return IRQ_HANDLED; +} + +struct irqaction wmt_timer_irq = { + .name = "wmt_timer", + .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, + .handler = wmt_timer_interrupt, + .dev_id = &wmt_clockevent, +}; + +static void __init wmt_clockevent_init(struct clock_event_device *evt, + unsigned int irq, struct irqaction *act) +{ + /* reset some elements for clockevent */ + evt->cpumask = cpumask_of(smp_processor_id()); + evt->mult = div_sc(WMT_CLOCK_TICK_RATE, NSEC_PER_SEC, evt->shift); + evt->max_delta_ns = clockevent_delta2ns(0x7fffffff, evt); + evt->min_delta_ns = clockevent_delta2ns(MIN_HRTMR_CYC_DELTA, evt); + fq_dbg("%s, mult:%u, shift:%d, max_delta:%llu, min_delta:%llu\n\n\n", + evt->name, evt->mult, evt->shift, evt->max_delta_ns, evt->min_delta_ns); + + /* setup os timer1 irq */ + if (setup_irq(irq, act)) + printk(KERN_ERR "setup clockevent %s irq %u, failed!\n\n", evt->name, irq); + + /* register clockevent */ + clockevents_register_device(evt); + + return ; +} + +/* for sched_clock */ +static u32 notrace wmt_read_sched_clock(void) +{ + return wmt_os_timer_read_counter(); +} + +/* for MPcore local timer */ +#ifdef CONFIG_LOCAL_TIMERS +extern void wmt3498_init_clocks(void); +#define WMT_LOCAL_TIMER_BASE (0xD8018000 + 0x600) +#define WMT_LOCAL_TIMER_PPI_NUM 29 +static DEFINE_TWD_LOCAL_TIMER(wmt_local_timer, + WMT_LOCAL_TIMER_BASE, WMT_LOCAL_TIMER_PPI_NUM); + +/* check if uboot env wmt.twd.disable = 1, if twd disable (=1), return 1, + * if twd enable, return 0 + */ +static int wmt_twd_is_disabled(void) +{ + int ret = 0; + int varlen = 128; + unsigned int drv_en = 0; + unsigned char buf[128] = {0}; + + /* uboot env name is: wmt.twd.disable */ + ret = wmt_getsyspara("wmt.twd.disable", buf, &varlen); + if (ret) { + ret = 0; + goto out; + } + + sscanf(buf, "%d", &drv_en); + if (drv_en) { + ret = -ENODEV; + goto out; + } + +out: + return ret; +} +#endif + +static void __init wmt_timer_init(void) +{ + /* prepare OS timer hardware, irq disabled */ + wmt_os_timer_init(); + /* os timer1 as clocksourece */ + wmt_clocksource_init(&wmt_clocksource); + wmt_clocksource_init1(&wmt_clocksource1); + wmt_clocksource_init2(&wmt_clocksource2); + wmt_clocksource_init3(&wmt_clocksource3); + /* sched_clock for timestamp, as printk.... */ + setup_sched_clock(wmt_read_sched_clock, 32, WMT_CLOCK_TICK_RATE); + /* os timer1 as clockevent device */ + wmt_clockevent_init(&wmt_clockevent, IRQ_OST1, &wmt_timer_irq); + +#ifdef CONFIG_LOCAL_TIMERS + wmt3498_init_clocks(); + if (wmt_twd_is_disabled()) { + printk(KERN_INFO "WMT local timer disabled\n"); + goto out; + } + if (twd_local_timer_register(&wmt_local_timer)) + printk(KERN_ERR "Register ARM local timer failed! Use broadcast mode!\n"); +#endif +out: + /* this is a MUST operation */ + wmt_os_timer_enable_irq(); + + return ; +} + +struct sys_timer wmt_timer = { + .init = wmt_timer_init +}; + + +/* below code is for old design compatibility */ +inline unsigned int wmt_read_oscr(void) +{ + return wmt_os_timer_read_counter(); +} +EXPORT_SYMBOL(wmt_read_oscr); + +int wmt_rtc_on = 1; +static int __init no_rtc(char *str) +{ + wmt_rtc_on = 0; + return 1; +} +__setup("nortc", no_rtc); + + +static void wmt_rtc_init(void) +{ + fq_dbg("Enter\n"); + + RTCC_VAL = (RTCC_ENA|RTCC_INTTYPE); + if (!(RTSR_VAL&RTSR_VAILD)) + while (!(RTSR_VAL&RTSR_VAILD)) + ; + /* Reset RTC alarm settings */ + //RTAS_VAL = 0; + /* Reset RTC test mode. */ + RTTM_VAL = 0; + + /* Patch 1, RTCD default value isn't 0x41 and it will not sync with RTDS. */ + if (RTCD_VAL == 0) { + while (RTWS_VAL & RTWS_DATESET) + ; + RTDS_VAL = RTDS_VAL; + } + + /* + * Disable all RTC control functions. + * Set to 24-hr format and update type to each second. + * Disable sec/min update interrupt. + * Let RTC free run without interrupts. + */ + /* RTCC_VAL &= ~(RTCC_12HR | RTCC_INTENA); */ + /* RTCC_VAL |= RTCC_ENA | RTCC_INTTYPE; */ + RTCC_VAL = (RTCC_ENA|RTCC_INTTYPE); + + if (RTWS_VAL & RTWS_CONTROL) { + while (RTWS_VAL & RTWS_CONTROL) + ; + } + + fq_dbg("Exit\n\n\n"); + return ; +} + +void wmt_read_rtc(unsigned int *date, unsigned int *time) +{ + /* before read rtc, we should check if rtc already be initialized */ + if (((RTCC_VAL & RTCC_ENA) == 0) || ((RTSR_VAL & RTSR_VAILD) == 0)) { + wmt_rtc_init(); + } + + if (!(RTSR_VAL & RTSR_VAILD)) + while (!(RTSR_VAL & RTSR_VAILD)) + ; + + if (RTWS_VAL & RTWS_DATESET) + while (RTWS_VAL & RTWS_DATESET) + ; + + *date = RTCD_VAL; + + if (RTWS_VAL & RTWS_TIMESET) + while (RTWS_VAL & RTWS_TIMESET) + ; + + *time = RTCT_VAL; +} +EXPORT_SYMBOL(wmt_read_rtc); + +/* + * get current Gregorian date from RTC, + * coverts to seconds since 1970-01-01 00:00:00 + */ +static unsigned long wmt_get_rtc_time(void) +{ + unsigned int date = 0; + unsigned int time = 0; + + /* if no rtc or rtc disabled, return 0, + * means timekeeping is 1970-01-01 00:00:00 when system booting */ + if (!wmt_rtc_on) + return 0; + + wmt_read_rtc(&date, &time); + return mktime(RTCD_CENT(date)? 2100+RTCD_YEAR(date) : 2000+RTCD_YEAR(date), + RTCD_MON(date), RTCD_MDAY(date),RTCT_HOUR(time), + RTCT_MIN(time), RTCT_SEC(time)); +} + +void read_persistent_clock(struct timespec *ts) +{ + ts->tv_nsec = 0; + ts->tv_sec = (__kernel_time_t)wmt_get_rtc_time(); + + fq_dbg("seconds read from RTC is %lu\n\n\n", ts->tv_sec); + return ; +} |