Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1// SPDX-License-Identifier: GPL-2.0
  2// 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	0x40
 11
 12#define TIMER_STATUS	0x00
 13#define TIMER_VALUE	0x04
 14#define TIMER_CONTRL	0x10
 15#define TIMER_CONFIG	0x20
 16#define TIMER_DIV	0x24
 17#define TIMER_INI	0x28
 18
 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 two
135	 * 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 by
140	 * 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);