Date: Sat, 26 Dec 2009 00:48:17 +0700 From: Yohanes Nugroho <yohanes@gmail.com> To: freebsd-arm@freebsd.org Subject: Re: CNS11XX FreeBSD port completed Message-ID: <260bb65e0912250948w6f714367w672a1ebf037fb7f7@mail.gmail.com> In-Reply-To: <260bb65e0912110627o6b67b399vabaae57477b91023@mail.gmail.com> References: <260bb65e0912110627o6b67b399vabaae57477b91023@mail.gmail.com>
next in thread | previous in thread | raw e-mail | index | archive | help
[-- Attachment #1 --]
Hi,
To make it easy for others to read the changes I have made, attached
is the diff version against SVN head. There is one change that may be
should not be commited. In vfs_mount.c, I added
pause("WAIT", hz * 10);
That line can be removed if the patch from this:
http://lists.freebsd.org/pipermail/freebsd-current/2009-October/012361.html
is applied
--
Regards
Yohanes
http://yohan.es/
On Fri, Dec 11, 2009 at 9:27 PM, Yohanes Nugroho <yohanes@gmail.com> wrote:
> Hi,
>
> Today I have "completed" the CNS11XX (Cavium Econa ARM formerly known
> as StarSemi STR9104) FreeBSD ARM Port. By "complete" I mean I have
> written all the drivers (usb, network, and flash). I haven't checked
> the correctness of the implementation. The device is a network
> attached storage, Emprex NSD-100, sent to me by Bruce Simpson.
>
> This is my first FreeBSD port, so I think there will be mistakes on my
> code. So I am requesting anyone who have time to have a look at the
> code, or if you have the same device, may be you can try it. You will
> need to solder a serial port to the device to try it.
>
> From the usability point of view, the port is usable. I can put the
> FreeBSD kernel to the flash, and boot it from there with the root
> filesystem on a USB disk (the boot loader also supports booting from
> tftp, and during the development I boot using tftp). The device can
> enter multi user mode, and I can ssh/ftp to the device, and compile
> some things on the device itself.
>
> The only problem left (at least what is visible from user space) is
> the slow network speed (2 megabyte per second, while the linux version
> is around 3.8 megabyte per second). Pyun YongHyeon have helped me a
> lot with the network driver (I am currently asking him to have a final
> look at the driver). I have tried following all of his suggestions to
> make a better (more correct driver), and I suspect the problem is not
> on the network driver itself.
>
> I am suspecting that the problem is in pmap. There has been several
> pmap related problems lately (see
> http://lists.freebsd.org/pipermail/freebsd-arm/2009-October/002030.html).
> It is also possible that the pmap code is now fine, and I have made
> errors when porting the NetBSD CPU code for FA526 to FreeBSD.
>
> If I compile my current code with the latest code from HEAD, and
> activated WITNESS and DIAGNOSTIC i will get the error at err-1.txt,
> and If I activated WITNESS, DIAGNOSTIC and INVARIANT i got this the
> message in err-2.txt. With the same options I didn't get this error
> several months ago .
>
> My code is available at
>
> http://p4db.freebsd.org/depotTreeBrowser.cgi?FSPC=//depot/projects/str91xx/src/sys/arm/econa&HIDEDEL=NO
>
> plus additional code from to support FA526 CPU adapted from NetBSD:
>
> http://p4db.freebsd.org/fileLogView.cgi?FSPC=//depot/projects/str91xx/src/sys/arm/arm/cpufunc_asm_fa526.S
> http://p4db.freebsd.org/fileViewer.cgi?FSPC=//depot/projects/str91xx/src/sys/arm/arm/cpufunc.c
>
> Instruction to compile, and write to flash using dd to cfi0 is available at:
>
> http://tinyhack.com/2009/09/28/cnx11xxstr91xx-freebsd-progress/
> http://tinyhack.com/2009/12/11/cns11xx-freebsd-port-completed/
>
> --
> Regards
> Yohanes
> http://yohan.es/
>
[-- Attachment #2 --]
Index: sys/arm/arm/elf_trampoline.c
===================================================================
--- sys/arm/arm/elf_trampoline.c (revision 200988)
+++ sys/arm/arm/elf_trampoline.c (working copy)
@@ -57,6 +57,8 @@
#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)
Index: sys/arm/arm/cpufunc.c
===================================================================
--- sys/arm/arm/cpufunc.c (revision 200988)
+++ sys/arm/arm/cpufunc.c (working copy)
@@ -781,6 +781,73 @@
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 @@
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 @@
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 @@
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,60 @@
}
#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 */
+ //curcpu()->ci_ctrl = cpuctrl;
+ 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) },
Index: sys/arm/arm/cpufunc_asm_fa526.S
===================================================================
--- sys/arm/arm/cpufunc_asm_fa526.S (revision 0)
+++ sys/arm/arm/cpufunc_asm_fa526.S (revision 0)
@@ -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>
+
+#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, c5, 5 // Enter sleep mode ? invalidate iscratcpad ram*/
+ mcr p15, 0, r0, c7, c0, 4 /* Wait for interrupt (IDLE mode) */
+ 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
+
Index: sys/arm/include/cpuconf.h
===================================================================
--- sys/arm/include/cpuconf.h (revision 200988)
+++ sys/arm/include/cpuconf.h (working copy)
@@ -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
Index: sys/arm/include/cpufunc.h
===================================================================
--- sys/arm/include/cpufunc.h (revision 200988)
+++ sys/arm/include/cpufunc.h (working copy)
@@ -283,6 +283,28 @@
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 @@
#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)
Index: sys/arm/econa/econa_var.h
===================================================================
--- sys/arm/econa/econa_var.h (revision 0)
+++ sys/arm/econa/econa_var.h (revision 0)
@@ -0,0 +1,44 @@
+/*-
+ * Copyright (c) 2009 Yohanes Nugroho All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#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 sc_st;
+ bus_space_handle_t sc_sh;
+ bus_space_handle_t sc_sys_sh;
+ struct rman sc_irq_rman;
+ struct rman sc_mem_rman;
+};
+
+struct econa_ivar {
+ struct resource_list resources;
+};
+
+#endif
Index: sys/arm/econa/files.econa
===================================================================
--- sys/arm/econa/files.econa (revision 0)
+++ sys/arm/econa/files.econa (revision 0)
@@ -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
Index: sys/arm/econa/econa_machdep.c
===================================================================
--- sys/arm/econa/econa_machdep.c (revision 0)
+++ sys/arm/econa/econa_machdep.c (revision 0)
@@ -0,0 +1,430 @@
+/*-
+ * Copyright (c) 2009 Yohanes Nugroho
+ * 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"
+
+#define KERNEL_PT_SYS 0 /* Page table for mapping proc0 zero page */
+#define KERNEL_PT_KERN 1
+#define KERNEL_PT_KERN_NUM 22
+#define KERNEL_PT_AFKERNEL KERNEL_PT_KERN + KERNEL_PT_KERN_NUM /* L2 table for mapping after kernel */
+#define KERNEL_PT_AFKERNEL_NUM 5
+
+/* this should be evenly divisable by PAGE_SIZE / L2_TABLE_SIZE_REAL (or 4) */
+#define NUM_KERNEL_PTS (KERNEL_PT_AFKERNEL + KERNEL_PT_AFKERNEL_NUM)
+
+/* 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
+ */
+ 0x40000000, /*virtual*/
+ 0x40000000, /*physical*/
+ 0x1000000, /*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.
+ */
+ 0x70000000, /*virtual*/
+ 0x70000000, /*physical*/
+ 0xE000000, /*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,
+ },
+ {
+ /*
+ * NET
+ */
+ ECONA_NET_VBASE, /*virtual*/
+ ECONA_NET_PBASE, /*physical*/
+ ECONA_NET_SIZE, /*size 16M*/
+ VM_PROT_READ|VM_PROT_WRITE,
+ PTE_NOCACHE,
+ },
+ {
+ /*
+ * CFI
+ */
+ ECONA_CFI_VBASE, /*virtual*/
+ ECONA_CFI_PBASE, /*physical*/
+ ECONA_CFI_SIZE, /*size 16M*/
+ VM_PROT_READ|VM_PROT_WRITE,
+ PTE_NOCACHE,
+ },
+ {
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ }
+};
+
+
+void *
+initarm(void *arg, void *arg2)
+{
+ struct pv_addr kernel_l1pt;
+ int loop, i;
+ u_int l1pagetable;
+ vm_offset_t freemempos;
+ vm_offset_t afterkern;
+ uint32_t memsize;
+ vm_offset_t lastaddr;
+ volatile uint32_t * ddr = (uint32_t *)0x4000000C;
+
+ boot_arg1 = arg;
+ boot_arg2 = arg2;
+ boothowto = RB_VERBOSE;
+ boothowto |= RB_SINGLE;
+
+ 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();
+
+ memsize = 32*1024*1024;
+
+ switch (((*ddr) >> 4) & 0x3) {
+ case 0:
+ memsize = 8*1024*1024;
+ break;
+ case 1:
+ memsize = 16*1024*1024;
+ break;
+ case 2:
+ memsize = 32*1024*1024;
+ break;
+ case 3:
+ memsize = 64*1024*1024;
+ break;
+ }
+
+
+ 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)));
+}
Index: sys/arm/econa/if_ece.c
===================================================================
--- sys/arm/econa/if_ece.c (revision 0)
+++ sys/arm/econa/if_ece.c (revision 0)
@@ -0,0 +1,2060 @@
+/*-
+ * Copyright (c) 2009 Yohanes Nugroho
+ *
+ * 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: src/sys/arm/econa/if_ece.c$");
+
+#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 <machine/bus.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 <machine/intr.h>
+
+#include "miibus_if.h"
+
+static uint8_t my_vlan0_mac[ETHER_ADDR_LEN] = { 0x00, 0xaa, 0xbb, 0xcc, 0xdd, 0x19 };
+
+
+/*boot loader expects the hardware state to be the same*/
+int initial_switch_config;
+int initial_cpu_config;
+int initial_port0_config;
+int initial_port1_config;
+
+
+static inline uint32_t
+RD4(struct ece_softc *sc, bus_size_t off)
+{
+ return bus_read_4(sc->mem_res, off);
+}
+
+static inline void
+WR4(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 void poweron(void);
+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;
+
+ WR4(sc, PHY_CONTROL, 1 << 15);
+ WR4(sc, PHY_CONTROL, ((phy & 0x1) |
+ ((reg & 0x1F) << 8) | (0x1 << 14)));
+
+ for (ii = 0; ii < 0x1000; ii++) {
+ status = RD4(sc, PHY_CONTROL);
+ if (status & (0x1 << 15)) {
+ /* clear the rw_ok status, and clear other bits value */
+ WR4(sc, PHY_CONTROL, (0x1 << 15));
+ val = ((status >> 16) & 0xFFFF);
+ return val;
+ }
+ }
+ return 0;
+}
+
+
+static inline void
+phy_write(struct ece_softc *sc, int phy, int reg, int data)
+{
+ int ii;
+
+ WR4(sc, PHY_CONTROL, 1 << 15);
+ WR4(sc, PHY_CONTROL,
+ ((phy & 0x1) | ((reg & 0x1F) << 8) |
+ (0x1 << 13) | ((data & 0xFFFF) << 16)));
+ for (ii = 0; ii < 0x1000; ii++) {
+ if (RD4(sc, PHY_CONTROL) & (0x1 << 15)) {
+ /* clear the rw_ok status, and clear other bits value */
+ WR4(sc, PHY_CONTROL, (0x1 << 15));
+ return;
+ }
+ }
+}
+
+
+/*currently only ic_plus phy is supported*/
+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 ((phy1_id == 0xFFFF) && (phy0_id == 0x0243)) {
+ return IC_PLUS_PHY;
+ }
+ return NOT_FOUND_PHY;
+}
+
+static int
+ece_probe(device_t dev)
+{
+ device_set_desc(dev, "Econa Ethernet Controller");
+ return (0);
+}
+
+/*make sure that the interface is not powered off*/
+static void
+poweron(void)
+{
+ int ii;
+ uint32_t cfg_reg;
+ /*TODO: handle power management properly*/
+ volatile uint32_t * power = (uint32_t *)0x77000004;
+
+ cfg_reg = *(power);
+ /* set reset bit to HIGH active; */
+ cfg_reg |= 0x10;
+ (*power) = cfg_reg;
+ /*pulse delay */
+ for (ii = 0; ii < 0xFFF; ii++)
+ DELAY(100); ;
+ /* set reset bit to LOW active; */
+ cfg_reg &= ~0x10;
+ (*power) = cfg_reg;
+ /*pulse delay */
+ for (ii = 0; ii < 0xFFF; ii++)
+ DELAY(100); ;
+ /* set reset bit to HIGH active; */
+ cfg_reg |= 0x10;
+ (*power) = cfg_reg;
+
+}
+
+
+static int
+ece_attach(device_t dev)
+{
+ struct ece_softc *sc = device_get_softc(dev);
+ struct ifnet *ifp = NULL;
+ struct sysctl_ctx_list *sctx;
+ struct sysctl_oid *soid;
+ int err = 0;
+ u_char eaddr[ETHER_ADDR_LEN];
+ uint32_t rnd;
+ int i, rid;
+
+ poweron();
+
+ 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;
+
+ 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;
+
+ sc->use_rmii = (RD4(sc, ETH_CFG) & ETH_CFG_RMII) == ETH_CFG_RMII;
+
+ /* Sysctls */
+ sctx = device_get_sysctl_ctx(dev);
+ soid = device_get_sysctl_tree(dev);
+ SYSCTL_ADD_UINT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "rmii",
+ CTLFLAG_RD, &sc->use_rmii, 0, "rmii in use");
+
+ 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] = my_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*/
+ WR4(sc, INTERRUPT_MASK, 0x00000000);
+
+ /*enable port 0*/
+ WR4(sc, PORT_0_CONFIG, RD4(sc, PORT_0_CONFIG) & ~((0x1 << 18)));
+
+ 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)
+{
+ /*TODO: release resources*/
+
+ 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, 1, /* maxsize, nsegments */
+ sizeof(eth_tx_desc_t)*ECE_MAX_TX_BUFFERS, 0, /* maxsegsz, 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, MAX_FRAGMENT, /* maxsize, 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;
+
+
+ /* 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 */
+ sizeof(eth_rx_desc_t)*ECE_MAX_RX_BUFFERS, 1, /* maxsize, nsegments */
+ sizeof(eth_rx_desc_t)*ECE_MAX_RX_BUFFERS, 0, /* maxsegsz, flags */
+ NULL, NULL, /* lockfunc, lockfuncarg */
+ &sc->dmatag_data_rx); /* dmat */
+
+ /*allocate 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 (int 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;
+#if 0
+ int i;
+ int table_end;
+ char mac[ETHER_ADDR_LEN];
+#endif
+
+ struct ifnet *ifp;
+
+ sc = device_get_softc(dev);
+ ifp = sc->ifp;
+
+ initial_switch_config = RD4(sc, SWITCH_CONFIG);
+ initial_cpu_config = RD4(sc, CPU_PORT_CONFIG);
+ initial_port0_config = RD4(sc, MAC_PORT_0_CONFIG);
+ initial_port1_config = RD4(sc, MAC_PORT_1_CONFIG);
+
+#if 0
+ printf("initial_switch_config %08x\n", initial_switch_config);
+ printf("initial_cpu_config %08x\n", initial_cpu_config);
+ printf("initial_port0_config %08x\n", initial_port0_config);
+ printf("initial_port1_config %08x\n", initial_port1_config);
+
+ WR4(sc, SWITCH_CONFIG, 0x3aa730);
+ WR4(sc, CPU_PORT_CONFIG, 0x804c0000);
+ WR4(sc, MAC_PORT_0_CONFIG, 0xe6463d94);
+ WR4(sc, MAC_PORT_1_CONFIG, 0x463d96);
+
+ table_end = read_mac_entry(sc, mac, 1);
+ while (!table_end) {
+ for (i = 0; i<ETHER_ADDR_LEN; i++) {
+ printf("%02x ", mac[i]);
+ }
+ printf("\n");
+ table_end = read_mac_entry(sc, mac, 0);
+ }
+
+#endif
+ /*Disable Port 0 */
+ mac_port_config = RD4(sc, MAC_PORT_0_CONFIG);
+ mac_port_config |= ((0x1 << 18));
+ WR4(sc, MAC_PORT_0_CONFIG, mac_port_config);
+
+ /*Disable Port 1 */
+ mac_port_config = RD4(sc, MAC_PORT_1_CONFIG);
+ mac_port_config |= ((0x1 << 18));
+ WR4(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;
+ }
+
+
+ WR4(sc, TS_DESCRIPTOR_POINTER, sc->ring_paddr_tx);
+ WR4(sc, TS_DESCRIPTOR_BASE_ADDR, sc->ring_paddr_tx);
+
+ WR4(sc, FS_DESCRIPTOR_POINTER, sc->ring_paddr_rx);
+ WR4(sc, FS_DESCRIPTOR_BASE_ADDR, sc->ring_paddr_rx);
+
+
+ WR4(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;
+ WR4(ec, ARL_TABLE_ACCESS_CONTROL_0, 0);
+ WR4(ec, ARL_TABLE_ACCESS_CONTROL_1, 0);
+ WR4(ec, ARL_TABLE_ACCESS_CONTROL_2, 0);
+ if (first)
+ WR4(ec, ARL_TABLE_ACCESS_CONTROL_0, 0x1);
+ else
+ WR4(ec, ARL_TABLE_ACCESS_CONTROL_0, 0x2);
+
+ for (ii = 0; ii < 0x1000; ii++) {
+ if (RD4(ec, ARL_TABLE_ACCESS_CONTROL_1) & (0x1))
+ break;
+ }
+ entry_val = (uint32_t*) (&entry);
+ entry_val[0] = RD4(ec, ARL_TABLE_ACCESS_CONTROL_1);
+ entry_val[1] = RD4(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);
+
+ WR4(ec, ARL_TABLE_ACCESS_CONTROL_0, 0);
+ WR4(ec, ARL_TABLE_ACCESS_CONTROL_1, 0);
+ WR4(ec, ARL_TABLE_ACCESS_CONTROL_2, 0);
+
+ WR4(ec, ARL_TABLE_ACCESS_CONTROL_1, entry_val[0]);
+ WR4(ec, ARL_TABLE_ACCESS_CONTROL_2, entry_val[1]);
+
+ /* issue the write command */
+ WR4(ec, ARL_TABLE_ACCESS_CONTROL_0, (0x1 << 3));
+
+ for (ii = 0; ii < 0x1000; ii++) {
+ if (RD4(ec, ARL_TABLE_ACCESS_CONTROL_1) & (0x1))
+ 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, my_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 */
+ 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
+ */
+ sw_config = RD4(sc, SWITCH_CONFIG);
+ /* enable fast aging */
+ sw_config |= (0xF);
+ /* CRC stripping */
+ //sw_config |= (0x1 << 21);
+ /* IVL learning */
+ sw_config |= (0x1 << 22);
+ /* HNAT disable */
+ sw_config &= ~(0x1 << 23);
+
+ sw_config |= (1 << 29 | 1 << 30 | 1 << 28);
+
+ WR4(sc, SWITCH_CONFIG, sw_config);
+
+ sw_config = RD4(sc, SWITCH_CONFIG);
+
+ mac_port_config = RD4(sc, MAC_PORT_0_CONFIG);
+
+ if (!(mac_port_config & 0x1) || (mac_port_config & 0x2))
+ if_printf(sc->ifp, "STR9104: Link Down, 0x%08x!\n",
+ mac_port_config);
+ else {
+ WR4(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 = RD4(sc, VLAN_PORT_PVID) & (~(0x7 << 0));
+ WR4(sc, VLAN_PORT_PVID, val);
+ val = RD4(sc, VLAN_PORT_PVID) | ((port0) & 0x07);
+ WR4(sc, VLAN_PORT_PVID, val);
+ val = RD4(sc, VLAN_PORT_PVID) & (~(0x7 << 4));
+ WR4(sc, VLAN_PORT_PVID, val);
+ val = RD4(sc, VLAN_PORT_PVID) | (((port1) & 0x07) << 4);
+ WR4(sc, VLAN_PORT_PVID, val);
+
+ val = RD4(sc, VLAN_PORT_PVID) & (~(0x7 << 8));
+ WR4(sc, VLAN_PORT_PVID, val);
+ val = RD4(sc, VLAN_PORT_PVID) | (((cpu) & 0x07) << 8);
+ WR4(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 = RD4(sc, reg);
+ WR4(sc, reg, val & (~(0xFFF << 0)));
+ val = RD4(sc, reg);
+ WR4(sc, reg, val|((vid & 0xFFF) << 0));
+ } else {
+ val = RD4(sc, reg);
+ WR4(sc, reg, val & (~(0xFFF << 12)));
+ val = RD4(sc, reg);
+ WR4(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 = RD4(sc, VLAN_MEMBER_PORT_MAP) & (~(0x7 << shift));
+ WR4(sc, VLAN_MEMBER_PORT_MAP, val);
+ val = RD4(sc, VLAN_MEMBER_PORT_MAP);
+ WR4(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; /* VLAN0_VLAN_TAG .. VLAN7_VLAN_TAG*/
+
+ shift = vlan*3;
+ val = RD4(sc, VLAN_TAG_PORT_MAP) & (~(0x7 << shift));
+ WR4(sc, VLAN_TAG_PORT_MAP, val);
+ val = RD4(sc, VLAN_TAG_PORT_MAP);
+ WR4(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 = RD4(sc, CPU_PORT_CONFIG);
+ /*SA learning Disable */
+ cpu_port_config |= (0x1 << 19);
+ /*offset 4byte +2 */
+ cpu_port_config &= ~(1 << 31);
+
+
+ WR4(sc, CPU_PORT_CONFIG, cpu_port_config);
+
+ if (!write_arl_table_entry(sc, 0, 1, VLAN0_GROUP_ID,
+ STATIC_ENTRY, VLAN0_GROUP,
+ my_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 */
+ WR4(sc, INTERRUPT_MASK, 0xffff1fff);
+
+ /* clear previous interrupt sources */
+ WR4(sc, INTERRUPT_STATUS, 0x00001FFF);
+
+ WR4(sc, TS_DMA_CONTROL, 0);
+ WR4(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);
+ if (gw_phy_type != IC_PLUS_PHY) {
+ device_printf(sc->dev, "PHY type is not recognized (%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(my_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(my_vlan0_mac, eaddr, ETHER_ADDR_LEN);
+ set_mac_address(sc, eaddr, ETHER_ADDR_LEN);
+}
+
+/* TODO: the device doesn't have MAC,
+ * should read the configuration stored in FLASH
+ */
+static int
+ece_get_mac(struct ece_softc *sc, u_char *eaddr)
+{
+ return (ENXIO);
+}
+
+/*version for one segment only*/
+static void
+ece_intr_rx_locked(struct ece_softc *sc, int count)
+{
+ struct ifnet *ifp = sc->ifp;
+ struct rx_desc_info *rxdesc;
+
+ uint32_t status;
+
+ int fssd_curr;
+ int fssd;
+ int rxcount;
+ int i;
+ int idx;
+ struct mbuf *mb;
+ eth_rx_desc_t *desc;
+
+ fssd_curr = RD4(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++) {
+ /* Get status */
+ 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;
+ /*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 X+2 So we need to adjust
+ * this 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) {
+ WR4(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 = RD4(sc, INTERRUPT_STATUS);
+
+ WR4(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*/
+ /*sync*/
+ 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*/
+ WR4(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*/
+ WR4(sc, FS_DMA_CONTROL, 0);
+ return;
+ }
+ taskqueue_enqueue(sc->sc_tq, &sc->sc_intr_task);
+ WR4(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 = RD4(sc, BIST_RESULT_TEST_0);
+ if ((cfg_reg & (1<<17)))
+ break;
+ DELAY(100);
+ }
+ /* set to default values */
+ WR4(sc, SWITCH_CONFIG, 0x007AA7A1);
+ WR4(sc, MAC_PORT_0_CONFIG, 0x00423D00);
+ WR4(sc, MAC_PORT_1_CONFIG, 0x00423D80);
+ WR4(sc, CPU_PORT_CONFIG, 0x004C0000);
+
+
+ hardware_init(sc);
+
+ mac_port_config = RD4(sc, MAC_PORT_0_CONFIG);
+ mac_port_config &= (~(0x1 << 18)); /* Enable Port 0 */
+ WR4(sc, MAC_PORT_0_CONFIG, mac_port_config);
+
+
+ cpu_port_config = RD4(sc, CPU_PORT_CONFIG);
+ /* enable CPU */
+ cpu_port_config &= ~(0x1 << 18);
+ WR4(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 */
+ WR4(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;
+ int error;
+ int seg;
+ int nsegs;
+ int desc_no;
+ eth_tx_desc_t *desc = 0;
+
+ int csum_flags;
+
+ 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");
+ }
+
+ 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 flag to start the sending process */
+ 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;
+#if 0
+ struct mbuf *mdef;
+#endif
+ 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 0
+ if (m0->m_next != NULL) {
+ mdef = m_defrag(m0, M_DONTWAIT);
+ if (mdef) {
+ m0 = mdef;
+ }
+ }
+#endif
+ 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) {
+ /*sync the ring*/
+ bus_dmamap_sync(sc->dmatag_ring_tx, sc->dmamap_ring_tx,
+ BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
+ WR4(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;
+
+
+ WR4(sc, TS_DMA_CONTROL, 0);
+ WR4(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 = RD4(sc, MAC_PORT_0_CONFIG);
+ mac_port_config |= ((0x1 << 18));
+ WR4(sc, MAC_PORT_0_CONFIG, mac_port_config);
+
+ /*Disable Port 1 */
+ mac_port_config = RD4(sc, MAC_PORT_1_CONFIG);
+ mac_port_config |= ((0x1 << 18));
+ WR4(sc, MAC_PORT_1_CONFIG, mac_port_config);
+
+ /* disable all interrupt status sources */
+ WR4(sc, INTERRUPT_MASK, 0x00001FFF);
+
+ /* clear previous interrupt sources */
+ WR4(sc, INTERRUPT_STATUS, 0x00001FFF);
+
+ WR4(sc, SWITCH_CONFIG, initial_switch_config);
+ WR4(sc, CPU_PORT_CONFIG, initial_cpu_config);
+ WR4(sc, MAC_PORT_0_CONFIG, initial_port0_config);
+ WR4(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*/
+ WR4(sc, PORT_0_CONFIG,
+ RD4(sc, PORT_0_CONFIG) & ~((0x1 << 18)));
+ WR4(sc, INTERRUPT_MASK, 0x00000000);
+ WR4(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 = RD4(sc, MAC_PORT_0_CONFIG);
+ mac_port_config &= (~(0x1 << 27));
+ mac_port_config &= (~(0x1 << 26));
+ WR4(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*/
+ 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);
Index: sys/arm/econa/if_ecereg.h
===================================================================
--- sys/arm/econa/if_ecereg.h (revision 0)
+++ sys/arm/econa/if_ecereg.h (revision 0)
@@ -0,0 +1,135 @@
+/*-
+ * 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: src/sys/dev/jme/if_jmereg.h,v 1.6 2008/12/04 02:16:53 yongari Exp $
+ */
+
+#ifndef _IF_ECEREG_H
+#define _IF_ECEREG_H
+
+#define ETH_CFG 0x08
+#define ETH_CFG_RMII (1 << 15)
+#define PHY_CONTROL 0x00
+#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 PORT0 (1 << 0) /* bit map : bit 0 */
+#define PORT1 (1 << 1) /* bit map : bit 1 */
+#define CPU_PORT (1 << 2) /* bit map : bit 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 VLAN0_VLAN_TAG (0)
+#define VLAN1_VLAN_TAG (0)
+#define VLAN2_VLAN_TAG (0)
+#define VLAN3_VLAN_TAG (0)
+#define VLAN4_VLAN_TAG (0)
+#define VLAN5_VLAN_TAG (0)
+#define VLAN6_VLAN_TAG (0)
+#define VLAN7_VLAN_TAG (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
+
+
+#endif
Index: sys/arm/econa/cfi_bus_econa.c
===================================================================
--- sys/arm/econa/cfi_bus_econa.c (revision 0)
+++ sys/arm/econa/cfi_bus_econa.c (revision 0)
@@ -0,0 +1,66 @@
+/*-
+ * Copyright (c) 2009 Yohanes Nugroho
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <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);
Index: sys/arm/econa/timer.c
===================================================================
--- sys/arm/econa/timer.c (revision 0)
+++ sys/arm/econa/timer.c (revision 0)
@@ -0,0 +1,432 @@
+/*-
+ * Copyright (c) 2009 Yohanes Nugroho. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <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
+
+unsigned int CPU_clock = 200000000;
+unsigned int AHB_clock;
+unsigned int APB_clock;
+
+unsigned int get_tclk(void);
+
+static unsigned long timer_counter = 0, sys_clock = 100000000;
+
+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",
+ .tc_frequency = 0, /* This is assigned on the fly in the init sequence */
+ .tc_counter_mask = ~0u,
+ .tc_quality = 1000,
+};
+
+
+static struct ec_timer_softc *timer_softc = NULL;
+
+static inline
+void WR4(unsigned int val, unsigned int addr)
+{
+ bus_space_write_4(timer_softc->timer_bst,
+ timer_softc->timer_bsh, addr, val);
+
+}
+
+static inline
+unsigned int RD4(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 = RD4(TIMER_TM1_COUNTER_REG_OFFSET);
+ 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 = RD4(TIMER_TM_CR_REG_OFFSET);
+
+ mask_value = RD4(TIMER_TM_INTR_MASK_REG_OFFSET);
+ WR4(counter_value, TIMER_TM1_COUNTER_REG_OFFSET);
+ WR4(counter_value, TIMER_TM1_LOAD_REG_OFFSET);
+ WR4(0, TIMER_TM1_MATCH1_REG_OFFSET);
+ WR4(0,TIMER_TM1_MATCH2_REG_OFFSET);
+
+ control_value &= ~(TIMER1_CLOCK_SOURCE);
+ control_value |= TIMER1_UP_DOWN_COUNT;
+
+ WR4(0, TIMER_TM2_COUNTER_REG_OFFSET);
+ WR4(0, TIMER_TM2_LOAD_REG_OFFSET);
+ WR4(~0u, TIMER_TM2_MATCH1_REG_OFFSET);
+ WR4(~0u,TIMER_TM2_MATCH2_REG_OFFSET);
+
+ control_value &= ~(TIMER2_CLOCK_SOURCE);
+ control_value &= ~(TIMER2_UP_DOWN_COUNT);
+
+ mask_value &= ~(63);
+
+ WR4(control_value, TIMER_TM_CR_REG_OFFSET);
+ WR4(mask_value, TIMER_TM_INTR_MASK_REG_OFFSET);
+}
+
+/*
+ * Enable timer
+ */
+static inline void
+timer_enable(void)
+{
+ unsigned int control_value;
+
+ control_value = RD4(TIMER_TM_CR_REG_OFFSET);
+
+ control_value |= TIMER1_OVERFLOW_ENABLE;
+ control_value |= TIMER1_ENABLE;
+ control_value |= TIMER2_OVERFLOW_ENABLE;
+ control_value |= TIMER2_ENABLE;
+
+ WR4(control_value, TIMER_TM_CR_REG_OFFSET);
+}
+
+
+static inline unsigned int
+read_second_timer_counter(void)
+{
+ return RD4(TIMER_TM2_COUNTER_REG_OFFSET);
+}
+
+
+/*
+ * Get timer interrupt status
+ */
+static inline unsigned int
+read_timer_interrupt_status(void)
+{
+ return RD4(TIMER_TM_INTR_STATUS_REG_OFFSET);
+}
+
+/*
+ * Clear timer interrupt status
+ */
+static inline void
+clear_timer_interrupt_status(unsigned int irq)
+{
+ unsigned int interrupt_status;
+
+ interrupt_status = RD4(TIMER_TM_INTR_STATUS_REG_OFFSET);
+ 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);
+ }
+
+ WR4(interrupt_status, TIMER_TM_INTR_STATUS_REG_OFFSET);
+}
+
+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;
+}
+
+/*
+ * Get STR9100 System Clock
+*/
+static unsigned long
+get_system_clock(void)
+{
+ sys_clock = 100000000;
+
+ switch (((*(unsigned int *)SYSTEM_CLOCK_INFO) >> 6) & 0x3) {
+ case 0:
+ sys_clock = 87500000;
+ CPU_clock = 175000000;
+ break;
+ case 1:
+ sys_clock = 100000000;
+ CPU_clock = 200000000;
+ break;
+ case 2:
+ sys_clock = 112500000;
+ CPU_clock = 225000000;
+ break;
+ case 3:
+ sys_clock = 125000000;
+ CPU_clock = 250000000;
+ break;
+ }
+ AHB_clock = CPU_clock >> 1;
+ APB_clock = AHB_clock >> 1;
+ return sys_clock;
+}
+
+unsigned int
+get_tclk(void)
+{
+ return CPU_clock;
+}
+
+/*
+ * Setup timer
+ */
+static inline void
+do_setup_timer(void)
+{
+ get_system_clock();
+ 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)
+{
+
+}
+
+void
+cpu_reset(void)
+{
+ volatile unsigned int * control = (unsigned int * )CPU_RESET_REGISTER;
+ *control |= (1 << 0);
+ *control &= (~(1 << 0));
+ while (1);
+}
+
+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 = RD4(TIMER_INTERRUPT_STATUS_REG);
+ val &= ~(TIMER1_OVERFLOW_INTERRUPT);
+ WR4(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)
+{
+ int error;
+ void *ihl;
+ struct ec_timer_softc *sc;
+
+ 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);
Index: sys/arm/econa/econa.c
===================================================================
--- sys/arm/econa/econa.c (revision 0)
+++ sys/arm/econa/econa.c (revision 0)
@@ -0,0 +1,656 @@
+/*-
+ * Copyright (c) 2009 Yohanes Nugroho. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <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;
+
+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[] =
+{
+ // All the "system" devices
+ {
+ "econa_ic", 0,
+ ECONA_IO_BASE + ECONA_PIC_BASE, ECONA_PIC_SIZE,
+ 0 // Interrupt controller has no interrupts!
+ },
+ {
+ "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_NET_VBASE, ECONA_NET_SIZE,
+ ECONA_IRQ_STATUS,
+ ECONA_IRQ_TSTC, ECONA_IRQ_FSRC,
+ ECONA_IRQ_TSQE, ECONA_IRQ_FSQF,
+ },
+ { 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
+RD4(struct econa_softc *sc, bus_size_t off)
+{
+ return bus_space_read_4(sc->sc_st, sc->sc_sys_sh, off);
+}
+
+static inline void
+WR4(struct econa_softc *sc, bus_size_t off, uint32_t val)
+{
+ return bus_space_write_4(sc->sc_st, sc->sc_sys_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 = RD4(sc, INTC_INTERRUPT_TRIGGER_MODE_REG_OFFSET);
+
+ if (mode == INTC_LEVEL_TRIGGER) {
+ if (val & (1UL << irq)) {
+ val &= ~(1UL << irq);
+ WR4(sc, INTC_INTERRUPT_TRIGGER_MODE_REG_OFFSET, val);
+ }
+ } else {
+ if (!(val & (1UL << irq))) {
+ val |= (1UL << irq);
+ WR4(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 = RD4(sc, INTC_INTERRUPT_TRIGGER_LEVEL_REG_OFFSET);
+
+ if ((level == INTC_ACTIVE_HIGH) || (level == INTC_RISING_EDGE)) {
+ if (val & (1UL << irq)) {
+ val &= ~(1UL << irq);
+ WR4(sc, INTC_INTERRUPT_TRIGGER_LEVEL_REG_OFFSET, val);
+ }
+ } else {
+ if (!(val & (1UL << irq))) {
+ val |= (1UL << irq);
+ WR4(sc, INTC_INTERRUPT_TRIGGER_LEVEL_REG_OFFSET, val);
+ }
+ }
+}
+
+
+
+static int
+econa_attach(device_t dev)
+{
+ struct econa_softc *sc = device_get_softc(dev);
+ int i;
+
+ econa_softc = sc;
+ sc->sc_st = &econa_bs_tag;
+ sc->sc_sh = ECONA_IO_BASE;
+ sc->dev = dev;
+ if (bus_space_subregion(sc->sc_st, sc->sc_sh, ECONA_PIC_BASE,
+ ECONA_PIC_SIZE, &sc->sc_sys_sh) != 0)
+ panic("Unable to map IRQ registers");
+ sc->sc_irq_rman.rm_type = RMAN_ARRAY;
+ sc->sc_irq_rman.rm_descr = "ECONA IRQs";
+ sc->sc_mem_rman.rm_type = RMAN_ARRAY;
+ sc->sc_mem_rman.rm_descr = "ECONA Memory";
+ if (rman_init(&sc->sc_irq_rman) != 0 ||
+ rman_manage_region(&sc->sc_irq_rman, 0, 31) != 0)
+ panic("econa_attach: failed to set up IRQ rman");
+ if (rman_init(&sc->sc_mem_rman) != 0 ||
+ rman_manage_region(&sc->sc_mem_rman, 0,
+ ~0) != 0)
+ panic("econa_attach: failed to set up memory rman");
+
+ WR4(sc, INTC_INTERRUPT_CLEAR_EDGE_TRIGGER_REG_OFFSET, 0xffffffff);
+
+ WR4(sc, INTC_INTERRUPT_MASK_REG_OFFSET, 0xffffffff);
+
+ WR4(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);
+ }
+ }
+
+ 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) {
+ //printf("resource not found\r\n");
+ 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);
+ /*printf("econa_alloc_resource start %08lx end %08lx, count = %08lx \r\n",
+ start, end, count);*/
+
+ }
+ switch (type)
+ {
+ case SYS_RES_IRQ:
+ rle->res = rman_reserve_resource(&sc->sc_irq_rman,
+ start, end, count, flags, child);
+ break;
+ case SYS_RES_MEMORY:
+ rle->res = rman_reserve_resource(&sc->sc_mem_rman,
+ start, end, count, flags, child);
+ if (rle->res != NULL) {
+ rman_set_bustag(rle->res, &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 = RD4(econa_softc,INTC_INTERRUPT_MASK_REG_OFFSET) | 1<<nb;
+ WR4(econa_softc, INTC_INTERRUPT_MASK_REG_OFFSET, value);
+}
+
+
+void
+arm_unmask_irq(uintptr_t nb)
+{
+ unsigned int value;
+
+ value = RD4(econa_softc,
+ INTC_INTERRUPT_CLEAR_EDGE_TRIGGER_REG_OFFSET) | (1 << nb);
+
+ WR4(econa_softc,
+ INTC_INTERRUPT_CLEAR_EDGE_TRIGGER_REG_OFFSET, value);
+
+ value = RD4(econa_softc, INTC_INTERRUPT_MASK_REG_OFFSET)& ~(1 << nb);
+
+ WR4(econa_softc, INTC_INTERRUPT_MASK_REG_OFFSET, value);
+
+}
+
+
+int
+arm_get_next_irq(int x)
+{
+
+ int irq;
+
+ irq = RD4(econa_softc, INTC_INTERRUPT_STATUS_REG_OFFSET) &
+ ~(RD4(econa_softc, INTC_INTERRUPT_MASK_REG_OFFSET));
+
+ if (irq!=0) {
+ return (ffs(irq) - 1);
+ }
+
+ return (-1);
+}
+
+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);
Index: sys/arm/econa/ehci_ebus.c
===================================================================
--- sys/arm/econa/ehci_ebus.c (revision 0)
+++ sys/arm/econa/ehci_ebus.c (revision 0)
@@ -0,0 +1,313 @@
+/*-
+ * Copyright (C) 2009 Yohanes Nugroho
+ * based on ehci_mbus.c
+ * Copyright (C) 2008 MARVELL INTERNATIONAL LTD.
+ * All rights reserved.
+ *
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of MARVELL nor the names of contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include <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 USB_BRIDGE_INTR_CAUSE 0x210
+#define USB_BRIDGE_INTR_MASK 0x214
+
+#define EHCI_HC_DEVSTR "CNS11XX USB EHCI"
+
+
+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);
+
+ 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 = 0x4000000;
+
+ 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);
Index: sys/arm/econa/econa_reg.h
===================================================================
--- sys/arm/econa/econa_reg.h (revision 0)
+++ sys/arm/econa/econa_reg.h (revision 0)
@@ -0,0 +1,172 @@
+/*-
+ * Copyright (c) 2009 Yohanes Nugroho All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#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_IO_BASE 0x70000000
+#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_EHCI 24
+#define ECONA_IRQ_OHCI 23
+
+#define ECONA_NET_PBASE 0x70000000
+#define ECONA_NET_VBASE 0xE0000000
+#define ECONA_NET_SIZE 0x1000000
+
+
+#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
+
+/*IC*/
+
+#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_OFFSET 0x00
+#define TIMER_TM1_LOAD_REG_OFFSET 0x04
+#define TIMER_TM1_MATCH1_REG_OFFSET 0x08
+#define TIMER_TM1_MATCH2_REG_OFFSET 0x0C
+
+#define TIMER_TM2_COUNTER_REG_OFFSET 0x10
+#define TIMER_TM2_LOAD_REG_OFFSET 0x14
+#define TIMER_TM2_MATCH1_REG_OFFSET 0x18
+#define TIMER_TM2_MATCH2_REG_OFFSET 0x1C
+
+#define TIMER_TM_CR_REG_OFFSET 0x30
+#define TIMER_TM_INTR_STATUS_REG_OFFSET 0x34
+#define TIMER_TM_INTR_MASK_REG_OFFSET 0x38
+
+#define TIMER_TM_REVISION_REG_OFFSET 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_INFO (0x77000014)
+#define CPU_RESET_REGISTER (0x77000014)
+
+
+#endif
Index: sys/arm/econa/if_ecevar.h
===================================================================
--- sys/arm/econa/if_ecevar.h (revision 0)
+++ sys/arm/econa/if_ecevar.h (revision 0)
@@ -0,0 +1,196 @@
+/*-
+ * Copyright (c) 2009 Yohanes Nugroho All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#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; /* Interrupt handle: queue full */
+ void *intrhand_tx; /* Interrupt handle: queue full */
+ void *intrhand_status; /* Interrupt handle */
+
+ 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 */
+
+ int use_rmii;
+
+ 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 descritor*/
+ 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
Index: sys/arm/econa/ohci_ec.c
===================================================================
--- sys/arm/econa/ohci_ec.c (revision 0)
+++ sys/arm/econa/ohci_ec.c (revision 0)
@@ -0,0 +1,242 @@
+/*-
+ * Copyright (c) 2009 Yohanes Nugroho
+ *
+ * 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);
+
+
+
+ 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);
Index: sys/arm/econa/uart_cpu_ec.c
===================================================================
--- sys/arm/econa/uart_cpu_ec.c (revision 0)
+++ sys/arm/econa/uart_cpu_ec.c (revision 0)
@@ -0,0 +1,88 @@
+/*-
+ * Copyright (C) 2009 Yohanes Nugroho
+ * All rights reserved.
+ *
+ * Developed by Semihalf.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of MARVELL nor the names of contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "opt_uart.h"
+
+#include <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);
+}
Index: sys/arm/econa/uart_bus_ec.c
===================================================================
--- sys/arm/econa/uart_bus_ec.c (revision 0)
+++ sys/arm/econa/uart_bus_ec.c (revision 0)
@@ -0,0 +1,80 @@
+/*-
+ * Copyright (C) 2009 Yohanes Nugroho
+ * All rights reserved.
+ *
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of MARVELL nor the names of contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <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);
Index: sys/arm/econa/std.econa
===================================================================
--- sys/arm/econa/std.econa (revision 0)
+++ sys/arm/econa/std.econa (revision 0)
@@ -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
Index: sys/conf/Makefile.arm
===================================================================
--- sys/conf/Makefile.arm (revision 200988)
+++ sys/conf/Makefile.arm (working copy)
@@ -73,7 +73,7 @@
$S/$M/$M/cpufunc_asm_sa1.S $S/$M/$M/cpufunc_asm_arm10.S \
$S/$M/$M/cpufunc_asm_xscale.S $S/$M/$M/cpufunc_asm.S \
$S/$M/$M/cpufunc_asm_xscale_c3.S $S/$M/$M/cpufunc_asm_armv5_ec.S \
- $S/$M/$M/cpufunc_asm_sheeva.S
+ $S/$M/$M/cpufunc_asm_sheeva.S $S/$M/$M/cpufunc_asm_fa526.S
KERNEL_EXTRA=trampoline
KERNEL_EXTRA_INSTALL=kernel.gz.tramp
trampoline: ${KERNEL_KO}.tramp
Index: sys/conf/options.arm
===================================================================
--- sys/conf/options.arm (revision 200988)
+++ sys/conf/options.arm (working copy)
@@ -36,3 +36,4 @@
AT91_BWCT opt_at91.h
AT91_TSC opt_at91.h
AT91_KWIKBYTE opt_at91.h
+CPU_FA526 opt_global.h
Index: sys/kern/vfs_mount.c
===================================================================
--- sys/kern/vfs_mount.c (revision 200988)
+++ sys/kern/vfs_mount.c (working copy)
@@ -1403,6 +1403,11 @@
struct timeval lastfail;
int curfail = 0;
+ /* temporary solution for timeout waiting for USB disk root
+ * filesystem
+ */
+ pause("WAIT", hz * 10);
+
for (;;) {
DROP_GIANT();
g_waitidle();
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?260bb65e0912250948w6f714367w672a1ebf037fb7f7>
