Path: blob/master/drivers/clocksource/timer-digicolor.c
26278 views
// SPDX-License-Identifier: GPL-2.01/*2* Conexant Digicolor timer driver3*4* Author: Baruch Siach <[email protected]>5*6* Copyright (C) 2014 Paradox Innovation Ltd.7*8* Based on:9* Allwinner SoCs hstimer driver10*11* Copyright (C) 2013 Maxime Ripard12*13* Maxime Ripard <[email protected]>14*/1516/*17* Conexant Digicolor SoCs have 8 configurable timers, named from "Timer A" to18* "Timer H". Timer A is the only one with watchdog support, so it is dedicated19* to the watchdog driver. This driver uses Timer B for sched_clock(), and20* Timer C for clockevents.21*/2223#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt2425#include <linux/clk.h>26#include <linux/clockchips.h>27#include <linux/interrupt.h>28#include <linux/irq.h>29#include <linux/irqreturn.h>30#include <linux/sched/clock.h>31#include <linux/sched_clock.h>32#include <linux/of.h>33#include <linux/of_address.h>34#include <linux/of_irq.h>3536enum {37TIMER_A,38TIMER_B,39TIMER_C,40TIMER_D,41TIMER_E,42TIMER_F,43TIMER_G,44TIMER_H,45};4647#define CONTROL(t) ((t)*8)48#define COUNT(t) ((t)*8 + 4)4950#define CONTROL_DISABLE 051#define CONTROL_ENABLE BIT(0)52#define CONTROL_MODE(m) ((m) << 4)53#define CONTROL_MODE_ONESHOT CONTROL_MODE(1)54#define CONTROL_MODE_PERIODIC CONTROL_MODE(2)5556struct digicolor_timer {57struct clock_event_device ce;58void __iomem *base;59u32 ticks_per_jiffy;60int timer_id; /* one of TIMER_* */61};6263static struct digicolor_timer *dc_timer(struct clock_event_device *ce)64{65return container_of(ce, struct digicolor_timer, ce);66}6768static inline void dc_timer_disable(struct clock_event_device *ce)69{70struct digicolor_timer *dt = dc_timer(ce);71writeb(CONTROL_DISABLE, dt->base + CONTROL(dt->timer_id));72}7374static inline void dc_timer_enable(struct clock_event_device *ce, u32 mode)75{76struct digicolor_timer *dt = dc_timer(ce);77writeb(CONTROL_ENABLE | mode, dt->base + CONTROL(dt->timer_id));78}7980static inline void dc_timer_set_count(struct clock_event_device *ce,81unsigned long count)82{83struct digicolor_timer *dt = dc_timer(ce);84writel(count, dt->base + COUNT(dt->timer_id));85}8687static int digicolor_clkevt_shutdown(struct clock_event_device *ce)88{89dc_timer_disable(ce);90return 0;91}9293static int digicolor_clkevt_set_oneshot(struct clock_event_device *ce)94{95dc_timer_disable(ce);96dc_timer_enable(ce, CONTROL_MODE_ONESHOT);97return 0;98}99100static int digicolor_clkevt_set_periodic(struct clock_event_device *ce)101{102struct digicolor_timer *dt = dc_timer(ce);103104dc_timer_disable(ce);105dc_timer_set_count(ce, dt->ticks_per_jiffy);106dc_timer_enable(ce, CONTROL_MODE_PERIODIC);107return 0;108}109110static int digicolor_clkevt_next_event(unsigned long evt,111struct clock_event_device *ce)112{113dc_timer_disable(ce);114dc_timer_set_count(ce, evt);115dc_timer_enable(ce, CONTROL_MODE_ONESHOT);116117return 0;118}119120static struct digicolor_timer dc_timer_dev = {121.ce = {122.name = "digicolor_tick",123.rating = 340,124.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,125.set_state_shutdown = digicolor_clkevt_shutdown,126.set_state_periodic = digicolor_clkevt_set_periodic,127.set_state_oneshot = digicolor_clkevt_set_oneshot,128.tick_resume = digicolor_clkevt_shutdown,129.set_next_event = digicolor_clkevt_next_event,130},131.timer_id = TIMER_C,132};133134static irqreturn_t digicolor_timer_interrupt(int irq, void *dev_id)135{136struct clock_event_device *evt = dev_id;137138evt->event_handler(evt);139140return IRQ_HANDLED;141}142143static u64 notrace digicolor_timer_sched_read(void)144{145return ~readl(dc_timer_dev.base + COUNT(TIMER_B));146}147148static int __init digicolor_timer_init(struct device_node *node)149{150unsigned long rate;151struct clk *clk;152int ret, irq;153154/*155* timer registers are shared with the watchdog timer;156* don't map exclusively157*/158dc_timer_dev.base = of_iomap(node, 0);159if (!dc_timer_dev.base) {160pr_err("Can't map registers\n");161return -ENXIO;162}163164irq = irq_of_parse_and_map(node, dc_timer_dev.timer_id);165if (irq <= 0) {166pr_err("Can't parse IRQ\n");167return -EINVAL;168}169170clk = of_clk_get(node, 0);171if (IS_ERR(clk)) {172pr_err("Can't get timer clock\n");173return PTR_ERR(clk);174}175clk_prepare_enable(clk);176rate = clk_get_rate(clk);177dc_timer_dev.ticks_per_jiffy = DIV_ROUND_UP(rate, HZ);178179writeb(CONTROL_DISABLE, dc_timer_dev.base + CONTROL(TIMER_B));180writel(UINT_MAX, dc_timer_dev.base + COUNT(TIMER_B));181writeb(CONTROL_ENABLE, dc_timer_dev.base + CONTROL(TIMER_B));182183sched_clock_register(digicolor_timer_sched_read, 32, rate);184clocksource_mmio_init(dc_timer_dev.base + COUNT(TIMER_B), node->name,185rate, 340, 32, clocksource_mmio_readl_down);186187ret = request_irq(irq, digicolor_timer_interrupt,188IRQF_TIMER | IRQF_IRQPOLL, "digicolor_timerC",189&dc_timer_dev.ce);190if (ret) {191pr_warn("request of timer irq %d failed (%d)\n", irq, ret);192return ret;193}194195dc_timer_dev.ce.cpumask = cpu_possible_mask;196dc_timer_dev.ce.irq = irq;197198clockevents_config_and_register(&dc_timer_dev.ce, rate, 0, 0xffffffff);199200return 0;201}202TIMER_OF_DECLARE(conexant_digicolor, "cnxt,cx92755-timer",203digicolor_timer_init);204205206