brintos

brintos / linux-shallow public Read only

0
0
Text · 4.0 KiB · 8d386ad Raw
156 lines · c
1// SPDX-License-Identifier: GPL-2.02// Copyright (C) 2018 Hangzhou C-SKY Microsystems co.,ltd.3 4#include <linux/init.h>5#include <linux/interrupt.h>6#include <linux/sched_clock.h>7 8#include "timer-of.h"9 10#define CLKSRC_OFFSET	0x4011 12#define TIMER_STATUS	0x0013#define TIMER_VALUE	0x0414#define TIMER_CONTRL	0x1015#define TIMER_CONFIG	0x2016#define TIMER_DIV	0x2417#define TIMER_INI	0x2818 19#define GX6605S_STATUS_CLR	BIT(0)20#define GX6605S_CONTRL_RST	BIT(0)21#define GX6605S_CONTRL_START	BIT(1)22#define GX6605S_CONFIG_EN	BIT(0)23#define GX6605S_CONFIG_IRQ_EN	BIT(1)24 25static irqreturn_t gx6605s_timer_interrupt(int irq, void *dev)26{27	struct clock_event_device *ce = dev;28	void __iomem *base = timer_of_base(to_timer_of(ce));29 30	writel_relaxed(GX6605S_STATUS_CLR, base + TIMER_STATUS);31	writel_relaxed(0, base + TIMER_INI);32 33	ce->event_handler(ce);34 35	return IRQ_HANDLED;36}37 38static int gx6605s_timer_set_oneshot(struct clock_event_device *ce)39{40	void __iomem *base = timer_of_base(to_timer_of(ce));41 42	/* reset and stop counter */43	writel_relaxed(GX6605S_CONTRL_RST, base + TIMER_CONTRL);44 45	/* enable with irq and start */46	writel_relaxed(GX6605S_CONFIG_EN | GX6605S_CONFIG_IRQ_EN,47		       base + TIMER_CONFIG);48 49	return 0;50}51 52static int gx6605s_timer_set_next_event(unsigned long delta,53					struct clock_event_device *ce)54{55	void __iomem *base = timer_of_base(to_timer_of(ce));56 57	/* use reset to pause timer */58	writel_relaxed(GX6605S_CONTRL_RST, base + TIMER_CONTRL);59 60	/* config next timeout value */61	writel_relaxed(ULONG_MAX - delta, base + TIMER_INI);62	writel_relaxed(GX6605S_CONTRL_START, base + TIMER_CONTRL);63 64	return 0;65}66 67static int gx6605s_timer_shutdown(struct clock_event_device *ce)68{69	void __iomem *base = timer_of_base(to_timer_of(ce));70 71	writel_relaxed(0, base + TIMER_CONTRL);72	writel_relaxed(0, base + TIMER_CONFIG);73 74	return 0;75}76 77static struct timer_of to = {78	.flags = TIMER_OF_IRQ | TIMER_OF_BASE | TIMER_OF_CLOCK,79	.clkevt = {80		.rating			= 300,81		.features		= CLOCK_EVT_FEAT_DYNIRQ |82					  CLOCK_EVT_FEAT_ONESHOT,83		.set_state_shutdown	= gx6605s_timer_shutdown,84		.set_state_oneshot	= gx6605s_timer_set_oneshot,85		.set_next_event		= gx6605s_timer_set_next_event,86		.cpumask		= cpu_possible_mask,87	},88	.of_irq = {89		.handler		= gx6605s_timer_interrupt,90		.flags			= IRQF_TIMER | IRQF_IRQPOLL,91	},92};93 94static u64 notrace gx6605s_sched_clock_read(void)95{96	void __iomem *base;97 98	base = timer_of_base(&to) + CLKSRC_OFFSET;99 100	return (u64)readl_relaxed(base + TIMER_VALUE);101}102 103static void gx6605s_clkevt_init(void __iomem *base)104{105	writel_relaxed(0, base + TIMER_DIV);106	writel_relaxed(0, base + TIMER_CONFIG);107 108	clockevents_config_and_register(&to.clkevt, timer_of_rate(&to), 2,109					ULONG_MAX);110}111 112static int gx6605s_clksrc_init(void __iomem *base)113{114	writel_relaxed(0, base + TIMER_DIV);115	writel_relaxed(0, base + TIMER_INI);116 117	writel_relaxed(GX6605S_CONTRL_RST, base + TIMER_CONTRL);118 119	writel_relaxed(GX6605S_CONFIG_EN, base + TIMER_CONFIG);120 121	writel_relaxed(GX6605S_CONTRL_START, base + TIMER_CONTRL);122 123	sched_clock_register(gx6605s_sched_clock_read, 32, timer_of_rate(&to));124 125	return clocksource_mmio_init(base + TIMER_VALUE, "gx6605s",126			timer_of_rate(&to), 200, 32, clocksource_mmio_readl_up);127}128 129static int __init gx6605s_timer_init(struct device_node *np)130{131	int ret;132 133	/*134	 * The timer driver is for nationalchip gx6605s SOC and there are two135	 * same timer in gx6605s. We use one for clkevt and another for clksrc.136	 *137	 * The timer is mmio map to access, so we need give mmio address in dts.138	 *139	 * It provides a 32bit countup timer and interrupt will be caused by140	 * count-overflow.141	 * So we need set-next-event by ULONG_MAX - delta in TIMER_INI reg.142	 *143	 * The counter at 0x0  offset is clock event.144	 * The counter at 0x40 offset is clock source.145	 * They are the same in hardware, just different used by driver.146	 */147	ret = timer_of_init(np, &to);148	if (ret)149		return ret;150 151	gx6605s_clkevt_init(timer_of_base(&to));152 153	return gx6605s_clksrc_init(timer_of_base(&to) + CLKSRC_OFFSET);154}155TIMER_OF_DECLARE(csky_gx6605s_timer, "csky,gx6605s-timer", gx6605s_timer_init);156