samsung_pwm_timer.c 11.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
/*
 * Copyright (c) 2011 Samsung Electronics Co., Ltd.
 *		http://www.samsung.com/
 *
 * samsung - Common hr-timer support (s3c and s5p)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
*/

#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/clockchips.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/slab.h>

#include <clocksource/samsung_pwm.h>

#include <asm/sched_clock.h>

/*
 * Clocksource driver
 */

#define REG_TCFG0			0x00
#define REG_TCFG1			0x04
#define REG_TCON			0x08
#define REG_TINT_CSTAT			0x44

#define REG_TCNTB(chan)			(0x0c + 12 * (chan))
#define REG_TCMPB(chan)			(0x10 + 12 * (chan))

#define TCFG0_PRESCALER_MASK		0xff
#define TCFG0_PRESCALER1_SHIFT		8

#define TCFG1_SHIFT(x)	  		((x) * 4)
#define TCFG1_MUX_MASK	  		0xf

#define TCON_START(chan)		(1 << (4 * (chan) + 0))
#define TCON_MANUALUPDATE(chan)		(1 << (4 * (chan) + 1))
#define TCON_INVERT(chan)		(1 << (4 * (chan) + 2))
#define TCON_AUTORELOAD(chan)		(1 << (4 * (chan) + 3))

52 53 54
DEFINE_SPINLOCK(samsung_pwm_lock);
EXPORT_SYMBOL(samsung_pwm_lock);

55 56 57 58 59 60 61
struct samsung_pwm_clocksource {
	void __iomem *base;
	unsigned int irq[SAMSUNG_PWM_NUM];
	struct samsung_pwm_variant variant;

	struct clk *timerclk;

62 63 64 65 66
	unsigned int event_id;
	unsigned int source_id;
	unsigned int tcnt_max;
	unsigned int tscaler_div;
	unsigned int tdiv;
67 68

	unsigned long clock_count_per_tick;
69 70
};

71
static struct samsung_pwm_clocksource pwm;
72

73
static void samsung_timer_set_prescale(unsigned int channel, u16 prescale)
74 75 76 77 78 79 80 81
{
	unsigned long flags;
	u8 shift = 0;
	u32 reg;

	if (channel >= 2)
		shift = TCFG0_PRESCALER1_SHIFT;

82
	spin_lock_irqsave(&samsung_pwm_lock, flags);
83

84
	reg = readl(pwm.base + REG_TCFG0);
85 86
	reg &= ~(TCFG0_PRESCALER_MASK << shift);
	reg |= (prescale - 1) << shift;
87
	writel(reg, pwm.base + REG_TCFG0);
88

89
	spin_unlock_irqrestore(&samsung_pwm_lock, flags);
90 91
}

92
static void samsung_timer_set_divisor(unsigned int channel, u8 divisor)
93 94 95 96 97 98
{
	u8 shift = TCFG1_SHIFT(channel);
	unsigned long flags;
	u32 reg;
	u8 bits;

99
	bits = (fls(divisor) - 1) - pwm.variant.div_base;
100

101
	spin_lock_irqsave(&samsung_pwm_lock, flags);
102

103
	reg = readl(pwm.base + REG_TCFG1);
104 105
	reg &= ~(TCFG1_MUX_MASK << shift);
	reg |= bits << shift;
106
	writel(reg, pwm.base + REG_TCFG1);
107

108
	spin_unlock_irqrestore(&samsung_pwm_lock, flags);
109 110 111 112 113 114 115 116 117 118
}

static void samsung_time_stop(unsigned int channel)
{
	unsigned long tcon;
	unsigned long flags;

	if (channel > 0)
		++channel;

119
	spin_lock_irqsave(&samsung_pwm_lock, flags);
120

121
	tcon = __raw_readl(pwm.base + REG_TCON);
122
	tcon &= ~TCON_START(channel);
123
	__raw_writel(tcon, pwm.base + REG_TCON);
124

125
	spin_unlock_irqrestore(&samsung_pwm_lock, flags);
126 127 128 129 130 131 132 133 134 135 136
}

static void samsung_time_setup(unsigned int channel, unsigned long tcnt)
{
	unsigned long tcon;
	unsigned long flags;
	unsigned int tcon_chan = channel;

	if (tcon_chan > 0)
		++tcon_chan;

137
	spin_lock_irqsave(&samsung_pwm_lock, flags);
138

139
	tcon = __raw_readl(pwm.base + REG_TCON);
140 141 142 143

	tcon &= ~(TCON_START(tcon_chan) | TCON_AUTORELOAD(tcon_chan));
	tcon |= TCON_MANUALUPDATE(tcon_chan);

144 145 146
	__raw_writel(tcnt, pwm.base + REG_TCNTB(channel));
	__raw_writel(tcnt, pwm.base + REG_TCMPB(channel));
	__raw_writel(tcon, pwm.base + REG_TCON);
147

148
	spin_unlock_irqrestore(&samsung_pwm_lock, flags);
149 150 151 152 153 154 155 156 157 158
}

static void samsung_time_start(unsigned int channel, bool periodic)
{
	unsigned long tcon;
	unsigned long flags;

	if (channel > 0)
		++channel;

159
	spin_lock_irqsave(&samsung_pwm_lock, flags);
160

161
	tcon = __raw_readl(pwm.base + REG_TCON);
162 163 164 165 166 167 168 169 170

	tcon &= ~TCON_MANUALUPDATE(channel);
	tcon |= TCON_START(channel);

	if (periodic)
		tcon |= TCON_AUTORELOAD(channel);
	else
		tcon &= ~TCON_AUTORELOAD(channel);

171
	__raw_writel(tcon, pwm.base + REG_TCON);
172

173
	spin_unlock_irqrestore(&samsung_pwm_lock, flags);
174 175 176 177 178
}

static int samsung_set_next_event(unsigned long cycles,
				struct clock_event_device *evt)
{
179 180 181 182 183 184 185 186 187 188 189 190 191
	/*
	 * This check is needed to account for internal rounding
	 * errors inside clockevents core, which might result in
	 * passing cycles = 0, which in turn would not generate any
	 * timer interrupt and hang the system.
	 *
	 * Another solution would be to set up the clockevent device
	 * with min_delta = 2, but this would unnecessarily increase
	 * the minimum sleep period.
	 */
	if (!cycles)
		cycles = 1;

192 193
	samsung_time_setup(pwm.event_id, cycles);
	samsung_time_start(pwm.event_id, false);
194 195 196 197 198 199 200

	return 0;
}

static void samsung_timer_resume(void)
{
	/* event timer restart */
201
	samsung_time_setup(pwm.event_id, pwm.clock_count_per_tick - 1);
202
	samsung_time_start(pwm.event_id, true);
203 204

	/* source timer restart */
205 206
	samsung_time_setup(pwm.source_id, pwm.tcnt_max);
	samsung_time_start(pwm.source_id, true);
207 208 209 210 211
}

static void samsung_set_mode(enum clock_event_mode mode,
				struct clock_event_device *evt)
{
212
	samsung_time_stop(pwm.event_id);
213 214 215

	switch (mode) {
	case CLOCK_EVT_MODE_PERIODIC:
216
		samsung_time_setup(pwm.event_id, pwm.clock_count_per_tick - 1);
217
		samsung_time_start(pwm.event_id, true);
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
		break;

	case CLOCK_EVT_MODE_ONESHOT:
		break;

	case CLOCK_EVT_MODE_UNUSED:
	case CLOCK_EVT_MODE_SHUTDOWN:
		break;

	case CLOCK_EVT_MODE_RESUME:
		samsung_timer_resume();
		break;
	}
}

static struct clock_event_device time_event_device = {
	.name		= "samsung_event_timer",
	.features	= CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
	.rating		= 200,
	.set_next_event	= samsung_set_next_event,
	.set_mode	= samsung_set_mode,
};

static irqreturn_t samsung_clock_event_isr(int irq, void *dev_id)
{
	struct clock_event_device *evt = dev_id;

245 246 247
	if (pwm.variant.has_tint_cstat) {
		u32 mask = (1 << pwm.event_id);
		writel(mask | (mask << 5), pwm.base + REG_TINT_CSTAT);
248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
	}

	evt->event_handler(evt);

	return IRQ_HANDLED;
}

static struct irqaction samsung_clock_event_irq = {
	.name		= "samsung_time_irq",
	.flags		= IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
	.handler	= samsung_clock_event_isr,
	.dev_id		= &time_event_device,
};

static void __init samsung_clockevent_init(void)
{
	unsigned long pclk;
	unsigned long clock_rate;
	unsigned int irq_number;

268
	pclk = clk_get_rate(pwm.timerclk);
269

270 271
	samsung_timer_set_prescale(pwm.event_id, pwm.tscaler_div);
	samsung_timer_set_divisor(pwm.event_id, pwm.tdiv);
272

273 274
	clock_rate = pclk / (pwm.tscaler_div * pwm.tdiv);
	pwm.clock_count_per_tick = clock_rate / HZ;
275 276

	time_event_device.cpumask = cpumask_of(0);
277 278
	clockevents_config_and_register(&time_event_device,
						clock_rate, 1, pwm.tcnt_max);
279

280
	irq_number = pwm.irq[pwm.event_id];
281 282
	setup_irq(irq_number, &samsung_clock_event_irq);

283 284 285
	if (pwm.variant.has_tint_cstat) {
		u32 mask = (1 << pwm.event_id);
		writel(mask | (mask << 5), pwm.base + REG_TINT_CSTAT);
286 287 288 289 290
	}
}

static void __iomem *samsung_timer_reg(void)
{
291
	switch (pwm.source_id) {
292 293 294 295
	case 0:
	case 1:
	case 2:
	case 3:
296
		return pwm.base + pwm.source_id * 0x0c + 0x14;
297 298

	case 4:
299
		return pwm.base + 0x40;
300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329

	default:
		BUG();
	}
}

/*
 * Override the global weak sched_clock symbol with this
 * local implementation which uses the clocksource to get some
 * better resolution when scheduling the kernel. We accept that
 * this wraps around for now, since it is just a relative time
 * stamp. (Inspired by U300 implementation.)
 */
static u32 notrace samsung_read_sched_clock(void)
{
	void __iomem *reg = samsung_timer_reg();

	if (!reg)
		return 0;

	return ~__raw_readl(reg);
}

static void __init samsung_clocksource_init(void)
{
	void __iomem *reg = samsung_timer_reg();
	unsigned long pclk;
	unsigned long clock_rate;
	int ret;

330
	pclk = clk_get_rate(pwm.timerclk);
331

332 333
	samsung_timer_set_prescale(pwm.source_id, pwm.tscaler_div);
	samsung_timer_set_divisor(pwm.source_id, pwm.tdiv);
334

335
	clock_rate = pclk / (pwm.tscaler_div * pwm.tdiv);
336

337 338
	samsung_time_setup(pwm.source_id, pwm.tcnt_max);
	samsung_time_start(pwm.source_id, true);
339 340

	setup_sched_clock(samsung_read_sched_clock,
341
						pwm.variant.bits, clock_rate);
342 343

	ret = clocksource_mmio_init(reg, "samsung_clocksource_timer",
344
					clock_rate, 250, pwm.variant.bits,
345 346 347 348 349 350 351
					clocksource_mmio_readl_down);
	if (ret)
		panic("samsung_clocksource_timer: can't register clocksource\n");
}

static void __init samsung_timer_resources(void)
{
352 353
	pwm.timerclk = clk_get(NULL, "timers");
	if (IS_ERR(pwm.timerclk))
354 355
		panic("failed to get timers clock for timer");

356
	clk_prepare_enable(pwm.timerclk);
357

358 359 360 361
	pwm.tcnt_max = (1UL << pwm.variant.bits) - 1;
	if (pwm.variant.bits == 16) {
		pwm.tscaler_div = 25;
		pwm.tdiv = 2;
362
	} else {
363 364
		pwm.tscaler_div = 2;
		pwm.tdiv = 1;
365 366 367 368 369 370
	}
}

/*
 * PWM master driver
 */
371
static void __init _samsung_pwm_clocksource_init(void)
372 373 374 375
{
	u8 mask;
	int channel;

376
	mask = ~pwm.variant.output_mask & ((1 << SAMSUNG_PWM_NUM) - 1);
377 378 379
	channel = fls(mask) - 1;
	if (channel < 0)
		panic("failed to find PWM channel for clocksource");
380
	pwm.source_id = channel;
381 382 383 384 385

	mask &= ~(1 << channel);
	channel = fls(mask) - 1;
	if (channel < 0)
		panic("failed to find PWM channel for clock event");
386
	pwm.event_id = channel;
387 388 389 390 391 392

	samsung_timer_resources();
	samsung_clockevent_init();
	samsung_clocksource_init();
}

393 394 395 396 397 398 399 400 401 402 403
void __init samsung_pwm_clocksource_init(void __iomem *base,
			unsigned int *irqs, struct samsung_pwm_variant *variant)
{
	pwm.base = base;
	memcpy(&pwm.variant, variant, sizeof(pwm.variant));
	memcpy(pwm.irq, irqs, SAMSUNG_PWM_NUM * sizeof(*irqs));

	_samsung_pwm_clocksource_init();
}

#ifdef CONFIG_CLKSRC_OF
404 405 406 407 408 409 410 411 412
static void __init samsung_pwm_alloc(struct device_node *np,
				     const struct samsung_pwm_variant *variant)
{
	struct resource res;
	struct property *prop;
	const __be32 *cur;
	u32 val;
	int i;

413
	memcpy(&pwm.variant, variant, sizeof(pwm.variant));
414
	for (i = 0; i < SAMSUNG_PWM_NUM; ++i)
415
		pwm.irq[i] = irq_of_parse_and_map(np, i);
416 417 418 419 420 421 422

	of_property_for_each_u32(np, "samsung,pwm-outputs", prop, cur, val) {
		if (val >= SAMSUNG_PWM_NUM) {
			pr_warning("%s: invalid channel index in samsung,pwm-outputs property\n",
								__func__);
			continue;
		}
423
		pwm.variant.output_mask |= 1 << val;
424 425 426 427 428 429 430 431 432
	}

	of_address_to_resource(np, 0, &res);
	if (!request_mem_region(res.start,
				resource_size(&res), "samsung-pwm")) {
		pr_err("%s: failed to request IO mem region\n", __func__);
		return;
	}

433 434
	pwm.base = ioremap(res.start, resource_size(&res));
	if (!pwm.base) {
435 436 437 438 439
		pr_err("%s: failed to map PWM registers\n", __func__);
		release_mem_region(res.start, resource_size(&res));
		return;
	}

440
	_samsung_pwm_clocksource_init();
441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493
}

static const struct samsung_pwm_variant s3c24xx_variant = {
	.bits		= 16,
	.div_base	= 1,
	.has_tint_cstat	= false,
	.tclk_mask	= (1 << 4),
};

static void __init s3c2410_pwm_clocksource_init(struct device_node *np)
{
	samsung_pwm_alloc(np, &s3c24xx_variant);
}
CLOCKSOURCE_OF_DECLARE(s3c2410_pwm, "samsung,s3c2410-pwm", s3c2410_pwm_clocksource_init);

static const struct samsung_pwm_variant s3c64xx_variant = {
	.bits		= 32,
	.div_base	= 0,
	.has_tint_cstat	= true,
	.tclk_mask	= (1 << 7) | (1 << 6) | (1 << 5),
};

static void __init s3c64xx_pwm_clocksource_init(struct device_node *np)
{
	samsung_pwm_alloc(np, &s3c64xx_variant);
}
CLOCKSOURCE_OF_DECLARE(s3c6400_pwm, "samsung,s3c6400-pwm", s3c64xx_pwm_clocksource_init);

static const struct samsung_pwm_variant s5p64x0_variant = {
	.bits		= 32,
	.div_base	= 0,
	.has_tint_cstat	= true,
	.tclk_mask	= 0,
};

static void __init s5p64x0_pwm_clocksource_init(struct device_node *np)
{
	samsung_pwm_alloc(np, &s5p64x0_variant);
}
CLOCKSOURCE_OF_DECLARE(s5p6440_pwm, "samsung,s5p6440-pwm", s5p64x0_pwm_clocksource_init);

static const struct samsung_pwm_variant s5p_variant = {
	.bits		= 32,
	.div_base	= 0,
	.has_tint_cstat	= true,
	.tclk_mask	= (1 << 5),
};

static void __init s5p_pwm_clocksource_init(struct device_node *np)
{
	samsung_pwm_alloc(np, &s5p_variant);
}
CLOCKSOURCE_OF_DECLARE(s5pc100_pwm, "samsung,s5pc100-pwm", s5p_pwm_clocksource_init);
494
#endif