diff options
Diffstat (limited to 'sys')
37 files changed, 5146 insertions, 37 deletions
diff --git a/sys/conf/Makefile.mips b/sys/conf/Makefile.mips index 443065a..e03432c 100644 --- a/sys/conf/Makefile.mips +++ b/sys/conf/Makefile.mips @@ -28,35 +28,73 @@ S= ../../.. .endif .include "$S/conf/kern.pre.mk" +SYSTEM_LD:= ${SYSTEM_LD:$S/conf/ldscript.$M=ldscript.$M} +SYSTEM_DEP:= ${SYSTEM_DEP:$S/conf/ldscript.$M=ldscript.$M} + # XXX: Such sweeping assumptions... MACHINE=mips MACHINE_ARCH=mips +KERNLOADADDR?=0x80001000 +# This obscure value is defined by CFE for WR160N +# To be changed later +TRAMPLOADADDR?=0x807963c0 MKMODULESENV+= MACHINE=${MACHINE} MACHINE_ARCH=${MACHINE_ARCH} # We default to the MIPS32 ISA, if none specified in the # kernel configuration file. ARCH_FLAGS?=-march=mips32 +EXTRA_FLAGS=-fno-pic -mno-abicalls -mno-dsp -G0 HACK_EXTRA_FLAGS=-shared .if defined(TARGET_BIG_ENDIAN) CFLAGS+=-EB SYSTEM_LD+=-EB +EXTRA_FLAGS+=-EB +TRAMP_LDFLAGS+=-Wl,-EB HACK_EXTRA_FLAGS+=-EB -Wl,-EB .else CFLAGS+=-EL SYSTEM_LD+=-EL +EXTRA_FLAGS+=-EL +TRAMP_LDFLAGS+=-Wl,-EL HACK_EXTRA_FLAGS+=-EL -Wl,-EL .endif # We add the -fno-pic flag to kernels because otherwise performance # is extremely poor, as well as -mno-abicalls to force no ABI usage. -CFLAGS+=-fno-pic -mno-abicalls -G0 $(ARCH_FLAGS) -HACK_EXTRA_FLAGS+=-fno-pic -mno-abicalls -G0 $(ARCH_FLAGS) +CFLAGS+=${EXTRA_FLAGS} $(ARCH_FLAGS) +HACK_EXTRA_FLAGS+=${EXTRA_FLAGS} $(ARCH_FLAGS) # XXX hardcoded kernel entry point ASM_CFLAGS+=${CFLAGS} -D_LOCORE -DLOCORE +KERNEL_EXTRA=trampoline +trampoline: ${KERNEL_KO}.tramp.bin +${KERNEL_KO}.tramp.bin: ${KERNEL_KO} $S/$M/$M/elf_trampoline.c \ + $S/$M/$M/inckern.S + ${OBJCOPY} --strip-symbol '$$d' --strip-symbol '$$a' \ + -g --strip-symbol '$$t' ${FULLKERNEL} ${KERNEL_KO}.tmp + sed s/${KERNLOADADDR}/${TRAMPLOADADDR}/ ldscript.$M | \ + sed s/" + SIZEOF_HEADERS"// > ldscript.$M.tramp.noheader + # Generate .S file that setups stack and jumps to trampoline + echo "#include <machine/asm.h>" >tmphack.S + echo "ENTRY(_start)" >>tmphack.S + echo "la t0, kernel_end" >>tmphack.S + echo "move sp, t0" >>tmphack.S + echo "add sp, 0x2000" >>tmphack.S + echo "and sp, ~0x7" >>tmphack.S + echo "la t0, _startC" >>tmphack.S + echo "j t0" >>tmphack.S + echo "END(_start)" >>tmphack.S + echo "#define KERNNAME \"${KERNEL_KO}.tmp\"" >opt_kernname.h + ${CC} -O -nostdlib -I. -I$S ${EXTRA_FLAGS} ${TRAMP_LDFLAGS} -Xlinker \ + -T -Xlinker ldscript.$M.tramp.noheader tmphack.S \ + $S/$M/$M/elf_trampoline.c $S/$M/$M/inckern.S \ + -o ${KERNEL_KO}.tramp.noheader + ${OBJCOPY} -S -O binary ${KERNEL_KO}.tramp.noheader \ + ${KERNEL_KO}.tramp.bin \ + %BEFORE_DEPEND %OBJS @@ -69,6 +107,12 @@ ASM_CFLAGS+=${CFLAGS} -D_LOCORE -DLOCORE %CLEAN +CLEAN+= ldscript.$M ldscript.$M.tramp.noheader \ + ${KERNEL_KO}.tramp.noheader ${KERNEL_KO}.tramp.bin + +ldscript.$M: $S/conf/ldscript.$M + cat $S/conf/ldscript.$M|sed s/KERNLOADADDR/${KERNLOADADDR}/g \ + > ldscript.$M %RULES .include "$S/conf/kern.post.mk" diff --git a/sys/conf/files.mips b/sys/conf/files.mips index 1e233d7..bbedd96 100644 --- a/sys/conf/files.mips +++ b/sys/conf/files.mips @@ -96,3 +96,7 @@ dev/cfe/cfe_api.c optional cfe dev/cfe/cfe_console.c optional cfe_console #dev/cfe/cfe_resource.c optional cfe # not yet needed +dev/siba/siba.c optional siba +dev/siba/siba_pcib.c optional siba pci +dev/siba/siba_cc.c optional siba +#mips/sentry5/siba_mips.c optional siba # not yet diff --git a/sys/conf/ldscript.mips b/sys/conf/ldscript.mips index ca4a70b..3e55dba 100644 --- a/sys/conf/ldscript.mips +++ b/sys/conf/ldscript.mips @@ -43,7 +43,7 @@ PROVIDE (_DYNAMIC = 0); SECTIONS { /* Read-only sections, merged into text segment: */ - . = 0x80100000 + SIZEOF_HEADERS; + . = KERNLOADADDR + SIZEOF_HEADERS; .interp : { *(.interp) } .hash : { *(.hash) } .dynsym : { *(.dynsym) } diff --git a/sys/mips/sentry5/siba_cc.c b/sys/dev/siba/siba_cc.c index cd78f0b..cd78f0b 100644 --- a/sys/mips/sentry5/siba_cc.c +++ b/sys/dev/siba/siba_cc.c diff --git a/sys/mips/sentry5/siba_mips.c b/sys/dev/siba/siba_mips.c index 676da83..676da83 100644 --- a/sys/mips/sentry5/siba_mips.c +++ b/sys/dev/siba/siba_mips.c diff --git a/sys/mips/sentry5/siba_sdram.c b/sys/dev/siba/siba_sdram.c index 8e74e53..8e74e53 100644 --- a/sys/mips/sentry5/siba_sdram.c +++ b/sys/dev/siba/siba_sdram.c diff --git a/sys/mips/alchemy/alchemy_machdep.c b/sys/mips/alchemy/alchemy_machdep.c new file mode 100644 index 0000000..eca9b18 --- /dev/null +++ b/sys/mips/alchemy/alchemy_machdep.c @@ -0,0 +1,157 @@ +/*- + * Copyright (C) 2007 by Oleksandr Tymoshenko. 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 OR HIS RELATIVES 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 MIND, 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_ddb.h" + +#include <sys/param.h> +#include <sys/conf.h> +#include <sys/kernel.h> +#include <sys/systm.h> +#include <sys/imgact.h> +#include <sys/bio.h> +#include <sys/buf.h> +#include <sys/bus.h> +#include <sys/cpu.h> +#include <sys/cons.h> +#include <sys/exec.h> +#include <sys/ucontext.h> +#include <sys/proc.h> +#include <sys/kdb.h> +#include <sys/ptrace.h> +#include <sys/reboot.h> +#include <sys/signalvar.h> +#include <sys/sysent.h> +#include <sys/sysproto.h> +#include <sys/user.h> + +#include <vm/vm.h> +#include <vm/vm_object.h> +#include <vm/vm_page.h> +#include <vm/vm_pager.h> + +#include <machine/cache.h> +#include <machine/clock.h> +#include <machine/cpu.h> +#include <machine/cpuinfo.h> +#include <machine/cpufunc.h> +#include <machine/cpuregs.h> +#include <machine/hwfunc.h> +#include <machine/intr_machdep.h> +#include <machine/locore.h> +#include <machine/md_var.h> +#include <machine/pte.h> +#include <machine/sigframe.h> +#include <machine/trap.h> +#include <machine/vmparam.h> + +extern int *edata; +extern int *end; + +static void +mips_init(void) +{ + int i; + + printf("entry: mips_init()\n"); + + bootverbose = 1; + realmem = btoc(16 << 20); + + for (i = 0; i < 10; i++) { + phys_avail[i] = 0; + } + + /* phys_avail regions are in bytes */ + phys_avail[0] = MIPS_KSEG0_TO_PHYS((vm_offset_t)&end); + phys_avail[1] = ctob(realmem); + + physmem = realmem; + + init_param1(); + init_param2(physmem); + mips_cpu_init(); + pmap_bootstrap(); + mips_proc0_init(); + mutex_init(); +#ifdef DDB + kdb_init(); +#endif +} + +void +platform_halt(void) +{ + +} + + +void +platform_identify(void) +{ + +} + +void +platform_reset(void) +{ + + __asm __volatile("li $25, 0xbfc00000"); + __asm __volatile("j $25"); +} + +void +platform_trap_enter(void) +{ + +} + +void +platform_trap_exit(void) +{ + +} + +void +platform_start(__register_t a0 __unused, __register_t a1 __unused, + __register_t a2 __unused, __register_t a3 __unused) +{ + vm_offset_t kernend; + uint64_t platform_counter_freq = 175 * 1000 * 1000; + + /* clear the BSS and SBSS segments */ + kernend = round_page((vm_offset_t)&end); + memset(&edata, 0, kernend - (vm_offset_t)(&edata)); + + cninit(); + mips_init(); + /* Set counter_freq for tick_init_params() */ + platform_counter_freq = 175 * 1000 * 1000; + + mips_timer_init_params(platform_counter_freq, 0); +} diff --git a/sys/mips/alchemy/aureg.h b/sys/mips/alchemy/aureg.h new file mode 100644 index 0000000..dfa2103 --- /dev/null +++ b/sys/mips/alchemy/aureg.h @@ -0,0 +1,373 @@ +/* $NetBSD: aureg.h,v 1.18 2006/10/02 06:44:00 gdamore Exp $ */ + +/* + * Copyright 2002 Wasabi Systems, Inc. + * All rights reserved. + * + * Written by Simon Burge for Wasabi Systems, Inc. + * + * 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 for the NetBSD Project by + * Wasabi Systems, Inc. + * 4. The name of Wasabi Systems, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC + * 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 _MIPS_ALCHEMY_AUREG_H +#define _MIPS_ALCHEMY_AUREG_H + +/************************************************************************/ +/******************** AC97 Controller registers *********************/ +/************************************************************************/ +#define AC97_BASE 0x10000000 + +/************************************************************************/ +/*********************** USB Host registers *************************/ +/************************************************************************/ +#define USBH_BASE 0x10100000 +#define AU1550_USBH_BASE 0x14020000 + +#define USBH_ENABLE 0x7fffc +#define USBH_SIZE 0x100000 + +#define AU1550_USBH_ENABLE 0x7ffc +#define AU1550_USBH_SIZE 0x60000 + +/************************************************************************/ +/********************** USB Device registers ************************/ +/************************************************************************/ +#define USBD_BASE 0x10200000 + +/************************************************************************/ +/************************* IRDA registers ***************************/ +/************************************************************************/ +#define IRDA_BASE 0x10300000 + +/************************************************************************/ +/****************** Interrupt Controller registers ******************/ +/************************************************************************/ + +#define IC0_BASE 0x10400000 +#define IC1_BASE 0x11800000 + +/* + * The *_READ registers read the current value of the register + * The *_SET registers set to 1 all bits that are written 1 + * The *_CLEAR registers clear to zero all bits that are written as 1 + */ +#define IC_CONFIG0_READ 0x40 /* See table below */ +#define IC_CONFIG0_SET 0x40 +#define IC_CONFIG0_CLEAR 0x44 + +#define IC_CONFIG1_READ 0x48 /* See table below */ +#define IC_CONFIG1_SET 0x48 +#define IC_CONFIG1_CLEAR 0x4c + +#define IC_CONFIG2_READ 0x50 /* See table below */ +#define IC_CONFIG2_SET 0x50 +#define IC_CONFIG2_CLEAR 0x54 + +#define IC_REQUEST0_INT 0x54 /* Show active interrupts on request 0 */ + +#define IC_SOURCE_READ 0x58 /* Interrupt source */ +#define IC_SOURCE_SET 0x58 /* 0 - test bit used as source */ +#define IC_SOURCE_CLEAR 0x5c /* 1 - peripheral/GPIO used as source */ + +#define IC_REQUEST1_INT 0x5c /* Show active interrupts on request 1 */ + +#define IC_ASSIGN_REQUEST_READ 0x60 /* Assigns the interrupt to one of the */ +#define IC_ASSIGN_REQUEST_SET 0x60 /* CPU requests (0 - assign to request 1, */ +#define IC_ASSIGN_REQUEST_CLEAR 0x64 /* 1 - assign to request 0) */ + +#define IC_WAKEUP_READ 0x68 /* Controls whether the interrupt can */ +#define IC_WAKEUP_SET 0x68 /* cause a wakeup from IDLE */ +#define IC_WAKEUP_CLEAR 0x6c + +#define IC_MASK_READ 0x70 /* Enables/Disables the interrupt */ +#define IC_MASK_SET 0x70 +#define IC_MASK_CLEAR 0x74 + +#define IC_RISING_EDGE 0x78 /* Check/clear rising edge */ + +#define IC_FALLING_EDGE 0x7c /* Check/clear falling edge */ + +#define IC_TEST_BIT 0x80 /* single bit source select */ + +/* + * Interrupt Configuration Register Functions + * + * Cfg2[n] Cfg1[n] Cfg0[n] Function + * 0 0 0 Interrupts Disabled + * 0 0 1 Rising Edge Enabled + * 0 1 0 Falling Edge Enabled + * 0 1 1 Rising and Falling Edge Enabled + * 1 0 0 Interrupts Disabled + * 1 0 1 High Level Enabled + * 1 1 0 Low Level Enabled + * 1 1 1 Both Levels and Both Edges Enabled + */ + +/************************************************************************/ +/************* Programable Serial Controller registers **************/ +/************************************************************************/ + +#define PSC0_BASE 0x11A00000 +#define PSC1_BASE 0x11B00000 +#define PSC2_BASE 0x10A00000 +#define PSC3_BASE 0x10B00000 + + +/************************************************************************/ +/********************** Ethernet MAC registers **********************/ +/************************************************************************/ + +#define MAC0_BASE 0x10500000 +#define MAC1_BASE 0x10510000 +#define MACx_SIZE 0x28 + +#define AU1500_MAC0_BASE 0x11500000 /* Grr, different on Au1500 */ +#define AU1500_MAC1_BASE 0x11510000 /* Grr, different on Au1500 */ + +#define MAC0_ENABLE 0x10520000 +#define MAC1_ENABLE 0x10520004 +#define MACENx_SIZE 0x04 + +#define AU1500_MAC0_ENABLE 0x11520000 /* Grr, different on Au1500 */ +#define AU1500_MAC1_ENABLE 0x11520004 /* Grr, different on Au1500 */ + +#define MAC0_DMA_BASE 0x14004000 +#define MAC1_DMA_BASE 0x14004200 +#define MACx_DMA_SIZE 0x140 + +/************************************************************************/ +/********************** Static Bus registers ************************/ +/************************************************************************/ +#define STATIC_BUS_BASE 0x14001000 + +/************************************************************************/ +/******************** Secure Digital registers **********************/ +/************************************************************************/ +#define SD0_BASE 0x10600000 +#define SD1_BASE 0x10680000 + +/************************************************************************/ +/************************* I^2S registers ***************************/ +/************************************************************************/ +#define I2S_BASE 0x11000000 + +/************************************************************************/ +/************************** UART registers **************************/ +/************************************************************************/ + +#define UART0_BASE 0x11100000 +#define UART1_BASE 0x11200000 +#define UART2_BASE 0x11300000 +#define UART3_BASE 0x11400000 + +/************************************************************************/ +/************************* SSI registers ****************************/ +/************************************************************************/ +#define SSI0_BASE 0x11600000 +#define SSI1_BASE 0x11680000 + +/************************************************************************/ +/************************ GPIO2 registers ***************************/ +/************************************************************************/ +#define GPIO_BASE 0x11900100 + +/************************************************************************/ +/************************ GPIO2 registers ***************************/ +/************************************************************************/ +#define GPIO2_BASE 0x11700000 + +/************************************************************************/ +/************************* PCI registers ****************************/ +/************************************************************************/ +#define PCI_BASE 0x14005000 +#define PCI_HEADER 0x14005100 +#define PCI_MEM_BASE 0x400000000ULL +#define PCI_IO_BASE 0x500000000ULL +#define PCI_CONFIG_BASE 0x600000000ULL + +/************************************************************************/ +/*********************** PCMCIA registers ***************************/ +/************************************************************************/ +#define PCMCIA_BASE 0xF00000000ULL + +/************************************************************************/ +/****************** Programmable Counter registers ******************/ +/************************************************************************/ + +#define SYS_BASE 0x11900000 + +#define PC_BASE SYS_BASE + +#define PC_TRIM0 0x00 /* PC0 Divide (16 bits) */ +#define PC_COUNTER_WRITE0 0x04 /* set PC0 */ +#define PC_MATCH0_0 0x08 /* match counter & interrupt */ +#define PC_MATCH1_0 0x0c /* match counter & interrupt */ +#define PC_MATCH2_0 0x10 /* match counter & interrupt */ +#define PC_COUNTER_CONTROL 0x14 /* Programmable Counter Control */ +#define CC_E1S 0x00800000 /* Enable PC1 write status */ +#define CC_T1S 0x00100000 /* Trim PC1 write status */ +#define CC_M21 0x00080000 /* Match 2 of PC1 write status */ +#define CC_M11 0x00040000 /* Match 1 of PC1 write status */ +#define CC_M01 0x00020000 /* Match 0 of PC1 write status */ +#define CC_C1S 0x00010000 /* PC1 write status */ +#define CC_BP 0x00004000 /* Bypass OSC (use GPIO1) */ +#define CC_EN1 0x00002000 /* Enable PC1 */ +#define CC_BT1 0x00001000 /* Bypass Trim on PC1 */ +#define CC_EN0 0x00000800 /* Enable PC0 */ +#define CC_BT0 0x00000400 /* Bypass Trim on PC0 */ +#define CC_EO 0x00000100 /* Enable Oscillator */ +#define CC_E0S 0x00000080 /* Enable PC0 write status */ +#define CC_32S 0x00000020 /* 32.768kHz OSC status */ +#define CC_T0S 0x00000010 /* Trim PC0 write status */ +#define CC_M20 0x00000008 /* Match 2 of PC0 write status */ +#define CC_M10 0x00000004 /* Match 1 of PC0 write status */ +#define CC_M00 0x00000002 /* Match 0 of PC0 write status */ +#define CC_C0S 0x00000001 /* PC0 write status */ +#define PC_COUNTER_READ_0 0x40 /* get PC0 */ +#define PC_TRIM1 0x44 /* PC1 Divide (16 bits) */ +#define PC_COUNTER_WRITE1 0x48 /* set PC1 */ +#define PC_MATCH0_1 0x4c /* match counter & interrupt */ +#define PC_MATCH1_1 0x50 /* match counter & interrupt */ +#define PC_MATCH2_1 0x54 /* match counter & interrupt */ +#define PC_COUNTER_READ_1 0x58 /* get PC1 */ + +#define PC_SIZE 0x5c /* size of register set */ +#define PC_RATE 32768 /* counter rate is 32.768kHz */ + +/************************************************************************/ +/******************* Frequency Generator Registers ******************/ +/************************************************************************/ + +#define SYS_FREQCTRL0 (SYS_BASE + 0x20) +#define SFC_FRDIV2(f) (f<<22) /* 29:22. Freq Divider 2 */ +#define SFC_FE2 (1<<21) /* Freq generator output enable 2 */ +#define SFC_FS2 (1<<20) /* Freq generator source 2 */ +#define SFC_FRDIV1(f) (f<<12) /* 19:12. Freq Divider 1 */ +#define SFC_FE1 (1<<11) /* Freq generator output enable 1 */ +#define SFC_FS1 (1<<10) /* Freq generator source 1 */ +#define SFC_FRDIV0(f) (f<<2) /* 9:2. Freq Divider 0 */ +#define SFC_FE0 2 /* Freq generator output enable 0 */ +#define SFC_FS0 1 /* Freq generator source 0 */ + +#define SYS_FREQCTRL1 (SYS_BASE + 0x24) +#define SFC_FRDIV5(f) (f<<22) /* 29:22. Freq Divider 5 */ +#define SFC_FE5 (1<<21) /* Freq generator output enable 5 */ +#define SFC_FS5 (1<<20) /* Freq generator source 5 */ +#define SFC_FRDIV4(f) (f<<12) /* 19:12. Freq Divider 4 */ +#define SFC_FE4 (1<<11) /* Freq generator output enable 4 */ +#define SFC_FS4 (1<<10) /* Freq generator source 4 */ +#define SFC_FRDIV3(f) (f<<2) /* 9:2. Freq Divider 3 */ +#define SFC_FE3 2 /* Freq generator output enable 3 */ +#define SFC_FS3 1 /* Freq generator source 3 */ + +/************************************************************************/ +/****************** Clock Source Control Registers ******************/ +/************************************************************************/ + +#define SYS_CLKSRC (SYS_BASE + 0x28) +#define SCS_ME1(n) (n<<27) /* EXTCLK1 Clock Mux input select */ +#define SCS_ME0(n) (n<<22) /* EXTCLK0 Clock Mux input select */ +#define SCS_MPC(n) (n<<17) /* PCI clock mux input select */ +#define SCS_MUH(n) (n<<12) /* USB Host clock mux input select */ +#define SCS_MUD(n) (n<<7) /* USB Device clock mux input select */ +#define SCS_MEx_AUX 0x1 /* Aux clock */ +#define SCS_MEx_FREQ0 0x2 /* FREQ0 */ +#define SCS_MEx_FREQ1 0x3 /* FREQ1 */ +#define SCS_MEx_FREQ2 0x4 /* FREQ2 */ +#define SCS_MEx_FREQ3 0x5 /* FREQ3 */ +#define SCS_MEx_FREQ4 0x6 /* FREQ4 */ +#define SCS_MEx_FREQ5 0x7 /* FREQ5 */ +#define SCS_DE1 (1<<26) /* EXTCLK1 clock divider select */ +#define SCS_CE1 (1<<25) /* EXTCLK1 clock select */ +#define SCS_DE0 (1<<21) /* EXTCLK0 clock divider select */ +#define SCS_CE0 (1<<20) /* EXTCLK0 clock select */ +#define SCS_DPC (1<<16) /* PCI clock divider select */ +#define SCS_CPC (1<<15) /* PCI clock select */ +#define SCS_DUH (1<<11) /* USB Host clock divider select */ +#define SCS_CUH (1<<10) /* USB Host clock select */ +#define SCS_DUD (1<<6) /* USB Device clock divider select */ +#define SCS_CUD (1<<5) /* USB Device clock select */ +/* + * Au1550 bits, needed for PSCs. Note that some bits collide with + * earlier parts. On Au1550, USB clocks (both device and host) are + * shared with PSC2, and must be configured for 48MHz. DBAU1550 YAMON + * does this by default. Also, EXTCLK0 is shared with PSC3. DBAU1550 + * YAMON does not configure any clocks besides PSC2. + */ +#define SCS_MP3(n) (n<<22) /* psc3_intclock mux */ +#define SCS_DP3 (1<<21) /* psc3_intclock divider */ +#define SCS_CP3 (1<<20) /* psc3_intclock select */ +#define SCS_MP1(n) (n<<12) /* psc1_intclock mux */ +#define SCS_DP1 (1<<11) /* psc1_intclock divider */ +#define SCS_CP1 (1<<10) /* psc1_intclock select */ +#define SCS_MP0(n) (n<<7) /* psc0_intclock mux */ +#define SCS_DP0 (1<<6) /* psc0_intclock divider */ +#define SCS_CP0 (1<<5) /* psc0_intclock seelct */ +#define SCS_MP2(n) (n<<2) /* psc2_intclock mux */ +#define SCS_DP2 (1<<1) /* psc2_intclock divider */ +#define SCS_CP2 (1<<0) /* psc2_intclock select */ + +/************************************************************************/ +/*************************** PIN Function *****************************/ +/************************************************************************/ + +#define SYS_PINFUNC (SYS_BASE + 0x2c) +#define SPF_PSC3_MASK (7<<20) +#define SPF_PSC3_AC97 (0<<17) /* select AC97/SPI */ +#define SPF_PSC3_I2S (1<<17) /* select I2S */ +#define SPF_PSC3_SMBUS (3<<17) /* select SMbus */ +#define SPF_PSC3_GPIO (7<<17) /* select gpio215:211 */ +#define SPF_PSC2_MASK (7<<17) +#define SPF_PSC2_AC97 (0<<17) /* select AC97/SPI */ +#define SPF_PSC2_I2S (1<<17) /* select I2S */ +#define SPF_PSC2_SMBUS (3<<17) /* select SMbus */ +#define SPF_PSC2_GPIO (7<<17) /* select gpio210:206*/ +#define SPF_CS (1<<16) /* extclk0 or 32kHz osc */ +#define SPF_USB (1<<15) /* host or device usb otg */ +#define SPF_U3T (1<<14) /* uart3 tx or gpio23 */ +#define SPF_U1R (1<<13) /* uart1 rx or gpio22 */ +#define SPF_U1T (1<<12) /* uart1 tx or gpio21 */ +#define SPF_EX1 (1<<10) /* gpio3 or extclk1 */ +#define SPF_EX0 (1<<9) /* gpio2 or extclk0/32kHz osc*/ +#define SPF_U3 (1<<7) /* gpio14:9 or uart3 */ +#define SPF_MBSa (1<<5) /* must be set */ +#define SPF_NI2 (1<<4) /* enet1 or gpio28:24 */ +#define SPF_U0 (1<<3) /* uart0 or gpio20 */ +#define SPF_MBSb (1<<2) /* must be set */ +#define SPF_S1 (1<<1) /* gpio17 or psc1_sync1 */ +#define SPF_S0 (1<<0) /* gpio16 or psc0_sync1 */ + +/************************************************************************/ +/*************************** PLL Control *****************************/ +/************************************************************************/ + +#define SYS_CPUPLL (SYS_BASE + 0x60) +#define SYS_AUXPLL (SYS_BASE + 0x64) + +#endif /* _MIPS_ALCHEMY_AUREG_H */ diff --git a/sys/mips/alchemy/files.alchemy b/sys/mips/alchemy/files.alchemy new file mode 100644 index 0000000..8534431 --- /dev/null +++ b/sys/mips/alchemy/files.alchemy @@ -0,0 +1,7 @@ +# $FreeBSD$ +# Alchmy on-board devices +# mips/alchemy/console.c standard +mips/alchemy/alchemy_machdep.c standard +mips/alchemy/obio.c standard +mips/alchemy/uart_bus_alchemy.c optional uart +mips/alchemy/uart_cpu_alchemy.c optional uart diff --git a/sys/mips/alchemy/obio.c b/sys/mips/alchemy/obio.c new file mode 100644 index 0000000..03e098d --- /dev/null +++ b/sys/mips/alchemy/obio.c @@ -0,0 +1,501 @@ +/* $NetBSD: obio.c,v 1.11 2003/07/15 00:25:05 lukem Exp $ */ + +/*- + * Copyright (c) 2001, 2002, 2003 Wasabi Systems, Inc. + * All rights reserved. + * + * Written by Jason R. Thorpe for Wasabi Systems, Inc. + * + * 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 for the NetBSD Project by + * Wasabi Systems, Inc. + * 4. The name of Wasabi Systems, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC + * 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/interrupt.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/rman.h> +#include <sys/malloc.h> + +#include <machine/bus.h> + +#include <mips/adm5120/adm5120reg.h> +#include <mips/adm5120/obiovar.h> + +/* MIPS HW interrupts of IRQ/FIQ respectively */ +#define ADM5120_INTR 0 +#define ADM5120_FAST_INTR 1 + +/* Interrupt levels */ +#define INTR_IRQ 0 +#define INTR_FIQ 1 + +int irq_priorities[NIRQS] = { + INTR_IRQ, /* flash */ + INTR_FIQ, /* uart0 */ + INTR_FIQ, /* uart1 */ + INTR_IRQ, /* ahci */ + INTR_IRQ, /* unknown */ + INTR_IRQ, /* unknown */ + INTR_IRQ, /* unknown */ + INTR_IRQ, /* unknown */ + INTR_IRQ, /* unknown */ + INTR_IRQ, /* admsw */ + INTR_IRQ, /* unknown */ + INTR_IRQ, /* unknown */ + INTR_IRQ, /* unknown */ + INTR_IRQ, /* unknown */ + INTR_IRQ, /* unknown */ + INTR_IRQ, /* unknown */ + INTR_IRQ, /* unknown */ + INTR_IRQ, /* unknown */ + INTR_IRQ, /* unknown */ + INTR_IRQ, /* unknown */ + INTR_IRQ, /* unknown */ + INTR_IRQ, /* unknown */ + INTR_IRQ, /* unknown */ + INTR_IRQ, /* unknown */ + INTR_IRQ, /* unknown */ + INTR_IRQ, /* unknown */ + INTR_IRQ, /* unknown */ + INTR_IRQ, /* unknown */ + INTR_IRQ, /* unknown */ + INTR_IRQ, /* unknown */ + INTR_IRQ, /* unknown */ + INTR_IRQ, /* unknown */ +}; + + +#define REG_READ(o) *((volatile uint32_t *)MIPS_PHYS_TO_KSEG1(ADM5120_BASE_ICU + (o))) +#define REG_WRITE(o,v) (REG_READ(o)) = (v) + +static int obio_activate_resource(device_t, device_t, int, int, + struct resource *); +static device_t obio_add_child(device_t, int, const char *, int); +static struct resource * + obio_alloc_resource(device_t, device_t, int, int *, u_long, + u_long, u_long, u_int); +static int obio_attach(device_t); +static int obio_deactivate_resource(device_t, device_t, int, int, + struct resource *); +static struct resource_list * + obio_get_resource_list(device_t, device_t); +static void obio_hinted_child(device_t, const char *, int); +static int obio_intr(void *); +static int obio_probe(device_t); +static int obio_release_resource(device_t, device_t, int, int, + struct resource *); +static int obio_setup_intr(device_t, device_t, struct resource *, int, + driver_filter_t *, driver_intr_t *, void *, void **); +static int obio_teardown_intr(device_t, device_t, struct resource *, + void *); + +static int +obio_probe(device_t dev) +{ + + return (0); +} + +static int +obio_attach(device_t dev) +{ + struct obio_softc *sc = device_get_softc(dev); + int rid; + + sc->oba_mem_rman.rm_type = RMAN_ARRAY; + sc->oba_mem_rman.rm_descr = "OBIO memeory"; + if (rman_init(&sc->oba_mem_rman) != 0 || + rman_manage_region(&sc->oba_mem_rman, OBIO_MEM_START, + OBIO_MEM_START + OBIO_MEM_SIZE) != 0) + panic("obio_attach: failed to set up I/O rman"); + + sc->oba_irq_rman.rm_type = RMAN_ARRAY; + sc->oba_irq_rman.rm_descr = "OBIO IRQ"; + + if (rman_init(&sc->oba_irq_rman) != 0 || + rman_manage_region(&sc->oba_irq_rman, 0, NIRQS-1) != 0) + panic("obio_attach: failed to set up IRQ rman"); + + /* Hook up our interrupt handler. */ + if ((sc->sc_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, + ADM5120_INTR, ADM5120_INTR, 1, + RF_SHAREABLE | RF_ACTIVE)) == NULL) { + device_printf(dev, "unable to allocate IRQ resource\n"); + return (ENXIO); + } + + if ((bus_setup_intr(dev, sc->sc_irq, INTR_TYPE_MISC, obio_intr, NULL, + sc, &sc->sc_ih))) { + device_printf(dev, + "WARNING: unable to register interrupt handler\n"); + return (ENXIO); + } + + /* Hook up our FAST interrupt handler. */ + if ((sc->sc_fast_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, + ADM5120_FAST_INTR, ADM5120_FAST_INTR, 1, + RF_SHAREABLE | RF_ACTIVE)) == NULL) { + device_printf(dev, "unable to allocate IRQ resource\n"); + return (ENXIO); + } + + if ((bus_setup_intr(dev, sc->sc_fast_irq, INTR_TYPE_MISC, obio_intr, + NULL, sc, &sc->sc_fast_ih))) { + device_printf(dev, + "WARNING: unable to register interrupt handler\n"); + return (ENXIO); + } + + /* disable all interrupts */ + REG_WRITE(ICU_ENABLE_REG, ICU_INT_MASK); + + bus_generic_probe(dev); + bus_enumerate_hinted_children(dev); + bus_generic_attach(dev); + + return (0); +} + +static struct resource * +obio_alloc_resource(device_t bus, device_t child, int type, int *rid, + u_long start, u_long end, u_long count, u_int flags) +{ + struct obio_softc *sc = device_get_softc(bus); + struct obio_ivar *ivar = device_get_ivars(child); + struct resource *rv; + struct resource_list_entry *rle; + struct rman *rm; + int isdefault, needactivate, passthrough; + + isdefault = (start == 0UL && end == ~0UL && count == 1); + needactivate = flags & RF_ACTIVE; + passthrough = (device_get_parent(child) != bus); + rle = NULL; + + if (passthrough) + return (BUS_ALLOC_RESOURCE(device_get_parent(bus), child, type, + rid, start, end, count, flags)); + + /* + * If this is an allocation of the "default" range for a given RID, + * and we know what the resources for this device are (ie. they aren't + * maintained by a child bus), then work out the start/end values. + */ + if (isdefault) { + rle = resource_list_find(&ivar->resources, type, *rid); + if (rle == NULL) + return (NULL); + if (rle->res != NULL) { + panic("%s: resource entry is busy", __func__); + } + start = rle->start; + end = rle->end; + count = rle->count; + } + + switch (type) { + case SYS_RES_IRQ: + rm = &sc->oba_irq_rman; + break; + case SYS_RES_MEMORY: + rm = &sc->oba_mem_rman; + break; + default: + printf("%s: unknown resource type %d\n", __func__, type); + return (0); + } + + rv = rman_reserve_resource(rm, start, end, count, flags, child); + if (rv == 0) { + printf("%s: could not reserve resource\n", __func__); + return (0); + } + + rman_set_rid(rv, *rid); + + if (needactivate) { + if (bus_activate_resource(child, type, *rid, rv)) { + printf("%s: could not activate resource\n", __func__); + rman_release_resource(rv); + return (0); + } + } + + return (rv); +} + +static int +obio_activate_resource(device_t bus, device_t child, int type, int rid, + struct resource *r) +{ + + /* + * If this is a memory resource, track the direct mapping + * in the uncached MIPS KSEG1 segment. + */ + if (type == SYS_RES_MEMORY) { + void *vaddr; + + vaddr = (void *)MIPS_PHYS_TO_KSEG1((intptr_t)rman_get_start(r)); + rman_set_virtual(r, vaddr); + rman_set_bustag(r, MIPS_BUS_SPACE_MEM); + rman_set_bushandle(r, (bus_space_handle_t)vaddr); + } + + return (rman_activate_resource(r)); +} + +static int +obio_deactivate_resource(device_t bus, device_t child, int type, int rid, + struct resource *r) +{ + + return (rman_deactivate_resource(r)); +} + +static int +obio_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 = obio_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 +obio_setup_intr(device_t dev, device_t child, struct resource *ires, + int flags, driver_filter_t *filt, driver_intr_t *handler, + void *arg, void **cookiep) +{ + struct obio_softc *sc = device_get_softc(dev); + struct intr_event *event; + int irq, error, priority; + uint32_t irqmask; + + irq = rman_get_start(ires); + + if (irq >= NIRQS) + panic("%s: bad irq %d", __func__, irq); + + event = sc->sc_eventstab[irq]; + if (event == NULL) { + error = intr_event_create(&event, (void *)irq, 0, irq, + (mask_fn)mips_mask_irq, (mask_fn)mips_unmask_irq, + NULL, NULL, "obio intr%d:", irq); + + sc->sc_eventstab[irq] = event; + } + else + panic("obio: Can't share IRQs"); + + intr_event_add_handler(event, device_get_nameunit(child), filt, + handler, arg, intr_priority(flags), flags, cookiep); + + irqmask = 1 << irq; + priority = irq_priorities[irq]; + + if (priority == INTR_FIQ) + REG_WRITE(ICU_MODE_REG, REG_READ(ICU_MODE_REG) | irqmask); + else + REG_WRITE(ICU_MODE_REG, REG_READ(ICU_MODE_REG) & ~irqmask); + + /* enable */ + REG_WRITE(ICU_ENABLE_REG, irqmask); + + return (0); +} + +static int +obio_teardown_intr(device_t dev, device_t child, struct resource *ires, + void *cookie) +{ + struct obio_softc *sc = device_get_softc(dev); + int irq, result; + uint32_t irqmask; + + irq = rman_get_start(ires); + if (irq >= NIRQS) + panic("%s: bad irq %d", __func__, irq); + + if (sc->sc_eventstab[irq] == NULL) + panic("Trying to teardown unoccupied IRQ"); + + irqmask = 1 << irq; /* only used as a mask from here on */ + + /* disable this irq in HW */ + REG_WRITE(ICU_DISABLE_REG, irqmask); + + result = intr_event_remove_handler(cookie); + if (!result) { + sc->sc_eventstab[irq] = NULL; + } + + return (result); +} + +static int +obio_intr(void *arg) +{ + struct obio_softc *sc = arg; + struct intr_event *event; + uint32_t irqstat; + int irq; + + irqstat = REG_READ(ICU_FIQ_STATUS_REG); + irqstat |= REG_READ(ICU_STATUS_REG); + + irq = 0; + while (irqstat != 0) { + if ((irqstat & 1) == 1) { + event = sc->sc_eventstab[irq]; + if (!event || TAILQ_EMPTY(&event->ie_handlers)) + continue; + + /* TODO: pass frame as an argument*/ + /* TODO: log stray interrupt */ + intr_event_handle(event, NULL); + } + + irq++; + irqstat >>= 1; + } + + return (FILTER_HANDLED); +} + +static void +obio_hinted_child(device_t bus, const char *dname, int dunit) +{ + device_t child; + long maddr; + int msize; + int irq; + int result; + + child = BUS_ADD_CHILD(bus, 0, dname, dunit); + + /* + * Set hard-wired resources for hinted child using + * specific RIDs. + */ + resource_long_value(dname, dunit, "maddr", &maddr); + resource_int_value(dname, dunit, "msize", &msize); + + + result = bus_set_resource(child, SYS_RES_MEMORY, 0, + maddr, msize); + if (result != 0) + device_printf(bus, "warning: bus_set_resource() failed\n"); + + if (resource_int_value(dname, dunit, "irq", &irq) == 0) { + result = bus_set_resource(child, SYS_RES_IRQ, 0, irq, 1); + if (result != 0) + device_printf(bus, + "warning: bus_set_resource() failed\n"); + } +} + +static device_t +obio_add_child(device_t bus, int order, const char *name, int unit) +{ + device_t child; + struct obio_ivar *ivar; + + ivar = malloc(sizeof(struct obio_ivar), M_DEVBUF, M_WAITOK | M_ZERO); + if (ivar == NULL) { + printf("Failed to allocate ivar\n"); + return (0); + } + resource_list_init(&ivar->resources); + + child = device_add_child_ordered(bus, order, name, unit); + if (child == NULL) { + printf("Can't add child %s%d ordered\n", name, unit); + return (0); + } + + device_set_ivars(child, ivar); + + return (child); +} + +/* + * Helper routine for bus_generic_rl_get_resource/bus_generic_rl_set_resource + * Provides pointer to resource_list for these routines + */ +static struct resource_list * +obio_get_resource_list(device_t dev, device_t child) +{ + struct obio_ivar *ivar; + + ivar = device_get_ivars(child); + return (&(ivar->resources)); +} + +static device_method_t obio_methods[] = { + DEVMETHOD(bus_activate_resource, obio_activate_resource), + DEVMETHOD(bus_add_child, obio_add_child), + DEVMETHOD(bus_alloc_resource, obio_alloc_resource), + DEVMETHOD(bus_deactivate_resource, obio_deactivate_resource), + DEVMETHOD(bus_get_resource_list, obio_get_resource_list), + DEVMETHOD(bus_hinted_child, obio_hinted_child), + DEVMETHOD(bus_release_resource, obio_release_resource), + DEVMETHOD(bus_setup_intr, obio_setup_intr), + DEVMETHOD(bus_teardown_intr, obio_teardown_intr), + DEVMETHOD(device_attach, obio_attach), + DEVMETHOD(device_probe, obio_probe), + DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), + DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource), + + {0, 0}, +}; + +static driver_t obio_driver = { + "obio", + obio_methods, + sizeof(struct obio_softc), +}; +static devclass_t obio_devclass; + +DRIVER_MODULE(obio, nexus, obio_driver, obio_devclass, 0, 0); diff --git a/sys/mips/alchemy/std.alchemy b/sys/mips/alchemy/std.alchemy new file mode 100644 index 0000000..a955b67 --- /dev/null +++ b/sys/mips/alchemy/std.alchemy @@ -0,0 +1,8 @@ +# $FreeBSD$ +# Standard include file for Alchemy Au1xxx CPUs: +# Au1000, Au1200, Au1250, Au1500 and Au1550 + +files "../alchemy/files.alchemy" + +cpu CPU_MIPS4KC +options ISA_MIPS32 diff --git a/sys/mips/alchemy/uart_bus_alchemy.c b/sys/mips/alchemy/uart_bus_alchemy.c new file mode 100644 index 0000000..5c2315b --- /dev/null +++ b/sys/mips/alchemy/uart_bus_alchemy.c @@ -0,0 +1,87 @@ +/*- + * Copyright (c) 2007 Bruce M. Simpson. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * $Id$ + */ +/* + * Skeleton of this file was based on respective code for ARM + * code written by Olivier Houchard. + */ + +#include "opt_uart.h" + +#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/pci/pcivar.h> + +#include <dev/uart/uart.h> +#include <dev/uart/uart_bus.h> +#include <dev/uart/uart_cpu.h> + +#include <mips/alchemy/aureg.h> + +#include "uart_if.h" + +static int uart_alchemy_probe(device_t dev); + +static device_method_t uart_alchemy_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, uart_alchemy_probe), + DEVMETHOD(device_attach, uart_bus_attach), + DEVMETHOD(device_detach, uart_bus_detach), + { 0, 0 } +}; + +static driver_t uart_alchemy_driver = { + uart_driver_name, + uart_alchemy_methods, + sizeof(struct uart_softc), +}; + +extern SLIST_HEAD(uart_devinfo_list, uart_devinfo) uart_sysdevs; + +static int +uart_alchemy_probe(device_t dev) +{ + struct uart_softc *sc; + + sc = device_get_softc(dev); + sc->sc_sysdev = SLIST_FIRST(&uart_sysdevs); + sc->sc_class = &uart_ns8250_class; + bcopy(&sc->sc_sysdev->bas, &sc->sc_bas, sizeof(sc->sc_bas)); + + return (uart_bus_probe(dev, 0, 0, 0, 0)); +} + +DRIVER_MODULE(uart, obio, uart_alchemy_driver, uart_devclass, 0, 0); diff --git a/sys/mips/alchemy/uart_cpu_alchemy.c b/sys/mips/alchemy/uart_cpu_alchemy.c new file mode 100644 index 0000000..931aed6 --- /dev/null +++ b/sys/mips/alchemy/uart_cpu_alchemy.c @@ -0,0 +1,79 @@ +/*- + * Copyright (c) 2006 Wojciech A. Koszek <wkoszek@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id$ + */ +/* + * Skeleton of this file was based on respective code for ARM + * code written by Olivier Houchard. + */ + +#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 <mips/alchemy/aureg.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) +{ + + di->ops = uart_getops(&uart_ns8250_class); + di->bas.chan = 0; + di->bas.bst = 0; + di->bas.regshft = 0; + di->bas.rclk = 0; + di->baudrate = 115200; + di->databits = 8; + di->stopbits = 1; + di->parity = UART_PARITY_NONE; + + uart_bus_space_io = 0; + uart_bus_space_mem = MIPS_PHYS_TO_KSEG1(UART0_BASE); + di->bas.bsh = MIPS_PHYS_TO_KSEG1(UART0_BASE); + + return (0); +} diff --git a/sys/mips/atheros/apb.c b/sys/mips/atheros/apb.c new file mode 100644 index 0000000..d53408c --- /dev/null +++ b/sys/mips/atheros/apb.c @@ -0,0 +1,433 @@ +/*- + * Copyright (c) 2009, Oleksandr Tymoshenko <gonzo@FreeBSD.org> + * 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/interrupt.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/rman.h> +#include <sys/malloc.h> + +#include <machine/bus.h> + +#include <mips/atheros/apbvar.h> +#include <mips/atheros/ar71xxreg.h> + +#undef APB_DEBUG +#ifdef APB_DEBUG +#define dprintf printf +#else +#define dprintf(x, arg...) +#endif /* APB_DEBUG */ + +static int apb_activate_resource(device_t, device_t, int, int, + struct resource *); +static device_t apb_add_child(device_t, int, const char *, int); +static struct resource * + apb_alloc_resource(device_t, device_t, int, int *, u_long, + u_long, u_long, u_int); +static int apb_attach(device_t); +static int apb_deactivate_resource(device_t, device_t, int, int, + struct resource *); +static struct resource_list * + apb_get_resource_list(device_t, device_t); +static void apb_hinted_child(device_t, const char *, int); +static int apb_intr(void *); +static int apb_probe(device_t); +static int apb_release_resource(device_t, device_t, int, int, + struct resource *); +static int apb_setup_intr(device_t, device_t, struct resource *, int, + driver_filter_t *, driver_intr_t *, void *, void **); +static int apb_teardown_intr(device_t, device_t, struct resource *, + void *); + +static void apb_mask_irq(unsigned int irq) +{ + uint32_t reg; + + reg = ATH_READ_REG(AR71XX_MISC_INTR_MASK); + ATH_WRITE_REG(AR71XX_MISC_INTR_MASK, reg & ~(1 << irq)); + +} + +static void apb_unmask_irq(unsigned int irq) +{ + uint32_t reg; + + reg = ATH_READ_REG(AR71XX_MISC_INTR_MASK); + ATH_WRITE_REG(AR71XX_MISC_INTR_MASK, reg | (1 << irq)); +} + +static int +apb_probe(device_t dev) +{ + + return (0); +} + +static int +apb_attach(device_t dev) +{ + struct apb_softc *sc = device_get_softc(dev); + int rid = 0; + + device_set_desc(dev, "APB Bus bridge"); + sc->apb_irq_rman.rm_type = RMAN_ARRAY; + sc->apb_irq_rman.rm_descr = "APB IRQ"; + + if (rman_init(&sc->apb_irq_rman) != 0 || + rman_manage_region(&sc->apb_irq_rman, + APB_IRQ_BASE, APB_IRQ_END) != 0) + panic("apb_attach: failed to set up IRQ rman"); + + if ((sc->sc_misc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_SHAREABLE | RF_ACTIVE)) == NULL) { + device_printf(dev, "unable to allocate IRQ resource\n"); + return (ENXIO); + } + + if ((bus_setup_intr(dev, sc->sc_misc_irq, INTR_TYPE_MISC, + apb_intr, NULL, sc, &sc->sc_misc_ih))) { + device_printf(dev, + "WARNING: unable to register interrupt handler\n"); + return (ENXIO); + } + + bus_generic_probe(dev); + bus_enumerate_hinted_children(dev); + bus_generic_attach(dev); + + return (0); +} + +static struct resource * +apb_alloc_resource(device_t bus, device_t child, int type, int *rid, + u_long start, u_long end, u_long count, u_int flags) +{ + struct apb_softc *sc = device_get_softc(bus); + struct apb_ivar *ivar = device_get_ivars(child); + struct resource *rv; + struct resource_list_entry *rle; + struct rman *rm; + int isdefault, needactivate, passthrough; + + isdefault = (start == 0UL && end == ~0UL); + needactivate = flags & RF_ACTIVE; + /* + * Pass memory requests to nexus device + */ + passthrough = (device_get_parent(child) != bus) || + (type == SYS_RES_MEMORY); + rle = NULL; + + dprintf("%s: entry (%p, %p, %d, %p, %p, %p, %ld, %d)\n", + __func__, bus, child, type, rid, (void *)(intptr_t)start, + (void *)(intptr_t)end, count, flags); + + if (passthrough) + return (BUS_ALLOC_RESOURCE(device_get_parent(bus), child, type, + rid, start, end, count, flags)); + + /* + * If this is an allocation of the "default" range for a given RID, + * and we know what the resources for this device are (ie. they aren't + * maintained by a child bus), then work out the start/end values. + */ + + if (isdefault) { + rle = resource_list_find(&ivar->resources, type, *rid); + if (rle == NULL) { + return (NULL); + } + + if (rle->res != NULL) { + panic("%s: resource entry is busy", __func__); + } + start = rle->start; + end = rle->end; + count = rle->count; + + dprintf("%s: default resource (%p, %p, %ld)\n", + __func__, (void *)(intptr_t)start, + (void *)(intptr_t)end, count); + } + + switch (type) { + case SYS_RES_IRQ: + rm = &sc->apb_irq_rman; + break; + default: + printf("%s: unknown resource type %d\n", __func__, type); + return (0); + } + + rv = rman_reserve_resource(rm, start, end, count, flags, child); + if (rv == 0) { + printf("%s: could not reserve resource\n", __func__); + return (0); + } + + rman_set_rid(rv, *rid); + + if (needactivate) { + if (bus_activate_resource(child, type, *rid, rv)) { + printf("%s: could not activate resource\n", __func__); + rman_release_resource(rv); + return (0); + } + } + + return (rv); +} + +static int +apb_activate_resource(device_t bus, device_t child, int type, int rid, + struct resource *r) +{ + + /* XXX: should we mask/unmask IRQ here? */ + return (BUS_ACTIVATE_RESOURCE(device_get_parent(bus), child, + type, rid, r)); +} + +static int +apb_deactivate_resource(device_t bus, device_t child, int type, int rid, + struct resource *r) +{ + + /* XXX: should we mask/unmask IRQ here? */ + return (BUS_DEACTIVATE_RESOURCE(device_get_parent(bus), child, + type, rid, r)); +} + +static int +apb_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 = apb_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 +apb_setup_intr(device_t bus, device_t child, struct resource *ires, + int flags, driver_filter_t *filt, driver_intr_t *handler, + void *arg, void **cookiep) +{ + struct apb_softc *sc = device_get_softc(bus); + struct intr_event *event; + int irq, error; + + irq = rman_get_start(ires); + + if (irq > APB_IRQ_END) + panic("%s: bad irq %d", __func__, irq); + + event = sc->sc_eventstab[irq]; + if (event == NULL) { + error = intr_event_create(&event, (void *)irq, 0, irq, + (mask_fn)apb_mask_irq, (mask_fn)apb_unmask_irq, + NULL, NULL, + "apb intr%d:", irq); + + sc->sc_eventstab[irq] = event; + } + + intr_event_add_handler(event, device_get_nameunit(child), filt, + handler, arg, intr_priority(flags), flags, cookiep); + + return (0); +} + +static int +apb_teardown_intr(device_t dev, device_t child, struct resource *ires, + void *cookie) +{ + struct apb_softc *sc = device_get_softc(dev); + int irq, result; + + irq = rman_get_start(ires); + if (irq > APB_IRQ_END) + panic("%s: bad irq %d", __func__, irq); + + if (sc->sc_eventstab[irq] == NULL) + panic("Trying to teardown unoccupied IRQ"); + + apb_mask_irq(irq); + + result = intr_event_remove_handler(cookie); + if (!result) + sc->sc_eventstab[irq] = NULL; + + return (result); +} + +static int +apb_intr(void *arg) +{ + struct apb_softc *sc = arg; + struct intr_event *event; + uint32_t reg, irq; + + reg = ATH_READ_REG(AR71XX_MISC_INTR_STATUS); + for (irq = 0; irq < APB_NIRQS; irq++) { + if (reg & (1 << irq)) { + event = sc->sc_eventstab[irq]; + if (!event || TAILQ_EMPTY(&event->ie_handlers)) { + printf("Stray IRQ %d\n", irq); + continue; + } + + /* TODO: frame instead of NULL? */ + intr_event_handle(event, NULL); + } + } + + return (FILTER_HANDLED); +} + +static void +apb_hinted_child(device_t bus, const char *dname, int dunit) +{ + device_t child; + long maddr; + int msize; + int irq; + int result; + int mem_hints_count; + + child = BUS_ADD_CHILD(bus, 0, dname, dunit); + + /* + * Set hard-wired resources for hinted child using + * specific RIDs. + */ + mem_hints_count = 0; + if (resource_long_value(dname, dunit, "maddr", &maddr) == 0) + mem_hints_count++; + if (resource_int_value(dname, dunit, "msize", &msize) == 0) + mem_hints_count++; + + /* check if all info for mem resource has been provided */ + if ((mem_hints_count > 0) && (mem_hints_count < 2)) { + printf("Either maddr or msize hint is missing for %s%d\n", + dname, dunit); + } else if (mem_hints_count) { + result = bus_set_resource(child, SYS_RES_MEMORY, 0, + maddr, msize); + if (result != 0) + device_printf(bus, + "warning: bus_set_resource() failed\n"); + } + + if (resource_int_value(dname, dunit, "irq", &irq) == 0) { + result = bus_set_resource(child, SYS_RES_IRQ, 0, irq, 1); + if (result != 0) + device_printf(bus, + "warning: bus_set_resource() failed\n"); + } +} + +static device_t +apb_add_child(device_t bus, int order, const char *name, int unit) +{ + device_t child; + struct apb_ivar *ivar; + + ivar = malloc(sizeof(struct apb_ivar), M_DEVBUF, M_WAITOK | M_ZERO); + if (ivar == NULL) { + printf("Failed to allocate ivar\n"); + return (0); + } + resource_list_init(&ivar->resources); + + child = device_add_child_ordered(bus, order, name, unit); + if (child == NULL) { + printf("Can't add child %s%d ordered\n", name, unit); + return (0); + } + + device_set_ivars(child, ivar); + + return (child); +} + +/* + * Helper routine for bus_generic_rl_get_resource/bus_generic_rl_set_resource + * Provides pointer to resource_list for these routines + */ +static struct resource_list * +apb_get_resource_list(device_t dev, device_t child) +{ + struct apb_ivar *ivar; + + ivar = device_get_ivars(child); + return (&(ivar->resources)); +} + +static device_method_t apb_methods[] = { + DEVMETHOD(bus_activate_resource, apb_activate_resource), + DEVMETHOD(bus_add_child, apb_add_child), + DEVMETHOD(bus_alloc_resource, apb_alloc_resource), + DEVMETHOD(bus_deactivate_resource, apb_deactivate_resource), + DEVMETHOD(bus_get_resource_list, apb_get_resource_list), + DEVMETHOD(bus_hinted_child, apb_hinted_child), + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_release_resource, apb_release_resource), + DEVMETHOD(bus_setup_intr, apb_setup_intr), + DEVMETHOD(bus_teardown_intr, apb_teardown_intr), + DEVMETHOD(device_attach, apb_attach), + DEVMETHOD(device_probe, apb_probe), + DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), + DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource), + + {0, 0}, +}; + +static driver_t apb_driver = { + "apb", + apb_methods, + sizeof(struct apb_softc), +}; +static devclass_t apb_devclass; + +DRIVER_MODULE(apb, nexus, apb_driver, apb_devclass, 0, 0); diff --git a/sys/mips/atheros/apbvar.h b/sys/mips/atheros/apbvar.h new file mode 100644 index 0000000..e1d29f4 --- /dev/null +++ b/sys/mips/atheros/apbvar.h @@ -0,0 +1,48 @@ +/*- + * Copyright (c) 2009, Oleksandr Tymoshenko <gonzo@FreeBSD.org> + * 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. + */ + +#ifndef _APBVAR_H_ +#define _APBVAR_H_ + +#define APB_IRQ_BASE 0 +#define APB_IRQ_END 7 +#define APB_NIRQS 8 + +struct apb_softc { + struct rman apb_irq_rman; + /* IRQ events structs for child devices */ + struct intr_event *sc_eventstab[APB_NIRQS]; + /* Resources and cookies for MIPS CPU INTs */ + struct resource *sc_misc_irq; + void *sc_misc_ih; +}; + +struct apb_ivar { + struct resource_list resources; +}; + +#endif /* _APBVAR_H_ */ diff --git a/sys/mips/atheros/ar71xx_machdep.c b/sys/mips/atheros/ar71xx_machdep.c new file mode 100644 index 0000000..a5699ed --- /dev/null +++ b/sys/mips/atheros/ar71xx_machdep.c @@ -0,0 +1,161 @@ +/*- + * Copyright (c) 2009 Oleksandr Tymoshenko + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <machine/cpuregs.h> + +#include <mips/sentry5/s5reg.h> + +#include "opt_ddb.h" + +#include <sys/param.h> +#include <sys/conf.h> +#include <sys/kernel.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/cons.h> +#include <sys/kdb.h> + +#include <vm/vm.h> +#include <vm/vm_page.h> + +#include <machine/clock.h> +#include <machine/cpu.h> +#include <machine/hwfunc.h> +#include <machine/md_var.h> +#include <machine/trap.h> +#include <machine/vmparam.h> + +#include <mips/atheros/ar71xxreg.h> + +extern int *edata; +extern int *end; + +void +platform_halt(void) +{ + +} + +void +platform_identify(void) +{ + +} + +void +platform_reset(void) +{ + uint32_t reg = ATH_READ_REG(AR71XX_RST_RESET); + + ATH_WRITE_REG(AR71XX_RST_RESET, reg | RST_RESET_FULL_CHIP); + /* Wait for reset */ + while(1) + ; +} + +void +platform_trap_enter(void) +{ + +} + +void +platform_trap_exit(void) +{ + +} + +void +platform_start(__register_t a0 __unused, __register_t a1 __unused, + __register_t a2 __unused, __register_t a3 __unused) +{ + vm_offset_t kernend; + uint64_t platform_counter_freq; + uint32_t reg; + + /* clear the BSS and SBSS segments */ + kernend = round_page((vm_offset_t)&end); + memset(&edata, 0, kernend - (vm_offset_t)(&edata)); + + /* TODO: Get available memory from RedBoot. Is it possible? */ + realmem = btoc(64*1024*1024); + /* phys_avail regions are in bytes */ + phys_avail[0] = MIPS_KSEG0_TO_PHYS((vm_offset_t)&end); + phys_avail[1] = ctob(realmem); + + physmem = realmem; + + /* + * ns8250 uart code uses DELAY so ticker should be inititalized + * before cninit. And tick_init_params refers to hz, so * init_param1 + * should be called first. + */ + init_param1(); + /* TODO: Get CPU freq from RedBoot. Is it possible? */ + platform_counter_freq = 680000000UL; + mips_timer_init_params(platform_counter_freq, 0); + cninit(); + + printf("arguments: \n"); + printf(" a0 = %08x\n", a0); + printf(" a1 = %08x\n", a1); + printf(" a2 = %08x\n", a2); + printf(" a3 = %08x\n", a3); + + init_param2(physmem); + mips_cpu_init(); + pmap_bootstrap(); + mips_proc0_init(); + mutex_init(); + + /* + * Reset USB devices + */ + reg = ATH_READ_REG(AR71XX_RST_RESET); + reg |= + RST_RESET_USB_OHCI_DLL | RST_RESET_USB_HOST | RST_RESET_USB_PHY; + ATH_WRITE_REG(AR71XX_RST_RESET, reg); + DELAY(1000); + reg &= + ~(RST_RESET_USB_OHCI_DLL | RST_RESET_USB_HOST | RST_RESET_USB_PHY); + ATH_WRITE_REG(AR71XX_RST_RESET, reg); + + ATH_WRITE_REG(AR71XX_USB_CTRL_CONFIG, + USB_CTRL_CONFIG_OHCI_DES_SWAP | USB_CTRL_CONFIG_OHCI_BUF_SWAP | + USB_CTRL_CONFIG_EHCI_DES_SWAP | USB_CTRL_CONFIG_EHCI_BUF_SWAP); + + ATH_WRITE_REG(AR71XX_USB_CTRL_FLADJ, + (32 << USB_CTRL_FLADJ_HOST_SHIFT) | (3 << USB_CTRL_FLADJ_A5_SHIFT)); + DELAY(1000); + +#ifdef DDB + kdb_init(); +#endif +} diff --git a/sys/mips/atheros/ar71xx_ohci.c b/sys/mips/atheros/ar71xx_ohci.c new file mode 100644 index 0000000..b7adc61 --- /dev/null +++ b/sys/mips/atheros/ar71xx_ohci.c @@ -0,0 +1,205 @@ +/*- + * Copyright (c) 2009, Oleksandr Tymoshenko <gonzo@FreeBSD.org> + * 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/module.h> +#include <sys/mutex.h> +#include <sys/bus.h> +#include <sys/queue.h> +#include <machine/bus.h> +#include <sys/rman.h> +#include <machine/resource.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> +#include <dev/usb/usbdivar.h> +#include <dev/usb/usb_mem.h> + +#include <dev/usb/ohcireg.h> +#include <dev/usb/ohcivar.h> + +static int ar71xx_ohci_attach(device_t dev); +static int ar71xx_ohci_detach(device_t dev); +static int ar71xx_ohci_probe(device_t dev); + +struct ar71xx_ohci_softc +{ + struct ohci_softc sc_ohci; +}; + +static int +ar71xx_ohci_probe(device_t dev) +{ + device_set_desc(dev, "AR71XX integrated OHCI controller"); + return (BUS_PROBE_DEFAULT); +} + +static int +ar71xx_ohci_attach(device_t dev) +{ + struct ar71xx_ohci_softc *sc = device_get_softc(dev); + int err; + int rid; + + rid = 0; + sc->sc_ohci.io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (sc->sc_ohci.io_res == NULL) { + err = ENOMEM; + goto error; + } + sc->sc_ohci.iot = rman_get_bustag(sc->sc_ohci.io_res); + sc->sc_ohci.ioh = rman_get_bushandle(sc->sc_ohci.io_res); + + rid = 0; + sc->sc_ohci.irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_ACTIVE); + if (sc->sc_ohci.irq_res == NULL) { + err = ENOMEM; + goto error; + } + sc->sc_ohci.sc_bus.bdev = device_add_child(dev, "usb", -1); + if (sc->sc_ohci.sc_bus.bdev == NULL) { + err = ENOMEM; + goto error; + } + device_set_ivars(sc->sc_ohci.sc_bus.bdev, &sc->sc_ohci.sc_bus); + + /* Allocate a parent dma tag for DMA maps */ + err = bus_dma_tag_create(bus_get_dma_tag(dev), 1, 0, + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, + BUS_SPACE_MAXSIZE_32BIT, USB_DMA_NSEG, BUS_SPACE_MAXSIZE_32BIT, 0, + NULL, NULL, &sc->sc_ohci.sc_bus.parent_dmatag); + if (err) { + device_printf(dev, "Could not allocate parent DMA tag (%d)\n", + err); + err = ENXIO; + goto error; + } + + /* Allocate a dma tag for transfer buffers */ + err = bus_dma_tag_create(sc->sc_ohci.sc_bus.parent_dmatag, 1, 0, + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, + BUS_SPACE_MAXSIZE_32BIT, USB_DMA_NSEG, BUS_SPACE_MAXSIZE_32BIT, 0, + busdma_lock_mutex, &Giant, &sc->sc_ohci.sc_bus.buffer_dmatag); + if (err) { + device_printf(dev, "Could not allocate transfer tag (%d)\n", + err); + err = ENXIO; + goto error; + } + + err = bus_setup_intr(dev, sc->sc_ohci.irq_res, INTR_TYPE_BIO, NULL, + ohci_intr, sc, &sc->sc_ohci.ih); + if (err) { + err = ENXIO; + goto error; + } + strlcpy(sc->sc_ohci.sc_vendor, "Atheros", + sizeof(sc->sc_ohci.sc_vendor)); + + bus_space_write_4(sc->sc_ohci.iot, sc->sc_ohci.ioh, OHCI_CONTROL, 0); + + err = ohci_init(&sc->sc_ohci); + if (!err) { + sc->sc_ohci.sc_flags |= OHCI_SCFLG_DONEINIT; + err = device_probe_and_attach(sc->sc_ohci.sc_bus.bdev); + } + +error: + if (err) { + ar71xx_ohci_detach(dev); + return (err); + } + return (err); +} + +static int +ar71xx_ohci_detach(device_t dev) +{ + struct ar71xx_ohci_softc *sc = device_get_softc(dev); + + if (sc->sc_ohci.sc_flags & OHCI_SCFLG_DONEINIT) { + ohci_detach(&sc->sc_ohci, 0); + sc->sc_ohci.sc_flags &= ~OHCI_SCFLG_DONEINIT; + } + + if (sc->sc_ohci.ih) { + bus_teardown_intr(dev, sc->sc_ohci.irq_res, sc->sc_ohci.ih); + sc->sc_ohci.ih = NULL; + } + + if (sc->sc_ohci.sc_bus.parent_dmatag != NULL) + bus_dma_tag_destroy(sc->sc_ohci.sc_bus.parent_dmatag); + if (sc->sc_ohci.sc_bus.buffer_dmatag != NULL) + bus_dma_tag_destroy(sc->sc_ohci.sc_bus.buffer_dmatag); + + if (sc->sc_ohci.sc_bus.bdev) { + device_delete_child(dev, sc->sc_ohci.sc_bus.bdev); + sc->sc_ohci.sc_bus.bdev = NULL; + } + if (sc->sc_ohci.irq_res) { + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_ohci.irq_res); + sc->sc_ohci.irq_res = NULL; + } + if (sc->sc_ohci.io_res) { + bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_ohci.io_res); + sc->sc_ohci.io_res = NULL; + sc->sc_ohci.iot = 0; + sc->sc_ohci.ioh = 0; + } + return (0); +} + +static device_method_t ohci_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ar71xx_ohci_probe), + DEVMETHOD(device_attach, ar71xx_ohci_attach), + DEVMETHOD(device_detach, ar71xx_ohci_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 ar71xx_ohci_softc), +}; + +static devclass_t ohci_devclass; + +DRIVER_MODULE(ohci, apb, ohci_driver, ohci_devclass, 0, 0); diff --git a/sys/mips/atheros/ar71xx_pci.c b/sys/mips/atheros/ar71xx_pci.c new file mode 100644 index 0000000..d3a9295 --- /dev/null +++ b/sys/mips/atheros/ar71xx_pci.c @@ -0,0 +1,429 @@ +/*- + * Copyright (c) 2009, Oleksandr Tymoshenko <gonzo@FreeBSD.org> + * 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> + +#include <sys/bus.h> +#include <sys/interrupt.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/rman.h> + +#include <vm/vm.h> +#include <vm/pmap.h> +#include <vm/vm_extern.h> + +#include <machine/bus.h> +#include <machine/cpu.h> +#include <machine/pmap.h> + +#include <dev/pci/pcivar.h> +#include <dev/pci/pcireg.h> + +#include <dev/pci/pcib_private.h> +#include "pcib_if.h" + +#include "mips/atheros/ar71xxreg.h" + +#undef AR71XX_PCI_DEBUG +#ifdef AR71XX_PCI_DEBUG +#define dprintf printf +#else +#define dprintf(x, arg...) +#endif + +struct ar71xx_pci_softc { + device_t sc_dev; + + int sc_busno; + struct rman sc_mem_rman; + struct rman sc_irq_rman; + + struct resource *sc_irq; + void *sc_ih; +}; + +/* + * get bitmask for bytes of interest: + * 0 - we want this byte, 1 - ignore it. e.g: we read 1 byte + * from register 7. Bitmask would be: 0111 + */ +static uint32_t +ar71xx_get_bytes_to_read(int reg, int bytes) +{ + uint32_t bytes_to_read = 0; + if ((bytes % 4) == 0) + bytes_to_read = 0; + else if ((bytes % 4) == 1) + bytes_to_read = (~(1 << (reg % 4))) & 0xf; + else if ((bytes % 4) == 2) + bytes_to_read = (~(3 << (reg % 4))) & 0xf; + else + panic("%s: wrong combination", __func__); + + return (bytes_to_read); +} + +static int +ar71xx_pci_check_bus_error(void) +{ + uint32_t error, addr, has_errors = 0; + error = ATH_READ_REG(AR71XX_PCI_ERROR) & 0x3; + dprintf("%s: PCI error = %02x\n", __func__, error); + if (error) { + addr = ATH_READ_REG(AR71XX_PCI_ERROR_ADDR); + + /* Do not report it yet */ +#if 0 + printf("PCI bus error %d at addr 0x%08x\n", error, addr); +#endif + ATH_WRITE_REG(AR71XX_PCI_ERROR, error); + has_errors = 1; + } + + error = ATH_READ_REG(AR71XX_PCI_AHB_ERROR) & 0x1; + dprintf("%s: AHB error = %02x\n", __func__, error); + if (error) { + addr = ATH_READ_REG(AR71XX_PCI_AHB_ERROR_ADDR); + /* Do not report it yet */ +#if 0 + printf("AHB bus error %d at addr 0x%08x\n", error, addr); +#endif + ATH_WRITE_REG(AR71XX_PCI_AHB_ERROR, error); + has_errors = 1; + } + + return (has_errors); +} + +static uint32_t +ar71xx_pci_make_addr(int bus, int slot, int func, int reg) +{ + if (bus == 0) { + return ((1 << slot) | (func << 8) | (reg & ~3)); + } else { + return ((bus << 16) | (slot << 11) | (func << 8) + | (reg & ~3) | 1); + } +} + +static int +ar71xx_pci_conf_setup(int bus, int slot, int func, int reg, int bytes, + uint32_t cmd) +{ + uint32_t addr = ar71xx_pci_make_addr(bus, slot, func, (reg & ~3)); + cmd |= (ar71xx_get_bytes_to_read(reg, bytes) << 4); + + ATH_WRITE_REG(AR71XX_PCI_CONF_ADDR, addr); + ATH_WRITE_REG(AR71XX_PCI_CONF_CMD, cmd); + + dprintf("%s: tag (%x, %x, %x) %d/%d addr=%08x, cmd=%08x\n", __func__, + bus, slot, func, reg, bytes, addr, cmd); + + return ar71xx_pci_check_bus_error(); +} + +static uint32_t +ar71xx_pci_read_config(device_t dev, int bus, int slot, int func, int reg, + int bytes) +{ + uint32_t data; + uint32_t cmd, shift, mask; + + /* register access is 32-bit aligned */ + shift = (reg & 3) * 8; + if (shift) + mask = (1 << shift) - 1; + else + mask = 0xffffffff; + + dprintf("%s: tag (%x, %x, %x) reg %d(%d)\n", __func__, bus, slot, + func, reg, bytes); + + if ((bus == 0) && (slot == 0) && (func == 0)) { + cmd = PCI_LCONF_CMD_READ | (reg & ~3); + ATH_WRITE_REG(AR71XX_PCI_LCONF_CMD, cmd); + data = ATH_READ_REG(AR71XX_PCI_LCONF_READ_DATA); + } else { + if (ar71xx_pci_conf_setup(bus, slot, func, reg, bytes, + PCI_CONF_CMD_READ) == 0) + data = ATH_READ_REG(AR71XX_PCI_CONF_READ_DATA); + else + data = -1; + } + + /* get request bytes from 32-bit word */ + data = (data >> shift) & mask; + + dprintf("%s: read 0x%x\n", __func__, data); + + return (data); +} + +static void +ar71xx_pci_write_config(device_t dev, int bus, int slot, int func, int reg, + uint32_t data, int bytes) +{ + uint32_t cmd; + + dprintf("%s: tag (%x, %x, %x) reg %d(%d)\n", __func__, bus, slot, + func, reg, bytes); + + data = data << (8*(reg % 4)); + + if ((bus == 0) && (slot == 0) && (func == 0)) { + cmd = PCI_LCONF_CMD_WRITE | (reg & ~3); + cmd |= ar71xx_get_bytes_to_read(reg, bytes) << 20; + ATH_WRITE_REG(AR71XX_PCI_LCONF_CMD, cmd); + ATH_WRITE_REG(AR71XX_PCI_LCONF_WRITE_DATA, data); + } else { + if (ar71xx_pci_conf_setup(bus, slot, func, reg, bytes, + PCI_CONF_CMD_WRITE) == 0) + ATH_WRITE_REG(AR71XX_PCI_CONF_WRITE_DATA, data); + } +} + +static int +at71xx_pci_intr(void *v) +{ + panic("Implement me: %s\n", __func__); + return FILTER_HANDLED; +} + +static int +ar71xx_pci_probe(device_t dev) +{ + + return (0); +} + +static int +ar71xx_pci_attach(device_t dev) +{ + int busno = 0; + int rid = 0; + uint32_t reset; + struct ar71xx_pci_softc *sc = device_get_softc(dev); + + sc->sc_mem_rman.rm_type = RMAN_ARRAY; + sc->sc_mem_rman.rm_descr = "ar71xx PCI memory window"; + if (rman_init(&sc->sc_mem_rman) != 0 || + rman_manage_region(&sc->sc_mem_rman, AR71XX_PCI_MEM_BASE, + AR71XX_PCI_MEM_BASE + AR71XX_PCI_MEM_SIZE - 1) != 0) { + panic("ar71xx_pci_attach: failed to set up I/O rman"); + } + + sc->sc_irq_rman.rm_type = RMAN_ARRAY; + sc->sc_irq_rman.rm_descr = "ar71xx PCI IRQs"; + if (rman_init(&sc->sc_irq_rman) != 0 || + rman_manage_region(&sc->sc_irq_rman, AR71XX_PCI_IRQ_START, + AR71XX_PCI_IRQ_END) != 0) + panic("ar71xx_pci_attach: failed to set up IRQ rman"); + + + ATH_WRITE_REG(AR71XX_PCI_INTR_STATUS, 0); + ATH_WRITE_REG(AR71XX_PCI_INTR_MASK, 0); + + /* Hook up our interrupt handler. */ + if ((sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_SHAREABLE | RF_ACTIVE)) == NULL) { + device_printf(dev, "unable to allocate IRQ resource\n"); + return ENXIO; + } + + if ((bus_setup_intr(dev, sc->sc_irq, INTR_TYPE_MISC, + at71xx_pci_intr, NULL, sc, &sc->sc_ih))) { + device_printf(dev, + "WARNING: unable to register interrupt handler\n"); + return ENXIO; + } + + /* reset PCI core and PCI bus */ + reset = ATH_READ_REG(AR71XX_RST_RESET); + reset |= (RST_RESET_PCI_CORE | RST_RESET_PCI_BUS); + ATH_WRITE_REG(AR71XX_RST_RESET, reset); + DELAY(1000); + + reset &= ~(RST_RESET_PCI_CORE | RST_RESET_PCI_BUS); + ATH_WRITE_REG(AR71XX_RST_RESET, reset); + DELAY(1000); + + /* Init PCI windows */ + ATH_WRITE_REG(AR71XX_PCI_WINDOW0, PCI_WINDOW0_ADDR); + ATH_WRITE_REG(AR71XX_PCI_WINDOW1, PCI_WINDOW1_ADDR); + ATH_WRITE_REG(AR71XX_PCI_WINDOW2, PCI_WINDOW2_ADDR); + ATH_WRITE_REG(AR71XX_PCI_WINDOW3, PCI_WINDOW3_ADDR); + ATH_WRITE_REG(AR71XX_PCI_WINDOW4, PCI_WINDOW4_ADDR); + ATH_WRITE_REG(AR71XX_PCI_WINDOW5, PCI_WINDOW5_ADDR); + ATH_WRITE_REG(AR71XX_PCI_WINDOW6, PCI_WINDOW6_ADDR); + ATH_WRITE_REG(AR71XX_PCI_WINDOW7, PCI_WINDOW7_CONF_ADDR); + DELAY(1000); + + ar71xx_pci_check_bus_error(); + + /* Fixup internal PCI bridge */ + ar71xx_pci_write_config(dev, 0, 0, 0, PCIR_COMMAND, + PCIM_CMD_BUSMASTEREN | PCIM_CMD_MEMEN + | PCIM_CMD_SERRESPEN | PCIM_CMD_BACKTOBACK + | PCIM_CMD_PERRESPEN | PCIM_CMD_MWRICEN, 2); + + device_add_child(dev, "pci", busno); + return (bus_generic_attach(dev)); +} + +static int +ar71xx_pci_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) +{ + struct ar71xx_pci_softc *sc = device_get_softc(dev); + + switch (which) { + case PCIB_IVAR_DOMAIN: + *result = 0; + return (0); + case PCIB_IVAR_BUS: + *result = sc->sc_busno; + return (0); + } + + return (ENOENT); +} + +static int +ar71xx_pci_write_ivar(device_t dev, device_t child, int which, uintptr_t result) +{ + struct ar71xx_pci_softc * sc = device_get_softc(dev); + + switch (which) { + case PCIB_IVAR_BUS: + sc->sc_busno = result; + return (0); + } + + return (ENOENT); +} + +static struct resource * +ar71xx_pci_alloc_resource(device_t bus, device_t child, int type, int *rid, + u_long start, u_long end, u_long count, u_int flags) +{ + + struct ar71xx_pci_softc *sc = device_get_softc(bus); + struct resource *rv = NULL; + struct rman *rm; + + switch (type) { + case SYS_RES_IRQ: + rm = &sc->sc_irq_rman; + break; + case SYS_RES_MEMORY: + rm = &sc->sc_mem_rman; + break; + default: + return (NULL); + } + + rv = rman_reserve_resource(rm, start, end, count, flags, child); + + if (rv == NULL) + return (NULL); + + rman_set_rid(rv, *rid); + + if (flags & RF_ACTIVE) { + if (bus_activate_resource(child, type, *rid, rv)) { + rman_release_resource(rv); + return (NULL); + } + } + + return (rv); +} + +static int +ar71xx_pci_teardown_intr(device_t dev, device_t child, struct resource *res, + void *cookie) +{ + + return (intr_event_remove_handler(cookie)); +} + +static int +ar71xx_pci_maxslots(device_t dev) +{ + + return (PCI_SLOTMAX); +} + +static int +ar71xx_pci_route_interrupt(device_t pcib, device_t device, int pin) +{ + + return (pin); +} + +static device_method_t ar71xx_pci_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ar71xx_pci_probe), + DEVMETHOD(device_attach, ar71xx_pci_attach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_resume, bus_generic_resume), + + /* Bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_read_ivar, ar71xx_pci_read_ivar), + DEVMETHOD(bus_write_ivar, ar71xx_pci_write_ivar), + DEVMETHOD(bus_alloc_resource, ar71xx_pci_alloc_resource), + DEVMETHOD(bus_release_resource, bus_generic_release_resource), + DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), + DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), + DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), + DEVMETHOD(bus_teardown_intr, ar71xx_pci_teardown_intr), + + /* pcib interface */ + DEVMETHOD(pcib_maxslots, ar71xx_pci_maxslots), + DEVMETHOD(pcib_read_config, ar71xx_pci_read_config), + DEVMETHOD(pcib_write_config, ar71xx_pci_write_config), + DEVMETHOD(pcib_route_interrupt, ar71xx_pci_route_interrupt), + + {0, 0} +}; + +static driver_t ar71xx_pci_driver = { + "pcib", + ar71xx_pci_methods, + sizeof(struct ar71xx_pci_softc), +}; + +static devclass_t ar71xx_pci_devclass; + +DRIVER_MODULE(ar71xx_pci, nexus, ar71xx_pci_driver, ar71xx_pci_devclass, 0, 0); diff --git a/sys/mips/atheros/ar71xxreg.h b/sys/mips/atheros/ar71xxreg.h new file mode 100644 index 0000000..9367792 --- /dev/null +++ b/sys/mips/atheros/ar71xxreg.h @@ -0,0 +1,317 @@ +/*- + * Copyright (c) 2009 Oleksandr Tymoshenko + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef _AR71XX_REG_H_ +#define _AR71XX_REG_H_ + +#define ATH_READ_REG(reg) \ + *((volatile uint32_t *)MIPS_PHYS_TO_KSEG1((reg))) + +#define ATH_WRITE_REG(reg, val) \ + *((volatile uint32_t *)MIPS_PHYS_TO_KSEG1((reg))) = (val) + +/* PCI region */ +#define AR71XX_PCI_MEM_BASE 0x10000000 +/* + * PCI mem windows is 0x08000000 bytes long but we exclude control + * region from the resource manager + */ +#define AR71XX_PCI_MEM_SIZE 0x07000000 +#define AR71XX_PCI_IRQ_START 0 +#define AR71XX_PCI_IRQ_END 2 + +/* PCI config registers */ +#define AR71XX_PCI_LCONF_CMD 0x17010000 +#define PCI_LCONF_CMD_READ 0x00000000 +#define PCI_LCONF_CMD_WRITE 0x00010000 +#define AR71XX_PCI_LCONF_WRITE_DATA 0x17010004 +#define AR71XX_PCI_LCONF_READ_DATA 0x17010008 +#define AR71XX_PCI_CONF_ADDR 0x1701000C +#define AR71XX_PCI_CONF_CMD 0x17010010 +#define PCI_CONF_CMD_READ 0x0000000A +#define PCI_CONF_CMD_WRITE 0x0000000B +#define AR71XX_PCI_CONF_WRITE_DATA 0x17010014 +#define AR71XX_PCI_CONF_READ_DATA 0x17010018 +#define AR71XX_PCI_ERROR 0x1701001C +#define AR71XX_PCI_ERROR_ADDR 0x17010020 +#define AR71XX_PCI_AHB_ERROR 0x17010024 +#define AR71XX_PCI_AHB_ERROR_ADDR 0x17010028 + +/* APB region */ +/* DDR registers */ +#define AR71XX_DDR_CONFIG 0x18000000 +#define AR71XX_DDR_CONFIG2 0x18000004 +#define AR71XX_DDR_MODE_REGISTER 0x18000008 +#define AR71XX_DDR_EXT_MODE_REGISTER 0x1800000C +#define AR71XX_DDR_CONTROL 0x18000010 +#define AR71XX_DDR_REFRESH 0x18000014 +#define AR71XX_DDR_RD_DATA_THIS_CYCLE 0x18000018 +#define AR71XX_TAP_CONTROL0 0x1800001C +#define AR71XX_TAP_CONTROL1 0x18000020 +#define AR71XX_TAP_CONTROL2 0x18000024 +#define AR71XX_TAP_CONTROL3 0x18000028 +#define AR71XX_PCI_WINDOW0 0x1800007C +#define AR71XX_PCI_WINDOW1 0x18000080 +#define AR71XX_PCI_WINDOW2 0x18000084 +#define AR71XX_PCI_WINDOW3 0x18000088 +#define AR71XX_PCI_WINDOW4 0x1800008C +#define AR71XX_PCI_WINDOW5 0x18000090 +#define AR71XX_PCI_WINDOW6 0x18000094 +#define AR71XX_PCI_WINDOW7 0x18000098 +#define AR71XX_WB_FLUSH_GE0 0x1800009C +#define AR71XX_WB_FLUSH_GE1 0x180000A0 +#define AR71XX_WB_FLUSH_USB 0x180000A4 +#define AR71XX_WB_FLUSH_PCI 0x180000A8 + +/* + * Values for PCI_WINDOW_X registers + */ +#define PCI_WINDOW0_ADDR 0x10000000 +#define PCI_WINDOW1_ADDR 0x11000000 +#define PCI_WINDOW2_ADDR 0x12000000 +#define PCI_WINDOW3_ADDR 0x13000000 +#define PCI_WINDOW4_ADDR 0x14000000 +#define PCI_WINDOW5_ADDR 0x15000000 +#define PCI_WINDOW6_ADDR 0x16000000 +#define PCI_WINDOW7_ADDR 0x17000000 +/* This value enables acces to PCI config registers */ +#define PCI_WINDOW7_CONF_ADDR 0x07000000 + +#define AR71XX_UART_ADDR 0x18020000 + +#define AR71XX_USB_CTRL_FLADJ 0x18030000 +#define USB_CTRL_FLADJ_HOST_SHIFT 12 +#define USB_CTRL_FLADJ_A5_SHIFT 10 +#define USB_CTRL_FLADJ_A4_SHIFT 8 +#define USB_CTRL_FLADJ_A3_SHIFT 6 +#define USB_CTRL_FLADJ_A2_SHIFT 4 +#define USB_CTRL_FLADJ_A1_SHIFT 2 +#define USB_CTRL_FLADJ_A0_SHIFT 0 +#define AR71XX_USB_CTRL_CONFIG 0x18030004 +#define USB_CTRL_CONFIG_OHCI_DES_SWAP (1 << 19) +#define USB_CTRL_CONFIG_OHCI_BUF_SWAP (1 << 18) +#define USB_CTRL_CONFIG_EHCI_DES_SWAP (1 << 17) +#define USB_CTRL_CONFIG_EHCI_BUF_SWAP (1 << 16) +#define USB_CTRL_CONFIG_DISABLE_XTL (1 << 13) +#define USB_CTRL_CONFIG_OVERRIDE_XTL (1 << 12) +#define USB_CTRL_CONFIG_CLK_SEL_SHIFT 4 +#define USB_CTRL_CONFIG_CLK_SEL_MASK 3 +#define USB_CTRL_CONFIG_CLK_SEL_12 0 +#define USB_CTRL_CONFIG_CLK_SEL_24 1 +#define USB_CTRL_CONFIG_CLK_SEL_48 2 +#define USB_CTRL_CONFIG_OVER_CURRENT_AS_GPIO (1 << 8) +#define USB_CTRL_CONFIG_SS_SIMULATION_MODE (1 << 2) +#define USB_CTRL_CONFIG_RESUME_UTMI_PLS_DIS (1 << 1) +#define USB_CTRL_CONFIG_UTMI_BACKWARD_ENB (1 << 0) + +#define AR71XX_PLL_CPU_CONFIG 0x18050000 +#define AR71XX_PLL_SEC_CONFIG 0x18050004 +#define AR71XX_PLL_CPU_CLK_CTRL 0x18050008 +#define AR71XX_PLL_ETH_INT0_CLK 0x18050010 +#define AR71XX_PLL_ETH_INT1_CLK 0x18050014 +#define XPLL_ETH_INT_CLK_10 0x00991099 +#define XPLL_ETH_INT_CLK_100 0x00441011 +#define XPLL_ETH_INT_CLK_1000 0x13110000 +#define XPLL_ETH_INT_CLK_1000_GMII 0x14110000 +#define PLL_ETH_INT_CLK_10 0x00991099 +#define PLL_ETH_INT_CLK_100 0x00001099 +#define PLL_ETH_INT_CLK_1000 0x00110000 +#define AR71XX_PLL_ETH_EXT_CLK 0x18050018 +#define AR71XX_PLL_PCI_CLK 0x1805001C + +/* + * APB interrupt status and mask register and interrupt bit numbers for + */ +#define AR71XX_MISC_INTR_STATUS 0x18060010 +#define AR71XX_MISC_INTR_MASK 0x18060014 +#define MISC_INTR_TIMER 0 +#define MISC_INTR_ERROR 1 +#define MISC_INTR_GPIO 2 +#define MISC_INTR_UART 3 +#define MISC_INTR_WATCHDOG 4 +#define MISC_INTR_PERF 5 +#define MISC_INTR_OHCI 6 +#define MISC_INTR_DMA 7 + +#define AR71XX_PCI_INTR_STATUS 0x18060018 +#define AR71XX_PCI_INTR_MASK 0x1806001C +#define PCI_INTR_CORE (1 << 4) + +#define AR71XX_RST_RESET 0x18060024 +#define RST_RESET_FULL_CHIP (1 << 24) /* Same as pulling + the reset pin */ +#define RST_RESET_CPU_COLD (1 << 20) /* Cold reset */ +#define RST_RESET_GE1_MAC (1 << 13) +#define RST_RESET_GE1_PHY (1 << 12) +#define RST_RESET_GE0_MAC (1 << 9) +#define RST_RESET_GE0_PHY (1 << 8) +#define RST_RESET_USB_OHCI_DLL (1 << 6) +#define RST_RESET_USB_HOST (1 << 5) +#define RST_RESET_USB_PHY (1 << 4) +#define RST_RESET_PCI_BUS (1 << 1) +#define RST_RESET_PCI_CORE (1 << 0) + +/* + * GigE adapters region + */ +#define AR71XX_MAC0_BASE 0x19000000 +#define AR71XX_MAC1_BASE 0x1A000000 + +#define AR71XX_MAC_CFG1 0x00 +#define MAC_CFG1_SOFT_RESET (1 << 31) +#define MAC_CFG1_SIMUL_RESET (1 << 30) +#define MAC_CFG1_MAC_RX_BLOCK_RESET (1 << 19) +#define MAC_CFG1_MAC_TX_BLOCK_RESET (1 << 18) +#define MAC_CFG1_RX_FUNC_RESET (1 << 17) +#define MAC_CFG1_TX_FUNC_RESET (1 << 16) +#define MAC_CFG1_LOOPBACK (1 << 8) +#define MAC_CFG1_RXFLOW_CTRL (1 << 5) +#define MAC_CFG1_TXFLOW_CTRL (1 << 4) +#define MAC_CFG1_SYNC_RX (1 << 3) +#define MAC_CFG1_RX_ENABLE (1 << 2) +#define MAC_CFG1_SYNC_TX (1 << 1) +#define MAC_CFG1_TX_ENABLE (1 << 0) +#define AR71XX_MAC_CFG2 0x04 +#define MAC_CFG2_PREAMBLE_LEN_MASK 0xf +#define MAC_CFG2_PREAMBLE_LEN_SHIFT 12 +#define MAC_CFG2_IFACE_MODE_1000 (2 << 8) +#define MAC_CFG2_IFACE_MODE_10_100 (1 << 8) +#define MAC_CFG2_IFACE_MODE_SHIFT 8 +#define MAC_CFG2_IFACE_MODE_MASK 3 +#define MAC_CFG2_HUGE_FRAME (1 << 5) +#define MAC_CFG2_LENGTH_FIELD (1 << 4) +#define MAC_CFG2_ENABLE_PADCRC (1 << 2) +#define MAC_CFG2_ENABLE_CRC (1 << 1) +#define MAC_CFG2_FULL_DUPLEX (1 << 0) +#define AR71XX_MAC_IFG 0x08 +#define AR71XX_MAC_HDUPLEX 0x0C +#define AR71XX_MAC_MAX_FRAME_LEN 0x10 +#define AR71XX_MAC_MII_CFG 0x20 +#define MAC_MII_CFG_RESET (1 << 31) +#define MAC_MII_CFG_SCAN_AUTO_INC (1 << 5) +#define MAC_MII_CFG_PREAMBLE_SUP (1 << 4) +#define MAC_MII_CFG_CLOCK_SELECT_MASK 0x7 +#define MAC_MII_CFG_CLOCK_DIV_4 0 +#define MAC_MII_CFG_CLOCK_DIV_6 2 +#define MAC_MII_CFG_CLOCK_DIV_8 3 +#define MAC_MII_CFG_CLOCK_DIV_10 4 +#define MAC_MII_CFG_CLOCK_DIV_14 5 +#define MAC_MII_CFG_CLOCK_DIV_20 6 +#define MAC_MII_CFG_CLOCK_DIV_28 7 +#define AR71XX_MAC_MII_CMD 0x24 +#define MAC_MII_CMD_SCAN_CYCLE (1 << 1) +#define MAC_MII_CMD_READ 1 +#define MAC_MII_CMD_WRITE 0 +#define AR71XX_MAC_MII_ADDR 0x28 +#define MAC_MII_PHY_ADDR_SHIFT 8 +#define MAC_MII_PHY_ADDR_MASK 0xff +#define MAC_MII_REG_MASK 0x1f +#define AR71XX_MAC_MII_CONTROL 0x2C +#define MAC_MII_CONTROL_MASK 0xffff +#define AR71XX_MAC_MII_STATUS 0x30 +#define MAC_MII_STATUS_MASK 0xffff +#define AR71XX_MAC_MII_INDICATOR 0x34 +#define MAC_MII_INDICATOR_NOT_VALID (1 << 2) +#define MAC_MII_INDICATOR_SCANNING (1 << 1) +#define MAC_MII_INDICATOR_BUSY (1 << 0) +#define AR71XX_MAC_IFCONTROL 0x38 +#define MAC_IFCONTROL_SPEED (1 << 16) +#define AR71XX_MAC_STA_ADDR1 0x40 +#define AR71XX_MAC_STA_ADDR2 0x44 +#define AR71XX_MAC_FIFO_CFG0 0x48 +#define FIFO_CFG0_TX_FABRIC (1 << 4) +#define FIFO_CFG0_TX_SYSTEM (1 << 3) +#define FIFO_CFG0_RX_FABRIC (1 << 2) +#define FIFO_CFG0_RX_SYSTEM (1 << 1) +#define FIFO_CFG0_WATERMARK (1 << 0) +#define FIFO_CFG0_ALL ((1 << 5) - 1) +#define FIFO_CFG0_ENABLE_SHIFT 8 +#define AR71XX_MAC_FIFO_CFG1 0x4C +#define AR71XX_MAC_FIFO_CFG2 0x50 +#define AR71XX_MAC_FIFO_TX_THRESHOLD 0x54 +#define AR71XX_MAC_FIFO_RX_FILTMATCH 0x58 +#define FIFO_RX_FILTMATCH_ALL ((1 << 18) - 1) +#define AR71XX_MAC_FIFO_RX_FILTMASK 0x5C +#define FIFO_RX_FILTMASK_BYTE_MODE (1 << 19) +#define FIFO_RX_FILTMASK_NO_SHORT_FRAME (1 << 18) +#define FIFO_RX_FILTMASK_ALL ((1 << 20) - 1) +/* + * These flags applicable both to AR71XX_MAC_FIFO_RX_FILTMASK and + * to AR71XX_MAC_FIFO_RX_FILTMATCH + */ +#define FIFO_RX_FILT_UNICAST (1 << 17) +#define FIFO_RX_FILT_TRUNC_FRAME (1 << 16) +#define FIFO_RX_FILT_VLAN_TAG (1 << 15) +#define FIFO_RX_FILT_UNSUP_OPCODE (1 << 14) +#define FIFO_RX_FILT_PAUSE_FRAME (1 << 13) +#define FIFO_RX_FILT_CTRL_FRAME (1 << 12) +#define FIFO_RX_FILT_LONG_EVENT (1 << 11) +#define FIFO_RX_FILT_DRIBBLE_NIBBLE (1 << 10) +#define FIFO_RX_FILT_BCAST (1 << 9) +#define FIFO_RX_FILT_MCAST (1 << 8) +#define FIFO_RX_FILT_OK (1 << 7) +#define FIFO_RX_FILT_OORANGE (1 << 6) +#define FIFO_RX_FILT_LEN_MSMTCH (1 << 5) +#define FIFO_RX_FILT_CRC_ERROR (1 << 4) +#define FIFO_RX_FILT_CODE_ERROR (1 << 3) +#define FIFO_RX_FILT_FALSE_CARRIER (1 << 2) +#define FIFO_RX_FILT_RX_DV_EVENT (1 << 1) +#define FIFO_RX_FILT_DROP_EVENT (1 << 0) +#define AR71XX_MAC_FIFO_RAM0 0x60 +#define AR71XX_MAC_FIFO_RAM1 0x64 +#define AR71XX_MAC_FIFO_RAM2 0x68 +#define AR71XX_MAC_FIFO_RAM3 0x6C +#define AR71XX_MAC_FIFO_RAM4 0x70 +#define AR71XX_MAC_FIFO_RAM5 0x74 +#define AR71XX_MAC_FIFO_RAM6 0x78 +#define AR71XX_DMA_TX_CONTROL 0x180 +#define DMA_TX_CONTROL_EN (1 << 0) +#define AR71XX_DMA_TX_DESC 0x184 +#define AR71XX_DMA_TX_STATUS 0x188 +#define DMA_TX_STATUS_PCOUNT_MASK 0xff +#define DMA_TX_STATUS_PCOUNT_SHIFT 16 +#define DMA_TX_STATUS_BUS_ERROR (1 << 3) +#define DMA_TX_STATUS_UNDERRUN (1 << 1) +#define DMA_TX_STATUS_PKT_SENT (1 << 0) +#define AR71XX_DMA_RX_CONTROL 0x18C +#define DMA_RX_CONTROL_EN (1 << 0) +#define AR71XX_DMA_RX_DESC 0x190 +#define AR71XX_DMA_RX_STATUS 0x194 +#define DMA_RX_STATUS_PCOUNT_MASK 0xff +#define DMA_RX_STATUS_PCOUNT_SHIFT 16 +#define DMA_RX_STATUS_BUS_ERROR (1 << 3) +#define DMA_RX_STATUS_OVERFLOW (1 << 1) +#define DMA_RX_STATUS_PKT_RECVD (1 << 0) +#define AR71XX_DMA_INTR 0x198 +#define AR71XX_DMA_INTR_STATUS 0x19C +#define DMA_INTR_ALL ((1 << 8) - 1) +#define DMA_INTR_RX_BUS_ERROR (1 << 7) +#define DMA_INTR_RX_OVERFLOW (1 << 6) +#define DMA_INTR_RX_PKT_RCVD (1 << 4) +#define DMA_INTR_TX_BUS_ERROR (1 << 3) +#define DMA_INTR_TX_UNDERRUN (1 << 1) +#define DMA_INTR_TX_PKT_SENT (1 << 0) + +#endif /* _AR71XX_REG_H_ */ diff --git a/sys/mips/atheros/files.ar71xx b/sys/mips/atheros/files.ar71xx new file mode 100644 index 0000000..78e1d9c --- /dev/null +++ b/sys/mips/atheros/files.ar71xx @@ -0,0 +1,9 @@ +# $FreeBSD$ + +mips/atheros/apb.c standard +mips/atheros/ar71xx_machdep.c standard +mips/atheros/ar71xx_ohci.c optional ohci +mips/atheros/ar71xx_pci.c optional pci +mips/atheros/if_arge.c optional arge +mips/atheros/uart_bus_ar71xx.c optional uart +mips/atheros/uart_cpu_ar71xx.c optional uart diff --git a/sys/mips/atheros/if_arge.c b/sys/mips/atheros/if_arge.c new file mode 100644 index 0000000..7dbfe70 --- /dev/null +++ b/sys/mips/atheros/if_arge.c @@ -0,0 +1,1657 @@ +/*- + * Copyright (c) 2009, Oleksandr Tymoshenko + * 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * AR71XX gigabit ethernet driver + */ +#include <sys/param.h> +#include <sys/endian.h> +#include <sys/systm.h> +#include <sys/sockio.h> +#include <sys/mbuf.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/socket.h> +#include <sys/taskqueue.h> + +#include <net/if.h> +#include <net/if_arp.h> +#include <net/ethernet.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_types.h> + +#include <net/bpf.h> + +#include <machine/bus.h> +#include <machine/cache.h> +#include <machine/resource.h> +#include <vm/vm_param.h> +#include <vm/vm.h> +#include <vm/pmap.h> +#include <machine/pmap.h> +#include <sys/bus.h> +#include <sys/rman.h> + +#include <dev/mii/mii.h> +#include <dev/mii/miivar.h> + +#include <dev/pci/pcireg.h> +#include <dev/pci/pcivar.h> + +MODULE_DEPEND(arge, ether, 1, 1, 1); +MODULE_DEPEND(arge, miibus, 1, 1, 1); + +#include "miibus_if.h" + +#include <mips/atheros/ar71xxreg.h> +#include <mips/atheros/if_argevar.h> + +#undef ARGE_DEBUG +#ifdef ARGE_DEBUG +#define dprintf printf +#else +#define dprintf(x, arg...) +#endif + +static int arge_attach(device_t); +static int arge_detach(device_t); +static int arge_fix_chain(struct mbuf **mp); +static void arge_flush_ddr(struct arge_softc *); +static int arge_ifmedia_upd(struct ifnet *); +static void arge_ifmedia_sts(struct ifnet *, struct ifmediareq *); +static int arge_ioctl(struct ifnet *, u_long, caddr_t); +static void arge_init(void *); +static void arge_init_locked(struct arge_softc *); +static void arge_link_task(void *, int); +static int arge_miibus_readreg(device_t, int, int); +static void arge_miibus_statchg(device_t); +static int arge_miibus_writereg(device_t, int, int, int); +static int arge_probe(device_t); +static void arge_reset_dma(struct arge_softc *); +static int arge_resume(device_t); +static int arge_rx_ring_init(struct arge_softc *); +static int arge_tx_ring_init(struct arge_softc *); +static void arge_shutdown(device_t); +static void arge_start(struct ifnet *); +static void arge_start_locked(struct ifnet *); +static void arge_stop(struct arge_softc *); +static int arge_suspend(device_t); + +static void arge_rx_locked(struct arge_softc *); +static void arge_tx_locked(struct arge_softc *); +static void arge_intr(void *); +static int arge_intr_filter(void *); +static void arge_tx_intr(struct arge_softc *, uint32_t); +static void arge_rx_intr(struct arge_softc *, uint32_t); +static void arge_tick(void *); + +static void arge_dmamap_cb(void *, bus_dma_segment_t *, int, int); +static int arge_dma_alloc(struct arge_softc *); +static void arge_dma_free(struct arge_softc *); +static int arge_newbuf(struct arge_softc *, int); +static __inline void arge_fixup_rx(struct mbuf *); + +static device_method_t arge_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, arge_probe), + DEVMETHOD(device_attach, arge_attach), + DEVMETHOD(device_detach, arge_detach), + DEVMETHOD(device_suspend, arge_suspend), + DEVMETHOD(device_resume, arge_resume), + DEVMETHOD(device_shutdown, arge_shutdown), + + /* bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_driver_added, bus_generic_driver_added), + + /* MII interface */ + DEVMETHOD(miibus_readreg, arge_miibus_readreg), + DEVMETHOD(miibus_writereg, arge_miibus_writereg), + DEVMETHOD(miibus_statchg, arge_miibus_statchg), + + { 0, 0 } +}; + +static driver_t arge_driver = { + "arge", + arge_methods, + sizeof(struct arge_softc) +}; + +static devclass_t arge_devclass; + +DRIVER_MODULE(arge, nexus, arge_driver, arge_devclass, 0, 0); +DRIVER_MODULE(miibus, arge, miibus_driver, miibus_devclass, 0, 0); + +/* + * Flushes all + */ +static void +arge_flush_ddr(struct arge_softc *sc) +{ + + ATH_WRITE_REG(sc->arge_ddr_flush_reg, 1); + while (ATH_READ_REG(sc->arge_ddr_flush_reg) & 1) + ; + + ATH_WRITE_REG(sc->arge_ddr_flush_reg, 1); + while (ATH_READ_REG(sc->arge_ddr_flush_reg) & 1) + ; +} + +static int +arge_probe(device_t dev) +{ + + device_set_desc(dev, "Atheros AR71xx built-in ethernet interface"); + return (0); +} + +static int +arge_attach(device_t dev) +{ + uint8_t eaddr[ETHER_ADDR_LEN]; + struct ifnet *ifp; + struct arge_softc *sc; + int error = 0, rid, phynum; + uint32_t reg; + + sc = device_get_softc(dev); + sc->arge_dev = dev; + sc->arge_mac_unit = device_get_unit(dev); + + KASSERT(((sc->arge_mac_unit == 0) || (sc->arge_mac_unit == 1)), + ("if_arge: Only MAC0 and MAC1 supported")); + if (sc->arge_mac_unit == 0) { + sc->arge_ddr_flush_reg = AR71XX_WB_FLUSH_GE0; + sc->arge_pll_reg = AR71XX_PLL_ETH_INT0_CLK; + } else { + sc->arge_ddr_flush_reg = AR71XX_WB_FLUSH_GE1; + sc->arge_pll_reg = AR71XX_PLL_ETH_INT1_CLK; + } + + /* + * Get which PHY of 5 available we should use for this unit + */ + if (resource_int_value(device_get_name(dev), device_get_unit(dev), + "phy", &phynum) != 0) { + /* + * Use port 4 (WAN) for GE0. For any other port use + * its PHY the same as its unit number + */ + if (sc->arge_mac_unit == 0) + phynum = 4; + else + phynum = sc->arge_mac_unit; + + device_printf(dev, "No PHY specified, using %d\n", phynum); + } + + sc->arge_phy_num = phynum; + + + mtx_init(&sc->arge_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, + MTX_DEF); + callout_init_mtx(&sc->arge_stat_callout, &sc->arge_mtx, 0); + TASK_INIT(&sc->arge_link_task, 0, arge_link_task, sc); + + /* Map control/status registers. */ + sc->arge_rid = 0; + sc->arge_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, + &sc->arge_rid, RF_ACTIVE); + + if (sc->arge_res == NULL) { + device_printf(dev, "couldn't map memory\n"); + error = ENXIO; + goto fail; + } + + /* Allocate interrupts */ + rid = 0; + sc->arge_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_SHAREABLE | RF_ACTIVE); + + if (sc->arge_irq == NULL) { + device_printf(dev, "couldn't map interrupt\n"); + error = ENXIO; + goto fail; + } + + /* Allocate ifnet structure. */ + ifp = sc->arge_ifp = if_alloc(IFT_ETHER); + + if (ifp == NULL) { + device_printf(dev, "couldn't allocate ifnet structure\n"); + error = ENOSPC; + goto fail; + } + + 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_ioctl = arge_ioctl; + ifp->if_start = arge_start; + ifp->if_init = arge_init; + + /* XXX: add real size */ + IFQ_SET_MAXLEN(&ifp->if_snd, 9); + ifp->if_snd.ifq_maxlen = 9; + IFQ_SET_READY(&ifp->if_snd); + + ifp->if_capenable = ifp->if_capabilities; + + eaddr[0] = 0x00; + eaddr[1] = 0x15; + eaddr[2] = 0x6d; + eaddr[3] = 0xc1; + eaddr[4] = 0x28; + eaddr[5] = 0x2e; + + if (arge_dma_alloc(sc) != 0) { + error = ENXIO; + goto fail; + } + + ARGE_WRITE(sc, AR71XX_MAC_CFG1, + MAC_CFG1_SYNC_RX | MAC_CFG1_RX_ENABLE | + MAC_CFG1_SYNC_TX | MAC_CFG1_TX_ENABLE); + + reg = ARGE_READ(sc, AR71XX_MAC_CFG2); + reg |= MAC_CFG2_ENABLE_PADCRC | MAC_CFG2_LENGTH_FIELD ; + ARGE_WRITE(sc, AR71XX_MAC_CFG2, reg); + + ARGE_WRITE(sc, AR71XX_MAC_MAX_FRAME_LEN, 1536); + + /* Reset MII bus */ + ARGE_WRITE(sc, AR71XX_MAC_MII_CFG, MAC_MII_CFG_RESET); + DELAY(100); + ARGE_WRITE(sc, AR71XX_MAC_MII_CFG, MAC_MII_CFG_CLOCK_DIV_28); + DELAY(100); + + /* + * Set all Ethernet address registers to the same initial values + * set all four addresses to 66-88-aa-cc-dd-ee + */ + ARGE_WRITE(sc, AR71XX_MAC_STA_ADDR1, 0x6dc1282e); + ARGE_WRITE(sc, AR71XX_MAC_STA_ADDR2, 0x00000015); + + ARGE_WRITE(sc, AR71XX_MAC_FIFO_CFG0, + FIFO_CFG0_ALL << FIFO_CFG0_ENABLE_SHIFT); + ARGE_WRITE(sc, AR71XX_MAC_FIFO_CFG1, 0x0fff0000); + ARGE_WRITE(sc, AR71XX_MAC_FIFO_CFG2, 0x00001fff); + + reg = FIFO_RX_FILTMATCH_ALL; + ARGE_WRITE(sc, AR71XX_MAC_FIFO_RX_FILTMATCH, reg); + + reg = FIFO_RX_FILTMASK_ALL; + reg &= ~FIFO_RX_FILTMASK_BYTE_MODE; + ARGE_WRITE(sc, AR71XX_MAC_FIFO_RX_FILTMASK, reg); + + /* Do MII setup. */ + if (mii_phy_probe(dev, &sc->arge_miibus, + arge_ifmedia_upd, arge_ifmedia_sts)) { + device_printf(dev, "MII without any phy!\n"); + error = ENXIO; + goto fail; + } + + /* Call MI attach routine. */ + ether_ifattach(ifp, eaddr); + + /* Hook interrupt last to avoid having to lock softc */ + error = bus_setup_intr(dev, sc->arge_irq, INTR_TYPE_NET | INTR_MPSAFE, + arge_intr_filter, arge_intr, sc, &sc->arge_intrhand); + + if (error) { + device_printf(dev, "couldn't set up irq\n"); + ether_ifdetach(ifp); + goto fail; + } + +fail: + if (error) + arge_detach(dev); + + return (error); +} + +static int +arge_detach(device_t dev) +{ + struct arge_softc *sc = device_get_softc(dev); + struct ifnet *ifp = sc->arge_ifp; + + KASSERT(mtx_initialized(&sc->arge_mtx), ("arge mutex not initialized")); + + /* These should only be active if attach succeeded */ + if (device_is_attached(dev)) { + ARGE_LOCK(sc); + sc->arge_detach = 1; + arge_stop(sc); + ARGE_UNLOCK(sc); + taskqueue_drain(taskqueue_swi, &sc->arge_link_task); + ether_ifdetach(ifp); + } + + if (sc->arge_miibus) + device_delete_child(dev, sc->arge_miibus); + bus_generic_detach(dev); + + if (sc->arge_intrhand) + bus_teardown_intr(dev, sc->arge_irq, sc->arge_intrhand); + + if (sc->arge_res) + bus_release_resource(dev, SYS_RES_MEMORY, sc->arge_rid, + sc->arge_res); + + if (ifp) + if_free(ifp); + + arge_dma_free(sc); + + mtx_destroy(&sc->arge_mtx); + + return (0); + +} + +static int +arge_suspend(device_t dev) +{ + + panic("%s", __func__); + return 0; +} + +static int +arge_resume(device_t dev) +{ + + panic("%s", __func__); + return 0; +} + +static void +arge_shutdown(device_t dev) +{ + struct arge_softc *sc; + + sc = device_get_softc(dev); + + ARGE_LOCK(sc); + arge_stop(sc); + ARGE_UNLOCK(sc); +} + +static int +arge_miibus_readreg(device_t dev, int phy, int reg) +{ + struct arge_softc * sc = device_get_softc(dev); + int i, result; + uint32_t addr = 0x1000 | (phy << MAC_MII_PHY_ADDR_SHIFT) + | (reg & MAC_MII_REG_MASK); + + if (phy != sc->arge_phy_num) + return (0); + + ARGE_WRITE(sc, AR71XX_MAC_MII_CMD, MAC_MII_CMD_WRITE); + ARGE_WRITE(sc, AR71XX_MAC_MII_ADDR, addr); + ARGE_WRITE(sc, AR71XX_MAC_MII_CMD, MAC_MII_CMD_READ); + + i = ARGE_MII_TIMEOUT; + while ((ARGE_READ(sc, AR71XX_MAC_MII_INDICATOR) & + MAC_MII_INDICATOR_BUSY) && (i--)) + DELAY(5); + + if (i < 0) { + dprintf("%s timedout\n", __func__); + /* XXX: return ERRNO istead? */ + return (-1); + } + + result = ARGE_READ(sc, AR71XX_MAC_MII_STATUS) & MAC_MII_STATUS_MASK; + ARGE_WRITE(sc, AR71XX_MAC_MII_CMD, MAC_MII_CMD_WRITE); + dprintf("%s: phy=%d, reg=%02x, value[%08x]=%04x\n", __func__, + phy, reg, addr, result); + + return (result); +} + +static int +arge_miibus_writereg(device_t dev, int phy, int reg, int data) +{ + struct arge_softc * sc = device_get_softc(dev); + int i; + uint32_t addr = 0x1000 + | (phy << MAC_MII_PHY_ADDR_SHIFT) | (reg & MAC_MII_REG_MASK); + + dprintf("%s: phy=%d, reg=%02x, value=%04x\n", __func__, + phy, reg, data); + + ARGE_WRITE(sc, AR71XX_MAC_MII_ADDR, addr); + ARGE_WRITE(sc, AR71XX_MAC_MII_CONTROL, data); + + i = ARGE_MII_TIMEOUT; + while ((ARGE_READ(sc, AR71XX_MAC_MII_INDICATOR) & + MAC_MII_INDICATOR_BUSY) && (i--)) + DELAY(5); + + if (i < 0) { + dprintf("%s timedout\n", __func__); + /* XXX: return ERRNO istead? */ + return (-1); + } + + return (0); +} + +static void +arge_miibus_statchg(device_t dev) +{ + struct arge_softc *sc; + + sc = device_get_softc(dev); + taskqueue_enqueue(taskqueue_swi, &sc->arge_link_task); +} + +static void +arge_link_task(void *arg, int pending) +{ + struct arge_softc *sc; + struct mii_data *mii; + struct ifnet *ifp; + uint32_t media; + uint32_t cfg, ifcontrol, rx_filtmask, pll, sec_cfg; + + sc = (struct arge_softc *)arg; + + ARGE_LOCK(sc); + mii = device_get_softc(sc->arge_miibus); + ifp = sc->arge_ifp; + if (mii == NULL || ifp == NULL || + (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { + ARGE_UNLOCK(sc); + return; + } + + if (mii->mii_media_status & IFM_ACTIVE) { + + media = IFM_SUBTYPE(mii->mii_media_active); + + if (media != IFM_NONE) { + sc->arge_link_status = 1; + + cfg = ARGE_READ(sc, AR71XX_MAC_CFG2); + ifcontrol = ARGE_READ(sc, AR71XX_MAC_IFCONTROL); + rx_filtmask = + ARGE_READ(sc, AR71XX_MAC_FIFO_RX_FILTMASK); + + cfg &= ~(MAC_CFG2_IFACE_MODE_1000 + | MAC_CFG2_IFACE_MODE_10_100 + | MAC_CFG2_FULL_DUPLEX); + ifcontrol &= ~MAC_IFCONTROL_SPEED; + rx_filtmask &= ~FIFO_RX_FILTMASK_BYTE_MODE; + + switch(media) { + case IFM_10_T: + cfg |= MAC_CFG2_IFACE_MODE_10_100; + pll = PLL_ETH_INT_CLK_10; + break; + case IFM_100_TX: + cfg |= MAC_CFG2_IFACE_MODE_10_100; + ifcontrol |= MAC_IFCONTROL_SPEED; + pll = PLL_ETH_INT_CLK_100; + break; + case IFM_1000_T: + case IFM_1000_SX: + cfg |= MAC_CFG2_IFACE_MODE_1000; + rx_filtmask |= FIFO_RX_FILTMASK_BYTE_MODE; + pll = PLL_ETH_INT_CLK_1000; + break; + default: + pll = PLL_ETH_INT_CLK_100; + device_printf(sc->arge_dev, + "Unknown media %d\n", media); + } + + ARGE_WRITE(sc, AR71XX_MAC_FIFO_TX_THRESHOLD, + 0x008001ff); + + ARGE_WRITE(sc, AR71XX_MAC_CFG2, cfg); + ARGE_WRITE(sc, AR71XX_MAC_IFCONTROL, ifcontrol); + ARGE_WRITE(sc, AR71XX_MAC_FIFO_RX_FILTMASK, + rx_filtmask); + + /* set PLL registers */ + sec_cfg = ATH_READ_REG(AR71XX_PLL_CPU_CONFIG); + sec_cfg &= ~(3 << 17); + sec_cfg |= (2 << 17); + + ATH_WRITE_REG(AR71XX_PLL_CPU_CONFIG, sec_cfg); + DELAY(100); + + ATH_WRITE_REG(sc->arge_pll_reg, pll); + + sec_cfg |= (3 << 17); + ATH_WRITE_REG(AR71XX_PLL_CPU_CONFIG, sec_cfg); + DELAY(100); + + sec_cfg &= ~(3 << 17); + ATH_WRITE_REG(AR71XX_PLL_CPU_CONFIG, sec_cfg); + DELAY(100); + } + } else + sc->arge_link_status = 0; + + ARGE_UNLOCK(sc); +} + +static void +arge_reset_dma(struct arge_softc *sc) +{ + unsigned int i; + + ARGE_WRITE(sc, AR71XX_DMA_RX_CONTROL, 0); + ARGE_WRITE(sc, AR71XX_DMA_TX_CONTROL, 0); + + ARGE_WRITE(sc, AR71XX_DMA_RX_DESC, 0); + ARGE_WRITE(sc, AR71XX_DMA_TX_DESC, 0); + + /* Clear all possible RX interrupts */ + for (i = 0; i < ARGE_RX_RING_COUNT; i++) + ARGE_WRITE(sc, AR71XX_DMA_RX_STATUS, DMA_RX_STATUS_PKT_RECVD); + + /* + * Clear all possible TX interrupts + */ + for (i = 0; i < ARGE_TX_RING_COUNT; i++) + ARGE_WRITE(sc, AR71XX_DMA_TX_STATUS, DMA_TX_STATUS_PKT_SENT); + + /* + * Now Rx/Tx errors + */ + ARGE_WRITE(sc, AR71XX_DMA_RX_STATUS, + DMA_RX_STATUS_BUS_ERROR | DMA_RX_STATUS_OVERFLOW); + ARGE_WRITE(sc, AR71XX_DMA_TX_STATUS, + DMA_TX_STATUS_BUS_ERROR | DMA_TX_STATUS_UNDERRUN); +} + + + +static void +arge_init(void *xsc) +{ + struct arge_softc *sc = xsc; + + ARGE_LOCK(sc); + arge_init_locked(sc); + ARGE_UNLOCK(sc); +} + +static void +arge_init_locked(struct arge_softc *sc) +{ + struct ifnet *ifp = sc->arge_ifp; + struct mii_data *mii; + + ARGE_LOCK_ASSERT(sc); + + mii = device_get_softc(sc->arge_miibus); + + arge_stop(sc); + + /* Init circular RX list. */ + if (arge_rx_ring_init(sc) != 0) { + device_printf(sc->arge_dev, + "initialization failed: no memory for rx buffers\n"); + arge_stop(sc); + return; + } + + /* Init tx descriptors. */ + arge_tx_ring_init(sc); + + arge_reset_dma(sc); + + sc->arge_link_status = 0; + mii_mediachg(mii); + + ifp->if_drv_flags |= IFF_DRV_RUNNING; + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + + callout_reset(&sc->arge_stat_callout, hz, arge_tick, sc); + ARGE_WRITE(sc, AR71XX_DMA_TX_DESC, ARGE_TX_RING_ADDR(sc, 0)); + ARGE_WRITE(sc, AR71XX_DMA_RX_DESC, ARGE_RX_RING_ADDR(sc, 0)); + + /* Start listening */ + ARGE_WRITE(sc, AR71XX_DMA_RX_CONTROL, DMA_RX_CONTROL_EN); + + /* Enable interrupts */ + ARGE_WRITE(sc, AR71XX_DMA_INTR, DMA_INTR_ALL); +} + +/* + * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data + * pointers to the fragment pointers. + */ +static int +arge_encap(struct arge_softc *sc, struct mbuf **m_head) +{ + struct arge_txdesc *txd; + struct arge_desc *desc, *prev_desc; + bus_dma_segment_t txsegs[ARGE_MAXFRAGS]; + int error, i, nsegs, prod, si, prev_prod; + + ARGE_LOCK_ASSERT(sc); + + prod = sc->arge_cdata.arge_tx_prod; + txd = &sc->arge_cdata.arge_txdesc[prod]; + error = bus_dmamap_load_mbuf_sg(sc->arge_cdata.arge_tx_tag, + txd->tx_dmamap, *m_head, txsegs, &nsegs, BUS_DMA_NOWAIT); + + if (error == EFBIG) { + panic("EFBIG"); + } else if (error != 0) + return (error); + + if (nsegs == 0) { + m_freem(*m_head); + *m_head = NULL; + return (EIO); + } + + /* Check number of available descriptors. */ + if (sc->arge_cdata.arge_tx_cnt + nsegs >= (ARGE_TX_RING_COUNT - 1)) { + bus_dmamap_unload(sc->arge_cdata.arge_tx_tag, txd->tx_dmamap); + return (ENOBUFS); + } + + txd->tx_m = *m_head; + bus_dmamap_sync(sc->arge_cdata.arge_tx_tag, txd->tx_dmamap, + BUS_DMASYNC_PREWRITE); + + si = prod; + + /* + * Make a list of descriptors for this packet. DMA controller will + * walk through it while arge_link is not zero. + */ + prev_prod = prod; + desc = prev_desc = NULL; + for (i = 0; i < nsegs; i++) { + desc = &sc->arge_rdata.arge_tx_ring[prod]; + desc->packet_ctrl = ARGE_DMASIZE(txsegs[i].ds_len); + + desc->packet_addr = txsegs[i].ds_addr; + /* link with previous descriptor */ + if (prev_desc) + prev_desc->packet_ctrl |= ARGE_DESC_MORE; + + sc->arge_cdata.arge_tx_cnt++; + prev_desc = desc; + ARGE_INC(prod, ARGE_TX_RING_COUNT); + } + + /* Update producer index. */ + sc->arge_cdata.arge_tx_prod = prod; + + /* Sync descriptors. */ + bus_dmamap_sync(sc->arge_cdata.arge_tx_ring_tag, + sc->arge_cdata.arge_tx_ring_map, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + + /* Start transmitting */ + ARGE_WRITE(sc, AR71XX_DMA_TX_CONTROL, DMA_TX_CONTROL_EN); + return (0); +} + +static void +arge_start(struct ifnet *ifp) +{ + struct arge_softc *sc; + + sc = ifp->if_softc; + + ARGE_LOCK(sc); + arge_start_locked(ifp); + ARGE_UNLOCK(sc); +} + +static void +arge_start_locked(struct ifnet *ifp) +{ + struct arge_softc *sc; + struct mbuf *m_head; + int enq; + + sc = ifp->if_softc; + + ARGE_LOCK_ASSERT(sc); + + if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != + IFF_DRV_RUNNING || sc->arge_link_status == 0 ) + return; + + arge_flush_ddr(sc); + + for (enq = 0; !IFQ_DRV_IS_EMPTY(&ifp->if_snd) && + sc->arge_cdata.arge_tx_cnt < ARGE_TX_RING_COUNT - 2; ) { + IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head); + if (m_head == NULL) + break; + + /* + * Fix mbuf chain, all fragments should be 4 bytes aligned and + * even 4 bytes + */ + arge_fix_chain(&m_head); + + if (m_head == NULL) { + dprintf("failed to adjust mbuf chain\n"); + } + + /* + * Pack the data into the transmit ring. + */ + if (arge_encap(sc, &m_head)) { + if (m_head == NULL) + break; + IFQ_DRV_PREPEND(&ifp->if_snd, m_head); + ifp->if_drv_flags |= IFF_DRV_OACTIVE; + break; + } + + enq++; + /* + * If there's a BPF listener, bounce a copy of this frame + * to him. + */ + ETHER_BPF_MTAP(ifp, m_head); + } +} + +static void +arge_stop(struct arge_softc *sc) +{ + struct ifnet *ifp; + + ARGE_LOCK_ASSERT(sc); + + ifp = sc->arge_ifp; + ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); + callout_stop(&sc->arge_stat_callout); + + /* mask out interrupts */ + ARGE_WRITE(sc, AR71XX_DMA_INTR, 0); + + arge_reset_dma(sc); +} + + +static int +arge_ioctl(struct ifnet *ifp, u_long command, caddr_t data) +{ + struct arge_softc *sc = ifp->if_softc; + struct ifreq *ifr = (struct ifreq *) data; + struct mii_data *mii; + int error; + + switch (command) { + case SIOCSIFFLAGS: + printf("Implement me: SIOCSIFFLAGS\n"); + error = 0; + break; + case SIOCADDMULTI: + case SIOCDELMULTI: + printf("Implement me: SIOCDELMULTI\n"); + error = 0; + break; + case SIOCGIFMEDIA: + case SIOCSIFMEDIA: + printf("Implement me: SIOCSIFMEDIA\n"); + mii = device_get_softc(sc->arge_miibus); + error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); + break; + case SIOCSIFCAP: + error = 0; + ifp->if_hwassist = 0; + printf("Implement me: SIOCSIFCAP\n"); + break; + default: + error = ether_ioctl(ifp, command, data); + break; + } + + return (error); +} + +/* + * Set media options. + */ +static int +arge_ifmedia_upd(struct ifnet *ifp) +{ + struct arge_softc *sc; + struct mii_data *mii; + struct mii_softc *miisc; + int error; + + sc = ifp->if_softc; + ARGE_LOCK(sc); + mii = device_get_softc(sc->arge_miibus); + if (mii->mii_instance) { + LIST_FOREACH(miisc, &mii->mii_phys, mii_list) + mii_phy_reset(miisc); + } + error = mii_mediachg(mii); + ARGE_UNLOCK(sc); + + return (error); +} + +/* + * Report current media status. + */ +static void +arge_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) +{ + struct arge_softc *sc = ifp->if_softc; + struct mii_data *mii; + + mii = device_get_softc(sc->arge_miibus); + ARGE_LOCK(sc); + mii_pollstat(mii); + ARGE_UNLOCK(sc); + ifmr->ifm_active = mii->mii_media_active; + ifmr->ifm_status = mii->mii_media_status; +} + +struct arge_dmamap_arg { + bus_addr_t arge_busaddr; +}; + +static void +arge_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error) +{ + struct arge_dmamap_arg *ctx; + + if (error != 0) + return; + ctx = arg; + ctx->arge_busaddr = segs[0].ds_addr; +} + +static int +arge_dma_alloc(struct arge_softc *sc) +{ + struct arge_dmamap_arg ctx; + struct arge_txdesc *txd; + struct arge_rxdesc *rxd; + int error, i; + + /* Create parent DMA tag. */ + error = bus_dma_tag_create( + bus_get_dma_tag(sc->arge_dev), /* parent */ + 1, 0, /* alignment, boundary */ + BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + BUS_SPACE_MAXSIZE_32BIT, /* maxsize */ + 0, /* nsegments */ + BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ + 0, /* flags */ + NULL, NULL, /* lockfunc, lockarg */ + &sc->arge_cdata.arge_parent_tag); + if (error != 0) { + device_printf(sc->arge_dev, "failed to create parent DMA tag\n"); + goto fail; + } + /* Create tag for Tx ring. */ + error = bus_dma_tag_create( + sc->arge_cdata.arge_parent_tag, /* parent */ + ARGE_RING_ALIGN, 0, /* alignment, boundary */ + BUS_SPACE_MAXADDR, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + ARGE_TX_DMA_SIZE, /* maxsize */ + 1, /* nsegments */ + ARGE_TX_DMA_SIZE, /* maxsegsize */ + 0, /* flags */ + NULL, NULL, /* lockfunc, lockarg */ + &sc->arge_cdata.arge_tx_ring_tag); + if (error != 0) { + device_printf(sc->arge_dev, "failed to create Tx ring DMA tag\n"); + goto fail; + } + + /* Create tag for Rx ring. */ + error = bus_dma_tag_create( + sc->arge_cdata.arge_parent_tag, /* parent */ + ARGE_RING_ALIGN, 0, /* alignment, boundary */ + BUS_SPACE_MAXADDR, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + ARGE_RX_DMA_SIZE, /* maxsize */ + 1, /* nsegments */ + ARGE_RX_DMA_SIZE, /* maxsegsize */ + 0, /* flags */ + NULL, NULL, /* lockfunc, lockarg */ + &sc->arge_cdata.arge_rx_ring_tag); + if (error != 0) { + device_printf(sc->arge_dev, "failed to create Rx ring DMA tag\n"); + goto fail; + } + + /* Create tag for Tx buffers. */ + error = bus_dma_tag_create( + sc->arge_cdata.arge_parent_tag, /* parent */ + sizeof(uint32_t), 0, /* alignment, boundary */ + BUS_SPACE_MAXADDR, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + MCLBYTES * ARGE_MAXFRAGS, /* maxsize */ + ARGE_MAXFRAGS, /* nsegments */ + MCLBYTES, /* maxsegsize */ + 0, /* flags */ + NULL, NULL, /* lockfunc, lockarg */ + &sc->arge_cdata.arge_tx_tag); + if (error != 0) { + device_printf(sc->arge_dev, "failed to create Tx DMA tag\n"); + goto fail; + } + + /* Create tag for Rx buffers. */ + error = bus_dma_tag_create( + sc->arge_cdata.arge_parent_tag, /* parent */ + ARGE_RX_ALIGN, 0, /* alignment, boundary */ + BUS_SPACE_MAXADDR, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + MCLBYTES, /* maxsize */ + 1, /* nsegments */ + MCLBYTES, /* maxsegsize */ + 0, /* flags */ + NULL, NULL, /* lockfunc, lockarg */ + &sc->arge_cdata.arge_rx_tag); + if (error != 0) { + device_printf(sc->arge_dev, "failed to create Rx DMA tag\n"); + goto fail; + } + + /* Allocate DMA'able memory and load the DMA map for Tx ring. */ + error = bus_dmamem_alloc(sc->arge_cdata.arge_tx_ring_tag, + (void **)&sc->arge_rdata.arge_tx_ring, BUS_DMA_WAITOK | + BUS_DMA_COHERENT | BUS_DMA_ZERO, &sc->arge_cdata.arge_tx_ring_map); + if (error != 0) { + device_printf(sc->arge_dev, + "failed to allocate DMA'able memory for Tx ring\n"); + goto fail; + } + + ctx.arge_busaddr = 0; + error = bus_dmamap_load(sc->arge_cdata.arge_tx_ring_tag, + sc->arge_cdata.arge_tx_ring_map, sc->arge_rdata.arge_tx_ring, + ARGE_TX_DMA_SIZE, arge_dmamap_cb, &ctx, 0); + if (error != 0 || ctx.arge_busaddr == 0) { + device_printf(sc->arge_dev, + "failed to load DMA'able memory for Tx ring\n"); + goto fail; + } + sc->arge_rdata.arge_tx_ring_paddr = ctx.arge_busaddr; + + /* Allocate DMA'able memory and load the DMA map for Rx ring. */ + error = bus_dmamem_alloc(sc->arge_cdata.arge_rx_ring_tag, + (void **)&sc->arge_rdata.arge_rx_ring, BUS_DMA_WAITOK | + BUS_DMA_COHERENT | BUS_DMA_ZERO, &sc->arge_cdata.arge_rx_ring_map); + if (error != 0) { + device_printf(sc->arge_dev, + "failed to allocate DMA'able memory for Rx ring\n"); + goto fail; + } + + ctx.arge_busaddr = 0; + error = bus_dmamap_load(sc->arge_cdata.arge_rx_ring_tag, + sc->arge_cdata.arge_rx_ring_map, sc->arge_rdata.arge_rx_ring, + ARGE_RX_DMA_SIZE, arge_dmamap_cb, &ctx, 0); + if (error != 0 || ctx.arge_busaddr == 0) { + device_printf(sc->arge_dev, + "failed to load DMA'able memory for Rx ring\n"); + goto fail; + } + sc->arge_rdata.arge_rx_ring_paddr = ctx.arge_busaddr; + + /* Create DMA maps for Tx buffers. */ + for (i = 0; i < ARGE_TX_RING_COUNT; i++) { + txd = &sc->arge_cdata.arge_txdesc[i]; + txd->tx_m = NULL; + txd->tx_dmamap = NULL; + error = bus_dmamap_create(sc->arge_cdata.arge_tx_tag, 0, + &txd->tx_dmamap); + if (error != 0) { + device_printf(sc->arge_dev, + "failed to create Tx dmamap\n"); + goto fail; + } + } + /* Create DMA maps for Rx buffers. */ + if ((error = bus_dmamap_create(sc->arge_cdata.arge_rx_tag, 0, + &sc->arge_cdata.arge_rx_sparemap)) != 0) { + device_printf(sc->arge_dev, + "failed to create spare Rx dmamap\n"); + goto fail; + } + for (i = 0; i < ARGE_RX_RING_COUNT; i++) { + rxd = &sc->arge_cdata.arge_rxdesc[i]; + rxd->rx_m = NULL; + rxd->rx_dmamap = NULL; + error = bus_dmamap_create(sc->arge_cdata.arge_rx_tag, 0, + &rxd->rx_dmamap); + if (error != 0) { + device_printf(sc->arge_dev, + "failed to create Rx dmamap\n"); + goto fail; + } + } + +fail: + return (error); +} + +static void +arge_dma_free(struct arge_softc *sc) +{ + struct arge_txdesc *txd; + struct arge_rxdesc *rxd; + int i; + + /* Tx ring. */ + if (sc->arge_cdata.arge_tx_ring_tag) { + if (sc->arge_cdata.arge_tx_ring_map) + bus_dmamap_unload(sc->arge_cdata.arge_tx_ring_tag, + sc->arge_cdata.arge_tx_ring_map); + if (sc->arge_cdata.arge_tx_ring_map && + sc->arge_rdata.arge_tx_ring) + bus_dmamem_free(sc->arge_cdata.arge_tx_ring_tag, + sc->arge_rdata.arge_tx_ring, + sc->arge_cdata.arge_tx_ring_map); + sc->arge_rdata.arge_tx_ring = NULL; + sc->arge_cdata.arge_tx_ring_map = NULL; + bus_dma_tag_destroy(sc->arge_cdata.arge_tx_ring_tag); + sc->arge_cdata.arge_tx_ring_tag = NULL; + } + /* Rx ring. */ + if (sc->arge_cdata.arge_rx_ring_tag) { + if (sc->arge_cdata.arge_rx_ring_map) + bus_dmamap_unload(sc->arge_cdata.arge_rx_ring_tag, + sc->arge_cdata.arge_rx_ring_map); + if (sc->arge_cdata.arge_rx_ring_map && + sc->arge_rdata.arge_rx_ring) + bus_dmamem_free(sc->arge_cdata.arge_rx_ring_tag, + sc->arge_rdata.arge_rx_ring, + sc->arge_cdata.arge_rx_ring_map); + sc->arge_rdata.arge_rx_ring = NULL; + sc->arge_cdata.arge_rx_ring_map = NULL; + bus_dma_tag_destroy(sc->arge_cdata.arge_rx_ring_tag); + sc->arge_cdata.arge_rx_ring_tag = NULL; + } + /* Tx buffers. */ + if (sc->arge_cdata.arge_tx_tag) { + for (i = 0; i < ARGE_TX_RING_COUNT; i++) { + txd = &sc->arge_cdata.arge_txdesc[i]; + if (txd->tx_dmamap) { + bus_dmamap_destroy(sc->arge_cdata.arge_tx_tag, + txd->tx_dmamap); + txd->tx_dmamap = NULL; + } + } + bus_dma_tag_destroy(sc->arge_cdata.arge_tx_tag); + sc->arge_cdata.arge_tx_tag = NULL; + } + /* Rx buffers. */ + if (sc->arge_cdata.arge_rx_tag) { + for (i = 0; i < ARGE_RX_RING_COUNT; i++) { + rxd = &sc->arge_cdata.arge_rxdesc[i]; + if (rxd->rx_dmamap) { + bus_dmamap_destroy(sc->arge_cdata.arge_rx_tag, + rxd->rx_dmamap); + rxd->rx_dmamap = NULL; + } + } + if (sc->arge_cdata.arge_rx_sparemap) { + bus_dmamap_destroy(sc->arge_cdata.arge_rx_tag, + sc->arge_cdata.arge_rx_sparemap); + sc->arge_cdata.arge_rx_sparemap = 0; + } + bus_dma_tag_destroy(sc->arge_cdata.arge_rx_tag); + sc->arge_cdata.arge_rx_tag = NULL; + } + + if (sc->arge_cdata.arge_parent_tag) { + bus_dma_tag_destroy(sc->arge_cdata.arge_parent_tag); + sc->arge_cdata.arge_parent_tag = NULL; + } +} + +/* + * Initialize the transmit descriptors. + */ +static int +arge_tx_ring_init(struct arge_softc *sc) +{ + struct arge_ring_data *rd; + struct arge_txdesc *txd; + bus_addr_t addr; + int i; + + sc->arge_cdata.arge_tx_prod = 0; + sc->arge_cdata.arge_tx_cons = 0; + sc->arge_cdata.arge_tx_cnt = 0; + sc->arge_cdata.arge_tx_pkts = 0; + + rd = &sc->arge_rdata; + bzero(rd->arge_tx_ring, sizeof(rd->arge_tx_ring)); + for (i = 0; i < ARGE_TX_RING_COUNT; i++) { + if (i == ARGE_TX_RING_COUNT - 1) + addr = ARGE_TX_RING_ADDR(sc, 0); + else + addr = ARGE_TX_RING_ADDR(sc, i + 1); + rd->arge_tx_ring[i].packet_ctrl = ARGE_DESC_EMPTY; + rd->arge_tx_ring[i].next_desc = addr; + txd = &sc->arge_cdata.arge_txdesc[i]; + txd->tx_m = NULL; + } + + bus_dmamap_sync(sc->arge_cdata.arge_tx_ring_tag, + sc->arge_cdata.arge_tx_ring_map, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + + return (0); +} + +/* + * Initialize the RX descriptors and allocate mbufs for them. Note that + * we arrange the descriptors in a closed ring, so that the last descriptor + * points back to the first. + */ +static int +arge_rx_ring_init(struct arge_softc *sc) +{ + struct arge_ring_data *rd; + struct arge_rxdesc *rxd; + bus_addr_t addr; + int i; + + sc->arge_cdata.arge_rx_cons = 0; + + rd = &sc->arge_rdata; + bzero(rd->arge_rx_ring, sizeof(rd->arge_rx_ring)); + for (i = 0; i < ARGE_RX_RING_COUNT; i++) { + rxd = &sc->arge_cdata.arge_rxdesc[i]; + rxd->rx_m = NULL; + rxd->desc = &rd->arge_rx_ring[i]; + if (i == ARGE_RX_RING_COUNT - 1) + addr = ARGE_RX_RING_ADDR(sc, 0); + else + addr = ARGE_RX_RING_ADDR(sc, i + 1); + rd->arge_rx_ring[i].packet_ctrl = ARGE_DESC_EMPTY; + rd->arge_rx_ring[i].next_desc = addr; + if (arge_newbuf(sc, i) != 0) + return (ENOBUFS); + } + + bus_dmamap_sync(sc->arge_cdata.arge_rx_ring_tag, + sc->arge_cdata.arge_rx_ring_map, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + + return (0); +} + +/* + * Initialize an RX descriptor and attach an MBUF cluster. + */ +static int +arge_newbuf(struct arge_softc *sc, int idx) +{ + struct arge_desc *desc; + struct arge_rxdesc *rxd; + struct mbuf *m; + bus_dma_segment_t segs[1]; + bus_dmamap_t map; + int nsegs; + + m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); + if (m == NULL) + return (ENOBUFS); + m->m_len = m->m_pkthdr.len = MCLBYTES; + m_adj(m, sizeof(uint64_t)); + + if (bus_dmamap_load_mbuf_sg(sc->arge_cdata.arge_rx_tag, + sc->arge_cdata.arge_rx_sparemap, m, segs, &nsegs, 0) != 0) { + m_freem(m); + return (ENOBUFS); + } + KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs)); + + rxd = &sc->arge_cdata.arge_rxdesc[idx]; + if (rxd->rx_m != NULL) { + bus_dmamap_sync(sc->arge_cdata.arge_rx_tag, rxd->rx_dmamap, + BUS_DMASYNC_POSTREAD); + bus_dmamap_unload(sc->arge_cdata.arge_rx_tag, rxd->rx_dmamap); + } + map = rxd->rx_dmamap; + rxd->rx_dmamap = sc->arge_cdata.arge_rx_sparemap; + sc->arge_cdata.arge_rx_sparemap = map; + bus_dmamap_sync(sc->arge_cdata.arge_rx_tag, rxd->rx_dmamap, + BUS_DMASYNC_PREREAD); + rxd->rx_m = m; + desc = rxd->desc; + desc->packet_addr = segs[0].ds_addr; + desc->packet_ctrl = (desc->packet_ctrl & ~ARGE_DESC_SIZE_MASK) + | ARGE_DMASIZE(segs[0].ds_len); + + return (0); +} + +static __inline void +arge_fixup_rx(struct mbuf *m) +{ + int i; + uint16_t *src, *dst; + + src = mtod(m, uint16_t *); + dst = src - 1; + + for (i = 0; i < (m->m_len / sizeof(uint16_t) + 1); i++) + *dst++ = *src++; + + m->m_data -= ETHER_ALIGN; +} + + +static void +arge_tx_locked(struct arge_softc *sc) +{ + struct arge_txdesc *txd; + struct arge_desc *cur_tx; + struct ifnet *ifp; + uint32_t ctrl; + int cons, prod; + + ARGE_LOCK_ASSERT(sc); + + cons = sc->arge_cdata.arge_tx_cons; + prod = sc->arge_cdata.arge_tx_prod; + if (cons == prod) + return; + + bus_dmamap_sync(sc->arge_cdata.arge_tx_ring_tag, + sc->arge_cdata.arge_tx_ring_map, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + + ifp = sc->arge_ifp; + /* + * Go through our tx list and free mbufs for those + * frames that have been transmitted. + */ + for (; cons != prod; ARGE_INC(cons, ARGE_TX_RING_COUNT)) { + cur_tx = &sc->arge_rdata.arge_tx_ring[cons]; + ctrl = cur_tx->packet_ctrl; + /* Check if descriptor has "finished" flag */ + if ((ctrl & ARGE_DESC_EMPTY) == 0) + break; + + ARGE_WRITE(sc, AR71XX_DMA_TX_STATUS, DMA_TX_STATUS_PKT_SENT); + + sc->arge_cdata.arge_tx_cnt--; + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + + txd = &sc->arge_cdata.arge_txdesc[cons]; + + cur_tx->packet_ctrl = ARGE_DESC_EMPTY; + + ifp->if_opackets++; + + bus_dmamap_sync(sc->arge_cdata.arge_tx_tag, txd->tx_dmamap, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->arge_cdata.arge_tx_tag, txd->tx_dmamap); + + /* Free only if it's first descriptor in list */ + if (txd->tx_m) + m_freem(txd->tx_m); + txd->tx_m = NULL; + + /* reset descriptor */ + cur_tx->packet_ctrl = ARGE_DESC_EMPTY; + cur_tx->packet_addr = 0; + } + + sc->arge_cdata.arge_tx_cons = cons; + + bus_dmamap_sync(sc->arge_cdata.arge_tx_ring_tag, + sc->arge_cdata.arge_tx_ring_map, BUS_DMASYNC_PREWRITE); +} + + +static void +arge_rx_locked(struct arge_softc *sc) +{ + struct arge_rxdesc *rxd; + struct ifnet *ifp = sc->arge_ifp; + int cons, prog, packet_len; + struct arge_desc *cur_rx; + struct mbuf *m; + + ARGE_LOCK_ASSERT(sc); + + cons = sc->arge_cdata.arge_rx_cons; + + bus_dmamap_sync(sc->arge_cdata.arge_rx_ring_tag, + sc->arge_cdata.arge_rx_ring_map, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + + for (prog = 0; prog < ARGE_RX_RING_COUNT; + ARGE_INC(cons, ARGE_RX_RING_COUNT)) { + cur_rx = &sc->arge_rdata.arge_rx_ring[cons]; + rxd = &sc->arge_cdata.arge_rxdesc[cons]; + m = rxd->rx_m; + + if ((cur_rx->packet_ctrl & ARGE_DESC_EMPTY) != 0) + break; + + ARGE_WRITE(sc, AR71XX_DMA_RX_STATUS, DMA_RX_STATUS_PKT_RECVD); + + prog++; + + packet_len = ARGE_DMASIZE(cur_rx->packet_ctrl); + bus_dmamap_sync(sc->arge_cdata.arge_rx_tag, rxd->rx_dmamap, + BUS_DMASYNC_PREREAD); + m = rxd->rx_m; + + arge_fixup_rx(m); + m->m_pkthdr.rcvif = ifp; + /* Skip 4 bytes of CRC */ + m->m_pkthdr.len = m->m_len = packet_len - ETHER_CRC_LEN; + ifp->if_ipackets++; + + ARGE_UNLOCK(sc); + (*ifp->if_input)(ifp, m); + ARGE_LOCK(sc); + + /* Reinit descriptor */ + cur_rx->packet_ctrl = ARGE_DESC_EMPTY; + cur_rx->packet_addr = 0; + if (arge_newbuf(sc, cons) != 0) { + device_printf(sc->arge_dev, + "Failed to allocate buffer\n"); + break; + } + + bus_dmamap_sync(sc->arge_cdata.arge_rx_ring_tag, + sc->arge_cdata.arge_rx_ring_map, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + + } + + if (prog > 0) { + sc->arge_cdata.arge_rx_cons = cons; + + bus_dmamap_sync(sc->arge_cdata.arge_rx_ring_tag, + sc->arge_cdata.arge_rx_ring_map, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + } +} + +static void +arge_rx_intr(struct arge_softc *sc, uint32_t status) +{ + + ARGE_LOCK(sc); + /* interrupts are masked by filter */ + arge_rx_locked(sc); + + /* unmask interrupts */ + ARGE_SET_BITS(sc, + AR71XX_DMA_INTR, DMA_INTR_RX_OVERFLOW | DMA_INTR_RX_PKT_RCVD); + ARGE_UNLOCK(sc); +} + +static int +arge_intr_filter(void *arg) +{ + struct arge_softc *sc = arg; + uint32_t status, ints; + + status = ARGE_READ(sc, AR71XX_DMA_INTR_STATUS); + ints = ARGE_READ(sc, AR71XX_DMA_INTR); + +#if 0 + dprintf("int mask(filter) = %b\n", ints, + "\20\10RX_BUS_ERROR\7RX_OVERFLOW\5RX_PKT_RCVD" + "\4TX_BUS_ERROR\2TX_UNDERRUN\1TX_PKT_SENT"); + dprintf("status(filter) = %b\n", status, + "\20\10RX_BUS_ERROR\7RX_OVERFLOW\5RX_PKT_RCVD" + "\4TX_BUS_ERROR\2TX_UNDERRUN\1TX_PKT_SENT"); +#endif + + if (status & DMA_INTR_ALL) { + if (status & (DMA_INTR_RX_PKT_RCVD | DMA_INTR_RX_OVERFLOW)) + ARGE_CLEAR_BITS(sc, AR71XX_DMA_INTR, + DMA_INTR_RX_OVERFLOW | DMA_INTR_RX_PKT_RCVD); + + if (status & (DMA_INTR_TX_PKT_SENT | DMA_INTR_TX_UNDERRUN)) + ARGE_CLEAR_BITS(sc, AR71XX_DMA_INTR, + DMA_INTR_TX_UNDERRUN | DMA_INTR_TX_PKT_SENT); + + sc->arge_intr_status = status; + return (FILTER_SCHEDULE_THREAD); + } + + sc->arge_intr_status = 0; + return (FILTER_STRAY); +} + +static void +arge_intr(void *arg) +{ + struct arge_softc *sc = arg; + uint32_t status; + + status = sc->arge_intr_status; + +#if 0 + dprintf("int status(intr) = %b\n", status, + "\20\10\7RX_OVERFLOW\5RX_PKT_RCVD" + "\4TX_BUS_ERROR\2TX_UNDERRUN\1TX_PKT_SENT"); +#endif + + /* + * Is it our interrupt at all? + */ + if (status == 0) + return; + + if (status & DMA_INTR_RX_BUS_ERROR) { + ARGE_WRITE(sc, AR71XX_DMA_RX_STATUS, DMA_RX_STATUS_BUS_ERROR); + device_printf(sc->arge_dev, "RX bus error"); + return; + } + + if (status & DMA_INTR_TX_BUS_ERROR) { + ARGE_WRITE(sc, AR71XX_DMA_TX_STATUS, DMA_TX_STATUS_BUS_ERROR); + device_printf(sc->arge_dev, "TX bus error"); + return; + } + + if (status & (DMA_INTR_RX_PKT_RCVD | DMA_INTR_RX_OVERFLOW)) + arge_rx_intr(sc, status); + + if (status & (DMA_INTR_TX_PKT_SENT | DMA_INTR_TX_UNDERRUN)) + arge_tx_intr(sc, status); +} + +static void +arge_tx_intr(struct arge_softc *sc, uint32_t status) +{ + ARGE_LOCK(sc); + + /* Interrupts are masked by filter */ + arge_tx_locked(sc); + + ARGE_UNLOCK(sc); +} + +static void +arge_tick(void *xsc) +{ + struct arge_softc *sc = xsc; + struct mii_data *mii; + + ARGE_LOCK_ASSERT(sc); + + mii = device_get_softc(sc->arge_miibus); + mii_tick(mii); + callout_reset(&sc->arge_stat_callout, hz, arge_tick, sc); +} + +/* + * Create a copy of a single mbuf. It can have either internal or + * external data, it may have a packet header. External data is really + * copied, so the new buffer is writeable. + */ +static struct mbuf * +copy_mbuf(struct mbuf *m) +{ + struct mbuf *new; + + MGET(new, M_DONTWAIT, MT_DATA); + if (new == NULL) + return (NULL); + + if (m->m_flags & M_PKTHDR) { + M_MOVE_PKTHDR(new, m); + if (m->m_len > MHLEN) + MCLGET(new, M_WAIT); + } else { + if (m->m_len > MLEN) + MCLGET(new, M_WAIT); + } + + bcopy(m->m_data, new->m_data, m->m_len); + new->m_len = m->m_len; + new->m_flags &= ~M_RDONLY; + + return (new); +} + + + +static int +arge_fix_chain(struct mbuf **mp) +{ + struct mbuf *m = *mp, *prev = NULL, *next, *new; + u_int mlen = 0, fill = 0; + int first, off; + u_char *d, *cp; + + do { + next = m->m_next; + + if ((uintptr_t)mtod(m, void *) % 4 != 0 || + (m->m_len % 4 != 0 && next)) { + /* + * Needs fixing + */ + first = (m == *mp); + + d = mtod(m, u_char *); + if ((off = (uintptr_t)(void *)d % 4) != 0) { + if (M_WRITABLE(m)) { + bcopy(d, d - off, m->m_len); + m->m_data = (caddr_t)(d - off); + } else { + if ((new = copy_mbuf(m)) == NULL) { + goto fail; + } + if (prev) + prev->m_next = new; + new->m_next = next; + m_free(m); + m = new; + } + } + + if ((off = m->m_len % 4) != 0) { + if (!M_WRITABLE(m)) { + if ((new = copy_mbuf(m)) == NULL) { + goto fail; + } + if (prev) + prev->m_next = new; + new->m_next = next; + m_free(m); + m = new; + } + d = mtod(m, u_char *) + m->m_len; + off = 4 - off; + while (off) { + if (next == NULL) { + *d++ = 0; + fill++; + } else if (next->m_len == 0) { + next = m_free(next); + continue; + } else { + cp = mtod(next, u_char *); + *d++ = *cp++; + next->m_len--; + next->m_data = (caddr_t)cp; + } + off--; + m->m_len++; + } + } + + if (first) + *mp = m; + } + + mlen += m->m_len; + prev = m; + } while ((m = next) != NULL); + + return (mlen - fill); + + fail: + m_freem(*mp); + *mp = NULL; + return (0); +} diff --git a/sys/mips/atheros/if_argevar.h b/sys/mips/atheros/if_argevar.h new file mode 100644 index 0000000..1966e61 --- /dev/null +++ b/sys/mips/atheros/if_argevar.h @@ -0,0 +1,138 @@ +/*- + * Copyright (c) 2009, Oleksandr Tymoshenko + * 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. + */ + +#ifndef __IF_ARGEVAR_H__ +#define __IF_ARGEVAR_H__ + +#define ARGE_TX_RING_COUNT 128 +#define ARGE_RX_RING_COUNT 128 +#define ARGE_RX_DMA_SIZE ARGE_RX_RING_COUNT * sizeof(struct arge_desc) +#define ARGE_TX_DMA_SIZE ARGE_TX_RING_COUNT * sizeof(struct arge_desc) +#define ARGE_MAXFRAGS 8 +#define ARGE_RING_ALIGN sizeof(struct arge_desc) +#define ARGE_RX_ALIGN sizeof(uint32_t) +#define ARGE_MAXFRAGS 8 +#define ARGE_TX_RING_ADDR(sc, i) \ + ((sc)->arge_rdata.arge_tx_ring_paddr + sizeof(struct arge_desc) * (i)) +#define ARGE_RX_RING_ADDR(sc, i) \ + ((sc)->arge_rdata.arge_rx_ring_paddr + sizeof(struct arge_desc) * (i)) +#define ARGE_INC(x,y) (x) = (((x) + 1) % y) + + +#define ARGE_MII_TIMEOUT 1000 + +#define ARGE_LOCK(_sc) mtx_lock(&(_sc)->arge_mtx) +#define ARGE_UNLOCK(_sc) mtx_unlock(&(_sc)->arge_mtx) +#define ARGE_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->arge_mtx, MA_OWNED) + +/* + * register space access macros + */ +#define ARGE_WRITE(sc, reg, val) do { \ + bus_write_4(sc->arge_res, (reg), (val)); \ + } while (0) + +#define ARGE_READ(sc, reg) bus_read_4(sc->arge_res, (reg)) + +#define ARGE_SET_BITS(sc, reg, bits) \ + ARGE_WRITE(sc, reg, ARGE_READ(sc, (reg)) | (bits)) + +#define ARGE_CLEAR_BITS(sc, reg, bits) \ + ARGE_WRITE(sc, reg, ARGE_READ(sc, (reg)) & ~(bits)) + +#define ARGE_DESC_EMPTY (1 << 31) +#define ARGE_DESC_MORE (1 << 24) +#define ARGE_DESC_SIZE_MASK ((1 << 12) - 1) +#define ARGE_DMASIZE(len) ((len) & ARGE_DESC_SIZE_MASK) +struct arge_desc { + uint32_t packet_addr; + uint32_t packet_ctrl; + uint32_t next_desc; + uint32_t padding; +}; + +struct arge_txdesc { + struct mbuf *tx_m; + bus_dmamap_t tx_dmamap; +}; + +struct arge_rxdesc { + struct mbuf *rx_m; + bus_dmamap_t rx_dmamap; + struct arge_desc *desc; +}; + +struct arge_chain_data { + bus_dma_tag_t arge_parent_tag; + bus_dma_tag_t arge_tx_tag; + struct arge_txdesc arge_txdesc[ARGE_TX_RING_COUNT]; + bus_dma_tag_t arge_rx_tag; + struct arge_rxdesc arge_rxdesc[ARGE_RX_RING_COUNT]; + bus_dma_tag_t arge_tx_ring_tag; + bus_dma_tag_t arge_rx_ring_tag; + bus_dmamap_t arge_tx_ring_map; + bus_dmamap_t arge_rx_ring_map; + bus_dmamap_t arge_rx_sparemap; + int arge_tx_pkts; + int arge_tx_prod; + int arge_tx_cons; + int arge_tx_cnt; + int arge_rx_cons; +}; + +struct arge_ring_data { + struct arge_desc *arge_rx_ring; + struct arge_desc *arge_tx_ring; + bus_addr_t arge_rx_ring_paddr; + bus_addr_t arge_tx_ring_paddr; +}; + +struct arge_softc { + struct ifnet *arge_ifp; /* interface info */ + device_t arge_dev; + struct resource *arge_res; + int arge_rid; + struct resource *arge_irq; + void *arge_intrhand; + device_t arge_miibus; + bus_dma_tag_t arge_parent_tag; + bus_dma_tag_t arge_tag; + struct mtx arge_mtx; + struct callout arge_stat_callout; + struct task arge_link_task; + struct arge_chain_data arge_cdata; + struct arge_ring_data arge_rdata; + int arge_link_status; + int arge_detach; + uint32_t arge_intr_status; + int arge_mac_unit; + int arge_phy_num; + uint32_t arge_ddr_flush_reg; + uint32_t arge_pll_reg; +}; + +#endif /* __IF_ARGEVAR_H__ */ diff --git a/sys/mips/atheros/uart_bus_ar71xx.c b/sys/mips/atheros/uart_bus_ar71xx.c new file mode 100644 index 0000000..1cb611c --- /dev/null +++ b/sys/mips/atheros/uart_bus_ar71xx.c @@ -0,0 +1,79 @@ +/*- + * Copyright (c) 2009, Oleksandr Tymoshenko <gonzo@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + */ +#include "opt_uart.h" + +#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 <dev/uart/uart.h> +#include <dev/uart/uart_cpu.h> +#include <dev/uart/uart_bus.h> + +#include <mips/atheros/ar71xxreg.h> + +#include "uart_if.h" + +static int uart_ar71xx_probe(device_t dev); +extern struct uart_class uart_ar71xx_uart_class; + +static device_method_t uart_ar71xx_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, uart_ar71xx_probe), + DEVMETHOD(device_attach, uart_bus_attach), + DEVMETHOD(device_detach, uart_bus_detach), + { 0, 0 } +}; + +static driver_t uart_ar71xx_driver = { + uart_driver_name, + uart_ar71xx_methods, + sizeof(struct uart_softc), +}; + +extern SLIST_HEAD(uart_devinfo_list, uart_devinfo) uart_sysdevs; + +static int +uart_ar71xx_probe(device_t dev) +{ + struct uart_softc *sc; + + sc = device_get_softc(dev); + sc->sc_sysdev = SLIST_FIRST(&uart_sysdevs); + sc->sc_class = &uart_ns8250_class; + bcopy(&sc->sc_sysdev->bas, &sc->sc_bas, sizeof(sc->sc_bas)); + + return (uart_bus_probe(dev, 2, 85000000, 0, 0)); +} + +DRIVER_MODULE(uart, apb, uart_ar71xx_driver, uart_devclass, 0, 0); diff --git a/sys/mips/atheros/uart_cpu_ar71xx.c b/sys/mips/atheros/uart_cpu_ar71xx.c new file mode 100644 index 0000000..4d6b7e7 --- /dev/null +++ b/sys/mips/atheros/uart_cpu_ar71xx.c @@ -0,0 +1,78 @@ +/*- + * Copyright (c) 2009 Oleksandr Tymoshenko + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ +#include "opt_uart.h" + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> + +#include <machine/bus.h> + +#include <dev/uart/uart.h> +#include <dev/uart/uart_cpu.h> + +#include <mips/atheros/ar71xxreg.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) +{ + di->ops = uart_getops(&uart_ns8250_class); + di->bas.chan = 0; + di->bas.bst = MIPS_BUS_SPACE_MEM; + di->bas.regshft = 2; + /* TODO: calculate proper AHB freq using PLL registers */ + di->bas.rclk = 85000000; + di->baudrate = 115200; + di->databits = 8; + di->stopbits = 1; + di->parity = UART_PARITY_NONE; + + /* TODO: check if uart_bus_space_io mandatory to set */ + uart_bus_space_io = MIPS_BUS_SPACE_IO; + uart_bus_space_mem = MIPS_BUS_SPACE_MEM; + /* + * FIXME: + * 3 is to compensate big endian, uart operates + * with bus_space_read_1/bus_space_write_1 and hence gets + * highest byte instead of lowest one. Actual fix will involve + * MIPS bus_space fixing. + */ + di->bas.bsh = MIPS_PHYS_TO_KSEG1(AR71XX_UART_ADDR) + 3; + return (0); +} diff --git a/sys/mips/conf/ADM5120 b/sys/mips/conf/ADM5120 index a03d102..7a47abe 100644 --- a/sys/mips/conf/ADM5120 +++ b/sys/mips/conf/ADM5120 @@ -25,7 +25,6 @@ makeoptions MIPS_LITTLE_ENDIAN=defined # Don't build any modules yet. makeoptions MODULES_OVERRIDE="" -options KERNVIRTADDR=0x80100000 include "../adm5120/std.adm5120" hints "ADM5120.hints" #Default places to look for devices. diff --git a/sys/mips/conf/ALCHEMY b/sys/mips/conf/ALCHEMY new file mode 100644 index 0000000..c1fb59a --- /dev/null +++ b/sys/mips/conf/ALCHEMY @@ -0,0 +1,66 @@ +# ALCHEMY -- Generic kernel for Alchemy Au1xxx CPUs. +# +# For more information on this file, please read the handbook section on +# Kernel Configuration Files: +# +# http://www.FreeBSD.org/doc/en_US.ISO8859-1/books/handbook/kernelconfig-config.html +# +# The handbook is also available locally in /usr/share/doc/handbook +# if you've installed the doc distribution, otherwise always see the +# FreeBSD World Wide Web server (http://www.FreeBSD.org/) for the +# latest information. +# +# An exhaustive list of options and more detailed explanations of the +# device lines is also present in the ../../conf/NOTES and NOTES files. +# If you are in doubt as to the purpose or necessity of a line, check first +# in NOTES. +# +# $FreeBSD$ + +ident ALCHEMY + +makeoptions ARCH_FLAGS=-march=mips32 +makeoptions MIPS_LITTLE_ENDIAN=defined + +# Don't build any modules yet. +makeoptions MODULES_OVERRIDE="" + +include "../alchemy/std.alchemy" + +hints "ALCHEMY.hints" #Default places to look for devices. + +makeoptions DEBUG=-g #Build kernel with gdb(1) debug symbols + +options DDB +options KDB + +options SCHED_4BSD #4BSD scheduler +options INET #InterNETworking +options NFSCLIENT #Network Filesystem Client +options NFS_ROOT #NFS usable as /, requires NFSCLIENT +options PSEUDOFS #Pseudo-filesystem framework +# options _KPOSIX_PRIORITY_SCHEDULING #Posix P1003_1B real-time extensions + +options BOOTP +options BOOTP_NFSROOT +options BOOTP_NFSV3 +options BOOTP_WIRED_TO=admsw0 +options BOOTP_COMPAT + +# options FFS #Berkeley Fast Filesystem +# options SOFTUPDATES #Enable FFS soft updates support +# options UFS_ACL #Support for access control lists +# options UFS_DIRHASH #Improve performance on big directories +options ROOTDEVNAME=\"nfs:10.0.0.1:/mnt/bsd\" + + +# Debugging for use in -current +options INVARIANTS #Enable calls of extra sanity checking +options INVARIANT_SUPPORT #Extra sanity checks of internal structures, required by INVARIANTS +#options WITNESS #Enable checks to detect deadlocks and cycles +#options WITNESS_SKIPSPIN #Don't run witness on spinlocks for speed + +device loop +device ether +device uart +# device md diff --git a/sys/mips/conf/AR71XX b/sys/mips/conf/AR71XX new file mode 100644 index 0000000..936d0f1 --- /dev/null +++ b/sys/mips/conf/AR71XX @@ -0,0 +1,37 @@ +# +# $FreeBSD$ +# + +ident AR71XX +cpu CPU_MIPS4KC +options CPU_NOFPU +options ISA_MIPS32 +makeoptions TARGET_BIG_ENDIAN +makeoptions KERNLOADADDR=0x80050000 + +files "../atheros/files.ar71xx" +hints "AR71XX.hints" + +makeoptions DEBUG=-g #Build kernel with gdb(1) debug symbols +makeoptions MODULES_OVERRIDE="" + +options DDB +options KDB + +options SCHED_4BSD #4BSD scheduler +options INET #InterNETworking +options NFSCLIENT #Network Filesystem Client +options NFS_ROOT #NFS usable as /, requires NFSCLIENT +options PSEUDOFS #Pseudo-filesystem framework +options _KPOSIX_PRIORITY_SCHEDULING #Posix P1003_1B real-time extensions + +# Debugging for use in -current +options INVARIANTS +options INVARIANT_SUPPORT + +device pci +device uart + +device loop +device ether +device md diff --git a/sys/mips/conf/AR71XX.hints b/sys/mips/conf/AR71XX.hints new file mode 100644 index 0000000..58fbdde --- /dev/null +++ b/sys/mips/conf/AR71XX.hints @@ -0,0 +1,25 @@ +# $FreeBSD$ +hint.apb.0.at="nexus0" +hint.apb.0.maddr=0x18000000 +hint.apb.0.msize=0x01000000 +hint.apb.0.irq=4 + +# uart0 +hint.uart.0.at="apb0" +# see atheros/uart_cpu_ar71xx.c why +3 +hint.uart.0.maddr=0x18020003 +hint.uart.0.msize=0x18 +hint.uart.0.irq=3 + +# pci +hint.pcib.0.at="nexus0" +hint.pcib.0.irq=0 + +hint.arge.0.at="nexus0" +hint.arge.0.maddr=0x19000000 +hint.arge.0.msize=0x1000 +hint.arge.0.irq=2 +# hint.arge.1.at="nexus0" +# hint.arge.1.maddr=0x1A000000 +# hint.arge.1.msize=0x1000 +# hint.arge.1.irq=3 diff --git a/sys/mips/conf/MALTA b/sys/mips/conf/MALTA index 9771c55..022cc52 100644 --- a/sys/mips/conf/MALTA +++ b/sys/mips/conf/MALTA @@ -27,7 +27,6 @@ options YAMON # Don't build any modules yet. makeoptions MODULES_OVERRIDE="" -options KERNVIRTADDR=0x80100000 options TICK_USE_YAMON_FREQ=defined #options TICK_USE_MALTA_RTC=defined diff --git a/sys/mips/conf/QEMU b/sys/mips/conf/QEMU index 0a2b122..bc7d649 100644 --- a/sys/mips/conf/QEMU +++ b/sys/mips/conf/QEMU @@ -27,7 +27,6 @@ makeoptions ARCH_FLAGS=-march=mips32 # Don't build any modules yet. makeoptions MODULES_OVERRIDE="" -options KERNVIRTADDR=0x80100000 include "../adm5120/std.adm5120" #hints "GENERIC.hints" #Default places to look for devices. diff --git a/sys/mips/conf/SENTRY5 b/sys/mips/conf/SENTRY5 index 7611e44..c3918e2 100644 --- a/sys/mips/conf/SENTRY5 +++ b/sys/mips/conf/SENTRY5 @@ -41,13 +41,6 @@ options CFE options CFE_CONSOLE options ALT_BREAK_TO_DEBUGGER -# cfe loader expects kernel at 0x80001000 for mips32 w/o backwards -# offsets in the linked elf image (see ldscript hack) -# XXX can we conditionalize the linker stuff on options CFE? -options KERNVIRTADDR=0x80001000 - -makeoptions LDSCRIPT_NAME= ldscript.mips.cfe - #makeoptions ARCH_FLAGS=-march=mips32 makeoptions MIPS_LITTLE_ENDIAN=defined makeoptions DEBUG=-g #Build kernel with gdb(1) debug symbols @@ -73,8 +66,8 @@ options INVARIANT_SUPPORT device siba # Sonics SiliconBackplane device pci # siba_pcib -device bfe # XXX will build both pci and siba -device miibus # attachments +# device bfe # XXX will build both pci and siba +# device miibus # attachments # pci devices # notyet: diff --git a/sys/mips/include/bus.h b/sys/mips/include/bus.h index 92557d7..42ac1df 100644 --- a/sys/mips/include/bus.h +++ b/sys/mips/include/bus.h @@ -101,9 +101,8 @@ * Map a region of device bus space into CPU virtual address space. */ -static __inline int bus_space_map(bus_space_tag_t t, bus_addr_t addr, - bus_size_t size, int flags, - bus_space_handle_t *bshp); +__inline int bus_space_map(bus_space_tag_t t, bus_addr_t addr, + bus_size_t size, int flags, bus_space_handle_t *bshp); static __inline int bus_space_map(bus_space_tag_t t __unused, bus_addr_t addr, diff --git a/sys/mips/mips/elf_machdep.c b/sys/mips/mips/elf_machdep.c index 19cb1a1..163d0ee 100644 --- a/sys/mips/mips/elf_machdep.c +++ b/sys/mips/mips/elf_machdep.c @@ -86,8 +86,7 @@ static Elf32_Brandinfo freebsd_brand_info = { .interp_path = "/libexec/ld-elf.so.1", .sysvec = &elf32_freebsd_sysvec, .interp_newpath = NULL, - .brand_note = &elf32_freebsd_brandnote, - .flags = BI_BRAND_NOTE + .flags = 0 }; SYSINIT(elf32, SI_SUB_EXEC, SI_ORDER_ANY, diff --git a/sys/mips/mips/elf_trampoline.c b/sys/mips/mips/elf_trampoline.c new file mode 100644 index 0000000..0f2ccc9 --- /dev/null +++ b/sys/mips/mips/elf_trampoline.c @@ -0,0 +1,133 @@ +/*- + * Copyright (c) 2005 Olivier Houchard. 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 <machine/asm.h> +#include <sys/param.h> +#include <sys/elf32.h> +#include <sys/inflate.h> +#include <machine/elf.h> +#include <machine/cpufunc.h> +#include <machine/stdarg.h> + +/* + * Since we are compiled outside of the normal kernel build process, we + * need to include opt_global.h manually. + */ +#include "opt_global.h" +#include "opt_kernname.h" + +extern char kernel_start[]; +extern char kernel_end[]; + +static __inline void * +memcpy(void *dst, const void *src, int len) +{ + const char *s = src; + char *d = dst; + + while (len) { + if (0 && len >= 4 && !((vm_offset_t)d & 3) && + !((vm_offset_t)s & 3)) { + *(uint32_t *)d = *(uint32_t *)s; + s += 4; + d += 4; + len -= 4; + } else { + *d++ = *s++; + len--; + } + } + return (dst); +} + +static __inline void +bzero(void *addr, int count) +{ + char *tmp = (char *)addr; + + while (count > 0) { + if (count >= 4 && !((vm_offset_t)tmp & 3)) { + *(uint32_t *)tmp = 0; + tmp += 4; + count -= 4; + } else { + *tmp = 0; + tmp++; + count--; + } + } +} + +/* + * Relocate PT_LOAD segements of kernel ELF image to their respective + * virtual addresses and return entry point + */ +void * +load_kernel(void * kstart) +{ + Elf32_Ehdr *eh; + Elf32_Phdr phdr[64] /* XXX */; + int i; + void *entry_point; + + eh = (Elf32_Ehdr *)kstart; + entry_point = (void*)eh->e_entry; + memcpy(phdr, (void *)(kstart + eh->e_phoff ), + eh->e_phnum * sizeof(phdr[0])); + + for (i = 0; i < eh->e_phnum; i++) { + volatile char c; + + if (phdr[i].p_type != PT_LOAD) + continue; + + memcpy((void *)(phdr[i].p_vaddr), + (void*)(kstart + phdr[i].p_offset), phdr[i].p_filesz); + /* Clean space from oversized segments, eg: bss. */ + if (phdr[i].p_filesz < phdr[i].p_memsz) + bzero((void *)(phdr[i].p_vaddr + phdr[i].p_filesz), + phdr[i].p_memsz - phdr[i].p_filesz); + } + + return entry_point; +} + +void +_startC(register_t a0, register_t a1, register_t a2, register_t a3) +{ + unsigned int * code; + int i; + void (*entry_point)(register_t, register_t, register_t, register_t); + + /* + * Relocate segment to the predefined memory location + * Most likely it will be KSEG0/KSEG1 address + */ + entry_point = load_kernel(kernel_start); + + /* Pass saved registers to original _start */ + entry_point(a0, a1, a2, a3); +} diff --git a/sys/mips/mips/inckern.S b/sys/mips/mips/inckern.S new file mode 100644 index 0000000..8610196 --- /dev/null +++ b/sys/mips/mips/inckern.S @@ -0,0 +1,34 @@ +/*- + * Copyright (c) 2005 Olivier Houchard. 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 "opt_kernname.h" + +#include <machine/asm.h> +__FBSDID("$FreeBSD$") +.section ".real_kernel","aw" +.globl kernel_start; +kernel_start: +.incbin KERNNAME +.globl kernel_end; +kernel_end: diff --git a/sys/mips/mips/nexus.c b/sys/mips/mips/nexus.c index 64cba66..e96eeab 100644 --- a/sys/mips/mips/nexus.c +++ b/sys/mips/mips/nexus.c @@ -249,6 +249,8 @@ nexus_hinted_child(device_t bus, const char *dname, int dunit) long maddr; int msize; int result; + int irq; + int mem_hints_count; child = BUS_ADD_CHILD(bus, 0, dname, dunit); @@ -256,17 +258,34 @@ nexus_hinted_child(device_t bus, const char *dname, int dunit) * Set hard-wired resources for hinted child using * specific RIDs. */ - resource_long_value(dname, dunit, "maddr", &maddr); - resource_int_value(dname, dunit, "msize", &msize); - - dprintf("%s: discovered hinted child %s at maddr %p(%d)\n", - __func__, device_get_nameunit(child), - (void *)(intptr_t)maddr, msize); + mem_hints_count = 0; + if (resource_long_value(dname, dunit, "maddr", &maddr) == 0) + mem_hints_count++; + if (resource_int_value(dname, dunit, "msize", &msize) == 0) + mem_hints_count++; + + /* check if all info for mem resource has been provided */ + if ((mem_hints_count > 0) && (mem_hints_count < 2)) { + printf("Either maddr or msize hint is missing for %s%d\n", + dname, dunit); + } else if (mem_hints_count) { + dprintf("%s: discovered hinted child %s at maddr %p(%d)\n", + __func__, device_get_nameunit(child), + (void *)(intptr_t)maddr, msize); + + result = bus_set_resource(child, SYS_RES_MEMORY, MIPS_MEM_RID, + maddr, msize); + if (result != 0) { + device_printf(bus, + "warning: bus_set_resource() failed\n"); + } + } - result = bus_set_resource(child, SYS_RES_MEMORY, MIPS_MEM_RID, - maddr, msize); - if (result != 0) { - device_printf(bus, "warning: bus_set_resource() failed\n"); + if (resource_int_value(dname, dunit, "irq", &irq) == 0) { + result = bus_set_resource(child, SYS_RES_IRQ, 0, irq, 1); + if (result != 0) + device_printf(bus, + "warning: bus_set_resource() failed\n"); } } diff --git a/sys/mips/sentry5/files.sentry5 b/sys/mips/sentry5/files.sentry5 index 61038e0..7992509 100644 --- a/sys/mips/sentry5/files.sentry5 +++ b/sys/mips/sentry5/files.sentry5 @@ -4,11 +4,4 @@ # for USB 1.1 OHCI, Ethernet and IPSEC cores # which are believed to be devices we have drivers for # which just need to be tweaked for attachment to an SSB system bus. - mips/sentry5/s5_machdep.c standard -dev/siba/siba.c optional siba -dev/siba/siba_pcib.c optional siba pci -mips/sentry5/siba_cc.c optional siba - -# notyet -#mips/sentry5/siba_mips.c optional siba |