dmtimer.c 22.5 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 319
	int c;

320
	pm_runtime_get_sync(&timer->pdev->dev);
321 322 323 324 325 326 327 328

	if (!(timer->capability & OMAP_TIMER_ALWON)) {
		if (timer->get_context_loss_count) {
			c = timer->get_context_loss_count(&timer->pdev->dev);
			if (c != timer->ctx_loss_count) {
				omap_timer_restore_context(timer);
				timer->ctx_loss_count = c;
			}
329 330
		} else {
			omap_timer_restore_context(timer);
331 332
		}
	}
333
}
334
EXPORT_SYMBOL_GPL(omap_dm_timer_enable);
335 336 337

void omap_dm_timer_disable(struct omap_dm_timer *timer)
{
338
	pm_runtime_put_sync(&timer->pdev->dev);
339
}
340
EXPORT_SYMBOL_GPL(omap_dm_timer_disable);
341

342 343
int omap_dm_timer_get_irq(struct omap_dm_timer *timer)
{
344 345 346
	if (timer)
		return timer->irq;
	return -EINVAL;
347
}
348
EXPORT_SYMBOL_GPL(omap_dm_timer_get_irq);
349 350

#if defined(CONFIG_ARCH_OMAP1)
351
#include <mach/hardware.h>
352 353 354 355 356 357
/**
 * 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)
{
358 359 360
	int i = 0;
	struct omap_dm_timer *timer = NULL;
	unsigned long flags;
361 362 363 364 365 366

	/* 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 */
367 368
	spin_lock_irqsave(&dm_timer_lock, flags);
	list_for_each_entry(timer, &omap_timer_list, node) {
369 370
		u32 l;

371
		l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
372 373
		if (l & OMAP_TIMER_CTRL_ST) {
			if (((omap_readl(MOD_CONF_CTRL_1) >> (i * 2)) & 0x03) == 0)
374 375 376 377
				inputmask &= ~(1 << 1);
			else
				inputmask &= ~(1 << 2);
		}
378
		i++;
379
	}
380
	spin_unlock_irqrestore(&dm_timer_lock, flags);
381 382 383

	return inputmask;
}
384
EXPORT_SYMBOL_GPL(omap_dm_timer_modify_idlect_mask);
385

386
#else
387

388
struct clk *omap_dm_timer_get_fclk(struct omap_dm_timer *timer)
389
{
390 391 392
	if (timer)
		return timer->fclk;
	return NULL;
393
}
394
EXPORT_SYMBOL_GPL(omap_dm_timer_get_fclk);
395

396 397 398
__u32 omap_dm_timer_modify_idlect_mask(__u32 inputmask)
{
	BUG();
399 400

	return 0;
401
}
402
EXPORT_SYMBOL_GPL(omap_dm_timer_modify_idlect_mask);
403

404
#endif
405

406
int omap_dm_timer_trigger(struct omap_dm_timer *timer)
407
{
408 409 410
	if (unlikely(!timer || pm_runtime_suspended(&timer->pdev->dev))) {
		pr_err("%s: timer not available or enabled.\n", __func__);
		return -EINVAL;
411 412
	}

413
	omap_dm_timer_write_reg(timer, OMAP_TIMER_TRIGGER_REG, 0);
414
	return 0;
415
}
416
EXPORT_SYMBOL_GPL(omap_dm_timer_trigger);
417

418
int omap_dm_timer_start(struct omap_dm_timer *timer)
419 420
{
	u32 l;
421

422 423 424
	if (unlikely(!timer))
		return -EINVAL;

425 426
	omap_dm_timer_enable(timer);

427 428 429 430 431
	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);
	}
432 433 434

	/* Save the context */
	timer->context.tclr = l;
435
	return 0;
436
}
437
EXPORT_SYMBOL_GPL(omap_dm_timer_start);
438

439
int omap_dm_timer_stop(struct omap_dm_timer *timer)
440
{
441
	unsigned long rate = 0;
442

443 444 445
	if (unlikely(!timer))
		return -EINVAL;

446
	if (!(timer->capability & OMAP_TIMER_NEEDS_RESET))
447
		rate = clk_get_rate(timer->fclk);
448

449
	__omap_dm_timer_stop(timer, timer->posted, rate);
450

451 452 453 454 455 456 457 458
	/*
	 * 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);
459
	return 0;
460
}
461
EXPORT_SYMBOL_GPL(omap_dm_timer_stop);
462

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

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

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

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

478 479 480 481 482
	/*
	 * 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
	 */
483
	if (pdata && pdata->set_timer_src)
484 485
		return pdata->set_timer_src(timer->pdev, source);

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

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

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

	case OMAP_TIMER_SRC_EXT_CLK:
499
		parent_name = "timer_ext_ck";
500 501 502 503 504 505
		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);
506
		return -EINVAL;
507 508
	}

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

	clk_put(parent);
515 516

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

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

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

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

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

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

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

555 556
	omap_dm_timer_enable(timer);

557
	l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
558
	if (autoreload) {
559
		l |= OMAP_TIMER_CTRL_AR;
560 561
		omap_dm_timer_write_reg(timer, OMAP_TIMER_LOAD_REG, load);
	} else {
562
		l &= ~OMAP_TIMER_CTRL_AR;
563
	}
564 565
	l |= OMAP_TIMER_CTRL_ST;

566
	__omap_dm_timer_load_start(timer, l, load, timer->posted);
567 568 569 570 571

	/* Save the context */
	timer->context.tclr = l;
	timer->context.tldr = load;
	timer->context.tcrr = load;
572
	return 0;
573
}
574
EXPORT_SYMBOL_GPL(omap_dm_timer_set_load_start);
575

576
int omap_dm_timer_set_match(struct omap_dm_timer *timer, int enable,
577
			     unsigned int match)
578 579 580
{
	u32 l;

581 582 583
	if (unlikely(!timer))
		return -EINVAL;

584
	omap_dm_timer_enable(timer);
585
	l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
T
Timo Teras 已提交
586
	if (enable)
587 588 589 590
		l |= OMAP_TIMER_CTRL_CE;
	else
		l &= ~OMAP_TIMER_CTRL_CE;
	omap_dm_timer_write_reg(timer, OMAP_TIMER_MATCH_REG, match);
591
	omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
592 593 594 595 596

	/* Save the context */
	timer->context.tclr = l;
	timer->context.tmar = match;
	omap_dm_timer_disable(timer);
597
	return 0;
598
}
599
EXPORT_SYMBOL_GPL(omap_dm_timer_set_match);
600

601
int omap_dm_timer_set_pwm(struct omap_dm_timer *timer, int def_on,
602
			   int toggle, int trigger)
603 604 605
{
	u32 l;

606 607 608
	if (unlikely(!timer))
		return -EINVAL;

609
	omap_dm_timer_enable(timer);
610
	l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
611 612 613 614 615 616 617
	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;
618
	omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
619 620 621 622

	/* Save the context */
	timer->context.tclr = l;
	omap_dm_timer_disable(timer);
623
	return 0;
624
}
625
EXPORT_SYMBOL_GPL(omap_dm_timer_set_pwm);
626

627
int omap_dm_timer_set_prescaler(struct omap_dm_timer *timer, int prescaler)
628 629 630
{
	u32 l;

631 632 633
	if (unlikely(!timer))
		return -EINVAL;

634
	omap_dm_timer_enable(timer);
635
	l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
636 637 638 639 640
	l &= ~(OMAP_TIMER_CTRL_PRE | (0x07 << 2));
	if (prescaler >= 0x00 && prescaler <= 0x07) {
		l |= OMAP_TIMER_CTRL_PRE;
		l |= prescaler << 2;
	}
641
	omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
642 643 644 645

	/* Save the context */
	timer->context.tclr = l;
	omap_dm_timer_disable(timer);
646
	return 0;
647
}
648
EXPORT_SYMBOL_GPL(omap_dm_timer_set_prescaler);
649

650
int omap_dm_timer_set_int_enable(struct omap_dm_timer *timer,
651
				  unsigned int value)
652
{
653 654 655
	if (unlikely(!timer))
		return -EINVAL;

656
	omap_dm_timer_enable(timer);
657
	__omap_dm_timer_int_enable(timer, value);
658 659 660 661 662

	/* Save the context */
	timer->context.tier = value;
	timer->context.twer = value;
	omap_dm_timer_disable(timer);
663
	return 0;
664
}
665
EXPORT_SYMBOL_GPL(omap_dm_timer_set_int_enable);
666

667 668 669 670 671 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
/**
 * 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);

698
unsigned int omap_dm_timer_read_status(struct omap_dm_timer *timer)
699
{
700 701
	unsigned int l;

702 703
	if (unlikely(!timer || pm_runtime_suspended(&timer->pdev->dev))) {
		pr_err("%s: timer not available or enabled.\n", __func__);
704 705 706
		return 0;
	}

707
	l = __raw_readl(timer->irq_stat);
708 709

	return l;
710
}
711
EXPORT_SYMBOL_GPL(omap_dm_timer_read_status);
712

713
int omap_dm_timer_write_status(struct omap_dm_timer *timer, unsigned int value)
714
{
715 716 717
	if (unlikely(!timer || pm_runtime_suspended(&timer->pdev->dev)))
		return -EINVAL;

718
	__omap_dm_timer_write_status(timer, value);
719

720
	return 0;
721
}
722
EXPORT_SYMBOL_GPL(omap_dm_timer_write_status);
723

724
unsigned int omap_dm_timer_read_counter(struct omap_dm_timer *timer)
725
{
726 727
	if (unlikely(!timer || pm_runtime_suspended(&timer->pdev->dev))) {
		pr_err("%s: timer not iavailable or enabled.\n", __func__);
728 729 730
		return 0;
	}

731
	return __omap_dm_timer_read_counter(timer, timer->posted);
732
}
733
EXPORT_SYMBOL_GPL(omap_dm_timer_read_counter);
734

735
int omap_dm_timer_write_counter(struct omap_dm_timer *timer, unsigned int value)
T
Timo Teras 已提交
736
{
737 738 739
	if (unlikely(!timer || pm_runtime_suspended(&timer->pdev->dev))) {
		pr_err("%s: timer not available or enabled.\n", __func__);
		return -EINVAL;
740 741
	}

742
	omap_dm_timer_write_reg(timer, OMAP_TIMER_COUNTER_REG, value);
743 744 745

	/* Save the context */
	timer->context.tcrr = value;
746
	return 0;
T
Timo Teras 已提交
747
}
748
EXPORT_SYMBOL_GPL(omap_dm_timer_write_counter);
T
Timo Teras 已提交
749

750
int omap_dm_timers_active(void)
751
{
752
	struct omap_dm_timer *timer;
753

754
	list_for_each_entry(timer, &omap_timer_list, node) {
755
		if (!timer->reserved)
756 757
			continue;

758
		if (omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG) &
759
		    OMAP_TIMER_CTRL_ST) {
760
			return 1;
761
		}
762 763 764
	}
	return 0;
}
765
EXPORT_SYMBOL_GPL(omap_dm_timers_active);
766

767 768 769 770 771 772 773
/**
 * 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.
 */
774
static int omap_dm_timer_probe(struct platform_device *pdev)
775 776 777
{
	unsigned long flags;
	struct omap_dm_timer *timer;
778 779
	struct resource *mem, *irq;
	struct device *dev = &pdev->dev;
780 781
	struct dmtimer_platform_data *pdata = pdev->dev.platform_data;

782
	if (!pdata && !dev->of_node) {
783
		dev_err(dev, "%s: no platform data.\n", __func__);
784 785 786 787 788
		return -ENODEV;
	}

	irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
	if (unlikely(!irq)) {
789
		dev_err(dev, "%s: no IRQ resource.\n", __func__);
790 791 792 793 794
		return -ENODEV;
	}

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

799
	timer = devm_kzalloc(dev, sizeof(struct omap_dm_timer), GFP_KERNEL);
800
	if (!timer) {
801 802
		dev_err(dev, "%s: memory alloc failed!\n", __func__);
		return  -ENOMEM;
803 804
	}

805 806 807
	timer->io_base = devm_ioremap_resource(dev, mem);
	if (IS_ERR(timer->io_base))
		return PTR_ERR(timer->io_base);
808

809 810 811 812 813 814 815 816 817 818 819
	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;
820
		timer->errata = pdata->timer_errata;
821 822
		timer->capability = pdata->timer_capability;
		timer->reserved = omap_dm_timer_reserved_systimer(timer->id);
823
		timer->get_context_loss_count = pdata->get_context_loss_count;
824 825
	}

826 827 828
	timer->irq = irq->start;
	timer->pdev = pdev;

829
	/* Skip pm_runtime_enable for OMAP1 */
830
	if (!(timer->capability & OMAP_TIMER_NEEDS_RESET)) {
831 832
		pm_runtime_enable(dev);
		pm_runtime_irq_safe(dev);
833 834
	}

835
	if (!timer->reserved) {
836
		pm_runtime_get_sync(dev);
837
		__omap_dm_timer_init_regs(timer);
838
		pm_runtime_put(dev);
839 840
	}

841 842 843 844 845
	/* 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);

846
	dev_dbg(dev, "Device Probed.\n");
847 848 849 850 851 852 853 854 855 856 857 858

	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.
 */
859
static int omap_dm_timer_remove(struct platform_device *pdev)
860 861 862 863 864 865 866
{
	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)
867 868
		if (!strcmp(dev_name(&timer->pdev->dev),
			    dev_name(&pdev->dev))) {
869 870 871 872 873 874 875 876 877
			list_del(&timer->node);
			ret = 0;
			break;
		}
	spin_unlock_irqrestore(&dm_timer_lock, flags);

	return ret;
}

878 879 880 881 882 883
static const struct of_device_id omap_timer_match[] = {
	{ .compatible = "ti,omap2-timer", },
	{},
};
MODULE_DEVICE_TABLE(of, omap_timer_match);

884 885
static struct platform_driver omap_dm_timer_driver = {
	.probe  = omap_dm_timer_probe,
886
	.remove = omap_dm_timer_remove,
887 888
	.driver = {
		.name   = "omap_timer",
889
		.of_match_table = of_match_ptr(omap_timer_match),
890 891 892 893
	},
};

early_platform_init("earlytimer", &omap_dm_timer_driver);
894
module_platform_driver(omap_dm_timer_driver);
895 896 897 898 899

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