dmtimer.c 15.0 KB
Newer Older
1 2 3 4 5
/*
 * linux/arch/arm/plat-omap/dmtimer.c
 *
 * OMAP Dual-Mode Timers
 *
6 7 8 9 10 11
 * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/
 * Tarun Kanti DebBarma <tarun.kanti@ti.com>
 * Thara Gopinath <thara@ti.com>
 *
 * dmtimer adaptation to platform_driver.
 *
12
 * Copyright (C) 2005 Nokia Corporation
13 14
 * OMAP2 support by Juha Yrjola
 * API improvements and OMAP2 clock framework support by Timo Teras
15
 *
16 17 18
 * Copyright (C) 2009 Texas Instruments
 * Added OMAP4 support - Santosh Shilimkar <santosh.shilimkar@ti.com>
 *
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or (at your
 * option) any later version.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
 * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * You should have received a copy of the  GNU General Public License along
 * with this program; if not, write  to the Free Software Foundation, Inc.,
 * 675 Mass Ave, Cambridge, MA 02139, USA.
 */

38
#include <linux/io.h>
39
#include <linux/slab.h>
40
#include <linux/err.h>
41

42
#include <plat/dmtimer.h>
43

44
static LIST_HEAD(omap_timer_list);
45
static DEFINE_SPINLOCK(dm_timer_lock);
46

47 48 49 50 51 52 53 54
/**
 * omap_dm_timer_read_reg - read timer registers in posted and non-posted mode
 * @timer:      timer pointer over which read operation to perform
 * @reg:        lowest byte holds the register offset
 *
 * The posted mode bit is encoded in reg. Note that in posted mode write
 * pending bit must be checked. Otherwise a read of a non completed write
 * will produce an error.
55 56
 */
static inline u32 omap_dm_timer_read_reg(struct omap_dm_timer *timer, u32 reg)
57
{
58 59
	WARN_ON((reg & 0xff) < _OMAP_TIMER_WAKEUP_EN_OFFSET);
	return __omap_dm_timer_read(timer, reg, timer->posted);
60
}
61

62 63 64 65 66 67 68 69 70
/**
 * omap_dm_timer_write_reg - write timer registers in posted and non-posted mode
 * @timer:      timer pointer over which write operation is to perform
 * @reg:        lowest byte holds the register offset
 * @value:      data to write into the register
 *
 * The posted mode bit is encoded in reg. Note that in posted mode the write
 * pending bit must be checked. Otherwise a write on a register which has a
 * pending write will be lost.
71 72 73
 */
static void omap_dm_timer_write_reg(struct omap_dm_timer *timer, u32 reg,
						u32 value)
74
{
75 76
	WARN_ON((reg & 0xff) < _OMAP_TIMER_WAKEUP_EN_OFFSET);
	__omap_dm_timer_write(timer, reg, value, timer->posted);
77 78
}

79
static void omap_dm_timer_wait_for_reset(struct omap_dm_timer *timer)
80
{
81 82
	int c;

83 84 85
	if (!timer->sys_stat)
		return;

86
	c = 0;
87
	while (!(__raw_readl(timer->sys_stat) & 1)) {
88 89 90 91 92 93
		c++;
		if (c > 100000) {
			printk(KERN_ERR "Timer failed to reset\n");
			return;
		}
	}
94 95
}

96 97
static void omap_dm_timer_reset(struct omap_dm_timer *timer)
{
98
	if (timer->pdev->id != 1) {
99 100 101
		omap_dm_timer_write_reg(timer, OMAP_TIMER_IF_CTRL_REG, 0x06);
		omap_dm_timer_wait_for_reset(timer);
	}
102

103
	__omap_dm_timer_reset(timer, 0, 0);
104
	timer->posted = 1;
105 106
}

107
int omap_dm_timer_prepare(struct omap_dm_timer *timer)
108
{
109 110 111 112 113 114 115 116 117 118
	struct dmtimer_platform_data *pdata = timer->pdev->dev.platform_data;
	int ret;

	timer->fclk = clk_get(&timer->pdev->dev, "fck");
	if (WARN_ON_ONCE(IS_ERR_OR_NULL(timer->fclk))) {
		timer->fclk = NULL;
		dev_err(&timer->pdev->dev, ": No fclk handle.\n");
		return -EINVAL;
	}

119
	omap_dm_timer_enable(timer);
120 121 122 123 124 125 126 127

	if (pdata->needs_manual_reset)
		omap_dm_timer_reset(timer);

	ret = omap_dm_timer_set_source(timer, OMAP_TIMER_SRC_32_KHZ);

	timer->posted = 1;
	return ret;
128 129 130 131
}

struct omap_dm_timer *omap_dm_timer_request(void)
{
132
	struct omap_dm_timer *timer = NULL, *t;
133
	unsigned long flags;
134
	int ret = 0;
135 136

	spin_lock_irqsave(&dm_timer_lock, flags);
137 138
	list_for_each_entry(t, &omap_timer_list, node) {
		if (t->reserved)
139 140
			continue;

141
		timer = t;
T
Timo Teras 已提交
142
		timer->reserved = 1;
143 144
		break;
	}
145 146 147 148 149 150 151 152

	if (timer) {
		ret = omap_dm_timer_prepare(timer);
		if (ret) {
			timer->reserved = 0;
			timer = NULL;
		}
	}
153 154
	spin_unlock_irqrestore(&dm_timer_lock, flags);

155 156
	if (!timer)
		pr_debug("%s: timer request failed!\n", __func__);
T
Timo Teras 已提交
157

158 159
	return timer;
}
160
EXPORT_SYMBOL_GPL(omap_dm_timer_request);
161 162

struct omap_dm_timer *omap_dm_timer_request_specific(int id)
163
{
164
	struct omap_dm_timer *timer = NULL, *t;
165
	unsigned long flags;
166
	int ret = 0;
167

168
	spin_lock_irqsave(&dm_timer_lock, flags);
169 170 171 172 173 174
	list_for_each_entry(t, &omap_timer_list, node) {
		if (t->pdev->id == id && !t->reserved) {
			timer = t;
			timer->reserved = 1;
			break;
		}
175
	}
176

177 178 179 180 181 182 183
	if (timer) {
		ret = omap_dm_timer_prepare(timer);
		if (ret) {
			timer->reserved = 0;
			timer = NULL;
		}
	}
184 185
	spin_unlock_irqrestore(&dm_timer_lock, flags);

186 187
	if (!timer)
		pr_debug("%s: timer%d request failed!\n", __func__, id);
T
Timo Teras 已提交
188

189
	return timer;
190
}
191
EXPORT_SYMBOL_GPL(omap_dm_timer_request_specific);
192

193 194
void omap_dm_timer_free(struct omap_dm_timer *timer)
{
195
	omap_dm_timer_disable(timer);
196
	clk_put(timer->fclk);
197

198 199 200
	WARN_ON(!timer->reserved);
	timer->reserved = 0;
}
201
EXPORT_SYMBOL_GPL(omap_dm_timer_free);
202

203 204
void omap_dm_timer_enable(struct omap_dm_timer *timer)
{
205 206
	struct dmtimer_platform_data *pdata = timer->pdev->dev.platform_data;

207 208 209
	if (timer->enabled)
		return;

210
	if (!pdata->needs_manual_reset) {
211 212 213
		clk_enable(timer->fclk);
		clk_enable(timer->iclk);
	}
214 215 216

	timer->enabled = 1;
}
217
EXPORT_SYMBOL_GPL(omap_dm_timer_enable);
218 219 220

void omap_dm_timer_disable(struct omap_dm_timer *timer)
{
221 222
	struct dmtimer_platform_data *pdata = timer->pdev->dev.platform_data;

223 224 225
	if (!timer->enabled)
		return;

226
	if (!pdata->needs_manual_reset) {
227 228 229
		clk_disable(timer->iclk);
		clk_disable(timer->fclk);
	}
230 231 232

	timer->enabled = 0;
}
233
EXPORT_SYMBOL_GPL(omap_dm_timer_disable);
234

235 236 237 238
int omap_dm_timer_get_irq(struct omap_dm_timer *timer)
{
	return timer->irq;
}
239
EXPORT_SYMBOL_GPL(omap_dm_timer_get_irq);
240 241 242

#if defined(CONFIG_ARCH_OMAP1)

243 244 245 246 247 248
/**
 * omap_dm_timer_modify_idlect_mask - Check if any running timers use ARMXOR
 * @inputmask: current value of idlect mask
 */
__u32 omap_dm_timer_modify_idlect_mask(__u32 inputmask)
{
249 250 251
	int i = 0;
	struct omap_dm_timer *timer = NULL;
	unsigned long flags;
252 253 254 255 256 257

	/* If ARMXOR cannot be idled this function call is unnecessary */
	if (!(inputmask & (1 << 1)))
		return inputmask;

	/* If any active timer is using ARMXOR return modified mask */
258 259
	spin_lock_irqsave(&dm_timer_lock, flags);
	list_for_each_entry(timer, &omap_timer_list, node) {
260 261
		u32 l;

262
		l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
263 264
		if (l & OMAP_TIMER_CTRL_ST) {
			if (((omap_readl(MOD_CONF_CTRL_1) >> (i * 2)) & 0x03) == 0)
265 266 267 268
				inputmask &= ~(1 << 1);
			else
				inputmask &= ~(1 << 2);
		}
269
		i++;
270
	}
271
	spin_unlock_irqrestore(&dm_timer_lock, flags);
272 273 274

	return inputmask;
}
275
EXPORT_SYMBOL_GPL(omap_dm_timer_modify_idlect_mask);
276

277
#else
278

279
struct clk *omap_dm_timer_get_fclk(struct omap_dm_timer *timer)
280
{
281
	return timer->fclk;
282
}
283
EXPORT_SYMBOL_GPL(omap_dm_timer_get_fclk);
284

285 286 287
__u32 omap_dm_timer_modify_idlect_mask(__u32 inputmask)
{
	BUG();
288 289

	return 0;
290
}
291
EXPORT_SYMBOL_GPL(omap_dm_timer_modify_idlect_mask);
292

293
#endif
294

295
void omap_dm_timer_trigger(struct omap_dm_timer *timer)
296
{
297
	omap_dm_timer_write_reg(timer, OMAP_TIMER_TRIGGER_REG, 0);
298
}
299
EXPORT_SYMBOL_GPL(omap_dm_timer_trigger);
300

301 302 303
void omap_dm_timer_start(struct omap_dm_timer *timer)
{
	u32 l;
304

305 306 307 308 309 310
	l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
	if (!(l & OMAP_TIMER_CTRL_ST)) {
		l |= OMAP_TIMER_CTRL_ST;
		omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
	}
}
311
EXPORT_SYMBOL_GPL(omap_dm_timer_start);
312

313
void omap_dm_timer_stop(struct omap_dm_timer *timer)
314
{
315
	unsigned long rate = 0;
316
	struct dmtimer_platform_data *pdata = timer->pdev->dev.platform_data;
317

318 319
	if (!pdata->needs_manual_reset)
		rate = clk_get_rate(timer->fclk);
320

321
	__omap_dm_timer_stop(timer, timer->posted, rate);
322
}
323
EXPORT_SYMBOL_GPL(omap_dm_timer_stop);
324

325
int omap_dm_timer_set_source(struct omap_dm_timer *timer, int source)
326
{
327 328 329
	int ret;
	struct dmtimer_platform_data *pdata = timer->pdev->dev.platform_data;

330
	if (source < 0 || source >= 3)
331
		return -EINVAL;
332

333 334 335 336 337
	omap_dm_timer_disable(timer);
	ret = pdata->set_timer_src(timer->pdev, source);
	omap_dm_timer_enable(timer);

	return ret;
338
}
339
EXPORT_SYMBOL_GPL(omap_dm_timer_set_source);
340

341 342
void omap_dm_timer_set_load(struct omap_dm_timer *timer, int autoreload,
			    unsigned int load)
343 344
{
	u32 l;
345

346
	l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
347 348 349 350
	if (autoreload)
		l |= OMAP_TIMER_CTRL_AR;
	else
		l &= ~OMAP_TIMER_CTRL_AR;
351
	omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
352
	omap_dm_timer_write_reg(timer, OMAP_TIMER_LOAD_REG, load);
353

354
	omap_dm_timer_write_reg(timer, OMAP_TIMER_TRIGGER_REG, 0);
355
}
356
EXPORT_SYMBOL_GPL(omap_dm_timer_set_load);
357

358 359 360 361 362 363 364
/* Optimized set_load which removes costly spin wait in timer_start */
void omap_dm_timer_set_load_start(struct omap_dm_timer *timer, int autoreload,
                            unsigned int load)
{
	u32 l;

	l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
365
	if (autoreload) {
366
		l |= OMAP_TIMER_CTRL_AR;
367 368
		omap_dm_timer_write_reg(timer, OMAP_TIMER_LOAD_REG, load);
	} else {
369
		l &= ~OMAP_TIMER_CTRL_AR;
370
	}
371 372
	l |= OMAP_TIMER_CTRL_ST;

373
	__omap_dm_timer_load_start(timer, l, load, timer->posted);
374
}
375
EXPORT_SYMBOL_GPL(omap_dm_timer_set_load_start);
376

377 378
void omap_dm_timer_set_match(struct omap_dm_timer *timer, int enable,
			     unsigned int match)
379 380 381 382
{
	u32 l;

	l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
T
Timo Teras 已提交
383
	if (enable)
384 385 386
		l |= OMAP_TIMER_CTRL_CE;
	else
		l &= ~OMAP_TIMER_CTRL_CE;
387
	omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
388
	omap_dm_timer_write_reg(timer, OMAP_TIMER_MATCH_REG, match);
389
}
390
EXPORT_SYMBOL_GPL(omap_dm_timer_set_match);
391

392 393
void omap_dm_timer_set_pwm(struct omap_dm_timer *timer, int def_on,
			   int toggle, int trigger)
394 395 396 397
{
	u32 l;

	l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
398 399 400 401 402 403 404
	l &= ~(OMAP_TIMER_CTRL_GPOCFG | OMAP_TIMER_CTRL_SCPWM |
	       OMAP_TIMER_CTRL_PT | (0x03 << 10));
	if (def_on)
		l |= OMAP_TIMER_CTRL_SCPWM;
	if (toggle)
		l |= OMAP_TIMER_CTRL_PT;
	l |= trigger << 10;
405 406
	omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
}
407
EXPORT_SYMBOL_GPL(omap_dm_timer_set_pwm);
408

409
void omap_dm_timer_set_prescaler(struct omap_dm_timer *timer, int prescaler)
410 411 412 413
{
	u32 l;

	l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
414 415 416 417 418
	l &= ~(OMAP_TIMER_CTRL_PRE | (0x07 << 2));
	if (prescaler >= 0x00 && prescaler <= 0x07) {
		l |= OMAP_TIMER_CTRL_PRE;
		l |= prescaler << 2;
	}
419 420
	omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
}
421
EXPORT_SYMBOL_GPL(omap_dm_timer_set_prescaler);
422

423 424
void omap_dm_timer_set_int_enable(struct omap_dm_timer *timer,
				  unsigned int value)
425
{
426
	__omap_dm_timer_int_enable(timer, value);
427
}
428
EXPORT_SYMBOL_GPL(omap_dm_timer_set_int_enable);
429

430
unsigned int omap_dm_timer_read_status(struct omap_dm_timer *timer)
431
{
432 433
	unsigned int l;

434
	l = __raw_readl(timer->irq_stat);
435 436

	return l;
437
}
438
EXPORT_SYMBOL_GPL(omap_dm_timer_read_status);
439

440
void omap_dm_timer_write_status(struct omap_dm_timer *timer, unsigned int value)
441
{
442
	__omap_dm_timer_write_status(timer, value);
443
}
444
EXPORT_SYMBOL_GPL(omap_dm_timer_write_status);
445

446
unsigned int omap_dm_timer_read_counter(struct omap_dm_timer *timer)
447
{
448
	return __omap_dm_timer_read_counter(timer, timer->posted);
449
}
450
EXPORT_SYMBOL_GPL(omap_dm_timer_read_counter);
451

T
Timo Teras 已提交
452 453
void omap_dm_timer_write_counter(struct omap_dm_timer *timer, unsigned int value)
{
454
	omap_dm_timer_write_reg(timer, OMAP_TIMER_COUNTER_REG, value);
T
Timo Teras 已提交
455
}
456
EXPORT_SYMBOL_GPL(omap_dm_timer_write_counter);
T
Timo Teras 已提交
457

458
int omap_dm_timers_active(void)
459
{
460
	struct omap_dm_timer *timer;
461

462
	list_for_each_entry(timer, &omap_timer_list, node) {
463 464 465
		if (!timer->enabled)
			continue;

466
		if (omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG) &
467
		    OMAP_TIMER_CTRL_ST) {
468
			return 1;
469
		}
470 471 472
	}
	return 0;
}
473
EXPORT_SYMBOL_GPL(omap_dm_timers_active);
474

475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603
/**
 * omap_dm_timer_probe - probe function called for every registered device
 * @pdev:	pointer to current timer platform device
 *
 * Called by driver framework at the end of device registration for all
 * timer devices.
 */
static int __devinit omap_dm_timer_probe(struct platform_device *pdev)
{
	int ret;
	unsigned long flags;
	struct omap_dm_timer *timer;
	struct resource *mem, *irq, *ioarea;
	struct dmtimer_platform_data *pdata = pdev->dev.platform_data;

	if (!pdata) {
		dev_err(&pdev->dev, "%s: no platform data.\n", __func__);
		return -ENODEV;
	}

	irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
	if (unlikely(!irq)) {
		dev_err(&pdev->dev, "%s: no IRQ resource.\n", __func__);
		return -ENODEV;
	}

	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (unlikely(!mem)) {
		dev_err(&pdev->dev, "%s: no memory resource.\n", __func__);
		return -ENODEV;
	}

	ioarea = request_mem_region(mem->start, resource_size(mem),
			pdev->name);
	if (!ioarea) {
		dev_err(&pdev->dev, "%s: region already claimed.\n", __func__);
		return -EBUSY;
	}

	timer = kzalloc(sizeof(struct omap_dm_timer), GFP_KERNEL);
	if (!timer) {
		dev_err(&pdev->dev, "%s: no memory for omap_dm_timer.\n",
			__func__);
		ret = -ENOMEM;
		goto err_free_ioregion;
	}

	timer->io_base = ioremap(mem->start, resource_size(mem));
	if (!timer->io_base) {
		dev_err(&pdev->dev, "%s: ioremap failed.\n", __func__);
		ret = -ENOMEM;
		goto err_free_mem;
	}

	timer->id = pdev->id;
	timer->irq = irq->start;
	timer->pdev = pdev;

	/* add the timer element to the list */
	spin_lock_irqsave(&dm_timer_lock, flags);
	list_add_tail(&timer->node, &omap_timer_list);
	spin_unlock_irqrestore(&dm_timer_lock, flags);

	dev_dbg(&pdev->dev, "Device Probed.\n");

	return 0;

err_free_mem:
	kfree(timer);

err_free_ioregion:
	release_mem_region(mem->start, resource_size(mem));

	return ret;
}

/**
 * omap_dm_timer_remove - cleanup a registered timer device
 * @pdev:	pointer to current timer platform device
 *
 * Called by driver framework whenever a timer device is unregistered.
 * In addition to freeing platform resources it also deletes the timer
 * entry from the local list.
 */
static int __devexit omap_dm_timer_remove(struct platform_device *pdev)
{
	struct omap_dm_timer *timer;
	unsigned long flags;
	int ret = -EINVAL;

	spin_lock_irqsave(&dm_timer_lock, flags);
	list_for_each_entry(timer, &omap_timer_list, node)
		if (timer->pdev->id == pdev->id) {
			list_del(&timer->node);
			kfree(timer);
			ret = 0;
			break;
		}
	spin_unlock_irqrestore(&dm_timer_lock, flags);

	return ret;
}

static struct platform_driver omap_dm_timer_driver = {
	.probe  = omap_dm_timer_probe,
	.remove = omap_dm_timer_remove,
	.driver = {
		.name   = "omap_timer",
	},
};

static int __init omap_dm_timer_driver_init(void)
{
	return platform_driver_register(&omap_dm_timer_driver);
}

static void __exit omap_dm_timer_driver_exit(void)
{
	platform_driver_unregister(&omap_dm_timer_driver);
}

early_platform_init("earlytimer", &omap_dm_timer_driver);
module_init(omap_dm_timer_driver_init);
module_exit(omap_dm_timer_driver_exit);

MODULE_DESCRIPTION("OMAP Dual-Mode Timer Driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" DRIVER_NAME);
MODULE_AUTHOR("Texas Instruments Inc");