summaryrefslogtreecommitdiff
path: root/sys/arch/beagle/dev/prcm.c
blob: 6fb56f3648728f9786b3efff4372959be6c73498 (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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
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
245
246
247
248
249
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
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
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
/* $OpenBSD: prcm.c,v 1.5 2011/03/14 15:16:46 jasper Exp $ */
/*
 * Copyright (c) 2007,2009 Dale Rahn <drahn@openbsd.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/*
 * Driver for the Power, Reset and Clock Management Module (PRCM).
 */

#include <sys/types.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/time.h>
#include <sys/device.h>

#include <machine/bus.h>
#include <machine/intr.h>
#include <arm/cpufunc.h>
#include <beagle/beagle/ahb.h>
#include <beagle/dev/prcmvar.h>

#define CM_FCLKEN_IVA2		0x0000
#define CM_CLKEN_PLL_IVA2	0x0004
#define CM_IDLEST_IVA2		0x0020
#define CM_IDLEST_PLL_IVA2	0x0024
#define CM_AUTOIDLE_PLL_IVA2	0x0034
#define CM_CLKSEL1_PLL_IVA2	0x0040
#define CM_CLKSEL2_PLL_IVA2	0x0044
#define CM_CLKSTCTRL_IVA2	0x0048
#define CM_CLKSTST_IVA2		0x004c

#define PRCM_REVISION		0x0800
#define PRCM_SYSCONFIG		0x0810

#define	CM_CLKSEL_MPU		0x0940
#define CM_CLKSTCTRL_MPU	0x0948
#define RM_RSTST_MPU		0x0958
#define PM_WKDEP_MPU		0x09C8
#define PM_EVGENCTRL_MPU	0x09D4
#define PM_EVEGENONTIM_MPU	0x09D8
#define PM_EVEGENOFFTIM_MPU	0x09DC
#define PM_PWSTCTRL_MPU		0x09E0
#define PM_PWSTST_MPU		0x09E4

#define  CM_ICLKEN1_CORE	0x0a10
#define  CM_ICLKEN1_CORE_MSK	0x7ffffed2
#define  CM_ICLKEN2_CORE	0x0a14
#define  CM_ICLKEN2_CORE_MSK	0x0000001f
#define  CM_ICLKEN3_CORE	0x0a18
#define  CM_ICLKEN3_CORE_MSK	0x00000004
#define  CM_ICLKEN4_CORE	0x0a1C
#define  CM_IDLEST1_CORE	0x0a20
#define  CM_IDLEST2_CORE	0x0a24
#define  CM_IDLEST4_CORE	0x0a2C
#define  CM_AUTOIDLE1_CORE	0x0a30
#define  CM_AUTOIDLE2_CORE	0x0a34
#define  CM_AUTOIDLE3_CORE	0x0a38
#define  CM_AUTOIDLE4_CORE	0x0a3C
#define  CM_CLKSEL1_CORE	0x0a40
#define  CM_CLKSEL2_CORE	0x0a44
#define  CM_CLKSTCTRL_CORE	0x0a48
#define  PM_WKEN1_CORE		0x0aA0
#define  PM_WKEN2_CORE		0x0aA4
#define  PM_WKST1_CORE		0x0aB0
#define  PM_WKST2_CORE		0x0aB4
#define  PM_WKDEP_CORE		0x0aC8
#define  PM_PWSTCTRL_CORE	0x0aE0
#define  PM_PWSTST_CORE		0x0aE4
#define  CM_FCLKEN_GFX		0x0b00
#define  CM_ICLKEN_GFX		0x0b10

#define CM_IDLEST_GFX		0x0b20
#define CM_CLKSEL_GFX		0x0b40
#define CM_CLKSTCTRL_GFX	0x0b48
#define RM_RSTCTRL_GFX		0x0b50
#define RM_RSTST_GFX		0x0b58
#define PM_WKDEP_GFX		0x0bC8
#define PM_PWSTCTRL_GFX		0x0bE0
#define PM_PWSTST_GFX		0x0bE4
#define CM_FCLKEN_WKUP		0x0c00
#define		CM_FCLKEN_WKUP_GPT1	1
#define		CM_FCLKEN_WKUP_GPIOS	4
#define		CM_FCLKEN_WKUP_MPU_WDT	8
#define CM_ICLKEN_WKUP		0xc10
#define		CM_ICLKEN_WKUP_GPT1	0x01
#define		CM_ICLKEN_WKUP_32KSYNC	0x02
#define		CM_ICLKEN_WKUP_GPIOS	0x04
#define		CM_ICLKEN_WKUP_MPU_WDT	0x08
#define		CM_ICLKEN_WKUP_WDT1	0x10
#define		CM_ICLKEN_WKUP_OMAPCTRL	0x20
#define	CM_IDLEST_WKUP		0x0c20
#define CM_AUTOIDLE_WKUP	0x0c30
#define CM_CLKSEL_WKUP		0x0c40
#define RM_RSTCTRL_WKUP		0x0c50
#define RM_RSTTIME_WKUP		0x0c54
#define RM_RSTST_WKUP		0x0c58
#define PM_WKEN_WKUP		0x0cA0
#define PM_WKST_WKUP		0x0cB0
#define CM_CLKEN_PLL		0x0d00
#define CM_CLKEN2_PLL		0x0d04
#define CM_IDLEST_CKGEN		0x0d20
#define CM_AUTOIDLE_PLL		0x0d30
#define CM_AUTOIDLE2_PLL	0x0d34
#define CM_CLKSEL1_PLL		0x0d40
#define CM_CLKSEL2_PLL		0x0d44
#define CM_CLKSEL3_PLL		0x0d48
#define CM_CLKSEL4_PLL		0x0d4C
#define CM_CLKSEL5_PLL		0x0d50
#define CM_FCLKEN_PER		0x1000
#define CM_ICLKEN_PER		0x1010
#define CM_IDLEST_PER		0x1020
#define CM_AUTOIDLE_PER		0x1030
#define CM_CLKSEL_PER		0x1040
#define CM_SLEEPDEP_PER		0x1044
#define CM_CLKSTCTRL_PER	0x1048
#define CM_CLKSTST_PER		0x104C

#define CM_CLKSEL1_EMU		0x5140
#define CM_CLKSTCTRL_EMU	0x5148
#define CM_CLKSTST_EMU		0x514C
#define CM_CLKSEL2_EMU		0x5150
#define CM_CLKSEL3_EMU		0x5154

#define CM_POLCTRL		0x529C

#define CM_IDLEST_NEON		0x5320
#define CM_CLKSTCTRL_NEON	0x5348

#define CM_FCLKEN_USBHOST	0x5400
#define CM_ICLKEN_USBHOST	0x5410
#define CM_IDLEST_USBHOST	0x5420
#define CM_AUTOIDLE_USBHOST	0x5430
#define CM_SLEEPDEP_USBHOST	0x5444
#define CM_CLKSTCTRL_USBHOST	0x5448
#define CM_CLKSTST_USBHOST	0x544C

uint32_t prcm_imask_cur[PRCM_REG_MAX];
uint32_t prcm_fmask_cur[PRCM_REG_MAX];
uint32_t prcm_imask_mask[PRCM_REG_MAX];
uint32_t prcm_fmask_mask[PRCM_REG_MAX];
uint32_t prcm_imask_addr[PRCM_REG_MAX];
uint32_t prcm_fmask_addr[PRCM_REG_MAX];

#define PRCM_SIZE	0x2000

#define SYS_CLK		13 /* SYS_CLK speed in MHz */

bus_space_tag_t		prcm_iot;
bus_space_handle_t	prcm_ioh;
int prcm_attached;

int     prcm_match(struct device *, void *, void *);
void    prcm_attach(struct device *, struct device *, void *);
int	prcm_setup_dpll5(void);

struct cfattach	prcm_ca = {
	sizeof (struct device), prcm_match, prcm_attach
};

struct cfdriver prcm_cd = {
	NULL, "prcm", DV_DULL
};

int
prcm_match(struct device *parent, void *v, void *aux)
{
	/* only attach once */
	if (prcm_attached != 0)
		return (0);
	return (1);
}

void
prcm_attach(struct device *parent, struct device *self, void *args)
{
        struct ahb_attach_args *aa = args;
	prcm_iot = aa->aa_iot;
	u_int32_t reg;

	if (bus_space_map(prcm_iot, aa->aa_addr, PRCM_SIZE, 0, &prcm_ioh))
		panic("prcm_attach: bus_space_map failed!");

	reg = bus_space_read_4(prcm_iot, prcm_ioh, PRCM_REVISION);
	printf(" rev %d.%d\n", reg >> 4 & 0xf, reg & 0xf);
	
	prcm_attached = 1;

	/* Setup the 120MHZ DPLL5 clock, to be used by USB. */
	prcm_setup_dpll5();

	/* XXX */
#if 1
	printf("CM_FCLKEN1_CORE %x\n", bus_space_read_4(prcm_iot, prcm_ioh, CM_FCLKEN1_CORE));
	printf("CM_ICLKEN1_CORE %x\n", bus_space_read_4(prcm_iot, prcm_ioh, CM_ICLKEN1_CORE));
	printf("CM_AUTOIDLE1_CORE %x\n", bus_space_read_4(prcm_iot, prcm_ioh, CM_AUTOIDLE1_CORE));

	printf("CM_FCLKEN_WKUP %x\n", bus_space_read_4(prcm_iot, prcm_ioh, CM_FCLKEN_WKUP));
	printf(" CM_IDLEST_WKUP %x\n", bus_space_read_4(prcm_iot, prcm_ioh,  CM_IDLEST_WKUP));
//	bus_space_write_4(prcm_iot, prcm_ioh, 
#endif

#if 0
	reg = bus_space_read_4(prcm_iot, prcm_ioh, CM_FCLKEN1_CORE);
	reg |= CM_FCLKEN1_CORE_GP3|CM_FCLKEN1_CORE_GP2;
	bus_space_write_4(prcm_iot, prcm_ioh, CM_FCLKEN1_CORE, reg);
	reg = bus_space_read_4(prcm_iot, prcm_ioh, CM_ICLKEN1_CORE);
	reg |= CM_ICLKEN1_CORE_GP3|CM_ICLKEN1_CORE_GP2;
	bus_space_write_4(prcm_iot, prcm_ioh, CM_ICLKEN1_CORE, reg);

	reg = bus_space_read_4(prcm_iot, prcm_ioh, CM_FCLKEN_WKUP);
	reg |= CM_FCLKEN_WKUP_MPU_WDT | CM_FCLKEN_WKUP_GPT1;
	bus_space_write_4(prcm_iot, prcm_ioh, CM_FCLKEN_WKUP, reg);

	reg = bus_space_read_4(prcm_iot, prcm_ioh, CM_ICLKEN_WKUP);
	reg |= CM_ICLKEN_WKUP_MPU_WDT | CM_ICLKEN_WKUP_GPT1;
	bus_space_write_4(prcm_iot, prcm_ioh, CM_ICLKEN_WKUP, reg);
#endif
	prcm_fmask_mask[PRCM_REG_CORE_CLK1] = PRCM_REG_CORE_CLK1_FMASK;
	prcm_imask_mask[PRCM_REG_CORE_CLK1] = PRCM_REG_CORE_CLK1_IMASK;
	prcm_fmask_addr[PRCM_REG_CORE_CLK1] = PRCM_REG_CORE_CLK1_FADDR;
	prcm_imask_addr[PRCM_REG_CORE_CLK1] = PRCM_REG_CORE_CLK1_IADDR;

	prcm_fmask_mask[PRCM_REG_CORE_CLK2] = PRCM_REG_CORE_CLK2_FMASK;
	prcm_imask_mask[PRCM_REG_CORE_CLK2] = PRCM_REG_CORE_CLK2_IMASK;
	prcm_fmask_addr[PRCM_REG_CORE_CLK2] = PRCM_REG_CORE_CLK2_FADDR;
	prcm_imask_addr[PRCM_REG_CORE_CLK2] = PRCM_REG_CORE_CLK2_IADDR;

	prcm_fmask_mask[PRCM_REG_CORE_CLK3] = PRCM_REG_CORE_CLK3_FMASK;
	prcm_imask_mask[PRCM_REG_CORE_CLK3] = PRCM_REG_CORE_CLK3_IMASK;
	prcm_fmask_addr[PRCM_REG_CORE_CLK3] = PRCM_REG_CORE_CLK3_FADDR;
	prcm_imask_addr[PRCM_REG_CORE_CLK3] = PRCM_REG_CORE_CLK3_IADDR;

	prcm_fmask_mask[PRCM_REG_USBHOST] = PRCM_REG_USBHOST_FMASK;
	prcm_imask_mask[PRCM_REG_USBHOST] = PRCM_REG_USBHOST_IMASK;
	prcm_fmask_addr[PRCM_REG_USBHOST] = PRCM_REG_USBHOST_FADDR;
	prcm_imask_addr[PRCM_REG_USBHOST] = PRCM_REG_USBHOST_IADDR;

}

void
prcm_setclock(int clock, int speed)
{
#if 1
	u_int32_t oreg, reg, mask;
	if (clock == 1) {
		oreg = bus_space_read_4(prcm_iot, prcm_ioh, CM_CLKSEL_WKUP);
		mask = 1;
		reg = (oreg &~mask) | (speed & mask);
		printf(" prcm_setclock old %08x new %08x",  oreg, reg );
		bus_space_write_4(prcm_iot, prcm_ioh, CM_CLKSEL_WKUP, reg);
	} else if (clock >= 2 && clock <= 9) {
		int shift =  (clock-2);
		oreg = bus_space_read_4(prcm_iot, prcm_ioh, CM_CLKSEL_PER);

		mask = 1 << (mask);
		reg =  (oreg & ~mask) | ( (speed << shift) & mask);
		printf(" prcm_setclock old %08x new %08x",  oreg, reg);

		bus_space_write_4(prcm_iot, prcm_ioh, CM_CLKSEL_PER, reg);
	} else
		panic("prcm_setclock invalid clock %d", clock);
#endif
}

void
prcm_enableclock(int bit)
{
	u_int32_t fclk, iclk, fmask, imask, mbit;
	int freg, ireg, reg;
	printf("prcm_enableclock %d:", bit);

	reg = bit >> 5;

	freg = prcm_fmask_addr[reg];
	ireg = prcm_imask_addr[reg];
	fmask = prcm_fmask_mask[reg];
	imask = prcm_imask_mask[reg];

	mbit =  1 << (bit & 0x1f);
#if 0
	printf("reg %d faddr 0x%08x iaddr 0x%08x, fmask 0x%08x "
	    "imask 0x%08x mbit %x", reg, freg, ireg, fmask, imask, mbit);
#endif
	if (fmask & mbit) { /* dont access the register if bit isn't present */
		fclk = bus_space_read_4(prcm_iot, prcm_ioh, freg);
		prcm_fmask_cur[reg] = fclk | mbit;
		bus_space_write_4(prcm_iot, prcm_ioh, freg, fclk | mbit);
		printf(" fclk %08x %08x",  fclk, fclk | mbit);
	}

	if (imask & mbit) { /* dont access the register if bit isn't present */
		iclk = bus_space_read_4(prcm_iot, prcm_ioh, ireg);
		prcm_imask_cur[reg] = iclk | mbit;
		bus_space_write_4(prcm_iot, prcm_ioh, ireg, iclk | mbit);
		printf(" iclk %08x %08x",  iclk, iclk | mbit);
	}
	printf ("\n");
}

/*
 * OMAP35xx Power, Reset, and Clock Management Reference Guide
 * (sprufa5.pdf) and AM/DM37x Multimedia Device Technical Reference
 * Manual (sprugn4h.pdf) note that DPLL5 provides a 120MHz clock for
 * peripheral domain modules (page 107 and page 302).
 * The reference clock for DPLL5 is DPLL5_ALWON_FCLK which is
 * SYS_CLK, running at 13MHz.
 */
int
prcm_setup_dpll5(void)
{
	uint32_t val;

	/*
	 * We need to set the multiplier and divider values for PLL.
	 * To end up with 120MHz we take SYS_CLK, divide by it and multiply
	 * with 120 (sprugn4h.pdf, 13.4.11.4.1 SSC Configuration)
	 */
	val = ((120 & 0x7ff) << 8) | ((SYS_CLK - 1) & 0x7f);
	bus_space_write_4(prcm_iot, prcm_ioh, CM_CLKSEL4_PLL, val);

	/* Clock divider from the PLL to the 120MHz clock. */
	bus_space_write_4(prcm_iot, prcm_ioh, CM_CLKSEL5_PLL, val);

	/*
	 * spruf98o.pdf, page 2319:
	 * PERIPH2_DPLL_FREQSEL is 0x7 1.75MHz to 2.1MHz
	 * EN_PERIPH2_DPLL is 0x7
	 */
	val = (7 << 4) | (7 << 0);
	bus_space_write_4(prcm_iot, prcm_ioh, CM_CLKEN2_PLL, val);

	/* Disable the interconnect clock auto-idle. */
	bus_space_write_4(prcm_iot, prcm_ioh, CM_AUTOIDLE2_PLL, 0x0);

	/* Wait until DPLL5 is locked and there's clock activity. */
	while ((val = bus_space_read_4(prcm_iot, prcm_ioh,
	    CM_IDLEST_CKGEN) & 0x01) == 0x00) {
#ifdef DIAGNOSTIC
		printf("CM_IDLEST_PLL = 0x&08x\n", val);
#endif
	}

	return 0;
}