aboutsummaryrefslogtreecommitdiff
path: root/sys/mips/cavium
diff options
context:
space:
mode:
authorJayachandran C. <jchandra@FreeBSD.org>2010-11-27 12:26:40 +0000
committerJayachandran C. <jchandra@FreeBSD.org>2010-11-27 12:26:40 +0000
commit43f6e368b0a5523c2727da0b0248172b488b758e (patch)
treefd7cffbdc745cef2675b66c7c0b61767483a2475 /sys/mips/cavium
parent30409a7564cd898dd84a0f5835cf3931705d01c0 (diff)
downloadsrc-43f6e368b0a5523c2727da0b0248172b488b758e.tar.gz
src-43f6e368b0a5523c2727da0b0248172b488b758e.zip
Merge MIPS platform support to 8-STABLE.
This commit merges the MIPS platform changes that was now stable in -CURRENT into 8-STABLE. The MIPS changesets are too many (~400) to list here. But the changesets merged in this commit that affect other platforms are summarized below: r204635 : (changes to sys/dev/hwpmc, lib/libpmc, sys/sys/pmc.h) Add support for hwpmc(4) on the MIPS 24K, 32 bit, embedded processor. r205845: (changes to sys/modules/Makefile) Fix for building modules on mips and arm. r204031: (changes to sys/kern/link_elf_obj.c) printf fix, as part of kernel module support for MIPS. r206404: (changes to sys/arm/include/bus.h) Add BUS_SPACE_UNRESTRICTED and define it to be ~0, just like all the other platforms - for arm and mips. r206819: (changes to sys/vm/) Add VMFS_TLB_ALIGNED_SPACE option and kmem_alloc_nofault_space(), which is used to allocate kernel stack address on MIPS. r208165, r211087: (sys/kern/subr_smp.c, sys/kern/sched_ule.c) Enable ULE scheduler for MIPS, Fix for an issue in SMP when 32 cpus are enabled. r208659: (sys/{ia64/ia64,mips/mips,sun4v/sun4v}/pmap.c) Simplify the inner loop of get_pv_entry() r208794: (changes to sys/vm/) Make vm_contig_grow_cache() extern, and use it when vm_phys_alloc_contig() fails to allocate MIPS page table pages. r210327: (changes to sys/vm/) Support for MIPS page table page allocation. Add a new function 'vm_page_t vm_page_alloc_freelist(int flind, int order, int req)' to vm/vm_page.c to allocate a page from a specified freelist, and other related changes. Reviewed by: alc(vm changes only) Approved by: kib(re), alc(vm), imp(mips), jmallett(mips), gnn(mips pmc)
Notes
Notes: svn path=/stable/8/; revision=215938
Diffstat (limited to 'sys/mips/cavium')
-rw-r--r--sys/mips/cavium/asm_octeon.S66
-rw-r--r--sys/mips/cavium/ciu.c374
-rw-r--r--sys/mips/cavium/cryptocteon/cavium_crypto.c2222
-rw-r--r--sys/mips/cavium/cryptocteon/cryptocteon.c526
-rw-r--r--sys/mips/cavium/cryptocteon/cryptocteonvar.h94
-rw-r--r--sys/mips/cavium/cvmx_config.h194
-rw-r--r--sys/mips/cavium/dev/rgmii/octeon_fau.c42
-rw-r--r--sys/mips/cavium/dev/rgmii/octeon_fau.h220
-rw-r--r--sys/mips/cavium/dev/rgmii/octeon_fpa.c230
-rw-r--r--sys/mips/cavium/dev/rgmii/octeon_fpa.h259
-rw-r--r--sys/mips/cavium/dev/rgmii/octeon_ipd.c148
-rw-r--r--sys/mips/cavium/dev/rgmii/octeon_ipd.h204
-rw-r--r--sys/mips/cavium/dev/rgmii/octeon_pip.h219
-rw-r--r--sys/mips/cavium/dev/rgmii/octeon_pko.c378
-rw-r--r--sys/mips/cavium/dev/rgmii/octeon_pko.h332
-rw-r--r--sys/mips/cavium/dev/rgmii/octeon_rgmx.c2328
-rw-r--r--sys/mips/cavium/dev/rgmii/octeon_rgmx.h630
-rw-r--r--sys/mips/cavium/driveid.h259
-rw-r--r--sys/mips/cavium/files.octeon169
-rw-r--r--sys/mips/cavium/obio.c207
-rw-r--r--sys/mips/cavium/obiovar.h55
-rw-r--r--sys/mips/cavium/octe/cavium-ethernet.h99
-rw-r--r--sys/mips/cavium/octe/ethernet-common.c292
-rw-r--r--sys/mips/cavium/octe/ethernet-common.h52
-rw-r--r--sys/mips/cavium/octe/ethernet-defines.h101
-rw-r--r--sys/mips/cavium/octe/ethernet-headers.h50
-rw-r--r--sys/mips/cavium/octe/ethernet-mdio.c141
-rw-r--r--sys/mips/cavium/octe/ethernet-mdio.h40
-rw-r--r--sys/mips/cavium/octe/ethernet-mem.c207
-rw-r--r--sys/mips/cavium/octe/ethernet-mem.h33
-rw-r--r--sys/mips/cavium/octe/ethernet-mv88e61xx.c127
-rw-r--r--sys/mips/cavium/octe/ethernet-mv88e61xx.h34
-rw-r--r--sys/mips/cavium/octe/ethernet-rgmii.c340
-rw-r--r--sys/mips/cavium/octe/ethernet-rx.c416
-rw-r--r--sys/mips/cavium/octe/ethernet-rx.h37
-rw-r--r--sys/mips/cavium/octe/ethernet-sgmii.c117
-rw-r--r--sys/mips/cavium/octe/ethernet-spi.c309
-rw-r--r--sys/mips/cavium/octe/ethernet-tx.c402
-rw-r--r--sys/mips/cavium/octe/ethernet-tx.h34
-rw-r--r--sys/mips/cavium/octe/ethernet-util.h84
-rw-r--r--sys/mips/cavium/octe/ethernet-xaui.c116
-rw-r--r--sys/mips/cavium/octe/ethernet.c594
-rw-r--r--sys/mips/cavium/octe/mv88e61xxphy.c630
-rw-r--r--sys/mips/cavium/octe/mv88e61xxphyreg.h149
-rw-r--r--sys/mips/cavium/octe/octe.c522
-rw-r--r--sys/mips/cavium/octe/octebus.c123
-rw-r--r--sys/mips/cavium/octe/octebusvar.h41
-rw-r--r--sys/mips/cavium/octe/wrapper-cvmx-includes.h50
-rw-r--r--sys/mips/cavium/octeon_ds1337.c213
-rw-r--r--sys/mips/cavium/octeon_ebt3000_cf.c704
-rw-r--r--sys/mips/cavium/octeon_ebt3000_cf.h35
-rw-r--r--sys/mips/cavium/octeon_machdep.c566
-rw-r--r--sys/mips/cavium/octeon_mp.c127
-rw-r--r--sys/mips/cavium/octeon_pcmap_regs.h300
-rw-r--r--sys/mips/cavium/octeon_rnd.c137
-rw-r--r--sys/mips/cavium/octeon_rtc.c130
-rw-r--r--sys/mips/cavium/octeonreg.h247
-rw-r--r--sys/mips/cavium/octopci.c884
-rw-r--r--sys/mips/cavium/octopci_bus_space.c583
-rw-r--r--sys/mips/cavium/octopcireg.h105
-rw-r--r--sys/mips/cavium/octopcivar.h35
-rw-r--r--sys/mips/cavium/std.octeon17
-rw-r--r--sys/mips/cavium/uart_bus_octeonusart.c122
-rw-r--r--sys/mips/cavium/uart_cpu_octeonusart.c174
-rw-r--r--sys/mips/cavium/uart_dev_oct16550.c836
-rw-r--r--sys/mips/cavium/usb/octusb.c1922
-rw-r--r--sys/mips/cavium/usb/octusb.h147
-rw-r--r--sys/mips/cavium/usb/octusb_octeon.c223
68 files changed, 21693 insertions, 0 deletions
diff --git a/sys/mips/cavium/asm_octeon.S b/sys/mips/cavium/asm_octeon.S
new file mode 100644
index 000000000000..94ac875a1996
--- /dev/null
+++ b/sys/mips/cavium/asm_octeon.S
@@ -0,0 +1,66 @@
+/*-
+ * Copyright (c) 2004-2010 Juli Mallett <jmallett@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.
+ *
+ * $FreeBSD$
+ */
+
+#include <machine/asm.h>
+
+ .set noreorder
+
+#ifdef SMP
+/*
+ * This function must be implemented in assembly because it is called early
+ * in AP boot without a valid stack.
+ */
+LEAF(platform_processor_id)
+ .set push
+ .set mips32r2
+ jr ra
+ rdhwr v0, $0
+ .set pop
+END(platform_processor_id)
+
+/*
+ * Called on APs to wait until they are told to launch.
+ */
+LEAF(octeon_ap_wait)
+ jal platform_processor_id
+ nop
+
+1: ll t0, octeon_ap_boot
+ bne v0, t0, 1b
+ nop
+
+ move t0, zero
+ sc t0, octeon_ap_boot
+
+ beqz t0, 1b
+ nop
+
+ j mpentry
+ nop
+END(octeon_ap_wait)
+#endif
diff --git a/sys/mips/cavium/ciu.c b/sys/mips/cavium/ciu.c
new file mode 100644
index 000000000000..eb58928ca958
--- /dev/null
+++ b/sys/mips/cavium/ciu.c
@@ -0,0 +1,374 @@
+/*-
+ * Copyright (c) 2010 Juli Mallett <jmallett@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.
+ *
+ * $FreeBSD$
+ */
+
+#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 <machine/intr_machdep.h>
+
+#include <contrib/octeon-sdk/cvmx.h>
+#include <contrib/octeon-sdk/cvmx-interrupt.h>
+
+/*
+ * This bus sits between devices/buses and nexus and handles CIU interrupts
+ * and passes everything else through. It should really be a nexus subclass
+ * or something, but for now this will be sufficient.
+ */
+
+#define CIU_IRQ_HARD (0)
+
+#define CIU_IRQ_EN0_BEGIN CVMX_IRQ_WORKQ0
+#define CIU_IRQ_EN0_END CVMX_IRQ_BOOTDMA
+#define CIU_IRQ_EN0_COUNT ((CIU_IRQ_EN0_END - CIU_IRQ_EN0_BEGIN) + 1)
+
+#define CIU_IRQ_EN1_BEGIN CVMX_IRQ_WDOG0
+#define CIU_IRQ_EN1_END CVMX_IRQ_WDOG15
+#define CIU_IRQ_EN1_COUNT ((CIU_IRQ_EN1_END - CIU_IRQ_EN1_BEGIN) + 1)
+
+struct ciu_softc {
+ struct rman irq_rman;
+ struct resource *ciu_irq;
+};
+
+static mips_intrcnt_t ciu_en0_intrcnt[CIU_IRQ_EN0_COUNT];
+static mips_intrcnt_t ciu_en1_intrcnt[CIU_IRQ_EN1_COUNT];
+
+static struct intr_event *ciu_en0_intr_events[CIU_IRQ_EN0_COUNT];
+static struct intr_event *ciu_en1_intr_events[CIU_IRQ_EN1_COUNT];
+
+static int ciu_probe(device_t);
+static int ciu_attach(device_t);
+static struct resource *ciu_alloc_resource(device_t, device_t, int, int *,
+ u_long, u_long, u_long, u_int);
+static int ciu_setup_intr(device_t, device_t, struct resource *,
+ int, driver_filter_t *, driver_intr_t *,
+ void *, void **);
+static int ciu_teardown_intr(device_t, device_t,
+ struct resource *, void *);
+static void ciu_hinted_child(device_t, const char *, int);
+
+static void ciu_en0_intr_mask(void *);
+static void ciu_en0_intr_unmask(void *);
+
+static void ciu_en1_intr_mask(void *);
+static void ciu_en1_intr_unmask(void *);
+
+static int ciu_intr(void *);
+
+static int
+ciu_probe(device_t dev)
+{
+ if (device_get_unit(dev) != 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Cavium Octeon Central Interrupt Unit");
+ return (0);
+}
+
+static int
+ciu_attach(device_t dev)
+{
+ char name[MAXCOMLEN + 1];
+ struct ciu_softc *sc;
+ unsigned i;
+ int error;
+ int rid;
+
+ sc = device_get_softc(dev);
+
+ rid = 0;
+ sc->ciu_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, CIU_IRQ_HARD,
+ CIU_IRQ_HARD, 1, RF_ACTIVE);
+ if (sc->ciu_irq == NULL) {
+ device_printf(dev, "could not allocate irq%d\n", CIU_IRQ_HARD);
+ return (ENXIO);
+ }
+
+ error = bus_setup_intr(dev, sc->ciu_irq, INTR_TYPE_MISC, ciu_intr,
+ NULL, sc, NULL);
+ if (error != 0) {
+ device_printf(dev, "bus_setup_intr failed: %d\n", error);
+ return (error);
+ }
+
+ sc->irq_rman.rm_type = RMAN_ARRAY;
+ sc->irq_rman.rm_descr = "CIU IRQ";
+
+ error = rman_init(&sc->irq_rman);
+ if (error != 0)
+ return (error);
+
+ /*
+ * We have two contiguous IRQ regions, use a single rman.
+ */
+ error = rman_manage_region(&sc->irq_rman, CIU_IRQ_EN0_BEGIN,
+ CIU_IRQ_EN1_END);
+ if (error != 0)
+ return (error);
+
+ for (i = 0; i < CIU_IRQ_EN0_COUNT; i++) {
+ snprintf(name, sizeof name, "int%d:", i + CIU_IRQ_EN0_BEGIN);
+ ciu_en0_intrcnt[i] = mips_intrcnt_create(name);
+ }
+
+ for (i = 0; i < CIU_IRQ_EN1_COUNT; i++) {
+ snprintf(name, sizeof name, "int%d:", i + CIU_IRQ_EN1_BEGIN);
+ ciu_en1_intrcnt[i] = mips_intrcnt_create(name);
+ }
+
+ bus_generic_probe(dev);
+ bus_generic_attach(dev);
+
+ return (0);
+}
+
+static struct resource *
+ciu_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 resource *res;
+ struct ciu_softc *sc;
+
+ sc = device_get_softc(bus);
+
+ switch (type) {
+ case SYS_RES_IRQ:
+ break;
+ default:
+ return (bus_alloc_resource(device_get_parent(bus), type, rid,
+ start, end, count, flags));
+ }
+
+ /*
+ * One interrupt at a time for now.
+ */
+ if (start != end)
+ return (NULL);
+
+ res = rman_reserve_resource(&sc->irq_rman, start, end, count, flags,
+ child);
+ if (res != NULL)
+ return (res);
+
+ return (NULL);
+}
+
+static int
+ciu_setup_intr(device_t bus, device_t child, struct resource *res, int flags,
+ driver_filter_t *filter, driver_intr_t *intr, void *arg,
+ void **cookiep)
+{
+ struct intr_event *event, **eventp;
+ void (*mask_func)(void *);
+ void (*unmask_func)(void *);
+ mips_intrcnt_t intrcnt;
+ int error;
+ int irq;
+
+ irq = rman_get_start(res);
+ if (irq <= CIU_IRQ_EN0_END) {
+ eventp = &ciu_en0_intr_events[irq - CIU_IRQ_EN0_BEGIN];
+ intrcnt = ciu_en0_intrcnt[irq - CIU_IRQ_EN0_BEGIN];
+ mask_func = ciu_en0_intr_mask;
+ unmask_func = ciu_en0_intr_unmask;
+ } else {
+ eventp = &ciu_en1_intr_events[irq - CIU_IRQ_EN1_BEGIN];
+ intrcnt = ciu_en1_intrcnt[irq - CIU_IRQ_EN1_BEGIN];
+ mask_func = ciu_en1_intr_mask;
+ unmask_func = ciu_en1_intr_unmask;
+ }
+
+ if ((event = *eventp) == NULL) {
+ error = intr_event_create(eventp, (void *)(uintptr_t)irq, 0,
+ irq, mask_func, unmask_func, NULL, NULL, "int%d", irq);
+ if (error != 0)
+ return (error);
+
+ event = *eventp;
+
+ unmask_func((void *)(uintptr_t)irq);
+ }
+
+ intr_event_add_handler(event, device_get_nameunit(child),
+ filter, intr, arg, intr_priority(flags), flags, cookiep);
+
+ mips_intrcnt_setname(intrcnt, event->ie_fullname);
+
+ return (0);
+}
+
+static int
+ciu_teardown_intr(device_t bus, device_t child, struct resource *res,
+ void *cookie)
+{
+ int error;
+
+ error = intr_event_remove_handler(cookie);
+ if (error != 0)
+ return (error);
+
+ return (0);
+}
+
+static void
+ciu_hinted_child(device_t bus, const char *dname, int dunit)
+{
+ BUS_ADD_CHILD(bus, 0, dname, dunit);
+}
+
+static void
+ciu_en0_intr_mask(void *arg)
+{
+ uint64_t mask;
+ int irq;
+
+ irq = (uintptr_t)arg;
+ mask = cvmx_read_csr(CVMX_CIU_INTX_EN0(cvmx_get_core_num()*2));
+ mask &= ~(1ull << (irq - CIU_IRQ_EN0_BEGIN));
+ cvmx_write_csr(CVMX_CIU_INTX_EN0(cvmx_get_core_num()*2), mask);
+}
+
+static void
+ciu_en0_intr_unmask(void *arg)
+{
+ uint64_t mask;
+ int irq;
+
+ irq = (uintptr_t)arg;
+ mask = cvmx_read_csr(CVMX_CIU_INTX_EN0(cvmx_get_core_num()*2));
+ mask |= 1ull << (irq - CIU_IRQ_EN0_BEGIN);
+ cvmx_write_csr(CVMX_CIU_INTX_EN0(cvmx_get_core_num()*2), mask);
+}
+
+static void
+ciu_en1_intr_mask(void *arg)
+{
+ uint64_t mask;
+ int irq;
+
+ irq = (uintptr_t)arg;
+ mask = cvmx_read_csr(CVMX_CIU_INTX_EN1(cvmx_get_core_num()*2));
+ mask &= ~(1ull << (irq - CIU_IRQ_EN1_BEGIN));
+ cvmx_write_csr(CVMX_CIU_INTX_EN1(cvmx_get_core_num()*2), mask);
+}
+
+static void
+ciu_en1_intr_unmask(void *arg)
+{
+ uint64_t mask;
+ int irq;
+
+ irq = (uintptr_t)arg;
+ mask = cvmx_read_csr(CVMX_CIU_INTX_EN1(cvmx_get_core_num()*2));
+ mask |= 1ull << (irq - CIU_IRQ_EN1_BEGIN);
+ cvmx_write_csr(CVMX_CIU_INTX_EN1(cvmx_get_core_num()*2), mask);
+}
+
+static int
+ciu_intr(void *arg)
+{
+ struct ciu_softc *sc;
+ uint64_t en0_sum, en1_sum;
+ uint64_t en0_mask, en1_mask;
+ int irq_index;
+ int error;
+
+ sc = arg;
+ (void)sc;
+
+ en0_sum = cvmx_read_csr(CVMX_CIU_INTX_SUM0(cvmx_get_core_num()*2));
+ en1_sum = cvmx_read_csr(CVMX_CIU_INT_SUM1);
+
+ en0_mask = cvmx_read_csr(CVMX_CIU_INTX_EN0(cvmx_get_core_num()*2));
+ en1_mask = cvmx_read_csr(CVMX_CIU_INTX_EN1(cvmx_get_core_num()*2));
+
+ en0_sum &= en0_mask;
+ en1_sum &= en1_mask;
+
+ if (en0_sum == 0 && en1_sum == 0)
+ return (FILTER_STRAY);
+
+ irq_index = 0;
+ for (irq_index = 0; en0_sum != 0; irq_index++, en0_sum >>= 1) {
+ if ((en0_sum & 1) == 0)
+ continue;
+
+ mips_intrcnt_inc(ciu_en0_intrcnt[irq_index]);
+
+ error = intr_event_handle(ciu_en0_intr_events[irq_index], NULL);
+ if (error != 0)
+ printf("%s: stray en0 irq%d\n", __func__, irq_index);
+ }
+
+ irq_index = 0;
+ for (irq_index = 0; en1_sum != 0; irq_index++, en1_sum >>= 1) {
+ if ((en1_sum & 1) == 0)
+ continue;
+
+ mips_intrcnt_inc(ciu_en1_intrcnt[irq_index]);
+
+ error = intr_event_handle(ciu_en1_intr_events[irq_index], NULL);
+ if (error != 0)
+ printf("%s: stray en1 irq%d\n", __func__, irq_index);
+ }
+
+ return (FILTER_HANDLED);
+}
+
+static device_method_t ciu_methods[] = {
+ DEVMETHOD(device_probe, ciu_probe),
+ DEVMETHOD(device_attach, ciu_attach),
+
+ DEVMETHOD(bus_alloc_resource, ciu_alloc_resource),
+ DEVMETHOD(bus_activate_resource,bus_generic_activate_resource),
+ DEVMETHOD(bus_setup_intr, ciu_setup_intr),
+ DEVMETHOD(bus_teardown_intr, ciu_teardown_intr),
+
+ DEVMETHOD(bus_add_child, bus_generic_add_child),
+ DEVMETHOD(bus_hinted_child, ciu_hinted_child),
+
+ { 0, 0 }
+};
+
+static driver_t ciu_driver = {
+ "ciu",
+ ciu_methods,
+ sizeof(struct ciu_softc),
+};
+static devclass_t ciu_devclass;
+DRIVER_MODULE(ciu, nexus, ciu_driver, ciu_devclass, 0, 0);
diff --git a/sys/mips/cavium/cryptocteon/cavium_crypto.c b/sys/mips/cavium/cryptocteon/cavium_crypto.c
new file mode 100644
index 000000000000..ff77a61eafbf
--- /dev/null
+++ b/sys/mips/cavium/cryptocteon/cavium_crypto.c
@@ -0,0 +1,2222 @@
+/*
+ * vim:sw=4 ts=8
+ */
+/*
+ * Copyright (c) 2009 David McCullough <david.mccullough@securecomputing.com>
+ *
+ * Copyright (c) 2003-2007 Cavium Networks (support@cavium.com). All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Cavium Networks
+ * 4. Cavium Networks' name may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * This Software, including technical data, may be subject to U.S. export
+ * control laws, including the U.S. Export Administration Act and its
+ * associated regulations, and may be subject to export or import regulations
+ * in other countries. You warrant that You will comply strictly in all
+ * respects with all such regulations and acknowledge that you have the
+ * responsibility to obtain licenses to export, re-export or import the
+ * Software.
+ *
+ * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS" AND
+ * WITH ALL FAULTS AND CAVIUM MAKES NO PROMISES, REPRESENTATIONS OR WARRANTIES,
+ * EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO THE
+ * SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR
+ * DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM
+ * SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE,
+ * MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF
+ * VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR
+ * CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR
+ * PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+*/
+/****************************************************************************/
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/uio.h>
+
+#include <opencrypto/cryptodev.h>
+
+#include <contrib/octeon-sdk/cvmx.h>
+
+#include <mips/cavium/cryptocteon/cryptocteonvar.h>
+
+/****************************************************************************/
+
+#define IOV_INIT(iov, ptr, idx, len) \
+ do { \
+ (idx) = 0; \
+ (ptr) = (iov)[(idx)].iov_base; \
+ (len) = (iov)[(idx)].iov_len; \
+ } while (0)
+
+/*
+ * XXX
+ * It would be better if this were an IOV_READ/IOV_WRITE macro instead so
+ * that we could detect overflow before it happens rather than right after,
+ * which is especially bad since there is usually no IOV_CONSUME after the
+ * final read or write.
+ */
+#define IOV_CONSUME(iov, ptr, idx, len) \
+ do { \
+ if ((len) > sizeof *(ptr)) { \
+ (len) -= sizeof *(ptr); \
+ (ptr)++; \
+ } else { \
+ if ((len) != sizeof *(ptr)) \
+ panic("%s: went past end of iovec.", __func__); \
+ (idx)++; \
+ (ptr) = (iov)[(idx)].iov_base; \
+ (len) = (iov)[(idx)].iov_len; \
+ } \
+ } while (0)
+
+static inline unsigned long octeon_crypto_enable(void)
+{
+ register_t s;
+
+ s = intr_disable();
+ mips_wr_status(mips_rd_status() | MIPS_SR_COP_2_BIT);
+
+ return (s);
+}
+
+static inline void octeon_crypto_disable(register_t s)
+{
+ mips_wr_status(mips_rd_status() & ~MIPS_SR_COP_2_BIT);
+ intr_restore(s);
+}
+
+#define ESP_HEADER_LENGTH 8
+#define DES_CBC_IV_LENGTH 8
+#define AES_CBC_IV_LENGTH 16
+#define ESP_HMAC_LEN 12
+
+#define ESP_HEADER_LENGTH 8
+#define DES_CBC_IV_LENGTH 8
+
+/****************************************************************************/
+
+#define CVM_LOAD_SHA_UNIT(dat, next) { \
+ if (next == 0) { \
+ next = 1; \
+ CVMX_MT_HSH_DAT (dat, 0); \
+ } else if (next == 1) { \
+ next = 2; \
+ CVMX_MT_HSH_DAT (dat, 1); \
+ } else if (next == 2) { \
+ next = 3; \
+ CVMX_MT_HSH_DAT (dat, 2); \
+ } else if (next == 3) { \
+ next = 4; \
+ CVMX_MT_HSH_DAT (dat, 3); \
+ } else if (next == 4) { \
+ next = 5; \
+ CVMX_MT_HSH_DAT (dat, 4); \
+ } else if (next == 5) { \
+ next = 6; \
+ CVMX_MT_HSH_DAT (dat, 5); \
+ } else if (next == 6) { \
+ next = 7; \
+ CVMX_MT_HSH_DAT (dat, 6); \
+ } else { \
+ CVMX_MT_HSH_STARTSHA (dat); \
+ next = 0; \
+ } \
+}
+
+#define CVM_LOAD2_SHA_UNIT(dat1, dat2, next) { \
+ if (next == 0) { \
+ CVMX_MT_HSH_DAT (dat1, 0); \
+ CVMX_MT_HSH_DAT (dat2, 1); \
+ next = 2; \
+ } else if (next == 1) { \
+ CVMX_MT_HSH_DAT (dat1, 1); \
+ CVMX_MT_HSH_DAT (dat2, 2); \
+ next = 3; \
+ } else if (next == 2) { \
+ CVMX_MT_HSH_DAT (dat1, 2); \
+ CVMX_MT_HSH_DAT (dat2, 3); \
+ next = 4; \
+ } else if (next == 3) { \
+ CVMX_MT_HSH_DAT (dat1, 3); \
+ CVMX_MT_HSH_DAT (dat2, 4); \
+ next = 5; \
+ } else if (next == 4) { \
+ CVMX_MT_HSH_DAT (dat1, 4); \
+ CVMX_MT_HSH_DAT (dat2, 5); \
+ next = 6; \
+ } else if (next == 5) { \
+ CVMX_MT_HSH_DAT (dat1, 5); \
+ CVMX_MT_HSH_DAT (dat2, 6); \
+ next = 7; \
+ } else if (next == 6) { \
+ CVMX_MT_HSH_DAT (dat1, 6); \
+ CVMX_MT_HSH_STARTSHA (dat2); \
+ next = 0; \
+ } else { \
+ CVMX_MT_HSH_STARTSHA (dat1); \
+ CVMX_MT_HSH_DAT (dat2, 0); \
+ next = 1; \
+ } \
+}
+
+/****************************************************************************/
+
+#define CVM_LOAD_MD5_UNIT(dat, next) { \
+ if (next == 0) { \
+ next = 1; \
+ CVMX_MT_HSH_DAT (dat, 0); \
+ } else if (next == 1) { \
+ next = 2; \
+ CVMX_MT_HSH_DAT (dat, 1); \
+ } else if (next == 2) { \
+ next = 3; \
+ CVMX_MT_HSH_DAT (dat, 2); \
+ } else if (next == 3) { \
+ next = 4; \
+ CVMX_MT_HSH_DAT (dat, 3); \
+ } else if (next == 4) { \
+ next = 5; \
+ CVMX_MT_HSH_DAT (dat, 4); \
+ } else if (next == 5) { \
+ next = 6; \
+ CVMX_MT_HSH_DAT (dat, 5); \
+ } else if (next == 6) { \
+ next = 7; \
+ CVMX_MT_HSH_DAT (dat, 6); \
+ } else { \
+ CVMX_MT_HSH_STARTMD5 (dat); \
+ next = 0; \
+ } \
+}
+
+#define CVM_LOAD2_MD5_UNIT(dat1, dat2, next) { \
+ if (next == 0) { \
+ CVMX_MT_HSH_DAT (dat1, 0); \
+ CVMX_MT_HSH_DAT (dat2, 1); \
+ next = 2; \
+ } else if (next == 1) { \
+ CVMX_MT_HSH_DAT (dat1, 1); \
+ CVMX_MT_HSH_DAT (dat2, 2); \
+ next = 3; \
+ } else if (next == 2) { \
+ CVMX_MT_HSH_DAT (dat1, 2); \
+ CVMX_MT_HSH_DAT (dat2, 3); \
+ next = 4; \
+ } else if (next == 3) { \
+ CVMX_MT_HSH_DAT (dat1, 3); \
+ CVMX_MT_HSH_DAT (dat2, 4); \
+ next = 5; \
+ } else if (next == 4) { \
+ CVMX_MT_HSH_DAT (dat1, 4); \
+ CVMX_MT_HSH_DAT (dat2, 5); \
+ next = 6; \
+ } else if (next == 5) { \
+ CVMX_MT_HSH_DAT (dat1, 5); \
+ CVMX_MT_HSH_DAT (dat2, 6); \
+ next = 7; \
+ } else if (next == 6) { \
+ CVMX_MT_HSH_DAT (dat1, 6); \
+ CVMX_MT_HSH_STARTMD5 (dat2); \
+ next = 0; \
+ } else { \
+ CVMX_MT_HSH_STARTMD5 (dat1); \
+ CVMX_MT_HSH_DAT (dat2, 0); \
+ next = 1; \
+ } \
+}
+
+/****************************************************************************/
+
+void
+octo_calc_hash(uint8_t auth, unsigned char *key, uint64_t *inner, uint64_t *outer)
+{
+ uint8_t hash_key[64];
+ uint64_t *key1;
+ register uint64_t xor1 = 0x3636363636363636ULL;
+ register uint64_t xor2 = 0x5c5c5c5c5c5c5c5cULL;
+ register_t s;
+
+ dprintf("%s()\n", __func__);
+
+ memset(hash_key, 0, sizeof(hash_key));
+ memcpy(hash_key, (uint8_t *) key, (auth ? 20 : 16));
+ key1 = (uint64_t *) hash_key;
+ s = octeon_crypto_enable();
+ if (auth) {
+ CVMX_MT_HSH_IV(0x67452301EFCDAB89ULL, 0);
+ CVMX_MT_HSH_IV(0x98BADCFE10325476ULL, 1);
+ CVMX_MT_HSH_IV(0xC3D2E1F000000000ULL, 2);
+ } else {
+ CVMX_MT_HSH_IV(0x0123456789ABCDEFULL, 0);
+ CVMX_MT_HSH_IV(0xFEDCBA9876543210ULL, 1);
+ }
+
+ CVMX_MT_HSH_DAT((*key1 ^ xor1), 0);
+ key1++;
+ CVMX_MT_HSH_DAT((*key1 ^ xor1), 1);
+ key1++;
+ CVMX_MT_HSH_DAT((*key1 ^ xor1), 2);
+ key1++;
+ CVMX_MT_HSH_DAT((*key1 ^ xor1), 3);
+ key1++;
+ CVMX_MT_HSH_DAT((*key1 ^ xor1), 4);
+ key1++;
+ CVMX_MT_HSH_DAT((*key1 ^ xor1), 5);
+ key1++;
+ CVMX_MT_HSH_DAT((*key1 ^ xor1), 6);
+ key1++;
+ if (auth)
+ CVMX_MT_HSH_STARTSHA((*key1 ^ xor1));
+ else
+ CVMX_MT_HSH_STARTMD5((*key1 ^ xor1));
+
+ CVMX_MF_HSH_IV(inner[0], 0);
+ CVMX_MF_HSH_IV(inner[1], 1);
+ if (auth) {
+ inner[2] = 0;
+ CVMX_MF_HSH_IV(((uint64_t *) inner)[2], 2);
+ }
+
+ memset(hash_key, 0, sizeof(hash_key));
+ memcpy(hash_key, (uint8_t *) key, (auth ? 20 : 16));
+ key1 = (uint64_t *) hash_key;
+ if (auth) {
+ CVMX_MT_HSH_IV(0x67452301EFCDAB89ULL, 0);
+ CVMX_MT_HSH_IV(0x98BADCFE10325476ULL, 1);
+ CVMX_MT_HSH_IV(0xC3D2E1F000000000ULL, 2);
+ } else {
+ CVMX_MT_HSH_IV(0x0123456789ABCDEFULL, 0);
+ CVMX_MT_HSH_IV(0xFEDCBA9876543210ULL, 1);
+ }
+
+ CVMX_MT_HSH_DAT((*key1 ^ xor2), 0);
+ key1++;
+ CVMX_MT_HSH_DAT((*key1 ^ xor2), 1);
+ key1++;
+ CVMX_MT_HSH_DAT((*key1 ^ xor2), 2);
+ key1++;
+ CVMX_MT_HSH_DAT((*key1 ^ xor2), 3);
+ key1++;
+ CVMX_MT_HSH_DAT((*key1 ^ xor2), 4);
+ key1++;
+ CVMX_MT_HSH_DAT((*key1 ^ xor2), 5);
+ key1++;
+ CVMX_MT_HSH_DAT((*key1 ^ xor2), 6);
+ key1++;
+ if (auth)
+ CVMX_MT_HSH_STARTSHA((*key1 ^ xor2));
+ else
+ CVMX_MT_HSH_STARTMD5((*key1 ^ xor2));
+
+ CVMX_MF_HSH_IV(outer[0], 0);
+ CVMX_MF_HSH_IV(outer[1], 1);
+ if (auth) {
+ outer[2] = 0;
+ CVMX_MF_HSH_IV(outer[2], 2);
+ }
+ octeon_crypto_disable(s);
+ return;
+}
+
+/****************************************************************************/
+/* DES functions */
+
+int
+octo_des_cbc_encrypt(
+ struct octo_sess *od,
+ struct iovec *iov, size_t iovcnt, size_t iovlen,
+ int auth_off, int auth_len,
+ int crypt_off, int crypt_len,
+ int icv_off, uint8_t *ivp)
+{
+ uint64_t *data;
+ int data_i, data_l;
+ register_t s;
+
+ dprintf("%s()\n", __func__);
+
+ if (__predict_false(od == NULL || iov==NULL || iovlen==0 || ivp==NULL ||
+ (crypt_off & 0x7) || (crypt_off + crypt_len > iovlen))) {
+ dprintf("%s: Bad parameters od=%p iov=%p iovlen=%d "
+ "auth_off=%d auth_len=%d crypt_off=%d crypt_len=%d "
+ "icv_off=%d ivp=%p\n", __func__, od, iov, iovlen,
+ auth_off, auth_len, crypt_off, crypt_len, icv_off, ivp);
+ return -EINVAL;
+ }
+
+ IOV_INIT(iov, data, data_i, data_l);
+
+ CVMX_PREFETCH0(ivp);
+ CVMX_PREFETCH0(od->octo_enckey);
+
+ s = octeon_crypto_enable();
+
+ /* load 3DES Key */
+ CVMX_MT_3DES_KEY(((uint64_t *) od->octo_enckey)[0], 0);
+ if (od->octo_encklen == 24) {
+ CVMX_MT_3DES_KEY(((uint64_t *) od->octo_enckey)[1], 1);
+ CVMX_MT_3DES_KEY(((uint64_t *) od->octo_enckey)[2], 2);
+ } else if (od->octo_encklen == 8) {
+ CVMX_MT_3DES_KEY(((uint64_t *) od->octo_enckey)[0], 1);
+ CVMX_MT_3DES_KEY(((uint64_t *) od->octo_enckey)[0], 2);
+ } else {
+ octeon_crypto_disable(s);
+ dprintf("%s: Bad key length %d\n", __func__, od->octo_encklen);
+ return -EINVAL;
+ }
+
+ CVMX_MT_3DES_IV(* (uint64_t *) ivp);
+
+ while (crypt_off > 0) {
+ IOV_CONSUME(iov, data, data_i, data_l);
+ crypt_off -= 8;
+ }
+
+ while (crypt_len > 0) {
+ CVMX_MT_3DES_ENC_CBC(*data);
+ CVMX_MF_3DES_RESULT(*data);
+ IOV_CONSUME(iov, data, data_i, data_l);
+ crypt_len -= 8;
+ }
+
+ octeon_crypto_disable(s);
+ return 0;
+}
+
+
+int
+octo_des_cbc_decrypt(
+ struct octo_sess *od,
+ struct iovec *iov, size_t iovcnt, size_t iovlen,
+ int auth_off, int auth_len,
+ int crypt_off, int crypt_len,
+ int icv_off, uint8_t *ivp)
+{
+ uint64_t *data;
+ int data_i, data_l;
+ register_t s;
+
+ dprintf("%s()\n", __func__);
+
+ if (__predict_false(od == NULL || iov==NULL || iovlen==0 || ivp==NULL ||
+ (crypt_off & 0x7) || (crypt_off + crypt_len > iovlen))) {
+ dprintf("%s: Bad parameters od=%p iov=%p iovlen=%d "
+ "auth_off=%d auth_len=%d crypt_off=%d crypt_len=%d "
+ "icv_off=%d ivp=%p\n", __func__, od, iov, iovlen,
+ auth_off, auth_len, crypt_off, crypt_len, icv_off, ivp);
+ return -EINVAL;
+ }
+
+ IOV_INIT(iov, data, data_i, data_l);
+
+ CVMX_PREFETCH0(ivp);
+ CVMX_PREFETCH0(od->octo_enckey);
+
+ s = octeon_crypto_enable();
+
+ /* load 3DES Key */
+ CVMX_MT_3DES_KEY(((uint64_t *) od->octo_enckey)[0], 0);
+ if (od->octo_encklen == 24) {
+ CVMX_MT_3DES_KEY(((uint64_t *) od->octo_enckey)[1], 1);
+ CVMX_MT_3DES_KEY(((uint64_t *) od->octo_enckey)[2], 2);
+ } else if (od->octo_encklen == 8) {
+ CVMX_MT_3DES_KEY(((uint64_t *) od->octo_enckey)[0], 1);
+ CVMX_MT_3DES_KEY(((uint64_t *) od->octo_enckey)[0], 2);
+ } else {
+ octeon_crypto_disable(s);
+ dprintf("%s: Bad key length %d\n", __func__, od->octo_encklen);
+ return -EINVAL;
+ }
+
+ CVMX_MT_3DES_IV(* (uint64_t *) ivp);
+
+ while (crypt_off > 0) {
+ IOV_CONSUME(iov, data, data_i, data_l);
+ crypt_off -= 8;
+ }
+
+ while (crypt_len > 0) {
+ CVMX_MT_3DES_DEC_CBC(*data);
+ CVMX_MF_3DES_RESULT(*data);
+ IOV_CONSUME(iov, data, data_i, data_l);
+ crypt_len -= 8;
+ }
+
+ octeon_crypto_disable(s);
+ return 0;
+}
+
+/****************************************************************************/
+/* AES functions */
+
+int
+octo_aes_cbc_encrypt(
+ struct octo_sess *od,
+ struct iovec *iov, size_t iovcnt, size_t iovlen,
+ int auth_off, int auth_len,
+ int crypt_off, int crypt_len,
+ int icv_off, uint8_t *ivp)
+{
+ uint64_t *data, *pdata;
+ int data_i, data_l;
+ register_t s;
+
+ dprintf("%s()\n", __func__);
+
+ if (__predict_false(od == NULL || iov==NULL || iovlen==0 || ivp==NULL ||
+ (crypt_off & 0x7) || (crypt_off + crypt_len > iovlen))) {
+ dprintf("%s: Bad parameters od=%p iov=%p iovlen=%d "
+ "auth_off=%d auth_len=%d crypt_off=%d crypt_len=%d "
+ "icv_off=%d ivp=%p\n", __func__, od, iov, iovlen,
+ auth_off, auth_len, crypt_off, crypt_len, icv_off, ivp);
+ return -EINVAL;
+ }
+
+ IOV_INIT(iov, data, data_i, data_l);
+
+ CVMX_PREFETCH0(ivp);
+ CVMX_PREFETCH0(od->octo_enckey);
+
+ s = octeon_crypto_enable();
+
+ /* load AES Key */
+ CVMX_MT_AES_KEY(((uint64_t *) od->octo_enckey)[0], 0);
+ CVMX_MT_AES_KEY(((uint64_t *) od->octo_enckey)[1], 1);
+
+ if (od->octo_encklen == 16) {
+ CVMX_MT_AES_KEY(0x0, 2);
+ CVMX_MT_AES_KEY(0x0, 3);
+ } else if (od->octo_encklen == 24) {
+ CVMX_MT_AES_KEY(((uint64_t *) od->octo_enckey)[2], 2);
+ CVMX_MT_AES_KEY(0x0, 3);
+ } else if (od->octo_encklen == 32) {
+ CVMX_MT_AES_KEY(((uint64_t *) od->octo_enckey)[2], 2);
+ CVMX_MT_AES_KEY(((uint64_t *) od->octo_enckey)[3], 3);
+ } else {
+ octeon_crypto_disable(s);
+ dprintf("%s: Bad key length %d\n", __func__, od->octo_encklen);
+ return -EINVAL;
+ }
+ CVMX_MT_AES_KEYLENGTH(od->octo_encklen / 8 - 1);
+
+ CVMX_MT_AES_IV(((uint64_t *) ivp)[0], 0);
+ CVMX_MT_AES_IV(((uint64_t *) ivp)[1], 1);
+
+ while (crypt_off > 0) {
+ IOV_CONSUME(iov, data, data_i, data_l);
+ crypt_off -= 8;
+ }
+
+ while (crypt_len > 0) {
+ pdata = data;
+ CVMX_MT_AES_ENC_CBC0(*data);
+ IOV_CONSUME(iov, data, data_i, data_l);
+ CVMX_MT_AES_ENC_CBC1(*data);
+ CVMX_MF_AES_RESULT(*pdata, 0);
+ CVMX_MF_AES_RESULT(*data, 1);
+ IOV_CONSUME(iov, data, data_i, data_l);
+ crypt_len -= 16;
+ }
+
+ octeon_crypto_disable(s);
+ return 0;
+}
+
+
+int
+octo_aes_cbc_decrypt(
+ struct octo_sess *od,
+ struct iovec *iov, size_t iovcnt, size_t iovlen,
+ int auth_off, int auth_len,
+ int crypt_off, int crypt_len,
+ int icv_off, uint8_t *ivp)
+{
+ uint64_t *data, *pdata;
+ int data_i, data_l;
+ register_t s;
+
+ dprintf("%s()\n", __func__);
+
+ if (__predict_false(od == NULL || iov==NULL || iovlen==0 || ivp==NULL ||
+ (crypt_off & 0x7) || (crypt_off + crypt_len > iovlen))) {
+ dprintf("%s: Bad parameters od=%p iov=%p iovlen=%d "
+ "auth_off=%d auth_len=%d crypt_off=%d crypt_len=%d "
+ "icv_off=%d ivp=%p\n", __func__, od, iov, iovlen,
+ auth_off, auth_len, crypt_off, crypt_len, icv_off, ivp);
+ return -EINVAL;
+ }
+
+ IOV_INIT(iov, data, data_i, data_l);
+
+ CVMX_PREFETCH0(ivp);
+ CVMX_PREFETCH0(od->octo_enckey);
+
+ s = octeon_crypto_enable();
+
+ /* load AES Key */
+ CVMX_MT_AES_KEY(((uint64_t *) od->octo_enckey)[0], 0);
+ CVMX_MT_AES_KEY(((uint64_t *) od->octo_enckey)[1], 1);
+
+ if (od->octo_encklen == 16) {
+ CVMX_MT_AES_KEY(0x0, 2);
+ CVMX_MT_AES_KEY(0x0, 3);
+ } else if (od->octo_encklen == 24) {
+ CVMX_MT_AES_KEY(((uint64_t *) od->octo_enckey)[2], 2);
+ CVMX_MT_AES_KEY(0x0, 3);
+ } else if (od->octo_encklen == 32) {
+ CVMX_MT_AES_KEY(((uint64_t *) od->octo_enckey)[2], 2);
+ CVMX_MT_AES_KEY(((uint64_t *) od->octo_enckey)[3], 3);
+ } else {
+ octeon_crypto_disable(s);
+ dprintf("%s: Bad key length %d\n", __func__, od->octo_encklen);
+ return -EINVAL;
+ }
+ CVMX_MT_AES_KEYLENGTH(od->octo_encklen / 8 - 1);
+
+ CVMX_MT_AES_IV(((uint64_t *) ivp)[0], 0);
+ CVMX_MT_AES_IV(((uint64_t *) ivp)[1], 1);
+
+ while (crypt_off > 0) {
+ IOV_CONSUME(iov, data, data_i, data_l);
+ crypt_off -= 8;
+ }
+
+ while (crypt_len > 0) {
+ pdata = data;
+ CVMX_MT_AES_DEC_CBC0(*data);
+ IOV_CONSUME(iov, data, data_i, data_l);
+ CVMX_MT_AES_DEC_CBC1(*data);
+ CVMX_MF_AES_RESULT(*pdata, 0);
+ CVMX_MF_AES_RESULT(*data, 1);
+ IOV_CONSUME(iov, data, data_i, data_l);
+ crypt_len -= 16;
+ }
+
+ octeon_crypto_disable(s);
+ return 0;
+}
+
+/****************************************************************************/
+/* MD5 */
+
+int
+octo_null_md5_encrypt(
+ struct octo_sess *od,
+ struct iovec *iov, size_t iovcnt, size_t iovlen,
+ int auth_off, int auth_len,
+ int crypt_off, int crypt_len,
+ int icv_off, uint8_t *ivp)
+{
+ register int next = 0;
+ uint64_t *data;
+ uint64_t tmp1, tmp2;
+ int data_i, data_l, alen = auth_len;
+ register_t s;
+
+ dprintf("%s()\n", __func__);
+
+ if (__predict_false(od == NULL || iov==NULL || iovlen==0 ||
+ (auth_off & 0x7) || (auth_off + auth_len > iovlen))) {
+ dprintf("%s: Bad parameters od=%p iov=%p iovlen=%d "
+ "auth_off=%d auth_len=%d crypt_off=%d crypt_len=%d "
+ "icv_off=%d ivp=%p\n", __func__, od, iov, iovlen,
+ auth_off, auth_len, crypt_off, crypt_len, icv_off, ivp);
+ return -EINVAL;
+ }
+
+ IOV_INIT(iov, data, data_i, data_l);
+
+ s = octeon_crypto_enable();
+
+ /* Load MD5 IV */
+ CVMX_MT_HSH_IV(od->octo_hminner[0], 0);
+ CVMX_MT_HSH_IV(od->octo_hminner[1], 1);
+
+ while (auth_off > 0) {
+ IOV_CONSUME(iov, data, data_i, data_l);
+ auth_off -= 8;
+ }
+
+ while (auth_len > 0) {
+ CVM_LOAD_MD5_UNIT(*data, next);
+ auth_len -= 8;
+ IOV_CONSUME(iov, data, data_i, data_l);
+ }
+
+ /* finish the hash */
+ CVMX_PREFETCH0(od->octo_hmouter);
+#if 0
+ if (__predict_false(inplen)) {
+ uint64_t tmp = 0;
+ uint8_t *p = (uint8_t *) & tmp;
+ p[inplen] = 0x80;
+ do {
+ inplen--;
+ p[inplen] = ((uint8_t *) data)[inplen];
+ } while (inplen);
+ CVM_LOAD_MD5_UNIT(tmp, next);
+ } else {
+ CVM_LOAD_MD5_UNIT(0x8000000000000000ULL, next);
+ }
+#else
+ CVM_LOAD_MD5_UNIT(0x8000000000000000ULL, next);
+#endif
+
+ /* Finish Inner hash */
+ while (next != 7) {
+ CVM_LOAD_MD5_UNIT(((uint64_t) 0x0ULL), next);
+ }
+ CVMX_ES64(tmp1, ((alen + 64) << 3));
+ CVM_LOAD_MD5_UNIT(tmp1, next);
+
+ /* Get the inner hash of HMAC */
+ CVMX_MF_HSH_IV(tmp1, 0);
+ CVMX_MF_HSH_IV(tmp2, 1);
+
+ /* Initialize hash unit */
+ CVMX_MT_HSH_IV(od->octo_hmouter[0], 0);
+ CVMX_MT_HSH_IV(od->octo_hmouter[1], 1);
+
+ CVMX_MT_HSH_DAT(tmp1, 0);
+ CVMX_MT_HSH_DAT(tmp2, 1);
+ CVMX_MT_HSH_DAT(0x8000000000000000ULL, 2);
+ CVMX_MT_HSH_DATZ(3);
+ CVMX_MT_HSH_DATZ(4);
+ CVMX_MT_HSH_DATZ(5);
+ CVMX_MT_HSH_DATZ(6);
+ CVMX_ES64(tmp1, ((64 + 16) << 3));
+ CVMX_MT_HSH_STARTMD5(tmp1);
+
+ /* save the HMAC */
+ IOV_INIT(iov, data, data_i, data_l);
+ while (icv_off > 0) {
+ IOV_CONSUME(iov, data, data_i, data_l);
+ icv_off -= 8;
+ }
+ CVMX_MF_HSH_IV(*data, 0);
+ IOV_CONSUME(iov, data, data_i, data_l);
+ CVMX_MF_HSH_IV(tmp1, 1);
+ *(uint32_t *)data = (uint32_t) (tmp1 >> 32);
+
+ octeon_crypto_disable(s);
+ return 0;
+}
+
+/****************************************************************************/
+/* SHA1 */
+
+int
+octo_null_sha1_encrypt(
+ struct octo_sess *od,
+ struct iovec *iov, size_t iovcnt, size_t iovlen,
+ int auth_off, int auth_len,
+ int crypt_off, int crypt_len,
+ int icv_off, uint8_t *ivp)
+{
+ register int next = 0;
+ uint64_t *data;
+ uint64_t tmp1, tmp2, tmp3;
+ int data_i, data_l, alen = auth_len;
+ register_t s;
+
+ dprintf("%s()\n", __func__);
+
+ if (__predict_false(od == NULL || iov==NULL || iovlen==0 ||
+ (auth_off & 0x7) || (auth_off + auth_len > iovlen))) {
+ dprintf("%s: Bad parameters od=%p iov=%p iovlen=%d "
+ "auth_off=%d auth_len=%d crypt_off=%d crypt_len=%d "
+ "icv_off=%d ivp=%p\n", __func__, od, iov, iovlen,
+ auth_off, auth_len, crypt_off, crypt_len, icv_off, ivp);
+ return -EINVAL;
+ }
+
+ IOV_INIT(iov, data, data_i, data_l);
+
+ s = octeon_crypto_enable();
+
+ /* Load SHA1 IV */
+ CVMX_MT_HSH_IV(od->octo_hminner[0], 0);
+ CVMX_MT_HSH_IV(od->octo_hminner[1], 1);
+ CVMX_MT_HSH_IV(od->octo_hminner[2], 2);
+
+ while (auth_off > 0) {
+ IOV_CONSUME(iov, data, data_i, data_l);
+ auth_off -= 8;
+ }
+
+ while (auth_len > 0) {
+ CVM_LOAD_SHA_UNIT(*data, next);
+ auth_len -= 8;
+ IOV_CONSUME(iov, data, data_i, data_l);
+ }
+
+ /* finish the hash */
+ CVMX_PREFETCH0(od->octo_hmouter);
+#if 0
+ if (__predict_false(inplen)) {
+ uint64_t tmp = 0;
+ uint8_t *p = (uint8_t *) & tmp;
+ p[inplen] = 0x80;
+ do {
+ inplen--;
+ p[inplen] = ((uint8_t *) data)[inplen];
+ } while (inplen);
+ CVM_LOAD_MD5_UNIT(tmp, next);
+ } else {
+ CVM_LOAD_MD5_UNIT(0x8000000000000000ULL, next);
+ }
+#else
+ CVM_LOAD_SHA_UNIT(0x8000000000000000ULL, next);
+#endif
+
+ /* Finish Inner hash */
+ while (next != 7) {
+ CVM_LOAD_SHA_UNIT(((uint64_t) 0x0ULL), next);
+ }
+ CVM_LOAD_SHA_UNIT((uint64_t) ((alen + 64) << 3), next);
+
+ /* Get the inner hash of HMAC */
+ CVMX_MF_HSH_IV(tmp1, 0);
+ CVMX_MF_HSH_IV(tmp2, 1);
+ tmp3 = 0;
+ CVMX_MF_HSH_IV(tmp3, 2);
+
+ /* Initialize hash unit */
+ CVMX_MT_HSH_IV(od->octo_hmouter[0], 0);
+ CVMX_MT_HSH_IV(od->octo_hmouter[1], 1);
+ CVMX_MT_HSH_IV(od->octo_hmouter[2], 2);
+
+ CVMX_MT_HSH_DAT(tmp1, 0);
+ CVMX_MT_HSH_DAT(tmp2, 1);
+ tmp3 |= 0x0000000080000000;
+ CVMX_MT_HSH_DAT(tmp3, 2);
+ CVMX_MT_HSH_DATZ(3);
+ CVMX_MT_HSH_DATZ(4);
+ CVMX_MT_HSH_DATZ(5);
+ CVMX_MT_HSH_DATZ(6);
+ CVMX_MT_HSH_STARTSHA((uint64_t) ((64 + 20) << 3));
+
+ /* save the HMAC */
+ IOV_INIT(iov, data, data_i, data_l);
+ while (icv_off > 0) {
+ IOV_CONSUME(iov, data, data_i, data_l);
+ icv_off -= 8;
+ }
+ CVMX_MF_HSH_IV(*data, 0);
+ IOV_CONSUME(iov, data, data_i, data_l);
+ CVMX_MF_HSH_IV(tmp1, 1);
+ *(uint32_t *)data = (uint32_t) (tmp1 >> 32);
+
+ octeon_crypto_disable(s);
+ return 0;
+}
+
+/****************************************************************************/
+/* DES MD5 */
+
+int
+octo_des_cbc_md5_encrypt(
+ struct octo_sess *od,
+ struct iovec *iov, size_t iovcnt, size_t iovlen,
+ int auth_off, int auth_len,
+ int crypt_off, int crypt_len,
+ int icv_off, uint8_t *ivp)
+{
+ register int next = 0;
+ union {
+ uint32_t data32[2];
+ uint64_t data64[1];
+ } mydata;
+ uint64_t *data = &mydata.data64[0];
+ uint32_t *data32;
+ uint64_t tmp1, tmp2;
+ int data_i, data_l, alen = auth_len;
+ register_t s;
+
+ dprintf("%s()\n", __func__);
+
+ if (__predict_false(od == NULL || iov==NULL || iovlen==0 || ivp==NULL ||
+ (crypt_off & 0x3) || (crypt_off + crypt_len > iovlen) ||
+ (crypt_len & 0x7) ||
+ (auth_len & 0x7) ||
+ (auth_off & 0x3) || (auth_off + auth_len > iovlen))) {
+ dprintf("%s: Bad parameters od=%p iov=%p iovlen=%d "
+ "auth_off=%d auth_len=%d crypt_off=%d crypt_len=%d "
+ "icv_off=%d ivp=%p\n", __func__, od, iov, iovlen,
+ auth_off, auth_len, crypt_off, crypt_len, icv_off, ivp);
+ return -EINVAL;
+ }
+
+ IOV_INIT(iov, data32, data_i, data_l);
+
+ CVMX_PREFETCH0(ivp);
+ CVMX_PREFETCH0(od->octo_enckey);
+
+ s = octeon_crypto_enable();
+
+ /* load 3DES Key */
+ CVMX_MT_3DES_KEY(((uint64_t *) od->octo_enckey)[0], 0);
+ if (od->octo_encklen == 24) {
+ CVMX_MT_3DES_KEY(((uint64_t *) od->octo_enckey)[1], 1);
+ CVMX_MT_3DES_KEY(((uint64_t *) od->octo_enckey)[2], 2);
+ } else if (od->octo_encklen == 8) {
+ CVMX_MT_3DES_KEY(((uint64_t *) od->octo_enckey)[0], 1);
+ CVMX_MT_3DES_KEY(((uint64_t *) od->octo_enckey)[0], 2);
+ } else {
+ octeon_crypto_disable(s);
+ dprintf("%s: Bad key length %d\n", __func__, od->octo_encklen);
+ return -EINVAL;
+ }
+
+ CVMX_MT_3DES_IV(* (uint64_t *) ivp);
+
+ /* Load MD5 IV */
+ CVMX_MT_HSH_IV(od->octo_hminner[0], 0);
+ CVMX_MT_HSH_IV(od->octo_hminner[1], 1);
+
+ while (crypt_off > 0 && auth_off > 0) {
+ IOV_CONSUME(iov, data32, data_i, data_l);
+ crypt_off -= 4;
+ auth_off -= 4;
+ }
+
+ while (crypt_len > 0 || auth_len > 0) {
+ uint32_t *first = data32;
+ mydata.data32[0] = *first;
+ IOV_CONSUME(iov, data32, data_i, data_l);
+ mydata.data32[1] = *data32;
+ if (crypt_off <= 0) {
+ if (crypt_len > 0) {
+ CVMX_MT_3DES_ENC_CBC(*data);
+ CVMX_MF_3DES_RESULT(*data);
+ crypt_len -= 8;
+ }
+ } else
+ crypt_off -= 8;
+ if (auth_off <= 0) {
+ if (auth_len > 0) {
+ CVM_LOAD_MD5_UNIT(*data, next);
+ auth_len -= 8;
+ }
+ } else
+ auth_off -= 8;
+ *first = mydata.data32[0];
+ *data32 = mydata.data32[1];
+ IOV_CONSUME(iov, data32, data_i, data_l);
+ }
+
+ /* finish the hash */
+ CVMX_PREFETCH0(od->octo_hmouter);
+#if 0
+ if (__predict_false(inplen)) {
+ uint64_t tmp = 0;
+ uint8_t *p = (uint8_t *) & tmp;
+ p[inplen] = 0x80;
+ do {
+ inplen--;
+ p[inplen] = ((uint8_t *) data)[inplen];
+ } while (inplen);
+ CVM_LOAD_MD5_UNIT(tmp, next);
+ } else {
+ CVM_LOAD_MD5_UNIT(0x8000000000000000ULL, next);
+ }
+#else
+ CVM_LOAD_MD5_UNIT(0x8000000000000000ULL, next);
+#endif
+
+ /* Finish Inner hash */
+ while (next != 7) {
+ CVM_LOAD_MD5_UNIT(((uint64_t) 0x0ULL), next);
+ }
+ CVMX_ES64(tmp1, ((alen + 64) << 3));
+ CVM_LOAD_MD5_UNIT(tmp1, next);
+
+ /* Get the inner hash of HMAC */
+ CVMX_MF_HSH_IV(tmp1, 0);
+ CVMX_MF_HSH_IV(tmp2, 1);
+
+ /* Initialize hash unit */
+ CVMX_MT_HSH_IV(od->octo_hmouter[0], 0);
+ CVMX_MT_HSH_IV(od->octo_hmouter[1], 1);
+
+ CVMX_MT_HSH_DAT(tmp1, 0);
+ CVMX_MT_HSH_DAT(tmp2, 1);
+ CVMX_MT_HSH_DAT(0x8000000000000000ULL, 2);
+ CVMX_MT_HSH_DATZ(3);
+ CVMX_MT_HSH_DATZ(4);
+ CVMX_MT_HSH_DATZ(5);
+ CVMX_MT_HSH_DATZ(6);
+ CVMX_ES64(tmp1, ((64 + 16) << 3));
+ CVMX_MT_HSH_STARTMD5(tmp1);
+
+ /* save the HMAC */
+ IOV_INIT(iov, data32, data_i, data_l);
+ while (icv_off > 0) {
+ IOV_CONSUME(iov, data32, data_i, data_l);
+ icv_off -= 4;
+ }
+ CVMX_MF_HSH_IV(tmp1, 0);
+ *data32 = (uint32_t) (tmp1 >> 32);
+ IOV_CONSUME(iov, data32, data_i, data_l);
+ *data32 = (uint32_t) tmp1;
+ IOV_CONSUME(iov, data32, data_i, data_l);
+ CVMX_MF_HSH_IV(tmp1, 1);
+ *data32 = (uint32_t) (tmp1 >> 32);
+
+ octeon_crypto_disable(s);
+ return 0;
+}
+
+int
+octo_des_cbc_md5_decrypt(
+ struct octo_sess *od,
+ struct iovec *iov, size_t iovcnt, size_t iovlen,
+ int auth_off, int auth_len,
+ int crypt_off, int crypt_len,
+ int icv_off, uint8_t *ivp)
+{
+ register int next = 0;
+ union {
+ uint32_t data32[2];
+ uint64_t data64[1];
+ } mydata;
+ uint64_t *data = &mydata.data64[0];
+ uint32_t *data32;
+ uint64_t tmp1, tmp2;
+ int data_i, data_l, alen = auth_len;
+ register_t s;
+
+ dprintf("%s()\n", __func__);
+
+ if (__predict_false(od == NULL || iov==NULL || iovlen==0 || ivp==NULL ||
+ (crypt_off & 0x3) || (crypt_off + crypt_len > iovlen) ||
+ (crypt_len & 0x7) ||
+ (auth_len & 0x7) ||
+ (auth_off & 0x3) || (auth_off + auth_len > iovlen))) {
+ dprintf("%s: Bad parameters od=%p iov=%p iovlen=%d "
+ "auth_off=%d auth_len=%d crypt_off=%d crypt_len=%d "
+ "icv_off=%d ivp=%p\n", __func__, od, iov, iovlen,
+ auth_off, auth_len, crypt_off, crypt_len, icv_off, ivp);
+ return -EINVAL;
+ }
+
+ IOV_INIT(iov, data32, data_i, data_l);
+
+ CVMX_PREFETCH0(ivp);
+ CVMX_PREFETCH0(od->octo_enckey);
+
+ s = octeon_crypto_enable();
+
+ /* load 3DES Key */
+ CVMX_MT_3DES_KEY(((uint64_t *) od->octo_enckey)[0], 0);
+ if (od->octo_encklen == 24) {
+ CVMX_MT_3DES_KEY(((uint64_t *) od->octo_enckey)[1], 1);
+ CVMX_MT_3DES_KEY(((uint64_t *) od->octo_enckey)[2], 2);
+ } else if (od->octo_encklen == 8) {
+ CVMX_MT_3DES_KEY(((uint64_t *) od->octo_enckey)[0], 1);
+ CVMX_MT_3DES_KEY(((uint64_t *) od->octo_enckey)[0], 2);
+ } else {
+ octeon_crypto_disable(s);
+ dprintf("%s: Bad key length %d\n", __func__, od->octo_encklen);
+ return -EINVAL;
+ }
+
+ CVMX_MT_3DES_IV(* (uint64_t *) ivp);
+
+ /* Load MD5 IV */
+ CVMX_MT_HSH_IV(od->octo_hminner[0], 0);
+ CVMX_MT_HSH_IV(od->octo_hminner[1], 1);
+
+ while (crypt_off > 0 && auth_off > 0) {
+ IOV_CONSUME(iov, data32, data_i, data_l);
+ crypt_off -= 4;
+ auth_off -= 4;
+ }
+
+ while (crypt_len > 0 || auth_len > 0) {
+ uint32_t *first = data32;
+ mydata.data32[0] = *first;
+ IOV_CONSUME(iov, data32, data_i, data_l);
+ mydata.data32[1] = *data32;
+ if (auth_off <= 0) {
+ if (auth_len > 0) {
+ CVM_LOAD_MD5_UNIT(*data, next);
+ auth_len -= 8;
+ }
+ } else
+ auth_off -= 8;
+ if (crypt_off <= 0) {
+ if (crypt_len > 0) {
+ CVMX_MT_3DES_DEC_CBC(*data);
+ CVMX_MF_3DES_RESULT(*data);
+ crypt_len -= 8;
+ }
+ } else
+ crypt_off -= 8;
+ *first = mydata.data32[0];
+ *data32 = mydata.data32[1];
+ IOV_CONSUME(iov, data32, data_i, data_l);
+ }
+
+ /* finish the hash */
+ CVMX_PREFETCH0(od->octo_hmouter);
+#if 0
+ if (__predict_false(inplen)) {
+ uint64_t tmp = 0;
+ uint8_t *p = (uint8_t *) & tmp;
+ p[inplen] = 0x80;
+ do {
+ inplen--;
+ p[inplen] = ((uint8_t *) data)[inplen];
+ } while (inplen);
+ CVM_LOAD_MD5_UNIT(tmp, next);
+ } else {
+ CVM_LOAD_MD5_UNIT(0x8000000000000000ULL, next);
+ }
+#else
+ CVM_LOAD_MD5_UNIT(0x8000000000000000ULL, next);
+#endif
+
+ /* Finish Inner hash */
+ while (next != 7) {
+ CVM_LOAD_MD5_UNIT(((uint64_t) 0x0ULL), next);
+ }
+ CVMX_ES64(tmp1, ((alen + 64) << 3));
+ CVM_LOAD_MD5_UNIT(tmp1, next);
+
+ /* Get the inner hash of HMAC */
+ CVMX_MF_HSH_IV(tmp1, 0);
+ CVMX_MF_HSH_IV(tmp2, 1);
+
+ /* Initialize hash unit */
+ CVMX_MT_HSH_IV(od->octo_hmouter[0], 0);
+ CVMX_MT_HSH_IV(od->octo_hmouter[1], 1);
+
+ CVMX_MT_HSH_DAT(tmp1, 0);
+ CVMX_MT_HSH_DAT(tmp2, 1);
+ CVMX_MT_HSH_DAT(0x8000000000000000ULL, 2);
+ CVMX_MT_HSH_DATZ(3);
+ CVMX_MT_HSH_DATZ(4);
+ CVMX_MT_HSH_DATZ(5);
+ CVMX_MT_HSH_DATZ(6);
+ CVMX_ES64(tmp1, ((64 + 16) << 3));
+ CVMX_MT_HSH_STARTMD5(tmp1);
+
+ /* save the HMAC */
+ IOV_INIT(iov, data32, data_i, data_l);
+ while (icv_off > 0) {
+ IOV_CONSUME(iov, data32, data_i, data_l);
+ icv_off -= 4;
+ }
+ CVMX_MF_HSH_IV(tmp1, 0);
+ *data32 = (uint32_t) (tmp1 >> 32);
+ IOV_CONSUME(iov, data32, data_i, data_l);
+ *data32 = (uint32_t) tmp1;
+ IOV_CONSUME(iov, data32, data_i, data_l);
+ CVMX_MF_HSH_IV(tmp1, 1);
+ *data32 = (uint32_t) (tmp1 >> 32);
+
+ octeon_crypto_disable(s);
+ return 0;
+}
+
+/****************************************************************************/
+/* DES SHA */
+
+int
+octo_des_cbc_sha1_encrypt(
+ struct octo_sess *od,
+ struct iovec *iov, size_t iovcnt, size_t iovlen,
+ int auth_off, int auth_len,
+ int crypt_off, int crypt_len,
+ int icv_off, uint8_t *ivp)
+{
+ register int next = 0;
+ union {
+ uint32_t data32[2];
+ uint64_t data64[1];
+ } mydata;
+ uint64_t *data = &mydata.data64[0];
+ uint32_t *data32;
+ uint64_t tmp1, tmp2, tmp3;
+ int data_i, data_l, alen = auth_len;
+ register_t s;
+
+ dprintf("%s()\n", __func__);
+
+ if (__predict_false(od == NULL || iov==NULL || iovlen==0 || ivp==NULL ||
+ (crypt_off & 0x3) || (crypt_off + crypt_len > iovlen) ||
+ (crypt_len & 0x7) ||
+ (auth_len & 0x7) ||
+ (auth_off & 0x3) || (auth_off + auth_len > iovlen))) {
+ dprintf("%s: Bad parameters od=%p iov=%p iovlen=%d "
+ "auth_off=%d auth_len=%d crypt_off=%d crypt_len=%d "
+ "icv_off=%d ivp=%p\n", __func__, od, iov, iovlen,
+ auth_off, auth_len, crypt_off, crypt_len, icv_off, ivp);
+ return -EINVAL;
+ }
+
+ IOV_INIT(iov, data32, data_i, data_l);
+
+ CVMX_PREFETCH0(ivp);
+ CVMX_PREFETCH0(od->octo_enckey);
+
+ s = octeon_crypto_enable();
+
+ /* load 3DES Key */
+ CVMX_MT_3DES_KEY(((uint64_t *) od->octo_enckey)[0], 0);
+ if (od->octo_encklen == 24) {
+ CVMX_MT_3DES_KEY(((uint64_t *) od->octo_enckey)[1], 1);
+ CVMX_MT_3DES_KEY(((uint64_t *) od->octo_enckey)[2], 2);
+ } else if (od->octo_encklen == 8) {
+ CVMX_MT_3DES_KEY(((uint64_t *) od->octo_enckey)[0], 1);
+ CVMX_MT_3DES_KEY(((uint64_t *) od->octo_enckey)[0], 2);
+ } else {
+ octeon_crypto_disable(s);
+ dprintf("%s: Bad key length %d\n", __func__, od->octo_encklen);
+ return -EINVAL;
+ }
+
+ CVMX_MT_3DES_IV(* (uint64_t *) ivp);
+
+ /* Load SHA1 IV */
+ CVMX_MT_HSH_IV(od->octo_hminner[0], 0);
+ CVMX_MT_HSH_IV(od->octo_hminner[1], 1);
+ CVMX_MT_HSH_IV(od->octo_hminner[2], 2);
+
+ while (crypt_off > 0 && auth_off > 0) {
+ IOV_CONSUME(iov, data32, data_i, data_l);
+ crypt_off -= 4;
+ auth_off -= 4;
+ }
+
+ while (crypt_len > 0 || auth_len > 0) {
+ uint32_t *first = data32;
+ mydata.data32[0] = *first;
+ IOV_CONSUME(iov, data32, data_i, data_l);
+ mydata.data32[1] = *data32;
+ if (crypt_off <= 0) {
+ if (crypt_len > 0) {
+ CVMX_MT_3DES_ENC_CBC(*data);
+ CVMX_MF_3DES_RESULT(*data);
+ crypt_len -= 8;
+ }
+ } else
+ crypt_off -= 8;
+ if (auth_off <= 0) {
+ if (auth_len > 0) {
+ CVM_LOAD_SHA_UNIT(*data, next);
+ auth_len -= 8;
+ }
+ } else
+ auth_off -= 8;
+ *first = mydata.data32[0];
+ *data32 = mydata.data32[1];
+ IOV_CONSUME(iov, data32, data_i, data_l);
+ }
+
+ /* finish the hash */
+ CVMX_PREFETCH0(od->octo_hmouter);
+#if 0
+ if (__predict_false(inplen)) {
+ uint64_t tmp = 0;
+ uint8_t *p = (uint8_t *) & tmp;
+ p[inplen] = 0x80;
+ do {
+ inplen--;
+ p[inplen] = ((uint8_t *) data)[inplen];
+ } while (inplen);
+ CVM_LOAD_SHA_UNIT(tmp, next);
+ } else {
+ CVM_LOAD_SHA_UNIT(0x8000000000000000ULL, next);
+ }
+#else
+ CVM_LOAD_SHA_UNIT(0x8000000000000000ULL, next);
+#endif
+
+ /* Finish Inner hash */
+ while (next != 7) {
+ CVM_LOAD_SHA_UNIT(((uint64_t) 0x0ULL), next);
+ }
+ CVM_LOAD_SHA_UNIT((uint64_t) ((alen + 64) << 3), next);
+
+ /* Get the inner hash of HMAC */
+ CVMX_MF_HSH_IV(tmp1, 0);
+ CVMX_MF_HSH_IV(tmp2, 1);
+ tmp3 = 0;
+ CVMX_MF_HSH_IV(tmp3, 2);
+
+ /* Initialize hash unit */
+ CVMX_MT_HSH_IV(od->octo_hmouter[0], 0);
+ CVMX_MT_HSH_IV(od->octo_hmouter[1], 1);
+ CVMX_MT_HSH_IV(od->octo_hmouter[2], 2);
+
+ CVMX_MT_HSH_DAT(tmp1, 0);
+ CVMX_MT_HSH_DAT(tmp2, 1);
+ tmp3 |= 0x0000000080000000;
+ CVMX_MT_HSH_DAT(tmp3, 2);
+ CVMX_MT_HSH_DATZ(3);
+ CVMX_MT_HSH_DATZ(4);
+ CVMX_MT_HSH_DATZ(5);
+ CVMX_MT_HSH_DATZ(6);
+ CVMX_MT_HSH_STARTSHA((uint64_t) ((64 + 20) << 3));
+
+ /* save the HMAC */
+ IOV_INIT(iov, data32, data_i, data_l);
+ while (icv_off > 0) {
+ IOV_CONSUME(iov, data32, data_i, data_l);
+ icv_off -= 4;
+ }
+ CVMX_MF_HSH_IV(tmp1, 0);
+ *data32 = (uint32_t) (tmp1 >> 32);
+ IOV_CONSUME(iov, data32, data_i, data_l);
+ *data32 = (uint32_t) tmp1;
+ IOV_CONSUME(iov, data32, data_i, data_l);
+ CVMX_MF_HSH_IV(tmp1, 1);
+ *data32 = (uint32_t) (tmp1 >> 32);
+
+ octeon_crypto_disable(s);
+ return 0;
+}
+
+int
+octo_des_cbc_sha1_decrypt(
+ struct octo_sess *od,
+ struct iovec *iov, size_t iovcnt, size_t iovlen,
+ int auth_off, int auth_len,
+ int crypt_off, int crypt_len,
+ int icv_off, uint8_t *ivp)
+{
+ register int next = 0;
+ union {
+ uint32_t data32[2];
+ uint64_t data64[1];
+ } mydata;
+ uint64_t *data = &mydata.data64[0];
+ uint32_t *data32;
+ uint64_t tmp1, tmp2, tmp3;
+ int data_i, data_l, alen = auth_len;
+ register_t s;
+
+ dprintf("%s()\n", __func__);
+
+ if (__predict_false(od == NULL || iov==NULL || iovlen==0 || ivp==NULL ||
+ (crypt_off & 0x3) || (crypt_off + crypt_len > iovlen) ||
+ (crypt_len & 0x7) ||
+ (auth_len & 0x7) ||
+ (auth_off & 0x3) || (auth_off + auth_len > iovlen))) {
+ dprintf("%s: Bad parameters od=%p iov=%p iovlen=%d "
+ "auth_off=%d auth_len=%d crypt_off=%d crypt_len=%d "
+ "icv_off=%d ivp=%p\n", __func__, od, iov, iovlen,
+ auth_off, auth_len, crypt_off, crypt_len, icv_off, ivp);
+ return -EINVAL;
+ }
+
+ IOV_INIT(iov, data32, data_i, data_l);
+
+ CVMX_PREFETCH0(ivp);
+ CVMX_PREFETCH0(od->octo_enckey);
+
+ s = octeon_crypto_enable();
+
+ /* load 3DES Key */
+ CVMX_MT_3DES_KEY(((uint64_t *) od->octo_enckey)[0], 0);
+ if (od->octo_encklen == 24) {
+ CVMX_MT_3DES_KEY(((uint64_t *) od->octo_enckey)[1], 1);
+ CVMX_MT_3DES_KEY(((uint64_t *) od->octo_enckey)[2], 2);
+ } else if (od->octo_encklen == 8) {
+ CVMX_MT_3DES_KEY(((uint64_t *) od->octo_enckey)[0], 1);
+ CVMX_MT_3DES_KEY(((uint64_t *) od->octo_enckey)[0], 2);
+ } else {
+ octeon_crypto_disable(s);
+ dprintf("%s: Bad key length %d\n", __func__, od->octo_encklen);
+ return -EINVAL;
+ }
+
+ CVMX_MT_3DES_IV(* (uint64_t *) ivp);
+
+ /* Load SHA1 IV */
+ CVMX_MT_HSH_IV(od->octo_hminner[0], 0);
+ CVMX_MT_HSH_IV(od->octo_hminner[1], 1);
+ CVMX_MT_HSH_IV(od->octo_hminner[2], 2);
+
+ while (crypt_off > 0 && auth_off > 0) {
+ IOV_CONSUME(iov, data32, data_i, data_l);
+ crypt_off -= 4;
+ auth_off -= 4;
+ }
+
+ while (crypt_len > 0 || auth_len > 0) {
+ uint32_t *first = data32;
+ mydata.data32[0] = *first;
+ IOV_CONSUME(iov, data32, data_i, data_l);
+ mydata.data32[1] = *data32;
+ if (auth_off <= 0) {
+ if (auth_len > 0) {
+ CVM_LOAD_SHA_UNIT(*data, next);
+ auth_len -= 8;
+ }
+ } else
+ auth_off -= 8;
+ if (crypt_off <= 0) {
+ if (crypt_len > 0) {
+ CVMX_MT_3DES_DEC_CBC(*data);
+ CVMX_MF_3DES_RESULT(*data);
+ crypt_len -= 8;
+ }
+ } else
+ crypt_off -= 8;
+ *first = mydata.data32[0];
+ *data32 = mydata.data32[1];
+ IOV_CONSUME(iov, data32, data_i, data_l);
+ }
+
+ /* finish the hash */
+ CVMX_PREFETCH0(od->octo_hmouter);
+#if 0
+ if (__predict_false(inplen)) {
+ uint64_t tmp = 0;
+ uint8_t *p = (uint8_t *) & tmp;
+ p[inplen] = 0x80;
+ do {
+ inplen--;
+ p[inplen] = ((uint8_t *) data)[inplen];
+ } while (inplen);
+ CVM_LOAD_SHA_UNIT(tmp, next);
+ } else {
+ CVM_LOAD_SHA_UNIT(0x8000000000000000ULL, next);
+ }
+#else
+ CVM_LOAD_SHA_UNIT(0x8000000000000000ULL, next);
+#endif
+
+ /* Finish Inner hash */
+ while (next != 7) {
+ CVM_LOAD_SHA_UNIT(((uint64_t) 0x0ULL), next);
+ }
+ CVM_LOAD_SHA_UNIT((uint64_t) ((alen + 64) << 3), next);
+
+ /* Get the inner hash of HMAC */
+ CVMX_MF_HSH_IV(tmp1, 0);
+ CVMX_MF_HSH_IV(tmp2, 1);
+ tmp3 = 0;
+ CVMX_MF_HSH_IV(tmp3, 2);
+
+ /* Initialize hash unit */
+ CVMX_MT_HSH_IV(od->octo_hmouter[0], 0);
+ CVMX_MT_HSH_IV(od->octo_hmouter[1], 1);
+ CVMX_MT_HSH_IV(od->octo_hmouter[2], 2);
+
+ CVMX_MT_HSH_DAT(tmp1, 0);
+ CVMX_MT_HSH_DAT(tmp2, 1);
+ tmp3 |= 0x0000000080000000;
+ CVMX_MT_HSH_DAT(tmp3, 2);
+ CVMX_MT_HSH_DATZ(3);
+ CVMX_MT_HSH_DATZ(4);
+ CVMX_MT_HSH_DATZ(5);
+ CVMX_MT_HSH_DATZ(6);
+ CVMX_MT_HSH_STARTSHA((uint64_t) ((64 + 20) << 3));
+ /* save the HMAC */
+ IOV_INIT(iov, data32, data_i, data_l);
+ while (icv_off > 0) {
+ IOV_CONSUME(iov, data32, data_i, data_l);
+ icv_off -= 4;
+ }
+ CVMX_MF_HSH_IV(tmp1, 0);
+ *data32 = (uint32_t) (tmp1 >> 32);
+ IOV_CONSUME(iov, data32, data_i, data_l);
+ *data32 = (uint32_t) tmp1;
+ IOV_CONSUME(iov, data32, data_i, data_l);
+ CVMX_MF_HSH_IV(tmp1, 1);
+ *data32 = (uint32_t) (tmp1 >> 32);
+
+ octeon_crypto_disable(s);
+ return 0;
+}
+
+/****************************************************************************/
+/* AES MD5 */
+
+int
+octo_aes_cbc_md5_encrypt(
+ struct octo_sess *od,
+ struct iovec *iov, size_t iovcnt, size_t iovlen,
+ int auth_off, int auth_len,
+ int crypt_off, int crypt_len,
+ int icv_off, uint8_t *ivp)
+{
+ register int next = 0;
+ union {
+ uint32_t data32[2];
+ uint64_t data64[1];
+ } mydata[2];
+ uint64_t *pdata = &mydata[0].data64[0];
+ uint64_t *data = &mydata[1].data64[0];
+ uint32_t *data32;
+ uint64_t tmp1, tmp2;
+ int data_i, data_l, alen = auth_len;
+ register_t s;
+
+ dprintf("%s()\n", __func__);
+
+ if (__predict_false(od == NULL || iov==NULL || iovlen==0 || ivp==NULL ||
+ (crypt_off & 0x3) || (crypt_off + crypt_len > iovlen) ||
+ (crypt_len & 0x7) ||
+ (auth_len & 0x7) ||
+ (auth_off & 0x3) || (auth_off + auth_len > iovlen))) {
+ dprintf("%s: Bad parameters od=%p iov=%p iovlen=%d "
+ "auth_off=%d auth_len=%d crypt_off=%d crypt_len=%d "
+ "icv_off=%d ivp=%p\n", __func__, od, iov, iovlen,
+ auth_off, auth_len, crypt_off, crypt_len, icv_off, ivp);
+ return -EINVAL;
+ }
+
+ IOV_INIT(iov, data32, data_i, data_l);
+
+ CVMX_PREFETCH0(ivp);
+ CVMX_PREFETCH0(od->octo_enckey);
+
+ s = octeon_crypto_enable();
+
+ /* load AES Key */
+ CVMX_MT_AES_KEY(((uint64_t *) od->octo_enckey)[0], 0);
+ CVMX_MT_AES_KEY(((uint64_t *) od->octo_enckey)[1], 1);
+
+ if (od->octo_encklen == 16) {
+ CVMX_MT_AES_KEY(0x0, 2);
+ CVMX_MT_AES_KEY(0x0, 3);
+ } else if (od->octo_encklen == 24) {
+ CVMX_MT_AES_KEY(((uint64_t *) od->octo_enckey)[2], 2);
+ CVMX_MT_AES_KEY(0x0, 3);
+ } else if (od->octo_encklen == 32) {
+ CVMX_MT_AES_KEY(((uint64_t *) od->octo_enckey)[2], 2);
+ CVMX_MT_AES_KEY(((uint64_t *) od->octo_enckey)[3], 3);
+ } else {
+ octeon_crypto_disable(s);
+ dprintf("%s: Bad key length %d\n", __func__, od->octo_encklen);
+ return -EINVAL;
+ }
+ CVMX_MT_AES_KEYLENGTH(od->octo_encklen / 8 - 1);
+
+ CVMX_MT_AES_IV(((uint64_t *) ivp)[0], 0);
+ CVMX_MT_AES_IV(((uint64_t *) ivp)[1], 1);
+
+ /* Load MD5 IV */
+ CVMX_MT_HSH_IV(od->octo_hminner[0], 0);
+ CVMX_MT_HSH_IV(od->octo_hminner[1], 1);
+
+ while (crypt_off > 0 && auth_off > 0) {
+ IOV_CONSUME(iov, data32, data_i, data_l);
+ crypt_off -= 4;
+ auth_off -= 4;
+ }
+
+ while (crypt_len > 0 || auth_len > 0) {
+ uint32_t *pdata32[3];
+
+ pdata32[0] = data32;
+ mydata[0].data32[0] = *data32;
+ IOV_CONSUME(iov, data32, data_i, data_l);
+
+ pdata32[1] = data32;
+ mydata[0].data32[1] = *data32;
+ IOV_CONSUME(iov, data32, data_i, data_l);
+
+ pdata32[2] = data32;
+ mydata[1].data32[0] = *data32;
+ IOV_CONSUME(iov, data32, data_i, data_l);
+
+ mydata[1].data32[1] = *data32;
+
+
+ if (crypt_off <= 0) {
+ if (crypt_len > 0) {
+ CVMX_MT_AES_ENC_CBC0(*pdata);
+ CVMX_MT_AES_ENC_CBC1(*data);
+ CVMX_MF_AES_RESULT(*pdata, 0);
+ CVMX_MF_AES_RESULT(*data, 1);
+ crypt_len -= 16;
+ }
+ } else
+ crypt_off -= 16;
+
+ if (auth_off <= 0) {
+ if (auth_len > 0) {
+ CVM_LOAD_MD5_UNIT(*pdata, next);
+ CVM_LOAD_MD5_UNIT(*data, next);
+ auth_len -= 16;
+ }
+ } else
+ auth_off -= 16;
+
+ *pdata32[0] = mydata[0].data32[0];
+ *pdata32[1] = mydata[0].data32[1];
+ *pdata32[2] = mydata[1].data32[0];
+ *data32 = mydata[1].data32[1];
+
+ IOV_CONSUME(iov, data32, data_i, data_l);
+ }
+
+ /* finish the hash */
+ CVMX_PREFETCH0(od->octo_hmouter);
+#if 0
+ if (__predict_false(inplen)) {
+ uint64_t tmp = 0;
+ uint8_t *p = (uint8_t *) & tmp;
+ p[inplen] = 0x80;
+ do {
+ inplen--;
+ p[inplen] = ((uint8_t *) data)[inplen];
+ } while (inplen);
+ CVM_LOAD_MD5_UNIT(tmp, next);
+ } else {
+ CVM_LOAD_MD5_UNIT(0x8000000000000000ULL, next);
+ }
+#else
+ CVM_LOAD_MD5_UNIT(0x8000000000000000ULL, next);
+#endif
+
+ /* Finish Inner hash */
+ while (next != 7) {
+ CVM_LOAD_MD5_UNIT(((uint64_t) 0x0ULL), next);
+ }
+ CVMX_ES64(tmp1, ((alen + 64) << 3));
+ CVM_LOAD_MD5_UNIT(tmp1, next);
+
+ /* Get the inner hash of HMAC */
+ CVMX_MF_HSH_IV(tmp1, 0);
+ CVMX_MF_HSH_IV(tmp2, 1);
+
+ /* Initialize hash unit */
+ CVMX_MT_HSH_IV(od->octo_hmouter[0], 0);
+ CVMX_MT_HSH_IV(od->octo_hmouter[1], 1);
+
+ CVMX_MT_HSH_DAT(tmp1, 0);
+ CVMX_MT_HSH_DAT(tmp2, 1);
+ CVMX_MT_HSH_DAT(0x8000000000000000ULL, 2);
+ CVMX_MT_HSH_DATZ(3);
+ CVMX_MT_HSH_DATZ(4);
+ CVMX_MT_HSH_DATZ(5);
+ CVMX_MT_HSH_DATZ(6);
+ CVMX_ES64(tmp1, ((64 + 16) << 3));
+ CVMX_MT_HSH_STARTMD5(tmp1);
+
+ /* save the HMAC */
+ IOV_INIT(iov, data32, data_i, data_l);
+ while (icv_off > 0) {
+ IOV_CONSUME(iov, data32, data_i, data_l);
+ icv_off -= 4;
+ }
+ CVMX_MF_HSH_IV(tmp1, 0);
+ *data32 = (uint32_t) (tmp1 >> 32);
+ IOV_CONSUME(iov, data32, data_i, data_l);
+ *data32 = (uint32_t) tmp1;
+ IOV_CONSUME(iov, data32, data_i, data_l);
+ CVMX_MF_HSH_IV(tmp1, 1);
+ *data32 = (uint32_t) (tmp1 >> 32);
+
+ octeon_crypto_disable(s);
+ return 0;
+}
+
+int
+octo_aes_cbc_md5_decrypt(
+ struct octo_sess *od,
+ struct iovec *iov, size_t iovcnt, size_t iovlen,
+ int auth_off, int auth_len,
+ int crypt_off, int crypt_len,
+ int icv_off, uint8_t *ivp)
+{
+ register int next = 0;
+ union {
+ uint32_t data32[2];
+ uint64_t data64[1];
+ } mydata[2];
+ uint64_t *pdata = &mydata[0].data64[0];
+ uint64_t *data = &mydata[1].data64[0];
+ uint32_t *data32;
+ uint64_t tmp1, tmp2;
+ int data_i, data_l, alen = auth_len;
+ register_t s;
+
+ dprintf("%s()\n", __func__);
+
+ if (__predict_false(od == NULL || iov==NULL || iovlen==0 || ivp==NULL ||
+ (crypt_off & 0x3) || (crypt_off + crypt_len > iovlen) ||
+ (crypt_len & 0x7) ||
+ (auth_len & 0x7) ||
+ (auth_off & 0x3) || (auth_off + auth_len > iovlen))) {
+ dprintf("%s: Bad parameters od=%p iov=%p iovlen=%d "
+ "auth_off=%d auth_len=%d crypt_off=%d crypt_len=%d "
+ "icv_off=%d ivp=%p\n", __func__, od, iov, iovlen,
+ auth_off, auth_len, crypt_off, crypt_len, icv_off, ivp);
+ return -EINVAL;
+ }
+
+ IOV_INIT(iov, data32, data_i, data_l);
+
+ CVMX_PREFETCH0(ivp);
+ CVMX_PREFETCH0(od->octo_enckey);
+
+ s = octeon_crypto_enable();
+
+ /* load AES Key */
+ CVMX_MT_AES_KEY(((uint64_t *) od->octo_enckey)[0], 0);
+ CVMX_MT_AES_KEY(((uint64_t *) od->octo_enckey)[1], 1);
+
+ if (od->octo_encklen == 16) {
+ CVMX_MT_AES_KEY(0x0, 2);
+ CVMX_MT_AES_KEY(0x0, 3);
+ } else if (od->octo_encklen == 24) {
+ CVMX_MT_AES_KEY(((uint64_t *) od->octo_enckey)[2], 2);
+ CVMX_MT_AES_KEY(0x0, 3);
+ } else if (od->octo_encklen == 32) {
+ CVMX_MT_AES_KEY(((uint64_t *) od->octo_enckey)[2], 2);
+ CVMX_MT_AES_KEY(((uint64_t *) od->octo_enckey)[3], 3);
+ } else {
+ octeon_crypto_disable(s);
+ dprintf("%s: Bad key length %d\n", __func__, od->octo_encklen);
+ return -EINVAL;
+ }
+ CVMX_MT_AES_KEYLENGTH(od->octo_encklen / 8 - 1);
+
+ CVMX_MT_AES_IV(((uint64_t *) ivp)[0], 0);
+ CVMX_MT_AES_IV(((uint64_t *) ivp)[1], 1);
+
+ /* Load MD5 IV */
+ CVMX_MT_HSH_IV(od->octo_hminner[0], 0);
+ CVMX_MT_HSH_IV(od->octo_hminner[1], 1);
+
+ while (crypt_off > 0 && auth_off > 0) {
+ IOV_CONSUME(iov, data32, data_i, data_l);
+ crypt_off -= 4;
+ auth_off -= 4;
+ }
+
+ while (crypt_len > 0 || auth_len > 0) {
+ uint32_t *pdata32[3];
+
+ pdata32[0] = data32;
+ mydata[0].data32[0] = *data32;
+ IOV_CONSUME(iov, data32, data_i, data_l);
+ pdata32[1] = data32;
+ mydata[0].data32[1] = *data32;
+ IOV_CONSUME(iov, data32, data_i, data_l);
+ pdata32[2] = data32;
+ mydata[1].data32[0] = *data32;
+ IOV_CONSUME(iov, data32, data_i, data_l);
+ mydata[1].data32[1] = *data32;
+
+ if (auth_off <= 0) {
+ if (auth_len > 0) {
+ CVM_LOAD_MD5_UNIT(*pdata, next);
+ CVM_LOAD_MD5_UNIT(*data, next);
+ auth_len -= 16;
+ }
+ } else
+ auth_off -= 16;
+
+ if (crypt_off <= 0) {
+ if (crypt_len > 0) {
+ CVMX_MT_AES_DEC_CBC0(*pdata);
+ CVMX_MT_AES_DEC_CBC1(*data);
+ CVMX_MF_AES_RESULT(*pdata, 0);
+ CVMX_MF_AES_RESULT(*data, 1);
+ crypt_len -= 16;
+ }
+ } else
+ crypt_off -= 16;
+
+ *pdata32[0] = mydata[0].data32[0];
+ *pdata32[1] = mydata[0].data32[1];
+ *pdata32[2] = mydata[1].data32[0];
+ *data32 = mydata[1].data32[1];
+
+ IOV_CONSUME(iov, data32, data_i, data_l);
+ }
+
+ /* finish the hash */
+ CVMX_PREFETCH0(od->octo_hmouter);
+#if 0
+ if (__predict_false(inplen)) {
+ uint64_t tmp = 0;
+ uint8_t *p = (uint8_t *) & tmp;
+ p[inplen] = 0x80;
+ do {
+ inplen--;
+ p[inplen] = ((uint8_t *) data)[inplen];
+ } while (inplen);
+ CVM_LOAD_MD5_UNIT(tmp, next);
+ } else {
+ CVM_LOAD_MD5_UNIT(0x8000000000000000ULL, next);
+ }
+#else
+ CVM_LOAD_MD5_UNIT(0x8000000000000000ULL, next);
+#endif
+
+ /* Finish Inner hash */
+ while (next != 7) {
+ CVM_LOAD_MD5_UNIT(((uint64_t) 0x0ULL), next);
+ }
+ CVMX_ES64(tmp1, ((alen + 64) << 3));
+ CVM_LOAD_MD5_UNIT(tmp1, next);
+
+ /* Get the inner hash of HMAC */
+ CVMX_MF_HSH_IV(tmp1, 0);
+ CVMX_MF_HSH_IV(tmp2, 1);
+
+ /* Initialize hash unit */
+ CVMX_MT_HSH_IV(od->octo_hmouter[0], 0);
+ CVMX_MT_HSH_IV(od->octo_hmouter[1], 1);
+
+ CVMX_MT_HSH_DAT(tmp1, 0);
+ CVMX_MT_HSH_DAT(tmp2, 1);
+ CVMX_MT_HSH_DAT(0x8000000000000000ULL, 2);
+ CVMX_MT_HSH_DATZ(3);
+ CVMX_MT_HSH_DATZ(4);
+ CVMX_MT_HSH_DATZ(5);
+ CVMX_MT_HSH_DATZ(6);
+ CVMX_ES64(tmp1, ((64 + 16) << 3));
+ CVMX_MT_HSH_STARTMD5(tmp1);
+
+ /* save the HMAC */
+ IOV_INIT(iov, data32, data_i, data_l);
+ while (icv_off > 0) {
+ IOV_CONSUME(iov, data32, data_i, data_l);
+ icv_off -= 4;
+ }
+ CVMX_MF_HSH_IV(tmp1, 0);
+ *data32 = (uint32_t) (tmp1 >> 32);
+ IOV_CONSUME(iov, data32, data_i, data_l);
+ *data32 = (uint32_t) tmp1;
+ IOV_CONSUME(iov, data32, data_i, data_l);
+ CVMX_MF_HSH_IV(tmp1, 1);
+ *data32 = (uint32_t) (tmp1 >> 32);
+
+ octeon_crypto_disable(s);
+ return 0;
+}
+
+/****************************************************************************/
+/* AES SHA1 */
+
+int
+octo_aes_cbc_sha1_encrypt(
+ struct octo_sess *od,
+ struct iovec *iov, size_t iovcnt, size_t iovlen,
+ int auth_off, int auth_len,
+ int crypt_off, int crypt_len,
+ int icv_off, uint8_t *ivp)
+{
+ register int next = 0;
+ union {
+ uint32_t data32[2];
+ uint64_t data64[1];
+ } mydata[2];
+ uint64_t *pdata = &mydata[0].data64[0];
+ uint64_t *data = &mydata[1].data64[0];
+ uint32_t *data32;
+ uint64_t tmp1, tmp2, tmp3;
+ int data_i, data_l, alen = auth_len;
+ register_t s;
+
+ dprintf("%s()\n", __func__);
+
+ if (__predict_false(od == NULL || iov==NULL || iovlen==0 || ivp==NULL ||
+ (crypt_off & 0x3) || (crypt_off + crypt_len > iovlen) ||
+ (crypt_len & 0x7) ||
+ (auth_len & 0x7) ||
+ (auth_off & 0x3) || (auth_off + auth_len > iovlen))) {
+ dprintf("%s: Bad parameters od=%p iov=%p iovlen=%d "
+ "auth_off=%d auth_len=%d crypt_off=%d crypt_len=%d "
+ "icv_off=%d ivp=%p\n", __func__, od, iov, iovlen,
+ auth_off, auth_len, crypt_off, crypt_len, icv_off, ivp);
+ return -EINVAL;
+ }
+
+ IOV_INIT(iov, data32, data_i, data_l);
+
+ CVMX_PREFETCH0(ivp);
+ CVMX_PREFETCH0(od->octo_enckey);
+
+ s = octeon_crypto_enable();
+
+ /* load AES Key */
+ CVMX_MT_AES_KEY(((uint64_t *) od->octo_enckey)[0], 0);
+ CVMX_MT_AES_KEY(((uint64_t *) od->octo_enckey)[1], 1);
+
+ if (od->octo_encklen == 16) {
+ CVMX_MT_AES_KEY(0x0, 2);
+ CVMX_MT_AES_KEY(0x0, 3);
+ } else if (od->octo_encklen == 24) {
+ CVMX_MT_AES_KEY(((uint64_t *) od->octo_enckey)[2], 2);
+ CVMX_MT_AES_KEY(0x0, 3);
+ } else if (od->octo_encklen == 32) {
+ CVMX_MT_AES_KEY(((uint64_t *) od->octo_enckey)[2], 2);
+ CVMX_MT_AES_KEY(((uint64_t *) od->octo_enckey)[3], 3);
+ } else {
+ octeon_crypto_disable(s);
+ dprintf("%s: Bad key length %d\n", __func__, od->octo_encklen);
+ return -EINVAL;
+ }
+ CVMX_MT_AES_KEYLENGTH(od->octo_encklen / 8 - 1);
+
+ CVMX_MT_AES_IV(((uint64_t *) ivp)[0], 0);
+ CVMX_MT_AES_IV(((uint64_t *) ivp)[1], 1);
+
+ /* Load SHA IV */
+ CVMX_MT_HSH_IV(od->octo_hminner[0], 0);
+ CVMX_MT_HSH_IV(od->octo_hminner[1], 1);
+ CVMX_MT_HSH_IV(od->octo_hminner[2], 2);
+
+ while (crypt_off > 0 && auth_off > 0) {
+ IOV_CONSUME(iov, data32, data_i, data_l);
+ crypt_off -= 4;
+ auth_off -= 4;
+ }
+
+ while (crypt_len > 0 || auth_len > 0) {
+ uint32_t *pdata32[3];
+
+ pdata32[0] = data32;
+ mydata[0].data32[0] = *data32;
+ IOV_CONSUME(iov, data32, data_i, data_l);
+ pdata32[1] = data32;
+ mydata[0].data32[1] = *data32;
+ IOV_CONSUME(iov, data32, data_i, data_l);
+ pdata32[2] = data32;
+ mydata[1].data32[0] = *data32;
+ IOV_CONSUME(iov, data32, data_i, data_l);
+ mydata[1].data32[1] = *data32;
+
+
+ if (crypt_off <= 0) {
+ if (crypt_len > 0) {
+ CVMX_MT_AES_ENC_CBC0(*pdata);
+ CVMX_MT_AES_ENC_CBC1(*data);
+ CVMX_MF_AES_RESULT(*pdata, 0);
+ CVMX_MF_AES_RESULT(*data, 1);
+ crypt_len -= 16;
+ }
+ } else
+ crypt_off -= 16;
+
+ if (auth_off <= 0) {
+ if (auth_len > 0) {
+ CVM_LOAD_SHA_UNIT(*pdata, next);
+ CVM_LOAD_SHA_UNIT(*data, next);
+ auth_len -= 16;
+ }
+ } else
+ auth_off -= 16;
+
+ *pdata32[0] = mydata[0].data32[0];
+ *pdata32[1] = mydata[0].data32[1];
+ *pdata32[2] = mydata[1].data32[0];
+ *data32 = mydata[1].data32[1];
+
+ IOV_CONSUME(iov, data32, data_i, data_l);
+ }
+
+ /* finish the hash */
+ CVMX_PREFETCH0(od->octo_hmouter);
+#if 0
+ if (__predict_false(inplen)) {
+ uint64_t tmp = 0;
+ uint8_t *p = (uint8_t *) & tmp;
+ p[inplen] = 0x80;
+ do {
+ inplen--;
+ p[inplen] = ((uint8_t *) data)[inplen];
+ } while (inplen);
+ CVM_LOAD_SHA_UNIT(tmp, next);
+ } else {
+ CVM_LOAD_SHA_UNIT(0x8000000000000000ULL, next);
+ }
+#else
+ CVM_LOAD_SHA_UNIT(0x8000000000000000ULL, next);
+#endif
+
+ /* Finish Inner hash */
+ while (next != 7) {
+ CVM_LOAD_SHA_UNIT(((uint64_t) 0x0ULL), next);
+ }
+ CVM_LOAD_SHA_UNIT((uint64_t) ((alen + 64) << 3), next);
+
+ /* Get the inner hash of HMAC */
+ CVMX_MF_HSH_IV(tmp1, 0);
+ CVMX_MF_HSH_IV(tmp2, 1);
+ tmp3 = 0;
+ CVMX_MF_HSH_IV(tmp3, 2);
+
+ /* Initialize hash unit */
+ CVMX_MT_HSH_IV(od->octo_hmouter[0], 0);
+ CVMX_MT_HSH_IV(od->octo_hmouter[1], 1);
+ CVMX_MT_HSH_IV(od->octo_hmouter[2], 2);
+
+ CVMX_MT_HSH_DAT(tmp1, 0);
+ CVMX_MT_HSH_DAT(tmp2, 1);
+ tmp3 |= 0x0000000080000000;
+ CVMX_MT_HSH_DAT(tmp3, 2);
+ CVMX_MT_HSH_DATZ(3);
+ CVMX_MT_HSH_DATZ(4);
+ CVMX_MT_HSH_DATZ(5);
+ CVMX_MT_HSH_DATZ(6);
+ CVMX_MT_HSH_STARTSHA((uint64_t) ((64 + 20) << 3));
+
+ /* finish the hash */
+ CVMX_PREFETCH0(od->octo_hmouter);
+#if 0
+ if (__predict_false(inplen)) {
+ uint64_t tmp = 0;
+ uint8_t *p = (uint8_t *) & tmp;
+ p[inplen] = 0x80;
+ do {
+ inplen--;
+ p[inplen] = ((uint8_t *) data)[inplen];
+ } while (inplen);
+ CVM_LOAD_MD5_UNIT(tmp, next);
+ } else {
+ CVM_LOAD_MD5_UNIT(0x8000000000000000ULL, next);
+ }
+#else
+ CVM_LOAD_MD5_UNIT(0x8000000000000000ULL, next);
+#endif
+
+ /* save the HMAC */
+ IOV_INIT(iov, data32, data_i, data_l);
+ while (icv_off > 0) {
+ IOV_CONSUME(iov, data32, data_i, data_l);
+ icv_off -= 4;
+ }
+ CVMX_MF_HSH_IV(tmp1, 0);
+ *data32 = (uint32_t) (tmp1 >> 32);
+ IOV_CONSUME(iov, data32, data_i, data_l);
+ *data32 = (uint32_t) tmp1;
+ IOV_CONSUME(iov, data32, data_i, data_l);
+ CVMX_MF_HSH_IV(tmp1, 1);
+ *data32 = (uint32_t) (tmp1 >> 32);
+
+ octeon_crypto_disable(s);
+ return 0;
+}
+
+int
+octo_aes_cbc_sha1_decrypt(
+ struct octo_sess *od,
+ struct iovec *iov, size_t iovcnt, size_t iovlen,
+ int auth_off, int auth_len,
+ int crypt_off, int crypt_len,
+ int icv_off, uint8_t *ivp)
+{
+ register int next = 0;
+ union {
+ uint32_t data32[2];
+ uint64_t data64[1];
+ } mydata[2];
+ uint64_t *pdata = &mydata[0].data64[0];
+ uint64_t *data = &mydata[1].data64[0];
+ uint32_t *data32;
+ uint64_t tmp1, tmp2, tmp3;
+ int data_i, data_l, alen = auth_len;
+ register_t s;
+
+ dprintf("%s()\n", __func__);
+
+ if (__predict_false(od == NULL || iov==NULL || iovlen==0 || ivp==NULL ||
+ (crypt_off & 0x3) || (crypt_off + crypt_len > iovlen) ||
+ (crypt_len & 0x7) ||
+ (auth_len & 0x7) ||
+ (auth_off & 0x3) || (auth_off + auth_len > iovlen))) {
+ dprintf("%s: Bad parameters od=%p iov=%p iovlen=%d "
+ "auth_off=%d auth_len=%d crypt_off=%d crypt_len=%d "
+ "icv_off=%d ivp=%p\n", __func__, od, iov, iovlen,
+ auth_off, auth_len, crypt_off, crypt_len, icv_off, ivp);
+ return -EINVAL;
+ }
+
+ IOV_INIT(iov, data32, data_i, data_l);
+
+ CVMX_PREFETCH0(ivp);
+ CVMX_PREFETCH0(od->octo_enckey);
+
+ s = octeon_crypto_enable();
+
+ /* load AES Key */
+ CVMX_MT_AES_KEY(((uint64_t *) od->octo_enckey)[0], 0);
+ CVMX_MT_AES_KEY(((uint64_t *) od->octo_enckey)[1], 1);
+
+ if (od->octo_encklen == 16) {
+ CVMX_MT_AES_KEY(0x0, 2);
+ CVMX_MT_AES_KEY(0x0, 3);
+ } else if (od->octo_encklen == 24) {
+ CVMX_MT_AES_KEY(((uint64_t *) od->octo_enckey)[2], 2);
+ CVMX_MT_AES_KEY(0x0, 3);
+ } else if (od->octo_encklen == 32) {
+ CVMX_MT_AES_KEY(((uint64_t *) od->octo_enckey)[2], 2);
+ CVMX_MT_AES_KEY(((uint64_t *) od->octo_enckey)[3], 3);
+ } else {
+ octeon_crypto_disable(s);
+ dprintf("%s: Bad key length %d\n", __func__, od->octo_encklen);
+ return -EINVAL;
+ }
+ CVMX_MT_AES_KEYLENGTH(od->octo_encklen / 8 - 1);
+
+ CVMX_MT_AES_IV(((uint64_t *) ivp)[0], 0);
+ CVMX_MT_AES_IV(((uint64_t *) ivp)[1], 1);
+
+ /* Load MD5 IV */
+ CVMX_MT_HSH_IV(od->octo_hminner[0], 0);
+ CVMX_MT_HSH_IV(od->octo_hminner[1], 1);
+ CVMX_MT_HSH_IV(od->octo_hminner[2], 2);
+
+ while (crypt_off > 0 && auth_off > 0) {
+ IOV_CONSUME(iov, data32, data_i, data_l);
+ crypt_off -= 4;
+ auth_off -= 4;
+ }
+
+ while (crypt_len > 0 || auth_len > 0) {
+ uint32_t *pdata32[3];
+
+ pdata32[0] = data32;
+ mydata[0].data32[0] = *data32;
+ IOV_CONSUME(iov, data32, data_i, data_l);
+ pdata32[1] = data32;
+ mydata[0].data32[1] = *data32;
+ IOV_CONSUME(iov, data32, data_i, data_l);
+ pdata32[2] = data32;
+ mydata[1].data32[0] = *data32;
+ IOV_CONSUME(iov, data32, data_i, data_l);
+ mydata[1].data32[1] = *data32;
+
+ if (auth_off <= 0) {
+ if (auth_len > 0) {
+ CVM_LOAD_SHA_UNIT(*pdata, next);
+ CVM_LOAD_SHA_UNIT(*data, next);
+ auth_len -= 16;
+ }
+ } else
+ auth_off -= 16;
+
+ if (crypt_off <= 0) {
+ if (crypt_len > 0) {
+ CVMX_MT_AES_DEC_CBC0(*pdata);
+ CVMX_MT_AES_DEC_CBC1(*data);
+ CVMX_MF_AES_RESULT(*pdata, 0);
+ CVMX_MF_AES_RESULT(*data, 1);
+ crypt_len -= 16;
+ }
+ } else
+ crypt_off -= 16;
+
+ *pdata32[0] = mydata[0].data32[0];
+ *pdata32[1] = mydata[0].data32[1];
+ *pdata32[2] = mydata[1].data32[0];
+ *data32 = mydata[1].data32[1];
+
+ IOV_CONSUME(iov, data32, data_i, data_l);
+ }
+
+ /* finish the hash */
+ CVMX_PREFETCH0(od->octo_hmouter);
+#if 0
+ if (__predict_false(inplen)) {
+ uint64_t tmp = 0;
+ uint8_t *p = (uint8_t *) & tmp;
+ p[inplen] = 0x80;
+ do {
+ inplen--;
+ p[inplen] = ((uint8_t *) data)[inplen];
+ } while (inplen);
+ CVM_LOAD_SHA_UNIT(tmp, next);
+ } else {
+ CVM_LOAD_SHA_UNIT(0x8000000000000000ULL, next);
+ }
+#else
+ CVM_LOAD_SHA_UNIT(0x8000000000000000ULL, next);
+#endif
+
+ /* Finish Inner hash */
+ while (next != 7) {
+ CVM_LOAD_SHA_UNIT(((uint64_t) 0x0ULL), next);
+ }
+ CVM_LOAD_SHA_UNIT((uint64_t) ((alen + 64) << 3), next);
+
+ /* Get the inner hash of HMAC */
+ CVMX_MF_HSH_IV(tmp1, 0);
+ CVMX_MF_HSH_IV(tmp2, 1);
+ tmp3 = 0;
+ CVMX_MF_HSH_IV(tmp3, 2);
+
+ /* Initialize hash unit */
+ CVMX_MT_HSH_IV(od->octo_hmouter[0], 0);
+ CVMX_MT_HSH_IV(od->octo_hmouter[1], 1);
+ CVMX_MT_HSH_IV(od->octo_hmouter[2], 2);
+
+ CVMX_MT_HSH_DAT(tmp1, 0);
+ CVMX_MT_HSH_DAT(tmp2, 1);
+ tmp3 |= 0x0000000080000000;
+ CVMX_MT_HSH_DAT(tmp3, 2);
+ CVMX_MT_HSH_DATZ(3);
+ CVMX_MT_HSH_DATZ(4);
+ CVMX_MT_HSH_DATZ(5);
+ CVMX_MT_HSH_DATZ(6);
+ CVMX_MT_HSH_STARTSHA((uint64_t) ((64 + 20) << 3));
+
+ /* finish the hash */
+ CVMX_PREFETCH0(od->octo_hmouter);
+#if 0
+ if (__predict_false(inplen)) {
+ uint64_t tmp = 0;
+ uint8_t *p = (uint8_t *) & tmp;
+ p[inplen] = 0x80;
+ do {
+ inplen--;
+ p[inplen] = ((uint8_t *) data)[inplen];
+ } while (inplen);
+ CVM_LOAD_MD5_UNIT(tmp, next);
+ } else {
+ CVM_LOAD_MD5_UNIT(0x8000000000000000ULL, next);
+ }
+#else
+ CVM_LOAD_MD5_UNIT(0x8000000000000000ULL, next);
+#endif
+
+ /* save the HMAC */
+ IOV_INIT(iov, data32, data_i, data_l);
+ while (icv_off > 0) {
+ IOV_CONSUME(iov, data32, data_i, data_l);
+ icv_off -= 4;
+ }
+ CVMX_MF_HSH_IV(tmp1, 0);
+ *data32 = (uint32_t) (tmp1 >> 32);
+ IOV_CONSUME(iov, data32, data_i, data_l);
+ *data32 = (uint32_t) tmp1;
+ IOV_CONSUME(iov, data32, data_i, data_l);
+ CVMX_MF_HSH_IV(tmp1, 1);
+ *data32 = (uint32_t) (tmp1 >> 32);
+
+ octeon_crypto_disable(s);
+ return 0;
+}
+
+/****************************************************************************/
diff --git a/sys/mips/cavium/cryptocteon/cryptocteon.c b/sys/mips/cavium/cryptocteon/cryptocteon.c
new file mode 100644
index 000000000000..16a0bb161d3d
--- /dev/null
+++ b/sys/mips/cavium/cryptocteon/cryptocteon.c
@@ -0,0 +1,526 @@
+/*
+ * Octeon Crypto for OCF
+ *
+ * Written by David McCullough <david_mccullough@securecomputing.com>
+ * Copyright (C) 2009 David McCullough
+ *
+ * LICENSE TERMS
+ *
+ * The free distribution and use of this software in both source and binary
+ * form is allowed (with or without changes) provided that:
+ *
+ * 1. distributions of this source code include the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ *
+ * 2. distributions in binary form include the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other associated materials;
+ *
+ * 3. the copyright holder's name is not used to endorse products
+ * built using this software without specific written permission.
+ *
+ * DISCLAIMER
+ *
+ * This software is provided 'as is' with no explicit or implied warranties
+ * in respect of its properties, including, but not limited to, correctness
+ * and/or fitness for purpose.
+ * ---------------------------------------------------------------------------
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/uio.h>
+
+#include <opencrypto/cryptodev.h>
+
+#include <contrib/octeon-sdk/cvmx.h>
+
+#include <mips/cavium/cryptocteon/cryptocteonvar.h>
+
+#include "cryptodev_if.h"
+
+struct cryptocteon_softc {
+ int32_t sc_cid; /* opencrypto id */
+ struct octo_sess **sc_sessions;
+ uint32_t sc_sesnum;
+};
+
+int cryptocteon_debug = 0;
+TUNABLE_INT("hw.cryptocteon.debug", &cryptocteon_debug);
+
+static void cryptocteon_identify(driver_t *, device_t);
+static int cryptocteon_probe(device_t);
+static int cryptocteon_attach(device_t);
+
+static int cryptocteon_process(device_t, struct cryptop *, int);
+static int cryptocteon_newsession(device_t, u_int32_t *, struct cryptoini *);
+static int cryptocteon_freesession(device_t, u_int64_t);
+
+static void
+cryptocteon_identify(driver_t *drv, device_t parent)
+{
+ if (octeon_has_feature(OCTEON_FEATURE_CRYPTO))
+ BUS_ADD_CHILD(parent, 0, "cryptocteon", 0);
+}
+
+static int
+cryptocteon_probe(device_t dev)
+{
+ device_set_desc(dev, "Octeon Secure Coprocessor");
+ return (0);
+}
+
+static int
+cryptocteon_attach(device_t dev)
+{
+ struct cryptocteon_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ sc->sc_cid = crypto_get_driverid(dev, CRYPTOCAP_F_HARDWARE | CRYPTOCAP_F_SYNC);
+ if (sc->sc_cid < 0) {
+ device_printf(dev, "crypto_get_driverid ret %d\n", sc->sc_cid);
+ return (ENXIO);
+ }
+
+ crypto_register(sc->sc_cid, CRYPTO_MD5_HMAC, 0, 0);
+ crypto_register(sc->sc_cid, CRYPTO_SHA1_HMAC, 0, 0);
+ crypto_register(sc->sc_cid, CRYPTO_DES_CBC, 0, 0);
+ crypto_register(sc->sc_cid, CRYPTO_3DES_CBC, 0, 0);
+ crypto_register(sc->sc_cid, CRYPTO_AES_CBC, 0, 0);
+
+ return (0);
+}
+
+/*
+ * Generate a new octo session. We artifically limit it to a single
+ * hash/cipher or hash-cipher combo just to make it easier, most callers
+ * do not expect more than this anyway.
+ */
+static int
+cryptocteon_newsession(device_t dev, u_int32_t *sid, struct cryptoini *cri)
+{
+ struct cryptoini *c, *encini = NULL, *macini = NULL;
+ struct cryptocteon_softc *sc;
+ struct octo_sess **ocd;
+ int i;
+
+ sc = device_get_softc(dev);
+
+ if (sid == NULL || cri == NULL || sc == NULL)
+ return (EINVAL);
+
+ /*
+ * To keep it simple, we only handle hash, cipher or hash/cipher in a
+ * session, you cannot currently do multiple ciphers/hashes in one
+ * session even though it would be possibel to code this driver to
+ * handle it.
+ */
+ for (i = 0, c = cri; c && i < 2; i++) {
+ if (c->cri_alg == CRYPTO_MD5_HMAC ||
+ c->cri_alg == CRYPTO_SHA1_HMAC ||
+ c->cri_alg == CRYPTO_NULL_HMAC) {
+ if (macini) {
+ break;
+ }
+ macini = c;
+ }
+ if (c->cri_alg == CRYPTO_DES_CBC ||
+ c->cri_alg == CRYPTO_3DES_CBC ||
+ c->cri_alg == CRYPTO_AES_CBC ||
+ c->cri_alg == CRYPTO_NULL_CBC) {
+ if (encini) {
+ break;
+ }
+ encini = c;
+ }
+ c = c->cri_next;
+ }
+ if (!macini && !encini) {
+ dprintf("%s,%d - EINVAL bad cipher/hash or combination\n",
+ __FILE__, __LINE__);
+ return EINVAL;
+ }
+ if (c) {
+ dprintf("%s,%d - EINVAL cannot handle chained cipher/hash combos\n",
+ __FILE__, __LINE__);
+ return EINVAL;
+ }
+
+ /*
+ * So we have something we can do, lets setup the session
+ */
+
+ if (sc->sc_sessions) {
+ for (i = 1; i < sc->sc_sesnum; i++)
+ if (sc->sc_sessions[i] == NULL)
+ break;
+ } else
+ i = 1; /* NB: to silence compiler warning */
+
+ if (sc->sc_sessions == NULL || i == sc->sc_sesnum) {
+ if (sc->sc_sessions == NULL) {
+ i = 1; /* We leave sc->sc_sessions[0] empty */
+ sc->sc_sesnum = CRYPTO_SW_SESSIONS;
+ } else
+ sc->sc_sesnum *= 2;
+
+ ocd = malloc(sc->sc_sesnum * sizeof(struct octo_sess *),
+ M_DEVBUF, M_NOWAIT | M_ZERO);
+ if (ocd == NULL) {
+ /* Reset session number */
+ if (sc->sc_sesnum == CRYPTO_SW_SESSIONS)
+ sc->sc_sesnum = 0;
+ else
+ sc->sc_sesnum /= 2;
+ dprintf("%s,%d: ENOBUFS\n", __FILE__, __LINE__);
+ return ENOBUFS;
+ }
+
+ /* Copy existing sessions */
+ if (sc->sc_sessions) {
+ memcpy(ocd, sc->sc_sessions,
+ (sc->sc_sesnum / 2) * sizeof(struct octo_sess *));
+ free(sc->sc_sessions, M_DEVBUF);
+ }
+
+ sc->sc_sessions = ocd;
+ }
+
+ ocd = &sc->sc_sessions[i];
+ *sid = i;
+
+ *ocd = malloc(sizeof(struct octo_sess), M_DEVBUF, M_NOWAIT | M_ZERO);
+ if (*ocd == NULL) {
+ cryptocteon_freesession(NULL, i);
+ dprintf("%s,%d: ENOBUFS\n", __FILE__, __LINE__);
+ return ENOBUFS;
+ }
+
+ if (encini && encini->cri_key) {
+ (*ocd)->octo_encklen = (encini->cri_klen + 7) / 8;
+ memcpy((*ocd)->octo_enckey, encini->cri_key, (*ocd)->octo_encklen);
+ }
+
+ if (macini && macini->cri_key) {
+ (*ocd)->octo_macklen = (macini->cri_klen + 7) / 8;
+ memcpy((*ocd)->octo_mackey, macini->cri_key, (*ocd)->octo_macklen);
+ }
+
+ (*ocd)->octo_mlen = 0;
+ if (encini && encini->cri_mlen)
+ (*ocd)->octo_mlen = encini->cri_mlen;
+ else if (macini && macini->cri_mlen)
+ (*ocd)->octo_mlen = macini->cri_mlen;
+ else
+ (*ocd)->octo_mlen = 12;
+
+ /*
+ * point c at the enc if it exists, otherwise the mac
+ */
+ c = encini ? encini : macini;
+
+ switch (c->cri_alg) {
+ case CRYPTO_DES_CBC:
+ case CRYPTO_3DES_CBC:
+ (*ocd)->octo_ivsize = 8;
+ switch (macini ? macini->cri_alg : -1) {
+ case CRYPTO_MD5_HMAC:
+ (*ocd)->octo_encrypt = octo_des_cbc_md5_encrypt;
+ (*ocd)->octo_decrypt = octo_des_cbc_md5_decrypt;
+ octo_calc_hash(0, macini->cri_key, (*ocd)->octo_hminner,
+ (*ocd)->octo_hmouter);
+ break;
+ case CRYPTO_SHA1_HMAC:
+ (*ocd)->octo_encrypt = octo_des_cbc_sha1_encrypt;
+ (*ocd)->octo_decrypt = octo_des_cbc_sha1_encrypt;
+ octo_calc_hash(1, macini->cri_key, (*ocd)->octo_hminner,
+ (*ocd)->octo_hmouter);
+ break;
+ case -1:
+ (*ocd)->octo_encrypt = octo_des_cbc_encrypt;
+ (*ocd)->octo_decrypt = octo_des_cbc_decrypt;
+ break;
+ default:
+ cryptocteon_freesession(NULL, i);
+ dprintf("%s,%d: EINVALn", __FILE__, __LINE__);
+ return EINVAL;
+ }
+ break;
+ case CRYPTO_AES_CBC:
+ (*ocd)->octo_ivsize = 16;
+ switch (macini ? macini->cri_alg : -1) {
+ case CRYPTO_MD5_HMAC:
+ (*ocd)->octo_encrypt = octo_aes_cbc_md5_encrypt;
+ (*ocd)->octo_decrypt = octo_aes_cbc_md5_decrypt;
+ octo_calc_hash(0, macini->cri_key, (*ocd)->octo_hminner,
+ (*ocd)->octo_hmouter);
+ break;
+ case CRYPTO_SHA1_HMAC:
+ (*ocd)->octo_encrypt = octo_aes_cbc_sha1_encrypt;
+ (*ocd)->octo_decrypt = octo_aes_cbc_sha1_decrypt;
+ octo_calc_hash(1, macini->cri_key, (*ocd)->octo_hminner,
+ (*ocd)->octo_hmouter);
+ break;
+ case -1:
+ (*ocd)->octo_encrypt = octo_aes_cbc_encrypt;
+ (*ocd)->octo_decrypt = octo_aes_cbc_decrypt;
+ break;
+ default:
+ cryptocteon_freesession(NULL, i);
+ dprintf("%s,%d: EINVALn", __FILE__, __LINE__);
+ return EINVAL;
+ }
+ break;
+ case CRYPTO_MD5_HMAC:
+ (*ocd)->octo_encrypt = octo_null_md5_encrypt;
+ (*ocd)->octo_decrypt = octo_null_md5_encrypt;
+ octo_calc_hash(0, macini->cri_key, (*ocd)->octo_hminner,
+ (*ocd)->octo_hmouter);
+ break;
+ case CRYPTO_SHA1_HMAC:
+ (*ocd)->octo_encrypt = octo_null_sha1_encrypt;
+ (*ocd)->octo_decrypt = octo_null_sha1_encrypt;
+ octo_calc_hash(1, macini->cri_key, (*ocd)->octo_hminner,
+ (*ocd)->octo_hmouter);
+ break;
+ default:
+ cryptocteon_freesession(NULL, i);
+ dprintf("%s,%d: EINVALn", __FILE__, __LINE__);
+ return EINVAL;
+ }
+
+ (*ocd)->octo_encalg = encini ? encini->cri_alg : -1;
+ (*ocd)->octo_macalg = macini ? macini->cri_alg : -1;
+
+ return 0;
+}
+
+/*
+ * Free a session.
+ */
+static int
+cryptocteon_freesession(device_t dev, u_int64_t tid)
+{
+ struct cryptocteon_softc *sc;
+ u_int32_t sid = CRYPTO_SESID2LID(tid);
+
+ sc = device_get_softc(dev);
+
+ if (sc == NULL)
+ return (EINVAL);
+
+ if (sid > sc->sc_sesnum || sc->sc_sessions == NULL ||
+ sc->sc_sessions[sid] == NULL)
+ return (EINVAL);
+
+ /* Silently accept and return */
+ if (sid == 0)
+ return(0);
+
+ if (sc->sc_sessions[sid])
+ free(sc->sc_sessions[sid], M_DEVBUF);
+ sc->sc_sessions[sid] = NULL;
+ return 0;
+}
+
+/*
+ * Process a request.
+ */
+static int
+cryptocteon_process(device_t dev, struct cryptop *crp, int hint)
+{
+ struct cryptodesc *crd;
+ struct octo_sess *od;
+ u_int32_t lid;
+ size_t iovcnt, iovlen;
+ struct mbuf *m = NULL;
+ struct uio *uiop = NULL;
+ struct cryptodesc *enccrd = NULL, *maccrd = NULL;
+ unsigned char *ivp = NULL;
+ unsigned char iv_data[HASH_MAX_LEN];
+ int auth_off = 0, auth_len = 0, crypt_off = 0, crypt_len = 0, icv_off = 0;
+ struct cryptocteon_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ if (sc == NULL || crp == NULL)
+ return EINVAL;
+
+ crp->crp_etype = 0;
+
+ if (crp->crp_desc == NULL || crp->crp_buf == NULL) {
+ dprintf("%s,%d: EINVAL\n", __FILE__, __LINE__);
+ crp->crp_etype = EINVAL;
+ goto done;
+ }
+
+ lid = crp->crp_sid & 0xffffffff;
+ if (lid >= sc->sc_sesnum || lid == 0 || sc->sc_sessions == NULL ||
+ sc->sc_sessions[lid] == NULL) {
+ crp->crp_etype = ENOENT;
+ dprintf("%s,%d: ENOENT\n", __FILE__, __LINE__);
+ goto done;
+ }
+ od = sc->sc_sessions[lid];
+
+ /*
+ * do some error checking outside of the loop for m and IOV processing
+ * this leaves us with valid m or uiop pointers for later
+ */
+ if (crp->crp_flags & CRYPTO_F_IMBUF) {
+ unsigned frags;
+
+ m = (struct mbuf *) crp->crp_buf;
+ for (frags = 0; m != NULL; frags++)
+ m = m->m_next;
+
+ if (frags >= UIO_MAXIOV) {
+ printf("%s,%d: %d frags > UIO_MAXIOV", __FILE__, __LINE__, frags);
+ goto done;
+ }
+
+ m = (struct mbuf *) crp->crp_buf;
+ } else if (crp->crp_flags & CRYPTO_F_IOV) {
+ uiop = (struct uio *) crp->crp_buf;
+ if (uiop->uio_iovcnt > UIO_MAXIOV) {
+ printf("%s,%d: %d uio_iovcnt > UIO_MAXIOV", __FILE__, __LINE__,
+ uiop->uio_iovcnt);
+ goto done;
+ }
+ }
+
+ /* point our enccrd and maccrd appropriately */
+ crd = crp->crp_desc;
+ if (crd->crd_alg == od->octo_encalg) enccrd = crd;
+ if (crd->crd_alg == od->octo_macalg) maccrd = crd;
+ crd = crd->crd_next;
+ if (crd) {
+ if (crd->crd_alg == od->octo_encalg) enccrd = crd;
+ if (crd->crd_alg == od->octo_macalg) maccrd = crd;
+ crd = crd->crd_next;
+ }
+ if (crd) {
+ crp->crp_etype = EINVAL;
+ dprintf("%s,%d: ENOENT - descriptors do not match session\n",
+ __FILE__, __LINE__);
+ goto done;
+ }
+
+ if (enccrd) {
+ if (enccrd->crd_flags & CRD_F_IV_EXPLICIT) {
+ ivp = enccrd->crd_iv;
+ } else {
+ ivp = iv_data;
+ crypto_copydata(crp->crp_flags, crp->crp_buf,
+ enccrd->crd_inject, od->octo_ivsize, (caddr_t) ivp);
+ }
+
+ if (maccrd) {
+ auth_off = maccrd->crd_skip;
+ auth_len = maccrd->crd_len;
+ icv_off = maccrd->crd_inject;
+ }
+
+ crypt_off = enccrd->crd_skip;
+ crypt_len = enccrd->crd_len;
+ } else { /* if (maccrd) */
+ auth_off = maccrd->crd_skip;
+ auth_len = maccrd->crd_len;
+ icv_off = maccrd->crd_inject;
+ }
+
+ /*
+ * setup the I/O vector to cover the buffer
+ */
+ if (crp->crp_flags & CRYPTO_F_IMBUF) {
+ iovcnt = 0;
+ iovlen = 0;
+
+ while (m != NULL) {
+ od->octo_iov[iovcnt].iov_base = mtod(m, void *);
+ od->octo_iov[iovcnt].iov_len = m->m_len;
+
+ m = m->m_next;
+ iovlen += od->octo_iov[iovcnt++].iov_len;
+ }
+ } else if (crp->crp_flags & CRYPTO_F_IOV) {
+ iovlen = 0;
+ for (iovcnt = 0; iovcnt < uiop->uio_iovcnt; iovcnt++) {
+ od->octo_iov[iovcnt].iov_base = uiop->uio_iov[iovcnt].iov_base;
+ od->octo_iov[iovcnt].iov_len = uiop->uio_iov[iovcnt].iov_len;
+
+ iovlen += od->octo_iov[iovcnt].iov_len;
+ }
+ } else {
+ iovlen = crp->crp_ilen;
+ od->octo_iov[0].iov_base = crp->crp_buf;
+ od->octo_iov[0].iov_len = crp->crp_ilen;
+ iovcnt = 1;
+ }
+
+
+ /*
+ * setup a new explicit key
+ */
+ if (enccrd) {
+ if (enccrd->crd_flags & CRD_F_KEY_EXPLICIT) {
+ od->octo_encklen = (enccrd->crd_klen + 7) / 8;
+ memcpy(od->octo_enckey, enccrd->crd_key, od->octo_encklen);
+ }
+ }
+ if (maccrd) {
+ if (maccrd->crd_flags & CRD_F_KEY_EXPLICIT) {
+ od->octo_macklen = (maccrd->crd_klen + 7) / 8;
+ memcpy(od->octo_mackey, maccrd->crd_key, od->octo_macklen);
+ od->octo_mackey_set = 0;
+ }
+ if (!od->octo_mackey_set) {
+ octo_calc_hash(maccrd->crd_alg == CRYPTO_MD5_HMAC ? 0 : 1,
+ maccrd->crd_key, od->octo_hminner, od->octo_hmouter);
+ od->octo_mackey_set = 1;
+ }
+ }
+
+
+ if (!enccrd || (enccrd->crd_flags & CRD_F_ENCRYPT))
+ (*od->octo_encrypt)(od, od->octo_iov, iovcnt, iovlen,
+ auth_off, auth_len, crypt_off, crypt_len, icv_off, ivp);
+ else
+ (*od->octo_decrypt)(od, od->octo_iov, iovcnt, iovlen,
+ auth_off, auth_len, crypt_off, crypt_len, icv_off, ivp);
+
+done:
+ crypto_done(crp);
+ return 0;
+}
+
+static device_method_t cryptocteon_methods[] = {
+ /* device methods */
+ DEVMETHOD(device_identify, cryptocteon_identify),
+ DEVMETHOD(device_probe, cryptocteon_probe),
+ DEVMETHOD(device_attach, cryptocteon_attach),
+
+ /* crypto device methods */
+ DEVMETHOD(cryptodev_newsession, cryptocteon_newsession),
+ DEVMETHOD(cryptodev_freesession,cryptocteon_freesession),
+ DEVMETHOD(cryptodev_process, cryptocteon_process),
+
+ { 0, 0 }
+};
+
+static driver_t cryptocteon_driver = {
+ "cryptocteon",
+ cryptocteon_methods,
+ sizeof (struct cryptocteon_softc),
+};
+static devclass_t cryptocteon_devclass;
+DRIVER_MODULE(cryptocteon, nexus, cryptocteon_driver, cryptocteon_devclass, 0, 0);
diff --git a/sys/mips/cavium/cryptocteon/cryptocteonvar.h b/sys/mips/cavium/cryptocteon/cryptocteonvar.h
new file mode 100644
index 000000000000..e722298d899e
--- /dev/null
+++ b/sys/mips/cavium/cryptocteon/cryptocteonvar.h
@@ -0,0 +1,94 @@
+/*
+ * Octeon Crypto for OCF
+ *
+ * Written by David McCullough <david_mccullough@securecomputing.com>
+ * Copyright (C) 2009 David McCullough
+ *
+ * LICENSE TERMS
+ *
+ * The free distribution and use of this software in both source and binary
+ * form is allowed (with or without changes) provided that:
+ *
+ * 1. distributions of this source code include the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ *
+ * 2. distributions in binary form include the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other associated materials;
+ *
+ * 3. the copyright holder's name is not used to endorse products
+ * built using this software without specific written permission.
+ *
+ * DISCLAIMER
+ *
+ * This software is provided 'as is' with no explicit or implied warranties
+ * in respect of its properties, including, but not limited to, correctness
+ * and/or fitness for purpose.
+ * ---------------------------------------------------------------------------
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MIPS_CAVIUM_CRYPTOCTEON_CRYPTOCTEONVAR_H_
+#define _MIPS_CAVIUM_CRYPTOCTEON_CRYPTOCTEONVAR_H_
+
+struct octo_sess;
+
+typedef int octo_encrypt_t(struct octo_sess *od, struct iovec *iov, size_t iovcnt, size_t iovlen, int auth_off, int auth_len, int crypt_off, int crypt_len, int icv_off, uint8_t *ivp);
+typedef int octo_decrypt_t(struct octo_sess *od, struct iovec *iov, size_t iovcnt, size_t iovlen, int auth_off, int auth_len, int crypt_off, int crypt_len, int icv_off, uint8_t *ivp);
+
+struct octo_sess {
+ int octo_encalg;
+ #define MAX_CIPHER_KEYLEN 64
+ char octo_enckey[MAX_CIPHER_KEYLEN];
+ int octo_encklen;
+
+ int octo_macalg;
+ #define MAX_HASH_KEYLEN 64
+ char octo_mackey[MAX_HASH_KEYLEN];
+ int octo_macklen;
+ int octo_mackey_set;
+
+ int octo_mlen;
+ int octo_ivsize;
+
+ octo_encrypt_t *octo_encrypt;
+ octo_decrypt_t *octo_decrypt;
+
+ uint64_t octo_hminner[3];
+ uint64_t octo_hmouter[3];
+
+ struct iovec octo_iov[UIO_MAXIOV];
+};
+
+#define dprintf(fmt, ...) \
+ do { \
+ if (cryptocteon_debug) \
+ printf("%s: " fmt, __func__, ## __VA_ARGS__); \
+ } while (0)
+
+extern int cryptocteon_debug;
+
+void octo_calc_hash(uint8_t, unsigned char *, uint64_t *, uint64_t *);
+
+/* XXX Actually just hashing functions, not encryption. */
+octo_encrypt_t octo_null_md5_encrypt;
+octo_encrypt_t octo_null_sha1_encrypt;
+
+octo_encrypt_t octo_des_cbc_encrypt;
+octo_encrypt_t octo_des_cbc_md5_encrypt;
+octo_encrypt_t octo_des_cbc_sha1_encrypt;
+
+octo_decrypt_t octo_des_cbc_decrypt;
+octo_decrypt_t octo_des_cbc_md5_decrypt;
+octo_decrypt_t octo_des_cbc_sha1_decrypt;
+
+octo_encrypt_t octo_aes_cbc_encrypt;
+octo_encrypt_t octo_aes_cbc_md5_encrypt;
+octo_encrypt_t octo_aes_cbc_sha1_encrypt;
+
+octo_decrypt_t octo_aes_cbc_decrypt;
+octo_decrypt_t octo_aes_cbc_md5_decrypt;
+octo_decrypt_t octo_aes_cbc_sha1_decrypt;
+
+#endif /* !_MIPS_CAVIUM_CRYPTOCTEON_CRYPTOCTEONVAR_H_ */
diff --git a/sys/mips/cavium/cvmx_config.h b/sys/mips/cavium/cvmx_config.h
new file mode 100644
index 000000000000..7dd2c630c184
--- /dev/null
+++ b/sys/mips/cavium/cvmx_config.h
@@ -0,0 +1,194 @@
+/***********************license start***************
+ * Copyright (c) 2003-2008 Cavium Networks (support@cavium.com). All rights
+ * reserved.
+ *
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * 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.
+ *
+ * * Neither the name of Cavium Networks nor the names of
+ * its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written
+ * permission.
+ *
+ * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+ * AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS
+ * OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH
+ * RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY
+ * REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT
+ * DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES
+ * OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR
+ * PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET
+ * POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT
+ * OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+ *
+ *
+ * For any questions regarding licensing please contact marketing@caviumnetworks.com
+ *
+ ***********************license end**************************************/
+/* $FreeBSD$ */
+
+#ifndef _CVMX_CONFIG_H
+#define _CVMX_CONFIG_H
+
+#include "opt_cvmx.h"
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <machine/pmap.h>
+#include <machine/stdarg.h>
+
+#define asm __asm
+
+#define CVMX_DONT_INCLUDE_CONFIG
+
+/* Define to enable the use of simple executive packet output functions.
+** For packet I/O setup enable the helper functions below.
+*/
+#define CVMX_ENABLE_PKO_FUNCTIONS
+
+/* Define to enable the use of simple executive helper functions. These
+** include many harware setup functions. See cvmx-helper.[ch] for
+** details.
+*/
+#define CVMX_ENABLE_HELPER_FUNCTIONS
+
+/* CVMX_HELPER_FIRST_MBUFF_SKIP is the number of bytes to reserve before
+** the beginning of the packet. If necessary, override the default
+** here. See the IPD section of the hardware manual for MBUFF SKIP
+** details.*/
+#define CVMX_HELPER_FIRST_MBUFF_SKIP 184
+
+/* CVMX_HELPER_NOT_FIRST_MBUFF_SKIP is the number of bytes to reserve in each
+** chained packet element. If necessary, override the default here */
+#define CVMX_HELPER_NOT_FIRST_MBUFF_SKIP 0
+
+/* CVMX_HELPER_ENABLE_BACK_PRESSURE controls whether back pressure is enabled
+** for all input ports. This controls if IPD sends backpressure to all ports if
+** Octeon's FPA pools don't have enough packet or work queue entries. Even when
+** this is off, it is still possible to get backpressure from individual
+** hardware ports. When configuring backpressure, also check
+** CVMX_HELPER_DISABLE_*_BACKPRESSURE below. If necessary, override the default
+** here */
+#define CVMX_HELPER_ENABLE_BACK_PRESSURE 1
+
+/* CVMX_HELPER_ENABLE_IPD controls if the IPD is enabled in the helper
+** function. Once it is enabled the hardware starts accepting packets. You
+** might want to skip the IPD enable if configuration changes are need
+** from the default helper setup. If necessary, override the default here */
+#define CVMX_HELPER_ENABLE_IPD 1
+
+/* CVMX_HELPER_INPUT_TAG_TYPE selects the type of tag that the IPD assigns
+** to incoming packets. */
+#define CVMX_HELPER_INPUT_TAG_TYPE CVMX_POW_TAG_TYPE_ORDERED
+
+/* The following select which fields are used by the PIP to generate
+** the tag on INPUT
+** 0: don't include
+** 1: include */
+#define CVMX_HELPER_INPUT_TAG_IPV6_SRC_IP 0
+#define CVMX_HELPER_INPUT_TAG_IPV6_DST_IP 0
+#define CVMX_HELPER_INPUT_TAG_IPV6_SRC_PORT 0
+#define CVMX_HELPER_INPUT_TAG_IPV6_DST_PORT 0
+#define CVMX_HELPER_INPUT_TAG_IPV6_NEXT_HEADER 0
+#define CVMX_HELPER_INPUT_TAG_IPV4_SRC_IP 0
+#define CVMX_HELPER_INPUT_TAG_IPV4_DST_IP 0
+#define CVMX_HELPER_INPUT_TAG_IPV4_SRC_PORT 0
+#define CVMX_HELPER_INPUT_TAG_IPV4_DST_PORT 0
+#define CVMX_HELPER_INPUT_TAG_IPV4_PROTOCOL 0
+#define CVMX_HELPER_INPUT_TAG_INPUT_PORT 1
+
+/* Select skip mode for input ports */
+#define CVMX_HELPER_INPUT_PORT_SKIP_MODE CVMX_PIP_PORT_CFG_MODE_SKIPL2
+
+/* Define the number of queues per output port */
+#define CVMX_HELPER_PKO_QUEUES_PER_PORT_INTERFACE0 1
+#define CVMX_HELPER_PKO_QUEUES_PER_PORT_INTERFACE1 1
+
+/* Configure PKO to use per-core queues (PKO lockless operation).
+** Please see the related SDK documentation for PKO that illustrates
+** how to enable and configure this option. */
+//#define CVMX_ENABLE_PKO_LOCKLESS_OPERATION 1
+//#define CVMX_HELPER_PKO_MAX_PORTS_INTERFACE0 8
+//#define CVMX_HELPER_PKO_MAX_PORTS_INTERFACE1 8
+
+/* Force backpressure to be disabled. This overrides all other
+** backpressure configuration */
+#define CVMX_HELPER_DISABLE_RGMII_BACKPRESSURE 1
+
+/* Disable the SPI4000's processing of backpressure packets and backpressure
+** generation. When this is 1, the SPI4000 will not stop sending packets when
+** receiving backpressure. It will also not generate backpressure packets when
+** its internal FIFOs are full. */
+#define CVMX_HELPER_DISABLE_SPI4000_BACKPRESSURE 1
+
+/* CVMX_HELPER_SPI_TIMEOUT is used to determine how long the SPI initialization
+** routines wait for SPI training. You can override the value using
+** executive-config.h if necessary */
+#define CVMX_HELPER_SPI_TIMEOUT 10
+
+/* Select the number of low latency memory ports (interfaces) that
+** will be configured. Valid values are 1 and 2.
+*/
+#define CVMX_LLM_CONFIG_NUM_PORTS 2
+
+/* Enable the fix for PKI-100 errata ("Size field is 8 too large in WQE and next
+** pointers"). If CVMX_ENABLE_LEN_M8_FIX is set to 0, the fix for this errata will
+** not be enabled.
+** 0: Fix is not enabled
+** 1: Fix is enabled, if supported by hardware
+*/
+#define CVMX_ENABLE_LEN_M8_FIX 1
+
+#if defined(CVMX_ENABLE_HELPER_FUNCTIONS) && !defined(CVMX_ENABLE_PKO_FUNCTIONS)
+#define CVMX_ENABLE_PKO_FUNCTIONS
+#endif
+
+/************************* Config Specific Defines ************************/
+#define CVMX_LLM_NUM_PORTS 1
+#define CVMX_PKO_QUEUES_PER_PORT_INTERFACE0 1 /**< PKO queues per port for interface 0 (ports 0-15) */
+#define CVMX_PKO_QUEUES_PER_PORT_INTERFACE1 1 /**< PKO queues per port for interface 1 (ports 16-31) */
+#define CVMX_PKO_MAX_PORTS_INTERFACE0 CVMX_HELPER_PKO_MAX_PORTS_INTERFACE0 /**< Limit on the number of PKO ports enabled for interface 0 */
+#define CVMX_PKO_MAX_PORTS_INTERFACE1 CVMX_HELPER_PKO_MAX_PORTS_INTERFACE1 /**< Limit on the number of PKO ports enabled for interface 1 */
+#define CVMX_PKO_QUEUES_PER_PORT_PCI 1 /**< PKO queues per port for PCI (ports 32-35) */
+#define CVMX_PKO_QUEUES_PER_PORT_LOOP 1 /**< PKO queues per port for Loop devices (ports 36-39) */
+
+/************************* FPA allocation *********************************/
+/* Pool sizes in bytes, must be multiple of a cache line */
+#define CVMX_FPA_POOL_0_SIZE (15 * CVMX_CACHE_LINE_SIZE)
+#define CVMX_FPA_POOL_1_SIZE (1 * CVMX_CACHE_LINE_SIZE)
+#define CVMX_FPA_POOL_2_SIZE (8 * CVMX_CACHE_LINE_SIZE)
+#define CVMX_FPA_POOL_3_SIZE (0 * CVMX_CACHE_LINE_SIZE)
+#define CVMX_FPA_POOL_4_SIZE (0 * CVMX_CACHE_LINE_SIZE)
+#define CVMX_FPA_POOL_5_SIZE (0 * CVMX_CACHE_LINE_SIZE)
+#define CVMX_FPA_POOL_6_SIZE (0 * CVMX_CACHE_LINE_SIZE)
+#define CVMX_FPA_POOL_7_SIZE (0 * CVMX_CACHE_LINE_SIZE)
+
+/* Pools in use */
+#define CVMX_FPA_PACKET_POOL (0) /**< Packet buffers */
+#define CVMX_FPA_PACKET_POOL_SIZE CVMX_FPA_POOL_0_SIZE
+#define CVMX_FPA_WQE_POOL (1) /**< Work queue entrys */
+#define CVMX_FPA_WQE_POOL_SIZE CVMX_FPA_POOL_1_SIZE
+#define CVMX_FPA_OUTPUT_BUFFER_POOL (2) /**< PKO queue command buffers */
+#define CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE CVMX_FPA_POOL_2_SIZE
+
+/************************* FAU allocation ********************************/
+#define CVMX_FAU_REG_END 2048
+
+#define CVMX_SCR_SCRATCH 0
+
+#endif /* !_CVMX_CONFIG_H */
diff --git a/sys/mips/cavium/dev/rgmii/octeon_fau.c b/sys/mips/cavium/dev/rgmii/octeon_fau.c
new file mode 100644
index 000000000000..849ffd437992
--- /dev/null
+++ b/sys/mips/cavium/dev/rgmii/octeon_fau.c
@@ -0,0 +1,42 @@
+/*------------------------------------------------------------------
+ * octeon_fau.c Fetch & Add Block
+ *
+ *------------------------------------------------------------------
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+
+#include <mips/octeon1/octeon_pcmap_regs.h>
+#include "octeon_fau.h"
+
+/*
+ * oct_fau_init
+ *
+ * How do we initialize FAU unit. I don't even think we can reset it.
+ */
+void octeon_fau_init (void)
+{
+}
+
+
+/*
+ * oct_fau_enable
+ *
+ * Let the Fetch/Add unit roll
+ */
+void octeon_fau_enable (void)
+{
+}
+
+
+/*
+ * oct_fau_disable
+ *
+ * disable fau
+ *
+ * Don't know if we can even do that.
+ */
+void octeon_fau_disable (void)
+{
+}
diff --git a/sys/mips/cavium/dev/rgmii/octeon_fau.h b/sys/mips/cavium/dev/rgmii/octeon_fau.h
new file mode 100644
index 000000000000..acf5132e2e89
--- /dev/null
+++ b/sys/mips/cavium/dev/rgmii/octeon_fau.h
@@ -0,0 +1,220 @@
+/***********************license start***************
+ * Copyright (c) 2003-2008 Cavium Networks (support@cavium.com). All rights
+ * reserved.
+ *
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * 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.
+ *
+ * * Neither the name of Cavium Networks nor the names of
+ * its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written
+ * permission.
+ *
+ * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+ * AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS
+ * OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH
+ * RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY
+ * REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT
+ * DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES
+ * OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR
+ * PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET
+ * POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT
+ * OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+ *
+ *
+ * For any questions regarding licensing please contact marketing@caviumnetworks.com
+ *
+ ***********************license end**************************************/
+
+/* $FreeBSD$ */
+
+/*------------------------------------------------------------------
+ * octeon_fau.h Fetch & Add Unit
+ *
+ *------------------------------------------------------------------
+ */
+
+
+#ifndef ___OCTEON_FAU__H___
+#define ___OCTEON_FAU__H___
+
+
+
+
+typedef enum {
+ OCTEON_FAU_OP_SIZE_8 = 0,
+ OCTEON_FAU_OP_SIZE_16 = 1,
+ OCTEON_FAU_OP_SIZE_32 = 2,
+ OCTEON_FAU_OP_SIZE_64 = 3
+} octeon_fau_op_size_t;
+
+
+
+#define OCTEON_FAU_LOAD_IO_ADDRESS octeon_build_io_address(0x1e, 0)
+#define OCTEON_FAU_BITS_SCRADDR 63,56
+#define OCTEON_FAU_BITS_LEN 55,48
+#define OCTEON_FAU_BITS_INEVAL 35,14
+#define OCTEON_FAU_BITS_TAGWAIT 13,13
+#define OCTEON_FAU_BITS_NOADD 13,13
+#define OCTEON_FAU_BITS_SIZE 12,11
+#define OCTEON_FAU_BITS_REGISTER 10,0
+
+#define OCTEON_FAU_REG_64_ADDR(x) ((x <<3) + OCTEON_FAU_REG_64_START)
+typedef enum
+{
+ OCTEON_FAU_REG_64_START = 0,
+ OCTEON_FAU_REG_OQ_ADDR_INDEX = OCTEON_FAU_REG_64_ADDR(0),
+ OCTEON_FAU_REG_OQ_ADDR_END = OCTEON_FAU_REG_64_ADDR(31),
+ OCTEON_FAU_REG_64_END = OCTEON_FAU_REG_64_ADDR(39),
+} octeon_fau_reg_64_t;
+
+#define OCTEON_FAU_REG_32_ADDR(x) ((x <<2) + OCTEON_FAU_REG_32_START)
+typedef enum
+{
+ OCTEON_FAU_REG_32_START = OCTEON_FAU_REG_64_END,
+ OCTEON_FAU_REG_32_END = OCTEON_FAU_REG_32_ADDR(0),
+} octeon_fau_reg_32_t;
+
+
+
+/*
+ * octeon_fau_atomic_address
+ *
+ * Builds a I/O address for accessing the FAU
+ *
+ * @param tagwait Should the atomic add wait for the current tag switch
+ * operation to complete.
+ * - 0 = Don't wait
+ * - 1 = Wait for tag switch to complete
+ * @param reg FAU atomic register to access. 0 <= reg < 4096.
+ * - Step by 2 for 16 bit access.
+ * - Step by 4 for 32 bit access.
+ * - Step by 8 for 64 bit access.
+ * @param value Signed value to add.
+ * Note: When performing 32 and 64 bit access, only the low
+ * 22 bits are available.
+ * @return Address to read from for atomic update
+ */
+static inline uint64_t octeon_fau_atomic_address (uint64_t tagwait, uint64_t reg,
+ int64_t value)
+{
+ return (OCTEON_ADD_IO_SEG(OCTEON_FAU_LOAD_IO_ADDRESS) |
+ octeon_build_bits(OCTEON_FAU_BITS_INEVAL, value) |
+ octeon_build_bits(OCTEON_FAU_BITS_TAGWAIT, tagwait) |
+ octeon_build_bits(OCTEON_FAU_BITS_REGISTER, reg));
+}
+
+
+/*
+ * octeon_fau_store_address
+ *
+ * Builds a store I/O address for writing to the FAU
+ *
+ * noadd 0 = Store value is atomically added to the current value
+ * 1 = Store value is atomically written over the current value
+ * reg FAU atomic register to access. 0 <= reg < 4096.
+ * - Step by 2 for 16 bit access.
+ * - Step by 4 for 32 bit access.
+ * - Step by 8 for 64 bit access.
+ * Returns Address to store for atomic update
+ */
+static inline uint64_t octeon_fau_store_address (uint64_t noadd, uint64_t reg)
+{
+ return (OCTEON_ADD_IO_SEG(OCTEON_FAU_LOAD_IO_ADDRESS) |
+ octeon_build_bits(OCTEON_FAU_BITS_NOADD, noadd) |
+ octeon_build_bits(OCTEON_FAU_BITS_REGISTER, reg));
+}
+
+
+/*
+ * octeon_fau_atomic_add32
+ *
+ * Perform an atomic 32 bit add
+ *
+ * @param reg FAU atomic register to access. 0 <= reg < 4096.
+ * - Step by 4 for 32 bit access.
+ * @param value Signed value to add.
+ */
+static inline void octeon_fau_atomic_add32 (octeon_fau_reg_32_t reg, int32_t value)
+{
+ oct_write32(octeon_fau_store_address(0, reg), value);
+}
+
+/*
+ * octeon_fau_fetch_and_add
+ *
+ * reg FAU atomic register to access. 0 <= reg < 4096.
+ * - Step by 8 for 64 bit access.
+ * value Signed value to add.
+ * Note: Only the low 22 bits are available.
+ * returns Value of the register before the update
+ */
+static inline int64_t octeon_fau_fetch_and_add64 (octeon_fau_reg_64_t reg,
+ int64_t val64)
+{
+
+ return (oct_read64(octeon_fau_atomic_address(0, reg, val64)));
+}
+
+/*
+ * octeon_fau_fetch_and_add32
+ *
+ * reg FAU atomic register to access. 0 <= reg < 4096.
+ * - Step by 8 for 64 bit access.
+ * value Signed value to add.
+ * Note: Only the low 22 bits are available.
+ * returns Value of the register before the update
+ */
+static inline int32_t octeon_fau_fetch_and_add32 (octeon_fau_reg_64_t reg,
+ int32_t val32)
+{
+ return (oct_read32(octeon_fau_atomic_address(0, reg, val32)));
+}
+
+/*
+ * octeon_fau_atomic_write32
+ *
+ * Perform an atomic 32 bit write
+ *
+ * @param reg FAU atomic register to access. 0 <= reg < 4096.
+ * - Step by 4 for 32 bit access.
+ * @param value Signed value to write.
+ */
+static inline void octeon_fau_atomic_write32(octeon_fau_reg_32_t reg, int32_t value)
+{
+ oct_write32(octeon_fau_store_address(1, reg), value);
+}
+
+
+/*
+ * octeon_fau_atomic_write64
+ *
+ * Perform an atomic 32 bit write
+ *
+ * reg FAU atomic register to access. 0 <= reg < 4096.
+ * - Step by 8 for 64 bit access.
+ * value Signed value to write.
+ */
+static inline void octeon_fau_atomic_write64 (octeon_fau_reg_64_t reg, int64_t value)
+{
+ oct_write64(octeon_fau_store_address(1, reg), value);
+}
+
+
+static inline void octeon_fau_atomic_add64 (octeon_fau_reg_64_t reg, int64_t value)
+{
+ oct_write64_int64(octeon_fau_store_address(0, reg), value);
+}
+
+
+#endif /* ___OCTEON_FAU__H___ */
diff --git a/sys/mips/cavium/dev/rgmii/octeon_fpa.c b/sys/mips/cavium/dev/rgmii/octeon_fpa.c
new file mode 100644
index 000000000000..3e43a886b122
--- /dev/null
+++ b/sys/mips/cavium/dev/rgmii/octeon_fpa.c
@@ -0,0 +1,230 @@
+/***********************license start***************
+ * Copyright (c) 2003-2008 Cavium Networks (support@cavium.com). All rights
+ * reserved.
+ *
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * 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.
+ *
+ * * Neither the name of Cavium Networks nor the names of
+ * its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written
+ * permission.
+ *
+ * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+ * AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS
+ * OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH
+ * RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY
+ * REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT
+ * DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES
+ * OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR
+ * PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET
+ * POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT
+ * OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+ *
+ *
+ * For any questions regarding licensing please contact marketing@caviumnetworks.com
+ *
+ ***********************license end**************************************/
+
+/*------------------------------------------------------------------
+ * octeon_fpa.c Free Pool Allocator
+ *
+ *------------------------------------------------------------------
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+
+#include <mips/cavium/octeon_pcmap_regs.h>
+#include "octeon_fpa.h"
+
+
+#define FPA_DEBUG 1
+
+/*
+ * octeon_dump_fpa
+ *
+ */
+void octeon_dump_fpa (void)
+{
+ int i;
+ octeon_fpa_ctl_status_t status;
+ octeon_fpa_queue_available_t q_avail;
+
+ status.word64 = oct_read64(OCTEON_FPA_CTL_STATUS);
+ if (!status.bits.enb) {
+ printf("\n FPA Disabled");
+ /*
+ * No dumping if disabled
+ */
+ return;
+ }
+ printf(" FPA Ctrl-Status-reg 0x%llX := 0x%llX EN %X M1_E %X M0_E %X\n",
+ OCTEON_FPA_CTL_STATUS, (unsigned long long)status.word64,
+ status.bits.enb, status.bits.mem1_err, status.bits.mem0_err);
+ for (i = 0; i < OCTEON_FPA_QUEUES; i++) {
+ printf(" Pool: %d\n", i);
+
+ q_avail.word64 = oct_read64((OCTEON_FPA_QUEUE_AVAILABLE + (i)*8ull));
+ printf(" Avail-reg 0x%llX := Size: 0x%X\n",
+ (OCTEON_FPA_QUEUE_AVAILABLE + (i)*8ull), q_avail.bits.queue_size);
+ }
+}
+
+void octeon_dump_fpa_pool (u_int pool)
+{
+ octeon_fpa_ctl_status_t status;
+ octeon_fpa_queue_available_t q_avail;
+
+ status.word64 = oct_read64(OCTEON_FPA_CTL_STATUS);
+ if (!status.bits.enb) {
+ printf("\n FPA Disabled");
+ /*
+ * No dumping if disabled
+ */
+ return;
+ }
+ printf(" FPA Ctrl-Status-reg 0x%llX := 0x%llX EN %X M1_E %X M0_E %X\n",
+ OCTEON_FPA_CTL_STATUS, (unsigned long long)status.word64,
+ status.bits.enb, status.bits.mem1_err, status.bits.mem0_err);
+ q_avail.word64 = oct_read64((OCTEON_FPA_QUEUE_AVAILABLE + (pool)*8ull));
+ printf(" FPA Pool: %u Avail-reg 0x%llX := Size: 0x%X\n", pool,
+ (OCTEON_FPA_QUEUE_AVAILABLE + (pool)*8ull), q_avail.bits.queue_size);
+}
+
+
+u_int octeon_fpa_pool_size (u_int pool)
+{
+ octeon_fpa_queue_available_t q_avail;
+ u_int size = 0;
+
+ if (pool < 7) {
+ q_avail.word64 = oct_read64((OCTEON_FPA_QUEUE_AVAILABLE + (pool)*8ull));
+ size = q_avail.bits.queue_size;
+ }
+ return (size);
+}
+
+
+/*
+ * octeon_enable_fpa
+ *
+ * configure fpa with defaults and then mark it enabled.
+ */
+void octeon_enable_fpa (void)
+{
+ int i;
+ octeon_fpa_ctl_status_t status;
+ octeon_fpa_fpf_marks_t marks;
+
+ for (i = 0; i < OCTEON_FPA_QUEUES; i++) {
+ marks.word64 = oct_read64((OCTEON_FPA_FPF_MARKS + (i)*8ull));
+
+ marks.bits.fpf_wr = 0xe0;
+ oct_write64((OCTEON_FPA_FPF_MARKS + (i)*8ull), marks.word64);
+ }
+
+ /* Enforce a 10 cycle delay between config and enable */
+ octeon_wait(10);
+
+ status.word64 = 0;
+ status.bits.enb = 1;
+ oct_write64(OCTEON_FPA_CTL_STATUS, status.word64);
+}
+
+
+#define FPA_DEBUG_TERSE 1
+
+/*
+ * octeon_fpa_fill_pool_mem
+ *
+ * Fill the specified FPA pool with elem_num number of
+ * elements of size elem_size_words * 8
+ */
+void octeon_fpa_fill_pool_mem (u_int pool, u_int elem_size_words, u_int elem_num)
+{
+ void *memory;
+ u_int bytes, elem_size_bytes;
+ u_int block_size;
+
+#ifdef FPA_DEBUG
+ u_int elems = elem_num;
+ printf(" FPA fill: Pool %u elem_size_words %u Num: %u\n", pool, elem_size_words, elem_num);
+#endif
+ elem_size_bytes = elem_size_words * sizeof(uint64_t);
+ block_size = OCTEON_ALIGN(elem_size_bytes);
+
+// block_size = ((elem_size_bytes / OCTEON_FPA_POOL_ALIGNMENT) + 1) * OCTEON_FPA_POOL_ALIGNMENT;
+
+ bytes = (elem_num * block_size);
+
+#ifdef FPA_DEBUG
+ printf(" elem_size_bytes = words * 8 = %u; block_size %u\n", elem_size_bytes, block_size);
+#endif
+
+
+#ifdef FPA_DEBUG
+ int block = 0;
+
+ printf(" %% Filling Pool %u with %u blocks of %u bytes %u words\n",
+ pool, elem_num, elem_size_bytes, elem_size_words);
+#endif
+
+// memory = malloc(bytes, M_DEVBUF, M_NOWAIT | M_ZERO);
+ memory = contigmalloc(bytes, M_DEVBUF, M_NOWAIT | M_ZERO,
+ 0, 0x20000000,
+ OCTEON_FPA_POOL_ALIGNMENT, 0);
+
+ if (memory == NULL) {
+ printf(" %% FPA pool %u could not be filled with %u bytes\n",
+ pool, bytes);
+ return;
+ }
+
+ /*
+ * Forward Align allocated mem to needed alignment. Don't worry about growth, we
+ * already preallocated extra
+ */
+#ifdef FPA_DEBUG
+ printf(" %% Huge MemBlock %p Bytes %u\n", memory, bytes);
+#endif
+
+ memory = (void *) OCTEON_ALIGN(memory);
+
+#ifdef FPA_DEBUG_TERSE
+ printf("FPA fill: %u Count: %u SizeBytes: %u SizeBytesAligned: %u 1st: %p = %p\n",
+ pool, elem_num, elem_size_bytes, block_size, memory,
+ (void *)(intptr_t)OCTEON_PTR2PHYS(memory));
+#endif
+
+// memory = (void *) ((((u_int) memory / OCTEON_FPA_POOL_ALIGNMENT) + 1) * OCTEON_FPA_POOL_ALIGNMENT);
+
+ while (elem_num--) {
+#ifdef FPA_DEBUG
+ if (((elems - elem_num) < 4) || (elem_num < 4))
+ printf(" %% Block %d: %p Phys %p Bytes %u\n", block, memory,
+ (void *)(intptr_t)OCTEON_PTR2PHYS(memory), elem_size_bytes);
+ block++;
+#endif
+ octeon_fpa_free(memory, pool, 0);
+ memory = (void *) (((u_long) memory) + block_size);
+ }
+}
+
diff --git a/sys/mips/cavium/dev/rgmii/octeon_fpa.h b/sys/mips/cavium/dev/rgmii/octeon_fpa.h
new file mode 100644
index 000000000000..33d273bd9e3f
--- /dev/null
+++ b/sys/mips/cavium/dev/rgmii/octeon_fpa.h
@@ -0,0 +1,259 @@
+/***********************license start***************
+ * Copyright (c) 2003-2008 Cavium Networks (support@cavium.com). All rights
+ * reserved.
+ *
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * 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.
+ *
+ * * Neither the name of Cavium Networks nor the names of
+ * its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written
+ * permission.
+ *
+ * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+ * AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS
+ * OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH
+ * RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY
+ * REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT
+ * DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES
+ * OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR
+ * PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET
+ * POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT
+ * OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+ *
+ *
+ * For any questions regarding licensing please contact marketing@caviumnetworks.com
+ *
+ ***********************license end**************************************/
+
+/* $FreeBSD$ */
+
+/*------------------------------------------------------------------
+ * octeon_fpa.h Free Pool Allocator
+ *
+ *------------------------------------------------------------------
+ */
+
+
+#ifndef ___OCTEON_FPA__H___
+#define ___OCTEON_FPA__H___
+
+
+#define OCTEON_FPA_FPA_OUTPUT_BUFFER_POOL 2 /* Same in octeon_rgmx.h */
+
+
+/*
+ * OCTEON_FPA_FPF_MARKS = FPA's Queue Free Page FIFO Read Write Marks
+ *
+ * The high and low watermark register that determines when we write and
+ * read free pages from L2C for Queue.
+ */
+typedef union {
+ uint64_t word64;
+ struct {
+ uint64_t reserved : 42; /* Must be zero */
+ uint64_t fpf_wr : 11; /* Write Hi Water mark */
+ uint64_t fpf_rd : 11; /* Read Lo Water mark */
+ } bits;
+} octeon_fpa_fpf_marks_t;
+
+
+/*
+ * OCTEON_FPA_CTL_STATUS = FPA's Control/Status Register
+ *
+ * The FPA's interrupt enable register.
+ * - Use with the CVMX_FPA_CTL_STATUS CSR.
+ */
+typedef union {
+ uint64_t word64;
+ struct {
+ uint64_t reserved : 49; /* Must be zero */
+ uint64_t enb : 1; /* Enable */
+ uint64_t mem1_err : 7; /* ECC flip 1 */
+ uint64_t mem0_err : 7; /* ECC flip 0 */
+ } bits;
+} octeon_fpa_ctl_status_t;
+
+
+/*
+ * OCTEON_FPA_FPF_SIZE = FPA's Queue N Free Page FIFO Size
+ *
+ * The number of page pointers that will be kept local to the FPA for
+ * this Queue. FPA Queues are assigned in order from Queue 0 to
+ * Queue 7, though only Queue 0 through Queue x can be used.
+ * The sum of the 8 (0-7)OCTEON_FPA_FPF#_SIZE registers must be limited to 2048.
+ * - Use with the CVMX_FPA_FPF0_SIZE CSR.
+ */
+typedef union {
+ uint64_t word64;
+ struct {
+ uint64_t reserved : 52; /* Must be zero */
+ /*
+ * The number of entries assigned in the FPA FIFO (used to hold
+ * page-pointers) for this Queue.
+ * The value of this register must divisable by 2, and the FPA will
+ * ignore bit [0] of this register.
+ * The total of the FPF_SIZ field of the 8 (0-7)OCTEON_FPA_FPF#_MARKS
+ * registers must not exceed 2048.
+ * After writing this field the FPA will need 10 core clock cycles
+ * to be ready for operation. The assignment of location in
+ * the FPA FIFO must start with Queue 0, then 1, 2, etc.
+ * The number of useable entries will be FPF_SIZ-2.
+ */
+ uint64_t fpf_siz : 12;
+ } bits;
+} octeon_fpa_fpf_size_t;
+
+/*
+ *OCTEON_FPA_INT_ENB = FPA's Interrupt Enable
+ *
+ * The FPA's interrupt enable register.
+ * - Use with the CVMX_FPA_INT_ENB CSR.
+ */
+typedef union {
+ uint64_t word64;
+ struct {
+ uint64_t reserved : 60; /* Must be zero */
+ uint64_t fed1_dbe : 1; /* Int iff bit3 Int-Sum set */
+ uint64_t fed1_sbe : 1; /* Int iff bit2 Int-Sum set */
+ uint64_t fed0_dbe : 1; /* Int iff bit1 Int-Sum set */
+ uint64_t fed0_sbe : 1; /* Int iff bit0 Int-Sum set */
+ } bits;
+} octeon_fpa_int_enb_t;
+
+/**
+ *OCTEON_FPA_INT_SUM = FPA's Interrupt Summary Register
+ *
+ * Contains the diffrent interrupt summary bits of the FPA.
+ * - Use with the CVMX_FPA_INT_SUM CSR.
+ */
+typedef union {
+ uint64_t word64;
+ struct {
+ uint64_t reserved : 60; /**< Must be zero */
+ uint64_t fed1_dbe : 1;
+ uint64_t fed1_sbe : 1;
+ uint64_t fed0_dbe : 1;
+ uint64_t fed0_sbe : 1;
+ } bits;
+} octeon_fpa_int_sum_t;
+
+
+/*
+ *OCTEON_FPA_QUEUE_PAGES_AVAILABLE = FPA's Queue 0-7 Free Page Available Register
+ *
+ * The number of page pointers that are available in the FPA and local DRAM.
+ * - Use with the CVMX_FPA_QUEX_AVAILABLE(0..7) CSR.
+ */
+typedef union {
+ uint64_t word64;
+ struct {
+ uint64_t reserved : 38; /* Must be zero */
+ uint64_t queue_size : 26; /* free pages available */
+ } bits;
+} octeon_fpa_queue_available_t;
+
+
+/*
+ *OCTEON_FPA_QUEUE_PAGE_INDEX
+ *
+ */
+typedef union {
+ uint64_t word64;
+ struct {
+ uint64_t reserved : 39; /* Must be zero */
+ uint64_t page_index : 25; /* page_index */
+ } bits;
+} octeon_fpa_queue_page_index_t;
+
+
+#define OCTEON_DID_FPA 5ULL
+
+#define OCTEON_FPA_POOL_ALIGNMENT (OCTEON_CACHE_LINE_SIZE)
+
+
+/*
+ * Externs
+ */
+extern void octeon_dump_fpa(void);
+extern void octeon_dump_fpa_pool(u_int pool);
+extern u_int octeon_fpa_pool_size(u_int pool);
+extern void octeon_enable_fpa(void);
+extern void octeon_fpa_fill_pool_mem(u_int pool,
+ u_int block_size_words,
+ u_int block_num);
+
+/*
+ * octeon_fpa_free
+ *
+ * Free a mem-block to FPA pool.
+ *
+ * Takes away this 'buffer' from SW and passes it to FPA for management.
+ *
+ * pool is FPA pool num, ptr is block ptr, num_cache_lines is number of
+ * cache lines to invalidate (not written back).
+ */
+static inline void octeon_fpa_free (void *ptr, u_int pool,
+ u_int num_cache_lines)
+{
+ octeon_addr_t free_ptr;
+
+ free_ptr.word64 = (uint64_t)OCTEON_PTR2PHYS(ptr);
+
+ free_ptr.sfilldidspace.didspace = OCTEON_ADDR_DIDSPACE(
+ OCTEON_ADDR_FULL_DID(OCTEON_DID_FPA, pool));
+
+ /*
+ * Do not 'sync'
+ * asm volatile ("sync\n");
+ */
+ oct_write64(free_ptr.word64, num_cache_lines);
+}
+
+
+
+/*
+ * octeon_fpa_alloc
+ *
+ * Allocate a new block from the FPA
+ *
+ * Buffer passes away from FPA management to SW control
+ */
+static inline void *octeon_fpa_alloc (u_int pool)
+{
+ uint64_t address;
+
+ address = oct_read64(OCTEON_ADDR_DID(OCTEON_ADDR_FULL_DID(OCTEON_DID_FPA,
+ pool)));
+ if (address) {
+
+/*
+ * 32 bit FPA pointers only
+ */
+ /*
+ * We only use 32 bit pointers at this time
+ */
+/*XXX mips64 issue */
+ return ((void *) MIPS_PHYS_TO_KSEG0(address & 0xffffffff));
+ }
+ return (NULL);
+}
+
+static inline uint64_t octeon_fpa_alloc_phys (u_int pool)
+{
+
+ return (oct_read64(OCTEON_ADDR_DID(OCTEON_ADDR_FULL_DID(OCTEON_DID_FPA,
+ pool))));
+}
+
+#endif /* ___OCTEON_FPA__H___ */
diff --git a/sys/mips/cavium/dev/rgmii/octeon_ipd.c b/sys/mips/cavium/dev/rgmii/octeon_ipd.c
new file mode 100644
index 000000000000..8757a1046598
--- /dev/null
+++ b/sys/mips/cavium/dev/rgmii/octeon_ipd.c
@@ -0,0 +1,148 @@
+/***********************license start***************
+ * Copyright (c) 2003-2008 Cavium Networks (support@cavium.com). All rights
+ * reserved.
+ *
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * 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.
+ *
+ * * Neither the name of Cavium Networks nor the names of
+ * its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written
+ * permission.
+ *
+ * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+ * AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS
+ * OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH
+ * RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY
+ * REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT
+ * DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES
+ * OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR
+ * PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET
+ * POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT
+ * OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+ *
+ *
+ * For any questions regarding licensing please contact marketing@caviumnetworks.com
+ *
+ ***********************license end**************************************/
+
+/*------------------------------------------------------------------
+ * octeon_ipd.c Input Packet Unit
+ *
+ *------------------------------------------------------------------
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+
+#include <mips/cavium/octeon_pcmap_regs.h>
+#include "octeon_ipd.h"
+
+/*
+ * octeon_ipd_enable
+ *
+ * enable ipd
+ */
+void octeon_ipd_enable (void)
+{
+ octeon_ipd_ctl_status_t octeon_ipd_reg;
+
+ octeon_ipd_reg.word64 = oct_read64(OCTEON_IPD_CTL_STATUS);
+ octeon_ipd_reg.bits.ipd_en = 1;
+ oct_write64(OCTEON_IPD_CTL_STATUS, octeon_ipd_reg.word64);
+}
+
+
+/*
+ * octeon_ipd_disable
+ *
+ * disable ipd
+ */
+void octeon_ipd_disable (void)
+{
+ octeon_ipd_ctl_status_t octeon_ipd_reg;
+
+ octeon_ipd_reg.word64 = oct_read64(OCTEON_IPD_CTL_STATUS);
+ octeon_ipd_reg.bits.ipd_en = 0;
+ oct_write64(OCTEON_IPD_CTL_STATUS, octeon_ipd_reg.word64);
+}
+
+
+/*
+ * octeon_ipd_config
+ *
+ * Configure IPD
+ *
+ * mbuff_size Packets buffer size in 8 byte words
+ * first_mbuff_skip
+ * Number of 8 byte words to skip in the first buffer
+ * not_first_mbuff_skip
+ * Number of 8 byte words to skip in each following buffer
+ * first_back Must be same as first_mbuff_skip / Cache_Line_size
+ * second_back
+ * Must be same as not_first_mbuff_skip / Cache_Line_Size
+ * wqe_fpa_pool
+ * FPA pool to get work entries from
+ * cache_mode
+ * back_pres_enable_flag
+ * Enable or disable port back pressure
+ */
+void octeon_ipd_config (u_int mbuff_size,
+ u_int first_mbuff_skip,
+ u_int not_first_mbuff_skip,
+ u_int first_back,
+ u_int second_back,
+ u_int wqe_fpa_pool,
+ octeon_ipd_mode_t cache_mode,
+ u_int back_pres_enable_flag)
+{
+ octeon_ipd_mbuff_first_skip_t first_skip;
+ octeon_ipd_mbuff_not_first_skip_t not_first_skip;
+ octeon_ipd_mbuff_size_t size;
+ octeon_ipd_first_next_ptr_back_t first_back_struct;
+ octeon_ipd_second_next_ptr_back_t second_back_struct;
+ octeon_ipd_wqe_fpa_pool_t wqe_pool;
+ octeon_ipd_ctl_status_t octeon_ipd_ctl_reg;
+
+ first_skip.word64 = 0;
+ first_skip.bits.skip_sz = first_mbuff_skip;
+ oct_write64(OCTEON_IPD_1ST_MBUFF_SKIP, first_skip.word64);
+
+ not_first_skip.word64 = 0;
+ not_first_skip.bits.skip_sz = not_first_mbuff_skip;
+ oct_write64(OCTEON_IPD_NOT_1ST_MBUFF_SKIP, not_first_skip.word64);
+
+ size.word64 = 0;
+ size.bits.mb_size = mbuff_size;
+ oct_write64(OCTEON_IPD_PACKET_MBUFF_SIZE, size.word64);
+
+ first_back_struct.word64 = 0;
+ first_back_struct.bits.back = first_back;
+ oct_write64(OCTEON_IPD_1ST_NEXT_PTR_BACK, first_back_struct.word64);
+
+ second_back_struct.word64 = 0;
+ second_back_struct.bits.back = second_back;
+ oct_write64(OCTEON_IPD_2ND_NEXT_PTR_BACK, second_back_struct.word64);
+
+ wqe_pool.word64 = 0;
+ wqe_pool.bits.wqe_pool = wqe_fpa_pool;
+ oct_write64(OCTEON_IPD_WQE_FPA_QUEUE, wqe_pool.word64);
+
+ octeon_ipd_ctl_reg.word64 = 0;
+ octeon_ipd_ctl_reg.bits.opc_mode = cache_mode;
+ octeon_ipd_ctl_reg.bits.pbp_en = back_pres_enable_flag;
+ oct_write64(OCTEON_IPD_CTL_STATUS, octeon_ipd_ctl_reg.word64);
+}
diff --git a/sys/mips/cavium/dev/rgmii/octeon_ipd.h b/sys/mips/cavium/dev/rgmii/octeon_ipd.h
new file mode 100644
index 000000000000..aabfaaec23d9
--- /dev/null
+++ b/sys/mips/cavium/dev/rgmii/octeon_ipd.h
@@ -0,0 +1,204 @@
+/***********************license start***************
+ * Copyright (c) 2003-2008 Cavium Networks (support@cavium.com). All rights
+ * reserved.
+ *
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * 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.
+ *
+ * * Neither the name of Cavium Networks nor the names of
+ * its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written
+ * permission.
+ *
+ * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+ * AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS
+ * OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH
+ * RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY
+ * REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT
+ * DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES
+ * OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR
+ * PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET
+ * POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT
+ * OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+ *
+ *
+ * For any questions regarding licensing please contact marketing@caviumnetworks.com
+ *
+ ***********************license end**************************************/
+
+/* $FreeBSD$ */
+
+/*------------------------------------------------------------------
+ * octeon_ipd.h Input Packet Unit
+ *
+ *------------------------------------------------------------------
+ */
+
+
+#ifndef ___OCTEON_IPD__H___
+#define ___OCTEON_IPD__H___
+
+
+
+typedef enum {
+ OCTEON_IPD_OPC_MODE_STT = 0LL, /* All blocks DRAM, not cached in L2 */
+ OCTEON_IPD_OPC_MODE_STF = 1LL, /* All blocks into L2 */
+ OCTEON_IPD_OPC_MODE_STF1_STT = 2LL, /* 1st block L2, rest DRAM */
+ OCTEON_IPD_OPC_MODE_STF2_STT = 3LL /* 1st, 2nd blocks L2, rest DRAM */
+} octeon_ipd_mode_t;
+
+
+
+
+/*
+ * IPD_CTL_STATUS = IPS'd Control Status Register
+ * The number of words in a MBUFF used for packet data store.
+ */
+typedef union {
+ uint64_t word64;
+ struct {
+ uint64_t reserved : 58; /* Reserved */
+ uint64_t pkt_lend : 1; /* Pkt Lil-Endian Writes to L2C */
+ uint64_t wqe_lend : 1; /* WQE Lik-Endian Writes to L2C */
+ uint64_t pbp_en : 1; /* Enable Back-Pressure */
+ octeon_ipd_mode_t opc_mode : 2; /* Pkt data in Mem/L2-cache ? */
+ uint64_t ipd_en : 1; /* Enable IPD */
+ } bits;
+} octeon_ipd_ctl_status_t;
+
+
+/*
+ * IPD_1ST_NEXT_PTR_BACK = IPD First Next Pointer Back Values
+ *
+ * Contains the Back Field for use in creating the Next Pointer Header
+ * for the First MBUF
+ */
+typedef union {
+ uint64_t word64;
+ struct {
+ uint64_t reserved : 60; /* Must be zero */
+ uint64_t back : 4; /* Used to find head of buffer from the nxt-hdr-ptr. */
+ } bits;
+} octeon_ipd_first_next_ptr_back_t;
+
+
+/*
+ * IPD_INTERRUPT_ENB = IPD Interrupt Enable Register
+ *
+ * Used to enable the various interrupting conditions of IPD
+ */
+typedef union {
+ uint64_t word64;
+ struct {
+ uint64_t reserved : 59; /* Must be zero */
+ uint64_t bp_sub : 1; /* BP subtract is illegal val */
+ uint64_t prc_par3 : 1; /* PBM Bits [127:96] Parity Err */
+ uint64_t prc_par2 : 1; /* PBM Bits [ 95:64] Parity Err */
+ uint64_t prc_par1 : 1; /* PBM Bits [ 63:32] Parity Err */
+ uint64_t prc_par0 : 1; /* PBM Bits [ 31:0 ] Parity Err */
+ } bits;
+} octeon_ipd_int_enb_t;
+
+
+/*
+ * IPD_INTERRUPT_SUM = IPD Interrupt Summary Register
+ *
+ * Set when an interrupt condition occurs, write '1' to clear.
+ */
+typedef union {
+ uint64_t word64;
+ struct {
+ uint64_t reserved : 59; /* Must be zero */
+ uint64_t bp_sub : 1; /* BP subtract is illegal val */
+ uint64_t prc_par3 : 1; /* PBM Bits [127:96] Parity Err */
+ uint64_t prc_par2 : 1; /* PBM Bits [ 95:64] Parity Err */
+ uint64_t prc_par1 : 1; /* PBM Bits [ 63:32] Parity Err */
+ uint64_t prc_par0 : 1; /* PBM Bits [ 31:0 ] Parity Err */
+ } bits;
+} octeon_ipd_int_sum_t;
+
+
+/**
+ * IPD_1ST_MBUFF_SKIP = IPD First MBUFF Word Skip Size
+ *
+ * The number of words that the IPD will skip when writing the first MBUFF.
+ */
+typedef union {
+ uint64_t word64;
+ struct {
+ uint64_t reserved : 58; /* Must be zero */
+ uint64_t skip_sz : 6; /* 64bit words from the top of */
+ /* 1st MBUFF that the IPD will */
+ /* store the next-pointer. */
+ /* [0..32] && */
+ /* (skip_sz + 16) <= IPD_PACKET_MBUFF_SIZE[MB_SIZE]. */
+ } bits;
+} octeon_ipd_mbuff_first_skip_t;
+
+
+/*
+ * IPD_PACKET_MBUFF_SIZE = IPD's PACKET MUBUF Size In Words
+ *
+ * The number of words in a MBUFF used for packet data store.
+ */
+typedef union {
+ uint64_t word64;
+ struct {
+ uint64_t reserved : 52; /* Must be zero */
+ uint64_t mb_size : 12; /* 64bit words in a MBUF. */
+ /* Must be [32..2048] */
+ /* Is also the size of the FPA's */
+ /* Queue-0 Free-Page */
+ } bits;
+} octeon_ipd_mbuff_size_t;
+
+
+/*
+ * IPD_WQE_FPA_QUEUE = IPD Work-Queue-Entry FPA Page Size
+ *
+ * Which FPA Queue (0-7) to fetch page-pointers from for WQE's
+ */
+typedef union {
+ uint64_t word64;
+ struct {
+ uint64_t reserved : 61; /* Must be zero */
+ uint64_t wqe_pool : 3; /* FPA Pool to fetch WQE Page-ptrs */
+ } bits;
+} octeon_ipd_wqe_fpa_pool_t;
+
+
+
+
+/* End of Control and Status Register (CSR) definitions */
+
+typedef octeon_ipd_mbuff_first_skip_t octeon_ipd_mbuff_not_first_skip_t;
+typedef octeon_ipd_first_next_ptr_back_t octeon_ipd_second_next_ptr_back_t;
+
+
+/*
+ * Externs
+ */
+extern void octeon_ipd_enable(void);
+extern void octeon_ipd_disable(void);
+extern void octeon_ipd_config(u_int mbuff_size,
+ u_int first_mbuff_skip,
+ u_int not_first_mbuff_skip,
+ u_int first_back,
+ u_int second_back,
+ u_int wqe_fpa_pool,
+ octeon_ipd_mode_t cache_mode,
+ u_int back_pres_enable_flag);
+
+
+
+#endif /* ___OCTEON_IPD__H___ */
diff --git a/sys/mips/cavium/dev/rgmii/octeon_pip.h b/sys/mips/cavium/dev/rgmii/octeon_pip.h
new file mode 100644
index 000000000000..a13d2446b010
--- /dev/null
+++ b/sys/mips/cavium/dev/rgmii/octeon_pip.h
@@ -0,0 +1,219 @@
+/***********************license start***************
+ * Copyright (c) 2003-2008 Cavium Networks (support@cavium.com). All rights
+ * reserved.
+ *
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * 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.
+ *
+ * * Neither the name of Cavium Networks nor the names of
+ * its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written
+ * permission.
+ *
+ * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+ * AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS
+ * OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH
+ * RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY
+ * REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT
+ * DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES
+ * OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR
+ * PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET
+ * POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT
+ * OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+ *
+ *
+ * For any questions regarding licensing please contact marketing@caviumnetworks.com
+ *
+ ***********************license end**************************************/
+
+/* $FreeBSD$ */
+
+/*
+ * octeon_pip.h Packet Input Processing Block
+ *
+ */
+
+
+
+#ifndef __OCTEON_PIP_H__
+#define __OCTEON_PIP_H__
+
+/**
+ * Enumeration representing the amount of packet processing
+ * and validation performed by the input hardware.
+ */
+typedef enum
+{
+ OCTEON_PIP_PORT_CFG_MODE_NONE = 0ull, /**< Packet input doesn't perform any
+ processing of the input packet. */
+ OCTEON_PIP_PORT_CFG_MODE_SKIPL2 = 1ull,/**< Full packet processing is performed
+ with pointer starting at the L2
+ (ethernet MAC) header. */
+ OCTEON_PIP_PORT_CFG_MODE_SKIPIP = 2ull /**< Input packets are assumed to be IP.
+ Results from non IP packets is
+ undefined. Pointers reference the
+ beginning of the IP header. */
+} octeon_pip_port_parse_mode_t;
+
+
+
+#define OCTEON_PIP_PRT_CFGX(offset) (0x80011800A0000200ull+((offset)*8))
+#define OCTEON_PIP_PRT_TAGX(offset) (0x80011800A0000400ull+((offset)*8))
+#define OCTEON_PIP_STAT_INB_PKTS(port) (0x80011800A0001A00ull+((port) * 32))
+#define OCTEON_PIP_STAT_INB_ERRS(port) (0x80011800A0001A10ull+((port) * 32))
+
+/*
+ * PIP Global Config
+ */
+typedef union {
+ uint64_t word64;
+ struct {
+ uint64_t reserved2 : 45; /* Must be zero */
+ uint64_t tag_syn : 1; /* Not Include src_crc in TCP..*/
+ uint64_t ip6_udp : 1; /* IPv6/UDP checksum is mandatory */
+ uint64_t max_l2 : 1; /* Largest L2 frame. 0/1 : 1500/1535 */
+ uint64_t reserved1 : 5; /* Must be zero */
+ uint64_t raw_shf : 3; /* PCI RAW Packet shift/pad amount */
+ uint64_t reserved0 : 5; /* Must be zero */
+ uint64_t nip_shf : 3; /* Non-IP shift/pad amount */
+ } bits;
+} octeon_pip_gbl_cfg_t;
+
+
+typedef union {
+ uint64_t word64;
+ struct {
+ uint64_t reserved4 : 37; /* Must be zero */
+ uint64_t qos : 3; /* Default POW QoS queue */
+ uint64_t qos_wat : 4; /* Bitfield to enable QoS watcher */
+ /* look up tables. 4 per port. */
+ uint64_t reserved3 : 1; /* Must be zero */
+ uint64_t spare : 1; /* Must be zero */
+ uint64_t qos_diff : 1; /* Use IP diffserv to determine */
+ /* the queue in the POW */
+ uint64_t qos_vlan : 1; /* Use the VLAN tag to determine */
+ /* the queue in the POW */
+ uint64_t reserved2 : 3; /* Must be zero */
+ uint64_t crc_en : 1; /* Enable HW checksum */
+ uint64_t reserved1 : 2; /* Must be zero */
+ octeon_pip_port_parse_mode_t mode : 2; /* Raw/Parsed/IP/etc */
+ uint64_t reserved0 : 1; /* Must be zero */
+ uint64_t skip : 7; /* 8 byte words to skip in the */
+ /* beginning of a packet buffer */
+ } bits;
+} octeon_pip_port_cfg_t;
+
+
+
+/*
+ * Packet input to POW interface. How input packets are tagged for
+ * the POW is controlled here.
+ */
+typedef union {
+ uint64_t word64;
+ struct {
+ uint64_t reserved : 24; /**< Reserved */
+ uint64_t grptagbase : 4; /**< Offset to use when computing group from tag bits
+ when GRPTAG is set. Only applies to IP packets.
+ (PASS2 only) */
+ uint64_t grptagmask : 4; /**< Which bits of the tag to exclude when computing
+ group when GRPTAG is set. Only applies to IP packets.
+ (PASS2 only) */
+ uint64_t grptag : 1; /**< When set, use the lower bit of the tag to compute
+ the group in the work queue entry
+ GRP = WQE[TAG[3:0]] & ~GRPTAGMASK + GRPTAGBASE.
+ Only applies to IP packets. (PASS2 only) */
+ uint64_t spare : 1; /**< Spare bit
+ (PASS2 only) */
+ uint64_t tag_mode : 2; /**< Which tag algorithm to use
+ 0 = always use tuple tag algorithm
+ 1 = always use mask tag algorithm
+ 2 = if packet is IP, use tuple else use mask
+ 3 = tuple XOR mask
+ (PASS2 only) */
+ uint64_t inc_vs : 2; /**< determines the VLAN ID (VID) to be included in
+ tuple tag when VLAN stacking is detected
+ 0 = do not include VID in tuple tag generation
+ 1 = include VID (VLAN0) in hash
+ 2 = include VID (VLAN1) in hash
+ 3 = include VID ([VLAN0,VLAN1]) in hash
+ (PASS2 only) */
+ uint64_t inc_vlan : 1; /**< when set, the VLAN ID is included in tuple tag
+ when VLAN stacking is not detected
+ 0 = do not include VID in tuple tag generation
+ 1 = include VID in hash
+ (PASS2 only) */
+ uint64_t inc_prt_flag : 1; /**< sets whether the port is included in tuple tag */
+ uint64_t ip6_dprt_flag : 1; /**< sets whether the TCP/UDP dst port is
+ included in tuple tag for IPv6 packets */
+ uint64_t ip4_dprt_flag : 1; /**< sets whether the TCP/UDP dst port is
+ included in tuple tag for IPv4 */
+ uint64_t ip6_sprt_flag : 1; /**< sets whether the TCP/UDP src port is
+ included in tuple tag for IPv6 packets */
+ uint64_t ip4_sprt_flag : 1; /**< sets whether the TCP/UDP src port is
+ included in tuple tag for IPv4 */
+ uint64_t ip6_nxth_flag : 1; /**< sets whether ipv6 includes next header in tuple
+ tag hash */
+ uint64_t ip4_pctl_flag : 1; /**< sets whether ipv4 includes protocol in tuple
+ tag hash */
+ uint64_t ip6_dst_flag : 1; /**< sets whether ipv6 includes dst address in tuple
+ tag hash */
+ uint64_t ip4_dst_flag : 1; /**< sets whether ipv4 includes dst address in tuple
+ tag hash */
+ uint64_t ip6_src_flag : 1; /**< sets whether ipv6 includes src address in tuple
+ tag hash */
+ uint64_t ip4_src_flag : 1; /**< sets whether ipv4 includes src address in tuple
+ tag hash */
+ uint64_t tcp6_tag_type : 2; /**< sets the tag_type of a TCP packet (IPv6)
+ 0 = ordered tags
+ 1 = atomic tags
+ 2 = Null tags */
+ uint64_t tcp4_tag_type : 2; /**< sets the tag_type of a TCP packet (IPv4)
+ 0 = ordered tags
+ 1 = atomic tags
+ 2 = Null tags */
+ uint64_t ip6_tag_type : 2; /**< sets whether IPv6 packet tag type
+ 0 = ordered tags
+ 1 = atomic tags
+ 2 = Null tags */
+ uint64_t ip4_tag_type : 2; /**< sets whether IPv4 packet tag type
+ 0 = ordered tags
+ 1 = atomic tags
+ 2 = Null tags */
+ uint64_t non_tag_type : 2; /**< sets whether non-IP packet tag type
+ 0 = ordered tags
+ 1 = atomic tags
+ 2 = Null tags */
+ uint64_t grp : 4; /* POW group for input pkts */
+ } bits;
+} octeon_pip_port_tag_cfg_t;
+
+
+/**
+ * Configure an ethernet input port
+ *
+ * @param port_num Port number to configure
+ * @param port_cfg Port hardware configuration
+ * @param port_tag_cfg
+ * Port POW tagging configuration
+ */
+static inline void octeon_pip_config_port(u_int port_num,
+ octeon_pip_port_cfg_t port_cfg,
+ octeon_pip_port_tag_cfg_t port_tag_cfg)
+{
+ oct_write64(OCTEON_PIP_PRT_CFGX(port_num), port_cfg.word64);
+ oct_write64(OCTEON_PIP_PRT_TAGX(port_num), port_tag_cfg.word64);
+}
+
+
+#endif /* __OCTEON_PIP_H__ */
diff --git a/sys/mips/cavium/dev/rgmii/octeon_pko.c b/sys/mips/cavium/dev/rgmii/octeon_pko.c
new file mode 100644
index 000000000000..e93ef87004bd
--- /dev/null
+++ b/sys/mips/cavium/dev/rgmii/octeon_pko.c
@@ -0,0 +1,378 @@
+/***********************license start***************
+ * Copyright (c) 2003-2008 Cavium Networks (support@cavium.com). All rights
+ * reserved.
+ *
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * 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.
+ *
+ * * Neither the name of Cavium Networks nor the names of
+ * its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written
+ * permission.
+ *
+ * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+ * AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS
+ * OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH
+ * RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY
+ * REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT
+ * DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES
+ * OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR
+ * PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET
+ * POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT
+ * OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+ *
+ *
+ * For any questions regarding licensing please contact marketing@caviumnetworks.com
+ *
+ ***********************license end**************************************/
+
+/*------------------------------------------------------------------
+ * octeon_pko.c Packet Output Unit
+ *
+ *------------------------------------------------------------------
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <mips/cavium/octeon_pcmap_regs.h>
+#include "octeon_fau.h"
+#include "octeon_fpa.h"
+#include "octeon_pko.h"
+
+
+/*
+ *
+ */
+static void octeon_pko_clear_port_counts (u_int port)
+{
+ u_int port_num;
+ octeon_pko_read_idx_t octeon_pko_idx;
+
+ octeon_pko_idx.word64 = 0;
+ octeon_pko_idx.bits.idx = port;
+ octeon_pko_idx.bits.inc = 0;
+ oct_write64(OCTEON_PKO_REG_READ_IDX, octeon_pko_idx.word64);
+
+ port_num = port;
+ oct_write64(OCTEON_PKO_MEM_COUNT0, port_num);
+ port_num = port;
+ oct_write64(OCTEON_PKO_MEM_COUNT1, port_num);
+}
+
+/*
+ * octeon_pko_init
+ *
+ */
+void octeon_pko_init (void)
+{
+ u_int queue, port;
+ octeon_pko_read_idx_t octeon_pko_idx;
+ octeon_pko_queue_cfg_t octeon_pko_queue_cfg;
+
+ for (port = 0; port < OCTEON_PKO_PORTS_MAX; port++) {
+ octeon_pko_clear_port_counts(port);
+ }
+
+ octeon_pko_idx.word64 = 0;
+ octeon_pko_idx.bits.idx = 0;
+ octeon_pko_idx.bits.inc = 1;
+ oct_write64(OCTEON_PKO_REG_READ_IDX, octeon_pko_idx.word64);
+ for (queue = 0; queue < OCTEON_PKO_QUEUES_MAX; queue++) {
+
+ octeon_pko_queue_cfg.word64 = 0;
+ octeon_pko_queue_cfg.bits.queue = queue;
+ octeon_pko_queue_cfg.bits.port = OCTEON_PKO_PORT_ILLEGAL;
+ octeon_pko_queue_cfg.bits.buf_ptr = 0;
+ oct_write64(OCTEON_PKO_MEM_QUEUE_PTRS, octeon_pko_queue_cfg.word64);
+ }
+}
+
+
+/*
+ * octeon_pko_enable
+ *
+ * enable pko
+ */
+void octeon_pko_enable (void)
+{
+
+ /*
+ * PKO enable
+ */
+ oct_write64(OCTEON_PKO_REG_FLAGS, 3); /* octeon_pko_enable() */
+}
+
+
+/*
+ * octeon_pko_disable
+ *
+ * disable pko
+ */
+void octeon_pko_disable (void)
+{
+
+ /*
+ * PKO disable
+ */
+ oct_write64(OCTEON_PKO_REG_FLAGS, 0); /* pko_disable() */
+}
+
+/*
+ * octeon_pko_config_cmdbuf_global_defaults
+ *
+ */
+void octeon_pko_config_cmdbuf_global_defaults (u_int cmdbuf_pool,
+ u_int cmdbuf_pool_elem_size )
+{
+ octeon_pko_pool_cfg_t octeon_pko_pool_config;
+
+ octeon_pko_pool_config.word64 = 0;
+ octeon_pko_pool_config.bits.pool = cmdbuf_pool;
+ octeon_pko_pool_config.bits.size = cmdbuf_pool_elem_size;
+ oct_write64(OCTEON_PKO_CMD_BUF, octeon_pko_pool_config.word64);
+}
+
+/*
+ * octeon_pko_config_rgmx_ports
+ *
+ * Configure rgmx pko. Always enables 4 + 4 ports
+ */
+void octeon_pko_config_rgmx_ports (void)
+{
+ octeon_pko_reg_gmx_port_mode_t octeon_pko_gmx_mode;
+
+ octeon_pko_gmx_mode.word64 = 0;
+ octeon_pko_gmx_mode.bits.mode0 = 2; /* 16 >> 2 == 4 ports */
+ octeon_pko_gmx_mode.bits.mode1 = 2; /* 16 >> 2 == 4 ports */
+ oct_write64(OCTEON_PKO_GMX_PORT_MODE, octeon_pko_gmx_mode.word64);
+}
+
+
+/*
+ * octeon_pko_config
+ *
+ * Configure PKO
+ *
+ */
+void octeon_pko_config (void)
+{
+}
+
+/*
+ * octeon_pko_get_port_status
+ *
+ * Get the status counters for a PKO port.
+ *
+ * port_num Port number to get statistics for.
+ * clear Set to 1 to clear the counters after they are read
+ * status Where to put the results.
+ */
+void octeon_pko_get_port_status (u_int port, u_int clear,
+ octeon_pko_port_status_t *status)
+{
+ octeon_word_t packet_num;
+ octeon_pko_read_idx_t octeon_pko_idx;
+
+ packet_num.word64 = 0;
+
+ octeon_pko_idx.word64 = 0;
+ octeon_pko_idx.bits.idx = port;
+ octeon_pko_idx.bits.inc = 0;
+ oct_write64(OCTEON_PKO_REG_READ_IDX, octeon_pko_idx.word64);
+
+ packet_num.word64 = oct_read64(OCTEON_PKO_MEM_COUNT0);
+ status->packets = packet_num.bits.word32lo;
+
+ status->octets = oct_read64(OCTEON_PKO_MEM_COUNT1);
+ status->doorbell = oct_read64(OCTEON_PKO_MEM_DEBUG9);
+ status->doorbell = (status->doorbell >> 8) & 0xfffff;
+ if (clear) {
+ octeon_pko_clear_port_counts(port);
+ }
+}
+
+static void octeon_pko_doorbell_data_dump(uint64_t port);
+
+static void octeon_pko_doorbell_data_dump (uint64_t port)
+{
+ octeon_pko_port_status_t status;
+
+ octeon_pko_get_port_status(port, 0, &status);
+ printf("\n Port #%lld Pkts %ld Bytes %lld DoorBell %lld",
+ (unsigned long long)port, status.packets,
+ (unsigned long long)status.octets,
+ (unsigned long long)status.doorbell);
+}
+
+/*
+ * octeon_pko_show
+ *
+ * Show the OCTEON_PKO status & configs
+ */
+void octeon_pko_show (u_int start_port, u_int end_port)
+{
+ u_int queue, queue_max, gmx_int0_ports, gmx_int1_ports;
+ u_int port;
+ uint64_t val64;
+ octeon_pko_port_status_t status;
+ octeon_pko_pool_cfg_t octeon_pko_pool_config;
+ octeon_pko_read_idx_t octeon_pko_idx;
+ octeon_pko_queue_mode_t octeon_pko_queue_mode;
+ octeon_pko_reg_gmx_port_mode_t octeon_pko_gmx_mode;
+ octeon_pko_crc_ports_enable_t octeon_pko_crc_ports;
+ octeon_pko_queue_cfg_t octeon_pko_queue_cfg;
+
+ printf("\n\nPKO Status:");
+ val64 = oct_read64(OCTEON_PKO_REG_FLAGS);
+ if ((val64 & 0x3) != 0x3) {
+ printf(" Disabled");
+ return;
+ } else {
+ printf(" Enabled");
+ }
+ octeon_pko_queue_mode.word64 = oct_read64(OCTEON_PKO_QUEUE_MODE);
+ queue_max = (128 >> octeon_pko_queue_mode.bits.mode);
+ octeon_pko_gmx_mode.word64 = oct_read64(OCTEON_PKO_GMX_PORT_MODE);
+ gmx_int0_ports = (16 >> octeon_pko_gmx_mode.bits.mode0);
+ gmx_int1_ports = (16 >> octeon_pko_gmx_mode.bits.mode1);
+ octeon_pko_crc_ports.word64 = oct_read64(OCTEON_PKO_REG_CRC_ENABLE);
+ printf("\n Total Queues: 0..%d Ports GMX0 %d GMX1 %d CRC 0x%X",
+ queue_max - 1, gmx_int0_ports, gmx_int1_ports,
+ octeon_pko_crc_ports.bits.crc_ports_mask);
+
+ octeon_pko_pool_config.word64 = oct_read64(OCTEON_PKO_CMD_BUF);
+ printf("\n CmdBuf Pool: %d CmdBuf Size in Words: %d Bytes: %d",
+ octeon_pko_pool_config.bits.pool, octeon_pko_pool_config.bits.size,
+ octeon_pko_pool_config.bits.size * 8);
+
+ octeon_pko_idx.word64 = 0;
+ octeon_pko_idx.bits.idx = 0;
+ octeon_pko_idx.bits.inc = 1;
+ oct_write64(OCTEON_PKO_REG_READ_IDX, octeon_pko_idx.word64);
+ for (queue = 0; queue < queue_max; queue++) {
+
+ octeon_pko_queue_cfg.word64 = oct_read64(OCTEON_PKO_MEM_QUEUE_PTRS);
+ if (!octeon_pko_queue_cfg.bits.buf_ptr) continue;
+ printf("\n Port # %d Queue %3d [%d] BufPtr: 0x%llX Mask: %X%s",
+ octeon_pko_queue_cfg.bits.port, octeon_pko_queue_cfg.bits.queue,
+ octeon_pko_queue_cfg.bits.index,
+ (unsigned long long)octeon_pko_queue_cfg.bits.buf_ptr,
+ octeon_pko_queue_cfg.bits.qos_mask,
+ (octeon_pko_queue_cfg.bits.tail)? " Last":"");
+ }
+ printf("\n");
+
+ for (port = start_port; port < (end_port + 1); port++) {
+
+ octeon_pko_get_port_status(port, 0, &status);
+ octeon_pko_doorbell_data_dump(port);
+
+ }
+}
+
+
+
+
+/*
+ * octeon_pko_config_port
+ *
+ * Configure a output port and the associated queues for use.
+ *
+ */
+octeon_pko_status_t octeon_pko_config_port (u_int port,
+ u_int base_queue,
+ u_int num_queues,
+ const u_int priority[],
+ u_int pko_output_cmdbuf_fpa_pool,
+ octeon_pko_sw_queue_info_t sw_queues[])
+{
+ octeon_pko_status_t result_code;
+ u_int queue;
+ octeon_pko_queue_cfg_t qconfig;
+
+ if ((port >= OCTEON_PKO_PORTS_MAX) && (port != OCTEON_PKO_PORT_ILLEGAL)) {
+ printf("\n%% Error: octeon_pko_config_port: Invalid port %u", port);
+ return (OCTEON_PKO_INVALID_PORT);
+ }
+
+ if ((base_queue + num_queues) > OCTEON_PKO_QUEUES_MAX) {
+ printf("\n%% Error: octeon_pko_config_port: Invalid queue range");
+ return (OCTEON_PKO_INVALID_QUEUE);
+ }
+
+ result_code = OCTEON_PKO_SUCCESS;
+
+ for (queue = 0; queue < num_queues; queue++) {
+ uint64_t buf_ptr = 0;
+
+ qconfig.word64 = 0;
+ qconfig.bits.tail = (queue == (num_queues - 1)) ? 1 : 0;
+ qconfig.bits.index = queue;
+ qconfig.bits.port = port;
+ qconfig.bits.queue = base_queue + queue;
+
+ /* Convert the priority into an enable bit field. */
+ /* Try to space the bits out evenly so the pkts don't get grouped up */
+ switch ((int)priority[queue]) {
+ case 0: qconfig.bits.qos_mask = 0x00; break;
+ case 1: qconfig.bits.qos_mask = 0x01; break;
+ case 2: qconfig.bits.qos_mask = 0x11; break;
+ case 3: qconfig.bits.qos_mask = 0x49; break;
+ case 4: qconfig.bits.qos_mask = 0x55; break;
+ case 5: qconfig.bits.qos_mask = 0x57; break;
+ case 6: qconfig.bits.qos_mask = 0x77; break;
+ case 7: qconfig.bits.qos_mask = 0x7f; break;
+ case 8: qconfig.bits.qos_mask = 0xff; break;
+ default:
+ printf("\n%% Error: octeon_pko_config_port Invalid priority %llu",
+ (unsigned long long)priority[queue]);
+ qconfig.bits.qos_mask = 0xff;
+ result_code = OCTEON_PKO_INVALID_PRIORITY;
+ break;
+ }
+ if (port != OCTEON_PKO_PORT_ILLEGAL) {
+
+ buf_ptr = octeon_fpa_alloc_phys(pko_output_cmdbuf_fpa_pool);
+ if (!buf_ptr) {
+ printf("\n%% Error: octeon_pko_config_port: Unable to allocate");
+ return (OCTEON_PKO_NO_MEMORY);
+ }
+
+ sw_queues[queue].xmit_command_state = (buf_ptr << OCTEON_PKO_INDEX_BITS);
+ octeon_spinlock_init(&(sw_queues[queue].lock));
+
+//#define DEBUG_TX
+
+#ifdef DEBUG_TX
+ printf(" PKO: port %u pool: %u base+queue %u %u %u buf_ptr: 0x%llX\n",
+ port,
+ pko_output_cmdbuf_fpa_pool,
+ base_queue, queue, base_queue+queue,
+ buf_ptr);
+
+#endif
+ qconfig.bits.buf_ptr = buf_ptr;
+ oct_write64(OCTEON_PKO_MEM_QUEUE_PTRS, qconfig.word64);
+
+ }
+ }
+
+ return (result_code);
+}
+
diff --git a/sys/mips/cavium/dev/rgmii/octeon_pko.h b/sys/mips/cavium/dev/rgmii/octeon_pko.h
new file mode 100644
index 000000000000..c6ece8c2a379
--- /dev/null
+++ b/sys/mips/cavium/dev/rgmii/octeon_pko.h
@@ -0,0 +1,332 @@
+/***********************license start***************
+ * Copyright (c) 2003-2008 Cavium Networks (support@cavium.com). All rights
+ * reserved.
+ *
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * 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.
+ *
+ * * Neither the name of Cavium Networks nor the names of
+ * its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written
+ * permission.
+ *
+ * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+ * AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS
+ * OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH
+ * RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY
+ * REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT
+ * DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES
+ * OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR
+ * PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET
+ * POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT
+ * OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+ *
+ *
+ * For any questions regarding licensing please contact marketing@caviumnetworks.com
+ *
+ ***********************license end**************************************/
+
+/* $FreeBSD$ */
+
+/*------------------------------------------------------------------
+ * octeon_pko.h Packet Output Block
+ *
+ *------------------------------------------------------------------
+ */
+
+
+#ifndef ___OCTEON_PKO__H___
+#define ___OCTEON_PKO__H___
+
+
+
+/*
+ * PKO Command Buffer Register.
+ * Specify Pool-# and Size of each entry in Pool. For Output Cmd Buffers.
+ */
+typedef union {
+ uint64_t word64;
+ struct {
+ uint64_t unused_mbz : 41; /* Must be zero */
+ uint64_t pool : 3; /* FPA Pool to use */
+ uint64_t unused_mbz2 : 7; /* Must be zero */
+ uint64_t size : 13; /* Size of the pool blocks */
+ } bits;
+} octeon_pko_pool_cfg_t;
+
+
+/*
+ * PKO GMX Mode Register
+ * Specify the # of GMX1 ports and GMX0 ports
+ */
+typedef union {
+ uint64_t word64;
+ struct {
+ uint64_t unused_mbz : 58; /* MBZ */
+ uint64_t mode1 : 3; /* # GMX1 ports; */
+ /* 16 >> MODE1, 0 <= MODE1 <=4 */
+ uint64_t mode0 : 3; /* # GMX0 ports; */
+ /* 16 >> MODE0, 0 <= MODE0 <=4 */
+ } bits;
+} octeon_pko_reg_gmx_port_mode_t;
+
+
+typedef union {
+ uint64_t word64;
+ struct {
+ uint64_t unused_mbz : 62; /* MBZ */
+ uint64_t mode : 2; /* Queues Mode */
+ } bits;
+} octeon_pko_queue_mode_t;
+
+
+typedef union {
+ uint64_t word64;
+ struct {
+ uint64_t unused_mbz : 32; /* MBZ */
+ uint64_t crc_ports_mask : 32; /* CRC Ports Enable mask */
+ } bits;
+} octeon_pko_crc_ports_enable_t;
+
+
+
+#define OCTEON_PKO_QUEUES_MAX 128
+#define OCTEON_PKO_PORTS_MAX 36
+#define OCTEON_PKO_PORT_ILLEGAL 63
+
+/* Defines how the PKO command buffer FAU register is used */
+
+#define OCTEON_PKO_INDEX_BITS 12
+#define OCTEON_PKO_INDEX_MASK ((1ull << OCTEON_PKO_INDEX_BITS) - 1)
+
+
+
+typedef enum {
+ OCTEON_PKO_SUCCESS,
+ OCTEON_PKO_INVALID_PORT,
+ OCTEON_PKO_INVALID_QUEUE,
+ OCTEON_PKO_INVALID_PRIORITY,
+ OCTEON_PKO_NO_MEMORY
+} octeon_pko_status_t;
+
+
+typedef struct {
+ long packets;
+ uint64_t octets;
+ uint64_t doorbell;
+} octeon_pko_port_status_t;
+
+
+typedef union {
+ uint64_t word64;
+ struct {
+ octeon_mips_space_t mem_space : 2; /* Octeon IO_SEG */
+ uint64_t unused_mbz :13; /* Must be zero */
+ uint64_t is_io : 1; /* Must be one */
+ uint64_t did : 8; /* device-ID on non-coherent bus*/
+ uint64_t unused_mbz2 : 4; /* Must be zero */
+ uint64_t unused_mbz3 :18; /* Must be zero */
+ uint64_t port : 6; /* output port */
+ uint64_t queue : 9; /* output queue to send */
+ uint64_t unused_mbz4 : 3; /* Must be zero */
+ } bits;
+} octeon_pko_doorbell_address_t;
+
+/*
+ * Structure of the first packet output command word.
+ */
+typedef union {
+ uint64_t word64;
+ struct {
+ octeon_fau_op_size_t size1 : 2; /* The size of reg1 operation */
+ /* - could be 8, 16, 32, or 64 bits */
+ octeon_fau_op_size_t size0 : 2; /* The size of the reg0 operation */
+ /* - could be 8, 16, 32, or 64 bits */
+ uint64_t subone1 : 1; /* Subtract 1, else sub pkt size */
+ uint64_t reg1 :11; /* The register, subtract will be */
+ /* done if reg1 is non-zero */
+ uint64_t subone0 : 1; /* Subtract 1, else sub pkt size */
+ uint64_t reg0 :11; /* The register, subtract will be */
+ /* done if reg0 is non-zero */
+ uint64_t unused : 2; /* Must be zero */
+ uint64_t wqp : 1; /* If rsp, then word3 contains a */
+ /* ptr to a work queue entry */
+ uint64_t rsp : 1; /* HW will respond when done */
+ uint64_t gather : 1; /* If set, the supplied pkt_ptr is */
+ /* a ptr to a list of pkt_ptr's */
+ uint64_t ipoffp1 : 7; /* Off to IP hdr. For HW checksum */
+ uint64_t ignore_i : 1; /* Ignore I bit in all pointers */
+ uint64_t dontfree : 1; /* Don't free buffs containing pkt */
+ uint64_t segs : 6; /* Number of segs. If gather set, */
+ /* also gather list length */
+ uint64_t total_bytes :16; /* Includes L2, w/o trailing CRC */
+ } bits;
+} octeon_pko_command_word0_t;
+
+
+typedef union {
+ void* ptr;
+ uint64_t word64;
+ struct {
+ uint64_t i : 1; /* Invert the "free" pick of the overall pkt. */
+ /* For inbound pkts, HW always sets this to 0 */
+ uint64_t back : 4; /* Amount to back up to get to buffer start */
+ /* in cache lines. This is mostly less than 1 */
+ /* complete cache line; so the value is zero */
+ uint64_t pool : 3; /* FPA pool that the buffer belongs to */
+ uint64_t size :16; /* segment size (bytes) pointed at by addr */
+ uint64_t addr :40; /* Ptr to 1st data byte. NOT buffer */
+ } bits;
+} octeon_pko_packet_ptr_t;
+
+
+/*
+ * Definition of the hardware structure used to configure an
+ * output queue.
+ */
+typedef union {
+ uint64_t word64;
+ struct {
+ uint64_t unused_mbz : 3; /* Must be zero */
+ uint64_t qos_mask : 8; /* Control Mask priority */
+ /* across 8 QOS levels */
+ uint64_t buf_ptr : 36; /* Command buffer pointer, */
+ /* 8 byte-aligned */
+ uint64_t tail : 1; /* Set if this queue is the tail */
+ /* of the port queue array */
+ uint64_t index : 3; /* Index (distance from head) in */
+ /* the port queue array */
+ uint64_t port : 6; /* Port ID for this queue mapping */
+ uint64_t queue : 7; /* Hardware queue number */
+ } bits;
+} octeon_pko_queue_cfg_t;
+
+
+typedef union {
+ uint64_t word64;
+ struct {
+ uint64_t unused_mbz : 48;
+ uint64_t inc : 8;
+ uint64_t idx : 8;
+ } bits;
+} octeon_pko_read_idx_t;
+
+
+typedef struct octeon_pko_sw_queue_info_t_
+{
+ uint64_t xmit_command_state;
+ octeon_spinlock_t lock;
+ uint32_t pad[29];
+} octeon_pko_sw_queue_info_t;
+
+
+
+#define OCTEON_DID_PKT 10ULL
+#define OCTEON_DID_PKT_SEND OCTEON_ADDR_FULL_DID(OCTEON_DID_PKT,2ULL)
+
+
+/*
+ * Ring the packet output doorbell. This tells the packet
+ * output hardware that "len" command words have been added
+ * to its pending list. This command includes the required
+ * SYNCW before the doorbell ring.
+ *
+ * @param port Port the packet is for
+ * @param queue Queue the packet is for
+ * @param len Length of the command in 64 bit words
+ */
+extern void octeon_pko_doorbell_data(u_int port);
+
+//#define CORE_0_ONLY 1
+
+static inline void octeon_pko_ring_doorbell (u_int port, u_int queue,
+ u_int len)
+{
+ octeon_pko_doorbell_address_t ptr;
+
+ ptr.word64 = 0;
+ ptr.bits.mem_space = OCTEON_IO_SEG;
+ ptr.bits.did = OCTEON_DID_PKT_SEND;
+ ptr.bits.is_io = 1;
+ ptr.bits.port = port;
+ ptr.bits.queue = queue;
+ OCTEON_SYNCWS;
+ oct_write64(ptr.word64, len);
+}
+
+
+
+#define OCTEON_PKO_QUEUES_PER_PORT_INTERFACE0 1
+#define OCTEON_PKO_QUEUES_PER_PORT_INTERFACE1 1
+#define OCTEON_PKO_QUEUES_PER_PORT_PCI 1
+
+/*
+ * octeon_pko_get_base_queue
+ *
+ * For a given port number, return the base pko output queue
+ * for the port.
+ */
+static inline u_int octeon_pko_get_base_queue (u_int port)
+{
+ if (port < 16) {
+ return (port * OCTEON_PKO_QUEUES_PER_PORT_INTERFACE0);
+ }
+ if (port < 32) {
+ return (16 * OCTEON_PKO_QUEUES_PER_PORT_INTERFACE0 +
+ (port - 16) * OCTEON_PKO_QUEUES_PER_PORT_INTERFACE1);
+ }
+ return (16 * OCTEON_PKO_QUEUES_PER_PORT_INTERFACE0 +
+ 16 * OCTEON_PKO_QUEUES_PER_PORT_INTERFACE1 +
+ (port - 32) * OCTEON_PKO_QUEUES_PER_PORT_PCI);
+}
+
+
+/*
+ * For a given port number, return the number of pko output queues.
+ *
+ * @param port Port number
+ * @return Number of output queues
+ */
+static inline u_int octeon_pko_get_num_queues(u_int port)
+{
+ if (port < 16) {
+ return (OCTEON_PKO_QUEUES_PER_PORT_INTERFACE0);
+ } else if (port<32) {
+ return (OCTEON_PKO_QUEUES_PER_PORT_INTERFACE1);
+ }
+
+ return (OCTEON_PKO_QUEUES_PER_PORT_PCI);
+}
+
+
+
+/*
+ * Externs
+ */
+extern void octeon_pko_init(void);
+extern void octeon_pko_enable(void);
+extern void octeon_pko_disable(void);
+extern void octeon_pko_show(u_int start_port, u_int end_port);
+extern void octeon_pko_config(void);
+extern void octeon_pko_config_cmdbuf_global_defaults(u_int cmdbuf_pool, u_int elem_size);
+extern void octeon_pko_config_rgmx_ports(void);
+extern void octeon_pko_get_port_status(u_int, u_int, octeon_pko_port_status_t *status);
+extern octeon_pko_status_t octeon_pko_config_port(u_int port,
+ u_int base_queue,
+ u_int num_queues,
+ const u_int priority[],
+ u_int pko_output_cmdbuf_fpa_pool,
+ octeon_pko_sw_queue_info_t sw_queues[]);
+
+
+#endif /* ___OCTEON_PKO__H___ */
diff --git a/sys/mips/cavium/dev/rgmii/octeon_rgmx.c b/sys/mips/cavium/dev/rgmii/octeon_rgmx.c
new file mode 100644
index 000000000000..5b84af5dcaad
--- /dev/null
+++ b/sys/mips/cavium/dev/rgmii/octeon_rgmx.c
@@ -0,0 +1,2328 @@
+/***********************license start***************
+ * Copyright (c) 2003-2008 Cavium Networks (support@cavium.com). All rights
+ * reserved.
+ *
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * 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.
+ *
+ * * Neither the name of Cavium Networks nor the names of
+ * its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written
+ * permission.
+ *
+ * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+ * AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS
+ * OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH
+ * RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY
+ * REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT
+ * DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES
+ * OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR
+ * PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET
+ * POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT
+ * OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+ *
+ *
+ * For any questions regarding licensing please contact marketing@caviumnetworks.com
+ *
+ ***********************license end**************************************/
+
+/*
+ * octeon_rgmx.c RGMII Ethernet Interfaces on Octeon
+ *
+ */
+
+
+/*
+ * Driver for the Reduced Gigabit Media Independent Interface (RGMII)
+ * present on the Cavium Networks' Octeon chip.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/sysctl.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/power.h>
+#include <sys/smp.h>
+#include <sys/time.h>
+#include <sys/timetc.h>
+#include <sys/malloc.h>
+#include <sys/kthread.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/mbuf.h>
+#include <sys/taskqueue.h>
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_mib.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#include <net/bpf.h>
+
+#include <machine/clock.h>
+#include <machine/locore.h>
+#include <machine/md_var.h>
+
+#include <mips/cavium/octeon_pcmap_regs.h>
+
+#include "octeon_fau.h"
+#include "octeon_fpa.h"
+#include "octeon_ipd.h"
+#include "octeon_pko.h"
+#include "octeon_pip.h"
+#include "octeon_rgmx.h"
+
+
+/* The "battleship" boards have 8 ports */
+#define OCTEON_RGMX_NUM_PORTS_MAX 8
+#define NUM_TX_PACKETS 80
+#define NUM_RX_PACKETS 300
+#define MAX_RX_BUFS (NUM_RX_PACKETS) * (OCTEON_RGMX_NUM_PORTS_MAX)
+#define MAX_TX_BUFS (NUM_TX_PACKETS)
+#define OCTEON_RGMX_DEV_NAME "rgmx"
+#define OCTEON_RGMX_MIN_PORT 0
+#define OCTEON_RGMX_MAX_PORT 19
+#define OCTEON_RGMX_OQUEUE_PER_PORT 8
+
+
+#define OCTEON_RGMX_SCHEDULED_ISRS 1 /* Use Scheduled ISRs from kernel tasks */
+
+
+#ifndef POW_MAX_LOOP
+#define POW_MAX_LOOP 0x800
+#endif
+
+
+/*
+ * CIU related stuff for enabling POW interrupts
+ */
+#define OCTEON_RGMX_CIU_INTX CIU_INT_0
+#define OCTEON_RGMX_CIU_ENX CIU_EN_0
+
+MALLOC_DEFINE(M_RGMII_WQE, "rgmii_wqe", "FPA pool for WQEs");
+
+/* Driver data */
+
+struct rgmx_softc_dev {
+ device_t sc_dev; /* Device ID */
+ uint64_t link_status;
+ struct ifnet *ifp;
+ int sc_unit;
+
+ u_int port;
+ u_int idx;
+ u_char ieee[6];
+
+ u_short txb_size; /* size of TX buffer, in bytes */
+
+ /* Transmission buffer management. */
+ u_short txb_free; /* free bytes in TX buffer */
+ u_char txb_count; /* number of packets in TX buffer */
+ u_char txb_sched; /* number of scheduled packets */
+
+ /* Media information. */
+ struct ifmedia media; /* used by if_media. */
+ u_short mbitmap; /* bitmap for supported media; see bit2media */
+ int defmedia; /* default media */
+ struct ifqueue tx_pending_queue; /* Queue of mbuf given to PKO currently */
+ octeon_pko_sw_queue_info_t *outq_ptr;
+
+ struct mtx mtx;
+};
+
+
+/*
+ * Device methods
+ */
+static int rgmii_probe(device_t);
+static void rgmii_identify(driver_t *, device_t);
+static int rgmii_attach(device_t);
+
+
+
+/*
+ * Octeon specific routines
+ */
+static int octeon_has_4ports(void);
+static void octeon_config_rgmii_port(u_int port);
+static void octeon_rgmx_config_pip(u_int port);
+static void octeon_line_status_loop(void *);
+static void octeon_rx_loop(void *);
+static void octeon_config_hw_units_post_ports(void);
+static void octeon_config_hw_units_pre_ports(void);
+static void octeon_config_hw_units_port(struct rgmx_softc_dev *sc, u_int port);
+static struct rgmx_softc_dev *get_rgmx_softc(u_int port);
+static void octeon_rgmx_start_port(u_int port);
+static u_int octeon_rgmx_stop_port(u_int port);
+static u_int get_rgmx_port_ordinal(u_int port);
+static void octeon_rgmx_set_mac(u_int port);
+static void octeon_rgmx_init_sc(struct rgmx_softc_dev *sc, device_t dev, u_int port, u_int num_devices);
+static int octeon_rgmx_init_ifnet(struct rgmx_softc_dev *sc);
+static void octeon_rgmx_stop(struct rgmx_softc_dev *sc);
+static void octeon_rgmx_config_speed(u_int port, u_int);
+#ifdef DEBUG_RGMX_DUMP
+static void octeon_dump_rgmx_stats(u_int port);
+static void octeon_dump_pow_stats(void);
+#endif
+#ifdef __not_used__
+static void rgmx_timer_periodic(void);
+#endif
+static void octeon_rgmx_enable_RED_all(int, int);
+
+#ifdef OCTEON_RGMX_SCHEDULED_ISRS
+static void octeon_rgmx_isr_link(void *context, int pending);
+static void octeon_rgmx_isr_rxtx(void *context, int pending);
+static int octeon_rgmx_intr_fast(void *arg);
+#else
+static int octeon_rgmx_intr(void *arg);
+#endif
+
+
+
+
+
+
+
+/* Standard driver entry points. These can be static. */
+static void octeon_rgmx_init (void *);
+//static driver_intr_t rgmx_intr;
+static void octeon_rgmx_config_cam (struct ifnet *);
+static int octeon_rgmx_ioctl (struct ifnet *, u_long, caddr_t);
+static void octeon_rgmx_output_start (struct ifnet *);
+static void octeon_rgmx_output_start_locked (struct ifnet *);
+static int octeon_rgmx_medchange (struct ifnet *);
+static void octeon_rgmx_medstat (struct ifnet *, struct ifmediareq *);
+
+
+/* Mapping between media bitmap (in fe_softc.mbitmap) and ifm_media. */
+static int const bit2media [] = {
+ IFM_ETHER | IFM_AUTO,
+ IFM_ETHER | IFM_MANUAL,
+ IFM_ETHER | IFM_10_T,
+ IFM_ETHER | IFM_10_2,
+ IFM_ETHER | IFM_10_5,
+ IFM_ETHER | IFM_10_FL,
+ IFM_ETHER | IFM_10_T,
+ /* More can be added here... */
+};
+
+/* Mapping between media bitmap (in fe_softc.mbitmap) and ifm_media. */
+#define MB_HA 0x0001
+#define MB_HM 0x0002
+#define MB_HT 0x0004
+#define MB_H2 0x0008
+#define MB_H5 0x0010
+#define MB_HF 0x0020
+#define MB_FT 0x0040
+
+#define LEBLEN (ETHER_MAX_LEN + ETHER_VLAN_ENCAP_LEN)
+
+
+static struct rgmx_softc_dev *rgmx_scdev_array[OCTEON_RGMX_NUM_PORTS_MAX] = {NULL};
+static u_int port_array[OCTEON_RGMX_NUM_PORTS_MAX] = {0};
+static u_int num_devices = 0;
+static octeon_pko_sw_queue_info_t output_queues_array[OCTEON_RGMX_NUM_PORTS_MAX * OCTEON_RGMX_OQUEUE_PER_PORT];
+static struct resource *irq_res; /* Interrupt resource. */
+static void *int_handler_tag;
+
+
+#ifdef OCTEON_RGMX_SCHEDULED_ISRS
+
+struct task link_isr_task;
+struct task rxtx_isr_task;
+struct taskqueue *tq; /* private task queue */
+
+#endif
+
+
+
+static u_int get_rgmx_port_ordinal (u_int port)
+{
+ u_int idx;
+
+ for (idx = 0; idx < OCTEON_RGMX_NUM_PORTS_MAX; idx++) {
+ if (port_array[idx] == port) {
+ return (idx);
+ }
+ }
+ return (-1);
+}
+
+static struct rgmx_softc_dev *get_rgmx_softc (u_int port)
+{
+ u_int idx;
+
+ idx = get_rgmx_port_ordinal(port);
+ if (idx != -1) {
+ return (rgmx_scdev_array[idx]);
+ }
+ return (NULL);
+}
+
+
+
+static void octeon_rgmx_init_sc (struct rgmx_softc_dev *sc, device_t dev, u_int port, u_int num_devices)
+{
+ int ii;
+
+ /* No software-controllable media selection. */
+ sc->mbitmap = MB_HM;
+ sc->defmedia = MB_HM;
+
+ sc->sc_dev = dev;
+ sc->port = port;
+ sc->idx = num_devices;
+ sc->link_status = 0;
+ sc->sc_unit = num_devices;
+ sc->mbitmap = MB_HT;
+ sc->defmedia = MB_HT;
+ sc->tx_pending_queue.ifq_maxlen = NUM_TX_PACKETS;
+ sc->tx_pending_queue.ifq_head = sc->tx_pending_queue.ifq_tail = NULL;
+ sc->tx_pending_queue.ifq_len = sc->tx_pending_queue.ifq_drops = 0;
+ mtx_init(&sc->tx_pending_queue.ifq_mtx, "if->sc->txpq.ifqmtx", NULL, MTX_DEF);
+
+ sc->outq_ptr = &(output_queues_array[num_devices * OCTEON_RGMX_OQUEUE_PER_PORT]);
+
+ for (ii = 0; ii < 6; ii++) {
+ sc->ieee[ii] = octeon_mac_addr[ii];
+ }
+ sc->ieee[5] += get_rgmx_port_ordinal(port);
+
+}
+
+static int octeon_rgmx_init_ifnet (struct rgmx_softc_dev *sc)
+{
+ struct ifnet *ifp;
+
+ ifp = sc->ifp = if_alloc(IFT_ETHER);
+ if (NULL == ifp) {
+ device_printf(sc->sc_dev, "can not ifalloc for rgmx port\n");
+ return (ENOSPC);
+ }
+ /*
+ * Initialize ifnet structure
+ */
+ ifp->if_softc = sc;
+ if_initname(sc->ifp, device_get_name(sc->sc_dev), device_get_unit(sc->sc_dev));
+ ifp->if_start = octeon_rgmx_output_start;
+ ifp->if_ioctl = octeon_rgmx_ioctl;
+ ifp->if_hwassist = CSUM_TCP | CSUM_UDP;
+ ifp->if_capabilities = IFCAP_HWCSUM;
+ ifp->if_capenable = ifp->if_capabilities;
+ ifp->if_init = octeon_rgmx_init;
+ ifp->if_linkmib = NULL; // &sc->mibdata;
+ ifp->if_linkmiblen = 0; // sizeof (sc->mibdata);
+ /*
+ * Set fixed interface flags.
+ */
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+// | IFF_NEEDSGIANT;
+ if (ifp->if_snd.ifq_maxlen == 0)
+ ifp->if_snd.ifq_maxlen = ifqmaxlen;
+
+ ifmedia_init(&sc->media, 0, octeon_rgmx_medchange, octeon_rgmx_medstat);
+ ifmedia_add(&sc->media, bit2media[0], 0, NULL);
+ ifmedia_set(&sc->media, bit2media[0]);
+
+ ether_ifattach(sc->ifp, sc->ieee);
+
+ return (0);
+}
+
+
+
+/* Driver methods */
+
+
+/* ------------------------------------------------------------------- *
+ * rgmii_identify() *
+ * ------------------------------------------------------------------- */
+static void rgmii_identify (driver_t *drv, device_t parent)
+{
+ BUS_ADD_CHILD(parent, 0, "rgmii", 0);
+}
+
+
+/* ------------------------------------------------------------------- *
+ * rgmii_probe() *
+ * ------------------------------------------------------------------- */
+static int rgmii_probe (device_t dev)
+{
+ if (device_get_unit(dev) != 0)
+ panic("can't probe/attach more rgmii devices\n");
+
+ device_set_desc(dev, "Octeon RGMII");
+ return (0);
+}
+
+
+
+/* ------------------------------------------------------------------- *
+ * rgmii_attach() *
+ * ------------------------------------------------------------------- */
+static int rgmii_attach (device_t dev)
+{
+ struct rgmx_softc_dev *sc;
+ device_t child;
+ int iface, port, nr_ports, error;
+ void *softc;
+ int irq_rid;
+
+ octeon_config_hw_units_pre_ports();
+
+ /* Count interfaces and ports*/
+ octeon_gmxx_inf_mode_t iface_mode;
+ iface_mode.word64 = 0;
+
+ for (iface = 0; iface < 2; iface++) {
+ iface_mode.word64 = oct_read64(OCTEON_RGMX_INF_MODE(iface));
+
+ /* interface is either disabled or SPI */
+ if (!iface_mode.bits.en)
+ continue;
+ if (octeon_get_chipid() == OCTEON_CN3020_CHIP) {
+ nr_ports = 2;
+ } else {
+ nr_ports = (octeon_has_4ports()) ? 4 : 3;
+ if (iface_mode.bits.type ) {
+ if (octeon_get_chipid() == OCTEON_CN5020_CHIP)
+ nr_ports = 2;
+ else
+ continue;
+ }
+ }
+
+ oct_write64(OCTEON_RGMX_TX_PRTS(iface), nr_ports);
+
+ for (port = iface * 16; port < iface * 16 + nr_ports; port++) {
+
+ child = device_add_child(dev, OCTEON_RGMX_DEV_NAME, num_devices);
+ if (child == NULL)
+ panic("%s: device_add_child() failed\n", __func__);
+
+ softc = malloc(sizeof(struct rgmx_softc_dev), M_DEVBUF, M_NOWAIT | M_ZERO);
+ if (!softc) {
+ panic("%s malloc failed for softc\n", __func__);
+ }
+ device_set_softc(child, softc);
+ device_set_desc(child, "Octeon RGMII");
+ sc = device_get_softc(child);
+ if (!sc) {
+ printf(" No sc\n");
+ num_devices++;
+ continue;
+ }
+ port_array[num_devices] = port;
+ rgmx_scdev_array[num_devices] = sc;
+ RGMX_LOCK_INIT(sc, device_get_nameunit(child));
+ octeon_rgmx_init_sc(sc, child, port, num_devices);
+ octeon_config_hw_units_port(sc, port);
+ if (octeon_rgmx_init_ifnet(sc)) {
+ device_printf(dev, " ifinit failed for rgmx port %u\n", port);
+ return (ENOSPC);
+ }
+ num_devices++;
+ }
+ }
+
+ octeon_config_hw_units_post_ports();
+
+ irq_rid = 0;
+ irq_res = bus_alloc_resource(dev, SYS_RES_IRQ, &irq_rid, 0, 0, 1, RF_SHAREABLE | RF_ACTIVE);
+ if (irq_res == NULL) {
+ device_printf(dev, "failed to allocate irq\n");
+ return (ENXIO);
+ }
+
+
+#ifdef OCTEON_RGMX_SCHEDULED_ISRS
+ /*
+ * Single task queues for all child devices. Since POW gives us a unified
+ * interrupt based on POW groups, not based on PORTs.
+ */
+ TASK_INIT(&rxtx_isr_task, 0, octeon_rgmx_isr_rxtx, NULL);
+ TASK_INIT(&link_isr_task, 0, octeon_rgmx_isr_link, NULL);
+ tq = taskqueue_create_fast("octeon_rgmx_taskq", M_NOWAIT,
+ taskqueue_thread_enqueue, &tq);
+ taskqueue_start_threads(&tq, 1, PI_NET, "%s taskq", device_get_nameunit(dev));
+
+ error = bus_setup_intr(dev, irq_res, INTR_TYPE_NET, octeon_rgmx_intr_fast, NULL,
+ NULL, &int_handler_tag);
+ if (error != 0) {
+ device_printf(dev, "bus_setup_intr returned %d\n", error);
+ taskqueue_free(tq);
+ tq = NULL;
+ return (error);
+ }
+
+#else /* OCTEON_RGMX_SCHEDULED_ISRS */
+
+ error = bus_setup_intr(dev, irq_res, INTR_TYPE_NET, octeon_rgmx_intr, NULL,
+ NULL, &int_handler_tag);
+
+ if (error != 0) {
+ device_printf(dev, "bus_setup_intr returned %d\n", error);
+ tq = NULL;
+ return (error);
+ }
+
+#endif /* OCTEON_RGMX_SCHEDULED_ISRS */
+
+ return (bus_generic_attach(dev));
+}
+
+
+
+
+#define OCTEON_MAX_RGMX_PORT_NUMS 32
+
+
+
+#define OCTEON_POW_RX_GROUP_NUM 0
+#define OCTEON_POW_TX_GROUP_NUM 1 /* If using TX WQE from PKO */
+
+#define OCTEON_POW_RX_GROUP_MASK (1 << OCTEON_POW_RX_GROUP_NUM)
+#define OCTEON_POW_TX_GROUP_MASK (1 << OCTEON_POW_TX_GROUP_NUM)
+#define OCTEON_POW_ALL_OUR_GROUPS_MASK (OCTEON_POW_RX_GROUP_MASK | OCTEON_POW_RX_GROUP_MASK)
+#define OCTEON_POW_ALL_GROUPS_MASK 0xffff
+#define OCTEON_POW_WORKQUEUE_INT (0x8001670000000200ull)
+#define OCTEON_POW_WORKQUEUE_INT_PC (0x8001670000000208ull)
+#define OCTEON_POW_WORKQUEUE_INT_THRESHOLD(group_num) ((0x8001670000000080ull+((group_num)*0x8)))
+#define OCTEON_RGMX_POW_NOS_CNT (0x8001670000000228ull)
+#define OCTEON_POW_INT_CNTR(core) (0x8001670000000100ull+((core)*0x8))
+#define OCTEON_POW_INPT_Q_ALL_QOS (0x8001670000000388ull)
+#define OCTEON_POW_INPT_QOS_GRP(grp) (0x8001670000000340ull + ((grp) * 0x8))
+
+
+
+
+#define NUM_RX_PACKETS_CTL (MAX_RX_BUFS + 3000)
+#define NUM_TX_PACKETS_CTL 40
+
+#define FPA_NOPOOL 0
+
+#define OCTEON_FPA_RX_PACKET_POOL 0
+#define OCTEON_FPA_RX_PACKET_POOL_WORDS 208 /* 2048 bytes */
+#define OCTEON_FPA_RX_PACKET_POOL_ELEM_SIZE (OCTEON_FPA_RX_PACKET_POOL_WORDS)
+#define OCTEON_FPA_RX_PACKET_POOL_ELEMENTS (MAX_RX_BUFS)
+#define OCTEON_RX_MAX_SIZE (OCTEON_FPA_RX_PACKET_POOL_WORDS * sizeof(uint64_t))
+
+#define OCTEON_FPA_WQE_RX_POOL 1
+#define OCTEON_FPA_WQE_RX_WORDS (OCTEON_CACHE_LINE_SIZE/8)
+#define OCTEON_FPA_WQE_RX_POOL_ELEM_SIZE (OCTEON_FPA_WQE_RX_WORDS)
+#define OCTEON_FPA_WQE_RX_POOL_ELEMENTS (NUM_RX_PACKETS_CTL)
+
+#define OCTEON_FPA_TX_PACKET_POOL 2
+#define OCTEON_FPA_TX_PACKET_POOL_WORDS 208 /* 2048 bytes */
+#define OCTEON_FPA_TX_PACKET_POOL_ELEM_SIZE (OCTEON_FPA_TX_PACKET_POOL_WORDS)
+#define OCTEON_FPA_TX_PACKET_POOL_ELEMENTS (MAX_TX_BUFS)
+#define OCTEON_TX_MAX_SIZE (OCTEON_FPA_TX_PACKET_POOL_WORDS * sizeof(uint64_t))
+
+#define OCTEON_FPA_TX_CMDBUF_POOL 3
+#define OCTEON_FPA_TX_CMD_SIZE 2
+#define OCTEON_FPA_TX_CMD_NUM 300
+#define OCTEON_FPA_TX_CMDBUF_POOL_WORDS (OCTEON_FPA_TX_CMD_SIZE * OCTEON_FPA_TX_CMD_NUM)
+#define OCTEON_FPA_TX_CMDBUF_POOL_ELEM_SIZE (OCTEON_FPA_TX_CMDBUF_POOL_WORDS +1)
+#define OCTEON_FPA_TX_CMDBUF_POOL_ELEMENTS (30 * OCTEON_RGMX_NUM_PORTS_MAX)
+
+#define FIRST_PARTICLE_SKIP 0
+#define NOT_FIRST_PARTICLE_SKIP 0
+
+#define ENABLE_BACK_PRESSURE 0
+#define RGMX_MAX_PAK_RECEIVE 5000000
+
+
+#ifdef OCTEON_RGMX_SCHEDULED_ISRS
+
+
+static void octeon_rgmx_isr_link (void *context, int pending)
+{
+ octeon_line_status_loop(NULL);
+}
+
+
+static void octeon_rgmx_isr_rxtx (void *context, int pending)
+{
+ octeon_rx_loop(NULL);
+}
+
+
+/*********************************************************************
+ *
+ * Fast Interrupt Service routine
+ *
+ *********************************************************************/
+
+//#define OCTEON_RGMX_POW_TIME_THR_INTS 1
+
+
+static int octeon_rgmx_intr_fast(void *arg)
+{
+
+ int handled_flag = 0;
+ uint64_t ciu_summary;
+
+ ciu_summary = ciu_get_int_summary(CIU_THIS_CORE, OCTEON_RGMX_CIU_INTX,
+ OCTEON_RGMX_CIU_ENX);
+
+ if (ciu_summary & CIU_GENTIMER_BITS_ENABLE(CIU_GENTIMER_NUM_1)) {
+
+ /*
+ * Timer Interrupt for link status checks
+ * Acknowledging it will mask it for this cycle.
+ */
+ ciu_clear_int_summary(CIU_THIS_CORE, OCTEON_RGMX_CIU_INTX,
+ OCTEON_RGMX_CIU_ENX,
+ CIU_GENTIMER_BITS_ENABLE(CIU_GENTIMER_NUM_1));
+
+ taskqueue_enqueue(taskqueue_fast, &link_isr_task);
+ handled_flag = 1;
+ }
+
+ if (ciu_summary & OCTEON_POW_ALL_GROUPS_MASK) {
+#ifndef OCTEON_RGMX_POW_TIME_THR_INTS
+ /*
+ * When using POW IQ/DSQ size based interrupts, then
+ * ack the interrupts right away. So they don't interrupt
+ * until the queue size goes to 0 again.
+ */
+ oct_write64(OCTEON_POW_WORKQUEUE_INT,
+ 0x10001 << OCTEON_POW_RX_GROUP_NUM);
+
+#else
+ /*
+ * We use POW thresholds based interrupt signalled on timer
+ * countdown. Acknowledge it now so that it doesn't
+ * interrupt us until next countdown to zero.
+ */
+ oct_write64(OCTEON_POW_WORKQUEUE_INT,
+ 0x1 << OCTEON_POW_RX_GROUP_NUM);
+#endif
+
+ taskqueue_enqueue(tq, &rxtx_isr_task);
+ handled_flag = 1;
+ }
+
+ return ((handled_flag) ? FILTER_HANDLED : FILTER_STRAY);
+}
+
+
+#else /* ! OCTEON_RGMX_SCHEDULED_ISRS */
+
+
+/*
+ * octeon_rgmx_intr
+ *
+ * This is direct inline isr. Will do all its work and heavy-lifting in interrupt context.
+ *
+ * Also note that the RGMX_LOCK/UNLOCK code will have to checked/added, since that is new and
+ * was not supported with this model.
+ */
+static int octeon_rgmx_intr (void *arg)
+{
+ int flag = 0;
+ uint64_t ciu_summary;
+
+ /*
+ * read ciu to see if any bits are pow
+ */
+ while (1) {
+ ciu_summary = ciu_get_int_summary(CIU_THIS_CORE, OCTEON_RGMX_CIU_INTX,
+ OCTEON_RGMX_CIU_ENX);
+
+ if ((ciu_summary & (OCTEON_POW_ALL_GROUPS_MASK | CIU_GENTIMER_BITS_ENABLE(CIU_GENTIMER_NUM_1))) == 0) {
+ break;
+ }
+
+ flag = 1;
+
+ if (ciu_summary & OCTEON_POW_ALL_GROUPS_MASK) {
+ octeon_rx_loop(NULL);
+ /*
+ * Acknowledge the interrupt after processing queues.
+ */
+ oct_write64(OCTEON_POW_WORKQUEUE_INT, OCTEON_POW_RX_GROUP_MASK);
+ }
+ if (ciu_summary & CIU_GENTIMER_BITS_ENABLE(CIU_GENTIMER_NUM_1)) {
+ octeon_line_status_loop(NULL);
+ ciu_clear_int_summary(CIU_THIS_CORE, OCTEON_RGMX_CIU_INTX,
+ OCTEON_RGMX_CIU_ENX,
+ CIU_GENTIMER_BITS_ENABLE(CIU_GENTIMER_NUM_1));
+ }
+ }
+
+ return ((flag) ? FILTER_HANDLED : FILTER_STRAY);
+}
+
+
+#endif /* OCTEON_RGMX_SCHEDULED_ISRS */
+
+
+
+static struct mbuf *octeon_rgmx_build_new_rx_mbuf(struct ifnet *ifp, void *data_start, u_int totlen);
+
+static struct mbuf *octeon_rgmx_build_new_rx_mbuf (struct ifnet *ifp, void *data_start, u_int totlen)
+{
+ struct mbuf *m, *m0, *newm;
+ caddr_t newdata;
+ int len;
+
+ if (totlen <= ETHER_HDR_LEN || totlen > LEBLEN - ETHER_CRC_LEN) {
+#ifdef LEDEBUG
+ if_printf(ifp, "invalid packet size %d; dropping\n", totlen);
+#endif
+ return (NULL);
+ }
+
+ MGETHDR(m0, M_DONTWAIT, MT_DATA);
+ if (m0 == NULL) {
+ return (NULL);
+ }
+
+ /* Initialize packet header info. */
+ m0->m_pkthdr.rcvif = ifp;
+ m0->m_pkthdr.len = totlen;
+ m0->m_pkthdr.csum_flags = CSUM_IP_CHECKED | CSUM_IP_VALID | CSUM_DATA_VALID | CSUM_PSEUDO_HDR;
+ m0->m_pkthdr.csum_data = 0xffff;
+ len = MHLEN;
+ m = m0;
+
+ while (totlen > 0) {
+ if (totlen >= MINCLSIZE) {
+ MCLGET(m, M_DONTWAIT);
+ if ((m->m_flags & M_EXT) == 0)
+ goto octeon_rgmx_build_new_rx_mbuf_bad;
+ len = MCLBYTES;
+ }
+
+ if (m == m0) {
+ newdata = (caddr_t)ALIGN(m->m_data + ETHER_HDR_LEN) - ETHER_HDR_LEN;
+ len -= newdata - m->m_data;
+ m->m_data = newdata;
+ }
+
+ /* Set the length of this mbuf. */
+ m->m_len = len = min(totlen, len);
+ bcopy(data_start, mtod(m, caddr_t), len);
+ data_start = (void *) (((u_long) (data_start)) + len);
+
+ totlen -= len;
+ if (totlen > 0) {
+ MGET(newm, M_DONTWAIT, MT_DATA);
+ if (newm == 0)
+ goto octeon_rgmx_build_new_rx_mbuf_bad;
+ len = MLEN;
+ m = m->m_next = newm;
+ }
+ }
+
+ return (m0);
+
+octeon_rgmx_build_new_rx_mbuf_bad:
+
+ m_freem(m0);
+ return (NULL);
+}
+
+
+
+//#define DEBUG_RX 1
+
+static void octeon_rgmx_rx_process_work (octeon_wqe_t *work, u_int port)
+{
+ struct rgmx_softc_dev *sc;
+ struct ifnet *ifp;
+ u_int len;
+ void *data_start, *new_data_start;
+ struct mbuf *mbuf;
+
+//#define DEBUG_RX_PKT_DUMP 1
+#ifdef DEBUG_RX_PKT_DUMP
+ int i; u_char *dc;
+#endif
+
+ data_start = octeon_pow_pktptr_to_kbuffer(work->packet_ptr);
+
+//#define DEBUG_RX2
+#ifdef DEBUG_RX2
+ printf(" WQE 0x%X: port:%u ", work, port);
+ printf(" Grp: %u, %llX Tag: %u %llX type: %u 0x%llx\n",
+ work->grp, work->grp, work->tag, work->tag, work->tag_type, work->tag_type);
+#endif
+
+ if ((port >= OCTEON_RGMX_MIN_PORT) || (port <= OCTEON_RGMX_MAX_PORT)) {
+
+ sc = get_rgmx_softc(port);
+
+ if (!sc || !sc->ifp) {
+
+ printf(" octeon_rgmx_rx_process_work No sc or sc->ifp - port:%u", port);
+ } else {
+
+ ifp = sc->ifp;
+
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
+
+ if (!work->word2.bits.rcv_error) {
+
+ len = work->len;
+
+ /*
+ * We cannot pass the same FPA phys-buffer higher up.
+ * User space will not be able to use this phys-buffer.
+ *
+ * Start building a mbuf packet here using data_start & len.
+ */
+
+ new_data_start = data_start;
+ if (!work->word2.bits.not_IP) {
+ new_data_start = (void *) (((unsigned long) (new_data_start)) + 14);
+ /* mark it as checksum checked */
+ } else {
+ new_data_start = (void *) (((unsigned long) (new_data_start)) + 8);
+ }
+
+#ifdef DEBUG_RX_PKT_DUMP
+ dc = new_data_start; printf("In:\n");
+ for (i = 0; i < len; i++) { if (!(i % 16)) printf ("\n"); printf(" %02X", dc[i]); }
+#endif
+
+ mbuf = octeon_rgmx_build_new_rx_mbuf(ifp, new_data_start, len);
+ if (mbuf) {
+// printf(" Passing pkt to ifp: pkt_len: %u len: %u ", mbuf->m_pkthdr.len, mbuf->m_len);
+#ifdef DEBUG_RX_PKT_DUMP
+
+ dc = mtod(mbuf, u_char *); printf("\n"); printf("In: ");
+ for (i = 0; i < mbuf->m_len; i++) { if (!(i % 16)) printf ("\n"); printf(" %02X", dc[i]); }
+
+#endif
+
+ /* Feed the packet to upper layer. */
+ (*ifp->if_input)(ifp, mbuf);
+ ifp->if_ipackets++;
+
+ } else { /* mbuf error */
+ if_printf(ifp, "mbuf rx construct error\n");
+ printf(" mbuf rx construct error\n");
+ ifp->if_ierrors++;
+ } /* mbuf error */
+
+ } else { /* rcv_error */
+ ifp->if_ierrors++;
+ } /* rcv_error */
+
+ } /* IFF_DRV_RUNNING */
+
+ } /* sc && sc->ifp */
+
+ } else { /* port number */
+ printf(" rgmx_rx:%u bad port\n", port);
+ }
+
+ octeon_fpa_free(data_start, OCTEON_FPA_RX_PACKET_POOL, 0);
+ octeon_fpa_free((void *)work, OCTEON_FPA_WQE_RX_POOL, 0);
+}
+
+
+
+
+/* ------------------------------------------------------------------- *
+ * octeon_rx_loop() *
+ * ------------------------------------------------------------------- */
+
+
+//#define OCTEON_VISUAL_RGMX 1
+#ifdef OCTEON_VISUAL_RGMX
+static int where0 = 0;
+static int where1 = 0;
+#endif
+
+static void octeon_rx_loop (void *unused)
+{
+ u_int core_id;
+ uint64_t prev_grp_mask;
+ u_int pak_count;
+ octeon_wqe_t *work;
+
+ core_id = octeon_get_core_num();
+ pak_count = 0;
+
+ /* Only allow work for our group */
+ prev_grp_mask = oct_read64(OCTEON_POW_CORE_GROUP_MASK(core_id));
+ oct_write64(OCTEON_POW_CORE_GROUP_MASK(core_id), OCTEON_POW_ALL_GROUPS_MASK);
+
+
+#ifdef OCTEON_VISUAL_RGMX
+ octeon_led_run_wheel(&where0, 3);
+#endif
+ while(1) {
+
+ if (pak_count++ > RGMX_MAX_PAK_RECEIVE) {
+ break;
+ }
+
+ work = octeon_pow_work_request_sync(OCTEON_POW_WAIT);
+
+ if (work == NULL) {
+ /*
+ * No more incoming packets. We can take a break now.
+ */
+ break;
+ }
+
+#ifdef OCTEON_VISUAL_RGMX
+ octeon_led_run_wheel(&where1, 4);
+#endif
+ octeon_rgmx_rx_process_work(work, work->ipprt);
+
+ }
+
+ oct_write64(OCTEON_POW_CORE_GROUP_MASK(core_id), prev_grp_mask);
+}
+
+
+static void *octeon_rgmx_write_mbufs_to_fpa_buff (struct rgmx_softc_dev *sc, struct mbuf *m, u_int len)
+{
+ struct mbuf *mp;
+ void *data_area;
+ u_char *write_offset;
+
+ /*
+ * FIXME
+ *
+ * Compare len with max FPA-tx-packet size. Or else we will possibly corrupt the next pkt.
+ */
+
+
+ /*
+ * Get an FPA buffer from Xmit-packets FPA pool
+ */
+ data_area = octeon_fpa_alloc(OCTEON_FPA_TX_PACKET_POOL);
+ if (!data_area) {
+ /*
+ * Fail. No room. No resources.
+ */
+ return (NULL);
+ }
+
+ /*
+ * Transfer the data from mbuf chain to the transmission buffer.
+ */
+ write_offset = data_area;
+ for (mp = m; mp != 0; mp = mp->m_next) {
+ if (mp->m_len) {
+ bcopy(mtod(mp, caddr_t), write_offset, mp->m_len);
+ write_offset = (u_char *) (((u_long) write_offset) + mp->m_len);
+ }
+ }
+ return (data_area);
+}
+
+
+static u_int octeon_rgmx_pko_xmit_packet (struct rgmx_softc_dev *sc, void *out_buff, u_int len, u_int checksum)
+{
+ octeon_pko_command_word0_t pko_cmd;
+ octeon_pko_packet_ptr_t pko_pkt_word;
+ u_long temp;
+ u_short xmit_cmd_index;
+ uint64_t *xmit_cmd_ptr;
+ uint64_t xmit_cmd_state;
+ int queue = 0; // we should randomize queue # based on core num. Using same
+ // queue 0 for this port, by all cores on is less efficient.
+
+ /*
+ * Prepare the PKO buffer and command word.
+ * Cmd Buf Word 0
+ * No FAU
+ * Set #-segs and #-bytes
+ */
+ pko_cmd.word64 = 0;
+ pko_cmd.bits.segs = 1;
+ pko_cmd.bits.total_bytes = len;
+ if (checksum) {
+ pko_cmd.bits.ipoffp1 = ETHER_HDR_LEN + 1; /* IPOffP1 is +1 based. 1 means offset 0 */
+ }
+
+ /*
+ * Build the PKO buffer pointer. PKO Cmd Buf Word 1
+ */
+ pko_pkt_word.word64 = 0;
+ pko_pkt_word.bits.addr = OCTEON_PTR2PHYS(out_buff);
+ pko_pkt_word.bits.pool = OCTEON_FPA_TX_PACKET_POOL;
+ pko_pkt_word.bits.size = 2048; // dummy. Actual len is above.
+
+#ifdef DEBUG_TX
+ printf(" PKO: 0x%llX 0x%llX ", pko_cmd.word64, pko_pkt_word.word64);
+#endif
+
+ /*
+ * Get the queue command ptr location from the per port per queue, pko info struct.
+ */
+ octeon_spinlock_lock(&(sc->outq_ptr[queue].lock));
+#ifdef DEBUG_TX
+ printf(" xmit: sc->outq_ptr[queue].xmit_command_state: 0x%llX ", sc->outq_ptr[queue].xmit_command_state);
+#endif
+ xmit_cmd_state = sc->outq_ptr[queue].xmit_command_state;
+ sc->outq_ptr[queue].xmit_command_state = xmit_cmd_state + 2;
+
+ temp = (u_long) (xmit_cmd_state >> OCTEON_PKO_INDEX_BITS);
+#ifdef DEBUG_TX
+ printf(" temp: 0x%X ", temp);
+#endif
+ xmit_cmd_ptr = (uint64_t *) MIPS_PHYS_TO_KSEG0(temp);
+ xmit_cmd_index = xmit_cmd_state & OCTEON_PKO_INDEX_MASK;
+ xmit_cmd_ptr += xmit_cmd_index;
+
+ /*
+ * We end the PKO cmd buffer at odd boundary. Towards the end we will have
+ * 4 or 3 or 2 or 1 or 0 word remaining. Case of 4, 2, or 0 can never happen.
+ * We only care when we have 3 words remaining. In this case we write our 2 words
+ * for PKO command and 3rd word as chain for next PKO cmd buffer.
+ */
+ xmit_cmd_ptr[0] = pko_cmd.word64;
+
+ if (xmit_cmd_index < (OCTEON_FPA_TX_CMDBUF_POOL_WORDS - 2)) {
+ /*
+ * Plenty of space left. Write our 2nd word and worry the next time.
+ */
+ xmit_cmd_ptr[1] = pko_pkt_word.word64;
+
+ } else {
+ /*
+ * 3 words or less are left. We write our 2nd word now and then put in a chain link
+ * to new PKO cmd buf.
+ */
+ uint64_t phys_cmd_buf = octeon_fpa_alloc_phys(OCTEON_FPA_TX_CMDBUF_POOL);
+
+ if (!phys_cmd_buf) {
+ /*
+ * FPA pool for xmit-buffer-commands is empty.
+ */
+ sc->outq_ptr[queue].xmit_command_state -= 2;
+ octeon_spinlock_unlock(&(sc->outq_ptr[queue].lock));
+ return (0);
+ }
+
+ xmit_cmd_ptr[1] = pko_pkt_word.word64;
+ xmit_cmd_ptr[2] = phys_cmd_buf;
+
+ sc->outq_ptr[queue].xmit_command_state = (phys_cmd_buf << OCTEON_PKO_INDEX_BITS);
+ }
+ /*
+ * Unlock queue structures.
+ */
+ octeon_spinlock_unlock(&(sc->outq_ptr[queue].lock));
+
+ /*
+ * 2 words incremented in PKO. Ring the doorbell.
+ */
+#ifdef DEBUG_TX
+ printf(" Ringing doorbell: Port %u Queue %u words 2", sc->port, octeon_pko_get_base_queue(sc->port) + queue);
+#endif
+ octeon_pko_ring_doorbell(sc->port, octeon_pko_get_base_queue(sc->port) + queue, 2);
+
+ return (1);
+}
+
+
+static void octeon_rgmx_xmit_mark_buffers_done(struct rgmx_softc_dev *sc, u_int n);
+
+static void octeon_rgmx_xmit_mark_buffers_done (struct rgmx_softc_dev *sc, u_int n)
+{
+ struct mbuf *m;
+ u_int i;
+
+ for (i = 0; i < n; i++) {
+ /*
+ * Remove packets in queue. Leaving a lag of 3, to allow for PKO in-flight xmission
+ */
+ if (_IF_QLEN(&sc->tx_pending_queue) > 4) {
+ IF_DEQUEUE(&sc->tx_pending_queue, m);
+ if (!m) {
+ break; // Queue became empty now. Break out.
+ }
+ /*
+ * Return the mbuf to system.
+ */
+ m_freem(m);
+ }
+ }
+ if (!i) {
+ return; // Nothing removed from queue.
+ }
+
+ /*
+ * The transmitter is no more active.
+ * Reset output active flag and watchdog timer.
+ */
+ sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+}
+
+
+#define OCTEON_RGMX_FLUSH_N_XMIT_MBUFS_EACH_LOOP 5
+#define OCTEON_RGMX_FLUSH_PENDING_MBUFS_MAX 1000
+
+#ifdef __not_used__
+/*
+ * octeon_rgmx_output_flush
+ *
+ * Drop all packets queued at ifnet layer.
+ */
+static void octeon_rgmx_output_flush (struct ifnet *ifp)
+{
+ struct mbuf *m;
+ u_int max_flush = OCTEON_RGMX_FLUSH_PENDING_MBUFS_MAX; /* Arbitrarily high number */
+
+ while (max_flush-- && _IF_QLEN(&ifp->if_snd)) {
+ /*
+ * Get the next mbuf Packet chain to flush.
+ */
+ IF_DEQUEUE(&ifp->if_snd, m);
+ if (m == NULL) {
+ /* No more packets to flush */
+ break;
+ }
+ _IF_DROP(&ifp->if_snd);
+ m_freem(m);
+ ifp->if_oerrors++;
+ }
+}
+#endif
+
+/*
+ * octeon_rgmx_output_start
+ *
+ * Start output on interface.
+ */
+static void octeon_rgmx_output_start (struct ifnet *ifp)
+{
+ struct rgmx_softc_dev *sc = ifp->if_softc;
+
+ RGMX_LOCK(sc);
+ octeon_rgmx_output_start_locked(ifp);
+ RGMX_UNLOCK(sc);
+}
+
+
+
+/*
+ * octeon_rgmx_output_start_locked
+ *
+ * Start output on interface. Assume Driver locked
+ */
+static void octeon_rgmx_output_start_locked (struct ifnet *ifp)
+{
+ struct rgmx_softc_dev *sc = ifp->if_softc;
+ struct mbuf *m;
+ u_int len, need_l4_checksum;
+ void *out_buff;
+
+ /*
+ * Take out some of the last queued mbuf's from xmit-pending queue
+ */
+ octeon_rgmx_xmit_mark_buffers_done(sc, OCTEON_RGMX_FLUSH_N_XMIT_MBUFS_EACH_LOOP);
+
+ while (1) {
+ /*
+ * See if there is room to put another packet in the buffer.
+ * We *could* do better job by peeking the send queue to
+ * know the length of the next packet. Current version just
+ * tests against the worst case (i.e., longest packet). FIXME.
+ *
+ * When adding the packet-peek feature, don't forget adding a
+ * test on txb_count against QUEUEING_MAX.
+ * There is a little chance the packet count exceeds
+ * the limit. Assume transmission buffer is 8KB (2x8KB
+ * configuration) and an application sends a bunch of small
+ * (i.e., minimum packet sized) packets rapidly. An 8KB
+ * buffer can hold 130 blocks of 62 bytes long...
+ */
+
+ /*
+ * If unable to send more.
+ */
+ if (_IF_QLEN(&sc->tx_pending_queue) >= MAX_TX_BUFS) {
+ printf(" Xmit not possible. NO room %u", _IF_QLEN(&sc->tx_pending_queue));
+ goto indicate_active;
+ }
+
+
+ /*
+ * Get the next mbuf chain for a packet to send.
+ */
+ IF_DEQUEUE(&ifp->if_snd, m);
+ if (m == NULL) {
+ /* No more packets to send. */
+ goto indicate_inactive;
+ }
+
+ len = m->m_pkthdr.len;
+ /*
+ * Should never send big packets. If such a packet is passed,
+ * it should be a bug of upper layer. We just ignore it.
+ * ... Partial (too short) packets, neither.
+ */
+ if (len < ETHER_HDR_LEN ||
+ len > ETHER_MAX_LEN - ETHER_CRC_LEN) {
+ /*
+ * Fail. Bad packet size. Return the mbuf to system.
+ */
+ if_printf(ifp,
+ "got an out-of-spec packet (%u bytes) to send\n", len);
+ m_freem(m);
+ goto indicate_active;
+ }
+
+ /*
+ * Copy the mbuf chain into the transmission buffer.
+ * txb_* variables are updated as necessary.
+ */
+ out_buff = octeon_rgmx_write_mbufs_to_fpa_buff(sc, m, len);
+ if (!out_buff) {
+ /*
+ * No FPA physical buf resource.
+ * Let's requeue it back. And slow it down for a while.
+ */
+ IF_PREPEND(&ifp->if_snd, m);
+ goto indicate_active;
+ }
+
+ need_l4_checksum = (m->m_pkthdr.csum_flags & (CSUM_TCP | CSUM_UDP)) ? 1 : 0;
+
+ /*
+ * put the mbuf onto pending queue
+ */
+//#define DEBUG_TX_PKT_DUMP 1
+#ifdef DEBUG_TX_PKT_DUMP
+ int ii;
+ u_char *dc = out_buff;
+
+ printf("\n"); printf("Out: ");
+ for (ii = 0; ii < len; ii++) printf(" %X", dc[ii]); printf("\n");
+#endif
+
+ ETHER_BPF_MTAP(ifp, m);
+
+ IF_ENQUEUE(&sc->tx_pending_queue, m);
+
+ /*
+ * Pass the mbuf data packet to PKO for xmission.
+ */
+ octeon_rgmx_pko_xmit_packet(sc, out_buff, len, need_l4_checksum);
+
+ ifp->if_opackets++;
+ }
+
+indicate_inactive:
+ /*
+ * We are using the !OACTIVE flag to indicate to
+ * the outside world that we can accept an
+ * additional packet rather than that the
+ * transmitter is _actually_ active. Indeed, the
+ * transmitter may be active, but if we haven't
+ * filled all the buffers with data then we still
+ * want to accept more.
+ */
+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+ return;
+
+
+indicate_active:
+ /*
+ * The transmitter is active, and there are no room for
+ * more outgoing packets in the transmission buffer.
+ */
+ ifp->if_oerrors++;
+// sc->mibdata.dot3StatsInternalMacTransmitErrors++;
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ return;
+}
+
+
+
+
+/* ------------------------------------------------------------------- *
+ * octeon_config_hw_units() *
+ * ------------------------------------------------------------------- *
+ *
+ * Initialize Octeon hardware components. To get the RGMX going.
+ *
+ */
+static void octeon_config_hw_units_pre_ports (void)
+{
+
+ /* Enable FPA */
+ octeon_enable_fpa();
+
+ /* Enable PKO */
+ octeon_pko_enable();
+
+ /* Init PKO */
+ octeon_pko_init();
+
+
+ /* Fill FPA */
+
+ /*
+ * Input Buffers Pool
+ * Pool 0
+ */
+ octeon_fpa_fill_pool_mem(OCTEON_FPA_RX_PACKET_POOL, OCTEON_FPA_RX_PACKET_POOL_ELEM_SIZE,
+ OCTEON_FPA_RX_PACKET_POOL_ELEMENTS);
+
+ /*
+ * WQE Blocks Pool
+ * Pool 1
+ */
+ octeon_fpa_fill_pool_mem(OCTEON_FPA_WQE_RX_POOL, OCTEON_FPA_WQE_RX_POOL_ELEM_SIZE,
+ OCTEON_FPA_WQE_RX_POOL_ELEMENTS);
+
+ /*
+ * PKO Command Pool
+ * Pool 3
+ */
+ octeon_fpa_fill_pool_mem(OCTEON_FPA_TX_CMDBUF_POOL, OCTEON_FPA_TX_CMDBUF_POOL_ELEM_SIZE,
+ OCTEON_FPA_TX_CMDBUF_POOL_ELEMENTS);
+
+ /*
+ * Output Buffers Pool
+ * Pool 2
+ */
+ octeon_fpa_fill_pool_mem(OCTEON_FPA_TX_PACKET_POOL, OCTEON_FPA_TX_PACKET_POOL_ELEM_SIZE,
+ OCTEON_FPA_TX_PACKET_POOL_ELEMENTS);
+
+
+
+ octeon_rgmx_enable_RED_all(OCTEON_FPA_RX_PACKET_POOL_ELEMENTS >> 2, OCTEON_FPA_RX_PACKET_POOL_ELEMENTS >> 3);
+
+ /* Configure IPD */
+ octeon_ipd_config(OCTEON_FPA_RX_PACKET_POOL_WORDS,
+ FIRST_PARTICLE_SKIP / 8,
+ NOT_FIRST_PARTICLE_SKIP / 8,
+ FIRST_PARTICLE_SKIP / 128,
+ NOT_FIRST_PARTICLE_SKIP / 128,
+ OCTEON_FPA_WQE_RX_POOL,
+ OCTEON_IPD_OPC_MODE_STF,
+ ENABLE_BACK_PRESSURE);
+
+ /*
+ * PKO setup Output Command Buffers
+ */
+ octeon_pko_config_cmdbuf_global_defaults(OCTEON_FPA_TX_CMDBUF_POOL,
+ OCTEON_FPA_TX_CMDBUF_POOL_ELEM_SIZE);
+
+}
+
+
+
+static void octeon_config_hw_units_port (struct rgmx_softc_dev *sc, u_int port)
+{
+ const u_int priorities[8] = {8,8,8,8,8,8,8,8};
+ u_int total_queues, base_queue;
+
+ octeon_config_rgmii_port(port);
+
+ total_queues = octeon_pko_get_num_queues(port);
+ base_queue = octeon_pko_get_base_queue(port);
+ /* Packet output configures Queue and Ports */
+ octeon_pko_config_port(port, base_queue,
+ total_queues,
+ priorities,
+ OCTEON_FPA_TX_CMDBUF_POOL,
+ sc->outq_ptr);
+
+ octeon_rgmx_set_mac(port);
+
+ /* Setup Port input tagging */
+ octeon_rgmx_config_pip(port);
+}
+
+
+typedef union
+{
+ uint64_t word64;
+ struct
+ {
+ uint64_t rsvd3 : 35;
+ uint64_t enable : 1;
+ uint64_t time_thr : 4;
+ uint64_t rsvd2 : 1;
+ uint64_t ds_thr : 11;
+ uint64_t rsvd : 1;
+ uint64_t iq_thr : 11;
+ } bits;
+} octeon_rgmx_pow_int_threshold_t;
+
+typedef union
+{
+ uint64_t word64;
+ struct
+ {
+ uint64_t rsvd : 36;
+ uint64_t tc_cnt : 4;
+ uint64_t ds_cnt : 12;
+ uint64_t iq_cnt : 12;
+ } bits;
+} octeon_rgmx_pow_int_cnt_t;
+
+typedef union
+{
+ uint64_t word64;
+ struct
+ {
+ uint64_t rsvd3 : 4;
+ uint64_t thr_freq : 28; // R/O
+ uint64_t rsvd2 : 4;
+ uint64_t thr_period : 20;
+ uint64_t rsvd : 8;
+ } bits;
+} octeon_rgmx_pow_int_pc_t;
+
+
+typedef union
+{
+ uint64_t word64;
+ struct
+ {
+ uint64_t rsvd : 52;
+ uint64_t nos_cnt : 12;
+ } bits;
+} octeon_rgmx_pow_nos_cnt;
+
+
+
+typedef union
+{
+ uint64_t word64;
+ struct
+ {
+ uint64_t rsvd : 32;
+ uint64_t inb_pkts : 32;
+ } bits;
+} octeon_rgmx_pip_inb_pkts;
+
+typedef union
+{
+ uint64_t word64;
+ struct
+ {
+ uint64_t rsvd : 48;
+ uint64_t inb_errs : 16;
+ } bits;
+} octeon_rgmx_pip_inb_errs;
+
+
+
+typedef union
+{
+ uint64_t word64;
+ struct
+ {
+ uint64_t rsvd : 32;
+ uint64_t iq_cnt : 32;
+ } bits;
+} octeon_pow_inpt_q_all_qos;
+
+
+
+typedef union
+{
+ uint64_t word64;
+ struct
+ {
+ uint64_t rsvd : 32;
+ uint64_t iq_cnt : 32;
+ } bits;
+} octeon_pow_inpt_q_grp_qos;
+
+
+static void octeon_config_hw_units_post_ports (void)
+{
+
+ octeon_rgmx_pow_int_threshold_t thr;
+ octeon_rgmx_pow_int_pc_t intpc;
+
+ thr.word64 = 0;
+ intpc.word64 = 0;
+ intpc.bits.thr_freq = (500 * 1000 * 1000) / (1000 * 16 * 256);
+
+#ifdef OCTEON_RGMX_POW_TIME_THR_INTS
+ thr.bits.enable = 1;
+ thr.bits.time_thr = 0xf;
+ oct_write64(OCTEON_POW_WORKQUEUE_INT_THRESHOLD(OCTEON_POW_RX_GROUP_NUM), thr.word64);
+
+ oct_write64(OCTEON_POW_WORKQUEUE_INT_PC, intpc.word64);
+
+#else
+ thr.bits.ds_thr = thr.bits.iq_thr = 1; // Only if doing absolute queue-cnt interrupts.
+ oct_write64(OCTEON_POW_WORKQUEUE_INT_THRESHOLD(OCTEON_POW_RX_GROUP_NUM), thr.word64);
+#endif
+
+ ciu_enable_interrupts(PCPU_GET(cpuid), OCTEON_RGMX_CIU_INTX, OCTEON_RGMX_CIU_ENX,
+ (OCTEON_POW_RX_GROUP_MASK |
+ CIU_GENTIMER_BITS_ENABLE(CIU_GENTIMER_NUM_1)), CIU_MIPS_IP2);
+
+ ciu_clear_int_summary(CIU_THIS_CORE, OCTEON_RGMX_CIU_INTX,
+ OCTEON_RGMX_CIU_ENX, CIU_GENTIMER_BITS_ENABLE(CIU_GENTIMER_NUM_1));
+
+ octeon_ciu_start_gtimer(CIU_GENTIMER_NUM_1, OCTEON_GENTIMER_PERIODIC,
+ OCTEON_GENTIMER_LEN_1SEC);
+ /*
+ * Enable IPD
+ */
+ octeon_ipd_enable();
+}
+
+
+
+
+
+static void octeon_rgmx_config_pip (u_int port)
+{
+ octeon_pip_gbl_cfg_t pip_config;
+ octeon_pip_port_cfg_t pip_port_config;
+ octeon_pip_port_tag_cfg_t pip_tag_config;
+
+ /*
+ * PIP Global config
+ */
+ pip_config.word64 = 0;
+ pip_config.bits.max_l2 = 1;
+ oct_write64(OCTEON_PIP_GBL_CFG, pip_config.word64);
+
+ /*
+ * PIP Port config
+ */
+ pip_port_config.word64 = 0;
+ pip_port_config.bits.mode = OCTEON_PIP_PORT_CFG_MODE_SKIPL2;
+ pip_port_config.bits.qos = port & 0x7;
+ pip_port_config.bits.crc_en = 1;
+
+
+ /*
+ * PIP -> POW tags config
+ *
+ * We don't use any pkt input fields for tag hash, except for Port#
+ */
+ pip_tag_config.word64 = 0;
+
+ pip_tag_config.bits.grptag = 0;
+ pip_tag_config.bits.grptagmask = 0xf;
+ pip_tag_config.bits.grptagbase = 1;
+
+ pip_tag_config.bits.ip6_src_flag = 0;
+ pip_tag_config.bits.ip6_dst_flag = 0;
+ pip_tag_config.bits.ip6_sprt_flag = 0;
+ pip_tag_config.bits.ip6_dprt_flag = 0;
+ pip_tag_config.bits.ip6_nxth_flag = 0;
+
+ pip_tag_config.bits.ip4_src_flag = 1;
+ pip_tag_config.bits.ip4_dst_flag = 1;
+ pip_tag_config.bits.ip4_sprt_flag = 1;
+ pip_tag_config.bits.ip4_dprt_flag = 1;
+ pip_tag_config.bits.ip4_pctl_flag = 1;
+
+ pip_tag_config.bits.tcp6_tag_type = 0;
+ pip_tag_config.bits.tcp4_tag_type = 0;
+ pip_tag_config.bits.ip6_tag_type = 0;
+ pip_tag_config.bits.ip4_tag_type = 0;
+ pip_tag_config.bits.inc_prt_flag = 1;
+ pip_tag_config.bits.non_tag_type = OCTEON_POW_TAG_TYPE_NULL;
+ pip_tag_config.bits.grp = OCTEON_POW_RX_GROUP_NUM;
+
+ octeon_pip_config_port(port, pip_port_config, pip_tag_config);
+
+ oct_write64(OCTEON_POW_CORE_GROUP_MASK(OUR_CORE), OCTEON_POW_ALL_GROUPS_MASK);
+
+}
+
+
+/*
+ * octeon_rgmx_stop_port
+ *
+ */
+static u_int octeon_rgmx_stop_port (u_int port)
+{
+ int interface = INTERFACE(port);
+ int index = INDEX(port);
+ octeon_rgmx_prtx_cfg_t gmx_cfg;
+ u_int last_enabled = 0;
+
+ gmx_cfg.word64 = oct_read64(OCTEON_RGMX_PRTX_CFG(index, interface));
+ last_enabled = (gmx_cfg.bits.en == 1);
+ gmx_cfg.bits.en = 0;
+ oct_write64(OCTEON_RGMX_PRTX_CFG(index, interface), gmx_cfg.word64);
+ return (last_enabled);
+}
+
+static void octeon_rgmx_start_port(u_int port)
+{
+ int interface = INTERFACE(port);
+ int index = INDEX(port);
+ octeon_rgmx_prtx_cfg_t gmx_cfg;
+
+ gmx_cfg.word64 = oct_read64(OCTEON_RGMX_PRTX_CFG(index, interface));
+ gmx_cfg.bits.en = 1;
+ oct_write64(OCTEON_RGMX_PRTX_CFG(index, interface), gmx_cfg.word64);
+}
+
+
+static void octeon_rgmx_stop (struct rgmx_softc_dev *sc)
+{
+ octeon_rgmx_stop_port(sc->port);
+
+ /* Reset transmitter variables and interface flags. */
+ sc->ifp->if_drv_flags &= ~(IFF_DRV_OACTIVE | IFF_DRV_RUNNING);
+ sc->txb_count = 0;
+ sc->txb_sched = 0;
+}
+
+
+/* Change the media selection. */
+static int octeon_rgmx_medchange (struct ifnet *ifp)
+{
+ struct rgmx_softc_dev *sc = ifp->if_softc;
+
+#ifdef DIAGNOSTIC
+ /* If_media should not pass any request for a media which this
+ interface doesn't support. */
+ int b;
+
+ for (b = 0; bit2media[b] != 0; b++) {
+ if (bit2media[b] == sc->media.ifm_media) break;
+ }
+ if (((1 << b) & sc->mbitmap) == 0) {
+ if_printf(sc->ifp,
+ "got an unsupported media request (0x%x)\n",
+ sc->media.ifm_media);
+ return EINVAL;
+ }
+#endif
+
+ /* We don't actually change media when the interface is down.
+ fe_init() will do the job, instead. Should we also wait
+ until the transmission buffer being empty? Changing the
+ media when we are sending a frame will cause two garbages
+ on wires, one on old media and another on new. FIXME */
+ if (sc->ifp->if_flags & IFF_UP) {
+ printf(" Media change requested while IF is up\n");
+ } else {
+ printf(" Media change requested while IF is Down\n");
+ }
+
+ return 0;
+}
+
+
+static void octeon_rgmx_medstat (struct ifnet *ifp, struct ifmediareq *ifm)
+{
+ struct rgmx_softc_dev *sc = ifp->if_softc;
+ octeon_rgmx_rxx_rx_inbnd_t link_status;
+
+ octeon_rgmx_config_speed(sc->port, 1);
+
+ RGMX_LOCK(sc);
+
+ ifm->ifm_status = IFM_AVALID;
+ ifm->ifm_active = IFM_ETHER;
+
+ /*
+ * Parse link status.
+ */
+ link_status.word64 = sc->link_status;
+
+ if (!link_status.bits.status) {
+ RGMX_UNLOCK(sc);
+ return;
+ }
+
+ ifm->ifm_status |= IFM_ACTIVE;
+
+ switch (link_status.bits.speed) {
+ case 0:
+ ifm->ifm_active |= IFM_10_T;
+ break;
+ case 1:
+ ifm->ifm_active |= IFM_100_TX;
+ break;
+ case 2:
+ ifm->ifm_active |= IFM_1000_T;;
+ break;
+ default:
+ /* Unknown! */
+ break;
+ }
+
+ /*
+ * Check duplex.
+ */
+ if (link_status.bits.duplex == 1)
+ ifm->ifm_active |= IFM_FDX;
+ else
+ ifm->ifm_active |= IFM_HDX;
+
+ RGMX_UNLOCK(sc);
+}
+
+static void octeon_rgmx_config_cam(struct ifnet *ifp)
+{
+ struct rgmx_softc_dev *sc = ifp->if_softc;
+ u_int port = sc->port;
+ int index = INDEX(port);
+ int iface = INTERFACE(port);
+ u_int last_enabled;
+ uint64_t adr_ctl;
+
+ last_enabled = octeon_rgmx_stop_port(port);
+
+ adr_ctl = oct_read64(OCTEON_RGMX_RXX_ADR_CTL(index, iface));
+
+ /*
+ * Always accept broadcast traffic.
+ */
+ if ((adr_ctl & OCTEON_RGMX_ADRCTL_ACCEPT_BROADCAST) == 0)
+ adr_ctl |= OCTEON_RGMX_ADRCTL_ACCEPT_BROADCAST;
+
+ /*
+ * Accept all multicast in all multicast mode and in
+ * promiscuous mode.
+ *
+ * XXX Since we don't handle programming the CAM for
+ * multicast filtering, always accept all multicast.
+ */
+ adr_ctl &= ~OCTEON_RGMX_ADRCTL_REJECT_ALL_MULTICAST;
+ adr_ctl |= OCTEON_RGMX_ADRCTL_ACCEPT_ALL_MULTICAST;
+
+ /*
+ * In promiscuous mode, the CAM is shut off, so reject everything.
+ * Otherwise, filter using the CAM.
+ */
+ if ((ifp->if_flags & IFF_PROMISC) != 0) {
+ adr_ctl &= ~OCTEON_RGMX_ADRCTL_CAM_MODE_ACCEPT_DMAC;
+ adr_ctl |= OCTEON_RGMX_ADRCTL_CAM_MODE_REJECT_DMAC;
+ } else {
+ adr_ctl &= ~OCTEON_RGMX_ADRCTL_CAM_MODE_REJECT_DMAC;
+ adr_ctl |= OCTEON_RGMX_ADRCTL_CAM_MODE_ACCEPT_DMAC;
+ }
+
+ oct_write64(OCTEON_RGMX_RXX_ADR_CTL(index, iface), adr_ctl);
+
+ /*
+ * If in promiscuous mode, disable the CAM.
+ */
+ if ((ifp->if_flags & IFF_PROMISC) != 0)
+ oct_write64(OCTEON_RGMX_RXX_ADR_CAM_EN(index, iface), 0);
+ else
+ oct_write64(OCTEON_RGMX_RXX_ADR_CAM_EN(index, iface), 1);
+
+ if (last_enabled) octeon_rgmx_start_port(port);
+}
+
+static int octeon_rgmx_ioctl (struct ifnet * ifp, u_long command, caddr_t data)
+{
+ struct rgmx_softc_dev *sc = ifp->if_softc;
+ struct ifreq *ifr = (struct ifreq *)data;
+ int error = 0;
+
+ if (!sc) {
+ printf(" octeon_rgmx_ioctl. No sc\n");
+ return (0);
+ }
+ switch (command) {
+
+ case SIOCSIFFLAGS:
+ /*
+ * Switch interface state between "running" and
+ * "stopped", reflecting the UP flag.
+ */
+ if (ifp->if_flags & IFF_UP) {
+ /*
+ * New state is IFF_UP
+ * Restart or Start now, if driver is not running currently.
+ */
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
+ octeon_rgmx_init(sc);
+ }
+ octeon_rgmx_config_cam(ifp);
+ } else {
+ /*
+ * New state is IFF_DOWN.
+ * Stop & shut it down now, if driver is running currently.
+ */
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) {
+ octeon_rgmx_stop(sc);
+ }
+ }
+ break;
+
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ break;
+
+ case SIOCSIFMEDIA:
+ case SIOCGIFMEDIA:
+ /* Let if_media to handle these commands and to call
+ us back. */
+ error = ifmedia_ioctl(ifp, ifr, &sc->media, command);
+ break;
+
+ case SIOCSIFCAP:
+ {
+ int mask;
+
+ ifp->if_hwassist &= ~CSUM_TSO;
+ ifp->if_capenable &= ~IFCAP_VLAN_HWTAGGING;
+ mask = ifr->ifr_reqcap ^ ifp->if_capenable;
+ if (mask & IFCAP_HWCSUM) {
+ ifp->if_capenable ^= IFCAP_HWCSUM;
+ if (ifp->if_capenable & IFCAP_TXCSUM) {
+ ifp->if_hwassist |= (CSUM_TCP | CSUM_UDP);
+ } else {
+ ifp->if_hwassist &= ~(CSUM_TCP | CSUM_UDP);
+ }
+ }
+ }
+ break;
+
+ default:
+ error = ether_ioctl(ifp, command, data);
+ break;
+ }
+
+ return (error);
+}
+
+static void octeon_rgmx_init (void *xsc)
+{
+ struct rgmx_softc_dev *sc = xsc;
+
+ /* Enable interrupts. */
+ /* For RGMX they are already enabled earlier */
+
+ /* Enable transmitter and receiver. */
+ /* For RGMX they are already enabled earlier */
+
+ /* Flush out all HW receive buffers for this interface. */
+ /* For RGMX, no means to flush an individual port */
+
+ /* Set 'running' flag, because we are now running. */
+ sc->ifp->if_drv_flags |= IFF_DRV_RUNNING;
+
+ /* Set the HW Address filter. aka program Mac-addr & Multicast filters */
+ /* For RGMX this was taken care of via set_mac_addr() */
+
+ /* Kick start the output */
+ /* Hopefully PKO is running and will pick up packets via the timer or receive loop */
+
+ /* Set link status. */
+ octeon_rgmx_config_speed(sc->port, 1);
+}
+
+
+
+static void octeon_rgmx_config_speed (u_int port, u_int report_link)
+{
+ int index = INDEX(port);
+ int iface = INTERFACE(port);
+ struct rgmx_softc_dev *sc;
+ octeon_rgmx_rxx_rx_inbnd_t link_status, old_link_status;
+ octeon_rgmx_prtx_cfg_t gmx_cfg;
+ uint64_t val64_tx_clk, val64_tx_slot, val64_tx_burst;
+ u_int last_enabled;
+
+ sc = get_rgmx_softc(port);
+ if (!sc) {
+ printf(" config_speed didn't find sc int:%u port:%u", iface, port);
+ return;
+ }
+
+ /*
+ * Look up interface-port speed params
+ */
+ link_status.word64 = oct_read64(OCTEON_RGMX_RXX_RX_INBND(index, iface));
+
+ RGMX_LOCK(sc);
+
+ /*
+ * Compre to prev known state. If same then nothing to do.
+ */
+ if (link_status.word64 == sc->link_status) {
+ RGMX_UNLOCK(sc);
+ return;
+ }
+ old_link_status.word64 = sc->link_status;
+
+ /*
+ * Compare to previous state modulo link status. If only link
+ * status is different, we don't need to change media.
+ */
+ if (old_link_status.bits.duplex != link_status.bits.duplex ||
+ old_link_status.bits.speed != link_status.bits.speed) {
+ last_enabled = octeon_rgmx_stop_port(port);
+
+ gmx_cfg.word64 = oct_read64(OCTEON_RGMX_PRTX_CFG(index, iface));
+
+ /*
+ * Duplex
+ * XXX Set based on link_status.bits.duplex?
+ */
+ gmx_cfg.bits.duplex = 1;
+
+ switch (link_status.bits.speed) {
+ case 0: /* 10Mbps */
+ gmx_cfg.bits.speed = 0;
+ gmx_cfg.bits.slottime = 0;
+ val64_tx_clk = 50; val64_tx_slot = 0x40; val64_tx_burst = 0;
+ break;
+
+ case 1: /* 100Mbps */
+ gmx_cfg.bits.speed = 0;
+ gmx_cfg.bits.slottime = 0;
+ val64_tx_clk = 5; val64_tx_slot = 0x40; val64_tx_burst = 0;
+ break;
+
+ case 2: /* 1Gbps */
+ gmx_cfg.bits.speed = 1;
+ gmx_cfg.bits.slottime = 1;
+ val64_tx_clk = 1; val64_tx_slot = 0x200; val64_tx_burst = 0x2000;
+ break;
+
+ case 3: /* ?? */
+ default:
+ gmx_cfg.bits.speed = 1;
+ gmx_cfg.bits.slottime = 1;
+ val64_tx_clk = 1; val64_tx_slot = 0x200; val64_tx_burst = 0x2000;
+ break;
+ }
+
+ oct_write64(OCTEON_RGMX_TXX_CLK(index, iface), val64_tx_clk);
+ oct_write64(OCTEON_RGMX_TXX_SLOT(index, iface), val64_tx_slot);
+ oct_write64(OCTEON_RGMX_TXX_BURST(index, iface), val64_tx_burst);
+
+ oct_write64(OCTEON_RGMX_PRTX_CFG(index, iface), gmx_cfg.word64);
+
+ if (last_enabled) octeon_rgmx_start_port(port);
+ }
+
+ /*
+ * Now check and possibly change link status.
+ */
+ if (link_status.bits.status != old_link_status.bits.status) {
+ if (report_link) {
+ if (link_status.bits.status) {
+ if_link_state_change(sc->ifp, LINK_STATE_UP);
+ } else {
+ if_link_state_change(sc->ifp, LINK_STATE_DOWN);
+ }
+ }
+ }
+
+ if (report_link) {
+ sc->link_status = link_status.word64;
+ } else {
+ /*
+ * We can't update link status proper since we can't
+ * change it in the interface, so keep the old link
+ * status intact but note the current speed and duplex
+ * settings.
+ */
+ link_status.bits.status = old_link_status.bits.status;
+ sc->link_status = link_status.word64;
+ }
+
+ RGMX_UNLOCK(sc);
+}
+
+
+
+#ifdef DEBUG_RGMX_DUMP
+static void octeon_dump_rgmx_stats (u_int port)
+{
+
+}
+#endif
+
+#ifdef __not_used__
+static void rgmx_timer_periodic (void)
+{
+ u_int port;
+ int index;
+ struct rgmx_softc_dev *sc;
+ struct ifnet *ifp;
+
+ for (index = 0; index < OCTEON_RGMX_NUM_PORTS_MAX; index ++) {
+
+ port = port_array[index];
+ sc = rgmx_scdev_array[index];
+
+ /*
+ * Skip over ports/slots not in service.
+ */
+ if ((port < OCTEON_RGMX_MIN_PORT) || (port > OCTEON_RGMX_MAX_PORT)) {
+ continue;
+ }
+ if ((NULL == sc) || (((struct rgmx_softc_dev *)-1) == sc)) {
+ continue;
+ }
+
+ /*
+ * Now look for anamolous conditions
+ */
+ if (sc != get_rgmx_softc(port)) {
+ printf(" port %u sc %p not in sync with index: %u\n",
+ port, sc, index);
+ continue;
+ }
+
+ if (sc->port != port) {
+ printf(" port %u sc %p port-> %u not in sync with index: %u\n",
+ port, sc, sc->port, index);
+ continue;
+ }
+
+ ifp = sc->ifp;
+ if (ifp == NULL) {
+ printf(" port %u sc %p . Bad ifp %p\n", port, sc, ifp);
+ continue;
+ }
+
+ /*
+ * Check if packets queued at ifnet layer. Kick start output if we can.
+ */
+ if (sc->ifp->if_flags & IFF_UP) {
+ octeon_rgmx_output_start(ifp);
+ } else {
+ octeon_rgmx_output_flush(ifp);
+ }
+
+ /*
+ * Check if line status changed ? Adjust ourselves.
+ */
+ octeon_rgmx_config_speed(port, 1);
+ }
+}
+#endif
+
+#ifdef DEBUG_RGMX_DUMP
+static void octeon_dump_pow_stats(void)
+{
+ octeon_rgmx_pow_nos_cnt nos_cnt;
+ octeon_rgmx_pow_int_pc_t intpc;
+ octeon_rgmx_pow_int_threshold_t thr;
+ octeon_rgmx_pow_int_cnt_t int_cnt;
+ int core = octeon_get_core_num();
+ octeon_pow_inpt_q_all_qos inpt_q_all;
+ octeon_pow_inpt_q_grp_qos inpt_q_grp;
+ octeon_rgmx_pip_inb_pkts pkts;
+ octeon_rgmx_pip_inb_errs errs;
+ static u_int pkts0 = 0;
+ static u_int pkts1 = 0;
+ static u_int errs0 = 0;
+ static u_int errs1 = 0;
+ int i;
+
+
+ nos_cnt.word64 = oct_read64(OCTEON_RGMX_POW_NOS_CNT);
+ if (nos_cnt.bits.nos_cnt) printf(" *** No sched cnt %u\n", nos_cnt.bits.nos_cnt);
+ printf(" \nGroup mask: 0x%llX WorkQueue Int : 0x%llX\n", oct_read64(OCTEON_POW_CORE_GROUP_MASK(OUR_CORE)), oct_read64(OCTEON_POW_WORKQUEUE_INT));
+ intpc.word64 = oct_read64(OCTEON_POW_WORKQUEUE_INT_PC);
+ printf(" Intr Periodic Cntr: PC %u thr: %u\n", intpc.bits.thr_freq, intpc.bits.thr_period);
+ thr.word64 = oct_read64(OCTEON_POW_WORKQUEUE_INT_THRESHOLD(OCTEON_POW_RX_GROUP_NUM));
+ printf(" Thresholds iq %u ds %u time %u enable %u\n",
+ thr.bits.iq_thr, thr.bits.ds_thr, thr.bits.time_thr, thr.bits.enable);
+ int_cnt.word64 = oct_read64(OCTEON_POW_INT_CNTR(core));
+ printf(" Int_cnt iq_cnt %u ds_cnt %u tc_cnt %u\n",
+ int_cnt.bits.iq_cnt, int_cnt.bits.ds_cnt, int_cnt.bits.tc_cnt);
+ pkts.word64 = oct_read64(OCTEON_PIP_STAT_INB_PKTS(16)); pkts0 += pkts.bits.inb_pkts;
+ errs.word64 = oct_read64(OCTEON_PIP_STAT_INB_ERRS(16)); errs0 += errs.bits.inb_errs;
+ pkts.word64 = oct_read64(OCTEON_PIP_STAT_INB_PKTS(17)); pkts1 += pkts.bits.inb_pkts;
+ errs.word64 = oct_read64(OCTEON_PIP_STAT_INB_ERRS(17)); errs1 += errs.bits.inb_errs;
+ printf(" PIP inbound pkts(16): %u Errors: %u inbound(17): %u Errors: %u\n", pkts0, errs0, pkts1, errs1);
+ inpt_q_all.word64 = oct_read64(OCTEON_POW_INPT_Q_ALL_QOS);
+ printf(" All queued pkt in qos Levels: %u -- ", inpt_q_all.bits.iq_cnt);
+ for (i = 0 ; i < 7; i++) {
+ inpt_q_grp.word64 = oct_read64(OCTEON_POW_INPT_QOS_GRP(i));
+ if (inpt_q_grp.bits.iq_cnt) printf(" Grp-%u: %u ", i, inpt_q_grp.bits.iq_cnt);
+ }
+}
+#endif
+
+/* ------------------------------------------------------------------- *
+ * octeon_line_status_loop() *
+ * ------------------------------------------------------------------- */
+static void octeon_line_status_loop (void *unused)
+{
+ struct rgmx_softc_dev *sc;
+ u_int idx;
+
+ for (idx = 0; idx < num_devices; idx++) {
+ sc = rgmx_scdev_array[idx];
+ if (sc && sc->ifp) {
+ if ((sc->ifp->if_drv_flags & IFF_DRV_RUNNING)) {
+ octeon_rgmx_config_speed(sc->port, 1);
+
+ octeon_rgmx_output_start(sc->ifp);
+ }
+ }
+ }
+
+//#define DEBUG_RGMX_DUMP
+#ifdef DEBUG_RGMX_DUMP
+ static int count = 0;
+
+ if (++count > 5) {
+ count = 0;
+// octeon_dump_fpa_pool(OCTEON_FPA_RX_PACKET_POOL);
+// octeon_dump_fpa_pool(OCTEON_FPA_WQE_RX_POOL);
+// octeon_dump_fpa_pool(OCTEON_FPA_TX_PACKET_POOL);
+ octeon_dump_rgmx_stats(16);
+ octeon_dump_pow_stats();
+ }
+#endif
+}
+
+
+/* ------------------------------------------------------------------- *
+ * octeon_rgmx_set_mac *
+ * ------------------------------------------------------------------- *
+ *
+ * octeon_rgmx_set_mac
+ *
+ * Program the ethernet HW address
+ *
+ */
+static void octeon_rgmx_set_mac (u_int port)
+{
+ struct rgmx_softc_dev *sc;
+ u_int iface = INTERFACE(port);
+ u_int index = INDEX(port);
+ int ii;
+ uint64_t mac = 0;
+ u_int last_enabled;
+
+ sc = get_rgmx_softc(port);
+ if (!sc) {
+ printf(" octeon_rgmx_set_mac Missing sc. port:%u", port);
+ return;
+ }
+
+ for (ii = 0; ii < 6; ii++) {
+ mac = (mac << 8) | (uint64_t)(sc->ieee[ii]);
+ }
+
+ last_enabled = octeon_rgmx_stop_port(port);
+
+ oct_write64(OCTEON_RGMX_SMACX(index, iface), mac);
+ oct_write64(OCTEON_RGMX_RXX_ADR_CAM0(index, iface), sc->ieee[0]);
+ oct_write64(OCTEON_RGMX_RXX_ADR_CAM1(index, iface), sc->ieee[1]);
+ oct_write64(OCTEON_RGMX_RXX_ADR_CAM2(index, iface), sc->ieee[2]);
+ oct_write64(OCTEON_RGMX_RXX_ADR_CAM3(index, iface), sc->ieee[3]);
+ oct_write64(OCTEON_RGMX_RXX_ADR_CAM4(index, iface), sc->ieee[4]);
+ oct_write64(OCTEON_RGMX_RXX_ADR_CAM5(index, iface), sc->ieee[5]);
+ oct_write64(OCTEON_RGMX_RXX_ADR_CTL(index, iface),
+ OCTEON_RGMX_ADRCTL_ACCEPT_BROADCAST |
+ OCTEON_RGMX_ADRCTL_ACCEPT_ALL_MULTICAST |
+ OCTEON_RGMX_ADRCTL_CAM_MODE_ACCEPT_DMAC);
+ oct_write64(OCTEON_RGMX_RXX_ADR_CAM_EN(index, iface), 1);
+ if (last_enabled) octeon_rgmx_start_port(port);
+}
+
+
+/* ------------------------------------------------------------------- *
+ * octeon_config_rgmii_port() *
+ * ------------------------------------------------------------------- */
+static void octeon_config_rgmii_port (u_int port)
+{
+ u_int iface = INTERFACE(port);
+ u_int index = INDEX(port);
+
+ /*
+ * Configure an RGMII port
+ */
+ octeon_rgmx_prtx_cfg_t gmx_cfg;
+
+ /* Enable ASX */
+ oct_write64(OCTEON_ASXX_RX_PRT_EN(iface), oct_read64(OCTEON_ASXX_RX_PRT_EN(iface)) | (1<<index));
+ oct_write64(OCTEON_ASXX_TX_PRT_EN(iface), oct_read64(OCTEON_ASXX_TX_PRT_EN(iface)) | (1<<index));
+
+ /* Enable RGMX */
+ gmx_cfg.word64 = oct_read64(OCTEON_RGMX_PRTX_CFG(index, iface));
+ gmx_cfg.bits.en = 1;
+ oct_write64(OCTEON_RGMX_PRTX_CFG(index, iface), gmx_cfg.word64);
+
+ octeon_rgmx_config_speed(port, 0);
+
+ oct_write64(OCTEON_RGMX_TXX_THRESH(index, iface), 32);
+
+ /*
+ * Set hi water mark
+ */
+ oct_write64(OCTEON_ASXX_TX_HI_WATERX(index, iface), 10);
+ if (octeon_get_chipid() == OCTEON_CN5020_CHIP) {
+ oct_write64(OCTEON_ASXX_TX_CLK_SETX(index, iface), 16);
+ oct_write64(OCTEON_ASXX_RX_CLK_SETX(index, iface), 16);
+ } else {
+ oct_write64(OCTEON_ASXX_TX_CLK_SETX(index, iface), 24);
+ oct_write64(OCTEON_ASXX_RX_CLK_SETX(index, iface), 24);
+ }
+}
+
+
+
+static void octeon_rgmx_enable_RED_queue (int queue, int slow_drop, int all_drop)
+{
+ octeon_rgmx_ipd_queue_red_marks_t red_marks;
+ octeon_rgmx_ipd_red_q_param_t red_param;
+
+ if (slow_drop == all_drop) { printf("Bad val in %s", __FUNCTION__); return; }
+ red_marks.word64 = 0;
+ red_marks.bits.all_drop = all_drop;
+ red_marks.bits.slow_drop = slow_drop;
+ oct_write64(OCTEON_IPD_QOSX_RED_MARKS(queue), red_marks.word64);
+
+ /* Use the actual queue 0 counter, not the average */
+ red_param.word64 = 0;
+ red_param.bits.prb_con = (255ul << 24) / (slow_drop - all_drop);
+ red_param.bits.avg_con = 1;
+ red_param.bits.new_con = 255;
+ red_param.bits.use_pagecount = 1;
+ oct_write64(OCTEON_IPD_RED_Q_PARAM(queue), red_param.word64);
+}
+
+
+static void octeon_rgmx_enable_RED_all (int slow_drop, int all_drop)
+{
+
+ int port, queue;
+ octeon_ipd_port_bp_page_count_t ipd_bp_page_count;
+ octeon_ipd_red_port_enable_t red_port_enable;
+
+ /*
+ * First remove BP settings
+ */
+ ipd_bp_page_count.word64 = 0;
+ ipd_bp_page_count.bits.bp_enable = 0;
+ ipd_bp_page_count.bits.page_count = 100;
+
+ for (port = 0; port < OCTEON_RGMX_MAX_PORT; port++) {
+ oct_write64(OCTEON_IPD_PORT_BP_PAGE_COUNT(port), ipd_bp_page_count.word64);
+ }
+
+ /*
+ * Enable RED for each individual queue
+ */
+ for (queue = 0; queue < 8; queue++) {
+ octeon_rgmx_enable_RED_queue(queue, slow_drop, all_drop);
+ }
+
+ oct_write64(OCTEON_IPD_BP_PORT_RED_END, 0);
+
+ red_port_enable.word64 = 0;
+ red_port_enable.bits.port_enable = 0xfffffffffull;
+ red_port_enable.bits.avg_dly = 10000;
+ red_port_enable.bits.prb_dly = 10000;
+ oct_write64(OCTEON_IPD_RED_PORT_ENABLE, red_port_enable.word64);
+}
+
+
+
+/* ------------------------------------------------------------------- *
+ * octeon_has_4ports() *
+ * ------------------------------------------------------------------- */
+static int octeon_has_4ports (void)
+{
+ u_int chipid;
+ int retcode = 1;
+
+ chipid = octeon_get_chipid();
+
+ switch (chipid) {
+ case OCTEON_CN31XX_CHIP:
+ case OCTEON_CN30XX_CHIP:
+ case OCTEON_CN5020_CHIP:
+ retcode = 0;
+ break;
+
+ default:
+ break;
+ }
+ return (retcode);
+}
+
+
+#ifdef __not_used__
+/*
+ * octeon_rgmx_free_intr
+ *
+ * We have 4 child and one parent device.
+ * It's tricky and unexpected that anyone will detach the device that is built'in on
+ * the chip.
+ * We will not support detachment for now. But keep adding good code that will be used
+ * someday.
+ */
+static void octeon_rgmx_free_intr (struct rgmx_softc_dev *sc)
+{
+ device_t dev = sc->sc_dev;
+
+ /*
+ * Make sure that sc/dev are the parent Root structs. Not one
+ * of the rgmxN childs.
+ */
+ if (int_handler_tag != NULL) {
+ bus_teardown_intr(dev, irq_res, int_handler_tag);
+ int_handler_tag = NULL;
+ }
+
+#ifdef OCTEON_RGMX_SCHEDULED_ISRS
+ if (tq != NULL) {
+ taskqueue_drain(tq, &rxtx_isr_task);
+ taskqueue_drain(taskqueue_fast, &link_isr_task);
+ taskqueue_free(tq);
+ tq = NULL;
+ }
+#endif
+
+}
+#endif
+
+static device_method_t rgmii_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, rgmii_probe),
+ DEVMETHOD(device_identify, rgmii_identify),
+ DEVMETHOD(device_attach, rgmii_attach),
+ DEVMETHOD(device_detach, bus_generic_detach),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+
+ { 0, 0 }
+};
+
+static driver_t rgmii_driver = {
+ "rgmii", rgmii_methods, sizeof(struct rgmx_softc_dev)
+};
+
+static devclass_t rgmii_devclass;
+
+DRIVER_MODULE(rgmii, nexus, rgmii_driver, rgmii_devclass, 0, 0);
diff --git a/sys/mips/cavium/dev/rgmii/octeon_rgmx.h b/sys/mips/cavium/dev/rgmii/octeon_rgmx.h
new file mode 100644
index 000000000000..19393416da81
--- /dev/null
+++ b/sys/mips/cavium/dev/rgmii/octeon_rgmx.h
@@ -0,0 +1,630 @@
+/***********************license start***************
+ * Copyright (c) 2003-2008 Cavium Networks (support@cavium.com). All rights
+ * reserved.
+ *
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * 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.
+ *
+ * * Neither the name of Cavium Networks nor the names of
+ * its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written
+ * permission.
+ *
+ * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+ * AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS
+ * OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH
+ * RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY
+ * REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT
+ * DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES
+ * OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR
+ * PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET
+ * POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT
+ * OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+ *
+ *
+ * For any questions regarding licensing please contact marketing@caviumnetworks.com
+ *
+ ***********************license end**************************************/
+
+/* $FreeBSD$ */
+
+/*------------------------------------------------------------------
+ * octeon_rgmx.h RGMII Ethernet Interfaces
+ *
+ *------------------------------------------------------------------
+ */
+
+
+#ifndef ___OCTEON_RGMX__H___
+#define ___OCTEON_RGMX__H___
+
+
+
+#define OCTEON_FPA_PACKET_POOL 0
+#define OCTEON_FPA_WQE_RX_POOL 1
+#define OCTEON_FPA_OUTPUT_BUFFER_POOL 2
+#define OCTEON_FPA_WQE_POOL_SIZE (1 * OCTEON_CACHE_LINE_SIZE)
+#define OCTEON_FPA_OUTPUT_BUFFER_POOL_SIZE (8 * OCTEON_CACHE_LINE_SIZE)
+#define OCTEON_FPA_PACKET_POOL_SIZE (16 * OCTEON_CACHE_LINE_SIZE)
+
+#define OCTEON_POW_WORK_REQUEST(wait) (0x8001600000000000ull | (wait<<3))
+
+typedef union
+{
+ void* ptr;
+ uint64_t word64;
+ struct
+ {
+ uint64_t i : 1;
+ uint64_t back : 4;
+ uint64_t pool : 3;
+ uint64_t size :16;
+ uint64_t addr :40;
+ } bits;
+} octeon_buf_ptr_t;
+
+/**
+ * Work queue entry format
+ */
+typedef struct
+{
+ uint16_t hw_chksum;
+ uint8_t unused;
+ uint64_t next_ptr : 40;
+ uint64_t len :16;
+ uint64_t ipprt : 6;
+ uint64_t qos : 3;
+ uint64_t grp : 4;
+ uint64_t tag_type : 3;
+ uint64_t tag :32;
+ union
+ {
+ uint64_t word64;
+ struct
+ {
+ uint64_t bufs : 8;
+ uint64_t ip_offset : 8;
+ uint64_t vlan_valid : 1;
+ uint64_t unassigned : 2;
+ uint64_t vlan_cfi : 1;
+ uint64_t vlan_id :12;
+ uint64_t unassigned2 :12;
+ uint64_t dec_ipcomp : 1;
+ uint64_t tcp_or_udp : 1;
+ uint64_t dec_ipsec : 1;
+ uint64_t is_v6 : 1;
+ uint64_t software : 1;
+ uint64_t L4_error : 1;
+ uint64_t is_frag : 1;
+ uint64_t IP_exc : 1;
+ uint64_t is_bcast : 1;
+ uint64_t is_mcast : 1;
+ uint64_t not_IP : 1;
+ uint64_t rcv_error : 1;
+ uint64_t err_code : 8;
+ } bits;
+ struct
+ {
+ uint64_t bufs : 8;
+ uint64_t unused : 8;
+ uint64_t vlan_valid : 1;
+ uint64_t unassigned : 2;
+ uint64_t vlan_cfi : 1;
+ uint64_t vlan_id :12;
+ uint64_t unassigned2 :16;
+ uint64_t software : 1;
+ uint64_t unassigned3 : 1;
+ uint64_t is_rarp : 1;
+ uint64_t is_arp : 1;
+ uint64_t is_bcast : 1;
+ uint64_t is_mcast : 1;
+ uint64_t not_IP : 1;
+ uint64_t rcv_error : 1;
+ uint64_t err_code : 8;
+ } snoip;
+ } word2;
+ octeon_buf_ptr_t packet_ptr;
+ uint8_t packet_data[96];
+} octeon_wqe_t;
+
+typedef union {
+ uint64_t word64;
+
+ struct {
+ uint64_t scraddr : 8; /**< the (64-bit word) location in scratchpad to write to (if len != 0) */
+ uint64_t len : 8; /**< the number of words in the response (0 => no response) */
+ uint64_t did : 8; /**< the ID of the device on the non-coherent bus */
+ uint64_t unused :36;
+ uint64_t wait : 1; /**< if set, don't return load response until work is available */
+ uint64_t unused2 : 3;
+ } bits;
+
+} octeon_pow_iobdma_store_t;
+
+
+/**
+ * Wait flag values for pow functions.
+ */
+typedef enum
+{
+ OCTEON_POW_WAIT = 1,
+ OCTEON_POW_NO_WAIT = 0,
+} octeon_pow_wait_t;
+
+
+
+static inline void * phys_to_virt (unsigned long address)
+{
+ return (void *)(address + 0x80000000UL);
+}
+
+// decode within DMA space
+typedef enum {
+ OCTEON_ADD_WIN_DMA_ADD = 0L, // add store data to the write buffer entry, allocating it if necessary
+ OCTEON_ADD_WIN_DMA_SENDMEM = 1L, // send out the write buffer entry to DRAM
+ // store data must be normal DRAM memory space address in this case
+ OCTEON_ADD_WIN_DMA_SENDDMA = 2L, // send out the write buffer entry as an IOBDMA command
+ // see OCTEON_ADD_WIN_DMA_SEND_DEC for data contents
+ OCTEON_ADD_WIN_DMA_SENDIO = 3L, // send out the write buffer entry as an IO write
+ // store data must be normal IO space address in this case
+ OCTEON_ADD_WIN_DMA_SENDSINGLE = 4L, // send out a single-tick command on the NCB bus
+ // no write buffer data needed/used
+} octeon_add_win_dma_dec_t;
+
+
+#define OCTEON_OCT_DID_FPA 5ULL
+#define OCTEON_OCT_DID_TAG 12ULL
+#define OCTEON_OCT_DID_TAG_SWTAG OCTEON_ADDR_FULL_DID(OCTEON_OCT_DID_TAG, 0ULL)
+
+
+#define OCTEON_IOBDMA_OFFSET (-3*1024ll)
+#define OCTEON_IOBDMA_SEP 16
+#define OCTEON_IOBDMA_SENDSINGLE (OCTEON_IOBDMA_OFFSET + \
+ (OCTEON_ADD_WIN_DMA_SENDSINGLE *\
+ OCTEON_IOBDMA_SEP))
+
+static inline void octeon_send_single (uint64_t data)
+{
+ oct_write64((uint64_t)(OCTEON_IOBDMA_SENDSINGLE * (long long)8), data);
+}
+
+
+static inline void octeon_pow_work_request_async_nocheck (int scratch_addr,
+ octeon_pow_wait_t wait)
+{
+ octeon_pow_iobdma_store_t data;
+
+ /* scratch_addr must be 8 byte aligned */
+ data.bits.scraddr = scratch_addr >> 3;
+ data.bits.len = 1;
+ data.bits.did = OCTEON_OCT_DID_TAG_SWTAG;
+ data.bits.wait = wait;
+ octeon_send_single(data.word64);
+}
+
+
+
+/**
+ * octeon_gmx_inf_mode
+ *
+ * GMX_INF_MODE = Interface Mode
+ *
+ */
+typedef union
+{
+ uint64_t word64;
+ struct gmxx_inf_mode_s
+ {
+ uint64_t reserved_3_63 : 61;
+ uint64_t p0mii : 1; /**< Port 0 Interface Mode
+ 0: Port 0 is RGMII
+ 1: Port 0 is MII */
+ uint64_t en : 1; /**< Interface Enable */
+ uint64_t type : 1; /**< Interface Mode
+ 0: RGMII Mode
+ 1: Spi4 Mode */
+ } bits;
+ struct gmxx_inf_mode_cn3020
+ {
+ uint64_t reserved_2_63 : 62;
+ uint64_t en : 1; /**< Interface Enable */
+ uint64_t type : 1; /**< Interface Mode
+ 0: All three ports are RGMII ports
+ 1: prt0 is RGMII, prt1 is GMII, and prt2 is unused */
+ } cn3020;
+ struct gmxx_inf_mode_s cn30xx;
+ struct gmxx_inf_mode_cn3020 cn31xx;
+ struct gmxx_inf_mode_cn3020 cn36xx;
+ struct gmxx_inf_mode_cn3020 cn38xx;
+ struct gmxx_inf_mode_cn3020 cn38xxp2;
+ struct gmxx_inf_mode_cn3020 cn56xx;
+ struct gmxx_inf_mode_cn3020 cn58xx;
+} octeon_gmxx_inf_mode_t;
+
+
+
+
+typedef union {
+ uint64_t word64;
+ struct {
+ uint64_t reserved : 60; /* Reserved */
+ uint64_t slottime : 1; /* Slot Time for Half-Duplex */
+ /* operation - 0 = 512 bitimes (10/100Mbs operation) */
+ /* - 1 = 4096 bitimes (1000Mbs operation) */
+ uint64_t duplex : 1; /* Duplex - 0 = Half Duplex */
+ /* (collisions/extentions/bursts) - 1 = Full Duplex */
+ uint64_t speed : 1; /* Link Speed - 0 = 10/100Mbs */
+ /* operation - 1 = 1000Mbs operation */
+ uint64_t en : 1; /* Link Enable */
+ } bits;
+} octeon_rgmx_prtx_cfg_t;
+
+
+/*
+ * GMX_RX_INBND = RGMX InBand Link Status
+ *
+ */
+typedef union {
+ uint64_t word64;
+ struct {
+ uint64_t reserved : 60; /* Reserved */
+ uint64_t duplex : 1; /* 0 = Half, 1 = Full */
+ uint64_t speed : 2; /* Inbound Link Speed */
+ /* 00 = 2.5Mhz, 01 = 25Mhz */
+ /* 10 = 125MHz, 11 = Reserved */
+ uint64_t status : 1; /* Inbound Status Up/Down */
+ } bits;
+} octeon_rgmx_rxx_rx_inbnd_t;
+
+
+
+typedef union
+{
+ uint64_t word64;
+ struct {
+ uint64_t all_drop : 32;
+ uint64_t slow_drop : 32;
+ } bits;
+} octeon_rgmx_ipd_queue_red_marks_t;
+
+
+typedef union
+{
+ uint64_t word64;
+ struct {
+ uint64_t reserved : 15;
+ uint64_t use_pagecount : 1;
+ uint64_t new_con : 8;
+ uint64_t avg_con : 8;
+ uint64_t prb_con : 32;
+ } bits;
+} octeon_rgmx_ipd_red_q_param_t;
+
+
+
+typedef union
+{
+ uint64_t word64;
+ struct {
+ uint64_t reserved : 46;
+ uint64_t bp_enable : 1;
+ uint64_t page_count : 17;
+ } bits;
+} octeon_ipd_port_bp_page_count_t;
+
+
+typedef union
+{
+ uint64_t word64;
+ struct {
+ uint64_t prb_dly : 14;
+ uint64_t avg_dly : 14;
+ uint64_t port_enable : 36;
+ } bits;
+} octeon_ipd_red_port_enable_t;
+
+
+/**
+ * Tag type definitions
+ */
+typedef enum
+{
+ OCTEON_POW_TAG_TYPE_ORDERED = 0L, /**< Tag ordering is maintained */
+ OCTEON_POW_TAG_TYPE_ATOMIC = 1L, /**< Tag ordering is maintained, and at most one PP has the tag */
+ OCTEON_POW_TAG_TYPE_NULL = 2L, /**< The work queue entry from the order
+ - NEVER tag switch from NULL to NULL */
+ OCTEON_POW_TAG_TYPE_NULL_NULL = 3L /**< A tag switch to NULL, and there is no space reserved in POW
+ - NEVER tag switch to NULL_NULL
+ - NEVER tag switch from NULL_NULL
+ - NULL_NULL is entered at the beginning of time and on a deschedule.
+ - NULL_NULL can be exited by a new work request. A NULL_SWITCH load can also switch the state to NULL */
+} octeon_pow_tag_type_t ;
+
+/**
+ * This structure defines the response to a load/SENDSINGLE to POW (except CSR reads)
+ */
+typedef union {
+ uint64_t word64;
+
+ octeon_wqe_t *wqp;
+
+ // response to new work request loads
+ struct {
+ uint64_t no_work : 1; // set when no new work queue entry was returned
+ // If there was de-scheduled work, the HW will definitely
+ // return it. When this bit is set, it could mean
+ // either mean:
+ // - There was no work, or
+ // - There was no work that the HW could find. This
+ // case can happen, regardless of the wait bit value
+ // in the original request, when there is work
+ // in the IQ's that is too deep down the list.
+ uint64_t unused : 23;
+ uint64_t addr : 40; // 36 in O1 -- the work queue pointer
+ } s_work;
+
+ // response to NULL_RD request loads
+ struct {
+ uint64_t unused : 62;
+ uint64_t state : 2; // of type octeon_pow_tag_type_t
+ // state is one of the following:
+ // OCTEON_POW_TAG_TYPE_ORDERED
+ // OCTEON_POW_TAG_TYPE_ATOMIC
+ // OCTEON_POW_TAG_TYPE_NULL
+ // OCTEON_POW_TAG_TYPE_NULL_NULL
+ } s_null_rd;
+
+} octeon_pow_tag_load_resp_t;
+
+
+/*
+ * This structure describes the address to load stuff from POW
+ */
+typedef union {
+ uint64_t word64;
+
+ // address for new work request loads (did<2:0> == 0)
+ struct {
+ uint64_t mem_region :2;
+ uint64_t mbz :13;
+ uint64_t is_io : 1; // must be one
+ uint64_t did : 8; // the ID of POW -- did<2:0> == 0 in this case
+ uint64_t unaddr : 4;
+ uint64_t unused :32;
+ uint64_t wait : 1; // if set, don't return load response until work is available
+ uint64_t mbzl : 3; // must be zero
+ } swork; // physical address
+
+
+ // address for NULL_RD request (did<2:0> == 4)
+ // when this is read, HW attempts to change the state to NULL if it is NULL_NULL
+ // (the hardware cannot switch from NULL_NULL to NULL if a POW entry is not available -
+ // software may need to recover by finishing another piece of work before a POW
+ // entry can ever become available.)
+ struct {
+ uint64_t mem_region :2;
+ uint64_t mbz :13;
+ uint64_t is_io : 1; // must be one
+ uint64_t did : 8; // the ID of POW -- did<2:0> == 4 in this case
+ uint64_t unaddr : 4;
+ uint64_t unused :33;
+ uint64_t mbzl : 3; // must be zero
+ } snull_rd; // physical address
+
+ // address for CSR accesses
+ struct {
+ uint64_t mem_region :2;
+ uint64_t mbz :13;
+ uint64_t is_io : 1; // must be one
+ uint64_t did : 8; // the ID of POW -- did<2:0> == 7 in this case
+ uint64_t unaddr : 4;
+ uint64_t csraddr:36; // only 36 bits in O1, addr<2:0> must be zero
+ } stagcsr; // physical address
+
+} octeon_pow_load_addr_t;
+
+
+static inline void octeon_pow_tag_switch_wait (void)
+{
+ uint64_t switch_complete;
+
+ do
+ {
+ OCTEON_CHORD_HEX(&switch_complete);
+ } while (!switch_complete);
+
+ return;
+}
+
+
+static inline octeon_wqe_t *octeon_pow_work_request_sync_nocheck (octeon_pow_wait_t wait)
+{
+ octeon_pow_load_addr_t ptr;
+ octeon_pow_tag_load_resp_t result;
+
+ ptr.word64 = 0;
+ ptr.swork.mem_region = OCTEON_IO_SEG;
+ ptr.swork.is_io = 1;
+ ptr.swork.did = OCTEON_OCT_DID_TAG_SWTAG;
+ ptr.swork.wait = wait;
+
+ result.word64 = oct_read64(ptr.word64);
+
+ if (result.s_work.no_work || !result.s_work.addr) {
+ return NULL;
+ }
+ return (octeon_wqe_t *) MIPS_PHYS_TO_KSEG0(result.s_work.addr);
+}
+
+static inline octeon_wqe_t *octeon_pow_work_request_sync_nocheck_debug (octeon_pow_wait_t wait)
+{
+ octeon_pow_load_addr_t ptr;
+ octeon_pow_tag_load_resp_t result;
+
+ ptr.word64 = 0;
+ ptr.swork.mem_region = OCTEON_IO_SEG;
+ ptr.swork.is_io = 1;
+ ptr.swork.did = OCTEON_OCT_DID_TAG_SWTAG;
+ ptr.swork.wait = wait;
+
+ result.word64 = oct_read64(ptr.word64);
+
+ printf("WQE Result: 0x%llX No-work %X Addr %llX Ptr: %p\n",
+ (unsigned long long)result.word64, result.s_work.no_work,
+ (unsigned long long)result.s_work.addr,
+ (void *)MIPS_PHYS_TO_KSEG0(result.s_work.addr));
+
+ if (result.s_work.no_work || !result.s_work.addr) {
+ return NULL;
+ }
+ return (octeon_wqe_t *) MIPS_PHYS_TO_KSEG0(result.s_work.addr);
+}
+
+static inline octeon_wqe_t *octeon_pow_work_request_sync (octeon_pow_wait_t wait)
+{
+ octeon_pow_tag_switch_wait();
+ return (octeon_pow_work_request_sync_nocheck(wait));
+}
+
+
+static inline octeon_wqe_t *octeon_pow_work_request_sync_debug (octeon_pow_wait_t wait)
+{
+ octeon_pow_tag_switch_wait();
+ return (octeon_pow_work_request_sync_nocheck_debug(wait));
+}
+
+
+
+/**
+ * Gets result of asynchronous work request. Performs a IOBDMA sync
+ * to wait for the response.
+ *
+ * @param scratch_addr Scratch memory address to get result from
+ * Byte address, must be 8 byte aligned.
+ * @return Returns the WQE from the scratch register, or NULL if no work was available.
+ */
+static inline octeon_wqe_t *octeon_pow_work_response_async(int scratch_addr)
+{
+ octeon_pow_tag_load_resp_t result;
+
+ OCTEON_SYNCIOBDMA;
+ result.word64 = oct_scratch_read64(scratch_addr);
+
+ if (result.s_work.no_work) {
+ return NULL;
+ }
+ return (octeon_wqe_t*) MIPS_PHYS_TO_KSEG0(result.s_work.addr);
+}
+
+
+
+/*
+ * The address from POW is a physical address. Adjust for back ptr, as well as
+ * make it accessible using KSEG0.
+ */
+static inline void *octeon_pow_pktptr_to_kbuffer (octeon_buf_ptr_t pkt_ptr)
+{
+ return ((void *)MIPS_PHYS_TO_KSEG0(
+ ((pkt_ptr.bits.addr >> 7) - pkt_ptr.bits.back) << 7));
+}
+
+#define INTERFACE(port) (port >> 4) /* Ports 0-15 are interface 0, 16-31 are interface 1 */
+#define INDEX(port) (port & 0xf)
+
+
+#define OCTEON_RGMX_PRTX_CFG(index,interface) (0x8001180008000010ull+((index)*2048)+((interface)*0x8000000ull))
+#define OCTEON_RGMX_SMACX(offset,block_id) (0x8001180008000230ull+((offset)*2048)+((block_id)*0x8000000ull))
+#define OCTEON_RGMX_RXX_ADR_CAM0(offset,block_id) (0x8001180008000180ull+((offset)*2048)+((block_id)*0x8000000ull))
+#define OCTEON_RGMX_RXX_ADR_CAM1(offset,block_id) (0x8001180008000188ull+((offset)*2048)+((block_id)*0x8000000ull))
+#define OCTEON_RGMX_RXX_ADR_CAM2(offset,block_id) (0x8001180008000190ull+((offset)*2048)+((block_id)*0x8000000ull))
+#define OCTEON_RGMX_RXX_ADR_CAM3(offset,block_id) (0x8001180008000198ull+((offset)*2048)+((block_id)*0x8000000ull))
+#define OCTEON_RGMX_RXX_ADR_CAM4(offset,block_id) (0x80011800080001A0ull+((offset)*2048)+((block_id)*0x8000000ull))
+#define OCTEON_RGMX_RXX_ADR_CAM5(offset,block_id) (0x80011800080001A8ull+((offset)*2048)+((block_id)*0x8000000ull))
+#define OCTEON_RGMX_RXX_ADR_CTL(offset,block_id) (0x8001180008000100ull+((offset)*2048)+((block_id)*0x8000000ull))
+#define OCTEON_RGMX_RXX_ADR_CAM_EN(offset,block_id) (0x8001180008000108ull+((offset)*2048)+((block_id)*0x8000000ull))
+#define OCTEON_RGMX_INF_MODE(block_id) (0x80011800080007F8ull+((block_id)*0x8000000ull))
+#define OCTEON_RGMX_TX_PRTS(block_id) (0x8001180008000480ull+((block_id)*0x8000000ull))
+#define OCTEON_ASXX_RX_PRT_EN(block_id) (0x80011800B0000000ull+((block_id)*0x8000000ull))
+#define OCTEON_ASXX_TX_PRT_EN(block_id) (0x80011800B0000008ull+((block_id)*0x8000000ull))
+#define OCTEON_RGMX_TXX_THRESH(offset,block_id) (0x8001180008000210ull+((offset)*2048)+((block_id)*0x8000000ull))
+#define OCTEON_ASXX_TX_HI_WATERX(offset,block_id) (0x80011800B0000080ull+((offset)*8)+((block_id)*0x8000000ull))
+#define OCTEON_ASXX_RX_CLK_SETX(offset,block_id) (0x80011800B0000020ull+((offset)*8)+((block_id)*0x8000000ull))
+#define OCTEON_ASXX_TX_CLK_SETX(offset,block_id) (0x80011800B0000048ull+((offset)*8)+((block_id)*0x8000000ull))
+#define OCTEON_RGMX_RXX_RX_INBND(offset,block_id) (0x8001180008000060ull+((offset)*2048)+((block_id)*0x8000000ull))
+#define OCTEON_RGMX_TXX_CLK(offset,block_id) (0x8001180008000208ull+((offset)*2048)+((block_id)*0x8000000ull))
+#define OCTEON_RGMX_TXX_SLOT(offset,block_id) (0x8001180008000220ull+((offset)*2048)+((block_id)*0x8000000ull))
+#define OCTEON_RGMX_TXX_BURST(offset,block_id) (0x8001180008000228ull+((offset)*2048)+((block_id)*0x8000000ull))
+#define OCTEON_PIP_GBL_CTL (0x80011800A0000020ull)
+#define OCTEON_PIP_GBL_CFG (0x80011800A0000028ull)
+#define OCTEON_PIP_PRT_CFGX(offset) (0x80011800A0000200ull+((offset)*8))
+#define OCTEON_PIP_PRT_TAGX(offset) (0x80011800A0000400ull+((offset)*8))
+
+
+
+#define OUR_CORE 0
+#define IP2 0
+#define IP3 1
+#define CIU_TIMERS 4
+#define OCTEON_POW_CORE_GROUP_MASK(core) (0x8001670000000000ull + (8 * core))
+
+#define OCTEON_CIU_INT_EN0(CORE,IP) (0x8001070000000200ull + (IP * 16) + \
+ ((CORE) * 32))
+#define OCTEON_CIU_INT_SUM0(CORE,IP) (0x8001070000000000ull + (IP * 8) + \
+ ((CORE) * 32))
+#define OCTEON_CIU_TIMX(offset) (0x8001070000000480ull+((offset)*8))
+
+#define OCTEON_POW_WQ_INT_THRX(offset) ((0x8001670000000080ull+((offset)*8)))
+#define OCTEON_POW_WQ_INT_CNTX(offset) ((0x8001670000000100ull+((offset)*8)))
+#define OCTEON_POW_QOS_THRX(offset) ((0x8001670000000180ull+((offset)*8)))
+#define OCTEON_POW_QOS_RNDX(offset) ((0x80016700000001C0ull+((offset)*8)))
+#define OCTEON_POW_WQ_INT_PC (0x8001670000000208ull)
+#define OCTEON_POW_NW_TIM (0x8001670000000210ull)
+#define OCTEON_POW_ECC_ERR (0x8001670000000218ull)
+#define OCTEON_POW_INT_CTL (0x8001670000000220ull)
+#define OCTEON_POW_NOS_CNT (0x8001670000000228ull)
+#define OCTEON_POW_WS_PCX(offset) ((0x8001670000000280ull+((offset)*8)))
+#define OCTEON_POW_WA_PCX(offset) ((0x8001670000000300ull+((offset)*8)))
+#define OCTEON_POW_IQ_CNTX(offset) ((0x8001670000000340ull+((offset)*8)))
+#define OCTEON_POW_WA_COM_PC (0x8001670000000380ull)
+#define OCTEON_POW_IQ_COM_CNT (0x8001670000000388ull)
+#define OCTEON_POW_TS_PC (0x8001670000000390ull)
+#define OCTEON_POW_DS_PC (0x8001670000000398ull)
+#define OCTEON_POW_BIST_STAT (0x80016700000003F8ull)
+
+
+#define OCTEON_POW_WQ_INT (0x8001670000000200ull)
+
+#define OCTEON_IPD_PORT_BP_COUNTERS_PAIRX(offset) (0x80014F00000001B8ull+((offset)*8))
+
+/*
+ * Current Counts that triggered interrupt
+ */
+#define OCTEON_POW_WQ_INT_CNTX(offset) ((0x8001670000000100ull+((offset)*8)))
+
+
+
+#define OCTEON_RGMX_ADRCTL_CAM_MODE_REJECT_DMAC 0
+#define OCTEON_RGMX_ADRCTL_ACCEPT_BROADCAST 1
+#define OCTEON_RGMX_ADRCTL_REJECT_ALL_MULTICAST 2
+#define OCTEON_RGMX_ADRCTL_ACCEPT_ALL_MULTICAST 4
+#define OCTEON_RGMX_ADRCTL_CAM_MODE_ACCEPT_DMAC 8
+
+
+#define RGMX_LOCK_INIT(_sc, _name) \
+ mtx_init(&(_sc)->mtx, _name, MTX_NETWORK_LOCK, MTX_DEF)
+#define RGMX_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->mtx)
+#define RGMX_LOCK(_sc) mtx_lock(&(_sc)->mtx)
+#define RGMX_UNLOCK(_sc) mtx_unlock(&(_sc)->mtx)
+#define RGMX_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->mtx, MA_OWNED)
+
+#endif /* ___OCTEON_RGMX__H___ */
diff --git a/sys/mips/cavium/driveid.h b/sys/mips/cavium/driveid.h
new file mode 100644
index 000000000000..b7befea11702
--- /dev/null
+++ b/sys/mips/cavium/driveid.h
@@ -0,0 +1,259 @@
+
+/*
+ * driveid.h
+ *
+ */
+
+#ifndef __DRIVEID_H__
+#define __DRIVEID_H__
+
+
+struct hd_driveid {
+ unsigned short config; /* lots of obsolete bit flags */
+ unsigned short cyls; /* Obsolete, "physical" cyls */
+ unsigned short reserved2; /* reserved (word 2) */
+ unsigned short heads; /* Obsolete, "physical" heads */
+ unsigned short track_bytes; /* unformatted bytes per track */
+ unsigned short sector_bytes; /* unformatted bytes per sector */
+ unsigned short sectors; /* Obsolete, "physical" sectors per track */
+ unsigned short vendor0; /* vendor unique */
+ unsigned short vendor1; /* vendor unique */
+ unsigned short vendor2; /* Retired vendor unique */
+ unsigned char serial_no[20]; /* 0 = not_specified */
+ unsigned short buf_type; /* Retired */
+ unsigned short buf_size; /* Retired, 512 byte increments
+ * 0 = not_specified
+ */
+ unsigned short ecc_bytes; /* for r/w long cmds; 0 = not_specified */
+ unsigned char fw_rev[8]; /* 0 = not_specified */
+ unsigned char model[40]; /* 0 = not_specified */
+ unsigned char max_multsect; /* 0=not_implemented */
+ unsigned char vendor3; /* vendor unique */
+ unsigned short dword_io; /* 0=not_implemented; 1=implemented */
+ unsigned char vendor4; /* vendor unique */
+ unsigned char capability; /* (upper byte of word 49)
+ * 3: IORDYsup
+ * 2: IORDYsw
+ * 1: LBA
+ * 0: DMA
+ */
+ unsigned short reserved50; /* reserved (word 50) */
+ unsigned char vendor5; /* Obsolete, vendor unique */
+ unsigned char tPIO; /* Obsolete, 0=slow, 1=medium, 2=fast */
+ unsigned char vendor6; /* Obsolete, vendor unique */
+ unsigned char tDMA; /* Obsolete, 0=slow, 1=medium, 2=fast */
+ unsigned short field_valid; /* (word 53)
+ * 2: ultra_ok word 88
+ * 1: eide_ok words 64-70
+ * 0: cur_ok words 54-58
+ */
+ unsigned short cur_cyls; /* Obsolete, logical cylinders */
+ unsigned short cur_heads; /* Obsolete, l heads */
+ unsigned short cur_sectors; /* Obsolete, l sectors per track */
+ unsigned short cur_capacity0; /* Obsolete, l total sectors on drive */
+ unsigned short cur_capacity1; /* Obsolete, (2 words, misaligned int) */
+ unsigned char multsect; /* current multiple sector count */
+ unsigned char multsect_valid; /* when (bit0==1) multsect is ok */
+ unsigned int lba_capacity; /* Obsolete, total number of sectors */
+ unsigned short dma_1word; /* Obsolete, single-word dma info */
+ unsigned short dma_mword; /* multiple-word dma info */
+ unsigned short eide_pio_modes; /* bits 0:mode3 1:mode4 */
+ unsigned short eide_dma_min; /* min mword dma cycle time (ns) */
+ unsigned short eide_dma_time; /* recommended mword dma cycle time (ns) */
+ unsigned short eide_pio; /* min cycle time (ns), no IORDY */
+ unsigned short eide_pio_iordy; /* min cycle time (ns), with IORDY */
+ unsigned short words69_70[2]; /* reserved words 69-70
+ * future command overlap and queuing
+ */
+ /* HDIO_GET_IDENTITY currently returns only words 0 through 70 */
+ unsigned short words71_74[4]; /* reserved words 71-74
+ * for IDENTIFY PACKET DEVICE command
+ */
+ unsigned short queue_depth; /* (word 75)
+ * 15:5 reserved
+ * 4:0 Maximum queue depth -1
+ */
+ unsigned short words76_79[4]; /* reserved words 76-79 */
+ unsigned short major_rev_num; /* (word 80) */
+ unsigned short minor_rev_num; /* (word 81) */
+ unsigned short command_set_1; /* (word 82) supported
+ * 15: Obsolete
+ * 14: NOP command
+ * 13: READ_BUFFER
+ * 12: WRITE_BUFFER
+ * 11: Obsolete
+ * 10: Host Protected Area
+ * 9: DEVICE Reset
+ * 8: SERVICE Interrupt
+ * 7: Release Interrupt
+ * 6: look-ahead
+ * 5: write cache
+ * 4: PACKET Command
+ * 3: Power Management Feature Set
+ * 2: Removable Feature Set
+ * 1: Security Feature Set
+ * 0: SMART Feature Set
+ */
+ unsigned short command_set_2; /* (word 83)
+ * 15: Shall be ZERO
+ * 14: Shall be ONE
+ * 13: FLUSH CACHE EXT
+ * 12: FLUSH CACHE
+ * 11: Device Configuration Overlay
+ * 10: 48-bit Address Feature Set
+ * 9: Automatic Acoustic Management
+ * 8: SET MAX security
+ * 7: reserved 1407DT PARTIES
+ * 6: SetF sub-command Power-Up
+ * 5: Power-Up in Standby Feature Set
+ * 4: Removable Media Notification
+ * 3: APM Feature Set
+ * 2: CFA Feature Set
+ * 1: READ/WRITE DMA QUEUED
+ * 0: Download MicroCode
+ */
+ unsigned short cfsse; /* (word 84)
+ * cmd set-feature supported extensions
+ * 15: Shall be ZERO
+ * 14: Shall be ONE
+ * 13:6 reserved
+ * 5: General Purpose Logging
+ * 4: Streaming Feature Set
+ * 3: Media Card Pass Through
+ * 2: Media Serial Number Valid
+ * 1: SMART selt-test supported
+ * 0: SMART error logging
+ */
+ unsigned short cfs_enable_1; /* (word 85)
+ * command set-feature enabled
+ * 15: Obsolete
+ * 14: NOP command
+ * 13: READ_BUFFER
+ * 12: WRITE_BUFFER
+ * 11: Obsolete
+ * 10: Host Protected Area
+ * 9: DEVICE Reset
+ * 8: SERVICE Interrupt
+ * 7: Release Interrupt
+ * 6: look-ahead
+ * 5: write cache
+ * 4: PACKET Command
+ * 3: Power Management Feature Set
+ * 2: Removable Feature Set
+ * 1: Security Feature Set
+ * 0: SMART Feature Set
+ */
+ unsigned short cfs_enable_2; /* (word 86)
+ * command set-feature enabled
+ * 15: Shall be ZERO
+ * 14: Shall be ONE
+ * 13: FLUSH CACHE EXT
+ * 12: FLUSH CACHE
+ * 11: Device Configuration Overlay
+ * 10: 48-bit Address Feature Set
+ * 9: Automatic Acoustic Management
+ * 8: SET MAX security
+ * 7: reserved 1407DT PARTIES
+ * 6: SetF sub-command Power-Up
+ * 5: Power-Up in Standby Feature Set
+ * 4: Removable Media Notification
+ * 3: APM Feature Set
+ * 2: CFA Feature Set
+ * 1: READ/WRITE DMA QUEUED
+ * 0: Download MicroCode
+ */
+ unsigned short csf_default; /* (word 87)
+ * command set-feature default
+ * 15: Shall be ZERO
+ * 14: Shall be ONE
+ * 13:6 reserved
+ * 5: General Purpose Logging enabled
+ * 4: Valid CONFIGURE STREAM executed
+ * 3: Media Card Pass Through enabled
+ * 2: Media Serial Number Valid
+ * 1: SMART selt-test supported
+ * 0: SMART error logging
+ */
+ unsigned short dma_ultra; /* (word 88) */
+ unsigned short trseuc; /* time required for security erase */
+ unsigned short trsEuc; /* time required for enhanced erase */
+ unsigned short CurAPMvalues; /* current APM values */
+ unsigned short mprc; /* master password revision code */
+ unsigned short hw_config; /* hardware config (word 93)
+ * 15: Shall be ZERO
+ * 14: Shall be ONE
+ * 13:
+ * 12:
+ * 11:
+ * 10:
+ * 9:
+ * 8:
+ * 7:
+ * 6:
+ * 5:
+ * 4:
+ * 3:
+ * 2:
+ * 1:
+ * 0: Shall be ONE
+ */
+ unsigned short acoustic; /* (word 94)
+ * 15:8 Vendor's recommended value
+ * 7:0 current value
+ */
+ unsigned short msrqs; /* min stream request size */
+ unsigned short sxfert; /* stream transfer time */
+ unsigned short sal; /* stream access latency */
+ unsigned int spg; /* stream performance granularity */
+ unsigned long long lba_capacity_2;/* 48-bit total number of sectors */
+ unsigned short words104_125[22];/* reserved words 104-125 */
+ unsigned short last_lun; /* (word 126) */
+ unsigned short word127; /* (word 127) Feature Set
+ * Removable Media Notification
+ * 15:2 reserved
+ * 1:0 00 = not supported
+ * 01 = supported
+ * 10 = reserved
+ * 11 = reserved
+ */
+ unsigned short dlf; /* (word 128)
+ * device lock function
+ * 15:9 reserved
+ * 8 security level 1:max 0:high
+ * 7:6 reserved
+ * 5 enhanced erase
+ * 4 expire
+ * 3 frozen
+ * 2 locked
+ * 1 en/disabled
+ * 0 capability
+ */
+ unsigned short csfo; /* (word 129)
+ * current set features options
+ * 15:4 reserved
+ * 3: auto reassign
+ * 2: reverting
+ * 1: read-look-ahead
+ * 0: write cache
+ */
+ unsigned short words130_155[26];/* reserved vendor words 130-155 */
+ unsigned short word156; /* reserved vendor word 156 */
+ unsigned short words157_159[3];/* reserved vendor words 157-159 */
+ unsigned short cfa_power; /* (word 160) CFA Power Mode
+ * 15 word 160 supported
+ * 14 reserved
+ * 13
+ * 12
+ * 11:0
+ */
+ unsigned short words161_175[15];/* Reserved for CFA */
+ unsigned short words176_205[30];/* Current Media Serial Number */
+ unsigned short words206_254[49];/* reserved words 206-254 */
+ unsigned short integrity_word; /* (word 255)
+ * 15:8 Checksum
+ * 7:0 Signature
+ */
+};
+
+#endif /* __DRIVEID_H__ */
+
diff --git a/sys/mips/cavium/files.octeon1 b/sys/mips/cavium/files.octeon1
new file mode 100644
index 000000000000..9bd883751256
--- /dev/null
+++ b/sys/mips/cavium/files.octeon1
@@ -0,0 +1,69 @@
+# $FreeBSD$
+# Octeon Support Files
+#
+mips/cavium/asm_octeon.S optional smp
+mips/cavium/ciu.c standard
+mips/cavium/obio.c optional uart
+mips/cavium/octeon_ds1337.c standard
+mips/cavium/octeon_ebt3000_cf.c optional cf
+mips/cavium/octeon_machdep.c standard
+mips/cavium/octeon_mp.c optional smp
+mips/cavium/octeon_rtc.c standard
+mips/cavium/uart_bus_octeonusart.c optional uart
+mips/cavium/uart_cpu_octeonusart.c optional uart
+mips/cavium/uart_dev_oct16550.c optional uart
+mips/mips/intr_machdep.c standard
+mips/mips/tick.c standard
+
+mips/cavium/octeon_rnd.c optional random
+
+mips/cavium/cryptocteon/cavium_crypto.c optional cryptocteon
+mips/cavium/cryptocteon/cryptocteon.c optional cryptocteon
+
+mips/cavium/octe/ethernet.c optional octe
+mips/cavium/octe/ethernet-mv88e61xx.c optional octe octeon_vendor_lanner
+mips/cavium/octe/ethernet-common.c optional octe
+mips/cavium/octe/ethernet-mdio.c optional octe
+mips/cavium/octe/ethernet-mem.c optional octe
+mips/cavium/octe/ethernet-rgmii.c optional octe
+mips/cavium/octe/ethernet-rx.c optional octe
+mips/cavium/octe/ethernet-sgmii.c optional octe
+mips/cavium/octe/ethernet-spi.c optional octe
+mips/cavium/octe/ethernet-tx.c optional octe
+mips/cavium/octe/ethernet-xaui.c optional octe
+mips/cavium/octe/mv88e61xxphy.c optional octe mv88e61xxphy
+mips/cavium/octe/octe.c optional octe
+mips/cavium/octe/octebus.c optional octe
+
+mips/cavium/octopci.c optional pci
+mips/cavium/octopci_bus_space.c optional pci
+
+mips/cavium/usb/octusb.c optional usb octusb
+mips/cavium/usb/octusb_octeon.c optional usb octusb
+
+contrib/octeon-sdk/cvmx-usb.c optional octusb
+
+# XXX Some files could be excluded in some configurations. Making them
+# optional but on in the default config would seem reasonable.
+contrib/octeon-sdk/cvmx-cmd-queue.c standard
+contrib/octeon-sdk/cvmx-bootmem.c standard
+contrib/octeon-sdk/cvmx-fpa.c standard
+contrib/octeon-sdk/cvmx-helper.c standard
+contrib/octeon-sdk/cvmx-helper-board.c standard
+contrib/octeon-sdk/cvmx-helper-errata.c standard
+contrib/octeon-sdk/cvmx-helper-fpa.c standard
+contrib/octeon-sdk/cvmx-helper-loop.c standard
+contrib/octeon-sdk/cvmx-helper-npi.c standard
+contrib/octeon-sdk/cvmx-helper-rgmii.c standard
+contrib/octeon-sdk/cvmx-helper-sgmii.c standard
+contrib/octeon-sdk/cvmx-helper-spi.c standard
+contrib/octeon-sdk/cvmx-helper-util.c standard
+contrib/octeon-sdk/cvmx-helper-xaui.c standard
+contrib/octeon-sdk/cvmx-pko.c standard
+contrib/octeon-sdk/cvmx-spi.c standard
+contrib/octeon-sdk/cvmx-spi4000.c standard
+contrib/octeon-sdk/cvmx-sysinfo.c standard
+contrib/octeon-sdk/cvmx-thunder.c standard
+contrib/octeon-sdk/cvmx-twsi.c standard
+contrib/octeon-sdk/cvmx-warn.c standard
+contrib/octeon-sdk/octeon-model.c standard
diff --git a/sys/mips/cavium/obio.c b/sys/mips/cavium/obio.c
new file mode 100644
index 000000000000..47057303c070
--- /dev/null
+++ b/sys/mips/cavium/obio.c
@@ -0,0 +1,207 @@
+/* $NetBSD: obio.c,v 1.11 2003/07/15 00:25:05 lukem Exp $ */
+
+/*-
+ * Copyright (c) 2001, 2002, 2003 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Jason R. Thorpe for Wasabi Systems, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed for the NetBSD Project by
+ * Wasabi Systems, Inc.
+ * 4. The name of Wasabi Systems, Inc. may not be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * On-board device autoconfiguration support for Cavium OCTEON 1 family of
+ * SoC devices.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/malloc.h>
+
+#include <machine/bus.h>
+
+#include <mips/cavium/octeon_pcmap_regs.h>
+#include <mips/cavium/obiovar.h>
+
+#include <contrib/octeon-sdk/cvmx.h>
+#include <contrib/octeon-sdk/cvmx-interrupt.h>
+
+extern struct bus_space octeon_uart_tag;
+
+static void obio_identify(driver_t *, device_t);
+static int obio_probe(device_t);
+static int obio_attach(device_t);
+
+static void
+obio_identify(driver_t *drv, device_t parent)
+{
+ BUS_ADD_CHILD(parent, 0, "obio", 0);
+}
+
+static int
+obio_probe(device_t dev)
+{
+ if (device_get_unit(dev) != 0)
+ return (ENXIO);
+ return (0);
+}
+
+static int
+obio_attach(device_t dev)
+{
+ struct obio_softc *sc = device_get_softc(dev);
+
+ sc->oba_st = mips_bus_space_generic;
+ /*
+ * XXX
+ * Here and elsewhere using RBR as a base address because it kind of
+ * is, but that feels pretty sloppy. Should consider adding a define
+ * that's more semantic, at least.
+ */
+ sc->oba_addr = CVMX_MIO_UARTX_RBR(0);
+ sc->oba_size = 0x10000;
+ sc->oba_rman.rm_type = RMAN_ARRAY;
+ sc->oba_rman.rm_descr = "OBIO I/O";
+ if (rman_init(&sc->oba_rman) != 0 ||
+ rman_manage_region(&sc->oba_rman,
+ sc->oba_addr, sc->oba_addr + sc->oba_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";
+
+ /*
+ * This module is intended for UART purposes only and
+ * manages IRQs for UART0 and UART1.
+ */
+ if (rman_init(&sc->oba_irq_rman) != 0 ||
+ rman_manage_region(&sc->oba_irq_rman, CVMX_IRQ_UART0, CVMX_IRQ_UART1) != 0)
+ panic("obio_attach: failed to set up IRQ rman");
+
+ device_add_child(dev, "uart", 1); /* Setup Uart-1 first. */
+ device_add_child(dev, "uart", 0); /* Uart-0 next. So it is first in console list */
+ bus_generic_probe(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 resource *rv;
+ struct rman *rm;
+ bus_space_tag_t bt = 0;
+ bus_space_handle_t bh = 0;
+ struct obio_softc *sc = device_get_softc(bus);
+
+ switch (type) {
+ case SYS_RES_IRQ:
+ switch (device_get_unit(child)) {
+ case 0:
+ start = end = CVMX_IRQ_UART0;
+ break;
+ case 1:
+ start = end = CVMX_IRQ_UART1;
+ break;
+ default:
+ return (NULL);
+ }
+ rm = &sc->oba_irq_rman;
+ break;
+ case SYS_RES_MEMORY:
+ return (NULL);
+ case SYS_RES_IOPORT:
+ rm = &sc->oba_rman;
+ bt = &octeon_uart_tag;
+ bh = CVMX_MIO_UARTX_RBR(device_get_unit(child));
+ start = bh;
+ break;
+ default:
+ return (NULL);
+ }
+
+ rv = rman_reserve_resource(rm, start, end, count, flags, child);
+ if (rv == NULL) {
+ return (NULL);
+ }
+ if (type == SYS_RES_IRQ) {
+ return (rv);
+ }
+ rman_set_rid(rv, *rid);
+ rman_set_bustag(rv, bt);
+ rman_set_bushandle(rv, bh);
+
+ if (0) {
+ if (bus_activate_resource(child, type, *rid, rv)) {
+ rman_release_resource(rv);
+ return (NULL);
+ }
+ }
+ return (rv);
+
+}
+
+static int
+obio_activate_resource(device_t bus, device_t child, int type, int rid,
+ struct resource *r)
+{
+ return (0);
+}
+static device_method_t obio_methods[] = {
+ /* Device methods */
+ DEVMETHOD(device_identify, obio_identify),
+ DEVMETHOD(device_probe, obio_probe),
+ DEVMETHOD(device_attach, obio_attach),
+
+ /* Bus methods */
+ DEVMETHOD(bus_alloc_resource, obio_alloc_resource),
+ DEVMETHOD(bus_activate_resource,obio_activate_resource),
+ DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
+ DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
+
+ DEVMETHOD(bus_add_child, bus_generic_add_child),
+
+ {0, 0},
+};
+
+static driver_t obio_driver = {
+ "obio",
+ obio_methods,
+ sizeof(struct obio_softc),
+};
+static devclass_t obio_devclass;
+
+DRIVER_MODULE(obio, ciu, obio_driver, obio_devclass, 0, 0);
diff --git a/sys/mips/cavium/obiovar.h b/sys/mips/cavium/obiovar.h
new file mode 100644
index 000000000000..3b58fe631ad0
--- /dev/null
+++ b/sys/mips/cavium/obiovar.h
@@ -0,0 +1,55 @@
+/* $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 _OCTEON_OBIOVAR_H_
+#define _OCTEON_OBIOVAR_H_
+
+#include <sys/rman.h>
+
+struct obio_softc {
+ bus_space_tag_t oba_st; /* bus space tag */
+ bus_addr_t oba_addr; /* address of device */
+ bus_size_t oba_size; /* size of device */
+ struct rman oba_rman;
+ struct rman oba_irq_rman;
+
+};
+
+#endif /* _OCTEON_OBIOVAR_H_ */
diff --git a/sys/mips/cavium/octe/cavium-ethernet.h b/sys/mips/cavium/octe/cavium-ethernet.h
new file mode 100644
index 000000000000..dce2d2bcfa79
--- /dev/null
+++ b/sys/mips/cavium/octe/cavium-ethernet.h
@@ -0,0 +1,99 @@
+/*************************************************************************
+Copyright (c) 2003-2007 Cavium Networks (support@cavium.com). All rights
+reserved.
+
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * 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.
+
+ * Neither the name of Cavium Networks nor the names of
+ its contributors may be used to endorse or promote products
+ derived from this software without specific prior written
+ permission.
+
+This Software, including technical data, may be subject to U.S. export control laws, including the U.S. Export Administration Act and its associated regulations, and may be subject to export or import regulations in other countries.
+
+TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+
+*************************************************************************/
+/* $FreeBSD$ */
+
+/**
+ * @file
+ * External interface for the Cavium Octeon ethernet driver.
+ *
+ * $Id: cavium-ethernet.h 41589 2009-03-19 19:58:58Z cchavva $
+ *
+ */
+#ifndef CAVIUM_ETHERNET_H
+#define CAVIUM_ETHERNET_H
+
+#include <net/if_media.h>
+
+/**
+ * This is the definition of the Ethernet driver's private
+ * driver state stored in ifp->if_softc.
+ */
+typedef struct {
+ /* XXX FreeBSD device softcs must start with an ifnet pointer. */
+ struct ifnet *ifp;
+
+ int port; /* PKO hardware output port */
+ int queue; /* PKO hardware queue for the port */
+ int fau; /* Hardware fetch and add to count outstanding tx buffers */
+ int imode; /* Type of port. This is one of the enums in cvmx_helper_interface_mode_t */
+#if 0
+ struct ifnet_stats stats; /* Device statistics */
+#endif
+ uint64_t link_info; /* Last negotiated link state */
+ void (*poll)(struct ifnet *ifp); /* Called periodically to check link status */
+
+ /*
+ * FreeBSD additions.
+ */
+ device_t dev;
+ device_t miibus;
+
+ int (*open)(struct ifnet *ifp);
+ int (*stop)(struct ifnet *ifp);
+
+ int (*init)(struct ifnet *ifp);
+ void (*uninit)(struct ifnet *ifp);
+
+ uint8_t mac[6];
+ int phy_id;
+ const char *phy_device;
+ int (*mdio_read)(struct ifnet *, int, int);
+ void (*mdio_write)(struct ifnet *, int, int, int);
+
+ struct ifqueue tx_free_queue[16];
+
+ int need_link_update;
+ struct task link_task;
+ struct ifmedia media;
+ int if_flags;
+
+ struct mtx tx_mtx;
+} cvm_oct_private_t;
+
+
+/**
+ * Free a work queue entry received in a intercept callback.
+ *
+ * @param work_queue_entry
+ * Work queue entry to free
+ * @return Zero on success, Negative on failure.
+ */
+int cvm_oct_free_work(void *work_queue_entry);
+
+#endif
diff --git a/sys/mips/cavium/octe/ethernet-common.c b/sys/mips/cavium/octe/ethernet-common.c
new file mode 100644
index 000000000000..9db9830915e8
--- /dev/null
+++ b/sys/mips/cavium/octe/ethernet-common.c
@@ -0,0 +1,292 @@
+/*************************************************************************
+Copyright (c) 2003-2007 Cavium Networks (support@cavium.com). All rights
+reserved.
+
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * 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.
+
+ * Neither the name of Cavium Networks nor the names of
+ its contributors may be used to endorse or promote products
+ derived from this software without specific prior written
+ permission.
+
+This Software, including technical data, may be subject to U.S. export control laws, including the U.S. Export Administration Act and its associated regulations, and may be subject to export or import regulations in other countries.
+
+TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+
+*************************************************************************/
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+
+#include <net/ethernet.h>
+#include <net/if.h>
+
+#include "wrapper-cvmx-includes.h"
+#include "ethernet-headers.h"
+
+extern int octeon_is_simulation(void);
+extern cvmx_bootinfo_t *octeon_bootinfo;
+extern int pow_send_group;
+extern int always_use_pow;
+extern char pow_send_list[];
+
+
+/**
+ * Get the low level ethernet statistics
+ *
+ * @param dev Device to get the statistics from
+ * @return Pointer to the statistics
+ */
+#if 0
+static struct ifnet_stats *cvm_oct_common_get_stats(struct ifnet *ifp)
+{
+ cvmx_pip_port_status_t rx_status;
+ cvmx_pko_port_status_t tx_status;
+ cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
+
+ if (priv->port < CVMX_PIP_NUM_INPUT_PORTS) {
+ if (octeon_is_simulation()) {
+ /* The simulator doesn't support statistics */
+ memset(&rx_status, 0, sizeof(rx_status));
+ memset(&tx_status, 0, sizeof(tx_status));
+ } else {
+ cvmx_pip_get_port_status(priv->port, 1, &rx_status);
+ cvmx_pko_get_port_status(priv->port, 1, &tx_status);
+ }
+
+ priv->stats.rx_packets += rx_status.inb_packets;
+ priv->stats.tx_packets += tx_status.packets;
+ priv->stats.rx_bytes += rx_status.inb_octets;
+ priv->stats.tx_bytes += tx_status.octets;
+ priv->stats.multicast += rx_status.multicast_packets;
+ priv->stats.rx_crc_errors += rx_status.inb_errors;
+ priv->stats.rx_frame_errors += rx_status.fcs_align_err_packets;
+
+ /* The drop counter must be incremented atomically since the RX
+ tasklet also increments it */
+#ifdef CONFIG_64BIT
+ cvmx_atomic_add64_nosync(&priv->stats.rx_dropped, rx_status.dropped_packets);
+#else
+ cvmx_atomic_add32_nosync((int32_t *)&priv->stats.rx_dropped, rx_status.dropped_packets);
+#endif
+ }
+
+ return &priv->stats;
+}
+#endif
+
+
+/**
+ * Set the multicast list. Currently unimplemented.
+ *
+ * @param dev Device to work on
+ */
+void cvm_oct_common_set_multicast_list(struct ifnet *ifp)
+{
+ cvmx_gmxx_prtx_cfg_t gmx_cfg;
+ cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
+ int interface = INTERFACE(priv->port);
+ int index = INDEX(priv->port);
+
+ if ((interface < 2) && (cvmx_helper_interface_get_mode(interface) != CVMX_HELPER_INTERFACE_MODE_SPI)) {
+ cvmx_gmxx_rxx_adr_ctl_t control;
+ control.u64 = 0;
+ control.s.bcst = 1; /* Allow broadcast MAC addresses */
+
+ if (/*ifp->mc_list || */(ifp->if_flags&IFF_ALLMULTI) ||
+ (ifp->if_flags & IFF_PROMISC))
+ control.s.mcst = 2; /* Force accept multicast packets */
+ else
+ control.s.mcst = 1; /* Force reject multicat packets */
+
+ if (ifp->if_flags & IFF_PROMISC)
+ control.s.cam_mode = 0; /* Reject matches if promisc. Since CAM is shut off, should accept everything */
+ else
+ control.s.cam_mode = 1; /* Filter packets based on the CAM */
+
+ gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
+ cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64 & ~1ull);
+
+ cvmx_write_csr(CVMX_GMXX_RXX_ADR_CTL(index, interface), control.u64);
+ if (ifp->if_flags&IFF_PROMISC)
+ cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM_EN(index, interface), 0);
+ else
+ cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM_EN(index, interface), 1);
+
+ cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64);
+ }
+}
+
+
+/**
+ * Set the hardware MAC address for a device
+ *
+ * @param dev Device to change the MAC address for
+ * @param addr Address structure to change it too.
+ */
+void cvm_oct_common_set_mac_address(struct ifnet *ifp, const void *addr)
+{
+ cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
+ cvmx_gmxx_prtx_cfg_t gmx_cfg;
+ int interface = INTERFACE(priv->port);
+ int index = INDEX(priv->port);
+
+ memcpy(priv->mac, addr, 6);
+
+ if ((interface < 2) && (cvmx_helper_interface_get_mode(interface) != CVMX_HELPER_INTERFACE_MODE_SPI)) {
+ int i;
+ const uint8_t *ptr = addr;
+ uint64_t mac = 0;
+ for (i = 0; i < 6; i++)
+ mac = (mac<<8) | (uint64_t)(ptr[i]);
+
+ gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
+ cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64 & ~1ull);
+
+ cvmx_write_csr(CVMX_GMXX_SMACX(index, interface), mac);
+ cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM0(index, interface), ptr[0]);
+ cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM1(index, interface), ptr[1]);
+ cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM2(index, interface), ptr[2]);
+ cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM3(index, interface), ptr[3]);
+ cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM4(index, interface), ptr[4]);
+ cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM5(index, interface), ptr[5]);
+ cvm_oct_common_set_multicast_list(ifp);
+ cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64);
+ }
+}
+
+
+/**
+ * Change the link MTU. Unimplemented
+ *
+ * @param dev Device to change
+ * @param new_mtu The new MTU
+ * @return Zero on success
+ */
+int cvm_oct_common_change_mtu(struct ifnet *ifp, int new_mtu)
+{
+ cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
+ int interface = INTERFACE(priv->port);
+ int index = INDEX(priv->port);
+ int vlan_bytes = 4;
+
+ /* Limit the MTU to make sure the ethernet packets are between 64 bytes
+ and 65535 bytes */
+ if ((new_mtu + 14 + 4 + vlan_bytes < 64) || (new_mtu + 14 + 4 + vlan_bytes > 65392)) {
+ printf("MTU must be between %d and %d.\n", 64-14-4-vlan_bytes, 65392-14-4-vlan_bytes);
+ return -EINVAL;
+ }
+ ifp->if_mtu = new_mtu;
+
+ if ((interface < 2) && (cvmx_helper_interface_get_mode(interface) != CVMX_HELPER_INTERFACE_MODE_SPI)) {
+ int max_packet = new_mtu + 14 + 4 + vlan_bytes; /* Add ethernet header and FCS, and VLAN if configured. */
+
+ if (OCTEON_IS_MODEL(OCTEON_CN3XXX) || OCTEON_IS_MODEL(OCTEON_CN58XX)) {
+ /* Signal errors on packets larger than the MTU */
+ cvmx_write_csr(CVMX_GMXX_RXX_FRM_MAX(index, interface), max_packet);
+ } else {
+ /* Set the hardware to truncate packets larger than the MTU and
+ smaller the 64 bytes */
+ cvmx_pip_frm_len_chkx_t frm_len_chk;
+ frm_len_chk.u64 = 0;
+ frm_len_chk.s.minlen = 64;
+ frm_len_chk.s.maxlen = max_packet;
+ cvmx_write_csr(CVMX_PIP_FRM_LEN_CHKX(interface), frm_len_chk.u64);
+ }
+ /* Set the hardware to truncate packets larger than the MTU. The
+ jabber register must be set to a multiple of 8 bytes, so round up */
+ cvmx_write_csr(CVMX_GMXX_RXX_JABBER(index, interface), (max_packet + 7) & ~7u);
+ }
+ return 0;
+}
+
+
+/**
+ * Per network device initialization
+ *
+ * @param dev Device to initialize
+ * @return Zero on success
+ */
+int cvm_oct_common_init(struct ifnet *ifp)
+{
+ static int count;
+ char mac[6] = {
+ octeon_bootinfo->mac_addr_base[0],
+ octeon_bootinfo->mac_addr_base[1],
+ octeon_bootinfo->mac_addr_base[2],
+ octeon_bootinfo->mac_addr_base[3],
+ octeon_bootinfo->mac_addr_base[4],
+ octeon_bootinfo->mac_addr_base[5] + count};
+ cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
+
+ /* Force the interface to use the POW send if always_use_pow was
+ specified or it is in the pow send list */
+ if ((pow_send_group != -1) && (always_use_pow || strstr(pow_send_list, if_name(ifp))))
+ priv->queue = -1;
+
+ ifp->if_mtu = ETHERMTU;
+
+ count++;
+
+#if 0
+ ifp->get_stats = cvm_oct_common_get_stats;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ ifp->poll_controller = cvm_oct_poll_controller;
+#endif
+#endif
+
+ cvm_oct_mdio_setup_device(ifp);
+
+ cvm_oct_common_set_mac_address(ifp, mac);
+ cvm_oct_common_change_mtu(ifp, ifp->if_mtu);
+
+#if 0
+ /* Zero out stats for port so we won't mistakenly show counters from the
+ bootloader */
+ memset(ifp->get_stats(ifp), 0, sizeof(struct ifnet_stats));
+#endif
+
+ /*
+ * Do any last-minute board-specific initialization.
+ */
+ switch (cvmx_sysinfo_get()->board_type) {
+#if defined(OCTEON_VENDOR_LANNER)
+ case CVMX_BOARD_TYPE_CUST_LANNER_MR320:
+ if (priv->phy_id == 16)
+ cvm_oct_mv88e61xx_setup_device(ifp);
+ break;
+#endif
+ default:
+ break;
+ }
+
+ device_attach(priv->dev);
+
+ return 0;
+}
+
+void cvm_oct_common_uninit(struct ifnet *ifp)
+{
+ /* Currently nothing to do */
+}
+
diff --git a/sys/mips/cavium/octe/ethernet-common.h b/sys/mips/cavium/octe/ethernet-common.h
new file mode 100644
index 000000000000..ef6e4aa06da2
--- /dev/null
+++ b/sys/mips/cavium/octe/ethernet-common.h
@@ -0,0 +1,52 @@
+/*************************************************************************
+Copyright (c) 2003-2007 Cavium Networks (support@cavium.com). All rights
+reserved.
+
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * 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.
+
+ * Neither the name of Cavium Networks nor the names of
+ its contributors may be used to endorse or promote products
+ derived from this software without specific prior written
+ permission.
+
+This Software, including technical data, may be subject to U.S. export control laws, including the U.S. Export Administration Act and its associated regulations, and may be subject to export or import regulations in other countries.
+
+TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+
+*************************************************************************/
+/* $FreeBSD$ */
+
+int cvm_oct_common_init(struct ifnet *ifp);
+void cvm_oct_common_uninit(struct ifnet *ifp);
+
+int cvm_oct_common_change_mtu(struct ifnet *ifp, int new_mtu);
+void cvm_oct_common_set_multicast_list(struct ifnet *ifp);
+void cvm_oct_common_set_mac_address(struct ifnet *ifp, const void *);
+
+int cvm_oct_init_module(device_t);
+void cvm_oct_cleanup_module(void);
+
+/*
+ * XXX/juli
+ * These belong elsewhere but we can't stomach the nested extern.
+ */
+int cvm_oct_rgmii_init(struct ifnet *ifp);
+void cvm_oct_rgmii_uninit(struct ifnet *ifp);
+int cvm_oct_sgmii_init(struct ifnet *ifp);
+void cvm_oct_sgmii_uninit(struct ifnet *ifp);
+int cvm_oct_spi_init(struct ifnet *ifp);
+void cvm_oct_spi_uninit(struct ifnet *ifp);
+int cvm_oct_xaui_init(struct ifnet *ifp);
+void cvm_oct_xaui_uninit(struct ifnet *ifp);
diff --git a/sys/mips/cavium/octe/ethernet-defines.h b/sys/mips/cavium/octe/ethernet-defines.h
new file mode 100644
index 000000000000..64babf5a9614
--- /dev/null
+++ b/sys/mips/cavium/octe/ethernet-defines.h
@@ -0,0 +1,101 @@
+/*************************************************************************
+Copyright (c) 2003-2007 Cavium Networks (support@cavium.com). All rights
+reserved.
+
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * 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.
+
+ * Neither the name of Cavium Networks nor the names of
+ its contributors may be used to endorse or promote products
+ derived from this software without specific prior written
+ permission.
+
+This Software, including technical data, may be subject to U.S. export control laws, including the U.S. Export Administration Act and its associated regulations, and may be subject to export or import regulations in other countries.
+
+TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+
+*************************************************************************/
+/* $FreeBSD$ */
+
+/*
+ * A few defines are used to control the operation of this driver:
+ * CONFIG_CAVIUM_RESERVE32
+ * This kernel config options controls the amount of memory configured
+ * in a wired TLB entry for all processes to share. If this is set, the
+ * driver will use this memory instead of kernel memory for pools. This
+ * allows 32bit userspace application to access the buffers, but also
+ * requires all received packets to be copied.
+ * CONFIG_CAVIUM_OCTEON_NUM_PACKET_BUFFERS
+ * This kernel config option allows the user to control the number of
+ * packet and work queue buffers allocated by the driver. If this is zero,
+ * the driver uses the default from below.
+ * USE_MBUFS_IN_HW
+ * Tells the driver to populate the packet buffers with kernel mbufs.
+ * This allows the driver to receive packets without copying them. It also
+ * means that 32bit userspace can't access the packet buffers.
+ * USE_32BIT_SHARED
+ * This define tells the driver to allocate memory for buffers from the
+ * 32bit sahred region instead of the kernel memory space.
+ * USE_HW_TCPUDP_CHECKSUM
+ * Controls if the Octeon TCP/UDP checksum engine is used for packet
+ * output. If this is zero, the kernel will perform the checksum in
+ * software.
+ * USE_MULTICORE_RECEIVE
+ * Process receive interrupts on multiple cores. This spreads the network
+ * load across the first 8 processors. If ths is zero, only one core
+ * processes incomming packets.
+ * USE_ASYNC_IOBDMA
+ * Use asynchronous IO access to hardware. This uses Octeon's asynchronous
+ * IOBDMAs to issue IO accesses without stalling. Set this to zero
+ * to disable this. Note that IOBDMAs require CVMSEG.
+ */
+#ifndef CONFIG_CAVIUM_RESERVE32
+#define CONFIG_CAVIUM_RESERVE32 0
+#endif
+
+#if CONFIG_CAVIUM_RESERVE32
+ #define USE_32BIT_SHARED 1
+ #define USE_MBUFS_IN_HW 0
+#else
+ #define USE_32BIT_SHARED 0
+ #define USE_MBUFS_IN_HW 1
+#endif
+
+#define INTERRUPT_LIMIT 10000 /* Max interrupts per second per core */
+/*#define INTERRUPT_LIMIT 0 *//* Don't limit the number of interrupts */
+#define USE_HW_TCPUDP_CHECKSUM 1
+#define USE_MULTICORE_RECEIVE 1
+#define USE_RED 1 /* Enable Random Early Dropping under load */
+#if 0
+#define USE_ASYNC_IOBDMA (CONFIG_CAVIUM_OCTEON_CVMSEG_SIZE > 0)
+#else
+#define USE_ASYNC_IOBDMA 0
+#endif
+#define USE_10MBPS_PREAMBLE_WORKAROUND 1 /* Allow SW based preamble removal at 10Mbps to workaround PHYs giving us bad preambles */
+#define DONT_WRITEBACK(x) (x) /* Use this to have all FPA frees also tell the L2 not to write data to memory */
+/*#define DONT_WRITEBACK(x) 0 *//* Use this to not have FPA frees control L2 */
+
+#define MAX_RX_PACKETS 120 /* Maximum number of packets to process per interrupt. */
+#define MAX_OUT_QUEUE_DEPTH 1000
+
+#ifndef SMP
+#undef USE_MULTICORE_RECEIVE
+#define USE_MULTICORE_RECEIVE 0
+#endif
+
+#define IP_PROTOCOL_TCP 6
+#define IP_PROTOCOL_UDP 0x11
+#define FAU_NUM_PACKET_BUFFERS_TO_FREE (CVMX_FAU_REG_END - sizeof(uint32_t))
+#define TOTAL_NUMBER_OF_PORTS (CVMX_PIP_NUM_INPUT_PORTS+1)
+
diff --git a/sys/mips/cavium/octe/ethernet-headers.h b/sys/mips/cavium/octe/ethernet-headers.h
new file mode 100644
index 000000000000..3945e13ac678
--- /dev/null
+++ b/sys/mips/cavium/octe/ethernet-headers.h
@@ -0,0 +1,50 @@
+/*************************************************************************
+Copyright (c) 2003-2007 Cavium Networks (support@cavium.com). All rights
+reserved.
+
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * 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.
+
+ * Neither the name of Cavium Networks nor the names of
+ its contributors may be used to endorse or promote products
+ derived from this software without specific prior written
+ permission.
+
+This Software, including technical data, may be subject to U.S. export control laws, including the U.S. Export Administration Act and its associated regulations, and may be subject to export or import regulations in other countries.
+
+TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+
+*************************************************************************/
+/* $FreeBSD$ */
+
+#ifndef __ETHERNET_HEADERS_H__
+#define __ETHERNET_HEADERS_H__
+
+#include "cavium-ethernet.h"
+#include "ethernet-common.h"
+#include "ethernet-defines.h"
+#include "ethernet-mdio.h"
+#include "ethernet-mem.h"
+#include "ethernet-rx.h"
+#include "ethernet-tx.h"
+#include "ethernet-util.h"
+
+/*
+ * Any board- or vendor-specific includes.
+ */
+#ifdef OCTEON_VENDOR_LANNER
+#include "ethernet-mv88e61xx.h"
+#endif
+
+#endif
diff --git a/sys/mips/cavium/octe/ethernet-mdio.c b/sys/mips/cavium/octe/ethernet-mdio.c
new file mode 100644
index 000000000000..19b4d43c70dd
--- /dev/null
+++ b/sys/mips/cavium/octe/ethernet-mdio.c
@@ -0,0 +1,141 @@
+/*************************************************************************
+Copyright (c) 2003-2007 Cavium Networks (support@cavium.com). All rights
+reserved.
+
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * 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.
+
+ * Neither the name of Cavium Networks nor the names of
+ its contributors may be used to endorse or promote products
+ derived from this software without specific prior written
+ permission.
+
+This Software, including technical data, may be subject to U.S. export control laws, including the U.S. Export Administration Act and its associated regulations, and may be subject to export or import regulations in other countries.
+
+TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+
+*************************************************************************/
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+
+#include <net/ethernet.h>
+#include <net/if.h>
+
+#include "wrapper-cvmx-includes.h"
+#include "ethernet-headers.h"
+
+struct mtx cvm_oct_mdio_mtx;
+MTX_SYSINIT(cvm_oct_mdio, &cvm_oct_mdio_mtx, "MDIO", MTX_DEF);
+
+/**
+ * Perform an MII read. Called by the generic MII routines
+ *
+ * @param dev Device to perform read for
+ * @param phy_id The MII phy id
+ * @param location Register location to read
+ * @return Result from the read or zero on failure
+ */
+int cvm_oct_mdio_read(struct ifnet *ifp, int phy_id, int location)
+{
+ cvmx_smi_cmd_t smi_cmd;
+ cvmx_smi_rd_dat_t smi_rd;
+
+ MDIO_LOCK();
+ smi_cmd.u64 = 0;
+ smi_cmd.s.phy_op = 1;
+ smi_cmd.s.phy_adr = phy_id;
+ smi_cmd.s.reg_adr = location;
+ cvmx_write_csr(CVMX_SMI_CMD, smi_cmd.u64);
+
+ do {
+#if 0
+ if (!in_interrupt())
+ yield();
+#endif
+ smi_rd.u64 = cvmx_read_csr(CVMX_SMI_RD_DAT);
+ } while (smi_rd.s.pending);
+
+ MDIO_UNLOCK();
+
+ if (smi_rd.s.val)
+ return smi_rd.s.dat;
+ else
+ return 0;
+}
+
+
+/**
+ * Perform an MII write. Called by the generic MII routines
+ *
+ * @param dev Device to perform write for
+ * @param phy_id The MII phy id
+ * @param location Register location to write
+ * @param val Value to write
+ */
+void cvm_oct_mdio_write(struct ifnet *ifp, int phy_id, int location, int val)
+{
+ cvmx_smi_cmd_t smi_cmd;
+ cvmx_smi_wr_dat_t smi_wr;
+
+ MDIO_LOCK();
+ smi_wr.u64 = 0;
+ smi_wr.s.dat = val;
+ cvmx_write_csr(CVMX_SMI_WR_DAT, smi_wr.u64);
+
+ smi_cmd.u64 = 0;
+ smi_cmd.s.phy_op = 0;
+ smi_cmd.s.phy_adr = phy_id;
+ smi_cmd.s.reg_adr = location;
+ cvmx_write_csr(CVMX_SMI_CMD, smi_cmd.u64);
+
+ do {
+#if 0
+ if (!in_interrupt())
+ yield();
+#endif
+ smi_wr.u64 = cvmx_read_csr(CVMX_SMI_WR_DAT);
+ } while (smi_wr.s.pending);
+ MDIO_UNLOCK();
+}
+
+/**
+ * Setup the MDIO device structures
+ *
+ * @param dev Device to setup
+ *
+ * @return Zero on success, negative on failure
+ */
+int cvm_oct_mdio_setup_device(struct ifnet *ifp)
+{
+ cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
+
+ priv->phy_id = cvmx_helper_board_get_mii_address(priv->port);
+ priv->phy_device = NULL;
+ priv->mdio_read = NULL;
+ priv->mdio_write = NULL;
+
+ return 0;
+}
+
diff --git a/sys/mips/cavium/octe/ethernet-mdio.h b/sys/mips/cavium/octe/ethernet-mdio.h
new file mode 100644
index 000000000000..c2c4ec69230e
--- /dev/null
+++ b/sys/mips/cavium/octe/ethernet-mdio.h
@@ -0,0 +1,40 @@
+/*************************************************************************
+Copyright (c) 2003-2007 Cavium Networks (support@cavium.com). All rights
+reserved.
+
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * 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.
+
+ * Neither the name of Cavium Networks nor the names of
+ its contributors may be used to endorse or promote products
+ derived from this software without specific prior written
+ permission.
+
+This Software, including technical data, may be subject to U.S. export control laws, including the U.S. Export Administration Act and its associated regulations, and may be subject to export or import regulations in other countries.
+
+TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+
+*************************************************************************/
+/* $FreeBSD$ */
+
+extern struct mtx cvm_oct_mdio_mtx;
+
+#define MDIO_LOCK() mtx_lock(&cvm_oct_mdio_mtx)
+#define MDIO_UNLOCK() mtx_unlock(&cvm_oct_mdio_mtx)
+#define MDIO_TRYLOCK() mtx_trylock(&cvm_oct_mdio_mtx)
+
+int cvm_oct_mdio_read(struct ifnet *ifp, int phy_id, int location);
+void cvm_oct_mdio_write(struct ifnet *ifp, int phy_id, int location, int val);
+int cvm_oct_mdio_setup_device(struct ifnet *ifp);
+
diff --git a/sys/mips/cavium/octe/ethernet-mem.c b/sys/mips/cavium/octe/ethernet-mem.c
new file mode 100644
index 000000000000..5d2fa5462162
--- /dev/null
+++ b/sys/mips/cavium/octe/ethernet-mem.c
@@ -0,0 +1,207 @@
+/*************************************************************************
+Copyright (c) 2003-2007 Cavium Networks (support@cavium.com). All rights
+reserved.
+
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * 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.
+
+ * Neither the name of Cavium Networks nor the names of
+ its contributors may be used to endorse or promote products
+ derived from this software without specific prior written
+ permission.
+
+This Software, including technical data, may be subject to U.S. export control laws, including the U.S. Export Administration Act and its associated regulations, and may be subject to export or import regulations in other countries.
+
+TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+
+*************************************************************************/
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+
+#include <net/ethernet.h>
+#include <net/if.h>
+
+#include "wrapper-cvmx-includes.h"
+#include "ethernet-headers.h"
+
+/**
+ * Fill the supplied hardware pool with mbufs
+ *
+ * @param pool Pool to allocate an mbuf for
+ * @param size Size of the buffer needed for the pool
+ * @param elements Number of buffers to allocate
+ */
+static int cvm_oct_fill_hw_mbuf(int pool, int size, int elements)
+{
+ int freed = elements;
+ while (freed) {
+ KASSERT(size <= MCLBYTES - 128, ("mbuf clusters are too small"));
+
+ struct mbuf *m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
+ if (__predict_false(m == NULL)) {
+ printf("Failed to allocate mbuf for hardware pool %d\n", pool);
+ break;
+ }
+
+ m->m_data += 128 - (((uintptr_t)m->m_data) & 0x7f);
+ *(struct mbuf **)(m->m_data - sizeof(void *)) = m;
+ cvmx_fpa_free(m->m_data, pool, DONT_WRITEBACK(size/128));
+ freed--;
+ }
+ return (elements - freed);
+}
+
+
+/**
+ * Free the supplied hardware pool of mbufs
+ *
+ * @param pool Pool to allocate an mbuf for
+ * @param size Size of the buffer needed for the pool
+ * @param elements Number of buffers to allocate
+ */
+static void cvm_oct_free_hw_mbuf(int pool, int size, int elements)
+{
+ char *memory;
+
+ do {
+ memory = cvmx_fpa_alloc(pool);
+ if (memory) {
+ struct mbuf *m = *(struct mbuf **)(memory - sizeof(void *));
+ elements--;
+ m_freem(m);
+ }
+ } while (memory);
+
+ if (elements < 0)
+ printf("Warning: Freeing of pool %u had too many mbufs (%d)\n", pool, elements);
+ else if (elements > 0)
+ printf("Warning: Freeing of pool %u is missing %d mbufs\n", pool, elements);
+}
+
+
+/**
+ * This function fills a hardware pool with memory. Depending
+ * on the config defines, this memory might come from the
+ * kernel or global 32bit memory allocated with
+ * cvmx_bootmem_alloc.
+ *
+ * @param pool Pool to populate
+ * @param size Size of each buffer in the pool
+ * @param elements Number of buffers to allocate
+ */
+static int cvm_oct_fill_hw_memory(int pool, int size, int elements)
+{
+ char *memory;
+ int freed = elements;
+
+ if (USE_32BIT_SHARED) {
+#if 0
+ extern uint64_t octeon_reserve32_memory;
+
+ memory = cvmx_bootmem_alloc_range(elements*size, 128, octeon_reserve32_memory,
+ octeon_reserve32_memory + (CONFIG_CAVIUM_RESERVE32<<20) - 1);
+ if (memory == NULL)
+ panic("Unable to allocate %u bytes for FPA pool %d\n", elements*size, pool);
+
+ printf("Memory range %p - %p reserved for hardware\n", memory, memory + elements*size - 1);
+
+ while (freed) {
+ cvmx_fpa_free(memory, pool, 0);
+ memory += size;
+ freed--;
+ }
+#else
+ panic("%s: may need to implement using shared memory.", __func__);
+#endif
+ } else {
+ while (freed) {
+ /* We need to force alignment to 128 bytes here */
+#if 0
+ memory = kmalloc(size + 127, GFP_ATOMIC);
+#else
+ panic("%s: not yet implemented.", __func__);
+#endif
+ if (__predict_false(memory == NULL)) {
+ printf("Unable to allocate %u bytes for FPA pool %d\n", elements*size, pool);
+ break;
+ }
+ memory = (char *)(((unsigned long)memory+127) & -128);
+ cvmx_fpa_free(memory, pool, 0);
+ freed--;
+ }
+ }
+ return (elements - freed);
+}
+
+
+/**
+ * Free memory previously allocated with cvm_oct_fill_hw_memory
+ *
+ * @param pool FPA pool to free
+ * @param size Size of each buffer in the pool
+ * @param elements Number of buffers that should be in the pool
+ */
+static void cvm_oct_free_hw_memory(int pool, int size, int elements)
+{
+ if (USE_32BIT_SHARED) {
+ printf("Warning: 32 shared memory is not freeable\n");
+ } else {
+ char *memory;
+ do {
+ memory = cvmx_fpa_alloc(pool);
+ if (memory) {
+ elements--;
+#if 0
+ kfree(phys_to_virt(cvmx_ptr_to_phys(memory)));
+#else
+ panic("%s: not yet implemented.", __func__);
+#endif
+ }
+ } while (memory);
+
+ if (elements < 0)
+ printf("Freeing of pool %u had too many buffers (%d)\n", pool, elements);
+ else if (elements > 0)
+ printf("Warning: Freeing of pool %u is missing %d buffers\n", pool, elements);
+ }
+}
+
+
+int cvm_oct_mem_fill_fpa(int pool, int size, int elements)
+{
+ int freed;
+ if (USE_MBUFS_IN_HW)
+ freed = cvm_oct_fill_hw_mbuf(pool, size, elements);
+ else
+ freed = cvm_oct_fill_hw_memory(pool, size, elements);
+ return (freed);
+}
+
+void cvm_oct_mem_empty_fpa(int pool, int size, int elements)
+{
+ if (USE_MBUFS_IN_HW)
+ cvm_oct_free_hw_mbuf(pool, size, elements);
+ else
+ cvm_oct_free_hw_memory(pool, size, elements);
+}
+
diff --git a/sys/mips/cavium/octe/ethernet-mem.h b/sys/mips/cavium/octe/ethernet-mem.h
new file mode 100644
index 000000000000..d9058d9b18c6
--- /dev/null
+++ b/sys/mips/cavium/octe/ethernet-mem.h
@@ -0,0 +1,33 @@
+/*************************************************************************
+Copyright (c) 2003-2007 Cavium Networks (support@cavium.com). All rights
+reserved.
+
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * 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.
+
+ * Neither the name of Cavium Networks nor the names of
+ its contributors may be used to endorse or promote products
+ derived from this software without specific prior written
+ permission.
+
+This Software, including technical data, may be subject to U.S. export control laws, including the U.S. Export Administration Act and its associated regulations, and may be subject to export or import regulations in other countries.
+
+TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+
+*************************************************************************/
+/* $FreeBSD$ */
+
+int cvm_oct_mem_fill_fpa(int pool, int size, int elements);
+void cvm_oct_mem_empty_fpa(int pool, int size, int elements);
+
diff --git a/sys/mips/cavium/octe/ethernet-mv88e61xx.c b/sys/mips/cavium/octe/ethernet-mv88e61xx.c
new file mode 100644
index 000000000000..31dbd74ecd25
--- /dev/null
+++ b/sys/mips/cavium/octe/ethernet-mv88e61xx.c
@@ -0,0 +1,127 @@
+/*-
+ * Copyright (c) 2010 Juli Mallett <jmallett@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.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Interface to the Marvell 88E61XX SMI/MDIO.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+
+#include <dev/mii/mii.h>
+
+#include <net/ethernet.h>
+#include <net/if.h>
+
+#include "wrapper-cvmx-includes.h"
+#include "ethernet-headers.h"
+
+#define MV88E61XX_SMI_REG_CMD 0x00 /* Indirect command register. */
+#define MV88E61XX_SMI_CMD_BUSY 0x8000 /* Busy bit. */
+#define MV88E61XX_SMI_CMD_22 0x1000 /* Clause 22 (default 45.) */
+#define MV88E61XX_SMI_CMD_READ 0x0800 /* Read command. */
+#define MV88E61XX_SMI_CMD_WRITE 0x0400 /* Write command. */
+#define MV88E61XX_SMI_CMD_PHY(phy) (((phy) & 0x1f) << 5)
+#define MV88E61XX_SMI_CMD_REG(reg) ((reg) & 0x1f)
+
+#define MV88E61XX_SMI_REG_DAT 0x01 /* Indirect data register. */
+
+static int cvm_oct_mv88e61xx_smi_read(struct ifnet *, int, int);
+static void cvm_oct_mv88e61xx_smi_write(struct ifnet *, int, int, int);
+static int cvm_oct_mv88e61xx_smi_wait(struct ifnet *);
+
+int
+cvm_oct_mv88e61xx_setup_device(struct ifnet *ifp)
+{
+ cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
+
+ priv->mdio_read = cvm_oct_mv88e61xx_smi_read;
+ priv->mdio_write = cvm_oct_mv88e61xx_smi_write;
+ priv->phy_device = "mv88e61xxphy";
+
+ return (0);
+}
+
+static int
+cvm_oct_mv88e61xx_smi_read(struct ifnet *ifp, int phy_id, int location)
+{
+ cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
+ int error;
+
+ error = cvm_oct_mv88e61xx_smi_wait(ifp);
+ if (error != 0)
+ return (0);
+
+ cvm_oct_mdio_write(ifp, priv->phy_id, MV88E61XX_SMI_REG_CMD,
+ MV88E61XX_SMI_CMD_BUSY | MV88E61XX_SMI_CMD_22 |
+ MV88E61XX_SMI_CMD_READ | MV88E61XX_SMI_CMD_PHY(phy_id) |
+ MV88E61XX_SMI_CMD_REG(location));
+
+ error = cvm_oct_mv88e61xx_smi_wait(ifp);
+ if (error != 0)
+ return (0);
+
+ return (cvm_oct_mdio_read(ifp, priv->phy_id, MV88E61XX_SMI_REG_DAT));
+}
+
+static void
+cvm_oct_mv88e61xx_smi_write(struct ifnet *ifp, int phy_id, int location, int val)
+{
+ cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
+
+ cvm_oct_mv88e61xx_smi_wait(ifp);
+ cvm_oct_mdio_write(ifp, priv->phy_id, MV88E61XX_SMI_REG_DAT, val);
+ cvm_oct_mdio_write(ifp, priv->phy_id, MV88E61XX_SMI_REG_CMD,
+ MV88E61XX_SMI_CMD_BUSY | MV88E61XX_SMI_CMD_22 |
+ MV88E61XX_SMI_CMD_WRITE | MV88E61XX_SMI_CMD_PHY(phy_id) |
+ MV88E61XX_SMI_CMD_REG(location));
+ cvm_oct_mv88e61xx_smi_wait(ifp);
+}
+
+static int
+cvm_oct_mv88e61xx_smi_wait(struct ifnet *ifp)
+{
+ cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
+ uint16_t cmd;
+ unsigned i;
+
+ for (i = 0; i < 10000; i++) {
+ cmd = cvm_oct_mdio_read(ifp, priv->phy_id, MV88E61XX_SMI_REG_CMD);
+ if ((cmd & MV88E61XX_SMI_CMD_BUSY) == 0)
+ return (0);
+ }
+ return (ETIMEDOUT);
+}
diff --git a/sys/mips/cavium/octe/ethernet-mv88e61xx.h b/sys/mips/cavium/octe/ethernet-mv88e61xx.h
new file mode 100644
index 000000000000..b61c05494d98
--- /dev/null
+++ b/sys/mips/cavium/octe/ethernet-mv88e61xx.h
@@ -0,0 +1,34 @@
+/*-
+ * Copyright (c) 2010 Juli Mallett <jmallett@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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _CAVIUM_OCTE_ETHERNET_MV88E61XX_H_
+#define _CAVIUM_OCTE_ETHERNET_MV88E61XX_H_
+
+int cvm_oct_mv88e61xx_setup_device(struct ifnet *ifp);
+
+#endif /* !_CAVIUM_OCTE_ETHERNET_MV88E61XX_H_ */
diff --git a/sys/mips/cavium/octe/ethernet-rgmii.c b/sys/mips/cavium/octe/ethernet-rgmii.c
new file mode 100644
index 000000000000..ac55de8a705b
--- /dev/null
+++ b/sys/mips/cavium/octe/ethernet-rgmii.c
@@ -0,0 +1,340 @@
+/*************************************************************************
+Copyright (c) 2003-2007 Cavium Networks (support@cavium.com). All rights
+reserved.
+
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * 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.
+
+ * Neither the name of Cavium Networks nor the names of
+ its contributors may be used to endorse or promote products
+ derived from this software without specific prior written
+ permission.
+
+This Software, including technical data, may be subject to U.S. export control laws, including the U.S. Export Administration Act and its associated regulations, and may be subject to export or import regulations in other countries.
+
+TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+
+*************************************************************************/
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
+#include <sys/mbuf.h>
+#include <sys/rman.h>
+#include <sys/socket.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+
+#include <net/ethernet.h>
+#include <net/if.h>
+
+#include "wrapper-cvmx-includes.h"
+#include "ethernet-headers.h"
+
+#include "octebusvar.h"
+
+extern int octeon_is_simulation(void);
+extern struct ifnet *cvm_oct_device[];
+
+static struct mtx global_register_lock;
+MTX_SYSINIT(global_register_lock, &global_register_lock,
+ "RGMII Global", MTX_SPIN);
+
+static int number_rgmii_ports;
+
+static void cvm_oct_rgmii_poll(struct ifnet *ifp)
+{
+ cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
+ cvmx_helper_link_info_t link_info;
+
+ /* Take the global register lock since we are going to touch
+ registers that affect more than one port */
+ mtx_lock_spin(&global_register_lock);
+
+ link_info = cvmx_helper_link_get(priv->port);
+ if (link_info.u64 == priv->link_info) {
+
+ /* If the 10Mbps preamble workaround is supported and we're
+ at 10Mbps we may need to do some special checking */
+ if (USE_10MBPS_PREAMBLE_WORKAROUND && (link_info.s.speed == 10)) {
+
+ /* Read the GMXX_RXX_INT_REG[PCTERR] bit and
+ see if we are getting preamble errors */
+ int interface = INTERFACE(priv->port);
+ int index = INDEX(priv->port);
+ cvmx_gmxx_rxx_int_reg_t gmxx_rxx_int_reg;
+ gmxx_rxx_int_reg.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_REG(index, interface));
+ if (gmxx_rxx_int_reg.s.pcterr) {
+
+ /* We are getting preamble errors at 10Mbps.
+ Most likely the PHY is giving us packets
+ with mis aligned preambles. In order to get
+ these packets we need to disable preamble
+ checking and do it in software */
+ cvmx_gmxx_rxx_frm_ctl_t gmxx_rxx_frm_ctl;
+ cvmx_ipd_sub_port_fcs_t ipd_sub_port_fcs;
+
+ /* Disable preamble checking */
+ gmxx_rxx_frm_ctl.u64 = cvmx_read_csr(CVMX_GMXX_RXX_FRM_CTL(index, interface));
+ gmxx_rxx_frm_ctl.s.pre_chk = 0;
+ cvmx_write_csr(CVMX_GMXX_RXX_FRM_CTL(index, interface), gmxx_rxx_frm_ctl.u64);
+
+ /* Disable FCS stripping */
+ ipd_sub_port_fcs.u64 = cvmx_read_csr(CVMX_IPD_SUB_PORT_FCS);
+ ipd_sub_port_fcs.s.port_bit &= 0xffffffffull ^ (1ull<<priv->port);
+ cvmx_write_csr(CVMX_IPD_SUB_PORT_FCS, ipd_sub_port_fcs.u64);
+
+ /* Clear any error bits */
+ cvmx_write_csr(CVMX_GMXX_RXX_INT_REG(index, interface), gmxx_rxx_int_reg.u64);
+ DEBUGPRINT("%s: Using 10Mbps with software preamble removal\n", if_name(ifp));
+ }
+ }
+ mtx_unlock_spin(&global_register_lock);
+ return;
+ }
+
+ /* If the 10Mbps preamble workaround is allowed we need to on
+ preamble checking, FCS stripping, and clear error bits on
+ every speed change. If errors occur during 10Mbps operation
+ the above code will change this stuff */
+ if (USE_10MBPS_PREAMBLE_WORKAROUND) {
+
+ cvmx_gmxx_rxx_frm_ctl_t gmxx_rxx_frm_ctl;
+ cvmx_ipd_sub_port_fcs_t ipd_sub_port_fcs;
+ cvmx_gmxx_rxx_int_reg_t gmxx_rxx_int_reg;
+ int interface = INTERFACE(priv->port);
+ int index = INDEX(priv->port);
+
+ /* Enable preamble checking */
+ gmxx_rxx_frm_ctl.u64 = cvmx_read_csr(CVMX_GMXX_RXX_FRM_CTL(index, interface));
+ gmxx_rxx_frm_ctl.s.pre_chk = 1;
+ cvmx_write_csr(CVMX_GMXX_RXX_FRM_CTL(index, interface), gmxx_rxx_frm_ctl.u64);
+ /* Enable FCS stripping */
+ ipd_sub_port_fcs.u64 = cvmx_read_csr(CVMX_IPD_SUB_PORT_FCS);
+ ipd_sub_port_fcs.s.port_bit |= 1ull<<priv->port;
+ cvmx_write_csr(CVMX_IPD_SUB_PORT_FCS, ipd_sub_port_fcs.u64);
+ /* Clear any error bits */
+ gmxx_rxx_int_reg.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_REG(index, interface));
+ cvmx_write_csr(CVMX_GMXX_RXX_INT_REG(index, interface), gmxx_rxx_int_reg.u64);
+ }
+
+ link_info = cvmx_helper_link_autoconf(priv->port);
+ priv->link_info = link_info.u64;
+ priv->need_link_update = 1;
+ mtx_unlock_spin(&global_register_lock);
+}
+
+
+static int cvm_oct_rgmii_rml_interrupt(void *dev_id)
+{
+ cvmx_npi_rsl_int_blocks_t rsl_int_blocks;
+ int index;
+ int return_status = FILTER_STRAY;
+
+ rsl_int_blocks.u64 = cvmx_read_csr(CVMX_NPI_RSL_INT_BLOCKS);
+
+ /* Check and see if this interrupt was caused by the GMX0 block */
+ if (rsl_int_blocks.s.gmx0) {
+
+ int interface = 0;
+ /* Loop through every port of this interface */
+ for (index = 0; index < cvmx_helper_ports_on_interface(interface); index++) {
+
+ /* Read the GMX interrupt status bits */
+ cvmx_gmxx_rxx_int_reg_t gmx_rx_int_reg;
+ gmx_rx_int_reg.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_REG(index, interface));
+ gmx_rx_int_reg.u64 &= cvmx_read_csr(CVMX_GMXX_RXX_INT_EN(index, interface));
+ /* Poll the port if inband status changed */
+ if (gmx_rx_int_reg.s.phy_dupx || gmx_rx_int_reg.s.phy_link || gmx_rx_int_reg.s.phy_spd) {
+
+ struct ifnet *ifp = cvm_oct_device[cvmx_helper_get_ipd_port(interface, index)];
+ if (ifp)
+ cvm_oct_rgmii_poll(ifp);
+ gmx_rx_int_reg.u64 = 0;
+ gmx_rx_int_reg.s.phy_dupx = 1;
+ gmx_rx_int_reg.s.phy_link = 1;
+ gmx_rx_int_reg.s.phy_spd = 1;
+ cvmx_write_csr(CVMX_GMXX_RXX_INT_REG(index, interface), gmx_rx_int_reg.u64);
+ return_status = FILTER_HANDLED;
+ }
+ }
+ }
+
+ /* Check and see if this interrupt was caused by the GMX1 block */
+ if (rsl_int_blocks.s.gmx1) {
+
+ int interface = 1;
+ /* Loop through every port of this interface */
+ for (index = 0; index < cvmx_helper_ports_on_interface(interface); index++) {
+
+ /* Read the GMX interrupt status bits */
+ cvmx_gmxx_rxx_int_reg_t gmx_rx_int_reg;
+ gmx_rx_int_reg.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_REG(index, interface));
+ gmx_rx_int_reg.u64 &= cvmx_read_csr(CVMX_GMXX_RXX_INT_EN(index, interface));
+ /* Poll the port if inband status changed */
+ if (gmx_rx_int_reg.s.phy_dupx || gmx_rx_int_reg.s.phy_link || gmx_rx_int_reg.s.phy_spd) {
+
+ struct ifnet *ifp = cvm_oct_device[cvmx_helper_get_ipd_port(interface, index)];
+ if (ifp)
+ cvm_oct_rgmii_poll(ifp);
+ gmx_rx_int_reg.u64 = 0;
+ gmx_rx_int_reg.s.phy_dupx = 1;
+ gmx_rx_int_reg.s.phy_link = 1;
+ gmx_rx_int_reg.s.phy_spd = 1;
+ cvmx_write_csr(CVMX_GMXX_RXX_INT_REG(index, interface), gmx_rx_int_reg.u64);
+ return_status = FILTER_HANDLED;
+ }
+ }
+ }
+ return return_status;
+}
+
+
+static int cvm_oct_rgmii_open(struct ifnet *ifp)
+{
+ cvmx_gmxx_prtx_cfg_t gmx_cfg;
+ cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
+ int interface = INTERFACE(priv->port);
+ int index = INDEX(priv->port);
+ cvmx_helper_link_info_t link_info;
+
+ gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
+ gmx_cfg.s.en = 1;
+ cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64);
+
+ if (!octeon_is_simulation()) {
+ link_info = cvmx_helper_link_get(priv->port);
+ if (!link_info.s.link_up)
+ if_link_state_change(ifp, LINK_STATE_DOWN);
+ else
+ if_link_state_change(ifp, LINK_STATE_UP);
+ }
+
+ return 0;
+}
+
+static int cvm_oct_rgmii_stop(struct ifnet *ifp)
+{
+ cvmx_gmxx_prtx_cfg_t gmx_cfg;
+ cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
+ int interface = INTERFACE(priv->port);
+ int index = INDEX(priv->port);
+
+ gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
+ gmx_cfg.s.en = 0;
+ cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64);
+ return 0;
+}
+
+int cvm_oct_rgmii_init(struct ifnet *ifp)
+{
+ struct octebus_softc *sc;
+ cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
+ int error;
+ int rid;
+
+ cvm_oct_common_init(ifp);
+ priv->open = cvm_oct_rgmii_open;
+ priv->stop = cvm_oct_rgmii_stop;
+ priv->stop(ifp);
+
+ /* Due to GMX errata in CN3XXX series chips, it is necessary to take the
+ link down immediately whne the PHY changes state. In order to do this
+ we call the poll function every time the RGMII inband status changes.
+ This may cause problems if the PHY doesn't implement inband status
+ properly */
+ if (number_rgmii_ports == 0) {
+ sc = device_get_softc(device_get_parent(priv->dev));
+
+ rid = 0;
+ sc->sc_rgmii_irq = bus_alloc_resource(sc->sc_dev, SYS_RES_IRQ,
+ &rid, CVMX_IRQ_RML,
+ CVMX_IRQ_RML, 1,
+ RF_ACTIVE);
+ if (sc->sc_rgmii_irq == NULL) {
+ device_printf(sc->sc_dev, "could not allocate RGMII irq");
+ return ENXIO;
+ }
+
+ error = bus_setup_intr(sc->sc_dev, sc->sc_rgmii_irq,
+ INTR_TYPE_NET | INTR_MPSAFE,
+ cvm_oct_rgmii_rml_interrupt, NULL,
+ &number_rgmii_ports, NULL);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "could not setup RGMII irq");
+ return error;
+ }
+ }
+ number_rgmii_ports++;
+
+ /* Only true RGMII ports need to be polled. In GMII mode, port 0 is really
+ a RGMII port */
+ if (((priv->imode == CVMX_HELPER_INTERFACE_MODE_GMII) && (priv->port == 0)) ||
+ (priv->imode == CVMX_HELPER_INTERFACE_MODE_RGMII)) {
+
+ if (!octeon_is_simulation()) {
+
+ cvmx_gmxx_rxx_int_en_t gmx_rx_int_en;
+ int interface = INTERFACE(priv->port);
+ int index = INDEX(priv->port);
+
+ /* Enable interrupts on inband status changes for this port */
+ gmx_rx_int_en.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_EN(index, interface));
+ gmx_rx_int_en.s.phy_dupx = 1;
+ gmx_rx_int_en.s.phy_link = 1;
+ gmx_rx_int_en.s.phy_spd = 1;
+ cvmx_write_csr(CVMX_GMXX_RXX_INT_EN(index, interface), gmx_rx_int_en.u64);
+ priv->poll = cvm_oct_rgmii_poll;
+ }
+ }
+
+ return 0;
+}
+
+void cvm_oct_rgmii_uninit(struct ifnet *ifp)
+{
+ cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
+ cvm_oct_common_uninit(ifp);
+
+ /* Only true RGMII ports need to be polled. In GMII mode, port 0 is really
+ a RGMII port */
+ if (((priv->imode == CVMX_HELPER_INTERFACE_MODE_GMII) && (priv->port == 0)) ||
+ (priv->imode == CVMX_HELPER_INTERFACE_MODE_RGMII)) {
+
+ if (!octeon_is_simulation()) {
+
+ cvmx_gmxx_rxx_int_en_t gmx_rx_int_en;
+ int interface = INTERFACE(priv->port);
+ int index = INDEX(priv->port);
+
+ /* Disable interrupts on inband status changes for this port */
+ gmx_rx_int_en.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_EN(index, interface));
+ gmx_rx_int_en.s.phy_dupx = 0;
+ gmx_rx_int_en.s.phy_link = 0;
+ gmx_rx_int_en.s.phy_spd = 0;
+ cvmx_write_csr(CVMX_GMXX_RXX_INT_EN(index, interface), gmx_rx_int_en.u64);
+ }
+ }
+
+ /* Remove the interrupt handler when the last port is removed */
+ number_rgmii_ports--;
+ if (number_rgmii_ports == 0)
+ panic("%s: need to implement IRQ release.", __func__);
+}
+
diff --git a/sys/mips/cavium/octe/ethernet-rx.c b/sys/mips/cavium/octe/ethernet-rx.c
new file mode 100644
index 000000000000..2f8f0a8fbae0
--- /dev/null
+++ b/sys/mips/cavium/octe/ethernet-rx.c
@@ -0,0 +1,416 @@
+/*************************************************************************
+Copyright (c) 2003-2007 Cavium Networks (support@cavium.com). All rights
+reserved.
+
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * 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.
+
+ * Neither the name of Cavium Networks nor the names of
+ its contributors may be used to endorse or promote products
+ derived from this software without specific prior written
+ permission.
+
+This Software, including technical data, may be subject to U.S. export control laws, including the U.S. Export Administration Act and its associated regulations, and may be subject to export or import regulations in other countries.
+
+TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+
+*************************************************************************/
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/proc.h>
+#include <sys/sched.h>
+#include <sys/smp.h>
+#include <sys/taskqueue.h>
+
+#include <net/ethernet.h>
+#include <net/if.h>
+
+#include "wrapper-cvmx-includes.h"
+#include "ethernet-headers.h"
+
+extern int pow_receive_group;
+extern struct ifnet *cvm_oct_device[];
+
+static struct task cvm_oct_task;
+static struct taskqueue *cvm_oct_taskq;
+
+/**
+ * Interrupt handler. The interrupt occurs whenever the POW
+ * transitions from 0->1 packets in our group.
+ *
+ * @param cpl
+ * @param dev_id
+ * @param regs
+ * @return
+ */
+int cvm_oct_do_interrupt(void *dev_id)
+{
+ /* Acknowledge the interrupt */
+ if (INTERRUPT_LIMIT)
+ cvmx_write_csr(CVMX_POW_WQ_INT, 1<<pow_receive_group);
+ else
+ cvmx_write_csr(CVMX_POW_WQ_INT, 0x10001<<pow_receive_group);
+ taskqueue_enqueue(cvm_oct_taskq, &cvm_oct_task);
+ return FILTER_HANDLED;
+}
+
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+/**
+ * This is called when the kernel needs to manually poll the
+ * device. For Octeon, this is simply calling the interrupt
+ * handler. We actually poll all the devices, not just the
+ * one supplied.
+ *
+ * @param dev Device to poll. Unused
+ */
+void cvm_oct_poll_controller(struct ifnet *ifp)
+{
+ taskqueue_enqueue(cvm_oct_taskq, &cvm_oct_task);
+}
+#endif
+
+/**
+ * This is called on receive errors, and determines if the packet
+ * can be dropped early-on in cvm_oct_tasklet_rx().
+ *
+ * @param work Work queue entry pointing to the packet.
+ * @return Non-zero if the packet can be dropped, zero otherwise.
+ */
+static inline int cvm_oct_check_rcv_error(cvmx_wqe_t *work)
+{
+ if ((work->word2.snoip.err_code == 10) && (work->len <= 64)) {
+ /* Ignore length errors on min size packets. Some equipment
+ incorrectly pads packets to 64+4FCS instead of 60+4FCS.
+ Note these packets still get counted as frame errors. */
+ } else
+ if (USE_10MBPS_PREAMBLE_WORKAROUND && ((work->word2.snoip.err_code == 5) || (work->word2.snoip.err_code == 7))) {
+
+ /* We received a packet with either an alignment error or a
+ FCS error. This may be signalling that we are running
+ 10Mbps with GMXX_RXX_FRM_CTL[PRE_CHK} off. If this is the
+ case we need to parse the packet to determine if we can
+ remove a non spec preamble and generate a correct packet */
+ int interface = cvmx_helper_get_interface_num(work->ipprt);
+ int index = cvmx_helper_get_interface_index_num(work->ipprt);
+ cvmx_gmxx_rxx_frm_ctl_t gmxx_rxx_frm_ctl;
+ gmxx_rxx_frm_ctl.u64 = cvmx_read_csr(CVMX_GMXX_RXX_FRM_CTL(index, interface));
+ if (gmxx_rxx_frm_ctl.s.pre_chk == 0) {
+
+ uint8_t *ptr = cvmx_phys_to_ptr(work->packet_ptr.s.addr);
+ int i = 0;
+
+ while (i < work->len-1) {
+ if (*ptr != 0x55)
+ break;
+ ptr++;
+ i++;
+ }
+
+ if (*ptr == 0xd5) {
+ /*
+ DEBUGPRINT("Port %d received 0xd5 preamble\n", work->ipprt);
+ */
+ work->packet_ptr.s.addr += i+1;
+ work->len -= i+5;
+ } else
+ if ((*ptr & 0xf) == 0xd) {
+ /*
+ DEBUGPRINT("Port %d received 0x?d preamble\n", work->ipprt);
+ */
+ work->packet_ptr.s.addr += i;
+ work->len -= i+4;
+ for (i = 0; i < work->len; i++) {
+ *ptr = ((*ptr&0xf0)>>4) | ((*(ptr+1)&0xf)<<4);
+ ptr++;
+ }
+ } else {
+ DEBUGPRINT("Port %d unknown preamble, packet dropped\n", work->ipprt);
+ /*
+ cvmx_helper_dump_packet(work);
+ */
+ cvm_oct_free_work(work);
+ return 1;
+ }
+ }
+ } else {
+ DEBUGPRINT("Port %d receive error code %d, packet dropped\n", work->ipprt, work->word2.snoip.err_code);
+ cvm_oct_free_work(work);
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * Tasklet function that is scheduled on a core when an interrupt occurs.
+ *
+ * @param unused
+ */
+void cvm_oct_tasklet_rx(void *context, int pending)
+{
+ int coreid;
+ uint64_t old_group_mask;
+ uint64_t old_scratch;
+ int rx_count = 0;
+ int number_to_free;
+ int num_freed;
+ int packet_not_copied;
+
+ sched_pin();
+ coreid = cvmx_get_core_num();
+
+ /* Prefetch cvm_oct_device since we know we need it soon */
+ CVMX_PREFETCH(cvm_oct_device, 0);
+
+ if (USE_ASYNC_IOBDMA) {
+ /* Save scratch in case userspace is using it */
+ CVMX_SYNCIOBDMA;
+ old_scratch = cvmx_scratch_read64(CVMX_SCR_SCRATCH);
+ }
+
+ /* Only allow work for our group (and preserve priorities) */
+ old_group_mask = cvmx_read_csr(CVMX_POW_PP_GRP_MSKX(coreid));
+ cvmx_write_csr(CVMX_POW_PP_GRP_MSKX(coreid),
+ (old_group_mask & ~0xFFFFull) | 1<<pow_receive_group);
+
+ if (USE_ASYNC_IOBDMA)
+ cvmx_pow_work_request_async(CVMX_SCR_SCRATCH, CVMX_POW_NO_WAIT);
+
+ while (1) {
+ struct mbuf *m = NULL;
+ int mbuf_in_hw;
+ cvmx_wqe_t *work;
+
+ if (USE_ASYNC_IOBDMA) {
+ work = cvmx_pow_work_response_async(CVMX_SCR_SCRATCH);
+ } else {
+ if ((INTERRUPT_LIMIT == 0) || (rx_count < MAX_RX_PACKETS))
+ work = cvmx_pow_work_request_sync(CVMX_POW_NO_WAIT);
+ else
+ work = NULL;
+ }
+ CVMX_PREFETCH(work, 0);
+ if (work == NULL)
+ break;
+
+ /* Limit each core to processing MAX_RX_PACKETS packets without a break.
+ This way the RX can't starve the TX task. */
+ if (USE_ASYNC_IOBDMA) {
+
+ if ((INTERRUPT_LIMIT == 0) || (rx_count < MAX_RX_PACKETS))
+ cvmx_pow_work_request_async_nocheck(CVMX_SCR_SCRATCH, CVMX_POW_NO_WAIT);
+ else {
+ cvmx_scratch_write64(CVMX_SCR_SCRATCH, 0x8000000000000000ull);
+ cvmx_pow_tag_sw_null_nocheck();
+ }
+ }
+
+ mbuf_in_hw = USE_MBUFS_IN_HW && work->word2.s.bufs == 1;
+ if ((mbuf_in_hw)) {
+ m = *(struct mbuf **)(cvm_oct_get_buffer_ptr(work->packet_ptr) - sizeof(void *));
+ CVMX_PREFETCH(m, offsetof(struct mbuf, m_data));
+ CVMX_PREFETCH(m, offsetof(struct mbuf, m_pkthdr));
+ }
+ CVMX_PREFETCH(cvm_oct_device[work->ipprt], 0);
+ //CVMX_PREFETCH(m, 0);
+
+
+ rx_count++;
+ /* Immediately throw away all packets with receive errors */
+ if ((work->word2.snoip.rcv_error)) {
+ if (cvm_oct_check_rcv_error(work))
+ continue;
+ }
+
+ /* We can only use the zero copy path if mbufs are in the FPA pool
+ and the packet fits in a single buffer */
+ if ((mbuf_in_hw)) {
+ CVMX_PREFETCH(m->m_data, 0);
+
+ m->m_pkthdr.len = m->m_len = work->len;
+
+ packet_not_copied = 1;
+
+ /*
+ * Adjust the data pointer based on the offset
+ * of the packet within the buffer.
+ */
+ m->m_data += (work->packet_ptr.s.back << 7) + (work->packet_ptr.s.addr & 0x7f);
+ } else {
+
+ /* We have to copy the packet. First allocate an
+ mbuf for it */
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL) {
+ DEBUGPRINT("Port %d failed to allocate mbuf, packet dropped\n", work->ipprt);
+ cvm_oct_free_work(work);
+ continue;
+ }
+
+ /* Check if we've received a packet that was entirely
+ stored in the work entry. This is untested */
+ if ((work->word2.s.bufs == 0)) {
+ uint8_t *ptr = work->packet_data;
+
+ if (cvmx_likely(!work->word2.s.not_IP)) {
+ /* The beginning of the packet moves
+ for IP packets */
+ if (work->word2.s.is_v6)
+ ptr += 2;
+ else
+ ptr += 6;
+ }
+ panic("%s: not yet implemented; copy in small packet.", __func__);
+ /* No packet buffers to free */
+ } else {
+ int segments = work->word2.s.bufs;
+ cvmx_buf_ptr_t segment_ptr = work->packet_ptr;
+ int len = work->len;
+
+ while (segments--) {
+ cvmx_buf_ptr_t next_ptr = *(cvmx_buf_ptr_t *)cvmx_phys_to_ptr(segment_ptr.s.addr-8);
+ /* Octeon Errata PKI-100: The segment
+ size is wrong. Until it is fixed,
+ calculate the segment size based on
+ the packet pool buffer size. When
+ it is fixed, the following line
+ should be replaced with this one:
+ int segment_size = segment_ptr.s.size; */
+ int segment_size = CVMX_FPA_PACKET_POOL_SIZE - (segment_ptr.s.addr - (((segment_ptr.s.addr >> 7) - segment_ptr.s.back) << 7));
+ /* Don't copy more than what is left
+ in the packet */
+ if (segment_size > len)
+ segment_size = len;
+ /* Copy the data into the packet */
+ panic("%s: not yet implemented; copy in packet segments.", __func__);
+#if 0
+ memcpy(m_put(m, segment_size), cvmx_phys_to_ptr(segment_ptr.s.addr), segment_size);
+#endif
+ /* Reduce the amount of bytes left
+ to copy */
+ len -= segment_size;
+ segment_ptr = next_ptr;
+ }
+ }
+ packet_not_copied = 0;
+ }
+
+ if (((work->ipprt < TOTAL_NUMBER_OF_PORTS) &&
+ cvm_oct_device[work->ipprt])) {
+ struct ifnet *ifp = cvm_oct_device[work->ipprt];
+
+ /* Only accept packets for devices
+ that are currently up */
+ if ((ifp->if_flags & IFF_UP)) {
+ m->m_pkthdr.rcvif = ifp;
+
+ if ((ifp->if_capenable & IFCAP_RXCSUM) != 0) {
+ if ((work->word2.s.not_IP || work->word2.s.IP_exc || work->word2.s.L4_error))
+ m->m_pkthdr.csum_flags = 0; /* XXX */
+ else {
+ m->m_pkthdr.csum_flags = CSUM_IP_CHECKED | CSUM_IP_VALID | CSUM_DATA_VALID | CSUM_PSEUDO_HDR;
+ m->m_pkthdr.csum_data = 0xffff;
+ }
+ } else {
+ m->m_pkthdr.csum_flags = 0; /* XXX */
+ }
+
+ ifp->if_ipackets++;
+
+ (*ifp->if_input)(ifp, m);
+ } else {
+ /* Drop any packet received for a device that isn't up */
+ /*
+ DEBUGPRINT("%s: Device not up, packet dropped\n",
+ if_name(ifp));
+ */
+ m_freem(m);
+ }
+ } else {
+ /* Drop any packet received for a device that
+ doesn't exist */
+ DEBUGPRINT("Port %d not controlled by Linux, packet dropped\n", work->ipprt);
+ m_freem(m);
+ }
+
+ /* Check to see if the mbuf and work share
+ the same packet buffer */
+ if (USE_MBUFS_IN_HW && (packet_not_copied)) {
+ /* This buffer needs to be replaced, increment
+ the number of buffers we need to free by one */
+ cvmx_fau_atomic_add32(
+ FAU_NUM_PACKET_BUFFERS_TO_FREE, 1);
+
+ cvmx_fpa_free(work, CVMX_FPA_WQE_POOL,
+ DONT_WRITEBACK(1));
+ } else
+ cvm_oct_free_work(work);
+ }
+
+ /* Restore the original POW group mask */
+ cvmx_write_csr(CVMX_POW_PP_GRP_MSKX(coreid), old_group_mask);
+ if (USE_ASYNC_IOBDMA) {
+ /* Restore the scratch area */
+ cvmx_scratch_write64(CVMX_SCR_SCRATCH, old_scratch);
+ }
+
+ if (USE_MBUFS_IN_HW) {
+ /* Refill the packet buffer pool */
+ number_to_free =
+ cvmx_fau_fetch_and_add32(FAU_NUM_PACKET_BUFFERS_TO_FREE, 0);
+
+ if (number_to_free > 0) {
+ cvmx_fau_atomic_add32(FAU_NUM_PACKET_BUFFERS_TO_FREE,
+ -number_to_free);
+ num_freed =
+ cvm_oct_mem_fill_fpa(CVMX_FPA_PACKET_POOL,
+ CVMX_FPA_PACKET_POOL_SIZE,
+ number_to_free);
+ if (num_freed != number_to_free) {
+ cvmx_fau_atomic_add32(FAU_NUM_PACKET_BUFFERS_TO_FREE,
+ number_to_free - num_freed);
+ }
+ }
+ }
+ sched_unpin();
+}
+
+
+
+void cvm_oct_rx_initialize(void)
+{
+ TASK_INIT(&cvm_oct_task, 0, cvm_oct_tasklet_rx, NULL);
+
+ cvm_oct_taskq = taskqueue_create_fast("oct_rx", M_NOWAIT,
+ taskqueue_thread_enqueue,
+ &cvm_oct_taskq);
+ taskqueue_start_threads(&cvm_oct_taskq, min(mp_ncpus, MAXCPU),
+ PI_NET, "octe taskq");
+}
+
+void cvm_oct_rx_shutdown(void)
+{
+ panic("%s: not yet implemented.", __func__);
+}
+
diff --git a/sys/mips/cavium/octe/ethernet-rx.h b/sys/mips/cavium/octe/ethernet-rx.h
new file mode 100644
index 000000000000..735d08724328
--- /dev/null
+++ b/sys/mips/cavium/octe/ethernet-rx.h
@@ -0,0 +1,37 @@
+/*************************************************************************
+Copyright (c) 2003-2007 Cavium Networks (support@cavium.com). All rights
+reserved.
+
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * 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.
+
+ * Neither the name of Cavium Networks nor the names of
+ its contributors may be used to endorse or promote products
+ derived from this software without specific prior written
+ permission.
+
+This Software, including technical data, may be subject to U.S. export control laws, including the U.S. Export Administration Act and its associated regulations, and may be subject to export or import regulations in other countries.
+
+TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+
+*************************************************************************/
+/* $FreeBSD$ */
+
+int cvm_oct_do_interrupt(void *dev_id);
+void cvm_oct_poll_controller(struct ifnet *ifp);
+void cvm_oct_tasklet_rx(void *context, int pending);
+
+void cvm_oct_rx_initialize(void);
+void cvm_oct_rx_shutdown(void);
+
diff --git a/sys/mips/cavium/octe/ethernet-sgmii.c b/sys/mips/cavium/octe/ethernet-sgmii.c
new file mode 100644
index 000000000000..17e6be712bc4
--- /dev/null
+++ b/sys/mips/cavium/octe/ethernet-sgmii.c
@@ -0,0 +1,117 @@
+/*************************************************************************
+Copyright (c) 2003-2007 Cavium Networks (support@cavium.com). All rights
+reserved.
+
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * 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.
+
+ * Neither the name of Cavium Networks nor the names of
+ its contributors may be used to endorse or promote products
+ derived from this software without specific prior written
+ permission.
+
+This Software, including technical data, may be subject to U.S. export control laws, including the U.S. Export Administration Act and its associated regulations, and may be subject to export or import regulations in other countries.
+
+TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+
+*************************************************************************/
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+
+#include <net/ethernet.h>
+#include <net/if.h>
+
+#include "wrapper-cvmx-includes.h"
+#include "ethernet-headers.h"
+
+extern int octeon_is_simulation(void);
+
+static int cvm_oct_sgmii_open(struct ifnet *ifp)
+{
+ cvmx_gmxx_prtx_cfg_t gmx_cfg;
+ cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
+ int interface = INTERFACE(priv->port);
+ int index = INDEX(priv->port);
+ cvmx_helper_link_info_t link_info;
+
+ gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
+ gmx_cfg.s.en = 1;
+ cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64);
+
+ if (!octeon_is_simulation()) {
+ link_info = cvmx_helper_link_get(priv->port);
+ if (!link_info.s.link_up)
+ if_link_state_change(ifp, LINK_STATE_DOWN);
+ else
+ if_link_state_change(ifp, LINK_STATE_UP);
+ }
+
+ return 0;
+}
+
+static int cvm_oct_sgmii_stop(struct ifnet *ifp)
+{
+ cvmx_gmxx_prtx_cfg_t gmx_cfg;
+ cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
+ int interface = INTERFACE(priv->port);
+ int index = INDEX(priv->port);
+
+ gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
+ gmx_cfg.s.en = 0;
+ cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64);
+ return 0;
+}
+
+static void cvm_oct_sgmii_poll(struct ifnet *ifp)
+{
+ cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
+ cvmx_helper_link_info_t link_info;
+
+ link_info = cvmx_helper_link_get(priv->port);
+ if (link_info.u64 == priv->link_info)
+ return;
+
+ link_info = cvmx_helper_link_autoconf(priv->port);
+ priv->link_info = link_info.u64;
+ priv->need_link_update = 1;
+}
+
+int cvm_oct_sgmii_init(struct ifnet *ifp)
+{
+ cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
+ cvm_oct_common_init(ifp);
+ priv->open = cvm_oct_sgmii_open;
+ priv->stop = cvm_oct_sgmii_stop;
+ priv->stop(ifp);
+ if (!octeon_is_simulation())
+ priv->poll = cvm_oct_sgmii_poll;
+
+ /* FIXME: Need autoneg logic */
+ return 0;
+}
+
+void cvm_oct_sgmii_uninit(struct ifnet *ifp)
+{
+ cvm_oct_common_uninit(ifp);
+}
+
diff --git a/sys/mips/cavium/octe/ethernet-spi.c b/sys/mips/cavium/octe/ethernet-spi.c
new file mode 100644
index 000000000000..100f642ef195
--- /dev/null
+++ b/sys/mips/cavium/octe/ethernet-spi.c
@@ -0,0 +1,309 @@
+/*************************************************************************
+Copyright (c) 2003-2007 Cavium Networks (support@cavium.com). All rights
+reserved.
+
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * 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.
+
+ * Neither the name of Cavium Networks nor the names of
+ its contributors may be used to endorse or promote products
+ derived from this software without specific prior written
+ permission.
+
+This Software, including technical data, may be subject to U.S. export control laws, including the U.S. Export Administration Act and its associated regulations, and may be subject to export or import regulations in other countries.
+
+TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+
+*************************************************************************/
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
+#include <sys/mbuf.h>
+#include <sys/rman.h>
+#include <sys/socket.h>
+
+#include <net/ethernet.h>
+#include <net/if.h>
+
+#include "wrapper-cvmx-includes.h"
+#include "ethernet-headers.h"
+
+#include "octebusvar.h"
+
+static int number_spi_ports;
+static int need_retrain[2] = {0, 0};
+
+static int cvm_oct_spi_rml_interrupt(void *dev_id)
+{
+ int return_status = FILTER_STRAY;
+ cvmx_npi_rsl_int_blocks_t rsl_int_blocks;
+
+ /* Check and see if this interrupt was caused by the GMX block */
+ rsl_int_blocks.u64 = cvmx_read_csr(CVMX_NPI_RSL_INT_BLOCKS);
+ if (rsl_int_blocks.s.spx1) { /* 19 - SPX1_INT_REG & STX1_INT_REG */
+
+ cvmx_spxx_int_reg_t spx_int_reg;
+ cvmx_stxx_int_reg_t stx_int_reg;
+
+ spx_int_reg.u64 = cvmx_read_csr(CVMX_SPXX_INT_REG(1));
+ cvmx_write_csr(CVMX_SPXX_INT_REG(1), spx_int_reg.u64);
+ if (!need_retrain[1]) {
+
+ spx_int_reg.u64 &= cvmx_read_csr(CVMX_SPXX_INT_MSK(1));
+ if (spx_int_reg.s.spf)
+ printf("SPI1: SRX Spi4 interface down\n");
+ if (spx_int_reg.s.calerr)
+ printf("SPI1: SRX Spi4 Calendar table parity error\n");
+ if (spx_int_reg.s.syncerr)
+ printf("SPI1: SRX Consecutive Spi4 DIP4 errors have exceeded SPX_ERR_CTL[ERRCNT]\n");
+ if (spx_int_reg.s.diperr)
+ printf("SPI1: SRX Spi4 DIP4 error\n");
+ if (spx_int_reg.s.tpaovr)
+ printf("SPI1: SRX Selected port has hit TPA overflow\n");
+ if (spx_int_reg.s.rsverr)
+ printf("SPI1: SRX Spi4 reserved control word detected\n");
+ if (spx_int_reg.s.drwnng)
+ printf("SPI1: SRX Spi4 receive FIFO drowning/overflow\n");
+ if (spx_int_reg.s.clserr)
+ printf("SPI1: SRX Spi4 packet closed on non-16B alignment without EOP\n");
+ if (spx_int_reg.s.spiovr)
+ printf("SPI1: SRX Spi4 async FIFO overflow\n");
+ if (spx_int_reg.s.abnorm)
+ printf("SPI1: SRX Abnormal packet termination (ERR bit)\n");
+ if (spx_int_reg.s.prtnxa)
+ printf("SPI1: SRX Port out of range\n");
+ }
+
+ stx_int_reg.u64 = cvmx_read_csr(CVMX_STXX_INT_REG(1));
+ cvmx_write_csr(CVMX_STXX_INT_REG(1), stx_int_reg.u64);
+ if (!need_retrain[1]) {
+
+ stx_int_reg.u64 &= cvmx_read_csr(CVMX_STXX_INT_MSK(1));
+ if (stx_int_reg.s.syncerr)
+ printf("SPI1: STX Interface encountered a fatal error\n");
+ if (stx_int_reg.s.frmerr)
+ printf("SPI1: STX FRMCNT has exceeded STX_DIP_CNT[MAXFRM]\n");
+ if (stx_int_reg.s.unxfrm)
+ printf("SPI1: STX Unexpected framing sequence\n");
+ if (stx_int_reg.s.nosync)
+ printf("SPI1: STX ERRCNT has exceeded STX_DIP_CNT[MAXDIP]\n");
+ if (stx_int_reg.s.diperr)
+ printf("SPI1: STX DIP2 error on the Spi4 Status channel\n");
+ if (stx_int_reg.s.datovr)
+ printf("SPI1: STX Spi4 FIFO overflow error\n");
+ if (stx_int_reg.s.ovrbst)
+ printf("SPI1: STX Transmit packet burst too big\n");
+ if (stx_int_reg.s.calpar1)
+ printf("SPI1: STX Calendar Table Parity Error Bank1\n");
+ if (stx_int_reg.s.calpar0)
+ printf("SPI1: STX Calendar Table Parity Error Bank0\n");
+ }
+
+ cvmx_write_csr(CVMX_SPXX_INT_MSK(1), 0);
+ cvmx_write_csr(CVMX_STXX_INT_MSK(1), 0);
+ need_retrain[1] = 1;
+ return_status = FILTER_HANDLED;
+ }
+
+ if (rsl_int_blocks.s.spx0) { /* 18 - SPX0_INT_REG & STX0_INT_REG */
+ cvmx_spxx_int_reg_t spx_int_reg;
+ cvmx_stxx_int_reg_t stx_int_reg;
+
+ spx_int_reg.u64 = cvmx_read_csr(CVMX_SPXX_INT_REG(0));
+ cvmx_write_csr(CVMX_SPXX_INT_REG(0), spx_int_reg.u64);
+ if (!need_retrain[0]) {
+
+ spx_int_reg.u64 &= cvmx_read_csr(CVMX_SPXX_INT_MSK(0));
+ if (spx_int_reg.s.spf)
+ printf("SPI0: SRX Spi4 interface down\n");
+ if (spx_int_reg.s.calerr)
+ printf("SPI0: SRX Spi4 Calendar table parity error\n");
+ if (spx_int_reg.s.syncerr)
+ printf("SPI0: SRX Consecutive Spi4 DIP4 errors have exceeded SPX_ERR_CTL[ERRCNT]\n");
+ if (spx_int_reg.s.diperr)
+ printf("SPI0: SRX Spi4 DIP4 error\n");
+ if (spx_int_reg.s.tpaovr)
+ printf("SPI0: SRX Selected port has hit TPA overflow\n");
+ if (spx_int_reg.s.rsverr)
+ printf("SPI0: SRX Spi4 reserved control word detected\n");
+ if (spx_int_reg.s.drwnng)
+ printf("SPI0: SRX Spi4 receive FIFO drowning/overflow\n");
+ if (spx_int_reg.s.clserr)
+ printf("SPI0: SRX Spi4 packet closed on non-16B alignment without EOP\n");
+ if (spx_int_reg.s.spiovr)
+ printf("SPI0: SRX Spi4 async FIFO overflow\n");
+ if (spx_int_reg.s.abnorm)
+ printf("SPI0: SRX Abnormal packet termination (ERR bit)\n");
+ if (spx_int_reg.s.prtnxa)
+ printf("SPI0: SRX Port out of range\n");
+ }
+
+ stx_int_reg.u64 = cvmx_read_csr(CVMX_STXX_INT_REG(0));
+ cvmx_write_csr(CVMX_STXX_INT_REG(0), stx_int_reg.u64);
+ if (!need_retrain[0]) {
+
+ stx_int_reg.u64 &= cvmx_read_csr(CVMX_STXX_INT_MSK(0));
+ if (stx_int_reg.s.syncerr)
+ printf("SPI0: STX Interface encountered a fatal error\n");
+ if (stx_int_reg.s.frmerr)
+ printf("SPI0: STX FRMCNT has exceeded STX_DIP_CNT[MAXFRM]\n");
+ if (stx_int_reg.s.unxfrm)
+ printf("SPI0: STX Unexpected framing sequence\n");
+ if (stx_int_reg.s.nosync)
+ printf("SPI0: STX ERRCNT has exceeded STX_DIP_CNT[MAXDIP]\n");
+ if (stx_int_reg.s.diperr)
+ printf("SPI0: STX DIP2 error on the Spi4 Status channel\n");
+ if (stx_int_reg.s.datovr)
+ printf("SPI0: STX Spi4 FIFO overflow error\n");
+ if (stx_int_reg.s.ovrbst)
+ printf("SPI0: STX Transmit packet burst too big\n");
+ if (stx_int_reg.s.calpar1)
+ printf("SPI0: STX Calendar Table Parity Error Bank1\n");
+ if (stx_int_reg.s.calpar0)
+ printf("SPI0: STX Calendar Table Parity Error Bank0\n");
+ }
+
+ cvmx_write_csr(CVMX_SPXX_INT_MSK(0), 0);
+ cvmx_write_csr(CVMX_STXX_INT_MSK(0), 0);
+ need_retrain[0] = 1;
+ return_status = FILTER_HANDLED;
+ }
+
+ return return_status;
+}
+
+static void cvm_oct_spi_enable_error_reporting(int interface)
+{
+ cvmx_spxx_int_msk_t spxx_int_msk;
+ cvmx_stxx_int_msk_t stxx_int_msk;
+
+ spxx_int_msk.u64 = cvmx_read_csr(CVMX_SPXX_INT_MSK(interface));
+ spxx_int_msk.s.calerr = 1;
+ spxx_int_msk.s.syncerr = 1;
+ spxx_int_msk.s.diperr = 1;
+ spxx_int_msk.s.tpaovr = 1;
+ spxx_int_msk.s.rsverr = 1;
+ spxx_int_msk.s.drwnng = 1;
+ spxx_int_msk.s.clserr = 1;
+ spxx_int_msk.s.spiovr = 1;
+ spxx_int_msk.s.abnorm = 1;
+ spxx_int_msk.s.prtnxa = 1;
+ cvmx_write_csr(CVMX_SPXX_INT_MSK(interface), spxx_int_msk.u64);
+
+ stxx_int_msk.u64 = cvmx_read_csr(CVMX_STXX_INT_MSK(interface));
+ stxx_int_msk.s.frmerr = 1;
+ stxx_int_msk.s.unxfrm = 1;
+ stxx_int_msk.s.nosync = 1;
+ stxx_int_msk.s.diperr = 1;
+ stxx_int_msk.s.datovr = 1;
+ stxx_int_msk.s.ovrbst = 1;
+ stxx_int_msk.s.calpar1 = 1;
+ stxx_int_msk.s.calpar0 = 1;
+ cvmx_write_csr(CVMX_STXX_INT_MSK(interface), stxx_int_msk.u64);
+}
+
+static void cvm_oct_spi_poll(struct ifnet *ifp)
+{
+ static int spi4000_port;
+ cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
+ int interface;
+
+ for (interface = 0; interface < 2; interface++) {
+
+ if ((priv->port == interface*16) && need_retrain[interface]) {
+
+ if (cvmx_spi_restart_interface(interface, CVMX_SPI_MODE_DUPLEX, 10) == 0) {
+ need_retrain[interface] = 0;
+ cvm_oct_spi_enable_error_reporting(interface);
+ }
+ }
+
+ /* The SPI4000 TWSI interface is very slow. In order not to
+ bring the system to a crawl, we only poll a single port
+ every second. This means negotiation speed changes
+ take up to 10 seconds, but at least we don't waste
+ absurd amounts of time waiting for TWSI */
+ if (priv->port == spi4000_port) {
+ /* This function does nothing if it is called on an
+ interface without a SPI4000 */
+ cvmx_spi4000_check_speed(interface, priv->port);
+ /* Normal ordering increments. By decrementing
+ we only match once per iteration */
+ spi4000_port--;
+ if (spi4000_port < 0)
+ spi4000_port = 10;
+ }
+ }
+}
+
+
+int cvm_oct_spi_init(struct ifnet *ifp)
+{
+ struct octebus_softc *sc;
+ cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
+ int error;
+ int rid;
+
+ if (number_spi_ports == 0) {
+ sc = device_get_softc(device_get_parent(priv->dev));
+
+ rid = 0;
+ sc->sc_spi_irq = bus_alloc_resource(sc->sc_dev, SYS_RES_IRQ,
+ &rid, CVMX_IRQ_RML,
+ CVMX_IRQ_RML, 1,
+ RF_ACTIVE);
+ if (sc->sc_spi_irq == NULL) {
+ device_printf(sc->sc_dev, "could not allocate SPI irq");
+ return ENXIO;
+ }
+
+ error = bus_setup_intr(sc->sc_dev, sc->sc_spi_irq,
+ INTR_TYPE_NET | INTR_MPSAFE,
+ cvm_oct_spi_rml_interrupt, NULL,
+ &number_spi_ports, NULL);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "could not setup SPI irq");
+ return error;
+ }
+ }
+ number_spi_ports++;
+
+ if ((priv->port == 0) || (priv->port == 16)) {
+ cvm_oct_spi_enable_error_reporting(INTERFACE(priv->port));
+ priv->poll = cvm_oct_spi_poll;
+ }
+ cvm_oct_common_init(ifp);
+ return 0;
+}
+
+void cvm_oct_spi_uninit(struct ifnet *ifp)
+{
+ int interface;
+
+ cvm_oct_common_uninit(ifp);
+ number_spi_ports--;
+ if (number_spi_ports == 0) {
+ for (interface = 0; interface < 2; interface++) {
+ cvmx_write_csr(CVMX_SPXX_INT_MSK(interface), 0);
+ cvmx_write_csr(CVMX_STXX_INT_MSK(interface), 0);
+ }
+ panic("%s: IRQ release not yet implemented.", __func__);
+ }
+}
diff --git a/sys/mips/cavium/octe/ethernet-tx.c b/sys/mips/cavium/octe/ethernet-tx.c
new file mode 100644
index 000000000000..8866f1203446
--- /dev/null
+++ b/sys/mips/cavium/octe/ethernet-tx.c
@@ -0,0 +1,402 @@
+/*************************************************************************
+Copyright (c) 2003-2007 Cavium Networks (support@cavium.com). All rights
+reserved.
+
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * 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.
+
+ * Neither the name of Cavium Networks nor the names of
+ its contributors may be used to endorse or promote products
+ derived from this software without specific prior written
+ permission.
+
+This Software, including technical data, may be subject to U.S. export control laws, including the U.S. Export Administration Act and its associated regulations, and may be subject to export or import regulations in other countries.
+
+TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+
+*************************************************************************/
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+
+#include <net/bpf.h>
+#include <net/ethernet.h>
+#include <net/if.h>
+
+#include "wrapper-cvmx-includes.h"
+#include "ethernet-headers.h"
+
+/* You can define GET_MBUF_QOS() to override how the mbuf output function
+ determines which output queue is used. The default implementation
+ always uses the base queue for the port. If, for example, you wanted
+ to use the m->priority fieid, define GET_MBUF_QOS as:
+ #define GET_MBUF_QOS(m) ((m)->priority) */
+#ifndef GET_MBUF_QOS
+ #define GET_MBUF_QOS(m) 0
+#endif
+
+extern int pow_send_group;
+
+
+/**
+ * Packet transmit
+ *
+ * @param m Packet to send
+ * @param dev Device info structure
+ * @return Always returns zero
+ */
+int cvm_oct_xmit(struct mbuf *m, struct ifnet *ifp)
+{
+ cvmx_pko_command_word0_t pko_command;
+ cvmx_buf_ptr_t hw_buffer;
+ uint64_t old_scratch;
+ uint64_t old_scratch2;
+ int dropped;
+ int qos;
+ cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
+ int32_t in_use;
+ int32_t buffers_to_free;
+ cvmx_wqe_t *work;
+
+ /* Prefetch the private data structure.
+ It is larger that one cache line */
+ CVMX_PREFETCH(priv, 0);
+
+ /* Start off assuming no drop */
+ dropped = 0;
+
+ /* The check on CVMX_PKO_QUEUES_PER_PORT_* is designed to completely
+ remove "qos" in the event neither interface supports multiple queues
+ per port */
+ if ((CVMX_PKO_QUEUES_PER_PORT_INTERFACE0 > 1) ||
+ (CVMX_PKO_QUEUES_PER_PORT_INTERFACE1 > 1)) {
+ qos = GET_MBUF_QOS(m);
+ if (qos <= 0)
+ qos = 0;
+ else if (qos >= cvmx_pko_get_num_queues(priv->port))
+ qos = 0;
+ } else
+ qos = 0;
+
+ if (USE_ASYNC_IOBDMA) {
+ /* Save scratch in case userspace is using it */
+ CVMX_SYNCIOBDMA;
+ old_scratch = cvmx_scratch_read64(CVMX_SCR_SCRATCH);
+ old_scratch2 = cvmx_scratch_read64(CVMX_SCR_SCRATCH+8);
+
+ /* Assume we're going to be able t osend this packet. Fetch and increment
+ the number of pending packets for output */
+ cvmx_fau_async_fetch_and_add32(CVMX_SCR_SCRATCH+8, FAU_NUM_PACKET_BUFFERS_TO_FREE, 0);
+ cvmx_fau_async_fetch_and_add32(CVMX_SCR_SCRATCH, priv->fau+qos*4, 1);
+ }
+
+ /* The CN3XXX series of parts has an errata (GMX-401) which causes the
+ GMX block to hang if a collision occurs towards the end of a
+ <68 byte packet. As a workaround for this, we pad packets to be
+ 68 bytes whenever we are in half duplex mode. We don't handle
+ the case of having a small packet but no room to add the padding.
+ The kernel should always give us at least a cache line */
+ if (__predict_false(m->m_pkthdr.len < 64) && OCTEON_IS_MODEL(OCTEON_CN3XXX)) {
+ cvmx_gmxx_prtx_cfg_t gmx_prt_cfg;
+ int interface = INTERFACE(priv->port);
+ int index = INDEX(priv->port);
+
+ if (interface < 2) {
+ /* We only need to pad packet in half duplex mode */
+ gmx_prt_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
+ if (gmx_prt_cfg.s.duplex == 0) {
+ static uint8_t pad[64];
+
+ if (!m_append(m, sizeof pad - m->m_pkthdr.len, pad))
+ printf("%s: unable to padd small packet.", __func__);
+ }
+ }
+ }
+
+ /*
+ * If the packet is not fragmented.
+ */
+ if (m->m_pkthdr.len == m->m_len) {
+ /* Build the PKO buffer pointer */
+ hw_buffer.u64 = 0;
+ hw_buffer.s.addr = cvmx_ptr_to_phys(m->m_data);
+ hw_buffer.s.pool = 0;
+ hw_buffer.s.size = m->m_len;
+
+ /* Build the PKO command */
+ pko_command.u64 = 0;
+ pko_command.s.segs = 1;
+
+ work = NULL;
+ } else {
+ struct mbuf *n;
+ unsigned segs;
+ uint64_t *gp;
+
+ /*
+ * The packet is fragmented, we need to send a list of segments
+ * in memory we borrow from the WQE pool.
+ */
+ work = cvmx_fpa_alloc(CVMX_FPA_WQE_POOL);
+ gp = (uint64_t *)work;
+
+ segs = 0;
+ for (n = m; n != NULL; n = n->m_next) {
+ if (segs == CVMX_FPA_WQE_POOL_SIZE / sizeof (uint64_t))
+ panic("%s: too many segments in packet; call m_collapse().", __func__);
+
+ /* Build the PKO buffer pointer */
+ hw_buffer.u64 = 0;
+ hw_buffer.s.addr = cvmx_ptr_to_phys(n->m_data);
+ hw_buffer.s.pool = 0;
+ hw_buffer.s.size = n->m_len;
+
+ *gp++ = hw_buffer.u64;
+ segs++;
+ }
+
+ /* Build the PKO buffer gather list pointer */
+ hw_buffer.u64 = 0;
+ hw_buffer.s.addr = cvmx_ptr_to_phys(work);
+ hw_buffer.s.pool = CVMX_FPA_WQE_POOL;
+ hw_buffer.s.size = segs;
+
+ /* Build the PKO command */
+ pko_command.u64 = 0;
+ pko_command.s.segs = segs;
+ pko_command.s.gather = 1;
+ }
+
+ /* Finish building the PKO command */
+ pko_command.s.n2 = 1; /* Don't pollute L2 with the outgoing packet */
+ pko_command.s.dontfree = 1;
+ pko_command.s.reg0 = priv->fau+qos*4;
+ pko_command.s.reg0 = priv->fau+qos*4;
+ pko_command.s.total_bytes = m->m_pkthdr.len;
+ pko_command.s.size0 = CVMX_FAU_OP_SIZE_32;
+ pko_command.s.subone0 = 1;
+
+ /* Check if we can use the hardware checksumming */
+ if (USE_HW_TCPUDP_CHECKSUM &&
+ (m->m_pkthdr.csum_flags & (CSUM_TCP | CSUM_UDP)) != 0) {
+ /* Use hardware checksum calc */
+ pko_command.s.ipoffp1 = ETHER_HDR_LEN + 1;
+ }
+
+ IF_LOCK(&priv->tx_free_queue[qos]);
+ if (USE_ASYNC_IOBDMA) {
+ /* Get the number of mbufs in use by the hardware */
+ CVMX_SYNCIOBDMA;
+ in_use = cvmx_scratch_read64(CVMX_SCR_SCRATCH);
+ buffers_to_free = cvmx_scratch_read64(CVMX_SCR_SCRATCH+8);
+ } else {
+ /* Get the number of mbufs in use by the hardware */
+ in_use = cvmx_fau_fetch_and_add32(priv->fau+qos*4, 1);
+ buffers_to_free = cvmx_fau_fetch_and_add32(FAU_NUM_PACKET_BUFFERS_TO_FREE, 0);
+ }
+
+ cvmx_pko_send_packet_prepare(priv->port, priv->queue + qos, CVMX_PKO_LOCK_CMD_QUEUE);
+
+ /* Drop this packet if we have too many already queued to the HW */
+ if (_IF_QFULL(&priv->tx_free_queue[qos])) {
+ dropped = 1;
+ }
+ /* Send the packet to the output queue */
+ else
+ if (__predict_false(cvmx_pko_send_packet_finish(priv->port, priv->queue + qos, pko_command, hw_buffer, CVMX_PKO_LOCK_CMD_QUEUE))) {
+ DEBUGPRINT("%s: Failed to send the packet\n", if_name(ifp));
+ dropped = 1;
+ }
+
+ if (USE_ASYNC_IOBDMA) {
+ /* Restore the scratch area */
+ cvmx_scratch_write64(CVMX_SCR_SCRATCH, old_scratch);
+ cvmx_scratch_write64(CVMX_SCR_SCRATCH+8, old_scratch2);
+ }
+
+ if (__predict_false(dropped)) {
+ m_freem(m);
+ cvmx_fau_atomic_add32(priv->fau+qos*4, -1);
+ ifp->if_oerrors++;
+ } else {
+ /* Put this packet on the queue to be freed later */
+ _IF_ENQUEUE(&priv->tx_free_queue[qos], m);
+
+ /* Pass it to any BPF listeners. */
+ ETHER_BPF_MTAP(ifp, m);
+ }
+ if (work != NULL)
+ cvmx_fpa_free(work, CVMX_FPA_WQE_POOL, DONT_WRITEBACK(1));
+
+ /* Free mbufs not in use by the hardware */
+ if (_IF_QLEN(&priv->tx_free_queue[qos]) > in_use) {
+ while (_IF_QLEN(&priv->tx_free_queue[qos]) > in_use) {
+ _IF_DEQUEUE(&priv->tx_free_queue[qos], m);
+ m_freem(m);
+ }
+ }
+ IF_UNLOCK(&priv->tx_free_queue[qos]);
+
+ return dropped;
+}
+
+
+/**
+ * Packet transmit to the POW
+ *
+ * @param m Packet to send
+ * @param dev Device info structure
+ * @return Always returns zero
+ */
+int cvm_oct_xmit_pow(struct mbuf *m, struct ifnet *ifp)
+{
+ cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
+ char *packet_buffer;
+ char *copy_location;
+
+ /* Get a work queue entry */
+ cvmx_wqe_t *work = cvmx_fpa_alloc(CVMX_FPA_WQE_POOL);
+ if (__predict_false(work == NULL)) {
+ DEBUGPRINT("%s: Failed to allocate a work queue entry\n", if_name(ifp));
+ ifp->if_oerrors++;
+ m_freem(m);
+ return 0;
+ }
+
+ /* Get a packet buffer */
+ packet_buffer = cvmx_fpa_alloc(CVMX_FPA_PACKET_POOL);
+ if (__predict_false(packet_buffer == NULL)) {
+ DEBUGPRINT("%s: Failed to allocate a packet buffer\n",
+ if_name(ifp));
+ cvmx_fpa_free(work, CVMX_FPA_WQE_POOL, DONT_WRITEBACK(1));
+ ifp->if_oerrors++;
+ m_freem(m);
+ return 0;
+ }
+
+ /* Calculate where we need to copy the data to. We need to leave 8 bytes
+ for a next pointer (unused). We also need to include any configure
+ skip. Then we need to align the IP packet src and dest into the same
+ 64bit word. The below calculation may add a little extra, but that
+ doesn't hurt */
+ copy_location = packet_buffer + sizeof(uint64_t);
+ copy_location += ((CVMX_HELPER_FIRST_MBUFF_SKIP+7)&0xfff8) + 6;
+
+ /* We have to copy the packet since whoever processes this packet
+ will free it to a hardware pool. We can't use the trick of
+ counting outstanding packets like in cvm_oct_xmit */
+ m_copydata(m, 0, m->m_pkthdr.len, copy_location);
+
+ /* Fill in some of the work queue fields. We may need to add more
+ if the software at the other end needs them */
+#if 0
+ work->hw_chksum = m->csum;
+#endif
+ work->len = m->m_pkthdr.len;
+ work->ipprt = priv->port;
+ work->qos = priv->port & 0x7;
+ work->grp = pow_send_group;
+ work->tag_type = CVMX_HELPER_INPUT_TAG_TYPE;
+ work->tag = pow_send_group; /* FIXME */
+ work->word2.u64 = 0; /* Default to zero. Sets of zero later are commented out */
+ work->word2.s.bufs = 1;
+ work->packet_ptr.u64 = 0;
+ work->packet_ptr.s.addr = cvmx_ptr_to_phys(copy_location);
+ work->packet_ptr.s.pool = CVMX_FPA_PACKET_POOL;
+ work->packet_ptr.s.size = CVMX_FPA_PACKET_POOL_SIZE;
+ work->packet_ptr.s.back = (copy_location - packet_buffer)>>7;
+
+ panic("%s: POW transmit not quite implemented yet.", __func__);
+#if 0
+ if (m->protocol == htons(ETH_P_IP)) {
+ work->word2.s.ip_offset = 14;
+ #if 0
+ work->word2.s.vlan_valid = 0; /* FIXME */
+ work->word2.s.vlan_cfi = 0; /* FIXME */
+ work->word2.s.vlan_id = 0; /* FIXME */
+ work->word2.s.dec_ipcomp = 0; /* FIXME */
+ #endif
+ work->word2.s.tcp_or_udp = (ip_hdr(m)->protocol == IP_PROTOCOL_TCP) || (ip_hdr(m)->protocol == IP_PROTOCOL_UDP);
+ #if 0
+ work->word2.s.dec_ipsec = 0; /* FIXME */
+ work->word2.s.is_v6 = 0; /* We only support IPv4 right now */
+ work->word2.s.software = 0; /* Hardware would set to zero */
+ work->word2.s.L4_error = 0; /* No error, packet is internal */
+ #endif
+ work->word2.s.is_frag = !((ip_hdr(m)->frag_off == 0) || (ip_hdr(m)->frag_off == 1<<14));
+ #if 0
+ work->word2.s.IP_exc = 0; /* Assume Linux is sending a good packet */
+ #endif
+ work->word2.s.is_bcast = (m->pkt_type == PACKET_BROADCAST);
+ work->word2.s.is_mcast = (m->pkt_type == PACKET_MULTICAST);
+ #if 0
+ work->word2.s.not_IP = 0; /* This is an IP packet */
+ work->word2.s.rcv_error = 0; /* No error, packet is internal */
+ work->word2.s.err_code = 0; /* No error, packet is internal */
+ #endif
+
+ /* When copying the data, include 4 bytes of the ethernet header to
+ align the same way hardware does */
+ memcpy(work->packet_data, m->data + 10, sizeof(work->packet_data));
+ } else {
+ #if 0
+ work->word2.snoip.vlan_valid = 0; /* FIXME */
+ work->word2.snoip.vlan_cfi = 0; /* FIXME */
+ work->word2.snoip.vlan_id = 0; /* FIXME */
+ work->word2.snoip.software = 0; /* Hardware would set to zero */
+ #endif
+ work->word2.snoip.is_rarp = m->protocol == htons(ETH_P_RARP);
+ work->word2.snoip.is_arp = m->protocol == htons(ETH_P_ARP);
+ work->word2.snoip.is_bcast = (m->pkt_type == PACKET_BROADCAST);
+ work->word2.snoip.is_mcast = (m->pkt_type == PACKET_MULTICAST);
+ work->word2.snoip.not_IP = 1; /* IP was done up above */
+ #if 0
+ work->word2.snoip.rcv_error = 0; /* No error, packet is internal */
+ work->word2.snoip.err_code = 0; /* No error, packet is internal */
+ #endif
+ memcpy(work->packet_data, m->data, sizeof(work->packet_data));
+ }
+#endif
+
+ /* Submit the packet to the POW */
+ cvmx_pow_work_submit(work, work->tag, work->tag_type, work->qos, work->grp);
+ ifp->if_opackets++;
+ ifp->if_obytes += m->m_pkthdr.len;
+ m_freem(m);
+ return 0;
+}
+
+
+/**
+ * This function frees all mbufs that are currenty queued for TX.
+ *
+ * @param dev Device being shutdown
+ */
+void cvm_oct_tx_shutdown(struct ifnet *ifp)
+{
+ cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
+ int qos;
+
+ for (qos = 0; qos < 16; qos++) {
+ IF_DRAIN(&priv->tx_free_queue[qos]);
+ }
+}
diff --git a/sys/mips/cavium/octe/ethernet-tx.h b/sys/mips/cavium/octe/ethernet-tx.h
new file mode 100644
index 000000000000..33c551a716bf
--- /dev/null
+++ b/sys/mips/cavium/octe/ethernet-tx.h
@@ -0,0 +1,34 @@
+/*************************************************************************
+Copyright (c) 2003-2007 Cavium Networks (support@cavium.com). All rights
+reserved.
+
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * 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.
+
+ * Neither the name of Cavium Networks nor the names of
+ its contributors may be used to endorse or promote products
+ derived from this software without specific prior written
+ permission.
+
+This Software, including technical data, may be subject to U.S. export control laws, including the U.S. Export Administration Act and its associated regulations, and may be subject to export or import regulations in other countries.
+
+TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+
+*************************************************************************/
+/* $FreeBSD$ */
+
+int cvm_oct_xmit(struct mbuf *m, struct ifnet *ifp);
+int cvm_oct_xmit_pow(struct mbuf *m, struct ifnet *ifp);
+void cvm_oct_tx_shutdown(struct ifnet *ifp);
+
diff --git a/sys/mips/cavium/octe/ethernet-util.h b/sys/mips/cavium/octe/ethernet-util.h
new file mode 100644
index 000000000000..37b75bb2af59
--- /dev/null
+++ b/sys/mips/cavium/octe/ethernet-util.h
@@ -0,0 +1,84 @@
+/*************************************************************************
+Copyright (c) 2003-2007 Cavium Networks (support@cavium.com). All rights
+reserved.
+
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * 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.
+
+ * Neither the name of Cavium Networks nor the names of
+ its contributors may be used to endorse or promote products
+ derived from this software without specific prior written
+ permission.
+
+This Software, including technical data, may be subject to U.S. export control laws, including the U.S. Export Administration Act and its associated regulations, and may be subject to export or import regulations in other countries.
+
+TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+
+*************************************************************************/
+/* $FreeBSD$ */
+
+#define DEBUGPRINT(format, ...) printf(format, ##__VA_ARGS__)
+
+/**
+ * Given a packet data address, return a pointer to the
+ * beginning of the packet buffer.
+ *
+ * @param packet_ptr Packet data hardware address
+ * @return Packet buffer pointer
+ */
+static inline char *cvm_oct_get_buffer_ptr(cvmx_buf_ptr_t packet_ptr)
+{
+ return cvmx_phys_to_ptr(((packet_ptr.s.addr >> 7) - packet_ptr.s.back) << 7);
+}
+
+
+/**
+ * Given an IPD/PKO port number, return the logical interface it is
+ * on.
+ *
+ * @param ipd_port Port to check
+ *
+ * @return Logical interface
+ */
+static inline int INTERFACE(int ipd_port)
+{
+ if (ipd_port < 32) /* Interface 0 or 1 for RGMII,GMII,SPI, etc */
+ return ipd_port>>4;
+ else if (ipd_port < 36) /* Interface 2 for NPI */
+ return 2;
+ else if (ipd_port < 40) /* Interface 3 for loopback */
+ return 3;
+ else if (ipd_port == 40) /* Non existant interface for POW0 */
+ return 4;
+ else
+ panic("Illegal ipd_port %d passed to INTERFACE\n", ipd_port);
+}
+
+
+/**
+ * Given an IPD/PKO port number, return the port's index on a
+ * logical interface.
+ *
+ * @param ipd_port Port to check
+ *
+ * @return Index into interface port list
+ */
+static inline int INDEX(int ipd_port)
+{
+ if (ipd_port < 32)
+ return ipd_port & 15;
+ else
+ return ipd_port & 3;
+}
+
diff --git a/sys/mips/cavium/octe/ethernet-xaui.c b/sys/mips/cavium/octe/ethernet-xaui.c
new file mode 100644
index 000000000000..455aec67e036
--- /dev/null
+++ b/sys/mips/cavium/octe/ethernet-xaui.c
@@ -0,0 +1,116 @@
+/*************************************************************************
+Copyright (c) 2003-2007 Cavium Networks (support@cavium.com). All rights
+reserved.
+
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * 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.
+
+ * Neither the name of Cavium Networks nor the names of
+ its contributors may be used to endorse or promote products
+ derived from this software without specific prior written
+ permission.
+
+This Software, including technical data, may be subject to U.S. export control laws, including the U.S. Export Administration Act and its associated regulations, and may be subject to export or import regulations in other countries.
+
+TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+
+*************************************************************************/
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+
+#include <net/ethernet.h>
+#include <net/if.h>
+
+#include "wrapper-cvmx-includes.h"
+#include "ethernet-headers.h"
+
+extern int octeon_is_simulation(void);
+
+static int cvm_oct_xaui_open(struct ifnet *ifp)
+{
+ cvmx_gmxx_prtx_cfg_t gmx_cfg;
+ cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
+ int interface = INTERFACE(priv->port);
+ int index = INDEX(priv->port);
+ cvmx_helper_link_info_t link_info;
+
+ gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
+ gmx_cfg.s.en = 1;
+ cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64);
+
+ if (!octeon_is_simulation()) {
+ link_info = cvmx_helper_link_get(priv->port);
+ if (!link_info.s.link_up)
+ if_link_state_change(ifp, LINK_STATE_DOWN);
+ else
+ if_link_state_change(ifp, LINK_STATE_UP);
+ }
+ return 0;
+}
+
+static int cvm_oct_xaui_stop(struct ifnet *ifp)
+{
+ cvmx_gmxx_prtx_cfg_t gmx_cfg;
+ cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
+ int interface = INTERFACE(priv->port);
+ int index = INDEX(priv->port);
+
+ gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
+ gmx_cfg.s.en = 0;
+ cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64);
+ return 0;
+}
+
+static void cvm_oct_xaui_poll(struct ifnet *ifp)
+{
+ cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
+ cvmx_helper_link_info_t link_info;
+
+ link_info = cvmx_helper_link_get(priv->port);
+ if (link_info.u64 == priv->link_info)
+ return;
+
+ link_info = cvmx_helper_link_autoconf(priv->port);
+ priv->link_info = link_info.u64;
+ priv->need_link_update = 1;
+}
+
+
+int cvm_oct_xaui_init(struct ifnet *ifp)
+{
+ cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
+ cvm_oct_common_init(ifp);
+ priv->open = cvm_oct_xaui_open;
+ priv->stop = cvm_oct_xaui_stop;
+ priv->stop(ifp);
+ if (!octeon_is_simulation())
+ priv->poll = cvm_oct_xaui_poll;
+
+ return 0;
+}
+
+void cvm_oct_xaui_uninit(struct ifnet *ifp)
+{
+ cvm_oct_common_uninit(ifp);
+}
+
diff --git a/sys/mips/cavium/octe/ethernet.c b/sys/mips/cavium/octe/ethernet.c
new file mode 100644
index 000000000000..1463c8ad4c93
--- /dev/null
+++ b/sys/mips/cavium/octe/ethernet.c
@@ -0,0 +1,594 @@
+/*************************************************************************
+Copyright (c) 2003-2007 Cavium Networks (support@cavium.com). All rights
+reserved.
+
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * 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.
+
+ * Neither the name of Cavium Networks nor the names of
+ its contributors may be used to endorse or promote products
+ derived from this software without specific prior written
+ permission.
+
+This Software, including technical data, may be subject to U.S. export control laws, including the U.S. Export Administration Act and its associated regulations, and may be subject to export or import regulations in other countries.
+
+TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+*************************************************************************/
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
+#include <sys/rman.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/module.h>
+#include <sys/smp.h>
+#include <sys/taskqueue.h>
+
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <net/if_types.h>
+
+#include "wrapper-cvmx-includes.h"
+#include "ethernet-headers.h"
+
+#include "octebusvar.h"
+
+/*
+ * XXX/juli
+ * Convert 0444 to tunables, 0644 to sysctls.
+ */
+#if defined(CONFIG_CAVIUM_OCTEON_NUM_PACKET_BUFFERS) && CONFIG_CAVIUM_OCTEON_NUM_PACKET_BUFFERS
+int num_packet_buffers = CONFIG_CAVIUM_OCTEON_NUM_PACKET_BUFFERS;
+#else
+int num_packet_buffers = 1024;
+#endif
+TUNABLE_INT("hw.octe.num_packet_buffers", &num_packet_buffers);
+/*
+ "\t\tNumber of packet buffers to allocate and store in the\n"
+ "\t\tFPA. By default, 1024 packet buffers are used unless\n"
+ "\t\tCONFIG_CAVIUM_OCTEON_NUM_PACKET_BUFFERS is defined." */
+
+int pow_receive_group = 15;
+TUNABLE_INT("hw.octe.pow_receive_group", &pow_receive_group);
+/*
+ "\t\tPOW group to receive packets from. All ethernet hardware\n"
+ "\t\twill be configured to send incomming packets to this POW\n"
+ "\t\tgroup. Also any other software can submit packets to this\n"
+ "\t\tgroup for the kernel to process." */
+
+int pow_send_group = -1; /* XXX Should be a sysctl. */
+TUNABLE_INT("hw.octe.pow_send_group", &pow_send_group);
+/*
+ "\t\tPOW group to send packets to other software on. This\n"
+ "\t\tcontrols the creation of the virtual device pow0.\n"
+ "\t\talways_use_pow also depends on this value." */
+
+int always_use_pow;
+TUNABLE_INT("hw.octe.always_use_pow", &always_use_pow);
+/*
+ "\t\tWhen set, always send to the pow group. This will cause\n"
+ "\t\tpackets sent to real ethernet devices to be sent to the\n"
+ "\t\tPOW group instead of the hardware. Unless some other\n"
+ "\t\tapplication changes the config, packets will still be\n"
+ "\t\treceived from the low level hardware. Use this option\n"
+ "\t\tto allow a CVMX app to intercept all packets from the\n"
+ "\t\tlinux kernel. You must specify pow_send_group along with\n"
+ "\t\tthis option." */
+
+char pow_send_list[128] = "";
+TUNABLE_STR("hw.octe.pow_send_list", pow_send_list, sizeof pow_send_list);
+/*
+ "\t\tComma separated list of ethernet devices that should use the\n"
+ "\t\tPOW for transmit instead of the actual ethernet hardware. This\n"
+ "\t\tis a per port version of always_use_pow. always_use_pow takes\n"
+ "\t\tprecedence over this list. For example, setting this to\n"
+ "\t\t\"eth2,spi3,spi7\" would cause these three devices to transmit\n"
+ "\t\tusing the pow_send_group." */
+
+
+static int disable_core_queueing = 1;
+TUNABLE_INT("hw.octe.disable_core_queueing", &disable_core_queueing);
+/*
+ "\t\tWhen set the networking core's tx_queue_len is set to zero. This\n"
+ "\t\tallows packets to be sent without lock contention in the packet scheduler\n"
+ "\t\tresulting in some cases in improved throughput.\n" */
+
+extern int octeon_is_simulation(void);
+
+/**
+ * Exported from the kernel so we can determine board information. It is
+ * passed by the bootloader to the kernel.
+ */
+extern cvmx_bootinfo_t *octeon_bootinfo;
+
+/**
+ * Periodic timer to check auto negotiation
+ */
+static struct callout cvm_oct_poll_timer;
+
+/**
+ * Array of every ethernet device owned by this driver indexed by
+ * the ipd input port number.
+ */
+struct ifnet *cvm_oct_device[TOTAL_NUMBER_OF_PORTS];
+
+/**
+ * Task to handle link status changes.
+ */
+static struct taskqueue *cvm_oct_link_taskq;
+
+/**
+ * Function to update link status.
+ */
+static void cvm_oct_update_link(void *context, int pending)
+{
+ cvm_oct_private_t *priv = (cvm_oct_private_t *)context;
+ struct ifnet *ifp = priv->ifp;
+ cvmx_helper_link_info_t link_info;
+
+ link_info.u64 = priv->link_info;
+
+ if (link_info.s.link_up) {
+ if_link_state_change(ifp, LINK_STATE_UP);
+ if (priv->queue != -1)
+ DEBUGPRINT("%s: %u Mbps %s duplex, port %2d, queue %2d\n",
+ if_name(ifp), link_info.s.speed,
+ (link_info.s.full_duplex) ? "Full" : "Half",
+ priv->port, priv->queue);
+ else
+ DEBUGPRINT("%s: %u Mbps %s duplex, port %2d, POW\n",
+ if_name(ifp), link_info.s.speed,
+ (link_info.s.full_duplex) ? "Full" : "Half",
+ priv->port);
+ } else {
+ if_link_state_change(ifp, LINK_STATE_DOWN);
+ DEBUGPRINT("%s: Link down\n", if_name(ifp));
+ }
+ priv->need_link_update = 0;
+}
+
+/**
+ * Periodic timer tick for slow management operations
+ *
+ * @param arg Device to check
+ */
+static void cvm_do_timer(void *arg)
+{
+ static int port;
+ static int updated;
+ if (port < CVMX_PIP_NUM_INPUT_PORTS) {
+ if (cvm_oct_device[port]) {
+ int queues_per_port;
+ int qos;
+ cvm_oct_private_t *priv = (cvm_oct_private_t *)cvm_oct_device[port]->if_softc;
+ if (priv->poll)
+ {
+ /* skip polling if we don't get the lock */
+ if (MDIO_TRYLOCK()) {
+ priv->poll(cvm_oct_device[port]);
+ MDIO_UNLOCK();
+
+ if (priv->need_link_update) {
+ updated++;
+ taskqueue_enqueue(cvm_oct_link_taskq, &priv->link_task);
+ }
+ }
+ }
+
+ queues_per_port = cvmx_pko_get_num_queues(port);
+ /* Drain any pending packets in the free list */
+ for (qos = 0; qos < queues_per_port; qos++) {
+ if (_IF_QLEN(&priv->tx_free_queue[qos]) > 0) {
+ IF_LOCK(&priv->tx_free_queue[qos]);
+ while (_IF_QLEN(&priv->tx_free_queue[qos]) > cvmx_fau_fetch_and_add32(priv->fau+qos*4, 0)) {
+ struct mbuf *m;
+
+ _IF_DEQUEUE(&priv->tx_free_queue[qos], m);
+ m_freem(m);
+ }
+ IF_UNLOCK(&priv->tx_free_queue[qos]);
+
+ /*
+ * XXX locking!
+ */
+ priv->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+ }
+ }
+#if 0
+ cvm_oct_device[port]->get_stats(cvm_oct_device[port]);
+#endif
+ }
+ port++;
+ /* Poll the next port in a 50th of a second.
+ This spreads the polling of ports out a little bit */
+ callout_reset(&cvm_oct_poll_timer, hz / 50, cvm_do_timer, NULL);
+ } else {
+ port = 0;
+ /* If any updates were made in this run, continue iterating at
+ * 1/50th of a second, so that if a link has merely gone down
+ * temporarily (e.g. because of interface reinitialization) it
+ * will not be forced to stay down for an entire second.
+ */
+ if (updated > 0) {
+ updated = 0;
+ callout_reset(&cvm_oct_poll_timer, hz / 50, cvm_do_timer, NULL);
+ } else {
+ /* All ports have been polled. Start the next iteration through
+ the ports in one second */
+ callout_reset(&cvm_oct_poll_timer, hz, cvm_do_timer, NULL);
+ }
+ }
+}
+
+
+/**
+ * Configure common hardware for all interfaces
+ */
+static void cvm_oct_configure_common_hw(device_t bus)
+{
+ struct octebus_softc *sc;
+ int error;
+ int rid;
+
+ sc = device_get_softc(bus);
+
+ /* Setup the FPA */
+ cvmx_fpa_enable();
+ cvm_oct_mem_fill_fpa(CVMX_FPA_PACKET_POOL, CVMX_FPA_PACKET_POOL_SIZE, num_packet_buffers);
+ cvm_oct_mem_fill_fpa(CVMX_FPA_WQE_POOL, CVMX_FPA_WQE_POOL_SIZE, num_packet_buffers);
+ if (CVMX_FPA_OUTPUT_BUFFER_POOL != CVMX_FPA_PACKET_POOL)
+ cvm_oct_mem_fill_fpa(CVMX_FPA_OUTPUT_BUFFER_POOL, CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE, 128);
+
+ if (USE_RED)
+ cvmx_helper_setup_red(num_packet_buffers/4, num_packet_buffers/8);
+
+ /* Enable the MII interface */
+ if (!octeon_is_simulation())
+ cvmx_write_csr(CVMX_SMI_EN, 1);
+
+ /* Register an IRQ hander for to receive POW interrupts */
+ rid = 0;
+ sc->sc_rx_irq = bus_alloc_resource(bus, SYS_RES_IRQ, &rid,
+ CVMX_IRQ_WORKQ0 + pow_receive_group,
+ CVMX_IRQ_WORKQ0 + pow_receive_group,
+ 1, RF_ACTIVE);
+ if (sc->sc_rx_irq == NULL) {
+ device_printf(bus, "could not allocate workq irq");
+ return;
+ }
+
+ error = bus_setup_intr(bus, sc->sc_rx_irq, INTR_TYPE_NET | INTR_MPSAFE,
+ cvm_oct_do_interrupt, NULL, cvm_oct_device,
+ NULL);
+ if (error != 0) {
+ device_printf(bus, "could not setup workq irq");
+ return;
+ }
+
+
+#ifdef SMP
+ if (USE_MULTICORE_RECEIVE) {
+ critical_enter();
+ {
+ int cpu;
+ for (cpu = 0; cpu < mp_maxid; cpu++) {
+ if (!CPU_ABSENT(cpu) &&
+ (cpu != PCPU_GET(cpuid))) {
+ cvmx_ciu_intx0_t en;
+ en.u64 = cvmx_read_csr(CVMX_CIU_INTX_EN0(cpu*2));
+ en.s.workq |= (1<<pow_receive_group);
+ cvmx_write_csr(CVMX_CIU_INTX_EN0(cpu*2), en.u64);
+ }
+ }
+ }
+ critical_exit();
+ }
+#endif
+}
+
+
+/**
+ * Free a work queue entry received in a intercept callback.
+ *
+ * @param work_queue_entry
+ * Work queue entry to free
+ * @return Zero on success, Negative on failure.
+ */
+int cvm_oct_free_work(void *work_queue_entry)
+{
+ cvmx_wqe_t *work = work_queue_entry;
+
+ int segments = work->word2.s.bufs;
+ cvmx_buf_ptr_t segment_ptr = work->packet_ptr;
+
+ while (segments--) {
+ cvmx_buf_ptr_t next_ptr = *(cvmx_buf_ptr_t *)cvmx_phys_to_ptr(segment_ptr.s.addr-8);
+ if (__predict_false(!segment_ptr.s.i))
+ cvmx_fpa_free(cvm_oct_get_buffer_ptr(segment_ptr), segment_ptr.s.pool, DONT_WRITEBACK(CVMX_FPA_PACKET_POOL_SIZE/128));
+ segment_ptr = next_ptr;
+ }
+ cvmx_fpa_free(work, CVMX_FPA_WQE_POOL, DONT_WRITEBACK(1));
+
+ return 0;
+}
+
+
+/**
+ * Module/ driver initialization. Creates the linux network
+ * devices.
+ *
+ * @return Zero on success
+ */
+int cvm_oct_init_module(device_t bus)
+{
+ device_t dev;
+ int ifnum;
+ int num_interfaces;
+ int interface;
+ int fau = FAU_NUM_PACKET_BUFFERS_TO_FREE;
+ int qos;
+
+ printf("cavium-ethernet: %s\n", OCTEON_SDK_VERSION_STRING);
+
+#if 0
+ cvm_oct_proc_initialize();
+#endif
+ cvm_oct_rx_initialize();
+ cvm_oct_configure_common_hw(bus);
+
+ cvmx_helper_initialize_packet_io_global();
+
+ /* Change the input group for all ports before input is enabled */
+ num_interfaces = cvmx_helper_get_number_of_interfaces();
+ for (interface = 0; interface < num_interfaces; interface++) {
+ int num_ports = cvmx_helper_ports_on_interface(interface);
+ int port;
+
+ for (port = cvmx_helper_get_ipd_port(interface, 0); port < cvmx_helper_get_ipd_port(interface, num_ports); port++) {
+ cvmx_pip_prt_tagx_t pip_prt_tagx;
+ pip_prt_tagx.u64 = cvmx_read_csr(CVMX_PIP_PRT_TAGX(port));
+ pip_prt_tagx.s.grp = pow_receive_group;
+ cvmx_write_csr(CVMX_PIP_PRT_TAGX(port), pip_prt_tagx.u64);
+ }
+ }
+
+ cvmx_helper_ipd_and_packet_input_enable();
+
+ memset(cvm_oct_device, 0, sizeof(cvm_oct_device));
+
+ cvm_oct_link_taskq = taskqueue_create("octe link", M_NOWAIT,
+ taskqueue_thread_enqueue, &cvm_oct_link_taskq);
+ taskqueue_start_threads(&cvm_oct_link_taskq, 1, PI_NET,
+ "octe link taskq");
+
+ /* Initialize the FAU used for counting packet buffers that need to be freed */
+ cvmx_fau_atomic_write32(FAU_NUM_PACKET_BUFFERS_TO_FREE, 0);
+
+ if ((pow_send_group != -1)) {
+ struct ifnet *ifp;
+
+ printf("\tConfiguring device for POW only access\n");
+ dev = BUS_ADD_CHILD(bus, 0, "pow", 0);
+ if (dev != NULL)
+ ifp = if_alloc(IFT_ETHER);
+ if (dev != NULL && ifp != NULL) {
+ /* Initialize the device private structure. */
+ cvm_oct_private_t *priv;
+
+ device_probe(dev);
+ priv = device_get_softc(dev);
+ priv->dev = dev;
+ priv->ifp = ifp;
+ priv->init = cvm_oct_common_init;
+ priv->imode = CVMX_HELPER_INTERFACE_MODE_DISABLED;
+ priv->port = CVMX_PIP_NUM_INPUT_PORTS;
+ priv->queue = -1;
+ TASK_INIT(&priv->link_task, 0, cvm_oct_update_link, priv);
+
+ device_set_desc(dev, "Cavium Octeon POW Ethernet\n");
+
+ ifp->if_softc = priv;
+
+ if (priv->init(ifp) < 0) {
+ printf("\t\tFailed to register ethernet device for POW\n");
+ panic("%s: need to free ifp.", __func__);
+ } else {
+ cvm_oct_device[CVMX_PIP_NUM_INPUT_PORTS] = ifp;
+ printf("\t\t%s: POW send group %d, receive group %d\n",
+ if_name(ifp), pow_send_group, pow_receive_group);
+ }
+ } else {
+ printf("\t\tFailed to allocate ethernet device for POW\n");
+ }
+ }
+
+ ifnum = 0;
+ num_interfaces = cvmx_helper_get_number_of_interfaces();
+ for (interface = 0; interface < num_interfaces; interface++) {
+ cvmx_helper_interface_mode_t imode = cvmx_helper_interface_get_mode(interface);
+ int num_ports = cvmx_helper_ports_on_interface(interface);
+ int port;
+
+ for (port = cvmx_helper_get_ipd_port(interface, 0); port < cvmx_helper_get_ipd_port(interface, num_ports); port++) {
+ cvm_oct_private_t *priv;
+ struct ifnet *ifp;
+
+ dev = BUS_ADD_CHILD(bus, 0, "octe", ifnum++);
+ if (dev != NULL)
+ ifp = if_alloc(IFT_ETHER);
+ if (dev == NULL || ifp == NULL) {
+ printf("\t\tFailed to allocate ethernet device for port %d\n", port);
+ continue;
+ }
+ /* XXX/juli set max send q len. */
+#if 0
+ if (disable_core_queueing)
+ ifp->tx_queue_len = 0;
+#endif
+
+ /* Initialize the device private structure. */
+ device_probe(dev);
+ priv = device_get_softc(dev);
+ priv->dev = dev;
+ priv->ifp = ifp;
+ priv->imode = imode;
+ priv->port = port;
+ priv->queue = cvmx_pko_get_base_queue(priv->port);
+ priv->fau = fau - cvmx_pko_get_num_queues(port) * 4;
+ for (qos = 0; qos < cvmx_pko_get_num_queues(port); qos++)
+ cvmx_fau_atomic_write32(priv->fau+qos*4, 0);
+ TASK_INIT(&priv->link_task, 0, cvm_oct_update_link, priv);
+
+ switch (priv->imode) {
+
+ /* These types don't support ports to IPD/PKO */
+ case CVMX_HELPER_INTERFACE_MODE_DISABLED:
+ case CVMX_HELPER_INTERFACE_MODE_PCIE:
+ case CVMX_HELPER_INTERFACE_MODE_PICMG:
+ break;
+
+ case CVMX_HELPER_INTERFACE_MODE_NPI:
+ priv->init = cvm_oct_common_init;
+ priv->uninit = cvm_oct_common_uninit;
+ device_set_desc(dev, "Cavium Octeon NPI Ethernet");
+ break;
+
+ case CVMX_HELPER_INTERFACE_MODE_XAUI:
+ priv->init = cvm_oct_xaui_init;
+ priv->uninit = cvm_oct_xaui_uninit;
+ device_set_desc(dev, "Cavium Octeon XAUI Ethernet");
+ break;
+
+ case CVMX_HELPER_INTERFACE_MODE_LOOP:
+ priv->init = cvm_oct_common_init;
+ priv->uninit = cvm_oct_common_uninit;
+ device_set_desc(dev, "Cavium Octeon LOOP Ethernet");
+ break;
+
+ case CVMX_HELPER_INTERFACE_MODE_SGMII:
+ priv->init = cvm_oct_sgmii_init;
+ priv->uninit = cvm_oct_sgmii_uninit;
+ device_set_desc(dev, "Cavium Octeon SGMII Ethernet");
+ break;
+
+ case CVMX_HELPER_INTERFACE_MODE_SPI:
+ priv->init = cvm_oct_spi_init;
+ priv->uninit = cvm_oct_spi_uninit;
+ device_set_desc(dev, "Cavium Octeon SPI Ethernet");
+ break;
+
+ case CVMX_HELPER_INTERFACE_MODE_RGMII:
+ priv->init = cvm_oct_rgmii_init;
+ priv->uninit = cvm_oct_rgmii_uninit;
+ device_set_desc(dev, "Cavium Octeon RGMII Ethernet");
+ break;
+
+ case CVMX_HELPER_INTERFACE_MODE_GMII:
+ priv->init = cvm_oct_rgmii_init;
+ priv->uninit = cvm_oct_rgmii_uninit;
+ device_set_desc(dev, "Cavium Octeon GMII Ethernet");
+ break;
+ }
+
+ ifp->if_softc = priv;
+
+ if (!priv->init) {
+ panic("%s: unsupported device type, need to free ifp.", __func__);
+ } else
+ if (priv->init(ifp) < 0) {
+ printf("\t\tFailed to register ethernet device for interface %d, port %d\n",
+ interface, priv->port);
+ panic("%s: init failed, need to free ifp.", __func__);
+ } else {
+ cvm_oct_device[priv->port] = ifp;
+ fau -= cvmx_pko_get_num_queues(priv->port) * sizeof(uint32_t);
+ }
+ }
+ }
+
+ if (INTERRUPT_LIMIT) {
+ /* Set the POW timer rate to give an interrupt at most INTERRUPT_LIMIT times per second */
+ cvmx_write_csr(CVMX_POW_WQ_INT_PC, octeon_bootinfo->eclock_hz/(INTERRUPT_LIMIT*16*256)<<8);
+
+ /* Enable POW timer interrupt. It will count when there are packets available */
+ cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0x1ful<<24);
+ } else {
+ /* Enable POW interrupt when our port has at least one packet */
+ cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0x1001);
+ }
+
+ callout_init(&cvm_oct_poll_timer, CALLOUT_MPSAFE);
+ callout_reset(&cvm_oct_poll_timer, hz, cvm_do_timer, NULL);
+
+ return 0;
+}
+
+
+/**
+ * Module / driver shutdown
+ *
+ * @return Zero on success
+ */
+void cvm_oct_cleanup_module(void)
+{
+ int port;
+
+ /* Disable POW interrupt */
+ cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0);
+
+ cvmx_ipd_disable();
+
+#if 0
+ /* Free the interrupt handler */
+ free_irq(8 + pow_receive_group, cvm_oct_device);
+#endif
+
+ callout_stop(&cvm_oct_poll_timer);
+ cvm_oct_rx_shutdown();
+ cvmx_pko_disable();
+
+ /* Free the ethernet devices */
+ for (port = 0; port < TOTAL_NUMBER_OF_PORTS; port++) {
+ if (cvm_oct_device[port]) {
+ cvm_oct_tx_shutdown(cvm_oct_device[port]);
+#if 0
+ unregister_netdev(cvm_oct_device[port]);
+ kfree(cvm_oct_device[port]);
+#else
+ panic("%s: need to detach and free interface.", __func__);
+#endif
+ cvm_oct_device[port] = NULL;
+ }
+ }
+
+ cvmx_pko_shutdown();
+#if 0
+ cvm_oct_proc_shutdown();
+#endif
+
+ cvmx_ipd_free_ptr();
+
+ /* Free the HW pools */
+ cvm_oct_mem_empty_fpa(CVMX_FPA_PACKET_POOL, CVMX_FPA_PACKET_POOL_SIZE, num_packet_buffers);
+ cvm_oct_mem_empty_fpa(CVMX_FPA_WQE_POOL, CVMX_FPA_WQE_POOL_SIZE, num_packet_buffers);
+ if (CVMX_FPA_OUTPUT_BUFFER_POOL != CVMX_FPA_PACKET_POOL)
+ cvm_oct_mem_empty_fpa(CVMX_FPA_OUTPUT_BUFFER_POOL, CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE, 128);
+}
diff --git a/sys/mips/cavium/octe/mv88e61xxphy.c b/sys/mips/cavium/octe/mv88e61xxphy.c
new file mode 100644
index 000000000000..6d018c7cb74c
--- /dev/null
+++ b/sys/mips/cavium/octe/mv88e61xxphy.c
@@ -0,0 +1,630 @@
+/*-
+ * Copyright (c) 2010 Juli Mallett <jmallett@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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Driver for the Marvell 88E61xx family of switch PHYs
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/errno.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/sysctl.h>
+
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <net/if_media.h>
+
+#include "miibus_if.h"
+
+#include "mv88e61xxphyreg.h"
+
+struct mv88e61xxphy_softc;
+
+struct mv88e61xxphy_port_softc {
+ struct mv88e61xxphy_softc *sc_switch;
+ unsigned sc_port;
+ unsigned sc_domain;
+ unsigned sc_vlan;
+ unsigned sc_priority;
+ unsigned sc_flags;
+};
+
+#define MV88E61XXPHY_PORT_FLAG_VTU_UPDATE (0x0001)
+
+struct mv88e61xxphy_softc {
+ device_t sc_dev;
+ struct mv88e61xxphy_port_softc sc_ports[MV88E61XX_PORTS];
+};
+
+enum mv88e61xxphy_vtu_membership_type {
+ MV88E61XXPHY_VTU_UNMODIFIED,
+ MV88E61XXPHY_VTU_UNTAGGED,
+ MV88E61XXPHY_VTU_TAGGED,
+ MV88E61XXPHY_VTU_DISCARDED,
+};
+
+enum mv88e61xxphy_sysctl_link_type {
+ MV88E61XXPHY_LINK_SYSCTL_DUPLEX,
+ MV88E61XXPHY_LINK_SYSCTL_LINK,
+ MV88E61XXPHY_LINK_SYSCTL_MEDIA,
+};
+
+enum mv88e61xxphy_sysctl_port_type {
+ MV88E61XXPHY_PORT_SYSCTL_DOMAIN,
+ MV88E61XXPHY_PORT_SYSCTL_VLAN,
+ MV88E61XXPHY_PORT_SYSCTL_PRIORITY,
+};
+
+/*
+ * Register access macros.
+ */
+#define MV88E61XX_READ(sc, phy, reg) \
+ MIIBUS_READREG(device_get_parent((sc)->sc_dev), (phy), (reg))
+
+#define MV88E61XX_WRITE(sc, phy, reg, val) \
+ MIIBUS_WRITEREG(device_get_parent((sc)->sc_dev), (phy), (reg), (val))
+
+#define MV88E61XX_READ_PORT(psc, reg) \
+ MV88E61XX_READ((psc)->sc_switch, MV88E61XX_PORT((psc)->sc_port), (reg))
+
+#define MV88E61XX_WRITE_PORT(psc, reg, val) \
+ MV88E61XX_WRITE((psc)->sc_switch, MV88E61XX_PORT((psc)->sc_port), (reg), (val))
+
+static int mv88e61xxphy_probe(device_t);
+static int mv88e61xxphy_attach(device_t);
+
+static void mv88e61xxphy_init(struct mv88e61xxphy_softc *);
+static void mv88e61xxphy_init_port(struct mv88e61xxphy_port_softc *);
+static void mv88e61xxphy_init_vtu(struct mv88e61xxphy_softc *);
+static int mv88e61xxphy_sysctl_link_proc(SYSCTL_HANDLER_ARGS);
+static int mv88e61xxphy_sysctl_port_proc(SYSCTL_HANDLER_ARGS);
+static void mv88e61xxphy_vtu_load(struct mv88e61xxphy_softc *, uint16_t);
+static void mv88e61xxphy_vtu_set_membership(struct mv88e61xxphy_softc *, unsigned, enum mv88e61xxphy_vtu_membership_type);
+static void mv88e61xxphy_vtu_wait(struct mv88e61xxphy_softc *);
+
+static int
+mv88e61xxphy_probe(device_t dev)
+{
+ uint16_t val;
+
+ val = MIIBUS_READREG(device_get_parent(dev), MV88E61XX_PORT(0),
+ MV88E61XX_PORT_REVISION);
+ switch (val >> 4) {
+ case 0x121:
+ device_set_desc(dev, "Marvell Link Street 88E6123 3-Port Gigabit Switch");
+ return (0);
+ case 0x161:
+ device_set_desc(dev, "Marvell Link Street 88E6161 6-Port Gigabit Switch");
+ return (0);
+ case 0x165:
+ device_set_desc(dev, "Marvell Link Street 88E6161 6-Port Advanced Gigabit Switch");
+ return (0);
+ default:
+ return (ENXIO);
+ }
+}
+
+static int
+mv88e61xxphy_attach(device_t dev)
+{
+ char portbuf[] = "N";
+ struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(dev);
+ struct sysctl_oid *tree = device_get_sysctl_tree(dev);
+ struct sysctl_oid_list *child = SYSCTL_CHILDREN(tree);
+ struct sysctl_oid *port_node, *portN_node;
+ struct sysctl_oid_list *port_tree, *portN_tree;
+ struct mv88e61xxphy_softc *sc;
+ unsigned port;
+
+ sc = device_get_softc(dev);
+ sc->sc_dev = dev;
+
+ /*
+ * Initialize port softcs.
+ */
+ for (port = 0; port < MV88E61XX_PORTS; port++) {
+ struct mv88e61xxphy_port_softc *psc;
+
+ psc = &sc->sc_ports[port];
+ psc->sc_switch = sc;
+ psc->sc_port = port;
+ psc->sc_domain = 0; /* One broadcast domain by default. */
+ psc->sc_vlan = port + 1; /* Tag VLANs by default. */
+ psc->sc_priority = 0; /* No default special priority. */
+ psc->sc_flags = 0;
+ }
+
+ /*
+ * Add per-port sysctl tree/handlers.
+ */
+ port_node = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "port",
+ CTLFLAG_RD, NULL, "Switch Ports");
+ port_tree = SYSCTL_CHILDREN(port_node);
+ for (port = 0; port < MV88E61XX_PORTS; port++) {
+ struct mv88e61xxphy_port_softc *psc;
+
+ psc = &sc->sc_ports[port];
+
+ portbuf[0] = '0' + port;
+ portN_node = SYSCTL_ADD_NODE(ctx, port_tree, OID_AUTO, portbuf,
+ CTLFLAG_RD, NULL, "Switch Port");
+ portN_tree = SYSCTL_CHILDREN(portN_node);
+
+ SYSCTL_ADD_PROC(ctx, portN_tree, OID_AUTO, "duplex",
+ CTLFLAG_RD | CTLTYPE_INT, psc,
+ MV88E61XXPHY_LINK_SYSCTL_DUPLEX,
+ mv88e61xxphy_sysctl_link_proc, "IU",
+ "Media duplex status (0 = half duplex; 1 = full duplex)");
+
+ SYSCTL_ADD_PROC(ctx, portN_tree, OID_AUTO, "link",
+ CTLFLAG_RD | CTLTYPE_INT, psc,
+ MV88E61XXPHY_LINK_SYSCTL_LINK,
+ mv88e61xxphy_sysctl_link_proc, "IU",
+ "Link status (0 = down; 1 = up)");
+
+ SYSCTL_ADD_PROC(ctx, portN_tree, OID_AUTO, "media",
+ CTLFLAG_RD | CTLTYPE_INT, psc,
+ MV88E61XXPHY_LINK_SYSCTL_MEDIA,
+ mv88e61xxphy_sysctl_link_proc, "IU",
+ "Media speed (0 = unknown; 10 = 10Mbps; 100 = 100Mbps; 1000 = 1Gbps)");
+
+ SYSCTL_ADD_PROC(ctx, portN_tree, OID_AUTO, "domain",
+ CTLFLAG_RW | CTLTYPE_INT, psc,
+ MV88E61XXPHY_PORT_SYSCTL_DOMAIN,
+ mv88e61xxphy_sysctl_port_proc, "IU",
+ "Broadcast domain (ports can only talk to other ports in the same domain)");
+
+ SYSCTL_ADD_PROC(ctx, portN_tree, OID_AUTO, "vlan",
+ CTLFLAG_RW | CTLTYPE_INT, psc,
+ MV88E61XXPHY_PORT_SYSCTL_VLAN,
+ mv88e61xxphy_sysctl_port_proc, "IU",
+ "Tag packets from/for this port with a given VLAN.");
+
+ SYSCTL_ADD_PROC(ctx, portN_tree, OID_AUTO, "priority",
+ CTLFLAG_RW | CTLTYPE_INT, psc,
+ MV88E61XXPHY_PORT_SYSCTL_PRIORITY,
+ mv88e61xxphy_sysctl_port_proc, "IU",
+ "Default packet priority for this port.");
+ }
+
+ mv88e61xxphy_init(sc);
+
+ return (0);
+}
+
+static void
+mv88e61xxphy_init(struct mv88e61xxphy_softc *sc)
+{
+ unsigned port;
+ uint16_t val;
+ unsigned i;
+
+ /* Disable all ports. */
+ for (port = 0; port < MV88E61XX_PORTS; port++) {
+ struct mv88e61xxphy_port_softc *psc;
+
+ psc = &sc->sc_ports[port];
+
+ val = MV88E61XX_READ_PORT(psc, MV88E61XX_PORT_CONTROL);
+ val &= ~0x3;
+ MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_CONTROL, val);
+ }
+
+ DELAY(2000);
+
+ /* Reset the switch. */
+ MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_CONTROL, 0xc400);
+ for (i = 0; i < 100; i++) {
+ val = MV88E61XX_READ(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_STATUS);
+ if ((val & 0xc800) == 0xc800)
+ break;
+ DELAY(10);
+ }
+ if (i == 100) {
+ device_printf(sc->sc_dev, "%s: switch reset timed out.\n", __func__);
+ return;
+ }
+
+ /* Disable PPU. */
+ MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_CONTROL, 0x0000);
+
+ /* Configure host port and send monitor frames to it. */
+ MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_MONITOR,
+ (MV88E61XX_HOST_PORT << 12) | (MV88E61XX_HOST_PORT << 8) |
+ (MV88E61XX_HOST_PORT << 4));
+
+ /* Disable remote management. */
+ MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_REMOTE_MGMT, 0x0000);
+
+ /* Send all specifically-addressed frames to the host port. */
+ MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL2, MV88E61XX_GLOBAL2_MANAGE_2X, 0xffff);
+ MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL2, MV88E61XX_GLOBAL2_MANAGE_0X, 0xffff);
+
+ /* Remove provider-supplied tag and use it for switching. */
+ MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL2, MV88E61XX_GLOBAL2_CONTROL2,
+ MV88E61XX_GLOBAL2_CONTROL2_REMOVE_PTAG);
+
+ /* Configure all ports. */
+ for (port = 0; port < MV88E61XX_PORTS; port++) {
+ struct mv88e61xxphy_port_softc *psc;
+
+ psc = &sc->sc_ports[port];
+ mv88e61xxphy_init_port(psc);
+ }
+
+ /* Reprogram VLAN table (VTU.) */
+ mv88e61xxphy_init_vtu(sc);
+
+ /* Enable all ports. */
+ for (port = 0; port < MV88E61XX_PORTS; port++) {
+ struct mv88e61xxphy_port_softc *psc;
+
+ psc = &sc->sc_ports[port];
+
+ val = MV88E61XX_READ_PORT(psc, MV88E61XX_PORT_CONTROL);
+ val |= 0x3;
+ MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_CONTROL, val);
+ }
+}
+
+static void
+mv88e61xxphy_init_port(struct mv88e61xxphy_port_softc *psc)
+{
+ struct mv88e61xxphy_softc *sc;
+ unsigned allow_mask;
+
+ sc = psc->sc_switch;
+
+ /* Set media type and flow control. */
+ if (psc->sc_port != MV88E61XX_HOST_PORT) {
+ /* Don't force any media type or flow control. */
+ MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_FORCE_MAC, 0x0003);
+ } else {
+ /* Make CPU port 1G FDX. */
+ MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_FORCE_MAC, 0x003e);
+ }
+
+ /* Don't limit flow control pauses. */
+ MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_PAUSE_CONTROL, 0x0000);
+
+ /* Set various port functions per Linux. */
+ if (psc->sc_port != MV88E61XX_HOST_PORT) {
+ MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_CONTROL, 0x04bc);
+ } else {
+ /*
+ * Send frames for unknown unicast and multicast groups to
+ * host, too.
+ */
+ MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_CONTROL, 0x063f);
+ }
+
+ if (psc->sc_port != MV88E61XX_HOST_PORT) {
+ /* Disable trunking. */
+ MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_CONTROL2, 0x0000);
+ } else {
+ /* Disable trunking and send learn messages to host. */
+ MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_CONTROL2, 0x8000);
+ }
+
+ /*
+ * Port-based VLAN map; isolates MAC tables and forces ports to talk
+ * only to the host.
+ *
+ * Always allow the host to send to all ports and allow all ports to
+ * send to the host.
+ */
+ if (psc->sc_port != MV88E61XX_HOST_PORT) {
+ allow_mask = 1 << MV88E61XX_HOST_PORT;
+ } else {
+ allow_mask = (1 << MV88E61XX_PORTS) - 1;
+ allow_mask &= ~(1 << MV88E61XX_HOST_PORT);
+ }
+ MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_VLAN_MAP,
+ (psc->sc_domain << 12) | allow_mask);
+
+ /* VLAN tagging. Set default priority and VLAN tag (or none.) */
+ MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_VLAN,
+ (psc->sc_priority << 14) | psc->sc_vlan);
+
+ if (psc->sc_port == MV88E61XX_HOST_PORT) {
+ /* Set provider ingress tag. */
+ MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_PROVIDER_PROTO,
+ ETHERTYPE_VLAN);
+
+ /* Set provider egress tag. */
+ MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_ETHER_PROTO,
+ ETHERTYPE_VLAN);
+
+ /* Use secure 802.1q mode and accept only tagged frames. */
+ MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_FILTER,
+ MV88E61XX_PORT_FILTER_MAP_DEST |
+ MV88E61XX_PORT_FILTER_8021Q_SECURE |
+ MV88E61XX_PORT_FILTER_DISCARD_UNTAGGED);
+ } else {
+ /* Don't allow tagged frames. */
+ MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_FILTER,
+ MV88E61XX_PORT_FILTER_MAP_DEST |
+ MV88E61XX_PORT_FILTER_DISCARD_TAGGED);
+ }
+}
+
+static void
+mv88e61xxphy_init_vtu(struct mv88e61xxphy_softc *sc)
+{
+ unsigned port;
+
+ /*
+ * Start flush of the VTU.
+ */
+ mv88e61xxphy_vtu_wait(sc);
+ MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_VTU_OP,
+ MV88E61XX_GLOBAL_VTU_OP_BUSY | MV88E61XX_GLOBAL_VTU_OP_OP_FLUSH);
+
+ /*
+ * Queue each port's VLAN to be programmed.
+ */
+ for (port = 0; port < MV88E61XX_PORTS; port++) {
+ struct mv88e61xxphy_port_softc *psc;
+
+ psc = &sc->sc_ports[port];
+ psc->sc_flags &= ~MV88E61XXPHY_PORT_FLAG_VTU_UPDATE;
+ if (psc->sc_vlan == 0)
+ continue;
+ psc->sc_flags |= MV88E61XXPHY_PORT_FLAG_VTU_UPDATE;
+ }
+
+ /*
+ * Program each VLAN that is in use.
+ */
+ for (port = 0; port < MV88E61XX_PORTS; port++) {
+ struct mv88e61xxphy_port_softc *psc;
+
+ psc = &sc->sc_ports[port];
+ if ((psc->sc_flags & MV88E61XXPHY_PORT_FLAG_VTU_UPDATE) == 0)
+ continue;
+ mv88e61xxphy_vtu_load(sc, psc->sc_vlan);
+ }
+
+ /*
+ * Wait for last pending VTU operation to complete.
+ */
+ mv88e61xxphy_vtu_wait(sc);
+}
+
+static int
+mv88e61xxphy_sysctl_link_proc(SYSCTL_HANDLER_ARGS)
+{
+ struct mv88e61xxphy_port_softc *psc = arg1;
+ enum mv88e61xxphy_sysctl_link_type type = arg2;
+ uint16_t val;
+ unsigned out;
+
+ val = MV88E61XX_READ_PORT(psc, MV88E61XX_PORT_STATUS);
+ switch (type) {
+ case MV88E61XXPHY_LINK_SYSCTL_DUPLEX:
+ if ((val & MV88E61XX_PORT_STATUS_DUPLEX) != 0)
+ out = 1;
+ else
+ out = 0;
+ break;
+ case MV88E61XXPHY_LINK_SYSCTL_LINK:
+ if ((val & MV88E61XX_PORT_STATUS_LINK) != 0)
+ out = 1;
+ else
+ out = 0;
+ break;
+ case MV88E61XXPHY_LINK_SYSCTL_MEDIA:
+ switch (val & MV88E61XX_PORT_STATUS_MEDIA) {
+ case MV88E61XX_PORT_STATUS_MEDIA_10M:
+ out = 10;
+ break;
+ case MV88E61XX_PORT_STATUS_MEDIA_100M:
+ out = 100;
+ break;
+ case MV88E61XX_PORT_STATUS_MEDIA_1G:
+ out = 1000;
+ break;
+ default:
+ out = 0;
+ break;
+ }
+ break;
+ default:
+ return (EINVAL);
+ }
+ return (sysctl_handle_int(oidp, NULL, out, req));
+}
+
+static int
+mv88e61xxphy_sysctl_port_proc(SYSCTL_HANDLER_ARGS)
+{
+ struct mv88e61xxphy_port_softc *psc = arg1;
+ enum mv88e61xxphy_sysctl_port_type type = arg2;
+ struct mv88e61xxphy_softc *sc = psc->sc_switch;
+ unsigned max, val, *valp;
+ int error;
+
+ switch (type) {
+ case MV88E61XXPHY_PORT_SYSCTL_DOMAIN:
+ valp = &psc->sc_domain;
+ max = 0xf;
+ break;
+ case MV88E61XXPHY_PORT_SYSCTL_VLAN:
+ valp = &psc->sc_vlan;
+ max = 0x1000;
+ break;
+ case MV88E61XXPHY_PORT_SYSCTL_PRIORITY:
+ valp = &psc->sc_priority;
+ max = 3;
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ val = *valp;
+ error = sysctl_handle_int(oidp, &val, 0, req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+
+ /* Bounds check value. */
+ if (val >= max)
+ return (EINVAL);
+
+ /* Reinitialize switch with new value. */
+ *valp = val;
+ mv88e61xxphy_init(sc);
+
+ return (0);
+}
+
+static void
+mv88e61xxphy_vtu_load(struct mv88e61xxphy_softc *sc, uint16_t vid)
+{
+ unsigned port;
+
+ /*
+ * Wait for previous operation to complete.
+ */
+ mv88e61xxphy_vtu_wait(sc);
+
+ /*
+ * Set VID.
+ */
+ MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_VTU_VID,
+ MV88E61XX_GLOBAL_VTU_VID_VALID | vid);
+
+ /*
+ * Add ports to this VTU.
+ */
+ for (port = 0; port < MV88E61XX_PORTS; port++) {
+ struct mv88e61xxphy_port_softc *psc;
+
+ psc = &sc->sc_ports[port];
+ if (psc->sc_vlan == vid) {
+ /*
+ * Send this port its VLAN traffic untagged.
+ */
+ psc->sc_flags &= ~MV88E61XXPHY_PORT_FLAG_VTU_UPDATE;
+ mv88e61xxphy_vtu_set_membership(sc, port, MV88E61XXPHY_VTU_UNTAGGED);
+ } else if (psc->sc_port == MV88E61XX_HOST_PORT) {
+ /*
+ * The host sees all VLANs tagged.
+ */
+ mv88e61xxphy_vtu_set_membership(sc, port, MV88E61XXPHY_VTU_TAGGED);
+ } else {
+ /*
+ * This port isn't on this VLAN.
+ */
+ mv88e61xxphy_vtu_set_membership(sc, port, MV88E61XXPHY_VTU_DISCARDED);
+ }
+ }
+
+ /*
+ * Start adding this entry.
+ */
+ MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_VTU_OP,
+ MV88E61XX_GLOBAL_VTU_OP_BUSY |
+ MV88E61XX_GLOBAL_VTU_OP_OP_VTU_LOAD);
+}
+
+static void
+mv88e61xxphy_vtu_set_membership(struct mv88e61xxphy_softc *sc, unsigned port,
+ enum mv88e61xxphy_vtu_membership_type type)
+{
+ unsigned shift, reg;
+ uint16_t bits;
+ uint16_t val;
+
+ switch (type) {
+ case MV88E61XXPHY_VTU_UNMODIFIED:
+ bits = 0;
+ break;
+ case MV88E61XXPHY_VTU_UNTAGGED:
+ bits = 1;
+ break;
+ case MV88E61XXPHY_VTU_TAGGED:
+ bits = 2;
+ break;
+ case MV88E61XXPHY_VTU_DISCARDED:
+ bits = 3;
+ break;
+ default:
+ return;
+ }
+
+ if (port < 4) {
+ reg = MV88E61XX_GLOBAL_VTU_DATA_P0P3;
+ shift = port * 4;
+ } else {
+ reg = MV88E61XX_GLOBAL_VTU_DATA_P4P5;
+ shift = (port - 4) * 4;
+ }
+
+ val = MV88E61XX_READ(sc, MV88E61XX_GLOBAL, reg);
+ val |= bits << shift;
+ MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL, reg, val);
+}
+
+static void
+mv88e61xxphy_vtu_wait(struct mv88e61xxphy_softc *sc)
+{
+ uint16_t val;
+
+ for (;;) {
+ val = MV88E61XX_READ(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_VTU_OP);
+ if ((val & MV88E61XX_GLOBAL_VTU_OP_BUSY) == 0)
+ return;
+ }
+}
+
+static device_method_t mv88e61xxphy_methods[] = {
+ /* device interface */
+ DEVMETHOD(device_probe, mv88e61xxphy_probe),
+ DEVMETHOD(device_attach, mv88e61xxphy_attach),
+ DEVMETHOD(device_detach, bus_generic_detach),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+
+ { 0, 0 }
+};
+
+static devclass_t mv88e61xxphy_devclass;
+
+static driver_t mv88e61xxphy_driver = {
+ "mv88e61xxphy",
+ mv88e61xxphy_methods,
+ sizeof(struct mv88e61xxphy_softc)
+};
+
+DRIVER_MODULE(mv88e61xxphy, octe, mv88e61xxphy_driver, mv88e61xxphy_devclass, 0, 0);
diff --git a/sys/mips/cavium/octe/mv88e61xxphyreg.h b/sys/mips/cavium/octe/mv88e61xxphyreg.h
new file mode 100644
index 000000000000..420741f2fe21
--- /dev/null
+++ b/sys/mips/cavium/octe/mv88e61xxphyreg.h
@@ -0,0 +1,149 @@
+/*-
+ * Copyright (c) 2010 Juli Mallett <jmallett@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.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Register definitions for Marvell MV88E61XX
+ *
+ * Note that names and definitions were gleaned from Linux and U-Boot patches
+ * released by Marvell, often by looking at contextual use of the registers
+ * involved, and may not be representative of the full functionality of those
+ * registers and are certainly not an exhaustive enumeration of registers.
+ *
+ * For an exhaustive enumeration of registers, check out the QD-DSDT package
+ * included in the Marvell ARM Feroceon Board Support Package for Linux.
+ */
+
+#ifndef _MIPS_CAVIUM_OCTE_MV88E61XXPHYREG_H_
+#define _MIPS_CAVIUM_OCTE_MV88E61XXPHYREG_H_
+
+/*
+ * Port addresses & per-port registers.
+ */
+#define MV88E61XX_PORT(x) (0x10 + (x))
+#define MV88E61XX_HOST_PORT (5)
+#define MV88E61XX_PORTS (6)
+
+#define MV88E61XX_PORT_STATUS (0x00)
+#define MV88E61XX_PORT_FORCE_MAC (0x01)
+#define MV88E61XX_PORT_PAUSE_CONTROL (0x02)
+#define MV88E61XX_PORT_REVISION (0x03)
+#define MV88E61XX_PORT_CONTROL (0x04)
+#define MV88E61XX_PORT_CONTROL2 (0x05)
+#define MV88E61XX_PORT_VLAN_MAP (0x06)
+#define MV88E61XX_PORT_VLAN (0x07)
+#define MV88E61XX_PORT_FILTER (0x08)
+#define MV88E61XX_PORT_EGRESS_CONTROL (0x09)
+#define MV88E61XX_PORT_EGRESS_CONTROL2 (0x0a)
+#define MV88E61XX_PORT_PORT_LEARN (0x0b)
+#define MV88E61XX_PORT_ATU_CONTROL (0x0c)
+#define MV88E61XX_PORT_PRIORITY_CONTROL (0x0d)
+#define MV88E61XX_PORT_ETHER_PROTO (0x0f)
+#define MV88E61XX_PORT_PROVIDER_PROTO (0x1a)
+#define MV88E61XX_PORT_PRIORITY_MAP (0x18)
+#define MV88E61XX_PORT_PRIORITY_MAP2 (0x19)
+
+/*
+ * Fields and values in each register.
+ */
+#define MV88E61XX_PORT_STATUS_MEDIA (0x0300)
+#define MV88E61XX_PORT_STATUS_MEDIA_10M (0x0000)
+#define MV88E61XX_PORT_STATUS_MEDIA_100M (0x0100)
+#define MV88E61XX_PORT_STATUS_MEDIA_1G (0x0200)
+#define MV88E61XX_PORT_STATUS_DUPLEX (0x0400)
+#define MV88E61XX_PORT_STATUS_LINK (0x0800)
+#define MV88E61XX_PORT_STATUS_FC (0x8000)
+
+#define MV88E61XX_PORT_CONTROL_DOUBLE_TAG (0x0200)
+
+#define MV88E61XX_PORT_FILTER_MAP_DEST (0x0080)
+#define MV88E61XX_PORT_FILTER_DISCARD_UNTAGGED (0x0100)
+#define MV88E61XX_PORT_FILTER_DISCARD_TAGGED (0x0200)
+#define MV88E61XX_PORT_FILTER_8021Q_MODE (0x0c00)
+#define MV88E61XX_PORT_FILTER_8021Q_DISABLED (0x0000)
+#define MV88E61XX_PORT_FILTER_8021Q_FALLBACK (0x0400)
+#define MV88E61XX_PORT_FILTER_8021Q_CHECK (0x0800)
+#define MV88E61XX_PORT_FILTER_8021Q_SECURE (0x0c00)
+
+/*
+ * Global address & global registers.
+ */
+#define MV88E61XX_GLOBAL (0x1b)
+
+#define MV88E61XX_GLOBAL_STATUS (0x00)
+#define MV88E61XX_GLOBAL_CONTROL (0x04)
+#define MV88E61XX_GLOBAL_VTU_OP (0x05)
+#define MV88E61XX_GLOBAL_VTU_VID (0x06)
+#define MV88E61XX_GLOBAL_VTU_DATA_P0P3 (0x07)
+#define MV88E61XX_GLOBAL_VTU_DATA_P4P5 (0x08)
+#define MV88E61XX_GLOBAL_ATU_CONTROL (0x0a)
+#define MV88E61XX_GLOBAL_PRIORITY_MAP (0x18)
+#define MV88E61XX_GLOBAL_MONITOR (0x1a)
+#define MV88E61XX_GLOBAL_REMOTE_MGMT (0x1c)
+#define MV88E61XX_GLOBAL_STATS (0x1d)
+
+/*
+ * Fields and values in each register.
+ */
+#define MV88E61XX_GLOBAL_VTU_OP_BUSY (0x8000)
+#define MV88E61XX_GLOBAL_VTU_OP_OP (0x7000)
+#define MV88E61XX_GLOBAL_VTU_OP_OP_FLUSH (0x1000)
+#define MV88E61XX_GLOBAL_VTU_OP_OP_VTU_LOAD (0x3000)
+
+#define MV88E61XX_GLOBAL_VTU_VID_VALID (0x1000)
+
+/*
+ * Second global address & second global registers.
+ */
+#define MV88E61XX_GLOBAL2 (0x1c)
+
+#define MV88E61XX_GLOBAL2_MANAGE_2X (0x02)
+#define MV88E61XX_GLOBAL2_MANAGE_0X (0x03)
+#define MV88E61XX_GLOBAL2_CONTROL2 (0x05)
+#define MV88E61XX_GLOBAL2_TRUNK_MASK (0x07)
+#define MV88E61XX_GLOBAL2_TRUNK_MAP (0x08)
+#define MV88E61XX_GLOBAL2_RATELIMIT (0x09)
+#define MV88E61XX_GLOBAL2_VLAN_CONTROL (0x0b)
+#define MV88E61XX_GLOBAL2_MAC_ADDRESS (0x0d)
+
+/*
+ * Fields and values in each register.
+ */
+#define MV88E61XX_GLOBAL2_CONTROL2_DOUBLE_USE (0x8000)
+#define MV88E61XX_GLOBAL2_CONTROL2_LOOP_PREVENT (0x4000)
+#define MV88E61XX_GLOBAL2_CONTROL2_FLOW_MESSAGE (0x2000)
+#define MV88E61XX_GLOBAL2_CONTROL2_FLOOD_BC (0x1000)
+#define MV88E61XX_GLOBAL2_CONTROL2_REMOVE_PTAG (0x0800)
+#define MV88E61XX_GLOBAL2_CONTROL2_AGE_INT (0x0400)
+#define MV88E61XX_GLOBAL2_CONTROL2_FLOW_TAG (0x0200)
+#define MV88E61XX_GLOBAL2_CONTROL2_ALWAYS_VTU (0x0100)
+#define MV88E61XX_GLOBAL2_CONTROL2_FORCE_FC_PRI (0x0080)
+#define MV88E61XX_GLOBAL2_CONTROL2_FC_PRI (0x0070)
+#define MV88E61XX_GLOBAL2_CONTROL2_MGMT_TO_HOST (0x0008)
+#define MV88E61XX_GLOBAL2_CONTROL2_MGMT_PRI (0x0007)
+
+#endif /* !_MIPS_CAVIUM_OCTE_MV88E61XXPHYREG_H_ */
diff --git a/sys/mips/cavium/octe/octe.c b/sys/mips/cavium/octe/octe.c
new file mode 100644
index 000000000000..2e0573459dbc
--- /dev/null
+++ b/sys/mips/cavium/octe/octe.c
@@ -0,0 +1,522 @@
+/*-
+ * Copyright (c) 2010 Juli Mallett <jmallett@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.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Cavium Octeon Ethernet devices.
+ *
+ * XXX This file should be moved to if_octe.c
+ * XXX The driver may have sufficient locking but we need locking to protect
+ * the interfaces presented here, right?
+ */
+
+#include "opt_inet.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
+#include <sys/mbuf.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/sysctl.h>
+
+#include <net/bpf.h>
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+#include <net/if_var.h>
+#include <net/if_vlan_var.h>
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#endif
+
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+
+#include "wrapper-cvmx-includes.h"
+#include "cavium-ethernet.h"
+
+#include "ethernet-common.h"
+#include "ethernet-defines.h"
+#include "ethernet-mdio.h"
+#include "ethernet-tx.h"
+
+#include "miibus_if.h"
+
+#define OCTE_TX_LOCK(priv) mtx_lock(&(priv)->tx_mtx)
+#define OCTE_TX_UNLOCK(priv) mtx_unlock(&(priv)->tx_mtx)
+
+static int octe_probe(device_t);
+static int octe_attach(device_t);
+static int octe_detach(device_t);
+static int octe_shutdown(device_t);
+
+static int octe_miibus_readreg(device_t, int, int);
+static int octe_miibus_writereg(device_t, int, int, int);
+
+static void octe_init(void *);
+static void octe_stop(void *);
+static void octe_start(struct ifnet *);
+
+static int octe_mii_medchange(struct ifnet *);
+static void octe_mii_medstat(struct ifnet *, struct ifmediareq *);
+
+static int octe_medchange(struct ifnet *);
+static void octe_medstat(struct ifnet *, struct ifmediareq *);
+
+static int octe_ioctl(struct ifnet *, u_long, caddr_t);
+
+static device_method_t octe_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, octe_probe),
+ DEVMETHOD(device_attach, octe_attach),
+ DEVMETHOD(device_detach, octe_detach),
+ DEVMETHOD(device_shutdown, octe_shutdown),
+
+ /* MII interface */
+ DEVMETHOD(miibus_readreg, octe_miibus_readreg),
+ DEVMETHOD(miibus_writereg, octe_miibus_writereg),
+
+ { 0, 0 }
+};
+
+static driver_t octe_driver = {
+ "octe",
+ octe_methods,
+ sizeof (cvm_oct_private_t),
+};
+
+static devclass_t octe_devclass;
+
+DRIVER_MODULE(octe, octebus, octe_driver, octe_devclass, 0, 0);
+DRIVER_MODULE(miibus, octe, miibus_driver, miibus_devclass, 0, 0);
+
+static driver_t pow_driver = {
+ "pow",
+ octe_methods,
+ sizeof (cvm_oct_private_t),
+};
+
+static devclass_t pow_devclass;
+
+DRIVER_MODULE(pow, octebus, pow_driver, pow_devclass, 0, 0);
+
+static int
+octe_probe(device_t dev)
+{
+ return (0);
+}
+
+static int
+octe_attach(device_t dev)
+{
+ struct ifnet *ifp;
+ cvm_oct_private_t *priv;
+ device_t child;
+ unsigned qos;
+ int error;
+
+ priv = device_get_softc(dev);
+ ifp = priv->ifp;
+
+ if_initname(ifp, device_get_name(dev), device_get_unit(dev));
+
+ if (priv->phy_id != -1) {
+ if (priv->phy_device == NULL) {
+ error = mii_phy_probe(dev, &priv->miibus, octe_mii_medchange,
+ octe_mii_medstat);
+ if (error != 0)
+ device_printf(dev, "missing phy %u\n", priv->phy_id);
+ } else {
+ child = device_add_child(dev, priv->phy_device, -1);
+ if (child == NULL)
+ device_printf(dev, "missing phy %u device %s\n", priv->phy_id, priv->phy_device);
+ }
+ }
+
+ if (priv->miibus == NULL) {
+ ifmedia_init(&priv->media, 0, octe_medchange, octe_medstat);
+
+ ifmedia_add(&priv->media, IFM_ETHER | IFM_AUTO, 0, NULL);
+ ifmedia_set(&priv->media, IFM_ETHER | IFM_AUTO);
+ }
+
+ /*
+ * XXX
+ * We don't support programming the multicast filter right now, although it
+ * ought to be easy enough. (Presumably it's just a matter of putting
+ * multicast addresses in the CAM?)
+ */
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST | IFF_ALLMULTI;
+ ifp->if_init = octe_init;
+ ifp->if_ioctl = octe_ioctl;
+ ifp->if_start = octe_start;
+
+ priv->if_flags = ifp->if_flags;
+
+ mtx_init(&priv->tx_mtx, ifp->if_xname, "octe tx send queue", MTX_DEF);
+
+ for (qos = 0; qos < 16; qos++) {
+ mtx_init(&priv->tx_free_queue[qos].ifq_mtx, ifp->if_xname, "octe tx free queue", MTX_DEF);
+ IFQ_SET_MAXLEN(&priv->tx_free_queue[qos], MAX_OUT_QUEUE_DEPTH);
+ }
+
+ ether_ifattach(ifp, priv->mac);
+
+ ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header);
+ ifp->if_capabilities = IFCAP_VLAN_MTU | IFCAP_HWCSUM;
+ ifp->if_capenable = ifp->if_capabilities;
+ ifp->if_hwassist = CSUM_TCP | CSUM_UDP;
+
+ OCTE_TX_LOCK(priv);
+ IFQ_SET_MAXLEN(&ifp->if_snd, MAX_OUT_QUEUE_DEPTH);
+ ifp->if_snd.ifq_drv_maxlen = MAX_OUT_QUEUE_DEPTH;
+ IFQ_SET_READY(&ifp->if_snd);
+ OCTE_TX_UNLOCK(priv);
+
+ return (bus_generic_attach(dev));
+}
+
+static int
+octe_detach(device_t dev)
+{
+ return (0);
+}
+
+static int
+octe_shutdown(device_t dev)
+{
+ return (octe_detach(dev));
+}
+
+static int
+octe_miibus_readreg(device_t dev, int phy, int reg)
+{
+ cvm_oct_private_t *priv;
+
+ priv = device_get_softc(dev);
+
+ /*
+ * Try interface-specific MII routine.
+ */
+ if (priv->mdio_read != NULL)
+ return (priv->mdio_read(priv->ifp, phy, reg));
+
+ /*
+ * Try generic MII routine.
+ */
+ if (phy != priv->phy_id)
+ return (0);
+
+ return (cvm_oct_mdio_read(priv->ifp, phy, reg));
+}
+
+static int
+octe_miibus_writereg(device_t dev, int phy, int reg, int val)
+{
+ cvm_oct_private_t *priv;
+
+ priv = device_get_softc(dev);
+
+ /*
+ * Try interface-specific MII routine.
+ */
+ if (priv->mdio_write != NULL) {
+ priv->mdio_write(priv->ifp, phy, reg, val);
+ return (0);
+ }
+
+ /*
+ * Try generic MII routine.
+ */
+ KASSERT(phy == priv->phy_id,
+ ("write to phy %u but our phy is %u", phy, priv->phy_id));
+ cvm_oct_mdio_write(priv->ifp, phy, reg, val);
+
+ return (0);
+}
+
+static void
+octe_init(void *arg)
+{
+ struct ifnet *ifp;
+ cvm_oct_private_t *priv;
+
+ priv = arg;
+ ifp = priv->ifp;
+
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0)
+ octe_stop(priv);
+
+ if (priv->open != NULL)
+ priv->open(ifp);
+
+ if (((ifp->if_flags ^ priv->if_flags) & (IFF_ALLMULTI | IFF_MULTICAST | IFF_PROMISC)) != 0)
+ cvm_oct_common_set_multicast_list(ifp);
+
+ cvm_oct_common_set_mac_address(ifp, IF_LLADDR(ifp));
+
+ if (priv->poll != NULL)
+ priv->poll(ifp);
+ if (priv->miibus != NULL)
+ mii_mediachg(device_get_softc(priv->miibus));
+
+ ifp->if_drv_flags |= IFF_DRV_RUNNING;
+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+}
+
+static void
+octe_stop(void *arg)
+{
+ struct ifnet *ifp;
+ cvm_oct_private_t *priv;
+
+ priv = arg;
+ ifp = priv->ifp;
+
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
+ return;
+
+ if (priv->stop != NULL)
+ priv->stop(ifp);
+
+ ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+}
+
+static void
+octe_start(struct ifnet *ifp)
+{
+ cvm_oct_private_t *priv;
+ struct mbuf *m;
+ int error;
+
+ priv = ifp->if_softc;
+
+ if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != IFF_DRV_RUNNING)
+ return;
+
+ OCTE_TX_LOCK(priv);
+ while (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) {
+ IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
+
+ OCTE_TX_UNLOCK(priv);
+
+ if (priv->queue != -1) {
+ error = cvm_oct_xmit(m, ifp);
+ } else {
+ error = cvm_oct_xmit_pow(m, ifp);
+ }
+
+ if (error != 0) {
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ return;
+ }
+
+ OCTE_TX_LOCK(priv);
+ }
+ OCTE_TX_UNLOCK(priv);
+}
+
+static int
+octe_mii_medchange(struct ifnet *ifp)
+{
+ cvm_oct_private_t *priv;
+ struct mii_data *mii;
+
+ priv = ifp->if_softc;
+ mii = device_get_softc(priv->miibus);
+
+ if (mii->mii_instance) {
+ struct mii_softc *miisc;
+
+ LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
+ mii_phy_reset(miisc);
+ }
+ mii_mediachg(mii);
+
+ return (0);
+}
+
+static void
+octe_mii_medstat(struct ifnet *ifp, struct ifmediareq *ifm)
+{
+ cvm_oct_private_t *priv;
+ struct mii_data *mii;
+
+ priv = ifp->if_softc;
+ mii = device_get_softc(priv->miibus);
+
+ mii_pollstat(mii);
+ ifm->ifm_active = mii->mii_media_active;
+ ifm->ifm_status = mii->mii_media_status;
+}
+
+static int
+octe_medchange(struct ifnet *ifp)
+{
+ return (ENOTSUP);
+}
+
+static void
+octe_medstat(struct ifnet *ifp, struct ifmediareq *ifm)
+{
+ cvm_oct_private_t *priv;
+ cvmx_helper_link_info_t link_info;
+
+ priv = ifp->if_softc;
+
+ ifm->ifm_status = IFM_AVALID;
+ ifm->ifm_active = IFT_ETHER;
+
+ if (priv->poll == NULL)
+ return;
+ priv->poll(ifp);
+
+ link_info.u64 = priv->link_info;
+
+ if (!link_info.s.link_up)
+ return;
+
+ ifm->ifm_status |= IFM_ACTIVE;
+
+ switch (link_info.s.speed) {
+ case 10:
+ ifm->ifm_active |= IFM_10_T;
+ break;
+ case 100:
+ ifm->ifm_active |= IFM_100_TX;
+ break;
+ case 1000:
+ ifm->ifm_active |= IFM_1000_T;
+ break;
+ case 10000:
+ ifm->ifm_active |= IFM_10G_T;
+ break;
+ }
+
+ if (link_info.s.full_duplex)
+ ifm->ifm_active |= IFM_FDX;
+ else
+ ifm->ifm_active |= IFM_HDX;
+}
+
+static int
+octe_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+ cvm_oct_private_t *priv;
+ struct mii_data *mii;
+ struct ifreq *ifr;
+#ifdef INET
+ struct ifaddr *ifa;
+#endif
+ int error;
+
+ priv = ifp->if_softc;
+ ifr = (struct ifreq *)data;
+#ifdef INET
+ ifa = (struct ifaddr *)data;
+#endif
+
+ switch (cmd) {
+ case SIOCSIFADDR:
+#ifdef INET
+ /*
+ * Avoid reinitialization unless it's necessary.
+ */
+ if (ifa->ifa_addr->sa_family == AF_INET) {
+ ifp->if_flags |= IFF_UP;
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
+ octe_init(priv);
+ arp_ifinit(ifp, ifa);
+
+ return (0);
+ }
+#endif
+ error = ether_ioctl(ifp, cmd, data);
+ if (error != 0)
+ return (error);
+ return (0);
+
+ case SIOCSIFFLAGS:
+ if (ifp->if_flags == priv->if_flags)
+ return (0);
+ if ((ifp->if_flags & IFF_UP) != 0) {
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
+ octe_init(priv);
+ } else {
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0)
+ octe_stop(priv);
+ }
+ priv->if_flags = ifp->if_flags;
+ return (0);
+
+ case SIOCSIFCAP:
+ /*
+ * Just change the capabilities in software, currently none
+ * require reprogramming hardware, they just toggle whether we
+ * make use of already-present facilities in software.
+ */
+ ifp->if_capenable = ifr->ifr_reqcap;
+ return (0);
+
+ case SIOCSIFMTU:
+ error = cvm_oct_common_change_mtu(ifp, ifr->ifr_mtu);
+ if (error != 0)
+ return (EINVAL);
+ return (0);
+
+ case SIOCSIFMEDIA:
+ case SIOCGIFMEDIA:
+ if (priv->miibus != NULL) {
+ mii = device_get_softc(priv->miibus);
+ error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd);
+ if (error != 0)
+ return (error);
+ return (0);
+ }
+ error = ifmedia_ioctl(ifp, ifr, &priv->media, cmd);
+ if (error != 0)
+ return (error);
+ return (0);
+
+ default:
+ error = ether_ioctl(ifp, cmd, data);
+ if (error != 0)
+ return (error);
+ return (0);
+ }
+}
diff --git a/sys/mips/cavium/octe/octebus.c b/sys/mips/cavium/octe/octebus.c
new file mode 100644
index 000000000000..ec67cf6761f8
--- /dev/null
+++ b/sys/mips/cavium/octe/octebus.c
@@ -0,0 +1,123 @@
+/*-
+ * Copyright (c) 2010 Juli Mallett <jmallett@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.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Cavium Octeon Ethernet pseudo-bus attachment.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
+#include <sys/mbuf.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/sysctl.h>
+
+#include "ethernet-common.h"
+
+#include "octebusvar.h"
+
+static void octebus_identify(driver_t *drv, device_t parent);
+static int octebus_probe(device_t dev);
+static int octebus_attach(device_t dev);
+static int octebus_detach(device_t dev);
+static int octebus_shutdown(device_t dev);
+
+static device_method_t octebus_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_identify, octebus_identify),
+ DEVMETHOD(device_probe, octebus_probe),
+ DEVMETHOD(device_attach, octebus_attach),
+ DEVMETHOD(device_detach, octebus_detach),
+ DEVMETHOD(device_shutdown, octebus_shutdown),
+
+ /* Bus interface. */
+ DEVMETHOD(bus_add_child, bus_generic_add_child),
+
+ { 0, 0 }
+};
+
+static driver_t octebus_driver = {
+ "octebus",
+ octebus_methods,
+ sizeof (struct octebus_softc),
+};
+
+static devclass_t octebus_devclass;
+
+DRIVER_MODULE(octebus, ciu, octebus_driver, octebus_devclass, 0, 0);
+
+static void
+octebus_identify(driver_t *drv, device_t parent)
+{
+ BUS_ADD_CHILD(parent, 0, "octebus", 0);
+}
+
+static int
+octebus_probe(device_t dev)
+{
+ if (device_get_unit(dev) != 0)
+ return (ENXIO);
+ device_set_desc(dev, "Cavium Octeon Ethernet pseudo-bus");
+ return (0);
+}
+
+static int
+octebus_attach(device_t dev)
+{
+ struct octebus_softc *sc;
+ int rv;
+
+ sc = device_get_softc(dev);
+ sc->sc_dev = dev;
+
+ rv = cvm_oct_init_module(dev);
+ if (rv != 0)
+ return (ENXIO);
+
+ return (0);
+}
+
+static int
+octebus_detach(device_t dev)
+{
+ cvm_oct_cleanup_module();
+ return (0);
+}
+
+static int
+octebus_shutdown(device_t dev)
+{
+ return (octebus_detach(dev));
+}
diff --git a/sys/mips/cavium/octe/octebusvar.h b/sys/mips/cavium/octe/octebusvar.h
new file mode 100644
index 000000000000..061c02018407
--- /dev/null
+++ b/sys/mips/cavium/octe/octebusvar.h
@@ -0,0 +1,41 @@
+/*-
+ * Copyright (c) 2010 Juli Mallett <jmallett@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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _CAVIUM_OCTE_OCTEBUSVAR_H_
+#define _CAVIUM_OCTE_OCTEBUSVAR_H_
+
+struct octebus_softc {
+ device_t sc_dev;
+
+ struct resource *sc_rx_irq;
+
+ struct resource *sc_rgmii_irq;
+ struct resource *sc_spi_irq;
+};
+
+#endif /* !_CAVIUM_OCTE_OCTEBUSVAR_H_ */
diff --git a/sys/mips/cavium/octe/wrapper-cvmx-includes.h b/sys/mips/cavium/octe/wrapper-cvmx-includes.h
new file mode 100644
index 000000000000..bc695052e715
--- /dev/null
+++ b/sys/mips/cavium/octe/wrapper-cvmx-includes.h
@@ -0,0 +1,50 @@
+/*************************************************************************
+Copyright (c) 2003-2007 Cavium Networks (support@cavium.com). All rights
+reserved.
+
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * 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.
+
+ * Neither the name of Cavium Networks nor the names of
+ its contributors may be used to endorse or promote products
+ derived from this software without specific prior written
+ permission.
+
+This Software, including technical data, may be subject to U.S. export control laws, including the U.S. Export Administration Act and its associated regulations, and may be subject to export or import regulations in other countries.
+
+TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+
+*************************************************************************/
+/* $FreeBSD$ */
+
+#ifndef __WRAPPER_CVMX_INCLUDES_H__
+#define __WRAPPER_CVMX_INCLUDES_H__
+
+#include <contrib/octeon-sdk/cvmx.h>
+#include <contrib/octeon-sdk/cvmx-version.h>
+#include <contrib/octeon-sdk/cvmx-atomic.h>
+#include <contrib/octeon-sdk/cvmx-ciu.h>
+#include <contrib/octeon-sdk/cvmx-pip.h>
+#include <contrib/octeon-sdk/cvmx-ipd.h>
+#include <contrib/octeon-sdk/cvmx-pko.h>
+#include <contrib/octeon-sdk/cvmx-pow.h>
+#include <contrib/octeon-sdk/cvmx-gmx.h>
+#include <contrib/octeon-sdk/cvmx-spi.h>
+#include <contrib/octeon-sdk/cvmx-bootmem.h>
+#include <contrib/octeon-sdk/cvmx-app-init.h>
+#include <contrib/octeon-sdk/cvmx-helper.h>
+#include <contrib/octeon-sdk/cvmx-helper-board.h>
+#include <contrib/octeon-sdk/cvmx-interrupt.h>
+
+#endif
diff --git a/sys/mips/cavium/octeon_ds1337.c b/sys/mips/cavium/octeon_ds1337.c
new file mode 100644
index 000000000000..b79d7342f0bb
--- /dev/null
+++ b/sys/mips/cavium/octeon_ds1337.c
@@ -0,0 +1,213 @@
+/***********************license start***************
+ * Copyright (c) 2003-2008 Cavium Networks (support@cavium.com). All rights
+ * reserved.
+ *
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * 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.
+ *
+ * * Neither the name of Cavium Networks nor the names of
+ * its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written
+ * permission.
+ *
+ * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+ * AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS
+ * OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH
+ * RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY
+ * REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT
+ * DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES
+ * OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR
+ * PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET
+ * POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT
+ * OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+ *
+ *
+ * For any questions regarding licensing please contact marketing@caviumnetworks.com
+ *
+ ***********************license end**************************************/
+
+
+
+
+
+
+/**
+ * @file
+ *
+ * Interface to the EBH-30xx specific devices
+ *
+ * <hr>$Revision: 41586 $<hr>
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/timespec.h>
+#include <sys/clock.h>
+#include <sys/libkern.h>
+
+#include <contrib/octeon-sdk/cvmx.h>
+#include <contrib/octeon-sdk/cvmx-cn3010-evb-hs5.h>
+#include <contrib/octeon-sdk/cvmx-twsi.h>
+
+#define CT_CHECK(_expr, _msg) \
+ do { \
+ if (_expr) { \
+ cvmx_dprintf("Warning: RTC has invalid %s field\n", (_msg)); \
+ rc = -1; \
+ } \
+ } while(0);
+
+static int validate_ct_struct(struct clocktime *ct)
+{
+ int rc = 0;
+
+ if (!ct)
+ return -1;
+
+ CT_CHECK(ct->sec < 0 || ct->sec > 60, "second"); /* + Leap sec */
+ CT_CHECK(ct->min < 0 || ct->min > 59, "minute");
+ CT_CHECK(ct->hour < 0 || ct->hour > 23, "hour");
+ CT_CHECK(ct->day < 1 || ct->day > 31, "day");
+ CT_CHECK(ct->dow < 0 || ct->dow > 6, "day of week");
+ CT_CHECK(ct->mon < 0 || ct->mon > 11, "month");
+ CT_CHECK(ct->year < 0 || ct->year > 200,"year");
+
+ return rc;
+}
+
+/*
+ * Board-specifc RTC read
+ * Time is expressed in seconds from epoch (Jan 1 1970 at 00:00:00 UTC)
+ * and converted internally to calendar format.
+ */
+uint32_t cvmx_rtc_ds1337_read(void)
+{
+ int i, retry;
+ uint8_t reg[8];
+ uint8_t sec;
+ struct clocktime ct;
+ struct timespec ts;
+
+
+ memset(&reg, 0, sizeof(reg));
+ memset(&ct, 0, sizeof(ct));
+
+ for(retry=0; retry<2; retry++)
+ {
+ /* Lockless read: detects the infrequent roll-over and retries */
+ reg[0] = cvmx_twsi_read8(CVMX_RTC_DS1337_ADDR, 0x0);
+ for(i=1; i<7; i++)
+ reg[i] = cvmx_twsi_read8_cur_addr(CVMX_RTC_DS1337_ADDR);
+
+ sec = cvmx_twsi_read8(CVMX_RTC_DS1337_ADDR, 0x0);
+ if ((sec & 0xf) == (reg[0] & 0xf))
+ break; /* Time did not roll-over, value is correct */
+ }
+
+ ct.sec = bcd2bin(reg[0] & 0x7f);
+ ct.min = bcd2bin(reg[1] & 0x7f);
+ ct.hour = bcd2bin(reg[2] & 0x3f);
+ if ((reg[2] & 0x40) && (reg[2] & 0x20)) /* AM/PM format and is PM time */
+ {
+ ct.hour = (ct.hour + 12) % 24;
+ }
+ ct.dow = (reg[3] & 0x7) - 1; /* Day of week field is 0..6 */
+ ct.day = bcd2bin(reg[4] & 0x3f);
+ ct.mon = bcd2bin(reg[5] & 0x1f) - 1; /* Month field is 0..11 */
+ ct.year = ((reg[5] & 0x80) ? 100 : 0) + bcd2bin(reg[6]);
+
+
+ if (validate_ct_struct(&ct))
+ cvmx_dprintf("Warning: RTC calendar is not configured properly\n");
+
+ if (clock_ct_to_ts(&ct, &ts) != 0) {
+ cvmx_dprintf("Warning: RTC calendar is not configured properly\n");
+ return 0;
+ }
+
+ return ts.tv_sec;
+}
+
+/*
+ * Board-specific RTC write
+ * Time returned is in seconds from epoch (Jan 1 1970 at 00:00:00 UTC)
+ */
+int cvmx_rtc_ds1337_write(uint32_t time)
+{
+ struct clocktime ct;
+ struct timespec ts;
+ int i, rc, retry;
+ uint8_t reg[8];
+ uint8_t sec;
+
+ ts.tv_sec = time;
+ ts.tv_nsec = 0;
+
+ clock_ts_to_ct(&ts, &ct);
+
+ if (validate_ct_struct(&ct))
+ {
+ cvmx_dprintf("Error: RTC was passed wrong calendar values, write failed\n");
+ goto ct_invalid;
+ }
+
+ reg[0] = bin2bcd(ct.sec);
+ reg[1] = bin2bcd(ct.min);
+ reg[2] = bin2bcd(ct.hour); /* Force 0..23 format even if using AM/PM */
+ reg[3] = bin2bcd(ct.dow + 1);
+ reg[4] = bin2bcd(ct.day);
+ reg[5] = bin2bcd(ct.mon + 1);
+ if (ct.year >= 100) /* Set century bit*/
+ {
+ reg[5] |= 0x80;
+ }
+ reg[6] = bin2bcd(ct.year % 100);
+
+ /* Lockless write: detects the infrequent roll-over and retries */
+ for(retry=0; retry<2; retry++)
+ {
+ rc = 0;
+ for(i=0; i<7; i++)
+ {
+ rc |= cvmx_twsi_write8(CVMX_RTC_DS1337_ADDR, i, reg[i]);
+ }
+
+ sec = cvmx_twsi_read8(CVMX_RTC_DS1337_ADDR, 0x0);
+ if ((sec & 0xf) == (reg[0] & 0xf))
+ break; /* Time did not roll-over, value is correct */
+ }
+
+ return (rc ? -1 : 0);
+
+ ct_invalid:
+ return -1;
+}
+
+#ifdef CVMX_RTC_DEBUG
+
+void cvmx_rtc_ds1337_dump_state(void)
+{
+ int i = 0;
+
+ printf("RTC:\n");
+ printf("%d : %02X ", i, cvmx_twsi_read8(CVMX_RTC_DS1337_ADDR, 0x0));
+ for(i=1; i<16; i++) {
+ printf("%02X ", cvmx_twsi_read8_cur_addr(CVMX_RTC_DS1337_ADDR));
+ }
+ printf("\n");
+}
+
+#endif /* CVMX_RTC_DEBUG */
diff --git a/sys/mips/cavium/octeon_ebt3000_cf.c b/sys/mips/cavium/octeon_ebt3000_cf.c
new file mode 100644
index 000000000000..7955d193e502
--- /dev/null
+++ b/sys/mips/cavium/octeon_ebt3000_cf.c
@@ -0,0 +1,704 @@
+/***********************license start***************
+ * Copyright (c) 2003-2008 Cavium Networks (support@cavium.com). All rights
+ * reserved.
+ *
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * 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.
+ *
+ * * Neither the name of Cavium Networks nor the names of
+ * its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written
+ * permission.
+ *
+ * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+ * AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS
+ * OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH
+ * RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY
+ * REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT
+ * DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES
+ * OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR
+ * PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET
+ * POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT
+ * OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+ *
+ *
+ * For any questions regarding licensing please contact marketing@caviumnetworks.com
+ *
+ ***********************license end**************************************/
+
+/*
+ * octeon_ebt3000_cf.c
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/bio.h>
+#include <sys/systm.h>
+#include <sys/sysctl.h>
+#include <sys/ata.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/power.h>
+#include <sys/smp.h>
+#include <sys/time.h>
+#include <sys/timetc.h>
+#include <sys/malloc.h>
+
+#include <geom/geom.h>
+
+#include <machine/clock.h>
+#include <machine/locore.h>
+#include <machine/md_var.h>
+#include <machine/cpuregs.h>
+
+#include <mips/cavium/octeon_pcmap_regs.h>
+
+#include <contrib/octeon-sdk/cvmx.h>
+
+/* ATA Commands */
+#define CMD_READ_SECTOR 0x20
+#define CMD_WRITE_SECTOR 0x30
+#define CMD_IDENTIFY 0xEC
+
+/* The ATA Task File */
+#define TF_DATA 0x00
+#define TF_ERROR 0x01
+#define TF_PRECOMP 0x01
+#define TF_SECTOR_COUNT 0x02
+#define TF_SECTOR_NUMBER 0x03
+#define TF_CYL_LSB 0x04
+#define TF_CYL_MSB 0x05
+#define TF_DRV_HEAD 0x06
+#define TF_STATUS 0x07
+#define TF_COMMAND 0x07
+
+/* Status Register */
+#define STATUS_BSY 0x80 /* Drive is busy */
+#define STATUS_RDY 0x40 /* Drive is ready */
+#define STATUS_DF 0x20 /* Device fault */
+#define STATUS_DRQ 0x08 /* Data can be transferred */
+
+/* Miscelaneous */
+#define SECTOR_SIZE 512
+#define WAIT_DELAY 1000
+#define NR_TRIES 1000
+#define SWAP_SHORT(x) ((x << 8) | (x >> 8))
+#define MODEL_STR_SIZE 40
+
+/* XXX */
+extern cvmx_bootinfo_t *octeon_bootinfo;
+
+/* Globals */
+int bus_width;
+void *base_addr;
+
+/* Device softc */
+struct cf_priv {
+
+ device_t dev;
+ struct drive_param *drive_param;
+
+ struct bio_queue_head cf_bq;
+ struct g_geom *cf_geom;
+ struct g_provider *cf_provider;
+
+};
+
+/* Device parameters */
+struct drive_param{
+ union {
+ char buf[SECTOR_SIZE];
+ struct ata_params driveid;
+ } u;
+
+ char model[MODEL_STR_SIZE];
+ uint32_t nr_sectors;
+ uint16_t sector_size;
+ uint16_t heads;
+ uint16_t tracks;
+ uint16_t sec_track;
+
+} drive_param;
+
+/* GEOM class implementation */
+static g_access_t cf_access;
+static g_start_t cf_start;
+static g_ioctl_t cf_ioctl;
+
+struct g_class g_cf_class = {
+ .name = "CF",
+ .version = G_VERSION,
+ .start = cf_start,
+ .access = cf_access,
+ .ioctl = cf_ioctl,
+};
+
+DECLARE_GEOM_CLASS(g_cf_class, g_cf);
+
+/* Device methods */
+static int cf_probe(device_t);
+static void cf_identify(driver_t *, device_t);
+static int cf_attach(device_t);
+static int cf_attach_geom(void *, int);
+
+/* ATA methods */
+static int cf_cmd_identify(void);
+static int cf_cmd_write(uint32_t, uint32_t, void *);
+static int cf_cmd_read(uint32_t, uint32_t, void *);
+static int cf_wait_busy(void);
+static int cf_send_cmd(uint32_t, uint8_t);
+static void cf_attach_geom_proxy(void *arg, int flag);
+
+/* Miscelenous */
+static void cf_swap_ascii(unsigned char[], char[]);
+
+
+/* ------------------------------------------------------------------- *
+ * cf_access() *
+ * ------------------------------------------------------------------- */
+static int cf_access (struct g_provider *pp, int r, int w, int e)
+{
+
+ pp->sectorsize = drive_param.sector_size;
+ pp->stripesize = drive_param.heads * drive_param.sec_track * drive_param.sector_size;
+ pp->mediasize = pp->stripesize * drive_param.tracks;
+
+ return (0);
+}
+
+
+/* ------------------------------------------------------------------- *
+ * cf_start() *
+ * ------------------------------------------------------------------- */
+static void cf_start (struct bio *bp)
+{
+ int error;
+
+ /*
+ * Handle actual I/O requests. The request is passed down through
+ * the bio struct.
+ */
+
+ if(bp->bio_cmd & BIO_GETATTR) {
+ if (g_handleattr_int(bp, "GEOM::fwsectors", drive_param.sec_track))
+ return;
+ if (g_handleattr_int(bp, "GEOM::fwheads", drive_param.heads))
+ return;
+ g_io_deliver(bp, ENOIOCTL);
+ return;
+ }
+
+ if ((bp->bio_cmd & (BIO_READ | BIO_WRITE))) {
+
+ if (bp->bio_cmd & BIO_READ) {
+ error = cf_cmd_read(bp->bio_length / drive_param.sector_size,
+ bp->bio_offset / drive_param.sector_size, bp->bio_data);
+ } else if (bp->bio_cmd & BIO_WRITE) {
+ error = cf_cmd_write(bp->bio_length / drive_param.sector_size,
+ bp->bio_offset/drive_param.sector_size, bp->bio_data);
+ } else {
+ printf("%s: unrecognized bio_cmd %x.\n", __func__, bp->bio_cmd);
+ error = ENOTSUP;
+ }
+
+ if (error != 0) {
+ g_io_deliver(bp, error);
+ return;
+ }
+
+ bp->bio_resid = 0;
+ bp->bio_completed = bp->bio_length;
+ g_io_deliver(bp, 0);
+ }
+}
+
+
+static int cf_ioctl (struct g_provider *pp, u_long cmd, void *data, int fflag, struct thread *td)
+{
+ return (0);
+}
+
+
+/* ------------------------------------------------------------------- *
+ * cf_cmd_read() *
+ * ------------------------------------------------------------------- *
+ *
+ * Read nr_sectors from the device starting from start_sector.
+ */
+static int cf_cmd_read (uint32_t nr_sectors, uint32_t start_sector, void *buf)
+{
+ unsigned long lba;
+ uint32_t count;
+ uint16_t *ptr_16;
+ uint8_t *ptr_8;
+ int error;
+
+//#define OCTEON_VISUAL_CF_0 1
+#ifdef OCTEON_VISUAL_CF_0
+ octeon_led_write_char(0, 'R');
+#endif
+ ptr_8 = (uint8_t*)buf;
+ ptr_16 = (uint16_t*)buf;
+ lba = start_sector;
+
+
+ while (nr_sectors--) {
+ error = cf_send_cmd(lba, CMD_READ_SECTOR);
+ if (error != 0) {
+ printf("%s: cf_send_cmd(CMD_READ_SECTOR) failed: %d\n", __func__, error);
+ return (error);
+ }
+
+ if (bus_width == 8) {
+ volatile uint8_t *task_file = (volatile uint8_t*)base_addr;
+ volatile uint8_t dummy;
+ for (count = 0; count < SECTOR_SIZE; count++) {
+ *ptr_8++ = task_file[TF_DATA];
+ if ((count & 0xf) == 0) dummy = task_file[TF_STATUS];
+ }
+ } else {
+ volatile uint16_t *task_file = (volatile uint16_t*)base_addr;
+ volatile uint16_t dummy;
+ for (count = 0; count < SECTOR_SIZE; count+=2) {
+ uint16_t temp;
+ temp = task_file[TF_DATA];
+ *ptr_16++ = SWAP_SHORT(temp);
+ if ((count & 0xf) == 0) dummy = task_file[TF_STATUS/2];
+ }
+ }
+
+ lba ++;
+ }
+#ifdef OCTEON_VISUAL_CF_0
+ octeon_led_write_char(0, ' ');
+#endif
+ return (0);
+}
+
+
+/* ------------------------------------------------------------------- *
+ * cf_cmd_write() *
+ * ------------------------------------------------------------------- *
+ *
+ * Write nr_sectors to the device starting from start_sector.
+ */
+static int cf_cmd_write (uint32_t nr_sectors, uint32_t start_sector, void *buf)
+{
+ uint32_t lba;
+ uint32_t count;
+ uint16_t *ptr_16;
+ uint8_t *ptr_8;
+ int error;
+
+//#define OCTEON_VISUAL_CF_1 1
+#ifdef OCTEON_VISUAL_CF_1
+ octeon_led_write_char(1, 'W');
+#endif
+ lba = start_sector;
+ ptr_8 = (uint8_t*)buf;
+ ptr_16 = (uint16_t*)buf;
+
+ while (nr_sectors--) {
+ error = cf_send_cmd(lba, CMD_WRITE_SECTOR);
+ if (error != 0) {
+ printf("%s: cf_send_cmd(CMD_WRITE_SECTOR) failed: %d\n", __func__, error);
+ return (error);
+ }
+
+ if (bus_width == 8) {
+ volatile uint8_t *task_file;
+ volatile uint8_t dummy;
+
+ task_file = (volatile uint8_t *) base_addr;
+ for (count = 0; count < SECTOR_SIZE; count++) {
+ task_file[TF_DATA] = *ptr_8++;
+ if ((count & 0xf) == 0) dummy = task_file[TF_STATUS];
+ }
+ } else {
+ volatile uint16_t *task_file;
+ volatile uint16_t dummy;
+
+ task_file = (volatile uint16_t *) base_addr;
+ for (count = 0; count < SECTOR_SIZE; count+=2) {
+ uint16_t temp = *ptr_16++;
+ task_file[TF_DATA] = SWAP_SHORT(temp);
+ if ((count & 0xf) == 0) dummy = task_file[TF_STATUS/2];
+ }
+ }
+
+ lba ++;
+ }
+#ifdef OCTEON_VISUAL_CF_1
+ octeon_led_write_char(1, ' ');
+#endif
+ return (0);
+}
+
+
+/* ------------------------------------------------------------------- *
+ * cf_cmd_identify() *
+ * ------------------------------------------------------------------- *
+ *
+ * Read parameters and other information from the drive and store
+ * it in the drive_param structure
+ *
+ */
+static int cf_cmd_identify (void)
+{
+ int count;
+ uint8_t status;
+ int error;
+
+ if (bus_width == 8) {
+ volatile uint8_t *task_file;
+
+ task_file = (volatile uint8_t *) base_addr;
+
+ while ((status = task_file[TF_STATUS]) & STATUS_BSY) {
+ DELAY(WAIT_DELAY);
+ }
+
+ task_file[TF_SECTOR_COUNT] = 0;
+ task_file[TF_SECTOR_NUMBER] = 0;
+ task_file[TF_CYL_LSB] = 0;
+ task_file[TF_CYL_MSB] = 0;
+ task_file[TF_DRV_HEAD] = 0;
+ task_file[TF_COMMAND] = CMD_IDENTIFY;
+
+ error = cf_wait_busy();
+ if (error == 0) {
+ for (count = 0; count < SECTOR_SIZE; count++)
+ drive_param.u.buf[count] = task_file[TF_DATA];
+ }
+ } else {
+ volatile uint16_t *task_file;
+
+ task_file = (volatile uint16_t *) base_addr;
+
+ while ((status = (task_file[TF_STATUS/2]>>8)) & STATUS_BSY) {
+ DELAY(WAIT_DELAY);
+ }
+
+ task_file[TF_SECTOR_COUNT/2] = 0; /* this includes TF_SECTOR_NUMBER */
+ task_file[TF_CYL_LSB/2] = 0; /* this includes TF_CYL_MSB */
+ task_file[TF_DRV_HEAD/2] = 0 | (CMD_IDENTIFY<<8); /* this includes TF_COMMAND */
+
+ error = cf_wait_busy();
+ if (error == 0) {
+ for (count = 0; count < SECTOR_SIZE; count+=2) {
+ uint16_t temp;
+ temp = task_file[TF_DATA];
+
+ /* endianess will be swapped below */
+ drive_param.u.buf[count] = (temp & 0xff);
+ drive_param.u.buf[count+1] = (temp & 0xff00)>>8;
+ }
+ }
+ }
+ if (error != 0) {
+ printf("%s: identify failed: %d\n", __func__, error);
+ return (error);
+ }
+
+ cf_swap_ascii(drive_param.u.driveid.model, drive_param.model);
+
+ drive_param.sector_size = 512; //= SWAP_SHORT (drive_param.u.driveid.sector_bytes);
+ drive_param.heads = SWAP_SHORT (drive_param.u.driveid.current_heads);
+ drive_param.tracks = SWAP_SHORT (drive_param.u.driveid.current_cylinders);
+ drive_param.sec_track = SWAP_SHORT (drive_param.u.driveid.current_sectors);
+ drive_param.nr_sectors = (uint32_t)SWAP_SHORT (drive_param.u.driveid.lba_size_1) |
+ ((uint32_t)SWAP_SHORT (drive_param.u.driveid.lba_size_2));
+
+ return (0);
+}
+
+
+/* ------------------------------------------------------------------- *
+ * cf_send_cmd() *
+ * ------------------------------------------------------------------- *
+ *
+ * Send command to read/write one sector specified by lba.
+ *
+ */
+static int cf_send_cmd (uint32_t lba, uint8_t cmd)
+{
+ uint8_t status;
+
+ if (bus_width == 8) {
+ volatile uint8_t *task_file;
+
+ task_file = (volatile uint8_t *) base_addr;
+
+ while ( (status = task_file[TF_STATUS]) & STATUS_BSY) {
+ DELAY(WAIT_DELAY);
+ }
+
+ task_file[TF_SECTOR_COUNT] = 1;
+ task_file[TF_SECTOR_NUMBER] = (lba & 0xff);
+ task_file[TF_CYL_LSB] = ((lba >> 8) & 0xff);
+ task_file[TF_CYL_MSB] = ((lba >> 16) & 0xff);
+ task_file[TF_DRV_HEAD] = ((lba >> 24) & 0xff) | 0xe0;
+ task_file[TF_COMMAND] = cmd;
+
+ } else {
+ volatile uint16_t *task_file;
+
+ task_file = (volatile uint16_t *) base_addr;
+
+ while ( (status = (task_file[TF_STATUS/2]>>8)) & STATUS_BSY) {
+ DELAY(WAIT_DELAY);
+ }
+
+ task_file[TF_SECTOR_COUNT/2] = 1 | ((lba & 0xff) << 8);
+ task_file[TF_CYL_LSB/2] = ((lba >> 8) & 0xff) | (((lba >> 16) & 0xff) << 8);
+ task_file[TF_DRV_HEAD/2] = (((lba >> 24) & 0xff) | 0xe0) | (cmd << 8);
+
+ }
+
+ return (cf_wait_busy());
+}
+
+/* ------------------------------------------------------------------- *
+ * cf_wait_busy() *
+ * ------------------------------------------------------------------- *
+ *
+ * Wait until the drive finishes a given command and data is
+ * ready to be transferred. This is done by repeatedly checking
+ * the BSY bit of the status register. When the controller is ready for
+ * data transfer, it clears the BSY bit and sets the DRQ bit.
+ *
+ * If the DF bit is ever set, we return error.
+ *
+ * This code originally spun on DRQ. If that behavior turns out to be
+ * necessary, a flag can be added or this function can be called
+ * repeatedly as long as it is returning ENXIO.
+ */
+static int cf_wait_busy (void)
+{
+ uint8_t status;
+
+//#define OCTEON_VISUAL_CF_2 1
+#ifdef OCTEON_VISUAL_CF_2
+ static int where0 = 0;
+
+ octeon_led_run_wheel(&where0, 2);
+#endif
+
+ if (bus_width == 8) {
+ volatile uint8_t *task_file;
+ task_file = (volatile uint8_t *)base_addr;
+
+ status = task_file[TF_STATUS];
+ while ((status & STATUS_BSY) == STATUS_BSY) {
+ if ((status & STATUS_DF) != 0) {
+ printf("%s: device fault (status=%x)\n", __func__, status);
+ return (EIO);
+ }
+ DELAY(WAIT_DELAY);
+ status = task_file[TF_STATUS];
+ }
+ } else {
+ volatile uint16_t *task_file;
+ task_file = (volatile uint16_t *)base_addr;
+
+ status = task_file[TF_STATUS/2]>>8;
+ while ((status & STATUS_BSY) == STATUS_BSY) {
+ if ((status & STATUS_DF) != 0) {
+ printf("%s: device fault (status=%x)\n", __func__, status);
+ return (EIO);
+ }
+ DELAY(WAIT_DELAY);
+ status = (uint8_t)(task_file[TF_STATUS/2]>>8);
+ }
+ }
+ if ((status & STATUS_DRQ) == 0) {
+ printf("%s: device not ready (status=%x)\n", __func__, status);
+ return (ENXIO);
+ }
+
+#ifdef OCTEON_VISUAL_CF_2
+ octeon_led_write_char(2, ' ');
+#endif
+ return (0);
+}
+
+/* ------------------------------------------------------------------- *
+ * cf_swap_ascii() *
+ * ------------------------------------------------------------------- *
+ *
+ * The ascii string returned by the controller specifying
+ * the model of the drive is byte-swaped. This routine
+ * corrects the byte ordering.
+ *
+ */
+static void cf_swap_ascii (unsigned char str1[], char str2[])
+{
+ int i;
+
+ for(i = 0; i < MODEL_STR_SIZE; i++) {
+ str2[i] = str1[i^1];
+ }
+}
+
+
+/* ------------------------------------------------------------------- *
+ * cf_probe() *
+ * ------------------------------------------------------------------- */
+
+static int cf_probe (device_t dev)
+{
+ if (octeon_is_simulation()) return 1;
+
+ if (device_get_unit(dev) != 0) {
+ panic("can't attach more devices\n");
+ }
+
+ device_set_desc(dev, "Octeon Compact Flash Driver");
+
+ return (cf_cmd_identify());
+}
+
+/* ------------------------------------------------------------------- *
+ * cf_identify() *
+ * ------------------------------------------------------------------- *
+ *
+ * Find the bootbus region for the CF to determine
+ * 16 or 8 bit and check to see if device is
+ * inserted.
+ *
+ */
+static void cf_identify (driver_t *drv, device_t parent)
+{
+ uint8_t status;
+ int bus_region;
+ int count = 0;
+ cvmx_mio_boot_reg_cfgx_t cfg;
+
+ if (octeon_is_simulation())
+ return;
+
+ base_addr = cvmx_phys_to_ptr(octeon_bootinfo->compact_flash_common_base_addr);
+
+ for (bus_region = 0; bus_region < 8; bus_region++)
+ {
+ cfg.u64 = cvmx_read_csr(CVMX_MIO_BOOT_REG_CFGX(bus_region));
+ if (cfg.s.base == octeon_bootinfo->compact_flash_common_base_addr >> 16)
+ {
+ bus_width = (cfg.s.width) ? 16: 8;
+ printf("Compact flash found in bootbus region %d (%d bit).\n", bus_region, bus_width);
+ break;
+ }
+ }
+
+ if (bus_width == 8) {
+ volatile uint8_t *task_file;
+ task_file = (volatile uint8_t *) base_addr;
+ /* Check if CF is inserted */
+ while ( (status = task_file[TF_STATUS]) & STATUS_BSY){
+ if ((count++) == NR_TRIES ) {
+ printf("Compact Flash not present\n");
+ return;
+ }
+ DELAY(WAIT_DELAY);
+ }
+ } else {
+ volatile uint16_t *task_file;
+ task_file = (volatile uint16_t *) base_addr;
+ /* Check if CF is inserted */
+ while ( (status = (task_file[TF_STATUS/2]>>8)) & STATUS_BSY){
+ if ((count++) == NR_TRIES ) {
+ printf("Compact Flash not present\n");
+ return;
+ }
+ DELAY(WAIT_DELAY);
+ }
+ }
+
+ BUS_ADD_CHILD(parent, 0, "cf", 0);
+}
+
+
+/* ------------------------------------------------------------------- *
+ * cf_attach_geom() *
+ * ------------------------------------------------------------------- */
+
+static int cf_attach_geom (void *arg, int flag)
+{
+ struct cf_priv *cf_priv;
+
+ cf_priv = (struct cf_priv *) arg;
+ cf_priv->cf_geom = g_new_geomf(&g_cf_class, "cf%d", device_get_unit(cf_priv->dev));
+ cf_priv->cf_provider = g_new_providerf(cf_priv->cf_geom, cf_priv->cf_geom->name);
+ cf_priv->cf_geom->softc = cf_priv;
+ g_error_provider(cf_priv->cf_provider, 0);
+
+ return (0);
+}
+
+/* ------------------------------------------------------------------- *
+ * cf_attach_geom() *
+ * ------------------------------------------------------------------- */
+static void cf_attach_geom_proxy (void *arg, int flag)
+{
+ cf_attach_geom(arg, flag);
+}
+
+
+
+/* ------------------------------------------------------------------- *
+ * cf_attach() *
+ * ------------------------------------------------------------------- */
+
+static int cf_attach (device_t dev)
+{
+ struct cf_priv *cf_priv;
+
+ if (octeon_is_simulation()) return 1;
+
+ cf_priv = device_get_softc(dev);
+ cf_priv->dev = dev;
+ cf_priv->drive_param = &drive_param;
+
+ g_post_event(cf_attach_geom_proxy, cf_priv, M_WAITOK, NULL);
+ bioq_init(&cf_priv->cf_bq);
+
+ return 0;
+}
+
+
+static device_method_t cf_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, cf_probe),
+ DEVMETHOD(device_identify, cf_identify),
+ DEVMETHOD(device_attach, cf_attach),
+ DEVMETHOD(device_detach, bus_generic_detach),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+
+ { 0, 0 }
+};
+
+static driver_t cf_driver = {
+ "cf",
+ cf_methods,
+ sizeof(struct cf_priv)
+};
+
+static devclass_t cf_devclass;
+
+DRIVER_MODULE(cf, nexus, cf_driver, cf_devclass, 0, 0);
+
diff --git a/sys/mips/cavium/octeon_ebt3000_cf.h b/sys/mips/cavium/octeon_ebt3000_cf.h
new file mode 100644
index 000000000000..1e995128b967
--- /dev/null
+++ b/sys/mips/cavium/octeon_ebt3000_cf.h
@@ -0,0 +1,35 @@
+/*
+ * octeon_ebt3000_cf.h
+ *
+ */
+
+
+#ifndef __OCTEON_EBT3000_H__
+#define __OCTEON_EBT3000_H__
+
+
+
+#define OCTEON_CF_COMMON_BASE_ADDR (0x1d000000 | (1 << 11))
+#define OCTEON_MIO_BOOT_REG_CFGX(offset) (0x8001180000000000ull + ((offset) * 8))
+
+
+typedef union
+{
+ uint64_t word64;
+ struct
+ {
+ uint64_t reserved : 27; /**< Reserved */
+ uint64_t sam : 1; /**< Region 0 SAM */
+ uint64_t we_ext : 2; /**< Region 0 write enable count extension */
+ uint64_t oe_ext : 2; /**< Region 0 output enable count extension */
+ uint64_t en : 1; /**< Region 0 enable */
+ uint64_t orbit : 1; /**< No function for region 0 */
+ uint64_t ale : 1; /**< Region 0 ALE mode */
+ uint64_t width : 1; /**< Region 0 bus width */
+ uint64_t size : 12; /**< Region 0 size */
+ uint64_t base : 16; /**< Region 0 base address */
+ } bits;
+} octeon_mio_boot_reg_cfgx_t;
+
+
+#endif /* __OCTEON_EBT3000_H__ */
diff --git a/sys/mips/cavium/octeon_machdep.c b/sys/mips/cavium/octeon_machdep.c
new file mode 100644
index 000000000000..0a1480e5be91
--- /dev/null
+++ b/sys/mips/cavium/octeon_machdep.c
@@ -0,0 +1,566 @@
+/*-
+ * 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.
+ *
+ * $FreeBSD$
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#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/time.h>
+#include <sys/timetc.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/atomic.h>
+#include <machine/cache.h>
+#include <machine/clock.h>
+#include <machine/cpu.h>
+#include <machine/cpuregs.h>
+#include <machine/cpufunc.h>
+#include <mips/cavium/octeon_pcmap_regs.h>
+#include <machine/hwfunc.h>
+#include <machine/intr_machdep.h>
+#include <machine/locore.h>
+#include <machine/md_var.h>
+#include <machine/pcpu.h>
+#include <machine/pte.h>
+#include <machine/trap.h>
+#include <machine/vmparam.h>
+
+#include <contrib/octeon-sdk/cvmx.h>
+#include <contrib/octeon-sdk/cvmx-bootmem.h>
+#include <contrib/octeon-sdk/cvmx-interrupt.h>
+#include <contrib/octeon-sdk/cvmx-version.h>
+
+#if defined(__mips_n64)
+#define MAX_APP_DESC_ADDR 0xffffffffafffffff
+#else
+#define MAX_APP_DESC_ADDR 0xafffffff
+#endif
+
+#define OCTEON_CLOCK_DEFAULT (500 * 1000 * 1000)
+
+struct octeon_feature_description {
+ octeon_feature_t ofd_feature;
+ const char *ofd_string;
+};
+
+extern int *edata;
+extern int *end;
+
+static const struct octeon_feature_description octeon_feature_descriptions[] = {
+ { OCTEON_FEATURE_SAAD, "SAAD" },
+ { OCTEON_FEATURE_ZIP, "ZIP" },
+ { OCTEON_FEATURE_CRYPTO, "CRYPTO" },
+ { OCTEON_FEATURE_PCIE, "PCIE" },
+ { OCTEON_FEATURE_KEY_MEMORY, "KEY_MEMORY" },
+ { OCTEON_FEATURE_LED_CONTROLLER, "LED_CONTROLLER" },
+ { OCTEON_FEATURE_TRA, "TRA" },
+ { OCTEON_FEATURE_MGMT_PORT, "MGMT_PORT" },
+ { OCTEON_FEATURE_RAID, "RAID" },
+ { OCTEON_FEATURE_USB, "USB" },
+ { OCTEON_FEATURE_NO_WPTR, "NO_WPTR" },
+ { OCTEON_FEATURE_DFA, "DFA" },
+ { OCTEON_FEATURE_MDIO_CLAUSE_45, "MDIO_CLAUSE_45" },
+ { 0, NULL }
+};
+
+uint64_t ciu_get_en_reg_addr_new(int corenum, int intx, int enx, int ciu_ip);
+void ciu_dump_interrutps_enabled(int core_num, int intx, int enx, int ciu_ip);
+
+static uint64_t octeon_get_ticks(void);
+static unsigned octeon_get_timecount(struct timecounter *tc);
+
+static void octeon_boot_params_init(register_t ptr);
+
+static struct timecounter octeon_timecounter = {
+ octeon_get_timecount, /* get_timecount */
+ 0, /* no poll_pps */
+ 0xffffffffu, /* octeon_mask */
+ 0, /* frequency */
+ "Octeon", /* name */
+ 900, /* quality (adjusted in code) */
+};
+
+void
+platform_cpu_init()
+{
+ /* Nothing special yet */
+}
+
+/*
+ * Perform a board-level soft-reset.
+ */
+void
+platform_reset(void)
+{
+ cvmx_write_csr(CVMX_CIU_SOFT_RST, 1);
+}
+
+void
+octeon_led_write_char(int char_position, char val)
+{
+ uint64_t ptr = (OCTEON_CHAR_LED_BASE_ADDR | 0xf8);
+
+ if (octeon_is_simulation())
+ return;
+
+ char_position &= 0x7; /* only 8 chars */
+ ptr += char_position;
+ oct_write8_x8(ptr, val);
+}
+
+void
+octeon_led_write_char0(char val)
+{
+ uint64_t ptr = (OCTEON_CHAR_LED_BASE_ADDR | 0xf8);
+
+ if (octeon_is_simulation())
+ return;
+ oct_write8_x8(ptr, val);
+}
+
+void
+octeon_led_write_hexchar(int char_position, char hexval)
+{
+ uint64_t ptr = (OCTEON_CHAR_LED_BASE_ADDR | 0xf8);
+ char char1, char2;
+
+ if (octeon_is_simulation())
+ return;
+
+ char1 = (hexval >> 4) & 0x0f; char1 = (char1 < 10)?char1+'0':char1+'7';
+ char2 = (hexval & 0x0f); char2 = (char2 < 10)?char2+'0':char2+'7';
+ char_position &= 0x7; /* only 8 chars */
+ if (char_position > 6)
+ char_position = 6;
+ ptr += char_position;
+ oct_write8_x8(ptr, char1);
+ ptr++;
+ oct_write8_x8(ptr, char2);
+}
+
+void
+octeon_led_write_string(const char *str)
+{
+ uint64_t ptr = (OCTEON_CHAR_LED_BASE_ADDR | 0xf8);
+ int i;
+
+ if (octeon_is_simulation())
+ return;
+
+ for (i=0; i<8; i++, ptr++) {
+ if (str && *str)
+ oct_write8_x8(ptr, *str++);
+ else
+ oct_write8_x8(ptr, ' ');
+ (void)cvmx_read_csr(CVMX_MIO_BOOT_BIST_STAT);
+ }
+}
+
+static char progress[8] = { '-', '/', '|', '\\', '-', '/', '|', '\\'};
+
+void
+octeon_led_run_wheel(int *prog_count, int led_position)
+{
+ if (octeon_is_simulation())
+ return;
+ octeon_led_write_char(led_position, progress[*prog_count]);
+ *prog_count += 1;
+ *prog_count &= 0x7;
+}
+
+void
+octeon_led_write_hex(uint32_t wl)
+{
+ char nbuf[80];
+
+ sprintf(nbuf, "%X", wl);
+ octeon_led_write_string(nbuf);
+}
+
+/*
+ * octeon_debug_symbol
+ *
+ * Does nothing.
+ * Used to mark the point for simulator to begin tracing
+ */
+void
+octeon_debug_symbol(void)
+{
+}
+
+/*
+ * octeon_ciu_reset
+ *
+ * Shutdown all CIU to IP2, IP3 mappings
+ */
+void
+octeon_ciu_reset(void)
+{
+ /* Disable all CIU interrupts by default */
+ cvmx_write_csr(CVMX_CIU_INTX_EN0(cvmx_get_core_num()*2), 0);
+ cvmx_write_csr(CVMX_CIU_INTX_EN0(cvmx_get_core_num()*2+1), 0);
+ cvmx_write_csr(CVMX_CIU_INTX_EN1(cvmx_get_core_num()*2), 0);
+ cvmx_write_csr(CVMX_CIU_INTX_EN1(cvmx_get_core_num()*2+1), 0);
+
+#ifdef SMP
+ /* Enable the MBOX interrupts. */
+ cvmx_write_csr(CVMX_CIU_INTX_EN0(cvmx_get_core_num()*2+1),
+ (1ull << (CVMX_IRQ_MBOX0 - 8)) |
+ (1ull << (CVMX_IRQ_MBOX1 - 8)));
+#endif
+}
+
+static void
+octeon_memory_init(void)
+{
+ vm_paddr_t phys_end;
+ int64_t addr;
+ unsigned i;
+
+ phys_end = round_page(MIPS_KSEG0_TO_PHYS((vm_offset_t)&end));
+
+ if (octeon_is_simulation()) {
+ /* Simulator we limit to 96 meg */
+ phys_avail[0] = phys_end;
+ phys_avail[1] = 96 << 20;
+
+ realmem = physmem = btoc(phys_avail[1] - phys_avail[0]);
+ return;
+ }
+
+ /*
+ * Allocate memory from bootmem 1MB at a time and merge
+ * adjacent entries.
+ */
+ i = 0;
+ while (i < PHYS_AVAIL_ENTRIES) {
+ addr = cvmx_bootmem_phy_alloc(1 << 20, phys_end,
+ ~(vm_paddr_t)0, PAGE_SIZE, 0);
+ if (addr == -1)
+ break;
+
+ /*
+ * The SDK needs to be able to easily map any memory that might
+ * come to it e.g. in the form of an mbuf. Because on !n64 we
+ * can't direct-map some addresses and we don't want to manage
+ * temporary mappings within the SDK, don't feed memory that
+ * can't be direct-mapped to the kernel.
+ */
+#if !defined(__mips_n64)
+ if (!MIPS_DIRECT_MAPPABLE(addr + (1 << 20) - 1))
+ continue;
+#endif
+
+ physmem += btoc(1 << 20);
+
+ if (i > 0 && phys_avail[i - 1] == addr) {
+ phys_avail[i - 1] += 1 << 20;
+ continue;
+ }
+
+ phys_avail[i + 0] = addr;
+ phys_avail[i + 1] = addr + (1 << 20);
+
+ i += 2;
+ }
+
+ realmem = physmem;
+}
+
+void
+platform_start(__register_t a0, __register_t a1, __register_t a2 __unused,
+ __register_t a3)
+{
+ const struct octeon_feature_description *ofd;
+ uint64_t platform_counter_freq;
+
+ /*
+ * XXX
+ * octeon_boot_params_init() should be called before anything else,
+ * certainly before any output; we may find out from the boot
+ * descriptor's flags that we're supposed to use the PCI or UART1
+ * consoles rather than UART0. No point doing that reorganization
+ * until we actually intercept UART_DEV_CONSOLE for the UART1 case
+ * and somehow handle the PCI console, which we lack code for
+ * entirely.
+ */
+
+ /* Initialize pcpu stuff */
+ mips_pcpu0_init();
+ mips_timer_early_init(OCTEON_CLOCK_DEFAULT);
+ cninit();
+
+ octeon_ciu_reset();
+ octeon_boot_params_init(a3);
+ /*
+ * XXX
+ * We can certainly parse command line arguments or U-Boot environment
+ * to determine whether to bootverbose / single user / ... I think
+ * stass has patches to add support for loader things to U-Boot even.
+ */
+ bootverbose = 1;
+
+ /*
+ * For some reason on the cn38xx simulator ebase register is set to
+ * 0x80001000 at bootup time. Move it back to the default, but
+ * when we move to having support for multiple executives, we need
+ * to rethink this.
+ */
+ mips_wr_ebase(0x80000000);
+
+ octeon_memory_init();
+ init_param1();
+ init_param2(physmem);
+ mips_cpu_init();
+ pmap_bootstrap();
+ mips_proc0_init();
+ mutex_init();
+ kdb_init();
+#ifdef KDB
+ if (boothowto & RB_KDB)
+ kdb_enter(KDB_WHY_BOOTFLAGS, "Boot flags requested debugger");
+#endif
+ platform_counter_freq = cvmx_sysinfo_get()->cpu_clock_hz;
+
+ octeon_timecounter.tc_frequency = cvmx_sysinfo_get()->cpu_clock_hz;
+ platform_timecounter = &octeon_timecounter;
+
+ mips_timer_init_params(platform_counter_freq, 0);
+
+ set_cputicker(octeon_get_ticks, cvmx_sysinfo_get()->cpu_clock_hz, 0);
+
+#ifdef SMP
+ /*
+ * Clear any pending IPIs.
+ */
+ cvmx_write_csr(CVMX_CIU_MBOX_CLRX(0), 0xffffffff);
+#endif
+
+ printf("Octeon SDK: %s\n", OCTEON_SDK_VERSION_STRING);
+ printf("Available Octeon features:");
+ for (ofd = octeon_feature_descriptions; ofd->ofd_string != NULL; ofd++)
+ if (octeon_has_feature(ofd->ofd_feature))
+ printf(" %s", ofd->ofd_string);
+ printf("\n");
+}
+
+static uint64_t
+octeon_get_ticks(void)
+{
+ uint64_t cvmcount;
+
+ CVMX_MF_CYCLE(cvmcount);
+ return (cvmcount);
+}
+
+static unsigned
+octeon_get_timecount(struct timecounter *tc)
+{
+ return ((unsigned)octeon_get_ticks());
+}
+
+/* impSTART: This stuff should move back into the Cavium SDK */
+/*
+ ****************************************************************************************
+ *
+ * APP/BOOT DESCRIPTOR STUFF
+ *
+ ****************************************************************************************
+ */
+
+/* Define the struct that is initialized by the bootloader used by the
+ * startup code.
+ *
+ * Copyright (c) 2004, 2005, 2006 Cavium Networks.
+ *
+ * The authors hereby grant permission to use, copy, modify, distribute,
+ * and license this software and its documentation for any purpose, provided
+ * that existing copyright notices are retained in all copies and that this
+ * notice is included verbatim in any distributions. No written agreement,
+ * license, or royalty fee is required for any of the authorized uses.
+ * Modifications to this software may be copyrighted by their authors
+ * and need not follow the licensing terms described here, provided that
+ * the new terms are clearly indicated on the first page of each file where
+ * they apply.
+ */
+
+#define OCTEON_CURRENT_DESC_VERSION 6
+#define OCTEON_ARGV_MAX_ARGS (64)
+#define OCTOEN_SERIAL_LEN 20
+
+typedef struct {
+ /* Start of block referenced by assembly code - do not change! */
+ uint32_t desc_version;
+ uint32_t desc_size;
+
+ uint64_t stack_top;
+ uint64_t heap_base;
+ uint64_t heap_end;
+ uint64_t entry_point; /* Only used by bootloader */
+ uint64_t desc_vaddr;
+ /* End of This block referenced by assembly code - do not change! */
+
+ uint32_t exception_base_addr;
+ uint32_t stack_size;
+ uint32_t heap_size;
+ uint32_t argc; /* Argc count for application */
+ uint32_t argv[OCTEON_ARGV_MAX_ARGS];
+ uint32_t flags;
+ uint32_t core_mask;
+ uint32_t dram_size; /**< DRAM size in megabyes */
+ uint32_t phy_mem_desc_addr; /**< physical address of free memory descriptor block*/
+ uint32_t debugger_flags_base_addr; /**< used to pass flags from app to debugger */
+ uint32_t eclock_hz; /**< CPU clock speed, in hz */
+ uint32_t dclock_hz; /**< DRAM clock speed, in hz */
+ uint32_t spi_clock_hz; /**< SPI4 clock in hz */
+ uint16_t board_type;
+ uint8_t board_rev_major;
+ uint8_t board_rev_minor;
+ uint16_t chip_type;
+ uint8_t chip_rev_major;
+ uint8_t chip_rev_minor;
+ char board_serial_number[OCTOEN_SERIAL_LEN];
+ uint8_t mac_addr_base[6];
+ uint8_t mac_addr_count;
+ uint64_t cvmx_desc_vaddr;
+} octeon_boot_descriptor_t;
+
+cvmx_bootinfo_t *octeon_bootinfo;
+
+static octeon_boot_descriptor_t *app_desc_ptr;
+
+int
+octeon_is_simulation(void)
+{
+ switch (cvmx_sysinfo_get()->board_type) {
+ case CVMX_BOARD_TYPE_SIM:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static void
+octeon_process_app_desc_ver_6(void)
+{
+ void *phy_mem_desc_ptr;
+
+ /* XXX Why is 0x00000000ffffffffULL a bad value? */
+ if (app_desc_ptr->cvmx_desc_vaddr == 0 ||
+ app_desc_ptr->cvmx_desc_vaddr == 0xfffffffful)
+ panic("Bad octeon_bootinfo %p", octeon_bootinfo);
+
+ octeon_bootinfo =
+ (cvmx_bootinfo_t *)(intptr_t)app_desc_ptr->cvmx_desc_vaddr;
+ octeon_bootinfo =
+ (cvmx_bootinfo_t *) ((intptr_t)octeon_bootinfo | MIPS_KSEG0_START);
+ if (octeon_bootinfo->major_version != 1)
+ panic("Incompatible CVMX descriptor from bootloader: %d.%d %p",
+ (int) octeon_bootinfo->major_version,
+ (int) octeon_bootinfo->minor_version, octeon_bootinfo);
+
+ phy_mem_desc_ptr =
+ (void *)MIPS_PHYS_TO_KSEG0(octeon_bootinfo->phy_mem_desc_addr);
+ cvmx_sysinfo_minimal_initialize(phy_mem_desc_ptr,
+ octeon_bootinfo->board_type,
+ octeon_bootinfo->board_rev_major,
+ octeon_bootinfo->board_rev_minor,
+ octeon_bootinfo->eclock_hz);
+}
+
+static void
+octeon_boot_params_init(register_t ptr)
+{
+ if (ptr == 0 || ptr >= MAX_APP_DESC_ADDR)
+ panic("app descriptor passed at invalid address %#jx",
+ (uintmax_t)ptr);
+
+ app_desc_ptr = (octeon_boot_descriptor_t *)(intptr_t)ptr;
+ if (app_desc_ptr->desc_version < 6)
+ panic("Your boot code is too old to be supported.");
+ octeon_process_app_desc_ver_6();
+
+ KASSERT(octeon_bootinfo != NULL, ("octeon_bootinfo should be set"));
+
+ if (cvmx_sysinfo_get()->phy_mem_desc_ptr == NULL)
+ panic("Your boot loader did not supply a memory descriptor.");
+ cvmx_bootmem_init(cvmx_sysinfo_get()->phy_mem_desc_ptr);
+
+ printf("Boot Descriptor Ver: %u -> %u/%u",
+ app_desc_ptr->desc_version, octeon_bootinfo->major_version,
+ octeon_bootinfo->minor_version);
+ printf(" CPU clock: %uMHz Core Mask: %#x\n",
+ cvmx_sysinfo_get()->cpu_clock_hz / 1000000,
+ cvmx_sysinfo_get()->core_mask);
+ printf(" Board Type: %u Revision: %u/%u\n",
+ cvmx_sysinfo_get()->board_type,
+ cvmx_sysinfo_get()->board_rev_major,
+ cvmx_sysinfo_get()->board_rev_minor);
+
+ printf(" Mac Address %02X.%02X.%02X.%02X.%02X.%02X (%d)\n",
+ octeon_bootinfo->mac_addr_base[0],
+ octeon_bootinfo->mac_addr_base[1],
+ octeon_bootinfo->mac_addr_base[2],
+ octeon_bootinfo->mac_addr_base[3],
+ octeon_bootinfo->mac_addr_base[4],
+ octeon_bootinfo->mac_addr_base[5],
+ octeon_bootinfo->mac_addr_count);
+
+#if defined(OCTEON_BOARD_CAPK_0100ND)
+ if (cvmx_sysinfo_get()->board_type != CVMX_BOARD_TYPE_CN3010_EVB_HS5)
+ printf("Compiled for CAPK-0100ND, but board type is %s\n",
+ cvmx_board_type_to_string(cvmx_sysinfo_get()->board_type));
+#else
+ printf("Board: %s\n",
+ cvmx_board_type_to_string(cvmx_sysinfo_get()->board_type));
+#endif
+ printf("Model: %s\n", octeon_model_get_string(cvmx_get_proc_id()));
+}
+/* impEND: This stuff should move back into the Cavium SDK */
diff --git a/sys/mips/cavium/octeon_mp.c b/sys/mips/cavium/octeon_mp.c
new file mode 100644
index 000000000000..34de442eedba
--- /dev/null
+++ b/sys/mips/cavium/octeon_mp.c
@@ -0,0 +1,127 @@
+/*-
+ * Copyright (c) 2004-2010 Juli Mallett <jmallett@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.
+ *
+ * $FreeBSD$
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/smp.h>
+#include <sys/systm.h>
+
+#include <machine/hwfunc.h>
+#include <machine/md_var.h>
+#include <machine/smp.h>
+
+#include <mips/cavium/octeon_pcmap_regs.h>
+
+#include <contrib/octeon-sdk/cvmx.h>
+#include <contrib/octeon-sdk/cvmx-interrupt.h>
+
+/* XXX */
+extern cvmx_bootinfo_t *octeon_bootinfo;
+
+unsigned octeon_ap_boot = ~0;
+
+void
+platform_ipi_send(int cpuid)
+{
+ cvmx_write_csr(CVMX_CIU_MBOX_SETX(cpuid), 1);
+ mips_wbflush();
+}
+
+void
+platform_ipi_clear(void)
+{
+ uint64_t action;
+
+ action = cvmx_read_csr(CVMX_CIU_MBOX_CLRX(PCPU_GET(cpuid)));
+ KASSERT(action == 1, ("unexpected IPIs: %#jx", (uintmax_t)action));
+ cvmx_write_csr(CVMX_CIU_MBOX_CLRX(PCPU_GET(cpuid)), action);
+}
+
+int
+platform_ipi_intrnum(void)
+{
+ return (1);
+}
+
+void
+platform_init_ap(int cpuid)
+{
+ unsigned ipi_int_mask, clock_int_mask;
+
+ /*
+ * Set the exception base.
+ */
+ mips_wr_ebase(0x80000000 | cpuid);
+
+ /*
+ * Clear any pending IPIs.
+ */
+ cvmx_write_csr(CVMX_CIU_MBOX_CLRX(cpuid), 0xffffffff);
+
+ /*
+ * Set up interrupts.
+ */
+ octeon_ciu_reset();
+
+ /*
+ * Unmask the clock and ipi interrupts.
+ */
+ clock_int_mask = hard_int_mask(5);
+ ipi_int_mask = hard_int_mask(platform_ipi_intrnum());
+ set_intr_mask(ipi_int_mask | clock_int_mask);
+
+ mips_wbflush();
+}
+
+int
+platform_num_processors(void)
+{
+ return (bitcount32(octeon_bootinfo->core_mask));
+}
+
+struct cpu_group *
+platform_smp_topo(void)
+{
+ return (smp_topo_none());
+}
+
+int
+platform_start_ap(int cpuid)
+{
+ if (atomic_cmpset_32(&octeon_ap_boot, ~0, cpuid) == 0)
+ return (-1);
+ for (;;) {
+ DELAY(1000);
+ if (atomic_cmpset_32(&octeon_ap_boot, 0, ~0) != 0)
+ return (0);
+ printf("Waiting for cpu%d to start\n", cpuid);
+ }
+}
diff --git a/sys/mips/cavium/octeon_pcmap_regs.h b/sys/mips/cavium/octeon_pcmap_regs.h
new file mode 100644
index 000000000000..f9ee4dfd30fa
--- /dev/null
+++ b/sys/mips/cavium/octeon_pcmap_regs.h
@@ -0,0 +1,300 @@
+/***********************license start***************
+ * Copyright (c) 2003-2008 Cavium Networks (support@cavium.com). All rights
+ * reserved.
+ *
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * 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.
+ *
+ * * Neither the name of Cavium Networks nor the names of
+ * its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written
+ * permission.
+ *
+ * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+ * AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS
+ * OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH
+ * RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY
+ * REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT
+ * DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES
+ * OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR
+ * PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET
+ * POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT
+ * OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+ *
+ *
+ * For any questions regarding licensing please contact marketing@caviumnetworks.com
+ *
+ ***********************license end**************************************/
+
+/*
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors."
+ */
+
+/* $FreeBSD$ */
+
+#ifndef __OCTEON_PCMAP_REGS_H__
+#define __OCTEON_PCMAP_REGS_H__
+
+#ifndef LOCORE
+
+/*
+ * Utility inlines & macros
+ */
+
+#if defined(__mips_n64)
+#define oct_write64(a, v) (*(volatile uint64_t *)(a) = (uint64_t)(v))
+#define oct_write8_x8(a, v) (*(volatile uint8_t *)(a) = (uint8_t)(v))
+
+#define OCT_READ(n, t) \
+static inline t oct_read ## n(uintptr_t a) \
+{ \
+ volatile t *p = (volatile t *)a; \
+ return (*p); \
+}
+
+OCT_READ(8, uint8_t);
+OCT_READ(16, uint16_t);
+OCT_READ(32, uint32_t);
+OCT_READ(64, uint64_t);
+
+#elif defined(__mips_n32) || defined(__mips_o32)
+#if defined(__mips_n32)
+static inline void oct_write64 (uint64_t csr_addr, uint64_t val64)
+{
+ __asm __volatile (
+ ".set push\n"
+ ".set mips64\n"
+ "sd %0, 0(%1)\n"
+ ".set pop\n"
+ :
+ : "r"(val64), "r"(csr_addr));
+}
+
+static inline void oct_write8_x8 (uint64_t csr_addr, uint8_t val8)
+{
+ __asm __volatile (
+ ".set push\n"
+ ".set mips64\n"
+ "sb %0, 0(%1)\n"
+ ".set pop\n"
+ :
+ : "r"(val8), "r"(csr_addr));
+}
+
+#define OCT_READ(n, t, insn) \
+static inline t oct_read ## n(uint64_t a) \
+{ \
+ uint64_t tmp; \
+ \
+ __asm __volatile ( \
+ ".set push\n" \
+ ".set mips64\n" \
+ insn "\t%0, 0(%1)\n" \
+ ".set pop\n" \
+ : "=r"(tmp) \
+ : "r"(a)); \
+ return ((t)tmp); \
+}
+
+OCT_READ(8, uint8_t, "lb");
+OCT_READ(16, uint16_t, "lh");
+OCT_READ(32, uint32_t, "lw");
+OCT_READ(64, uint64_t, "ld");
+#else
+
+/*
+ * XXX
+ * Add o32 variants that load the address into a register and the result out
+ * of a register properly, and simply disable interrupts before and after and
+ * hope that we don't need to refill or modify the TLB to access the address.
+ * I'd be a lot happier if csr_addr were a physical address and we mapped it
+ * into XKPHYS here so that we could guarantee that interrupts were the only
+ * kind of exception we needed to worry about.
+ *
+ * Also, some of this inline assembly is needlessly verbose. Oh, well.
+ */
+static inline void oct_write64 (uint64_t csr_addr, uint64_t val64)
+{
+ uint32_t csr_addrh = csr_addr >> 32;
+ uint32_t csr_addrl = csr_addr;
+ uint32_t valh = val64 >> 32;
+ uint32_t vall = val64;
+ uint32_t tmp1;
+ uint32_t tmp2;
+ uint32_t tmp3;
+ register_t sr;
+
+ sr = intr_disable();
+
+ __asm __volatile (
+ ".set push\n"
+ ".set mips64\n"
+ ".set noreorder\n"
+ ".set noat\n"
+ "dsll %0, %3, 32\n"
+ "dsll %1, %5, 32\n"
+ "dsll %2, %4, 32\n"
+ "dsrl %2, %2, 32\n"
+ "or %0, %0, %2\n"
+ "dsll %2, %6, 32\n"
+ "dsrl %2, %2, 32\n"
+ "or %1, %1, %2\n"
+ "sd %0, 0(%1)\n"
+ ".set pop\n"
+ : "=&r" (tmp1), "=&r" (tmp2), "=&r" (tmp3)
+ : "r" (valh), "r" (vall), "r" (csr_addrh), "r" (csr_addrl));
+
+ intr_restore(sr);
+}
+
+static inline void oct_write8_x8 (uint64_t csr_addr, uint8_t val8)
+{
+ uint32_t csr_addrh = csr_addr >> 32;
+ uint32_t csr_addrl = csr_addr;
+ uint32_t tmp1;
+ uint32_t tmp2;
+ register_t sr;
+
+ sr = intr_disable();
+
+ __asm __volatile (
+ ".set push\n"
+ ".set mips64\n"
+ ".set noreorder\n"
+ ".set noat\n"
+ "dsll %0, %3, 32\n"
+ "dsll %1, %4, 32\n"
+ "dsrl %1, %1, 32\n"
+ "or %0, %0, %1\n"
+ "sb %2, 0(%0)\n"
+ ".set pop\n"
+ : "=&r" (tmp1), "=&r" (tmp2)
+ : "r" (val8), "r" (csr_addrh), "r" (csr_addrl));
+
+ intr_restore(sr);
+}
+
+#define OCT_READ(n, t, insn) \
+static inline t oct_read ## n(uint64_t csr_addr) \
+{ \
+ uint32_t csr_addrh = csr_addr >> 32; \
+ uint32_t csr_addrl = csr_addr; \
+ uint32_t tmp1, tmp2; \
+ register_t sr; \
+ \
+ sr = intr_disable(); \
+ \
+ __asm __volatile ( \
+ ".set push\n" \
+ ".set mips64\n" \
+ ".set noreorder\n" \
+ ".set noat\n" \
+ "dsll %1, %2, 32\n" \
+ "dsll %0, %3, 32\n" \
+ "dsrl %0, %0, 32\n" \
+ "or %1, %1, %0\n" \
+ "lb %1, 0(%1)\n" \
+ ".set pop\n" \
+ : "=&r" (tmp1), "=&r" (tmp2) \
+ : "r" (csr_addrh), "r" (csr_addrl)); \
+ \
+ intr_restore(sr); \
+ \
+ return ((t)tmp2); \
+}
+
+OCT_READ(8, uint8_t, "lb");
+OCT_READ(16, uint16_t, "lh");
+OCT_READ(32, uint32_t, "lw");
+
+static inline uint64_t oct_read64 (uint64_t csr_addr)
+{
+ uint32_t csr_addrh = csr_addr >> 32;
+ uint32_t csr_addrl = csr_addr;
+ uint32_t valh;
+ uint32_t vall;
+ register_t sr;
+
+ sr = intr_disable();
+
+ __asm __volatile (
+ ".set push\n"
+ ".set mips64\n"
+ ".set noreorder\n"
+ ".set noat\n"
+ "dsll %0, %2, 32\n"
+ "dsll %1, %3, 32\n"
+ "dsrl %1, %1, 32\n"
+ "or %0, %0, %1\n"
+ "ld %1, 0(%0)\n"
+ "dsrl %0, %1, 32\n"
+ "dsll %1, %1, 32\n"
+ "dsrl %1, %1, 32\n"
+ ".set pop\n"
+ : "=&r" (valh), "=&r" (vall)
+ : "r" (csr_addrh), "r" (csr_addrl));
+
+ intr_restore(sr);
+
+ return ((uint64_t)valh << 32) | vall;
+}
+#endif
+
+#endif
+
+#define oct_write64_int64(a, v) (oct_write64(a, (int64_t)(v)))
+
+/*
+ * Most write bus transactions are actually 64-bit on Octeon.
+ */
+static inline void oct_write8 (uint64_t csr_addr, uint8_t val8)
+{
+ oct_write64(csr_addr, (uint64_t) val8);
+}
+
+static inline void oct_write16 (uint64_t csr_addr, uint16_t val16)
+{
+ oct_write64(csr_addr, (uint64_t) val16);
+}
+
+static inline void oct_write32 (uint64_t csr_addr, uint32_t val32)
+{
+ oct_write64(csr_addr, (uint64_t) val32);
+}
+
+#define oct_readint32(a) ((int32_t)oct_read32((a)))
+
+/*
+ * octeon_machdep.c
+ *
+ * Direct to Board Support level.
+ */
+extern void octeon_led_write_char(int char_position, char val);
+extern void octeon_led_write_hexchar(int char_position, char hexval);
+extern void octeon_led_write_hex(uint32_t wl);
+extern void octeon_led_write_string(const char *str);
+extern void octeon_reset(void);
+extern void octeon_led_write_char0(char val);
+extern void octeon_led_run_wheel(int *pos, int led_position);
+extern void octeon_debug_symbol(void);
+extern void octeon_ciu_reset(void);
+extern int octeon_is_simulation(void);
+#endif /* LOCORE */
+
+/*
+ * EBT3000 LED Unit
+ */
+#define OCTEON_CHAR_LED_BASE_ADDR (0x1d020000 | (0x1ffffffffull << 31))
+
+#endif /* !OCTEON_PCMAP_REGS_H__ */
diff --git a/sys/mips/cavium/octeon_rnd.c b/sys/mips/cavium/octeon_rnd.c
new file mode 100644
index 000000000000..634a4fa86f04
--- /dev/null
+++ b/sys/mips/cavium/octeon_rnd.c
@@ -0,0 +1,137 @@
+/*-
+ * Copyright (c) 2010 Juli Mallett <jmallett@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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/clock.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/random.h>
+
+#include <contrib/octeon-sdk/cvmx.h>
+#include <contrib/octeon-sdk/cvmx-rng.h>
+
+/*
+ * XXX
+ * random_harvest(9) says to call it with no more than 16 bytes, but at least
+ * safe(4) seems to violate that rule.
+ */
+#define OCTEON_RND_WORDS 2
+
+struct octeon_rnd_softc {
+ uint64_t sc_entropy[OCTEON_RND_WORDS];
+ struct callout sc_callout;
+};
+
+static void octeon_rnd_identify(driver_t *drv, device_t parent);
+static int octeon_rnd_attach(device_t dev);
+static int octeon_rnd_probe(device_t dev);
+static int octeon_rnd_detach(device_t dev);
+
+static void octeon_rnd_harvest(void *);
+
+static device_method_t octeon_rnd_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_identify, octeon_rnd_identify),
+ DEVMETHOD(device_probe, octeon_rnd_probe),
+ DEVMETHOD(device_attach, octeon_rnd_attach),
+ DEVMETHOD(device_detach, octeon_rnd_detach),
+
+ { 0, 0 }
+};
+
+static driver_t octeon_rnd_driver = {
+ "rnd",
+ octeon_rnd_methods,
+ sizeof (struct octeon_rnd_softc)
+};
+static devclass_t octeon_rnd_devclass;
+DRIVER_MODULE(rnd, nexus, octeon_rnd_driver, octeon_rnd_devclass, 0, 0);
+
+static void
+octeon_rnd_identify(driver_t *drv, device_t parent)
+{
+ BUS_ADD_CHILD(parent, 0, "rnd", 0);
+}
+
+static int
+octeon_rnd_probe(device_t dev)
+{
+ if (device_get_unit(dev) != 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Cavium Octeon Random Number Generator");
+ return (0);
+}
+
+static int
+octeon_rnd_attach(device_t dev)
+{
+ struct octeon_rnd_softc *sc;
+
+ sc = device_get_softc(dev);
+ callout_init(&sc->sc_callout, CALLOUT_MPSAFE);
+ callout_reset(&sc->sc_callout, hz * 5, octeon_rnd_harvest, sc);
+
+ cvmx_rng_enable();
+
+ return (0);
+}
+
+static int
+octeon_rnd_detach(device_t dev)
+{
+ struct octeon_rnd_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ callout_stop(&sc->sc_callout);
+
+ return (0);
+}
+
+static void
+octeon_rnd_harvest(void *arg)
+{
+ struct octeon_rnd_softc *sc;
+ unsigned i;
+
+ sc = arg;
+
+ for (i = 0; i < OCTEON_RND_WORDS; i++)
+ sc->sc_entropy[i] = cvmx_rng_get_random64();
+ random_harvest(sc->sc_entropy, sizeof sc->sc_entropy,
+ sizeof sc->sc_entropy * 8, 0, RANDOM_PURE);
+
+ callout_reset(&sc->sc_callout, hz * 5, octeon_rnd_harvest, sc);
+}
diff --git a/sys/mips/cavium/octeon_rtc.c b/sys/mips/cavium/octeon_rtc.c
new file mode 100644
index 000000000000..ca2debdfc6a3
--- /dev/null
+++ b/sys/mips/cavium/octeon_rtc.c
@@ -0,0 +1,130 @@
+/*-
+ * Copyright (c) 2010 Juli Mallett <jmallett@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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/clock.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+
+#include <contrib/octeon-sdk/cvmx.h>
+#include <contrib/octeon-sdk/cvmx-rtc.h>
+
+#include "clock_if.h"
+
+static int octeon_rtc_attach(device_t dev);
+static int octeon_rtc_probe(device_t dev);
+
+static int octeon_rtc_settime(device_t dev, struct timespec *ts);
+static int octeon_rtc_gettime(device_t dev, struct timespec *ts);
+
+static device_method_t octeon_rtc_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, octeon_rtc_probe),
+ DEVMETHOD(device_attach, octeon_rtc_attach),
+
+ /* clock interface */
+ DEVMETHOD(clock_gettime, octeon_rtc_gettime),
+ DEVMETHOD(clock_settime, octeon_rtc_settime),
+
+ { 0, 0 }
+};
+
+static driver_t octeon_rtc_driver = {
+ "rtc",
+ octeon_rtc_methods,
+ 0
+};
+static devclass_t octeon_rtc_devclass;
+DRIVER_MODULE(rtc, nexus, octeon_rtc_driver, octeon_rtc_devclass, 0, 0);
+
+static int
+octeon_rtc_probe(device_t dev)
+{
+ cvmx_rtc_options_t supported;
+
+ if (device_get_unit(dev) != 0)
+ return (ENXIO);
+
+ supported = cvmx_rtc_supported();
+ if (supported == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Cavium Octeon Realtime Clock");
+ return (0);
+}
+
+static int
+octeon_rtc_attach(device_t dev)
+{
+ cvmx_rtc_options_t supported;
+
+ supported = cvmx_rtc_supported();
+ if ((supported & CVMX_RTC_READ) == 0)
+ return (ENXIO);
+
+ clock_register(dev, 1000000);
+ return (0);
+}
+
+static int
+octeon_rtc_settime(device_t dev, struct timespec *ts)
+{
+ cvmx_rtc_options_t supported;
+ uint32_t status;
+
+ supported = cvmx_rtc_supported();
+ if ((supported & CVMX_RTC_WRITE) == 0)
+ return (ENOTSUP);
+
+ status = cvmx_rtc_write(ts->tv_sec);
+ if (status != 0)
+ return (EINVAL);
+
+ return (0);
+}
+
+static int
+octeon_rtc_gettime(device_t dev, struct timespec *ts)
+{
+ uint32_t secs;
+
+ secs = cvmx_rtc_read();
+ if (secs == 0)
+ return (ENOTSUP);
+
+ ts->tv_sec = secs;
+ ts->tv_nsec = 0;
+
+ return (0);
+}
diff --git a/sys/mips/cavium/octeonreg.h b/sys/mips/cavium/octeonreg.h
new file mode 100644
index 000000000000..44e927db36b2
--- /dev/null
+++ b/sys/mips/cavium/octeonreg.h
@@ -0,0 +1,247 @@
+/* $NetBSD: octeonreg.h,v 1.1 2002/03/07 14:44:04 simonb Exp $ */
+
+/*
+ * Copyright 2002 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Simon Burge for Wasabi Systems, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed for the NetBSD Project by
+ * Wasabi Systems, Inc.
+ * 4. The name of Wasabi Systems, Inc. may not be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ Memory Map
+
+ 0000.0000 * 128MB Typically SDRAM (on Core Board)
+ 0800.0000 * 256MB Typically PCI
+ 1800.0000 * 62MB Typically PCI
+ 1be0.0000 * 2MB Typically System controller's internal registers
+ 1c00.0000 * 32MB Typically not used
+ 1e00.0000 4MB Monitor Flash
+ 1e40.0000 12MB reserved
+ 1f00.0000 12MB Switches
+ LEDs
+ ASCII display
+ Soft reset
+ FPGA revision number
+ CBUS UART (tty2)
+ General Purpose I/O
+ I2C controller
+ 1f10.0000 * 11MB Typically System Controller specific
+ 1fc0.0000 4MB Maps to Monitor Flash
+ 1fd0.0000 * 3MB Typically System Controller specific
+
+ * depends on implementation of the Core Board and of software
+ */
+
+/*
+ CPU interrupts
+
+ NMI South Bridge or NMI button
+ 0 South Bridge INTR
+ 1 South Bridge SMI
+ 2 CBUS UART (tty2)
+ 3 COREHI (Core Card)
+ 4 CORELO (Core Card)
+ 5 Not used, driven inactive (typically CPU internal timer interrupt
+
+ IRQ mapping (as used by YAMON)
+
+ 0 Timer South Bridge
+ 1 Keyboard SuperIO
+ 2 Reserved by South Bridge (for cascading)
+ 3 UART (tty1) SuperIO
+ 4 UART (tty0) SuperIO
+ 5 Not used
+ 6 Floppy Disk SuperIO
+ 7 Parallel Port SuperIO
+ 8 Real Time Clock South Bridge
+ 9 I2C bus South Bridge
+ 10 PCI A,B,eth PCI slot 1..4, Ethernet
+ 11 PCI C,audio PCI slot 1..4, Audio, USB (South Bridge)
+ PCI D,USB
+ 12 Mouse SuperIO
+ 13 Reserved by South Bridge
+ 14 Primary IDE Primary IDE slot
+ 15 Secondary IDE Secondary IDE slot/Compact flash connector
+ */
+
+#define OCTEON_SYSTEMRAM_BASE 0x00000000 /* System RAM: */
+#define OCTEON_SYSTEMRAM_SIZE 0x08000000 /* 128 MByte */
+
+#define OCTEON_PCIMEM1_BASE 0x08000000 /* PCI 1 memory: */
+#define OCTEON_PCIMEM1_SIZE 0x08000000 /* 128 MByte */
+
+#define OCTEON_PCIMEM2_BASE 0x10000000 /* PCI 2 memory: */
+#define OCTEON_PCIMEM2_SIZE 0x08000000 /* 128 MByte */
+
+#define OCTEON_PCIMEM3_BASE 0x18000000 /* PCI 3 memory */
+#define OCTEON_PCIMEM3_SIZE 0x03e00000 /* 62 MByte */
+
+#define OCTEON_CORECTRL_BASE 0x1be00000 /* Core control: */
+#define OCTEON_CORECTRL_SIZE 0x00200000 /* 2 MByte */
+
+#define OCTEON_RESERVED_BASE1 0x1c000000 /* Reserved: */
+#define OCTEON_RESERVED_SIZE1 0x02000000 /* 32 MByte */
+
+#define OCTEON_MONITORFLASH_BASE 0x1e000000 /* Monitor Flash: */
+#define OCTEON_MONITORFLASH_SIZE 0x003e0000 /* 4 MByte */
+#define OCTEON_MONITORFLASH_SECTORSIZE 0x00010000 /* Sect. = 64 KB */
+
+#define OCTEON_FILEFLASH_BASE 0x1e3e0000 /* File Flash (for monitor): */
+#define OCTEON_FILEFLASH_SIZE 0x00020000 /* 128 KByte */
+
+#define OCTEON_FILEFLASH_SECTORSIZE 0x00010000 /* Sect. = 64 KB */
+
+#define OCTEON_RESERVED_BASE2 0x1e400000 /* Reserved: */
+#define OCTEON_RESERVED_SIZE2 0x00c00000 /* 12 MByte */
+
+#define OCTEON_FPGA_BASE 0x1f000000 /* FPGA: */
+#define OCTEON_FPGA_SIZE 0x00c00000 /* 12 MByte */
+
+#define OCTEON_NMISTATUS (OCTEON_FPGA_BASE + 0x24)
+#define OCTEON_NMI_SB 0x2 /* Pending NMI from the South Bridge */
+#define OCTEON_NMI_ONNMI 0x1 /* Pending NMI from the ON/NMI push button */
+
+#define OCTEON_NMIACK (OCTEON_FPGA_BASE + 0x104)
+#define OCTEON_NMIACK_ONNMI 0x1 /* Write 1 to acknowledge ON/NMI */
+
+#define OCTEON_SWITCH (OCTEON_FPGA_BASE + 0x200)
+#define OCTEON_SWITCH_MASK 0xff /* settings of DIP switch S2 */
+
+#define OCTEON_STATUS (OCTEON_FPGA_BASE + 0x208)
+#define OCTEON_ST_MFWR 0x10 /* Monitor Flash is write protected (JP1) */
+#define OCTEON_S54 0x08 /* switch S5-4 - set YAMON factory default mode */
+#define OCTEON_S53 0x04 /* switch S5-3 */
+#define OCTEON_BIGEND 0x02 /* switch S5-2 - big endian mode */
+
+#define OCTEON_JMPRS (OCTEON_FPGA_BASE + 0x210)
+#define OCTEON_JMPRS_PCICLK 0x1c /* PCI clock frequency */
+#define OCTEON_JMPRS_EELOCK 0x02 /* I2C EEPROM is write protected */
+
+#define OCTEON_LEDBAR (OCTEON_FPGA_BASE + 0x408)
+#define OCTEON_ASCIIWORD (OCTEON_FPGA_BASE + 0x410)
+#define OCTEON_ASCII_BASE (OCTEON_FPGA_BASE + 0x418)
+#define OCTEON_ASCIIPOS0 0x00
+#define OCTEON_ASCIIPOS1 0x08
+#define OCTEON_ASCIIPOS2 0x10
+#define OCTEON_ASCIIPOS3 0x18
+#define OCTEON_ASCIIPOS4 0x20
+#define OCTEON_ASCIIPOS5 0x28
+#define OCTEON_ASCIIPOS6 0x30
+#define OCTEON_ASCIIPOS7 0x38
+
+#define OCTEON_SOFTRES (OCTEON_FPGA_BASE + 0x500)
+#define OCTEON_GORESET 0x42 /* write this to OCTEON_SOFTRES for board reset */
+
+/*
+ * BRKRES is the number of milliseconds before a "break" on tty will
+ * trigger a reset. A value of 0 will disable the reset.
+ */
+#define OCTEON_BRKRES (OCTEON_FPGA_BASE + 0x508)
+#define OCTEON_BRKRES_MASK 0xff
+
+#define OCTEON_CBUSUART 0x8001180000000800ull
+/* 16C550C UART, 8 bit registers on 8 byte boundaries */
+/* RXTX 0x00 */
+/* INTEN 0x08 */
+/* IIFIFO 0x10 */
+/* LCTRL 0x18 */
+/* MCTRL 0x20 */
+/* LSTAT 0x28 */
+/* MSTAT 0x30 */
+/* SCRATCH 0x38 */
+#define OCTEON_CBUSUART_INTR 2
+
+#define OCTEON_GPIO_BASE (OCTEON_FPGA_BASE + 0xa00)
+#define OCTEON_GPOUT 0x0
+#define OCTEON_GPINP 0x8
+
+#define OCTEON_BOOTROM_BASE 0x1fc00000 /* Boot ROM: */
+#define OCTEON_BOOTROM_SIZE 0x00400000 /* 4 MByte */
+
+#define OCTEON_REVISION 0x1fc00010
+#define OCTEON_REV_FPGRV 0xff0000 /* CBUS FPGA revision */
+#define OCTEON_REV_CORID 0x00fc00 /* Core Board ID */
+#define OCTEON_REV_CORRV 0x000300 /* Core Board Revision */
+#define OCTEON_REV_PROID 0x0000f0 /* Product ID */
+#define OCTEON_REV_PRORV 0x00000f /* Product Revision */
+
+/* PCI definitions */
+
+#define OCTEON_UART0ADR 0x8001180000000800ull
+#define OCTEON_UART1ADR 0x8001180000000C00ull
+#define OCTEON_UART_SIZE 0x400
+
+#define OCTEON_MIO_BOOT_BIST_STAT 0x80011800000000F8ull
+
+
+
+/**************************
+ * To Delete
+ */
+#define OCTEON_SOUTHBRIDGE_INTR 0
+
+#define OCTEON_PCI0_IO_BASE OCTEON_PCIMEM3_BASE
+#define OCTEON_PCI0_ADDR( addr ) (OCTEON_PCI0_IO_BASE + (addr))
+
+#define OCTEON_RTCADR 0x70 // OCTEON_PCI_IO_ADDR8(0x70)
+#define OCTEON_RTCDAT 0x71 // OCTEON_PCI_IO_ADDR8(0x71)
+
+#define OCTEON_SMSC_COM1_ADR 0x3f8
+#define OCTEON_SMSC_COM2_ADR 0x2f8
+#define OCTEON_UARTT0ADR OCTEON_PCI0_ADDR(OCTEON_SMSC_COM1_ADR)
+#define OCTEON_UARTT1ADR OCTEON_SMSC_COM2_ADR // OCTEON_PCI0_ADDR(OCTEON_SMSC_COM2_ADR)
+
+#define OCTEON_SMSC_1284_ADR 0x378
+#define OCTEON_1284ADR OCTEON_SMSC_1284_ADR // OCTEON_PCI0_ADDR(OCTEON_SMSC_1284_ADR)
+
+#define OCTEON_SMSC_FDD_ADR 0x3f0
+#define OCTEON_FDDADR OCTEON_SMSC_FDD_ADR // OCTEON_PCI0_ADDR(OCTEON_SMSC_FDD_ADR)
+
+#define OCTEON_SMSC_KYBD_ADR 0x60 /* Fixed 0x60, 0x64 */
+#define OCTEON_KYBDADR OCTEON_SMSC_KYBD_ADR // OCTEON_PCI0_ADDR(OCTEON_SMSC_KYBD_ADR)
+#define OCTEON_SMSC_MOUSE_ADR OCTEON_SMSC_KYBD_ADR
+#define OCTEON_MOUSEADR OCTEON_KYBDADR
+
+
+#define OCTEON_DMA_PCI_PCIBASE 0x00000000UL
+#define OCTEON_DMA_PCI_PHYSBASE 0x00000000UL
+#define OCTEON_DMA_PCI_SIZE (256 * 1024 * 1024)
+
+#define OCTEON_DMA_ISA_PCIBASE 0x00800000UL
+#define OCTEON_DMA_ISA_PHYSBASE 0x00000000UL
+#define OCTEON_DMA_ISA_SIZE (8 * 1024 * 1024)
+
+#ifndef _LOCORE
+void led_bar(uint8_t);
+void led_display_word(uint32_t);
+void led_display_str(const char *);
+void led_display_char(int, uint8_t);
+#endif
diff --git a/sys/mips/cavium/octopci.c b/sys/mips/cavium/octopci.c
new file mode 100644
index 000000000000..d065ae399ccf
--- /dev/null
+++ b/sys/mips/cavium/octopci.c
@@ -0,0 +1,884 @@
+/*-
+ * Copyright (c) 2010 Juli Mallett <jmallett@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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+
+#include <sys/bus.h>
+#include <sys/endian.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 <contrib/octeon-sdk/cvmx.h>
+#include <contrib/octeon-sdk/cvmx-interrupt.h>
+
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+
+#include <dev/pci/pcib_private.h>
+
+#include <mips/cavium/octopcireg.h>
+#include <mips/cavium/octopcivar.h>
+
+#include "pcib_if.h"
+
+#define NPI_WRITE(addr, value) cvmx_write64_uint32((addr) ^ 4, (value))
+#define NPI_READ(addr) cvmx_read64_uint32((addr) ^ 4)
+
+struct octopci_softc {
+ device_t sc_dev;
+
+ unsigned sc_domain;
+ unsigned sc_bus;
+
+ unsigned sc_io_next;
+ struct rman sc_io;
+
+ unsigned sc_mem1_next;
+ struct rman sc_mem1;
+};
+
+static void octopci_identify(driver_t *, device_t);
+static int octopci_probe(device_t);
+static int octopci_attach(device_t);
+static int octopci_read_ivar(device_t, device_t, int,
+ uintptr_t *);
+static struct resource *octopci_alloc_resource(device_t, device_t, int, int *,
+ u_long, u_long, u_long, u_int);
+static int octopci_activate_resource(device_t, device_t, int, int,
+ struct resource *);
+static int octopci_maxslots(device_t);
+static uint32_t octopci_read_config(device_t, u_int, u_int, u_int, u_int, int);
+static void octopci_write_config(device_t, u_int, u_int, u_int, u_int,
+ uint32_t, int);
+static int octopci_route_interrupt(device_t, device_t, int);
+
+static void octopci_init_bar(device_t, unsigned, unsigned, unsigned, unsigned, uint8_t *);
+static unsigned octopci_init_device(device_t, unsigned, unsigned, unsigned, unsigned);
+static unsigned octopci_init_bus(device_t, unsigned);
+static uint64_t octopci_cs_addr(unsigned, unsigned, unsigned, unsigned);
+
+static void
+octopci_identify(driver_t *drv, device_t parent)
+{
+ BUS_ADD_CHILD(parent, 0, "pcib", 0);
+}
+
+static int
+octopci_probe(device_t dev)
+{
+ if (device_get_unit(dev) != 0)
+ return (ENXIO);
+ if (octeon_has_feature(OCTEON_FEATURE_PCIE))
+ return (ENXIO);
+ /* XXX Check sysinfo flag. */
+ device_set_desc(dev, "Cavium Octeon PCI bridge");
+ return (0);
+}
+
+static int
+octopci_attach(device_t dev)
+{
+ struct octopci_softc *sc;
+ cvmx_npi_mem_access_subid_t npi_mem_access_subid;
+ cvmx_npi_pci_int_arb_cfg_t npi_pci_int_arb_cfg;
+ cvmx_npi_ctl_status_t npi_ctl_status;
+ cvmx_pci_ctl_status_2_t pci_ctl_status_2;
+ cvmx_pci_cfg56_t pci_cfg56;
+ cvmx_pci_cfg22_t pci_cfg22;
+ cvmx_pci_cfg16_t pci_cfg16;
+ cvmx_pci_cfg19_t pci_cfg19;
+ cvmx_pci_cfg01_t pci_cfg01;
+ unsigned subbus;
+ unsigned i;
+ int error;
+
+ /*
+ * Reset the PCI bus.
+ */
+ cvmx_write_csr(CVMX_CIU_SOFT_PRST, 0x1);
+ cvmx_read_csr(CVMX_CIU_SOFT_PRST);
+
+ DELAY(2000);
+
+ npi_ctl_status.u64 = 0;
+ npi_ctl_status.s.max_word = 1;
+ npi_ctl_status.s.timer = 1;
+ cvmx_write_csr(CVMX_NPI_CTL_STATUS, npi_ctl_status.u64);
+
+ /*
+ * Set host mode.
+ */
+ switch (cvmx_sysinfo_get()->board_type) {
+#if defined(OCTEON_VENDOR_LANNER)
+ case CVMX_BOARD_TYPE_CUST_LANNER_MR320:
+ case CVMX_BOARD_TYPE_CUST_LANNER_MR955:
+ /* 32-bit PCI-X */
+ cvmx_write_csr(CVMX_CIU_SOFT_PRST, 0x0);
+ break;
+#endif
+ default:
+ /* 64-bit PCI-X */
+ cvmx_write_csr(CVMX_CIU_SOFT_PRST, 0x4);
+ break;
+ }
+ cvmx_read_csr(CVMX_CIU_SOFT_PRST);
+
+ DELAY(2000);
+
+ /*
+ * Enable BARs and configure big BAR mode.
+ */
+ pci_ctl_status_2.u32 = 0;
+ pci_ctl_status_2.s.bb1_hole = 5; /* 256MB hole in BAR1 */
+ pci_ctl_status_2.s.bb1_siz = 1; /* BAR1 is 2GB */
+ pci_ctl_status_2.s.bb_ca = 1; /* Bypass cache for big BAR */
+ pci_ctl_status_2.s.bb_es = 1; /* Do big BAR byte-swapping */
+ pci_ctl_status_2.s.bb1 = 1; /* BAR1 is big */
+ pci_ctl_status_2.s.bb0 = 1; /* BAR0 is big */
+ pci_ctl_status_2.s.bar2pres = 1; /* BAR2 present */
+ pci_ctl_status_2.s.pmo_amod = 1; /* Round-robin priority */
+ pci_ctl_status_2.s.tsr_hwm = 1;
+ pci_ctl_status_2.s.bar2_enb = 1; /* Enable BAR2 */
+ pci_ctl_status_2.s.bar2_esx = 1; /* Do BAR2 byte-swapping */
+ pci_ctl_status_2.s.bar2_cax = 1; /* Bypass cache for BAR2 */
+
+ NPI_WRITE(CVMX_NPI_PCI_CTL_STATUS_2, pci_ctl_status_2.u32);
+
+ DELAY(2000);
+
+ pci_ctl_status_2.u32 = NPI_READ(CVMX_NPI_PCI_CTL_STATUS_2);
+
+ device_printf(dev, "%u-bit PCI%s bus.\n",
+ pci_ctl_status_2.s.ap_64ad ? 64 : 32,
+ pci_ctl_status_2.s.ap_pcix ? "-X" : "");
+
+ /*
+ * Set up transaction splitting, etc., parameters.
+ */
+ pci_cfg19.u32 = 0;
+ pci_cfg19.s.mrbcm = 1;
+ if (pci_ctl_status_2.s.ap_pcix) {
+ pci_cfg19.s.mdrrmc = 0;
+ pci_cfg19.s.tdomc = 4;
+ } else {
+ pci_cfg19.s.mdrrmc = 2;
+ pci_cfg19.s.tdomc = 1;
+ }
+ NPI_WRITE(CVMX_NPI_PCI_CFG19, pci_cfg19.u32);
+ NPI_READ(CVMX_NPI_PCI_CFG19);
+
+ /*
+ * Set up PCI error handling and memory access.
+ */
+ pci_cfg01.u32 = 0;
+ pci_cfg01.s.fbbe = 1;
+ pci_cfg01.s.see = 1;
+ pci_cfg01.s.pee = 1;
+ pci_cfg01.s.me = 1;
+ pci_cfg01.s.msae = 1;
+ if (pci_ctl_status_2.s.ap_pcix) {
+ pci_cfg01.s.fbb = 0;
+ } else {
+ pci_cfg01.s.fbb = 1;
+ }
+ NPI_WRITE(CVMX_NPI_PCI_CFG01, pci_cfg01.u32);
+ NPI_READ(CVMX_NPI_PCI_CFG01);
+
+ /*
+ * Enable the Octeon bus arbiter.
+ */
+ npi_pci_int_arb_cfg.u64 = 0;
+ npi_pci_int_arb_cfg.s.en = 1;
+ cvmx_write_csr(CVMX_NPI_PCI_INT_ARB_CFG, npi_pci_int_arb_cfg.u64);
+
+ /*
+ * Disable master latency timer.
+ */
+ pci_cfg16.u32 = 0;
+ pci_cfg16.s.mltd = 1;
+ NPI_WRITE(CVMX_NPI_PCI_CFG16, pci_cfg16.u32);
+ NPI_READ(CVMX_NPI_PCI_CFG16);
+
+ /*
+ * Configure master arbiter.
+ */
+ pci_cfg22.u32 = 0;
+ pci_cfg22.s.flush = 1;
+ pci_cfg22.s.mrv = 255;
+ NPI_WRITE(CVMX_NPI_PCI_CFG22, pci_cfg22.u32);
+ NPI_READ(CVMX_NPI_PCI_CFG22);
+
+ /*
+ * Set up PCI-X capabilities.
+ */
+ if (pci_ctl_status_2.s.ap_pcix) {
+ pci_cfg56.u32 = 0;
+ pci_cfg56.s.most = 3;
+ pci_cfg56.s.roe = 1; /* Enable relaxed ordering */
+ pci_cfg56.s.dpere = 1;
+ pci_cfg56.s.ncp = 0xe8;
+ pci_cfg56.s.pxcid = 7;
+ NPI_WRITE(CVMX_NPI_PCI_CFG56, pci_cfg56.u32);
+ NPI_READ(CVMX_NPI_PCI_CFG56);
+ }
+
+ NPI_WRITE(CVMX_NPI_PCI_READ_CMD_6, 0x22);
+ NPI_READ(CVMX_NPI_PCI_READ_CMD_6);
+ NPI_WRITE(CVMX_NPI_PCI_READ_CMD_C, 0x33);
+ NPI_READ(CVMX_NPI_PCI_READ_CMD_C);
+ NPI_WRITE(CVMX_NPI_PCI_READ_CMD_E, 0x33);
+ NPI_READ(CVMX_NPI_PCI_READ_CMD_E);
+
+ /*
+ * Configure MEM1 sub-DID access.
+ */
+ npi_mem_access_subid.u64 = 0;
+ npi_mem_access_subid.s.esr = 1; /* Byte-swap on read */
+ npi_mem_access_subid.s.esw = 1; /* Byte-swap on write */
+ switch (cvmx_sysinfo_get()->board_type) {
+#if defined(OCTEON_VENDOR_LANNER)
+ case CVMX_BOARD_TYPE_CUST_LANNER_MR955:
+ npi_mem_access_subid.s.shortl = 1;
+ break;
+#endif
+ default:
+ break;
+ }
+ cvmx_write_csr(CVMX_NPI_MEM_ACCESS_SUBID3, npi_mem_access_subid.u64);
+
+ /*
+ * Configure BAR2. Linux says this has to come first.
+ */
+ NPI_WRITE(CVMX_NPI_PCI_CFG08, 0x00000000);
+ NPI_READ(CVMX_NPI_PCI_CFG08);
+ NPI_WRITE(CVMX_NPI_PCI_CFG09, 0x00000080);
+ NPI_READ(CVMX_NPI_PCI_CFG09);
+
+ /*
+ * Disable BAR1 IndexX.
+ */
+ for (i = 0; i < 32; i++) {
+ NPI_WRITE(CVMX_NPI_PCI_BAR1_INDEXX(i), 0);
+ NPI_READ(CVMX_NPI_PCI_BAR1_INDEXX(i));
+ }
+
+ /*
+ * Configure BAR0 and BAR1.
+ */
+ NPI_WRITE(CVMX_NPI_PCI_CFG04, 0x00000000);
+ NPI_READ(CVMX_NPI_PCI_CFG04);
+ NPI_WRITE(CVMX_NPI_PCI_CFG05, 0x00000000);
+ NPI_READ(CVMX_NPI_PCI_CFG05);
+
+ NPI_WRITE(CVMX_NPI_PCI_CFG06, 0x80000000);
+ NPI_READ(CVMX_NPI_PCI_CFG06);
+ NPI_WRITE(CVMX_NPI_PCI_CFG07, 0x00000000);
+ NPI_READ(CVMX_NPI_PCI_CFG07);
+
+ /*
+ * Clear PCI interrupts.
+ */
+ cvmx_write_csr(CVMX_NPI_PCI_INT_SUM2, 0xffffffffffffffffull);
+
+ sc = device_get_softc(dev);
+ sc->sc_dev = dev;
+ sc->sc_domain = 0;
+ sc->sc_bus = 0;
+
+ sc->sc_io.rm_type = RMAN_ARRAY;
+ sc->sc_io.rm_descr = "Cavium Octeon PCI I/O Ports";
+ error = rman_init(&sc->sc_io);
+ if (error != 0)
+ return (error);
+
+ error = rman_manage_region(&sc->sc_io, CVMX_OCT_PCI_IO_BASE,
+ CVMX_OCT_PCI_IO_BASE + CVMX_OCT_PCI_IO_SIZE);
+ if (error != 0)
+ return (error);
+
+ sc->sc_mem1.rm_type = RMAN_ARRAY;
+ sc->sc_mem1.rm_descr = "Cavium Octeon PCI Memory";
+ error = rman_init(&sc->sc_mem1);
+ if (error != 0)
+ return (error);
+
+ error = rman_manage_region(&sc->sc_mem1, CVMX_OCT_PCI_MEM1_BASE,
+ CVMX_OCT_PCI_MEM1_BASE + CVMX_OCT_PCI_MEM1_SIZE);
+ if (error != 0)
+ return (error);
+
+ /*
+ * Next offsets for resource allocation in octopci_init_bar.
+ */
+ sc->sc_io_next = 0;
+ sc->sc_mem1_next = 0;
+
+ /*
+ * Configure devices.
+ */
+ octopci_write_config(dev, 0, 0, 0, PCIR_SUBBUS_1, 0xff, 1);
+ subbus = octopci_init_bus(dev, 0);
+ octopci_write_config(dev, 0, 0, 0, PCIR_SUBBUS_1, subbus, 1);
+
+ device_add_child(dev, "pci", 0);
+
+ return (bus_generic_attach(dev));
+}
+
+static int
+octopci_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
+{
+ struct octopci_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ switch (which) {
+ case PCIB_IVAR_DOMAIN:
+ *result = sc->sc_domain;
+ return (0);
+ case PCIB_IVAR_BUS:
+ *result = sc->sc_bus;
+ return (0);
+
+ }
+ return (ENOENT);
+}
+
+static struct resource *
+octopci_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 octopci_softc *sc;
+ struct resource *res;
+ struct rman *rm;
+ int error;
+
+ sc = device_get_softc(bus);
+
+ switch (type) {
+ case SYS_RES_IRQ:
+ res = bus_generic_alloc_resource(bus, child, type, rid, start,
+ end, count, flags);
+ if (res != NULL)
+ return (res);
+ return (NULL);
+ case SYS_RES_MEMORY:
+ rm = &sc->sc_mem1;
+ break;
+ case SYS_RES_IOPORT:
+ rm = &sc->sc_io;
+ break;
+ default:
+ return (NULL);
+ }
+
+ res = rman_reserve_resource(rm, start, end, count, flags, child);
+ if (res == NULL)
+ return (NULL);
+
+ rman_set_rid(res, *rid);
+ rman_set_bustag(res, octopci_bus_space);
+
+ switch (type) {
+ case SYS_RES_MEMORY:
+ rman_set_bushandle(res, CVMX_ADDR_DID(CVMX_FULL_DID(CVMX_OCT_DID_PCI, CVMX_OCT_SUBDID_PCI_MEM1)) + rman_get_start(res));
+ break;
+ case SYS_RES_IOPORT:
+ rman_set_bushandle(res, CVMX_ADDR_DID(CVMX_FULL_DID(CVMX_OCT_DID_PCI, CVMX_OCT_SUBDID_PCI_IO)) + rman_get_start(res));
+#if __mips_n64
+ rman_set_virtual(res, (void *)rman_get_bushandle(res));
+#else
+ /*
+ * XXX
+ * We can't access ports via a 32-bit pointer.
+ */
+ rman_set_virtual(res, NULL);
+#endif
+ break;
+ }
+
+ if ((flags & RF_ACTIVE) != 0) {
+ error = bus_activate_resource(child, type, *rid, res);
+ if (error != 0) {
+ rman_release_resource(res);
+ return (NULL);
+ }
+ }
+
+ return (res);
+}
+
+static int
+octopci_activate_resource(device_t bus, device_t child, int type, int rid,
+ struct resource *res)
+{
+ bus_space_handle_t bh;
+ int error;
+
+ switch (type) {
+ case SYS_RES_IRQ:
+ error = bus_generic_activate_resource(bus, child, type, rid,
+ res);
+ if (error != 0)
+ return (error);
+ return (0);
+ case SYS_RES_MEMORY:
+ case SYS_RES_IOPORT:
+ error = bus_space_map(rman_get_bustag(res),
+ rman_get_bushandle(res), rman_get_size(res), 0, &bh);
+ if (error != 0)
+ return (error);
+ rman_set_bushandle(res, bh);
+ break;
+ default:
+ return (ENXIO);
+ }
+
+ error = rman_activate_resource(res);
+ if (error != 0)
+ return (error);
+ return (0);
+}
+
+static int
+octopci_maxslots(device_t dev)
+{
+ return (PCI_SLOTMAX);
+}
+
+static uint32_t
+octopci_read_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg,
+ int bytes)
+{
+ struct octopci_softc *sc;
+ uint64_t addr;
+ uint32_t data;
+
+ sc = device_get_softc(dev);
+
+ addr = octopci_cs_addr(bus, slot, func, reg);
+
+ switch (bytes) {
+ case 4:
+ data = le32toh(cvmx_read64_uint32(addr));
+ return (data);
+ case 2:
+ data = le16toh(cvmx_read64_uint16(addr));
+ return (data);
+ case 1:
+ data = cvmx_read64_uint8(addr);
+ return (data);
+ default:
+ return ((uint32_t)-1);
+ }
+}
+
+static void
+octopci_write_config(device_t dev, u_int bus, u_int slot, u_int func,
+ u_int reg, uint32_t data, int bytes)
+{
+ struct octopci_softc *sc;
+ uint64_t addr;
+
+ sc = device_get_softc(dev);
+
+ addr = octopci_cs_addr(bus, slot, func, reg);
+
+ switch (bytes) {
+ case 4:
+ cvmx_write64_uint32(addr, htole32(data));
+ return;
+ case 2:
+ cvmx_write64_uint16(addr, htole16(data));
+ return;
+ case 1:
+ cvmx_write64_uint8(addr, data);
+ return;
+ default:
+ return;
+ }
+}
+
+static int
+octopci_route_interrupt(device_t dev, device_t child, int pin)
+{
+ struct octopci_softc *sc;
+ unsigned bus, slot, func;
+ unsigned irq;
+
+ sc = device_get_softc(dev);
+
+ bus = pci_get_bus(child);
+ slot = pci_get_slot(child);
+ func = pci_get_function(child);
+
+ /*
+ * Board types we have to know at compile-time.
+ */
+#if defined(OCTEON_BOARD_CAPK_0100ND)
+ if (bus == 0 && slot == 12 && func == 0)
+ return (CVMX_IRQ_PCI_INT2);
+#endif
+
+ /*
+ * For board types we can determine at runtime.
+ */
+ switch (cvmx_sysinfo_get()->board_type) {
+#if defined(OCTEON_VENDOR_LANNER)
+ case CVMX_BOARD_TYPE_CUST_LANNER_MR955:
+ return (CVMX_IRQ_PCI_INT0 + pin - 1);
+ case CVMX_BOARD_TYPE_CUST_LANNER_MR320:
+ if (slot < 32) {
+ if (slot == 3 || slot == 9)
+ irq = pin;
+ else
+ irq = pin - 1;
+ return (CVMX_IRQ_PCI_INT0 + (irq & 3));
+ }
+ break;
+#endif
+ default:
+ break;
+ }
+
+ irq = slot + pin - 3;
+
+ return (CVMX_IRQ_PCI_INT0 + (irq & 3));
+}
+
+static void
+octopci_init_bar(device_t dev, unsigned b, unsigned s, unsigned f, unsigned barnum, uint8_t *commandp)
+{
+ struct octopci_softc *sc;
+ uint32_t bar;
+ unsigned size;
+
+ sc = device_get_softc(dev);
+
+ octopci_write_config(dev, b, s, f, PCIR_BAR(barnum), 0xffffffff, 4);
+ bar = octopci_read_config(dev, b, s, f, PCIR_BAR(barnum), 4);
+
+ if (bar == 0) {
+ /* Bar not implemented. */
+ return;
+ }
+
+ /* XXX Some of this is wrong for 64-bit busses. */
+
+ if (PCI_BAR_IO(bar)) {
+ size = ~(bar & PCIM_BAR_IO_BASE) + 1;
+
+ sc->sc_io_next = (sc->sc_io_next + size - 1) & ~(size - 1);
+ if (sc->sc_io_next + size > CVMX_OCT_PCI_IO_SIZE) {
+ device_printf(dev, "%02x.%02x:%02x: no ports for BAR%u.\n",
+ b, s, f, barnum);
+ return;
+ }
+ octopci_write_config(dev, b, s, f, PCIR_BAR(barnum),
+ CVMX_OCT_PCI_IO_BASE + sc->sc_io_next, 4);
+ sc->sc_io_next += size;
+
+ /*
+ * Enable I/O ports.
+ */
+ *commandp |= PCIM_CMD_PORTEN;
+ } else {
+ size = ~(bar & (uint32_t)PCIM_BAR_MEM_BASE) + 1;
+
+ sc->sc_mem1_next = (sc->sc_mem1_next + size - 1) & ~(size - 1);
+ if (sc->sc_mem1_next + size > CVMX_OCT_PCI_MEM1_SIZE) {
+ device_printf(dev, "%02x.%02x:%02x: no memory for BAR%u.\n",
+ b, s, f, barnum);
+ return;
+ }
+ octopci_write_config(dev, b, s, f, PCIR_BAR(barnum),
+ CVMX_OCT_PCI_MEM1_BASE + sc->sc_mem1_next, 4);
+ sc->sc_mem1_next += size;
+
+ /*
+ * Enable memory access.
+ */
+ *commandp |= PCIM_CMD_MEMEN;
+ }
+}
+
+static unsigned
+octopci_init_device(device_t dev, unsigned b, unsigned s, unsigned f, unsigned secbus)
+{
+ unsigned barnum, bars;
+ uint8_t brctl;
+ uint8_t class, subclass;
+ uint8_t command;
+ uint8_t hdrtype;
+
+ /* Read header type (again.) */
+ hdrtype = octopci_read_config(dev, b, s, f, PCIR_HDRTYPE, 1);
+
+ /*
+ * Disable memory and I/O while programming BARs.
+ */
+ command = octopci_read_config(dev, b, s, f, PCIR_COMMAND, 1);
+ command &= ~(PCIM_CMD_MEMEN | PCIM_CMD_PORTEN);
+ octopci_write_config(dev, b, s, f, PCIR_COMMAND, command, 1);
+
+ DELAY(10000);
+
+ /* Program BARs. */
+ switch (hdrtype & PCIM_HDRTYPE) {
+ case PCIM_HDRTYPE_NORMAL:
+ bars = 6;
+ break;
+ case PCIM_HDRTYPE_BRIDGE:
+ bars = 2;
+ break;
+ case PCIM_HDRTYPE_CARDBUS:
+ bars = 0;
+ break;
+ default:
+ device_printf(dev, "%02x.%02x:%02x: invalid header type %#x\n",
+ b, s, f, hdrtype);
+ return (secbus);
+ }
+
+ for (barnum = 0; barnum < bars; barnum++)
+ octopci_init_bar(dev, b, s, f, barnum, &command);
+
+ /* Enable bus mastering. */
+ command |= PCIM_CMD_BUSMASTEREN;
+
+ /* Enable whatever facilities the BARs require. */
+ octopci_write_config(dev, b, s, f, PCIR_COMMAND, command, 1);
+
+ DELAY(10000);
+
+ /*
+ * Set cache line size. On Octeon it should be 128 bytes,
+ * but according to Linux some Intel bridges have trouble
+ * with values over 64 bytes, so use 64 bytes.
+ */
+ octopci_write_config(dev, b, s, f, PCIR_CACHELNSZ, 16, 1);
+
+ /* Set latency timer. */
+ octopci_write_config(dev, b, s, f, PCIR_LATTIMER, 48, 1);
+
+ /* Board-specific or device-specific fixups and workarounds. */
+ switch (cvmx_sysinfo_get()->board_type) {
+#if defined(OCTEON_VENDOR_LANNER)
+ case CVMX_BOARD_TYPE_CUST_LANNER_MR955:
+ if (b == 1 && s == 7 && f == 0) {
+ bus_addr_t busaddr, unitbusaddr;
+ uint32_t bar;
+ uint32_t tmp;
+ unsigned unit;
+
+ /*
+ * Set Tx DMA power.
+ */
+ bar = octopci_read_config(dev, b, s, f,
+ PCIR_BAR(3), 4);
+ busaddr = CVMX_ADDR_DID(CVMX_FULL_DID(CVMX_OCT_DID_PCI,
+ CVMX_OCT_SUBDID_PCI_MEM1));
+ busaddr += (bar & (uint32_t)PCIM_BAR_MEM_BASE);
+ for (unit = 0; unit < 4; unit++) {
+ unitbusaddr = busaddr + 0x430 + (unit << 8);
+ tmp = le32toh(cvmx_read64_uint32(unitbusaddr));
+ tmp &= ~0x700;
+ tmp |= 0x300;
+ cvmx_write64_uint32(unitbusaddr, htole32(tmp));
+ }
+ }
+ break;
+#endif
+ default:
+ break;
+ }
+
+ /* Configure PCI-PCI bridges. */
+ class = octopci_read_config(dev, b, s, f, PCIR_CLASS, 1);
+ if (class != PCIC_BRIDGE)
+ return (secbus);
+
+ subclass = octopci_read_config(dev, b, s, f, PCIR_SUBCLASS, 1);
+ if (subclass != PCIS_BRIDGE_PCI)
+ return (secbus);
+
+ /* Enable memory and I/O access. */
+ command |= PCIM_CMD_MEMEN | PCIM_CMD_PORTEN;
+ octopci_write_config(dev, b, s, f, PCIR_COMMAND, command, 1);
+
+ /* Enable errors and parity checking. Do a bus reset. */
+ brctl = octopci_read_config(dev, b, s, f, PCIR_BRIDGECTL_1, 1);
+ brctl |= PCIB_BCR_PERR_ENABLE | PCIB_BCR_SERR_ENABLE;
+
+ /* Perform a secondary bus reset. */
+ brctl |= PCIB_BCR_SECBUS_RESET;
+ octopci_write_config(dev, b, s, f, PCIR_BRIDGECTL_1, brctl, 1);
+ DELAY(100000);
+ brctl &= ~PCIB_BCR_SECBUS_RESET;
+ octopci_write_config(dev, b, s, f, PCIR_BRIDGECTL_1, brctl, 1);
+
+ secbus++;
+
+ /* Program memory and I/O ranges. */
+ octopci_write_config(dev, b, s, f, PCIR_MEMBASE_1,
+ CVMX_OCT_PCI_MEM1_BASE >> 16, 2);
+ octopci_write_config(dev, b, s, f, PCIR_MEMLIMIT_1,
+ (CVMX_OCT_PCI_MEM1_BASE + CVMX_OCT_PCI_MEM1_SIZE - 1) >> 16, 2);
+
+ octopci_write_config(dev, b, s, f, PCIR_IOBASEL_1,
+ CVMX_OCT_PCI_IO_BASE >> 8, 1);
+ octopci_write_config(dev, b, s, f, PCIR_IOBASEH_1,
+ CVMX_OCT_PCI_IO_BASE >> 16, 2);
+
+ octopci_write_config(dev, b, s, f, PCIR_IOLIMITL_1,
+ (CVMX_OCT_PCI_IO_BASE + CVMX_OCT_PCI_IO_SIZE - 1) >> 8, 1);
+ octopci_write_config(dev, b, s, f, PCIR_IOLIMITH_1,
+ (CVMX_OCT_PCI_IO_BASE + CVMX_OCT_PCI_IO_SIZE - 1) >> 16, 2);
+
+ /* Program prefetchable memory decoder. */
+ /* XXX */
+
+ /* Probe secondary/subordinate buses. */
+ octopci_write_config(dev, b, s, f, PCIR_PRIBUS_1, b, 1);
+ octopci_write_config(dev, b, s, f, PCIR_SECBUS_1, secbus, 1);
+ octopci_write_config(dev, b, s, f, PCIR_SUBBUS_1, 0xff, 1);
+
+ /* Perform a secondary bus reset. */
+ brctl |= PCIB_BCR_SECBUS_RESET;
+ octopci_write_config(dev, b, s, f, PCIR_BRIDGECTL_1, brctl, 1);
+ DELAY(100000);
+ brctl &= ~PCIB_BCR_SECBUS_RESET;
+ octopci_write_config(dev, b, s, f, PCIR_BRIDGECTL_1, brctl, 1);
+
+ /* Give the bus time to settle now before reading configspace. */
+ DELAY(100000);
+
+ secbus = octopci_init_bus(dev, secbus);
+
+ octopci_write_config(dev, b, s, f, PCIR_SUBBUS_1, secbus, 1);
+
+ return (secbus);
+}
+
+static unsigned
+octopci_init_bus(device_t dev, unsigned b)
+{
+ unsigned s, f;
+ uint8_t hdrtype;
+ unsigned secbus;
+
+ secbus = b;
+
+ for (s = 0; s <= PCI_SLOTMAX; s++) {
+ for (f = 0; f <= PCI_FUNCMAX; f++) {
+ hdrtype = octopci_read_config(dev, b, s, f, PCIR_HDRTYPE, 1);
+
+ if (hdrtype == 0xff) {
+ if (f == 0)
+ break; /* Next slot. */
+ continue; /* Next function. */
+ }
+
+ secbus = octopci_init_device(dev, b, s, f, secbus);
+
+ if (f == 0 && (hdrtype & PCIM_MFDEV) == 0)
+ break; /* Next slot. */
+ }
+ }
+
+ return (secbus);
+}
+
+static uint64_t
+octopci_cs_addr(unsigned bus, unsigned slot, unsigned func, unsigned reg)
+{
+ octeon_pci_config_space_address_t pci_addr;
+
+ pci_addr.u64 = 0;
+ pci_addr.s.upper = 2;
+ pci_addr.s.io = 1;
+ pci_addr.s.did = 3;
+ pci_addr.s.subdid = CVMX_OCT_SUBDID_PCI_CFG;
+ pci_addr.s.endian_swap = 1;
+ pci_addr.s.bus = bus;
+ pci_addr.s.dev = slot;
+ pci_addr.s.func = func;
+ pci_addr.s.reg = reg;
+
+ return (pci_addr.u64);
+}
+
+static device_method_t octopci_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_identify, octopci_identify),
+ DEVMETHOD(device_probe, octopci_probe),
+ DEVMETHOD(device_attach, octopci_attach),
+
+ /* Bus interface */
+ DEVMETHOD(bus_read_ivar, octopci_read_ivar),
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+ DEVMETHOD(bus_alloc_resource, octopci_alloc_resource),
+ DEVMETHOD(bus_release_resource, bus_generic_release_resource),
+ DEVMETHOD(bus_activate_resource,octopci_activate_resource),
+ DEVMETHOD(bus_deactivate_resource,bus_generic_deactivate_resource),
+ DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
+ DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
+
+ DEVMETHOD(bus_add_child, bus_generic_add_child),
+
+ /* pcib interface */
+ DEVMETHOD(pcib_maxslots, octopci_maxslots),
+ DEVMETHOD(pcib_read_config, octopci_read_config),
+ DEVMETHOD(pcib_write_config, octopci_write_config),
+ DEVMETHOD(pcib_route_interrupt, octopci_route_interrupt),
+
+ {0, 0}
+};
+
+static driver_t octopci_driver = {
+ "pcib",
+ octopci_methods,
+ sizeof(struct octopci_softc),
+};
+static devclass_t octopci_devclass;
+DRIVER_MODULE(octopci, ciu, octopci_driver, octopci_devclass, 0, 0);
diff --git a/sys/mips/cavium/octopci_bus_space.c b/sys/mips/cavium/octopci_bus_space.c
new file mode 100644
index 000000000000..870fe325f371
--- /dev/null
+++ b/sys/mips/cavium/octopci_bus_space.c
@@ -0,0 +1,583 @@
+/* $NetBSD: bus.h,v 1.12 1997/10/01 08:25:15 fvdl Exp $ */
+/*-
+ * $Id: bus.h,v 1.6 2007/08/09 11:23:32 katta Exp $
+ *
+ * Copyright (c) 1996, 1997 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
+ * NASA Ames Research Center.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Copyright (c) 1996 Charles M. Hannum. All rights reserved.
+ * Copyright (c) 1996 Christopher G. Demetriou. 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Christopher G. Demetriou
+ * for the NetBSD Project.
+ * 4. 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.
+ *
+ * from: src/sys/alpha/include/bus.h,v 1.5 1999/08/28 00:38:40 peter
+ * $FreeBSD$
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/ktr.h>
+#include <sys/endian.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+#include <vm/vm_kern.h>
+#include <vm/vm_extern.h>
+
+#include <machine/bus.h>
+#include <machine/cache.h>
+
+#include <mips/cavium/octopcivar.h>
+
+#include <contrib/octeon-sdk/cvmx.h>
+
+static struct bus_space octopci_space = {
+ /* cookie */
+ (void *) 0,
+
+ /* mapping/unmapping */
+ octopci_bs_map,
+ octopci_bs_unmap,
+ octopci_bs_subregion,
+
+ /* allocation/deallocation */
+ NULL,
+ NULL,
+
+ /* barrier */
+ octopci_bs_barrier,
+
+ /* read (single) */
+ octopci_bs_r_1,
+ octopci_bs_r_2,
+ octopci_bs_r_4,
+ NULL,
+
+ /* read multiple */
+ octopci_bs_rm_1,
+ octopci_bs_rm_2,
+ octopci_bs_rm_4,
+ NULL,
+
+ /* read region */
+ octopci_bs_rr_1,
+ octopci_bs_rr_2,
+ octopci_bs_rr_4,
+ NULL,
+
+ /* write (single) */
+ octopci_bs_w_1,
+ octopci_bs_w_2,
+ octopci_bs_w_4,
+ NULL,
+
+ /* write multiple */
+ octopci_bs_wm_1,
+ octopci_bs_wm_2,
+ octopci_bs_wm_4,
+ NULL,
+
+ /* write region */
+ NULL,
+ octopci_bs_wr_2,
+ octopci_bs_wr_4,
+ NULL,
+
+ /* set multiple */
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+
+ /* set region */
+ NULL,
+ octopci_bs_sr_2,
+ octopci_bs_sr_4,
+ NULL,
+
+ /* copy */
+ NULL,
+ octopci_bs_c_2,
+ NULL,
+ NULL,
+
+ /* read (single) stream */
+ octopci_bs_r_1,
+ octopci_bs_r_2,
+ octopci_bs_r_4,
+ NULL,
+
+ /* read multiple stream */
+ octopci_bs_rm_1,
+ octopci_bs_rm_2,
+ octopci_bs_rm_4,
+ NULL,
+
+ /* read region stream */
+ octopci_bs_rr_1,
+ octopci_bs_rr_2,
+ octopci_bs_rr_4,
+ NULL,
+
+ /* write (single) stream */
+ octopci_bs_w_1,
+ octopci_bs_w_2,
+ octopci_bs_w_4,
+ NULL,
+
+ /* write multiple stream */
+ octopci_bs_wm_1,
+ octopci_bs_wm_2,
+ octopci_bs_wm_4,
+ NULL,
+
+ /* write region stream */
+ NULL,
+ octopci_bs_wr_2,
+ octopci_bs_wr_4,
+ NULL,
+};
+
+#define rd8(a) cvmx_read64_uint8(a)
+#define rd16(a) le16toh(cvmx_read64_uint16(a))
+#define rd32(a) le32toh(cvmx_read64_uint32(a))
+#define wr8(a, v) cvmx_write64_uint8(a, v)
+#define wr16(a, v) cvmx_write64_uint16(a, htole16(v))
+#define wr32(a, v) cvmx_write64_uint32(a, htole32(v))
+
+/* octopci bus_space tag */
+bus_space_tag_t octopci_bus_space = &octopci_space;
+
+int
+octopci_bs_map(void *t __unused, bus_addr_t addr,
+ bus_size_t size __unused, int flags __unused,
+ bus_space_handle_t *bshp)
+{
+
+ *bshp = addr;
+ return (0);
+}
+
+void
+octopci_bs_unmap(void *t __unused, bus_space_handle_t bh __unused,
+ bus_size_t size __unused)
+{
+
+ /* Do nothing */
+}
+
+int
+octopci_bs_subregion(void *t __unused, bus_space_handle_t handle __unused,
+ bus_size_t offset __unused, bus_size_t size __unused,
+ bus_space_handle_t *nhandle __unused)
+{
+
+ printf("SUBREGION?!?!?!\n");
+ /* Do nothing */
+ return (0);
+}
+
+uint8_t
+octopci_bs_r_1(void *t, bus_space_handle_t handle,
+ bus_size_t offset)
+{
+
+ return (rd8(handle + offset));
+}
+
+uint16_t
+octopci_bs_r_2(void *t, bus_space_handle_t handle,
+ bus_size_t offset)
+{
+
+ return (rd16(handle + offset));
+}
+
+uint32_t
+octopci_bs_r_4(void *t, bus_space_handle_t handle,
+ bus_size_t offset)
+{
+
+ return (rd32(handle + offset));
+}
+
+
+void
+octopci_bs_rm_1(void *t, bus_space_handle_t bsh,
+ bus_size_t offset, uint8_t *addr, size_t count)
+{
+
+ while (count--)
+ *addr++ = rd8(bsh + offset);
+}
+
+void
+octopci_bs_rm_2(void *t, bus_space_handle_t bsh,
+ bus_size_t offset, uint16_t *addr, size_t count)
+{
+ bus_addr_t baddr = bsh + offset;
+
+ while (count--)
+ *addr++ = rd16(baddr);
+}
+
+void
+octopci_bs_rm_4(void *t, bus_space_handle_t bsh,
+ bus_size_t offset, uint32_t *addr, size_t count)
+{
+ bus_addr_t baddr = bsh + offset;
+
+ while (count--)
+ *addr++ = rd32(baddr);
+}
+
+
+/*
+ * Read `count' 1, 2, 4, or 8 byte quantities from bus space
+ * described by tag/handle and starting at `offset' and copy into
+ * buffer provided.
+ */
+void
+octopci_bs_rr_1(void *t, bus_space_handle_t bsh,
+ bus_size_t offset, uint8_t *addr, size_t count)
+{
+ bus_addr_t baddr = bsh + offset;
+
+ while (count--) {
+ *addr++ = rd8(baddr);
+ baddr += 1;
+ }
+}
+
+void
+octopci_bs_rr_2(void *t, bus_space_handle_t bsh,
+ bus_size_t offset, uint16_t *addr, size_t count)
+{
+ bus_addr_t baddr = bsh + offset;
+
+ while (count--) {
+ *addr++ = rd16(baddr);
+ baddr += 2;
+ }
+}
+
+void
+octopci_bs_rr_4(void *t, bus_space_handle_t bsh,
+ bus_size_t offset, uint32_t *addr, size_t count)
+{
+ bus_addr_t baddr = bsh + offset;
+
+ while (count--) {
+ *addr++ = rd32(baddr);
+ baddr += 4;
+ }
+}
+
+/*
+ * Write the 1, 2, 4, or 8 byte value `value' to bus space
+ * described by tag/handle/offset.
+ */
+void
+octopci_bs_w_1(void *t, bus_space_handle_t bsh,
+ bus_size_t offset, uint8_t value)
+{
+
+ wr8(bsh + offset, value);
+}
+
+void
+octopci_bs_w_2(void *t, bus_space_handle_t bsh,
+ bus_size_t offset, uint16_t value)
+{
+
+ wr16(bsh + offset, value);
+}
+
+void
+octopci_bs_w_4(void *t, bus_space_handle_t bsh,
+ bus_size_t offset, uint32_t value)
+{
+
+ wr32(bsh + offset, value);
+}
+
+/*
+ * Write `count' 1, 2, 4, or 8 byte quantities from the buffer
+ * provided to bus space described by tag/handle/offset.
+ */
+void
+octopci_bs_wm_1(void *t, bus_space_handle_t bsh,
+ bus_size_t offset, const uint8_t *addr, size_t count)
+{
+ bus_addr_t baddr = bsh + offset;
+
+ while (count--)
+ wr8(baddr, *addr++);
+}
+
+void
+octopci_bs_wm_2(void *t, bus_space_handle_t bsh,
+ bus_size_t offset, const uint16_t *addr, size_t count)
+{
+ bus_addr_t baddr = bsh + offset;
+
+ while (count--)
+ wr16(baddr, *addr++);
+}
+
+void
+octopci_bs_wm_4(void *t, bus_space_handle_t bsh,
+ bus_size_t offset, const uint32_t *addr, size_t count)
+{
+ bus_addr_t baddr = bsh + offset;
+
+ while (count--)
+ wr32(baddr, *addr++);
+}
+
+/*
+ * Write `count' 1, 2, 4, or 8 byte quantities from the buffer provided
+ * to bus space described by tag/handle starting at `offset'.
+ */
+void
+octopci_bs_wr_1(void *t, bus_space_handle_t bsh,
+ bus_size_t offset, const uint8_t *addr, size_t count)
+{
+ bus_addr_t baddr = bsh + offset;
+
+ while (count--) {
+ wr8(baddr, *addr++);
+ baddr += 1;
+ }
+}
+
+void
+octopci_bs_wr_2(void *t, bus_space_handle_t bsh,
+ bus_size_t offset, const uint16_t *addr, size_t count)
+{
+ bus_addr_t baddr = bsh + offset;
+
+ while (count--) {
+ wr16(baddr, *addr++);
+ baddr += 2;
+ }
+}
+
+void
+octopci_bs_wr_4(void *t, bus_space_handle_t bsh,
+ bus_size_t offset, const uint32_t *addr, size_t count)
+{
+ bus_addr_t baddr = bsh + offset;
+
+ while (count--) {
+ wr32(baddr, *addr++);
+ baddr += 4;
+ }
+}
+
+/*
+ * Write the 1, 2, 4, or 8 byte value `val' to bus space described
+ * by tag/handle/offset `count' times.
+ */
+void
+octopci_bs_sm_1(void *t, bus_space_handle_t bsh,
+ bus_size_t offset, uint8_t value, size_t count)
+{
+ bus_addr_t addr = bsh + offset;
+
+ while (count--)
+ wr8(addr, value);
+}
+
+void
+octopci_bs_sm_2(void *t, bus_space_handle_t bsh,
+ bus_size_t offset, uint16_t value, size_t count)
+{
+ bus_addr_t addr = bsh + offset;
+
+ while (count--)
+ wr16(addr, value);
+}
+
+void
+octopci_bs_sm_4(void *t, bus_space_handle_t bsh,
+ bus_size_t offset, uint32_t value, size_t count)
+{
+ bus_addr_t addr = bsh + offset;
+
+ while (count--)
+ wr32(addr, value);
+}
+
+/*
+ * Write `count' 1, 2, 4, or 8 byte value `val' to bus space described
+ * by tag/handle starting at `offset'.
+ */
+void
+octopci_bs_sr_1(void *t, bus_space_handle_t bsh,
+ bus_size_t offset, uint8_t value, size_t count)
+{
+ bus_addr_t addr = bsh + offset;
+
+ for (; count != 0; count--, addr++)
+ wr8(addr, value);
+}
+
+void
+octopci_bs_sr_2(void *t, bus_space_handle_t bsh,
+ bus_size_t offset, uint16_t value, size_t count)
+{
+ bus_addr_t addr = bsh + offset;
+
+ for (; count != 0; count--, addr += 2)
+ wr16(addr, value);
+}
+
+void
+octopci_bs_sr_4(void *t, bus_space_handle_t bsh,
+ bus_size_t offset, uint32_t value, size_t count)
+{
+ bus_addr_t addr = bsh + offset;
+
+ for (; count != 0; count--, addr += 4)
+ wr32(addr, value);
+}
+
+/*
+ * Copy `count' 1, 2, 4, or 8 byte values from bus space starting
+ * at tag/bsh1/off1 to bus space starting at tag/bsh2/off2.
+ */
+void
+octopci_bs_c_1(void *t, bus_space_handle_t bsh1,
+ bus_size_t off1, bus_space_handle_t bsh2,
+ bus_size_t off2, size_t count)
+{
+ bus_addr_t addr1 = bsh1 + off1;
+ bus_addr_t addr2 = bsh2 + off2;
+
+ if (addr1 >= addr2) {
+ /* src after dest: copy forward */
+ for (; count != 0; count--, addr1++, addr2++)
+ wr8(addr2, rd8(addr1));
+ } else {
+ /* dest after src: copy backwards */
+ for (addr1 += (count - 1), addr2 += (count - 1);
+ count != 0; count--, addr1--, addr2--)
+ wr8(addr2, rd8(addr1));
+ }
+}
+
+void
+octopci_bs_c_2(void *t, bus_space_handle_t bsh1,
+ bus_size_t off1, bus_space_handle_t bsh2,
+ bus_size_t off2, size_t count)
+{
+ bus_addr_t addr1 = bsh1 + off1;
+ bus_addr_t addr2 = bsh2 + off2;
+
+ if (addr1 >= addr2) {
+ /* src after dest: copy forward */
+ for (; count != 0; count--, addr1 += 2, addr2 += 2)
+ wr16(addr2, rd16(addr1));
+ } else {
+ /* dest after src: copy backwards */
+ for (addr1 += 2 * (count - 1), addr2 += 2 * (count - 1);
+ count != 0; count--, addr1 -= 2, addr2 -= 2)
+ wr16(addr2, rd16(addr1));
+ }
+}
+
+void
+octopci_bs_c_4(void *t, bus_space_handle_t bsh1,
+ bus_size_t off1, bus_space_handle_t bsh2,
+ bus_size_t off2, size_t count)
+{
+ bus_addr_t addr1 = bsh1 + off1;
+ bus_addr_t addr2 = bsh2 + off2;
+
+ if (addr1 >= addr2) {
+ /* src after dest: copy forward */
+ for (; count != 0; count--, addr1 += 4, addr2 += 4)
+ wr32(addr2, rd32(addr1));
+ } else {
+ /* dest after src: copy backwards */
+ for (addr1 += 4 * (count - 1), addr2 += 4 * (count - 1);
+ count != 0; count--, addr1 -= 4, addr2 -= 4)
+ wr32(addr2, rd32(addr1));
+ }
+}
+
+void
+octopci_bs_barrier(void *t __unused,
+ bus_space_handle_t bsh __unused,
+ bus_size_t offset __unused, bus_size_t len __unused,
+ int flags)
+{
+#if 0
+ if (flags & BUS_SPACE_BARRIER_WRITE)
+ mips_dcache_wbinv_all();
+#endif
+}
diff --git a/sys/mips/cavium/octopcireg.h b/sys/mips/cavium/octopcireg.h
new file mode 100644
index 000000000000..2250309b87a0
--- /dev/null
+++ b/sys/mips/cavium/octopcireg.h
@@ -0,0 +1,105 @@
+/***********************license start************************************
+ * Copyright (c) 2005-2007 Cavium Networks (support@cavium.com). All rights
+ * reserved.
+ *
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * 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.
+ *
+ * * Neither the name of Cavium Networks nor the names of
+ * its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written
+ * permission.
+ *
+ * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+ * AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS
+ * OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH
+ * RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY
+ * REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT
+ * DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES
+ * OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR
+ * PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET
+ * POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT
+ * OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+ *
+ *
+ * For any questions regarding licensing please contact marketing@caviumnetworks.com
+ *
+ ***********************license end**************************************/
+/* $FreeBSD$ */
+
+#ifndef _CAVIUM_OCTOPCIREG_H_
+#define _CAVIUM_OCTOPCIREG_H_
+
+/**
+ * This is the bit decoding used for the Octeon PCI controller addresses for config space
+ */
+typedef union
+{
+ uint64_t u64;
+ uint64_t * u64_ptr;
+ uint32_t * u32_ptr;
+ uint16_t * u16_ptr;
+ uint8_t * u8_ptr;
+ struct
+ {
+ uint64_t upper : 2;
+ uint64_t reserved : 13;
+ uint64_t io : 1;
+ uint64_t did : 5;
+ uint64_t subdid : 3;
+ uint64_t reserved2 : 4;
+ uint64_t endian_swap : 2;
+ uint64_t reserved3 : 10;
+ uint64_t bus : 8;
+ uint64_t dev : 5;
+ uint64_t func : 3;
+ uint64_t reg : 8;
+ } s;
+} octeon_pci_config_space_address_t;
+
+typedef union
+{
+ uint64_t u64;
+ uint32_t * u32_ptr;
+ uint16_t * u16_ptr;
+ uint8_t * u8_ptr;
+ struct
+ {
+ uint64_t upper : 2;
+ uint64_t reserved : 13;
+ uint64_t io : 1;
+ uint64_t did : 5;
+ uint64_t subdid : 3;
+ uint64_t reserved2 : 4;
+ uint64_t endian_swap : 2;
+ uint64_t res1 : 1;
+ uint64_t port : 1;
+ uint64_t addr : 32;
+ } s;
+} octeon_pci_io_space_address_t;
+
+
+#define CVMX_OCT_SUBDID_PCI_CFG 1
+#define CVMX_OCT_SUBDID_PCI_IO 2
+#define CVMX_OCT_SUBDID_PCI_MEM1 3
+#define CVMX_OCT_SUBDID_PCI_MEM2 4
+#define CVMX_OCT_SUBDID_PCI_MEM3 5
+#define CVMX_OCT_SUBDID_PCI_MEM4 6
+
+#define CVMX_OCT_PCI_IO_BASE 0x00004000
+#define CVMX_OCT_PCI_IO_SIZE 0x08000000
+
+#define CVMX_OCT_PCI_MEM1_BASE 0xf0000000
+#define CVMX_OCT_PCI_MEM1_SIZE 0x0f000000
+
+#endif /* !_CAVIUM_OCTOPCIREG_H_ */
diff --git a/sys/mips/cavium/octopcivar.h b/sys/mips/cavium/octopcivar.h
new file mode 100644
index 000000000000..026e45ac9126
--- /dev/null
+++ b/sys/mips/cavium/octopcivar.h
@@ -0,0 +1,35 @@
+/*-
+ * Copyright (c) 2010 Juli Mallett <jmallett@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.
+ *
+ * $FreeBSD$
+ */
+#ifndef _MIPS_CAVIUM_OCTOPCIVAR_H
+#define _MIPS_CAVIUM_OCTOPCIVAR_H
+
+DECLARE_BUS_SPACE_PROTOTYPES(octopci);
+
+extern bus_space_tag_t octopci_bus_space;
+
+#endif /* !_MIPS_CAVIUM_OCTOPCIVAR_H */
diff --git a/sys/mips/cavium/std.octeon1 b/sys/mips/cavium/std.octeon1
new file mode 100644
index 000000000000..7b3152f361b3
--- /dev/null
+++ b/sys/mips/cavium/std.octeon1
@@ -0,0 +1,7 @@
+# /*
+# * This product includes software developed by the University of
+# * California, Berkeley and its contributors."
+# */
+# $FreeBSD$
+#
+files "../cavium/files.octeon1"
diff --git a/sys/mips/cavium/uart_bus_octeonusart.c b/sys/mips/cavium/uart_bus_octeonusart.c
new file mode 100644
index 000000000000..f6855a7c0984
--- /dev/null
+++ b/sys/mips/cavium/uart_bus_octeonusart.c
@@ -0,0 +1,122 @@
+/*-
+ * 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
+ * $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 <dev/pci/pcivar.h>
+
+#include <dev/uart/uart.h>
+#include <dev/uart/uart_bus.h>
+#include <dev/uart/uart_cpu.h>
+
+#include <mips/cavium/octeon_pcmap_regs.h>
+
+#include <contrib/octeon-sdk/cvmx.h>
+
+#include "uart_if.h"
+
+extern struct uart_class uart_oct16550_class;
+
+
+static int uart_octeon_probe(device_t dev);
+static void octeon_uart_identify(driver_t * drv, device_t parent);
+
+extern struct uart_class octeon_uart_class;
+
+static device_method_t uart_octeon_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, uart_octeon_probe),
+ DEVMETHOD(device_attach, uart_bus_attach),
+ DEVMETHOD(device_detach, uart_bus_detach),
+ DEVMETHOD(device_identify, octeon_uart_identify),
+ {0, 0}
+};
+
+static driver_t uart_octeon_driver = {
+ uart_driver_name,
+ uart_octeon_methods,
+ sizeof(struct uart_softc),
+};
+
+extern
+SLIST_HEAD(uart_devinfo_list, uart_devinfo) uart_sysdevs;
+
+static int
+uart_octeon_probe(device_t dev)
+{
+ struct uart_softc *sc;
+ int unit;
+
+ unit = device_get_unit(dev);
+ sc = device_get_softc(dev);
+ sc->sc_class = &uart_oct16550_class;
+
+ /*
+ * We inherit the settings from the systme console. Note, the bst
+ * bad bus_space_map are bogus here, but obio doesn't yet support
+ * them, it seems.
+ */
+ sc->sc_sysdev = SLIST_FIRST(&uart_sysdevs);
+ bcopy(&sc->sc_sysdev->bas, &sc->sc_bas, sizeof(sc->sc_bas));
+ sc->sc_bas.bst = uart_bus_space_mem;
+ /*
+ * XXX
+ * RBR isn't really a great base address.
+ */
+ if (bus_space_map(sc->sc_bas.bst, CVMX_MIO_UARTX_RBR(0),
+ uart_getrange(sc->sc_class), 0, &sc->sc_bas.bsh) != 0)
+ return (ENXIO);
+ return (uart_bus_probe(dev, sc->sc_bas.regshft, 0, 0, unit));
+}
+
+static void
+octeon_uart_identify(driver_t * drv, device_t parent)
+{
+ BUS_ADD_CHILD(parent, 0, "uart", 0);
+}
+
+DRIVER_MODULE(uart, obio, uart_octeon_driver, uart_devclass, 0, 0);
diff --git a/sys/mips/cavium/uart_cpu_octeonusart.c b/sys/mips/cavium/uart_cpu_octeonusart.c
new file mode 100644
index 000000000000..e5d0e4410ce8
--- /dev/null
+++ b/sys/mips/cavium/uart_cpu_octeonusart.c
@@ -0,0 +1,174 @@
+/*-
+ * Copyright (c) 2009 M. Warner Losh <imp@FreeBSD.org>
+ * 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$
+ */
+#include "opt_uart.h"
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/cons.h>
+
+#include <machine/bus.h>
+
+#include <dev/uart/uart.h>
+#include <dev/uart/uart_cpu.h>
+
+#include <mips/cavium/octeon_pcmap_regs.h>
+
+#include <contrib/octeon-sdk/cvmx.h>
+
+bus_space_tag_t uart_bus_space_io;
+bus_space_tag_t uart_bus_space_mem;
+
+/*
+ * Specailized uart bus space. We present a 1 apart byte oriented
+ * bus to the outside world, but internally translate to/from the 8-apart
+ * 64-bit word bus that's on the octeon. We only support simple read/write
+ * in this space. Everything else is undefined.
+ */
+static uint8_t
+ou_bs_r_1(void *t, bus_space_handle_t handle, bus_size_t offset)
+{
+
+ return (oct_read64(handle + offset));
+}
+
+static uint16_t
+ou_bs_r_2(void *t, bus_space_handle_t handle, bus_size_t offset)
+{
+
+ return (oct_read64(handle + offset));
+}
+
+static uint32_t
+ou_bs_r_4(void *t, bus_space_handle_t handle, bus_size_t offset)
+{
+
+ return (oct_read64(handle + offset));
+}
+
+static uint64_t
+ou_bs_r_8(void *t, bus_space_handle_t handle, bus_size_t offset)
+{
+
+ return (oct_read64(handle + offset));
+}
+
+static void
+ou_bs_w_1(void *t, bus_space_handle_t bsh, bus_size_t offset, uint8_t value)
+{
+
+ oct_write64(bsh + offset, value);
+}
+
+static void
+ou_bs_w_2(void *t, bus_space_handle_t bsh, bus_size_t offset, uint16_t value)
+{
+
+ oct_write64(bsh + offset, value);
+}
+
+static void
+ou_bs_w_4(void *t, bus_space_handle_t bsh, bus_size_t offset, uint32_t value)
+{
+
+ oct_write64(bsh + offset, value);
+}
+
+static void
+ou_bs_w_8(void *t, bus_space_handle_t bsh, bus_size_t offset, uint64_t value)
+{
+
+ oct_write64(bsh + offset, value);
+}
+
+struct bus_space octeon_uart_tag = {
+ .bs_map = generic_bs_map,
+ .bs_unmap = generic_bs_unmap,
+ .bs_subregion = generic_bs_subregion,
+ .bs_barrier = generic_bs_barrier,
+ .bs_r_1 = ou_bs_r_1,
+ .bs_r_2 = ou_bs_r_2,
+ .bs_r_4 = ou_bs_r_4,
+ .bs_r_8 = ou_bs_r_8,
+ .bs_w_1 = ou_bs_w_1,
+ .bs_w_2 = ou_bs_w_2,
+ .bs_w_4 = ou_bs_w_4,
+ .bs_w_8 = ou_bs_w_8,
+};
+
+extern struct uart_class uart_oct16550_class;
+
+int
+uart_cpu_eqres(struct uart_bas *b1, struct uart_bas *b2)
+{
+
+ return ((b1->bsh == b2->bsh && b1->bst == b2->bst) ? 1 : 0);
+}
+
+int
+uart_cpu_getdev(int devtype, struct uart_devinfo *di)
+{
+ struct uart_class *class = &uart_oct16550_class;
+
+ /*
+ * These fields need to be setup corretly for uart_getenv to
+ * work in all cases.
+ */
+ uart_bus_space_io = NULL; /* No io map for this device */
+ uart_bus_space_mem = &octeon_uart_tag;
+ di->bas.bst = uart_bus_space_mem;
+
+ /*
+ * If env specification for UART exists it takes precedence:
+ * hw.uart.console="mm:0xf1012000" or similar
+ */
+ if (uart_getenv(devtype, di, class) == 0)
+ return (0);
+
+ /*
+ * Fallback to UART0 for console.
+ */
+ di->ops = uart_getops(class);
+ di->bas.chan = 0;
+ /* XXX */
+ if (bus_space_map(di->bas.bst, CVMX_MIO_UARTX_RBR(0),
+ uart_getrange(class), 0, &di->bas.bsh) != 0)
+ return (ENXIO);
+ di->bas.regshft = 3;
+ di->bas.rclk = 0;
+ di->baudrate = 115200;
+ di->databits = 8;
+ di->stopbits = 1;
+ di->parity = UART_PARITY_NONE;
+
+ return (0);
+}
diff --git a/sys/mips/cavium/uart_dev_oct16550.c b/sys/mips/cavium/uart_dev_oct16550.c
new file mode 100644
index 000000000000..c7ad0ea51c5e
--- /dev/null
+++ b/sys/mips/cavium/uart_dev_oct16550.c
@@ -0,0 +1,836 @@
+/*-
+ * Copyright (c) 2003 Marcel Moolenaar
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * uart_dev_oct16550.c
+ *
+ * Derived from uart_dev_ns8250.c
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ */
+
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <machine/bus.h>
+#include <machine/pcpu.h>
+
+#include <dev/uart/uart.h>
+#include <dev/uart/uart_cpu.h>
+#include <dev/uart/uart_bus.h>
+
+#include <dev/ic/ns16550.h>
+
+#include <mips/cavium/octeon_pcmap_regs.h>
+
+#include <contrib/octeon-sdk/cvmx.h>
+#include <contrib/octeon-sdk/cvmx-interrupt.h>
+
+#include "uart_if.h"
+
+/*
+ * Clear pending interrupts. THRE is cleared by reading IIR. Data
+ * that may have been received gets lost here.
+ */
+static void
+oct16550_clrint (struct uart_bas *bas)
+{
+ uint8_t iir;
+
+ iir = uart_getreg(bas, REG_IIR);
+ while ((iir & IIR_NOPEND) == 0) {
+ iir &= IIR_IMASK;
+ if (iir == IIR_RLS)
+ (void)uart_getreg(bas, REG_LSR);
+ else if (iir == IIR_RXRDY || iir == IIR_RXTOUT)
+ (void)uart_getreg(bas, REG_DATA);
+ else if (iir == IIR_MLSC)
+ (void)uart_getreg(bas, REG_MSR);
+ else if (iir == IIR_BUSY)
+ (void) uart_getreg(bas, REG_USR);
+ uart_barrier(bas);
+ iir = uart_getreg(bas, REG_IIR);
+ }
+}
+
+static int delay_changed = 1;
+
+static int
+oct16550_delay (struct uart_bas *bas)
+{
+ int divisor;
+ u_char lcr;
+ static int delay = 0;
+
+ if (!delay_changed) return delay;
+ delay_changed = 0;
+ lcr = uart_getreg(bas, REG_LCR);
+ uart_setreg(bas, REG_LCR, lcr | LCR_DLAB);
+ uart_barrier(bas);
+ divisor = uart_getreg(bas, REG_DLL) | (uart_getreg(bas, REG_DLH) << 8);
+ uart_barrier(bas);
+ uart_setreg(bas, REG_LCR, lcr);
+ uart_barrier(bas);
+
+ if(!bas->rclk)
+ return 10; /* return an approx delay value */
+
+ /* 1/10th the time to transmit 1 character (estimate). */
+ if (divisor <= 134)
+ return (16000000 * divisor / bas->rclk);
+ return (16000 * divisor / (bas->rclk / 1000));
+
+}
+
+static int
+oct16550_divisor (int rclk, int baudrate)
+{
+ int actual_baud, divisor;
+ int error;
+
+ if (baudrate == 0)
+ return (0);
+
+ divisor = (rclk / (baudrate << 3) + 1) >> 1;
+ if (divisor == 0 || divisor >= 65536)
+ return (0);
+ actual_baud = rclk / (divisor << 4);
+
+ /* 10 times error in percent: */
+ error = ((actual_baud - baudrate) * 2000 / baudrate + 1) >> 1;
+
+ /* 3.0% maximum error tolerance: */
+ if (error < -30 || error > 30)
+ return (0);
+
+ return (divisor);
+}
+
+static int
+oct16550_drain (struct uart_bas *bas, int what)
+{
+ int delay, limit;
+
+ delay = oct16550_delay(bas);
+
+ if (what & UART_DRAIN_TRANSMITTER) {
+ /*
+ * Pick an arbitrary high limit to avoid getting stuck in
+ * an infinite loop when the hardware is broken. Make the
+ * limit high enough to handle large FIFOs.
+ */
+ limit = 10*10*10*1024;
+ while ((uart_getreg(bas, REG_LSR) & LSR_TEMT) == 0 && --limit)
+ DELAY(delay);
+ if (limit == 0) {
+ /* printf("oct16550: transmitter appears stuck... "); */
+ return (0);
+ }
+ }
+
+ if (what & UART_DRAIN_RECEIVER) {
+ /*
+ * Pick an arbitrary high limit to avoid getting stuck in
+ * an infinite loop when the hardware is broken. Make the
+ * limit high enough to handle large FIFOs and integrated
+ * UARTs. The HP rx2600 for example has 3 UARTs on the
+ * management board that tend to get a lot of data send
+ * to it when the UART is first activated.
+ */
+ limit=10*4096;
+ while ((uart_getreg(bas, REG_LSR) & LSR_RXRDY) && --limit) {
+ (void)uart_getreg(bas, REG_DATA);
+ uart_barrier(bas);
+ DELAY(delay << 2);
+ }
+ if (limit == 0) {
+ /* printf("oct16550: receiver appears broken... "); */
+ return (EIO);
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * We can only flush UARTs with FIFOs. UARTs without FIFOs should be
+ * drained. WARNING: this function clobbers the FIFO setting!
+ */
+static void
+oct16550_flush (struct uart_bas *bas, int what)
+{
+ uint8_t fcr;
+
+ fcr = FCR_ENABLE;
+ if (what & UART_FLUSH_TRANSMITTER)
+ fcr |= FCR_XMT_RST;
+ if (what & UART_FLUSH_RECEIVER)
+ fcr |= FCR_RCV_RST;
+ uart_setreg(bas, REG_FCR, fcr);
+ uart_barrier(bas);
+}
+
+static int
+oct16550_param (struct uart_bas *bas, int baudrate, int databits, int stopbits,
+ int parity)
+{
+ int divisor;
+ uint8_t lcr;
+
+ lcr = 0;
+ if (databits >= 8)
+ lcr |= LCR_8BITS;
+ else if (databits == 7)
+ lcr |= LCR_7BITS;
+ else if (databits == 6)
+ lcr |= LCR_6BITS;
+ else
+ lcr |= LCR_5BITS;
+ if (stopbits > 1)
+ lcr |= LCR_STOPB;
+ lcr |= parity << 3;
+
+ /* Set baudrate. */
+ if (baudrate > 0) {
+ divisor = oct16550_divisor(bas->rclk, baudrate);
+ if (divisor == 0)
+ return (EINVAL);
+ uart_setreg(bas, REG_LCR, lcr | LCR_DLAB);
+ uart_barrier(bas);
+ uart_setreg(bas, REG_DLL, divisor & 0xff);
+ uart_setreg(bas, REG_DLH, (divisor >> 8) & 0xff);
+ uart_barrier(bas);
+ delay_changed = 1;
+ }
+
+ /* Set LCR and clear DLAB. */
+ uart_setreg(bas, REG_LCR, lcr);
+ uart_barrier(bas);
+ return (0);
+}
+
+/*
+ * Low-level UART interface.
+ */
+static int oct16550_probe(struct uart_bas *bas);
+static void oct16550_init(struct uart_bas *bas, int, int, int, int);
+static void oct16550_term(struct uart_bas *bas);
+static void oct16550_putc(struct uart_bas *bas, int);
+static int oct16550_rxready(struct uart_bas *bas);
+static int oct16550_getc(struct uart_bas *bas, struct mtx *);
+
+struct uart_ops uart_oct16550_ops = {
+ .probe = oct16550_probe,
+ .init = oct16550_init,
+ .term = oct16550_term,
+ .putc = oct16550_putc,
+ .rxready = oct16550_rxready,
+ .getc = oct16550_getc,
+};
+
+static int
+oct16550_probe (struct uart_bas *bas)
+{
+ u_char val;
+
+ /* Check known 0 bits that don't depend on DLAB. */
+ val = uart_getreg(bas, REG_IIR);
+ if (val & 0x30)
+ return (ENXIO);
+ val = uart_getreg(bas, REG_MCR);
+ if (val & 0xc0)
+ return (ENXIO);
+ val = uart_getreg(bas, REG_USR);
+ if (val & 0xe0)
+ return (ENXIO);
+ return (0);
+}
+
+static void
+oct16550_init (struct uart_bas *bas, int baudrate, int databits, int stopbits,
+ int parity)
+{
+ u_char ier;
+
+ oct16550_param(bas, baudrate, databits, stopbits, parity);
+
+ /* Disable all interrupt sources. */
+ ier = uart_getreg(bas, REG_IER) & 0x0;
+ uart_setreg(bas, REG_IER, ier);
+ uart_barrier(bas);
+
+ /* Disable the FIFO (if present). */
+// uart_setreg(bas, REG_FCR, 0);
+ uart_barrier(bas);
+
+ /* Set RTS & DTR. */
+ uart_setreg(bas, REG_MCR, MCR_RTS | MCR_DTR);
+ uart_barrier(bas);
+
+ oct16550_clrint(bas);
+}
+
+static void
+oct16550_term (struct uart_bas *bas)
+{
+
+ /* Clear RTS & DTR. */
+ uart_setreg(bas, REG_MCR, 0);
+ uart_barrier(bas);
+}
+
+static inline void oct16550_wait_txhr_empty (struct uart_bas *bas, int limit, int delay)
+{
+ while (((uart_getreg(bas, REG_LSR) & LSR_THRE) == 0) &&
+ ((uart_getreg(bas, REG_USR) & USR_TXFIFO_NOTFULL) == 0))
+ DELAY(delay);
+}
+
+static void
+oct16550_putc (struct uart_bas *bas, int c)
+{
+ int delay;
+
+ /* 1/10th the time to transmit 1 character (estimate). */
+ delay = oct16550_delay(bas);
+ oct16550_wait_txhr_empty(bas, 100, delay);
+ uart_setreg(bas, REG_DATA, c);
+ uart_barrier(bas);
+ oct16550_wait_txhr_empty(bas, 100, delay);
+}
+
+static int
+oct16550_rxready (struct uart_bas *bas)
+{
+
+ return ((uart_getreg(bas, REG_LSR) & LSR_RXRDY) != 0 ? 1 : 0);
+}
+
+static int
+oct16550_getc (struct uart_bas *bas, struct mtx *hwmtx)
+{
+ int c, delay;
+
+ uart_lock(hwmtx);
+
+ /* 1/10th the time to transmit 1 character (estimate). */
+ delay = oct16550_delay(bas);
+
+ while ((uart_getreg(bas, REG_LSR) & LSR_RXRDY) == 0) {
+ uart_unlock(hwmtx);
+ DELAY(delay);
+ uart_lock(hwmtx);
+ }
+
+ c = uart_getreg(bas, REG_DATA);
+
+ uart_unlock(hwmtx);
+
+ return (c);
+}
+
+/*
+ * High-level UART interface.
+ */
+struct oct16550_softc {
+ struct uart_softc base;
+ uint8_t fcr;
+ uint8_t ier;
+ uint8_t mcr;
+};
+
+static int oct16550_bus_attach(struct uart_softc *);
+static int oct16550_bus_detach(struct uart_softc *);
+static int oct16550_bus_flush(struct uart_softc *, int);
+static int oct16550_bus_getsig(struct uart_softc *);
+static int oct16550_bus_ioctl(struct uart_softc *, int, intptr_t);
+static int oct16550_bus_ipend(struct uart_softc *);
+static int oct16550_bus_param(struct uart_softc *, int, int, int, int);
+static int oct16550_bus_probe(struct uart_softc *);
+static int oct16550_bus_receive(struct uart_softc *);
+static int oct16550_bus_setsig(struct uart_softc *, int);
+static int oct16550_bus_transmit(struct uart_softc *);
+
+static kobj_method_t oct16550_methods[] = {
+ KOBJMETHOD(uart_attach, oct16550_bus_attach),
+ KOBJMETHOD(uart_detach, oct16550_bus_detach),
+ KOBJMETHOD(uart_flush, oct16550_bus_flush),
+ KOBJMETHOD(uart_getsig, oct16550_bus_getsig),
+ KOBJMETHOD(uart_ioctl, oct16550_bus_ioctl),
+ KOBJMETHOD(uart_ipend, oct16550_bus_ipend),
+ KOBJMETHOD(uart_param, oct16550_bus_param),
+ KOBJMETHOD(uart_probe, oct16550_bus_probe),
+ KOBJMETHOD(uart_receive, oct16550_bus_receive),
+ KOBJMETHOD(uart_setsig, oct16550_bus_setsig),
+ KOBJMETHOD(uart_transmit, oct16550_bus_transmit),
+ { 0, 0 }
+};
+
+struct uart_class uart_oct16550_class = {
+ "oct16550 class",
+ oct16550_methods,
+ sizeof(struct oct16550_softc),
+ .uc_ops = &uart_oct16550_ops,
+ .uc_range = 8 << 3,
+ .uc_rclk = 0
+};
+
+#define SIGCHG(c, i, s, d) \
+ if (c) { \
+ i |= (i & s) ? s : s | d; \
+ } else { \
+ i = (i & s) ? (i & ~s) | d : i; \
+ }
+
+static int
+oct16550_bus_attach (struct uart_softc *sc)
+{
+ struct oct16550_softc *oct16550 = (struct oct16550_softc*)sc;
+ struct uart_bas *bas;
+ int unit;
+
+ unit = device_get_unit(sc->sc_dev);
+ bas = &sc->sc_bas;
+
+ oct16550_drain(bas, UART_DRAIN_TRANSMITTER);
+ oct16550->mcr = uart_getreg(bas, REG_MCR);
+ oct16550->fcr = FCR_ENABLE | FCR_RX_HIGH;
+ uart_setreg(bas, REG_FCR, oct16550->fcr);
+ uart_barrier(bas);
+ oct16550_bus_flush(sc, UART_FLUSH_RECEIVER|UART_FLUSH_TRANSMITTER);
+
+ if (oct16550->mcr & MCR_DTR)
+ sc->sc_hwsig |= SER_DTR;
+ if (oct16550->mcr & MCR_RTS)
+ sc->sc_hwsig |= SER_RTS;
+ oct16550_bus_getsig(sc);
+
+ oct16550_clrint(bas);
+ oct16550->ier = uart_getreg(bas, REG_IER) & 0xf0;
+ oct16550->ier |= IER_EMSC | IER_ERLS | IER_ERXRDY;
+ uart_setreg(bas, REG_IER, oct16550->ier);
+ uart_barrier(bas);
+
+ /*
+ * Enable the interrupt in CIU. // UART-x2 @ IP2
+ */
+ switch (unit) {
+ case 0:
+ cvmx_interrupt_unmask_irq(CVMX_IRQ_UART0);
+ break;
+ case 1:
+ cvmx_interrupt_unmask_irq(CVMX_IRQ_UART1);
+ break;
+ default:
+ panic("%s: invalid UART %d", __func__, unit);
+ }
+ return (0);
+}
+
+static int
+oct16550_bus_detach (struct uart_softc *sc)
+{
+ struct uart_bas *bas;
+ u_char ier;
+
+ bas = &sc->sc_bas;
+ ier = uart_getreg(bas, REG_IER) & 0xf0;
+ uart_setreg(bas, REG_IER, ier);
+ uart_barrier(bas);
+ oct16550_clrint(bas);
+ return (0);
+}
+
+static int
+oct16550_bus_flush (struct uart_softc *sc, int what)
+{
+ struct oct16550_softc *oct16550 = (struct oct16550_softc*)sc;
+ struct uart_bas *bas;
+ int error;
+
+ bas = &sc->sc_bas;
+ uart_lock(sc->sc_hwmtx);
+ if (sc->sc_rxfifosz > 1) {
+ oct16550_flush(bas, what);
+ uart_setreg(bas, REG_FCR, oct16550->fcr);
+ uart_barrier(bas);
+ error = 0;
+ } else
+ error = oct16550_drain(bas, what);
+ uart_unlock(sc->sc_hwmtx);
+ return (error);
+}
+
+static int
+oct16550_bus_getsig (struct uart_softc *sc)
+{
+ uint32_t new, old, sig;
+ uint8_t msr;
+
+ do {
+ old = sc->sc_hwsig;
+ sig = old;
+ uart_lock(sc->sc_hwmtx);
+ msr = uart_getreg(&sc->sc_bas, REG_MSR);
+ uart_unlock(sc->sc_hwmtx);
+ SIGCHG(msr & MSR_DSR, sig, SER_DSR, SER_DDSR);
+ SIGCHG(msr & MSR_CTS, sig, SER_CTS, SER_DCTS);
+ SIGCHG(msr & MSR_DCD, sig, SER_DCD, SER_DDCD);
+ SIGCHG(msr & MSR_RI, sig, SER_RI, SER_DRI);
+ new = sig & ~SER_MASK_DELTA;
+ } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new));
+ return (sig);
+}
+
+static int
+oct16550_bus_ioctl (struct uart_softc *sc, int request, intptr_t data)
+{
+ struct uart_bas *bas;
+ int baudrate, divisor, error;
+ uint8_t efr, lcr;
+
+ bas = &sc->sc_bas;
+ error = 0;
+ uart_lock(sc->sc_hwmtx);
+ switch (request) {
+ case UART_IOCTL_BREAK:
+ lcr = uart_getreg(bas, REG_LCR);
+ if (data)
+ lcr |= LCR_SBREAK;
+ else
+ lcr &= ~LCR_SBREAK;
+ uart_setreg(bas, REG_LCR, lcr);
+ uart_barrier(bas);
+ break;
+ case UART_IOCTL_IFLOW:
+ lcr = uart_getreg(bas, REG_LCR);
+ uart_barrier(bas);
+ uart_setreg(bas, REG_LCR, 0xbf);
+ uart_barrier(bas);
+ efr = uart_getreg(bas, REG_EFR);
+ if (data)
+ efr |= EFR_RTS;
+ else
+ efr &= ~EFR_RTS;
+ uart_setreg(bas, REG_EFR, efr);
+ uart_barrier(bas);
+ uart_setreg(bas, REG_LCR, lcr);
+ uart_barrier(bas);
+ break;
+ case UART_IOCTL_OFLOW:
+ lcr = uart_getreg(bas, REG_LCR);
+ uart_barrier(bas);
+ uart_setreg(bas, REG_LCR, 0xbf);
+ uart_barrier(bas);
+ efr = uart_getreg(bas, REG_EFR);
+ if (data)
+ efr |= EFR_CTS;
+ else
+ efr &= ~EFR_CTS;
+ uart_setreg(bas, REG_EFR, efr);
+ uart_barrier(bas);
+ uart_setreg(bas, REG_LCR, lcr);
+ uart_barrier(bas);
+ break;
+ case UART_IOCTL_BAUD:
+ lcr = uart_getreg(bas, REG_LCR);
+ uart_setreg(bas, REG_LCR, lcr | LCR_DLAB);
+ uart_barrier(bas);
+ divisor = uart_getreg(bas, REG_DLL) |
+ (uart_getreg(bas, REG_DLH) << 8);
+ uart_barrier(bas);
+ uart_setreg(bas, REG_LCR, lcr);
+ uart_barrier(bas);
+ baudrate = (divisor > 0) ? bas->rclk / divisor / 16 : 0;
+ delay_changed = 1;
+ if (baudrate > 0)
+ *(int*)data = baudrate;
+ else
+ error = ENXIO;
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+ uart_unlock(sc->sc_hwmtx);
+ return (error);
+}
+
+
+static int
+oct16550_bus_ipend(struct uart_softc *sc)
+{
+ struct uart_bas *bas;
+ int ipend = 0;
+ uint8_t iir, lsr;
+
+ bas = &sc->sc_bas;
+ uart_lock(sc->sc_hwmtx);
+
+ iir = uart_getreg(bas, REG_IIR) & IIR_IMASK;
+ if (iir != IIR_NOPEND) {
+
+ if (iir == IIR_RLS) {
+ lsr = uart_getreg(bas, REG_LSR);
+ if (lsr & LSR_OE)
+ ipend |= SER_INT_OVERRUN;
+ if (lsr & LSR_BI)
+ ipend |= SER_INT_BREAK;
+ if (lsr & LSR_RXRDY)
+ ipend |= SER_INT_RXREADY;
+
+ } else if (iir == IIR_RXRDY) {
+ ipend |= SER_INT_RXREADY;
+
+ } else if (iir == IIR_RXTOUT) {
+ ipend |= SER_INT_RXREADY;
+
+ } else if (iir == IIR_TXRDY) {
+ ipend |= SER_INT_TXIDLE;
+
+ } else if (iir == IIR_MLSC) {
+ ipend |= SER_INT_SIGCHG;
+
+ } else if (iir == IIR_BUSY) {
+ (void) uart_getreg(bas, REG_USR);
+ }
+ }
+ uart_unlock(sc->sc_hwmtx);
+
+//#define OCTEON_VISUAL_UART 1
+#ifdef OCTEON_VISUAL_UART
+ static int where1 = 0;
+
+ if (ipend) octeon_led_run_wheel(&where1, 6 + device_get_unit(sc->sc_dev));
+#endif
+
+ return ((sc->sc_leaving) ? 0 : ipend);
+}
+
+
+
+
+static int
+oct16550_bus_param (struct uart_softc *sc, int baudrate, int databits,
+ int stopbits, int parity)
+{
+ struct uart_bas *bas;
+ int error;
+
+ bas = &sc->sc_bas;
+ uart_lock(sc->sc_hwmtx);
+ error = oct16550_param(bas, baudrate, databits, stopbits, parity);
+ uart_unlock(sc->sc_hwmtx);
+ return (error);
+}
+
+static int
+oct16550_bus_probe (struct uart_softc *sc)
+{
+ struct uart_bas *bas;
+ int error;
+
+ bas = &sc->sc_bas;
+ bas->rclk = uart_oct16550_class.uc_rclk = cvmx_sysinfo_get()->cpu_clock_hz;
+
+ error = oct16550_probe(bas);
+ if (error) {
+ return (error);
+ }
+
+ uart_setreg(bas, REG_MCR, (MCR_DTR | MCR_RTS));
+
+ /*
+ * Enable FIFOs. And check that the UART has them. If not, we're
+ * done. Since this is the first time we enable the FIFOs, we reset
+ * them.
+ */
+ oct16550_drain(bas, UART_DRAIN_TRANSMITTER);
+#define ENABLE_OCTEON_FIFO 1
+#ifdef ENABLE_OCTEON_FIFO
+ uart_setreg(bas, REG_FCR, FCR_ENABLE | FCR_XMT_RST | FCR_RCV_RST);
+#endif
+ uart_barrier(bas);
+
+ oct16550_flush(bas, UART_FLUSH_RECEIVER|UART_FLUSH_TRANSMITTER);
+
+ if (device_get_unit(sc->sc_dev)) {
+ device_set_desc(sc->sc_dev, "Octeon-16550 channel 1");
+ } else {
+ device_set_desc(sc->sc_dev, "Octeon-16550 channel 0");
+ }
+#ifdef ENABLE_OCTEON_FIFO
+ sc->sc_rxfifosz = 64;
+ sc->sc_txfifosz = 64;
+#else
+ sc->sc_rxfifosz = 1;
+ sc->sc_txfifosz = 1;
+#endif
+
+
+#if 0
+ /*
+ * XXX there are some issues related to hardware flow control and
+ * it's likely that uart(4) is the cause. This basicly needs more
+ * investigation, but we avoid using for hardware flow control
+ * until then.
+ */
+ /* 16650s or higher have automatic flow control. */
+ if (sc->sc_rxfifosz > 16) {
+ sc->sc_hwiflow = 1;
+ sc->sc_hwoflow = 1;
+ }
+#endif
+
+ return (0);
+}
+
+static int
+oct16550_bus_receive (struct uart_softc *sc)
+{
+ struct uart_bas *bas;
+ int xc;
+ uint8_t lsr;
+
+ bas = &sc->sc_bas;
+ uart_lock(sc->sc_hwmtx);
+ lsr = uart_getreg(bas, REG_LSR);
+
+ while (lsr & LSR_RXRDY) {
+ if (uart_rx_full(sc)) {
+ sc->sc_rxbuf[sc->sc_rxput] = UART_STAT_OVERRUN;
+ break;
+ }
+ xc = uart_getreg(bas, REG_DATA);
+ if (lsr & LSR_FE)
+ xc |= UART_STAT_FRAMERR;
+ if (lsr & LSR_PE)
+ xc |= UART_STAT_PARERR;
+ uart_rx_put(sc, xc);
+ lsr = uart_getreg(bas, REG_LSR);
+ }
+ /* Discard everything left in the Rx FIFO. */
+ /*
+ * First do a dummy read/discard anyway, in case the UART was lying to us.
+ * This problem was seen on board, when IIR said RBR, but LSR said no RXRDY
+ * Results in a stuck ipend loop.
+ */
+ (void)uart_getreg(bas, REG_DATA);
+ while (lsr & LSR_RXRDY) {
+ (void)uart_getreg(bas, REG_DATA);
+ uart_barrier(bas);
+ lsr = uart_getreg(bas, REG_LSR);
+ }
+ uart_unlock(sc->sc_hwmtx);
+ return (0);
+}
+
+static int
+oct16550_bus_setsig (struct uart_softc *sc, int sig)
+{
+ struct oct16550_softc *oct16550 = (struct oct16550_softc*)sc;
+ struct uart_bas *bas;
+ uint32_t new, old;
+
+ bas = &sc->sc_bas;
+ do {
+ old = sc->sc_hwsig;
+ new = old;
+ if (sig & SER_DDTR) {
+ SIGCHG(sig & SER_DTR, new, SER_DTR,
+ SER_DDTR);
+ }
+ if (sig & SER_DRTS) {
+ SIGCHG(sig & SER_RTS, new, SER_RTS,
+ SER_DRTS);
+ }
+ } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new));
+ uart_lock(sc->sc_hwmtx);
+ oct16550->mcr &= ~(MCR_DTR|MCR_RTS);
+ if (new & SER_DTR)
+ oct16550->mcr |= MCR_DTR;
+ if (new & SER_RTS)
+ oct16550->mcr |= MCR_RTS;
+ uart_setreg(bas, REG_MCR, oct16550->mcr);
+ uart_barrier(bas);
+ uart_unlock(sc->sc_hwmtx);
+ return (0);
+}
+
+static int
+oct16550_bus_transmit (struct uart_softc *sc)
+{
+ struct oct16550_softc *oct16550 = (struct oct16550_softc*)sc;
+ struct uart_bas *bas;
+ int i;
+
+ bas = &sc->sc_bas;
+ uart_lock(sc->sc_hwmtx);
+#ifdef NO_UART_INTERRUPTS
+ for (i = 0; i < sc->sc_txdatasz; i++) {
+ oct16550_putc(bas, sc->sc_txbuf[i]);
+ }
+#else
+
+ oct16550_wait_txhr_empty(bas, 100, oct16550_delay(bas));
+ uart_setreg(bas, REG_IER, oct16550->ier | IER_ETXRDY);
+ uart_barrier(bas);
+
+ for (i = 0; i < sc->sc_txdatasz; i++) {
+ uart_setreg(bas, REG_DATA, sc->sc_txbuf[i]);
+ uart_barrier(bas);
+ }
+ sc->sc_txbusy = 1;
+#endif
+ uart_unlock(sc->sc_hwmtx);
+ return (0);
+}
diff --git a/sys/mips/cavium/usb/octusb.c b/sys/mips/cavium/usb/octusb.c
new file mode 100644
index 000000000000..598a304d8f33
--- /dev/null
+++ b/sys/mips/cavium/usb/octusb.c
@@ -0,0 +1,1922 @@
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*-
+ * Copyright (c) 2010 Hans Petter Selasky. 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.
+ */
+
+/*
+ * This file contains the driver for Octeon Executive Library USB
+ * Controller driver API.
+ */
+
+/* TODO: The root HUB port callback is not yet implemented. */
+
+#include <sys/stdint.h>
+#include <sys/stddef.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/linker_set.h>
+#include <sys/module.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+
+#define USB_DEBUG_VAR octusbdebug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_transfer.h>
+#include <dev/usb/usb_device.h>
+#include <dev/usb/usb_hub.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+
+#include <contrib/octeon-sdk/cvmx.h>
+#include <contrib/octeon-sdk/cvmx-usb.h>
+
+#include <mips/cavium/usb/octusb.h>
+
+#define OCTUSB_BUS2SC(bus) \
+ ((struct octusb_softc *)(((uint8_t *)(bus)) - \
+ ((uint8_t *)&(((struct octusb_softc *)0)->sc_bus))))
+
+#ifdef USB_DEBUG
+static int octusbdebug = 0;
+
+SYSCTL_NODE(_hw_usb, OID_AUTO, octusb, CTLFLAG_RW, 0, "OCTUSB");
+SYSCTL_INT(_hw_usb_octusb, OID_AUTO, debug, CTLFLAG_RW,
+ &octusbdebug, 0, "OCTUSB debug level");
+
+TUNABLE_INT("hw.usb.octusb.debug", &octusbdebug);
+
+#endif
+
+struct octusb_std_temp {
+ octusb_cmd_t *func;
+ struct octusb_td *td;
+ struct octusb_td *td_next;
+ struct usb_page_cache *pc;
+ uint32_t offset;
+ uint32_t len;
+ uint8_t short_pkt;
+ uint8_t setup_alt_next;
+};
+
+extern struct usb_bus_methods octusb_bus_methods;
+extern struct usb_pipe_methods octusb_device_bulk_methods;
+extern struct usb_pipe_methods octusb_device_ctrl_methods;
+extern struct usb_pipe_methods octusb_device_intr_methods;
+extern struct usb_pipe_methods octusb_device_isoc_methods;
+
+static void octusb_standard_done(struct usb_xfer *);
+static void octusb_device_done(struct usb_xfer *, usb_error_t);
+static void octusb_timeout(void *);
+static void octusb_do_poll(struct usb_bus *);
+
+static cvmx_usb_speed_t
+octusb_convert_speed(enum usb_dev_speed speed)
+{
+ ; /* indent fix */
+ switch (speed) {
+ case USB_SPEED_HIGH:
+ return (CVMX_USB_SPEED_HIGH);
+ case USB_SPEED_FULL:
+ return (CVMX_USB_SPEED_FULL);
+ default:
+ return (CVMX_USB_SPEED_LOW);
+ }
+}
+
+static cvmx_usb_transfer_t
+octusb_convert_ep_type(uint8_t ep_type)
+{
+ ; /* indent fix */
+ switch (ep_type & UE_XFERTYPE) {
+ case UE_CONTROL:
+ return (CVMX_USB_TRANSFER_CONTROL);
+ case UE_INTERRUPT:
+ return (CVMX_USB_TRANSFER_INTERRUPT);
+ case UE_ISOCHRONOUS:
+ return (CVMX_USB_TRANSFER_ISOCHRONOUS);
+ case UE_BULK:
+ return (CVMX_USB_TRANSFER_BULK);
+ default:
+ return (0); /* should not happen */
+ }
+}
+
+static uint8_t
+octusb_host_alloc_endpoint(struct octusb_td *td)
+{
+ struct octusb_softc *sc;
+ int ep_handle;
+
+ if (td->qh->fixup_pending)
+ return (1); /* busy */
+
+ if (td->qh->ep_allocated)
+ return (0); /* success */
+
+ /* get softc */
+ sc = td->qh->sc;
+
+ ep_handle = cvmx_usb_open_pipe(
+ &sc->sc_port[td->qh->port_index].state,
+ 0,
+ td->qh->dev_addr,
+ td->qh->ep_num,
+ octusb_convert_speed(td->qh->dev_speed),
+ td->qh->max_packet_size,
+ octusb_convert_ep_type(td->qh->ep_type),
+ (td->qh->ep_num & UE_DIR_IN) ? CVMX_USB_DIRECTION_IN :
+ CVMX_USB_DIRECTION_OUT,
+ td->qh->ep_interval,
+ td->qh->ep_mult,
+ td->qh->hs_hub_addr,
+ td->qh->hs_hub_port);
+
+ if (ep_handle < 0)
+ return (1); /* busy */
+
+ cvmx_usb_set_toggle(
+ &sc->sc_port[td->qh->port_index].state,
+ ep_handle, td->qh->ep_toggle_next);
+
+ td->qh->fixup_handle = -1;
+ td->qh->fixup_complete = 0;
+ td->qh->fixup_len = 0;
+ td->qh->fixup_off = 0;
+ td->qh->fixup_pending = 0;
+ td->qh->fixup_actlen = 0;
+
+ td->qh->ep_handle = ep_handle;
+ td->qh->ep_allocated = 1;
+
+ return (0); /* success */
+}
+
+static void
+octusb_host_free_endpoint(struct octusb_td *td)
+{
+ struct octusb_softc *sc;
+
+ if (td->qh->ep_allocated == 0)
+ return;
+
+ /* get softc */
+ sc = td->qh->sc;
+
+ if (td->qh->fixup_handle >= 0) {
+ /* cancel, if any */
+ cvmx_usb_cancel(&sc->sc_port[td->qh->port_index].state,
+ td->qh->ep_handle, td->qh->fixup_handle);
+ }
+ cvmx_usb_close_pipe(&sc->sc_port[td->qh->port_index].state, td->qh->ep_handle);
+
+ td->qh->ep_allocated = 0;
+}
+
+static void
+octusb_complete_cb(cvmx_usb_state_t *state,
+ cvmx_usb_callback_t reason,
+ cvmx_usb_complete_t status,
+ int pipe_handle, int submit_handle,
+ int bytes_transferred, void *user_data)
+{
+ struct octusb_td *td;
+
+ if (reason != CVMX_USB_CALLBACK_TRANSFER_COMPLETE)
+ return;
+
+ td = user_data;
+
+ td->qh->fixup_complete = 1;
+ td->qh->fixup_pending = 0;
+ td->qh->fixup_actlen = bytes_transferred;
+ td->qh->fixup_handle = -1;
+
+ switch (status) {
+ case CVMX_USB_COMPLETE_SUCCESS:
+ case CVMX_USB_COMPLETE_SHORT:
+ td->error_any = 0;
+ td->error_stall = 0;
+ break;
+ case CVMX_USB_COMPLETE_STALL:
+ td->error_stall = 1;
+ td->error_any = 1;
+ break;
+ default:
+ td->error_any = 1;
+ break;
+ }
+}
+
+static uint8_t
+octusb_host_control_header_tx(struct octusb_td *td)
+{
+ int status;
+
+ /* allocate endpoint and check pending */
+ if (octusb_host_alloc_endpoint(td))
+ return (1); /* busy */
+
+ /* check error */
+ if (td->error_any)
+ return (0); /* done */
+
+ if (td->qh->fixup_complete != 0) {
+ /* clear complete flag */
+ td->qh->fixup_complete = 0;
+
+ /* flush data */
+ usb_pc_cpu_invalidate(td->qh->fixup_pc);
+ return (0); /* done */
+ }
+ /* verify length */
+ if (td->remainder != 8) {
+ td->error_any = 1;
+ return (0); /* done */
+ }
+ usbd_copy_out(td->pc, td->offset, td->qh->fixup_buf, 8);
+
+ /* update offset and remainder */
+ td->offset += 8;
+ td->remainder -= 8;
+
+ /* setup data length and offset */
+ td->qh->fixup_len = UGETW(td->qh->fixup_buf + 6);
+ td->qh->fixup_off = 0;
+
+ if (td->qh->fixup_len > (OCTUSB_MAX_FIXUP - 8)) {
+ td->error_any = 1;
+ return (0); /* done */
+ }
+ /* do control IN request */
+ if (td->qh->fixup_buf[0] & UE_DIR_IN) {
+
+ struct octusb_softc *sc;
+
+ /* get softc */
+ sc = td->qh->sc;
+
+ /* flush data */
+ usb_pc_cpu_flush(td->qh->fixup_pc);
+
+ status = cvmx_usb_submit_control(
+ &sc->sc_port[td->qh->port_index].state,
+ td->qh->ep_handle, td->qh->fixup_phys,
+ td->qh->fixup_phys + 8, td->qh->fixup_len,
+ &octusb_complete_cb, td);
+ /* check status */
+ if (status < 0) {
+ td->error_any = 1;
+ return (0); /* done */
+ }
+ td->qh->fixup_handle = status;
+ td->qh->fixup_pending = 1;
+ td->qh->fixup_complete = 0;
+
+ return (1); /* busy */
+ }
+ return (0); /* done */
+}
+
+static uint8_t
+octusb_host_control_data_tx(struct octusb_td *td)
+{
+ uint32_t rem;
+
+ /* allocate endpoint and check pending */
+ if (octusb_host_alloc_endpoint(td))
+ return (1); /* busy */
+
+ /* check error */
+ if (td->error_any)
+ return (0); /* done */
+
+ rem = td->qh->fixup_len - td->qh->fixup_off;
+
+ if (td->remainder > rem) {
+ td->error_any = 1;
+ DPRINTFN(1, "Excess setup transmit data\n");
+ return (0); /* done */
+ }
+ usbd_copy_out(td->pc, td->offset, td->qh->fixup_buf +
+ td->qh->fixup_off + 8, td->remainder);
+
+ td->offset += td->remainder;
+ td->qh->fixup_off += td->remainder;
+ td->remainder = 0;
+
+ return (0); /* done */
+}
+
+static uint8_t
+octusb_host_control_data_rx(struct octusb_td *td)
+{
+ uint32_t rem;
+
+ /* allocate endpoint and check pending */
+ if (octusb_host_alloc_endpoint(td))
+ return (1); /* busy */
+
+ /* check error */
+ if (td->error_any)
+ return (0); /* done */
+
+ /* copy data from buffer */
+ rem = td->qh->fixup_actlen - td->qh->fixup_off;
+
+ if (rem > td->remainder)
+ rem = td->remainder;
+
+ usbd_copy_in(td->pc, td->offset, td->qh->fixup_buf +
+ td->qh->fixup_off + 8, rem);
+
+ td->offset += rem;
+ td->remainder -= rem;
+ td->qh->fixup_off += rem;
+
+ return (0); /* done */
+}
+
+static uint8_t
+octusb_host_control_status_tx(struct octusb_td *td)
+{
+ int status;
+
+ /* allocate endpoint and check pending */
+ if (octusb_host_alloc_endpoint(td))
+ return (1); /* busy */
+
+ /* check error */
+ if (td->error_any)
+ return (0); /* done */
+
+ if (td->qh->fixup_complete != 0) {
+ /* clear complete flag */
+ td->qh->fixup_complete = 0;
+ /* done */
+ return (0);
+ }
+ /* do control IN request */
+ if (!(td->qh->fixup_buf[0] & UE_DIR_IN)) {
+
+ struct octusb_softc *sc;
+
+ /* get softc */
+ sc = td->qh->sc;
+
+ /* flush data */
+ usb_pc_cpu_flush(td->qh->fixup_pc);
+
+ /* start USB transfer */
+ status = cvmx_usb_submit_control(
+ &sc->sc_port[td->qh->port_index].state,
+ td->qh->ep_handle, td->qh->fixup_phys,
+ td->qh->fixup_phys + 8, td->qh->fixup_len,
+ &octusb_complete_cb, td);
+
+ /* check status */
+ if (status < 0) {
+ td->error_any = 1;
+ return (0); /* done */
+ }
+ td->qh->fixup_handle = status;
+ td->qh->fixup_pending = 1;
+ td->qh->fixup_complete = 0;
+
+ return (1); /* busy */
+ }
+ return (0); /* done */
+}
+
+static uint8_t
+octusb_non_control_data_tx(struct octusb_td *td)
+{
+ struct octusb_softc *sc;
+ uint32_t rem;
+ int status;
+
+ /* allocate endpoint and check pending */
+ if (octusb_host_alloc_endpoint(td))
+ return (1); /* busy */
+
+ /* check error */
+ if (td->error_any)
+ return (0); /* done */
+
+ if ((td->qh->fixup_complete != 0) &&
+ ((td->qh->ep_type & UE_XFERTYPE) == UE_ISOCHRONOUS)) {
+ td->qh->fixup_complete = 0;
+ return (0); /* done */
+ }
+ /* check complete */
+ if (td->remainder == 0) {
+ if (td->short_pkt)
+ return (0); /* complete */
+ /* else need to send a zero length packet */
+ rem = 0;
+ td->short_pkt = 1;
+ } else {
+ /* get maximum length */
+ rem = OCTUSB_MAX_FIXUP % td->qh->max_frame_size;
+ rem = OCTUSB_MAX_FIXUP - rem;
+
+ if (rem == 0) {
+ /* should not happen */
+ DPRINTFN(1, "Fixup buffer is too small\n");
+ td->error_any = 1;
+ return (0); /* done */
+ }
+ /* get minimum length */
+ if (rem > td->remainder) {
+ rem = td->remainder;
+ if ((rem == 0) || (rem % td->qh->max_frame_size))
+ td->short_pkt = 1;
+ }
+ /* copy data into fixup buffer */
+ usbd_copy_out(td->pc, td->offset, td->qh->fixup_buf, rem);
+
+ /* flush data */
+ usb_pc_cpu_flush(td->qh->fixup_pc);
+
+ /* pre-increment TX buffer offset */
+ td->offset += rem;
+ td->remainder -= rem;
+ }
+
+ /* get softc */
+ sc = td->qh->sc;
+
+ switch (td->qh->ep_type & UE_XFERTYPE) {
+ case UE_ISOCHRONOUS:
+ td->qh->iso_pkt.offset = 0;
+ td->qh->iso_pkt.length = rem;
+ td->qh->iso_pkt.status = 0;
+ /* start USB transfer */
+ status = cvmx_usb_submit_isochronous(&sc->sc_port[td->qh->port_index].state,
+ td->qh->ep_handle, 1, CVMX_USB_ISOCHRONOUS_FLAGS_ALLOW_SHORT |
+ CVMX_USB_ISOCHRONOUS_FLAGS_ASAP, 1, &td->qh->iso_pkt,
+ td->qh->fixup_phys, rem, &octusb_complete_cb, td);
+ break;
+ case UE_BULK:
+ /* start USB transfer */
+ status = cvmx_usb_submit_bulk(&sc->sc_port[td->qh->port_index].state,
+ td->qh->ep_handle, td->qh->fixup_phys, rem, &octusb_complete_cb, td);
+ break;
+ case UE_INTERRUPT:
+ /* start USB transfer (interrupt or interrupt) */
+ status = cvmx_usb_submit_interrupt(&sc->sc_port[td->qh->port_index].state,
+ td->qh->ep_handle, td->qh->fixup_phys, rem, &octusb_complete_cb, td);
+ break;
+ default:
+ status = -1;
+ break;
+ }
+
+ /* check status */
+ if (status < 0) {
+ td->error_any = 1;
+ return (0); /* done */
+ }
+ td->qh->fixup_handle = status;
+ td->qh->fixup_len = rem;
+ td->qh->fixup_pending = 1;
+ td->qh->fixup_complete = 0;
+
+ return (1); /* busy */
+}
+
+static uint8_t
+octusb_non_control_data_rx(struct octusb_td *td)
+{
+ struct octusb_softc *sc;
+ uint32_t rem;
+ int status;
+ uint8_t got_short;
+
+ /* allocate endpoint and check pending */
+ if (octusb_host_alloc_endpoint(td))
+ return (1); /* busy */
+
+ /* check error */
+ if (td->error_any)
+ return (0); /* done */
+
+ got_short = 0;
+
+ if (td->qh->fixup_complete != 0) {
+
+ /* invalidate data */
+ usb_pc_cpu_invalidate(td->qh->fixup_pc);
+
+ rem = td->qh->fixup_actlen;
+
+ /* verify transfer length */
+ if (rem != td->qh->fixup_len) {
+ if (rem < td->qh->fixup_len) {
+ /* we have a short packet */
+ td->short_pkt = 1;
+ got_short = 1;
+ } else {
+ /* invalid USB packet */
+ td->error_any = 1;
+ return (0); /* we are complete */
+ }
+ }
+ /* copy data into fixup buffer */
+ usbd_copy_in(td->pc, td->offset, td->qh->fixup_buf, rem);
+
+ /* post-increment RX buffer offset */
+ td->offset += rem;
+ td->remainder -= rem;
+
+ td->qh->fixup_complete = 0;
+
+ if ((td->qh->ep_type & UE_XFERTYPE) == UE_ISOCHRONOUS)
+ return (0); /* done */
+ }
+ /* check if we are complete */
+ if ((td->remainder == 0) || got_short) {
+ if (td->short_pkt) {
+ /* we are complete */
+ return (0);
+ }
+ /* else need to receive a zero length packet */
+ rem = 0;
+ td->short_pkt = 1;
+ } else {
+ /* get maximum length */
+ rem = OCTUSB_MAX_FIXUP % td->qh->max_frame_size;
+ rem = OCTUSB_MAX_FIXUP - rem;
+
+ if (rem == 0) {
+ /* should not happen */
+ DPRINTFN(1, "Fixup buffer is too small\n");
+ td->error_any = 1;
+ return (0); /* done */
+ }
+ /* get minimum length */
+ if (rem > td->remainder)
+ rem = td->remainder;
+ }
+
+ /* invalidate data */
+ usb_pc_cpu_invalidate(td->qh->fixup_pc);
+
+ /* get softc */
+ sc = td->qh->sc;
+
+ switch (td->qh->ep_type & UE_XFERTYPE) {
+ case UE_ISOCHRONOUS:
+ td->qh->iso_pkt.offset = 0;
+ td->qh->iso_pkt.length = rem;
+ td->qh->iso_pkt.status = 0;
+ /* start USB transfer */
+ status = cvmx_usb_submit_isochronous(&sc->sc_port[td->qh->port_index].state,
+ td->qh->ep_handle, 1, CVMX_USB_ISOCHRONOUS_FLAGS_ALLOW_SHORT |
+ CVMX_USB_ISOCHRONOUS_FLAGS_ASAP, 1, &td->qh->iso_pkt,
+ td->qh->fixup_phys, rem, &octusb_complete_cb, td);
+ break;
+ case UE_BULK:
+ /* start USB transfer */
+ status = cvmx_usb_submit_bulk(&sc->sc_port[td->qh->port_index].state,
+ td->qh->ep_handle, td->qh->fixup_phys, rem, &octusb_complete_cb, td);
+ break;
+ case UE_INTERRUPT:
+ /* start USB transfer */
+ status = cvmx_usb_submit_interrupt(&sc->sc_port[td->qh->port_index].state,
+ td->qh->ep_handle, td->qh->fixup_phys, rem, &octusb_complete_cb, td);
+ break;
+ default:
+ status = -1;
+ break;
+ }
+
+ /* check status */
+ if (status < 0) {
+ td->error_any = 1;
+ return (0); /* done */
+ }
+ td->qh->fixup_handle = status;
+ td->qh->fixup_len = rem;
+ td->qh->fixup_pending = 1;
+ td->qh->fixup_complete = 0;
+
+ return (1); /* busy */
+}
+
+static uint8_t
+octusb_xfer_do_fifo(struct usb_xfer *xfer)
+{
+ struct octusb_td *td;
+
+ DPRINTFN(8, "\n");
+
+ td = xfer->td_transfer_cache;
+
+ while (1) {
+ if ((td->func) (td)) {
+ /* operation in progress */
+ break;
+ }
+ if (((void *)td) == xfer->td_transfer_last) {
+ goto done;
+ }
+ if (td->error_any) {
+ goto done;
+ } else if (td->remainder > 0) {
+ /*
+ * We had a short transfer. If there is no
+ * alternate next, stop processing !
+ */
+ if (td->alt_next == 0)
+ goto done;
+ }
+ /*
+ * Fetch the next transfer descriptor and transfer
+ * some flags to the next transfer descriptor
+ */
+ td = td->obj_next;
+ xfer->td_transfer_cache = td;
+ }
+ return (1); /* not complete */
+
+done:
+ /* compute all actual lengths */
+
+ octusb_standard_done(xfer);
+
+ return (0); /* complete */
+}
+
+static usb_error_t
+octusb_standard_done_sub(struct usb_xfer *xfer)
+{
+ struct octusb_td *td;
+ uint32_t len;
+ usb_error_t error;
+
+ DPRINTFN(8, "\n");
+
+ td = xfer->td_transfer_cache;
+
+ do {
+ len = td->remainder;
+
+ if (xfer->aframes != xfer->nframes) {
+ /*
+ * Verify the length and subtract
+ * the remainder from "frlengths[]":
+ */
+ if (len > xfer->frlengths[xfer->aframes]) {
+ td->error_any = 1;
+ } else {
+ xfer->frlengths[xfer->aframes] -= len;
+ }
+ }
+ /* Check for transfer error */
+ if (td->error_any) {
+ /* the transfer is finished */
+ error = td->error_stall ? USB_ERR_STALLED : USB_ERR_IOERROR;
+ td = NULL;
+ break;
+ }
+ /* Check for short transfer */
+ if (len > 0) {
+ if (xfer->flags_int.short_frames_ok) {
+ /* follow alt next */
+ if (td->alt_next) {
+ td = td->obj_next;
+ } else {
+ td = NULL;
+ }
+ } else {
+ /* the transfer is finished */
+ td = NULL;
+ }
+ error = 0;
+ break;
+ }
+ td = td->obj_next;
+
+ /* this USB frame is complete */
+ error = 0;
+ break;
+
+ } while (0);
+
+ /* update transfer cache */
+
+ xfer->td_transfer_cache = td;
+
+ return (error);
+}
+
+static void
+octusb_standard_done(struct usb_xfer *xfer)
+{
+ struct octusb_softc *sc;
+ struct octusb_qh *qh;
+ usb_error_t error = 0;
+
+ DPRINTFN(12, "xfer=%p endpoint=%p transfer done\n",
+ xfer, xfer->endpoint);
+
+ /* reset scanner */
+
+ xfer->td_transfer_cache = xfer->td_transfer_first;
+
+ if (xfer->flags_int.control_xfr) {
+
+ if (xfer->flags_int.control_hdr)
+ error = octusb_standard_done_sub(xfer);
+
+ xfer->aframes = 1;
+
+ if (xfer->td_transfer_cache == NULL)
+ goto done;
+ }
+ while (xfer->aframes != xfer->nframes) {
+
+ error = octusb_standard_done_sub(xfer);
+
+ xfer->aframes++;
+
+ if (xfer->td_transfer_cache == NULL)
+ goto done;
+ }
+
+ if (xfer->flags_int.control_xfr &&
+ !xfer->flags_int.control_act)
+ error = octusb_standard_done_sub(xfer);
+
+done:
+ /* update data toggle */
+
+ qh = xfer->qh_start[0];
+ sc = qh->sc;
+
+ xfer->endpoint->toggle_next =
+ cvmx_usb_get_toggle(
+ &sc->sc_port[qh->port_index].state,
+ qh->ep_handle) ? 1 : 0;
+
+ octusb_device_done(xfer, error);
+}
+
+static void
+octusb_interrupt_poll(struct octusb_softc *sc)
+{
+ struct usb_xfer *xfer;
+ uint8_t x;
+
+ /* poll all ports */
+ for (x = 0; x != sc->sc_noport; x++)
+ cvmx_usb_poll(&sc->sc_port[x].state);
+
+repeat:
+ TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) {
+ if (!octusb_xfer_do_fifo(xfer)) {
+ /* queue has been modified */
+ goto repeat;
+ }
+ }
+}
+
+static void
+octusb_start_standard_chain(struct usb_xfer *xfer)
+{
+ DPRINTFN(8, "\n");
+
+ /* poll one time */
+ if (octusb_xfer_do_fifo(xfer)) {
+
+ /* put transfer on interrupt queue */
+ usbd_transfer_enqueue(&xfer->xroot->bus->intr_q, xfer);
+
+ /* start timeout, if any */
+ if (xfer->timeout != 0) {
+ usbd_transfer_timeout_ms(xfer,
+ &octusb_timeout, xfer->timeout);
+ }
+ }
+}
+
+void
+octusb_iterate_hw_softc(struct usb_bus *bus, usb_bus_mem_sub_cb_t *cb)
+{
+
+}
+
+usb_error_t
+octusb_init(struct octusb_softc *sc)
+{
+ cvmx_usb_initialize_flags_t flags;
+ int status;
+ uint8_t x;
+
+ /* flush all cache into memory */
+
+ usb_bus_mem_flush_all(&sc->sc_bus, &octusb_iterate_hw_softc);
+
+ /* set up the bus struct */
+ sc->sc_bus.methods = &octusb_bus_methods;
+
+ /* get number of ports */
+ sc->sc_noport = cvmx_usb_get_num_ports();
+
+ /* check number of ports */
+ if (sc->sc_noport > OCTUSB_MAX_PORTS)
+ sc->sc_noport = OCTUSB_MAX_PORTS;
+
+ /* set USB revision */
+ sc->sc_bus.usbrev = USB_REV_2_0;
+
+ /* flags for port initialization */
+ flags = CVMX_USB_INITIALIZE_FLAGS_CLOCK_AUTO;
+#ifdef USB_DEBUG
+ if (octusbdebug > 100)
+ flags |= CVMX_USB_INITIALIZE_FLAGS_DEBUG_ALL;
+#endif
+
+ USB_BUS_LOCK(&sc->sc_bus);
+
+ /* setup all ports */
+ for (x = 0; x != sc->sc_noport; x++) {
+ status = cvmx_usb_initialize(&sc->sc_port[x].state, x, flags);
+ if (status < 0)
+ sc->sc_port[x].disabled = 1;
+ }
+
+ USB_BUS_UNLOCK(&sc->sc_bus);
+
+ /* catch lost interrupts */
+ octusb_do_poll(&sc->sc_bus);
+
+ return (0);
+}
+
+usb_error_t
+octusb_uninit(struct octusb_softc *sc)
+{
+ uint8_t x;
+
+ USB_BUS_LOCK(&sc->sc_bus);
+
+ for (x = 0; x != sc->sc_noport; x++) {
+ if (sc->sc_port[x].disabled == 0)
+ cvmx_usb_shutdown(&sc->sc_port[x].state);
+ }
+ USB_BUS_UNLOCK(&sc->sc_bus);
+
+ return (0);
+
+}
+
+void
+octusb_suspend(struct octusb_softc *sc)
+{
+
+}
+
+void
+octusb_resume(struct octusb_softc *sc)
+{
+
+}
+
+/*------------------------------------------------------------------------*
+ * octusb_interrupt - OCTUSB interrupt handler
+ *------------------------------------------------------------------------*/
+void
+octusb_interrupt(struct octusb_softc *sc)
+{
+ USB_BUS_LOCK(&sc->sc_bus);
+
+ DPRINTFN(16, "real interrupt\n");
+
+ /* poll all the USB transfers */
+ octusb_interrupt_poll(sc);
+
+ USB_BUS_UNLOCK(&sc->sc_bus);
+}
+
+/*------------------------------------------------------------------------*
+ * octusb_timeout - OCTUSB transfer timeout handler
+ *------------------------------------------------------------------------*/
+static void
+octusb_timeout(void *arg)
+{
+ struct usb_xfer *xfer = arg;
+
+ DPRINTF("xfer=%p\n", xfer);
+
+ USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED);
+
+ /* transfer is transferred */
+ octusb_device_done(xfer, USB_ERR_TIMEOUT);
+}
+
+/*------------------------------------------------------------------------*
+ * octusb_do_poll - OCTUSB poll transfers
+ *------------------------------------------------------------------------*/
+static void
+octusb_do_poll(struct usb_bus *bus)
+{
+ struct octusb_softc *sc = OCTUSB_BUS2SC(bus);
+
+ USB_BUS_LOCK(&sc->sc_bus);
+ octusb_interrupt_poll(sc);
+ USB_BUS_UNLOCK(&sc->sc_bus);
+}
+
+static void
+octusb_setup_standard_chain_sub(struct octusb_std_temp *temp)
+{
+ struct octusb_td *td;
+
+ /* get current Transfer Descriptor */
+ td = temp->td_next;
+ temp->td = td;
+
+ /* prepare for next TD */
+ temp->td_next = td->obj_next;
+
+ /* fill out the Transfer Descriptor */
+ td->func = temp->func;
+ td->pc = temp->pc;
+ td->offset = temp->offset;
+ td->remainder = temp->len;
+ td->error_any = 0;
+ td->error_stall = 0;
+ td->short_pkt = temp->short_pkt;
+ td->alt_next = temp->setup_alt_next;
+}
+
+static void
+octusb_setup_standard_chain(struct usb_xfer *xfer)
+{
+ struct octusb_std_temp temp;
+ struct octusb_td *td;
+ uint32_t x;
+
+ DPRINTFN(9, "addr=%d endpt=%d sumlen=%d speed=%d\n",
+ xfer->address, UE_GET_ADDR(xfer->endpointno),
+ xfer->sumlen, usbd_get_speed(xfer->xroot->udev));
+
+ /* setup starting point */
+ td = xfer->td_start[0];
+ xfer->td_transfer_first = td;
+ xfer->td_transfer_cache = td;
+
+ temp.td = NULL;
+ temp.td_next = td;
+ temp.setup_alt_next = xfer->flags_int.short_frames_ok;
+ temp.offset = 0;
+
+ /* check if we should prepend a setup message */
+
+ if (xfer->flags_int.control_xfr) {
+
+ if (xfer->flags_int.control_hdr) {
+
+ temp.func = &octusb_host_control_header_tx;
+ temp.len = xfer->frlengths[0];
+ temp.pc = xfer->frbuffers + 0;
+ temp.short_pkt = temp.len ? 1 : 0;
+
+ /* check for last frame */
+ if (xfer->nframes == 1) {
+ /*
+ * no STATUS stage yet, SETUP is
+ * last
+ */
+ if (xfer->flags_int.control_act)
+ temp.setup_alt_next = 0;
+ }
+ octusb_setup_standard_chain_sub(&temp);
+ }
+ x = 1;
+ } else {
+ x = 0;
+ }
+
+ if (x != xfer->nframes) {
+ if (xfer->endpointno & UE_DIR_IN) {
+ if (xfer->flags_int.control_xfr)
+ temp.func = &octusb_host_control_data_rx;
+ else
+ temp.func = &octusb_non_control_data_rx;
+ } else {
+ if (xfer->flags_int.control_xfr)
+ temp.func = &octusb_host_control_data_tx;
+ else
+ temp.func = &octusb_non_control_data_tx;
+ }
+
+ /* setup "pc" pointer */
+ temp.pc = xfer->frbuffers + x;
+ }
+ while (x != xfer->nframes) {
+
+ /* DATA0 or DATA1 message */
+
+ temp.len = xfer->frlengths[x];
+
+ x++;
+
+ if (x == xfer->nframes) {
+ if (xfer->flags_int.control_xfr) {
+ /* no STATUS stage yet, DATA is last */
+ if (xfer->flags_int.control_act)
+ temp.setup_alt_next = 0;
+ } else {
+ temp.setup_alt_next = 0;
+ }
+ }
+ if (temp.len == 0) {
+
+ /* make sure that we send an USB packet */
+
+ temp.short_pkt = 0;
+
+ } else {
+
+ /* regular data transfer */
+
+ temp.short_pkt = (xfer->flags.force_short_xfer) ? 0 : 1;
+ }
+
+ octusb_setup_standard_chain_sub(&temp);
+
+ if (xfer->flags_int.isochronous_xfr) {
+ /* get next data offset */
+ temp.offset += temp.len;
+ } else {
+ /* get next Page Cache pointer */
+ temp.pc = xfer->frbuffers + x;
+ }
+ }
+
+ /* check if we should append a status stage */
+
+ if (xfer->flags_int.control_xfr &&
+ !xfer->flags_int.control_act) {
+
+ temp.func = &octusb_host_control_status_tx;
+ temp.len = 0;
+ temp.pc = NULL;
+ temp.short_pkt = 0;
+ temp.setup_alt_next = 0;
+
+ octusb_setup_standard_chain_sub(&temp);
+ }
+ /* must have at least one frame! */
+ td = temp.td;
+ xfer->td_transfer_last = td;
+
+ /* properly setup QH */
+
+ td->qh->ep_allocated = 0;
+ td->qh->ep_toggle_next = xfer->endpoint->toggle_next ? 1 : 0;
+}
+
+/*------------------------------------------------------------------------*
+ * octusb_device_done - OCTUSB transfers done code
+ *
+ * NOTE: This function can be called more than one time in a row.
+ *------------------------------------------------------------------------*/
+static void
+octusb_device_done(struct usb_xfer *xfer, usb_error_t error)
+{
+ USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED);
+
+ DPRINTFN(2, "xfer=%p, endpoint=%p, error=%d\n",
+ xfer, xfer->endpoint, error);
+
+ /*
+ * 1) Free any endpoints.
+ * 2) Control transfers can be split and we should not re-open
+ * the data pipe between transactions unless there is an error.
+ */
+ if ((xfer->flags_int.control_act == 0) || (error != 0)) {
+ struct octusb_td *td;
+
+ td = xfer->td_start[0];
+
+ octusb_host_free_endpoint(td);
+ }
+ /* dequeue transfer and start next transfer */
+ usbd_transfer_done(xfer, error);
+}
+
+/*------------------------------------------------------------------------*
+ * octusb bulk support
+ *------------------------------------------------------------------------*/
+static void
+octusb_device_bulk_open(struct usb_xfer *xfer)
+{
+ return;
+}
+
+static void
+octusb_device_bulk_close(struct usb_xfer *xfer)
+{
+ octusb_device_done(xfer, USB_ERR_CANCELLED);
+}
+
+static void
+octusb_device_bulk_enter(struct usb_xfer *xfer)
+{
+ return;
+}
+
+static void
+octusb_device_bulk_start(struct usb_xfer *xfer)
+{
+ /* setup TDs */
+ octusb_setup_standard_chain(xfer);
+ octusb_start_standard_chain(xfer);
+}
+
+struct usb_pipe_methods octusb_device_bulk_methods =
+{
+ .open = octusb_device_bulk_open,
+ .close = octusb_device_bulk_close,
+ .enter = octusb_device_bulk_enter,
+ .start = octusb_device_bulk_start,
+};
+
+/*------------------------------------------------------------------------*
+ * octusb control support
+ *------------------------------------------------------------------------*/
+static void
+octusb_device_ctrl_open(struct usb_xfer *xfer)
+{
+ return;
+}
+
+static void
+octusb_device_ctrl_close(struct usb_xfer *xfer)
+{
+ octusb_device_done(xfer, USB_ERR_CANCELLED);
+}
+
+static void
+octusb_device_ctrl_enter(struct usb_xfer *xfer)
+{
+ return;
+}
+
+static void
+octusb_device_ctrl_start(struct usb_xfer *xfer)
+{
+ /* setup TDs */
+ octusb_setup_standard_chain(xfer);
+ octusb_start_standard_chain(xfer);
+}
+
+struct usb_pipe_methods octusb_device_ctrl_methods =
+{
+ .open = octusb_device_ctrl_open,
+ .close = octusb_device_ctrl_close,
+ .enter = octusb_device_ctrl_enter,
+ .start = octusb_device_ctrl_start,
+};
+
+/*------------------------------------------------------------------------*
+ * octusb interrupt support
+ *------------------------------------------------------------------------*/
+static void
+octusb_device_intr_open(struct usb_xfer *xfer)
+{
+ return;
+}
+
+static void
+octusb_device_intr_close(struct usb_xfer *xfer)
+{
+ octusb_device_done(xfer, USB_ERR_CANCELLED);
+}
+
+static void
+octusb_device_intr_enter(struct usb_xfer *xfer)
+{
+ return;
+}
+
+static void
+octusb_device_intr_start(struct usb_xfer *xfer)
+{
+ /* setup TDs */
+ octusb_setup_standard_chain(xfer);
+ octusb_start_standard_chain(xfer);
+}
+
+struct usb_pipe_methods octusb_device_intr_methods =
+{
+ .open = octusb_device_intr_open,
+ .close = octusb_device_intr_close,
+ .enter = octusb_device_intr_enter,
+ .start = octusb_device_intr_start,
+};
+
+/*------------------------------------------------------------------------*
+ * octusb isochronous support
+ *------------------------------------------------------------------------*/
+static void
+octusb_device_isoc_open(struct usb_xfer *xfer)
+{
+ return;
+}
+
+static void
+octusb_device_isoc_close(struct usb_xfer *xfer)
+{
+ octusb_device_done(xfer, USB_ERR_CANCELLED);
+}
+
+static void
+octusb_device_isoc_enter(struct usb_xfer *xfer)
+{
+ struct octusb_softc *sc = OCTUSB_BUS2SC(xfer->xroot->bus);
+ uint32_t temp;
+ uint32_t frame_count;
+ uint32_t fs_frames;
+
+ DPRINTFN(5, "xfer=%p next=%d nframes=%d\n",
+ xfer, xfer->endpoint->isoc_next, xfer->nframes);
+
+ /* get the current frame index */
+
+ frame_count = cvmx_usb_get_frame_number(
+ &sc->sc_port[xfer->xroot->udev->port_index].state);
+
+ /*
+ * check if the frame index is within the window where the frames
+ * will be inserted
+ */
+ temp = (frame_count - xfer->endpoint->isoc_next) & 0x7FF;
+
+ if (usbd_get_speed(xfer->xroot->udev) == USB_SPEED_HIGH) {
+ fs_frames = (xfer->nframes + 7) / 8;
+ } else {
+ fs_frames = xfer->nframes;
+ }
+
+ if ((xfer->endpoint->is_synced == 0) || (temp < fs_frames)) {
+ /*
+ * If there is data underflow or the pipe queue is
+ * empty we schedule the transfer a few frames ahead
+ * of the current frame position. Else two isochronous
+ * transfers might overlap.
+ */
+ xfer->endpoint->isoc_next = (frame_count + 3) & 0x7FF;
+ xfer->endpoint->is_synced = 1;
+ DPRINTFN(2, "start next=%d\n", xfer->endpoint->isoc_next);
+ }
+ /*
+ * compute how many milliseconds the insertion is ahead of the
+ * current frame position:
+ */
+ temp = (xfer->endpoint->isoc_next - frame_count) & 0x7FF;
+
+ /*
+ * pre-compute when the isochronous transfer will be finished:
+ */
+ xfer->isoc_time_complete =
+ usb_isoc_time_expand(&sc->sc_bus, frame_count) + temp +
+ fs_frames;
+
+ /* compute frame number for next insertion */
+ xfer->endpoint->isoc_next += fs_frames;
+}
+
+static void
+octusb_device_isoc_start(struct usb_xfer *xfer)
+{
+ /* setup TDs */
+ octusb_setup_standard_chain(xfer);
+ octusb_start_standard_chain(xfer);
+}
+
+struct usb_pipe_methods octusb_device_isoc_methods =
+{
+ .open = octusb_device_isoc_open,
+ .close = octusb_device_isoc_close,
+ .enter = octusb_device_isoc_enter,
+ .start = octusb_device_isoc_start,
+};
+
+/*------------------------------------------------------------------------*
+ * OCTUSB root HUB support
+ *------------------------------------------------------------------------*
+ * Simulate a hardware HUB by handling all the necessary requests.
+ *------------------------------------------------------------------------*/
+static const
+struct usb_device_descriptor octusb_devd = {
+ .bLength = sizeof(octusb_devd),
+ .bDescriptorType = UDESC_DEVICE,
+ .bcdUSB = {0x00, 0x02},
+ .bDeviceClass = UDCLASS_HUB,
+ .bDeviceSubClass = UDSUBCLASS_HUB,
+ .bDeviceProtocol = UDPROTO_FSHUB,
+ .bMaxPacketSize = 64,
+ .idVendor = {0},
+ .idProduct = {0},
+ .bcdDevice = {0x00, 0x01},
+ .iManufacturer = 1,
+ .iProduct = 2,
+ .iSerialNumber = 0,
+ .bNumConfigurations = 1,
+};
+
+static const
+struct usb_device_qualifier octusb_odevd = {
+ .bLength = sizeof(octusb_odevd),
+ .bDescriptorType = UDESC_DEVICE_QUALIFIER,
+ .bcdUSB = {0x00, 0x02},
+ .bDeviceClass = UDCLASS_HUB,
+ .bDeviceSubClass = UDSUBCLASS_HUB,
+ .bDeviceProtocol = UDPROTO_FSHUB,
+ .bMaxPacketSize0 = 0,
+ .bNumConfigurations = 0,
+ .bReserved = 0,
+};
+
+static const
+struct octusb_config_desc octusb_confd = {
+ .confd = {
+ .bLength = sizeof(struct usb_config_descriptor),
+ .bDescriptorType = UDESC_CONFIG,
+ .wTotalLength[0] = sizeof(octusb_confd),
+ .bNumInterface = 1,
+ .bConfigurationValue = 1,
+ .iConfiguration = 0,
+ .bmAttributes = UC_SELF_POWERED,
+ .bMaxPower = 0 /* max power */
+ },
+ .ifcd = {
+ .bLength = sizeof(struct usb_interface_descriptor),
+ .bDescriptorType = UDESC_INTERFACE,
+ .bNumEndpoints = 1,
+ .bInterfaceClass = UICLASS_HUB,
+ .bInterfaceSubClass = UISUBCLASS_HUB,
+ .bInterfaceProtocol = UIPROTO_FSHUB,
+ },
+ .endpd = {
+ .bLength = sizeof(struct usb_endpoint_descriptor),
+ .bDescriptorType = UDESC_ENDPOINT,
+ .bEndpointAddress = UE_DIR_IN | OCTUSB_INTR_ENDPT,
+ .bmAttributes = UE_INTERRUPT,
+ .wMaxPacketSize[0] = 8, /* max packet (63 ports) */
+ .bInterval = 255,
+ },
+};
+
+static const
+struct usb_hub_descriptor_min octusb_hubd =
+{
+ .bDescLength = sizeof(octusb_hubd),
+ .bDescriptorType = UDESC_HUB,
+ .bNbrPorts = 2,
+ .wHubCharacteristics = {UHD_PWR_NO_SWITCH | UHD_OC_INDIVIDUAL, 0},
+ .bPwrOn2PwrGood = 50,
+ .bHubContrCurrent = 0,
+ .DeviceRemovable = {0x00}, /* all ports are removable */
+};
+
+static usb_error_t
+octusb_roothub_exec(struct usb_device *udev,
+ struct usb_device_request *req, const void **pptr, uint16_t *plength)
+{
+ struct octusb_softc *sc = OCTUSB_BUS2SC(udev->bus);
+ const void *ptr;
+ const char *str_ptr;
+ uint16_t value;
+ uint16_t index;
+ uint16_t status;
+ uint16_t change;
+ uint16_t len;
+ usb_error_t err;
+ cvmx_usb_port_status_t usb_port_status;
+
+ USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED);
+
+ /* XXX disable power save mode, hence it is not supported */
+ udev->power_mode = USB_POWER_MODE_ON;
+
+ /* buffer reset */
+ ptr = (const void *)&sc->sc_hub_desc.temp;
+ len = 0;
+ err = 0;
+
+ value = UGETW(req->wValue);
+ index = UGETW(req->wIndex);
+
+ DPRINTFN(3, "type=0x%02x request=0x%02x wLen=0x%04x "
+ "wValue=0x%04x wIndex=0x%04x\n",
+ req->bmRequestType, req->bRequest,
+ UGETW(req->wLength), value, index);
+
+#define C(x,y) ((x) | ((y) << 8))
+ switch (C(req->bRequest, req->bmRequestType)) {
+ case C(UR_CLEAR_FEATURE, UT_WRITE_DEVICE):
+ case C(UR_CLEAR_FEATURE, UT_WRITE_INTERFACE):
+ case C(UR_CLEAR_FEATURE, UT_WRITE_ENDPOINT):
+ break;
+ case C(UR_GET_CONFIG, UT_READ_DEVICE):
+ len = 1;
+ sc->sc_hub_desc.temp[0] = sc->sc_conf;
+ break;
+ case C(UR_GET_DESCRIPTOR, UT_READ_DEVICE):
+ switch (value >> 8) {
+ case UDESC_DEVICE:
+ if ((value & 0xff) != 0) {
+ err = USB_ERR_IOERROR;
+ goto done;
+ }
+ len = sizeof(octusb_devd);
+
+ ptr = (const void *)&octusb_devd;
+ break;
+
+ case UDESC_DEVICE_QUALIFIER:
+ if ((value & 0xff) != 0) {
+ err = USB_ERR_IOERROR;
+ goto done;
+ }
+ len = sizeof(octusb_odevd);
+ ptr = (const void *)&octusb_odevd;
+ break;
+
+ case UDESC_CONFIG:
+ if ((value & 0xff) != 0) {
+ err = USB_ERR_IOERROR;
+ goto done;
+ }
+ len = sizeof(octusb_confd);
+ ptr = (const void *)&octusb_confd;
+ break;
+
+ case UDESC_STRING:
+ switch (value & 0xff) {
+ case 0: /* Language table */
+ str_ptr = "\001";
+ break;
+
+ case 1: /* Vendor */
+ str_ptr = "Cavium Networks";
+ break;
+
+ case 2: /* Product */
+ str_ptr = "OCTUSB Root HUB";
+ break;
+
+ default:
+ str_ptr = "";
+ break;
+ }
+
+ len = usb_make_str_desc(sc->sc_hub_desc.temp,
+ sizeof(sc->sc_hub_desc.temp), str_ptr);
+ break;
+
+ default:
+ err = USB_ERR_IOERROR;
+ goto done;
+ }
+ break;
+ case C(UR_GET_INTERFACE, UT_READ_INTERFACE):
+ len = 1;
+ sc->sc_hub_desc.temp[0] = 0;
+ break;
+ case C(UR_GET_STATUS, UT_READ_DEVICE):
+ len = 2;
+ USETW(sc->sc_hub_desc.stat.wStatus, UDS_SELF_POWERED);
+ break;
+ case C(UR_GET_STATUS, UT_READ_INTERFACE):
+ case C(UR_GET_STATUS, UT_READ_ENDPOINT):
+ len = 2;
+ USETW(sc->sc_hub_desc.stat.wStatus, 0);
+ break;
+ case C(UR_SET_ADDRESS, UT_WRITE_DEVICE):
+ if (value >= OCTUSB_MAX_DEVICES) {
+ err = USB_ERR_IOERROR;
+ goto done;
+ }
+ sc->sc_addr = value;
+ break;
+ case C(UR_SET_CONFIG, UT_WRITE_DEVICE):
+ if ((value != 0) && (value != 1)) {
+ err = USB_ERR_IOERROR;
+ goto done;
+ }
+ sc->sc_conf = value;
+ break;
+ case C(UR_SET_DESCRIPTOR, UT_WRITE_DEVICE):
+ break;
+ case C(UR_SET_FEATURE, UT_WRITE_DEVICE):
+ case C(UR_SET_FEATURE, UT_WRITE_INTERFACE):
+ case C(UR_SET_FEATURE, UT_WRITE_ENDPOINT):
+ err = USB_ERR_IOERROR;
+ goto done;
+ case C(UR_SET_INTERFACE, UT_WRITE_INTERFACE):
+ break;
+ case C(UR_SYNCH_FRAME, UT_WRITE_ENDPOINT):
+ break;
+ /* Hub requests */
+ case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_DEVICE):
+ break;
+ case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_OTHER):
+ DPRINTFN(4, "UR_CLEAR_PORT_FEATURE "
+ "port=%d feature=%d\n",
+ index, value);
+ if ((index < 1) ||
+ (index > sc->sc_noport) ||
+ sc->sc_port[index - 1].disabled) {
+ err = USB_ERR_IOERROR;
+ goto done;
+ }
+ index--;
+
+ switch (value) {
+ case UHF_PORT_ENABLE:
+ cvmx_usb_disable(&sc->sc_port[index].state);
+ break;
+ case UHF_PORT_SUSPEND:
+ case UHF_PORT_RESET:
+ break;
+ case UHF_C_PORT_CONNECTION:
+ cvmx_usb_set_status(&sc->sc_port[index].state,
+ cvmx_usb_get_status(&sc->sc_port[index].state));
+ break;
+ case UHF_C_PORT_ENABLE:
+ cvmx_usb_set_status(&sc->sc_port[index].state,
+ cvmx_usb_get_status(&sc->sc_port[index].state));
+ break;
+ case UHF_C_PORT_OVER_CURRENT:
+ cvmx_usb_set_status(&sc->sc_port[index].state,
+ cvmx_usb_get_status(&sc->sc_port[index].state));
+ break;
+ case UHF_C_PORT_RESET:
+ sc->sc_isreset = 0;
+ goto done;
+ case UHF_C_PORT_SUSPEND:
+ break;
+ case UHF_PORT_CONNECTION:
+ case UHF_PORT_OVER_CURRENT:
+ case UHF_PORT_POWER:
+ case UHF_PORT_LOW_SPEED:
+ default:
+ err = USB_ERR_IOERROR;
+ goto done;
+ }
+ break;
+ case C(UR_GET_DESCRIPTOR, UT_READ_CLASS_DEVICE):
+ if ((value & 0xff) != 0) {
+ err = USB_ERR_IOERROR;
+ goto done;
+ }
+ sc->sc_hubd = octusb_hubd;
+ sc->sc_hubd.bNbrPorts = sc->sc_noport;
+ len = sizeof(sc->sc_hubd);
+ ptr = (const void *)&sc->sc_hubd;
+ break;
+ case C(UR_GET_STATUS, UT_READ_CLASS_DEVICE):
+ len = 16;
+ memset(sc->sc_hub_desc.temp, 0, 16);
+ break;
+ case C(UR_GET_STATUS, UT_READ_CLASS_OTHER):
+ if ((index < 1) ||
+ (index > sc->sc_noport) ||
+ sc->sc_port[index - 1].disabled) {
+ err = USB_ERR_IOERROR;
+ goto done;
+ }
+ index--;
+
+ usb_port_status = cvmx_usb_get_status(&sc->sc_port[index].state);
+
+ status = change = 0;
+ if (usb_port_status.connected)
+ status |= UPS_CURRENT_CONNECT_STATUS;
+ if (usb_port_status.port_enabled)
+ status |= UPS_PORT_ENABLED;
+ if (usb_port_status.port_over_current)
+ status |= UPS_OVERCURRENT_INDICATOR;
+ if (usb_port_status.port_powered)
+ status |= UPS_PORT_POWER;
+
+ switch (usb_port_status.port_speed) {
+ case CVMX_USB_SPEED_HIGH:
+ status |= UPS_HIGH_SPEED;
+ break;
+ case CVMX_USB_SPEED_FULL:
+ break;
+ default:
+ status |= UPS_LOW_SPEED;
+ break;
+ }
+
+ if (usb_port_status.connect_change)
+ change |= UPS_C_CONNECT_STATUS;
+ if (sc->sc_isreset)
+ change |= UPS_C_PORT_RESET;
+
+ USETW(sc->sc_hub_desc.ps.wPortStatus, status);
+ USETW(sc->sc_hub_desc.ps.wPortChange, change);
+
+ len = sizeof(sc->sc_hub_desc.ps);
+ break;
+ case C(UR_SET_DESCRIPTOR, UT_WRITE_CLASS_DEVICE):
+ err = USB_ERR_IOERROR;
+ goto done;
+ case C(UR_SET_FEATURE, UT_WRITE_CLASS_DEVICE):
+ break;
+ case C(UR_SET_FEATURE, UT_WRITE_CLASS_OTHER):
+ if ((index < 1) ||
+ (index > sc->sc_noport) ||
+ sc->sc_port[index - 1].disabled) {
+ err = USB_ERR_IOERROR;
+ goto done;
+ }
+ index--;
+
+ switch (value) {
+ case UHF_PORT_ENABLE:
+ break;
+ case UHF_PORT_RESET:
+ cvmx_usb_disable(&sc->sc_port[index].state);
+ if (cvmx_usb_enable(&sc->sc_port[index].state)) {
+ err = USB_ERR_IOERROR;
+ goto done;
+ }
+ sc->sc_isreset = 1;
+ goto done;
+ case UHF_PORT_POWER:
+ /* pretend we turned on power */
+ goto done;
+ case UHF_PORT_SUSPEND:
+ case UHF_C_PORT_CONNECTION:
+ case UHF_C_PORT_ENABLE:
+ case UHF_C_PORT_OVER_CURRENT:
+ case UHF_PORT_CONNECTION:
+ case UHF_PORT_OVER_CURRENT:
+ case UHF_PORT_LOW_SPEED:
+ case UHF_C_PORT_SUSPEND:
+ case UHF_C_PORT_RESET:
+ default:
+ err = USB_ERR_IOERROR;
+ goto done;
+ }
+ break;
+ default:
+ err = USB_ERR_IOERROR;
+ goto done;
+ }
+done:
+ *plength = len;
+ *pptr = ptr;
+ return (err);
+}
+
+static void
+octusb_xfer_setup(struct usb_setup_params *parm)
+{
+ struct usb_page_search page_info;
+ struct usb_page_cache *pc;
+ struct octusb_softc *sc;
+ struct octusb_qh *qh;
+ struct usb_xfer *xfer;
+ void *last_obj;
+ uint32_t n;
+ uint32_t ntd;
+
+ sc = OCTUSB_BUS2SC(parm->udev->bus);
+ xfer = parm->curr_xfer;
+ qh = NULL;
+
+ /*
+ * NOTE: This driver does not use any of the parameters that
+ * are computed from the following values. Just set some
+ * reasonable dummies:
+ */
+
+ parm->hc_max_packet_size = 0x400;
+ parm->hc_max_packet_count = 3;
+ parm->hc_max_frame_size = 0xC00;
+
+ usbd_transfer_setup_sub(parm);
+
+ if (parm->err)
+ return;
+
+ /* Allocate a queue head */
+
+ if (usbd_transfer_setup_sub_malloc(
+ parm, &pc, sizeof(struct octusb_qh),
+ USB_HOST_ALIGN, 1)) {
+ parm->err = USB_ERR_NOMEM;
+ return;
+ }
+ if (parm->buf) {
+ usbd_get_page(pc, 0, &page_info);
+
+ qh = page_info.buffer;
+
+ /* fill out QH */
+
+ qh->sc = OCTUSB_BUS2SC(xfer->xroot->bus);
+ qh->max_frame_size = xfer->max_frame_size;
+ qh->max_packet_size = xfer->max_packet_size;
+ qh->ep_num = xfer->endpointno;
+ qh->ep_type = xfer->endpoint->edesc->bmAttributes;
+ qh->dev_addr = xfer->address;
+ qh->dev_speed = usbd_get_speed(xfer->xroot->udev);
+ qh->port_index = xfer->xroot->udev->port_index;
+
+ switch (xfer->endpoint->edesc->bmAttributes & UE_XFERTYPE) {
+ case UE_INTERRUPT:
+ if (usbd_get_speed(xfer->xroot->udev) == USB_SPEED_HIGH)
+ qh->ep_interval = xfer->interval * 8;
+ else
+ qh->ep_interval = xfer->interval * 1;
+ break;
+ case UE_ISOCHRONOUS:
+ qh->ep_interval = 1 << xfer->fps_shift;
+ break;
+ default:
+ qh->ep_interval = 0;
+ break;
+ }
+
+ qh->ep_mult = xfer->max_packet_count & 3;
+ qh->hs_hub_addr = xfer->xroot->udev->hs_hub_addr;
+ qh->hs_hub_port = xfer->xroot->udev->hs_port_no;
+ }
+ xfer->qh_start[0] = qh;
+
+ /* Allocate a fixup buffer */
+
+ if (usbd_transfer_setup_sub_malloc(
+ parm, &pc, OCTUSB_MAX_FIXUP,
+ OCTUSB_MAX_FIXUP, 1)) {
+ parm->err = USB_ERR_NOMEM;
+ return;
+ }
+ if (parm->buf) {
+ usbd_get_page(pc, 0, &page_info);
+
+ qh->fixup_phys = page_info.physaddr;
+ qh->fixup_pc = pc;
+ qh->fixup_buf = page_info.buffer;
+ }
+ /* Allocate transfer descriptors */
+
+ last_obj = NULL;
+
+ ntd = xfer->nframes + 1 /* STATUS */ + 1 /* SYNC */ ;
+
+ if (usbd_transfer_setup_sub_malloc(
+ parm, &pc, sizeof(struct octusb_td),
+ USB_HOST_ALIGN, ntd)) {
+ parm->err = USB_ERR_NOMEM;
+ return;
+ }
+ if (parm->buf) {
+ for (n = 0; n != ntd; n++) {
+ struct octusb_td *td;
+
+ usbd_get_page(pc + n, 0, &page_info);
+
+ td = page_info.buffer;
+
+ td->qh = qh;
+ td->obj_next = last_obj;
+
+ last_obj = td;
+ }
+ }
+ xfer->td_start[0] = last_obj;
+}
+
+static void
+octusb_ep_init(struct usb_device *udev, struct usb_endpoint_descriptor *edesc,
+ struct usb_endpoint *ep)
+{
+ struct octusb_softc *sc = OCTUSB_BUS2SC(udev->bus);
+
+ DPRINTFN(2, "endpoint=%p, addr=%d, endpt=%d, mode=%d (%d)\n",
+ ep, udev->address, edesc->bEndpointAddress,
+ udev->flags.usb_mode, sc->sc_addr);
+
+ if (udev->flags.usb_mode != USB_MODE_HOST) {
+ /* not supported */
+ return;
+ }
+ if (udev->device_index != sc->sc_addr) {
+ switch (edesc->bmAttributes & UE_XFERTYPE) {
+ case UE_CONTROL:
+ ep->methods = &octusb_device_ctrl_methods;
+ break;
+ case UE_INTERRUPT:
+ ep->methods = &octusb_device_intr_methods;
+ break;
+ case UE_ISOCHRONOUS:
+ if (udev->speed != USB_SPEED_LOW)
+ ep->methods = &octusb_device_isoc_methods;
+ break;
+ case UE_BULK:
+ ep->methods = &octusb_device_bulk_methods;
+ break;
+ default:
+ /* do nothing */
+ break;
+ }
+ }
+}
+
+static void
+octusb_xfer_unsetup(struct usb_xfer *xfer)
+{
+ DPRINTF("Nothing to do.\n");
+}
+
+static void
+octusb_get_dma_delay(struct usb_device *udev, uint32_t *pus)
+{
+ /* DMA delay - wait until any use of memory is finished */
+ *pus = (2125); /* microseconds */
+}
+
+static void
+octusb_device_resume(struct usb_device *udev)
+{
+ DPRINTF("Nothing to do.\n");
+}
+
+static void
+octusb_device_suspend(struct usb_device *udev)
+{
+ DPRINTF("Nothing to do.\n");
+}
+
+static void
+octusb_set_hw_power(struct usb_bus *bus)
+{
+ DPRINTF("Nothing to do.\n");
+}
+
+struct usb_bus_methods octusb_bus_methods = {
+ .endpoint_init = octusb_ep_init,
+ .xfer_setup = octusb_xfer_setup,
+ .xfer_unsetup = octusb_xfer_unsetup,
+ .get_dma_delay = octusb_get_dma_delay,
+ .device_resume = octusb_device_resume,
+ .device_suspend = octusb_device_suspend,
+ .set_hw_power = octusb_set_hw_power,
+ .roothub_exec = octusb_roothub_exec,
+ .xfer_poll = octusb_do_poll,
+};
diff --git a/sys/mips/cavium/usb/octusb.h b/sys/mips/cavium/usb/octusb.h
new file mode 100644
index 000000000000..31f4fc0835bd
--- /dev/null
+++ b/sys/mips/cavium/usb/octusb.h
@@ -0,0 +1,147 @@
+/* $FreeBSD$ */
+
+/*-
+ * Copyright (c) 2010 Hans Petter Selasky. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _OCTUSB_H_
+#define _OCTUSB_H_
+
+#define OCTUSB_MAX_DEVICES MIN(USB_MAX_DEVICES, 64)
+/*
+ * The second port is on a different IRQ and so we disable it for now.
+ */
+#if 1
+#define OCTUSB_MAX_PORTS 1 /* hardcoded */
+#else
+#define OCTUSB_MAX_PORTS 2 /* hardcoded */
+#endif
+#define OCTUSB_MAX_FIXUP 4096 /* bytes */
+#define OCTUSB_INTR_ENDPT 0x01
+
+struct octusb_qh;
+struct octusb_td;
+struct octusb_softc;
+
+typedef uint8_t (octusb_cmd_t)(struct octusb_td *td);
+
+struct octusb_td {
+ struct octusb_qh *qh;
+ struct octusb_td *obj_next;
+ struct usb_page_cache *pc;
+ octusb_cmd_t *func;
+
+ uint32_t remainder;
+ uint32_t offset;
+
+ uint8_t error_any:1;
+ uint8_t error_stall:1;
+ uint8_t short_pkt:1;
+ uint8_t alt_next:1;
+ uint8_t reserved:4;
+};
+
+struct octusb_qh {
+
+ uint64_t fixup_phys;
+
+ struct octusb_softc *sc;
+ struct usb_page_cache *fixup_pc;
+ uint8_t *fixup_buf;
+
+ cvmx_usb_iso_packet_t iso_pkt;
+
+ uint32_t fixup_off;
+
+ uint16_t max_frame_size;
+ uint16_t max_packet_size;
+ uint16_t fixup_actlen;
+ uint16_t fixup_len;
+ uint16_t ep_interval;
+
+ uint8_t dev_addr;
+ uint8_t dev_speed;
+ uint8_t ep_allocated;
+ uint8_t ep_mult;
+ uint8_t ep_num;
+ uint8_t ep_type;
+ uint8_t ep_toggle_next;
+ uint8_t port_index;
+ uint8_t fixup_complete;
+ uint8_t fixup_pending;
+ uint8_t hs_hub_addr;
+ uint8_t hs_hub_port;
+
+ int fixup_handle;
+ int ep_handle;
+};
+
+struct octusb_config_desc {
+ struct usb_config_descriptor confd;
+ struct usb_interface_descriptor ifcd;
+ struct usb_endpoint_descriptor endpd;
+} __packed;
+
+union octusb_hub_desc {
+ struct usb_status stat;
+ struct usb_port_status ps;
+ uint8_t temp[128];
+};
+
+struct octusb_port {
+ cvmx_usb_state_t state;
+ uint8_t disabled;
+};
+
+struct octusb_softc {
+
+ struct usb_bus sc_bus; /* base device */
+ union octusb_hub_desc sc_hub_desc;
+
+ struct usb_device *sc_devices[OCTUSB_MAX_DEVICES];
+
+ struct resource *sc_irq_res;
+ void *sc_intr_hdl;
+
+ struct octusb_port sc_port[OCTUSB_MAX_PORTS];
+ device_t sc_dev;
+
+ struct usb_hub_descriptor_min sc_hubd;
+
+ uint8_t sc_noport; /* number of ports */
+ uint8_t sc_addr; /* device address */
+ uint8_t sc_conf; /* device configuration */
+ uint8_t sc_isreset; /* set if current port is reset */
+
+ uint8_t sc_hub_idata[1];
+};
+
+usb_bus_mem_cb_t octusb_iterate_hw_softc;
+usb_error_t octusb_init(struct octusb_softc *);
+usb_error_t octusb_uninit(struct octusb_softc *);
+void octusb_suspend(struct octusb_softc *);
+void octusb_resume(struct octusb_softc *);
+void octusb_interrupt(struct octusb_softc *);
+
+#endif /* _OCTUSB_H_ */
diff --git a/sys/mips/cavium/usb/octusb_octeon.c b/sys/mips/cavium/usb/octusb_octeon.c
new file mode 100644
index 000000000000..edd8290fb080
--- /dev/null
+++ b/sys/mips/cavium/usb/octusb_octeon.c
@@ -0,0 +1,223 @@
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*-
+ * Copyright (c) 2007-2008 Hans Petter Selasky. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/stdint.h>
+#include <sys/stddef.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/linker_set.h>
+#include <sys/module.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+#include <sys/rman.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+
+#include <contrib/octeon-sdk/cvmx.h>
+#include <contrib/octeon-sdk/cvmx-interrupt.h>
+#include <contrib/octeon-sdk/cvmx-usb.h>
+
+#include <mips/cavium/usb/octusb.h>
+
+#define MEM_RID 0
+
+static device_identify_t octusb_octeon_identify;
+static device_probe_t octusb_octeon_probe;
+static device_attach_t octusb_octeon_attach;
+static device_detach_t octusb_octeon_detach;
+static device_shutdown_t octusb_octeon_shutdown;
+
+struct octusb_octeon_softc {
+ struct octusb_softc sc_dci; /* must be first */
+};
+
+static void
+octusb_octeon_identify(driver_t *drv, device_t parent)
+{
+ if (octeon_has_feature(OCTEON_FEATURE_USB))
+ BUS_ADD_CHILD(parent, 0, "octusb", 0);
+}
+
+static int
+octusb_octeon_probe(device_t dev)
+{
+ device_set_desc(dev, "Cavium Octeon USB controller");
+ return (0);
+}
+
+static int
+octusb_octeon_attach(device_t dev)
+{
+ struct octusb_octeon_softc *sc = device_get_softc(dev);
+ int err;
+ int rid;
+
+ /* setup controller interface softc */
+
+ /* initialise some bus fields */
+ sc->sc_dci.sc_bus.parent = dev;
+ sc->sc_dci.sc_bus.devices = sc->sc_dci.sc_devices;
+ sc->sc_dci.sc_bus.devices_max = OCTUSB_MAX_DEVICES;
+
+ /* get all DMA memory */
+ if (usb_bus_mem_alloc_all(&sc->sc_dci.sc_bus,
+ USB_GET_DMA_TAG(dev), NULL)) {
+ return (ENOMEM);
+ }
+ rid = 0;
+ sc->sc_dci.sc_irq_res =
+ bus_alloc_resource(dev, SYS_RES_IRQ, &rid,
+ CVMX_IRQ_USB, CVMX_IRQ_USB, 1, RF_ACTIVE);
+ if (!(sc->sc_dci.sc_irq_res)) {
+ goto error;
+ }
+
+ sc->sc_dci.sc_bus.bdev = device_add_child(dev, "usbus", -1);
+ if (!(sc->sc_dci.sc_bus.bdev)) {
+ goto error;
+ }
+ device_set_ivars(sc->sc_dci.sc_bus.bdev, &sc->sc_dci.sc_bus);
+
+#if (__FreeBSD_version >= 700031)
+ err = bus_setup_intr(dev, sc->sc_dci.sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
+ NULL, (driver_intr_t *)octusb_interrupt, sc, &sc->sc_dci.sc_intr_hdl);
+#else
+ err = bus_setup_intr(dev, sc->sc_dci.sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
+ (driver_intr_t *)octusb_interrupt, sc, &sc->sc_dci.sc_intr_hdl);
+#endif
+ if (err) {
+ sc->sc_dci.sc_intr_hdl = NULL;
+ goto error;
+ }
+ err = octusb_init(&sc->sc_dci);
+ if (!err) {
+ err = device_probe_and_attach(sc->sc_dci.sc_bus.bdev);
+ }
+ if (err) {
+ goto error;
+ }
+ return (0);
+
+error:
+ octusb_octeon_detach(dev);
+ return (ENXIO);
+}
+
+static int
+octusb_octeon_detach(device_t dev)
+{
+ struct octusb_octeon_softc *sc = device_get_softc(dev);
+ device_t bdev;
+ int err;
+
+ if (sc->sc_dci.sc_bus.bdev) {
+ bdev = sc->sc_dci.sc_bus.bdev;
+ device_detach(bdev);
+ device_delete_child(dev, bdev);
+ }
+ /* during module unload there are lots of children leftover */
+ device_delete_all_children(dev);
+
+ if (sc->sc_dci.sc_irq_res && sc->sc_dci.sc_intr_hdl) {
+ /*
+ * only call octusb_octeon_uninit() after octusb_octeon_init()
+ */
+ octusb_uninit(&sc->sc_dci);
+
+ err = bus_teardown_intr(dev, sc->sc_dci.sc_irq_res,
+ sc->sc_dci.sc_intr_hdl);
+ sc->sc_dci.sc_intr_hdl = NULL;
+ }
+ if (sc->sc_dci.sc_irq_res) {
+ bus_release_resource(dev, SYS_RES_IRQ, 0,
+ sc->sc_dci.sc_irq_res);
+ sc->sc_dci.sc_irq_res = NULL;
+ }
+ usb_bus_mem_free_all(&sc->sc_dci.sc_bus, NULL);
+
+ return (0);
+}
+
+static int
+octusb_octeon_shutdown(device_t dev)
+{
+ struct octusb_octeon_softc *sc = device_get_softc(dev);
+ int err;
+
+ err = bus_generic_shutdown(dev);
+ if (err)
+ return (err);
+
+ octusb_uninit(&sc->sc_dci);
+
+ return (0);
+}
+
+static device_method_t octusb_octeon_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_identify, octusb_octeon_identify),
+ DEVMETHOD(device_probe, octusb_octeon_probe),
+ DEVMETHOD(device_attach, octusb_octeon_attach),
+ DEVMETHOD(device_detach, octusb_octeon_detach),
+ DEVMETHOD(device_shutdown, octusb_octeon_shutdown),
+
+ /* Bus interface */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+
+ {0, 0}
+};
+
+static driver_t octusb_octeon_driver = {
+ "octusb",
+ octusb_octeon_methods,
+ sizeof(struct octusb_octeon_softc),
+};
+
+static devclass_t octusb_octeon_devclass;
+
+DRIVER_MODULE(octusb, ciu, octusb_octeon_driver, octusb_octeon_devclass, 0, 0);