xref: /kernel/linux/linux-6.6/arch/nios2/kernel/time.c (revision 62306a36)
162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Copyright (C) 2013-2014 Altera Corporation
362306a36Sopenharmony_ci * Copyright (C) 2010 Tobias Klauser <tklauser@distanz.ch>
462306a36Sopenharmony_ci * Copyright (C) 2004 Microtronix Datacom Ltd.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public
762306a36Sopenharmony_ci * License. See the file "COPYING" in the main directory of this archive
862306a36Sopenharmony_ci * for more details.
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/export.h>
1262306a36Sopenharmony_ci#include <linux/interrupt.h>
1362306a36Sopenharmony_ci#include <linux/clockchips.h>
1462306a36Sopenharmony_ci#include <linux/clocksource.h>
1562306a36Sopenharmony_ci#include <linux/delay.h>
1662306a36Sopenharmony_ci#include <linux/of.h>
1762306a36Sopenharmony_ci#include <linux/of_address.h>
1862306a36Sopenharmony_ci#include <linux/of_irq.h>
1962306a36Sopenharmony_ci#include <linux/io.h>
2062306a36Sopenharmony_ci#include <linux/slab.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#define ALTR_TIMER_COMPATIBLE		"altr,timer-1.0"
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#define ALTERA_TIMER_STATUS_REG	0
2562306a36Sopenharmony_ci#define ALTERA_TIMER_CONTROL_REG	4
2662306a36Sopenharmony_ci#define ALTERA_TIMER_PERIODL_REG	8
2762306a36Sopenharmony_ci#define ALTERA_TIMER_PERIODH_REG	12
2862306a36Sopenharmony_ci#define ALTERA_TIMER_SNAPL_REG		16
2962306a36Sopenharmony_ci#define ALTERA_TIMER_SNAPH_REG		20
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#define ALTERA_TIMER_CONTROL_ITO_MSK	(0x1)
3262306a36Sopenharmony_ci#define ALTERA_TIMER_CONTROL_CONT_MSK	(0x2)
3362306a36Sopenharmony_ci#define ALTERA_TIMER_CONTROL_START_MSK	(0x4)
3462306a36Sopenharmony_ci#define ALTERA_TIMER_CONTROL_STOP_MSK	(0x8)
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistruct nios2_timer {
3762306a36Sopenharmony_ci	void __iomem *base;
3862306a36Sopenharmony_ci	unsigned long freq;
3962306a36Sopenharmony_ci};
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistruct nios2_clockevent_dev {
4262306a36Sopenharmony_ci	struct nios2_timer timer;
4362306a36Sopenharmony_ci	struct clock_event_device ced;
4462306a36Sopenharmony_ci};
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistruct nios2_clocksource {
4762306a36Sopenharmony_ci	struct nios2_timer timer;
4862306a36Sopenharmony_ci	struct clocksource cs;
4962306a36Sopenharmony_ci};
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic inline struct nios2_clockevent_dev *
5262306a36Sopenharmony_ci	to_nios2_clkevent(struct clock_event_device *evt)
5362306a36Sopenharmony_ci{
5462306a36Sopenharmony_ci	return container_of(evt, struct nios2_clockevent_dev, ced);
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic inline struct nios2_clocksource *
5862306a36Sopenharmony_ci	to_nios2_clksource(struct clocksource *cs)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	return container_of(cs, struct nios2_clocksource, cs);
6162306a36Sopenharmony_ci}
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistatic u16 timer_readw(struct nios2_timer *timer, u32 offs)
6462306a36Sopenharmony_ci{
6562306a36Sopenharmony_ci	return readw(timer->base + offs);
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistatic void timer_writew(struct nios2_timer *timer, u16 val, u32 offs)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	writew(val, timer->base + offs);
7162306a36Sopenharmony_ci}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistatic inline unsigned long read_timersnapshot(struct nios2_timer *timer)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	unsigned long count;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	timer_writew(timer, 0, ALTERA_TIMER_SNAPL_REG);
7862306a36Sopenharmony_ci	count = timer_readw(timer, ALTERA_TIMER_SNAPH_REG) << 16 |
7962306a36Sopenharmony_ci		timer_readw(timer, ALTERA_TIMER_SNAPL_REG);
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	return count;
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistatic u64 nios2_timer_read(struct clocksource *cs)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	struct nios2_clocksource *nios2_cs = to_nios2_clksource(cs);
8762306a36Sopenharmony_ci	unsigned long flags;
8862306a36Sopenharmony_ci	u32 count;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	local_irq_save(flags);
9162306a36Sopenharmony_ci	count = read_timersnapshot(&nios2_cs->timer);
9262306a36Sopenharmony_ci	local_irq_restore(flags);
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	/* Counter is counting down */
9562306a36Sopenharmony_ci	return ~count;
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic struct nios2_clocksource nios2_cs = {
9962306a36Sopenharmony_ci	.cs = {
10062306a36Sopenharmony_ci		.name	= "nios2-clksrc",
10162306a36Sopenharmony_ci		.rating	= 250,
10262306a36Sopenharmony_ci		.read	= nios2_timer_read,
10362306a36Sopenharmony_ci		.mask	= CLOCKSOURCE_MASK(32),
10462306a36Sopenharmony_ci		.flags	= CLOCK_SOURCE_IS_CONTINUOUS,
10562306a36Sopenharmony_ci	},
10662306a36Sopenharmony_ci};
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cicycles_t get_cycles(void)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	/* Only read timer if it has been initialized */
11162306a36Sopenharmony_ci	if (nios2_cs.timer.base)
11262306a36Sopenharmony_ci		return nios2_timer_read(&nios2_cs.cs);
11362306a36Sopenharmony_ci	return 0;
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ciEXPORT_SYMBOL(get_cycles);
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistatic void nios2_timer_start(struct nios2_timer *timer)
11862306a36Sopenharmony_ci{
11962306a36Sopenharmony_ci	u16 ctrl;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	ctrl = timer_readw(timer, ALTERA_TIMER_CONTROL_REG);
12262306a36Sopenharmony_ci	ctrl |= ALTERA_TIMER_CONTROL_START_MSK;
12362306a36Sopenharmony_ci	timer_writew(timer, ctrl, ALTERA_TIMER_CONTROL_REG);
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistatic void nios2_timer_stop(struct nios2_timer *timer)
12762306a36Sopenharmony_ci{
12862306a36Sopenharmony_ci	u16 ctrl;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	ctrl = timer_readw(timer, ALTERA_TIMER_CONTROL_REG);
13162306a36Sopenharmony_ci	ctrl |= ALTERA_TIMER_CONTROL_STOP_MSK;
13262306a36Sopenharmony_ci	timer_writew(timer, ctrl, ALTERA_TIMER_CONTROL_REG);
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_cistatic void nios2_timer_config(struct nios2_timer *timer, unsigned long period,
13662306a36Sopenharmony_ci			       bool periodic)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	u16 ctrl;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	/* The timer's actual period is one cycle greater than the value
14162306a36Sopenharmony_ci	 * stored in the period register. */
14262306a36Sopenharmony_ci	 period--;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	ctrl = timer_readw(timer, ALTERA_TIMER_CONTROL_REG);
14562306a36Sopenharmony_ci	/* stop counter */
14662306a36Sopenharmony_ci	timer_writew(timer, ctrl | ALTERA_TIMER_CONTROL_STOP_MSK,
14762306a36Sopenharmony_ci		ALTERA_TIMER_CONTROL_REG);
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	/* write new count */
15062306a36Sopenharmony_ci	timer_writew(timer, period, ALTERA_TIMER_PERIODL_REG);
15162306a36Sopenharmony_ci	timer_writew(timer, period >> 16, ALTERA_TIMER_PERIODH_REG);
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	ctrl |= ALTERA_TIMER_CONTROL_START_MSK | ALTERA_TIMER_CONTROL_ITO_MSK;
15462306a36Sopenharmony_ci	if (periodic)
15562306a36Sopenharmony_ci		ctrl |= ALTERA_TIMER_CONTROL_CONT_MSK;
15662306a36Sopenharmony_ci	else
15762306a36Sopenharmony_ci		ctrl &= ~ALTERA_TIMER_CONTROL_CONT_MSK;
15862306a36Sopenharmony_ci	timer_writew(timer, ctrl, ALTERA_TIMER_CONTROL_REG);
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_cistatic int nios2_timer_set_next_event(unsigned long delta,
16262306a36Sopenharmony_ci	struct clock_event_device *evt)
16362306a36Sopenharmony_ci{
16462306a36Sopenharmony_ci	struct nios2_clockevent_dev *nios2_ced = to_nios2_clkevent(evt);
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	nios2_timer_config(&nios2_ced->timer, delta, false);
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	return 0;
16962306a36Sopenharmony_ci}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_cistatic int nios2_timer_shutdown(struct clock_event_device *evt)
17262306a36Sopenharmony_ci{
17362306a36Sopenharmony_ci	struct nios2_clockevent_dev *nios2_ced = to_nios2_clkevent(evt);
17462306a36Sopenharmony_ci	struct nios2_timer *timer = &nios2_ced->timer;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	nios2_timer_stop(timer);
17762306a36Sopenharmony_ci	return 0;
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_cistatic int nios2_timer_set_periodic(struct clock_event_device *evt)
18162306a36Sopenharmony_ci{
18262306a36Sopenharmony_ci	unsigned long period;
18362306a36Sopenharmony_ci	struct nios2_clockevent_dev *nios2_ced = to_nios2_clkevent(evt);
18462306a36Sopenharmony_ci	struct nios2_timer *timer = &nios2_ced->timer;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	period = DIV_ROUND_UP(timer->freq, HZ);
18762306a36Sopenharmony_ci	nios2_timer_config(timer, period, true);
18862306a36Sopenharmony_ci	return 0;
18962306a36Sopenharmony_ci}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_cistatic int nios2_timer_resume(struct clock_event_device *evt)
19262306a36Sopenharmony_ci{
19362306a36Sopenharmony_ci	struct nios2_clockevent_dev *nios2_ced = to_nios2_clkevent(evt);
19462306a36Sopenharmony_ci	struct nios2_timer *timer = &nios2_ced->timer;
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	nios2_timer_start(timer);
19762306a36Sopenharmony_ci	return 0;
19862306a36Sopenharmony_ci}
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ciirqreturn_t timer_interrupt(int irq, void *dev_id)
20162306a36Sopenharmony_ci{
20262306a36Sopenharmony_ci	struct clock_event_device *evt = (struct clock_event_device *) dev_id;
20362306a36Sopenharmony_ci	struct nios2_clockevent_dev *nios2_ced = to_nios2_clkevent(evt);
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	/* Clear the interrupt condition */
20662306a36Sopenharmony_ci	timer_writew(&nios2_ced->timer, 0, ALTERA_TIMER_STATUS_REG);
20762306a36Sopenharmony_ci	evt->event_handler(evt);
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	return IRQ_HANDLED;
21062306a36Sopenharmony_ci}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_cistatic int __init nios2_timer_get_base_and_freq(struct device_node *np,
21362306a36Sopenharmony_ci				void __iomem **base, u32 *freq)
21462306a36Sopenharmony_ci{
21562306a36Sopenharmony_ci	*base = of_iomap(np, 0);
21662306a36Sopenharmony_ci	if (!*base) {
21762306a36Sopenharmony_ci		pr_crit("Unable to map reg for %pOFn\n", np);
21862306a36Sopenharmony_ci		return -ENXIO;
21962306a36Sopenharmony_ci	}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	if (of_property_read_u32(np, "clock-frequency", freq)) {
22262306a36Sopenharmony_ci		pr_crit("Unable to get %pOFn clock frequency\n", np);
22362306a36Sopenharmony_ci		return -EINVAL;
22462306a36Sopenharmony_ci	}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	return 0;
22762306a36Sopenharmony_ci}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_cistatic struct nios2_clockevent_dev nios2_ce = {
23062306a36Sopenharmony_ci	.ced = {
23162306a36Sopenharmony_ci		.name = "nios2-clkevent",
23262306a36Sopenharmony_ci		.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
23362306a36Sopenharmony_ci		.rating = 250,
23462306a36Sopenharmony_ci		.shift = 32,
23562306a36Sopenharmony_ci		.set_next_event = nios2_timer_set_next_event,
23662306a36Sopenharmony_ci		.set_state_shutdown = nios2_timer_shutdown,
23762306a36Sopenharmony_ci		.set_state_periodic = nios2_timer_set_periodic,
23862306a36Sopenharmony_ci		.set_state_oneshot = nios2_timer_shutdown,
23962306a36Sopenharmony_ci		.tick_resume = nios2_timer_resume,
24062306a36Sopenharmony_ci	},
24162306a36Sopenharmony_ci};
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_cistatic __init int nios2_clockevent_init(struct device_node *timer)
24462306a36Sopenharmony_ci{
24562306a36Sopenharmony_ci	void __iomem *iobase;
24662306a36Sopenharmony_ci	u32 freq;
24762306a36Sopenharmony_ci	int irq, ret;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	ret = nios2_timer_get_base_and_freq(timer, &iobase, &freq);
25062306a36Sopenharmony_ci	if (ret)
25162306a36Sopenharmony_ci		return ret;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	irq = irq_of_parse_and_map(timer, 0);
25462306a36Sopenharmony_ci	if (!irq) {
25562306a36Sopenharmony_ci		pr_crit("Unable to parse timer irq\n");
25662306a36Sopenharmony_ci		return -EINVAL;
25762306a36Sopenharmony_ci	}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	nios2_ce.timer.base = iobase;
26062306a36Sopenharmony_ci	nios2_ce.timer.freq = freq;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	nios2_ce.ced.cpumask = cpumask_of(0);
26362306a36Sopenharmony_ci	nios2_ce.ced.irq = irq;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	nios2_timer_stop(&nios2_ce.timer);
26662306a36Sopenharmony_ci	/* clear pending interrupt */
26762306a36Sopenharmony_ci	timer_writew(&nios2_ce.timer, 0, ALTERA_TIMER_STATUS_REG);
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	ret = request_irq(irq, timer_interrupt, IRQF_TIMER, timer->name,
27062306a36Sopenharmony_ci			  &nios2_ce.ced);
27162306a36Sopenharmony_ci	if (ret) {
27262306a36Sopenharmony_ci		pr_crit("Unable to setup timer irq\n");
27362306a36Sopenharmony_ci		return ret;
27462306a36Sopenharmony_ci	}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	clockevents_config_and_register(&nios2_ce.ced, freq, 1, ULONG_MAX);
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	return 0;
27962306a36Sopenharmony_ci}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_cistatic __init int nios2_clocksource_init(struct device_node *timer)
28262306a36Sopenharmony_ci{
28362306a36Sopenharmony_ci	unsigned int ctrl;
28462306a36Sopenharmony_ci	void __iomem *iobase;
28562306a36Sopenharmony_ci	u32 freq;
28662306a36Sopenharmony_ci	int ret;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	ret = nios2_timer_get_base_and_freq(timer, &iobase, &freq);
28962306a36Sopenharmony_ci	if (ret)
29062306a36Sopenharmony_ci		return ret;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	nios2_cs.timer.base = iobase;
29362306a36Sopenharmony_ci	nios2_cs.timer.freq = freq;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	ret = clocksource_register_hz(&nios2_cs.cs, freq);
29662306a36Sopenharmony_ci	if (ret)
29762306a36Sopenharmony_ci		return ret;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	timer_writew(&nios2_cs.timer, USHRT_MAX, ALTERA_TIMER_PERIODL_REG);
30062306a36Sopenharmony_ci	timer_writew(&nios2_cs.timer, USHRT_MAX, ALTERA_TIMER_PERIODH_REG);
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	/* interrupt disable + continuous + start */
30362306a36Sopenharmony_ci	ctrl = ALTERA_TIMER_CONTROL_CONT_MSK | ALTERA_TIMER_CONTROL_START_MSK;
30462306a36Sopenharmony_ci	timer_writew(&nios2_cs.timer, ctrl, ALTERA_TIMER_CONTROL_REG);
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	/* Calibrate the delay loop directly */
30762306a36Sopenharmony_ci	lpj_fine = freq / HZ;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	return 0;
31062306a36Sopenharmony_ci}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci/*
31362306a36Sopenharmony_ci * The first timer instance will use as a clockevent. If there are two or
31462306a36Sopenharmony_ci * more instances, the second one gets used as clocksource and all
31562306a36Sopenharmony_ci * others are unused.
31662306a36Sopenharmony_ci*/
31762306a36Sopenharmony_cistatic int __init nios2_time_init(struct device_node *timer)
31862306a36Sopenharmony_ci{
31962306a36Sopenharmony_ci	static int num_called;
32062306a36Sopenharmony_ci	int ret;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	switch (num_called) {
32362306a36Sopenharmony_ci	case 0:
32462306a36Sopenharmony_ci		ret = nios2_clockevent_init(timer);
32562306a36Sopenharmony_ci		break;
32662306a36Sopenharmony_ci	case 1:
32762306a36Sopenharmony_ci		ret = nios2_clocksource_init(timer);
32862306a36Sopenharmony_ci		break;
32962306a36Sopenharmony_ci	default:
33062306a36Sopenharmony_ci		ret = 0;
33162306a36Sopenharmony_ci		break;
33262306a36Sopenharmony_ci	}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	num_called++;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	return ret;
33762306a36Sopenharmony_ci}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_civoid read_persistent_clock64(struct timespec64 *ts)
34062306a36Sopenharmony_ci{
34162306a36Sopenharmony_ci	ts->tv_sec = mktime64(2007, 1, 1, 0, 0, 0);
34262306a36Sopenharmony_ci	ts->tv_nsec = 0;
34362306a36Sopenharmony_ci}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_civoid __init time_init(void)
34662306a36Sopenharmony_ci{
34762306a36Sopenharmony_ci	struct device_node *np;
34862306a36Sopenharmony_ci	int count = 0;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	for_each_compatible_node(np, NULL,  ALTR_TIMER_COMPATIBLE)
35162306a36Sopenharmony_ci		count++;
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	if (count < 2)
35462306a36Sopenharmony_ci		panic("%d timer is found, it needs 2 timers in system\n", count);
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	timer_probe();
35762306a36Sopenharmony_ci}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ciTIMER_OF_DECLARE(nios2_timer, ALTR_TIMER_COMPATIBLE, nios2_time_init);
360