Index: arm/include/pte.h =================================================================== --- arm/include/pte.h (revision 230394) +++ arm/include/pte.h (working copy) @@ -223,6 +223,7 @@ #define L2_SHARED (1 << 10) #define L2_APX (1 << 9) #define L2_XN (1 << 0) +#define L2_SHARED (1 << 10) #define L2_L_TEX_MASK (0x7 << 12) /* Type Extension */ #define L2_L_TEX(x) (((x) & 0x7) << 12) #define L2_S_TEX_MASK (0x7 << 6) /* Type Extension */ Index: arm/omap/omap4/omap4_l2cache.c =================================================================== --- arm/omap/omap4/omap4_l2cache.c (revision 0) +++ arm/omap/omap4/omap4_l2cache.c (revision 0) @@ -0,0 +1,679 @@ +/*- + * 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; + +static struct omap4_softc *g_omap4_softc; + + +/** + * 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; + + g_omap4_softc = sc; + + /* 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); +} + + + + + Property changes on: arm/omap/omap4/omap4_l2cache.c ___________________________________________________________________ Added: svn:mime-type + text/plain Added: svn:keywords + FreeBSD=%H Added: svn:eol-style + native Index: arm/omap/omap4/files.omap44xx =================================================================== --- arm/omap/omap4/files.omap44xx (revision 230394) +++ arm/omap/omap4/files.omap44xx (working copy) @@ -22,10 +22,17 @@ arm/omap/omap4/omap4_if.m standard arm/omap/omap4/omap44xx.c standard arm/omap/omap4/omap4_intr.c standard +arm/omap/omap4/omap4_l2cache.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/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 + Index: arm/omap/omap4/omap4_secure.S =================================================================== --- arm/omap/omap4/omap4_secure.S (revision 0) +++ arm/omap/omap4/omap4_secure.S (revision 0) @@ -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} + Property changes on: arm/omap/omap4/omap4_secure.S ___________________________________________________________________ Added: svn:executable + * Added: svn:mime-type + text/plain Added: svn:keywords + FreeBSD=%H Added: svn:eol-style + native Index: arm/omap/omap4/uart_bus_omap3.c =================================================================== --- arm/omap/omap4/uart_bus_omap3.c (revision 0) +++ arm/omap/omap4/uart_bus_omap3.c (revision 0) @@ -0,0 +1,226 @@ +/*- + * 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 "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); + + + Property changes on: arm/omap/omap4/uart_bus_omap3.c ___________________________________________________________________ Added: svn:mime-type + text/plain Added: svn:keywords + FreeBSD=%H Added: svn:eol-style + native Index: arm/omap/omap4/omap44xx.c =================================================================== --- arm/omap/omap4/omap44xx.c (revision 230394) +++ arm/omap/omap4/omap44xx.c (working copy) @@ -154,6 +154,217 @@ { -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 - I2C + * The OMAP44xx chips contain 4 independant I2C controllers. + * Note: generally this should be the first function sub-device because + * it's used for the TWL power control device. + */ +#if 0 + { .name = "omap_i2c", + .unit = 0, + .mem = { { OMAP44XX_I2C1_HWBASE, OMAP44XX_I2C_SIZE }, + { 0, 0 } + }, + .irqs = { OMAP44XX_IRQ_I2C1, + -1, + }, + }, +#endif + + /** + * 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, + }, + }, + { .name = "omap_mmc", + .unit = 3, + .mem = { { OMAP44XX_MMCHS4_HWBASE, OMAP44XX_MMCHS_SIZE }, + { 0, 0 } + }, + .irqs = { OMAP44XX_IRQ_MMC4, + -1, + }, + }, + { .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 * @@ -282,7 +493,88 @@ bus_space_write_4(sc->sc_iotag, sc->sc_prv_timer_ioh, off, val); } +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. + */ +#if 0 + 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); +#endif + + /* 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 * @@ -308,6 +600,7 @@ bus_set_resource(self, SYS_RES_MEMORY, i, omap44xx_mem[i].base, omap44xx_mem[i].size); } + omap44xx_cpu_add_builtin_children(parent); } /** @@ -391,6 +684,10 @@ /* Setup basic timer stuff before cpu_initclocks is called */ omap4_init_timer(dev); + /* Setup the PL310 L2 cache controller */ + omap4_setup_l2cache_controller(sc); + + omap_scm_padconf_init_from_hints(dev); bus_generic_probe(dev); Index: arm/omap/omap_ehci.c =================================================================== --- arm/omap/omap_ehci.c (revision 0) +++ arm/omap/omap_ehci.c (revision 0) @@ -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); Property changes on: arm/omap/omap_ehci.c ___________________________________________________________________ Added: svn:mime-type + text/plain Added: svn:keywords + FreeBSD=%H Added: svn:eol-style + native Index: conf/files =================================================================== --- conf/files (revision 230313) +++ conf/files (working copy) @@ -1925,6 +1925,7 @@ 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 Index: dev/usb/usbdevs =================================================================== --- dev/usb/usbdevs (revision 230313) +++ dev/usb/usbdevs (working copy) @@ -3127,8 +3127,10 @@ 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 Index: dev/usb/net/if_smsc.c =================================================================== --- dev/usb/net/if_smsc.c (revision 0) +++ dev/usb/net/if_smsc.c (revision 0) @@ -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); + Property changes on: dev/usb/net/if_smsc.c ___________________________________________________________________ Added: svn:mime-type + text/plain Added: svn:keywords + FreeBSD=%H Added: svn:eol-style + native Index: dev/usb/net/if_smscreg.h =================================================================== --- dev/usb/net/if_smscreg.h (revision 0) +++ dev/usb/net/if_smscreg.h (revision 0) @@ -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 */ + Property changes on: dev/usb/net/if_smscreg.h ___________________________________________________________________ Added: svn:mime-type + text/plain Added: svn:keywords + FreeBSD=%H Added: svn:eol-style + native