162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * (C) Copyright 2009 Intel Corporation 462306a36Sopenharmony_ci * Author: Jacob Pan (jacob.jun.pan@intel.com) 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Shared with ARM platforms, Jamie Iles, Picochip 2011 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Support for the Synopsys DesignWare APB Timers. 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci#include <linux/dw_apb_timer.h> 1162306a36Sopenharmony_ci#include <linux/delay.h> 1262306a36Sopenharmony_ci#include <linux/kernel.h> 1362306a36Sopenharmony_ci#include <linux/interrupt.h> 1462306a36Sopenharmony_ci#include <linux/irq.h> 1562306a36Sopenharmony_ci#include <linux/io.h> 1662306a36Sopenharmony_ci#include <linux/slab.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#define APBT_MIN_PERIOD 4 1962306a36Sopenharmony_ci#define APBT_MIN_DELTA_USEC 200 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define APBTMR_N_LOAD_COUNT 0x00 2262306a36Sopenharmony_ci#define APBTMR_N_CURRENT_VALUE 0x04 2362306a36Sopenharmony_ci#define APBTMR_N_CONTROL 0x08 2462306a36Sopenharmony_ci#define APBTMR_N_EOI 0x0c 2562306a36Sopenharmony_ci#define APBTMR_N_INT_STATUS 0x10 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define APBTMRS_INT_STATUS 0xa0 2862306a36Sopenharmony_ci#define APBTMRS_EOI 0xa4 2962306a36Sopenharmony_ci#define APBTMRS_RAW_INT_STATUS 0xa8 3062306a36Sopenharmony_ci#define APBTMRS_COMP_VERSION 0xac 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#define APBTMR_CONTROL_ENABLE (1 << 0) 3362306a36Sopenharmony_ci/* 1: periodic, 0:free running. */ 3462306a36Sopenharmony_ci#define APBTMR_CONTROL_MODE_PERIODIC (1 << 1) 3562306a36Sopenharmony_ci#define APBTMR_CONTROL_INT (1 << 2) 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic inline struct dw_apb_clock_event_device * 3862306a36Sopenharmony_ciced_to_dw_apb_ced(struct clock_event_device *evt) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci return container_of(evt, struct dw_apb_clock_event_device, ced); 4162306a36Sopenharmony_ci} 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic inline struct dw_apb_clocksource * 4462306a36Sopenharmony_ciclocksource_to_dw_apb_clocksource(struct clocksource *cs) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci return container_of(cs, struct dw_apb_clocksource, cs); 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic inline u32 apbt_readl(struct dw_apb_timer *timer, unsigned long offs) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci return readl(timer->base + offs); 5262306a36Sopenharmony_ci} 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic inline void apbt_writel(struct dw_apb_timer *timer, u32 val, 5562306a36Sopenharmony_ci unsigned long offs) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci writel(val, timer->base + offs); 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic inline u32 apbt_readl_relaxed(struct dw_apb_timer *timer, unsigned long offs) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci return readl_relaxed(timer->base + offs); 6362306a36Sopenharmony_ci} 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistatic inline void apbt_writel_relaxed(struct dw_apb_timer *timer, u32 val, 6662306a36Sopenharmony_ci unsigned long offs) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci writel_relaxed(val, timer->base + offs); 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic void apbt_disable_int(struct dw_apb_timer *timer) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci u32 ctrl = apbt_readl(timer, APBTMR_N_CONTROL); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci ctrl |= APBTMR_CONTROL_INT; 7662306a36Sopenharmony_ci apbt_writel(timer, ctrl, APBTMR_N_CONTROL); 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci/** 8062306a36Sopenharmony_ci * dw_apb_clockevent_pause() - stop the clock_event_device from running 8162306a36Sopenharmony_ci * 8262306a36Sopenharmony_ci * @dw_ced: The APB clock to stop generating events. 8362306a36Sopenharmony_ci */ 8462306a36Sopenharmony_civoid dw_apb_clockevent_pause(struct dw_apb_clock_event_device *dw_ced) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci disable_irq(dw_ced->timer.irq); 8762306a36Sopenharmony_ci apbt_disable_int(&dw_ced->timer); 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistatic void apbt_eoi(struct dw_apb_timer *timer) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci apbt_readl_relaxed(timer, APBTMR_N_EOI); 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistatic irqreturn_t dw_apb_clockevent_irq(int irq, void *data) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci struct clock_event_device *evt = data; 9862306a36Sopenharmony_ci struct dw_apb_clock_event_device *dw_ced = ced_to_dw_apb_ced(evt); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci if (!evt->event_handler) { 10162306a36Sopenharmony_ci pr_info("Spurious APBT timer interrupt %d\n", irq); 10262306a36Sopenharmony_ci return IRQ_NONE; 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci if (dw_ced->eoi) 10662306a36Sopenharmony_ci dw_ced->eoi(&dw_ced->timer); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci evt->event_handler(evt); 10962306a36Sopenharmony_ci return IRQ_HANDLED; 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic void apbt_enable_int(struct dw_apb_timer *timer) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci u32 ctrl = apbt_readl(timer, APBTMR_N_CONTROL); 11562306a36Sopenharmony_ci /* clear pending intr */ 11662306a36Sopenharmony_ci apbt_readl(timer, APBTMR_N_EOI); 11762306a36Sopenharmony_ci ctrl &= ~APBTMR_CONTROL_INT; 11862306a36Sopenharmony_ci apbt_writel(timer, ctrl, APBTMR_N_CONTROL); 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic int apbt_shutdown(struct clock_event_device *evt) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci struct dw_apb_clock_event_device *dw_ced = ced_to_dw_apb_ced(evt); 12462306a36Sopenharmony_ci u32 ctrl; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci pr_debug("%s CPU %d state=shutdown\n", __func__, 12762306a36Sopenharmony_ci cpumask_first(evt->cpumask)); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci ctrl = apbt_readl(&dw_ced->timer, APBTMR_N_CONTROL); 13062306a36Sopenharmony_ci ctrl &= ~APBTMR_CONTROL_ENABLE; 13162306a36Sopenharmony_ci apbt_writel(&dw_ced->timer, ctrl, APBTMR_N_CONTROL); 13262306a36Sopenharmony_ci return 0; 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic int apbt_set_oneshot(struct clock_event_device *evt) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci struct dw_apb_clock_event_device *dw_ced = ced_to_dw_apb_ced(evt); 13862306a36Sopenharmony_ci u32 ctrl; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci pr_debug("%s CPU %d state=oneshot\n", __func__, 14162306a36Sopenharmony_ci cpumask_first(evt->cpumask)); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci ctrl = apbt_readl(&dw_ced->timer, APBTMR_N_CONTROL); 14462306a36Sopenharmony_ci /* 14562306a36Sopenharmony_ci * set free running mode, this mode will let timer reload max 14662306a36Sopenharmony_ci * timeout which will give time (3min on 25MHz clock) to rearm 14762306a36Sopenharmony_ci * the next event, therefore emulate the one-shot mode. 14862306a36Sopenharmony_ci */ 14962306a36Sopenharmony_ci ctrl &= ~APBTMR_CONTROL_ENABLE; 15062306a36Sopenharmony_ci ctrl &= ~APBTMR_CONTROL_MODE_PERIODIC; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci apbt_writel(&dw_ced->timer, ctrl, APBTMR_N_CONTROL); 15362306a36Sopenharmony_ci /* write again to set free running mode */ 15462306a36Sopenharmony_ci apbt_writel(&dw_ced->timer, ctrl, APBTMR_N_CONTROL); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci /* 15762306a36Sopenharmony_ci * DW APB p. 46, load counter with all 1s before starting free 15862306a36Sopenharmony_ci * running mode. 15962306a36Sopenharmony_ci */ 16062306a36Sopenharmony_ci apbt_writel(&dw_ced->timer, ~0, APBTMR_N_LOAD_COUNT); 16162306a36Sopenharmony_ci ctrl &= ~APBTMR_CONTROL_INT; 16262306a36Sopenharmony_ci ctrl |= APBTMR_CONTROL_ENABLE; 16362306a36Sopenharmony_ci apbt_writel(&dw_ced->timer, ctrl, APBTMR_N_CONTROL); 16462306a36Sopenharmony_ci return 0; 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic int apbt_set_periodic(struct clock_event_device *evt) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci struct dw_apb_clock_event_device *dw_ced = ced_to_dw_apb_ced(evt); 17062306a36Sopenharmony_ci unsigned long period = DIV_ROUND_UP(dw_ced->timer.freq, HZ); 17162306a36Sopenharmony_ci u32 ctrl; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci pr_debug("%s CPU %d state=periodic\n", __func__, 17462306a36Sopenharmony_ci cpumask_first(evt->cpumask)); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci ctrl = apbt_readl(&dw_ced->timer, APBTMR_N_CONTROL); 17762306a36Sopenharmony_ci ctrl |= APBTMR_CONTROL_MODE_PERIODIC; 17862306a36Sopenharmony_ci apbt_writel(&dw_ced->timer, ctrl, APBTMR_N_CONTROL); 17962306a36Sopenharmony_ci /* 18062306a36Sopenharmony_ci * DW APB p. 46, have to disable timer before load counter, 18162306a36Sopenharmony_ci * may cause sync problem. 18262306a36Sopenharmony_ci */ 18362306a36Sopenharmony_ci ctrl &= ~APBTMR_CONTROL_ENABLE; 18462306a36Sopenharmony_ci apbt_writel(&dw_ced->timer, ctrl, APBTMR_N_CONTROL); 18562306a36Sopenharmony_ci udelay(1); 18662306a36Sopenharmony_ci pr_debug("Setting clock period %lu for HZ %d\n", period, HZ); 18762306a36Sopenharmony_ci apbt_writel(&dw_ced->timer, period, APBTMR_N_LOAD_COUNT); 18862306a36Sopenharmony_ci ctrl |= APBTMR_CONTROL_ENABLE; 18962306a36Sopenharmony_ci apbt_writel(&dw_ced->timer, ctrl, APBTMR_N_CONTROL); 19062306a36Sopenharmony_ci return 0; 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_cistatic int apbt_resume(struct clock_event_device *evt) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci struct dw_apb_clock_event_device *dw_ced = ced_to_dw_apb_ced(evt); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci pr_debug("%s CPU %d state=resume\n", __func__, 19862306a36Sopenharmony_ci cpumask_first(evt->cpumask)); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci apbt_enable_int(&dw_ced->timer); 20162306a36Sopenharmony_ci return 0; 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic int apbt_next_event(unsigned long delta, 20562306a36Sopenharmony_ci struct clock_event_device *evt) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci u32 ctrl; 20862306a36Sopenharmony_ci struct dw_apb_clock_event_device *dw_ced = ced_to_dw_apb_ced(evt); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci /* Disable timer */ 21162306a36Sopenharmony_ci ctrl = apbt_readl_relaxed(&dw_ced->timer, APBTMR_N_CONTROL); 21262306a36Sopenharmony_ci ctrl &= ~APBTMR_CONTROL_ENABLE; 21362306a36Sopenharmony_ci apbt_writel_relaxed(&dw_ced->timer, ctrl, APBTMR_N_CONTROL); 21462306a36Sopenharmony_ci /* write new count */ 21562306a36Sopenharmony_ci apbt_writel_relaxed(&dw_ced->timer, delta, APBTMR_N_LOAD_COUNT); 21662306a36Sopenharmony_ci ctrl |= APBTMR_CONTROL_ENABLE; 21762306a36Sopenharmony_ci apbt_writel_relaxed(&dw_ced->timer, ctrl, APBTMR_N_CONTROL); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci return 0; 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci/** 22362306a36Sopenharmony_ci * dw_apb_clockevent_init() - use an APB timer as a clock_event_device 22462306a36Sopenharmony_ci * 22562306a36Sopenharmony_ci * @cpu: The CPU the events will be targeted at or -1 if CPU affiliation 22662306a36Sopenharmony_ci * isn't required. 22762306a36Sopenharmony_ci * @name: The name used for the timer and the IRQ for it. 22862306a36Sopenharmony_ci * @rating: The rating to give the timer. 22962306a36Sopenharmony_ci * @base: I/O base for the timer registers. 23062306a36Sopenharmony_ci * @irq: The interrupt number to use for the timer. 23162306a36Sopenharmony_ci * @freq: The frequency that the timer counts at. 23262306a36Sopenharmony_ci * 23362306a36Sopenharmony_ci * This creates a clock_event_device for using with the generic clock layer 23462306a36Sopenharmony_ci * but does not start and register it. This should be done with 23562306a36Sopenharmony_ci * dw_apb_clockevent_register() as the next step. If this is the first time 23662306a36Sopenharmony_ci * it has been called for a timer then the IRQ will be requested, if not it 23762306a36Sopenharmony_ci * just be enabled to allow CPU hotplug to avoid repeatedly requesting and 23862306a36Sopenharmony_ci * releasing the IRQ. 23962306a36Sopenharmony_ci */ 24062306a36Sopenharmony_cistruct dw_apb_clock_event_device * 24162306a36Sopenharmony_cidw_apb_clockevent_init(int cpu, const char *name, unsigned rating, 24262306a36Sopenharmony_ci void __iomem *base, int irq, unsigned long freq) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci struct dw_apb_clock_event_device *dw_ced = 24562306a36Sopenharmony_ci kzalloc(sizeof(*dw_ced), GFP_KERNEL); 24662306a36Sopenharmony_ci int err; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci if (!dw_ced) 24962306a36Sopenharmony_ci return NULL; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci dw_ced->timer.base = base; 25262306a36Sopenharmony_ci dw_ced->timer.irq = irq; 25362306a36Sopenharmony_ci dw_ced->timer.freq = freq; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci clockevents_calc_mult_shift(&dw_ced->ced, freq, APBT_MIN_PERIOD); 25662306a36Sopenharmony_ci dw_ced->ced.max_delta_ns = clockevent_delta2ns(0x7fffffff, 25762306a36Sopenharmony_ci &dw_ced->ced); 25862306a36Sopenharmony_ci dw_ced->ced.max_delta_ticks = 0x7fffffff; 25962306a36Sopenharmony_ci dw_ced->ced.min_delta_ns = clockevent_delta2ns(5000, &dw_ced->ced); 26062306a36Sopenharmony_ci dw_ced->ced.min_delta_ticks = 5000; 26162306a36Sopenharmony_ci dw_ced->ced.cpumask = cpu < 0 ? cpu_possible_mask : cpumask_of(cpu); 26262306a36Sopenharmony_ci dw_ced->ced.features = CLOCK_EVT_FEAT_PERIODIC | 26362306a36Sopenharmony_ci CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_DYNIRQ; 26462306a36Sopenharmony_ci dw_ced->ced.set_state_shutdown = apbt_shutdown; 26562306a36Sopenharmony_ci dw_ced->ced.set_state_periodic = apbt_set_periodic; 26662306a36Sopenharmony_ci dw_ced->ced.set_state_oneshot = apbt_set_oneshot; 26762306a36Sopenharmony_ci dw_ced->ced.set_state_oneshot_stopped = apbt_shutdown; 26862306a36Sopenharmony_ci dw_ced->ced.tick_resume = apbt_resume; 26962306a36Sopenharmony_ci dw_ced->ced.set_next_event = apbt_next_event; 27062306a36Sopenharmony_ci dw_ced->ced.irq = dw_ced->timer.irq; 27162306a36Sopenharmony_ci dw_ced->ced.rating = rating; 27262306a36Sopenharmony_ci dw_ced->ced.name = name; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci dw_ced->eoi = apbt_eoi; 27562306a36Sopenharmony_ci err = request_irq(irq, dw_apb_clockevent_irq, 27662306a36Sopenharmony_ci IRQF_TIMER | IRQF_IRQPOLL | IRQF_NOBALANCING, 27762306a36Sopenharmony_ci dw_ced->ced.name, &dw_ced->ced); 27862306a36Sopenharmony_ci if (err) { 27962306a36Sopenharmony_ci pr_err("failed to request timer irq\n"); 28062306a36Sopenharmony_ci kfree(dw_ced); 28162306a36Sopenharmony_ci dw_ced = NULL; 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci return dw_ced; 28562306a36Sopenharmony_ci} 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci/** 28862306a36Sopenharmony_ci * dw_apb_clockevent_resume() - resume a clock that has been paused. 28962306a36Sopenharmony_ci * 29062306a36Sopenharmony_ci * @dw_ced: The APB clock to resume. 29162306a36Sopenharmony_ci */ 29262306a36Sopenharmony_civoid dw_apb_clockevent_resume(struct dw_apb_clock_event_device *dw_ced) 29362306a36Sopenharmony_ci{ 29462306a36Sopenharmony_ci enable_irq(dw_ced->timer.irq); 29562306a36Sopenharmony_ci} 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci/** 29862306a36Sopenharmony_ci * dw_apb_clockevent_stop() - stop the clock_event_device and release the IRQ. 29962306a36Sopenharmony_ci * 30062306a36Sopenharmony_ci * @dw_ced: The APB clock to stop generating the events. 30162306a36Sopenharmony_ci */ 30262306a36Sopenharmony_civoid dw_apb_clockevent_stop(struct dw_apb_clock_event_device *dw_ced) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci free_irq(dw_ced->timer.irq, &dw_ced->ced); 30562306a36Sopenharmony_ci} 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci/** 30862306a36Sopenharmony_ci * dw_apb_clockevent_register() - register the clock with the generic layer 30962306a36Sopenharmony_ci * 31062306a36Sopenharmony_ci * @dw_ced: The APB clock to register as a clock_event_device. 31162306a36Sopenharmony_ci */ 31262306a36Sopenharmony_civoid dw_apb_clockevent_register(struct dw_apb_clock_event_device *dw_ced) 31362306a36Sopenharmony_ci{ 31462306a36Sopenharmony_ci apbt_writel(&dw_ced->timer, 0, APBTMR_N_CONTROL); 31562306a36Sopenharmony_ci clockevents_register_device(&dw_ced->ced); 31662306a36Sopenharmony_ci apbt_enable_int(&dw_ced->timer); 31762306a36Sopenharmony_ci} 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci/** 32062306a36Sopenharmony_ci * dw_apb_clocksource_start() - start the clocksource counting. 32162306a36Sopenharmony_ci * 32262306a36Sopenharmony_ci * @dw_cs: The clocksource to start. 32362306a36Sopenharmony_ci * 32462306a36Sopenharmony_ci * This is used to start the clocksource before registration and can be used 32562306a36Sopenharmony_ci * to enable calibration of timers. 32662306a36Sopenharmony_ci */ 32762306a36Sopenharmony_civoid dw_apb_clocksource_start(struct dw_apb_clocksource *dw_cs) 32862306a36Sopenharmony_ci{ 32962306a36Sopenharmony_ci /* 33062306a36Sopenharmony_ci * start count down from 0xffff_ffff. this is done by toggling the 33162306a36Sopenharmony_ci * enable bit then load initial load count to ~0. 33262306a36Sopenharmony_ci */ 33362306a36Sopenharmony_ci u32 ctrl = apbt_readl(&dw_cs->timer, APBTMR_N_CONTROL); 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci ctrl &= ~APBTMR_CONTROL_ENABLE; 33662306a36Sopenharmony_ci apbt_writel(&dw_cs->timer, ctrl, APBTMR_N_CONTROL); 33762306a36Sopenharmony_ci apbt_writel(&dw_cs->timer, ~0, APBTMR_N_LOAD_COUNT); 33862306a36Sopenharmony_ci /* enable, mask interrupt */ 33962306a36Sopenharmony_ci ctrl &= ~APBTMR_CONTROL_MODE_PERIODIC; 34062306a36Sopenharmony_ci ctrl |= (APBTMR_CONTROL_ENABLE | APBTMR_CONTROL_INT); 34162306a36Sopenharmony_ci apbt_writel(&dw_cs->timer, ctrl, APBTMR_N_CONTROL); 34262306a36Sopenharmony_ci /* read it once to get cached counter value initialized */ 34362306a36Sopenharmony_ci dw_apb_clocksource_read(dw_cs); 34462306a36Sopenharmony_ci} 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_cistatic u64 __apbt_read_clocksource(struct clocksource *cs) 34762306a36Sopenharmony_ci{ 34862306a36Sopenharmony_ci u32 current_count; 34962306a36Sopenharmony_ci struct dw_apb_clocksource *dw_cs = 35062306a36Sopenharmony_ci clocksource_to_dw_apb_clocksource(cs); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci current_count = apbt_readl_relaxed(&dw_cs->timer, 35362306a36Sopenharmony_ci APBTMR_N_CURRENT_VALUE); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci return (u64)~current_count; 35662306a36Sopenharmony_ci} 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_cistatic void apbt_restart_clocksource(struct clocksource *cs) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci struct dw_apb_clocksource *dw_cs = 36162306a36Sopenharmony_ci clocksource_to_dw_apb_clocksource(cs); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci dw_apb_clocksource_start(dw_cs); 36462306a36Sopenharmony_ci} 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci/** 36762306a36Sopenharmony_ci * dw_apb_clocksource_init() - use an APB timer as a clocksource. 36862306a36Sopenharmony_ci * 36962306a36Sopenharmony_ci * @rating: The rating to give the clocksource. 37062306a36Sopenharmony_ci * @name: The name for the clocksource. 37162306a36Sopenharmony_ci * @base: The I/O base for the timer registers. 37262306a36Sopenharmony_ci * @freq: The frequency that the timer counts at. 37362306a36Sopenharmony_ci * 37462306a36Sopenharmony_ci * This creates a clocksource using an APB timer but does not yet register it 37562306a36Sopenharmony_ci * with the clocksource system. This should be done with 37662306a36Sopenharmony_ci * dw_apb_clocksource_register() as the next step. 37762306a36Sopenharmony_ci */ 37862306a36Sopenharmony_cistruct dw_apb_clocksource * 37962306a36Sopenharmony_cidw_apb_clocksource_init(unsigned rating, const char *name, void __iomem *base, 38062306a36Sopenharmony_ci unsigned long freq) 38162306a36Sopenharmony_ci{ 38262306a36Sopenharmony_ci struct dw_apb_clocksource *dw_cs = kzalloc(sizeof(*dw_cs), GFP_KERNEL); 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci if (!dw_cs) 38562306a36Sopenharmony_ci return NULL; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci dw_cs->timer.base = base; 38862306a36Sopenharmony_ci dw_cs->timer.freq = freq; 38962306a36Sopenharmony_ci dw_cs->cs.name = name; 39062306a36Sopenharmony_ci dw_cs->cs.rating = rating; 39162306a36Sopenharmony_ci dw_cs->cs.read = __apbt_read_clocksource; 39262306a36Sopenharmony_ci dw_cs->cs.mask = CLOCKSOURCE_MASK(32); 39362306a36Sopenharmony_ci dw_cs->cs.flags = CLOCK_SOURCE_IS_CONTINUOUS; 39462306a36Sopenharmony_ci dw_cs->cs.resume = apbt_restart_clocksource; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci return dw_cs; 39762306a36Sopenharmony_ci} 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci/** 40062306a36Sopenharmony_ci * dw_apb_clocksource_register() - register the APB clocksource. 40162306a36Sopenharmony_ci * 40262306a36Sopenharmony_ci * @dw_cs: The clocksource to register. 40362306a36Sopenharmony_ci */ 40462306a36Sopenharmony_civoid dw_apb_clocksource_register(struct dw_apb_clocksource *dw_cs) 40562306a36Sopenharmony_ci{ 40662306a36Sopenharmony_ci clocksource_register_hz(&dw_cs->cs, dw_cs->timer.freq); 40762306a36Sopenharmony_ci} 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci/** 41062306a36Sopenharmony_ci * dw_apb_clocksource_read() - read the current value of a clocksource. 41162306a36Sopenharmony_ci * 41262306a36Sopenharmony_ci * @dw_cs: The clocksource to read. 41362306a36Sopenharmony_ci */ 41462306a36Sopenharmony_ciu64 dw_apb_clocksource_read(struct dw_apb_clocksource *dw_cs) 41562306a36Sopenharmony_ci{ 41662306a36Sopenharmony_ci return (u64)~apbt_readl(&dw_cs->timer, APBTMR_N_CURRENT_VALUE); 41762306a36Sopenharmony_ci} 418