18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  H8S TPU Driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright 2015 Yoshinori Sato <ysato@users.sourcefoge.jp>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/errno.h>
108c2ecf20Sopenharmony_ci#include <linux/kernel.h>
118c2ecf20Sopenharmony_ci#include <linux/init.h>
128c2ecf20Sopenharmony_ci#include <linux/clocksource.h>
138c2ecf20Sopenharmony_ci#include <linux/clk.h>
148c2ecf20Sopenharmony_ci#include <linux/io.h>
158c2ecf20Sopenharmony_ci#include <linux/of.h>
168c2ecf20Sopenharmony_ci#include <linux/of_address.h>
178c2ecf20Sopenharmony_ci#include <linux/of_irq.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#define TCR	0x0
208c2ecf20Sopenharmony_ci#define TSR	0x5
218c2ecf20Sopenharmony_ci#define TCNT	0x6
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#define TCFV	0x10
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_cistruct tpu_priv {
268c2ecf20Sopenharmony_ci	struct clocksource cs;
278c2ecf20Sopenharmony_ci	void __iomem *mapbase1;
288c2ecf20Sopenharmony_ci	void __iomem *mapbase2;
298c2ecf20Sopenharmony_ci	raw_spinlock_t lock;
308c2ecf20Sopenharmony_ci	unsigned int cs_enabled;
318c2ecf20Sopenharmony_ci};
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_cistatic inline unsigned long read_tcnt32(struct tpu_priv *p)
348c2ecf20Sopenharmony_ci{
358c2ecf20Sopenharmony_ci	unsigned long tcnt;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	tcnt = ioread16be(p->mapbase1 + TCNT) << 16;
388c2ecf20Sopenharmony_ci	tcnt |= ioread16be(p->mapbase2 + TCNT);
398c2ecf20Sopenharmony_ci	return tcnt;
408c2ecf20Sopenharmony_ci}
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cistatic int tpu_get_counter(struct tpu_priv *p, unsigned long long *val)
438c2ecf20Sopenharmony_ci{
448c2ecf20Sopenharmony_ci	unsigned long v1, v2, v3;
458c2ecf20Sopenharmony_ci	int o1, o2;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	o1 = ioread8(p->mapbase1 + TSR) & TCFV;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	/* Make sure the timer value is stable. Stolen from acpi_pm.c */
508c2ecf20Sopenharmony_ci	do {
518c2ecf20Sopenharmony_ci		o2 = o1;
528c2ecf20Sopenharmony_ci		v1 = read_tcnt32(p);
538c2ecf20Sopenharmony_ci		v2 = read_tcnt32(p);
548c2ecf20Sopenharmony_ci		v3 = read_tcnt32(p);
558c2ecf20Sopenharmony_ci		o1 = ioread8(p->mapbase1 + TSR) & TCFV;
568c2ecf20Sopenharmony_ci	} while (unlikely((o1 != o2) || (v1 > v2 && v1 < v3)
578c2ecf20Sopenharmony_ci			  || (v2 > v3 && v2 < v1) || (v3 > v1 && v3 < v2)));
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	*val = v2;
608c2ecf20Sopenharmony_ci	return o1;
618c2ecf20Sopenharmony_ci}
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_cistatic inline struct tpu_priv *cs_to_priv(struct clocksource *cs)
648c2ecf20Sopenharmony_ci{
658c2ecf20Sopenharmony_ci	return container_of(cs, struct tpu_priv, cs);
668c2ecf20Sopenharmony_ci}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_cistatic u64 tpu_clocksource_read(struct clocksource *cs)
698c2ecf20Sopenharmony_ci{
708c2ecf20Sopenharmony_ci	struct tpu_priv *p = cs_to_priv(cs);
718c2ecf20Sopenharmony_ci	unsigned long flags;
728c2ecf20Sopenharmony_ci	unsigned long long value;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&p->lock, flags);
758c2ecf20Sopenharmony_ci	if (tpu_get_counter(p, &value))
768c2ecf20Sopenharmony_ci		value += 0x100000000;
778c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&p->lock, flags);
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	return value;
808c2ecf20Sopenharmony_ci}
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cistatic int tpu_clocksource_enable(struct clocksource *cs)
838c2ecf20Sopenharmony_ci{
848c2ecf20Sopenharmony_ci	struct tpu_priv *p = cs_to_priv(cs);
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	WARN_ON(p->cs_enabled);
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	iowrite16be(0, p->mapbase1 + TCNT);
898c2ecf20Sopenharmony_ci	iowrite16be(0, p->mapbase2 + TCNT);
908c2ecf20Sopenharmony_ci	iowrite8(0x0f, p->mapbase1 + TCR);
918c2ecf20Sopenharmony_ci	iowrite8(0x03, p->mapbase2 + TCR);
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	p->cs_enabled = true;
948c2ecf20Sopenharmony_ci	return 0;
958c2ecf20Sopenharmony_ci}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_cistatic void tpu_clocksource_disable(struct clocksource *cs)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	struct tpu_priv *p = cs_to_priv(cs);
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	WARN_ON(!p->cs_enabled);
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	iowrite8(0, p->mapbase1 + TCR);
1048c2ecf20Sopenharmony_ci	iowrite8(0, p->mapbase2 + TCR);
1058c2ecf20Sopenharmony_ci	p->cs_enabled = false;
1068c2ecf20Sopenharmony_ci}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_cistatic struct tpu_priv tpu_priv = {
1098c2ecf20Sopenharmony_ci	.cs = {
1108c2ecf20Sopenharmony_ci		.name = "H8S_TPU",
1118c2ecf20Sopenharmony_ci		.rating = 200,
1128c2ecf20Sopenharmony_ci		.read = tpu_clocksource_read,
1138c2ecf20Sopenharmony_ci		.enable = tpu_clocksource_enable,
1148c2ecf20Sopenharmony_ci		.disable = tpu_clocksource_disable,
1158c2ecf20Sopenharmony_ci		.mask = CLOCKSOURCE_MASK(sizeof(unsigned long) * 8),
1168c2ecf20Sopenharmony_ci		.flags = CLOCK_SOURCE_IS_CONTINUOUS,
1178c2ecf20Sopenharmony_ci	},
1188c2ecf20Sopenharmony_ci};
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci#define CH_L 0
1218c2ecf20Sopenharmony_ci#define CH_H 1
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_cistatic int __init h8300_tpu_init(struct device_node *node)
1248c2ecf20Sopenharmony_ci{
1258c2ecf20Sopenharmony_ci	void __iomem *base[2];
1268c2ecf20Sopenharmony_ci	struct clk *clk;
1278c2ecf20Sopenharmony_ci	int ret = -ENXIO;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	clk = of_clk_get(node, 0);
1308c2ecf20Sopenharmony_ci	if (IS_ERR(clk)) {
1318c2ecf20Sopenharmony_ci		pr_err("failed to get clock for clocksource\n");
1328c2ecf20Sopenharmony_ci		return PTR_ERR(clk);
1338c2ecf20Sopenharmony_ci	}
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	base[CH_L] = of_iomap(node, CH_L);
1368c2ecf20Sopenharmony_ci	if (!base[CH_L]) {
1378c2ecf20Sopenharmony_ci		pr_err("failed to map registers for clocksource\n");
1388c2ecf20Sopenharmony_ci		goto free_clk;
1398c2ecf20Sopenharmony_ci	}
1408c2ecf20Sopenharmony_ci	base[CH_H] = of_iomap(node, CH_H);
1418c2ecf20Sopenharmony_ci	if (!base[CH_H]) {
1428c2ecf20Sopenharmony_ci		pr_err("failed to map registers for clocksource\n");
1438c2ecf20Sopenharmony_ci		goto unmap_L;
1448c2ecf20Sopenharmony_ci	}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	tpu_priv.mapbase1 = base[CH_L];
1478c2ecf20Sopenharmony_ci	tpu_priv.mapbase2 = base[CH_H];
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	return clocksource_register_hz(&tpu_priv.cs, clk_get_rate(clk) / 64);
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ciunmap_L:
1528c2ecf20Sopenharmony_ci	iounmap(base[CH_H]);
1538c2ecf20Sopenharmony_cifree_clk:
1548c2ecf20Sopenharmony_ci	clk_put(clk);
1558c2ecf20Sopenharmony_ci	return ret;
1568c2ecf20Sopenharmony_ci}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ciTIMER_OF_DECLARE(h8300_tpu, "renesas,tpu", h8300_tpu_init);
159