From bd3c01a1260ced850ee348809712be1f2db24772 Mon Sep 17 00:00:00 2001 From: Sichen Zhao <1473996754@qq.com> Date: Thu, 13 Jul 2017 10:24:04 +0800 Subject: Import am335x usb driver file from FreeBSD. --- freebsd/sys/arm/ti/am335x/am335x_musb.c | 421 +++++++++++++++ freebsd/sys/arm/ti/am335x/am335x_prcm.c | 857 +++++++++++++++++++++++++++++++ freebsd/sys/arm/ti/am335x/am335x_scm.h | 49 ++ freebsd/sys/arm/ti/am335x/am335x_usbss.c | 226 ++++++++ freebsd/sys/arm/ti/ti_cpuid.h | 83 +++ freebsd/sys/arm/ti/ti_prcm.c | 357 +++++++++++++ freebsd/sys/arm/ti/ti_prcm.h | 207 ++++++++ freebsd/sys/arm/ti/ti_scm.c | 176 +++++++ freebsd/sys/arm/ti/ti_scm.h | 56 ++ freebsd/sys/arm/ti/tivar.h | 41 ++ 10 files changed, 2473 insertions(+) create mode 100644 freebsd/sys/arm/ti/am335x/am335x_musb.c create mode 100644 freebsd/sys/arm/ti/am335x/am335x_prcm.c create mode 100644 freebsd/sys/arm/ti/am335x/am335x_scm.h create mode 100644 freebsd/sys/arm/ti/am335x/am335x_usbss.c create mode 100644 freebsd/sys/arm/ti/ti_cpuid.h create mode 100644 freebsd/sys/arm/ti/ti_prcm.c create mode 100644 freebsd/sys/arm/ti/ti_prcm.h create mode 100644 freebsd/sys/arm/ti/ti_scm.c create mode 100644 freebsd/sys/arm/ti/ti_scm.h create mode 100644 freebsd/sys/arm/ti/tivar.h (limited to 'freebsd/sys/arm') diff --git a/freebsd/sys/arm/ti/am335x/am335x_musb.c b/freebsd/sys/arm/ti/am335x/am335x_musb.c new file mode 100644 index 00000000..52d799ce --- /dev/null +++ b/freebsd/sys/arm/ti/am335x/am335x_musb.c @@ -0,0 +1,421 @@ +#include + +/*- + * Copyright (c) 2013 Oleksandr Tymoshenko + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 THE AUTHOR OR CONTRIBUTORS 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. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#define USB_DEBUG_VAR usbssdebug + +#include +#include +#include +#include + +#include + +#include +#include +#include + +#define USBCTRL_REV 0x00 +#define USBCTRL_CTRL 0x14 +#define USBCTRL_STAT 0x18 +#define USBCTRL_IRQ_STAT0 0x30 +#define IRQ_STAT0_RXSHIFT 16 +#define IRQ_STAT0_TXSHIFT 0 +#define USBCTRL_IRQ_STAT1 0x34 +#define IRQ_STAT1_DRVVBUS (1 << 8) +#define USBCTRL_INTEN_SET0 0x38 +#define USBCTRL_INTEN_SET1 0x3C +#define USBCTRL_INTEN_USB_ALL 0x1ff +#define USBCTRL_INTEN_USB_SOF (1 << 3) +#define USBCTRL_INTEN_CLR0 0x40 +#define USBCTRL_INTEN_CLR1 0x44 +#define USBCTRL_UTMI 0xE0 +#define USBCTRL_UTMI_FSDATAEXT (1 << 1) +#define USBCTRL_MODE 0xE8 +#define USBCTRL_MODE_IDDIG (1 << 8) +#define USBCTRL_MODE_IDDIGMUX (1 << 7) + +/* USBSS resource + 2 MUSB ports */ + +#define RES_USBCORE 0 +#define RES_USBCTRL 1 + +#define USB_WRITE4(sc, idx, reg, val) do { \ + bus_write_4((sc)->sc_mem_res[idx], (reg), (val)); \ +} while (0) + +#define USB_READ4(sc, idx, reg) bus_read_4((sc)->sc_mem_res[idx], (reg)) + +#define USBCTRL_WRITE4(sc, reg, val) \ + USB_WRITE4((sc), RES_USBCTRL, (reg), (val)) +#define USBCTRL_READ4(sc, reg) \ + USB_READ4((sc), RES_USBCTRL, (reg)) + +static struct resource_spec am335x_musbotg_mem_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { SYS_RES_MEMORY, 1, RF_ACTIVE }, + { -1, 0, 0 } +}; + +#ifdef USB_DEBUG +static int usbssdebug = 0; + +static SYSCTL_NODE(_hw_usb, OID_AUTO, am335x_usbss, CTLFLAG_RW, 0, "AM335x USBSS"); +SYSCTL_INT(_hw_usb_am335x_usbss, OID_AUTO, debug, CTLFLAG_RW, + &usbssdebug, 0, "Debug level"); +#endif + +static device_probe_t musbotg_probe; +static device_attach_t musbotg_attach; +static device_detach_t musbotg_detach; + +struct musbotg_super_softc { + struct musbotg_softc sc_otg; + struct resource *sc_mem_res[2]; + int sc_irq_rid; +}; + +static void +musbotg_vbus_poll(struct musbotg_super_softc *sc) +{ + uint32_t stat; + + if (sc->sc_otg.sc_mode == MUSB2_DEVICE_MODE) + musbotg_vbus_interrupt(&sc->sc_otg, 1); + else { + stat = USBCTRL_READ4(sc, USBCTRL_STAT); + musbotg_vbus_interrupt(&sc->sc_otg, stat & 1); + } +} + +/* + * Arg to musbotg_clocks_on and musbot_clocks_off is + * a uint32_t * pointing to the SCM register offset. + */ +static uint32_t USB_CTRL[] = {SCM_USB_CTRL0, SCM_USB_CTRL1}; + +static void +musbotg_clocks_on(void *arg) +{ + struct musbotg_softc *sc; + uint32_t c, reg; + + sc = arg; + reg = USB_CTRL[sc->sc_id]; + + ti_scm_reg_read_4(reg, &c); + c &= ~3; /* Enable power */ + c |= 1 << 19; /* VBUS detect enable */ + c |= 1 << 20; /* Session end enable */ + ti_scm_reg_write_4(reg, c); +} + +static void +musbotg_clocks_off(void *arg) +{ + struct musbotg_softc *sc; + uint32_t c, reg; + + sc = arg; + reg = USB_CTRL[sc->sc_id]; + + /* Disable power to PHY */ + ti_scm_reg_read_4(reg, &c); + ti_scm_reg_write_4(reg, c | 3); +} + +static void +musbotg_ep_int_set(struct musbotg_softc *sc, int ep, int on) +{ + struct musbotg_super_softc *ssc = sc->sc_platform_data; + uint32_t epmask; + + epmask = ((1 << ep) << IRQ_STAT0_RXSHIFT); + epmask |= ((1 << ep) << IRQ_STAT0_TXSHIFT); + if (on) + USBCTRL_WRITE4(ssc, USBCTRL_INTEN_SET0, epmask); + else + USBCTRL_WRITE4(ssc, USBCTRL_INTEN_CLR0, epmask); +} + +static void +musbotg_wrapper_interrupt(void *arg) +{ + struct musbotg_softc *sc = arg; + struct musbotg_super_softc *ssc = sc->sc_platform_data; + uint32_t stat, stat0, stat1; + + stat = USBCTRL_READ4(ssc, USBCTRL_STAT); + stat0 = USBCTRL_READ4(ssc, USBCTRL_IRQ_STAT0); + stat1 = USBCTRL_READ4(ssc, USBCTRL_IRQ_STAT1); + if (stat0) + USBCTRL_WRITE4(ssc, USBCTRL_IRQ_STAT0, stat0); + if (stat1) + USBCTRL_WRITE4(ssc, USBCTRL_IRQ_STAT1, stat1); + + DPRINTFN(4, "port%d: stat0=%08x stat1=%08x, stat=%08x\n", + sc->sc_id, stat0, stat1, stat); + + if (stat1 & IRQ_STAT1_DRVVBUS) + musbotg_vbus_interrupt(sc, stat & 1); + + musbotg_interrupt(arg, ((stat0 >> 16) & 0xffff), + stat0 & 0xffff, stat1 & 0xff); +} + +static int +musbotg_probe(device_t dev) +{ + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_is_compatible(dev, "ti,musb-am33xx")) + return (ENXIO); + + device_set_desc(dev, "TI AM33xx integrated USB OTG controller"); + + return (BUS_PROBE_DEFAULT); +} + +static int +musbotg_attach(device_t dev) +{ + struct musbotg_super_softc *sc = device_get_softc(dev); + char mode[16]; + int err; + uint32_t reg; + + sc->sc_otg.sc_id = device_get_unit(dev); + + /* Request the memory resources */ + err = bus_alloc_resources(dev, am335x_musbotg_mem_spec, + sc->sc_mem_res); + if (err) { + device_printf(dev, + "Error: could not allocate mem resources\n"); + return (ENXIO); + } + + /* Request the IRQ resources */ + sc->sc_otg.sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, + &sc->sc_irq_rid, RF_ACTIVE); + if (sc->sc_otg.sc_irq_res == NULL) { + device_printf(dev, + "Error: could not allocate irq resources\n"); + return (ENXIO); + } + + /* setup MUSB OTG USB controller interface softc */ + sc->sc_otg.sc_clocks_on = &musbotg_clocks_on; + sc->sc_otg.sc_clocks_off = &musbotg_clocks_off; + sc->sc_otg.sc_clocks_arg = &sc->sc_otg; + + sc->sc_otg.sc_ep_int_set = musbotg_ep_int_set; + + /* initialise some bus fields */ + sc->sc_otg.sc_bus.parent = dev; + sc->sc_otg.sc_bus.devices = sc->sc_otg.sc_devices; + sc->sc_otg.sc_bus.devices_max = MUSB2_MAX_DEVICES; + sc->sc_otg.sc_bus.dma_bits = 32; + + /* get all DMA memory */ + if (usb_bus_mem_alloc_all(&sc->sc_otg.sc_bus, + USB_GET_DMA_TAG(dev), NULL)) { + device_printf(dev, + "Failed allocate bus mem for musb\n"); + return (ENOMEM); + } + sc->sc_otg.sc_io_res = sc->sc_mem_res[RES_USBCORE]; + sc->sc_otg.sc_io_tag = + rman_get_bustag(sc->sc_otg.sc_io_res); + sc->sc_otg.sc_io_hdl = + rman_get_bushandle(sc->sc_otg.sc_io_res); + sc->sc_otg.sc_io_size = + rman_get_size(sc->sc_otg.sc_io_res); + + sc->sc_otg.sc_bus.bdev = device_add_child(dev, "usbus", -1); + if (!(sc->sc_otg.sc_bus.bdev)) { + device_printf(dev, "No busdev for musb\n"); + goto error; + } + device_set_ivars(sc->sc_otg.sc_bus.bdev, + &sc->sc_otg.sc_bus); + + err = bus_setup_intr(dev, sc->sc_otg.sc_irq_res, + INTR_TYPE_BIO | INTR_MPSAFE, + NULL, (driver_intr_t *)musbotg_wrapper_interrupt, + &sc->sc_otg, &sc->sc_otg.sc_intr_hdl); + if (err) { + sc->sc_otg.sc_intr_hdl = NULL; + device_printf(dev, + "Failed to setup interrupt for musb\n"); + goto error; + } + + sc->sc_otg.sc_platform_data = sc; + if (OF_getprop(ofw_bus_get_node(dev), "dr_mode", mode, + sizeof(mode)) > 0) { + if (strcasecmp(mode, "host") == 0) + sc->sc_otg.sc_mode = MUSB2_HOST_MODE; + else + sc->sc_otg.sc_mode = MUSB2_DEVICE_MODE; + } else { + /* Beaglebone defaults: USB0 device, USB1 HOST. */ + if (sc->sc_otg.sc_id == 0) + sc->sc_otg.sc_mode = MUSB2_DEVICE_MODE; + else + sc->sc_otg.sc_mode = MUSB2_HOST_MODE; + } + + /* + * software-controlled function + */ + + if (sc->sc_otg.sc_mode == MUSB2_HOST_MODE) { + reg = USBCTRL_READ4(sc, USBCTRL_MODE); + reg |= USBCTRL_MODE_IDDIGMUX; + reg &= ~USBCTRL_MODE_IDDIG; + USBCTRL_WRITE4(sc, USBCTRL_MODE, reg); + USBCTRL_WRITE4(sc, USBCTRL_UTMI, + USBCTRL_UTMI_FSDATAEXT); + } else { + reg = USBCTRL_READ4(sc, USBCTRL_MODE); + reg |= USBCTRL_MODE_IDDIGMUX; + reg |= USBCTRL_MODE_IDDIG; + USBCTRL_WRITE4(sc, USBCTRL_MODE, reg); + } + + reg = USBCTRL_INTEN_USB_ALL & ~USBCTRL_INTEN_USB_SOF; + USBCTRL_WRITE4(sc, USBCTRL_INTEN_SET1, reg); + USBCTRL_WRITE4(sc, USBCTRL_INTEN_CLR0, 0xffffffff); + + err = musbotg_init(&sc->sc_otg); + if (!err) + err = device_probe_and_attach(sc->sc_otg.sc_bus.bdev); + + if (err) + goto error; + + /* poll VBUS one time */ + musbotg_vbus_poll(sc); + + return (0); + +error: + musbotg_detach(dev); + return (ENXIO); +} + +static int +musbotg_detach(device_t dev) +{ + struct musbotg_super_softc *sc = device_get_softc(dev); + int err; + + /* during module unload there are lots of children leftover */ + device_delete_children(dev); + + if (sc->sc_otg.sc_irq_res && sc->sc_otg.sc_intr_hdl) { + /* + * only call musbotg_uninit() after musbotg_init() + */ + musbotg_uninit(&sc->sc_otg); + + err = bus_teardown_intr(dev, sc->sc_otg.sc_irq_res, + sc->sc_otg.sc_intr_hdl); + sc->sc_otg.sc_intr_hdl = NULL; + } + + usb_bus_mem_free_all(&sc->sc_otg.sc_bus, NULL); + + /* Free resources if any */ + if (sc->sc_mem_res[0]) + bus_release_resources(dev, am335x_musbotg_mem_spec, + sc->sc_mem_res); + + if (sc->sc_otg.sc_irq_res) + bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irq_rid, + sc->sc_otg.sc_irq_res); + + return (0); +} + +static device_method_t musbotg_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, musbotg_probe), + DEVMETHOD(device_attach, musbotg_attach), + DEVMETHOD(device_detach, musbotg_detach), + DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_resume, bus_generic_resume), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + + DEVMETHOD_END +}; + +static driver_t musbotg_driver = { + .name = "musbotg", + .methods = musbotg_methods, + .size = sizeof(struct musbotg_super_softc), +}; + +static devclass_t musbotg_devclass; + +DRIVER_MODULE(musbotg, usbss, musbotg_driver, musbotg_devclass, 0, 0); +MODULE_DEPEND(musbotg, usbss, 1, 1, 1); diff --git a/freebsd/sys/arm/ti/am335x/am335x_prcm.c b/freebsd/sys/arm/ti/am335x/am335x_prcm.c new file mode 100644 index 00000000..1d10f7f2 --- /dev/null +++ b/freebsd/sys/arm/ti/am335x/am335x_prcm.c @@ -0,0 +1,857 @@ +#include + +/*- + * Copyright (c) 2012 Damjan Marion + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 THE AUTHOR OR CONTRIBUTORS 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. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#include "am335x_scm.h" + +#define CM_PER 0 +#define CM_PER_L4LS_CLKSTCTRL (CM_PER + 0x000) +#define CM_PER_L3S_CLKSTCTRL (CM_PER + 0x004) +#define CM_PER_L3_CLKSTCTRL (CM_PER + 0x00C) +#define CM_PER_CPGMAC0_CLKCTRL (CM_PER + 0x014) +#define CM_PER_LCDC_CLKCTRL (CM_PER + 0x018) +#define CM_PER_USB0_CLKCTRL (CM_PER + 0x01C) +#define CM_PER_TPTC0_CLKCTRL (CM_PER + 0x024) +#define CM_PER_UART5_CLKCTRL (CM_PER + 0x038) +#define CM_PER_MMC0_CLKCTRL (CM_PER + 0x03C) +#define CM_PER_I2C2_CLKCTRL (CM_PER + 0x044) +#define CM_PER_I2C1_CLKCTRL (CM_PER + 0x048) +#define CM_PER_SPI0_CLKCTRL (CM_PER + 0x04C) +#define CM_PER_SPI1_CLKCTRL (CM_PER + 0x050) +#define CM_PER_UART1_CLKCTRL (CM_PER + 0x06C) +#define CM_PER_UART2_CLKCTRL (CM_PER + 0x070) +#define CM_PER_UART3_CLKCTRL (CM_PER + 0x074) +#define CM_PER_UART4_CLKCTRL (CM_PER + 0x078) +#define CM_PER_TIMER7_CLKCTRL (CM_PER + 0x07C) +#define CM_PER_TIMER2_CLKCTRL (CM_PER + 0x080) +#define CM_PER_TIMER3_CLKCTRL (CM_PER + 0x084) +#define CM_PER_TIMER4_CLKCTRL (CM_PER + 0x088) +#define CM_PER_GPIO1_CLKCTRL (CM_PER + 0x0AC) +#define CM_PER_GPIO2_CLKCTRL (CM_PER + 0x0B0) +#define CM_PER_GPIO3_CLKCTRL (CM_PER + 0x0B4) +#define CM_PER_TPCC_CLKCTRL (CM_PER + 0x0BC) +#define CM_PER_EPWMSS1_CLKCTRL (CM_PER + 0x0CC) +#define CM_PER_EPWMSS0_CLKCTRL (CM_PER + 0x0D4) +#define CM_PER_EPWMSS2_CLKCTRL (CM_PER + 0x0D8) +#define CM_PER_L3_INSTR_CLKCTRL (CM_PER + 0x0DC) +#define CM_PER_L3_CLKCTRL (CM_PER + 0x0E0) +#define CM_PER_PRUSS_CLKCTRL (CM_PER + 0x0E8) +#define CM_PER_TIMER5_CLKCTRL (CM_PER + 0x0EC) +#define CM_PER_TIMER6_CLKCTRL (CM_PER + 0x0F0) +#define CM_PER_MMC1_CLKCTRL (CM_PER + 0x0F4) +#define CM_PER_MMC2_CLKCTRL (CM_PER + 0x0F8) +#define CM_PER_TPTC1_CLKCTRL (CM_PER + 0x0FC) +#define CM_PER_TPTC2_CLKCTRL (CM_PER + 0x100) +#define CM_PER_SPINLOCK0_CLKCTRL (CM_PER + 0x10C) +#define CM_PER_MAILBOX0_CLKCTRL (CM_PER + 0x110) +#define CM_PER_OCPWP_L3_CLKSTCTRL (CM_PER + 0x12C) +#define CM_PER_OCPWP_CLKCTRL (CM_PER + 0x130) +#define CM_PER_CPSW_CLKSTCTRL (CM_PER + 0x144) +#define CM_PER_PRUSS_CLKSTCTRL (CM_PER + 0x140) + +#define CM_WKUP 0x400 +#define CM_WKUP_CLKSTCTRL (CM_WKUP + 0x000) +#define CM_WKUP_CONTROL_CLKCTRL (CM_WKUP + 0x004) +#define CM_WKUP_GPIO0_CLKCTRL (CM_WKUP + 0x008) +#define CM_WKUP_CM_L3_AON_CLKSTCTRL (CM_WKUP + 0x01C) +#define CM_WKUP_CM_CLKSEL_DPLL_MPU (CM_WKUP + 0x02C) +#define CM_WKUP_CM_IDLEST_DPLL_DISP (CM_WKUP + 0x048) +#define CM_WKUP_CM_CLKSEL_DPLL_DISP (CM_WKUP + 0x054) +#define CM_WKUP_CM_CLKDCOLDO_DPLL_PER (CM_WKUP + 0x07C) +#define CM_WKUP_CM_CLKMODE_DPLL_DISP (CM_WKUP + 0x098) +#define CM_WKUP_I2C0_CLKCTRL (CM_WKUP + 0x0B8) +#define CM_WKUP_ADC_TSC_CLKCTRL (CM_WKUP + 0x0BC) + +#define CM_DPLL 0x500 +#define CLKSEL_TIMER7_CLK (CM_DPLL + 0x004) +#define CLKSEL_TIMER2_CLK (CM_DPLL + 0x008) +#define CLKSEL_TIMER3_CLK (CM_DPLL + 0x00C) +#define CLKSEL_TIMER4_CLK (CM_DPLL + 0x010) +#define CLKSEL_TIMER5_CLK (CM_DPLL + 0x018) +#define CLKSEL_TIMER6_CLK (CM_DPLL + 0x01C) +#define CLKSEL_PRUSS_OCP_CLK (CM_DPLL + 0x030) + +#define CM_RTC 0x800 +#define CM_RTC_RTC_CLKCTRL (CM_RTC + 0x000) +#define CM_RTC_CLKSTCTRL (CM_RTC + 0x004) + +#define PRM_PER 0xC00 +#define PRM_PER_RSTCTRL (PRM_PER + 0x00) + +#define PRM_DEVICE_OFFSET 0xF00 +#define PRM_RSTCTRL (PRM_DEVICE_OFFSET + 0x00) + +struct am335x_prcm_softc { + struct resource * res[2]; + bus_space_tag_t bst; + bus_space_handle_t bsh; +}; + +static struct resource_spec am335x_prcm_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { -1, 0 } +}; + +static struct am335x_prcm_softc *am335x_prcm_sc = NULL; + +static int am335x_clk_noop_activate(struct ti_clock_dev *clkdev); +static int am335x_clk_generic_activate(struct ti_clock_dev *clkdev); +static int am335x_clk_gpio_activate(struct ti_clock_dev *clkdev); +static int am335x_clk_noop_deactivate(struct ti_clock_dev *clkdev); +static int am335x_clk_generic_deactivate(struct ti_clock_dev *clkdev); +static int am335x_clk_noop_set_source(struct ti_clock_dev *clkdev, clk_src_t clksrc); +static int am335x_clk_generic_set_source(struct ti_clock_dev *clkdev, clk_src_t clksrc); +static int am335x_clk_hsmmc_get_source_freq(struct ti_clock_dev *clkdev, unsigned int *freq); +static int am335x_clk_get_sysclk_freq(struct ti_clock_dev *clkdev, unsigned int *freq); +static int am335x_clk_get_arm_fclk_freq(struct ti_clock_dev *clkdev, unsigned int *freq); +static int am335x_clk_get_arm_disp_freq(struct ti_clock_dev *clkdev, unsigned int *freq); +static int am335x_clk_set_arm_disp_freq(struct ti_clock_dev *clkdev, unsigned int freq); +static void am335x_prcm_reset(void); +static int am335x_clk_cpsw_activate(struct ti_clock_dev *clkdev); +static int am335x_clk_musb0_activate(struct ti_clock_dev *clkdev); +static int am335x_clk_lcdc_activate(struct ti_clock_dev *clkdev); +static int am335x_clk_pruss_activate(struct ti_clock_dev *clkdev); + +#define AM335X_NOOP_CLOCK_DEV(i) \ + { .id = (i), \ + .clk_activate = am335x_clk_noop_activate, \ + .clk_deactivate = am335x_clk_noop_deactivate, \ + .clk_set_source = am335x_clk_noop_set_source, \ + .clk_accessible = NULL, \ + .clk_get_source_freq = NULL, \ + .clk_set_source_freq = NULL \ + } + +#define AM335X_GENERIC_CLOCK_DEV(i) \ + { .id = (i), \ + .clk_activate = am335x_clk_generic_activate, \ + .clk_deactivate = am335x_clk_generic_deactivate, \ + .clk_set_source = am335x_clk_generic_set_source, \ + .clk_accessible = NULL, \ + .clk_get_source_freq = NULL, \ + .clk_set_source_freq = NULL \ + } + +#define AM335X_GPIO_CLOCK_DEV(i) \ + { .id = (i), \ + .clk_activate = am335x_clk_gpio_activate, \ + .clk_deactivate = am335x_clk_generic_deactivate, \ + .clk_set_source = am335x_clk_generic_set_source, \ + .clk_accessible = NULL, \ + .clk_get_source_freq = NULL, \ + .clk_set_source_freq = NULL \ + } + +#define AM335X_MMCHS_CLOCK_DEV(i) \ + { .id = (i), \ + .clk_activate = am335x_clk_generic_activate, \ + .clk_deactivate = am335x_clk_generic_deactivate, \ + .clk_set_source = am335x_clk_generic_set_source, \ + .clk_accessible = NULL, \ + .clk_get_source_freq = am335x_clk_hsmmc_get_source_freq, \ + .clk_set_source_freq = NULL \ + } + +struct ti_clock_dev ti_am335x_clk_devmap[] = { + /* System clocks */ + { .id = SYS_CLK, + .clk_activate = NULL, + .clk_deactivate = NULL, + .clk_set_source = NULL, + .clk_accessible = NULL, + .clk_get_source_freq = am335x_clk_get_sysclk_freq, + .clk_set_source_freq = NULL, + }, + /* MPU (ARM) core clocks */ + { .id = MPU_CLK, + .clk_activate = NULL, + .clk_deactivate = NULL, + .clk_set_source = NULL, + .clk_accessible = NULL, + .clk_get_source_freq = am335x_clk_get_arm_fclk_freq, + .clk_set_source_freq = NULL, + }, + /* CPSW Ethernet Switch core clocks */ + { .id = CPSW_CLK, + .clk_activate = am335x_clk_cpsw_activate, + .clk_deactivate = NULL, + .clk_set_source = NULL, + .clk_accessible = NULL, + .clk_get_source_freq = NULL, + .clk_set_source_freq = NULL, + }, + + /* Mentor USB HS controller core clocks */ + { .id = MUSB0_CLK, + .clk_activate = am335x_clk_musb0_activate, + .clk_deactivate = NULL, + .clk_set_source = NULL, + .clk_accessible = NULL, + .clk_get_source_freq = NULL, + .clk_set_source_freq = NULL, + }, + + /* LCD controller clocks */ + { .id = LCDC_CLK, + .clk_activate = am335x_clk_lcdc_activate, + .clk_deactivate = NULL, + .clk_set_source = NULL, + .clk_accessible = NULL, + .clk_get_source_freq = am335x_clk_get_arm_disp_freq, + .clk_set_source_freq = am335x_clk_set_arm_disp_freq, + }, + + /* UART */ + AM335X_NOOP_CLOCK_DEV(UART1_CLK), + AM335X_GENERIC_CLOCK_DEV(UART2_CLK), + AM335X_GENERIC_CLOCK_DEV(UART3_CLK), + AM335X_GENERIC_CLOCK_DEV(UART4_CLK), + AM335X_GENERIC_CLOCK_DEV(UART5_CLK), + AM335X_GENERIC_CLOCK_DEV(UART6_CLK), + + /* DMTimer */ + AM335X_GENERIC_CLOCK_DEV(TIMER2_CLK), + AM335X_GENERIC_CLOCK_DEV(TIMER3_CLK), + AM335X_GENERIC_CLOCK_DEV(TIMER4_CLK), + AM335X_GENERIC_CLOCK_DEV(TIMER5_CLK), + AM335X_GENERIC_CLOCK_DEV(TIMER6_CLK), + AM335X_GENERIC_CLOCK_DEV(TIMER7_CLK), + + /* GPIO, we use hwmods as reference, not units in spec */ + AM335X_GPIO_CLOCK_DEV(GPIO1_CLK), + AM335X_GPIO_CLOCK_DEV(GPIO2_CLK), + AM335X_GPIO_CLOCK_DEV(GPIO3_CLK), + AM335X_GPIO_CLOCK_DEV(GPIO4_CLK), + + /* I2C we use hwmods as reference, not units in spec */ + AM335X_GENERIC_CLOCK_DEV(I2C1_CLK), + AM335X_GENERIC_CLOCK_DEV(I2C2_CLK), + AM335X_GENERIC_CLOCK_DEV(I2C3_CLK), + + /* McSPI we use hwmods as reference, not units in spec */ + AM335X_GENERIC_CLOCK_DEV(SPI0_CLK), + AM335X_GENERIC_CLOCK_DEV(SPI1_CLK), + + /* TSC_ADC */ + AM335X_GENERIC_CLOCK_DEV(TSC_ADC_CLK), + + /* EDMA */ + AM335X_GENERIC_CLOCK_DEV(EDMA_TPCC_CLK), + AM335X_GENERIC_CLOCK_DEV(EDMA_TPTC0_CLK), + AM335X_GENERIC_CLOCK_DEV(EDMA_TPTC1_CLK), + AM335X_GENERIC_CLOCK_DEV(EDMA_TPTC2_CLK), + + /* MMCHS */ + AM335X_MMCHS_CLOCK_DEV(MMC1_CLK), + AM335X_MMCHS_CLOCK_DEV(MMC2_CLK), + AM335X_MMCHS_CLOCK_DEV(MMC3_CLK), + + /* PWMSS */ + AM335X_GENERIC_CLOCK_DEV(PWMSS0_CLK), + AM335X_GENERIC_CLOCK_DEV(PWMSS1_CLK), + AM335X_GENERIC_CLOCK_DEV(PWMSS2_CLK), + + /* System Mailbox clock */ + AM335X_GENERIC_CLOCK_DEV(MAILBOX0_CLK), + + /* SPINLOCK */ + AM335X_GENERIC_CLOCK_DEV(SPINLOCK0_CLK), + + /* PRU-ICSS */ + { .id = PRUSS_CLK, + .clk_activate = am335x_clk_pruss_activate, + .clk_deactivate = NULL, + .clk_set_source = NULL, + .clk_accessible = NULL, + .clk_get_source_freq = NULL, + .clk_set_source_freq = NULL, + }, + + /* RTC */ + AM335X_GENERIC_CLOCK_DEV(RTC_CLK), + + { INVALID_CLK_IDENT, NULL, NULL, NULL, NULL } +}; + +struct am335x_clk_details { + clk_ident_t id; + uint32_t clkctrl_reg; + uint32_t clksel_reg; +}; + +#define _CLK_DETAIL(i, c, s) \ + { .id = (i), \ + .clkctrl_reg = (c), \ + .clksel_reg = (s), \ + } + +static struct am335x_clk_details g_am335x_clk_details[] = { + + /* UART. UART0 clock not controllable. */ + _CLK_DETAIL(UART1_CLK, 0, 0), + _CLK_DETAIL(UART2_CLK, CM_PER_UART1_CLKCTRL, 0), + _CLK_DETAIL(UART3_CLK, CM_PER_UART2_CLKCTRL, 0), + _CLK_DETAIL(UART4_CLK, CM_PER_UART3_CLKCTRL, 0), + _CLK_DETAIL(UART5_CLK, CM_PER_UART4_CLKCTRL, 0), + _CLK_DETAIL(UART6_CLK, CM_PER_UART5_CLKCTRL, 0), + + /* DMTimer modules */ + _CLK_DETAIL(TIMER2_CLK, CM_PER_TIMER2_CLKCTRL, CLKSEL_TIMER2_CLK), + _CLK_DETAIL(TIMER3_CLK, CM_PER_TIMER3_CLKCTRL, CLKSEL_TIMER3_CLK), + _CLK_DETAIL(TIMER4_CLK, CM_PER_TIMER4_CLKCTRL, CLKSEL_TIMER4_CLK), + _CLK_DETAIL(TIMER5_CLK, CM_PER_TIMER5_CLKCTRL, CLKSEL_TIMER5_CLK), + _CLK_DETAIL(TIMER6_CLK, CM_PER_TIMER6_CLKCTRL, CLKSEL_TIMER6_CLK), + _CLK_DETAIL(TIMER7_CLK, CM_PER_TIMER7_CLKCTRL, CLKSEL_TIMER7_CLK), + + /* GPIO modules, hwmods start with gpio1 */ + _CLK_DETAIL(GPIO1_CLK, CM_WKUP_GPIO0_CLKCTRL, 0), + _CLK_DETAIL(GPIO2_CLK, CM_PER_GPIO1_CLKCTRL, 0), + _CLK_DETAIL(GPIO3_CLK, CM_PER_GPIO2_CLKCTRL, 0), + _CLK_DETAIL(GPIO4_CLK, CM_PER_GPIO3_CLKCTRL, 0), + + /* I2C modules, hwmods start with i2c1 */ + _CLK_DETAIL(I2C1_CLK, CM_WKUP_I2C0_CLKCTRL, 0), + _CLK_DETAIL(I2C2_CLK, CM_PER_I2C1_CLKCTRL, 0), + _CLK_DETAIL(I2C3_CLK, CM_PER_I2C2_CLKCTRL, 0), + + /* McSPI modules, hwmods start with spi0 */ + _CLK_DETAIL(SPI0_CLK, CM_PER_SPI0_CLKCTRL, 0), + _CLK_DETAIL(SPI1_CLK, CM_PER_SPI1_CLKCTRL, 0), + + /* TSC_ADC module */ + _CLK_DETAIL(TSC_ADC_CLK, CM_WKUP_ADC_TSC_CLKCTRL, 0), + + /* EDMA modules */ + _CLK_DETAIL(EDMA_TPCC_CLK, CM_PER_TPCC_CLKCTRL, 0), + _CLK_DETAIL(EDMA_TPTC0_CLK, CM_PER_TPTC0_CLKCTRL, 0), + _CLK_DETAIL(EDMA_TPTC1_CLK, CM_PER_TPTC1_CLKCTRL, 0), + _CLK_DETAIL(EDMA_TPTC2_CLK, CM_PER_TPTC2_CLKCTRL, 0), + + /* MMCHS modules, hwmods start with mmc1*/ + _CLK_DETAIL(MMC1_CLK, CM_PER_MMC0_CLKCTRL, 0), + _CLK_DETAIL(MMC2_CLK, CM_PER_MMC1_CLKCTRL, 0), + _CLK_DETAIL(MMC3_CLK, CM_PER_MMC1_CLKCTRL, 0), + + /* PWMSS modules */ + _CLK_DETAIL(PWMSS0_CLK, CM_PER_EPWMSS0_CLKCTRL, 0), + _CLK_DETAIL(PWMSS1_CLK, CM_PER_EPWMSS1_CLKCTRL, 0), + _CLK_DETAIL(PWMSS2_CLK, CM_PER_EPWMSS2_CLKCTRL, 0), + + _CLK_DETAIL(MAILBOX0_CLK, CM_PER_MAILBOX0_CLKCTRL, 0), + _CLK_DETAIL(SPINLOCK0_CLK, CM_PER_SPINLOCK0_CLKCTRL, 0), + + /* RTC module */ + _CLK_DETAIL(RTC_CLK, CM_RTC_RTC_CLKCTRL, 0), + + { INVALID_CLK_IDENT, 0}, +}; + +/* Read/Write macros */ +#define prcm_read_4(reg) \ + bus_space_read_4(am335x_prcm_sc->bst, am335x_prcm_sc->bsh, reg) +#define prcm_write_4(reg, val) \ + bus_space_write_4(am335x_prcm_sc->bst, am335x_prcm_sc->bsh, reg, val) + +void am335x_prcm_setup_dmtimer(int); + +static int +am335x_prcm_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (ofw_bus_is_compatible(dev, "ti,am3-prcm")) { + device_set_desc(dev, "AM335x Power and Clock Management"); + return(BUS_PROBE_DEFAULT); + } + + return (ENXIO); +} + +static int +am335x_prcm_attach(device_t dev) +{ + struct am335x_prcm_softc *sc = device_get_softc(dev); + unsigned int sysclk, fclk; + + if (am335x_prcm_sc) + return (ENXIO); + + if (bus_alloc_resources(dev, am335x_prcm_spec, sc->res)) { + device_printf(dev, "could not allocate resources\n"); + return (ENXIO); + } + + sc->bst = rman_get_bustag(sc->res[0]); + sc->bsh = rman_get_bushandle(sc->res[0]); + + am335x_prcm_sc = sc; + ti_cpu_reset = am335x_prcm_reset; + + if (am335x_clk_get_sysclk_freq(NULL, &sysclk) != 0) + sysclk = 0; + if (am335x_clk_get_arm_fclk_freq(NULL, &fclk) != 0) + fclk = 0; + if (sysclk && fclk) + device_printf(dev, "Clocks: System %u.%01u MHz, CPU %u MHz\n", + sysclk/1000000, (sysclk % 1000000)/100000, fclk/1000000); + else + device_printf(dev, "can't read frequencies yet (SCM device not ready?)\n"); + + return (0); +} + +static device_method_t am335x_prcm_methods[] = { + DEVMETHOD(device_probe, am335x_prcm_probe), + DEVMETHOD(device_attach, am335x_prcm_attach), + { 0, 0 } +}; + +static driver_t am335x_prcm_driver = { + "am335x_prcm", + am335x_prcm_methods, + sizeof(struct am335x_prcm_softc), +}; + +static devclass_t am335x_prcm_devclass; + +DRIVER_MODULE(am335x_prcm, simplebus, am335x_prcm_driver, + am335x_prcm_devclass, 0, 0); +MODULE_VERSION(am335x_prcm, 1); +MODULE_DEPEND(am335x_prcm, ti_scm, 1, 1, 1); + +static struct am335x_clk_details* +am335x_clk_details(clk_ident_t id) +{ + struct am335x_clk_details *walker; + + for (walker = g_am335x_clk_details; walker->id != INVALID_CLK_IDENT; walker++) { + if (id == walker->id) + return (walker); + } + + return NULL; +} + +static int +am335x_clk_noop_activate(struct ti_clock_dev *clkdev) +{ + + return (0); +} + +static int +am335x_clk_generic_activate(struct ti_clock_dev *clkdev) +{ + struct am335x_prcm_softc *sc = am335x_prcm_sc; + struct am335x_clk_details* clk_details; + + if (sc == NULL) + return ENXIO; + + clk_details = am335x_clk_details(clkdev->id); + + if (clk_details == NULL) + return (ENXIO); + + /* set *_CLKCTRL register MODULEMODE[1:0] to enable(2) */ + prcm_write_4(clk_details->clkctrl_reg, 2); + while ((prcm_read_4(clk_details->clkctrl_reg) & 0x3) != 2) + DELAY(10); + + return (0); +} + +static int +am335x_clk_gpio_activate(struct ti_clock_dev *clkdev) +{ + struct am335x_prcm_softc *sc = am335x_prcm_sc; + struct am335x_clk_details* clk_details; + + if (sc == NULL) + return ENXIO; + + clk_details = am335x_clk_details(clkdev->id); + + if (clk_details == NULL) + return (ENXIO); + + /* set *_CLKCTRL register MODULEMODE[1:0] to enable(2) */ + /* set *_CLKCTRL register OPTFCLKEN_GPIO_1_G DBCLK[18] to FCLK_EN(1) */ + prcm_write_4(clk_details->clkctrl_reg, 2 | (1 << 18)); + while ((prcm_read_4(clk_details->clkctrl_reg) & + (3 | (1 << 18) )) != (2 | (1 << 18))) + DELAY(10); + + return (0); +} + +static int +am335x_clk_noop_deactivate(struct ti_clock_dev *clkdev) +{ + + return(0); +} + +static int +am335x_clk_generic_deactivate(struct ti_clock_dev *clkdev) +{ + struct am335x_prcm_softc *sc = am335x_prcm_sc; + struct am335x_clk_details* clk_details; + + if (sc == NULL) + return ENXIO; + + clk_details = am335x_clk_details(clkdev->id); + + if (clk_details == NULL) + return (ENXIO); + + /* set *_CLKCTRL register MODULEMODE[1:0] to disable(0) */ + prcm_write_4(clk_details->clkctrl_reg, 0); + while ((prcm_read_4(clk_details->clkctrl_reg) & 0x3) != 0) + DELAY(10); + + return (0); +} + +static int +am335x_clk_noop_set_source(struct ti_clock_dev *clkdev, clk_src_t clksrc) +{ + + return (0); +} + +static int +am335x_clk_generic_set_source(struct ti_clock_dev *clkdev, clk_src_t clksrc) +{ + struct am335x_prcm_softc *sc = am335x_prcm_sc; + struct am335x_clk_details* clk_details; + uint32_t reg; + + if (sc == NULL) + return ENXIO; + + clk_details = am335x_clk_details(clkdev->id); + + if (clk_details == NULL) + return (ENXIO); + + switch (clksrc) { + case EXT_CLK: + reg = 0; /* SEL2: TCLKIN clock */ + break; + case SYSCLK_CLK: + reg = 1; /* SEL1: CLK_M_OSC clock */ + break; + case F32KHZ_CLK: + reg = 2; /* SEL3: CLK_32KHZ clock */ + break; + default: + return (ENXIO); + } + + prcm_write_4(clk_details->clksel_reg, reg); + while ((prcm_read_4(clk_details->clksel_reg) & 0x3) != reg) + DELAY(10); + + return (0); +} + +static int +am335x_clk_hsmmc_get_source_freq(struct ti_clock_dev *clkdev, unsigned int *freq) +{ + *freq = 96000000; + return (0); +} + +static int +am335x_clk_get_sysclk_freq(struct ti_clock_dev *clkdev, unsigned int *freq) +{ + uint32_t ctrl_status; + + /* Read the input clock freq from the control module. */ + if (ti_scm_reg_read_4(SCM_CTRL_STATUS, &ctrl_status)) + return (ENXIO); + + switch ((ctrl_status>>22) & 0x3) { + case 0x0: + /* 19.2Mhz */ + *freq = 19200000; + break; + case 0x1: + /* 24Mhz */ + *freq = 24000000; + break; + case 0x2: + /* 25Mhz */ + *freq = 25000000; + break; + case 0x3: + /* 26Mhz */ + *freq = 26000000; + break; + } + + return (0); +} + +#define DPLL_BYP_CLKSEL(reg) ((reg>>23) & 1) +#define DPLL_DIV(reg) ((reg & 0x7f)+1) +#define DPLL_MULT(reg) ((reg>>8) & 0x7FF) +#define DPLL_MAX_MUL 0x800 +#define DPLL_MAX_DIV 0x80 + +static int +am335x_clk_get_arm_fclk_freq(struct ti_clock_dev *clkdev, unsigned int *freq) +{ + uint32_t reg; + uint32_t sysclk; + + reg = prcm_read_4(CM_WKUP_CM_CLKSEL_DPLL_MPU); + + /*Check if we are running in bypass */ + if (DPLL_BYP_CLKSEL(reg)) + return ENXIO; + + am335x_clk_get_sysclk_freq(NULL, &sysclk); + *freq = DPLL_MULT(reg) * (sysclk / DPLL_DIV(reg)); + return(0); +} + +static int +am335x_clk_get_arm_disp_freq(struct ti_clock_dev *clkdev, unsigned int *freq) +{ + uint32_t reg; + uint32_t sysclk; + + reg = prcm_read_4(CM_WKUP_CM_CLKSEL_DPLL_DISP); + + /*Check if we are running in bypass */ + if (DPLL_BYP_CLKSEL(reg)) + return ENXIO; + + am335x_clk_get_sysclk_freq(NULL, &sysclk); + *freq = DPLL_MULT(reg) * (sysclk / DPLL_DIV(reg)); + return(0); +} + +static int +am335x_clk_set_arm_disp_freq(struct ti_clock_dev *clkdev, unsigned int freq) +{ + uint32_t sysclk; + uint32_t mul, div; + uint32_t i, j; + unsigned int delta, min_delta; + + am335x_clk_get_sysclk_freq(NULL, &sysclk); + + /* Bypass mode */ + prcm_write_4(CM_WKUP_CM_CLKMODE_DPLL_DISP, 0x4); + + /* Make sure it's in bypass mode */ + while (!(prcm_read_4(CM_WKUP_CM_IDLEST_DPLL_DISP) + & (1 << 8))) + DELAY(10); + + /* Dumb and non-optimal implementation */ + min_delta = freq; + for (i = 1; i < DPLL_MAX_MUL; i++) { + for (j = 1; j < DPLL_MAX_DIV; j++) { + delta = abs(freq - i*(sysclk/j)); + if (delta < min_delta) { + mul = i; + div = j; + min_delta = delta; + } + if (min_delta == 0) + break; + } + } + + prcm_write_4(CM_WKUP_CM_CLKSEL_DPLL_DISP, (mul << 8) | (div - 1)); + + /* Locked mode */ + prcm_write_4(CM_WKUP_CM_CLKMODE_DPLL_DISP, 0x7); + + int timeout = 10000; + while ((!(prcm_read_4(CM_WKUP_CM_IDLEST_DPLL_DISP) + & (1 << 0))) && timeout--) + DELAY(10); + + return(0); +} + +static void +am335x_prcm_reset(void) +{ + prcm_write_4(PRM_RSTCTRL, (1<<1)); +} + +static int +am335x_clk_cpsw_activate(struct ti_clock_dev *clkdev) +{ + struct am335x_prcm_softc *sc = am335x_prcm_sc; + + if (sc == NULL) + return ENXIO; + + /* set MODULENAME to ENABLE */ + prcm_write_4(CM_PER_CPGMAC0_CLKCTRL, 2); + + /* wait for IDLEST to become Func(0) */ + while(prcm_read_4(CM_PER_CPGMAC0_CLKCTRL) & (3<<16)); + + /*set CLKTRCTRL to SW_WKUP(2) */ + prcm_write_4(CM_PER_CPSW_CLKSTCTRL, 2); + + /* wait for 125 MHz OCP clock to become active */ + while((prcm_read_4(CM_PER_CPSW_CLKSTCTRL) & (1<<4)) == 0); + return(0); +} + +static int +am335x_clk_musb0_activate(struct ti_clock_dev *clkdev) +{ + struct am335x_prcm_softc *sc = am335x_prcm_sc; + + if (sc == NULL) + return ENXIO; + + /* set ST_DPLL_CLKDCOLDO(9) to CLK_GATED(1) */ + /* set DPLL_CLKDCOLDO_GATE_CTRL(8) to CLK_ENABLE(1)*/ + prcm_write_4(CM_WKUP_CM_CLKDCOLDO_DPLL_PER, 0x300); + + /*set MODULEMODE to ENABLE(2) */ + prcm_write_4(CM_PER_USB0_CLKCTRL, 2); + + /* wait for MODULEMODE to become ENABLE(2) */ + while ((prcm_read_4(CM_PER_USB0_CLKCTRL) & 0x3) != 2) + DELAY(10); + + /* wait for IDLEST to become Func(0) */ + while(prcm_read_4(CM_PER_USB0_CLKCTRL) & (3<<16)) + DELAY(10); + + return(0); +} + +static int +am335x_clk_lcdc_activate(struct ti_clock_dev *clkdev) +{ + struct am335x_prcm_softc *sc = am335x_prcm_sc; + + if (sc == NULL) + return (ENXIO); + + /* + * For now set frequency to 2*VGA_PIXEL_CLOCK + */ + am335x_clk_set_arm_disp_freq(clkdev, 25175000*2); + + /*set MODULEMODE to ENABLE(2) */ + prcm_write_4(CM_PER_LCDC_CLKCTRL, 2); + + /* wait for MODULEMODE to become ENABLE(2) */ + while ((prcm_read_4(CM_PER_LCDC_CLKCTRL) & 0x3) != 2) + DELAY(10); + + /* wait for IDLEST to become Func(0) */ + while(prcm_read_4(CM_PER_LCDC_CLKCTRL) & (3<<16)) + DELAY(10); + + return (0); +} + +static int +am335x_clk_pruss_activate(struct ti_clock_dev *clkdev) +{ + struct am335x_prcm_softc *sc = am335x_prcm_sc; + + if (sc == NULL) + return (ENXIO); + + /* Set MODULEMODE to ENABLE(2) */ + prcm_write_4(CM_PER_PRUSS_CLKCTRL, 2); + + /* Wait for MODULEMODE to become ENABLE(2) */ + while ((prcm_read_4(CM_PER_PRUSS_CLKCTRL) & 0x3) != 2) + DELAY(10); + + /* Set CLKTRCTRL to SW_WKUP(2) */ + prcm_write_4(CM_PER_PRUSS_CLKSTCTRL, 2); + + /* Wait for the 200 MHz OCP clock to become active */ + while ((prcm_read_4(CM_PER_PRUSS_CLKSTCTRL) & (1<<4)) == 0) + DELAY(10); + + /* Wait for the 200 MHz IEP clock to become active */ + while ((prcm_read_4(CM_PER_PRUSS_CLKSTCTRL) & (1<<5)) == 0) + DELAY(10); + + /* Wait for the 192 MHz UART clock to become active */ + while ((prcm_read_4(CM_PER_PRUSS_CLKSTCTRL) & (1<<6)) == 0) + DELAY(10); + + /* Select L3F as OCP clock */ + prcm_write_4(CLKSEL_PRUSS_OCP_CLK, 0); + while ((prcm_read_4(CLKSEL_PRUSS_OCP_CLK) & 0x3) != 0) + DELAY(10); + + /* Clear the RESET bit */ + prcm_write_4(PRM_PER_RSTCTRL, prcm_read_4(PRM_PER_RSTCTRL) & ~2); + + return (0); +} diff --git a/freebsd/sys/arm/ti/am335x/am335x_scm.h b/freebsd/sys/arm/ti/am335x/am335x_scm.h new file mode 100644 index 00000000..6116be06 --- /dev/null +++ b/freebsd/sys/arm/ti/am335x/am335x_scm.h @@ -0,0 +1,49 @@ +/*- + * Copyright (c) 2012 Oleksandr Tymoshenko + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 THE AUTHOR OR CONTRIBUTORS 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. + * + * $FreeBSD$ + */ +#ifndef __AM335X_SCM_H__ +#define __AM335X_SCM_H__ + +/* AM335x-specific registers for control module (scm) */ +#define SCM_CTRL_STATUS 0x40 +#define SCM_BGAP_CTRL 0x448 +#define SCM_BGAP_TEMP_MASK 0xff +#define SCM_BGAP_TEMP_SHIFT 8 +#define SCM_BGAP_BGOFF (1 << 6) +#define SCM_BGAP_SOC (1 << 4) +#define SCM_BGAP_CLRZ (1 << 3) +#define SCM_BGAP_CONTCONV (1 << 2) +#define SCM_BGAP_EOCZ (1 << 1) +#define SCM_USB_CTRL0 0x620 +#define SCM_USB_STS0 0x624 +#define SCM_USB_CTRL1 0x628 +#define SCM_USB_STS1 0x62C +#define SCM_MAC_ID0_LO 0x630 +#define SCM_MAC_ID0_HI 0x634 +#define SCM_PWMSS_CTRL 0x664 + +#endif /* __AM335X_SCM_H__ */ diff --git a/freebsd/sys/arm/ti/am335x/am335x_usbss.c b/freebsd/sys/arm/ti/am335x/am335x_usbss.c new file mode 100644 index 00000000..8369171d --- /dev/null +++ b/freebsd/sys/arm/ti/am335x/am335x_usbss.c @@ -0,0 +1,226 @@ +#include + +/*- + * Copyright (c) 2013 Oleksandr Tymoshenko + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 THE AUTHOR OR CONTRIBUTORS 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. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include + +#define AM335X_USB_PORTS 2 + +#define USBSS_REVREG 0x00 +#define USBSS_SYSCONFIG 0x10 +#define USBSS_SYSCONFIG_SRESET 1 + +#define USBCTRL_REV 0x00 +#define USBCTRL_CTRL 0x14 +#define USBCTRL_STAT 0x18 +#define USBCTRL_IRQ_STAT0 0x30 +#define IRQ_STAT0_RXSHIFT 16 +#define IRQ_STAT0_TXSHIFT 0 +#define USBCTRL_IRQ_STAT1 0x34 +#define IRQ_STAT1_DRVVBUS (1 << 8) +#define USBCTRL_INTEN_SET0 0x38 +#define USBCTRL_INTEN_SET1 0x3C +#define USBCTRL_INTEN_USB_ALL 0x1ff +#define USBCTRL_INTEN_USB_SOF (1 << 3) +#define USBCTRL_INTEN_CLR0 0x40 +#define USBCTRL_INTEN_CLR1 0x44 +#define USBCTRL_UTMI 0xE0 +#define USBCTRL_UTMI_FSDATAEXT (1 << 1) +#define USBCTRL_MODE 0xE8 +#define USBCTRL_MODE_IDDIG (1 << 8) +#define USBCTRL_MODE_IDDIGMUX (1 << 7) + +#define USBSS_WRITE4(sc, reg, val) \ + bus_write_4((sc)->sc_mem_res, (reg), (val)) +#define USBSS_READ4(sc, reg) \ + bus_read_4((sc)->sc_mem_res, (reg)) + +static device_probe_t usbss_probe; +static device_attach_t usbss_attach; +static device_detach_t usbss_detach; + +struct usbss_softc { + struct simplebus_softc simplebus_sc; + struct resource *sc_mem_res; + int sc_mem_rid; +}; + +static int +usbss_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_is_compatible(dev, "ti,am33xx-usb")) + return (ENXIO); + + device_set_desc(dev, "TI AM33xx integrated USB OTG controller"); + + return (BUS_PROBE_DEFAULT); +} + +static int +usbss_attach(device_t dev) +{ + struct usbss_softc *sc = device_get_softc(dev); + int i; + uint32_t rev; + phandle_t node; + + /* Request the memory resources */ + sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, + &sc->sc_mem_rid, RF_ACTIVE); + if (sc->sc_mem_res == NULL) { + device_printf(dev, + "Error: could not allocate mem resources\n"); + return (ENXIO); + } + + /* Enable device clocks. */ + ti_prcm_clk_enable(MUSB0_CLK); + + /* + * Reset USBSS, USB0 and USB1. + * The registers of USB subsystem must not be accessed while the + * reset pulse is active (200ns). + */ + USBSS_WRITE4(sc, USBSS_SYSCONFIG, USBSS_SYSCONFIG_SRESET); + DELAY(100); + i = 10; + while (USBSS_READ4(sc, USBSS_SYSCONFIG) & USBSS_SYSCONFIG_SRESET) { + DELAY(100); + if (i-- == 0) { + device_printf(dev, "reset timeout.\n"); + return (ENXIO); + } + } + + /* Read the module revision. */ + rev = USBSS_READ4(sc, USBSS_REVREG); + device_printf(dev, "TI AM335X USBSS v%d.%d.%d\n", + (rev >> 8) & 7, (rev >> 6) & 3, rev & 63); + + node = ofw_bus_get_node(dev); + + if (node == -1) { + usbss_detach(dev); + return (ENXIO); + } + + simplebus_init(dev, node); + + /* + * Allow devices to identify. + */ + bus_generic_probe(dev); + + /* + * Now walk the OFW tree and attach top-level devices. + */ + for (node = OF_child(node); node > 0; node = OF_peer(node)) + simplebus_add_device(dev, node, 0, NULL, -1, NULL); + + return (bus_generic_attach(dev)); +} + +static int +usbss_detach(device_t dev) +{ + struct usbss_softc *sc = device_get_softc(dev); + + /* Free resources if any */ + if (sc->sc_mem_res) + bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_mem_rid, + sc->sc_mem_res); + + /* during module unload there are lots of children leftover */ + device_delete_children(dev); + + return (0); +} + +static device_method_t usbss_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, usbss_probe), + DEVMETHOD(device_attach, usbss_attach), + DEVMETHOD(device_detach, usbss_detach), + DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_resume, bus_generic_resume), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + + DEVMETHOD_END +}; + +DEFINE_CLASS_1(usbss, usbss_driver, usbss_methods, + sizeof(struct usbss_softc), simplebus_driver); +static devclass_t usbss_devclass; +DRIVER_MODULE(usbss, simplebus, usbss_driver, usbss_devclass, 0, 0); +MODULE_DEPEND(usbss, usb, 1, 1, 1); diff --git a/freebsd/sys/arm/ti/ti_cpuid.h b/freebsd/sys/arm/ti/ti_cpuid.h new file mode 100644 index 00000000..715f080e --- /dev/null +++ b/freebsd/sys/arm/ti/ti_cpuid.h @@ -0,0 +1,83 @@ +/*- + * Copyright (c) 2011 + * Ben Gray . + * 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. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``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 AUTHOR OR CONTRIBUTORS 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. + * + * $FreeBSD$ + */ + +#ifndef _TI_CPUID_H_ +#define _TI_CPUID_H_ + +#define OMAP_MAKEREV(d, a, b, c) \ + (uint32_t)(((d) << 16) | (((a) & 0xf) << 8) | (((b) & 0xf) << 4) | ((c) & 0xf)) + +#define OMAP_REV_DEVICE(x) (((x) >> 16) & 0xffff) +#define OMAP_REV_MAJOR(x) (((x) >> 8) & 0xf) +#define OMAP_REV_MINOR(x) (((x) >> 4) & 0xf) +#define OMAP_REV_MINOR_MINOR(x) (((x) >> 0) & 0xf) + +#define OMAP3350_DEV 0x3530 +#define OMAP3350_REV_ES1_0 OMAP_MAKEREV(OMAP3350_DEV, 1, 0, 0) +#define OMAP3530_REV_ES2_0 OMAP_MAKEREV(OMAP3350_DEV, 2, 0, 0) +#define OMAP3530_REV_ES2_1 OMAP_MAKEREV(OMAP3350_DEV, 2, 1, 0) +#define OMAP3530_REV_ES3_0 OMAP_MAKEREV(OMAP3350_DEV, 3, 0, 0) +#define OMAP3530_REV_ES3_1 OMAP_MAKEREV(OMAP3350_DEV, 3, 1, 0) +#define OMAP3530_REV_ES3_1_2 OMAP_MAKEREV(OMAP3350_DEV, 3, 1, 2) + +#define OMAP4430_DEV 0x4430 +#define OMAP4430_REV_ES1_0 OMAP_MAKEREV(OMAP4430_DEV, 1, 0, 0) +#define OMAP4430_REV_ES2_0 OMAP_MAKEREV(OMAP4430_DEV, 2, 0, 0) +#define OMAP4430_REV_ES2_1 OMAP_MAKEREV(OMAP4430_DEV, 2, 1, 0) +#define OMAP4430_REV_ES2_2 OMAP_MAKEREV(OMAP4430_DEV, 2, 2, 0) +#define OMAP4430_REV_ES2_3 OMAP_MAKEREV(OMAP4430_DEV, 2, 3, 0) +#define OMAP4430_REV_UNKNOWN OMAP_MAKEREV(OMAP4430_DEV, 9, 9, 9) + +#define OMAP4460_DEV 0x4460 +#define OMAP4460_REV_ES1_0 OMAP_MAKEREV(OMAP4460_DEV, 1, 0, 0) +#define OMAP4460_REV_ES1_1 OMAP_MAKEREV(OMAP4460_DEV, 1, 1, 0) +#define OMAP4460_REV_UNKNOWN OMAP_MAKEREV(OMAP4460_DEV, 9, 9, 9) + +#define OMAP4470_DEV 0x4470 +#define OMAP4470_REV_ES1_0 OMAP_MAKEREV(OMAP4470_DEV, 1, 0, 0) +#define OMAP4470_REV_UNKNOWN OMAP_MAKEREV(OMAP4470_DEV, 9, 9, 9) + +#define OMAP_UNKNOWN_DEV OMAP_MAKEREV(0x9999, 9, 9, 9) + +#define AM335X_DEVREV(x) ((x) >> 28) + +#define CHIP_OMAP_4 0 +#define CHIP_AM335X 1 + +extern int _ti_chip; + +static __inline int ti_chip(void) +{ + KASSERT(_ti_chip != -1, ("Can't determine TI Chip")); + return _ti_chip; +} + +uint32_t ti_revision(void); + +#endif /* _TI_CPUID_H_ */ diff --git a/freebsd/sys/arm/ti/ti_prcm.c b/freebsd/sys/arm/ti/ti_prcm.c new file mode 100644 index 00000000..b4dd03fb --- /dev/null +++ b/freebsd/sys/arm/ti/ti_prcm.c @@ -0,0 +1,357 @@ +#include + +/* + * Copyright (c) 2010 + * Ben Gray . + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ben Gray. + * 4. 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. + */ + +/** + * Power, Reset and Clock Management Module + * + * This is a very simple driver wrapper around the PRCM set of registers in + * the OMAP3 chip. It allows you to turn on and off things like the functional + * and interface clocks to the various on-chip modules. + * + */ +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +/** + * ti_*_clk_devmap - Array of clock devices, should be defined one per SoC + * + * This array is typically defined in one of the targeted *_prcm_clk.c + * files and is specific to the given SoC platform. Each entry in the array + * corresponds to an individual clock device. + */ +extern struct ti_clock_dev ti_omap4_clk_devmap[]; +extern struct ti_clock_dev ti_am335x_clk_devmap[]; + +/** + * ti_prcm_clk_dev - returns a pointer to the clock device with given id + * @clk: the ID of the clock device to get + * + * Simply iterates through the clk_devmap global array and returns a pointer + * to the clock device if found. + * + * LOCKING: + * None + * + * RETURNS: + * The pointer to the clock device on success, on failure NULL is returned. + */ +static struct ti_clock_dev * +ti_prcm_clk_dev(clk_ident_t clk) +{ + struct ti_clock_dev *clk_dev; + + /* Find the clock within the devmap - it's a bit inefficent having a for + * loop for this, but this function should only called when a driver is + * being activated so IMHO not a big issue. + */ + clk_dev = NULL; + switch(ti_chip()) { +#ifdef SOC_OMAP4 + case CHIP_OMAP_4: + clk_dev = &(ti_omap4_clk_devmap[0]); + break; +#endif +#ifdef SOC_TI_AM335X + case CHIP_AM335X: + clk_dev = &(ti_am335x_clk_devmap[0]); + break; +#endif + } + if (clk_dev == NULL) + panic("No clock devmap found"); + while (clk_dev->id != INVALID_CLK_IDENT) { + if (clk_dev->id == clk) { + return (clk_dev); + } + clk_dev++; + } + + /* Sanity check we managed to find the clock */ + printf("ti_prcm: Failed to find clock device (%d)\n", clk); + return (NULL); +} + +/** + * ti_prcm_clk_valid - enables a clock for a particular module + * @clk: identifier for the module to enable, see ti_prcm.h for a list + * of possible modules. + * Example: OMAP3_MODULE_MMC1_ICLK or OMAP3_MODULE_GPTIMER10_FCLK. + * + * This function can enable either a functional or interface clock. + * + * The real work done to enable the clock is really done in the callback + * function associated with the clock, this function is simply a wrapper + * around that. + * + * LOCKING: + * Internally locks the driver context. + * + * RETURNS: + * Returns 0 on success or positive error code on failure. + */ +int +ti_prcm_clk_valid(clk_ident_t clk) +{ + int ret = 0; + + if (ti_prcm_clk_dev(clk) == NULL) + ret = EINVAL; + + return (ret); +} + + +/** + * ti_prcm_clk_enable - enables a clock for a particular module + * @clk: identifier for the module to enable, see ti_prcm.h for a list + * of possible modules. + * Example: OMAP3_MODULE_MMC1_ICLK or OMAP3_MODULE_GPTIMER10_FCLK. + * + * This function can enable either a functional or interface clock. + * + * The real work done to enable the clock is really done in the callback + * function associated with the clock, this function is simply a wrapper + * around that. + * + * LOCKING: + * Internally locks the driver context. + * + * RETURNS: + * Returns 0 on success or positive error code on failure. + */ +int +ti_prcm_clk_enable(clk_ident_t clk) +{ + struct ti_clock_dev *clk_dev; + int ret; + + /* Find the clock within the devmap - it's a bit inefficent having a for + * loop for this, but this function should only called when a driver is + * being activated so IMHO not a big issue. + */ + clk_dev = ti_prcm_clk_dev(clk); + + /* Sanity check we managed to find the clock */ + if (clk_dev == NULL) + return (EINVAL); + + /* Activate the clock */ + if (clk_dev->clk_activate) + ret = clk_dev->clk_activate(clk_dev); + else + ret = EINVAL; + + return (ret); +} + + +/** + * ti_prcm_clk_disable - disables a clock for a particular module + * @clk: identifier for the module to enable, see ti_prcm.h for a list + * of possible modules. + * Example: OMAP3_MODULE_MMC1_ICLK or OMAP3_MODULE_GPTIMER10_FCLK. + * + * This function can enable either a functional or interface clock. + * + * The real work done to enable the clock is really done in the callback + * function associated with the clock, this function is simply a wrapper + * around that. + * + * LOCKING: + * Internally locks the driver context. + * + * RETURNS: + * Returns 0 on success or positive error code on failure. + */ +int +ti_prcm_clk_disable(clk_ident_t clk) +{ + struct ti_clock_dev *clk_dev; + int ret; + + /* Find the clock within the devmap - it's a bit inefficent having a for + * loop for this, but this function should only called when a driver is + * being activated so IMHO not a big issue. + */ + clk_dev = ti_prcm_clk_dev(clk); + + /* Sanity check we managed to find the clock */ + if (clk_dev == NULL) + return (EINVAL); + + /* Activate the clock */ + if (clk_dev->clk_deactivate) + ret = clk_dev->clk_deactivate(clk_dev); + else + ret = EINVAL; + + return (ret); +} + +/** + * ti_prcm_clk_set_source - sets the source + * @clk: identifier for the module to enable, see ti_prcm.h for a list + * of possible modules. + * Example: OMAP3_MODULE_MMC1_ICLK or OMAP3_MODULE_GPTIMER10_FCLK. + * + * This function can enable either a functional or interface clock. + * + * The real work done to enable the clock is really done in the callback + * function associated with the clock, this function is simply a wrapper + * around that. + * + * LOCKING: + * Internally locks the driver context. + * + * RETURNS: + * Returns 0 on success or positive error code on failure. + */ +int +ti_prcm_clk_set_source(clk_ident_t clk, clk_src_t clksrc) +{ + struct ti_clock_dev *clk_dev; + int ret; + + /* Find the clock within the devmap - it's a bit inefficent having a for + * loop for this, but this function should only called when a driver is + * being activated so IMHO not a big issue. + */ + clk_dev = ti_prcm_clk_dev(clk); + + /* Sanity check we managed to find the clock */ + if (clk_dev == NULL) + return (EINVAL); + + /* Activate the clock */ + if (clk_dev->clk_set_source) + ret = clk_dev->clk_set_source(clk_dev, clksrc); + else + ret = EINVAL; + + return (ret); +} + + +/** + * ti_prcm_clk_get_source_freq - gets the source clock frequency + * @clk: identifier for the module to enable, see ti_prcm.h for a list + * of possible modules. + * @freq: pointer to an integer that upon return will contain the src freq + * + * This function returns the frequency of the source clock. + * + * The real work done to enable the clock is really done in the callback + * function associated with the clock, this function is simply a wrapper + * around that. + * + * LOCKING: + * Internally locks the driver context. + * + * RETURNS: + * Returns 0 on success or positive error code on failure. + */ +int +ti_prcm_clk_get_source_freq(clk_ident_t clk, unsigned int *freq) +{ + struct ti_clock_dev *clk_dev; + int ret; + + /* Find the clock within the devmap - it's a bit inefficent having a for + * loop for this, but this function should only called when a driver is + * being activated so IMHO not a big issue. + */ + clk_dev = ti_prcm_clk_dev(clk); + + /* Sanity check we managed to find the clock */ + if (clk_dev == NULL) + return (EINVAL); + + /* Get the source frequency of the clock */ + if (clk_dev->clk_get_source_freq) + ret = clk_dev->clk_get_source_freq(clk_dev, freq); + else + ret = EINVAL; + + return (ret); +} + +/** + * ti_prcm_clk_set_source_freq - sets the source clock frequency as close to freq as possible + * @clk: identifier for the module to enable, see ti_prcm.h for a list + * of possible modules. + * @freq: requested freq + * + * LOCKING: + * Internally locks the driver context. + * + * RETURNS: + * Returns 0 on success or positive error code on failure. + */ +int +ti_prcm_clk_set_source_freq(clk_ident_t clk, unsigned int freq) +{ + struct ti_clock_dev *clk_dev; + int ret; + + clk_dev = ti_prcm_clk_dev(clk); + + /* Sanity check we managed to find the clock */ + if (clk_dev == NULL) + return (EINVAL); + + /* Get the source frequency of the clock */ + if (clk_dev->clk_set_source_freq) + ret = clk_dev->clk_set_source_freq(clk_dev, freq); + else + ret = EINVAL; + + return (ret); +} diff --git a/freebsd/sys/arm/ti/ti_prcm.h b/freebsd/sys/arm/ti/ti_prcm.h new file mode 100644 index 00000000..61b69607 --- /dev/null +++ b/freebsd/sys/arm/ti/ti_prcm.h @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2010 + * Ben Gray . + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ben Gray. + * 4. 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. + * + * $FreeBSD$ + */ + + +/* + * Texas Instruments - OMAP3xxx series processors + * + * Reference: + * OMAP35x Applications Processor + * Technical Reference Manual + * (omap35xx_techref.pdf) + */ +#ifndef _TI_PRCM_H_ +#define _TI_PRCM_H_ + +typedef enum { + + INVALID_CLK_IDENT = 0, + + /* System clocks, typically you can only call ti_prcm_clk_get_source_freq() + * on these clocks as they are enabled by default. + */ + SYS_CLK = 1, + + /* The MPU (ARM) core clock */ + MPU_CLK = 20, + + /* MMC modules */ + MMC1_CLK = 100, + MMC2_CLK, + MMC3_CLK, + MMC4_CLK, + MMC5_CLK, + MMC6_CLK, + + /* I2C modules */ + I2C1_CLK = 200, + I2C2_CLK, + I2C3_CLK, + I2C4_CLK, + I2C5_CLK, + + /* USB module(s) */ + USBTLL_CLK = 300, + USBHSHOST_CLK, + USBFSHOST_CLK, + USBP1_PHY_CLK, + USBP2_PHY_CLK, + USBP1_UTMI_CLK, + USBP2_UTMI_CLK, + USBP1_HSIC_CLK, + USBP2_HSIC_CLK, + + /* UART modules */ + UART1_CLK = 400, + UART2_CLK, + UART3_CLK, + UART4_CLK, + UART5_CLK, + UART6_CLK, + UART7_CLK, + UART8_CLK, + UART9_CLK, + + /* General purpose timer modules */ + TIMER1_CLK = 500, + TIMER2_CLK, + TIMER3_CLK, + TIMER4_CLK, + TIMER5_CLK, + TIMER6_CLK, + TIMER7_CLK, + TIMER8_CLK, + TIMER9_CLK, + TIMER10_CLK, + TIMER11_CLK, + TIMER12_CLK, + + /* McBSP module(s) */ + MCBSP1_CLK = 600, + MCBSP2_CLK, + MCBSP3_CLK, + MCBSP4_CLK, + MCBSP5_CLK, + + /* General purpose I/O modules */ + GPIO1_CLK = 700, + GPIO2_CLK, + GPIO3_CLK, + GPIO4_CLK, + GPIO5_CLK, + GPIO6_CLK, + GPIO7_CLK, + + /* sDMA module */ + SDMA_CLK = 800, + + /* CPSW modules */ + CPSW_CLK = 1000, + + /* Mentor USB modules */ + MUSB0_CLK = 1100, + + /* EDMA module */ + EDMA_TPCC_CLK = 1200, + EDMA_TPTC0_CLK, + EDMA_TPTC1_CLK, + EDMA_TPTC2_CLK, + + /* LCD controller module */ + LCDC_CLK = 1300, + + /* PWM modules */ + PWMSS0_CLK = 1400, + PWMSS1_CLK, + PWMSS2_CLK, + + /* Mailbox modules */ + MAILBOX0_CLK = 1500, + + /* Spinlock modules */ + SPINLOCK0_CLK = 1600, + + PRUSS_CLK = 1700, + + TSC_ADC_CLK = 1800, + + /* RTC module */ + RTC_CLK = 1900, + + /* McSPI */ + SPI0_CLK = 2000, + SPI1_CLK, +} clk_ident_t; + +/* + * + */ +typedef enum { + SYSCLK_CLK, /* System clock */ + EXT_CLK, + + F32KHZ_CLK, /* 32KHz clock */ + F48MHZ_CLK, /* 48MHz clock */ + F64MHZ_CLK, /* 64MHz clock */ + F96MHZ_CLK, /* 96MHz clock */ + +} clk_src_t; + +struct ti_clock_dev { + /* The profile of the timer */ + clk_ident_t id; + + /* A bunch of callbacks associated with the clock device */ + int (*clk_activate)(struct ti_clock_dev *clkdev); + int (*clk_deactivate)(struct ti_clock_dev *clkdev); + int (*clk_set_source)(struct ti_clock_dev *clkdev, + clk_src_t clksrc); + int (*clk_accessible)(struct ti_clock_dev *clkdev); + int (*clk_set_source_freq)(struct ti_clock_dev *clkdev, + unsigned int freq); + int (*clk_get_source_freq)(struct ti_clock_dev *clkdev, + unsigned int *freq); +}; + +int ti_prcm_clk_valid(clk_ident_t clk); +int ti_prcm_clk_enable(clk_ident_t clk); +int ti_prcm_clk_disable(clk_ident_t clk); +int ti_prcm_clk_accessible(clk_ident_t clk); +int ti_prcm_clk_disable_autoidle(clk_ident_t clk); +int ti_prcm_clk_set_source(clk_ident_t clk, clk_src_t clksrc); +int ti_prcm_clk_set_source_freq(clk_ident_t clk, unsigned int freq); +int ti_prcm_clk_get_source_freq(clk_ident_t clk, unsigned int *freq); +void ti_prcm_reset(void); + +#endif /* _TI_PRCM_H_ */ diff --git a/freebsd/sys/arm/ti/ti_scm.c b/freebsd/sys/arm/ti/ti_scm.c new file mode 100644 index 00000000..2e54262e --- /dev/null +++ b/freebsd/sys/arm/ti/ti_scm.c @@ -0,0 +1,176 @@ +#include + +/* + * Copyright (c) 2010 + * Ben Gray . + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ben Gray. + * 4. 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. + */ + +/** + * SCM - System Control Module + * + * Hopefully in the end this module will contain a bunch of utility functions + * for configuring and querying the general system control registers, but for + * now it only does pin(pad) multiplexing. + * + * This is different from the GPIO module in that it is used to configure the + * pins between modules not just GPIO input/output. + * + * This file contains the generic top level driver, however it relies on chip + * specific settings and therefore expects an array of ti_scm_padconf structs + * call ti_padconf_devmap to be located somewhere in the kernel. + * + */ +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include "ti_scm.h" + +static struct resource_spec ti_scm_res_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, /* Control memory window */ + { -1, 0 } +}; + +static struct ti_scm_softc *ti_scm_sc; + +#define ti_scm_read_4(sc, reg) \ + bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg)) +#define ti_scm_write_4(sc, reg, val) \ + bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val)) + +/* + * Device part of OMAP SCM driver + */ +static int +ti_scm_probe(device_t dev) +{ + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_is_compatible(dev, "syscon")) + return (ENXIO); + + if (ti_scm_sc) { + return (EEXIST); + } + + device_set_desc(dev, "TI Control Module"); + return (BUS_PROBE_DEFAULT); +} + +/** + * ti_scm_attach - attaches the timer to the simplebus + * @dev: new device + * + * Reserves memory and interrupt resources, stores the softc structure + * globally and registers both the timecount and eventtimer objects. + * + * RETURNS + * Zero on success or ENXIO if an error occuried. + */ +static int +ti_scm_attach(device_t dev) +{ + struct ti_scm_softc *sc = device_get_softc(dev); + + sc->sc_dev = dev; + + if (bus_alloc_resources(dev, ti_scm_res_spec, sc->sc_res)) { + device_printf(dev, "could not allocate resources\n"); + return (ENXIO); + } + + /* Global timer interface */ + sc->sc_bst = rman_get_bustag(sc->sc_res[0]); + sc->sc_bsh = rman_get_bushandle(sc->sc_res[0]); + + ti_scm_sc = sc; + + /* Attach platform extensions, if any. */ + bus_generic_probe(dev); + + return (bus_generic_attach(dev)); +} + +int +ti_scm_reg_read_4(uint32_t reg, uint32_t *val) +{ + if (!ti_scm_sc) + return (ENXIO); + + *val = ti_scm_read_4(ti_scm_sc, reg); + return (0); +} + +int +ti_scm_reg_write_4(uint32_t reg, uint32_t val) +{ + if (!ti_scm_sc) + return (ENXIO); + + ti_scm_write_4(ti_scm_sc, reg, val); + return (0); +} + + +static device_method_t ti_scm_methods[] = { + DEVMETHOD(device_probe, ti_scm_probe), + DEVMETHOD(device_attach, ti_scm_attach), + + { 0, 0 } +}; + +static driver_t ti_scm_driver = { + "ti_scm", + ti_scm_methods, + sizeof(struct ti_scm_softc), +}; + +static devclass_t ti_scm_devclass; + +EARLY_DRIVER_MODULE(ti_scm, simplebus, ti_scm_driver, ti_scm_devclass, 0, 0, + BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); diff --git a/freebsd/sys/arm/ti/ti_scm.h b/freebsd/sys/arm/ti/ti_scm.h new file mode 100644 index 00000000..1342ddc6 --- /dev/null +++ b/freebsd/sys/arm/ti/ti_scm.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2010 + * Ben Gray . + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ben Gray. + * 4. 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. + * + * $FreeBSD$ + */ + + +/** + * Functions to configure the PIN multiplexing on the chip. + * + * This is different from the GPIO module in that it is used to configure the + * pins between modules not just GPIO input output. + * + */ +#ifndef _TI_SCM_H_ +#define _TI_SCM_H_ + +struct ti_scm_softc { + device_t sc_dev; + struct resource * sc_res[4]; + bus_space_tag_t sc_bst; + bus_space_handle_t sc_bsh; +}; + +int ti_scm_reg_read_4(uint32_t reg, uint32_t *val); +int ti_scm_reg_write_4(uint32_t reg, uint32_t val); + +#endif /* _TI_SCM_H_ */ diff --git a/freebsd/sys/arm/ti/tivar.h b/freebsd/sys/arm/ti/tivar.h new file mode 100644 index 00000000..248ad901 --- /dev/null +++ b/freebsd/sys/arm/ti/tivar.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2010 + * Ben Gray . + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ben Gray. + * 4. 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. + * + * $FreeBSD$ + */ + +#ifndef _TIVAR_H_ +#define _TIVAR_H_ + +/* board-dependent reset function implementation */ +extern void (*ti_cpu_reset)(void); + +#endif /* _TIVAR_H_ */ -- cgit v1.2.3