blob: ed0b493445774e9d72d04b7bb426b8b49649f243 [file] [log] [blame]
Yoshinori Sato618b9022015-01-28 02:52:42 +09001/*
Yoshinori Sato4633f4c2015-11-07 01:31:44 +09002 * H8S TPU Driver
Yoshinori Sato618b9022015-01-28 02:52:42 +09003 *
4 * Copyright 2015 Yoshinori Sato <ysato@users.sourcefoge.jp>
5 *
6 */
7
8#include <linux/errno.h>
9#include <linux/sched.h>
10#include <linux/kernel.h>
11#include <linux/interrupt.h>
12#include <linux/init.h>
13#include <linux/platform_device.h>
14#include <linux/slab.h>
15#include <linux/clocksource.h>
16#include <linux/module.h>
17#include <linux/clk.h>
18#include <linux/io.h>
19#include <linux/of.h>
Yoshinori Sato4633f4c2015-11-07 01:31:44 +090020#include <linux/of_address.h>
21#include <linux/of_irq.h>
Yoshinori Sato618b9022015-01-28 02:52:42 +090022
23#define TCR 0
24#define TMDR 1
25#define TIOR 2
26#define TER 4
27#define TSR 5
28#define TCNT 6
29#define TGRA 8
30#define TGRB 10
31#define TGRC 12
32#define TGRD 14
33
34struct tpu_priv {
Yoshinori Sato618b9022015-01-28 02:52:42 +090035 struct clocksource cs;
Yoshinori Sato618b9022015-01-28 02:52:42 +090036 unsigned long mapbase1;
37 unsigned long mapbase2;
38 raw_spinlock_t lock;
39 unsigned int cs_enabled;
40};
41
42static inline unsigned long read_tcnt32(struct tpu_priv *p)
43{
44 unsigned long tcnt;
45
46 tcnt = ctrl_inw(p->mapbase1 + TCNT) << 16;
47 tcnt |= ctrl_inw(p->mapbase2 + TCNT);
48 return tcnt;
49}
50
51static int tpu_get_counter(struct tpu_priv *p, unsigned long long *val)
52{
53 unsigned long v1, v2, v3;
54 int o1, o2;
55
56 o1 = ctrl_inb(p->mapbase1 + TSR) & 0x10;
57
58 /* Make sure the timer value is stable. Stolen from acpi_pm.c */
59 do {
60 o2 = o1;
61 v1 = read_tcnt32(p);
62 v2 = read_tcnt32(p);
63 v3 = read_tcnt32(p);
64 o1 = ctrl_inb(p->mapbase1 + TSR) & 0x10;
65 } while (unlikely((o1 != o2) || (v1 > v2 && v1 < v3)
66 || (v2 > v3 && v2 < v1) || (v3 > v1 && v3 < v2)));
67
68 *val = v2;
69 return o1;
70}
71
72static inline struct tpu_priv *cs_to_priv(struct clocksource *cs)
73{
74 return container_of(cs, struct tpu_priv, cs);
75}
76
77static cycle_t tpu_clocksource_read(struct clocksource *cs)
78{
79 struct tpu_priv *p = cs_to_priv(cs);
80 unsigned long flags;
81 unsigned long long value;
82
83 raw_spin_lock_irqsave(&p->lock, flags);
84 if (tpu_get_counter(p, &value))
85 value += 0x100000000;
86 raw_spin_unlock_irqrestore(&p->lock, flags);
87
88 return value;
89}
90
91static int tpu_clocksource_enable(struct clocksource *cs)
92{
93 struct tpu_priv *p = cs_to_priv(cs);
94
95 WARN_ON(p->cs_enabled);
96
97 ctrl_outw(0, p->mapbase1 + TCNT);
98 ctrl_outw(0, p->mapbase2 + TCNT);
99 ctrl_outb(0x0f, p->mapbase1 + TCR);
100 ctrl_outb(0x03, p->mapbase2 + TCR);
101
102 p->cs_enabled = true;
103 return 0;
104}
105
106static void tpu_clocksource_disable(struct clocksource *cs)
107{
108 struct tpu_priv *p = cs_to_priv(cs);
109
110 WARN_ON(!p->cs_enabled);
111
112 ctrl_outb(0, p->mapbase1 + TCR);
113 ctrl_outb(0, p->mapbase2 + TCR);
114 p->cs_enabled = false;
115}
116
Yoshinori Sato4633f4c2015-11-07 01:31:44 +0900117static struct tpu_priv tpu_priv = {
118 .cs = {
119 .name = "H8S_TPU",
120 .rating = 200,
121 .read = tpu_clocksource_read,
122 .enable = tpu_clocksource_enable,
123 .disable = tpu_clocksource_disable,
124 .mask = CLOCKSOURCE_MASK(sizeof(unsigned long) * 8),
125 .flags = CLOCK_SOURCE_IS_CONTINUOUS,
126 },
127};
128
Yoshinori Sato618b9022015-01-28 02:52:42 +0900129#define CH_L 0
130#define CH_H 1
131
Yoshinori Sato4633f4c2015-11-07 01:31:44 +0900132static void __init h8300_tpu_init(struct device_node *node)
Yoshinori Sato618b9022015-01-28 02:52:42 +0900133{
Yoshinori Sato4633f4c2015-11-07 01:31:44 +0900134 void __iomem *base[2];
135 struct clk *clk;
Yoshinori Sato618b9022015-01-28 02:52:42 +0900136
Yoshinori Sato4633f4c2015-11-07 01:31:44 +0900137 clk = of_clk_get(node, 0);
138 if (IS_ERR(clk)) {
139 pr_err("failed to get clock for clocksource\n");
140 return;
Yoshinori Sato618b9022015-01-28 02:52:42 +0900141 }
142
Yoshinori Sato4633f4c2015-11-07 01:31:44 +0900143 base[CH_L] = of_iomap(node, CH_L);
144 if (!base[CH_L]) {
145 pr_err("failed to map registers for clocksource\n");
146 goto free_clk;
147 }
148 base[CH_H] = of_iomap(node, CH_H);
149 if (!base[CH_H]) {
150 pr_err("failed to map registers for clocksource\n");
151 goto unmap_L;
Yoshinori Sato618b9022015-01-28 02:52:42 +0900152 }
153
Yoshinori Sato4633f4c2015-11-07 01:31:44 +0900154 tpu_priv.mapbase1 = (unsigned long)base[CH_L];
155 tpu_priv.mapbase2 = (unsigned long)base[CH_H];
Yoshinori Sato618b9022015-01-28 02:52:42 +0900156
Yoshinori Sato4633f4c2015-11-07 01:31:44 +0900157 clocksource_register_hz(&tpu_priv.cs, clk_get_rate(clk) / 64);
Yoshinori Sato618b9022015-01-28 02:52:42 +0900158
Yoshinori Sato4633f4c2015-11-07 01:31:44 +0900159 return;
160
161unmap_L:
162 iounmap(base[CH_H]);
163free_clk:
164 clk_put(clk);
Yoshinori Sato618b9022015-01-28 02:52:42 +0900165}
166
Yoshinori Sato4633f4c2015-11-07 01:31:44 +0900167CLOCKSOURCE_OF_DECLARE(h8300_tpu, "renesas,tpu", h8300_tpu_init);