18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  H8/300 16bit Timer driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright 2015 Yoshinori Sato <ysato@users.sourcefoge.jp>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
98c2ecf20Sopenharmony_ci#include <linux/init.h>
108c2ecf20Sopenharmony_ci#include <linux/clocksource.h>
118c2ecf20Sopenharmony_ci#include <linux/clk.h>
128c2ecf20Sopenharmony_ci#include <linux/io.h>
138c2ecf20Sopenharmony_ci#include <linux/of.h>
148c2ecf20Sopenharmony_ci#include <linux/of_address.h>
158c2ecf20Sopenharmony_ci#include <linux/of_irq.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#define TSTR	0
188c2ecf20Sopenharmony_ci#define TISRC	6
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#define TCR	0
218c2ecf20Sopenharmony_ci#define TCNT	2
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#define bset(b, a) iowrite8(ioread8(a) | (1 << (b)), (a))
248c2ecf20Sopenharmony_ci#define bclr(b, a) iowrite8(ioread8(a) & ~(1 << (b)), (a))
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistruct timer16_priv {
278c2ecf20Sopenharmony_ci	struct clocksource cs;
288c2ecf20Sopenharmony_ci	unsigned long total_cycles;
298c2ecf20Sopenharmony_ci	void __iomem *mapbase;
308c2ecf20Sopenharmony_ci	void __iomem *mapcommon;
318c2ecf20Sopenharmony_ci	unsigned short cs_enabled;
328c2ecf20Sopenharmony_ci	unsigned char enb;
338c2ecf20Sopenharmony_ci	unsigned char ovf;
348c2ecf20Sopenharmony_ci	unsigned char ovie;
358c2ecf20Sopenharmony_ci};
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistatic unsigned long timer16_get_counter(struct timer16_priv *p)
388c2ecf20Sopenharmony_ci{
398c2ecf20Sopenharmony_ci	unsigned short v1, v2, v3;
408c2ecf20Sopenharmony_ci	unsigned char  o1, o2;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	o1 = ioread8(p->mapcommon + TISRC) & p->ovf;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	/* Make sure the timer value is stable. Stolen from acpi_pm.c */
458c2ecf20Sopenharmony_ci	do {
468c2ecf20Sopenharmony_ci		o2 = o1;
478c2ecf20Sopenharmony_ci		v1 = ioread16be(p->mapbase + TCNT);
488c2ecf20Sopenharmony_ci		v2 = ioread16be(p->mapbase + TCNT);
498c2ecf20Sopenharmony_ci		v3 = ioread16be(p->mapbase + TCNT);
508c2ecf20Sopenharmony_ci		o1 = ioread8(p->mapcommon + TISRC) & p->ovf;
518c2ecf20Sopenharmony_ci	} while (unlikely((o1 != o2) || (v1 > v2 && v1 < v3)
528c2ecf20Sopenharmony_ci			  || (v2 > v3 && v2 < v1) || (v3 > v1 && v3 < v2)));
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	if (likely(!o1))
558c2ecf20Sopenharmony_ci		return v2;
568c2ecf20Sopenharmony_ci	else
578c2ecf20Sopenharmony_ci		return v2 + 0x10000;
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cistatic irqreturn_t timer16_interrupt(int irq, void *dev_id)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	struct timer16_priv *p = (struct timer16_priv *)dev_id;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	bclr(p->ovf, p->mapcommon + TISRC);
668c2ecf20Sopenharmony_ci	p->total_cycles += 0x10000;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
698c2ecf20Sopenharmony_ci}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic inline struct timer16_priv *cs_to_priv(struct clocksource *cs)
728c2ecf20Sopenharmony_ci{
738c2ecf20Sopenharmony_ci	return container_of(cs, struct timer16_priv, cs);
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistatic u64 timer16_clocksource_read(struct clocksource *cs)
778c2ecf20Sopenharmony_ci{
788c2ecf20Sopenharmony_ci	struct timer16_priv *p = cs_to_priv(cs);
798c2ecf20Sopenharmony_ci	unsigned long raw, value;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	value = p->total_cycles;
828c2ecf20Sopenharmony_ci	raw = timer16_get_counter(p);
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	return value + raw;
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistatic int timer16_enable(struct clocksource *cs)
888c2ecf20Sopenharmony_ci{
898c2ecf20Sopenharmony_ci	struct timer16_priv *p = cs_to_priv(cs);
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	WARN_ON(p->cs_enabled);
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	p->total_cycles = 0;
948c2ecf20Sopenharmony_ci	iowrite16be(0x0000, p->mapbase + TCNT);
958c2ecf20Sopenharmony_ci	iowrite8(0x83, p->mapbase + TCR);
968c2ecf20Sopenharmony_ci	bset(p->ovie, p->mapcommon + TISRC);
978c2ecf20Sopenharmony_ci	bset(p->enb, p->mapcommon + TSTR);
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	p->cs_enabled = true;
1008c2ecf20Sopenharmony_ci	return 0;
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistatic void timer16_disable(struct clocksource *cs)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	struct timer16_priv *p = cs_to_priv(cs);
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	WARN_ON(!p->cs_enabled);
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	bclr(p->ovie, p->mapcommon + TISRC);
1108c2ecf20Sopenharmony_ci	bclr(p->enb, p->mapcommon + TSTR);
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	p->cs_enabled = false;
1138c2ecf20Sopenharmony_ci}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_cistatic struct timer16_priv timer16_priv = {
1168c2ecf20Sopenharmony_ci	.cs = {
1178c2ecf20Sopenharmony_ci		.name = "h8300_16timer",
1188c2ecf20Sopenharmony_ci		.rating = 200,
1198c2ecf20Sopenharmony_ci		.read = timer16_clocksource_read,
1208c2ecf20Sopenharmony_ci		.enable = timer16_enable,
1218c2ecf20Sopenharmony_ci		.disable = timer16_disable,
1228c2ecf20Sopenharmony_ci		.mask = CLOCKSOURCE_MASK(sizeof(unsigned long) * 8),
1238c2ecf20Sopenharmony_ci		.flags = CLOCK_SOURCE_IS_CONTINUOUS,
1248c2ecf20Sopenharmony_ci	},
1258c2ecf20Sopenharmony_ci};
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci#define REG_CH   0
1288c2ecf20Sopenharmony_ci#define REG_COMM 1
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_cistatic int __init h8300_16timer_init(struct device_node *node)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	void __iomem *base[2];
1338c2ecf20Sopenharmony_ci	int ret, irq;
1348c2ecf20Sopenharmony_ci	unsigned int ch;
1358c2ecf20Sopenharmony_ci	struct clk *clk;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	clk = of_clk_get(node, 0);
1388c2ecf20Sopenharmony_ci	if (IS_ERR(clk)) {
1398c2ecf20Sopenharmony_ci		pr_err("failed to get clock for clocksource\n");
1408c2ecf20Sopenharmony_ci		return PTR_ERR(clk);
1418c2ecf20Sopenharmony_ci	}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	ret = -ENXIO;
1448c2ecf20Sopenharmony_ci	base[REG_CH] = of_iomap(node, 0);
1458c2ecf20Sopenharmony_ci	if (!base[REG_CH]) {
1468c2ecf20Sopenharmony_ci		pr_err("failed to map registers for clocksource\n");
1478c2ecf20Sopenharmony_ci		goto free_clk;
1488c2ecf20Sopenharmony_ci	}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	base[REG_COMM] = of_iomap(node, 1);
1518c2ecf20Sopenharmony_ci	if (!base[REG_COMM]) {
1528c2ecf20Sopenharmony_ci		pr_err("failed to map registers for clocksource\n");
1538c2ecf20Sopenharmony_ci		goto unmap_ch;
1548c2ecf20Sopenharmony_ci	}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	ret = -EINVAL;
1578c2ecf20Sopenharmony_ci	irq = irq_of_parse_and_map(node, 0);
1588c2ecf20Sopenharmony_ci	if (!irq) {
1598c2ecf20Sopenharmony_ci		pr_err("failed to get irq for clockevent\n");
1608c2ecf20Sopenharmony_ci		goto unmap_comm;
1618c2ecf20Sopenharmony_ci	}
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	of_property_read_u32(node, "renesas,channel", &ch);
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	timer16_priv.mapbase = base[REG_CH];
1668c2ecf20Sopenharmony_ci	timer16_priv.mapcommon = base[REG_COMM];
1678c2ecf20Sopenharmony_ci	timer16_priv.enb = ch;
1688c2ecf20Sopenharmony_ci	timer16_priv.ovf = ch;
1698c2ecf20Sopenharmony_ci	timer16_priv.ovie = 4 + ch;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	ret = request_irq(irq, timer16_interrupt,
1728c2ecf20Sopenharmony_ci			  IRQF_TIMER, timer16_priv.cs.name, &timer16_priv);
1738c2ecf20Sopenharmony_ci	if (ret < 0) {
1748c2ecf20Sopenharmony_ci		pr_err("failed to request irq %d of clocksource\n", irq);
1758c2ecf20Sopenharmony_ci		goto unmap_comm;
1768c2ecf20Sopenharmony_ci	}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	clocksource_register_hz(&timer16_priv.cs,
1798c2ecf20Sopenharmony_ci				clk_get_rate(clk) / 8);
1808c2ecf20Sopenharmony_ci	return 0;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ciunmap_comm:
1838c2ecf20Sopenharmony_ci	iounmap(base[REG_COMM]);
1848c2ecf20Sopenharmony_ciunmap_ch:
1858c2ecf20Sopenharmony_ci	iounmap(base[REG_CH]);
1868c2ecf20Sopenharmony_cifree_clk:
1878c2ecf20Sopenharmony_ci	clk_put(clk);
1888c2ecf20Sopenharmony_ci	return ret;
1898c2ecf20Sopenharmony_ci}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ciTIMER_OF_DECLARE(h8300_16bit, "renesas,16bit-timer",
1928c2ecf20Sopenharmony_ci			   h8300_16timer_init);
193