From 69436c4cabfac51fcdfa42784059f5ae68801cd4 Mon Sep 17 00:00:00 2001 From: rpaulo Date: Mon, 4 Jan 2010 03:35:45 +0000 Subject: Add support for Cavium Econa CNS11XX ARM boards. These boards were previously know by StarSemi STR9104. Tested by the submitter on an Emprex NSD-100 board. Submitted by: Yohanes Nugroho Reviewed by: freebsd-arm, stas Obtained from: //depot/projects/str91xx/... --- sys/arm/arm/cpufunc.c | 140 ++- sys/arm/arm/cpufunc_asm_fa526.S | 209 +++++ sys/arm/arm/elf_trampoline.c | 2 + sys/arm/conf/CNS11XXNAS | 126 +++ sys/arm/conf/CNS11XXNAS.hints | 1 + sys/arm/econa/cfi_bus_econa.c | 67 ++ sys/arm/econa/econa.c | 758 +++++++++++++++ sys/arm/econa/econa_machdep.c | 396 ++++++++ sys/arm/econa/econa_reg.h | 180 ++++ sys/arm/econa/econa_var.h | 52 ++ sys/arm/econa/ehci_ebus.c | 300 ++++++ sys/arm/econa/files.econa | 14 + sys/arm/econa/if_ece.c | 1948 +++++++++++++++++++++++++++++++++++++++ sys/arm/econa/if_ecereg.h | 154 ++++ sys/arm/econa/if_ecevar.h | 193 ++++ sys/arm/econa/ohci_ec.c | 241 +++++ sys/arm/econa/std.econa | 14 + sys/arm/econa/timer.c | 382 ++++++++ sys/arm/econa/uart_bus_ec.c | 79 ++ sys/arm/econa/uart_cpu_ec.c | 87 ++ sys/arm/include/cpuconf.h | 5 +- sys/arm/include/cpufunc.h | 23 + 22 files changed, 5368 insertions(+), 3 deletions(-) create mode 100644 sys/arm/arm/cpufunc_asm_fa526.S create mode 100644 sys/arm/conf/CNS11XXNAS create mode 100644 sys/arm/conf/CNS11XXNAS.hints create mode 100644 sys/arm/econa/cfi_bus_econa.c create mode 100644 sys/arm/econa/econa.c create mode 100644 sys/arm/econa/econa_machdep.c create mode 100644 sys/arm/econa/econa_reg.h create mode 100644 sys/arm/econa/econa_var.h create mode 100644 sys/arm/econa/ehci_ebus.c create mode 100644 sys/arm/econa/files.econa create mode 100644 sys/arm/econa/if_ece.c create mode 100644 sys/arm/econa/if_ecereg.h create mode 100644 sys/arm/econa/if_ecevar.h create mode 100644 sys/arm/econa/ohci_ec.c create mode 100644 sys/arm/econa/std.econa create mode 100644 sys/arm/econa/timer.c create mode 100644 sys/arm/econa/uart_bus_ec.c create mode 100644 sys/arm/econa/uart_cpu_ec.c (limited to 'sys/arm') diff --git a/sys/arm/arm/cpufunc.c b/sys/arm/arm/cpufunc.c index 18291a1..4354f0a 100644 --- a/sys/arm/arm/cpufunc.c +++ b/sys/arm/arm/cpufunc.c @@ -781,6 +781,73 @@ struct cpu_functions xscalec3_cpufuncs = { xscale_setup /* cpu setup */ }; #endif /* CPU_XSCALE_81342 */ + + +#if defined(CPU_FA526) +struct cpu_functions fa526_cpufuncs = { + /* CPU functions */ + + .cf_id = cpufunc_id, + .cf_cpwait = cpufunc_nullop, + + /* MMU functions */ + + .cf_control = cpufunc_control, + .cf_domains = cpufunc_domains, + .cf_setttb = fa526_setttb, + .cf_faultstatus = cpufunc_faultstatus, + .cf_faultaddress = cpufunc_faultaddress, + + /* TLB functions */ + + .cf_tlb_flushID = armv4_tlb_flushID, + .cf_tlb_flushID_SE = fa526_tlb_flushID_SE, + .cf_tlb_flushI = armv4_tlb_flushI, + .cf_tlb_flushI_SE = fa526_tlb_flushI_SE, + .cf_tlb_flushD = armv4_tlb_flushD, + .cf_tlb_flushD_SE = armv4_tlb_flushD_SE, + + /* Cache operations */ + + .cf_icache_sync_all = fa526_icache_sync_all, + .cf_icache_sync_range = fa526_icache_sync_range, + + .cf_dcache_wbinv_all = fa526_dcache_wbinv_all, + .cf_dcache_wbinv_range = fa526_dcache_wbinv_range, + .cf_dcache_inv_range = fa526_dcache_inv_range, + .cf_dcache_wb_range = fa526_dcache_wb_range, + + .cf_idcache_wbinv_all = fa526_idcache_wbinv_all, + .cf_idcache_wbinv_range = fa526_idcache_wbinv_range, + + + .cf_l2cache_wbinv_all = cpufunc_nullop, + .cf_l2cache_wbinv_range = (void *)cpufunc_nullop, + .cf_l2cache_inv_range = (void *)cpufunc_nullop, + .cf_l2cache_wb_range = (void *)cpufunc_nullop, + + + /* Other functions */ + + .cf_flush_prefetchbuf = fa526_flush_prefetchbuf, + .cf_drain_writebuf = armv4_drain_writebuf, + .cf_flush_brnchtgt_C = cpufunc_nullop, + .cf_flush_brnchtgt_E = fa526_flush_brnchtgt_E, + + .cf_sleep = fa526_cpu_sleep, + + /* Soft functions */ + + .cf_dataabt_fixup = cpufunc_null_fixup, + .cf_prefetchabt_fixup = cpufunc_null_fixup, + + .cf_context_switch = fa526_context_switch, + + .cf_setup = fa526_setup +}; +#endif /* CPU_FA526 */ + + /* * Global constants also used by locore.s */ @@ -793,6 +860,7 @@ u_int cpu_reset_needs_v4_MMU_disable; /* flag used in locore.s */ defined (CPU_ARM9E) || defined (CPU_ARM10) || \ defined(CPU_XSCALE_80200) || defined(CPU_XSCALE_80321) || \ defined(CPU_XSCALE_PXA2X0) || defined(CPU_XSCALE_IXP425) || \ + defined(CPU_FA526) || \ defined(CPU_XSCALE_80219) || defined(CPU_XSCALE_81342) static void get_cachetype_cp15(void); @@ -1073,6 +1141,19 @@ set_cpufuncs() goto out; } #endif /* CPU_SA1110 */ +#ifdef CPU_FA526 + if (cputype == CPU_ID_FA526) { + cpufuncs = fa526_cpufuncs; + cpu_reset_needs_v4_MMU_disable = 1; /* SA needs it */ + get_cachetype_cp15(); + pmap_pte_init_generic(); + + /* Use powersave on this CPU. */ + cpu_do_powersave = 1; + + goto out; + } +#endif /* CPU_FA526 */ #ifdef CPU_IXP12X0 if (cputype == CPU_ID_IXP1200) { cpufuncs = ixp12x0_cpufuncs; @@ -1547,7 +1628,8 @@ late_abort_fixup(arg) defined(CPU_XSCALE_80200) || defined(CPU_XSCALE_80321) || \ defined(CPU_XSCALE_PXA2X0) || defined(CPU_XSCALE_IXP425) || \ defined(CPU_XSCALE_80219) || defined(CPU_XSCALE_81342) || \ - defined(CPU_ARM10) || defined(CPU_ARM11) + defined(CPU_ARM10) || defined(CPU_ARM11) || \ + defined(CPU_FA526) #define IGN 0 #define OR 1 @@ -2013,6 +2095,62 @@ sa11x0_setup(args) } #endif /* CPU_SA1100 || CPU_SA1110 */ +#if defined(CPU_FA526) +struct cpu_option fa526_options[] = { +#ifdef COMPAT_12 + { "nocache", IGN, BIC, (CPU_CONTROL_IC_ENABLE | + CPU_CONTROL_DC_ENABLE) }, + { "nowritebuf", IGN, BIC, CPU_CONTROL_WBUF_ENABLE }, +#endif /* COMPAT_12 */ + { "cpu.cache", BIC, OR, (CPU_CONTROL_IC_ENABLE | + CPU_CONTROL_DC_ENABLE) }, + { "cpu.nocache", OR, BIC, (CPU_CONTROL_IC_ENABLE | + CPU_CONTROL_DC_ENABLE) }, + { "cpu.writebuf", BIC, OR, CPU_CONTROL_WBUF_ENABLE }, + { "cpu.nowritebuf", OR, BIC, CPU_CONTROL_WBUF_ENABLE }, + { NULL, IGN, IGN, 0 } +}; + +void +fa526_setup(char *args) +{ + int cpuctrl, cpuctrlmask; + + cpuctrl = CPU_CONTROL_MMU_ENABLE | CPU_CONTROL_32BP_ENABLE + | CPU_CONTROL_32BD_ENABLE | CPU_CONTROL_SYST_ENABLE + | CPU_CONTROL_IC_ENABLE | CPU_CONTROL_DC_ENABLE + | CPU_CONTROL_WBUF_ENABLE | CPU_CONTROL_LABT_ENABLE; + cpuctrlmask = CPU_CONTROL_MMU_ENABLE | CPU_CONTROL_32BP_ENABLE + | CPU_CONTROL_32BD_ENABLE | CPU_CONTROL_SYST_ENABLE + | CPU_CONTROL_IC_ENABLE | CPU_CONTROL_DC_ENABLE + | CPU_CONTROL_WBUF_ENABLE | CPU_CONTROL_ROM_ENABLE + | CPU_CONTROL_BEND_ENABLE | CPU_CONTROL_AFLT_ENABLE + | CPU_CONTROL_LABT_ENABLE | CPU_CONTROL_BPRD_ENABLE + | CPU_CONTROL_CPCLK | CPU_CONTROL_VECRELOC; + +#ifndef ARM32_DISABLE_ALIGNMENT_FAULTS + cpuctrl |= CPU_CONTROL_AFLT_ENABLE; +#endif + + cpuctrl = parse_cpu_options(args, fa526_options, cpuctrl); + +#ifdef __ARMEB__ + cpuctrl |= CPU_CONTROL_BEND_ENABLE; +#endif + + if (vector_page == ARM_VECTORS_HIGH) + cpuctrl |= CPU_CONTROL_VECRELOC; + + /* Clear out the cache */ + cpu_idcache_wbinv_all(); + + /* Set the control register */ + ctrl = cpuctrl; + cpu_control(0xffffffff, cpuctrl); +} +#endif /* CPU_FA526 */ + + #if defined(CPU_IXP12X0) struct cpu_option ixp12x0_options[] = { { "cpu.cache", BIC, OR, (CPU_CONTROL_IC_ENABLE | CPU_CONTROL_DC_ENABLE) }, diff --git a/sys/arm/arm/cpufunc_asm_fa526.S b/sys/arm/arm/cpufunc_asm_fa526.S new file mode 100644 index 0000000..d68d7a6 --- /dev/null +++ b/sys/arm/arm/cpufunc_asm_fa526.S @@ -0,0 +1,209 @@ +/* $NetBSD: cpufunc_asm_fa526.S,v 1.3 2008/10/15 16:56:49 matt Exp $*/ +/*- + * Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Matt Thomas + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + + +#include +__FBSDID("$FreeBSD$"); + +#define CACHELINE_SIZE 16 + +ENTRY(fa526_setttb) + mov r1, #0 + mcr p15, 0, r1, c7, c14, 0 /* clean and invalidate D$ */ + mcr p15, 0, r1, c7, c5, 0 /* invalidate I$ */ + mcr p15, 0, r1, c7, c5, 6 /* invalidate BTB */ + mcr p15, 0, r1, c7, c10, 4 /* drain write and fill buffer */ + + mcr p15, 0, r0, c2, c0, 0 /* Write the TTB */ + + /* If we have updated the TTB we must flush the TLB */ + mcr p15, 0, r1, c8, c7, 0 /* invalidate I+D TLB */ + + /* Make sure that pipeline is emptied */ + mov r0, r0 + mov r0, r0 + mov pc, lr + +/* + * TLB functions + */ +ENTRY(fa526_tlb_flushID_SE) + mcr p15, 0, r0, c8, c7, 1 /* flush Utlb single entry */ + mov pc, lr + +/* + * TLB functions + */ +ENTRY(fa526_tlb_flushI_SE) + mcr p15, 0, r0, c8, c5, 1 /* flush Itlb single entry */ + mov pc, lr + +ENTRY(fa526_cpu_sleep) + mov r0, #0 +/* nop + nop*/ + mcr p15, 0, r0, c7, c0, 4 /* Wait for interrupt*/ + mov pc, lr + +ENTRY(fa526_flush_prefetchbuf) + mov r0, #0 + mcr p15, 0, r0, c7, c5, 4 /* Pre-fetch flush */ + mov pc, lr + +/* + * Cache functions + */ +ENTRY(fa526_idcache_wbinv_all) + mov r0, #0 + mcr p15, 0, r0, c7, c14, 0 /* clean and invalidate D$ */ + mcr p15, 0, r0, c7, c5, 0 /* invalidate I$ */ + mcr p15, 0, r0, c7, c10, 4 /* drain write buffer */ + mov pc, lr + +ENTRY(fa526_icache_sync_all) + mov r0, #0 + mcr p15, 0, r0, c7, c5, 0 /* invalidate I$ */ + mov pc, lr + +ENTRY(fa526_dcache_wbinv_all) + mov r0, #0 + mcr p15, 0, r0, c7, c14, 0 /* clean and invalidate D$ */ + mcr p15, 0, r0, c7, c10, 4 /* drain write buffer */ + mov pc, lr + +/* + * Soft functions + */ +ENTRY(fa526_dcache_wbinv_range) + cmp r1, #0x4000 + bhs _C_LABEL(fa526_dcache_wbinv_all) + + and r2, r0, #(CACHELINE_SIZE - 1) + add r1, r1, r2 + bic r0, r0, #(CACHELINE_SIZE - 1) + +1: mcr p15, 0, r0, c7, c14, 1 /* clean and invalidate D$ entry */ + add r0, r0, #CACHELINE_SIZE + subs r1, r1, #CACHELINE_SIZE + bhi 1b + + mcr p15, 0, r0, c7, c10, 4 /* drain write buffer */ + mov pc, lr + +ENTRY(fa526_dcache_wb_range) + cmp r1, #0x4000 + bls 1f + + mov r0, #0 + mcr p15, 0, r0, c7, c10, 0 /* clean entire D$ */ + b 3f + +1: and r2, r0, #(CACHELINE_SIZE - 1) + add r1, r1, r2 + bic r0, r0, #(CACHELINE_SIZE - 1) + +2: mcr p15, 0, r0, c7, c10, 1 /* clean D$ entry */ + add r0, r0, #CACHELINE_SIZE + subs r1, r1, #CACHELINE_SIZE + bhi 2b + +3: mcr p15, 0, r0, c7, c10, 4 /* drain write buffer */ + mov pc, lr + +ENTRY(fa526_dcache_inv_range) + and r2, r0, #(CACHELINE_SIZE - 1) + add r1, r1, r2 + bic r0, r0, #(CACHELINE_SIZE - 1) + +1: mcr p15, 0, r0, c7, c6, 1 /* invalidate D$ single entry */ + add r0, r0, #CACHELINE_SIZE + subs r1, r1, #CACHELINE_SIZE + bhi 1b + + mov pc, lr + +ENTRY(fa526_idcache_wbinv_range) + cmp r1, #0x4000 + bhs _C_LABEL(fa526_idcache_wbinv_all) + + and r2, r0, #(CACHELINE_SIZE - 1) + add r1, r1, r2 + bic r0, r0, #(CACHELINE_SIZE - 1) + +1: mcr p15, 0, r0, c7, c14, 1 /* clean and invalidate D$ entry */ + mcr p15, 0, r0, c7, c5, 1 /* invalidate I$ entry */ + add r0, r0, #CACHELINE_SIZE + subs r1, r1, #CACHELINE_SIZE + bhi 1b + +2: mcr p15, 0, r0, c7, c10, 4 /* drain write buffer */ + mov pc, lr + +ENTRY(fa526_icache_sync_range) + cmp r1, #0x4000 + bhs _C_LABEL(fa526_icache_sync_all) + + and r2, r0, #(CACHELINE_SIZE - 1) + add r1, r1, r2 + bic r0, r0, #(CACHELINE_SIZE - 1) + +1: mcr p15, 0, r0, c7, c10, 1 /* clean D$ entry */ + mcr p15, 0, r0, c7, c5, 1 /* invalidate I$ entry */ + add r0, r0, #CACHELINE_SIZE + subs r1, r1, #CACHELINE_SIZE + bhi 1b + +2: mcr p15, 0, r0, c7, c10, 4 /* drain write buffer */ + mov pc, lr + +ENTRY(fa526_flush_brnchtgt_E) + mov r0, #0 + mcr p15, 0, r0, c7, c5, 6 /* invalidate BTB cache */ + mov pc, lr + +ENTRY(fa526_context_switch) + /* + * CF_CACHE_PURGE_ID will *ALWAYS* be called prior to this. + * Thus the data cache will contain only kernel data and the + * instruction cache will contain only kernel code, and all + * kernel mappings are shared by all processes. + */ + + mcr p15, 0, r0, c2, c0, 0 /* Write the TTB */ + + /* If we have updated the TTB we must flush the TLB */ + mov r0, #0 + mcr p15, 0, r0, c8, c7, 0 /* flush the I+D tlb */ + + /* Make sure that pipeline is emptied */ + mov r0, r0 + mov r0, r0 + mov pc, lr + diff --git a/sys/arm/arm/elf_trampoline.c b/sys/arm/arm/elf_trampoline.c index 2755f81..6addbc3 100644 --- a/sys/arm/arm/elf_trampoline.c +++ b/sys/arm/arm/elf_trampoline.c @@ -57,6 +57,8 @@ void __startC(void); #define cpu_idcache_wbinv_all arm8_cache_purgeID #elif defined(CPU_ARM9) #define cpu_idcache_wbinv_all arm9_idcache_wbinv_all +#elif defined(CPU_FA526) +#define cpu_idcache_wbinv_all fa526_idcache_wbinv_all #elif defined(CPU_ARM9E) #define cpu_idcache_wbinv_all armv5_ec_idcache_wbinv_all #elif defined(CPU_ARM10) diff --git a/sys/arm/conf/CNS11XXNAS b/sys/arm/conf/CNS11XXNAS new file mode 100644 index 0000000..4d4fbf5 --- /dev/null +++ b/sys/arm/conf/CNS11XXNAS @@ -0,0 +1,126 @@ +# CNS11XXNAS - StarSemi STR9104/Cavium CNS1102 NAS +# kernel configuration file for FreeBSD/arm +# +# For more information on this file, please read the handbook section on +# Kernel Configuration Files: +# +# http://www.FreeBSD.org/doc/en_US.ISO8859-1/books/handbook/kernelconfig-config.html +# +# The handbook is also available locally in /usr/share/doc/handbook +# if you've installed the doc distribution, otherwise always see the +# FreeBSD World Wide Web server (http://www.FreeBSD.org/) for the +# latest information. +# +# An exhaustive list of options and more detailed explanations of the +# device lines is also present in the ../../conf/NOTES and NOTES files. +# If you are in doubt as to the purpose or necessity of a line, check first +# in NOTES. +# +# $FreeBSD$ + +ident CNS11XXNAS + +#options PHYSADDR=0x10000000 +#options KERNPHYSADDR=0x10200000 +#options KERNVIRTADDR=0xc0200000 # Used in ldscript.arm +#options FLASHADDR=0x50000000 +#options LOADERRAMADDR=0x00000000 +#options STARTUP_PAGETABLE_ADDR=0x10000000 + +include "../econa/std.econa" + +#To statically compile in device wiring instead of /boot/device.hints +hints "CNS11XXNAS.hints" +makeoptions MODULES_OVERRIDE="" + +makeoptions DEBUG=-g #Build kernel with gdb(1) debug symbols +options HZ=100 +options DEVICE_POLLING + +# Debugging for use in -current +options KDB +#options GDB +options DDB #Enable the kernel debugger +#options INVARIANTS #Enable calls of extra sanity checking +#options INVARIANT_SUPPORT #Extra sanity checks of internal structures, required by INVARIANTS +#options WITNESS #Enable checks to detect deadlocks and cycles +##options WITNESS_SKIPSPIN #Don't run witness on spinlocks for speed +#options DIAGNOSTIC + + +#options COMPAT_FREEBSD5 +#options COMPAT_FREEBSD6 +#options COMPAT_FREEBSD7 + + +options SCHED_ULE #ULE scheduler +#options SCHED_4BSD #4BSD scheduler +options GEOM_PART_GPT # GUID Partition Tables. +#options GEOM_PART_EBR +#options GEOM_PART_EBR_COMPAT +options GEOM_LABEL # Provides labelization + + +options INET #InterNETworking +options INET6 #IPv6 communications protocols +options FFS #Berkeley Fast Filesystem +options SOFTUPDATES #Enable FFS soft updates support +options UFS_ACL #Support for access control lists +options UFS_DIRHASH #Improve performance on big directories +options NFSCLIENT #Network Filesystem Client +#options NFSSERVER #Network Filesystem Server +#options NFSLOCKD #Network Lock Manager +options NFS_ROOT #NFS usable as /, requires NFSCLIENT +options MSDOSFS #MSDOS Filesystem +#options CD9660 #ISO 9660 Filesystem +#options PROCFS #Process filesystem (requires PSEUDOFS) +options PSEUDOFS #Pseudo-filesystem framework +options SCSI_DELAY=5000 #Delay (in ms) before probing SCSI +options KTRACE #ktrace(1) support +options SYSVSHM #SYSV-style shared memory +options SYSVMSG #SYSV-style message queues +options SYSVSEM #SYSV-style semaphores +options _KPOSIX_PRIORITY_SCHEDULING #Posix P1003_1B real-time extensions +options MUTEX_NOINLINE #Mutex inlines are space hogs +options RWLOCK_NOINLINE #rwlock inlines are space hogs +options SX_NOINLINE #sx inliens are space hogs +#options BOOTP +#options BOOTP_NFSROOT +#options BOOTP_NFSV3 +#options BOOTP_WIRED_TO=npe0 +#options BOOTP_COMPAT + +#device pci +device uart + + +device firmware +device mii # Minimal mii routines +device ether +device bpf + +device pty +device loop + +device md +device random # Entropy device + +#options ARM_USE_SMALL_ALLOC + +device usb +#options USB_DEBUG +device ohci +device ehci +device umass +device scbus # SCSI bus (required for SCSI) +device da # Direct Access (disks) +device pass +device cfi + +#device udav # Davicom DM9601E USB + +device geom_label +device geom_journal +device geom_part_bsd + +options ROOTDEVNAME=\"ufs:da0s1a\" diff --git a/sys/arm/conf/CNS11XXNAS.hints b/sys/arm/conf/CNS11XXNAS.hints new file mode 100644 index 0000000..e8c0da7 --- /dev/null +++ b/sys/arm/conf/CNS11XXNAS.hints @@ -0,0 +1 @@ +# $FreeBSD$ diff --git a/sys/arm/econa/cfi_bus_econa.c b/sys/arm/econa/cfi_bus_econa.c new file mode 100644 index 0000000..88b6899 --- /dev/null +++ b/sys/arm/econa/cfi_bus_econa.c @@ -0,0 +1,67 @@ +/*- + * Copyright (c) 2009 Yohanes Nugroho + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include + +static int +cfi_econa_probe(device_t dev) +{ + + return cfi_probe(dev); +} + +static device_method_t cfi_econa_methods[] = { + /* device interface */ + DEVMETHOD(device_probe, cfi_econa_probe), + DEVMETHOD(device_attach, cfi_attach), + DEVMETHOD(device_detach, cfi_detach), + + {0, 0} +}; + +static driver_t cfi_econa_driver = { + cfi_driver_name, + cfi_econa_methods, + sizeof(struct cfi_softc), +}; +DRIVER_MODULE(cfi, econaarm, cfi_econa_driver, cfi_devclass, 0, 0); diff --git a/sys/arm/econa/econa.c b/sys/arm/econa/econa.c new file mode 100644 index 0000000..eb6d04e --- /dev/null +++ b/sys/arm/econa/econa.c @@ -0,0 +1,758 @@ +/*- + * Copyright (c) 2009 Yohanes Nugroho + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define _ARM32_BUS_DMA_PRIVATE +#include +#include +#include + +#include "econa_reg.h" +#include "econa_var.h" + +static struct econa_softc *econa_softc; + +unsigned int CPU_clock = 200000000; +unsigned int AHB_clock; +unsigned int APB_clock; + +bs_protos(generic); +bs_protos(generic_armv4); + +struct bus_space econa_bs_tag = { + /* cookie */ + (void *) 0, + + /* mapping/unmapping */ + generic_bs_map, + generic_bs_unmap, + generic_bs_subregion, + + /* allocation/deallocation */ + generic_bs_alloc, + generic_bs_free, + + /* barrier */ + generic_bs_barrier, + + /* read (single) */ + generic_bs_r_1, + generic_armv4_bs_r_2, + generic_bs_r_4, + NULL, + + /* read multiple */ + generic_bs_rm_1, + generic_armv4_bs_rm_2, + generic_bs_rm_4, + NULL, + + /* read region */ + generic_bs_rr_1, + generic_armv4_bs_rr_2, + generic_bs_rr_4, + NULL, + + /* write (single) */ + generic_bs_w_1, + generic_armv4_bs_w_2, + generic_bs_w_4, + NULL, + + /* write multiple */ + generic_bs_wm_1, + generic_armv4_bs_wm_2, + generic_bs_wm_4, + NULL, + + /* write region */ + NULL, + NULL, + NULL, + NULL, + + /* set multiple */ + NULL, + NULL, + NULL, + NULL, + + /* set region */ + NULL, + NULL, + NULL, + NULL, + + /* copy */ + NULL, + NULL, + NULL, + NULL, + + /* read (single) stream */ + NULL, + NULL, + NULL, + NULL, + + /* read multiple stream */ + NULL, + generic_armv4_bs_rm_2, + NULL, + NULL, + + /* read region stream */ + NULL, + NULL, + NULL, + NULL, + + /* write (single) stream */ + NULL, + NULL, + NULL, + NULL, + + /* write multiple stream */ + NULL, + generic_armv4_bs_wm_2, + NULL, + NULL, + + /* write region stream */ + NULL, + NULL, + NULL, + NULL +}; + +bus_space_tag_t obio_tag = &econa_bs_tag; + +static int +econa_probe(device_t dev) +{ + + device_set_desc(dev, "ECONA device bus"); + return (0); +} + +static void +econa_identify(driver_t *drv, device_t parent) +{ + + BUS_ADD_CHILD(parent, 0, "econaarm", 0); +} + +struct arm32_dma_range * +bus_dma_get_range(void) +{ + + return (NULL); +} + +int +bus_dma_get_range_nb(void) +{ + + return (0); +} + +extern void irq_entry(void); + +static void +econa_add_child(device_t dev, int prio, const char *name, int unit, + bus_addr_t addr, bus_size_t size, + int irq0, int irq1, + int irq2, int irq3, int irq4) +{ + device_t kid; + struct econa_ivar *ivar; + + kid = device_add_child_ordered(dev, prio, name, unit); + if (kid == NULL) { + printf("Can't add child %s%d ordered\n", name, unit); + return; + } + ivar = malloc(sizeof(*ivar), M_DEVBUF, M_NOWAIT | M_ZERO); + if (ivar == NULL) { + device_delete_child(dev, kid); + return; + } + device_set_ivars(kid, ivar); + resource_list_init(&ivar->resources); + if (irq0 != -1) + bus_set_resource(kid, SYS_RES_IRQ, 0, irq0, 1); + if (irq1 != 0) + bus_set_resource(kid, SYS_RES_IRQ, 1, irq1, 1); + if (irq2 != 0) + bus_set_resource(kid, SYS_RES_IRQ, 2, irq2, 1); + if (irq3 != 0) + bus_set_resource(kid, SYS_RES_IRQ, 3, irq3, 1); + if (irq4 != 0) + bus_set_resource(kid, SYS_RES_IRQ, 4, irq4, 1); + + if (addr != 0) + bus_set_resource(kid, SYS_RES_MEMORY, 0, addr, size); + +} + +struct cpu_devs +{ + const char *name; + int unit; + bus_addr_t mem_base; + bus_size_t mem_len; + int irq0; + int irq1; + int irq2; + int irq3; + int irq4; +}; + +struct cpu_devs econarm_devs[] = +{ + { + "econa_ic", 0, + ECONA_IO_BASE + ECONA_PIC_BASE, ECONA_PIC_SIZE, + 0 + }, + { + "system", 0, + ECONA_IO_BASE + ECONA_SYSTEM_BASE, ECONA_SYSTEM_SIZE, + 0 + }, + { + "uart", 0, + ECONA_IO_BASE + ECONA_UART_BASE, ECONA_UART_SIZE, + ECONA_IRQ_UART + }, + { + "timer", 0, + ECONA_IO_BASE + ECONA_TIMER_BASE, ECONA_TIMER_SIZE, + ECONA_IRQ_TIMER_1, ECONA_IRQ_TIMER_2 + }, + { + "ohci", 0, + ECONA_OHCI_VBASE, ECONA_OHCI_SIZE, + ECONA_IRQ_OHCI + }, + { + "ehci", 0, + ECONA_EHCI_VBASE, ECONA_EHCI_SIZE, + ECONA_IRQ_EHCI + }, + { + "cfi", 0, + ECONA_CFI_VBASE, ECONA_CFI_SIZE, + 0 + }, + { + "ece", 0, + ECONA_IO_BASE + ECONA_NET_BASE, ECONA_NET_SIZE, + ECONA_IRQ_STATUS, + ECONA_IRQ_TSTC, ECONA_IRQ_FSRC, + ECONA_IRQ_TSQE, ECONA_IRQ_FSQF, + }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0 } +}; + +static void +econa_cpu_add_builtin_children(device_t dev, struct econa_softc *sc) +{ + int i; + struct cpu_devs *walker; + + for (i = 0, walker = econarm_devs; walker->name; i++, walker++) { + econa_add_child(dev, i, walker->name, walker->unit, + walker->mem_base, walker->mem_len, + walker->irq0,walker->irq1, walker->irq2, + walker->irq3, walker->irq4); + } + +} + +struct intc_trigger_t { + int mode; + int level; +}; + +static struct intc_trigger_t intc_trigger_table[] = { + {INTC_EDGE_TRIGGER, INTC_RISING_EDGE}, + {INTC_EDGE_TRIGGER, INTC_RISING_EDGE}, + {INTC_EDGE_TRIGGER, INTC_FALLING_EDGE}, + {INTC_EDGE_TRIGGER, INTC_RISING_EDGE}, + {INTC_TRIGGER_UNKNOWN, INTC_TRIGGER_UNKNOWN}, + {INTC_LEVEL_TRIGGER, INTC_ACTIVE_LOW}, + {INTC_LEVEL_TRIGGER, INTC_ACTIVE_LOW}, + {INTC_LEVEL_TRIGGER, INTC_ACTIVE_HIGH}, + {INTC_TRIGGER_UNKNOWN, INTC_TRIGGER_UNKNOWN}, + {INTC_LEVEL_TRIGGER, INTC_ACTIVE_HIGH}, + {INTC_LEVEL_TRIGGER, INTC_ACTIVE_HIGH}, + {INTC_LEVEL_TRIGGER, INTC_ACTIVE_HIGH}, + {INTC_LEVEL_TRIGGER, INTC_ACTIVE_HIGH}, + {INTC_TRIGGER_UNKNOWN, INTC_TRIGGER_UNKNOWN}, + {INTC_LEVEL_TRIGGER, INTC_ACTIVE_HIGH}, + {INTC_EDGE_TRIGGER, INTC_FALLING_EDGE}, + {INTC_TRIGGER_UNKNOWN, INTC_TRIGGER_UNKNOWN}, + {INTC_TRIGGER_UNKNOWN, INTC_TRIGGER_UNKNOWN}, + {INTC_LEVEL_TRIGGER, INTC_ACTIVE_HIGH}, + {INTC_EDGE_TRIGGER, INTC_RISING_EDGE}, + {INTC_EDGE_TRIGGER, INTC_RISING_EDGE}, + {INTC_EDGE_TRIGGER, INTC_RISING_EDGE}, + {INTC_EDGE_TRIGGER, INTC_RISING_EDGE}, + {INTC_LEVEL_TRIGGER, INTC_ACTIVE_LOW}, + {INTC_LEVEL_TRIGGER, INTC_ACTIVE_LOW}, +}; + +static inline uint32_t +read_4(struct econa_softc *sc, bus_size_t off) +{ + + return bus_space_read_4(sc->ec_st, sc->ec_sys_sh, off); +} + +static inline void +write_4(struct econa_softc *sc, bus_size_t off, uint32_t val) +{ + + return bus_space_write_4(sc->ec_st, sc->ec_sys_sh, off, val); +} + +static inline uint32_t +system_read_4(struct econa_softc *sc, bus_size_t off) +{ + + return bus_space_read_4(sc->ec_st, sc->ec_system_sh, off); +} + +static inline void +system_write_4(struct econa_softc *sc, bus_size_t off, uint32_t val) +{ + + return bus_space_write_4(sc->ec_st, sc->ec_system_sh, off, val); +} + + + +static inline void +econa_set_irq_mode(struct econa_softc * sc, unsigned int irq, + unsigned int mode) +{ + unsigned int val; + + if ((mode != INTC_LEVEL_TRIGGER) && (mode != INTC_EDGE_TRIGGER)) + return; + + val = read_4(sc, INTC_INTERRUPT_TRIGGER_MODE_REG_OFFSET); + + if (mode == INTC_LEVEL_TRIGGER) { + if (val & (1UL << irq)) { + val &= ~(1UL << irq); + write_4(sc, INTC_INTERRUPT_TRIGGER_MODE_REG_OFFSET, + val); + } + } else { + if (!(val & (1UL << irq))) { + val |= (1UL << irq); + write_4(sc, INTC_INTERRUPT_TRIGGER_MODE_REG_OFFSET, + val); + } + } +} + +/* + * Configure interrupt trigger level to be Active High/Low + * or Rising/Falling Edge + */ +static inline void +econa_set_irq_level(struct econa_softc * sc, + unsigned int irq, unsigned int level) +{ + unsigned int val; + + if ((level != INTC_ACTIVE_HIGH) && + (level != INTC_ACTIVE_LOW) && + (level != INTC_RISING_EDGE) && + (level != INTC_FALLING_EDGE)) { + return; + } + + val = read_4(sc, INTC_INTERRUPT_TRIGGER_LEVEL_REG_OFFSET); + + if ((level == INTC_ACTIVE_HIGH) || (level == INTC_RISING_EDGE)) { + if (val & (1UL << irq)) { + val &= ~(1UL << irq); + write_4(sc, INTC_INTERRUPT_TRIGGER_LEVEL_REG_OFFSET, + val); + } + } else { + if (!(val & (1UL << irq))) { + val |= (1UL << irq); + write_4(sc, INTC_INTERRUPT_TRIGGER_LEVEL_REG_OFFSET, + val); + } + } +} + +static void +get_system_clock(void) +{ + uint32_t sclock = system_read_4(econa_softc, SYSTEM_CLOCK); + + sclock = (sclock >> 6) & 0x03; + + switch (sclock) { + case 0: + CPU_clock = 175000000; + break; + case 1: + CPU_clock = 200000000; + break; + case 2: + CPU_clock = 225000000; + break; + case 3: + CPU_clock = 250000000; + break; + } + AHB_clock = CPU_clock >> 1; + APB_clock = AHB_clock >> 1; +} + +static int +econa_attach(device_t dev) +{ + struct econa_softc *sc = device_get_softc(dev); + int i; + + econa_softc = sc; + sc->ec_st = &econa_bs_tag; + sc->ec_sh = ECONA_IO_BASE; + sc->dev = dev; + if (bus_space_subregion(sc->ec_st, sc->ec_sh, ECONA_PIC_BASE, + ECONA_PIC_SIZE, &sc->ec_sys_sh) != 0) + panic("Unable to map IRQ registers"); + + if (bus_space_subregion(sc->ec_st, sc->ec_sh, ECONA_SYSTEM_BASE, + ECONA_SYSTEM_SIZE, &sc->ec_system_sh) != 0) + panic("Unable to map IRQ registers"); + + sc->ec_irq_rman.rm_type = RMAN_ARRAY; + sc->ec_irq_rman.rm_descr = "ECONA IRQs"; + sc->ec_mem_rman.rm_type = RMAN_ARRAY; + sc->ec_mem_rman.rm_descr = "ECONA Memory"; + if (rman_init(&sc->ec_irq_rman) != 0 || + rman_manage_region(&sc->ec_irq_rman, 0, 31) != 0) + panic("econa_attach: failed to set up IRQ rman"); + if (rman_init(&sc->ec_mem_rman) != 0 || + rman_manage_region(&sc->ec_mem_rman, 0, + ~0) != 0) + panic("econa_attach: failed to set up memory rman"); + + write_4(sc, INTC_INTERRUPT_CLEAR_EDGE_TRIGGER_REG_OFFSET, 0xffffffff); + + write_4(sc, INTC_INTERRUPT_MASK_REG_OFFSET, 0xffffffff); + + write_4(sc, INTC_FIQ_MODE_SELECT_REG_OFFSET, 0); + + /*initialize irq*/ + for (i = 0; i < 32; i++) { + if (intc_trigger_table[i].mode != INTC_TRIGGER_UNKNOWN) { + econa_set_irq_mode(sc,i, intc_trigger_table[i].mode); + econa_set_irq_level(sc, i, intc_trigger_table[i].level); + } + } + + get_system_clock(); + + econa_cpu_add_builtin_children(dev, sc); + + bus_generic_probe(dev); + bus_generic_attach(dev); + enable_interrupts(I32_bit | F32_bit); + + return (0); +} + +static struct resource * +econa_alloc_resource(device_t dev, device_t child, int type, int *rid, + u_long start, u_long end, u_long count, u_int flags) +{ + struct econa_softc *sc = device_get_softc(dev); + struct resource_list_entry *rle; + struct econa_ivar *ivar = device_get_ivars(child); + struct resource_list *rl = &ivar->resources; + + if (device_get_parent(child) != dev) + return (BUS_ALLOC_RESOURCE(device_get_parent(dev), child, + type, rid, start, end, count, flags)); + + rle = resource_list_find(rl, type, *rid); + if (rle == NULL) { + return (NULL); + } + if (rle->res) + panic("Resource rid %d type %d already in use", *rid, type); + if (start == 0UL && end == ~0UL) { + start = rle->start; + count = ulmax(count, rle->count); + end = ulmax(rle->end, start + count - 1); + } + switch (type) + { + case SYS_RES_IRQ: + rle->res = rman_reserve_resource(&sc->ec_irq_rman, + start, end, count, flags, child); + break; + case SYS_RES_MEMORY: + rle->res = rman_reserve_resource(&sc->ec_mem_rman, + start, end, count, flags, child); + if (rle->res != NULL) { + rman_set_bustag(rle->res, &econa_bs_tag); + rman_set_bushandle(rle->res, start); + } + break; + } + if (rle->res) { + rle->start = rman_get_start(rle->res); + rle->end = rman_get_end(rle->res); + rle->count = count; + rman_set_rid(rle->res, *rid); + } + return (rle->res); +} + +static struct resource_list * +econa_get_resource_list(device_t dev, device_t child) +{ + struct econa_ivar *ivar; + ivar = device_get_ivars(child); + return (&(ivar->resources)); +} + +static int +econa_release_resource(device_t dev, device_t child, int type, + int rid, struct resource *r) +{ + struct resource_list *rl; + struct resource_list_entry *rle; + + rl = econa_get_resource_list(dev, child); + if (rl == NULL) + return (EINVAL); + rle = resource_list_find(rl, type, rid); + if (rle == NULL) + return (EINVAL); + rman_release_resource(r); + rle->res = NULL; + return (0); +} + +static int +econa_setup_intr(device_t dev, device_t child, + struct resource *ires, int flags, driver_filter_t *filt, + driver_intr_t *intr, void *arg, void **cookiep) +{ + + if (rman_get_start(ires) == ECONA_IRQ_SYSTEM && filt == NULL) + panic("All system interrupt ISRs must be FILTER"); + + BUS_SETUP_INTR(device_get_parent(dev), child, ires, flags, filt, + intr, arg, cookiep); + + arm_unmask_irq(rman_get_start(ires)); + + return (0); +} + +static int +econa_teardown_intr(device_t dev, device_t child, struct resource *res, + void *cookie) +{ + + return (BUS_TEARDOWN_INTR(device_get_parent(dev), child, res, cookie)); +} + +static int +econa_activate_resource(device_t bus, device_t child, int type, int rid, + struct resource *r) +{ + + return (rman_activate_resource(r)); +} + +static int +econa_print_child(device_t dev, device_t child) +{ + struct econa_ivar *ivars; + struct resource_list *rl; + int retval = 0; + + ivars = device_get_ivars(child); + rl = &ivars->resources; + + retval += bus_print_child_header(dev, child); + + retval += resource_list_print_type(rl, "port", SYS_RES_IOPORT, "%#lx"); + retval += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#lx"); + retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld"); + if (device_get_flags(dev)) + retval += printf(" flags %#x", device_get_flags(dev)); + + retval += bus_print_child_footer(dev, child); + + return (retval); +} + +void +arm_mask_irq(uintptr_t nb) +{ + unsigned int value; + + value = read_4(econa_softc,INTC_INTERRUPT_MASK_REG_OFFSET) | 1< + * Copyright (c) 1994-1998 Mark Brinicombe. + * Copyright (c) 1994 Brini. + * All rights reserved. + * + * This code is derived from software written for Brini by Mark Brinicombe + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Brini. + * 4. The name of the company nor the name of the author may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY BRINI ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL BRINI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include "opt_msgbuf.h" + +#include +__FBSDID("$FreeBSD$"); + +#define _ARM32_BUS_DMA_PRIVATE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "econa_reg.h" + +/* Page table for mapping proc0 zero page */ +#define KERNEL_PT_SYS 0 +#define KERNEL_PT_KERN 1 +#define KERNEL_PT_KERN_NUM 22 +/* L2 table for mapping after kernel */ +#define KERNEL_PT_AFKERNEL KERNEL_PT_KERN + KERNEL_PT_KERN_NUM +#define KERNEL_PT_AFKERNEL_NUM 5 + +/* this should be evenly divisable by PAGE_SIZE / L2_TABLE_SIZE_REAL (or 4) */ +#define NUM_KERNEL_PTS (KERNEL_PT_AFKERNEL + KERNEL_PT_AFKERNEL_NUM) + +/* Define various stack sizes in pages */ +#define IRQ_STACK_SIZE 1 +#define ABT_STACK_SIZE 1 +#define UND_STACK_SIZE 1 + +extern u_int data_abort_handler_address; +extern u_int prefetch_abort_handler_address; +extern u_int undefined_handler_address; + +struct pv_addr kernel_pt_table[NUM_KERNEL_PTS]; + +extern void *_end; + +extern int *end; + +struct pcpu __pcpu; +struct pcpu *pcpup = &__pcpu; + +/* Physical and virtual addresses for some global pages */ + +vm_paddr_t phys_avail[10]; +vm_paddr_t dump_avail[4]; +vm_offset_t physical_pages; + +struct pv_addr systempage; +struct pv_addr msgbufpv; +struct pv_addr irqstack; +struct pv_addr undstack; +struct pv_addr abtstack; +struct pv_addr kernelstack; + +static void *boot_arg1; +static void *boot_arg2; + +static struct trapframe proc0_tf; + +/* Static device mappings. */ +static const struct pmap_devmap econa_devmap[] = { + { + /* + * This maps DDR SDRAM + */ + ECONA_SDRAM_BASE, /*virtual*/ + ECONA_SDRAM_BASE, /*physical*/ + ECONA_SDRAM_SIZE, /*size*/ + VM_PROT_READ|VM_PROT_WRITE, + PTE_NOCACHE, + }, + /* + * Map the on-board devices VA == PA so that we can access them + * with the MMU on or off. + */ + { + /* + * This maps the interrupt controller, the UART + * and the timer. + */ + ECONA_IO_BASE, /*virtual*/ + ECONA_IO_BASE, /*physical*/ + ECONA_IO_SIZE, /*size*/ + VM_PROT_READ|VM_PROT_WRITE, + PTE_NOCACHE, + }, + { + /* + * OHCI + EHCI + */ + ECONA_OHCI_VBASE, /*virtual*/ + ECONA_OHCI_PBASE, /*physical*/ + ECONA_USB_SIZE, /*size*/ + VM_PROT_READ|VM_PROT_WRITE, + PTE_NOCACHE, + }, + { + /* + * CFI + */ + ECONA_CFI_VBASE, /*virtual*/ + ECONA_CFI_PBASE, /*physical*/ + ECONA_CFI_SIZE, + VM_PROT_READ|VM_PROT_WRITE, + PTE_NOCACHE, + }, + { + 0, + 0, + 0, + 0, + 0, + } +}; + + +void * +initarm(void *arg, void *arg2) +{ + struct pv_addr kernel_l1pt; + volatile uint32_t * ddr = (uint32_t *)0x4000000C; + int loop, i; + u_int l1pagetable; + vm_offset_t afterkern; + vm_offset_t freemempos; + vm_offset_t lastaddr; + uint32_t memsize; + int mem_info; + + + boot_arg1 = arg; + boot_arg2 = arg2; + boothowto = RB_VERBOSE; + + set_cpufuncs(); + lastaddr = fake_preload_metadata(); + pcpu_init(pcpup, 0, sizeof(struct pcpu)); + PCPU_SET(curthread, &thread0); + + + freemempos = (lastaddr + PAGE_MASK) & ~PAGE_MASK; + /* Define a macro to simplify memory allocation */ +#define valloc_pages(var, np) \ + alloc_pages((var).pv_va, (np)); \ + (var).pv_pa = (var).pv_va + (KERNPHYSADDR - KERNVIRTADDR); + +#define alloc_pages(var, np) \ + (var) = freemempos; \ + freemempos += (np * PAGE_SIZE); \ + memset((char *)(var), 0, ((np) * PAGE_SIZE)); + + while (((freemempos - L1_TABLE_SIZE) & (L1_TABLE_SIZE - 1)) != 0) + freemempos += PAGE_SIZE; + valloc_pages(kernel_l1pt, L1_TABLE_SIZE / PAGE_SIZE); + for (loop = 0; loop < NUM_KERNEL_PTS; ++loop) { + if (!(loop % (PAGE_SIZE / L2_TABLE_SIZE_REAL))) { + valloc_pages(kernel_pt_table[loop], + L2_TABLE_SIZE / PAGE_SIZE); + } else { + kernel_pt_table[loop].pv_va = freemempos - + (loop % (PAGE_SIZE / L2_TABLE_SIZE_REAL)) * + L2_TABLE_SIZE_REAL; + kernel_pt_table[loop].pv_pa = + kernel_pt_table[loop].pv_va - KERNVIRTADDR + + KERNPHYSADDR; + } + i++; + } + /* + * Allocate a page for the system page mapped to V0x00000000 + * This page will just contain the system vectors and can be + * shared by all processes. + */ + valloc_pages(systempage, 1); + + /* Allocate stacks for all modes */ + valloc_pages(irqstack, IRQ_STACK_SIZE); + valloc_pages(abtstack, ABT_STACK_SIZE); + valloc_pages(undstack, UND_STACK_SIZE); + valloc_pages(kernelstack, KSTACK_PAGES); + valloc_pages(msgbufpv, round_page(MSGBUF_SIZE) / PAGE_SIZE); + + /* + * Now we start construction of the L1 page table + * We start by mapping the L2 page tables into the L1. + * This means that we can replace L1 mappings later on if necessary + */ + l1pagetable = kernel_l1pt.pv_va; + + /* Map the L2 pages tables in the L1 page table */ + pmap_link_l2pt(l1pagetable, ARM_VECTORS_HIGH, + &kernel_pt_table[KERNEL_PT_SYS]); + for (i = 0; i < KERNEL_PT_KERN_NUM; i++) + pmap_link_l2pt(l1pagetable, KERNBASE + i * L1_S_SIZE, + &kernel_pt_table[KERNEL_PT_KERN + i]); + pmap_map_chunk(l1pagetable, KERNBASE, PHYSADDR, + (((uint32_t)lastaddr - KERNBASE) + PAGE_SIZE) & ~(PAGE_SIZE - 1), + VM_PROT_READ|VM_PROT_WRITE, PTE_CACHE); + afterkern = round_page((lastaddr + L1_S_SIZE) & ~(L1_S_SIZE - 1)); + for (i = 0; i < KERNEL_PT_AFKERNEL_NUM; i++) { + pmap_link_l2pt(l1pagetable, afterkern + i * L1_S_SIZE, + &kernel_pt_table[KERNEL_PT_AFKERNEL + i]); + } + + /* Map the vector page. */ + pmap_map_entry(l1pagetable, ARM_VECTORS_HIGH, systempage.pv_pa, + VM_PROT_READ|VM_PROT_WRITE, PTE_CACHE); + + + /* Map the stack pages */ + pmap_map_chunk(l1pagetable, irqstack.pv_va, irqstack.pv_pa, + IRQ_STACK_SIZE * PAGE_SIZE, VM_PROT_READ|VM_PROT_WRITE, PTE_CACHE); + pmap_map_chunk(l1pagetable, abtstack.pv_va, abtstack.pv_pa, + ABT_STACK_SIZE * PAGE_SIZE, VM_PROT_READ|VM_PROT_WRITE, PTE_CACHE); + pmap_map_chunk(l1pagetable, undstack.pv_va, undstack.pv_pa, + UND_STACK_SIZE * PAGE_SIZE, VM_PROT_READ|VM_PROT_WRITE, PTE_CACHE); + pmap_map_chunk(l1pagetable, kernelstack.pv_va, kernelstack.pv_pa, + KSTACK_PAGES * PAGE_SIZE, VM_PROT_READ|VM_PROT_WRITE, PTE_CACHE); + + pmap_map_chunk(l1pagetable, kernel_l1pt.pv_va, kernel_l1pt.pv_pa, + L1_TABLE_SIZE, VM_PROT_READ|VM_PROT_WRITE, PTE_PAGETABLE); + pmap_map_chunk(l1pagetable, msgbufpv.pv_va, msgbufpv.pv_pa, + MSGBUF_SIZE, VM_PROT_READ|VM_PROT_WRITE, PTE_CACHE); + + for (loop = 0; loop < NUM_KERNEL_PTS; ++loop) { + pmap_map_chunk(l1pagetable, kernel_pt_table[loop].pv_va, + kernel_pt_table[loop].pv_pa, L2_TABLE_SIZE, + VM_PROT_READ|VM_PROT_WRITE, PTE_PAGETABLE); + } + + pmap_devmap_bootstrap(l1pagetable, econa_devmap); + cpu_domains((DOMAIN_CLIENT << (PMAP_DOMAIN_KERNEL*2)) | DOMAIN_CLIENT); + setttb(kernel_l1pt.pv_pa); + cpu_tlb_flushID(); + cpu_domains(DOMAIN_CLIENT << (PMAP_DOMAIN_KERNEL*2)); + cninit(); + mem_info = ((*ddr) >> 4) & 0x3; + memsize = (8<pcb_flags = 0; + thread0.td_frame = &proc0_tf; + pcpup->pc_curpcb = thread0.td_pcb; + + arm_vector_init(ARM_VECTORS_HIGH, ARM_VEC_ALL); + + pmap_curmaxkvaddr = afterkern + L1_S_SIZE * (KERNEL_PT_KERN_NUM - 1); + + /* + * ARM_USE_SMALL_ALLOC uses dump_avail, so it must be filled before + * calling pmap_bootstrap. + */ + dump_avail[0] = PHYSADDR; + dump_avail[1] = PHYSADDR + memsize; + dump_avail[2] = 0; + dump_avail[3] = 0; + + pmap_bootstrap(freemempos, + KERNVIRTADDR + 3 * memsize, + &kernel_l1pt); + + msgbufp = (void*)msgbufpv.pv_va; + msgbufinit(msgbufp, MSGBUF_SIZE); + + mutex_init(); + + i = 0; +#if PHYSADDR != KERNPHYSADDR + phys_avail[i++] = PHYSADDR; + phys_avail[i++] = KERNPHYSADDR; +#endif + phys_avail[i++] = virtual_avail - KERNVIRTADDR + KERNPHYSADDR; + + phys_avail[i++] = PHYSADDR + memsize; + phys_avail[i++] = 0; + phys_avail[i++] = 0; + /* Do basic tuning, hz etc */ + init_param1(); + init_param2(physmem); + kdb_init(); + + return ((void *)(kernelstack.pv_va + USPACE_SVC_STACK_TOP - + sizeof(struct pcb))); +} diff --git a/sys/arm/econa/econa_reg.h b/sys/arm/econa/econa_reg.h new file mode 100644 index 0000000..0650194 --- /dev/null +++ b/sys/arm/econa/econa_reg.h @@ -0,0 +1,180 @@ +/*- + * Copyright (c) 2009 Yohanes Nugroho + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ +#ifndef _ARM_ECONA_REG_H +#define _ARM_ECONA_REG_H + +#define ECONA_SRAM_SIZE 0x10000000 +#define ECONA_DRAM_BASE 0x00000000 /* DRAM (via DDR Control Module) */ + +#define ECONA_SDRAM_BASE 0x40000000 +#define ECONA_SDRAM_SIZE 0x1000000 + + +#define ECONA_IO_BASE 0x70000000 +#define ECONA_IO_SIZE 0x0E000000 +#define ECONA_PIC_BASE 0x0D000000 +#define ECONA_PIC_SIZE 0x01000000 + +#define ECONA_UART_BASE 0x08000000 +#define ECONA_UART_SIZE 0x01000000 +#define ECONA_IRQ_UART 0xA + +#define ECONA_TIMER_BASE 0x09000000 +#define ECONA_TIMER_SIZE 0x01000000 +#define ECONA_IRQ_TIMER_1 0 +#define ECONA_IRQ_TIMER_2 1 +#define ECONA_IRQ_OHCI 23 +#define ECONA_IRQ_EHCI 24 + +#define ECONA_NET_BASE 0x00000000 + +#define ECONA_SYSTEM_BASE 0x07000000 +#define ECONA_SYSTEM_SIZE 0x01000000 + +#define ECONA_NET_SIZE 0x01000000 + +#define ECONA_CFI_PBASE 0x10000000 +#define ECONA_CFI_VBASE 0xD0000000 +#define ECONA_CFI_SIZE 0x10000000 + +#define ECONA_IRQ_STATUS 18 +#define ECONA_IRQ_TSTC 19 +#define ECONA_IRQ_FSRC 20 +#define ECONA_IRQ_TSQE 21 +#define ECONA_IRQ_FSQF 22 + +#define ECONA_IRQ_SYSTEM 0 + +#define ECONA_EHCI_PBASE 0xC8000000 +#define ECONA_EHCI_VBASE 0xF8000000 +#define ECONA_EHCI_SIZE 0x8000000 + +#define ECONA_OHCI_PBASE 0xC0000000 +#define ECONA_OHCI_VBASE 0xF0000000 +#define ECONA_OHCI_SIZE 0x8000000 + +#define ECONA_USB_SIZE 0xf000000 + +/*Interrupt controller*/ +#define INTC_LEVEL_TRIGGER 0 +#define INTC_EDGE_TRIGGER 1 +#define INTC_ACTIVE_HIGH 0 +#define INTC_ACTIVE_LOW 1 +/* + * define rising/falling edge for edge trigger mode + */ +#define INTC_RISING_EDGE 0 +#define INTC_FALLING_EDGE 1 + +#define INTC_INTERRUPT_SOURCE_REG_OFFSET 0x00 +#define INTC_INTERRUPT_MASK_REG_OFFSET 0x04 +#define INTC_INTERRUPT_CLEAR_EDGE_TRIGGER_REG_OFFSET 0x08 +#define INTC_INTERRUPT_TRIGGER_MODE_REG_OFFSET 0x0C +#define INTC_INTERRUPT_TRIGGER_LEVEL_REG_OFFSET 0x10 +#define INTC_INTERRUPT_STATUS_REG_OFFSET 0x14 +#define INTC_FIQ_MODE_SELECT_REG_OFFSET 0x18 +#define INTC_SOFTWARE_INTERRUPT_REG_OFFSET 0x1C + + +/* + * define rising/falling edge for edge trigger mode + */ +#define INTC_RISING_EDGE 0 +#define INTC_FALLING_EDGE 1 + + +#define TIMER_TM1_COUNTER_REG 0x00 +#define TIMER_TM1_LOAD_REG 0x04 +#define TIMER_TM1_MATCH1_REG 0x08 +#define TIMER_TM1_MATCH2_REG 0x0C + +#define TIMER_TM2_COUNTER_REG 0x10 +#define TIMER_TM2_LOAD_REG 0x14 +#define TIMER_TM2_MATCH1_REG 0x18 +#define TIMER_TM2_MATCH2_REG 0x1C + +#define TIMER_TM_CR_REG 0x30 +#define TIMER_TM_INTR_STATUS_REG 0x34 +#define TIMER_TM_INTR_MASK_REG 0x38 + +#define TIMER_TM_REVISION_REG 0x3C + + +#define INTC_TIMER1_BIT_INDEX 0 + +#define TIMER1_UP_DOWN_COUNT (1<<9) +#define TIMER2_UP_DOWN_COUNT (1<<10) + +#define TIMER1_MATCH1_INTR (1<<0) +#define TIMER1_MATCH2_INTR (1<<1) +#define TIMER1_OVERFLOW_INTR (1<<2) + + +#define TIMER2_MATCH1_INTR (1<<3) +#define TIMER2_MATCH2_INTR (1<<4) +#define TIMER2_OVERFLOW_INTR (1<<5) + + +#define TIMER_CLOCK_SOURCE_PCLK 0 +#define TIMER_CLOCK_SOURCE_EXT_CLK 1 + +/* + * define interrupt trigger mode + */ +#define INTC_LEVEL_TRIGGER 0 +#define INTC_EDGE_TRIGGER 1 + + +#define INTC_TRIGGER_UNKNOWN -1 + +#define TIMER1_OVERFLOW_INTERRUPT (1<<2) +#define TIMER2_OVERFLOW_INTERRUPT (1<<5) +#define TIMER_INTERRUPT_STATUS_REG 0x34 + + +#define TIMER1_ENABLE (1<<0) +#define TIMER1_CLOCK_SOURCE (1<<1) +#define TIMER1_OVERFLOW_ENABLE (1<<2) + + +#define TIMER2_ENABLE (1<<3) +#define TIMER2_CLOCK_SOURCE (1<<4) +#define TIMER2_OVERFLOW_ENABLE (1<<5) + + +#define TIMER_1 1 + +#define EC_UART_CLOCK 14769200 +#define EC_UART_REGSHIFT 2 + +#define SYSTEM_CLOCK 0x14 +#define RESET_CONTROL 0x4 +#define GLOBAL_RESET 0x1 +#define NET_INTERFACE_RESET (0x1 << 4) + +#endif diff --git a/sys/arm/econa/econa_var.h b/sys/arm/econa/econa_var.h new file mode 100644 index 0000000..044dcb7 --- /dev/null +++ b/sys/arm/econa/econa_var.h @@ -0,0 +1,52 @@ +/*- + * Copyright (c) 2009 Yohanes Nugroho . + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _ARM_ECONA_VAR_H +#define _ARM_ECONA_VAR_H + +extern bus_space_tag_t obio_tag; + +struct econa_softc { + device_t dev; + bus_space_tag_t ec_st; + bus_space_handle_t ec_sh; + bus_space_handle_t ec_sys_sh; + bus_space_handle_t ec_system_sh; + struct rman ec_irq_rman; + struct rman ec_mem_rman; +}; + +struct econa_ivar { + struct resource_list resources; +}; + +void power_on_network_interface (void); +unsigned int get_tclk (void); + + +#endif diff --git a/sys/arm/econa/ehci_ebus.c b/sys/arm/econa/ehci_ebus.c new file mode 100644 index 0000000..7b49225 --- /dev/null +++ b/sys/arm/econa/ehci_ebus.c @@ -0,0 +1,300 @@ +/*- + * Copyright (C) 2009 Yohanes Nugroho + * based on ehci_mbus.c + * Copyright (C) 2008 MARVELL INTERNATIONAL LTD. + * All rights reserved. + * + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of MARVELL nor the names of contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + +#include +__FBSDID("$FreeBSD$"); + +#include "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 + + +static device_attach_t ehci_ebus_attach; +static device_detach_t ehci_ebus_detach; +static device_shutdown_t ehci_ebus_shutdown; +static device_suspend_t ehci_ebus_suspend; +static device_resume_t ehci_ebus_resume; + + +static void *ih_err; + +#define EHCI_HC_DEVSTR "CNS11XX USB EHCI" +#define USB_BRIDGE_INTR_MASK 0x214 + +static int +ehci_ebus_suspend(device_t self) +{ + ehci_softc_t *sc = device_get_softc(self); + int err; + + err = bus_generic_suspend(self); + if (err) + return (err); + ehci_suspend(sc); + return (0); +} + +static int +ehci_ebus_resume(device_t self) +{ + ehci_softc_t *sc = device_get_softc(self); + + ehci_resume(sc); + + bus_generic_resume(self); + + return (0); +} + +static int +ehci_ebus_shutdown(device_t self) +{ + ehci_softc_t *sc = device_get_softc(self); + int err; + + err = bus_generic_shutdown(self); + if (err) + return (err); + ehci_shutdown(sc); + + return (0); +} + +static int +ehci_ebus_probe(device_t self) +{ + + device_set_desc(self, EHCI_HC_DEVSTR); + + return (BUS_PROBE_DEFAULT); +} + +static int +ehci_ebus_attach(device_t self) +{ + ehci_softc_t *sc = device_get_softc(self); + bus_space_handle_t bsh; + int err; + int rid; + + /* initialise some bus fields */ + sc->sc_bus.parent = self; + sc->sc_bus.devices = sc->sc_devices; + sc->sc_bus.devices_max = EHCI_MAX_DEVICES; + + /* get all DMA memory */ + if (usb_bus_mem_alloc_all(&sc->sc_bus, + USB_GET_DMA_TAG(self), &ehci_iterate_hw_softc)) { + return (ENOMEM); + } + + sc->sc_bus.usbrev = USB_REV_2_0; + + rid = 0; + sc->sc_io_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, + &rid, RF_ACTIVE); + if (!sc->sc_io_res) { + device_printf(self, "Could not map memory\n"); + goto error; + } + sc->sc_io_tag = rman_get_bustag(sc->sc_io_res); + bsh = rman_get_bushandle(sc->sc_io_res); + + /*magic, undocumented initialization*/ + bus_space_write_4((sc)->sc_io_tag, bsh, 0x04, 0x106); + + bus_space_write_4((sc)->sc_io_tag, bsh, 0x40, (3 << 5)|0x2000); + + DELAY(1000); + + sc->sc_io_size = 4096; + + if (bus_space_subregion(sc->sc_io_tag, bsh, 0x4000000, + sc->sc_io_size, &sc->sc_io_hdl) != 0) + panic("%s: unable to subregion USB host registers", + device_get_name(self)); + + rid = 0; + sc->sc_irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid, + RF_SHAREABLE | RF_ACTIVE); + if (sc->sc_irq_res == NULL) { + device_printf(self, "Could not allocate irq\n"); + ehci_ebus_detach(self); + return (ENXIO); + } + + sc->sc_bus.bdev = device_add_child(self, "usbus", -1); + if (!sc->sc_bus.bdev) { + device_printf(self, "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, EHCI_HC_DEVSTR); + + sprintf(sc->sc_vendor, "Cavium"); + + err = bus_setup_intr(self,sc->sc_irq_res, + INTR_TYPE_BIO | INTR_MPSAFE, NULL, + (driver_intr_t *)ehci_interrupt, sc, + &sc->sc_intr_hdl); + if (err) { + device_printf(self, "Could not setup error irq, %d\n", err); + ih_err = NULL; + goto error; + } + + err = ehci_init(sc); + if (!err) { + err = device_probe_and_attach(sc->sc_bus.bdev); + } + if (err) { + device_printf(self, "USB init failed err=%d\n", err); + goto error; + } + return (0); + +error: + ehci_ebus_detach(self); + return (ENXIO); +} + +static int +ehci_ebus_detach(device_t self) +{ + ehci_softc_t *sc = device_get_softc(self); + device_t bdev; + int err; + + if (sc->sc_bus.bdev) { + bdev = sc->sc_bus.bdev; + device_detach(bdev); + device_delete_child(self, bdev); + } + /* during module unload there are lots of children leftover */ + device_delete_all_children(self); + + /* + * disable interrupts that might have been switched on in ehci_init + */ + if (sc->sc_io_res) { + EWRITE4(sc, EHCI_USBINTR, 0); + EWRITE4(sc, USB_BRIDGE_INTR_MASK, 0); + } + if (sc->sc_irq_res && sc->sc_intr_hdl) { + /* + * only call ehci_detach() after ehci_init() + */ + ehci_detach(sc); + + err = bus_teardown_intr(self, sc->sc_irq_res, sc->sc_intr_hdl); + + if (err) + /* XXX or should we panic? */ + device_printf(self, "Could not tear down irq, %d\n", + err); + sc->sc_intr_hdl = NULL; + } + if (sc->sc_irq_res) { + bus_release_resource(self, SYS_RES_IRQ, 1, sc->sc_irq_res); + sc->sc_irq_res = NULL; + } + if (sc->sc_io_res) { + bus_release_resource(self, SYS_RES_MEMORY, 0, + sc->sc_io_res); + sc->sc_io_res = NULL; + } + usb_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc); + + return (0); +} + +static device_method_t ehci_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ehci_ebus_probe), + DEVMETHOD(device_attach, ehci_ebus_attach), + DEVMETHOD(device_detach, ehci_ebus_detach), + DEVMETHOD(device_suspend, ehci_ebus_suspend), + DEVMETHOD(device_resume, ehci_ebus_resume), + DEVMETHOD(device_shutdown, ehci_ebus_shutdown), + + /* Bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + + {0, 0} +}; + +static driver_t ehci_driver = { + "ehci", + ehci_methods, + sizeof(ehci_softc_t), +}; + +static devclass_t ehci_devclass; + +DRIVER_MODULE(ehci, econaarm, ehci_driver, ehci_devclass, 0, 0); +MODULE_DEPEND(ehci, usb, 1, 1, 1); diff --git a/sys/arm/econa/files.econa b/sys/arm/econa/files.econa new file mode 100644 index 0000000..1adb9c1 --- /dev/null +++ b/sys/arm/econa/files.econa @@ -0,0 +1,14 @@ +# $FreeBSD$ +arm/arm/cpufunc_asm_fa526.S standard +arm/econa/econa_machdep.c standard +arm/econa/econa.c standard +arm/econa/timer.c standard +arm/econa/uart_bus_ec.c optional uart +arm/econa/uart_cpu_ec.c optional uart +dev/uart/uart_dev_ns8250.c optional uart +arm/arm/irq_dispatch.S standard +arm/arm/bus_space_generic.c standard +arm/econa/ehci_ebus.c standard ehci +arm/econa/ohci_ec.c standard ohci +arm/econa/if_ece.c standard +arm/econa/cfi_bus_econa.c optional cfi diff --git a/sys/arm/econa/if_ece.c b/sys/arm/econa/if_ece.c new file mode 100644 index 0000000..136860c --- /dev/null +++ b/sys/arm/econa/if_ece.c @@ -0,0 +1,1948 @@ +/*- + * Copyright (c) 2009 Yohanes Nugroho + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef INET +#include +#include +#include +#include +#endif + +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include + +/* "device miibus" required. See GENERIC if you get errors here. */ +#include "miibus_if.h" + +static uint8_t +vlan0_mac[ETHER_ADDR_LEN] = {0x00, 0xaa, 0xbb, 0xcc, 0xdd, 0x19}; + +/* + * Boot loader expects the hardware state to be the same when we + * restart the device (warm boot), so we need to save the initial + * config values. + */ +int initial_switch_config; +int initial_cpu_config; +int initial_port0_config; +int initial_port1_config; + +static inline uint32_t +read_4(struct ece_softc *sc, bus_size_t off) +{ + + return (bus_read_4(sc->mem_res, off)); +} + +static inline void +write_4(struct ece_softc *sc, bus_size_t off, uint32_t val) +{ + + bus_write_4(sc->mem_res, off, val); +} + +#define ECE_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) +#define ECE_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) +#define ECE_LOCK_INIT(_sc) \ + mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->dev), \ + MTX_NETWORK_LOCK, MTX_DEF) + +#define ECE_TXLOCK(_sc) mtx_lock(&(_sc)->sc_mtx_tx) +#define ECE_TXUNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx_tx) +#define ECE_TXLOCK_INIT(_sc) \ + mtx_init(&_sc->sc_mtx_tx, device_get_nameunit(_sc->dev), \ + "ECE TX Lock", MTX_DEF) + +#define ECE_CLEANUPLOCK(_sc) mtx_lock(&(_sc)->sc_mtx_cleanup) +#define ECE_CLEANUPUNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx_cleanup) +#define ECE_CLEANUPLOCK_INIT(_sc) \ + mtx_init(&_sc->sc_mtx_cleanup, device_get_nameunit(_sc->dev), \ + "ECE cleanup Lock", MTX_DEF) + +#define ECE_RXLOCK(_sc) mtx_lock(&(_sc)->sc_mtx_rx) +#define ECE_RXUNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx_rx) +#define ECE_RXLOCK_INIT(_sc) \ + mtx_init(&_sc->sc_mtx_rx, device_get_nameunit(_sc->dev), \ + "ECE RX Lock", MTX_DEF) + +#define ECE_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); +#define ECE_TXLOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx_tx); +#define ECE_RXLOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx_rx); +#define ECE_CLEANUPLOCK_DESTROY(_sc) \ + mtx_destroy(&_sc->sc_mtx_cleanup); + +#define ECE_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED); +#define ECE_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED); + +static devclass_t ece_devclass; + +/* ifnet entry points */ + +static void eceinit_locked(void *); +static void ecestart_locked(struct ifnet *); + +static void eceinit(void *); +static void ecestart(struct ifnet *); +static void ecestop(struct ece_softc *); +static int eceioctl(struct ifnet * ifp, u_long, caddr_t); + +/* bus entry points */ + +static int ece_probe(device_t dev); +static int ece_attach(device_t dev); +static int ece_detach(device_t dev); +static void ece_intr(void *); +static void ece_intr_qf(void *); +static void ece_intr_status(void *xsc); + +/* helper routines */ +static int ece_activate(device_t dev); +static void ece_deactivate(device_t dev); +static int ece_ifmedia_upd(struct ifnet *ifp); +static void ece_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr); +static int ece_get_mac(struct ece_softc *sc, u_char *eaddr); +static void ece_set_mac(struct ece_softc *sc, u_char *eaddr); +static int configure_cpu_port(struct ece_softc *sc); +static int configure_lan_port(struct ece_softc *sc, int phy_type); +static void set_pvid(struct ece_softc *sc, int port0, int port1, int cpu); +static void set_vlan_vid(struct ece_softc *sc, int vlan); +static void set_vlan_member(struct ece_softc *sc, int vlan); +static void set_vlan_tag(struct ece_softc *sc, int vlan); +static int hardware_init(struct ece_softc *sc); +static void ece_intr_rx_locked(struct ece_softc *sc, int count); + +static void ece_free_desc_dma_tx(struct ece_softc *sc); +static void ece_free_desc_dma_rx(struct ece_softc *sc); + +static void ece_intr_task(void *arg, int pending __unused); +static void ece_tx_task(void *arg, int pending __unused); +static void ece_cleanup_task(void *arg, int pending __unused); + +static int ece_allocate_dma(struct ece_softc *sc); + +static void ece_intr_tx(void *xsc); + +static void clear_mac_entries(struct ece_softc *ec, int include_this_mac); + +static uint32_t read_mac_entry(struct ece_softc *ec, + uint8_t *mac_result, + int first); + +/*PHY related functions*/ +static inline int +phy_read(struct ece_softc *sc, int phy, int reg) +{ + int val; + int ii; + int status; + + write_4(sc, PHY_CONTROL, PHY_RW_OK); + write_4(sc, PHY_CONTROL, + (PHY_ADDRESS(phy)|PHY_READ_COMMAND | + PHY_REGISTER(reg))); + + for (ii = 0; ii < 0x1000; ii++) { + status = read_4(sc, PHY_CONTROL); + if (status & PHY_RW_OK) { + /* Clear the rw_ok status, and clear other + * bits value. */ + write_4(sc, PHY_CONTROL, PHY_RW_OK); + val = PHY_GET_DATA(status); + return (val); + } + } + return (0); +} + +static inline void +phy_write(struct ece_softc *sc, int phy, int reg, int data) +{ + int ii; + + write_4(sc, PHY_CONTROL, PHY_RW_OK); + write_4(sc, PHY_CONTROL, + PHY_ADDRESS(phy) | PHY_REGISTER(reg) | + PHY_WRITE_COMMAND | PHY_DATA(data)); + for (ii = 0; ii < 0x1000; ii++) { + if (read_4(sc, PHY_CONTROL) & PHY_RW_OK) { + /* Clear the rw_ok status, and clear other + * bits value. + */ + write_4(sc, PHY_CONTROL, PHY_RW_OK); + return; + } + } +} + +static int get_phy_type(struct ece_softc *sc) +{ + uint16_t phy0_id = 0, phy1_id = 0; + + /* + * Use SMI (MDC/MDIO) to read Link Partner's PHY Identifier + * Register 1. + */ + phy0_id = phy_read(sc, 0, 0x2); + phy1_id = phy_read(sc, 1, 0x2); + + if ((phy0_id == 0xFFFF) && (phy1_id == 0x000F)) + return (ASIX_GIGA_PHY); + else if ((phy0_id == 0x0243) && (phy1_id == 0x0243)) + return (TWO_SINGLE_PHY); + else if ((phy0_id == 0xFFFF) && (phy1_id == 0x0007)) + return (VSC8601_GIGA_PHY); + else if ((phy0_id == 0x0243) && (phy1_id == 0xFFFF)) + return (IC_PLUS_PHY); + + return (NOT_FOUND_PHY); +} + +static int +ece_probe(device_t dev) +{ + + device_set_desc(dev, "Econa Ethernet Controller"); + return (0); +} + + +static int +ece_attach(device_t dev) +{ + struct ece_softc *sc; + struct ifnet *ifp = NULL; + struct sysctl_ctx_list *sctx; + struct sysctl_oid *soid; + u_char eaddr[ETHER_ADDR_LEN]; + int err; + int i, rid; + uint32_t rnd; + + err = 0; + + sc = device_get_softc(dev); + + sc->dev = dev; + + rid = 0; + sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (sc->mem_res == NULL) + goto out; + + power_on_network_interface(); + + rid = 0; + sc->irq_res_status = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_ACTIVE); + if (sc->irq_res_status == NULL) + goto out; + + rid = 1; + /*TSTC: Fm-Switch-Tx-Complete*/ + sc->irq_res_tx = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_ACTIVE); + if (sc->irq_res_tx == NULL) + goto out; + + rid = 2; + /*FSRC: Fm-Switch-Rx-Complete*/ + sc->irq_res_rec = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_ACTIVE); + if (sc->irq_res_rec == NULL) + goto out; + + rid = 4; + /*FSQF: Fm-Switch-Queue-Full*/ + sc->irq_res_qf = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_ACTIVE); + if (sc->irq_res_qf == NULL) + goto out; + + err = ece_activate(dev); + if (err) + goto out; + + /* Sysctls */ + sctx = device_get_sysctl_ctx(dev); + soid = device_get_sysctl_tree(dev); + + ECE_LOCK_INIT(sc); + + callout_init_mtx(&sc->tick_ch, &sc->sc_mtx, 0); + + if ((err = ece_get_mac(sc, eaddr)) != 0) { + /* No MAC address configured. Generate the random one. */ + if (bootverbose) + device_printf(dev, + "Generating random ethernet address.\n"); + rnd = arc4random(); + + /*from if_ae.c/if_ate.c*/ + /* + * Set OUI to convenient locally assigned address. 'b' + * is 0x62, which has the locally assigned bit set, and + * the broadcast/multicast bit clear. + */ + eaddr[0] = 'b'; + eaddr[1] = 's'; + eaddr[2] = 'd'; + eaddr[3] = (rnd >> 16) & 0xff; + eaddr[4] = (rnd >> 8) & 0xff; + eaddr[5] = rnd & 0xff; + + for (i = 0; i < ETHER_ADDR_LEN; i++) + eaddr[i] = vlan0_mac[i]; + } + ece_set_mac(sc, eaddr); + sc->ifp = ifp = if_alloc(IFT_ETHER); + if (mii_phy_probe(dev, &sc->miibus, ece_ifmedia_upd, + ece_ifmedia_sts)) { + device_printf(dev, "Cannot find my PHY.\n"); + err = ENXIO; + goto out; + } + ifp->if_softc = sc; + if_initname(ifp, device_get_name(dev), device_get_unit(dev)); + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + + ifp->if_capabilities = IFCAP_HWCSUM; + + ifp->if_hwassist = (CSUM_IP | CSUM_TCP | CSUM_UDP); + ifp->if_capenable = ifp->if_capabilities; + ifp->if_start = ecestart; + ifp->if_ioctl = eceioctl; + ifp->if_init = eceinit; + ifp->if_snd.ifq_drv_maxlen = ECE_MAX_TX_BUFFERS - 1; + IFQ_SET_MAXLEN(&ifp->if_snd, ECE_MAX_TX_BUFFERS - 1); + IFQ_SET_READY(&ifp->if_snd); + + /* Create local taskq. */ + + TASK_INIT(&sc->sc_intr_task, 0, ece_intr_task, sc); + TASK_INIT(&sc->sc_tx_task, 1, ece_tx_task, ifp); + TASK_INIT(&sc->sc_cleanup_task, 2, ece_cleanup_task, sc); + sc->sc_tq = taskqueue_create_fast("ece_taskq", M_WAITOK, + taskqueue_thread_enqueue, + &sc->sc_tq); + if (sc->sc_tq == NULL) { + device_printf(sc->dev, "could not create taskqueue\n"); + goto out; + } + + ether_ifattach(ifp, eaddr); + + /* + * Activate interrupts + */ + err = bus_setup_intr(dev, sc->irq_res_rec, INTR_TYPE_NET | INTR_MPSAFE, + NULL, ece_intr, sc, &sc->intrhand); + if (err) { + ether_ifdetach(ifp); + ECE_LOCK_DESTROY(sc); + goto out; + } + + err = bus_setup_intr(dev, sc->irq_res_status, + INTR_TYPE_NET | INTR_MPSAFE, + NULL, ece_intr_status, sc, &sc->intrhand_status); + if (err) { + ether_ifdetach(ifp); + ECE_LOCK_DESTROY(sc); + goto out; + } + + err = bus_setup_intr(dev, sc->irq_res_qf, INTR_TYPE_NET | INTR_MPSAFE, + NULL,ece_intr_qf, sc, &sc->intrhand_qf); + + if (err) { + ether_ifdetach(ifp); + ECE_LOCK_DESTROY(sc); + goto out; + } + + err = bus_setup_intr(dev, sc->irq_res_tx, INTR_TYPE_NET | INTR_MPSAFE, + NULL, ece_intr_tx, sc, &sc->intrhand_tx); + + if (err) { + ether_ifdetach(ifp); + ECE_LOCK_DESTROY(sc); + goto out; + } + + ECE_TXLOCK_INIT(sc); + ECE_RXLOCK_INIT(sc); + ECE_CLEANUPLOCK_INIT(sc); + + /* Enable all interrupt sources. */ + write_4(sc, INTERRUPT_MASK, 0x00000000); + + /* Enable port 0. */ + write_4(sc, PORT_0_CONFIG, read_4(sc, PORT_0_CONFIG) & ~(PORT_DISABLE)); + + taskqueue_start_threads(&sc->sc_tq, 1, PI_NET, "%s taskq", + device_get_nameunit(sc->dev)); + +out:; + if (err) + ece_deactivate(dev); + if (err && ifp) + if_free(ifp); + return (err); +} + +static int +ece_detach(device_t dev) +{ + struct ece_softc *sc = device_get_softc(dev); + struct ifnet *ifp = sc->ifp; + + ecestop(sc); + if (ifp != NULL) { + ether_ifdetach(ifp); + if_free(ifp); + } + ece_deactivate(dev); + return (0); +} + +static void +ece_getaddr(void *arg, bus_dma_segment_t *segs, int nsegs, int error) +{ + u_int32_t *paddr; + KASSERT(nsegs == 1, ("wrong number of segments, should be 1")); + paddr = arg; + *paddr = segs->ds_addr; +} + +static int +ece_alloc_desc_dma_tx(struct ece_softc *sc) +{ + int i; + int error; + + /* Allocate a busdma tag and DMA safe memory for TX/RX descriptors. */ + error = bus_dma_tag_create(sc->sc_parent_tag, /* parent */ + 16, 0, /* alignment, boundary */ + BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filtfunc, filtfuncarg */ + sizeof(eth_tx_desc_t)*ECE_MAX_TX_BUFFERS, /* max size */ + 1, /*nsegments */ + sizeof(eth_tx_desc_t)*ECE_MAX_TX_BUFFERS, + 0, /* flags */ + NULL, NULL, /* lockfunc, lockfuncarg */ + &sc->dmatag_data_tx); /* dmat */ + + /* Allocate memory for TX ring. */ + error = bus_dmamem_alloc(sc->dmatag_data_tx, + (void**)&(sc->desc_tx), + BUS_DMA_NOWAIT | BUS_DMA_ZERO | + BUS_DMA_COHERENT, + &(sc->dmamap_ring_tx)); + + if (error) { + if_printf(sc->ifp, "failed to allocate DMA memory\n"); + bus_dma_tag_destroy(sc->dmatag_data_tx); + sc->dmatag_data_tx = 0; + return (ENXIO); + } + + /* Load Ring DMA. */ + error = bus_dmamap_load(sc->dmatag_data_tx, sc->dmamap_ring_tx, + sc->desc_tx, + sizeof(eth_tx_desc_t)*ECE_MAX_TX_BUFFERS, + ece_getaddr, + &(sc->ring_paddr_tx), BUS_DMA_NOWAIT); + + if (error) { + if_printf(sc->ifp, "can't load descriptor\n"); + bus_dmamem_free(sc->dmatag_data_tx, sc->desc_tx, + sc->dmamap_ring_tx); + sc->desc_tx = NULL; + bus_dma_tag_destroy(sc->dmatag_data_tx); + sc->dmatag_data_tx = 0; + return (ENXIO); + } + + /* Allocate a busdma tag for mbufs. Alignment is 2 bytes */ + error = bus_dma_tag_create(sc->sc_parent_tag, /* parent */ + 1, 0, /* alignment, boundary */ + BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filtfunc, filtfuncarg */ + MCLBYTES*MAX_FRAGMENT, /* maxsize */ + MAX_FRAGMENT, /* nsegments */ + MCLBYTES, 0, /* maxsegsz, flags */ + NULL, NULL, /* lockfunc, lockfuncarg */ + &sc->dmatag_ring_tx); /* dmat */ + + if (error) { + if_printf(sc->ifp, "failed to create busdma tag for mbufs\n"); + return (ENXIO); + } + + for (i = 0; i < ECE_MAX_TX_BUFFERS; i++) { + /* Create dma map for each descriptor. */ + error = bus_dmamap_create(sc->dmatag_ring_tx, 0, + &(sc->tx_desc[i].dmamap)); + if (error) { + if_printf(sc->ifp, "failed to create map for mbuf\n"); + return (ENXIO); + } + } + return (0); +} + +static void +ece_free_desc_dma_tx(struct ece_softc *sc) +{ + int i; + + for (i = 0; i < ECE_MAX_TX_BUFFERS; i++) { + if (sc->tx_desc[i].buff) { + m_freem(sc->tx_desc[i].buff); + sc->tx_desc[i].buff= 0; + } + } + + if (sc->dmamap_ring_tx) { + bus_dmamap_unload(sc->dmatag_data_tx, sc->dmamap_ring_tx); + if (sc->desc_tx) { + bus_dmamem_free(sc->dmatag_data_tx, + sc->desc_tx, sc->dmamap_ring_tx); + } + sc->dmamap_ring_tx = 0; + } + + if (sc->dmatag_data_tx) { + bus_dma_tag_destroy(sc->dmatag_data_tx); + sc->dmatag_data_tx = 0; + } + + if (sc->dmatag_ring_tx) { + for (i = 0; idmatag_ring_tx, + sc->tx_desc[i].dmamap); + sc->tx_desc[i].dmamap = 0; + } + bus_dma_tag_destroy(sc->dmatag_ring_tx); + sc->dmatag_ring_tx = 0; + } +} + +static int +ece_alloc_desc_dma_rx(struct ece_softc *sc) +{ + int error; + int i; + + /* Allocate a busdma tag and DMA safe memory for RX descriptors. */ + error = bus_dma_tag_create(sc->sc_parent_tag, /* parent */ + 16, 0, /* alignment, boundary */ + BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filtfunc, filtfuncarg */ + /* maxsize, nsegments */ + sizeof(eth_rx_desc_t)*ECE_MAX_RX_BUFFERS, 1, + /* maxsegsz, flags */ + sizeof(eth_rx_desc_t)*ECE_MAX_RX_BUFFERS, 0, + NULL, NULL, /* lockfunc, lockfuncarg */ + &sc->dmatag_data_rx); /* dmat */ + + /* Allocate RX ring. */ + error = bus_dmamem_alloc(sc->dmatag_data_rx, + (void**)&(sc->desc_rx), + BUS_DMA_NOWAIT | BUS_DMA_ZERO | + BUS_DMA_COHERENT, + &(sc->dmamap_ring_rx)); + + if (error) { + if_printf(sc->ifp, "failed to allocate DMA memory\n"); + return (ENXIO); + } + + /* Load dmamap. */ + error = bus_dmamap_load(sc->dmatag_data_rx, sc->dmamap_ring_rx, + sc->desc_rx, + sizeof(eth_rx_desc_t)*ECE_MAX_RX_BUFFERS, + ece_getaddr, + &(sc->ring_paddr_rx), BUS_DMA_NOWAIT); + + if (error) { + if_printf(sc->ifp, "can't load descriptor\n"); + bus_dmamem_free(sc->dmatag_data_rx, sc->desc_rx, + sc->dmamap_ring_rx); + bus_dma_tag_destroy(sc->dmatag_data_rx); + sc->desc_rx = NULL; + return (ENXIO); + } + + /* Allocate a busdma tag for mbufs. */ + error = bus_dma_tag_create(sc->sc_parent_tag,/* parent */ + 16, 0, /* alignment, boundary */ + BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filtfunc, filtfuncarg */ + MCLBYTES, 1, /* maxsize, nsegments */ + MCLBYTES, 0, /* maxsegsz, flags */ + NULL, NULL, /* lockfunc, lockfuncarg */ + &sc->dmatag_ring_rx); /* dmat */ + + if (error) { + if_printf(sc->ifp, "failed to create busdma tag for mbufs\n"); + return (ENXIO); + } + + for (i = 0; idmatag_ring_rx, 0, + &sc->rx_desc[i].dmamap); + if (error) { + if_printf(sc->ifp, "failed to create map for mbuf\n"); + return (ENXIO); + } + } + + error = bus_dmamap_create(sc->dmatag_ring_rx, 0, &sc->rx_sparemap); + if (error) { + if_printf(sc->ifp, "failed to create spare map\n"); + return (ENXIO); + } + + return (0); +} + +static void +ece_free_desc_dma_rx(struct ece_softc *sc) +{ + int i; + + for (i = 0; i < ECE_MAX_RX_BUFFERS; i++) { + if (sc->rx_desc[i].buff) { + m_freem(sc->rx_desc[i].buff); + sc->rx_desc[i].buff= 0; + } + } + + if (sc->dmatag_data_rx) { + bus_dmamap_unload(sc->dmatag_data_rx, sc->dmamap_ring_rx); + bus_dmamem_free(sc->dmatag_data_rx, sc->desc_rx, + sc->dmamap_ring_rx); + bus_dma_tag_destroy(sc->dmatag_data_rx); + sc->dmatag_data_rx = 0; + sc->dmamap_ring_rx = 0; + sc->desc_rx = 0; + } + + if (sc->dmatag_ring_rx) { + for (i = 0; i < ECE_MAX_RX_BUFFERS; i++) + bus_dmamap_destroy(sc->dmatag_ring_rx, + sc->rx_desc[i].dmamap); + bus_dmamap_destroy(sc->dmatag_ring_rx, sc->rx_sparemap); + bus_dma_tag_destroy(sc->dmatag_ring_rx); + sc->dmatag_ring_rx = 0; + } +} + +static int +ece_new_rxbuf(struct ece_softc *sc, struct rx_desc_info* descinfo) +{ + struct mbuf *new_mbuf; + bus_dma_segment_t seg[1]; + bus_dmamap_t map; + int error; + int nsegs; + bus_dma_tag_t tag; + + tag = sc->dmatag_ring_rx; + + new_mbuf = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); + + if (new_mbuf == NULL) + return (ENOBUFS); + + new_mbuf->m_len = new_mbuf->m_pkthdr.len = MCLBYTES; + + error = bus_dmamap_load_mbuf_sg(tag, sc->rx_sparemap, new_mbuf, + seg, &nsegs, BUS_DMA_NOWAIT); + + KASSERT(nsegs == 1, ("Too many segments returned!")); + + if (nsegs != 1 || error) { + m_free(new_mbuf); + return (ENOBUFS); + } + + if (descinfo->buff != NULL) { + bus_dmamap_sync(tag, descinfo->dmamap, BUS_DMASYNC_POSTREAD); + bus_dmamap_unload(tag, descinfo->dmamap); + } + + map = descinfo->dmamap; + descinfo->dmamap = sc->rx_sparemap; + sc->rx_sparemap = map; + + bus_dmamap_sync(tag, descinfo->dmamap, BUS_DMASYNC_PREREAD); + + descinfo->buff = new_mbuf; + descinfo->desc->data_ptr = seg->ds_addr; + descinfo->desc->length = seg->ds_len - 2; + + return (0); +} + +static int +ece_allocate_dma(struct ece_softc *sc) +{ + eth_tx_desc_t *desctx; + eth_rx_desc_t *descrx; + int i; + int error; + + /* Create parent tag for tx and rx */ + error = bus_dma_tag_create( + bus_get_dma_tag(sc->dev),/* parent */ + 1, 0, /* alignment, boundary */ + BUS_SPACE_MAXADDR, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + BUS_SPACE_MAXSIZE_32BIT, 0,/* maxsize, nsegments */ + BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ + 0, /* flags */ + NULL, NULL, /* lockfunc, lockarg */ + &sc->sc_parent_tag); + + ece_alloc_desc_dma_tx(sc); + + for (i = 0; i < ECE_MAX_TX_BUFFERS; i++) { + desctx = (eth_tx_desc_t *)(&sc->desc_tx[i]); + memset(desctx, 0, sizeof(eth_tx_desc_t)); + desctx->length = MAX_PACKET_LEN; + desctx->cown = 1; + if (i == ECE_MAX_TX_BUFFERS - 1) + desctx->eor = 1; + } + + ece_alloc_desc_dma_rx(sc); + + for (i = 0; i < ECE_MAX_RX_BUFFERS; i++) { + descrx = &(sc->desc_rx[i]); + memset(descrx, 0, sizeof(eth_rx_desc_t)); + sc->rx_desc[i].desc = descrx; + sc->rx_desc[i].buff = 0; + ece_new_rxbuf(sc, &(sc->rx_desc[i])); + + if (i == ECE_MAX_RX_BUFFERS - 1) + descrx->eor = 1; + } + sc->tx_prod = 0; + sc->tx_cons = 0; + sc->last_rx = 0; + sc->desc_curr_tx = 0; + + return (0); +} + +static int +ece_activate(device_t dev) +{ + struct ece_softc *sc; + int err; + uint32_t mac_port_config; + struct ifnet *ifp; + + sc = device_get_softc(dev); + ifp = sc->ifp; + + initial_switch_config = read_4(sc, SWITCH_CONFIG); + initial_cpu_config = read_4(sc, CPU_PORT_CONFIG); + initial_port0_config = read_4(sc, MAC_PORT_0_CONFIG); + initial_port1_config = read_4(sc, MAC_PORT_1_CONFIG); + + /* Disable Port 0 */ + mac_port_config = read_4(sc, MAC_PORT_0_CONFIG); + mac_port_config |= (PORT_DISABLE); + write_4(sc, MAC_PORT_0_CONFIG, mac_port_config); + + /* Disable Port 1 */ + mac_port_config = read_4(sc, MAC_PORT_1_CONFIG); + mac_port_config |= (PORT_DISABLE); + write_4(sc, MAC_PORT_1_CONFIG, mac_port_config); + + err = ece_allocate_dma(sc); + if (err) { + if_printf(sc->ifp, "failed allocating dma\n"); + goto out; + } + + write_4(sc, TS_DESCRIPTOR_POINTER, sc->ring_paddr_tx); + write_4(sc, TS_DESCRIPTOR_BASE_ADDR, sc->ring_paddr_tx); + + write_4(sc, FS_DESCRIPTOR_POINTER, sc->ring_paddr_rx); + write_4(sc, FS_DESCRIPTOR_BASE_ADDR, sc->ring_paddr_rx); + + write_4(sc, FS_DMA_CONTROL, 1); + + return (0); +out: + return (ENXIO); + +} + +static void +ece_deactivate(device_t dev) +{ + struct ece_softc *sc; + + sc = device_get_softc(dev); + + if (sc->intrhand) + bus_teardown_intr(dev, sc->irq_res_rec, sc->intrhand); + + sc->intrhand = 0; + + if (sc->intrhand_qf) + bus_teardown_intr(dev, sc->irq_res_qf, sc->intrhand_qf); + + sc->intrhand_qf = 0; + + bus_generic_detach(sc->dev); + if (sc->miibus) + device_delete_child(sc->dev, sc->miibus); + if (sc->mem_res) + bus_release_resource(dev, SYS_RES_IOPORT, + rman_get_rid(sc->mem_res), sc->mem_res); + sc->mem_res = 0; + + if (sc->irq_res_rec) + bus_release_resource(dev, SYS_RES_IRQ, + rman_get_rid(sc->irq_res_rec), sc->irq_res_rec); + + if (sc->irq_res_qf) + bus_release_resource(dev, SYS_RES_IRQ, + rman_get_rid(sc->irq_res_qf), sc->irq_res_qf); + + if (sc->irq_res_qf) + bus_release_resource(dev, SYS_RES_IRQ, + rman_get_rid(sc->irq_res_status), sc->irq_res_status); + + sc->irq_res_rec = 0; + sc->irq_res_qf = 0; + sc->irq_res_status = 0; + ECE_TXLOCK_DESTROY(sc); + ECE_RXLOCK_DESTROY(sc); + + ece_free_desc_dma_tx(sc); + ece_free_desc_dma_rx(sc); + + return; +} + +/* + * Change media according to request. + */ +static int +ece_ifmedia_upd(struct ifnet *ifp) +{ + struct ece_softc *sc = ifp->if_softc; + struct mii_data *mii; + int error; + + mii = device_get_softc(sc->miibus); + ECE_LOCK(sc); + error = mii_mediachg(mii); + ECE_UNLOCK(sc); + return (error); +} + +/* + * Notify the world which media we're using. + */ +static void +ece_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) +{ + struct ece_softc *sc = ifp->if_softc; + struct mii_data *mii; + + mii = device_get_softc(sc->miibus); + ECE_LOCK(sc); + mii_pollstat(mii); + ifmr->ifm_active = mii->mii_media_active; + ifmr->ifm_status = mii->mii_media_status; + ECE_UNLOCK(sc); +} + +static void +ece_tick(void *xsc) +{ + struct ece_softc *sc = xsc; + struct mii_data *mii; + int active; + + mii = device_get_softc(sc->miibus); + active = mii->mii_media_active; + mii_tick(mii); + + /* + * Schedule another timeout one second from now. + */ + callout_reset(&sc->tick_ch, hz, ece_tick, sc); +} + +static uint32_t +read_mac_entry(struct ece_softc *ec, + uint8_t *mac_result, + int first) +{ + uint32_t ii; + struct arl_table_entry_t entry; + uint32_t *entry_val; + write_4(ec, ARL_TABLE_ACCESS_CONTROL_0, 0); + write_4(ec, ARL_TABLE_ACCESS_CONTROL_1, 0); + write_4(ec, ARL_TABLE_ACCESS_CONTROL_2, 0); + if (first) + write_4(ec, ARL_TABLE_ACCESS_CONTROL_0, 0x1); + else + write_4(ec, ARL_TABLE_ACCESS_CONTROL_0, 0x2); + + for (ii = 0; ii < 0x1000; ii++) + if (read_4(ec, ARL_TABLE_ACCESS_CONTROL_1) & (0x1)) + break; + + entry_val = (uint32_t*) (&entry); + entry_val[0] = read_4(ec, ARL_TABLE_ACCESS_CONTROL_1); + entry_val[1] = read_4(ec, ARL_TABLE_ACCESS_CONTROL_2); + + if (mac_result) + memcpy(mac_result, entry.mac_addr, ETHER_ADDR_LEN); + + return (entry.table_end); +} + +static uint32_t +write_arl_table_entry(struct ece_softc *ec, + uint32_t filter, + uint32_t vlan_mac, + uint32_t vlan_gid, + uint32_t age_field, + uint32_t port_map, + const uint8_t *mac_addr) +{ + uint32_t ii; + uint32_t *entry_val; + struct arl_table_entry_t entry; + + memset(&entry, 0, sizeof(entry)); + + entry.filter = filter; + entry.vlan_mac = vlan_mac; + entry.vlan_gid = vlan_gid; + entry.age_field = age_field; + entry.port_map = port_map; + memcpy(entry.mac_addr, mac_addr, ETHER_ADDR_LEN); + + entry_val = (uint32_t*) (&entry); + + write_4(ec, ARL_TABLE_ACCESS_CONTROL_0, 0); + write_4(ec, ARL_TABLE_ACCESS_CONTROL_1, 0); + write_4(ec, ARL_TABLE_ACCESS_CONTROL_2, 0); + + write_4(ec, ARL_TABLE_ACCESS_CONTROL_1, entry_val[0]); + write_4(ec, ARL_TABLE_ACCESS_CONTROL_2, entry_val[1]); + + write_4(ec, ARL_TABLE_ACCESS_CONTROL_0, ARL_WRITE_COMMAND); + + for (ii = 0; ii < 0x1000; ii++) + if (read_4(ec, ARL_TABLE_ACCESS_CONTROL_1) & + ARL_COMMAND_COMPLETE) + return (1); /* Write OK. */ + + /* Write failed. */ + return (0); +} + +static void +remove_mac_entry(struct ece_softc *sc, + uint8_t *mac) +{ + + /* Invalid age_field mean erase this entry. */ + write_arl_table_entry(sc, 0, 1, VLAN0_GROUP_ID, + INVALID_ENTRY, VLAN0_GROUP, + mac); +} + +static void +add_mac_entry(struct ece_softc *sc, + uint8_t *mac) +{ + + write_arl_table_entry(sc, 0, 1, VLAN0_GROUP_ID, + NEW_ENTRY, VLAN0_GROUP, + mac); +} + +/** + * The behavior of ARL table reading and deletion is not well defined + * in the documentation. To be safe, all mac addresses are put to a + * list, then deleted. + * + */ +static void +clear_mac_entries(struct ece_softc *ec, int include_this_mac) +{ + int table_end; + struct mac_list * temp; + struct mac_list * mac_list_header; + struct mac_list * current; + char mac[ETHER_ADDR_LEN]; + + current = 0; + mac_list_header = 0; + + table_end = read_mac_entry(ec, mac, 1); + while (!table_end) { + if (!include_this_mac && + memcmp(mac, vlan0_mac, ETHER_ADDR_LEN) == 0) { + /* Read next entry. */ + table_end = read_mac_entry(ec, mac, 0); + continue; + } + + temp = (struct mac_list*)malloc(sizeof(struct mac_list), + M_DEVBUF, + M_NOWAIT | M_ZERO); + memcpy(temp->mac_addr, mac, ETHER_ADDR_LEN); + temp->next = 0; + if (mac_list_header) { + current->next = temp; + current = temp; + } else { + mac_list_header = temp; + current = temp; + } + /* Read next Entry */ + table_end = read_mac_entry(ec, mac, 0); + } + + current = mac_list_header; + + while (current) { + remove_mac_entry(ec, current->mac_addr); + temp = current; + current = current->next; + free(temp, M_DEVBUF); + } +} + +static int +configure_lan_port(struct ece_softc *sc, int phy_type) +{ + uint32_t sw_config; + uint32_t mac_port_config; + + /* + * Configure switch + */ + sw_config = read_4(sc, SWITCH_CONFIG); + /* Enable fast aging. */ + sw_config |= FAST_AGING; + /* Enable IVL learning. */ + sw_config |= IVL_LEARNING; + /* Disable hardware NAT. */ + sw_config &= ~(HARDWARE_NAT); + + sw_config |= SKIP_L2_LOOKUP_PORT_0 | SKIP_L2_LOOKUP_PORT_1| NIC_MODE; + + write_4(sc, SWITCH_CONFIG, sw_config); + + sw_config = read_4(sc, SWITCH_CONFIG); + + mac_port_config = read_4(sc, MAC_PORT_0_CONFIG); + + if (!(mac_port_config & 0x1) || (mac_port_config & 0x2)) + if_printf(sc->ifp, "Link Down\n"); + else + write_4(sc, MAC_PORT_0_CONFIG, mac_port_config); + return (0); +} + +static void +set_pvid(struct ece_softc *sc, int port0, int port1, int cpu) +{ + uint32_t val; + val = read_4(sc, VLAN_PORT_PVID) & (~(0x7 << 0)); + write_4(sc, VLAN_PORT_PVID, val); + val = read_4(sc, VLAN_PORT_PVID) | ((port0) & 0x07); + write_4(sc, VLAN_PORT_PVID, val); + val = read_4(sc, VLAN_PORT_PVID) & (~(0x7 << 4)); + write_4(sc, VLAN_PORT_PVID, val); + val = read_4(sc, VLAN_PORT_PVID) | (((port1) & 0x07) << 4); + write_4(sc, VLAN_PORT_PVID, val); + + val = read_4(sc, VLAN_PORT_PVID) & (~(0x7 << 8)); + write_4(sc, VLAN_PORT_PVID, val); + val = read_4(sc, VLAN_PORT_PVID) | (((cpu) & 0x07) << 8); + write_4(sc, VLAN_PORT_PVID, val); + +} + +/* VLAN related functions */ +static void +set_vlan_vid(struct ece_softc *sc, int vlan) +{ + const uint32_t regs[] = { + VLAN_VID_0_1, + VLAN_VID_0_1, + VLAN_VID_2_3, + VLAN_VID_2_3, + VLAN_VID_4_5, + VLAN_VID_4_5, + VLAN_VID_6_7, + VLAN_VID_6_7 + }; + + const int vids[] = { + VLAN0_VID, + VLAN1_VID, + VLAN2_VID, + VLAN3_VID, + VLAN4_VID, + VLAN5_VID, + VLAN6_VID, + VLAN7_VID + }; + + uint32_t val; + uint32_t reg; + int vid; + + reg = regs[vlan]; + vid = vids[vlan]; + + if (vlan & 1) { + val = read_4(sc, reg); + write_4(sc, reg, val & (~(0xFFF << 0))); + val = read_4(sc, reg); + write_4(sc, reg, val|((vid & 0xFFF) << 0)); + } else { + val = read_4(sc, reg); + write_4(sc, reg, val & (~(0xFFF << 12))); + val = read_4(sc, reg); + write_4(sc, reg, val|((vid & 0xFFF) << 12)); + } +} + +static void +set_vlan_member(struct ece_softc *sc, int vlan) +{ + unsigned char shift; + uint32_t val; + int group; + const int groups[] = { + VLAN0_GROUP, + VLAN1_GROUP, + VLAN2_GROUP, + VLAN3_GROUP, + VLAN4_GROUP, + VLAN5_GROUP, + VLAN6_GROUP, + VLAN7_GROUP + }; + + group = groups[vlan]; + + shift = vlan*3; + val = read_4(sc, VLAN_MEMBER_PORT_MAP) & (~(0x7 << shift)); + write_4(sc, VLAN_MEMBER_PORT_MAP, val); + val = read_4(sc, VLAN_MEMBER_PORT_MAP); + write_4(sc, VLAN_MEMBER_PORT_MAP, val | ((group & 0x7) << shift)); +} + +static void +set_vlan_tag(struct ece_softc *sc, int vlan) +{ + unsigned char shift; + uint32_t val; + + int tag = 0; + + shift = vlan*3; + val = read_4(sc, VLAN_TAG_PORT_MAP) & (~(0x7 << shift)); + write_4(sc, VLAN_TAG_PORT_MAP, val); + val = read_4(sc, VLAN_TAG_PORT_MAP); + write_4(sc, VLAN_TAG_PORT_MAP, val | ((tag & 0x7) << shift)); +} + +static int +configure_cpu_port(struct ece_softc *sc) +{ + uint32_t cpu_port_config; + int i; + + cpu_port_config = read_4(sc, CPU_PORT_CONFIG); + /* SA learning Disable */ + cpu_port_config |= (SA_LEARNING_DISABLE); + /* set data offset + 2 */ + cpu_port_config &= ~(1 << 31); + + write_4(sc, CPU_PORT_CONFIG, cpu_port_config); + + if (!write_arl_table_entry(sc, 0, 1, VLAN0_GROUP_ID, + STATIC_ENTRY, VLAN0_GROUP, + vlan0_mac)) + return (1); + + set_pvid(sc, PORT0_PVID, PORT1_PVID, CPU_PORT_PVID); + + for (i = 0; i < 8; i++) { + set_vlan_vid(sc, i); + set_vlan_member(sc, i); + set_vlan_tag(sc, i); + } + + /* disable all interrupt status sources */ + write_4(sc, INTERRUPT_MASK, 0xffff1fff); + + /* clear previous interrupt sources */ + write_4(sc, INTERRUPT_STATUS, 0x00001FFF); + + write_4(sc, TS_DMA_CONTROL, 0); + write_4(sc, FS_DMA_CONTROL, 0); + return (0); +} + +static int +hardware_init(struct ece_softc *sc) +{ + int status = 0; + static int gw_phy_type; + + gw_phy_type = get_phy_type(sc); + /* Currently only ic_plus phy is supported. */ + if (gw_phy_type != IC_PLUS_PHY) { + device_printf(sc->dev, "PHY type is not supported (%d)\n", + gw_phy_type); + return (-1); + } + status = configure_lan_port(sc, gw_phy_type); + configure_cpu_port(sc); + return (0); +} + +static void +set_mac_address(struct ece_softc *sc, const char *mac, int mac_len) +{ + + /* Invalid age_field mean erase this entry. */ + write_arl_table_entry(sc, 0, 1, VLAN0_GROUP_ID, + INVALID_ENTRY, VLAN0_GROUP, + mac); + memcpy(vlan0_mac, mac, ETHER_ADDR_LEN); + + write_arl_table_entry(sc, 0, 1, VLAN0_GROUP_ID, + STATIC_ENTRY, VLAN0_GROUP, + mac); +} + +static void +ece_set_mac(struct ece_softc *sc, u_char *eaddr) +{ + memcpy(vlan0_mac, eaddr, ETHER_ADDR_LEN); + set_mac_address(sc, eaddr, ETHER_ADDR_LEN); +} + +/* + * TODO: the device doesn't have MAC stored, we should read the + * configuration stored in FLASH, but the format depends on the + * bootloader used.* + */ +static int +ece_get_mac(struct ece_softc *sc, u_char *eaddr) +{ + return (ENXIO); +} + +static void +ece_intr_rx_locked(struct ece_softc *sc, int count) +{ + struct ifnet *ifp = sc->ifp; + struct mbuf *mb; + struct rx_desc_info *rxdesc; + eth_rx_desc_t *desc; + + int fssd_curr; + int fssd; + int i; + int idx; + int rxcount; + uint32_t status; + + fssd_curr = read_4(sc, FS_DESCRIPTOR_POINTER); + + fssd = (fssd_curr - (uint32_t)sc->ring_paddr_rx)>>4; + + desc = sc->rx_desc[sc->last_rx].desc; + + /* Prepare to read the data in the ring. */ + bus_dmamap_sync(sc->dmatag_ring_rx, + sc->dmamap_ring_rx, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + + if (fssd > sc->last_rx) + rxcount = fssd - sc->last_rx; + else if (fssd < sc->last_rx) + rxcount = (ECE_MAX_RX_BUFFERS - sc->last_rx) + fssd; + else { + if (desc->cown == 0) + return; + else + rxcount = ECE_MAX_RX_BUFFERS; + } + + for (i= 0; i < rxcount; i++) { + status = desc->cown; + if (!status) + break; + + idx = sc->last_rx; + rxdesc = &sc->rx_desc[idx]; + mb = rxdesc->buff; + + if (desc->length < ETHER_MIN_LEN - ETHER_CRC_LEN || + desc->length > ETHER_MAX_LEN - ETHER_CRC_LEN + + ETHER_VLAN_ENCAP_LEN) { + ifp->if_ierrors++; + desc->cown = 0; + desc->length = MCLBYTES - 2; + /* Invalid packet, skip and process next + * packet. + */ + continue; + } + + if (ece_new_rxbuf(sc, rxdesc) != 0) { + ifp->if_iqdrops++; + desc->cown = 0; + desc->length = MCLBYTES - 2; + break; + } + + /** + * The device will write to addrress + 2 So we need to adjust + * the address after the packet is received. + */ + mb->m_data += 2; + mb->m_len = mb->m_pkthdr.len = desc->length; + + mb->m_flags |= M_PKTHDR; + mb->m_pkthdr.rcvif = ifp; + if ((ifp->if_capenable & IFCAP_RXCSUM) != 0) { + /*check for valid checksum*/ + if ( (!desc->l4f) && (desc->prot != 3)) { + mb->m_pkthdr.csum_flags |= CSUM_IP_CHECKED; + mb->m_pkthdr.csum_flags |= CSUM_IP_VALID; + mb->m_pkthdr.csum_data = 0xffff; + } + } + ECE_RXUNLOCK(sc); + (*ifp->if_input)(ifp, mb); + ECE_RXLOCK(sc); + + desc->cown = 0; + desc->length = MCLBYTES - 2; + + bus_dmamap_sync(sc->dmatag_ring_rx, + sc->dmamap_ring_rx, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + + if (sc->last_rx == ECE_MAX_RX_BUFFERS - 1) + sc->last_rx = 0; + else + sc->last_rx++; + + desc = sc->rx_desc[sc->last_rx].desc; + } + + /* Sync updated flags. */ + bus_dmamap_sync(sc->dmatag_ring_rx, + sc->dmamap_ring_rx, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + + return; +} + +static void +ece_intr_task(void *arg, int pending __unused) +{ + struct ece_softc *sc = arg; + ECE_RXLOCK(sc); + ece_intr_rx_locked(sc, -1); + ECE_RXUNLOCK(sc); +} + +static void +ece_intr(void *xsc) +{ + struct ece_softc *sc = xsc; + struct ifnet *ifp = sc->ifp; + + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { + write_4(sc, FS_DMA_CONTROL, 0); + return; + } + + taskqueue_enqueue(sc->sc_tq, &sc->sc_intr_task); + + if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) + taskqueue_enqueue(sc->sc_tq, &sc->sc_tx_task); +} + +static void +ece_intr_status(void *xsc) +{ + struct ece_softc *sc = xsc; + struct ifnet *ifp = sc->ifp; + int stat; + + stat = read_4(sc, INTERRUPT_STATUS); + + write_4(sc, INTERRUPT_STATUS, stat); + + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) { + if ((stat & ERROR_MASK) != 0) + ifp->if_iqdrops++; + } +} + +static void +ece_cleanup_locked(struct ece_softc *sc) +{ + eth_tx_desc_t *desc; + + if (sc->tx_cons == sc->tx_prod) return; + + /* Prepare to read the ring (owner bit). */ + bus_dmamap_sync(sc->dmatag_ring_tx, + sc->dmamap_ring_tx, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + + while (sc->tx_cons != sc->tx_prod) { + desc = sc->tx_desc[sc->tx_cons].desc; + if (desc->cown != 0) { + struct tx_desc_info *td = &(sc->tx_desc[sc->tx_cons]); + /* We are finished with this descriptor ... */ + bus_dmamap_sync(sc->dmatag_data_tx, td->dmamap, + BUS_DMASYNC_POSTWRITE); + /* ... and unload, so we can reuse. */ + bus_dmamap_unload(sc->dmatag_data_tx, td->dmamap); + m_freem(td->buff); + td->buff = 0; + sc->tx_cons = (sc->tx_cons + 1) % ECE_MAX_TX_BUFFERS; + } else { + break; + } + } + +} + +static void +ece_cleanup_task(void *arg, int pending __unused) +{ + struct ece_softc *sc = arg; + ECE_CLEANUPLOCK(sc); + ece_cleanup_locked(sc); + ECE_CLEANUPUNLOCK(sc); +} + +static void +ece_intr_tx(void *xsc) +{ + struct ece_softc *sc = xsc; + struct ifnet *ifp = sc->ifp; + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { + /* This should not happen, stop DMA. */ + write_4(sc, FS_DMA_CONTROL, 0); + return; + } + taskqueue_enqueue(sc->sc_tq, &sc->sc_cleanup_task); +} + +static void +ece_intr_qf(void *xsc) +{ + struct ece_softc *sc = xsc; + struct ifnet *ifp = sc->ifp; + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { + /* This should not happen, stop DMA. */ + write_4(sc, FS_DMA_CONTROL, 0); + return; + } + taskqueue_enqueue(sc->sc_tq, &sc->sc_intr_task); + write_4(sc, FS_DMA_CONTROL, 1); +} + +/* + * Reset and initialize the chip + */ +static void +eceinit_locked(void *xsc) +{ + struct ece_softc *sc = xsc; + struct ifnet *ifp = sc->ifp; + struct mii_data *mii; + uint32_t cfg_reg; + uint32_t cpu_port_config; + uint32_t mac_port_config; + + while (1) { + cfg_reg = read_4(sc, BIST_RESULT_TEST_0); + if ((cfg_reg & (1<<17))) + break; + DELAY(100); + } + /* Set to default values. */ + write_4(sc, SWITCH_CONFIG, 0x007AA7A1); + write_4(sc, MAC_PORT_0_CONFIG, 0x00423D00); + write_4(sc, MAC_PORT_1_CONFIG, 0x00423D80); + write_4(sc, CPU_PORT_CONFIG, 0x004C0000); + + hardware_init(sc); + + mac_port_config = read_4(sc, MAC_PORT_0_CONFIG); + + /* Enable Port 0 */ + mac_port_config &= (~(PORT_DISABLE)); + write_4(sc, MAC_PORT_0_CONFIG, mac_port_config); + + cpu_port_config = read_4(sc, CPU_PORT_CONFIG); + /* Enable CPU. */ + cpu_port_config &= ~(PORT_DISABLE); + write_4(sc, CPU_PORT_CONFIG, cpu_port_config); + + /* + * Set 'running' flag, and clear output active flag + * and attempt to start the output + */ + ifp->if_drv_flags |= IFF_DRV_RUNNING; + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + + mii = device_get_softc(sc->miibus); + mii_pollstat(mii); + /* Enable DMA. */ + write_4(sc, FS_DMA_CONTROL, 1); + + callout_reset(&sc->tick_ch, hz, ece_tick, sc); +} + +static inline int +ece_encap(struct ece_softc *sc, struct mbuf *m0) +{ + struct ifnet *ifp; + bus_dma_segment_t segs[MAX_FRAGMENT]; + bus_dmamap_t mapp; + eth_tx_desc_t *desc = 0; + int csum_flags; + int desc_no; + int error; + int nsegs; + int seg; + + ifp = sc->ifp; + + /* Fetch unused map */ + mapp = sc->tx_desc[sc->tx_prod].dmamap; + + error = bus_dmamap_load_mbuf_sg(sc->dmatag_ring_tx, mapp, + m0, segs, &nsegs, + BUS_DMA_NOWAIT); + + if (error != 0) { + bus_dmamap_unload(sc->dmatag_ring_tx, mapp); + return ((error != 0) ? error : -1); + } + + desc = &(sc->desc_tx[sc->desc_curr_tx]); + sc->tx_desc[sc->tx_prod].desc = desc; + sc->tx_desc[sc->tx_prod].buff = m0; + desc_no = sc->desc_curr_tx; + + for (seg = 0; seg < nsegs; seg++) { + if (desc->cown == 0 ) { + if_printf(ifp, "ERROR: descriptor is still used\n"); + return (-1); + } + + desc->length = segs[seg].ds_len; + desc->data_ptr = segs[seg].ds_addr; + + if (seg == 0) { + desc->fs = 1; + } else { + desc->fs = 0; + } + if (seg == nsegs - 1) { + desc->ls = 1; + } else { + desc->ls = 0; + } + + csum_flags = m0->m_pkthdr.csum_flags; + + desc->fr = 1; + desc->pmap = 1; + desc->insv = 0; + desc->ico = 0; + desc->tco = 0; + desc->uco = 0; + desc->interrupt = 1; + + if (csum_flags & CSUM_IP) { + desc->ico = 1; + if (csum_flags & CSUM_TCP) + desc->tco = 1; + if (csum_flags & CSUM_UDP) + desc->uco = 1; + } + + desc++; + sc->desc_curr_tx = (sc->desc_curr_tx + 1) % ECE_MAX_TX_BUFFERS; + if (sc->desc_curr_tx == 0) { + desc = (eth_tx_desc_t *)&(sc->desc_tx[0]); + } + } + + desc = sc->tx_desc[sc->tx_prod].desc; + + sc->tx_prod = (sc->tx_prod + 1) % ECE_MAX_TX_BUFFERS; + + /* + * After all descriptors are set, we set the flags to start the + * sending proces. + */ + for (seg = 0; seg < nsegs; seg++) { + desc->cown = 0; + desc++; + desc_no = (desc_no + 1) % ECE_MAX_TX_BUFFERS; + if (desc_no == 0) + desc = (eth_tx_desc_t *)&(sc->desc_tx[0]); + } + + bus_dmamap_sync(sc->dmatag_data_tx, mapp, BUS_DMASYNC_PREWRITE); + return (0); +} + +/* + * dequeu packets and transmit + */ +static void +ecestart_locked(struct ifnet *ifp) +{ + struct ece_softc *sc; + struct mbuf *m0; + uint32_t queued = 0; + + sc = ifp->if_softc; + if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != + IFF_DRV_RUNNING) + return; + + bus_dmamap_sync(sc->dmatag_ring_tx, + sc->dmamap_ring_tx, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + + for (;;) { + /* Get packet from the queue */ + IF_DEQUEUE(&ifp->if_snd, m0); + if (m0 == NULL) + break; + if (ece_encap(sc, m0)) { + IF_PREPEND(&ifp->if_snd, m0); + ifp->if_drv_flags |= IFF_DRV_OACTIVE; + break; + } + queued++; + BPF_MTAP(ifp, m0); + } + if (queued) { + bus_dmamap_sync(sc->dmatag_ring_tx, sc->dmamap_ring_tx, + BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE); + write_4(sc, TS_DMA_CONTROL, 1); + } +} + +static void +eceinit(void *xsc) +{ + struct ece_softc *sc = xsc; + ECE_LOCK(sc); + eceinit_locked(sc); + ECE_UNLOCK(sc); +} + +static void +ece_tx_task(void *arg, int pending __unused) +{ + struct ifnet *ifp; + ifp = (struct ifnet *)arg; + ecestart(ifp); +} + +static void +ecestart(struct ifnet *ifp) +{ + struct ece_softc *sc = ifp->if_softc; + ECE_TXLOCK(sc); + ecestart_locked(ifp); + ECE_TXUNLOCK(sc); +} + +/* + * Turn off interrupts, and stop the nic. Can be called with sc->ifp + * NULL so be careful. + */ +static void +ecestop(struct ece_softc *sc) +{ + struct ifnet *ifp = sc->ifp; + uint32_t mac_port_config; + + write_4(sc, TS_DMA_CONTROL, 0); + write_4(sc, FS_DMA_CONTROL, 0); + + if (ifp) + ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); + + callout_stop(&sc->tick_ch); + + /*Disable Port 0 */ + mac_port_config = read_4(sc, MAC_PORT_0_CONFIG); + mac_port_config |= (PORT_DISABLE); + write_4(sc, MAC_PORT_0_CONFIG, mac_port_config); + + /*Disable Port 1 */ + mac_port_config = read_4(sc, MAC_PORT_1_CONFIG); + mac_port_config |= (PORT_DISABLE); + write_4(sc, MAC_PORT_1_CONFIG, mac_port_config); + + /* Disable all interrupt status sources. */ + write_4(sc, INTERRUPT_MASK, 0x00001FFF); + + /* Clear previous interrupt sources. */ + write_4(sc, INTERRUPT_STATUS, 0x00001FFF); + + write_4(sc, SWITCH_CONFIG, initial_switch_config); + write_4(sc, CPU_PORT_CONFIG, initial_cpu_config); + write_4(sc, MAC_PORT_0_CONFIG, initial_port0_config); + write_4(sc, MAC_PORT_1_CONFIG, initial_port1_config); + + clear_mac_entries(sc, 1); +} + +static void +ece_restart(struct ece_softc *sc) +{ + struct ifnet *ifp = sc->ifp; + + ifp->if_drv_flags |= IFF_DRV_RUNNING; + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + /* Enable port 0. */ + write_4(sc, PORT_0_CONFIG, + read_4(sc, PORT_0_CONFIG) & ~(PORT_DISABLE)); + write_4(sc, INTERRUPT_MASK, 0x00000000); + write_4(sc, FS_DMA_CONTROL, 1); + callout_reset(&sc->tick_ch, hz, ece_tick, sc); +} + +static void +set_filter(struct ece_softc *sc) +{ + struct ifnet *ifp; + struct ifmultiaddr *ifma; + uint32_t mac_port_config; + + ifp = sc->ifp; + + clear_mac_entries(sc, 0); + if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { + mac_port_config = read_4(sc, MAC_PORT_0_CONFIG); + mac_port_config &= ~(DISABLE_BROADCAST_PACKET); + mac_port_config &= ~(DISABLE_MULTICAST_PACKET); + write_4(sc, MAC_PORT_0_CONFIG, mac_port_config); + return; + } + if_maddr_rlock(ifp); + TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + add_mac_entry(sc, + LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); + } + if_maddr_runlock(ifp); +} + +static int +eceioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + struct ece_softc *sc = ifp->if_softc; + struct mii_data *mii; + struct ifreq *ifr = (struct ifreq *)data; + int mask, error = 0; + + switch (cmd) { + case SIOCSIFFLAGS: + ECE_LOCK(sc); + if ((ifp->if_flags & IFF_UP) == 0 && + ifp->if_drv_flags & IFF_DRV_RUNNING) { + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + ecestop(sc); + } else { + /* Reinitialize card on any parameter change. */ + if ((ifp->if_flags & IFF_UP) && + !(ifp->if_drv_flags & IFF_DRV_RUNNING)) + ece_restart(sc); + } + ECE_UNLOCK(sc); + break; + + case SIOCADDMULTI: + case SIOCDELMULTI: + ECE_LOCK(sc); + set_filter(sc); + ECE_UNLOCK(sc); + break; + + case SIOCSIFMEDIA: + case SIOCGIFMEDIA: + mii = device_get_softc(sc->miibus); + error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd); + break; + case SIOCSIFCAP: + mask = ifp->if_capenable ^ ifr->ifr_reqcap; + if (mask & IFCAP_VLAN_MTU) { + ECE_LOCK(sc); + ECE_UNLOCK(sc); + } + default: + error = ether_ioctl(ifp, cmd, data); + break; + } + return (error); +} + +static void +ece_child_detached(device_t dev, device_t child) +{ + struct ece_softc *sc; + + sc = device_get_softc(dev); + if (child == sc->miibus) + sc->miibus = NULL; +} + +/* + * MII bus support routines. + */ +static int +ece_miibus_readreg(device_t dev, int phy, int reg) +{ + struct ece_softc *sc; + /* Only one phy in this device. */ + if (phy>0) + return (0); + sc = device_get_softc(dev); + return (phy_read(sc, phy, reg)); +} + +static int +ece_miibus_writereg(device_t dev, int phy, int reg, int data) +{ + struct ece_softc *sc; + sc = device_get_softc(dev); + phy_write(sc, phy, reg, data); + return (0); +} + +static device_method_t ece_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ece_probe), + DEVMETHOD(device_attach, ece_attach), + DEVMETHOD(device_detach, ece_detach), + + /* Bus interface */ + DEVMETHOD(bus_child_detached, ece_child_detached), + + /* MII interface */ + DEVMETHOD(miibus_readreg, ece_miibus_readreg), + DEVMETHOD(miibus_writereg, ece_miibus_writereg), + + { 0, 0 } +}; + +static driver_t ece_driver = { + "ece", + ece_methods, + sizeof(struct ece_softc), +}; + +DRIVER_MODULE(ece, econaarm, ece_driver, ece_devclass, 0, 0); +DRIVER_MODULE(miibus, ece, miibus_driver, miibus_devclass, 0, 0); +MODULE_DEPEND(ece, miibus, 1, 1, 1); +MODULE_DEPEND(ece, ether, 1, 1, 1); diff --git a/sys/arm/econa/if_ecereg.h b/sys/arm/econa/if_ecereg.h new file mode 100644 index 0000000..23929e0 --- /dev/null +++ b/sys/arm/econa/if_ecereg.h @@ -0,0 +1,154 @@ +/*- + * Copyright (c) 2009, Yohanes Nugroho + * 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 unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _IF_ECEREG_H +#define _IF_ECEREG_H + +#define ETH_CFG 0x08 +#define ETH_CFG_RMII (1 << 15) +#define PHY_CONTROL 0x00 +#define PHY_RW_OK (1<<15) + +#define PHY_ADDRESS(x) ((x) & 0x1) +#define PHY_REGISTER(r) (((r) & 0x1F) << 8) +#define PHY_WRITE_COMMAND (1<<13) +#define PHY_READ_COMMAND (1<<14) +#define PHY_GET_DATA(d) (((d) >> 16) & 0xFFFF) +#define PHY_DATA(d) (((d) & 0xFFFF) << 16) + +#define PORT_0_CONFIG 0x08 + +#define ARL_TABLE_ACCESS_CONTROL_0 0x050 +#define ARL_TABLE_ACCESS_CONTROL_1 0x054 +#define ARL_TABLE_ACCESS_CONTROL_2 0x058 + +#define ARL_WRITE_COMMAND (1<<3) +#define ARL_LOOKUP_COMMAND (1<<2) +#define ARL_COMMAND_COMPLETE (1) + + +#define PORT0 (1 << 0) +#define PORT1 (1 << 1) +#define CPU_PORT (1 << 2) + + +#define VLAN0_GROUP_ID (0) +#define VLAN1_GROUP_ID (1) +#define VLAN2_GROUP_ID (2) +#define VLAN3_GROUP_ID (3) +#define VLAN4_GROUP_ID (4) +#define VLAN5_GROUP_ID (5) +#define VLAN6_GROUP_ID (6) +#define VLAN7_GROUP_ID (7) + +#define PORT0_PVID (VLAN1_GROUP_ID) +#define PORT1_PVID (VLAN2_GROUP_ID) +#define CPU_PORT_PVID (VLAN0_GROUP_ID) + +#define VLAN0_VID (0x111) +#define VLAN1_VID (0x222) +#define VLAN2_VID (0x333) +#define VLAN3_VID (0x444) +#define VLAN4_VID (0x555) +#define VLAN5_VID (0x666) +#define VLAN6_VID (0x777) +#define VLAN7_VID (0x888) + +#define VLAN0_GROUP (PORT0 | PORT1 | CPU_PORT) +#define VLAN1_GROUP (PORT0 | CPU_PORT) +#define VLAN2_GROUP (PORT1 | CPU_PORT) +#define VLAN3_GROUP (0) +#define VLAN4_GROUP (0) +#define VLAN5_GROUP (0) +#define VLAN6_GROUP (0) +#define VLAN7_GROUP (0) + +#define SWITCH_CONFIG 0x004 +#define MAC_PORT_0_CONFIG 0x008 +#define MAC_PORT_1_CONFIG 0x00C +#define CPU_PORT_CONFIG 0x010 +#define BIST_RESULT_TEST_0 0x094 + +#define FS_DMA_CONTROL 0x104 +#define TS_DMA_CONTROL 0x100 + +#define INTERRUPT_MASK 0x08C +#define INTERRUPT_STATUS 0x088 + +#define TS_DESCRIPTOR_POINTER 0x108 +#define TS_DESCRIPTOR_BASE_ADDR 0x110 +#define FS_DESCRIPTOR_POINTER 0x10C +#define FS_DESCRIPTOR_BASE_ADDR 0x114 + + +#define VLAN_VID_0_1 0x060 +#define VLAN_VID_2_3 0x064 +#define VLAN_VID_4_5 0x068 +#define VLAN_VID_6_7 0x06C + +#define VLAN_PORT_PVID 0x05C +#define VLAN_MEMBER_PORT_MAP 0x070 +#define VLAN_TAG_PORT_MAP 0x074 + + +#define ASIX_GIGA_PHY 1 +#define TWO_SINGLE_PHY 2 +#define AGERE_GIGA_PHY 3 +#define VSC8601_GIGA_PHY 4 +#define IC_PLUS_PHY 5 +#define NOT_FOUND_PHY (-1) + +#define MAX_PACKET_LEN (1536) + +#define INVALID_ENTRY 0 +#define NEW_ENTRY 0x1 +#define STATIC_ENTRY 0x7 + +/*mask status except for link change*/ +#define ERROR_MASK 0xFFFFFF7F + +/*hardware interface flags*/ + +#define FAST_AGING (0xf) +#define IVL_LEARNING (0x1 << 22) +/*hardware NAT accelerator*/ +#define HARDWARE_NAT (0x1 << 23) +/*aging time setting*/ + +/*skip lookup*/ +#define SKIP_L2_LOOKUP_PORT_1 (1 << 29) +#define SKIP_L2_LOOKUP_PORT_0 (1 << 28) + +#define NIC_MODE (1 << 30) +#define PORT_DISABLE (1 << 18) +#define SA_LEARNING_DISABLE (1 << 19) +#define DISABLE_BROADCAST_PACKET (1 << 27) +#define DISABLE_MULTICAST_PACKET ( 1 << 26) + +#endif diff --git a/sys/arm/econa/if_ecevar.h b/sys/arm/econa/if_ecevar.h new file mode 100644 index 0000000..00278ce --- /dev/null +++ b/sys/arm/econa/if_ecevar.h @@ -0,0 +1,193 @@ +/*- + * Copyright (c) 2009 Yohanes Nugroho + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _IFECEVAR_H +#define _IFECEVAR_H + +#define ECE_MAX_TX_BUFFERS 128 +#define ECE_MAX_RX_BUFFERS 128 +#define MAX_FRAGMENT 32 + +typedef struct { + /* 1st 32Bits */ + uint32_t data_ptr; + /* 2nd 32Bits*/ + uint32_t length:16; + + uint32_t tco:1; /*tcp checksum offload*/ + uint32_t uco:1; /*udp checksum offload*/ + uint32_t ico:1; /*ip checksum offload*/ + /* force_route_port_map*/ + uint32_t pmap:3; + /* force_route */ + uint32_t fr:1; + /* force_priority_value */ + uint32_t pri:3; + /* force_priority */ + uint32_t fp:1; + /*interrupt_bit*/ + uint32_t interrupt:1; + /*last_seg*/ + uint32_t ls:1; + /*first_seg*/ + uint32_t fs:1; + /* end_bit */ + uint32_t eor:1; + /* c_bit */ + uint32_t cown:1; + /* 3rd 32Bits*/ + /*vid_index*/ + uint32_t vid:3; + /*insert_vid_tag*/ + uint32_t insv:1; + /*pppoe_section_index*/ + uint32_t sid:3; + /*insert_pppoe_section*/ + uint32_t inss:1; + uint32_t unused:24; + /* 4th 32Bits*/ + uint32_t unused2; + +} eth_tx_desc_t; + +typedef struct{ + uint32_t data_ptr; + uint32_t length:16; + uint32_t l4f:1; + uint32_t ipf:1; + uint32_t prot:2; + uint32_t hr:6; + uint32_t sp:2; + uint32_t ls:1; + uint32_t fs:1; + uint32_t eor:1; + uint32_t cown:1; + uint32_t unused; + uint32_t unused2; +} eth_rx_desc_t; + + +struct rx_desc_info { + struct mbuf*buff; + bus_dmamap_t dmamap; + eth_rx_desc_t *desc; +}; + +struct tx_desc_info { + struct mbuf*buff; + bus_dmamap_t dmamap; + eth_tx_desc_t *desc; +}; + + +struct ece_softc +{ + struct ifnet *ifp; /* ifnet pointer */ + struct mtx sc_mtx; /* global mutex */ + struct mtx sc_mtx_tx; /* tx mutex */ + struct mtx sc_mtx_rx; /* rx mutex */ + struct mtx sc_mtx_cleanup; /* rx mutex */ + + bus_dma_tag_t sc_parent_tag; /* parent bus DMA tag */ + + device_t dev; /* Myself */ + device_t miibus; /* My child miibus */ + void *intrhand; /* Interrupt handle */ + void *intrhand_qf; /* queue full */ + void *intrhand_tx; /* tx complete */ + void *intrhand_status; /* error status */ + + struct resource *irq_res_tx; /* transmit */ + struct resource *irq_res_rec; /* receive */ + struct resource *irq_res_qf; /* queue full */ + struct resource *irq_res_status; /* status */ + + struct resource *mem_res; /* Memory resource */ + + struct callout tick_ch; /* Tick callout */ + + struct taskqueue *sc_tq; + struct task sc_intr_task; + struct task sc_cleanup_task; + struct task sc_tx_task; + + bus_dmamap_t dmamap_ring_tx; + bus_dmamap_t dmamap_ring_rx; + bus_dmamap_t rx_sparemap; + + /*dma tag for ring*/ + bus_dma_tag_t dmatag_ring_tx; + bus_dma_tag_t dmatag_ring_rx; + + /*dma tag for data*/ + bus_dma_tag_t dmatag_data_tx; + bus_dma_tag_t dmatag_data_rx; + + /*the ring*/ + eth_tx_desc_t* desc_tx; + eth_rx_desc_t* desc_rx; + + /*ring physical address*/ + bus_addr_t ring_paddr_tx; + bus_addr_t ring_paddr_rx; + + /*index of last received descriptor*/ + uint32_t last_rx; + struct rx_desc_info rx_desc[ECE_MAX_RX_BUFFERS]; + + /* tx producer index */ + uint32_t tx_prod; + /* tx consumer index */ + uint32_t tx_cons; + /* tx ring index*/ + uint32_t desc_curr_tx; + + struct tx_desc_info tx_desc[ECE_MAX_TX_BUFFERS]; +}; + + +struct arl_table_entry_t { + uint32_t cmd_complete: 1; + uint32_t table_end: 1; + uint32_t search_match: 1; + uint32_t filter:1; /*if set, packet will be dropped */ + uint32_t vlan_mac:1; /*indicates that this is the gateway mac address*/ + uint32_t vlan_gid:3; /*vlan id*/ + uint32_t age_field:3; + uint32_t port_map:3; + /*48 bit mac address*/ + uint8_t mac_addr[6]; + uint8_t pad[2]; +}; + +struct mac_list{ + char mac_addr[6]; + struct mac_list *next; +}; + +#endif diff --git a/sys/arm/econa/ohci_ec.c b/sys/arm/econa/ohci_ec.c new file mode 100644 index 0000000..6bff376 --- /dev/null +++ b/sys/arm/econa/ohci_ec.c @@ -0,0 +1,241 @@ +/*- + * Copyright (c) 2009 Yohanes Nugroho + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include + +#define MEM_RID 0 + +static device_probe_t ohci_ec_probe; +static device_attach_t ohci_ec_attach; +static device_detach_t ohci_ec_detach; + +struct ec_ohci_softc { + struct ohci_softc sc_ohci; /* must be first */ +}; + +static int +ohci_ec_probe(device_t dev) +{ + device_set_desc(dev, "Econa integrated OHCI controller"); + return (BUS_PROBE_DEFAULT); +} + +static int +ohci_ec_attach(device_t dev) +{ + struct ec_ohci_softc *sc = device_get_softc(dev); + bus_space_handle_t bsh; + int err; + int rid; + + /* initialise some bus fields */ + sc->sc_ohci.sc_bus.parent = dev; + sc->sc_ohci.sc_bus.devices = sc->sc_ohci.sc_devices; + sc->sc_ohci.sc_bus.devices_max = OHCI_MAX_DEVICES; + + /* get all DMA memory */ + if (usb_bus_mem_alloc_all(&sc->sc_ohci.sc_bus, + USB_GET_DMA_TAG(dev), &ohci_iterate_hw_softc)) { + return (ENOMEM); + } + sc->sc_ohci.sc_dev = dev; + + rid = MEM_RID; + + sc->sc_ohci.sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, + &rid, RF_ACTIVE); + + if (!(sc->sc_ohci.sc_io_res)) { + err = ENOMEM; + goto error; + } + sc->sc_ohci.sc_io_tag = rman_get_bustag(sc->sc_ohci.sc_io_res); + bsh = rman_get_bushandle(sc->sc_ohci.sc_io_res); + /* Undocumented magic initialization */ + bus_space_write_4((sc)->sc_ohci.sc_io_tag, bsh,0x04, 0x146); + + bus_space_write_4((sc)->sc_ohci.sc_io_tag, bsh,0x44, 0x0200); + + DELAY(1000); + + sc->sc_ohci.sc_io_size = rman_get_size(sc->sc_ohci.sc_io_res); + + if (bus_space_subregion(sc->sc_ohci.sc_io_tag, bsh, 0x4000000, + sc->sc_ohci.sc_io_size, &sc->sc_ohci.sc_io_hdl) != 0) + panic("%s: unable to subregion USB host registers", + device_get_name(dev)); + + rid = 0; + sc->sc_ohci.sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_ACTIVE); + if (!(sc->sc_ohci.sc_irq_res)) { + goto error; + } + sc->sc_ohci.sc_bus.bdev = device_add_child(dev, "usbus", -1); + if (!(sc->sc_ohci.sc_bus.bdev)) { + goto error; + } + device_set_ivars(sc->sc_ohci.sc_bus.bdev, &sc->sc_ohci.sc_bus); + + strlcpy(sc->sc_ohci.sc_vendor, "Cavium", + sizeof(sc->sc_ohci.sc_vendor)); + +#if (__FreeBSD_version >= 700031) + err = bus_setup_intr(dev, sc->sc_ohci.sc_irq_res, + INTR_TYPE_BIO | INTR_MPSAFE, NULL, + (driver_intr_t *)ohci_interrupt, sc, + &sc->sc_ohci.sc_intr_hdl); +#else + err = bus_setup_intr(dev, sc->sc_ohci.sc_irq_res, + INTR_TYPE_BIO | INTR_MPSAFE, + (driver_intr_t *)ohci_interrupt, sc, + &sc->sc_ohci.sc_intr_hdl); +#endif + if (err) { + sc->sc_ohci.sc_intr_hdl = NULL; + goto error; + } + + bus_space_write_4(sc->sc_ohci.sc_io_tag, sc->sc_ohci.sc_io_hdl, + OHCI_CONTROL, 0); + + err = ohci_init(&sc->sc_ohci); + if (!err) { + err = device_probe_and_attach(sc->sc_ohci.sc_bus.bdev); + } + if (err) { + goto error; + } + return (0); + +error: + ohci_ec_detach(dev); + return (ENXIO); +} + +static int +ohci_ec_detach(device_t dev) +{ + struct ec_ohci_softc *sc = device_get_softc(dev); + device_t bdev; + int err; + + if (sc->sc_ohci.sc_bus.bdev) { + bdev = sc->sc_ohci.sc_bus.bdev; + device_detach(bdev); + device_delete_child(dev, bdev); + } + /* during module unload there are lots of children leftover */ + device_delete_all_children(dev); + + bus_space_write_4(sc->sc_ohci.sc_io_tag, sc->sc_ohci.sc_io_hdl, + OHCI_CONTROL, 0); + + if (sc->sc_ohci.sc_irq_res && sc->sc_ohci.sc_intr_hdl) { + /* + * only call ohci_detach() after ohci_init() + */ + ohci_detach(&sc->sc_ohci); + + err = bus_teardown_intr(dev, sc->sc_ohci.sc_irq_res, + sc->sc_ohci.sc_intr_hdl); + sc->sc_ohci.sc_intr_hdl = NULL; + } + if (sc->sc_ohci.sc_irq_res) { + bus_release_resource(dev, SYS_RES_IRQ, 0, + sc->sc_ohci.sc_irq_res); + sc->sc_ohci.sc_irq_res = NULL; + } + if (sc->sc_ohci.sc_io_res) { + bus_release_resource(dev, SYS_RES_MEMORY, MEM_RID, + sc->sc_ohci.sc_io_res); + sc->sc_ohci.sc_io_res = NULL; + } + usb_bus_mem_free_all(&sc->sc_ohci.sc_bus, &ohci_iterate_hw_softc); + + return (0); +} + +static device_method_t ohci_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ohci_ec_probe), + DEVMETHOD(device_attach, ohci_ec_attach), + DEVMETHOD(device_detach, ohci_ec_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + + /* Bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + + {0, 0} +}; + +static driver_t ohci_driver = { + "ohci", + ohci_methods, + sizeof(struct ec_ohci_softc), +}; + +static devclass_t ohci_devclass; + +DRIVER_MODULE(ohci, econaarm, ohci_driver, ohci_devclass, 0, 0); +MODULE_DEPEND(ohci, usb, 1, 1, 1); diff --git a/sys/arm/econa/std.econa b/sys/arm/econa/std.econa new file mode 100644 index 0000000..660648d --- /dev/null +++ b/sys/arm/econa/std.econa @@ -0,0 +1,14 @@ +# $FreeBSD$ + +files "../econa/files.econa" +cpu CPU_FA526 +makeoptions CONF_CFLAGS=-march=armv4 +options PHYSADDR=0x00000000 +makeoptions KERNPHYSADDR=0x01000000 +makeoptions KERNVIRTADDR=0xc1000000 + +options KERNPHYSADDR=0x01000000 +options KERNVIRTADDR=0xc1000000 # Used in ldscript.arm +options FLASHADDR=0xD0000000 +options LOADERRAMADDR=0x00000000 +options STARTUP_PAGETABLE_ADDR=0x00100000 diff --git a/sys/arm/econa/timer.c b/sys/arm/econa/timer.c new file mode 100644 index 0000000..e7cca79 --- /dev/null +++ b/sys/arm/econa/timer.c @@ -0,0 +1,382 @@ +/*- + * Copyright (c) 2009 Yohanes Nugroho . + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "econa_reg.h" +#include "econa_var.h" + +#define INITIAL_TIMECOUNTER (0xffffffff) + +static int timers_initialized = 0; + +#define HZ 100 + +extern unsigned int CPU_clock; +extern unsigned int AHB_clock; +extern unsigned int APB_clock; + +static unsigned long timer_counter = 0; + +struct ec_timer_softc { + struct resource * timer_res[3]; + bus_space_tag_t timer_bst; + bus_space_handle_t timer_bsh; + struct mtx timer_mtx; +}; + +static struct resource_spec ec_timer_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { SYS_RES_IRQ, 0, RF_ACTIVE }, + { SYS_RES_IRQ, 1, RF_ACTIVE }, + { -1, 0 } +}; + +static unsigned ec_timer_get_timecount(struct timecounter *); + +static struct timecounter ec_timecounter = { + .tc_get_timecount = ec_timer_get_timecount, + .tc_name = "CPU Timer", + /* This is assigned on the fly in the init sequence */ + .tc_frequency = 0, + .tc_counter_mask = ~0u, + .tc_quality = 1000, +}; + +static struct ec_timer_softc *timer_softc = NULL; + +static inline +void write_4(unsigned int val, unsigned int addr) +{ + bus_space_write_4(timer_softc->timer_bst, + timer_softc->timer_bsh, addr, val); + +} + +static inline +unsigned int read_4(unsigned int addr) +{ + + return bus_space_read_4(timer_softc->timer_bst, + timer_softc->timer_bsh, addr); +} + +#define uSECS_PER_TICK (1000000 / APB_clock) +#define TICKS2USECS(x) ((x) * uSECS_PER_TICK) + +static unsigned +read_timer_counter_noint(void) +{ + + arm_mask_irq(0); + unsigned int v = read_4(TIMER_TM1_COUNTER_REG); + arm_unmask_irq(0); + return v; +} + +void +DELAY(int usec) +{ + uint32_t val, val_temp; + int nticks; + + if (!timers_initialized) { + for (; usec > 0; usec--) + for (val = 100; val > 0; val--) + ; + return; + } + + val = read_timer_counter_noint(); + nticks = (((APB_clock / 1000) * usec) / 1000) + 100; + + while (nticks > 0) { + val_temp = read_timer_counter_noint(); + if (val > val_temp) + nticks -= (val - val_temp); + else + nticks -= (val + (timer_counter - val_temp)); + + val = val_temp; + } + +} + +/* + * Setup timer + */ +static inline void +setup_timer(unsigned int counter_value) +{ + unsigned int control_value; + unsigned int mask_value; + + control_value = read_4(TIMER_TM_CR_REG); + + mask_value = read_4(TIMER_TM_INTR_MASK_REG); + write_4(counter_value, TIMER_TM1_COUNTER_REG); + write_4(counter_value, TIMER_TM1_LOAD_REG); + write_4(0, TIMER_TM1_MATCH1_REG); + write_4(0,TIMER_TM1_MATCH2_REG); + + control_value &= ~(TIMER1_CLOCK_SOURCE); + control_value |= TIMER1_UP_DOWN_COUNT; + + write_4(0, TIMER_TM2_COUNTER_REG); + write_4(0, TIMER_TM2_LOAD_REG); + write_4(~0u, TIMER_TM2_MATCH1_REG); + write_4(~0u,TIMER_TM2_MATCH2_REG); + + control_value &= ~(TIMER2_CLOCK_SOURCE); + control_value &= ~(TIMER2_UP_DOWN_COUNT); + + mask_value &= ~(63); + + write_4(control_value, TIMER_TM_CR_REG); + write_4(mask_value, TIMER_TM_INTR_MASK_REG); +} + +/* + * Enable timer + */ +static inline void +timer_enable(void) +{ + unsigned int control_value; + + control_value = read_4(TIMER_TM_CR_REG); + + control_value |= TIMER1_OVERFLOW_ENABLE; + control_value |= TIMER1_ENABLE; + control_value |= TIMER2_OVERFLOW_ENABLE; + control_value |= TIMER2_ENABLE; + + write_4(control_value, TIMER_TM_CR_REG); +} + +static inline unsigned int +read_second_timer_counter(void) +{ + + return read_4(TIMER_TM2_COUNTER_REG); +} + +/* + * Get timer interrupt status + */ +static inline unsigned int +read_timer_interrupt_status(void) +{ + + return read_4(TIMER_TM_INTR_STATUS_REG); +} + +/* + * Clear timer interrupt status + */ +static inline void +clear_timer_interrupt_status(unsigned int irq) +{ + unsigned int interrupt_status; + + interrupt_status = read_4(TIMER_TM_INTR_STATUS_REG); + if (irq == 0) { + if (interrupt_status & (TIMER1_MATCH1_INTR)) + interrupt_status &= ~(TIMER1_MATCH1_INTR); + if (interrupt_status & (TIMER1_MATCH2_INTR)) + interrupt_status &= ~(TIMER1_MATCH2_INTR); + if (interrupt_status & (TIMER1_OVERFLOW_INTR)) + interrupt_status &= ~(TIMER1_OVERFLOW_INTR); + } + if (irq == 1) { + if (interrupt_status & (TIMER2_MATCH1_INTR)) + interrupt_status &= ~(TIMER2_MATCH1_INTR); + if (interrupt_status & (TIMER2_MATCH2_INTR)) + interrupt_status &= ~(TIMER2_MATCH2_INTR); + if (interrupt_status & (TIMER2_OVERFLOW_INTR)) + interrupt_status &= ~(TIMER2_OVERFLOW_INTR); + } + + write_4(interrupt_status, TIMER_TM_INTR_STATUS_REG); +} + +static unsigned +ec_timer_get_timecount(struct timecounter *a) +{ + unsigned int ticks1; + arm_mask_irq(1); + ticks1 = read_second_timer_counter(); + arm_unmask_irq(1); + return ticks1; +} + +/* + * Setup timer + */ +static inline void +do_setup_timer(void) +{ + + timer_counter = APB_clock/HZ; + /* + * setup timer-related values + */ + setup_timer(timer_counter); +} + +void +cpu_initclocks(void) +{ + + ec_timecounter.tc_frequency = APB_clock; + tc_init(&ec_timecounter); + timer_enable(); + timers_initialized = 1; +} + +void +cpu_startprofclock(void) +{ + +} + +void +cpu_stopprofclock(void) +{ + +} + +static int +ec_timer_probe(device_t dev) +{ + + device_set_desc(dev, "Econa CPU Timer"); + return (0); +} + +static int +ec_reset(void *arg) +{ + + arm_mask_irq(1); + clear_timer_interrupt_status(1); + arm_unmask_irq(1); + return (FILTER_HANDLED); +} + +static int +ec_hardclock(void *arg) +{ + struct trapframe *frame; + unsigned int val; + /*clear timer interrupt status*/ + + arm_mask_irq(0); + + val = read_4(TIMER_INTERRUPT_STATUS_REG); + val &= ~(TIMER1_OVERFLOW_INTERRUPT); + write_4(val, TIMER_INTERRUPT_STATUS_REG); + + frame = (struct trapframe *)arg; + hardclock(TRAPF_USERMODE(frame), TRAPF_PC(frame)); + + arm_unmask_irq(0); + + return (FILTER_HANDLED); +} + +static int +ec_timer_attach(device_t dev) +{ + struct ec_timer_softc *sc; + int error; + void *ihl; + + + if (timer_softc != NULL) + return (ENXIO); + + sc = (struct ec_timer_softc *)device_get_softc(dev); + + timer_softc = sc; + + error = bus_alloc_resources(dev, ec_timer_spec, sc->timer_res); + if (error) { + device_printf(dev, "could not allocate resources\n"); + return (ENXIO); + } + + sc->timer_bst = rman_get_bustag(sc->timer_res[0]); + sc->timer_bsh = rman_get_bushandle(sc->timer_res[0]); + + do_setup_timer(); + + if (bus_setup_intr(dev, sc->timer_res[1], INTR_TYPE_CLK, + ec_hardclock, NULL, NULL, &ihl) != 0) { + bus_release_resources(dev, ec_timer_spec, sc->timer_res); + device_printf(dev, "could not setup hardclock interrupt\n"); + return (ENXIO); + } + + if (bus_setup_intr(dev, sc->timer_res[2], INTR_TYPE_CLK, + ec_reset, NULL, NULL, &ihl) != 0) { + bus_release_resources(dev, ec_timer_spec, sc->timer_res); + device_printf(dev, "could not setup timer interrupt\n"); + return (ENXIO); + } + + return (0); +} + +static device_method_t ec_timer_methods[] = { + DEVMETHOD(device_probe, ec_timer_probe), + DEVMETHOD(device_attach, ec_timer_attach), + { 0, 0 } +}; + +static driver_t ec_timer_driver = { + "timer", + ec_timer_methods, + sizeof(struct ec_timer_softc), +}; + +static devclass_t ec_timer_devclass; + +DRIVER_MODULE(timer, econaarm, ec_timer_driver, ec_timer_devclass, 0, 0); diff --git a/sys/arm/econa/uart_bus_ec.c b/sys/arm/econa/uart_bus_ec.c new file mode 100644 index 0000000..1a40e7f --- /dev/null +++ b/sys/arm/econa/uart_bus_ec.c @@ -0,0 +1,79 @@ +/*- + * Copyright (C) 2009 Yohanes Nugroho + * All rights reserved. + * + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of MARVELL nor the names of contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include + +static int uart_ec_probe(device_t dev); + +static device_method_t uart_ec_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, uart_ec_probe), + DEVMETHOD(device_attach, uart_bus_attach), + DEVMETHOD(device_detach, uart_bus_detach), + { 0, 0 } +}; + +static driver_t uart_ec_driver = { + uart_driver_name, + uart_ec_methods, + sizeof(struct uart_softc), +}; + +static int +uart_ec_probe(device_t dev) +{ + struct uart_softc *sc; + int status; + + sc = device_get_softc(dev); + sc->sc_class = &uart_ns8250_class; + status = uart_bus_probe(dev, EC_UART_REGSHIFT, EC_UART_CLOCK, 0, 0); + return (status); +} + +DRIVER_MODULE(uart, econaarm, uart_ec_driver, uart_devclass, 0, 0); diff --git a/sys/arm/econa/uart_cpu_ec.c b/sys/arm/econa/uart_cpu_ec.c new file mode 100644 index 0000000..3d42cda --- /dev/null +++ b/sys/arm/econa/uart_cpu_ec.c @@ -0,0 +1,87 @@ +/*- + * Copyright (C) 2009 Yohanes Nugroho + * All rights reserved. + * + * Developed by Semihalf. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of MARVELL nor the names of contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "opt_uart.h" + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#include + +#include +#include + +#include + +#include +#include + +bus_space_tag_t uart_bus_space_io; +bus_space_tag_t uart_bus_space_mem; + +int +uart_cpu_eqres(struct uart_bas *b1, struct uart_bas *b2) +{ + + return ((b1->bsh == b2->bsh && b1->bst == b2->bst) ? 1 : 0); +} + +int +uart_cpu_getdev(int devtype, struct uart_devinfo *di) +{ + struct uart_class *class = &uart_ns8250_class; + + di->ops = uart_getops(class); + di->bas.chan = 0; + di->bas.bst = obio_tag; + + if (bus_space_map(di->bas.bst, ECONA_IO_BASE + ECONA_UART_BASE, + ECONA_UART_SIZE, + 0, &di->bas.bsh) != 0) { + return (ENXIO); + } + + di->baudrate = 0; + di->bas.regshft = EC_UART_REGSHIFT; + di->bas.rclk = EC_UART_CLOCK ; + di->databits = 8; + di->stopbits = 1; + di->parity = UART_PARITY_NONE; + uart_bus_space_mem = obio_tag; + uart_bus_space_io = NULL; + + return (0); +} diff --git a/sys/arm/include/cpuconf.h b/sys/arm/include/cpuconf.h index 458507a..942b591 100644 --- a/sys/arm/include/cpuconf.h +++ b/sys/arm/include/cpuconf.h @@ -61,6 +61,7 @@ defined(CPU_XSCALE_80200) + \ defined(CPU_XSCALE_80321) + \ defined(CPU_XSCALE_PXA2X0) + \ + defined(CPU_FA526) + \ defined(CPU_XSCALE_IXP425)) /* @@ -68,7 +69,7 @@ */ #if (defined(CPU_ARM7TDMI) || defined(CPU_ARM8) || defined(CPU_ARM9) || \ defined(CPU_SA110) || defined(CPU_SA1100) || defined(CPU_SA1110) || \ - defined(CPU_IXP12X0) || defined(CPU_XSCALE_IXP425)) + defined(CPU_IXP12X0) || defined(CPU_XSCALE_IXP425) || defined(CPU_FA526)) #define ARM_ARCH_4 1 #else #define ARM_ARCH_4 0 @@ -125,7 +126,7 @@ #if (defined(CPU_ARM6) || defined(CPU_ARM7) || defined(CPU_ARM7TDMI) || \ defined(CPU_ARM8) || defined(CPU_ARM9) || defined(CPU_ARM9E) || \ - defined(CPU_ARM10) || defined(CPU_ARM11)) + defined(CPU_ARM10) || defined(CPU_ARM11) || defined(CPU_FA526)) #define ARM_MMU_GENERIC 1 #else #define ARM_MMU_GENERIC 0 diff --git a/sys/arm/include/cpufunc.h b/sys/arm/include/cpufunc.h index 74f21e4..8494966 100644 --- a/sys/arm/include/cpufunc.h +++ b/sys/arm/include/cpufunc.h @@ -283,6 +283,28 @@ void arm8_setup (char *string); u_int arm8_clock_config (u_int, u_int); #endif + +#ifdef CPU_FA526 +void fa526_setup (char *arg); +void fa526_setttb (u_int ttb); +void fa526_context_switch (void); +void fa526_cpu_sleep (int); +void fa526_tlb_flushI_SE (u_int); +void fa526_tlb_flushID_SE (u_int); +void fa526_flush_prefetchbuf (void); +void fa526_flush_brnchtgt_E (u_int); + +void fa526_icache_sync_all (void); +void fa526_icache_sync_range(vm_offset_t start, vm_size_t end); +void fa526_dcache_wbinv_all (void); +void fa526_dcache_wbinv_range(vm_offset_t start, vm_size_t end); +void fa526_dcache_inv_range (vm_offset_t start, vm_size_t end); +void fa526_dcache_wb_range (vm_offset_t start, vm_size_t end); +void fa526_idcache_wbinv_all(void); +void fa526_idcache_wbinv_range(vm_offset_t start, vm_size_t end); +#endif + + #ifdef CPU_SA110 void sa110_setup (char *string); void sa110_context_switch (void); @@ -445,6 +467,7 @@ extern unsigned armv5_dcache_index_inc; #if defined(CPU_ARM9) || defined(CPU_ARM9E) || defined(CPU_ARM10) || \ defined(CPU_SA110) || defined(CPU_SA1100) || defined(CPU_SA1110) || \ defined(CPU_XSCALE_80200) || defined(CPU_XSCALE_80321) || \ + defined(CPU_FA526) || \ defined(CPU_XSCALE_PXA2X0) || defined(CPU_XSCALE_IXP425) || \ defined(CPU_XSCALE_80219) || defined(CPU_XSCALE_81342) -- cgit v1.1