/* * Copyright 2018-2019 Arkmicro, Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope 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 . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define TIMER0_BASE 0x00 #define TIMER1_BASE 0x04 #define TIMER2_BASE 0x08 #if defined(CONFIG_TIMER_ARK1668) #define TIMER_TCTL 0x00 #define TIMER_TPRS 0x10 #define TIMER_TMOD 0x20 #define TIMER_TCNT 0x30 #define TIMER_TSTA 0x40 #define TIMER_INT_MAINTAIN 0 #define TIMER_CNT_VAL 0xffffff #define TIMER_CNT_BITS 24 #elif defined(CONFIG_TIMER_ARKN141) #define TIMER_TCTL 0x00 #define TIMER_TPRS 0x0C #define TIMER_TMOD 0x18 #define TIMER_TCNT 0x24 #define TIMER_INT_MAINTAIN (1 << 3) #define TIMER_INT_STATUS (1 << 4) #define TIMER_CNT_VAL 0xffffffff #define TIMER_CNT_BITS 32 #define USE_SIBLING_TIMER #endif #define TIMER_CTRL_IE (1 << 2) #define TIMER_CTRL_PERIODIC (1 << 1) #define TIMER_CTRL_ENABLE (1 << 0) static void __iomem *sched_clock_base; static u64 notrace ark_read(void) { return ~(readl_relaxed(sched_clock_base + TIMER_TCNT) | ((u64)((1 << (32 - TIMER_CNT_BITS)) - 1) << TIMER_CNT_BITS)); } void __init ark_timer_disable(void __iomem *base) { writel(0, base + TIMER_TCTL); } int __init ark_clocksource_and_sched_clock_init(void __iomem *base, const char *name, struct clk *clk, int use_sched_clock) { long rate; rate = clk_get_rate(clk); if (rate == 0) return -EINVAL; /* setup timer 1 as free-running clocksource */ writel(0, base + TIMER_TCTL); writel(TIMER_CNT_VAL, base + TIMER_TMOD); writel(TIMER_CTRL_ENABLE | TIMER_CTRL_PERIODIC, base + TIMER_TCTL); clocksource_mmio_init(base + TIMER_TCNT, name, rate, 300, TIMER_CNT_BITS, clocksource_mmio_readl_down); if (use_sched_clock) { sched_clock_base = base; sched_clock_register(ark_read, TIMER_CNT_BITS, rate); } return 0; } static void __iomem *clkevt_base; static unsigned long clkevt_reload; #ifdef USE_SIBLING_TIMER static unsigned long usec_reload; #endif /* * IRQ handler for the timer */ static irqreturn_t ark_timer_interrupt(int irq, void *dev_id) { struct clock_event_device *evt = dev_id; u32 reg; /* clear the interrupt */ #if defined(CONFIG_TIMER_ARK1668) reg = readl(clkevt_base + TIMER_TSTA); reg &= ~1; writel(reg, clkevt_base + TIMER_TSTA); #elif defined(CONFIG_TIMER_ARKN141) reg = readl(clkevt_base + TIMER_TCTL); reg &= ~TIMER_INT_STATUS; writel(reg, clkevt_base + TIMER_TCTL); #endif #ifdef USE_SIBLING_TIMER writel(0, clkevt_base + TIMER2_BASE + TIMER_TCTL); #endif evt->event_handler(evt); return IRQ_HANDLED; } static inline void timer_shutdown(struct clock_event_device *evt) { #if defined(CONFIG_TIMER_ARK1668) writel(0, clkevt_base + TIMER_TCTL); #elif defined(CONFIG_TIMER_ARKN141) /* not clear interrupt */ writel(1 << TIMER_INT_STATUS, clkevt_base + TIMER_TCTL); #endif } static int ark_shutdown(struct clock_event_device *evt) { timer_shutdown(evt); return 0; } static int ark_set_periodic(struct clock_event_device *evt) { unsigned long ctrl = TIMER_INT_MAINTAIN | TIMER_CTRL_IE | TIMER_CTRL_PERIODIC | TIMER_CTRL_ENABLE; timer_shutdown(evt); #if defined(CONFIG_TIMER_ARK1668) writel(1 << 1, clkevt_base + TIMER_TSTA); #endif writel(clkevt_reload, clkevt_base + TIMER_TMOD); writel(ctrl, clkevt_base + TIMER_TCTL); return 0; } static int ark_set_next_event(unsigned long next, struct clock_event_device *evt) { unsigned long ctrl = TIMER_INT_MAINTAIN | TIMER_CTRL_IE | TIMER_CTRL_ENABLE; timer_shutdown(evt); #if defined(CONFIG_TIMER_ARK1668) writel(1 << 1, clkevt_base + TIMER_TSTA); #endif writel(next, clkevt_base + TIMER_TMOD); writel(ctrl, clkevt_base + TIMER_TCTL); #ifdef USE_SIBLING_TIMER writel(0, clkevt_base + TIMER2_BASE + TIMER_TCTL); writel(next + usec_reload, clkevt_base + TIMER2_BASE + TIMER_TMOD); writel(ctrl, clkevt_base + TIMER2_BASE + TIMER_TCTL); #endif return 0; } static struct clock_event_device ark_clockevent = { .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, .set_state_shutdown = ark_shutdown, .set_state_periodic = ark_set_periodic, .set_state_oneshot = ark_shutdown, .tick_resume = ark_shutdown, .set_next_event = ark_set_next_event, .rating = 300, }; static struct irqaction ark_timer_irq = { .name = "timer", .flags = IRQF_TIMER | IRQF_IRQPOLL, .handler = ark_timer_interrupt, .dev_id = &ark_clockevent, }; int __init ark_clockevents_init(void __iomem *base, unsigned int irq, struct clk *clk, const char *name) { struct clock_event_device *evt = &ark_clockevent; long rate; rate = clk_get_rate(clk); if (rate == 0) return -EINVAL; clkevt_base = base; clkevt_reload = DIV_ROUND_CLOSEST(rate, HZ); #ifdef USE_SIBLING_TIMER usec_reload = DIV_ROUND_CLOSEST(rate, 1000000); #endif evt->name = name; evt->irq = irq; evt->cpumask = cpu_possible_mask; writel(0, base + TIMER_TCTL); setup_irq(irq, &ark_timer_irq); clockevents_config_and_register(evt, rate, 0xf, TIMER_CNT_VAL); return 0; } #ifdef USE_SIBLING_TIMER static irqreturn_t ark_sibling_timer_interrupt(int irq, void *dev_id) { u32 ctrl, mod, cnt; writel(0, clkevt_base + TIMER2_BASE + TIMER_TCTL); //restart clockevent timer ctrl = readl(clkevt_base + TIMER_TCTL); mod = readl(clkevt_base + TIMER_TMOD); cnt = readl(clkevt_base + TIMER_TCNT); if (ctrl == (TIMER_INT_MAINTAIN | TIMER_CTRL_IE | TIMER_CTRL_ENABLE) && mod != 0 && cnt == 0) { writel(0, clkevt_base + TIMER_TCTL); writel(10, clkevt_base + TIMER_TMOD); writel(ctrl, clkevt_base + TIMER_TCTL); } return IRQ_HANDLED; } static struct irqaction ark_sibling_timer_irq = { .name = "sibling-timer", .flags = IRQF_TIMER | IRQF_IRQPOLL, .handler = ark_sibling_timer_interrupt, }; #endif static int __init ark_timer_init(struct device_node *np) { static bool initialized = false; void __iomem *base; int irq, ret = -EINVAL; struct clk *clk; const char *name = of_get_property(np, "compatible", NULL); base = of_iomap(np, 0); if (!base) return -ENXIO; /* Ensure timers are disabled */ writel(0, base + TIMER_TCTL); writel(0, base + TIMER1_BASE + TIMER_TCTL); //reset clk prescale writel(0, base + TIMER_TPRS); writel(0, base + TIMER1_BASE + TIMER_TPRS); if (initialized || !of_device_is_available(np)) { ret = -EINVAL; goto err; } clk = of_clk_get(np, 0); if (IS_ERR(clk)) goto err; irq = irq_of_parse_and_map(np, 0); if (irq <= 0) goto err; ret = ark_clockevents_init(base, irq, clk , name); if (ret) goto err; ret = ark_clocksource_and_sched_clock_init(base + TIMER1_BASE, name, clk, 1); if (ret) goto err; #ifdef USE_SIBLING_TIMER /* trigger a timer to detect clockevent timer error */ writel(0, base + TIMER2_BASE + TIMER_TCTL); writel(0, base + TIMER2_BASE + TIMER_TPRS); irq = irq_of_parse_and_map(np, 1); setup_irq(irq, &ark_sibling_timer_irq); #endif initialized = true; return 0; err: iounmap(base); return ret; } TIMER_OF_DECLARE(arkmicro_timer, "arkmicro,ark-timer", ark_timer_init); MODULE_AUTHOR("Sim"); MODULE_DESCRIPTION("Arkmicro timer driver"); MODULE_LICENSE("GPL v2");