blob: 49f1c805fc9598edf5d7021b435419b40e24b64f [file] [log] [blame]
Kuninori Morimoto0b9294f2018-08-22 02:26:20 +00001// SPDX-License-Identifier: GPL-2.0
Magnus Damm9570ef22009-05-01 06:51:00 +00002/*
3 * SuperH Timer Support - TMU
4 *
5 * Copyright (C) 2009 Magnus Damm
Magnus Damm9570ef22009-05-01 06:51:00 +00006 */
7
Magnus Damm9570ef22009-05-01 06:51:00 +00008#include <linux/clk.h>
Magnus Damm9570ef22009-05-01 06:51:00 +00009#include <linux/clockchips.h>
Laurent Pinchart13931f82014-02-12 16:56:44 +010010#include <linux/clocksource.h>
11#include <linux/delay.h>
12#include <linux/err.h>
13#include <linux/init.h>
14#include <linux/interrupt.h>
15#include <linux/io.h>
16#include <linux/ioport.h>
17#include <linux/irq.h>
Paul Gortmaker7deeab52011-07-03 13:36:22 -040018#include <linux/module.h>
Laurent Pinchart3e29b552014-04-11 16:23:40 +020019#include <linux/of.h>
Laurent Pinchart13931f82014-02-12 16:56:44 +010020#include <linux/platform_device.h>
Rafael J. Wysocki2ee619f2012-03-13 22:40:00 +010021#include <linux/pm_domain.h>
Rafael J. Wysockieaa49a82012-08-06 01:41:20 +020022#include <linux/pm_runtime.h>
Laurent Pinchart13931f82014-02-12 16:56:44 +010023#include <linux/sh_timer.h>
24#include <linux/slab.h>
25#include <linux/spinlock.h>
Magnus Damm9570ef22009-05-01 06:51:00 +000026
Laurent Pinchart8c7f21e2014-01-28 12:36:48 +010027enum sh_tmu_model {
Laurent Pinchart8c7f21e2014-01-28 12:36:48 +010028 SH_TMU,
29 SH_TMU_SH3,
30};
31
Laurent Pinchart0a72aa32014-01-27 22:04:17 +010032struct sh_tmu_device;
Laurent Pinchartde2d12c2014-01-27 15:29:19 +010033
34struct sh_tmu_channel {
Laurent Pinchart0a72aa32014-01-27 22:04:17 +010035 struct sh_tmu_device *tmu;
Laurent Pinchartfe68eb82014-01-27 22:04:17 +010036 unsigned int index;
Laurent Pinchartde2d12c2014-01-27 15:29:19 +010037
Laurent Pinchartde693462014-01-27 22:04:17 +010038 void __iomem *base;
Laurent Pinchart1c56cf62014-02-17 11:27:49 +010039 int irq;
Laurent Pinchartde2d12c2014-01-27 15:29:19 +010040
Magnus Damm9570ef22009-05-01 06:51:00 +000041 unsigned long periodic;
42 struct clock_event_device ced;
43 struct clocksource cs;
Rafael J. Wysockieaa49a82012-08-06 01:41:20 +020044 bool cs_enabled;
Rafael J. Wysocki61a53bf2012-08-06 01:48:17 +020045 unsigned int enable_count;
Magnus Damm9570ef22009-05-01 06:51:00 +000046};
47
Laurent Pinchart0a72aa32014-01-27 22:04:17 +010048struct sh_tmu_device {
Laurent Pinchartde2d12c2014-01-27 15:29:19 +010049 struct platform_device *pdev;
50
51 void __iomem *mapbase;
52 struct clk *clk;
Nicolai Stangec3c0a202017-02-06 22:12:00 +010053 unsigned long rate;
Laurent Pinchartde2d12c2014-01-27 15:29:19 +010054
Laurent Pinchart8c7f21e2014-01-28 12:36:48 +010055 enum sh_tmu_model model;
56
Laurent Pinchart2b027f12014-02-17 16:49:05 +010057 raw_spinlock_t lock; /* Protect the shared start/stop register */
58
Laurent Pincharta5de49f2014-01-27 22:04:17 +010059 struct sh_tmu_channel *channels;
60 unsigned int num_channels;
Laurent Pinchart8c7f21e2014-01-28 12:36:48 +010061
62 bool has_clockevent;
63 bool has_clocksource;
Laurent Pinchartde2d12c2014-01-27 15:29:19 +010064};
65
Magnus Damm9570ef22009-05-01 06:51:00 +000066#define TSTR -1 /* shared register */
67#define TCOR 0 /* channel register */
68#define TCNT 1 /* channel register */
69#define TCR 2 /* channel register */
70
Laurent Pinchart5cfe2d12014-01-29 00:33:08 +010071#define TCR_UNF (1 << 8)
72#define TCR_UNIE (1 << 5)
73#define TCR_TPSC_CLK4 (0 << 0)
74#define TCR_TPSC_CLK16 (1 << 0)
75#define TCR_TPSC_CLK64 (2 << 0)
76#define TCR_TPSC_CLK256 (3 << 0)
77#define TCR_TPSC_CLK1024 (4 << 0)
78#define TCR_TPSC_MASK (7 << 0)
79
Laurent Pinchartde2d12c2014-01-27 15:29:19 +010080static inline unsigned long sh_tmu_read(struct sh_tmu_channel *ch, int reg_nr)
Magnus Damm9570ef22009-05-01 06:51:00 +000081{
Magnus Damm9570ef22009-05-01 06:51:00 +000082 unsigned long offs;
83
Laurent Pinchart8c7f21e2014-01-28 12:36:48 +010084 if (reg_nr == TSTR) {
85 switch (ch->tmu->model) {
Laurent Pinchart8c7f21e2014-01-28 12:36:48 +010086 case SH_TMU_SH3:
87 return ioread8(ch->tmu->mapbase + 2);
88 case SH_TMU:
89 return ioread8(ch->tmu->mapbase + 4);
90 }
91 }
Magnus Damm9570ef22009-05-01 06:51:00 +000092
93 offs = reg_nr << 2;
94
95 if (reg_nr == TCR)
Laurent Pinchartde693462014-01-27 22:04:17 +010096 return ioread16(ch->base + offs);
Magnus Damm9570ef22009-05-01 06:51:00 +000097 else
Laurent Pinchartde693462014-01-27 22:04:17 +010098 return ioread32(ch->base + offs);
Magnus Damm9570ef22009-05-01 06:51:00 +000099}
100
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100101static inline void sh_tmu_write(struct sh_tmu_channel *ch, int reg_nr,
Magnus Damm9570ef22009-05-01 06:51:00 +0000102 unsigned long value)
103{
Magnus Damm9570ef22009-05-01 06:51:00 +0000104 unsigned long offs;
105
106 if (reg_nr == TSTR) {
Laurent Pinchart8c7f21e2014-01-28 12:36:48 +0100107 switch (ch->tmu->model) {
Laurent Pinchart8c7f21e2014-01-28 12:36:48 +0100108 case SH_TMU_SH3:
109 return iowrite8(value, ch->tmu->mapbase + 2);
110 case SH_TMU:
111 return iowrite8(value, ch->tmu->mapbase + 4);
112 }
Magnus Damm9570ef22009-05-01 06:51:00 +0000113 }
114
115 offs = reg_nr << 2;
116
117 if (reg_nr == TCR)
Laurent Pinchartde693462014-01-27 22:04:17 +0100118 iowrite16(value, ch->base + offs);
Magnus Damm9570ef22009-05-01 06:51:00 +0000119 else
Laurent Pinchartde693462014-01-27 22:04:17 +0100120 iowrite32(value, ch->base + offs);
Magnus Damm9570ef22009-05-01 06:51:00 +0000121}
122
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100123static void sh_tmu_start_stop_ch(struct sh_tmu_channel *ch, int start)
Magnus Damm9570ef22009-05-01 06:51:00 +0000124{
Magnus Damm9570ef22009-05-01 06:51:00 +0000125 unsigned long flags, value;
126
127 /* start stop register shared by multiple timer channels */
Laurent Pinchart2b027f12014-02-17 16:49:05 +0100128 raw_spin_lock_irqsave(&ch->tmu->lock, flags);
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100129 value = sh_tmu_read(ch, TSTR);
Magnus Damm9570ef22009-05-01 06:51:00 +0000130
131 if (start)
Laurent Pinchartfe68eb82014-01-27 22:04:17 +0100132 value |= 1 << ch->index;
Magnus Damm9570ef22009-05-01 06:51:00 +0000133 else
Laurent Pinchartfe68eb82014-01-27 22:04:17 +0100134 value &= ~(1 << ch->index);
Magnus Damm9570ef22009-05-01 06:51:00 +0000135
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100136 sh_tmu_write(ch, TSTR, value);
Laurent Pinchart2b027f12014-02-17 16:49:05 +0100137 raw_spin_unlock_irqrestore(&ch->tmu->lock, flags);
Magnus Damm9570ef22009-05-01 06:51:00 +0000138}
139
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100140static int __sh_tmu_enable(struct sh_tmu_channel *ch)
Magnus Damm9570ef22009-05-01 06:51:00 +0000141{
Magnus Damm9570ef22009-05-01 06:51:00 +0000142 int ret;
143
Paul Mundtd4905ce2011-05-31 15:23:20 +0900144 /* enable clock */
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100145 ret = clk_enable(ch->tmu->clk);
Magnus Damm9570ef22009-05-01 06:51:00 +0000146 if (ret) {
Laurent Pinchartfe68eb82014-01-27 22:04:17 +0100147 dev_err(&ch->tmu->pdev->dev, "ch%u: cannot enable clock\n",
148 ch->index);
Magnus Damm9570ef22009-05-01 06:51:00 +0000149 return ret;
150 }
151
152 /* make sure channel is disabled */
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100153 sh_tmu_start_stop_ch(ch, 0);
Magnus Damm9570ef22009-05-01 06:51:00 +0000154
155 /* maximum timeout */
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100156 sh_tmu_write(ch, TCOR, 0xffffffff);
157 sh_tmu_write(ch, TCNT, 0xffffffff);
Magnus Damm9570ef22009-05-01 06:51:00 +0000158
159 /* configure channel to parent clock / 4, irq off */
Laurent Pinchart5cfe2d12014-01-29 00:33:08 +0100160 sh_tmu_write(ch, TCR, TCR_TPSC_CLK4);
Magnus Damm9570ef22009-05-01 06:51:00 +0000161
162 /* enable channel */
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100163 sh_tmu_start_stop_ch(ch, 1);
Magnus Damm9570ef22009-05-01 06:51:00 +0000164
165 return 0;
166}
167
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100168static int sh_tmu_enable(struct sh_tmu_channel *ch)
Rafael J. Wysocki61a53bf2012-08-06 01:48:17 +0200169{
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100170 if (ch->enable_count++ > 0)
Rafael J. Wysocki61a53bf2012-08-06 01:48:17 +0200171 return 0;
172
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100173 pm_runtime_get_sync(&ch->tmu->pdev->dev);
174 dev_pm_syscore_device(&ch->tmu->pdev->dev, true);
Rafael J. Wysocki61a53bf2012-08-06 01:48:17 +0200175
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100176 return __sh_tmu_enable(ch);
Rafael J. Wysocki61a53bf2012-08-06 01:48:17 +0200177}
178
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100179static void __sh_tmu_disable(struct sh_tmu_channel *ch)
Magnus Damm9570ef22009-05-01 06:51:00 +0000180{
181 /* disable channel */
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100182 sh_tmu_start_stop_ch(ch, 0);
Magnus Damm9570ef22009-05-01 06:51:00 +0000183
Magnus Dammbe890a12009-06-17 05:04:04 +0000184 /* disable interrupts in TMU block */
Laurent Pinchart5cfe2d12014-01-29 00:33:08 +0100185 sh_tmu_write(ch, TCR, TCR_TPSC_CLK4);
Magnus Dammbe890a12009-06-17 05:04:04 +0000186
Paul Mundtd4905ce2011-05-31 15:23:20 +0900187 /* stop clock */
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100188 clk_disable(ch->tmu->clk);
Magnus Damm9570ef22009-05-01 06:51:00 +0000189}
190
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100191static void sh_tmu_disable(struct sh_tmu_channel *ch)
Rafael J. Wysocki61a53bf2012-08-06 01:48:17 +0200192{
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100193 if (WARN_ON(ch->enable_count == 0))
Rafael J. Wysocki61a53bf2012-08-06 01:48:17 +0200194 return;
195
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100196 if (--ch->enable_count > 0)
Rafael J. Wysocki61a53bf2012-08-06 01:48:17 +0200197 return;
198
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100199 __sh_tmu_disable(ch);
Rafael J. Wysocki61a53bf2012-08-06 01:48:17 +0200200
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100201 dev_pm_syscore_device(&ch->tmu->pdev->dev, false);
202 pm_runtime_put(&ch->tmu->pdev->dev);
Rafael J. Wysocki61a53bf2012-08-06 01:48:17 +0200203}
204
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100205static void sh_tmu_set_next(struct sh_tmu_channel *ch, unsigned long delta,
Magnus Damm9570ef22009-05-01 06:51:00 +0000206 int periodic)
207{
208 /* stop timer */
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100209 sh_tmu_start_stop_ch(ch, 0);
Magnus Damm9570ef22009-05-01 06:51:00 +0000210
211 /* acknowledge interrupt */
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100212 sh_tmu_read(ch, TCR);
Magnus Damm9570ef22009-05-01 06:51:00 +0000213
214 /* enable interrupt */
Laurent Pinchart5cfe2d12014-01-29 00:33:08 +0100215 sh_tmu_write(ch, TCR, TCR_UNIE | TCR_TPSC_CLK4);
Magnus Damm9570ef22009-05-01 06:51:00 +0000216
217 /* reload delta value in case of periodic timer */
218 if (periodic)
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100219 sh_tmu_write(ch, TCOR, delta);
Magnus Damm9570ef22009-05-01 06:51:00 +0000220 else
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100221 sh_tmu_write(ch, TCOR, 0xffffffff);
Magnus Damm9570ef22009-05-01 06:51:00 +0000222
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100223 sh_tmu_write(ch, TCNT, delta);
Magnus Damm9570ef22009-05-01 06:51:00 +0000224
225 /* start timer */
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100226 sh_tmu_start_stop_ch(ch, 1);
Magnus Damm9570ef22009-05-01 06:51:00 +0000227}
228
229static irqreturn_t sh_tmu_interrupt(int irq, void *dev_id)
230{
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100231 struct sh_tmu_channel *ch = dev_id;
Magnus Damm9570ef22009-05-01 06:51:00 +0000232
233 /* disable or acknowledge interrupt */
Viresh Kumar2bcc4da2015-06-18 16:24:36 +0530234 if (clockevent_state_oneshot(&ch->ced))
Laurent Pinchart5cfe2d12014-01-29 00:33:08 +0100235 sh_tmu_write(ch, TCR, TCR_TPSC_CLK4);
Magnus Damm9570ef22009-05-01 06:51:00 +0000236 else
Laurent Pinchart5cfe2d12014-01-29 00:33:08 +0100237 sh_tmu_write(ch, TCR, TCR_UNIE | TCR_TPSC_CLK4);
Magnus Damm9570ef22009-05-01 06:51:00 +0000238
239 /* notify clockevent layer */
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100240 ch->ced.event_handler(&ch->ced);
Magnus Damm9570ef22009-05-01 06:51:00 +0000241 return IRQ_HANDLED;
242}
243
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100244static struct sh_tmu_channel *cs_to_sh_tmu(struct clocksource *cs)
Magnus Damm9570ef22009-05-01 06:51:00 +0000245{
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100246 return container_of(cs, struct sh_tmu_channel, cs);
Magnus Damm9570ef22009-05-01 06:51:00 +0000247}
248
Thomas Gleixnera5a1d1c2016-12-21 20:32:01 +0100249static u64 sh_tmu_clocksource_read(struct clocksource *cs)
Magnus Damm9570ef22009-05-01 06:51:00 +0000250{
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100251 struct sh_tmu_channel *ch = cs_to_sh_tmu(cs);
Magnus Damm9570ef22009-05-01 06:51:00 +0000252
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100253 return sh_tmu_read(ch, TCNT) ^ 0xffffffff;
Magnus Damm9570ef22009-05-01 06:51:00 +0000254}
255
256static int sh_tmu_clocksource_enable(struct clocksource *cs)
257{
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100258 struct sh_tmu_channel *ch = cs_to_sh_tmu(cs);
Magnus Damm0aeac452011-04-25 22:38:37 +0900259 int ret;
Magnus Damm9570ef22009-05-01 06:51:00 +0000260
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100261 if (WARN_ON(ch->cs_enabled))
Rafael J. Wysocki61a53bf2012-08-06 01:48:17 +0200262 return 0;
263
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100264 ret = sh_tmu_enable(ch);
Nicolai Stangec3c0a202017-02-06 22:12:00 +0100265 if (!ret)
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100266 ch->cs_enabled = true;
Rafael J. Wysocki61a53bf2012-08-06 01:48:17 +0200267
Magnus Damm0aeac452011-04-25 22:38:37 +0900268 return ret;
Magnus Damm9570ef22009-05-01 06:51:00 +0000269}
270
271static void sh_tmu_clocksource_disable(struct clocksource *cs)
272{
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100273 struct sh_tmu_channel *ch = cs_to_sh_tmu(cs);
Rafael J. Wysockieaa49a82012-08-06 01:41:20 +0200274
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100275 if (WARN_ON(!ch->cs_enabled))
Rafael J. Wysocki61a53bf2012-08-06 01:48:17 +0200276 return;
Rafael J. Wysockieaa49a82012-08-06 01:41:20 +0200277
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100278 sh_tmu_disable(ch);
279 ch->cs_enabled = false;
Rafael J. Wysockieaa49a82012-08-06 01:41:20 +0200280}
281
282static void sh_tmu_clocksource_suspend(struct clocksource *cs)
283{
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100284 struct sh_tmu_channel *ch = cs_to_sh_tmu(cs);
Rafael J. Wysockieaa49a82012-08-06 01:41:20 +0200285
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100286 if (!ch->cs_enabled)
Rafael J. Wysocki61a53bf2012-08-06 01:48:17 +0200287 return;
Rafael J. Wysockieaa49a82012-08-06 01:41:20 +0200288
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100289 if (--ch->enable_count == 0) {
290 __sh_tmu_disable(ch);
291 pm_genpd_syscore_poweroff(&ch->tmu->pdev->dev);
Rafael J. Wysocki61a53bf2012-08-06 01:48:17 +0200292 }
Rafael J. Wysockieaa49a82012-08-06 01:41:20 +0200293}
294
295static void sh_tmu_clocksource_resume(struct clocksource *cs)
296{
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100297 struct sh_tmu_channel *ch = cs_to_sh_tmu(cs);
Rafael J. Wysockieaa49a82012-08-06 01:41:20 +0200298
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100299 if (!ch->cs_enabled)
Rafael J. Wysocki61a53bf2012-08-06 01:48:17 +0200300 return;
301
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100302 if (ch->enable_count++ == 0) {
303 pm_genpd_syscore_poweron(&ch->tmu->pdev->dev);
304 __sh_tmu_enable(ch);
Rafael J. Wysocki61a53bf2012-08-06 01:48:17 +0200305 }
Magnus Damm9570ef22009-05-01 06:51:00 +0000306}
307
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100308static int sh_tmu_register_clocksource(struct sh_tmu_channel *ch,
Laurent Pinchartf1010ed2014-02-19 17:00:31 +0100309 const char *name)
Magnus Damm9570ef22009-05-01 06:51:00 +0000310{
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100311 struct clocksource *cs = &ch->cs;
Magnus Damm9570ef22009-05-01 06:51:00 +0000312
313 cs->name = name;
Laurent Pinchartf1010ed2014-02-19 17:00:31 +0100314 cs->rating = 200;
Magnus Damm9570ef22009-05-01 06:51:00 +0000315 cs->read = sh_tmu_clocksource_read;
316 cs->enable = sh_tmu_clocksource_enable;
317 cs->disable = sh_tmu_clocksource_disable;
Rafael J. Wysockieaa49a82012-08-06 01:41:20 +0200318 cs->suspend = sh_tmu_clocksource_suspend;
319 cs->resume = sh_tmu_clocksource_resume;
Magnus Damm9570ef22009-05-01 06:51:00 +0000320 cs->mask = CLOCKSOURCE_MASK(32);
321 cs->flags = CLOCK_SOURCE_IS_CONTINUOUS;
Aurelien Jarno66f49122010-05-31 21:45:48 +0000322
Laurent Pinchartfe68eb82014-01-27 22:04:17 +0100323 dev_info(&ch->tmu->pdev->dev, "ch%u: used as clock source\n",
324 ch->index);
Magnus Damm0aeac452011-04-25 22:38:37 +0900325
Nicolai Stangec3c0a202017-02-06 22:12:00 +0100326 clocksource_register_hz(cs, ch->tmu->rate);
Magnus Damm9570ef22009-05-01 06:51:00 +0000327 return 0;
328}
329
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100330static struct sh_tmu_channel *ced_to_sh_tmu(struct clock_event_device *ced)
Magnus Damm9570ef22009-05-01 06:51:00 +0000331{
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100332 return container_of(ced, struct sh_tmu_channel, ced);
Magnus Damm9570ef22009-05-01 06:51:00 +0000333}
334
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100335static void sh_tmu_clock_event_start(struct sh_tmu_channel *ch, int periodic)
Magnus Damm9570ef22009-05-01 06:51:00 +0000336{
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100337 sh_tmu_enable(ch);
Magnus Damm9570ef22009-05-01 06:51:00 +0000338
Magnus Damm9570ef22009-05-01 06:51:00 +0000339 if (periodic) {
Nicolai Stangec3c0a202017-02-06 22:12:00 +0100340 ch->periodic = (ch->tmu->rate + HZ/2) / HZ;
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100341 sh_tmu_set_next(ch, ch->periodic, 1);
Magnus Damm9570ef22009-05-01 06:51:00 +0000342 }
343}
344
Viresh Kumar2bcc4da2015-06-18 16:24:36 +0530345static int sh_tmu_clock_event_shutdown(struct clock_event_device *ced)
Magnus Damm9570ef22009-05-01 06:51:00 +0000346{
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100347 struct sh_tmu_channel *ch = ced_to_sh_tmu(ced);
Viresh Kumar2bcc4da2015-06-18 16:24:36 +0530348
Viresh Kumar452b1322015-07-21 08:01:14 +0530349 if (clockevent_state_oneshot(ced) || clockevent_state_periodic(ced))
350 sh_tmu_disable(ch);
Viresh Kumar2bcc4da2015-06-18 16:24:36 +0530351 return 0;
352}
353
354static int sh_tmu_clock_event_set_state(struct clock_event_device *ced,
355 int periodic)
356{
357 struct sh_tmu_channel *ch = ced_to_sh_tmu(ced);
Magnus Damm9570ef22009-05-01 06:51:00 +0000358
359 /* deal with old setting first */
Viresh Kumar2bcc4da2015-06-18 16:24:36 +0530360 if (clockevent_state_oneshot(ced) || clockevent_state_periodic(ced))
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100361 sh_tmu_disable(ch);
Magnus Damm9570ef22009-05-01 06:51:00 +0000362
Viresh Kumar2bcc4da2015-06-18 16:24:36 +0530363 dev_info(&ch->tmu->pdev->dev, "ch%u: used for %s clock events\n",
364 ch->index, periodic ? "periodic" : "oneshot");
365 sh_tmu_clock_event_start(ch, periodic);
366 return 0;
367}
368
369static int sh_tmu_clock_event_set_oneshot(struct clock_event_device *ced)
370{
371 return sh_tmu_clock_event_set_state(ced, 0);
372}
373
374static int sh_tmu_clock_event_set_periodic(struct clock_event_device *ced)
375{
376 return sh_tmu_clock_event_set_state(ced, 1);
Magnus Damm9570ef22009-05-01 06:51:00 +0000377}
378
379static int sh_tmu_clock_event_next(unsigned long delta,
380 struct clock_event_device *ced)
381{
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100382 struct sh_tmu_channel *ch = ced_to_sh_tmu(ced);
Magnus Damm9570ef22009-05-01 06:51:00 +0000383
Viresh Kumar2bcc4da2015-06-18 16:24:36 +0530384 BUG_ON(!clockevent_state_oneshot(ced));
Magnus Damm9570ef22009-05-01 06:51:00 +0000385
386 /* program new delta value */
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100387 sh_tmu_set_next(ch, delta, 0);
Magnus Damm9570ef22009-05-01 06:51:00 +0000388 return 0;
389}
390
Rafael J. Wysockieaa49a82012-08-06 01:41:20 +0200391static void sh_tmu_clock_event_suspend(struct clock_event_device *ced)
392{
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100393 pm_genpd_syscore_poweroff(&ced_to_sh_tmu(ced)->tmu->pdev->dev);
Rafael J. Wysockieaa49a82012-08-06 01:41:20 +0200394}
395
396static void sh_tmu_clock_event_resume(struct clock_event_device *ced)
397{
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100398 pm_genpd_syscore_poweron(&ced_to_sh_tmu(ced)->tmu->pdev->dev);
Rafael J. Wysockieaa49a82012-08-06 01:41:20 +0200399}
400
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100401static void sh_tmu_register_clockevent(struct sh_tmu_channel *ch,
Laurent Pinchartf1010ed2014-02-19 17:00:31 +0100402 const char *name)
Magnus Damm9570ef22009-05-01 06:51:00 +0000403{
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100404 struct clock_event_device *ced = &ch->ced;
Magnus Damm9570ef22009-05-01 06:51:00 +0000405 int ret;
406
Magnus Damm9570ef22009-05-01 06:51:00 +0000407 ced->name = name;
408 ced->features = CLOCK_EVT_FEAT_PERIODIC;
409 ced->features |= CLOCK_EVT_FEAT_ONESHOT;
Laurent Pinchartf1010ed2014-02-19 17:00:31 +0100410 ced->rating = 200;
Magnus Dammf2a54732014-12-16 18:48:54 +0900411 ced->cpumask = cpu_possible_mask;
Magnus Damm9570ef22009-05-01 06:51:00 +0000412 ced->set_next_event = sh_tmu_clock_event_next;
Viresh Kumar2bcc4da2015-06-18 16:24:36 +0530413 ced->set_state_shutdown = sh_tmu_clock_event_shutdown;
414 ced->set_state_periodic = sh_tmu_clock_event_set_periodic;
415 ced->set_state_oneshot = sh_tmu_clock_event_set_oneshot;
Rafael J. Wysockieaa49a82012-08-06 01:41:20 +0200416 ced->suspend = sh_tmu_clock_event_suspend;
417 ced->resume = sh_tmu_clock_event_resume;
Magnus Damm9570ef22009-05-01 06:51:00 +0000418
Laurent Pinchartfe68eb82014-01-27 22:04:17 +0100419 dev_info(&ch->tmu->pdev->dev, "ch%u: used for clock events\n",
420 ch->index);
Paul Mundt39774072012-06-11 17:10:16 +0900421
Nicolai Stangec3c0a202017-02-06 22:12:00 +0100422 clockevents_config_and_register(ced, ch->tmu->rate, 0x300, 0xffffffff);
Paul Mundtda64c2a2010-02-25 16:37:46 +0900423
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100424 ret = request_irq(ch->irq, sh_tmu_interrupt,
Laurent Pinchart1c56cf62014-02-17 11:27:49 +0100425 IRQF_TIMER | IRQF_IRQPOLL | IRQF_NOBALANCING,
Laurent Pinchartde2d12c2014-01-27 15:29:19 +0100426 dev_name(&ch->tmu->pdev->dev), ch);
Magnus Damm9570ef22009-05-01 06:51:00 +0000427 if (ret) {
Laurent Pinchartfe68eb82014-01-27 22:04:17 +0100428 dev_err(&ch->tmu->pdev->dev, "ch%u: failed to request irq %d\n",
429 ch->index, ch->irq);
Magnus Damm9570ef22009-05-01 06:51:00 +0000430 return;
431 }
Magnus Damm9570ef22009-05-01 06:51:00 +0000432}
433
Laurent Pinchart84876d02014-02-17 16:04:16 +0100434static int sh_tmu_register(struct sh_tmu_channel *ch, const char *name,
Laurent Pinchartf1010ed2014-02-19 17:00:31 +0100435 bool clockevent, bool clocksource)
Magnus Damm9570ef22009-05-01 06:51:00 +0000436{
Laurent Pinchart8c7f21e2014-01-28 12:36:48 +0100437 if (clockevent) {
438 ch->tmu->has_clockevent = true;
Laurent Pinchartf1010ed2014-02-19 17:00:31 +0100439 sh_tmu_register_clockevent(ch, name);
Laurent Pinchart8c7f21e2014-01-28 12:36:48 +0100440 } else if (clocksource) {
441 ch->tmu->has_clocksource = true;
Laurent Pinchartf1010ed2014-02-19 17:00:31 +0100442 sh_tmu_register_clocksource(ch, name);
Laurent Pinchart8c7f21e2014-01-28 12:36:48 +0100443 }
Magnus Damm9570ef22009-05-01 06:51:00 +0000444
445 return 0;
446}
447
Laurent Pinchart8c7f21e2014-01-28 12:36:48 +0100448static int sh_tmu_channel_setup(struct sh_tmu_channel *ch, unsigned int index,
449 bool clockevent, bool clocksource,
Laurent Pincharta94ddaa2014-01-27 22:04:17 +0100450 struct sh_tmu_device *tmu)
451{
Laurent Pinchart8c7f21e2014-01-28 12:36:48 +0100452 /* Skip unused channels. */
453 if (!clockevent && !clocksource)
454 return 0;
Laurent Pincharta94ddaa2014-01-27 22:04:17 +0100455
Laurent Pincharta94ddaa2014-01-27 22:04:17 +0100456 ch->tmu = tmu;
Laurent Pinchart681b9e82014-01-28 15:52:46 +0100457 ch->index = index;
Laurent Pincharta94ddaa2014-01-27 22:04:17 +0100458
Laurent Pinchart681b9e82014-01-28 15:52:46 +0100459 if (tmu->model == SH_TMU_SH3)
460 ch->base = tmu->mapbase + 4 + ch->index * 12;
461 else
462 ch->base = tmu->mapbase + 8 + ch->index * 12;
Laurent Pinchart8c7f21e2014-01-28 12:36:48 +0100463
Laurent Pinchartc54697a2014-05-16 14:44:23 +0200464 ch->irq = platform_get_irq(tmu->pdev, index);
Laurent Pincharta94ddaa2014-01-27 22:04:17 +0100465 if (ch->irq < 0) {
Laurent Pinchartfe68eb82014-01-27 22:04:17 +0100466 dev_err(&tmu->pdev->dev, "ch%u: failed to get irq\n",
467 ch->index);
Laurent Pincharta94ddaa2014-01-27 22:04:17 +0100468 return ch->irq;
469 }
470
471 ch->cs_enabled = false;
472 ch->enable_count = 0;
473
Laurent Pinchart84876d02014-02-17 16:04:16 +0100474 return sh_tmu_register(ch, dev_name(&tmu->pdev->dev),
Laurent Pinchart8c7f21e2014-01-28 12:36:48 +0100475 clockevent, clocksource);
476}
477
478static int sh_tmu_map_memory(struct sh_tmu_device *tmu)
479{
480 struct resource *res;
481
482 res = platform_get_resource(tmu->pdev, IORESOURCE_MEM, 0);
483 if (!res) {
484 dev_err(&tmu->pdev->dev, "failed to get I/O memory\n");
485 return -ENXIO;
486 }
487
488 tmu->mapbase = ioremap_nocache(res->start, resource_size(res));
489 if (tmu->mapbase == NULL)
490 return -ENXIO;
491
Laurent Pinchart8c7f21e2014-01-28 12:36:48 +0100492 return 0;
493}
494
Laurent Pinchart3e29b552014-04-11 16:23:40 +0200495static int sh_tmu_parse_dt(struct sh_tmu_device *tmu)
496{
497 struct device_node *np = tmu->pdev->dev.of_node;
498
499 tmu->model = SH_TMU;
500 tmu->num_channels = 3;
501
502 of_property_read_u32(np, "#renesas,channels", &tmu->num_channels);
503
504 if (tmu->num_channels != 2 && tmu->num_channels != 3) {
505 dev_err(&tmu->pdev->dev, "invalid number of channels %u\n",
506 tmu->num_channels);
507 return -EINVAL;
508 }
509
510 return 0;
511}
512
Laurent Pinchart0a72aa32014-01-27 22:04:17 +0100513static int sh_tmu_setup(struct sh_tmu_device *tmu, struct platform_device *pdev)
Magnus Damm9570ef22009-05-01 06:51:00 +0000514{
Laurent Pinchart8c7f21e2014-01-28 12:36:48 +0100515 unsigned int i;
Laurent Pinchart1c56cf62014-02-17 11:27:49 +0100516 int ret;
Magnus Damm9570ef22009-05-01 06:51:00 +0000517
Laurent Pinchart3e29b552014-04-11 16:23:40 +0200518 tmu->pdev = pdev;
519
520 raw_spin_lock_init(&tmu->lock);
521
522 if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) {
523 ret = sh_tmu_parse_dt(tmu);
524 if (ret < 0)
525 return ret;
526 } else if (pdev->dev.platform_data) {
527 const struct platform_device_id *id = pdev->id_entry;
528 struct sh_timer_config *cfg = pdev->dev.platform_data;
529
530 tmu->model = id->driver_data;
531 tmu->num_channels = hweight8(cfg->channels_mask);
532 } else {
Laurent Pinchart0a72aa32014-01-27 22:04:17 +0100533 dev_err(&tmu->pdev->dev, "missing platform data\n");
Laurent Pinchart8c7f21e2014-01-28 12:36:48 +0100534 return -ENXIO;
Magnus Damm9570ef22009-05-01 06:51:00 +0000535 }
536
Laurent Pinchart8c7f21e2014-01-28 12:36:48 +0100537 /* Get hold of clock. */
Laurent Pinchart681b9e82014-01-28 15:52:46 +0100538 tmu->clk = clk_get(&tmu->pdev->dev, "fck");
Laurent Pinchart0a72aa32014-01-27 22:04:17 +0100539 if (IS_ERR(tmu->clk)) {
540 dev_err(&tmu->pdev->dev, "cannot get clock\n");
Laurent Pinchart8c7f21e2014-01-28 12:36:48 +0100541 return PTR_ERR(tmu->clk);
Magnus Damm9570ef22009-05-01 06:51:00 +0000542 }
Laurent Pinchart1c09eb32013-11-08 11:08:00 +0100543
Laurent Pinchart0a72aa32014-01-27 22:04:17 +0100544 ret = clk_prepare(tmu->clk);
Laurent Pinchart1c09eb32013-11-08 11:08:00 +0100545 if (ret < 0)
Laurent Pinchart8c7f21e2014-01-28 12:36:48 +0100546 goto err_clk_put;
Laurent Pinchart1c09eb32013-11-08 11:08:00 +0100547
Nicolai Stangec3c0a202017-02-06 22:12:00 +0100548 /* Determine clock rate. */
549 ret = clk_enable(tmu->clk);
550 if (ret < 0)
551 goto err_clk_unprepare;
552
553 tmu->rate = clk_get_rate(tmu->clk) / 4;
554 clk_disable(tmu->clk);
555
Laurent Pinchart8c7f21e2014-01-28 12:36:48 +0100556 /* Map the memory resource. */
557 ret = sh_tmu_map_memory(tmu);
558 if (ret < 0) {
559 dev_err(&tmu->pdev->dev, "failed to remap I/O memory\n");
560 goto err_clk_unprepare;
Laurent Pincharta5de49f2014-01-27 22:04:17 +0100561 }
562
Laurent Pinchart8c7f21e2014-01-28 12:36:48 +0100563 /* Allocate and setup the channels. */
Kees Cook6396bb22018-06-12 14:03:40 -0700564 tmu->channels = kcalloc(tmu->num_channels, sizeof(*tmu->channels),
Laurent Pinchart8c7f21e2014-01-28 12:36:48 +0100565 GFP_KERNEL);
566 if (tmu->channels == NULL) {
567 ret = -ENOMEM;
568 goto err_unmap;
569 }
Laurent Pincharta5de49f2014-01-27 22:04:17 +0100570
Laurent Pinchart681b9e82014-01-28 15:52:46 +0100571 /*
572 * Use the first channel as a clock event device and the second channel
573 * as a clock source.
574 */
575 for (i = 0; i < tmu->num_channels; ++i) {
576 ret = sh_tmu_channel_setup(&tmu->channels[i], i,
577 i == 0, i == 1, tmu);
Laurent Pinchart8c7f21e2014-01-28 12:36:48 +0100578 if (ret < 0)
579 goto err_unmap;
Laurent Pinchart8c7f21e2014-01-28 12:36:48 +0100580 }
581
582 platform_set_drvdata(pdev, tmu);
Laurent Pinchart394a4482013-11-08 11:07:59 +0100583
584 return 0;
585
Laurent Pinchart8c7f21e2014-01-28 12:36:48 +0100586err_unmap:
Laurent Pincharta5de49f2014-01-27 22:04:17 +0100587 kfree(tmu->channels);
Laurent Pinchart681b9e82014-01-28 15:52:46 +0100588 iounmap(tmu->mapbase);
Laurent Pinchart8c7f21e2014-01-28 12:36:48 +0100589err_clk_unprepare:
Laurent Pinchart0a72aa32014-01-27 22:04:17 +0100590 clk_unprepare(tmu->clk);
Laurent Pinchart8c7f21e2014-01-28 12:36:48 +0100591err_clk_put:
Laurent Pinchart0a72aa32014-01-27 22:04:17 +0100592 clk_put(tmu->clk);
Magnus Damm9570ef22009-05-01 06:51:00 +0000593 return ret;
594}
595
Greg Kroah-Hartman18505142012-12-21 15:11:38 -0800596static int sh_tmu_probe(struct platform_device *pdev)
Magnus Damm9570ef22009-05-01 06:51:00 +0000597{
Laurent Pinchart0a72aa32014-01-27 22:04:17 +0100598 struct sh_tmu_device *tmu = platform_get_drvdata(pdev);
Magnus Damm9570ef22009-05-01 06:51:00 +0000599 int ret;
600
Rafael J. Wysockieaa49a82012-08-06 01:41:20 +0200601 if (!is_early_platform_device(pdev)) {
Rafael J. Wysocki61a53bf2012-08-06 01:48:17 +0200602 pm_runtime_set_active(&pdev->dev);
603 pm_runtime_enable(&pdev->dev);
Rafael J. Wysockieaa49a82012-08-06 01:41:20 +0200604 }
Rafael J. Wysocki2ee619f2012-03-13 22:40:00 +0100605
Laurent Pinchart0a72aa32014-01-27 22:04:17 +0100606 if (tmu) {
Paul Mundt214a6072010-03-10 16:26:25 +0900607 dev_info(&pdev->dev, "kept as earlytimer\n");
Rafael J. Wysocki61a53bf2012-08-06 01:48:17 +0200608 goto out;
Magnus Damm9570ef22009-05-01 06:51:00 +0000609 }
610
Laurent Pinchart3b77a832014-01-27 22:04:17 +0100611 tmu = kzalloc(sizeof(*tmu), GFP_KERNEL);
Jingoo Han814876b2014-05-22 14:05:07 +0200612 if (tmu == NULL)
Magnus Damm9570ef22009-05-01 06:51:00 +0000613 return -ENOMEM;
Magnus Damm9570ef22009-05-01 06:51:00 +0000614
Laurent Pinchart0a72aa32014-01-27 22:04:17 +0100615 ret = sh_tmu_setup(tmu, pdev);
Magnus Damm9570ef22009-05-01 06:51:00 +0000616 if (ret) {
Laurent Pinchart0a72aa32014-01-27 22:04:17 +0100617 kfree(tmu);
Rafael J. Wysocki61a53bf2012-08-06 01:48:17 +0200618 pm_runtime_idle(&pdev->dev);
619 return ret;
Magnus Damm9570ef22009-05-01 06:51:00 +0000620 }
Rafael J. Wysocki61a53bf2012-08-06 01:48:17 +0200621 if (is_early_platform_device(pdev))
622 return 0;
623
624 out:
Laurent Pinchart8c7f21e2014-01-28 12:36:48 +0100625 if (tmu->has_clockevent || tmu->has_clocksource)
Rafael J. Wysocki61a53bf2012-08-06 01:48:17 +0200626 pm_runtime_irq_safe(&pdev->dev);
627 else
628 pm_runtime_idle(&pdev->dev);
629
630 return 0;
Magnus Damm9570ef22009-05-01 06:51:00 +0000631}
632
Greg Kroah-Hartman18505142012-12-21 15:11:38 -0800633static int sh_tmu_remove(struct platform_device *pdev)
Magnus Damm9570ef22009-05-01 06:51:00 +0000634{
635 return -EBUSY; /* cannot unregister clockevent and clocksource */
636}
637
Laurent Pinchart8c7f21e2014-01-28 12:36:48 +0100638static const struct platform_device_id sh_tmu_id_table[] = {
Laurent Pinchart8c7f21e2014-01-28 12:36:48 +0100639 { "sh-tmu", SH_TMU },
640 { "sh-tmu-sh3", SH_TMU_SH3 },
641 { }
642};
643MODULE_DEVICE_TABLE(platform, sh_tmu_id_table);
644
Laurent Pinchart3e29b552014-04-11 16:23:40 +0200645static const struct of_device_id sh_tmu_of_table[] __maybe_unused = {
646 { .compatible = "renesas,tmu" },
647 { }
648};
649MODULE_DEVICE_TABLE(of, sh_tmu_of_table);
650
Magnus Damm9570ef22009-05-01 06:51:00 +0000651static struct platform_driver sh_tmu_device_driver = {
652 .probe = sh_tmu_probe,
Greg Kroah-Hartman18505142012-12-21 15:11:38 -0800653 .remove = sh_tmu_remove,
Magnus Damm9570ef22009-05-01 06:51:00 +0000654 .driver = {
655 .name = "sh_tmu",
Laurent Pinchart3e29b552014-04-11 16:23:40 +0200656 .of_match_table = of_match_ptr(sh_tmu_of_table),
Laurent Pinchart8c7f21e2014-01-28 12:36:48 +0100657 },
658 .id_table = sh_tmu_id_table,
Magnus Damm9570ef22009-05-01 06:51:00 +0000659};
660
661static int __init sh_tmu_init(void)
662{
663 return platform_driver_register(&sh_tmu_device_driver);
664}
665
666static void __exit sh_tmu_exit(void)
667{
668 platform_driver_unregister(&sh_tmu_device_driver);
669}
670
671early_platform_init("earlytimer", &sh_tmu_device_driver);
Simon Hormanb9773c32013-03-05 15:40:42 +0900672subsys_initcall(sh_tmu_init);
Magnus Damm9570ef22009-05-01 06:51:00 +0000673module_exit(sh_tmu_exit);
674
675MODULE_AUTHOR("Magnus Damm");
676MODULE_DESCRIPTION("SuperH TMU Timer Driver");
677MODULE_LICENSE("GPL v2");