// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2019 MediaTek Inc. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* for aee dump */ #include /* for aee dump */ #ifndef cycle_t typedef u64 cycle_t; #endif #define APXGPT_OF_COMPTIBLE_NAME "mediatek,apxgpt" /* * CONFIG_MTK_TIMER_BC_IRQ_FORCE_CPU0: * * Always force irq affinity to cpu 0. For debugging purpose only. */ /* #define CONFIG_MTK_TIMER_BC_IRQ_FORCE_CPU0 */ /* * CONFIG_MTK_TIMER_DEBUG: * * Enable debugging mechanism. */ /* #define CONFIG_MTK_TIMER_DEBUG */ /* * CONFIG_MTK_TIMER_AEE_DUMP: * * Enable dump during debugging flow, for example, HWT. * The debugging information will be collected to DB. */ #define CONFIG_MTK_TIMER_AEE_DUMP #ifdef CONFIG_MTK_TIMER_APXGPT_V1 #ifdef CONFIG_MTK_TIMER_AEE_DUMP #ifdef CONFIG_MTK_RAM_CONSOLE #include static char gpt_clkevt_aee_dump_buf[128]; static uint64_t gpt_time_clkevt_handler_entry; static uint64_t gpt_time_clkevt_handler_exit; static uint64_t gpt_time_clkevt_set_next_event_entry; static uint64_t gpt_time_clkevt_set_next_event_exit; static uint64_t gpt_time_int_handler_entry; static uint64_t gpt_time_int_handler_exit; #define _MTK_TIMER_DBG_AEE_DUMP #endif #endif #endif /* * Common apxpgt definition and functions used when, * 1. CONFIG_MTK_TIMER_APXGPT_V1 on: apxgpt will be used. * 2. CONFIG_MTK_TIMER_SYSTIMER on: apxgpt will be un-initialized * in init stage. */ #if defined(CONFIG_MTK_TIMER_APXGPT_V1) || defined(CONFIG_MTK_TIMER_SYSTIMER) #define GPT_CLKEVT_ID (GPT1) #define GPT_CLKSRC_ID (GPT2) #define GPT_IRQEN (AP_XGPT_BASE + 0x0000) #define GPT_IRQSTA (AP_XGPT_BASE + 0x0004) #define GPT_IRQACK (AP_XGPT_BASE + 0x0008) #define GPT1_BASE (AP_XGPT_BASE + 0x0010) #define GPT_CON (0x00) #define GPT_CLK (0x04) #define GPT_CNT (0x08) #define GPT_CMP (0x0C) #define GPT_CNTH (0x18) #define GPT_CMPH (0x1C) #define GPT_CON_ENABLE (0x1 << 0) #define GPT_CON_CLRCNT (0x1 << 1) #define GPT_CON_OPMODE (0x3 << 4) #define GPT_OPMODE_MASK (0x3) #define GPT_CLKDIV_MASK (0xf) #define GPT_CLKSRC_MASK (0x1) #define GPT_OPMODE_OFFSET (4) #define GPT_CLKSRC_OFFSET (4) #define GPT_FEAT_64_BIT (0x0001) #define GPT_ISR (0x0010) #define GPT_IN_USE (0x0100) /************define this for 32/64 compatible**************/ #define GPT_BIT_MASK_L 0x00000000FFFFFFFF #define GPT_BIT_MASK_H 0xFFFFFFFF00000000 /****************************************************/ struct mt_gpt_timers { int tmr_irq; void __iomem *tmr_regs; }; struct mt_gpt_device { unsigned int id; unsigned int mode; unsigned int clksrc; unsigned int clkdiv; unsigned int cmp[2]; void (*func)(unsigned long); int flags; int features; void __iomem *base_addr; }; /* * Return GPT4 count(before init clear) to record * kernel start time between LK and kernel */ /* 1000000 / 76.92ns = 13000.520 */ #define GPT4_1MS_TICK ((u32)(13000)) #define GPT4_BASE (AP_XGPT_BASE + 0x0040) #define mt_gpt_set_reg(val, addr) \ mt_reg_sync_writel(__raw_readl(addr)|(val), addr) #define mt_gpt_clr_reg(val, addr) \ mt_reg_sync_writel(__raw_readl(addr)&~(val), addr) static struct mt_gpt_timers gpt_timers; static struct mt_gpt_device gpt_devs[NR_GPTS]; #define AP_XGPT_BASE gpt_timers.tmr_regs static void __gpt_disable_irq(struct mt_gpt_device *dev) { mt_gpt_clr_reg(0x1 << (dev->id), GPT_IRQEN); } static void __gpt_ack_irq(struct mt_gpt_device *dev) { mt_reg_sync_writel(0x1 << (dev->id), GPT_IRQACK); } static void __gpt_reset(struct mt_gpt_device *dev) { mt_reg_sync_writel(0x0, dev->base_addr + GPT_CON); __gpt_disable_irq(dev); __gpt_ack_irq(dev); mt_reg_sync_writel(0x0, dev->base_addr + GPT_CLK); mt_reg_sync_writel(0x2, dev->base_addr + GPT_CON); mt_reg_sync_writel(0x0, dev->base_addr + GPT_CMP); if (dev->features & GPT_FEAT_64_BIT) mt_reg_sync_writel(0, dev->base_addr + GPT_CMPH); } static void gpt_devs_init(void) { int i; for (i = 0; i < NR_GPTS; i++) { gpt_devs[i].id = i; gpt_devs[i].base_addr = GPT1_BASE + 0x10 * i; pr_info("gpt%d, base=0x%lx\n", i + 1, (unsigned long)gpt_devs[i].base_addr); } gpt_devs[GPT6].features |= GPT_FEAT_64_BIT; } #endif #ifdef CONFIG_MTK_TIMER_APXGPT_V1 static DEFINE_SPINLOCK(gpt_lock); static unsigned int boot_time_value; static unsigned int gpt_boot_up_time(void) { unsigned int tick; tick = __raw_readl(GPT4_BASE + GPT_CNT); return ((tick + (GPT4_1MS_TICK - 1)) / GPT4_1MS_TICK); } /*********************************************************/ static struct mt_gpt_device *id_to_dev(unsigned int id) { return id < NR_GPTS ? gpt_devs + id : NULL; } #define gpt_update_lock(flags) spin_lock_irqsave(&gpt_lock, flags) #define gpt_update_unlock(flags) spin_unlock_irqrestore(&gpt_lock, flags) static inline void noop(unsigned long data) { } static void(*handlers[])(unsigned long) = { noop, noop, noop, noop, noop, noop, noop, }; static struct tasklet_struct task[NR_GPTS]; static void task_sched(unsigned long data) { unsigned int id = (unsigned int)data; tasklet_schedule(&task[id]); } static irqreturn_t gpt_handler(int irq, void *dev_id); static void __gpt_ack_irq(struct mt_gpt_device *dev); static cycle_t mt_gpt_read(struct clocksource *cs); static int mt_gpt_clkevt_next_event(unsigned long cycles, struct clock_event_device *evt); static int mt_gpt_clkevt_shutdown(struct clock_event_device *clk); static int mt_gpt_clkevt_oneshot(struct clock_event_device *clk); static int mt_gpt_clkevt_resume(struct clock_event_device *clk); static int mt_gpt_set_periodic(struct clock_event_device *clk); static struct clocksource gpt_clocksource = { .name = "mtk-timer", .rating = 300, .read = mt_gpt_read, .mask = CLOCKSOURCE_MASK(32), .shift = 25, .flags = CLOCK_SOURCE_IS_CONTINUOUS, }; static struct clock_event_device gpt_clockevent = { .name = "mtk_tick", /* * CLOCK_EVT_FEAT_DYNIRQ: Core shall set the interrupt affinity * dynamically in broadcast mode. * CLOCK_EVT_FEAT_ONESHOT: Use one-shot mode for tick broadcast. */ #ifdef CONFIG_MTK_TIMER_BC_IRQ_FORCE_CPU0 .features = CLOCK_EVT_FEAT_ONESHOT, #else .features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_DYNIRQ, #endif .shift = 32, .rating = 300, .set_next_event = mt_gpt_clkevt_next_event, .set_state_shutdown = mt_gpt_clkevt_shutdown, .set_state_periodic = mt_gpt_set_periodic, .set_state_oneshot = mt_gpt_clkevt_oneshot, .tick_resume = mt_gpt_clkevt_resume, }; static struct irqaction gpt_irq = { .name = "mt-gpt", .flags = IRQF_TIMER | IRQF_IRQPOLL | IRQF_TRIGGER_LOW | IRQF_PERCPU, .handler = gpt_handler, .dev_id = &gpt_clockevent, }; static inline unsigned int gpt_get_and_ack_irq(void) { unsigned int id; unsigned int mask; unsigned int status = __raw_readl(GPT_IRQSTA); for (id = GPT1; id < NR_GPTS; id++) { mask = 0x1 << id; if (status & mask) { mt_reg_sync_writel(mask, GPT_IRQACK); break; } } return id; } /* * gpt_handler users are listed as below, * * For ACAO project: * GPT1: SoC timer for tick-broadcasting (oneshot) * * For HPS project: * GPT4: Wakeup source for MTK idle framework (oneshot) */ static irqreturn_t gpt_handler(int irq, void *dev_id) { unsigned int id; struct mt_gpt_device *dev; #ifdef _MTK_TIMER_DBG_AEE_DUMP gpt_time_int_handler_entry = sched_clock(); #endif id = gpt_get_and_ack_irq(); dev = id_to_dev(id); if (likely(dev)) { if (!(dev->flags & GPT_ISR)) handlers[id](id); else handlers[id]((unsigned long)dev_id); } else pr_info("GPT id is %d\n", id); #ifdef _MTK_TIMER_DBG_AEE_DUMP gpt_time_int_handler_exit = sched_clock(); #endif return IRQ_HANDLED; } static void __gpt_enable_irq(struct mt_gpt_device *dev) { mt_gpt_set_reg(0x1 << (dev->id), GPT_IRQEN); } static void __gpt_get_cnt(struct mt_gpt_device *dev, unsigned int *ptr) { *ptr = __raw_readl(dev->base_addr + GPT_CNT); if (dev->features & GPT_FEAT_64_BIT) *(++ptr) = __raw_readl(dev->base_addr + GPT_CNTH); } static void __gpt_get_cmp(struct mt_gpt_device *dev, unsigned int *ptr) { *ptr = __raw_readl(dev->base_addr + GPT_CMP); if (dev->features & GPT_FEAT_64_BIT) *(++ptr) = __raw_readl(dev->base_addr + GPT_CMPH); } static void __gpt_set_mode(struct mt_gpt_device *dev, unsigned int mode) { unsigned int ctl = __raw_readl(dev->base_addr + GPT_CON); mode <<= GPT_OPMODE_OFFSET; ctl &= ~GPT_CON_OPMODE; ctl |= mode; mt_reg_sync_writel(ctl, dev->base_addr + GPT_CON); dev->mode = mode; } static void __gpt_set_clk(struct mt_gpt_device *dev, unsigned int clksrc, unsigned int clkdiv) { unsigned int clk = (clksrc << GPT_CLKSRC_OFFSET) | clkdiv; mt_reg_sync_writel(clk, dev->base_addr + GPT_CLK); dev->clksrc = clksrc; dev->clkdiv = clkdiv; } static void __gpt_set_cmp(struct mt_gpt_device *dev, unsigned int cmpl, unsigned int cmph) { mt_reg_sync_writel(cmpl, dev->base_addr + GPT_CMP); dev->cmp[0] = cmpl; if (dev->features & GPT_FEAT_64_BIT) { mt_reg_sync_writel(cmph, dev->base_addr + GPT_CMPH); dev->cmp[1] = cmpl; } } static void __gpt_clrcnt(struct mt_gpt_device *dev) { mt_gpt_set_reg(GPT_CON_CLRCNT, dev->base_addr + GPT_CON); while (__raw_readl(dev->base_addr + GPT_CNT)) cpu_relax(); } static void __gpt_start(struct mt_gpt_device *dev) { mt_gpt_set_reg(GPT_CON_ENABLE, dev->base_addr + GPT_CON); } static void __gpt_wait_clrcnt(void) { /* * if gpt is running in 32K domain, it needs 3T (~90 us) for clearing * old counter. */ #define WAIT_CLR_CNT_TIME_NS 100000 uint64_t start_time = 0, end_time = 0; start_time = sched_clock(); end_time = start_time; while ((end_time - start_time) < WAIT_CLR_CNT_TIME_NS) end_time = sched_clock(); } static void __gpt_wait_clrcnt_then_start(struct mt_gpt_device *dev) { __gpt_wait_clrcnt(); __gpt_start(dev); } static void __gpt_stop(struct mt_gpt_device *dev) { mt_gpt_clr_reg(GPT_CON_ENABLE, dev->base_addr + GPT_CON); } static void __gpt_set_flags(struct mt_gpt_device *dev, unsigned int flags) { dev->flags |= flags; } static void __gpt_set_handler(struct mt_gpt_device *dev, void (*func)(unsigned long)) { if (func) { if (dev->flags & GPT_ISR) handlers[dev->id] = func; else { tasklet_init(&task[dev->id], func, 0); handlers[dev->id] = task_sched; } } dev->func = func; } static void setup_gpt_dev_locked(struct mt_gpt_device *dev, unsigned int mode, unsigned int clksrc, unsigned int clkdiv, unsigned int cmp, void (*func)(unsigned long), unsigned int flags) { __gpt_set_flags(dev, flags | GPT_IN_USE); __gpt_set_mode(dev, mode & GPT_OPMODE_MASK); __gpt_set_clk(dev, clksrc & GPT_CLKSRC_MASK, clkdiv & GPT_CLKDIV_MASK); if (func) __gpt_set_handler(dev, func); if (dev->mode != GPT_FREE_RUN) { __gpt_set_cmp(dev, cmp, 0); if (!(dev->flags & GPT_NOIRQEN)) __gpt_enable_irq(dev); } if (!(dev->flags & GPT_NOAUTOEN)) __gpt_start(dev); } static int mt_gpt_clkevt_next_event(unsigned long cycles, struct clock_event_device *evt) { struct mt_gpt_device *dev = id_to_dev(GPT_CLKEVT_ID); #ifdef _MTK_TIMER_DBG_AEE_DUMP gpt_time_clkevt_set_next_event_entry = sched_clock(); #endif /* * disable irq first because we do not expect interrupt is triggered * by old compare value. */ __gpt_disable_irq(dev); /* * Configure gpt1 to use 13MHz clock during re-configuration. * * Reason: Clock synchronization issue may happen if gpt is in 32KHz * domain during re-configuration. For example: Updating cmp * value may need to wait a period of time (e.g., 3.5T) to let * gpt hw finish jobs: gpt hw will clear counter value * automatically while setting new cmp value. * * GPT_EN shall be enabled after gpt hw finishes above job, * otherwise gpt may work abnormally, e.g., wrong cmp value * is latched or counter value is not reset. * * If gpt is running under 13MHz, above waiting is not required * since gpt hw guarantees that GPT_EN is applied after above * job is done. */ __gpt_set_clk(dev, GPT_CLK_SRC_SYS & GPT_CLKSRC_MASK, GPT_CLK_DIV_1 & GPT_CLKDIV_MASK); __gpt_stop(dev); if (cycles < 3) { pr_info("[mt_gpt] invalid cycles < 3\n"); cycles = 3; } /* * Do cmp first because updating cmp will trigger most * complicated behavior in gpt hw. See above description. */ __gpt_set_cmp(dev, cycles, 0); /* ack irq */ __gpt_ack_irq(dev); /* ensure irq is enabled before next running */ __gpt_enable_irq(dev); /* * Configure gpt1 to use 32KHz clock before enabling. * * Reason: 13MHz clock source may be disabled during some * low-power scenarios, e.g., SODI. We shall use a * always-on clock after enabling, e.g., 32KHz. */ __gpt_set_clk(dev, GPT_CLK_SRC_RTC & GPT_CLKSRC_MASK, GPT_CLK_DIV_1 & GPT_CLKDIV_MASK); __gpt_start(dev); #ifdef _MTK_TIMER_DBG_AEE_DUMP gpt_time_clkevt_set_next_event_exit = sched_clock(); #endif return 0; } static int mt_gpt_clkevt_shutdown(struct clock_event_device *clk) { struct mt_gpt_device *dev = id_to_dev(GPT_CLKEVT_ID); __gpt_stop(dev); __gpt_disable_irq(dev); __gpt_ack_irq(dev); return 0; } static int mt_gpt_clkevt_resume(struct clock_event_device *clk) { return mt_gpt_clkevt_shutdown(clk); } static int mt_gpt_clkevt_oneshot(struct clock_event_device *clk) { struct mt_gpt_device *dev = id_to_dev(GPT_CLKEVT_ID); __gpt_stop(dev); __gpt_set_mode(dev, GPT_ONE_SHOT); /* __gpt_enable_irq(dev); */ /* __gpt_clrcnt_and_start(dev); */ return 0; } static int mt_gpt_set_periodic(struct clock_event_device *clk) { struct mt_gpt_device *dev = id_to_dev(GPT_CLKEVT_ID); __gpt_stop(dev); __gpt_set_mode(dev, GPT_REPEAT); __gpt_enable_irq(dev); __gpt_wait_clrcnt_then_start(dev); return 0; } static cycle_t mt_gpt_read(struct clocksource *cs) { cycle_t cycles; unsigned int cnt[2] = {0, 0}; struct mt_gpt_device *dev = id_to_dev(GPT_CLKSRC_ID); __gpt_get_cnt(dev, cnt); if (GPT_CLKSRC_ID != GPT6) { /* * force do mask for high 32-bit to avoid unpredicted * alignment */ cycles = (GPT_BIT_MASK_L & (cycle_t) (cnt[0])); } else { cycles = (GPT_BIT_MASK_H & (((cycle_t) (cnt[1])) << 32)) | (GPT_BIT_MASK_L&((cycle_t) (cnt[0]))); } return cycles; } static void clkevt_handler(unsigned long data) { struct clock_event_device *evt = (struct clock_event_device *)data; #ifdef _MTK_TIMER_DBG_AEE_DUMP gpt_time_clkevt_handler_entry = sched_clock(); #endif #if defined(CONFIG_MTK_TIMER_DEBUG) && \ !defined(CONFIG_MTK_TIMER_BC_IRQ_FORCE_CPU0) int cpu; int err = 0; cpu = mt_irq_dump_cpu(evt->irq); if (cpu < 0) { pr_info("[mt_gpt] invalid irq query! ret %d\n", cpu); err = 1; } else { if (cpu != smp_processor_id()) { pr_info("[mt_gpt] wrong irq! irq_cpu %d, cur_cpu %d\n", cpu, smp_processor_id()); err = 1; } if (cpu != evt->irq_affinity_on) { pr_info("[mt_gpt] wrong affinity! irq_cpu %d, affinity %d\n", cpu, evt->irq_affinity_on); err = 1; } } if (!err) mt_irq_dump_status(evt->irq); #endif evt->event_handler(evt); #ifdef _MTK_TIMER_DBG_AEE_DUMP gpt_time_clkevt_handler_exit = sched_clock(); #endif } static inline void setup_clksrc(u32 freq) { struct clocksource *cs = &gpt_clocksource; struct mt_gpt_device *dev = id_to_dev(GPT_CLKSRC_ID); pr_info("setup_clksrc1: dev->base_addr=0x%lx GPT2_CON=0x%x\n", (unsigned long)dev->base_addr, __raw_readl(dev->base_addr)); /* add GPT_NOIRQEN flag to avoid irq asserted because * clksrc is not used */ setup_gpt_dev_locked(dev, GPT_FREE_RUN, GPT_CLK_SRC_SYS, GPT_CLK_DIV_1, 0, NULL, GPT_NOIRQEN); /* clocksource_register(cs); */ clocksource_register_hz(cs, freq); pr_info("setup_clksrc2: dev->base_addr=0x%lx GPT2_CON=0x%x\n", (unsigned long)dev->base_addr, __raw_readl(dev->base_addr)); } static inline void setup_clkevt(u32 freq, int irq) { unsigned int cmp[2]; unsigned int clkdiv; struct clock_event_device *evt = &gpt_clockevent; struct mt_gpt_device *dev = id_to_dev(GPT_CLKEVT_ID); /* ensure to provide irq number for tick_broadcast_set_affinity() */ evt->irq = irq; evt->mult = div_sc(freq, NSEC_PER_SEC, evt->shift); evt->max_delta_ns = clockevent_delta2ns(0xffffffff, evt); evt->min_delta_ns = clockevent_delta2ns(3, evt); evt->cpumask = cpu_possible_mask; if (freq == 13000000) clkdiv = GPT_CLK_SRC_SYS; else clkdiv = GPT_CLK_SRC_RTC; /* * 1. Use always-on 32K clock since 13M/26M clock will be * disabled during SODI * 2. Configure this device as ONESHOT mode as tick broadcast device. */ setup_gpt_dev_locked(dev, GPT_ONE_SHOT, clkdiv, GPT_CLK_DIV_1, freq / HZ, clkevt_handler, GPT_ISR); __gpt_get_cmp(dev, cmp); pr_info("apxgpt%d: clkdiv=%d, cmp=%d, hz=%d, freq=%d\n", GPT_CLKEVT_ID + 1, clkdiv, cmp[0], HZ, freq); clockevents_register_device(evt); } static void __init mt_gpt_init_acao(struct device_node *node) { u32 freq; struct clk *clk_evt; /* inquiry clk_evt, freq is RTC_CLK_RATE (32KHz) */ clk_evt = of_clk_get(node, 0); if (IS_ERR(clk_evt)) { pr_info("can't get timer clk_evt\n"); return; } if (clk_prepare_enable(clk_evt)) { pr_info("can't prepare clk_evt\n"); clk_put(clk_evt); return; } freq = (u32)clk_get_rate(clk_evt); WARN(!freq, "can't get freq of clk_evt\n"); #ifdef CONFIG_MTK_TIMER_BC_IRQ_FORCE_CPU0 irq_force_affinity(gpt_timers.tmr_irq, cpumask_of(0)); #endif setup_clkevt(freq, gpt_timers.tmr_irq); pr_info("acao clkevt, freq=%d\n", freq); } static void release_gpt_dev_locked(struct mt_gpt_device *dev) { __gpt_reset(dev); handlers[dev->id] = noop; dev->func = NULL; dev->flags = 0; } /* gpt is counting or not */ static int __gpt_get_status(struct mt_gpt_device *dev) { return !!(__raw_readl(dev->base_addr + GPT_CON) & GPT_CON_ENABLE); } /********************** export area *********************/ int request_gpt(unsigned int id, unsigned int mode, unsigned int clksrc, unsigned int clkdiv, unsigned int cmp, void (*func)(unsigned long), unsigned int flags) { unsigned long save_flags; struct mt_gpt_device *dev = id_to_dev(id); if (!dev) return -EINVAL; if (dev->flags & GPT_IN_USE) { pr_info("%s: GPT%d is in use!\n", __func__, (id + 1)); return -EBUSY; } gpt_update_lock(save_flags); setup_gpt_dev_locked(dev, mode, clksrc, clkdiv, cmp, func, flags); gpt_update_unlock(save_flags); return 0; } EXPORT_SYMBOL(request_gpt); int free_gpt(unsigned int id) { unsigned long save_flags; struct mt_gpt_device *dev = id_to_dev(id); if (!dev) return -EINVAL; if (!(dev->flags & GPT_IN_USE)) return 0; gpt_update_lock(save_flags); release_gpt_dev_locked(dev); gpt_update_unlock(save_flags); return 0; } EXPORT_SYMBOL(free_gpt); int start_gpt(unsigned int id) { unsigned long save_flags; struct mt_gpt_device *dev = id_to_dev(id); if (!dev) return -EINVAL; if (!(dev->flags & GPT_IN_USE)) { pr_info("%s: GPT%d is not in use!\n", __func__, id); return -EBUSY; } gpt_update_lock(save_flags); __gpt_clrcnt(dev); __gpt_start(dev); gpt_update_unlock(save_flags); return 0; } EXPORT_SYMBOL(start_gpt); int stop_gpt(unsigned int id) { unsigned long save_flags; struct mt_gpt_device *dev = id_to_dev(id); if (!dev) return -EINVAL; if (!(dev->flags & GPT_IN_USE)) { pr_info("%s: GPT%d is not in use!\n", __func__, id); return -EBUSY; } gpt_update_lock(save_flags); __gpt_stop(dev); gpt_update_unlock(save_flags); return 0; } EXPORT_SYMBOL(stop_gpt); int restart_gpt(unsigned int id) { unsigned long save_flags; struct mt_gpt_device *dev = id_to_dev(id); if (!dev) return -EINVAL; if (!(dev->flags & GPT_IN_USE)) { pr_info("%s: GPT%d is not in use!\n", __func__, id); return -EBUSY; } gpt_update_lock(save_flags); __gpt_start(dev); gpt_update_unlock(save_flags); return 0; } EXPORT_SYMBOL(restart_gpt); int gpt_is_counting(unsigned int id) { unsigned long save_flags; int is_counting; struct mt_gpt_device *dev = id_to_dev(id); if (!dev) return -EINVAL; if (!(dev->flags & GPT_IN_USE)) { pr_info("%s: GPT%d is not in use!\n", __func__, id); return -EBUSY; } gpt_update_lock(save_flags); is_counting = __gpt_get_status(dev); gpt_update_unlock(save_flags); return is_counting; } EXPORT_SYMBOL(gpt_is_counting); int gpt_set_cmp(unsigned int id, unsigned int val) { unsigned long save_flags; struct mt_gpt_device *dev = id_to_dev(id); if (!dev) return -EINVAL; if (dev->mode == GPT_FREE_RUN) return -EINVAL; gpt_update_lock(save_flags); __gpt_set_cmp(dev, val, 0); gpt_update_unlock(save_flags); return 0; } EXPORT_SYMBOL(gpt_set_cmp); int gpt_get_cmp(unsigned int id, unsigned int *ptr) { unsigned long save_flags; struct mt_gpt_device *dev = id_to_dev(id); if (!dev || !ptr) return -EINVAL; gpt_update_lock(save_flags); __gpt_get_cmp(dev, ptr); gpt_update_unlock(save_flags); return 0; } EXPORT_SYMBOL(gpt_get_cmp); int gpt_get_cnt(unsigned int id, unsigned int *ptr) { unsigned long save_flags; struct mt_gpt_device *dev = id_to_dev(id); if (!dev || !ptr) return -EINVAL; if (!(dev->features & GPT_FEAT_64_BIT)) { __gpt_get_cnt(dev, ptr); } else { gpt_update_lock(save_flags); __gpt_get_cnt(dev, ptr); gpt_update_unlock(save_flags); } return 0; } EXPORT_SYMBOL(gpt_get_cnt); u64 mtk_timer_get_cnt(u8 timer) { unsigned long save_flags; unsigned int val[2] = {0, 0}; u64 cnt = 0; struct mt_gpt_device *dev = id_to_dev(timer - 1); if (!dev || timer <= 0) return -EINVAL; if (!(dev->features & GPT_FEAT_64_BIT)) { __gpt_get_cnt(dev, val); cnt = (((u64)val[1] << 32) | (u64)val[0]); } else { gpt_update_lock(save_flags); __gpt_get_cnt(dev, val); gpt_update_unlock(save_flags); cnt = ((u64)val[0]) & 0x00000000FFFFFFFF; } return cnt; } int gpt_check_irq(unsigned int id) { unsigned int mask = 0x1 << id; unsigned int status = __raw_readl(GPT_IRQSTA); return (status & mask) ? 1 : 0; } EXPORT_SYMBOL(gpt_check_irq); int gpt_check_and_ack_irq(unsigned int id) { unsigned int mask = 0x1 << id; unsigned int status = __raw_readl(GPT_IRQSTA); if (status & mask) { mt_reg_sync_writel(mask, GPT_IRQACK); return 1; } else { return 0; } } EXPORT_SYMBOL(gpt_check_and_ack_irq); unsigned int gpt_boot_time(void) { return boot_time_value; } EXPORT_SYMBOL(gpt_boot_time); int gpt_set_clk(unsigned int id, unsigned int clksrc, unsigned int clkdiv) { unsigned long save_flags; struct mt_gpt_device *dev = id_to_dev(id); if (!dev) return -EINVAL; if (!(dev->flags & GPT_IN_USE)) { pr_info("%s: GPT%d is not in use!\n", __func__, id); return -EBUSY; } gpt_update_lock(save_flags); __gpt_stop(dev); __gpt_set_clk(dev, clksrc, clkdiv); __gpt_start(dev); gpt_update_unlock(save_flags); return 0; } EXPORT_SYMBOL(gpt_set_clk); static int __init mt_gpt_init(struct device_node *node) { int i; unsigned long save_flags; gpt_update_lock(save_flags); /* Setup IRQ numbers */ gpt_timers.tmr_irq = irq_of_parse_and_map(node, 0); /* Setup IO addresses */ gpt_timers.tmr_regs = of_iomap(node, 0); pr_info("base=0x%lx, irq=%d\n", (unsigned long)gpt_timers.tmr_regs, gpt_timers.tmr_irq); /* setup gpt itself */ gpt_devs_init(); for (i = 0; i < NR_GPTS; i++) __gpt_reset(&gpt_devs[i]); setup_irq(gpt_timers.tmr_irq, &gpt_irq); mt_gpt_init_acao(node); /* record the time when init GPT */ boot_time_value = gpt_boot_up_time(); gpt_update_unlock(save_flags); return 0; } CLOCKSOURCE_OF_DECLARE(mtk_apxgpt, APXGPT_OF_COMPTIBLE_NAME, mt_gpt_init); #endif