162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Amlogic Meson6 SoCs timer handling. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2014 Carlo Caione <carlo@caione.org> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Based on code from Amlogic, Inc 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/bitfield.h> 1162306a36Sopenharmony_ci#include <linux/bitops.h> 1262306a36Sopenharmony_ci#include <linux/clk.h> 1362306a36Sopenharmony_ci#include <linux/clockchips.h> 1462306a36Sopenharmony_ci#include <linux/interrupt.h> 1562306a36Sopenharmony_ci#include <linux/irq.h> 1662306a36Sopenharmony_ci#include <linux/irqreturn.h> 1762306a36Sopenharmony_ci#include <linux/sched_clock.h> 1862306a36Sopenharmony_ci#include <linux/of.h> 1962306a36Sopenharmony_ci#include <linux/of_address.h> 2062306a36Sopenharmony_ci#include <linux/of_irq.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#ifdef CONFIG_ARM 2362306a36Sopenharmony_ci#include <linux/delay.h> 2462306a36Sopenharmony_ci#endif 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define MESON_ISA_TIMER_MUX 0x00 2762306a36Sopenharmony_ci#define MESON_ISA_TIMER_MUX_TIMERD_EN BIT(19) 2862306a36Sopenharmony_ci#define MESON_ISA_TIMER_MUX_TIMERC_EN BIT(18) 2962306a36Sopenharmony_ci#define MESON_ISA_TIMER_MUX_TIMERB_EN BIT(17) 3062306a36Sopenharmony_ci#define MESON_ISA_TIMER_MUX_TIMERA_EN BIT(16) 3162306a36Sopenharmony_ci#define MESON_ISA_TIMER_MUX_TIMERD_MODE BIT(15) 3262306a36Sopenharmony_ci#define MESON_ISA_TIMER_MUX_TIMERC_MODE BIT(14) 3362306a36Sopenharmony_ci#define MESON_ISA_TIMER_MUX_TIMERB_MODE BIT(13) 3462306a36Sopenharmony_ci#define MESON_ISA_TIMER_MUX_TIMERA_MODE BIT(12) 3562306a36Sopenharmony_ci#define MESON_ISA_TIMER_MUX_TIMERE_INPUT_CLOCK_MASK GENMASK(10, 8) 3662306a36Sopenharmony_ci#define MESON_ISA_TIMER_MUX_TIMERE_INPUT_CLOCK_SYSTEM_CLOCK 0x0 3762306a36Sopenharmony_ci#define MESON_ISA_TIMER_MUX_TIMERE_INPUT_CLOCK_1US 0x1 3862306a36Sopenharmony_ci#define MESON_ISA_TIMER_MUX_TIMERE_INPUT_CLOCK_10US 0x2 3962306a36Sopenharmony_ci#define MESON_ISA_TIMER_MUX_TIMERE_INPUT_CLOCK_100US 0x3 4062306a36Sopenharmony_ci#define MESON_ISA_TIMER_MUX_TIMERE_INPUT_CLOCK_1MS 0x4 4162306a36Sopenharmony_ci#define MESON_ISA_TIMER_MUX_TIMERD_INPUT_CLOCK_MASK GENMASK(7, 6) 4262306a36Sopenharmony_ci#define MESON_ISA_TIMER_MUX_TIMERC_INPUT_CLOCK_MASK GENMASK(5, 4) 4362306a36Sopenharmony_ci#define MESON_ISA_TIMER_MUX_TIMERB_INPUT_CLOCK_MASK GENMASK(3, 2) 4462306a36Sopenharmony_ci#define MESON_ISA_TIMER_MUX_TIMERA_INPUT_CLOCK_MASK GENMASK(1, 0) 4562306a36Sopenharmony_ci#define MESON_ISA_TIMER_MUX_TIMERABCD_INPUT_CLOCK_1US 0x0 4662306a36Sopenharmony_ci#define MESON_ISA_TIMER_MUX_TIMERABCD_INPUT_CLOCK_10US 0x1 4762306a36Sopenharmony_ci#define MESON_ISA_TIMER_MUX_TIMERABCD_INPUT_CLOCK_100US 0x0 4862306a36Sopenharmony_ci#define MESON_ISA_TIMER_MUX_TIMERABCD_INPUT_CLOCK_1MS 0x3 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci#define MESON_ISA_TIMERA 0x04 5162306a36Sopenharmony_ci#define MESON_ISA_TIMERB 0x08 5262306a36Sopenharmony_ci#define MESON_ISA_TIMERC 0x0c 5362306a36Sopenharmony_ci#define MESON_ISA_TIMERD 0x10 5462306a36Sopenharmony_ci#define MESON_ISA_TIMERE 0x14 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic void __iomem *timer_base; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci#ifdef CONFIG_ARM 5962306a36Sopenharmony_cistatic unsigned long meson6_read_current_timer(void) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci return readl_relaxed(timer_base + MESON_ISA_TIMERE); 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic struct delay_timer meson6_delay_timer = { 6562306a36Sopenharmony_ci .read_current_timer = meson6_read_current_timer, 6662306a36Sopenharmony_ci .freq = 1000 * 1000, 6762306a36Sopenharmony_ci}; 6862306a36Sopenharmony_ci#endif 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic u64 notrace meson6_timer_sched_read(void) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci return (u64)readl(timer_base + MESON_ISA_TIMERE); 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic void meson6_clkevt_time_stop(void) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci u32 val = readl(timer_base + MESON_ISA_TIMER_MUX); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci writel(val & ~MESON_ISA_TIMER_MUX_TIMERA_EN, 8062306a36Sopenharmony_ci timer_base + MESON_ISA_TIMER_MUX); 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic void meson6_clkevt_time_setup(unsigned long delay) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci writel(delay, timer_base + MESON_ISA_TIMERA); 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic void meson6_clkevt_time_start(bool periodic) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci u32 val = readl(timer_base + MESON_ISA_TIMER_MUX); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci if (periodic) 9362306a36Sopenharmony_ci val |= MESON_ISA_TIMER_MUX_TIMERA_MODE; 9462306a36Sopenharmony_ci else 9562306a36Sopenharmony_ci val &= ~MESON_ISA_TIMER_MUX_TIMERA_MODE; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci writel(val | MESON_ISA_TIMER_MUX_TIMERA_EN, 9862306a36Sopenharmony_ci timer_base + MESON_ISA_TIMER_MUX); 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistatic int meson6_shutdown(struct clock_event_device *evt) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci meson6_clkevt_time_stop(); 10462306a36Sopenharmony_ci return 0; 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic int meson6_set_oneshot(struct clock_event_device *evt) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci meson6_clkevt_time_stop(); 11062306a36Sopenharmony_ci meson6_clkevt_time_start(false); 11162306a36Sopenharmony_ci return 0; 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistatic int meson6_set_periodic(struct clock_event_device *evt) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci meson6_clkevt_time_stop(); 11762306a36Sopenharmony_ci meson6_clkevt_time_setup(USEC_PER_SEC / HZ - 1); 11862306a36Sopenharmony_ci meson6_clkevt_time_start(true); 11962306a36Sopenharmony_ci return 0; 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic int meson6_clkevt_next_event(unsigned long evt, 12362306a36Sopenharmony_ci struct clock_event_device *unused) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci meson6_clkevt_time_stop(); 12662306a36Sopenharmony_ci meson6_clkevt_time_setup(evt); 12762306a36Sopenharmony_ci meson6_clkevt_time_start(false); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci return 0; 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic struct clock_event_device meson6_clockevent = { 13362306a36Sopenharmony_ci .name = "meson6_tick", 13462306a36Sopenharmony_ci .rating = 400, 13562306a36Sopenharmony_ci .features = CLOCK_EVT_FEAT_PERIODIC | 13662306a36Sopenharmony_ci CLOCK_EVT_FEAT_ONESHOT, 13762306a36Sopenharmony_ci .set_state_shutdown = meson6_shutdown, 13862306a36Sopenharmony_ci .set_state_periodic = meson6_set_periodic, 13962306a36Sopenharmony_ci .set_state_oneshot = meson6_set_oneshot, 14062306a36Sopenharmony_ci .tick_resume = meson6_shutdown, 14162306a36Sopenharmony_ci .set_next_event = meson6_clkevt_next_event, 14262306a36Sopenharmony_ci}; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic irqreturn_t meson6_timer_interrupt(int irq, void *dev_id) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci struct clock_event_device *evt = (struct clock_event_device *)dev_id; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci evt->event_handler(evt); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci return IRQ_HANDLED; 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic int __init meson6_timer_init(struct device_node *node) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci u32 val; 15662306a36Sopenharmony_ci int ret, irq; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci timer_base = of_io_request_and_map(node, 0, "meson6-timer"); 15962306a36Sopenharmony_ci if (IS_ERR(timer_base)) { 16062306a36Sopenharmony_ci pr_err("Can't map registers\n"); 16162306a36Sopenharmony_ci return -ENXIO; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci irq = irq_of_parse_and_map(node, 0); 16562306a36Sopenharmony_ci if (irq <= 0) { 16662306a36Sopenharmony_ci pr_err("Can't parse IRQ\n"); 16762306a36Sopenharmony_ci return -EINVAL; 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci /* Set 1us for timer E */ 17162306a36Sopenharmony_ci val = readl(timer_base + MESON_ISA_TIMER_MUX); 17262306a36Sopenharmony_ci val &= ~MESON_ISA_TIMER_MUX_TIMERE_INPUT_CLOCK_MASK; 17362306a36Sopenharmony_ci val |= FIELD_PREP(MESON_ISA_TIMER_MUX_TIMERE_INPUT_CLOCK_MASK, 17462306a36Sopenharmony_ci MESON_ISA_TIMER_MUX_TIMERE_INPUT_CLOCK_1US); 17562306a36Sopenharmony_ci writel(val, timer_base + MESON_ISA_TIMER_MUX); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci sched_clock_register(meson6_timer_sched_read, 32, USEC_PER_SEC); 17862306a36Sopenharmony_ci clocksource_mmio_init(timer_base + MESON_ISA_TIMERE, node->name, 17962306a36Sopenharmony_ci 1000 * 1000, 300, 32, clocksource_mmio_readl_up); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci /* Timer A base 1us */ 18262306a36Sopenharmony_ci val &= ~MESON_ISA_TIMER_MUX_TIMERA_INPUT_CLOCK_MASK; 18362306a36Sopenharmony_ci val |= FIELD_PREP(MESON_ISA_TIMER_MUX_TIMERA_INPUT_CLOCK_MASK, 18462306a36Sopenharmony_ci MESON_ISA_TIMER_MUX_TIMERABCD_INPUT_CLOCK_1US); 18562306a36Sopenharmony_ci writel(val, timer_base + MESON_ISA_TIMER_MUX); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci /* Stop the timer A */ 18862306a36Sopenharmony_ci meson6_clkevt_time_stop(); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci ret = request_irq(irq, meson6_timer_interrupt, 19162306a36Sopenharmony_ci IRQF_TIMER | IRQF_IRQPOLL, "meson6_timer", 19262306a36Sopenharmony_ci &meson6_clockevent); 19362306a36Sopenharmony_ci if (ret) { 19462306a36Sopenharmony_ci pr_warn("failed to setup irq %d\n", irq); 19562306a36Sopenharmony_ci return ret; 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci meson6_clockevent.cpumask = cpu_possible_mask; 19962306a36Sopenharmony_ci meson6_clockevent.irq = irq; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci clockevents_config_and_register(&meson6_clockevent, USEC_PER_SEC, 20262306a36Sopenharmony_ci 1, 0xfffe); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci#ifdef CONFIG_ARM 20562306a36Sopenharmony_ci /* Also use MESON_ISA_TIMERE for delays */ 20662306a36Sopenharmony_ci register_current_timer_delay(&meson6_delay_timer); 20762306a36Sopenharmony_ci#endif 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci return 0; 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ciTIMER_OF_DECLARE(meson6, "amlogic,meson6-timer", 21262306a36Sopenharmony_ci meson6_timer_init); 213