1 /* $OpenBSD: prcm.c,v 1.9 2014/05/08 21:17:01 miod Exp $ */
3 * Copyright (c) 2007,2009 Dale Rahn <drahn@openbsd.org>
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 * Ben Gray <ben.r.gray@gmail.com>.
21 * All rights reserved.
23 * Redistribution and use in source and binary forms, with or without
24 * modification, are permitted provided that the following conditions
26 * 1. Redistributions of source code must retain the above copyright
27 * notice, this list of conditions and the following disclaimer.
28 * 2. Redistributions in binary form must reproduce the above copyright
29 * notice, this list of conditions and the following disclaimer in the
30 * documentation and/or other materials provided with the distribution.
31 * 3. The name of the company nor the name of the author may be used to
32 * endorse or promote products derived from this software without specific
33 * prior written permission.
35 * THIS SOFTWARE IS PROVIDED BY BEN GRAY ``AS IS'' AND ANY EXPRESS OR
36 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
37 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
38 * IN NO EVENT SHALL BEN GRAY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
39 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
40 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
41 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
42 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
43 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
44 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
48 * Driver for the Power, Reset and Clock Management Module (PRCM).
51 #include <sys/types.h>
52 #include <sys/param.h>
53 #include <sys/systm.h>
54 #include <sys/kernel.h>
56 #include <sys/device.h>
58 #include <machine/bus.h>
59 #include <machine/intr.h>
60 #include <arm/cpufunc.h>
61 #include <armv7/armv7/armv7var.h>
62 #include <armv7/omap/prcmvar.h>
64 #include <armv7/omap/am335x_prcmreg.h>
65 #include <armv7/omap/omap3_prcmreg.h>
66 #include <armv7/omap/omap4_prcmreg.h>
68 #define PRCM_REVISION 0x0800
69 #define PRCM_SYSCONFIG 0x0810
71 uint32_t prcm_imask_mask
[PRCM_REG_MAX
];
72 uint32_t prcm_fmask_mask
[PRCM_REG_MAX
];
73 uint32_t prcm_imask_addr
[PRCM_REG_MAX
];
74 uint32_t prcm_fmask_addr
[PRCM_REG_MAX
];
76 #define SYS_CLK 13 /* SYS_CLK speed in MHz */
80 bus_space_tag_t sc_iot
;
81 bus_space_handle_t sc_prcm
;
82 bus_space_handle_t sc_cm1
;
83 bus_space_handle_t sc_cm2
;
84 void (*sc_setup
)(struct prcm_softc
*sc
);
85 void (*sc_enablemodule
)(struct prcm_softc
*sc
, int mod
);
86 void (*sc_setclock
)(struct prcm_softc
*sc
,
87 int clock
, int speed
);
92 int prcm_match(struct device
*, void *, void *);
93 void prcm_attach(struct device
*, struct device
*, void *);
94 int prcm_setup_dpll5(struct prcm_softc
*);
95 uint32_t prcm_v3_bit(int mod
);
96 uint32_t prcm_am335x_clkctrl(int mod
);
98 void prcm_am335x_enablemodule(struct prcm_softc
*, int);
99 void prcm_am335x_setclock(struct prcm_softc
*, int, int);
101 void prcm_v3_setup(struct prcm_softc
*);
102 void prcm_v3_enablemodule(struct prcm_softc
*, int);
103 void prcm_v3_setclock(struct prcm_softc
*, int, int);
105 void prcm_v4_enablemodule(struct prcm_softc
*, int);
106 int prcm_v4_hsusbhost_activate(int);
107 int prcm_v4_hsusbhost_set_source(int, int);
109 struct cfattach prcm_ca
= {
110 sizeof (struct prcm_softc
), NULL
, prcm_attach
113 struct cfdriver prcm_cd
= {
114 NULL
, "prcm", DV_DULL
118 prcm_attach(struct device
*parent
, struct device
*self
, void *args
)
120 struct armv7_attach_args
*aa
= args
;
121 struct prcm_softc
*sc
= (struct prcm_softc
*) self
;
124 sc
->sc_iot
= aa
->aa_iot
;
127 case BOARD_ID_AM335X_BEAGLEBONE
:
129 sc
->sc_enablemodule
= prcm_am335x_enablemodule
;
130 sc
->sc_setclock
= prcm_am335x_setclock
;
132 case BOARD_ID_OMAP3_BEAGLE
:
133 case BOARD_ID_OMAP3_OVERO
:
134 sc
->sc_setup
= prcm_v3_setup
;
135 sc
->sc_enablemodule
= prcm_v3_enablemodule
;
136 sc
->sc_setclock
= prcm_v3_setclock
;
138 case BOARD_ID_OMAP4_PANDA
:
140 sc
->sc_enablemodule
= prcm_v4_enablemodule
;
141 sc
->sc_setclock
= NULL
;
147 if (bus_space_map(sc
->sc_iot
, aa
->aa_dev
->mem
[0].addr
,
148 aa
->aa_dev
->mem
[0].size
, 0, &sc
->sc_prcm
))
149 panic("prcm_attach: bus_space_map failed!");
152 bus_space_map(sc
->sc_iot
, aa
->aa_dev
->mem
[1].addr
,
153 aa
->aa_dev
->mem
[1].size
, 0, &sc
->sc_cm1
))
154 panic("prcm_attach: bus_space_map failed!");
157 bus_space_map(sc
->sc_iot
, aa
->aa_dev
->mem
[2].addr
,
158 aa
->aa_dev
->mem
[2].size
, 0, &sc
->sc_cm2
))
159 panic("prcm_attach: bus_space_map failed!");
161 reg
= bus_space_read_4(sc
->sc_iot
, sc
->sc_prcm
, PRCM_REVISION
);
162 printf(" rev %d.%d\n", reg
>> 4 & 0xf, reg
& 0xf);
164 if (sc
->sc_setup
!= NULL
)
169 prcm_v3_setup(struct prcm_softc
*sc
)
171 /* Setup the 120MHZ DPLL5 clock, to be used by USB. */
172 prcm_setup_dpll5(sc
);
174 prcm_fmask_mask
[PRCM_REG_CORE_CLK1
] = PRCM_REG_CORE_CLK1_FMASK
;
175 prcm_imask_mask
[PRCM_REG_CORE_CLK1
] = PRCM_REG_CORE_CLK1_IMASK
;
176 prcm_fmask_addr
[PRCM_REG_CORE_CLK1
] = PRCM_REG_CORE_CLK1_FADDR
;
177 prcm_imask_addr
[PRCM_REG_CORE_CLK1
] = PRCM_REG_CORE_CLK1_IADDR
;
179 prcm_fmask_mask
[PRCM_REG_CORE_CLK2
] = PRCM_REG_CORE_CLK2_FMASK
;
180 prcm_imask_mask
[PRCM_REG_CORE_CLK2
] = PRCM_REG_CORE_CLK2_IMASK
;
181 prcm_fmask_addr
[PRCM_REG_CORE_CLK2
] = PRCM_REG_CORE_CLK2_FADDR
;
182 prcm_imask_addr
[PRCM_REG_CORE_CLK2
] = PRCM_REG_CORE_CLK2_IADDR
;
184 prcm_fmask_mask
[PRCM_REG_CORE_CLK3
] = PRCM_REG_CORE_CLK3_FMASK
;
185 prcm_imask_mask
[PRCM_REG_CORE_CLK3
] = PRCM_REG_CORE_CLK3_IMASK
;
186 prcm_fmask_addr
[PRCM_REG_CORE_CLK3
] = PRCM_REG_CORE_CLK3_FADDR
;
187 prcm_imask_addr
[PRCM_REG_CORE_CLK3
] = PRCM_REG_CORE_CLK3_IADDR
;
189 prcm_fmask_mask
[PRCM_REG_WKUP
] = PRCM_REG_WKUP_FMASK
;
190 prcm_imask_mask
[PRCM_REG_WKUP
] = PRCM_REG_WKUP_IMASK
;
191 prcm_fmask_addr
[PRCM_REG_WKUP
] = PRCM_REG_WKUP_FADDR
;
192 prcm_imask_addr
[PRCM_REG_WKUP
] = PRCM_REG_WKUP_IADDR
;
194 prcm_fmask_mask
[PRCM_REG_PER
] = PRCM_REG_PER_FMASK
;
195 prcm_imask_mask
[PRCM_REG_PER
] = PRCM_REG_PER_IMASK
;
196 prcm_fmask_addr
[PRCM_REG_PER
] = PRCM_REG_PER_FADDR
;
197 prcm_imask_addr
[PRCM_REG_PER
] = PRCM_REG_PER_IADDR
;
199 prcm_fmask_mask
[PRCM_REG_USBHOST
] = PRCM_REG_USBHOST_FMASK
;
200 prcm_imask_mask
[PRCM_REG_USBHOST
] = PRCM_REG_USBHOST_IMASK
;
201 prcm_fmask_addr
[PRCM_REG_USBHOST
] = PRCM_REG_USBHOST_FADDR
;
202 prcm_imask_addr
[PRCM_REG_USBHOST
] = PRCM_REG_USBHOST_IADDR
;
206 prcm_setclock(int clock
, int speed
)
208 struct prcm_softc
*sc
= prcm_cd
.cd_devs
[0];
210 if (!sc
->sc_setclock
)
211 panic("%s: not initialised!", __func__
);
213 sc
->sc_setclock(sc
, clock
, speed
);
217 prcm_am335x_setclock(struct prcm_softc
*sc
, int clock
, int speed
)
219 u_int32_t oreg
, reg
, mask
;
221 /* set CLKSEL register */
223 oreg
= bus_space_read_4(sc
->sc_iot
, sc
->sc_prcm
,
224 PRCM_AM335X_CLKSEL_TIMER2_CLK
);
228 bus_space_write_4(sc
->sc_iot
, sc
->sc_prcm
,
229 PRCM_AM335X_CLKSEL_TIMER2_CLK
, reg
);
230 } else if (clock
== 2) {
231 oreg
= bus_space_read_4(sc
->sc_iot
, sc
->sc_prcm
,
232 PRCM_AM335X_CLKSEL_TIMER3_CLK
);
236 bus_space_write_4(sc
->sc_iot
, sc
->sc_prcm
,
237 PRCM_AM335X_CLKSEL_TIMER3_CLK
, reg
);
242 prcm_v3_setclock(struct prcm_softc
*sc
, int clock
, int speed
)
244 u_int32_t oreg
, reg
, mask
;
247 oreg
= bus_space_read_4(sc
->sc_iot
, sc
->sc_prcm
, CM_CLKSEL_WKUP
);
249 reg
= (oreg
&~mask
) | (speed
& mask
);
250 bus_space_write_4(sc
->sc_iot
, sc
->sc_prcm
, CM_CLKSEL_WKUP
, reg
);
251 } else if (clock
>= 2 && clock
<= 9) {
252 int shift
= (clock
-2);
253 oreg
= bus_space_read_4(sc
->sc_iot
, sc
->sc_prcm
, CM_CLKSEL_PER
);
255 reg
= (oreg
& ~mask
) | ( (speed
<< shift
) & mask
);
256 bus_space_write_4(sc
->sc_iot
, sc
->sc_prcm
, CM_CLKSEL_PER
, reg
);
258 panic("%s: invalid clock %d", __func__
, clock
);
266 return PRCM_CLK_EN_MMC1
;
268 return PRCM_CLK_EN_MMC2
;
270 return PRCM_CLK_EN_MMC3
;
272 return PRCM_CLK_EN_USB
;
274 return PRCM_CLK_EN_GPIO1
;
276 return PRCM_CLK_EN_GPIO2
;
278 return PRCM_CLK_EN_GPIO3
;
280 return PRCM_CLK_EN_GPIO4
;
282 return PRCM_CLK_EN_GPIO5
;
284 return PRCM_CLK_EN_GPIO6
;
286 panic("%s: module not found\n", __func__
);
291 prcm_am335x_clkctrl(int mod
)
295 return PRCM_AM335X_TIMER2_CLKCTRL
;
297 return PRCM_AM335X_TIMER3_CLKCTRL
;
299 return PRCM_AM335X_MMC0_CLKCTRL
;
301 return PRCM_AM335X_MMC1_CLKCTRL
;
303 return PRCM_AM335X_MMC2_CLKCTRL
;
305 return PRCM_AM335X_USB0_CLKCTRL
;
307 return PRCM_AM335X_GPIO0_CLKCTRL
;
309 return PRCM_AM335X_GPIO1_CLKCTRL
;
311 return PRCM_AM335X_GPIO2_CLKCTRL
;
313 return PRCM_AM335X_GPIO3_CLKCTRL
;
315 return PRCM_AM335X_TPCC_CLKCTRL
;
317 return PRCM_AM335X_TPTC0_CLKCTRL
;
319 return PRCM_AM335X_TPTC1_CLKCTRL
;
321 return PRCM_AM335X_TPTC2_CLKCTRL
;
323 return PRCM_AM335X_I2C0_CLKCTRL
;
325 return PRCM_AM335X_I2C1_CLKCTRL
;
327 return PRCM_AM335X_I2C2_CLKCTRL
;
329 panic("%s: module not found\n", __func__
);
334 prcm_enablemodule(int mod
)
336 struct prcm_softc
*sc
= prcm_cd
.cd_devs
[0];
338 if (!sc
->sc_enablemodule
)
339 panic("%s: not initialised!", __func__
);
341 sc
->sc_enablemodule(sc
, mod
);
345 prcm_am335x_enablemodule(struct prcm_softc
*sc
, int mod
)
350 /*set enable bits in CLKCTRL register */
351 reg
= prcm_am335x_clkctrl(mod
);
352 clkctrl
= bus_space_read_4(sc
->sc_iot
, sc
->sc_prcm
, reg
);
353 clkctrl
&=~AM335X_CLKCTRL_MODULEMODE_MASK
;
354 clkctrl
|= AM335X_CLKCTRL_MODULEMODE_ENABLE
;
355 bus_space_write_4(sc
->sc_iot
, sc
->sc_prcm
, reg
, clkctrl
);
357 /* wait until module is enabled */
358 while (bus_space_read_4(sc
->sc_iot
, sc
->sc_prcm
, reg
) & 0x30000)
363 prcm_v3_enablemodule(struct prcm_softc
*sc
, int mod
)
366 uint32_t fclk
, iclk
, fmask
, imask
, mbit
;
369 bit
= prcm_v3_bit(mod
);
372 freg
= prcm_fmask_addr
[reg
];
373 ireg
= prcm_imask_addr
[reg
];
374 fmask
= prcm_fmask_mask
[reg
];
375 imask
= prcm_imask_mask
[reg
];
377 mbit
= 1 << (bit
& 0x1f);
378 if (fmask
& mbit
) { /* dont access the register if bit isn't present */
379 fclk
= bus_space_read_4(sc
->sc_iot
, sc
->sc_prcm
, freg
);
380 bus_space_write_4(sc
->sc_iot
, sc
->sc_prcm
, freg
, fclk
| mbit
);
382 if (imask
& mbit
) { /* dont access the register if bit isn't present */
383 iclk
= bus_space_read_4(sc
->sc_iot
, sc
->sc_prcm
, ireg
);
384 bus_space_write_4(sc
->sc_iot
, sc
->sc_prcm
, ireg
, iclk
| mbit
);
390 prcm_v4_enablemodule(struct prcm_softc
*sc
, int mod
)
397 prcm_v4_hsusbhost_set_source(mod
, 0);
400 case PRCM_USBP1_UTMI
:
401 case PRCM_USBP1_HSIC
:
402 case PRCM_USBP2_UTMI
:
403 case PRCM_USBP2_HSIC
:
404 prcm_v4_hsusbhost_activate(mod
);
415 panic("%s: module not found\n", __func__
);
420 prcm_v4_hsusbhost_activate(int type
)
422 struct prcm_softc
*sc
= prcm_cd
.cd_devs
[0];
424 uint32_t clksel_reg_off
;
425 uint32_t clksel
, oclksel
;
431 /* We need the CM_L3INIT_HSUSBHOST_CLKCTRL register in CM2 register set */
432 clksel_reg_off
= O4_L3INIT_CM2_OFFSET
+ 0x58;
433 clksel
= bus_space_read_4(sc
->sc_iot
, sc
->sc_cm2
, clksel_reg_off
);
435 /* Enable the module and also enable the optional func clocks */
436 if (type
== PRCM_USB
) {
437 clksel
&= ~O4_CLKCTRL_MODULEMODE_MASK
;
438 clksel
|= /*O4_CLKCTRL_MODULEMODE_ENABLE*/2;
440 clksel
|= (0x1 << 15); /* USB-HOST clock control: FUNC48MCLK */
446 panic("%s: invalid type %d", __func__
, type
);
449 bus_space_write_4(sc
->sc_iot
, sc
->sc_cm2
, clksel_reg_off
, clksel
);
451 /* Try MAX_MODULE_ENABLE_WAIT number of times to check if enabled */
452 for (i
= 0; i
< O4_MAX_MODULE_ENABLE_WAIT
; i
++) {
453 clksel
= bus_space_read_4(sc
->sc_iot
, sc
->sc_cm2
, clksel_reg_off
);
454 if ((clksel
& O4_CLKCTRL_IDLEST_MASK
) == O4_CLKCTRL_IDLEST_ENABLED
)
458 /* Check the enabled state */
459 if ((clksel
& O4_CLKCTRL_IDLEST_MASK
) != O4_CLKCTRL_IDLEST_ENABLED
) {
460 printf("Error: HERE failed to enable module with clock %d\n", type
);
461 printf("Error: 0x%08x => 0x%08x\n", clksel_reg_off
, clksel
);
469 prcm_v4_hsusbhost_set_source(int clk
, int clksrc
)
471 struct prcm_softc
*sc
= prcm_cd
.cd_devs
[0];
472 uint32_t clksel_reg_off
;
476 if (clk
== PRCM_USBP1_PHY
)
478 else if (clk
!= PRCM_USBP2_PHY
)
483 /* We need the CM_L3INIT_HSUSBHOST_CLKCTRL register in CM2 register set */
484 clksel_reg_off
= O4_L3INIT_CM2_OFFSET
+ 0x58;
485 clksel
= bus_space_read_4(sc
->sc_iot
, sc
->sc_cm2
, clksel_reg_off
);
487 /* XXX: Set the clock source to either external or internal */
489 clksel
|= (0x1 << bit
);
491 clksel
&= ~(0x1 << bit
);
493 bus_space_write_4(sc
->sc_iot
, sc
->sc_cm2
, clksel_reg_off
, clksel
);
499 * OMAP35xx Power, Reset, and Clock Management Reference Guide
500 * (sprufa5.pdf) and AM/DM37x Multimedia Device Technical Reference
501 * Manual (sprugn4h.pdf) note that DPLL5 provides a 120MHz clock for
502 * peripheral domain modules (page 107 and page 302).
503 * The reference clock for DPLL5 is DPLL5_ALWON_FCLK which is
504 * SYS_CLK, running at 13MHz.
507 prcm_setup_dpll5(struct prcm_softc
*sc
)
512 * We need to set the multiplier and divider values for PLL.
513 * To end up with 120MHz we take SYS_CLK, divide by it and multiply
514 * with 120 (sprugn4h.pdf, 13.4.11.4.1 SSC Configuration)
516 val
= ((120 & 0x7ff) << 8) | ((SYS_CLK
- 1) & 0x7f);
517 bus_space_write_4(sc
->sc_iot
, sc
->sc_prcm
, CM_CLKSEL4_PLL
, val
);
519 /* Clock divider from the PLL to the 120MHz clock. */
520 bus_space_write_4(sc
->sc_iot
, sc
->sc_prcm
, CM_CLKSEL5_PLL
, val
);
523 * spruf98o.pdf, page 2319:
524 * PERIPH2_DPLL_FREQSEL is 0x7 1.75MHz to 2.1MHz
525 * EN_PERIPH2_DPLL is 0x7
527 val
= (7 << 4) | (7 << 0);
528 bus_space_write_4(sc
->sc_iot
, sc
->sc_prcm
, CM_CLKEN2_PLL
, val
);
530 /* Disable the interconnect clock auto-idle. */
531 bus_space_write_4(sc
->sc_iot
, sc
->sc_prcm
, CM_AUTOIDLE2_PLL
, 0x0);
533 /* Wait until DPLL5 is locked and there's clock activity. */
534 while ((val
= bus_space_read_4(sc
->sc_iot
, sc
->sc_prcm
,
535 CM_IDLEST_CKGEN
) & 0x01) == 0x00) {
537 printf("CM_IDLEST_PLL = 0x%08x\n", val
);