Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1/*
  2 * Amlogic Meson6 SoCs timer handling.
  3 *
  4 * Copyright (C) 2014 Carlo Caione <carlo@caione.org>
  5 *
  6 * Based on code from Amlogic, Inc
  7 *
  8 * This file is licensed under the terms of the GNU General Public
  9 * License version 2.  This program is licensed "as is" without any
 10 * warranty of any kind, whether express or implied.
 11 */
 12
 13#include <linux/clk.h>
 14#include <linux/clockchips.h>
 15#include <linux/interrupt.h>
 16#include <linux/irq.h>
 17#include <linux/irqreturn.h>
 18#include <linux/sched_clock.h>
 19#include <linux/of.h>
 20#include <linux/of_address.h>
 21#include <linux/of_irq.h>
 22
 23#define CED_ID			0
 24#define CSD_ID			4
 25
 26#define TIMER_ISA_MUX		0
 27#define TIMER_ISA_VAL(t)	(((t) + 1) << 2)
 28
 29#define TIMER_INPUT_BIT(t)	(2 * (t))
 30#define TIMER_ENABLE_BIT(t)	(16 + (t))
 31#define TIMER_PERIODIC_BIT(t)	(12 + (t))
 32
 33#define TIMER_CED_INPUT_MASK	(3UL << TIMER_INPUT_BIT(CED_ID))
 34#define TIMER_CSD_INPUT_MASK	(7UL << TIMER_INPUT_BIT(CSD_ID))
 35
 36#define TIMER_CED_UNIT_1US	0
 37#define TIMER_CSD_UNIT_1US	1
 38
 39static void __iomem *timer_base;
 40
 41static u64 notrace meson6_timer_sched_read(void)
 42{
 43	return (u64)readl(timer_base + TIMER_ISA_VAL(CSD_ID));
 44}
 45
 46static void meson6_clkevt_time_stop(unsigned char timer)
 47{
 48	u32 val = readl(timer_base + TIMER_ISA_MUX);
 49
 50	writel(val & ~TIMER_ENABLE_BIT(timer), timer_base + TIMER_ISA_MUX);
 51}
 52
 53static void meson6_clkevt_time_setup(unsigned char timer, unsigned long delay)
 54{
 55	writel(delay, timer_base + TIMER_ISA_VAL(timer));
 56}
 57
 58static void meson6_clkevt_time_start(unsigned char timer, bool periodic)
 59{
 60	u32 val = readl(timer_base + TIMER_ISA_MUX);
 61
 62	if (periodic)
 63		val |= TIMER_PERIODIC_BIT(timer);
 64	else
 65		val &= ~TIMER_PERIODIC_BIT(timer);
 66
 67	writel(val | TIMER_ENABLE_BIT(timer), timer_base + TIMER_ISA_MUX);
 68}
 69
 70static int meson6_shutdown(struct clock_event_device *evt)
 71{
 72	meson6_clkevt_time_stop(CED_ID);
 73	return 0;
 74}
 75
 76static int meson6_set_oneshot(struct clock_event_device *evt)
 77{
 78	meson6_clkevt_time_stop(CED_ID);
 79	meson6_clkevt_time_start(CED_ID, false);
 80	return 0;
 81}
 82
 83static int meson6_set_periodic(struct clock_event_device *evt)
 84{
 85	meson6_clkevt_time_stop(CED_ID);
 86	meson6_clkevt_time_setup(CED_ID, USEC_PER_SEC / HZ - 1);
 87	meson6_clkevt_time_start(CED_ID, true);
 88	return 0;
 89}
 90
 91static int meson6_clkevt_next_event(unsigned long evt,
 92				    struct clock_event_device *unused)
 93{
 94	meson6_clkevt_time_stop(CED_ID);
 95	meson6_clkevt_time_setup(CED_ID, evt);
 96	meson6_clkevt_time_start(CED_ID, false);
 97
 98	return 0;
 99}
100
101static struct clock_event_device meson6_clockevent = {
102	.name			= "meson6_tick",
103	.rating			= 400,
104	.features		= CLOCK_EVT_FEAT_PERIODIC |
105				  CLOCK_EVT_FEAT_ONESHOT,
106	.set_state_shutdown	= meson6_shutdown,
107	.set_state_periodic	= meson6_set_periodic,
108	.set_state_oneshot	= meson6_set_oneshot,
109	.tick_resume		= meson6_shutdown,
110	.set_next_event		= meson6_clkevt_next_event,
111};
112
113static irqreturn_t meson6_timer_interrupt(int irq, void *dev_id)
114{
115	struct clock_event_device *evt = (struct clock_event_device *)dev_id;
116
117	evt->event_handler(evt);
118
119	return IRQ_HANDLED;
120}
121
122static struct irqaction meson6_timer_irq = {
123	.name		= "meson6_timer",
124	.flags		= IRQF_TIMER | IRQF_IRQPOLL,
125	.handler	= meson6_timer_interrupt,
126	.dev_id		= &meson6_clockevent,
127};
128
129static int __init meson6_timer_init(struct device_node *node)
130{
131	u32 val;
132	int ret, irq;
133
134	timer_base = of_io_request_and_map(node, 0, "meson6-timer");
135	if (IS_ERR(timer_base)) {
136		pr_err("Can't map registers\n");
137		return -ENXIO;
138	}
139
140	irq = irq_of_parse_and_map(node, 0);
141	if (irq <= 0) {
142		pr_err("Can't parse IRQ\n");
143		return -EINVAL;
144	}
145
146	/* Set 1us for timer E */
147	val = readl(timer_base + TIMER_ISA_MUX);
148	val &= ~TIMER_CSD_INPUT_MASK;
149	val |= TIMER_CSD_UNIT_1US << TIMER_INPUT_BIT(CSD_ID);
150	writel(val, timer_base + TIMER_ISA_MUX);
151
152	sched_clock_register(meson6_timer_sched_read, 32, USEC_PER_SEC);
153	clocksource_mmio_init(timer_base + TIMER_ISA_VAL(CSD_ID), node->name,
154			      1000 * 1000, 300, 32, clocksource_mmio_readl_up);
155
156	/* Timer A base 1us */
157	val &= ~TIMER_CED_INPUT_MASK;
158	val |= TIMER_CED_UNIT_1US << TIMER_INPUT_BIT(CED_ID);
159	writel(val, timer_base + TIMER_ISA_MUX);
160
161	/* Stop the timer A */
162	meson6_clkevt_time_stop(CED_ID);
163
164	ret = setup_irq(irq, &meson6_timer_irq);
165	if (ret) {
166		pr_warn("failed to setup irq %d\n", irq);
167		return ret;
168	}
169
170	meson6_clockevent.cpumask = cpu_possible_mask;
171	meson6_clockevent.irq = irq;
172
173	clockevents_config_and_register(&meson6_clockevent, USEC_PER_SEC,
174					1, 0xfffe);
175	return 0;
176}
177TIMER_OF_DECLARE(meson6, "amlogic,meson6-timer",
178		       meson6_timer_init);