summaryrefslogtreecommitdiff
path: root/sys/arch/armv7/omap/prcm.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/arch/armv7/omap/prcm.c')
-rw-r--r--sys/arch/armv7/omap/prcm.c482
1 files changed, 482 insertions, 0 deletions
diff --git a/sys/arch/armv7/omap/prcm.c b/sys/arch/armv7/omap/prcm.c
new file mode 100644
index 00000000000..c255c861962
--- /dev/null
+++ b/sys/arch/armv7/omap/prcm.c
@@ -0,0 +1,482 @@
+/* $OpenBSD: prcm.c,v 1.1 2013/09/04 14:38:31 patrick 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.
+ */
+
+/*-
+ * Copyright (c) 2011
+ * Ben Gray <ben.r.gray@gmail.com>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the company nor the name of the author may be used to
+ * endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BEN GRAY ``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 BEN GRAY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 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.
+ */
+
+/*
+ * 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 <armv7/omap/omapvar.h>
+#include <armv7/omap/prcmvar.h>
+
+#include <armv7/omap/am335x_prcmreg.h>
+#include <armv7/omap/omap3_prcmreg.h>
+#include <armv7/omap/omap4_prcmreg.h>
+
+#define PRCM_REVISION 0x0800
+#define PRCM_SYSCONFIG 0x0810
+
+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 SYS_CLK 13 /* SYS_CLK speed in MHz */
+
+struct prcm_softc {
+ struct device sc_dev;
+ bus_space_tag_t sc_iot;
+ bus_space_handle_t sc_prcm;
+ bus_space_handle_t sc_cm1;
+ bus_space_handle_t sc_cm2;
+ void (*sc_setup)(struct prcm_softc *sc);
+ void (*sc_enablemodule)(struct prcm_softc *sc, int mod);
+ void (*sc_setclock)(struct prcm_softc *sc,
+ int clock, int speed);
+ uint32_t cm1_avail;
+ uint32_t cm2_avail;
+};
+
+int prcm_match(struct device *, void *, void *);
+void prcm_attach(struct device *, struct device *, void *);
+int prcm_setup_dpll5(struct prcm_softc *);
+uint32_t prcm_v3_bit(int mod);
+uint32_t prcm_am335x_clkctrl(int mod);
+
+void prcm_am335x_enablemodule(struct prcm_softc *, int);
+void prcm_am335x_setclock(struct prcm_softc *, int, int);
+
+void prcm_v3_setup(struct prcm_softc *);
+void prcm_v3_enablemodule(struct prcm_softc *, int);
+void prcm_v3_setclock(struct prcm_softc *, int, int);
+
+void prcm_v4_enablemodule(struct prcm_softc *, int);
+int prcm_v4_hsusbhost_activate(int);
+int prcm_v4_hsusbhost_set_source(int, int);
+
+struct cfattach prcm_ca = {
+ sizeof (struct prcm_softc), NULL, prcm_attach
+};
+
+struct cfdriver prcm_cd = {
+ NULL, "prcm", DV_DULL
+};
+
+void
+prcm_attach(struct device *parent, struct device *self, void *args)
+{
+ struct omap_attach_args *oa = args;
+ struct prcm_softc *sc = (struct prcm_softc *) self;
+ u_int32_t reg;
+
+ sc->sc_iot = oa->oa_iot;
+
+ switch (board_id) {
+ case BOARD_ID_AM335X_BEAGLEBONE:
+ sc->sc_setup = NULL;
+ sc->sc_enablemodule = prcm_am335x_enablemodule;
+ sc->sc_setclock = prcm_am335x_setclock;
+ break;
+ case BOARD_ID_OMAP3_BEAGLE:
+ case BOARD_ID_OMAP3_OVERO:
+ sc->sc_setup = prcm_v3_setup;
+ sc->sc_enablemodule = prcm_v3_enablemodule;
+ sc->sc_setclock = prcm_v3_setclock;
+ break;
+ case BOARD_ID_OMAP4_PANDA:
+ sc->sc_setup = NULL;
+ sc->sc_enablemodule = prcm_v4_enablemodule;
+ sc->sc_setclock = NULL;
+ sc->cm1_avail = 1;
+ sc->cm2_avail = 1;
+ break;
+ }
+
+ if (bus_space_map(sc->sc_iot, oa->oa_dev->mem[0].addr,
+ oa->oa_dev->mem[0].size, 0, &sc->sc_prcm))
+ panic("prcm_attach: bus_space_map failed!");
+
+ if (sc->cm1_avail &&
+ bus_space_map(sc->sc_iot, oa->oa_dev->mem[1].addr,
+ oa->oa_dev->mem[1].size, 0, &sc->sc_cm1))
+ panic("prcm_attach: bus_space_map failed!");
+
+ if (sc->cm2_avail &&
+ bus_space_map(sc->sc_iot, oa->oa_dev->mem[2].addr,
+ oa->oa_dev->mem[2].size, 0, &sc->sc_cm2))
+ panic("prcm_attach: bus_space_map failed!");
+
+ reg = bus_space_read_4(sc->sc_iot, sc->sc_prcm, PRCM_REVISION);
+ printf(" rev %d.%d\n", reg >> 4 & 0xf, reg & 0xf);
+
+ if (sc->sc_setup)
+ sc->sc_setup(sc);
+
+ printf("\n");
+}
+
+void
+prcm_v3_setup(struct prcm_softc *sc)
+{
+ /* Setup the 120MHZ DPLL5 clock, to be used by USB. */
+ prcm_setup_dpll5(sc);
+
+ 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)
+{
+ struct prcm_softc *sc = prcm_cd.cd_devs[0];
+
+ if (!sc->sc_setclock)
+ panic("%s: not initialised!", __func__);
+
+ sc->sc_setclock(sc, clock, speed);
+}
+
+void
+prcm_am335x_setclock(struct prcm_softc *sc, int clock, int speed)
+{
+ u_int32_t oreg, reg, mask;
+
+ /* set CLKSEL register */
+ if (clock == 1) {
+ oreg = bus_space_read_4(sc->sc_iot, sc->sc_prcm,
+ PRCM_AM335X_CLKSEL_TIMER2_CLK);
+ mask = 3;
+ reg = oreg & ~mask;
+ reg |=0x02;
+ bus_space_write_4(sc->sc_iot, sc->sc_prcm,
+ PRCM_AM335X_CLKSEL_TIMER2_CLK, reg);
+ } else if (clock == 2) {
+ oreg = bus_space_read_4(sc->sc_iot, sc->sc_prcm,
+ PRCM_AM335X_CLKSEL_TIMER3_CLK);
+ mask = 3;
+ reg = oreg & ~mask;
+ reg |=0x02;
+ bus_space_write_4(sc->sc_iot, sc->sc_prcm,
+ PRCM_AM335X_CLKSEL_TIMER3_CLK, reg);
+ }
+}
+
+void
+prcm_v3_setclock(struct prcm_softc *sc, int clock, int speed)
+{
+ u_int32_t oreg, reg, mask;
+
+ if (clock == 1) {
+ oreg = bus_space_read_4(sc->sc_iot, sc->sc_prcm, CM_CLKSEL_WKUP);
+ mask = 1;
+ reg = (oreg &~mask) | (speed & mask);
+ bus_space_write_4(sc->sc_iot, sc->sc_prcm, CM_CLKSEL_WKUP, reg);
+ } else if (clock >= 2 && clock <= 9) {
+ int shift = (clock-2);
+ oreg = bus_space_read_4(sc->sc_iot, sc->sc_prcm, CM_CLKSEL_PER);
+ mask = 1 << (shift);
+ reg = (oreg & ~mask) | ( (speed << shift) & mask);
+ bus_space_write_4(sc->sc_iot, sc->sc_prcm, CM_CLKSEL_PER, reg);
+ } else
+ panic("%s: invalid clock %d", __func__, clock);
+}
+
+uint32_t
+prcm_v3_bit(int mod)
+{
+ switch(mod) {
+ case PRCM_MMC:
+ return PRCM_CLK_EN_MMC1;
+ case PRCM_USB:
+ return PRCM_CLK_EN_USB;
+ default:
+ panic("%s: module not found\n", __func__);
+ }
+}
+
+uint32_t
+prcm_am335x_clkctrl(int mod)
+{
+ switch(mod) {
+ case PRCM_TIMER2:
+ return PRCM_AM335X_TIMER2_CLKCTRL;
+ case PRCM_TIMER3:
+ return PRCM_AM335X_TIMER3_CLKCTRL;
+ case PRCM_MMC:
+ return PRCM_AM335X_MMC0_CLKCTRL;
+ case PRCM_USB:
+ return PRCM_AM335X_USB0_CLKCTRL;
+ default:
+ panic("%s: module not found\n", __func__);
+ }
+}
+
+void
+prcm_enablemodule(int mod)
+{
+ struct prcm_softc *sc = prcm_cd.cd_devs[0];
+
+ if (!sc->sc_enablemodule)
+ panic("%s: not initialised!", __func__);
+
+ sc->sc_enablemodule(sc, mod);
+}
+
+void
+prcm_am335x_enablemodule(struct prcm_softc *sc, int mod)
+{
+ uint32_t clkctrl;
+ int reg;
+
+ /*set enable bits in CLKCTRL register */
+ reg = prcm_am335x_clkctrl(mod);
+ clkctrl = bus_space_read_4(sc->sc_iot, sc->sc_prcm, reg);
+ clkctrl &=~AM335X_CLKCTRL_MODULEMODE_MASK;
+ clkctrl |= AM335X_CLKCTRL_MODULEMODE_ENABLE;
+ bus_space_write_4(sc->sc_iot, sc->sc_prcm, reg, clkctrl);
+
+ /* wait until module is enabled */
+ while (bus_space_read_4(sc->sc_iot, sc->sc_prcm, reg) & 0x30000)
+ ;
+}
+
+void
+prcm_v3_enablemodule(struct prcm_softc *sc, int mod)
+{
+ uint32_t bit;
+ uint32_t fclk, iclk, fmask, imask, mbit;
+ int freg, ireg, reg;
+
+ bit = prcm_v3_bit(mod);
+ 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 (fmask & mbit) { /* dont access the register if bit isn't present */
+ fclk = bus_space_read_4(sc->sc_iot, sc->sc_prcm, freg);
+ bus_space_write_4(sc->sc_iot, sc->sc_prcm, freg, fclk | mbit);
+ }
+ if (imask & mbit) { /* dont access the register if bit isn't present */
+ iclk = bus_space_read_4(sc->sc_iot, sc->sc_prcm, ireg);
+ bus_space_write_4(sc->sc_iot, sc->sc_prcm, ireg, iclk | mbit);
+ }
+ printf("\n");
+}
+
+void
+prcm_v4_enablemodule(struct prcm_softc *sc, int mod)
+{
+ switch (mod) {
+ case PRCM_USBP1_PHY:
+ case PRCM_USBP2_PHY:
+ prcm_v4_hsusbhost_set_source(mod, 0);
+ case PRCM_USB:
+ case PRCM_USBTLL:
+ case PRCM_USBP1_UTMI:
+ case PRCM_USBP1_HSIC:
+ case PRCM_USBP2_UTMI:
+ case PRCM_USBP2_HSIC:
+ prcm_v4_hsusbhost_activate(mod);
+ return;
+ default:
+ panic("%s: module not found\n", __func__);
+ }
+}
+
+int
+prcm_v4_hsusbhost_activate(int type)
+{
+ struct prcm_softc *sc = prcm_cd.cd_devs[0];
+ uint32_t i;
+ uint32_t clksel_reg_off;
+ uint32_t clksel, oclksel;
+
+ switch (type) {
+ case PRCM_USB:
+ case PRCM_USBP1_PHY:
+ case PRCM_USBP2_PHY:
+ /* We need the CM_L3INIT_HSUSBHOST_CLKCTRL register in CM2 register set */
+ clksel_reg_off = O4_L3INIT_CM2_OFFSET + 0x58;
+ clksel = bus_space_read_4(sc->sc_iot, sc->sc_cm2, clksel_reg_off);
+ oclksel = clksel;
+ /* Enable the module and also enable the optional func clocks */
+ if (type == PRCM_USB) {
+ clksel &= ~O4_CLKCTRL_MODULEMODE_MASK;
+ clksel |= /*O4_CLKCTRL_MODULEMODE_ENABLE*/2;
+
+ clksel |= (0x1 << 15); /* USB-HOST clock control: FUNC48MCLK */
+ }
+
+ break;
+
+ default:
+ panic("%s: invalid type %d", type);
+ return (EINVAL);
+ }
+ bus_space_write_4(sc->sc_iot, sc->sc_cm2, clksel_reg_off, clksel);
+
+ /* Try MAX_MODULE_ENABLE_WAIT number of times to check if enabled */
+ for (i = 0; i < O4_MAX_MODULE_ENABLE_WAIT; i++) {
+ clksel = bus_space_read_4(sc->sc_iot, sc->sc_cm2, clksel_reg_off);
+ if ((clksel & O4_CLKCTRL_IDLEST_MASK) == O4_CLKCTRL_IDLEST_ENABLED)
+ break;
+ }
+
+ /* Check the enabled state */
+ if ((clksel & O4_CLKCTRL_IDLEST_MASK) != O4_CLKCTRL_IDLEST_ENABLED) {
+ printf("Error: HERE failed to enable module with clock %d\n", type);
+ printf("Error: 0x%08x => 0x%08x\n", clksel_reg_off, clksel);
+ return (ETIMEDOUT);
+ }
+
+ return (0);
+}
+
+int
+prcm_v4_hsusbhost_set_source(int clk, int clksrc)
+{
+ struct prcm_softc *sc = prcm_cd.cd_devs[0];
+ uint32_t clksel_reg_off;
+ uint32_t clksel;
+ unsigned int bit;
+
+ if (clk == PRCM_USBP1_PHY)
+ bit = 24;
+ else if (clk != PRCM_USBP2_PHY)
+ bit = 25;
+ else
+ return (-EINVAL);
+
+ /* We need the CM_L3INIT_HSUSBHOST_CLKCTRL register in CM2 register set */
+ clksel_reg_off = O4_L3INIT_CM2_OFFSET + 0x58;
+ clksel = bus_space_read_4(sc->sc_iot, sc->sc_cm2, clksel_reg_off);
+
+ /* XXX: Set the clock source to either external or internal */
+ if (clksrc == 0)
+ clksel |= (0x1 << bit);
+ else
+ clksel &= ~(0x1 << bit);
+
+ bus_space_write_4(sc->sc_iot, sc->sc_cm2, clksel_reg_off, clksel);
+
+ return (0);
+}
+
+/*
+ * 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(struct prcm_softc *sc)
+{
+ 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(sc->sc_iot, sc->sc_prcm, CM_CLKSEL4_PLL, val);
+
+ /* Clock divider from the PLL to the 120MHz clock. */
+ bus_space_write_4(sc->sc_iot, sc->sc_prcm, 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(sc->sc_iot, sc->sc_prcm, CM_CLKEN2_PLL, val);
+
+ /* Disable the interconnect clock auto-idle. */
+ bus_space_write_4(sc->sc_iot, sc->sc_prcm, CM_AUTOIDLE2_PLL, 0x0);
+
+ /* Wait until DPLL5 is locked and there's clock activity. */
+ while ((val = bus_space_read_4(sc->sc_iot, sc->sc_prcm,
+ CM_IDLEST_CKGEN) & 0x01) == 0x00) {
+#ifdef DIAGNOSTIC
+ printf("CM_IDLEST_PLL = 0x%08x\n", val);
+#endif
+ }
+
+ return 0;
+}