diff options
Diffstat (limited to 'sys/arch/armv7/omap/prcm.c')
-rw-r--r-- | sys/arch/armv7/omap/prcm.c | 482 |
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; +} |