diff --git a/sys/arm/conf/BEAGLEBONE b/sys/arm/conf/BEAGLEBONE new file mode 100644 index 000000000000..1a6426dd324c --- /dev/null +++ b/sys/arm/conf/BEAGLEBONE @@ -0,0 +1,126 @@ +# BEAGLEBONE -- Custom configuration for the BeagleBone ARM development +# platform, check out http://www.beagleboard.org/bone +# +# For more information on this file, please read the handbook section on +# Kernel Configuration Files: +# +# http://www.FreeBSD.org/doc/en_US.ISO8859-1/books/handbook/kernelconfig-config.html +# +# The handbook is also available locally in /usr/share/doc/handbook +# if you've installed the doc distribution, otherwise always see the +# FreeBSD World Wide Web server (http://www.FreeBSD.org/) for the +# latest information. +# +# An exhaustive list of options and more detailed explanations of the +# device lines is also present in the ../../conf/NOTES and NOTES files. +# If you are in doubt as to the purpose or necessity of a line, check first +# in NOTES. +# +# $FreeBSD$ + +ident BEAGLEBONE + +include "../ti/am335x/std.beaglebone" + +makeoptions MODULES_OVERRIDE="" +makeoptions WITHOUT_MODULES="ahc" + +options HZ=100 +options SCHED_4BSD #4BSD scheduler +options INET #InterNETworking +options INET6 #IPv6 communications protocols +options FFS #Berkeley Fast Filesystem +options SOFTUPDATES #Enable FFS soft updates support +options UFS_ACL #Support for access control lists +options UFS_DIRHASH #Improve performance on big directories +options MSDOSFS #MSDOS Filesystem +options CD9660 #ISO 9660 Filesystem +options PROCFS #Process filesystem (requires PSEUDOFS) +options PSEUDOFS #Pseudo-filesystem framework +options COMPAT_43 #Compatible with BSD 4.3 [KEEP THIS!] +options SCSI_DELAY=5000 #Delay (in ms) before probing SCSI +options KTRACE #ktrace(1) support +options SYSVSHM #SYSV-style shared memory +options SYSVMSG #SYSV-style message queues +options SYSVSEM #SYSV-style semaphores +options _KPOSIX_PRIORITY_SCHEDULING #Posix P1003_1B real-time extensions +options KBD_INSTALL_CDEV # install a CDEV entry in /dev +options PREEMPTION + +# Debugging +makeoptions DEBUG=-g #Build kernel with gdb(1) debug symbols +options BREAK_TO_DEBUGGER +#options VERBOSE_SYSINIT #Enable verbose sysinit messages +options KDB +options DDB #Enable the kernel debugger +options INVARIANTS #Enable calls of extra sanity checking +options INVARIANT_SUPPORT #Extra sanity checks of internal structures, required by INVARIANTS +options WITNESS #Enable checks to detect deadlocks and cycles +options WITNESS_SKIPSPIN #Don't run witness on spinlocks for speed +#options DIAGNOSTIC + +# NFS support +#options NFSCL +#options NFSSERVER #Network Filesystem Server +#options NFSCLIENT #Network Filesystem Client + +# Uncomment this for NFS root +#options NFS_ROOT #NFS usable as /, requires NFSCLIENT +#options BOOTP_NFSROOT +#options BOOTP_COMPAT +#options BOOTP +#options BOOTP_NFSV3 +#options BOOTP_WIRED_TO=cpsw0 + + +# MMC/SD/SDIO card slot support +device mmc # mmc/sd bus +device mmcsd # mmc/sd flash cards + +# Boot device is 2nd slice on MMC/SD card +options ROOTDEVNAME=\"ufs:mmcsd0s2\" + +# Console and misc +device uart +device uart_ns8250 +device pty +device snp +device md +device random # Entropy device + +# I2C support +device iicbus +device iic +device ti_i2c +device am335x_pmic # AM335x Power Management IC (TPC65217) + +# GPIO +device gpio + +# USB support +device usb +options USB_DEBUG +#options USB_REQ_DEBUG +#options USB_VERBOSE +device musb +device umass +device scbus # SCSI bus (required for SCSI) +device da # Direct Access (disks) + +# Ethernet +device loop +device ether +device mii +device smscphy +device cpsw +device bpf + +# USB ethernet support, requires miibus +device miibus +device axe # ASIX Electronics USB Ethernet + +# Flattened Device Tree +options FDT +options FDT_DTB_STATIC +makeoptions FDT_DTS_FILE=beaglebone.dts + diff --git a/sys/arm/conf/PANDABOARD b/sys/arm/conf/PANDABOARD new file mode 100644 index 000000000000..7a4f474d6a4d --- /dev/null +++ b/sys/arm/conf/PANDABOARD @@ -0,0 +1,144 @@ +# PANDABOARD -- Custom configuration for the PandaBoard ARM development +# platform, check out www.pandaboard.org +# +# For more information on this file, please read the handbook section on +# Kernel Configuration Files: +# +# http://www.FreeBSD.org/doc/en_US.ISO8859-1/books/handbook/kernelconfig-config.html +# +# The handbook is also available locally in /usr/share/doc/handbook +# if you've installed the doc distribution, otherwise always see the +# FreeBSD World Wide Web server (http://www.FreeBSD.org/) for the +# latest information. +# +# An exhaustive list of options and more detailed explanations of the +# device lines is also present in the ../../conf/NOTES and NOTES files. +# If you are in doubt as to the purpose or necessity of a line, check first +# in NOTES. +# +# $FreeBSD$ + +ident PANDABOARD + + + +# This probably wants to move somewhere else. Maybe we can create a basic +# OMAP4340 config, then make a PANDABOARD config that includes the basic one, +# adds the start addresses and custom devices plus pulls in this hints file. + +hints "PANDABOARD.hints" + +include "../ti/omap4/pandaboard/std.pandaboard" + +#To statically compile in device wiring instead of /boot/device.hints +makeoptions MODULES_OVERRIDE="" +makeoptions WITHOUT_MODULES="ahc" + +makeoptions DEBUG=-g #Build kernel with gdb(1) debug symbols +options HZ=100 + +options SCHED_4BSD #4BSD scheduler +options INET #InterNETworking +#options INET6 #IPv6 communications protocols +options FFS #Berkeley Fast Filesystem +options SOFTUPDATES #Enable FFS soft updates support +options UFS_ACL #Support for access control lists +options UFS_DIRHASH #Improve performance on big directories +options NFSCLIENT #Network Filesystem Client +device snp +#options NFSCL +#options NFSSERVER #Network Filesystem Server +options NFS_ROOT #NFS usable as /, requires NFSCLIENT +options BREAK_TO_DEBUGGER +options BOOTP_NFSROOT +options BOOTP_COMPAT +options BOOTP +options BOOTP_NFSV3 +options BOOTP_WIRED_TO=ue0 +options MSDOSFS #MSDOS Filesystem +#options CD9660 #ISO 9660 Filesystem +#options PROCFS #Process filesystem (requires PSEUDOFS) +options PSEUDOFS #Pseudo-filesystem framework +options COMPAT_43 #Compatible with BSD 4.3 [KEEP THIS!] +options SCSI_DELAY=5000 #Delay (in ms) before probing SCSI +options KTRACE #ktrace(1) support +options SYSVSHM #SYSV-style shared memory +options SYSVMSG #SYSV-style message queues +options SYSVSEM #SYSV-style semaphores +options _KPOSIX_PRIORITY_SCHEDULING #Posix P1003_1B real-time extensions +options KBD_INSTALL_CDEV # install a CDEV entry in /dev + +options PREEMPTION + +# MMC/SD/SDIO Card slot support +device mmc # mmc/sd bus +device mmcsd # mmc/sd flash cards + +# I2C support +device iicbus +device iic +device ti_i2c + +device loop +device ether +device mii +device smc +device smcphy +device uart +device uart_ns8250 + +device gpio + +device pty + +device pl310 # PL310 L2 cache controller +# Debugging for use in -current +#options VERBOSE_SYSINIT #Enable verbose sysinit messages +options KDB +options DDB #Enable the kernel debugger +#options INVARIANTS #Enable calls of extra sanity checking +#options INVARIANT_SUPPORT #Extra sanity checks of internal structures, required by INVARIANTS +#options WITNESS #Enable checks to detect deadlocks and cycles +#options WITNESS_SKIPSPIN #Don't run witness on spinlocks for speed +#options DIAGNOSTIC + +device md + +# The following enables MFS as root, this seems similar to an initramfs or initrd +# as used in Linux. +# options MD_ROOT +# options MD_ROOT_SIZE=7560 + +device random # Entropy device + +# USB support +device usb +options USB_DEBUG +#options USB_REQ_DEBUG +#options USB_VERBOSE +device ohci +device ehci +device umass +device scbus # SCSI bus (required for SCSI) +device da # Direct Access (disks) + + +# USB Ethernet support, requires miibus +device miibus +# device axe # ASIX Electronics USB Ethernet +device smsc # SMSC LAN95xx USB Ethernet + + +# OMAP-specific devices +device ti_sdma +device twl +device twl_vreg +device twl_clks + +# Flattened Device Tree +options FDT +options FDT_DTB_STATIC +makeoptions FDT_DTS_FILE=pandaboard.dts + +# device vfp # vfp/neon +# options ARM_VFP_SUPPORT # vfp/neon diff --git a/sys/arm/conf/PANDABOARD.hints b/sys/arm/conf/PANDABOARD.hints new file mode 100644 index 000000000000..1aef3af9cd54 --- /dev/null +++ b/sys/arm/conf/PANDABOARD.hints @@ -0,0 +1,61 @@ +# $FreeBSD$ + +# USB ECHI + +# +# TI OMAP Power Management and System Companion Device sitting on the I2C bus +# hint.tps65950.0.at="iicbus0" +# hint.tps65950.0.addr=0xd0 + + +# +# Defines the GPIO pin used to detect the Write Protect stat of the MMC/SD card. +#hint.omap_mmc.0.wp_gpio="23" + + +# +# If 'phy_reset" is set, then the accompaning PHY is reset using one of the +# GPIO pins. If the reset GPIO pin is not -1 then the pin will be toggled when +# the USB driver is loaded. +hint.ehci.0.phy_reset="0" + +# +# Sets the PHY mode for the individual ports, the following values are allowed +# - EHCI_HCD_OMAP3_MODE_UNKNOWN 0 +# - EHCI_HCD_OMAP3_MODE_PHY 1 +# - EHCI_HCD_OMAP3_MODE_TLL 2 +hint.ehci.0.phy_mode_0="1" +hint.ehci.0.phy_mode_1="0" +hint.ehci.0.phy_mode_2="0" + +# +# If specified the value indicates a pin that is toggled as a heart-beat. The +# heart beat pusle is triggered every 500ms using the system tick timer. +hint.omap_clk.0.heartbeat_gpio="150" + + +# +# Padconf (pinmux) settings - typically this would be set by the boot-loader +# but can be overridden here. These hints are applied to the H/W when the +# SCM module is initialised. +# +# The format is: +# hint.omap_scm.0.padconf.= +# +# Where the options can be one of the following: +# output, input, input_pullup, input_pulldown +# + +# Setup the pin settings for the HS USB Host (PHY mode) +hint.omap4.0.padconf.ag19="usbb1_ulpiphy_stp:output" +hint.omap4.0.padconf.ae18="usbb1_ulpiphy_clk:input_pulldown" +hint.omap4.0.padconf.af19="usbb1_ulpiphy_dir:input_pulldown" +hint.omap4.0.padconf.ae19="usbb1_ulpiphy_nxt:input_pulldown" +hint.omap4.0.padconf.af18="usbb1_ulpiphy_dat0:input_pulldown" +hint.omap4.0.padconf.ag18="usbb1_ulpiphy_dat1:input_pulldown" +hint.omap4.0.padconf.ae17="usbb1_ulpiphy_dat2:input_pulldown" +hint.omap4.0.padconf.af17="usbb1_ulpiphy_dat3:input_pulldown" +hint.omap4.0.padconf.ah17="usbb1_ulpiphy_dat4:input_pulldown" +hint.omap4.0.padconf.ae16="usbb1_ulpiphy_dat5:input_pulldown" +hint.omap4.0.padconf.af16="usbb1_ulpiphy_dat6:input_pulldown" +hint.omap4.0.padconf.ag16="usbb1_ulpiphy_dat7:input_pulldown" diff --git a/sys/arm/ti/aintc.c b/sys/arm/ti/aintc.c new file mode 100644 index 000000000000..eb664929b870 --- /dev/null +++ b/sys/arm/ti/aintc.c @@ -0,0 +1,179 @@ +/*- + * Copyright (c) 2012 Damjan Marion + * All rights reserved. + * + * Based on OMAP3 INTC code by Ben Gray + * + * 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 + +#define INTC_REVISION 0x00 +#define INTC_SYSCONFIG 0x10 +#define INTC_SYSSTATUS 0x14 +#define INTC_SIR_IRQ 0x40 +#define INTC_CONTROL 0x48 +#define INTC_THRESHOLD 0x68 +#define INTC_MIR_CLEAR(x) (0x88 + ((x) * 0x20)) +#define INTC_MIR_SET(x) (0x8C + ((x) * 0x20)) +#define INTC_ISR_SET(x) (0x90 + ((x) * 0x20)) +#define INTC_ISR_CLEAR(x) (0x94 + ((x) * 0x20)) + +struct ti_aintc_softc { + device_t sc_dev; + struct resource * aintc_res[3]; + bus_space_tag_t aintc_bst; + bus_space_handle_t aintc_bsh; + uint8_t ver; +}; + +static struct resource_spec ti_aintc_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { -1, 0 } +}; + + +static struct ti_aintc_softc *ti_aintc_sc = NULL; + +#define aintc_read_4(reg) \ + bus_space_read_4(ti_aintc_sc->aintc_bst, ti_aintc_sc->aintc_bsh, reg) +#define aintc_write_4(reg, val) \ + bus_space_write_4(ti_aintc_sc->aintc_bst, ti_aintc_sc->aintc_bsh, reg, val) + + +static int +ti_aintc_probe(device_t dev) +{ + if (!ofw_bus_is_compatible(dev, "ti,aintc")) + return (ENXIO); + device_set_desc(dev, "TI AINTC Interrupt Controller"); + return (BUS_PROBE_DEFAULT); +} + +static int +ti_aintc_attach(device_t dev) +{ + struct ti_aintc_softc *sc = device_get_softc(dev); + uint32_t x; + + sc->sc_dev = dev; + + if (ti_aintc_sc) + return (ENXIO); + + if (bus_alloc_resources(dev, ti_aintc_spec, sc->aintc_res)) { + device_printf(dev, "could not allocate resources\n"); + return (ENXIO); + } + + sc->aintc_bst = rman_get_bustag(sc->aintc_res[0]); + sc->aintc_bsh = rman_get_bushandle(sc->aintc_res[0]); + + ti_aintc_sc = sc; + + x = aintc_read_4(INTC_REVISION); + device_printf(dev, "Revision %u.%u\n",(x >> 4) & 0xF, x & 0xF); + + /* SoftReset */ + aintc_write_4(INTC_SYSCONFIG, 2); + + /* Wait for reset to complete */ + while(!(aintc_read_4(INTC_SYSSTATUS) & 1)); + + /*Set Priority Threshold */ + aintc_write_4(INTC_THRESHOLD, 0xFF); + + return (0); +} + +static device_method_t ti_aintc_methods[] = { + DEVMETHOD(device_probe, ti_aintc_probe), + DEVMETHOD(device_attach, ti_aintc_attach), + { 0, 0 } +}; + +static driver_t ti_aintc_driver = { + "aintc", + ti_aintc_methods, + sizeof(struct ti_aintc_softc), +}; + +static devclass_t ti_aintc_devclass; + +DRIVER_MODULE(aintc, simplebus, ti_aintc_driver, ti_aintc_devclass, 0, 0); + +int +arm_get_next_irq(int last_irq) +{ + uint32_t active_irq; + + if (last_irq != -1) { + aintc_write_4(INTC_ISR_CLEAR(last_irq >> 5), + 1UL << (last_irq & 0x1F)); + aintc_write_4(INTC_CONTROL,1); + } + + /* Get the next active interrupt */ + active_irq = aintc_read_4(INTC_SIR_IRQ); + + /* Check for spurious interrupt */ + if ((active_irq & 0xffffff80)) { + device_printf(ti_aintc_sc->sc_dev, + "Spurious interrupt detected (0x%08x)\n", active_irq); + return -1; + } + + if (active_irq != last_irq) + return active_irq; + else + return -1; +} + +void +arm_mask_irq(uintptr_t nb) +{ + aintc_write_4(INTC_MIR_SET(nb >> 5), (1UL << (nb & 0x1F))); +} + +void +arm_unmask_irq(uintptr_t nb) +{ + aintc_write_4(INTC_MIR_CLEAR(nb >> 5), (1UL << (nb & 0x1F))); +} diff --git a/sys/arm/ti/am335x/am335x_dmtimer.c b/sys/arm/ti/am335x/am335x_dmtimer.c new file mode 100644 index 000000000000..c2d01393787b --- /dev/null +++ b/sys/arm/ti/am335x/am335x_dmtimer.c @@ -0,0 +1,385 @@ +/*- + * 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 + +#define AM335X_NUM_TIMERS 8 + +#define DMTIMER_TIDR 0x00 /* Identification Register */ +#define DMTIMER_TIOCP_CFG 0x10 /* Timer OCP Configuration Reg */ +#define DMTIMER_IQR_EOI 0x20 /* Timer IRQ End-Of-Interrupt Reg */ +#define DMTIMER_IRQSTATUS_RAW 0x24 /* Timer IRQSTATUS Raw Reg */ +#define DMTIMER_IRQSTATUS 0x28 /* Timer IRQSTATUS Reg */ +#define DMTIMER_IRQENABLE_SET 0x2c /* Timer IRQSTATUS Set Reg */ +#define DMTIMER_IRQENABLE_CLR 0x30 /* Timer IRQSTATUS Clear Reg */ +#define DMTIMER_IRQWAKEEN 0x34 /* Timer IRQ Wakeup Enable Reg */ +#define DMTIMER_TCLR 0x38 /* Timer Control Register */ +#define DMTIMER_TCRR 0x3C /* Timer Counter Register */ +#define DMTIMER_TLDR 0x40 /* Timer Load Reg */ +#define DMTIMER_TTGR 0x44 /* Timer Trigger Reg */ +#define DMTIMER_TWPS 0x48 /* Timer Write Posted Status Reg */ +#define DMTIMER_TMAR 0x4C /* Timer Match Reg */ +#define DMTIMER_TCAR1 0x50 /* Timer Capture Reg */ +#define DMTIMER_TSICR 0x54 /* Timer Synchr. Interface Control Reg */ +#define DMTIMER_TCAR2 0x48 /* Timer Capture Reg */ + + +struct am335x_dmtimer_softc { + struct resource * tmr_mem_res[AM335X_NUM_TIMERS]; + struct resource * tmr_irq_res[AM335X_NUM_TIMERS]; + uint32_t sysclk_freq; + struct am335x_dmtimer { + bus_space_tag_t bst; + bus_space_handle_t bsh; + struct eventtimer et; + } t[AM335X_NUM_TIMERS]; +}; + +static struct resource_spec am335x_dmtimer_mem_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { SYS_RES_MEMORY, 1, RF_ACTIVE }, + { SYS_RES_MEMORY, 2, RF_ACTIVE }, + { SYS_RES_MEMORY, 3, RF_ACTIVE }, + { SYS_RES_MEMORY, 4, RF_ACTIVE }, + { SYS_RES_MEMORY, 5, RF_ACTIVE }, + { SYS_RES_MEMORY, 6, RF_ACTIVE }, + { SYS_RES_MEMORY, 7, RF_ACTIVE }, + { -1, 0, 0 } +}; +static struct resource_spec am335x_dmtimer_irq_spec[] = { + { SYS_RES_IRQ, 0, RF_ACTIVE }, + { SYS_RES_IRQ, 1, RF_ACTIVE }, + { SYS_RES_IRQ, 2, RF_ACTIVE }, + { SYS_RES_IRQ, 3, RF_ACTIVE }, + { SYS_RES_IRQ, 4, RF_ACTIVE }, + { SYS_RES_IRQ, 5, RF_ACTIVE }, + { SYS_RES_IRQ, 6, RF_ACTIVE }, + { SYS_RES_IRQ, 7, RF_ACTIVE }, + { -1, 0, 0 } +}; + +static struct am335x_dmtimer *am335x_dmtimer_tc_tmr = NULL; + +/* Read/Write macros for Timer used as timecounter */ +#define am335x_dmtimer_tc_read_4(reg) \ + bus_space_read_4(am335x_dmtimer_tc_tmr->bst, \ + am335x_dmtimer_tc_tmr->bsh, reg) + +#define am335x_dmtimer_tc_write_4(reg, val) \ + bus_space_write_4(am335x_dmtimer_tc_tmr->bst, \ + am335x_dmtimer_tc_tmr->bsh, reg, val) + +/* Read/Write macros for Timer used as eventtimer */ +#define am335x_dmtimer_et_read_4(reg) \ + bus_space_read_4(tmr->bst, tmr->bsh, reg) + +#define am335x_dmtimer_et_write_4(reg, val) \ + bus_space_write_4(tmr->bst, tmr->bsh, reg, val) + +static unsigned am335x_dmtimer_tc_get_timecount(struct timecounter *); + +static struct timecounter am335x_dmtimer_tc = { + .tc_name = "AM335x Timecouter", + .tc_get_timecount = am335x_dmtimer_tc_get_timecount, + .tc_poll_pps = NULL, + .tc_counter_mask = ~0u, + .tc_frequency = 0, + .tc_quality = 1000, +}; + +static unsigned +am335x_dmtimer_tc_get_timecount(struct timecounter *tc) +{ + return am335x_dmtimer_tc_read_4(DMTIMER_TCRR); +} + +static int +am335x_dmtimer_start(struct eventtimer *et, struct bintime *first, + struct bintime *period) +{ + struct am335x_dmtimer *tmr = (struct am335x_dmtimer *)et->et_priv; + uint32_t load, count; + uint32_t tclr = 0; + + if (period != NULL) { + load = (et->et_frequency * (period->frac >> 32)) >> 32; + if (period->sec > 0) + load += et->et_frequency * period->sec; + tclr |= 2; /* autoreload bit */ + panic("periodic timer not implemented\n"); + } else { + load = 0; + } + + if (first != NULL) { + count = (tmr->et.et_frequency * (first->frac >> 32)) >> 32; + if (first->sec != 0) + count += tmr->et.et_frequency * first->sec; + } else { + count = load; + } + + /* Reset Timer */ + am335x_dmtimer_et_write_4(DMTIMER_TSICR, 2); + + /* Wait for reset to complete */ + while (am335x_dmtimer_et_read_4(DMTIMER_TIOCP_CFG) & 1); + + /* set load value */ + am335x_dmtimer_et_write_4(DMTIMER_TLDR, 0xFFFFFFFE - load); + + /* set counter value */ + am335x_dmtimer_et_write_4(DMTIMER_TCRR, 0xFFFFFFFE - count); + + /* enable overflow interrupt */ + am335x_dmtimer_et_write_4(DMTIMER_IRQENABLE_SET, 2); + + /* start timer(ST) */ + tclr |= 1; + am335x_dmtimer_et_write_4(DMTIMER_TCLR, tclr); + + return (0); +} + +static int +am335x_dmtimer_stop(struct eventtimer *et) +{ + struct am335x_dmtimer *tmr = (struct am335x_dmtimer *)et->et_priv; + + /* Disable all interrupts */ + am335x_dmtimer_et_write_4(DMTIMER_IRQENABLE_CLR, 7); + + /* Stop Timer */ + am335x_dmtimer_et_write_4(DMTIMER_TCLR, 0); + + return (0); +} + +static int +am335x_dmtimer_intr(void *arg) +{ + struct am335x_dmtimer *tmr = (struct am335x_dmtimer *)arg; + + /* Ack interrupt */ + am335x_dmtimer_et_write_4(DMTIMER_IRQSTATUS, 7); + if (tmr->et.et_active) + tmr->et.et_event_cb(&tmr->et, tmr->et.et_arg); + + return (FILTER_HANDLED); +} + +static int +am335x_dmtimer_probe(device_t dev) +{ + struct am335x_dmtimer_softc *sc; + sc = (struct am335x_dmtimer_softc *)device_get_softc(dev); + + if (ofw_bus_is_compatible(dev, "ti,am335x-dmtimer")) { + device_set_desc(dev, "AM335x DMTimer"); + return(BUS_PROBE_DEFAULT); + } + + return (ENXIO); +} + +static int +am335x_dmtimer_attach(device_t dev) +{ + struct am335x_dmtimer_softc *sc = device_get_softc(dev); + void *ihl; + int err; + int i; + + if (am335x_dmtimer_tc_tmr != NULL) + return (EINVAL); + + /* Get the base clock frequency */ + err = ti_prcm_clk_get_source_freq(SYS_CLK, &sc->sysclk_freq); + if (err) { + device_printf(dev, "Error: could not get sysclk frequency\n"); + return (ENXIO); + } + + /* Request the memory resources */ + err = bus_alloc_resources(dev, am335x_dmtimer_mem_spec, + sc->tmr_mem_res); + if (err) { + device_printf(dev, "Error: could not allocate mem resources\n"); + return (ENXIO); + } + + /* Request the IRQ resources */ + err = bus_alloc_resources(dev, am335x_dmtimer_irq_spec, + sc->tmr_irq_res); + if (err) { + device_printf(dev, "Error: could not allocate irq resources\n"); + return (ENXIO); + } + + for(i=0;it[i].bst = rman_get_bustag(sc->tmr_mem_res[i]); + sc->t[i].bsh = rman_get_bushandle(sc->tmr_mem_res[i]); + } + + /* Configure DMTimer2 and DMTimer3 source and enable them */ + err = ti_prcm_clk_set_source(DMTIMER2_CLK, SYSCLK_CLK); + err |= ti_prcm_clk_enable(DMTIMER2_CLK); + err |= ti_prcm_clk_set_source(DMTIMER3_CLK, SYSCLK_CLK); + err |= ti_prcm_clk_enable(DMTIMER3_CLK); + if (err) { + device_printf(dev, "Error: could not setup timer clock\n"); + return (ENXIO); + } + + /* Take DMTimer2 for TC */ + am335x_dmtimer_tc_tmr = &sc->t[2]; + + /* Reset Timer */ + am335x_dmtimer_tc_write_4(DMTIMER_TSICR, 2); + + /* Wait for reset to complete */ + while (am335x_dmtimer_tc_read_4(DMTIMER_TIOCP_CFG) & 1); + + /* set load value */ + am335x_dmtimer_tc_write_4(DMTIMER_TLDR, 0); + + /* set counter value */ + am335x_dmtimer_tc_write_4(DMTIMER_TCRR, 0); + + /* Set Timer autoreload(AR) and start timer(ST) */ + am335x_dmtimer_tc_write_4(DMTIMER_TCLR, 3); + + am335x_dmtimer_tc.tc_frequency = sc->sysclk_freq; + tc_init(&am335x_dmtimer_tc); + + /* Register DMTimer3 as ET */ + + /* Setup and enable the timer */ + if (bus_setup_intr(dev, sc->tmr_irq_res[3], INTR_TYPE_CLK, + am335x_dmtimer_intr, NULL, &sc->t[3], &ihl) != 0) { + bus_release_resources(dev, am335x_dmtimer_irq_spec, + sc->tmr_irq_res); + device_printf(dev, "Unable to setup the clock irq handler.\n"); + return (ENXIO); + } + + sc->t[3].et.et_name = "AM335x Eventtimer0"; + sc->t[3].et.et_flags = ET_FLAGS_PERIODIC | ET_FLAGS_ONESHOT; + sc->t[3].et.et_quality = 1000; + sc->t[3].et.et_frequency = sc->sysclk_freq; + sc->t[3].et.et_min_period.sec = 0; + sc->t[3].et.et_min_period.frac = + ((0x00000002LLU << 32) / sc->t[3].et.et_frequency) << 32; + sc->t[3].et.et_max_period.sec = 0xfffffff0U / sc->t[3].et.et_frequency; + sc->t[3].et.et_max_period.frac = + ((0xfffffffeLLU << 32) / sc->t[3].et.et_frequency) << 32; + sc->t[3].et.et_start = am335x_dmtimer_start; + sc->t[3].et.et_stop = am335x_dmtimer_stop; + sc->t[3].et.et_priv = &sc->t[3]; + et_register(&sc->t[3].et); + + return (0); +} + +static device_method_t am335x_dmtimer_methods[] = { + DEVMETHOD(device_probe, am335x_dmtimer_probe), + DEVMETHOD(device_attach, am335x_dmtimer_attach), + { 0, 0 } +}; + +static driver_t am335x_dmtimer_driver = { + "am335x_dmtimer", + am335x_dmtimer_methods, + sizeof(struct am335x_dmtimer_softc), +}; + +static devclass_t am335x_dmtimer_devclass; + +DRIVER_MODULE(am335x_dmtimer, simplebus, am335x_dmtimer_driver, am335x_dmtimer_devclass, 0, 0); +MODULE_DEPEND(am335x_dmtimer, am335x_prcm, 1, 1, 1); + +void +cpu_initclocks(void) +{ + cpu_initclocks_bsp(); +} + +void +DELAY(int usec) +{ + int32_t counts; + uint32_t first, last; + + if (am335x_dmtimer_tc_tmr == NULL) { + for (; usec > 0; usec--) + for (counts = 200; counts > 0; counts--) + /* Prevent gcc from optimizing out the loop */ + cpufunc_nullop(); + return; + } + + /* Get the number of times to count */ + counts = usec * ((am335x_dmtimer_tc.tc_frequency / 1000000) + 1);; + + first = am335x_dmtimer_tc_read_4(DMTIMER_TCRR); + + while (counts > 0) { + last = am335x_dmtimer_tc_read_4(DMTIMER_TCRR); + if (last>first) { + counts -= (int32_t)(last - first); + } else { + counts -= (int32_t)((0xFFFFFFFF - first) + last); + } + first = last; + } +} + diff --git a/sys/arm/ti/am335x/am335x_pmic.c b/sys/arm/ti/am335x/am335x_pmic.c new file mode 100644 index 000000000000..eab400ac7ba3 --- /dev/null +++ b/sys/arm/ti/am335x/am335x_pmic.c @@ -0,0 +1,176 @@ +/*- + * 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$"); +/* +* TPS65217 PMIC companion chip for AM335x SoC sitting on I2C bus +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "iicbus_if.h" + +#define TPS65217A 0x7 +#define TPS65217B 0xF + +/* TPS65217 Reisters */ +#define TPS65217_CHIPID_REG 0x00 +#define TPS65217_STATUS_REG 0x0A + +#define MAX_IIC_DATA_SIZE 2 + + +struct am335x_pmic_softc { + device_t sc_dev; + uint32_t sc_addr; + struct intr_config_hook enum_hook; +}; + +static int +am335x_pmic_read(device_t dev, uint8_t addr, uint8_t *data, uint8_t size) +{ + struct am335x_pmic_softc *sc = device_get_softc(dev); + struct iic_msg msg[] = { + { sc->sc_addr, IIC_M_WR, 1, &addr }, + { sc->sc_addr, IIC_M_RD, size, data }, + }; + return (iicbus_transfer(dev, msg, 2)); +} + +#ifdef notyet +static int +am335x_pmic_write(device_t dev, uint8_t address, uint8_t *data, uint8_t size) +{ + uint8_t buffer[MAX_IIC_DATA_SIZE + 1]; + struct am335x_pmic_softc *sc = device_get_softc(dev); + struct iic_msg msg[] = { + { sc->sc_addr, IIC_M_WR, size + 1, buffer }, + }; + + if (size > MAX_IIC_DATA_SIZE) + return (ENOMEM); + + buffer[0] = address; + memcpy(buffer + 1, data, size); + + return (iicbus_transfer(dev, msg, 1)); +} +#endif + +static int +am335x_pmic_probe(device_t dev) +{ + struct am335x_pmic_softc *sc; + + if (!ofw_bus_is_compatible(dev, "ti,am335x-pmic")) + return (ENXIO); + + sc = device_get_softc(dev); + sc->sc_dev = dev; + sc->sc_addr = iicbus_get_addr(dev); + + device_set_desc(dev, "TI TPS65217 Power Management IC"); + + return (0); +} + +static void +am335x_pmic_start(void *xdev) +{ + struct am335x_pmic_softc *sc; + device_t dev = (device_t)xdev; + uint8_t reg; + char name[20]; + char pwr[4][11] = {"Unknown", "USB", "AC", "USB and AC"}; + + sc = device_get_softc(dev); + + am335x_pmic_read(dev, TPS65217_CHIPID_REG, ®, 1); + switch (reg>>4) { + case TPS65217A: + sprintf(name, "TPS65217A ver 1.%u", reg & 0xF); + break; + case TPS65217B: + sprintf(name, "TPS65217B ver 1.%u", reg & 0xF); + break; + default: + sprintf(name, "Unknown PMIC"); + } + + am335x_pmic_read(dev, TPS65217_STATUS_REG, ®, 1); + device_printf(dev, "%s powered by %s\n", name, pwr[(reg>>2)&0x03]); + + config_intrhook_disestablish(&sc->enum_hook); +} + +static int +am335x_pmic_attach(device_t dev) +{ + struct am335x_pmic_softc *sc; + + sc = device_get_softc(dev); + + sc->enum_hook.ich_func = am335x_pmic_start; + sc->enum_hook.ich_arg = dev; + + if (config_intrhook_establish(&sc->enum_hook) != 0) + return (ENOMEM); + + return (0); +} + +static device_method_t am335x_pmic_methods[] = { + DEVMETHOD(device_probe, am335x_pmic_probe), + DEVMETHOD(device_attach, am335x_pmic_attach), + {0, 0}, +}; + +static driver_t am335x_pmic_driver = { + "am335x_pmic", + am335x_pmic_methods, + sizeof(struct am335x_pmic_softc), +}; + +static devclass_t am335x_pmic_devclass; + +DRIVER_MODULE(am335x_pmic, iicbus, am335x_pmic_driver, am335x_pmic_devclass, 0, 0); +MODULE_VERSION(am335x_pmic, 1); +MODULE_DEPEND(am335x_pmic, iicbus, 1, 1, 1); diff --git a/sys/arm/ti/am335x/am335x_prcm.c b/sys/arm/ti/am335x/am335x_prcm.c new file mode 100644 index 000000000000..abc37e62d96a --- /dev/null +++ b/sys/arm/ti/am335x/am335x_prcm.c @@ -0,0 +1,568 @@ +/*- + * 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 + +#include +#include + +#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_USB0_CLKCTRL (CM_PER + 0x01C) +#define CM_PER_TPTC0_CLKCTRL (CM_PER + 0x024) +#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_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_L3_INSTR_CLKCTRL (CM_PER + 0x0DC) +#define CM_PER_L3_CLKCTRL (CM_PER + 0x0E0) +#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_OCPWP_L3_CLKSTCTRL (CM_PER + 0x12C) +#define CM_PER_OCPWP_CLKCTRL (CM_PER + 0x130) +#define CM_PER_CPSW_CLKSTCTRL (CM_PER + 0x144) + +#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_CLKDCOLDO_DPLL_PER (CM_WKUP + 0x07C) +#define CM_WKUP_I2C0_CLKCTRL (CM_WKUP + 0x0B8) + +#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 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_generic_activate(struct ti_clock_dev *clkdev); +static int am335x_clk_gpio_activate(struct ti_clock_dev *clkdev); +static int am335x_clk_generic_deactivate(struct ti_clock_dev *clkdev); +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 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); + +#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 \ + } + +#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 \ + } + +#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 \ + } + +struct ti_clock_dev ti_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, + }, + /* 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, + }, + /* 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, + }, + + /* 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, + }, + + /* DMTimer */ + AM335X_GENERIC_CLOCK_DEV(DMTIMER2_CLK), + AM335X_GENERIC_CLOCK_DEV(DMTIMER3_CLK), + AM335X_GENERIC_CLOCK_DEV(DMTIMER4_CLK), + AM335X_GENERIC_CLOCK_DEV(DMTIMER5_CLK), + AM335X_GENERIC_CLOCK_DEV(DMTIMER6_CLK), + AM335X_GENERIC_CLOCK_DEV(DMTIMER7_CLK), + + /* GPIO */ + AM335X_GPIO_CLOCK_DEV(GPIO0_CLK), + AM335X_GPIO_CLOCK_DEV(GPIO1_CLK), + AM335X_GPIO_CLOCK_DEV(GPIO2_CLK), + AM335X_GPIO_CLOCK_DEV(GPIO3_CLK), + + /* I2C */ + AM335X_GENERIC_CLOCK_DEV(I2C0_CLK), + AM335X_GENERIC_CLOCK_DEV(I2C1_CLK), + AM335X_GENERIC_CLOCK_DEV(I2C2_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(MMC0_CLK), + AM335X_MMCHS_CLOCK_DEV(MMC1_CLK), + AM335X_MMCHS_CLOCK_DEV(MMC2_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[] = { + + /* DMTimer modules */ + _CLK_DETAIL(DMTIMER2_CLK, CM_PER_TIMER2_CLKCTRL, CLKSEL_TIMER2_CLK), + _CLK_DETAIL(DMTIMER3_CLK, CM_PER_TIMER3_CLKCTRL, CLKSEL_TIMER3_CLK), + _CLK_DETAIL(DMTIMER4_CLK, CM_PER_TIMER4_CLKCTRL, CLKSEL_TIMER4_CLK), + _CLK_DETAIL(DMTIMER5_CLK, CM_PER_TIMER5_CLKCTRL, CLKSEL_TIMER5_CLK), + _CLK_DETAIL(DMTIMER6_CLK, CM_PER_TIMER6_CLKCTRL, CLKSEL_TIMER6_CLK), + _CLK_DETAIL(DMTIMER7_CLK, CM_PER_TIMER7_CLKCTRL, CLKSEL_TIMER7_CLK), + + /* GPIO modules */ + _CLK_DETAIL(GPIO0_CLK, CM_WKUP_GPIO0_CLKCTRL, 0), + _CLK_DETAIL(GPIO1_CLK, CM_PER_GPIO1_CLKCTRL, 0), + _CLK_DETAIL(GPIO2_CLK, CM_PER_GPIO2_CLKCTRL, 0), + _CLK_DETAIL(GPIO3_CLK, CM_PER_GPIO3_CLKCTRL, 0), + + /* I2C modules */ + _CLK_DETAIL(I2C0_CLK, CM_WKUP_I2C0_CLKCTRL, 0), + _CLK_DETAIL(I2C1_CLK, CM_PER_I2C1_CLKCTRL, 0), + _CLK_DETAIL(I2C2_CLK, CM_PER_I2C2_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*/ + _CLK_DETAIL(MMC0_CLK, CM_PER_MMC0_CLKCTRL, 0), + _CLK_DETAIL(MMC1_CLK, CM_PER_MMC1_CLKCTRL, 0), + _CLK_DETAIL(MMC2_CLK, CM_PER_MMC1_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_is_compatible(dev, "am335x,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; + + am335x_clk_get_sysclk_freq(NULL, &sysclk); + am335x_clk_get_arm_fclk_freq(NULL, &fclk); + device_printf(dev, "Clocks: System %u.%01u MHz, CPU %u MHz\n", + sysclk/1000000, (sysclk % 1000000)/100000, fclk/1000000); + + 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_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_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_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_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 */ + /* control_status reg (0x40) */ + if (ti_scm_reg_read_4(0x40, &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); +} + +static int +am335x_clk_get_arm_fclk_freq(struct ti_clock_dev *clkdev, unsigned int *freq) +{ + uint32_t reg; + uint32_t sysclk; +#define DPLL_BYP_CLKSEL(reg) ((reg>>23) & 1) +#define DPLL_DIV(reg) ((reg & 0x7f)+1) +#define DPLL_MULT(reg) ((reg>>8) & 0x7FF) + + 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 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); +} + + diff --git a/sys/arm/ti/am335x/am335x_reg.h b/sys/arm/ti/am335x/am335x_reg.h new file mode 100644 index 000000000000..ca617e3f19e4 --- /dev/null +++ b/sys/arm/ti/am335x/am335x_reg.h @@ -0,0 +1,41 @@ +/*- + * 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. + * + * $FreeBSD$ + */ + +#ifndef _AM335X_REG_H_ +#define _AM335X_REG_H_ + +#define AM335X_L4_WKUP_BASE 0x44C00000 +#define AM335X_L4_WKUP_SIZE 0x400000 + +#define AM335X_CONTROL_BASE AM335X_L4_WKUP_BASE + 0x210000 +#define AM335X_CONTROL_SIZE 0x2000 +#define AM335X_CONTROL_DEVICE_ID 0x0600 +#define AM335X_CONTROL_DEV_FEATURE 0x0604 + +#endif + diff --git a/sys/arm/ti/am335x/am335x_scm_padconf.c b/sys/arm/ti/am335x/am335x_scm_padconf.c new file mode 100644 index 000000000000..4ed63a32d96f --- /dev/null +++ b/sys/arm/ti/am335x/am335x_scm_padconf.c @@ -0,0 +1,373 @@ +/*- + * 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 + +#define _PIN(r, b, gp, gm, m0, m1, m2, m3, m4, m5, m6, m7) \ + { .reg_off = r, \ + .gpio_pin = gp, \ + .gpio_mode = gm, \ + .ballname = b, \ + .muxmodes[0] = m0, \ + .muxmodes[1] = m1, \ + .muxmodes[2] = m2, \ + .muxmodes[3] = m3, \ + .muxmodes[4] = m4, \ + .muxmodes[5] = m5, \ + .muxmodes[6] = m6, \ + .muxmodes[7] = m7, \ + } + +#define SLEWCTRL (0x01 << 6) /* faster(0) or slower(1) slew rate. */ +#define RXACTIVE (0x01 << 5) /* Input enable value for the Pad */ +#define PULLTYPESEL (0x01 << 4) /* Pad pullup/pulldown type selection */ +#define PULLUDEN (0x01 << 3) /* Pullup/pulldown disabled */ + +#define PADCONF_OUTPUT (0) +#define PADCONF_OUTPUT_PULLUP (PULLTYPESEL) +#define PADCONF_INPUT (RXACTIVE | PULLUDEN) +#define PADCONF_INPUT_PULLUP (RXACTIVE | PULLTYPESEL) +#define PADCONF_INPUT_PULLDOWN (RXACTIVE) +#define PADCONF_INPUT_PULLUP_SLOW (PADCONF_INPUT_PULLUP | SLEWCTRL) + +const struct ti_scm_padstate ti_padstate_devmap[] = { + {"output", PADCONF_OUTPUT }, + {"output_pullup", PADCONF_OUTPUT_PULLUP }, + {"input", PADCONF_INPUT }, + {"input_pulldown", PADCONF_INPUT_PULLDOWN }, + {"input_pullup", PADCONF_INPUT_PULLUP }, + {"i2c", PADCONF_INPUT_PULLUP_SLOW }, + { .state = NULL } +}; + +const struct ti_scm_padconf ti_padconf_devmap[] = { + _PIN(0x800, "GPMC_AD0", 32, 7,"gpmc_ad0", "mmc1_dat0", NULL, NULL, NULL, NULL, NULL, "gpio1_0"), + _PIN(0x804, "GPMC_AD1", 33, 7,"gpmc_ad1", "mmc1_dat1", NULL, NULL, NULL, NULL, NULL, "gpio1_1"), + _PIN(0x808, "GPMC_AD2", 34, 7,"gpmc_ad2", "mmc1_dat2", NULL, NULL, NULL, NULL, NULL, "gpio1_2"), + _PIN(0x80C, "GPMC_AD3", 35, 7,"gpmc_ad3", "mmc1_dat3", NULL, NULL, NULL, NULL, NULL, "gpio1_3"), + _PIN(0x810, "GPMC_AD4", 36, 7,"gpmc_ad4", "mmc1_dat4", NULL, NULL, NULL, NULL, NULL, "gpio1_4"), + _PIN(0x814, "GPMC_AD5", 37, 7,"gpmc_ad5", "mmc1_dat5", NULL, NULL, NULL, NULL, NULL, "gpio1_5"), + _PIN(0x818, "GPMC_AD6", 38, 7,"gpmc_ad6", "mmc1_dat6", NULL, NULL, NULL, NULL, NULL, "gpio1_6"), + _PIN(0x81C, "GPMC_AD7", 39, 7,"gpmc_ad7", "mmc1_dat7", NULL, NULL, NULL, NULL, NULL, "gpio1_7"), +#if 0 /* Incomplete Entries - fill with data from table 2-7 in datasheet */ + _PIN(0x820, "gpmc_ad8", 0, 0, "gpmc_ad8", NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x824, "gpmc_ad9", 0, 0, "gpmc_ad9", NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x828, "gpmc_ad10", 0, 0, "gpmc_ad10", NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x82C, "gpmc_ad11", 0, 0, "gpmc_ad11", NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x830, "gpmc_ad12", 0, 0, "gpmc_ad12", NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x834, "gpmc_ad13", 0, 0, "gpmc_ad13", NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x838, "gpmc_ad14", 0, 0, "gpmc_ad14", NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x83C, "gpmc_ad15", 0, 0, "gpmc_ad15", NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x840, "gpmc_a0", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x844, "gpmc_a1", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x848, "gpmc_a2", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x84C, "gpmc_a3", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x850, "gpmc_a4", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), +#endif + _PIN(0x854, "GPMC_A5", 53, 7, "gpmc_a5", "gmii2_txd0", "rgmii2_td0", "rmii2_txd0", "gpmc_a21", "pr1_mii1_rxd3", "eQEP1B_in", "gpio1_21"), + _PIN(0x858, "GPMC_A6", 54, 7, "gpmc_a6", "gmii2_txclk", "rgmii2_tclk", "mmc2_dat4", "gpmc_a22", "pr1_mii1_rxd2", "eQEP1_index", "gpio1_22"), + _PIN(0x85C, "GPMC_A7", 55, 7, "gpmc_a7", "gmii2_rxclk", "rgmii2_rclk", "mmc2_dat5", "gpmc_a23", "pr1_mii1_rxd1", "eQEP1_strobe", "gpio1_23"), + _PIN(0x860, "GPMC_A8", 56, 7, "gpmc_a8", "gmii2_rxd3", "rgmii2_rd3", "mmc2_dat6", "gpmc_a24", "pr1_mii1_rxd0", "mcasp0_aclkx", "gpio1_24"), +#if 0 + _PIN(0x864, "gpmc_a9", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x868, "gpmc_a10", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x86C, "gpmc_a11", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x870, "gpmc_wait0", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x874, "gpmc_wpn", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x878, "gpmc_be1n", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x87c, "gpmc_csn0", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x880, "gpmc_csn1", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x884, "gpmc_csn2", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x888, "gpmc_csn3", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x88c, "gpmc_clk", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x890, "gpmc_advn_ale", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x894, "gpmc_oen_ren", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x898, "gpmc_wen", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x89c, "gpmc_be0n_cle", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x8a0, "lcd_data0", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x8a4, "lcd_data1", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x8a8, "lcd_data2", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x8ac, "lcd_data3", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x8b0, "lcd_data4", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x8b4, "lcd_data5", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x8b8, "lcd_data6", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x8bc, "lcd_data7", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x8c0, "lcd_data8", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x8c4, "lcd_data9", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x8c8, "lcd_data10", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x8cc, "lcd_data11", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x8d0, "lcd_data12", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x8d4, "lcd_data13", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x8d8, "lcd_data14", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x8dc, "lcd_data15", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x8e0, "lcd_vsync", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x8e4, "lcd_hsync", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x8e8, "lcd_pclk", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x8ec, "lcd_ac_bias_en", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), +#endif + _PIN(0x8f0, "MMC0_DAT3", 90, 7, "mmc0_dat3", "gpmc_a20", "uart4_ctsn", "timer5", "uart1_dcdn", "pr1_pru0_pru_r30_8", "pr1_pru0_pru_r31_8", "gpio2_26"), + _PIN(0x8f4, "MMC0_DAT2", 91, 7, "mmc0_dat2", "gpmc_a21", "uart4_rtsn", "timer6", "uart1_dsrn", "pr1_pru0_pru_r30_9", "pr1_pru0_pru_r31_9", "gpio2_27"), + _PIN(0x8f8, "MMC0_DAT1", 92, 7, "mmc0_dat1", "gpmc_a22", "uart5_ctsn", "uart3_rxd", "uart1_dtrn", "pr1_pru0_pru_r30_10", "pr1_pru0_pru_r31_10", "gpio2_28"), + _PIN(0x8fc, "MMC0_DAT0", 93, 7, "mmc0_dat0", "gpmc_a23", "uart5_rtsn", "uart3_txd", "uart1_rin", "pr1_pru0_pru_r30_11", "pr1_pru0_pru_r31_11", "gpio2_29"), + _PIN(0x900, "MMC0_CLK", 94, 7, "mmc0_clk", "gpmc_a24", "uart3_ctsn", "uart2_rxd", "dcan1_tx", "pr1_pru0_pru_r30_12", "pr1_pru0_pru_r31_12", "gpio2_30"), + _PIN(0x904, "MMC0_CMD", 95, 7, "mmc0_cmd", "gpmc_a25", "uart3_rtsn", "uart2_txd", "dcan1_rx", "pr1_pru0_pru_r30_13", "pr1_pru0_pru_r31_13", "gpio2_31"), + _PIN(0x908, "MII1_COL", 96, 7, "gmii1_col", "rmii2_refclk", "spi1_sclk", "uart5_rxd", "mcasp1_axr2", "mmc2_dat3", "mcasp0_axr2", "gpio3_0"), + _PIN(0x90c, "MII1_CRS", 97, 7, "gmii1_crs", "rmii1_crs_dv", "spi1_d0", "I2C1_SDA", "mcasp1_aclkx", "uart5_ctsn", "uart2_rxd", "gpio3_1"), + _PIN(0x910, "MII1_RX_ER", 98, 7, "gmii1_rxerr", "rmii1_rxerr", "spi1_d1", "I2C1_SCL", "mcasp1_fsx", "uart5_rtsn", "uart2_txd", "gpio3_2"), + _PIN(0x914, "MII1_TX_EN", 99, 7, "gmii1_txen", "rmii1_txen", "rgmii1_tctl", "timer4", "mcasp1_axr0", "eQEP0_index", "mmc2_cmd", "gpio3_3"), + _PIN(0x918, "MII1_RX_DV", 100, 7, "gmii1_rxdv", "cd_memory_clk", "rgmii1_rctl", "uart5_txd", "mcasp1_aclkx", "mmc2_dat0", "mcasp0_aclkr", "gpio3_4"), + _PIN(0x91c, "MII1_TXD3", 16, 7, "gmii1_txd3", "dcan0_tx", "rgmii1_td3", "uart4_rxd", "mcasp1_fsx", "mmc2_dat1", "mcasp0_fsr", "gpio0_16"), + _PIN(0x920, "MII1_TXD2", 17, 7, "gmii1_txd2", "dcan0_rx", "rgmii1_td2", "uart4_txd", "mcasp1_axr0", "mmc2_dat2", "mcasp0_ahclkx", "gpio0_17"), + _PIN(0x924, "MII1_TXD1", 21, 7, "gmii1_txd1", "rmii1_txd1", "rgmii1_td1", "mcasp1_fsr", "mcasp1_axr1", "eQEP0A_in", "mmc1_cmd", "gpio0_21"), + _PIN(0x928, "MII1_TXD0", 28, 7, "gmii1_txd0", "rmii1_txd0", "rgmii1_td0", "mcasp1_axr2", "mcasp1_aclkr", "eQEP0B_in", "mmc1_clk", "gpio0_28"), + _PIN(0x92c, "MII1_TX_CLK", 105, 7, "gmii1_txclk", "uart2_rxd", "rgmii1_tclk", "mmc0_dat7", "mmc1_dat0", "uart1_dcdn", "mcasp0_aclkx", "gpio3_9"), + _PIN(0x930, "MII1_RX_CLK", 106, 7, "gmii1_rxclk", "uart2_txd", "rgmii1_rclk", "mmc0_dat6", "mmc1_dat1", "uart1_dsrn", "mcasp0_fsx", "gpio3_10"), + _PIN(0x934, "MII1_RXD3", 82, 7, "gmii1_rxd3", "uart3_rxd", "rgmii1_rd3", "mmc0_dat5", "mmc1_dat2", "uart1_dtrn", "mcasp0_axr0", "gpio2_18"), + _PIN(0x938, "MII1_RXD2", 83, 7, "gmii1_rxd2", "uart3_txd", "rgmii1_rd2", "mmc0_dat4", "mmc1_dat3", "uart1_rin", "mcasp0_axr1", "gpio2_19"), + _PIN(0x93c, "MII1_RXD1", 84, 7, "gmii1_rxd1", "rmii1_rxd1", "rgmii1_rd1", "mcasp1_axr3", "mcasp1_fsr", "eQEP0_strobe", "mmc2_clk", "gpio2_20"), + _PIN(0x940, "MII1_RXD0", 85, 7, "gmii1_rxd0", "rmii1_rxd0", "rgmii1_rd0", "mcasp1_ahclkx", "mcasp1_ahclkr", "mcasp1_aclkr", "mcasp0_axr3", "gpio2_21"), + _PIN(0x944, "RMII1_REF_CLK", 29, 7, "rmii1_refclk", "xdma_event_intr2", "spi1_cs0", "uart5_txd", "mcasp1_axr3", "mmc0_pow", "mcasp1_ahclkx", "gpio0_29"), + _PIN(0x948, "MDIO", 0, 7, "mdio_data", "timer6", "uart5_rxd", "uart3_ctsn", "mmc0_sdcd","mmc1_cmd", "mmc2_cmd","gpio0_0"), + _PIN(0x94c, "MDC", 1, 7, "mdio_clk", "timer5", "uart5_txd", "uart3_rtsn", "mmc0_sdwp", "mmc1_clk", "mmc2_clk", "gpio0_1"), +#if 0 /* Incomplete Entries - fill with data from table 2-7 in datasheet */ + _PIN(0x950, "spi0_sclk", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x954, "spi0_d0", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), +#endif + _PIN(0x958, "spi0_d1", 4, 7, "spi0_d1", "mmc1_sdwp", "I2C1_SDA", "ehrpwm0_tripzone_input", "pr1_uart0_rxd", "pr1_edio_data_in0", "pr1_edio_data_out0", "gpio0_4"), + _PIN(0x95c, "spi0_cs0", 5, 7, "spi0_cs0", "mmc2_sdwp", "I2C1_SCL", "ehrpwm0_synci", "pr1_uart0_txd", "pr1_edio_data_in1", "pr1_edio_data_out1", "gpio0_5"), +#if 0 + _PIN(0x960, "spi0_cs1", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x964, "ecap0_in_pwm0_out",0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x968, "uart0_ctsn", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x96c, "uart0_rtsn", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x970, "uart0_rxd", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x974, "uart0_txd", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), +#endif + _PIN(0x978, "uart1_ctsn", 12, 7, "uart1_ctsn", "timer6_mux1", "dcan0_tx", "I2C2_SDA", "spi1_cs0", "pr1_uart0_cts_n", "pr1_edc_latch0_in", "gpio0_12"), + _PIN(0x97c, "uart1_rtsn", 13, 7, "uart1_rtsn", "timer5_mux1", "dcan0_rx", "I2C2_SCL", "spi1_cs1", "pr1_uart0_rts_n ", "pr1_edc_latch1_in", "gpio0_13"), +#if 0 + _PIN(0x980, "uart1_rxd", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x984, "uart1_txd", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), +#endif + _PIN(0x988, "I2C0_SDA", 101, 7, "I2C0_SDA", "timer4", "uart2_ctsn", "eCAP2_in_PWM2_out", NULL, NULL, NULL, "gpio3_5"), + _PIN(0x98c, "I2C0_SCL", 102, 7, "I2C0_SCL", "timer7", "uart2_rtsn", "eCAP1_in_PWM1_out", NULL, NULL, NULL, "gpio3_6"), +#if 0 + _PIN(0x990, "mcasp0_aclkx", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x994, "mcasp0_fsx", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x998, "mcasp0_axr0", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x99c, "mcasp0_ahclkr", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x9a0, "mcasp0_aclkr", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x9a4, "mcasp0_fsr", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x9a8, "mcasp0_axr1", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x9ac, "mcasp0_ahclkx", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x9b0, "xdma_event_intr0", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x9b4, "xdma_event_intr1", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x9b8, "nresetin_out", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x9bc, "porz", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x9c0, "nnmi", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x9c4, "osc0_in", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x9c8, "osc0_out", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x9cc, "osc0_vss", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x9d0, "tms", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x9d4, "tdi", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x9d8, "tdo", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x9dc, "tck", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x9e0, "ntrst", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x9e4, "emu0", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x9e8, "emu1", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x9ec, "osc1_in", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x9f0, "osc1_out", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x9f4, "osc1_vss", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x9f8, "rtc_porz", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x9fc, "pmic_power_en", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa00, "ext_wakeup", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa04, "enz_kaldo_1p8v", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), +#endif + _PIN(0xa08, "USB0_DM", 0, 0, "USB0_DM", NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa0c, "USB0_DP", 0, 0, "USB0_DP", NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa10, "USB0_CE", 0, 0, "USB0_CE", NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa14, "USB0_ID", 0, 0, "USB0_ID", NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa18, "USB0_VBUS", 0, 0, "USB0_VBUS", NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa1c, "USB0_DRVVBUS", 18, 7, "USB0_DRVVBUS", NULL, NULL, NULL, NULL, NULL, NULL, "gpio0_18"), + _PIN(0xa20, "USB1_DM", 0, 0, "USB1_DM", NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa24, "USB1_DP", 0, 0, "USB1_DP", NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa28, "USB1_CE", 0, 0, "USB1_CE", NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa2c, "USB1_ID", 0, 0, "USB1_ID", NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa30, "USB1_VBUS", 0, 0, "USB1_VBUS", NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa34, "USB1_DRVVBUS", 109, 7, "USB1_DRVVBUS", NULL, NULL, NULL, NULL, NULL, NULL, "gpio3_13"), +#if 0 + _PIN(0xa38, "ddr_resetn", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa3c, "ddr_csn0", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa40, "ddr_cke", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa44, "ddr_ck", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa48, "ddr_nck", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa4c, "ddr_casn", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa50, "ddr_rasn", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa54, "ddr_wen", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa58, "ddr_ba0", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa5c, "ddr_ba1", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa60, "ddr_ba2", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa64, "ddr_a0", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa68, "ddr_a1", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa6c, "ddr_a2", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa70, "ddr_a3", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa74, "ddr_a4", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa78, "ddr_a5", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa7c, "ddr_a6", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa80, "ddr_a7", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa84, "ddr_a8", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa88, "ddr_a9", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa8c, "ddr_a10", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa90, "ddr_a11", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa94, "ddr_a12", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa98, "ddr_a13", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa9c, "ddr_a14", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xaa0, "ddr_a15", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xaa4, "ddr_odt", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xaa8, "ddr_d0", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xaac, "ddr_d1", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xab0, "ddr_d2", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xab4, "ddr_d3", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xab8, "ddr_d4", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xabc, "ddr_d5", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xac0, "ddr_d6", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xac4, "ddr_d7", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xac8, "ddr_d8", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xacc, "ddr_d9", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xad0, "ddr_d10", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xad4, "ddr_d11", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xad8, "ddr_d12", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xadc, "ddr_d13", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xae0, "ddr_d14", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xae4, "ddr_d15", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xae8, "ddr_dqm0", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xaec, "ddr_dqm1", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xaf0, "ddr_dqs0", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xaf4, "ddr_dqsn0", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xaf8, "ddr_dqs1", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xafc, "ddr_dqsn1", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xb00, "ddr_vref", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xb04, "ddr_vtp", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xb08, "ddr_strben0", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xb0c, "ddr_strben1", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xb2c, "ain0", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xb28, "ain1", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xb24, "ain2", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xb20, "ain3", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xb1c, "ain4", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xb18, "ain5", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xb14, "ain6", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xb10, "ain7", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xb30, "vrefp", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xb34, "vrefn", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xb38, "avdd", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xb3c, "avss", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xb40, "iforce", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xb44, "vsense", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xb48, "testout", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), +#endif + { .ballname = NULL }, +}; + +const struct ti_scm_device ti_scm_dev = { + .padconf_muxmode_mask = 0x7, + .padconf_sate_mask = 0x78, + .padstate = (struct ti_scm_padstate *) &ti_padstate_devmap, + .padconf = (struct ti_scm_padconf *) &ti_padconf_devmap, +}; + +int +ti_scm_padconf_set_gpioflags(uint32_t gpio, uint32_t flags) +{ + unsigned int state = 0; + if (flags & GPIO_PIN_OUTPUT) { + if (flags & GPIO_PIN_PULLUP) + state = PADCONF_OUTPUT_PULLUP; + else + state = PADCONF_OUTPUT; + } else if (flags & GPIO_PIN_INPUT) { + if (flags & GPIO_PIN_PULLUP) + state = PADCONF_INPUT_PULLUP; + else if (flags & GPIO_PIN_PULLDOWN) + state = PADCONF_INPUT_PULLDOWN; + else + state = PADCONF_INPUT; + } + return ti_scm_padconf_set_gpiomode(gpio, state); +} + +void +ti_scm_padconf_get_gpioflags(uint32_t gpio, uint32_t *flags) +{ + unsigned int state; + if (ti_scm_padconf_get_gpiomode(gpio, &state) != 0) + *flags = 0; + else { + switch (state) { + case PADCONF_OUTPUT: + *flags = GPIO_PIN_OUTPUT; + break; + case PADCONF_OUTPUT_PULLUP: + *flags = GPIO_PIN_OUTPUT | GPIO_PIN_PULLUP; + break; + case PADCONF_INPUT: + *flags = GPIO_PIN_INPUT; + break; + case PADCONF_INPUT_PULLUP: + *flags = GPIO_PIN_INPUT | GPIO_PIN_PULLUP; + break; + case PADCONF_INPUT_PULLDOWN: + *flags = GPIO_PIN_INPUT | GPIO_PIN_PULLDOWN; + break; + default: + *flags = 0; + break; + } + } +} + diff --git a/sys/arm/ti/am335x/files.am335x b/sys/arm/ti/am335x/files.am335x new file mode 100644 index 000000000000..e4e7aa17261c --- /dev/null +++ b/sys/arm/ti/am335x/files.am335x @@ -0,0 +1,9 @@ +#$FreeBSD$ + +arm/ti/aintc.c standard +arm/ti/am335x/am335x_prcm.c standard +arm/ti/am335x/am335x_dmtimer.c standard +arm/ti/am335x/am335x_scm_padconf.c standard +arm/ti/ti_edma3.c standard +arm/ti/ti_mmchs.c optional mmc +arm/ti/cpsw/if_cpsw.c optional cpsw diff --git a/sys/arm/ti/am335x/files.beaglebone b/sys/arm/ti/am335x/files.beaglebone new file mode 100644 index 000000000000..ff527ce212da --- /dev/null +++ b/sys/arm/ti/am335x/files.beaglebone @@ -0,0 +1,3 @@ +#$FreeBSD$ + +arm/ti/am335x/am335x_pmic.c optional am335x_pmic diff --git a/sys/arm/ti/am335x/std.am335x b/sys/arm/ti/am335x/std.am335x new file mode 100644 index 000000000000..1801cee60eac --- /dev/null +++ b/sys/arm/ti/am335x/std.am335x @@ -0,0 +1,21 @@ +# AM335x generic configuration +#$FreeBSD$ +files "../ti/am335x/files.am335x" +include "../ti/std.ti" +makeoption ARM_LITTLE_ENDIAN + +# Physical memory starts at 0x80000000. We assume images are loaded at +# 0x80200000, e.g. from u-boot with 'fatload mmc 0 0x80200000 kernel.bin' +# +# +options PHYSADDR=0x80000000 +options KERNPHYSADDR=0x80200000 +makeoptions KERNPHYSADDR=0x80200000 +options KERNVIRTADDR=0xc0200000 # Used in ldscript.arm +makeoptions KERNVIRTADDR=0xc0200000 + +options STARTUP_PAGETABLE_ADDR=0x80000000 + +options SOC_TI_AM335X + +options ARM_L2_PIPT diff --git a/sys/arm/ti/am335x/std.beaglebone b/sys/arm/ti/am335x/std.beaglebone new file mode 100644 index 000000000000..299e822532d2 --- /dev/null +++ b/sys/arm/ti/am335x/std.beaglebone @@ -0,0 +1,4 @@ +# $FreeBSD$ + +files "../ti/am335x/files.beaglebone" +include "../ti/am335x/std.am335x" diff --git a/sys/arm/ti/bus_space.c b/sys/arm/ti/bus_space.c new file mode 100644 index 000000000000..4cce8203abcc --- /dev/null +++ b/sys/arm/ti/bus_space.c @@ -0,0 +1,113 @@ +/*- + * Copyright (C) 2012 FreeBSD Foundation + * 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. Neither the name of MARVELL nor the names of contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include + +#include + +/* Prototypes for all the bus_space structure functions */ +bs_protos(generic); +bs_protos(generic_armv4); + +struct bus_space _base_tag = { + /* cookie */ + .bs_cookie = (void *) 0, + + /* mapping/unmapping */ + .bs_map = generic_bs_map, + .bs_unmap = generic_bs_unmap, + .bs_subregion = generic_bs_subregion, + + /* allocation/deallocation */ + .bs_alloc = generic_bs_alloc, + .bs_free = generic_bs_free, + + /* barrier */ + .bs_barrier = generic_bs_barrier, + + /* read (single) */ + .bs_r_1 = generic_bs_r_1, + .bs_r_2 = generic_armv4_bs_r_2, + .bs_r_4 = generic_bs_r_4, + .bs_r_8 = NULL, + + /* read multiple */ + .bs_rm_1 = generic_bs_rm_1, + .bs_rm_2 = generic_armv4_bs_rm_2, + .bs_rm_4 = generic_bs_rm_4, + .bs_rm_8 = NULL, + + /* read region */ + .bs_rr_1 = generic_bs_rr_1, + .bs_rr_2 = generic_armv4_bs_rr_2, + .bs_rr_4 = generic_bs_rr_4, + .bs_rr_8 = NULL, + + /* write (single) */ + .bs_w_1 = generic_bs_w_1, + .bs_w_2 = generic_armv4_bs_w_2, + .bs_w_4 = generic_bs_w_4, + .bs_w_8 = NULL, + + /* write multiple */ + .bs_wm_1 = generic_bs_wm_1, + .bs_wm_2 = generic_armv4_bs_wm_2, + .bs_wm_4 = generic_bs_wm_4, + .bs_wm_8 = NULL, + + /* write region */ + .bs_wr_1 = generic_bs_wr_1, + .bs_wr_2 = generic_armv4_bs_wr_2, + .bs_wr_4 = generic_bs_wr_4, + .bs_wr_8 = NULL, + + /* set multiple */ + /* XXX not implemented */ + + /* set region */ + .bs_sr_1 = NULL, + .bs_sr_2 = generic_armv4_bs_sr_2, + .bs_sr_4 = generic_bs_sr_4, + .bs_sr_8 = NULL, + + /* copy */ + .bs_c_1 = NULL, + .bs_c_2 = generic_armv4_bs_c_2, + .bs_c_4 = NULL, + .bs_c_8 = NULL, +}; + +bus_space_tag_t fdtbus_bs_tag = &_base_tag; diff --git a/sys/arm/ti/common.c b/sys/arm/ti/common.c new file mode 100644 index 000000000000..983e6f807617 --- /dev/null +++ b/sys/arm/ti/common.c @@ -0,0 +1,98 @@ +/*- + * Copyright (C) 2008-2011 MARVELL INTERNATIONAL LTD. + * All rights reserved. + * + * Developed by Semihalf. + * + * 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. Neither the name of MARVELL nor the names of contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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. + */ + +#include "opt_global.h" + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +struct fdt_fixup_entry fdt_fixup_table[] = { + { NULL, NULL } +}; + +#ifdef SOC_OMAP4 +static int +fdt_gic_decode_ic(phandle_t node, pcell_t *intr, int *interrupt, int *trig, + int *pol) +{ + + if (!fdt_is_compatible(node, "arm,gic")) + return (ENXIO); + + *interrupt = fdt32_to_cpu(intr[0]); + *trig = INTR_TRIGGER_CONFORM; + *pol = INTR_POLARITY_CONFORM; + + return (0); +} +#endif + +#ifdef SOC_TI_AM335X +static int +fdt_aintc_decode_ic(phandle_t node, pcell_t *intr, int *interrupt, int *trig, + int *pol) +{ + + if (!fdt_is_compatible(node, "ti,aintc")) + return (ENXIO); + + *interrupt = fdt32_to_cpu(intr[0]); + *trig = INTR_TRIGGER_CONFORM; + *pol = INTR_POLARITY_CONFORM; + + return (0); +} +#endif + +fdt_pic_decode_t fdt_pic_table[] = { +#ifdef SOC_OMAP4 + &fdt_gic_decode_ic, +#endif +#ifdef SOC_TI_AM335X + &fdt_aintc_decode_ic, +#endif + NULL +}; diff --git a/sys/arm/ti/cpsw/if_cpsw.c b/sys/arm/ti/cpsw/if_cpsw.c new file mode 100644 index 000000000000..68635282c87b --- /dev/null +++ b/sys/arm/ti/cpsw/if_cpsw.c @@ -0,0 +1,1251 @@ +/*- + * 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. + */ + +/* + * TI 3 Port Switch Ethernet (CPSW) Driver + * Found in TI8148, AM335x SoCs + */ + +#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 "miibus_if.h" + +static int cpsw_probe(device_t dev); +static int cpsw_attach(device_t dev); +static int cpsw_detach(device_t dev); +static int cpsw_shutdown(device_t dev); +static int cpsw_suspend(device_t dev); +static int cpsw_resume(device_t dev); + +static int cpsw_miibus_readreg(device_t dev, int phy, int reg); +static int cpsw_miibus_writereg(device_t dev, int phy, int reg, int value); + +static int cpsw_ifmedia_upd(struct ifnet *ifp); +static void cpsw_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr); + +static void cpsw_init(void *arg); +static void cpsw_init_locked(void *arg); +static void cpsw_start(struct ifnet *ifp); +static void cpsw_start_locked(struct ifnet *ifp); +static void cpsw_stop(struct cpsw_softc *sc); +static int cpsw_ioctl(struct ifnet *ifp, u_long command, caddr_t data); +static int cpsw_allocate_dma(struct cpsw_softc *sc); +static int cpsw_free_dma(struct cpsw_softc *sc); +static int cpsw_new_rxbuf(struct cpsw_softc *sc, uint32_t i, uint32_t next); +static void cpsw_watchdog(struct cpsw_softc *sc); + +static void cpsw_intr_rx_thresh(void *arg); +static void cpsw_intr_rx(void *arg); +static void cpsw_intr_rx_locked(void *arg); +static void cpsw_intr_tx(void *arg); +static void cpsw_intr_tx_locked(void *arg); +static void cpsw_intr_misc(void *arg); + +static void cpsw_ale_read_entry(struct cpsw_softc *sc, uint16_t idx, uint32_t *ale_entry); +static void cpsw_ale_write_entry(struct cpsw_softc *sc, uint16_t idx, uint32_t *ale_entry); +static int cpsw_ale_uc_entry_set(struct cpsw_softc *sc, uint8_t port, uint8_t *mac); +static int cpsw_ale_mc_entry_set(struct cpsw_softc *sc, uint8_t portmap, uint8_t *mac); +#ifdef CPSW_DEBUG +static void cpsw_ale_dump_table(struct cpsw_softc *sc); +#endif + +static device_method_t cpsw_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, cpsw_probe), + DEVMETHOD(device_attach, cpsw_attach), + DEVMETHOD(device_detach, cpsw_detach), + DEVMETHOD(device_shutdown, cpsw_shutdown), + DEVMETHOD(device_suspend, cpsw_suspend), + DEVMETHOD(device_resume, cpsw_resume), + /* MII interface */ + DEVMETHOD(miibus_readreg, cpsw_miibus_readreg), + DEVMETHOD(miibus_writereg, cpsw_miibus_writereg), + { 0, 0 } +}; + +static driver_t cpsw_driver = { + "cpsw", + cpsw_methods, + sizeof(struct cpsw_softc), +}; + +static devclass_t cpsw_devclass; + + +DRIVER_MODULE(cpsw, simplebus, cpsw_driver, cpsw_devclass, 0, 0); +DRIVER_MODULE(miibus, cpsw, miibus_driver, miibus_devclass, 0, 0); +MODULE_DEPEND(cpsw, ether, 1, 1, 1); +MODULE_DEPEND(cpsw, miibus, 1, 1, 1); + +static struct resource_spec res_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { SYS_RES_IRQ, 0, RF_ACTIVE | RF_SHAREABLE }, + { SYS_RES_IRQ, 1, RF_ACTIVE | RF_SHAREABLE }, + { SYS_RES_IRQ, 2, RF_ACTIVE | RF_SHAREABLE }, + { SYS_RES_IRQ, 3, RF_ACTIVE | RF_SHAREABLE }, + { -1, 0 } +}; + +static struct { + driver_intr_t *handler; + char * description; +} cpsw_intrs[CPSW_INTR_COUNT + 1] = { + { cpsw_intr_rx_thresh,"CPSW RX threshold interrupt" }, + { cpsw_intr_rx, "CPSW RX interrupt" }, + { cpsw_intr_tx, "CPSW TX interrupt" }, + { cpsw_intr_misc,"CPSW misc interrupt" }, +}; + +/* Locking macros */ +#define CPSW_TX_LOCK(sc) do { \ + mtx_assert(&(sc)->rx_lock, MA_NOTOWNED); \ + mtx_lock(&(sc)->tx_lock); \ +} while (0) + +#define CPSW_TX_UNLOCK(sc) mtx_unlock(&(sc)->tx_lock) +#define CPSW_TX_LOCK_ASSERT(sc) mtx_assert(&(sc)->tx_lock, MA_OWNED) + +#define CPSW_RX_LOCK(sc) do { \ + mtx_assert(&(sc)->tx_lock, MA_NOTOWNED); \ + mtx_lock(&(sc)->rx_lock); \ +} while (0) + +#define CPSW_RX_UNLOCK(sc) mtx_unlock(&(sc)->rx_lock) +#define CPSW_RX_LOCK_ASSERT(sc) mtx_assert(&(sc)->rx_lock, MA_OWNED) + +#define CPSW_GLOBAL_LOCK(sc) do { \ + if ((mtx_owned(&(sc)->tx_lock) ? 1 : 0) != \ + (mtx_owned(&(sc)->rx_lock) ? 1 : 0)) { \ + panic("cpsw deadlock possibility detection!"); \ + } \ + mtx_lock(&(sc)->tx_lock); \ + mtx_lock(&(sc)->rx_lock); \ +} while (0) + +#define CPSW_GLOBAL_UNLOCK(sc) do { \ + CPSW_RX_UNLOCK(sc); \ + CPSW_TX_UNLOCK(sc); \ +} while (0) + +#define CPSW_GLOBAL_LOCK_ASSERT(sc) do { \ + CPSW_TX_LOCK_ASSERT(sc); \ + CPSW_RX_LOCK_ASSERT(sc); \ +} while (0) + + +static int +cpsw_probe(device_t dev) +{ + + if (!ofw_bus_is_compatible(dev, "ti,cpsw")) + return (ENXIO); + + device_set_desc(dev, "3-port Switch Ethernet Subsystem"); + return (BUS_PROBE_DEFAULT); +} + +static int +cpsw_attach(device_t dev) +{ + struct cpsw_softc *sc; + struct mii_softc *miisc; + struct ifnet *ifp; + uint8_t mac_addr[ETHER_ADDR_LEN]; + int i, error, phy; + uint32_t reg; + + sc = device_get_softc(dev); + sc->dev = dev; + memcpy(sc->mac_addr, mac_addr, ETHER_ADDR_LEN); + sc->node = ofw_bus_get_node(dev); + + /* Get phy address from fdt */ + if (fdt_get_phyaddr(sc->node, sc->dev, &phy, (void **)&sc->phy_sc) != 0) { + device_printf(dev, "failed to get PHY address from FDT\n"); + return (ENXIO); + } + /* Initialize mutexes */ + mtx_init(&sc->tx_lock, device_get_nameunit(dev), + "cpsw TX lock", MTX_DEF); + mtx_init(&sc->rx_lock, device_get_nameunit(dev), + "cpsw RX lock", MTX_DEF); + + /* Allocate IO and IRQ resources */ + error = bus_alloc_resources(dev, res_spec, sc->res); + if (error) { + device_printf(dev, "could not allocate resources\n"); + cpsw_detach(dev); + return (ENXIO); + } + + reg = cpsw_read_4(CPSW_SS_IDVER); + device_printf(dev, "Version %d.%d (%d)\n", (reg >> 8 & 0x7), + reg & 0xFF, (reg >> 11) & 0x1F); + + /* Allocate DMA, buffers, buffer descriptors */ + error = cpsw_allocate_dma(sc); + if (error) { + cpsw_detach(dev); + return (ENXIO); + } + + //cpsw_add_sysctls(sc); TODO + + /* Allocate network interface */ + ifp = sc->ifp = if_alloc(IFT_ETHER); + if (ifp == NULL) { + device_printf(dev, "if_alloc() failed\n"); + cpsw_detach(dev); + return (ENOMEM); + } + + if_initname(ifp, device_get_name(dev), device_get_unit(dev)); + ifp->if_softc = sc; + ifp->if_flags = IFF_SIMPLEX | IFF_MULTICAST | IFF_BROADCAST; + ifp->if_capabilities = IFCAP_VLAN_MTU | IFCAP_HWCSUM; //FIXME VLAN? + ifp->if_capenable = ifp->if_capabilities; + + ifp->if_init = cpsw_init; + ifp->if_start = cpsw_start; + ifp->if_ioctl = cpsw_ioctl; + + ifp->if_snd.ifq_drv_maxlen = CPSW_MAX_TX_BUFFERS - 1; + IFQ_SET_MAXLEN(&ifp->if_snd, ifp->if_snd.ifq_drv_maxlen); + IFQ_SET_READY(&ifp->if_snd); + + /* Get low part of MAC address from control module (mac_id0_lo) */ + ti_scm_reg_read_4(0x630, ®); + mac_addr[0] = (reg >> 8) & 0xFF; + mac_addr[1] = reg & 0xFF; + + /* Get high part of MAC address from control module (mac_id0_hi) */ + ti_scm_reg_read_4(0x634, ®); + mac_addr[2] = (reg >> 24) & 0xFF; + mac_addr[3] = (reg >> 16) & 0xFF; + mac_addr[4] = (reg >> 8) & 0xFF; + mac_addr[5] = reg & 0xFF; + + ether_ifattach(ifp, sc->mac_addr); + callout_init(&sc->wd_callout, 0); + + /* Initialze MDIO - ENABLE, PREAMBLE=0, FAULTENB, CLKDIV=0xFF */ + /* TODO Calculate MDCLK=CLK/(CLKDIV+1) */ + cpsw_write_4(MDIOCONTROL, (1<<30) | (1<<18) | 0xFF); + + /* Attach PHY(s) */ + error = mii_attach(dev, &sc->miibus, ifp, cpsw_ifmedia_upd, + cpsw_ifmedia_sts, BMSR_DEFCAPMASK, phy, MII_OFFSET_ANY, 0); + if (error) { + device_printf(dev, "attaching PHYs failed\n"); + cpsw_detach(dev); + return (error); + } + sc->mii = device_get_softc(sc->miibus); + + /* Tell the MAC where to find the PHY so autoneg works */ + miisc = LIST_FIRST(&sc->mii->mii_phys); + + /* Select PHY and enable interrupts */ + cpsw_write_4(MDIOUSERPHYSEL0, (1 << 6) | (miisc->mii_phy & 0x1F)); + + /* Attach interrupt handlers */ + for (i = 1; i <= CPSW_INTR_COUNT; ++i) { + error = bus_setup_intr(dev, sc->res[i], + INTR_TYPE_NET | INTR_MPSAFE, + NULL, *cpsw_intrs[i - 1].handler, + sc, &sc->ih_cookie[i - 1]); + if (error) { + device_printf(dev, "could not setup %s\n", + cpsw_intrs[i].description); + cpsw_detach(dev); + return (error); + } + } + + return (0); +} + +static int +cpsw_detach(device_t dev) +{ + struct cpsw_softc *sc; + int error,i; + + sc = device_get_softc(dev); + + /* Stop controller and free TX queue */ + if (sc->ifp) + cpsw_shutdown(dev); + + /* Wait for stopping ticks */ + callout_drain(&sc->wd_callout); + + /* Stop and release all interrupts */ + for (i = 0; i < CPSW_INTR_COUNT; ++i) { + if (!sc->ih_cookie[i]) + continue; + + error = bus_teardown_intr(dev, sc->res[1 + i], sc->ih_cookie[i]); + if (error) + device_printf(dev, "could not release %s\n", + cpsw_intrs[i + 1].description); + } + + /* Detach network interface */ + if (sc->ifp) { + ether_ifdetach(sc->ifp); + if_free(sc->ifp); + } + + /* Free DMA resources */ + cpsw_free_dma(sc); + + /* Free IO memory handler */ + bus_release_resources(dev, res_spec, sc->res); + + /* Destroy mutexes */ + mtx_destroy(&sc->rx_lock); + mtx_destroy(&sc->tx_lock); + + return (0); +} + +static int +cpsw_suspend(device_t dev) +{ + + device_printf(dev, "%s\n", __FUNCTION__); + return (0); +} + +static int +cpsw_resume(device_t dev) +{ + + device_printf(dev, "%s\n", __FUNCTION__); + return (0); +} + +static int +cpsw_shutdown(device_t dev) +{ + struct cpsw_softc *sc = device_get_softc(dev); + + CPSW_GLOBAL_LOCK(sc); + + cpsw_stop(sc); + + CPSW_GLOBAL_UNLOCK(sc); + + return (0); +} + +static int +cpsw_miibus_readreg(device_t dev, int phy, int reg) +{ + struct cpsw_softc *sc; + uint32_t r; + uint32_t retries = CPSW_MIIBUS_RETRIES; + + sc = device_get_softc(dev); + + /* Wait until interface is ready by watching GO bit */ + while(--retries && (cpsw_read_4(MDIOUSERACCESS0) & (1 << 31)) ) + DELAY(CPSW_MIIBUS_DELAY); + if (!retries) + device_printf(dev, "Timeout while waiting for MDIO.\n"); + + /* Set GO, phy and reg */ + cpsw_write_4(MDIOUSERACCESS0, (1 << 31) | + ((reg & 0x1F) << 21) | ((phy & 0x1F) << 16)); + + while(--retries && (cpsw_read_4(MDIOUSERACCESS0) & (1 << 31)) ) + DELAY(CPSW_MIIBUS_DELAY); + if (!retries) + device_printf(dev, "Timeout while waiting for MDIO.\n"); + + r = cpsw_read_4(MDIOUSERACCESS0); + /* Check for ACK */ + if(r & (1<<29)) { + return (r & 0xFFFF); + } + device_printf(dev, "Failed to read from PHY.\n"); + return 0; +} + +static int +cpsw_miibus_writereg(device_t dev, int phy, int reg, int value) +{ + struct cpsw_softc *sc; + uint32_t retries = CPSW_MIIBUS_RETRIES; + + sc = device_get_softc(dev); + + /* Wait until interface is ready by watching GO bit */ + while(--retries && (cpsw_read_4(MDIOUSERACCESS0) & (1 << 31)) ) + DELAY(CPSW_MIIBUS_DELAY); + if (!retries) + device_printf(dev, "Timeout while waiting for MDIO.\n"); + + /* Set GO, WRITE, phy, reg and value */ + cpsw_write_4(MDIOUSERACCESS0, (value & 0xFFFF) | (3 << 30) | + ((reg & 0x1F) << 21) | ((phy & 0x1F) << 16)); + + while(--retries && (cpsw_read_4(MDIOUSERACCESS0) & (1 << 31)) ) + DELAY(CPSW_MIIBUS_DELAY); + if (!retries) + device_printf(dev, "Timeout while waiting for MDIO.\n"); + + /* Check for ACK */ + if(cpsw_read_4(MDIOUSERACCESS0) & (1<<29)) { + return 0; + } + device_printf(dev, "Failed to write to PHY.\n"); + + return 0; +} + +static int +cpsw_allocate_dma(struct cpsw_softc *sc) +{ + int err; + int i; + + /* Allocate a busdma tag and DMA safe memory for tx mbufs. */ + err = bus_dma_tag_create( + bus_get_dma_tag(sc->dev), /* parent */ + 1, 0, /* alignment, boundary */ + BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filtfunc, filtfuncarg */ + MCLBYTES, 1, /* maxsize, nsegments */ + MCLBYTES, 0, /* maxsegsz, flags */ + NULL, NULL, /* lockfunc, lockfuncarg */ + &sc->mbuf_dtag); /* dmatag */ + + if (err) + return (ENOMEM); + for (i = 0; i < CPSW_MAX_TX_BUFFERS; i++) { + if ( bus_dmamap_create(sc->mbuf_dtag, 0, &sc->tx_dmamap[i])) { + if_printf(sc->ifp, "failed to create dmamap for rx mbuf\n"); + return (ENOMEM); + } + } + + for (i = 0; i < CPSW_MAX_RX_BUFFERS; i++) { + if ( bus_dmamap_create(sc->mbuf_dtag, 0, &sc->rx_dmamap[i])) { + if_printf(sc->ifp, "failed to create dmamap for rx mbuf\n"); + return (ENOMEM); + } + } + + return (0); +} + +static int +cpsw_free_dma(struct cpsw_softc *sc) +{ + (void)sc; /* UNUSED */ + // TODO + return 0; +} + +static int +cpsw_new_rxbuf(struct cpsw_softc *sc, uint32_t i, uint32_t next) +{ + bus_dma_segment_t seg[1]; + struct cpsw_cpdma_bd bd; + int error; + int nsegs; + + if (sc->rx_mbuf[i]) { + bus_dmamap_sync(sc->mbuf_dtag, sc->rx_dmamap[i], BUS_DMASYNC_POSTREAD); + bus_dmamap_unload(sc->mbuf_dtag, sc->rx_dmamap[i]); + } + + sc->rx_mbuf[i] = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); + if (sc->rx_mbuf[i] == NULL) + return (ENOBUFS); + + sc->rx_mbuf[i]->m_len = sc->rx_mbuf[i]->m_pkthdr.len = sc->rx_mbuf[i]->m_ext.ext_size; + + error = bus_dmamap_load_mbuf_sg(sc->mbuf_dtag, sc->rx_dmamap[i], + sc->rx_mbuf[i], seg, &nsegs, BUS_DMA_NOWAIT); + + KASSERT(nsegs == 1, ("Too many segments returned!")); + if (nsegs != 1 || error) + panic("%s: nsegs(%d), error(%d)",__func__, nsegs, error); + + bus_dmamap_sync(sc->mbuf_dtag, sc->rx_dmamap[i], BUS_DMASYNC_PREREAD); + + /* Create and submit new rx descriptor*/ + bd.next = next; + bd.bufptr = seg->ds_addr; + bd.buflen = MCLBYTES-1; + bd.bufoff = 2; /* make IP hdr aligned with 4 */ + bd.pktlen = 0; + bd.flags = CPDMA_BD_OWNER; + cpsw_cpdma_write_rxbd(i, &bd); + + return (0); +} + + +static int +cpsw_encap(struct cpsw_softc *sc, struct mbuf *m0) +{ + bus_dma_segment_t seg[1]; + struct cpsw_cpdma_bd bd; + int error; + int nsegs; + int idx; + + if (sc->txbd_queue_size == CPSW_MAX_TX_BUFFERS) + return (ENOBUFS); + + idx = sc->txbd_head + sc->txbd_queue_size; + + if (idx >= (CPSW_MAX_TX_BUFFERS) ) + idx -= CPSW_MAX_TX_BUFFERS; + + /* Create mapping in DMA memory */ + error = bus_dmamap_load_mbuf_sg(sc->mbuf_dtag, sc->tx_dmamap[idx], m0, seg, &nsegs, + BUS_DMA_NOWAIT); + sc->tc[idx]++; + if (error != 0 || nsegs != 1 ) { + bus_dmamap_unload(sc->mbuf_dtag, sc->tx_dmamap[idx]); + return ((error != 0) ? error : -1); + } + bus_dmamap_sync(sc->mbuf_dtag, sc->tx_dmamap[idx], BUS_DMASYNC_PREWRITE); + + /* Fill descriptor data */ + bd.next = 0; + bd.bufptr = seg->ds_addr; + bd.bufoff = 0; + bd.buflen = (seg->ds_len < 64 ? 64 : seg->ds_len); + bd.pktlen = (seg->ds_len < 64 ? 64 : seg->ds_len); + /* Set OWNERSHIP, SOP, EOP */ + bd.flags = (7<<13); + + /* Write descriptor */ + cpsw_cpdma_write_txbd(idx, &bd); + + /* Previous descriptor should point to us */ + cpsw_cpdma_write_txbd_next(((idx-1<0)?(CPSW_MAX_TX_BUFFERS-1):(idx-1)), + cpsw_cpdma_txbd_paddr(idx)); + + sc->txbd_queue_size++; + + return (0); +} + +static void +cpsw_start(struct ifnet *ifp) +{ + struct cpsw_softc *sc = ifp->if_softc; + + CPSW_TX_LOCK(sc); + cpsw_start_locked(ifp); + CPSW_TX_UNLOCK(sc); +} + +static void +cpsw_start_locked(struct ifnet *ifp) +{ + struct cpsw_softc *sc = ifp->if_softc; + struct mbuf *m0, *mtmp; + uint32_t queued = 0; + + CPSW_TX_LOCK_ASSERT(sc); + + if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != + IFF_DRV_RUNNING) + return; + + for (;;) { + /* Get packet from the queue */ + IF_DEQUEUE(&ifp->if_snd, m0); + if (m0 == NULL) + break; + + mtmp = m_defrag(m0, M_DONTWAIT); + if (mtmp) + m0 = mtmp; + + if (cpsw_encap(sc, m0)) { + IF_PREPEND(&ifp->if_snd, m0); + ifp->if_drv_flags |= IFF_DRV_OACTIVE; + break; + } + queued++; + BPF_MTAP(ifp, m0); + } + + if (!queued) + return; + + if (sc->eoq) { + cpsw_write_4(CPSW_CPDMA_TX_HDP(0), cpsw_cpdma_txbd_paddr(sc->txbd_head)); + sc->eoq = 0; + } + sc->wd_timer = 5; +} + +static void +cpsw_stop(struct cpsw_softc *sc) +{ + struct ifnet *ifp; + + ifp = sc->ifp; + + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) + return; + + /* Stop tick engine */ + callout_stop(&sc->wd_callout); + + /* Disable interface */ + ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); + sc->wd_timer = 0; + + /* Disable interrupts TODO */ + +} + +static int +cpsw_ioctl(struct ifnet *ifp, u_long command, caddr_t data) +{ + struct cpsw_softc *sc = ifp->if_softc; + struct ifreq *ifr = (struct ifreq *)data; + int error; + uint32_t flags; + + error = 0; + + // FIXME + switch (command) { + case SIOCSIFFLAGS: + CPSW_GLOBAL_LOCK(sc); + if (ifp->if_flags & IFF_UP) { + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + flags = ifp->if_flags ^ sc->cpsw_if_flags; + if (flags & IFF_PROMISC) + printf("%s: SIOCSIFFLAGS " + "IFF_PROMISC unimplemented\n", + __func__); + + if (flags & IFF_ALLMULTI) + printf("%s: SIOCSIFFLAGS " + "IFF_ALLMULTI unimplemented\n", + __func__); + } else { + printf("%s: SIOCSIFFLAGS cpsw_init_locked\n", __func__); + //cpsw_init_locked(sc); + } + } + else if (ifp->if_drv_flags & IFF_DRV_RUNNING) + cpsw_stop(sc); + + sc->cpsw_if_flags = ifp->if_flags; + CPSW_GLOBAL_UNLOCK(sc); + break; + printf("%s: SIOCSIFFLAGS\n",__func__); + break; + case SIOCADDMULTI: + printf("%s: SIOCADDMULTI\n",__func__); + break; + case SIOCDELMULTI: + printf("%s: SIOCDELMULTI\n",__func__); + break; + case SIOCSIFCAP: + printf("%s: SIOCSIFCAP\n",__func__); + break; + case SIOCGIFMEDIA: /* fall through */ + printf("%s: SIOCGIFMEDIA\n",__func__); + error = ifmedia_ioctl(ifp, ifr, &sc->mii->mii_media, command); + break; + case SIOCSIFMEDIA: + printf("%s: SIOCSIFMEDIA\n",__func__); + error = ifmedia_ioctl(ifp, ifr, &sc->mii->mii_media, command); + break; + default: + error = ether_ioctl(ifp, command, data); + } + return (error); +} + +static void +cpsw_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) +{ + struct cpsw_softc *sc = ifp->if_softc; + struct mii_data *mii; + + CPSW_TX_LOCK(sc); + + mii = sc->mii; + mii_pollstat(mii); + + ifmr->ifm_active = mii->mii_media_active; + ifmr->ifm_status = mii->mii_media_status; + + CPSW_TX_UNLOCK(sc); +} + + +static int +cpsw_ifmedia_upd(struct ifnet *ifp) +{ + struct cpsw_softc *sc = ifp->if_softc; + + if (ifp->if_flags & IFF_UP) { + CPSW_GLOBAL_LOCK(sc); + + sc->cpsw_media_status = sc->mii->mii_media.ifm_media; + mii_mediachg(sc->mii); + cpsw_init_locked(sc); + + CPSW_GLOBAL_UNLOCK(sc); + } + + return (0); +} + +static void +cpsw_intr_rx_thresh(void *arg) +{ + (void)arg; /* UNUSED */ +} + +static void +cpsw_intr_rx(void *arg) +{ + struct cpsw_softc *sc = arg; + CPSW_RX_LOCK(sc); + cpsw_intr_rx_locked(arg); + CPSW_RX_UNLOCK(sc); +} + +static void +cpsw_intr_rx_locked(void *arg) +{ + struct cpsw_softc *sc = arg; + struct cpsw_cpdma_bd bd; + struct ifnet *ifp; + int i; + + ifp = sc->ifp; + + i = sc->rxbd_head; + cpsw_cpdma_read_rxbd(i, &bd); + + while (bd.flags & CPDMA_BD_SOP) { + cpsw_write_4(CPSW_CPDMA_RX_CP(0), cpsw_cpdma_rxbd_paddr(i)); + + bus_dmamap_sync(sc->mbuf_dtag, sc->rx_dmamap[i], BUS_DMASYNC_POSTREAD); + + /* Fill mbuf */ + sc->rx_mbuf[i]->m_hdr.mh_data +=2; + sc->rx_mbuf[i]->m_len = bd.pktlen-2; + sc->rx_mbuf[i]->m_pkthdr.len = bd.pktlen-2; + sc->rx_mbuf[i]->m_flags |= M_PKTHDR; + sc->rx_mbuf[i]->m_pkthdr.rcvif = ifp; + + if ((ifp->if_capenable & IFCAP_RXCSUM) != 0) { + /* check for valid CRC by looking into pkt_err[5:4] */ + if ( (bd.flags & CPDMA_BD_PKT_ERR_MASK) == 0 ) { + sc->rx_mbuf[i]->m_pkthdr.csum_flags |= CSUM_IP_CHECKED; + sc->rx_mbuf[i]->m_pkthdr.csum_flags |= CSUM_IP_VALID; + sc->rx_mbuf[i]->m_pkthdr.csum_data = 0xffff; + } + } + + /* Handover packet */ + CPSW_RX_UNLOCK(sc); + (*ifp->if_input)(ifp, sc->rx_mbuf[i]); + CPSW_RX_LOCK(sc); + + /* Allocate new buffer for current descriptor */ + cpsw_new_rxbuf(sc, i, 0); + + /* we are not at tail so old tail BD should point to new one */ + cpsw_cpdma_write_rxbd_next(sc->rxbd_tail, + cpsw_cpdma_rxbd_paddr(i)); + + /* Check if EOQ is reached */ + if (cpsw_cpdma_read_rxbd_flags(sc->rxbd_tail) & CPDMA_BD_EOQ) { + cpsw_write_4(CPSW_CPDMA_RX_HDP(0), cpsw_cpdma_rxbd_paddr(i)); + } + sc->rxbd_tail = i; + + /* read next descriptor */ + if (++i == CPSW_MAX_RX_BUFFERS) + i = 0; + cpsw_cpdma_read_rxbd(i, &bd); + sc->rxbd_head = i; + } + + cpsw_write_4(CPSW_CPDMA_CPDMA_EOI_VECTOR, 1); +} + +static void +cpsw_intr_tx(void *arg) +{ + struct cpsw_softc *sc = arg; + CPSW_TX_LOCK(sc); + cpsw_intr_tx_locked(arg); + CPSW_TX_UNLOCK(sc); +} + +static void +cpsw_intr_tx_locked(void *arg) +{ + struct cpsw_softc *sc = arg; + uint32_t flags; + + if(sc->txbd_head == -1) + return; + + if(sc->txbd_queue_size<1) { + /* in some casses interrupt happens even when there is no + data in transmit queue */ + return; + } + + /* Disable watchdog */ + sc->wd_timer = 0; + + flags = cpsw_cpdma_read_txbd_flags(sc->txbd_head); + + /* After BD is transmitted CPDMA will set OWNER to 0 */ + if (flags & CPDMA_BD_OWNER) + return; + + if(flags & CPDMA_BD_EOQ) + sc->eoq=1; + + /* release dmamap and mbuf */ + bus_dmamap_sync(sc->mbuf_dtag, sc->tx_dmamap[sc->txbd_head], + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->mbuf_dtag, sc->tx_dmamap[sc->txbd_head]); + m_freem(sc->tx_mbuf[sc->txbd_head]); + + cpsw_write_4(CPSW_CPDMA_TX_CP(0), cpsw_cpdma_txbd_paddr(sc->txbd_head)); + + if (++sc->txbd_head == CPSW_MAX_TX_BUFFERS) + sc->txbd_head = 0; + + --sc->txbd_queue_size; + + cpsw_write_4(CPSW_CPDMA_CPDMA_EOI_VECTOR, 2); + cpsw_write_4(CPSW_CPDMA_CPDMA_EOI_VECTOR, 1); +} + +static void +cpsw_intr_misc(void *arg) +{ + struct cpsw_softc *sc = arg; + uint32_t stat = cpsw_read_4(CPSW_WR_C_MISC_STAT(0)); + printf("%s: stat=%x\n",__func__,stat); + /* EOI_RX_PULSE */ + cpsw_write_4(CPSW_CPDMA_CPDMA_EOI_VECTOR, 3); +} + +static void +cpsw_tick(void *msc) +{ + struct cpsw_softc *sc = msc; + + /* Check for TX timeout */ + cpsw_watchdog(sc); + + mii_tick(sc->mii); + + /* Check for media type change */ + if(sc->cpsw_media_status != sc->mii->mii_media.ifm_media) { + printf("%s: media type changed (ifm_media=%x)\n",__func__, + sc->mii->mii_media.ifm_media); + cpsw_ifmedia_upd(sc->ifp); + } + + /* Schedule another timeout one second from now */ + callout_reset(&sc->wd_callout, hz, cpsw_tick, sc); +} + +static void +cpsw_watchdog(struct cpsw_softc *sc) +{ + struct ifnet *ifp; + + ifp = sc->ifp; + + CPSW_GLOBAL_LOCK(sc); + + if (sc->wd_timer == 0 || --sc->wd_timer) { + CPSW_GLOBAL_UNLOCK(sc); + return; + } + + ifp->if_oerrors++; + if_printf(ifp, "watchdog timeout\n"); + + cpsw_stop(sc); + cpsw_init_locked(sc); + + CPSW_GLOBAL_UNLOCK(sc); +} + +static void +cpsw_init(void *arg) +{ + struct cpsw_softc *sc = arg; + CPSW_GLOBAL_LOCK(sc); + cpsw_init_locked(arg); + CPSW_GLOBAL_UNLOCK(sc); +} + +int once = 1; + +static void +cpsw_init_locked(void *arg) +{ + struct ifnet *ifp; + struct cpsw_softc *sc = arg; + uint8_t broadcast_address[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + uint32_t next_bdp; + uint32_t i; + + ifp = sc->ifp; + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) + return; + + printf("%s: start\n",__func__); + + /* Reset writer */ + cpsw_write_4(CPSW_WR_SOFT_RESET, 1); + while(cpsw_read_4(CPSW_WR_SOFT_RESET) & 1); + + /* Reset SS */ + cpsw_write_4(CPSW_SS_SOFT_RESET, 1); + while(cpsw_read_4(CPSW_SS_SOFT_RESET) & 1); + + /* Clear table (30) and enable ALE(31) */ + if (once) + cpsw_write_4(CPSW_ALE_CONTROL, (3 << 30)); + else + cpsw_write_4(CPSW_ALE_CONTROL, (1 << 31)); + once = 0; // FIXME + + /* Reset and init Sliver port 1 and 2 */ + for(i=0;i<2;i++) { + /* Reset */ + cpsw_write_4(CPSW_SL_SOFT_RESET(i), 1); + while(cpsw_read_4(CPSW_SL_SOFT_RESET(i)) & 1); + /* Set Slave Mapping */ + cpsw_write_4(CPSW_SL_RX_PRI_MAP(i),0x76543210); + cpsw_write_4(CPSW_PORT_P_TX_PRI_MAP(i+1),0x33221100); + cpsw_write_4(CPSW_SL_RX_MAXLEN(i),0x5f2); + /* Set MAC Address */ + cpsw_write_4(CPSW_PORT_P_SA_HI(i+1), sc->mac_addr[0] | + (sc->mac_addr[1] << 8) | + (sc->mac_addr[2] << 16) | + (sc->mac_addr[3] << 24)); + cpsw_write_4(CPSW_PORT_P_SA_LO(i+1), sc->mac_addr[4] | + (sc->mac_addr[5] << 8)); + + /* Set MACCONTROL for ports 0,1: FULLDUPLEX(1), GMII_EN(5), + IFCTL_A(15), IFCTL_B(16) FIXME */ + cpsw_write_4(CPSW_SL_MACCONTROL(i), 1 | (1<<5) | (1<<15)); + + /* Set ALE port to forwarding(3) */ + cpsw_write_4(CPSW_ALE_PORTCTL(i+1), 3); + } + + /* Set Host Port Mapping */ + cpsw_write_4(CPSW_PORT_P0_CPDMA_TX_PRI_MAP, 0x76543210); + cpsw_write_4(CPSW_PORT_P0_CPDMA_RX_CH_MAP, 0); + + /* Set ALE port to forwarding(3)*/ + cpsw_write_4(CPSW_ALE_PORTCTL(0), 3); + + /* Add own MAC address and broadcast to ALE */ + cpsw_ale_uc_entry_set(sc, 0, sc->mac_addr); + cpsw_ale_mc_entry_set(sc, 7, broadcast_address); + + cpsw_write_4(CPSW_SS_PTYPE, 0); + /* Enable statistics for ports 0, 1 and 2 */ + cpsw_write_4(CPSW_SS_STAT_PORT_EN, 7); + + /* Reset CPDMA */ + cpsw_write_4(CPSW_CPDMA_SOFT_RESET, 1); + while(cpsw_read_4(CPSW_CPDMA_SOFT_RESET) & 1); + + for(i = 0; i < 8; i++) { + cpsw_write_4(CPSW_CPDMA_TX_HDP(i), 0); + cpsw_write_4(CPSW_CPDMA_RX_HDP(i), 0); + cpsw_write_4(CPSW_CPDMA_TX_CP(i), 0); + cpsw_write_4(CPSW_CPDMA_RX_CP(i), 0); + } + + cpsw_write_4(CPSW_CPDMA_RX_FREEBUFFER(0), 0); + + /* Initialize RX Buffer Descriptors */ + i = CPSW_MAX_RX_BUFFERS; + next_bdp = 0; + while (i--) { + cpsw_new_rxbuf(sc, i, next_bdp); + /* Increment number of free RX buffers */ + //cpsw_write_4(CPSW_CPDMA_RX_FREEBUFFER(0), 1); + next_bdp = cpsw_cpdma_rxbd_paddr(i); + } + + sc->rxbd_head = 0; + sc->rxbd_tail = CPSW_MAX_RX_BUFFERS-1; + sc->txbd_head = 0; + sc->eoq = 1; + sc->txbd_queue_size = 0; + + /* Make IP hdr aligned with 4 */ + cpsw_write_4(CPSW_CPDMA_RX_BUFFER_OFFSET, 2); + /* Write channel 0 RX HDP */ + cpsw_write_4(CPSW_CPDMA_RX_HDP(0), cpsw_cpdma_rxbd_paddr(0)); + + /* Clear all interrupt Masks */ + cpsw_write_4(CPSW_CPDMA_RX_INTMASK_CLEAR, 0xFFFFFFFF); + cpsw_write_4(CPSW_CPDMA_TX_INTMASK_CLEAR, 0xFFFFFFFF); + + /* Enable TX & RX DMA */ + cpsw_write_4(CPSW_CPDMA_TX_CONTROL, 1); + cpsw_write_4(CPSW_CPDMA_RX_CONTROL, 1); + + /* Enable TX and RX interrupt receive for core 0 */ + cpsw_write_4(CPSW_WR_C_TX_EN(0), 0xFF); + cpsw_write_4(CPSW_WR_C_RX_EN(0), 0xFF); + //cpsw_write_4(CPSW_WR_C_MISC_EN(0), 0x3F); + + /* Enable host Error Interrupt */ + cpsw_write_4(CPSW_CPDMA_DMA_INTMASK_SET, 1); + + /* Enable interrupts for TX and RX Channel 0 */ + cpsw_write_4(CPSW_CPDMA_TX_INTMASK_SET, 1); + cpsw_write_4(CPSW_CPDMA_RX_INTMASK_SET, 1); + + /* Ack stalled irqs */ + cpsw_write_4(CPSW_CPDMA_CPDMA_EOI_VECTOR, 0); + cpsw_write_4(CPSW_CPDMA_CPDMA_EOI_VECTOR, 1); + cpsw_write_4(CPSW_CPDMA_CPDMA_EOI_VECTOR, 2); + cpsw_write_4(CPSW_CPDMA_CPDMA_EOI_VECTOR, 3); + + /* Initialze MDIO - ENABLE, PREAMBLE=0, FAULTENB, CLKDIV=0xFF */ + /* TODO Calculate MDCLK=CLK/(CLKDIV+1) */ + cpsw_write_4(MDIOCONTROL, (1<<30) | (1<<18) | 0xFF); + + /* Select MII in GMII_SEL, Internal Delay mode */ + //ti_scm_reg_write_4(0x650, 0); + + /* Activate network interface */ + sc->ifp->if_drv_flags |= IFF_DRV_RUNNING; + sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + sc->wd_timer = 0; + callout_reset(&sc->wd_callout, hz, cpsw_tick, sc); +} + +static void +cpsw_ale_read_entry(struct cpsw_softc *sc, uint16_t idx, uint32_t *ale_entry) +{ + cpsw_write_4(CPSW_ALE_TBLCTL, idx & 1023); + ale_entry[0] = cpsw_read_4(CPSW_ALE_TBLW0); + ale_entry[1] = cpsw_read_4(CPSW_ALE_TBLW1); + ale_entry[2] = cpsw_read_4(CPSW_ALE_TBLW2); +} + +static void +cpsw_ale_write_entry(struct cpsw_softc *sc, uint16_t idx, uint32_t *ale_entry) +{ + cpsw_write_4(CPSW_ALE_TBLW0, ale_entry[0]); + cpsw_write_4(CPSW_ALE_TBLW1, ale_entry[1]); + cpsw_write_4(CPSW_ALE_TBLW2, ale_entry[2]); + cpsw_write_4(CPSW_ALE_TBLCTL, (idx & 1023) | (1 << 31)); +} + +static int +cpsw_ale_find_entry_by_mac(struct cpsw_softc *sc, uint8_t *mac) +{ + int i; + uint32_t ale_entry[3]; + for(i=0; i< CPSW_MAX_ALE_ENTRIES; i++) { + cpsw_ale_read_entry(sc, i, ale_entry); + if ((((ale_entry[1] >> 8) & 0xFF) == mac[0]) && + (((ale_entry[1] >> 0) & 0xFF) == mac[1]) && + (((ale_entry[0] >>24) & 0xFF) == mac[2]) && + (((ale_entry[0] >>16) & 0xFF) == mac[3]) && + (((ale_entry[0] >> 8) & 0xFF) == mac[4]) && + (((ale_entry[0] >> 0) & 0xFF) == mac[5])) { + return (i); + } + } + return CPSW_MAX_ALE_ENTRIES; +} + +static int +cpsw_ale_find_free_entry(struct cpsw_softc *sc) +{ + int i; + uint32_t ale_entry[3]; + for(i=0; i< CPSW_MAX_ALE_ENTRIES; i++) { + cpsw_ale_read_entry(sc, i, ale_entry); + /* Entry Type[61:60] is 0 for free entry */ + if (((ale_entry[1] >> 28) & 3) == 0) { + return i; + } + } + return CPSW_MAX_ALE_ENTRIES; +} + + +static int +cpsw_ale_uc_entry_set(struct cpsw_softc *sc, uint8_t port, uint8_t *mac) +{ + int i; + uint32_t ale_entry[3]; + + if ((i = cpsw_ale_find_entry_by_mac(sc, mac)) == CPSW_MAX_ALE_ENTRIES) { + i = cpsw_ale_find_free_entry(sc); + } + + if (i == CPSW_MAX_ALE_ENTRIES) + return (ENOMEM); + + /* Set MAC address */ + ale_entry[0] = mac[2]<<24 | mac[3]<<16 | mac[4]<<8 | mac[5]; + ale_entry[1] = mac[0]<<8 | mac[1]; + + /* Entry type[61:60] is addr entry(1) */ + ale_entry[1] |= 0x10<<24; + + /* Set portmask [67:66] */ + ale_entry[2] = (port & 3) << 2; + + cpsw_ale_write_entry(sc, i, ale_entry); + + return 0; +} + +static int +cpsw_ale_mc_entry_set(struct cpsw_softc *sc, uint8_t portmap, uint8_t *mac) +{ + int i; + uint32_t ale_entry[3]; + + if ((i = cpsw_ale_find_entry_by_mac(sc, mac)) == CPSW_MAX_ALE_ENTRIES) { + i = cpsw_ale_find_free_entry(sc); + } + + if (i == CPSW_MAX_ALE_ENTRIES) + return (ENOMEM); + + /* Set MAC address */ + ale_entry[0] = mac[2]<<24 | mac[3]<<16 | mac[4]<<8 | mac[5]; + ale_entry[1] = mac[0]<<8 | mac[1]; + + /* Entry type[61:60] is addr entry(1), Mcast fwd state[63:62] is fw(3)*/ + ale_entry[1] |= 0xd0<<24; + + /* Set portmask [68:66] */ + ale_entry[2] = (portmap & 7) << 2; + + cpsw_ale_write_entry(sc, i, ale_entry); + + return 0; +} + +#ifdef CPSW_DEBUG +static void +cpsw_ale_dump_table(struct cpsw_softc *sc) { + int i; + uint32_t ale_entry[3]; + for(i=0; i< CPSW_MAX_ALE_ENTRIES; i++) { + cpsw_ale_read_entry(sc, i, ale_entry); + if (ale_entry[0] || ale_entry[1] || ale_entry[2]) { + printf("ALE[%4u] %08x %08x %08x ", i, ale_entry[0], + ale_entry[1],ale_entry[2]); + printf("mac: %02x:%02x:%02x:%02x:%02x:%02x ", + (ale_entry[1] >> 8) & 0xFF, + (ale_entry[1] >> 0) & 0xFF, + (ale_entry[0] >>24) & 0xFF, + (ale_entry[0] >>16) & 0xFF, + (ale_entry[0] >> 8) & 0xFF, + (ale_entry[0] >> 0) & 0xFF); + printf( ((ale_entry[1]>>8)&1) ? "mcast " : "ucast "); + printf("type: %u ", (ale_entry[1]>>28)&3); + printf("port: %u ", (ale_entry[2]>>2)&7); + printf("\n"); + } + } +} +#endif diff --git a/sys/arm/ti/cpsw/if_cpswreg.h b/sys/arm/ti/cpsw/if_cpswreg.h new file mode 100644 index 000000000000..a4264d449fc3 --- /dev/null +++ b/sys/arm/ti/cpsw/if_cpswreg.h @@ -0,0 +1,107 @@ +/*- + * 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. + * + * $FreeBSD$ + */ + +#ifndef _IF_CPSWREG_H +#define _IF_CPSWREG_H + +#define CPSW_SS_OFFSET 0x0000 +#define CPSW_SS_IDVER (CPSW_SS_OFFSET + 0x00) +#define CPSW_SS_SOFT_RESET (CPSW_SS_OFFSET + 0x08) +#define CPSW_SS_STAT_PORT_EN (CPSW_SS_OFFSET + 0x0C) +#define CPSW_SS_PTYPE (CPSW_SS_OFFSET + 0x10) + +#define CPSW_PORT_OFFSET 0x0100 +#define CPSW_PORT_P_TX_PRI_MAP(p) (CPSW_PORT_OFFSET + 0x118 + ((p-1) * 0x100)) +#define CPSW_PORT_P0_CPDMA_TX_PRI_MAP (CPSW_PORT_OFFSET + 0x01C) +#define CPSW_PORT_P0_CPDMA_RX_CH_MAP (CPSW_PORT_OFFSET + 0x020) +#define CPSW_PORT_P_SA_LO(p) (CPSW_PORT_OFFSET + 0x120 + ((p-1) * 0x100)) +#define CPSW_PORT_P_SA_HI(p) (CPSW_PORT_OFFSET + 0x124 + ((p-1) * 0x100)) + +#define CPSW_CPDMA_OFFSET 0x0800 +#define CPSW_CPDMA_TX_CONTROL (CPSW_CPDMA_OFFSET + 0x04) +#define CPSW_CPDMA_RX_CONTROL (CPSW_CPDMA_OFFSET + 0x14) +#define CPSW_CPDMA_SOFT_RESET (CPSW_CPDMA_OFFSET + 0x1c) +#define CPSW_CPDMA_DMACONTROL (CPSW_CPDMA_OFFSET + 0x20) +#define CPSW_CPDMA_DMASTATUS (CPSW_CPDMA_OFFSET + 0x24) +#define CPSW_CPDMA_RX_BUFFER_OFFSET (CPSW_CPDMA_OFFSET + 0x28) +#define CPSW_CPDMA_TX_INTSTAT_RAW (CPSW_CPDMA_OFFSET + 0x80) +#define CPSW_CPDMA_TX_INTSTAT_MASKED (CPSW_CPDMA_OFFSET + 0x84) +#define CPSW_CPDMA_TX_INTMASK_SET (CPSW_CPDMA_OFFSET + 0x88) +#define CPSW_CPDMA_TX_INTMASK_CLEAR (CPSW_CPDMA_OFFSET + 0x8C) +#define CPSW_CPDMA_CPDMA_EOI_VECTOR (CPSW_CPDMA_OFFSET + 0x94) +#define CPSW_CPDMA_RX_INTSTAT_RAW (CPSW_CPDMA_OFFSET + 0xA0) +#define CPSW_CPDMA_RX_INTSTAT_MASKED (CPSW_CPDMA_OFFSET + 0xA4) +#define CPSW_CPDMA_RX_INTMASK_SET (CPSW_CPDMA_OFFSET + 0xA8) +#define CPSW_CPDMA_RX_INTMASK_CLEAR (CPSW_CPDMA_OFFSET + 0xAc) +#define CPSW_CPDMA_DMA_INTSTAT_RAW (CPSW_CPDMA_OFFSET + 0xB0) +#define CPSW_CPDMA_DMA_INTSTAT_MASKED (CPSW_CPDMA_OFFSET + 0xB4) +#define CPSW_CPDMA_DMA_INTMASK_SET (CPSW_CPDMA_OFFSET + 0xB8) +#define CPSW_CPDMA_DMA_INTMASK_CLEAR (CPSW_CPDMA_OFFSET + 0xBC) +#define CPSW_CPDMA_RX_FREEBUFFER(p) (CPSW_CPDMA_OFFSET + 0x0e0 + ((p) * 0x04)) +#define CPSW_CPDMA_TX_HDP(p) (CPSW_CPDMA_OFFSET + 0x200 + ((p) * 0x04)) +#define CPSW_CPDMA_RX_HDP(p) (CPSW_CPDMA_OFFSET + 0x220 + ((p) * 0x04)) +#define CPSW_CPDMA_TX_CP(p) (CPSW_CPDMA_OFFSET + 0x240 + ((p) * 0x04)) +#define CPSW_CPDMA_RX_CP(p) (CPSW_CPDMA_OFFSET + 0x260 + ((p) * 0x04)) + +#define CPSW_CPTS_OFFSET 0x0C00 + +#define CPSW_ALE_OFFSET 0x0D00 +#define CPSW_ALE_CONTROL (CPSW_ALE_OFFSET + 0x08) +#define CPSW_ALE_TBLCTL (CPSW_ALE_OFFSET + 0x20) +#define CPSW_ALE_TBLW2 (CPSW_ALE_OFFSET + 0x34) +#define CPSW_ALE_TBLW1 (CPSW_ALE_OFFSET + 0x38) +#define CPSW_ALE_TBLW0 (CPSW_ALE_OFFSET + 0x3C) +#define CPSW_ALE_PORTCTL(p) (CPSW_ALE_OFFSET + 0x40 + ((p) * 0x04)) + +#define CPSW_SL_OFFSET 0x0D80 +#define CPSW_SL_MACCONTROL(p) (CPSW_SL_OFFSET + (0x40 * (p)) + 0x04) +#define CPSW_SL_SOFT_RESET(p) (CPSW_SL_OFFSET + (0x40 * (p)) + 0x0C) +#define CPSW_SL_RX_MAXLEN(p) (CPSW_SL_OFFSET + (0x40 * (p)) + 0x10) +#define CPSW_SL_RX_PRI_MAP(p) (CPSW_SL_OFFSET + (0x40 * (p)) + 0x24) + +#define MDIO_OFFSET 0x1000 +#define MDIOCONTROL (MDIO_OFFSET + 0x04) +#define MDIOUSERACCESS0 (MDIO_OFFSET + 0x80) +#define MDIOUSERPHYSEL0 (MDIO_OFFSET + 0x84) + +#define CPSW_WR_OFFSET 0x1200 +#define CPSW_WR_SOFT_RESET (CPSW_WR_OFFSET + 0x04) +#define CPSW_WR_CONTROL (CPSW_WR_OFFSET + 0x08) +#define CPSW_WR_INT_CONTROL (CPSW_WR_OFFSET + 0x0c) +#define CPSW_WR_C_RX_THRESH_EN(p) (CPSW_WR_OFFSET + (0x10 * (p)) + 0x10) +#define CPSW_WR_C_RX_EN(p) (CPSW_WR_OFFSET + (0x10 * (p)) + 0x14) +#define CPSW_WR_C_TX_EN(p) (CPSW_WR_OFFSET + (0x10 * (p)) + 0x18) +#define CPSW_WR_C_MISC_EN(p) (CPSW_WR_OFFSET + (0x10 * (p)) + 0x1C) +#define CPSW_WR_C_RX_THRESH_STAT(p) (CPSW_WR_OFFSET + (0x10 * (p)) + 0x40) +#define CPSW_WR_C_RX_STAT(p) (CPSW_WR_OFFSET + (0x10 * (p)) + 0x44) +#define CPSW_WR_C_TX_STAT(p) (CPSW_WR_OFFSET + (0x10 * (p)) + 0x48) +#define CPSW_WR_C_MISC_STAT(p) (CPSW_WR_OFFSET + (0x10 * (p)) + 0x4C) + +#define CPSW_CPPI_RAM_OFFSET 0x2000 + +#endif /*_IF_CPSWREG_H */ diff --git a/sys/arm/ti/cpsw/if_cpswvar.h b/sys/arm/ti/cpsw/if_cpswvar.h new file mode 100644 index 000000000000..836bc2df296a --- /dev/null +++ b/sys/arm/ti/cpsw/if_cpswvar.h @@ -0,0 +1,124 @@ +/*- + * 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. + * + * $FreeBSD$ + */ + +#ifndef _IF_CPSWVAR_H +#define _IF_CPSWVAR_H + +#define CPSW_INTR_COUNT 4 + +/* MII BUS */ +#define CPSW_MIIBUS_RETRIES 5 +#define CPSW_MIIBUS_DELAY 1000 + +#define CPSW_MAX_TX_BUFFERS 128 +#define CPSW_MAX_RX_BUFFERS 128 +#define CPSW_MAX_ALE_ENTRIES 1024 + +struct cpsw_softc { + struct ifnet *ifp; + phandle_t node; + device_t dev; + uint8_t mac_addr[ETHER_ADDR_LEN]; + device_t miibus; + struct mii_data *mii; + struct mtx tx_lock; /* transmitter lock */ + struct mtx rx_lock; /* receiver lock */ + struct resource *res[1 + CPSW_INTR_COUNT]; /* resources */ + void *ih_cookie[CPSW_INTR_COUNT]; /* interrupt handlers cookies */ + + uint32_t cpsw_if_flags; + int cpsw_media_status; + + struct callout wd_callout; + int wd_timer; + + /* buffers */ + bus_dma_tag_t mbuf_dtag; + bus_dmamap_t tx_dmamap[CPSW_MAX_TX_BUFFERS]; + bus_dmamap_t rx_dmamap[CPSW_MAX_RX_BUFFERS]; + struct mbuf *tx_mbuf[CPSW_MAX_TX_BUFFERS]; + struct mbuf *rx_mbuf[CPSW_MAX_RX_BUFFERS]; + int txbd_head; + int txbd_queue_size; + int rxbd_head; + int rxbd_tail; + + int tmp; + int eoq; + int tc[CPSW_MAX_TX_BUFFERS]; + int tc_unload[CPSW_MAX_TX_BUFFERS]; + + struct cpsw_softc *phy_sc; +}; + +#define CPDMA_BD_SOP (1<<15) +#define CPDMA_BD_EOP (1<<14) +#define CPDMA_BD_OWNER (1<<13) +#define CPDMA_BD_EOQ (1<<12) +#define CPDMA_BD_PKT_ERR_MASK (3<< 4) + +struct cpsw_cpdma_bd { + volatile uint32_t next; + volatile uint32_t bufptr; + volatile uint16_t buflen; + volatile uint16_t bufoff; + volatile uint16_t pktlen; + volatile uint16_t flags; +}; + +/* Read/Write macros */ +#define cpsw_read_4(reg) bus_read_4(sc->res[0], reg) +#define cpsw_write_4(reg, val) bus_write_4(sc->res[0], reg, val) + +#define cpsw_cpdma_txbd_offset(i) \ + (CPSW_CPPI_RAM_OFFSET + ((i)*16)) +#define cpsw_cpdma_txbd_paddr(i) (cpsw_cpdma_txbd_offset(i) + \ + vtophys(rman_get_start(sc->res[0]))) +#define cpsw_cpdma_read_txbd(i, val) \ + bus_read_region_4(sc->res[0], cpsw_cpdma_txbd_offset(i), (uint32_t *) val, 4) +#define cpsw_cpdma_write_txbd(i, val) \ + bus_write_region_4(sc->res[0], cpsw_cpdma_txbd_offset(i), (uint32_t *) val, 4) +#define cpsw_cpdma_write_txbd_next(i, val) \ + bus_write_4(sc->res[0], cpsw_cpdma_txbd_offset(i), val) +#define cpsw_cpdma_read_txbd_flags(i) \ + bus_read_2(sc->res[0], cpsw_cpdma_txbd_offset(i)+14) + +#define cpsw_cpdma_rxbd_offset(i) \ + (CPSW_CPPI_RAM_OFFSET + ((CPSW_MAX_TX_BUFFERS + (i))*16)) +#define cpsw_cpdma_rxbd_paddr(i) (cpsw_cpdma_rxbd_offset(i) + \ + vtophys(rman_get_start(sc->res[0]))) +#define cpsw_cpdma_read_rxbd(i, val) \ + bus_read_region_4(sc->res[0], cpsw_cpdma_rxbd_offset(i), (uint32_t *) val, 4) +#define cpsw_cpdma_write_rxbd(i, val) \ + bus_write_region_4(sc->res[0], cpsw_cpdma_rxbd_offset(i), (uint32_t *) val, 4) +#define cpsw_cpdma_write_rxbd_next(i, val) \ + bus_write_4(sc->res[0], cpsw_cpdma_rxbd_offset(i), val) +#define cpsw_cpdma_read_rxbd_flags(i) \ + bus_read_2(sc->res[0], cpsw_cpdma_rxbd_offset(i)+14) + +#endif /*_IF_CPSWVAR_H */ diff --git a/sys/arm/ti/files.ti b/sys/arm/ti/files.ti new file mode 100644 index 000000000000..baeaa0078941 --- /dev/null +++ b/sys/arm/ti/files.ti @@ -0,0 +1,25 @@ +#$FreeBSD$ + +kern/kern_clocksource.c standard + +arm/arm/bus_space_generic.c standard +arm/arm/bus_space_asm_generic.S standard +arm/arm/cpufunc_asm_armv5.S standard +arm/arm/cpufunc_asm_arm10.S standard +arm/arm/cpufunc_asm_arm11.S standard +arm/arm/cpufunc_asm_armv7.S standard +arm/arm/irq_dispatch.S standard + +arm/ti/bus_space.c standard +arm/ti/common.c standard +arm/ti/ti_cpuid.c standard +arm/ti/ti_machdep.c standard +arm/ti/ti_prcm.c standard +arm/ti/ti_scm.c standard + +arm/ti/ti_gpio.c optional gpio +arm/ti/ti_i2c.c optional ti_i2c +dev/ofw/ofw_iicbus.c optional iicbus + +dev/uart/uart_dev_ns8250.c optional uart + diff --git a/sys/arm/ti/omap3/omap3_reg.h b/sys/arm/ti/omap3/omap3_reg.h new file mode 100644 index 000000000000..3b4e2ea2599f --- /dev/null +++ b/sys/arm/ti/omap3/omap3_reg.h @@ -0,0 +1,780 @@ +/*- + * 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$ + */ + +/* + * Texas Instruments - OMAP3xxx series processors + * + * Reference: + * OMAP35x Applications Processor + * Technical Reference Manual + * (omap35xx_techref.pdf) + * + * + * Note: + * The devices are mapped into address above 0xD000_0000 as the kernel space + * memory is at 0xC000_0000 and above. The first 256MB after this is reserved + * for the size of the kernel, everything above that is reserved for SoC + * devices. + * + */ +#ifndef _OMAP35XX_REG_H_ +#define _OMAP35XX_REG_H_ + +#ifndef _LOCORE +#include /* for uint32_t */ +#endif + + + + +#define OMAP35XX_SDRAM0_START 0x80000000UL +#define OMAP35XX_SDRAM1_START 0xA0000000UL +#define OMAP35XX_SDRAM_BANKS 2 +#define OMAP35XX_SDRAM_BANK_SIZE 0x20000000UL + + +/* Physical/Virtual address for SDRAM controller */ + +#define OMAP35XX_SMS_VBASE 0x6C000000UL +#define OMAP35XX_SMS_HWBASE 0x6C000000UL +#define OMAP35XX_SMS_SIZE 0x01000000UL + +#define OMAP35XX_SDRC_VBASE 0x6D000000UL +#define OMAP35XX_SDRC_HWBASE 0x6D000000UL +#define OMAP35XX_SDRC_SIZE 0x01000000UL + + + +/* Physical/Virtual address for I/O space */ + +#define OMAP35XX_L3_VBASE 0xD0000000UL +#define OMAP35XX_L3_HWBASE 0x68000000UL +#define OMAP35XX_L3_SIZE 0x01000000UL + +#define OMAP35XX_L4_CORE_VBASE 0xE8000000UL +#define OMAP35XX_L4_CORE_HWBASE 0x48000000UL +#define OMAP35XX_L4_CORE_SIZE 0x01000000UL + +#define OMAP35XX_L4_WAKEUP_VBASE 0xE8300000UL +#define OMAP35XX_L4_WAKEUP_HWBASE 0x48300000UL +#define OMAP35XX_L4_WAKEUP_SIZE 0x00040000UL + +#define OMAP35XX_L4_PERIPH_VBASE 0xE9000000UL +#define OMAP35XX_L4_PERIPH_HWBASE 0x49000000UL +#define OMAP35XX_L4_PERIPH_SIZE 0x00100000UL + + +/* + * L4-CORE Physical/Virtual addresss offsets + */ +#define OMAP35XX_SCM_OFFSET 0x00002000UL +#define OMAP35XX_CM_OFFSET 0x00004000UL +#define OMAP35XX_SDMA_OFFSET 0x00056000UL +#define OMAP35XX_I2C3_OFFSET 0x00060000UL +#define OMAP35XX_USB_TLL_OFFSET 0x00062000UL +#define OMAP35XX_USB_UHH_OFFSET 0x00064000UL +#define OMAP35XX_USB_EHCI_OFFSET 0x00064800UL + + +#define OMAP35XX_UART1_OFFSET 0x0006A000UL +#define OMAP35XX_UART2_OFFSET 0x0006C000UL +#define OMAP35XX_I2C1_OFFSET 0x00070000UL +#define OMAP35XX_I2C2_OFFSET 0x00072000UL +#define OMAP35XX_MCBSP1_OFFSET 0x00074000UL +#define OMAP35XX_GPTIMER10_OFFSET 0x00086000UL +#define OMAP35XX_GPTIMER11_OFFSET 0x00088000UL +#define OMAP35XX_MCBSP5_OFFSET 0x00096000UL +#define OMAP35XX_MMU1_OFFSET 0x000BD400UL +#define OMAP35XX_INTCPS_OFFSET 0x00200000UL + + +/* + * L4-WAKEUP Physical/Virtual addresss offsets + */ +#define OMAP35XX_PRM_OFFSET 0x00006000UL +#define OMAP35XX_GPIO1_OFFSET 0x00010000UL +#define OMAP35XX_GPTIMER1_OFFSET 0x00018000UL + + + +/* + * L4-PERIPH Physical/Virtual addresss offsets + */ +#define OMAP35XX_UART3_OFFSET 0x00020000UL +#define OMAP35XX_MCBSP2_OFFSET 0x00022000UL +#define OMAP35XX_MCBSP3_OFFSET 0x00024000UL +#define OMAP35XX_MCBSP4_OFFSET 0x00026000UL +#define OMAP35XX_SIDETONE_MCBSP2_OFFSET 0x00028000UL +#define OMAP35XX_SIDETONE_MCBSP3_OFFSET 0x0002A000UL +#define OMAP35XX_GPTIMER2_OFFSET 0x00032000UL +#define OMAP35XX_GPTIMER3_OFFSET 0x00034000UL +#define OMAP35XX_GPTIMER4_OFFSET 0x00036000UL +#define OMAP35XX_GPTIMER5_OFFSET 0x00038000UL +#define OMAP35XX_GPTIMER6_OFFSET 0x0003A000UL +#define OMAP35XX_GPTIMER7_OFFSET 0x0003C000UL +#define OMAP35XX_GPTIMER8_OFFSET 0x0003E000UL +#define OMAP35XX_GPTIMER9_OFFSET 0x00040000UL +#define OMAP35XX_GPIO2_OFFSET 0x00050000UL +#define OMAP35XX_GPIO3_OFFSET 0x00052000UL +#define OMAP35XX_GPIO4_OFFSET 0x00054000UL +#define OMAP35XX_GPIO5_OFFSET 0x00056000UL +#define OMAP35XX_GPIO6_OFFSET 0x00058000UL + + + + + + +/* + * System Control Module + */ +#define OMAP35XX_SCM_HWBASE (OMAP35XX_L4_CORE_HWBASE + OMAP35XX_SCM_OFFSET) +#define OMAP35XX_SCM_VBASE (OMAP35XX_L4_CORE_VBASE + OMAP35XX_SCM_OFFSET) +#define OMAP35XX_SCM_SIZE 0x00001000UL + +#define OMAP35XX_SCM_REVISION 0x00000000UL +#define OMAP35XX_SCM_SYSCONFIG 0x00000010UL +#define OMAP35XX_SCM_PADCONFS_BASE 0x00000030UL +#define OMAP35XX_SCM_DEVCONF0 0x00000274UL +#define OMAP35XX_SCM_MEM_DFTRW0 0x00000278UL + + + + +/* + * + */ +#define OMAP35XX_CM_HWBASE (OMAP35XX_L4_CORE_HWBASE + OMAP35XX_CM_OFFSET) +#define OMAP35XX_CM_VBASE (OMAP35XX_L4_CORE_VBASE + OMAP35XX_CM_OFFSET) +#define OMAP35XX_CM_SIZE 0x00001500UL + +#define OMAP35XX_CM_CORE_OFFSET 0x00000A00UL +#define OMAP35XX_CM_CORE_SIZE 0x00000100UL +#define OMAP35XX_CM_FCLKEN1_CORE (OMAP35XX_CM_CORE_OFFSET + 0x0000UL) +#define OMAP35XX_CM_FCLKEN3_CORE (OMAP35XX_CM_CORE_OFFSET + 0x0008UL) +#define OMAP35XX_CM_ICLKEN1_CORE (OMAP35XX_CM_CORE_OFFSET + 0x0010UL) +#define OMAP35XX_CM_ICLKEN2_CORE (OMAP35XX_CM_CORE_OFFSET + 0x0014UL) +#define OMAP35XX_CM_ICLKEN3_CORE (OMAP35XX_CM_CORE_OFFSET + 0x0018UL) +#define OMAP35XX_CM_IDLEST1_CORE (OMAP35XX_CM_CORE_OFFSET + 0x0020UL) +#define OMAP35XX_CM_IDLEST2_CORE (OMAP35XX_CM_CORE_OFFSET + 0x0024UL) +#define OMAP35XX_CM_IDLEST3_CORE (OMAP35XX_CM_CORE_OFFSET + 0x0028UL) +#define OMAP35XX_CM_AUTOIDLE1_CORE (OMAP35XX_CM_CORE_OFFSET + 0x0030UL) +#define OMAP35XX_CM_AUTOIDLE2_CORE (OMAP35XX_CM_CORE_OFFSET + 0x0034UL) +#define OMAP35XX_CM_AUTOIDLE3_CORE (OMAP35XX_CM_CORE_OFFSET + 0x0038UL) +#define OMAP35XX_CM_CLKSEL_CORE (OMAP35XX_CM_CORE_OFFSET + 0x0040UL) +#define OMAP35XX_CM_CLKSTCTRL_CORE (OMAP35XX_CM_CORE_OFFSET + 0x0048UL) +#define OMAP35XX_CM_CLKSTST_CORE (OMAP35XX_CM_CORE_OFFSET + 0x004CUL) + +#define OMAP35XX_CM_WKUP_OFFSET 0x00000C00UL +#define OMAP35XX_CM_WKUP_SIZE 0x00000100UL +#define OMAP35XX_CM_FCLKEN_WKUP (OMAP35XX_CM_WKUP_OFFSET + 0x0000UL) +#define OMAP35XX_CM_ICLKEN_WKUP (OMAP35XX_CM_WKUP_OFFSET + 0x0010UL) +#define OMAP35XX_CM_IDLEST_WKUP (OMAP35XX_CM_WKUP_OFFSET + 0x0020UL) +#define OMAP35XX_CM_AUTOIDLE_WKUP (OMAP35XX_CM_WKUP_OFFSET + 0x0030UL) +#define OMAP35XX_CM_CLKSEL_WKUP (OMAP35XX_CM_WKUP_OFFSET + 0x0040UL) + +#define OMAP35XX_CM_PLL_OFFSET 0x00000D00UL +#define OMAP35XX_CM_PLL_SIZE 0x00000100UL +#define OMAP35XX_CM_CLKEN_PLL (OMAP35XX_CM_PLL_OFFSET + 0x0000UL) +#define OMAP35XX_CM_CLKEN2_PLL (OMAP35XX_CM_PLL_OFFSET + 0x0004UL) +#define OMAP35XX_CM_IDLEST_CKGEN (OMAP35XX_CM_PLL_OFFSET + 0x0020UL) +#define OMAP35XX_CM_IDLEST2_CKGEN (OMAP35XX_CM_PLL_OFFSET + 0x0024UL) +#define OMAP35XX_CM_AUTOIDLE_PLL (OMAP35XX_CM_PLL_OFFSET + 0x0030UL) +#define OMAP35XX_CM_AUTOIDLE2_PLL (OMAP35XX_CM_PLL_OFFSET + 0x0034UL) +#define OMAP35XX_CM_CLKSEL1_PLL (OMAP35XX_CM_PLL_OFFSET + 0x0040UL) +#define OMAP35XX_CM_CLKSEL2_PLL (OMAP35XX_CM_PLL_OFFSET + 0x0044UL) +#define OMAP35XX_CM_CLKSEL3_PLL (OMAP35XX_CM_PLL_OFFSET + 0x0048UL) +#define OMAP35XX_CM_CLKSEL4_PLL (OMAP35XX_CM_PLL_OFFSET + 0x004CUL) +#define OMAP35XX_CM_CLKSEL5_PLL (OMAP35XX_CM_PLL_OFFSET + 0x0050UL) +#define OMAP35XX_CM_CLKOUT_CTRL (OMAP35XX_CM_PLL_OFFSET + 0x0070UL) + +#define OMAP35XX_CM_PER_OFFSET 0x00001000UL +#define OMAP35XX_CM_PER_SIZE 0x00000100UL +#define OMAP35XX_CM_FCLKEN_PER (OMAP35XX_CM_PER_OFFSET + 0x0000UL) +#define OMAP35XX_CM_ICLKEN_PER (OMAP35XX_CM_PER_OFFSET + 0x0010UL) +#define OMAP35XX_CM_IDLEST_PER (OMAP35XX_CM_PER_OFFSET + 0x0020UL) +#define OMAP35XX_CM_AUTOIDLE_PER (OMAP35XX_CM_PER_OFFSET + 0x0030UL) +#define OMAP35XX_CM_CLKSEL_PER (OMAP35XX_CM_PER_OFFSET + 0x0040UL) +#define OMAP35XX_CM_SLEEPDEP_PER (OMAP35XX_CM_PER_OFFSET + 0x0044UL) +#define OMAP35XX_CM_CLKSTCTRL_PER (OMAP35XX_CM_PER_OFFSET + 0x0048UL) +#define OMAP35XX_CM_CLKSTST_PER (OMAP35XX_CM_PER_OFFSET + 0x004CUL) + +#define OMAP35XX_CM_USBHOST_OFFSET 0x00001400UL +#define OMAP35XX_CM_USBHOST_SIZE 0x00000100UL +#define OMAP35XX_CM_FCLKEN_USBHOST (OMAP35XX_CM_USBHOST_OFFSET + 0x0000UL) +#define OMAP35XX_CM_ICLKEN_USBHOST (OMAP35XX_CM_USBHOST_OFFSET + 0x0010UL) +#define OMAP35XX_CM_IDLEST_USBHOST (OMAP35XX_CM_USBHOST_OFFSET + 0x0020UL) +#define OMAP35XX_CM_AUTOIDLE_USBHOST (OMAP35XX_CM_USBHOST_OFFSET + 0x0030UL) +#define OMAP35XX_CM_SLEEPDEP_USBHOST (OMAP35XX_CM_USBHOST_OFFSET + 0x0044UL) +#define OMAP35XX_CM_CLKSTCTRL_USBHOST (OMAP35XX_CM_USBHOST_OFFSET + 0x0048UL) +#define OMAP35XX_CM_CLKSTST_USBHOST (OMAP35XX_CM_USBHOST_OFFSET + 0x004CUL) + + + + +/* + * + */ +#define OMAP35XX_PRM_HWBASE (OMAP35XX_L4_WAKEUP_HWBASE + OMAP35XX_PRM_OFFSET) +#define OMAP35XX_PRM_VBASE (OMAP35XX_L4_WAKEUP_VBASE + OMAP35XX_PRM_OFFSET) +#define OMAP35XX_PRM_SIZE 0x00001600UL + +#define OMAP35XX_PRM_CLKCTRL_OFFSET 0x00000D00UL +#define OMAP35XX_PRM_CLKCTRL_SIZE 0x00000100UL +#define OMAP35XX_PRM_CLKSEL (OMAP35XX_PRM_CLKCTRL_OFFSET + 0x0040UL) +#define OMAP35XX_PRM_CLKOUT_CTRL (OMAP35XX_PRM_CLKCTRL_OFFSET + 0x0070UL) + +#define OMAP35XX_PRM_GLOBAL_OFFSET 0x00001200UL +#define OMAP35XX_PRM_GLOBAL_SIZE 0x00000100UL +#define OMAP35XX_PRM_CLKSRC_CTRL (OMAP35XX_PRM_GLOBAL_OFFSET + 0x0070UL) + + + + + +/* + * Uarts + */ +#define OMAP35XX_UART1_HWBASE (OMAP35XX_L4_CORE_HWBASE + OMAP35XX_UART1_OFFSET) +#define OMAP35XX_UART1_VBASE (OMAP35XX_L4_CORE_VBASE + OMAP35XX_UART1_OFFSET) +#define OMAP35XX_UART1_SIZE 0x00001000UL + +#define OMAP35XX_UART2_HWBASE (OMAP35XX_L4_CORE_HWBASE + OMAP35XX_UART2_OFFSET) +#define OMAP35XX_UART2_VBASE (OMAP35XX_L4_CORE_VBASE + OMAP35XX_UART2_OFFSET) +#define OMAP35XX_UART2_SIZE 0x00001000UL + +#define OMAP35XX_UART3_HWBASE (OMAP35XX_L4_PERIPH_HWBASE + OMAP35XX_UART3_OFFSET) +#define OMAP35XX_UART3_VBASE (OMAP35XX_L4_PERIPH_VBASE + OMAP35XX_UART3_OFFSET) +#define OMAP35XX_UART3_SIZE 0x00001000UL + + + + +/* + * I2C Modules + */ +#define OMAP35XX_I2C1_HWBASE (OMAP35XX_L4_CORE_HWBASE + OMAP35XX_I2C1_OFFSET) +#define OMAP35XX_I2C1_VBASE (OMAP35XX_L4_CORE_VBASE + OMAP35XX_I2C1_OFFSET) +#define OMAP35XX_I2C1_SIZE 0x00000080UL + +#define OMAP35XX_I2C2_HWBASE (OMAP35XX_L4_CORE_HWBASE + OMAP35XX_I2C2_OFFSET) +#define OMAP35XX_I2C2_VBASE (OMAP35XX_L4_CORE_VBASE + OMAP35XX_I2C2_OFFSET) +#define OMAP35XX_I2C2_SIZE 0x00000080UL + +#define OMAP35XX_I2C3_HWBASE (OMAP35XX_L4_CORE_HWBASE + OMAP35XX_I2C3_OFFSET) +#define OMAP35XX_I2C3_VBASE (OMAP35XX_L4_CORE_VBASE + OMAP35XX_I2C3_OFFSET) +#define OMAP35XX_I2C3_SIZE 0x00000080UL + +#define OMAP35XX_I2C_IE 0x04 +#define OMAP35XX_I2C_STAT 0x08 +#define OMAP35XX_I2C_WE 0x0C +#define OMAP35XX_I2C_SYSS 0x10 +#define OMAP35XX_I2C_BUF 0x14 +#define OMAP35XX_I2C_CNT 0x18 +#define OMAP35XX_I2C_DATA 0x1C +#define OMAP35XX_I2C_SYSC 0x20 +#define OMAP35XX_I2C_CON 0x24 +#define OMAP35XX_I2C_OA0 0x28 +#define OMAP35XX_I2C_SA 0x2C +#define OMAP35XX_I2C_PSC 0x30 +#define OMAP35XX_I2C_SCLL 0x34 +#define OMAP35XX_I2C_SCLH 0x38 +#define OMAP35XX_I2C_SYSTEST 0x3C +#define OMAP35XX_I2C_BUFSTAT 0x40 +#define OMAP35XX_I2C_OA1 0x44 +#define OMAP35XX_I2C_OA2 0x48 +#define OMAP35XX_I2C_OA3 0x4C +#define OMAP35XX_I2C_ACTOA 0x50 +#define OMAP35XX_I2C_SBLOCK 0x54 + + + +/* + * McBSP Modules + */ +#define OMAP35XX_MCBSP1_HWBASE (OMAP35XX_L4_CORE_HWBASE + OMAP35XX_MCBSP1_OFFSET) +#define OMAP35XX_MCBSP1_VBASE (OMAP35XX_L4_CORE_VBASE + OMAP35XX_MCBSP1_OFFSET) +#define OMAP35XX_MCBSP1_SIZE 0x00001000UL + +#define OMAP35XX_MCBSP2_HWBASE (OMAP35XX_L4_PERIPH_HWBASE + OMAP35XX_MCBSP2_OFFSET) +#define OMAP35XX_MCBSP2_VBASE (OMAP35XX_L4_PERIPH_VBASE + OMAP35XX_MCBSP2_OFFSET) +#define OMAP35XX_MCBSP2_SIZE 0x00001000UL + +#define OMAP35XX_MCBSP3_HWBASE (OMAP35XX_L4_PERIPH_HWBASE + OMAP35XX_MCBSP3_OFFSET) +#define OMAP35XX_MCBSP3_VBASE (OMAP35XX_L4_PERIPH_VBASE + OMAP35XX_MCBSP3_OFFSET) +#define OMAP35XX_MCBSP3_SIZE 0x00001000UL + +#define OMAP35XX_MCBSP4_HWBASE (OMAP35XX_L4_PERIPH_HWBASE + OMAP35XX_MCBSP4_OFFSET) +#define OMAP35XX_MCBSP4_VBASE (OMAP35XX_L4_PERIPH_VBASE + OMAP35XX_MCBSP4_OFFSET) +#define OMAP35XX_MCBSP4_SIZE 0x00001000UL + +#define OMAP35XX_MCBSP5_HWBASE (OMAP35XX_L4_CORE_HWBASE + OMAP35XX_MCBSP5_OFFSET) +#define OMAP35XX_MCBSP5_VBASE (OMAP35XX_L4_CORE_VBASE + OMAP35XX_MCBSP5_OFFSET) +#define OMAP35XX_MCBSP5_SIZE 0x00001000UL + +#define OMAP35XX_MCBSP_DRR 0x0000 +#define OMAP35XX_MCBSP_DXR 0x0008 +#define OMAP35XX_MCBSP_SPCR2 0x0010 +#define OMAP35XX_MCBSP_SPCR1 0x0014 +#define OMAP35XX_MCBSP_RCR2 0x0018 +#define OMAP35XX_MCBSP_RCR1 0x001C +#define OMAP35XX_MCBSP_XCR2 0x0020 +#define OMAP35XX_MCBSP_XCR1 0x0024 +#define OMAP35XX_MCBSP_SRGR2 0x0028 +#define OMAP35XX_MCBSP_SRGR1 0x002C +#define OMAP35XX_MCBSP_MCR2 0x0030 +#define OMAP35XX_MCBSP_MCR1 0x0034 +#define OMAP35XX_MCBSP_RCERA 0x0038 +#define OMAP35XX_MCBSP_RCERB 0x003C +#define OMAP35XX_MCBSP_XCERA 0x0040 +#define OMAP35XX_MCBSP_XCERB 0x0044 +#define OMAP35XX_MCBSP_PCR 0x0048 +#define OMAP35XX_MCBSP_RCERC 0x004C +#define OMAP35XX_MCBSP_RCERD 0x0050 +#define OMAP35XX_MCBSP_XCERC 0x0054 +#define OMAP35XX_MCBSP_XCERD 0x0058 +#define OMAP35XX_MCBSP_RCERE 0x005C +#define OMAP35XX_MCBSP_RCERF 0x0060 +#define OMAP35XX_MCBSP_XCERE 0x0064 +#define OMAP35XX_MCBSP_XCERF 0x0068 +#define OMAP35XX_MCBSP_RCERG 0x006C +#define OMAP35XX_MCBSP_RCERH 0x0070 +#define OMAP35XX_MCBSP_XCERG 0x0074 +#define OMAP35XX_MCBSP_XCERH 0x0078 +#define OMAP35XX_MCBSP_RINTCLR 0x0080 +#define OMAP35XX_MCBSP_XINTCLR 0x0084 +#define OMAP35XX_MCBSP_ROVFLCLR 0x0088 +#define OMAP35XX_MCBSP_SYSCONFIG 0x008C +#define OMAP35XX_MCBSP_THRSH2 0x0090 +#define OMAP35XX_MCBSP_THRSH1 0x0094 +#define OMAP35XX_MCBSP_IRQSTATUS 0x00A0 +#define OMAP35XX_MCBSP_IRQENABLE 0x00A4 +#define OMAP35XX_MCBSP_WAKEUPEN 0x00A8 +#define OMAP35XX_MCBSP_XCCR 0x00AC +#define OMAP35XX_MCBSP_RCCR 0x00B0 +#define OMAP35XX_MCBSP_XBUFFSTAT 0x00B4 +#define OMAP35XX_MCBSP_RBUFFSTAT 0x00B8 +#define OMAP35XX_MCBSP_SSELCR 0x00BC +#define OMAP35XX_MCBSP_STATUS 0x00C0 + + + +/* + * USB TTL Module + */ +#define OMAP35XX_USBTLL_HWBASE (OMAP35XX_L4_CORE_HWBASE + OMAP35XX_USBTLL_OFFSET) +#define OMAP35XX_USBTLL_VBASE (OMAP35XX_L4_CORE_VBASE + OMAP35XX_USBTLL_OFFSET) +#define OMAP35XX_USBTLL_SIZE 0x00001000UL + +#define OMAP35XX_USBTLL_REVISION 0x0000 +#define OMAP35XX_USBTLL_SYSCONFIG 0x0010 +#define OMAP35XX_USBTLL_SYSSTATUS 0x0014 +#define OMAP35XX_USBTLL_IRQSTATUS 0x0018 +#define OMAP35XX_USBTLL_IRQENABLE 0x001C +#define OMAP35XX_USBTLL_TLL_SHARED_CONF 0x0030 +#define OMAP35XX_USBTLL_TLL_CHANNEL_CONF(i) (0x0040 + (0x04 * (i))) +#define OMAP35XX_USBTLL_ULPI_VENDOR_ID_LO(i) (0x0800 + (0x100 * (i))) +#define OMAP35XX_USBTLL_ULPI_VENDOR_ID_HI(i) (0x0801 + (0x100 * (i))) +#define OMAP35XX_USBTLL_ULPI_PRODUCT_ID_LO(i) (0x0802 + (0x100 * (i))) +#define OMAP35XX_USBTLL_ULPI_PRODUCT_ID_HI(i) (0x0803 + (0x100 * (i))) +#define OMAP35XX_USBTLL_ULPI_FUNCTION_CTRL(i) (0x0804 + (0x100 * (i))) +#define OMAP35XX_USBTLL_ULPI_FUNCTION_CTRL_SET(i) (0x0805 + (0x100 * (i))) +#define OMAP35XX_USBTLL_ULPI_FUNCTION_CTRL_CLR(i) (0x0806 + (0x100 * (i))) +#define OMAP35XX_USBTLL_ULPI_INTERFACE_CTRL(i) (0x0807 + (0x100 * (i))) +#define OMAP35XX_USBTLL_ULPI_INTERFACE_CTRL_SET(i) (0x0808 + (0x100 * (i))) +#define OMAP35XX_USBTLL_ULPI_INTERFACE_CTRL_CLR(i) (0x0809 + (0x100 * (i))) +#define OMAP35XX_USBTLL_ULPI_OTG_CTRL(i) (0x080A + (0x100 * (i))) +#define OMAP35XX_USBTLL_ULPI_OTG_CTRL_SET(i) (0x080B + (0x100 * (i))) +#define OMAP35XX_USBTLL_ULPI_OTG_CTRL_CLR(i) (0x080C + (0x100 * (i))) +#define OMAP35XX_USBTLL_ULPI_USB_INT_EN_RISE(i) (0x080D + (0x100 * (i))) +#define OMAP35XX_USBTLL_ULPI_USB_INT_EN_RISE_SET(i) (0x080E + (0x100 * (i))) +#define OMAP35XX_USBTLL_ULPI_USB_INT_EN_RISE_CLR(i) (0x080F + (0x100 * (i))) +#define OMAP35XX_USBTLL_ULPI_USB_INT_EN_FALL(i) (0x0810 + (0x100 * (i))) +#define OMAP35XX_USBTLL_ULPI_USB_INT_EN_FALL_SET(i) (0x0811 + (0x100 * (i))) +#define OMAP35XX_USBTLL_ULPI_USB_INT_EN_FALL_CLR(i) (0x0812 + (0x100 * (i))) +#define OMAP35XX_USBTLL_ULPI_USB_INT_STATUS(i) (0x0813 + (0x100 * (i))) +#define OMAP35XX_USBTLL_ULPI_USB_INT_LATCH(i) (0x0814 + (0x100 * (i))) +#define OMAP35XX_USBTLL_ULPI_DEBUG(i) (0x0815 + (0x100 * (i))) +#define OMAP35XX_USBTLL_ULPI_SCRATCH_REGISTER(i) (0x0816 + (0x100 * (i))) +#define OMAP35XX_USBTLL_ULPI_SCRATCH_REGISTER_SET(i) (0x0817 + (0x100 * (i))) +#define OMAP35XX_USBTLL_ULPI_SCRATCH_REGISTER_CLR(i) (0x0818 + (0x100 * (i))) +#define OMAP35XX_USBTLL_ULPI_EXTENDED_SET_ACCESS(i) (0x082F + (0x100 * (i))) +#define OMAP35XX_USBTLL_ULPI_UTMI_VCONTROL_EN(i) (0x0830 + (0x100 * (i))) +#define OMAP35XX_USBTLL_ULPI_UTMI_VCONTROL_EN_SET(i) (0x0831 + (0x100 * (i))) +#define OMAP35XX_USBTLL_ULPI_UTMI_VCONTROL_EN_CLR(i) (0x0832 + (0x100 * (i))) +#define OMAP35XX_USBTLL_ULPI_UTMI_VCONTROL_STATUS(i) (0x0833 + (0x100 * (i))) +#define OMAP35XX_USBTLL_ULPI_UTMI_VCONTROL_LATCH(i) (0x0834 + (0x100 * (i))) +#define OMAP35XX_USBTLL_ULPI_UTMI_VSTATUS(i) (0x0835 + (0x100 * (i))) +#define OMAP35XX_USBTLL_ULPI_UTMI_VSTATUS_SET(i) (0x0836 + (0x100 * (i))) +#define OMAP35XX_USBTLL_ULPI_UTMI_VSTATUS_CLR(i) (0x0837 + (0x100 * (i))) +#define OMAP35XX_USBTLL_ULPI_USB_INT_LATCH_NOCLR(i) (0x0838 + (0x100 * (i))) +#define OMAP35XX_USBTLL_ULPI_VENDOR_INT_EN(i) (0x083B + (0x100 * (i))) +#define OMAP35XX_USBTLL_ULPI_VENDOR_INT_EN_SET(i) (0x083C + (0x100 * (i))) +#define OMAP35XX_USBTLL_ULPI_VENDOR_INT_EN_CLR(i) (0x083D + (0x100 * (i))) +#define OMAP35XX_USBTLL_ULPI_VENDOR_INT_STATUS(i) (0x083E + (0x100 * (i))) +#define OMAP35XX_USBTLL_ULPI_VENDOR_INT_LATCH(i) (0x083F + (0x100 * (i))) + + +/* + * USB Host Module + */ +#define OMAP35XX_USB_TLL_HWBASE (OMAP35XX_L4_CORE_HWBASE + OMAP35XX_USB_TLL_OFFSET) +#define OMAP35XX_USB_TLL_VBASE (OMAP35XX_L4_CORE_VBASE + OMAP35XX_USB_TLL_OFFSET) +#define OMAP35XX_USB_TLL_SIZE 0x00001000UL + +#define OMAP35XX_USB_EHCI_HWBASE (OMAP35XX_L4_CORE_HWBASE + OMAP35XX_USB_EHCI_OFFSET) +#define OMAP35XX_USB_EHCI_VBASE (OMAP35XX_L4_CORE_VBASE + OMAP35XX_USB_EHCI_OFFSET) +#define OMAP35XX_USB_EHCI_SIZE 0x00000400UL + +#define OMAP35XX_USB_UHH_HWBASE (OMAP35XX_L4_CORE_HWBASE + OMAP35XX_USB_UHH_OFFSET) +#define OMAP35XX_USB_UHH_VBASE (OMAP35XX_L4_CORE_VBASE + OMAP35XX_USB_UHH_OFFSET) +#define OMAP35XX_USB_UHH_SIZE 0x00000400UL + + + + + +/* + * SDRAM Controler (SDRC) + * PA 0x6D00_0000 + */ + +#define OMAP35XX_SDRC_SYSCONFIG (OMAP35XX_SDRC_VBASE + 0x10) +#define OMAP35XX_SDRC_SYSSTATUS (OMAP35XX_SDRC_VBASE + 0x14) +#define OMAP35XX_SDRC_CS_CFG (OMAP35XX_SDRC_VBASE + 0x40) +#define OMAP35XX_SDRC_SHARING (OMAP35XX_SDRC_VBASE + 0x44) +#define OMAP35XX_SDRC_ERR_ADDR (OMAP35XX_SDRC_VBASE + 0x48) +#define OMAP35XX_SDRC_ERR_TYPE (OMAP35XX_SDRC_VBASE + 0x4C) +#define OMAP35XX_SDRC_DLLA_CTRL (OMAP35XX_SDRC_VBASE + 0x60) +#define OMAP35XX_SDRC_DLLA_STATUS (OMAP35XX_SDRC_VBASE + 0x64) +#define OMAP35XX_SDRC_POWER_REG (OMAP35XX_SDRC_VBASE + 0x70) +#define OMAP35XX_SDRC_MCFG(p) (OMAP35XX_SDRC_VBASE + 0x80 + (0x30 * (p))) +#define OMAP35XX_SDRC_MR(p) (OMAP35XX_SDRC_VBASE + 0x84 + (0x30 * (p))) +#define OMAP35XX_SDRC_EMR2(p) (OMAP35XX_SDRC_VBASE + 0x8C + (0x30 * (p))) +#define OMAP35XX_SDRC_ACTIM_CTRLA(p) (OMAP35XX_SDRC_VBASE + 0x9C + (0x28 * (p))) +#define OMAP35XX_SDRC_ACTIM_CTRLB(p) (OMAP35XX_SDRC_VBASE + 0xA0 + (0x28 * (p))) +#define OMAP35XX_SDRC_RFR_CTRL(p) (OMAP35XX_SDRC_VBASE + 0xA4 + (0x30 * (p))) +#define OMAP35XX_SDRC_MANUAL(p) (OMAP35XX_SDRC_VBASE + 0xA8 + (0x30 * (p))) + + +/* + * SDMA Offset + * PA 0x4805 6000 + */ + +#define OMAP35XX_SDMA_HWBASE (OMAP35XX_L4_CORE_HWBASE + OMAP35XX_SDMA_OFFSET) +#define OMAP35XX_SDMA_VBASE (OMAP35XX_L4_CORE_VBASE + OMAP35XX_SDMA_OFFSET) +#define OMAP35XX_SDMA_SIZE 0x00001000UL + + + +/* + * Interrupt Controller Unit. + * PA 0x4820_0000 + */ + +#define OMAP35XX_INTCPS_HWBASE (OMAP35XX_L4_CORE_HWBASE + OMAP35XX_INTCPS_OFFSET) +#define OMAP35XX_INTCPS_VBASE (OMAP35XX_L4_CORE_VBASE + OMAP35XX_INTCPS_OFFSET) +#define OMAP35XX_INTCPS_SIZE 0x00001000UL + +#define OMAP35XX_INTCPS_SYSCONFIG (OMAP35XX_INTCPS_VBASE + 0x10) +#define OMAP35XX_INTCPS_SYSSTATUS (OMAP35XX_INTCPS_VBASE + 0x14) +#define OMAP35XX_INTCPS_SIR_IRQ (OMAP35XX_INTCPS_VBASE + 0x40) +#define OMAP35XX_INTCPS_SIR_FIQ (OMAP35XX_INTCPS_VBASE + 0x44) +#define OMAP35XX_INTCPS_CONTROL (OMAP35XX_INTCPS_VBASE + 0x48) +#define OMAP35XX_INTCPS_PROTECTION (OMAP35XX_INTCPS_VBASE + 0x4C) +#define OMAP35XX_INTCPS_IDLE (OMAP35XX_INTCPS_VBASE + 0x50) +#define OMAP35XX_INTCPS_IRQ_PRIORITY (OMAP35XX_INTCPS_VBASE + 0x60) +#define OMAP35XX_INTCPS_FIQ_PRIORITY (OMAP35XX_INTCPS_VBASE + 0x64) +#define OMAP35XX_INTCPS_THRESHOLD (OMAP35XX_INTCPS_VBASE + 0x68) +#define OMAP35XX_INTCPS_ITR(n) (OMAP35XX_INTCPS_VBASE + 0x80 + (0x20 * (n))) +#define OMAP35XX_INTCPS_MIR(n) (OMAP35XX_INTCPS_VBASE + 0x84 + (0x20 * (n))) +#define OMAP35XX_INTCPS_MIR_CLEAR(n) (OMAP35XX_INTCPS_VBASE + 0x88 + (0x20 * (n))) +#define OMAP35XX_INTCPS_MIR_SET(n) (OMAP35XX_INTCPS_VBASE + 0x8C + (0x20 * (n))) +#define OMAP35XX_INTCPS_ISR_SET(n) (OMAP35XX_INTCPS_VBASE + 0x90 + (0x20 * (n))) +#define OMAP35XX_INTCPS_ISR_CLEAR(n) (OMAP35XX_INTCPS_VBASE + 0x94 + (0x20 * (n))) +#define OMAP35XX_INTCPS_PENDING_IRQ(n) (OMAP35XX_INTCPS_VBASE + 0x98 + (0x20 * (n))) +#define OMAP35XX_INTCPS_PENDING_FIQ(n) (OMAP35XX_INTCPS_VBASE + 0x9C + (0x20 * (n))) +#define OMAP35XX_INTCPS_ILR(m) (OMAP35XX_INTCPS_VBASE + 0x100 + (0x4 * (m))) + + +#define OMAP35XX_IRQ_EMUINT 0 /* MPU emulation(2) */ +#define OMAP35XX_IRQ_COMMTX 1 /* MPU emulation(2) */ +#define OMAP35XX_IRQ_COMMRX 2 /* MPU emulation(2) */ +#define OMAP35XX_IRQ_BENCH 3 /* MPU emulation(2) */ +#define OMAP35XX_IRQ_MCBSP2_ST 4 /* Sidetone MCBSP2 overflow */ +#define OMAP35XX_IRQ_MCBSP3_ST 5 /* Sidetone MCBSP3 overflow */ +#define OMAP35XX_IRQ_SSM_ABORT 6 /* MPU subsystem secure state-machine abort (2) */ +#define OMAP35XX_IRQ_SYS_NIRQ 7 /* External source (active low) */ +#define OMAP35XX_IRQ_RESERVED8 8 /* RESERVED */ +#define OMAP35XX_IRQ_SMX_DBG 9 /* SMX error for debug */ +#define OMAP35XX_IRQ_SMX_APP 10 /* SMX error for application */ +#define OMAP35XX_IRQ_PRCM_MPU 11 /* PRCM module IRQ */ +#define OMAP35XX_IRQ_SDMA0 12 /* System DMA request 0(3) */ +#define OMAP35XX_IRQ_SDMA1 13 /* System DMA request 1(3) */ +#define OMAP35XX_IRQ_SDMA2 14 /* System DMA request 2 */ +#define OMAP35XX_IRQ_SDMA3 15 /* System DMA request 3 */ +#define OMAP35XX_IRQ_MCBSP1 16 /* McBSP module 1 IRQ (3) */ +#define OMAP35XX_IRQ_MCBSP2 17 /* McBSP module 2 IRQ (3) */ +#define OMAP35XX_IRQ_SR1 18 /* SmartReflexâ„¢ 1 */ +#define OMAP35XX_IRQ_SR2 19 /* SmartReflexâ„¢ 2 */ +#define OMAP35XX_IRQ_GPMC 20 /* General-purpose memory controller module */ +#define OMAP35XX_IRQ_SGX 21 /* 2D/3D graphics module */ +#define OMAP35XX_IRQ_MCBSP3 22 /* McBSP module 3(3) */ +#define OMAP35XX_IRQ_MCBSP4 23 /* McBSP module 4(3) */ +#define OMAP35XX_IRQ_CAM0 24 /* Camera interface request 0 */ +#define OMAP35XX_IRQ_DSS 25 /* Display subsystem module(3) */ +#define OMAP35XX_IRQ_MAIL_U0 26 /* Mailbox user 0 request */ +#define OMAP35XX_IRQ_MCBSP5_IRQ1 27 /* McBSP module 5 (3) */ +#define OMAP35XX_IRQ_IVA2_MMU 28 /* IVA2 MMU */ +#define OMAP35XX_IRQ_GPIO1_MPU 29 /* GPIO module 1(3) */ +#define OMAP35XX_IRQ_GPIO2_MPU 30 /* GPIO module 2(3) */ +#define OMAP35XX_IRQ_GPIO3_MPU 31 /* GPIO module 3(3) */ +#define OMAP35XX_IRQ_GPIO4_MPU 32 /* GPIO module 4(3) */ +#define OMAP35XX_IRQ_GPIO5_MPU 33 /* GPIO module 5(3) */ +#define OMAP35XX_IRQ_GPIO6_MPU 34 /* GPIO module 6(3) */ +#define OMAP35XX_IRQ_USIM 35 /* USIM interrupt (HS devices only) (4) */ +#define OMAP35XX_IRQ_WDT3 36 /* Watchdog timer module 3 overflow */ +#define OMAP35XX_IRQ_GPT1 37 /* General-purpose timer module 1 */ +#define OMAP35XX_IRQ_GPT2 38 /* General-purpose timer module 2 */ +#define OMAP35XX_IRQ_GPT3 39 /* General-purpose timer module 3 */ +#define OMAP35XX_IRQ_GPT4 40 /* General-purpose timer module 4 */ +#define OMAP35XX_IRQ_GPT5 41 /* General-purpose timer module 5(3) */ +#define OMAP35XX_IRQ_GPT6 42 /* General-purpose timer module 6(3) */ +#define OMAP35XX_IRQ_GPT7 43 /* General-purpose timer module 7(3) */ +#define OMAP35XX_IRQ_GPT8 44 /* General-purpose timer module 8(3) */ +#define OMAP35XX_IRQ_GPT9 45 /* General-purpose timer module 9 */ +#define OMAP35XX_IRQ_GPT10 46 /* General-purpose timer module 10 */ +#define OMAP35XX_IRQ_GPT11 47 /* General-purpose timer module 11 */ +#define OMAP35XX_IRQ_SPI4 48 /* McSPI module 4 */ +#define OMAP35XX_IRQ_SHA1MD5_2 49 /* SHA-1/MD5 crypto-accelerator 2 (HS devices only)(4) */ +#define OMAP35XX_IRQ_FPKA_IRQREADY_N 50 /* PKA crypto-accelerator (HS devices only) (4) */ +#define OMAP35XX_IRQ_SHA2MD5 51 /* SHA-2/MD5 crypto-accelerator 1 (HS devices only) (4) */ +#define OMAP35XX_IRQ_RNG 52 /* RNG module (HS devices only) (4) */ +#define OMAP35XX_IRQ_MG 53 /* MG function (3) */ +#define OMAP35XX_IRQ_MCBSP4_TX 54 /* McBSP module 4 transmit(3) */ +#define OMAP35XX_IRQ_MCBSP4_RX 55 /* McBSP module 4 receive(3) */ +#define OMAP35XX_IRQ_I2C1 56 /* I2C module 1 */ +#define OMAP35XX_IRQ_I2C2 57 /* I2C module 2 */ +#define OMAP35XX_IRQ_HDQ 58 /* HDQ / One-wire */ +#define OMAP35XX_IRQ_MCBSP1_TX 59 /* McBSP module 1 transmit(3) */ +#define OMAP35XX_IRQ_MCBSP1_RX 60 /* McBSP module 1 receive(3) */ +#define OMAP35XX_IRQ_I2C3 61 /* I2C module 3 */ +#define OMAP35XX_IRQ_McBSP2_TX 62 /* McBSP module 2 transmit(3) */ +#define OMAP35XX_IRQ_McBSP2_RX 63 /* McBSP module 2 receive(3) */ +#define OMAP35XX_IRQ_FPKA_IRQRERROR_N 64 /* PKA crypto-accelerator (HS devices only) (4) */ +#define OMAP35XX_IRQ_SPI1 65 /* McSPI module 1 */ +#define OMAP35XX_IRQ_SPI2 66 /* McSPI module 2 */ +#define OMAP35XX_IRQ_RESERVED67 67 /* RESERVED */ +#define OMAP35XX_IRQ_RESERVED68 68 /* RESERVED */ +#define OMAP35XX_IRQ_RESERVED69 69 /* RESERVED */ +#define OMAP35XX_IRQ_RESERVED70 70 /* RESERVED */ +#define OMAP35XX_IRQ_RESERVED71 71 /* RESERVED */ +#define OMAP35XX_IRQ_UART1 72 /* UART module 1 */ +#define OMAP35XX_IRQ_UART2 73 /* UART module 2 */ +#define OMAP35XX_IRQ_UART3 74 /* UART module 3 (also infrared)(3) */ +#define OMAP35XX_IRQ_PBIAS 75 /* Merged interrupt for PBIASlite1 and 2 */ +#define OMAP35XX_IRQ_OHCI 76 /* OHCI controller HSUSB MP Host Interrupt */ +#define OMAP35XX_IRQ_EHCI 77 /* EHCI controller HSUSB MP Host Interrupt */ +#define OMAP35XX_IRQ_TLL 78 /* HSUSB MP TLL Interrupt */ +#define OMAP35XX_IRQ_PARTHASH 79 /* SHA2/MD5 crypto-accelerator 1 (HS devices only) (4) */ +#define OMAP35XX_IRQ_RESERVED80 80 /* Reserved */ +#define OMAP35XX_IRQ_MCBSP5_TX 81 /* McBSP module 5 transmit(3) */ +#define OMAP35XX_IRQ_MCBSP5_RX 82 /* McBSP module 5 receive(3) */ +#define OMAP35XX_IRQ_MMC1 83 /* MMC/SD module 1 */ +#define OMAP35XX_IRQ_MS 84 /* MS-PROâ„¢ module */ +#define OMAP35XX_IRQ_RESERVED85 85 /* Reserved */ +#define OMAP35XX_IRQ_MMC2 86 /* MMC/SD module 2 */ +#define OMAP35XX_IRQ_MPU_ICR 87 /* MPU ICR */ +#define OMAP35XX_IRQ_RESERVED 88 /* RESERVED */ +#define OMAP35XX_IRQ_MCBSP3_TX 89 /* McBSP module 3 transmit(3) */ +#define OMAP35XX_IRQ_MCBSP3_RX 90 /* McBSP module 3 receive(3) */ +#define OMAP35XX_IRQ_SPI3 91 /* McSPI module 3 */ +#define OMAP35XX_IRQ_HSUSB_MC_NINT 92 /* High-Speed USB OTG controller */ +#define OMAP35XX_IRQ_HSUSB_DMA_NINT 93 /* High-Speed USB OTG DMA controller */ +#define OMAP35XX_IRQ_MMC3 94 /* MMC/SD module 3 */ +#define OMAP35XX_IRQ_GPT12 95 /* General-purpose timer module 12 */ + + + + +/* + * General Purpose Timers + */ +#define OMAP35XX_GPTIMER1_VBASE (OMAP35XX_L4_WAKEUP_VBASE + OMAP35XX_GPTIMER1_OFFSET) +#define OMAP35XX_GPTIMER1_HWBASE (OMAP35XX_L4_WAKEUP_HWBASE + OMAP35XX_GPTIMER1_OFFSET) +#define OMAP35XX_GPTIMER2_VBASE (OMAP35XX_L4_PERIPH_VBASE + OMAP35XX_GPTIMER2_OFFSET) +#define OMAP35XX_GPTIMER2_HWBASE (OMAP35XX_L4_PERIPH_HWBASE + OMAP35XX_GPTIMER2_OFFSET) +#define OMAP35XX_GPTIMER3_VBASE (OMAP35XX_L4_PERIPH_VBASE + OMAP35XX_GPTIMER3_OFFSET) +#define OMAP35XX_GPTIMER3_HWBASE (OMAP35XX_L4_PERIPH_HWBASE + OMAP35XX_GPTIMER3_OFFSET) +#define OMAP35XX_GPTIMER4_VBASE (OMAP35XX_L4_PERIPH_VBASE + OMAP35XX_GPTIMER4_OFFSET) +#define OMAP35XX_GPTIMER4_HWBASE (OMAP35XX_L4_PERIPH_HWBASE + OMAP35XX_GPTIMER4_OFFSET) +#define OMAP35XX_GPTIMER5_VBASE (OMAP35XX_L4_PERIPH_VBASE + OMAP35XX_GPTIMER5_OFFSET) +#define OMAP35XX_GPTIMER5_HWBASE (OMAP35XX_L4_PERIPH_HWBASE + OMAP35XX_GPTIMER5_OFFSET) +#define OMAP35XX_GPTIMER6_VBASE (OMAP35XX_L4_PERIPH_VBASE + OMAP35XX_GPTIMER6_OFFSET) +#define OMAP35XX_GPTIMER6_HWBASE (OMAP35XX_L4_PERIPH_HWBASE + OMAP35XX_GPTIMER6_OFFSET) +#define OMAP35XX_GPTIMER7_VBASE (OMAP35XX_L4_PERIPH_VBASE + OMAP35XX_GPTIMER7_OFFSET) +#define OMAP35XX_GPTIMER7_HWBASE (OMAP35XX_L4_PERIPH_HWBASE + OMAP35XX_GPTIMER7_OFFSET) +#define OMAP35XX_GPTIMER8_VBASE (OMAP35XX_L4_PERIPH_VBASE + OMAP35XX_GPTIMER8_OFFSET) +#define OMAP35XX_GPTIMER8_HWBASE (OMAP35XX_L4_PERIPH_HWBASE + OMAP35XX_GPTIMER8_OFFSET) +#define OMAP35XX_GPTIMER9_VBASE (OMAP35XX_L4_PERIPH_VBASE + OMAP35XX_GPTIMER9_OFFSET) +#define OMAP35XX_GPTIMER9_HWBASE (OMAP35XX_L4_PERIPH_HWBASE + OMAP35XX_GPTIMER9_OFFSET) +#define OMAP35XX_GPTIMER10_VBASE (OMAP35XX_L4_CORE_VBASE + OMAP35XX_GPTIMER10_OFFSET) +#define OMAP35XX_GPTIMER10_HWBASE (OMAP35XX_L4_CORE_HWBASE + OMAP35XX_GPTIMER10_OFFSET) +#define OMAP35XX_GPTIMER11_VBASE (OMAP35XX_L4_CORE_VBASE + OMAP35XX_GPTIMER11_OFFSET) +#define OMAP35XX_GPTIMER11_HWBASE (OMAP35XX_L4_CORE_HWBASE + OMAP35XX_GPTIMER11_OFFSET) +#define OMAP35XX_GPTIMER12_VBASE 0x48304000UL /* GPTIMER12 */ +#define OMAP35XX_GPTIMER_SIZE 0x00001000UL + + + +/* Timer register offsets */ +#define OMAP35XX_GPTIMER_TIOCP_CFG 0x010 +#define OMAP35XX_GPTIMER_TISTAT 0x014 +#define OMAP35XX_GPTIMER_TISR 0x018 +#define OMAP35XX_GPTIMER_TIER 0x01C +#define OMAP35XX_GPTIMER_TWER 0x020 +#define OMAP35XX_GPTIMER_TCLR 0x024 +#define OMAP35XX_GPTIMER_TCRR 0x028 +#define OMAP35XX_GPTIMER_TLDR 0x02C +#define OMAP35XX_GPTIMER_TTGR 0x030 +#define OMAP35XX_GPTIMER_TWPS 0x034 +#define OMAP35XX_GPTIMER_TMAR 0x038 +#define OMAP35XX_GPTIMER_TCAR1 0x03C +#define OMAP35XX_GPTIMER_TSICR 0x040 +#define OMAP35XX_GPTIMER_TCAR2 0x044 +#define OMAP35XX_GPTIMER_TPIR 0x048 +#define OMAP35XX_GPTIMER_TNIR 0x04C +#define OMAP35XX_GPTIMER_TCVR 0x050 +#define OMAP35XX_GPTIMER_TOCR 0x054 +#define OMAP35XX_GPTIMER_TOWR 0x058 + +/* Bit values */ +#define MAT_IT_FLAG 0x01 +#define OVF_IT_FLAG 0x02 +#define TCAR_IT_FLAG 0x04 + + + +/* + * GPIO - General Purpose IO + */ + +/* Base addresses for the GPIO modules */ +#define OMAP35XX_GPIO1_HWBASE (OMAP35XX_L4_WAKEUP_HWBASE + OMAP35XX_GPIO1_OFFSET) +#define OMAP35XX_GPIO1_VBASE (OMAP35XX_L4_WAKEUP_VBASE + OMAP35XX_GPIO1_OFFSET) +#define OMAP35XX_GPIO1_SIZE 0x00001000UL +#define OMAP35XX_GPIO2_HWBASE (OMAP35XX_L4_PERIPH_HWBASE + OMAP35XX_GPIO2_OFFSET) +#define OMAP35XX_GPIO2_VBASE (OMAP35XX_L4_PERIPH_VBASE + OMAP35XX_GPIO2_OFFSET) +#define OMAP35XX_GPIO2_SIZE 0x00001000UL +#define OMAP35XX_GPIO3_HWBASE (OMAP35XX_L4_PERIPH_HWBASE + OMAP35XX_GPIO3_OFFSET) +#define OMAP35XX_GPIO3_VBASE (OMAP35XX_L4_PERIPH_VBASE + OMAP35XX_GPIO3_OFFSET) +#define OMAP35XX_GPIO3_SIZE 0x00001000UL +#define OMAP35XX_GPIO4_HWBASE (OMAP35XX_L4_PERIPH_HWBASE + OMAP35XX_GPIO4_OFFSET) +#define OMAP35XX_GPIO4_VBASE (OMAP35XX_L4_PERIPH_VBASE + OMAP35XX_GPIO4_OFFSET) +#define OMAP35XX_GPIO4_SIZE 0x00001000UL +#define OMAP35XX_GPIO5_HWBASE (OMAP35XX_L4_PERIPH_HWBASE + OMAP35XX_GPIO5_OFFSET) +#define OMAP35XX_GPIO5_VBASE (OMAP35XX_L4_PERIPH_VBASE + OMAP35XX_GPIO5_OFFSET) +#define OMAP35XX_GPIO5_SIZE 0x00001000UL +#define OMAP35XX_GPIO6_HWBASE (OMAP35XX_L4_PERIPH_HWBASE + OMAP35XX_GPIO6_OFFSET) +#define OMAP35XX_GPIO6_VBASE (OMAP35XX_L4_PERIPH_VBASE + OMAP35XX_GPIO6_OFFSET) +#define OMAP35XX_GPIO6_SIZE 0x00001000UL + + + +/* Register offsets within the banks above */ +#define OMAP35XX_GPIO_SYSCONFIG 0x010 +#define OMAP35XX_GPIO_SYSSTATUS 0x014 +#define OMAP35XX_GPIO_IRQSTATUS1 0x018 +#define OMAP35XX_GPIO_IRQENABLE1 0x01C +#define OMAP35XX_GPIO_WAKEUPENABLE 0x020 +#define OMAP35XX_GPIO_IRQSTATUS2 0x028 +#define OMAP35XX_GPIO_IRQENABLE2 0x02C +#define OMAP35XX_GPIO_CTRL 0x030 +#define OMAP35XX_GPIO_OE 0x034 +#define OMAP35XX_GPIO_DATAIN 0x038 +#define OMAP35XX_GPIO_DATAOUT 0x03C +#define OMAP35XX_GPIO_LEVELDETECT0 0x040 +#define OMAP35XX_GPIO_LEVELDETECT1 0x044 +#define OMAP35XX_GPIO_RISINGDETECT 0x048 +#define OMAP35XX_GPIO_FALLINGDETECT 0x04C +#define OMAP35XX_GPIO_DEBOUNCENABLE 0x050 +#define OMAP35XX_GPIO_DEBOUNCINGTIME 0x054 +#define OMAP35XX_GPIO_CLEARIRQENABLE1 0x060 +#define OMAP35XX_GPIO_SETIRQENABLE1 0x064 +#define OMAP35XX_GPIO_CLEARIRQENABLE2 0x070 +#define OMAP35XX_GPIO_SETIRQENABLE2 0x074 +#define OMAP35XX_GPIO_CLEARWKUENA 0x080 +#define OMAP35XX_GPIO_SETWKUENA 0x084 +#define OMAP35XX_GPIO_CLEARDATAOUT 0x090 +#define OMAP35XX_GPIO_SETDATAOUT 0x094 + + +/* + * MMC/SD/SDIO + */ + +/* Base addresses for the MMC/SD/SDIO modules */ +#define OMAP35XX_MMCHS1_HWBASE (OMAP35XX_L4_CORE_HWBASE + 0x0009C000) +#define OMAP35XX_MMCHS1_VBASE (OMAP35XX_L4_CORE_VBASE + 0x0009C000) +#define OMAP35XX_MMCHS2_HWBASE (OMAP35XX_L4_CORE_HWBASE + 0x000B4000) +#define OMAP35XX_MMCHS2_VBASE (OMAP35XX_L4_CORE_VBASE + 0x000B4000) +#define OMAP35XX_MMCHS3_HWBASE (OMAP35XX_L4_CORE_HWBASE + 0x000AD000) +#define OMAP35XX_MMCHS3_VBASE (OMAP35XX_L4_CORE_VBASE + 0x000AD000) +#define OMAP35XX_MMCHS_SIZE 0x00000200UL + +/* Register offsets within each of the MMC/SD/SDIO controllers */ +#define OMAP35XX_MMCHS_SYSCONFIG 0x010 +#define OMAP35XX_MMCHS_SYSSTATUS 0x014 +#define OMAP35XX_MMCHS_CSRE 0x024 +#define OMAP35XX_MMCHS_SYSTEST 0x028 +#define OMAP35XX_MMCHS_CON 0x02C +#define OMAP35XX_MMCHS_PWCNT 0x030 +#define OMAP35XX_MMCHS_BLK 0x104 +#define OMAP35XX_MMCHS_ARG 0x108 +#define OMAP35XX_MMCHS_CMD 0x10C +#define OMAP35XX_MMCHS_RSP10 0x110 +#define OMAP35XX_MMCHS_RSP32 0x114 +#define OMAP35XX_MMCHS_RSP54 0x118 +#define OMAP35XX_MMCHS_RSP76 0x11C +#define OMAP35XX_MMCHS_DATA 0x120 +#define OMAP35XX_MMCHS_PSTATE 0x124 +#define OMAP35XX_MMCHS_HCTL 0x128 +#define OMAP35XX_MMCHS_SYSCTL 0x12C +#define OMAP35XX_MMCHS_STAT 0x130 +#define OMAP35XX_MMCHS_IE 0x134 +#define OMAP35XX_MMCHS_ISE 0x138 +#define OMAP35XX_MMCHS_AC12 0x13C +#define OMAP35XX_MMCHS_CAPA 0x140 +#define OMAP35XX_MMCHS_CUR_CAPA 0x148 +#define OMAP35XX_MMCHS_REV 0x1FC + + + +#endif /* _OMAP35XX_REG_H_ */ diff --git a/sys/arm/ti/omap4/files.omap4 b/sys/arm/ti/omap4/files.omap4 new file mode 100644 index 000000000000..d9c043d6028d --- /dev/null +++ b/sys/arm/ti/omap4/files.omap4 @@ -0,0 +1,19 @@ +#$FreeBSD$ + +arm/arm/gic.c standard +arm/arm/mpcore_timer.c standard +arm/ti/ti_smc.S standard + +arm/ti/usb/omap_ehci.c optional usb ehci +arm/ti/ti_sdma.c optional ti_sdma +arm/ti/ti_mmchs.c optional mmc + +arm/ti/omap4/omap4_l2cache.c optional pl310 +arm/ti/omap4/omap4_prcm_clks.c standard +arm/ti/omap4/omap4_scm_padconf.c standard +arm/ti/omap4/omap4_mp.c optional smp + +arm/ti/twl/twl.c optional twl +arm/ti/twl/twl_vreg.c optional twl twl_vreg +arm/ti/twl/twl_clks.c optional twl twl_clks + diff --git a/sys/arm/ti/omap4/omap4_l2cache.c b/sys/arm/ti/omap4/omap4_l2cache.c new file mode 100644 index 000000000000..b663a1af3999 --- /dev/null +++ b/sys/arm/ti/omap4/omap4_l2cache.c @@ -0,0 +1,39 @@ +/*- + * Copyright (c) 2012 Olivier Houchard. 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 ``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 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 + +void +platform_init_pl310(struct pl310_softc *softc) +{ + ti_smc0(1, 0, L2CACHE_ENABLE_L2); +} + diff --git a/sys/arm/ti/omap4/omap4_mp.c b/sys/arm/ti/omap4/omap4_mp.c new file mode 100644 index 000000000000..abb0d88cfe84 --- /dev/null +++ b/sys/arm/ti/omap4/omap4_mp.c @@ -0,0 +1,87 @@ +/*- + * Copyright (c) 2012 Olivier Houchard. 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 ``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 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 + +void mpentry(void); +void mptramp(void); + +void +platform_mp_init_secondary(void) +{ + gic_init_secondary(); +} + +void +platform_mp_setmaxid(void) +{ + + mp_maxid = 1; +} + +int +platform_mp_probe(void) +{ + + mp_ncpus = 2; + return (1); +} + +void +platform_mp_start_ap(void) +{ + bus_addr_t scu_addr; + + if (bus_space_map(fdtbus_bs_tag, 0x48240000, 0x1000, 0, &scu_addr) != 0) + panic("Couldn't map the SCU\n"); + /* Enable the SCU */ + *(volatile unsigned int *)scu_addr |= 1; + //*(volatile unsigned int *)(scu_addr + 0x30) |= 1; + cpu_idcache_wbinv_all(); + cpu_l2cache_wbinv_all(); + ti_smc0(0x200, 0xfffffdff, MODIFY_AUX_CORE_0); + ti_smc0(pmap_kextract(mpentry), 0, WRITE_AUX_CORE_1); + armv7_sev(); + bus_space_unmap(fdtbus_bs_tag, scu_addr, 0x1000); +} + +void +platform_ipi_send(cpuset_t cpus, u_int ipi) +{ + pic_ipi_send(cpus, ipi); +} diff --git a/sys/arm/ti/omap4/omap4_prcm_clks.c b/sys/arm/ti/omap4/omap4_prcm_clks.c new file mode 100644 index 000000000000..a4cd41c601a4 --- /dev/null +++ b/sys/arm/ti/omap4/omap4_prcm_clks.c @@ -0,0 +1,1418 @@ +/*- + * 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. + * 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. + */ + +#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 + +/* + * This file defines the clock configuration for the OMAP4xxx series of + * devices. + * + * How This is Suppose to Work + * =========================== + * - There is a top level omap_prcm module that defines all OMAP SoC drivers + * should use to enable/disable the system clocks regardless of the version + * of OMAP device they are running on. This top level PRCM module is just + * a thin shim to chip specific functions that perform the donkey work of + * configuring the clock - this file is the 'donkey' for OMAP44xx devices. + * + * - The key bit in this file is the omap_clk_devmap array, it's + * used by the omap_prcm driver to determine what clocks are valid and which + * functions to call to manipulate them. + * + * - In essence you just need to define some callbacks for each of the + * clocks and then you're done. + * + * - The other thing that is worth noting is that when the omap_prcm device + * is registered you typically pass in some memory ranges which are the + * SYS_MEMORY resources. These resources are in turn allocated using + * bus_allocate_resources(...) and the resource handles are passed to all + * individual clock callback handlers. + * + * + * + * OMAP4 devices are different from the previous OMAP3 devices in that there + * is no longer a separate functional and interface clock for each module, + * instead there is typically an interface clock that spans many modules. + * + */ + +#define FREQ_96MHZ 96000000 +#define FREQ_64MHZ 64000000 +#define FREQ_48MHZ 48000000 +#define FREQ_32KHZ 32000 + +/** + * We need three memory regions to cover all the clock configuration registers. + * + * PRM Instance - 0x4A30 6000 : 0x4A30 8000 + * CM1 Instance - 0x4A00 4000 : 0x4A00 5000 + * CM2 Instance - 0x4A00 8000 : 0x4A00 A000 + * + */ +#define PRM_INSTANCE_MEM_REGION 0 +#define CM1_INSTANCE_MEM_REGION 1 +#define CM2_INSTANCE_MEM_REGION 2 + +/** + * Address offsets from the PRM memory region to the top level clock control + * registers. + */ +#define CKGEN_PRM_OFFSET 0x00000100UL +#define MPU_PRM_OFFSET 0x00000300UL +#define DSP_PRM_OFFSET 0x00000400UL +#define ABE_PRM_OFFSET 0x00000500UL +#define ALWAYS_ON_PRM_OFFSET 0x00000600UL +#define CORE_PRM_OFFSET 0x00000700UL +#define IVAHD_PRM_OFFSET 0x00000F00UL +#define CAM_PRM_OFFSET 0x00001000UL +#define DSS_PRM_OFFSET 0x00001100UL +#define SGX_PRM_OFFSET 0x00001200UL +#define L3INIT_PRM_OFFSET 0x00001300UL +#define L4PER_PRM_OFFSET 0x00001400UL +#define WKUP_PRM_OFFSET 0x00001700UL +#define WKUP_CM_OFFSET 0x00001800UL +#define EMU_PRM_OFFSET 0x00001900UL +#define EMU_CM_OFFSET 0x00001A00UL +#define DEVICE_PRM_OFFSET 0x00001B00UL +#define INSTR_PRM_OFFSET 0x00001F00UL + +#define CM_ABE_DSS_SYS_CLKSEL_OFFSET (CKGEN_PRM_OFFSET + 0x0000UL) +#define CM_L4_WKUP_CLKSELL_OFFSET (CKGEN_PRM_OFFSET + 0x0008UL) +#define CM_ABE_PLL_REF_CLKSEL_OFFSET (CKGEN_PRM_OFFSET + 0x000CUL) +#define CM_SYS_CLKSEL_OFFSET (CKGEN_PRM_OFFSET + 0x0010UL) + +/** + * Address offsets from the CM1 memory region to the top level clock control + * registers. + */ +#define CKGEN_CM1_OFFSET 0x00000100UL +#define MPU_CM1_OFFSET 0x00000300UL +#define DSP_CM1_OFFSET 0x00000400UL +#define ABE_CM1_OFFSET 0x00000500UL +#define RESTORE_CM1_OFFSET 0x00000E00UL +#define INSTR_CM1_OFFSET 0x00000F00UL + +#define CM_CLKSEL_DPLL_MPU (CKGEN_CM1_OFFSET + 0x006CUL) + +/** + * Address offsets from the CM2 memory region to the top level clock control + * registers. + */ +#define INTRCONN_SOCKET_CM2_OFFSET 0x00000000UL +#define CKGEN_CM2_OFFSET 0x00000100UL +#define ALWAYS_ON_CM2_OFFSET 0x00000600UL +#define CORE_CM2_OFFSET 0x00000700UL +#define IVAHD_CM2_OFFSET 0x00000F00UL +#define CAM_CM2_OFFSET 0x00001000UL +#define DSS_CM2_OFFSET 0x00001100UL +#define SGX_CM2_OFFSET 0x00001200UL +#define L3INIT_CM2_OFFSET 0x00001300UL +#define L4PER_CM2_OFFSET 0x00001400UL +#define RESTORE_CM2_OFFSET 0x00001E00UL +#define INSTR_CM2_OFFSET 0x00001F00UL + +#define CLKCTRL_MODULEMODE_MASK 0x00000003UL +#define CLKCTRL_MODULEMODE_DISABLE 0x00000000UL +#define CLKCTRL_MODULEMODE_AUTO 0x00000001UL +#define CLKCTRL_MODULEMODE_ENABLE 0x00000001UL + +#define CLKCTRL_IDLEST_MASK 0x00030000UL +#define CLKCTRL_IDLEST_ENABLED 0x00000000UL +#define CLKCTRL_IDLEST_WAKING 0x00010000UL +#define CLKCTRL_IDLEST_IDLE 0x00020000UL +#define CLKCTRL_IDLEST_DISABLED 0x00030000UL + +static struct resource_spec omap4_scm_res_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, /* Control memory window */ + { SYS_RES_MEMORY, 1, RF_ACTIVE }, /* Control memory window */ + { SYS_RES_MEMORY, 2, RF_ACTIVE }, /* Control memory window */ + { -1, 0 } +}; + +struct omap4_prcm_softc { + struct resource *sc_res[3]; +}; + +static struct omap4_prcm_softc *omap4_prcm_sc; + +static int omap4_clk_generic_activate(struct ti_clock_dev *clkdev); +static int omap4_clk_generic_deactivate(struct ti_clock_dev *clkdev); +static int omap4_clk_generic_accessible(struct ti_clock_dev *clkdev); +static int omap4_clk_generic_set_source(struct ti_clock_dev *clkdev, clk_src_t clksrc); +static int omap4_clk_generic_get_source_freq(struct ti_clock_dev *clkdev, unsigned int *freq); + +static int omap4_clk_gptimer_set_source(struct ti_clock_dev *clkdev, clk_src_t clksrc); +static int omap4_clk_gptimer_get_source_freq(struct ti_clock_dev *clkdev, unsigned int *freq); + +static int omap4_clk_hsmmc_set_source(struct ti_clock_dev *clkdev, clk_src_t clksrc); +static int omap4_clk_hsmmc_get_source_freq(struct ti_clock_dev *clkdev, unsigned int *freq); + +static int omap4_clk_hsusbhost_set_source(struct ti_clock_dev *clkdev, clk_src_t clksrc); +static int omap4_clk_hsusbhost_activate(struct ti_clock_dev *clkdev); +static int omap4_clk_hsusbhost_deactivate(struct ti_clock_dev *clkdev); +static int omap4_clk_hsusbhost_accessible(struct ti_clock_dev *clkdev); + +static int omap4_clk_get_sysclk_freq(struct ti_clock_dev *clkdev, unsigned int *freq); +static int omap4_clk_get_arm_fclk_freq(struct ti_clock_dev *clkdev, unsigned int *freq); + +/** + * omap_clk_devmap - Array of clock devices available on OMAP4xxx devices + * + * This map only defines which clocks are valid and the callback functions + * for clock activate, deactivate, etc. It is used by the top level omap_prcm + * driver. + * + * The actual details of the clocks (config registers, bit fields, sources, + * etc) are in the private g_omap3_clk_details array below. + * + */ + +#define OMAP4_GENERIC_CLOCK_DEV(i) \ + { .id = (i), \ + .clk_activate = omap4_clk_generic_activate, \ + .clk_deactivate = omap4_clk_generic_deactivate, \ + .clk_set_source = omap4_clk_generic_set_source, \ + .clk_accessible = omap4_clk_generic_accessible, \ + .clk_get_source_freq = omap4_clk_generic_get_source_freq \ + } + +#define OMAP4_GPTIMER_CLOCK_DEV(i) \ + { .id = (i), \ + .clk_activate = omap4_clk_generic_activate, \ + .clk_deactivate = omap4_clk_generic_deactivate, \ + .clk_set_source = omap4_clk_gptimer_set_source, \ + .clk_accessible = omap4_clk_generic_accessible, \ + .clk_get_source_freq = omap4_clk_gptimer_get_source_freq \ + } + +#define OMAP4_HSMMC_CLOCK_DEV(i) \ + { .id = (i), \ + .clk_activate = omap4_clk_generic_activate, \ + .clk_deactivate = omap4_clk_generic_deactivate, \ + .clk_set_source = omap4_clk_hsmmc_set_source, \ + .clk_accessible = omap4_clk_generic_accessible, \ + .clk_get_source_freq = omap4_clk_hsmmc_get_source_freq \ + } + +#define OMAP4_HSUSBHOST_CLOCK_DEV(i) \ + { .id = (i), \ + .clk_activate = omap4_clk_hsusbhost_activate, \ + .clk_deactivate = omap4_clk_hsusbhost_deactivate, \ + .clk_set_source = omap4_clk_hsusbhost_set_source, \ + .clk_accessible = omap4_clk_hsusbhost_accessible, \ + .clk_get_source_freq = NULL \ + } + + +struct ti_clock_dev ti_clk_devmap[] = { + + /* System clocks */ + { .id = SYS_CLK, + .clk_activate = NULL, + .clk_deactivate = NULL, + .clk_set_source = NULL, + .clk_accessible = NULL, + .clk_get_source_freq = omap4_clk_get_sysclk_freq, + }, + /* MPU (ARM) core clocks */ + { .id = MPU_CLK, + .clk_activate = NULL, + .clk_deactivate = NULL, + .clk_set_source = NULL, + .clk_accessible = NULL, + .clk_get_source_freq = omap4_clk_get_arm_fclk_freq, + }, + + + /* UART device clocks */ + OMAP4_GENERIC_CLOCK_DEV(UART1_CLK), + OMAP4_GENERIC_CLOCK_DEV(UART2_CLK), + OMAP4_GENERIC_CLOCK_DEV(UART3_CLK), + OMAP4_GENERIC_CLOCK_DEV(UART4_CLK), + + /* Timer device source clocks */ + OMAP4_GPTIMER_CLOCK_DEV(GPTIMER1_CLK), + OMAP4_GPTIMER_CLOCK_DEV(GPTIMER2_CLK), + OMAP4_GPTIMER_CLOCK_DEV(GPTIMER3_CLK), + OMAP4_GPTIMER_CLOCK_DEV(GPTIMER4_CLK), + OMAP4_GPTIMER_CLOCK_DEV(GPTIMER5_CLK), + OMAP4_GPTIMER_CLOCK_DEV(GPTIMER6_CLK), + OMAP4_GPTIMER_CLOCK_DEV(GPTIMER7_CLK), + OMAP4_GPTIMER_CLOCK_DEV(GPTIMER8_CLK), + OMAP4_GPTIMER_CLOCK_DEV(GPTIMER9_CLK), + OMAP4_GPTIMER_CLOCK_DEV(GPTIMER10_CLK), + OMAP4_GPTIMER_CLOCK_DEV(GPTIMER11_CLK), + + /* MMC device clocks (MMC1 and MMC2 can have different input clocks) */ + OMAP4_HSMMC_CLOCK_DEV(MMC1_CLK), + OMAP4_HSMMC_CLOCK_DEV(MMC2_CLK), + OMAP4_GENERIC_CLOCK_DEV(MMC3_CLK), + OMAP4_GENERIC_CLOCK_DEV(MMC4_CLK), + OMAP4_GENERIC_CLOCK_DEV(MMC5_CLK), + + /* USB HS (high speed TLL, EHCI and OHCI) */ + OMAP4_HSUSBHOST_CLOCK_DEV(USBTLL_CLK), + OMAP4_HSUSBHOST_CLOCK_DEV(USBHSHOST_CLK), + OMAP4_HSUSBHOST_CLOCK_DEV(USBFSHOST_CLK), + OMAP4_HSUSBHOST_CLOCK_DEV(USBP1_PHY_CLK), + OMAP4_HSUSBHOST_CLOCK_DEV(USBP2_PHY_CLK), + OMAP4_HSUSBHOST_CLOCK_DEV(USBP1_UTMI_CLK), + OMAP4_HSUSBHOST_CLOCK_DEV(USBP2_UTMI_CLK), + OMAP4_HSUSBHOST_CLOCK_DEV(USBP1_HSIC_CLK), + OMAP4_HSUSBHOST_CLOCK_DEV(USBP2_HSIC_CLK), + + /* GPIO */ + OMAP4_GENERIC_CLOCK_DEV(GPIO1_CLK), + OMAP4_GENERIC_CLOCK_DEV(GPIO2_CLK), + OMAP4_GENERIC_CLOCK_DEV(GPIO3_CLK), + OMAP4_GENERIC_CLOCK_DEV(GPIO4_CLK), + OMAP4_GENERIC_CLOCK_DEV(GPIO5_CLK), + OMAP4_GENERIC_CLOCK_DEV(GPIO6_CLK), + + /* sDMA */ + OMAP4_GENERIC_CLOCK_DEV(SDMA_CLK), + + /* I2C */ + OMAP4_GENERIC_CLOCK_DEV(I2C1_CLK), + OMAP4_GENERIC_CLOCK_DEV(I2C2_CLK), + OMAP4_GENERIC_CLOCK_DEV(I2C3_CLK), + OMAP4_GENERIC_CLOCK_DEV(I2C4_CLK), + + { INVALID_CLK_IDENT, NULL, NULL, NULL, NULL } +}; + +/** + * omap4_clk_details - Stores details for all the different clocks supported + * + * Whenever an operation on a clock is being performed (activated, deactivated, + * etc) this array is looked up to find the correct register and bit(s) we + * should be modifying. + * + */ +struct omap4_clk_details { + clk_ident_t id; + + uint32_t mem_region; + uint32_t clksel_reg; + + int32_t src_freq; + + uint32_t enable_mode; +}; + +#define OMAP4_GENERIC_CLOCK_DETAILS(i, f, m, r, e) \ + { .id = (i), \ + .mem_region = (m), \ + .clksel_reg = (r), \ + .src_freq = (f), \ + .enable_mode = (e), \ + } + +static struct omap4_clk_details g_omap4_clk_details[] = { + + /* UART */ + OMAP4_GENERIC_CLOCK_DETAILS(UART1_CLK, FREQ_48MHZ, CM2_INSTANCE_MEM_REGION, + (L4PER_CM2_OFFSET + 0x0140), CLKCTRL_MODULEMODE_ENABLE), + OMAP4_GENERIC_CLOCK_DETAILS(UART2_CLK, FREQ_48MHZ, CM2_INSTANCE_MEM_REGION, + (L4PER_CM2_OFFSET + 0x0148), CLKCTRL_MODULEMODE_ENABLE), + OMAP4_GENERIC_CLOCK_DETAILS(UART3_CLK, FREQ_48MHZ, CM2_INSTANCE_MEM_REGION, + (L4PER_CM2_OFFSET + 0x0140), CLKCTRL_MODULEMODE_ENABLE), + OMAP4_GENERIC_CLOCK_DETAILS(UART4_CLK, FREQ_48MHZ, CM2_INSTANCE_MEM_REGION, + (L4PER_CM2_OFFSET + 0x0148), CLKCTRL_MODULEMODE_ENABLE), + + /* General purpose timers */ + OMAP4_GENERIC_CLOCK_DETAILS(GPTIMER1_CLK, -1, PRM_INSTANCE_MEM_REGION, + (WKUP_CM_OFFSET + 0x040), CLKCTRL_MODULEMODE_ENABLE), + OMAP4_GENERIC_CLOCK_DETAILS(GPTIMER2_CLK, -1, CM2_INSTANCE_MEM_REGION, + (L4PER_CM2_OFFSET + 0x038), CLKCTRL_MODULEMODE_ENABLE), + OMAP4_GENERIC_CLOCK_DETAILS(GPTIMER3_CLK, -1, CM2_INSTANCE_MEM_REGION, + (L4PER_CM2_OFFSET + 0x040), CLKCTRL_MODULEMODE_ENABLE), + OMAP4_GENERIC_CLOCK_DETAILS(GPTIMER4_CLK, -1, CM2_INSTANCE_MEM_REGION, + (L4PER_CM2_OFFSET + 0x048), CLKCTRL_MODULEMODE_ENABLE), + OMAP4_GENERIC_CLOCK_DETAILS(GPTIMER5_CLK, -1, CM1_INSTANCE_MEM_REGION, + (ABE_CM1_OFFSET + 0x068), CLKCTRL_MODULEMODE_ENABLE), + OMAP4_GENERIC_CLOCK_DETAILS(GPTIMER6_CLK, -1, CM1_INSTANCE_MEM_REGION, + (ABE_CM1_OFFSET + 0x070), CLKCTRL_MODULEMODE_ENABLE), + OMAP4_GENERIC_CLOCK_DETAILS(GPTIMER7_CLK, -1, CM1_INSTANCE_MEM_REGION, + (ABE_CM1_OFFSET + 0x078), CLKCTRL_MODULEMODE_ENABLE), + OMAP4_GENERIC_CLOCK_DETAILS(GPTIMER8_CLK, -1, CM1_INSTANCE_MEM_REGION, + (ABE_CM1_OFFSET + 0x080), CLKCTRL_MODULEMODE_ENABLE), + OMAP4_GENERIC_CLOCK_DETAILS(GPTIMER9_CLK, -1, CM2_INSTANCE_MEM_REGION, + (L4PER_CM2_OFFSET + 0x050), CLKCTRL_MODULEMODE_ENABLE), + OMAP4_GENERIC_CLOCK_DETAILS(GPTIMER10_CLK, -1, CM2_INSTANCE_MEM_REGION, + (L4PER_CM2_OFFSET + 0x028), CLKCTRL_MODULEMODE_ENABLE), + OMAP4_GENERIC_CLOCK_DETAILS(GPTIMER11_CLK, -1, CM2_INSTANCE_MEM_REGION, + (L4PER_CM2_OFFSET + 0x030), CLKCTRL_MODULEMODE_ENABLE), + + /* HSMMC (MMC1 and MMC2 can have different input clocks) */ + OMAP4_GENERIC_CLOCK_DETAILS(MMC1_CLK, -1, CM2_INSTANCE_MEM_REGION, + (L3INIT_CM2_OFFSET + 0x028), /*CLKCTRL_MODULEMODE_ENABLE*/2), + OMAP4_GENERIC_CLOCK_DETAILS(MMC2_CLK, -1, CM2_INSTANCE_MEM_REGION, + (L3INIT_CM2_OFFSET + 0x030), /*CLKCTRL_MODULEMODE_ENABLE*/2), + OMAP4_GENERIC_CLOCK_DETAILS(MMC3_CLK, FREQ_48MHZ, CM2_INSTANCE_MEM_REGION, + (L4PER_CM2_OFFSET + 0x120), /*CLKCTRL_MODULEMODE_ENABLE*/2), + OMAP4_GENERIC_CLOCK_DETAILS(MMC4_CLK, FREQ_48MHZ, CM2_INSTANCE_MEM_REGION, + (L4PER_CM2_OFFSET + 0x128), /*CLKCTRL_MODULEMODE_ENABLE*/2), + OMAP4_GENERIC_CLOCK_DETAILS(MMC5_CLK, FREQ_48MHZ, CM2_INSTANCE_MEM_REGION, + (L4PER_CM2_OFFSET + 0x160), /*CLKCTRL_MODULEMODE_ENABLE*/1), + + /* GPIO modules */ + OMAP4_GENERIC_CLOCK_DETAILS(GPIO1_CLK, -1, PRM_INSTANCE_MEM_REGION, + (WKUP_CM_OFFSET + 0x038), CLKCTRL_MODULEMODE_AUTO), + OMAP4_GENERIC_CLOCK_DETAILS(GPIO2_CLK, -1, CM2_INSTANCE_MEM_REGION, + (L4PER_CM2_OFFSET + 0x060), CLKCTRL_MODULEMODE_AUTO), + OMAP4_GENERIC_CLOCK_DETAILS(GPIO3_CLK, -1, CM2_INSTANCE_MEM_REGION, + (L4PER_CM2_OFFSET + 0x068), CLKCTRL_MODULEMODE_AUTO), + OMAP4_GENERIC_CLOCK_DETAILS(GPIO4_CLK, -1, CM2_INSTANCE_MEM_REGION, + (L4PER_CM2_OFFSET + 0x070), CLKCTRL_MODULEMODE_AUTO), + OMAP4_GENERIC_CLOCK_DETAILS(GPIO5_CLK, -1, CM2_INSTANCE_MEM_REGION, + (L4PER_CM2_OFFSET + 0x078), CLKCTRL_MODULEMODE_AUTO), + OMAP4_GENERIC_CLOCK_DETAILS(GPIO6_CLK, -1, CM2_INSTANCE_MEM_REGION, + (L4PER_CM2_OFFSET + 0x080), CLKCTRL_MODULEMODE_AUTO), + + /* sDMA block */ + OMAP4_GENERIC_CLOCK_DETAILS(SDMA_CLK, -1, CM2_INSTANCE_MEM_REGION, + (CORE_CM2_OFFSET + 0x300), CLKCTRL_MODULEMODE_AUTO), + + /* I2C modules */ + OMAP4_GENERIC_CLOCK_DETAILS(I2C1_CLK, -1, CM2_INSTANCE_MEM_REGION, + (L4PER_CM2_OFFSET + 0x0A0), CLKCTRL_MODULEMODE_ENABLE), + OMAP4_GENERIC_CLOCK_DETAILS(I2C2_CLK, -1, CM2_INSTANCE_MEM_REGION, + (L4PER_CM2_OFFSET + 0x0A8), CLKCTRL_MODULEMODE_ENABLE), + OMAP4_GENERIC_CLOCK_DETAILS(I2C3_CLK, -1, CM2_INSTANCE_MEM_REGION, + (L4PER_CM2_OFFSET + 0x0B0), CLKCTRL_MODULEMODE_ENABLE), + OMAP4_GENERIC_CLOCK_DETAILS(I2C4_CLK, -1, CM2_INSTANCE_MEM_REGION, + (L4PER_CM2_OFFSET + 0x0B8), CLKCTRL_MODULEMODE_ENABLE), + + { INVALID_CLK_IDENT, 0, 0, 0, 0 }, +}; + +/** + * MAX_MODULE_ENABLE_WAIT - the number of loops to wait for the module to come + * alive. + * + */ +#define MAX_MODULE_ENABLE_WAIT 100 + +/** + * ARRAY_SIZE - Macro to return the number of elements in a static const array. + * + */ +#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) + +/** + * omap4_clk_details - writes a 32-bit value to one of the timer registers + * @timer: Timer device context + * @off: The offset of a register from the timer register address range + * @val: The value to write into the register + * + * + * RETURNS: + * nothing + */ +static struct omap4_clk_details* +omap4_clk_details(clk_ident_t id) +{ + struct omap4_clk_details *walker; + + for (walker = g_omap4_clk_details; walker->id != INVALID_CLK_IDENT; walker++) { + if (id == walker->id) + return (walker); + } + + return NULL; +} + +/** + * omap4_clk_generic_activate - checks if a module is accessible + * @module: identifier for the module to check, see omap3_prcm.h for a list + * of possible modules. + * Example: OMAP3_MODULE_MMC1 + * + * + * + * LOCKING: + * Inherits the locks from the omap_prcm driver, no internal locking. + * + * RETURNS: + * Returns 0 on success or a positive error code on failure. + */ +static int +omap4_clk_generic_activate(struct ti_clock_dev *clkdev) +{ + struct omap4_prcm_softc *sc = omap4_prcm_sc; + struct omap4_clk_details* clk_details; + struct resource* clk_mem_res; + uint32_t clksel; + unsigned int i; + + if (sc == NULL) + return ENXIO; + + clk_details = omap4_clk_details(clkdev->id); + + if (clk_details == NULL) + return (ENXIO); + + clk_mem_res = sc->sc_res[clk_details->mem_region]; + + if (clk_mem_res == NULL) + return (EINVAL); + + /* All the 'generic' clocks have a CLKCTRL register which is more or less + * generic - the have at least two fielda called MODULEMODE and IDLEST. + */ + clksel = bus_read_4(clk_mem_res, clk_details->clksel_reg); + clksel &= ~CLKCTRL_MODULEMODE_MASK; + clksel |= clk_details->enable_mode; + bus_write_4(clk_mem_res, clk_details->clksel_reg, clksel); + + /* Now poll on the IDLEST register to tell us if the module has come up. + * TODO: We need to take into account the parent clocks. + */ + + /* Try MAX_MODULE_ENABLE_WAIT number of times to check if enabled */ + for (i = 0; i < MAX_MODULE_ENABLE_WAIT; i++) { + clksel = bus_read_4(clk_mem_res, clk_details->clksel_reg); + if ((clksel & CLKCTRL_IDLEST_MASK) == CLKCTRL_IDLEST_ENABLED) + break; + DELAY(10); + } + + /* Check the enabled state */ + if ((clksel & CLKCTRL_IDLEST_MASK) != CLKCTRL_IDLEST_ENABLED) { + printf("Error: failed to enable module with clock %d\n", clkdev->id); + printf("Error: 0x%08x => 0x%08x\n", clk_details->clksel_reg, clksel); + return (ETIMEDOUT); + } + + return (0); +} + +/** + * omap4_clk_generic_deactivate - checks if a module is accessible + * @module: identifier for the module to check, see omap3_prcm.h for a list + * of possible modules. + * Example: OMAP3_MODULE_MMC1 + * + * + * + * LOCKING: + * Inherits the locks from the omap_prcm driver, no internal locking. + * + * RETURNS: + * Returns 0 on success or a positive error code on failure. + */ +static int +omap4_clk_generic_deactivate(struct ti_clock_dev *clkdev) +{ + struct omap4_prcm_softc *sc = omap4_prcm_sc; + struct omap4_clk_details* clk_details; + struct resource* clk_mem_res; + uint32_t clksel; + + if (sc == NULL) + return ENXIO; + + clk_details = omap4_clk_details(clkdev->id); + + if (clk_details == NULL) + return (ENXIO); + + clk_mem_res = sc->sc_res[clk_details->mem_region]; + + if (clk_mem_res == NULL) + return (EINVAL); + + /* All the 'generic' clocks have a CLKCTRL register which is more or less + * generic - the have at least two fielda called MODULEMODE and IDLEST. + */ + clksel = bus_read_4(clk_mem_res, clk_details->clksel_reg); + clksel &= ~CLKCTRL_MODULEMODE_MASK; + clksel |= CLKCTRL_MODULEMODE_DISABLE; + bus_write_4(clk_mem_res, clk_details->clksel_reg, clksel); + + return (0); +} + +/** + * omap4_clk_generic_set_source - checks if a module is accessible + * @module: identifier for the module to check, see omap3_prcm.h for a list + * of possible modules. + * Example: OMAP3_MODULE_MMC1 + * + * + * + * LOCKING: + * Inherits the locks from the omap_prcm driver, no internal locking. + * + * RETURNS: + * Returns 0 on success or a positive error code on failure. + */ +static int +omap4_clk_generic_set_source(struct ti_clock_dev *clkdev, + clk_src_t clksrc) +{ + + return (0); +} + +/** + * omap4_clk_generic_accessible - checks if a module is accessible + * @module: identifier for the module to check, see omap3_prcm.h for a list + * of possible modules. + * Example: OMAP3_MODULE_MMC1 + * + * + * + * LOCKING: + * Inherits the locks from the omap_prcm driver, no internal locking. + * + * RETURNS: + * Returns 0 on success or a negative error code on failure. + */ +static int +omap4_clk_generic_accessible(struct ti_clock_dev *clkdev) +{ + struct omap4_prcm_softc *sc = omap4_prcm_sc; + struct omap4_clk_details* clk_details; + struct resource* clk_mem_res; + uint32_t clksel; + + if (sc == NULL) + return ENXIO; + + clk_details = omap4_clk_details(clkdev->id); + + if (clk_details == NULL) + return (ENXIO); + + clk_mem_res = sc->sc_res[clk_details->mem_region]; + + if (clk_mem_res == NULL) + return (EINVAL); + + clksel = bus_read_4(clk_mem_res, clk_details->clksel_reg); + + /* Check the enabled state */ + if ((clksel & CLKCTRL_IDLEST_MASK) != CLKCTRL_IDLEST_ENABLED) + return (0); + + return (1); +} + +/** + * omap4_clk_generic_get_source_freq - checks if a module is accessible + * @module: identifier for the module to check, see omap3_prcm.h for a list + * of possible modules. + * Example: OMAP3_MODULE_MMC1 + * + * + * + * LOCKING: + * Inherits the locks from the omap_prcm driver, no internal locking. + * + * RETURNS: + * Returns 0 on success or a negative error code on failure. + */ +static int +omap4_clk_generic_get_source_freq(struct ti_clock_dev *clkdev, + unsigned int *freq + ) +{ + struct omap4_clk_details* clk_details = omap4_clk_details(clkdev->id); + + if (clk_details == NULL) + return (ENXIO); + + /* Simply return the stored frequency */ + if (freq) + *freq = (unsigned int)clk_details->src_freq; + + return (0); +} + + +/** + * omap4_clk_gptimer_set_source - checks if a module is accessible + * @module: identifier for the module to check, see omap3_prcm.h for a list + * of possible modules. + * Example: OMAP3_MODULE_MMC1 + * + * + * + * LOCKING: + * Inherits the locks from the omap_prcm driver, no internal locking. + * + * RETURNS: + * Returns 0 on success or a negative error code on failure. + */ +static int +omap4_clk_gptimer_set_source(struct ti_clock_dev *clkdev, + clk_src_t clksrc) +{ + struct omap4_prcm_softc *sc = omap4_prcm_sc; + struct omap4_clk_details* clk_details; + struct resource* clk_mem_res; + + if (sc == NULL) + return ENXIO; + + clk_details = omap4_clk_details(clkdev->id); + + if (clk_details == NULL) + return (ENXIO); + + clk_mem_res = sc->sc_res[clk_details->mem_region]; + + if (clk_mem_res == NULL) + return (EINVAL); + + /* TODO: Implement */ + + return (0); +} + +/** + * omap4_clk_gptimer_get_source_freq - checks if a module is accessible + * @module: identifier for the module to check, see omap3_prcm.h for a list + * of possible modules. + * Example: OMAP3_MODULE_MMC1 + * + * + * + * LOCKING: + * Inherits the locks from the omap_prcm driver, no internal locking. + * + * RETURNS: + * Returns 0 on success or a negative error code on failure. + */ +static int +omap4_clk_gptimer_get_source_freq(struct ti_clock_dev *clkdev, + unsigned int *freq + ) +{ + struct omap4_prcm_softc *sc = omap4_prcm_sc; + struct omap4_clk_details* clk_details; + struct resource* clk_mem_res; + uint32_t clksel; + unsigned int src_freq; + + if (sc == NULL) + return ENXIO; + + clk_details = omap4_clk_details(clkdev->id); + + if (clk_details == NULL) + return (ENXIO); + + clk_mem_res = sc->sc_res[clk_details->mem_region]; + + if (clk_mem_res == NULL) + return (EINVAL); + + /* Need to read the CLKSEL field to determine the clock source */ + clksel = bus_read_4(clk_mem_res, clk_details->clksel_reg); + if (clksel & (0x1UL << 24)) + src_freq = FREQ_32KHZ; + else + omap4_clk_get_sysclk_freq(NULL, &src_freq); + + /* Return the frequency */ + if (freq) + *freq = src_freq; + + return (0); +} + +/** + * omap4_clk_hsmmc_set_source - sets the source clock (freq) + * @clkdev: pointer to the clockdev structure (id field will contain clock id) + * + * The MMC 1 and 2 clocks can be source from either a 64MHz or 96MHz clock. + * + * LOCKING: + * Inherits the locks from the omap_prcm driver, no internal locking. + * + * RETURNS: + * Returns 0 on success or a negative error code on failure. + */ +static int +omap4_clk_hsmmc_set_source(struct ti_clock_dev *clkdev, + clk_src_t clksrc) +{ + struct omap4_prcm_softc *sc = omap4_prcm_sc; + struct omap4_clk_details* clk_details; + struct resource* clk_mem_res; + uint32_t clksel; + + if (sc == NULL) + return ENXIO; + + clk_details = omap4_clk_details(clkdev->id); + + if (clk_details == NULL) + return (ENXIO); + + clk_mem_res = sc->sc_res[clk_details->mem_region]; + + if (clk_mem_res == NULL) + return (EINVAL); + + /* For MMC modules 3, 4 & 5 you can't change the freq, it's always 48MHz */ + if ((clkdev->id == MMC3_CLK) || (clkdev->id == MMC4_CLK) || + (clkdev->id == MMC5_CLK)) { + if (clksrc != F48MHZ_CLK) + return (EINVAL); + return 0; + } + + + clksel = bus_read_4(clk_mem_res, clk_details->clksel_reg); + + /* Bit 24 is set if 96MHz clock or cleared for 64MHz clock */ + if (clksrc == F64MHZ_CLK) + clksel &= ~(0x1UL << 24); + else if (clksrc == F96MHZ_CLK) + clksel |= (0x1UL << 24); + else + return (EINVAL); + + bus_write_4(clk_mem_res, clk_details->clksel_reg, clksel); + + return (0); +} + +/** + * omap4_clk_hsmmc_get_source_freq - checks if a module is accessible + * @clkdev: pointer to the clockdev structure (id field will contain clock id) + * + * + * + * LOCKING: + * Inherits the locks from the omap_prcm driver, no internal locking. + * + * RETURNS: + * Returns 0 on success or a negative error code on failure. + */ +static int +omap4_clk_hsmmc_get_source_freq(struct ti_clock_dev *clkdev, + unsigned int *freq + ) +{ + struct omap4_prcm_softc *sc = omap4_prcm_sc; + struct omap4_clk_details* clk_details; + struct resource* clk_mem_res; + uint32_t clksel; + unsigned int src_freq; + + if (sc == NULL) + return ENXIO; + + clk_details = omap4_clk_details(clkdev->id); + + if (clk_details == NULL) + return (ENXIO); + + clk_mem_res = sc->sc_res[clk_details->mem_region]; + + if (clk_mem_res == NULL) + return (EINVAL); + + switch (clkdev->id) { + case MMC1_CLK: + case MMC2_CLK: + /* Need to read the CLKSEL field to determine the clock source */ + clksel = bus_read_4(clk_mem_res, clk_details->clksel_reg); + if (clksel & (0x1UL << 24)) + src_freq = FREQ_96MHZ; + else + src_freq = FREQ_64MHZ; + break; + case MMC3_CLK: + case MMC4_CLK: + case MMC5_CLK: + src_freq = FREQ_48MHZ; + break; + default: + return (EINVAL); + } + + /* Return the frequency */ + if (freq) + *freq = src_freq; + + return (0); +} + +/** + * omap4_clk_get_sysclk_freq - gets the sysclk frequency + * @sc: pointer to the clk module/device context + * + * Read the clocking information from the power-control/boot-strap registers, + * and stored in two global variables. + * + * RETURNS: + * nothing, values are saved in global variables + */ +static int +omap4_clk_get_sysclk_freq(struct ti_clock_dev *clkdev, + unsigned int *freq) +{ + uint32_t clksel; + uint32_t sysclk; + struct omap4_prcm_softc *sc = omap4_prcm_sc; + + if (sc == NULL) + return ENXIO; + + /* Read the input clock freq from the configuration register (CM_SYS_CLKSEL) */ + clksel = bus_read_4(sc->sc_res[PRM_INSTANCE_MEM_REGION], CM_SYS_CLKSEL_OFFSET); + switch (clksel & 0x7) { + case 0x1: + /* 12Mhz */ + sysclk = 12000000; + break; + case 0x3: + /* 16.8Mhz */ + sysclk = 16800000; + break; + case 0x4: + /* 19.2Mhz */ + sysclk = 19200000; + break; + case 0x5: + /* 26Mhz */ + sysclk = 26000000; + break; + case 0x7: + /* 38.4Mhz */ + sysclk = 38400000; + break; + default: + panic("%s: Invalid clock freq", __func__); + } + + /* Return the value */ + if (freq) + *freq = sysclk; + + return (0); +} + +/** + * omap4_clk_get_arm_fclk_freq - gets the MPU clock frequency + * @clkdev: ignored + * @freq: pointer which upon return will contain the freq in hz + * @mem_res: array of allocated memory resources + * + * Reads the frequency setting information registers and returns the value + * in the freq variable. + * + * RETURNS: + * returns 0 on success, a positive error code on failure. + */ +static int +omap4_clk_get_arm_fclk_freq(struct ti_clock_dev *clkdev, + unsigned int *freq) +{ + uint32_t clksel; + uint32_t pll_mult, pll_div; + uint32_t mpuclk, sysclk; + struct omap4_prcm_softc *sc = omap4_prcm_sc; + + if (sc == NULL) + return ENXIO; + + /* Read the clksel register which contains the DPLL multiple and divide + * values. These are applied to the sysclk. + */ + clksel = bus_read_4(sc->sc_res[CM1_INSTANCE_MEM_REGION], CM_CLKSEL_DPLL_MPU); + + pll_mult = ((clksel >> 8) & 0x7ff); + pll_div = (clksel & 0x7f) + 1; + + + /* Get the system clock freq */ + omap4_clk_get_sysclk_freq(NULL, &sysclk); + + + /* Calculate the MPU freq */ + mpuclk = (sysclk * pll_mult) / pll_div; + + /* Return the value */ + if (freq) + *freq = mpuclk; + + return (0); +} + +/** + * omap4_clk_hsusbhost_activate - activates the USB clocks for the given module + * @clkdev: pointer to the clock device structure. + * @mem_res: array of memory resouces allocated by the top level PRCM driver. + * + * The USB clocking setup seems to be a bit more tricky than the other modules, + * to start with the clocking diagram for the HS host module shows 13 different + * clocks. So to try and make it easier to follow the clocking activation + * and deactivation is handled in it's own set of callbacks. + * + * LOCKING: + * Inherits the locks from the omap_prcm driver, no internal locking. + * + * RETURNS: + * Returns 0 on success or a positive error code on failure. + */ + +struct dpll_param { + unsigned int m; + unsigned int n; + unsigned int m2; + unsigned int m3; + unsigned int m4; + unsigned int m5; + unsigned int m6; + unsigned int m7; +}; +/* USB parameters */ +struct dpll_param usb_dpll_param[7] = { + /* 12M values */ + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + /* 13M values */ + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + /* 16.8M values */ + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + /* 19.2M values */ + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + /* 26M values */ + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + /* 27M values */ + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + /* 38.4M values */ +#ifdef CONFIG_OMAP4_SDC + {0x32, 0x1, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0}, +#else + {0x32, 0x1, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0}, +#endif +}; +static int +omap4_clk_hsusbhost_activate(struct ti_clock_dev *clkdev) +{ + struct omap4_prcm_softc *sc = omap4_prcm_sc; + struct resource* clk_mem_res; + uint32_t clksel_reg_off; + uint32_t clksel; + unsigned int i; + + if (sc == NULL) + return ENXIO; + + switch (clkdev->id) { + case USBTLL_CLK: + /* For the USBTLL module we need to enable the following clocks: + * - INIT_L4_ICLK (will be enabled by bootloader) + * - TLL_CH0_FCLK + * - TLL_CH1_FCLK + */ + + /* We need the CM_L3INIT_HSUSBTLL_CLKCTRL register in CM2 register set */ + clk_mem_res = sc->sc_res[CM2_INSTANCE_MEM_REGION]; + clksel_reg_off = L3INIT_CM2_OFFSET + 0x68; + + /* Enable the module and also enable the optional func clocks for + * channels 0 & 1 (is this needed ?) + */ + clksel = bus_read_4(clk_mem_res, clksel_reg_off); + clksel &= ~CLKCTRL_MODULEMODE_MASK; + clksel |= CLKCTRL_MODULEMODE_ENABLE; + + clksel |= (0x1 << 8); /* USB-HOST optional clock: USB_CH0_CLK */ + clksel |= (0x1 << 9); /* USB-HOST optional clock: USB_CH1_CLK */ + break; + + case USBHSHOST_CLK: + case USBP1_PHY_CLK: + case USBP2_PHY_CLK: + case USBP1_UTMI_CLK: + case USBP2_UTMI_CLK: + case USBP1_HSIC_CLK: + case USBP2_HSIC_CLK: + /* For the USB HS HOST module we need to enable the following clocks: + * - INIT_L4_ICLK (will be enabled by bootloader) + * - INIT_L3_ICLK (will be enabled by bootloader) + * - INIT_48MC_FCLK + * - UTMI_ROOT_GFCLK (UTMI only, create a new clock for that ?) + * - UTMI_P1_FCLK (UTMI only, create a new clock for that ?) + * - UTMI_P2_FCLK (UTMI only, create a new clock for that ?) + * - HSIC_P1_60 (HSIC only, create a new clock for that ?) + * - HSIC_P1_480 (HSIC only, create a new clock for that ?) + * - HSIC_P2_60 (HSIC only, create a new clock for that ?) + * - HSIC_P2_480 (HSIC only, create a new clock for that ?) + */ + + /* We need the CM_L3INIT_HSUSBHOST_CLKCTRL register in CM2 register set */ + clk_mem_res = sc->sc_res[CM2_INSTANCE_MEM_REGION]; + clksel_reg_off = L3INIT_CM2_OFFSET + 0x58; + clksel = bus_read_4(clk_mem_res, clksel_reg_off); + /* Enable the module and also enable the optional func clocks */ + if (clkdev->id == USBHSHOST_CLK) { + clksel &= ~CLKCTRL_MODULEMODE_MASK; + clksel |= /*CLKCTRL_MODULEMODE_ENABLE*/2; + + clksel |= (0x1 << 15); /* USB-HOST clock control: FUNC48MCLK */ + } + + else if (clkdev->id == USBP1_UTMI_CLK) + clksel |= (0x1 << 8); /* UTMI_P1_CLK */ + else if (clkdev->id == USBP2_UTMI_CLK) + clksel |= (0x1 << 9); /* UTMI_P2_CLK */ + + else if (clkdev->id == USBP1_HSIC_CLK) + clksel |= (0x5 << 11); /* HSIC60M_P1_CLK + HSIC480M_P1_CLK */ + else if (clkdev->id == USBP2_HSIC_CLK) + clksel |= (0x5 << 12); /* HSIC60M_P2_CLK + HSIC480M_P2_CLK */ + + break; + + default: + return (EINVAL); + } + + bus_write_4(clk_mem_res, clksel_reg_off, clksel); + + /* Try MAX_MODULE_ENABLE_WAIT number of times to check if enabled */ + for (i = 0; i < MAX_MODULE_ENABLE_WAIT; i++) { + clksel = bus_read_4(clk_mem_res, clksel_reg_off); + if ((clksel & CLKCTRL_IDLEST_MASK) == CLKCTRL_IDLEST_ENABLED) + break; + } + + /* Check the enabled state */ + if ((clksel & CLKCTRL_IDLEST_MASK) != CLKCTRL_IDLEST_ENABLED) { + printf("Error: HERE failed to enable module with clock %d\n", clkdev->id); + printf("Error: 0x%08x => 0x%08x\n", clksel_reg_off, clksel); + return (ETIMEDOUT); + } + + return (0); +} + +/** + * omap4_clk_generic_deactivate - checks if a module is accessible + * @clkdev: pointer to the clock device structure. + * @mem_res: array of memory resouces allocated by the top level PRCM driver. + * + * + * + * LOCKING: + * Inherits the locks from the omap_prcm driver, no internal locking. + * + * RETURNS: + * Returns 0 on success or a positive error code on failure. + */ +static int +omap4_clk_hsusbhost_deactivate(struct ti_clock_dev *clkdev) +{ + struct omap4_prcm_softc *sc = omap4_prcm_sc; + struct resource* clk_mem_res; + uint32_t clksel_reg_off; + uint32_t clksel; + + if (sc == NULL) + return ENXIO; + + switch (clkdev->id) { + case USBTLL_CLK: + /* We need the CM_L3INIT_HSUSBTLL_CLKCTRL register in CM2 register set */ + clk_mem_res = sc->sc_res[CM2_INSTANCE_MEM_REGION]; + clksel_reg_off = L3INIT_CM2_OFFSET + 0x68; + + clksel = bus_read_4(clk_mem_res, clksel_reg_off); + clksel &= ~CLKCTRL_MODULEMODE_MASK; + clksel |= CLKCTRL_MODULEMODE_DISABLE; + break; + + case USBHSHOST_CLK: + case USBP1_PHY_CLK: + case USBP2_PHY_CLK: + case USBP1_UTMI_CLK: + case USBP2_UTMI_CLK: + case USBP1_HSIC_CLK: + case USBP2_HSIC_CLK: + /* For the USB HS HOST module we need to enable the following clocks: + * - INIT_L4_ICLK (will be enabled by bootloader) + * - INIT_L3_ICLK (will be enabled by bootloader) + * - INIT_48MC_FCLK + * - UTMI_ROOT_GFCLK (UTMI only, create a new clock for that ?) + * - UTMI_P1_FCLK (UTMI only, create a new clock for that ?) + * - UTMI_P2_FCLK (UTMI only, create a new clock for that ?) + * - HSIC_P1_60 (HSIC only, create a new clock for that ?) + * - HSIC_P1_480 (HSIC only, create a new clock for that ?) + * - HSIC_P2_60 (HSIC only, create a new clock for that ?) + * - HSIC_P2_480 (HSIC only, create a new clock for that ?) + */ + + /* We need the CM_L3INIT_HSUSBHOST_CLKCTRL register in CM2 register set */ + clk_mem_res = sc->sc_res[CM2_INSTANCE_MEM_REGION]; + clksel_reg_off = L3INIT_CM2_OFFSET + 0x58; + clksel = bus_read_4(clk_mem_res, clksel_reg_off); + + /* Enable the module and also enable the optional func clocks */ + if (clkdev->id == USBHSHOST_CLK) { + clksel &= ~CLKCTRL_MODULEMODE_MASK; + clksel |= CLKCTRL_MODULEMODE_DISABLE; + + clksel &= ~(0x1 << 15); /* USB-HOST clock control: FUNC48MCLK */ + } + + else if (clkdev->id == USBP1_UTMI_CLK) + clksel &= ~(0x1 << 8); /* UTMI_P1_CLK */ + else if (clkdev->id == USBP2_UTMI_CLK) + clksel &= ~(0x1 << 9); /* UTMI_P2_CLK */ + + else if (clkdev->id == USBP1_HSIC_CLK) + clksel &= ~(0x5 << 11); /* HSIC60M_P1_CLK + HSIC480M_P1_CLK */ + else if (clkdev->id == USBP2_HSIC_CLK) + clksel &= ~(0x5 << 12); /* HSIC60M_P2_CLK + HSIC480M_P2_CLK */ + + break; + + default: + return (EINVAL); + } + + bus_write_4(clk_mem_res, clksel_reg_off, clksel); + + return (0); +} + +/** + * omap4_clk_hsusbhost_accessible - checks if a module is accessible + * @clkdev: pointer to the clock device structure. + * @mem_res: array of memory resouces allocated by the top level PRCM driver. + * + * + * + * LOCKING: + * Inherits the locks from the omap_prcm driver, no internal locking. + * + * RETURNS: + * Returns 0 if module is not enable, 1 if module is enabled or a negative + * error code on failure. + */ +static int +omap4_clk_hsusbhost_accessible(struct ti_clock_dev *clkdev) +{ + struct omap4_prcm_softc *sc = omap4_prcm_sc; + struct resource* clk_mem_res; + uint32_t clksel_reg_off; + uint32_t clksel; + + if (sc == NULL) + return ENXIO; + + if (clkdev->id == USBTLL_CLK) { + /* We need the CM_L3INIT_HSUSBTLL_CLKCTRL register in CM2 register set */ + clk_mem_res = sc->sc_res[CM2_INSTANCE_MEM_REGION]; + clksel_reg_off = L3INIT_CM2_OFFSET + 0x68; + } + else if (clkdev->id == USBHSHOST_CLK) { + /* We need the CM_L3INIT_HSUSBHOST_CLKCTRL register in CM2 register set */ + clk_mem_res = sc->sc_res[CM2_INSTANCE_MEM_REGION]; + clksel_reg_off = L3INIT_CM2_OFFSET + 0x58; + } + else { + return (EINVAL); + } + + clksel = bus_read_4(clk_mem_res, clksel_reg_off); + + /* Check the enabled state */ + if ((clksel & CLKCTRL_IDLEST_MASK) != CLKCTRL_IDLEST_ENABLED) + return (0); + + return (1); +} + +/** + * omap4_clk_hsusbhost_set_source - sets the source clocks + * @clkdev: pointer to the clock device structure. + * @clksrc: the clock source ID for the given clock. + * @mem_res: array of memory resouces allocated by the top level PRCM driver. + * + * + * + * LOCKING: + * Inherits the locks from the omap_prcm driver, no internal locking. + * + * RETURNS: + * Returns 0 if sucessful otherwise a negative error code on failure. + */ +static int +omap4_clk_hsusbhost_set_source(struct ti_clock_dev *clkdev, + clk_src_t clksrc) +{ + struct omap4_prcm_softc *sc = omap4_prcm_sc; + struct resource* clk_mem_res; + uint32_t clksel_reg_off; + uint32_t clksel; + unsigned int bit; + + if (sc == NULL) + return ENXIO; + + if (clkdev->id == USBP1_PHY_CLK) + bit = 24; + else if (clkdev->id != USBP2_PHY_CLK) + bit = 25; + else + return (EINVAL); + + /* We need the CM_L3INIT_HSUSBHOST_CLKCTRL register in CM2 register set */ + clk_mem_res = sc->sc_res[CM2_INSTANCE_MEM_REGION]; + clksel_reg_off = L3INIT_CM2_OFFSET + 0x58; + clksel = bus_read_4(clk_mem_res, clksel_reg_off); + + /* Set the clock source to either external or internal */ + if (clksrc == EXT_CLK) + clksel |= (0x1 << bit); + else + clksel &= ~(0x1 << bit); + + bus_write_4(clk_mem_res, clksel_reg_off, clksel); + + return (0); +} + +#define PRM_RSTCTRL 0x1b00 +#define PRM_RSTCTRL_RESET 0x2 + +static void +omap4_prcm_reset(void) +{ + struct omap4_prcm_softc *sc = omap4_prcm_sc; + bus_write_4(sc->sc_res[0], PRM_RSTCTRL, + bus_read_4(sc->sc_res[0], PRM_RSTCTRL) | PRM_RSTCTRL_RESET); + bus_read_4(sc->sc_res[0], PRM_RSTCTRL); +} + +/** + * omap4_prcm_probe - probe function for the driver + * @dev: prcm device handle + * + * Simply sets the name of the driver module. + * + * LOCKING: + * None + * + * RETURNS: + * Always returns 0 + */ +static int +omap4_prcm_probe(device_t dev) +{ + if (!ofw_bus_is_compatible(dev, "ti,omap4_prcm")) + return (ENXIO); + + device_set_desc(dev, "TI OMAP Power, Reset and Clock Management"); + return (0); +} + +/** + * omap_prcm_attach - attach function for the driver + * @dev: prcm device handle + * + * Allocates and sets up the driver context, this simply entails creating a + * bus mappings for the PRCM register set. + * + * LOCKING: + * None + * + * RETURNS: + * Always returns 0 + */ +static int +omap4_prcm_attach(device_t dev) +{ + struct omap4_prcm_softc *sc = device_get_softc(dev); + + if (bus_alloc_resources(dev, omap4_scm_res_spec, sc->sc_res)) { + device_printf(dev, "could not allocate resources\n"); + return (ENXIO); + } + + omap4_prcm_sc = sc; + ti_cpu_reset = omap4_prcm_reset; + + return (0); +} + +static device_method_t omap4_prcm_methods[] = { + DEVMETHOD(device_probe, omap4_prcm_probe), + DEVMETHOD(device_attach, omap4_prcm_attach), + {0, 0}, +}; + +static driver_t omap4_prcm_driver = { + "omap4_prcm", + omap4_prcm_methods, + sizeof(struct omap4_prcm_softc), +}; + +static devclass_t omap4_prcm_devclass; + +DRIVER_MODULE(omap4_prcm, simplebus, omap4_prcm_driver, omap4_prcm_devclass, 0, 0); +MODULE_VERSION(omap4_prcm, 1); diff --git a/sys/arm/ti/omap4/omap4_reg.h b/sys/arm/ti/omap4/omap4_reg.h new file mode 100644 index 000000000000..5a071c28f7c6 --- /dev/null +++ b/sys/arm/ti/omap4/omap4_reg.h @@ -0,0 +1,586 @@ +/*- + * 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$ + */ + +/* + * Texas Instruments - OMAP44xx series processors + * + * Reference: + * OMAP44xx Applications Processor + * Technical Reference Manual + * (omap44xx_techref.pdf) + * + * + * Note: + * The devices are mapped into address above 0xD000_0000 as the kernel space + * memory is at 0xC000_0000 and above. The first 256MB after this is reserved + * for the size of the kernel, everything above that is reserved for SoC + * devices. + * + */ +#ifndef _OMAP44XX_REG_H_ +#define _OMAP44XX_REG_H_ + +#ifndef _LOCORE +#include /* for uint32_t */ +#endif + + + + + +/* Physical/Virtual address for SDRAM controller */ + +#define OMAP44XX_SMS_VBASE 0x6C000000UL +#define OMAP44XX_SMS_HWBASE 0x6C000000UL +#define OMAP44XX_SMS_SIZE 0x01000000UL + +#define OMAP44XX_SDRC_VBASE 0x6D000000UL +#define OMAP44XX_SDRC_HWBASE 0x6D000000UL +#define OMAP44XX_SDRC_SIZE 0x01000000UL + + + +/* Physical/Virtual address for I/O space */ + +#define OMAP44XX_L3_EMU_VBASE 0xD4000000UL +#define OMAP44XX_L3_EMU_HWBASE 0x54000000UL +#define OMAP44XX_L3_EMU_SIZE 0x00200000UL + +#define OMAP44XX_L3_EMIF1_VBASE 0xEC000000UL +#define OMAP44XX_L3_EMIF1_HWBASE 0x4C000000UL +#define OMAP44XX_L3_EMIF1_SIZE 0x01000000UL + +#define OMAP44XX_L3_EMIF2_VBASE 0xED000000UL +#define OMAP44XX_L3_EMIF2_HWBASE 0x4D000000UL +#define OMAP44XX_L3_EMIF2_SIZE 0x01000000UL + + +#define OMAP44XX_L4_CORE_VBASE 0xEA000000UL +#define OMAP44XX_L4_CORE_HWBASE 0x4A000000UL +#define OMAP44XX_L4_CORE_SIZE 0x01000000UL + +#define OMAP44XX_L4_WAKEUP_VBASE 0xEA300000UL +#define OMAP44XX_L4_WAKEUP_HWBASE 0x4A300000UL +#define OMAP44XX_L4_WAKEUP_SIZE 0x00040000UL + +#define OMAP44XX_L4_PERIPH_VBASE 0xE8000000UL +#define OMAP44XX_L4_PERIPH_HWBASE 0x48000000UL +#define OMAP44XX_L4_PERIPH_SIZE 0x01000000UL + +#define OMAP44XX_L4_ABE_VBASE 0xE9000000UL +#define OMAP44XX_L4_ABE_HWBASE 0x49000000UL +#define OMAP44XX_L4_ABE_SIZE 0x00100000UL + + +/* Physical/Virtual address for MPU Subsystem space */ + +#define OMAP44XX_MPU_SUBSYS_VBASE (OMAP44XX_L4_PERIPH_VBASE + 0x00240000UL) +#define OMAP44XX_MPU_SUBSYS_HWBASE (OMAP44XX_L4_PERIPH_HWBASE + 0x00240000UL) +#define OMAP44XX_MPU_SUBSYS_SIZE 0x00004000UL + +/* + * MPU Subsystem addresss offsets + */ +#define OMAP44XX_SCU_OFFSET 0x00000000UL +#define OMAP44XX_GIC_CPU_OFFSET 0x00000100UL +#define OMAP44XX_GBL_TIMER_OFFSET 0x00000200UL +#define OMAP44XX_PRV_TIMER_OFFSET 0x00000600UL +#define OMAP44XX_GIC_DIST_OFFSET 0x00001000UL +#define OMAP44XX_PL310_OFFSET 0x00002000UL +#define OMAP44XX_CORTEXA9_SOCKET_PRCM_OFFSET 0x00003000UL +#define OMAP44XX_CORTEXA9_PRM_OFFSET 0x00003200UL +#define OMAP44XX_CORTEXA9_CPU0_OFFSET 0x00003400UL +#define OMAP44XX_CORTEXA9_CPU1_OFFSET 0x00003800UL + +#define OMAP44XX_SCU_HWBASE (OMAP44XX_MPU_SUBSYS_HWBASE + OMAP44XX_SCU_OFFSET) +#define OMAP44XX_SCU_VBASE (OMAP44XX_MPU_SUBSYS_VBASE + OMAP44XX_SCU_OFFSET) +#define OMAP44XX_SCU_SIZE 0x00000080UL +#define OMAP44XX_GIC_CPU_HWBASE (OMAP44XX_MPU_SUBSYS_HWBASE + OMAP44XX_GIC_CPU_OFFSET) +#define OMAP44XX_GIC_CPU_VBASE (OMAP44XX_MPU_SUBSYS_VBASE + OMAP44XX_GIC_CPU_OFFSET) +#define OMAP44XX_GIC_CPU_SIZE 0x00000100UL +#define OMAP44XX_GBL_TIMER_HWBASE (OMAP44XX_MPU_SUBSYS_HWBASE + OMAP44XX_GBL_TIMER_OFFSET) +#define OMAP44XX_GBL_TIMER_VBASE (OMAP44XX_MPU_SUBSYS_VBASE + OMAP44XX_GBL_TIMER_OFFSET) +#define OMAP44XX_GBL_TIMER_SIZE 0x00000100UL +#define OMAP44XX_PRV_TIMER_HWBASE (OMAP44XX_MPU_SUBSYS_HWBASE + OMAP44XX_PRV_TIMER_OFFSET) +#define OMAP44XX_PRV_TIMER_VBASE (OMAP44XX_MPU_SUBSYS_VBASE + OMAP44XX_PRV_TIMER_OFFSET) +#define OMAP44XX_PRV_TIMER_SIZE 0x00000100UL +#define OMAP44XX_GIC_DIST_HWBASE (OMAP44XX_MPU_SUBSYS_HWBASE + OMAP44XX_GIC_DIST_OFFSET) +#define OMAP44XX_GIC_DIST_VBASE (OMAP44XX_MPU_SUBSYS_VBASE + OMAP44XX_GIC_DIST_OFFSET) +#define OMAP44XX_GIC_DIST_SIZE 0x00000100UL +#define OMAP44XX_PL310_HWBASE (OMAP44XX_MPU_SUBSYS_HWBASE + OMAP44XX_PL310_OFFSET) +#define OMAP44XX_PL310_VBASE (OMAP44XX_MPU_SUBSYS_VBASE + OMAP44XX_PL310_OFFSET) +#define OMAP44XX_PL310_SIZE 0x00001000UL + + + + +/* + * L4-CORE Physical/Virtual addresss offsets + */ +#define OMAP44XX_SCM_OFFSET 0x00002000UL +#define OMAP44XX_CM_OFFSET 0x00004000UL +#define OMAP44XX_SDMA_OFFSET 0x00056000UL +#define OMAP44XX_USB_TLL_OFFSET 0x00062000UL +#define OMAP44XX_USB_UHH_OFFSET 0x00064000UL +#define OMAP44XX_USB_OHCI_OFFSET 0x00064800UL +#define OMAP44XX_USB_EHCI_OFFSET 0x00064C00UL +#define OMAP44XX_MCBSP1_OFFSET 0x00074000UL +#define OMAP44XX_MCBSP5_OFFSET 0x00096000UL +#define OMAP44XX_SCM_PADCONF_OFFSET 0x00100000UL + +/* + * L4-WAKEUP Physical/Virtual addresss offsets + */ +#define OMAP44XX_PRM_OFFSET 0x00006000UL +#define OMAP44XX_SCRM_OFFSET 0x0000A000UL +#define OMAP44XX_GPIO1_OFFSET 0x00010000UL +#define OMAP44XX_GPTIMER1_OFFSET 0x00018000UL + + + +/* + * L4-PERIPH Physical/Virtual addresss offsets + */ +#define OMAP44XX_UART3_OFFSET 0x00020000UL +#define OMAP44XX_GPTIMER2_OFFSET 0x00032000UL +#define OMAP44XX_GPTIMER3_OFFSET 0x00034000UL +#define OMAP44XX_GPTIMER4_OFFSET 0x00036000UL +#define OMAP44XX_GPTIMER9_OFFSET 0x0003E000UL +#define OMAP44XX_GPIO2_OFFSET 0x00055000UL +#define OMAP44XX_GPIO3_OFFSET 0x00057000UL +#define OMAP44XX_GPIO4_OFFSET 0x00059000UL +#define OMAP44XX_GPIO5_OFFSET 0x0005B000UL +#define OMAP44XX_GPIO6_OFFSET 0x0005D000UL +#define OMAP44XX_I2C3_OFFSET 0x00060000UL +#define OMAP44XX_UART1_OFFSET 0x0006A000UL +#define OMAP44XX_UART2_OFFSET 0x0006C000UL +#define OMAP44XX_UART4_OFFSET 0x0006E000UL +#define OMAP44XX_I2C1_OFFSET 0x00070000UL +#define OMAP44XX_I2C2_OFFSET 0x00072000UL +#define OMAP44XX_SLIMBUS2_OFFSET 0x00076000UL +#define OMAP44XX_ELM_OFFSET 0x00078000UL +#define OMAP44XX_GPTIMER10_OFFSET 0x00086000UL +#define OMAP44XX_GPTIMER11_OFFSET 0x00088000UL +#define OMAP44XX_MCBSP4_OFFSET 0x00096000UL +#define OMAP44XX_MCSPI1_OFFSET 0x00098000UL +#define OMAP44XX_MCSPI2_OFFSET 0x0009A000UL +#define OMAP44XX_MMCHS1_OFFSET 0x0009C000UL +#define OMAP44XX_MMCSD3_OFFSET 0x000AD000UL +#define OMAP44XX_MMCHS2_OFFSET 0x000B4000UL +#define OMAP44XX_MMCSD4_OFFSET 0x000D1000UL +#define OMAP44XX_MMCSD5_OFFSET 0x000D5000UL +#define OMAP44XX_I2C4_OFFSET 0x00350000UL + +/* The following are registers defined as part of the ARM MPCORE system, + * they are not SoC components rather registers that control the MPCORE core. + */ +// #define OMAP44XX_SCU_OFFSET 0x48240000 /* Snoop control unit */ +// #define OMAP44XX_GIC_PROC_OFFSET 0x48240100 /* Interrupt controller unit */ +// #define OMAP44XX_MPU_TIMER_OFFSET 0x48240600 +// #define OMAP44XX_GIC_INTR_OFFSET 0x48241000 +// #define OMAP44XX_PL310_OFFSET 0x48242000 /* L2 Cache controller */ + + +/* + * L4-ABE Physical/Virtual addresss offsets + */ +#define OMAP44XX_GPTIMER5_OFFSET 0x00038000UL +#define OMAP44XX_GPTIMER6_OFFSET 0x0003A000UL +#define OMAP44XX_GPTIMER7_OFFSET 0x0003C000UL +#define OMAP44XX_GPTIMER8_OFFSET 0x0003E000UL + + + + + +/* + * System Control Module + */ +#define OMAP44XX_SCM_HWBASE (OMAP44XX_L4_CORE_HWBASE + OMAP44XX_SCM_OFFSET) +#define OMAP44XX_SCM_VBASE (OMAP44XX_L4_CORE_VBASE + OMAP44XX_SCM_OFFSET) +#define OMAP44XX_SCM_SIZE 0x00001000UL + + + +/* + * + */ +#define OMAP44XX_CM_HWBASE (OMAP44XX_L4_CORE_HWBASE + OMAP44XX_CM_OFFSET) +#define OMAP44XX_CM_VBASE (OMAP44XX_L4_CORE_VBASE + OMAP44XX_CM_OFFSET) +#define OMAP44XX_CM_SIZE 0x00001500UL + + +/* + * + */ +#define OMAP44XX_PRM_HWBASE (OMAP44XX_L4_WAKEUP_HWBASE + OMAP44XX_PRM_OFFSET) +#define OMAP44XX_PRM_VBASE (OMAP44XX_L4_WAKEUP_VBASE + OMAP44XX_PRM_OFFSET) +#define OMAP44XX_PRM_SIZE 0x00001600UL + +/* + * + */ +#define OMAP44XX_SCRM_HWBASE (OMAP44XX_L4_WAKEUP_HWBASE + OMAP44XX_SCRM_OFFSET) +#define OMAP44XX_SCRM_VBASE (OMAP44XX_L4_WAKEUP_VBASE + OMAP44XX_SCRM_OFFSET) +#define OMAP44XX_SCRM_SIZE 0x00000800UL + + + +/* + * Uarts + */ +#define OMAP44XX_UART1_HWBASE (OMAP44XX_L4_CORE_HWBASE + OMAP44XX_UART1_OFFSET) +#define OMAP44XX_UART1_VBASE (OMAP44XX_L4_CORE_VBASE + OMAP44XX_UART1_OFFSET) +#define OMAP44XX_UART1_SIZE 0x00001000UL +#define OMAP44XX_UART2_HWBASE (OMAP44XX_L4_CORE_HWBASE + OMAP44XX_UART2_OFFSET) +#define OMAP44XX_UART2_VBASE (OMAP44XX_L4_CORE_VBASE + OMAP44XX_UART2_OFFSET) +#define OMAP44XX_UART2_SIZE 0x00001000UL +#define OMAP44XX_UART3_HWBASE (OMAP44XX_L4_PERIPH_HWBASE + OMAP44XX_UART3_OFFSET) +#define OMAP44XX_UART3_VBASE (OMAP44XX_L4_PERIPH_VBASE + OMAP44XX_UART3_OFFSET) +#define OMAP44XX_UART3_SIZE 0x00001000UL +#define OMAP44XX_UART4_HWBASE (OMAP44XX_L4_PERIPH_HWBASE + OMAP44XX_UART4_OFFSET) +#define OMAP44XX_UART4_VBASE (OMAP44XX_L4_PERIPH_VBASE + OMAP44XX_UART4_OFFSET) +#define OMAP44XX_UART4_SIZE 0x00001000UL + + + + +/* + * I2C Modules + */ +#define OMAP44XX_I2C1_HWBASE (OMAP44XX_L4_CORE_HWBASE + OMAP44XX_I2C1_OFFSET) +#define OMAP44XX_I2C1_VBASE (OMAP44XX_L4_CORE_VBASE + OMAP44XX_I2C1_OFFSET) +#define OMAP44XX_I2C1_SIZE 0x00000080UL +#define OMAP44XX_I2C2_HWBASE (OMAP44XX_L4_CORE_HWBASE + OMAP44XX_I2C2_OFFSET) +#define OMAP44XX_I2C2_VBASE (OMAP44XX_L4_CORE_VBASE + OMAP44XX_I2C2_OFFSET) +#define OMAP44XX_I2C2_SIZE 0x00000080UL +#define OMAP44XX_I2C3_HWBASE (OMAP44XX_L4_CORE_HWBASE + OMAP44XX_I2C3_OFFSET) +#define OMAP44XX_I2C3_VBASE (OMAP44XX_L4_CORE_VBASE + OMAP44XX_I2C3_OFFSET) +#define OMAP44XX_I2C3_SIZE 0x00000080UL + + + +/* + * McBSP Modules + */ +#define OMAP44XX_MCBSP1_HWBASE (OMAP44XX_L4_CORE_HWBASE + OMAP44XX_MCBSP1_OFFSET) +#define OMAP44XX_MCBSP1_VBASE (OMAP44XX_L4_CORE_VBASE + OMAP44XX_MCBSP1_OFFSET) +#define OMAP44XX_MCBSP1_SIZE 0x00001000UL +#define OMAP44XX_MCBSP2_HWBASE (OMAP44XX_L4_PERIPH_HWBASE + OMAP44XX_MCBSP2_OFFSET) +#define OMAP44XX_MCBSP2_VBASE (OMAP44XX_L4_PERIPH_VBASE + OMAP44XX_MCBSP2_OFFSET) +#define OMAP44XX_MCBSP2_SIZE 0x00001000UL +#define OMAP44XX_MCBSP3_HWBASE (OMAP44XX_L4_PERIPH_HWBASE + OMAP44XX_MCBSP3_OFFSET) +#define OMAP44XX_MCBSP3_VBASE (OMAP44XX_L4_PERIPH_VBASE + OMAP44XX_MCBSP3_OFFSET) +#define OMAP44XX_MCBSP3_SIZE 0x00001000UL +#define OMAP44XX_MCBSP4_HWBASE (OMAP44XX_L4_PERIPH_HWBASE + OMAP44XX_MCBSP4_OFFSET) +#define OMAP44XX_MCBSP4_VBASE (OMAP44XX_L4_PERIPH_VBASE + OMAP44XX_MCBSP4_OFFSET) +#define OMAP44XX_MCBSP4_SIZE 0x00001000UL +#define OMAP44XX_MCBSP5_HWBASE (OMAP44XX_L4_CORE_HWBASE + OMAP44XX_MCBSP5_OFFSET) +#define OMAP44XX_MCBSP5_VBASE (OMAP44XX_L4_CORE_VBASE + OMAP44XX_MCBSP5_OFFSET) +#define OMAP44XX_MCBSP5_SIZE 0x00001000UL + + + +/* + * USB TTL Module + */ +#define OMAP44XX_USB_TLL_HWBASE (OMAP44XX_L4_CORE_HWBASE + OMAP44XX_USB_TLL_OFFSET) +#define OMAP44XX_USB_TLL_VBASE (OMAP44XX_L4_CORE_VBASE + OMAP44XX_USB_TLL_OFFSET) +#define OMAP44XX_USB_TLL_SIZE 0x00001000UL + +/* + * USB Host Module + */ +#define OMAP44XX_USB_UHH_HWBASE (OMAP44XX_L4_CORE_HWBASE + OMAP44XX_USB_UHH_OFFSET) +#define OMAP44XX_USB_UHH_VBASE (OMAP44XX_L4_CORE_VBASE + OMAP44XX_USB_UHH_OFFSET) +#define OMAP44XX_USB_UHH_SIZE 0x00000700UL + +/* + * USB OHCI Module + */ +#define OMAP44XX_USB_OHCI_HWBASE (OMAP44XX_L4_CORE_HWBASE + OMAP44XX_USB_OHCI_OFFSET) +#define OMAP44XX_USB_OHCI_VBASE (OMAP44XX_L4_CORE_VBASE + OMAP44XX_USB_OHCI_OFFSET) +#define OMAP44XX_USB_OHCI_SIZE 0x00000400UL + +/* + * USB EHCI Module + */ +#define OMAP44XX_USB_EHCI_HWBASE (OMAP44XX_L4_CORE_HWBASE + OMAP44XX_USB_EHCI_OFFSET) +#define OMAP44XX_USB_EHCI_VBASE (OMAP44XX_L4_CORE_VBASE + OMAP44XX_USB_EHCI_OFFSET) +#define OMAP44XX_USB_EHCI_SIZE 0x0000400UL + + + + + +/* + * SDMA Offset + * PA 0x4805 6000 + */ + +#define OMAP44XX_SDMA_HWBASE (OMAP44XX_L4_CORE_HWBASE + OMAP44XX_SDMA_OFFSET) +#define OMAP44XX_SDMA_VBASE (OMAP44XX_L4_CORE_VBASE + OMAP44XX_SDMA_OFFSET) +#define OMAP44XX_SDMA_SIZE 0x00001000UL + + + +/* + * Interrupt Controller Unit. + * + * Refer to the omap4_intr.c file for interrupt controller (GIC) + * implementation. + * + * Note: + * - 16 Interprocessor interrupts (IPI): ID[15:0] + * - 2 private Timer/Watchdog interrupts: ID[30:29] + * - 2 legacy nFIQ & nIRQ: one per CPU, bypasses the interrupt distributor + * logic and directly drives interrupt requests into CPU if used in + * legacy mode (else treated like other interrupts lines with ID28 + * and ID31 respectively) + * - 128 hardware interrupts: ID[159:32] (rising-edge or high-level sensitive). + */ +#define OMAP44XX_HARDIRQ(x) (32 + (x)) + +#define OMAP44XX_IRQ_L2CACHE OMAP44XX_HARDIRQ(0) /* L2 cache controller interrupt */ +#define OMAP44XX_IRQ_CTI_0 OMAP44XX_HARDIRQ(1) /* Cross-trigger module 0 (CTI0) interrupt */ +#define OMAP44XX_IRQ_CTI_1 OMAP44XX_HARDIRQ(2) /* Cross-trigger module 1 (CTI1) interrupt */ +#define OMAP44XX_IRQ_RESERVED3 OMAP44XX_HARDIRQ(3) /* RESERVED */ +#define OMAP44XX_IRQ_ELM OMAP44XX_HARDIRQ(4) /* Error location process completion */ +#define OMAP44XX_IRQ_RESERVED5 OMAP44XX_HARDIRQ(5) /* RESERVED */ +#define OMAP44XX_IRQ_RESERVED6 OMAP44XX_HARDIRQ(6) /* RESERVED */ +#define OMAP44XX_IRQ_SYS_NIRQ OMAP44XX_HARDIRQ(7) /* External source (active low) */ +#define OMAP44XX_IRQ_RESERVED8 OMAP44XX_HARDIRQ(8) /* RESERVED */ +#define OMAP44XX_IRQ_L3_DBG OMAP44XX_HARDIRQ(9) /* L3 interconnect debug error */ +#define OMAP44XX_IRQ_L3_APP OMAP44XX_HARDIRQ(10) /* L3 interconnect application error */ +#define OMAP44XX_IRQ_PRCM_MPU OMAP44XX_HARDIRQ(11) /* PRCM module IRQ */ +#define OMAP44XX_IRQ_SDMA0 OMAP44XX_HARDIRQ(12) /* System DMA request 0(3) */ +#define OMAP44XX_IRQ_SDMA1 OMAP44XX_HARDIRQ(13) /* System DMA request 1(3) */ +#define OMAP44XX_IRQ_SDMA2 OMAP44XX_HARDIRQ(14) /* System DMA request 2 */ +#define OMAP44XX_IRQ_SDMA3 OMAP44XX_HARDIRQ(15) /* System DMA request 3 */ +#define OMAP44XX_IRQ_MCBSP4 OMAP44XX_HARDIRQ(16) /* McBSP module 4 IRQ */ +#define OMAP44XX_IRQ_MCBSP1 OMAP44XX_HARDIRQ(17) /* McBSP module 1 IRQ */ +#define OMAP44XX_IRQ_SR1 OMAP44XX_HARDIRQ(18) /* SmartReflexâ„¢ 1 */ +#define OMAP44XX_IRQ_SR2 OMAP44XX_HARDIRQ(19) /* SmartReflexâ„¢ 2 */ +#define OMAP44XX_IRQ_GPMC OMAP44XX_HARDIRQ(20) /* General-purpose memory controller module */ +#define OMAP44XX_IRQ_SGX OMAP44XX_HARDIRQ(21) /* 2D/3D graphics module */ +#define OMAP44XX_IRQ_MCBSP2 OMAP44XX_HARDIRQ(22) /* McBSP module 2 */ +#define OMAP44XX_IRQ_MCBSP3 OMAP44XX_HARDIRQ(23) /* McBSP module 3 */ +#define OMAP44XX_IRQ_ISS5 OMAP44XX_HARDIRQ(24) /* Imaging subsystem interrupt 5 */ +#define OMAP44XX_IRQ_DSS OMAP44XX_HARDIRQ(25) /* Display subsystem module(3) */ +#define OMAP44XX_IRQ_MAIL_U0 OMAP44XX_HARDIRQ(26) /* Mailbox user 0 request */ +#define OMAP44XX_IRQ_C2C_SSCM OMAP44XX_HARDIRQ(27) /* C2C status interrupt */ +#define OMAP44XX_IRQ_DSP_MMU OMAP44XX_HARDIRQ(28) /* DSP MMU */ +#define OMAP44XX_IRQ_GPIO1_MPU OMAP44XX_HARDIRQ(29) /* GPIO module 1(3) */ +#define OMAP44XX_IRQ_GPIO2_MPU OMAP44XX_HARDIRQ(30) /* GPIO module 2(3) */ +#define OMAP44XX_IRQ_GPIO3_MPU OMAP44XX_HARDIRQ(31) /* GPIO module 3(3) */ +#define OMAP44XX_IRQ_GPIO4_MPU OMAP44XX_HARDIRQ(32) /* GPIO module 4(3) */ +#define OMAP44XX_IRQ_GPIO5_MPU OMAP44XX_HARDIRQ(33) /* GPIO module 5(3) */ +#define OMAP44XX_IRQ_GPIO6_MPU OMAP44XX_HARDIRQ(34) /* GPIO module 6(3) */ +#define OMAP44XX_IRQ_RESERVED35 OMAP44XX_HARDIRQ(35) /* RESERVED */ +#define OMAP44XX_IRQ_WDT3 OMAP44XX_HARDIRQ(36) /* Watchdog timer module 3 overflow */ +#define OMAP44XX_IRQ_GPT1 OMAP44XX_HARDIRQ(37) /* General-purpose timer module 1 */ +#define OMAP44XX_IRQ_GPT2 OMAP44XX_HARDIRQ(38) /* General-purpose timer module 2 */ +#define OMAP44XX_IRQ_GPT3 OMAP44XX_HARDIRQ(39) /* General-purpose timer module 3 */ +#define OMAP44XX_IRQ_GPT4 OMAP44XX_HARDIRQ(40) /* General-purpose timer module 4 */ +#define OMAP44XX_IRQ_GPT5 OMAP44XX_HARDIRQ(41) /* General-purpose timer module 5 */ +#define OMAP44XX_IRQ_GPT6 OMAP44XX_HARDIRQ(42) /* General-purpose timer module 6 */ +#define OMAP44XX_IRQ_GPT7 OMAP44XX_HARDIRQ(43) /* General-purpose timer module 7 */ +#define OMAP44XX_IRQ_GPT8 OMAP44XX_HARDIRQ(44) /* General-purpose timer module 8 */ +#define OMAP44XX_IRQ_GPT9 OMAP44XX_HARDIRQ(45) /* General-purpose timer module 9 */ +#define OMAP44XX_IRQ_GPT10 OMAP44XX_HARDIRQ(46) /* General-purpose timer module 10 */ +#define OMAP44XX_IRQ_GPT11 OMAP44XX_HARDIRQ(47) /* General-purpose timer module 11 */ +#define OMAP44XX_IRQ_MCSPI4 OMAP44XX_HARDIRQ(48) /* McSPI module 4 */ +#define OMAP44XX_IRQ_RESERVED49 OMAP44XX_HARDIRQ(49) /* RESERVED */ +#define OMAP44XX_IRQ_RESERVED50 OMAP44XX_HARDIRQ(50) /* RESERVED */ +#define OMAP44XX_IRQ_RESERVED51 OMAP44XX_HARDIRQ(51) /* RESERVED */ +#define OMAP44XX_IRQ_RESERVED52 OMAP44XX_HARDIRQ(52) /* RESERVED */ +#define OMAP44XX_IRQ_DSS_DSI1 OMAP44XX_HARDIRQ(53) /* Display Subsystem DSI1 interrupt */ +#define OMAP44XX_IRQ_RESERVED54 OMAP44XX_HARDIRQ(54) /* RESERVED */ +#define OMAP44XX_IRQ_RESERVED55 OMAP44XX_HARDIRQ(55) /* RESERVED */ +#define OMAP44XX_IRQ_I2C1 OMAP44XX_HARDIRQ(56) /* I2C module 1 */ +#define OMAP44XX_IRQ_I2C2 OMAP44XX_HARDIRQ(57) /* I2C module 2 */ +#define OMAP44XX_IRQ_HDQ OMAP44XX_HARDIRQ(58) /* HDQ / One-wire */ +#define OMAP44XX_IRQ_MMC5 OMAP44XX_HARDIRQ(59) /* MMC5 interrupt */ +#define OMAP44XX_IRQ_RESERVED60 OMAP44XX_HARDIRQ(60) /* RESERVED */ +#define OMAP44XX_IRQ_I2C3 OMAP44XX_HARDIRQ(61) /* I2C module 3 */ +#define OMAP44XX_IRQ_I2C4 OMAP44XX_HARDIRQ(62) /* I2C module 4 */ +#define OMAP44XX_IRQ_RESERVED63 OMAP44XX_HARDIRQ(63) /* RESERVED */ +#define OMAP44XX_IRQ_RESERVED64 OMAP44XX_HARDIRQ(64) /* RESERVED */ +#define OMAP44XX_IRQ_MCSPI1 OMAP44XX_HARDIRQ(65) /* McSPI module 1 */ +#define OMAP44XX_IRQ_MCSPI2 OMAP44XX_HARDIRQ(66) /* McSPI module 2 */ +#define OMAP44XX_IRQ_HSI_P1 OMAP44XX_HARDIRQ(67) /* HSI Port 1 interrupt */ +#define OMAP44XX_IRQ_HSI_P2 OMAP44XX_HARDIRQ(68) /* HSI Port 2 interrupt */ +#define OMAP44XX_IRQ_FDIF_3 OMAP44XX_HARDIRQ(69) /* Face detect interrupt 3 */ +#define OMAP44XX_IRQ_UART4 OMAP44XX_HARDIRQ(70) /* UART module 4 interrupt */ +#define OMAP44XX_IRQ_HSI_DMA OMAP44XX_HARDIRQ(71) /* HSI DMA engine MPU request */ +#define OMAP44XX_IRQ_UART1 OMAP44XX_HARDIRQ(72) /* UART module 1 */ +#define OMAP44XX_IRQ_UART2 OMAP44XX_HARDIRQ(73) /* UART module 2 */ +#define OMAP44XX_IRQ_UART3 OMAP44XX_HARDIRQ(74) /* UART module 3 (also infrared)(3) */ +#define OMAP44XX_IRQ_PBIAS OMAP44XX_HARDIRQ(75) /* Merged interrupt for PBIASlite1 and 2 */ +#define OMAP44XX_IRQ_OHCI OMAP44XX_HARDIRQ(76) /* OHCI controller HSUSB MP Host Interrupt */ +#define OMAP44XX_IRQ_EHCI OMAP44XX_HARDIRQ(77) /* EHCI controller HSUSB MP Host Interrupt */ +#define OMAP44XX_IRQ_TLL OMAP44XX_HARDIRQ(78) /* HSUSB MP TLL Interrupt */ +#define OMAP44XX_IRQ_RESERVED79 OMAP44XX_HARDIRQ(79) /* RESERVED */ +#define OMAP44XX_IRQ_WDT2 OMAP44XX_HARDIRQ(80) /* WDTIMER2 interrupt */ +#define OMAP44XX_IRQ_RESERVED81 OMAP44XX_HARDIRQ(81) /* RESERVED */ +#define OMAP44XX_IRQ_RESERVED82 OMAP44XX_HARDIRQ(82) /* RESERVED */ +#define OMAP44XX_IRQ_MMC1 OMAP44XX_HARDIRQ(83) /* MMC/SD module 1 */ +#define OMAP44XX_IRQ_DSS_DSI2 OMAP44XX_HARDIRQ(84) /* Display subsystem DSI2 interrupt */ +#define OMAP44XX_IRQ_RESERVED85 OMAP44XX_HARDIRQ(85) /* Reserved */ +#define OMAP44XX_IRQ_MMC2 OMAP44XX_HARDIRQ(86) /* MMC/SD module 2 */ +#define OMAP44XX_IRQ_MPU_ICR OMAP44XX_HARDIRQ(87) /* MPU ICR */ +#define OMAP44XX_IRQ_C2C_GPI OMAP44XX_HARDIRQ(88) /* C2C GPI interrupt */ +#define OMAP44XX_IRQ_FSUSB OMAP44XX_HARDIRQ(89) /* FS-USB - host controller Interrupt */ +#define OMAP44XX_IRQ_FSUSB_SMI OMAP44XX_HARDIRQ(90) /* FS-USB - host controller SMI Interrupt */ +#define OMAP44XX_IRQ_MCSPI3 OMAP44XX_HARDIRQ(91) /* McSPI module 3 */ +#define OMAP44XX_IRQ_HSUSB_OTG OMAP44XX_HARDIRQ(92) /* High-Speed USB OTG controller */ +#define OMAP44XX_IRQ_HSUSB_OTG_DMA OMAP44XX_HARDIRQ(93) /* High-Speed USB OTG DMA controller */ +#define OMAP44XX_IRQ_MMC3 OMAP44XX_HARDIRQ(94) /* MMC/SD module 3 */ +#define OMAP44XX_IRQ_RESERVED95 OMAP44XX_HARDIRQ(95) /* RESERVED */ +#define OMAP44XX_IRQ_MMC4 OMAP44XX_HARDIRQ(96) /* MMC4 interrupt */ +#define OMAP44XX_IRQ_SLIMBUS1 OMAP44XX_HARDIRQ(97) /* SLIMBUS1 interrupt */ +#define OMAP44XX_IRQ_SLIMBUS2 OMAP44XX_HARDIRQ(98) /* SLIMBUS2 interrupt */ +#define OMAP44XX_IRQ_ABE OMAP44XX_HARDIRQ(99) /* Audio back-end interrupt */ +#define OMAP44XX_IRQ_CORTEXM3_MMU OMAP44XX_HARDIRQ(100) /* Cortex-M3 MMU interrupt */ +#define OMAP44XX_IRQ_DSS_HDMI OMAP44XX_HARDIRQ(101) /* Display subsystem HDMI interrupt */ +#define OMAP44XX_IRQ_SR_IVA OMAP44XX_HARDIRQ(102) /* SmartReflex IVA interrupt */ +#define OMAP44XX_IRQ_IVAHD1 OMAP44XX_HARDIRQ(103) /* Sync interrupt from iCONT2 (vDMA) */ +#define OMAP44XX_IRQ_IVAHD2 OMAP44XX_HARDIRQ(104) /* Sync interrupt from iCONT1 */ +#define OMAP44XX_IRQ_RESERVED105 OMAP44XX_HARDIRQ(105) /* RESERVED */ +#define OMAP44XX_IRQ_RESERVED106 OMAP44XX_HARDIRQ(106) /* RESERVED */ +#define OMAP44XX_IRQ_IVAHD_MAILBOX0 OMAP44XX_HARDIRQ(107) /* IVAHD mailbox interrupt */ +#define OMAP44XX_IRQ_RESERVED108 OMAP44XX_HARDIRQ(108) /* RESERVED */ +#define OMAP44XX_IRQ_MCASP1 OMAP44XX_HARDIRQ(109) /* McASP1 transmit interrupt */ +#define OMAP44XX_IRQ_EMIF1 OMAP44XX_HARDIRQ(110) /* EMIF1 interrupt */ +#define OMAP44XX_IRQ_EMIF2 OMAP44XX_HARDIRQ(111) /* EMIF2 interrupt */ +#define OMAP44XX_IRQ_MCPDM OMAP44XX_HARDIRQ(112) /* MCPDM interrupt */ +#define OMAP44XX_IRQ_DMM OMAP44XX_HARDIRQ(113) /* DMM interrupt */ +#define OMAP44XX_IRQ_DMIC OMAP44XX_HARDIRQ(114) /* DMIC interrupt */ +#define OMAP44XX_IRQ_RESERVED115 OMAP44XX_HARDIRQ(115) /* RESERVED */ +#define OMAP44XX_IRQ_RESERVED116 OMAP44XX_HARDIRQ(116) /* RESERVED */ +#define OMAP44XX_IRQ_RESERVED117 OMAP44XX_HARDIRQ(117) /* RESERVED */ +#define OMAP44XX_IRQ_RESERVED118 OMAP44XX_HARDIRQ(118) /* RESERVED */ +#define OMAP44XX_IRQ_SYS_NIRQ2 OMAP44XX_HARDIRQ(119) /* External source 2 (active low) */ +#define OMAP44XX_IRQ_KBD OMAP44XX_HARDIRQ(120) /* Keyboard controller interrupt */ +#define OMAP44XX_IRQ_RESERVED121 OMAP44XX_HARDIRQ(121) /* RESERVED */ +#define OMAP44XX_IRQ_RESERVED122 OMAP44XX_HARDIRQ(122) /* RESERVED */ +#define OMAP44XX_IRQ_RESERVED123 OMAP44XX_HARDIRQ(123) /* RESERVED */ +#define OMAP44XX_IRQ_RESERVED124 OMAP44XX_HARDIRQ(124) /* RESERVED */ +#define OMAP44XX_IRQ_RESERVED125 OMAP44XX_HARDIRQ(125) /* RESERVED */ +#define OMAP44XX_IRQ_RESERVED126 OMAP44XX_HARDIRQ(126) /* RESERVED */ +#define OMAP44XX_IRQ_RESERVED127 OMAP44XX_HARDIRQ(127) /* RESERVED */ + + + +/* + * General Purpose Timers + */ +#define OMAP44XX_GPTIMER1_VBASE (OMAP44XX_L4_WAKEUP_VBASE + OMAP44XX_GPTIMER1_OFFSET) +#define OMAP44XX_GPTIMER1_HWBASE (OMAP44XX_L4_WAKEUP_HWBASE + OMAP44XX_GPTIMER1_OFFSET) +#define OMAP44XX_GPTIMER2_VBASE (OMAP44XX_L4_PERIPH_VBASE + OMAP44XX_GPTIMER2_OFFSET) +#define OMAP44XX_GPTIMER2_HWBASE (OMAP44XX_L4_PERIPH_HWBASE + OMAP44XX_GPTIMER2_OFFSET) +#define OMAP44XX_GPTIMER3_VBASE (OMAP44XX_L4_PERIPH_VBASE + OMAP44XX_GPTIMER3_OFFSET) +#define OMAP44XX_GPTIMER3_HWBASE (OMAP44XX_L4_PERIPH_HWBASE + OMAP44XX_GPTIMER3_OFFSET) +#define OMAP44XX_GPTIMER4_VBASE (OMAP44XX_L4_PERIPH_VBASE + OMAP44XX_GPTIMER4_OFFSET) +#define OMAP44XX_GPTIMER4_HWBASE (OMAP44XX_L4_PERIPH_HWBASE + OMAP44XX_GPTIMER4_OFFSET) +#define OMAP44XX_GPTIMER5_VBASE (OMAP44XX_L4_ABE_VBASE + OMAP44XX_GPTIMER5_OFFSET) +#define OMAP44XX_GPTIMER5_HWBASE (OMAP44XX_L4_ABE_HWBASE + OMAP44XX_GPTIMER5_OFFSET) +#define OMAP44XX_GPTIMER6_VBASE (OMAP44XX_L4_ABE_VBASE + OMAP44XX_GPTIMER6_OFFSET) +#define OMAP44XX_GPTIMER6_HWBASE (OMAP44XX_L4_ABE_HWBASE + OMAP44XX_GPTIMER6_OFFSET) +#define OMAP44XX_GPTIMER7_VBASE (OMAP44XX_L4_ABE_VBASE + OMAP44XX_GPTIMER7_OFFSET) +#define OMAP44XX_GPTIMER7_HWBASE (OMAP44XX_L4_ABE_HWBASE + OMAP44XX_GPTIMER7_OFFSET) +#define OMAP44XX_GPTIMER8_VBASE (OMAP44XX_L4_ABE_VBASE + OMAP44XX_GPTIMER8_OFFSET) +#define OMAP44XX_GPTIMER8_HWBASE (OMAP44XX_L4_ABE_HWBASE + OMAP44XX_GPTIMER8_OFFSET) +#define OMAP44XX_GPTIMER9_VBASE (OMAP44XX_L4_PERIPH_VBASE + OMAP44XX_GPTIMER9_OFFSET) +#define OMAP44XX_GPTIMER9_HWBASE (OMAP44XX_L4_PERIPH_HWBASE + OMAP44XX_GPTIMER9_OFFSET) +#define OMAP44XX_GPTIMER10_VBASE (OMAP44XX_L4_PERIPH_VBASE + OMAP44XX_GPTIMER10_OFFSET) +#define OMAP44XX_GPTIMER10_HWBASE (OMAP44XX_L4_PERIPH_HWBASE + OMAP44XX_GPTIMER10_OFFSET) +#define OMAP44XX_GPTIMER11_VBASE (OMAP44XX_L4_PERIPH_VBASE + OMAP44XX_GPTIMER11_OFFSET) +#define OMAP44XX_GPTIMER11_HWBASE (OMAP44XX_L4_PERIPH_HWBASE + OMAP44XX_GPTIMER11_OFFSET) +#define OMAP44XX_GPTIMER_SIZE 0x00001000UL + + + +/* + * GPIO - General Purpose IO + */ + +/* Base addresses for the GPIO modules */ +#define OMAP44XX_GPIO1_HWBASE (OMAP44XX_L4_WAKEUP_HWBASE + OMAP44XX_GPIO1_OFFSET) +#define OMAP44XX_GPIO1_VBASE (OMAP44XX_L4_WAKEUP_VBASE + OMAP44XX_GPIO1_OFFSET) +#define OMAP44XX_GPIO1_SIZE 0x00001000UL +#define OMAP44XX_GPIO2_HWBASE (OMAP44XX_L4_PERIPH_HWBASE + OMAP44XX_GPIO2_OFFSET) +#define OMAP44XX_GPIO2_VBASE (OMAP44XX_L4_PERIPH_VBASE + OMAP44XX_GPIO2_OFFSET) +#define OMAP44XX_GPIO2_SIZE 0x00001000UL +#define OMAP44XX_GPIO3_HWBASE (OMAP44XX_L4_PERIPH_HWBASE + OMAP44XX_GPIO3_OFFSET) +#define OMAP44XX_GPIO3_VBASE (OMAP44XX_L4_PERIPH_VBASE + OMAP44XX_GPIO3_OFFSET) +#define OMAP44XX_GPIO3_SIZE 0x00001000UL +#define OMAP44XX_GPIO4_HWBASE (OMAP44XX_L4_PERIPH_HWBASE + OMAP44XX_GPIO4_OFFSET) +#define OMAP44XX_GPIO4_VBASE (OMAP44XX_L4_PERIPH_VBASE + OMAP44XX_GPIO4_OFFSET) +#define OMAP44XX_GPIO4_SIZE 0x00001000UL +#define OMAP44XX_GPIO5_HWBASE (OMAP44XX_L4_PERIPH_HWBASE + OMAP44XX_GPIO5_OFFSET) +#define OMAP44XX_GPIO5_VBASE (OMAP44XX_L4_PERIPH_VBASE + OMAP44XX_GPIO5_OFFSET) +#define OMAP44XX_GPIO5_SIZE 0x00001000UL +#define OMAP44XX_GPIO6_HWBASE (OMAP44XX_L4_PERIPH_HWBASE + OMAP44XX_GPIO6_OFFSET) +#define OMAP44XX_GPIO6_VBASE (OMAP44XX_L4_PERIPH_VBASE + OMAP44XX_GPIO6_OFFSET) +#define OMAP44XX_GPIO6_SIZE 0x00001000UL + + +/* + * MMC/SD/SDIO + */ + +/* Base addresses for the MMC/SD/SDIO modules */ +#define OMAP44XX_MMCHS1_HWBASE (OMAP44XX_L4_PERIPH_HWBASE + OMAP44XX_MMCHS1_OFFSET) +#define OMAP44XX_MMCHS1_VBASE (OMAP44XX_L4_PERIPH_VBASE + OMAP44XX_MMCHS1_OFFSET) +#define OMAP44XX_MMCHS2_HWBASE (OMAP44XX_L4_PERIPH_HWBASE + OMAP44XX_MMCHS2_OFFSET) +#define OMAP44XX_MMCHS2_VBASE (OMAP44XX_L4_PERIPH_VBASE + OMAP44XX_MMCHS2_OFFSET) +#define OMAP44XX_MMCHS3_HWBASE (OMAP44XX_L4_PERIPH_HWBASE + OMAP44XX_MMCSD3_OFFSET) +#define OMAP44XX_MMCHS3_VBASE (OMAP44XX_L4_PERIPH_VBASE + OMAP44XX_MMCSD3_OFFSET) +#define OMAP44XX_MMCHS4_HWBASE (OMAP44XX_L4_PERIPH_HWBASE + OMAP44XX_MMCSD4_OFFSET) +#define OMAP44XX_MMCHS4_VBASE (OMAP44XX_L4_PERIPH_VBASE + OMAP44XX_MMCSD4_OFFSET) +#define OMAP44XX_MMCHS5_HWBASE (OMAP44XX_L4_PERIPH_HWBASE + OMAP44XX_MMCSD5_OFFSET) +#define OMAP44XX_MMCHS5_VBASE (OMAP44XX_L4_PERIPH_VBASE + OMAP44XX_MMCSD5_OFFSET) +#define OMAP44XX_MMCHS_SIZE 0x00001000UL + + + +/* + * SCM - System Control Module + */ + +/* Base addresses for the SC modules */ +#define OMAP44XX_SCM_PADCONF_HWBASE (OMAP44XX_L4_CORE_HWBASE + OMAP44XX_SCM_PADCONF_OFFSET) +#define OMAP44XX_SCM_PADCONF_VBASE (OMAP44XX_L4_CORE_VBASE + OMAP44XX_SCM_PADCONF_OFFSET) +#define OMAP44XX_SCM_PADCONF_SIZE 0x00001000UL + + + + +#endif /* _OMAP44XX_REG_H_ */ diff --git a/sys/arm/ti/omap4/omap4_scm_padconf.c b/sys/arm/ti/omap4/omap4_scm_padconf.c new file mode 100644 index 000000000000..4cd3a564d464 --- /dev/null +++ b/sys/arm/ti/omap4/omap4_scm_padconf.c @@ -0,0 +1,405 @@ +/*- + * 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. + * 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. + */ +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + + +/* + * This file defines the pin mux configuration for the OMAP4xxx series of + * devices. + * + * How This is Suppose to Work + * =========================== + * - There is a top level ti_scm module (System Control Module) that is + * the interface for all omap drivers, which can use it to change the mux + * settings for individual pins. (That said, typically the pin mux settings + * are set to defaults by the 'hints' and then not altered by the driver). + * + * - For this to work the top level driver needs all the pin info, and hence + * this is where this file comes in. Here we define all the pin information + * that is supplied to the top level driver. + * + */ + +#define CONTROL_PADCONF_WAKEUP_EVENT (1UL << 15) +#define CONTROL_PADCONF_WAKEUP_ENABLE (1UL << 14) +#define CONTROL_PADCONF_OFF_PULL_UP (1UL << 13) +#define CONTROL_PADCONF_OFF_PULL_ENABLE (1UL << 12) +#define CONTROL_PADCONF_OFF_OUT_HIGH (1UL << 11) +#define CONTROL_PADCONF_OFF_OUT_ENABLE (1UL << 10) +#define CONTROL_PADCONF_OFF_ENABLE (1UL << 9) +#define CONTROL_PADCONF_INPUT_ENABLE (1UL << 8) +#define CONTROL_PADCONF_PULL_UP (1UL << 4) +#define CONTROL_PADCONF_PULL_ENABLE (1UL << 3) +#define CONTROL_PADCONF_MUXMODE_MASK (0x7) + +#define CONTROL_PADCONF_SATE_MASK ( CONTROL_PADCONF_WAKEUP_EVENT \ + | CONTROL_PADCONF_WAKEUP_ENABLE \ + | CONTROL_PADCONF_OFF_PULL_UP \ + | CONTROL_PADCONF_OFF_PULL_ENABLE \ + | CONTROL_PADCONF_OFF_OUT_HIGH \ + | CONTROL_PADCONF_OFF_OUT_ENABLE \ + | CONTROL_PADCONF_OFF_ENABLE \ + | CONTROL_PADCONF_INPUT_ENABLE \ + | CONTROL_PADCONF_PULL_UP \ + | CONTROL_PADCONF_PULL_ENABLE ) + +/* Active pin states */ +#define PADCONF_PIN_OUTPUT 0 +#define PADCONF_PIN_INPUT CONTROL_PADCONF_INPUT_ENABLE +#define PADCONF_PIN_INPUT_PULLUP ( CONTROL_PADCONF_INPUT_ENABLE \ + | CONTROL_PADCONF_PULL_ENABLE \ + | CONTROL_PADCONF_PULL_UP) +#define PADCONF_PIN_INPUT_PULLDOWN ( CONTROL_PADCONF_INPUT_ENABLE \ + | CONTROL_PADCONF_PULL_ENABLE ) + +/* Off mode states */ +#define PADCONF_PIN_OFF_NONE 0 +#define PADCONF_PIN_OFF_OUTPUT_HIGH ( CONTROL_PADCONF_OFF_ENABLE \ + | CONTROL_PADCONF_OFF_OUT_ENABLE \ + | CONTROL_PADCONF_OFF_OUT_HIGH) +#define PADCONF_PIN_OFF_OUTPUT_LOW ( CONTROL_PADCONF_OFF_ENABLE \ + | CONTROL_PADCONF_OFF_OUT_ENABLE) +#define PADCONF_PIN_OFF_INPUT_PULLUP ( CONTROL_PADCONF_OFF_ENABLE \ + | CONTROL_PADCONF_OFF_PULL_ENABLE \ + | CONTROL_PADCONF_OFF_PULL_UP) +#define PADCONF_PIN_OFF_INPUT_PULLDOWN ( CONTROL_PADCONF_OFF_ENABLE \ + | CONTROL_PADCONF_OFF_PULL_ENABLE) +#define PADCONF_PIN_OFF_WAKEUPENABLE CONTROL_PADCONF_WAKEUP_ENABLE + + +#define _PINDEF(r, b, gp, gm, m0, m1, m2, m3, m4, m5, m6, m7) \ + { .reg_off = r, \ + .gpio_pin = gp, \ + .gpio_mode = gm, \ + .ballname = b, \ + .muxmodes[0] = m0, \ + .muxmodes[1] = m1, \ + .muxmodes[2] = m2, \ + .muxmodes[3] = m3, \ + .muxmodes[4] = m4, \ + .muxmodes[5] = m5, \ + .muxmodes[6] = m6, \ + .muxmodes[7] = m7, \ + } + +const struct ti_scm_padstate ti_padstate_devmap[] = { + {"output", PADCONF_PIN_OUTPUT}, + {"input", PADCONF_PIN_INPUT}, + {"input_pullup", PADCONF_PIN_INPUT_PULLUP}, + {"input_pulldown", PADCONF_PIN_INPUT_PULLDOWN}, + { .state = NULL } +}; + +/* + * Table 18-10, p. 3470 + */ +const struct ti_scm_padconf ti_padconf_devmap[] = { + _PINDEF(0x0040, "c12", 0, 0, "gpmc_ad0", "sdmmc2_dat0", NULL, NULL, NULL, NULL, NULL, NULL), + _PINDEF(0x0042, "d12", 0, 0, "gpmc_ad1", "sdmmc2_dat1", NULL, NULL, NULL, NULL, NULL, NULL), + _PINDEF(0x0044, "c13", 0, 0, "gpmc_ad2", "sdmmc2_dat2", NULL, NULL, NULL, NULL, NULL, NULL), + _PINDEF(0x0046, "d13", 0, 0, "gpmc_ad3", "sdmmc2_dat3", NULL, NULL, NULL, NULL, NULL, NULL), + _PINDEF(0x0048, "c15", 0, 0, "gpmc_ad4", "sdmmc2_dat4", "sdmmc2_dir_dat0", NULL, NULL, NULL, NULL, NULL), + _PINDEF(0x004a, "d15", 0, 0, "gpmc_ad5", "sdmmc2_dat5", "sdmmc2_dir_dat1", NULL, NULL, NULL, NULL, NULL), + _PINDEF(0x004c, "a16", 0, 0, "gpmc_ad6", "sdmmc2_dat6", "sdmmc2_dir_cmd", NULL, NULL, NULL, NULL, NULL), + _PINDEF(0x004e, "b16", 0, 0, "gpmc_ad7", "sdmmc2_dat7", "sdmmc2_clk_fdbk", NULL, NULL, NULL, NULL, NULL), + _PINDEF(0x0050, "c16", 32, 3, "gpmc_ad8", "kpd_row0", "c2c_data15", "gpio_32", NULL, "sdmmc1_dat0", NULL, NULL), + _PINDEF(0x0052, "d16", 33, 3, "gpmc_ad9", "kpd_row1", "c2c_data14", "gpio_33", NULL, "sdmmc1_dat1", NULL, NULL), + _PINDEF(0x0054, "c17", 34, 3, "gpmc_ad10", "kpd_row2", "c2c_data13", "gpio_34", NULL, "sdmmc1_dat2", NULL, NULL), + _PINDEF(0x0056, "d17", 35, 3, "gpmc_ad11", "kpd_row3", "c2c_data12", "gpio_35", NULL, "sdmmc1_dat3", NULL, NULL), + _PINDEF(0x0058, "c18", 36, 3, "gpmc_ad12", "kpd_col0", "c2c_data11", "gpio_36", NULL, "sdmmc1_dat4", NULL, NULL), + _PINDEF(0x005a, "d18", 37, 3, "gpmc_ad13", "kpd_col1", "c2c_data10", "gpio_37", NULL, "sdmmc1_dat5", NULL, NULL), + _PINDEF(0x005c, "c19", 38, 3, "gpmc_ad14", "kpd_col2", "c2c_data9", "gpio_38", NULL, "sdmmc1_dat6", NULL, NULL), + _PINDEF(0x005e, "d19", 39, 3, "gpmc_ad15", "kpd_col3", "c2c_data8", "gpio_39", NULL, "sdmmc1_dat7", NULL, NULL), + _PINDEF(0x0060, "b17", 40, 3, "gpmc_a16", "kpd_row4", "c2c_datain0", "gpio_40", "venc_656_data0", NULL, NULL, "safe_mode"), + _PINDEF(0x0062, "a18", 41, 3, "gpmc_a17", "kpd_row5", "c2c_datain1", "gpio_41", "venc_656_data1", NULL, NULL, "safe_mode"), + _PINDEF(0x0064, "b18", 42, 3, "gpmc_a18", "kpd_row6", "c2c_datain2", "gpio_42", "venc_656_data2", NULL, NULL, "safe_mode"), + _PINDEF(0x0066, "a19", 43, 3, "gpmc_a19", "kpd_row7", "c2c_datain3", "gpio_43", "venc_656_data3", NULL, NULL, "safe_mode"), + _PINDEF(0x0068, "b19", 44, 3, "gpmc_a20", "kpd_col4", "c2c_datain4", "gpio_44", "venc_656_data4", NULL, NULL, "safe_mode"), + _PINDEF(0x006a, "b20", 45, 3, "gpmc_a21", "kpd_col5", "c2c_datain5", "gpio_45", "venc_656_data5", NULL, NULL, "safe_mode"), + _PINDEF(0x006c, "a21", 46, 3, "gpmc_a22", "kpd_col6", "c2c_datain6", "gpio_46", "venc_656_data6", NULL, NULL, "safe_mode"), + _PINDEF(0x006e, "b21", 47, 3, "gpmc_a23", "kpd_col7", "c2c_datain7", "gpio_47", "venc_656_data7", NULL, NULL, "safe_mode"), + _PINDEF(0x0070, "c20", 48, 3, "gpmc_a24", "kpd_col8", "c2c_clkout0", "gpio_48", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x0072, "d20", 49, 3, "gpmc_a25", NULL, "c2c_clkout1", "gpio_49", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x0074, "b25", 50, 3, "gpmc_ncs0", NULL, NULL, "gpio_50", "sys_ndmareq0", NULL, NULL, NULL), + _PINDEF(0x0076, "c21", 51, 3, "gpmc_ncs1", NULL, "c2c_dataout6", "gpio_51", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x0078, "d21", 52, 3, "gpmc_ncs2", "kpd_row8", "c2c_dataout7", "gpio_52", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x007a, "c22", 53, 3, "gpmc_ncs3", "gpmc_dir", "c2c_dataout4", "gpio_53", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x007c, "c25", 54, 3, "gpmc_nwp", "dsi1_te0", NULL, "gpio_54", "sys_ndmareq1", NULL, NULL, NULL), + _PINDEF(0x007e, "b22", 55, 3, "gpmc_clk", NULL, NULL, "gpio_55", "sys_ndmareq2", "sdmmc1_cmd", NULL, NULL), + _PINDEF(0x0080, "d25", 56, 3, "gpmc_nadv_ale", "dsi1_te1", NULL, "gpio_56", "sys_ndmareq3", "sdmmc1_clk", NULL, NULL), + _PINDEF(0x0082, "b11", 0, 0, "gpmc_noe", "sdmmc2_clk", NULL, NULL, NULL, NULL, NULL, NULL), + _PINDEF(0x0084, "b12", 0, 0, "gpmc_nwe", "sdmmc2_cmd", NULL, NULL, NULL, NULL, NULL, NULL), + _PINDEF(0x0086, "c23", 59, 3, "gpmc_nbe0_cle", "dsi2_te0", NULL, "gpio_59", NULL, NULL, NULL, NULL), + _PINDEF(0x0088, "d22", 60, 3, "gpmc_nbe1", NULL, "c2c_dataout5", "gpio_60", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x008a, "b26", 61, 3, "gpmc_wait0", "dsi2_te1", NULL, "gpio_61", NULL, NULL, NULL, NULL), + _PINDEF(0x008c, "b23", 62, 3, "gpmc_wait1", NULL, "c2c_dataout2", "gpio_62", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x008e, "d23", 100, 3, "gpmc_wait2", "usbc1_icusb_txen", "c2c_dataout3", "gpio_100", "sys_ndmareq0", NULL, NULL, "safe_mode"), + _PINDEF(0x0090, "a24", 101, 3, "gpmc_ncs4", "dsi1_te0", "c2c_clkin0", "gpio_101", "sys_ndmareq1", NULL, NULL, "safe_mode"), + _PINDEF(0x0092, "b24", 102, 3, "gpmc_ncs5", "dsi1_te1", "c2c_clkin1", "gpio_102", "sys_ndmareq2", NULL, NULL, "safe_mode"), + _PINDEF(0x0094, "c24", 103, 3, "gpmc_ncs6", "dsi2_te0", "c2c_dataout0", "gpio_103", "sys_ndmareq3", NULL, NULL, "safe_mode"), + _PINDEF(0x0096, "d24", 104, 3, "gpmc_ncs7", "dsi2_te1", "c2c_dataout1", "gpio_104", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x0098, "b9", 63, 3, "hdmi_hpd", NULL, NULL, "gpio_63", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x009a, "b10", 64, 3, "hdmi_cec", NULL, NULL, "gpio_64", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x009c, "a8", 65, 3, "hdmi_ddc_scl", NULL, NULL, "gpio_65", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x009e, "b8", 66, 3, "hdmi_ddc_sda", NULL, NULL, "gpio_66", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x00a0, "r26", 0, 0, "csi21_dx0", NULL, NULL, "gpi_67", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x00a2, "r25", 0, 0, "csi21_dy0", NULL, NULL, "gpi_68", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x00a4, "t26", 0, 0, "csi21_dx1", NULL, NULL, "gpi_69", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x00a6, "t25", 0, 0, "csi21_dy1", NULL, NULL, "gpi_70", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x00a8, "u26", 0, 0, "csi21_dx2", NULL, NULL, "gpi_71", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x00aa, "u25", 0, 0, "csi21_dy2", NULL, NULL, "gpi_72", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x00ac, "v26", 0, 0, "csi21_dx3", NULL, NULL, "gpi_73", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x00ae, "v25", 0, 0, "csi21_dy3", NULL, NULL, "gpi_74", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x00b0, "w26", 0, 0, "csi21_dx4", NULL, NULL, "gpi_75", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x00b2, "w25", 0, 0, "csi21_dy4", NULL, NULL, "gpi_76", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x00b4, "m26", 0, 0, "csi22_dx0", NULL, NULL, "gpi_77", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x00b6, "m25", 0, 0, "csi22_dy0", NULL, NULL, "gpi_78", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x00b8, "n26", 0, 0, "csi22_dx1", NULL, NULL, "gpi_79", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x00ba, "n25", 0, 0, "csi22_dy1", NULL, NULL, "gpi_80", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x00bc, "t27", 81, 3, "cam_shutter", NULL, NULL, "gpio_81", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x00be, "u27", 82, 3, "cam_strobe", NULL, NULL, "gpio_82", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x00c0, "v27", 83, 3, "cam_globalreset", NULL, NULL, "gpio_83", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x00c2, "ae18", 84, 3, "usbb1_ulpitll_clk", "hsi1_cawake", NULL, "gpio_84", "usbb1_ulpiphy_clk", NULL, "hw_dbg20", "safe_mode"), + _PINDEF(0x00c4, "ag19", 85, 3, "usbb1_ulpitll_stp", "hsi1_cadata", "mcbsp4_clkr", "gpio_85", "usbb1_ulpiphy_stp", "usbb1_mm_rxdp", "hw_dbg21", "safe_mode"), + _PINDEF(0x00c6, "af19", 86, 3, "usbb1_ulpitll_dir", "hsi1_caflag", "mcbsp4_fsr", "gpio_86", "usbb1_ulpiphy_dir", NULL, "hw_dbg22", "safe_mode"), + _PINDEF(0x00c8, "ae19", 87, 3, "usbb1_ulpitll_nxt", "hsi1_acready", "mcbsp4_fsx", "gpio_87", "usbb1_ulpiphy_nxt", "usbb1_mm_rxdm", "hw_dbg23", "safe_mode"), + _PINDEF(0x00ca, "af18", 88, 3, "usbb1_ulpitll_dat0", "hsi1_acwake", "mcbsp4_clkx", "gpio_88", "usbb1_ulpiphy_dat0", "usbb1_mm_txen", "hw_dbg24", "safe_mode"), + _PINDEF(0x00cc, "ag18", 89, 3, "usbb1_ulpitll_dat1", "hsi1_acdata", "mcbsp4_dx", "gpio_89", "usbb1_ulpiphy_dat1", "usbb1_mm_txdat", "hw_dbg25", "safe_mode"), + _PINDEF(0x00ce, "ae17", 90, 3, "usbb1_ulpitll_dat2", "hsi1_acflag", "mcbsp4_dr", "gpio_90", "usbb1_ulpiphy_dat2", "usbb1_mm_txse0", "hw_dbg26", "safe_mode"), + _PINDEF(0x00d0, "af17", 91, 3, "usbb1_ulpitll_dat3", "hsi1_caready", NULL, "gpio_91", "usbb1_ulpiphy_dat3", "usbb1_mm_rxrcv", "hw_dbg27", "safe_mode"), + _PINDEF(0x00d2, "ah17", 92, 3, "usbb1_ulpitll_dat4", "dmtimer8_pwm_evt", "abe_mcbsp3_dr", "gpio_92", "usbb1_ulpiphy_dat4", NULL, "hw_dbg28", "safe_mode"), + _PINDEF(0x00d4, "ae16", 93, 3, "usbb1_ulpitll_dat5", "dmtimer9_pwm_evt", "abe_mcbsp3_dx", "gpio_93", "usbb1_ulpiphy_dat5", NULL, "hw_dbg29", "safe_mode"), + _PINDEF(0x00d6, "af16", 94, 3, "usbb1_ulpitll_dat6", "dmtimer10_pwm_evt", "abe_mcbsp3_clkx", "gpio_94", "usbb1_ulpiphy_dat6", "abe_dmic_din3", "hw_dbg30", "safe_mode"), + _PINDEF(0x00d8, "ag16", 95, 3, "usbb1_ulpitll_dat7", "dmtimer11_pwm_evt", "abe_mcbsp3_fsx", "gpio_95", "usbb1_ulpiphy_dat7", "abe_dmic_clk3", "hw_dbg31", "safe_mode"), + _PINDEF(0x00da, "af14", 96, 3, "usbb1_hsic_data", NULL, NULL, "gpio_96", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x00dc, "ae14", 97, 3, "usbb1_hsic_strobe", NULL, NULL, "gpio_97", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x00de, "h2", 98, 3, "usbc1_icusb_dp", NULL, NULL, "gpio_98", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x00e0, "h3", 99, 3, "usbc1_icusb_dm", NULL, NULL, "gpio_99", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x00e2, "d2", 100, 3, "sdmmc1_clk", NULL, "dpm_emu19", "gpio_100", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x00e4, "e3", 101, 3, "sdmmc1_cmd", NULL, "uart1_rx", "gpio_101", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x00e6, "e4", 102, 3, "sdmmc1_dat0", NULL, "dpm_emu18", "gpio_102", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x00e8, "e2", 103, 3, "sdmmc1_dat1", NULL, "dpm_emu17", "gpio_103", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x00ea, "e1", 104, 3, "sdmmc1_dat2", NULL, "dpm_emu16", "gpio_104", "jtag_tms_tmsc", NULL, NULL, "safe_mode"), + _PINDEF(0x00ec, "f4", 105, 3, "sdmmc1_dat3", NULL, "dpm_emu15", "gpio_105", "jtag_tck", NULL, NULL, "safe_mode"), + _PINDEF(0x00ee, "f3", 106, 3, "sdmmc1_dat4", NULL, NULL, "gpio_106", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x00f0, "f1", 107, 3, "sdmmc1_dat5", NULL, NULL, "gpio_107", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x00f2, "g4", 108, 3, "sdmmc1_dat6", NULL, NULL, "gpio_108", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x00f4, "g3", 109, 3, "sdmmc1_dat7", NULL, NULL, "gpio_109", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x00f6, "ad27", 110, 3, "abe_mcbsp2_clkx", "mcspi2_clk", "abe_mcasp_ahclkx", "gpio_110", "usbb2_mm_rxdm", NULL, NULL, "safe_mode"), + _PINDEF(0x00f8, "ad26", 111, 3, "abe_mcbsp2_dr", "mcspi2_somi", "abe_mcasp_axr", "gpio_111", "usbb2_mm_rxdp", NULL, NULL, "safe_mode"), + _PINDEF(0x00fa, "ad25", 112, 3, "abe_mcbsp2_dx", "mcspi2_simo", "abe_mcasp_amute", "gpio_112", "usbb2_mm_rxrcv", NULL, NULL, "safe_mode"), + _PINDEF(0x00fc, "ac28", 113, 3, "abe_mcbsp2_fsx", "mcspi2_cs0", "abe_mcasp_afsx", "gpio_113", "usbb2_mm_txen", NULL, NULL, "safe_mode"), + _PINDEF(0x00fe, "ac26", 114, 3, "abe_mcbsp1_clkx", "abe_slimbus1_clock", NULL, "gpio_114", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x0100, "ac25", 115, 3, "abe_mcbsp1_dr", "abe_slimbus1_data", NULL, "gpio_115", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x0102, "ab25", 116, 3, "abe_mcbsp1_dx", "sdmmc3_dat2", "abe_mcasp_aclkx", "gpio_116", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x0104, "ac27", 117, 3, "abe_mcbsp1_fsx", "sdmmc3_dat3", "abe_mcasp_amutein", "gpio_117", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x0106, "ag25", 0, 0, "abe_pdm_ul_data", "abe_mcbsp3_dr", NULL, NULL, NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x0108, "af25", 0, 0, "abe_pdm_dl_data", "abe_mcbsp3_dx", NULL, NULL, NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x010a, "ae25", 0, 0, "abe_pdm_frame", "abe_mcbsp3_clkx", NULL, NULL, NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x010c, "af26", 0, 0, "abe_pdm_lb_clk", "abe_mcbsp3_fsx", NULL, NULL, NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x010e, "ah26", 118, 3, "abe_clks", NULL, NULL, "gpio_118", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x0110, "ae24", 119, 3, "abe_dmic_clk1", NULL, NULL, "gpio_119", "usbb2_mm_txse0", "uart4_cts", NULL, "safe_mode"), + _PINDEF(0x0112, "af24", 120, 3, "abe_dmic_din1", NULL, NULL, "gpio_120", "usbb2_mm_txdat", "uart4_rts", NULL, "safe_mode"), + _PINDEF(0x0114, "ag24", 121, 3, "abe_dmic_din2", "slimbus2_clock", "abe_mcasp_axr", "gpio_121", NULL, "dmtimer11_pwm_evt", NULL, "safe_mode"), + _PINDEF(0x0116, "ah24", 122, 3, "abe_dmic_din3", "slimbus2_data", "abe_dmic_clk2", "gpio_122", NULL, "dmtimer9_pwm_evt", NULL, "safe_mode"), + _PINDEF(0x0118, "ab26", 123, 3, "uart2_cts", "sdmmc3_clk", NULL, "gpio_123", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x011a, "ab27", 124, 3, "uart2_rts", "sdmmc3_cmd", NULL, "gpio_124", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x011c, "aa25", 125, 3, "uart2_rx", "sdmmc3_dat0", NULL, "gpio_125", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x011e, "aa26", 126, 3, "uart2_tx", "sdmmc3_dat1", NULL, "gpio_126", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x0120, "aa27", 127, 3, "hdq_sio", "i2c3_sccb", "i2c2_sccb", "gpio_127", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x0122, "ae28", 0, 0, "i2c1_scl", NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PINDEF(0x0124, "ae26", 0, 0, "i2c1_sda", NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PINDEF(0x0126, "c26", 128, 3, "i2c2_scl", "uart1_rx", NULL, "gpio_128", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x0128, "d26", 129, 3, "i2c2_sda", "uart1_tx", NULL, "gpio_129", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x012a, "w27", 130, 3, "i2c3_scl", NULL, NULL, "gpio_130", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x012c, "y27", 131, 3, "i2c3_sda", NULL, NULL, "gpio_131", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x012e, "ag21", 132, 3, "i2c4_scl", NULL, NULL, "gpio_132", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x0130, "ah22", 133, 3, "i2c4_sda", NULL, NULL, "gpio_133", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x0132, "af22", 134, 3, "mcspi1_clk", NULL, NULL, "gpio_134", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x0134, "ae22", 135, 3, "mcspi1_somi", NULL, NULL, "gpio_135", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x0136, "ag22", 136, 3, "mcspi1_simo", NULL, NULL, "gpio_136", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x0138, "ae23", 137, 3, "mcspi1_cs0", NULL, NULL, "gpio_137", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x013a, "af23", 138, 3, "mcspi1_cs1", "uart1_rx", NULL, "gpio_138", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x013c, "ag23", 139, 3, "mcspi1_cs2", "uart1_cts", "slimbus2_clock", "gpio_139", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x013e, "ah23", 140, 3, "mcspi1_cs3", "uart1_rts", "slimbus2_data", "gpio_140", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x0140, "f27", 141, 3, "uart3_cts_rctx", "uart1_tx", NULL, "gpio_141", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x0142, "f28", 142, 3, "uart3_rts_sd", NULL, NULL, "gpio_142", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x0144, "g27", 143, 3, "uart3_rx_irrx", "dmtimer8_pwm_evt", NULL, "gpio_143", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x0146, "g28", 144, 3, "uart3_tx_irtx", "dmtimer9_pwm_evt", NULL, "gpio_144", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x0148, "ae5", 145, 3, "sdmmc5_clk", "mcspi2_clk", "usbc1_icusb_dp", "gpio_145", NULL, "sdmmc2_clk", NULL, "safe_mode"), + _PINDEF(0x014a, "af5", 146, 3, "sdmmc5_cmd", "mcspi2_simo", "usbc1_icusb_dm", "gpio_146", NULL, "sdmmc2_cmd", NULL, "safe_mode"), + _PINDEF(0x014c, "ae4", 147, 3, "sdmmc5_dat0", "mcspi2_somi", "usbc1_icusb_rcv", "gpio_147", NULL, "sdmmc2_dat0", NULL, "safe_mode"), + _PINDEF(0x014e, "af4", 148, 3, "sdmmc5_dat1", NULL, "usbc1_icusb_txen", "gpio_148", NULL, "sdmmc2_dat1", NULL, "safe_mode"), + _PINDEF(0x0150, "ag3", 149, 3, "sdmmc5_dat2", "mcspi2_cs1", NULL, "gpio_149", NULL, "sdmmc2_dat2", NULL, "safe_mode"), + _PINDEF(0x0152, "af3", 150, 3, "sdmmc5_dat3", "mcspi2_cs0", NULL, "gpio_150", NULL, "sdmmc2_dat3", NULL, "safe_mode"), + _PINDEF(0x0154, "ae21", 151, 3, "mcspi4_clk", "sdmmc4_clk", "kpd_col6", "gpio_151", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x0156, "af20", 152, 3, "mcspi4_simo", "sdmmc4_cmd", "kpd_col7", "gpio_152", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x0158, "af21", 153, 3, "mcspi4_somi", "sdmmc4_dat0", "kpd_row6", "gpio_153", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x015a, "ae20", 154, 3, "mcspi4_cs0", "sdmmc4_dat3", "kpd_row7", "gpio_154", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x015c, "ag20", 155, 3, "uart4_rx", "sdmmc4_dat2", "kpd_row8", "gpio_155", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x015e, "ah19", 156, 3, "uart4_tx", "sdmmc4_dat1", "kpd_col8", "gpio_156", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x0160, "ag12", 157, 3, "usbb2_ulpitll_clk", "usbb2_ulpiphy_clk", "sdmmc4_cmd", "gpio_157", "hsi2_cawake", NULL, NULL, "safe_mode"), + _PINDEF(0x0162, "af12", 158, 3, "usbb2_ulpitll_stp", "usbb2_ulpiphy_stp", "sdmmc4_clk", "gpio_158", "hsi2_cadata", "dispc2_data23", NULL, "safe_mode"), + _PINDEF(0x0164, "ae12", 159, 3, "usbb2_ulpitll_dir", "usbb2_ulpiphy_dir", "sdmmc4_dat0", "gpio_159", "hsi2_caflag", "dispc2_data22", NULL, "safe_mode"), + _PINDEF(0x0166, "ag13", 160, 3, "usbb2_ulpitll_nxt", "usbb2_ulpiphy_nxt", "sdmmc4_dat1", "gpio_160", "hsi2_acready", "dispc2_data21", NULL, "safe_mode"), + _PINDEF(0x0168, "ae11", 161, 3, "usbb2_ulpitll_dat0", "usbb2_ulpiphy_dat0", "sdmmc4_dat2", "gpio_161", "hsi2_acwake", "dispc2_data20", "usbb2_mm_txen", "safe_mode"), + _PINDEF(0x016a, "af11", 162, 3, "usbb2_ulpitll_dat1", "usbb2_ulpiphy_dat1", "sdmmc4_dat3", "gpio_162", "hsi2_acdata", "dispc2_data19", "usbb2_mm_txdat", "safe_mode"), + _PINDEF(0x016c, "ag11", 163, 3, "usbb2_ulpitll_dat2", "usbb2_ulpiphy_dat2", "sdmmc3_dat2", "gpio_163", "hsi2_acflag", "dispc2_data18", "usbb2_mm_txse0", "safe_mode"), + _PINDEF(0x016e, "ah11", 164, 3, "usbb2_ulpitll_dat3", "usbb2_ulpiphy_dat3", "sdmmc3_dat1", "gpio_164", "hsi2_caready", "dispc2_data15", "rfbi_data15", "safe_mode"), + _PINDEF(0x0170, "ae10", 165, 3, "usbb2_ulpitll_dat4", "usbb2_ulpiphy_dat4", "sdmmc3_dat0", "gpio_165", "mcspi3_somi", "dispc2_data14", "rfbi_data14", "safe_mode"), + _PINDEF(0x0172, "af10", 166, 3, "usbb2_ulpitll_dat5", "usbb2_ulpiphy_dat5", "sdmmc3_dat3", "gpio_166", "mcspi3_cs0", "dispc2_data13", "rfbi_data13", "safe_mode"), + _PINDEF(0x0174, "ag10", 167, 3, "usbb2_ulpitll_dat6", "usbb2_ulpiphy_dat6", "sdmmc3_cmd", "gpio_167", "mcspi3_simo", "dispc2_data12", "rfbi_data12", "safe_mode"), + _PINDEF(0x0176, "ae9", 168, 3, "usbb2_ulpitll_dat7", "usbb2_ulpiphy_dat7", "sdmmc3_clk", "gpio_168", "mcspi3_clk", "dispc2_data11", "rfbi_data11", "safe_mode"), + _PINDEF(0x0178, "af13", 169, 3, "usbb2_hsic_data", NULL, NULL, "gpio_169", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x017a, "ae13", 170, 3, "usbb2_hsic_strobe", NULL, NULL, "gpio_170", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x017c, "g26", 171, 3, "kpd_col3", "kpd_col0", NULL, "gpio_171", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x017e, "g25", 172, 3, "kpd_col4", "kpd_col1", NULL, "gpio_172", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x0180, "h26", 173, 3, "kpd_col5", "kpd_col2", NULL, "gpio_173", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x0182, "h25", 174, 3, "kpd_col0", "kpd_col3", NULL, "gpio_174", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x0184, "j27", 0, 0, "kpd_col1", "kpd_col4", NULL, "gpio_0", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x0186, "h27", 1, 3, "kpd_col2", "kpd_col5", NULL, "gpio_1", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x0188, "j26", 175, 3, "kpd_row3", "kpd_row0", NULL, "gpio_175", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x018a, "j25", 176, 3, "kpd_row4", "kpd_row1", NULL, "gpio_176", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x018c, "k26", 177, 3, "kpd_row5", "kpd_row2", NULL, "gpio_177", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x018e, "k25", 178, 3, "kpd_row0", "kpd_row3", NULL, "gpio_178", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x0190, "l27", 2, 3, "kpd_row1", "kpd_row4", NULL, "gpio_2", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x0192, "k27", 3, 3, "kpd_row2", "kpd_row5", NULL, "gpio_3", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x0194, "c3", 0, 0, "usba0_otg_ce", NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PINDEF(0x0196, "b5", 0, 0, "usba0_otg_dp", "uart3_rx_irrx", "uart2_rx", NULL, NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x0198, "b4", 0, 0, "usba0_otg_dm", "uart3_tx_irtx", "uart2_tx", NULL, NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x019a, "aa28", 181, 3, "fref_clk1_out", NULL, NULL, "gpio_181", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x019c, "y28", 182, 3, "fref_clk2_out", NULL, NULL, "gpio_182", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x019e, "ae6", 0, 0, "sys_nirq1", NULL, NULL, NULL, NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x01a0, "af6", 183, 3, "sys_nirq2", NULL, NULL, "gpio_183", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x01a2, "f26", 184, 3, "sys_boot0", NULL, NULL, "gpio_184", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x01a4, "e27", 185, 3, "sys_boot1", NULL, NULL, "gpio_185", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x01a6, "e26", 186, 3, "sys_boot2", NULL, NULL, "gpio_186", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x01a8, "e25", 187, 3, "sys_boot3", NULL, NULL, "gpio_187", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x01aa, "d28", 188, 3, "sys_boot4", NULL, NULL, "gpio_188", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x01ac, "d27", 189, 3, "sys_boot5", NULL, NULL, "gpio_189", NULL, NULL, NULL, "safe_mode"), + _PINDEF(0x01ae, "m2", 11, 3, "dpm_emu0", NULL, NULL, "gpio_11", NULL, NULL, "hw_dbg0", "safe_mode"), + _PINDEF(0x01b0, "n2", 12, 3, "dpm_emu1", NULL, NULL, "gpio_12", NULL, NULL, "hw_dbg1", "safe_mode"), + _PINDEF(0x01b2, "p2", 13, 3, "dpm_emu2", "usba0_ulpiphy_clk", NULL, "gpio_13", NULL, "dispc2_fid", "hw_dbg2", "safe_mode"), + _PINDEF(0x01b4, "v1", 14, 3, "dpm_emu3", "usba0_ulpiphy_stp", NULL, "gpio_14", "rfbi_data10", "dispc2_data10", "hw_dbg3", "safe_mode"), + _PINDEF(0x01b6, "v2", 15, 3, "dpm_emu4", "usba0_ulpiphy_dir", NULL, "gpio_15", "rfbi_data9", "dispc2_data9", "hw_dbg4", "safe_mode"), + _PINDEF(0x01b8, "w1", 16, 3, "dpm_emu5", "usba0_ulpiphy_nxt", NULL, "gpio_16", "rfbi_te_vsync0", "dispc2_data16", "hw_dbg5", "safe_mode"), + _PINDEF(0x01ba, "w2", 17, 3, "dpm_emu6", "usba0_ulpiphy_dat0", "uart3_tx_irtx", "gpio_17", "rfbi_hsync0", "dispc2_data17", "hw_dbg6", "safe_mode"), + _PINDEF(0x01bc, "w3", 18, 3, "dpm_emu7", "usba0_ulpiphy_dat1", "uart3_rx_irrx", "gpio_18", "rfbi_cs0", "dispc2_hsync", "hw_dbg7", "safe_mode"), + _PINDEF(0x01be, "w4", 19, 3, "dpm_emu8", "usba0_ulpiphy_dat2", "uart3_rts_sd", "gpio_19", "rfbi_re", "dispc2_pclk", "hw_dbg8", "safe_mode"), + _PINDEF(0x01c0, "y2", 20, 3, "dpm_emu9", "usba0_ulpiphy_dat3", "uart3_cts_rctx", "gpio_20", "rfbi_we", "dispc2_vsync", "hw_dbg9", "safe_mode"), + _PINDEF(0x01c2, "y3", 21, 3, "dpm_emu10", "usba0_ulpiphy_dat4", NULL, "gpio_21", "rfbi_a0", "dispc2_de", "hw_dbg10", "safe_mode"), + _PINDEF(0x01c4, "y4", 22, 3, "dpm_emu11", "usba0_ulpiphy_dat5", NULL, "gpio_22", "rfbi_data8", "dispc2_data8", "hw_dbg11", "safe_mode"), + _PINDEF(0x01c6, "aa1", 23, 3, "dpm_emu12", "usba0_ulpiphy_dat6", NULL, "gpio_23", "rfbi_data7", "dispc2_data7", "hw_dbg12", "safe_mode"), + _PINDEF(0x01c8, "aa2", 24, 3, "dpm_emu13", "usba0_ulpiphy_dat7", NULL, "gpio_24", "rfbi_data6", "dispc2_data6", "hw_dbg13", "safe_mode"), + _PINDEF(0x01ca, "aa3", 25, 3, "dpm_emu14", "sys_drm_msecure", "uart1_rx", "gpio_25", "rfbi_data5", "dispc2_data5", "hw_dbg14", "safe_mode"), + _PINDEF(0x01cc, "aa4", 26, 3, "dpm_emu15", "sys_secure_indicator", NULL, "gpio_26", "rfbi_data4", "dispc2_data4", "hw_dbg15", "safe_mode"), + _PINDEF(0x01ce, "ab2", 27, 3, "dpm_emu16", "dmtimer8_pwm_evt", "dsi1_te0", "gpio_27", "rfbi_data3", "dispc2_data3", "hw_dbg16", "safe_mode"), + _PINDEF(0x01d0, "ab3", 28, 3, "dpm_emu17", "dmtimer9_pwm_evt", "dsi1_te1", "gpio_28", "rfbi_data2", "dispc2_data2", "hw_dbg17", "safe_mode"), + _PINDEF(0x01d2, "ab4", 190, 3, "dpm_emu18", "dmtimer10_pwm_evt", "dsi2_te0", "gpio_190", "rfbi_data1", "dispc2_data1", "hw_dbg18", "safe_mode"), + _PINDEF(0x01d4, "ac4", 191, 3, "dpm_emu19", "dmtimer11_pwm_evt", "dsi2_te1", "gpio_191", "rfbi_data0", "dispc2_data0", "hw_dbg19", "safe_mode"), + { .ballname = NULL }, +}; + +const struct ti_scm_device ti_scm_dev = { + .padconf_muxmode_mask = CONTROL_PADCONF_MUXMODE_MASK, + .padconf_sate_mask = CONTROL_PADCONF_SATE_MASK, + .padstate = (struct ti_scm_padstate *) &ti_padstate_devmap, + .padconf = (struct ti_scm_padconf *) &ti_padconf_devmap, +}; + +int +ti_scm_padconf_set_gpioflags(uint32_t gpio, uint32_t flags) +{ + unsigned int state = 0; + /* First the SCM driver needs to be told to put the pad into GPIO mode */ + if (flags & GPIO_PIN_OUTPUT) + state = PADCONF_PIN_OUTPUT; + else if (flags & GPIO_PIN_INPUT) { + if (flags & GPIO_PIN_PULLUP) + state = PADCONF_PIN_INPUT_PULLUP; + else if (flags & GPIO_PIN_PULLDOWN) + state = PADCONF_PIN_INPUT_PULLDOWN; + else + state = PADCONF_PIN_INPUT; + } + return ti_scm_padconf_set_gpiomode(gpio, state); +} + +void +ti_scm_padconf_get_gpioflags(uint32_t gpio, uint32_t *flags) +{ + unsigned int state; + /* Get the current pin state */ + if (ti_scm_padconf_get_gpiomode(gpio, &state) != 0) + *flags = 0; + else { + switch (state) { + case PADCONF_PIN_OUTPUT: + *flags = GPIO_PIN_OUTPUT; + break; + case PADCONF_PIN_INPUT: + *flags = GPIO_PIN_INPUT; + break; + case PADCONF_PIN_INPUT_PULLUP: + *flags = GPIO_PIN_INPUT | GPIO_PIN_PULLUP; + break; + case PADCONF_PIN_INPUT_PULLDOWN: + *flags = GPIO_PIN_INPUT | GPIO_PIN_PULLDOWN; + break; + default: + *flags = 0; + break; + } + } +} + diff --git a/sys/arm/ti/omap4/omap4_smc.h b/sys/arm/ti/omap4/omap4_smc.h new file mode 100644 index 000000000000..11ff8533c89c --- /dev/null +++ b/sys/arm/ti/omap4/omap4_smc.h @@ -0,0 +1,52 @@ +/*- + * Copyright (c) 2012 Olivier Houchard. 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 ``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 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 OMAP4_SMC_H_ +#define OMAP4_SMC_H_ +/* Define the various function IDs used by the OMAP4 */ +#define L2CACHE_WRITE_DEBUG_REG 0x100 +#define L2CACHE_CLEAN_INV_RANG 0x101 +#define L2CACHE_ENABLE_L2 0x102 +#define READ_AUX_CORE_REGS 0x103 +#define MODIFY_AUX_CORE_0 0x104 +#define WRITE_AUX_CORE_1 0x105 +#define READ_WKG_CTRL_REG 0x106 +#define CLEAR_WKG_CTRL_REG 0x107 +#define SET_POWER_STATUS_REG 0x108 +#define WRITE_AUXCTRL_REG 0x109 +#define LOCKDOWN_TLB 0x10a +#define SELECT_TLB_ENTRY_FOR_WRITE 0x10b +#define READ_TLB_VA_ENTRY 0x10c +#define WRITE_TLB_VA_ENTRY 0x10d +#define READ_TLB_PA_ENTRY 0x10e +#define WRITE_TLB_PA_ENTRY 0x10f +#define READ_TLB_ATTR_ENTRY 0x110 +#define WRITE_TLB_ATTR_ENTRY 0x111 +#define WRITE_LATENCY_CTRL_REG 0x112 +#define WRITE_PREFETCH_CTRL_REG 0x113 +#endif /* OMAP4_SMC_H_ */ diff --git a/sys/arm/ti/omap4/omap4var.h b/sys/arm/ti/omap4/omap4var.h new file mode 100644 index 000000000000..8246e765bc85 --- /dev/null +++ b/sys/arm/ti/omap4/omap4var.h @@ -0,0 +1,91 @@ +/*- + * 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. 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 _OMAP4VAR_H_ +#define _OMAP4VAR_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + + +void omap4_mask_all_intr(void); +void omap4_post_filter_intr(void *arg); + +struct omap4_softc { + device_t sc_dev; + bus_space_tag_t sc_iotag; + + /* Handles for the two generic interrupt controller (GIC) register mappings */ + bus_space_handle_t sc_gic_cpu_ioh; + bus_space_handle_t sc_gic_dist_ioh; + + /* Handle for the PL310 L2 cache controller */ + bus_space_handle_t sc_pl310_ioh; + + /* Handle for the global and provate timer register set in the Cortex core */ + bus_space_handle_t sc_prv_timer_ioh; + bus_space_handle_t sc_gbl_timer_ioh; + + /* SCM access */ + struct resource *sc_scm_mem; + int sc_scm_rid; +}; + +struct omap4_intr_conf { + int num; + unsigned int priority; + unsigned int target_cpu; +}; + +int omap4_setup_intr_controller(device_t dev, + const struct omap4_intr_conf *irqs); +int omap4_setup_gic_cpu(unsigned int prio_mask); + +void omap4_init_timer(device_t dev); + +int omap4_setup_l2cache_controller(struct omap4_softc *sc); +void omap4_smc_call(uint32_t fn, uint32_t arg); + +#endif /* _OMAP4VAR_H_ */ diff --git a/sys/arm/ti/omap4/pandaboard/files.pandaboard b/sys/arm/ti/omap4/pandaboard/files.pandaboard new file mode 100644 index 000000000000..f658ffb3dc7b --- /dev/null +++ b/sys/arm/ti/omap4/pandaboard/files.pandaboard @@ -0,0 +1,3 @@ +# $FreeBSD$ + +arm/ti/omap4/pandaboard/pandaboard.c standard diff --git a/sys/arm/ti/omap4/pandaboard/pandaboard.c b/sys/arm/ti/omap4/pandaboard/pandaboard.c new file mode 100644 index 000000000000..043f2a0a65e6 --- /dev/null +++ b/sys/arm/ti/omap4/pandaboard/pandaboard.c @@ -0,0 +1,210 @@ +/*- + * 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. + * 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. + */ +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +/* Registers in the SCRM that control the AUX clocks */ +#define SCRM_ALTCLKSRC (0x110) +#define SCRM_AUXCLK0 (0x0310) +#define SCRM_AUXCLK1 (0x0314) +#define SCRM_AUXCLK2 (0x0318) +#define SCRM_AUXCLK3 (0x031C) + +/* Some of the GPIO register set */ +#define GPIO1_OE (0x0134) +#define GPIO1_CLEARDATAOUT (0x0190) +#define GPIO1_SETDATAOUT (0x0194) +#define GPIO2_OE (0x0134) +#define GPIO2_CLEARDATAOUT (0x0190) +#define GPIO2_SETDATAOUT (0x0194) + +/* Some of the PADCONF register set */ +#define CONTROL_WKUP_PAD0_FREF_CLK3_OUT (0x058) +#define CONTROL_CORE_PAD1_KPD_COL2 (0x186) +#define CONTROL_CORE_PAD0_GPMC_WAIT1 (0x08C) + +#define REG_WRITE32(r, x) *((volatile uint32_t*)(r)) = (uint32_t)(x) +#define REG_READ32(r) *((volatile uint32_t*)(r)) + +#define REG_WRITE16(r, x) *((volatile uint16_t*)(r)) = (uint16_t)(x) +#define REG_READ16(r) *((volatile uint16_t*)(r)) + +/** + * usb_hub_init - initialises and resets the external USB hub + * + * The USB hub needs to be held in reset while the power is being applied + * and the reference clock is enabled at 19.2MHz. The following is the + * layout of the USB hub taken from the Pandaboard reference manual. + * + * + * .-------------. .--------------. .----------------. + * | OMAP4430 | | USB3320C | | LAN9514 | + * | | | | | USB Hub / Eth | + * | CLK | <------ | CLKOUT | | | + * | STP | ------> | STP | | | + * | DIR | <------ | DIR | | | + * | NXT | <------ | NXT | | | + * | DAT0 | <-----> | DAT0 | | | + * | DAT1 | <-----> | DAT1 DP | <-----> | DP | + * | DAT2 | <-----> | DAT2 DM | <-----> | DM | + * | DAT3 | <-----> | DAT3 | | | + * | DAT4 | <-----> | DAT4 | | | + * | DAT5 | <-----> | DAT5 | +----> | N_RESET | + * | DAT6 | <-----> | DAT6 | | | | + * | DAT7 | <-----> | DAT7 | | | | + * | | | | | +-> | VDD33IO | + * | AUX_CLK3 | ------> | REFCLK | | +-> | VDD33A | + * | | | | | | | | + * | GPIO_62 | --+---> | RESET | | | | | + * | | | | | | | | | + * | | | '--------------' | | '----------------' + * | | | .--------------. | | + * | | '---->| VOLT CONVERT |--' | + * | | '--------------' | + * | | | + * | | .--------------. | + * | GPIO_1 | ------> | TPS73633 |-----' + * | | '--------------' + * '-------------' + * + * + * RETURNS: + * nothing. + */ +static void +usb_hub_init(void) +{ + bus_space_handle_t scrm_addr, gpio1_addr, gpio2_addr, scm_addr; + + if (bus_space_map(fdtbus_bs_tag, OMAP44XX_SCRM_HWBASE, + OMAP44XX_SCRM_SIZE, 0, &scrm_addr) != 0) + panic("Couldn't map SCRM registers"); + if (bus_space_map(fdtbus_bs_tag, OMAP44XX_GPIO1_HWBASE, + OMAP44XX_GPIO1_SIZE, 0, &gpio1_addr) != 0) + panic("Couldn't map GPIO1 registers"); + if (bus_space_map(fdtbus_bs_tag, OMAP44XX_GPIO2_HWBASE, + OMAP44XX_GPIO2_SIZE, 0, &gpio2_addr) != 0) + panic("Couldn't map GPIO2 registers"); + if (bus_space_map(fdtbus_bs_tag, OMAP44XX_SCM_PADCONF_HWBASE, + OMAP44XX_SCM_PADCONF_SIZE, 0, &scm_addr) != 0) + panic("Couldn't map SCM Padconf registers"); + + + + /* Need to set FREF_CLK3_OUT to 19.2 MHz and pump it out on pin GPIO_WK31. + * We know the SYS_CLK is 38.4Mhz and therefore to get the needed 19.2Mhz, + * just use a 2x divider and ensure the SYS_CLK is used as the source. + */ + REG_WRITE32(scrm_addr + SCRM_AUXCLK3, (1 << 16) | /* Divider of 2 */ + (0 << 1) | /* Use the SYS_CLK as the source */ + (1 << 8)); /* Enable the clock */ + + /* Enable the clock out to the pin (GPIO_WK31). + * muxmode=fref_clk3_out, pullup/down=disabled, input buffer=disabled, + * wakeup=disabled. + */ + REG_WRITE16(scm_addr + CONTROL_WKUP_PAD0_FREF_CLK3_OUT, 0x0000); + + + /* Disable the power to the USB hub, drive GPIO1 low */ + REG_WRITE32(gpio1_addr + GPIO1_OE, REG_READ32(gpio1_addr + + GPIO1_OE) & ~(1UL << 1)); + REG_WRITE32(gpio1_addr + GPIO1_CLEARDATAOUT, (1UL << 1)); + REG_WRITE16(scm_addr + CONTROL_CORE_PAD1_KPD_COL2, 0x0003); + + + /* Reset the USB PHY and Hub using GPIO_62 */ + REG_WRITE32(gpio2_addr + GPIO2_OE, + REG_READ32(gpio2_addr + GPIO2_OE) & ~(1UL << 30)); + REG_WRITE32(gpio2_addr + GPIO2_CLEARDATAOUT, (1UL << 30)); + REG_WRITE16(scm_addr + CONTROL_CORE_PAD0_GPMC_WAIT1, 0x0003); + DELAY(10); + REG_WRITE32(gpio2_addr + GPIO2_SETDATAOUT, (1UL << 30)); + + + /* Enable power to the hub (GPIO_1) */ + REG_WRITE32(gpio1_addr + GPIO1_SETDATAOUT, (1UL << 1)); + bus_space_unmap(fdtbus_bs_tag, scrm_addr, OMAP44XX_SCRM_SIZE); + bus_space_unmap(fdtbus_bs_tag, gpio1_addr, OMAP44XX_GPIO1_SIZE); + bus_space_unmap(fdtbus_bs_tag, gpio2_addr, OMAP44XX_GPIO2_SIZE); + bus_space_unmap(fdtbus_bs_tag, scm_addr, OMAP44XX_SCM_PADCONF_SIZE); +} + +/** + * board_init - initialises the pandaboard + * @dummy: ignored + * + * This function is called before any of the driver are initialised, which is + * annoying because it means we can't use the SCM, PRCM and GPIO modules which + * would really be useful. + * + * So we don't have: + * - any drivers + * - no interrupts + * + * What we do have: + * - virt/phys mappings from the devmap (see omap4.c) + * - + * + * + * So we are hamstrung without the useful drivers and we have to go back to + * direct register manupulation. Luckly we don't have to do to much, basically + * just setup the usb hub/ethernet. + * + */ +static void +board_init(void *dummy) +{ + /* Initialise the USB phy and hub */ + usb_hub_init(); + + /* + * XXX Board identification e.g. read out from FPGA or similar should + * go here + */ +} + +SYSINIT(board_init, SI_SUB_CPU, SI_ORDER_THIRD, board_init, NULL); diff --git a/sys/arm/ti/omap4/pandaboard/std.pandaboard b/sys/arm/ti/omap4/pandaboard/std.pandaboard new file mode 100644 index 000000000000..6a65f66bb13d --- /dev/null +++ b/sys/arm/ti/omap4/pandaboard/std.pandaboard @@ -0,0 +1,4 @@ +# $FreeBSD$ + +include "../ti/omap4/std.omap4" +files "../ti/omap4/pandaboard/files.pandaboard" diff --git a/sys/arm/ti/omap4/std.omap4 b/sys/arm/ti/omap4/std.omap4 new file mode 100644 index 000000000000..056a8238f0de --- /dev/null +++ b/sys/arm/ti/omap4/std.omap4 @@ -0,0 +1,21 @@ +# Omap4430 generic configuration +#$FreeBSD$ +files "../ti/omap4/files.omap4" +include "../ti/std.ti" +makeoption ARM_LITTLE_ENDIAN + +# Physical memory starts at 0x80000000. We assume images are loaded at +# 0x80200000, e.g. from u-boot with 'fatload mmc 0 0x80200000 kernel.bin' +# +# +options PHYSADDR=0x80000000 +options KERNPHYSADDR=0x80200000 +makeoptions KERNPHYSADDR=0x80200000 +options KERNVIRTADDR=0xc0200000 # Used in ldscript.arm +makeoptions KERNVIRTADDR=0xc0200000 + +options STARTUP_PAGETABLE_ADDR=0x80000000 + +options SOC_OMAP4 + +options ARM_L2_PIPT diff --git a/sys/arm/ti/std.ti b/sys/arm/ti/std.ti new file mode 100644 index 000000000000..f3bc4fbf9fc1 --- /dev/null +++ b/sys/arm/ti/std.ti @@ -0,0 +1,5 @@ +# $FreeBSD$ + +cpu CPU_CORTEXA + +files "../ti/files.ti" diff --git a/sys/arm/ti/ti_cpuid.c b/sys/arm/ti/ti_cpuid.c new file mode 100644 index 000000000000..16de8aa32bb2 --- /dev/null +++ b/sys/arm/ti/ti_cpuid.c @@ -0,0 +1,285 @@ +/*- + * 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. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#define OMAP4_STD_FUSE_DIE_ID_0 0x2200 +#define OMAP4_ID_CODE 0x2204 +#define OMAP4_STD_FUSE_DIE_ID_1 0x2208 +#define OMAP4_STD_FUSE_DIE_ID_2 0x220C +#define OMAP4_STD_FUSE_DIE_ID_3 0x2210 +#define OMAP4_STD_FUSE_PROD_ID_0 0x2214 +#define OMAP4_STD_FUSE_PROD_ID_1 0x2218 + +#define OMAP3_ID_CODE 0xA204 + +static uint32_t chip_revision = 0xffffffff; + +/** + * ti_revision - Returns the revision number of the device + * + * Simply returns an identifier for the revision of the chip we are running + * on. + * + * RETURNS + * A 32-bit identifier for the current chip + */ +uint32_t +ti_revision(void) +{ + return chip_revision; +} + +/** + * omap4_get_revision - determines omap4 revision + * + * Reads the registers to determine the revision of the chip we are currently + * running on. Stores the information in global variables. + * + * + */ +static void +omap4_get_revision(void) +{ + uint32_t id_code; + uint32_t revision; + uint32_t hawkeye; + bus_space_handle_t bsh; + + /* The chip revsion is read from the device identification registers and + * the JTAG (?) tap registers, which are located in address 0x4A00_2200 to + * 0x4A00_2218. This is part of the L4_CORE memory range and should have + * been mapped in by the machdep.c code. + * + * STD_FUSE_DIE_ID_0 0x4A00 2200 + * ID_CODE 0x4A00 2204 (this is the only one we need) + * STD_FUSE_DIE_ID_1 0x4A00 2208 + * STD_FUSE_DIE_ID_2 0x4A00 220C + * STD_FUSE_DIE_ID_3 0x4A00 2210 + * STD_FUSE_PROD_ID_0 0x4A00 2214 + * STD_FUSE_PROD_ID_1 0x4A00 2218 + */ + // id_code = REG_READ32(OMAP44XX_L4_CORE_VBASE + OMAP4_ID_CODE); + //FIXME Should we map somewhere else? + bus_space_map(fdtbus_bs_tag,OMAP44XX_L4_CORE_HWBASE, 0x4000, 0, &bsh); + id_code = bus_space_read_4(fdtbus_bs_tag, bsh, OMAP4_ID_CODE); + bus_space_unmap(fdtbus_bs_tag, bsh, 0x4000); + + hawkeye = ((id_code >> 12) & 0xffff); + revision = ((id_code >> 28) & 0xf); + + /* Apparently according to the linux code there were some ES2.0 samples that + * have the wrong id code and report themselves as ES1.0 silicon. So used + * the ARM cpuid to get the correct revision. + */ + if (revision == 0) { + id_code = cpufunc_id(); + revision = (id_code & 0xf) - 1; + } + + switch (hawkeye) { + case 0xB852: + if (revision == 0) + chip_revision = OMAP4430_REV_ES1_0; + else + chip_revision = OMAP4430_REV_ES2_0; + break; + case 0xB95C: + if (revision == 3) + chip_revision = OMAP4430_REV_ES2_1; + else if (revision == 4) + chip_revision = OMAP4430_REV_ES2_2; + else + chip_revision = OMAP4430_REV_ES2_3; + break; + default: + /* Default to the latest revision if we can't determine type */ + chip_revision = OMAP4430_REV_ES2_3; + break; + } + printf("Texas Instruments OMAP%04x Processor, Revision ES%u.%u\n", + OMAP_REV_DEVICE(chip_revision), OMAP_REV_MAJOR(chip_revision), + OMAP_REV_MINOR(chip_revision)); +} + +/** + * omap3_get_revision - determines omap3 revision + * + * Reads the registers to determine the revision of the chip we are currently + * running on. Stores the information in global variables. + * + * WARNING: This function currently only really works for OMAP3530 devices. + * + * + * + */ +static void +omap3_get_revision(void) +{ + uint32_t id_code; + uint32_t revision; + uint32_t hawkeye; + bus_space_handle_t bsh; + + /* The chip revsion is read from the device identification registers and + * the JTAG (?) tap registers, which are located in address 0x4A00_2200 to + * 0x4A00_2218. This is part of the L4_CORE memory range and should have + * been mapped in by the machdep.c code. + * + * CONTROL_IDCODE 0x4830 A204 (this is the only one we need) + * + * + */ + //id_code = REG_READ32(OMAP35XX_L4_WAKEUP_VBASE + OMAP3_ID_CODE); + bus_space_map(fdtbus_bs_tag,OMAP35XX_L4_WAKEUP_HWBASE, 0x10000, 0, &bsh); + id_code = bus_space_read_4(fdtbus_bs_tag, bsh, OMAP3_ID_CODE); + bus_space_unmap(fdtbus_bs_tag, bsh, 0x4000); + + hawkeye = ((id_code >> 12) & 0xffff); + revision = ((id_code >> 28) & 0xf); + + switch (hawkeye) { + case 0xB6D6: + chip_revision = OMAP3350_REV_ES1_0; + break; + case 0xB7AE: + if (revision == 1) + chip_revision = OMAP3530_REV_ES2_0; + else if (revision == 2) + chip_revision = OMAP3530_REV_ES2_1; + else if (revision == 3) + chip_revision = OMAP3530_REV_ES3_0; + else if (revision == 4) + chip_revision = OMAP3530_REV_ES3_1; + else if (revision == 7) + chip_revision = OMAP3530_REV_ES3_1_2; + break; + default: + /* Default to the latest revision if we can't determine type */ + chip_revision = OMAP3530_REV_ES3_1_2; + break; + } + printf("Texas Instruments OMAP%04x Processor, Revision ES%u.%u\n", + OMAP_REV_DEVICE(chip_revision), OMAP_REV_MAJOR(chip_revision), + OMAP_REV_MINOR(chip_revision)); +} + +static void +am335x_get_revision(void) +{ + uint32_t dev_feature; + uint8_t cpu_last_char; + bus_space_handle_t bsh; + + bus_space_map(fdtbus_bs_tag, AM335X_CONTROL_BASE, AM335X_CONTROL_SIZE, 0, &bsh); + chip_revision = bus_space_read_4(fdtbus_bs_tag, bsh, AM335X_CONTROL_DEVICE_ID); + dev_feature = bus_space_read_4(fdtbus_bs_tag, bsh, AM335X_CONTROL_DEV_FEATURE); + bus_space_unmap(fdtbus_bs_tag, bsh, AM335X_CONTROL_SIZE); + + switch (dev_feature) { + case 0x00FF0382: + cpu_last_char='2'; + break; + case 0x20FF0382: + cpu_last_char='4'; + break; + case 0x00FF0383: + cpu_last_char='6'; + break; + case 0x00FE0383: + cpu_last_char='7'; + break; + case 0x20FF0383: + cpu_last_char='8'; + break; + case 0x20FE0383: + cpu_last_char='9'; + break; + default: + cpu_last_char='x'; + } + + printf("Texas Instruments AM335%c Processor, Revision ES1.%u\n", + cpu_last_char, AM335X_DEVREV(chip_revision)); +} + +/** + * ti_cpu_ident - attempts to identify the chip we are running on + * @dummy: ignored + * + * This function is called before any of the driver are initialised, however + * the basic virt to phys maps have been setup in machdep.c so we can still + * access the required registers, we just have to use direct register reads + * and writes rather than going through the bus stuff. + * + * + */ +static void +ti_cpu_ident(void *dummy) +{ + switch(ti_chip()) { + case CHIP_OMAP_3: + omap3_get_revision(); + break; + case CHIP_OMAP_4: + omap4_get_revision(); + break; + case CHIP_AM335X: + am335x_get_revision(); + break; + default: + panic("Unknown chip type, fixme!\n"); + } +} + +SYSINIT(ti_cpu_ident, SI_SUB_CPU, SI_ORDER_SECOND, ti_cpu_ident, NULL); diff --git a/sys/arm/ti/ti_cpuid.h b/sys/arm/ti/ti_cpuid.h new file mode 100644 index 000000000000..f68f54ceecdc --- /dev/null +++ b/sys/arm/ti/ti_cpuid.h @@ -0,0 +1,77 @@ +/*- + * 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 AM335X_DEVREV(x) ((x) >> 28) + +#define CHIP_OMAP_3 0 +#define CHIP_OMAP_4 1 +#define CHIP_AM335X 2 + +static __inline int ti_chip(void) +{ +#if defined(SOC_OMAP4) + return CHIP_OMAP_4; +#elif defined(SOC_OMAP3) + return CHIP_OMAP_3; +#elif defined(SOC_TI_AM335X) + return CHIP_AM335X; +#else +# error Chip type not defined, ensure SOC_xxxx is defined +#endif +} + +uint32_t ti_revision(void); + +#endif /* _TI_CPUID_H_ */ diff --git a/sys/arm/ti/ti_edma3.c b/sys/arm/ti/ti_edma3.c new file mode 100644 index 000000000000..b7570103d7b5 --- /dev/null +++ b/sys/arm/ti/ti_edma3.c @@ -0,0 +1,424 @@ +/*- + * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/ + * 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. + * 3. Neither the name of authors nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * 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 + +#define TI_EDMA3_NUM_TCS 3 +#define TI_EDMA3_NUM_IRQS 3 +#define TI_EDMA3_NUM_DMA_CHS 64 +#define TI_EDMA3_NUM_QDMA_CHS 8 + +#define TI_EDMA3CC_PID 0x000 +#define TI_EDMA3CC_DCHMAP(p) (0x100 + ((p)*4)) +#define TI_EDMA3CC_DMAQNUM(n) (0x240 + ((n)*4)) +#define TI_EDMA3CC_QDMAQNUM 0x260 +#define TI_EDMA3CC_EMCR 0x308 +#define TI_EDMA3CC_EMCRH 0x30C +#define TI_EDMA3CC_QEMCR 0x314 +#define TI_EDMA3CC_CCERR 0x318 +#define TI_EDMA3CC_CCERRCLR 0x31C +#define TI_EDMA3CC_DRAE(p) (0x340 + ((p)*8)) +#define TI_EDMA3CC_DRAEH(p) (0x344 + ((p)*8)) +#define TI_EDMA3CC_QRAE(p) (0x380 + ((p)*4)) +#define TI_EDMA3CC_S_ESR(p) (0x2010 + ((p)*0x200)) +#define TI_EDMA3CC_S_ESRH(p) (0x2014 + ((p)*0x200)) +#define TI_EDMA3CC_S_SECR(p) (0x2040 + ((p)*0x200)) +#define TI_EDMA3CC_S_SECRH(p) (0x2044 + ((p)*0x200)) +#define TI_EDMA3CC_S_EESR(p) (0x2030 + ((p)*0x200)) +#define TI_EDMA3CC_S_EESRH(p) (0x2034 + ((p)*0x200)) +#define TI_EDMA3CC_S_IESR(p) (0x2060 + ((p)*0x200)) +#define TI_EDMA3CC_S_IESRH(p) (0x2064 + ((p)*0x200)) +#define TI_EDMA3CC_S_IPR(p) (0x2068 + ((p)*0x200)) +#define TI_EDMA3CC_S_IPRH(p) (0x206C + ((p)*0x200)) +#define TI_EDMA3CC_S_QEESR(p) (0x208C + ((p)*0x200)) + +#define TI_EDMA3CC_PARAM_OFFSET 0x4000 +#define TI_EDMA3CC_OPT(p) (TI_EDMA3CC_PARAM_OFFSET + 0x0 + ((p)*0x20)) + +#define TI_EDMA3CC_DMAQNUM_SET(c,q) ((0x7 & (q)) << (((c) % 8) * 4)) +#define TI_EDMA3CC_DMAQNUM_CLR(c) (~(0x7 << (((c) % 8) * 4))) +#define TI_EDMA3CC_QDMAQNUM_SET(c,q) ((0x7 & (q)) << ((c) * 4)) +#define TI_EDMA3CC_QDMAQNUM_CLR(c) (~(0x7 << ((c) * 4))) + +#define TI_EDMA3CC_OPT_TCC_CLR (~(0x3F000)) +#define TI_EDMA3CC_OPT_TCC_SET(p) (((0x3F000 >> 12) & (p)) << 12) + +struct ti_edma3_softc { + device_t sc_dev; + struct resource * mem_res[TI_EDMA3_NUM_TCS+1]; + struct resource * irq_res[TI_EDMA3_NUM_IRQS]; + void *ih_cookie[TI_EDMA3_NUM_IRQS]; +}; + +static struct ti_edma3_softc *ti_edma3_sc = NULL; + +static struct resource_spec ti_edma3_mem_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { SYS_RES_MEMORY, 1, RF_ACTIVE }, + { SYS_RES_MEMORY, 2, RF_ACTIVE }, + { SYS_RES_MEMORY, 3, RF_ACTIVE }, + { -1, 0, 0 } +}; +static struct resource_spec ti_edma3_irq_spec[] = { + { SYS_RES_IRQ, 0, RF_ACTIVE }, + { SYS_RES_IRQ, 1, RF_ACTIVE }, + { SYS_RES_IRQ, 2, RF_ACTIVE }, + { -1, 0, 0 } +}; + +/* Read/Write macros */ +#define ti_edma3_cc_rd_4(reg) bus_read_4(ti_edma3_sc->mem_res[0], reg) +#define ti_edma3_cc_wr_4(reg, val) bus_write_4(ti_edma3_sc->mem_res[0], reg, val) +#define ti_edma3_tc_rd_4(c, reg) bus_read_4(ti_edma3_sc->mem_res[c+1], reg) +#define ti_edma3_tc_wr_4(c, reg, val) bus_write_4(ti_edma3_sc->mem_res[c+1], reg, val) + +static void ti_edma3_intr_comp(void *arg); +static void ti_edma3_intr_mperr(void *arg); +static void ti_edma3_intr_err(void *arg); + +static struct { + driver_intr_t *handler; + char * description; +} ti_edma3_intrs[TI_EDMA3_NUM_IRQS] = { + { ti_edma3_intr_comp, "EDMA Completion Interrupt" }, + { ti_edma3_intr_mperr, "EDMA Memory Protection Error Interrupt" }, + { ti_edma3_intr_err, "EDMA Error Interrupt" }, +}; + +static int +ti_edma3_probe(device_t dev) +{ + if (!ofw_bus_is_compatible(dev, "ti,edma")) + return (ENXIO); + + device_set_desc(dev, "TI EDMA Controller"); + return (0); +} + +static int +ti_edma3_attach(device_t dev) +{ + struct ti_edma3_softc *sc = device_get_softc(dev); + uint32_t reg; + int err; + int i; + + if (ti_edma3_sc) + return (ENXIO); + + ti_edma3_sc = sc; + sc->sc_dev = dev; + + /* Request the memory resources */ + err = bus_alloc_resources(dev, ti_edma3_mem_spec, sc->mem_res); + if (err) { + device_printf(dev, "Error: could not allocate mem resources\n"); + return (ENXIO); + } + + /* Request the IRQ resources */ + err = bus_alloc_resources(dev, ti_edma3_irq_spec, sc->irq_res); + if (err) { + device_printf(dev, "Error: could not allocate irq resources\n"); + return (ENXIO); + } + + /* Enable Channel Controller */ + ti_prcm_clk_enable(EDMA_TPCC_CLK); + + reg = ti_edma3_cc_rd_4(TI_EDMA3CC_PID); + + device_printf(dev, "EDMA revision %08x\n", reg); + + + /* Attach interrupt handlers */ + for (i = 0; i < TI_EDMA3_NUM_IRQS; ++i) { + err = bus_setup_intr(dev, sc->irq_res[i], INTR_TYPE_MISC | + INTR_MPSAFE, NULL, *ti_edma3_intrs[i].handler, + sc, &sc->ih_cookie[i]); + if (err) { + device_printf(dev, "could not setup %s\n", + ti_edma3_intrs[i].description); + return (err); + } + } + + return (0); +} + +static device_method_t ti_edma3_methods[] = { + DEVMETHOD(device_probe, ti_edma3_probe), + DEVMETHOD(device_attach, ti_edma3_attach), + {0, 0}, +}; + +static driver_t ti_edma3_driver = { + "ti_edma3", + ti_edma3_methods, + sizeof(struct ti_edma3_softc), +}; +static devclass_t ti_edma3_devclass; + +DRIVER_MODULE(ti_edma3, simplebus, ti_edma3_driver, ti_edma3_devclass, 0, 0); +MODULE_DEPEND(ti_edma3, ti_prcm, 1, 1, 1); + +static void +ti_edma3_intr_comp(void *arg) +{ + printf("%s: unimplemented\n", __func__); +} + +static void +ti_edma3_intr_mperr(void *arg) +{ + printf("%s: unimplemented\n", __func__); +} + +static void +ti_edma3_intr_err(void *arg) +{ + printf("%s: unimplemented\n", __func__); +} + +void +ti_edma3_init(unsigned int eqn) +{ + uint32_t reg; + int i; + + /* on AM335x Event queue 0 is always mapped to Transfer Controller 0, + * event queue 1 to TC2, etc. So we are asking PRCM to power on specific + * TC based on what event queue we need to initialize */ + ti_prcm_clk_enable(EDMA_TPTC0_CLK + eqn); + + /* Clear Event Missed Regs */ + ti_edma3_cc_wr_4(TI_EDMA3CC_EMCR, 0xFFFFFFFF); + ti_edma3_cc_wr_4(TI_EDMA3CC_EMCRH, 0xFFFFFFFF); + ti_edma3_cc_wr_4(TI_EDMA3CC_QEMCR, 0xFFFFFFFF); + + /* Clear Error Reg */ + ti_edma3_cc_wr_4(TI_EDMA3CC_CCERRCLR, 0xFFFFFFFF); + + /* Enable DMA channels 0-63 */ + ti_edma3_cc_wr_4(TI_EDMA3CC_DRAE(0), 0xFFFFFFFF); + ti_edma3_cc_wr_4(TI_EDMA3CC_DRAEH(0), 0xFFFFFFFF); + + for (i = 0; i < 64; i++) { + ti_edma3_cc_wr_4(TI_EDMA3CC_DCHMAP(i), i<<5); + } + + /* Initialize the DMA Queue Number Registers */ + for (i = 0; i < TI_EDMA3_NUM_DMA_CHS; i++) { + reg = ti_edma3_cc_rd_4(TI_EDMA3CC_DMAQNUM(i>>3)); + reg &= TI_EDMA3CC_DMAQNUM_CLR(i); + reg |= TI_EDMA3CC_DMAQNUM_SET(i, eqn); + ti_edma3_cc_wr_4(TI_EDMA3CC_DMAQNUM(i>>3), reg); + } + + /* Enable the QDMA Region access for all channels */ + ti_edma3_cc_wr_4(TI_EDMA3CC_QRAE(0), (1 << TI_EDMA3_NUM_QDMA_CHS) - 1); + + /*Initialize QDMA Queue Number Registers */ + for (i = 0; i < TI_EDMA3_NUM_QDMA_CHS; i++) { + reg = ti_edma3_cc_rd_4(TI_EDMA3CC_QDMAQNUM); + reg &= TI_EDMA3CC_QDMAQNUM_CLR(i); + reg |= TI_EDMA3CC_QDMAQNUM_SET(i, eqn); + ti_edma3_cc_wr_4(TI_EDMA3CC_QDMAQNUM, reg); + } +} + +#ifdef notyet +int +ti_edma3_enable_event_intr(unsigned int ch) +{ + uint32_t reg; + + if (ch >= TI_EDMA3_NUM_DMA_CHS) + return (EINVAL); + + if (ch < 32) { + ti_edma3_cc_wr_4(TI_EDMA3CC_S_IESR(0), 1 << ch); + } else { + ti_edma3_cc_wr_4(TI_EDMA3CC_S_IESRH(0), 1 << (ch - 32)); + } + return 0; +} +#endif + +int +ti_edma3_request_dma_ch(unsigned int ch, unsigned int tccn, unsigned int eqn) +{ + uint32_t reg; + + if (ch >= TI_EDMA3_NUM_DMA_CHS) + return (EINVAL); + + /* Enable the DMA channel in the DRAE/DRAEH registers */ + if (ch < 32) { + reg = ti_edma3_cc_rd_4(TI_EDMA3CC_DRAE(0)); + reg |= (0x01 << ch); + ti_edma3_cc_wr_4(TI_EDMA3CC_DRAE(0), reg); + } else { + reg = ti_edma3_cc_rd_4(TI_EDMA3CC_DRAEH(0)); + reg |= (0x01 << (ch - 32)); + ti_edma3_cc_wr_4(TI_EDMA3CC_DRAEH(0), reg); + } + + /* Associate DMA Channel to Event Queue */ + reg = ti_edma3_cc_rd_4(TI_EDMA3CC_DMAQNUM(ch >> 3)); + reg &= TI_EDMA3CC_DMAQNUM_CLR(ch); + reg |= TI_EDMA3CC_DMAQNUM_SET((ch), eqn); + ti_edma3_cc_wr_4(TI_EDMA3CC_DMAQNUM(ch >> 3), reg); + + /* Set TCC in corresponding PaRAM Entry */ + reg = ti_edma3_cc_rd_4(TI_EDMA3CC_OPT(ch)); + reg &= TI_EDMA3CC_OPT_TCC_CLR; + reg |= TI_EDMA3CC_OPT_TCC_SET(ch); + ti_edma3_cc_wr_4(TI_EDMA3CC_OPT(ch), reg); + + return 0; +} + +int +ti_edma3_request_qdma_ch(unsigned int ch, unsigned int tccn, unsigned int eqn) +{ + uint32_t reg; + + if (ch >= TI_EDMA3_NUM_DMA_CHS) + return (EINVAL); + + /* Enable the QDMA channel in the QRAE registers */ + reg = ti_edma3_cc_rd_4(TI_EDMA3CC_QRAE(0)); + reg |= (0x01 << ch); + ti_edma3_cc_wr_4(TI_EDMA3CC_QRAE(0), reg); + + /* Associate QDMA Channel to Event Queue */ + reg = ti_edma3_cc_rd_4(TI_EDMA3CC_QDMAQNUM); + reg |= TI_EDMA3CC_QDMAQNUM_SET(ch, eqn); + ti_edma3_cc_wr_4(TI_EDMA3CC_QDMAQNUM, reg); + + /* Set TCC in corresponding PaRAM Entry */ + reg = ti_edma3_cc_rd_4(TI_EDMA3CC_OPT(ch)); + reg &= TI_EDMA3CC_OPT_TCC_CLR; + reg |= TI_EDMA3CC_OPT_TCC_SET(ch); + ti_edma3_cc_wr_4(TI_EDMA3CC_OPT(ch), reg); + + return 0; +} + +int +ti_edma3_enable_transfer_manual(unsigned int ch) +{ + if (ch >= TI_EDMA3_NUM_DMA_CHS) + return (EINVAL); + + /* set corresponding bit in ESR/ESRH to set a event */ + if (ch < 32) { + ti_edma3_cc_wr_4(TI_EDMA3CC_S_ESR(0), 1 << ch); + } else { + ti_edma3_cc_wr_4(TI_EDMA3CC_S_ESRH(0), 1 << (ch - 32)); + } + + return 0; +} + +int +ti_edma3_enable_transfer_qdma(unsigned int ch) +{ + if (ch >= TI_EDMA3_NUM_QDMA_CHS) + return (EINVAL); + + /* set corresponding bit in QEESR to enable QDMA event */ + ti_edma3_cc_wr_4(TI_EDMA3CC_S_QEESR(0), (1 << ch)); + + return 0; +} + +int +ti_edma3_enable_transfer_event(unsigned int ch) +{ + if (ch >= TI_EDMA3_NUM_DMA_CHS) + return (EINVAL); + + /* Clear SECR(H) & EMCR(H) to clean any previous NULL request + * and set corresponding bit in EESR to enable DMA event */ + if(ch < 32) { + ti_edma3_cc_wr_4(TI_EDMA3CC_S_SECR(0), (1 << ch)); + ti_edma3_cc_wr_4(TI_EDMA3CC_EMCR, (1 << ch)); + ti_edma3_cc_wr_4(TI_EDMA3CC_S_EESR(0), (1 << ch)); + } else { + ti_edma3_cc_wr_4(TI_EDMA3CC_S_SECRH(0), 1 << (ch - 32)); + ti_edma3_cc_wr_4(TI_EDMA3CC_EMCRH, 1 << (ch - 32)); + ti_edma3_cc_wr_4(TI_EDMA3CC_S_EESRH(0), 1 << (ch - 32)); + } + + return 0; +} + +void +ti_edma3_param_write(unsigned int ch, struct ti_edma3cc_param_set *prs) +{ + bus_write_region_4(ti_edma3_sc->mem_res[0], TI_EDMA3CC_OPT(ch), + (uint32_t *) prs, 8); +} + +void +ti_edma3_param_read(unsigned int ch, struct ti_edma3cc_param_set *prs) +{ + bus_read_region_4(ti_edma3_sc->mem_res[0], TI_EDMA3CC_OPT(ch), + (uint32_t *) prs, 8); +} diff --git a/sys/arm/ti/ti_edma3.h b/sys/arm/ti/ti_edma3.h new file mode 100644 index 000000000000..f149c9c9e1b3 --- /dev/null +++ b/sys/arm/ti/ti_edma3.h @@ -0,0 +1,81 @@ +/*- + * 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. + * + * $FreeBSD$ + */ + +#ifndef _TI_EDMA3_H_ +#define _TI_EDMA3_H_ + +/* Direct Mapped EDMA3 Events */ +#define TI_EDMA3_EVENT_SDTXEVT1 2 +#define TI_EDMA3_EVENT_SDRXEVT1 3 +#define TI_EDMA3_EVENT_SDTXEVT0 24 +#define TI_EDMA3_EVENT_SDRXEVT0 25 + +struct ti_edma3cc_param_set { + struct { + uint32_t sam:1; /* Source address mode */ + uint32_t dam:1; /* Destination address mode */ + uint32_t syncdim:1; /* Transfer synchronization dimension */ + uint32_t static_set:1; /* Static Set */ + uint32_t :4; + uint32_t fwid:3; /* FIFO Width */ + uint32_t tccmode:1; /* Transfer complete code mode */ + uint32_t tcc:6; /* Transfer complete code */ + uint32_t :2; + uint32_t tcinten:1; /* Transfer complete interrupt enable */ + uint32_t itcinten:1; /* Intermediate xfer completion intr. ena */ + uint32_t tcchen:1; /* Transfer complete chaining enable */ + uint32_t itcchen:1; /* Intermediate xfer completion chaining ena */ + uint32_t privid:4; /* Privilege identification */ + uint32_t :3; + uint32_t priv:1; /* Privilege level */ + } opt; + uint32_t src; /* Channel Source Address */ + uint16_t acnt; /* Count for 1st Dimension */ + uint16_t bcnt; /* Count for 2nd Dimension */ + uint32_t dst; /* Channel Destination Address */ + int16_t srcbidx; /* Source B Index */ + int16_t dstbidx; /* Destination B Index */ + uint16_t link; /* Link Address */ + uint16_t bcntrld; /* BCNT Reload */ + int16_t srccidx; /* Source C Index */ + int16_t dstcidx; /* Destination C Index */ + uint16_t ccnt; /* Count for 3rd Dimension */ + uint16_t reserved; /* Reserved */ +}; + +void ti_edma3_init(unsigned int eqn); +int ti_edma3_request_dma_ch(unsigned int ch, unsigned int tccn, unsigned int eqn); +int ti_edma3_request_qdma_ch(unsigned int ch, unsigned int tccn, unsigned int eqn); +int ti_edma3_enable_transfer_manual(unsigned int ch); +int ti_edma3_enable_transfer_qdma(unsigned int ch); +int ti_edma3_enable_transfer_event(unsigned int ch); + +void ti_edma3_param_write(unsigned int ch, struct ti_edma3cc_param_set *prs); +void ti_edma3_param_read(unsigned int ch, struct ti_edma3cc_param_set *prs); + +#endif /* _TI_EDMA3_H_ */ diff --git a/sys/arm/ti/ti_gpio.c b/sys/arm/ti/ti_gpio.c new file mode 100644 index 000000000000..979326faf8a3 --- /dev/null +++ b/sys/arm/ti/ti_gpio.c @@ -0,0 +1,802 @@ +/*- + * 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. + */ + +/** + * Very simple GPIO (general purpose IO) driver module for TI OMAP SoC's. + * + * Currently this driver only does the basics, get a value on a pin & set a + * value on a pin. Hopefully over time I'll expand this to be a bit more generic + * and support interrupts and other various bits on the SoC can do ... in the + * meantime this is all you get. + * + * Beware the OMA datasheet(s) lists GPIO banks 1-6, whereas I've used 0-5 here + * in the code. + * + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include "gpio_if.h" + + /* Register definitions */ +#define TI_GPIO_REVISION 0x0000 +#define TI_GPIO_SYSCONFIG 0x0010 +#if defined(SOC_OMAP3) +#define TI_GPIO_REVISION 0x0000 +#define TI_GPIO_SYSCONFIG 0x0010 +#define TI_GPIO_SYSSTATUS 0x0014 +#define TI_GPIO_IRQSTATUS1 0x0018 +#define TI_GPIO_IRQENABLE1 0x001C +#define TI_GPIO_WAKEUPENABLE 0x0020 +#define TI_GPIO_IRQSTATUS2 0x0028 +#define TI_GPIO_IRQENABLE2 0x002C +#define TI_GPIO_CTRL 0x0030 +#define TI_GPIO_OE 0x0034 +#define TI_GPIO_DATAIN 0x0038 +#define TI_GPIO_DATAOUT 0x003C +#define TI_GPIO_LEVELDETECT0 0x0040 +#define TI_GPIO_LEVELDETECT1 0x0044 +#define TI_GPIO_RISINGDETECT 0x0048 +#define TI_GPIO_FALLINGDETECT 0x004C +#define TI_GPIO_DEBOUNCENABLE 0x0050 +#define TI_GPIO_DEBOUNCINGTIME 0x0054 +#define TI_GPIO_CLEARIRQENABLE1 0x0060 +#define TI_GPIO_SETIRQENABLE1 0x0064 +#define TI_GPIO_CLEARIRQENABLE2 0x0070 +#define TI_GPIO_SETIRQENABLE2 0x0074 +#define TI_GPIO_CLEARWKUENA 0x0080 +#define TI_GPIO_SETWKUENA 0x0084 +#define TI_GPIO_CLEARDATAOUT 0x0090 +#define TI_GPIO_SETDATAOUT 0x0094 +#elif defined(SOC_OMAP4) || defined(SOC_TI_AM335X) +#define TI_GPIO_IRQSTATUS_RAW_0 0x0024 +#define TI_GPIO_IRQSTATUS_RAW_1 0x0028 +#define TI_GPIO_IRQSTATUS_0 0x002C +#define TI_GPIO_IRQSTATUS_1 0x0030 +#define TI_GPIO_IRQSTATUS_SET_0 0x0034 +#define TI_GPIO_IRQSTATUS_SET_1 0x0038 +#define TI_GPIO_IRQSTATUS_CLR_0 0x003C +#define TI_GPIO_IRQSTATUS_CLR_1 0x0040 +#define TI_GPIO_IRQWAKEN_0 0x0044 +#define TI_GPIO_IRQWAKEN_1 0x0048 +#define TI_GPIO_SYSSTATUS 0x0114 +#define TI_GPIO_IRQSTATUS1 0x0118 +#define TI_GPIO_IRQENABLE1 0x011C +#define TI_GPIO_WAKEUPENABLE 0x0120 +#define TI_GPIO_IRQSTATUS2 0x0128 +#define TI_GPIO_IRQENABLE2 0x012C +#define TI_GPIO_CTRL 0x0130 +#define TI_GPIO_OE 0x0134 +#define TI_GPIO_DATAIN 0x0138 +#define TI_GPIO_DATAOUT 0x013C +#define TI_GPIO_LEVELDETECT0 0x0140 +#define TI_GPIO_LEVELDETECT1 0x0144 +#define TI_GPIO_RISINGDETECT 0x0148 +#define TI_GPIO_FALLINGDETECT 0x014C +#define TI_GPIO_DEBOUNCENABLE 0x0150 +#define TI_GPIO_DEBOUNCINGTIME 0x0154 +#define TI_GPIO_CLEARIRQENABLE1 0x0160 +#define TI_GPIO_SETIRQENABLE1 0x0164 +#define TI_GPIO_CLEARIRQENABLE2 0x0170 +#define TI_GPIO_SETIRQENABLE2 0x0174 +#define TI_GPIO_CLEARWKUPENA 0x0180 +#define TI_GPIO_SETWKUENA 0x0184 +#define TI_GPIO_CLEARDATAOUT 0x0190 +#define TI_GPIO_SETDATAOUT 0x0194 +#else +#error "Unknown SoC" +#endif + + /*Other SoC Specific definitions*/ +#if defined(SOC_OMAP3) +#define MAX_GPIO_BANKS 6 +#define FIRST_GPIO_BANK 1 +#define PINS_PER_BANK 32 +#define TI_GPIO_REV 0x00000025 +#elif defined(SOC_OMAP4) +#define MAX_GPIO_BANKS 6 +#define FIRST_GPIO_BANK 1 +#define PINS_PER_BANK 32 +#define TI_GPIO_REV 0x50600801 +#elif defined(SOC_TI_AM335X) +#define MAX_GPIO_BANKS 4 +#define FIRST_GPIO_BANK 0 +#define PINS_PER_BANK 32 +#define TI_GPIO_REV 0x50600801 +#endif + +/** + * ti_gpio_mem_spec - Resource specification used when allocating resources + * ti_gpio_irq_spec - Resource specification used when allocating resources + * + * This driver module can have up to six independent memory regions, each + * region typically controls 32 GPIO pins. + */ +static struct resource_spec ti_gpio_mem_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { SYS_RES_MEMORY, 1, RF_ACTIVE | RF_OPTIONAL }, + { SYS_RES_MEMORY, 2, RF_ACTIVE | RF_OPTIONAL }, + { SYS_RES_MEMORY, 3, RF_ACTIVE | RF_OPTIONAL }, +#if !defined(SOC_TI_AM335X) + { SYS_RES_MEMORY, 4, RF_ACTIVE | RF_OPTIONAL }, + { SYS_RES_MEMORY, 5, RF_ACTIVE | RF_OPTIONAL }, +#endif + { -1, 0, 0 } +}; +static struct resource_spec ti_gpio_irq_spec[] = { + { SYS_RES_IRQ, 0, RF_ACTIVE }, + { SYS_RES_IRQ, 1, RF_ACTIVE | RF_OPTIONAL }, + { SYS_RES_IRQ, 2, RF_ACTIVE | RF_OPTIONAL }, + { SYS_RES_IRQ, 3, RF_ACTIVE | RF_OPTIONAL }, +#if !defined(SOC_TI_AM335X) + { SYS_RES_IRQ, 4, RF_ACTIVE | RF_OPTIONAL }, + { SYS_RES_IRQ, 5, RF_ACTIVE | RF_OPTIONAL }, +#endif + { -1, 0, 0 } +}; + +/** + * Structure that stores the driver context. + * + * This structure is allocated during driver attach. + */ +struct ti_gpio_softc { + device_t sc_dev; + + /* The memory resource(s) for the PRCM register set, when the device is + * created the caller can assign up to 4 memory regions. + */ + struct resource* sc_mem_res[MAX_GPIO_BANKS]; + struct resource* sc_irq_res[MAX_GPIO_BANKS]; + + /* The handle for the register IRQ handlers */ + void* sc_irq_hdl[MAX_GPIO_BANKS]; + + /* The following describes the H/W revision of each of the GPIO banks */ + uint32_t sc_revision[MAX_GPIO_BANKS]; + + struct mtx sc_mtx; +}; + +/** + * Macros for driver mutex locking + */ +#define TI_GPIO_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) +#define TI_GPIO_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) +#define TI_GPIO_LOCK_INIT(_sc) \ + mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->sc_dev), \ + "ti_gpio", MTX_DEF) +#define TI_GPIO_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); +#define TI_GPIO_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED); +#define TI_GPIO_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED); + +/** + * ti_gpio_read_4 - reads a 16-bit value from one of the PADCONFS registers + * @sc: GPIO device context + * @bank: The bank to read from + * @off: The offset of a register from the GPIO register address range + * + * + * RETURNS: + * 32-bit value read from the register. + */ +static inline uint32_t +ti_gpio_read_4(struct ti_gpio_softc *sc, unsigned int bank, bus_size_t off) +{ + return (bus_read_4(sc->sc_mem_res[bank], off)); +} + +/** + * ti_gpio_write_4 - writes a 32-bit value to one of the PADCONFS registers + * @sc: GPIO device context + * @bank: The bank to write to + * @off: The offset of a register from the GPIO register address range + * @val: The value to write into the register + * + * RETURNS: + * nothing + */ +static inline void +ti_gpio_write_4(struct ti_gpio_softc *sc, unsigned int bank, bus_size_t off, + uint32_t val) +{ + bus_write_4(sc->sc_mem_res[bank], off, val); +} + +/** + * ti_gpio_pin_max - Returns the maximum number of GPIO pins + * @dev: gpio device handle + * @maxpin: pointer to a value that upon return will contain the maximum number + * of pins in the device. + * + * + * LOCKING: + * Internally locks the context + * + * RETURNS: + * Returns 0 on success otherwise an error code + */ +static int +ti_gpio_pin_max(device_t dev, int *maxpin) +{ + struct ti_gpio_softc *sc = device_get_softc(dev); + unsigned int i; + unsigned int banks = 0; + + TI_GPIO_LOCK(sc); + + /* Calculate how many valid banks we have and then multiply that by 32 to + * give use the total number of pins. + */ + for (i = 0; i < MAX_GPIO_BANKS; i++) { + if (sc->sc_mem_res[i] != NULL) + banks++; + } + + *maxpin = (banks * PINS_PER_BANK); + + TI_GPIO_UNLOCK(sc); + + return (0); +} + +/** + * ti_gpio_pin_getcaps - Gets the capabilties of a given pin + * @dev: gpio device handle + * @pin: the number of the pin + * @caps: pointer to a value that upon return will contain the capabilities + * + * Currently all pins have the same capability, notably: + * - GPIO_PIN_INPUT + * - GPIO_PIN_OUTPUT + * - GPIO_PIN_PULLUP + * - GPIO_PIN_PULLDOWN + * + * LOCKING: + * Internally locks the context + * + * RETURNS: + * Returns 0 on success otherwise an error code + */ +static int +ti_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) +{ + struct ti_gpio_softc *sc = device_get_softc(dev); + uint32_t bank = (pin / PINS_PER_BANK); + + TI_GPIO_LOCK(sc); + + /* Sanity check the pin number is valid */ + if ((bank > MAX_GPIO_BANKS) || (sc->sc_mem_res[bank] == NULL)) { + TI_GPIO_UNLOCK(sc); + return (EINVAL); + } + + *caps = (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT |GPIO_PIN_PULLUP | + GPIO_PIN_PULLDOWN); + + TI_GPIO_UNLOCK(sc); + + return (0); +} + +/** + * ti_gpio_pin_getflags - Gets the current flags of a given pin + * @dev: gpio device handle + * @pin: the number of the pin + * @flags: upon return will contain the current flags of the pin + * + * Reads the current flags of a given pin, here we actually read the H/W + * registers to determine the flags, rather than storing the value in the + * setflags call. + * + * LOCKING: + * Internally locks the context + * + * RETURNS: + * Returns 0 on success otherwise an error code + */ +static int +ti_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) +{ + struct ti_gpio_softc *sc = device_get_softc(dev); + uint32_t bank = (pin / PINS_PER_BANK); + + TI_GPIO_LOCK(sc); + + /* Sanity check the pin number is valid */ + if ((bank > MAX_GPIO_BANKS) || (sc->sc_mem_res[bank] == NULL)) { + TI_GPIO_UNLOCK(sc); + return (EINVAL); + } + + /* Get the current pin state */ + ti_scm_padconf_get_gpioflags(pin, flags); + + TI_GPIO_UNLOCK(sc); + + return (0); +} + +/** + * ti_gpio_pin_getname - Gets the name of a given pin + * @dev: gpio device handle + * @pin: the number of the pin + * @name: buffer to put the name in + * + * The driver simply calls the pins gpio_n, where 'n' is obviously the number + * of the pin. + * + * LOCKING: + * Internally locks the context + * + * RETURNS: + * Returns 0 on success otherwise an error code + */ +static int +ti_gpio_pin_getname(device_t dev, uint32_t pin, char *name) +{ + struct ti_gpio_softc *sc = device_get_softc(dev); + uint32_t bank = (pin / PINS_PER_BANK); + + TI_GPIO_LOCK(sc); + + /* Sanity check the pin number is valid */ + if ((bank > MAX_GPIO_BANKS) || (sc->sc_mem_res[bank] == NULL)) { + TI_GPIO_UNLOCK(sc); + return (EINVAL); + } + + /* Set a very simple name */ + snprintf(name, GPIOMAXNAME, "gpio_%u", pin); + name[GPIOMAXNAME - 1] = '\0'; + + TI_GPIO_UNLOCK(sc); + + return (0); +} + +/** + * ti_gpio_pin_setflags - Sets the flags for a given pin + * @dev: gpio device handle + * @pin: the number of the pin + * @flags: the flags to set + * + * The flags of the pin correspond to things like input/output mode, pull-ups, + * pull-downs, etc. This driver doesn't support all flags, only the following: + * - GPIO_PIN_INPUT + * - GPIO_PIN_OUTPUT + * - GPIO_PIN_PULLUP + * - GPIO_PIN_PULLDOWN + * + * LOCKING: + * Internally locks the context + * + * RETURNS: + * Returns 0 on success otherwise an error code + */ +static int +ti_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) +{ + struct ti_gpio_softc *sc = device_get_softc(dev); + uint32_t bank = (pin / PINS_PER_BANK); + uint32_t mask = (1UL << (pin % PINS_PER_BANK)); + uint32_t reg_val; + + /* Sanity check the flags supplied are valid, i.e. not input and output */ + if ((flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) == 0x0000) + return (EINVAL); + if ((flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) == + (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) + return (EINVAL); + if ((flags & (GPIO_PIN_PULLUP|GPIO_PIN_PULLDOWN)) == + (GPIO_PIN_PULLUP|GPIO_PIN_PULLDOWN)) + return (EINVAL); + + + TI_GPIO_LOCK(sc); + + /* Sanity check the pin number is valid */ + if ((bank > MAX_GPIO_BANKS) || (sc->sc_mem_res[bank] == NULL)) { + TI_GPIO_UNLOCK(sc); + return (EINVAL); + } + + /* Set the GPIO mode and state */ + if (ti_scm_padconf_set_gpioflags(pin, flags) != 0) { + TI_GPIO_UNLOCK(sc); + return (EINVAL); + } + + /* If configuring as an output set the "output enable" bit */ + reg_val = ti_gpio_read_4(sc, bank, TI_GPIO_OE); + if (flags & GPIO_PIN_INPUT) + reg_val |= mask; + else + reg_val &= ~mask; + ti_gpio_write_4(sc, bank, TI_GPIO_OE, reg_val); + + + TI_GPIO_UNLOCK(sc); + + return (0); +} + +/** + * ti_gpio_pin_set - Sets the current level on a GPIO pin + * @dev: gpio device handle + * @pin: the number of the pin + * @value: non-zero value will drive the pin high, otherwise the pin is + * driven low. + * + * + * LOCKING: + * Internally locks the context + * + * RETURNS: + * Returns 0 on success otherwise a error code + */ +static int +ti_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) +{ + struct ti_gpio_softc *sc = device_get_softc(dev); + uint32_t bank = (pin / PINS_PER_BANK); + uint32_t mask = (1UL << (pin % PINS_PER_BANK)); + + TI_GPIO_LOCK(sc); + + /* Sanity check the pin number is valid */ + if ((bank > MAX_GPIO_BANKS) || (sc->sc_mem_res[bank] == NULL)) { + TI_GPIO_UNLOCK(sc); + return (EINVAL); + } + + ti_gpio_write_4(sc, bank, (value == GPIO_PIN_LOW) ? TI_GPIO_CLEARDATAOUT + : TI_GPIO_SETDATAOUT, mask); + + TI_GPIO_UNLOCK(sc); + + return (0); +} + +/** + * ti_gpio_pin_get - Gets the current level on a GPIO pin + * @dev: gpio device handle + * @pin: the number of the pin + * @value: pointer to a value that upond return will contain the pin value + * + * The pin must be configured as an input pin beforehand, otherwise this + * function will fail. + * + * LOCKING: + * Internally locks the context + * + * RETURNS: + * Returns 0 on success otherwise a error code + */ +static int +ti_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *value) +{ + struct ti_gpio_softc *sc = device_get_softc(dev); + uint32_t bank = (pin / PINS_PER_BANK); + uint32_t mask = (1UL << (pin % PINS_PER_BANK)); + uint32_t val = 0; + + TI_GPIO_LOCK(sc); + + /* Sanity check the pin number is valid */ + if ((bank > MAX_GPIO_BANKS) || (sc->sc_mem_res[bank] == NULL)) { + TI_GPIO_UNLOCK(sc); + return (EINVAL); + } + + /* Sanity check the pin is not configured as an output */ + val = ti_gpio_read_4(sc, bank, TI_GPIO_OE); + + if ((val & mask) == mask) { + TI_GPIO_UNLOCK(sc); + return (EINVAL); + } + + /* Read the value on the pin */ + *value = (ti_gpio_read_4(sc, bank, TI_GPIO_DATAIN) & mask) ? 1 : 0; + + TI_GPIO_UNLOCK(sc); + + return (0); +} + +/** + * ti_gpio_pin_toggle - Toggles a given GPIO pin + * @dev: gpio device handle + * @pin: the number of the pin + * + * + * LOCKING: + * Internally locks the context + * + * RETURNS: + * Returns 0 on success otherwise a error code + */ +static int +ti_gpio_pin_toggle(device_t dev, uint32_t pin) +{ + struct ti_gpio_softc *sc = device_get_softc(dev); + uint32_t bank = (pin / PINS_PER_BANK); + uint32_t mask = (1UL << (pin % PINS_PER_BANK)); + uint32_t val; + + TI_GPIO_LOCK(sc); + + /* Sanity check the pin number is valid */ + if ((bank > MAX_GPIO_BANKS) || (sc->sc_mem_res[bank] == NULL)) { + TI_GPIO_UNLOCK(sc); + return (EINVAL); + } + + /* Toggle the pin */ + val = ti_gpio_read_4(sc, bank, TI_GPIO_DATAOUT); + if (val & mask) + ti_gpio_write_4(sc, bank, TI_GPIO_CLEARDATAOUT, mask); + else + ti_gpio_write_4(sc, bank, TI_GPIO_SETDATAOUT, mask); + + TI_GPIO_UNLOCK(sc); + + return (0); +} + +/** + * ti_gpio_intr - ISR for all GPIO modules + * @arg: the soft context pointer + * + * Unsused + * + * LOCKING: + * Internally locks the context + * + */ +static void +ti_gpio_intr(void *arg) +{ + struct ti_gpio_softc *sc = arg; + + TI_GPIO_LOCK(sc); + /* TODO: something useful */ + TI_GPIO_UNLOCK(sc); +} + +/** + * ti_gpio_probe - probe function for the driver + * @dev: gpio device handle + * + * Simply sets the name of the driver + * + * LOCKING: + * None + * + * RETURNS: + * Always returns 0 + */ +static int +ti_gpio_probe(device_t dev) +{ + if (!ofw_bus_is_compatible(dev, "ti,gpio")) + return (ENXIO); + + device_set_desc(dev, "TI General Purpose I/O (GPIO)"); + return (0); +} + +/** + * ti_gpio_attach - attach function for the driver + * @dev: gpio device handle + * + * Allocates and sets up the driver context for all GPIO banks. This function + * expects the memory ranges and IRQs to already be allocated to the driver. + * + * LOCKING: + * None + * + * RETURNS: + * Always returns 0 + */ +static int +ti_gpio_attach(device_t dev) +{ + struct ti_gpio_softc *sc = device_get_softc(dev); + unsigned int i; + int err = 0; + + sc->sc_dev = dev; + + TI_GPIO_LOCK_INIT(sc); + + + /* There are up to 6 different GPIO register sets located in different + * memory areas on the chip. The memory range should have been set for + * the driver when it was added as a child. + */ + err = bus_alloc_resources(dev, ti_gpio_mem_spec, sc->sc_mem_res); + if (err) { + device_printf(dev, "Error: could not allocate mem resources\n"); + return (ENXIO); + } + + /* Request the IRQ resources */ + err = bus_alloc_resources(dev, ti_gpio_irq_spec, sc->sc_irq_res); + if (err) { + device_printf(dev, "Error: could not allocate irq resources\n"); + return (ENXIO); + } + + /* Setup the IRQ resources */ + for (i = 0; i < MAX_GPIO_BANKS; i++) { + if (sc->sc_irq_res[i] == NULL) + break; + + /* Register an interrupt handler for each of the IRQ resources */ + if ((bus_setup_intr(dev, sc->sc_irq_res[i], INTR_TYPE_MISC | INTR_MPSAFE, + NULL, ti_gpio_intr, sc, &(sc->sc_irq_hdl[i])))) { + device_printf(dev, "WARNING: unable to register interrupt handler\n"); + return (ENXIO); + } + } + + /* Store the device handle back in the sc */ + sc->sc_dev = dev; + + /* We need to go through each block and ensure the clocks are running and + * the module is enabled. It might be better to do this only when the + * pins are configured which would result in less power used if the GPIO + * pins weren't used ... + */ + for (i = 0; i < MAX_GPIO_BANKS; i++) { + if (sc->sc_mem_res[i] != NULL) { + + /* Enable the interface and functional clocks for the module */ + ti_prcm_clk_enable(GPIO0_CLK + FIRST_GPIO_BANK + i); + + /* Read the revision number of the module. TI don't publish the + * actual revision numbers, so instead the values have been + * determined by experimentation. + */ + sc->sc_revision[i] = ti_gpio_read_4(sc, i, TI_GPIO_REVISION); + + /* Check the revision */ + if (sc->sc_revision[i] != TI_GPIO_REV) { + device_printf(dev, "Warning: could not determine the revision" + "of %u GPIO module (revision:0x%08x)\n", + i, sc->sc_revision[i]); + continue; + } + + /* Disable interrupts for all pins */ + ti_gpio_write_4(sc, i, TI_GPIO_CLEARIRQENABLE1, 0xffffffff); + ti_gpio_write_4(sc, i, TI_GPIO_CLEARIRQENABLE2, 0xffffffff); + } + } + + /* Finish of the probe call */ + device_add_child(dev, "gpioc", device_get_unit(dev)); + device_add_child(dev, "gpiobus", device_get_unit(dev)); + return (bus_generic_attach(dev)); +} + +/** + * ti_gpio_detach - detach function for the driver + * @dev: scm device handle + * + * Allocates and sets up the driver context, this simply entails creating a + * bus mappings for the SCM register set. + * + * LOCKING: + * None + * + * RETURNS: + * Always returns 0 + */ +static int +ti_gpio_detach(device_t dev) +{ + struct ti_gpio_softc *sc = device_get_softc(dev); + unsigned int i; + + KASSERT(mtx_initialized(&sc->sc_mtx), ("gpio mutex not initialized")); + + /* Disable all interrupts */ + for (i = 0; i < MAX_GPIO_BANKS; i++) { + if (sc->sc_mem_res[i] != NULL) { + ti_gpio_write_4(sc, i, TI_GPIO_CLEARIRQENABLE1, 0xffffffff); + ti_gpio_write_4(sc, i, TI_GPIO_CLEARIRQENABLE2, 0xffffffff); + } + } + + bus_generic_detach(dev); + + /* Release the memory and IRQ resources */ + for (i = 0; i < MAX_GPIO_BANKS; i++) { + if (sc->sc_mem_res[i] != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, i, sc->sc_mem_res[i]); + if (sc->sc_irq_res[i] != NULL) + bus_release_resource(dev, SYS_RES_IRQ, i, sc->sc_irq_res[i]); + } + + TI_GPIO_LOCK_DESTROY(sc); + + return(0); +} + +static device_method_t ti_gpio_methods[] = { + DEVMETHOD(device_probe, ti_gpio_probe), + DEVMETHOD(device_attach, ti_gpio_attach), + DEVMETHOD(device_detach, ti_gpio_detach), + + /* GPIO protocol */ + DEVMETHOD(gpio_pin_max, ti_gpio_pin_max), + DEVMETHOD(gpio_pin_getname, ti_gpio_pin_getname), + DEVMETHOD(gpio_pin_getflags, ti_gpio_pin_getflags), + DEVMETHOD(gpio_pin_getcaps, ti_gpio_pin_getcaps), + DEVMETHOD(gpio_pin_setflags, ti_gpio_pin_setflags), + DEVMETHOD(gpio_pin_get, ti_gpio_pin_get), + DEVMETHOD(gpio_pin_set, ti_gpio_pin_set), + DEVMETHOD(gpio_pin_toggle, ti_gpio_pin_toggle), + {0, 0}, +}; + +static driver_t ti_gpio_driver = { + "gpio", + ti_gpio_methods, + sizeof(struct ti_gpio_softc), +}; +static devclass_t ti_gpio_devclass; + +DRIVER_MODULE(ti_gpio, simplebus, ti_gpio_driver, ti_gpio_devclass, 0, 0); diff --git a/sys/arm/ti/ti_i2c.c b/sys/arm/ti/ti_i2c.c new file mode 100644 index 000000000000..28bf3cadd04c --- /dev/null +++ b/sys/arm/ti/ti_i2c.c @@ -0,0 +1,1179 @@ +/*- + * 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. + */ + +/** + * Driver for the I2C module on the TI SoC. + * + * This driver is heavily based on the TWI driver for the AT91 (at91_twi.c). + * + * CAUTION: The I2Ci registers are limited to 16 bit and 8 bit data accesses, + * 32 bit data access is not allowed and can corrupt register content. + * + * This driver currently doesn't use DMA for the transfer, although I hope to + * incorporate that sometime in the future. The idea being that for transaction + * larger than a certain size the DMA engine is used, for anything less the + * normal interrupt/fifo driven option is used. + * + * + * WARNING: This driver uses mtx_sleep and interrupts to perform transactions, + * which means you can't do a transaction during startup before the interrupts + * have been enabled. Hint - the freebsd function config_intrhook_establish(). + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "iicbus_if.h" + +/** + * I2C device driver context, a pointer to this is stored in the device + * driver structure. + */ +struct ti_i2c_softc +{ + device_t sc_dev; + uint32_t device_id; + struct resource* sc_irq_res; + struct resource* sc_mem_res; + device_t sc_iicbus; + + void* sc_irq_h; + + struct mtx sc_mtx; + + volatile uint16_t sc_stat_flags; /* contains the status flags last IRQ */ + + uint16_t sc_i2c_addr; + uint16_t sc_rev; +}; + +struct ti_i2c_clock_config +{ + int speed; + int bitrate; + uint8_t psc; /* Fast/Standard mode prescale divider */ + uint8_t scll; /* Fast/Standard mode SCL low time */ + uint8_t sclh; /* Fast/Standard mode SCL high time */ + uint8_t hsscll; /* High Speed mode SCL low time */ + uint8_t hssclh; /* High Speed mode SCL high time */ +}; + +static struct ti_i2c_clock_config ti_i2c_clock_configs[] = { + +#if defined(SOC_OMAP4) + { IIC_SLOW, 100000, 23, 13, 15, 0, 0}, + { IIC_FAST, 400000, 9, 5, 7, 0, 0}, + { IIC_FASTEST, 3310000, 1, 113, 115, 7, 10}, +#elif defined(SOC_TI_AM335X) + { IIC_SLOW, 100000, 3, 53, 55, 0, 0}, + { IIC_FAST, 400000, 3, 8, 10, 0, 0}, + { IIC_FASTEST, 400000, 3, 8, 10, 0, 0}, /* This might be higher */ +#else +#error "TI I2C driver is not supported on this SoC" +#endif + { -1, 0 } +}; + + +#define TI_I2C_REV1 0x003C /* OMAP3 */ +#define TI_I2C_REV2 0x000A /* OMAP4 */ + +/** + * Locking macros used throughout the driver + */ +#define TI_I2C_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) +#define TI_I2C_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) +#define TI_I2C_LOCK_INIT(_sc) \ + mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->sc_dev), \ + "ti_i2c", MTX_DEF) +#define TI_I2C_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); +#define TI_I2C_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED); +#define TI_I2C_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED); + +#ifdef DEBUG +#define ti_i2c_dbg(_sc, fmt, args...) \ + device_printf((_sc)->sc_dev, fmt, ##args) +#else +#define ti_i2c_dbg(_sc, fmt, args...) +#endif + +static devclass_t ti_i2c_devclass; + +/* bus entry points */ + +static int ti_i2c_probe(device_t dev); +static int ti_i2c_attach(device_t dev); +static int ti_i2c_detach(device_t dev); +static void ti_i2c_intr(void *); + +/* OFW routine */ +static phandle_t ti_i2c_get_node(device_t bus, device_t dev); + +/* helper routines */ +static int ti_i2c_activate(device_t dev); +static void ti_i2c_deactivate(device_t dev); + +/** + * ti_i2c_read_2 - reads a 16-bit value from one of the I2C registers + * @sc: I2C device context + * @off: the byte offset within the register bank to read from. + * + * + * LOCKING: + * No locking required + * + * RETURNS: + * 16-bit value read from the register. + */ +static inline uint16_t +ti_i2c_read_2(struct ti_i2c_softc *sc, bus_size_t off) +{ + return bus_read_2(sc->sc_mem_res, off); +} + +/** + * ti_i2c_write_2 - writes a 16-bit value to one of the I2C registers + * @sc: I2C device context + * @off: the byte offset within the register bank to read from. + * @val: the value to write into the register + * + * LOCKING: + * No locking required + * + * RETURNS: + * 16-bit value read from the register. + */ +static inline void +ti_i2c_write_2(struct ti_i2c_softc *sc, bus_size_t off, uint16_t val) +{ + bus_write_2(sc->sc_mem_res, off, val); +} + +/** + * ti_i2c_read_reg - reads a 16-bit value from one of the I2C registers + * take into account revision-dependent register offset + * @sc: I2C device context + * @off: the byte offset within the register bank to read from. + * + * + * LOCKING: + * No locking required + * + * RETURNS: + * 16-bit value read from the register. + */ +static inline uint16_t +ti_i2c_read_reg(struct ti_i2c_softc *sc, bus_size_t off) +{ + /* XXXOMAP3: FIXME add registers mapping here */ + return bus_read_2(sc->sc_mem_res, off); +} + +/** + * ti_i2c_write_reg - writes a 16-bit value to one of the I2C registers + * take into account revision-dependent register offset + * @sc: I2C device context + * @off: the byte offset within the register bank to read from. + * @val: the value to write into the register + * + * LOCKING: + * No locking required + * + * RETURNS: + * 16-bit value read from the register. + */ +static inline void +ti_i2c_write_reg(struct ti_i2c_softc *sc, bus_size_t off, uint16_t val) +{ + /* XXXOMAP3: FIXME add registers mapping here */ + bus_write_2(sc->sc_mem_res, off, val); +} + +/** + * ti_i2c_set_intr_enable - writes the interrupt enable register + * @sc: I2C device context + * @ie: bitmask of the interrupts to enable + * + * This function is needed as writing the I2C_IE register on the OMAP4 devices + * doesn't seem to actually enable the interrupt, rather you have to write + * through the I2C_IRQENABLE_CLR and I2C_IRQENABLE_SET registers. + * + * LOCKING: + * No locking required + * + * RETURNS: + * Nothing. + */ +static inline void +ti_i2c_set_intr_enable(struct ti_i2c_softc *sc, uint16_t ie) +{ + /* XXXOMAP3: FIXME */ + ti_i2c_write_2(sc, I2C_REG_IRQENABLE_CLR, 0xffff); + if (ie) + ti_i2c_write_2(sc, I2C_REG_IRQENABLE_SET, ie); +} + +/** + * ti_i2c_reset - attach function for the driver + * @dev: i2c device handle + * + * + * + * LOCKING: + * Called from timer context + * + * RETURNS: + * EH_HANDLED or EH_NOT_HANDLED + */ +static int +ti_i2c_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr) +{ + struct ti_i2c_softc *sc = device_get_softc(dev); + struct ti_i2c_clock_config *clkcfg; + uint16_t con_reg; + + clkcfg = ti_i2c_clock_configs; + while (clkcfg->speed != -1) { + if (clkcfg->speed == speed) + break; + /* take slow if speed is unknown */ + if ((speed == IIC_UNKNOWN) && (clkcfg->speed == IIC_SLOW)) + break; + clkcfg++; + } + if (clkcfg->speed == -1) + return (EINVAL); + + TI_I2C_LOCK(sc); + + if (oldaddr) + *oldaddr = sc->sc_i2c_addr; + sc->sc_i2c_addr = addr; + + /* First disable the controller while changing the clocks */ + con_reg = ti_i2c_read_reg(sc, I2C_REG_CON); + ti_i2c_write_reg(sc, I2C_REG_CON, 0x0000); + + /* Program the prescaler */ + ti_i2c_write_reg(sc, I2C_REG_PSC, clkcfg->psc); + + /* Set the bitrate */ + ti_i2c_write_reg(sc, I2C_REG_SCLL, clkcfg->scll | (clkcfg->hsscll<<8)); + ti_i2c_write_reg(sc, I2C_REG_SCLH, clkcfg->sclh | (clkcfg->hssclh<<8)); + + /* Set the remote slave address */ + ti_i2c_write_reg(sc, I2C_REG_SA, addr); + + /* Check if we are dealing with high speed mode */ + if ((clkcfg->hsscll + clkcfg->hssclh) > 0) + con_reg = I2C_CON_OPMODE_HS; + else + con_reg = I2C_CON_OPMODE_STD; + + /* Enable the I2C module again */ + ti_i2c_write_reg(sc, I2C_REG_CON, I2C_CON_I2C_EN | con_reg); + + TI_I2C_UNLOCK(sc); + + return 0; +} + +/** + * ti_i2c_intr - interrupt handler for the I2C module + * @dev: i2c device handle + * + * + * + * LOCKING: + * Called from timer context + * + * RETURNS: + * EH_HANDLED or EH_NOT_HANDLED + */ +static void +ti_i2c_intr(void *arg) +{ + struct ti_i2c_softc *sc = (struct ti_i2c_softc*) arg; + uint16_t status; + + status = ti_i2c_read_reg(sc, I2C_REG_STAT); + if (status == 0) + return; + + TI_I2C_LOCK(sc); + + /* save the flags */ + sc->sc_stat_flags |= status; + + /* clear the status flags */ + ti_i2c_write_reg(sc, I2C_REG_STAT, status); + + /* wakeup the process the started the transaction */ + wakeup(sc); + + TI_I2C_UNLOCK(sc); + + return; +} + +/** + * ti_i2c_wait - waits for the specific event to occur + * @sc: i2c driver context + * @flags: the event(s) to wait on, this is a bitmask of the I2C_STAT_??? flags + * @statp: if not null will contain the status flags upon return + * @timo: the number of ticks to wait + * + * + * + * LOCKING: + * The driver context must be locked before calling this function. Internally + * the function sleeps, releasing the lock as it does so, however the lock is + * always retaken before this function returns. + * + * RETURNS: + * 0 if the event(s) were tripped within timeout period + * EBUSY if timedout waiting for the events + * ENXIO if a NACK event was received + */ +static int +ti_i2c_wait(struct ti_i2c_softc *sc, uint16_t flags, uint16_t *statp, int timo) +{ + int waittime = timo; + int start_ticks = ticks; + int rc; + + TI_I2C_ASSERT_LOCKED(sc); + + /* check if the condition has already occured, the interrupt routine will + * clear the status flags. + */ + if ((sc->sc_stat_flags & flags) == 0) { + + /* condition(s) haven't occured so sleep on the IRQ */ + while (waittime > 0) { + + rc = mtx_sleep(sc, &sc->sc_mtx, 0, "I2Cwait", waittime); + if (rc == EWOULDBLOCK) { + /* timed-out, simply break out of the loop */ + break; + } else { + /* IRQ has been tripped, but need to sanity check we have the + * right events in the status flag. + */ + if ((sc->sc_stat_flags & flags) != 0) + break; + + /* event hasn't been tripped so wait some more */ + waittime -= (ticks - start_ticks); + start_ticks = ticks; + } + } + } + + /* copy the actual status bits */ + if (statp != NULL) + *statp = sc->sc_stat_flags; + + /* return the status found */ + if ((sc->sc_stat_flags & flags) != 0) + rc = 0; + else + rc = EBUSY; + + /* clear the flags set by the interrupt handler */ + sc->sc_stat_flags = 0; + + return (rc); +} + +/** + * ti_i2c_wait_for_free_bus - waits for the bus to become free + * @sc: i2c driver context + * @timo: the time to wait for the bus to become free + * + * + * + * LOCKING: + * The driver context must be locked before calling this function. Internally + * the function sleeps, releasing the lock as it does so, however the lock is + * always taken before this function returns. + * + * RETURNS: + * 0 if the event(s) were tripped within timeout period + * EBUSY if timedout waiting for the events + * ENXIO if a NACK event was received + */ +static int +ti_i2c_wait_for_free_bus(struct ti_i2c_softc *sc, int timo) +{ + /* check if the bus is free, BB bit = 0 */ + if ((ti_i2c_read_reg(sc, I2C_REG_STAT) & I2C_STAT_BB) == 0) + return 0; + + /* enable bus free interrupts */ + ti_i2c_set_intr_enable(sc, I2C_IE_BF); + + /* wait for the bus free interrupt to be tripped */ + return ti_i2c_wait(sc, I2C_STAT_BF, NULL, timo); +} + +/** + * ti_i2c_read_bytes - attempts to perform a read operation + * @sc: i2c driver context + * @buf: buffer to hold the received bytes + * @len: the number of bytes to read + * + * This function assumes the slave address is already set + * + * LOCKING: + * The context lock should be held before calling this function + * + * RETURNS: + * 0 on function succeeded + * EINVAL if invalid message is passed as an arg + */ +static int +ti_i2c_read_bytes(struct ti_i2c_softc *sc, uint8_t *buf, uint16_t len) +{ + int timo = (hz / 4); + int err = 0; + uint16_t con_reg; + uint16_t events; + uint16_t status; + uint32_t amount = 0; + uint32_t sofar = 0; + uint32_t i; + + /* wait for the bus to become free */ + err = ti_i2c_wait_for_free_bus(sc, timo); + if (err != 0) { + device_printf(sc->sc_dev, "bus never freed\n"); + return (err); + } + + /* set the events to wait for */ + events = I2C_IE_RDR | /* Receive draining interrupt */ + I2C_IE_RRDY | /* Receive Data Ready interrupt */ + I2C_IE_ARDY | /* Register Access Ready interrupt */ + I2C_IE_NACK | /* No Acknowledgment interrupt */ + I2C_IE_AL; + + /* enable interrupts for the events we want */ + ti_i2c_set_intr_enable(sc, events); + + /* write the number of bytes to read */ + ti_i2c_write_reg(sc, I2C_REG_CNT, len); + + /* clear the write bit and initiate the read transaction. Setting the STT + * (start) bit initiates the transfer. + */ + con_reg = ti_i2c_read_reg(sc, I2C_REG_CON); + con_reg &= ~I2C_CON_TRX; + con_reg |= I2C_CON_MST | I2C_CON_STT | I2C_CON_STP; + ti_i2c_write_reg(sc, I2C_REG_CON, con_reg); + + /* reading loop */ + while (1) { + + /* wait for an event */ + err = ti_i2c_wait(sc, events, &status, timo); + if (err != 0) { + break; + } + + /* check for the error conditions */ + if (status & I2C_STAT_NACK) { + /* no ACK from slave */ + ti_i2c_dbg(sc, "NACK\n"); + err = ENXIO; + break; + } + if (status & I2C_STAT_AL) { + /* arbitration lost */ + ti_i2c_dbg(sc, "Arbitration lost\n"); + err = ENXIO; + break; + } + + /* check if we have finished */ + if (status & I2C_STAT_ARDY) { + /* register access ready - transaction complete basically */ + ti_i2c_dbg(sc, "ARDY transaction complete\n"); + err = 0; + break; + } + + /* read some data */ + if (status & I2C_STAT_RDR) { + /* Receive draining interrupt - last data received */ + ti_i2c_dbg(sc, "Receive draining interrupt\n"); + + /* get the number of bytes in the FIFO */ + amount = ti_i2c_read_reg(sc, I2C_REG_BUFSTAT); + amount >>= 8; + amount &= 0x3f; + } + else if (status & I2C_STAT_RRDY) { + /* Receive data ready interrupt - enough data received */ + ti_i2c_dbg(sc, "Receive data ready interrupt\n"); + + /* get the number of bytes in the FIFO */ + amount = ti_i2c_read_reg(sc, I2C_REG_BUF); + amount >>= 8; + amount &= 0x3f; + amount += 1; + } + + /* sanity check we haven't overwritten the array */ + if ((sofar + amount) > len) { + ti_i2c_dbg(sc, "to many bytes to read\n"); + amount = (len - sofar); + } + + /* read the bytes from the fifo */ + for (i = 0; i < amount; i++) { + buf[sofar++] = (uint8_t)(ti_i2c_read_reg(sc, I2C_REG_DATA) & 0xff); + } + + /* attempt to clear the receive ready bits */ + ti_i2c_write_reg(sc, I2C_REG_STAT, I2C_STAT_RDR | I2C_STAT_RRDY); + } + + /* reset the registers regardless if there was an error or not */ + ti_i2c_set_intr_enable(sc, 0x0000); + ti_i2c_write_reg(sc, I2C_REG_CON, I2C_CON_I2C_EN | I2C_CON_MST | I2C_CON_STP); + + return (err); +} + +/** + * ti_i2c_write_bytes - attempts to perform a read operation + * @sc: i2c driver context + * @buf: buffer containing the bytes to write + * @len: the number of bytes to write + * + * This function assumes the slave address is already set + * + * LOCKING: + * The context lock should be held before calling this function + * + * RETURNS: + * 0 on function succeeded + * EINVAL if invalid message is passed as an arg + */ +static int +ti_i2c_write_bytes(struct ti_i2c_softc *sc, const uint8_t *buf, uint16_t len) +{ + int timo = (hz / 4); + int err = 0; + uint16_t con_reg; + uint16_t events; + uint16_t status; + uint32_t amount = 0; + uint32_t sofar = 0; + uint32_t i; + + /* wait for the bus to become free */ + err = ti_i2c_wait_for_free_bus(sc, timo); + if (err != 0) + return (err); + + /* set the events to wait for */ + events = I2C_IE_XDR | /* Transmit draining interrupt */ + I2C_IE_XRDY | /* Transmit Data Ready interrupt */ + I2C_IE_ARDY | /* Register Access Ready interrupt */ + I2C_IE_NACK | /* No Acknowledgment interrupt */ + I2C_IE_AL; + + /* enable interrupts for the events we want*/ + ti_i2c_set_intr_enable(sc, events); + + /* write the number of bytes to write */ + ti_i2c_write_reg(sc, I2C_REG_CNT, len); + + /* set the write bit and initiate the write transaction. Setting the STT + * (start) bit initiates the transfer. + */ + con_reg = ti_i2c_read_reg(sc, I2C_REG_CON); + con_reg |= I2C_CON_TRX | I2C_CON_MST | I2C_CON_STT | I2C_CON_STP; + ti_i2c_write_reg(sc, I2C_REG_CON, con_reg); + + /* writing loop */ + while (1) { + + /* wait for an event */ + err = ti_i2c_wait(sc, events, &status, timo); + if (err != 0) { + break; + } + + /* check for the error conditions */ + if (status & I2C_STAT_NACK) { + /* no ACK from slave */ + ti_i2c_dbg(sc, "NACK\n"); + err = ENXIO; + break; + } + if (status & I2C_STAT_AL) { + /* arbitration lost */ + ti_i2c_dbg(sc, "Arbitration lost\n"); + err = ENXIO; + break; + } + + /* check if we have finished */ + if (status & I2C_STAT_ARDY) { + /* register access ready - transaction complete basically */ + ti_i2c_dbg(sc, "ARDY transaction complete\n"); + err = 0; + break; + } + + /* read some data */ + if (status & I2C_STAT_XDR) { + /* Receive draining interrupt - last data received */ + ti_i2c_dbg(sc, "Transmit draining interrupt\n"); + + /* get the number of bytes in the FIFO */ + amount = ti_i2c_read_reg(sc, I2C_REG_BUFSTAT); + amount &= 0x3f; + } + else if (status & I2C_STAT_XRDY) { + /* Receive data ready interrupt - enough data received */ + ti_i2c_dbg(sc, "Transmit data ready interrupt\n"); + + /* get the number of bytes in the FIFO */ + amount = ti_i2c_read_reg(sc, I2C_REG_BUF); + amount &= 0x3f; + amount += 1; + } + + /* sanity check we haven't overwritten the array */ + if ((sofar + amount) > len) { + ti_i2c_dbg(sc, "to many bytes to write\n"); + amount = (len - sofar); + } + + /* write the bytes from the fifo */ + for (i = 0; i < amount; i++) { + ti_i2c_write_reg(sc, I2C_REG_DATA, buf[sofar++]); + } + + /* attempt to clear the transmit ready bits */ + ti_i2c_write_reg(sc, I2C_REG_STAT, I2C_STAT_XDR | I2C_STAT_XRDY); + } + + /* reset the registers regardless if there was an error or not */ + ti_i2c_set_intr_enable(sc, 0x0000); + ti_i2c_write_reg(sc, I2C_REG_CON, I2C_CON_I2C_EN | I2C_CON_MST | I2C_CON_STP); + + return (err); +} + +/** + * ti_i2c_transfer - called to perform the transfer + * @dev: i2c device handle + * @msgs: the messages to send/receive + * @nmsgs: the number of messages in the msgs array + * + * + * LOCKING: + * Internally locked + * + * RETURNS: + * 0 on function succeeded + * EINVAL if invalid message is passed as an arg + */ +static int +ti_i2c_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs) +{ + struct ti_i2c_softc *sc = device_get_softc(dev); + int err = 0; + uint32_t i; + uint16_t len; + uint8_t *buf; + + TI_I2C_LOCK(sc); + + for (i = 0; i < nmsgs; i++) { + + len = msgs[i].len; + buf = msgs[i].buf; + + /* zero byte transfers aren't allowed */ + if (len == 0 || buf == NULL) { + err = EINVAL; + goto out; + } + + /* set the slave address */ + ti_i2c_write_reg(sc, I2C_REG_SA, msgs[i].slave); + + /* perform the read or write */ + if (msgs[i].flags & IIC_M_RD) { + err = ti_i2c_read_bytes(sc, buf, len); + } else { + err = ti_i2c_write_bytes(sc, buf, len); + } + + } + +out: + TI_I2C_UNLOCK(sc); + + return (err); +} + +/** + * ti_i2c_callback - not sure about this one + * @dev: i2c device handle + * + * + * + * LOCKING: + * Called from timer context + * + * RETURNS: + * EH_HANDLED or EH_NOT_HANDLED + */ +static int +ti_i2c_callback(device_t dev, int index, caddr_t data) +{ + int error = 0; + + switch (index) { + case IIC_REQUEST_BUS: + break; + + case IIC_RELEASE_BUS: + break; + + default: + error = EINVAL; + } + + return (error); +} + +/** + * ti_i2c_activate - initialises and activates an I2C bus + * @dev: i2c device handle + * @num: the number of the I2C controller to activate; 1, 2 or 3 + * + * + * LOCKING: + * Assumed called in an atomic context. + * + * RETURNS: + * nothing + */ +static int +ti_i2c_activate(device_t dev) +{ + struct ti_i2c_softc *sc = (struct ti_i2c_softc*) device_get_softc(dev); + unsigned int timeout = 0; + uint16_t con_reg; + int err; + clk_ident_t clk; + + /* + * The following sequence is taken from the OMAP3530 technical reference + * + * 1. Enable the functional and interface clocks (see Section 18.3.1.1.1). + */ + clk = I2C0_CLK + sc->device_id; + err = ti_prcm_clk_enable(clk); + if (err) + return (err); + + /* There seems to be a bug in the I2C reset mechanism, for some reason you + * need to disable the I2C module before issuing the reset and then enable + * it again after to detect the reset done. + * + * I found this out by looking at the Linux driver implementation, thanks + * linux guys! + */ + + /* Disable the I2C controller */ + ti_i2c_write_reg(sc, I2C_REG_CON, 0x0000); + + /* Issue a softreset to the controller */ + /* XXXOMAP3: FIXME */ + bus_write_2(sc->sc_mem_res, I2C_REG_SYSC, 0x0002); + + /* Re-enable the module and then check for the reset done */ + ti_i2c_write_reg(sc, I2C_REG_CON, I2C_CON_I2C_EN); + + while ((ti_i2c_read_reg(sc, I2C_REG_SYSS) & 0x01) == 0x00) { + if (timeout++ > 100) { + return (EBUSY); + } + DELAY(100); + } + + /* Disable the I2C controller once again, now that the reset has finished */ + ti_i2c_write_reg(sc, I2C_REG_CON, 0x0000); + + /* 2. Program the prescaler to obtain an approximately 12-MHz internal + * sampling clock (I2Ci_INTERNAL_CLK) by programming the corresponding + * value in the I2Ci.I2C_PSC[3:0] PSC field. + * This value depends on the frequency of the functional clock (I2Ci_FCLK). + * Because this frequency is 96MHz, the I2Ci.I2C_PSC[7:0] PSC field value + * is 0x7. + */ + + /* Program the prescaler to obtain an approximately 12-MHz internal + * sampling clock. + */ + ti_i2c_write_reg(sc, I2C_REG_PSC, 0x0017); + + /* 3. Program the I2Ci.I2C_SCLL[7:0] SCLL and I2Ci.I2C_SCLH[7:0] SCLH fields + * to obtain a bit rate of 100K bps or 400K bps. These values depend on + * the internal sampling clock frequency (see Table 18-12). + */ + + /* Set the bitrate to 100kbps */ + ti_i2c_write_reg(sc, I2C_REG_SCLL, 0x000d); + ti_i2c_write_reg(sc, I2C_REG_SCLH, 0x000f); + + /* 4. (Optional) Program the I2Ci.I2C_SCLL[15:8] HSSCLL and + * I2Ci.I2C_SCLH[15:8] HSSCLH fields to obtain a bit rate of 400K bps or + * 3.4M bps (for the second phase of HS mode). These values depend on the + * internal sampling clock frequency (see Table 18-12). + * + * 5. (Optional) If a bit rate of 3.4M bps is used and the bus line + * capacitance exceeds 45 pF, program the CONTROL.CONTROL_DEVCONF1[12] + * I2C1HSMASTER bit for I2C1, the CONTROL.CONTROL_DEVCONF1[13] + * I2C2HSMASTER bit for I2C2, or the CONTROL.CONTROL_DEVCONF1[14] + * I2C3HSMASTER bit for I2C3. + */ + + /* 6. Configure the Own Address of the I2C controller by storing it in the + * I2Ci.I2C_OA0 register. Up to four Own Addresses can be programmed in + * the I2Ci.I2C_OAi registers (with I = 0, 1, 2, 3) for each I2C + * controller. + * + * Note: For a 10-bit address, set the corresponding expand Own Address bit + * in the I2Ci.I2C_CON register. + */ + + /* Driver currently always in single master mode so ignore this step */ + + /* 7. Set the TX threshold (in transmitter mode) and the RX threshold (in + * receiver mode) by setting the I2Ci.I2C_BUF[5:0]XTRSH field to (TX + * threshold - 1) and the I2Ci.I2C_BUF[13:8]RTRSH field to (RX threshold + * - 1), where the TX and RX thresholds are greater than or equal to 1. + */ + + /* Set the FIFO buffer threshold, note I2C1 & I2C2 have 8 byte FIFO, whereas + * I2C3 has 64 bytes. Threshold set to 5 for now. + */ + ti_i2c_write_reg(sc, I2C_REG_BUF, 0x0404); + + /* + * 8. Take the I2C controller out of reset by setting the I2Ci.I2C_CON[15] + * I2C_EN bit to 1. + */ + ti_i2c_write_reg(sc, I2C_REG_CON, I2C_CON_I2C_EN | I2C_CON_OPMODE_STD); + + /* + * To initialize the I2C controller, perform the following steps: + * + * 1. Configure the I2Ci.I2C_CON register: + * · For master or slave mode, set the I2Ci.I2C_CON[10] MST bit (0: slave, + * 1: master). + * · For transmitter or receiver mode, set the I2Ci.I2C_CON[9] TRX bit + * (0: receiver, 1: transmitter). + */ + con_reg = ti_i2c_read_reg(sc, I2C_REG_CON); + con_reg |= I2C_CON_MST; + ti_i2c_write_reg(sc, I2C_REG_CON, con_reg); + + /* 2. If using an interrupt to transmit/receive data, set to 1 the + * corresponding bit in the I2Ci.I2C_IE register (the I2Ci.I2C_IE[4] + * XRDY_IE bit for the transmit interrupt, the I2Ci.I2C_IE[3] RRDY bit + * for the receive interrupt). + */ + ti_i2c_set_intr_enable(sc, I2C_IE_XRDY | I2C_IE_RRDY); + + /* 3. If using DMA to receive/transmit data, set to 1 the corresponding bit + * in the I2Ci.I2C_BUF register (the I2Ci.I2C_BUF[15] RDMA_EN bit for the + * receive DMA channel, the I2Ci.I2C_BUF[7] XDMA_EN bit for the transmit + * DMA channel). + */ + + /* not using DMA for now, so ignore this */ + + return (0); +} + +/** + * ti_i2c_deactivate - deactivates the controller and releases resources + * @dev: i2c device handle + * + * + * + * LOCKING: + * Assumed called in an atomic context. + * + * RETURNS: + * nothing + */ +static void +ti_i2c_deactivate(device_t dev) +{ + struct ti_i2c_softc *sc = device_get_softc(dev); + clk_ident_t clk; + + /* Disable the controller - cancel all transactions */ + ti_i2c_write_reg(sc, I2C_REG_CON, 0x0000); + + /* Release the interrupt handler */ + if (sc->sc_irq_h) { + bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_irq_h); + sc->sc_irq_h = 0; + } + + bus_generic_detach(sc->sc_dev); + + /* Unmap the I2C controller registers */ + if (sc->sc_mem_res != 0) { + bus_release_resource(dev, SYS_RES_MEMORY, rman_get_rid(sc->sc_irq_res), + sc->sc_mem_res); + sc->sc_mem_res = NULL; + } + + /* Release the IRQ resource */ + if (sc->sc_irq_res != NULL) { + bus_release_resource(dev, SYS_RES_IRQ, rman_get_rid(sc->sc_irq_res), + sc->sc_irq_res); + sc->sc_irq_res = NULL; + } + + /* Finally disable the functional and interface clocks */ + clk = I2C0_CLK + sc->device_id; + ti_prcm_clk_disable(clk); + + return; +} + +/** + * ti_i2c_probe - probe function for the driver + * @dev: i2c device handle + * + * + * + * LOCKING: + * + * + * RETURNS: + * Always returns 0 + */ +static int +ti_i2c_probe(device_t dev) +{ + if (!ofw_bus_is_compatible(dev, "ti,i2c")) + return (ENXIO); + + device_set_desc(dev, "TI I2C Controller"); + return (0); +} + +/** + * ti_i2c_attach - attach function for the driver + * @dev: i2c device handle + * + * Initialised driver data structures and activates the I2C controller. + * + * LOCKING: + * + * + * RETURNS: + * + */ +static int +ti_i2c_attach(device_t dev) +{ + struct ti_i2c_softc *sc = device_get_softc(dev); + phandle_t node; + pcell_t did; + int err; + int rid; + + sc->sc_dev = dev; + + /* Get the i2c device id from FDT */ + node = ofw_bus_get_node(dev); + if ((OF_getprop(node, "i2c-device-id", &did, sizeof(did))) <= 0) { + device_printf(dev, "missing i2c-device-id attribute in FDT\n"); + return (ENXIO); + } + sc->device_id = fdt32_to_cpu(did); + + TI_I2C_LOCK_INIT(sc); + + /* Get the memory resource for the register mapping */ + rid = 0; + sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (sc->sc_mem_res == NULL) + panic("%s: Cannot map registers", device_get_name(dev)); + + /* Allocate an IRQ resource for the MMC controller */ + rid = 0; + sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_ACTIVE | RF_SHAREABLE); + if (sc->sc_irq_res == NULL) { + err = ENOMEM; + goto out; + } + + /* XXXOMAP3: FIXME get proper revision here */ + /* First read the version number of the I2C module */ + sc->sc_rev = ti_i2c_read_2(sc, I2C_REG_REVNB_HI) & 0xff; + + device_printf(dev, "I2C revision %d.%d\n", sc->sc_rev >> 4, + sc->sc_rev & 0xf); + + /* Activate the H/W */ + err = ti_i2c_activate(dev); + if (err) { + device_printf(dev, "ti_i2c_activate failed\n"); + goto out; + } + + /* activate the interrupt */ + err = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE, + NULL, ti_i2c_intr, sc, &sc->sc_irq_h); + if (err) + goto out; + + /* Attach to the iicbus */ + if ((sc->sc_iicbus = device_add_child(dev, "iicbus", -1)) == NULL) + device_printf(dev, "could not allocate iicbus instance\n"); + + /* Probe and attach the iicbus */ + bus_generic_attach(dev); + +out: + if (err) { + ti_i2c_deactivate(dev); + TI_I2C_LOCK_DESTROY(sc); + } + + return (err); +} + +/** + * ti_i2c_detach - detach function for the driver + * @dev: i2c device handle + * + * + * + * LOCKING: + * + * + * RETURNS: + * Always returns 0 + */ +static int +ti_i2c_detach(device_t dev) +{ + struct ti_i2c_softc *sc = device_get_softc(dev); + int rv; + + ti_i2c_deactivate(dev); + + if (sc->sc_iicbus && (rv = device_delete_child(dev, sc->sc_iicbus)) != 0) + return (rv); + + TI_I2C_LOCK_DESTROY(sc); + + return (0); +} + + +static phandle_t +ti_i2c_get_node(device_t bus, device_t dev) +{ + /* + * Share controller node with iibus device + */ + return ofw_bus_get_node(bus); +} + +static device_method_t ti_i2c_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ti_i2c_probe), + DEVMETHOD(device_attach, ti_i2c_attach), + DEVMETHOD(device_detach, ti_i2c_detach), + + /* OFW methods */ + DEVMETHOD(ofw_bus_get_node, ti_i2c_get_node), + + /* iicbus interface */ + DEVMETHOD(iicbus_callback, ti_i2c_callback), + DEVMETHOD(iicbus_reset, ti_i2c_reset), + DEVMETHOD(iicbus_transfer, ti_i2c_transfer), + { 0, 0 } +}; + +static driver_t ti_i2c_driver = { + "iichb", + ti_i2c_methods, + sizeof(struct ti_i2c_softc), +}; + +DRIVER_MODULE(ti_iic, simplebus, ti_i2c_driver, ti_i2c_devclass, 0, 0); +DRIVER_MODULE(iicbus, ti_iic, iicbus_driver, iicbus_devclass, 0, 0); + +MODULE_DEPEND(ti_iic, ti_prcm, 1, 1, 1); +MODULE_DEPEND(ti_iic, iicbus, 1, 1, 1); diff --git a/sys/arm/ti/ti_i2c.h b/sys/arm/ti/ti_i2c.h new file mode 100644 index 000000000000..f569f875fc5e --- /dev/null +++ b/sys/arm/ti/ti_i2c.h @@ -0,0 +1,115 @@ +/*- + * 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_I2C_H_ +#define _TI_I2C_H_ + +/** + * Header file for the OMAP I2C driver. + * + * Simply contains register bit flags. + */ + +/* + * OMAP4 I2C Registers, Summary 1 + */ +#define I2C_REG_IE 0x84 +#define I2C_IE_XDR (1UL << 14) /* Transmit draining interrupt */ +#define I2C_IE_RDR (1UL << 13) /* Receive draining interrupt */ +#define I2C_IE_AAS (1UL << 9) /* Addressed as Slave interrupt */ +#define I2C_IE_BF (1UL << 8) /* Bus Free interrupt */ +#define I2C_IE_AERR (1UL << 7) /* Access Error interrupt */ +#define I2C_IE_STC (1UL << 6) /* Start Condition interrupt */ +#define I2C_IE_GC (1UL << 5) /* General Call interrupt */ +#define I2C_IE_XRDY (1UL << 4) /* Transmit Data Ready interrupt */ +#define I2C_IE_RRDY (1UL << 3) /* Receive Data Ready interrupt */ +#define I2C_IE_ARDY (1UL << 2) /* Register Access Ready interrupt */ +#define I2C_IE_NACK (1UL << 1) /* No Acknowledgment interrupt */ +#define I2C_IE_AL (1UL << 0) /* Arbitration Lost interrupt */ +#define I2C_REG_STAT 0x88 +#define I2C_STAT_XDR (1UL << 14) +#define I2C_STAT_RDR (1UL << 13) +#define I2C_STAT_BB (1UL << 12) +#define I2C_STAT_ROVR (1UL << 11) +#define I2C_STAT_XUDF (1UL << 10) +#define I2C_STAT_AAS (1UL << 9) +#define I2C_STAT_BF (1UL << 8) +#define I2C_STAT_AERR (1UL << 7) +#define I2C_STAT_STC (1UL << 6) +#define I2C_STAT_GC (1UL << 5) +#define I2C_STAT_XRDY (1UL << 4) +#define I2C_STAT_RRDY (1UL << 3) +#define I2C_STAT_ARDY (1UL << 2) +#define I2C_STAT_NACK (1UL << 1) +#define I2C_STAT_AL (1UL << 0) +#define I2C_REG_SYSS 0x90 +#define I2C_REG_BUF 0x94 +#define I2C_REG_CNT 0x98 +#define I2C_REG_DATA 0x9c +#define I2C_REG_CON 0xa4 +#define I2C_CON_I2C_EN (1UL << 15) +#define I2C_CON_OPMODE_STD (0UL << 12) +#define I2C_CON_OPMODE_HS (1UL << 12) +#define I2C_CON_OPMODE_SCCB (2UL << 12) +#define I2C_CON_OPMODE_MASK (3UL << 13) +#define I2C_CON_I2C_STB (1UL << 11) +#define I2C_CON_MST (1UL << 10) +#define I2C_CON_TRX (1UL << 9) +#define I2C_CON_XSA (1UL << 8) +#define I2C_CON_XOA0 (1UL << 7) +#define I2C_CON_XOA1 (1UL << 6) +#define I2C_CON_XOA2 (1UL << 5) +#define I2C_CON_XOA3 (1UL << 4) +#define I2C_CON_STP (1UL << 1) +#define I2C_CON_STT (1UL << 0) +#define I2C_REG_OA0 0xa8 +#define I2C_REG_SA 0xac +#define I2C_REG_PSC 0xb0 +#define I2C_REG_SCLL 0xb4 +#define I2C_REG_SCLH 0xb8 +#define I2C_REG_SYSTEST 0xbc +#define I2C_REG_BUFSTAT 0xc0 +#define I2C_REG_OA1 0xc4 +#define I2C_REG_OA2 0xc8 +#define I2C_REG_OA3 0xcc +#define I2C_REG_ACTOA 0xd0 +#define I2C_REG_SBLOCK 0xd4 + +/* + * OMAP4 I2C Registers, Summary 2 + */ +#define I2C_REG_REVNB_LO 0x00 +#define I2C_REG_REVNB_HI 0x04 +#define I2C_REG_SYSC 0x10 +#define I2C_REG_IRQENABLE_SET 0x2C +#define I2C_REG_IRQENABLE_CLR 0x30 + + + +#endif /* _TI_I2C_H_ */ diff --git a/sys/arm/ti/ti_machdep.c b/sys/arm/ti/ti_machdep.c new file mode 100644 index 000000000000..3a5fe85600e1 --- /dev/null +++ b/sys/arm/ti/ti_machdep.c @@ -0,0 +1,618 @@ +/*- + * Copyright (c) 1994-1998 Mark Brinicombe. + * Copyright (c) 1994 Brini. + * All rights reserved. + * + * This code is derived from software written for Brini by Mark Brinicombe + * + * 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 Brini. + * 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 BRINI ``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 BRINI 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. + * + * from: FreeBSD: //depot/projects/arm/src/sys/arm/at91/kb920x_machdep.c, rev 45 + */ + +#include "opt_ddb.h" +#include "opt_platform.h" +#include "opt_global.h" + +#include +__FBSDID("$FreeBSD$"); + +#define _ARM32_BUS_DMA_PRIVATE +#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 +#include +#include +#include +#include +#include +#include + +#include + +#define DEBUG +#ifdef DEBUG +#define debugf(fmt, args...) printf(fmt, ##args) +#else +#define debugf(fmt, args...) +#endif + +/* Start of address space used for bootstrap map */ +#define DEVMAP_BOOTSTRAP_MAP_START 0xE0000000 + +/* + * This is the number of L2 page tables required for covering max + * (hypothetical) memsize of 4GB and all kernel mappings (vectors, msgbuf, + * stacks etc.), uprounded to be divisible by 4. + */ +#define KERNEL_PT_MAX 78 + +/* Define various stack sizes in pages */ +#define IRQ_STACK_SIZE 1 +#define ABT_STACK_SIZE 1 +#define UND_STACK_SIZE 1 + +extern unsigned char kernbase[]; +extern unsigned char _etext[]; +extern unsigned char _edata[]; +extern unsigned char __bss_start[]; +extern unsigned char _end[]; + +#ifdef DDB +extern vm_offset_t ksym_start, ksym_end; +#endif + +extern u_int data_abort_handler_address; +extern u_int prefetch_abort_handler_address; +extern u_int undefined_handler_address; + +extern vm_offset_t pmap_bootstrap_lastaddr; +extern int *end; + +struct pv_addr kernel_pt_table[KERNEL_PT_MAX]; + +/* Physical and virtual addresses for some global pages */ +vm_paddr_t phys_avail[10]; +vm_paddr_t dump_avail[4]; +vm_offset_t physical_pages; +vm_offset_t pmap_bootstrap_lastaddr; +vm_paddr_t pmap_pa; + +const struct pmap_devmap *pmap_devmap_bootstrap_table; +struct pv_addr systempage; +struct pv_addr msgbufpv; +struct pv_addr irqstack; +struct pv_addr undstack; +struct pv_addr abtstack; +struct pv_addr kernelstack; + +void set_stackptrs(int cpu); + +static struct mem_region availmem_regions[FDT_MEM_REGIONS]; +static int availmem_regions_sz; + +static void print_kenv(void); +static void print_kernel_section_addr(void); + +static void physmap_init(void); +static int platform_devmap_init(void); +void (*ti_cpu_reset)(void); + +static char * +kenv_next(char *cp) +{ + + if (cp != NULL) { + while (*cp != 0) + cp++; + cp++; + if (*cp == 0) + cp = NULL; + } + return (cp); +} + +static void +print_kenv(void) +{ + int len; + char *cp; + + debugf("loader passed (static) kenv:\n"); + if (kern_envp == NULL) { + debugf(" no env, null ptr\n"); + return; + } + debugf(" kern_envp = 0x%08x\n", (uint32_t)kern_envp); + + len = 0; + for (cp = kern_envp; cp != NULL; cp = kenv_next(cp)) + debugf(" %x %s\n", (uint32_t)cp, cp); +} + +static void +print_kernel_section_addr(void) +{ + + debugf("kernel image addresses:\n"); + debugf(" kernbase = 0x%08x\n", (uint32_t)kernbase); + debugf(" _etext (sdata) = 0x%08x\n", (uint32_t)_etext); + debugf(" _edata = 0x%08x\n", (uint32_t)_edata); + debugf(" __bss_start = 0x%08x\n", (uint32_t)__bss_start); + debugf(" _end = 0x%08x\n", (uint32_t)_end); +} + +static void +physmap_init(void) +{ + int i, j, cnt; + vm_offset_t phys_kernelend, kernload; + uint32_t s, e, sz; + struct mem_region *mp, *mp1; + + phys_kernelend = KERNPHYSADDR + (virtual_avail - KERNVIRTADDR); + kernload = KERNPHYSADDR; + ti_cpu_reset = NULL; + + /* + * Remove kernel physical address range from avail + * regions list. Page align all regions. + * Non-page aligned memory isn't very interesting to us. + * Also, sort the entries for ascending addresses. + */ + sz = 0; + cnt = availmem_regions_sz; + debugf("processing avail regions:\n"); + for (mp = availmem_regions; mp->mr_size; mp++) { + s = mp->mr_start; + e = mp->mr_start + mp->mr_size; + debugf(" %08x-%08x -> ", s, e); + /* Check whether this region holds all of the kernel. */ + if (s < kernload && e > phys_kernelend) { + availmem_regions[cnt].mr_start = phys_kernelend; + availmem_regions[cnt++].mr_size = e - phys_kernelend; + e = kernload; + } + /* Look whether this regions starts within the kernel. */ + if (s >= kernload && s < phys_kernelend) { + if (e <= phys_kernelend) + goto empty; + s = phys_kernelend; + } + /* Now look whether this region ends within the kernel. */ + if (e > kernload && e <= phys_kernelend) { + if (s >= kernload) { + goto empty; + } + e = kernload; + } + /* Now page align the start and size of the region. */ + s = round_page(s); + e = trunc_page(e); + if (e < s) + e = s; + sz = e - s; + debugf("%08x-%08x = %x\n", s, e, sz); + + /* Check whether some memory is left here. */ + if (sz == 0) { + empty: + printf("skipping\n"); + bcopy(mp + 1, mp, + (cnt - (mp - availmem_regions)) * sizeof(*mp)); + cnt--; + mp--; + continue; + } + + /* Do an insertion sort. */ + for (mp1 = availmem_regions; mp1 < mp; mp1++) + if (s < mp1->mr_start) + break; + if (mp1 < mp) { + bcopy(mp1, mp1 + 1, (char *)mp - (char *)mp1); + mp1->mr_start = s; + mp1->mr_size = sz; + } else { + mp->mr_start = s; + mp->mr_size = sz; + } + } + availmem_regions_sz = cnt; + + /* Fill in phys_avail table, based on availmem_regions */ + debugf("fill in phys_avail:\n"); + for (i = 0, j = 0; i < availmem_regions_sz; i++, j += 2) { + + debugf(" region: 0x%08x - 0x%08x (0x%08x)\n", + availmem_regions[i].mr_start, + availmem_regions[i].mr_start + availmem_regions[i].mr_size, + availmem_regions[i].mr_size); + + phys_avail[j] = availmem_regions[i].mr_start; + phys_avail[j + 1] = availmem_regions[i].mr_start + + availmem_regions[i].mr_size; + } + phys_avail[j] = 0; + phys_avail[j + 1] = 0; +} + +void * +initarm(struct arm_boot_params *abp) +{ + struct pv_addr kernel_l1pt; + struct pv_addr dpcpu; + vm_offset_t dtbp, freemempos, l2_start, lastaddr; + uint32_t memsize, l2size; + void *kmdp; + u_int l1pagetable; + int i = 0, j = 0, err_devmap = 0; + + lastaddr = parse_boot_param(abp); + memsize = 0; + set_cpufuncs(); + + + kmdp = preload_search_by_type("elf kernel"); + if (kmdp != NULL) + dtbp = MD_FETCH(kmdp, MODINFOMD_DTBP, vm_offset_t); + else + dtbp = (vm_offset_t)NULL; + +#if defined(FDT_DTB_STATIC) + /* + * In case the device tree blob was not retrieved (from metadata) try + * to use the statically embedded one. + */ + if (dtbp == (vm_offset_t)NULL) + dtbp = (vm_offset_t)&fdt_static_dtb; +#endif + + if (OF_install(OFW_FDT, 0) == FALSE) + while (1); + + if (OF_init((void *)dtbp) != 0) + while (1); + + /* Grab physical memory regions information from device tree. */ + if (fdt_get_mem_regions(availmem_regions, &availmem_regions_sz, + &memsize) != 0) + while(1); + +// if (fdt_immr_addr(OMAP44XX_L4_PERIPH_VBASE) != 0) +// while (1); + + /* Platform-specific initialisation */ + pmap_bootstrap_lastaddr = DEVMAP_BOOTSTRAP_MAP_START - ARM_NOCACHE_KVA_SIZE; + + pcpu0_init(); + + /* Calculate number of L2 tables needed for mapping vm_page_array */ + l2size = (memsize / PAGE_SIZE) * sizeof(struct vm_page); + l2size = (l2size >> L1_S_SHIFT) + 1; + + /* + * Add one table for end of kernel map, one for stacks, msgbuf and + * L1 and L2 tables map and one for vectors map. + */ + l2size += 3; + + /* Make it divisible by 4 */ + l2size = (l2size + 3) & ~3; + +#define KERNEL_TEXT_BASE (KERNBASE) + freemempos = (lastaddr + PAGE_MASK) & ~PAGE_MASK; + + /* Define a macro to simplify memory allocation */ +#define valloc_pages(var, np) \ + alloc_pages((var).pv_va, (np)); \ + (var).pv_pa = (var).pv_va + (KERNPHYSADDR - KERNVIRTADDR); + +#define alloc_pages(var, np) \ + (var) = freemempos; \ + freemempos += (np * PAGE_SIZE); \ + memset((char *)(var), 0, ((np) * PAGE_SIZE)); + + while (((freemempos - L1_TABLE_SIZE) & (L1_TABLE_SIZE - 1)) != 0) + freemempos += PAGE_SIZE; + valloc_pages(kernel_l1pt, L1_TABLE_SIZE / PAGE_SIZE); + + for (i = 0; i < l2size; ++i) { + if (!(i % (PAGE_SIZE / L2_TABLE_SIZE_REAL))) { + valloc_pages(kernel_pt_table[i], + L2_TABLE_SIZE / PAGE_SIZE); + j = i; + } else { + kernel_pt_table[i].pv_va = kernel_pt_table[j].pv_va + + L2_TABLE_SIZE_REAL * (i - j); + kernel_pt_table[i].pv_pa = + kernel_pt_table[i].pv_va - KERNVIRTADDR + + KERNPHYSADDR; + + } + } + /* + * Allocate a page for the system page mapped to 0x00000000 + * or 0xffff0000. This page will just contain the system vectors + * and can be shared by all processes. + */ + valloc_pages(systempage, 1); + + /* Allocate dynamic per-cpu area. */ + valloc_pages(dpcpu, DPCPU_SIZE / PAGE_SIZE); + dpcpu_init((void *)dpcpu.pv_va, 0); + + /* Allocate stacks for all modes */ + valloc_pages(irqstack, (IRQ_STACK_SIZE * MAXCPU)); + valloc_pages(abtstack, (ABT_STACK_SIZE * MAXCPU)); + valloc_pages(undstack, (UND_STACK_SIZE * MAXCPU)); + valloc_pages(kernelstack, (KSTACK_PAGES * MAXCPU)); + + init_param1(); + + valloc_pages(msgbufpv, round_page(msgbufsize) / PAGE_SIZE); + + /* + * Now we start construction of the L1 page table + * We start by mapping the L2 page tables into the L1. + * This means that we can replace L1 mappings later on if necessary + */ + l1pagetable = kernel_l1pt.pv_va; + + /* + * Try to map as much as possible of kernel text and data using + * 1MB section mapping and for the rest of initial kernel address + * space use L2 coarse tables. + * + * Link L2 tables for mapping remainder of kernel (modulo 1MB) + * and kernel structures + */ + l2_start = lastaddr & ~(L1_S_OFFSET); + for (i = 0 ; i < l2size - 1; i++) + pmap_link_l2pt(l1pagetable, l2_start + i * L1_S_SIZE, + &kernel_pt_table[i]); + + pmap_curmaxkvaddr = l2_start + (l2size - 1) * L1_S_SIZE; + + /* Map kernel code and data */ + pmap_map_chunk(l1pagetable, KERNVIRTADDR, KERNPHYSADDR, + (((uint32_t)(lastaddr) - KERNVIRTADDR) + PAGE_MASK) & ~PAGE_MASK, + VM_PROT_READ|VM_PROT_WRITE, PTE_CACHE); + + + /* Map L1 directory and allocated L2 page tables */ + pmap_map_chunk(l1pagetable, kernel_l1pt.pv_va, kernel_l1pt.pv_pa, + L1_TABLE_SIZE, VM_PROT_READ|VM_PROT_WRITE, PTE_PAGETABLE); + + pmap_map_chunk(l1pagetable, kernel_pt_table[0].pv_va, + kernel_pt_table[0].pv_pa, + L2_TABLE_SIZE_REAL * l2size, + VM_PROT_READ|VM_PROT_WRITE, PTE_PAGETABLE); + + /* Map allocated DPCPU, stacks and msgbuf */ + pmap_map_chunk(l1pagetable, dpcpu.pv_va, dpcpu.pv_pa, + freemempos - dpcpu.pv_va, + VM_PROT_READ|VM_PROT_WRITE, PTE_CACHE); + + /* Link and map the vector page */ + pmap_link_l2pt(l1pagetable, ARM_VECTORS_HIGH, + &kernel_pt_table[l2size - 1]); + pmap_map_entry(l1pagetable, ARM_VECTORS_HIGH, systempage.pv_pa, + VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE, PTE_CACHE); + + /* Map pmap_devmap[] entries */ + err_devmap = platform_devmap_init(); + pmap_devmap_bootstrap(l1pagetable, pmap_devmap_bootstrap_table); + + cpu_domains((DOMAIN_CLIENT << (PMAP_DOMAIN_KERNEL * 2)) | + DOMAIN_CLIENT); + pmap_pa = kernel_l1pt.pv_pa; + setttb(kernel_l1pt.pv_pa); + cpu_tlb_flushID(); + cpu_domains(DOMAIN_CLIENT << (PMAP_DOMAIN_KERNEL * 2)); + + /* + * Only after the SOC registers block is mapped we can perform device + * tree fixups, as they may attempt to read parameters from hardware. + */ + OF_interpret("perform-fixup", 0); + + cninit(); + + physmem = memsize / PAGE_SIZE; + + debugf("initarm: console initialized\n"); + debugf(" arg1 kmdp = 0x%08x\n", (uint32_t)kmdp); + debugf(" boothowto = 0x%08x\n", boothowto); + debugf(" dtbp = 0x%08x\n", (uint32_t)dtbp); + print_kernel_section_addr(); + print_kenv(); + + if (err_devmap != 0) + printf("WARNING: could not fully configure devmap, error=%d\n", + err_devmap); + + /* + * Pages were allocated during the secondary bootstrap for the + * stacks for different CPU modes. + * We must now set the r13 registers in the different CPU modes to + * point to these stacks. + * Since the ARM stacks use STMFD etc. we must set r13 to the top end + * of the stack memory. + */ + cpu_control(CPU_CONTROL_MMU_ENABLE, CPU_CONTROL_MMU_ENABLE); + + set_stackptrs(0); + + /* + * We must now clean the cache again.... + * Cleaning may be done by reading new data to displace any + * dirty data in the cache. This will have happened in setttb() + * but since we are boot strapping the addresses used for the read + * may have just been remapped and thus the cache could be out + * of sync. A re-clean after the switch will cure this. + * After booting there are no gross relocations of the kernel thus + * this problem will not occur after initarm(). + */ + cpu_idcache_wbinv_all(); + + /* Set stack for exception handlers */ + data_abort_handler_address = (u_int)data_abort_handler; + prefetch_abort_handler_address = (u_int)prefetch_abort_handler; + undefined_handler_address = (u_int)undefinedinstruction_bounce; + undefined_init(); + + init_proc0(kernelstack.pv_va); + arm_vector_init(ARM_VECTORS_HIGH, ARM_VEC_ALL); + + arm_dump_avail_init(memsize, sizeof(dump_avail) / sizeof(dump_avail[0])); + pmap_bootstrap(freemempos, pmap_bootstrap_lastaddr, &kernel_l1pt); + msgbufp = (void *)msgbufpv.pv_va; + msgbufinit(msgbufp, msgbufsize); + mutex_init(); + + /* + * Prepare map of physical memory regions available to vm subsystem. + */ + physmap_init(); + + /* Do basic tuning, hz etc */ + init_param2(physmem); + kdb_init(); + + return ((void *)(kernelstack.pv_va + USPACE_SVC_STACK_TOP - + sizeof(struct pcb))); +} + +void +set_stackptrs(int cpu) +{ + + set_stackptr(PSR_IRQ32_MODE, + irqstack.pv_va + ((IRQ_STACK_SIZE * PAGE_SIZE) * (cpu + 1))); + set_stackptr(PSR_ABT32_MODE, + abtstack.pv_va + ((ABT_STACK_SIZE * PAGE_SIZE) * (cpu + 1))); + set_stackptr(PSR_UND32_MODE, + undstack.pv_va + ((UND_STACK_SIZE * PAGE_SIZE) * (cpu + 1))); +} + +#define FDT_DEVMAP_MAX (2) // FIXME +static struct pmap_devmap fdt_devmap[FDT_DEVMAP_MAX] = { + { 0, 0, 0, 0, 0, } +}; + + +/* + * Construct pmap_devmap[] with DT-derived config data. + */ +static int +platform_devmap_init(void) +{ + int i = 0; +#if defined(SOC_OMAP4) + fdt_devmap[i].pd_va = 0xE8000000; + fdt_devmap[i].pd_pa = 0x48000000; + fdt_devmap[i].pd_size = 0x1000000; + fdt_devmap[i].pd_prot = VM_PROT_READ | VM_PROT_WRITE; + fdt_devmap[i].pd_cache = PTE_DEVICE; + i++; +#elif defined(SOC_TI_AM335X) + fdt_devmap[i].pd_va = 0xE4C00000; + fdt_devmap[i].pd_pa = 0x44C00000; /* L4_WKUP */ + fdt_devmap[i].pd_size = 0x400000; /* 4 MB */ + fdt_devmap[i].pd_prot = VM_PROT_READ | VM_PROT_WRITE; + fdt_devmap[i].pd_cache = PTE_DEVICE; + i++; +#else +#error "Unknown SoC" +#endif + + pmap_devmap_bootstrap_table = &fdt_devmap[0]; + return (0); +} + +struct arm32_dma_range * +bus_dma_get_range(void) +{ + + return (NULL); +} + +int +bus_dma_get_range_nb(void) +{ + + return (0); +} + +void +cpu_reset() +{ + if (ti_cpu_reset) + (*ti_cpu_reset)(); + else + printf("no cpu_reset implementation\n"); + printf("Reset failed!\n"); + while (1); +} + diff --git a/sys/arm/ti/ti_mmchs.c b/sys/arm/ti/ti_mmchs.c new file mode 100644 index 000000000000..cf9dc214a686 --- /dev/null +++ b/sys/arm/ti/ti_mmchs.c @@ -0,0 +1,1839 @@ +/*- + * 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. + */ + +/** + * Driver for the MMC/SD/SDIO module on the TI OMAP series of SoCs. + * + * This driver is heavily based on the SD/MMC driver for the AT91 (at91_mci.c). + * + * It's important to realise that the MMC state machine is already in the kernel + * and this driver only exposes the specific interfaces of the controller. + * + * This driver is still very much a work in progress, I've verified that basic + * sector reading can be performed. But I've yet to test it with a file system + * or even writing. In addition I've only tested the driver with an SD card, + * I've no idea if MMC cards work. + * + */ +#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 "gpio_if.h" + +#include "mmcbr_if.h" +#include "mmcbus_if.h" + +#include +#include +#include +#include +#include + +#include +#include + +#ifdef DEBUG +#define ti_mmchs_dbg(sc, fmt, args...) \ + device_printf((sc)->sc_dev, fmt, ## args); +#else +#define ti_mmchs_dbg(sc, fmt, args...) +#endif + +/** + * Structure that stores the driver context + */ +struct ti_mmchs_softc { + device_t sc_dev; + uint32_t device_id; + struct resource* sc_irq_res; + struct resource* sc_mem_res; + + void* sc_irq_h; + + bus_dma_tag_t sc_dmatag; + bus_dmamap_t sc_dmamap; + int sc_dmamapped; + + unsigned int sc_dmach_rd; + unsigned int sc_dmach_wr; + int dma_rx_trig; + int dma_tx_trig; + + device_t sc_gpio_dev; + int sc_wp_gpio_pin; /* GPIO pin for MMC write protect */ + + device_t sc_vreg_dev; + const char* sc_vreg_name; + + struct mtx sc_mtx; + + struct mmc_host host; + struct mmc_request* req; + struct mmc_command* curcmd; + + int flags; +#define CMD_STARTED 1 +#define STOP_STARTED 2 + + int bus_busy; /* TODO: Needed ? */ + + void* sc_cmd_data_vaddr; + int sc_cmd_data_len; + + /* The offset applied to each of the register base addresses, OMAP4 + * register sets are offset 0x100 from the OMAP3 series. + */ + unsigned long sc_reg_off; + + /* The physical address of the MMCHS_DATA register, used for the DMA xfers */ + unsigned long sc_data_reg_paddr; + + /* The reference clock frequency */ + unsigned int sc_ref_freq; + + enum mmc_power_mode sc_cur_power_mode; +}; + +/** + * Macros for driver mutex locking + */ +#define TI_MMCHS_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) +#define TI_MMCHS_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) +#define TI_MMCHS_LOCK_INIT(_sc) \ + mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->sc_dev), \ + "ti_mmchs", MTX_DEF) +#define TI_MMCHS_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); +#define TI_MMCHS_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED); +#define TI_MMCHS_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED); + +static void ti_mmchs_start(struct ti_mmchs_softc *sc); + +/** + * ti_mmchs_read_4 - reads a 32-bit value from a register + * ti_mmchs_write_4 - writes a 32-bit value to a register + * @sc: pointer to the driver context + * @off: register offset to read from + * @val: the value to write into the register + * + * LOCKING: + * None + * + * RETURNS: + * The 32-bit value read from the register + */ +static inline uint32_t +ti_mmchs_read_4(struct ti_mmchs_softc *sc, bus_size_t off) +{ + return bus_read_4(sc->sc_mem_res, (sc->sc_reg_off + off)); +} + +static inline void +ti_mmchs_write_4(struct ti_mmchs_softc *sc, bus_size_t off, uint32_t val) +{ + bus_write_4(sc->sc_mem_res, (sc->sc_reg_off + off), val); +} + +/** + * ti_mmchs_reset_controller - + * @arg: caller supplied arg + * @segs: array of segments (although in our case should only be one) + * @nsegs: number of segments (in our case should be 1) + * @error: + * + * + * + */ +static void +ti_mmchs_reset_controller(struct ti_mmchs_softc *sc, uint32_t bit) +{ + unsigned long attempts; + uint32_t sysctl; + + ti_mmchs_dbg(sc, "reseting controller - bit 0x%08x\n", bit); + + sysctl = ti_mmchs_read_4(sc, MMCHS_SYSCTL); + ti_mmchs_write_4(sc, MMCHS_SYSCTL, sysctl | bit); + + if ((ti_chip() == CHIP_OMAP_4) && (ti_revision() > OMAP4430_REV_ES1_0)) { + /* OMAP4 ES2 and greater has an updated reset logic. + * Monitor a 0->1 transition first + */ + attempts = 10000; + while (!(ti_mmchs_read_4(sc, MMCHS_SYSCTL) & bit) && (attempts-- > 0)) + continue; + } + + attempts = 10000; + while ((ti_mmchs_read_4(sc, MMCHS_SYSCTL) & bit) && (attempts-- > 0)) + continue; + + if (ti_mmchs_read_4(sc, MMCHS_SYSCTL) & bit) + device_printf(sc->sc_dev, "Error - Timeout waiting on controller reset\n"); +} + +/** + * ti_mmchs_getaddr - called by the DMA function to simply return the phys addr + * @arg: caller supplied arg + * @segs: array of segments (although in our case should only be one) + * @nsegs: number of segments (in our case should be 1) + * @error: + * + * This function is called by bus_dmamap_load() after it has compiled an array + * of segments, each segment is a phsyical chunk of memory. However in our case + * we should only have one segment, because we don't (yet?) support DMA scatter + * gather. To ensure we only have one segment, the DMA tag was created by + * bus_dma_tag_create() (called from ti_mmchs_attach) with nsegments set to 1. + * + */ +static void +ti_mmchs_getaddr(void *arg, bus_dma_segment_t *segs, int nsegs, int error) +{ + if (error != 0) + return; + + *(bus_addr_t *)arg = segs[0].ds_addr; +} + +#ifndef SOC_TI_AM335X +/** + * ti_mmchs_dma_intr - interrupt handler for DMA events triggered by the controller + * @ch: the dma channel number + * @status: bit field of the status bytes + * @data: callback data, in this case a pointer to the controller struct + * + * + * LOCKING: + * Called from interrupt context + * + */ +static void +ti_mmchs_dma_intr(unsigned int ch, uint32_t status, void *data) +{ + /* Ignore for now ... we don't need this interrupt as we already have the + * interrupt from the MMC controller. + */ +} +#endif + +/** + * ti_mmchs_intr_xfer_compl - called if a 'transfer complete' IRQ was received + * @sc: pointer to the driver context + * @cmd: the command that was sent previously + * + * This function is simply responsible for syncing up the DMA buffer. + * + * LOCKING: + * Called from interrupt context + * + * RETURNS: + * Return value indicates if the transaction is complete, not done = 0, done != 0 + */ +static int +ti_mmchs_intr_xfer_compl(struct ti_mmchs_softc *sc, struct mmc_command *cmd) +{ + uint32_t cmd_reg; + + /* Read command register to test whether this command was a read or write. */ + cmd_reg = ti_mmchs_read_4(sc, MMCHS_CMD); + + /* Sync-up the DMA buffer so the caller can access the new memory */ + if (cmd_reg & MMCHS_CMD_DDIR) { + bus_dmamap_sync(sc->sc_dmatag, sc->sc_dmamap, BUS_DMASYNC_POSTREAD); + bus_dmamap_unload(sc->sc_dmatag, sc->sc_dmamap); + } + else { + bus_dmamap_sync(sc->sc_dmatag, sc->sc_dmamap, BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->sc_dmatag, sc->sc_dmamap); + } + sc->sc_dmamapped--; + + /* Debugging dump of the data received */ +#if 0 + { + int i; + uint8_t *p = (uint8_t*) sc->sc_cmd_data_vaddr; + for (i=0; isc_cmd_data_len; i++) { + if ((i % 16) == 0) + printf("\n0x%04x : ", i); + printf("%02X ", *p++); + } + printf("\n"); + } +#endif + + /* We are done, transfer complete */ + return 1; +} + +/** + * ti_mmchs_intr_cmd_compl - called if a 'command complete' IRQ was received + * @sc: pointer to the driver context + * @cmd: the command that was sent previously + * + * + * LOCKING: + * Called from interrupt context + * + * RETURNS: + * Return value indicates if the transaction is complete, not done = 0, done != 0 + */ +static int +ti_mmchs_intr_cmd_compl(struct ti_mmchs_softc *sc, struct mmc_command *cmd) +{ + uint32_t cmd_reg; + + /* Copy the response into the request struct ... if a response was + * expected */ + if (cmd != NULL && (cmd->flags & MMC_RSP_PRESENT)) { + if (cmd->flags & MMC_RSP_136) { + cmd->resp[3] = ti_mmchs_read_4(sc, MMCHS_RSP10); + cmd->resp[2] = ti_mmchs_read_4(sc, MMCHS_RSP32); + cmd->resp[1] = ti_mmchs_read_4(sc, MMCHS_RSP54); + cmd->resp[0] = ti_mmchs_read_4(sc, MMCHS_RSP76); + } else { + cmd->resp[0] = ti_mmchs_read_4(sc, MMCHS_RSP10); + } + } + + /* Check if the command was expecting some data transfer, if not + * we are done. */ + cmd_reg = ti_mmchs_read_4(sc, MMCHS_CMD); + return ((cmd_reg & MMCHS_CMD_DP) == 0); +} + +/** + * ti_mmchs_intr_error - handles error interrupts + * @sc: pointer to the driver context + * @cmd: the command that was sent previously + * @stat_reg: the value that was in the status register + * + * + * LOCKING: + * Called from interrupt context + * + * RETURNS: + * Return value indicates if the transaction is complete, not done = 0, done != 0 + */ +static int +ti_mmchs_intr_error(struct ti_mmchs_softc *sc, struct mmc_command *cmd, + uint32_t stat_reg) +{ + ti_mmchs_dbg(sc, "error in xfer - stat 0x%08x\n", stat_reg); + + /* Ignore CRC errors on CMD2 and ACMD47, per relevant standards */ + if ((stat_reg & MMCHS_STAT_CCRC) && (cmd->opcode == MMC_SEND_OP_COND || + cmd->opcode == ACMD_SD_SEND_OP_COND)) + cmd->error = MMC_ERR_NONE; + else if (stat_reg & (MMCHS_STAT_CTO | MMCHS_STAT_DTO)) + cmd->error = MMC_ERR_TIMEOUT; + else if (stat_reg & (MMCHS_STAT_CCRC | MMCHS_STAT_DCRC)) + cmd->error = MMC_ERR_BADCRC; + else + cmd->error = MMC_ERR_FAILED; + + /* If a dma transaction we should also stop the dma transfer */ + if (ti_mmchs_read_4(sc, MMCHS_CMD) & MMCHS_CMD_DE) { + + /* Abort the DMA transfer (DDIR bit tells direction) */ + if (ti_mmchs_read_4(sc, MMCHS_CMD) & MMCHS_CMD_DDIR) +#ifdef SOC_TI_AM335X + printf("%s: DMA unimplemented\n", __func__); +#else + ti_sdma_stop_xfer(sc->sc_dmach_rd); +#endif + else +#ifdef SOC_TI_AM335X + printf("%s: DMA unimplemented\n", __func__); +#else + ti_sdma_stop_xfer(sc->sc_dmach_wr); +#endif + + /* If an error occure abort the DMA operation and free the dma map */ + if ((sc->sc_dmamapped > 0) && (cmd->error != MMC_ERR_NONE)) { + bus_dmamap_unload(sc->sc_dmatag, sc->sc_dmamap); + sc->sc_dmamapped--; + } + } + + /* Command error occured? ... if so issue a soft reset for the cmd fsm */ + if (stat_reg & (MMCHS_STAT_CCRC | MMCHS_STAT_CTO)) { + ti_mmchs_reset_controller(sc, MMCHS_SYSCTL_SRC); + } + + /* Data error occured? ... if so issue a soft reset for the data line */ + if (stat_reg & (MMCHS_STAT_DEB | MMCHS_STAT_DCRC | MMCHS_STAT_DTO)) { + ti_mmchs_reset_controller(sc, MMCHS_SYSCTL_SRD); + } + + /* On any error the command is cancelled ... so we are done */ + return 1; +} + +/** + * ti_mmchs_intr - interrupt handler for MMC/SD/SDIO controller + * @arg: pointer to the driver context + * + * Interrupt handler for the MMC/SD/SDIO controller, responsible for handling + * the IRQ and clearing the status flags. + * + * LOCKING: + * Called from interrupt context + * + * RETURNS: + * nothing + */ +static void +ti_mmchs_intr(void *arg) +{ + struct ti_mmchs_softc *sc = (struct ti_mmchs_softc *) arg; + uint32_t stat_reg; + int done = 0; + + TI_MMCHS_LOCK(sc); + + stat_reg = ti_mmchs_read_4(sc, MMCHS_STAT) & (ti_mmchs_read_4(sc, + MMCHS_IE) | MMCHS_STAT_ERRI); + + if (sc->curcmd == NULL) { + device_printf(sc->sc_dev, "Error: current cmd NULL, already done?\n"); + ti_mmchs_write_4(sc, MMCHS_STAT, stat_reg); + TI_MMCHS_UNLOCK(sc); + return; + } + + if (stat_reg & MMCHS_STAT_ERRI) { + /* An error has been tripped in the status register */ + done = ti_mmchs_intr_error(sc, sc->curcmd, stat_reg); + + } else { + + /* NOTE: This implementation could be a bit inefficent, I don't think + * it is necessary to handle both the 'command complete' and 'transfer + * complete' for data transfers ... presumably just transfer complete + * is enough. + */ + + /* No error */ + sc->curcmd->error = MMC_ERR_NONE; + + /* Check if the command completed */ + if (stat_reg & MMCHS_STAT_CC) { + done = ti_mmchs_intr_cmd_compl(sc, sc->curcmd); + } + + /* Check if the transfer has completed */ + if (stat_reg & MMCHS_STAT_TC) { + done = ti_mmchs_intr_xfer_compl(sc, sc->curcmd); + } + + } + + /* Clear all the interrupt status bits by writing the value back */ + ti_mmchs_write_4(sc, MMCHS_STAT, stat_reg); + + /* This may mark the command as done if there is no stop request */ + /* TODO: This is a bit ugly, needs fix-up */ + if (done) { + ti_mmchs_start(sc); + } + + TI_MMCHS_UNLOCK(sc); +} + +#ifdef SOC_TI_AM335X +static void +ti_mmchs_edma3_rx_xfer_setup(struct ti_mmchs_softc *sc, uint32_t src_paddr, + uint32_t dst_paddr, uint16_t blk_size, uint16_t num_blks) +{ + struct ti_edma3cc_param_set ps; + + bzero(&ps, sizeof(struct ti_edma3cc_param_set)); + ps.src = src_paddr; + ps.dst = dst_paddr; + ps.dstbidx = 4; + ps.dstcidx = blk_size; + ps.acnt = 4; + ps.bcnt = blk_size/4; + ps.ccnt = num_blks; + ps.link = 0xffff; + ps.opt.tcc = sc->dma_rx_trig; + ps.opt.tcinten = 1; + ps.opt.fwid = 2; /* fifo width is 32 */ + ps.opt.sam = 1; + ps.opt.syncdim = 1; + + ti_edma3_param_write(sc->dma_rx_trig, &ps); + ti_edma3_enable_transfer_event(sc->dma_rx_trig); +} + +static void +ti_mmchs_edma3_tx_xfer_setup(struct ti_mmchs_softc *sc, uint32_t src_paddr, + uint32_t dst_paddr, uint16_t blk_size, uint16_t num_blks) +{ + struct ti_edma3cc_param_set ps; + + bzero(&ps, sizeof(struct ti_edma3cc_param_set)); + ps.src = src_paddr; + ps.dst = dst_paddr; + ps.srccidx = blk_size; + ps.bcnt = blk_size/4; + ps.ccnt = num_blks; + ps.srcbidx = 4; + ps.acnt = 0x4; + ps.link = 0xffff; + ps.opt.tcc = sc->dma_tx_trig; + ps.opt.tcinten = 1; + ps.opt.fwid = 2; /* fifo width is 32 */ + ps.opt.dam = 1; + ps.opt.syncdim = 1; + + ti_edma3_param_write(sc->dma_tx_trig, &ps); + ti_edma3_enable_transfer_event(sc->dma_tx_trig); +} +#endif + +/** + * ti_mmchs_start_cmd - starts the given command + * @sc: pointer to the driver context + * @cmd: the command to start + * + * The call tree for this function is + * - ti_mmchs_start_cmd + * - ti_mmchs_start + * - ti_mmchs_request + * + * LOCKING: + * Caller should be holding the OMAP_MMC lock. + * + * RETURNS: + * nothing + */ +static void +ti_mmchs_start_cmd(struct ti_mmchs_softc *sc, struct mmc_command *cmd) +{ + uint32_t cmd_reg, con_reg, ise_reg; + struct mmc_data *data; + struct mmc_request *req; + void *vaddr; + bus_addr_t paddr; +#ifndef SOC_TI_AM335X + uint32_t pktsize; +#endif + sc->curcmd = cmd; + data = cmd->data; + req = cmd->mrq; + + /* Ensure the STR and MIT bits are cleared, these are only used for special + * command types. + */ + con_reg = ti_mmchs_read_4(sc, MMCHS_CON); + con_reg &= ~(MMCHS_CON_STR | MMCHS_CON_MIT); + + /* Load the command into bits 29:24 of the CMD register */ + cmd_reg = (uint32_t)(cmd->opcode & 0x3F) << 24; + + /* Set the default set of interrupts */ + ise_reg = (MMCHS_STAT_CERR | MMCHS_STAT_CTO | MMCHS_STAT_CC | MMCHS_STAT_CEB); + + /* Enable CRC checking if requested */ + if (cmd->flags & MMC_RSP_CRC) + ise_reg |= MMCHS_STAT_CCRC; + + /* Enable reply index checking if the response supports it */ + if (cmd->flags & MMC_RSP_OPCODE) + ise_reg |= MMCHS_STAT_CIE; + + /* Set the expected response length */ + if (MMC_RSP(cmd->flags) == MMC_RSP_NONE) { + cmd_reg |= MMCHS_CMD_RSP_TYPE_NO; + } else { + if (cmd->flags & MMC_RSP_136) + cmd_reg |= MMCHS_CMD_RSP_TYPE_136; + else if (cmd->flags & MMC_RSP_BUSY) + cmd_reg |= MMCHS_CMD_RSP_TYPE_48_BSY; + else + cmd_reg |= MMCHS_CMD_RSP_TYPE_48; + + /* Enable command index/crc checks if necessary expected */ + if (cmd->flags & MMC_RSP_CRC) + cmd_reg |= MMCHS_CMD_CCCE; + if (cmd->flags & MMC_RSP_OPCODE) + cmd_reg |= MMCHS_CMD_CICE; + } + + /* Set the bits for the special commands CMD12 (MMC_STOP_TRANSMISSION) and + * CMD52 (SD_IO_RW_DIRECT) */ + if (cmd->opcode == MMC_STOP_TRANSMISSION) + cmd_reg |= MMCHS_CMD_CMD_TYPE_IO_ABORT; + + /* Check if there is any data to write */ + if (data == NULL) { + /* Clear the block count */ + ti_mmchs_write_4(sc, MMCHS_BLK, 0); + + /* The no data case is fairly simple */ + ti_mmchs_write_4(sc, MMCHS_CON, con_reg); + ti_mmchs_write_4(sc, MMCHS_IE, ise_reg); + ti_mmchs_write_4(sc, MMCHS_ISE, ise_reg); + ti_mmchs_write_4(sc, MMCHS_ARG, cmd->arg); + ti_mmchs_write_4(sc, MMCHS_CMD, cmd_reg); + return; + } + + /* Indicate that data is present */ + cmd_reg |= MMCHS_CMD_DP | MMCHS_CMD_MSBS | MMCHS_CMD_BCE; + + /* Indicate a read operation */ + if (data->flags & MMC_DATA_READ) + cmd_reg |= MMCHS_CMD_DDIR; + + /* Streaming mode */ + if (data->flags & MMC_DATA_STREAM) { + con_reg |= MMCHS_CON_STR; + } + + /* Multi-block mode */ + if (data->flags & MMC_DATA_MULTI) { + cmd_reg |= MMCHS_CMD_MSBS; + } + + /* Enable extra interrupt sources for the transfer */ + ise_reg |= (MMCHS_STAT_TC | MMCHS_STAT_DTO | MMCHS_STAT_DEB | MMCHS_STAT_CEB); + if (cmd->flags & MMC_RSP_CRC) + ise_reg |= MMCHS_STAT_DCRC; + + /* Enable the DMA transfer bit */ + cmd_reg |= MMCHS_CMD_DE; + + /* Set the block size and block count */ + ti_mmchs_write_4(sc, MMCHS_BLK, (1 << 16) | data->len); + + /* Setup the DMA stuff */ + if (data->flags & (MMC_DATA_READ | MMC_DATA_WRITE)) { + + vaddr = data->data; + data->xfer_len = 0; + + /* Map the buffer buf into bus space using the dmamap map. */ + if (bus_dmamap_load(sc->sc_dmatag, sc->sc_dmamap, vaddr, data->len, + ti_mmchs_getaddr, &paddr, 0) != 0) { + + if (req->cmd->flags & STOP_STARTED) + req->stop->error = MMC_ERR_NO_MEMORY; + else + req->cmd->error = MMC_ERR_NO_MEMORY; + sc->req = NULL; + sc->curcmd = NULL; + req->done(req); + return; + } + +#ifndef SOC_TI_AM335X + /* Calculate the packet size, the max packet size is 512 bytes + * (or 128 32-bit elements). + */ + pktsize = min((data->len / 4), (512 / 4)); +#endif + /* Sync the DMA buffer and setup the DMA controller */ + if (data->flags & MMC_DATA_READ) { + bus_dmamap_sync(sc->sc_dmatag, sc->sc_dmamap, BUS_DMASYNC_PREREAD); +#ifdef SOC_TI_AM335X + ti_mmchs_edma3_rx_xfer_setup(sc, sc->sc_data_reg_paddr, + paddr, data->len, 1); +#else + ti_sdma_start_xfer_packet(sc->sc_dmach_rd, sc->sc_data_reg_paddr, + paddr, 1, (data->len / 4), pktsize); +#endif + } else { + bus_dmamap_sync(sc->sc_dmatag, sc->sc_dmamap, BUS_DMASYNC_PREWRITE); +#ifdef SOC_TI_AM335X + ti_mmchs_edma3_tx_xfer_setup(sc, paddr, + sc->sc_data_reg_paddr, data->len, 1); +#else + ti_sdma_start_xfer_packet(sc->sc_dmach_wr, paddr, + sc->sc_data_reg_paddr, 1, (data->len / 4), pktsize); +#endif + } + + /* Increase the mapped count */ + sc->sc_dmamapped++; + + sc->sc_cmd_data_vaddr = vaddr; + sc->sc_cmd_data_len = data->len; + } + + /* Finally kick off the command */ + ti_mmchs_write_4(sc, MMCHS_CON, con_reg); + ti_mmchs_write_4(sc, MMCHS_IE, ise_reg); + ti_mmchs_write_4(sc, MMCHS_ISE, ise_reg); + ti_mmchs_write_4(sc, MMCHS_ARG, cmd->arg); + ti_mmchs_write_4(sc, MMCHS_CMD, cmd_reg); + + /* and we're done */ +} + +/** + * ti_mmchs_start - starts a request stored in the driver context + * @sc: pointer to the driver context + * + * This function is called by ti_mmchs_request() in response to a read/write + * request from the MMC core module. + * + * LOCKING: + * Caller should be holding the OMAP_MMC lock. + * + * RETURNS: + * nothing + */ +static void +ti_mmchs_start(struct ti_mmchs_softc *sc) +{ + struct mmc_request *req; + + /* Sanity check we have a request */ + req = sc->req; + if (req == NULL) + return; + + /* assert locked */ + if (!(sc->flags & CMD_STARTED)) { + sc->flags |= CMD_STARTED; + ti_mmchs_start_cmd(sc, req->cmd); + return; + } + + if (!(sc->flags & STOP_STARTED) && req->stop) { + sc->flags |= STOP_STARTED; + ti_mmchs_start_cmd(sc, req->stop); + return; + } + + /* We must be done -- bad idea to do this while locked? */ + sc->req = NULL; + sc->curcmd = NULL; + req->done(req); +} + +/** + * ti_mmchs_request - entry point for all read/write/cmd requests + * @brdev: mmc bridge device handle + * @reqdev: the device doing the requesting ? + * @req: the action requested + * + * LOCKING: + * None, internally takes the OMAP_MMC lock. + * + * RETURNS: + * 0 on success + * EBUSY if the driver is already performing a request + */ +static int +ti_mmchs_request(device_t brdev, device_t reqdev, struct mmc_request *req) +{ + struct ti_mmchs_softc *sc = device_get_softc(brdev); + + TI_MMCHS_LOCK(sc); + + /* + * XXX do we want to be able to queue up multiple commands? + * XXX sounds like a good idea, but all protocols are sync, so + * XXX maybe the idea is naive... + */ + if (sc->req != NULL) { + TI_MMCHS_UNLOCK(sc); + return (EBUSY); + } + + /* Store the request and start the command */ + sc->req = req; + sc->flags = 0; + ti_mmchs_start(sc); + + TI_MMCHS_UNLOCK(sc); + + return (0); +} + +/** + * ti_mmchs_get_ro - returns the status of the read-only setting + * @brdev: mmc bridge device handle + * @reqdev: device doing the request + * + * This function is relies on hint'ed values to determine which GPIO is used + * to determine if the write protect is enabled. On the BeagleBoard the pin + * is GPIO_23. + * + * LOCKING: + * - + * + * RETURNS: + * 0 if not read-only + * 1 if read only + */ +static int +ti_mmchs_get_ro(device_t brdev, device_t reqdev) +{ + struct ti_mmchs_softc *sc = device_get_softc(brdev); + unsigned int readonly = 0; + + TI_MMCHS_LOCK(sc); + + if ((sc->sc_wp_gpio_pin != -1) && (sc->sc_gpio_dev != NULL)) { + if (GPIO_PIN_GET(sc->sc_gpio_dev, sc->sc_wp_gpio_pin, &readonly) != 0) + readonly = 0; + else + readonly = (readonly == 0) ? 0 : 1; + } + + TI_MMCHS_UNLOCK(sc); + + return (readonly); +} + +/** + * ti_mmchs_send_init_stream - sets bus/controller settings + * @brdev: mmc bridge device handle + * @reqdev: device doing the request + * + * Send init stream sequence to card before sending IDLE command + * + * LOCKING: + * + * + * RETURNS: + * 0 if function succeeded + */ +static void +ti_mmchs_send_init_stream(struct ti_mmchs_softc *sc) +{ + unsigned long timeout; + uint32_t ie, ise, con; + + ti_mmchs_dbg(sc, "Performing init sequence\n"); + + /* Prior to issuing any command, the MMCHS controller has to execute a + * special INIT procedure. The MMCHS controller has to generate a clock + * during 1ms. During the INIT procedure, the MMCHS controller generates 80 + * clock periods. In order to keep the 1ms gap, the MMCHS controller should + * be configured to generate a clock whose frequency is smaller or equal to + * 80 KHz. If the MMCHS controller divider bitfield width doesn't allow to + * choose big values, the MMCHS controller driver should perform the INIT + * procedure twice or three times. Twice is generally enough. + * + * The INIt procedure is executed by setting MMCHS1.MMCHS_CON[1] INIT + * bitfield to 1 and by sending a dummy command, writing 0x00000000 in + * MMCHS1.MMCHS_CMD register. + */ + + /* Disable interrupt status events but enable interrupt generation. + * This doesn't seem right to me, but if the interrupt generation is not + * enabled the CC bit doesn't seem to be set in the STAT register. + */ + + /* Enable interrupt generation */ + ie = ti_mmchs_read_4(sc, MMCHS_IE); + ti_mmchs_write_4(sc, MMCHS_IE, 0x307F0033); + + /* Disable generation of status events (stops interrupt triggering) */ + ise = ti_mmchs_read_4(sc, MMCHS_ISE); + ti_mmchs_write_4(sc, MMCHS_ISE, 0); + + /* Set the initialise stream bit */ + con = ti_mmchs_read_4(sc, MMCHS_CON); + con |= MMCHS_CON_INIT; + ti_mmchs_write_4(sc, MMCHS_CON, con); + + /* Write a dummy command 0x00 */ + ti_mmchs_write_4(sc, MMCHS_CMD, 0x00000000); + + /* Loop waiting for the command to finish */ + timeout = hz; + do { + pause("MMCINIT", 1); + if (timeout-- == 0) { + device_printf(sc->sc_dev, "Error: first stream init timed out\n"); + break; + } + } while (!(ti_mmchs_read_4(sc, MMCHS_STAT) & MMCHS_STAT_CC)); + + /* Clear the command complete status bit */ + ti_mmchs_write_4(sc, MMCHS_STAT, MMCHS_STAT_CC); + + /* Write another dummy command 0x00 */ + ti_mmchs_write_4(sc, MMCHS_CMD, 0x00000000); + + /* Loop waiting for the second command to finish */ + timeout = hz; + do { + pause("MMCINIT", 1); + if (timeout-- == 0) { + device_printf(sc->sc_dev, "Error: second stream init timed out\n"); + break; + } + } while (!(ti_mmchs_read_4(sc, MMCHS_STAT) & MMCHS_STAT_CC)); + + /* Clear the stream init bit */ + con &= ~MMCHS_CON_INIT; + ti_mmchs_write_4(sc, MMCHS_CON, con); + + /* Clear the status register, then restore the IE and ISE registers */ + ti_mmchs_write_4(sc, MMCHS_STAT, 0xffffffff); + ti_mmchs_read_4(sc, MMCHS_STAT); + + ti_mmchs_write_4(sc, MMCHS_ISE, ise); + ti_mmchs_write_4(sc, MMCHS_IE, ie); +} + +/** + * ti_mmchs_update_ios - sets bus/controller settings + * @brdev: mmc bridge device handle + * @reqdev: device doing the request + * + * Called to set the bus and controller settings that need to be applied to + * the actual HW. Currently this function just sets the bus width and the + * clock speed. + * + * LOCKING: + * + * + * RETURNS: + * 0 if function succeeded + */ +static int +ti_mmchs_update_ios(device_t brdev, device_t reqdev) +{ + struct ti_mmchs_softc *sc; + struct mmc_host *host; + struct mmc_ios *ios; + uint32_t clkdiv; + uint32_t hctl_reg; + uint32_t con_reg; + uint32_t sysctl_reg; +#ifndef SOC_TI_AM335X + uint16_t mv; +#endif + unsigned long timeout; + int do_card_init = 0; + + sc = device_get_softc(brdev); + host = &sc->host; + ios = &host->ios; + + /* Read the initial values of the registers */ + hctl_reg = ti_mmchs_read_4(sc, MMCHS_HCTL); + con_reg = ti_mmchs_read_4(sc, MMCHS_CON); + + /* Set the bus width */ + switch (ios->bus_width) { + case bus_width_1: + hctl_reg &= ~MMCHS_HCTL_DTW; + con_reg &= ~MMCHS_CON_DW8; + break; + case bus_width_4: + hctl_reg |= MMCHS_HCTL_DTW; + con_reg &= ~MMCHS_CON_DW8; + break; + case bus_width_8: + con_reg |= MMCHS_CON_DW8; + break; + } + + /* Finally write all these settings back to the registers */ + ti_mmchs_write_4(sc, MMCHS_HCTL, hctl_reg); + ti_mmchs_write_4(sc, MMCHS_CON, con_reg); + + /* Check if we need to change the external voltage regulator */ + if (sc->sc_cur_power_mode != ios->power_mode) { + + if (ios->power_mode == power_up) { + + /* Set the power level */ + hctl_reg = ti_mmchs_read_4(sc, MMCHS_HCTL); + hctl_reg &= ~(MMCHS_HCTL_SDVS_MASK | MMCHS_HCTL_SDBP); + + if ((ios->vdd == -1) || (ios->vdd >= vdd_240)) { +#ifndef SOC_TI_AM335X + mv = 3000; +#endif + hctl_reg |= MMCHS_HCTL_SDVS_V30; + } else { +#ifndef SOC_TI_AM335X + mv = 1800; +#endif + hctl_reg |= MMCHS_HCTL_SDVS_V18; + } + + ti_mmchs_write_4(sc, MMCHS_HCTL, hctl_reg); + +#ifdef SOC_TI_AM335X + printf("%s: TWL unimplemented\n", __func__); +#else + /* Set the desired voltage on the regulator */ + if (sc->sc_vreg_dev && sc->sc_vreg_name) + twl_vreg_set_voltage(sc->sc_vreg_dev, sc->sc_vreg_name, mv); +#endif + /* Enable the bus power */ + ti_mmchs_write_4(sc, MMCHS_HCTL, (hctl_reg | MMCHS_HCTL_SDBP)); + timeout = hz; + while (!(ti_mmchs_read_4(sc, MMCHS_HCTL) & MMCHS_HCTL_SDBP)) { + if (timeout-- == 0) + break; + pause("MMC_PWRON", 1); + } + + } else if (ios->power_mode == power_off) { + /* Disable the bus power */ + hctl_reg = ti_mmchs_read_4(sc, MMCHS_HCTL); + ti_mmchs_write_4(sc, MMCHS_HCTL, (hctl_reg & ~MMCHS_HCTL_SDBP)); + +#ifdef SOC_TI_AM335X + printf("%s: TWL unimplemented\n", __func__); +#else + /* Turn the power off on the voltage regulator */ + if (sc->sc_vreg_dev && sc->sc_vreg_name) + twl_vreg_set_voltage(sc->sc_vreg_dev, sc->sc_vreg_name, 0); +#endif + } else if (ios->power_mode == power_on) { + /* Force a card re-initialisation sequence */ + do_card_init = 1; + } + + /* Save the new power state */ + sc->sc_cur_power_mode = ios->power_mode; + } + + /* need the MMCHS_SYSCTL register */ + sysctl_reg = ti_mmchs_read_4(sc, MMCHS_SYSCTL); + + /* Just in case this hasn't been setup before, set the timeout to the default */ + sysctl_reg &= ~MMCHS_SYSCTL_DTO_MASK; + sysctl_reg |= MMCHS_SYSCTL_DTO(0xe); + + /* Disable the clock output while configuring the new clock */ + sysctl_reg &= ~(MMCHS_SYSCTL_ICE | MMCHS_SYSCTL_CEN); + ti_mmchs_write_4(sc, MMCHS_SYSCTL, sysctl_reg); + + /* bus mode? */ + if (ios->clock == 0) { + clkdiv = 0; + } else { + clkdiv = sc->sc_ref_freq / ios->clock; + if (clkdiv < 1) + clkdiv = 1; + if ((sc->sc_ref_freq / clkdiv) > ios->clock) + clkdiv += 1; + if (clkdiv > 250) + clkdiv = 250; + } + + /* Set the new clock divider */ + sysctl_reg &= ~MMCHS_SYSCTL_CLKD_MASK; + sysctl_reg |= MMCHS_SYSCTL_CLKD(clkdiv); + + /* Write the new settings ... */ + ti_mmchs_write_4(sc, MMCHS_SYSCTL, sysctl_reg); + /* ... write the internal clock enable bit ... */ + ti_mmchs_write_4(sc, MMCHS_SYSCTL, sysctl_reg | MMCHS_SYSCTL_ICE); + /* ... wait for the clock to stablise ... */ + while (((sysctl_reg = ti_mmchs_read_4(sc, MMCHS_SYSCTL)) & + MMCHS_SYSCTL_ICS) == 0) { + continue; + } + /* ... then enable */ + sysctl_reg |= MMCHS_SYSCTL_CEN; + ti_mmchs_write_4(sc, MMCHS_SYSCTL, sysctl_reg); + + /* If the power state has changed to 'power_on' then run the init sequence*/ + if (do_card_init) { + ti_mmchs_send_init_stream(sc); + } + + /* Set the bus mode (opendrain or normal) */ + con_reg = ti_mmchs_read_4(sc, MMCHS_CON); + if (ios->bus_mode == opendrain) + con_reg |= MMCHS_CON_OD; + else + con_reg &= ~MMCHS_CON_OD; + ti_mmchs_write_4(sc, MMCHS_CON, con_reg); + + return (0); +} + +/** + * ti_mmchs_acquire_host - + * @brdev: mmc bridge device handle + * @reqdev: device doing the request + * + * TODO: Is this function needed ? + * + * LOCKING: + * none + * + * RETURNS: + * 0 function succeeded + * + */ +static int +ti_mmchs_acquire_host(device_t brdev, device_t reqdev) +{ + struct ti_mmchs_softc *sc = device_get_softc(brdev); + int err = 0; + + TI_MMCHS_LOCK(sc); + + while (sc->bus_busy) { + msleep(sc, &sc->sc_mtx, PZERO, "mmc", hz / 5); + } + + sc->bus_busy++; + + TI_MMCHS_UNLOCK(sc); + + return (err); +} + +/** + * ti_mmchs_release_host - + * @brdev: mmc bridge device handle + * @reqdev: device doing the request + * + * TODO: Is this function needed ? + * + * LOCKING: + * none + * + * RETURNS: + * 0 function succeeded + * + */ +static int +ti_mmchs_release_host(device_t brdev, device_t reqdev) +{ + struct ti_mmchs_softc *sc = device_get_softc(brdev); + + TI_MMCHS_LOCK(sc); + + sc->bus_busy--; + wakeup(sc); + + TI_MMCHS_UNLOCK(sc); + + return (0); +} + +/** + * ti_mmchs_read_ivar - returns driver conf variables + * @bus: + * @child: + * @which: The variable to get the result for + * @result: Upon return will store the variable value + * + * + * + * LOCKING: + * None, caller must hold locks + * + * RETURNS: + * 0 on success + * EINVAL if the variable requested is invalid + */ +static int +ti_mmchs_read_ivar(device_t bus, device_t child, int which, uintptr_t *result) +{ + struct ti_mmchs_softc *sc = device_get_softc(bus); + + switch (which) { + case MMCBR_IVAR_BUS_MODE: + *(int *)result = sc->host.ios.bus_mode; + break; + case MMCBR_IVAR_BUS_WIDTH: + *(int *)result = sc->host.ios.bus_width; + break; + case MMCBR_IVAR_CHIP_SELECT: + *(int *)result = sc->host.ios.chip_select; + break; + case MMCBR_IVAR_CLOCK: + *(int *)result = sc->host.ios.clock; + break; + case MMCBR_IVAR_F_MIN: + *(int *)result = sc->host.f_min; + break; + case MMCBR_IVAR_F_MAX: + *(int *)result = sc->host.f_max; + break; + case MMCBR_IVAR_HOST_OCR: + *(int *)result = sc->host.host_ocr; + break; + case MMCBR_IVAR_MODE: + *(int *)result = sc->host.mode; + break; + case MMCBR_IVAR_OCR: + *(int *)result = sc->host.ocr; + break; + case MMCBR_IVAR_POWER_MODE: + *(int *)result = sc->host.ios.power_mode; + break; + case MMCBR_IVAR_VDD: + *(int *)result = sc->host.ios.vdd; + break; + case MMCBR_IVAR_CAPS: + *(int *)result = sc->host.caps; + break; + case MMCBR_IVAR_MAX_DATA: + *(int *)result = 1; + break; + default: + return (EINVAL); + } + return (0); +} + +/** + * ti_mmchs_write_ivar - writes a driver conf variables + * @bus: + * @child: + * @which: The variable to set + * @value: The value to write into the variable + * + * + * + * LOCKING: + * None, caller must hold locks + * + * RETURNS: + * 0 on success + * EINVAL if the variable requested is invalid + */ +static int +ti_mmchs_write_ivar(device_t bus, device_t child, int which, uintptr_t value) +{ + struct ti_mmchs_softc *sc = device_get_softc(bus); + + switch (which) { + case MMCBR_IVAR_BUS_MODE: + sc->host.ios.bus_mode = value; + break; + case MMCBR_IVAR_BUS_WIDTH: + sc->host.ios.bus_width = value; + break; + case MMCBR_IVAR_CHIP_SELECT: + sc->host.ios.chip_select = value; + break; + case MMCBR_IVAR_CLOCK: + sc->host.ios.clock = value; + break; + case MMCBR_IVAR_MODE: + sc->host.mode = value; + break; + case MMCBR_IVAR_OCR: + sc->host.ocr = value; + break; + case MMCBR_IVAR_POWER_MODE: + sc->host.ios.power_mode = value; + break; + case MMCBR_IVAR_VDD: + sc->host.ios.vdd = value; + break; + /* These are read-only */ + case MMCBR_IVAR_CAPS: + case MMCBR_IVAR_HOST_OCR: + case MMCBR_IVAR_F_MIN: + case MMCBR_IVAR_F_MAX: + case MMCBR_IVAR_MAX_DATA: + return (EINVAL); + default: + return (EINVAL); + } + return (0); +} + +/** + * ti_mmchs_hw_init - initialises the MMC/SD/SIO controller + * @dev: mmc device handle + * + * Called by the driver attach function during driver initialisation. This + * function is responsibly to setup the controller ready for transactions. + * + * LOCKING: + * No locking, assumed to only be called during initialisation. + * + * RETURNS: + * nothing + */ +static void +ti_mmchs_hw_init(device_t dev) +{ + struct ti_mmchs_softc *sc = device_get_softc(dev); + clk_ident_t clk; + unsigned long timeout; + uint32_t sysctl; + uint32_t capa; + uint32_t con; + + /* 1: Enable the controller and interface/functional clocks */ + clk = MMC0_CLK + sc->device_id; + + if (ti_prcm_clk_enable(clk) != 0) { + device_printf(dev, "Error: failed to enable MMC clock\n"); + return; + } + + /* 1a: Get the frequency of the source clock */ + if (ti_prcm_clk_get_source_freq(clk, &sc->sc_ref_freq) != 0) { + device_printf(dev, "Error: failed to get source clock freq\n"); + return; + } + + /* 2: Issue a softreset to the controller */ + ti_mmchs_write_4(sc, MMCHS_SYSCONFIG, 0x0002); + timeout = 100; + while ((ti_mmchs_read_4(sc, MMCHS_SYSSTATUS) & 0x01) == 0x0) { + DELAY(1000); + if (timeout-- == 0) { + device_printf(dev, "Error: reset operation timed out\n"); + return; + } + } + + /* 3: Reset both the command and data state machines */ + sysctl = ti_mmchs_read_4(sc, MMCHS_SYSCTL); + ti_mmchs_write_4(sc, MMCHS_SYSCTL, sysctl | MMCHS_SYSCTL_SRA); + timeout = 100; + while ((ti_mmchs_read_4(sc, MMCHS_SYSCTL) & MMCHS_SYSCTL_SRA) != 0x0) { + DELAY(1000); + if (timeout-- == 0) { + device_printf(dev, "Error: reset operation timed out\n"); + return; + } + } + + /* 4: Set initial host configuration (1-bit mode, pwroff) and capabilities */ + ti_mmchs_write_4(sc, MMCHS_HCTL, MMCHS_HCTL_SDVS_V30); + + capa = ti_mmchs_read_4(sc, MMCHS_CAPA); + ti_mmchs_write_4(sc, MMCHS_CAPA, capa | MMCHS_CAPA_VS30 | MMCHS_CAPA_VS18); + + /* 5: Set the initial bus configuration + * 0 CTPL_MMC_SD : Control Power for DAT1 line + * 0 WPP_ACTIVE_HIGH : Write protect polarity + * 0 CDP_ACTIVE_HIGH : Card detect polarity + * 0 CTO_ENABLED : MMC interrupt command + * 0 DW8_DISABLED : 8-bit mode MMC select + * 0 MODE_FUNC : Mode select + * 0 STREAM_DISABLED : Stream command + * 0 HR_DISABLED : Broadcast host response + * 0 INIT_DISABLED : Send initialization stream + * 0 OD_DISABLED : No Open Drain + */ + con = ti_mmchs_read_4(sc, MMCHS_CON) & MMCHS_CON_DVAL_MASK; + ti_mmchs_write_4(sc, MMCHS_CON, con); + +} + +/** + * ti_mmchs_fini - shutdown the MMC/SD/SIO controller + * @dev: mmc device handle + * + * Responsible for shutting done the MMC controller, this function may be + * called as part of a reset sequence. + * + * LOCKING: + * No locking, assumed to be called during tear-down/reset. + * + * RETURNS: + * nothing + */ +static void +ti_mmchs_hw_fini(device_t dev) +{ + struct ti_mmchs_softc *sc = device_get_softc(dev); + + /* Disable all interrupts */ + ti_mmchs_write_4(sc, MMCHS_ISE, 0x00000000); + ti_mmchs_write_4(sc, MMCHS_IE, 0x00000000); + + /* Disable the functional and interface clocks */ + ti_prcm_clk_disable(MMC0_CLK + sc->device_id); +} + +/** + * ti_mmchs_init_dma_channels - initalise the DMA channels + * @sc: driver soft context + * + * Attempts to activate an RX and TX DMA channel for the MMC device. + * + * LOCKING: + * No locking, assumed to be called during tear-down/reset. + * + * RETURNS: + * 0 on success, a negative error code on failure. + */ +static int +ti_mmchs_init_dma_channels(struct ti_mmchs_softc *sc) +{ +#ifdef SOC_TI_AM335X + switch (sc->device_id) { + case 0: + sc->dma_tx_trig = TI_EDMA3_EVENT_SDTXEVT0; + sc->dma_rx_trig = TI_EDMA3_EVENT_SDRXEVT0; + break; + case 1: + sc->dma_tx_trig = TI_EDMA3_EVENT_SDTXEVT1; + sc->dma_rx_trig = TI_EDMA3_EVENT_SDRXEVT1; + break; + default: + return(EINVAL); + } + +#define EVTQNUM 0 + /* TODO EDMA3 have 3 queues, so we need some queue allocation call */ + ti_edma3_init(EVTQNUM); + ti_edma3_request_dma_ch(sc->dma_tx_trig, sc->dma_tx_trig, EVTQNUM); + ti_edma3_request_dma_ch(sc->dma_rx_trig, sc->dma_rx_trig, EVTQNUM); +#else + int err; + uint32_t rev; + + /* Get the current chip revision */ + rev = ti_revision(); + if ((OMAP_REV_DEVICE(rev) != OMAP4430_DEV) && (sc->device_id > 3)) + return(EINVAL); + + /* Get the DMA MMC triggers */ + switch (sc->device_id) { + case 1: + sc->dma_tx_trig = 60; + sc->dma_rx_trig = 61; + break; + case 2: + sc->dma_tx_trig = 46; + sc->dma_rx_trig = 47; + break; + case 3: + sc->dma_tx_trig = 76; + sc->dma_rx_trig = 77; + break; + /* The following are OMAP4 only */ + case 4: + sc->dma_tx_trig = 56; + sc->dma_rx_trig = 57; + break; + case 5: + sc->dma_tx_trig = 58; + sc->dma_rx_trig = 59; + break; + default: + return(EINVAL); + } + + /* Activate a RX channel from the OMAP DMA driver */ + err = ti_sdma_activate_channel(&sc->sc_dmach_rd, ti_mmchs_dma_intr, sc); + if (err != 0) + return(err); + + /* Setup the RX channel for MMC data transfers */ + ti_sdma_set_xfer_burst(sc->sc_dmach_rd, TI_SDMA_BURST_NONE, + TI_SDMA_BURST_64); + ti_sdma_set_xfer_data_type(sc->sc_dmach_rd, TI_SDMA_DATA_32BITS_SCALAR); + ti_sdma_sync_params(sc->sc_dmach_rd, sc->dma_rx_trig, + TI_SDMA_SYNC_PACKET | TI_SDMA_SYNC_TRIG_ON_SRC); + ti_sdma_set_addr_mode(sc->sc_dmach_rd, TI_SDMA_ADDR_CONSTANT, + TI_SDMA_ADDR_POST_INCREMENT); + + /* Activate and configure the TX DMA channel */ + err = ti_sdma_activate_channel(&sc->sc_dmach_wr, ti_mmchs_dma_intr, sc); + if (err != 0) + return(err); + + /* Setup the TX channel for MMC data transfers */ + ti_sdma_set_xfer_burst(sc->sc_dmach_wr, TI_SDMA_BURST_64, + TI_SDMA_BURST_NONE); + ti_sdma_set_xfer_data_type(sc->sc_dmach_wr, TI_SDMA_DATA_32BITS_SCALAR); + ti_sdma_sync_params(sc->sc_dmach_wr, sc->dma_tx_trig, + TI_SDMA_SYNC_PACKET | TI_SDMA_SYNC_TRIG_ON_DST); + ti_sdma_set_addr_mode(sc->sc_dmach_wr, TI_SDMA_ADDR_POST_INCREMENT, + TI_SDMA_ADDR_CONSTANT); +#endif + return(0); +} + +/** + * ti_mmchs_deactivate - deactivates the driver + * @dev: mmc device handle + * + * Unmaps the register set and releases the IRQ resource. + * + * LOCKING: + * None required + * + * RETURNS: + * nothing + */ +static void +ti_mmchs_deactivate(device_t dev) +{ + struct ti_mmchs_softc *sc= device_get_softc(dev); + + /* Remove the IRQ handler */ + if (sc->sc_irq_h != NULL) { + bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_irq_h); + sc->sc_irq_h = NULL; + } + + /* Do the generic detach */ + bus_generic_detach(sc->sc_dev); + +#ifdef SOC_TI_AM335X + printf("%s: DMA unimplemented\n", __func__); +#else + /* Deactivate the DMA channels */ + ti_sdma_deactivate_channel(sc->sc_dmach_rd); + ti_sdma_deactivate_channel(sc->sc_dmach_wr); +#endif + + /* Unmap the MMC controller registers */ + if (sc->sc_mem_res != 0) { + bus_release_resource(dev, SYS_RES_MEMORY, rman_get_rid(sc->sc_irq_res), + sc->sc_mem_res); + sc->sc_mem_res = NULL; + } + + /* Release the IRQ resource */ + if (sc->sc_irq_res != NULL) { + bus_release_resource(dev, SYS_RES_IRQ, rman_get_rid(sc->sc_irq_res), + sc->sc_irq_res); + sc->sc_irq_res = NULL; + } + + return; +} + +/** + * ti_mmchs_activate - activates the driver + * @dev: mmc device handle + * + * Maps in the register set and requests an IRQ handler for the MMC controller. + * + * LOCKING: + * None required + * + * RETURNS: + * 0 on sucess + * ENOMEM if failed to map register set + */ +static int +ti_mmchs_activate(device_t dev) +{ + struct ti_mmchs_softc *sc = device_get_softc(dev); + unsigned long addr; + int rid; + int err; + + /* Get the memory resource for the register mapping */ + rid = 0; + sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (sc->sc_mem_res == NULL) + panic("%s: Cannot map registers", device_get_name(dev)); + + /* Allocate an IRQ resource for the MMC controller */ + rid = 0; + sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_ACTIVE | RF_SHAREABLE); + if (sc->sc_irq_res == NULL) + goto errout; + + /* Allocate DMA tags and maps */ + err = bus_dma_tag_create(bus_get_dma_tag(dev), 1, 0, + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, + NULL, MAXPHYS, 1, MAXPHYS, BUS_DMA_ALLOCNOW, NULL, + NULL, &sc->sc_dmatag); + if (err != 0) + goto errout; + + err = bus_dmamap_create(sc->sc_dmatag, 0, &sc->sc_dmamap); + if (err != 0) + goto errout; + + /* Initialise the DMA channels to be used by the controller */ + err = ti_mmchs_init_dma_channels(sc); + if (err != 0) + goto errout; + + /* Set the register offset */ + if (ti_chip() == CHIP_OMAP_3) + sc->sc_reg_off = OMAP3_MMCHS_REG_OFFSET; + else if (ti_chip() == CHIP_OMAP_4) + sc->sc_reg_off = OMAP4_MMCHS_REG_OFFSET; + else if (ti_chip() == CHIP_AM335X) + sc->sc_reg_off = AM335X_MMCHS_REG_OFFSET; + else + panic("Unknown OMAP device\n"); + + /* Get the physical address of the MMC data register, needed for DMA */ + addr = vtophys(rman_get_start(sc->sc_mem_res)); + sc->sc_data_reg_paddr = addr + sc->sc_reg_off + MMCHS_DATA; + + /* Set the initial power state to off */ + sc->sc_cur_power_mode = power_off; + + return (0); + +errout: + ti_mmchs_deactivate(dev); + return (ENOMEM); +} + +/** + * ti_mmchs_probe - probe function for the driver + * @dev: mmc device handle + * + * + * + * RETURNS: + * always returns 0 + */ +static int +ti_mmchs_probe(device_t dev) +{ + if (!ofw_bus_is_compatible(dev, "ti,mmchs")) + return (ENXIO); + + device_set_desc(dev, "TI MMC/SD/SDIO High Speed Interface"); + return (0); +} + +/** + * ti_mmchs_attach - attach function for the driver + * @dev: mmc device handle + * + * Driver initialisation, sets-up the bus mappings, DMA mapping/channels and + * the actual controller by calling ti_mmchs_init(). + * + * RETURNS: + * Returns 0 on success or a negative error code. + */ +static int +ti_mmchs_attach(device_t dev) +{ + struct ti_mmchs_softc *sc = device_get_softc(dev); + int unit = device_get_unit(dev); + phandle_t node; + pcell_t did; + int err; + + /* Save the device and bus tag */ + sc->sc_dev = dev; + + /* Get the mmchs device id from FDT */ + node = ofw_bus_get_node(dev); + if ((OF_getprop(node, "mmchs-device-id", &did, sizeof(did))) <= 0) { + device_printf(dev, "missing mmchs-device-id attribute in FDT\n"); + return (ENXIO); + } + sc->device_id = fdt32_to_cpu(did); + + /* Initiate the mtex lock */ + TI_MMCHS_LOCK_INIT(sc); + + /* Indicate the DMA channels haven't yet been allocated */ + sc->sc_dmach_rd = (unsigned int)-1; + sc->sc_dmach_wr = (unsigned int)-1; + + /* Get the hint'ed write detect pin */ + /* TODO: take this from FDT */ + if (resource_int_value("ti_mmchs", unit, "wp_gpio", &sc->sc_wp_gpio_pin) != 0){ + sc->sc_wp_gpio_pin = -1; + } else { + /* Get the GPIO device, we need this for the write protect pin */ + sc->sc_gpio_dev = devclass_get_device(devclass_find("gpio"), 0); + if (sc->sc_gpio_dev == NULL) + device_printf(dev, "Error: failed to get the GPIO device\n"); + else + GPIO_PIN_SETFLAGS(sc->sc_gpio_dev, sc->sc_wp_gpio_pin, + GPIO_PIN_INPUT); + } + + /* Get the TWL voltage regulator device, we need this to for setting the + * voltage of the bus on certain OMAP platforms. + */ + sc->sc_vreg_name = NULL; + + /* TODO: add voltage regulator knob to FDT */ +#ifdef notyet + sc->sc_vreg_dev = devclass_get_device(devclass_find("twl_vreg"), 0); + if (sc->sc_vreg_dev == NULL) { + device_printf(dev, "Error: failed to get the votlage regulator" + " device\n"); + sc->sc_vreg_name = NULL; + } +#endif + + /* Activate the device */ + err = ti_mmchs_activate(dev); + if (err) + goto out; + + /* Initialise the controller */ + ti_mmchs_hw_init(dev); + + /* Activate the interrupt and attach a handler */ + err = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE, + NULL, ti_mmchs_intr, sc, &sc->sc_irq_h); + if (err != 0) + goto out; + + /* Add host details */ + sc->host.f_min = sc->sc_ref_freq / 1023; + sc->host.f_max = sc->sc_ref_freq; + sc->host.host_ocr = MMC_OCR_290_300 | MMC_OCR_300_310; + sc->host.caps = MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA; + + device_add_child(dev, "mmc", 0); + + device_set_ivars(dev, &sc->host); + err = bus_generic_attach(dev); + +out: + if (err) { + TI_MMCHS_LOCK_DESTROY(sc); + ti_mmchs_deactivate(dev); + +#ifdef SOC_TI_AM335X + printf("%s: DMA unimplemented\n", __func__); +#else + if (sc->sc_dmach_rd != (unsigned int)-1) + ti_sdma_deactivate_channel(sc->sc_dmach_rd); + if (sc->sc_dmach_wr != (unsigned int)-1) + ti_sdma_deactivate_channel(sc->sc_dmach_wr); +#endif + } + + return (err); +} + +/** + * ti_mmchs_detach - dettach function for the driver + * @dev: mmc device handle + * + * Shutdowns the controll and release resources allocated by the driver. + * + * RETURNS: + * Always returns 0. + */ +static int +ti_mmchs_detach(device_t dev) +{ +#ifndef SOC_TI_AM335X + struct ti_mmchs_softc *sc = device_get_softc(dev); +#endif + + ti_mmchs_hw_fini(dev); + ti_mmchs_deactivate(dev); + +#ifdef SOC_TI_AM335X + printf("%s: DMA unimplemented\n", __func__); +#else + ti_sdma_deactivate_channel(sc->sc_dmach_wr); + ti_sdma_deactivate_channel(sc->sc_dmach_rd); +#endif + + return (0); +} + +static device_method_t ti_mmchs_methods[] = { + /* device_if */ + DEVMETHOD(device_probe, ti_mmchs_probe), + DEVMETHOD(device_attach, ti_mmchs_attach), + DEVMETHOD(device_detach, ti_mmchs_detach), + + /* Bus interface */ + DEVMETHOD(bus_read_ivar, ti_mmchs_read_ivar), + DEVMETHOD(bus_write_ivar, ti_mmchs_write_ivar), + + /* mmcbr_if - MMC state machine callbacks */ + DEVMETHOD(mmcbr_update_ios, ti_mmchs_update_ios), + DEVMETHOD(mmcbr_request, ti_mmchs_request), + DEVMETHOD(mmcbr_get_ro, ti_mmchs_get_ro), + DEVMETHOD(mmcbr_acquire_host, ti_mmchs_acquire_host), + DEVMETHOD(mmcbr_release_host, ti_mmchs_release_host), + + {0, 0}, +}; + +static driver_t ti_mmchs_driver = { + "ti_mmchs", + ti_mmchs_methods, + sizeof(struct ti_mmchs_softc), +}; +static devclass_t ti_mmchs_devclass; + +DRIVER_MODULE(ti_mmchs, simplebus, ti_mmchs_driver, ti_mmchs_devclass, 0, 0); +MODULE_DEPEND(ti_mmchs, ti_prcm, 1, 1, 1); +#ifdef SOC_TI_AM335X +MODULE_DEPEND(ti_mmchs, ti_edma, 1, 1, 1); +#else +MODULE_DEPEND(ti_mmchs, ti_sdma, 1, 1, 1); +#endif +MODULE_DEPEND(ti_mmchs, ti_gpio, 1, 1, 1); + +/* FIXME: MODULE_DEPEND(ti_mmchs, twl_vreg, 1, 1, 1); */ diff --git a/sys/arm/ti/ti_mmchs.h b/sys/arm/ti/ti_mmchs.h new file mode 100644 index 000000000000..5a7f3f4be6c6 --- /dev/null +++ b/sys/arm/ti/ti_mmchs.h @@ -0,0 +1,170 @@ +/*- + * 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_MMCHS_H_ +#define _TI_MMCHS_H_ + +/** + * Header file for the TI MMC/SD/SDIO driver. + * + * Simply contains register addresses and bit flags. + */ + +/* Register offsets within each of the MMC/SD/SDIO controllers */ +#define MMCHS_SYSCONFIG 0x010 +#define MMCHS_SYSSTATUS 0x014 +#define MMCHS_CSRE 0x024 +#define MMCHS_SYSTEST 0x028 +#define MMCHS_CON 0x02C +#define MMCHS_PWCNT 0x030 +#define MMCHS_BLK 0x104 +#define MMCHS_ARG 0x108 +#define MMCHS_CMD 0x10C +#define MMCHS_RSP10 0x110 +#define MMCHS_RSP32 0x114 +#define MMCHS_RSP54 0x118 +#define MMCHS_RSP76 0x11C +#define MMCHS_DATA 0x120 +#define MMCHS_PSTATE 0x124 +#define MMCHS_HCTL 0x128 +#define MMCHS_SYSCTL 0x12C +#define MMCHS_STAT 0x130 +#define MMCHS_IE 0x134 +#define MMCHS_ISE 0x138 +#define MMCHS_AC12 0x13C +#define MMCHS_CAPA 0x140 +#define MMCHS_CUR_CAPA 0x148 +#define MMCHS_REV 0x1FC + +/* OMAP4 and OMAP4 have different register addresses */ +#define OMAP3_MMCHS_REG_OFFSET 0x000 +#define OMAP4_MMCHS_REG_OFFSET 0x100 +#define AM335X_MMCHS_REG_OFFSET 0x100 + +/* Register bit settings */ +#define MMCHS_STAT_BADA (1UL << 29) +#define MMCHS_STAT_CERR (1UL << 28) +#define MMCHS_STAT_ACE (1UL << 24) +#define MMCHS_STAT_DEB (1UL << 22) +#define MMCHS_STAT_DCRC (1UL << 21) +#define MMCHS_STAT_DTO (1UL << 20) +#define MMCHS_STAT_CIE (1UL << 19) +#define MMCHS_STAT_CEB (1UL << 18) +#define MMCHS_STAT_CCRC (1UL << 17) +#define MMCHS_STAT_CTO (1UL << 16) +#define MMCHS_STAT_ERRI (1UL << 15) +#define MMCHS_STAT_OBI (1UL << 9) +#define MMCHS_STAT_CIRQ (1UL << 8) +#define MMCHS_STAT_BRR (1UL << 5) +#define MMCHS_STAT_BWR (1UL << 4) +#define MMCHS_STAT_BGE (1UL << 2) +#define MMCHS_STAT_TC (1UL << 1) +#define MMCHS_STAT_CC (1UL << 0) + +#define MMCHS_STAT_CLEAR_MASK 0x3BFF8337UL + +#define MMCHS_SYSCTL_SRD (1UL << 26) +#define MMCHS_SYSCTL_SRC (1UL << 25) +#define MMCHS_SYSCTL_SRA (1UL << 24) +#define MMCHS_SYSCTL_DTO(x) (((x) & 0xf) << 16) +#define MMCHS_SYSCTL_DTO_MASK MMCHS_SYSCTL_DTO(0xf) +#define MMCHS_SYSCTL_CLKD(x) (((x) & 0x3ff) << 6) +#define MMCHS_SYSCTL_CLKD_MASK MMCHS_SYSCTL_CLKD(0x3ff) +#define MMCHS_SYSCTL_CEN (1UL << 2) +#define MMCHS_SYSCTL_ICS (1UL << 1) +#define MMCHS_SYSCTL_ICE (1UL << 0) + +#define MMCHS_HCTL_OBWE (1UL << 27) +#define MMCHS_HCTL_REM (1UL << 26) +#define MMCHS_HCTL_INS (1UL << 25) +#define MMCHS_HCTL_IWE (1UL << 24) +#define MMCHS_HCTL_IBG (1UL << 19) +#define MMCHS_HCTL_RWC (1UL << 18) +#define MMCHS_HCTL_CR (1UL << 17) +#define MMCHS_HCTL_SBGR (1UL << 16) +#define MMCHS_HCTL_SDVS_MASK (7UL << 9) +#define MMCHS_HCTL_SDVS_V18 (5UL << 9) +#define MMCHS_HCTL_SDVS_V30 (6UL << 9) +#define MMCHS_HCTL_SDVS_V33 (7UL << 9) +#define MMCHS_HCTL_SDBP (1UL << 8) +#define MMCHS_HCTL_DTW (1UL << 1) + +#define MMCHS_CAPA_VS18 (1UL << 26) +#define MMCHS_CAPA_VS30 (1UL << 25) +#define MMCHS_CAPA_VS33 (1UL << 24) + +#define MMCHS_CMD_CMD_TYPE_IO_ABORT (3UL << 21) +#define MMCHS_CMD_CMD_TYPE_FUNC_SEL (2UL << 21) +#define MMCHS_CMD_CMD_TYPE_SUSPEND (1UL << 21) +#define MMCHS_CMD_CMD_TYPE_OTHERS (0UL << 21) +#define MMCHS_CMD_CMD_TYPE_MASK (3UL << 22) + +#define MMCHS_CMD_DP (1UL << 21) +#define MMCHS_CMD_CICE (1UL << 20) +#define MMCHS_CMD_CCCE (1UL << 19) + +#define MMCHS_CMD_RSP_TYPE_MASK (3UL << 16) +#define MMCHS_CMD_RSP_TYPE_NO (0UL << 16) +#define MMCHS_CMD_RSP_TYPE_136 (1UL << 16) +#define MMCHS_CMD_RSP_TYPE_48 (2UL << 16) +#define MMCHS_CMD_RSP_TYPE_48_BSY (3UL << 16) + +#define MMCHS_CMD_MSBS (1UL << 5) +#define MMCHS_CMD_DDIR (1UL << 4) +#define MMCHS_CMD_ACEN (1UL << 2) +#define MMCHS_CMD_BCE (1UL << 1) +#define MMCHS_CMD_DE (1UL << 0) + +#define MMCHS_CON_CLKEXTFREE (1UL << 16) +#define MMCHS_CON_PADEN (1UL << 15) +#define MMCHS_CON_OBIE (1UL << 14) +#define MMCHS_CON_OBIP (1UL << 13) +#define MMCHS_CON_CEATA (1UL << 12) +#define MMCHS_CON_CTPL (1UL << 11) + +#define MMCHS_CON_DVAL_8_4MS (3UL << 9) +#define MMCHS_CON_DVAL_1MS (2UL << 9) +#define MMCHS_CON_DVAL_231US (1UL << 9) +#define MMCHS_CON_DVAL_33US (0UL << 9) +#define MMCHS_CON_DVAL_MASK (3UL << 9) + +#define MMCHS_CON_WPP (1UL << 8) +#define MMCHS_CON_CDP (1UL << 7) +#define MMCHS_CON_MIT (1UL << 6) +#define MMCHS_CON_DW8 (1UL << 5) +#define MMCHS_CON_MODE (1UL << 4) +#define MMCHS_CON_STR (1UL << 3) +#define MMCHS_CON_HR (1UL << 2) +#define MMCHS_CON_INIT (1UL << 1) +#define MMCHS_CON_OD (1UL << 0) + +#define MMCHS_CAPA_VS18 (1UL << 26) +#define MMCHS_CAPA_VS30 (1UL << 25) +#define MMCHS_CAPA_VS33 (1UL << 24) + +#endif /* _TI_MMCHS_H_ */ diff --git a/sys/arm/ti/ti_prcm.c b/sys/arm/ti/ti_prcm.c new file mode 100644 index 000000000000..57709ade62db --- /dev/null +++ b/sys/arm/ti/ti_prcm.c @@ -0,0 +1,309 @@ +/* + * 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 Managment 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 +#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_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 = &(ti_clk_devmap[0]); + 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); +} diff --git a/sys/arm/ti/ti_prcm.h b/sys/arm/ti/ti_prcm.h new file mode 100644 index 000000000000..7f6b99d51f0c --- /dev/null +++ b/sys/arm/ti/ti_prcm.h @@ -0,0 +1,185 @@ +/* + * 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 { + + /* 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 */ + MMC0_CLK = 100, + MMC1_CLK, + MMC2_CLK, + MMC3_CLK, + MMC4_CLK, + MMC5_CLK, + + /* I2C modules */ + I2C0_CLK = 200, + I2C1_CLK, + I2C2_CLK, + I2C3_CLK, + I2C4_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, + + /* General purpose timer modules */ + GPTIMER1_CLK = 500, + GPTIMER2_CLK, + GPTIMER3_CLK, + GPTIMER4_CLK, + GPTIMER5_CLK, + GPTIMER6_CLK, + GPTIMER7_CLK, + GPTIMER8_CLK, + GPTIMER9_CLK, + GPTIMER10_CLK, + GPTIMER11_CLK, + GPTIMER12_CLK, + + /* McBSP module(s) */ + MCBSP1_CLK = 600, + MCBSP2_CLK, + MCBSP3_CLK, + MCBSP4_CLK, + MCBSP5_CLK, + + /* General purpose I/O modules */ + GPIO0_CLK = 700, + GPIO1_CLK, + GPIO2_CLK, + GPIO3_CLK, + GPIO4_CLK, + GPIO5_CLK, + GPIO6_CLK, + + /* sDMA module */ + SDMA_CLK = 800, + + /* DMTimer modules */ + DMTIMER0_CLK = 900, + DMTIMER1_CLK, + DMTIMER2_CLK, + DMTIMER3_CLK, + DMTIMER4_CLK, + DMTIMER5_CLK, + DMTIMER6_CLK, + DMTIMER7_CLK, + + /* 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, + + INVALID_CLK_IDENT + +} 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_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_get_source_freq(clk_ident_t clk, unsigned int *freq); +void ti_prcm_reset(void); + +#endif /* _TI_PRCM_H_ */ diff --git a/sys/arm/ti/ti_scm.c b/sys/arm/ti/ti_scm.c new file mode 100644 index 000000000000..0ae39e99f6db --- /dev/null +++ b/sys/arm/ti/ti_scm.c @@ -0,0 +1,493 @@ +/* + * 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 +#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_2(sc, reg) \ + bus_space_read_2((sc)->sc_bst, (sc)->sc_bsh, (reg)) +#define ti_scm_write_2(sc, reg, val) \ + bus_space_write_2((sc)->sc_bst, (sc)->sc_bsh, (reg), (val)) +#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)) + + +/** + * ti_padconf_devmap - Array of pins, should be defined one per SoC + * + * This array is typically defined in one of the targeted *_scm_pinumx.c + * files and is specific to the given SoC platform. Each entry in the array + * corresponds to an individual pin. + */ +extern const struct ti_scm_device ti_scm_dev; + + +/** + * ti_scm_padconf_from_name - searches the list of pads and returns entry + * with matching ball name. + * @ballname: the name of the ball + * + * RETURNS: + * A pointer to the matching padconf or NULL if the ball wasn't found. + */ +static const struct ti_scm_padconf* +ti_scm_padconf_from_name(const char *ballname) +{ + const struct ti_scm_padconf *padconf; + + padconf = ti_scm_dev.padconf; + while (padconf->ballname != NULL) { + if (strcmp(ballname, padconf->ballname) == 0) + return(padconf); + padconf++; + } + + return (NULL); +} + +/** + * ti_scm_padconf_set_internal - sets the muxmode and state for a pad/pin + * @padconf: pointer to the pad structure + * @muxmode: the name of the mode to use for the pin, i.e. "uart1_rx" + * @state: the state to put the pad/pin in, i.e. PADCONF_PIN_??? + * + * + * LOCKING: + * Internally locks it's own context. + * + * RETURNS: + * 0 on success. + * EINVAL if pin requested is outside valid range or already in use. + */ +static int +ti_scm_padconf_set_internal(struct ti_scm_softc *sc, + const struct ti_scm_padconf *padconf, + const char *muxmode, unsigned int state) +{ + unsigned int mode; + uint16_t reg_val; + + /* populate the new value for the PADCONF register */ + reg_val = (uint16_t)(state & ti_scm_dev.padconf_sate_mask); + + /* find the new mode requested */ + for (mode = 0; mode < 8; mode++) { + if ((padconf->muxmodes[mode] != NULL) && + (strcmp(padconf->muxmodes[mode], muxmode) == 0)) { + break; + } + } + + /* couldn't find the mux mode */ + if (mode >= 8) + return (EINVAL); + + /* set the mux mode */ + reg_val |= (uint16_t)(mode & ti_scm_dev.padconf_muxmode_mask); + + printf("setting internal %x for %s\n", reg_val, muxmode); + /* write the register value (16-bit writes) */ + ti_scm_write_2(sc, padconf->reg_off, reg_val); + + return (0); +} + +/** + * ti_scm_padconf_set - sets the muxmode and state for a pad/pin + * @padname: the name of the pad, i.e. "c12" + * @muxmode: the name of the mode to use for the pin, i.e. "uart1_rx" + * @state: the state to put the pad/pin in, i.e. PADCONF_PIN_??? + * + * + * LOCKING: + * Internally locks it's own context. + * + * RETURNS: + * 0 on success. + * EINVAL if pin requested is outside valid range or already in use. + */ +int +ti_scm_padconf_set(const char *padname, const char *muxmode, unsigned int state) +{ + const struct ti_scm_padconf *padconf; + + if (!ti_scm_sc) + return (ENXIO); + + /* find the pin in the devmap */ + padconf = ti_scm_padconf_from_name(padname); + if (padconf == NULL) + return (EINVAL); + + return (ti_scm_padconf_set_internal(ti_scm_sc, padconf, muxmode, state)); +} + +/** + * ti_scm_padconf_get - gets the muxmode and state for a pad/pin + * @padname: the name of the pad, i.e. "c12" + * @muxmode: upon return will contain the name of the muxmode of the pin + * @state: upon return will contain the state of the the pad/pin + * + * + * LOCKING: + * Internally locks it's own context. + * + * RETURNS: + * 0 on success. + * EINVAL if pin requested is outside valid range or already in use. + */ +int +ti_scm_padconf_get(const char *padname, const char **muxmode, + unsigned int *state) +{ + const struct ti_scm_padconf *padconf; + uint16_t reg_val; + + if (!ti_scm_sc) + return (ENXIO); + + /* find the pin in the devmap */ + padconf = ti_scm_padconf_from_name(padname); + if (padconf == NULL) + return (EINVAL); + + /* read the register value (16-bit reads) */ + reg_val = ti_scm_read_2(ti_scm_sc, padconf->reg_off); + + /* save the state */ + if (state) + *state = (reg_val & ti_scm_dev.padconf_sate_mask); + + /* save the mode */ + if (muxmode) + *muxmode = padconf->muxmodes[(reg_val & ti_scm_dev.padconf_muxmode_mask)]; + + return (0); +} + +/** + * ti_scm_padconf_set_gpiomode - converts a pad to GPIO mode. + * @gpio: the GPIO pin number (0-195) + * @state: the state to put the pad/pin in, i.e. PADCONF_PIN_??? + * + * + * + * LOCKING: + * Internally locks it's own context. + * + * RETURNS: + * 0 on success. + * EINVAL if pin requested is outside valid range or already in use. + */ +int +ti_scm_padconf_set_gpiomode(uint32_t gpio, unsigned int state) +{ + const struct ti_scm_padconf *padconf; + uint16_t reg_val; + + if (!ti_scm_sc) + return (ENXIO); + + /* find the gpio pin in the padconf array */ + padconf = ti_scm_dev.padconf; + while (padconf->ballname != NULL) { + if (padconf->gpio_pin == gpio) + break; + padconf++; + } + if (padconf->ballname == NULL) + return (EINVAL); + + /* populate the new value for the PADCONF register */ + reg_val = (uint16_t)(state & ti_scm_dev.padconf_sate_mask); + + /* set the mux mode */ + reg_val |= (uint16_t)(padconf->gpio_mode & ti_scm_dev.padconf_muxmode_mask); + + /* write the register value (16-bit writes) */ + ti_scm_write_2(ti_scm_sc, padconf->reg_off, reg_val); + + return (0); +} + +/** + * ti_scm_padconf_get_gpiomode - gets the current GPIO mode of the pin + * @gpio: the GPIO pin number (0-195) + * @state: upon return will contain the state + * + * + * + * LOCKING: + * Internally locks it's own context. + * + * RETURNS: + * 0 on success. + * EINVAL if pin requested is outside valid range or not configured as GPIO. + */ +int +ti_scm_padconf_get_gpiomode(uint32_t gpio, unsigned int *state) +{ + const struct ti_scm_padconf *padconf; + uint16_t reg_val; + + if (!ti_scm_sc) + return (ENXIO); + + /* find the gpio pin in the padconf array */ + padconf = ti_scm_dev.padconf; + while (padconf->ballname != NULL) { + if (padconf->gpio_pin == gpio) + break; + padconf++; + } + if (padconf->ballname == NULL) + return (EINVAL); + + /* read the current register settings */ + reg_val = ti_scm_read_2(ti_scm_sc, padconf->reg_off); + + /* check to make sure the pins is configured as GPIO in the first state */ + if ((reg_val & ti_scm_dev.padconf_muxmode_mask) != padconf->gpio_mode) + return (EINVAL); + + /* read and store the reset of the state, i.e. pull-up, pull-down, etc */ + if (state) + *state = (reg_val & ti_scm_dev.padconf_sate_mask); + + return (0); +} + +/** + * ti_scm_padconf_init_from_hints - processes the hints for padconf + * @sc: the driver soft context + * + * + * + * LOCKING: + * Internally locks it's own context. + * + * RETURNS: + * 0 on success. + * EINVAL if pin requested is outside valid range or already in use. + */ +static int +ti_scm_padconf_init_from_fdt(struct ti_scm_softc *sc) +{ + const struct ti_scm_padconf *padconf; + const struct ti_scm_padstate *padstates; + int err; + phandle_t node; + int len; + char *fdt_pad_config; + int i; + char *padname, *muxname, *padstate; + + node = ofw_bus_get_node(sc->sc_dev); + len = OF_getproplen(node, "scm-pad-config"); + OF_getprop_alloc(node, "scm-pad-config", 1, (void **)&fdt_pad_config); + + i = len; + while (i > 0) { + padname = fdt_pad_config; + fdt_pad_config += strlen(padname) + 1; + i -= strlen(padname) + 1; + if (i <= 0) + break; + + muxname = fdt_pad_config; + fdt_pad_config += strlen(muxname) + 1; + i -= strlen(muxname) + 1; + if (i <= 0) + break; + + padstate = fdt_pad_config; + fdt_pad_config += strlen(padstate) + 1; + i -= strlen(padstate) + 1; + if (i < 0) + break; + + padconf = ti_scm_dev.padconf; + + while (padconf->ballname != NULL) { + if (strcmp(padconf->ballname, padname) == 0) { + padstates = ti_scm_dev.padstate; + err = 1; + while (padstates->state != NULL) { + if (strcmp(padstates->state, padstate) == 0) { + err = ti_scm_padconf_set_internal(sc, + padconf, muxname, padstates->reg); + } + padstates++; + } + if (err) + device_printf(sc->sc_dev, "err: failed to configure" + "pin \"%s\"\n", padconf->ballname); + } + padconf++; + } + } + return (0); +} + +/* + * Device part of OMAP SCM driver + */ + +static int +ti_scm_probe(device_t dev) +{ + if (!ofw_bus_is_compatible(dev, "ti,scm")) + return (ENXIO); + + 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 sucess or ENXIO if an error occuried. + */ +static int +ti_scm_attach(device_t dev) +{ + struct ti_scm_softc *sc = device_get_softc(dev); + + if (ti_scm_sc) + return (ENXIO); + + 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; + + ti_scm_padconf_init_from_fdt(sc); + + return (0); +} + +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; + +DRIVER_MODULE(ti_scm, simplebus, ti_scm_driver, ti_scm_devclass, 0, 0); diff --git a/sys/arm/ti/ti_scm.h b/sys/arm/ti/ti_scm.h new file mode 100644 index 000000000000..a2b11940c0f7 --- /dev/null +++ b/sys/arm/ti/ti_scm.h @@ -0,0 +1,84 @@ +/* + * 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_padconf { + uint16_t reg_off; + uint16_t gpio_pin; + uint16_t gpio_mode; + const char *ballname; + const char *muxmodes[8]; +}; + +struct ti_scm_padstate { + const char *state; + uint16_t reg; +}; + +struct ti_scm_device { + uint16_t padconf_muxmode_mask; + uint16_t padconf_sate_mask; + struct ti_scm_padstate *padstate; + struct ti_scm_padconf *padconf; +}; + +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_padconf_set(const char *padname, const char *muxmode, + unsigned int state); +int ti_scm_padconf_get(const char *padname, const char **muxmode, + unsigned int *state); +int ti_scm_padconf_set_gpiomode(uint32_t gpio, unsigned int state); +int ti_scm_padconf_get_gpiomode(uint32_t gpio, unsigned int *state); +int ti_scm_padconf_set_gpioflags(uint32_t gpio, uint32_t flags); +void ti_scm_padconf_get_gpioflags(uint32_t gpio, uint32_t *flags); +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/sys/arm/ti/ti_sdma.c b/sys/arm/ti/ti_sdma.c new file mode 100644 index 000000000000..4c55c41e31c5 --- /dev/null +++ b/sys/arm/ti/ti_sdma.c @@ -0,0 +1,1246 @@ +/*- + * 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. + */ + +#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 + +/** + * Kernel functions for using the DMA controller + * + * + * DMA TRANSFERS: + * A DMA transfer block consists of a number of frames (FN). Each frame + * consists of a number of elements, and each element can have a size of 8, 16, + * or 32 bits. + * + * OMAP44xx and newer chips support linked list (aka scatter gather) transfers, + * where a linked list of source/destination pairs can be placed in memory + * for the H/W to process. Earlier chips only allowed you to chain multiple + * channels together. However currently this linked list feature is not + * supported by the driver. + * + */ + +/** + * Data structure per DMA channel. + * + * + */ +struct ti_sdma_channel { + + /* + * The configuration registers for the given channel, these are modified + * by the set functions and only written to the actual registers when a + * transaction is started. + */ + uint32_t reg_csdp; + uint32_t reg_ccr; + uint32_t reg_cicr; + + /* Set when one of the configuration registers above change */ + uint32_t need_reg_write; + + /* Callback function used when an interrupt is tripped on the given channel */ + void (*callback)(unsigned int ch, uint32_t ch_status, void *data); + + /* Callback data passed in the callback ... duh */ + void* callback_data; + +}; + +/** + * DMA driver context, allocated and stored globally, this driver is not + * intetned to ever be unloaded (see ti_sdma_sc). + * + */ +struct ti_sdma_softc { + device_t sc_dev; + struct resource* sc_irq_res; + struct resource* sc_mem_res; + + /* + * I guess in theory we should have a mutex per DMA channel for register + * modifications. But since we know we are never going to be run on a SMP + * system, we can use just the single lock for all channels. + */ + struct mtx sc_mtx; + + /* Stores the H/W revision read from the registers */ + uint32_t sc_hw_rev; + + /* + * Bits in the sc_active_channels data field indicate if the channel has + * been activated. + */ + uint32_t sc_active_channels; + + struct ti_sdma_channel sc_channel[NUM_DMA_CHANNELS]; + +}; + +static struct ti_sdma_softc *ti_sdma_sc = NULL; + +/** + * Macros for driver mutex locking + */ +#define TI_SDMA_LOCK(_sc) mtx_lock_spin(&(_sc)->sc_mtx) +#define TI_SDMA_UNLOCK(_sc) mtx_unlock_spin(&(_sc)->sc_mtx) +#define TI_SDMA_LOCK_INIT(_sc) \ + mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->sc_dev), \ + "ti_sdma", MTX_SPIN) +#define TI_SDMA_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); +#define TI_SDMA_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED); +#define TI_SDMA_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED); + +/** + * Function prototypes + * + */ +static void ti_sdma_intr(void *); + +/** + * ti_sdma_read_4 - reads a 32-bit value from one of the DMA registers + * @sc: DMA device context + * @off: The offset of a register from the DMA register address range + * + * + * RETURNS: + * 32-bit value read from the register. + */ +static inline uint32_t +ti_sdma_read_4(struct ti_sdma_softc *sc, bus_size_t off) +{ + return bus_read_4(sc->sc_mem_res, off); +} + +/** + * ti_sdma_write_4 - writes a 32-bit value to one of the DMA registers + * @sc: DMA device context + * @off: The offset of a register from the DMA register address range + * + * + * RETURNS: + * 32-bit value read from the register. + */ +static inline void +ti_sdma_write_4(struct ti_sdma_softc *sc, bus_size_t off, uint32_t val) +{ + bus_write_4(sc->sc_mem_res, off, val); +} + +/** + * ti_sdma_is_omap3_rev - returns true if H/W is from OMAP3 series + * @sc: DMA device context + * + */ +static inline int +ti_sdma_is_omap3_rev(struct ti_sdma_softc *sc) +{ + return (sc->sc_hw_rev == DMA4_OMAP3_REV); +} + +/** + * ti_sdma_is_omap4_rev - returns true if H/W is from OMAP4 series + * @sc: DMA device context + * + */ +static inline int +ti_sdma_is_omap4_rev(struct ti_sdma_softc *sc) +{ + return (sc->sc_hw_rev == DMA4_OMAP4_REV); +} + +/** + * ti_sdma_intr - interrupt handler for all 4 DMA IRQs + * @arg: ignored + * + * Called when any of the four DMA IRQs are triggered. + * + * LOCKING: + * DMA registers protected by internal mutex + * + * RETURNS: + * nothing + */ +static void +ti_sdma_intr(void *arg) +{ + struct ti_sdma_softc *sc = ti_sdma_sc; + uint32_t intr; + uint32_t csr; + unsigned int ch, j; + struct ti_sdma_channel* channel; + + TI_SDMA_LOCK(sc); + + for (j = 0; j < NUM_DMA_IRQS; j++) { + + /* Get the flag interrupts (enabled) */ + intr = ti_sdma_read_4(sc, DMA4_IRQSTATUS_L(j)); + intr &= ti_sdma_read_4(sc, DMA4_IRQENABLE_L(j)); + if (intr == 0x00000000) + continue; + + /* Loop through checking the status bits */ + for (ch = 0; ch < NUM_DMA_CHANNELS; ch++) { + if (intr & (1 << ch)) { + channel = &sc->sc_channel[ch]; + + /* Read the CSR regsiter and verify we don't have a spurious IRQ */ + csr = ti_sdma_read_4(sc, DMA4_CSR(ch)); + if (csr == 0) { + device_printf(sc->sc_dev, "Spurious DMA IRQ for channel " + "%d\n", ch); + continue; + } + + /* Sanity check this channel is active */ + if ((sc->sc_active_channels & (1 << ch)) == 0) { + device_printf(sc->sc_dev, "IRQ %d for a non-activated " + "channel %d\n", j, ch); + continue; + } + + /* Check the status error codes */ + if (csr & DMA4_CSR_DROP) + device_printf(sc->sc_dev, "Synchronization event drop " + "occurred during the transfer on channel %u\n", + ch); + if (csr & DMA4_CSR_SECURE_ERR) + device_printf(sc->sc_dev, "Secure transaction error event " + "on channel %u\n", ch); + if (csr & DMA4_CSR_MISALIGNED_ADRS_ERR) + device_printf(sc->sc_dev, "Misaligned address error event " + "on channel %u\n", ch); + if (csr & DMA4_CSR_TRANS_ERR) { + device_printf(sc->sc_dev, "Transaction error event on " + "channel %u\n", ch); + /* + * Apparently according to linux code, there is an errata + * that says the channel is not disabled upon this error. + * They explicitly disable the channel here .. since I + * haven't seen the errata, I'm going to ignore for now. + */ + } + + /* Clear the status flags for the IRQ */ + ti_sdma_write_4(sc, DMA4_CSR(ch), DMA4_CSR_CLEAR_MASK); + ti_sdma_write_4(sc, DMA4_IRQSTATUS_L(j), (1 << ch)); + + /* Call the callback for the given channel */ + if (channel->callback) + channel->callback(ch, csr, channel->callback_data); + } + } + } + + TI_SDMA_UNLOCK(sc); + + return; +} + +/** + * ti_sdma_activate_channel - activates a DMA channel + * @ch: upon return contains the channel allocated + * @callback: a callback function to associate with the channel + * @data: optional data supplied when the callback is called + * + * Simply activates a channel be enabling and writing default values to the + * channel's register set. It doesn't start a transaction, just populates the + * internal data structures and sets defaults. + * + * Note this function doesn't enable interrupts, for that you need to call + * ti_sdma_enable_channel_irq(). If not using IRQ to detect the end of the + * transfer, you can use ti_sdma_status_poll() to detect a change in the + * status. + * + * A channel must be activated before any of the other DMA functions can be + * called on it. + * + * LOCKING: + * DMA registers protected by internal mutex + * + * RETURNS: + * 0 on success, otherwise an error code + */ +int +ti_sdma_activate_channel(unsigned int *ch, + void (*callback)(unsigned int ch, uint32_t status, void *data), + void *data) +{ + struct ti_sdma_softc *sc = ti_sdma_sc; + struct ti_sdma_channel *channel = NULL; + uint32_t addr; + unsigned int i; + + /* Sanity check */ + if (sc == NULL) + return (ENOMEM); + + if (ch == NULL) + return (EINVAL); + + TI_SDMA_LOCK(sc); + + /* Check to see if all channels are in use */ + if (sc->sc_active_channels == 0xffffffff) { + TI_SDMA_UNLOCK(sc); + return (ENOMEM); + } + + /* Find the first non-active channel */ + for (i = 0; i < NUM_DMA_CHANNELS; i++) { + if (!(sc->sc_active_channels & (0x1 << i))) { + sc->sc_active_channels |= (0x1 << i); + *ch = i; + break; + } + } + + /* Get the channel struct and populate the fields */ + channel = &sc->sc_channel[*ch]; + + channel->callback = callback; + channel->callback_data = data; + + channel->need_reg_write = 1; + + /* Set the default configuration for the DMA channel */ + channel->reg_csdp = DMA4_CSDP_DATA_TYPE(0x2) + | DMA4_CSDP_SRC_BURST_MODE(0) + | DMA4_CSDP_DST_BURST_MODE(0) + | DMA4_CSDP_SRC_ENDIANISM(0) + | DMA4_CSDP_DST_ENDIANISM(0) + | DMA4_CSDP_WRITE_MODE(0) + | DMA4_CSDP_SRC_PACKED(0) + | DMA4_CSDP_DST_PACKED(0); + + channel->reg_ccr = DMA4_CCR_DST_ADDRESS_MODE(1) + | DMA4_CCR_SRC_ADDRESS_MODE(1) + | DMA4_CCR_READ_PRIORITY(0) + | DMA4_CCR_WRITE_PRIORITY(0) + | DMA4_CCR_SYNC_TRIGGER(0) + | DMA4_CCR_FRAME_SYNC(0) + | DMA4_CCR_BLOCK_SYNC(0); + + channel->reg_cicr = DMA4_CICR_TRANS_ERR_IE + | DMA4_CICR_SECURE_ERR_IE + | DMA4_CICR_SUPERVISOR_ERR_IE + | DMA4_CICR_MISALIGNED_ADRS_ERR_IE; + + /* Clear all the channel registers, this should abort any transaction */ + for (addr = DMA4_CCR(*ch); addr <= DMA4_COLOR(*ch); addr += 4) + ti_sdma_write_4(sc, addr, 0x00000000); + + TI_SDMA_UNLOCK(sc); + + return 0; +} + +/** + * ti_sdma_deactivate_channel - deactivates a channel + * @ch: the channel to deactivate + * + * + * + * LOCKING: + * DMA registers protected by internal mutex + * + * RETURNS: + * EH_HANDLED or EH_NOT_HANDLED + */ +int +ti_sdma_deactivate_channel(unsigned int ch) +{ + struct ti_sdma_softc *sc = ti_sdma_sc; + unsigned int j; + unsigned int addr; + + /* Sanity check */ + if (sc == NULL) + return (ENOMEM); + + TI_SDMA_LOCK(sc); + + /* First check if the channel is currently active */ + if ((sc->sc_active_channels & (1 << ch)) == 0) { + TI_SDMA_UNLOCK(sc); + return (EBUSY); + } + + /* Mark the channel as inactive */ + sc->sc_active_channels &= ~(1 << ch); + + /* Disable all DMA interrupts for the channel. */ + ti_sdma_write_4(sc, DMA4_CICR(ch), 0); + + /* Make sure the DMA transfer is stopped. */ + ti_sdma_write_4(sc, DMA4_CCR(ch), 0); + + /* Clear the CSR register and IRQ status register */ + ti_sdma_write_4(sc, DMA4_CSR(ch), DMA4_CSR_CLEAR_MASK); + for (j = 0; j < NUM_DMA_IRQS; j++) { + ti_sdma_write_4(sc, DMA4_IRQSTATUS_L(j), (1 << ch)); + } + + /* Clear all the channel registers, this should abort any transaction */ + for (addr = DMA4_CCR(ch); addr <= DMA4_COLOR(ch); addr += 4) + ti_sdma_write_4(sc, addr, 0x00000000); + + TI_SDMA_UNLOCK(sc); + + return 0; +} + +/** + * ti_sdma_disable_channel_irq - disables IRQ's on the given channel + * @ch: the channel to disable IRQ's on + * + * Disable interupt generation for the given channel. + * + * LOCKING: + * DMA registers protected by internal mutex + * + * RETURNS: + * EH_HANDLED or EH_NOT_HANDLED + */ +int +ti_sdma_disable_channel_irq(unsigned int ch) +{ + struct ti_sdma_softc *sc = ti_sdma_sc; + uint32_t irq_enable; + unsigned int j; + + /* Sanity check */ + if (sc == NULL) + return (ENOMEM); + + TI_SDMA_LOCK(sc); + + if ((sc->sc_active_channels & (1 << ch)) == 0) { + TI_SDMA_UNLOCK(sc); + return (EINVAL); + } + + /* Disable all the individual error conditions */ + sc->sc_channel[ch].reg_cicr = 0x0000; + ti_sdma_write_4(sc, DMA4_CICR(ch), 0x0000); + + /* Disable the channel interrupt enable */ + for (j = 0; j < NUM_DMA_IRQS; j++) { + irq_enable = ti_sdma_read_4(sc, DMA4_IRQENABLE_L(j)); + irq_enable &= ~(1 << ch); + + ti_sdma_write_4(sc, DMA4_IRQENABLE_L(j), irq_enable); + } + + /* Indicate the registers need to be rewritten on the next transaction */ + sc->sc_channel[ch].need_reg_write = 1; + + TI_SDMA_UNLOCK(sc); + + return (0); +} + +/** + * ti_sdma_disable_channel_irq - enables IRQ's on the given channel + * @ch: the channel to enable IRQ's on + * @flags: bitmask of interrupt types to enable + * + * Flags can be a bitmask of the following options: + * DMA_IRQ_FLAG_DROP + * DMA_IRQ_FLAG_HALF_FRAME_COMPL + * DMA_IRQ_FLAG_FRAME_COMPL + * DMA_IRQ_FLAG_START_LAST_FRAME + * DMA_IRQ_FLAG_BLOCK_COMPL + * DMA_IRQ_FLAG_ENDOF_PKT + * DMA_IRQ_FLAG_DRAIN + * + * + * LOCKING: + * DMA registers protected by internal mutex + * + * RETURNS: + * EH_HANDLED or EH_NOT_HANDLED + */ +int +ti_sdma_enable_channel_irq(unsigned int ch, uint32_t flags) +{ + struct ti_sdma_softc *sc = ti_sdma_sc; + uint32_t irq_enable; + + /* Sanity check */ + if (sc == NULL) + return (ENOMEM); + + TI_SDMA_LOCK(sc); + + if ((sc->sc_active_channels & (1 << ch)) == 0) { + TI_SDMA_UNLOCK(sc); + return (EINVAL); + } + + /* Always enable the error interrupts if we have interrupts enabled */ + flags |= DMA4_CICR_TRANS_ERR_IE | DMA4_CICR_SECURE_ERR_IE | + DMA4_CICR_SUPERVISOR_ERR_IE | DMA4_CICR_MISALIGNED_ADRS_ERR_IE; + + sc->sc_channel[ch].reg_cicr = flags; + + /* Write the values to the register */ + ti_sdma_write_4(sc, DMA4_CICR(ch), flags); + + /* Enable the channel interrupt enable */ + irq_enable = ti_sdma_read_4(sc, DMA4_IRQENABLE_L(0)); + irq_enable |= (1 << ch); + + ti_sdma_write_4(sc, DMA4_IRQENABLE_L(0), irq_enable); + + /* Indicate the registers need to be rewritten on the next transaction */ + sc->sc_channel[ch].need_reg_write = 1; + + TI_SDMA_UNLOCK(sc); + + return (0); +} + +/** + * ti_sdma_get_channel_status - returns the status of a given channel + * @ch: the channel number to get the status of + * @status: upon return will contain the status bitmask, see below for possible + * values. + * + * DMA_STATUS_DROP + * DMA_STATUS_HALF + * DMA_STATUS_FRAME + * DMA_STATUS_LAST + * DMA_STATUS_BLOCK + * DMA_STATUS_SYNC + * DMA_STATUS_PKT + * DMA_STATUS_TRANS_ERR + * DMA_STATUS_SECURE_ERR + * DMA_STATUS_SUPERVISOR_ERR + * DMA_STATUS_MISALIGNED_ADRS_ERR + * DMA_STATUS_DRAIN_END + * + * + * LOCKING: + * DMA registers protected by internal mutex + * + * RETURNS: + * EH_HANDLED or EH_NOT_HANDLED + */ +int +ti_sdma_get_channel_status(unsigned int ch, uint32_t *status) +{ + struct ti_sdma_softc *sc = ti_sdma_sc; + uint32_t csr; + + /* Sanity check */ + if (sc == NULL) + return (ENOMEM); + + TI_SDMA_LOCK(sc); + + if ((sc->sc_active_channels & (1 << ch)) == 0) { + TI_SDMA_UNLOCK(sc); + return (EINVAL); + } + + TI_SDMA_UNLOCK(sc); + + csr = ti_sdma_read_4(sc, DMA4_CSR(ch)); + + if (status != NULL) + *status = csr; + + return (0); +} + +/** + * ti_sdma_start_xfer - starts a DMA transfer + * @ch: the channel number to set the endianess of + * @src_paddr: the source phsyical address + * @dst_paddr: the destination phsyical address + * @frmcnt: the number of frames per block + * @elmcnt: the number of elements in a frame, an element is either an 8, 16 + * or 32-bit value as defined by ti_sdma_set_xfer_burst() + * + * + * LOCKING: + * DMA registers protected by internal mutex + * + * RETURNS: + * EH_HANDLED or EH_NOT_HANDLED + */ +int +ti_sdma_start_xfer(unsigned int ch, unsigned int src_paddr, + unsigned long dst_paddr, + unsigned int frmcnt, unsigned int elmcnt) +{ + struct ti_sdma_softc *sc = ti_sdma_sc; + struct ti_sdma_channel *channel; + uint32_t ccr; + + /* Sanity check */ + if (sc == NULL) + return (ENOMEM); + + TI_SDMA_LOCK(sc); + + if ((sc->sc_active_channels & (1 << ch)) == 0) { + TI_SDMA_UNLOCK(sc); + return (EINVAL); + } + + channel = &sc->sc_channel[ch]; + + /* a) Write the CSDP register */ + ti_sdma_write_4(sc, DMA4_CSDP(ch), + channel->reg_csdp | DMA4_CSDP_WRITE_MODE(1)); + + /* b) Set the number of element per frame CEN[23:0] */ + ti_sdma_write_4(sc, DMA4_CEN(ch), elmcnt); + + /* c) Set the number of frame per block CFN[15:0] */ + ti_sdma_write_4(sc, DMA4_CFN(ch), frmcnt); + + /* d) Set the Source/dest start address index CSSA[31:0]/CDSA[31:0] */ + ti_sdma_write_4(sc, DMA4_CSSA(ch), src_paddr); + ti_sdma_write_4(sc, DMA4_CDSA(ch), dst_paddr); + + /* e) Write the CCR register */ + ti_sdma_write_4(sc, DMA4_CCR(ch), channel->reg_ccr); + + /* f) - Set the source element index increment CSEI[15:0] */ + ti_sdma_write_4(sc, DMA4_CSE(ch), 0x0001); + + /* - Set the source frame index increment CSFI[15:0] */ + ti_sdma_write_4(sc, DMA4_CSF(ch), 0x0001); + + /* - Set the destination element index increment CDEI[15:0]*/ + ti_sdma_write_4(sc, DMA4_CDE(ch), 0x0001); + + /* - Set the destination frame index increment CDFI[31:0] */ + ti_sdma_write_4(sc, DMA4_CDF(ch), 0x0001); + + /* Clear the status register */ + ti_sdma_write_4(sc, DMA4_CSR(ch), 0x1FFE); + + /* Write the start-bit and away we go */ + ccr = ti_sdma_read_4(sc, DMA4_CCR(ch)); + ccr |= (1 << 7); + ti_sdma_write_4(sc, DMA4_CCR(ch), ccr); + + /* Clear the reg write flag */ + channel->need_reg_write = 0; + + TI_SDMA_UNLOCK(sc); + + return (0); +} + +/** + * ti_sdma_start_xfer_packet - starts a packet DMA transfer + * @ch: the channel number to use for the transfer + * @src_paddr: the source physical address + * @dst_paddr: the destination physical address + * @frmcnt: the number of frames to transfer + * @elmcnt: the number of elements in a frame, an element is either an 8, 16 + * or 32-bit value as defined by ti_sdma_set_xfer_burst() + * @pktsize: the number of elements in each transfer packet + * + * The @frmcnt and @elmcnt define the overall number of bytes to transfer, + * typically @frmcnt is 1 and @elmcnt contains the total number of elements. + * @pktsize is the size of each individual packet, there might be multiple + * packets per transfer. i.e. for the following with element size of 32-bits + * + * frmcnt = 1, elmcnt = 512, pktsize = 128 + * + * Total transfer bytes = 1 * 512 = 512 elements or 2048 bytes + * Packets transfered = 128 / 512 = 4 + * + * + * LOCKING: + * DMA registers protected by internal mutex + * + * RETURNS: + * EH_HANDLED or EH_NOT_HANDLED + */ +int +ti_sdma_start_xfer_packet(unsigned int ch, unsigned int src_paddr, + unsigned long dst_paddr, unsigned int frmcnt, + unsigned int elmcnt, unsigned int pktsize) +{ + struct ti_sdma_softc *sc = ti_sdma_sc; + struct ti_sdma_channel *channel; + uint32_t ccr; + + /* Sanity check */ + if (sc == NULL) + return (ENOMEM); + + TI_SDMA_LOCK(sc); + + if ((sc->sc_active_channels & (1 << ch)) == 0) { + TI_SDMA_UNLOCK(sc); + return (EINVAL); + } + + channel = &sc->sc_channel[ch]; + + /* a) Write the CSDP register */ + if (channel->need_reg_write) + ti_sdma_write_4(sc, DMA4_CSDP(ch), + channel->reg_csdp | DMA4_CSDP_WRITE_MODE(1)); + + /* b) Set the number of elements to transfer CEN[23:0] */ + ti_sdma_write_4(sc, DMA4_CEN(ch), elmcnt); + + /* c) Set the number of frames to transfer CFN[15:0] */ + ti_sdma_write_4(sc, DMA4_CFN(ch), frmcnt); + + /* d) Set the Source/dest start address index CSSA[31:0]/CDSA[31:0] */ + ti_sdma_write_4(sc, DMA4_CSSA(ch), src_paddr); + ti_sdma_write_4(sc, DMA4_CDSA(ch), dst_paddr); + + /* e) Write the CCR register */ + ti_sdma_write_4(sc, DMA4_CCR(ch), + channel->reg_ccr | DMA4_CCR_PACKET_TRANS); + + /* f) - Set the source element index increment CSEI[15:0] */ + ti_sdma_write_4(sc, DMA4_CSE(ch), 0x0001); + + /* - Set the packet size, this is dependent on the sync source */ + if (channel->reg_ccr & DMA4_CCR_SEL_SRC_DST_SYNC(1)) + ti_sdma_write_4(sc, DMA4_CSF(ch), pktsize); + else + ti_sdma_write_4(sc, DMA4_CDF(ch), pktsize); + + /* - Set the destination frame index increment CDFI[31:0] */ + ti_sdma_write_4(sc, DMA4_CDE(ch), 0x0001); + + /* Clear the status register */ + ti_sdma_write_4(sc, DMA4_CSR(ch), 0x1FFE); + + /* Write the start-bit and away we go */ + ccr = ti_sdma_read_4(sc, DMA4_CCR(ch)); + ccr |= (1 << 7); + ti_sdma_write_4(sc, DMA4_CCR(ch), ccr); + + /* Clear the reg write flag */ + channel->need_reg_write = 0; + + TI_SDMA_UNLOCK(sc); + + return (0); +} + +/** + * ti_sdma_stop_xfer - stops any currently active transfers + * @ch: the channel number to set the endianess of + * + * This function call is effectively a NOP if no transaction is in progress. + * + * LOCKING: + * DMA registers protected by internal mutex + * + * RETURNS: + * EH_HANDLED or EH_NOT_HANDLED + */ +int +ti_sdma_stop_xfer(unsigned int ch) +{ + struct ti_sdma_softc *sc = ti_sdma_sc; + unsigned int j; + + /* Sanity check */ + if (sc == NULL) + return (ENOMEM); + + TI_SDMA_LOCK(sc); + + if ((sc->sc_active_channels & (1 << ch)) == 0) { + TI_SDMA_UNLOCK(sc); + return (EINVAL); + } + + /* Disable all DMA interrupts for the channel. */ + ti_sdma_write_4(sc, DMA4_CICR(ch), 0); + + /* Make sure the DMA transfer is stopped. */ + ti_sdma_write_4(sc, DMA4_CCR(ch), 0); + + /* Clear the CSR register and IRQ status register */ + ti_sdma_write_4(sc, DMA4_CSR(ch), DMA4_CSR_CLEAR_MASK); + for (j = 0; j < NUM_DMA_IRQS; j++) { + ti_sdma_write_4(sc, DMA4_IRQSTATUS_L(j), (1 << ch)); + } + + /* Configuration registers need to be re-written on the next xfer */ + sc->sc_channel[ch].need_reg_write = 1; + + TI_SDMA_UNLOCK(sc); + + return (0); +} + +/** + * ti_sdma_set_xfer_endianess - sets the endianess of subsequent transfers + * @ch: the channel number to set the endianess of + * @src: the source endianess (either DMA_ENDIAN_LITTLE or DMA_ENDIAN_BIG) + * @dst: the destination endianess (either DMA_ENDIAN_LITTLE or DMA_ENDIAN_BIG) + * + * + * LOCKING: + * DMA registers protected by internal mutex + * + * RETURNS: + * EH_HANDLED or EH_NOT_HANDLED + */ +int +ti_sdma_set_xfer_endianess(unsigned int ch, unsigned int src, unsigned int dst) +{ + struct ti_sdma_softc *sc = ti_sdma_sc; + + /* Sanity check */ + if (sc == NULL) + return (ENOMEM); + + TI_SDMA_LOCK(sc); + + if ((sc->sc_active_channels & (1 << ch)) == 0) { + TI_SDMA_UNLOCK(sc); + return (EINVAL); + } + + sc->sc_channel[ch].reg_csdp &= ~DMA4_CSDP_SRC_ENDIANISM(1); + sc->sc_channel[ch].reg_csdp |= DMA4_CSDP_SRC_ENDIANISM(src); + + sc->sc_channel[ch].reg_csdp &= ~DMA4_CSDP_DST_ENDIANISM(1); + sc->sc_channel[ch].reg_csdp |= DMA4_CSDP_DST_ENDIANISM(dst); + + sc->sc_channel[ch].need_reg_write = 1; + + TI_SDMA_UNLOCK(sc); + + return 0; +} + +/** + * ti_sdma_set_xfer_burst - sets the source and destination element size + * @ch: the channel number to set the burst settings of + * @src: the source endianess (either DMA_BURST_NONE, DMA_BURST_16, DMA_BURST_32 + * or DMA_BURST_64) + * @dst: the destination endianess (either DMA_BURST_NONE, DMA_BURST_16, + * DMA_BURST_32 or DMA_BURST_64) + * + * This function sets the size of the elements for all subsequent transfers. + * + * LOCKING: + * DMA registers protected by internal mutex + * + * RETURNS: + * EH_HANDLED or EH_NOT_HANDLED + */ +int +ti_sdma_set_xfer_burst(unsigned int ch, unsigned int src, unsigned int dst) +{ + struct ti_sdma_softc *sc = ti_sdma_sc; + + /* Sanity check */ + if (sc == NULL) + return (ENOMEM); + + TI_SDMA_LOCK(sc); + + if ((sc->sc_active_channels & (1 << ch)) == 0) { + TI_SDMA_UNLOCK(sc); + return (EINVAL); + } + + sc->sc_channel[ch].reg_csdp &= ~DMA4_CSDP_SRC_BURST_MODE(0x3); + sc->sc_channel[ch].reg_csdp |= DMA4_CSDP_SRC_BURST_MODE(src); + + sc->sc_channel[ch].reg_csdp &= ~DMA4_CSDP_DST_BURST_MODE(0x3); + sc->sc_channel[ch].reg_csdp |= DMA4_CSDP_DST_BURST_MODE(dst); + + sc->sc_channel[ch].need_reg_write = 1; + + TI_SDMA_UNLOCK(sc); + + return 0; +} + +/** + * ti_sdma_set_xfer_data_type - driver attach function + * @ch: the channel number to set the endianess of + * @type: the xfer data type (either DMA_DATA_8BITS_SCALAR, DMA_DATA_16BITS_SCALAR + * or DMA_DATA_32BITS_SCALAR) + * + * + * LOCKING: + * DMA registers protected by internal mutex + * + * RETURNS: + * EH_HANDLED or EH_NOT_HANDLED + */ +int +ti_sdma_set_xfer_data_type(unsigned int ch, unsigned int type) +{ + struct ti_sdma_softc *sc = ti_sdma_sc; + + /* Sanity check */ + if (sc == NULL) + return (ENOMEM); + + TI_SDMA_LOCK(sc); + + if ((sc->sc_active_channels & (1 << ch)) == 0) { + TI_SDMA_UNLOCK(sc); + return (EINVAL); + } + + sc->sc_channel[ch].reg_csdp &= ~DMA4_CSDP_DATA_TYPE(0x3); + sc->sc_channel[ch].reg_csdp |= DMA4_CSDP_DATA_TYPE(type); + + sc->sc_channel[ch].need_reg_write = 1; + + TI_SDMA_UNLOCK(sc); + + return 0; +} + +/** + * ti_sdma_set_callback - driver attach function + * @dev: dma device handle + * + * + * + * LOCKING: + * DMA registers protected by internal mutex + * + * RETURNS: + * EH_HANDLED or EH_NOT_HANDLED + */ +int +ti_sdma_set_callback(unsigned int ch, + void (*callback)(unsigned int ch, uint32_t status, void *data), + void *data) +{ + struct ti_sdma_softc *sc = ti_sdma_sc; + + /* Sanity check */ + if (sc == NULL) + return (ENOMEM); + + TI_SDMA_LOCK(sc); + + if ((sc->sc_active_channels & (1 << ch)) == 0) { + TI_SDMA_UNLOCK(sc); + return (EINVAL); + } + + sc->sc_channel[ch].callback = callback; + sc->sc_channel[ch].callback_data = data; + + sc->sc_channel[ch].need_reg_write = 1; + + TI_SDMA_UNLOCK(sc); + + return 0; +} + +/** + * ti_sdma_sync_params - sets channel sync settings + * @ch: the channel number to set the sync on + * @trigger: the number of the sync trigger, this depends on what other H/W + * module is triggering/receiving the DMA transactions + * @mode: flags describing the sync mode to use, it may have one or more of + * the following bits set; TI_SDMA_SYNC_FRAME, + * TI_SDMA_SYNC_BLOCK, TI_SDMA_SYNC_TRIG_ON_SRC. + * + * + * + * LOCKING: + * DMA registers protected by internal mutex + * + * RETURNS: + * EH_HANDLED or EH_NOT_HANDLED + */ +int +ti_sdma_sync_params(unsigned int ch, unsigned int trigger, unsigned int mode) +{ + struct ti_sdma_softc *sc = ti_sdma_sc; + uint32_t ccr; + + /* Sanity check */ + if (sc == NULL) + return (ENOMEM); + + TI_SDMA_LOCK(sc); + + if ((sc->sc_active_channels & (1 << ch)) == 0) { + TI_SDMA_UNLOCK(sc); + return (EINVAL); + } + + ccr = sc->sc_channel[ch].reg_ccr; + + ccr &= ~DMA4_CCR_SYNC_TRIGGER(0x7F); + ccr |= DMA4_CCR_SYNC_TRIGGER(trigger + 1); + + if (mode & TI_SDMA_SYNC_FRAME) + ccr |= DMA4_CCR_FRAME_SYNC(1); + else + ccr &= ~DMA4_CCR_FRAME_SYNC(1); + + if (mode & TI_SDMA_SYNC_BLOCK) + ccr |= DMA4_CCR_BLOCK_SYNC(1); + else + ccr &= ~DMA4_CCR_BLOCK_SYNC(1); + + if (mode & TI_SDMA_SYNC_TRIG_ON_SRC) + ccr |= DMA4_CCR_SEL_SRC_DST_SYNC(1); + else + ccr &= ~DMA4_CCR_SEL_SRC_DST_SYNC(1); + + sc->sc_channel[ch].reg_ccr = ccr; + + sc->sc_channel[ch].need_reg_write = 1; + + TI_SDMA_UNLOCK(sc); + + return 0; +} + +/** + * ti_sdma_set_addr_mode - driver attach function + * @ch: the channel number to set the endianess of + * @rd_mode: the xfer source addressing mode (either DMA_ADDR_CONSTANT, + * DMA_ADDR_POST_INCREMENT, DMA_ADDR_SINGLE_INDEX or + * DMA_ADDR_DOUBLE_INDEX) + * @wr_mode: the xfer destination addressing mode (either DMA_ADDR_CONSTANT, + * DMA_ADDR_POST_INCREMENT, DMA_ADDR_SINGLE_INDEX or + * DMA_ADDR_DOUBLE_INDEX) + * + * + * LOCKING: + * DMA registers protected by internal mutex + * + * RETURNS: + * EH_HANDLED or EH_NOT_HANDLED + */ +int +ti_sdma_set_addr_mode(unsigned int ch, unsigned int src_mode, + unsigned int dst_mode) +{ + struct ti_sdma_softc *sc = ti_sdma_sc; + uint32_t ccr; + + /* Sanity check */ + if (sc == NULL) + return (ENOMEM); + + TI_SDMA_LOCK(sc); + + if ((sc->sc_active_channels & (1 << ch)) == 0) { + TI_SDMA_UNLOCK(sc); + return (EINVAL); + } + + ccr = sc->sc_channel[ch].reg_ccr; + + ccr &= ~DMA4_CCR_SRC_ADDRESS_MODE(0x3); + ccr |= DMA4_CCR_SRC_ADDRESS_MODE(src_mode); + + ccr &= ~DMA4_CCR_DST_ADDRESS_MODE(0x3); + ccr |= DMA4_CCR_DST_ADDRESS_MODE(dst_mode); + + sc->sc_channel[ch].reg_ccr = ccr; + + sc->sc_channel[ch].need_reg_write = 1; + + TI_SDMA_UNLOCK(sc); + + return 0; +} + +/** + * ti_sdma_probe - driver probe function + * @dev: dma device handle + * + * + * + * RETURNS: + * Always returns 0. + */ +static int +ti_sdma_probe(device_t dev) +{ + if (!ofw_bus_is_compatible(dev, "ti,sdma")) + return (ENXIO); + + device_set_desc(dev, "TI sDMA Controller"); + return (0); +} + +/** + * ti_sdma_attach - driver attach function + * @dev: dma device handle + * + * Initialises memory mapping/pointers to the DMA register set and requests + * IRQs. This is effectively the setup function for the driver. + * + * RETURNS: + * 0 on success or a negative error code failure. + */ +static int +ti_sdma_attach(device_t dev) +{ + struct ti_sdma_softc *sc = device_get_softc(dev); + unsigned int timeout; + unsigned int i; + int rid; + void *ihl; + int err; + + /* Setup the basics */ + sc->sc_dev = dev; + + /* No channels active at the moment */ + sc->sc_active_channels = 0x00000000; + + /* Mutex to protect the shared data structures */ + TI_SDMA_LOCK_INIT(sc); + + /* Get the memory resource for the register mapping */ + rid = 0; + sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); + if (sc->sc_mem_res == NULL) + panic("%s: Cannot map registers", device_get_name(dev)); + + /* Enable the interface and functional clocks */ + ti_prcm_clk_enable(SDMA_CLK); + + /* Read the sDMA revision register and sanity check it's known */ + sc->sc_hw_rev = ti_sdma_read_4(sc, DMA4_REVISION); + device_printf(dev, "sDMA revision %08x\n", sc->sc_hw_rev); + + if (!ti_sdma_is_omap4_rev(sc) && !ti_sdma_is_omap3_rev(sc)) { + device_printf(sc->sc_dev, "error - unknown sDMA H/W revision\n"); + return (EINVAL); + } + + /* Disable all interrupts */ + for (i = 0; i < NUM_DMA_IRQS; i++) { + ti_sdma_write_4(sc, DMA4_IRQENABLE_L(i), 0x00000000); + } + + /* Soft-reset is only supported on pre-OMAP44xx devices */ + if (ti_sdma_is_omap3_rev(sc)) { + + /* Soft-reset */ + ti_sdma_write_4(sc, DMA4_OCP_SYSCONFIG, 0x0002); + + /* Set the timeout to 100ms*/ + timeout = (hz < 10) ? 1 : ((100 * hz) / 1000); + + /* Wait for DMA reset to complete */ + while ((ti_sdma_read_4(sc, DMA4_SYSSTATUS) & 0x1) == 0x0) { + + /* Sleep for a tick */ + pause("DMARESET", 1); + + if (timeout-- == 0) { + device_printf(sc->sc_dev, "sDMA reset operation timed out\n"); + return (EINVAL); + } + } + } + + /* + * Install interrupt handlers for the for possible interrupts. Any channel + * can trip one of the four IRQs + */ + rid = 0; + sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_ACTIVE | RF_SHAREABLE); + if (sc->sc_irq_res == NULL) + panic("Unable to setup the dma irq handler.\n"); + + err = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE, + NULL, ti_sdma_intr, NULL, &ihl); + if (err) + panic("%s: Cannot register IRQ", device_get_name(dev)); + + /* Store the DMA structure globally ... this driver should never be unloaded */ + ti_sdma_sc = sc; + + return (0); +} + +static device_method_t ti_sdma_methods[] = { + DEVMETHOD(device_probe, ti_sdma_probe), + DEVMETHOD(device_attach, ti_sdma_attach), + {0, 0}, +}; + +static driver_t ti_sdma_driver = { + "ti_sdma", + ti_sdma_methods, + sizeof(struct ti_sdma_softc), +}; +static devclass_t ti_sdma_devclass; + +DRIVER_MODULE(ti_sdma, simplebus, ti_sdma_driver, ti_sdma_devclass, 0, 0); +MODULE_DEPEND(ti_sdma, ti_prcm, 1, 1, 1); diff --git a/sys/arm/ti/ti_sdma.h b/sys/arm/ti/ti_sdma.h new file mode 100644 index 000000000000..2d7e626b2f9a --- /dev/null +++ b/sys/arm/ti/ti_sdma.h @@ -0,0 +1,111 @@ +/*- + * 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$ + */ + +/** + * sDMA device driver interface for the TI SoC + * + * See the ti_sdma.c file for implementation details. + * + * Reference: + * OMAP35x Applications Processor + * Technical Reference Manual + * (omap35xx_techref.pdf) + */ +#ifndef _TI_DMA_H_ +#define _TI_DMA_H_ + +#define TI_SDMA_ENDIAN_BIG 0x1 +#define TI_SDMA_ENDIAN_LITTLE 0x0 + +#define TI_SDMA_BURST_NONE 0x0 +#define TI_SDMA_BURST_16 0x1 +#define TI_SDMA_BURST_32 0x2 +#define TI_SDMA_BURST_64 0x3 + +#define TI_SDMA_DATA_8BITS_SCALAR 0x0 +#define TI_SDMA_DATA_16BITS_SCALAR 0x1 +#define TI_SDMA_DATA_32BITS_SCALAR 0x2 + +#define TI_SDMA_ADDR_CONSTANT 0x0 +#define TI_SDMA_ADDR_POST_INCREMENT 0x1 +#define TI_SDMA_ADDR_SINGLE_INDEX 0x2 +#define TI_SDMA_ADDR_DOUBLE_INDEX 0x3 + +/** + * Status flags for the DMA callback + * + */ +#define TI_SDMA_STATUS_DROP (1UL << 1) +#define TI_SDMA_STATUS_HALF (1UL << 2) +#define TI_SDMA_STATUS_FRAME (1UL << 3) +#define TI_SDMA_STATUS_LAST (1UL << 4) +#define TI_SDMA_STATUS_BLOCK (1UL << 5) +#define TI_SDMA_STATUS_SYNC (1UL << 6) +#define TI_SDMA_STATUS_PKT (1UL << 7) +#define TI_SDMA_STATUS_TRANS_ERR (1UL << 8) +#define TI_SDMA_STATUS_SECURE_ERR (1UL << 9) +#define TI_SDMA_STATUS_SUPERVISOR_ERR (1UL << 10) +#define TI_SDMA_STATUS_MISALIGNED_ADRS_ERR (1UL << 11) +#define TI_SDMA_STATUS_DRAIN_END (1UL << 12) + +#define TI_SDMA_SYNC_FRAME (1UL << 0) +#define TI_SDMA_SYNC_BLOCK (1UL << 1) +#define TI_SDMA_SYNC_PACKET (TI_SDMA_SYNC_FRAME | TI_SDMA_SYNC_BLOCK) +#define TI_SDMA_SYNC_TRIG_ON_SRC (1UL << 8) +#define TI_SDMA_SYNC_TRIG_ON_DST (1UL << 9) + +#define TI_SDMA_IRQ_FLAG_DROP (1UL << 1) +#define TI_SDMA_IRQ_FLAG_HALF_FRAME_COMPL (1UL << 2) +#define TI_SDMA_IRQ_FLAG_FRAME_COMPL (1UL << 3) +#define TI_SDMA_IRQ_FLAG_START_LAST_FRAME (1UL << 4) +#define TI_SDMA_IRQ_FLAG_BLOCK_COMPL (1UL << 5) +#define TI_SDMA_IRQ_FLAG_ENDOF_PKT (1UL << 7) +#define TI_SDMA_IRQ_FLAG_DRAIN (1UL << 12) + +int ti_sdma_activate_channel(unsigned int *ch, + void (*callback)(unsigned int ch, uint32_t status, void *data), void *data); +int ti_sdma_deactivate_channel(unsigned int ch); +int ti_sdma_start_xfer(unsigned int ch, unsigned int src_paddr, + unsigned long dst_paddr, unsigned int frmcnt, unsigned int elmcnt); +int ti_sdma_start_xfer_packet(unsigned int ch, unsigned int src_paddr, + unsigned long dst_paddr, unsigned int frmcnt, unsigned int elmcnt, + unsigned int pktsize); +int ti_sdma_stop_xfer(unsigned int ch); +int ti_sdma_enable_channel_irq(unsigned int ch, uint32_t flags); +int ti_sdma_disable_channel_irq(unsigned int ch); +int ti_sdma_get_channel_status(unsigned int ch, uint32_t *status); +int ti_sdma_set_xfer_endianess(unsigned int ch, unsigned int src, unsigned int dst); +int ti_sdma_set_xfer_burst(unsigned int ch, unsigned int src, unsigned int dst); +int ti_sdma_set_xfer_data_type(unsigned int ch, unsigned int type); +int ti_sdma_set_callback(unsigned int ch, + void (*callback)(unsigned int ch, uint32_t status, void *data), void *data); +int ti_sdma_sync_params(unsigned int ch, unsigned int trigger, unsigned int mode); +int ti_sdma_set_addr_mode(unsigned int ch, unsigned int src_mode, unsigned int dst_mode); + +#endif /* _TI_SDMA_H_ */ diff --git a/sys/arm/ti/ti_sdmareg.h b/sys/arm/ti/ti_sdmareg.h new file mode 100644 index 000000000000..c19af123837a --- /dev/null +++ b/sys/arm/ti/ti_sdmareg.h @@ -0,0 +1,133 @@ +/*- + * 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_SDMAREG_H__ +#define __TI_SDMAREG_H__ + +/** + * The number of DMA channels possible on the controller. + */ +#define NUM_DMA_CHANNELS 32 +#define NUM_DMA_IRQS 4 + +/** + * Register offsets + */ +#define DMA4_REVISION 0x0000 +#define DMA4_IRQSTATUS_L(j) (0x0008 + ((j) * 0x4)) +#define DMA4_IRQENABLE_L(j) (0x0018 + ((j) * 0x4)) +#define DMA4_SYSSTATUS 0x0028 +#define DMA4_OCP_SYSCONFIG 0x002C +#define DMA4_CAPS_0 0x0064 +#define DMA4_CAPS_2 0x006C +#define DMA4_CAPS_3 0x0070 +#define DMA4_CAPS_4 0x0074 +#define DMA4_GCR 0x0078 +#define DMA4_CCR(i) (0x0080 + ((i) * 0x60)) +#define DMA4_CLNK_CTRL(i) (0x0084 + ((i) * 0x60)) +#define DMA4_CICR(i) (0x0088 + ((i) * 0x60)) +#define DMA4_CSR(i) (0x008C + ((i) * 0x60)) +#define DMA4_CSDP(i) (0x0090 + ((i) * 0x60)) +#define DMA4_CEN(i) (0x0094 + ((i) * 0x60)) +#define DMA4_CFN(i) (0x0098 + ((i) * 0x60)) +#define DMA4_CSSA(i) (0x009C + ((i) * 0x60)) +#define DMA4_CDSA(i) (0x00A0 + ((i) * 0x60)) +#define DMA4_CSE(i) (0x00A4 + ((i) * 0x60)) +#define DMA4_CSF(i) (0x00A8 + ((i) * 0x60)) +#define DMA4_CDE(i) (0x00AC + ((i) * 0x60)) +#define DMA4_CDF(i) (0x00B0 + ((i) * 0x60)) +#define DMA4_CSAC(i) (0x00B4 + ((i) * 0x60)) +#define DMA4_CDAC(i) (0x00B8 + ((i) * 0x60)) +#define DMA4_CCEN(i) (0x00BC + ((i) * 0x60)) +#define DMA4_CCFN(i) (0x00C0 + ((i) * 0x60)) +#define DMA4_COLOR(i) (0x00C4 + ((i) * 0x60)) + +/* The following register are only defined on OMAP44xx (and newer?) */ +#define DMA4_CDP(i) (0x00D0 + ((i) * 0x60)) +#define DMA4_CNDP(i) (0x00D4 + ((i) * 0x60)) +#define DMA4_CCDN(i) (0x00D8 + ((i) * 0x60)) + +/** + * Various register field settings + */ +#define DMA4_CSDP_DATA_TYPE(x) (((x) & 0x3) << 0) +#define DMA4_CSDP_SRC_BURST_MODE(x) (((x) & 0x3) << 7) +#define DMA4_CSDP_DST_BURST_MODE(x) (((x) & 0x3) << 14) +#define DMA4_CSDP_SRC_ENDIANISM(x) (((x) & 0x1) << 21) +#define DMA4_CSDP_DST_ENDIANISM(x) (((x) & 0x1) << 19) +#define DMA4_CSDP_WRITE_MODE(x) (((x) & 0x3) << 16) +#define DMA4_CSDP_SRC_PACKED(x) (((x) & 0x1) << 6) +#define DMA4_CSDP_DST_PACKED(x) (((x) & 0x1) << 13) + +#define DMA4_CCR_DST_ADDRESS_MODE(x) (((x) & 0x3) << 14) +#define DMA4_CCR_SRC_ADDRESS_MODE(x) (((x) & 0x3) << 12) +#define DMA4_CCR_READ_PRIORITY(x) (((x) & 0x1) << 6) +#define DMA4_CCR_WRITE_PRIORITY(x) (((x) & 0x1) << 26) +#define DMA4_CCR_SYNC_TRIGGER(x) ((((x) & 0x60) << 14) \ + | ((x) & 0x1f)) +#define DMA4_CCR_FRAME_SYNC(x) (((x) & 0x1) << 5) +#define DMA4_CCR_BLOCK_SYNC(x) (((x) & 0x1) << 18) +#define DMA4_CCR_SEL_SRC_DST_SYNC(x) (((x) & 0x1) << 24) + +#define DMA4_CCR_PACKET_TRANS (DMA4_CCR_FRAME_SYNC(1) | \ + DMA4_CCR_BLOCK_SYNC(1) ) + +#define DMA4_CSR_DROP (1UL << 1) +#define DMA4_CSR_HALF (1UL << 2) +#define DMA4_CSR_FRAME (1UL << 3) +#define DMA4_CSR_LAST (1UL << 4) +#define DMA4_CSR_BLOCK (1UL << 5) +#define DMA4_CSR_SYNC (1UL << 6) +#define DMA4_CSR_PKT (1UL << 7) +#define DMA4_CSR_TRANS_ERR (1UL << 8) +#define DMA4_CSR_SECURE_ERR (1UL << 9) +#define DMA4_CSR_SUPERVISOR_ERR (1UL << 10) +#define DMA4_CSR_MISALIGNED_ADRS_ERR (1UL << 11) +#define DMA4_CSR_DRAIN_END (1UL << 12) +#define DMA4_CSR_CLEAR_MASK (0xffe) + +#define DMA4_CICR_DROP_IE (1UL << 1) +#define DMA4_CICR_HALF_IE (1UL << 2) +#define DMA4_CICR_FRAME_IE (1UL << 3) +#define DMA4_CICR_LAST_IE (1UL << 4) +#define DMA4_CICR_BLOCK_IE (1UL << 5) +#define DMA4_CICR_PKT_IE (1UL << 7) +#define DMA4_CICR_TRANS_ERR_IE (1UL << 8) +#define DMA4_CICR_SECURE_ERR_IE (1UL << 9) +#define DMA4_CICR_SUPERVISOR_ERR_IE (1UL << 10) +#define DMA4_CICR_MISALIGNED_ADRS_ERR_IE (1UL << 11) +#define DMA4_CICR_DRAIN_IE (1UL << 12) + +/** + * The following H/W revision values were found be experimentation, TI don't + * publish the revision numbers. The TRM says "TI internal Data". + */ +#define DMA4_OMAP3_REV 0x00000040 +#define DMA4_OMAP4_REV 0x00010900 + +#endif /* __TI_SDMAREG_H__ */ diff --git a/sys/arm/ti/ti_smc.S b/sys/arm/ti/ti_smc.S new file mode 100644 index 000000000000..c74f4e725626 --- /dev/null +++ b/sys/arm/ti/ti_smc.S @@ -0,0 +1,37 @@ +/*- + * Copyright (c) 2012 Olivier Houchard. 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 ``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 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 +#include +__FBSDID("$FreeBSD$"); + +/* Issue a smc #0 call */ +/* r0 and r1 contains the eventual arguments, r2 contains the function ID */ +ENTRY(ti_smc0) + stmfd sp!, {r4-r12, lr} + mov r12, r2 /* the rom expects the function ID in r12 */ + dsb + smc #0 + ldmfd sp!, {r4-r12, pc} + diff --git a/sys/arm/ti/ti_smc.h b/sys/arm/ti/ti_smc.h new file mode 100644 index 000000000000..7fae8ab4f79e --- /dev/null +++ b/sys/arm/ti/ti_smc.h @@ -0,0 +1,33 @@ +/*- + * Copyright (c) 2012 Olivier Houchard. 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 ``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 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_SMC_H_ +#define TI_SMC_H_ +uint32_t ti_smc0(uint32_t r0, uint32_t r1, uint32_t function_id); +#endif /* TI_SMC_H_ */ diff --git a/sys/arm/ti/tivar.h b/sys/arm/ti/tivar.h new file mode 100644 index 000000000000..248ad901fa91 --- /dev/null +++ b/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_ */ diff --git a/sys/arm/ti/twl/twl.c b/sys/arm/ti/twl/twl.c new file mode 100644 index 000000000000..8edaf2fea180 --- /dev/null +++ b/sys/arm/ti/twl/twl.c @@ -0,0 +1,464 @@ +/*- + * 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. + */ + +#include +__FBSDID("$FreeBSD$"); + +/* + * Texas Instruments TWL4030/TWL5030/TWL60x0/TPS659x0 Power Management and + * Audio CODEC devices. + * + * This code is based on the Linux TWL multifunctional device driver, which is + * copyright (C) 2005-2006 Texas Instruments, Inc. + * + * These chips are typically used as support ICs for the OMAP range of embedded + * ARM processes/SOC from Texas Instruments. They are typically used to control + * on board voltages, however some variants have other features like audio + * codecs, USB OTG transceivers, RTC, PWM, etc. + * + * This driver acts as a bus for more specific companion devices. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "arm/ti/twl/twl.h" + +/* TWL device IDs */ +#define TWL_DEVICE_UNKNOWN 0xffff +#define TWL_DEVICE_4030 0x4030 +#define TWL_DEVICE_6025 0x6025 +#define TWL_DEVICE_6030 0x6030 + +/* Each TWL device typically has more than one I2C address */ +#define TWL_MAX_SUBADDRS 4 + +/* The maxium number of bytes that can be written in one call */ +#define TWL_MAX_IIC_DATA_SIZE 63 + +/* The TWL devices typically use 4 I2C address for the different internal + * register sets, plus one SmartReflex I2C address. + */ +#define TWL_CHIP_ID0 0x48 +#define TWL_CHIP_ID1 0x49 +#define TWL_CHIP_ID2 0x4A +#define TWL_CHIP_ID3 0x4B + +#define TWL_SMARTREFLEX_CHIP_ID 0x12 + +#define TWL_INVALID_CHIP_ID 0xff + +struct twl_softc { + device_t sc_dev; + struct mtx sc_mtx; + unsigned int sc_type; + + uint8_t sc_subaddr_map[TWL_MAX_SUBADDRS]; + + struct intr_config_hook sc_scan_hook; + + device_t sc_vreg; + device_t sc_clks; +}; + +/** + * Macros for driver mutex locking + */ +#define TWL_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) +#define TWL_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) +#define TWL_LOCK_INIT(_sc) \ + mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->sc_dev), \ + "twl", MTX_DEF) +#define TWL_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); +#define TWL_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED); +#define TWL_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED); + + +/** + * twl_is_4030 - returns true if the device is TWL4030 + * twl_is_6025 - returns true if the device is TWL6025 + * twl_is_6030 - returns true if the device is TWL6030 + * @sc: device soft context + * + * Returns a non-zero value if the device matches. + * + * RETURNS: + * Returns a non-zero value if the device matches, otherwise zero. + */ +int +twl_is_4030(device_t dev) +{ + struct twl_softc *sc = device_get_softc(dev); + return (sc->sc_type == TWL_DEVICE_4030); +} + +int +twl_is_6025(device_t dev) +{ + struct twl_softc *sc = device_get_softc(dev); + return (sc->sc_type == TWL_DEVICE_6025); +} + +int +twl_is_6030(device_t dev) +{ + struct twl_softc *sc = device_get_softc(dev); + return (sc->sc_type == TWL_DEVICE_6030); +} + + +/** + * twl_read - read one or more registers from the TWL device + * @sc: device soft context + * @nsub: the sub-module to read from + * @reg: the register offset within the module to read + * @buf: buffer to store the bytes in + * @cnt: the number of bytes to read + * + * Reads one or more registers and stores the result in the suppled buffer. + * + * RETURNS: + * Zero on success or an error code on failure. + */ +int +twl_read(device_t dev, uint8_t nsub, uint8_t reg, uint8_t *buf, uint16_t cnt) +{ + struct twl_softc *sc; + struct iic_msg msg[2]; + uint8_t addr; + int rc; + + sc = device_get_softc(dev); + + TWL_LOCK(sc); + addr = sc->sc_subaddr_map[nsub]; + TWL_UNLOCK(sc); + + if (addr == TWL_INVALID_CHIP_ID) + return (EIO); + + + /* Set the address to read from */ + msg[0].slave = addr; + msg[0].flags = IIC_M_WR | IIC_M_NOSTOP; + msg[0].len = 1; + msg[0].buf = ® + /* Read the data back */ + msg[1].slave = addr; + msg[1].flags = IIC_M_RD; + msg[1].len = cnt; + msg[1].buf = buf; + + rc = iicbus_transfer(dev, msg, 2); + if (rc != 0) { + device_printf(dev, "iicbus read failed (adr:0x%02x, reg:0x%02x)\n", + addr, reg); + return (EIO); + } + + return (0); +} + +/** + * twl_write - writes one or more registers to the TWL device + * @sc: device soft context + * @nsub: the sub-module to read from + * @reg: the register offset within the module to read + * @buf: data to write + * @cnt: the number of bytes to write + * + * Writes one or more registers. + * + * RETURNS: + * Zero on success or a negative error code on failure. + */ +int +twl_write(device_t dev, uint8_t nsub, uint8_t reg, uint8_t *buf, uint16_t cnt) +{ + struct twl_softc *sc; + struct iic_msg msg; + uint8_t addr; + uint8_t tmp_buf[TWL_MAX_IIC_DATA_SIZE + 1]; + int rc; + + if (cnt > TWL_MAX_IIC_DATA_SIZE) + return (ENOMEM); + + /* Set the register address as the first byte */ + tmp_buf[0] = reg; + memcpy(&tmp_buf[1], buf, cnt); + + sc = device_get_softc(dev); + + TWL_LOCK(sc); + addr = sc->sc_subaddr_map[nsub]; + TWL_UNLOCK(sc); + + if (addr == TWL_INVALID_CHIP_ID) + return (EIO); + + + /* Setup the transfer and execute it */ + msg.slave = addr; + msg.flags = IIC_M_WR; + msg.len = cnt + 1; + msg.buf = tmp_buf; + + rc = iicbus_transfer(dev, &msg, 1); + if (rc != 0) { + device_printf(sc->sc_dev, "iicbus write failed (adr:0x%02x, reg:0x%02x)\n", + addr, reg); + return (EIO); + } + + return (0); +} + +/** + * twl_test_present - checks if a device with given address is present + * @sc: device soft context + * @addr: the address of the device to scan for + * + * Sends just the address byte and checks for an ACK. If no ACK then device + * is assumed to not be present. + * + * RETURNS: + * EIO if device is not present, otherwise 0 is returned. + */ +static int +twl_test_present(struct twl_softc *sc, uint8_t addr) +{ + struct iic_msg msg; + uint8_t tmp; + + /* Set the address to read from */ + msg.slave = addr; + msg.flags = IIC_M_RD; + msg.len = 1; + msg.buf = &tmp; + + if (iicbus_transfer(sc->sc_dev, &msg, 1) != 0) + return (EIO); + + return (0); +} + +/** + * twl_scan - scans the i2c bus for sub modules + * @dev: the twl device + * + * TWL devices don't just have one i2c slave address, rather they have up to + * 5 other addresses, each is for separate modules within the device. This + * function scans the bus for 4 possible sub-devices and stores the info + * internally. + * + */ +static void +twl_scan(void *dev) +{ + struct twl_softc *sc; + unsigned i; + uint8_t devs[TWL_MAX_SUBADDRS]; + uint8_t base = TWL_CHIP_ID0; + + sc = device_get_softc((device_t)dev); + + memset(devs, TWL_INVALID_CHIP_ID, TWL_MAX_SUBADDRS); + + /* Try each of the addresses (0x48, 0x49, 0x4a & 0x4b) to determine which + * sub modules we have. + */ + for (i = 0; i < TWL_MAX_SUBADDRS; i++) { + if (twl_test_present(sc, (base + i)) == 0) { + devs[i] = (base + i); + device_printf(sc->sc_dev, "Found (sub)device at 0x%02x\n", (base + i)); + } + } + + TWL_LOCK(sc); + memcpy(sc->sc_subaddr_map, devs, TWL_MAX_SUBADDRS); + TWL_UNLOCK(sc); + + /* Finished with the interrupt hook */ + config_intrhook_disestablish(&sc->sc_scan_hook); +} + +/** + * twl_probe - + * @dev: the twl device + * + * Scans the FDT for a match for the device, possible compatible device + * strings are; "ti,twl6030", "ti,twl6025", "ti,twl4030". + * + * The FDT compat string also determines the type of device (it is currently + * not possible to dynamically determine the device type). + * + */ +static int +twl_probe(device_t dev) +{ + phandle_t node; + const char *compat; + int len, l; + struct twl_softc *sc; + + if ((compat = ofw_bus_get_compat(dev)) == NULL) + return (ENXIO); + + if ((node = ofw_bus_get_node(dev)) == 0) + return (ENXIO); + + /* Get total 'compatible' prop len */ + if ((len = OF_getproplen(node, "compatible")) <= 0) + return (ENXIO); + + sc = device_get_softc(dev); + sc->sc_dev = dev; + sc->sc_type = TWL_DEVICE_UNKNOWN; + + while (len > 0) { + if (strncasecmp(compat, "ti,twl6030", 10) == 0) + sc->sc_type = TWL_DEVICE_6030; + else if (strncasecmp(compat, "ti,twl6025", 10) == 0) + sc->sc_type = TWL_DEVICE_6025; + else if (strncasecmp(compat, "ti,twl4030", 10) == 0) + sc->sc_type = TWL_DEVICE_4030; + + if (sc->sc_type != TWL_DEVICE_UNKNOWN) + break; + + /* Slide to the next sub-string. */ + l = strlen(compat) + 1; + compat += l; + len -= l; + } + + switch (sc->sc_type) { + case TWL_DEVICE_4030: + device_set_desc(dev, "TI TWL4030/TPS659x0 Companion IC"); + break; + case TWL_DEVICE_6025: + device_set_desc(dev, "TI TWL6025 Companion IC"); + break; + case TWL_DEVICE_6030: + device_set_desc(dev, "TI TWL6030 Companion IC"); + break; + case TWL_DEVICE_UNKNOWN: + default: + return (ENXIO); + } + + return (0); +} + +static int +twl_attach(device_t dev) +{ + struct twl_softc *sc; + + sc = device_get_softc(dev); + sc->sc_dev = dev; + + TWL_LOCK_INIT(sc); + + /* We have to wait until interrupts are enabled. I2C read and write + * only works if the interrupts are available. + */ + sc->sc_scan_hook.ich_func = twl_scan; + sc->sc_scan_hook.ich_arg = dev; + + if (config_intrhook_establish(&sc->sc_scan_hook) != 0) + return (ENOMEM); + + /* FIXME: should be in DTS file */ + if ((sc->sc_vreg = device_add_child(dev, "twl_vreg", -1)) == NULL) + device_printf(dev, "could not allocate twl_vreg instance\n"); + if ((sc->sc_clks = device_add_child(dev, "twl_clks", -1)) == NULL) + device_printf(dev, "could not allocate twl_clks instance\n"); + + return (bus_generic_attach(dev)); +} + +static int +twl_detach(device_t dev) +{ + struct twl_softc *sc; + + sc = device_get_softc(dev); + + if (sc->sc_vreg) + device_delete_child(dev, sc->sc_vreg); + if (sc->sc_clks) + device_delete_child(dev, sc->sc_clks); + + + TWL_LOCK_DESTROY(sc); + + return (0); +} + +static device_method_t twl_methods[] = { + DEVMETHOD(device_probe, twl_probe), + DEVMETHOD(device_attach, twl_attach), + DEVMETHOD(device_detach, twl_detach), + + {0, 0}, +}; + +static driver_t twl_driver = { + "twl", + twl_methods, + sizeof(struct twl_softc), +}; +static devclass_t twl_devclass; + +DRIVER_MODULE(twl, iicbus, twl_driver, twl_devclass, 0, 0); +MODULE_VERSION(twl, 1); diff --git a/sys/arm/ti/twl/twl.h b/sys/arm/ti/twl/twl.h new file mode 100644 index 000000000000..07f2cfd8c9c9 --- /dev/null +++ b/sys/arm/ti/twl/twl.h @@ -0,0 +1,39 @@ +/*- + * 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 _TWL_H_ +#define _TWL_H_ + +int twl_read(device_t dev, uint8_t nsub, uint8_t reg, uint8_t *buf, uint16_t cnt); +int twl_write(device_t dev, uint8_t nsub, uint8_t reg, uint8_t *buf, uint16_t cnt); + +int twl_is_4030(device_t dev); +int twl_is_6025(device_t dev); +int twl_is_6030(device_t dev); + +#endif /* _TWL_H_ */ diff --git a/sys/arm/ti/twl/twl_clks.c b/sys/arm/ti/twl/twl_clks.c new file mode 100644 index 000000000000..d78a73351422 --- /dev/null +++ b/sys/arm/ti/twl/twl_clks.c @@ -0,0 +1,675 @@ +/*- + * Copyright (c) 2012 + * 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. + */ + +#include +__FBSDID("$FreeBSD$"); + +/* + * Texas Instruments TWL4030/TWL5030/TWL60x0/TPS659x0 Power Management. + * + * This driver covers the external clocks, allows for enabling & + * disabling their output. + * + * + * + * FLATTENED DEVICE TREE (FDT) + * Startup override settings can be specified in the FDT, if they are they + * should be under the twl parent device and take the following form: + * + * external-clocks = "name1", "state1", + * "name2", "state2", + * etc; + * + * Each override should be a pair, the first entry is the name of the clock + * the second is the state to set, possible strings are either "on" or "off". + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "twl.h" +#include "twl_clks.h" + + +static int twl_clks_debug = 1; + + +/* + * Power Groups bits for the 4030 and 6030 devices + */ +#define TWL4030_P3_GRP 0x80 /* Peripherals, power group */ +#define TWL4030_P2_GRP 0x40 /* Modem power group */ +#define TWL4030_P1_GRP 0x20 /* Application power group (FreeBSD control) */ + +#define TWL6030_P3_GRP 0x04 /* Modem power group */ +#define TWL6030_P2_GRP 0x02 /* Connectivity power group */ +#define TWL6030_P1_GRP 0x01 /* Application power group (FreeBSD control) */ + +/* + * Register offsets within a clk regulator register set + */ +#define TWL_CLKS_GRP 0x00 /* Regulator GRP register */ +#define TWL_CLKS_STATE 0x02 /* TWL6030 only */ + + + +/** + * Support voltage regulators for the different IC's + */ +struct twl_clock { + const char *name; + uint8_t subdev; + uint8_t regbase; +}; + +static const struct twl_clock twl4030_clocks[] = { + { "32kclkout", 0, 0x8e }, + { NULL, 0, 0x00 } +}; + +static const struct twl_clock twl6030_clocks[] = { + { "clk32kg", 0, 0xbc }, + { "clk32kao", 0, 0xb9 }, + { "clk32kaudio", 0, 0xbf }, + { NULL, 0, 0x00 } +}; + +#define TWL_CLKS_MAX_NAMELEN 32 + +struct twl_clk_entry { + LIST_ENTRY(twl_clk_entry) link; + struct sysctl_oid *oid; + char name[TWL_CLKS_MAX_NAMELEN]; + uint8_t sub_dev; /* the sub-device number for the clock */ + uint8_t reg_off; /* register base address of the clock */ +}; + +struct twl_clks_softc { + device_t sc_dev; /* twl_clk device */ + device_t sc_pdev; /* parent device (twl) */ + struct sx sc_sx; /* internal locking */ + struct intr_config_hook sc_init_hook; + LIST_HEAD(twl_clk_list, twl_clk_entry) sc_clks_list; +}; + +/** + * Macros for driver shared locking + */ +#define TWL_CLKS_XLOCK(_sc) sx_xlock(&(_sc)->sc_sx) +#define TWL_CLKS_XUNLOCK(_sc) sx_xunlock(&(_sc)->sc_sx) +#define TWL_CLKS_SLOCK(_sc) sx_slock(&(_sc)->sc_sx) +#define TWL_CLKS_SUNLOCK(_sc) sx_sunlock(&(_sc)->sc_sx) +#define TWL_CLKS_LOCK_INIT(_sc) sx_init(&(_sc)->sc_sx, "twl_clks") +#define TWL_CLKS_LOCK_DESTROY(_sc) sx_destroy(&(_sc)->sc_sx); + +#define TWL_CLKS_ASSERT_LOCKED(_sc) sx_assert(&(_sc)->sc_sx, SA_LOCKED); + +#define TWL_CLKS_LOCK_UPGRADE(_sc) \ + do { \ + while (!sx_try_upgrade(&(_sc)->sc_sx)) \ + pause("twl_clks_ex", (hz / 100)); \ + } while(0) +#define TWL_CLKS_LOCK_DOWNGRADE(_sc) sx_downgrade(&(_sc)->sc_sx); + + + + +/** + * twl_clks_read_1 - read single register from the TWL device + * twl_clks_write_1 - writes a single register in the TWL device + * @sc: device context + * @clk: the clock device we're reading from / writing to + * @off: offset within the clock's register set + * @val: the value to write or a pointer to a variable to store the result + * + * RETURNS: + * Zero on success or an error code on failure. + */ +static inline int +twl_clks_read_1(struct twl_clks_softc *sc, struct twl_clk_entry *clk, + uint8_t off, uint8_t *val) +{ + return (twl_read(sc->sc_pdev, clk->sub_dev, clk->reg_off + off, val, 1)); +} + +static inline int +twl_clks_write_1(struct twl_clks_softc *sc, struct twl_clk_entry *clk, + uint8_t off, uint8_t val) +{ + return (twl_write(sc->sc_pdev, clk->sub_dev, clk->reg_off + off, &val, 1)); +} + + +/** + * twl_clks_is_enabled - determines if a clock is enabled + * @dev: TWL CLK device + * @name: the name of the clock + * @enabled: upon return will contain the 'enabled' state + * + * LOCKING: + * Internally the function takes and releases the TWL lock. + * + * RETURNS: + * Zero on success or a negative error code on failure. + */ +int +twl_clks_is_enabled(device_t dev, const char *name, int *enabled) +{ + struct twl_clks_softc *sc = device_get_softc(dev); + struct twl_clk_entry *clk; + int found = 0; + int err; + uint8_t grp, state; + + TWL_CLKS_SLOCK(sc); + + LIST_FOREACH(clk, &sc->sc_clks_list, link) { + if (strcmp(clk->name, name) == 0) { + found = 1; + break; + } + } + + if (!found) { + TWL_CLKS_SUNLOCK(sc); + return (EINVAL); + } + + + if (twl_is_4030(sc->sc_pdev)) { + + err = twl_clks_read_1(sc, clk, TWL_CLKS_GRP, &grp); + if (!err) + *enabled = (grp & TWL4030_P1_GRP); + + } else if (twl_is_6030(sc->sc_pdev) || twl_is_6025(sc->sc_pdev)) { + + TWL_CLKS_LOCK_UPGRADE(sc); + + /* Check the clock is in the application group */ + if (twl_is_6030(sc->sc_pdev)) { + err = twl_clks_read_1(sc, clk, TWL_CLKS_GRP, &grp); + if (err) { + TWL_CLKS_LOCK_DOWNGRADE(sc); + goto done; + } + + if (!(grp & TWL6030_P1_GRP)) { + TWL_CLKS_LOCK_DOWNGRADE(sc); + *enabled = 0; /* disabled */ + goto done; + } + } + + /* Read the application mode state and verify it's ON */ + err = twl_clks_read_1(sc, clk, TWL_CLKS_STATE, &state); + if (!err) + *enabled = ((state & 0x0C) == 0x04); + + TWL_CLKS_LOCK_DOWNGRADE(sc); + + } else { + err = EINVAL; + } + +done: + TWL_CLKS_SUNLOCK(sc); + return (err); +} + + +/** + * twl_clks_set_state - enables/disables a clock output + * @sc: device context + * @clk: the clock entry to enable/disable + * @enable: non-zero the clock is enabled, zero the clock is disabled + * + * LOCKING: + * The TWL CLK lock must be held before this function is called. + * + * RETURNS: + * Zero on success or an error code on failure. + */ +static int +twl_clks_set_state(struct twl_clks_softc *sc, struct twl_clk_entry *clk, + int enable) +{ + int xlocked; + int err; + uint8_t grp; + + TWL_CLKS_ASSERT_LOCKED(sc); + + /* Upgrade the lock to exclusive because about to perform read-mod-write */ + xlocked = sx_xlocked(&sc->sc_sx); + if (!xlocked) + TWL_CLKS_LOCK_UPGRADE(sc); + + err = twl_clks_read_1(sc, clk, TWL_CLKS_GRP, &grp); + if (err) + goto done; + + if (twl_is_4030(sc->sc_pdev)) { + + /* On the TWL4030 we just need to ensure the clock is in the right + * power domain, don't need to turn on explicitly like TWL6030. + */ + if (enable) + grp |= TWL4030_P1_GRP; + else + grp &= ~(TWL4030_P1_GRP | TWL4030_P2_GRP | TWL4030_P3_GRP); + + err = twl_clks_write_1(sc, clk, TWL_CLKS_GRP, grp); + + } else if (twl_is_6030(sc->sc_pdev) || twl_is_6025(sc->sc_pdev)) { + + /* Make sure the clock belongs to at least the APP power group */ + if (twl_is_6030(sc->sc_pdev) && !(grp & TWL6030_P1_GRP)) { + grp |= TWL6030_P1_GRP; + err = twl_clks_write_1(sc, clk, TWL_CLKS_GRP, grp); + if (err) + goto done; + } + + /* On TWL6030 we need to make sure we disable power for all groups */ + if (twl_is_6030(sc->sc_pdev)) + grp = TWL6030_P1_GRP | TWL6030_P2_GRP | TWL6030_P3_GRP; + else + grp = 0x00; + + /* Set the state of the clock */ + if (enable) + err = twl_clks_write_1(sc, clk, TWL_CLKS_STATE, (grp << 5) | 0x01); + else + err = twl_clks_write_1(sc, clk, TWL_CLKS_STATE, (grp << 5)); + + } else { + + err = EINVAL; + } + +done: + if (!xlocked) + TWL_CLKS_LOCK_DOWNGRADE(sc); + + if ((twl_clks_debug > 1) && !err) + device_printf(sc->sc_dev, "%s : %sabled\n", clk->name, + enable ? "en" : "dis"); + + return (err); +} + + +/** + * twl_clks_disable - disables a clock output + * @dev: TWL clk device +* @name: the name of the clock + * + * LOCKING: + * Internally the function takes and releases the TWL lock. + * + * RETURNS: +* Zero on success or an error code on failure. + */ +int +twl_clks_disable(device_t dev, const char *name) +{ + struct twl_clks_softc *sc = device_get_softc(dev); + struct twl_clk_entry *clk; + int err = EINVAL; + + TWL_CLKS_SLOCK(sc); + + LIST_FOREACH(clk, &sc->sc_clks_list, link) { + if (strcmp(clk->name, name) == 0) { + err = twl_clks_set_state(sc, clk, 0); + break; + } + } + + TWL_CLKS_SUNLOCK(sc); + return (err); +} + +/** + * twl_clks_enable - enables a clock output + * @dev: TWL clk device + * @name: the name of the clock + * + * LOCKING: + * Internally the function takes and releases the TWL CLKS lock. + * + * RETURNS: + * Zero on success or an error code on failure. + */ +int +twl_clks_enable(device_t dev, const char *name) +{ + struct twl_clks_softc *sc = device_get_softc(dev); + struct twl_clk_entry *clk; + int err = EINVAL; + + TWL_CLKS_SLOCK(sc); + + LIST_FOREACH(clk, &sc->sc_clks_list, link) { + if (strcmp(clk->name, name) == 0) { + err = twl_clks_set_state(sc, clk, 1); + break; + } + } + + TWL_CLKS_SUNLOCK(sc); + return (err); +} + +/** + * twl_clks_sysctl_clock - reads the state of the clock + * @SYSCTL_HANDLER_ARGS: arguments for the callback + * + * Returns the clock status; disabled is zero and enabled is non-zero. + * + * LOCKING: + * It's expected the TWL lock is held while this function is called. + * + * RETURNS: + * EIO if device is not present, otherwise 0 is returned. + */ +static int +twl_clks_sysctl_clock(SYSCTL_HANDLER_ARGS) +{ + struct twl_clks_softc *sc = (struct twl_clks_softc*)arg1; + int err; + int enabled = 0; + + if ((err = twl_clks_is_enabled(sc->sc_dev, oidp->oid_name, &enabled)) != 0) + return err; + + return sysctl_handle_int(oidp, &enabled, 0, req); +} + +/** + * twl_clks_add_clock - adds single clock sysctls for the device + * @sc: device soft context + * @name: the name of the regulator + * @nsub: the number of the subdevice + * @regbase: the base address of the clocks registers + * + * Adds a single clock to the device and also a sysctl interface for + * querying it's status. + * + * LOCKING: + * It's expected the exclusive lock is held while this function is called. + * + * RETURNS: + * Pointer to the new clock entry on success, otherwise NULL on failure. + */ +static struct twl_clk_entry* +twl_clks_add_clock(struct twl_clks_softc *sc, const char *name, + uint8_t nsub, uint8_t regbase) +{ + struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev); + struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev); + struct twl_clk_entry *new; + + TWL_CLKS_ASSERT_LOCKED(sc); + + new = malloc(sizeof(struct twl_clk_entry), M_DEVBUF, M_NOWAIT | M_ZERO); + if (new == NULL) + return (NULL); + + + strncpy(new->name, name, TWL_CLKS_MAX_NAMELEN); + new->name[TWL_CLKS_MAX_NAMELEN - 1] = '\0'; + + new->sub_dev = nsub; + new->reg_off = regbase; + + + + /* Add a sysctl entry for the clock */ + new->oid = SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, name, + CTLTYPE_INT | CTLFLAG_RD, sc, 0, + twl_clks_sysctl_clock, "I", "external clock"); + + /* Finally add the regulator to list of supported regulators */ + LIST_INSERT_HEAD(&sc->sc_clks_list, new, link); + + return (new); +} + +/** + * twl_clks_add_clocks - populates the internal list of clocks + * @sc: device soft context + * @chip: the name of the chip used in the hints + * @clks the list of clocks supported by the device + * + * Loops over the list of clocks and adds them to the device context. Also + * scans the FDT to determine if there are any clocks that should be + * enabled/disabled automatically. + * + * LOCKING: + * Internally takes the exclusive lock while adding the clocks to the + * device context. + * + * RETURNS: + * Always returns 0. + */ +static int +twl_clks_add_clocks(struct twl_clks_softc *sc, const struct twl_clock *clks) +{ + int err; + const struct twl_clock *walker; + struct twl_clk_entry *entry; + phandle_t child; + char rnames[256]; + char *name, *state; + int len = 0, prop_len; + int enable; + + + TWL_CLKS_XLOCK(sc); + + /* Add the regulators from the list */ + walker = &clks[0]; + while (walker->name != NULL) { + + /* Add the regulator to the list */ + entry = twl_clks_add_clock(sc, walker->name, walker->subdev, + walker->regbase); + if (entry == NULL) + continue; + + walker++; + } + + /* Check for any FDT settings that need to be applied */ + child = ofw_bus_get_node(sc->sc_pdev); + if (child) { + + prop_len = OF_getprop(child, "external-clocks", rnames, sizeof(rnames)); + while (len < prop_len) { + name = rnames + len; + len += strlen(name) + 1; + if ((len >= prop_len) || (name[0] == '\0')) + break; + + state = rnames + len; + len += strlen(state) + 1; + if (state[0] == '\0') + break; + + enable = !strncmp(state, "on", 2); + + LIST_FOREACH(entry, &sc->sc_clks_list, link) { + if (strcmp(entry->name, name) == 0) { + twl_clks_set_state(sc, entry, enable); + break; + } + } + } + } + + TWL_CLKS_XUNLOCK(sc); + + + if (twl_clks_debug) { + LIST_FOREACH(entry, &sc->sc_clks_list, link) { + err = twl_clks_is_enabled(sc->sc_dev, entry->name, &enable); + if (!err) + device_printf(sc->sc_dev, "%s : %s\n", entry->name, + enable ? "on" : "off"); + } + } + + return (0); +} + +/** + * twl_clks_init - initialises the list of clocks + * @dev: the twl_clks device + * + * This function is called as an intrhook once interrupts have been enabled, + * this is done so that the driver has the option to enable/disable a clock + * based on settings providied in the FDT. + * + * LOCKING: + * May takes the exclusive lock in the function. + */ +static void +twl_clks_init(void *dev) +{ + struct twl_clks_softc *sc; + + sc = device_get_softc((device_t)dev); + + if (twl_is_4030(sc->sc_pdev)) + twl_clks_add_clocks(sc, twl4030_clocks); + else if (twl_is_6030(sc->sc_pdev) || twl_is_6025(sc->sc_pdev)) + twl_clks_add_clocks(sc, twl6030_clocks); + + config_intrhook_disestablish(&sc->sc_init_hook); +} + +static int +twl_clks_probe(device_t dev) +{ + if (twl_is_4030(device_get_parent(dev))) + device_set_desc(dev, "TI TWL4030 PMIC External Clocks"); + else if (twl_is_6025(device_get_parent(dev)) || + twl_is_6030(device_get_parent(dev))) + device_set_desc(dev, "TI TWL6025/TWL6030 PMIC External Clocks"); + else + return (ENXIO); + + return (0); +} + +static int +twl_clks_attach(device_t dev) +{ + struct twl_clks_softc *sc; + + sc = device_get_softc(dev); + sc->sc_dev = dev; + sc->sc_pdev = device_get_parent(dev); + + TWL_CLKS_LOCK_INIT(sc); + + LIST_INIT(&sc->sc_clks_list); + + + sc->sc_init_hook.ich_func = twl_clks_init; + sc->sc_init_hook.ich_arg = dev; + + if (config_intrhook_establish(&sc->sc_init_hook) != 0) + return (ENOMEM); + + return (0); +} + +static int +twl_clks_detach(device_t dev) +{ + struct twl_clks_softc *sc; + struct twl_clk_entry *clk; + struct twl_clk_entry *tmp; + + sc = device_get_softc(dev); + + TWL_CLKS_XLOCK(sc); + + LIST_FOREACH_SAFE(clk, &sc->sc_clks_list, link, tmp) { + LIST_REMOVE(clk, link); + sysctl_remove_oid(clk->oid, 1, 0); + free(clk, M_DEVBUF); + } + + TWL_CLKS_XUNLOCK(sc); + + TWL_CLKS_LOCK_DESTROY(sc); + + return (0); +} + +static device_method_t twl_clks_methods[] = { + DEVMETHOD(device_probe, twl_clks_probe), + DEVMETHOD(device_attach, twl_clks_attach), + DEVMETHOD(device_detach, twl_clks_detach), + + {0, 0}, +}; + +static driver_t twl_clks_driver = { + "twl_clks", + twl_clks_methods, + sizeof(struct twl_clks_softc), +}; + +static devclass_t twl_clks_devclass; + +DRIVER_MODULE(twl_clks, twl, twl_clks_driver, twl_clks_devclass, 0, 0); +MODULE_VERSION(twl_clks, 1); diff --git a/sys/arm/ti/twl/twl_clks.h b/sys/arm/ti/twl/twl_clks.h new file mode 100644 index 000000000000..c5d89f38531d --- /dev/null +++ b/sys/arm/ti/twl/twl_clks.h @@ -0,0 +1,38 @@ +/*- + * Copyright (c) 2012 + * 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 _TWL_CLKS_H_ +#define _TWL_CLKS_H_ + + +int twl_clks_enable(device_t dev, const char *name); +int twl_clks_disable(device_t dev, const char *name); +int twl_clks_is_enabled(device_t dev, const char *name, int *enabled); + + +#endif /* _TWL_CLKS_H_ */ diff --git a/sys/arm/ti/twl/twl_vreg.c b/sys/arm/ti/twl/twl_vreg.c new file mode 100644 index 000000000000..26c2e2690f2d --- /dev/null +++ b/sys/arm/ti/twl/twl_vreg.c @@ -0,0 +1,1053 @@ +/*- + * 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. + */ + +#include +__FBSDID("$FreeBSD$"); + +/* + * Texas Instruments TWL4030/TWL5030/TWL60x0/TPS659x0 Power Management. + * + * This driver covers the voltages regulators (LDO), allows for enabling & + * disabling the voltage output and adjusting the voltage level. + * + * Voltage regulators can belong to different power groups, in this driver we + * put the regulators under our control in the "Application power group". + * + * + * FLATTENED DEVICE TREE (FDT) + * Startup override settings can be specified in the FDT, if they are they + * should be under the twl parent device and take the following form: + * + * voltage-regulators = "name1", "millivolts1", + * "name2", "millivolts2"; + * + * Each override should be a pair, the first entry is the name of the regulator + * the second is the voltage (in millivolts) to set for the given regulator. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "twl.h" +#include "twl_vreg.h" + +static int twl_vreg_debug = 1; + + +/* + * Power Groups bits for the 4030 and 6030 devices + */ +#define TWL4030_P3_GRP 0x80 /* Peripherals, power group */ +#define TWL4030_P2_GRP 0x40 /* Modem power group */ +#define TWL4030_P1_GRP 0x20 /* Application power group (FreeBSD control) */ + +#define TWL6030_P3_GRP 0x04 /* Modem power group */ +#define TWL6030_P2_GRP 0x02 /* Connectivity power group */ +#define TWL6030_P1_GRP 0x01 /* Application power group (FreeBSD control) */ + +/* + * Register offsets within a LDO regulator register set + */ +#define TWL_VREG_GRP 0x00 /* Regulator GRP register */ +#define TWL_VREG_STATE 0x02 +#define TWL_VREG_VSEL 0x03 /* Voltage select register */ + +#define UNDF 0xFFFF + +static const uint16_t twl6030_voltages[] = { + 0000, 1000, 1100, 1200, 1300, 1400, 1500, 1600, + 1700, 1800, 1900, 2000, 2100, 2200, 2300, 2400, + 2500, 2600, 2700, 2800, 2900, 3000, 3100, 3200, + 3300, UNDF, UNDF, UNDF, UNDF, UNDF, UNDF, 2750 +}; + +static const uint16_t twl4030_vaux1_voltages[] = { + 1500, 1800, 2500, 2800, 3000, 3000, 3000, 3000 +}; +static const uint16_t twl4030_vaux2_voltages[] = { + 1700, 1700, 1900, 1300, 1500, 1800, 2000, 2500, + 2100, 2800, 2200, 2300, 2400, 2400, 2400, 2400 +}; +static const uint16_t twl4030_vaux3_voltages[] = { + 1500, 1800, 2500, 2800, 3000, 3000, 3000, 3000 +}; +static const uint16_t twl4030_vaux4_voltages[] = { + 700, 1000, 1200, 1300, 1500, 1800, 1850, 2500, + 2600, 2800, 2850, 3000, 3150, 3150, 3150, 3150 +}; +static const uint16_t twl4030_vmmc1_voltages[] = { + 1850, 2850, 3000, 3150 +}; +static const uint16_t twl4030_vmmc2_voltages[] = { + 1000, 1000, 1200, 1300, 1500, 1800, 1850, 2500, + 2600, 2800, 2850, 3000, 3150, 3150, 3150, 3150 +}; +static const uint16_t twl4030_vpll1_voltages[] = { + 1000, 1200, 1300, 1800, 2800, 3000, 3000, 3000 +}; +static const uint16_t twl4030_vpll2_voltages[] = { + 700, 1000, 1200, 1300, 1500, 1800, 1850, 2500, + 2600, 2800, 2850, 3000, 3150, 3150, 3150, 3150 +}; +static const uint16_t twl4030_vsim_voltages[] = { + 1000, 1200, 1300, 1800, 2800, 3000, 3000, 3000 +}; +static const uint16_t twl4030_vdac_voltages[] = { + 1200, 1300, 1800, 1800 +}; +static const uint16_t twl4030_vdd1_voltages[] = { + 800, 1450 +}; +static const uint16_t twl4030_vdd2_voltages[] = { + 800, 1450, 1500 +}; +static const uint16_t twl4030_vio_voltages[] = { + 1800, 1850 +}; +static const uint16_t twl4030_vintana2_voltages[] = { + 2500, 2750 +}; + +/** + * Support voltage regulators for the different IC's + */ +struct twl_regulator { + const char *name; + uint8_t subdev; + uint8_t regbase; + + uint16_t fixedvoltage; + + const uint16_t *voltages; + uint32_t num_voltages; +}; + +#define TWL_REGULATOR_ADJUSTABLE(name, subdev, reg, voltages) \ + { name, subdev, reg, 0, voltages, (sizeof(voltages)/sizeof(voltages[0])) } +#define TWL_REGULATOR_FIXED(name, subdev, reg, voltage) \ + { name, subdev, reg, voltage, NULL, 0 } + +static const struct twl_regulator twl4030_regulators[] = { + TWL_REGULATOR_ADJUSTABLE("vaux1", 0, 0x17, twl4030_vaux1_voltages), + TWL_REGULATOR_ADJUSTABLE("vaux2", 0, 0x1B, twl4030_vaux2_voltages), + TWL_REGULATOR_ADJUSTABLE("vaux3", 0, 0x1F, twl4030_vaux3_voltages), + TWL_REGULATOR_ADJUSTABLE("vaux4", 0, 0x23, twl4030_vaux4_voltages), + TWL_REGULATOR_ADJUSTABLE("vmmc1", 0, 0x27, twl4030_vmmc1_voltages), + TWL_REGULATOR_ADJUSTABLE("vmmc2", 0, 0x2B, twl4030_vmmc2_voltages), + TWL_REGULATOR_ADJUSTABLE("vpll1", 0, 0x2F, twl4030_vpll1_voltages), + TWL_REGULATOR_ADJUSTABLE("vpll2", 0, 0x33, twl4030_vpll2_voltages), + TWL_REGULATOR_ADJUSTABLE("vsim", 0, 0x37, twl4030_vsim_voltages), + TWL_REGULATOR_ADJUSTABLE("vdac", 0, 0x3B, twl4030_vdac_voltages), + TWL_REGULATOR_ADJUSTABLE("vintana2", 0, 0x43, twl4030_vintana2_voltages), + TWL_REGULATOR_FIXED("vintana1", 0, 0x3F, 1500), + TWL_REGULATOR_FIXED("vintdig", 0, 0x47, 1500), + TWL_REGULATOR_FIXED("vusb1v5", 0, 0x71, 1500), + TWL_REGULATOR_FIXED("vusb1v8", 0, 0x74, 1800), + TWL_REGULATOR_FIXED("vusb3v1", 0, 0x77, 3100), + { NULL, 0, 0x00, 0, NULL, 0 } +}; + +static const struct twl_regulator twl6030_regulators[] = { + TWL_REGULATOR_ADJUSTABLE("vaux1", 0, 0x84, twl6030_voltages), + TWL_REGULATOR_ADJUSTABLE("vaux2", 0, 0x89, twl6030_voltages), + TWL_REGULATOR_ADJUSTABLE("vaux3", 0, 0x8C, twl6030_voltages), + TWL_REGULATOR_ADJUSTABLE("vmmc", 0, 0x98, twl6030_voltages), + TWL_REGULATOR_ADJUSTABLE("vpp", 0, 0x9C, twl6030_voltages), + TWL_REGULATOR_ADJUSTABLE("vusim", 0, 0xA4, twl6030_voltages), + TWL_REGULATOR_FIXED("vmem", 0, 0x64, 1800), + TWL_REGULATOR_FIXED("vusb", 0, 0xA0, 3300), + TWL_REGULATOR_FIXED("v1v8", 0, 0x46, 1800), + TWL_REGULATOR_FIXED("v2v1", 0, 0x4C, 2100), + TWL_REGULATOR_FIXED("v1v29", 0, 0x40, 1290), + TWL_REGULATOR_FIXED("vcxio", 0, 0x90, 1800), + TWL_REGULATOR_FIXED("vdac", 0, 0x94, 1800), + TWL_REGULATOR_FIXED("vana", 0, 0x80, 2100), + { NULL, 0, 0x00, 0, NULL, 0 } +}; + +#define TWL_VREG_MAX_NAMELEN 32 + +struct twl_regulator_entry { + LIST_ENTRY(twl_regulator_entry) entries; + char name[TWL_VREG_MAX_NAMELEN]; + struct sysctl_oid *oid; + uint8_t sub_dev; /* TWL sub-device group */ + uint8_t reg_off; /* base register offset for the LDO */ + uint16_t fixed_voltage; /* the (milli)voltage if LDO is fixed */ + const uint16_t *supp_voltages; /* pointer to an array of possible voltages */ + uint32_t num_supp_voltages; /* the number of supplied voltages */ +}; + +struct twl_vreg_softc { + device_t sc_dev; + device_t sc_pdev; + struct sx sc_sx; + + struct intr_config_hook sc_init_hook; + LIST_HEAD(twl_regulator_list, twl_regulator_entry) sc_vreg_list; +}; + + +#define TWL_VREG_XLOCK(_sc) sx_xlock(&(_sc)->sc_sx) +#define TWL_VREG_XUNLOCK(_sc) sx_xunlock(&(_sc)->sc_sx) +#define TWL_VREG_SLOCK(_sc) sx_slock(&(_sc)->sc_sx) +#define TWL_VREG_SUNLOCK(_sc) sx_sunlock(&(_sc)->sc_sx) +#define TWL_VREG_LOCK_INIT(_sc) sx_init(&(_sc)->sc_sx, "twl_vreg") +#define TWL_VREG_LOCK_DESTROY(_sc) sx_destroy(&(_sc)->sc_sx); + +#define TWL_VREG_ASSERT_LOCKED(_sc) sx_assert(&(_sc)->sc_sx, SA_LOCKED); + +#define TWL_VREG_LOCK_UPGRADE(_sc) \ + do { \ + while (!sx_try_upgrade(&(_sc)->sc_sx)) \ + pause("twl_vreg_ex", (hz / 100)); \ + } while(0) +#define TWL_VREG_LOCK_DOWNGRADE(_sc) sx_downgrade(&(_sc)->sc_sx); + + + + +/** + * twl_vreg_read_1 - read single register from the TWL device + * twl_vreg_write_1 - write a single register in the TWL device + * @sc: device context + * @clk: the clock device we're reading from / writing to + * @off: offset within the clock's register set + * @val: the value to write or a pointer to a variable to store the result + * + * RETURNS: + * Zero on success or an error code on failure. + */ +static inline int +twl_vreg_read_1(struct twl_vreg_softc *sc, struct twl_regulator_entry *regulator, + uint8_t off, uint8_t *val) +{ + return (twl_read(sc->sc_pdev, regulator->sub_dev, + regulator->reg_off + off, val, 1)); +} + +static inline int +twl_vreg_write_1(struct twl_vreg_softc *sc, struct twl_regulator_entry *regulator, + uint8_t off, uint8_t val) +{ + return (twl_write(sc->sc_pdev, regulator->sub_dev, + regulator->reg_off + off, &val, 1)); +} + +/** + * twl_millivolt_to_vsel - gets the vsel bit value to write into the register + * for a desired voltage and regulator + * @sc: the device soft context + * @regulator: pointer to the regulator device + * @millivolts: the millivolts to find the bit value for + * @vsel: upon return will contain the corresponding register value + * + * Accepts a (milli)voltage value and tries to find the closest match to the + * actual supported voltages for the given regulator. If a match is found + * within 100mv of the target, @vsel is written with the match and 0 is + * returned. If no voltage match is found the function returns an non-zero + * value. + * + * RETURNS: + * Zero on success or an error code on failure. + */ +static int +twl_vreg_millivolt_to_vsel(struct twl_vreg_softc *sc, + struct twl_regulator_entry *regulator, int millivolts, uint8_t *vsel) +{ + int delta, smallest_delta; + unsigned i, closest_idx; + + TWL_VREG_ASSERT_LOCKED(sc); + + if (regulator->supp_voltages == NULL) + return (EINVAL); + + /* Loop over the support voltages and try and find the closest match */ + closest_idx = 0; + smallest_delta = 0x7fffffff; + for (i = 0; i < regulator->num_supp_voltages; i++) { + + /* Ignore undefined values */ + if (regulator->supp_voltages[i] == UNDF) + continue; + + /* Calculate the difference */ + delta = millivolts - (int)regulator->supp_voltages[i]; + if (abs(delta) < smallest_delta) { + smallest_delta = abs(delta); + closest_idx = i; + } + } + + /* Check we got a voltage that was within 100mv of the actual target, this + * is just a value I picked out of thin air. + */ + if ((smallest_delta > 100) && (closest_idx < 0x100)) + return (EINVAL); + + *vsel = closest_idx; + return (0); +} + +/** + * twl_vreg_is_regulator_enabled - returns the enabled status of the regulator + * @sc: the device soft context + * @regulator: pointer to the regulator device + * @enabled: stores the enabled status, zero disabled, non-zero enabled + * + * LOCKING: + * On entry expects the TWL VREG lock to be held. Will upgrade the lock to + * exclusive if not already but, if so, it will be downgraded again before + * returning. + * + * RETURNS: + * Zero on success or an error code on failure. + */ +static int +twl_vreg_is_regulator_enabled(struct twl_vreg_softc *sc, + struct twl_regulator_entry *regulator, int *enabled) +{ + int err; + uint8_t grp; + uint8_t state; + int xlocked; + + if (enabled == NULL) + return (EINVAL); + + TWL_VREG_ASSERT_LOCKED(sc); + + xlocked = sx_xlocked(&sc->sc_sx); + if (!xlocked) + TWL_VREG_LOCK_UPGRADE(sc); + + /* The status reading is different for the different devices */ + if (twl_is_4030(sc->sc_pdev)) { + + err = twl_vreg_read_1(sc, regulator, TWL_VREG_GRP, &state); + if (err) + goto done; + + *enabled = (state & TWL4030_P1_GRP); + + } else if (twl_is_6030(sc->sc_pdev) || twl_is_6025(sc->sc_pdev)) { + + /* Check the regulator is in the application group */ + if (twl_is_6030(sc->sc_pdev)) { + err = twl_vreg_read_1(sc, regulator, TWL_VREG_GRP, &grp); + if (err) + goto done; + + if (!(grp & TWL6030_P1_GRP)) { + *enabled = 0; /* disabled */ + goto done; + } + } + + /* Read the application mode state and verify it's ON */ + err = twl_vreg_read_1(sc, regulator, TWL_VREG_STATE, &state); + if (err) + goto done; + + *enabled = ((state & 0x0C) == 0x04); + + } else { + err = EINVAL; + } + +done: + if (!xlocked) + TWL_VREG_LOCK_DOWNGRADE(sc); + + return (err); +} + +/** + * twl_vreg_disable_regulator - disables a voltage regulator + * @sc: the device soft context + * @regulator: pointer to the regulator device + * + * Disables the regulator which will stop the output drivers. + * + * LOCKING: + * On entry expects the TWL VREG lock to be held. Will upgrade the lock to + * exclusive if not already but, if so, it will be downgraded again before + * returning. + * + * RETURNS: + * Zero on success or a positive error code on failure. + */ +static int +twl_vreg_disable_regulator(struct twl_vreg_softc *sc, + struct twl_regulator_entry *regulator) +{ + int err = 0; + uint8_t grp; + int xlocked; + + TWL_VREG_ASSERT_LOCKED(sc); + + xlocked = sx_xlocked(&sc->sc_sx); + if (!xlocked) + TWL_VREG_LOCK_UPGRADE(sc); + + if (twl_is_4030(sc->sc_pdev)) { + + /* Read the regulator CFG_GRP register */ + err = twl_vreg_read_1(sc, regulator, TWL_VREG_GRP, &grp); + if (err) + goto done; + + /* On the TWL4030 we just need to remove the regulator from all the + * power groups. + */ + grp &= ~(TWL4030_P1_GRP | TWL4030_P2_GRP | TWL4030_P3_GRP); + err = twl_vreg_write_1(sc, regulator, TWL_VREG_GRP, grp); + + } else if (twl_is_6030(sc->sc_pdev) || twl_is_6025(sc->sc_pdev)) { + + /* On TWL6030 we need to make sure we disable power for all groups */ + if (twl_is_6030(sc->sc_pdev)) + grp = TWL6030_P1_GRP | TWL6030_P2_GRP | TWL6030_P3_GRP; + else + grp = 0x00; + + /* Write the resource state to "OFF" */ + err = twl_vreg_write_1(sc, regulator, TWL_VREG_STATE, (grp << 5)); + } + +done: + if (!xlocked) + TWL_VREG_LOCK_DOWNGRADE(sc); + + return (err); +} + +/** + * twl_vreg_enable_regulator - enables the voltage regulator + * @sc: the device soft context + * @regulator: pointer to the regulator device + * + * Enables the regulator which will enable the voltage out at the currently + * set voltage. Set the voltage before calling this function to avoid + * driving the voltage too high/low by mistake. + * + * LOCKING: + * On entry expects the TWL VREG lock to be held. Will upgrade the lock to + * exclusive if not already but, if so, it will be downgraded again before + * returning. + * + * RETURNS: + * Zero on success or a positive error code on failure. + */ +static int +twl_vreg_enable_regulator(struct twl_vreg_softc *sc, + struct twl_regulator_entry *regulator) +{ + int err; + uint8_t grp; + int xlocked; + + TWL_VREG_ASSERT_LOCKED(sc); + + xlocked = sx_xlocked(&sc->sc_sx); + if (!xlocked) + TWL_VREG_LOCK_UPGRADE(sc); + + + err = twl_vreg_read_1(sc, regulator, TWL_VREG_GRP, &grp); + if (err) + goto done; + + /* Enable the regulator by ensuring it's in the application power group + * and is in the "on" state. + */ + if (twl_is_4030(sc->sc_pdev)) { + + /* On the TWL4030 we just need to ensure the regulator is in the right + * power domain, don't need to turn on explicitly like TWL6030. + */ + grp |= TWL4030_P1_GRP; + err = twl_vreg_write_1(sc, regulator, TWL_VREG_GRP, grp); + + } else if (twl_is_6030(sc->sc_pdev) || twl_is_6025(sc->sc_pdev)) { + + if (twl_is_6030(sc->sc_pdev) && !(grp & TWL6030_P1_GRP)) { + grp |= TWL6030_P1_GRP; + err = twl_vreg_write_1(sc, regulator, TWL_VREG_GRP, grp); + if (err) + goto done; + } + + /* Write the resource state to "ON" */ + err = twl_vreg_write_1(sc, regulator, TWL_VREG_STATE, (grp << 5) | 0x01); + } + +done: + if (!xlocked) + TWL_VREG_LOCK_DOWNGRADE(sc); + + return (err); +} + +/** + * twl_vreg_write_regulator_voltage - sets the voltage level on a regulator + * @sc: the device soft context + * @regulator: pointer to the regulator structure + * @millivolts: the voltage to set + * + * Sets the voltage output on a given regulator, if the regulator is not + * enabled, it will be enabled. + * + * LOCKING: + * On entry expects the TWL VREG lock to be held, may upgrade the lock to + * exclusive but if so it will be downgraded once again before returning. + * + * RETURNS: + * Zero on success or an error code on failure. + */ +static int +twl_vreg_write_regulator_voltage(struct twl_vreg_softc *sc, + struct twl_regulator_entry *regulator, int millivolts) +{ + int err; + uint8_t vsel; + int xlocked; + + TWL_VREG_ASSERT_LOCKED(sc); + + /* If millivolts is zero then we simply disable the output */ + if (millivolts == 0) + return (twl_vreg_disable_regulator(sc, regulator)); + + /* If the regulator has a fixed voltage then check the setting matches + * and simply enable. + */ + if (regulator->supp_voltages == NULL || regulator->num_supp_voltages == 0) { + if (millivolts != regulator->fixed_voltage) + return (EINVAL); + + return (twl_vreg_enable_regulator(sc, regulator)); + } + + /* Get the VSEL value for the given voltage */ + err = twl_vreg_millivolt_to_vsel(sc, regulator, millivolts, &vsel); + if (err) + return (err); + + + /* Need to upgrade because writing the voltage and enabling should be atomic */ + xlocked = sx_xlocked(&sc->sc_sx); + if (!xlocked) + TWL_VREG_LOCK_UPGRADE(sc); + + + /* Set voltage and enable (atomically) */ + err = twl_vreg_write_1(sc, regulator, TWL_VREG_VSEL, (vsel & 0x1f)); + if (!err) { + err = twl_vreg_enable_regulator(sc, regulator); + } + + if (!xlocked) + TWL_VREG_LOCK_DOWNGRADE(sc); + + if ((twl_vreg_debug > 1) && !err) + device_printf(sc->sc_dev, "%s : setting voltage to %dmV (vsel: 0x%x)\n", + regulator->name, millivolts, vsel); + + return (err); +} + +/** + * twl_vreg_read_regulator_voltage - reads the voltage on a given regulator + * @sc: the device soft context + * @regulator: pointer to the regulator structure + * @millivolts: upon return will contain the voltage on the regulator + * + * LOCKING: + * On entry expects the TWL VREG lock to be held. It will upgrade the lock to + * exclusive if not already, but if so, it will be downgraded again before + * returning. + * + * RETURNS: + * Zero on success, or otherwise an error code. + */ +static int +twl_vreg_read_regulator_voltage(struct twl_vreg_softc *sc, + struct twl_regulator_entry *regulator, int *millivolts) +{ + int err; + int en = 0; + int xlocked; + uint8_t vsel; + + TWL_VREG_ASSERT_LOCKED(sc); + + /* Need to upgrade the lock because checking enabled state and voltage + * should be atomic. + */ + xlocked = sx_xlocked(&sc->sc_sx); + if (!xlocked) + TWL_VREG_LOCK_UPGRADE(sc); + + + /* Check if the regulator is currently enabled */ + err = twl_vreg_is_regulator_enabled(sc, regulator, &en); + if (err) + goto done; + + *millivolts = 0; + if (!en) + goto done; + + + /* Not all voltages are adjustable */ + if (regulator->supp_voltages == NULL || !regulator->num_supp_voltages) { + *millivolts = regulator->fixed_voltage; + goto done; + } + + /* For variable voltages read the voltage register */ + err = twl_vreg_read_1(sc, regulator, TWL_VREG_VSEL, &vsel); + if (err) + goto done; + + vsel &= (regulator->num_supp_voltages - 1); + if (regulator->supp_voltages[vsel] == UNDF) { + err = EINVAL; + goto done; + } + + *millivolts = regulator->supp_voltages[vsel]; + +done: + if (!xlocked) + TWL_VREG_LOCK_DOWNGRADE(sc); + + if ((twl_vreg_debug > 1) && !err) + device_printf(sc->sc_dev, "%s : reading voltage is %dmV (vsel: 0x%x)\n", + regulator->name, *millivolts, vsel); + + return (err); +} + +/** + * twl_vreg_get_voltage - public interface to read the voltage on a regulator + * @dev: TWL VREG device + * @name: the name of the regulator to read the voltage of + * @millivolts: pointer to an integer that upon return will contain the mV + * + * If the regulator is disabled the function will set the @millivolts to zero. + * + * LOCKING: + * Internally the function takes and releases the TWL VREG lock. + * + * RETURNS: + * Zero on success or a negative error code on failure. + */ +int +twl_vreg_get_voltage(device_t dev, const char *name, int *millivolts) +{ + struct twl_vreg_softc *sc; + struct twl_regulator_entry *regulator; + int err = EINVAL; + + if (millivolts == NULL) + return (EINVAL); + + sc = device_get_softc(dev); + + TWL_VREG_SLOCK(sc); + + LIST_FOREACH(regulator, &sc->sc_vreg_list, entries) { + if (strcmp(regulator->name, name) == 0) { + err = twl_vreg_read_regulator_voltage(sc, regulator, millivolts); + break; + } + } + + TWL_VREG_SUNLOCK(sc); + + return (err); +} + +/** + * twl_vreg_set_voltage - public interface to write the voltage on a regulator + * @dev: TWL VREG device + * @name: the name of the regulator to read the voltage of + * @millivolts: the voltage to set in millivolts + * + * Sets the output voltage on a given regulator. If the regulator is a fixed + * voltage reg then the @millivolts value should match the fixed voltage. If + * a variable regulator then the @millivolt value must fit within the max/min + * range of the given regulator. + * + * LOCKING: + * Internally the function takes and releases the TWL VREG lock. + * + * RETURNS: + * Zero on success or a negative error code on failure. + */ +int +twl_vreg_set_voltage(device_t dev, const char *name, int millivolts) +{ + struct twl_vreg_softc *sc; + struct twl_regulator_entry *regulator; + int err = EINVAL; + + sc = device_get_softc(dev); + + TWL_VREG_SLOCK(sc); + + LIST_FOREACH(regulator, &sc->sc_vreg_list, entries) { + if (strcmp(regulator->name, name) == 0) { + err = twl_vreg_write_regulator_voltage(sc, regulator, millivolts); + break; + } + } + + TWL_VREG_SUNLOCK(sc); + + return (err); +} + +/** + * twl_sysctl_voltage - reads or writes the voltage for a regulator + * @SYSCTL_HANDLER_ARGS: arguments for the callback + * + * Callback for the sysctl entry for the regulator, simply used to return + * the voltage on a particular regulator. + * + * LOCKING: + * Takes the TWL_VREG shared lock internally. + * + * RETURNS: + * Zero on success or an error code on failure. + */ +static int +twl_vreg_sysctl_voltage(SYSCTL_HANDLER_ARGS) +{ + struct twl_vreg_softc *sc = (struct twl_vreg_softc*)arg1; + struct twl_regulator_entry *regulator; + int voltage; + int found = 0; + + TWL_VREG_SLOCK(sc); + + /* Find the regulator with the matching name */ + LIST_FOREACH(regulator, &sc->sc_vreg_list, entries) { + if (strcmp(regulator->name, oidp->oid_name) == 0) { + found = 1; + break; + } + } + + /* Sanity check that we found the regulator */ + if (!found) { + TWL_VREG_SUNLOCK(sc); + return (EINVAL); + } + + twl_vreg_read_regulator_voltage(sc, regulator, &voltage); + + TWL_VREG_SUNLOCK(sc); + + return sysctl_handle_int(oidp, &voltage, 0, req); +} + +/** + * twl_add_regulator - adds single voltage regulator sysctls for the device + * @sc: device soft context + * @name: the name of the regulator + * @nsub: the number of the subdevice + * @regbase: the base address of the voltage regulator registers + * @fixed_voltage: if a fixed voltage regulator this defines it's voltage + * @voltages: if a variable voltage regulator, an array of possible voltages + * @num_voltages: the number of entries @voltages + * + * Adds a voltage regulator to the device and also a sysctl interface for the + * regulator. + * + * LOCKING: + * The TWL_VEG exclusive lock must be held while this function is called. + * + * RETURNS: + * Pointer to the new regulator entry on success, otherwise on failure NULL. + */ +static struct twl_regulator_entry* +twl_vreg_add_regulator(struct twl_vreg_softc *sc, const char *name, + uint8_t nsub, uint8_t regbase, uint16_t fixed_voltage, + const uint16_t *voltages, uint32_t num_voltages) +{ + struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev); + struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev); + struct twl_regulator_entry *new; + + new = malloc(sizeof(struct twl_regulator_entry), M_DEVBUF, M_NOWAIT | M_ZERO); + if (new == NULL) + return (NULL); + + + strncpy(new->name, name, TWL_VREG_MAX_NAMELEN); + new->name[TWL_VREG_MAX_NAMELEN - 1] = '\0'; + + new->sub_dev = nsub; + new->reg_off = regbase; + + new->fixed_voltage = fixed_voltage; + + new->supp_voltages = voltages; + new->num_supp_voltages = num_voltages; + + + /* Add a sysctl entry for the voltage */ + new->oid = SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, name, + CTLTYPE_INT | CTLFLAG_RD, sc, 0, + twl_vreg_sysctl_voltage, "I", "voltage regulator"); + + /* Finally add the regulator to list of supported regulators */ + LIST_INSERT_HEAD(&sc->sc_vreg_list, new, entries); + + return (new); +} + +/** + * twl_vreg_add_regulators - adds any voltage regulators to the device + * @sc: device soft context + * @chip: the name of the chip used in the hints + * @regulators: the list of possible voltage regulators + * + * Loops over the list of regulators and matches up with the FDT values, + * adjusting the actual voltage based on the supplied values. + * + * LOCKING: + * The TWL_VEG exclusive lock must be held while this function is called. + * + * RETURNS: + * Always returns 0. + */ +static int +twl_vreg_add_regulators(struct twl_vreg_softc *sc, + const struct twl_regulator *regulators) +{ + int err; + int millivolts; + const struct twl_regulator *walker; + struct twl_regulator_entry *entry; + phandle_t child; + char rnames[256]; + char *name, *voltage; + int len = 0, prop_len; + + + /* Add the regulators from the list */ + walker = ®ulators[0]; + while (walker->name != NULL) { + + /* Add the regulator to the list */ + entry = twl_vreg_add_regulator(sc, walker->name, walker->subdev, + walker->regbase, walker->fixedvoltage, + walker->voltages, walker->num_voltages); + if (entry == NULL) + continue; + + walker++; + } + + + /* Check if the FDT is telling us to set any voltages */ + child = ofw_bus_get_node(sc->sc_pdev); + if (child) { + + prop_len = OF_getprop(child, "voltage-regulators", rnames, sizeof(rnames)); + while (len < prop_len) { + name = rnames + len; + len += strlen(name) + 1; + if ((len >= prop_len) || (name[0] == '\0')) + break; + + voltage = rnames + len; + len += strlen(voltage) + 1; + if (voltage[0] == '\0') + break; + + millivolts = strtoul(voltage, NULL, 0); + + LIST_FOREACH(entry, &sc->sc_vreg_list, entries) { + if (strcmp(entry->name, name) == 0) { + twl_vreg_write_regulator_voltage(sc, entry, millivolts); + break; + } + } + } + } + + + if (twl_vreg_debug) { + LIST_FOREACH(entry, &sc->sc_vreg_list, entries) { + err = twl_vreg_read_regulator_voltage(sc, entry, &millivolts); + if (!err) + device_printf(sc->sc_dev, "%s : %d mV\n", entry->name, millivolts); + } + } + + return (0); +} + +/** + * twl_vreg_init - initialises the list of regulators + * @dev: the twl_vreg device + * + * This function is called as an intrhook once interrupts have been enabled, + * this is done so that the driver has the option to enable/disable or set + * the voltage level based on settings providied in the FDT. + * + * LOCKING: + * Takes the exclusive lock in the function. + */ +static void +twl_vreg_init(void *dev) +{ + struct twl_vreg_softc *sc; + + sc = device_get_softc((device_t)dev); + + TWL_VREG_XLOCK(sc); + + if (twl_is_4030(sc->sc_pdev)) + twl_vreg_add_regulators(sc, twl4030_regulators); + else if (twl_is_6030(sc->sc_pdev) || twl_is_6025(sc->sc_pdev)) + twl_vreg_add_regulators(sc, twl6030_regulators); + + TWL_VREG_XUNLOCK(sc); + + config_intrhook_disestablish(&sc->sc_init_hook); +} + +static int +twl_vreg_probe(device_t dev) +{ + if (twl_is_4030(device_get_parent(dev))) + device_set_desc(dev, "TI TWL4030 PMIC Voltage Regulators"); + else if (twl_is_6025(device_get_parent(dev)) || + twl_is_6030(device_get_parent(dev))) + device_set_desc(dev, "TI TWL6025/TWL6030 PMIC Voltage Regulators"); + else + return (ENXIO); + + return (0); +} + +static int +twl_vreg_attach(device_t dev) +{ + struct twl_vreg_softc *sc; + + sc = device_get_softc(dev); + sc->sc_dev = dev; + sc->sc_pdev = device_get_parent(dev); + + TWL_VREG_LOCK_INIT(sc); + + LIST_INIT(&sc->sc_vreg_list); + + /* We have to wait until interrupts are enabled. I2C read and write + * only works if the interrupts are available. + */ + sc->sc_init_hook.ich_func = twl_vreg_init; + sc->sc_init_hook.ich_arg = dev; + + if (config_intrhook_establish(&sc->sc_init_hook) != 0) + return (ENOMEM); + + return (0); +} + +static int +twl_vreg_detach(device_t dev) +{ + struct twl_vreg_softc *sc; + struct twl_regulator_entry *regulator; + struct twl_regulator_entry *tmp; + + sc = device_get_softc(dev); + + /* Take the lock and free all the added regulators */ + TWL_VREG_XLOCK(sc); + + LIST_FOREACH_SAFE(regulator, &sc->sc_vreg_list, entries, tmp) { + LIST_REMOVE(regulator, entries); + sysctl_remove_oid(regulator->oid, 1, 0); + free(regulator, M_DEVBUF); + } + + TWL_VREG_XUNLOCK(sc); + + TWL_VREG_LOCK_DESTROY(sc); + + return (0); +} + +static device_method_t twl_vreg_methods[] = { + DEVMETHOD(device_probe, twl_vreg_probe), + DEVMETHOD(device_attach, twl_vreg_attach), + DEVMETHOD(device_detach, twl_vreg_detach), + + {0, 0}, +}; + +static driver_t twl_vreg_driver = { + "twl_vreg", + twl_vreg_methods, + sizeof(struct twl_vreg_softc), +}; + +static devclass_t twl_vreg_devclass; + +DRIVER_MODULE(twl_vreg, twl, twl_vreg_driver, twl_vreg_devclass, 0, 0); +MODULE_VERSION(twl_vreg, 1); diff --git a/sys/arm/ti/twl/twl_vreg.h b/sys/arm/ti/twl/twl_vreg.h new file mode 100644 index 000000000000..dc77dfcfaaca --- /dev/null +++ b/sys/arm/ti/twl/twl_vreg.h @@ -0,0 +1,36 @@ +/*- + * 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 _TWL_VREG_H_ +#define _TWL_VREG_H_ + + +int twl_vreg_get_voltage(device_t dev, const char *name, int *millivolts); +int twl_vreg_set_voltage(device_t dev, const char *name, int millivolts); + +#endif /* _TWL_VREG_H_ */ diff --git a/sys/arm/ti/usb/omap_ehci.c b/sys/arm/ti/usb/omap_ehci.c new file mode 100644 index 000000000000..b216dfbaf557 --- /dev/null +++ b/sys/arm/ti/usb/omap_ehci.c @@ -0,0 +1,1024 @@ +/*- + * 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. + */ + +/** + * Driver for the High Speed USB EHCI module on the TI OMAP3530 SoC. + * + * WARNING: I've only tried this driver on a limited number of USB peripherals, + * it is still very raw and bound to have numerous bugs in it. + * + * This driver is based on the FreeBSD IXP4xx EHCI driver with a lot of the + * setup sequence coming from the Linux community and their EHCI driver for + * OMAP. Without these as a base I don't think I would have been able to get + * this driver working. + * + * The driver only contains the EHCI parts, the module also supports OHCI and + * USB on-the-go (OTG), currently neither are supported. + * + * CAUTION: This driver was written to run on the beaglebaord dev board, so I + * have made some assumptions about the type of PHY used and some of the other + * settings. Bare that in mind if you intend to use this driver on another + * platform. + * + * NOTE: This module uses a few different clocks, one being a 60Mhz clock for + * the TTL part of the module. This clock is derived from DPPL5 which must be + * configured prior to loading this driver - it is not configured by the + * bootloader. It took me a long time to figure this out, and caused much + * frustration. This PLL is now setup in the timer/clocks part of the BSP, + * check out the omap_prcm_setup_dpll5() function in omap_prcm.c for more info. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include "opt_bus.h" + +#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 +#include +#include + +#include + +#include "gpio_if.h" + +struct omap_ehci_softc { + ehci_softc_t base; /* storage for EHCI code */ + + device_t sc_dev; + device_t sc_gpio_dev; + + /* TLL register set */ + struct resource* tll_mem_res; + + /* UHH register set */ + struct resource* uhh_mem_res; + + /* The revision of the HS USB HOST read from UHH_REVISION */ + uint32_t ehci_rev; + + /* The following details are provided by conf hints */ + int port_mode[3]; + int phy_reset[3]; + int reset_gpio_pin[3]; +}; + +static device_attach_t omap_ehci_attach; +static device_detach_t omap_ehci_detach; +static device_shutdown_t omap_ehci_shutdown; +static device_suspend_t omap_ehci_suspend; +static device_resume_t omap_ehci_resume; + +/** + * omap_tll_read_4 - read a 32-bit value from the USBTLL registers + * omap_tll_write_4 - write a 32-bit value from the USBTLL registers + * omap_tll_readb - read an 8-bit value from the USBTLL registers + * omap_tll_writeb - write an 8-bit value from the USBTLL registers + * @sc: omap ehci device context + * @off: byte offset within the register set to read from + * @val: the value to write into the register + * + * + * LOCKING: + * None + * + * RETURNS: + * nothing in case of write function, if read function returns the value read. + */ +static inline uint32_t +omap_tll_read_4(struct omap_ehci_softc *sc, bus_size_t off) +{ + return bus_read_4(sc->tll_mem_res, off); +} + +static inline void +omap_tll_write_4(struct omap_ehci_softc *sc, bus_size_t off, uint32_t val) +{ + bus_write_4(sc->tll_mem_res, off, val); +} + +static inline uint8_t +omap_tll_readb(struct omap_ehci_softc *sc, bus_size_t off) +{ + return bus_read_1(sc->tll_mem_res, off); +} + +static inline void +omap_tll_writeb(struct omap_ehci_softc *sc, bus_size_t off, uint8_t val) +{ + bus_write_1(sc->tll_mem_res, off, val); +} + +/** + * omap_ehci_read_4 - read a 32-bit value from the EHCI registers + * omap_ehci_write_4 - write a 32-bit value from the EHCI registers + * @sc: omap ehci device context + * @off: byte offset within the register set to read from + * @val: the value to write into the register + * + * + * LOCKING: + * None + * + * RETURNS: + * nothing in case of write function, if read function returns the value read. + */ +static inline uint32_t +omap_ehci_read_4(struct omap_ehci_softc *sc, bus_size_t off) +{ + return (bus_read_4(sc->base.sc_io_res, off)); +} +static inline void +omap_ehci_write_4(struct omap_ehci_softc *sc, bus_size_t off, uint32_t val) +{ + bus_write_4(sc->base.sc_io_res, off, val); +} + +/** + * omap_uhh_read_4 - read a 32-bit value from the UHH registers + * omap_uhh_write_4 - write a 32-bit value from the UHH registers + * @sc: omap ehci device context + * @off: byte offset within the register set to read from + * @val: the value to write into the register + * + * + * LOCKING: + * None + * + * RETURNS: + * nothing in case of write function, if read function returns the value read. + */ +static inline uint32_t +omap_uhh_read_4(struct omap_ehci_softc *sc, bus_size_t off) +{ + return bus_read_4(sc->uhh_mem_res, off); +} +static inline void +omap_uhh_write_4(struct omap_ehci_softc *sc, bus_size_t off, uint32_t val) +{ + bus_write_4(sc->uhh_mem_res, off, val); +} + +/** + * omap_ehci_utmi_init - initialises the UTMI part of the controller + * @isc: omap ehci device context + * + * + * + * LOCKING: + * none + * + * RETURNS: + * nothing + */ +static void +omap_ehci_utmi_init(struct omap_ehci_softc *isc, unsigned int en_mask) +{ + unsigned int i; + uint32_t reg; + + /* There are 3 TLL channels, one per USB controller so set them all up the + * same, SDR mode, bit stuffing and no autoidle. + */ + for (i=0; i<3; i++) { + reg = omap_tll_read_4(isc, OMAP_USBTLL_TLL_CHANNEL_CONF(i)); + + reg &= ~(TLL_CHANNEL_CONF_UTMIAUTOIDLE + | TLL_CHANNEL_CONF_ULPINOBITSTUFF + | TLL_CHANNEL_CONF_ULPIDDRMODE); + + omap_tll_write_4(isc, OMAP_USBTLL_TLL_CHANNEL_CONF(i), reg); + } + + /* Program the common TLL register */ + reg = omap_tll_read_4(isc, OMAP_USBTLL_TLL_SHARED_CONF); + + reg &= ~( TLL_SHARED_CONF_USB_90D_DDR_EN + | TLL_SHARED_CONF_USB_DIVRATIO_MASK); + reg |= ( TLL_SHARED_CONF_FCLK_IS_ON + | TLL_SHARED_CONF_USB_DIVRATIO_2 + | TLL_SHARED_CONF_USB_180D_SDR_EN); + + omap_tll_write_4(isc, OMAP_USBTLL_TLL_SHARED_CONF, reg); + + /* Enable channels now */ + for (i = 0; i < 3; i++) { + reg = omap_tll_read_4(isc, OMAP_USBTLL_TLL_CHANNEL_CONF(i)); + + /* Enable only the reg that is needed */ + if ((en_mask & (1 << i)) == 0) + continue; + + reg |= TLL_CHANNEL_CONF_CHANEN; + omap_tll_write_4(isc, OMAP_USBTLL_TLL_CHANNEL_CONF(i), reg); + } +} + +/** + * omap_ehci_soft_phy_reset - resets the phy using the reset command + * @isc: omap ehci device context + * @port: port to send the reset over + * + * + * LOCKING: + * none + * + * RETURNS: + * nothing + */ +static void +omap_ehci_soft_phy_reset(struct omap_ehci_softc *isc, unsigned int port) +{ + unsigned long timeout = (hz < 10) ? 1 : ((100 * hz) / 1000); + uint32_t reg; + + reg = ULPI_FUNC_CTRL_RESET + /* FUNCTION_CTRL_SET register */ + | (ULPI_SET(ULPI_FUNC_CTRL) << OMAP_USBHOST_INSNREG05_ULPI_REGADD_SHIFT) + /* Write */ + | (2 << OMAP_USBHOST_INSNREG05_ULPI_OPSEL_SHIFT) + /* PORTn */ + | ((port + 1) << OMAP_USBHOST_INSNREG05_ULPI_PORTSEL_SHIFT) + /* start ULPI access*/ + | (1 << OMAP_USBHOST_INSNREG05_ULPI_CONTROL_SHIFT); + + omap_ehci_write_4(isc, OMAP_USBHOST_INSNREG05_ULPI, reg); + + /* Wait for ULPI access completion */ + while ((omap_ehci_read_4(isc, OMAP_USBHOST_INSNREG05_ULPI) + & (1 << OMAP_USBHOST_INSNREG05_ULPI_CONTROL_SHIFT))) { + + /* Sleep for a tick */ + pause("USBPHY_RESET", 1); + + if (timeout-- == 0) { + device_printf(isc->sc_dev, "PHY reset operation timed out\n"); + break; + } + } +} + + +/** + * omap_ehci_init - initialises the USB host EHCI controller + * @isc: omap ehci device context + * + * This initialisation routine is quite heavily based on the work done by the + * OMAP Linux team (for which I thank them very much). The init sequence is + * almost identical, diverging only for the FreeBSD specifics. + * + * LOCKING: + * none + * + * RETURNS: + * 0 on success, a negative error code on failure. + */ +static int +omap_ehci_init(struct omap_ehci_softc *isc) +{ + unsigned long timeout; + int ret = 0; + uint8_t tll_ch_mask = 0; + uint32_t reg = 0; + int reset_performed = 0; + int i; + + device_printf(isc->sc_dev, "Starting TI EHCI USB Controller\n"); + + + /* Enable Clocks for high speed USBHOST */ + ti_prcm_clk_enable(USBHSHOST_CLK); + + /* Hold the PHY in reset while configuring */ + for (int i = 0; i < 3; i++) { + if (isc->phy_reset[i]) { + /* Configure the GPIO to drive low (hold in reset) */ + if ((isc->reset_gpio_pin[i] != -1) && (isc->sc_gpio_dev != NULL)) { + GPIO_PIN_SETFLAGS(isc->sc_gpio_dev, isc->reset_gpio_pin[i], + GPIO_PIN_OUTPUT); + GPIO_PIN_SET(isc->sc_gpio_dev, isc->reset_gpio_pin[i], + GPIO_PIN_LOW); + reset_performed = 1; + } + } + } + + /* Hold the PHY in RESET for enough time till DIR is high */ + if (reset_performed) + DELAY(10); + + /* Read the UHH revision */ + isc->ehci_rev = omap_uhh_read_4(isc, OMAP_USBHOST_UHH_REVISION); + device_printf(isc->sc_dev, "UHH revision 0x%08x\n", isc->ehci_rev); + + /* Initilise the low level interface module(s) */ + if (isc->ehci_rev == OMAP_EHCI_REV1) { + + /* Enable the USB TLL */ + ti_prcm_clk_enable(USBTLL_CLK); + + /* Perform TLL soft reset, and wait until reset is complete */ + omap_tll_write_4(isc, OMAP_USBTLL_SYSCONFIG, TLL_SYSCONFIG_SOFTRESET); + + /* Set the timeout to 100ms*/ + timeout = (hz < 10) ? 1 : ((100 * hz) / 1000); + + /* Wait for TLL reset to complete */ + while ((omap_tll_read_4(isc, OMAP_USBTLL_SYSSTATUS) & + TLL_SYSSTATUS_RESETDONE) == 0x00) { + + /* Sleep for a tick */ + pause("USBRESET", 1); + + if (timeout-- == 0) { + device_printf(isc->sc_dev, "TLL reset operation timed out\n"); + ret = EINVAL; + goto err_sys_status; + } + } + + device_printf(isc->sc_dev, "TLL RESET DONE\n"); + + /* CLOCKACTIVITY = 1 : OCP-derived internal clocks ON during idle + * SIDLEMODE = 2 : Smart-idle mode. Sidleack asserted after Idlereq + * assertion when no more activity on the USB. + * ENAWAKEUP = 1 : Wakeup generation enabled + */ + omap_tll_write_4(isc, OMAP_USBTLL_SYSCONFIG, TLL_SYSCONFIG_ENAWAKEUP | + TLL_SYSCONFIG_AUTOIDLE | + TLL_SYSCONFIG_SIDLE_SMART_IDLE | + TLL_SYSCONFIG_CACTIVITY); + + } else if (isc->ehci_rev == OMAP_EHCI_REV2) { + + /* For OMAP44xx devices you have to enable the per-port clocks: + * PHY_MODE - External ULPI clock + * TTL_MODE - Internal UTMI clock + * HSIC_MODE - Internal 480Mhz and 60Mhz clocks + */ + if (isc->ehci_rev == OMAP_EHCI_REV2) { + if (isc->port_mode[0] == EHCI_HCD_OMAP_MODE_PHY) { + ti_prcm_clk_set_source(USBP1_PHY_CLK, EXT_CLK); + ti_prcm_clk_enable(USBP1_PHY_CLK); + } else if (isc->port_mode[0] == EHCI_HCD_OMAP_MODE_TLL) + ti_prcm_clk_enable(USBP1_UTMI_CLK); + else if (isc->port_mode[0] == EHCI_HCD_OMAP_MODE_HSIC) + ti_prcm_clk_enable(USBP1_HSIC_CLK); + + if (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_PHY) { + ti_prcm_clk_set_source(USBP2_PHY_CLK, EXT_CLK); + ti_prcm_clk_enable(USBP2_PHY_CLK); + } else if (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_TLL) + ti_prcm_clk_enable(USBP2_UTMI_CLK); + else if (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_HSIC) + ti_prcm_clk_enable(USBP2_HSIC_CLK); + } + } + + /* Put UHH in SmartIdle/SmartStandby mode */ + reg = omap_uhh_read_4(isc, OMAP_USBHOST_UHH_SYSCONFIG); + if (isc->ehci_rev == OMAP_EHCI_REV1) { + reg &= ~(UHH_SYSCONFIG_SIDLEMODE_MASK | + UHH_SYSCONFIG_MIDLEMODE_MASK); + reg |= (UHH_SYSCONFIG_ENAWAKEUP | + UHH_SYSCONFIG_AUTOIDLE | + UHH_SYSCONFIG_CLOCKACTIVITY | + UHH_SYSCONFIG_SIDLEMODE_SMARTIDLE | + UHH_SYSCONFIG_MIDLEMODE_SMARTSTANDBY); + } else if (isc->ehci_rev == OMAP_EHCI_REV2) { + reg &= ~UHH_SYSCONFIG_IDLEMODE_MASK; + reg |= UHH_SYSCONFIG_IDLEMODE_NOIDLE; + reg &= ~UHH_SYSCONFIG_STANDBYMODE_MASK; + reg |= UHH_SYSCONFIG_STANDBYMODE_NOSTDBY; + } + omap_uhh_write_4(isc, OMAP_USBHOST_UHH_SYSCONFIG, reg); + device_printf(isc->sc_dev, "OMAP_UHH_SYSCONFIG: 0x%08x\n", reg); + + reg = omap_uhh_read_4(isc, OMAP_USBHOST_UHH_HOSTCONFIG); + + /* Setup ULPI bypass and burst configurations */ + reg |= (UHH_HOSTCONFIG_ENA_INCR4 | + UHH_HOSTCONFIG_ENA_INCR8 | + UHH_HOSTCONFIG_ENA_INCR16); + reg &= ~UHH_HOSTCONFIG_ENA_INCR_ALIGN; + + if (isc->ehci_rev == OMAP_EHCI_REV1) { + if (isc->port_mode[0] == EHCI_HCD_OMAP_MODE_UNKNOWN) + reg &= ~UHH_HOSTCONFIG_P1_CONNECT_STATUS; + if (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_UNKNOWN) + reg &= ~UHH_HOSTCONFIG_P2_CONNECT_STATUS; + if (isc->port_mode[2] == EHCI_HCD_OMAP_MODE_UNKNOWN) + reg &= ~UHH_HOSTCONFIG_P3_CONNECT_STATUS; + + /* Bypass the TLL module for PHY mode operation */ + if ((isc->port_mode[0] == EHCI_HCD_OMAP_MODE_PHY) || + (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_PHY) || + (isc->port_mode[2] == EHCI_HCD_OMAP_MODE_PHY)) + reg &= ~UHH_HOSTCONFIG_P1_ULPI_BYPASS; + else + reg |= UHH_HOSTCONFIG_P1_ULPI_BYPASS; + + } else if (isc->ehci_rev == OMAP_EHCI_REV2) { + reg |= UHH_HOSTCONFIG_APP_START_CLK; + + /* Clear port mode fields for PHY mode*/ + reg &= ~UHH_HOSTCONFIG_P1_MODE_MASK; + reg &= ~UHH_HOSTCONFIG_P2_MODE_MASK; + + if (isc->port_mode[0] == EHCI_HCD_OMAP_MODE_TLL) + reg |= UHH_HOSTCONFIG_P1_MODE_UTMI_PHY; + else if (isc->port_mode[0] == EHCI_HCD_OMAP_MODE_HSIC) + reg |= UHH_HOSTCONFIG_P1_MODE_HSIC; + + if (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_TLL) + reg |= UHH_HOSTCONFIG_P2_MODE_UTMI_PHY; + else if (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_HSIC) + reg |= UHH_HOSTCONFIG_P2_MODE_HSIC; + } + + omap_uhh_write_4(isc, OMAP_USBHOST_UHH_HOSTCONFIG, reg); + device_printf(isc->sc_dev, "UHH setup done, uhh_hostconfig=0x%08x\n", reg); + + + /* I found the code and comments in the Linux EHCI driver - thanks guys :) + * + * "An undocumented "feature" in the OMAP3 EHCI controller, causes suspended + * ports to be taken out of suspend when the USBCMD.Run/Stop bit is cleared + * (for example when we do ehci_bus_suspend). This breaks suspend-resume if + * the root-hub is allowed to suspend. Writing 1 to this undocumented + * register bit disables this feature and restores normal behavior." + */ +#if 0 + omap_ehci_write_4(isc, OMAP_USBHOST_INSNREG04, + OMAP_USBHOST_INSNREG04_DISABLE_UNSUSPEND); +#endif + + /* If any of the ports are configured in TLL mode, enable them */ + if ((isc->port_mode[0] == EHCI_HCD_OMAP_MODE_TLL) || + (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_TLL) || + (isc->port_mode[2] == EHCI_HCD_OMAP_MODE_TLL)) { + + if (isc->port_mode[0] == EHCI_HCD_OMAP_MODE_TLL) + tll_ch_mask |= 0x1; + if (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_TLL) + tll_ch_mask |= 0x2; + if (isc->port_mode[2] == EHCI_HCD_OMAP_MODE_TLL) + tll_ch_mask |= 0x4; + + /* Enable UTMI mode for required TLL channels */ + omap_ehci_utmi_init(isc, tll_ch_mask); + } + + + /* Release the PHY reset signal now we have configured everything */ + if (reset_performed) { + + /* Delay for 10ms */ + DELAY(10000); + + for (i = 0; i < 3; i++) { + /* Release reset */ + + if (isc->phy_reset[i] && (isc->reset_gpio_pin[i] != -1) + && (isc->sc_gpio_dev != NULL)) { + GPIO_PIN_SET(isc->sc_gpio_dev, + isc->reset_gpio_pin[i], GPIO_PIN_HIGH); + } + } + } + + /* Set the interrupt threshold control, it controls the maximum rate at + * which the host controller issues interrupts. We set it to 1 microframe + * at startup - the default is 8 mircoframes (equates to 1ms). + */ + reg = omap_ehci_read_4(isc, OMAP_USBHOST_USBCMD); + reg &= 0xff00ffff; + reg |= (1 << 16); + omap_ehci_write_4(isc, OMAP_USBHOST_USBCMD, reg); + + /* Soft reset the PHY using PHY reset command over ULPI */ + if (isc->port_mode[0] == EHCI_HCD_OMAP_MODE_PHY) + omap_ehci_soft_phy_reset(isc, 0); + if (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_PHY) + omap_ehci_soft_phy_reset(isc, 1); + + return(0); + +err_sys_status: + + /* Disable the TLL clocks */ + ti_prcm_clk_disable(USBTLL_CLK); + + /* Disable Clocks for USBHOST */ + ti_prcm_clk_disable(USBHSHOST_CLK); + + return(ret); +} + + +/** + * omap_ehci_fini - shutdown the EHCI controller + * @isc: omap ehci device context + * + * + * + * LOCKING: + * none + * + * RETURNS: + * 0 on success, a negative error code on failure. + */ +static void +omap_ehci_fini(struct omap_ehci_softc *isc) +{ + unsigned long timeout; + + device_printf(isc->sc_dev, "Stopping TI EHCI USB Controller\n"); + + /* Set the timeout */ + if (hz < 10) + timeout = 1; + else + timeout = (100 * hz) / 1000; + + /* Reset the UHH, OHCI and EHCI modules */ + omap_uhh_write_4(isc, OMAP_USBHOST_UHH_SYSCONFIG, 0x0002); + while ((omap_uhh_read_4(isc, OMAP_USBHOST_UHH_SYSSTATUS) & 0x07) == 0x00) { + /* Sleep for a tick */ + pause("USBRESET", 1); + + if (timeout-- == 0) { + device_printf(isc->sc_dev, "operation timed out\n"); + break; + } + } + + + /* Set the timeout */ + if (hz < 10) + timeout = 1; + else + timeout = (100 * hz) / 1000; + + /* Reset the TLL module */ + omap_tll_write_4(isc, OMAP_USBTLL_SYSCONFIG, 0x0002); + while ((omap_tll_read_4(isc, OMAP_USBTLL_SYSSTATUS) & (0x01)) == 0x00) { + /* Sleep for a tick */ + pause("USBRESET", 1); + + if (timeout-- == 0) { + device_printf(isc->sc_dev, "operation timed out\n"); + break; + } + } + + + /* Disable functional and interface clocks for the TLL and HOST modules */ + ti_prcm_clk_disable(USBTLL_CLK); + ti_prcm_clk_disable(USBHSHOST_CLK); + + device_printf(isc->sc_dev, "Clock to USB host has been disabled\n"); + +} + + + +/** + * omap_ehci_suspend - suspends the bus + * @dev: omap ehci device + * + * Effectively boilerplate EHCI suspend code. + * + * TODO: There is a lot more we could do here - i.e. force the controller into + * idle mode and disable all the clocks for start. + * + * LOCKING: + * none + * + * RETURNS: + * 0 on success or a positive error code + */ +static int +omap_ehci_suspend(device_t dev) +{ + ehci_softc_t *sc = device_get_softc(dev); + int err; + + sc = sc; + err = bus_generic_suspend(dev); + if (err) + return (err); + return (0); +} + + +/** + * omap_ehci_resume - resumes a suspended bus + * @dev: omap ehci device + * + * Effectively boilerplate EHCI resume code. + * + * LOCKING: + * none + * + * RETURNS: + * 0 on success or a positive error code on failure + */ +static int +omap_ehci_resume(device_t dev) +{ + ehci_softc_t *sc = device_get_softc(dev); + sc = sc; + + bus_generic_resume(dev); + + return (0); +} + + +/** + * omap_ehci_shutdown - starts the given command + * @dev: + * + * Effectively boilerplate EHCI shutdown code. + * + * LOCKING: + * none. + * + * RETURNS: + * 0 on success or a positive error code on failure + */ +static int +omap_ehci_shutdown(device_t dev) +{ + ehci_softc_t *sc = device_get_softc(dev); + int err; + + sc = sc; + err = bus_generic_shutdown(dev); + if (err) + return (err); + + return (0); +} + + +/** + * omap_ehci_probe - starts the given command + * @dev: + * + * Effectively boilerplate EHCI resume code. + * + * LOCKING: + * Caller should be holding the OMAP3_MMC lock. + * + * RETURNS: + * EH_HANDLED or EH_NOT_HANDLED + */ +static int +omap_ehci_probe(device_t dev) +{ + if (!ofw_bus_is_compatible(dev, "ti,usb-ehci")) + return (ENXIO); + + device_set_desc(dev, OMAP_EHCI_HC_DEVSTR); + + return (BUS_PROBE_DEFAULT); +} + +/** + * omap_ehci_attach - driver entry point, sets up the ECHI controller/driver + * @dev: the new device handle + * + * Sets up bus spaces, interrupt handles, etc for the EHCI controller. It also + * parses the resource hints and calls omap_ehci_init() to initialise the + * H/W. + * + * LOCKING: + * none + * + * RETURNS: + * 0 on success or a positive error code on failure. + */ +static int +omap_ehci_attach(device_t dev) +{ + struct omap_ehci_softc *isc = device_get_softc(dev); + phandle_t node; + /* 3 ports with 3 cells per port */ + pcell_t phyconf[3 * 3]; + pcell_t *phyconf_ptr; + ehci_softc_t *sc = &isc->base; + int err; + int rid; + int len, tuple_size; + int i; + + /* initialise some bus fields */ + sc->sc_bus.parent = dev; + sc->sc_bus.devices = sc->sc_devices; + sc->sc_bus.devices_max = EHCI_MAX_DEVICES; + + /* save the device */ + isc->sc_dev = dev; + + /* get all DMA memory */ + if (usb_bus_mem_alloc_all(&sc->sc_bus, USB_GET_DMA_TAG(dev), + &ehci_iterate_hw_softc)) { + return (ENOMEM); + } + + /* When the EHCI driver is added to the tree it is expected that 3 + * memory resources and 1 interrupt resource is assigned. The memory + * resources should be: + * 0 => EHCI register range + * 1 => UHH register range + * 2 => TLL register range + * + * The interrupt resource is just the single interupt for the controller. + */ + + /* Allocate resource for the EHCI register set */ + rid = 0; + sc->sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); + if (!sc->sc_io_res) { + device_printf(dev, "Error: Could not map EHCI memory\n"); + goto error; + } + /* Request an interrupt resource */ + rid = 0; + sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); + if (sc->sc_irq_res == NULL) { + device_printf(dev, "Error: could not allocate irq\n"); + goto error; + } + + /* Allocate resource for the UHH register set */ + rid = 1; + isc->uhh_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); + if (!isc->uhh_mem_res) { + device_printf(dev, "Error: Could not map UHH memory\n"); + goto error; + } + /* Allocate resource for the TLL register set */ + rid = 2; + isc->tll_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); + if (!isc->tll_mem_res) { + device_printf(dev, "Error: Could not map TLL memory\n"); + goto error; + } + + /* Add this device as a child of the USBus device */ + sc->sc_bus.bdev = device_add_child(dev, "usbus", -1); + if (!sc->sc_bus.bdev) { + device_printf(dev, "Error: could not add USB device\n"); + goto error; + } + + device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); + device_set_desc(sc->sc_bus.bdev, OMAP_EHCI_HC_DEVSTR); + + /* Set the vendor name */ + sprintf(sc->sc_vendor, "Texas Instruments"); + + /* Get the GPIO device, we may need this if the driver needs to toggle + * some pins for external PHY resets. + */ + isc->sc_gpio_dev = devclass_get_device(devclass_find("gpio"), 0); + if (isc->sc_gpio_dev == NULL) { + device_printf(dev, "Error: failed to get the GPIO device\n"); + goto error; + } + + /* Set the defaults for the hints */ + for (i = 0; i < 3; i++) { + isc->phy_reset[i] = 0; + isc->port_mode[i] = EHCI_HCD_OMAP_MODE_UNKNOWN; + isc->reset_gpio_pin[i] = -1; + } + + tuple_size = sizeof(pcell_t) * 3; + node = ofw_bus_get_node(dev); + len = OF_getprop(node, "phy-config", phyconf, sizeof(phyconf)); + if (len > 0) { + if (len % tuple_size) + goto error; + if ((len / tuple_size) != 3) + goto error; + + phyconf_ptr = phyconf; + for (i = 0; i < 3; i++) { + isc->port_mode[i] = fdt32_to_cpu(*phyconf_ptr); + isc->phy_reset[i] = fdt32_to_cpu(*(phyconf_ptr + 1)); + isc->reset_gpio_pin[i] = fdt32_to_cpu(*(phyconf_ptr + 2)); + + phyconf_ptr += 3; + } + } + + /* Initialise the ECHI registers */ + err = omap_ehci_init(isc); + if (err) { + device_printf(dev, "Error: could not setup OMAP EHCI, %d\n", err); + goto error; + } + + + /* Set the tag and size of the register set in the EHCI context */ + sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res); + sc->sc_io_tag = rman_get_bustag(sc->sc_io_res); + sc->sc_io_size = rman_get_size(sc->sc_io_res); + + + /* Setup the interrupt */ + err = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, + NULL, (driver_intr_t *)ehci_interrupt, sc, &sc->sc_intr_hdl); + if (err) { + device_printf(dev, "Error: could not setup irq, %d\n", err); + sc->sc_intr_hdl = NULL; + goto error; + } + + + /* Finally we are ready to kick off the ECHI host controller */ + err = ehci_init(sc); + if (err == 0) { + err = device_probe_and_attach(sc->sc_bus.bdev); + } + if (err) { + device_printf(dev, "Error: USB init failed err=%d\n", err); + goto error; + } + + return (0); + +error: + omap_ehci_detach(dev); + return (ENXIO); +} + +/** + * omap_ehci_detach - detach the device and cleanup the driver + * @dev: device handle + * + * Clean-up routine where everything initialised in omap_ehci_attach is + * freed and cleaned up. This function calls omap_ehci_fini() to shutdown + * the on-chip module. + * + * LOCKING: + * none + * + * RETURNS: + * Always returns 0 (success). + */ +static int +omap_ehci_detach(device_t dev) +{ + struct omap_ehci_softc *isc = device_get_softc(dev); + ehci_softc_t *sc = &isc->base; + device_t bdev; + int err; + + if (sc->sc_bus.bdev) { + bdev = sc->sc_bus.bdev; + device_detach(bdev); + device_delete_child(dev, bdev); + } + + /* during module unload there are lots of children leftover */ + device_delete_children(dev); + + /* + * disable interrupts that might have been switched on in ehci_init + */ + if (sc->sc_io_res) { + EWRITE4(sc, EHCI_USBINTR, 0); + } + + if (sc->sc_irq_res && sc->sc_intr_hdl) { + /* + * only call ehci_detach() after ehci_init() + */ + ehci_detach(sc); + + err = bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intr_hdl); + if (err) + device_printf(dev, "Error: could not tear down irq, %d\n", err); + sc->sc_intr_hdl = NULL; + } + + /* Free the resources stored in the base EHCI handler */ + if (sc->sc_irq_res) { + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res); + sc->sc_irq_res = NULL; + } + if (sc->sc_io_res) { + bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_io_res); + sc->sc_io_res = NULL; + } + + /* Release the other register set memory maps */ + if (isc->tll_mem_res) { + bus_release_resource(dev, SYS_RES_MEMORY, 0, isc->tll_mem_res); + isc->tll_mem_res = NULL; + } + if (isc->uhh_mem_res) { + bus_release_resource(dev, SYS_RES_MEMORY, 0, isc->uhh_mem_res); + isc->uhh_mem_res = NULL; + } + + usb_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc); + + omap_ehci_fini(isc); + + return (0); +} + +static device_method_t ehci_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, omap_ehci_probe), + DEVMETHOD(device_attach, omap_ehci_attach), + DEVMETHOD(device_detach, omap_ehci_detach), + DEVMETHOD(device_suspend, omap_ehci_suspend), + DEVMETHOD(device_resume, omap_ehci_resume), + DEVMETHOD(device_shutdown, omap_ehci_shutdown), + + /* Bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + + {0, 0} +}; + +static driver_t ehci_driver = { + "ehci", + ehci_methods, + sizeof(struct omap_ehci_softc), +}; + +static devclass_t ehci_devclass; + +DRIVER_MODULE(ehci, simplebus, ehci_driver, ehci_devclass, 0, 0); diff --git a/sys/arm/ti/usb/omap_usb.h b/sys/arm/ti/usb/omap_usb.h new file mode 100644 index 000000000000..bea795748dcd --- /dev/null +++ b/sys/arm/ti/usb/omap_usb.h @@ -0,0 +1,264 @@ +/* + * 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 _OMAP_USB_H_ +#define _OMAP_USB_H_ + +/* + * USB TTL Module + */ +#define OMAP_USBTLL_REVISION 0x0000 +#define OMAP_USBTLL_SYSCONFIG 0x0010 +#define OMAP_USBTLL_SYSSTATUS 0x0014 +#define OMAP_USBTLL_IRQSTATUS 0x0018 +#define OMAP_USBTLL_IRQENABLE 0x001C +#define OMAP_USBTLL_TLL_SHARED_CONF 0x0030 +#define OMAP_USBTLL_TLL_CHANNEL_CONF(i) (0x0040 + (0x04 * (i))) +#define OMAP_USBTLL_SAR_CNTX(i) (0x0400 + (0x04 * (i))) +#define OMAP_USBTLL_ULPI_VENDOR_ID_LO(i) (0x0800 + (0x100 * (i))) +#define OMAP_USBTLL_ULPI_VENDOR_ID_HI(i) (0x0801 + (0x100 * (i))) +#define OMAP_USBTLL_ULPI_PRODUCT_ID_LO(i) (0x0802 + (0x100 * (i))) +#define OMAP_USBTLL_ULPI_PRODUCT_ID_HI(i) (0x0803 + (0x100 * (i))) +#define OMAP_USBTLL_ULPI_FUNCTION_CTRL(i) (0x0804 + (0x100 * (i))) +#define OMAP_USBTLL_ULPI_FUNCTION_CTRL_SET(i) (0x0805 + (0x100 * (i))) +#define OMAP_USBTLL_ULPI_FUNCTION_CTRL_CLR(i) (0x0806 + (0x100 * (i))) +#define OMAP_USBTLL_ULPI_INTERFACE_CTRL(i) (0x0807 + (0x100 * (i))) +#define OMAP_USBTLL_ULPI_INTERFACE_CTRL_SET(i) (0x0808 + (0x100 * (i))) +#define OMAP_USBTLL_ULPI_INTERFACE_CTRL_CLR(i) (0x0809 + (0x100 * (i))) +#define OMAP_USBTLL_ULPI_OTG_CTRL(i) (0x080A + (0x100 * (i))) +#define OMAP_USBTLL_ULPI_OTG_CTRL_SET(i) (0x080B + (0x100 * (i))) +#define OMAP_USBTLL_ULPI_OTG_CTRL_CLR(i) (0x080C + (0x100 * (i))) +#define OMAP_USBTLL_ULPI_USB_INT_EN_RISE(i) (0x080D + (0x100 * (i))) +#define OMAP_USBTLL_ULPI_USB_INT_EN_RISE_SET(i) (0x080E + (0x100 * (i))) +#define OMAP_USBTLL_ULPI_USB_INT_EN_RISE_CLR(i) (0x080F + (0x100 * (i))) +#define OMAP_USBTLL_ULPI_USB_INT_EN_FALL(i) (0x0810 + (0x100 * (i))) +#define OMAP_USBTLL_ULPI_USB_INT_EN_FALL_SET(i) (0x0811 + (0x100 * (i))) +#define OMAP_USBTLL_ULPI_USB_INT_EN_FALL_CLR(i) (0x0812 + (0x100 * (i))) +#define OMAP_USBTLL_ULPI_USB_INT_STATUS(i) (0x0813 + (0x100 * (i))) +#define OMAP_USBTLL_ULPI_USB_INT_LATCH(i) (0x0814 + (0x100 * (i))) +#define OMAP_USBTLL_ULPI_DEBUG(i) (0x0815 + (0x100 * (i))) +#define OMAP_USBTLL_ULPI_SCRATCH_REGISTER(i) (0x0816 + (0x100 * (i))) +#define OMAP_USBTLL_ULPI_SCRATCH_REGISTER_SET(i) (0x0817 + (0x100 * (i))) +#define OMAP_USBTLL_ULPI_SCRATCH_REGISTER_CLR(i) (0x0818 + (0x100 * (i))) +#define OMAP_USBTLL_ULPI_EXTENDED_SET_ACCESS(i) (0x082F + (0x100 * (i))) +#define OMAP_USBTLL_ULPI_UTMI_VCONTROL_EN(i) (0x0830 + (0x100 * (i))) +#define OMAP_USBTLL_ULPI_UTMI_VCONTROL_EN_SET(i) (0x0831 + (0x100 * (i))) +#define OMAP_USBTLL_ULPI_UTMI_VCONTROL_EN_CLR(i) (0x0832 + (0x100 * (i))) +#define OMAP_USBTLL_ULPI_UTMI_VCONTROL_STATUS(i) (0x0833 + (0x100 * (i))) +#define OMAP_USBTLL_ULPI_UTMI_VCONTROL_LATCH(i) (0x0834 + (0x100 * (i))) +#define OMAP_USBTLL_ULPI_UTMI_VSTATUS(i) (0x0835 + (0x100 * (i))) +#define OMAP_USBTLL_ULPI_UTMI_VSTATUS_SET(i) (0x0836 + (0x100 * (i))) +#define OMAP_USBTLL_ULPI_UTMI_VSTATUS_CLR(i) (0x0837 + (0x100 * (i))) +#define OMAP_USBTLL_ULPI_USB_INT_LATCH_NOCLR(i) (0x0838 + (0x100 * (i))) +#define OMAP_USBTLL_ULPI_VENDOR_INT_EN(i) (0x083B + (0x100 * (i))) +#define OMAP_USBTLL_ULPI_VENDOR_INT_EN_SET(i) (0x083C + (0x100 * (i))) +#define OMAP_USBTLL_ULPI_VENDOR_INT_EN_CLR(i) (0x083D + (0x100 * (i))) +#define OMAP_USBTLL_ULPI_VENDOR_INT_STATUS(i) (0x083E + (0x100 * (i))) +#define OMAP_USBTLL_ULPI_VENDOR_INT_LATCH(i) (0x083F + (0x100 * (i))) + + +/* + * USB Host Module + */ + +/* UHH */ +#define OMAP_USBHOST_UHH_REVISION 0x0000 +#define OMAP_USBHOST_UHH_SYSCONFIG 0x0010 +#define OMAP_USBHOST_UHH_SYSSTATUS 0x0014 +#define OMAP_USBHOST_UHH_HOSTCONFIG 0x0040 +#define OMAP_USBHOST_UHH_DEBUG_CSR 0x0044 + +/* EHCI */ +#define OMAP_USBHOST_HCCAPBASE 0x0000 +#define OMAP_USBHOST_HCSPARAMS 0x0004 +#define OMAP_USBHOST_HCCPARAMS 0x0008 +#define OMAP_USBHOST_USBCMD 0x0010 +#define OMAP_USBHOST_USBSTS 0x0014 +#define OMAP_USBHOST_USBINTR 0x0018 +#define OMAP_USBHOST_FRINDEX 0x001C +#define OMAP_USBHOST_CTRLDSSEGMENT 0x0020 +#define OMAP_USBHOST_PERIODICLISTBASE 0x0024 +#define OMAP_USBHOST_ASYNCLISTADDR 0x0028 +#define OMAP_USBHOST_CONFIGFLAG 0x0050 +#define OMAP_USBHOST_PORTSC(i) (0x0054 + (0x04 * (i))) +#define OMAP_USBHOST_INSNREG00 0x0090 +#define OMAP_USBHOST_INSNREG01 0x0094 +#define OMAP_USBHOST_INSNREG02 0x0098 +#define OMAP_USBHOST_INSNREG03 0x009C +#define OMAP_USBHOST_INSNREG04 0x00A0 +#define OMAP_USBHOST_INSNREG05_UTMI 0x00A4 +#define OMAP_USBHOST_INSNREG05_ULPI 0x00A4 +#define OMAP_USBHOST_INSNREG06 0x00A8 +#define OMAP_USBHOST_INSNREG07 0x00AC +#define OMAP_USBHOST_INSNREG08 0x00B0 + +#define OMAP_USBHOST_INSNREG04_DISABLE_UNSUSPEND (1 << 5) + +#define OMAP_USBHOST_INSNREG05_ULPI_CONTROL_SHIFT 31 +#define OMAP_USBHOST_INSNREG05_ULPI_PORTSEL_SHIFT 24 +#define OMAP_USBHOST_INSNREG05_ULPI_OPSEL_SHIFT 22 +#define OMAP_USBHOST_INSNREG05_ULPI_REGADD_SHIFT 16 +#define OMAP_USBHOST_INSNREG05_ULPI_EXTREGADD_SHIFT 8 +#define OMAP_USBHOST_INSNREG05_ULPI_WRDATA_SHIFT 0 + + + + + +/* TLL Register Set */ +#define TLL_SYSCONFIG_CACTIVITY (1UL << 8) +#define TLL_SYSCONFIG_SIDLE_SMART_IDLE (2UL << 3) +#define TLL_SYSCONFIG_SIDLE_NO_IDLE (1UL << 3) +#define TLL_SYSCONFIG_SIDLE_FORCED_IDLE (0UL << 3) +#define TLL_SYSCONFIG_ENAWAKEUP (1UL << 2) +#define TLL_SYSCONFIG_SOFTRESET (1UL << 1) +#define TLL_SYSCONFIG_AUTOIDLE (1UL << 0) + +#define TLL_SYSSTATUS_RESETDONE (1UL << 0) + +#define TLL_SHARED_CONF_USB_90D_DDR_EN (1UL << 6) +#define TLL_SHARED_CONF_USB_180D_SDR_EN (1UL << 5) +#define TLL_SHARED_CONF_USB_DIVRATIO_MASK (7UL << 2) +#define TLL_SHARED_CONF_USB_DIVRATIO_128 (7UL << 2) +#define TLL_SHARED_CONF_USB_DIVRATIO_64 (6UL << 2) +#define TLL_SHARED_CONF_USB_DIVRATIO_32 (5UL << 2) +#define TLL_SHARED_CONF_USB_DIVRATIO_16 (4UL << 2) +#define TLL_SHARED_CONF_USB_DIVRATIO_8 (3UL << 2) +#define TLL_SHARED_CONF_USB_DIVRATIO_4 (2UL << 2) +#define TLL_SHARED_CONF_USB_DIVRATIO_2 (1UL << 2) +#define TLL_SHARED_CONF_USB_DIVRATIO_1 (0UL << 2) +#define TLL_SHARED_CONF_FCLK_REQ (1UL << 1) +#define TLL_SHARED_CONF_FCLK_IS_ON (1UL << 0) + +#define TLL_CHANNEL_CONF_DRVVBUS (1UL << 16) +#define TLL_CHANNEL_CONF_CHRGVBUS (1UL << 15) +#define TLL_CHANNEL_CONF_ULPINOBITSTUFF (1UL << 11) +#define TLL_CHANNEL_CONF_ULPIAUTOIDLE (1UL << 10) +#define TLL_CHANNEL_CONF_UTMIAUTOIDLE (1UL << 9) +#define TLL_CHANNEL_CONF_ULPIDDRMODE (1UL << 8) +#define TLL_CHANNEL_CONF_ULPIOUTCLKMODE (1UL << 7) +#define TLL_CHANNEL_CONF_TLLFULLSPEED (1UL << 6) +#define TLL_CHANNEL_CONF_TLLCONNECT (1UL << 5) +#define TLL_CHANNEL_CONF_TLLATTACH (1UL << 4) +#define TLL_CHANNEL_CONF_UTMIISADEV (1UL << 3) +#define TLL_CHANNEL_CONF_CHANEN (1UL << 0) + + +/* UHH Register Set */ +#define UHH_SYSCONFIG_MIDLEMODE_MASK (3UL << 12) +#define UHH_SYSCONFIG_MIDLEMODE_SMARTSTANDBY (2UL << 12) +#define UHH_SYSCONFIG_MIDLEMODE_NOSTANDBY (1UL << 12) +#define UHH_SYSCONFIG_MIDLEMODE_FORCESTANDBY (0UL << 12) +#define UHH_SYSCONFIG_CLOCKACTIVITY (1UL << 8) +#define UHH_SYSCONFIG_SIDLEMODE_MASK (3UL << 3) +#define UHH_SYSCONFIG_SIDLEMODE_SMARTIDLE (2UL << 3) +#define UHH_SYSCONFIG_SIDLEMODE_NOIDLE (1UL << 3) +#define UHH_SYSCONFIG_SIDLEMODE_FORCEIDLE (0UL << 3) +#define UHH_SYSCONFIG_ENAWAKEUP (1UL << 2) +#define UHH_SYSCONFIG_SOFTRESET (1UL << 1) +#define UHH_SYSCONFIG_AUTOIDLE (1UL << 0) + +#define UHH_HOSTCONFIG_APP_START_CLK (1UL << 31) +#define UHH_HOSTCONFIG_P3_CONNECT_STATUS (1UL << 10) +#define UHH_HOSTCONFIG_P2_CONNECT_STATUS (1UL << 9) +#define UHH_HOSTCONFIG_P1_CONNECT_STATUS (1UL << 8) +#define UHH_HOSTCONFIG_ENA_INCR_ALIGN (1UL << 5) +#define UHH_HOSTCONFIG_ENA_INCR16 (1UL << 4) +#define UHH_HOSTCONFIG_ENA_INCR8 (1UL << 3) +#define UHH_HOSTCONFIG_ENA_INCR4 (1UL << 2) +#define UHH_HOSTCONFIG_AUTOPPD_ON_OVERCUR_EN (1UL << 1) +#define UHH_HOSTCONFIG_P1_ULPI_BYPASS (1UL << 0) + +/* The following are on rev2 (OMAP44xx) of the EHCI only */ +#define UHH_SYSCONFIG_IDLEMODE_MASK (3UL << 2) +#define UHH_SYSCONFIG_IDLEMODE_NOIDLE (1UL << 2) +#define UHH_SYSCONFIG_STANDBYMODE_MASK (3UL << 4) +#define UHH_SYSCONFIG_STANDBYMODE_NOSTDBY (1UL << 4) + +#define UHH_HOSTCONFIG_P1_MODE_MASK (3UL << 16) +#define UHH_HOSTCONFIG_P1_MODE_ULPI_PHY (0UL << 16) +#define UHH_HOSTCONFIG_P1_MODE_UTMI_PHY (1UL << 16) +#define UHH_HOSTCONFIG_P1_MODE_HSIC (3UL << 16) +#define UHH_HOSTCONFIG_P2_MODE_MASK (3UL << 18) +#define UHH_HOSTCONFIG_P2_MODE_ULPI_PHY (0UL << 18) +#define UHH_HOSTCONFIG_P2_MODE_UTMI_PHY (1UL << 18) +#define UHH_HOSTCONFIG_P2_MODE_HSIC (3UL << 18) + +#define ULPI_FUNC_CTRL_RESET (1 << 5) + +/*-------------------------------------------------------------------------*/ + +/* + * Macros for Set and Clear + * See ULPI 1.1 specification to find the registers with Set and Clear offsets + */ +#define ULPI_SET(a) (a + 1) +#define ULPI_CLR(a) (a + 2) + +/*-------------------------------------------------------------------------*/ + +/* + * Register Map + */ +#define ULPI_VENDOR_ID_LOW 0x00 +#define ULPI_VENDOR_ID_HIGH 0x01 +#define ULPI_PRODUCT_ID_LOW 0x02 +#define ULPI_PRODUCT_ID_HIGH 0x03 +#define ULPI_FUNC_CTRL 0x04 +#define ULPI_IFC_CTRL 0x07 +#define ULPI_OTG_CTRL 0x0a +#define ULPI_USB_INT_EN_RISE 0x0d +#define ULPI_USB_INT_EN_FALL 0x10 +#define ULPI_USB_INT_STS 0x13 +#define ULPI_USB_INT_LATCH 0x14 +#define ULPI_DEBUG 0x15 +#define ULPI_SCRATCH 0x16 + +/* + * Values of UHH_REVISION - Note: these are not given in the TRM but taken + * from the linux OMAP EHCI driver (thanks guys). It has been verified on + * a Panda and Beagle board. + */ +#define OMAP_EHCI_REV1 0x00000010 /* OMAP3 */ +#define OMAP_EHCI_REV2 0x50700100 /* OMAP4 */ + +#define EHCI_VENDORID_OMAP3 0x42fa05 +#define OMAP_EHCI_HC_DEVSTR "TI OMAP USB 2.0 controller" + +#define EHCI_HCD_OMAP_MODE_UNKNOWN 0 +#define EHCI_HCD_OMAP_MODE_PHY 1 +#define EHCI_HCD_OMAP_MODE_TLL 2 +#define EHCI_HCD_OMAP_MODE_HSIC 3 + +#endif /* _OMAP_USB_H_ */ diff --git a/sys/boot/fdt/dts/beaglebone.dts b/sys/boot/fdt/dts/beaglebone.dts new file mode 100644 index 000000000000..2c68021e6db8 --- /dev/null +++ b/sys/boot/fdt/dts/beaglebone.dts @@ -0,0 +1,197 @@ +/*- + * 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. + * + * $FreeBSD$ + */ + +/dts-v1/; + +/ { + model = "beaglebone"; + compatible = "beaglebone", "ti,am335x"; + #address-cells = <1>; + #size-cells = <1>; + + interrupt-parent = <&AINTC>; + + aliases { + soc = &SOC; + uart0 = &uart0; + }; + + memory { + device_type = "memory"; + reg = < 0x80000000 0x10000000 >; /* 256MB RAM */ + }; + + SOC: am335x { + #address-cells = <1>; + #size-cells = <1>; + compatible = "simple-bus"; + ranges; + bus-frequency = <0>; + + AINTC: interrupt-controller@48200000 { + compatible = "ti,aintc"; + interrupt-controller; + #address-cells = <0>; + #interrupt-cells = <1>; + reg = < 0x48200000 0x1000 >; + }; + + scm@44e10000 { + compatible = "ti,scm"; + reg = < 0x44e10000 0x2000 >; + /* Set of triplets < padname, muxname, padstate> */ + scm-pad-config = + /* I2C0 */ + "I2C0_SDA", "I2C0_SDA","i2c", + "I2C0_SCL", "I2C0_SCL","i2c", + /* Ethernet */ + "MII1_RX_ER", "gmii1_rxerr", "input_pulldown", + "MII1_TX_EN", "gmii1_txen", "output", + "MII1_RX_DV", "gmii1_rxdv", "input_pulldown", + "MII1_TXD3", "gmii1_txd3", "output", + "MII1_TXD2", "gmii1_txd2", "output", + "MII1_TXD1", "gmii1_txd1", "output", + "MII1_TXD0", "gmii1_txd0", "output", + "MII1_TX_CLK", "gmii1_txclk", "input_pulldown", + "MII1_RX_CLK", "gmii1_rxclk", "input_pulldown", + "MII1_RXD3", "gmii1_rxd3", "input_pulldown", + "MII1_RXD2", "gmii1_rxd2", "input_pulldown", + "MII1_RXD1", "gmii1_rxd1", "input_pulldown", + "MII1_RXD0", "gmii1_rxd0", "input_pulldown", + "MDIO", "mdio_data", "input_pullup", + "MDC", "mdio_clk", "output_pullup", + /* MMCSD0 */ + "MMC0_CMD", "mmc0_cmd", "input_pullup", + "MMC0_CLK", "mmc0_clk", "input_pullup", + "MMC0_DAT0", "mmc0_dat0", "input_pullup", + "MMC0_DAT1", "mmc0_dat1", "input_pullup", + "MMC0_DAT2", "mmc0_dat2", "input_pullup", + "MMC0_DAT3", "mmc0_dat3", "input_pullup"; + }; + + prcm@44E00000 { + compatible = "am335x,prcm"; + #address-cells = <1>; + #size-cells = <1>; + reg = < 0x44E00000 0x1300 >; + }; + + dmtimers@44E05000 { + compatible = "ti,am335x-dmtimer"; + #address-cells = <1>; + #size-cells = <1>; + reg = < 0x44E05000 0x1000 + 0x44E31000 0x1000 + 0x48040000 0x1000 + 0x48042000 0x1000 + 0x48044000 0x1000 + 0x48046000 0x1000 + 0x48048000 0x1000 + 0x4804A000 0x1000 >; + interrupts = < 66 67 68 69 92 93 94 95 >; + interrupt-parent = <&AINTC>; + }; + + GPIO: gpio { + #gpio-cells = <3>; + compatible = "ti,gpio"; + gpio-controller; + reg =< 0x44E07000 0x1000 + 0x4804C000 0x1000 + 0x481AC000 0x1000 + 0x481AE000 0x1000 >; + interrupts = < 17 19 21 23 >; + interrupt-parent = <&AINTC>; + }; + + + uart0: serial@44E09000 { + compatible = "ns16550"; + reg = <0x44E09000 0x1000>; + reg-shift = <2>; + interrupts = < 72 >; + interrupt-parent = <&AINTC>; + clock-frequency = < 48000000 >; /* FIXME */ + }; + + edma3@49000000 { + compatible = "ti,edma3"; + reg =< 0x49000000 0x100000 /* Channel Controller Regs */ + 0x49800000 0x100000 /* Transfer Controller 0 Regs */ + 0x49900000 0x100000 /* Transfer Controller 1 Regs */ + 0x49a00000 0x100000 >; /* Transfer Controller 2 Regs */ + interrupts = <12 13 14>; + interrupt-parent = <&AINTC>; + }; + + mmchs0@4809C000 { + compatible = "ti,mmchs"; + reg =<0x48060000 0x1000 >; + interrupts = <64>; + interrupt-parent = <&AINTC>; + mmchs-device-id = <0>; + }; + + enet0: ethernet@4A100000 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "ti,cpsw"; + reg = <0x4A100000 0x3000>; + interrupts = <40 41 42 43>; + interrupt-parent = <&AINTC>; + phy-handle = <&phy0>; + mdio@0 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "ti,cpsw-mdio"; + phy0: ethernet-phy@0 { + reg = <0x0>; + }; + }; + }; + + i2c0: i2c@44e0b000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "ti,i2c"; + reg =< 0x44e0b000 0x1000 >; + interrupts = <70>; + interrupt-parent = <&AINTC>; + i2c-device-id = <0>; + pmic@24 { + compatible = "ti,am335x-pmic"; + reg = <0x24>; + }; + }; + }; + + chosen { + stdin = "uart0"; + stdout = "uart0"; + }; +}; diff --git a/sys/boot/fdt/dts/pandaboard.dts b/sys/boot/fdt/dts/pandaboard.dts new file mode 100644 index 000000000000..0f262b1be5b4 --- /dev/null +++ b/sys/boot/fdt/dts/pandaboard.dts @@ -0,0 +1,184 @@ +/*- + * Copyright (c) 2011 The FreeBSD Foundation + * All rights reserved. + * + * Developed by Damjan Marion + * + * 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$ + */ + +/dts-v1/; + +/ { + model = "pandaboard"; + compatible = "pandaboard", "ti,omap4430"; + #address-cells = <1>; + #size-cells = <1>; + + interrupt-parent = <&GIC>; + + aliases { + soc = &SOC; + uart3 = &uart3; + }; + + memory { + device_type = "memory"; + reg = < 0x80000000 0x40000000 >; /* 1GB RAM at 0x0 */ + }; + + SOC: omap4430 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "simple-bus"; + ranges; + bus-frequency = <0>; + + GIC: interrupt-controller@48241000 { + compatible = "arm,gic"; + interrupt-controller; + #address-cells = <0>; + #interrupt-cells = <1>; + reg = < 0x48241000 0x1000 >, /* Distributor Registers */ + < 0x48240100 0x0100 >; /* CPU Interface Registers */ + }; + + pl310@48242000 { + compatible = "arm,pl310"; + reg = < 0x48242000 0x1000 >; + }; + mp_tmr@48240200 { + compatible = "arm,mpcore-timers"; + clock-frequency = < 504000000 >; + #address-cells = <1>; + #size-cells = <0>; + reg = < 0x48240200 0x100 >, /* Global Timer Registers */ + < 0x48240600 0x100 >; /* Private Timer Registers */ + interrupts = < 27 29 >; + interrupt-parent = < &GIC >; + }; + + uart3: serial@48020000 { + compatible = "ns16550"; + reg = <0x48020000 0x1000>; + reg-shift = <2>; + interrupts = < 106 >; + interrupt-parent = <&GIC>; + clock-frequency = < 48000000 >; /* 48Mhz clock for all uarts */ + /* (techref 17.3.1.1) */ + }; + + scm@4a100000 { + compatible = "ti,scm"; + reg = < 0x4a100000 0x1000 >; + /* Set of triplets < padname, muxname, padstate> */ + scm-pad-config = + "ag19", "usbb1_ulpiphy_stp", "output", + "ae18", "usbb1_ulpiphy_clk", "input_pulldown", + "af19", "usbb1_ulpiphy_dir", "input_pulldown", + "ae19", "usbb1_ulpiphy_nxt", "input_pulldown", + "af18", "usbb1_ulpiphy_dat0", "input_pulldown", + "ag18", "usbb1_ulpiphy_dat1", "input_pulldown", + "ae17", "usbb1_ulpiphy_dat2", "input_pulldown", + "af17", "usbb1_ulpiphy_dat3", "input_pulldown", + "ah17", "usbb1_ulpiphy_dat4", "input_pulldown", + "ae16", "usbb1_ulpiphy_dat5", "input_pulldown", + "af16", "usbb1_ulpiphy_dat6", "input_pulldown", + "ag16", "usbb1_ulpiphy_dat7", "input_pulldown"; + }; + + omap4_prcm@4a306000 { + compatible = "ti,omap4_prcm"; + reg =< 0x4a306000 0x2000 + 0x4a004000 0x1000 + 0x4a008000 0x8000>; + }; + + GPIO: gpio { + #gpio-cells = <3>; + compatible = "ti,gpio"; + gpio-controller; + reg =< 0x4a310000 0x1000 + 0x48055000 0x1000 + 0x48057000 0x1000 + 0x48059000 0x1000 + 0x4805b000 0x1000 + 0x4805d000 0x1000>; + interrupts = <61 62 63 64 65 66>; + interrupt-parent = <&GIC>; + }; + + ehci { + compatible = "ti,usb-ehci", "usb-ehci"; + /* + * USB port PHY configuration is a tuple: + * mode is one of the following values: + * 0 - unknown + * 1 - PHY + * 2 - TLL + * 3 - HSIC + * + * reset indicates (if non-zero) if port reset is required + * gpio_pin - GPIO pin that is used to perform reset + */ + phy-config = < 1 0 0 + 0 0 0 + 0 0 0>; + reg = < 0x4a064c00 0x100 /* EHCI regs */ + 0x4a064000 0x700 /* UHH regs */ + 0x4a062000 0x1000>; /* TLL regs */ + interrupts = <109>; + interrupt-parent = <&GIC>; + }; + + I2C1: i2c@x48070000 { + compatible = "ti,i2c"; + reg =< 0x48070000 0x100 >; + interrupts = <88>; + interrupt-parent = <&GIC>; + i2c-device-id = <1>; + }; + + sdma@x48070000 { + compatible = "ti,sdma"; + reg =< 0x4A056000 0x1000 >; + interrupts = <44 45 46 47>; + interrupt-parent = <&GIC>; + }; + + mmc@x4809C000 { + compatible = "ti,mmchs"; + reg =<0x4809C000 0x1000 >; + interrupts = <115>; + interrupt-parent = <&GIC>; + mmchs-device-id = <1>; + }; + + }; + + chosen { + stdin = "uart3"; + stdout = "uart3"; + }; +}; diff --git a/sys/dev/mmc/mmc.c b/sys/dev/mmc/mmc.c index 77be79ab79be..a63d50154d6f 100644 --- a/sys/dev/mmc/mmc.c +++ b/sys/dev/mmc/mmc.c @@ -1730,5 +1730,6 @@ static driver_t mmc_driver = { }; static devclass_t mmc_devclass; +DRIVER_MODULE(mmc, ti_mmchs, mmc_driver, mmc_devclass, NULL, NULL); DRIVER_MODULE(mmc, at91_mci, mmc_driver, mmc_devclass, NULL, NULL); DRIVER_MODULE(mmc, sdhci, mmc_driver, mmc_devclass, NULL, NULL); diff --git a/sys/modules/Makefile b/sys/modules/Makefile index 9922d62814c0..ee135884984b 100644 --- a/sys/modules/Makefile +++ b/sys/modules/Makefile @@ -72,6 +72,7 @@ SUBDIR= \ ${_coff} \ ${_coretemp} \ ${_cp} \ + ${_cpsw} \ ${_cpuctl} \ ${_cpufreq} \ ${_crypto} \ @@ -735,6 +736,7 @@ _zfs= zfs .if ${MACHINE_CPUARCH} == "arm" _cfi= cfi +_cpsw= cpsw .endif .if ${MACHINE_CPUARCH} == "ia64" diff --git a/sys/modules/cpsw/Makefile b/sys/modules/cpsw/Makefile new file mode 100644 index 000000000000..508fb0389965 --- /dev/null +++ b/sys/modules/cpsw/Makefile @@ -0,0 +1,8 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../../arm/ti/cpsw + +KMOD= if_cpsw +SRCS= if_cpsw.c device_if.h bus_if.h ofw_bus_if.h miibus_if.h + +.include