summaryrefslogtreecommitdiffstats
path: root/drivers/clocksource/nomadik.c
blob: 8a3e6d930e3790ad36e88e3feb5dee61199a17ee (plain)
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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
/*
 * linux/arch/arm/mach-nomadik/timer.c
 *
 * Copyright (C) 2008 STMicroelectronics
 * Copyright (C) 2009 Alessandro Rubini, somewhat based on at91sam926x
 *
 * 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 <common.h>
#include <clock.h>
#include <io.h>
#include <init.h>
#include <driver.h>
#include <errno.h>
#include <linux/clk.h>
#include <linux/err.h>

/*
 * The MTU device hosts four different counters, with 4 set of
 * registers. These are register names.
 */

#define MTU_IMSC	0x00	/* Interrupt mask set/clear */
#define MTU_RIS		0x04	/* Raw interrupt status */
#define MTU_MIS		0x08	/* Masked interrupt status */
#define MTU_ICR		0x0C	/* Interrupt clear register */

/* per-timer registers take 0..3 as argument */
#define MTU_LR(x)	(0x10 + 0x10 * (x) + 0x00)	/* Load value */
#define MTU_VAL(x)	(0x10 + 0x10 * (x) + 0x04)	/* Current value */
#define MTU_CR(x)	(0x10 + 0x10 * (x) + 0x08)	/* Control reg */
#define MTU_BGLR(x)	(0x10 + 0x10 * (x) + 0x0c)	/* At next overflow */

/* bits for the control register */
#define MTU_CRn_ENA		0x80
#define MTU_CRn_PERIODIC	0x40	/* if 0 = free-running */
#define MTU_CRn_PRESCALE_MASK	0x0c
#define MTU_CRn_PRESCALE_1		0x00
#define MTU_CRn_PRESCALE_16		0x04
#define MTU_CRn_PRESCALE_256		0x08
#define MTU_CRn_32BITS		0x02
#define MTU_CRn_ONESHOT		0x01	/* if 0 = wraps reloading from BGLR*/

/* Other registers are usual amba/primecell registers, currently not used */
#define MTU_ITCR	0xff0
#define MTU_ITOP	0xff4

#define MTU_PERIPH_ID0	0xfe0
#define MTU_PERIPH_ID1	0xfe4
#define MTU_PERIPH_ID2	0xfe8
#define MTU_PERIPH_ID3	0xfeC

#define MTU_PCELL0	0xff0
#define MTU_PCELL1	0xff4
#define MTU_PCELL2	0xff8
#define MTU_PCELL3	0xffC

static __iomem void *mtu_base;
static u32 clk_prescale;
static u32 nmdk_cycle;		/* write-once */

/*
 * clocksource: the MTU device is a decrementing counters, so we negate
 * the value being read.
 */
static uint64_t nmdk_read_timer(void)
{
	return nmdk_cycle - readl(mtu_base + MTU_VAL(0));
}

static struct clocksource nmdk_clksrc = {
	.read	= nmdk_read_timer,
	.shift	= 20,
	.mask	= CLOCKSOURCE_MASK(32),
};

static void nmdk_timer_reset(void)
{
	u32 cr;

	/* Disable */
	writel(0, mtu_base + MTU_CR(0)); /* off */

	/* configure load and background-load, and fire it up */
	writel(nmdk_cycle, mtu_base + MTU_LR(0));
	writel(nmdk_cycle, mtu_base + MTU_BGLR(0));

	cr = clk_prescale | MTU_CRn_32BITS;
	writel(cr, mtu_base + MTU_CR(0));
	writel(cr | MTU_CRn_ENA, mtu_base + MTU_CR(0));
}

static int nmdk_mtu_probe(struct device_d *dev)
{
	static struct clk *mtu_clk;
	u32 rate;
	int ret;

	mtu_clk = clk_get(dev, NULL);
	if (IS_ERR(mtu_clk)) {
		ret = PTR_ERR(mtu_clk);
		dev_err(dev, "clock not found: %d\n", ret);
		return ret;
	}

	ret = clk_enable(mtu_clk);
	if (ret < 0) {
		dev_err(dev, "clock failed to enable: %d\n", ret);
		clk_put(mtu_clk);
		return ret;
	}

	rate = clk_get_rate(mtu_clk);
	if (rate > 32000000) {
		rate /= 16;
		clk_prescale = MTU_CRn_PRESCALE_16;
	} else {
		clk_prescale = MTU_CRn_PRESCALE_1;
	}

	nmdk_cycle = (rate + 1000 / 2) / 1000;

	/* Save global pointer to mtu, used by functions above */
	mtu_base = dev_request_mem_region(dev, 0);

	/* Init the timer and register clocksource */
	nmdk_timer_reset();

	nmdk_clksrc.mult = clocksource_hz2mult(rate, nmdk_clksrc.shift);

	init_clock(&nmdk_clksrc);

	return 0;
}

static struct driver_d nmdk_mtu_driver = {
	.name = "nomadik_mtu",
	.probe = nmdk_mtu_probe,
};

static int nmdk_mtu_init(void)
{
	return platform_driver_register(&nmdk_mtu_driver);
}
coredevice_initcall(nmdk_mtu_init);