diff options
Diffstat (limited to 'sys/mips/idt')
-rw-r--r-- | sys/mips/idt/files.idt | 8 | ||||
-rw-r--r-- | sys/mips/idt/idt_machdep.c | 188 | ||||
-rw-r--r-- | sys/mips/idt/idtpci.c | 565 | ||||
-rw-r--r-- | sys/mips/idt/idtreg.h | 153 | ||||
-rw-r--r-- | sys/mips/idt/if_kr.c | 1615 | ||||
-rw-r--r-- | sys/mips/idt/if_krreg.h | 284 | ||||
-rw-r--r-- | sys/mips/idt/obio.c | 514 | ||||
-rw-r--r-- | sys/mips/idt/obiovar.h | 67 | ||||
-rw-r--r-- | sys/mips/idt/std.idt | 5 | ||||
-rw-r--r-- | sys/mips/idt/uart_bus_rc32434.c | 100 | ||||
-rw-r--r-- | sys/mips/idt/uart_cpu_rc32434.c | 85 |
11 files changed, 3584 insertions, 0 deletions
diff --git a/sys/mips/idt/files.idt b/sys/mips/idt/files.idt new file mode 100644 index 0000000..370ccf9 --- /dev/null +++ b/sys/mips/idt/files.idt @@ -0,0 +1,8 @@ +# $FreeBSD$ + +mips/idt/idt_machdep.c standard +mips/idt/idtpci.c optional pci +mips/idt/if_kr.c optional kr +mips/idt/obio.c standard +mips/idt/uart_cpu_rc32434.c optional uart +mips/idt/uart_bus_rc32434.c optional uart diff --git a/sys/mips/idt/idt_machdep.c b/sys/mips/idt/idt_machdep.c new file mode 100644 index 0000000..040c3f9 --- /dev/null +++ b/sys/mips/idt/idt_machdep.c @@ -0,0 +1,188 @@ +/*- + * 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. + * + * $Id: $ + * + */ + +#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; + +void +platform_halt(void) +{ + +} + + +void +platform_identify(void) +{ + +} + +void +platform_reset(void) +{ + volatile unsigned int * p = (void *)0xb8008000; + /* + * TODO: we should take care of TLB stuff here. Otherwise + * board does not boots properly next time + */ + + /* Write 0x8000_0001 to the Reset register */ + *p = 0x80000001; + + __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, __register_t a1, + __register_t a2 __unused, __register_t a3 __unused) +{ + uint64_t platform_counter_freq; + vm_offset_t kernend; + int argc = a0; + char **argv = (char **)a1; + int i, mem; + + + /* clear the BSS and SBSS segments */ + kernend = round_page((vm_offset_t)&end); + memset(&edata, 0, kernend - (vm_offset_t)(&edata)); + + /* + * Looking for mem=XXM argument + */ + mem = 0; /* Just something to start with */ + for (i=0; i < argc; i++) { + if (strncmp(argv[i], "mem=", 4) == 0) { + mem = strtol(argv[i] + 4, NULL, 0); + break; + } + } + + bootverbose = 1; + if (mem > 0) + realmem = btoc(mem << 20); + else + realmem = btoc(32 << 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; + + /* + * 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: parse argc,argv */ + platform_counter_freq = 330000000UL; + mips_timer_init_params(platform_counter_freq, 1); + cninit(); + /* Panic here, after cninit */ + if (mem == 0) + panic("No mem=XX parameter in arguments"); + + printf("cmd line: "); + for (i=0; i < argc; i++) + printf("%s ", argv[i]); + printf("\n"); + + init_param2(physmem); + mips_cpu_init(); + pmap_bootstrap(); + mips_proc0_init(); + mutex_init(); +#ifdef DDB + kdb_init(); +#endif +} diff --git a/sys/mips/idt/idtpci.c b/sys/mips/idt/idtpci.c new file mode 100644 index 0000000..7e4414f --- /dev/null +++ b/sys/mips/idt/idtpci.c @@ -0,0 +1,565 @@ +/* $NetBSD: idtpci.c,v 1.1 2007/03/20 08:52:02 dyoung Exp $ */ + +/*- + * Copyright (c) 2007 David Young. + * Copyright (c) 2007 Oleskandr 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. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ +/*- + * Copyright (c) 2006 Itronix Inc. + * All rights reserved. + * + * Written by Garrett D'Amore for Itronix 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. The name of Itronix Inc. may not be used to endorse + * or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY ITRONIX 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 ITRONIX 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/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/mips32/idt/idtreg.h> + +#ifdef IDTPCI_DEBUG +int idtpci_debug = 1; +#define IDTPCI_DPRINTF(__fmt, ...) \ +do { \ + if (idtpci_debug) \ + printf((__fmt), __VA_ARGS__); \ +} while (/*CONSTCOND*/0) +#else /* !IDTPCI_DEBUG */ +#define IDTPCI_DPRINTF(__fmt, ...) do { } while (/*CONSTCOND*/0) +#endif /* IDTPCI_DEBUG */ + +#define IDTPCI_TAG_BUS_MASK 0x007f0000 +#define IDTPCI_TAG_DEVICE_MASK 0x00007800 +#define IDTPCI_TAG_FUNCTION_MASK 0x00000300 +#define IDTPCI_TAG_REGISTER_MASK 0x0000007c + +#define IDTPCI_MAX_DEVICE + +#define REG_READ(o) *((volatile uint32_t *)MIPS_PHYS_TO_KSEG1(IDT_BASE_PCI + (o))) +#define REG_WRITE(o,v) (REG_READ(o)) = (v) + +unsigned int korina_fixup[24] = { + 0x00000157, 0x00000000, 0x00003c04, 0x00000008, 0x18800001, 0x18000001, + 0x48000008, 0x00000000, 0x00000000, 0x00000000, 0x011d0214, 0x00000000, + 0x00000000, 0x00000000, 0x38080101, 0x00008080, 0x00000d6e, 0x00000000, + 0x00000051, 0x00000000, 0x00000055, 0x18000000, 0x00000000, 0x00000000 +}; + +struct idtpci_softc { + device_t sc_dev; + + int sc_busno; + struct rman sc_mem_rman[2]; + struct rman sc_io_rman[2]; + struct rman sc_irq_rman; + uint32_t sc_mem; + uint32_t sc_io; +}; + +static uint32_t +idtpci_make_addr(int bus, int slot, int func, int reg) +{ + + return 0x80000000 | (bus << 16) | (slot << 11) | (func << 8) | reg; +} + +static int +idtpci_probe(device_t dev) +{ + + return (0); +} + +static int +idtpci_attach(device_t dev) +{ + int busno = 0; + struct idtpci_softc *sc = device_get_softc(dev); + unsigned int pci_data, force_endianess = 0; + int i; + bus_addr_t addr; + + sc->sc_dev = dev; + sc->sc_busno = busno; + + sc->sc_io = 0; + sc->sc_mem = 0; + + /* TODO: Check for host mode */ + + /* Enabled PCI, IG mode, EAP mode */ + REG_WRITE(IDT_PCI_CNTL, IDT_PCI_CNTL_IGM | IDT_PCI_CNTL_EAP | + IDT_PCI_CNTL_EN); + /* Wait while "Reset in progress bit" set */ + while(1) { + pci_data = REG_READ(IDT_PCI_STATUS); + if((pci_data & IDT_PCI_STATUS_RIP) == 0) + break; + } + + /* Reset status register */ + REG_WRITE(IDT_PCI_STATUS, 0); + /* Mask interrupts related to status register */ + REG_WRITE(IDT_PCI_STATUS_MASK, 0xffffffff); + + /* Disable PCI decoupled access */ + REG_WRITE(IDT_PCI_DAC, 0); + /* Zero status and mask DA interrupts */ + REG_WRITE(IDT_PCI_DAS, 0); + REG_WRITE(IDT_PCI_DASM, 0x7f); + + /* Init PCI messaging unit */ + /* Disable messaging interrupts */ + REG_WRITE(IDT_PCI_IIC, 0); + REG_WRITE(IDT_PCI_IIM, 0xffffffff); + REG_WRITE(IDT_PCI_OIC, 0); + REG_WRITE(IDT_PCI_OIM, 0); + +#ifdef __MIPSEB__ + force_endianess = IDT_PCI_LBA_FE; +#endif + + /* LBA0 -- memory window */ + REG_WRITE(IDT_PCI_LBA0, IDT_PCIMEM0_BASE); + REG_WRITE(IDT_PCI_LBA0_MAP, IDT_PCIMEM0_BASE); + REG_WRITE(IDT_PCI_LBA0_CNTL, IDT_PCI_LBA_SIZE_16MB | force_endianess); + pci_data = REG_READ(IDT_PCI_LBA0_CNTL); + + /* LBA1 -- memory window */ + REG_WRITE(IDT_PCI_LBA1, IDT_PCIMEM1_BASE); + REG_WRITE(IDT_PCI_LBA1_MAP, IDT_PCIMEM1_BASE); + REG_WRITE(IDT_PCI_LBA1_CNTL, IDT_PCI_LBA_SIZE_256MB | force_endianess); + pci_data = REG_READ(IDT_PCI_LBA1_CNTL); + + /* LBA2 -- IO window */ + REG_WRITE(IDT_PCI_LBA2, IDT_PCIMEM2_BASE); + REG_WRITE(IDT_PCI_LBA2_MAP, IDT_PCIMEM2_BASE); + REG_WRITE(IDT_PCI_LBA2_CNTL, IDT_PCI_LBA_SIZE_4MB | IDT_PCI_LBA_MSI | + force_endianess); + pci_data = REG_READ(IDT_PCI_LBA2_CNTL); + + /* LBA3 -- IO window */ + REG_WRITE(IDT_PCI_LBA3, IDT_PCIMEM3_BASE); + REG_WRITE(IDT_PCI_LBA3_MAP, IDT_PCIMEM3_BASE); + REG_WRITE(IDT_PCI_LBA3_CNTL, IDT_PCI_LBA_SIZE_1MB | IDT_PCI_LBA_MSI | + force_endianess); + pci_data = REG_READ(IDT_PCI_LBA3_CNTL); + + + pci_data = REG_READ(IDT_PCI_CNTL) & ~IDT_PCI_CNTL_TNR; + REG_WRITE(IDT_PCI_CNTL, pci_data); + pci_data = REG_READ(IDT_PCI_CNTL); + + /* Rewrite Target Control register with default values */ + REG_WRITE(IDT_PCI_TC, (IDT_PCI_TC_DTIMER << 8) | IDT_PCI_TC_RTIMER); + + /* Perform Korina fixup */ + addr = idtpci_make_addr(0, 0, 0, 4); + for (i = 0; i < 24; i++) { + + REG_WRITE(IDT_PCI_CFG_ADDR, addr); + REG_WRITE(IDT_PCI_CFG_DATA, korina_fixup[i]); + __asm__ volatile ("sync"); + + REG_WRITE(IDT_PCI_CFG_ADDR, 0); + REG_WRITE(IDT_PCI_CFG_DATA, 0); + addr += 4; + } + + /* Use KSEG1 to access IO ports for it is uncached */ + sc->sc_io = 0; + sc->sc_io_rman[0].rm_type = RMAN_ARRAY; + sc->sc_io_rman[0].rm_descr = "IDTPCI I/O Ports window 1"; + if (rman_init(&sc->sc_io_rman[0]) != 0 || + rman_manage_region(&sc->sc_io_rman[0], + IDT_PCIMEM2_BASE, IDT_PCIMEM2_BASE + IDT_PCIMEM2_SIZE - 1) != 0) { + panic("idtpci_attach: failed to set up I/O rman"); + } + + sc->sc_io_rman[1].rm_type = RMAN_ARRAY; + sc->sc_io_rman[1].rm_descr = "IDTPCI I/O Ports window 2"; + if (rman_init(&sc->sc_io_rman[1]) != 0 || + rman_manage_region(&sc->sc_io_rman[1], + IDT_PCIMEM3_BASE, IDT_PCIMEM3_BASE + IDT_PCIMEM3_SIZE - 1) != 0) { + panic("idtpci_attach: failed to set up I/O rman"); + } + + /* Use KSEG1 to access PCI memory for it is uncached */ + sc->sc_mem = 0; + sc->sc_mem_rman[0].rm_type = RMAN_ARRAY; + sc->sc_mem_rman[0].rm_descr = "IDTPCI PCI Memory window 1"; + if (rman_init(&sc->sc_mem_rman[0]) != 0 || + rman_manage_region(&sc->sc_mem_rman[0], + IDT_PCIMEM0_BASE, IDT_PCIMEM0_BASE + IDT_PCIMEM0_SIZE) != 0) { + panic("idtpci_attach: failed to set up memory rman"); + } + + sc->sc_mem_rman[1].rm_type = RMAN_ARRAY; + sc->sc_mem_rman[1].rm_descr = "IDTPCI PCI Memory window 2"; + if (rman_init(&sc->sc_mem_rman[1]) != 0 || + rman_manage_region(&sc->sc_mem_rman[1], + IDT_PCIMEM1_BASE, IDT_PCIMEM1_BASE + IDT_PCIMEM1_SIZE) != 0) { + panic("idtpci_attach: failed to set up memory rman"); + } + + sc->sc_irq_rman.rm_type = RMAN_ARRAY; + sc->sc_irq_rman.rm_descr = "IDTPCI PCI IRQs"; + if (rman_init(&sc->sc_irq_rman) != 0 || + rman_manage_region(&sc->sc_irq_rman, PCI_IRQ_BASE, + PCI_IRQ_END) != 0) + panic("idtpci_attach: failed to set up IRQ rman"); + + device_add_child(dev, "pci", busno); + return (bus_generic_attach(dev)); +} + +static int +idtpci_maxslots(device_t dev) +{ + + return (PCI_SLOTMAX); +} + +static uint32_t +idtpci_read_config(device_t dev, int bus, int slot, int func, int reg, + int bytes) +{ + uint32_t data; + uint32_t shift, mask; + bus_addr_t addr; + + IDTPCI_DPRINTF("%s: tag (%x, %x, %x) reg %d(%d)\n", __func__, + bus, slot, func, reg, bytes); + + addr = idtpci_make_addr(bus, slot, func, reg); + + REG_WRITE(IDT_PCI_CFG_ADDR, addr); + data = REG_READ(IDT_PCI_CFG_DATA); + + switch (reg % 4) { + case 3: + shift = 24; + break; + case 2: + shift = 16; + break; + case 1: + shift = 8; + break; + default: + shift = 0; + break; + } + + switch (bytes) { + case 1: + mask = 0xff; + data = (data >> shift) & mask; + break; + case 2: + mask = 0xffff; + if (reg % 4 == 0) + data = data & mask; + else + data = (data >> 16) & mask; + break; + case 4: + break; + default: + panic("%s: wrong bytes count", __func__); + break; + } + + __asm__ volatile ("sync"); + IDTPCI_DPRINTF("%s: read 0x%x\n", __func__, data); + + return (data); +} + +static void +idtpci_write_config(device_t dev, int bus, int slot, int func, int reg, + uint32_t data, int bytes) +{ + bus_addr_t addr; + uint32_t reg_data; + uint32_t shift, mask; + + IDTPCI_DPRINTF("%s: tag (%x, %x, %x) reg %d(%d) data %08x\n", __func__, + bus, slot, func, reg, bytes, data); + + if (bytes != 4) { + reg_data = idtpci_read_config(dev, bus, slot, func, reg, 4); + + switch (reg % 4) { + case 3: + shift = 24; + break; + case 2: + shift = 16; + break; + case 1: + shift = 8; + break; + default: + shift = 0; + break; + } + + switch (bytes) { + case 1: + mask = 0xff; + data = (reg_data & ~ (mask << shift)) | (data << shift); + break; + case 2: + mask = 0xffff; + if (reg % 4 == 0) + data = (reg_data & ~mask) | data; + else + data = (reg_data & ~ (mask << shift)) | + (data << shift); + break; + case 4: + break; + default: + panic("%s: wrong bytes count", __func__); + break; + } + } + + addr = idtpci_make_addr(bus, slot, func, reg); + + + REG_WRITE(IDT_PCI_CFG_ADDR, addr); + REG_WRITE(IDT_PCI_CFG_DATA, data); + __asm__ volatile ("sync"); + + REG_WRITE(IDT_PCI_CFG_ADDR, 0); + REG_WRITE(IDT_PCI_CFG_DATA, 0); +} + +static int +idtpci_route_interrupt(device_t pcib, device_t device, int pin) +{ + static int idt_pci_table[2][12] = + { + { 0, 0, 2, 3, 2, 3, 0, 0, 0, 0, 0, 1 }, + { 0, 0, 1, 3, 0, 2, 1, 3, 0, 2, 1, 3 } + }; + int dev, bus, irq; + + dev = pci_get_slot(device); + bus = pci_get_bus(device); + if (bootverbose) + device_printf(pcib, "routing pin %d for %s\n", pin, + device_get_nameunit(device)); + if (bus >= 0 && bus <= 1 && + dev >= 0 && dev <= 11) { + irq = IP_IRQ(6, idt_pci_table[bus][dev] + 4); + if (bootverbose) + printf("idtpci: %d/%d/%d -> IRQ%d\n", + pci_get_bus(device), dev, pci_get_function(device), + irq); + return (irq); + } else + printf("idtpci: no mapping for %d/%d/%d\n", + pci_get_bus(device), dev, pci_get_function(device)); + + return (-1); +} + +static int +idtpci_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) +{ + struct idtpci_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 +idtpci_write_ivar(device_t dev, device_t child, int which, uintptr_t result) +{ + struct idtpci_softc * sc = device_get_softc(dev); + + switch (which) { + case PCIB_IVAR_BUS: + sc->sc_busno = result; + return (0); + } + return (ENOENT); +} + +static struct resource * +idtpci_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 idtpci_softc *sc = device_get_softc(bus); + struct resource *rv = NULL; + struct rman *rm1, *rm2; + + switch (type) { + case SYS_RES_IRQ: + rm1 = &sc->sc_irq_rman; + rm2 = NULL; + break; + case SYS_RES_MEMORY: + rm1 = &sc->sc_mem_rman[0]; + rm2 = &sc->sc_mem_rman[1]; + break; + case SYS_RES_IOPORT: + rm1 = &sc->sc_io_rman[0]; + rm2 = &sc->sc_io_rman[1]; + break; + default: + return (NULL); + } + + rv = rman_reserve_resource(rm1, start, end, count, flags, child); + + /* Try second window if it exists */ + if ((rv == NULL) && (rm2 != NULL)) + rv = rman_reserve_resource(rm2, 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 +idtpci_teardown_intr(device_t dev, device_t child, struct resource *res, + void *cookie) +{ + + return (intr_event_remove_handler(cookie)); +} + +static device_method_t idtpci_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, idtpci_probe), + DEVMETHOD(device_attach, idtpci_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, idtpci_read_ivar), + DEVMETHOD(bus_write_ivar, idtpci_write_ivar), + DEVMETHOD(bus_alloc_resource, idtpci_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, idtpci_teardown_intr), + + /* pcib interface */ + DEVMETHOD(pcib_maxslots, idtpci_maxslots), + DEVMETHOD(pcib_read_config, idtpci_read_config), + DEVMETHOD(pcib_write_config, idtpci_write_config), + DEVMETHOD(pcib_route_interrupt, idtpci_route_interrupt), + + {0, 0} +}; + +static driver_t idtpci_driver = { + "pcib", + idtpci_methods, + sizeof(struct idtpci_softc), +}; + +static devclass_t idtpci_devclass; + +DRIVER_MODULE(idtpci, obio, idtpci_driver, idtpci_devclass, 0, 0); diff --git a/sys/mips/idt/idtreg.h b/sys/mips/idt/idtreg.h new file mode 100644 index 0000000..3a088e7 --- /dev/null +++ b/sys/mips/idt/idtreg.h @@ -0,0 +1,153 @@ +/*- + * 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. + * + * $FreeBSD$ + * + */ +#ifndef __IDTREG_H__ +#define __IDTREG_H__ + +/* Interrupt controller */ +#define IDT_BASE_ICU 0x18038000 +#define ICU_IPEND2 0x00 +#define ICU_ITEST2 0x04 +#define ICU_IMASK2 0x08 +#define ICU_IPEND3 0x0C +#define ICU_ITEST3 0x10 +#define ICU_IMASK3 0x14 +#define ICU_IPEND4 0x18 +#define ICU_ITEST4 0x1c +#define ICU_IMASK4 0x20 +#define ICU_IPEND5 0x24 +#define ICU_ITEST5 0x28 +#define ICU_IMASK5 0x2c +#define ICU_IPEND6 0x30 +#define ICU_ITEST6 0x34 +#define ICU_IMASK6 0x38 +#define ICU_NMIPS 0x3c + +#define IDT_BASE_GPIO 0x18050000 +#define GPIO_FUNC 0x00 +#define GPIO_CFG 0x04 +#define GPIO_DATA 0x08 +#define GPIO_ILEVEL 0x0C +#define GPIO_ISTAT 0x10 +#define GPIO_NMIEN 0x14 + +#define IDT_BASE_UART0 0x18058000 + +/* PCI controller */ +#define IDT_BASE_PCI 0x18080000 +#define IDT_PCI_CNTL 0x00 +#define IDT_PCI_CNTL_EN 0x001 +#define IDT_PCI_CNTL_TNR 0x002 +#define IDT_PCI_CNTL_SCE 0x004 +#define IDT_PCI_CNTL_IEN 0x008 +#define IDT_PCI_CNTL_AAA 0x010 +#define IDT_PCI_CNTL_EAP 0x020 +#define IDT_PCI_CNTL_IGM 0x200 +#define IDT_PCI_STATUS 0x04 +#define IDT_PCI_STATUS_RIP 0x20000 +#define IDT_PCI_STATUS_MASK 0x08 +#define IDT_PCI_CFG_ADDR 0x0C +#define IDT_PCI_CFG_DATA 0x10 +/* LBA stuff */ +#define IDT_PCI_LBA0 0x14 +#define IDT_PCI_LBA0_CNTL 0x18 +#define IDT_PCI_LBA_MSI 0x01 +#define IDT_PCI_LBA_SIZE_1MB (0x14 << 2) +#define IDT_PCI_LBA_SIZE_2MB (0x15 << 2) +#define IDT_PCI_LBA_SIZE_4MB (0x16 << 2) +#define IDT_PCI_LBA_SIZE_8MB (0x17 << 2) +#define IDT_PCI_LBA_SIZE_16MB (0x18 << 2) +#define IDT_PCI_LBA_SIZE_32MB (0x19 << 2) +#define IDT_PCI_LBA_SIZE_64MB (0x1A << 2) +#define IDT_PCI_LBA_SIZE_128MB (0x1B << 2) +#define IDT_PCI_LBA_SIZE_256MB (0x1C << 2) +#define IDT_PCI_LBA_FE 0x80 +#define IDT_PCI_LBA_RT 0x100 +#define IDT_PCI_LBA0_MAP 0x1C +#define IDT_PCI_LBA1 0x20 +#define IDT_PCI_LBA1_CNTL 0x24 +#define IDT_PCI_LBA1_MAP 0x28 +#define IDT_PCI_LBA2 0x2C +#define IDT_PCI_LBA2_CNTL 0x30 +#define IDT_PCI_LBA2_MAP 0x34 +#define IDT_PCI_LBA3 0x38 +#define IDT_PCI_LBA3_CNTL 0x3C +#define IDT_PCI_LBA3_MAP 0x40 +/* decoupled registers */ +#define IDT_PCI_DAC 0x44 +#define IDT_PCI_DAS 0x48 +#define IDT_PCI_DASM 0x4C + +#define IDT_PCI_TC 0x5C +#define IDT_PCI_TC_RTIMER 0x10 +#define IDT_PCI_TC_DTIMER 0x08 +/* Messaging unit of PCI controller */ +#define IDT_PCI_IIC 0x8024 +#define IDT_PCI_IIM 0x8028 +#define IDT_PCI_OIC 0x8030 +#define IDT_PCI_OIM 0x8034 + +/* PCI-related stuff */ +#define IDT_PCIMEM0_BASE 0x50000000 +#define IDT_PCIMEM0_SIZE 0x01000000 + +#define IDT_PCIMEM1_BASE 0x60000000 +#define IDT_PCIMEM1_SIZE 0x10000000 + +#define IDT_PCIMEM2_BASE 0x18C00000 +#define IDT_PCIMEM2_SIZE 0x00400000 + +#define IDT_PCIMEM3_BASE 0x18800000 +#define IDT_PCIMEM3_SIZE 0x00100000 + +/* Interrupts-related stuff */ +#define IRQ_BASE 8 +/* Convert <IPbit, irq_offset> pair to IRQ number */ +#define IP_IRQ(IPbit, offset) ((IPbit - 2) * 32 + (offset) + IRQ_BASE) +/* The last one available IRQ */ +#define IRQ_END IP_IRQ(6, 31) +#define ICU_GROUP_REG_OFFSET 0x0C + +#define ICU_IP(irq) (((irq) - IRQ_BASE) & 0x1f) +#define ICU_IP_BIT(irq) (1 << ICU_IP(irq)) +#define ICU_GROUP(irq) (((irq) - IRQ_BASE) >> 5) + +#define ICU_GROUP_MASK_REG(group) \ + (ICU_IMASK2 + ((((group) - 2) * ICU_GROUP_REG_OFFSET))) +#define ICU_GROUP_IPEND_REG(group) \ + (ICU_IPEND2 + ((((group) - 2) * ICU_GROUP_REG_OFFSET))) + +#define ICU_IRQ_MASK_REG(irq) \ + (ICU_IMASK2 + ((ICU_GROUP(irq) * ICU_GROUP_REG_OFFSET))) +#define ICU_IRQ_IPEND_REG(irq) \ + (ICU_IPEND2 + ((ICU_GROUP(irq) * ICU_GROUP_REG_OFFSET))) + +#define PCI_IRQ_BASE IP_IRQ(6, 4) +#define PCI_IRQ_END IP_IRQ(6, 7) + +#endif /* __IDTREG_H__ */ + diff --git a/sys/mips/idt/if_kr.c b/sys/mips/idt/if_kr.c new file mode 100644 index 0000000..817cff2 --- /dev/null +++ b/sys/mips/idt/if_kr.c @@ -0,0 +1,1615 @@ +/*- + * Copyright (C) 2007 + * 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 ``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. + * + * $Id: $ + * + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * RC32434 Ethernet interface 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/resource.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(kr, ether, 1, 1, 1); +MODULE_DEPEND(kr, miibus, 1, 1, 1); + +#include "miibus_if.h" + +#include <mips/mips32/idt/if_krreg.h> + +#define KR_DEBUG + +static int kr_attach(device_t); +static int kr_detach(device_t); +static int kr_ifmedia_upd(struct ifnet *); +static void kr_ifmedia_sts(struct ifnet *, struct ifmediareq *); +static int kr_ioctl(struct ifnet *, u_long, caddr_t); +static void kr_init(void *); +static void kr_init_locked(struct kr_softc *); +static void kr_link_task(void *, int); +static int kr_miibus_readreg(device_t, int, int); +static void kr_miibus_statchg(device_t); +static int kr_miibus_writereg(device_t, int, int, int); +static int kr_probe(device_t); +static void kr_reset(struct kr_softc *); +static int kr_resume(device_t); +static int kr_rx_ring_init(struct kr_softc *); +static int kr_tx_ring_init(struct kr_softc *); +static void kr_shutdown(device_t); +static void kr_start(struct ifnet *); +static void kr_start_locked(struct ifnet *); +static void kr_stop(struct kr_softc *); +static int kr_suspend(device_t); + +static void kr_rx(struct kr_softc *); +static void kr_tx(struct kr_softc *); +static void kr_rx_intr(void *); +static void kr_tx_intr(void *); +static void kr_rx_und_intr(void *); +static void kr_tx_ovr_intr(void *); +static void kr_tick(void *); + +static void kr_dmamap_cb(void *, bus_dma_segment_t *, int, int); +static int kr_dma_alloc(struct kr_softc *); +static void kr_dma_free(struct kr_softc *); +static int kr_newbuf(struct kr_softc *, int); +static __inline void kr_fixup_rx(struct mbuf *); + +static device_method_t kr_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, kr_probe), + DEVMETHOD(device_attach, kr_attach), + DEVMETHOD(device_detach, kr_detach), + DEVMETHOD(device_suspend, kr_suspend), + DEVMETHOD(device_resume, kr_resume), + DEVMETHOD(device_shutdown, kr_shutdown), + + /* bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_driver_added, bus_generic_driver_added), + + /* MII interface */ + DEVMETHOD(miibus_readreg, kr_miibus_readreg), + DEVMETHOD(miibus_writereg, kr_miibus_writereg), + DEVMETHOD(miibus_statchg, kr_miibus_statchg), + + { 0, 0 } +}; + +static driver_t kr_driver = { + "kr", + kr_methods, + sizeof(struct kr_softc) +}; + +static devclass_t kr_devclass; + +DRIVER_MODULE(kr, obio, kr_driver, kr_devclass, 0, 0); +DRIVER_MODULE(kr, cardbus, kr_driver, kr_devclass, 0, 0); +DRIVER_MODULE(miibus, kr, miibus_driver, miibus_devclass, 0, 0); + +static int +kr_probe(device_t dev) +{ + + device_set_desc(dev, "RC32434 Ethernet interface"); + return (0); +} + +static int +kr_attach(device_t dev) +{ + uint8_t eaddr[ETHER_ADDR_LEN]; + struct ifnet *ifp; + struct kr_softc *sc; + int error = 0, rid; + int unit; + + sc = device_get_softc(dev); + unit = device_get_unit(dev); + sc->kr_dev = dev; + + mtx_init(&sc->kr_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, + MTX_DEF); + callout_init_mtx(&sc->kr_stat_callout, &sc->kr_mtx, 0); + TASK_INIT(&sc->kr_link_task, 0, kr_link_task, sc); + pci_enable_busmaster(dev); + + /* Map control/status registers. */ + sc->kr_rid = 0; + sc->kr_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->kr_rid, + RF_ACTIVE); + + if (sc->kr_res == NULL) { + device_printf(dev, "couldn't map memory\n"); + error = ENXIO; + goto fail; + } + + sc->kr_btag = rman_get_bustag(sc->kr_res); + sc->kr_bhandle = rman_get_bushandle(sc->kr_res); + + /* Allocate interrupts */ + rid = 0; + sc->kr_rx_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, KR_RX_IRQ, + KR_RX_IRQ, 1, RF_SHAREABLE | RF_ACTIVE); + + if (sc->kr_rx_irq == NULL) { + device_printf(dev, "couldn't map rx interrupt\n"); + error = ENXIO; + goto fail; + } + + rid = 0; + sc->kr_tx_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, KR_TX_IRQ, + KR_TX_IRQ, 1, RF_SHAREABLE | RF_ACTIVE); + + if (sc->kr_tx_irq == NULL) { + device_printf(dev, "couldn't map tx interrupt\n"); + error = ENXIO; + goto fail; + } + + rid = 0; + sc->kr_rx_und_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, + KR_RX_UND_IRQ, KR_RX_UND_IRQ, 1, RF_SHAREABLE | RF_ACTIVE); + + if (sc->kr_rx_und_irq == NULL) { + device_printf(dev, "couldn't map rx underrun interrupt\n"); + error = ENXIO; + goto fail; + } + + rid = 0; + sc->kr_tx_ovr_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, + KR_TX_OVR_IRQ, KR_TX_OVR_IRQ, 1, RF_SHAREABLE | RF_ACTIVE); + + if (sc->kr_tx_ovr_irq == NULL) { + device_printf(dev, "couldn't map tx overrun interrupt\n"); + error = ENXIO; + goto fail; + } + + /* Allocate ifnet structure. */ + ifp = sc->kr_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 = kr_ioctl; + ifp->if_start = kr_start; + ifp->if_init = kr_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] = 0x0C; + eaddr[2] = 0x42; + eaddr[3] = 0x09; + eaddr[4] = 0x5E; + eaddr[5] = 0x6B; + + if (kr_dma_alloc(sc) != 0) { + error = ENXIO; + goto fail; + } + + /* TODO: calculate prescale */ + CSR_WRITE_4(sc, KR_ETHMCP, (165000000 / (1250000 + 1)) & ~1); + + CSR_WRITE_4(sc, KR_MIIMCFG, KR_MIIMCFG_R); + DELAY(1000); + CSR_WRITE_4(sc, KR_MIIMCFG, 0); + + /* Do MII setup. */ + if (mii_phy_probe(dev, &sc->kr_miibus, + kr_ifmedia_upd, kr_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->kr_rx_irq, INTR_TYPE_NET | INTR_MPSAFE, + NULL, kr_rx_intr, sc, &sc->kr_rx_intrhand); + + if (error) { + device_printf(dev, "couldn't set up rx irq\n"); + ether_ifdetach(ifp); + goto fail; + } + + error = bus_setup_intr(dev, sc->kr_tx_irq, INTR_TYPE_NET | INTR_MPSAFE, + NULL, kr_tx_intr, sc, &sc->kr_tx_intrhand); + + if (error) { + device_printf(dev, "couldn't set up tx irq\n"); + ether_ifdetach(ifp); + goto fail; + } + + error = bus_setup_intr(dev, sc->kr_rx_und_irq, + INTR_TYPE_NET | INTR_MPSAFE, NULL, kr_rx_und_intr, sc, + &sc->kr_rx_und_intrhand); + + if (error) { + device_printf(dev, "couldn't set up rx underrun irq\n"); + ether_ifdetach(ifp); + goto fail; + } + + error = bus_setup_intr(dev, sc->kr_tx_ovr_irq, + INTR_TYPE_NET | INTR_MPSAFE, NULL, kr_tx_ovr_intr, sc, + &sc->kr_tx_ovr_intrhand); + + if (error) { + device_printf(dev, "couldn't set up tx overrun irq\n"); + ether_ifdetach(ifp); + goto fail; + } + +fail: + if (error) + kr_detach(dev); + + return (error); +} + +static int +kr_detach(device_t dev) +{ + struct kr_softc *sc = device_get_softc(dev); + struct ifnet *ifp = sc->kr_ifp; + + KASSERT(mtx_initialized(&sc->kr_mtx), ("vr mutex not initialized")); + + /* These should only be active if attach succeeded */ + if (device_is_attached(dev)) { + KR_LOCK(sc); + sc->kr_detach = 1; + kr_stop(sc); + KR_UNLOCK(sc); + taskqueue_drain(taskqueue_swi, &sc->kr_link_task); + ether_ifdetach(ifp); + } + if (sc->kr_miibus) + device_delete_child(dev, sc->kr_miibus); + bus_generic_detach(dev); + + if (sc->kr_rx_intrhand) + bus_teardown_intr(dev, sc->kr_rx_irq, sc->kr_rx_intrhand); + if (sc->kr_rx_irq) + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->kr_rx_irq); + if (sc->kr_tx_intrhand) + bus_teardown_intr(dev, sc->kr_tx_irq, sc->kr_tx_intrhand); + if (sc->kr_tx_irq) + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->kr_tx_irq); + if (sc->kr_rx_und_intrhand) + bus_teardown_intr(dev, sc->kr_rx_und_irq, + sc->kr_rx_und_intrhand); + if (sc->kr_rx_und_irq) + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->kr_rx_und_irq); + if (sc->kr_tx_ovr_intrhand) + bus_teardown_intr(dev, sc->kr_tx_ovr_irq, + sc->kr_tx_ovr_intrhand); + if (sc->kr_tx_ovr_irq) + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->kr_tx_ovr_irq); + + if (sc->kr_res) + bus_release_resource(dev, SYS_RES_MEMORY, sc->kr_rid, + sc->kr_res); + + if (ifp) + if_free(ifp); + + kr_dma_free(sc); + + mtx_destroy(&sc->kr_mtx); + + return (0); + +} + +static int +kr_suspend(device_t dev) +{ + + panic("%s", __func__); + return 0; +} + +static int +kr_resume(device_t dev) +{ + + panic("%s", __func__); + return 0; +} + +static void +kr_shutdown(device_t dev) +{ + struct kr_softc *sc; + + sc = device_get_softc(dev); + + KR_LOCK(sc); + kr_stop(sc); + KR_UNLOCK(sc); +} + +static int +kr_miibus_readreg(device_t dev, int phy, int reg) +{ + struct kr_softc * sc = device_get_softc(dev); + int i, result; + + i = KR_MII_TIMEOUT; + while ((CSR_READ_4(sc, KR_MIIMIND) & KR_MIIMIND_BSY) && i) + i--; + + if (i == 0) + device_printf(dev, "phy mii is busy %d:%d\n", phy, reg); + + CSR_WRITE_4(sc, KR_MIIMADDR, (phy << 8) | reg); + + i = KR_MII_TIMEOUT; + while ((CSR_READ_4(sc, KR_MIIMIND) & KR_MIIMIND_BSY) && i) + i--; + + if (i == 0) + device_printf(dev, "phy mii is busy %d:%d\n", phy, reg); + + CSR_WRITE_4(sc, KR_MIIMCMD, KR_MIIMCMD_RD); + + i = KR_MII_TIMEOUT; + while ((CSR_READ_4(sc, KR_MIIMIND) & KR_MIIMIND_BSY) && i) + i--; + + if (i == 0) + device_printf(dev, "phy mii read is timed out %d:%d\n", phy, + reg); + + if (CSR_READ_4(sc, KR_MIIMIND) & KR_MIIMIND_NV) + printf("phy mii readreg failed %d:%d: data not valid\n", + phy, reg); + + result = CSR_READ_4(sc , KR_MIIMRDD); + CSR_WRITE_4(sc, KR_MIIMCMD, 0); + + return (result); +} + +static int +kr_miibus_writereg(device_t dev, int phy, int reg, int data) +{ + struct kr_softc * sc = device_get_softc(dev); + int i; + + i = KR_MII_TIMEOUT; + while ((CSR_READ_4(sc, KR_MIIMIND) & KR_MIIMIND_BSY) && i) + i--; + + if (i == 0) + device_printf(dev, "phy mii is busy %d:%d\n", phy, reg); + + CSR_WRITE_4(sc, KR_MIIMADDR, (phy << 8) | reg); + + i = KR_MII_TIMEOUT; + while ((CSR_READ_4(sc, KR_MIIMIND) & KR_MIIMIND_BSY) && i) + i--; + + if (i == 0) + device_printf(dev, "phy mii is busy %d:%d\n", phy, reg); + + CSR_WRITE_4(sc, KR_MIIMWTD, data); + + i = KR_MII_TIMEOUT; + while ((CSR_READ_4(sc, KR_MIIMIND) & KR_MIIMIND_BSY) && i) + i--; + + if (i == 0) + device_printf(dev, "phy mii is busy %d:%d\n", phy, reg); + + return (0); +} + +static void +kr_miibus_statchg(device_t dev) +{ + struct kr_softc *sc; + + sc = device_get_softc(dev); + taskqueue_enqueue(taskqueue_swi, &sc->kr_link_task); +} + +static void +kr_link_task(void *arg, int pending) +{ + struct kr_softc *sc; + struct mii_data *mii; + struct ifnet *ifp; + /* int lfdx, mfdx; */ + + sc = (struct kr_softc *)arg; + + KR_LOCK(sc); + mii = device_get_softc(sc->kr_miibus); + ifp = sc->kr_ifp; + if (mii == NULL || ifp == NULL || + (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { + KR_UNLOCK(sc); + return; + } + + if (mii->mii_media_status & IFM_ACTIVE) { + if (IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) + sc->kr_link_status = 1; + } else + sc->kr_link_status = 0; + + KR_UNLOCK(sc); +} + +static void +kr_reset(struct kr_softc *sc) +{ + int i; + + CSR_WRITE_4(sc, KR_ETHINTFC, 0); + + for (i = 0; i < KR_TIMEOUT; i++) { + DELAY(10); + if (!(CSR_READ_4(sc, KR_ETHINTFC) & ETH_INTFC_RIP)) + break; + } + + if (i == KR_TIMEOUT) + device_printf(sc->kr_dev, "reset time out\n"); +} + +static void +kr_init(void *xsc) +{ + struct kr_softc *sc = xsc; + + KR_LOCK(sc); + kr_init_locked(sc); + KR_UNLOCK(sc); +} + +static void +kr_init_locked(struct kr_softc *sc) +{ + struct ifnet *ifp = sc->kr_ifp; + struct mii_data *mii; + + KR_LOCK_ASSERT(sc); + + mii = device_get_softc(sc->kr_miibus); + + kr_stop(sc); + kr_reset(sc); + + CSR_WRITE_4(sc, KR_ETHINTFC, ETH_INTFC_EN); + + /* Init circular RX list. */ + if (kr_rx_ring_init(sc) != 0) { + device_printf(sc->kr_dev, + "initialization failed: no memory for rx buffers\n"); + kr_stop(sc); + return; + } + + /* Init tx descriptors. */ + kr_tx_ring_init(sc); + + KR_DMA_WRITE_REG(KR_DMA_RXCHAN, DMA_S, 0); + KR_DMA_WRITE_REG(KR_DMA_RXCHAN, DMA_NDPTR, 0); + KR_DMA_WRITE_REG(KR_DMA_RXCHAN, DMA_DPTR, + sc->kr_rdata.kr_rx_ring_paddr); + + + KR_DMA_CLEARBITS_REG(KR_DMA_RXCHAN, DMA_SM, + DMA_SM_H | DMA_SM_E | DMA_SM_D) ; + + KR_DMA_WRITE_REG(KR_DMA_TXCHAN, DMA_S, 0); + KR_DMA_WRITE_REG(KR_DMA_TXCHAN, DMA_NDPTR, 0); + KR_DMA_WRITE_REG(KR_DMA_TXCHAN, DMA_DPTR, 0); + KR_DMA_CLEARBITS_REG(KR_DMA_TXCHAN, DMA_SM, + DMA_SM_F | DMA_SM_E); + + + /* Accept only packets destined for THIS Ethernet device address */ + CSR_WRITE_4(sc, KR_ETHARC, 1); + + /* + * Set all Ethernet address registers to the same initial values + * set all four addresses to 66-88-aa-cc-dd-ee + */ + CSR_WRITE_4(sc, KR_ETHSAL0, 0x42095E6B); + CSR_WRITE_4(sc, KR_ETHSAH0, 0x0000000C); + + CSR_WRITE_4(sc, KR_ETHSAL1, 0x42095E6B); + CSR_WRITE_4(sc, KR_ETHSAH1, 0x0000000C); + + CSR_WRITE_4(sc, KR_ETHSAL2, 0x42095E6B); + CSR_WRITE_4(sc, KR_ETHSAH2, 0x0000000C); + + CSR_WRITE_4(sc, KR_ETHSAL3, 0x42095E6B); + CSR_WRITE_4(sc, KR_ETHSAH3, 0x0000000C); + + CSR_WRITE_4(sc, KR_ETHMAC2, + KR_ETH_MAC2_PEN | KR_ETH_MAC2_CEN | KR_ETH_MAC2_FD); + + CSR_WRITE_4(sc, KR_ETHIPGT, KR_ETHIPGT_FULL_DUPLEX); + CSR_WRITE_4(sc, KR_ETHIPGR, 0x12); /* minimum value */ + + CSR_WRITE_4(sc, KR_MIIMCFG, KR_MIIMCFG_R); + DELAY(1000); + CSR_WRITE_4(sc, KR_MIIMCFG, 0); + + /* TODO: calculate prescale */ + CSR_WRITE_4(sc, KR_ETHMCP, (165000000 / (1250000 + 1)) & ~1); + + /* FIFO Tx threshold level */ + CSR_WRITE_4(sc, KR_ETHFIFOTT, 0x30); + + CSR_WRITE_4(sc, KR_ETHMAC1, KR_ETH_MAC1_RE); + + sc->kr_link_status = 0; + mii_mediachg(mii); + + ifp->if_drv_flags |= IFF_DRV_RUNNING; + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + + callout_reset(&sc->kr_stat_callout, hz, kr_tick, sc); +} + +static void +kr_start(struct ifnet *ifp) +{ + struct kr_softc *sc; + + sc = ifp->if_softc; + + KR_LOCK(sc); + kr_start_locked(ifp); + KR_UNLOCK(sc); +} + +/* + * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data + * pointers to the fragment pointers. + */ +static int +kr_encap(struct kr_softc *sc, struct mbuf **m_head) +{ + struct kr_txdesc *txd; + struct kr_desc *desc, *prev_desc; + bus_dma_segment_t txsegs[KR_MAXFRAGS]; + uint32_t link_addr; + int error, i, nsegs, prod, si, prev_prod; + + KR_LOCK_ASSERT(sc); + + prod = sc->kr_cdata.kr_tx_prod; + txd = &sc->kr_cdata.kr_txdesc[prod]; + error = bus_dmamap_load_mbuf_sg(sc->kr_cdata.kr_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->kr_cdata.kr_tx_cnt + nsegs >= (KR_TX_RING_CNT - 1)) { + bus_dmamap_unload(sc->kr_cdata.kr_tx_tag, txd->tx_dmamap); + return (ENOBUFS); + } + + txd->tx_m = *m_head; + bus_dmamap_sync(sc->kr_cdata.kr_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 kr_link is not zero. The last one should + * have COF flag set, to pickup next chain from NDPTR + */ + prev_prod = prod; + desc = prev_desc = NULL; + for (i = 0; i < nsegs; i++) { + desc = &sc->kr_rdata.kr_tx_ring[prod]; + desc->kr_ctl = KR_DMASIZE(txsegs[i].ds_len) | KR_CTL_IOF; + if (i == 0) + desc->kr_devcs = KR_DMATX_DEVCS_FD; + desc->kr_ca = txsegs[i].ds_addr; + desc->kr_link = 0; + /* link with previous descriptor */ + if (prev_desc) + prev_desc->kr_link = KR_TX_RING_ADDR(sc, prod); + + sc->kr_cdata.kr_tx_cnt++; + prev_desc = desc; + KR_INC(prod, KR_TX_RING_CNT); + } + + /* + * Set COF for last descriptor and mark last fragment with LD flag + */ + if (desc) { + desc->kr_ctl |= KR_CTL_COF; + desc->kr_devcs |= KR_DMATX_DEVCS_LD; + } + + /* Update producer index. */ + sc->kr_cdata.kr_tx_prod = prod; + + /* Sync descriptors. */ + bus_dmamap_sync(sc->kr_cdata.kr_tx_ring_tag, + sc->kr_cdata.kr_tx_ring_map, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + + /* Start transmitting */ + /* Check if new list is queued in NDPTR */ + if (KR_DMA_READ_REG(KR_DMA_TXCHAN, DMA_NDPTR) == 0) { + /* NDPTR is not busy - start new list */ + KR_DMA_WRITE_REG(KR_DMA_TXCHAN, DMA_NDPTR, + KR_TX_RING_ADDR(sc, si)); + } + else { + link_addr = KR_TX_RING_ADDR(sc, si); + /* Get previous descriptor */ + si = (si + KR_TX_RING_CNT - 1) % KR_TX_RING_CNT; + desc = &sc->kr_rdata.kr_tx_ring[si]; + desc->kr_link = link_addr; + } + + return (0); +} + +static void +kr_start_locked(struct ifnet *ifp) +{ + struct kr_softc *sc; + struct mbuf *m_head; + int enq; + + sc = ifp->if_softc; + + KR_LOCK_ASSERT(sc); + + if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != + IFF_DRV_RUNNING || sc->kr_link_status == 0 ) + return; + + for (enq = 0; !IFQ_DRV_IS_EMPTY(&ifp->if_snd) && + sc->kr_cdata.kr_tx_cnt < KR_TX_RING_CNT - 2; ) { + IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head); + if (m_head == NULL) + break; + /* + * Pack the data into the transmit ring. If we + * don't have room, set the OACTIVE flag and wait + * for the NIC to drain the ring. + */ + if (kr_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 +kr_stop(struct kr_softc *sc) +{ + struct ifnet *ifp; + + KR_LOCK_ASSERT(sc); + + + ifp = sc->kr_ifp; + ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); + callout_stop(&sc->kr_stat_callout); + + /* mask out RX interrupts */ + KR_DMA_SETBITS_REG(KR_DMA_RXCHAN, DMA_SM, + DMA_SM_D | DMA_SM_H | DMA_SM_E); + + /* mask out TX interrupts */ + KR_DMA_SETBITS_REG(KR_DMA_TXCHAN, DMA_SM, + DMA_SM_F | DMA_SM_E); + + /* Abort RX DMA transactions */ + if (KR_DMA_READ_REG(KR_DMA_RXCHAN, DMA_C) & DMA_C_R) { + /* Set ABORT bit if trunsuction is in progress */ + KR_DMA_WRITE_REG(KR_DMA_RXCHAN, DMA_C, DMA_C_ABORT); + /* XXX: Add timeout */ + while ((KR_DMA_READ_REG(KR_DMA_RXCHAN, DMA_S) & DMA_S_H) == 0) + DELAY(10); + KR_DMA_WRITE_REG(KR_DMA_RXCHAN, DMA_S, 0); + } + KR_DMA_WRITE_REG(KR_DMA_RXCHAN, DMA_DPTR, 0); + KR_DMA_WRITE_REG(KR_DMA_RXCHAN, DMA_NDPTR, 0); + + /* Abort TX DMA transactions */ + if (KR_DMA_READ_REG(KR_DMA_TXCHAN, DMA_C) & DMA_C_R) { + /* Set ABORT bit if trunsuction is in progress */ + KR_DMA_WRITE_REG(KR_DMA_TXCHAN, DMA_C, DMA_C_ABORT); + /* XXX: Add timeout */ + while ((KR_DMA_READ_REG(KR_DMA_TXCHAN, DMA_S) & DMA_S_H) == 0) + DELAY(10); + KR_DMA_WRITE_REG(KR_DMA_TXCHAN, DMA_S, 0); + } + KR_DMA_WRITE_REG(KR_DMA_TXCHAN, DMA_DPTR, 0); + KR_DMA_WRITE_REG(KR_DMA_TXCHAN, DMA_NDPTR, 0); + + CSR_WRITE_4(sc, KR_ETHINTFC, 0); +} + + +static int +kr_ioctl(struct ifnet *ifp, u_long command, caddr_t data) +{ + struct kr_softc *sc = ifp->if_softc; + struct ifreq *ifr = (struct ifreq *) data; + struct mii_data *mii; + int error; + + switch (command) { + case SIOCSIFFLAGS: +#if 0 + KR_LOCK(sc); + if (ifp->if_flags & IFF_UP) { + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + if ((ifp->if_flags ^ sc->kr_if_flags) & + (IFF_PROMISC | IFF_ALLMULTI)) + kr_set_filter(sc); + } else { + if (sc->kr_detach == 0) + kr_init_locked(sc); + } + } else { + if (ifp->if_drv_flags & IFF_DRV_RUNNING) + kr_stop(sc); + } + sc->kr_if_flags = ifp->if_flags; + KR_UNLOCK(sc); +#endif + error = 0; + break; + case SIOCADDMULTI: + case SIOCDELMULTI: +#if 0 + KR_LOCK(sc); + kr_set_filter(sc); + KR_UNLOCK(sc); +#endif + error = 0; + break; + case SIOCGIFMEDIA: + case SIOCSIFMEDIA: + mii = device_get_softc(sc->kr_miibus); + error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); + break; + case SIOCSIFCAP: + error = 0; +#if 0 + mask = ifr->ifr_reqcap ^ ifp->if_capenable; + if ((mask & IFCAP_HWCSUM) != 0) { + ifp->if_capenable ^= IFCAP_HWCSUM; + if ((IFCAP_HWCSUM & ifp->if_capenable) && + (IFCAP_HWCSUM & ifp->if_capabilities)) + ifp->if_hwassist = KR_CSUM_FEATURES; + else + ifp->if_hwassist = 0; + } + if ((mask & IFCAP_VLAN_HWTAGGING) != 0) { + ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING; + if (IFCAP_VLAN_HWTAGGING & ifp->if_capenable && + IFCAP_VLAN_HWTAGGING & ifp->if_capabilities && + ifp->if_drv_flags & IFF_DRV_RUNNING) { + KR_LOCK(sc); + kr_vlan_setup(sc); + KR_UNLOCK(sc); + } + } + VLAN_CAPABILITIES(ifp); +#endif + break; + default: + error = ether_ioctl(ifp, command, data); + break; + } + + return (error); +} + +/* + * Set media options. + */ +static int +kr_ifmedia_upd(struct ifnet *ifp) +{ + struct kr_softc *sc; + struct mii_data *mii; + struct mii_softc *miisc; + int error; + + sc = ifp->if_softc; + KR_LOCK(sc); + mii = device_get_softc(sc->kr_miibus); + if (mii->mii_instance) { + LIST_FOREACH(miisc, &mii->mii_phys, mii_list) + mii_phy_reset(miisc); + } + error = mii_mediachg(mii); + KR_UNLOCK(sc); + + return (error); +} + +/* + * Report current media status. + */ +static void +kr_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) +{ + struct kr_softc *sc = ifp->if_softc; + struct mii_data *mii; + + mii = device_get_softc(sc->kr_miibus); + KR_LOCK(sc); + mii_pollstat(mii); + KR_UNLOCK(sc); + ifmr->ifm_active = mii->mii_media_active; + ifmr->ifm_status = mii->mii_media_status; +} + +struct kr_dmamap_arg { + bus_addr_t kr_busaddr; +}; + +static void +kr_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error) +{ + struct kr_dmamap_arg *ctx; + + if (error != 0) + return; + ctx = arg; + ctx->kr_busaddr = segs[0].ds_addr; +} + +static int +kr_dma_alloc(struct kr_softc *sc) +{ + struct kr_dmamap_arg ctx; + struct kr_txdesc *txd; + struct kr_rxdesc *rxd; + int error, i; + + /* Create parent DMA tag. */ + error = bus_dma_tag_create( + bus_get_dma_tag(sc->kr_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->kr_cdata.kr_parent_tag); + if (error != 0) { + device_printf(sc->kr_dev, "failed to create parent DMA tag\n"); + goto fail; + } + /* Create tag for Tx ring. */ + error = bus_dma_tag_create( + sc->kr_cdata.kr_parent_tag, /* parent */ + KR_RING_ALIGN, 0, /* alignment, boundary */ + BUS_SPACE_MAXADDR, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + KR_TX_RING_SIZE, /* maxsize */ + 1, /* nsegments */ + KR_TX_RING_SIZE, /* maxsegsize */ + 0, /* flags */ + NULL, NULL, /* lockfunc, lockarg */ + &sc->kr_cdata.kr_tx_ring_tag); + if (error != 0) { + device_printf(sc->kr_dev, "failed to create Tx ring DMA tag\n"); + goto fail; + } + + /* Create tag for Rx ring. */ + error = bus_dma_tag_create( + sc->kr_cdata.kr_parent_tag, /* parent */ + KR_RING_ALIGN, 0, /* alignment, boundary */ + BUS_SPACE_MAXADDR, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + KR_RX_RING_SIZE, /* maxsize */ + 1, /* nsegments */ + KR_RX_RING_SIZE, /* maxsegsize */ + 0, /* flags */ + NULL, NULL, /* lockfunc, lockarg */ + &sc->kr_cdata.kr_rx_ring_tag); + if (error != 0) { + device_printf(sc->kr_dev, "failed to create Rx ring DMA tag\n"); + goto fail; + } + + /* Create tag for Tx buffers. */ + error = bus_dma_tag_create( + sc->kr_cdata.kr_parent_tag, /* parent */ + sizeof(uint32_t), 0, /* alignment, boundary */ + BUS_SPACE_MAXADDR, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + MCLBYTES * KR_MAXFRAGS, /* maxsize */ + KR_MAXFRAGS, /* nsegments */ + MCLBYTES, /* maxsegsize */ + 0, /* flags */ + NULL, NULL, /* lockfunc, lockarg */ + &sc->kr_cdata.kr_tx_tag); + if (error != 0) { + device_printf(sc->kr_dev, "failed to create Tx DMA tag\n"); + goto fail; + } + + /* Create tag for Rx buffers. */ + error = bus_dma_tag_create( + sc->kr_cdata.kr_parent_tag, /* parent */ + KR_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->kr_cdata.kr_rx_tag); + if (error != 0) { + device_printf(sc->kr_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->kr_cdata.kr_tx_ring_tag, + (void **)&sc->kr_rdata.kr_tx_ring, BUS_DMA_WAITOK | + BUS_DMA_COHERENT | BUS_DMA_ZERO, &sc->kr_cdata.kr_tx_ring_map); + if (error != 0) { + device_printf(sc->kr_dev, + "failed to allocate DMA'able memory for Tx ring\n"); + goto fail; + } + + ctx.kr_busaddr = 0; + error = bus_dmamap_load(sc->kr_cdata.kr_tx_ring_tag, + sc->kr_cdata.kr_tx_ring_map, sc->kr_rdata.kr_tx_ring, + KR_TX_RING_SIZE, kr_dmamap_cb, &ctx, 0); + if (error != 0 || ctx.kr_busaddr == 0) { + device_printf(sc->kr_dev, + "failed to load DMA'able memory for Tx ring\n"); + goto fail; + } + sc->kr_rdata.kr_tx_ring_paddr = ctx.kr_busaddr; + + /* Allocate DMA'able memory and load the DMA map for Rx ring. */ + error = bus_dmamem_alloc(sc->kr_cdata.kr_rx_ring_tag, + (void **)&sc->kr_rdata.kr_rx_ring, BUS_DMA_WAITOK | + BUS_DMA_COHERENT | BUS_DMA_ZERO, &sc->kr_cdata.kr_rx_ring_map); + if (error != 0) { + device_printf(sc->kr_dev, + "failed to allocate DMA'able memory for Rx ring\n"); + goto fail; + } + + ctx.kr_busaddr = 0; + error = bus_dmamap_load(sc->kr_cdata.kr_rx_ring_tag, + sc->kr_cdata.kr_rx_ring_map, sc->kr_rdata.kr_rx_ring, + KR_RX_RING_SIZE, kr_dmamap_cb, &ctx, 0); + if (error != 0 || ctx.kr_busaddr == 0) { + device_printf(sc->kr_dev, + "failed to load DMA'able memory for Rx ring\n"); + goto fail; + } + sc->kr_rdata.kr_rx_ring_paddr = ctx.kr_busaddr; + + /* Create DMA maps for Tx buffers. */ + for (i = 0; i < KR_TX_RING_CNT; i++) { + txd = &sc->kr_cdata.kr_txdesc[i]; + txd->tx_m = NULL; + txd->tx_dmamap = NULL; + error = bus_dmamap_create(sc->kr_cdata.kr_tx_tag, 0, + &txd->tx_dmamap); + if (error != 0) { + device_printf(sc->kr_dev, + "failed to create Tx dmamap\n"); + goto fail; + } + } + /* Create DMA maps for Rx buffers. */ + if ((error = bus_dmamap_create(sc->kr_cdata.kr_rx_tag, 0, + &sc->kr_cdata.kr_rx_sparemap)) != 0) { + device_printf(sc->kr_dev, + "failed to create spare Rx dmamap\n"); + goto fail; + } + for (i = 0; i < KR_RX_RING_CNT; i++) { + rxd = &sc->kr_cdata.kr_rxdesc[i]; + rxd->rx_m = NULL; + rxd->rx_dmamap = NULL; + error = bus_dmamap_create(sc->kr_cdata.kr_rx_tag, 0, + &rxd->rx_dmamap); + if (error != 0) { + device_printf(sc->kr_dev, + "failed to create Rx dmamap\n"); + goto fail; + } + } + +fail: + return (error); +} + +static void +kr_dma_free(struct kr_softc *sc) +{ + struct kr_txdesc *txd; + struct kr_rxdesc *rxd; + int i; + + /* Tx ring. */ + if (sc->kr_cdata.kr_tx_ring_tag) { + if (sc->kr_cdata.kr_tx_ring_map) + bus_dmamap_unload(sc->kr_cdata.kr_tx_ring_tag, + sc->kr_cdata.kr_tx_ring_map); + if (sc->kr_cdata.kr_tx_ring_map && + sc->kr_rdata.kr_tx_ring) + bus_dmamem_free(sc->kr_cdata.kr_tx_ring_tag, + sc->kr_rdata.kr_tx_ring, + sc->kr_cdata.kr_tx_ring_map); + sc->kr_rdata.kr_tx_ring = NULL; + sc->kr_cdata.kr_tx_ring_map = NULL; + bus_dma_tag_destroy(sc->kr_cdata.kr_tx_ring_tag); + sc->kr_cdata.kr_tx_ring_tag = NULL; + } + /* Rx ring. */ + if (sc->kr_cdata.kr_rx_ring_tag) { + if (sc->kr_cdata.kr_rx_ring_map) + bus_dmamap_unload(sc->kr_cdata.kr_rx_ring_tag, + sc->kr_cdata.kr_rx_ring_map); + if (sc->kr_cdata.kr_rx_ring_map && + sc->kr_rdata.kr_rx_ring) + bus_dmamem_free(sc->kr_cdata.kr_rx_ring_tag, + sc->kr_rdata.kr_rx_ring, + sc->kr_cdata.kr_rx_ring_map); + sc->kr_rdata.kr_rx_ring = NULL; + sc->kr_cdata.kr_rx_ring_map = NULL; + bus_dma_tag_destroy(sc->kr_cdata.kr_rx_ring_tag); + sc->kr_cdata.kr_rx_ring_tag = NULL; + } + /* Tx buffers. */ + if (sc->kr_cdata.kr_tx_tag) { + for (i = 0; i < KR_TX_RING_CNT; i++) { + txd = &sc->kr_cdata.kr_txdesc[i]; + if (txd->tx_dmamap) { + bus_dmamap_destroy(sc->kr_cdata.kr_tx_tag, + txd->tx_dmamap); + txd->tx_dmamap = NULL; + } + } + bus_dma_tag_destroy(sc->kr_cdata.kr_tx_tag); + sc->kr_cdata.kr_tx_tag = NULL; + } + /* Rx buffers. */ + if (sc->kr_cdata.kr_rx_tag) { + for (i = 0; i < KR_RX_RING_CNT; i++) { + rxd = &sc->kr_cdata.kr_rxdesc[i]; + if (rxd->rx_dmamap) { + bus_dmamap_destroy(sc->kr_cdata.kr_rx_tag, + rxd->rx_dmamap); + rxd->rx_dmamap = NULL; + } + } + if (sc->kr_cdata.kr_rx_sparemap) { + bus_dmamap_destroy(sc->kr_cdata.kr_rx_tag, + sc->kr_cdata.kr_rx_sparemap); + sc->kr_cdata.kr_rx_sparemap = 0; + } + bus_dma_tag_destroy(sc->kr_cdata.kr_rx_tag); + sc->kr_cdata.kr_rx_tag = NULL; + } + + if (sc->kr_cdata.kr_parent_tag) { + bus_dma_tag_destroy(sc->kr_cdata.kr_parent_tag); + sc->kr_cdata.kr_parent_tag = NULL; + } +} + +/* + * Initialize the transmit descriptors. + */ +static int +kr_tx_ring_init(struct kr_softc *sc) +{ + struct kr_ring_data *rd; + struct kr_txdesc *txd; + bus_addr_t addr; + int i; + + sc->kr_cdata.kr_tx_prod = 0; + sc->kr_cdata.kr_tx_cons = 0; + sc->kr_cdata.kr_tx_cnt = 0; + sc->kr_cdata.kr_tx_pkts = 0; + + rd = &sc->kr_rdata; + bzero(rd->kr_tx_ring, KR_TX_RING_SIZE); + for (i = 0; i < KR_TX_RING_CNT; i++) { + if (i == KR_TX_RING_CNT - 1) + addr = KR_TX_RING_ADDR(sc, 0); + else + addr = KR_TX_RING_ADDR(sc, i + 1); + rd->kr_tx_ring[i].kr_ctl = KR_CTL_IOF; + rd->kr_tx_ring[i].kr_ca = 0; + rd->kr_tx_ring[i].kr_devcs = 0; + rd->kr_tx_ring[i].kr_link = 0; + txd = &sc->kr_cdata.kr_txdesc[i]; + txd->tx_m = NULL; + } + + bus_dmamap_sync(sc->kr_cdata.kr_tx_ring_tag, + sc->kr_cdata.kr_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 +kr_rx_ring_init(struct kr_softc *sc) +{ + struct kr_ring_data *rd; + struct kr_rxdesc *rxd; + bus_addr_t addr; + int i; + + sc->kr_cdata.kr_rx_cons = 0; + + rd = &sc->kr_rdata; + bzero(rd->kr_rx_ring, KR_RX_RING_SIZE); + for (i = 0; i < KR_RX_RING_CNT; i++) { + rxd = &sc->kr_cdata.kr_rxdesc[i]; + rxd->rx_m = NULL; + rxd->desc = &rd->kr_rx_ring[i]; + if (i == KR_RX_RING_CNT - 1) + addr = KR_RX_RING_ADDR(sc, 0); + else + addr = KR_RX_RING_ADDR(sc, i + 1); + rd->kr_rx_ring[i].kr_ctl = KR_CTL_IOD; + if (i == KR_RX_RING_CNT - 1) + rd->kr_rx_ring[i].kr_ctl |= KR_CTL_COD; + rd->kr_rx_ring[i].kr_devcs = 0; + rd->kr_rx_ring[i].kr_ca = 0; + rd->kr_rx_ring[i].kr_link = addr; + if (kr_newbuf(sc, i) != 0) + return (ENOBUFS); + } + + bus_dmamap_sync(sc->kr_cdata.kr_rx_ring_tag, + sc->kr_cdata.kr_rx_ring_map, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + + return (0); +} + +/* + * Initialize an RX descriptor and attach an MBUF cluster. + */ +static int +kr_newbuf(struct kr_softc *sc, int idx) +{ + struct kr_desc *desc; + struct kr_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->kr_cdata.kr_rx_tag, + sc->kr_cdata.kr_rx_sparemap, m, segs, &nsegs, 0) != 0) { + m_freem(m); + return (ENOBUFS); + } + KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs)); + + rxd = &sc->kr_cdata.kr_rxdesc[idx]; + if (rxd->rx_m != NULL) { + bus_dmamap_sync(sc->kr_cdata.kr_rx_tag, rxd->rx_dmamap, + BUS_DMASYNC_POSTREAD); + bus_dmamap_unload(sc->kr_cdata.kr_rx_tag, rxd->rx_dmamap); + } + map = rxd->rx_dmamap; + rxd->rx_dmamap = sc->kr_cdata.kr_rx_sparemap; + sc->kr_cdata.kr_rx_sparemap = map; + bus_dmamap_sync(sc->kr_cdata.kr_rx_tag, rxd->rx_dmamap, + BUS_DMASYNC_PREREAD); + rxd->rx_m = m; + desc = rxd->desc; + desc->kr_ca = segs[0].ds_addr; + desc->kr_ctl |= KR_DMASIZE(segs[0].ds_len); + rxd->saved_ca = desc->kr_ca ; + rxd->saved_ctl = desc->kr_ctl ; + + return (0); +} + +static __inline void +kr_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 +kr_tx(struct kr_softc *sc) +{ + struct kr_txdesc *txd; + struct kr_desc *cur_tx; + struct ifnet *ifp; + uint32_t ctl, devcs; + int cons, prod; + + KR_LOCK_ASSERT(sc); + + cons = sc->kr_cdata.kr_tx_cons; + prod = sc->kr_cdata.kr_tx_prod; + if (cons == prod) + return; + + bus_dmamap_sync(sc->kr_cdata.kr_tx_ring_tag, + sc->kr_cdata.kr_tx_ring_map, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + + ifp = sc->kr_ifp; + /* + * Go through our tx list and free mbufs for those + * frames that have been transmitted. + */ + for (; cons != prod; KR_INC(cons, KR_TX_RING_CNT)) { + cur_tx = &sc->kr_rdata.kr_tx_ring[cons]; + ctl = cur_tx->kr_ctl; + devcs = cur_tx->kr_devcs; + /* Check if descriptor has "finished" flag */ + if ((ctl & KR_CTL_F) == 0) + break; + + sc->kr_cdata.kr_tx_cnt--; + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + + txd = &sc->kr_cdata.kr_txdesc[cons]; + + if (devcs & KR_DMATX_DEVCS_TOK) + ifp->if_opackets++; + else { + ifp->if_oerrors++; + /* collisions: medium busy, late collision */ + if ((devcs & KR_DMATX_DEVCS_EC) || + (devcs & KR_DMATX_DEVCS_LC)) + ifp->if_collisions++; + } + + bus_dmamap_sync(sc->kr_cdata.kr_tx_tag, txd->tx_dmamap, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->kr_cdata.kr_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->kr_ctl = KR_CTL_IOF; + cur_tx->kr_devcs = 0; + cur_tx->kr_ca = 0; + cur_tx->kr_link = 0; + } + + sc->kr_cdata.kr_tx_cons = cons; + + bus_dmamap_sync(sc->kr_cdata.kr_tx_ring_tag, + sc->kr_cdata.kr_tx_ring_map, BUS_DMASYNC_PREWRITE); +} + + +static void +kr_rx(struct kr_softc *sc) +{ + struct kr_rxdesc *rxd; + struct ifnet *ifp = sc->kr_ifp; + int cons, prog, packet_len, count, error; + struct kr_desc *cur_rx; + struct mbuf *m; + + KR_LOCK_ASSERT(sc); + + cons = sc->kr_cdata.kr_rx_cons; + + bus_dmamap_sync(sc->kr_cdata.kr_rx_ring_tag, + sc->kr_cdata.kr_rx_ring_map, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + + for (prog = 0; prog < KR_RX_RING_CNT; KR_INC(cons, KR_RX_RING_CNT)) { + cur_rx = &sc->kr_rdata.kr_rx_ring[cons]; + rxd = &sc->kr_cdata.kr_rxdesc[cons]; + m = rxd->rx_m; + + if ((cur_rx->kr_ctl & KR_CTL_D) == 0) + break; + + prog++; + + packet_len = KR_PKTSIZE(cur_rx->kr_devcs); + count = m->m_len - KR_DMASIZE(cur_rx->kr_ctl); + /* Assume it's error */ + error = 1; + + if (packet_len != count) + ifp->if_ierrors++; + else if (count < 64) + ifp->if_ierrors++; + else if ((cur_rx->kr_devcs & KR_DMARX_DEVCS_LD) == 0) + ifp->if_ierrors++; + else if ((cur_rx->kr_devcs & KR_DMARX_DEVCS_ROK) != 0) { + error = 0; + bus_dmamap_sync(sc->kr_cdata.kr_rx_tag, rxd->rx_dmamap, + BUS_DMASYNC_PREREAD); + m = rxd->rx_m; + kr_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++; + + KR_UNLOCK(sc); + (*ifp->if_input)(ifp, m); + KR_LOCK(sc); + } + + if (error) { + /* Restore CONTROL and CA values, reset DEVCS */ + cur_rx->kr_ctl = rxd->saved_ctl; + cur_rx->kr_ca = rxd->saved_ca; + cur_rx->kr_devcs = 0; + } + else { + /* Reinit descriptor */ + cur_rx->kr_ctl = KR_CTL_IOD; + if (cons == KR_RX_RING_CNT - 1) + cur_rx->kr_ctl |= KR_CTL_COD; + cur_rx->kr_devcs = 0; + cur_rx->kr_ca = 0; + if (kr_newbuf(sc, cons) != 0) { + device_printf(sc->kr_dev, + "Failed to allocate buffer\n"); + break; + } + } + + bus_dmamap_sync(sc->kr_cdata.kr_rx_ring_tag, + sc->kr_cdata.kr_rx_ring_map, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + + } + + if (prog > 0) { + sc->kr_cdata.kr_rx_cons = cons; + + bus_dmamap_sync(sc->kr_cdata.kr_rx_ring_tag, + sc->kr_cdata.kr_rx_ring_map, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + } +} + +static void +kr_rx_intr(void *arg) +{ + struct kr_softc *sc = arg; + uint32_t status; + + KR_LOCK(sc); + + /* mask out interrupts */ + KR_DMA_SETBITS_REG(KR_DMA_RXCHAN, DMA_SM, + DMA_SM_D | DMA_SM_H | DMA_SM_E); + + status = KR_DMA_READ_REG(KR_DMA_RXCHAN, DMA_S); + if (status & (DMA_S_D | DMA_S_E | DMA_S_H)) { + kr_rx(sc); + + if (status & DMA_S_E) + device_printf(sc->kr_dev, "RX DMA error\n"); + } + + /* Reread status */ + status = KR_DMA_READ_REG(KR_DMA_RXCHAN, DMA_S); + + /* restart DMA RX if it has been halted */ + if (status & DMA_S_H) { + KR_DMA_WRITE_REG(KR_DMA_RXCHAN, DMA_DPTR, + KR_RX_RING_ADDR(sc, sc->kr_cdata.kr_rx_cons)); + } + + KR_DMA_WRITE_REG(KR_DMA_RXCHAN, DMA_S, ~status); + + /* Enable F, H, E interrupts */ + KR_DMA_CLEARBITS_REG(KR_DMA_RXCHAN, DMA_SM, + DMA_SM_D | DMA_SM_H | DMA_SM_E); + + KR_UNLOCK(sc); +} + +static void +kr_tx_intr(void *arg) +{ + struct kr_softc *sc = arg; + uint32_t status; + + KR_LOCK(sc); + + /* mask out interrupts */ + KR_DMA_SETBITS_REG(KR_DMA_TXCHAN, DMA_SM, + DMA_SM_F | DMA_SM_E); + + status = KR_DMA_READ_REG(KR_DMA_TXCHAN, DMA_S); + if (status & (DMA_S_F | DMA_S_E)) { + kr_tx(sc); + if (status & DMA_S_E) + device_printf(sc->kr_dev, "DMA error\n"); + } + + KR_DMA_WRITE_REG(KR_DMA_TXCHAN, DMA_S, ~status); + + /* Enable F, E interrupts */ + KR_DMA_CLEARBITS_REG(KR_DMA_TXCHAN, DMA_SM, + DMA_SM_F | DMA_SM_E); + + KR_UNLOCK(sc); + +} + +static void +kr_rx_und_intr(void *arg) +{ + + panic("interrupt: %s\n", __func__); +} + +static void +kr_tx_ovr_intr(void *arg) +{ + + panic("interrupt: %s\n", __func__); +} + +static void +kr_tick(void *xsc) +{ + struct kr_softc *sc = xsc; + struct mii_data *mii; + + KR_LOCK_ASSERT(sc); + + mii = device_get_softc(sc->kr_miibus); + mii_tick(mii); + callout_reset(&sc->kr_stat_callout, hz, kr_tick, sc); +} diff --git a/sys/mips/idt/if_krreg.h b/sys/mips/idt/if_krreg.h new file mode 100644 index 0000000..b20900a --- /dev/null +++ b/sys/mips/idt/if_krreg.h @@ -0,0 +1,284 @@ +/*- + * Copyright (C) 2007 + * 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 ``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. + * + * $FreeBSD$ + * + */ + +#ifndef __IF_KRREG_H__ +#define __IF_KRREG_H__ + +#define KR_ETHINTFC 0x0000 /* Ethernet interface control */ +#define ETH_INTFC_EN 0x0001 +#define ETH_INTFC_RIP 0x0004 +#define ETH_INTFC_EN 0x0001 +#define KR_ETHFIFOTT 0x0004 /* Ethernet FIFO transmit threshold */ +#define KR_ETHARC 0x0008 /* Ethernet address recognition control */ +#define KR_ETHHASH0 0x000C /* Ethernet hash table 0 */ +#define KR_ETHHASH1 0x0010 /* Ethernet hash table 1 */ +#define KR_ETHPFS 0x0024 /* Ethernet pause frame status */ +#define KR_ETHMCP 0x0028 /* Ethernet management clock prescalar */ +#define KR_ETHSAL0 0x0100 /* Ethernet station address 0 low */ +#define KR_ETHSAH0 0x0104 /* Ethernet station address 0 high */ +#define KR_ETHSAL1 0x0108 /* Ethernet station address 1 low */ +#define KR_ETHSAH1 0x010C /* Ethernet station address 1 high */ +#define KR_ETHSAL2 0x0110 /* Ethernet station address 2 low */ +#define KR_ETHSAH2 0x0114 /* Ethernet station address 2 high */ +#define KR_ETHSAL3 0x0118 /* Ethernet station address 3 low */ +#define KR_ETHSAH3 0x011C /* Ethernet station address 3 high */ +#define KR_ETHRBC 0x0120 /* Ethernet receive byte count */ +#define KR_ETHRPC 0x0124 /* Ethernet receive packet count */ +#define KR_ETHRUPC 0x0128 /* Ethernet receive undersized packet cnt */ +#define KR_ETHRFC 0x012C /* Ethernet receive fragment count */ +#define KR_ETHTBC 0x0130 /* Ethernet transmit byte count */ +#define KR_ETHGPF 0x0134 /* Ethernet generate pause frame */ +#define KR_ETHMAC1 0x0200 /* Ethernet MAC configuration 1 */ +#define KR_ETH_MAC1_RE 0x01 +#define KR_ETH_MAC1_PAF 0x02 +#define KR_ETH_MAC1_MR 0x80 +#define KR_ETHMAC2 0x0204 /* Ethernet MAC configuration 2 */ +#define KR_ETH_MAC2_FD 0x01 +#define KR_ETH_MAC2_FLC 0x02 +#define KR_ETH_MAC2_HFE 0x04 +#define KR_ETH_MAC2_DC 0x08 +#define KR_ETH_MAC2_CEN 0x10 +#define KR_ETH_MAC2_PEN 0x20 +#define KR_ETH_MAC2_VPE 0x08 +#define KR_ETHIPGT 0x0208 /* Ethernet back-to-back inter-packet gap */ +#define KR_ETHIPGR 0x020C /* Ethernet non back-to-back inter-packet gap */ +#define KR_ETHCLRT 0x0210 /* Ethernet collision window retry */ +#define KR_ETHMAXF 0x0214 /* Ethernet maximum frame length */ +#define KR_ETHMTEST 0x021C /* Ethernet MAC test */ +#define KR_MIIMCFG 0x0220 /* MII management configuration */ +#define KR_MIIMCFG_R 0x8000 +#define KR_MIIMCMD 0x0224 /* MII management command */ +#define KR_MIIMCMD_RD 0x01 +#define KR_MIIMCMD_SCN 0x02 +#define KR_MIIMADDR 0x0228 /* MII management address */ +#define KR_MIIMWTD 0x022C /* MII management write data */ +#define KR_MIIMRDD 0x0230 /* MII management read data */ +#define KR_MIIMIND 0x0234 /* MII management indicators */ +#define KR_MIIMIND_BSY 0x1 +#define KR_MIIMIND_SCN 0x2 +#define KR_MIIMIND_NV 0x4 +#define KR_ETHCFSA0 0x0240 /* Ethernet control frame station address 0 */ +#define KR_ETHCFSA1 0x0244 /* Ethernet control frame station address 1 */ +#define KR_ETHCFSA2 0x0248 /* Ethernet control frame station address 2 */ + +#define KR_ETHIPGT_HALF_DUPLEX 0x12 +#define KR_ETHIPGT_FULL_DUPLEX 0x15 + +#define KR_TIMEOUT 0xf000 +#define KR_MII_TIMEOUT 0xf000 + +#define KR_RX_IRQ 40 +#define KR_TX_IRQ 41 +#define KR_RX_UND_IRQ 42 +#define KR_TX_OVR_IRQ 43 +#define RC32434_DMA_BASE_ADDR MIPS_PHYS_TO_KSEG1(0x18040000) +#define DMA_C 0x00 +#define DMA_C_R 0x01 +#define DMA_C_ABORT 0x10 +#define DMA_S 0x04 +#define DMA_S_F 0x01 +#define DMA_S_D 0x02 +#define DMA_S_C 0x04 +#define DMA_S_E 0x08 +#define DMA_S_H 0x10 +#define DMA_SM 0x08 +#define DMA_SM_F 0x01 +#define DMA_SM_D 0x02 +#define DMA_SM_C 0x04 +#define DMA_SM_E 0x08 +#define DMA_SM_H 0x10 +#define DMA_DPTR 0x0C +#define DMA_NDPTR 0x10 + +#define RC32434_DMA_CHAN_SIZE 0x14 +#define KR_DMA_RXCHAN 0 +#define KR_DMA_TXCHAN 1 + +#define KR_DMA_READ_REG(chan, reg) \ + (*(volatile uint32_t *) \ + (RC32434_DMA_BASE_ADDR + chan * RC32434_DMA_CHAN_SIZE + reg)) + +#define KR_DMA_WRITE_REG(chan, reg, val) \ + ((*(volatile uint32_t *) \ + (RC32434_DMA_BASE_ADDR + chan * RC32434_DMA_CHAN_SIZE + reg)) = val) + +#define KR_DMA_SETBITS_REG(chan, reg, bits) \ + KR_DMA_WRITE_REG((chan), (reg), KR_DMA_READ_REG((chan), (reg)) | (bits)) + +#define KR_DMA_CLEARBITS_REG(chan, reg, bits) \ + KR_DMA_WRITE_REG((chan), (reg), \ + KR_DMA_READ_REG((chan), (reg)) & ~(bits)) + +struct kr_desc { + uint32_t kr_ctl; + uint32_t kr_ca; + uint32_t kr_devcs; + uint32_t kr_link; +}; + + +#define KR_DMASIZE(len) ((len) & ((1 << 18)-1)) +#define KR_PKTSIZE(len) ((len & 0xffff0000) >> 16) + +#define KR_CTL_COF 0x02000000 +#define KR_CTL_COD 0x04000000 +#define KR_CTL_IOF 0x08000000 +#define KR_CTL_IOD 0x10000000 +#define KR_CTL_T 0x20000000 +#define KR_CTL_D 0x40000000 +#define KR_CTL_F 0x80000000 + +#define KR_DMARX_DEVCS_RSV 0x00000001 +#define KR_DMARX_DEVCS_LD 0x00000002 +#define KR_DMARX_DEVCS_ROK 0x00000004 +#define KR_DMARX_DEVCS_FM 0x00000008 +#define KR_DMARX_DEVCS_MP 0x00000010 +#define KR_DMARX_DEVCS_BP 0x00000020 +#define KR_DMARX_DEVCS_VLT 0x00000040 +#define KR_DMARX_DEVCS_CF 0x00000080 +#define KR_DMARX_DEVCS_OVR 0x00000100 +#define KR_DMARX_DEVCS_CRC 0x00000200 +#define KR_DMARX_DEVCS_CV 0x00000400 +#define KR_DMARX_DEVCS_DB 0x00000800 +#define KR_DMARX_DEVCS_LE 0x00001000 +#define KR_DMARX_DEVCS_LOR 0x00002000 +#define KR_DMARX_DEVCS_CES 0x00004000 + +#define KR_DMATX_DEVCS_FD 0x00000001 +#define KR_DMATX_DEVCS_LD 0x00000002 +#define KR_DMATX_DEVCS_OEN 0x00000004 +#define KR_DMATX_DEVCS_PEN 0x00000008 +#define KR_DMATX_DEVCS_CEN 0x00000010 +#define KR_DMATX_DEVCS_HEN 0x00000020 +#define KR_DMATX_DEVCS_TOK 0x00000040 +#define KR_DMATX_DEVCS_MP 0x00000080 +#define KR_DMATX_DEVCS_BP 0x00000100 +#define KR_DMATX_DEVCS_UND 0x00000200 +#define KR_DMATX_DEVCS_OF 0x00000400 +#define KR_DMATX_DEVCS_ED 0x00000800 +#define KR_DMATX_DEVCS_EC 0x00001000 +#define KR_DMATX_DEVCS_LC 0x00002000 +#define KR_DMATX_DEVCS_TD 0x00004000 +#define KR_DMATX_DEVCS_CRC 0x00008000 +#define KR_DMATX_DEVCS_LE 0x00010000 + +#define KR_RX_RING_CNT 128 +#define KR_TX_RING_CNT 128 +#define KR_TX_RING_SIZE sizeof(struct kr_desc) * KR_TX_RING_CNT +#define KR_RX_RING_SIZE sizeof(struct kr_desc) * KR_RX_RING_CNT +#define KR_RING_ALIGN sizeof(struct kr_desc) +#define KR_RX_ALIGN sizeof(uint32_t) +#define KR_MAXFRAGS 8 +#define KR_TX_INTR_THRESH 8 + +#define KR_TX_RING_ADDR(sc, i) \ + ((sc)->kr_rdata.kr_tx_ring_paddr + sizeof(struct kr_desc) * (i)) +#define KR_RX_RING_ADDR(sc, i) \ + ((sc)->kr_rdata.kr_rx_ring_paddr + sizeof(struct kr_desc) * (i)) +#define KR_INC(x,y) (x) = (((x) + 1) % y) + +struct kr_txdesc { + struct mbuf *tx_m; + bus_dmamap_t tx_dmamap; +}; + +struct kr_rxdesc { + struct mbuf *rx_m; + bus_dmamap_t rx_dmamap; + struct kr_desc *desc; + /* Use this values on error instead of allocating new mbuf */ + uint32_t saved_ctl, saved_ca; +}; + +struct kr_chain_data { + bus_dma_tag_t kr_parent_tag; + bus_dma_tag_t kr_tx_tag; + struct kr_txdesc kr_txdesc[KR_TX_RING_CNT]; + bus_dma_tag_t kr_rx_tag; + struct kr_rxdesc kr_rxdesc[KR_RX_RING_CNT]; + bus_dma_tag_t kr_tx_ring_tag; + bus_dma_tag_t kr_rx_ring_tag; + bus_dmamap_t kr_tx_ring_map; + bus_dmamap_t kr_rx_ring_map; + bus_dmamap_t kr_rx_sparemap; + int kr_tx_pkts; + int kr_tx_prod; + int kr_tx_cons; + int kr_tx_cnt; + int kr_rx_cons; +}; + +struct kr_ring_data { + struct kr_desc *kr_rx_ring; + struct kr_desc *kr_tx_ring; + bus_addr_t kr_rx_ring_paddr; + bus_addr_t kr_tx_ring_paddr; +}; + +struct kr_softc { + struct ifnet *kr_ifp; /* interface info */ + bus_space_handle_t kr_bhandle; /* bus space handle */ + bus_space_tag_t kr_btag; /* bus space tag */ + device_t kr_dev; + struct resource *kr_res; + int kr_rid; + struct resource *kr_rx_irq; + void *kr_rx_intrhand; + struct resource *kr_tx_irq; + void *kr_tx_intrhand; + struct resource *kr_rx_und_irq; + void *kr_rx_und_intrhand; + struct resource *kr_tx_ovr_irq; + void *kr_tx_ovr_intrhand; + device_t kr_miibus; + bus_dma_tag_t kr_parent_tag; + bus_dma_tag_t kr_tag; + struct mtx kr_mtx; + struct callout kr_stat_callout; + struct task kr_link_task; + struct kr_chain_data kr_cdata; + struct kr_ring_data kr_rdata; + int kr_link_status; + int kr_detach; +}; + +#define KR_LOCK(_sc) mtx_lock(&(_sc)->kr_mtx) +#define KR_UNLOCK(_sc) mtx_unlock(&(_sc)->kr_mtx) +#define KR_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->kr_mtx, MA_OWNED) + +/* + * register space access macros + */ +#define CSR_WRITE_4(sc, reg, val) \ + bus_space_write_4(sc->kr_btag, sc->kr_bhandle, reg, val) + +#define CSR_READ_4(sc, reg) \ + bus_space_read_4(sc->kr_btag, sc->kr_bhandle, reg) + +#endif /* __IF_KRREG_H__ */ diff --git a/sys/mips/idt/obio.c b/sys/mips/idt/obio.c new file mode 100644 index 0000000..c938f72 --- /dev/null +++ b/sys/mips/idt/obio.c @@ -0,0 +1,514 @@ +/*- + * Copyright (c) 2007, Oleksandr Tymoshenko <gonzo@freebsd.org> + * + * 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/mips32/idt/idtreg.h> +#include <mips/mips32/idt/obiovar.h> + +#define ICU_REG_READ(o) \ + *((volatile uint32_t *)MIPS_PHYS_TO_KSEG1(IDT_BASE_ICU + (o))) +#define ICU_REG_WRITE(o,v) (ICU_REG_READ(o)) = (v) + +#define GPIO_REG_READ(o) \ + *((volatile uint32_t *)MIPS_PHYS_TO_KSEG1(IDT_BASE_GPIO + (o))) +#define GPIO_REG_WRITE(o,v) (GPIO_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 void obio_mask_irq(unsigned int irq) +{ + int ip_bit, mask, mask_register; + + /* mask IRQ */ + mask_register = ICU_IRQ_MASK_REG(irq); + ip_bit = ICU_IP_BIT(irq); + + mask = ICU_REG_READ(mask_register); + ICU_REG_WRITE(mask_register, mask | ip_bit); +} + +static void obio_unmask_irq(unsigned int irq) +{ + int ip_bit, mask, mask_register; + + /* unmask IRQ */ + mask_register = ICU_IRQ_MASK_REG(irq); + ip_bit = ICU_IP_BIT(irq); + + mask = ICU_REG_READ(mask_register); + ICU_REG_WRITE(mask_register, mask & ~ip_bit); +} + +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, irq; + + 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, IRQ_BASE, IRQ_END) != 0) + panic("obio_attach: failed to set up IRQ rman"); + + /* Hook up our interrupt handlers. We should handle IRQ0..IRQ4*/ + for(irq = 0; irq < 5; irq++) { + if ((sc->sc_irq[irq] = bus_alloc_resource(dev, SYS_RES_IRQ, + &rid, irq, irq, 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[irq], INTR_TYPE_MISC, + obio_intr, NULL, sc, &sc->sc_ih[irq]))) { + 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 * +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); + 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) +{ + + /* XXX: should we mask/unmask IRQ here? */ + return (BUS_ACTIVATE_RESOURCE(device_get_parent(bus), child, + type, rid, r)); +} + +static int +obio_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 +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, ip_bit, error, mask, mask_register; + + 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, + (mask_fn)obio_mask_irq, (mask_fn)obio_unmask_irq, + NULL, NULL, + "obio 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); + + /* unmask IRQ */ + mask_register = ICU_IRQ_MASK_REG(irq); + ip_bit = ICU_IP_BIT(irq); + + mask = ICU_REG_READ(mask_register); + ICU_REG_WRITE(mask_register, mask & ~ip_bit); + + 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 mask_register, mask, ip_bit; + + 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"); + + /* mask IRQ */ + mask_register = ICU_IRQ_MASK_REG(irq); + ip_bit = ICU_IP_BIT(irq); + + mask = ICU_REG_READ(mask_register); + ICU_REG_WRITE(mask_register, mask | ip_bit); + + 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; + struct intr_handler *ih; + uint32_t irqstat, ipend, imask, xpend; + int irq, thread, group, i, ret; + + irqstat = 0; + irq = 0; + for (group = 2; group <= 6; group++) { + ipend = ICU_REG_READ(ICU_GROUP_IPEND_REG(group)); + imask = ICU_REG_READ(ICU_GROUP_MASK_REG(group)); + xpend = ipend; + ipend &= ~imask; + + while ((i = fls(xpend)) != 0) { + xpend &= ~(1 << (i - 1)); + irq = IP_IRQ(group, i - 1); + } + + while ((i = fls(ipend)) != 0) { + ipend &= ~(1 << (i - 1)); + irq = IP_IRQ(group, i - 1); + event = sc->sc_eventstab[irq]; + thread = 0; +#ifndef INTR_FILTER + obio_mask_irq(irq); +#endif + if (!event || TAILQ_EMPTY(&event->ie_handlers)) { +#ifdef INTR_FILTER + obio_unmask_irq(irq); +#endif + continue; + } + +#ifdef INTR_FILTER + /* TODO: frame instead of NULL? */ + intr_event_handle(event, NULL); + /* XXX: Log stray IRQs */ +#else + /* Execute fast handlers. */ + TAILQ_FOREACH(ih, &event->ie_handlers, + ih_next) { + if (ih->ih_filter == NULL) + thread = 1; + else + ret = ih->ih_filter(ih->ih_argument); + /* + * Wrapper handler special case: see + * intr_execute_handlers() in + * i386/intr_machdep.c + */ + if (!thread) { + if (ret == FILTER_SCHEDULE_THREAD) + thread = 1; + } + } + + /* Schedule thread if needed. */ + if (thread) + intr_event_schedule_thread(event); + else + obio_unmask_irq(irq); + } + } +#endif +#if 0 + ipend = ICU_REG_READ(ICU_IPEND2); + printf("ipend2 = %08x!\n", ipend); + + ipend = ICU_REG_READ(ICU_IPEND3); + printf("ipend3 = %08x!\n", ipend); + + ipend = ICU_REG_READ(ICU_IPEND4); + printf("ipend4 = %08x!\n", ipend); + ipend = ICU_REG_READ(ICU_IPEND5); + printf("ipend5 = %08x!\n", ipend); + + ipend = ICU_REG_READ(ICU_IPEND6); + printf("ipend6 = %08x!\n", ipend); +#endif + while (irqstat != 0) { + if ((irqstat & 1) == 1) { + } + + 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/idt/obiovar.h b/sys/mips/idt/obiovar.h new file mode 100644 index 0000000..526efac --- /dev/null +++ b/sys/mips/idt/obiovar.h @@ -0,0 +1,67 @@ +/* $NetBSD: obiovar.h,v 1.4 2003/06/16 17:40:53 thorpej Exp $ */ + +/*- + * Copyright (c) 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. + * + * $FreeBSD$ + * + */ + +#ifndef _ADM5120_OBIOVAR_H_ +#define _ADM5120_OBIOVAR_H_ + +#include <sys/rman.h> + +/* Number of controller's IRQs */ +#define NIRQS 32*5 + +/* Number of CPU IRQ lines */ +#define MIPS_IRQS 5 + +#define OBIO_MEM_START 0x18000000L +#define OBIO_MEM_SIZE 0x200000 + +struct obio_softc { + struct rman oba_mem_rman; + struct rman oba_irq_rman; + struct intr_event *sc_eventstab[NIRQS]; /* IRQ events structs */ + struct resource *sc_irq[MIPS_IRQS]; /* IRQ resource */ + void *sc_ih[MIPS_IRQS]; /* interrupt cookie */ +}; + +struct obio_ivar { + struct resource_list resources; +}; + +#endif /* _ADM5120_OBIOVAR_H_ */ diff --git a/sys/mips/idt/std.idt b/sys/mips/idt/std.idt new file mode 100644 index 0000000..fa067e7 --- /dev/null +++ b/sys/mips/idt/std.idt @@ -0,0 +1,5 @@ +# $FreeBSD$ +# Standard include file for IDT + +files "../mips32/idt/files.idt" +options ISA_MIPS32 diff --git a/sys/mips/idt/uart_bus_rc32434.c b/sys/mips/idt/uart_bus_rc32434.c new file mode 100644 index 0000000..0626b52 --- /dev/null +++ b/sys/mips/idt/uart_bus_rc32434.c @@ -0,0 +1,100 @@ +/*- + * 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. + */ + +/* + * XXXMIPS: This file is hacked from arm/... . XXXMIPS here means this file is + * experimental and was written for MIPS32 port. + */ +#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 <mips/mips32/idt/idtreg.h> + +#include <dev/pci/pcivar.h> + +#include <dev/uart/uart.h> +#include <dev/uart/uart_bus.h> +#include <dev/uart/uart_cpu.h> + +#include <dev/ic/ns16550.h> + +#include "uart_if.h" + +static int uart_rc32434_probe(device_t dev); + +extern struct uart_class uart_rc32434_uart_class; + +static device_method_t uart_rc32434_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, uart_rc32434_probe), + DEVMETHOD(device_attach, uart_bus_attach), + DEVMETHOD(device_detach, uart_bus_detach), + { 0, 0 } +}; + +static driver_t uart_rc32434_driver = { + uart_driver_name, + uart_rc32434_methods, + sizeof(struct uart_softc), +}; + +extern SLIST_HEAD(uart_devinfo_list, uart_devinfo) uart_sysdevs; + +static int +uart_rc32434_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)); + sc->sc_sysdev->bas.regshft = 2; + sc->sc_sysdev->bas.bst = 0; + sc->sc_sysdev->bas.bsh = MIPS_PHYS_TO_KSEG1(IDT_BASE_UART0); + sc->sc_bas.regshft = 2; + sc->sc_bas.bst = 0; + sc->sc_bas.bsh = MIPS_PHYS_TO_KSEG1(IDT_BASE_UART0); + + return (uart_bus_probe(dev, 2, 330000000UL/2, 0, 0)); +} + +DRIVER_MODULE(uart, obio, uart_rc32434_driver, uart_devclass, 0, 0); diff --git a/sys/mips/idt/uart_cpu_rc32434.c b/sys/mips/idt/uart_cpu_rc32434.c new file mode 100644 index 0000000..6bbc5bd --- /dev/null +++ b/sys/mips/idt/uart_cpu_rc32434.c @@ -0,0 +1,85 @@ +/*- + * 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. + */ +/* + * XXXMIPS: This file is hacked from arm/... . XXXMIPS here means this file is + * experimental and was written for MIPS32 port. + */ +#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> + +extern struct uart_class uart_rc32434_uart_class; +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) +{ + uint32_t maddr; + + if (resource_int_value("uart", 0, "maddr", &maddr) != 0 || + maddr == 0) + return (ENXIO); + + /* Got it. Fill in the instance and return it. */ + di->ops = uart_getops(&uart_ns8250_class); + di->bas.chan = 0; + di->bas.bst = 0; + di->bas.regshft = 2; + di->bas.rclk = 330000000UL/2; /* IPbus clock */ + di->baudrate = 115200; + di->databits = 8; + di->stopbits = 1; + di->parity = UART_PARITY_NONE; + uart_bus_space_io = 0; + uart_bus_space_mem = 0; + di->bas.bsh = MIPS_PHYS_TO_KSEG1(maddr); + return (0); +} |