h8300_tpu.c 3.1 KB
Newer Older
Y
Yoshinori Sato 已提交
1
/*
2
 *  H8S TPU Driver
Y
Yoshinori Sato 已提交
3 4 5 6 7 8 9 10 11 12 13 14
 *
 *  Copyright 2015 Yoshinori Sato <ysato@users.sourcefoge.jp>
 *
 */

#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/clocksource.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/of.h>
15 16
#include <linux/of_address.h>
#include <linux/of_irq.h>
Y
Yoshinori Sato 已提交
17

18 19 20
#define TCR	0x0
#define TSR	0x5
#define TCNT	0x6
Y
Yoshinori Sato 已提交
21 22 23

struct tpu_priv {
	struct clocksource cs;
24 25
	void __iomem *mapbase1;
	void __iomem *mapbase2;
Y
Yoshinori Sato 已提交
26 27 28 29 30 31 32 33
	raw_spinlock_t lock;
	unsigned int cs_enabled;
};

static inline unsigned long read_tcnt32(struct tpu_priv *p)
{
	unsigned long tcnt;

34 35
	tcnt = readw(p->mapbase1 + TCNT) << 16;
	tcnt |= readw(p->mapbase2 + TCNT);
Y
Yoshinori Sato 已提交
36 37 38 39 40 41 42 43
	return tcnt;
}

static int tpu_get_counter(struct tpu_priv *p, unsigned long long *val)
{
	unsigned long v1, v2, v3;
	int o1, o2;

44
	o1 = readb(p->mapbase1 + TSR) & 0x10;
Y
Yoshinori Sato 已提交
45 46 47 48 49 50 51

	/* Make sure the timer value is stable. Stolen from acpi_pm.c */
	do {
		o2 = o1;
		v1 = read_tcnt32(p);
		v2 = read_tcnt32(p);
		v3 = read_tcnt32(p);
52
		o1 = readb(p->mapbase1 + TSR) & 0x10;
Y
Yoshinori Sato 已提交
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
	} while (unlikely((o1 != o2) || (v1 > v2 && v1 < v3)
			  || (v2 > v3 && v2 < v1) || (v3 > v1 && v3 < v2)));

	*val = v2;
	return o1;
}

static inline struct tpu_priv *cs_to_priv(struct clocksource *cs)
{
	return container_of(cs, struct tpu_priv, cs);
}

static cycle_t tpu_clocksource_read(struct clocksource *cs)
{
	struct tpu_priv *p = cs_to_priv(cs);
	unsigned long flags;
	unsigned long long value;

	raw_spin_lock_irqsave(&p->lock, flags);
	if (tpu_get_counter(p, &value))
		value += 0x100000000;
	raw_spin_unlock_irqrestore(&p->lock, flags);

	return value;
}

static int tpu_clocksource_enable(struct clocksource *cs)
{
	struct tpu_priv *p = cs_to_priv(cs);

	WARN_ON(p->cs_enabled);

85 86 87 88
	writew(0, p->mapbase1 + TCNT);
	writew(0, p->mapbase2 + TCNT);
	writeb(0x0f, p->mapbase1 + TCR);
	writeb(0x03, p->mapbase2 + TCR);
Y
Yoshinori Sato 已提交
89 90 91 92 93 94 95 96 97 98 99

	p->cs_enabled = true;
	return 0;
}

static void tpu_clocksource_disable(struct clocksource *cs)
{
	struct tpu_priv *p = cs_to_priv(cs);

	WARN_ON(!p->cs_enabled);

100 101
	writeb(0, p->mapbase1 + TCR);
	writeb(0, p->mapbase2 + TCR);
Y
Yoshinori Sato 已提交
102 103 104
	p->cs_enabled = false;
}

105 106 107 108 109 110 111 112 113 114 115 116
static struct tpu_priv tpu_priv = {
	.cs = {
		.name = "H8S_TPU",
		.rating = 200,
		.read = tpu_clocksource_read,
		.enable = tpu_clocksource_enable,
		.disable = tpu_clocksource_disable,
		.mask = CLOCKSOURCE_MASK(sizeof(unsigned long) * 8),
		.flags = CLOCK_SOURCE_IS_CONTINUOUS,
	},
};

Y
Yoshinori Sato 已提交
117 118 119
#define CH_L 0
#define CH_H 1

120
static void __init h8300_tpu_init(struct device_node *node)
Y
Yoshinori Sato 已提交
121
{
122 123
	void __iomem *base[2];
	struct clk *clk;
Y
Yoshinori Sato 已提交
124

125 126 127 128
	clk = of_clk_get(node, 0);
	if (IS_ERR(clk)) {
		pr_err("failed to get clock for clocksource\n");
		return;
Y
Yoshinori Sato 已提交
129 130
	}

131 132 133 134
	base[CH_L] = of_iomap(node, CH_L);
	if (!base[CH_L]) {
		pr_err("failed to map registers for clocksource\n");
		goto free_clk;
Y
Yoshinori Sato 已提交
135
	}
136 137 138 139
	base[CH_H] = of_iomap(node, CH_H);
	if (!base[CH_H]) {
		pr_err("failed to map registers for clocksource\n");
		goto unmap_L;
Y
Yoshinori Sato 已提交
140 141
	}

142 143
	tpu_priv.mapbase1 = base[CH_L];
	tpu_priv.mapbase2 = base[CH_H];
Y
Yoshinori Sato 已提交
144

145
	clocksource_register_hz(&tpu_priv.cs, clk_get_rate(clk) / 64);
Y
Yoshinori Sato 已提交
146

147
	return;
Y
Yoshinori Sato 已提交
148

149 150 151 152
unmap_L:
	iounmap(base[CH_H]);
free_clk:
	clk_put(clk);
Y
Yoshinori Sato 已提交
153 154
}

155
CLOCKSOURCE_OF_DECLARE(h8300_tpu, "renesas,tpu", h8300_tpu_init);