162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * arch/arm/plat-spear/time.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2010 ST Microelectronics 662306a36Sopenharmony_ci * Shiraz Hashim<shiraz.linux.kernel@gmail.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/clk.h> 1062306a36Sopenharmony_ci#include <linux/clockchips.h> 1162306a36Sopenharmony_ci#include <linux/clocksource.h> 1262306a36Sopenharmony_ci#include <linux/err.h> 1362306a36Sopenharmony_ci#include <linux/init.h> 1462306a36Sopenharmony_ci#include <linux/interrupt.h> 1562306a36Sopenharmony_ci#include <linux/ioport.h> 1662306a36Sopenharmony_ci#include <linux/io.h> 1762306a36Sopenharmony_ci#include <linux/kernel.h> 1862306a36Sopenharmony_ci#include <linux/of_irq.h> 1962306a36Sopenharmony_ci#include <linux/of_address.h> 2062306a36Sopenharmony_ci#include <linux/time.h> 2162306a36Sopenharmony_ci#include <linux/irq.h> 2262306a36Sopenharmony_ci#include <asm/mach/time.h> 2362306a36Sopenharmony_ci#include "generic.h" 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci/* 2662306a36Sopenharmony_ci * We would use TIMER0 and TIMER1 as clockevent and clocksource. 2762306a36Sopenharmony_ci * Timer0 and Timer1 both belong to same gpt block in cpu subbsystem. Further 2862306a36Sopenharmony_ci * they share same functional clock. Any change in one's functional clock will 2962306a36Sopenharmony_ci * also affect other timer. 3062306a36Sopenharmony_ci */ 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#define CLKEVT 0 /* gpt0, channel0 as clockevent */ 3362306a36Sopenharmony_ci#define CLKSRC 1 /* gpt0, channel1 as clocksource */ 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/* Register offsets, x is channel number */ 3662306a36Sopenharmony_ci#define CR(x) ((x) * 0x80 + 0x80) 3762306a36Sopenharmony_ci#define IR(x) ((x) * 0x80 + 0x84) 3862306a36Sopenharmony_ci#define LOAD(x) ((x) * 0x80 + 0x88) 3962306a36Sopenharmony_ci#define COUNT(x) ((x) * 0x80 + 0x8C) 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/* Reg bit definitions */ 4262306a36Sopenharmony_ci#define CTRL_INT_ENABLE 0x0100 4362306a36Sopenharmony_ci#define CTRL_ENABLE 0x0020 4462306a36Sopenharmony_ci#define CTRL_ONE_SHOT 0x0010 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci#define CTRL_PRESCALER1 0x0 4762306a36Sopenharmony_ci#define CTRL_PRESCALER2 0x1 4862306a36Sopenharmony_ci#define CTRL_PRESCALER4 0x2 4962306a36Sopenharmony_ci#define CTRL_PRESCALER8 0x3 5062306a36Sopenharmony_ci#define CTRL_PRESCALER16 0x4 5162306a36Sopenharmony_ci#define CTRL_PRESCALER32 0x5 5262306a36Sopenharmony_ci#define CTRL_PRESCALER64 0x6 5362306a36Sopenharmony_ci#define CTRL_PRESCALER128 0x7 5462306a36Sopenharmony_ci#define CTRL_PRESCALER256 0x8 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci#define INT_STATUS 0x1 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/* 5962306a36Sopenharmony_ci * Minimum clocksource/clockevent timer range in seconds 6062306a36Sopenharmony_ci */ 6162306a36Sopenharmony_ci#define SPEAR_MIN_RANGE 4 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic __iomem void *gpt_base; 6462306a36Sopenharmony_cistatic struct clk *gpt_clk; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic int clockevent_next_event(unsigned long evt, 6762306a36Sopenharmony_ci struct clock_event_device *clk_event_dev); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic void __init spear_clocksource_init(void) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci u32 tick_rate; 7262306a36Sopenharmony_ci u16 val; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci /* program the prescaler (/256)*/ 7562306a36Sopenharmony_ci writew(CTRL_PRESCALER256, gpt_base + CR(CLKSRC)); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci /* find out actual clock driving Timer */ 7862306a36Sopenharmony_ci tick_rate = clk_get_rate(gpt_clk); 7962306a36Sopenharmony_ci tick_rate >>= CTRL_PRESCALER256; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci writew(0xFFFF, gpt_base + LOAD(CLKSRC)); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci val = readw(gpt_base + CR(CLKSRC)); 8462306a36Sopenharmony_ci val &= ~CTRL_ONE_SHOT; /* autoreload mode */ 8562306a36Sopenharmony_ci val |= CTRL_ENABLE ; 8662306a36Sopenharmony_ci writew(val, gpt_base + CR(CLKSRC)); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci /* register the clocksource */ 8962306a36Sopenharmony_ci clocksource_mmio_init(gpt_base + COUNT(CLKSRC), "tmr1", tick_rate, 9062306a36Sopenharmony_ci 200, 16, clocksource_mmio_readw_up); 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic inline void spear_timer_shutdown(struct clock_event_device *evt) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci u16 val = readw(gpt_base + CR(CLKEVT)); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci /* stop the timer */ 9862306a36Sopenharmony_ci val &= ~CTRL_ENABLE; 9962306a36Sopenharmony_ci writew(val, gpt_base + CR(CLKEVT)); 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic int spear_shutdown(struct clock_event_device *evt) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci spear_timer_shutdown(evt); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci return 0; 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistatic int spear_set_oneshot(struct clock_event_device *evt) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci u16 val; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci /* stop the timer */ 11462306a36Sopenharmony_ci spear_timer_shutdown(evt); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci val = readw(gpt_base + CR(CLKEVT)); 11762306a36Sopenharmony_ci val |= CTRL_ONE_SHOT; 11862306a36Sopenharmony_ci writew(val, gpt_base + CR(CLKEVT)); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci return 0; 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic int spear_set_periodic(struct clock_event_device *evt) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci u32 period; 12662306a36Sopenharmony_ci u16 val; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci /* stop the timer */ 12962306a36Sopenharmony_ci spear_timer_shutdown(evt); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci period = clk_get_rate(gpt_clk) / HZ; 13262306a36Sopenharmony_ci period >>= CTRL_PRESCALER16; 13362306a36Sopenharmony_ci writew(period, gpt_base + LOAD(CLKEVT)); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci val = readw(gpt_base + CR(CLKEVT)); 13662306a36Sopenharmony_ci val &= ~CTRL_ONE_SHOT; 13762306a36Sopenharmony_ci val |= CTRL_ENABLE | CTRL_INT_ENABLE; 13862306a36Sopenharmony_ci writew(val, gpt_base + CR(CLKEVT)); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci return 0; 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic struct clock_event_device clkevt = { 14462306a36Sopenharmony_ci .name = "tmr0", 14562306a36Sopenharmony_ci .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, 14662306a36Sopenharmony_ci .set_state_shutdown = spear_shutdown, 14762306a36Sopenharmony_ci .set_state_periodic = spear_set_periodic, 14862306a36Sopenharmony_ci .set_state_oneshot = spear_set_oneshot, 14962306a36Sopenharmony_ci .tick_resume = spear_shutdown, 15062306a36Sopenharmony_ci .set_next_event = clockevent_next_event, 15162306a36Sopenharmony_ci .shift = 0, /* to be computed */ 15262306a36Sopenharmony_ci}; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic int clockevent_next_event(unsigned long cycles, 15562306a36Sopenharmony_ci struct clock_event_device *clk_event_dev) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci u16 val = readw(gpt_base + CR(CLKEVT)); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci if (val & CTRL_ENABLE) 16062306a36Sopenharmony_ci writew(val & ~CTRL_ENABLE, gpt_base + CR(CLKEVT)); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci writew(cycles, gpt_base + LOAD(CLKEVT)); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci val |= CTRL_ENABLE | CTRL_INT_ENABLE; 16562306a36Sopenharmony_ci writew(val, gpt_base + CR(CLKEVT)); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci return 0; 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cistatic irqreturn_t spear_timer_interrupt(int irq, void *dev_id) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci struct clock_event_device *evt = &clkevt; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci writew(INT_STATUS, gpt_base + IR(CLKEVT)); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci evt->event_handler(evt); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci return IRQ_HANDLED; 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic void __init spear_clockevent_init(int irq) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci u32 tick_rate; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci /* program the prescaler */ 18662306a36Sopenharmony_ci writew(CTRL_PRESCALER16, gpt_base + CR(CLKEVT)); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci tick_rate = clk_get_rate(gpt_clk); 18962306a36Sopenharmony_ci tick_rate >>= CTRL_PRESCALER16; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci clkevt.cpumask = cpumask_of(0); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci clockevents_config_and_register(&clkevt, tick_rate, 3, 0xfff0); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci if (request_irq(irq, spear_timer_interrupt, IRQF_TIMER, "timer", NULL)) 19662306a36Sopenharmony_ci pr_err("Failed to request irq %d (timer)\n", irq); 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cistatic const struct of_device_id timer_of_match[] __initconst = { 20062306a36Sopenharmony_ci { .compatible = "st,spear-timer", }, 20162306a36Sopenharmony_ci { }, 20262306a36Sopenharmony_ci}; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_civoid __init spear_setup_of_timer(void) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci struct device_node *np; 20762306a36Sopenharmony_ci int irq, ret; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci np = of_find_matching_node(NULL, timer_of_match); 21062306a36Sopenharmony_ci if (!np) { 21162306a36Sopenharmony_ci pr_err("%s: No timer passed via DT\n", __func__); 21262306a36Sopenharmony_ci return; 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci irq = irq_of_parse_and_map(np, 0); 21662306a36Sopenharmony_ci if (!irq) { 21762306a36Sopenharmony_ci pr_err("%s: No irq passed for timer via DT\n", __func__); 21862306a36Sopenharmony_ci goto err_put_np; 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci gpt_base = of_iomap(np, 0); 22262306a36Sopenharmony_ci if (!gpt_base) { 22362306a36Sopenharmony_ci pr_err("%s: of iomap failed\n", __func__); 22462306a36Sopenharmony_ci goto err_put_np; 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci gpt_clk = clk_get_sys("gpt0", NULL); 22862306a36Sopenharmony_ci if (IS_ERR(gpt_clk)) { 22962306a36Sopenharmony_ci pr_err("%s:couldn't get clk for gpt\n", __func__); 23062306a36Sopenharmony_ci goto err_iomap; 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci ret = clk_prepare_enable(gpt_clk); 23462306a36Sopenharmony_ci if (ret < 0) { 23562306a36Sopenharmony_ci pr_err("%s:couldn't prepare-enable gpt clock\n", __func__); 23662306a36Sopenharmony_ci goto err_prepare_enable_clk; 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci of_node_put(np); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci spear_clockevent_init(irq); 24262306a36Sopenharmony_ci spear_clocksource_init(); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci return; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cierr_prepare_enable_clk: 24762306a36Sopenharmony_ci clk_put(gpt_clk); 24862306a36Sopenharmony_cierr_iomap: 24962306a36Sopenharmony_ci iounmap(gpt_base); 25062306a36Sopenharmony_cierr_put_np: 25162306a36Sopenharmony_ci of_node_put(np); 25262306a36Sopenharmony_ci} 253