dmtimer.c 22.8 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/clk.h>
39
#include <linux/module.h>
40
#include <linux/io.h>
41
#include <linux/device.h>
42
#include <linux/err.h>
43
#include <linux/pm_runtime.h>
44 45
#include <linux/of.h>
#include <linux/of_device.h>
46 47
#include <linux/platform_device.h>
#include <linux/platform_data/dmtimer-omap.h>
48

49
#include <plat/dmtimer.h>
50

51
static u32 omap_reserved_systimers;
52
static LIST_HEAD(omap_timer_list);
53
static DEFINE_SPINLOCK(dm_timer_lock);
54

55 56 57 58 59 60 61 62
/**
 * 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.
63 64
 */
static inline u32 omap_dm_timer_read_reg(struct omap_dm_timer *timer, u32 reg)
65
{
66 67
	WARN_ON((reg & 0xff) < _OMAP_TIMER_WAKEUP_EN_OFFSET);
	return __omap_dm_timer_read(timer, reg, timer->posted);
68
}
69

70 71 72 73 74 75 76 77 78
/**
 * 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.
79 80 81
 */
static void omap_dm_timer_write_reg(struct omap_dm_timer *timer, u32 reg,
						u32 value)
82
{
83 84
	WARN_ON((reg & 0xff) < _OMAP_TIMER_WAKEUP_EN_OFFSET);
	__omap_dm_timer_write(timer, reg, value, timer->posted);
85 86
}

87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
static void omap_timer_restore_context(struct omap_dm_timer *timer)
{
	omap_dm_timer_write_reg(timer, OMAP_TIMER_WAKEUP_EN_REG,
				timer->context.twer);
	omap_dm_timer_write_reg(timer, OMAP_TIMER_COUNTER_REG,
				timer->context.tcrr);
	omap_dm_timer_write_reg(timer, OMAP_TIMER_LOAD_REG,
				timer->context.tldr);
	omap_dm_timer_write_reg(timer, OMAP_TIMER_MATCH_REG,
				timer->context.tmar);
	omap_dm_timer_write_reg(timer, OMAP_TIMER_IF_CTRL_REG,
				timer->context.tsicr);
	__raw_writel(timer->context.tier, timer->irq_ena);
	omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG,
				timer->context.tclr);
}

104
static int omap_dm_timer_reset(struct omap_dm_timer *timer)
105
{
106
	u32 l, timeout = 100000;
107

108 109
	if (timer->revision != 1)
		return -EINVAL;
110

111 112 113 114 115 116 117 118 119 120
	omap_dm_timer_write_reg(timer, OMAP_TIMER_IF_CTRL_REG, 0x06);

	do {
		l = __omap_dm_timer_read(timer,
					 OMAP_TIMER_V1_SYS_STAT_OFFSET, 0);
	} while (!l && timeout--);

	if (!timeout) {
		dev_err(&timer->pdev->dev, "Timer failed to reset\n");
		return -ETIMEDOUT;
121
	}
122

123 124 125 126 127 128 129 130
	/* Configure timer for smart-idle mode */
	l = __omap_dm_timer_read(timer, OMAP_TIMER_OCP_CFG_OFFSET, 0);
	l |= 0x2 << 0x3;
	__omap_dm_timer_write(timer, OMAP_TIMER_OCP_CFG_OFFSET, l, 0);

	timer->posted = 0;

	return 0;
131 132
}

133
static int omap_dm_timer_prepare(struct omap_dm_timer *timer)
134
{
135 136
	int rc;

J
Jon Hunter 已提交
137 138 139 140 141 142 143 144 145 146 147
	/*
	 * FIXME: OMAP1 devices do not use the clock framework for dmtimers so
	 * do not call clk_get() for these devices.
	 */
	if (!(timer->capability & OMAP_TIMER_NEEDS_RESET)) {
		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;
		}
148 149
	}

150 151
	omap_dm_timer_enable(timer);

152 153 154 155 156 157 158
	if (timer->capability & OMAP_TIMER_NEEDS_RESET) {
		rc = omap_dm_timer_reset(timer);
		if (rc) {
			omap_dm_timer_disable(timer);
			return rc;
		}
	}
159

160 161
	__omap_dm_timer_enable_posted(timer);
	omap_dm_timer_disable(timer);
162

163
	return omap_dm_timer_set_source(timer, OMAP_TIMER_SRC_32_KHZ);
164 165
}

166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
static inline u32 omap_dm_timer_reserved_systimer(int id)
{
	return (omap_reserved_systimers & (1 << (id - 1))) ? 1 : 0;
}

int omap_dm_timer_reserve_systimer(int id)
{
	if (omap_dm_timer_reserved_systimer(id))
		return -ENODEV;

	omap_reserved_systimers |= (1 << (id - 1));

	return 0;
}

181 182
struct omap_dm_timer *omap_dm_timer_request(void)
{
183
	struct omap_dm_timer *timer = NULL, *t;
184
	unsigned long flags;
185
	int ret = 0;
186 187

	spin_lock_irqsave(&dm_timer_lock, flags);
188 189
	list_for_each_entry(t, &omap_timer_list, node) {
		if (t->reserved)
190 191
			continue;

192
		timer = t;
T
Timo Teras 已提交
193
		timer->reserved = 1;
194 195
		break;
	}
196
	spin_unlock_irqrestore(&dm_timer_lock, flags);
197 198 199 200 201 202 203 204

	if (timer) {
		ret = omap_dm_timer_prepare(timer);
		if (ret) {
			timer->reserved = 0;
			timer = NULL;
		}
	}
205

206 207
	if (!timer)
		pr_debug("%s: timer request failed!\n", __func__);
T
Timo Teras 已提交
208

209 210
	return timer;
}
211
EXPORT_SYMBOL_GPL(omap_dm_timer_request);
212 213

struct omap_dm_timer *omap_dm_timer_request_specific(int id)
214
{
215
	struct omap_dm_timer *timer = NULL, *t;
216
	unsigned long flags;
217
	int ret = 0;
218

219 220 221 222 223 224 225
	/* Requesting timer by ID is not supported when device tree is used */
	if (of_have_populated_dt()) {
		pr_warn("%s: Please use omap_dm_timer_request_by_cap()\n",
			__func__);
		return NULL;
	}

226
	spin_lock_irqsave(&dm_timer_lock, flags);
227 228 229 230 231 232
	list_for_each_entry(t, &omap_timer_list, node) {
		if (t->pdev->id == id && !t->reserved) {
			timer = t;
			timer->reserved = 1;
			break;
		}
233
	}
234
	spin_unlock_irqrestore(&dm_timer_lock, flags);
235

236 237 238 239 240 241 242
	if (timer) {
		ret = omap_dm_timer_prepare(timer);
		if (ret) {
			timer->reserved = 0;
			timer = NULL;
		}
	}
243

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

247
	return timer;
248
}
249
EXPORT_SYMBOL_GPL(omap_dm_timer_request_specific);
250

251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302
/**
 * omap_dm_timer_request_by_cap - Request a timer by capability
 * @cap:	Bit mask of capabilities to match
 *
 * Find a timer based upon capabilities bit mask. Callers of this function
 * should use the definitions found in the plat/dmtimer.h file under the
 * comment "timer capabilities used in hwmod database". Returns pointer to
 * timer handle on success and a NULL pointer on failure.
 */
struct omap_dm_timer *omap_dm_timer_request_by_cap(u32 cap)
{
	struct omap_dm_timer *timer = NULL, *t;
	unsigned long flags;

	if (!cap)
		return NULL;

	spin_lock_irqsave(&dm_timer_lock, flags);
	list_for_each_entry(t, &omap_timer_list, node) {
		if ((!t->reserved) && ((t->capability & cap) == cap)) {
			/*
			 * If timer is not NULL, we have already found one timer
			 * but it was not an exact match because it had more
			 * capabilites that what was required. Therefore,
			 * unreserve the last timer found and see if this one
			 * is a better match.
			 */
			if (timer)
				timer->reserved = 0;

			timer = t;
			timer->reserved = 1;

			/* Exit loop early if we find an exact match */
			if (t->capability == cap)
				break;
		}
	}
	spin_unlock_irqrestore(&dm_timer_lock, flags);

	if (timer && omap_dm_timer_prepare(timer)) {
		timer->reserved = 0;
		timer = NULL;
	}

	if (!timer)
		pr_debug("%s: timer request failed!\n", __func__);

	return timer;
}
EXPORT_SYMBOL_GPL(omap_dm_timer_request_by_cap);

303
int omap_dm_timer_free(struct omap_dm_timer *timer)
304
{
305 306 307
	if (unlikely(!timer))
		return -EINVAL;

308
	clk_put(timer->fclk);
309

310 311
	WARN_ON(!timer->reserved);
	timer->reserved = 0;
312
	return 0;
313
}
314
EXPORT_SYMBOL_GPL(omap_dm_timer_free);
315

316 317
void omap_dm_timer_enable(struct omap_dm_timer *timer)
{
318
	pm_runtime_get_sync(&timer->pdev->dev);
319
}
320
EXPORT_SYMBOL_GPL(omap_dm_timer_enable);
321 322 323

void omap_dm_timer_disable(struct omap_dm_timer *timer)
{
324
	pm_runtime_put_sync(&timer->pdev->dev);
325
}
326
EXPORT_SYMBOL_GPL(omap_dm_timer_disable);
327

328 329
int omap_dm_timer_get_irq(struct omap_dm_timer *timer)
{
330 331 332
	if (timer)
		return timer->irq;
	return -EINVAL;
333
}
334
EXPORT_SYMBOL_GPL(omap_dm_timer_get_irq);
335 336

#if defined(CONFIG_ARCH_OMAP1)
337
#include <mach/hardware.h>
338 339 340 341 342 343
/**
 * 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)
{
344 345 346
	int i = 0;
	struct omap_dm_timer *timer = NULL;
	unsigned long flags;
347 348 349 350 351 352

	/* 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 */
353 354
	spin_lock_irqsave(&dm_timer_lock, flags);
	list_for_each_entry(timer, &omap_timer_list, node) {
355 356
		u32 l;

357
		l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
358 359
		if (l & OMAP_TIMER_CTRL_ST) {
			if (((omap_readl(MOD_CONF_CTRL_1) >> (i * 2)) & 0x03) == 0)
360 361 362 363
				inputmask &= ~(1 << 1);
			else
				inputmask &= ~(1 << 2);
		}
364
		i++;
365
	}
366
	spin_unlock_irqrestore(&dm_timer_lock, flags);
367 368 369

	return inputmask;
}
370
EXPORT_SYMBOL_GPL(omap_dm_timer_modify_idlect_mask);
371

372
#else
373

374
struct clk *omap_dm_timer_get_fclk(struct omap_dm_timer *timer)
375
{
376 377 378
	if (timer)
		return timer->fclk;
	return NULL;
379
}
380
EXPORT_SYMBOL_GPL(omap_dm_timer_get_fclk);
381

382 383 384
__u32 omap_dm_timer_modify_idlect_mask(__u32 inputmask)
{
	BUG();
385 386

	return 0;
387
}
388
EXPORT_SYMBOL_GPL(omap_dm_timer_modify_idlect_mask);
389

390
#endif
391

392
int omap_dm_timer_trigger(struct omap_dm_timer *timer)
393
{
394 395 396
	if (unlikely(!timer || pm_runtime_suspended(&timer->pdev->dev))) {
		pr_err("%s: timer not available or enabled.\n", __func__);
		return -EINVAL;
397 398
	}

399
	omap_dm_timer_write_reg(timer, OMAP_TIMER_TRIGGER_REG, 0);
400
	return 0;
401
}
402
EXPORT_SYMBOL_GPL(omap_dm_timer_trigger);
403

404
int omap_dm_timer_start(struct omap_dm_timer *timer)
405 406
{
	u32 l;
407

408 409 410
	if (unlikely(!timer))
		return -EINVAL;

411 412
	omap_dm_timer_enable(timer);

413
	if (!(timer->capability & OMAP_TIMER_ALWON)) {
414 415
		if (timer->get_context_loss_count &&
			timer->get_context_loss_count(&timer->pdev->dev) !=
416
				timer->ctx_loss_count)
417 418 419
			omap_timer_restore_context(timer);
	}

420 421 422 423 424
	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);
	}
425 426 427

	/* Save the context */
	timer->context.tclr = l;
428
	return 0;
429
}
430
EXPORT_SYMBOL_GPL(omap_dm_timer_start);
431

432
int omap_dm_timer_stop(struct omap_dm_timer *timer)
433
{
434
	unsigned long rate = 0;
435

436 437 438
	if (unlikely(!timer))
		return -EINVAL;

439
	if (!(timer->capability & OMAP_TIMER_NEEDS_RESET))
440
		rate = clk_get_rate(timer->fclk);
441

442
	__omap_dm_timer_stop(timer, timer->posted, rate);
443

444 445 446 447 448
	if (!(timer->capability & OMAP_TIMER_ALWON)) {
		if (timer->get_context_loss_count)
			timer->ctx_loss_count =
				timer->get_context_loss_count(&timer->pdev->dev);
	}
449 450 451 452 453 454 455 456 457

	/*
	 * Since the register values are computed and written within
	 * __omap_dm_timer_stop, we need to use read to retrieve the
	 * context.
	 */
	timer->context.tclr =
			omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
	omap_dm_timer_disable(timer);
458
	return 0;
459
}
460
EXPORT_SYMBOL_GPL(omap_dm_timer_stop);
461

462
int omap_dm_timer_set_source(struct omap_dm_timer *timer, int source)
463
{
464
	int ret;
465
	char *parent_name = NULL;
466
	struct clk *parent;
467 468 469 470 471 472
	struct dmtimer_platform_data *pdata;

	if (unlikely(!timer))
		return -EINVAL;

	pdata = timer->pdev->dev.platform_data;
473

474
	if (source < 0 || source >= 3)
475
		return -EINVAL;
476

477 478 479 480 481
	/*
	 * FIXME: Used for OMAP1 devices only because they do not currently
	 * use the clock framework to set the parent clock. To be removed
	 * once OMAP1 migrated to using clock framework for dmtimers
	 */
482
	if (pdata && pdata->set_timer_src)
483 484
		return pdata->set_timer_src(timer->pdev, source);

485
	if (!timer->fclk)
486 487 488 489
		return -EINVAL;

	switch (source) {
	case OMAP_TIMER_SRC_SYS_CLK:
490
		parent_name = "timer_sys_ck";
491 492 493
		break;

	case OMAP_TIMER_SRC_32_KHZ:
494
		parent_name = "timer_32k_ck";
495 496 497
		break;

	case OMAP_TIMER_SRC_EXT_CLK:
498
		parent_name = "timer_ext_ck";
499 500 501 502 503 504
		break;
	}

	parent = clk_get(&timer->pdev->dev, parent_name);
	if (IS_ERR_OR_NULL(parent)) {
		pr_err("%s: %s not found\n", __func__, parent_name);
505
		return -EINVAL;
506 507
	}

508
	ret = clk_set_parent(timer->fclk, parent);
509 510 511 512 513
	if (IS_ERR_VALUE(ret))
		pr_err("%s: failed to set %s as parent\n", __func__,
			parent_name);

	clk_put(parent);
514 515

	return ret;
516
}
517
EXPORT_SYMBOL_GPL(omap_dm_timer_set_source);
518

519
int omap_dm_timer_set_load(struct omap_dm_timer *timer, int autoreload,
520
			    unsigned int load)
521 522
{
	u32 l;
523

524 525 526
	if (unlikely(!timer))
		return -EINVAL;

527
	omap_dm_timer_enable(timer);
528
	l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
529 530 531 532
	if (autoreload)
		l |= OMAP_TIMER_CTRL_AR;
	else
		l &= ~OMAP_TIMER_CTRL_AR;
533
	omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
534
	omap_dm_timer_write_reg(timer, OMAP_TIMER_LOAD_REG, load);
535

536
	omap_dm_timer_write_reg(timer, OMAP_TIMER_TRIGGER_REG, 0);
537 538 539 540
	/* Save the context */
	timer->context.tclr = l;
	timer->context.tldr = load;
	omap_dm_timer_disable(timer);
541
	return 0;
542
}
543
EXPORT_SYMBOL_GPL(omap_dm_timer_set_load);
544

545
/* Optimized set_load which removes costly spin wait in timer_start */
546
int omap_dm_timer_set_load_start(struct omap_dm_timer *timer, int autoreload,
547 548 549 550
                            unsigned int load)
{
	u32 l;

551 552 553
	if (unlikely(!timer))
		return -EINVAL;

554 555
	omap_dm_timer_enable(timer);

556
	if (!(timer->capability & OMAP_TIMER_ALWON)) {
557 558
		if (timer->get_context_loss_count &&
			timer->get_context_loss_count(&timer->pdev->dev) !=
559
				timer->ctx_loss_count)
560 561 562
			omap_timer_restore_context(timer);
	}

563
	l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
564
	if (autoreload) {
565
		l |= OMAP_TIMER_CTRL_AR;
566 567
		omap_dm_timer_write_reg(timer, OMAP_TIMER_LOAD_REG, load);
	} else {
568
		l &= ~OMAP_TIMER_CTRL_AR;
569
	}
570 571
	l |= OMAP_TIMER_CTRL_ST;

572
	__omap_dm_timer_load_start(timer, l, load, timer->posted);
573 574 575 576 577

	/* Save the context */
	timer->context.tclr = l;
	timer->context.tldr = load;
	timer->context.tcrr = load;
578
	return 0;
579
}
580
EXPORT_SYMBOL_GPL(omap_dm_timer_set_load_start);
581

582
int omap_dm_timer_set_match(struct omap_dm_timer *timer, int enable,
583
			     unsigned int match)
584 585 586
{
	u32 l;

587 588 589
	if (unlikely(!timer))
		return -EINVAL;

590
	omap_dm_timer_enable(timer);
591
	l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
T
Timo Teras 已提交
592
	if (enable)
593 594 595 596
		l |= OMAP_TIMER_CTRL_CE;
	else
		l &= ~OMAP_TIMER_CTRL_CE;
	omap_dm_timer_write_reg(timer, OMAP_TIMER_MATCH_REG, match);
597
	omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
598 599 600 601 602

	/* Save the context */
	timer->context.tclr = l;
	timer->context.tmar = match;
	omap_dm_timer_disable(timer);
603
	return 0;
604
}
605
EXPORT_SYMBOL_GPL(omap_dm_timer_set_match);
606

607
int omap_dm_timer_set_pwm(struct omap_dm_timer *timer, int def_on,
608
			   int toggle, int trigger)
609 610 611
{
	u32 l;

612 613 614
	if (unlikely(!timer))
		return -EINVAL;

615
	omap_dm_timer_enable(timer);
616
	l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
617 618 619 620 621 622 623
	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;
624
	omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
625 626 627 628

	/* Save the context */
	timer->context.tclr = l;
	omap_dm_timer_disable(timer);
629
	return 0;
630
}
631
EXPORT_SYMBOL_GPL(omap_dm_timer_set_pwm);
632

633
int omap_dm_timer_set_prescaler(struct omap_dm_timer *timer, int prescaler)
634 635 636
{
	u32 l;

637 638 639
	if (unlikely(!timer))
		return -EINVAL;

640
	omap_dm_timer_enable(timer);
641
	l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
642 643 644 645 646
	l &= ~(OMAP_TIMER_CTRL_PRE | (0x07 << 2));
	if (prescaler >= 0x00 && prescaler <= 0x07) {
		l |= OMAP_TIMER_CTRL_PRE;
		l |= prescaler << 2;
	}
647
	omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
648 649 650 651

	/* Save the context */
	timer->context.tclr = l;
	omap_dm_timer_disable(timer);
652
	return 0;
653
}
654
EXPORT_SYMBOL_GPL(omap_dm_timer_set_prescaler);
655

656
int omap_dm_timer_set_int_enable(struct omap_dm_timer *timer,
657
				  unsigned int value)
658
{
659 660 661
	if (unlikely(!timer))
		return -EINVAL;

662
	omap_dm_timer_enable(timer);
663
	__omap_dm_timer_int_enable(timer, value);
664 665 666 667 668

	/* Save the context */
	timer->context.tier = value;
	timer->context.twer = value;
	omap_dm_timer_disable(timer);
669
	return 0;
670
}
671
EXPORT_SYMBOL_GPL(omap_dm_timer_set_int_enable);
672

673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703
/**
 * omap_dm_timer_set_int_disable - disable timer interrupts
 * @timer:	pointer to timer handle
 * @mask:	bit mask of interrupts to be disabled
 *
 * Disables the specified timer interrupts for a timer.
 */
int omap_dm_timer_set_int_disable(struct omap_dm_timer *timer, u32 mask)
{
	u32 l = mask;

	if (unlikely(!timer))
		return -EINVAL;

	omap_dm_timer_enable(timer);

	if (timer->revision == 1)
		l = __raw_readl(timer->irq_ena) & ~mask;

	__raw_writel(l, timer->irq_dis);
	l = omap_dm_timer_read_reg(timer, OMAP_TIMER_WAKEUP_EN_REG) & ~mask;
	omap_dm_timer_write_reg(timer, OMAP_TIMER_WAKEUP_EN_REG, l);

	/* Save the context */
	timer->context.tier &= ~mask;
	timer->context.twer &= ~mask;
	omap_dm_timer_disable(timer);
	return 0;
}
EXPORT_SYMBOL_GPL(omap_dm_timer_set_int_disable);

704
unsigned int omap_dm_timer_read_status(struct omap_dm_timer *timer)
705
{
706 707
	unsigned int l;

708 709
	if (unlikely(!timer || pm_runtime_suspended(&timer->pdev->dev))) {
		pr_err("%s: timer not available or enabled.\n", __func__);
710 711 712
		return 0;
	}

713
	l = __raw_readl(timer->irq_stat);
714 715

	return l;
716
}
717
EXPORT_SYMBOL_GPL(omap_dm_timer_read_status);
718

719
int omap_dm_timer_write_status(struct omap_dm_timer *timer, unsigned int value)
720
{
721 722 723
	if (unlikely(!timer || pm_runtime_suspended(&timer->pdev->dev)))
		return -EINVAL;

724
	__omap_dm_timer_write_status(timer, value);
725

726
	return 0;
727
}
728
EXPORT_SYMBOL_GPL(omap_dm_timer_write_status);
729

730
unsigned int omap_dm_timer_read_counter(struct omap_dm_timer *timer)
731
{
732 733
	if (unlikely(!timer || pm_runtime_suspended(&timer->pdev->dev))) {
		pr_err("%s: timer not iavailable or enabled.\n", __func__);
734 735 736
		return 0;
	}

737
	return __omap_dm_timer_read_counter(timer, timer->posted);
738
}
739
EXPORT_SYMBOL_GPL(omap_dm_timer_read_counter);
740

741
int omap_dm_timer_write_counter(struct omap_dm_timer *timer, unsigned int value)
T
Timo Teras 已提交
742
{
743 744 745
	if (unlikely(!timer || pm_runtime_suspended(&timer->pdev->dev))) {
		pr_err("%s: timer not available or enabled.\n", __func__);
		return -EINVAL;
746 747
	}

748
	omap_dm_timer_write_reg(timer, OMAP_TIMER_COUNTER_REG, value);
749 750 751

	/* Save the context */
	timer->context.tcrr = value;
752
	return 0;
T
Timo Teras 已提交
753
}
754
EXPORT_SYMBOL_GPL(omap_dm_timer_write_counter);
T
Timo Teras 已提交
755

756
int omap_dm_timers_active(void)
757
{
758
	struct omap_dm_timer *timer;
759

760
	list_for_each_entry(timer, &omap_timer_list, node) {
761
		if (!timer->reserved)
762 763
			continue;

764
		if (omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG) &
765
		    OMAP_TIMER_CTRL_ST) {
766
			return 1;
767
		}
768 769 770
	}
	return 0;
}
771
EXPORT_SYMBOL_GPL(omap_dm_timers_active);
772

773 774 775 776 777 778 779 780 781 782 783
/**
 * 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)
{
	unsigned long flags;
	struct omap_dm_timer *timer;
784 785
	struct resource *mem, *irq;
	struct device *dev = &pdev->dev;
786 787
	struct dmtimer_platform_data *pdata = pdev->dev.platform_data;

788
	if (!pdata && !dev->of_node) {
789
		dev_err(dev, "%s: no platform data.\n", __func__);
790 791 792 793 794
		return -ENODEV;
	}

	irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
	if (unlikely(!irq)) {
795
		dev_err(dev, "%s: no IRQ resource.\n", __func__);
796 797 798 799 800
		return -ENODEV;
	}

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

805
	timer = devm_kzalloc(dev, sizeof(struct omap_dm_timer), GFP_KERNEL);
806
	if (!timer) {
807 808
		dev_err(dev, "%s: memory alloc failed!\n", __func__);
		return  -ENOMEM;
809 810
	}

811
	timer->io_base = devm_request_and_ioremap(dev, mem);
812
	if (!timer->io_base) {
813 814
		dev_err(dev, "%s: region already claimed.\n", __func__);
		return -ENOMEM;
815 816
	}

817 818 819 820 821 822 823 824 825 826 827
	if (dev->of_node) {
		if (of_find_property(dev->of_node, "ti,timer-alwon", NULL))
			timer->capability |= OMAP_TIMER_ALWON;
		if (of_find_property(dev->of_node, "ti,timer-dsp", NULL))
			timer->capability |= OMAP_TIMER_HAS_DSP_IRQ;
		if (of_find_property(dev->of_node, "ti,timer-pwm", NULL))
			timer->capability |= OMAP_TIMER_HAS_PWM;
		if (of_find_property(dev->of_node, "ti,timer-secure", NULL))
			timer->capability |= OMAP_TIMER_SECURE;
	} else {
		timer->id = pdev->id;
828
		timer->errata = pdata->timer_errata;
829 830
		timer->capability = pdata->timer_capability;
		timer->reserved = omap_dm_timer_reserved_systimer(timer->id);
831
		timer->get_context_loss_count = pdata->get_context_loss_count;
832 833
	}

834 835 836
	timer->irq = irq->start;
	timer->pdev = pdev;

837
	/* Skip pm_runtime_enable for OMAP1 */
838
	if (!(timer->capability & OMAP_TIMER_NEEDS_RESET)) {
839 840
		pm_runtime_enable(dev);
		pm_runtime_irq_safe(dev);
841 842
	}

843
	if (!timer->reserved) {
844
		pm_runtime_get_sync(dev);
845
		__omap_dm_timer_init_regs(timer);
846
		pm_runtime_put(dev);
847 848
	}

849 850 851 852 853
	/* 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);

854
	dev_dbg(dev, "Device Probed.\n");
855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874

	return 0;
}

/**
 * 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)
875 876
		if (!strcmp(dev_name(&timer->pdev->dev),
			    dev_name(&pdev->dev))) {
877 878 879 880 881 882 883 884 885
			list_del(&timer->node);
			ret = 0;
			break;
		}
	spin_unlock_irqrestore(&dm_timer_lock, flags);

	return ret;
}

886 887 888 889 890 891
static const struct of_device_id omap_timer_match[] = {
	{ .compatible = "ti,omap2-timer", },
	{},
};
MODULE_DEVICE_TABLE(of, omap_timer_match);

892 893
static struct platform_driver omap_dm_timer_driver = {
	.probe  = omap_dm_timer_probe,
894
	.remove = __devexit_p(omap_dm_timer_remove),
895 896
	.driver = {
		.name   = "omap_timer",
897
		.of_match_table = of_match_ptr(omap_timer_match),
898 899 900 901
	},
};

early_platform_init("earlytimer", &omap_dm_timer_driver);
902
module_platform_driver(omap_dm_timer_driver);
903 904 905 906 907

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