diff --git a/sys/arm/conf/PANDABOARD b/sys/arm/conf/PANDABOARD new file mode 100644 index 0000000..9b32a71 --- /dev/null +++ b/sys/arm/conf/PANDABOARD @@ -0,0 +1,131 @@ +# 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 "../omap/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 ALT_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 omap3_i2c + +device loop +device ether +device mii +device smc +device smcphy +device uart +device uart_ns8250 + +device gpio + +device pty + +# 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 + + diff --git a/sys/arm/conf/PANDABOARD.hints b/sys/arm/conf/PANDABOARD.hints new file mode 100644 index 0000000..911ae18 --- /dev/null +++ b/sys/arm/conf/PANDABOARD.hints @@ -0,0 +1,64 @@ +# $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.omap_scm.0.padconf.ag19="usbb1_ulpiphy_stp:output" +hint.omap_scm.0.padconf.ae18="usbb1_ulpiphy_clk:input_pulldown" +hint.omap_scm.0.padconf.af19="usbb1_ulpiphy_dir:input_pulldown" +hint.omap_scm.0.padconf.ae19="usbb1_ulpiphy_nxt:input_pulldown" +hint.omap_scm.0.padconf.af18="usbb1_ulpiphy_dat0:input_pulldown" +hint.omap_scm.0.padconf.ag18="usbb1_ulpiphy_dat1:input_pulldown" +hint.omap_scm.0.padconf.ae17="usbb1_ulpiphy_dat2:input_pulldown" +hint.omap_scm.0.padconf.af17="usbb1_ulpiphy_dat3:input_pulldown" +hint.omap_scm.0.padconf.ah17="usbb1_ulpiphy_dat4:input_pulldown" +hint.omap_scm.0.padconf.ae16="usbb1_ulpiphy_dat5:input_pulldown" +hint.omap_scm.0.padconf.af16="usbb1_ulpiphy_dat6:input_pulldown" +hint.omap_scm.0.padconf.ag16="usbb1_ulpiphy_dat7:input_pulldown" + diff --git a/sys/arm/include/cpuconf.h b/sys/arm/include/cpuconf.h index 14c196e..c0311ab 100644 --- a/sys/arm/include/cpuconf.h +++ b/sys/arm/include/cpuconf.h @@ -94,6 +94,8 @@ #define ARM_ARCH_7A 0 #endif +#define ARM_ARCH_6 0 + #define ARM_NARCH (ARM_ARCH_4 + ARM_ARCH_5 + ARM_ARCH_6 | ARM_ARCH_7A) #if ARM_NARCH == 0 && !defined(KLD_MODULE) && defined(_KERNEL) #error ARM_NARCH is 0 diff --git a/sys/arm/omap/omap.c b/sys/arm/omap/omap.c new file mode 100644 index 0000000..15ed30c --- /dev/null +++ b/sys/arm/omap/omap.c @@ -0,0 +1,575 @@ +/* + * 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. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + + + +struct omap_softc *g_omap_softc = NULL; + +static int omap_probe(device_t); +static void omap_identify(driver_t *, device_t); +static int omap_attach(device_t); + +extern const struct pmap_devmap omap_devmap[]; + + + +/** + * cpu_reset + * + * Called by the system to reset the CPU - obvious really + * + */ +void +cpu_reset() +{ + omap_prcm_reset(); + printf("Reset failed!\n"); + while (1); +} + + + + +/* Proto types for all the bus_space structure functions */ +bs_protos(generic); +bs_protos(generic_armv7); + +struct bus_space omap_bs_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_armv7_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_armv7_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_armv7_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_armv7_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_armv7_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_armv7_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_armv7_bs_sr_2, + .bs_sr_4 = generic_bs_sr_4, + .bs_sr_8 = NULL, + + /* copy */ + .bs_c_1 = NULL, + .bs_c_2 = generic_armv7_bs_c_2, + .bs_c_4 = NULL, + .bs_c_8 = NULL, +}; + + + + + + +/** + * omap_identify + * + * + * + */ +static void +omap_identify(driver_t *driver, device_t parent) +{ + BUS_ADD_CHILD(parent, 0, "omap", 0); +} + +/** + * omap_probe - driver probe function + * @dev: the root device + * + * Simply sets the name of this base driver + * + */ +static int +omap_probe(device_t dev) +{ + printf("[BRG] %s, %d\n", __FUNCTION__, __LINE__); + + device_set_desc(dev, "TI OMAP"); + return (0); +} + + + + +/** + * omap_attach + * + * + * + */ +static int +omap_attach(device_t dev) +{ + struct omap_softc *sc = device_get_softc(dev); + const struct pmap_devmap *pdevmap; + + printf("[BRG] %s, %d\n", __FUNCTION__, __LINE__); + + g_omap_softc = sc; + sc->sc_iotag = &omap_bs_tag; + sc->sc_dev = dev; + + + /* Set all interrupts as the resource */ + sc->sc_irq_rman.rm_type = RMAN_ARRAY; + sc->sc_irq_rman.rm_descr = "OMAP IRQs"; + printf("[BRG] %s, %d : NIRQ = %d\n", __FUNCTION__, __LINE__, NIRQ); + if (rman_init(&sc->sc_irq_rman) != 0 || + rman_manage_region(&sc->sc_irq_rman, 0, NIRQ) != 0) { + panic("%s: failed to set up IRQ rman", __func__); + } + + /* Setup the memory map based on initial device map in *_machdep.c */ + sc->sc_mem_rman.rm_type = RMAN_ARRAY; + sc->sc_mem_rman.rm_descr = "OMAP Memory"; + + if (rman_init(&sc->sc_mem_rman) != 0) + panic("%s: failed to set up memory rman", __func__); + + for (pdevmap = omap_devmap; pdevmap->pd_va != 0; pdevmap++) { + if (rman_manage_region(&sc->sc_mem_rman, pdevmap->pd_pa, + pdevmap->pd_pa + pdevmap->pd_size - 1) != 0) { + panic("%s: failed to set up memory regions", __func__); + } + } + + + /* The device list will be created by the 'cpu' device when it is identified */ + + bus_generic_probe(dev); + bus_generic_attach(dev); + enable_interrupts(I32_bit | F32_bit); + return (0); +} + + + +/** + * omap_devmap_phys2virt + * + * This function will be called when bus_alloc_resource(...) if the memory + * region requested is in the range of the managed values set by + * rman_manage_region(...) above. + * + * For SYS_RES_MEMORY resource types the omap_attach() calls rman_manage_region + * with the list of pyshical mappings defined in the omap_devmap region map. + * However because we are working with physical addresses, we need to convert + * the physical to virtual within this function and return the virtual address + * in the bus tag field. + * + */ +static u_long +omap_devmap_phys2virt(u_long pa) +{ + const struct pmap_devmap *pdevmap; + + for (pdevmap = omap_devmap; pdevmap->pd_va != 0; pdevmap++) { + if ((pa >= pdevmap->pd_pa) && (pa < (pdevmap->pd_pa + pdevmap->pd_size))) { + return (pdevmap->pd_va + (pa - pdevmap->pd_pa)); + } + } + + panic("%s: failed to find addr mapping for 0x%08lx", __func__, pa); + return (0); +} + + +/** + * omap_alloc_resource + * + * This function will be called when bus_alloc_resource(...) if the memory + * region requested is in the range of the managed values set by + * rman_manage_region(...) above. + * + * For SYS_RES_MEMORY resource types the omap_attach() calls rman_manage_region + * with the list of pyshical mappings defined in the omap_devmap region map. + * However because we are working with physical addresses, we need to convert + * the physical to virtual within this function and return the virtual address + * in the bus tag field. + * + */ +static struct resource * +omap_alloc_resource(device_t dev, device_t child, int type, int *rid, + u_long start, u_long end, u_long count, u_int flags) +{ + struct omap_softc *sc = device_get_softc(dev); + struct resource_list_entry *rle; + struct omap_ivar *ivar = device_get_ivars(child); + struct resource_list *rl = &ivar->resources; + + /* If we aren't the parent pass it onto the actual parent */ + if (device_get_parent(child) != dev) { + return (BUS_ALLOC_RESOURCE(device_get_parent(dev), child, + type, rid, start, end, count, flags)); + } + + /* Find the resource in the list */ + rle = resource_list_find(rl, type, *rid); + if (rle == NULL) + return (NULL); + if (rle->res) + panic("Resource rid %d type %d already in use", *rid, type); + + if (start == 0UL && end == ~0UL) { + start = rle->start; + count = ulmax(count, rle->count); + end = ulmax(rle->end, start + count - 1); + } + + switch (type) + { + case SYS_RES_IRQ: + rle->res = rman_reserve_resource(&sc->sc_irq_rman, + start, end, count, flags, child); + break; + case SYS_RES_MEMORY: + rle->res = rman_reserve_resource(&sc->sc_mem_rman, + start, end, count, flags, child); + if (rle->res != NULL) { + rman_set_bustag(rle->res, &omap_bs_tag); + rman_set_bushandle(rle->res, omap_devmap_phys2virt(start)); + } + break; + } + + if (rle->res) { + rle->start = rman_get_start(rle->res); + rle->end = rman_get_end(rle->res); + rle->count = count; + rman_set_rid(rle->res, *rid); + } + return (rle->res); +} + + +/** + * omap_get_resource_list + * + * + * + */ +static struct resource_list * +omap_get_resource_list(device_t dev, device_t child) +{ + struct omap_ivar *ivar; + + ivar = device_get_ivars(child); + return (&(ivar->resources)); +} + + + +/** + * omap_release_resource + * + * + * + */ +static int +omap_release_resource(device_t dev, device_t child, int type, int rid, + struct resource *r) +{ + struct resource_list *rl; + struct resource_list_entry *rle; + + rl = omap_get_resource_list(dev, child); + if (rl == NULL) + return (EINVAL); + + rle = resource_list_find(rl, type, rid); + if (rle == NULL) + return (EINVAL); + + rman_release_resource(r); + rle->res = NULL; + return (0); +} + + +/** + * omap_activate_resource + * + * + * + */ +static int +omap_activate_resource(device_t dev, device_t child, int type, int rid, + struct resource *r) +{ + printf("%s : %d\n", __func__, __LINE__); + +#if 0 + struct omap3_softc *sc = device_get_softc(dev); + const struct hwvtrans *vtrans; + uint32_t start = rman_get_start(r); + uint32_t size = rman_get_size(r); + + if (type == SYS_RES_MEMORY) { + vtrans = omap3_gethwvtrans(start, size); + if (vtrans == NULL) { /* NB: should not happen */ + device_printf(child, "%s: no mapping for 0x%lx:0x%lx\n", + __func__, start, size); + return (ENOENT); + } + rman_set_bustag(r, sc->sc_iot); + rman_set_bushandle(r, vtrans->vbase + (start - vtrans->hwbase)); + } +#endif + return (rman_activate_resource(r)); +} + + +/** + * omap_deactivate_resource + * + * + * + */ +static int +omap_deactivate_resource(device_t bus, device_t child, int type, int rid, + struct resource *r) +{ + /* NB: no private resources, just deactive */ + return (rman_deactivate_resource(r)); +} + + + +/** + * omap_print_child + * + * + * + */ +static int +omap_print_child(device_t dev, device_t child) +{ + struct omap_ivar *ivars; + struct resource_list *rl; + int retval = 0; + + ivars = device_get_ivars(child); + rl = &ivars->resources; + + retval += bus_print_child_header(dev, child); + + retval += resource_list_print_type(rl, "port", SYS_RES_IOPORT, "%#lx"); + retval += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#lx"); + retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld"); + if (device_get_flags(dev)) + retval += printf(" flags %#x", device_get_flags(dev)); + + retval += bus_print_child_footer(dev, child); + + return (retval); +} + + + + +/** + * omap_setup_intr - initialises and unmasks the IRQ. + * @dev: + * @child: + * @res: + * @flags: + * @filt: + * @intr: + * @arg: + * @cookiep: + * + * + * + * + * RETURNS: + * 0 on success + */ +int +omap_setup_intr(device_t dev, device_t child, + struct resource *res, int flags, driver_filter_t *filt, + driver_intr_t *intr, void *arg, void **cookiep) +{ + unsigned int i; + + /* Macro .. does something ? */ + BUS_SETUP_INTR(device_get_parent(dev), child, res, flags, filt, intr, + arg, cookiep); + + /* Enable all the interrupts in the range ... will probably be only one */ + for (i = rman_get_start(res); (i < NIRQ) && (i <= rman_get_end(res)); i++) { + arm_unmask_irq(i); + } + + return (0); +} + +/** + * omap_teardown_intr - + * @dev: + * @child: + * @res: + * @cookie: + * + * + * + * + * RETURNS: + * 0 on success + */ +int +omap_teardown_intr(device_t dev, device_t child, struct resource *res, + void *cookie) +{ + unsigned int i; + + /* Mask (disable) all the interrupts in the range ... will probably be only one */ + for (i = rman_get_start(res); (i < NIRQ) && (i <= rman_get_end(res)); i++) { + arm_mask_irq(i); + } + + return (BUS_TEARDOWN_INTR(device_get_parent(dev), child, res, cookie)); +} + + + +static device_method_t omap_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, omap_probe), + DEVMETHOD(device_attach, omap_attach), + DEVMETHOD(device_identify, omap_identify), + + /* Bus interface */ + DEVMETHOD(bus_alloc_resource, omap_alloc_resource), + DEVMETHOD(bus_activate_resource, omap_activate_resource), + DEVMETHOD(bus_deactivate_resource, omap_deactivate_resource), + DEVMETHOD(bus_get_resource_list, omap_get_resource_list), + DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource), + DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), + DEVMETHOD(bus_release_resource, omap_release_resource), + + DEVMETHOD(bus_setup_intr, omap_setup_intr), + DEVMETHOD(bus_teardown_intr, omap_teardown_intr), + + DEVMETHOD(bus_print_child, omap_print_child), + + {0, 0}, +}; + +static driver_t omap_driver = { + "omap", + omap_methods, + sizeof(struct omap_softc), +}; +static devclass_t omap_devclass; + +DRIVER_MODULE(omap, nexus, omap_driver, omap_devclass, 0, 0); + + + + + + diff --git a/sys/arm/omap/omap3/files.omap35xx b/sys/arm/omap/omap3/files.omap35xx new file mode 100644 index 0000000..2e790cc --- /dev/null +++ b/sys/arm/omap/omap3/files.omap35xx @@ -0,0 +1,36 @@ +#$FreeBSD$ + +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/omap/omap_machdep.c standard + +arm/omap/omap.c standard +arm/omap/omap_cpuid.c standard +arm/omap/omap_dma.c standard +arm/omap/omap_prcm.c standard +arm/omap/omap_scm.c standard +arm/omap/omap_gpio.c optional gpio +arm/omap/omap_gptimer.c standard +arm/omap/omap_mmc.c optional mmc +arm/omap/omap_space_asm.S standard +arm/omap/omap_i2c.c optional iic + +arm/omap/omap3/omap35xx.c standard +arm/omap/omap3/omap3_intr.c standard +arm/omap/omap3/omap3_prcm_clks.c standard +arm/omap/omap3/omap3_scm_padconf.c standard +arm/omap/omap3/omap3_timer.c standard + +arm/omap/omap3/uart_cpu_omap3.c optional uart + +dev/uart/uart_dev_ns8250.c optional uart + +# USB Host controller +arm/omap/omap_ehci.c optional ehci usb + diff --git a/sys/arm/omap/omap3/omap35xx.c b/sys/arm/omap/omap3/omap35xx.c new file mode 100644 index 0000000..9fd2dea --- /dev/null +++ b/sys/arm/omap/omap3/omap35xx.c @@ -0,0 +1,490 @@ +/*- + * 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 author may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 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 + +#define _ARM32_BUS_DMA_PRIVATE +#include + +#include +#include +#include + +struct omap3_softc *g_omap3_softc = NULL; + + +/* + * Standard priority levels for the system - 0 has the highest priority and 63 + * is the lowest. + * + * Currently these are all set to the same standard value. + */ +static const int omap35xx_irq_prio[96] = +{ + 0, /* MPU emulation(2) */ + 0, /* MPU emulation(2) */ + 0, /* MPU emulation(2) */ + 0, /* MPU emulation(2) */ + 0, /* Sidetone MCBSP2 overflow */ + 0, /* Sidetone MCBSP3 overflow */ + 0, /* MPU subsystem secure state-machine abort (2) */ + 0, /* External source (active low) */ + 0, /* RESERVED */ + 0, /* SMX error for debug */ + 0, /* SMX error for application */ + 0, /* PRCM module IRQ */ + 0, /* System DMA request 0(3) */ + 0, /* System DMA request 1(3) */ + 0, /* System DMA request 2 */ + 0, /* System DMA request 3 */ + 0, /* McBSP module 1 IRQ (3) */ + 0, /* McBSP module 2 IRQ (3) */ + 0, /* SmartReflex™ 1 */ + 0, /* SmartReflex™ 2 */ + 0, /* General-purpose memory controller module */ + 0, /* 2D/3D graphics module */ + 0, /* McBSP module 3(3) */ + 0, /* McBSP module 4(3) */ + 0, /* Camera interface request 0 */ + 0, /* Display subsystem module(3) */ + 0, /* Mailbox user 0 request */ + 0, /* McBSP module 5 (3) */ + 0, /* IVA2 MMU */ + 0, /* GPIO module 1(3) */ + 0, /* GPIO module 2(3) */ + 0, /* GPIO module 3(3) */ + 0, /* GPIO module 4(3) */ + 0, /* GPIO module 5(3) */ + 0, /* GPIO module 6(3) */ + 0, /* USIM interrupt (HS devices only) (4) */ + 0, /* Watchdog timer module 3 overflow */ + 0, /* General-purpose timer module 1 */ + 0, /* General-purpose timer module 2 */ + 0, /* General-purpose timer module 3 */ + 0, /* General-purpose timer module 4 */ + 0, /* General-purpose timer module 5(3) */ + 0, /* General-purpose timer module 6(3) */ + 0, /* General-purpose timer module 7(3) */ + 0, /* General-purpose timer module 8(3) */ + 0, /* General-purpose timer module 9 */ + 0, /* General-purpose timer module 10 */ + 0, /* General-purpose timer module 11 */ + 0, /* McSPI module 4 */ + 0, /* SHA-1/MD5 crypto-accelerator 2 (HS devices only)(4) */ + 0, /* PKA crypto-accelerator (HS devices only) (4) */ + 0, /* SHA-2/MD5 crypto-accelerator 1 (HS devices only) (4) */ + 0, /* RNG module (HS devices only) (4) */ + 0, /* MG function (3) */ + 0, /* McBSP module 4 transmit(3) */ + 0, /* McBSP module 4 receive(3) */ + 0, /* I2C module 1 */ + 0, /* I2C module 2 */ + 0, /* HDQ / One-wire */ + 0, /* McBSP module 1 transmit(3) */ + 0, /* McBSP module 1 receive(3) */ + 0, /* I2C module 3 */ + 0, /* McBSP module 2 transmit(3) */ + 0, /* McBSP module 2 receive(3) */ + 0, /* PKA crypto-accelerator (HS devices only) (4) */ + 0, /* McSPI module 1 */ + 0, /* McSPI module 2 */ + 0, /* RESERVED */ + 0, /* RESERVED */ + 0, /* RESERVED */ + 0, /* RESERVED */ + 0, /* RESERVED */ + 0, /* UART module 1 */ + 0, /* UART module 2 */ + 0, /* UART module 3 (also infrared)(3) */ + 0, /* Merged interrupt for PBIASlite1 and 2 */ + 0, /* OHCI controller HSUSB MP Host Interrupt */ + 0, /* EHCI controller HSUSB MP Host Interrupt */ + 0, /* HSUSB MP TLL Interrupt */ + 0, /* SHA2/MD5 crypto-accelerator 1 (HS devices only) (4) */ + 0, /* Reserved */ + 0, /* McBSP module 5 transmit(3) */ + 0, /* McBSP module 5 receive(3) */ + 0, /* MMC/SD module 1 */ + 0, /* MS-PRO™ module */ + 0, /* Reserved */ + 0, /* MMC/SD module 2 */ + 0, /* MPU ICR */ + 0, /* RESERVED */ + 0, /* McBSP module 3 transmit(3) */ + 0, /* McBSP module 3 receive(3) */ + 0, /* McSPI module 3 */ + 0, /* High-Speed USB OTG controller */ + 0, /* High-Speed USB OTG DMA controller */ + 0, /* MMC/SD module 3 */ + 0, /* General-purpose timer module 12 */ +}; + + +static const struct omap_cpu_dev omap35xx_devs[] = +{ + /** + * OMAP35xx - General Purpose Timers + * This module provides interfaces to the general purpose timers. + */ + { .name = "omap_gptimer", + .unit = 0, + .mem = { { OMAP35XX_GPTIMER1_HWBASE, OMAP35XX_GPTIMER_SIZE }, + { OMAP35XX_GPTIMER2_HWBASE, OMAP35XX_GPTIMER_SIZE }, + { OMAP35XX_GPTIMER3_HWBASE, OMAP35XX_GPTIMER_SIZE }, + { OMAP35XX_GPTIMER4_HWBASE, OMAP35XX_GPTIMER_SIZE }, + { OMAP35XX_GPTIMER5_HWBASE, OMAP35XX_GPTIMER_SIZE }, + { OMAP35XX_GPTIMER6_HWBASE, OMAP35XX_GPTIMER_SIZE }, + { OMAP35XX_GPTIMER7_HWBASE, OMAP35XX_GPTIMER_SIZE }, + { OMAP35XX_GPTIMER8_HWBASE, OMAP35XX_GPTIMER_SIZE }, + { OMAP35XX_GPTIMER9_HWBASE, OMAP35XX_GPTIMER_SIZE }, + { OMAP35XX_GPTIMER10_HWBASE, OMAP35XX_GPTIMER_SIZE }, + { OMAP35XX_GPTIMER11_HWBASE, OMAP35XX_GPTIMER_SIZE }, + { 0, 0 } + }, + .irqs = { OMAP35XX_IRQ_GPT1, + OMAP35XX_IRQ_GPT2, + OMAP35XX_IRQ_GPT3, + OMAP35XX_IRQ_GPT4, + OMAP35XX_IRQ_GPT5, + OMAP35XX_IRQ_GPT6, + OMAP35XX_IRQ_GPT7, + OMAP35XX_IRQ_GPT8, + OMAP35XX_IRQ_GPT9, + OMAP35XX_IRQ_GPT10, + OMAP35XX_IRQ_GPT11, + -1, + }, + }, + + /** + * OMAP35xx - DMA + * This module provides interfaces to the direct memory access controller + */ + { .name = "omap_dma", + .unit = 0, + .mem = { { OMAP35XX_SDMA_HWBASE, OMAP35XX_SDMA_SIZE }, + { 0, 0 } + }, + .irqs = { OMAP35XX_IRQ_SDMA0, + OMAP35XX_IRQ_SDMA1, + OMAP35XX_IRQ_SDMA2, + OMAP35XX_IRQ_SDMA3, + -1, + }, + }, + + /** + * OMAP35xx - GPIO + * There are 6 GPIO register sets, with each set representing 32 GPIO + * pins. + */ + { .name = "gpio", + .unit = 0, + .mem = { { OMAP35XX_GPIO1_HWBASE, OMAP35XX_GPIO1_SIZE }, + { OMAP35XX_GPIO2_HWBASE, OMAP35XX_GPIO2_SIZE }, + { OMAP35XX_GPIO3_HWBASE, OMAP35XX_GPIO3_SIZE }, + { OMAP35XX_GPIO4_HWBASE, OMAP35XX_GPIO4_SIZE }, + { OMAP35XX_GPIO5_HWBASE, OMAP35XX_GPIO5_SIZE }, + { OMAP35XX_GPIO6_HWBASE, OMAP35XX_GPIO6_SIZE }, + { 0, 0 } + }, + .irqs = { OMAP35XX_IRQ_GPIO1_MPU, + OMAP35XX_IRQ_GPIO2_MPU, + OMAP35XX_IRQ_GPIO3_MPU, + OMAP35XX_IRQ_GPIO4_MPU, + OMAP35XX_IRQ_GPIO5_MPU, + OMAP35XX_IRQ_GPIO6_MPU, + -1, + }, + }, + + /** + * OMAP35xx - MMC/SDIO + * There are a total of 3 MMC modules on OMAP3 + */ + { .name = "omap_mmc", + .unit = 0, + .mem = { { OMAP35XX_MMCHS1_HWBASE, OMAP35XX_MMCHS_SIZE }, + { 0, 0 } + }, + .irqs = { OMAP35XX_IRQ_MMC1, + -1, + }, + }, + + /** + * OMAP35xx - USB EHCI + * The OMAP EHCI driver expects three different register sets, one for + * the actual EHCI registers and the other two control the interface. + */ + { .name = "ehci", + .unit = 0, + .mem = { { OMAP35XX_USB_EHCI_HWBASE, OMAP35XX_USB_EHCI_SIZE }, + { OMAP35XX_USB_UHH_HWBASE, OMAP35XX_USB_UHH_SIZE }, + { OMAP35XX_USB_TLL_HWBASE, OMAP35XX_USB_TLL_SIZE }, + { 0, 0 } + }, + .irqs = { OMAP35XX_IRQ_EHCI, + -1, + }, + }, + + /** + * OMAP35xx - I2C + * This module provides interfaces to the I2C controller + */ + { .name = "omap_i2c", + .unit = 0, + .mem = { { OMAP35XX_I2C1_HWBASE, OMAP35XX_I2C1_SIZE }, + { 0, 0 } + }, + .irqs = { OMAP35XX_IRQ_I2C1, + -1, + }, + }, + + { 0, 0, { { 0, 0 } }, { -1 } } +}; + + + +/** + * omap_sdram_size - called from machdep to get the total memory size + * + * Since this function is called very early in the boot, there is none of the + * bus handling code setup. However the boot device map will be setup, so + * we can directly access registers already mapped. + * + * This is a bit ugly, but since we need this information early on and the + * only way to get it (appart from hardcoding it or via kernel args) is to read + * from the EMIF_SRAM registers. + * + * RETURNS: + * The size of memory in bytes. + */ +unsigned int +omap_sdram_size(void) +{ + uint32_t size; + uint32_t sdrc_mcfg_0, sdrc_mcfg_1; + + sdrc_mcfg_0 = *((volatile uint32_t *)(OMAP35XX_SDRC_MCFG(0))); + sdrc_mcfg_1 = *((volatile uint32_t *)(OMAP35XX_SDRC_MCFG(1))); + + /* The size is given in bits 17:8 in 2MB chunk sizes */ + size = ((sdrc_mcfg_0 >> 8) & 0x3FF) * (2 * 1024 * 1024); + size += ((sdrc_mcfg_1 >> 8) & 0x3FF) * (2 * 1024 * 1024); + +printf("[BRG] omap_sdram_size : size = %u\n", size); + + return (size); +} + + + + +/** + * omap35xx_add_child - add a child item to the root omap device + * @dev: the parent device + * @order: defines roughly the order with which to add the child to the parent + * @name: the name to give to the child item + * @unit: the unit number for the device + * @addr: the base address of the register set for device + * @size: the number of a bytes in register set + * @irq: the IRQ number(s) for the device + * + * Adds a child to the omap base device. + * + */ +static void +omap35xx_add_child(device_t dev, int prio, const char *name, int unit, + const struct omap_mem_range mem[], const int irqs[]) +{ + device_t kid; + struct omap_ivar *ivar; + unsigned int i; + + /* Start by adding the actual child to the parent (us) */ + kid = device_add_child_ordered(dev, prio, name, unit); + if (kid == NULL) { + printf("Can't add child %s%d ordered\n", name, unit); + return; + } + + /* Allocate some memory for the omap_ivar structure */ + ivar = malloc(sizeof(*ivar), M_DEVBUF, M_NOWAIT | M_ZERO); + if (ivar == NULL) { + device_delete_child(dev, kid); + printf("Can't add alloc ivar\n"); + return; + } + + /* Assign the ivars to the child item and populate with the device resources */ + device_set_ivars(kid, ivar); + + /* Assign the IRQ(s) in the resource list */ + resource_list_init(&ivar->resources); + if (irqs) { + for (i = 0; *irqs != -1; i++, irqs++) { + bus_set_resource(kid, SYS_RES_IRQ, i, *irqs, 1); + } + } + + /* Assign the memory region to the resource list */ + if (mem) { + for (i = 0; mem->base != 0; i++, mem++) { + bus_set_resource(kid, SYS_RES_MEMORY, i, mem->base, mem->size); + } + } +} + + +/** + * omap35xx_cpu_add_builtin_children - adds the SoC child components + * @dev: this device, the one we are adding to + * + * Adds child devices from the omap35xx_devs list. + * + */ +static void +omap35xx_cpu_add_builtin_children(device_t dev) +{ + int i; + const struct omap_cpu_dev *walker; + + /* Setup all the clock devices - this is not the tick timers, rather it's + * the individual functional and interface clocks for the SoC modules. + */ + omap3_clk_init(dev, 1); + + /* Setup the system control module driver, which basically is just the + * padconf (pinmux) for the OMAP35xx devices. + */ + omap3_padconf_init(dev, 1); + + /* Add the rest of the children from the array above */ + for (i = 5, walker = omap35xx_devs; walker->name; i++, walker++) { + omap35xx_add_child(dev, i, walker->name, walker->unit, + walker->mem, walker->irqs); + } +} + + +/** + * omap35xx_identify - adds the SoC child components + * @dev: this device, the one we are adding to + * + * Adds a child to the omap3 base device. + * + */ +static void +omap35xx_identify(driver_t *drv, device_t parent) +{ + /* Add the resources for this "omap35xx" device */ + omap35xx_add_child(parent, 0, "omap35xx", 0, NULL, NULL); + + /* Add the other SOC components */ + omap35xx_cpu_add_builtin_children(parent); +} + +/** + * omap35xx_probe - called when the device is probed + * @dev: the new device + * + * All we do in this routine is set the description of this device + * + */ +static int +omap35xx_probe(device_t dev) +{ + device_set_desc(dev, "TI OMAP35XX"); + return (0); +} + +/** + * omap35xx_attach - called when the device is attached + * @dev: the new device + * + * All we do in this routine is set the description of this device + * + */ +static int +omap35xx_attach(device_t dev) +{ + struct omap_softc *omapsc = device_get_softc(device_get_parent(dev)); + struct omap3_softc *sc = device_get_softc(dev); + + sc->sc_iotag = omapsc->sc_iotag; + sc->sc_dev = dev; + + + /* Map in the interrupt controller register set */ + if (bus_space_map(sc->sc_iotag, OMAP35XX_INTCPS_HWBASE, + OMAP35XX_INTCPS_SIZE, 0, &sc->sc_intcps_ioh)) { + panic("%s: Cannot map registers", device_get_name(dev)); + } + + + /* Save the device structure globally for the IRQ handling */ + g_omap3_softc = sc; + + /* TODO: Revisit - Install an interrupt post filter */ + arm_post_filter = omap3_post_filter_intr; + + /* Setup the OMAP3 interrupt controller */ + omap3_setup_intr_controller(g_omap3_softc, omap35xx_irq_prio); + + return (0); +} + + + +static device_method_t omap35xx_methods[] = { + DEVMETHOD(device_probe, omap35xx_probe), + DEVMETHOD(device_attach, omap35xx_attach), + DEVMETHOD(device_identify, omap35xx_identify), + {0, 0}, +}; + +static driver_t omap35xx_driver = { + "omap35xx", + omap35xx_methods, + sizeof(struct omap3_softc), +}; + +static devclass_t omap35xx_devclass; + +DRIVER_MODULE(omap35xx, omap, omap35xx_driver, omap35xx_devclass, 0, 0); diff --git a/sys/arm/omap/omap3/omap35xx_reg.h b/sys/arm/omap/omap3/omap35xx_reg.h new file mode 100644 index 0000000..7407a8f --- /dev/null +++ b/sys/arm/omap/omap3/omap35xx_reg.h @@ -0,0 +1,779 @@ +/*- + * 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 author may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 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. + */ + +/* + * 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/omap/omap3/omap3_intr.c b/sys/arm/omap/omap3/omap3_intr.c new file mode 100644 index 0000000..1f1431d --- /dev/null +++ b/sys/arm/omap/omap3/omap3_intr.c @@ -0,0 +1,234 @@ +/*- + * 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 author may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 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 + + + +/* + * There are a number of ways that interrupt handling is implemented in + * the various ARM platforms, the PXA has the neatest way, it creates another + * device driver that handles everything. However IMO this is rather heavy- + * weight for playing with IRQs which should be quite fast ... so I've + * gone for something similar to the IXP425, which just directly plays with + * registers. This assumes that the interrupt control registers are already + * mapped in virtual memory at a fixed virtual address ... simplies. + * + * The intcps (OMAP3 interrupt controller) has some nice registers, were + * you write a bit to clear or set the mask register ... I think in theory + * that means that you don't need to disable interrupts while doing this, + * because it is an atomic operation. + * + * TODO: check this. + * + */ + + + +#define INTCPS_SYSCONFIG 0x10 +#define INTCPS_SYSSTATUS 0x14 +#define INTCPS_SIR_IRQ 0x40 +#define INTCPS_SIR_FIQ 0x44 +#define INTCPS_CONTROL 0x48 +#define INTCPS_PROTECTION 0x4C +#define INTCPS_IDLE 0x50 +#define INTCPS_IRQ_PRIORITY 0x60 +#define INTCPS_FIQ_PRIORITY 0x64 +#define INTCPS_THRESHOLD 0x68 +#define INTCPS_ITR(n) (0x80 + (0x20 * (n))) +#define INTCPS_MIR(n) (0x84 + (0x20 * (n))) +#define INTCPS_MIR_CLEAR(n) (0x88 + (0x20 * (n))) +#define INTCPS_MIR_SET(n) (0x8C + (0x20 * (n))) +#define INTCPS_ISR_SET(n) (0x90 + (0x20 * (n))) +#define INTCPS_ISR_CLEAR(n) (0x94 + (0x20 * (n))) +#define INTCPS_PENDING_IRQ(n) (0x98 + (0x20 * (n))) +#define INTCPS_PENDING_FIQ(n) (0x9C + (0x20 * (n))) +#define INTCPS_ILR(m) (0x100 + (0x4 * (m))) + + + + +/** + * omap3_post_filter_intr - called after the IRQ has been filtered + * @arg: the IRQ number + * + * Called after the interrupt handler has done it's stuff, can be used to + * clean up interrupts that haven't been handled properly. + * + * + * RETURNS: + * nothing + */ +void +omap3_post_filter_intr(void *arg) +{ + /* uintptr_t irq = (uintptr_t) arg; */ + + /* data synchronization barrier */ + cpu_drain_writebuf(); +} + + + + + +/** + * arm_mask_irq - masks an IRQ (disables it) + * @nb: the number of the IRQ to mask (disable) + * + * Disables the interrupt at the HW level. + * + * + * RETURNS: + * nothing + */ +void +arm_mask_irq(uintptr_t nb) +{ + bus_space_write_4(g_omap3_softc->sc_iotag, g_omap3_softc->sc_intcps_ioh, + INTCPS_MIR_SET(nb >> 5), 1UL << (nb & 0x1F)); +} + + +/** + * arm_unmask_irq - unmasks an IRQ (enables it) + * @nb: the number of the IRQ to unmask (enable) + * + * Enables the interrupt at the HW level. + * + * + * RETURNS: + * nothing + */ +void +arm_unmask_irq(uintptr_t nb) +{ + // printf("[BRG] unmasking IRQ %d (off %d, bit %d)\n", nb, (nb >> 5), (nb & 0x1F)); + + bus_space_write_4(g_omap3_softc->sc_iotag, g_omap3_softc->sc_intcps_ioh, + INTCPS_MIR_CLEAR(nb >> 5), 1UL << (nb & 0x1F)); +} + + + +/** + * arm_get_next_irq - gets the next tripped interrupt + * @last_irq: the number of the last IRQ processed + * + * Enables the interrupt at the HW level. + * + * + * RETURNS: + * nothing + */ +int +arm_get_next_irq(int last_irq) +{ + uint32_t active_irq; + + /* clean-up the last IRQ */ + if (last_irq != -1) { + + /* clear the interrupt status flag */ + bus_space_write_4(g_omap3_softc->sc_iotag, g_omap3_softc->sc_intcps_ioh, + INTCPS_ISR_CLEAR(last_irq >> 5), + 1UL << (last_irq & 0x1F)); + + /* tell the interrupt logic we've dealt with the interrupt */ + bus_space_write_4(g_omap3_softc->sc_iotag, g_omap3_softc->sc_intcps_ioh, + INTCPS_CONTROL, 1); + } + + /* Get the next active interrupt */ + active_irq = bus_space_read_4(g_omap3_softc->sc_iotag, + g_omap3_softc->sc_intcps_ioh, INTCPS_SIR_IRQ); + + /* Check for spurious interrupt */ + if ((active_irq & 0xffffff80) == 0xffffff80) { + device_printf(g_omap3_softc->sc_dev, "Spurious interrupt detected " + "(0x%08x)\n", active_irq); + return -1; + } + + /* Just get the active IRQ part */ + active_irq &= 0x7F; + + /* Return the new IRQ if it is different from the previous */ + if (active_irq != last_irq) + return active_irq; + else + return -1; +} + + +/** + * omap3_setup_intr_controller - configures and enables the OMAP3 interrupt + * controller (INTCPS) + * + * + * + * RETURNS: + * nothing + */ +int +omap3_setup_intr_controller(struct omap3_softc *sc, const const int *irqs) +{ + uint32_t syscfg; + uint32_t i; + + if (sc != g_omap3_softc) + panic("Invalid omap3 soft context\n"); + + + /* Reset the interrupt controller */ + bus_space_write_4(g_omap3_softc->sc_iotag, g_omap3_softc->sc_intcps_ioh, + INTCPS_SYSCONFIG, 0x2); + + /* Loop a number of times to check if the INTCPS has come out of reset */ + for (i = 0; i < 10000; i++) { + syscfg = bus_space_read_4(g_omap3_softc->sc_iotag, + g_omap3_softc->sc_intcps_ioh, INTCPS_SYSCONFIG); + if (syscfg & 0x1UL) + break; + } + + + + return 0; +} + diff --git a/sys/arm/omap/omap3/omap3_prcm_clks.c b/sys/arm/omap/omap3/omap3_prcm_clks.c new file mode 100644 index 0000000..6f4973a --- /dev/null +++ b/sys/arm/omap/omap3/omap3_prcm_clks.c @@ -0,0 +1,1197 @@ +/*- + * 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 author may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 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 +#include +#include +#include +#include + +#include +#include +#include + + + +/* + * This file defines the clock configuration for the OMAP3xxx 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 OMAP35xx 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 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. + * + * + * + * + */ + + +void +omap3_clk_init(device_t dev, int prio); + +static int +omap3_clk_generic_activate(const struct omap_clock_dev *clkdev, + struct resource* mem_res[]); + +static int +omap3_clk_generic_deactivate(const struct omap_clock_dev *clkdev, + struct resource* mem_res[]); + +static int +omap3_clk_generic_accessible(const struct omap_clock_dev *clkdev, + struct resource* mem_res[]); + +static int +omap3_clk_generic_set_source(const struct omap_clock_dev *clkdev, clk_src_t clksrc, + struct resource* mem_res[]); + +static int +omap3_clk_generic_get_source_freq(const struct omap_clock_dev *clkdev, + unsigned int *freq, + struct resource* mem_res[]); + + +static int +omap3_clk_gptimer_get_source_freq(const struct omap_clock_dev *clkdev, + unsigned int *freq, + struct resource* mem_res[]); +static int +omap3_clk_gptimer_set_source(const struct omap_clock_dev *clkdev, + clk_src_t clksrc, struct resource* mem_res[]); + + + +static int +omap3_clk_alwayson_null_func(const struct omap_clock_dev *clkdev, + struct resource* mem_res[]); + + + +static int +omap3_clk_get_sysclk_freq(const struct omap_clock_dev *clkdev, + unsigned int *freq, struct resource* mem_res[]); + +static int +omap3_clk_get_arm_fclk_freq(const struct omap_clock_dev *clkdev, + unsigned int *freq, struct resource* mem_res[]); + + + +static int +omap3_clk_hsusbhost_activate(const struct omap_clock_dev *clkdev, + struct resource* mem_res[]); + +static int +omap3_clk_hsusbhost_deactivate(const struct omap_clock_dev *clkdev, + struct resource* mem_res[]); + + + + +#define FREQ_96MHZ 96000000 +#define FREQ_64MHZ 64000000 +#define FREQ_48MHZ 48000000 +#define FREQ_32KHZ 32000 + + + +/** + * Only one memory regions is needed for OMAP35xx clock control (unlike OMAP4) + * + * CM Instance - 0x4800 4000 : 0x4800 5500 + * PRM Instance - 0x4830 6000 : 0x4830 8000 + * + */ +#define CM_INSTANCE_MEM_REGION 0 +#define PRM_INSTANCE_MEM_REGION 1 + +#define IVA2_CM_OFFSET 0x0000 +#define OCP_SYSTEM_CM_OFFSET 0x0800 +#define MPU_CM_OFFSET 0x0900 +#define CORE_CM_OFFSET 0x0A00 +#define SGX_CM_OFFSET 0x0B00 +#define WKUP_CM_OFFSET 0x0C00 +#define CLOCK_CTRL_CM_OFFSET 0x0D00 +#define DSS_CM_OFFSET 0x0E00 +#define CAM_CM_OFFSET 0x0F00 +#define PER_CM_OFFSET 0x1000 +#define EMU_CM_OFFSET 0x1100 +#define GLOBAL_CM_OFFSET 0x1200 +#define NEON_CM_OFFSET 0x1300 +#define USBHOST_CM_OFFSET 0x1400 + +#define IVA2_PRM_OFFSET 0x0000 +#define OCP_SYSTEM_PRM_OFFSET 0x0800 +#define MPU_PRM_OFFSET 0x0900 +#define CORE_PRM_OFFSET 0x0A00 +#define SGX_PRM_OFFSET 0x0B00 +#define WKUP_PRM_OFFSET 0x0C00 +#define CLOCK_CTRL_PRM_OFFSET 0x0D00 +#define DSS_PRM_OFFSET 0x0E00 +#define CAM_PRM_OFFSET 0x0F00 +#define PER_PRM_OFFSET 0x1000 +#define EMU_PRM_OFFSET 0x1100 +#define GLOBAL_PRM_OFFSET 0x1200 +#define NEON_PRM_OFFSET 0x1300 +#define USBHOST_PRM_OFFSET 0x1400 + + + + + + +/** + * omap_clk_devmap - Array of clock devices available on OMAP3xxx 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 OMAP3_GENERIC_CLOCK_DEV(i) \ + { .id = (i), \ + .clk_activate = omap3_clk_generic_activate, \ + .clk_deactivate = omap3_clk_generic_deactivate, \ + .clk_set_source = omap3_clk_generic_set_source, \ + .clk_accessible = omap3_clk_generic_accessible, \ + .clk_get_source_freq = omap3_clk_generic_get_source_freq \ + } + +#define OMAP3_GPTIMER_CLOCK_DEV(i) \ + { .id = (i), \ + .clk_activate = omap3_clk_generic_activate, \ + .clk_deactivate = omap3_clk_generic_deactivate, \ + .clk_set_source = omap3_clk_gptimer_set_source, \ + .clk_accessible = omap3_clk_generic_accessible, \ + .clk_get_source_freq = omap3_clk_gptimer_get_source_freq \ + } + +#define OMAP3_ALWAYSON_CLOCK_DEV(i) \ + { .id = (i), \ + .clk_activate = omap3_clk_alwayson_null_func, \ + .clk_deactivate = omap3_clk_alwayson_null_func, \ + .clk_set_source = NULL, \ + .clk_accessible = omap3_clk_alwayson_null_func, \ + .clk_get_source_freq = NULL \ + } + +#define OMAP3_HSUSBHOST_CLOCK_DEV(i) \ + { .id = (i), \ + .clk_activate = omap3_clk_hsusbhost_activate, \ + .clk_deactivate = omap3_clk_hsusbhost_deactivate, \ + .clk_set_source = NULL, \ + .clk_accessible = omap3_clk_generic_accessible, \ + .clk_get_source_freq = NULL \ + } + + +const struct omap_clock_dev omap_clk_devmap[] = { + + /* System clock */ + { .id = SYS_CLK, + .clk_activate = NULL, + .clk_deactivate = NULL, + .clk_set_source = NULL, + .clk_accessible = NULL, + .clk_get_source_freq = omap3_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 = omap3_clk_get_arm_fclk_freq, + }, + + + /* UART device clocks */ + OMAP3_GENERIC_CLOCK_DEV(UART1_CLK), + OMAP3_GENERIC_CLOCK_DEV(UART2_CLK), + OMAP3_GENERIC_CLOCK_DEV(UART3_CLK), + OMAP3_GENERIC_CLOCK_DEV(UART4_CLK), + + /* Timer device source clocks */ + OMAP3_GPTIMER_CLOCK_DEV(GPTIMER1_CLK), + OMAP3_GPTIMER_CLOCK_DEV(GPTIMER2_CLK), + OMAP3_GPTIMER_CLOCK_DEV(GPTIMER3_CLK), + OMAP3_GPTIMER_CLOCK_DEV(GPTIMER4_CLK), + OMAP3_GPTIMER_CLOCK_DEV(GPTIMER5_CLK), + OMAP3_GPTIMER_CLOCK_DEV(GPTIMER6_CLK), + OMAP3_GPTIMER_CLOCK_DEV(GPTIMER7_CLK), + OMAP3_GPTIMER_CLOCK_DEV(GPTIMER8_CLK), + OMAP3_GPTIMER_CLOCK_DEV(GPTIMER9_CLK), + OMAP3_GPTIMER_CLOCK_DEV(GPTIMER10_CLK), + OMAP3_GPTIMER_CLOCK_DEV(GPTIMER11_CLK), + + /* MMC device clocks (MMC1 and MMC2 can have different input clocks) */ + OMAP3_GENERIC_CLOCK_DEV(MMC1_CLK), + OMAP3_GENERIC_CLOCK_DEV(MMC2_CLK), + OMAP3_GENERIC_CLOCK_DEV(MMC3_CLK), + + /* USB HS (high speed TLL, EHCI and OHCI) */ + OMAP3_GENERIC_CLOCK_DEV(USBTLL_CLK), + OMAP3_HSUSBHOST_CLOCK_DEV(USBHSHOST_CLK), + + /* GPIO */ + OMAP3_GENERIC_CLOCK_DEV(GPIO1_CLK), + OMAP3_GENERIC_CLOCK_DEV(GPIO2_CLK), + OMAP3_GENERIC_CLOCK_DEV(GPIO3_CLK), + OMAP3_GENERIC_CLOCK_DEV(GPIO4_CLK), + OMAP3_GENERIC_CLOCK_DEV(GPIO5_CLK), + OMAP3_GENERIC_CLOCK_DEV(GPIO6_CLK), + + /* I2C */ + OMAP3_GENERIC_CLOCK_DEV(I2C1_CLK), + OMAP3_GENERIC_CLOCK_DEV(I2C2_CLK), + OMAP3_GENERIC_CLOCK_DEV(I2C3_CLK), + + /* sDMA */ + OMAP3_ALWAYSON_CLOCK_DEV(SDMA_CLK), + + { INVALID_CLK_IDENT, NULL, NULL, NULL, NULL } +}; + + + + + + +/** + * g_omap3_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 omap3_clk_details { + clk_ident_t id; + int32_t src_freq; + + /* The register offset from the CM module register region of the registers*/ + uint32_t fclken_offset; + uint32_t iclken_offset; + uint32_t idlest_offset; + + /* The bit offset for the clock */ + uint32_t bit_offset; +}; + +#define OMAP3_GENERIC_CLOCK_DETAILS(d, freq, base, fclk, iclk, idlest, bit) \ + { .id = (d), \ + .src_freq = (freq), \ + .fclken_offset = ((base) + (fclk)), \ + .iclken_offset = ((base) + (iclk)), \ + .idlest_offset = ((base) + (idlest)), \ + .bit_offset = (bit), \ + } + +static const struct omap3_clk_details g_omap3_clk_details[] = { + + /* UART */ + OMAP3_GENERIC_CLOCK_DETAILS(UART1_CLK, FREQ_48MHZ, CORE_CM_OFFSET, + 0x00, 0x10, 0x20, 13), + OMAP3_GENERIC_CLOCK_DETAILS(UART2_CLK, FREQ_48MHZ, CORE_CM_OFFSET, + 0x00, 0x10, 0x20, 14), + OMAP3_GENERIC_CLOCK_DETAILS(UART3_CLK, FREQ_48MHZ, PER_CM_OFFSET, + 0x00, 0x10, 0x20, 11), + + /* General purpose timers */ + OMAP3_GENERIC_CLOCK_DETAILS(GPTIMER1_CLK, -1, WKUP_CM_OFFSET, + 0x00, 0x10, 0x20, 3), + OMAP3_GENERIC_CLOCK_DETAILS(GPTIMER2_CLK, -1, PER_CM_OFFSET, + 0x00, 0x10, 0x20, 3), + OMAP3_GENERIC_CLOCK_DETAILS(GPTIMER3_CLK, -1, PER_CM_OFFSET, + 0x00, 0x10, 0x20, 4), + OMAP3_GENERIC_CLOCK_DETAILS(GPTIMER4_CLK, -1, PER_CM_OFFSET, + 0x00, 0x10, 0x20, 5), + OMAP3_GENERIC_CLOCK_DETAILS(GPTIMER5_CLK, -1, PER_CM_OFFSET, + 0x00, 0x10, 0x20, 6), + OMAP3_GENERIC_CLOCK_DETAILS(GPTIMER6_CLK, -1, PER_CM_OFFSET, + 0x00, 0x10, 0x20, 7), + OMAP3_GENERIC_CLOCK_DETAILS(GPTIMER7_CLK, -1, PER_CM_OFFSET, + 0x00, 0x10, 0x20, 8), + OMAP3_GENERIC_CLOCK_DETAILS(GPTIMER8_CLK, -1, PER_CM_OFFSET, + 0x00, 0x10, 0x20, 9), + OMAP3_GENERIC_CLOCK_DETAILS(GPTIMER9_CLK, -1, PER_CM_OFFSET, + 0x00, 0x10, 0x20, 10), + OMAP3_GENERIC_CLOCK_DETAILS(GPTIMER10_CLK, -1, CORE_CM_OFFSET, + 0x00, 0x10, 0x20, 11), + OMAP3_GENERIC_CLOCK_DETAILS(GPTIMER11_CLK, -1, CORE_CM_OFFSET, + 0x00, 0x10, 0x20, 12), + + /* HSMMC (MMC1 and MMC2 can have different input clocks) */ + OMAP3_GENERIC_CLOCK_DETAILS(MMC1_CLK, FREQ_96MHZ, CORE_CM_OFFSET, + 0x00, 0x10, 0x20, 24), + OMAP3_GENERIC_CLOCK_DETAILS(MMC2_CLK, FREQ_96MHZ, CORE_CM_OFFSET, + 0x00, 0x10, 0x20, 25), + OMAP3_GENERIC_CLOCK_DETAILS(MMC3_CLK, FREQ_96MHZ, CORE_CM_OFFSET, + 0x00, 0x10, 0x20, 30), + + /* USB HS (high speed TLL, EHCI and OHCI) */ + OMAP3_GENERIC_CLOCK_DETAILS(USBTLL_CLK, -1, CORE_CM_OFFSET, + 0x08, 0x18, 0x28, 2), + OMAP3_GENERIC_CLOCK_DETAILS(USBHSHOST_CLK, -1, USBHOST_CM_OFFSET, + 0x00, 0x10, 0x20, 1), + + /* GPIO modules */ + OMAP3_GENERIC_CLOCK_DETAILS(GPIO1_CLK, -1, WKUP_CM_OFFSET, + 0x00, 0x10, 0x20, 3), + OMAP3_GENERIC_CLOCK_DETAILS(GPIO2_CLK, -1, PER_CM_OFFSET, + 0x00, 0x10, 0x20, 13), + OMAP3_GENERIC_CLOCK_DETAILS(GPIO3_CLK, -1, PER_CM_OFFSET, + 0x00, 0x10, 0x20, 14), + OMAP3_GENERIC_CLOCK_DETAILS(GPIO4_CLK, -1, PER_CM_OFFSET, + 0x00, 0x10, 0x20, 15), + OMAP3_GENERIC_CLOCK_DETAILS(GPIO5_CLK, -1, PER_CM_OFFSET, + 0x00, 0x10, 0x20, 16), + OMAP3_GENERIC_CLOCK_DETAILS(GPIO6_CLK, -1, PER_CM_OFFSET, + 0x00, 0x10, 0x20, 17), + + /* I2C modules */ + OMAP3_GENERIC_CLOCK_DETAILS(I2C1_CLK, -1, CORE_CM_OFFSET, + 0x00, 0x10, 0x20, 15), + OMAP3_GENERIC_CLOCK_DETAILS(I2C2_CLK, -1, CORE_CM_OFFSET, + 0x00, 0x10, 0x20, 16), + OMAP3_GENERIC_CLOCK_DETAILS(I2C3_CLK, -1, CORE_CM_OFFSET, + 0x00, 0x10, 0x20, 17), + + + { 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 1000 + + +/** + * ARRAY_SIZE - Macro to return the number of elements in a static const array. + * + */ +#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) + + + + +/** + * omap3_clk_wait_on_reg - loops for MAX_MODULE_ENABLE_WAIT times or until + * register bit(s) change. + * @mem_res: memory resource of the register to read + * @off: offset of the register within mem_res + * @mask: the mask to bitwise AND with the register + * @cmp: if this value matches the register value after the mask is applied + * the function returns with 0. + * + * + * RETURNS: + * Returns 0 on success or ETIMEDOUT on failure. + */ +static int +omap3_clk_wait_on_reg(struct resource* mem_res, bus_size_t off, uint32_t mask, + uint32_t cmp) +{ + unsigned int i; + for (i = 0; i < MAX_MODULE_ENABLE_WAIT; i++) { + if ((bus_read_4(mem_res, off) & mask) == cmp) + return (0); + } + + return (ETIMEDOUT); +} + + + + + +/** + * omap3_clk_details - returns a pointer to the generic clock details + * @id: The ID of the clock to get the details for + * + * This function iterates over the g_omap3_clk_details array and returns a + * pointer to the entry that matches the supplied ID, or NULL if no entry + * is found. + * + * RETURNS: + * Pointer to clock details or NULL on failure + */ +static const struct omap3_clk_details* +omap3_clk_details(clk_ident_t id) +{ + const struct omap3_clk_details *walker; + + for (walker = g_omap3_clk_details; walker->id != INVALID_CLK_IDENT; walker++) { + if (id == walker->id) + return (walker); + } + + return NULL; +} + + + + +/** + * omap3_clk_alwayson_null_func - dummy function for always on clocks + * @clkdev: pointer to the clock device structure (ignored) + * @mem_res: array of memory resources mapped when PRCM driver attached (ignored) + * + * + * + * 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 +omap3_clk_alwayson_null_func(const struct omap_clock_dev *clkdev, + struct resource* mem_res[]) +{ + return (0); +} + + + + + +/** + * omap3_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 +omap3_clk_get_sysclk_freq(const struct omap_clock_dev *clkdev, + unsigned int *freq, struct resource* mem_res[]) +{ + uint32_t clksel; + uint32_t clknsel; + unsigned int oscclk; + unsigned int sysclk; + + /* Read the input clock freq from the configuration register */ + clknsel = bus_read_4(mem_res[PRM_INSTANCE_MEM_REGION], CLOCK_CTRL_PRM_OFFSET + 0x40); + switch (clknsel & 0x7) { + case 0x0: + /* 12Mhz */ + oscclk = 12000000; + break; + case 0x1: + /* 13Mhz */ + oscclk = 13000000; + break; + case 0x2: + /* 19.2Mhz */ + oscclk = 19200000; + break; + case 0x3: + /* 26Mhz */ + oscclk = 26000000; + break; + case 0x4: + /* 38.4Mhz */ + oscclk = 38400000; + break; + case 0x5: + /* 16.8Mhz */ + oscclk = 16800000; + break; + default: + panic("%s: Invalid clock freq", __func__); + } + + /* Read the value of the clock divider used for the system clock */ + clksel = bus_read_4(mem_res[PRM_INSTANCE_MEM_REGION], GLOBAL_PRM_OFFSET + 0x70); + switch (clksel & 0xC0) { + case 0x40: + sysclk = oscclk; + break; + case 0x80: + sysclk = oscclk / 2; + break; + default: + panic("%s: Invalid system clock divider", __func__); + } + + /* Return the value */ + if (freq) + *freq = sysclk; + + return (0); +} + + + +/** + * omap3_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 +omap3_clk_get_arm_fclk_freq(const struct omap_clock_dev *clkdev, + unsigned int *freq, struct resource* mem_res[]) +{ + unsigned int sysclk; + unsigned int coreclk; + unsigned int mpuclk; + uint32_t clksel; + uint32_t clkout; + + + /* Get the SYSCLK freq */ + omap3_clk_get_sysclk_freq(clkdev, &sysclk, mem_res); + + + /* First get the freq of the CORE_CLK (feed from DPLL3) */ + clksel = bus_read_4(mem_res[CM_INSTANCE_MEM_REGION], CLOCK_CTRL_CM_OFFSET + 0x40); + clkout = (sysclk * ((clksel >> 16) & 0x7FF)) / (((clksel >> 8) & 0x7F) + 1); + coreclk = clkout / (clksel >> 27); + + + /* Next get the freq for the MPU_CLK */ + clksel = bus_read_4(mem_res[CM_INSTANCE_MEM_REGION], MPU_CM_OFFSET + 0x40); + mpuclk = (coreclk * ((clksel >> 8) & 0x7FF)) / ((clksel & 0x7F) + 1); + + + /* Return the value */ + if (freq) + *freq = mpuclk; + + return (0); +} + + + + + + + + +/** + * omap3_clk_generic_activate - activates a modules iinterface and func clock + * @clkdev: pointer to the clock device structure. + * @mem_res: array of memory resources mapped when PRCM driver attached + * + * + * + * 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 +omap3_clk_generic_activate(const struct omap_clock_dev *clkdev, + struct resource* mem_res[]) +{ + const struct omap3_clk_details* clk_details = omap3_clk_details(clkdev->id); + struct resource* clk_mem_res = mem_res[CM_INSTANCE_MEM_REGION]; + uint32_t fclken, iclken; + + if (clk_details == NULL) + return (ENXIO); + if (clk_mem_res == NULL) + return (ENOMEM); + + + /* All the 'generic' clocks have a FCLKEN, ICLKEN and IDLEST register which + * is for the functional, interface and clock status regsters respectively. + */ + + /* Enable the interface clock */ + iclken = bus_read_4(clk_mem_res, clk_details->iclken_offset); + iclken |= (1UL << clk_details->bit_offset); + bus_write_4(clk_mem_res, clk_details->iclken_offset, iclken); + + /* Read back the value to ensure the write has taken place ... needed ? */ + iclken = bus_read_4(clk_mem_res, clk_details->iclken_offset); + + + /* Enable the functional clock */ + fclken = bus_read_4(clk_mem_res, clk_details->fclken_offset); + fclken |= (1UL << clk_details->bit_offset); + bus_write_4(clk_mem_res, clk_details->fclken_offset, fclken); + + /* Read back the value to ensure the write has taken place ... needed ? */ + fclken = bus_read_4(clk_mem_res, clk_details->fclken_offset); + + + /* 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 */ + if (omap3_clk_wait_on_reg(clk_mem_res, clk_details->idlest_offset, + (1UL << clk_details->bit_offset), 0) != 0) { + printf("Error: failed to enable module with clock %d\n", clkdev->id); + return (ETIMEDOUT); + } + + return (0); +} + + + +/** + * omap3_clk_generic_deactivate - deactivates a modules clock + * @clkdev: pointer to the clock device structure. + * @mem_res: array of memory resources mapped when PRCM driver attached + * + * + * + * 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 +omap3_clk_generic_deactivate(const struct omap_clock_dev *clkdev, + struct resource* mem_res[]) +{ + const struct omap3_clk_details* clk_details = omap3_clk_details(clkdev->id); + struct resource* clk_mem_res = mem_res[CM_INSTANCE_MEM_REGION]; + uint32_t fclken, iclken; + + if (clk_details == NULL) + return (ENXIO); + if (clk_mem_res == NULL) + return (ENOMEM); + + + /* All the 'generic' clocks have a FCLKEN, ICLKEN and IDLEST register which + * is for the functional, interface and clock status regsters respectively. + */ + + /* Disable the interface clock */ + iclken = bus_read_4(clk_mem_res, clk_details->iclken_offset); + iclken &= ~(1UL << clk_details->bit_offset); + bus_write_4(clk_mem_res, clk_details->iclken_offset, iclken); + + /* Disable the functional clock */ + fclken = bus_read_4(clk_mem_res, clk_details->fclken_offset); + fclken &= ~(1UL << clk_details->bit_offset); + bus_write_4(clk_mem_res, clk_details->fclken_offset, fclken); + + + return (0); +} + + +/** + * omap3_clk_generic_set_source - checks if a module is accessible + * @clkdev: pointer to the clock device structure. + * + * + * + * 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 +omap3_clk_generic_set_source(const struct omap_clock_dev *clkdev, + clk_src_t clksrc, struct resource* mem_res[]) +{ + + + return (0); +} + +/** + * omap3_clk_generic_accessible - checks if a module is accessible + * @clkdev: pointer to the clock device structure. + * + * + * + * 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 +omap3_clk_generic_accessible(const struct omap_clock_dev *clkdev, + struct resource* mem_res[]) +{ + const struct omap3_clk_details* clk_details = omap3_clk_details(clkdev->id); + struct resource* clk_mem_res = mem_res[CM_INSTANCE_MEM_REGION]; + uint32_t idlest; + + if (clk_details == NULL) + return (ENXIO); + if (clk_mem_res == NULL) + return (ENOMEM); + + idlest = bus_read_4(clk_mem_res, clk_details->idlest_offset); + + /* Check the enabled state */ + if ((idlest & (1UL << clk_details->bit_offset)) == 0) + return (0); + + return (1); +} + + +/** + * omap3_clk_generic_get_source_freq - checks if a module is accessible + * @clkdev: pointer to the clock device structure. + * + * + * + * 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 +omap3_clk_generic_get_source_freq(const struct omap_clock_dev *clkdev, + unsigned int *freq, + struct resource* mem_res[]) +{ + const struct omap3_clk_details* clk_details = omap3_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); +} + + + +/** + * omap3_clk_gptimer_set_source - sets the source clock for one of the GP timers + * @clkdev: pointer to the clock device structure. + * @clksrc: enum describing the clock source. + * @mem_res: an array of memory handles for the device. + * + * + * 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 +omap3_clk_gptimer_set_source(const struct omap_clock_dev *clkdev, + clk_src_t clksrc, struct resource* mem_res[]) +{ + const struct omap3_clk_details* clk_details = omap3_clk_details(clkdev->id); + struct resource* clk_mem_res = mem_res[CM_INSTANCE_MEM_REGION]; + uint32_t bit, regoff; + uint32_t clksel; + + if (clk_details == NULL) + return (ENXIO); + if (clk_mem_res == NULL) + return (ENOMEM); + + /* Set the source freq by writing the clksel register */ + switch (clkdev->id) { + case GPTIMER1_CLK: + bit = 0; + regoff = WKUP_CM_OFFSET + 0x40; + break; + case GPTIMER2_CLK ... GPTIMER9_CLK: + bit = (clkdev->id - GPTIMER2_CLK); + regoff = PER_CM_OFFSET + 0x40; + break; + case GPTIMER10_CLK ... GPTIMER11_CLK: + bit = 6 + (clkdev->id - GPTIMER10_CLK); + regoff = CORE_CM_OFFSET + 0x40; + break; + default: + return (EINVAL); + } + + /* Set the CLKSEL bit if then the SYS_CLK is the source */ + clksel = bus_read_4(clk_mem_res, regoff); + + if (clksrc == SYSCLK_CLK) + clksel |= (0x1UL << bit); + else + clksel &= ~(0x1UL << bit); + + bus_write_4(clk_mem_res, regoff, clksel); + + /* Read back the value to ensure the write has taken place ... needed ? */ + clksrc = bus_read_4(clk_mem_res, regoff); + + return (0); +} + +/** + * omap3_clk_gptimer_get_source_freq - gets the source freq of the clock + * @clkdev: pointer to the clock device structure. + * @clksrc: enum describing the clock source. + * @mem_res: an array of memory handles for the device. + * + * + * + * 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 +omap3_clk_gptimer_get_source_freq(const struct omap_clock_dev *clkdev, + unsigned int *freq, + struct resource* mem_res[]) +{ + const struct omap3_clk_details* clk_details = omap3_clk_details(clkdev->id); + struct resource* clk_mem_res = mem_res[CM_INSTANCE_MEM_REGION]; + uint32_t bit, regoff; + uint32_t clksel; + unsigned int src_freq; + + if (clk_details == NULL) + return (ENXIO); + if (clk_mem_res == NULL) + return (ENOMEM); + + /* Determine the source freq by reading the clksel register */ + switch (clkdev->id) { + case GPTIMER1_CLK: + bit = 0; + regoff = WKUP_CM_OFFSET + 0x40; + break; + case GPTIMER2_CLK ... GPTIMER9_CLK: + bit = (clkdev->id - GPTIMER2_CLK); + regoff = PER_CM_OFFSET + 0x40; + break; + case GPTIMER10_CLK ... GPTIMER11_CLK: + bit = 6 + (clkdev->id - GPTIMER10_CLK); + regoff = CORE_CM_OFFSET + 0x40; + break; + default: + return (EINVAL); + } + + + /* If the CLKSEL bit is set then the SYS_CLK is the source */ + clksel = bus_read_4(clk_mem_res, regoff); + if (clksel & (0x1UL << bit)) + omap3_clk_get_sysclk_freq(NULL, &src_freq, mem_res); + else + src_freq = FREQ_32KHZ; + + /* Return the frequency */ + if (freq) + *freq = src_freq; + + return (0); +} + + + + +/** + * omap3_clk_setup_dpll5 - setup DPLL5 which is needed for the 120M_FCLK + * @dev: prcm device handle + * + * Sets up the DPLL5 at the frequency specified by the mul and div arguments + * (the source clock is SYS_CLK). + * + * LOCKING: + * None + * + * RETURNS: + * Returns 0 on success otherwise ETIMEDOUT if DPLL failed to lock. + */ +static int +omap3_clk_setup_dpll5(struct resource* cm_mem_res, uint32_t mul, uint32_t div) +{ + uint32_t val; + + /* DPPL5 uses DPLL5_ALWON_FCLK as it's reference clock, this is just SYS_CLK + * which on the beagleboard is 13MHz. + */ + + /* Set the multipler and divider values for the PLL. We want 120Mhz so take + * the system clock (13Mhz) divide by that then multiple by 120. + */ + val = ((mul & 0x7ff) << 8) | ((div - 1) & 0x7f); + bus_write_4(cm_mem_res, CLOCK_CTRL_CM_OFFSET + 0x4C, val); + + /* This is the clock divider from the PLL into the 120Mhz clock supplied to + * the USB module. */ + val = 0x01; + bus_write_4(cm_mem_res, CLOCK_CTRL_CM_OFFSET + 0x50, val); + + /* PERIPH2_DPLL_FREQSEL = 0x7 (1.75 MHz—2.1 MHz) + * EN_PERIPH2_DPLL = 0x7 (Enables the DPLL5 in lock mode) + */ + val = (7 << 4) | (7 << 0); + bus_write_4(cm_mem_res, CLOCK_CTRL_CM_OFFSET + 0x04, val); + + + /* Disable auto-idle */ + bus_write_4(cm_mem_res, CLOCK_CTRL_CM_OFFSET + 0x34, 0x00); + + + /* Wait until the DPLL5 is locked and there is clock activity */ + return (omap3_clk_wait_on_reg(cm_mem_res, (CLOCK_CTRL_CM_OFFSET + 0x24), + 0x01, 0x01)); +} + + + +/** + * omap3_clk_usbhost_activate - activates a modules iinterface and func clock + * @clkdev: pointer to the clock device structure. + * @mem_res: array of memory resources mapped when PRCM driver attached + * + * + * + * 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 +omap3_clk_hsusbhost_activate(const struct omap_clock_dev *clkdev, + struct resource* mem_res[]) +{ + const struct omap3_clk_details* clk_details = omap3_clk_details(clkdev->id); + struct resource* clk_mem_res = mem_res[CM_INSTANCE_MEM_REGION]; + uint32_t fclken, iclken; + unsigned int sysclk; + + if (clk_details == NULL) + return (ENXIO); + if (clk_mem_res == NULL) + return (ENOMEM); + if (clkdev->id != USBHSHOST_CLK) + return (EINVAL); + + /* First ensure DPLL5 is setup and running, this provides the 120M clock */ + + /* SYS_CLK feeds the DPLL so need that to calculate the mul & div */ + omap3_clk_get_sysclk_freq(NULL, &sysclk, mem_res); + + /* Activate DPLL5 and therefore the 120M clock */ + if (omap3_clk_setup_dpll5(clk_mem_res, 120, 13) != 0) + return (ETIMEDOUT); + + + /* All the 'generic' clocks have a FCLKEN, ICLKEN and IDLEST register which + * is for the functional, interface and clock status regsters respectively. + */ + + /* Enable the interface clock */ + iclken = bus_read_4(clk_mem_res, clk_details->iclken_offset); + iclken |= 0x1; + bus_write_4(clk_mem_res, clk_details->iclken_offset, iclken); + + /* Read back the value to ensure the write has taken place ... needed ? */ + iclken = bus_read_4(clk_mem_res, clk_details->iclken_offset); + + + /* Enable the functional clock */ + fclken = bus_read_4(clk_mem_res, clk_details->fclken_offset); + fclken |= 0x03; + bus_write_4(clk_mem_res, clk_details->fclken_offset, fclken); + + /* Read back the value to ensure the write has taken place ... needed ? */ + fclken = bus_read_4(clk_mem_res, clk_details->fclken_offset); + + + /* 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. + */ + + if (omap3_clk_wait_on_reg(clk_mem_res, clk_details->idlest_offset, 0x02, + 0x00) != 0) { + printf("Error: failed to enable module with USB clock %d\n", clkdev->id); + return (ETIMEDOUT); + } + + return (0); +} + + + +/** + * omap3_clk_hsusbhost_deactivate - deactivates a modules clock + * @clkdev: pointer to the clock device structure. + * @mem_res: array of memory resources mapped when PRCM driver attached + * + * + * + * 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 +omap3_clk_hsusbhost_deactivate(const struct omap_clock_dev *clkdev, + struct resource* mem_res[]) +{ + const struct omap3_clk_details* clk_details = omap3_clk_details(clkdev->id); + struct resource* clk_mem_res = mem_res[CM_INSTANCE_MEM_REGION]; + uint32_t fclken, iclken; + + if (clk_details == NULL) + return (ENXIO); + if (clk_mem_res == NULL) + return (ENOMEM); + + + /* All the 'generic' clocks have a FCLKEN, ICLKEN and IDLEST register which + * is for the functional, interface and clock status regsters respectively. + */ + + /* Disable the interface clock */ + iclken = bus_read_4(clk_mem_res, clk_details->iclken_offset); + iclken &= ~(1UL << clk_details->bit_offset); + bus_write_4(clk_mem_res, clk_details->iclken_offset, iclken); + + /* Disable the functional clock */ + fclken = bus_read_4(clk_mem_res, clk_details->fclken_offset); + fclken &= ~(1UL << clk_details->bit_offset); + bus_write_4(clk_mem_res, clk_details->fclken_offset, fclken); + + + return (0); +} + + + + +/** + * omap3_clk_init - add a child item to the root omap3 device + * @dev: the parent device + * @prio: defines roughly the order with which to add the child to the parent + * + * Initialises the clock structure and add an instance of the omap_prcm to + * the parent device with the correct memory regions assigned. + * + * + */ +void +omap3_clk_init(device_t dev, int prio) +{ + device_t kid; + struct omap_ivar *ivar; + + /* Start by adding the actual child to the parent (us) */ + kid = device_add_child_ordered(dev, prio, "omap_prcm", 0); + if (kid == NULL) { + printf("Can't add child omap_prcm0 ordered\n"); + return; + } + + /* Allocate some memory for the omap_ivar structure */ + ivar = malloc(sizeof(*ivar), M_DEVBUF, M_NOWAIT | M_ZERO); + if (ivar == NULL) { + device_delete_child(dev, kid); + printf("Can't add alloc ivar\n"); + return; + } + + /* Assign the ivars to the child item and populate with the device resources */ + device_set_ivars(kid, ivar); + + /* Assign the IRQ(s) in the resource list */ + resource_list_init(&ivar->resources); + + /* Assign the memory region to the resource list */ + bus_set_resource(kid, SYS_RES_MEMORY, CM_INSTANCE_MEM_REGION, + OMAP35XX_CM_HWBASE, 0x2000); + bus_set_resource(kid, SYS_RES_MEMORY, PRM_INSTANCE_MEM_REGION, + OMAP35XX_PRM_HWBASE, 0x2000); +} + + diff --git a/sys/arm/omap/omap3/omap3_scm_padconf.c b/sys/arm/omap/omap3/omap3_scm_padconf.c new file mode 100644 index 0000000..e166169 --- /dev/null +++ b/sys/arm/omap/omap3/omap3_scm_padconf.c @@ -0,0 +1,335 @@ +/*- + * 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 author may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 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 +#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 omap_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 _OMAP_PINDEF(r, b, gp, gm, m0, m1, m2, m3, m4, m5, m6, m7) \ + { .reg_off = r, \ + .ballname = b, \ + .gpio_pin = gp, \ + .gpio_mode = gm, \ + .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 omap_scm_padconf omap_padconf_devmap[] = { + _OMAP_PINDEF(0x0116, "ag17", 99, 4, "cam_d0", NULL, NULL, NULL, "gpio_99", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0118, "ah17", 100, 4, "cam_d1", NULL, NULL, NULL, "gpio_100", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x012a, "b25", 109, 4, "cam_d10", NULL, NULL, NULL, "gpio_109", "hw_dbg8", NULL, "safe_mode"), + _OMAP_PINDEF(0x012c, "c26", 110, 4, "cam_d11", NULL, NULL, NULL, "gpio_110", "hw_dbg9", NULL, "safe_mode"), + _OMAP_PINDEF(0x011a, "b24", 101, 4, "cam_d2", NULL, NULL, NULL, "gpio_101", "hw_dbg4", NULL, "safe_mode"), + _OMAP_PINDEF(0x011c, "c24", 102, 4, "cam_d3", NULL, NULL, NULL, "gpio_102", "hw_dbg5", NULL, "safe_mode"), + _OMAP_PINDEF(0x011e, "d24", 103, 4, "cam_d4", NULL, NULL, NULL, "gpio_103", "hw_dbg6", NULL, "safe_mode"), + _OMAP_PINDEF(0x0120, "a25", 104, 4, "cam_d5", NULL, NULL, NULL, "gpio_104", "hw_dbg7", NULL, "safe_mode"), + _OMAP_PINDEF(0x0122, "k28", 105, 4, "cam_d6", NULL, NULL, NULL, "gpio_105", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0124, "l28", 106, 4, "cam_d7", NULL, NULL, NULL, "gpio_106", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0126, "k27", 107, 4, "cam_d8", NULL, NULL, NULL, "gpio_107", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0128, "l27", 108, 4, "cam_d9", NULL, NULL, NULL, "gpio_108", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0114, "c23", 98, 4, "cam_fld", NULL, "cam_global_reset", NULL, "gpio_98", "hw_dbg3", NULL, "safe_mode"), + _OMAP_PINDEF(0x010c, "a24", 94, 4, "cam_hs", NULL, NULL, NULL, "gpio_94", "hw_dbg0", NULL, "safe_mode"), + _OMAP_PINDEF(0x0112, "c27", 97, 4, "cam_pclk", NULL, NULL, NULL, "gpio_97", "hw_dbg2", NULL, "safe_mode"), + _OMAP_PINDEF(0x0132, "d25", 126, 4, "cam_strobe", NULL, NULL, NULL, "gpio_126", "hw_dbg11", NULL, "safe_mode"), + _OMAP_PINDEF(0x010e, "a23", 95, 4, "cam_vs", NULL, NULL, NULL, "gpio_95", "hw_dbg1", NULL, "safe_mode"), + _OMAP_PINDEF(0x0130, "b23", 167, 4, "cam_wen", NULL, "cam_shutter", NULL, "gpio_167", "hw_dbg10", NULL, "safe_mode"), + _OMAP_PINDEF(0x0110, "c25", 96, 4, "cam_xclka", NULL, NULL, NULL, "gpio_96", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x012e, "b26", 111, 4, "cam_xclkb", NULL, NULL, NULL, "gpio_111", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0134, "ag19", 112, 4, "csi2_dx0", NULL, NULL, NULL, "gpio_112", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0138, "ag18", 114, 4, "csi2_dx1", NULL, NULL, NULL, "gpio_114", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0136, "ah19", 113, 4, "csi2_dy0", NULL, NULL, NULL, "gpio_113", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x013a, "ah18", 115, 4, "csi2_dy1", NULL, NULL, NULL, "gpio_115", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00da, "e27", 69, 4, "dss_acbias", NULL, NULL, NULL, "gpio_69", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00dc, "ag22", 70, 4, "dss_data0", NULL, "uart1_cts", NULL, "gpio_70", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00de, "ah22", 71, 4, "dss_data1", NULL, "uart1_rts", NULL, "gpio_71", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00f0, "ad28", 80, 4, "dss_data10", NULL, NULL, NULL, "gpio_80", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00f2, "ad27", 81, 4, "dss_data11", NULL, NULL, NULL, "gpio_81", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00f4, "ab28", 82, 4, "dss_data12", NULL, NULL, NULL, "gpio_82", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00f6, "ab27", 83, 4, "dss_data13", NULL, NULL, NULL, "gpio_83", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00f8, "aa28", 84, 4, "dss_data14", NULL, NULL, NULL, "gpio_84", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00fa, "aa27", 85, 4, "dss_data15", NULL, NULL, NULL, "gpio_85", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00fc, "g25", 86, 4, "dss_data16", NULL, NULL, NULL, "gpio_86", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00fe, "h27", 87, 4, "dss_data17", NULL, NULL, NULL, "gpio_87", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0100, "h26", 88, 4, "dss_data18", NULL, "mcspi3_clk", "dss_data0", "gpio_88", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0102, "h25", 89, 4, "dss_data19", NULL, "mcspi3_simo", "dss_data1", "gpio_89", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0104, "e28", 90, 4, "dss_data20", NULL, "mcspi3_somi", "dss_data2", "gpio_90", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0106, "j26", 91, 4, "dss_data21", NULL, "mcspi3_cs0", "dss_data3", "gpio_91", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0108, "ac27", 92, 4, "dss_data22", NULL, "mcspi3_cs1", "dss_data4", "gpio_92", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x010a, "ac28", 93, 4, "dss_data23", NULL, NULL, "dss_data5", "gpio_93", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00e0, "ag23", 72, 4, "dss_data2", NULL, NULL, NULL, "gpio_72", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00e2, "ah23", 73, 4, "dss_data3", NULL, NULL, NULL, "gpio_73", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00e4, "ag24", 74, 4, "dss_data4", NULL, "uart3_rx_irrx", NULL, "gpio_74", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00e6, "ah24", 75, 4, "dss_data5", NULL, "uart3_tx_irtx", NULL, "gpio_75", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00e8, "e26", 76, 4, "dss_data6", NULL, "uart1_tx", NULL, "gpio_76", "hw_dbg14", NULL, "safe_mode"), + _OMAP_PINDEF(0x00ea, "f28", 77, 4, "dss_data7", NULL, "uart1_rx", NULL, "gpio_77", "hw_dbg15", NULL, "safe_mode"), + _OMAP_PINDEF(0x00ec, "f27", 78, 4, "dss_data8", NULL, NULL, NULL, "gpio_78", "hw_dbg16", NULL, "safe_mode"), + _OMAP_PINDEF(0x00ee, "g26", 79, 4, "dss_data9", NULL, NULL, NULL, "gpio_79", "hw_dbg17", NULL, "safe_mode"), + _OMAP_PINDEF(0x00d6, "d26", 67, 4, "dss_hsync", NULL, NULL, NULL, "gpio_67", "hw_dbg13", NULL, "safe_mode"), + _OMAP_PINDEF(0x00d4, "d28", 66, 4, "dss_pclk", NULL, NULL, NULL, "gpio_66", "hw_dbg12", NULL, "safe_mode"), + _OMAP_PINDEF(0x00d8, "d27", 68, 4, "dss_vsync", NULL, NULL, NULL, "gpio_68", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x05d8, "af10", 12, 4, "etk_clk", "mcbsp5_clkx", "sdmmc3_clk", "hsusb1_stp", "gpio_12", "mm1_rxdp", "hsusb1_tll_stp", "hw_dbg0"), + _OMAP_PINDEF(0x05da, "ae10", 13, 4, "etk_ctl", NULL, "sdmmc3_cmd", "hsusb1_clk", "gpio_13", NULL, "hsusb1_tll_clk", "hw_dbg1"), + _OMAP_PINDEF(0x05dc, "af11", 14, 4, "etk_d0", "mcspi3_simo", "sdmmc3_dat4", "hsusb1_data0", "gpio_14", "mm1_rxrcv", "hsusb1_tll_data0", "hw_dbg2"), + _OMAP_PINDEF(0x05de, "ag12", 15, 4, "etk_d1", "mcspi3_somi", NULL, "hsusb1_data1", "gpio_15", "mm1_txse0", "hsusb1_tll_data1", "hw_dbg3"), + _OMAP_PINDEF(0x05f0, "ae7", 24, 4, "etk_d10", NULL, "uart1_rx", "hsusb2_clk", "gpio_24", NULL, "hsusb2_tll_clk", "hw_dbg12"), + _OMAP_PINDEF(0x05f2, "af7", 25, 4, "etk_d11", NULL, NULL, "hsusb2_stp", "gpio_25", "mm2_rxdp", "hsusb2_tll_stp", "hw_dbg13"), + _OMAP_PINDEF(0x05f4, "ag7", 26, 4, "etk_d12", NULL, NULL, "hsusb2_dir", "gpio_26", NULL, "hsusb2_tll_dir", "hw_dbg14"), + _OMAP_PINDEF(0x05f6, "ah7", 27, 4, "etk_d13", NULL, NULL, "hsusb2_nxt", "gpio_27", "mm2_rxdm", "hsusb2_tll_nxt", "hw_dbg15"), + _OMAP_PINDEF(0x05f8, "ag8", 28, 4, "etk_d14", NULL, NULL, "hsusb2_data0", "gpio_28", "mm2_rxrcv", "hsusb2_tll_data0", "hw_dbg16"), + _OMAP_PINDEF(0x05fa, "ah8", 29, 4, "etk_d15", NULL, NULL, "hsusb2_data1", "gpio_29", "mm2_txse0", "hsusb2_tll_data1", "hw_dbg17"), + _OMAP_PINDEF(0x05e0, "ah12", 16, 4, "etk_d2", "mcspi3_cs0", NULL, "hsusb1_data2", "gpio_16", "mm1_txdat", "hsusb1_tll_data2", "hw_dbg4"), + _OMAP_PINDEF(0x05e2, "ae13", 17, 4, "etk_d3", "mcspi3_clk", "sdmmc3_dat3", "hsusb1_data7", "gpio_17", NULL, "hsusb1_tll_data7", "hw_dbg5"), + _OMAP_PINDEF(0x05e4, "ae11", 18, 4, "etk_d4", "mcbsp5_dr", "sdmmc3_dat0", "hsusb1_data4", "gpio_18", NULL, "hsusb1_tll_data4", "hw_dbg6"), + _OMAP_PINDEF(0x05e6, "ah9", 19, 4, "etk_d5", "mcbsp5_fsx", "sdmmc3_dat1", "hsusb1_data5", "gpio_19", NULL, "hsusb1_tll_data5", "hw_dbg7"), + _OMAP_PINDEF(0x05e8, "af13", 20, 4, "etk_d6", "mcbsp5_dx", "sdmmc3_dat2", "hsusb1_data6", "gpio_20", NULL, "hsusb1_tll_data6", "hw_dbg8"), + _OMAP_PINDEF(0x05ea, "ah14", 21, 4, "etk_d7", "mcspi3_cs1", "sdmmc3_dat7", "hsusb1_data3", "gpio_21", "mm1_txen_n", "hsusb1_tll_data3", "hw_dbg9"), + _OMAP_PINDEF(0x05ec, "af9", 22, 4, "etk_d8", "sys_drm_msecure", "sdmmc3_dat6", "hsusb1_dir", "gpio_22", NULL, "hsusb1_tll_dir", "hw_dbg10"), + _OMAP_PINDEF(0x05ee, "ag9", 23, 4, "etk_d9", "sys_secure_indicator", "sdmmc3_dat5", "hsusb1_nxt", "gpio_23", "mm1_rxdm", "hsusb1_tll_nxt", "hw_dbg11"), + _OMAP_PINDEF(0x007a, "n4", 34, 4, "gpmc_a1", NULL, NULL, NULL, "gpio_34", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x008c, "k3", 43, 4, "gpmc_a10", "sys_ndmareq3", NULL, NULL, "gpio_43", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x007c, "m4", 35, 4, "gpmc_a2", NULL, NULL, NULL, "gpio_35", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x007e, "l4", 36, 4, "gpmc_a3", NULL, NULL, NULL, "gpio_36", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0080, "k4", 37, 4, "gpmc_a4", NULL, NULL, NULL, "gpio_37", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0082, "t3", 38, 4, "gpmc_a5", NULL, NULL, NULL, "gpio_38", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0084, "r3", 39, 4, "gpmc_a6", NULL, NULL, NULL, "gpio_39", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0086, "n3", 40, 4, "gpmc_a7", NULL, NULL, NULL, "gpio_40", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0088, "m3", 41, 4, "gpmc_a8", NULL, NULL, NULL, "gpio_41", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x008a, "l3", 42, 4, "gpmc_a9", "sys_ndmareq2", NULL, NULL, "gpio_42", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00be, "t4", 59, 4, "gpmc_clk", NULL, NULL, NULL, "gpio_59", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00a2, "p1", 46, 4, "gpmc_d10", NULL, NULL, NULL, "gpio_46", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00a4, "r1", 47, 4, "gpmc_d11", NULL, NULL, NULL, "gpio_47", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00a6, "r2", 48, 4, "gpmc_d12", NULL, NULL, NULL, "gpio_48", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00a8, "t2", 49, 4, "gpmc_d13", NULL, NULL, NULL, "gpio_49", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00aa, "w1", 50, 4, "gpmc_d14", NULL, NULL, NULL, "gpio_50", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00ac, "y1", 51, 4, "gpmc_d15", NULL, NULL, NULL, "gpio_51", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x009e, "h2", 44, 4, "gpmc_d8", NULL, NULL, NULL, "gpio_44", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00a0, "k2", 45, 4, "gpmc_d9", NULL, NULL, NULL, "gpio_45", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00c6, "g3", 60, 4, "gpmc_nbe0_cle", NULL, NULL, NULL, "gpio_60", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00c8, "u3", 61, 4, "gpmc_nbe1", NULL, NULL, NULL, "gpio_61", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00b0, "h3", 52, 4, "gpmc_ncs1", NULL, NULL, NULL, "gpio_52", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00b2, "v8", 53, 4, "gpmc_ncs2", NULL, NULL, NULL, "gpio_53", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00b4, "u8", 54, 4, "gpmc_ncs3", "sys_ndmareq0", NULL, NULL, "gpio_54", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00b6, "t8", 55, 4, "gpmc_ncs4", "sys_ndmareq1", "mcbsp4_clkx", "gpt9_pwm_evt", "gpio_55", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00b8, "r8", 56, 4, "gpmc_ncs5", "sys_ndmareq2", "mcbsp4_dr", "gpt10_pwm_evt", "gpio_56", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00ba, "p8", 57, 4, "gpmc_ncs6", "sys_ndmareq3", "mcbsp4_dx", "gpt11_pwm_evt", "gpio_57", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00bc, "n8", 58, 4, "gpmc_ncs7", "gpmc_io_dir", "mcbsp4_fsx", "gpt8_pwm_evt", "gpio_58", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00ca, "h1", 62, 4, "gpmc_nwp", NULL, NULL, NULL, "gpio_62", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00ce, "l8", 63, 4, "gpmc_wait1", NULL, NULL, NULL, "gpio_63", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00d0, "k8", 64, 4, "gpmc_wait2", NULL, NULL, NULL, "gpio_64", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00d2, "j8", 65, 4, "gpmc_wait3", "sys_ndmareq1", NULL, NULL, "gpio_65", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x01c6, "j25", 170, 4, "hdq_sio", "sys_altclk", "i2c2_sccbe", "i2c3_sccbe", "gpio_170", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x01a2, "t28", 120, 4, "hsusb0_clk", NULL, NULL, NULL, "gpio_120", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x01aa, "t27", 125, 4, "hsusb0_data0", NULL, "uart3_tx_irtx", NULL, "gpio_125", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x01ac, "u28", 130, 4, "hsusb0_data1", NULL, "uart3_rx_irrx", NULL, "gpio_130", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x01ae, "u27", 131, 4, "hsusb0_data2", NULL, "uart3_rts_sd", NULL, "gpio_131", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x01b0, "u26", 169, 4, "hsusb0_data3", NULL, "uart3_cts_rctx", NULL, "gpio_169", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x01b2, "u25", 188, 4, "hsusb0_data4", NULL, NULL, NULL, "gpio_188", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x01b4, "v28", 189, 4, "hsusb0_data5", NULL, NULL, NULL, "gpio_189", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x01b6, "v27", 190, 4, "hsusb0_data6", NULL, NULL, NULL, "gpio_190", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x01b8, "v26", 191, 4, "hsusb0_data7", NULL, NULL, NULL, "gpio_191", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x01a6, "r28", 122, 4, "hsusb0_dir", NULL, NULL, NULL, "gpio_122", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x01a8, "t26", 124, 4, "hsusb0_nxt", NULL, NULL, NULL, "gpio_124", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x01a4, "t25", 121, 4, "hsusb0_stp", NULL, NULL, NULL, "gpio_121", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x01be, "af15", 168, 4, "i2c2_scl", NULL, NULL, NULL, "gpio_168", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x01c0, "ae15", 183, 4, "i2c2_sda", NULL, NULL, NULL, "gpio_183", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x01c2, "af14", 184, 4, "i2c3_scl", NULL, NULL, NULL, "gpio_184", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x01c4, "ag14", 185, 4, "i2c3_sda", NULL, NULL, NULL, "gpio_185", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0a00, "ad26", 0, 0, "i2c4_scl", "sys_nvmode1", NULL, NULL, NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0a02, "ae26", 0, 0, "i2c4_sda", "sys_nvmode2", NULL, NULL, NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0a24, "aa11", 11, 4, "jtag_emu0", NULL, NULL, NULL, "gpio_11", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0a26, "aa10", 31, 4, "jtag_emu1", NULL, NULL, NULL, "gpio_31", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x018c, "y21", 156, 4, "mcbsp1_clkr", "mcspi4_clk", NULL, NULL, "gpio_156", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0198, "w21", 162, 4, "mcbsp1_clkx", NULL, "mcbsp3_clkx", NULL, "gpio_162", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0192, "u21", 159, 4, "mcbsp1_dr", "mcspi4_somi", "mcbsp3_dr", NULL, "gpio_159", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0190, "v21", 158, 4, "mcbsp1_dx", "mcspi4_simo", "mcbsp3_dx", NULL, "gpio_158", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x018e, "aa21", 157, 4, "mcbsp1_fsr", NULL, "cam_global_reset", NULL, "gpio_157", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0196, "k26", 161, 4, "mcbsp1_fsx", "mcspi4_cs0", "mcbsp3_fsx", NULL, "gpio_161", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x013e, "n21", 117, 4, "mcbsp2_clkx", NULL, NULL, NULL, "gpio_117", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0140, "r21", 118, 4, "mcbsp2_dr", NULL, NULL, NULL, "gpio_118", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0142, "m21", 119, 4, "mcbsp2_dx", NULL, NULL, NULL, "gpio_119", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x013c, "p21", 116, 4, "mcbsp2_fsx", NULL, NULL, NULL, "gpio_116", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0170, "af5", 142, 4, "mcbsp3_clkx", "uart2_tx", NULL, NULL, "gpio_142", "hsusb3_tll_data6", NULL, "safe_mode"), + _OMAP_PINDEF(0x016e, "ae6", 141, 4, "mcbsp3_dr", "uart2_rts", NULL, NULL, "gpio_141", "hsusb3_tll_data5", NULL, "safe_mode"), + _OMAP_PINDEF(0x016c, "af6", 140, 4, "mcbsp3_dx", "uart2_cts", NULL, NULL, "gpio_140", "hsusb3_tll_data4", NULL, "safe_mode"), + _OMAP_PINDEF(0x0172, "ae5", 143, 4, "mcbsp3_fsx", "uart2_rx", NULL, NULL, "gpio_143", "hsusb3_tll_data7", NULL, "safe_mode"), + _OMAP_PINDEF(0x0184, "ae1", 152, 4, "mcbsp4_clkx", NULL, NULL, NULL, "gpio_152", "hsusb3_tll_data1", "mm3_txse0", "safe_mode"), + _OMAP_PINDEF(0x0186, "ad1", 153, 4, "mcbsp4_dr", NULL, NULL, NULL, "gpio_153", "hsusb3_tll_data0", "mm3_rxrcv", "safe_mode"), + _OMAP_PINDEF(0x0188, "ad2", 154, 4, "mcbsp4_dx", NULL, NULL, NULL, "gpio_154", "hsusb3_tll_data2", "mm3_txdat", "safe_mode"), + _OMAP_PINDEF(0x018a, "ac1", 155, 4, "mcbsp4_fsx", NULL, NULL, NULL, "gpio_155", "hsusb3_tll_data3", "mm3_txen_n", "safe_mode"), + _OMAP_PINDEF(0x0194, "t21", 160, 4, "mcbsp_clks", NULL, "cam_shutter", NULL, "gpio_160", "uart1_cts", NULL, "safe_mode"), + _OMAP_PINDEF(0x01c8, "ab3", 171, 4, "mcspi1_clk", "sdmmc2_dat4", NULL, NULL, "gpio_171", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x01ce, "ac2", 174, 4, "mcspi1_cs0", "sdmmc2_dat7", NULL, NULL, "gpio_174", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x01d0, "ac3", 175, 4, "mcspi1_cs1", NULL, NULL, "sdmmc3_cmd", "gpio_175", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x01d2, "ab1", 176, 4, "mcspi1_cs2", NULL, NULL, "sdmmc3_clk", "gpio_176", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x01d4, "ab2", 177, 4, "mcspi1_cs3", NULL, "hsusb2_tll_data2", "hsusb2_data2", "gpio_177", "mm2_txdat", NULL, "safe_mode"), + _OMAP_PINDEF(0x01ca, "ab4", 172, 4, "mcspi1_simo", "sdmmc2_dat5", NULL, NULL, "gpio_172", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x01cc, "aa4", 173, 4, "mcspi1_somi", "sdmmc2_dat6", NULL, NULL, "gpio_173", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x01d6, "aa3", 178, 4, "mcspi2_clk", NULL, "hsusb2_tll_data7", "hsusb2_data7", "gpio_178", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x01dc, "y4", 181, 4, "mcspi2_cs0", "gpt11_pwm_evt", "hsusb2_tll_data6", "hsusb2_data6", "gpio_181", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x01de, "v3", 182, 4, "mcspi2_cs1", "gpt8_pwm_evt", "hsusb2_tll_data3", "hsusb2_data3", "gpio_182", "mm2_txen_n", NULL, "safe_mode"), + _OMAP_PINDEF(0x01d8, "y2", 179, 4, "mcspi2_simo", "gpt9_pwm_evt", "hsusb2_tll_data4", "hsusb2_data4", "gpio_179", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x01da, "y3", 180, 4, "mcspi2_somi", "gpt10_pwm_evt", "hsusb2_tll_data5", "hsusb2_data5", "gpio_180", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0144, "n28", 120, 4, "sdmmc1_clk", NULL, NULL, NULL, "gpio_120", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0146, "m27", 121, 4, "sdmmc1_cmd", NULL, NULL, NULL, "gpio_121", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0148, "n27", 122, 4, "sdmmc1_dat0", NULL, NULL, NULL, "gpio_122", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x014a, "n26", 123, 4, "sdmmc1_dat1", NULL, NULL, NULL, "gpio_123", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x014c, "n25", 124, 4, "sdmmc1_dat2", NULL, NULL, NULL, "gpio_124", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x014e, "p28", 125, 4, "sdmmc1_dat3", NULL, NULL, NULL, "gpio_125", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0150, "p27", 126, 4, "sdmmc1_dat4", NULL, "sim_io", NULL, "gpio_126", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0152, "p26", 127, 4, "sdmmc1_dat5", NULL, "sim_clk", NULL, "gpio_127", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0154, "r27", 128, 4, "sdmmc1_dat6", NULL, "sim_pwrctrl", NULL, "gpio_128", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0156, "r25", 129, 4, "sdmmc1_dat7", NULL, "sim_rst", NULL, "gpio_129", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0158, "ae2", 130, 4, "sdmmc2_clk", "mcspi3_clk", NULL, NULL, "gpio_130", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x015a, "ag5", 131, 4, "sdmmc2_cmd", "mcspi3_simo", NULL, NULL, "gpio_131", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x015c, "ah5", 132, 4, "sdmmc2_dat0", "mcspi3_somi", NULL, NULL, "gpio_132", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x015e, "ah4", 133, 4, "sdmmc2_dat1", NULL, NULL, NULL, "gpio_133", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0160, "ag4", 134, 4, "sdmmc2_dat2", "mcspi3_cs1", NULL, NULL, "gpio_134", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0162, "af4", 135, 4, "sdmmc2_dat3", "mcspi3_cs0", NULL, NULL, "gpio_135", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0164, "ae4", 136, 4, "sdmmc2_dat4", "sdmmc2_dir_dat0", NULL, "sdmmc3_dat0", "gpio_136", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0166, "ah3", 137, 4, "sdmmc2_dat5", "sdmmc2_dir_dat1", "cam_global_reset", "sdmmc3_dat1", "gpio_137", "hsusb3_tll_stp", "mm3_rxdp", "safe_mode"), + _OMAP_PINDEF(0x0168, "af3", 138, 4, "sdmmc2_dat6", "sdmmc2_dir_cmd", "cam_shutter", "sdmmc3_dat2", "gpio_138", "hsusb3_tll_dir", NULL, "safe_mode"), + _OMAP_PINDEF(0x016a, "ae3", 139, 4, "sdmmc2_dat7", "sdmmc2_clkin", NULL, "sdmmc3_dat3", "gpio_139", "hsusb3_tll_nxt", "mm3_rxdm", "safe_mode"), + _OMAP_PINDEF(0x0262, "ae3", 0, 0, "sdrc_cke0", NULL, NULL, NULL, NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0264, "ae3", 0, 0, "sdrc_cke1", NULL, NULL, NULL, NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0a0a, "ah26", 2, 4, "sys_boot0", NULL, NULL, NULL, "gpio_2", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0a0c, "ag26", 3, 4, "sys_boot1", NULL, NULL, NULL, "gpio_3", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0a0e, "ae14", 4, 4, "sys_boot2", NULL, NULL, NULL, "gpio_4", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0a10, "af18", 5, 4, "sys_boot3", NULL, NULL, NULL, "gpio_5", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0a12, "af19", 6, 4, "sys_boot4", "sdmmc2_dir_dat2", NULL, NULL, "gpio_6", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0a14, "ae21", 7, 4, "sys_boot5", "sdmmc2_dir_dat3", NULL, NULL, "gpio_7", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0a16, "af21", 8, 4, "sys_boot6", NULL, NULL, NULL, "gpio_8", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0a1a, "ag25", 10, 4, "sys_clkout1", NULL, NULL, NULL, "gpio_10", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x01e2, "ae22", 186, 4, "sys_clkout2", NULL, NULL, NULL, "gpio_186", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0a06, "af25", 1, 4, "sys_clkreq", NULL, NULL, NULL, "gpio_1", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x01e0, "af26", 0, 0, "sys_nirq", NULL, NULL, NULL, "gpio_0", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0a08, "af24", 30, 4, "sys_nreswarm", NULL, NULL, NULL, "gpio_30", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0a18, "af22", 9, 4, "sys_off_mode", NULL, NULL, NULL, "gpio_9", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0180, "w8", 150, 4, "uart1_cts", NULL, NULL, NULL, "gpio_150", "hsusb3_tll_clk", NULL, "safe_mode"), + _OMAP_PINDEF(0x017e, "aa9", 149, 4, "uart1_rts", NULL, NULL, NULL, "gpio_149", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0182, "y8", 151, 4, "uart1_rx", NULL, "mcbsp1_clkr", "mcspi4_clk", "gpio_151", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x017c, "aa8", 148, 4, "uart1_tx", NULL, NULL, NULL, "gpio_148", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0174, "ab26", 144, 4, "uart2_cts", "mcbsp3_dx", "gpt9_pwm_evt", NULL, "gpio_144", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0176, "ab25", 145, 4, "uart2_rts", "mcbsp3_dr", "gpt10_pwm_evt", NULL, "gpio_145", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x017a, "ad25", 147, 4, "uart2_rx", "mcbsp3_fsx", "gpt8_pwm_evt", NULL, "gpio_147", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0178, "aa25", 146, 4, "uart2_tx", "mcbsp3_clkx", "gpt11_pwm_evt", NULL, "gpio_146", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x019a, "h18", 163, 4, "uart3_cts_rctx", NULL, NULL, NULL, "gpio_163", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x019c, "h19", 164, 4, "uart3_rts_sd", NULL, NULL, NULL, "gpio_164", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x019e, "h20", 165, 4, "uart3_rx_irrx", NULL, NULL, NULL, "gpio_165", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x01a0, "h21", 166, 4, "uart3_tx_irtx", NULL, NULL, NULL, "gpio_166", NULL, NULL, "safe_mode"), + { .ballname = NULL }, +}; + + + +/** + * omap4_pinmux_init - adds and initialise the OMAP SCM driver + * @dev: the parent device + * @prio: defines roughly the order with which to add the child to the parent + * + * Initialises the pinmux structure and add an instance of the omap_scm to + * the parent device with the correct memory regions assigned. + * + * + */ +void +omap3_padconf_init(device_t dev, int prio) +{ + device_t kid; + struct omap_ivar *ivar; + + /* Start by adding the actual child to the parent (us) */ + kid = device_add_child_ordered(dev, prio, "omap_scm", 0); + if (kid == NULL) { + printf("Can't add child omap_scm0 ordered\n"); + return; + } + + /* Allocate some memory for the omap_ivar structure */ + ivar = malloc(sizeof(*ivar), M_DEVBUF, M_NOWAIT | M_ZERO); + if (ivar == NULL) { + device_delete_child(dev, kid); + printf("Can't add alloc ivar\n"); + return; + } + + /* Assign the ivars to the child item and populate with the device resources */ + device_set_ivars(kid, ivar); + + /* Initialise the resource list */ + resource_list_init(&ivar->resources); + + /* Assign the memory region to the resource list */ + bus_set_resource(kid, SYS_RES_MEMORY, 0, OMAP35XX_SCM_HWBASE, + OMAP35XX_SCM_SIZE); +} + + diff --git a/sys/arm/omap/omap3/omap3_timer.c b/sys/arm/omap/omap3/omap3_timer.c new file mode 100644 index 0000000..fe3dc36 --- /dev/null +++ b/sys/arm/omap/omap3/omap3_timer.c @@ -0,0 +1,378 @@ +/*- + * 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 author may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 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. + */ + +/** + * To be confusing, there are two timers setup here, one is the system ticks + * that is suppose to go off 'hz' number of times a second. The other is a + * general purpose counter that is just used to provide a count to the system. + * + * For the system tick timer we use GPTIMER10, this has an acurate 1ms mode + * which is designed for system tick generation, however at the moment we don't + * use that feature, instead we just run it as a normal 32KHz timer. + * + * For the other timer we use GPTIMER11, for no special reason other than it + * comes after 10 and they are both in the core power domain. It's clocked + * of the SYS_CLK which (on the beagleboard at least) is 13MHz. + * + */ +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +//#define CPU_CLOCKSPEED 500000000 + +/** + * The pin to use for the heartbeat, the pin will be toggled every second. If + * you don't want a heartbeat, don't define the following. + * TODO: Move this to make options + */ +//#define OMAP3_HEARTBEAT_GPIO 150 + + +#define TICKTIMER_GPTIMER 10 +#define TIMECOUNT_GPTIMER 11 + + + +#define __omap3_delay(i) \ + do { \ + unsigned int cnt = (i); \ + __asm __volatile("1: subs %0, %0, 1\n" \ + " bne 1b\n" \ + : "+r" (cnt) : : "cc"); \ + } while(0) + + +static unsigned int delay_loops_per_us = 100; + + + +/** + * omap4_timer_get_timecount - returns the count in GPTIMER11, the system counter + * @tc: pointer to the timecounter structure used to register the callback + * + * + * + * RETURNS: + * the value in the counter + */ +static unsigned +omap3_timer_get_timecount(struct timecounter *tc) +{ + uint32_t count; + omap_gptimer_read_count(TIMECOUNT_GPTIMER, &count); + return(count); +} + +static struct timecounter g_omap3_timecounter = { + .tc_get_timecount = omap3_timer_get_timecount, /* get_timecount */ + .tc_poll_pps = NULL, /* no poll_pps */ + .tc_counter_mask = ~0u, /* counter_mask */ + .tc_frequency = 0, /* frequency */ + .tc_name = "OMAP3 Timer", /* name */ + .tc_quality = 1000, /* quality */ +}; + + + +/** + * omap3_calibrate_delay_loop - uses the setup timecounter to configure delay + * + * This is not very scientfic, basically just use the timecount to measure the + * time to do 1000 delay loops (for loop with 1024 loops). + * + * + */ +static int +omap3_calibrate_delay_loop(struct timecounter *tc) +{ + u_int oldirqstate; + unsigned int start, end; + uint64_t nanosecs; + + /* Disable interrupts to ensure they don't mess up the calculation */ + oldirqstate = disable_interrupts(I32_bit); + + start = omap3_timer_get_timecount(tc); + __omap3_delay(10240); + end = omap3_timer_get_timecount(tc); + + restore_interrupts(oldirqstate); + + /* Calculate the number of loops in 1us */ + nanosecs = ((uint64_t)(end - start) * 1000000000ULL) / tc->tc_frequency; + delay_loops_per_us = (unsigned int)((uint64_t)(10240 * 1000) / nanosecs); + + printf("OMAP: delay loop calibrated to %u cycles\n", delay_loops_per_us); + + return (0); +} + + + + +/** + * omap3_clk_intr - interrupt handler for the tick timer (GPTIMER10) + * @arg: the trapframe, needed for the hardclock system function. + * + * This interrupt is triggered every hz times a second. It's role is basically + * to just clear the interrupt status and set it up for triggering again, plus + * tell the system a tick timer has gone off by calling the hardclock() + * function from the kernel API. + * + * RETURNS: + * Always returns FILTER_HANDLED. + */ +static int +omap3_timer_tick_intr(void *arg) +{ + struct trapframe *frame = arg; +#if defined(OMAP3_HEARTBEAT_GPIO) + static int heartbeat_cnt = 0; +#endif + + /* Acknowledge the interrupt */ + omap_gptimer_intr_filter_ack(TICKTIMER_GPTIMER); + + /* Heartbeat */ +#if defined(OMAP3_HEARTBEAT_GPIO) + if (heartbeat_cnt++ >= (hz/2)) { + //printf("[BRG] ** tick **\n"); + omap3_gpio_pin_toggle(OMAP3_HEARTBEAT_GPIO); + heartbeat_cnt = 0; + } +#endif + + /* Do what we came here for */ + hardclock(TRAPF_USERMODE(frame), TRAPF_PC(frame)); + + /* Indicate we've handed the interrupt */ + return (FILTER_HANDLED); +} + + + + +/** + * cpu_initclocks - function called by the system in init the tick clock/timer + * + * This is where both the timercount and system ticks timer are started. + * + * RETURNS: + * nothing + */ +void +cpu_initclocks(void) +{ + u_int oldirqstate; + unsigned int timer_freq; + + oldirqstate = disable_interrupts(I32_bit); + + /* Number of microseconds between interrupts */ + tick = 1000000 / hz; + + /* Next setup one of the timers to be the system tick timer */ + if (omap_gptimer_activate(TICKTIMER_GPTIMER, OMAP_GPTIMER_PERIODIC_FLAG, + tick, NULL, NULL)) { + panic("Error: failed to activate system tick timer\n"); + } + + /* Setup an interrupt filter for the timer */ + if (omap_gptimer_set_intr_filter(TICKTIMER_GPTIMER, omap3_timer_tick_intr)) + panic("Error: failed to start system tick timer\n"); + + /* Lastly start the tick timer */ + if (omap_gptimer_start(TICKTIMER_GPTIMER)) + panic("Error: failed to start system tick timer\n"); + + omap_gptimer_get_freq(TICKTIMER_GPTIMER, &timer_freq); +printf("tick: timer_freq = %u\n", timer_freq); + + + + /* Setup another timer to be the timecounter */ + if (omap_gptimer_activate(TIMECOUNT_GPTIMER, OMAP_GPTIMER_PERIODIC_FLAG, 0, + NULL, NULL)) { + printf("Error: failed to activate system tick timer\n"); + } else if (omap_gptimer_start(TIMECOUNT_GPTIMER)) { + printf("Error: failed to start system tick timer\n"); + } + + + + /* Save the system clock speed */ + omap_gptimer_get_freq(TIMECOUNT_GPTIMER, &timer_freq); + g_omap3_timecounter.tc_frequency = timer_freq; + + /* Setup the time counter */ + tc_init(&g_omap3_timecounter); + + /* Calibrate the delay loop */ + omap3_calibrate_delay_loop(&g_omap3_timecounter); + + /* Restore interrupt state */ + restore_interrupts(oldirqstate); +} + + +/** + * DELAY - Delay for at least N microseconds. + * @n: number of microseconds to delay by + * + * This function is called all over the kernel and is suppose to provide a + * consistent delay. It is a busy loop and blocks polling a timer when called. + * + * RETURNS: + * nothing + */ +void +DELAY(int usec) +{ + if (usec <= 0) + return; + + for (; usec > 0; usec--) { + __omap3_delay(delay_loops_per_us); + } + +} + +#if 0 +void +DELAY(int n) +{ + int32_t counts_per_usec; + int32_t counts; + uint32_t first, last; + + if (n <= 0) + return; + + /* Check the timers are setup, if not just use a for loop for the meantime */ +// if (g_omap3_timecounter.tc_frequency == 0) { + if (1) { + + /* If the CPU clock speed is defined we use that via the 'cycle count' + * register, this should give us a pretty accurate delay value. If not + * defined we use a basic for loop with a very simply calculation. + */ +#if defined(CPU_CLOCKSPEED) + counts_per_usec = (CPU_CLOCKSPEED / 1000000); + counts = counts_per_usec * 1000; + + __asm __volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (first)); + while (counts > 0) { + __asm __volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (last)); + counts -= (int32_t)(last - first); + first = last; + } +#else + uint32_t val; + for (; n > 0; n--) + for (val = 1000; val > 0; val--) + continue; +#endif + return; + } + + /* Get the number of times to count */ + counts_per_usec = ((g_omap3_timecounter.tc_frequency / 1000000) + 1); + +//printf("[BRG] %s : %d\n", __FUNCTION__, __LINE__); + + /* + * Clamp the timeout at a maximum value (about 32 seconds with + * a 66MHz clock). *Nobody* should be delay()ing for anywhere + * near that length of time and if they are, they should be hung + * out to dry. + */ + if (n >= (0x80000000U / counts_per_usec)) + counts = (0x80000000U / counts_per_usec) - 1; + else + counts = n * counts_per_usec; + + first = omap3_timer_get_timecount(NULL); + + while (counts > 0) { + last = omap3_timer_get_timecount(NULL); + counts -= (int32_t)(last - first); + first = last; + } +} +#endif + + +/** + * cpu_startprofclock - Starts the profile clock + * + * + * RETURNS: + * nothing + */ +void +cpu_startprofclock(void) +{ + /* TODO: implement */ +} + + +/** + * cpu_startprofclock - Stop the profile clock + * + * + * RETURNS: + * nothing + */ +void +cpu_stopprofclock(void) +{ + /* TODO: implement */ +} diff --git a/sys/arm/omap/omap3/omap3var.h b/sys/arm/omap/omap3/omap3var.h new file mode 100644 index 0000000..78388a4 --- /dev/null +++ b/sys/arm/omap/omap3/omap3var.h @@ -0,0 +1,88 @@ +/*- + * 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 author may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 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. + */ + +#ifndef _OMAP3VAR_H_ +#define _OMAP3VAR_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + + +/* + * Random collection of functions and definitions ... needs cleanup + * + * + * + */ + + +void +omap3_post_filter_intr(void *arg); + + + + +struct omap3_softc { + device_t sc_dev; + bus_space_tag_t sc_iotag; + + /* Handle for the interrupt controller */ + bus_space_handle_t sc_intcps_ioh; +}; + +extern struct omap3_softc *g_omap3_softc; + + + +int +omap3_setup_intr_controller(struct omap3_softc *sc, const const int *irqs); + +void +omap3_clk_init(device_t dev, int prio); + +void +omap3_padconf_init(device_t dev, int prio); + + + + +#endif /* _OMAP3VAR_H_ */ diff --git a/sys/arm/omap/omap3/std.omap35xx b/sys/arm/omap/omap3/std.omap35xx new file mode 100644 index 0000000..04dd846 --- /dev/null +++ b/sys/arm/omap/omap3/std.omap35xx @@ -0,0 +1,21 @@ +# Cortex-A8 Omap3 generic configuration +#$FreeBSD$ +files "../omap/omap3/files.omap35xx" +include "../omap/std.omap" +#cpu CPU_CORTEXA8_OMAP3 +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_OMAP3 diff --git a/sys/arm/omap/omap3/uart_bus_omap3.c b/sys/arm/omap/omap3/uart_bus_omap3.c new file mode 100644 index 0000000..e0294d7 --- /dev/null +++ b/sys/arm/omap/omap3/uart_bus_omap3.c @@ -0,0 +1,228 @@ +/*- + * 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 author may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 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 + +#include +#include +#include + +#include +#include +#include +#include + +#include "uart_if.h" + +#define OMAP35XX_UART_FREQ 48000000 /* 48Mhz clock for all uarts (techref 17.3.1.1) */ + + + +static void +omap3_uart_init(device_t dev, int attach); + +extern SLIST_HEAD(uart_devinfo_list, uart_devinfo) uart_sysdevs; + + +/** + * omap3_uart_attach - attach function for the driver + * @dev: uart device handle + * + * Driver initialisation ... + * + * RETURNS: + * Returns 0 on success or an error code on failure. + */ +static int +omap3_uart_attach(device_t dev) +{ + int unit = device_get_unit(dev); + + device_printf(dev, "Attaching device %d\n", unit); + + /* TODO: ensure the interface clocks are running */ + + /* TODO: ensure the pin mappings are configured correctly */ + + omap3_uart_init(dev, 0); + int ret = uart_bus_attach(dev); + omap3_uart_init(dev, 1); + return (ret); +} + + +/** + * omap3_mmc_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 +omap3_uart_detach(device_t dev) +{ + int unit = device_get_unit(dev); + + device_printf(dev, "Detaching device %d\n", unit); + + return uart_bus_detach(dev); +} + + +#define OMAP4_UART_MDR1 0x8 +#define OMAP4_UART_SCR 0x10 +#define OMAP4_UART_MDR1_DISABLE 0x07 +#define OMAP4_UART_MDR1_MODE_16X 0 + + +static void +omap3_uart_init(device_t dev, int attach) +{ + vm_offset_t addr; + int clock; + + return; + switch(device_get_unit(dev)) { + case 0: + addr = OMAP44XX_UART1_VBASE; + clock = UART1_CLK; + break; + case 1: + addr = OMAP44XX_UART2_VBASE; + clock = UART2_CLK; + break; + case 2: + addr = OMAP44XX_UART3_VBASE; + clock = UART3_CLK; + break; + case 3: + addr = OMAP44XX_UART4_VBASE; + clock = UART4_CLK; + break; + default: + panic("Unknown uart"); + } + addr = OMAP44XX_UART3_VBASE; + clock = UART3_CLK; + if (!attach) { + + *(volatile char *)(addr + (OMAP4_UART_MDR1 << 2)) = OMAP4_UART_MDR1_DISABLE; +#if 0 + while (!((*(volatile int *)(addr + 0x58)) & 1)); +#endif + } + if (attach) + *(volatile char *)(addr + (OMAP4_UART_MDR1 << 2)) = OMAP4_UART_MDR1_MODE_16X; + omap_prcm_clk_enable(UART3_CLK); + DELAY(10000); + *(volatile char *)(addr + (2 << 2)) = 1 | 2 | 4; + DELAY(10000); + +} + +/** + * omap3_uart_probe - probe function for the driver + * @dev: uart device handle + * + * + * + * RETURNS: + * 0 on success, error code on failure. + */ +static int +omap3_uart_probe(device_t dev) +{ + struct uart_softc *sc; + int unit = device_get_unit(dev); + int rclk; + + device_printf(dev, "Probing device %d\n", unit); + + sc = device_get_softc(dev); + sc->sc_class = &uart_ns8250_class; + + if (resource_int_value("omap_uart", unit, "rclk", &rclk)) + rclk = OMAP35XX_UART_FREQ; + + // if (bootverbose) + device_printf(dev, "rclk %u\n", rclk); + //omap3_uart_init(dev, 0); + + + /* Depending on the device we need to assign it a register range and an + * IRQ number. + */ + //bus_set_resource(dev, SYS_RES_IRQ, 0, OMAP35XX_IRQ_UART1, 1); + //bus_set_resource(dev, SYS_RES_MEMORY, 0, OMAP35XX_UART3_HWBASE, OMAP35XX_UART3_SIZE); + + sc->sc_sysdev = SLIST_FIRST(&uart_sysdevs); + bcopy(&sc->sc_sysdev->bas, &sc->sc_bas, sizeof(sc->sc_bas)); + + + int ret = uart_bus_probe(dev, 2, rclk, 0, + device_get_unit(dev)); + //omap3_uart_init(dev, 1); + return ret; +} + + +static device_method_t omap3_uart_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, omap3_uart_probe), + DEVMETHOD(device_attach, omap3_uart_attach), + DEVMETHOD(device_detach, omap3_uart_detach), + { 0, 0 } +}; + + +static driver_t omap3_uart_driver = { + uart_driver_name, + omap3_uart_methods, + sizeof(struct uart_softc), +}; + +DRIVER_MODULE(uart, omap, omap3_uart_driver, uart_devclass, 0, 0); + + + diff --git a/sys/arm/omap/omap3/uart_cpu_omap3.c b/sys/arm/omap/omap3/uart_cpu_omap3.c new file mode 100644 index 0000000..38e9423 --- /dev/null +++ b/sys/arm/omap/omap3/uart_cpu_omap3.c @@ -0,0 +1,83 @@ +/*- + * Copyright (c) 2011 + * Ben Gray . + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. 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 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 + +#define OMAP35XX_UART_FREQ 48000000 /* 48Mhz clock for all uarts (techref 17.3.1.1) */ + +bus_space_tag_t uart_bus_space_io; +bus_space_tag_t uart_bus_space_mem; + + +/* --------------------------------------------------------------- */ +/* uart_cpu_eqres + * + * Checks if the two UART base addresses are equal, if they are + * 1 is returned, otherwise 0 is returned. + */ +int uart_cpu_eqres(struct uart_bas *b1, struct uart_bas *b2) +{ + return ((b1->bsh == b2->bsh && b1->bst == b2->bst) ? 1 : 0); +} + +/* --------------------------------------------------------------- */ +/* uart_cpu_getdev + * + * + * + */ +int uart_cpu_getdev(int devtype, struct uart_devinfo *di) +{ + di->ops = uart_getops(&uart_ns8250_class); + di->bas.chan = 0; + di->bas.bst = &omap_bs_tag; + di->bas.regshft = 2; + di->bas.rclk = OMAP35XX_UART_FREQ; + di->bas.bsh = OMAP35XX_UART3_VBASE; + di->baudrate = 115200; + di->databits = 8; + di->stopbits = 1; + di->parity = UART_PARITY_NONE; + uart_bus_space_mem = NULL; + uart_bus_space_io = &omap_bs_tag; + + return (0); +} diff --git a/sys/arm/omap/omap4/files.omap44xx b/sys/arm/omap/omap4/files.omap44xx new file mode 100644 index 0000000..b710ab9 --- /dev/null +++ b/sys/arm/omap/omap4/files.omap44xx @@ -0,0 +1,42 @@ +#$FreeBSD$ + +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/arm/cpufunc_asm_pj4b.S standard + +arm/omap/omap_machdep.c standard + +arm/omap/omap.c standard +arm/omap/omap_cpuid.c standard +arm/omap/omap_dma.c standard +arm/omap/omap_prcm.c standard +arm/omap/omap_scm.c standard +arm/omap/omap_gpio.c optional gpio +arm/omap/omap_gptimer.c standard +arm/omap/omap_dma.c standard +arm/omap/omap_mmc.c optional mmc +arm/omap/omap_i2c.c optional iic +arm/omap/omap_space_asm.S standard + +arm/omap/omap4/omap44xx.c standard +arm/omap/omap4/omap4_intr.c standard +arm/omap/omap4/omap4_prcm_clks.c standard +arm/omap/omap4/omap4_scm_padconf.c standard +arm/omap/omap4/omap4_secure.S standard +arm/omap/omap4/omap4_timer.c standard +arm/omap/omap4/omap4_l2cache.c standard + +arm/omap/omap3/uart_bus_omap3.c optional uart +arm/omap/omap4/uart_cpu_omap4.c optional uart + +dev/uart/uart_dev_ns8250.c optional uart + +# USB Host controller +arm/omap/omap_ehci.c optional ehci usb + +kern/kern_clocksource.c standard diff --git a/sys/arm/omap/omap4/omap44xx.c b/sys/arm/omap/omap4/omap44xx.c new file mode 100644 index 0000000..23730f1 --- /dev/null +++ b/sys/arm/omap/omap4/omap44xx.c @@ -0,0 +1,609 @@ +/*- + * 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 author may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 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 + +#define _ARM32_BUS_DMA_PRIVATE +#include + +#include +#include +#include + +struct omap4_softc *g_omap4_softc = NULL; + + + + + + +/* + * Standard priority levels for the system - 0 has the highest priority and 63 + * is the lowest. + * + * Currently these are all set to the same standard value. + */ +static const struct omap4_intr_conf omap44xx_irq_prio[] = +{ + { OMAP44XX_IRQ_L2CACHE, 0, 0x1}, /* L2 cache controller interrupt */ + { OMAP44XX_IRQ_CTI_0, 0, 0x1}, /* Cross-trigger module 0 (CTI0) interrupt */ + { OMAP44XX_IRQ_CTI_1, 0, 0x1}, /* Cross-trigger module 1 (CTI1) interrupt */ + { OMAP44XX_IRQ_ELM, 0, 0x1}, /* Error location process completion */ + { OMAP44XX_IRQ_SYS_NIRQ, 0, 0x1}, /* External source (active low) */ + { OMAP44XX_IRQ_L3_DBG, 0, 0x1}, /* L3 interconnect debug error */ + { OMAP44XX_IRQ_L3_APP, 0, 0x1}, /* L3 interconnect application error */ + { OMAP44XX_IRQ_PRCM_MPU, 0, 0x1}, /* PRCM module IRQ */ + { OMAP44XX_IRQ_SDMA0, 0, 0x1}, /* System DMA request 0(3) */ + { OMAP44XX_IRQ_SDMA1, 0, 0x1}, /* System DMA request 1(3) */ + { OMAP44XX_IRQ_SDMA2, 0, 0x1}, /* System DMA request 2 */ + { OMAP44XX_IRQ_SDMA3, 0, 0x1}, /* System DMA request 3 */ + { OMAP44XX_IRQ_MCBSP4, 0, 0x1}, /* McBSP module 4 IRQ */ + { OMAP44XX_IRQ_MCBSP1, 0, 0x1}, /* McBSP module 1 IRQ */ + { OMAP44XX_IRQ_SR1, 0, 0x1}, /* SmartReflex™ 1 */ + { OMAP44XX_IRQ_SR2, 0, 0x1}, /* SmartReflex™ 2 */ + { OMAP44XX_IRQ_GPMC, 0, 0x1}, /* General-purpose memory controller module */ + { OMAP44XX_IRQ_SGX, 0, 0x1}, /* 2D/3D graphics module */ + { OMAP44XX_IRQ_MCBSP2, 0, 0x1}, /* McBSP module 2 */ + { OMAP44XX_IRQ_MCBSP3, 0, 0x1}, /* McBSP module 3 */ + { OMAP44XX_IRQ_ISS5, 0, 0x1}, /* Imaging subsystem interrupt 5 */ + { OMAP44XX_IRQ_DSS, 0, 0x1}, /* Display subsystem module(3) */ + { OMAP44XX_IRQ_MAIL_U0, 0, 0x1}, /* Mailbox user 0 request */ + { OMAP44XX_IRQ_C2C_SSCM, 0, 0x1}, /* C2C status interrupt */ + { OMAP44XX_IRQ_DSP_MMU, 0, 0x1}, /* DSP MMU */ + { OMAP44XX_IRQ_GPIO1_MPU, 0, 0x1}, /* GPIO module 1(3) */ + { OMAP44XX_IRQ_GPIO2_MPU, 0, 0x1}, /* GPIO module 2(3) */ + { OMAP44XX_IRQ_GPIO3_MPU, 0, 0x1}, /* GPIO module 3(3) */ + { OMAP44XX_IRQ_GPIO4_MPU, 0, 0x1}, /* GPIO module 4(3) */ + { OMAP44XX_IRQ_GPIO5_MPU, 0, 0x1}, /* GPIO module 5(3) */ + { OMAP44XX_IRQ_GPIO6_MPU, 0, 0x1}, /* GPIO module 6(3) */ + { OMAP44XX_IRQ_WDT3, 0, 0x1}, /* Watchdog timer module 3 overflow */ + { OMAP44XX_IRQ_GPT1, 0, 0x1}, /* General-purpose timer module 1 */ + { OMAP44XX_IRQ_GPT2, 0, 0x1}, /* General-purpose timer module 2 */ + { OMAP44XX_IRQ_GPT3, 0, 0x1}, /* General-purpose timer module 3 */ + { OMAP44XX_IRQ_GPT4, 0, 0x1}, /* General-purpose timer module 4 */ + { OMAP44XX_IRQ_GPT5, 0, 0x1}, /* General-purpose timer module 5(3) */ + { OMAP44XX_IRQ_GPT6, 0, 0x1}, /* General-purpose timer module 6(3) */ + { OMAP44XX_IRQ_GPT7, 0, 0x1}, /* General-purpose timer module 7(3) */ + { OMAP44XX_IRQ_GPT8, 0, 0x1}, /* General-purpose timer module 8(3) */ + { OMAP44XX_IRQ_GPT9, 0, 0x1}, /* General-purpose timer module 9 */ + { OMAP44XX_IRQ_GPT10, 0, 0x1}, /* General-purpose timer module 10 */ + { OMAP44XX_IRQ_GPT11, 0, 0x1}, /* General-purpose timer module 11 */ + { OMAP44XX_IRQ_MCSPI4, 0, 0x1}, /* McSPI module 4 */ + { OMAP44XX_IRQ_DSS_DSI1, 0, 0x1}, /* Display Subsystem DSI1 interrupt */ + { OMAP44XX_IRQ_I2C1, 0, 0x1}, /* I2C module 1 */ + { OMAP44XX_IRQ_I2C2, 0, 0x1}, /* I2C module 2 */ + { OMAP44XX_IRQ_HDQ, 0, 0x1}, /* HDQ / One-wire */ + { OMAP44XX_IRQ_MMC5, 0, 0x1}, /* MMC5 interrupt */ + { OMAP44XX_IRQ_I2C3, 0, 0x1}, /* I2C module 3 */ + { OMAP44XX_IRQ_I2C4, 0, 0x1}, /* I2C module 4 */ + { OMAP44XX_IRQ_MCSPI1, 0, 0x1}, /* McSPI module 1 */ + { OMAP44XX_IRQ_MCSPI2, 0, 0x1}, /* McSPI module 2 */ + { OMAP44XX_IRQ_HSI_P1, 0, 0x1}, /* HSI Port 1 interrupt */ + { OMAP44XX_IRQ_HSI_P2, 0, 0x1}, /* HSI Port 2 interrupt */ + { OMAP44XX_IRQ_FDIF_3, 0, 0x1}, /* Face detect interrupt 3 */ + { OMAP44XX_IRQ_UART4, 0, 0x1}, /* UART module 4 interrupt */ + { OMAP44XX_IRQ_HSI_DMA, 0, 0x1}, /* HSI DMA engine MPU request */ + { OMAP44XX_IRQ_UART1, 0, 0x1}, /* UART module 1 */ + { OMAP44XX_IRQ_UART2, 0, 0x1}, /* UART module 2 */ + { OMAP44XX_IRQ_UART3, 0, 0x1}, /* UART module 3 (also infrared)(3) */ + { OMAP44XX_IRQ_PBIAS, 0, 0x1}, /* Merged interrupt for PBIASlite1 and 2 */ + { OMAP44XX_IRQ_OHCI, 0, 0x1}, /* OHCI controller HSUSB MP Host Interrupt */ + { OMAP44XX_IRQ_EHCI, 0, 0x1}, /* EHCI controller HSUSB MP Host Interrupt */ + { OMAP44XX_IRQ_TLL, 0, 0x1}, /* HSUSB MP TLL Interrupt */ + { OMAP44XX_IRQ_WDT2, 0, 0x1}, /* WDTIMER2 interrupt */ + { OMAP44XX_IRQ_MMC1, 0, 0x1}, /* MMC/SD module 1 */ + { OMAP44XX_IRQ_DSS_DSI2, 0, 0x1}, /* Display subsystem DSI2 interrupt */ + { OMAP44XX_IRQ_MMC2, 0, 0x1}, /* MMC/SD module 2 */ + { OMAP44XX_IRQ_MPU_ICR, 0, 0x1}, /* MPU ICR */ + { OMAP44XX_IRQ_C2C_GPI, 0, 0x1}, /* C2C GPI interrupt */ + { OMAP44XX_IRQ_FSUSB, 0, 0x1}, /* FS-USB - host controller Interrupt */ + { OMAP44XX_IRQ_FSUSB_SMI, 0, 0x1}, /* FS-USB - host controller SMI Interrupt */ + { OMAP44XX_IRQ_MCSPI3, 0, 0x1}, /* McSPI module 3 */ + { OMAP44XX_IRQ_HSUSB_OTG, 0, 0x1}, /* High-Speed USB OTG controller */ + { OMAP44XX_IRQ_HSUSB_OTG_DMA, 0, 0x1}, /* High-Speed USB OTG DMA controller */ + { OMAP44XX_IRQ_MMC3, 0, 0x1}, /* MMC/SD module 3 */ + { OMAP44XX_IRQ_MMC4, 0, 0x1}, /* MMC4 interrupt */ + { OMAP44XX_IRQ_SLIMBUS1, 0, 0x1}, /* SLIMBUS1 interrupt */ + { OMAP44XX_IRQ_SLIMBUS2, 0, 0x1}, /* SLIMBUS2 interrupt */ + { OMAP44XX_IRQ_ABE, 0, 0x1}, /* Audio back-end interrupt */ + { OMAP44XX_IRQ_CORTEXM3_MMU, 0, 0x1}, /* Cortex-M3 MMU interrupt */ + { OMAP44XX_IRQ_DSS_HDMI, 0, 0x1}, /* Display subsystem HDMI interrupt */ + { OMAP44XX_IRQ_SR_IVA, 0, 0x1}, /* SmartReflex IVA interrupt */ + { OMAP44XX_IRQ_IVAHD1, 0, 0x1}, /* Sync interrupt from iCONT2 (vDMA) */ + { OMAP44XX_IRQ_IVAHD2, 0, 0x1}, /* Sync interrupt from iCONT1 */ + { OMAP44XX_IRQ_IVAHD_MAILBOX0, 0, 0x1}, /* IVAHD mailbox interrupt */ + { OMAP44XX_IRQ_MCASP1, 0, 0x1}, /* McASP1 transmit interrupt */ + { OMAP44XX_IRQ_EMIF1, 0, 0x1}, /* EMIF1 interrupt */ + { OMAP44XX_IRQ_EMIF2, 0, 0x1}, /* EMIF2 interrupt */ + { OMAP44XX_IRQ_MCPDM, 0, 0x1}, /* MCPDM interrupt */ + { OMAP44XX_IRQ_DMM, 0, 0x1}, /* DMM interrupt */ + { OMAP44XX_IRQ_DMIC, 0, 0x1}, /* DMIC interrupt */ + { OMAP44XX_IRQ_SYS_NIRQ2, 0, 0x1}, /* External source 2 (active low) */ + { OMAP44XX_IRQ_KBD, 0, 0x1}, /* Keyboard controller interrupt */ + { -1, 0, 0 }, +}; + + +static const struct omap_cpu_dev omap44xx_devs[] = +{ + + /** + * OMAP44xx - General Purpose Timers + * This module provides interfaces to the general purpose timers. + */ + { .name = "omap_gptimer", + .unit = 0, + .mem = { { OMAP44XX_GPTIMER1_HWBASE, OMAP44XX_GPTIMER_SIZE }, + { OMAP44XX_GPTIMER2_HWBASE, OMAP44XX_GPTIMER_SIZE }, + { OMAP44XX_GPTIMER3_HWBASE, OMAP44XX_GPTIMER_SIZE }, + { OMAP44XX_GPTIMER4_HWBASE, OMAP44XX_GPTIMER_SIZE }, + { OMAP44XX_GPTIMER5_HWBASE, OMAP44XX_GPTIMER_SIZE }, + { OMAP44XX_GPTIMER6_HWBASE, OMAP44XX_GPTIMER_SIZE }, + { OMAP44XX_GPTIMER7_HWBASE, OMAP44XX_GPTIMER_SIZE }, + { OMAP44XX_GPTIMER8_HWBASE, OMAP44XX_GPTIMER_SIZE }, + { OMAP44XX_GPTIMER9_HWBASE, OMAP44XX_GPTIMER_SIZE }, + { OMAP44XX_GPTIMER10_HWBASE, OMAP44XX_GPTIMER_SIZE }, + { OMAP44XX_GPTIMER11_HWBASE, OMAP44XX_GPTIMER_SIZE }, + { 0, 0 } + }, + .irqs = { OMAP44XX_IRQ_GPT1, + OMAP44XX_IRQ_GPT2, + OMAP44XX_IRQ_GPT3, + OMAP44XX_IRQ_GPT4, + OMAP44XX_IRQ_GPT5, + OMAP44XX_IRQ_GPT6, + OMAP44XX_IRQ_GPT7, + OMAP44XX_IRQ_GPT8, + OMAP44XX_IRQ_GPT9, + OMAP44XX_IRQ_GPT10, + OMAP44XX_IRQ_GPT11, + -1, + }, + }, + + /** + * OMAP44xx - DMA + * This module provides interfaces to the direct memory access + */ + { .name = "omap_dma", + .unit = 0, + .mem = { { OMAP44XX_SDMA_HWBASE, OMAP44XX_SDMA_SIZE }, + { 0, 0 } + }, + .irqs = { OMAP44XX_IRQ_SDMA0, + OMAP44XX_IRQ_SDMA1, + OMAP44XX_IRQ_SDMA2, + OMAP44XX_IRQ_SDMA3, + -1, + }, + }, + /** + * OMAP44xx - GPIO + * There are 6 GPIO register sets, with each set representing 32 GPIO + * pins. + */ + { .name = "gpio", + .unit = 0, + .mem = { { OMAP44XX_GPIO1_HWBASE, OMAP44XX_GPIO1_SIZE }, + { OMAP44XX_GPIO2_HWBASE, OMAP44XX_GPIO2_SIZE }, + { OMAP44XX_GPIO3_HWBASE, OMAP44XX_GPIO3_SIZE }, + { OMAP44XX_GPIO4_HWBASE, OMAP44XX_GPIO4_SIZE }, + { OMAP44XX_GPIO5_HWBASE, OMAP44XX_GPIO5_SIZE }, + { OMAP44XX_GPIO6_HWBASE, OMAP44XX_GPIO6_SIZE }, + { 0, 0 } + }, + .irqs = { OMAP44XX_IRQ_GPIO1_MPU, + OMAP44XX_IRQ_GPIO2_MPU, + OMAP44XX_IRQ_GPIO3_MPU, + OMAP44XX_IRQ_GPIO4_MPU, + OMAP44XX_IRQ_GPIO5_MPU, + OMAP44XX_IRQ_GPIO6_MPU, + -1, + }, + }, + /** + * OMAP44xx - MMC/SDIO + * There are a total of 5 MMC modules on OMAP4, however at the mome + nt only + * 3 are enabled. + */ + { .name = "omap_mmc", + .unit = 0, + .mem = { { OMAP44XX_MMCHS1_HWBASE, OMAP44XX_MMCHS_SIZE }, + { 0, 0 } + }, + .irqs = { OMAP44XX_IRQ_MMC1, + -1, + }, + }, + { .name = "omap_mmc", + .unit = 1, + .mem = { { OMAP44XX_MMCHS2_HWBASE, OMAP44XX_MMCHS_SIZE }, + { 0, 0 } + }, + .irqs = { OMAP44XX_IRQ_MMC2, + -1, + }, + }, + { .name = "omap_mmc", + .unit = 2, + .mem = { { OMAP44XX_MMCHS3_HWBASE, OMAP44XX_MMCHS_SIZE }, + { 0, 0 } + }, + .irqs = { OMAP44XX_IRQ_MMC3, + -1, + }, + }, +#if 0 + { .name = "omap_mmc", + .unit = 3, + .mem = { { OMAP44XX_MMCHS4_HWBASE, OMAP44XX_MMCHS_SIZE }, + { 0, 0 } + }, + .irqs = { OMAP44XX_IRQ_MMC4, + -1, + }, + }, +#endif + { .name = "omap_mmc", + .unit = 4, + .mem = { { OMAP44XX_MMCHS5_HWBASE, OMAP44XX_MMCHS_SIZE }, + { 0, 0 } + }, + .irqs = { OMAP44XX_IRQ_MMC5, + -1, + }, + }, + + +#if 0 + { + .name = "uart", + .unit = 0, + .mem = { { OMAP44XX_UART1_HWBASE, OMAP44XX_UART1_SIZE }, + { 0, 0 } + }, + .irqs = { OMAP44XX_IRQ_UART1, + -1, + }, + }, + { + .name = "uart", + .unit = 1, + .mem = { { OMAP44XX_UART2_HWBASE, OMAP44XX_UART2_SIZE }, + { 0, 0 } + }, + .irqs = { OMAP44XX_IRQ_UART2, + -1, + }, + }, +#endif + { + .name = "uart", + .unit = 0, + .mem = { { OMAP44XX_UART3_HWBASE, OMAP44XX_UART3_SIZE }, + { 0, 0 } + }, + .irqs = { OMAP44XX_IRQ_UART3, + -1, + }, + }, +#if 0 + { + .name = "uart", + .unit = 3, + .mem = { { OMAP44XX_UART4_HWBASE, OMAP44XX_UART4_SIZE }, + { 0, 0 } + }, + .irqs = { OMAP44XX_IRQ_UART4, + -1, + }, + }, +#endif + /** + * OMAP44xx - USB EHCI + * The OMAP EHCI driver expects three different register sets, one for + * the actual EHCI registers and the other two control the interface. + */ + { .name = "ehci", + .unit = 0, + .mem = { { OMAP44XX_USB_EHCI_HWBASE, OMAP44XX_USB_EHCI_SIZE }, + { OMAP44XX_USB_UHH_HWBASE, OMAP44XX_USB_UHH_SIZE }, + { OMAP44XX_USB_TLL_HWBASE, OMAP44XX_USB_TLL_SIZE }, + { 0, 0 } + }, + .irqs = { OMAP44XX_IRQ_EHCI, + -1, + }, + }, + { 0, 0, { { 0, 0 } }, { -1 } } +}; + + +/** + * omap_sdram_size - called from machdep to get the total memory size + * + * Since this function is called very early in the boot, there is none of the + * bus handling code setup. However the boot device map will be setup, so + * we can directly access registers already mapped. + * + * This is a bit ugly, but since we need this information early on and the + * only way to get it (appart from hardcoding it or via kernel args) is to read + * from the EMIF_SRAM registers. + * + * RETURNS: + * The size of memory in bytes. + */ +unsigned int +omap_sdram_size(void) +{ + uint32_t sdram_cfg; + uint32_t ibank, ebank, pagesize; + + /* Read the two SDRAM config register */ + sdram_cfg = *(volatile uint32_t*)(OMAP44XX_L3_EMIF1_VBASE + 0x0008); + + /* Read the REG_EBANK, REG_IBANK and REG_PAGESIZE - together these tell + * us the maximum size of memory we can access. + */ + ibank = (sdram_cfg >> 4) & 0x7; + ebank = (sdram_cfg >> 3) & 0x1; + pagesize = (sdram_cfg >> 0) & 0x7; + +printf("[BRG] omap_sdram_size : ibank = %u : ebank = %u : pagesize = %u\n", + ibank, ebank, pagesize); + + /* Using the above read values we determine the memory size, this was + * gleened from tables 15-106 and 15-107 in the omap44xx tech ref manual. + */ + return (1UL << (25 + ibank + ebank + pagesize)); +} + + + + +/** + * omap44xx_add_child - add a child item to the root omap device + * @dev: the parent device + * @order: defines roughly the order with which to add the child to the parent + * @name: the name to give to the child item + * @unit: the unit number for the device + * @addr: the base address of the register set for device + * @size: the number of a bytes in register set + * @irq: the IRQ number(s) for the device + * + * Adds a child to the omap base device. + * + */ +static void +omap44xx_add_child(device_t dev, int prio, const char *name, int unit, + const struct omap_mem_range mem[], const int irqs[]) +{ + device_t kid; + struct omap_ivar *ivar; + unsigned int i; + + if (!strcmp(name, "uart")) { + printf("ADDING UART\n"); + } + printf("ADDING DEVICE %s\n", name); + /* Start by adding the actual child to the parent (us) */ + kid = device_add_child_ordered(dev, prio, name, unit); + if (kid == NULL) { + printf("Can't add child %s%d ordered\n", name, unit); + return; + } + + /* Allocate some memory for the omap_ivar structure */ + ivar = malloc(sizeof(*ivar), M_DEVBUF, M_NOWAIT | M_ZERO); + if (ivar == NULL) { + device_delete_child(dev, kid); + printf("Can't add alloc ivar\n"); + return; + } + + /* Assign the ivars to the child item and populate with the device resources */ + device_set_ivars(kid, ivar); + + /* Assign the IRQ(s) in the resource list */ + resource_list_init(&ivar->resources); + if (irqs) { + for (i = 0; *irqs != -1; i++, irqs++) { + bus_set_resource(kid, SYS_RES_IRQ, i, *irqs, 1); + } + } + + /* Assign the memory region to the resource list */ + if (mem) { + for (i = 0; mem->base != 0; i++, mem++) { + if (!strcmp(name, "uart")) + printf("ADDING MEM FOR UART\n"); + bus_set_resource(kid, SYS_RES_MEMORY, i, mem->base, mem->size); + } + } +} + + +/** + * omap44xx_cpu_add_builtin_children - adds the SoC child components + * @dev: this device, the one we are adding to + * + * Adds child devices from the omap44xx_devs list. + * + */ +static void +omap44xx_cpu_add_builtin_children(device_t dev) +{ + int i; + const struct omap_cpu_dev *walker; + + /* Setup all the clock devices - this is not the tick timers, rather it's + * the individual functional and interface clocks for the SoC modules. + */ + omap4_clk_init(dev, 1); + + /* Setup the system control module driver, which basically is just the + * padconf (pinmux) for the OMAP44xx devices. + */ + omap4_padconf_init(dev, 1); + + /* Add the rest of the children from the array above */ + for (i = 5, walker = omap44xx_devs; walker->name; i++, walker++) { + omap44xx_add_child(dev, i, walker->name, walker->unit, + walker->mem, walker->irqs); + } +} + + +/** + * omap44xx_identify - adds the SoC child components + * @dev: this device, the one we are adding to + * + * Adds a child to the omap3 base device. + * + */ +static void +omap44xx_identify(driver_t *drv, device_t parent) +{ + int omap44xx_irqs[] = { 27, 29, -1 }; + struct omap_mem_range omap44xx_mem[] = { { OMAP44XX_MPU_SUBSYS_HWBASE, + OMAP44XX_MPU_SUBSYS_SIZE }, + { 0, 0 } }; + + /* Add the resources for this "omap44xx" device, we add the memory + * and IRQ resources for the ARM cortex devices (timers, l2cache, etc). + */ + omap44xx_add_child(parent, 0, "omap44xx", 0, omap44xx_mem, omap44xx_irqs); + + /* Add the other SOC components */ + omap44xx_cpu_add_builtin_children(parent); +} + +/** + * omap44xx_probe - called when the device is probed + * @dev: the new device + * + * All we do in this routine is set the description of this device + * + */ +static int +omap44xx_probe(device_t dev) +{ + device_set_desc(dev, "TI OMAP44XX"); + return (0); +} + +/** + * omap44xx_attach - called when the device is attached + * @dev: the new device + * + * All we do in this routine is set the description of this device + * + */ +static int +omap44xx_attach(device_t dev) +{ + struct omap_softc *omapsc = device_get_softc(device_get_parent(dev)); + struct omap4_softc *sc = device_get_softc(dev); + + sc->sc_iotag = omapsc->sc_iotag; + sc->sc_dev = dev; + + + /* Map in the Generic Interrupt Controller (GIC) register set */ + if (bus_space_map(sc->sc_iotag, OMAP44XX_GIC_CPU_HWBASE, + OMAP44XX_GIC_CPU_SIZE, 0, &sc->sc_gic_cpu_ioh)) { + panic("%s: Cannot map registers", device_get_name(dev)); + } + + /* Also map in the CPU GIC register set */ + if (bus_space_map(sc->sc_iotag, OMAP44XX_GIC_DIST_HWBASE, + OMAP44XX_GIC_DIST_SIZE, 0, &sc->sc_gic_dist_ioh)) { + panic("%s: Cannot map registers", device_get_name(dev)); + } + + /* And the private and global timer register set */ + if (bus_space_map(sc->sc_iotag, OMAP44XX_PRV_TIMER_HWBASE, + OMAP44XX_PRV_TIMER_SIZE, 0, &sc->sc_prv_timer_ioh)) { + panic("%s: Cannot map registers", device_get_name(dev)); + } + if (bus_space_map(sc->sc_iotag, OMAP44XX_GBL_TIMER_HWBASE, + OMAP44XX_GBL_TIMER_SIZE, 0, &sc->sc_gbl_timer_ioh)) { + panic("%s: Cannot map registers", device_get_name(dev)); + } + + /* Map in the PL310 (L2 cache controller) as well */ + if (bus_space_map(sc->sc_iotag, OMAP44XX_PL310_HWBASE, + OMAP44XX_PL310_SIZE, 0, &sc->sc_pl310_ioh)) { + panic("%s: Cannot map registers", device_get_name(dev)); + } + + + + /* Save the device structure globally for the IRQ handling */ + g_omap4_softc = sc; + + /* Setup the PL310 L2 cache controller */ + omap4_setup_l2cache_controller(g_omap4_softc); + + /* TODO: Revisit - Install an interrupt post filter */ + arm_post_filter = omap4_post_filter_intr; + + /* Setup the ARM GIC interrupt controller */ + omap4_setup_intr_controller(g_omap4_softc, omap44xx_irq_prio); + omap4_setup_gic_cpu(g_omap4_softc, 0xF0); + + return (0); +} + + + +static device_method_t omap44xx_methods[] = { + DEVMETHOD(device_probe, omap44xx_probe), + DEVMETHOD(device_attach, omap44xx_attach), + DEVMETHOD(device_identify, omap44xx_identify), + {0, 0}, +}; + +static driver_t omap44xx_driver = { + "omap44xx", + omap44xx_methods, + sizeof(struct omap4_softc), +}; + +static devclass_t omap44xx_devclass; + +DRIVER_MODULE(omap44xx, omap, omap44xx_driver, omap44xx_devclass, 0, 0); diff --git a/sys/arm/omap/omap4/omap44xx_reg.h b/sys/arm/omap/omap4/omap44xx_reg.h new file mode 100644 index 0000000..97abda9 --- /dev/null +++ b/sys/arm/omap/omap4/omap44xx_reg.h @@ -0,0 +1,585 @@ +/* + * 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 author may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 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. + */ + +/* + * 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/omap/omap4/omap4_intr.c b/sys/arm/omap/omap4/omap4_intr.c new file mode 100644 index 0000000..31f82dd --- /dev/null +++ b/sys/arm/omap/omap4/omap4_intr.c @@ -0,0 +1,336 @@ +/*- + * 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 + + + +/* + * There are a number of ways that interrupt handling is implemented in + * the various ARM platforms, the PXA has the neatest way, it creates another + * device driver that handles everything. However IMO this is rather heavy- + * weight for playing with IRQs which should be quite fast ... so I've + * gone for something similar to the IXP425, which just directly plays with + * registers. This assumes that the interrupt control registers are already + * mapped in virtual memory at a fixed virtual address ... simplies. + * + * The OMAP4xxx processors use the ARM Generic Interrupt Controller (GIC), so + * this file/driver isn't really specific to the OMAP4 series of processors it + * is potentially much broader. However I haven't used any other devices with + * a GIC ... I guess if other platforms would like to use this driver it could + * be made more generic (as the name implies ;-) + * + * + */ + +/* GIC Distributor register map */ +#define ARM_GIC_ICDDCR 0x0000 +#define ARM_GIC_ICDICTR 0x0004 +#define ARM_GIC_ICDIIDR 0x0008 +#define ARM_GIC_ICDISR 0x0080 +#define ARM_GIC_ICDISER(n) (0x0100 + ((n) * 4)) +#define ARM_GIC_ICDICER(n) (0x0180 + ((n) * 4)) +#define ARM_GIC_ICDISPR(n) (0x0200 + ((n) * 4)) +#define ARM_GIC_ICDICPR(n) (0x0280 + ((n) * 4)) +#define ARM_GIC_ICDABR(n) (0x0300 + ((n) * 4)) +#define ARM_GIC_ICDIPR(n) (0x0400 + ((n) * 4)) +#define ARM_GIC_ICDIPTR(n) (0x0800 + ((n) * 4)) +#define ARM_GIC_ICDICFR(n) (0x0C00 + ((n) * 4)) +#define ARM_GIC_ICDSGIR 0x0F00 + +/* GIC CPU/Processor register map */ +#define ARM_GIC_ICCICR 0x0000 +#define ARM_GIC_ICCPMR 0x0004 +#define ARM_GIC_ICCBPR 0x0008 +#define ARM_GIC_ICCIAR 0x000C +#define ARM_GIC_ICCEOIR 0x0010 +#define ARM_GIC_ICCRPR 0x0014 +#define ARM_GIC_ICCHPIR 0x0018 +#define ARM_GIC_ICCABPR 0x001C +#define ARM_GIC_ICCIIDR 0x00FC + + + + + + + +/** + * omap4_mask_all_intr - masks all interrupts + * + * Called during initialisation to ensure all interrupts are masked before + * globally enabling interrupts. Should only be used at startup time. + * + * RETURNS: + * nothing + */ +void +omap4_mask_all_intr(void) +{ + unsigned int i = 0; + + bus_space_write_4(g_omap4_softc->sc_iotag, g_omap4_softc->sc_gic_dist_ioh, + ARM_GIC_ICDICER(i), 0xFFFFFFFF); + +} + + +/** + * omap4_setup_gic_distributor - configures and enables the distributor part + * of the generic interrupt controller (GIC). + * + * Enables and configures the distributor part of the GIC by programming the + * interrupt priorities and target CPU's. It also ensures the enable bit is + * set at the end of the process. + * + * RETURNS: + * nothing + */ +int +omap4_setup_intr_controller(struct omap4_softc *sc, + const struct omap4_intr_conf *irqs) +{ + u_int oldirqstate; + uint32_t reg_off, bit_off; + uint32_t icdicpr, icdiptr, icdictr; + uint32_t nirqs, i; + + /* Disable interrupts while configuring the controller */ + oldirqstate = disable_interrupts(I32_bit); + + /* Disable interrupt distribution while we are doing the work */ + bus_space_write_4(sc->sc_iotag, sc->sc_gic_dist_ioh, ARM_GIC_ICDDCR, 0x00); + + + /* Get the number of interrupts */ + icdictr = bus_space_read_4(sc->sc_iotag, sc->sc_gic_dist_ioh, ARM_GIC_ICDICTR); + nirqs = 32 * ((icdictr & 0x1f) + 1); + + + /* Set all global interrupts to be level triggered, active low. */ + for (i = 32; i < nirqs; i += 32) { + bus_space_write_4(sc->sc_iotag, sc->sc_gic_dist_ioh, + ARM_GIC_ICDICFR(i >> 5), 0x00000000); + } + + /* Disable all interrupts. */ + for (i = 32; i < nirqs; i += 32) { + bus_space_write_4(sc->sc_iotag, sc->sc_gic_dist_ioh, + ARM_GIC_ICDICER(i >> 5), 0xffffffff); + } + + /* Program the interrupt priorites and target CPU(s) */ + for (; irqs->num != -1; irqs++) { + reg_off = (irqs->num >> 2); + bit_off = (irqs->num & 0x3) << 3; + + /* Read the register, mask out the priority bits and then or in */ + icdicpr = bus_space_read_4(sc->sc_iotag, sc->sc_gic_dist_ioh, + ARM_GIC_ICDICPR(reg_off)); + + icdicpr &= ~(0xFFUL << bit_off); + icdicpr |= ((irqs->priority & 0xFF) << bit_off); + + bus_space_write_4(sc->sc_iotag, sc->sc_gic_dist_ioh, + ARM_GIC_ICDICPR(reg_off), icdicpr); + + + /* Read the register, mask out the target cpu bits and then or in */ + icdiptr = bus_space_read_4(sc->sc_iotag, sc->sc_gic_dist_ioh, + ARM_GIC_ICDIPTR(reg_off)); + + icdiptr &= ~(0xFFUL << bit_off); + icdiptr |= ((irqs->target_cpu & 0xFF) << bit_off); + + bus_space_write_4(sc->sc_iotag, sc->sc_gic_dist_ioh, + ARM_GIC_ICDIPTR(reg_off), icdiptr); + } + + /* Enable interrupt distribution */ + bus_space_write_4(sc->sc_iotag, sc->sc_gic_dist_ioh, ARM_GIC_ICDDCR, 0x01); + + /* Re-enable interrupts */ + restore_interrupts(oldirqstate); + + return (0); +} + +/** + * omap4_setup_gic_cpu - configures and enables the per-CPU part of the generic + * interrupt controller (GIC). + * + * Enables and configures the per-CPU part of the GIC by enabling interrupt + * routing and setting the default interrupt priority filter. + * + * RETURNS: + * nothing + */ +int +omap4_setup_gic_cpu(struct omap4_softc *sc, unsigned int prio_mask) +{ + uint32_t dcr; + uint32_t ictr; + uint32_t iidr; + + dcr = bus_space_read_4(sc->sc_iotag, sc->sc_gic_cpu_ioh, ARM_GIC_ICCICR); + ictr = bus_space_read_4(sc->sc_iotag, sc->sc_gic_cpu_ioh, ARM_GIC_ICCPMR); + iidr = bus_space_read_4(sc->sc_iotag, sc->sc_gic_cpu_ioh, ARM_GIC_ICCBPR); + + /* Disable interrupt to this CPU while we are doing the work */ + bus_space_write_4(sc->sc_iotag, sc->sc_gic_dist_ioh, ARM_GIC_ICCICR, 0x00); + + /* Enable interrupt to this CPU */ + bus_space_write_4(sc->sc_iotag, sc->sc_gic_dist_ioh, ARM_GIC_ICCICR, 0x01); + + return (0); +} + + + +/** + * omap3_post_filter_intr - called after the IRQ has been filtered + * @arg: the IRQ number + * + * Called after the interrupt handler has done it's stuff, can be used to + * clean up interrupts that haven't been handled properly. + * + * + * RETURNS: + * nothing + */ +void +omap4_post_filter_intr(void *arg) +{ + /* uintptr_t irq = (uintptr_t) arg; */ + + /* data synchronization barrier */ + cpu_drain_writebuf(); +} + + + + + +/** + * arm_mask_irq - masks an IRQ (disables it) + * @nb: the number of the IRQ to mask (disable) + * + * Disables the interrupt at the HW level. + * + * + * RETURNS: + * nothing + */ +void +arm_mask_irq(uintptr_t nb) +{ + /* Write the bit corresponding to the IRQ into the "Clear-Enable Register" */ + bus_space_write_4(g_omap4_softc->sc_iotag, g_omap4_softc->sc_gic_dist_ioh, + ARM_GIC_ICDICER(nb >> 5), (1UL << (nb & 0x1F))); +} + + +/** + * arm_unmask_irq - unmasks an IRQ (enables it) + * @nb: the number of the IRQ to unmask (enable) + * + * Enables the interrupt at the HW level. + * + * + * RETURNS: + * nothing + */ +void +arm_unmask_irq(uintptr_t nb) +{ + /* Write the bit corresponding to the IRQ into the "Set-Enable Register" */ + bus_space_write_4(g_omap4_softc->sc_iotag, g_omap4_softc->sc_gic_dist_ioh, + ARM_GIC_ICDISER(nb >> 5), (1UL << (nb & 0x1F))); +} + + +/** + * arm_get_next_irq - gets the next tripped interrupt + * @last_irq: the number of the last IRQ processed + * + * This function gets called when an interrupt is tripped, it is initially + * passed -1 in the last_irq argument and is suppose to return the IRQ number. + * The following is psuedo-code for how this is implemented in the core: + * + * irq_handler() + * { + * i = -1; + * while((i = arm_get_next_irq(i)) != -1) + * process_irq(i); + * } + * + * + * RETURNS: + * Returns the IRQ number or -1 if a spurious interrupt was detected + */ +int +arm_get_next_irq(int last_irq) +{ + uint32_t active_irq; + + /* clean-up the last IRQ */ + if (last_irq != -1) { + + /* write the last IRQ number to the end of interrupt register */ + bus_space_write_4(g_omap4_softc->sc_iotag, g_omap4_softc->sc_gic_cpu_ioh, + ARM_GIC_ICCEOIR, last_irq); + } + + /* Get the next active interrupt */ + active_irq = bus_space_read_4(g_omap4_softc->sc_iotag, + g_omap4_softc->sc_gic_cpu_ioh, + ARM_GIC_ICCIAR); + active_irq &= 0x3FF; + + /* Check for spurious interrupt */ + if (active_irq == 0x3FF) { + if (last_irq == -1) + printf("Spurious interrupt detected [0x%08x]\n", active_irq); + return -1; + } + + /* Return the new IRQ */ + return active_irq; +} diff --git a/sys/arm/omap/omap4/omap4_l2cache.c b/sys/arm/omap/omap4/omap4_l2cache.c new file mode 100644 index 0000000..d3a394a --- /dev/null +++ b/sys/arm/omap/omap4/omap4_l2cache.c @@ -0,0 +1,678 @@ +/*- + * 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 + + + +/* + * The OMAP4xxx processors use the ARM PL310 L2 Cache Controller, it is + * documented in an ARM document entitled "PL310 Cache Controller". + * + * As with the interrupt controller, this driver is much more generic than + * just the OMAP4 devices and perhaps it will be better to put this driver + * in the arm common directory as it's quite likely other Corext-A9 chips + * will use the PL310 L2 cache controller as well. + * + * + */ + + + +/** + * PL310 - L2 Cache Controller register offsets. + * + */ +#define PL310_CACHE_ID 0x000 +#define PL310_CACHE_TYPE 0x004 +#define PL310_CTRL 0x100 +#define PL310_AUX_CTRL 0x104 +#define PL310_EVENT_COUNTER_CTRL 0x200 +#define PL310_EVENT_COUNTER1_CONF 0x204 +#define PL310_EVENT_COUNTER0_CONF 0x208 +#define PL310_EVENT_COUNTER1_VAL 0x20C +#define PL310_EVENT_COUNTER0_VAL 0x210 +#define PL310_INTR_MASK 0x214 +#define PL310_MASKED_INTR_STAT 0x218 +#define PL310_RAW_INTR_STAT 0x21C +#define PL310_INTR_CLEAR 0x220 +#define PL310_CACHE_SYNC 0x730 +#define PL310_INV_LINE_PA 0x770 +#define PL310_INV_WAY 0x77C +#define PL310_CLEAN_LINE_PA 0x7B0 +#define PL310_CLEAN_LINE_IDX 0x7B8 +#define PL310_CLEAN_WAY 0x7BC +#define PL310_CLEAN_INV_LINE_PA 0x7F0 +#define PL310_CLEAN_INV_LINE_IDX 0x7F8 +#define PL310_CLEAN_INV_WAY 0x7FC +#define PL310_LOCKDOWN_D_WAY(x) (0x900 + ((x) * 8)) +#define PL310_LOCKDOWN_I_WAY(x) (0x904 + ((x) * 8)) +#define PL310_LOCKDOWN_LINE_ENABLE 0x950 +#define PL310_UNLOCK_ALL_LINES_WAY 0x954 +#define PL310_ADDR_FILTER_START 0xC00 +#define PL310_ADDR_FILTER_END 0xC04 +#define PL310_DEBUG_CTRL 0xF40 + + +#define PL310_AUX_CTRL_MASK 0xc0000fff +#define PL310_AUX_CTRL_ASSOCIATIVITY_SHIFT 16 +#define PL310_AUX_CTRL_WAY_SIZE_SHIFT 17 +#define PL310_AUX_CTRL_WAY_SIZE_MASK (0x3 << 17) +#define PL310_AUX_CTRL_SHARE_OVERRIDE_SHIFT 22 +#define PL310_AUX_CTRL_NS_LOCKDOWN_SHIFT 26 +#define PL310_AUX_CTRL_NS_INT_CTRL_SHIFT 27 +#define PL310_AUX_CTRL_DATA_PREFETCH_SHIFT 28 +#define PL310_AUX_CTRL_INSTR_PREFETCH_SHIFT 29 +#define PL310_AUX_CTRL_EARLY_BRESP_SHIFT 30 + + +/** + * ERRATAs - The following errata's are believed to apply to the PL310 L2 + * cache controller. + * + * Disclaimer: I can't access the ARM errata's for the PL310 so the following + * description is taken from the linux kernel. The presumption is that it's + * accurate. + * + */ + + +/** + * ERRATA 588369 + * + * The PL310 L2 cache controller implements three types of Clean & + * Invalidate maintenance operations: by Physical Address + * (offset 0x7F0), by Index/Way (0x7F8) and by Way (0x7FC). + * They are architecturally defined to behave as the execution of a + * clean operation followed immediately by an invalidate operation, + * both performing to the same memory location. This functionality + * is not correctly implemented in PL310 as clean lines are not + * invalidated as a result of these operations. + */ +#define PL310_ERRATA_588369 + +/** + * ERRATA 727915 + * + * PL310 implements the Clean & Invalidate by Way L2 cache maintenance + * operation (offset 0x7FC). This operation runs in background so that + * PL310 can handle normal accesses while it is in progress. Under very + * rare circumstances, due to this erratum, write data can be lost when + * PL310 treats a cacheable write transaction during a Clean & + * Invalidate by Way operation. + * + * Workaround: + * Disable Write-Back and Cache Linefill (Debug Control Register) + * Clean & Invalidate by Way (0x7FC) + * Re-enable Write-Back and Cache Linefill (Debug Control Register) + */ +#define PL310_ERRATA_727915 + + + + +void omap4_l2cache_wbinv_range(vm_paddr_t physaddr, vm_size_t size); +void omap4_l2cache_inv_range(vm_paddr_t physaddr, vm_size_t size); +void omap4_l2cache_wb_range(vm_paddr_t physaddr, vm_size_t size); +void omap4_l2cache_wbinv_all(void); +void omap4_l2cache_inv_all(void); +void omap4_l2cache_wb_all(void); + +static uint32_t g_l2cache_way_mask; + +static const uint32_t g_l2cache_line_size = 32; +static const uint32_t g_l2cache_align_mask = (32 - 1); + +static uint32_t g_l2cache_size; + + + +/** + * omap4_l2cache_readl - read a 32-bit value from the PL310 registers + * omap4_l2cache_writel - write a 32-bit value from the PL310 registers + * @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 +omap4_l2cache_readl(bus_size_t off) +{ + return bus_space_read_4(g_omap4_softc->sc_iotag, + g_omap4_softc->sc_pl310_ioh, off); +} +static inline void +omap4_l2cache_writel(bus_size_t off, uint32_t val) +{ + bus_space_write_4(g_omap4_softc->sc_iotag, g_omap4_softc->sc_pl310_ioh, + off, val); +} + + +#if defined(PL310_ERRATA_588369) || defined(PL310_ERRATA_727915) +/** + * omap4_l2cache_errata_wa_write_debug - writes to the debug register + * @val: the value to write into the debug register + * + * This function is needed for errata's 588369 and 727915, it writes to the + * PL310 debug register typically so that the write-back and cache linefill + * are disabled. + * + * Note we can't do a direct write to the register because it's in the secure + * monitor region. So instead use the smc instruction. + * + * LOCKING: + * None + * + * RETURNS: + * nothing + */ +static inline void +omap4_l2cache_errata_wa_write_debug(uint32_t val) +{ + /* Program PL310 L2 Cache controller debug register */ + omap4_smc_call(0x100, val); +} +#else +static inline void +omap4_l2cache_errata_wa_write_debug(uint32_t val) +{ +} +#endif + + +/** + * omap4_l2cache_wait_background_op - waits for a background operation to finish + * @reg: the offset of the register to poll + * @mask: returns when all the bits in this mask are not set in the register + * + * For background operations (Invalidate by Way, Clean by Way or Clean and + * Invalidate by Way) you need to poll on the register bits to determine when + * the operation has finished. + * + * + */ +static inline void +omap4_l2cache_wait_background_op(uint32_t reg, uint32_t mask) +{ + while (omap4_l2cache_readl(reg) & mask) + ; +} + + +/** + * omap4_l2cache_cache_sync - performs a cache sync operation + * + * According to the TRM: + * + * "Before writing to any other register you must perform an explicit + * Cache Sync operation. This is particularly important when the cache is + * enabled and changes to how the cache allocates new lines are to be made." + * + * + */ +static inline void +omap4_l2cache_cache_sync(void) +{ + omap4_l2cache_writel(PL310_CACHE_SYNC, 0); +} + + +/** + * omap4_l2cache_wbinv_line - performs a cache flush (write back + invalidate) + * @phys_addr - the physical address of the line to flush + * + * There are a couple of errata's that apply to the cache flush operation; + * 588369 and 727915. Which basically means the the wbinv operation needs + * to be wrapped in functions that disable the write-back and cache fill + * operations. + * + */ +static inline void +omap4_l2cache_wbinv_line(unsigned long physaddr) +{ +#if defined(PL310_ERRATA_588369) + /* TODO: Investigate errata 588369 */ + omap4_l2cache_writel(PL310_CLEAN_LINE_PA, physaddr); + omap4_l2cache_wait_background_op(PL310_CLEAN_LINE_PA, + g_l2cache_way_mask); + omap4_l2cache_writel(PL310_INV_LINE_PA, physaddr); + omap4_l2cache_wait_background_op(PL310_INV_LINE_PA, + g_l2cache_way_mask); +#else + omap4_l2cache_writel(PL310_CLEAN_INV_LINE_PA, physaddr); + omap4_l2cache_wait_background_op(PL310_CLEAN_INV_LINE_PA, + g_l2cache_way_mask); +#endif +} + +/** + * omap4_l2cache_inv_line - performs a cache sync operation + * + * + * + */ +static inline void +omap4_l2cache_inv_line(unsigned long physaddr) +{ + omap4_l2cache_writel(PL310_INV_LINE_PA, physaddr); + omap4_l2cache_wait_background_op(PL310_INV_LINE_PA, + g_l2cache_way_mask); +} + +/** + * omap4_l2cache_wb_line - performs a cache sync operation + * + * + * + */ +static inline void +omap4_l2cache_wb_line(unsigned long physaddr) +{ + omap4_l2cache_writel(PL310_CLEAN_LINE_PA, physaddr); + omap4_l2cache_wait_background_op(PL310_CLEAN_LINE_PA, + g_l2cache_way_mask); +} + + + +/** + * omap4_l2cache_wbinv_all - writes back and invalidates the entire cache + * + * Writes back and invalidates the entire cache by using the "Clean and + * Invalidate by Way" register and masking all ways. + * + */ +void +omap4_l2cache_wbinv_all(void) +{ + /* TODO: Add spinlocking when multicores is enabled */ + + /* Errata: disable write back and cache line fill */ + omap4_l2cache_errata_wa_write_debug(0x03); + + omap4_l2cache_writel(PL310_CLEAN_INV_WAY, g_l2cache_way_mask); + omap4_l2cache_wait_background_op(PL310_CLEAN_INV_WAY, g_l2cache_way_mask); + omap4_l2cache_cache_sync(); + + omap4_l2cache_errata_wa_write_debug(0x00); +} + + +/** + * omap4_l2cache_inv_all - invalidates the entire cache + * + * Invalidates the entire cache by using the "Invalidate by Way" register + * and masking all ways. + * + */ +void +omap4_l2cache_inv_all(void) +{ + /* TODO: Add spinlocking when multicores is enabled */ + + KASSERT(omap4_l2cache_readl(PL310_CTRL) & 0x1, ("l2cache enabled!")); + omap4_l2cache_writel(PL310_INV_WAY, g_l2cache_way_mask); + omap4_l2cache_wait_background_op(PL310_INV_WAY, g_l2cache_way_mask); + omap4_l2cache_cache_sync(); +} + + +/** + * omap4_l2cache_wb_all - writes back the entire cache + * + * Invalidates the entire cache by using the "Invalidate by Way" register + * and masking all ways. + * + */ +void +omap4_l2cache_wb_all(void) +{ + /* TODO: Add spinlocking when multicores is enabled */ + + omap4_l2cache_writel(PL310_CLEAN_WAY, g_l2cache_way_mask); + omap4_l2cache_wait_background_op(PL310_CLEAN_WAY, g_l2cache_way_mask); + omap4_l2cache_cache_sync(); +} + + + + + +/** + * omap4_l2cache_inv_range - invalidates a range of addresses + * @physaddr: physical address of the start of the range + * @size: size of the range in bytes + * + * Invalidates a virtual memory range from the L2 cache. + * + * Because the supplied address is virtual we need to convert to a physical + * address before issuing the clean cache instructions. + * + */ +void +omap4_l2cache_inv_range(vm_paddr_t physaddr, vm_size_t size) +{ + vm_paddr_t start; + vm_paddr_t end; + + /* TODO: Add spinlocking when multicores is enabled */ + + start = physaddr; + end = start + size; + + /* If the start or end addresses are not aligned on a cache line boundary + * we have a problem, in that by invalidating the whole cache line we will + * be invalidating valid data without writing it back to main memory. + * + * It is tempting to just do a write-back + invalidate on those cache lines, + * but we can't because for BUS_DMASYNC_POSTREAD type invalidation, the + * write-back will write invalidate data back to main memory. + * + * Unfortunatly there is no way to really solve this currently, I guess + * ideally we would like to get the virtual memory address and then we + * could take a temporary copy of the valid data before invalidating the + * entire line and then copy it back after the invalidate call. However + * since there is no nice way to get the virtual address we are a bit stuck. + * + */ + if ((start & g_l2cache_align_mask) || (end & g_l2cache_align_mask)) { + panic("l2-cache invalidate range not on cache line boundary\n"); + } + + + /* Invalidate all the addresses in the range */ + while (start < end) { + + /* TODO: In linux they release the spinlock after invalidating each page */ + + omap4_l2cache_inv_line(start); + start += g_l2cache_line_size; + } + + omap4_l2cache_wait_background_op(PL310_INV_LINE_PA, + g_l2cache_way_mask); + + /* Sync the cache */ + omap4_l2cache_cache_sync(); +} + + + +/** + * omap4_l2cache_wb_range - writes back a range of addresses + * @physaddr: physical address of the start of the range + * @size: size of the range in bytes + * + * Writes back (cleans) a virtual memory range from the L2 cache back to + * main memory. + * + * + */ +void +omap4_l2cache_wb_range(vm_paddr_t physaddr, vm_size_t size) +{ + vm_paddr_t start; + vm_paddr_t end; + + /* If the range is larger than entire cache then just write it all back */ + if (size >= g_l2cache_size) { + omap4_l2cache_wb_all(); + return; + } + + /* TODO: Add spinlocking when multicores is enabled */ + + /* Round the start and end address to be cache line aligned */ + end = (physaddr + size); + start = (physaddr & ~g_l2cache_align_mask); + + /* Write back all the addresses in the range */ + while (start < end) { + + /* TODO: In linux they release the spinlock after cleaning each page */ + + omap4_l2cache_wb_line(start); + start += g_l2cache_line_size; + } + + omap4_l2cache_wait_background_op(PL310_CLEAN_LINE_PA, + g_l2cache_way_mask); + /* Sync the cache */ + omap4_l2cache_cache_sync(); +} + + + + +/** + * omap4_l2cache_wbinv_range - writes back and invalidates a range of addresses + * @physaddr: phyiscal address of the start of the range + * @size: size of the range in bytes + * + * Writes back (cleans) and invalidates a virtual memory range from the L2 + * cache. + * + * + */ +void +omap4_l2cache_wbinv_range(vm_paddr_t physaddr, vm_size_t size) +{ + vm_paddr_t start; + vm_paddr_t end; + + /* If the range is larger than entire cache then just write it all back */ + if (size >= g_l2cache_size) { + omap4_l2cache_wbinv_all(); + return; + } + + /* TODO: Add spinlocking when multicores is enabled */ + + /* Round the start and end address to be cache line aligned */ + end = (physaddr + size); + start = (physaddr & ~g_l2cache_align_mask); + + /* Errata: disable write back and cache line fill */ + omap4_l2cache_errata_wa_write_debug(0x03); + + /* Write back all the addresses in the range */ + while (start < end) { + + /* TODO: In linux they release the spinlock after cleaning each page */ + + omap4_l2cache_wbinv_line(start); + start += g_l2cache_line_size; + } + + omap4_l2cache_errata_wa_write_debug(0x00); + + + /* Sync the cache */ + omap4_l2cache_wait_background_op(PL310_CLEAN_INV_LINE_PA, + g_l2cache_way_mask); + omap4_l2cache_cache_sync(); +} + + + +/** + * omap4_init_l2cache_controller - intialise the on chip cache controller + * @sc: the parent device + * @prio: defines roughly the order with which to add the child to the parent + * + * Initialises the clock structure and add an instance of the omap_prcm to + * the parent device with the correct memory regions assigned. + * + * + */ +static void +omap4_init_l2cache_controller(uint32_t aux_val, uint32_t aux_mask) +{ + uint32_t cache_id; + uint32_t cache_type; + uint32_t aux_ctrl; + uint32_t way_size; + uint32_t ways; + uint32_t ctrl; + + cache_id = omap4_l2cache_readl(PL310_CACHE_ID); + printf("PL310 L2 Cache Controller - %d.%d\n", ((cache_id >> 6) & 0xf), + (cache_id & 0x3f)); + + /* Read the cache details */ + cache_type = omap4_l2cache_readl(PL310_CACHE_TYPE); + aux_ctrl = omap4_l2cache_readl(PL310_AUX_CTRL); + + /* Apply the caller supplied details */ + aux_ctrl &= aux_mask; + aux_ctrl |= aux_val; + + /* Calculate the number of ways */ + if (aux_ctrl & (1 << PL310_AUX_CTRL_ASSOCIATIVITY_SHIFT)) + ways = 16; + else + ways = 8; + g_l2cache_way_mask = ((1 << ways) - 1); + + /* Calculate the size of the way (and hence the overall size) */ + way_size = 1 << (((aux_ctrl >> 17) & 0x7) + 13); + g_l2cache_size = (way_size * ways); + + /* Print the information */ + printf(" L2 Cache: %uKB/%dB %d ways\n", (g_l2cache_size / 1024), + g_l2cache_line_size, ways); + + + /* If the controller is currently disabled, clean the entire cache and + * enable. + */ + ctrl = omap4_l2cache_readl(PL310_CTRL); + if ((ctrl & 0x1) == 0x0) { + + /* Set the aux_ctrl values, these may have been changed by the caller */ + omap4_l2cache_writel(PL310_AUX_CTRL, aux_ctrl); + + /* invalidate the entire cache */ + omap4_l2cache_inv_all(); + + /* enable the cache controller */ + omap4_l2cache_writel(PL310_CTRL, 0x1); + } + omap4_l2cache_wbinv_all(); +} + + + + + + + +/** + * omap4_setup_l2cache_controller - intialise the on chip cache controller + * @sc: the parent device + * @prio: defines roughly the order with which to add the child to the parent + * + * Initialises the clock structure and add an instance of the omap_prcm to + * the parent device with the correct memory regions assigned. + * + * + */ +int +omap4_setup_l2cache_controller(struct omap4_softc *sc) +{ + uint32_t aux_val; + + KASSERT(sc == g_omap4_softc, ("Invalid sc / g_omap4_softc\n")); + + /* The following is a bit of hocus pocus taken from the L2 cache setup in + * Linux. The TRM doesn't contain much information on how to setup the L2 + * cache controller and some of these changes seem to come from errata's + * I can't find. Nevertheless the following works as the settings were + * basically lifted straight from Linux. + */ + + /* 16-way associativity, parity disabled + * Way size - 32KB (es1.0) + * Way size - 64KB (es2.0 +) + */ + aux_val = ((1 << PL310_AUX_CTRL_ASSOCIATIVITY_SHIFT) | + (0x1 << 25) | + (0x1 << PL310_AUX_CTRL_NS_LOCKDOWN_SHIFT) | + (0x1 << PL310_AUX_CTRL_NS_INT_CTRL_SHIFT)); + + if (omap_revision() == OMAP4430_REV_ES1_0) { + aux_val |= 0x2 << PL310_AUX_CTRL_WAY_SIZE_SHIFT; + } else { + aux_val |= ((0x3 << PL310_AUX_CTRL_WAY_SIZE_SHIFT) | + (0x1 << PL310_AUX_CTRL_SHARE_OVERRIDE_SHIFT) | + (0x1 << PL310_AUX_CTRL_DATA_PREFETCH_SHIFT) | + (0x1 << PL310_AUX_CTRL_INSTR_PREFETCH_SHIFT) | + (0x1 << PL310_AUX_CTRL_EARLY_BRESP_SHIFT)); + } + + if (omap_revision() != OMAP4430_REV_ES1_0) + omap4_smc_call(0x109, aux_val); + + /* Enable PL310 L2 Cache controller */ + omap4_smc_call(0x102, 0x1); + + /* Finally initialise the controller using the register set */ + omap4_init_l2cache_controller(aux_val, PL310_AUX_CTRL_MASK); + + /* Set the l2 functions in the set of cpufuncs */ + cpufuncs.cf_l2cache_wbinv_all = omap4_l2cache_wbinv_all; + cpufuncs.cf_l2cache_wbinv_range = omap4_l2cache_wbinv_range; + cpufuncs.cf_l2cache_inv_range = omap4_l2cache_inv_range; + cpufuncs.cf_l2cache_wb_range = omap4_l2cache_wb_range; + + return(0); +} + + + + + diff --git a/sys/arm/omap/omap4/omap4_prcm_clks.c b/sys/arm/omap/omap4/omap4_prcm_clks.c new file mode 100644 index 0000000..fc7dd83 --- /dev/null +++ b/sys/arm/omap/omap4/omap4_prcm_clks.c @@ -0,0 +1,1454 @@ +/*- + * 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 + + + +/* + * 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. + * + */ + + +void +omap4_clk_init(device_t dev, int prio); + +static int +omap4_clk_generic_activate(const struct omap_clock_dev *clkdev, + struct resource* mem_res[]); + +static int +omap4_clk_generic_deactivate(const struct omap_clock_dev *clkdev, + struct resource* mem_res[]); + +static int +omap4_clk_generic_accessible(const struct omap_clock_dev *clkdev, + struct resource* mem_res[]); + +static int +omap4_clk_generic_set_source(const struct omap_clock_dev *clkdev, clk_src_t clksrc, + struct resource* mem_res[]); + +static int +omap4_clk_generic_get_source_freq(const struct omap_clock_dev *clkdev, + unsigned int *freq, + struct resource* mem_res[]); + + + +static int +omap4_clk_gptimer_set_source(const struct omap_clock_dev *clkdev, clk_src_t clksrc, + struct resource* mem_res[]); +static int +omap4_clk_gptimer_get_source_freq(const struct omap_clock_dev *clkdev, + unsigned int *freq, struct resource* mem_res[]); + + +static int +omap4_clk_hsmmc_set_source(const struct omap_clock_dev *clkdev, clk_src_t clksrc, + struct resource* mem_res[]); +static int +omap4_clk_hsmmc_get_source_freq(const struct omap_clock_dev *clkdev, + unsigned int *freq, struct resource* mem_res[]); + + +static int +omap4_clk_hsusbhost_set_source(const struct omap_clock_dev *clkdev, + clk_src_t clksrc, struct resource* mem_res[]); +static int +omap4_clk_hsusbhost_activate(const struct omap_clock_dev *clkdev, + struct resource* mem_res[]); +static int +omap4_clk_hsusbhost_deactivate(const struct omap_clock_dev *clkdev, + struct resource* mem_res[]); +static int +omap4_clk_hsusbhost_accessible(const struct omap_clock_dev *clkdev, + struct resource* mem_res[]); + + +static int +omap4_clk_get_sysclk_freq(const struct omap_clock_dev *clkdev, + unsigned int *freq, struct resource* mem_res[]); + +static int +omap4_clk_get_arm_fclk_freq(const struct omap_clock_dev *clkdev, + unsigned int *freq, struct resource* mem_res[]); + + + + + + +#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 + + + +/** + * 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 \ + } + + +const struct omap_clock_dev omap_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), + + { INVALID_CLK_IDENT, NULL, NULL, NULL, NULL } +}; + + + + + + +/** + * g_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 const 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 + 0x320), CLKCTRL_MODULEMODE_AUTO), + + { 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 1000 + + +/** + * 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 const struct omap4_clk_details* +omap4_clk_details(clk_ident_t id) +{ + const 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(const struct omap_clock_dev *clkdev, + struct resource* mem_res[]) +{ + const struct omap4_clk_details* clk_details = omap4_clk_details(clkdev->id); + struct resource* clk_mem_res = mem_res[clk_details->mem_region]; + uint32_t clksel; + unsigned int i; + + if (clk_details == NULL) + return (ENXIO); + if (clk_mem_res == NULL) + return (ENOMEM); + + + + /* 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; + } + + /* 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(const struct omap_clock_dev *clkdev, + struct resource* mem_res[]) +{ + const struct omap4_clk_details* clk_details = omap4_clk_details(clkdev->id); + struct resource* clk_mem_res = mem_res[clk_details->mem_region]; + uint32_t clksel; + + if (clk_details == NULL) + return (ENXIO); + if (clk_mem_res == NULL) + return (ENOMEM); + + + /* 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(const struct omap_clock_dev *clkdev, + clk_src_t clksrc, struct resource* mem_res[]) +{ + + + 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(const struct omap_clock_dev *clkdev, + struct resource* mem_res[]) +{ + const struct omap4_clk_details* clk_details = omap4_clk_details(clkdev->id); + struct resource* clk_mem_res = mem_res[clk_details->mem_region]; + uint32_t clksel; + + if (clk_details == NULL) + return (ENXIO); + if (clk_mem_res == NULL) + return (ENOMEM); + + 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(const struct omap_clock_dev *clkdev, + unsigned int *freq, + struct resource* mem_res[]) +{ + const struct omap4_clk_details* clk_details = omap4_clk_details(clkdev->id); +#if 0 + uint32_t clksel; + unsigned int src_freq; +#endif + + 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(const struct omap_clock_dev *clkdev, + clk_src_t clksrc, struct resource* mem_res[]) +{ + const struct omap4_clk_details* clk_details = omap4_clk_details(clkdev->id); + struct resource* clk_mem_res = mem_res[clk_details->mem_region]; +#if 0 + uint32_t clksel; + unsigned int src_freq; +#endif + + if (clk_details == NULL) + return (ENXIO); + if (clk_mem_res == NULL) + return (ENOMEM); + + /* 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(const struct omap_clock_dev *clkdev, + unsigned int *freq, + struct resource* mem_res[]) +{ + const struct omap4_clk_details* clk_details = omap4_clk_details(clkdev->id); + struct resource* clk_mem_res = mem_res[clk_details->mem_region]; + uint32_t clksel; + unsigned int src_freq; + + if (clk_details == NULL) + return (ENXIO); + if (clk_mem_res == NULL) + return (ENOMEM); + + /* 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, mem_res); + + /* 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(const struct omap_clock_dev *clkdev, + clk_src_t clksrc, struct resource* mem_res[]) +{ + const struct omap4_clk_details* clk_details = omap4_clk_details(clkdev->id); + struct resource* clk_mem_res = mem_res[clk_details->mem_region]; + uint32_t clksel; + + if (clk_details == NULL) + return (ENXIO); + if (clk_mem_res == NULL) + return (ENOMEM); + + /* 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(const struct omap_clock_dev *clkdev, + unsigned int *freq, + struct resource* mem_res[]) +{ + const struct omap4_clk_details* clk_details = omap4_clk_details(clkdev->id); + struct resource* clk_mem_res = mem_res[clk_details->mem_region]; + uint32_t clksel; + unsigned int src_freq; + + if (clk_details == NULL) + return (ENXIO); + if (clk_mem_res == NULL) + return (ENOMEM); + + 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(const struct omap_clock_dev *clkdev, + unsigned int *freq, struct resource* mem_res[]) +{ + uint32_t clksel; + uint32_t sysclk; + + /* Read the input clock freq from the configuration register (CM_SYS_CLKSEL) */ + clksel = bus_read_4(mem_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(const struct omap_clock_dev *clkdev, + unsigned int *freq, struct resource* mem_res[]) +{ + uint32_t clksel; + uint32_t pll_mult, pll_div; + uint32_t mpuclk, sysclk; + + /* Read the clksel register which contains the DPLL multiple and divide + * values. These are applied to the sysclk. + */ + clksel = bus_read_4(mem_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, mem_res); + + + /* 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(const struct omap_clock_dev *clkdev, + struct resource* mem_res[]) +{ + struct resource* clk_mem_res; + uint32_t clksel_reg_off; + uint32_t clksel; + unsigned int i; +#if 0 + struct dpll_param *dpll_param; + + + + clk_mem_res = mem_res[CM2_INSTANCE_MEM_REGION]; + clksel_reg_off = 0x104; + clksel = bus_read_4(clk_mem_res, clksel_reg_off); + clksel &= ~CLKCTRL_MODULEMODE_MASK; + clksel = 1; + bus_write_4(clk_mem_res, clksel_reg_off, clksel); + clk_mem_res = mem_res[CM2_INSTANCE_MEM_REGION]; + clksel_reg_off = 0x180; + clksel = bus_read_4(clk_mem_res, clksel_reg_off); + clksel &= ~0x7; + clksel |= 4; + bus_write_4(clk_mem_res, clksel_reg_off, clksel); + clksel_reg_off = 0x184; + while (bus_read_4(clk_mem_res, clksel_reg_off) & 1) { + printf("GOT %x\n", bus_read_4(clk_mem_res, clksel_reg_off)); + } + + clksel_reg_off = 0x188; + clksel = bus_read_4(clk_mem_res, clksel_reg_off); + clksel &= ~0x7; + bus_write_4(clk_mem_res, clksel_reg_off, clksel); + + i = *(unsigned int *)(OMAP44XX_L4_CORE_VBASE + 0x306110); + printf("got %d\n", i); + dpll_param = &usb_dpll_param[i - 1]; + clksel_reg_off = 0x18c; + clksel = bus_read_4(clk_mem_res, clksel_reg_off); + clksel = (dpll_param->m << 8) | (dpll_param->n); + bus_write_4(clk_mem_res, clksel_reg_off, clksel); + clksel_reg_off = 0x190; + clksel = bus_read_4(clk_mem_res, clksel_reg_off); + clksel = 0x100 | (dpll_param->m2) | (1 << 8); + bus_write_4(clk_mem_res, clksel_reg_off, clksel); + + clk_mem_res = mem_res[CM2_INSTANCE_MEM_REGION]; + clksel_reg_off = 0x1b4; + clksel = bus_read_4(clk_mem_res, clksel_reg_off); + clksel |= (1 << 8); + bus_write_4(clk_mem_res, clksel_reg_off, clksel); + clksel_reg_off = 0x180; + clksel = bus_read_4(clk_mem_res, clksel_reg_off); + clksel &= ~0x7; + clksel |= 7; + bus_write_4(clk_mem_res, clksel_reg_off, clksel); + printf("YEAH YEAH\n"); + clksel_reg_off = 0x184; + while (!(bus_read_4(clk_mem_res, clksel_reg_off) & 1)) { + printf("pouic pouic %x\n", bus_read_4(clk_mem_res, clksel_reg_off) & 1); + } + + clksel_reg_off = 0x1b4; + clksel = 0x100; + bus_write_4(clk_mem_res, clksel_reg_off, clksel); +#if 0 + bus_write_4(clk_mem_res, L3INIT_CM2_OFFSET + 0x38, 1); + bus_write_4(clk_mem_res, L3INIT_CM2_OFFSET + 0x40, 2); + bus_write_4(clk_mem_res, L3INIT_CM2_OFFSET + 0x58, 2); + bus_write_4(clk_mem_res, L3INIT_CM2_OFFSET + 0x60, 1); + bus_write_4(clk_mem_res, L3INIT_CM2_OFFSET + 0x68, 1); + bus_write_4(clk_mem_res, L3INIT_CM2_OFFSET + 0xd0, 2); + bus_write_4(clk_mem_res, L3INIT_CM2_OFFSET + 0xe0, 0x301); +#endif +#endif + + 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 = mem_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 = mem_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(const struct omap_clock_dev *clkdev, + struct resource* mem_res[]) +{ + struct resource* clk_mem_res; + uint32_t clksel_reg_off; + uint32_t clksel; + + switch (clkdev->id) { + case USBTLL_CLK: + /* We need the CM_L3INIT_HSUSBTLL_CLKCTRL register in CM2 register set */ + clk_mem_res = mem_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 = mem_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(const struct omap_clock_dev *clkdev, + struct resource* mem_res[]) +{ + struct resource* clk_mem_res; + uint32_t clksel_reg_off; + uint32_t clksel; + + if (clkdev->id == USBTLL_CLK) { + /* We need the CM_L3INIT_HSUSBTLL_CLKCTRL register in CM2 register set */ + clk_mem_res = mem_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 = mem_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(const struct omap_clock_dev *clkdev, + clk_src_t clksrc, struct resource* mem_res[]) +{ + struct resource* clk_mem_res; + uint32_t clksel_reg_off; + uint32_t clksel; + unsigned int bit; + + 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 = mem_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); +} + + + + + + + +/** + * omap4_clk_init - add a child item to the root omap3 device + * @dev: the parent device + * @prio: defines roughly the order with which to add the child to the parent + * + * Initialises the clock structure and add an instance of the omap_prcm to + * the parent device with the correct memory regions assigned. + * + * + */ +void +omap4_clk_init(device_t dev, int prio) +{ + device_t kid; + struct omap_ivar *ivar; + + /* Start by adding the actual child to the parent (us) */ + kid = device_add_child_ordered(dev, prio, "omap_prcm", 0); + if (kid == NULL) { + printf("Can't add child omap_prcm0 ordered\n"); + return; + } + + /* Allocate some memory for the omap_ivar structure */ + ivar = malloc(sizeof(*ivar), M_DEVBUF, M_NOWAIT | M_ZERO); + if (ivar == NULL) { + device_delete_child(dev, kid); + printf("Can't add alloc ivar\n"); + return; + } + + /* Assign the ivars to the child item and populate with the device resources */ + device_set_ivars(kid, ivar); + + /* Assign the IRQ(s) in the resource list */ + resource_list_init(&ivar->resources); + + /* Assign the memory region to the resource list */ + bus_set_resource(kid, SYS_RES_MEMORY, PRM_INSTANCE_MEM_REGION, + 0x4A306000, 0x2000); + bus_set_resource(kid, SYS_RES_MEMORY, CM1_INSTANCE_MEM_REGION, + 0x4A004000, 0x1000); + bus_set_resource(kid, SYS_RES_MEMORY, CM2_INSTANCE_MEM_REGION, + 0x4A008000, 0x2000); +} + + + + + + diff --git a/sys/arm/omap/omap4/omap4_scm_padconf.c b/sys/arm/omap/omap4/omap4_scm_padconf.c new file mode 100644 index 0000000..ec3f1c0 --- /dev/null +++ b/sys/arm/omap/omap4/omap4_scm_padconf.c @@ -0,0 +1,338 @@ +/*- + * 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 + + +/* + * This file defines the pin mux configuration for the OMAP4xxx series of + * devices. + * + * How This is Suppose to Work + * =========================== + * - There is a top level omap_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 _OMAP_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 omap_scm_padconf omap_padconf_devmap[] = { + _OMAP_PINDEF(0x0040, "c12", 0, 0, "gpmc_ad0", "sdmmc2_dat0", NULL, NULL, NULL, NULL, NULL, NULL), + _OMAP_PINDEF(0x0042, "d12", 0, 0, "gpmc_ad1", "sdmmc2_dat1", NULL, NULL, NULL, NULL, NULL, NULL), + _OMAP_PINDEF(0x0044, "c13", 0, 0, "gpmc_ad2", "sdmmc2_dat2", NULL, NULL, NULL, NULL, NULL, NULL), + _OMAP_PINDEF(0x0046, "d13", 0, 0, "gpmc_ad3", "sdmmc2_dat3", NULL, NULL, NULL, NULL, NULL, NULL), + _OMAP_PINDEF(0x0048, "c15", 0, 0, "gpmc_ad4", "sdmmc2_dat4", "sdmmc2_dir_dat0", NULL, NULL, NULL, NULL, NULL), + _OMAP_PINDEF(0x004a, "d15", 0, 0, "gpmc_ad5", "sdmmc2_dat5", "sdmmc2_dir_dat1", NULL, NULL, NULL, NULL, NULL), + _OMAP_PINDEF(0x004c, "a16", 0, 0, "gpmc_ad6", "sdmmc2_dat6", "sdmmc2_dir_cmd", NULL, NULL, NULL, NULL, NULL), + _OMAP_PINDEF(0x004e, "b16", 0, 0, "gpmc_ad7", "sdmmc2_dat7", "sdmmc2_clk_fdbk", NULL, NULL, NULL, NULL, NULL), + _OMAP_PINDEF(0x0050, "c16", 32, 3, "gpmc_ad8", "kpd_row0", "c2c_data15", "gpio_32", NULL, "sdmmc1_dat0", NULL, NULL), + _OMAP_PINDEF(0x0052, "d16", 33, 3, "gpmc_ad9", "kpd_row1", "c2c_data14", "gpio_33", NULL, "sdmmc1_dat1", NULL, NULL), + _OMAP_PINDEF(0x0054, "c17", 34, 3, "gpmc_ad10", "kpd_row2", "c2c_data13", "gpio_34", NULL, "sdmmc1_dat2", NULL, NULL), + _OMAP_PINDEF(0x0056, "d17", 35, 3, "gpmc_ad11", "kpd_row3", "c2c_data12", "gpio_35", NULL, "sdmmc1_dat3", NULL, NULL), + _OMAP_PINDEF(0x0058, "c18", 36, 3, "gpmc_ad12", "kpd_col0", "c2c_data11", "gpio_36", NULL, "sdmmc1_dat4", NULL, NULL), + _OMAP_PINDEF(0x005a, "d18", 37, 3, "gpmc_ad13", "kpd_col1", "c2c_data10", "gpio_37", NULL, "sdmmc1_dat5", NULL, NULL), + _OMAP_PINDEF(0x005c, "c19", 38, 3, "gpmc_ad14", "kpd_col2", "c2c_data9", "gpio_38", NULL, "sdmmc1_dat6", NULL, NULL), + _OMAP_PINDEF(0x005e, "d19", 39, 3, "gpmc_ad15", "kpd_col3", "c2c_data8", "gpio_39", NULL, "sdmmc1_dat7", NULL, NULL), + _OMAP_PINDEF(0x0060, "b17", 40, 3, "gpmc_a16", "kpd_row4", "c2c_datain0", "gpio_40", "venc_656_data0", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0062, "a18", 41, 3, "gpmc_a17", "kpd_row5", "c2c_datain1", "gpio_41", "venc_656_data1", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0064, "b18", 42, 3, "gpmc_a18", "kpd_row6", "c2c_datain2", "gpio_42", "venc_656_data2", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0066, "a19", 43, 3, "gpmc_a19", "kpd_row7", "c2c_datain3", "gpio_43", "venc_656_data3", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0068, "b19", 44, 3, "gpmc_a20", "kpd_col4", "c2c_datain4", "gpio_44", "venc_656_data4", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x006a, "b20", 45, 3, "gpmc_a21", "kpd_col5", "c2c_datain5", "gpio_45", "venc_656_data5", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x006c, "a21", 46, 3, "gpmc_a22", "kpd_col6", "c2c_datain6", "gpio_46", "venc_656_data6", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x006e, "b21", 47, 3, "gpmc_a23", "kpd_col7", "c2c_datain7", "gpio_47", "venc_656_data7", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0070, "c20", 48, 3, "gpmc_a24", "kpd_col8", "c2c_clkout0", "gpio_48", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0072, "d20", 49, 3, "gpmc_a25", NULL, "c2c_clkout1", "gpio_49", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0074, "b25", 50, 3, "gpmc_ncs0", NULL, NULL, "gpio_50", "sys_ndmareq0", NULL, NULL, NULL), + _OMAP_PINDEF(0x0076, "c21", 51, 3, "gpmc_ncs1", NULL, "c2c_dataout6", "gpio_51", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0078, "d21", 52, 3, "gpmc_ncs2", "kpd_row8", "c2c_dataout7", "gpio_52", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x007a, "c22", 53, 3, "gpmc_ncs3", "gpmc_dir", "c2c_dataout4", "gpio_53", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x007c, "c25", 54, 3, "gpmc_nwp", "dsi1_te0", NULL, "gpio_54", "sys_ndmareq1", NULL, NULL, NULL), + _OMAP_PINDEF(0x007e, "b22", 55, 3, "gpmc_clk", NULL, NULL, "gpio_55", "sys_ndmareq2", "sdmmc1_cmd", NULL, NULL), + _OMAP_PINDEF(0x0080, "d25", 56, 3, "gpmc_nadv_ale", "dsi1_te1", NULL, "gpio_56", "sys_ndmareq3", "sdmmc1_clk", NULL, NULL), + _OMAP_PINDEF(0x0082, "b11", 0, 0, "gpmc_noe", "sdmmc2_clk", NULL, NULL, NULL, NULL, NULL, NULL), + _OMAP_PINDEF(0x0084, "b12", 0, 0, "gpmc_nwe", "sdmmc2_cmd", NULL, NULL, NULL, NULL, NULL, NULL), + _OMAP_PINDEF(0x0086, "c23", 59, 3, "gpmc_nbe0_cle", "dsi2_te0", NULL, "gpio_59", NULL, NULL, NULL, NULL), + _OMAP_PINDEF(0x0088, "d22", 60, 3, "gpmc_nbe1", NULL, "c2c_dataout5", "gpio_60", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x008a, "b26", 61, 3, "gpmc_wait0", "dsi2_te1", NULL, "gpio_61", NULL, NULL, NULL, NULL), + _OMAP_PINDEF(0x008c, "b23", 62, 3, "gpmc_wait1", NULL, "c2c_dataout2", "gpio_62", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x008e, "d23", 100, 3, "gpmc_wait2", "usbc1_icusb_txen", "c2c_dataout3", "gpio_100", "sys_ndmareq0", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0090, "a24", 101, 3, "gpmc_ncs4", "dsi1_te0", "c2c_clkin0", "gpio_101", "sys_ndmareq1", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0092, "b24", 102, 3, "gpmc_ncs5", "dsi1_te1", "c2c_clkin1", "gpio_102", "sys_ndmareq2", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0094, "c24", 103, 3, "gpmc_ncs6", "dsi2_te0", "c2c_dataout0", "gpio_103", "sys_ndmareq3", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0096, "d24", 104, 3, "gpmc_ncs7", "dsi2_te1", "c2c_dataout1", "gpio_104", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0098, "b9", 63, 3, "hdmi_hpd", NULL, NULL, "gpio_63", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x009a, "b10", 64, 3, "hdmi_cec", NULL, NULL, "gpio_64", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x009c, "a8", 65, 3, "hdmi_ddc_scl", NULL, NULL, "gpio_65", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x009e, "b8", 66, 3, "hdmi_ddc_sda", NULL, NULL, "gpio_66", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00a0, "r26", 0, 0, "csi21_dx0", NULL, NULL, "gpi_67", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00a2, "r25", 0, 0, "csi21_dy0", NULL, NULL, "gpi_68", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00a4, "t26", 0, 0, "csi21_dx1", NULL, NULL, "gpi_69", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00a6, "t25", 0, 0, "csi21_dy1", NULL, NULL, "gpi_70", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00a8, "u26", 0, 0, "csi21_dx2", NULL, NULL, "gpi_71", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00aa, "u25", 0, 0, "csi21_dy2", NULL, NULL, "gpi_72", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00ac, "v26", 0, 0, "csi21_dx3", NULL, NULL, "gpi_73", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00ae, "v25", 0, 0, "csi21_dy3", NULL, NULL, "gpi_74", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00b0, "w26", 0, 0, "csi21_dx4", NULL, NULL, "gpi_75", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00b2, "w25", 0, 0, "csi21_dy4", NULL, NULL, "gpi_76", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00b4, "m26", 0, 0, "csi22_dx0", NULL, NULL, "gpi_77", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00b6, "m25", 0, 0, "csi22_dy0", NULL, NULL, "gpi_78", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00b8, "n26", 0, 0, "csi22_dx1", NULL, NULL, "gpi_79", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00ba, "n25", 0, 0, "csi22_dy1", NULL, NULL, "gpi_80", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00bc, "t27", 81, 3, "cam_shutter", NULL, NULL, "gpio_81", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00be, "u27", 82, 3, "cam_strobe", NULL, NULL, "gpio_82", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00c0, "v27", 83, 3, "cam_globalreset", NULL, NULL, "gpio_83", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00c2, "ae18", 84, 3, "usbb1_ulpitll_clk", "hsi1_cawake", NULL, "gpio_84", "usbb1_ulpiphy_clk", NULL, "hw_dbg20", "safe_mode"), + _OMAP_PINDEF(0x00c4, "ag19", 85, 3, "usbb1_ulpitll_stp", "hsi1_cadata", "mcbsp4_clkr", "gpio_85", "usbb1_ulpiphy_stp", "usbb1_mm_rxdp", "hw_dbg21", "safe_mode"), + _OMAP_PINDEF(0x00c6, "af19", 86, 3, "usbb1_ulpitll_dir", "hsi1_caflag", "mcbsp4_fsr", "gpio_86", "usbb1_ulpiphy_dir", NULL, "hw_dbg22", "safe_mode"), + _OMAP_PINDEF(0x00c8, "ae19", 87, 3, "usbb1_ulpitll_nxt", "hsi1_acready", "mcbsp4_fsx", "gpio_87", "usbb1_ulpiphy_nxt", "usbb1_mm_rxdm", "hw_dbg23", "safe_mode"), + _OMAP_PINDEF(0x00ca, "af18", 88, 3, "usbb1_ulpitll_dat0", "hsi1_acwake", "mcbsp4_clkx", "gpio_88", "usbb1_ulpiphy_dat0", "usbb1_mm_txen", "hw_dbg24", "safe_mode"), + _OMAP_PINDEF(0x00cc, "ag18", 89, 3, "usbb1_ulpitll_dat1", "hsi1_acdata", "mcbsp4_dx", "gpio_89", "usbb1_ulpiphy_dat1", "usbb1_mm_txdat", "hw_dbg25", "safe_mode"), + _OMAP_PINDEF(0x00ce, "ae17", 90, 3, "usbb1_ulpitll_dat2", "hsi1_acflag", "mcbsp4_dr", "gpio_90", "usbb1_ulpiphy_dat2", "usbb1_mm_txse0", "hw_dbg26", "safe_mode"), + _OMAP_PINDEF(0x00d0, "af17", 91, 3, "usbb1_ulpitll_dat3", "hsi1_caready", NULL, "gpio_91", "usbb1_ulpiphy_dat3", "usbb1_mm_rxrcv", "hw_dbg27", "safe_mode"), + _OMAP_PINDEF(0x00d2, "ah17", 92, 3, "usbb1_ulpitll_dat4", "dmtimer8_pwm_evt", "abe_mcbsp3_dr", "gpio_92", "usbb1_ulpiphy_dat4", NULL, "hw_dbg28", "safe_mode"), + _OMAP_PINDEF(0x00d4, "ae16", 93, 3, "usbb1_ulpitll_dat5", "dmtimer9_pwm_evt", "abe_mcbsp3_dx", "gpio_93", "usbb1_ulpiphy_dat5", NULL, "hw_dbg29", "safe_mode"), + _OMAP_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"), + _OMAP_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"), + _OMAP_PINDEF(0x00da, "af14", 96, 3, "usbb1_hsic_data", NULL, NULL, "gpio_96", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00dc, "ae14", 97, 3, "usbb1_hsic_strobe", NULL, NULL, "gpio_97", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00de, "h2", 98, 3, "usbc1_icusb_dp", NULL, NULL, "gpio_98", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00e0, "h3", 99, 3, "usbc1_icusb_dm", NULL, NULL, "gpio_99", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00e2, "d2", 100, 3, "sdmmc1_clk", NULL, "dpm_emu19", "gpio_100", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00e4, "e3", 101, 3, "sdmmc1_cmd", NULL, "uart1_rx", "gpio_101", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00e6, "e4", 102, 3, "sdmmc1_dat0", NULL, "dpm_emu18", "gpio_102", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00e8, "e2", 103, 3, "sdmmc1_dat1", NULL, "dpm_emu17", "gpio_103", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00ea, "e1", 104, 3, "sdmmc1_dat2", NULL, "dpm_emu16", "gpio_104", "jtag_tms_tmsc", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00ec, "f4", 105, 3, "sdmmc1_dat3", NULL, "dpm_emu15", "gpio_105", "jtag_tck", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00ee, "f3", 106, 3, "sdmmc1_dat4", NULL, NULL, "gpio_106", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00f0, "f1", 107, 3, "sdmmc1_dat5", NULL, NULL, "gpio_107", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00f2, "g4", 108, 3, "sdmmc1_dat6", NULL, NULL, "gpio_108", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00f4, "g3", 109, 3, "sdmmc1_dat7", NULL, NULL, "gpio_109", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00f6, "ad27", 110, 3, "abe_mcbsp2_clkx", "mcspi2_clk", "abe_mcasp_ahclkx", "gpio_110", "usbb2_mm_rxdm", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00f8, "ad26", 111, 3, "abe_mcbsp2_dr", "mcspi2_somi", "abe_mcasp_axr", "gpio_111", "usbb2_mm_rxdp", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00fa, "ad25", 112, 3, "abe_mcbsp2_dx", "mcspi2_simo", "abe_mcasp_amute", "gpio_112", "usbb2_mm_rxrcv", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00fc, "ac28", 113, 3, "abe_mcbsp2_fsx", "mcspi2_cs0", "abe_mcasp_afsx", "gpio_113", "usbb2_mm_txen", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x00fe, "ac26", 114, 3, "abe_mcbsp1_clkx", "abe_slimbus1_clock", NULL, "gpio_114", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0100, "ac25", 115, 3, "abe_mcbsp1_dr", "abe_slimbus1_data", NULL, "gpio_115", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0102, "ab25", 116, 3, "abe_mcbsp1_dx", "sdmmc3_dat2", "abe_mcasp_aclkx", "gpio_116", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0104, "ac27", 117, 3, "abe_mcbsp1_fsx", "sdmmc3_dat3", "abe_mcasp_amutein", "gpio_117", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0106, "ag25", 0, 0, "abe_pdm_ul_data", "abe_mcbsp3_dr", NULL, NULL, NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0108, "af25", 0, 0, "abe_pdm_dl_data", "abe_mcbsp3_dx", NULL, NULL, NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x010a, "ae25", 0, 0, "abe_pdm_frame", "abe_mcbsp3_clkx", NULL, NULL, NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x010c, "af26", 0, 0, "abe_pdm_lb_clk", "abe_mcbsp3_fsx", NULL, NULL, NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x010e, "ah26", 118, 3, "abe_clks", NULL, NULL, "gpio_118", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0110, "ae24", 119, 3, "abe_dmic_clk1", NULL, NULL, "gpio_119", "usbb2_mm_txse0", "uart4_cts", NULL, "safe_mode"), + _OMAP_PINDEF(0x0112, "af24", 120, 3, "abe_dmic_din1", NULL, NULL, "gpio_120", "usbb2_mm_txdat", "uart4_rts", NULL, "safe_mode"), + _OMAP_PINDEF(0x0114, "ag24", 121, 3, "abe_dmic_din2", "slimbus2_clock", "abe_mcasp_axr", "gpio_121", NULL, "dmtimer11_pwm_evt", NULL, "safe_mode"), + _OMAP_PINDEF(0x0116, "ah24", 122, 3, "abe_dmic_din3", "slimbus2_data", "abe_dmic_clk2", "gpio_122", NULL, "dmtimer9_pwm_evt", NULL, "safe_mode"), + _OMAP_PINDEF(0x0118, "ab26", 123, 3, "uart2_cts", "sdmmc3_clk", NULL, "gpio_123", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x011a, "ab27", 124, 3, "uart2_rts", "sdmmc3_cmd", NULL, "gpio_124", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x011c, "aa25", 125, 3, "uart2_rx", "sdmmc3_dat0", NULL, "gpio_125", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x011e, "aa26", 126, 3, "uart2_tx", "sdmmc3_dat1", NULL, "gpio_126", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0120, "aa27", 127, 3, "hdq_sio", "i2c3_sccb", "i2c2_sccb", "gpio_127", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0122, "ae28", 0, 0, "i2c1_scl", NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _OMAP_PINDEF(0x0124, "ae26", 0, 0, "i2c1_sda", NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _OMAP_PINDEF(0x0126, "c26", 128, 3, "i2c2_scl", "uart1_rx", NULL, "gpio_128", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0128, "d26", 129, 3, "i2c2_sda", "uart1_tx", NULL, "gpio_129", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x012a, "w27", 130, 3, "i2c3_scl", NULL, NULL, "gpio_130", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x012c, "y27", 131, 3, "i2c3_sda", NULL, NULL, "gpio_131", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x012e, "ag21", 132, 3, "i2c4_scl", NULL, NULL, "gpio_132", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0130, "ah22", 133, 3, "i2c4_sda", NULL, NULL, "gpio_133", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0132, "af22", 134, 3, "mcspi1_clk", NULL, NULL, "gpio_134", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0134, "ae22", 135, 3, "mcspi1_somi", NULL, NULL, "gpio_135", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0136, "ag22", 136, 3, "mcspi1_simo", NULL, NULL, "gpio_136", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0138, "ae23", 137, 3, "mcspi1_cs0", NULL, NULL, "gpio_137", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x013a, "af23", 138, 3, "mcspi1_cs1", "uart1_rx", NULL, "gpio_138", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x013c, "ag23", 139, 3, "mcspi1_cs2", "uart1_cts", "slimbus2_clock", "gpio_139", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x013e, "ah23", 140, 3, "mcspi1_cs3", "uart1_rts", "slimbus2_data", "gpio_140", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0140, "f27", 141, 3, "uart3_cts_rctx", "uart1_tx", NULL, "gpio_141", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0142, "f28", 142, 3, "uart3_rts_sd", NULL, NULL, "gpio_142", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0144, "g27", 143, 3, "uart3_rx_irrx", "dmtimer8_pwm_evt", NULL, "gpio_143", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0146, "g28", 144, 3, "uart3_tx_irtx", "dmtimer9_pwm_evt", NULL, "gpio_144", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0148, "ae5", 145, 3, "sdmmc5_clk", "mcspi2_clk", "usbc1_icusb_dp", "gpio_145", NULL, "sdmmc2_clk", NULL, "safe_mode"), + _OMAP_PINDEF(0x014a, "af5", 146, 3, "sdmmc5_cmd", "mcspi2_simo", "usbc1_icusb_dm", "gpio_146", NULL, "sdmmc2_cmd", NULL, "safe_mode"), + _OMAP_PINDEF(0x014c, "ae4", 147, 3, "sdmmc5_dat0", "mcspi2_somi", "usbc1_icusb_rcv", "gpio_147", NULL, "sdmmc2_dat0", NULL, "safe_mode"), + _OMAP_PINDEF(0x014e, "af4", 148, 3, "sdmmc5_dat1", NULL, "usbc1_icusb_txen", "gpio_148", NULL, "sdmmc2_dat1", NULL, "safe_mode"), + _OMAP_PINDEF(0x0150, "ag3", 149, 3, "sdmmc5_dat2", "mcspi2_cs1", NULL, "gpio_149", NULL, "sdmmc2_dat2", NULL, "safe_mode"), + _OMAP_PINDEF(0x0152, "af3", 150, 3, "sdmmc5_dat3", "mcspi2_cs0", NULL, "gpio_150", NULL, "sdmmc2_dat3", NULL, "safe_mode"), + _OMAP_PINDEF(0x0154, "ae21", 151, 3, "mcspi4_clk", "sdmmc4_clk", "kpd_col6", "gpio_151", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0156, "af20", 152, 3, "mcspi4_simo", "sdmmc4_cmd", "kpd_col7", "gpio_152", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0158, "af21", 153, 3, "mcspi4_somi", "sdmmc4_dat0", "kpd_row6", "gpio_153", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x015a, "ae20", 154, 3, "mcspi4_cs0", "sdmmc4_dat3", "kpd_row7", "gpio_154", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x015c, "ag20", 155, 3, "uart4_rx", "sdmmc4_dat2", "kpd_row8", "gpio_155", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x015e, "ah19", 156, 3, "uart4_tx", "sdmmc4_dat1", "kpd_col8", "gpio_156", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0160, "ag12", 157, 3, "usbb2_ulpitll_clk", "usbb2_ulpiphy_clk", "sdmmc4_cmd", "gpio_157", "hsi2_cawake", NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0162, "af12", 158, 3, "usbb2_ulpitll_stp", "usbb2_ulpiphy_stp", "sdmmc4_clk", "gpio_158", "hsi2_cadata", "dispc2_data23", NULL, "safe_mode"), + _OMAP_PINDEF(0x0164, "ae12", 159, 3, "usbb2_ulpitll_dir", "usbb2_ulpiphy_dir", "sdmmc4_dat0", "gpio_159", "hsi2_caflag", "dispc2_data22", NULL, "safe_mode"), + _OMAP_PINDEF(0x0166, "ag13", 160, 3, "usbb2_ulpitll_nxt", "usbb2_ulpiphy_nxt", "sdmmc4_dat1", "gpio_160", "hsi2_acready", "dispc2_data21", NULL, "safe_mode"), + _OMAP_PINDEF(0x0168, "ae11", 161, 3, "usbb2_ulpitll_dat0", "usbb2_ulpiphy_dat0", "sdmmc4_dat2", "gpio_161", "hsi2_acwake", "dispc2_data20", "usbb2_mm_txen", "safe_mode"), + _OMAP_PINDEF(0x016a, "af11", 162, 3, "usbb2_ulpitll_dat1", "usbb2_ulpiphy_dat1", "sdmmc4_dat3", "gpio_162", "hsi2_acdata", "dispc2_data19", "usbb2_mm_txdat", "safe_mode"), + _OMAP_PINDEF(0x016c, "ag11", 163, 3, "usbb2_ulpitll_dat2", "usbb2_ulpiphy_dat2", "sdmmc3_dat2", "gpio_163", "hsi2_acflag", "dispc2_data18", "usbb2_mm_txse0", "safe_mode"), + _OMAP_PINDEF(0x016e, "ah11", 164, 3, "usbb2_ulpitll_dat3", "usbb2_ulpiphy_dat3", "sdmmc3_dat1", "gpio_164", "hsi2_caready", "dispc2_data15", "rfbi_data15", "safe_mode"), + _OMAP_PINDEF(0x0170, "ae10", 165, 3, "usbb2_ulpitll_dat4", "usbb2_ulpiphy_dat4", "sdmmc3_dat0", "gpio_165", "mcspi3_somi", "dispc2_data14", "rfbi_data14", "safe_mode"), + _OMAP_PINDEF(0x0172, "af10", 166, 3, "usbb2_ulpitll_dat5", "usbb2_ulpiphy_dat5", "sdmmc3_dat3", "gpio_166", "mcspi3_cs0", "dispc2_data13", "rfbi_data13", "safe_mode"), + _OMAP_PINDEF(0x0174, "ag10", 167, 3, "usbb2_ulpitll_dat6", "usbb2_ulpiphy_dat6", "sdmmc3_cmd", "gpio_167", "mcspi3_simo", "dispc2_data12", "rfbi_data12", "safe_mode"), + _OMAP_PINDEF(0x0176, "ae9", 168, 3, "usbb2_ulpitll_dat7", "usbb2_ulpiphy_dat7", "sdmmc3_clk", "gpio_168", "mcspi3_clk", "dispc2_data11", "rfbi_data11", "safe_mode"), + _OMAP_PINDEF(0x0178, "af13", 169, 3, "usbb2_hsic_data", NULL, NULL, "gpio_169", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x017a, "ae13", 170, 3, "usbb2_hsic_strobe", NULL, NULL, "gpio_170", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x017c, "g26", 171, 3, "kpd_col3", "kpd_col0", NULL, "gpio_171", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x017e, "g25", 172, 3, "kpd_col4", "kpd_col1", NULL, "gpio_172", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0180, "h26", 173, 3, "kpd_col5", "kpd_col2", NULL, "gpio_173", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0182, "h25", 174, 3, "kpd_col0", "kpd_col3", NULL, "gpio_174", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0184, "j27", 0, 0, "kpd_col1", "kpd_col4", NULL, "gpio_0", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0186, "h27", 1, 3, "kpd_col2", "kpd_col5", NULL, "gpio_1", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0188, "j26", 175, 3, "kpd_row3", "kpd_row0", NULL, "gpio_175", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x018a, "j25", 176, 3, "kpd_row4", "kpd_row1", NULL, "gpio_176", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x018c, "k26", 177, 3, "kpd_row5", "kpd_row2", NULL, "gpio_177", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x018e, "k25", 178, 3, "kpd_row0", "kpd_row3", NULL, "gpio_178", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0190, "l27", 2, 3, "kpd_row1", "kpd_row4", NULL, "gpio_2", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0192, "k27", 3, 3, "kpd_row2", "kpd_row5", NULL, "gpio_3", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0194, "c3", 0, 0, "usba0_otg_ce", NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _OMAP_PINDEF(0x0196, "b5", 0, 0, "usba0_otg_dp", "uart3_rx_irrx", "uart2_rx", NULL, NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x0198, "b4", 0, 0, "usba0_otg_dm", "uart3_tx_irtx", "uart2_tx", NULL, NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x019a, "aa28", 181, 3, "fref_clk1_out", NULL, NULL, "gpio_181", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x019c, "y28", 182, 3, "fref_clk2_out", NULL, NULL, "gpio_182", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x019e, "ae6", 0, 0, "sys_nirq1", NULL, NULL, NULL, NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x01a0, "af6", 183, 3, "sys_nirq2", NULL, NULL, "gpio_183", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x01a2, "f26", 184, 3, "sys_boot0", NULL, NULL, "gpio_184", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x01a4, "e27", 185, 3, "sys_boot1", NULL, NULL, "gpio_185", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x01a6, "e26", 186, 3, "sys_boot2", NULL, NULL, "gpio_186", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x01a8, "e25", 187, 3, "sys_boot3", NULL, NULL, "gpio_187", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x01aa, "d28", 188, 3, "sys_boot4", NULL, NULL, "gpio_188", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x01ac, "d27", 189, 3, "sys_boot5", NULL, NULL, "gpio_189", NULL, NULL, NULL, "safe_mode"), + _OMAP_PINDEF(0x01ae, "m2", 11, 3, "dpm_emu0", NULL, NULL, "gpio_11", NULL, NULL, "hw_dbg0", "safe_mode"), + _OMAP_PINDEF(0x01b0, "n2", 12, 3, "dpm_emu1", NULL, NULL, "gpio_12", NULL, NULL, "hw_dbg1", "safe_mode"), + _OMAP_PINDEF(0x01b2, "p2", 13, 3, "dpm_emu2", "usba0_ulpiphy_clk", NULL, "gpio_13", NULL, "dispc2_fid", "hw_dbg2", "safe_mode"), + _OMAP_PINDEF(0x01b4, "v1", 14, 3, "dpm_emu3", "usba0_ulpiphy_stp", NULL, "gpio_14", "rfbi_data10", "dispc2_data10", "hw_dbg3", "safe_mode"), + _OMAP_PINDEF(0x01b6, "v2", 15, 3, "dpm_emu4", "usba0_ulpiphy_dir", NULL, "gpio_15", "rfbi_data9", "dispc2_data9", "hw_dbg4", "safe_mode"), + _OMAP_PINDEF(0x01b8, "w1", 16, 3, "dpm_emu5", "usba0_ulpiphy_nxt", NULL, "gpio_16", "rfbi_te_vsync0", "dispc2_data16", "hw_dbg5", "safe_mode"), + _OMAP_PINDEF(0x01ba, "w2", 17, 3, "dpm_emu6", "usba0_ulpiphy_dat0", "uart3_tx_irtx", "gpio_17", "rfbi_hsync0", "dispc2_data17", "hw_dbg6", "safe_mode"), + _OMAP_PINDEF(0x01bc, "w3", 18, 3, "dpm_emu7", "usba0_ulpiphy_dat1", "uart3_rx_irrx", "gpio_18", "rfbi_cs0", "dispc2_hsync", "hw_dbg7", "safe_mode"), + _OMAP_PINDEF(0x01be, "w4", 19, 3, "dpm_emu8", "usba0_ulpiphy_dat2", "uart3_rts_sd", "gpio_19", "rfbi_re", "dispc2_pclk", "hw_dbg8", "safe_mode"), + _OMAP_PINDEF(0x01c0, "y2", 20, 3, "dpm_emu9", "usba0_ulpiphy_dat3", "uart3_cts_rctx", "gpio_20", "rfbi_we", "dispc2_vsync", "hw_dbg9", "safe_mode"), + _OMAP_PINDEF(0x01c2, "y3", 21, 3, "dpm_emu10", "usba0_ulpiphy_dat4", NULL, "gpio_21", "rfbi_a0", "dispc2_de", "hw_dbg10", "safe_mode"), + _OMAP_PINDEF(0x01c4, "y4", 22, 3, "dpm_emu11", "usba0_ulpiphy_dat5", NULL, "gpio_22", "rfbi_data8", "dispc2_data8", "hw_dbg11", "safe_mode"), + _OMAP_PINDEF(0x01c6, "aa1", 23, 3, "dpm_emu12", "usba0_ulpiphy_dat6", NULL, "gpio_23", "rfbi_data7", "dispc2_data7", "hw_dbg12", "safe_mode"), + _OMAP_PINDEF(0x01c8, "aa2", 24, 3, "dpm_emu13", "usba0_ulpiphy_dat7", NULL, "gpio_24", "rfbi_data6", "dispc2_data6", "hw_dbg13", "safe_mode"), + _OMAP_PINDEF(0x01ca, "aa3", 25, 3, "dpm_emu14", "sys_drm_msecure", "uart1_rx", "gpio_25", "rfbi_data5", "dispc2_data5", "hw_dbg14", "safe_mode"), + _OMAP_PINDEF(0x01cc, "aa4", 26, 3, "dpm_emu15", "sys_secure_indicator", NULL, "gpio_26", "rfbi_data4", "dispc2_data4", "hw_dbg15", "safe_mode"), + _OMAP_PINDEF(0x01ce, "ab2", 27, 3, "dpm_emu16", "dmtimer8_pwm_evt", "dsi1_te0", "gpio_27", "rfbi_data3", "dispc2_data3", "hw_dbg16", "safe_mode"), + _OMAP_PINDEF(0x01d0, "ab3", 28, 3, "dpm_emu17", "dmtimer9_pwm_evt", "dsi1_te1", "gpio_28", "rfbi_data2", "dispc2_data2", "hw_dbg17", "safe_mode"), + _OMAP_PINDEF(0x01d2, "ab4", 190, 3, "dpm_emu18", "dmtimer10_pwm_evt", "dsi2_te0", "gpio_190", "rfbi_data1", "dispc2_data1", "hw_dbg18", "safe_mode"), + _OMAP_PINDEF(0x01d4, "ac4", 191, 3, "dpm_emu19", "dmtimer11_pwm_evt", "dsi2_te1", "gpio_191", "rfbi_data0", "dispc2_data0", "hw_dbg19", "safe_mode"), + { .ballname = NULL }, +}; + + + +/** + * omap4_pinmux_init - adds and initialise the OMAP SCM driver + * @dev: the parent device + * @prio: defines roughly the order with which to add the child to the parent + * + * Initialises the pinmux structure and add an instance of the omap_scm to + * the parent device with the correct memory regions assigned. + * + * + */ +void +omap4_padconf_init(device_t dev, int prio) +{ + device_t kid; + struct omap_ivar *ivar; + + /* Start by adding the actual child to the parent (us) */ + kid = device_add_child_ordered(dev, prio, "omap_scm", 0); + if (kid == NULL) { + printf("Can't add child omap_scm0 ordered\n"); + return; + } + + /* Allocate some memory for the omap_ivar structure */ + ivar = malloc(sizeof(*ivar), M_DEVBUF, M_NOWAIT | M_ZERO); + if (ivar == NULL) { + device_delete_child(dev, kid); + printf("Can't add alloc ivar\n"); + return; + } + + /* Assign the ivars to the child item and populate with the device resources */ + device_set_ivars(kid, ivar); + + /* Initialise the resource list */ + resource_list_init(&ivar->resources); + + /* Assign the memory region to the resource list */ + bus_set_resource(kid, SYS_RES_MEMORY, 0, OMAP44XX_SCM_PADCONF_HWBASE, + OMAP44XX_SCM_PADCONF_SIZE); +} + + diff --git a/sys/arm/omap/omap4/omap4_secure.S b/sys/arm/omap/omap4/omap4_secure.S new file mode 100755 index 0000000..8b36743 --- /dev/null +++ b/sys/arm/omap/omap4/omap4_secure.S @@ -0,0 +1,60 @@ +/*- + * Copyright (c) 2011 + * Ben Gray . + * All rights reserved. + * + * Based heavily on the Linux OMAP secure API + * Copyright (C) 2010 Texas Instruments, Inc. + * Written by Santosh Shilimkar + * + * + * 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 author may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 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$"); + + +/** + * omap4_smc_call - issues a secure monitor API call + * @r0: contains the monitor API number + * @r1: contains the value to set + * + * This function will send a secure monitor call to the internal rom code in + * the trust mode. The rom code expects that r0 will contain the value and + * r12 will contain the API number, so internally the function swaps the + * register settings around. The trust mode code may also alter all the cpu + * registers so everything (including the lr register) is saved on the stack + * before the call. + * + * RETURNS: + * nothing + */ +ENTRY(omap4_smc_call) + stmfd sp!, {r2-r12, lr} + mov r12, r0 + mov r0, r1 + dsb + smc #0 + ldmfd sp!, {r2-r12, pc} + diff --git a/sys/arm/omap/omap4/omap4_timer.c b/sys/arm/omap/omap4/omap4_timer.c new file mode 100644 index 0000000..2c6c879 --- /dev/null +++ b/sys/arm/omap/omap4/omap4_timer.c @@ -0,0 +1,454 @@ +/*- + * 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 + + +/** + * After writing the GPTIMER driver I began thinking that these are probably + * not the best timers to use for the tick timer and time count stuff, rather + * perhaps it's a better idea to use the timers built into the A9 core. + * + * My reasoning is that the GPTIMER driver really should (and currently does) + * take a mutex to protect access to the timer structure when reading the time + * counter, and this may not be a good idea within system callback functions + * like timecounter. However that said the locks are currently SPIN_MTX + * types so I think this *shouldn't* be a problem. + * + * Regardless I think it is probably better to just use the A9 Core global + * timer and be done with it. + * + * + * Current System Timer Design + * =========================== + * + * Tick Timer => uses => Core 0 Private timer + * Timecount => uses => ARM Global timer + * + */ + + + +/* Private (per-CPU) timer register map */ +#define PRV_TIMER_LOAD 0x0000 +#define PRV_TIMER_COUNT 0x0004 +#define PRV_TIMER_CTRL 0x0008 +#define PRV_TIMER_INTR 0x000C + +#define PRV_TIMER_IRQ_NUM 29 + +#define PRV_TIMER_CTR_PRESCALER_SHIFT 8 +#define PRV_TIMER_CTRL_IRQ_ENABLE (1UL << 2) +#define PRV_TIMER_CTRL_AUTO_RELOAD (1UL << 1) +#define PRV_TIMER_CTRL_TIMER_ENABLE (1UL << 0) + +#define PRV_TIMER_INTR_EVENT (1UL << 0) + + + +/* Global timer register map */ +#define GBL_TIMER_COUNT_LOW 0x0000 +#define GBL_TIMER_COUNT_HIGH 0x0004 +#define GBL_TIMER_CTRL 0x0008 +#define GBL_TIMER_INTR 0x000C + +#define GBL_TIMER_IRQ_NUM 27 + +#define GBL_TIMER_CTR_PRESCALER_SHIFT 8 +#define GBL_TIMER_CTRL_AUTO_INC (1UL << 3) +#define GBL_TIMER_CTRL_IRQ_ENABLE (1UL << 2) +#define GBL_TIMER_CTRL_COMP_ENABLE (1UL << 1) +#define GBL_TIMER_CTRL_TIMER_ENABLE (1UL << 0) + +#define GBL_TIMER_INTR_EVENT (1UL << 0) + + + + + +static inline uint32_t +omap4_gbl_timer_readl(bus_size_t off) +{ + return (bus_space_read_4(g_omap4_softc->sc_iotag, + g_omap4_softc->sc_gbl_timer_ioh, off)); +} +static inline void +omap4_gbl_timer_writel(bus_size_t off, uint32_t val) +{ + bus_space_write_4(g_omap4_softc->sc_iotag, g_omap4_softc->sc_gbl_timer_ioh, + off, val); +} + + +static inline uint32_t +omap4_prv_timer_readl(bus_size_t off) +{ + return (bus_space_read_4(g_omap4_softc->sc_iotag, + g_omap4_softc->sc_prv_timer_ioh, off)); +} +static inline void +omap4_prv_timer_writel(bus_size_t off, uint32_t val) +{ + bus_space_write_4(g_omap4_softc->sc_iotag, g_omap4_softc->sc_prv_timer_ioh, + off, val); +} + + + +/** + * omap4_timer_get_timecount - returns the count in GPTIMER11, the system counter + * @tc: pointer to the timecounter structure used to register the callback + * + * + * + * RETURNS: + * the value in the counter + */ +static unsigned +omap4_timer_get_timecount(struct timecounter *tc) +{ + /* We only read the lower 32-bits, the timecount stuff only uses 32-bits + * so (for now?) ignore the upper 32-bits. + */ + return (omap4_gbl_timer_readl(GBL_TIMER_COUNT_LOW)); +} + +static struct timecounter g_omap4_timecounter = { + .tc_get_timecount = omap4_timer_get_timecount, /* get_timecount */ + .tc_poll_pps = NULL, /* no poll_pps */ + .tc_counter_mask = ~0u, /* counter_mask */ + .tc_frequency = 0, /* frequency */ + .tc_name = "OMAP4 Timer", /* name */ + .tc_quality = 1000, /* quality */ +}; + +static struct eventtimer g_omap4_eventtimer; + + +/** + * omap4_clk_intr - + * + * Interrupt handler for the private interrupt. + * + * RETURNS: + * nothing + */ +static int +omap4_clk_intr(void *arg) +{ + struct trapframe *frame = arg; + + bus_space_write_4(g_omap4_softc->sc_iotag, g_omap4_softc->sc_prv_timer_ioh, + PRV_TIMER_INTR, PRV_TIMER_INTR_EVENT); + + hardclock(TRAPF_USERMODE(frame), TRAPF_PC(frame)); + return (FILTER_HANDLED); +} + + +static int +omap4_timer_start(struct eventtimer *et, + struct bintime *first, struct bintime *period) +{ + + return (0); +} + +static int +omap4_timer_stop(struct eventtimer *et) +{ + + return (0); +} + +/** + * cpu_initclocks - function called by the system in init the tick clock/timer + * + * This is where both the timercount and system ticks timer are started. + * + * RETURNS: + * nothing + */ +void +cpu_initclocks(void) +{ + u_int oldirqstate; + struct resource *irq; + unsigned int mpuclk_freq; + unsigned int periphclk_freq; + int rid = 0; + void *ihl; + unsigned int prescaler; + uint64_t load64; + + /* First ensure we have what we need */ + if (g_omap4_softc == NULL) + panic("Error: omap4 base sc not setup before enabling global timer\n"); + + + oldirqstate = disable_interrupts(I32_bit); + + + /* Register an interrupt handler for general purpose timer 10 */ + irq = bus_alloc_resource(g_omap4_softc->sc_dev, SYS_RES_IRQ, &rid, + PRV_TIMER_IRQ_NUM, PRV_TIMER_IRQ_NUM, 1, RF_ACTIVE); + if (!irq) + panic("Unable to setup the clock irq handler.\n"); + else + bus_setup_intr(g_omap4_softc->sc_dev, irq, INTR_TYPE_CLK, + omap4_clk_intr, NULL, NULL, &ihl); + + + /* Ensure the timer is disabled before we start playing with it */ + bus_space_write_4(g_omap4_softc->sc_iotag, g_omap4_softc->sc_prv_timer_ioh, + PRV_TIMER_CTRL, 0x00000000); + + + /* Next get the "PERIPHCLK" freq - which I take to be the "LOCAL_INTCNT_FCLK" + * refered to in the OMAP44xx datasheet. The "LOCAL_INTCNT_FCLK" is half + * the ARM_FCLK. + */ + if (omap_prcm_clk_get_source_freq(MPU_CLK, &mpuclk_freq) != 0) + panic("Failed to get the SYSCLK frequency\n"); + + periphclk_freq = mpuclk_freq / 2; + + + /* The timer counts down from a 'load' value to zero, when it reaches zero + * it loads the 'load' value back in and starts counting down again. + * + * So we need to calculate the load value, the larger the load the value + * the better the accuracy. + */ + for (prescaler = 1; prescaler < 255; prescaler++) { + load64 = ((uint64_t)periphclk_freq / (uint64_t)hz) / (uint64_t)prescaler; + if (load64 <= 0xFFFFFFFFULL) { + break; + } + } + + +printf("[BRG] periphclk_freq = %u : hz = %d : load64 = %llu : prescaler = %u\n", + periphclk_freq, hz, load64, prescaler); + + + if (prescaler == 255) + panic("Really? couldn't fit timer tick in private counter\n"); + + /* Set the load value */ + omap4_prv_timer_writel(PRV_TIMER_LOAD, (uint32_t)load64); + omap4_prv_timer_writel(PRV_TIMER_COUNT, (uint32_t)load64); + + /* Setup and enable the timer */ + omap4_prv_timer_writel(PRV_TIMER_CTRL, + ((prescaler - 1) << PRV_TIMER_CTR_PRESCALER_SHIFT) | + PRV_TIMER_CTRL_IRQ_ENABLE | + PRV_TIMER_CTRL_AUTO_RELOAD | + PRV_TIMER_CTRL_TIMER_ENABLE); + + + /* Setup and enable the global timer to use as the timecounter */ + omap4_gbl_timer_writel(GBL_TIMER_CTRL, + (0x00 << GBL_TIMER_CTR_PRESCALER_SHIFT) | + GBL_TIMER_CTRL_TIMER_ENABLE); + + + /* Save the system clock speed */ + g_omap4_timecounter.tc_frequency = periphclk_freq; + + /* Setup the time counter */ + tc_init(&g_omap4_timecounter); + + g_omap4_eventtimer.et_name = "OMAP4 Event Timer"; + g_omap4_eventtimer.et_flags = ET_FLAGS_PERIODIC | ET_FLAGS_ONESHOT | + ET_FLAGS_PERCPU; + g_omap4_eventtimer.et_quality = 800; + g_omap4_eventtimer.et_frequency = 1000; + g_omap4_eventtimer.et_min_period.sec = 0; + g_omap4_eventtimer.et_min_period.frac = 0x00004000LLU << 32; /* To be safe. */ + g_omap4_eventtimer.et_max_period.sec = 0xfffffffeU / g_omap4_eventtimer.et_frequency; + g_omap4_eventtimer.et_max_period.frac = + ((0xfffffffeLLU << 32) / g_omap4_eventtimer.et_frequency) << 32; + g_omap4_eventtimer.et_start = omap4_timer_start; + g_omap4_eventtimer.et_stop = omap4_timer_stop; + g_omap4_eventtimer.et_priv = NULL; + et_register(&g_omap4_eventtimer); + + + restore_interrupts(oldirqstate); + + + +#if 0 + unsigned int sysclk_freq; + + /* Number of microseconds between interrupts */ + tick = 1000000 / hz; + + /* Next setup one of the timers to be the system tick timer */ + if (omap_timer_activate(TICKTIMER_GPTIMER, OMAP_TIMER_SYSTICK_FLAG, tick, + NULL, NULL)) { + panic("Error: failed to activate system tick timer\n"); + } else if (omap_timer_start(TICKTIMER_GPTIMER)) { + panic("Error: failed to start system tick timer\n"); + } + + + /* Enable the ARM Coretex-A9 core global timer, this is used for calculating + * delay times in the DELAY() function ... and as the system timecount + * value ?? + */ + + + + /* Setup another timer to be the timecounter */ + if (omap_timer_activate(TIMECOUNT_GPTIMER, OMAP_TIMER_PERIODIC_FLAG, 0, + NULL, NULL)) { + printf("Error: failed to activate system tick timer\n"); + } else if (omap_timer_start(TIMECOUNT_GPTIMER)) { + printf("Error: failed to start system tick timer\n"); + } + + /* Get the SYS_CLK frequency, this is the freq of the timer tick */ + if (omap_prcm_clk_get_source_freq(SYS_CLK, &sysclk_freq) != 0) + panic("Failed to get the SYSCLK frequency\n"); + + /* Save the system clock speed */ + g_omap4_timecounter.tc_frequency = sysclk_freq; + + /* Setup the time counter */ + tc_init(&g_omap4_timecounter); + +#endif + cpu_initclocks_bsp(); + +} + + + + +/** + * DELAY - Delay for at least N microseconds. + * @n: number of microseconds to delay by + * + * This function is called all over the kernel and is suppose to provide a + * consistent delay. It is a busy loop and blocks polling a timer when called. + * + * RETURNS: + * nothing + */ +void +DELAY(int n) +{ + int32_t counts_per_usec; + int32_t counts; + uint32_t first, last; + + if (n <= 0) + return; + + /* Check the timers are setup, if not just use a for loop for the meantime */ + if (g_omap4_timecounter.tc_frequency == 0) { + + /* If the CPU clock speed is defined we use that via the 'cycle count' + * register, this should give us a pretty accurate delay value. If not + * defined we use a basic for loop with a very simply calculation. + */ +#if defined(CPU_CLOCKSPEED) + counts_per_usec = (CPU_CLOCKSPEED / 1000000); + counts = counts_per_usec * 1000; + + __asm __volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (first)); + while (counts > 0) { + __asm __volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (last)); + counts -= (int32_t)(last - first); + first = last; + } +// printf("[BRG] CPU_CLOCKSPEED = %ul, first = %lu, last = %lu\n", +// CPU_CLOCKSPEED, first, last); +#else + uint32_t val; + for (; n > 0; n--) + for (val = 200; val > 0; val--) + cpufunc_nullop(); /* + * Prevent gcc from + * optimizing out the + * loop + */ + +#endif + return; + } + + /* Get the number of times to count */ + counts_per_usec = ((g_omap4_timecounter.tc_frequency / 1000000) + 1); + + + /* + * Clamp the timeout at a maximum value (about 32 seconds with + * a 66MHz clock). *Nobody* should be delay()ing for anywhere + * near that length of time and if they are, they should be hung + * out to dry. + */ + if (n >= (0x80000000U / counts_per_usec)) + counts = (0x80000000U / counts_per_usec) - 1; + else + counts = n * counts_per_usec; + + first = omap4_gbl_timer_readl(GBL_TIMER_COUNT_LOW); + + while (counts > 0) { + last = omap4_gbl_timer_readl(GBL_TIMER_COUNT_LOW); + counts -= (int32_t)(last - first); + first = last; + } +} diff --git a/sys/arm/omap/omap4/omap4var.h b/sys/arm/omap/omap4/omap4var.h new file mode 100644 index 0000000..3d595b2 --- /dev/null +++ b/sys/arm/omap/omap4/omap4var.h @@ -0,0 +1,122 @@ +/*- + * 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. + */ + +#ifndef _OMAP4VAR_H_ +#define _OMAP4VAR_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + + +/* + * Random collection of functions and definitions ... needs cleanup + * + * + * + */ + + +void +omap4_mask_all_intr(void); + +void +omap4_post_filter_intr(void *arg); + +int +omap4_setup_intr(device_t dev, device_t child, + struct resource *res, int flags, driver_filter_t *filt, + driver_intr_t *intr, void *arg, void **cookiep); + +int +omap4_teardown_intr(device_t dev, device_t child, struct resource *res, + void *cookie); + + + + +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; +}; + +extern struct omap4_softc *g_omap4_softc; + + +void +omap4_clk_init(device_t dev, int prio); + +void +omap4_padconf_init(device_t dev, int prio); + + +struct omap4_intr_conf { + int num; + unsigned int priority; + unsigned int target_cpu; +}; + +int +omap4_setup_intr_controller(struct omap4_softc *sc, + const struct omap4_intr_conf *irqs); + +int +omap4_setup_gic_cpu(struct omap4_softc *sc, unsigned int prio_mask); + +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/omap/omap4/pandaboard/files.pandaboard b/sys/arm/omap/omap4/pandaboard/files.pandaboard new file mode 100644 index 0000000..f5d430b --- /dev/null +++ b/sys/arm/omap/omap4/pandaboard/files.pandaboard @@ -0,0 +1,3 @@ +# $FreeBSD$ + +arm/omap/omap4/pandaboard/pandaboard.c standard diff --git a/sys/arm/omap/omap4/pandaboard/pandaboard.c b/sys/arm/omap/omap4/pandaboard/pandaboard.c new file mode 100644 index 0000000..e75966b --- /dev/null +++ b/sys/arm/omap/omap4/pandaboard/pandaboard.c @@ -0,0 +1,200 @@ +/*- + * 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 (OMAP44XX_SCRM_VBASE + 0x110) +#define SCRM_AUXCLK0 (OMAP44XX_SCRM_VBASE + 0x0310) +#define SCRM_AUXCLK1 (OMAP44XX_SCRM_VBASE + 0x0314) +#define SCRM_AUXCLK2 (OMAP44XX_SCRM_VBASE + 0x0318) +#define SCRM_AUXCLK3 (OMAP44XX_SCRM_VBASE + 0x031C) + +/* Some of the GPIO register set */ +#define GPIO1_OE (OMAP44XX_GPIO1_VBASE + 0x0134) +#define GPIO1_CLEARDATAOUT (OMAP44XX_GPIO1_VBASE + 0x0190) +#define GPIO1_SETDATAOUT (OMAP44XX_GPIO1_VBASE + 0x0194) +#define GPIO2_OE (OMAP44XX_GPIO2_VBASE + 0x0134) +#define GPIO2_CLEARDATAOUT (OMAP44XX_GPIO2_VBASE + 0x0190) +#define GPIO2_SETDATAOUT (OMAP44XX_GPIO2_VBASE + 0x0194) + +/* Some of the PADCONF register set */ +#define CONTROL_WKUP_PAD0_FREF_CLK3_OUT (OMAP44XX_SCM_PADCONF_VBASE + 0x058) +#define CONTROL_CORE_PAD1_KPD_COL2 (OMAP44XX_SCM_PADCONF_VBASE + 0x186) +#define CONTROL_CORE_PAD0_GPMC_WAIT1 (OMAP44XX_SCM_PADCONF_VBASE + 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) +{ + + + /* 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. + */ + //int pouet = REG_READ32(SCRM_AUXCLK3); + REG_WRITE32(SCRM_AUXCLK3, (1 << 16) | /* Divider of 2 */ + (0 << 1) | /* Use the SYS_CLK as the source */ + (1 << 8)); /* Enable the clock */ + +#if 0 + REG_WRITE32(SCRM_ALTCLKSRC, (1 << 1) | ( 3 << 2)); +#endif + /* Enable the clock out to the pin (GPIO_WK31). + * muxmode=fref_clk3_out, pullup/down=disabled, input buffer=disabled, + * wakeup=disabled. + */ + REG_WRITE16(CONTROL_WKUP_PAD0_FREF_CLK3_OUT, 0x0000); + + + /* Disable the power to the USB hub, drive GPIO1 low */ + REG_WRITE32(GPIO1_OE, REG_READ32(GPIO1_OE) & ~(1UL << 1)); + REG_WRITE32(GPIO1_CLEARDATAOUT, (1UL << 1)); + REG_WRITE16(CONTROL_CORE_PAD1_KPD_COL2, 0x0003); + + + /* Reset the USB PHY and Hub using GPIO_62 */ + REG_WRITE32(GPIO2_OE, REG_READ32(GPIO2_OE) & ~(1UL << 30)); + REG_WRITE32(GPIO2_CLEARDATAOUT, (1UL << 30)); + REG_WRITE16(CONTROL_CORE_PAD0_GPMC_WAIT1, 0x0003); + DELAY(10); + REG_WRITE32(GPIO2_SETDATAOUT, (1UL << 30)); + + + /* Enable power to the hub (GPIO_1) */ + REG_WRITE32(GPIO1_SETDATAOUT, (1UL << 1)); + +} + + +/** + * 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 omap44xx.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/omap/omap4/pandaboard/std.pandaboard b/sys/arm/omap/omap4/pandaboard/std.pandaboard new file mode 100644 index 0000000..379ac6b --- /dev/null +++ b/sys/arm/omap/omap4/pandaboard/std.pandaboard @@ -0,0 +1,4 @@ +# $FreeBSD$ + +include "../omap/omap4/std.omap44xx" +files "../omap/omap4/pandaboard/files.pandaboard" diff --git a/sys/arm/omap/omap4/std.omap44xx b/sys/arm/omap/omap4/std.omap44xx new file mode 100644 index 0000000..359b8c2 --- /dev/null +++ b/sys/arm/omap/omap4/std.omap44xx @@ -0,0 +1,21 @@ +# Omap4430 generic configuration +#$FreeBSD$ +files "../omap/omap4/files.omap44xx" +include "../omap/std.omap" +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/omap/omap4/uart_cpu_omap4.c b/sys/arm/omap/omap4/uart_cpu_omap4.c new file mode 100644 index 0000000..ce09448 --- /dev/null +++ b/sys/arm/omap/omap4/uart_cpu_omap4.c @@ -0,0 +1,90 @@ +/*- + * 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 + +bus_space_tag_t uart_bus_space_io; +bus_space_tag_t uart_bus_space_mem; + + +#define OMAP44XX_UART_FREQ 48000000 /* 48Mhz clock for all uarts (techref 17.3.1.1) */ + + +/* --------------------------------------------------------------- */ +/* uart_cpu_eqres + * + * Checks if the two UART base addresses are equal, if they are + * 1 is returned, otherwise 0 is returned. + */ +int uart_cpu_eqres(struct uart_bas *b1, struct uart_bas *b2) +{ + return ((b1->bsh == b2->bsh && b1->bst == b2->bst) ? 1 : 0); +} + +/* --------------------------------------------------------------- */ +/* uart_cpu_getdev + * + * + * + */ +int uart_cpu_getdev(int devtype, struct uart_devinfo *di) +{ + di->ops = uart_getops(&uart_ns8250_class); + di->bas.chan = 0; + di->bas.regshft = 2; + di->bas.rclk = OMAP44XX_UART_FREQ; + di->bas.bst = &omap_bs_tag; + + if (bus_space_map(di->bas.bst, OMAP44XX_UART3_HWBASE, OMAP44XX_UART3_SIZE, + 0, &di->bas.bsh) != 0) { + return (ENXIO); + } + + di->baudrate = 115200; + di->databits = 8; + di->stopbits = 1; + di->parity = UART_PARITY_NONE; + uart_bus_space_mem = NULL; + uart_bus_space_io = &omap_bs_tag; + + return (0); +} diff --git a/sys/arm/omap/omap_cpuid.c b/sys/arm/omap/omap_cpuid.c new file mode 100644 index 0000000..4dbb71f --- /dev/null +++ b/sys/arm/omap/omap_cpuid.c @@ -0,0 +1,245 @@ +/*- + * 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 author may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 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 +#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 + + +#define REG_READ32(r) *((volatile uint32_t*)(r)) + + + +static uint32_t g_omap_revision = (uint32_t)-1; + + + +/** + * omap_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 +omap_revision(void) +{ + return g_omap_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; + + /* 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); + + 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) + g_omap_revision = OMAP4430_REV_ES1_0; + else + g_omap_revision = OMAP4430_REV_ES2_0; + break; + case 0xB95C: + if (revision == 3) + g_omap_revision = OMAP4430_REV_ES2_1; + else if (revision == 4) + g_omap_revision = OMAP4430_REV_ES2_2; + else + g_omap_revision = OMAP4430_REV_ES2_3; + break; + default: + /* Default to the latest revision if we can't determine type */ + g_omap_revision = OMAP4430_REV_ES2_3; + break; + } + + printf("OMAP%04x ES%u.%u\n", OMAP_REV_DEVICE(g_omap_revision), + OMAP_REV_MAJOR(g_omap_revision), OMAP_REV_MINOR(g_omap_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; + + /* 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); + + hawkeye = ((id_code >> 12) & 0xffff); + revision = ((id_code >> 28) & 0xf); + + switch (hawkeye) { + case 0xB6D6: + g_omap_revision = OMAP3350_REV_ES1_0; + break; + case 0xB7AE: + if (revision == 1) + g_omap_revision = OMAP3530_REV_ES2_0; + else if (revision == 2) + g_omap_revision = OMAP3530_REV_ES2_1; + else if (revision == 3) + g_omap_revision = OMAP3530_REV_ES3_0; + else if (revision == 4) + g_omap_revision = OMAP3530_REV_ES3_1; + else if (revision == 7) + g_omap_revision = OMAP3530_REV_ES3_1_2; + break; + default: + /* Default to the latest revision if we can't determine type */ + g_omap_revision = OMAP3530_REV_ES3_1_2; + break; + } + + printf("OMAP%04x ES%u.%u\n", OMAP_REV_DEVICE(g_omap_revision), + OMAP_REV_MAJOR(g_omap_revision), OMAP_REV_MINOR(g_omap_revision)); +} + + + +/** + * omap_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 +omap_cpu_ident(void *dummy) +{ + if (omap_is_omap3()) + omap3_get_revision(); + else if (omap_is_omap4()) + omap4_get_revision(); + else + panic("Unknown OMAP chip type, fixme!\n"); + +} +SYSINIT(omap_cpu_ident, SI_SUB_CPU, SI_ORDER_SECOND, omap_cpu_ident, NULL); + + diff --git a/sys/arm/omap/omap_cpuid.h b/sys/arm/omap/omap_cpuid.h new file mode 100644 index 0000000..f9bbbe5 --- /dev/null +++ b/sys/arm/omap/omap_cpuid.h @@ -0,0 +1,82 @@ +/*- + * 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 author may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 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. + */ + +#ifndef _OMAP_CPUID_H_ +#define _OMAP_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 OMAP3530_DEV 0x3530 +#define OMAP4430_DEV 0x4430 + + +#define OMAP3350_REV_ES1_0 OMAP_MAKEREV(OMAP3530_DEV, 1, 0, 0) +#define OMAP3530_REV_ES2_0 OMAP_MAKEREV(OMAP3530_DEV, 2, 0, 0) +#define OMAP3530_REV_ES2_1 OMAP_MAKEREV(OMAP3530_DEV, 2, 1, 0) +#define OMAP3530_REV_ES3_0 OMAP_MAKEREV(OMAP3530_DEV, 3, 0, 0) +#define OMAP3530_REV_ES3_1 OMAP_MAKEREV(OMAP3530_DEV, 3, 1, 0) +#define OMAP3530_REV_ES3_1_2 OMAP_MAKEREV(OMAP3530_DEV, 3, 1, 2) + +#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) + + + + +#if defined(SOC_OMAP4) +# define omap_is_omap4() (1) +# define omap_is_omap3() (0) + +#elif defined(SOC_OMAP3) +# define omap_is_omap4() (0) +# define omap_is_omap3() (1) + +#else +# error OMAP chip type not defined, ensure SOC_OMAPxxxx is defined +#endif + + +uint32_t +omap_revision(void); + + + + +#endif /* _OMAP_CPUID_H_ */ diff --git a/sys/arm/omap/omap_dma.c b/sys/arm/omap/omap_dma.c new file mode 100644 index 0000000..6828365 --- /dev/null +++ b/sys/arm/omap/omap_dma.c @@ -0,0 +1,1781 @@ +/*- + * 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 author may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 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 +#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. + * + */ + + +/** + * The number of DMA channels possible on the controller. + */ +#define NUM_DMA_CHANNELS 32 + + +/** + * 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 + + + +/** + * Data structure per DMA channel. + * + * + */ +struct omap_dma_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 g_omap_dma_sc). + * + */ +struct omap_dma_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 omap_dma_channel + sc_channel[NUM_DMA_CHANNELS]; + +}; + +static struct omap_dma_softc *g_omap_dma_sc = NULL; + + +/** + * Macros for driver mutex locking + */ +#define OMAP_DMA_LOCK(_sc) mtx_lock_spin(&(_sc)->sc_mtx) +#define OMAP_DMA_UNLOCK(_sc) mtx_unlock_spin(&(_sc)->sc_mtx) +#define OMAP_DMA_LOCK_INIT(_sc) \ + mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->sc_dev), \ + "omap_dma", MTX_SPIN) +#define OMAP_DMA_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); +#define OMAP_DMA_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED); +#define OMAP_DMA_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED); + + + +/** + * Function prototypes + * + */ +static void omap_dma_intr(void *); + + + + +/** + * omap_dma_readl - 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 +omap_dma_readl(struct omap_dma_softc *sc, bus_size_t off) +{ + return bus_read_4(sc->sc_mem_res, off); +} + +/** + * omap_dma_writel - 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 +omap_dma_writel(struct omap_dma_softc *sc, bus_size_t off, uint32_t val) +{ + bus_write_4(sc->sc_mem_res, off, val); +} + + + +/** + * omap_dma_is_omap3_rev - returns true if H/W is from OMAP3 series + * omap_dma_is_omap4_rev - returns true if H/W is from OMAP4 series + * @sc: DMA device context + * + */ +static inline int +omap_dma_is_omap3_rev(struct omap_dma_softc *sc) +{ + return (sc->sc_hw_rev == DMA4_OMAP3_REV); +} +static inline int +omap_dma_is_omap4_rev(struct omap_dma_softc *sc) +{ + return (sc->sc_hw_rev == DMA4_OMAP4_REV); +} + + + + +/** + * omap_dma_test - test function, to be removed + * + * Simple test function use to verify the DMA behaviour + * + * RETURNS: + * 32-bit value read from the register. + */ +#if 0 +static void +omap_dma_test(void) +{ + struct omap_dma_softc *sc = g_omap_dma_sc; + unsigned int ch; + uint8_t *src; + uint8_t *dst; + vm_paddr_t src_pa; + vm_paddr_t dst_pa; + unsigned int timeout = 0; + uint32_t status = 0; + + src = malloc(PAGE_SIZE, M_DEVBUF, M_NOWAIT); + dst = malloc(PAGE_SIZE, M_DEVBUF, M_NOWAIT); + + src_pa = pmap_extract(pmap_kernel(), (vm_offset_t)src); + dst_pa = pmap_extract(pmap_kernel(), (vm_offset_t)dst); + + printf("[BRG] %s : %d : src=%p [PA: 0x%08x]\n", __func__, __LINE__, src, src_pa); + printf("[BRG] %s : %d : dst=%p [PA: 0x%08x]\n", __func__, __LINE__, dst, dst_pa); + + + if (omap_dma_activate_channel(&ch, NULL, NULL) != 0) { + printf("[BRG] %s : %d : omap_dma_activate_channel failed\n", __func__, __LINE__); + goto err_out; + } + + printf("[BRG] %s : %d : activated channel number %u\n", __func__, __LINE__, ch); + +// if (omap_dma_sync_params(ch, 0, DMA_SYNC_BLOCK) != 0) { +// printf("[BRG] %s : %d : omap_dma_sync_params failed\n", __func__, __LINE__); +// goto out; +// } + + + if (omap_dma_enable_channel_irq(ch, SDMA_IRQ_FLAG_BLOCK_COMPL) != 0) { + printf("[BRG] %s : %d : omap_dma_enable_channel_irq failed\n", __func__, __LINE__); + goto out; + } + + + if (omap_dma_start_xfer(ch, src_pa, dst_pa, (PAGE_SIZE / 4), 1) != 0) { + printf("[BRG] %s : %d : omap_dma_start_xfer failed\n", __func__, __LINE__); + goto out; + } + + + + + + while (status == 0 && timeout++ < 10000) { + if (omap_dma_get_channel_status(ch, &status) != 0) { + printf("[BRG] %s : %d : omap_dma_get_channel_status failed\n", __func__, __LINE__); + break; + } + + if ((timeout % 10) == 0) { + printf("[BRG] : DMA4_CSAC 0x%08x : DMA4_CDAC 0x%08x : DMA4_CSR 0x%08x\n", + omap_dma_readl(sc, DMA4_CSAC(ch)), + omap_dma_readl(sc, DMA4_CDAC(ch)), + status); + } + } + + printf("[BRG] %s : %d : status 0x%04x : timeout %d\n", __func__, __LINE__, status, timeout); + +out: + if (omap_dma_deactivate_channel(ch) != 0) { + printf("[BRG] %s : %d : omap_dma_deactivate_channel failed\n", __func__, __LINE__); + } + +err_out: + free(src, M_DEVBUF); + free(dst, M_DEVBUF); +} +#endif + + +/** + * omap_dma_dbg_dump_regs - test function, to be removed + * @arg: + * + * + * + * LOCKING: + * DMA registers protected by internal mutex + * + * RETURNS: + * EH_HANDLED or EH_NOT_HANDLED + */ +#if 0 +static void +omap_dma_dbg_dump_regs(unsigned int ch) +{ + struct omap_dma_softc *sc = g_omap_dma_sc; + + /* + "\tDMA4_SYSSTATUS 0x%08x\n" + "\tDMA4_OCP_SYSCONFIG 0x%08x\n" + "\tDMA4_CAPS_0 0x%08x\n" + "\tDMA4_CAPS_2 0x%08x\n" + "\tDMA4_CAPS_3 0x%08x\n" + "\tDMA4_CAPS_4 0x%08x\n" + "\tDMA4_GCR 0x%08x\n" + "\tDMA4_CCR 0x%08x\n" + */ + + printf("DMA register set (channel %u):\n" + "\tDMA4_IRQSTATUS_L 0x%08x 0x%08x 0x%08x 0x%08x\n" + "\tDMA4_IRQENABLE_L 0x%08x 0x%08x 0x%08x 0x%08x\n" + "\tDMA4_CCR 0x%08x\n" + "\tDMA4_CLNK_CTRL 0x%08x\n" + "\tDMA4_CICR 0x%08x\n" + "\tDMA4_CSR 0x%08x\n" + "\tDMA4_CSDP 0x%08x\n" + "\tDMA4_CEN 0x%08x\n" + "\tDMA4_CFN 0x%08x\n" + "\tDMA4_CSSA 0x%08x\n" + "\tDMA4_CDSA 0x%08x\n" + "\tDMA4_CSE 0x%08x\n" + "\tDMA4_CSF 0x%08x\n" + "\tDMA4_CDE 0x%08x\n" + "\tDMA4_CDF 0x%08x\n" + "\tDMA4_CSAC 0x%08x\n" + "\tDMA4_CDAC 0x%08x\n" + "\tDMA4_CCEN 0x%08x\n" + "\tDMA4_CCFN 0x%08x\n" + "\tDMA4_COLOR 0x%08x\n", + ch, + omap_dma_readl(sc, DMA4_IRQSTATUS_L(0)), + omap_dma_readl(sc, DMA4_IRQSTATUS_L(1)), + omap_dma_readl(sc, DMA4_IRQSTATUS_L(2)), + omap_dma_readl(sc, DMA4_IRQSTATUS_L(3)), + omap_dma_readl(sc, DMA4_IRQENABLE_L(0)), + omap_dma_readl(sc, DMA4_IRQENABLE_L(1)), + omap_dma_readl(sc, DMA4_IRQENABLE_L(2)), + omap_dma_readl(sc, DMA4_IRQENABLE_L(3)), + omap_dma_readl(sc, DMA4_CCR(ch)), + omap_dma_readl(sc, DMA4_CLNK_CTRL(ch)), + omap_dma_readl(sc, DMA4_CICR(ch)), + omap_dma_readl(sc, DMA4_CSR(ch)), + omap_dma_readl(sc, DMA4_CSDP(ch)), + omap_dma_readl(sc, DMA4_CEN(ch)), + omap_dma_readl(sc, DMA4_CFN(ch)), + omap_dma_readl(sc, DMA4_CSSA(ch)), + omap_dma_readl(sc, DMA4_CDSA(ch)), + omap_dma_readl(sc, DMA4_CSE(ch)), + omap_dma_readl(sc, DMA4_CSF(ch)), + omap_dma_readl(sc, DMA4_CDE(ch)), + omap_dma_readl(sc, DMA4_CDF(ch)), + omap_dma_readl(sc, DMA4_CSAC(ch)), + omap_dma_readl(sc, DMA4_CDAC(ch)), + omap_dma_readl(sc, DMA4_CCEN(ch)), + omap_dma_readl(sc, DMA4_CCFN(ch)), + omap_dma_readl(sc, DMA4_COLOR(ch)) + ); + +} +#endif + + +#if 0 +/** + * omap_dma_test - test function, to be removed + * + * Simple test function use to verify the DMA behaviour + * + * RETURNS: + * 32-bit value read from the register. + */ +static void +omap_dma_test_copy(struct omap_dma_softc *sc, vm_paddr_t dst_pa, + vm_paddr_t src_pa, size_t cnt) +{ + unsigned int ch; + unsigned int timeout = 0; + uint32_t status = 0; + + device_printf(sc->sc_dev, "omap_dma_test_copy : dst=0x%08x : src=0x%08x\n", + dst_pa, src_pa); + + if (omap_dma_activate_channel(&ch, NULL, NULL) != 0) { + device_printf(sc->sc_dev, "omap_dma_activate_channel failed\n"); + return; + } + + device_printf(sc->sc_dev, "activated channel number %u\n", ch); + +// if (omap_dma_sync_params(ch, 0, DMA_SYNC_BLOCK) != 0) { +// printf("[BRG] %s : %d : omap_dma_sync_params failed\n", __func__, __LINE__); +// goto out; +// } + + if (omap_dma_set_xfer_data_type(ch, OMAP_SDMA_DATA_8BITS_SCALAR) != 0) { + device_printf(sc->sc_dev, "omap_dma_enable_channel_irq failed\n"); + goto out; + } + + if (omap_dma_enable_channel_irq(ch, OMAP_SDMA_IRQ_FLAG_BLOCK_COMPL) != 0) { + device_printf(sc->sc_dev, "omap_dma_enable_channel_irq failed\n"); + goto out; + } + + if (omap_dma_start_xfer(ch, src_pa, dst_pa, cnt, 1) != 0) { + device_printf(sc->sc_dev, "omap_dma_start_xfer failed\n"); + goto out; + } + + while (status == 0 && timeout++ < 10000) { + if (omap_dma_get_channel_status(ch, &status) != 0) { + device_printf(sc->sc_dev, "omap_dma_get_channel_status failed\n"); + break; + } + + if ((timeout % 10) == 0) { + device_printf(sc->sc_dev, "DMA4_CSAC 0x%08x : DMA4_CDAC 0x%08x : DMA4_CSR 0x%08x\n", + omap_dma_readl(sc, DMA4_CSAC(ch)), + omap_dma_readl(sc, DMA4_CDAC(ch)), + status); + } + } + + device_printf(sc->sc_dev, "status 0x%04x : timeout %d\n", status, timeout); + +out: + if (omap_dma_deactivate_channel(ch) != 0) { + device_printf(sc->sc_dev, "omap_dma_deactivate_channel failed\n"); + } +} + + +static void +omap_dma_test_getaddr(void *arg, bus_dma_segment_t *segs, int nsegs, int err) +{ + if (err != 0) + return; + *(bus_addr_t *)arg = segs[0].ds_addr; +} + + +static int +omap_dma_test(void) +{ + struct omap_dma_softc *sc = g_omap_dma_sc; + int err; + unsigned int bufsize = PAGE_SIZE; + unsigned int tstsize = 33; + unsigned int tstoff = 67; + unsigned int i; + bus_dma_tag_t dmatag; + bus_dmamap_t src_dmamap; + bus_dmamap_t dst_dmamap; + uint8_t *src = NULL; + uint8_t *dst = NULL; + vm_paddr_t src_pa; + vm_paddr_t dst_pa; + + device_printf(sc->sc_dev, "Starting DMA test\n"); + + /* Allocate DMA tags and maps */ + err = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, + NULL, MAXPHYS, 1, MAXPHYS, BUS_DMA_ALLOCNOW, NULL, + NULL, &dmatag); + if (err != 0) + return(err); + + err = bus_dmamap_create(dmatag, 0, &src_dmamap); + if (err != 0) { + device_printf(sc->sc_dev, "Error - bus_dmamap_create failed\n"); + bus_dma_tag_destroy(dmatag); + return(err); + } + err = bus_dmamap_create(dmatag, 0, &dst_dmamap); + if (err != 0) { + device_printf(sc->sc_dev, "Error - bus_dmamap_create failed\n"); + bus_dmamap_destroy(dmatag, src_dmamap); + bus_dma_tag_destroy(dmatag); + return(err); + } + + /* Allocate a source and destination buffer for the test */ + src = malloc(bufsize, M_DEVBUF, M_NOWAIT); + dst = malloc(bufsize, M_DEVBUF, M_NOWAIT); + + /* Load a pattern into the source buffer */ + for (i = 0; i < bufsize; i++) { + src[i] = (uint8_t) (random() & 0xff); + } + + for (i = tstoff; i < (tstoff + tstsize); i++) { + if (!(i & 0xf)) + printf("\n0x%04x : ", i); + printf("%02x ", src[i]); + } + printf("\n"); + + /* Map the buffer buf into bus space using the dmamap map. */ + err = bus_dmamap_load(dmatag, src_dmamap, src + tstoff, tstsize, + omap_dma_test_getaddr, &src_pa, 0); + if (err != 0) { + device_printf(sc->sc_dev, "Error failed to load dma map\n"); + goto out; + } + err = bus_dmamap_load(dmatag, dst_dmamap, dst + tstoff, tstsize, + omap_dma_test_getaddr, &dst_pa, 0); + if (err != 0) { + device_printf(sc->sc_dev, "Error failed to load dma map\n"); + bus_dmamap_unload(dmatag, src_dmamap); + goto out; + } + + /* Ensure any caches are invalidated/flushed prior to the operation */ + bus_dmamap_sync(dmatag, src_dmamap, BUS_DMASYNC_PREWRITE); + + /* Perform the dma operation */ + omap_dma_test_copy(sc, dst_pa, src_pa, tstsize); + + /* Ensure any caches are invalidated/flushed prior to the operation */ + bus_dmamap_sync(dmatag, dst_dmamap, BUS_DMASYNC_POSTREAD); + + /* Unload the maps */ + bus_dmamap_unload(dmatag, src_dmamap); + bus_dmamap_unload(dmatag, dst_dmamap); + + /* Check the memory after the DMA operation */ + for (i = tstoff; i < (tstoff + tstsize); i++) { + if (src[i] != dst[i]) { + device_printf(sc->sc_dev, "Error: mismatch in the data after copy\n"); + break; + } + } + + if (i == (tstoff + tstsize)) + device_printf(sc->sc_dev, "DMA completed successifully\n"); + + for (i = tstoff; i < (tstoff + tstsize); i++) { + if (!(i & 0xf)) + printf("\n0x%04x : ", i); + printf("%02x ", dst[i]); + } + printf("\n"); + + +out: + if (src != NULL) + free(src, M_DEVBUF); + if (dst != NULL) + free(dst, M_DEVBUF); + + bus_dmamap_destroy(dmatag, src_dmamap); + bus_dmamap_destroy(dmatag, src_dmamap); + bus_dma_tag_destroy(dmatag); + + return(0); +} +#endif + + + +/** + * omap_dma_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 +omap_dma_intr(void *arg) +{ + struct omap_dma_softc *sc = g_omap_dma_sc; + uint32_t intr; + uint32_t csr; + unsigned int ch, j; + struct omap_dma_channel* channel; + + OMAP_DMA_LOCK(sc); + + for (j=0; j<4; j++) { + + /* Get the flag interrupts (enabled) */ + intr = omap_dma_readl(sc, DMA4_IRQSTATUS_L(j)); + intr &= omap_dma_readl(sc, DMA4_IRQENABLE_L(j)); + if (intr == 0x00000000) + continue; + + + /* Loop through checking the status bits */ + for (ch=0; ch<32; ch++) { + if (intr & (1 << ch)) { + channel = &sc->sc_channel[ch]; + + /* Read the CSR regsiter and verify we don't have a spurious IRQ */ + csr = omap_dma_readl(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 */ + omap_dma_writel(sc, DMA4_CSR(ch), DMA4_CSR_CLEAR_MASK); + omap_dma_writel(sc, DMA4_IRQSTATUS_L(j), (1 << ch)); + + /* Call the callback for the given channel */ + if (channel->callback) + channel->callback(ch, csr, channel->callback_data); + + } + } + } + + OMAP_DMA_UNLOCK(sc); + + return; +} + + + +/** + * omap_dma_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 + * omap_dma_enable_channel_irq(). If not using IRQ to detect the end of the + * transfer, you can use omap_dma_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 +omap_dma_activate_channel(unsigned int *ch, + void (*callback)(unsigned int ch, uint32_t status, void *data), + void *data) +{ + struct omap_dma_softc *sc = g_omap_dma_sc; + struct omap_dma_channel *channel = NULL; + uint32_t adr; + + /* Sanity check */ + if (sc == NULL) + return (ENOMEM); + if (ch == NULL) + return (EINVAL); + + OMAP_DMA_LOCK(sc); + + /* Check to see if all channels are in use */ + if (sc->sc_active_channels == 0xffffffff) { + OMAP_DMA_UNLOCK(sc); + return (ENOMEM); + } + + /* Find the first non-active channel */ + *ch = ffs(sc->sc_active_channels); + + /* Mark the channel as active */ + sc->sc_active_channels |= (1 << *ch); + + + + /* 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 (adr = DMA4_CCR(*ch); adr <= DMA4_COLOR(*ch); adr += 4) + omap_dma_writel(sc, adr, 0x00000000); + + OMAP_DMA_UNLOCK(sc); + + return 0; +} + + +/** + * omap_dma_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 +omap_dma_deactivate_channel(unsigned int ch) +{ + struct omap_dma_softc *sc = g_omap_dma_sc; + unsigned int j; + unsigned int adr; + + /* Sanity check */ + if (sc == NULL) + return (ENOMEM); + + + OMAP_DMA_LOCK(sc); + + /* First check if the channel is currently active */ + if ((sc->sc_active_channels & (1 << ch)) == 0) { + OMAP_DMA_UNLOCK(sc); + return (EBUSY); + } + + /* Mark the channel as inactive */ + sc->sc_active_channels &= ~(1 << ch); + + + /* Disable all DMA interrupts for the channel. */ + omap_dma_writel(sc, DMA4_CICR(ch), 0); + + /* Make sure the DMA transfer is stopped. */ + omap_dma_writel(sc, DMA4_CCR(ch), 0); + + + /* Clear the CSR register and IRQ status register */ + omap_dma_writel(sc, DMA4_CSR(ch), DMA4_CSR_CLEAR_MASK); + for (j=0; j<4; j++) { + omap_dma_writel(sc, DMA4_IRQSTATUS_L(j), (1 << ch)); + } + + + + /* Clear all the channel registers, this should abort any transaction */ + for (adr = DMA4_CCR(ch); adr <= DMA4_COLOR(ch); adr += 4) + omap_dma_writel(sc, adr, 0x00000000); + + + OMAP_DMA_UNLOCK(sc); + + return 0; +} + + +/** + * omap_dma_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 +omap_dma_disable_channel_irq(unsigned int ch) +{ + struct omap_dma_softc *sc = g_omap_dma_sc; + uint32_t irq_enable; + unsigned int j; + + /* Sanity check */ + if (sc == NULL) + return (ENOMEM); + + OMAP_DMA_LOCK(sc); + + if ((sc->sc_active_channels & (1 << ch)) == 0) { + OMAP_DMA_UNLOCK(sc); + return (EINVAL); + } + + /* Disable all the individual error conditions */ + sc->sc_channel[ch].reg_cicr = 0x0000; + omap_dma_writel(sc, DMA4_CICR(ch), 0x0000); + + /* Disable the channel interrupt enable */ + for (j=0; j<4; j++) { + irq_enable = omap_dma_readl(sc, DMA4_IRQENABLE_L(j)); + irq_enable &= ~(1 << ch); + + omap_dma_writel(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; + + OMAP_DMA_UNLOCK(sc); + + return (0); +} + +/** + * omap_dma_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 +omap_dma_enable_channel_irq(unsigned int ch, uint32_t flags) +{ + struct omap_dma_softc *sc = g_omap_dma_sc; + uint32_t irq_enable; + + /* Sanity check */ + if (sc == NULL) + return (ENOMEM); + + OMAP_DMA_LOCK(sc); + + if ((sc->sc_active_channels & (1 << ch)) == 0) { + OMAP_DMA_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 */ + omap_dma_writel(sc, DMA4_CICR(ch), flags); + + + /* Enable the channel interrupt enable */ + irq_enable = omap_dma_readl(sc, DMA4_IRQENABLE_L(0)); + irq_enable |= (1 << ch); + + omap_dma_writel(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; + + OMAP_DMA_UNLOCK(sc); + + return (0); +} + + +/** + * omap_dma_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 +omap_dma_get_channel_status(unsigned int ch, uint32_t *status) +{ + struct omap_dma_softc *sc = g_omap_dma_sc; + uint32_t csr; + + /* Sanity check */ + if (sc == NULL) + return (ENOMEM); + + OMAP_DMA_LOCK(sc); + + if ((sc->sc_active_channels & (1 << ch)) == 0) { + OMAP_DMA_UNLOCK(sc); + return (EINVAL); + } + + OMAP_DMA_UNLOCK(sc); + + csr = omap_dma_readl(sc, DMA4_CSR(ch)); + + if (status != NULL) + *status = csr; + + return (0); +} + + +/** + * omap_dma_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 omap_dma_set_xfer_burst() + * + * + * LOCKING: + * DMA registers protected by internal mutex + * + * RETURNS: + * EH_HANDLED or EH_NOT_HANDLED + */ +int +omap_dma_start_xfer(unsigned int ch, unsigned int src_paddr, + unsigned long dst_paddr, + unsigned int frmcnt, unsigned int elmcnt) +{ + struct omap_dma_softc *sc = g_omap_dma_sc; + struct omap_dma_channel *channel; + uint32_t ccr; + + /* Sanity check */ + if (sc == NULL) + return (ENOMEM); + + OMAP_DMA_LOCK(sc); + + if ((sc->sc_active_channels & (1 << ch)) == 0) { + OMAP_DMA_UNLOCK(sc); + return (EINVAL); + } + + channel = &sc->sc_channel[ch]; + + /* a) Write the CSDP register */ + //if (channel->need_reg_write) + omap_dma_writel(sc, DMA4_CSDP(ch), + channel->reg_csdp | DMA4_CSDP_WRITE_MODE(1)); + + + /* b) Set the number of element per frame CEN[23:0] */ + omap_dma_writel(sc, DMA4_CEN(ch), elmcnt); + + /* c) Set the number of frame per block CFN[15:0] */ + omap_dma_writel(sc, DMA4_CFN(ch), frmcnt); + + /* d) Set the Source/dest start address index CSSA[31:0]/CDSA[31:0] */ + omap_dma_writel(sc, DMA4_CSSA(ch), src_paddr); + omap_dma_writel(sc, DMA4_CDSA(ch), dst_paddr); + + + /* e) Write the CCR register */ + //if (channel->need_reg_write) + omap_dma_writel(sc, DMA4_CCR(ch), channel->reg_ccr); + + + /* f) - Set the source element index increment CSEI[15:0] */ + omap_dma_writel(sc, DMA4_CSE(ch), 0x0001); + + /* - Set the source frame index increment CSFI[15:0] */ + omap_dma_writel(sc, DMA4_CSF(ch), 0x0001); + + /* - Set the destination element index increment CDEI[15:0]*/ + omap_dma_writel(sc, DMA4_CDE(ch), 0x0001); + + /* - Set the destination frame index increment CDFI[31:0] */ + omap_dma_writel(sc, DMA4_CDF(ch), 0x0001); + + + + /* Clear the status register */ + omap_dma_writel(sc, DMA4_CSR(ch), 0x1FFE); + + /* DEBUG: omap_dma_dbg_dump_regs(ch); */ + + /* Write the start-bit and away we go */ + ccr = omap_dma_readl(sc, DMA4_CCR(ch)); + ccr |= (1 << 7); + omap_dma_writel(sc, DMA4_CCR(ch), ccr); + + + /* Clear the reg write flag */ + channel->need_reg_write = 0; + + OMAP_DMA_UNLOCK(sc); + + return (0); +} + + +/** + * omap_dma_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 omap_dma_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 +omap_dma_start_xfer_packet(unsigned int ch, unsigned int src_paddr, + unsigned long dst_paddr, unsigned int frmcnt, + unsigned int elmcnt, unsigned int pktsize) +{ + struct omap_dma_softc *sc = g_omap_dma_sc; + struct omap_dma_channel *channel; + uint32_t ccr; + + /* Sanity check */ + if (sc == NULL) + return (ENOMEM); + + OMAP_DMA_LOCK(sc); + + if ((sc->sc_active_channels & (1 << ch)) == 0) { + OMAP_DMA_UNLOCK(sc); + return (EINVAL); + } + + channel = &sc->sc_channel[ch]; + + /* a) Write the CSDP register */ + if (channel->need_reg_write) + omap_dma_writel(sc, DMA4_CSDP(ch), + channel->reg_csdp | DMA4_CSDP_WRITE_MODE(1)); + + + /* b) Set the number of elements to transfer CEN[23:0] */ + omap_dma_writel(sc, DMA4_CEN(ch), elmcnt); + + /* c) Set the number of frames to transfer CFN[15:0] */ + omap_dma_writel(sc, DMA4_CFN(ch), frmcnt); + + /* d) Set the Source/dest start address index CSSA[31:0]/CDSA[31:0] */ + omap_dma_writel(sc, DMA4_CSSA(ch), src_paddr); + omap_dma_writel(sc, DMA4_CDSA(ch), dst_paddr); + + + /* e) Write the CCR register */ + omap_dma_writel(sc, DMA4_CCR(ch), + channel->reg_ccr | DMA4_CCR_PACKET_TRANS); + + /* f) - Set the source element index increment CSEI[15:0] */ + omap_dma_writel(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)) + omap_dma_writel(sc, DMA4_CSF(ch), pktsize); + else + omap_dma_writel(sc, DMA4_CDE(ch), pktsize); + + /* - Set the destination frame index increment CDFI[31:0] */ + omap_dma_writel(sc, DMA4_CDF(ch), 0x0001); + + + + /* Clear the status register */ + omap_dma_writel(sc, DMA4_CSR(ch), 0x1FFE); + + /* DEBUG omap_dma_dbg_dump_regs(ch); */ + + /* Write the start-bit and away we go */ + ccr = omap_dma_readl(sc, DMA4_CCR(ch)); + ccr |= (1 << 7); + omap_dma_writel(sc, DMA4_CCR(ch), ccr); + + + /* Clear the reg write flag */ + channel->need_reg_write = 0; + + OMAP_DMA_UNLOCK(sc); + + return (0); +} + + +/** + * omap_dma_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 +omap_dma_stop_xfer(unsigned int ch) +{ + struct omap_dma_softc *sc = g_omap_dma_sc; + unsigned int j; + + /* Sanity check */ + if (sc == NULL) + return (ENOMEM); + + OMAP_DMA_LOCK(sc); + + if ((sc->sc_active_channels & (1 << ch)) == 0) { + OMAP_DMA_UNLOCK(sc); + return (EINVAL); + } + + + /* Disable all DMA interrupts for the channel. */ + omap_dma_writel(sc, DMA4_CICR(ch), 0); + + /* Make sure the DMA transfer is stopped. */ + omap_dma_writel(sc, DMA4_CCR(ch), 0); + + + /* Clear the CSR register and IRQ status register */ + omap_dma_writel(sc, DMA4_CSR(ch), DMA4_CSR_CLEAR_MASK); + for (j=0; j<4; j++) { + omap_dma_writel(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; + + OMAP_DMA_UNLOCK(sc); + + return (0); +} + + +/** + * omap_dma_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 +omap_dma_set_xfer_endianess(unsigned int ch, unsigned int src, unsigned int dst) +{ + struct omap_dma_softc *sc = g_omap_dma_sc; + + /* Sanity check */ + if (sc == NULL) + return (ENOMEM); + + OMAP_DMA_LOCK(sc); + + if ((sc->sc_active_channels & (1 << ch)) == 0) { + OMAP_DMA_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; + + OMAP_DMA_UNLOCK(sc); + + return 0; +} + + +/** + * omap_dma_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 +omap_dma_set_xfer_burst(unsigned int ch, unsigned int src, unsigned int dst) +{ + struct omap_dma_softc *sc = g_omap_dma_sc; + + /* Sanity check */ + if (sc == NULL) + return (ENOMEM); + + OMAP_DMA_LOCK(sc); + + if ((sc->sc_active_channels & (1 << ch)) == 0) { + OMAP_DMA_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; + + OMAP_DMA_UNLOCK(sc); + + return 0; +} + + +/** + * omap_dma_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 +omap_dma_set_xfer_data_type(unsigned int ch, unsigned int type) +{ + struct omap_dma_softc *sc = g_omap_dma_sc; + + /* Sanity check */ + if (sc == NULL) + return (ENOMEM); + + OMAP_DMA_LOCK(sc); + + if ((sc->sc_active_channels & (1 << ch)) == 0) { + OMAP_DMA_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; + + OMAP_DMA_UNLOCK(sc); + + return 0; +} + + +/** + * omap_dma_set_callback - driver attach function + * @dev: dma device handle + * + * + * + * LOCKING: + * DMA registers protected by internal mutex + * + * RETURNS: + * EH_HANDLED or EH_NOT_HANDLED + */ +int +omap_dma_set_callback(unsigned int ch, + void (*callback)(unsigned int ch, uint32_t status, void *data), + void *data) +{ + struct omap_dma_softc *sc = g_omap_dma_sc; + + /* Sanity check */ + if (sc == NULL) + return (ENOMEM); + + OMAP_DMA_LOCK(sc); + + if ((sc->sc_active_channels & (1 << ch)) == 0) { + OMAP_DMA_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; + + OMAP_DMA_UNLOCK(sc); + + return 0; +} + + +/** + * omap_dma_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; OMAP_SDMA_SYNC_FRAME, + * OMAP_SDMA_SYNC_BLOCK, OMAP_SDMA_SYNC_TRIG_ON_SRC. + * + * + * + * LOCKING: + * DMA registers protected by internal mutex + * + * RETURNS: + * EH_HANDLED or EH_NOT_HANDLED + */ +int +omap_dma_sync_params(unsigned int ch, unsigned int trigger, unsigned int mode) +{ + struct omap_dma_softc *sc = g_omap_dma_sc; + uint32_t ccr; + + /* Sanity check */ + if (sc == NULL) + return (ENOMEM); + + OMAP_DMA_LOCK(sc); + + if ((sc->sc_active_channels & (1 << ch)) == 0) { + OMAP_DMA_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 & OMAP_SDMA_SYNC_FRAME) + ccr |= DMA4_CCR_FRAME_SYNC(1); + else + ccr &= ~DMA4_CCR_FRAME_SYNC(1); + + if (mode & OMAP_SDMA_SYNC_BLOCK) + ccr |= DMA4_CCR_BLOCK_SYNC(1); + else + ccr &= ~DMA4_CCR_BLOCK_SYNC(1); + + if (mode & OMAP_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; + + OMAP_DMA_UNLOCK(sc); + + return 0; +} + + + +/** + * omap_dma_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 +omap_dma_set_addr_mode(unsigned int ch, unsigned int src_mode, + unsigned int dst_mode) +{ + struct omap_dma_softc *sc = g_omap_dma_sc; + uint32_t ccr; + + /* Sanity check */ + if (sc == NULL) + return (ENOMEM); + + OMAP_DMA_LOCK(sc); + + if ((sc->sc_active_channels & (1 << ch)) == 0) { + OMAP_DMA_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; + + OMAP_DMA_UNLOCK(sc); + + return 0; +} + + + + +/** + * omap_dma_probe - driver probe function + * @dev: dma device handle + * + * + * + * RETURNS: + * Always returns 0. + */ +static int +omap_dma_probe(device_t dev) +{ + + device_set_desc(dev, "TI OMAP sDMA Controller"); + return (0); +} + + +/** + * omap_dma_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 +omap_dma_attach(device_t dev) +{ + struct omap_dma_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 */ + OMAP_DMA_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 */ + omap_prcm_clk_enable(SDMA_CLK); + + + /* Read the sDMA revision register and sanity check it's known */ + sc->sc_hw_rev = omap_dma_readl(sc, DMA4_REVISION); + + if (!omap_dma_is_omap4_rev(sc) && !omap_dma_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<4; i++) { + omap_dma_writel(sc, DMA4_IRQENABLE_L(i), 0x00000000); + } + + /* Soft-reset is only supported on pre-OMAP44xx devices */ + if (omap_dma_is_omap3_rev(sc)) { + + /* Soft-reset */ + omap_dma_writel(sc, DMA4_OCP_SYSCONFIG, 0x0002); + + /* Set the timeout to 100ms*/ + timeout = (hz < 10) ? 1 : ((100 * hz) / 1000); + + /* Wait for DMA reset to complete */ + while ((omap_dma_readl(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, omap_dma_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 */ + g_omap_dma_sc = sc; + + + /* DEBUG : omap_dma_test(); */ + // omap_dma_test(); + + return (0); +} + + +static device_method_t g_omap_dma_methods[] = { + DEVMETHOD(device_probe, omap_dma_probe), + DEVMETHOD(device_attach, omap_dma_attach), + {0, 0}, +}; + +static driver_t g_omap_dma_driver = { + "omap_dma", + g_omap_dma_methods, + sizeof(struct omap_dma_softc), +}; +static devclass_t g_omap_dma_devclass; + +DRIVER_MODULE(omap_dma, omap, g_omap_dma_driver, g_omap_dma_devclass, 0, 0); +MODULE_DEPEND(omap_dma, omap_prcm, 1, 1, 1); + + + + + diff --git a/sys/arm/omap/omap_dma.h b/sys/arm/omap/omap_dma.h new file mode 100644 index 0000000..0f85fe6 --- /dev/null +++ b/sys/arm/omap/omap_dma.h @@ -0,0 +1,150 @@ +/*- + * 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 author may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 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. + */ + + +/** + * DMA device driver interface for the TI OMAP SoC + * + * See the omap_dma.c file for implementation details. + * + * Reference: + * OMAP35x Applications Processor + * Technical Reference Manual + * (omap35xx_techref.pdf) + */ +#ifndef _OMAP_DMA_H_ +#define _OMAP_DMA_H_ + + +#define OMAP_SDMA_ENDIAN_BIG 0x1 +#define OMAP_SDMA_ENDIAN_LITTLE 0x0 + +#define OMAP_SDMA_BURST_NONE 0x0 +#define OMAP_SDMA_BURST_16 0x1 +#define OMAP_SDMA_BURST_32 0x2 +#define OMAP_SDMA_BURST_64 0x3 + +#define OMAP_SDMA_DATA_8BITS_SCALAR 0x0 +#define OMAP_SDMA_DATA_16BITS_SCALAR 0x1 +#define OMAP_SDMA_DATA_32BITS_SCALAR 0x2 + +#define OMAP_SDMA_ADDR_CONSTANT 0x0 +#define OMAP_SDMA_ADDR_POST_INCREMENT 0x1 +#define OMAP_SDMA_ADDR_SINGLE_INDEX 0x2 +#define OMAP_SDMA_ADDR_DOUBLE_INDEX 0x3 + + +/** + * Status flags for the DMA callback + * + */ +#define OMAP_SDMA_STATUS_DROP (1UL << 1) +#define OMAP_SDMA_STATUS_HALF (1UL << 2) +#define OMAP_SDMA_STATUS_FRAME (1UL << 3) +#define OMAP_SDMA_STATUS_LAST (1UL << 4) +#define OMAP_SDMA_STATUS_BLOCK (1UL << 5) +#define OMAP_SDMA_STATUS_SYNC (1UL << 6) +#define OMAP_SDMA_STATUS_PKT (1UL << 7) +#define OMAP_SDMA_STATUS_TRANS_ERR (1UL << 8) +#define OMAP_SDMA_STATUS_SECURE_ERR (1UL << 9) +#define OMAP_SDMA_STATUS_SUPERVISOR_ERR (1UL << 10) +#define OMAP_SDMA_STATUS_MISALIGNED_ADRS_ERR (1UL << 11) +#define OMAP_SDMA_STATUS_DRAIN_END (1UL << 12) + +#define OMAP_SDMA_SYNC_FRAME (1UL << 0) +#define OMAP_SDMA_SYNC_BLOCK (1UL << 1) +#define OMAP_SDMA_SYNC_PACKET (OMAP_SDMA_SYNC_FRAME | OMAP_SDMA_SYNC_BLOCK) +#define OMAP_SDMA_SYNC_TRIG_ON_SRC (1UL << 8) +#define OMAP_SDMA_SYNC_TRIG_ON_DST (1UL << 9) + + +#define OMAP_SDMA_IRQ_FLAG_DROP (1UL << 1) +#define OMAP_SDMA_IRQ_FLAG_HALF_FRAME_COMPL (1UL << 2) +#define OMAP_SDMA_IRQ_FLAG_FRAME_COMPL (1UL << 3) +#define OMAP_SDMA_IRQ_FLAG_START_LAST_FRAME (1UL << 4) +#define OMAP_SDMA_IRQ_FLAG_BLOCK_COMPL (1UL << 5) +#define OMAP_SDMA_IRQ_FLAG_ENDOF_PKT (1UL << 7) +#define OMAP_SDMA_IRQ_FLAG_DRAIN (1UL << 12) + + + + +int +omap_dma_activate_channel(unsigned int *ch, + void (*callback)(unsigned int ch, uint32_t status, void *data), + void *data); + +int +omap_dma_deactivate_channel(unsigned int ch); + +int +omap_dma_start_xfer(unsigned int ch, unsigned int src_paddr, + unsigned long dst_paddr, + unsigned int frmcnt, unsigned int elmcnt); + +int +omap_dma_start_xfer_packet(unsigned int ch, unsigned int src_paddr, + unsigned long dst_paddr, unsigned int frmcnt, + unsigned int elmcnt, unsigned int pktsize); + +int +omap_dma_stop_xfer(unsigned int ch); + +int +omap_dma_enable_channel_irq(unsigned int ch, uint32_t flags); + +int +omap_dma_disable_channel_irq(unsigned int ch); + +int +omap_dma_get_channel_status(unsigned int ch, uint32_t *status); + + +int +omap_dma_set_xfer_endianess(unsigned int ch, unsigned int src, unsigned int dst); + +int +omap_dma_set_xfer_burst(unsigned int ch, unsigned int src, unsigned int dst); + +int +omap_dma_set_xfer_data_type(unsigned int ch, unsigned int type); + +int +omap_dma_set_callback(unsigned int ch, + void (*callback)(unsigned int ch, uint32_t status, void *data), + void *data); + +int +omap_dma_sync_params(unsigned int ch, unsigned int trigger, unsigned int mode); + +int +omap_dma_set_addr_mode(unsigned int ch, unsigned int src_mode, + unsigned int dst_mode); + + +#endif /* _OMAP_DMA_H_ */ diff --git a/sys/arm/omap/omap_ehci.c b/sys/arm/omap/omap_ehci.c new file mode 100644 index 0000000..4b889c2 --- /dev/null +++ b/sys/arm/omap/omap_ehci.c @@ -0,0 +1,1330 @@ +/*- + * 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 author may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 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. + */ + +/** + * 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. + * + */ + +/** + * HINTS + * + * The following hints control the way the driver sets up the H/W (check out + * BEAGLEBOARD.hints or PANDABOARD.hints for an example): + * + * hint.ehci.0.phy_reset + * If not zero the driver will attempt to reset the PHY by toggling a GPIO + * pin during EHCI initialisation. The pin to toggle is set by one of the + * following hints, a "-1" value means don't toggle. + * + * hint.ehci.0.phy_reset_gpio_0 + * hint.ehci.0.phy_reset_gpio_1 + * hint.ehci.0.phy_reset_gpio_2 + * See hint.ehci.0.phy_reset. + * + * hint.ehci.0.phy_mode_0 + * hint.ehci.0.phy_mode_1 + * hint.ehci.0.phy_mode_2 + * Tells the driver which mode to configure the port in, PHY, TLL, HSIC or + * NONE. The possible values are; 0 = EHCI_HCD_OMAP3_MODE_UNKNOWN, + * 1 = EHCI_HCD_OMAP3_MODE_PHY, 2 = EHCI_HCD_OMAP3_MODE_TLL and + * 3 = EHCI_HCD_OMAP3_MODE_HSIC + * + */ + +#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 "gpio_if.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_SYSSTATUS_EHCI_RESETDONE (1 << 2) + +#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 OMAP3_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 + + + +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; + 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_readl - read a 32-bit value from the USBTLL registers + * omap_tll_writel - 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_readl(struct omap_ehci_softc *sc, bus_size_t off) +{ + return bus_read_4(sc->tll_mem_res, off); +} +static inline void +omap_tll_writel(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_readl - read a 32-bit value from the EHCI registers + * omap_ehci_writel - 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_readl(struct omap_ehci_softc *sc, bus_size_t off) +{ + return (bus_read_4(sc->base.sc_io_res, off)); +} +static inline void +omap_ehci_writel(struct omap_ehci_softc *sc, bus_size_t off, uint32_t val) +{ + bus_write_4(sc->base.sc_io_res, off, val); +} + + + +/** + * omap_uhh_readl - read a 32-bit value from the UHH registers + * omap_uhh_writel - 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_readl(struct omap_ehci_softc *sc, bus_size_t off) +{ + return bus_read_4(sc->uhh_mem_res, off); +} +static inline void +omap_uhh_writel(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_readl(isc, OMAP_USBTLL_TLL_CHANNEL_CONF(i)); + + reg &= ~(TLL_CHANNEL_CONF_UTMIAUTOIDLE + | TLL_CHANNEL_CONF_ULPINOBITSTUFF + | TLL_CHANNEL_CONF_ULPIDDRMODE); + + omap_tll_writel(isc, OMAP_USBTLL_TLL_CHANNEL_CONF(i), reg); + } + + /* Program the common TLL register */ + reg = omap_tll_readl(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_writel(isc, OMAP_USBTLL_TLL_SHARED_CONF, reg); + + /* Enable channels now */ + for (i = 0; i < 3; i++) { + reg = omap_tll_readl(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_writel(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_writel(isc, OMAP_USBHOST_INSNREG05_ULPI, reg); + + timeout += 1000000; + /* Wait for ULPI access completion */ + while ((omap_ehci_readl(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; + + device_printf(isc->sc_dev, "Starting TI EHCI USB Controller\n"); + + + /* Enable Clocks for high speed USBHOST */ + omap_prcm_clk_enable(USBHSHOST_CLK); + + /* Hold the PHY in reset while configuring */ + if (isc->phy_reset) { + + /* Configure the GPIO to drive low (hold in reset) */ + if ((isc->reset_gpio_pin[0] != -1) && (isc->sc_gpio_dev != NULL)) { + GPIO_PIN_SETFLAGS(isc->sc_gpio_dev, isc->reset_gpio_pin[0], + GPIO_PIN_OUTPUT); + GPIO_PIN_SET(isc->sc_gpio_dev, isc->reset_gpio_pin[0], GPIO_PIN_LOW); + } + + /* Configure the GPIO to drive low (hold in reset) */ + if ((isc->reset_gpio_pin[1] != -1) && (isc->sc_gpio_dev != NULL)) { + GPIO_PIN_SETFLAGS(isc->sc_gpio_dev, isc->reset_gpio_pin[1], + GPIO_PIN_OUTPUT); + GPIO_PIN_SET(isc->sc_gpio_dev, isc->reset_gpio_pin[1], GPIO_PIN_LOW); + } + + /* Hold the PHY in RESET for enough time till DIR is high */ + DELAY(10); + } + + + /* Read the UHH revision */ + isc->ehci_rev = omap_uhh_readl(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 */ + omap_prcm_clk_enable(USBTLL_CLK); + + /* Perform TLL soft reset, and wait until reset is complete */ + omap_tll_writel(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_readl(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_writel(isc, OMAP_USBTLL_SYSCONFIG, TLL_SYSCONFIG_ENAWAKEUP | + TLL_SYSCONFIG_AUTOIDLE | + TLL_SYSCONFIG_SIDLE_SMART_IDLE | + TLL_SYSCONFIG_CACTIVITY); +// omap_tll_writel(isc, OMAP_USBTLL_SYSCONFIG, TLL_SYSCONFIG_ENAWAKEUP | +// TLL_SYSCONFIG_SIDLE_NO_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) { + omap_prcm_clk_set_source(USBP1_PHY_CLK, EXT_CLK); + omap_prcm_clk_enable(USBP1_PHY_CLK); + } else if (isc->port_mode[0] == EHCI_HCD_OMAP_MODE_TLL) + omap_prcm_clk_enable(USBP1_UTMI_CLK); + else if (isc->port_mode[0] == EHCI_HCD_OMAP_MODE_HSIC) + omap_prcm_clk_enable(USBP1_HSIC_CLK); + + if (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_PHY) { + omap_prcm_clk_set_source(USBP2_PHY_CLK, EXT_CLK); + omap_prcm_clk_enable(USBP2_PHY_CLK); + } else if (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_TLL) + omap_prcm_clk_enable(USBP2_UTMI_CLK); + else if (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_HSIC) + omap_prcm_clk_enable(USBP2_HSIC_CLK); + } + printf("UBERCORB\n"); + } + + + + + omap_tll_writel(isc, OMAP_USBTLL_SYSCONFIG, + TLL_SYSCONFIG_ENAWAKEUP | + TLL_SYSCONFIG_SIDLE_NO_IDLE | + TLL_SYSCONFIG_AUTOIDLE); +#if 0 + omap_uhh_writel(isc, OMAP_USBHOST_UHH_SYSCONFIG, /*UHH_SYSCONFIG_SOFTRESET*/1); + + /* Set the timeout to 100ms*/ + timeout = (hz < 10) ? 1 : ((100 * hz) / 1000); + + while ((omap_uhh_readl(isc, OMAP_USBHOST_UHH_SYSSTATUS) & + (UHH_SYSSTATUS_EHCI_RESETDONE | ( 1 << 1))) != ((1 << 1) | (1 << 2))) { + + /* Sleep for a tick */ + pause("USBRESET", 1); + + if (timeout-- == 0) { + device_printf(isc->sc_dev, "pouet reset operation timed out\n"); + ret = -EINVAL; + goto err_sys_status; + } + printf("POUIC\n"); + } + +#endif + /* Put UHH in SmartIdle/SmartStandby mode */ + reg = omap_uhh_readl(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_writel(isc, OMAP_USBHOST_UHH_SYSCONFIG, reg); + device_printf(isc->sc_dev, "OMAP_UHH_SYSCONFIG: 0x%08x\n", reg); + + reg = omap_uhh_readl(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_writel(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." + */ +// omap_ehci_writel(isc, OMAP_USBHOST_INSNREG04, +// OMAP_USBHOST_INSNREG04_DISABLE_UNSUSPEND); + + + /* 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 (isc->phy_reset) { + + /* Delay for 10ms */ + DELAY(10000); + + /* Release reset */ + if ((isc->reset_gpio_pin[0] != -1) && (isc->sc_gpio_dev != NULL)) + GPIO_PIN_SET(isc->sc_gpio_dev, isc->reset_gpio_pin[0], GPIO_PIN_HIGH); + + if ((isc->reset_gpio_pin[1] != -1) && (isc->sc_gpio_dev != NULL)) + GPIO_PIN_SET(isc->sc_gpio_dev, isc->reset_gpio_pin[1], 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_readl(isc, OMAP_USBHOST_USBCMD); + reg &= 0xff00ffff; + reg |= (1 << 16); + + omap_ehci_writel(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 */ + omap_prcm_clk_disable(USBTLL_CLK); + + /* Disable Clocks for USBHOST */ + omap_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_writel(isc, OMAP_USBHOST_UHH_SYSCONFIG, 0x0002); + while ((omap_uhh_readl(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_writel(isc, OMAP_USBTLL_SYSCONFIG, 0x0002); + while ((omap_tll_readl(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 */ + omap_prcm_clk_disable(USBTLL_CLK); + omap_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; + + err = bus_generic_suspend(dev); + if (err) + return (err); + ehci_suspend(sc); + 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); + + ehci_resume(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; + + err = bus_generic_shutdown(dev); + if (err) + return (err); + ehci_shutdown(sc); + + 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) +{ + device_set_desc(dev, OMAP3_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); + ehci_softc_t *sc = &isc->base; + int err; + int rid; + int rval; + + /* 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, OMAP3_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 */ + isc->phy_reset = 0; + isc->reset_gpio_pin[0] = isc->reset_gpio_pin[1] = isc->reset_gpio_pin[2] = -1; + isc->port_mode[0] = isc->port_mode[1] = isc->port_mode[2] = EHCI_HCD_OMAP_MODE_UNKNOWN; + + /* Hints are used to define the PHY interface settings */ + if (resource_int_value("ehci", 0, "phy_reset", &rval) == 0) + isc->phy_reset = rval; + + if (isc->phy_reset) { + printf("DOING PH RESET\n"); + if (resource_int_value("ehci", 0, "phy_reset_gpio_0", &rval) == 0) + isc->reset_gpio_pin[0] = rval; + if (resource_int_value("ehci", 0, "phy_reset_gpio_1", &rval) == 0) + isc->reset_gpio_pin[1] = rval; + if (resource_int_value("ehci", 0, "phy_reset_gpio_2", &rval) == 0) + isc->reset_gpio_pin[2] = rval; + } + + if (resource_int_value("ehci", 0, "phy_mode_0", &rval) == 0) + isc->port_mode[0] = rval; + if (resource_int_value("ehci", 0, "phy_mode_1", &rval) == 0) + isc->port_mode[1] = rval; + if (resource_int_value("ehci", 0, "phy_mode_2", &rval) == 0) + isc->port_mode[2] = rval; + printf("ports %d %d %d\n", isc->port_mode[0], isc->port_mode[1], isc->port_mode[2]); + + + /* 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, omap, ehci_driver, ehci_devclass, 0, 0); +MODULE_DEPEND(ehci, usb, 1, 1, 1); + +MODULE_DEPEND(ehci, omap_prcm, 1, 1, 1); +MODULE_DEPEND(ehci, omap_scm, 1, 1, 1); +MODULE_DEPEND(ehci, gpio, 1, 1, 1); diff --git a/sys/arm/omap/omap_gpio.c b/sys/arm/omap/omap_gpio.c new file mode 100644 index 0000000..b2e8eaf --- /dev/null +++ b/sys/arm/omap/omap_gpio.c @@ -0,0 +1,916 @@ +/*- + * 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 author may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 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. + */ + +/** + * 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 "gpio_if.h" + + + +#define OMAP_GPIO_REVISION 0x0000 +#define OMAP_GPIO_SYSCONFIG 0x0010 + +/* Register offsets of the GPIO banks on OMAP3 devices */ +#define OMAP3_GPIO_SYSSTATUS 0x0014 +#define OMAP3_GPIO_IRQSTATUS1 0x0018 +#define OMAP3_GPIO_IRQENABLE1 0x001C +#define OMAP3_GPIO_WAKEUPENABLE 0x0020 +#define OMAP3_GPIO_IRQSTATUS2 0x0028 +#define OMAP3_GPIO_IRQENABLE2 0x002C +#define OMAP3_GPIO_CTRL 0x0030 +#define OMAP3_GPIO_OE 0x0034 +#define OMAP3_GPIO_DATAIN 0x0038 +#define OMAP3_GPIO_DATAOUT 0x003C +#define OMAP3_GPIO_LEVELDETECT0 0x0040 +#define OMAP3_GPIO_LEVELDETECT1 0x0044 +#define OMAP3_GPIO_RISINGDETECT 0x0048 +#define OMAP3_GPIO_FALLINGDETECT 0x004C +#define OMAP3_GPIO_DEBOUNCENABLE 0x0050 +#define OMAP3_GPIO_DEBOUNCINGTIME 0x0054 +#define OMAP3_GPIO_CLEARIRQENABLE1 0x0060 +#define OMAP3_GPIO_SETIRQENABLE1 0x0064 +#define OMAP3_GPIO_CLEARIRQENABLE2 0x0070 +#define OMAP3_GPIO_SETIRQENABLE2 0x0074 +#define OMAP3_GPIO_CLEARWKUENA 0x0080 +#define OMAP3_GPIO_SETWKUENA 0x0084 +#define OMAP3_GPIO_CLEARDATAOUT 0x0090 +#define OMAP3_GPIO_SETDATAOUT 0x0094 + +/* Register offsets of the GPIO banks on OMAP4 devices */ +#define OMAP4_GPIO_IRQSTATUS_RAW_0 0x0024 +#define OMAP4_GPIO_IRQSTATUS_RAW_1 0x0028 +#define OMAP4_GPIO_IRQSTATUS_0 0x002C +#define OMAP4_GPIO_IRQSTATUS_1 0x0030 +#define OMAP4_GPIO_IRQSTATUS_SET_0 0x0034 +#define OMAP4_GPIO_IRQSTATUS_SET_1 0x0038 +#define OMAP4_GPIO_IRQSTATUS_CLR_0 0x003C +#define OMAP4_GPIO_IRQSTATUS_CLR_1 0x0040 +#define OMAP4_GPIO_IRQWAKEN_0 0x0044 +#define OMAP4_GPIO_IRQWAKEN_1 0x0048 +#define OMAP4_GPIO_SYSSTATUS 0x0114 +#define OMAP4_GPIO_IRQSTATUS1 0x0118 +#define OMAP4_GPIO_IRQENABLE1 0x011C +#define OMAP4_GPIO_WAKEUPENABLE 0x0120 +#define OMAP4_GPIO_IRQSTATUS2 0x0128 +#define OMAP4_GPIO_IRQENABLE2 0x012C +#define OMAP4_GPIO_CTRL 0x0130 +#define OMAP4_GPIO_OE 0x0134 +#define OMAP4_GPIO_DATAIN 0x0138 +#define OMAP4_GPIO_DATAOUT 0x013C +#define OMAP4_GPIO_LEVELDETECT0 0x0140 +#define OMAP4_GPIO_LEVELDETECT1 0x0144 +#define OMAP4_GPIO_RISINGDETECT 0x0148 +#define OMAP4_GPIO_FALLINGDETECT 0x014C +#define OMAP4_GPIO_DEBOUNCENABLE 0x0150 +#define OMAP4_GPIO_DEBOUNCINGTIME 0x0154 +#define OMAP4_GPIO_CLEARIRQENABLE1 0x0160 +#define OMAP4_GPIO_SETIRQENABLE1 0x0164 +#define OMAP4_GPIO_CLEARIRQENABLE2 0x0170 +#define OMAP4_GPIO_SETIRQENABLE2 0x0174 +#define OMAP4_GPIO_CLEARWKUPENA 0x0180 +#define OMAP4_GPIO_SETWKUENA 0x0184 +#define OMAP4_GPIO_CLEARDATAOUT 0x0190 +#define OMAP4_GPIO_SETDATAOUT 0x0194 + + +/** + * g_omap_gpio_mem_spec - Resource specification used when allocating resources + * g_omap_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 g_omap_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 }, + { SYS_RES_MEMORY, 4, RF_ACTIVE | RF_OPTIONAL }, + { SYS_RES_MEMORY, 5, RF_ACTIVE | RF_OPTIONAL }, + { -1, 0, 0 } +}; +static struct resource_spec g_omap_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 }, + { SYS_RES_IRQ, 4, RF_ACTIVE | RF_OPTIONAL }, + { SYS_RES_IRQ, 5, RF_ACTIVE | RF_OPTIONAL }, + { -1, 0, 0 } +}; + + +#define MAX_GPIO_BANKS 6 + +#define PINS_PER_BANK 32 + + +/** + * Structure that stores the driver context. + * + * This structure is allocated during driver attach. + */ +struct omap_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 OMAP_GPIO_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) +#define OMAP_GPIO_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) +#define OMAP_GPIO_LOCK_INIT(_sc) \ + mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->sc_dev), \ + "omap_gpio", MTX_DEF) +#define OMAP_GPIO_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); +#define OMAP_GPIO_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED); +#define OMAP_GPIO_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED); + + + +/** + * omap_gpio_readl - 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 +omap_gpio_readl(struct omap_gpio_softc *sc, unsigned int bank, bus_size_t off) +{ + return (bus_read_4(sc->sc_mem_res[bank], off)); +} + +/** + * omap_gpio_writel - 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 +omap_gpio_writel(struct omap_gpio_softc *sc, unsigned int bank, bus_size_t off, + uint32_t val) +{ + bus_write_4(sc->sc_mem_res[bank], off, val); +} + + + +/** + * The following H/W revision values were found be experimentation, TI don't + * publish the revision numbers. The TRM says "TI internal Data". + */ +#define OMAP3_GPIO_REV 0x00000025 +#define OMAP4_GPIO_REV 0x50600801 + +/** + * omap_gpio_is_omap4 - returns 1 if the GPIO module is from an OMAP4xxx chip + * omap_gpio_is_omap3 - returns 1 if the GPIO module is from an OMAP3xxx chip + * @sc: GPIO device context + * @bank: The bank to test the type of + * + * RETURNS: + * nothing + */ +static inline int +omap_gpio_is_omap4(struct omap_gpio_softc *sc, unsigned int bank) +{ + return (sc->sc_revision[bank] == OMAP4_GPIO_REV); +} +static inline int +omap_gpio_is_omap3(struct omap_gpio_softc *sc, unsigned int bank) +{ + return (sc->sc_revision[bank] == OMAP3_GPIO_REV); +} + + + + + +/** + * omap_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 +omap_gpio_pin_max(device_t dev, int *maxpin) +{ + struct omap_gpio_softc *sc = device_get_softc(dev); + unsigned int i; + unsigned int banks = 0; + + OMAP_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); + + OMAP_GPIO_UNLOCK(sc); + + return (0); +} + +/** + * omap_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 +omap_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) +{ + struct omap_gpio_softc *sc = device_get_softc(dev); + uint32_t bank = (pin / PINS_PER_BANK); + + OMAP_GPIO_LOCK(sc); + + /* Sanity check the pin number is valid */ + if ((bank > MAX_GPIO_BANKS) || (sc->sc_mem_res[bank] == NULL)) { + OMAP_GPIO_UNLOCK(sc); + return (EINVAL); + } + + *caps = (GPIO_PIN_INPUT | + GPIO_PIN_OUTPUT | + GPIO_PIN_PULLUP | + GPIO_PIN_PULLDOWN); + + OMAP_GPIO_UNLOCK(sc); + + return (0); +} + +/** + * omap_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 +omap_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) +{ + struct omap_gpio_softc *sc = device_get_softc(dev); + uint32_t bank = (pin / PINS_PER_BANK); + unsigned int state; + + OMAP_GPIO_LOCK(sc); + + /* Sanity check the pin number is valid */ + if ((bank > MAX_GPIO_BANKS) || (sc->sc_mem_res[bank] == NULL)) { + OMAP_GPIO_UNLOCK(sc); + return (EINVAL); + } + + /* Get the current pin state */ + if (omap_scm_padconf_get_gpiomode(pin, &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; + } + } + + OMAP_GPIO_UNLOCK(sc); + + return (0); +} + +/** + * omap_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 +omap_gpio_pin_getname(device_t dev, uint32_t pin, char *name) +{ + struct omap_gpio_softc *sc = device_get_softc(dev); + uint32_t bank = (pin / PINS_PER_BANK); + + OMAP_GPIO_LOCK(sc); + + /* Sanity check the pin number is valid */ + if ((bank > MAX_GPIO_BANKS) || (sc->sc_mem_res[bank] == NULL)) { + OMAP_GPIO_UNLOCK(sc); + return (EINVAL); + } + + /* Set a very simple name */ + snprintf(name, GPIOMAXNAME, "gpio_%u", pin); + name[GPIOMAXNAME - 1] = '\0'; + + OMAP_GPIO_UNLOCK(sc); + + return (0); +} + +/** + * omap_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 +omap_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) +{ + struct omap_gpio_softc *sc = device_get_softc(dev); + unsigned int state = 0; + uint32_t bank = (pin / PINS_PER_BANK); + uint32_t mask = (1UL << (pin % PINS_PER_BANK)); + uint32_t reg_off; + 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); + + + OMAP_GPIO_LOCK(sc); + + /* Sanity check the pin number is valid */ + if ((bank > MAX_GPIO_BANKS) || (sc->sc_mem_res[bank] == NULL)) { + OMAP_GPIO_UNLOCK(sc); + return (EINVAL); + } + + /* 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; + } + + /* Set the GPIO mode and state */ + if (omap_scm_padconf_set_gpiomode(pin, state) != 0) { + OMAP_GPIO_UNLOCK(sc); + return (EINVAL); + } + + + /* Get the offset of the register to use */ + if (omap_gpio_is_omap3(sc, bank)) + reg_off = OMAP3_GPIO_OE; + else if (omap_gpio_is_omap4(sc, bank)) + reg_off = OMAP4_GPIO_OE; + else { + OMAP_GPIO_UNLOCK(sc); + return (EINVAL); + } + + + /* If configuring as an output set the "output enable" bit */ + reg_val = omap_gpio_readl(sc, bank, reg_off); + if (flags & GPIO_PIN_INPUT) + reg_val |= mask; + else + reg_val &= ~mask; + omap_gpio_writel(sc, bank, reg_off, reg_val); + + + OMAP_GPIO_UNLOCK(sc); + + return (0); +} + +/** + * omap_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 +omap_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) +{ + struct omap_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_off; + + OMAP_GPIO_LOCK(sc); + + /* Sanity check the pin number is valid */ + if ((bank > MAX_GPIO_BANKS) || (sc->sc_mem_res[bank] == NULL)) { + OMAP_GPIO_UNLOCK(sc); + return (EINVAL); + } + + /* Set the pin value */ + if (omap_gpio_is_omap3(sc, bank)) + reg_off = (value == GPIO_PIN_LOW) ? OMAP3_GPIO_CLEARDATAOUT : + OMAP3_GPIO_SETDATAOUT; + else if (omap_gpio_is_omap4(sc, bank)) + reg_off = (value == GPIO_PIN_LOW) ? OMAP4_GPIO_CLEARDATAOUT : + OMAP4_GPIO_SETDATAOUT; + else { + OMAP_GPIO_UNLOCK(sc); + return (EINVAL); + } + + omap_gpio_writel(sc, bank, reg_off, mask); + + OMAP_GPIO_UNLOCK(sc); + + return (0); +} + +/** + * omap_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 +omap_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *value) +{ + struct omap_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; + + OMAP_GPIO_LOCK(sc); + + /* Sanity check the pin number is valid */ + if ((bank > MAX_GPIO_BANKS) || (sc->sc_mem_res[bank] == NULL)) { + OMAP_GPIO_UNLOCK(sc); + return (EINVAL); + } + + /* Sanity check the pin is not configured as an output */ + if (omap_gpio_is_omap3(sc, bank)) + val = omap_gpio_readl(sc, bank, OMAP3_GPIO_OE); + else if (omap_gpio_is_omap4(sc, bank)) + val = omap_gpio_readl(sc, bank, OMAP4_GPIO_OE); + if ((val & mask) == mask) { + OMAP_GPIO_UNLOCK(sc); + return (EINVAL); + } + + /* Read the value on the pin */ + if (omap_gpio_is_omap3(sc, bank)) + val = omap_gpio_readl(sc, bank, OMAP3_GPIO_DATAIN); + else if (omap_gpio_is_omap4(sc, bank)) + val = omap_gpio_readl(sc, bank, OMAP4_GPIO_DATAIN); + + *value = (val & mask) ? 1 : 0; + + OMAP_GPIO_UNLOCK(sc); + + return (0); +} + +/** + * omap_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 +omap_gpio_pin_toggle(device_t dev, uint32_t pin) +{ + struct omap_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; + + OMAP_GPIO_LOCK(sc); + + /* Sanity check the pin number is valid */ + if ((bank > MAX_GPIO_BANKS) || (sc->sc_mem_res[bank] == NULL)) { + OMAP_GPIO_UNLOCK(sc); + return (EINVAL); + } + + /* Toggle the pin */ + if (omap_gpio_is_omap3(sc, bank)) { + val = omap_gpio_readl(sc, bank, OMAP3_GPIO_DATAOUT); + if (val & mask) + omap_gpio_writel(sc, bank, OMAP3_GPIO_CLEARDATAOUT, mask); + else + omap_gpio_writel(sc, bank, OMAP3_GPIO_SETDATAOUT, mask); + } + else if (omap_gpio_is_omap4(sc, bank)) { + val = omap_gpio_readl(sc, bank, OMAP4_GPIO_DATAOUT); + if (val & mask) + omap_gpio_writel(sc, bank, OMAP4_GPIO_CLEARDATAOUT, mask); + else + omap_gpio_writel(sc, bank, OMAP4_GPIO_SETDATAOUT, mask); + } + + OMAP_GPIO_UNLOCK(sc); + + return (0); +} + + + +/** + * omap_gpio_intr - ISR for all GPIO modules + * @arg: the soft context pointer + * + * Unsused + * + * LOCKING: + * Internally locks the context + * + */ +static void +omap_gpio_intr(void *arg) +{ + struct omap_gpio_softc *sc = arg; + + OMAP_GPIO_LOCK(sc); + /* TODO: something useful */ + OMAP_GPIO_UNLOCK(sc); +} + + + + +/** + * omap_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 +omap_gpio_probe(device_t dev) +{ + device_set_desc(dev, "TI OMAP General Purpose I/O (GPIO)"); + return (0); +} + +/** + * omap_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 +omap_gpio_attach(device_t dev) +{ + struct omap_gpio_softc *sc = device_get_softc(dev); + unsigned int i; + int err = 0; + + KASSERT((device_get_unit(dev) == 0), + ("omap_gpio: Only one gpio module supported")); + + sc->sc_dev = dev; + + OMAP_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 (for example in omap44xx.c). + */ + err = bus_alloc_resources(dev, g_omap_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, g_omap_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, omap_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 */ + omap_prcm_clk_enable(GPIO1_CLK + 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 on OMAP4430 and OMAP3530 chips. + */ + sc->sc_revision[i] = omap_gpio_readl(sc, i, OMAP_GPIO_REVISION); + + /* Check the revision */ + if (!omap_gpio_is_omap4(sc, i) && !omap_gpio_is_omap3(sc, i)) { + 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 */ + if (omap_gpio_is_omap3(sc, i)) { + omap_gpio_writel(sc, i, OMAP3_GPIO_CLEARIRQENABLE1, 0xffffffff); + omap_gpio_writel(sc, i, OMAP3_GPIO_CLEARIRQENABLE2, 0xffffffff); + } + else if (omap_gpio_is_omap4(sc, i)) { + omap_gpio_writel(sc, i, OMAP4_GPIO_CLEARIRQENABLE1, 0xffffffff); + omap_gpio_writel(sc, i, OMAP4_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)); +} + +/** + * omap_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 +omap_gpio_detach(device_t dev) +{ + struct omap_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) { + if (omap_gpio_is_omap3(sc, i)) { + omap_gpio_writel(sc, i, OMAP3_GPIO_CLEARIRQENABLE1, 0xffffffff); + omap_gpio_writel(sc, i, OMAP3_GPIO_CLEARIRQENABLE2, 0xffffffff); + } else if (omap_gpio_is_omap4(sc, i)) { + omap_gpio_writel(sc, i, OMAP4_GPIO_CLEARIRQENABLE1, 0xffffffff); + omap_gpio_writel(sc, i, OMAP4_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]); + } + + OMAP_GPIO_LOCK_DESTROY(sc); + + return(0); +} + +static device_method_t omap_gpio_methods[] = { + DEVMETHOD(device_probe, omap_gpio_probe), + DEVMETHOD(device_attach, omap_gpio_attach), + DEVMETHOD(device_detach, omap_gpio_detach), + + /* GPIO protocol */ + DEVMETHOD(gpio_pin_max, omap_gpio_pin_max), + DEVMETHOD(gpio_pin_getname, omap_gpio_pin_getname), + DEVMETHOD(gpio_pin_getflags, omap_gpio_pin_getflags), + DEVMETHOD(gpio_pin_getcaps, omap_gpio_pin_getcaps), + DEVMETHOD(gpio_pin_setflags, omap_gpio_pin_setflags), + DEVMETHOD(gpio_pin_get, omap_gpio_pin_get), + DEVMETHOD(gpio_pin_set, omap_gpio_pin_set), + DEVMETHOD(gpio_pin_toggle, omap_gpio_pin_toggle), + {0, 0}, +}; + +static driver_t omap_gpio_driver = { + "gpio", + omap_gpio_methods, + sizeof(struct omap_gpio_softc), +}; +static devclass_t omap_gpio_devclass; + +DRIVER_MODULE(omap_gpio, omap, omap_gpio_driver, omap_gpio_devclass, 0, 0); +MODULE_VERSION(omap_gpio, 1); + +MODULE_DEPEND(omap_gpio, omap_scm, 1, 1, 1); +MODULE_DEPEND(omap_gpio, omap_prcm, 1, 1, 1); diff --git a/sys/arm/omap/omap_gptimer.c b/sys/arm/omap/omap_gptimer.c new file mode 100644 index 0000000..b524739 --- /dev/null +++ b/sys/arm/omap/omap_gptimer.c @@ -0,0 +1,1082 @@ +/* + * 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. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + + +#define MAX_NUM_TIMERS 12 + +#define OMAP_GPTIMER_PROFILE_UNKNOWN -1 +#define OMAP_GPTIMER_PROFILE_OMAP3 3 +#define OMAP_GPTIMER_PROFILE_OMAP4 4 + + +/* Standard registers for OMAP3 and some OMAP4 devices */ +#define OMAP_GPT_TIDR 0x0000 +#define OMAP_GPT_TIOCP_CFG 0x0010 +#define OMAP_GPT_TISTAT 0x0014 +#define OMAP_GPT_TISR 0x0018 +#define OMAP_GPT_TIER 0x001C +#define OMAP_GPT_TWER 0x0020 +#define OMAP_GPT_TCLR 0x0024 +#define OMAP_GPT_TCRR 0x0028 +#define OMAP_GPT_TLDR 0x002C +#define OMAP_GPT_TTGR 0x0030 +#define OMAP_GPT_TWPS 0x0034 +#define OMAP_GPT_TMAR 0x0038 +#define OMAP_GPT_TCAR1 0x003C +#define OMAP_GPT_TSICR 0x0040 +#define OMAP_GPT_TCAR2 0x0044 +#define OMAP_GPT_TPIR 0x0048 +#define OMAP_GPT_TNIR 0x004C +#define OMAP_GPT_TCVR 0x0050 +#define OMAP_GPT_TOCR 0x0054 +#define OMAP_GPT_TOWR 0x0058 + +/* Register set for OMAP4 timers 3,4,5,6,7,8,9 & 11 */ +#define OMAP4_GPT_IRQSTATUS_RAW 0x0024 +#define OMAP4_GPT_IRQSTATUS 0x0028 +#define OMAP4_GPT_IRQENABLE_SET 0x002C +#define OMAP4_GPT_IRQENABLE_CLR 0x0030 +#define OMAP4_GPT_IRQWAKEEN 0x0034 +#define OMAP4_GPT_TCLR 0x0038 +#define OMAP4_GPT_TCRR 0x003C +#define OMAP4_GPT_TLDR 0x0040 +#define OMAP4_GPT_TTGR 0x0044 +#define OMAP4_GPT_TWPS 0x0048 +#define OMAP4_GPT_TMAR 0x004C +#define OMAP4_GPT_TCAR1 0x0050 +#define OMAP4_GPT_TSICR 0x0054 +#define OMAP4_GPT_TCAR2 0x0058 + + + +/* GPT_TCLR Register bit fields */ +#define TCLR_GPO_CFG (0x1 << 14) +#define TCLR_CAPT_MODE (0x1 << 13) +#define TCLR_PT (0x1 << 12) +#define TCLR_TRG_MASK (0x3 << 10) +#define TCLR_TCM_MASK (0x3 << 8) +#define TCLR_SCPWM (0x1 << 7) +#define TCLR_CE (0x1 << 6) +#define TCLR_PRE (0x1 << 5) +#define TCLR_PTV_MASK (0x7 << 2) +#define TCLR_AR (0x1 << 1) +#define TCLR_ST (0x1 << 0) + +/* The interrupt/status bits used in the timer registers. */ +#define TCAR (0x1 << 2) +#define OVF (0x1 << 1) +#define MAT (0x1 << 0) + + + + +struct omap_gptimer_dev { + /* The profile of the timer */ + int profile; + + /* The source clock to use */ + unsigned int source; + + /* The register offsets of these key registers */ + uint32_t tclr; + uint32_t tcrr; + uint32_t tldr; +}; + + + +/** + * Data structure per Timer. + * + * + */ +struct omap_gptimer { + + /* Flags indicating current and configured status */ + unsigned int flags; + +#define OMAP_GPTIMER_AVAILABLE_FLAG 0x01000000 +#define OMAP_GPTIMER_ACTIVATED_FLAG 0x02000000 + + /* Lock taken when configuring the registers */ + struct mtx mtx; + + /* The memory resource for the timer register set */ + struct resource* mem_res; + + /* The IRQ resource and handle (if installed) for the timer */ + struct resource* irq_res; + void* irq_h; + + /* Callback function used when an interrupt is tripped on the given channel */ + void (*callback)(void *data); + + /* Callback data passed in the callback ... duh */ + void* callback_data; + + /* The profile of the timer, basically defines the register layout */ + unsigned int profile; + + /* The source clock to use */ + unsigned int source; + + /* Stores the address of the registers, needed because different version + * of the timer have different register offsets. + */ + uint32_t tclr; + uint32_t tcrr; + uint32_t tldr; + +}; + + +/** + * Macros for driver mutex locking + */ +#define OMAP_GPTIMER_LOCK(_tmr) mtx_lock(&(_tmr)->mtx) +#define OMAP_GPTIMER_UNLOCK(_tmr) mtx_unlock(&(_tmr)->mtx) +#define OMAP_GPTIMER_ASSERT_LOCKED(_tmr) mtx_assert(&(_tmr)->mtx, MA_OWNED); +#define OMAP_GPTIMER_ASSERT_UNLOCKED(_tmr) mtx_assert(&(_tmr)->mtx, MA_NOTOWNED); + + + +/** + * Timer driver context, allocated and stored globally, this driver is not + * intended to ever be unloaded (see g_omap_gptimer_sc). + * + */ +struct omap_gptimer_softc { + device_t sc_dev; + unsigned int sc_num_timers; + struct omap_gptimer sc_timers[MAX_NUM_TIMERS]; + struct omap_gptimer *sc_tick_timer; + +}; + +static struct omap_gptimer_softc *g_omap_gptimer_sc = NULL; + + + + + + +/** + * omap_gptimer_readl - reads a 32-bit value from one of the timer registers + * @timer: Timer device context + * @off: The offset of a register from the timer register address range + * + * + * RETURNS: + * 32-bit value read from the register. + */ +static inline uint32_t +omap_gptimer_readl(struct omap_gptimer *timer, bus_size_t off) +{ + return (bus_read_4(timer->mem_res, off)); +} + +/** + * omap_gptimer_writel - 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 inline void +omap_gptimer_writel(struct omap_gptimer *timer, bus_size_t off, uint32_t val) +{ + bus_write_4(timer->mem_res, off, val); +} + + + + +/** + * omap_gptimer_intr - interrupt handler for the timers + * @arg: pointer to the timer structure + * + * Interrupt handler for all the timer interrupts, invovkes the callback + * function supplied when the timer was activated. + * + * + * RETURNS: + * Nothing + */ +static void +omap_gptimer_intr(void *arg) +{ + struct omap_gptimer *timer = (struct omap_gptimer *) arg; + void (*callback)(void *data); + void* callback_data; + uint32_t stat = 0x0000; + + OMAP_GPTIMER_LOCK(timer); + + /* Read the interrupt status flag and clear it */ + if (timer->profile == OMAP_GPTIMER_PROFILE_OMAP3) { + + /* Read the status and it with the enable flag */ + stat = omap_gptimer_readl(timer, OMAP_GPT_TISR); + stat &= omap_gptimer_readl(timer, OMAP_GPT_TIER); + + /* Clear the status flag */ + omap_gptimer_writel(timer, OMAP_GPT_TISR, stat); + } + else if (timer->profile == OMAP_GPTIMER_PROFILE_OMAP4) { + + /* Read the status */ + stat = omap_gptimer_readl(timer, OMAP4_GPT_IRQSTATUS); + + /* Clear the status flag */ + omap_gptimer_writel(timer, OMAP4_GPT_IRQENABLE_CLR, stat); + } + + /* Store the callback details before releasing the lock */ + callback = timer->callback; + callback_data = timer->callback_data; + + OMAP_GPTIMER_UNLOCK(timer); + + /* Check if an actual overflow interrupt */ + if ((stat & OVF) && (callback != NULL)) + callback(timer->callback_data); + + +} + + + +/** + * omap_gptimer_intr_filter_ack - acknowledges a timer interrupt + * @n: the number of the timer (first timer is number 1) + * + * This function should only be called from filter interrupt handler installed + * by calling the omap_gptimer_set_intr_filter() function. + * + * RETURNS: + * Nothing + */ +void +omap_gptimer_intr_filter_ack(unsigned int n) +{ + struct omap_gptimer_softc *sc = g_omap_gptimer_sc; + struct omap_gptimer *timer; + uint32_t stat; + + /* Get a pointer to the individual timer struct */ + timer = &sc->sc_timers[n-1]; + + OMAP_GPTIMER_LOCK(timer); + + /* Read the interrupt status flag and clear it */ + if (timer->profile == OMAP_GPTIMER_PROFILE_OMAP3) { + + /* Read the status and it with the enable flag */ + stat = omap_gptimer_readl(timer, OMAP_GPT_TISR); + stat &= omap_gptimer_readl(timer, OMAP_GPT_TIER); + + /* Clear the status flag */ + omap_gptimer_writel(timer, OMAP_GPT_TISR, stat); + } + else if (timer->profile == OMAP_GPTIMER_PROFILE_OMAP4) { + + /* Read the status */ + stat = omap_gptimer_readl(timer, OMAP4_GPT_IRQSTATUS); + + /* Clear the status flag */ + omap_gptimer_writel(timer, OMAP4_GPT_IRQENABLE_CLR, stat); + } + + OMAP_GPTIMER_UNLOCK(timer); +} + + + +/** + * omap_gptimer_set_intr_filter - sets a filter + * @n: the number of the timer (first timer is number 1) + * + * + * RETURNS: + * Returns 0 on success, otherwise an error code + */ +int +omap_gptimer_set_intr_filter(unsigned int n, driver_filter_t filter) +{ + struct omap_gptimer_softc *sc = g_omap_gptimer_sc; + struct omap_gptimer *timer; + uint32_t val; + + /* Sanity checks */ + if (sc == NULL) + return (ENOMEM); + if ((n == 0) || (n > sc->sc_num_timers)) + return (EINVAL); + + /* Get a pointer to the individual timer struct */ + timer = &sc->sc_timers[n-1]; + + OMAP_GPTIMER_LOCK(timer); + + /* If a callback is already installed this won't work */ + if (timer->callback != NULL) { + OMAP_GPTIMER_UNLOCK(timer); + return(EINVAL); + } + + /* Sanity check the timer is already activated and periodic type */ + if ((timer->flags & (OMAP_GPTIMER_ACTIVATED_FLAG | OMAP_GPTIMER_PERIODIC_FLAG)) + != (OMAP_GPTIMER_ACTIVATED_FLAG | OMAP_GPTIMER_PERIODIC_FLAG)) { + OMAP_GPTIMER_UNLOCK(timer); + return(EINVAL); + } + + + /* Attempt to activate the interrupt for the tick */ + if (bus_setup_intr(sc->sc_dev, timer->irq_res, INTR_TYPE_CLK, + filter, NULL, NULL, &timer->irq_h)) { + device_printf(sc->sc_dev, "Error: failed to activate interrupt\n"); + OMAP_GPTIMER_UNLOCK(timer); + return(EINVAL); + } + + + /* Enable the overflow interrupts */ + if (timer->profile == OMAP_GPTIMER_PROFILE_OMAP3) { + val = omap_gptimer_readl(timer, OMAP_GPT_TIER); + val |= OVF; + omap_gptimer_writel(timer, OMAP_GPT_TIER, val); + } + else if (timer->profile == OMAP_GPTIMER_PROFILE_OMAP4) { + omap_gptimer_writel(timer, OMAP4_GPT_IRQENABLE_SET, OVF); + } + + OMAP_GPTIMER_UNLOCK(timer); + + return(0); +} + + + + + +/** + * omap_gptimer_activate - configures the timer + * @n: the number of the timer (first timer is number 1) + * @flags: defines the type of timer to turn on + * @time_ns: the period of the timer in nanoseconds + * @callback: if defined this function will be called when the timer overflows + * @data: data value to pass to the callback + * + * + * RETURNS: + * Returns 0 on success, otherwise an error code + */ +int +omap_gptimer_activate(unsigned int n, unsigned int flags, unsigned int time_us, + void (*callback)(void *data), void *data) +{ + struct omap_gptimer_softc *sc = g_omap_gptimer_sc; + struct omap_gptimer *timer; + uint32_t val; + uint64_t tickcount; + unsigned int freq; + uint64_t freq64; + uint32_t prescaler; + uint32_t startcount; + + + /* Sanity checks */ + if (sc == NULL) + return (ENOMEM); + if ((n == 0) || (n > sc->sc_num_timers)) + return (EINVAL); + + /* Get a pointer to the individual timer struct */ + timer = &sc->sc_timers[n-1]; + + /* Sanity check the timer is availabe and not activated */ + if (!(timer->flags & OMAP_GPTIMER_AVAILABLE_FLAG)) { + device_printf(sc->sc_dev, "Error: timer %d not available\n", n); + return (EINVAL); + } + if (timer->flags & OMAP_GPTIMER_ACTIVATED_FLAG) { + device_printf(sc->sc_dev, "Error: timer %d already activated\n", n); + return (EINVAL); + } + + /* Set up system clock information */ + if (omap_prcm_clk_valid(timer->source) != 0) { + device_printf(sc->sc_dev, "Error: failed to find source clock\n"); + return (EINVAL); + } + + + OMAP_GPTIMER_LOCK(timer); + + /* Enable the functional and interface clock */ + if (flags & OMAP_GPTIMER_32KCLK_FLAG) + omap_prcm_clk_set_source(timer->source, F32KHZ_CLK); + else + omap_prcm_clk_set_source(timer->source, SYSCLK_CLK); + + omap_prcm_clk_enable(timer->source); + + + /* Store the flags in the timer context */ + timer->flags &= 0xFF000000; + timer->flags |= (0x00FFFFFF & flags); + + + /* Reset the timer and poll on the reset complete flag */ + if (timer->profile == OMAP_GPTIMER_PROFILE_OMAP3) { + omap_gptimer_writel(timer, OMAP_GPT_TIOCP_CFG, 0x2); + /* TODO: add a timeout */ + while ((omap_gptimer_readl(timer, OMAP_GPT_TISTAT) & 0x01) == 0x00) + continue; + + /* Clear the interrupt status */ + omap_gptimer_writel(timer, OMAP_GPT_TISR, TCAR | OVF | MAT); + } + + else if (timer->profile == OMAP_GPTIMER_PROFILE_OMAP4) { + omap_gptimer_writel(timer, OMAP_GPT_TIOCP_CFG, 0x1); + /* TODO: add a timeout */ + while ((omap_gptimer_readl(timer, OMAP_GPT_TIOCP_CFG) & 0x01) == 0x01) + continue; + + /* Clear the interrupt status */ + omap_gptimer_writel(timer, OMAP4_GPT_IRQSTATUS, TCAR | OVF | MAT); + } + + + /* If the user supplied a zero value we set a free running timer */ + if (time_us == 0) { + + /* Set the initial value and roll over value to 0 */ + startcount = 0x00000000; + + } else { + + /* We need to calculate the number of timer ticks in either the reload + * value (for periodic timers) or the overflow + */ + omap_prcm_clk_get_source_freq(timer->source, &freq); + freq64 = freq; + + /* Calculate the period of the timer, 64 bit calculations used to + * prevent rollover. + */ + tickcount = (freq64 * (uint64_t)time_us) / 1000000; + + /* Must have a count of at least 1 */ + if (tickcount == 0) + tickcount = 1; + + /* If the number is too large then see if by enabling the prescaler it + * will fit, otherwise just set the max count we can do. + */ + if (tickcount > 0xFFFFFFFFULL) { + + /* Try and find a prescaler that will match */ + for (prescaler = 0; prescaler < 7; prescaler++) { + if (tickcount < (0x1ULL << (32 + prescaler))) { + break; + } + } + + /* Adjust the count and apply the prescaler */ + tickcount >>= (prescaler + 1); + + val = omap_gptimer_readl(timer, timer->tclr); + val &= ~TCLR_PTV_MASK; + val |= TCLR_PRE | (prescaler << 2); + omap_gptimer_writel(timer, timer->tclr, val); + } + + /* Calculate the start value */ + startcount = 0xFFFFFFFFUL - (uint32_t)(tickcount & 0xFFFFFFFFUL); + +printf("[BRG] %s, %d : freq64=%llu : tickcount=%llu : startcount=%u : time_us=%u\n", + __func__, __LINE__, freq64, tickcount, startcount, time_us); + } + + /* Load the start value into the count register */ + omap_gptimer_writel(timer, timer->tcrr, startcount); + + + + /* Enable autoload mode if configuring a periodic timer or system tick + * timer. Also set the reload count to match the period tick count. + */ + if (flags & OMAP_GPTIMER_PERIODIC_FLAG) { + /* Enable auto reload */ + val = omap_gptimer_readl(timer, timer->tclr); + val |= TCLR_AR; + omap_gptimer_writel(timer, timer->tclr, val); + + /* Set the reload value */ + omap_gptimer_writel(timer, timer->tldr, startcount); + } + + + /* If a callback function has been supplied setup a overflow interrupt */ + if (callback != NULL) { + + /* Save the callback function and the data for it */ + timer->callback = callback; + timer->callback_data = data; + + /* Activate the interrupt */ + if (bus_setup_intr(sc->sc_dev, timer->irq_res, + INTR_TYPE_MISC | INTR_MPSAFE, NULL, omap_gptimer_intr, + (void*)timer, &timer->irq_h)) { + device_printf(sc->sc_dev, "Error: failed to activate interrupt\n"); + } + + /* Enable the overflow interrupts. */ + if (timer->profile == OMAP_GPTIMER_PROFILE_OMAP3) { + val = omap_gptimer_readl(timer, OMAP_GPT_TIER); + val |= OVF; + omap_gptimer_writel(timer, OMAP_GPT_TIER, val); + } + else if (timer->profile == OMAP_GPTIMER_PROFILE_OMAP4) { + omap_gptimer_writel(timer, OMAP4_GPT_IRQENABLE_SET, OVF); + } + } + + + /* Finally set the activated flag */ + timer->flags |= OMAP_GPTIMER_ACTIVATED_FLAG; + + OMAP_GPTIMER_UNLOCK(timer); + +printf("[BRG] %s, %d\n", __func__, __LINE__); + + return (0); +} + + + +/** + * omap_gptimer_deactivate - deactivates an individual timer + * @n: the number of the timer (first timer is number 1) + * + * + * RETURNS: + * Returns 0 on success, otherwise an error code + */ +int +omap_gptimer_deactivate(unsigned int n) +{ + struct omap_gptimer_softc *sc = g_omap_gptimer_sc; + struct omap_gptimer *timer; + uint32_t val; + + /* Sanity checks */ + if (sc == NULL) + return (ENOMEM); + if ((n == 0) || (n > MAX_NUM_TIMERS)) + return (EINVAL); + + /* Get a pointer to the individual timer struct */ + timer = &sc->sc_timers[n-1]; + if (!(timer->flags & OMAP_GPTIMER_ACTIVATED_FLAG)) + return (EINVAL); + + + /* Stop the timer if running */ + val = omap_gptimer_readl(timer, timer->tclr); + val &= ~TCLR_ST; + omap_gptimer_writel(timer, timer->tclr, val); + + + /* Uninstall the interrupt handler (if installed) */ + if (timer->irq_h) { + bus_teardown_intr(sc->sc_dev, timer->irq_res, timer->irq_h); + timer->irq_h = NULL; + } + + /* Disable the functional and interface clock */ + omap_prcm_clk_disable(timer->source); + + + /* Finally clear the active flag */ + timer->flags &= ~OMAP_GPTIMER_ACTIVATED_FLAG; + + return (0); +} + + +/** + * omap_gptimer_start - starts a one-shot or periodic timer + * @n: the number of the timer (first timer is number 1) + * + * + * RETURNS: + * Returns 0 on success, otherwise an error code + */ +int +omap_gptimer_start(unsigned int n) +{ + struct omap_gptimer_softc *sc = g_omap_gptimer_sc; + struct omap_gptimer *timer; + uint32_t val; + + /* Sanity checks */ + if (sc == NULL) + return (ENOMEM); + if ((n == 0) || (n > MAX_NUM_TIMERS)) + return (EINVAL); + + /* Get a pointer to the individual timer struct */ + timer = &sc->sc_timers[n-1]; + if (!(timer->flags & OMAP_GPTIMER_ACTIVATED_FLAG)) + return (EINVAL); + + OMAP_GPTIMER_LOCK(timer); + + val = omap_gptimer_readl(timer, timer->tclr); + val |= TCLR_ST; + omap_gptimer_writel(timer, timer->tclr, val); + + OMAP_GPTIMER_UNLOCK(timer); + + return (0); +} + + +/** + * omap_gptimer_stop - stops a one-shot or periodic timer + * @n: the number of the timer (first timer is number 1) + * + * + * RETURNS: + * Returns 0 on success, otherwise an error code + */ +int +omap_gptimer_stop(unsigned int n) +{ + struct omap_gptimer_softc *sc = g_omap_gptimer_sc; + struct omap_gptimer *timer; + uint32_t val; + + /* Sanity checks */ + if (sc == NULL) + return (ENOMEM); + if ((n == 0) || (n > MAX_NUM_TIMERS)) + return (EINVAL); + + /* Get a pointer to the individual timer struct */ + timer = &sc->sc_timers[n-1]; + if (!(timer->flags & OMAP_GPTIMER_ACTIVATED_FLAG)) + return (EINVAL); + + OMAP_GPTIMER_LOCK(timer); + + val = omap_gptimer_readl(timer, timer->tclr); + val &= ~TCLR_ST; + omap_gptimer_writel(timer, timer->tclr, val); + + OMAP_GPTIMER_UNLOCK(timer); + + return (0); +} + + + +/** + * omap_gptimer_read_count - reads the current timer value + * @n: the number of the timer (first timer is number 1) + * @cnt: + * + * + * RETURNS: + * Returns 0 on success, otherwise an error code + */ +int +omap_gptimer_read_count(unsigned int n, uint32_t *cnt) +{ + struct omap_gptimer_softc *sc = g_omap_gptimer_sc; + struct omap_gptimer *timer; + int ret; + + /* Sanity checks */ + if (sc == NULL) + return (ENOMEM); + if ((n == 0) || (n > sc->sc_num_timers)) + return (EINVAL); + + /* Get a pointer to the individual timer struct */ + timer = &sc->sc_timers[n-1]; + + OMAP_GPTIMER_LOCK(timer); + + if (!(timer->flags & OMAP_GPTIMER_ACTIVATED_FLAG)) { + ret = EINVAL; + } else { + *cnt = omap_gptimer_readl(timer, timer->tcrr); + ret = 0; + } + + OMAP_GPTIMER_UNLOCK(timer); + + return (ret); +} + + +/** + * omap_gptimer_write_count - writes a value into the current count + * @n: the number of the timer (first timer is number 1) + * @cnt: the value to put in the count register + * + * + * + * RETURNS: + * Returns 0 on success, otherwise an error code + */ +int +omap_gptimer_write_count(unsigned int n, uint32_t cnt) +{ + struct omap_gptimer_softc *sc = g_omap_gptimer_sc; + struct omap_gptimer *timer; + + /* Sanity checks */ + if (sc == NULL) + return (ENOMEM); + if ((n == 0) || (n > MAX_NUM_TIMERS)) + return (EINVAL); + + /* Get a pointer to the individual timer struct */ + timer = &sc->sc_timers[n-1]; + if (!(timer->flags & OMAP_GPTIMER_ACTIVATED_FLAG)) + return (EINVAL); + + OMAP_GPTIMER_LOCK(timer); + + omap_gptimer_writel(timer, timer->tcrr, cnt); + + OMAP_GPTIMER_UNLOCK(timer); + + return (0); +} + + + +/** + * omap_gptimer_set_reload - sets the reload value for the driver if periodic + * timer + * @n: the number of the timer (first timer is number 1) + * @reload: the value to put in the reload register + * + * + * + * RETURNS: + * Returns 0 on success, otherwise an error code + */ +int +omap_gptimer_set_reload(unsigned int n, uint32_t reload) +{ + struct omap_gptimer_softc *sc = g_omap_gptimer_sc; + struct omap_gptimer *timer; + + /* Sanity checks */ + if (sc == NULL) + return (ENOMEM); + if ((n == 0) || (n > MAX_NUM_TIMERS)) + return (EINVAL); + + /* Get a pointer to the individual timer struct */ + timer = &sc->sc_timers[n-1]; + if (!(timer->flags & OMAP_GPTIMER_ACTIVATED_FLAG)) + return (EINVAL); + + OMAP_GPTIMER_LOCK(timer); + + omap_gptimer_writel(timer, timer->tldr, reload); + + OMAP_GPTIMER_UNLOCK(timer); + + return (0); +} + + + + +/** + * omap_gptimer_get_freq - gets the frequency of an activated timer + * @n: the number of the timer (first timer is number 1) + * @freq: unpon return will contain the current freq + * + * The timer must be activated, if not this function will return EINVAL. + * + * RETURNS: + * Returns 0 on success, otherwise an error code + */ +int +omap_gptimer_get_freq(unsigned int n, uint32_t *freq) +{ + struct omap_gptimer_softc *sc = g_omap_gptimer_sc; + struct omap_gptimer *timer; + unsigned int src_freq; + unsigned int tmr_freq; + unsigned int prescaler; + uint32_t tclr, tldr; + int rc; + + /* Sanity checks */ + if (sc == NULL) + return (ENOMEM); + if ((n == 0) || (n > MAX_NUM_TIMERS)) + return (EINVAL); + if (freq == NULL) + return (EINVAL); + + /* Get a pointer to the individual timer struct */ + timer = &sc->sc_timers[n-1]; + if (!(timer->flags & OMAP_GPTIMER_ACTIVATED_FLAG)) + return (EINVAL); + + /* We get the frequency by first reading the source frequency */ + if ((rc = omap_prcm_clk_get_source_freq(timer->source, &src_freq)) != 0) + return (rc); + + + OMAP_GPTIMER_LOCK(timer); + + /* Determine if the pre-scalar is enabled and if so the prescaler value */ + tclr = omap_gptimer_readl(timer, timer->tclr); + if (tclr & TCLR_PRE) + prescaler = 1UL << (((tclr & TCLR_PTV_MASK) >> 2) + 1); + else + prescaler = 1; + + /* Get the reload count */ + tldr = omap_gptimer_readl(timer, timer->tldr); + + OMAP_GPTIMER_UNLOCK(timer); + + + /* Calculate the tick freq */ + tmr_freq = (src_freq / prescaler); + + /* If auto-reload mode is set and the reload count is not zero then the + * frequency is the period between overflows. + */ + if ((tclr & TCLR_AR) && (tldr != 0x00000000)) { + tmr_freq /= ((0xffffffff - tldr) + 1); + } + + + if (freq != NULL) + *freq = tmr_freq; + + return (0); +} + + + +/** + * omap_gptimer_probe - driver probe function + * @dev: timer device handle + * + * + * + * RETURNS: + * Always returns 0. + */ +static int +omap_gptimer_probe(device_t dev) +{ + + device_set_desc(dev, "TI OMAP General Purpose Timers"); + return (0); +} + + +/** + * omap_gptimer_attach - driver attach function + * @dev: timer 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 +omap_gptimer_attach(device_t dev) +{ + struct omap_gptimer_softc *sc = device_get_softc(dev); + struct omap_gptimer *timer; + char name[32]; + unsigned int n = 0; + int rid; + uint32_t rev; + + + /* Setup the basics */ + sc->sc_dev = dev; + sc->sc_tick_timer = NULL; + + /* Go through and configure each individual timer */ + for (n = 0; n < MAX_NUM_TIMERS; n++) { + + timer = &sc->sc_timers[n]; + + /* First try and get the register addresses, if this fails we assume + * there are no more timers. + */ + rid = n; + timer->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (timer->mem_res == NULL) + break; + + /* Next try and get the interrupt resource, this is not fatal */ + rid = n; + timer->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_ACTIVE | RF_SHAREABLE); + + + /* Now we can setup the rest of the fields in the timer struct */ + + /* Mutex to protect the shared data structures */ + snprintf(name, 32, "omap_gptimer%u", (n + 1)); + mtx_init(&timer->mtx, device_get_nameunit(dev), name, MTX_SPIN); + + + /* Read the revision of the timer, the revision numbers aren't + * published by TI but I've retrieved the values from experimentation. + */ + rev = omap_gptimer_readl(timer, OMAP_GPT_TIDR); + switch (rev) { + case 0x00000013: /* OMAP3 without 1ms generation */ + case 0x00000015: + case 0x00000021: /* OMAP3 with 1ms generation */ + timer->profile = OMAP_GPTIMER_PROFILE_OMAP3; + break; + case 0x4fff1301: + case 0x4fff0301: + timer->profile = OMAP_GPTIMER_PROFILE_OMAP4; + break; + default: + timer->profile = OMAP_GPTIMER_PROFILE_UNKNOWN; + break; + } + + if (timer->profile == OMAP_GPTIMER_PROFILE_UNKNOWN) { + device_printf(dev, "Error: failed to determine the type of " + "GPTIMER%d, ignoring timer (rev: 0x%08x)\n", (n + 1), + rev); + break; + } + + /* Set the clock source for the timer, this is just a one to one + * mapping of the clock id to timer, i.e. n=0 => GPTIMER1_CLK. + */ + timer->source = (GPTIMER1_CLK + n); + + + /* Setup the profile for the GPTIMER's, this is chip specfic and + * determined by the array at the top of this file. + */ + if (timer->profile == OMAP_GPTIMER_PROFILE_OMAP3) { + timer->tldr = OMAP_GPT_TLDR; + timer->tcrr = OMAP_GPT_TCRR; + timer->tclr = OMAP_GPT_TCLR; + } + else if (timer->profile == OMAP_GPTIMER_PROFILE_OMAP4) { + timer->tldr = OMAP4_GPT_TLDR; + timer->tcrr = OMAP4_GPT_TCRR; + timer->tclr = OMAP4_GPT_TCLR; + } + + /* Finally mark the timer as available */ + timer->flags = OMAP_GPTIMER_AVAILABLE_FLAG; + } + + /* Store the number of timers installed */ + sc->sc_num_timers = n; + + /* Store the timer structure globally - this driver should never be unloaded */ + g_omap_gptimer_sc = sc; + + return (0); +} + + +static device_method_t g_omap_gptimer_methods[] = { + DEVMETHOD(device_probe, omap_gptimer_probe), + DEVMETHOD(device_attach, omap_gptimer_attach), + {0, 0}, +}; + +static driver_t g_omap_gptimer_driver = { + "omap_gptimer", + g_omap_gptimer_methods, + sizeof(struct omap_gptimer_softc), +}; +static devclass_t g_omap_gptimer_devclass; + +DRIVER_MODULE(omap_gptimer, omap, g_omap_gptimer_driver, g_omap_gptimer_devclass, 0, 0); +MODULE_DEPEND(omap_gptimer, omap_prcm, 1, 1, 1); + diff --git a/sys/arm/omap/omap_gptimer.h b/sys/arm/omap/omap_gptimer.h new file mode 100644 index 0000000..905ee69 --- /dev/null +++ b/sys/arm/omap/omap_gptimer.h @@ -0,0 +1,110 @@ +/* + * 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. + */ + + +/** + * Generic OMAP timer driver + * + * See the omap_timer.c file for implementation details. + * + * Reference: + * OMAP35xx Applications Processor Technical Reference Manual + * OMAP4430 Technical Reference Manual + */ +#ifndef _OMAP_TIMER_H_ +#define _OMAP_TIMER_H_ + + + + + + + +/* Indicates if the timer should be periodic, if not specified the timer is + * one-shot and you have to call omap_timer_start() to restart the timer. + */ +#define OMAP_GPTIMER_PERIODIC_FLAG 0x00000001 + +/* The following flags define the clocking source, if specified the timer will + * be clocked from the 32Khz source, otherwise it clocked from the sysclk. + */ +#define OMAP_GPTIMER_32KCLK_FLAG 0x00000100 + + +/* Flags in bit range 31:24 are reserved for internal use. */ + + + + + + +int +omap_gptimer_activate(unsigned int n, unsigned int flags, unsigned int time_us, + void (*callback)(void *data), void *data); + +int +omap_gptimer_deactivate(unsigned int n); + +int +omap_gptimer_start(unsigned int n); + +int +omap_gptimer_stop(unsigned int n); + + +int +omap_gptimer_read_count(unsigned int n, uint32_t *cnt); + +int +omap_gptimer_write_count(unsigned int n, uint32_t cnt); + +int +omap_gptimer_set_reload(unsigned int n, uint32_t reload); + + +int +omap_gptimer_get_freq(unsigned int n, uint32_t *freq); + + + +/* The following functions should only be used to install a general purpose + * timer as the system tick timer. + */ +int +omap_gptimer_set_intr_filter(unsigned int n, driver_filter_t filter); + +void +omap_gptimer_intr_filter_ack(unsigned int n); + + + +#endif /* _OMAP_TIMER_H_ */ diff --git a/sys/arm/omap/omap_i2c.c b/sys/arm/omap/omap_i2c.c new file mode 100644 index 0000000..31be4da --- /dev/null +++ b/sys/arm/omap/omap_i2c.c @@ -0,0 +1,1177 @@ +/*- + * 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 author may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 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. + */ + +/** + * Driver for the I2C module on the TI OMAP 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 "iicbus_if.h" + + +#define I2C_REV 0x00 +#define I2C_IE 0x04 +#define I2C_STAT 0x08 +#define I2C_SYSS 0x10 +#define I2C_BUF 0x14 +#define I2C_CNT 0x18 +#define I2C_DATA 0x1C +#define I2C_CON 0x24 +#define I2C_OA0 0x28 +#define I2C_SA 0x2C +#define I2C_PSC 0x30 +#define I2C_SCLL 0x34 +#define I2C_SCLH 0x38 +#define I2C_SYSTEST 0x3C +#define I2C_BUFSTAT 0x40 +#define I2C_OA1 0x44 +#define I2C_OA2 0x48 +#define I2C_OA3 0x4C +#define I2C_ACTOA 0x50 +#define I2C_SBLOCK 0x54 + + + + +/** + * I2C device driver context, a pointer to this is stored in the device + * driver structure. + */ +struct omap_i2c_softc +{ + device_t sc_dev; + 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; + + uint32_t sc_reg_off; + + /* The following are register offsets, they are different on OMAP3 & OMAP4 + * devices. + */ +#if 0 + uint32_t sc_ie_reg; + uint32_t sc_stat_reg; + uint32_t sc_buf_reg; + uint32_t sc_cnt_reg; + uint32_t sc_data_reg; + uint32_t sc_con_reg; + uint32_t sc_bufstat_reg; + uint32_t sc_syss_reg; + uint32_t sc_psc_reg; + uint32_t sc_sysc_reg; + uint32_t sc_scll_reg; + uint32_t sc_sclh_reg; + uint32_t sc_sa_reg; +#endif +}; + + +#define OMAP_I2C_REV1 0x003C /* OMAP3 */ +#define OMAP_I2C_REV2 0x0000 /* OMAP4 */ + + + +/** + * omap_i2c_readw - 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 +omap_i2c_readw(struct omap_i2c_softc *sc, bus_size_t off) +{ + return bus_read_2(sc->sc_mem_res, sc->sc_reg_off + off); +} + +/** + * omap_i2c_writew - 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 +omap_i2c_writew(struct omap_i2c_softc *sc, bus_size_t off, uint16_t val) +{ + bus_write_2(sc->sc_mem_res, sc->sc_reg_off + off, val); +} + + +/** + * Locking macros used throughout the driver + */ +#define OMAP_I2C_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) +#define OMAP_I2C_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) +#define OMAP_I2C_LOCK_INIT(_sc) \ + mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->sc_dev), \ + "omap_i2c", MTX_DEF) +#define OMAP_I2C_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); +#define OMAP_I2C_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED); +#define OMAP_I2C_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED); + + +static devclass_t omap_i2c_devclass; + +/* bus entry points */ + +static int omap_i2c_probe(device_t dev); +static int omap_i2c_attach(device_t dev); +static int omap_i2c_detach(device_t dev); +static void omap_i2c_intr(void *); + +/* helper routines */ +static int omap_i2c_activate(device_t dev); +static void omap_i2c_deactivate(device_t dev); + + + + + + + + +/** + * omap_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 +omap_i2c_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr) +{ + struct omap_i2c_softc *sc = device_get_softc(dev); + uint16_t psc_reg, scll_reg, sclh_reg, con_reg; + + + OMAP_I2C_LOCK(sc); + + if (oldaddr) + *oldaddr = sc->sc_i2c_addr; + sc->sc_i2c_addr = addr; + + + /* The header file doesn't actual tell you what speeds should be used for + * the 3 possible settings, so I'm going to go with the usual: + * + * IIC_SLOW => 100kbps + * IIC_FAST => 400kbps + * IIC_FASTEST => 3.4Mbps + * + * I2Ci_INTERNAL_CLK = I2Ci_FCLK / (PSC + 1) + * I2Ci_INTERNAL_CLK = 96MHZ / (PSC + 1) + */ + switch (speed) { + case IIC_FASTEST: + psc_reg = 0x0004; + scll_reg = 0x0811; + sclh_reg = 0x0a13; + break; + + case IIC_FAST: + psc_reg = 0x0009; + scll_reg = 0x0005; + sclh_reg = 0x0007; + break; + + case IIC_SLOW: + case IIC_UNKNOWN: + default: + psc_reg = 0x0017; + scll_reg = 0x000D; + sclh_reg = 0x000F; + break; + } + + /* First disable the controller while changing the clocks */ + con_reg = omap_i2c_readw(sc, I2C_CON); + omap_i2c_writew(sc, I2C_CON, 0x0000); + + /* Program the prescaler */ + omap_i2c_writew(sc, I2C_PSC, psc_reg); + + /* Set the bitrate */ + omap_i2c_writew(sc, I2C_SCLL, scll_reg); + omap_i2c_writew(sc, I2C_SCLH, sclh_reg); + + /* Set the remote slave address */ + omap_i2c_writew(sc, I2C_SA, addr); + + + /* Enable the I2C module again */ + con_reg = I2C_CON_I2C_EN; + con_reg |= (speed == IIC_FASTEST) ? I2C_CON_OPMODE_HS : I2C_CON_OPMODE_STD; + omap_i2c_writew(sc, I2C_CON, con_reg); + + + OMAP_I2C_UNLOCK(sc); + + return 0; +} + + + +/** + * omap_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 +omap_i2c_intr(void *arg) +{ + struct omap_i2c_softc *sc = (struct omap_i2c_softc*) arg; + uint16_t status; + + status = omap_i2c_readw(sc, I2C_STAT); + if (status == 0) + return; + + OMAP_I2C_LOCK(sc); + + /* save the flags */ + sc->sc_stat_flags |= status; + + /* clear the status flags */ + omap_i2c_writew(sc, I2C_STAT, status); + + /* wakeup the process the started the transaction */ + wakeup(sc); + + OMAP_I2C_UNLOCK(sc); + + return; +} + + +/** + * omap_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 +omap_i2c_wait(struct omap_i2c_softc *sc, uint16_t flags, uint16_t *statp, int timo) +{ + int waittime = timo; + int start_ticks = ticks; + int rc; + + + OMAP_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); +} + + +/** + * omap_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 +omap_i2c_wait_for_free_bus(struct omap_i2c_softc *sc, int timo) +{ + /* check if the bus is free, BB bit = 0 */ + if ((omap_i2c_readw(sc, I2C_STAT) & I2C_STAT_BB) == 0) + return 0; + + /* enable bus free interrupts */ + omap_i2c_writew(sc, I2C_IE, I2C_IE_BF); + + /* wait for the bus free interrupt to be tripped */ + return omap_i2c_wait(sc, I2C_STAT_BF, NULL, timo); +} + + +/** + * omap_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 +omap_i2c_read_bytes(struct omap_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; + uint32_t sofar = 0; + uint32_t i; + + /* wait for the bus to become free */ + err = omap_i2c_wait_for_free_bus(sc, timo); + if (err != 0) + 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*/ + omap_i2c_writew(sc, I2C_IE, events); + + /* write the number of bytes to read */ + omap_i2c_writew(sc, I2C_CNT, len); + + /* clear the write bit and initiate the read transaction. Setting the STT + * (start) bit initiates the transfer. + */ + con_reg = omap_i2c_readw(sc, I2C_CON); + con_reg &= ~I2C_CON_TRX; + con_reg |= I2C_CON_MST | I2C_CON_STT | I2C_CON_STP; + omap_i2c_writew(sc, I2C_CON, con_reg); + + + /* reading loop */ + while (1) { + + /* wait for an event */ + err = omap_i2c_wait(sc, events, &status, timo); + if (err != 0) + break; + + /* check for the error conditions */ + if (status & I2C_STAT_NACK) { + /* no ACK from slave */ + printf("[BRG] %s : %d : NACK\n", __func__, __LINE__); + err = -ENXIO; + break; + } + if (status & I2C_STAT_AL) { + /* arbitration lost */ + printf("[BRG] %s : %d : Arbitration lost\n", __func__, __LINE__); + err = -ENXIO; + break; + } + + /* check if we have finished */ + if (status & I2C_STAT_ARDY) { + /* register access ready - transaction complete basically */ + printf("[BRG] %s : %d : ARDY transaction complete\n", __func__, __LINE__); + err = 0; + break; + } + + + + /* read some data */ + if (status & I2C_STAT_RDR) { + /* Receive draining interrupt - last data received */ + printf("[BRG] %s : %d : Receive draining interrupt\n", __func__, __LINE__); + + /* get the number of bytes in the FIFO */ + amount = omap_i2c_readw(sc, I2C_BUFSTAT); + amount >>= 8; + amount &= 0x3f; + } + else if (status & I2C_STAT_RRDY) { + /* Receive data ready interrupt - enough data received */ + printf("[BRG] %s : %d : Receive data ready interrupt\n", __func__, __LINE__); + + /* get the number of bytes in the FIFO */ + amount = omap_i2c_readw(sc, I2C_BUF); + amount >>= 8; + amount &= 0x3f; + amount += 1; + } + + /* sanity check we haven't overwritten the array */ + if ((sofar + amount) > len) { + printf("[BRG] %s : %d : to many bytes to read\n", __func__, __LINE__); + amount = (len - sofar); + } + + /* read the bytes from the fifo */ + for (i = 0; i < amount; i++) { + buf[sofar++] = (uint8_t)(omap_i2c_readw(sc, I2C_DATA) & 0xff); + } + + /* attempt to clear the receive ready bits */ + omap_i2c_writew(sc, I2C_STAT, I2C_STAT_RDR | I2C_STAT_RRDY); + } + + /* reset the registers regardless if there was an error or not */ + omap_i2c_writew(sc, I2C_IE, 0x0000); + omap_i2c_writew(sc, I2C_CON, I2C_CON_I2C_EN | I2C_CON_MST | I2C_CON_STP); + + return(err); +} + + +/** + * omap_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 +omap_i2c_write_bytes(struct omap_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; + uint32_t sofar = 0; + uint32_t i; + + + /* wait for the bus to become free */ + err = omap_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*/ + omap_i2c_writew(sc, I2C_IE, events); + + /* write the number of bytes to write */ + omap_i2c_writew(sc, I2C_CNT, len); + + /* set the write bit and initiate the write transaction. Setting the STT + * (start) bit initiates the transfer. + */ + con_reg = omap_i2c_readw(sc, I2C_CON); + con_reg |= I2C_CON_TRX | I2C_CON_MST | I2C_CON_STT | I2C_CON_STP; + omap_i2c_writew(sc, I2C_CON, con_reg); + + + /* writing loop */ + while (1) { + + /* wait for an event */ + err = omap_i2c_wait(sc, events, &status, timo); + if (err != 0) + break; + + + /* check for the error conditions */ + if (status & I2C_STAT_NACK) { + /* no ACK from slave */ + printf("[BRG] %s : %d : NACK\n", __func__, __LINE__); + err = -ENXIO; + break; + } + if (status & I2C_STAT_AL) { + /* arbitration lost */ + printf("[BRG] %s : %d : Arbitration lost\n", __func__, __LINE__); + err = -ENXIO; + break; + } + + /* check if we have finished */ + if (status & I2C_STAT_ARDY) { + /* register access ready - transaction complete basically */ + printf("[BRG] %s : %d : ARDY transaction complete\n", __func__, __LINE__); + err = 0; + break; + } + + + + /* read some data */ + if (status & I2C_STAT_XDR) { + /* Receive draining interrupt - last data received */ + printf("[BRG] %s : %d : Transmit draining interrupt\n", __func__, __LINE__); + + /* get the number of bytes in the FIFO */ + amount = omap_i2c_readw(sc, I2C_BUFSTAT); + amount &= 0x3f; + } + else if (status & I2C_STAT_XRDY) { + /* Receive data ready interrupt - enough data received */ + printf("[BRG] %s : %d : Transmit data ready interrupt\n", __func__, __LINE__); + + /* get the number of bytes in the FIFO */ + amount = omap_i2c_readw(sc, I2C_BUF); + amount &= 0x3f; + amount += 1; + } + + /* sanity check we haven't overwritten the array */ + if ((sofar + amount) > len) { + printf("[BRG] %s : %d : to many bytes to write\n", __func__, __LINE__); + amount = (len - sofar); + } + + /* write the bytes from the fifo */ + for (i = 0; i < amount; i++) { + omap_i2c_writew(sc, I2C_DATA, buf[sofar++]); + } + + /* attempt to clear the transmit ready bits */ + omap_i2c_writew(sc, I2C_STAT, I2C_STAT_XDR | I2C_STAT_XRDY); + } + + /* reset the registers regardless if there was an error or not */ + omap_i2c_writew(sc, I2C_IE, 0x0000); + omap_i2c_writew(sc, I2C_CON, I2C_CON_I2C_EN | I2C_CON_MST | I2C_CON_STP); + + return(err); +} + + +/** + * omap_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 +omap_i2c_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs) +{ + struct omap_i2c_softc *sc = device_get_softc(dev); + int err = 0; + uint32_t i; + uint16_t len; + uint8_t *buf; + + OMAP_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 */ + omap_i2c_writew(sc, I2C_SA, msgs[i].slave); + + + /* perform the read or write */ + if (msgs[i].flags & IIC_M_RD) { + err = omap_i2c_read_bytes(sc, buf, len); + } else { + err = omap_i2c_write_bytes(sc, buf, len); + } + + } + +out:; + OMAP_I2C_UNLOCK(sc); + + return(err); +} + + +/** + * omap_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 +omap_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); +} + + +/** + * omap_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 +omap_i2c_activate(device_t dev) +{ + struct omap_i2c_softc *sc = (struct omap_i2c_softc*) device_get_softc(dev); + unsigned int timeout = 0; + int err = ENOMEM; + uint16_t con_reg; + 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 = I2C1_CLK + device_get_unit(dev); + omap_prcm_clk_enable(clk); + + + /* 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 */ + omap_i2c_writew(sc, I2C_CON, 0x0000); + + /* Issue a softreset to the controller */ +// omap_i2c_writew(sc, sc->sc_sysc_reg, 0x0002); + + /* Re-enable the module and then check for the reset done */ + omap_i2c_writew(sc, I2C_CON, I2C_CON_I2C_EN); + + while ((omap_i2c_readw(sc, I2C_SYSS) & 0x01) == 0x00) { + if (timeout++ > 100) { + err = -EBUSY; + goto errout; + } + DELAY(100); + } + + /* Disable the I2C controller once again, now that the reset has finished */ + omap_i2c_writew(sc, I2C_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. + */ + omap_i2c_writew(sc, I2C_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 */ + omap_i2c_writew(sc, I2C_SCLL, 0x000d); + omap_i2c_writew(sc, I2C_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. + */ + omap_i2c_writew(sc, I2C_BUF, 0x0404); + + + /* + * 8. Take the I2C controller out of reset by setting the I2Ci.I2C_CON[15] + * I2C_EN bit to 1. + */ + omap_i2c_writew(sc, I2C_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 = omap_i2c_readw(sc, I2C_CON); + con_reg |= I2C_CON_MST; + omap_i2c_writew(sc, I2C_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). + */ + omap_i2c_writew(sc, I2C_IE, 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); + +errout: + return(err); +} + + +/** + * omap_i2c_deactivate - deactivates the controller and releases resources + * @dev: i2c device handle + * + * + * + * LOCKING: + * Assumed called in an atomic context. + * + * RETURNS: + * nothing + */ +static void +omap_i2c_deactivate(device_t dev) +{ + struct omap_i2c_softc *sc = device_get_softc(dev); + clk_ident_t clk; + + /* Disable the controller - cancel all transactions */ + omap_i2c_writew(sc, I2C_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 = I2C1_CLK + device_get_unit(dev); + omap_prcm_clk_disable(clk); + + return; +} + + +/** + * omap_i2c_probe - probe function for the driver + * @dev: i2c device handle + * + * + * + * LOCKING: + * + * + * RETURNS: + * Always returns 0 + */ +static int +omap_i2c_probe(device_t dev) +{ + device_set_desc(dev, "TI OMAP I2C Controller"); + return (0); +} + + +/** + * omap_i2c_attach - attach function for the driver + * @dev: i2c device handle + * + * Initialised driver data structures and activates the I2C controller. + * + * LOCKING: + * + * + * RETURNS: + * + */ +static int +omap_i2c_attach(device_t dev) +{ + struct omap_i2c_softc *sc = device_get_softc(dev); + int err; + int rid; + + sc->sc_dev = dev; + + OMAP_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) { + OMAP_I2C_LOCK_DESTROY(sc); + goto out; + } + + + /* First read the version number of the I2C module */ + sc->sc_rev = omap_i2c_readw(sc, I2C_REV); + if (sc->sc_rev == OMAP_I2C_REV1) + sc->sc_reg_off = 0x00; + else if (sc->sc_rev == OMAP_I2C_REV2) + sc->sc_reg_off = 0x80; + else { + device_printf(dev, "Error: Unknown I2C revision 0x%04x\n", sc->sc_rev); + OMAP_I2C_LOCK_DESTROY(sc); + goto out; + } + + + /* Activate the H/W */ + err = omap_i2c_activate(dev); + if (err) { + OMAP_I2C_LOCK_DESTROY(sc); + goto out; + } + + /* activate the interrupt */ + err = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE, + NULL, omap_i2c_intr, sc, &sc->sc_irq_h); + if (err) { + OMAP_I2C_LOCK_DESTROY(sc); + 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) + omap_i2c_deactivate(dev); + return (err); +} + + + +/** + * omap_i2c_detach - detach function for the driver + * @dev: i2c device handle + * + * + * + * LOCKING: + * + * + * RETURNS: + * Always returns 0 + */ +static int +omap_i2c_detach(device_t dev) +{ + struct omap_i2c_softc *sc = device_get_softc(dev); + int rv; + + omap_i2c_deactivate(dev); + + if (sc->sc_iicbus && (rv = device_delete_child(dev, sc->sc_iicbus)) != 0) + return (rv); + + OMAP_I2C_LOCK_DESTROY(sc); + + return (0); +} + + + + +static device_method_t omap_i2c_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, omap_i2c_probe), + DEVMETHOD(device_attach, omap_i2c_attach), + DEVMETHOD(device_detach, omap_i2c_detach), + + /* iicbus interface */ + DEVMETHOD(iicbus_callback, omap_i2c_callback), + DEVMETHOD(iicbus_reset, omap_i2c_reset), + DEVMETHOD(iicbus_transfer, omap_i2c_transfer), + { 0, 0 } +}; + +static driver_t omap_i2c_driver = { + "omap_i2c", + omap_i2c_methods, + sizeof(struct omap_i2c_softc), +}; + +DRIVER_MODULE(omap_i2c, omap, omap_i2c_driver, omap_i2c_devclass, 0, 0); +DRIVER_MODULE(iicbus, omap_i2c, iicbus_driver, iicbus_devclass, 0, 0); + +MODULE_DEPEND(omap_i2c, omap_prcm, 1, 1, 1); +MODULE_DEPEND(omap_i2c, iicbus, 1, 1, 1); diff --git a/sys/arm/omap/omap_i2c.h b/sys/arm/omap/omap_i2c.h new file mode 100644 index 0000000..bb74660 --- /dev/null +++ b/sys/arm/omap/omap_i2c.h @@ -0,0 +1,120 @@ +/*- + * 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 author may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 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. + */ + +#ifndef _OMAP_I2C_H_ +#define _OMAP_I2C_H_ + +/** + * Header file for the OMAP I2C driver. + * + * Simply contains register bit flags. + */ + +/* The following are the register offsets for OMAP3 devices, for OMAP4 the + * registers are offset by another 0x80 bytes. + */ +#define I2C_IE 0x04 +#define I2C_STAT 0x08 +#define I2C_SYSS 0x10 +#define I2C_BUF 0x14 +#define I2C_CNT 0x18 +#define I2C_DATA 0x1C +#define I2C_CON 0x24 +#define I2C_OA0 0x28 +#define I2C_SA 0x2C +#define I2C_PSC 0x30 +#define I2C_SCLL 0x34 +#define I2C_SCLH 0x38 +#define I2C_SYSTEST 0x3C +#define I2C_BUFSTAT 0x40 +#define I2C_OA1 0x44 +#define I2C_OA2 0x48 +#define I2C_OA3 0x4C +#define I2C_ACTOA 0x50 +#define I2C_SBLOCK 0x54 + +#define OMAP3_I2C_WE 0x0C +#define OMAP3_I2C_SYSC 0x20 + +#define OMAP4_I2C_WE 0x34 +#define OMAP4_I2C_SYSC 0x10 + + + +#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_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_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) + + + +#endif /* _OMAP_I2C_H_ */ diff --git a/sys/arm/omap/omap_machdep.c b/sys/arm/omap/omap_machdep.c new file mode 100644 index 0000000..d998c90 --- /dev/null +++ b/sys/arm/omap/omap_machdep.c @@ -0,0 +1,596 @@ +/*- + * Copyright (c) 2009 Guillaume Ballet + * 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$"); + +#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 +#define DEBUG_INITARM +#define VERBOSE_INIT_ARM + +#include + + + + +#if defined(SOC_OMAP4) +#include +const struct pmap_devmap omap_devmap[] = { + /* + * Add the main memory areas, + */ +/* + { + .pd_va = OMAP44XX_L3_EMU_VBASE, + .pd_pa = OMAP44XX_L3_EMU_HWBASE, + .pd_size = OMAP44XX_L3_EMU_SIZE, + .pd_prot = VM_PROT_READ|VM_PROT_WRITE, + .pd_cache = PTE_DEVICE + }, +*/ + + /* SDRAM EMIF register set, mapped as read-only for now */ + { + .pd_va = OMAP44XX_L3_EMIF1_VBASE, + .pd_pa = OMAP44XX_L3_EMIF1_HWBASE, + .pd_size = OMAP44XX_L3_EMIF1_SIZE, + .pd_prot = VM_PROT_READ, + .pd_cache = PTE_NOCACHE + }, + { + .pd_va = OMAP44XX_L3_EMIF2_VBASE, + .pd_pa = OMAP44XX_L3_EMIF2_HWBASE, + .pd_size = OMAP44XX_L3_EMIF2_SIZE, + .pd_prot = VM_PROT_READ, + .pd_cache = PTE_NOCACHE + }, + + /* General memory areas */ + { + .pd_va = OMAP44XX_L4_CORE_VBASE, + .pd_pa = OMAP44XX_L4_CORE_HWBASE, + .pd_size = OMAP44XX_L4_CORE_SIZE, + .pd_prot = VM_PROT_READ|VM_PROT_WRITE, + .pd_cache = PTE_NOCACHE + }, + { + .pd_va = OMAP44XX_L4_PERIPH_VBASE, + .pd_pa = OMAP44XX_L4_PERIPH_HWBASE, + .pd_size = OMAP44XX_L4_PERIPH_SIZE, + .pd_prot = VM_PROT_READ|VM_PROT_WRITE, + .pd_cache = PTE_NOCACHE + }, + { + .pd_va = OMAP44XX_L4_ABE_VBASE, + .pd_pa = OMAP44XX_L4_ABE_HWBASE, + .pd_size = OMAP44XX_L4_ABE_SIZE, + .pd_prot = VM_PROT_READ|VM_PROT_WRITE, + .pd_cache = PTE_NOCACHE + }, + { 0, 0, 0, 0, 0 } /* Array terminator */ +}; + +#elif defined(SOC_OMAP3) +#include +const struct pmap_devmap omap_devmap[] = { + /* + * For the moment, map devices with PA==VA. + */ + { + /* 16MB of L4, covering all L4 core registers */ + .pd_va = OMAP35XX_L4_CORE_VBASE, + .pd_pa = OMAP35XX_L4_CORE_HWBASE, + .pd_size = OMAP35XX_L4_CORE_SIZE, + .pd_prot = VM_PROT_READ|VM_PROT_WRITE, + .pd_cache = PTE_NOCACHE + }, + { + /* 1MB of L4, covering the periph registers */ + .pd_va = OMAP35XX_L4_PERIPH_VBASE, + .pd_pa = OMAP35XX_L4_PERIPH_HWBASE, + .pd_size = OMAP35XX_L4_PERIPH_SIZE, + .pd_prot = VM_PROT_READ|VM_PROT_WRITE, + .pd_cache = PTE_NOCACHE + }, + { + /* 64Kb of L3, covering the SDRAM controller registers */ + .pd_va = OMAP35XX_SDRC_VBASE, + .pd_pa = OMAP35XX_SDRC_HWBASE, + .pd_size = OMAP35XX_SDRC_SIZE, + .pd_prot = VM_PROT_READ|VM_PROT_WRITE, + .pd_cache = PTE_NOCACHE + }, +#if 0 + { + /* 64Kb of L3, covering the SDRAM controller registers */ + .pd_va = OMAP35XX_L3_VBASE, + .pd_pa = OMAP35XX_L3_HWBASE, + .pd_size = OMAP35XX_L3_SIZE, + .pd_prot = VM_PROT_READ|VM_PROT_WRITE, + .pd_cache = PTE_NOCACHE + }, +#endif + { 0, 0, 0, 0, 0 } /* Array terminator */ +}; + +#else +#error OMAP chip type not defined + +#endif + + +#ifdef VERBOSE_INIT_ARM +#define DPRINTF(x) +#else +#define DPRINTF(x) +#endif + + +uint32_t omap3_chip_id; + +/* Physical page ranges */ +vm_paddr_t phys_avail[4]; +vm_paddr_t dump_avail[4]; + +struct pv_addr systempage; + +#define FIQ_STACK_SIZE 1 +#define IRQ_STACK_SIZE 1 +#define ABT_STACK_SIZE 1 +#define UND_STACK_SIZE 1 + + +extern int _start[]; +extern int _end[]; + + +extern void early_putchar(unsigned char); +extern void early_putstr(unsigned char *); +extern void early_printf(const char *fmt, ...); +extern void early_print_init(uint32_t hw_addr, uint32_t virt_addr); + + +extern void beagle_dump_ttb(vm_offset_t va); + +extern u_int data_abort_handler_address; +extern u_int prefetch_abort_handler_address; +extern u_int undefined_handler_address; + +static struct pv_addr fiqstack; /* Stack page descriptors for all modes */ +static struct pv_addr msgbuf; +static struct pv_addr irqstack; +static struct pv_addr undstack; +static struct pv_addr abtstack; +static struct pv_addr kernelstack; +static struct pv_addr kernel_l1pt; /* Level-1 page table entry */ + + + + +#define KERNEL_PT_SYS 0 /* Page table for mapping proc0 zero page */ +#define KERNEL_PT_KERN 1 +#define KERNEL_PT_KERN_NUM 22 +#define KERNEL_PT_AFKERNEL KERNEL_PT_KERN + KERNEL_PT_KERN_NUM /* L2 table for mapping after kernel */ +#define KERNEL_PT_AFKERNEL_NUM 5 + +/* this should be evenly divisable by PAGE_SIZE / L2_TABLE_SIZE_REAL (or 4) */ +#define NUM_KERNEL_PTS (KERNEL_PT_AFKERNEL + KERNEL_PT_AFKERNEL_NUM) + + + +static struct pv_addr kernel_page_tables[NUM_KERNEL_PTS]; /* Level-2 page table entries for the kernel */ + + + +static struct trapframe proc0_tf; + +#define PHYS2VIRT(x) ((x - KERNPHYSADDR) + KERNVIRTADDR) +#define VIRT2PHYS(x) ((x - KERNVIRTADDR) + KERNPHYSADDR) + +/* Macro stolen from the Xscale part, used to simplify TLB allocation */ +#define valloc_pages(var, np) \ + alloc_pages((var).pv_pa, (np)); \ + (var).pv_va = PHYS2VIRT((var).pv_pa); \ + DPRINTF(("va=%p pa=%p\n", (void*)(var).pv_va, (void*)(var).pv_pa)); + +#define alloc_pages(var, np) \ + (var) = freemempos; \ + freemempos += (np * PAGE_SIZE); \ + memset((char *)(var), 0, ((np) * PAGE_SIZE)); + +#define round_L_page(x) (((x) + L2_L_OFFSET) & L2_L_FRAME) + +#define VERBOSE_INIT_ARM + + + + +static void * +initarm_boilerplate(void *arg1, void *arg2) +{ + vm_offset_t freemempos; + vm_offset_t freemem_pt; + vm_offset_t lastaddr; + vm_offset_t offset; +// vm_offset_t afterkern; + uint32_t i, j; + uint32_t sdram_size; + u_int32_t l1pagetable; + size_t textsize; + size_t totalsize; + + /* + * Sets the CPU functions based on the CPU ID, this code is all + * in the cpufunc.c file. The function sets the cpufunc structure + * to match the machine and also initialises the pmap/pte code. + */ + set_cpufuncs(); + + + /* + * fake_preload_metadata() creates a fake boot descriptor table and + * returns the last address in the kernel. + */ + lastaddr = fake_preload_metadata(); + + + /* + * Initialize the MI portions of a struct per cpu structure. + */ + pcpu_init(pcpup, 0, sizeof(struct pcpu)); + PCPU_SET(curthread, &thread0); + init_param1(); + + /* + * Initialize freemempos. Pages are allocated from the end of the RAM's + * first 64MB, as it is what is covered by the default TLB in locore.S. + */ + freemempos = VIRT2PHYS(round_L_page(lastaddr)); + + /* Reserve L1 table pages now, as freemempos is 64K-aligned */ + valloc_pages(kernel_l1pt, L1_TABLE_SIZE / PAGE_SIZE); + /* + * Reserve the paging system pages, page #0 is reserved as a L2 table for + * the exception vector. Now allocate L2 page tables; they are linked to L1 below + */ + for (i = 0; i < NUM_KERNEL_PTS; ++i) { + j = (i % (PAGE_SIZE / L2_TABLE_SIZE_REAL)); + + if (j == 0) { + valloc_pages(kernel_page_tables[i], L2_TABLE_SIZE / PAGE_SIZE); + } else { + + kernel_page_tables[i].pv_pa = kernel_page_tables[i - j].pv_pa + + (j * L2_TABLE_SIZE_REAL); + kernel_page_tables[i].pv_va = kernel_page_tables[i - j].pv_va + + (j * L2_TABLE_SIZE_REAL); + } + } + + /* base of allocated pt's */ + freemem_pt = freemempos; + + + /* + * Allocate a page for the system page mapped to V0x00000000. This page + * will just contain the system vectors and can be shared by all processes. + * This is where the interrupt vector is stored. + */ + valloc_pages(systempage, 1); + systempage.pv_va = ARM_VECTORS_HIGH; + + /* Allocate dynamic per-cpu area. */ + /* TODO: valloc_pages(dpcpu, DPCPU_SIZE / PAGE_SIZE); */ + /* TODO: dpcpu_init((void *)dpcpu.pv_va, 0); */ + + + /* Allocate stacks for all modes */ + valloc_pages(fiqstack, FIQ_STACK_SIZE); + valloc_pages(irqstack, IRQ_STACK_SIZE); + valloc_pages(abtstack, ABT_STACK_SIZE); + valloc_pages(undstack, UND_STACK_SIZE); + valloc_pages(kernelstack, KSTACK_PAGES + 1); + valloc_pages(msgbuf, round_page(msgbufsize) / PAGE_SIZE); + + + + /* ** Build the TLBs **************************************************** */ + + /* + * Now construct the L1 page table. First map the L2 + * page tables into the L1 so we can replace L1 mappings + * later on if necessary + */ + l1pagetable = kernel_l1pt.pv_va; + + /* L2 table for the exception vector */ + pmap_link_l2pt(l1pagetable, ARM_VECTORS_HIGH & ~(0x100000 - 1), + &kernel_page_tables[0]); + + /* Insert a reference to the kernel L2 page tables into the L1 page. */ + for (i=1; ipcb_flags = 0; + thread0.td_frame = &proc0_tf; + pcpup->pc_curpcb = thread0.td_pcb; + +#ifdef VERBOSE_INIT_ARM + printf("\nThread-0 stack=0x%08x pcb=0x%08x\n", (unsigned int)thread0.td_kstack, (unsigned int)thread0.td_pcb); +#endif + + + /* Exception vector */ +#ifdef VERBOSE_INIT_ARM + printf("\nException vector at (0x%08x)\n", ((unsigned int *)systempage.pv_va)[0]); +#endif + arm_vector_init(ARM_VECTORS_HIGH, ARM_VEC_ALL); + + #ifdef VERBOSE_INIT_ARM + printf("\nException vector at (0x%08x)\n", ((unsigned int *)systempage.pv_va)[0]); +#endif + + /* First unbacked address of KVM */ + pmap_curmaxkvaddr = KERNVIRTADDR + 0x100000 * NUM_KERNEL_PTS; +#ifdef VERBOSE_INIT_ARM + printf("\npmap_curmaxkvaddr = 0x%08x\n", pmap_curmaxkvaddr); +#endif + + /* Get the size of the SDRAM, this is typically defined in the SoC specific + * files (i.e. omap44xx.c) + */ + sdram_size = omap_sdram_size(); +#ifdef VERBOSE_INIT_ARM + printf("\nSDRAM size 0x%08X, %dMB\n", sdram_size, (sdram_size / 1024 / 1024)); +#endif + + /* Physical ranges of available memory. */ + phys_avail[0] = freemempos; + phys_avail[1] = PHYSADDR + sdram_size; + phys_avail[2] = 0; + phys_avail[3] = 0; + + printf("WE HAVE MEMORY FROM %x TO %x %x %x\n", freemempos, PHYSADDR + sdram_size, PHYSADDR, sdram_size); + dump_avail[0] = PHYSADDR; + dump_avail[1] = PHYSADDR + sdram_size; + dump_avail[2] = 0; + dump_avail[3] = 0; + + physmem = sdram_size / PAGE_SIZE; + + pmap_bootstrap((freemempos&0x00ffffff)|0xc0000000, /* start address */ + 0xe8000000, /* end address, min adress for device stuff */ + &kernel_l1pt); + +// pmap_dump_ttb(pmap_kernel(), 1, 0); + + init_param2(physmem); + + /* Locking system */ + mutex_init(); + + msgbufp = (void*)msgbuf.pv_va; + msgbufinit(msgbufp, msgbufsize); + + /* Kernel debugger */ + kdb_init(); + vector_page_setprot(VM_PROT_READ); + + /* initarm returns the address of the kernel stack */ + return ((void *)(kernelstack.pv_va + USPACE_SVC_STACK_TOP - + sizeof(struct pcb))); + + +// return (void *)(kernelstack.pv_va + (KSTACK_PAGES + 1) * PAGE_SIZE); +} + + + + +void *initarm(void *arg1, void *arg2) +{ + return initarm_boilerplate(arg1, arg2); +} + + + +struct arm32_dma_range * +bus_dma_get_range(void) +{ + /* TODO: Need implementation ? */ + return (NULL); +} + +int +bus_dma_get_range_nb(void) +{ + /* TODO: Need implementation ? */ + return (0); +} + + diff --git a/sys/arm/omap/omap_mmc.c b/sys/arm/omap/omap_mmc.c new file mode 100644 index 0000000..4ed3001 --- /dev/null +++ b/sys/arm/omap/omap_mmc.c @@ -0,0 +1,1664 @@ +/*- + * 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 author may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 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. + */ + + +/** + * 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 "gpio_if.h" + +#include "mmcbr_if.h" +#include "mmcbus_if.h" + +#include +#include +#include +#include + +#include + + + +/** + * Structure that stores the driver context + */ +struct omap_mmc_softc { + device_t sc_dev; + 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; + + device_t sc_gpio_dev; + int sc_wp_gpio_pin; /* GPIO pin for MMC write protect */ + + 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; + +}; + +/** + * Macros for driver mutex locking + */ +#define OMAP_MMC_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) +#define OMAP_MMC_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) +#define OMAP_MMC_LOCK_INIT(_sc) \ + mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->sc_dev), \ + "omap_mmc", MTX_DEF) +#define OMAP_MMC_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); +#define OMAP_MMC_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED); +#define OMAP_MMC_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED); + + + +static void +omap_mmc_start(struct omap_mmc_softc *sc); + + +/** + * omap_mmc_readl - reads a 32-bit value from a register + * omap_mmc_writel - 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 +omap_mmc_readl(struct omap_mmc_softc *sc, bus_size_t off) +{ + return bus_read_4(sc->sc_mem_res, (sc->sc_reg_off + off)); +} + +static inline void +omap_mmc_writel(struct omap_mmc_softc *sc, bus_size_t off, uint32_t val) +{ + bus_write_4(sc->sc_mem_res, (sc->sc_reg_off + off), val); +} + + + + + + + +/** + * omap_mmc_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 omap_mmc_attach) with nsegments set to 1. + * + */ +static void +omap_mmc_getaddr(void *arg, bus_dma_segment_t *segs, int nsegs, int error) +{ + if (error != 0) + return; + + *(bus_addr_t *)arg = segs[0].ds_addr; +} + + + + +/** + * omap_mmc_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 +omap_mmc_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. + */ +} + + + +/** + * omap_mmc_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 +omap_mmc_intr_xfer_compl(struct omap_mmc_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 = omap_mmc_readl(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; +} + + +/** + * omap_mmc_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 +omap_mmc_intr_cmd_compl(struct omap_mmc_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] = omap_mmc_readl(sc, MMCHS_RSP10); + cmd->resp[2] = omap_mmc_readl(sc, MMCHS_RSP32); + cmd->resp[1] = omap_mmc_readl(sc, MMCHS_RSP54); + cmd->resp[0] = omap_mmc_readl(sc, MMCHS_RSP76); + } else { + cmd->resp[0] = omap_mmc_readl(sc, MMCHS_RSP10); + } + } + + /* Check if the command was expecting some data transfer, if not + * we are done. */ + cmd_reg = omap_mmc_readl(sc, MMCHS_CMD); + return ((cmd_reg & MMCHS_CMD_DP) == 0); +} + + +/** + * omap_mmc_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 +omap_mmc_intr_error(struct omap_mmc_softc *sc, struct mmc_command *cmd, + uint32_t stat_reg) +{ + uint32_t sysctl_reg; + + printf("ERROOR\n"); + /* 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; + + + /* Abort the DMA transfer (DDIR bit tells direction) */ + if (omap_mmc_readl(sc, MMCHS_CMD) & MMCHS_CMD_DDIR) + omap_dma_stop_xfer(sc->sc_dmach_rd); + else + omap_dma_stop_xfer(sc->sc_dmach_wr); + + + /* 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--; + } + + + /* 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)) { + + /* Reset the data lines and wait for it to complete */ + sysctl_reg = omap_mmc_readl(sc, MMCHS_SYSCTL); + sysctl_reg |= MMCHS_SYSCTL_SRD; + omap_mmc_writel(sc, MMCHS_SYSCTL, sysctl_reg); + + while (!omap_mmc_readl(sc, MMCHS_SYSCTL) & MMCHS_SYSCTL_SRD) + continue; + + /* TODO: This is probably not a good idea in an IRQ routine */ + while (omap_mmc_readl(sc, MMCHS_SYSCTL) & MMCHS_SYSCTL_SRD) + continue; + sysctl_reg = omap_mmc_readl(sc, MMCHS_SYSCTL); + sysctl_reg |= MMCHS_SYSCTL_SRC; + omap_mmc_writel(sc, MMCHS_SYSCTL, sysctl_reg); + + while (!omap_mmc_readl(sc, MMCHS_SYSCTL) & MMCHS_SYSCTL_SRC) + continue; + + /* TODO: This is probably not a good idea in an IRQ routine */ + while (omap_mmc_readl(sc, MMCHS_SYSCTL) & MMCHS_SYSCTL_SRC) + continue; + + } + + /* On any error the command is cancelled ... so we are done */ + return 1; +} + + + +/** + * omap_mmc_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 +omap_mmc_intr(void *arg) +{ + struct omap_mmc_softc *sc = (struct omap_mmc_softc *) arg; + uint32_t stat_reg; + int done = 0; + + + OMAP_MMC_LOCK(sc); + + stat_reg = omap_mmc_readl(sc, MMCHS_STAT) + & (omap_mmc_readl(sc, MMCHS_IE) | MMCHS_STAT_ERRI); + + if (sc->curcmd == NULL) { + device_printf(sc->sc_dev, "Error: current cmd NULL, already done?\n"); + omap_mmc_writel(sc, MMCHS_STAT, stat_reg); + OMAP_MMC_UNLOCK(sc); + return; + } + + + + if (stat_reg & MMCHS_STAT_ERRI) { + /* An error has been tripped in the status register */ + done = omap_mmc_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 = omap_mmc_intr_cmd_compl(sc, sc->curcmd); + } + + /* Check if the transfer has completed */ + if (stat_reg & MMCHS_STAT_TC) { + done = omap_mmc_intr_xfer_compl(sc, sc->curcmd); + } + + } + + /* Clear all the interrupt status bits by writing the value back */ + omap_mmc_writel(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) { + omap_mmc_start(sc); + } + + OMAP_MMC_UNLOCK(sc); +} + + + +/** + * omap_mmc_start_cmd - starts the given command + * @sc: pointer to the driver context + * @cmd: the command to start + * + * The call tree for this function is + * - omap_mmc_start_cmd + * - omap_mmc_start + * - omap_mmc_request + * + * LOCKING: + * Caller should be holding the OMAP_MMC lock. + * + * RETURNS: + * nothing + */ +static void +omap_mmc_start_cmd(struct omap_mmc_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; + uint32_t pktsize; + + + sc->curcmd = cmd; + data = cmd->data; + req = cmd->mrq = sc->req; + + /* By default no bits should be set for the CON register */ + con_reg = 0x0; + + /* 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; + + /* Put the controller in open drain mode */ + if (sc->host.ios.bus_mode == opendrain) + con_reg |= MMCHS_CON_OD; + + /* Check if there is any data to write */ + if (data == NULL) { + + /* The no data case is fairly simple */ + omap_mmc_writel(sc, MMCHS_CON, con_reg); + omap_mmc_writel(sc, MMCHS_IE, ise_reg); + omap_mmc_writel(sc, MMCHS_ISE, ise_reg); + omap_mmc_writel(sc, MMCHS_ARG, cmd->arg); + omap_mmc_writel(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; + + + //if (data->flags & (MMC_DATA_READ | MMC_DATA_WRITE)) + // cmd_reg |= MCI_CMDR_TRCMD_START; + + /* 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 */ + omap_mmc_writel(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, + omap_mmc_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; + } + + + /* Calculate the packet size, the max packet size is 512 bytes + * (or 128 32-bit elements). + */ + pktsize = min((data->len / 4), (512 / 4)); + + /* 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); + omap_dma_start_xfer_packet(sc->sc_dmach_rd, sc->sc_data_reg_paddr, + paddr, 1, (data->len / 4), pktsize); + } else { + bus_dmamap_sync(sc->sc_dmatag, sc->sc_dmamap, BUS_DMASYNC_PREWRITE); + omap_dma_start_xfer_packet(sc->sc_dmach_wr, paddr, + sc->sc_data_reg_paddr, 1, (data->len / 4), + pktsize); + } + + /* Increase the mapped count */ + sc->sc_dmamapped++; + + sc->sc_cmd_data_vaddr = vaddr; + sc->sc_cmd_data_len = data->len; + } + +#if 0 + printf("MMCHS_CON 0x%08x\n", con_reg); + printf("MMCHS_IE 0x%08x\n", ise_reg); + printf("MMCHS_ISE 0x%08x\n", ise_reg); + printf("MMCHS_CMD 0x%08x\n", cmd_reg); // 0x123a0033 + printf("MMCHS_ARG 0x%08x\n", cmd->arg); + printf("MMCHS_BLK 0x%08x\n", omap_mmc_readl(sc, MMCHS_BLK)); + printf("MMCHS_PSTATE 0x%08x\n", omap_mmc_readl(sc, MMCHS_PSTATE)); +#endif + + /* Finally kick off the command */ + omap_mmc_writel(sc, MMCHS_CON, con_reg); + omap_mmc_writel(sc, MMCHS_IE, ise_reg); + omap_mmc_writel(sc, MMCHS_ISE, ise_reg); + omap_mmc_writel(sc, MMCHS_ARG, cmd->arg); + omap_mmc_writel(sc, MMCHS_CMD, cmd_reg); + + /* and we're done */ +} + + + + +/** + * omap_mmc_start - starts a request stored in the driver context + * @sc: pointer to the driver context + * + * This function is called by omap_mmc_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 +omap_mmc_start(struct omap_mmc_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; + omap_mmc_start_cmd(sc, req->cmd); + return; + } + + if (!(sc->flags & STOP_STARTED) && req->stop) { + sc->flags |= STOP_STARTED; + omap_mmc_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); +} + + + +/** + * omap_mmc_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 +omap_mmc_request(device_t brdev, device_t reqdev, struct mmc_request *req) +{ + struct omap_mmc_softc *sc = device_get_softc(brdev); + + + OMAP_MMC_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) { + OMAP_MMC_UNLOCK(sc); + return (EBUSY); + } + + /* Store the request and start the command */ + sc->req = req; + sc->flags = 0; +#if 0 + sysctl_reg = omap_mmc_readl(sc, MMCHS_SYSCTL); + sysctl_reg |= MMCHS_SYSCTL_SRD; + omap_mmc_writel(sc, MMCHS_SYSCTL, sysctl_reg); + + while (!omap_mmc_readl(sc, MMCHS_SYSCTL) & MMCHS_SYSCTL_SRD) + continue; + + while (omap_mmc_readl(sc, MMCHS_SYSCTL) & MMCHS_SYSCTL_SRD) + continue; +#endif +#if 0 + sysctl_reg = omap_mmc_readl(sc, MMCHS_SYSCTL); + sysctl_reg |= MMCHS_SYSCTL_SRC; + omap_mmc_writel(sc, MMCHS_SYSCTL, sysctl_reg); + + while (!omap_mmc_readl(sc, MMCHS_SYSCTL) & MMCHS_SYSCTL_SRC) + continue; + + while (omap_mmc_readl(sc, MMCHS_SYSCTL) & MMCHS_SYSCTL_SRC) + continue; +#endif + + omap_mmc_start(sc); + + OMAP_MMC_UNLOCK(sc); + + return (0); +} + + +/** + * omap_mmc_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 +omap_mmc_get_ro(device_t brdev, device_t reqdev) +{ + struct omap_mmc_softc *sc = device_get_softc(brdev); + unsigned int readonly = 0; + + OMAP_MMC_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; + } + + OMAP_MMC_UNLOCK(sc); + +device_printf(brdev, "[BRG] %s : %d : readonly = %d : sc->sc_wp_gpio_pin = %d\n", + __func__, __LINE__, readonly, sc->sc_wp_gpio_pin); + + return (readonly); +} + + + +/** + * omap_mmc_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 +omap_mmc_update_ios(device_t brdev, device_t reqdev) +{ + struct omap_mmc_softc *sc; + struct mmc_host *host; + struct mmc_ios *ios; + uint32_t clkdiv; + uint32_t hctl_reg; + uint32_t sysctl_reg; + + sc = device_get_softc(brdev); + host = &sc->host; + ios = &host->ios; + + printf("UPDATE IOS\n"); + /* need the MMCHS_SYSCTL register */ + sysctl_reg = omap_mmc_readl(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); + printf("VDD %x\n", ios->vdd); + + /* Disable the clock output while configuring the new clock */ + sysctl_reg &= ~(MMCHS_SYSCTL_ICE | MMCHS_SYSCTL_CEN); + omap_mmc_writel(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 ... */ + omap_mmc_writel(sc, MMCHS_SYSCTL, sysctl_reg); + /* ... write the internal clock enable bit ... */ + omap_mmc_writel(sc, MMCHS_SYSCTL, sysctl_reg | MMCHS_SYSCTL_ICE); + /* ... wait for the clock to stablise ... */ + while (((sysctl_reg = omap_mmc_readl(sc, MMCHS_SYSCTL)) & + MMCHS_SYSCTL_ICS) == 0) { + continue; + } + /* ... then enable */ + sysctl_reg |= MMCHS_SYSCTL_CEN; + omap_mmc_writel(sc, MMCHS_SYSCTL, sysctl_reg); + + /* Set the bus width */ + hctl_reg = omap_mmc_readl(sc, MMCHS_HCTL); + uint32_t con = omap_mmc_readl(sc, 0x02c); + if (ios->bus_mode == opendrain) + con |= 0x1; + else + con &= ~0x1; + printf("set con to %x\n", con); + omap_mmc_writel(sc, 0x02c, con); + if (ios->bus_width == bus_width_4) + hctl_reg |= MMCHS_HCTL_DTW; + else + hctl_reg &= ~MMCHS_HCTL_DTW; + printf("bus width %d\n", ios->bus_width); + omap_mmc_writel(sc, MMCHS_HCTL, hctl_reg); + + + return (0); +} + + +/** + * omap_mmc_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 +omap_mmc_acquire_host(device_t brdev, device_t reqdev) +{ + struct omap_mmc_softc *sc = device_get_softc(brdev); + int err = 0; + + OMAP_MMC_LOCK(sc); + + while (sc->bus_busy) { + msleep(sc, &sc->sc_mtx, PZERO, "mmc", hz / 5); + } + + sc->bus_busy++; + + OMAP_MMC_UNLOCK(sc); + + return (err); +} + +/** + * omap_mmc_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 +omap_mmc_release_host(device_t brdev, device_t reqdev) +{ + struct omap_mmc_softc *sc = device_get_softc(brdev); + + OMAP_MMC_LOCK(sc); + + sc->bus_busy--; + wakeup(sc); + + OMAP_MMC_UNLOCK(sc); + + return (0); +} + + + + +/** + * omap_mmc_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 +omap_mmc_read_ivar(device_t bus, device_t child, int which, uintptr_t *result) +{ + struct omap_mmc_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); +} + + +/** + * omap_mmc_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 +omap_mmc_write_ivar(device_t bus, device_t child, int which, uintptr_t value) +{ + struct omap_mmc_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); +} + + + + + +/** + * omap_mmc_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 +omap_mmc_init(device_t dev) +{ + struct omap_mmc_softc *sc = device_get_softc(dev); + clk_ident_t clk; + unsigned long timeout = (hz < 10) ? 1 : ((100 * hz) / 1000); + uint32_t val; + uint32_t i; + + + /* 1: Enable the controller and interface/functional clocks */ + clk = MMC1_CLK + device_get_unit(dev); + if (omap_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 (omap_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 */ + omap_mmc_writel(sc, MMCHS_SYSCONFIG, 0x0002); + while ((omap_mmc_readl(sc, MMCHS_SYSSTATUS) & 0x01) == 0x00) { + pause("MMCRESET", 1); + if (timeout-- == 0) { + device_printf(dev, "Error: reset operation timed out\n"); + return; + } + } + + + /* 3: MMCHS Controller Voltage Capabilities Initialization */ + val = omap_mmc_readl(sc, MMCHS_CAPA); + omap_mmc_writel(sc, MMCHS_CAPA, (val | MMCHS_CAPA_VS30 | MMCHS_CAPA_VS18)); + + + /* 4: MMCHS Controller Default Initialization */ + + /* 4a: data bus width = 1, voltage = 1.8v, MMC bus power is on (not card's power) */ + omap_mmc_writel(sc, MMCHS_HCTL, 0x00000b00); + + /* 4b: card's clock enable and card's clock frequency divider. */ + omap_mmc_writel(sc, MMCHS_SYSCTL, 0x0000a007); + + /* 4c: Set MMC bus mode to open drain. */ + omap_mmc_writel(sc, MMCHS_CON, 0x00000001); + + + + /* 5: MMCHS Controller INIT Procedure Start */ + + /* 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. + */ + for (i=0; i<3; i++) { + + /* sets MMCHS1.MMCHS_CON[1] INIT to 1 */ + val = omap_mmc_readl(sc, MMCHS_CON); + val |= 0x00000002; + omap_mmc_writel(sc, MMCHS_CON, val); + + /* sends dummy command */ + omap_mmc_writel(sc, MMCHS_CMD, 0x00000000); + } + + + /* 6: MMCHS Controller Pre-card Identification Configuration */ + + /* Before card identification starts, the MMCHS controller's configuration + * should change. MMC card's clock should now be 400 KHz according to MMC + * system spec requirements. + */ + + /* data bus width = 1, voltage = 1.8v, MMC bus power is on (not card's power) */ + omap_mmc_writel(sc, MMCHS_HCTL, 0x00000b00); + + /* card's clock enable and card's clock frequency divider. */ + omap_mmc_writel(sc, MMCHS_SYSCTL, 0x00003C07); + + /* Set MMC bus mode to open drain. */ + omap_mmc_writel(sc, MMCHS_CON, 0x00000001); + + +} + + +/** + * omap_mmc_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 +omap_mmc_fini(device_t dev) +{ + /* TODO: Cleanup properly */ + + /* Disable the interrupts */ + + /* Disable the functional and interface clocks */ + switch (device_get_unit(dev)) { + case 0: + omap_prcm_clk_disable(MMC1_CLK); + break; + case 1: + omap_prcm_clk_disable(MMC2_CLK); + break; + case 2: + omap_prcm_clk_disable(MMC3_CLK); + break; + } + +} + + +/** + * omap_mmc_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 +omap_mmc_init_dma_channels(struct omap_mmc_softc *sc) +{ + int err; + int unit; + uint32_t rev; + int dma_rx_trig = -1; + int dma_tx_trig = -1; + + /* Get the device unit number, needed for getting the DMA trigger number */ + unit = device_get_unit(sc->sc_dev); + + /* Get the current chip revision */ + rev = omap_revision(); + if ((OMAP_REV_DEVICE(rev) != OMAP4430_DEV) && (unit > 2)) + return(-EINVAL); + + /* Get the DMA MMC triggers */ + switch (unit) { + case 0: + dma_tx_trig = 60; + dma_rx_trig = 61; + break; + case 1: + dma_tx_trig = 46; + dma_rx_trig = 47; + break; + case 2: + dma_tx_trig = 76; + dma_rx_trig = 77; + break; + /* The following are OMAP4 only */ + case 3: + dma_tx_trig = 56; + dma_rx_trig = 57; + break; + case 4: + dma_tx_trig = 58; + dma_rx_trig = 59; + break; + default: + return(-EINVAL); + } + + + /* Activate a RX channel from the OMAP DMA driver */ + err = omap_dma_activate_channel(&sc->sc_dmach_rd, omap_mmc_dma_intr, sc); + if (err != 0) + return(err); + + /* Setup the RX channel for MMC data transfers */ +// omap_dma_enable_channel_irq(sc->sc_dmach_rd, OMAP_SDMA_IRQ_FLAG_FRAME_COMPL); + omap_dma_set_xfer_burst(sc->sc_dmach_rd, OMAP_SDMA_BURST_NONE, + OMAP_SDMA_BURST_64); + omap_dma_set_xfer_data_type(sc->sc_dmach_rd, OMAP_SDMA_DATA_32BITS_SCALAR); + omap_dma_sync_params(sc->sc_dmach_rd, dma_rx_trig, + OMAP_SDMA_SYNC_PACKET | OMAP_SDMA_SYNC_TRIG_ON_SRC); + omap_dma_set_addr_mode(sc->sc_dmach_rd, OMAP_SDMA_ADDR_CONSTANT, + OMAP_SDMA_ADDR_POST_INCREMENT); + + + /* Activate and configure the TX DMA channel */ + err = omap_dma_activate_channel(&sc->sc_dmach_wr, omap_mmc_dma_intr, sc); + if (err != 0) + return(err); + + /* Setup the TX channel for MMC data transfers */ +// omap_dma_enable_channel_irq(sc->sc_dmach_wr, OMAP_SDMA_IRQ_FLAG_FRAME_COMPL); + omap_dma_set_xfer_burst(sc->sc_dmach_wr, OMAP_SDMA_BURST_64, + OMAP_SDMA_BURST_NONE); + omap_dma_set_xfer_data_type(sc->sc_dmach_wr, OMAP_SDMA_DATA_32BITS_SCALAR); + omap_dma_sync_params(sc->sc_dmach_wr, dma_tx_trig, + OMAP_SDMA_SYNC_PACKET | OMAP_SDMA_SYNC_TRIG_ON_DST); + omap_dma_set_addr_mode(sc->sc_dmach_wr, OMAP_SDMA_ADDR_POST_INCREMENT, + OMAP_SDMA_ADDR_CONSTANT); + + + return(0); +} + + + +/** + * omap_mmc_deactivate - deactivates the driver + * @dev: mmc device handle + * + * Unmaps the register set and releases the IRQ resource. + * + * LOCKING: + * None required + * + * RETURNS: + * nothing + */ +static void +omap_mmc_deactivate(device_t dev) +{ + struct omap_mmc_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); + + /* 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; +} + + +/** + * omap_mmc_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 +omap_mmc_activate(device_t dev) +{ + struct omap_mmc_softc *sc = device_get_softc(dev); + unsigned long addr; + int rid; + + /* 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; + + + /* Get the physical address of the MMC register set */ + addr = bus_get_resource_start(dev, SYS_RES_MEMORY, 0); + sc->sc_data_reg_paddr = addr + MMCHS_DATA; + + return (0); + +errout: + omap_mmc_deactivate(dev); + return (ENOMEM); +} + + + +/** + * omap_mmc_probe - probe function for the driver + * @dev: mmc device handle + * + * + * + * RETURNS: + * always returns 0 + */ +static int +omap_mmc_probe(device_t dev) +{ + + device_set_desc(dev, "TI OMAP MMC/SD/SDIO host bridge"); + return (0); +} + + +/** + * omap_mmc_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 omap_mmc_init(). + * + * RETURNS: + * Returns 0 on success or a negative error code. + */ +static int +omap_mmc_attach(device_t dev) +{ + struct omap_mmc_softc *sc = device_get_softc(dev); + int err; + device_t child; + + /* Save the device and bus tag */ + sc->sc_dev = dev; + + /* Initiate the mtex lock */ + OMAP_MMC_LOCK_INIT(sc); + + /* Indicate the DMA channels haven't yet been allocated */ + switch (device_get_unit(dev)) { + case 0: + sc->sc_dmach_rd = 62; + sc->sc_dmach_wr = 61; + break; + case 1: + sc->sc_dmach_rd = 48; + sc->sc_dmach_wr = 47; + break; + case 2: + sc->sc_dmach_rd = 78; + sc->sc_dmach_wr = 77; + break; + case 3: + sc->sc_dmach_rd = 58; + sc->sc_dmach_wr = 57; + break; + case 4: + sc->sc_dmach_rd = 60; + sc->sc_dmach_wr = 59; + break; + default: + panic("Invalid MMC unit !"); + } + + /* Get the hint'ed write detect pin */ + if (resource_int_value("omap_mmc", 0, "wp_gpio", &sc->sc_wp_gpio_pin) != 0){ + sc->sc_wp_gpio_pin = -1; + } else { + /* Get the GPIO device, we need this to 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); + } + + + int reg = *(int *)(OMAP44XX_SCM_PADCONF_VBASE + 0x628); + reg |= (1 << 31) | (1 << 30); + reg &= ~((1 << 29) | (1 << 28)); + reg |= (1 << 27) | (1 << 26) | (1 << 25); + *(int *)(OMAP44XX_SCM_PADCONF_VBASE + 0x628) = reg; + /* Activate the device */ + err = omap_mmc_activate(dev); + if (err) + goto out; + + + /* 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 out; + + err = bus_dmamap_create(sc->sc_dmatag, 0, &sc->sc_dmamap); + if (err != 0) + goto out; + + + + /* Initialise the DMA channels to be used by the controller */ + err = omap_mmc_init_dma_channels(sc); + if (err != 0) + goto out; + + + /* Set the register offset and reference clock freq */ + if (omap_is_omap3()) { + sc->sc_reg_off = OMAP3_MMCHS_REG_OFFSET; + } else if (omap_is_omap4()) { + sc->sc_reg_off = OMAP4_MMCHS_REG_OFFSET; + } else { + panic("Unknown OMAP device\n"); + } + + + /* Shutdown and restart the controller */ + omap_mmc_fini(dev); + omap_mmc_init(dev); + + + /* Activate the interrupt and attach a handler */ + err = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE, + NULL, omap_mmc_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_320_330 | MMC_OCR_330_340; + sc->host.caps = MMC_CAP_4_BIT_DATA; + + child = device_add_child(dev, "mmc", device_get_unit(dev)); + + device_set_ivars(dev, &sc->host); + err = bus_generic_attach(dev); + +out: + if (err) { + OMAP_MMC_LOCK_DESTROY(sc); + omap_mmc_deactivate(dev); + + if (sc->sc_dmach_rd != (unsigned int)-1) + omap_dma_deactivate_channel(sc->sc_dmach_rd); + if (sc->sc_dmach_wr != (unsigned int)-1) + omap_dma_deactivate_channel(sc->sc_dmach_wr); + } + + return (err); +} + + + +/** + * omap_mmc_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 +omap_mmc_detach(device_t dev) +{ + struct omap_mmc_softc *sc = device_get_softc(dev); + + omap_mmc_fini(dev); + omap_mmc_deactivate(dev); + + omap_dma_deactivate_channel(sc->sc_dmach_wr); + omap_dma_deactivate_channel(sc->sc_dmach_rd); + + return (0); +} + + +static device_method_t g_omap_mmc_methods[] = { + /* device_if */ + DEVMETHOD(device_probe, omap_mmc_probe), + DEVMETHOD(device_attach, omap_mmc_attach), + DEVMETHOD(device_detach, omap_mmc_detach), + + /* Bus interface */ + DEVMETHOD(bus_read_ivar, omap_mmc_read_ivar), + DEVMETHOD(bus_write_ivar, omap_mmc_write_ivar), + + /* mmcbr_if - MMC state machine callbacks */ + DEVMETHOD(mmcbr_update_ios, omap_mmc_update_ios), + DEVMETHOD(mmcbr_request, omap_mmc_request), + DEVMETHOD(mmcbr_get_ro, omap_mmc_get_ro), + DEVMETHOD(mmcbr_acquire_host, omap_mmc_acquire_host), + DEVMETHOD(mmcbr_release_host, omap_mmc_release_host), + + {0, 0}, +}; + +static driver_t g_omap_mmc_driver = { + "omap_mmc", + g_omap_mmc_methods, + sizeof(struct omap_mmc_softc), +}; +static devclass_t g_omap_mmc_devclass; + + +DRIVER_MODULE(omap_mmc, omap, g_omap_mmc_driver, g_omap_mmc_devclass, 0, 0); +MODULE_DEPEND(omap_mmc, omap_prcm, 1, 1, 1); +MODULE_DEPEND(omap_mmc, omap_dma, 1, 1, 1); +MODULE_DEPEND(omap_mmc, omap_gpio, 1, 1, 1); + diff --git a/sys/arm/omap/omap_mmc.h b/sys/arm/omap/omap_mmc.h new file mode 100644 index 0000000..e46c0c2 --- /dev/null +++ b/sys/arm/omap/omap_mmc.h @@ -0,0 +1,180 @@ +/*- + * 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 author may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 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. + */ +#ifndef _OMAP_MMC_H_ +#define _OMAP_MMC_H_ + + +/** + * Header file for the OMAP 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 + + + +/* 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(x) (((x) & 0x3) << 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_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_CAPA_VS18 (1UL << 26) +#define MMCHS_CAPA_VS30 (1UL << 25) +#define MMCHS_CAPA_VS33 (1UL << 24) + + + +#endif /* _OMAP_MMC_H_ */ diff --git a/sys/arm/omap/omap_prcm.c b/sys/arm/omap/omap_prcm.c new file mode 100644 index 0000000..16dc7a7 --- /dev/null +++ b/sys/arm/omap/omap_prcm.c @@ -0,0 +1,531 @@ +/* + * 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 + + + +/** + * Structure that stores the driver context. + * + * This structure is allocated during driver attach, it is not designed to be + * deallocated and a pointer to it is stored globally (g_omap3_prcm_softc). + */ +struct omap_prcm_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[4]; + + struct mtx sc_mtx; +}; + +static struct omap_prcm_softc *g_omap_prcm_softc = NULL; + + +/** + * g_omap_prcm_spec - Resource specification used when allocating resources + * + * This driver module can have up to four independant memory regions. + */ +static struct resource_spec g_omap_prcm_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 }, + { -1, 0, 0 } +}; + + +/** + * omap_clk_devmap - Array of clock devices, should be defined one per SoC + * + * This array is typically defined in one of the targeted omap??_prcm_clk.c + * files and is specific to the given SoC platform. Each entry in the array + * corresponds to an individual clock device. + */ +extern const struct omap_clock_dev omap_clk_devmap[]; + + + +/** + * Macros for driver mutex locking + */ +#define OMAP_PRCM_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) +#define OMAP_PRCM_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) +#define OMAP_PRCM_LOCK_INIT(_sc) \ + mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->sc_dev), \ + "omap_prcm", MTX_DEF) +#define OMAP_PRCM_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); +#define OMAP_PRCM_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED); +#define OMAP_PRCM_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED); + + + + +/** + * omap_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 const struct omap_clock_dev * +omap_prcm_clk_dev(struct omap_prcm_softc *sc, clk_ident_t clk) +{ + const struct omap_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 = &(omap_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 */ + device_printf(sc->sc_dev, "Error: Failed to find clock device (%d)\n", clk); + return (NULL); +} + + + + + + +/** + * omap_prcm_clk_valid - enables a clock for a particular module + * @clk: identifier for the module to enable, see omap_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 +omap_prcm_clk_valid(clk_ident_t clk) +{ + struct omap_prcm_softc *sc = g_omap_prcm_softc; + int ret = 0; + + /* Sanity check */ + if (sc == NULL) { + device_printf(sc->sc_dev, "Error: PRCM module not setup (%s)\n", __func__); + return (ENOMEM); + } + + OMAP_PRCM_LOCK(sc); + + if (omap_prcm_clk_dev(sc, clk) == NULL) + ret = EINVAL; + + OMAP_PRCM_UNLOCK(sc); + + return (ret); +} + + +/** + * omap_prcm_clk_enable - enables a clock for a particular module + * @clk: identifier for the module to enable, see omap_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 +omap_prcm_clk_enable(clk_ident_t clk) +{ + struct omap_prcm_softc *sc = g_omap_prcm_softc; + const struct omap_clock_dev *clk_dev; + int ret; + + /* Sanity check */ + if (sc == NULL) { + device_printf(sc->sc_dev, "Error: PRCM module not setup (%s)\n", __func__); + return (ENOMEM); + } + + OMAP_PRCM_LOCK(sc); + + /* 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 = omap_prcm_clk_dev(sc, clk); + + /* Sanity check we managed to find the clock */ + if (clk_dev == NULL) { + OMAP_PRCM_UNLOCK(sc); + return (EINVAL); + } + + /* Activate the clock */ + if (clk_dev->clk_activate) + ret = clk_dev->clk_activate(clk_dev, sc->sc_mem_res); + else + ret = EINVAL; + + + OMAP_PRCM_UNLOCK(sc); + + return (ret); +} + + +/** + * omap_prcm_clk_disable - disables a clock for a particular module + * @clk: identifier for the module to enable, see omap_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 +omap_prcm_clk_disable(clk_ident_t clk) +{ + struct omap_prcm_softc *sc = g_omap_prcm_softc; + const struct omap_clock_dev *clk_dev; + int ret; + + /* Sanity check */ + if (sc == NULL) { + device_printf(sc->sc_dev, "Error: PRCM module not setup (%s)\n", __func__); + return (ENOMEM); + } + + OMAP_PRCM_LOCK(sc); + + /* 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 = omap_prcm_clk_dev(sc, clk); + + /* Sanity check we managed to find the clock */ + if (clk_dev == NULL) { + OMAP_PRCM_UNLOCK(sc); + return (EINVAL); + } + + /* Activate the clock */ + if (clk_dev->clk_deactivate) + ret = clk_dev->clk_deactivate(clk_dev, sc->sc_mem_res); + else + ret = EINVAL; + + + OMAP_PRCM_UNLOCK(sc); + + return (ret); +} + + + +/** + * omap_prcm_clk_set_source - sets the source + * @clk: identifier for the module to enable, see omap_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 +omap_prcm_clk_set_source(clk_ident_t clk, clk_src_t clksrc) +{ + struct omap_prcm_softc *sc = g_omap_prcm_softc; + const struct omap_clock_dev *clk_dev; + int ret; + + /* Sanity check */ + if (sc == NULL) { + device_printf(sc->sc_dev, "Error: PRCM module not setup (%s)\n", __func__); + return (ENOMEM); + } + + OMAP_PRCM_LOCK(sc); + + /* 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 = omap_prcm_clk_dev(sc, clk); + + /* Sanity check we managed to find the clock */ + if (clk_dev == NULL) { + OMAP_PRCM_UNLOCK(sc); + return (EINVAL); + } + + /* Activate the clock */ + if (clk_dev->clk_set_source) + ret = clk_dev->clk_set_source(clk_dev, clksrc, sc->sc_mem_res); + else + ret = EINVAL; + + + OMAP_PRCM_UNLOCK(sc); + + return (ret); +} + + +/** + * omap_prcm_clk_get_source_freq - gets the source clock frequency + * @clk: identifier for the module to enable, see omap_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 +omap_prcm_clk_get_source_freq(clk_ident_t clk, unsigned int *freq) +{ + struct omap_prcm_softc *sc = g_omap_prcm_softc; + const struct omap_clock_dev *clk_dev; + int ret; + + /* Sanity check */ + if (sc == NULL) { + device_printf(sc->sc_dev, "Error: PRCM module not setup (%s)\n", __func__); + return (ENOMEM); + } + + OMAP_PRCM_LOCK(sc); + + /* 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 = omap_prcm_clk_dev(sc, clk); + + /* Sanity check we managed to find the clock */ + if (clk_dev == NULL) { + OMAP_PRCM_UNLOCK(sc); + 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, sc->sc_mem_res); + else + ret = EINVAL; + + + OMAP_PRCM_UNLOCK(sc); + + return (ret); +} + + + +/** + * omap_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 +omap_prcm_probe(device_t dev) +{ + 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 +omap_prcm_attach(device_t dev) +{ + struct omap_prcm_softc *sc = device_get_softc(dev); + int err; + + if (g_omap_prcm_softc != NULL) + return (ENXIO); + + sc->sc_dev = dev; + + OMAP_PRCM_LOCK_INIT(sc); + + /* Attempt to get the memory resource(s) */ + err = bus_alloc_resources(dev, g_omap_prcm_spec, sc->sc_mem_res); + if (err) { + device_printf(dev, "Error: could not allocate resources\n"); + return (ENXIO); + } + + + /* Setup DPLL5, this is the 120MHz clock used by the USB module */ + // omap3_prcm_setup_dpll5(sc, 120, 12); + + g_omap_prcm_softc = sc; + + return (0); +} + +#define PRM_RSTCTRL 0x1b00 +#define PRM_RSTCTRL_RESET 0x2 + +void +omap_prcm_reset(void) +{ + struct omap_prcm_softc *sc = g_omap_prcm_softc; + bus_write_4(sc->sc_mem_res[0], PRM_RSTCTRL, + bus_read_4(sc->sc_mem_res[0], PRM_RSTCTRL) | PRM_RSTCTRL_RESET); + bus_read_4(sc->sc_mem_res[0], PRM_RSTCTRL); +} + +static device_method_t g_omap_prcm_methods[] = { + DEVMETHOD(device_probe, omap_prcm_probe), + DEVMETHOD(device_attach, omap_prcm_attach), + {0, 0}, +}; + +static driver_t g_omap_prcm_driver = { + "omap_prcm", + g_omap_prcm_methods, + sizeof(struct omap_prcm_softc), +}; +static devclass_t g_omap_prcm_devclass; + +DRIVER_MODULE(omap_prcm, omap, g_omap_prcm_driver, g_omap_prcm_devclass, 0, 0); +MODULE_VERSION(omap_prcm, 1); + + + + diff --git a/sys/arm/omap/omap_prcm.h b/sys/arm/omap/omap_prcm.h new file mode 100644 index 0000000..3467d6a --- /dev/null +++ b/sys/arm/omap/omap_prcm.h @@ -0,0 +1,184 @@ +/* + * 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. + */ + + +/* + * Texas Instruments - OMAP3xxx series processors + * + * Reference: + * OMAP35x Applications Processor + * Technical Reference Manual + * (omap35xx_techref.pdf) + */ +#ifndef _OMAP_PRCM_H_ +#define _OMAP_PRCM_H_ + + +typedef enum { + + /* System clocks, typically you can only call omap_prcm_clk_get_source_freq() + * on these clocks as they are enabled by default. + */ + SYS_CLK = 1, + + /* The MPU (ARM) core clock */ + MPU_CLK = 20, + + /* MMC modules */ + MMC1_CLK = 100, + MMC2_CLK, + MMC3_CLK, + MMC4_CLK, + MMC5_CLK, + + /* I2C modules */ + I2C1_CLK = 200, + 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 */ + GPIO1_CLK = 700, + GPIO2_CLK, + GPIO3_CLK, + GPIO4_CLK, + GPIO5_CLK, + GPIO6_CLK, + + /* sDMA module */ + SDMA_CLK = 800, + + 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 omap_clock_dev { + /* The profile of the timer */ + clk_ident_t id; + + /* A bunch of callbacks associated with the clock device */ + int (*clk_activate)(const struct omap_clock_dev *clkdev, + struct resource* mem_res[]); + int (*clk_deactivate)(const struct omap_clock_dev *clkdev, + struct resource* mem_res[]); + int (*clk_set_source)(const struct omap_clock_dev *clkdev, clk_src_t clksrc, + struct resource* mem_res[]); + int (*clk_accessible)(const struct omap_clock_dev *clkdev, + struct resource* mem_res[]); + int (*clk_get_source_freq)(const struct omap_clock_dev *clkdev, + unsigned int *freq, struct resource* mem_res[]); + +}; + + + +int +omap_prcm_clk_valid(clk_ident_t clk); + +int +omap_prcm_clk_enable(clk_ident_t clk); + +int +omap_prcm_clk_disable(clk_ident_t clk); + +int +omap_prcm_clk_accessible(clk_ident_t clk); + +int +omap_prcm_clk_disable_autoidle(clk_ident_t clk); + +int +omap_prcm_clk_set_source(clk_ident_t clk, clk_src_t clksrc); + +int +omap_prcm_clk_get_source_freq(clk_ident_t clk, unsigned int *freq); + +void +omap_prcm_reset(void); + +#endif /* _OMAP_PRCM_H_ */ diff --git a/sys/arm/omap/omap_scm.c b/sys/arm/omap/omap_scm.c new file mode 100644 index 0000000..73fdfec --- /dev/null +++ b/sys/arm/omap/omap_scm.c @@ -0,0 +1,574 @@ +/* + * 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 omap_scm_padconf structs + * call omap_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 "omap_scm.h" + + + + + + + +/** + * Structure that stores the driver context. + * + * This structure is allocated during driver attach, it is not designed to be + * deallocated and a pointer to it is stored globally (g_omap3_scm_softc). + */ +struct omap_scm_softc { + device_t sc_dev; + struct resource* sc_mem_res; + struct mtx sc_mtx; +}; + +static struct omap_scm_softc *g_omap_scm_sc = NULL; + + + +/** + * omap_padconf_devmap - Array of pins, should be defined one per SoC + * + * This array is typically defined in one of the targeted omap??_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 omap_scm_padconf omap_padconf_devmap[]; + + + +/** + * Macros for driver mutex locking + */ +#define OMAP_SCM_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) +#define OMAP_SCM_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) +#define OMAP_SCM_LOCK_INIT(_sc) \ + mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->sc_dev), \ + "omap_scm", MTX_DEF) +#define OMAP_SCM_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); +#define OMAP_SCM_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED); +#define OMAP_SCM_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED); + + + + +/** + * omap_scm_reads - reads a 16-bit value from one of the PADCONFS registers + * @sc: SCM device context + * @off: The offset of a register from the SCM register address range + * + * + * RETURNS: + * 16-bit value read from the register. + */ +static inline uint16_t +omap_scm_reads(struct omap_scm_softc *sc, bus_size_t off) +{ + return (bus_read_2(sc->sc_mem_res, off)); +} + +/** + * omap_scm_writes - writes a 16-bit value to one of the PADCONFS registers + * @sc: SCM device context + * @off: The offset of a register from the SCM register address range + * @val: The value to write into the register + * + * RETURNS: + * nothing + */ +static inline void +omap_scm_writes(struct omap_scm_softc *sc, bus_size_t off, uint16_t val) +{ + bus_write_2(sc->sc_mem_res, off, val); +} + + + + + +/** + * omap_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 omap_scm_padconf* +omap_scm_padconf_from_name(const char *ballname) +{ + const struct omap_scm_padconf *padconf; + + padconf = omap_padconf_devmap; + while (padconf->ballname != NULL) { + if (strcmp(ballname, padconf->ballname) == 0) + return(padconf); + padconf++; + } + + return (NULL); +} + + +/** + * omap_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. + * -ENOMEM on driver not initialised. + * -EINVAL if pin requested is outside valid range or already in use. + */ +static int +omap_scm_padconf_set_internal(struct omap_scm_softc *sc, + const struct omap_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 & CONTROL_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 & CONTROL_PADCONF_MUXMODE_MASK); + + printf("setting internal %x for %s\n", reg_val, muxmode); + /* write the register value (16-bit writes) */ + omap_scm_writes(sc, padconf->reg_off, reg_val); + + return (0); +} + +/** + * omap_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. + * -ENOMEM on driver not initialised. + * -EINVAL if pin requested is outside valid range or already in use. + */ +int +omap_scm_padconf_set(const char *padname, const char *muxmode, unsigned int state) +{ + struct omap_scm_softc *sc = g_omap_scm_sc; + const struct omap_scm_padconf *padconf; + + /* sanity checking */ + if (sc == NULL) + return (-ENOMEM); + + /* find the pin in the devmap */ + padconf = omap_scm_padconf_from_name(padname); + if (padconf == NULL) + return (-EINVAL); + + return (omap_scm_padconf_set_internal(sc, padconf, muxmode, state)); +} + +/** + * omap_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. + * -ENOMEM on driver not initialised. + * -EINVAL if pin requested is outside valid range or already in use. + */ +int +omap_scm_padconf_get(const char *padname, const char **muxmode, unsigned int *state) +{ + struct omap_scm_softc *sc = g_omap_scm_sc; + const struct omap_scm_padconf *padconf; + uint16_t reg_val; + + /* sanity checking */ + if (sc == NULL) + return (-ENOMEM); + + /* find the pin in the devmap */ + padconf = omap_scm_padconf_from_name(padname); + if (padconf == NULL) + return (-EINVAL); + + /* read the register value (16-bit reads) */ + reg_val = omap_scm_reads(sc, padconf->reg_off); + + /* save the state */ + if (state) + *state = (reg_val & CONTROL_PADCONF_SATE_MASK); + + /* save the mode */ + if (muxmode) + *muxmode = padconf->muxmodes[(reg_val & CONTROL_PADCONF_MUXMODE_MASK)]; + + return (0); +} + + +/** + * omap_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. + * -ENOMEM on driver not initialised. + * -EINVAL if pin requested is outside valid range or already in use. + */ +int +omap_scm_padconf_set_gpiomode(uint32_t gpio, unsigned int state) +{ + struct omap_scm_softc *sc = g_omap_scm_sc; + const struct omap_scm_padconf *padconf; + uint16_t reg_val; + + /* sanity checking */ + if (sc == NULL) + return(-ENOMEM); + + /* find the gpio pin in the padconf array */ + padconf = omap_padconf_devmap; + 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 & CONTROL_PADCONF_SATE_MASK); + + /* set the mux mode */ + reg_val |= (uint16_t)(padconf->gpio_mode & CONTROL_PADCONF_MUXMODE_MASK); + + /* write the register value (16-bit writes) */ + omap_scm_writes(sc, padconf->reg_off, reg_val); + + return (0); +} + + + +/** + * omap_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. + * -ENOMEM on driver not initialised. + * -EINVAL if pin requested is outside valid range or not configured as GPIO. + */ +int +omap_scm_padconf_get_gpiomode(uint32_t gpio, unsigned int *state) +{ + struct omap_scm_softc *sc = g_omap_scm_sc; + const struct omap_scm_padconf *padconf; + uint16_t reg_val; + + /* sanity checking */ + if (sc == NULL) + return(-ENOMEM); + + /* find the gpio pin in the padconf array */ + padconf = omap_padconf_devmap; + while (padconf->ballname != NULL) { + if (padconf->gpio_pin == gpio) + break; + padconf++; + } + if (padconf->ballname == NULL) + return (-EINVAL); + + + /* read the current register settings */ + reg_val = omap_scm_reads(sc, padconf->reg_off); + + /* check to make sure the pins is configured as GPIO in the first state */ + if ((reg_val & CONTROL_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 & CONTROL_PADCONF_SATE_MASK); + + return (0); +} + + + +/** + * omap_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. + * -ENOMEM on driver not initialised. + * -EINVAL if pin requested is outside valid range or already in use. + */ +static int +omap_scm_padconf_init_from_hints(struct omap_scm_softc *sc) +{ + const struct omap_scm_padconf *padconf; + int err; + char resname[64]; + const char *resval; + char muxname[64]; + char padstate[64]; + + /* Hint names should be of the form "padconf." */ + strcpy(resname, "padconf."); + + + /* This is very inefficent ... basically we look up every possible pad name + * in the hints. Unfortunatly there doesn't seem to be any way to iterate + * over all the hints for a given device, so instead we have to manually + * probe for the existance of every possible pad. + */ + padconf = omap_padconf_devmap; + while (padconf->ballname != NULL) { + + strncpy(&resname[8], padconf->ballname, 50); + + err = resource_string_value("omap_scm", 0, resname, &resval); + if ((err == 0) && (resval != NULL)) { + + /* Found a matching pad/ball name in the hints section, the hint + * should be of the following format: + * : + * i.e. + * usbb1_ulpiphy_stp:output + */ + + /* Read the mux name */ + if (sscanf(resval, "%64[^:]:%64s", muxname, padstate) != 2) { + device_printf(sc->sc_dev, "err: padconf hint for pin \"%s\"" + "is incorrectly formated, ignoring hint.\n", + padconf->ballname); + } + + /* Convert the padstate to a flag and write the values */ + else { + if (strcmp(padstate, "output") == 0) + err = omap_scm_padconf_set_internal(sc, padconf, muxname, + PADCONF_PIN_OUTPUT); + else if (strcmp(padstate, "input") == 0) + err = omap_scm_padconf_set_internal(sc, padconf, muxname, + PADCONF_PIN_INPUT); + else if (strcmp(padstate, "input_pullup") == 0) + err = omap_scm_padconf_set_internal(sc, padconf, muxname, + PADCONF_PIN_INPUT_PULLUP); + else if (strcmp(padstate, "input_pulldown") == 0) + err = omap_scm_padconf_set_internal(sc, padconf, muxname, + PADCONF_PIN_INPUT_PULLDOWN); + else + device_printf(sc->sc_dev, "err: padconf hint for pin \"%s\"" + "has incorrectly formated state, ignoring hint.\n", + padconf->ballname); + } + } + + padconf++; + } + + return (0); +} + + + + + + +/** + * omap_scm_probe - probe function for the driver + * @dev: scm device handle + * + * Simply sets the name of the driver module. + * + * LOCKING: + * None + * + * RETURNS: + * Always returns 0 + */ +static int +omap_scm_probe(device_t dev) +{ + device_set_desc(dev, "TI OMAP System Control Module"); + return (0); +} + +/** + * omap_scm_attach - attach 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 +omap_scm_attach(device_t dev) +{ + struct omap_scm_softc *sc = device_get_softc(dev); + int rid; + + if (g_omap_scm_sc != NULL) + return (ENXIO); + + sc->sc_dev = dev; + + OMAP_SCM_LOCK_INIT(sc); + + /* Attempt to get the memory resource */ + rid = 0; + sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (sc->sc_mem_res == NULL) { + device_printf(dev, "Error: could not allocate resources\n"); + return (ENXIO); + } + + /* Save the sc globally */ + g_omap_scm_sc = sc; + + /* Process initial values based on the hints */ + omap_scm_padconf_init_from_hints(sc); + + return (0); +} + +static device_method_t g_omap_scm_methods[] = { + DEVMETHOD(device_probe, omap_scm_probe), + DEVMETHOD(device_attach, omap_scm_attach), + {0, 0}, +}; + +static driver_t g_omap_scm_driver = { + "omap_scm", + g_omap_scm_methods, + sizeof(struct omap_scm_softc), +}; +static devclass_t g_omap_scm_devclass; + +DRIVER_MODULE(omap_scm, omap, g_omap_scm_driver, g_omap_scm_devclass, 0, 0); +MODULE_VERSION(omap_scm, 1); + + + + diff --git a/sys/arm/omap/omap_scm.h b/sys/arm/omap/omap_scm.h new file mode 100644 index 0000000..2b55c7b --- /dev/null +++ b/sys/arm/omap/omap_scm.h @@ -0,0 +1,123 @@ +/* + * 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. + */ + + +/** + * 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 _OMAP_SCM_H_ +#define _OMAP_SCM_H_ + + + +#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 + + + +struct omap_scm_padconf { + uint16_t reg_off; + uint16_t gpio_pin; + uint16_t gpio_mode; + const char *ballname; + const char *muxmodes[8]; +}; + + + +int +omap_scm_padconf_set(const char *padname, const char *muxmode, unsigned int state); + +int +omap_scm_padconf_get(const char *padname, const char **muxmode, unsigned int *state); + +int +omap_scm_padconf_set_gpiomode(uint32_t gpio, unsigned int state); + +int +omap_scm_padconf_get_gpiomode(uint32_t gpio, unsigned int *state); + + + + +#endif /* _OMAP_SCM_H_ */ diff --git a/sys/arm/omap/omap_space_asm.S b/sys/arm/omap/omap_space_asm.S new file mode 100644 index 0000000..eb61af8 --- /dev/null +++ b/sys/arm/omap/omap_space_asm.S @@ -0,0 +1,183 @@ +/* $NetBSD: bus_space_asm_generic.S,v 1.3 2003/03/27 19:46:14 mycroft Exp $ */ + +/*- + * Copyright (c) 1997 Causality Limited. + * Copyright (c) 1997 Mark Brinicombe. + * 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 Mark Brinicombe + * for the NetBSD Project. + * 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 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 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 +#include +__FBSDID("$FreeBSD$"); + +/* + * Generic bus_space functions. + */ + +/* + * read single + */ + +ENTRY(generic_armv7_bs_r_2) + ldrh r0, [r1, r2] + RET + +/* + * write single + */ + +ENTRY(generic_armv7_bs_w_2) + strh r3, [r1, r2] + RET + +/* + * read multiple + */ + +ENTRY(generic_armv7_bs_rm_2) + add r0, r1, r2 + mov r1, r3 + ldr r2, [sp, #0] + teq r2, #0 + RETeq + +1: ldrh r3, [r0] + strh r3, [r1], #2 + subs r2, r2, #1 + bne 1b + + RET + +/* + * write multiple + */ + +ENTRY(generic_armv7_bs_wm_2) + add r0, r1, r2 + mov r1, r3 + ldr r2, [sp, #0] + teq r2, #0 + RETeq + +1: ldrh r3, [r1], #2 + strh r3, [r0] + subs r2, r2, #1 + bne 1b + + RET + +/* + * read region + */ + +ENTRY(generic_armv7_bs_rr_2) + add r0, r1, r2 + mov r1, r3 + ldr r2, [sp, #0] + teq r2, #0 + RETeq + +1: ldrh r3, [r0], #2 + strh r3, [r1], #2 + subs r2, r2, #1 + bne 1b + + RET + +/* + * write region. + */ + +ENTRY(generic_armv7_bs_wr_2) + add r0, r1, r2 + mov r1, r3 + ldr r2, [sp, #0] + teq r2, #0 + RETeq + +1: ldrh r3, [r1], #2 + strh r3, [r0], #2 + subs r2, r2, #1 + bne 1b + + RET + +/* + * set region + */ + +ENTRY(generic_armv7_bs_sr_2) + add r0, r1, r2 + mov r1, r3 + ldr r2, [sp, #0] + teq r2, #0 + RETeq + +1: strh r1, [r0], #2 + subs r2, r2, #1 + bne 1b + + RET + +/* + * copy region + */ + +ENTRY(generic_armv7_bs_c_2) + add r0, r1, r2 + ldr r2, [sp, #0] + add r1, r2, r3 + ldr r2, [sp, #4] + teq r2, #0 + RETeq + + cmp r0, r1 + blt 2f + +1: ldrh r3, [r0], #2 + strh r3, [r1], #2 + subs r2, r2, #1 + bne 1b + + RET + +2: add r0, r0, r2, lsl #1 + add r1, r1, r2, lsl #1 + sub r0, r0, #2 + sub r1, r1, #2 + +3: ldrh r3, [r0], #-2 + strh r3, [r1], #-2 + subs r2, r2, #1 + bne 3b + + RET diff --git a/sys/arm/omap/omapvar.h b/sys/arm/omap/omapvar.h new file mode 100644 index 0000000..8a2c215 --- /dev/null +++ b/sys/arm/omap/omapvar.h @@ -0,0 +1,165 @@ +/* + * 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. + */ + +#ifndef _OMAPVAR_H_ +#define _OMAPVAR_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + + +/* + * Random collection of functions and definitions ... needs cleanup + * + * + * + */ + + +extern struct bus_space omap_bs_tag; + +unsigned int +omap_sdram_size(void); + +void +omap_mask_all_intr(void); + +void +omap_post_filter_intr(void *arg); + +int +omap_setup_intr(device_t dev, device_t child, + struct resource *res, int flags, driver_filter_t *filt, + driver_intr_t *intr, void *arg, void **cookiep); + +int +omap_teardown_intr(device_t dev, device_t child, struct resource *res, + void *cookie); + + + +/** + * OMAP Device IDs + * + * These values are typically read out of the ID_CODE register, located at + * physical address 0x4A00 2204 on most OMAP devices + */ +#define OMAP_CPUID_OMAP3530 0x0C00 +#define OMAP_CPUID_OMAP3525 0x4C00 +#define OMAP_CPUID_OMAP3515 0x1C00 +#define OMAP_CPUID_OMAP3503 0x5C00 + +#define OMAP_CPUID_OMAP4430_ES1_2 0xB852 +#define OMAP_CPUID_OMAP4430 0xB95C + + + + +/** + * struct omap_softc + * + * + * + */ +extern uint32_t omap3_chip_id; + +static inline int +omap_cpu_is(uint32_t cpu) +{ + return ((omap3_chip_id & 0xffff) == cpu); +} + + +/** + * struct omap_softc + * + * + * + */ +struct omap_softc { + device_t sc_dev; + bus_space_tag_t sc_iotag; + bus_space_handle_t sc_ioh; + + struct rman sc_irq_rman; + struct rman sc_mem_rman; + bus_dma_tag_t sc_dmat; +}; + + +struct omap_mem_range { + bus_addr_t base; + bus_size_t size; +}; + +/** + * struct omap_cpu_dev + * + * Structure used to define all the SoC devices, it allows for two memory + * address ranges and 4 IRQ's per device. + * + */ +struct omap_cpu_dev { + const char *name; + int unit; + + struct omap_mem_range mem[16]; + int irqs[16]; +}; + + + +struct omap_ivar { + struct resource_list resources; +}; + + + + + +#endif /* _OMAP3VAR_H_ */ diff --git a/sys/arm/omap/std.omap b/sys/arm/omap/std.omap new file mode 100644 index 0000000..3e862a8 --- /dev/null +++ b/sys/arm/omap/std.omap @@ -0,0 +1,6 @@ +# $FreeBSD$ + +# This should be armv7-a but current gcc doesn't support it +# makeoptions CONF_CFLAGS=-march=armv6 + +cpu CPU_CORTEXA diff --git a/sys/conf/files b/sys/conf/files index 47857a6..9ec96dc 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1925,6 +1925,7 @@ dev/usb/net/if_ipheth.c optional ipheth dev/usb/net/if_kue.c optional kue dev/usb/net/if_mos.c optional mos dev/usb/net/if_rue.c optional rue +dev/usb/net/if_smsc.c optional smsc dev/usb/net/if_udav.c optional udav dev/usb/net/if_usie.c optional usie dev/usb/net/ruephy.c optional rue diff --git a/sys/conf/options.arm b/sys/conf/options.arm index a667f9c..1215a36 100644 --- a/sys/conf/options.arm +++ b/sys/conf/options.arm @@ -37,6 +37,8 @@ SOC_MV_FREY opt_global.h SOC_MV_KIRKWOOD opt_global.h SOC_MV_LOKIPLUS opt_global.h SOC_MV_ORION opt_global.h +SOC_OMAP3 opt_global.h +SOC_OMAP4 opt_global.h STARTUP_PAGETABLE_ADDR opt_global.h XSCALE_CACHE_READ_WRITE_ALLOCATE opt_global.h XSACLE_DISABLE_CCNT opt_timer.h diff --git a/sys/dev/usb/net/if_smsc.c b/sys/dev/usb/net/if_smsc.c new file mode 100644 index 0000000..b208a7d --- /dev/null +++ b/sys/dev/usb/net/if_smsc.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. + * 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$"); + +/* + * SMSC LAN9xxx devices. + * + * + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "usbdevs.h" + +#define USB_DEBUG_VAR smsc_debug +#include +#include + +#include +#include +#include + + +#ifdef USB_DEBUG +static int smsc_debug = 0; + +SYSCTL_NODE(_hw_usb, OID_AUTO, smsc, CTLFLAG_RW, 0, "USB smsc"); +SYSCTL_INT(_hw_usb_smsc, OID_AUTO, debug, CTLFLAG_RW, &smsc_debug, 0, + "Debug level"); +#endif + +/* + * Various supported device vendors/products. + */ +static const struct usb_device_id smsc_devs[] = { +#define SMSC_DEV(p,i) { USB_VPI(USB_VENDOR_SMC2, USB_PRODUCT_SMC2_##p, i) } + SMSC_DEV(LAN9514_ETH, 0), +#undef SMSC_DEV +}; + + +static device_probe_t smsc_probe; +static device_attach_t smsc_attach; +static device_detach_t smsc_detach; + +static usb_callback_t smsc_bulk_read_callback; +static usb_callback_t smsc_bulk_write_callback; +static usb_callback_t smsc_intr_callback; + +static miibus_readreg_t smsc_miibus_readreg; +static miibus_writereg_t smsc_miibus_writereg; +static miibus_statchg_t smsc_miibus_statchg; + +static uether_fn_t smsc_attach_post; +static uether_fn_t smsc_init; +static uether_fn_t smsc_stop; +static uether_fn_t smsc_start; +static uether_fn_t smsc_tick; +static uether_fn_t smsc_setmulti; +static uether_fn_t smsc_setpromisc; + +static int smsc_ifmedia_upd(struct ifnet *); +static void smsc_ifmedia_sts(struct ifnet *, struct ifmediareq *); + +static int smsc_chip_init(struct smsc_softc *sc); + +static const struct usb_config smsc_config[SMSC_N_TRANSFER] = { + + [SMSC_BULK_DT_WR] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .frames = 16, + .bufsize = 16 * MCLBYTES, + .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, + .callback = smsc_bulk_write_callback, + .timeout = 10000, /* 10 seconds */ + }, + + [SMSC_BULK_DT_RD] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .bufsize = 18944, /* bytes */ + .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .callback = smsc_bulk_read_callback, + .timeout = 0, /* no timeout */ + }, + + [SMSC_INTR_DT_RD] = { + .type = UE_INTERRUPT, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .bufsize = 0, /* use wMaxPacketSize */ + .callback = smsc_intr_callback, + }, +}; + +static const struct usb_ether_methods smsc_ue_methods = { + .ue_attach_post = smsc_attach_post, + .ue_start = smsc_start, + .ue_init = smsc_init, + .ue_stop = smsc_stop, + .ue_tick = smsc_tick, + .ue_setmulti = smsc_setmulti, + .ue_setpromisc = smsc_setpromisc, + .ue_mii_upd = smsc_ifmedia_upd, + .ue_mii_sts = smsc_ifmedia_sts, +}; + + + + + + +/** + * smsc_read_reg - Reads a 32-bit register on the device + * @sc: driver soft context + * + * + * + * RETURNS: + * Returns 0 on success or a negative error code. + */ +static int +smsc_read_reg(struct smsc_softc *sc, uint32_t off, uint32_t *data) +{ + struct usb_device_request req; + uint32_t buf; + usb_error_t err; + + SMSC_LOCK_ASSERT(sc, MA_OWNED); + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = USB_VENDOR_REQUEST_READ_REGISTER; + USETW(req.wValue, 0); + USETW(req.wIndex, off); + USETW(req.wLength, 4); + + err = uether_do_request(&sc->sc_ue, &req, &buf, 1000); + if (err != 0) + device_printf(sc->sc_ue.ue_dev, "Failed to read register 0x%0x\n", off); + + *data = le32toh(buf); + +// printf("%s : %d : off=0x%02x : data=0x%08x\n", __func__, __LINE__, off, *data); + + return (err); +} + +/** + * smsc_write_reg - Reads a 32-bit register on the device + * @sc: driver soft context + * + * + * + * RETURNS: + * Returns 0 on success or a negative error code. + */ +static int +smsc_write_reg(struct smsc_softc *sc, uint32_t off, uint32_t data) +{ + struct usb_device_request req; + uint32_t buf; + usb_error_t err; + +// printf("%s : %d : off=0x%02x : data=0x%08x\n", __func__, __LINE__, off, data); + + SMSC_LOCK_ASSERT(sc, MA_OWNED); + + buf = htole32(data); + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = USB_VENDOR_REQUEST_WRITE_REGISTER; + USETW(req.wValue, 0); + USETW(req.wIndex, off); + USETW(req.wLength, 4); + + err = uether_do_request(&sc->sc_ue, &req, &buf, 1000); + if (err != 0) + device_printf(sc->sc_ue.ue_dev, "Failed to read register 0x%0x\n", off); + + return (err); +} + + + + + +/** + * smsc_phy_wait_not_busy - Loops until the phy is not busy or timeout occurs + * @sc: device soft context + * + * Loop until the read is completed or timeout has occured. The time-out is + * currently fixed at 1 second. + * + * LOCKING: + * Should be called with the SMSC lock held. + * + * RETURNS: + * Returns 0 on success or a negative error code. + */ +static int +smsc_phy_wait_not_busy(struct smsc_softc *sc) +{ + usb_ticks_t start_ticks = ticks; + usb_ticks_t max_ticks = USB_MS_TO_TICKS(1000); + uint32_t val; + +// printf("%s : %d : \n", __func__, __LINE__); + + SMSC_LOCK_ASSERT(sc, MA_OWNED); + + do { + smsc_read_reg(sc, MII_ADDR, &val); + if (!(val & MII_BUSY_)) + return(0); + } while ((ticks - start_ticks) < max_ticks); + + return(-EIO); +} + +/** + * smsc_miibus_readreg - Reads a MII/MDIO register + * @dev: usb ether device + * @phy: the number of phy writing to + * @reg: the register address + * @val: the value to write + * + * + * + * RETURNS: + * Returns 0 on success or a negative error code. + */ +static int +smsc_miibus_readreg(device_t dev, int phy, int reg) +{ + struct smsc_softc *sc = device_get_softc(dev); + int err; + int locked; + uint32_t addr; + uint32_t val; + + locked = mtx_owned(&sc->sc_mtx); + if (!locked) + SMSC_LOCK(sc); + + /* confirm MII not busy */ + if ((err = smsc_phy_wait_not_busy(sc)) != 0) { + device_printf(sc->sc_ue.ue_dev, "MII is busy in smsc_miibus_readreg\n"); + goto done; + } + + /* set the address, index & direction (read from PHY) */ + addr = (phy << 11) | (reg << 6) | MII_READ_; + smsc_write_reg(sc, MII_ADDR, addr); + + if ((err = smsc_phy_wait_not_busy(sc)) != 0) { + device_printf(sc->sc_ue.ue_dev, "Timed out reading MII reg %02X\n", reg); + goto done; + } + + smsc_read_reg(sc, MII_DATA, &val); + +done: + if (!locked) + SMSC_UNLOCK(sc); + +// printf("%s : %d : phy=%d : reg=0x%02x : val=0x%04x\n", __func__, __LINE__, phy, reg, val); + + return (val & 0xFFFF); +} + +/** + * smsc_miibus_writereg - Writes a MII/MDIO register + * @dev: usb ether device + * @phy: the number of phy writing to + * @reg: the register address + * @val: the value to write + * + * + * + * RETURNS: + * Returns 0 on success or a negative error code. + */ +static int +smsc_miibus_writereg(device_t dev, int phy, int reg, int val) +{ + struct smsc_softc *sc = device_get_softc(dev); + int err; + int locked; + uint32_t addr; + +// printf("%s : %d : phy=%d : reg=0x%02x : val=0x%04x\n", __func__, __LINE__, phy, reg, val); + + if (sc->sc_phyno != phy) + return (0); + + locked = mtx_owned(&sc->sc_mtx); + if (!locked) + SMSC_LOCK(sc); + + /* confirm MII not busy */ + if ((err = smsc_phy_wait_not_busy(sc)) != 0) { + device_printf(sc->sc_ue.ue_dev, "MII is busy in smsc_miibus_writereg\n"); + goto done; + } + + val = htole32(val); + smsc_write_reg(sc, MII_DATA, val); + + /* set the address, index & direction (write to PHY) */ + addr = (phy << 11) | (reg << 6) | MII_WRITE_; + smsc_write_reg(sc, MII_ADDR, addr); + + if ((err = smsc_phy_wait_not_busy(sc)) != 0) + device_printf(sc->sc_ue.ue_dev, "Timed out writing MII reg %02X\n", reg); + +done: + if (!locked) + SMSC_UNLOCK(sc); + return (err); +} + + + +/** + * smsc_miibus_statchg - Called when the MII status changes + * @dev: usb ether device + * + * + * + * RETURNS: + * Returns 0 on success or a negative error code. + */ +static void +smsc_miibus_statchg(device_t dev) +{ + struct smsc_softc *sc = device_get_softc(dev); + struct mii_data *mii = uether_getmii(&sc->sc_ue); + struct ifnet *ifp; + int locked; + int err; + +// printf("%s : %d : \n", __func__, __LINE__); + + locked = mtx_owned(&sc->sc_mtx); + if (!locked) + SMSC_LOCK(sc); + + ifp = uether_getifp(&sc->sc_ue); + if (mii == NULL || ifp == NULL || + (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) + goto done; + + /* Use the MII status to determine link status */ + sc->sc_flags &= ~SMSC_FLAG_LINK; + if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) == + (IFM_ACTIVE | IFM_AVALID)) { + switch (IFM_SUBTYPE(mii->mii_media_active)) { + case IFM_10_T: + case IFM_100_TX: + sc->sc_flags |= SMSC_FLAG_LINK; + break; + case IFM_1000_T: + /* Gigabit ethernet not supported by chipset */ + break; + default: + break; + } + } + + /* Lost link, do nothing. */ + if ((sc->sc_flags & SMSC_FLAG_LINK) == 0) { + device_printf(sc->sc_ue.ue_dev, "Link flag not set\n"); + goto done; + } + + + /* Enable/disable full duplex operation */ + if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) { + device_printf(sc->sc_ue.ue_dev, "Enable full duplex operation\n"); + sc->sc_mac_cr &= ~MAC_CR_RCVOWN_; + sc->sc_mac_cr |= MAC_CR_FDPX_; + } else { + device_printf(sc->sc_ue.ue_dev, "Disable full duplex operation\n"); + sc->sc_mac_cr &= ~MAC_CR_FDPX_; + sc->sc_mac_cr |= MAC_CR_RCVOWN_; + } + + err = smsc_write_reg(sc, MAC_CR, sc->sc_mac_cr); + if (err) { + device_printf(dev, "media change failed, error %d\n", err); + } + +done: + if (!locked) + SMSC_UNLOCK(sc); +} + + + + +/** + * smsc_ifmedia_upd - Set media options + * @ifp: interface pointer + * + * Basically boilerplate code that simply calls the mii functions to set the + * media options. + * + * RETURNS: + * Returns 0 on success or a negative error code. + */ +static int +smsc_ifmedia_upd(struct ifnet *ifp) +{ + struct smsc_softc *sc = ifp->if_softc; + struct mii_data *mii = uether_getmii(&sc->sc_ue); + int err; + +// printf("%s : %d : \n", __func__, __LINE__); + + SMSC_LOCK_ASSERT(sc, MA_OWNED); + + if (mii->mii_instance) { + struct mii_softc *miisc; + + LIST_FOREACH(miisc, &mii->mii_phys, mii_list) + mii_phy_reset(miisc); + } + err = mii_mediachg(mii); + return (err); +} + +/** + * smsc_ifmedia_sts - Report current media status + * @ifp: + * @ifmr: + * + * Basically boilerplate code that simply calls the mii functions to get the + * media status. + * + * RETURNS: + * Returns 0 on success or a negative error code. + */ +static void +smsc_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) +{ + struct smsc_softc *sc = ifp->if_softc; + struct mii_data *mii = uether_getmii(&sc->sc_ue); + +// printf("%s : %d : \n", __func__, __LINE__); + + SMSC_LOCK(sc); + + mii_pollstat(mii); + + SMSC_UNLOCK(sc); + + ifmr->ifm_active = mii->mii_media_active; + ifmr->ifm_status = mii->mii_media_status; +} + + +/** + * smsc_hash - Calculate the hash of a mac address + * @addr: The mac address to calculate the has on + * + * + * RETURNS: + * Returns a value from 0-63 which is the hash of the mac address. + */ +static inline uint32_t +smsc_hash(uint8_t addr[ETHER_ADDR_LEN]) +{ + return (ether_crc32_be(addr, ETHER_ADDR_LEN) >> 26) & 0x3f; +} + + +/** + * smsc_setmulti - Setup multicast + * @ue: + * + * + * RETURNS: + * Returns 0 on success or a negative error code. + */ +static void +smsc_setmulti(struct usb_ether *ue) +{ + struct smsc_softc *sc = uether_getsc(ue); + struct ifnet *ifp = uether_getifp(ue); + struct ifmultiaddr *ifma; + uint32_t hashtbl[2] = { 0, 0 }; + uint32_t hash; + + printf("%s : %d : \n", __func__, __LINE__); + + SMSC_LOCK_ASSERT(sc, MA_OWNED); + + if (ifp->if_flags & IFF_PROMISC) { + /* Enter promiscuous mode and set the bits accordingly */ + device_printf(sc->sc_ue.ue_dev, "promiscuous mode enabled\n"); + sc->sc_mac_cr |= MAC_CR_PRMS_; + sc->sc_mac_cr &= ~(MAC_CR_MCPAS_ | MAC_CR_HPFILT_); + } else if (ifp->if_flags & IFF_ALLMULTI) { + /* Enter multicaste mode and set the bits accordingly */ + device_printf(sc->sc_ue.ue_dev, "receive all multicast enabled\n"); + sc->sc_mac_cr |= MAC_CR_MCPAS_; + sc->sc_mac_cr &= ~(MAC_CR_PRMS_ | MAC_CR_HPFILT_); + + } else { + /* Take the lock of the mac address list before hashing each of them */ + if_maddr_rlock(ifp); + + if (!TAILQ_EMPTY(&ifp->if_multiaddrs)) { + /* We are filtering on a set of address so calculate hashes of each + * of the address and set the corresponding bits in the register. + */ + sc->sc_mac_cr |= MAC_CR_HPFILT_; + sc->sc_mac_cr &= ~(MAC_CR_PRMS_ | MAC_CR_MCPAS_); + + TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + + hash = smsc_hash(LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); + hashtbl[hash >> 5] |= 1 << (hash & 0x1F); + } + } else { + /* Only receive packets with destination set to our mac address */ + sc->sc_mac_cr &= ~(MAC_CR_PRMS_ | MAC_CR_MCPAS_ | MAC_CR_HPFILT_); + } + + if_maddr_runlock(ifp); + + /* Debug */ + if (sc->sc_mac_cr & MAC_CR_HPFILT_) + device_printf(sc->sc_ue.ue_dev, "receive select group of macs\n"); + else + device_printf(sc->sc_ue.ue_dev, "receive own packets only\n"); + } + + /* Write the hash table and mac control registers */ + smsc_write_reg(sc, HASHH, hashtbl[1]); + smsc_write_reg(sc, HASHL, hashtbl[0]); + smsc_write_reg(sc, MAC_CR, sc->sc_mac_cr); +} + + +/** + * smsc_setpromisc - Setup promiscuous mode + * @ue: + * + * + * RETURNS: + * Returns 0 on success or a negative error code. + */ +static void +smsc_setpromisc(struct usb_ether *ue) +{ + struct smsc_softc *sc = uether_getsc(ue); + struct ifnet *ifp = uether_getifp(ue); + + device_printf(sc->sc_ue.ue_dev, "promiscuous mode enabled\n"); + + SMSC_LOCK_ASSERT(sc, MA_OWNED); + + /* Set/clear the promiscuous bit based on setting */ + if (ifp->if_flags & IFF_PROMISC) { + sc->sc_mac_cr |= MAC_CR_PRMS_; + } else { + sc->sc_mac_cr &= ~MAC_CR_PRMS_; + } + + /* Write mac control registers */ + smsc_write_reg(sc, MAC_CR, sc->sc_mac_cr); +} + + + +/** + * smsc_set_mac_address - Sets the mac address in the device + * @sc: driver soft context + * @addr: pointer to array contain at least 6 bytes of the mac + * + * Writes the MAC address into the device, usually this doesn't need to be + * done because typically the MAC is read from the attached EEPROM. + * + * + * RETURNS: + * Returns 0 on success or a negative error code. + */ +static int +smsc_set_mac_address(struct smsc_softc *sc, const uint8_t *addr) +{ + int err; + uint32_t tmp; + + printf("%s : %d : MAC %02x:%02x:%02x:%02x:%02x:%02x\n", __func__, __LINE__, + addr[5], addr[4], addr[3], addr[2], addr[1], addr[0]); + + /* Program the lower 4 bytes of the MAC */ + tmp = addr[3] << 24 | addr[2] << 16 | addr[1] << 8 | addr[0]; + if ((err = smsc_write_reg(sc, ADDRL, tmp)) != 0) + goto fail; + + /* Program the upper 2 bytes of the MAC */ + tmp = addr[5] << 8 | addr[4]; + err = smsc_write_reg(sc, ADDRH, tmp); + +fail: + return (err); +} + + + +/** + * smsc_reset - Reset the SMSC interface + * @sc: device soft context + * + * + * + * RETURNS: + * Returns 0 on success or a negative error code. + */ +static void +smsc_reset(struct smsc_softc *sc) +{ + struct usb_config_descriptor *cd; + usb_error_t err; + + printf("%s : %d : \n", __func__, __LINE__); + + cd = usbd_get_config_descriptor(sc->sc_ue.ue_udev); + + err = usbd_req_set_config(sc->sc_ue.ue_udev, &sc->sc_mtx, + cd->bConfigurationValue); + if (err) + device_printf(sc->sc_ue.ue_dev, "reset failed (ignored)\n"); + + /* Wait a little while for the chip to get its brains in order. */ + uether_pause(&sc->sc_ue, hz / 100); + + /* Reinitialize controller to achieve full reset. */ + smsc_chip_init(sc); +} + + +/** + * smsc_init - Initialises the LAN95xx chip + * @ue: USB ether interface + * + * + * + * RETURNS: + * Returns 0 on success or a negative error code. + */ +static void +smsc_init(struct usb_ether *ue) +{ + struct smsc_softc *sc = uether_getsc(ue); + struct ifnet *ifp = uether_getifp(ue); + +// printf("%s : %d : \n", __func__, __LINE__); + + SMSC_LOCK_ASSERT(sc, MA_OWNED); + + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) + return; + + /* Cancel pending I/O */ + smsc_stop(ue); + + /* Reset the ethernet interface */ + smsc_reset(sc); + + /* Set MAC address. */ + smsc_set_mac_address(sc, IF_LLADDR(ifp)); + + /* Load the multicast filter. */ + smsc_setmulti(ue); + + usbd_xfer_set_stall(sc->sc_xfer[SMSC_BULK_DT_WR]); + + + /* The chip supports TCP/UDP checksum offloading on TX and RX paths */ + //ifp->if_capabilities |= IFCAP_RXCSUM | IFCAP_TXCSUM; + // ifp->if_capenable |= IFCAP_RXCSUM | IFCAP_TXCSUM; + // ifp->if_hwassist = CSUM_TCP | CSUM_UDP; + + /* Indicate we are up and running */ + ifp->if_drv_flags |= IFF_DRV_RUNNING; + + /* Switch to selected media. */ + smsc_ifmedia_upd(ifp); + smsc_start(ue); +} + + +/** + * smsc_intr_callback - Inteerupt callback used to process the USB packet + * @xfer: the USB transfer + * @error: + * + * + * + * RETURNS: + * Returns 0 on success or a negative error code. + */ +static void +smsc_intr_callback(struct usb_xfer *xfer, usb_error_t error) +{ + //struct smsc_softc *sc = usbd_xfer_softc(xfer); + //struct ifnet *ifp = uether_getifp(&sc->sc_ue); + int actlen; + + usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); + +// printf("smsc_intr_callback : USB_GET_STATE(xfer) = %d\n", USB_GET_STATE(xfer)); +} + +/** + * smsc_bulk_read_callback - Read callback used to process the USB packet + * @xfer: the USB transfer + * @error: + * + * + * + * RETURNS: + * Returns 0 on success or a negative error code. + */ +static void +smsc_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct smsc_softc *sc = usbd_xfer_softc(xfer); + struct usb_ether *ue = &sc->sc_ue; + struct ifnet *ifp = uether_getifp(ue); +#if 0 + struct mbuf *m; +#endif + struct usb_page_cache *pc; + uint32_t rxhdr; + uint16_t pktlen; + uint32_t len; +#if 0 + uint32_t off; +#endif + + int actlen; + int frames; + static int count = 0; + + usbd_xfer_status(xfer, &actlen, NULL, &frames, NULL); + + +//printf("smsc_bulk_read_callback : USB_GET_STATE(xfer) = %d\n", USB_GET_STATE(xfer)); + + count++; + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + /* From looking at the linux driver it appears the received packet + * is prefixed with a 32-bit header, which contains information like + * the status of the received packet. + * + * Also there maybe multiple packets in the USB frame, each will + * have a header and each needs to have it's own mbuf allocated and + * populated for it. + */ + if (actlen < sizeof(rxhdr) + ETHER_CRC_LEN) { + ifp->if_ierrors++; + printf("DROP\n"); + goto tr_setup; + } + pc = usbd_xfer_get_frame(xfer, 0); + usbd_copy_out(pc, 0, &rxhdr, sizeof(rxhdr)); + actlen -= sizeof(rxhdr); + rxhdr = le32toh(rxhdr); + pktlen = (uint16_t)((rxhdr & RX_STS_FL_) >> 16); + if (0) + printf("actlen %d len %d\n", actlen, pktlen); + len = min(pktlen, actlen); + if (rxhdr & RX_STS_ES_) { + ifp->if_ierrors++; + printf("DROP\n"); + goto tr_setup; + } + uether_rxbuf(ue, pc, 2 + sizeof(rxhdr), len); + +#if 0 + + while (off < len) { + +// device_printf(sc->sc_ue.ue_dev, "off=%u : len=%u\n", off, len); + + /* evaluate status header */ + usbd_copy_out(pc, 0 + , &rxhdr, sizeof(rxhdr)); + actlen -= 4; + off += (sizeof(rxhdr) + ETHER_ALIGN); + rxhdr = le32toh(rxhdr); + +// device_printf(sc->sc_ue.ue_dev, "RX header=0x%08x\n", rxhdr); + + /* Get the packet length */ + pktlen = (uint16_t)((rxhdr & RX_STS_FL_) >> 16); + actlen = min(actlen, pktlen); + len -= ETHER_CRC_LEN; + + /* Check for any errors */ + if (rxhdr & RX_STS_ES_ || + pktlen < ETHER_HDR_LEN) { + device_printf(sc->sc_ue.ue_dev, "Error header=0x%08x\n", rxhdr); + ifp->if_ierrors++; + } else { + + uether_rxbuf(ue, pc, sizeof(rxhdr), actlen); + printf("packet length %d %d\n",actlen, pktlen); +#if 0 + /* Check if the ethernet frame is too big or too small */ + if (pktlen < ETHER_HDR_LEN || pktlen > (MCLBYTES - ETHER_ALIGN)) + goto tr_setup; + + /* Create a new mbuf to store the packet in */ + m = uether_newbuf(); + if (m == NULL) { + device_printf(sc->sc_ue.ue_dev, "Failed to create new mbuf\n"); + ifp->if_iqdrops++; + goto tr_setup; + } + + /* Copy the packet data into the mbuf */ + usbd_copy_out(pc, off, mtod(m, uint8_t *), pktlen); + + /* Check if RX TCP/UDP checksumming is being offloaded */ + if ((ifp->if_capenable & IFCAP_RXCSUM) != 0) { + device_printf(sc->sc_ue.ue_dev, "RX checksum offloaded\n"); + + /* Indicate the IP/UDP/TCP header has been validated */ + m->m_pkthdr.csum_flags = CSUM_IP_CHECKED | CSUM_IP_VALID | + CSUM_DATA_VALID | CSUM_PSEUDO_HDR; + + /* Copy the TCP/UDP checksum from the last 2 bytes of the + * transfer and put in the csum_data field. + */ + usbd_copy_out(pc, (off + pktlen), + &m->m_pkthdr.csum_data, 2); + + /* Update the offset for the extra 2 bytes of the csum */ + off += 2; + } + + /* */ + if (0) { + uint8_t buf[64]; + unsigned int i; + printf("[BRG] RECV : pktlen = %u : ", pktlen); + usbd_copy_out(pc, off, buf, min(64, pktlen)); + for (i = 0; i < min(64, pktlen); i++) { + if (i % 16 == 0) + printf("\n"); + printf("0x%02x ", buf[i]); + } + printf("\n"); + } + + /* Finally enqueue the mbuf on the receive queue */ + uether_rxmbuf(ue, m, pktlen); + + /* Read the packet skipping the 4 byte header */ + //uether_rxbuf(ue, pc, sizeof(rxhdr) + ETHER_ALIGN, pktlen); +#endif + } + break; + + /* Update the offset to move to the next potential packet */ + off += pktlen; + + /* Take account of packet alignment */ + off += (4 - ((pktlen + ETHER_ALIGN) % 4)) % 4; + } +#endif + + /* FALLTHROUGH */ + + case USB_ST_SETUP: +tr_setup: + usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); + usbd_transfer_submit(xfer); + uether_rxflush(ue); + return; + + default: + device_printf(sc->sc_ue.ue_dev, "bulk read error, %s", usbd_errstr(error)); + if (error != USB_ERR_CANCELLED) { + usbd_xfer_set_stall(xfer); + goto tr_setup; + } + device_printf(sc->sc_ue.ue_dev, "start rx %i", usbd_xfer_max_len(xfer)); + return; + } +} + + + +/** + * smsc_calc_sw_csum - Calculate a UDP or TCP software checksum manually + * @xfer: the USB transfer + * @error: + * + * This is needed as a workaround for small packets if H/W checksumming is + * enabled. + * + * RETURNS: + * Returns 0 on success or a negative error code. + */ +static int +smsc_calc_sw_csum(struct smsc_softc *sc, struct mbuf *m) +{ +#if 0 + unsigned int offset; + + /* Get a pointer to the Ethernet header */ + offset = sizeof(struct ether_header); + m = m_pullup(m, offset); + if (m == NULL) + return (-ENOBUFS); + eh = mtod(m, struct ether_header*); + + /* Check if hardware VLAN insertion is off. */ + if (eh->ether_type == htons(ETHERTYPE_VLAN)) { + offset = sizeof(struct ether_vlan_header); + m = m_pullup(m, offset); + if (m == NULL) + return (-ENOBUFS); + } + + /* Get a pointer to the IP header */ + m = m_pullup(m, offset + sizeof(struct ip)); + if (m == NULL) + return (-ENOBUFS); + ip = (struct ip *)(mtod(m, uint8_t*) + offset); + offset += (ip->ip_hl << 2); + + /* Process the TCP and UDP headers */ + if (m->m_pkthdr.csum_flags & CSUM_TCP) { + m = m_pullup(m, offset + sizeof(struct tcphdr)); + if (m == NULL) + return (-ENOBUFS); + + *(uint16_t *)(mtod(m uint8_t*) + offset + m->m_pkthdr.csum_data) = + in_cksum_skip(m, m->m_pkthdr.len, offset); + m->m_pkthdr.csum_flags &= ~CSUM_TCP; + + } else { + m = m_pullup(m, offset + sizeof(struct udphdr)); + if (m == NULL) + return (-ENOBUFS); + + *(uint16_t *)(mtod(m uint8_t*) + offset + m->m_pkthdr.csum_data) = + in_cksum_skip(m, m->m_pkthdr.len, offset); + m->m_pkthdr.csum_flags &= ~CSUM_UDP; + } +#endif + return (0); +} + + +/** + * smsc_calc_hw_csum_prefix - Calculate the 32-bit prefix for TX HW checksum + * @xfer: the USB transfer + * @error: + * + * This is needed as a workaround for small packets if H/W checksumming is + * enabled. + * + * RETURNS: + * Returns 0 on success or a negative error code. + */ +static int +smsc_calc_hw_csum_prefix(struct smsc_softc *sc, struct mbuf *m, + uint32_t *csum_prefix) +{ + + + return (0); +} + + +/** + * smsc_bulk_write_callback - Write callback used to send ethernet frame + * @xfer: the USB transfer + * @error: + * + * + * + * RETURNS: + * Returns 0 on success or a negative error code. + */ +static void +smsc_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct smsc_softc *sc = usbd_xfer_softc(xfer); + struct ifnet *ifp = uether_getifp(&sc->sc_ue); + struct usb_page_cache *pc; + struct mbuf *m; + uint32_t txhdr; + uint32_t frm_len = 0; + uint32_t csum_prefix = 0; + +// printf("smsc_bulk_write_callback : USB_GET_STATE = %d\n", USB_GET_STATE(xfer)); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: +// device_printf(sc->sc_ue.ue_dev, "transfer complete\n"); + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + /* FALLTHROUGH */ + + case USB_ST_SETUP: +tr_setup: + if ((sc->sc_flags & SMSC_FLAG_LINK) == 0 || + (ifp->if_drv_flags & IFF_DRV_OACTIVE) != 0) { + /* Don't send anything if there is no link or controller is + * busy. + */ + return; + } + + /* Pull the frame of the queue */ + IFQ_DRV_DEQUEUE(&ifp->if_snd, m); + if (m == NULL) + return; + + /* Get the frame so we copy in the header and frame data */ + pc = usbd_xfer_get_frame(xfer, 0); + +// device_printf(sc->sc_ue.ue_dev, "m->m_pkthdr.len = %d\n", m->m_pkthdr.len); + + /* Check if we can use the H/W checksumming */ + if ((ifp->if_capenable & IFCAP_TXCSUM) && + ((m->m_pkthdr.csum_flags & (CSUM_TCP | CSUM_UDP)) != 0)) { + device_printf(sc->sc_ue.ue_dev, "using H/W checksumming\n"); + + if (m->m_pkthdr.len <= 45) { + /* Apparently HW TX checksumming doesn't work on small pkts */ + if (smsc_calc_sw_csum(sc, m) != 0) { + ifp->if_oerrors++; + m_freem(m); + return; + } + } else { + /* If H/W checksumming is enabled so we need to create a pkt + * preamble that contains the start and end offsets of the + * packet data to calculate the check sum over. + */ + if (smsc_calc_hw_csum_prefix(sc, m, &csum_prefix) != 0) { + ifp->if_oerrors++; + m_freem(m); + return; + } + + /* Copy the prefix into the USB buffer and update the len */ + usbd_copy_in(pc, 8, &csum_prefix, sizeof(csum_prefix)); + frm_len += sizeof(csum_prefix); + } + } + + /* Each frame is prefixed with two 32-bit values describing the + * length and checksum offloading request. + */ + txhdr = m->m_pkthdr.len | TX_CMD_A_FIRST_SEG_ | TX_CMD_A_LAST_SEG_; + txhdr = htole32(txhdr); + usbd_copy_in(pc, 0, &txhdr, sizeof(txhdr)); + + txhdr = m->m_pkthdr.len; + if (csum_prefix) + txhdr |= TX_CMD_B_CSUM_ENABLE; + txhdr = htole32(txhdr); + usbd_copy_in(pc, 4, &txhdr, sizeof(txhdr)); + + frm_len += 8; + + + /* Next copy in the actual packet */ + usbd_m_copy_in(pc, frm_len, m, 0, m->m_pkthdr.len); + frm_len += m->m_pkthdr.len; + + /* Set the length of the transfer including the header */ + usbd_xfer_set_frame_len(xfer, 0, frm_len); + + /* Update the number of packets sent */ + ifp->if_opackets++; + + /* If there's a BPF listener, bounce a copy of this frame to him */ + BPF_MTAP(ifp, m); + + m_freem(m); + + usbd_transfer_submit(xfer); + return; + + default: + device_printf(sc->sc_ue.ue_dev, "usb error on tx: %s\n", usbd_errstr(error)); + ifp->if_oerrors++; + if (error != USB_ERR_CANCELLED) { + usbd_xfer_set_stall(xfer); + goto tr_setup; + } + return; + } + + +} + + +/** + * smsc_tick - Called periodically to monitor the start of the LAN95xx chip + * @ue: USB ether interface + * + * + * + * RETURNS: + * Nothing + */ +static void +smsc_tick(struct usb_ether *ue) +{ + struct smsc_softc *sc = uether_getsc(ue); + struct mii_data *mii = uether_getmii(&sc->sc_ue);; + + // printf("%s : entry\n", __func__); + + SMSC_LOCK_ASSERT(sc, MA_OWNED); + + mii_tick(mii); + if ((sc->sc_flags & SMSC_FLAG_LINK) == 0) { + smsc_miibus_statchg(ue->ue_dev); + if ((sc->sc_flags & SMSC_FLAG_LINK) != 0) + smsc_start(ue); + } +} + +/** + * smsc_start - Starts communication with the LAN95xx chip + * @ue: USB ether interface + * + * + * + * RETURNS: + * Nothing + */ +static void +smsc_start(struct usb_ether *ue) +{ + struct smsc_softc *sc = uether_getsc(ue); + +// printf("%s : entry\n", __func__); + + /* + * start the USB transfers, if not already started: + */ + usbd_transfer_start(sc->sc_xfer[SMSC_BULK_DT_RD]); + usbd_transfer_start(sc->sc_xfer[SMSC_BULK_DT_WR]); + usbd_transfer_start(sc->sc_xfer[SMSC_INTR_DT_RD]); +} + +/** + * smsc_stop - Stops communication with the LAN95xx chip + * @ue: USB ether interface + * + * + * + * RETURNS: + * Nothing + */ +static void +smsc_stop(struct usb_ether *ue) +{ + struct smsc_softc *sc = uether_getsc(ue); + struct ifnet *ifp = uether_getifp(ue); + +// printf("%s : entry\n", __func__); + + SMSC_LOCK_ASSERT(sc, MA_OWNED); + + ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); + sc->sc_flags &= ~SMSC_FLAG_LINK; + + /* + * stop all the transfers, if not already stopped: + */ + usbd_transfer_stop(sc->sc_xfer[SMSC_BULK_DT_WR]); + usbd_transfer_stop(sc->sc_xfer[SMSC_BULK_DT_RD]); + usbd_transfer_stop(sc->sc_xfer[SMSC_INTR_DT_RD]); +} + + + +/** + * smsc_start_tx_path - Starts the transmit path + * @sc: driver soft context + * + * + * + * RETURNS: + * Nothing + */ +static void +smsc_start_tx_path(struct smsc_softc *sc) +{ + SMSC_LOCK_ASSERT(sc, MA_OWNED); + + /* Enable Tx at MAC */ + sc->sc_mac_cr |= MAC_CR_TXEN_; + smsc_write_reg(sc, MAC_CR, sc->sc_mac_cr); + + /* Enable Tx at SCSRs */ + smsc_write_reg(sc, TX_CFG, TX_CFG_ON_); +} + +/** + * smsc_start_rx_path - Starts the receive path + * @sc: driver soft context + * + * + * + * RETURNS: + * Nothing + */ +static void +smsc_start_rx_path(struct smsc_softc *sc) +{ + SMSC_LOCK_ASSERT(sc, MA_OWNED); + + sc->sc_mac_cr |= MAC_CR_RXEN_; + smsc_write_reg(sc, MAC_CR, sc->sc_mac_cr); +} + + + + +/** + * smsc_phy_initialize - Initialises the in-built SMSC phy + * @sc: driver soft context + * + * + * + * RETURNS: + * Returns 0 on success or a negative error code. + */ +static int +smsc_phy_initialize(struct smsc_softc *sc) +{ + int bmcr, timeout = 0; + + SMSC_LOCK_ASSERT(sc, MA_OWNED); + + /* Reset phy and wait for reset to complete */ + smsc_miibus_writereg(sc->sc_ue.ue_dev, sc->sc_phyno, MII_BMCR, BMCR_RESET); + + do { + uether_pause(&sc->sc_ue, hz / 100); + bmcr = smsc_miibus_readreg(sc->sc_ue.ue_dev, sc->sc_phyno, MII_BMCR); + timeout++; + } while ((bmcr & MII_BMCR) && (timeout < 100)); + + if (timeout >= 100) { + device_printf(sc->sc_ue.ue_dev, "Error: timeout on PHY Reset"); + return -EIO; + } + + smsc_miibus_writereg(sc->sc_ue.ue_dev, sc->sc_phyno, MII_ANAR, + ANAR_10 | ANAR_10_FD | ANAR_TX | ANAR_TX_FD | /* all modes */ + ANAR_CSMA | + ANAR_FC | + ANAR_PAUSE_ASYM); + + /* Read to clear */ + smsc_miibus_readreg(sc->sc_ue.ue_dev, sc->sc_phyno, PHY_INT_SRC); + + /* Set the default PHY interrupt mask */ + smsc_miibus_writereg(sc->sc_ue.ue_dev, sc->sc_phyno, PHY_INT_MASK, + PHY_INT_MASK_DEFAULT_); + + /* Restart auto-negotation */ + bmcr = smsc_miibus_readreg(sc->sc_ue.ue_dev, sc->sc_phyno, MII_BMCR); + bmcr |= BMCR_STARTNEG; + smsc_miibus_writereg(sc->sc_ue.ue_dev, sc->sc_phyno, MII_BMCR, bmcr); + + device_printf(sc->sc_ue.ue_dev, "phy initialised successfully\n"); + return 0; +} + + +/** + * smsc_chip_init - Initialises the chip after power on + * @sc: driver soft context + * + * + * + * RETURNS: + * Returns 0 on success or a negative error code. + */ +static int +smsc_chip_init(struct smsc_softc *sc) +{ + int err; + unsigned int timeout; + uint32_t reg_val; + int burst_cap; + + + device_printf(sc->sc_ue.ue_dev, "entering smsc_chip_init\n"); + + + /* Enter H/W config mode */ + if ((err = smsc_write_reg(sc, HW_CFG, HW_CFG_LRST_)) < 0) { + device_printf(sc->sc_ue.ue_dev, "Failed to write HW_CFG_LRST_ bit in " + "HW_CFG register, err = %d\n", err); + goto reset_failed; + } + + /* Loop until entered H/W config mode */ + timeout = 0; + do { + + if ((err = smsc_read_reg(sc, HW_CFG, ®_val)) != 0) { + device_printf(sc->sc_ue.ue_dev, "Failed to read HW_CFG: %d\n", err); + goto reset_failed; + } + + uether_pause(&sc->sc_ue, hz / 100); + timeout++; + + } while ((reg_val & HW_CFG_LRST_) && (timeout < 100)); + + if (timeout >= 100) { + device_printf(sc->sc_ue.ue_dev, "Timed-out waiting for completion of Lite Reset\n"); + goto reset_failed; + } + + + + /* Reset the PHY */ + if ((err = smsc_write_reg(sc, PM_CTRL, PM_CTL_PHY_RST_)) != 0) { + device_printf(sc->sc_ue.ue_dev, "Failed to write PM_CTRL: %d\n", err); + goto reset_failed; + } + + timeout = 0; + do { + + if ((err = smsc_read_reg(sc, PM_CTRL, ®_val)) != 0) { + device_printf(sc->sc_ue.ue_dev, "Failed to read PM_CTRL: %d\n", err); + return err; + } + + uether_pause(&sc->sc_ue, hz / 100); + timeout++; + + } while ((reg_val & PM_CTL_PHY_RST_) && (timeout < 100)); + + if (timeout >= 100) { + device_printf(sc->sc_ue.ue_dev, "timeout waiting for PHY Reset\n"); + goto reset_failed; + } + + +#if 0 + /* Set the mac address */ + if ((err = smsc_set_mac_address(sc, dev)) != 0) { + device_printf(sc->sc_dev, "timeout waiting for PHY Reset\n"); + goto reset_failed; + } + + device_printf(sc->sc_dev, "MAC Address: %pM\n", dev->net->dev_addr); +#endif + + + /* Don't know what the HW_CFG_BIR_ bit is, but following the reset sequence + * as used in the Linux driver. + */ + if ((err = smsc_read_reg(sc, HW_CFG, ®_val)) != 0) { + device_printf(sc->sc_ue.ue_dev, "Failed to read HW_CFG: %d\n", err); + goto reset_failed; + } + + device_printf(sc->sc_ue.ue_dev, "Read Value from HW_CFG : 0x%08x\n", reg_val); + + reg_val |= HW_CFG_BIR_; + + if ((err = smsc_write_reg(sc, HW_CFG, reg_val)) != 0) { + device_printf(sc->sc_ue.ue_dev, "Failed to write HW_CFG_BIR_ bit in HW_CFG register, ret = %d\n", + err); + goto reset_failed; + } + + if ((err = smsc_read_reg(sc, HW_CFG, ®_val)) != 0) { + device_printf(sc->sc_ue.ue_dev, "Failed to read HW_CFG: %d\n", err); + goto reset_failed; + } + device_printf(sc->sc_ue.ue_dev, "Read Value from HW_CFG after writing HW_CFG_BIR_: 0x%08x\n", + reg_val); + + + /* There is a so called 'turbo mode' that the linux driver supports, it + * seems to allow you to jam multiple frames per Rx transaction. I support + * fo this isn't enabed in this driver yet as still unsure of the layout + * of the packets. + * + * So since we don't (yet?) support this mode the burst_cap(ability) is set + * to zero and the maximum URB sze is 2048. + */ + if (usbd_get_speed(sc->sc_ue.ue_udev) == USB_SPEED_HIGH) + burst_cap = 32; + else + burst_cap = 128; + + + /* Write the burst capability - number of ether frames that can be in a + * single RX transaction. + */ + if ((err = smsc_write_reg(sc, BURST_CAP, burst_cap)) < 0) { + device_printf(sc->sc_ue.ue_dev, "Failed to write BURST_CAP: %d\n", err); + goto reset_failed; + } + if ((err = smsc_read_reg(sc, BURST_CAP, ®_val)) < 0) { + device_printf(sc->sc_ue.ue_dev, "Failed to read BURST_CAP: %d\n", err); + goto reset_failed; + } + device_printf(sc->sc_ue.ue_dev, "Read Value from BURST_CAP after writing: 0x%08x\n", + reg_val); + + + /* Set the default bulk in delay */ + if ((err = smsc_write_reg(sc, BULK_IN_DLY, 0x00002000)) < 0) { + device_printf(sc->sc_ue.ue_dev, "ret = %d\n", err); + goto reset_failed; + } + if ((err = smsc_read_reg(sc, BULK_IN_DLY, ®_val)) < 0) { + device_printf(sc->sc_ue.ue_dev, "Failed to read BULK_IN_DLY: %d\n", err); + goto reset_failed; + } + device_printf(sc->sc_ue.ue_dev, "Read Value from BULK_IN_DLY after writing: 0x%08x\n", + reg_val); + + + /* Initialise the RX interface */ + err = smsc_read_reg(sc, HW_CFG, ®_val); + if (err < 0) { + device_printf(sc->sc_ue.ue_dev, "Failed to read HW_CFG: %d\n", err); + goto reset_failed; + } + device_printf(sc->sc_ue.ue_dev, "Read Value from HW_CFG: 0x%08x\n", reg_val); + + reg_val &= ~HW_CFG_RXDOFF_; + + /* Set Rx data offset=2, Make IP header aligns on word boundary. */ + reg_val |= ETHER_ALIGN << 9; + + if ((err = smsc_write_reg(sc, HW_CFG, reg_val)) < 0) { + device_printf(sc->sc_ue.ue_dev, "Failed to write HW_CFG register, ret=%d\n", + err); + goto reset_failed; + } + err = smsc_read_reg(sc, HW_CFG, ®_val); + if (err < 0) { + device_printf(sc->sc_ue.ue_dev, "Failed to read HW_CFG: %d\n", err); + goto reset_failed; + } + device_printf(sc->sc_ue.ue_dev, "Read Value from HW_CFG after writing: 0x%08x\n", reg_val); + + /* Clear the status register ? */ + err = smsc_write_reg(sc, INT_STS, 0xFFFFFFFF); + if (err < 0) { + device_printf(sc->sc_ue.ue_dev, "Failed to write INT_STS register, ret=%d\n", + err); + goto reset_failed; + } + + + /* Read and display the revision register */ + err = smsc_read_reg(sc, ID_REV, ®_val); + if (err < 0) { + device_printf(sc->sc_ue.ue_dev, "Failed to read ID_REV: %d\n", err); + goto reset_failed; + } + device_printf(sc->sc_ue.ue_dev, "ID_REV = 0x%08x\n", reg_val); + + + + + /* Configure GPIO pins as LED outputs */ + reg_val = LED_GPIO_CFG_SPD_LED | LED_GPIO_CFG_LNK_LED | LED_GPIO_CFG_FDX_LED; + err = smsc_write_reg(sc, LED_GPIO_CFG, reg_val); + if (err < 0) { + device_printf(sc->sc_ue.ue_dev, "Failed to write LED_GPIO_CFG register, ret=%d\n", + err); + goto reset_failed; + } + + + + /* Init Tx */ + err = smsc_write_reg(sc, FLOW, 0); + if (err < 0) { + device_printf(sc->sc_ue.ue_dev, "Failed to write FLOW: %d\n", err); + goto reset_failed; + } + + err = smsc_write_reg(sc, AFC_CFG, AFC_CFG_DEFAULT); + if (err < 0) { + device_printf(sc->sc_ue.ue_dev, "Failed to write AFC_CFG: %d\n", err); + goto reset_failed; + } + + /* Read the current MAC configuration */ + err = smsc_read_reg(sc, MAC_CR, &sc->sc_mac_cr); + if (err < 0) { + device_printf(sc->sc_ue.ue_dev, "Failed to read MAC_CR: %d\n", err); + goto reset_failed; + } + + /* Init Rx */ + /* Set Vlan */ + err = smsc_write_reg(sc, VLAN1, (uint32_t)ETHERTYPE_VLAN); + if (err < 0) { + device_printf(sc->sc_ue.ue_dev, "Failed to write VAN1: %d\n", err); + goto reset_failed; + } + +#if 0 + /* Enable or disable checksum offload engines */ + ethtool_op_set_tx_hw_csum(netdev, pdata->use_tx_csum); + err = smsc95xx_set_csums(dev); + if (err < 0) { + device_printf(sc->sc_ue.ue_dev, "Failed to set csum offload: %d\n", err); + goto reset_failed; + } +#endif + + /* Initialise the PHY */ + if (smsc_phy_initialize(sc) < 0) { + err = -EIO; + goto reset_failed; + } + + err = smsc_read_reg(sc, INT_EP_CTL, ®_val); + if (err < 0) { + device_printf(sc->sc_ue.ue_dev, "Failed to read INT_EP_CTL: %d\n", err); + goto reset_failed; + } + + /* enable PHY interrupts */ + reg_val |= INT_EP_CTL_PHY_INT_; + + err = smsc_write_reg(sc, INT_EP_CTL, reg_val); + if (err < 0) { + device_printf(sc->sc_ue.ue_dev, "Failed to write INT_EP_CTL: %d\n", err); + goto reset_failed; + } + + smsc_start_tx_path(sc); + smsc_start_rx_path(sc); + + + device_printf(sc->sc_ue.ue_dev, "smsc_reset, return 0\n"); + return (0); + +reset_failed: + + device_printf(sc->sc_ue.ue_dev, "smsc_reset, failed ret %d\n", err); + return (err); +} + + + + + + +/** + * smsc_attach_post - Called after the driver attached to the USB interface + * @ue: the USB ethernet device + * + * This is where the chip is intialised for the first time. This is different + * from the smsc_init() function in that that one is designed to setup the + * H/W to match the UE settings and can be called after a reset. + * + * + * RETURNS: + * Returns 0 on success or a negative error code. + */ +static void +smsc_attach_post(struct usb_ether *ue) +{ + struct smsc_softc *sc = uether_getsc(ue); + uint32_t mac_h, mac_l; + int err; + + printf("%s : %d : \n", __func__, __LINE__); + + /* Setup some of the basics */ + sc->sc_phyno = 1; + + /* Initialise the chip for the first time */ + smsc_chip_init(sc); + + + /* Attempt to get the mac address, if the an EEPROM is not attached this + * will just return FF:FF:FF:FF:FF:FF, so in such cases we invent a MAC + * address based on the tick time. + * + * TODO: add a hint so you can set the mac + */ + if ((err = smsc_read_reg(sc, ADDRL, &mac_l)) == 0) + err = smsc_read_reg(sc, ADDRH, &mac_h); + + device_printf(sc->sc_ue.ue_dev, "mac_h = 0x%08x : mac_l = 0x%08x\n", mac_h, mac_l); + + /* Create a temporary fake mac address */ + if ((err != 0) || (mac_h == 0x0000ffff) || (mac_l == 0xffffffff)) { + device_printf(sc->sc_ue.ue_dev, "faking MAC address\n"); + + /* TODO: currently using a TI mac address, couldn't find a SMSC one */ + sc->sc_ue.ue_eaddr[0] = 0x00; + sc->sc_ue.ue_eaddr[1] = 0x2a; + //memcpy(&sc->sc_ue.ue_eaddr[2], &ticks, 4); + sc->sc_ue.ue_eaddr[4] = 0x12; + sc->sc_ue.ue_eaddr[5] = 0x34; + } else { + sc->sc_ue.ue_eaddr[5] = (uint8_t)((mac_h >> 8) & 0xff); + sc->sc_ue.ue_eaddr[4] = (uint8_t)((mac_h) & 0xff); + sc->sc_ue.ue_eaddr[3] = (uint8_t)((mac_l >> 24) & 0xff); + sc->sc_ue.ue_eaddr[2] = (uint8_t)((mac_l >> 16) & 0xff); + sc->sc_ue.ue_eaddr[1] = (uint8_t)((mac_l >> 8) & 0xff); + sc->sc_ue.ue_eaddr[0] = (uint8_t)((mac_l) & 0xff); + } + + device_printf(sc->sc_ue.ue_dev, + "MAC address: %02x:%02x:%02x:%02x:%02x:%02x:\n", + sc->sc_ue.ue_eaddr[0], sc->sc_ue.ue_eaddr[1], + sc->sc_ue.ue_eaddr[2], sc->sc_ue.ue_eaddr[3], + sc->sc_ue.ue_eaddr[4], sc->sc_ue.ue_eaddr[5]); +} + + +/** + * smsc_probe - Probe the interface. + * @dev: smsc device handle + * + * Allocate softc structures, do ifmedia setup and ethernet/BPF attach. + * + * RETURNS: + * Returns 0 on success or a negative error code. + */ +static int +smsc_probe(device_t dev) +{ + struct usb_attach_arg *uaa = device_get_ivars(dev); + struct usb_endpoint *ep = uaa->device->endpoints; + struct usb_endpoint *end = ep + uaa->device->endpoints_max; + + printf("%s : %d : \n", __func__, __LINE__); + + if (uaa->usb_mode != USB_MODE_HOST) + return (ENXIO); + if (uaa->info.bConfigIndex != SMSC_CONFIG_INDEX) + return (ENXIO); + if (uaa->info.bIfaceIndex != SMSC_IFACE_IDX) + return (ENXIO); + printf("WE HAVE %d\n", uaa->device->endpoints_max); + for (; ep != end; ep++) { + printf("got %x\n", ep->edesc->bmAttributes); + } + + return (usbd_lookup_id_by_uaa(smsc_devs, sizeof(smsc_devs), uaa)); +} + + +/** + * smsc_attach - Attach the interface. + * @dev: smsc device handle + * + * Allocate softc structures, do ifmedia setup and ethernet/BPF attach. + * + * RETURNS: + * Returns 0 on success or a negative error code. + */ +static int +smsc_attach(device_t dev) +{ + struct usb_attach_arg *uaa = device_get_ivars(dev); + struct smsc_softc *sc = device_get_softc(dev); + struct usb_ether *ue = &sc->sc_ue; + uint8_t iface_index; + int err; + + printf("%s : %d : \n", __func__, __LINE__); + + sc->sc_flags = USB_GET_DRIVER_INFO(uaa); + + device_set_usb_desc(dev); + + mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); + + /* Setup the endpoints for the SMSC LAN95xx devices */ + iface_index = SMSC_IFACE_IDX; + err = usbd_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, + smsc_config, SMSC_N_TRANSFER, sc, &sc->sc_mtx); + if (err) { + device_printf(dev, "allocating USB transfers failed\n"); + goto detach; + } + + ue->ue_sc = sc; + ue->ue_dev = dev; + ue->ue_udev = uaa->device; + ue->ue_mtx = &sc->sc_mtx; + ue->ue_methods = &smsc_ue_methods; + + err = uether_ifattach(ue); + if (err) { + device_printf(dev, "could not attach interface\n"); + goto detach; + } + return (0); /* success */ + +detach: + smsc_detach(dev); + return (ENXIO); /* failure */ +} + +/** + * smsc_detach - Detach the interface. + * @dev: smsc device handle + * + * + * + * RETURNS: + * Returns 0 on success or a negative error code. + */ +static int +smsc_detach(device_t dev) +{ + struct smsc_softc *sc = device_get_softc(dev); + struct usb_ether *ue = &sc->sc_ue; + + usbd_transfer_unsetup(sc->sc_xfer, SMSC_N_TRANSFER); + uether_ifdetach(ue); + mtx_destroy(&sc->sc_mtx); + + return (0); +} + + +static device_method_t smsc_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, smsc_probe), + DEVMETHOD(device_attach, smsc_attach), + DEVMETHOD(device_detach, smsc_detach), + + /* bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_driver_added, bus_generic_driver_added), + + /* MII interface */ + DEVMETHOD(miibus_readreg, smsc_miibus_readreg), + DEVMETHOD(miibus_writereg, smsc_miibus_writereg), + DEVMETHOD(miibus_statchg, smsc_miibus_statchg), + + {0, 0} +}; + +static driver_t smsc_driver = { + .name = "smsc", + .methods = smsc_methods, + .size = sizeof(struct smsc_softc), +}; + +static devclass_t smsc_devclass; + +DRIVER_MODULE(smsc, uhub, smsc_driver, smsc_devclass, NULL, 0); +DRIVER_MODULE(miibus, smsc, miibus_driver, miibus_devclass, 0, 0); +MODULE_DEPEND(smsc, uether, 1, 1, 1); +MODULE_DEPEND(smsc, usb, 1, 1, 1); +MODULE_DEPEND(smsc, ether, 1, 1, 1); +MODULE_DEPEND(smsc, miibus, 1, 1, 1); +MODULE_VERSION(smsc, 1); + diff --git a/sys/dev/usb/net/if_smscreg.h b/sys/dev/usb/net/if_smscreg.h new file mode 100644 index 0000000..d707adb --- /dev/null +++ b/sys/dev/usb/net/if_smscreg.h @@ -0,0 +1,294 @@ + /*************************************************************************** + * + * Copyright (C) 2007-2008 SMSC + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + *****************************************************************************/ + +#ifndef _SMSC95XX_H +#define _SMSC95XX_H + +/* Tx command words */ +#define TX_CMD_A_DATA_OFFSET_ (0x001F0000) +#define TX_CMD_A_FIRST_SEG_ (0x00002000) +#define TX_CMD_A_LAST_SEG_ (0x00001000) +#define TX_CMD_A_BUF_SIZE_ (0x000007FF) + +#define TX_CMD_B_CSUM_ENABLE (0x00004000) +#define TX_CMD_B_ADD_CRC_DISABLE_ (0x00002000) +#define TX_CMD_B_DISABLE_PADDING_ (0x00001000) +#define TX_CMD_B_PKT_BYTE_LENGTH_ (0x000007FF) + +/* Rx status word */ +#define RX_STS_FF_ (0x40000000) /* Filter Fail */ +#define RX_STS_FL_ (0x3FFF0000) /* Frame Length */ +#define RX_STS_ES_ (0x00008000) /* Error Summary */ +#define RX_STS_BF_ (0x00002000) /* Broadcast Frame */ +#define RX_STS_LE_ (0x00001000) /* Length Error */ +#define RX_STS_RF_ (0x00000800) /* Runt Frame */ +#define RX_STS_MF_ (0x00000400) /* Multicast Frame */ +#define RX_STS_TL_ (0x00000080) /* Frame too long */ +#define RX_STS_CS_ (0x00000040) /* Collision Seen */ +#define RX_STS_FT_ (0x00000020) /* Frame Type */ +#define RX_STS_RW_ (0x00000010) /* Receive Watchdog */ +#define RX_STS_ME_ (0x00000008) /* Mii Error */ +#define RX_STS_DB_ (0x00000004) /* Dribbling */ +#define RX_STS_CRC_ (0x00000002) /* CRC Error */ + +/* SCSRs */ +#define ID_REV (0x00) +#define ID_REV_CHIP_ID_MASK_ (0xFFFF0000) +#define ID_REV_CHIP_REV_MASK_ (0x0000FFFF) +#define ID_REV_CHIP_ID_9500_ (0x9500) + +#define INT_STS (0x08) +#define INT_STS_TX_STOP_ (0x00020000) +#define INT_STS_RX_STOP_ (0x00010000) +#define INT_STS_PHY_INT_ (0x00008000) +#define INT_STS_TXE_ (0x00004000) +#define INT_STS_TDFU_ (0x00002000) +#define INT_STS_TDFO_ (0x00001000) +#define INT_STS_RXDF_ (0x00000800) +#define INT_STS_GPIOS_ (0x000007FF) + +#define RX_CFG (0x0C) +#define RX_FIFO_FLUSH_ (0x00000001) + +#define TX_CFG (0x10) +#define TX_CFG_ON_ (0x00000004) +#define TX_CFG_STOP_ (0x00000002) +#define TX_CFG_FIFO_FLUSH_ (0x00000001) + +#define HW_CFG (0x14) +#define HW_CFG_BIR_ (0x00001000) +#define HW_CFG_LEDB_ (0x00000800) +#define HW_CFG_RXDOFF_ (0x00000600) +#define HW_CFG_DRP_ (0x00000040) +#define HW_CFG_MEF_ (0x00000020) +#define HW_CFG_LRST_ (0x00000008) +#define HW_CFG_PSEL_ (0x00000004) +#define HW_CFG_BCE_ (0x00000002) +#define HW_CFG_SRST_ (0x00000001) + +#define PM_CTRL (0x20) +#define PM_CTL_DEV_RDY_ (0x00000080) +#define PM_CTL_SUS_MODE_ (0x00000060) +#define PM_CTL_SUS_MODE_0 (0x00000000) +#define PM_CTL_SUS_MODE_1 (0x00000020) +#define PM_CTL_SUS_MODE_2 (0x00000060) +#define PM_CTL_PHY_RST_ (0x00000010) +#define PM_CTL_WOL_EN_ (0x00000008) +#define PM_CTL_ED_EN_ (0x00000004) +#define PM_CTL_WUPS_ (0x00000003) +#define PM_CTL_WUPS_NO_ (0x00000000) +#define PM_CTL_WUPS_ED_ (0x00000001) +#define PM_CTL_WUPS_WOL_ (0x00000002) +#define PM_CTL_WUPS_MULTI_ (0x00000003) + +#define LED_GPIO_CFG (0x24) +#define LED_GPIO_CFG_SPD_LED (0x01000000) +#define LED_GPIO_CFG_LNK_LED (0x00100000) +#define LED_GPIO_CFG_FDX_LED (0x00010000) + +#define GPIO_CFG (0x28) + +#define AFC_CFG (0x2C) + +/* Hi watermark = 15.5Kb (~10 mtu pkts) */ +/* low watermark = 3k (~2 mtu pkts) */ +/* backpressure duration = ~ 350us */ +/* Apply FC on any frame. */ +#define AFC_CFG_DEFAULT (0x00F830A1) + +#define E2P_CMD (0x30) +#define E2P_CMD_BUSY_ (0x80000000) +#define E2P_CMD_MASK_ (0x70000000) +#define E2P_CMD_READ_ (0x00000000) +#define E2P_CMD_EWDS_ (0x10000000) +#define E2P_CMD_EWEN_ (0x20000000) +#define E2P_CMD_WRITE_ (0x30000000) +#define E2P_CMD_WRAL_ (0x40000000) +#define E2P_CMD_ERASE_ (0x50000000) +#define E2P_CMD_ERAL_ (0x60000000) +#define E2P_CMD_RELOAD_ (0x70000000) +#define E2P_CMD_TIMEOUT_ (0x00000400) +#define E2P_CMD_LOADED_ (0x00000200) +#define E2P_CMD_ADDR_ (0x000001FF) + +#define MAX_EEPROM_SIZE (512) + +#define E2P_DATA (0x34) +#define E2P_DATA_MASK_ (0x000000FF) + +#define BURST_CAP (0x38) + +#define GPIO_WAKE (0x64) + +#define INT_EP_CTL (0x68) +#define INT_EP_CTL_INTEP_ (0x80000000) +#define INT_EP_CTL_MACRTO_ (0x00080000) +#define INT_EP_CTL_TX_STOP_ (0x00020000) +#define INT_EP_CTL_RX_STOP_ (0x00010000) +#define INT_EP_CTL_PHY_INT_ (0x00008000) +#define INT_EP_CTL_TXE_ (0x00004000) +#define INT_EP_CTL_TDFU_ (0x00002000) +#define INT_EP_CTL_TDFO_ (0x00001000) +#define INT_EP_CTL_RXDF_ (0x00000800) +#define INT_EP_CTL_GPIOS_ (0x000007FF) + +#define BULK_IN_DLY (0x6C) + +/* MAC CSRs */ +#define MAC_CR (0x100) +#define MAC_CR_RXALL_ (0x80000000) +#define MAC_CR_RCVOWN_ (0x00800000) +#define MAC_CR_LOOPBK_ (0x00200000) +#define MAC_CR_FDPX_ (0x00100000) +#define MAC_CR_MCPAS_ (0x00080000) +#define MAC_CR_PRMS_ (0x00040000) +#define MAC_CR_INVFILT_ (0x00020000) +#define MAC_CR_PASSBAD_ (0x00010000) +#define MAC_CR_HFILT_ (0x00008000) +#define MAC_CR_HPFILT_ (0x00002000) +#define MAC_CR_LCOLL_ (0x00001000) +#define MAC_CR_BCAST_ (0x00000800) +#define MAC_CR_DISRTY_ (0x00000400) +#define MAC_CR_PADSTR_ (0x00000100) +#define MAC_CR_BOLMT_MASK (0x000000C0) +#define MAC_CR_DFCHK_ (0x00000020) +#define MAC_CR_TXEN_ (0x00000008) +#define MAC_CR_RXEN_ (0x00000004) + +#define ADDRH (0x104) + +#define ADDRL (0x108) + +#define HASHH (0x10C) + +#define HASHL (0x110) + +#define MII_ADDR (0x114) +#define MII_WRITE_ (0x02) +#define MII_BUSY_ (0x01) +#define MII_READ_ (0x00) /* ~of MII Write bit */ + +#define MII_DATA (0x118) + +#define FLOW (0x11C) +#define FLOW_FCPT_ (0xFFFF0000) +#define FLOW_FCPASS_ (0x00000004) +#define FLOW_FCEN_ (0x00000002) +#define FLOW_FCBSY_ (0x00000001) + +#define VLAN1 (0x120) + +#define VLAN2 (0x124) + +#define WUFF (0x128) + +#define WUCSR (0x12C) + +#define COE_CR (0x130) +#define Tx_COE_EN_ (0x00010000) +#define Rx_COE_MODE_ (0x00000002) +#define Rx_COE_EN_ (0x00000001) + +/* Vendor-specific PHY Definitions */ + +/* Mode Control/Status Register */ +#define PHY_MODE_CTRL_STS (17) +#define MODE_CTRL_STS_EDPWRDOWN_ ((uint16_t)0x2000) +#define MODE_CTRL_STS_ENERGYON_ ((uint16_t)0x0002) + +#define SPECIAL_CTRL_STS (27) +#define SPECIAL_CTRL_STS_OVRRD_AMDIX_ ((uint16_t)0x8000) +#define SPECIAL_CTRL_STS_AMDIX_ENABLE_ ((uint16_t)0x4000) +#define SPECIAL_CTRL_STS_AMDIX_STATE_ ((uint16_t)0x2000) + +#define PHY_INT_SRC (29) +#define PHY_INT_SRC_ENERGY_ON_ ((uint16_t)0x0080) +#define PHY_INT_SRC_ANEG_COMP_ ((uint16_t)0x0040) +#define PHY_INT_SRC_REMOTE_FAULT_ ((uint16_t)0x0020) +#define PHY_INT_SRC_LINK_DOWN_ ((uint16_t)0x0010) + +#define PHY_INT_MASK (30) +#define PHY_INT_MASK_ENERGY_ON_ ((uint16_t)0x0080) +#define PHY_INT_MASK_ANEG_COMP_ ((uint16_t)0x0040) +#define PHY_INT_MASK_REMOTE_FAULT_ ((uint16_t)0x0020) +#define PHY_INT_MASK_LINK_DOWN_ ((uint16_t)0x0010) +#define PHY_INT_MASK_DEFAULT_ (PHY_INT_MASK_ANEG_COMP_ | \ + PHY_INT_MASK_LINK_DOWN_) + +#define PHY_SPECIAL (31) +#define PHY_SPECIAL_SPD_ ((uint16_t)0x001C) +#define PHY_SPECIAL_SPD_10HALF_ ((uint16_t)0x0004) +#define PHY_SPECIAL_SPD_10FULL_ ((uint16_t)0x0014) +#define PHY_SPECIAL_SPD_100HALF_ ((uint16_t)0x0008) +#define PHY_SPECIAL_SPD_100FULL_ ((uint16_t)0x0018) + +/* USB Vendor Requests */ +#define USB_VENDOR_REQUEST_WRITE_REGISTER 0xA0 +#define USB_VENDOR_REQUEST_READ_REGISTER 0xA1 +#define USB_VENDOR_REQUEST_GET_STATS 0xA2 + +/* Interrupt Endpoint status word bitfields */ +#define INT_ENP_TX_STOP_ ((uint32_t)BIT(17)) +#define INT_ENP_RX_STOP_ ((uint32_t)BIT(16)) +#define INT_ENP_PHY_INT_ ((uint32_t)BIT(15)) +#define INT_ENP_TXE_ ((uint32_t)BIT(14)) +#define INT_ENP_TDFU_ ((uint32_t)BIT(13)) +#define INT_ENP_TDFO_ ((uint32_t)BIT(12)) +#define INT_ENP_RXDF_ ((uint32_t)BIT(11)) + + +#define SMSC_CONFIG_INDEX 0 /* config number 1 */ +#define SMSC_IFACE_IDX 0 + +/* + * From looking at the Linux SMSC logs I believe the LAN95xx devices have + * the following endpoints: + * Endpoints In 1 Out 2 Int 3 + * + */ +enum { + SMSC_BULK_DT_RD, + SMSC_BULK_DT_WR, + SMSC_INTR_DT_RD, + SMSC_N_TRANSFER, +}; + +struct smsc_softc { + struct usb_ether sc_ue; + struct mtx sc_mtx; + struct usb_xfer *sc_xfer[SMSC_N_TRANSFER]; + int sc_phyno; + + /* The following stores the settings in the mac control (MAC_CR) register */ + uint32_t sc_mac_cr; + + uint32_t sc_flags; +#define SMSC_FLAG_LINK 0x0001 +#define SMSC_FLAG_LAN9514 0x1000 /* LAN9514 */ + +}; + +#define SMSC_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) +#define SMSC_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) +#define SMSC_LOCK_ASSERT(_sc, t) mtx_assert(&(_sc)->sc_mtx, t) + + +#endif /* _SMSC95XX_H */ + diff --git a/sys/dev/usb/usbdevs b/sys/dev/usb/usbdevs index 4e3af74..026a414 100644 --- a/sys/dev/usb/usbdevs +++ b/sys/dev/usb/usbdevs @@ -3127,8 +3127,10 @@ product SMC 2206USB 0x0201 EZ Connect USB Ethernet product SMC 2862WG 0xee13 EZ Connect Wireless Adapter product SMC2 2020HUB 0x2020 USB Hub product SMC2 2514HUB 0x2514 USB Hub +product SMC2 LAN9514_ETH 0xec00 USB/Ethernet product SMC3 2662WUSB 0xa002 2662W-AR Wireless + /* SOHOware products */ product SOHOWARE NUB100 0x9100 10/100 USB Ethernet product SOHOWARE NUB110 0x9110 10/100 USB Ethernet diff --git a/sys/nfs/bootp_subr.c b/sys/nfs/bootp_subr.c index bd4a826..4f64c64 100644 --- a/sys/nfs/bootp_subr.c +++ b/sys/nfs/bootp_subr.c @@ -1604,6 +1604,7 @@ bootpc_init(void) #endif struct nfsv3_diskless *nd; struct thread *td; + int count = 0; nd = &nfsv3_diskless; td = curthread; @@ -1651,6 +1652,7 @@ bootpc_init(void) allocifctx(gctx); #endif +retry: IFNET_RLOCK(); for (ifp = TAILQ_FIRST(&V_ifnet), ifctx = gctx->interfaces; ifp != NULL && ifctx != NULL; @@ -1674,6 +1676,10 @@ bootpc_init(void) CURVNET_RESTORE(); if (gctx->interfaces == NULL || gctx->interfaces->ifp == NULL) { + if (count < 1000) { + pause("bootpc", hz / 10); + goto retry; + } #ifdef BOOTP_WIRED_TO panic("bootpc_init: Could not find interface specified " "by BOOTP_WIRED_TO: "