summaryrefslogtreecommitdiffstats
path: root/sys/arm
diff options
context:
space:
mode:
authorrpaulo <rpaulo@FreeBSD.org>2010-01-04 03:35:45 +0000
committerrpaulo <rpaulo@FreeBSD.org>2010-01-04 03:35:45 +0000
commit69436c4cabfac51fcdfa42784059f5ae68801cd4 (patch)
tree2fb376f68d2590dd7f858046d845d1af0f99f6ba /sys/arm
parentd3158f2b66a7748dd7e67c5fc04bc181f987c5ce (diff)
downloadFreeBSD-src-69436c4cabfac51fcdfa42784059f5ae68801cd4.zip
FreeBSD-src-69436c4cabfac51fcdfa42784059f5ae68801cd4.tar.gz
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 <yohanes at gmail.com> Reviewed by: freebsd-arm, stas Obtained from: //depot/projects/str91xx/...
Diffstat (limited to 'sys/arm')
-rw-r--r--sys/arm/arm/cpufunc.c140
-rw-r--r--sys/arm/arm/cpufunc_asm_fa526.S209
-rw-r--r--sys/arm/arm/elf_trampoline.c2
-rw-r--r--sys/arm/conf/CNS11XXNAS126
-rw-r--r--sys/arm/conf/CNS11XXNAS.hints1
-rw-r--r--sys/arm/econa/cfi_bus_econa.c67
-rw-r--r--sys/arm/econa/econa.c758
-rw-r--r--sys/arm/econa/econa_machdep.c396
-rw-r--r--sys/arm/econa/econa_reg.h180
-rw-r--r--sys/arm/econa/econa_var.h52
-rw-r--r--sys/arm/econa/ehci_ebus.c300
-rw-r--r--sys/arm/econa/files.econa14
-rw-r--r--sys/arm/econa/if_ece.c1948
-rw-r--r--sys/arm/econa/if_ecereg.h154
-rw-r--r--sys/arm/econa/if_ecevar.h193
-rw-r--r--sys/arm/econa/ohci_ec.c241
-rw-r--r--sys/arm/econa/std.econa14
-rw-r--r--sys/arm/econa/timer.c382
-rw-r--r--sys/arm/econa/uart_bus_ec.c79
-rw-r--r--sys/arm/econa/uart_cpu_ec.c87
-rw-r--r--sys/arm/include/cpuconf.h5
-rw-r--r--sys/arm/include/cpufunc.h23
22 files changed, 5368 insertions, 3 deletions
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 <matt@3am-software.com>
+ *
+ * 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 <machine/asm.h>
+__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 <yohanes@gmail.com>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/sysctl.h>
+
+#include <machine/bus.h>
+
+#include <dev/cfi/cfi_var.h>
+
+#include <arm/econa/econa_reg.h>
+#include <arm/econa/econa_var.h>
+
+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 <yohanes@gmail.com>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/types.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <vm/vm.h>
+#include <vm/vm_kern.h>
+#include <vm/pmap.h>
+#include <vm/vm_page.h>
+#include <vm/vm_extern.h>
+
+#define _ARM32_BUS_DMA_PRIVATE
+#include <machine/bus.h>
+#include <machine/intr.h>
+#include <machine/resource.h>
+
+#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<<nb;
+ write_4(econa_softc, INTC_INTERRUPT_MASK_REG_OFFSET, value);
+}
+
+void
+arm_unmask_irq(uintptr_t nb)
+{
+ unsigned int value;
+
+ value = read_4(econa_softc,
+ INTC_INTERRUPT_CLEAR_EDGE_TRIGGER_REG_OFFSET) | (1 << nb);
+ write_4(econa_softc,
+ INTC_INTERRUPT_CLEAR_EDGE_TRIGGER_REG_OFFSET, value);
+ value = read_4(econa_softc, INTC_INTERRUPT_MASK_REG_OFFSET)& ~(1 << nb);
+ write_4(econa_softc, INTC_INTERRUPT_MASK_REG_OFFSET, value);
+}
+
+int
+arm_get_next_irq(int x)
+{
+ int irq;
+
+ irq = read_4(econa_softc, INTC_INTERRUPT_STATUS_REG_OFFSET) &
+ ~(read_4(econa_softc, INTC_INTERRUPT_MASK_REG_OFFSET));
+
+ if (irq!=0) {
+ return (ffs(irq) - 1);
+ }
+
+ return (-1);
+}
+
+void
+cpu_reset(void)
+{
+ uint32_t control;
+
+ control = system_read_4(econa_softc, RESET_CONTROL);
+ control |= GLOBAL_RESET;
+ system_write_4(econa_softc, RESET_CONTROL, control);
+ control = system_read_4(econa_softc, RESET_CONTROL);
+ control &= (~(GLOBAL_RESET));
+ system_write_4(econa_softc, RESET_CONTROL, control);
+ while (1);
+}
+
+
+
+void
+power_on_network_interface(void)
+{
+ uint32_t cfg_reg;
+ int ii;
+
+ cfg_reg = system_read_4(econa_softc, RESET_CONTROL);
+ cfg_reg |= NET_INTERFACE_RESET;
+ /* set reset bit to HIGH active; */
+ system_write_4(econa_softc, RESET_CONTROL, cfg_reg);
+
+ /*pulse delay */
+ for (ii = 0; ii < 0xFFF; ii++)
+ DELAY(100);
+ /* set reset bit to LOW active; */
+ cfg_reg = system_read_4(econa_softc, RESET_CONTROL);
+ cfg_reg &= ~(NET_INTERFACE_RESET);
+ system_write_4(econa_softc, RESET_CONTROL, cfg_reg);
+
+ /*pulse delay */
+ for (ii = 0; ii < 0xFFF; ii++)
+ DELAY(100);
+ cfg_reg = system_read_4(econa_softc, RESET_CONTROL);
+ cfg_reg |= NET_INTERFACE_RESET;
+ /* set reset bit to HIGH active; */
+ system_write_4(econa_softc, RESET_CONTROL, cfg_reg);
+}
+
+unsigned int
+get_tclk(void)
+{
+
+ return CPU_clock;
+}
+
+static device_method_t econa_methods[] = {
+ DEVMETHOD(device_probe, econa_probe),
+ DEVMETHOD(device_attach, econa_attach),
+ DEVMETHOD(device_identify, econa_identify),
+ DEVMETHOD(bus_alloc_resource, econa_alloc_resource),
+ DEVMETHOD(bus_setup_intr, econa_setup_intr),
+ DEVMETHOD(bus_teardown_intr, econa_teardown_intr),
+ DEVMETHOD(bus_activate_resource, econa_activate_resource),
+ DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
+ DEVMETHOD(bus_get_resource_list, econa_get_resource_list),
+ DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource),
+ DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource),
+ DEVMETHOD(bus_release_resource, econa_release_resource),
+ DEVMETHOD(bus_print_child, econa_print_child),
+ {0, 0},
+};
+
+static driver_t econa_driver = {
+ "econaarm",
+ econa_methods,
+ sizeof(struct econa_softc),
+};
+static devclass_t econa_devclass;
+
+DRIVER_MODULE(econaarm, nexus, econa_driver, econa_devclass, 0, 0);
diff --git a/sys/arm/econa/econa_machdep.c b/sys/arm/econa/econa_machdep.c
new file mode 100644
index 0000000..c4e9806
--- /dev/null
+++ b/sys/arm/econa/econa_machdep.c
@@ -0,0 +1,396 @@
+/*-
+ * Copyright (c) 2009 Yohanes Nugroho <yohanes@gmail.com>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#define _ARM32_BUS_DMA_PRIVATE
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/sysproto.h>
+#include <sys/signalvar.h>
+#include <sys/imgact.h>
+#include <sys/kernel.h>
+#include <sys/ktr.h>
+#include <sys/linker.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mutex.h>
+#include <sys/pcpu.h>
+#include <sys/proc.h>
+#include <sys/ptrace.h>
+#include <sys/cons.h>
+#include <sys/bio.h>
+#include <sys/bus.h>
+#include <sys/buf.h>
+#include <sys/exec.h>
+#include <sys/kdb.h>
+#include <sys/msgbuf.h>
+#include <machine/reg.h>
+#include <machine/cpu.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+#include <vm/vm_object.h>
+#include <vm/vm_page.h>
+#include <vm/vm_pager.h>
+#include <vm/vm_map.h>
+#include <vm/vnode_pager.h>
+#include <machine/pmap.h>
+#include <machine/vmparam.h>
+#include <machine/pcb.h>
+#include <machine/undefined.h>
+#include <machine/machdep.h>
+#include <machine/metadata.h>
+#include <machine/armreg.h>
+#include <machine/bus.h>
+#include <sys/reboot.h>
+#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<<mem_info)*1024*1024;
+ physmem = memsize / PAGE_SIZE;
+
+ /*
+ * Pages were allocated during the secondary bootstrap for the
+ * stacks for different CPU modes.
+ * We must now set the r13 registers in the different CPU modes to
+ * point to these stacks.
+ * Since the ARM stacks use STMFD etc. we must set r13 to the top end
+ * of the stack memory.
+ */
+ cpu_control(CPU_CONTROL_MMU_ENABLE, CPU_CONTROL_MMU_ENABLE);
+
+ set_stackptr(PSR_IRQ32_MODE,
+ irqstack.pv_va + IRQ_STACK_SIZE * PAGE_SIZE);
+ set_stackptr(PSR_ABT32_MODE,
+ abtstack.pv_va + ABT_STACK_SIZE * PAGE_SIZE);
+ set_stackptr(PSR_UND32_MODE,
+ undstack.pv_va + UND_STACK_SIZE * PAGE_SIZE);
+
+ /*
+ * We must now clean the cache again....
+ * Cleaning may be done by reading new data to displace any
+ * dirty data in the cache. This will have happened in setttb()
+ * but since we are boot strapping the addresses used for the read
+ * may have just been remapped and thus the cache could be out
+ * of sync. A re-clean after the switch will cure this.
+ * After booting there are no gross relocations of the kernel thus
+ * this problem will not occur after initarm().
+ */
+ cpu_idcache_wbinv_all();
+
+ /* Set stack for exception handlers */
+ data_abort_handler_address = (u_int)data_abort_handler;
+ prefetch_abort_handler_address = (u_int)prefetch_abort_handler;
+ undefined_handler_address = (u_int)undefinedinstruction_bounce;
+ undefined_init();
+
+ proc_linkup0(&proc0, &thread0);
+ thread0.td_kstack = kernelstack.pv_va;
+ thread0.td_pcb = (struct pcb *)
+ (thread0.td_kstack + KSTACK_PAGES * PAGE_SIZE) - 1;
+ thread0.td_pcb->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 <yohanes@gmail.com>
+ * 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 <yohanes@gmail.com>.
+ * 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 <yohanes@gmail.com>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_bus.h"
+
+#include <machine/resource.h>
+
+#include <sys/stdint.h>
+#include <sys/stddef.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/linker_set.h>
+#include <sys/module.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+
+#include <sys/rman.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+#include <dev/usb/controller/ehci.h>
+#include <dev/usb/controller/ehcireg.h>
+
+
+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 <yohanes@gmail.com>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/sysctl.h>
+#include <sys/taskqueue.h>
+
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+#include <net/if_vlan_var.h>
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#endif
+
+#include <net/bpf.h>
+#include <net/bpfdesc.h>
+
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+
+#include <arm/econa/if_ecereg.h>
+#include <arm/econa/if_ecevar.h>
+#include <arm/econa/econa_var.h>
+
+#include <machine/bus.h>
+#include <machine/intr.h>
+
+/* "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; i<ECE_MAX_TX_BUFFERS; i++) {
+ bus_dmamap_destroy(sc->dmatag_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; i<ECE_MAX_RX_BUFFERS; i++) {
+ error = bus_dmamap_create(sc->dmatag_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 <yohanes@gmail.com>
+ * 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 <yohanes@gmail.com>
+ * 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 <yohanes@gmail.com>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/stdint.h>
+#include <sys/stddef.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/linker_set.h>
+#include <sys/module.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+#include <dev/usb/controller/ohci.h>
+#include <dev/usb/controller/ohcireg.h>
+
+#include <sys/rman.h>
+
+#include <arm/econa/econa_reg.h>
+
+#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 <yohanes@gmail.com>.
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/timetc.h>
+#include <sys/watchdog.h>
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/frame.h>
+#include <machine/intr.h>
+
+#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 <yohanes@gmail.com>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+
+#include <machine/bus.h>
+#include <sys/rman.h>
+#include <machine/resource.h>
+
+#include <dev/uart/uart.h>
+#include <dev/uart/uart_bus.h>
+#include <dev/uart/uart_cpu.h>
+
+#include <arm/econa/econa_reg.h>
+
+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 <yohanes@gmail.com>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/cons.h>
+
+#include <machine/bus.h>
+
+#include <dev/uart/uart.h>
+#include <dev/uart/uart_cpu.h>
+
+#include <sys/rman.h>
+
+#include <arm/econa/econa_reg.h>
+#include <arm/econa/econa_var.h>
+
+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)
OpenPOWER on IntegriCloud