18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * (C) Copyright 2009 Intel Corporation 48c2ecf20Sopenharmony_ci * Author: Jacob Pan (jacob.jun.pan@intel.com) 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Shared with ARM platforms, Jamie Iles, Picochip 2011 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Support for the Synopsys DesignWare APB Timers. 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci#include <linux/dw_apb_timer.h> 118c2ecf20Sopenharmony_ci#include <linux/delay.h> 128c2ecf20Sopenharmony_ci#include <linux/kernel.h> 138c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 148c2ecf20Sopenharmony_ci#include <linux/irq.h> 158c2ecf20Sopenharmony_ci#include <linux/io.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define APBT_MIN_PERIOD 4 198c2ecf20Sopenharmony_ci#define APBT_MIN_DELTA_USEC 200 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define APBTMR_N_LOAD_COUNT 0x00 228c2ecf20Sopenharmony_ci#define APBTMR_N_CURRENT_VALUE 0x04 238c2ecf20Sopenharmony_ci#define APBTMR_N_CONTROL 0x08 248c2ecf20Sopenharmony_ci#define APBTMR_N_EOI 0x0c 258c2ecf20Sopenharmony_ci#define APBTMR_N_INT_STATUS 0x10 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define APBTMRS_INT_STATUS 0xa0 288c2ecf20Sopenharmony_ci#define APBTMRS_EOI 0xa4 298c2ecf20Sopenharmony_ci#define APBTMRS_RAW_INT_STATUS 0xa8 308c2ecf20Sopenharmony_ci#define APBTMRS_COMP_VERSION 0xac 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define APBTMR_CONTROL_ENABLE (1 << 0) 338c2ecf20Sopenharmony_ci/* 1: periodic, 0:free running. */ 348c2ecf20Sopenharmony_ci#define APBTMR_CONTROL_MODE_PERIODIC (1 << 1) 358c2ecf20Sopenharmony_ci#define APBTMR_CONTROL_INT (1 << 2) 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic inline struct dw_apb_clock_event_device * 388c2ecf20Sopenharmony_ciced_to_dw_apb_ced(struct clock_event_device *evt) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci return container_of(evt, struct dw_apb_clock_event_device, ced); 418c2ecf20Sopenharmony_ci} 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic inline struct dw_apb_clocksource * 448c2ecf20Sopenharmony_ciclocksource_to_dw_apb_clocksource(struct clocksource *cs) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci return container_of(cs, struct dw_apb_clocksource, cs); 478c2ecf20Sopenharmony_ci} 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic inline u32 apbt_readl(struct dw_apb_timer *timer, unsigned long offs) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci return readl(timer->base + offs); 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic inline void apbt_writel(struct dw_apb_timer *timer, u32 val, 558c2ecf20Sopenharmony_ci unsigned long offs) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci writel(val, timer->base + offs); 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic inline u32 apbt_readl_relaxed(struct dw_apb_timer *timer, unsigned long offs) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci return readl_relaxed(timer->base + offs); 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic inline void apbt_writel_relaxed(struct dw_apb_timer *timer, u32 val, 668c2ecf20Sopenharmony_ci unsigned long offs) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci writel_relaxed(val, timer->base + offs); 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic void apbt_disable_int(struct dw_apb_timer *timer) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci u32 ctrl = apbt_readl(timer, APBTMR_N_CONTROL); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci ctrl |= APBTMR_CONTROL_INT; 768c2ecf20Sopenharmony_ci apbt_writel(timer, ctrl, APBTMR_N_CONTROL); 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci/** 808c2ecf20Sopenharmony_ci * dw_apb_clockevent_pause() - stop the clock_event_device from running 818c2ecf20Sopenharmony_ci * 828c2ecf20Sopenharmony_ci * @dw_ced: The APB clock to stop generating events. 838c2ecf20Sopenharmony_ci */ 848c2ecf20Sopenharmony_civoid dw_apb_clockevent_pause(struct dw_apb_clock_event_device *dw_ced) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci disable_irq(dw_ced->timer.irq); 878c2ecf20Sopenharmony_ci apbt_disable_int(&dw_ced->timer); 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic void apbt_eoi(struct dw_apb_timer *timer) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci apbt_readl_relaxed(timer, APBTMR_N_EOI); 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic irqreturn_t dw_apb_clockevent_irq(int irq, void *data) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci struct clock_event_device *evt = data; 988c2ecf20Sopenharmony_ci struct dw_apb_clock_event_device *dw_ced = ced_to_dw_apb_ced(evt); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci if (!evt->event_handler) { 1018c2ecf20Sopenharmony_ci pr_info("Spurious APBT timer interrupt %d\n", irq); 1028c2ecf20Sopenharmony_ci return IRQ_NONE; 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci if (dw_ced->eoi) 1068c2ecf20Sopenharmony_ci dw_ced->eoi(&dw_ced->timer); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci evt->event_handler(evt); 1098c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic void apbt_enable_int(struct dw_apb_timer *timer) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci u32 ctrl = apbt_readl(timer, APBTMR_N_CONTROL); 1158c2ecf20Sopenharmony_ci /* clear pending intr */ 1168c2ecf20Sopenharmony_ci apbt_readl(timer, APBTMR_N_EOI); 1178c2ecf20Sopenharmony_ci ctrl &= ~APBTMR_CONTROL_INT; 1188c2ecf20Sopenharmony_ci apbt_writel(timer, ctrl, APBTMR_N_CONTROL); 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic int apbt_shutdown(struct clock_event_device *evt) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci struct dw_apb_clock_event_device *dw_ced = ced_to_dw_apb_ced(evt); 1248c2ecf20Sopenharmony_ci u32 ctrl; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci pr_debug("%s CPU %d state=shutdown\n", __func__, 1278c2ecf20Sopenharmony_ci cpumask_first(evt->cpumask)); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci ctrl = apbt_readl(&dw_ced->timer, APBTMR_N_CONTROL); 1308c2ecf20Sopenharmony_ci ctrl &= ~APBTMR_CONTROL_ENABLE; 1318c2ecf20Sopenharmony_ci apbt_writel(&dw_ced->timer, ctrl, APBTMR_N_CONTROL); 1328c2ecf20Sopenharmony_ci return 0; 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cistatic int apbt_set_oneshot(struct clock_event_device *evt) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci struct dw_apb_clock_event_device *dw_ced = ced_to_dw_apb_ced(evt); 1388c2ecf20Sopenharmony_ci u32 ctrl; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci pr_debug("%s CPU %d state=oneshot\n", __func__, 1418c2ecf20Sopenharmony_ci cpumask_first(evt->cpumask)); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci ctrl = apbt_readl(&dw_ced->timer, APBTMR_N_CONTROL); 1448c2ecf20Sopenharmony_ci /* 1458c2ecf20Sopenharmony_ci * set free running mode, this mode will let timer reload max 1468c2ecf20Sopenharmony_ci * timeout which will give time (3min on 25MHz clock) to rearm 1478c2ecf20Sopenharmony_ci * the next event, therefore emulate the one-shot mode. 1488c2ecf20Sopenharmony_ci */ 1498c2ecf20Sopenharmony_ci ctrl &= ~APBTMR_CONTROL_ENABLE; 1508c2ecf20Sopenharmony_ci ctrl &= ~APBTMR_CONTROL_MODE_PERIODIC; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci apbt_writel(&dw_ced->timer, ctrl, APBTMR_N_CONTROL); 1538c2ecf20Sopenharmony_ci /* write again to set free running mode */ 1548c2ecf20Sopenharmony_ci apbt_writel(&dw_ced->timer, ctrl, APBTMR_N_CONTROL); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci /* 1578c2ecf20Sopenharmony_ci * DW APB p. 46, load counter with all 1s before starting free 1588c2ecf20Sopenharmony_ci * running mode. 1598c2ecf20Sopenharmony_ci */ 1608c2ecf20Sopenharmony_ci apbt_writel(&dw_ced->timer, ~0, APBTMR_N_LOAD_COUNT); 1618c2ecf20Sopenharmony_ci ctrl &= ~APBTMR_CONTROL_INT; 1628c2ecf20Sopenharmony_ci ctrl |= APBTMR_CONTROL_ENABLE; 1638c2ecf20Sopenharmony_ci apbt_writel(&dw_ced->timer, ctrl, APBTMR_N_CONTROL); 1648c2ecf20Sopenharmony_ci return 0; 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cistatic int apbt_set_periodic(struct clock_event_device *evt) 1688c2ecf20Sopenharmony_ci{ 1698c2ecf20Sopenharmony_ci struct dw_apb_clock_event_device *dw_ced = ced_to_dw_apb_ced(evt); 1708c2ecf20Sopenharmony_ci unsigned long period = DIV_ROUND_UP(dw_ced->timer.freq, HZ); 1718c2ecf20Sopenharmony_ci u32 ctrl; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci pr_debug("%s CPU %d state=periodic\n", __func__, 1748c2ecf20Sopenharmony_ci cpumask_first(evt->cpumask)); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci ctrl = apbt_readl(&dw_ced->timer, APBTMR_N_CONTROL); 1778c2ecf20Sopenharmony_ci ctrl |= APBTMR_CONTROL_MODE_PERIODIC; 1788c2ecf20Sopenharmony_ci apbt_writel(&dw_ced->timer, ctrl, APBTMR_N_CONTROL); 1798c2ecf20Sopenharmony_ci /* 1808c2ecf20Sopenharmony_ci * DW APB p. 46, have to disable timer before load counter, 1818c2ecf20Sopenharmony_ci * may cause sync problem. 1828c2ecf20Sopenharmony_ci */ 1838c2ecf20Sopenharmony_ci ctrl &= ~APBTMR_CONTROL_ENABLE; 1848c2ecf20Sopenharmony_ci apbt_writel(&dw_ced->timer, ctrl, APBTMR_N_CONTROL); 1858c2ecf20Sopenharmony_ci udelay(1); 1868c2ecf20Sopenharmony_ci pr_debug("Setting clock period %lu for HZ %d\n", period, HZ); 1878c2ecf20Sopenharmony_ci apbt_writel(&dw_ced->timer, period, APBTMR_N_LOAD_COUNT); 1888c2ecf20Sopenharmony_ci ctrl |= APBTMR_CONTROL_ENABLE; 1898c2ecf20Sopenharmony_ci apbt_writel(&dw_ced->timer, ctrl, APBTMR_N_CONTROL); 1908c2ecf20Sopenharmony_ci return 0; 1918c2ecf20Sopenharmony_ci} 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_cistatic int apbt_resume(struct clock_event_device *evt) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci struct dw_apb_clock_event_device *dw_ced = ced_to_dw_apb_ced(evt); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci pr_debug("%s CPU %d state=resume\n", __func__, 1988c2ecf20Sopenharmony_ci cpumask_first(evt->cpumask)); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci apbt_enable_int(&dw_ced->timer); 2018c2ecf20Sopenharmony_ci return 0; 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic int apbt_next_event(unsigned long delta, 2058c2ecf20Sopenharmony_ci struct clock_event_device *evt) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci u32 ctrl; 2088c2ecf20Sopenharmony_ci struct dw_apb_clock_event_device *dw_ced = ced_to_dw_apb_ced(evt); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci /* Disable timer */ 2118c2ecf20Sopenharmony_ci ctrl = apbt_readl_relaxed(&dw_ced->timer, APBTMR_N_CONTROL); 2128c2ecf20Sopenharmony_ci ctrl &= ~APBTMR_CONTROL_ENABLE; 2138c2ecf20Sopenharmony_ci apbt_writel_relaxed(&dw_ced->timer, ctrl, APBTMR_N_CONTROL); 2148c2ecf20Sopenharmony_ci /* write new count */ 2158c2ecf20Sopenharmony_ci apbt_writel_relaxed(&dw_ced->timer, delta, APBTMR_N_LOAD_COUNT); 2168c2ecf20Sopenharmony_ci ctrl |= APBTMR_CONTROL_ENABLE; 2178c2ecf20Sopenharmony_ci apbt_writel_relaxed(&dw_ced->timer, ctrl, APBTMR_N_CONTROL); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci return 0; 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci/** 2238c2ecf20Sopenharmony_ci * dw_apb_clockevent_init() - use an APB timer as a clock_event_device 2248c2ecf20Sopenharmony_ci * 2258c2ecf20Sopenharmony_ci * @cpu: The CPU the events will be targeted at or -1 if CPU affiliation 2268c2ecf20Sopenharmony_ci * isn't required. 2278c2ecf20Sopenharmony_ci * @name: The name used for the timer and the IRQ for it. 2288c2ecf20Sopenharmony_ci * @rating: The rating to give the timer. 2298c2ecf20Sopenharmony_ci * @base: I/O base for the timer registers. 2308c2ecf20Sopenharmony_ci * @irq: The interrupt number to use for the timer. 2318c2ecf20Sopenharmony_ci * @freq: The frequency that the timer counts at. 2328c2ecf20Sopenharmony_ci * 2338c2ecf20Sopenharmony_ci * This creates a clock_event_device for using with the generic clock layer 2348c2ecf20Sopenharmony_ci * but does not start and register it. This should be done with 2358c2ecf20Sopenharmony_ci * dw_apb_clockevent_register() as the next step. If this is the first time 2368c2ecf20Sopenharmony_ci * it has been called for a timer then the IRQ will be requested, if not it 2378c2ecf20Sopenharmony_ci * just be enabled to allow CPU hotplug to avoid repeatedly requesting and 2388c2ecf20Sopenharmony_ci * releasing the IRQ. 2398c2ecf20Sopenharmony_ci */ 2408c2ecf20Sopenharmony_cistruct dw_apb_clock_event_device * 2418c2ecf20Sopenharmony_cidw_apb_clockevent_init(int cpu, const char *name, unsigned rating, 2428c2ecf20Sopenharmony_ci void __iomem *base, int irq, unsigned long freq) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci struct dw_apb_clock_event_device *dw_ced = 2458c2ecf20Sopenharmony_ci kzalloc(sizeof(*dw_ced), GFP_KERNEL); 2468c2ecf20Sopenharmony_ci int err; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci if (!dw_ced) 2498c2ecf20Sopenharmony_ci return NULL; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci dw_ced->timer.base = base; 2528c2ecf20Sopenharmony_ci dw_ced->timer.irq = irq; 2538c2ecf20Sopenharmony_ci dw_ced->timer.freq = freq; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci clockevents_calc_mult_shift(&dw_ced->ced, freq, APBT_MIN_PERIOD); 2568c2ecf20Sopenharmony_ci dw_ced->ced.max_delta_ns = clockevent_delta2ns(0x7fffffff, 2578c2ecf20Sopenharmony_ci &dw_ced->ced); 2588c2ecf20Sopenharmony_ci dw_ced->ced.max_delta_ticks = 0x7fffffff; 2598c2ecf20Sopenharmony_ci dw_ced->ced.min_delta_ns = clockevent_delta2ns(5000, &dw_ced->ced); 2608c2ecf20Sopenharmony_ci dw_ced->ced.min_delta_ticks = 5000; 2618c2ecf20Sopenharmony_ci dw_ced->ced.cpumask = cpu < 0 ? cpu_possible_mask : cpumask_of(cpu); 2628c2ecf20Sopenharmony_ci dw_ced->ced.features = CLOCK_EVT_FEAT_PERIODIC | 2638c2ecf20Sopenharmony_ci CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_DYNIRQ; 2648c2ecf20Sopenharmony_ci dw_ced->ced.set_state_shutdown = apbt_shutdown; 2658c2ecf20Sopenharmony_ci dw_ced->ced.set_state_periodic = apbt_set_periodic; 2668c2ecf20Sopenharmony_ci dw_ced->ced.set_state_oneshot = apbt_set_oneshot; 2678c2ecf20Sopenharmony_ci dw_ced->ced.set_state_oneshot_stopped = apbt_shutdown; 2688c2ecf20Sopenharmony_ci dw_ced->ced.tick_resume = apbt_resume; 2698c2ecf20Sopenharmony_ci dw_ced->ced.set_next_event = apbt_next_event; 2708c2ecf20Sopenharmony_ci dw_ced->ced.irq = dw_ced->timer.irq; 2718c2ecf20Sopenharmony_ci dw_ced->ced.rating = rating; 2728c2ecf20Sopenharmony_ci dw_ced->ced.name = name; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci dw_ced->eoi = apbt_eoi; 2758c2ecf20Sopenharmony_ci err = request_irq(irq, dw_apb_clockevent_irq, 2768c2ecf20Sopenharmony_ci IRQF_TIMER | IRQF_IRQPOLL | IRQF_NOBALANCING, 2778c2ecf20Sopenharmony_ci dw_ced->ced.name, &dw_ced->ced); 2788c2ecf20Sopenharmony_ci if (err) { 2798c2ecf20Sopenharmony_ci pr_err("failed to request timer irq\n"); 2808c2ecf20Sopenharmony_ci kfree(dw_ced); 2818c2ecf20Sopenharmony_ci dw_ced = NULL; 2828c2ecf20Sopenharmony_ci } 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci return dw_ced; 2858c2ecf20Sopenharmony_ci} 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci/** 2888c2ecf20Sopenharmony_ci * dw_apb_clockevent_resume() - resume a clock that has been paused. 2898c2ecf20Sopenharmony_ci * 2908c2ecf20Sopenharmony_ci * @dw_ced: The APB clock to resume. 2918c2ecf20Sopenharmony_ci */ 2928c2ecf20Sopenharmony_civoid dw_apb_clockevent_resume(struct dw_apb_clock_event_device *dw_ced) 2938c2ecf20Sopenharmony_ci{ 2948c2ecf20Sopenharmony_ci enable_irq(dw_ced->timer.irq); 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci/** 2988c2ecf20Sopenharmony_ci * dw_apb_clockevent_stop() - stop the clock_event_device and release the IRQ. 2998c2ecf20Sopenharmony_ci * 3008c2ecf20Sopenharmony_ci * @dw_ced: The APB clock to stop generating the events. 3018c2ecf20Sopenharmony_ci */ 3028c2ecf20Sopenharmony_civoid dw_apb_clockevent_stop(struct dw_apb_clock_event_device *dw_ced) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci free_irq(dw_ced->timer.irq, &dw_ced->ced); 3058c2ecf20Sopenharmony_ci} 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci/** 3088c2ecf20Sopenharmony_ci * dw_apb_clockevent_register() - register the clock with the generic layer 3098c2ecf20Sopenharmony_ci * 3108c2ecf20Sopenharmony_ci * @dw_ced: The APB clock to register as a clock_event_device. 3118c2ecf20Sopenharmony_ci */ 3128c2ecf20Sopenharmony_civoid dw_apb_clockevent_register(struct dw_apb_clock_event_device *dw_ced) 3138c2ecf20Sopenharmony_ci{ 3148c2ecf20Sopenharmony_ci apbt_writel(&dw_ced->timer, 0, APBTMR_N_CONTROL); 3158c2ecf20Sopenharmony_ci clockevents_register_device(&dw_ced->ced); 3168c2ecf20Sopenharmony_ci apbt_enable_int(&dw_ced->timer); 3178c2ecf20Sopenharmony_ci} 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci/** 3208c2ecf20Sopenharmony_ci * dw_apb_clocksource_start() - start the clocksource counting. 3218c2ecf20Sopenharmony_ci * 3228c2ecf20Sopenharmony_ci * @dw_cs: The clocksource to start. 3238c2ecf20Sopenharmony_ci * 3248c2ecf20Sopenharmony_ci * This is used to start the clocksource before registration and can be used 3258c2ecf20Sopenharmony_ci * to enable calibration of timers. 3268c2ecf20Sopenharmony_ci */ 3278c2ecf20Sopenharmony_civoid dw_apb_clocksource_start(struct dw_apb_clocksource *dw_cs) 3288c2ecf20Sopenharmony_ci{ 3298c2ecf20Sopenharmony_ci /* 3308c2ecf20Sopenharmony_ci * start count down from 0xffff_ffff. this is done by toggling the 3318c2ecf20Sopenharmony_ci * enable bit then load initial load count to ~0. 3328c2ecf20Sopenharmony_ci */ 3338c2ecf20Sopenharmony_ci u32 ctrl = apbt_readl(&dw_cs->timer, APBTMR_N_CONTROL); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci ctrl &= ~APBTMR_CONTROL_ENABLE; 3368c2ecf20Sopenharmony_ci apbt_writel(&dw_cs->timer, ctrl, APBTMR_N_CONTROL); 3378c2ecf20Sopenharmony_ci apbt_writel(&dw_cs->timer, ~0, APBTMR_N_LOAD_COUNT); 3388c2ecf20Sopenharmony_ci /* enable, mask interrupt */ 3398c2ecf20Sopenharmony_ci ctrl &= ~APBTMR_CONTROL_MODE_PERIODIC; 3408c2ecf20Sopenharmony_ci ctrl |= (APBTMR_CONTROL_ENABLE | APBTMR_CONTROL_INT); 3418c2ecf20Sopenharmony_ci apbt_writel(&dw_cs->timer, ctrl, APBTMR_N_CONTROL); 3428c2ecf20Sopenharmony_ci /* read it once to get cached counter value initialized */ 3438c2ecf20Sopenharmony_ci dw_apb_clocksource_read(dw_cs); 3448c2ecf20Sopenharmony_ci} 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_cistatic u64 __apbt_read_clocksource(struct clocksource *cs) 3478c2ecf20Sopenharmony_ci{ 3488c2ecf20Sopenharmony_ci u32 current_count; 3498c2ecf20Sopenharmony_ci struct dw_apb_clocksource *dw_cs = 3508c2ecf20Sopenharmony_ci clocksource_to_dw_apb_clocksource(cs); 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci current_count = apbt_readl_relaxed(&dw_cs->timer, 3538c2ecf20Sopenharmony_ci APBTMR_N_CURRENT_VALUE); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci return (u64)~current_count; 3568c2ecf20Sopenharmony_ci} 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_cistatic void apbt_restart_clocksource(struct clocksource *cs) 3598c2ecf20Sopenharmony_ci{ 3608c2ecf20Sopenharmony_ci struct dw_apb_clocksource *dw_cs = 3618c2ecf20Sopenharmony_ci clocksource_to_dw_apb_clocksource(cs); 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci dw_apb_clocksource_start(dw_cs); 3648c2ecf20Sopenharmony_ci} 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci/** 3678c2ecf20Sopenharmony_ci * dw_apb_clocksource_init() - use an APB timer as a clocksource. 3688c2ecf20Sopenharmony_ci * 3698c2ecf20Sopenharmony_ci * @rating: The rating to give the clocksource. 3708c2ecf20Sopenharmony_ci * @name: The name for the clocksource. 3718c2ecf20Sopenharmony_ci * @base: The I/O base for the timer registers. 3728c2ecf20Sopenharmony_ci * @freq: The frequency that the timer counts at. 3738c2ecf20Sopenharmony_ci * 3748c2ecf20Sopenharmony_ci * This creates a clocksource using an APB timer but does not yet register it 3758c2ecf20Sopenharmony_ci * with the clocksource system. This should be done with 3768c2ecf20Sopenharmony_ci * dw_apb_clocksource_register() as the next step. 3778c2ecf20Sopenharmony_ci */ 3788c2ecf20Sopenharmony_cistruct dw_apb_clocksource * 3798c2ecf20Sopenharmony_cidw_apb_clocksource_init(unsigned rating, const char *name, void __iomem *base, 3808c2ecf20Sopenharmony_ci unsigned long freq) 3818c2ecf20Sopenharmony_ci{ 3828c2ecf20Sopenharmony_ci struct dw_apb_clocksource *dw_cs = kzalloc(sizeof(*dw_cs), GFP_KERNEL); 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci if (!dw_cs) 3858c2ecf20Sopenharmony_ci return NULL; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci dw_cs->timer.base = base; 3888c2ecf20Sopenharmony_ci dw_cs->timer.freq = freq; 3898c2ecf20Sopenharmony_ci dw_cs->cs.name = name; 3908c2ecf20Sopenharmony_ci dw_cs->cs.rating = rating; 3918c2ecf20Sopenharmony_ci dw_cs->cs.read = __apbt_read_clocksource; 3928c2ecf20Sopenharmony_ci dw_cs->cs.mask = CLOCKSOURCE_MASK(32); 3938c2ecf20Sopenharmony_ci dw_cs->cs.flags = CLOCK_SOURCE_IS_CONTINUOUS; 3948c2ecf20Sopenharmony_ci dw_cs->cs.resume = apbt_restart_clocksource; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci return dw_cs; 3978c2ecf20Sopenharmony_ci} 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci/** 4008c2ecf20Sopenharmony_ci * dw_apb_clocksource_register() - register the APB clocksource. 4018c2ecf20Sopenharmony_ci * 4028c2ecf20Sopenharmony_ci * @dw_cs: The clocksource to register. 4038c2ecf20Sopenharmony_ci */ 4048c2ecf20Sopenharmony_civoid dw_apb_clocksource_register(struct dw_apb_clocksource *dw_cs) 4058c2ecf20Sopenharmony_ci{ 4068c2ecf20Sopenharmony_ci clocksource_register_hz(&dw_cs->cs, dw_cs->timer.freq); 4078c2ecf20Sopenharmony_ci} 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci/** 4108c2ecf20Sopenharmony_ci * dw_apb_clocksource_read() - read the current value of a clocksource. 4118c2ecf20Sopenharmony_ci * 4128c2ecf20Sopenharmony_ci * @dw_cs: The clocksource to read. 4138c2ecf20Sopenharmony_ci */ 4148c2ecf20Sopenharmony_ciu64 dw_apb_clocksource_read(struct dw_apb_clocksource *dw_cs) 4158c2ecf20Sopenharmony_ci{ 4168c2ecf20Sopenharmony_ci return (u64)~apbt_readl(&dw_cs->timer, APBTMR_N_CURRENT_VALUE); 4178c2ecf20Sopenharmony_ci} 418