summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/mips/cavium/asm_octeon.S143
-rw-r--r--sys/mips/cavium/dev/rgmii/octeon_fau.c42
-rw-r--r--sys/mips/cavium/dev/rgmii/octeon_fau.h185
-rw-r--r--sys/mips/cavium/dev/rgmii/octeon_fpa.c187
-rw-r--r--sys/mips/cavium/dev/rgmii/octeon_fpa.h219
-rw-r--r--sys/mips/cavium/dev/rgmii/octeon_ipd.c107
-rw-r--r--sys/mips/cavium/dev/rgmii/octeon_ipd.h164
-rw-r--r--sys/mips/cavium/dev/rgmii/octeon_pip.h179
-rw-r--r--sys/mips/cavium/dev/rgmii/octeon_pko.c337
-rw-r--r--sys/mips/cavium/dev/rgmii/octeon_pko.h292
-rw-r--r--sys/mips/cavium/dev/rgmii/octeon_rgmx.c2222
-rw-r--r--sys/mips/cavium/dev/rgmii/octeon_rgmx.h590
-rw-r--r--sys/mips/cavium/driveid.h259
-rw-r--r--sys/mips/cavium/files.octeon117
-rw-r--r--sys/mips/cavium/obio.c182
-rw-r--r--sys/mips/cavium/obiovar.h58
-rw-r--r--sys/mips/cavium/octeon_ebt3000_cf.c621
-rw-r--r--sys/mips/cavium/octeon_ebt3000_cf.h35
-rw-r--r--sys/mips/cavium/octeon_machdep.c965
-rw-r--r--sys/mips/cavium/octeon_pcmap_regs.h1080
-rw-r--r--sys/mips/cavium/octeonreg.h247
-rw-r--r--sys/mips/cavium/std.octeon122
-rw-r--r--sys/mips/cavium/uart_bus_octeonusart.c121
-rw-r--r--sys/mips/cavium/uart_cpu_octeonusart.c191
-rw-r--r--sys/mips/cavium/uart_dev_oct16550.c827
25 files changed, 9292 insertions, 0 deletions
diff --git a/sys/mips/cavium/asm_octeon.S b/sys/mips/cavium/asm_octeon.S
new file mode 100644
index 0000000..e4df30e
--- /dev/null
+++ b/sys/mips/cavium/asm_octeon.S
@@ -0,0 +1,143 @@
+
+#include <machine/asm.h>
+#include <machine/cache_r4k.h>
+#include <machine/cpuregs.h>
+#include <machine/param.h>
+#include <machine/pte.h>
+
+#include "assym.s"
+
+
+
+#define CPU_DISABLE_INTERRUPTS(reg, reg2, reg3) \
+ mfc0 reg, MIPS_COP_0_STATUS; \
+ nop; \
+ move reg3, reg; \
+ li reg2, ~MIPS_SR_INT_IE; \
+ and reg, reg2, reg; \
+ mtc0 reg, MIPS_COP_0_STATUS; \
+ COP0_SYNC
+
+
+
+#define CPU_ENABLE_INTERRUPTS(reg, reg3) \
+ mfc0 reg, MIPS_COP_0_STATUS; \
+ nop; \
+ or reg, reg, reg3; \
+ mtc0 reg, MIPS_COP_0_STATUS; \
+ COP0_SYNC
+
+
+#define PUSHR(reg) \
+ addiu sp,sp,-16 ; \
+ sd reg, 8(sp) ; \
+ nop ;
+
+#define POPR(reg) \
+ ld reg, 8(sp) ; \
+ addiu sp,sp,16 ; \
+ nop ;
+
+
+
+
+/*
+ * octeon_ciu_get_interrupt_reg_addr
+ *
+ * Given Int-X, En-X combination, return the CIU Interrupt Enable Register addr
+ * a0 = ciu Int-X: 0/1
+ * a1 = ciu EN-0: 0/1
+ */
+LEAF(octeon_ciu_get_interrupt_reg_addr)
+ .set noreorder
+ .set mips3
+
+ beqz a0, ciu_get_interrupt_reg_addr_Int_0
+ nop
+
+ciu_get_interrupt_reg_addr_Int_1:
+ beqz a1, ciu_get_interrupt_reg_addr_Int_1_En_0
+ nop
+
+ciu_get_interrupt_reg_addr_Int_1_En1:
+ li a0, OCTEON_CIU_ADDR_HI
+ dsll32 a0, a0, 0
+ nop
+ ori a0, OCTEON_CIU_EN1_INT1_LO
+ j ciu_get_interrupt_reg_addr_ret
+ nop
+
+ciu_get_interrupt_reg_addr_Int_1_En_0:
+ li a0, OCTEON_CIU_ADDR_HI
+ dsll32 a0, a0, 0
+ nop
+ ori a0, OCTEON_CIU_EN0_INT1_LO
+ j ciu_get_interrupt_reg_addr_ret
+ nop
+
+ciu_get_interrupt_reg_addr_Int_0:
+ beqz a1, ciu_get_interrupt_reg_addr_Int_0_En_0
+ nop
+
+ciu_get_interrupt_reg_addr_Int_0_En_1:
+ li a0, OCTEON_CIU_ADDR_HI
+ dsll32 a0, a0, 0
+ nop
+ ori a0, OCTEON_CIU_EN1_INT0_LO
+ j ciu_get_interrupt_reg_addr_ret
+ nop
+
+ciu_get_interrupt_reg_addr_Int_0_En_0:
+ li a0, OCTEON_CIU_ADDR_HI
+ dsll32 a0, a0, 0
+ nop
+ ori a0, OCTEON_CIU_EN0_INT0_LO
+
+
+ciu_get_interrupt_reg_addr_ret:
+ j ra
+ nop
+
+ .set mips0
+ .set reorder
+END(octeon_ciu_get_interrupt_reg_addr)
+
+
+
+/*
+ * octeon_ciu_mask_all_interrupts
+ *
+ * a0 = ciu Interrupt-X: 0/1
+ * a1 = ciu Enable-X: 0/1
+ */
+LEAF(octeon_ciu_mask_all_interrupts)
+ .set noreorder
+ .set mips3
+
+ PUSHR(ra)
+ PUSHR(s0)
+
+ move t0, a0
+ move t1, a1
+ li a0, MIPS_SR_INT_IE
+ CPU_DISABLE_INTERRUPTS(a2, a1, s0)
+ move a0, t0
+ move t1, a1
+ jal octeon_ciu_get_interrupt_reg_addr
+ nop
+ ld a2, 0(a0) # Dummy read
+ nop
+ move a2, zero # Clear all
+ sd a2, 0(a0) # Write new Enable bits
+ nop
+ CPU_ENABLE_INTERRUPTS(a2, s0)
+
+ POPR(s0)
+ POPR(ra)
+ j ra # Return
+ nop # (bd slot)
+
+ .set mips0
+ .set reorder
+END(octeon_ciu_mask_all_interrupts)
+
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 0000000..849ffd4
--- /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 0000000..fc87395
--- /dev/null
+++ b/sys/mips/cavium/dev/rgmii/octeon_fau.h
@@ -0,0 +1,185 @@
+/*------------------------------------------------------------------
+ * 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);
+}
+
+
+extern void octeon_fau_init(void);
+extern void octeon_fau_enable(void);
+extern void octeon_fau_disable(void);
+
+
+#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 0000000..b5d8f7c
--- /dev/null
+++ b/sys/mips/cavium/dev/rgmii/octeon_fpa.c
@@ -0,0 +1,187 @@
+/*------------------------------------------------------------------
+ * octeon_fpa.c Free Pool Allocator
+ *
+ *------------------------------------------------------------------
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+
+#include <mips/octeon1/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 0x%X 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: 0x%X = %p\n",
+ pool, elem_num, elem_size_bytes, block_size, memory, (void *)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: 0x%X Phys 0x%X Bytes %u\n", block, memory, 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 0000000..174ff73
--- /dev/null
+++ b/sys/mips/cavium/dev/rgmii/octeon_fpa.h
@@ -0,0 +1,219 @@
+/*------------------------------------------------------------------
+ * 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 0000000..069b537
--- /dev/null
+++ b/sys/mips/cavium/dev/rgmii/octeon_ipd.c
@@ -0,0 +1,107 @@
+/*------------------------------------------------------------------
+ * octeon_ipd.c Input Packet Unit
+ *
+ *------------------------------------------------------------------
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+
+#include <mips/octeon1/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 0000000..21e26c8
--- /dev/null
+++ b/sys/mips/cavium/dev/rgmii/octeon_ipd.h
@@ -0,0 +1,164 @@
+/*------------------------------------------------------------------
+ * 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 0000000..f474e5e
--- /dev/null
+++ b/sys/mips/cavium/dev/rgmii/octeon_pip.h
@@ -0,0 +1,179 @@
+/*
+ * 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 0000000..cfd8cd4
--- /dev/null
+++ b/sys/mips/cavium/dev/rgmii/octeon_pko.c
@@ -0,0 +1,337 @@
+/*------------------------------------------------------------------
+ * octeon_pko.c Packet Output Unit
+ *
+ *------------------------------------------------------------------
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <mips/octeon1/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 0000000..4ecc3b9
--- /dev/null
+++ b/sys/mips/cavium/dev/rgmii/octeon_pko.h
@@ -0,0 +1,292 @@
+/*------------------------------------------------------------------
+ * 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 0000000..af4aba3
--- /dev/null
+++ b/sys/mips/cavium/dev/rgmii/octeon_rgmx.c
@@ -0,0 +1,2222 @@
+/*
+ * 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/octeon1/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];
+
+ char const * typestr; /* printable name of the interface. */
+ 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_mark_ready(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 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 *);
+#if 0
+static void octeon_rgmx_watchdog (struct ifnet *);
+#endif
+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;
+ /* Watchdog interface is now deprecated.
+ ifp->if_watchdog = octeon_rgmx_watchdog;
+ */
+ 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);
+ /* Print additional info when attached. */
+ device_printf(sc->sc_dev, "type %s, full duplex\n", sc->typestr);
+
+ 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);
+ }
+/*
+ * Don't call octeon_rgmx_mark_ready()
+ * ifnet will call it indirectly via octeon_rgmx_init()
+ *
+ * octeon_rgmx_mark_ready(sc);
+ */
+ 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.
+ */
+ void *pko_cmd_buf = octeon_fpa_alloc(OCTEON_FPA_TX_CMDBUF_POOL);
+ uint64_t phys_cmd_buf;
+
+ if (!pko_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);
+ }
+ phys_cmd_buf = OCTEON_PTR2PHYS(pko_cmd_buf);
+
+ 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;
+ sc->ifp->if_timer = 0;
+}
+
+
+#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
+
+ 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(OCTEON_CORE_ID, 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->ifp->if_timer = 0;
+ 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)
+{
+ /*
+ * No support for Media Status callback
+ */
+}
+
+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) {
+ printf(" SIOCSTIFFLAGS UP/Not-running\n"); break;
+ octeon_rgmx_init(sc);
+ } else {
+ printf(" SIOCSTIFFLAGS UP/Running\n"); break;
+ }
+ } 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) {
+ printf(" SIOCSTIFFLAGS Down/Running\n"); break;
+ octeon_rgmx_stop(sc);
+ } else {
+ printf(" SIOCSTIFFLAGS Down/Not-Running\n"); break;
+ }
+ }
+ 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);
+}
+
+
+
+
+/*
+ * octeon_rgmx_mark_ready
+ *
+ * Initialize the rgmx driver for this instance
+ * Initialize device.
+ */
+static void octeon_rgmx_mark_ready (struct rgmx_softc_dev *sc)
+{
+
+ /* 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 */
+}
+
+
+static void octeon_rgmx_init (void *xsc)
+{
+
+ /*
+ * Called mostly from ifnet interface ifp->if_init();
+ * I think we can anchor most of our iniialization here and
+ * not do it in different places from driver_attach().
+ */
+ /*
+ * For now, we only mark the interface ready
+ */
+ octeon_rgmx_mark_ready((struct rgmx_softc_dev *) xsc);
+}
+
+
+
+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));
+
+ /*
+ * Compre to prev known state. If same then nothing to do.
+ */
+ if (link_status.word64 == sc->link_status) {
+ return;
+ }
+
+ RGMX_LOCK(sc);
+
+ old_link_status.word64 = sc->link_status;
+
+ sc->link_status = link_status.word64;
+
+ last_enabled = octeon_rgmx_stop_port(port);
+
+ gmx_cfg.word64 = oct_read64(OCTEON_RGMX_PRTX_CFG(index, iface));
+
+ /*
+ * 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);
+
+ if (link_status.bits.status != old_link_status.bits.status) {
+
+//#define DEBUG_LINESTATUS
+ if (link_status.bits.status) {
+#ifdef DEBUG_LINESTATUS
+ printf(" %u/%u: Interface is now alive\n", iface, port);
+#endif
+ if (report_link) if_link_state_change(sc->ifp, LINK_STATE_UP);
+ } else {
+#ifdef DEBUG_LINESTATUS
+ printf(" %u/%u: Interface went down\n", iface, port);
+#endif
+ if (report_link) if_link_state_change(sc->ifp, LINK_STATE_DOWN);
+ }
+ }
+ 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() & 0xffffff00;
+
+ 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 0000000..e6d8e76
--- /dev/null
+++ b/sys/mips/cavium/dev/rgmii/octeon_rgmx.h
@@ -0,0 +1,590 @@
+/*------------------------------------------------------------------
+ * 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 0000000..b7befea
--- /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 0000000..190d3b8
--- /dev/null
+++ b/sys/mips/cavium/files.octeon1
@@ -0,0 +1,17 @@
+# $FreeBSD$
+# Octeon Support Files
+#
+mips/mips/mp_machdep.c optional smp
+mips/octeon1/dev/rgmii/octeon_fau.c optional rgmii
+mips/octeon1/dev/rgmii/octeon_fpa.c optional rgmii
+mips/octeon1/dev/rgmii/octeon_ipd.c optional rgmii
+mips/octeon1/dev/rgmii/octeon_pko.c optional rgmii
+mips/octeon1/dev/rgmii/octeon_rgmx.c optional rgmii
+mips/octeon1/obio.c optional uart
+mips/octeon1/octeon_ebt3000_cf.c optional cf
+mips/octeon1/octeon_machdep.c standard
+mips/octeon1/uart_bus_octeonusart.c optional uart
+mips/octeon1/uart_cpu_octeonusart.c optional uart
+mips/octeon1/uart_dev_oct16550.c optional uart
+mips/mips/intr_machdep.c standard
+mips/mips/tick.c standard
diff --git a/sys/mips/cavium/obio.c b/sys/mips/cavium/obio.c
new file mode 100644
index 0000000..de3910b
--- /dev/null
+++ b/sys/mips/cavium/obio.c
@@ -0,0 +1,182 @@
+/* $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 Intel IQ80321
+ * evaluation boards.
+ */
+
+#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/octeon1/octeonreg.h>
+#include <mips/octeon1/obiovar.h>
+
+int obio_probe(device_t);
+int obio_attach(device_t);
+
+/*
+ * We need only one obio. Any other device hanging off of it,
+ * shouldn't cause multiple of these to be found.
+ */
+static int have_one = 0;
+
+int
+obio_probe(device_t dev)
+{
+ if (!have_one) {
+ have_one = 1;
+ return 0;
+ }
+ return (ENXIO);
+}
+
+int
+obio_attach(device_t dev)
+{
+ struct obio_softc *sc = device_get_softc(dev);
+
+ sc->oba_st = mips_bus_space_generic;
+ sc->oba_addr = OCTEON_UART0ADR;
+ 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
+ * it's IRQ is 0 corresponding to IP2.
+ */
+ if (rman_init(&sc->oba_irq_rman) != 0 ||
+ rman_manage_region(&sc->oba_irq_rman, 0, 0) != 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:
+ rm = &sc->oba_irq_rman;
+ break;
+ case SYS_RES_MEMORY:
+ return (NULL);
+ case SYS_RES_IOPORT:
+ rm = &sc->oba_rman;
+ bt = sc->oba_st;
+ bh = device_get_unit(child) ? OCTEON_UART1ADR : OCTEON_UART0ADR;
+ 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[] = {
+ DEVMETHOD(device_probe, obio_probe),
+ DEVMETHOD(device_attach, obio_attach),
+
+ 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),
+
+ {0, 0},
+};
+
+static driver_t obio_driver = {
+ "obio",
+ obio_methods,
+ sizeof(struct obio_softc),
+};
+static devclass_t obio_devclass;
+
+DRIVER_MODULE(obio, nexus, obio_driver, obio_devclass, 0, 0);
diff --git a/sys/mips/cavium/obiovar.h b/sys/mips/cavium/obiovar.h
new file mode 100644
index 0000000..ab8b6b2
--- /dev/null
+++ b/sys/mips/cavium/obiovar.h
@@ -0,0 +1,58 @@
+/* $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 */
+ int oba_width; /* bus width */
+ int oba_irq; /* XINT interrupt bit # */
+ struct rman oba_rman;
+ struct rman oba_irq_rman;
+
+};
+extern struct bus_space obio_bs_tag;
+
+#endif /* _OCTEON_OBIOVAR_H_ */
diff --git a/sys/mips/cavium/octeon_ebt3000_cf.c b/sys/mips/cavium/octeon_ebt3000_cf.c
new file mode 100644
index 0000000..babeece
--- /dev/null
+++ b/sys/mips/cavium/octeon_ebt3000_cf.c
@@ -0,0 +1,621 @@
+/*
+ * 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/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 "octeon_ebt3000_cf.h"
+#include "driveid.h"
+#include <mips/octeon1/octeon_pcmap_regs.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_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 SWAP_LONG(x) (((x << 24) & 0xFF000000) | ((x << 8) & 0x00FF0000) | \
+ ((x >> 8) & 0x0000FF00) | ((x << 24) & 0x000000FF) )
+#define MODEL_STR_SIZE 40
+
+
+/* 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 hd_driveid 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,
+};
+
+/* 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 void cf_cmd_identify(void);
+static void cf_cmd_write(uint32_t, uint32_t, void *);
+static void cf_cmd_read(uint32_t, uint32_t, void *);
+static void cf_wait_busy(void);
+static void 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)
+{
+ /*
+ * 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) {
+ 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) {
+ cf_cmd_write(bp->bio_length / drive_param.sector_size,
+ bp->bio_offset/drive_param.sector_size, bp->bio_data);
+ }
+
+ 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 void 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;
+
+//#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--) {
+
+ cf_send_cmd(lba, CMD_READ_SECTOR);
+
+ 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
+}
+
+
+/* ------------------------------------------------------------------- *
+ * cf_cmd_write() *
+ * ------------------------------------------------------------------- *
+ *
+ * Write nr_sectors to the device starting from start_sector.
+ */
+static void 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;
+
+//#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--) {
+
+ cf_send_cmd(lba, CMD_WRITE_SECTOR);
+
+ 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
+}
+
+
+/* ------------------------------------------------------------------- *
+ * cf_cmd_identify() *
+ * ------------------------------------------------------------------- *
+ *
+ * Read parameters and other information from the drive and store
+ * it in the drive_param structure
+ *
+ */
+static void cf_cmd_identify (void)
+{
+ int count;
+ 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] = 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;
+
+ cf_wait_busy();
+
+ 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 */
+
+ cf_wait_busy();
+
+ 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;
+ }
+ }
+
+ 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.cur_heads);
+ drive_param.tracks = SWAP_SHORT (drive_param.u.driveid.cur_cyls);
+ drive_param.sec_track = SWAP_SHORT (drive_param.u.driveid.cur_sectors);
+ drive_param.nr_sectors = SWAP_LONG (drive_param.u.driveid.lba_capacity);
+
+}
+
+
+/* ------------------------------------------------------------------- *
+ * cf_send_cmd() *
+ * ------------------------------------------------------------------- *
+ *
+ * Send command to read/write one sector specified by lba.
+ *
+ */
+static void 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);
+
+ }
+
+ 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 and DRQ bits of the status register. When the controller
+ * is ready for data transfer, it clears the BSY bit and sets the
+ * DRQ bit.
+ *
+ */
+static void 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 || (status & STATUS_DRQ) != STATUS_DRQ ) {
+ 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 || (status & STATUS_DRQ) != STATUS_DRQ ) {
+ DELAY(WAIT_DELAY);
+ status = (uint8_t)(task_file[TF_STATUS/2]>>8);
+ }
+ }
+
+#ifdef OCTEON_VISUAL_CF_2
+ octeon_led_write_char(2, ' ');
+#endif
+}
+
+/* ------------------------------------------------------------------- *
+ * 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_board_real()) return 1;
+
+ if (device_get_unit(dev) != 0) {
+ panic("can't attach more devices\n");
+ }
+
+ device_set_desc(dev, "Octeon Compact Flash Driver");
+
+ cf_cmd_identify();
+
+ return (0);
+}
+
+/* ------------------------------------------------------------------- *
+ * 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;
+ octeon_mio_boot_reg_cfgx_t cfg;
+
+
+ if (!octeon_board_real())
+ return;
+
+ base_addr = (void *) MIPS_PHYS_TO_KSEG0(OCTEON_CF_COMMON_BASE_ADDR);
+
+ for (bus_region = 0; bus_region < 8; bus_region++)
+ {
+ cfg.word64 = oct_read64(OCTEON_MIO_BOOT_REG_CFGX(bus_region));
+ if (cfg.bits.base == OCTEON_CF_COMMON_BASE_ADDR >> 16)
+ {
+ bus_width = (cfg.bits.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_board_real()) 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 0000000..1e99512
--- /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 0000000..adec47b
--- /dev/null
+++ b/sys/mips/cavium/octeon_machdep.c
@@ -0,0 +1,965 @@
+/*-
+ * 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/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/octeon1/octeon_pcmap_regs.h>
+#include <mips/octeon1/octeonreg.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>
+
+#if defined(__mips_n64)
+#define MAX_APP_DESC_ADDR 0xffffffffafffffff
+#else
+#define MAX_APP_DESC_ADDR 0xafffffff
+#endif
+
+extern int *edata;
+extern int *end;
+
+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 void octeon_boot_params_init(register_t ptr);
+static uint64_t ciu_get_intr_sum_reg_addr(int core_num, int intx, int enx);
+static uint64_t ciu_get_intr_en_reg_addr(int core_num, int intx, int enx);
+
+void
+platform_cpu_init()
+{
+ /* Nothing special yet */
+}
+
+/*
+ * Perform a board-level soft-reset.
+ */
+void
+platform_reset(void)
+{
+ ((void(*)(void))0x1fc00000)(); /* Jump to this hex address */
+}
+
+
+static inline uint32_t
+octeon_disable_interrupts(void)
+{
+ uint32_t status_bits;
+
+ status_bits = mips_rd_status();
+ mips_wr_status(status_bits & ~MIPS_SR_INT_IE);
+ return (status_bits);
+}
+
+
+static inline void
+octeon_set_interrupts(uint32_t status_bits)
+{
+ mips_wr_status(status_bits);
+}
+
+
+void
+octeon_led_write_char(int char_position, char val)
+{
+ uint64_t ptr = (OCTEON_CHAR_LED_BASE_ADDR | 0xf8);
+
+ if (!octeon_board_real())
+ 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_board_real())
+ 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_board_real())
+ 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_board_real())
+ return;
+
+ for (i=0; i<8; i++, ptr++) {
+ if (str && *str)
+ oct_write8_x8(ptr, *str++);
+ else
+ oct_write8_x8(ptr, ' ');
+ oct_read64(OCTEON_MIO_BOOT_BIST_STAT);
+ }
+}
+
+static char progress[8] = { '-', '/', '|', '\\', '-', '/', '|', '\\'};
+
+void
+octeon_led_run_wheel(int *prog_count, int led_position)
+{
+ if (!octeon_board_real())
+ return;
+ octeon_led_write_char(led_position, progress[*prog_count]);
+ *prog_count += 1;
+ *prog_count &= 0x7;
+}
+
+#define LSR_DATAREADY 0x01 /* Data ready */
+#define LSR_THRE 0x20 /* Transmit holding register empty */
+#define LSR_TEMT 0x40 /* Transmitter Empty. THR, TSR & FIFO */
+#define USR_TXFIFO_NOTFULL 0x02 /* Uart TX FIFO Not full */
+
+/*
+ * octeon_uart_write_byte
+ *
+ * Put out a single byte off of uart port.
+ */
+
+void
+octeon_uart_write_byte(int uart_index, uint8_t ch)
+{
+ uint64_t val, val2;
+ if (uart_index < 0 || uart_index > 1)
+ return;
+
+ while (1) {
+ val = oct_read64(OCTEON_MIO_UART0_LSR + (uart_index * 0x400));
+ val2 = oct_read64(OCTEON_MIO_UART0_USR + (uart_index * 0x400));
+ if ((((uint8_t) val) & LSR_THRE) ||
+ (((uint8_t) val2) & USR_TXFIFO_NOTFULL)) {
+ break;
+ }
+ }
+
+ /* Write the byte */
+ oct_write8(OCTEON_MIO_UART0_THR + (uart_index * 0x400), (uint64_t) ch);
+
+ /* Force Flush the IOBus */
+ oct_read64(OCTEON_MIO_BOOT_BIST_STAT);
+}
+
+
+void
+octeon_uart_write_byte0(uint8_t ch)
+{
+ uint64_t val, val2;
+
+ while (1) {
+ val = oct_read64(OCTEON_MIO_UART0_LSR);
+ val2 = oct_read64(OCTEON_MIO_UART0_USR);
+ if ((((uint8_t) val) & LSR_THRE) ||
+ (((uint8_t) val2) & USR_TXFIFO_NOTFULL)) {
+ break;
+ }
+ }
+
+ /* Write the byte */
+ oct_write8(OCTEON_MIO_UART0_THR, (uint64_t) ch);
+
+ /* Force Flush the IOBus */
+ oct_read64(OCTEON_MIO_BOOT_BIST_STAT);
+}
+
+/*
+ * octeon_uart_write_string
+ *
+ */
+void
+octeon_uart_write_string(int uart_index, const char *str)
+{
+ /* Just loop writing one byte at a time */
+
+ while (*str) {
+ octeon_uart_write_byte(uart_index, *str);
+ if (*str == '\n') {
+ octeon_uart_write_byte(uart_index, '\r');
+ }
+ str++;
+ }
+}
+
+static char wstr[30];
+
+void
+octeon_led_write_hex(uint32_t wl)
+{
+ char nbuf[80];
+
+ sprintf(nbuf, "%X", wl);
+ octeon_led_write_string(nbuf);
+}
+
+
+void octeon_uart_write_hex2(uint32_t wl, uint32_t wh)
+{
+ sprintf(wstr, "0x%X-0x%X ", wh, wl);
+ octeon_uart_write_string(0, wstr);
+}
+
+void
+octeon_uart_write_hex(uint32_t wl)
+{
+ sprintf(wstr, " 0x%X ", wl);
+ octeon_uart_write_string(0, wstr);
+}
+
+/*
+ * octeon_wait_uart_flush
+ */
+void
+octeon_wait_uart_flush(int uart_index, uint8_t ch)
+{
+ uint64_t val;
+ int64_t val3;
+ uint32_t cpu_status_bits;
+
+ if (uart_index < 0 || uart_index > 1)
+ return;
+
+ cpu_status_bits = octeon_disable_interrupts();
+ /* Force Flush the IOBus */
+ oct_read64(OCTEON_MIO_BOOT_BIST_STAT);
+ for (val3 = 0xfffffffff; val3 > 0; val3--) {
+ val = oct_read64(OCTEON_MIO_UART0_LSR + (uart_index * 0x400));
+ if (((uint8_t) val) & LSR_TEMT)
+ break;
+ }
+ octeon_set_interrupts(cpu_status_bits);
+}
+
+
+/*
+ * octeon_debug_symbol
+ *
+ * Does nothing.
+ * Used to mark the point for simulator to begin tracing
+ */
+void
+octeon_debug_symbol(void)
+{
+}
+
+void
+octeon_ciu_stop_gtimer(int timer)
+{
+ oct_write64(OCTEON_CIU_GENTIMER_ADDR(timer), 0ll);
+}
+
+void
+octeon_ciu_start_gtimer(int timer, u_int one_shot, uint64_t time_cycles)
+{
+ octeon_ciu_gentimer gentimer;
+
+ gentimer.word64 = 0;
+ gentimer.bits.one_shot = one_shot;
+ gentimer.bits.len = time_cycles - 1;
+ oct_write64(OCTEON_CIU_GENTIMER_ADDR(timer), gentimer.word64);
+}
+
+/*
+ * octeon_ciu_reset
+ *
+ * Shutdown all CIU to IP2, IP3 mappings
+ */
+void
+octeon_ciu_reset(void)
+{
+
+ octeon_ciu_stop_gtimer(CIU_GENTIMER_NUM_0);
+ octeon_ciu_stop_gtimer(CIU_GENTIMER_NUM_1);
+ octeon_ciu_stop_gtimer(CIU_GENTIMER_NUM_2);
+ octeon_ciu_stop_gtimer(CIU_GENTIMER_NUM_3);
+
+ ciu_disable_intr(CIU_THIS_CORE, CIU_INT_0, CIU_EN_0);
+ ciu_disable_intr(CIU_THIS_CORE, CIU_INT_0, CIU_EN_1);
+ ciu_disable_intr(CIU_THIS_CORE, CIU_INT_1, CIU_EN_0);
+ ciu_disable_intr(CIU_THIS_CORE, CIU_INT_1, CIU_EN_1);
+
+ ciu_clear_int_summary(CIU_THIS_CORE, CIU_INT_0, CIU_EN_0, 0ll);
+ ciu_clear_int_summary(CIU_THIS_CORE, CIU_INT_1, CIU_EN_0, 0ll);
+ ciu_clear_int_summary(CIU_THIS_CORE, CIU_INT_1, CIU_EN_1, 0ll);
+}
+
+/*
+ * mips_disable_interrupt_controllers
+ *
+ * Disable interrupts in the CPU controller
+ */
+void
+mips_disable_interrupt_controls(void)
+{
+ /*
+ * Disable interrupts in CIU.
+ */
+ octeon_ciu_reset();
+}
+
+/*
+ * ciu_get_intr_sum_reg_addr
+ */
+static uint64_t
+ciu_get_intr_sum_reg_addr(int core_num, int intx, int enx)
+{
+ uint64_t ciu_intr_sum_reg_addr;
+
+ if (enx == CIU_EN_0)
+ ciu_intr_sum_reg_addr = OCTEON_CIU_SUMMARY_BASE_ADDR +
+ (core_num * 0x10) + (intx * 0x8);
+ else
+ ciu_intr_sum_reg_addr = OCTEON_CIU_SUMMARY_INT1_ADDR;
+
+ return (ciu_intr_sum_reg_addr);
+}
+
+
+/*
+ * ciu_get_intr_en_reg_addr
+ */
+static uint64_t
+ciu_get_intr_en_reg_addr(int core_num, int intx, int enx)
+{
+ uint64_t ciu_intr_reg_addr;
+
+ ciu_intr_reg_addr = OCTEON_CIU_ENABLE_BASE_ADDR +
+ ((enx == 0) ? 0x0 : 0x8) + (intx * 0x10) + (core_num * 0x20);
+ return (ciu_intr_reg_addr);
+}
+
+
+
+
+/*
+ * ciu_get_intr_reg_addr
+ *
+ * 200 ---int0,en0 ip2
+ * 208 ---int0,en1 ip2 ----> this is wrong... this is watchdog
+ *
+ * 210 ---int0,en0 ip3 --
+ * 218 ---int0,en1 ip3 ----> same here.. .this is watchdog... right?
+ *
+ * 220 ---int1,en0 ip2
+ * 228 ---int1,en1 ip2
+ * 230 ---int1,en0 ip3 --
+ * 238 ---int1,en1 ip3
+ *
+ */
+uint64_t
+ciu_get_en_reg_addr_new(int corenum, int intx, int enx, int ciu_ip)
+{
+ uint64_t ciu_intr_reg_addr = OCTEON_CIU_ENABLE_BASE_ADDR;
+
+ /* XXX kasserts? */
+ if (enx < CIU_EN_0 || enx > CIU_EN_1) {
+ printf("%s: invalid enx value %d, should be %d or %d\n",
+ __FUNCTION__, enx, CIU_EN_0, CIU_EN_1);
+ return 0;
+ }
+ if (intx < CIU_INT_0 || intx > CIU_INT_1) {
+ printf("%s: invalid intx value %d, should be %d or %d\n",
+ __FUNCTION__, enx, CIU_INT_0, CIU_INT_1);
+ return 0;
+ }
+ if (ciu_ip < CIU_MIPS_IP2 || ciu_ip > CIU_MIPS_IP3) {
+ printf("%s: invalid ciu_ip value %d, should be %d or %d\n",
+ __FUNCTION__, ciu_ip, CIU_MIPS_IP2, CIU_MIPS_IP3);
+ return 0;
+ }
+
+ ciu_intr_reg_addr += (enx * 0x8);
+ ciu_intr_reg_addr += (ciu_ip * 0x10);
+ ciu_intr_reg_addr += (intx * 0x20);
+ return (ciu_intr_reg_addr);
+}
+
+/*
+ * ciu_get_int_summary
+ */
+uint64_t
+ciu_get_int_summary(int core_num, int intx, int enx)
+{
+ uint64_t ciu_intr_sum_reg_addr;
+
+ if (core_num == CIU_THIS_CORE)
+ core_num = octeon_get_core_num();
+ ciu_intr_sum_reg_addr = ciu_get_intr_sum_reg_addr(core_num, intx, enx);
+ return (oct_read64(ciu_intr_sum_reg_addr));
+}
+
+//#define DEBUG_CIU 1
+
+#ifdef DEBUG_CIU
+#define DEBUG_CIU_SUM 1
+#define DEBUG_CIU_EN 1
+#endif
+
+
+/*
+ * ciu_clear_int_summary
+ */
+void
+ciu_clear_int_summary(int core_num, int intx, int enx, uint64_t write_bits)
+{
+ uint32_t cpu_status_bits;
+ uint64_t ciu_intr_sum_reg_addr;
+
+//#define DEBUG_CIU_SUM 1
+
+#ifdef DEBUG_CIU_SUM
+ uint64_t ciu_intr_sum_bits;
+#endif
+
+
+ if (core_num == CIU_THIS_CORE) {
+ core_num = octeon_get_core_num();
+ }
+
+#ifdef DEBUG_CIU_SUM
+ printf(" CIU: core %u clear sum IntX %u Enx %u Bits: 0x%llX\n",
+ core_num, intx, enx, write_bits);
+#endif
+
+ cpu_status_bits = octeon_disable_interrupts();
+
+ ciu_intr_sum_reg_addr = ciu_get_intr_sum_reg_addr(core_num, intx, enx);
+
+#ifdef DEBUG_CIU_SUM
+ ciu_intr_sum_bits = oct_read64(ciu_intr_sum_reg_addr); /* unneeded dummy read */
+ printf(" CIU: status: 0x%X reg_addr: 0x%llX Val: 0x%llX -> 0x%llX",
+ cpu_status_bits, ciu_intr_sum_reg_addr, ciu_intr_sum_bits,
+ ciu_intr_sum_bits | write_bits);
+#endif
+
+ oct_write64(ciu_intr_sum_reg_addr, write_bits);
+ oct_read64(OCTEON_MIO_BOOT_BIST_STAT); /* Bus Barrier */
+
+#ifdef DEBUG_CIU_SUM
+ printf(" Readback: 0x%llX\n\n ", (uint64_t) oct_read64(ciu_intr_sum_reg_addr));
+#endif
+
+ octeon_set_interrupts(cpu_status_bits);
+}
+
+/*
+ * ciu_disable_intr
+ */
+void
+ciu_disable_intr(int core_num, int intx, int enx)
+{
+ uint32_t cpu_status_bits;
+ uint64_t ciu_intr_reg_addr;
+
+ if (core_num == CIU_THIS_CORE)
+ core_num = octeon_get_core_num();
+
+ cpu_status_bits = octeon_disable_interrupts();
+
+ ciu_intr_reg_addr = ciu_get_intr_en_reg_addr(core_num, intx, enx);
+
+ oct_read64(ciu_intr_reg_addr); /* Dummy read */
+
+ oct_write64(ciu_intr_reg_addr, 0LL);
+ oct_read64(OCTEON_MIO_BOOT_BIST_STAT); /* Bus Barrier */
+
+ octeon_set_interrupts(cpu_status_bits);
+}
+
+void
+ciu_dump_interrutps_enabled(int core_num, int intx, int enx, int ciu_ip)
+{
+
+ uint64_t ciu_intr_reg_addr;
+ uint64_t ciu_intr_bits;
+
+ if (core_num == CIU_THIS_CORE) {
+ core_num = octeon_get_core_num();
+ }
+
+#ifndef OCTEON_SMP_1
+ ciu_intr_reg_addr = ciu_get_intr_en_reg_addr(core_num, intx, enx);
+#else
+ ciu_intr_reg_addr = ciu_get_en_reg_addr_new(core_num, intx, enx, ciu_ip);
+#endif
+
+ if (!ciu_intr_reg_addr) {
+ printf("Bad call to %s\n", __FUNCTION__);
+ while(1);
+ return;
+ }
+
+ ciu_intr_bits = oct_read64(ciu_intr_reg_addr);
+ printf(" CIU core %d int: %d en: %d ip: %d Add: %#llx enabled: %#llx SR: %x\n",
+ core_num, intx, enx, ciu_ip, (unsigned long long)ciu_intr_reg_addr,
+ (unsigned long long)ciu_intr_bits, mips_rd_status());
+}
+
+
+/*
+ * ciu_enable_interrupts
+ */
+void ciu_enable_interrupts(int core_num, int intx, int enx,
+ uint64_t set_these_interrupt_bits, int ciu_ip)
+{
+ uint32_t cpu_status_bits;
+ uint64_t ciu_intr_reg_addr;
+ uint64_t ciu_intr_bits;
+
+ if (core_num == CIU_THIS_CORE)
+ core_num = octeon_get_core_num();
+
+//#define DEBUG_CIU_EN 1
+
+#ifdef DEBUG_CIU_EN
+ printf(" CIU: core %u enabling Intx %u Enx %u IP %d Bits: 0x%llX\n",
+ core_num, intx, enx, ciu_ip, set_these_interrupt_bits);
+#endif
+
+ cpu_status_bits = octeon_disable_interrupts();
+
+#ifndef OCTEON_SMP_1
+ ciu_intr_reg_addr = ciu_get_intr_en_reg_addr(core_num, intx, enx);
+#else
+ ciu_intr_reg_addr = ciu_get_en_reg_addr_new(core_num, intx, enx, ciu_ip);
+#endif
+
+ if (!ciu_intr_reg_addr) {
+ printf("Bad call to %s\n", __FUNCTION__);
+ while(1);
+ return; /* XXX */
+ }
+
+ ciu_intr_bits = oct_read64(ciu_intr_reg_addr);
+
+#ifdef DEBUG_CIU_EN
+ printf(" CIU: status: 0x%X reg_addr: 0x%llX Val: 0x%llX -> 0x%llX",
+ cpu_status_bits, ciu_intr_reg_addr, ciu_intr_bits, ciu_intr_bits | set_these_interrupt_bits);
+#endif
+ ciu_intr_bits |= set_these_interrupt_bits;
+ oct_write64(ciu_intr_reg_addr, ciu_intr_bits);
+#ifdef OCTEON_SMP
+ mips_wbflush();
+#endif
+ oct_read64(OCTEON_MIO_BOOT_BIST_STAT); /* Bus Barrier */
+
+#ifdef DEBUG_CIU_EN
+ printf(" Readback: 0x%llX\n\n ",
+ (uint64_t)oct_read64(ciu_intr_reg_addr));
+#endif
+
+ octeon_set_interrupts(cpu_status_bits);
+}
+
+void
+platform_start(__register_t a0, __register_t a1, __register_t a2 __unused,
+ __register_t a3)
+{
+ uint64_t platform_counter_freq;
+ vm_offset_t kernend;
+ int argc = a0;
+ char **argv = (char **)a1;
+ int i, mem;
+
+ /* clear the BSS and SBSS segments */
+ kernend = round_page((vm_offset_t)&end);
+ memset(&edata, 0, kernend - (vm_offset_t)(&edata));
+
+ /* Initialize pcpu stuff */
+ mips_pcpu0_init();
+
+ octeon_boot_params_init(a3);
+ /* XXX octeon boot decriptor has args in it... */
+ octeon_ciu_reset();
+ octeon_uart_write_string(0, "Platform Starting\n");
+
+ /*
+ * Looking for mem=XXM argument
+ */
+ mem = 0; /* Just something to start with */
+ for (i=0; i < argc; i++) {
+ if (strncmp(argv[i], "mem=", 4) == 0) {
+ mem = strtol(argv[i] + 4, NULL, 0);
+ break;
+ }
+ }
+
+ bootverbose = 1;
+ if (mem > 0)
+ realmem = btoc(mem << 20);
+ else
+ realmem = btoc(32 << 20);
+
+ for (i = 0; i < 10; i++)
+ phys_avail[i] = 0;
+
+ /* phys_avail regions are in bytes */
+ phys_avail[0] = MIPS_KSEG0_TO_PHYS((vm_offset_t)&end);
+ phys_avail[1] = ctob(realmem);
+
+ physmem = realmem;
+
+ pmap_bootstrap();
+ mips_proc0_init();
+
+ init_param1();
+ /* TODO: parse argc,argv */
+ platform_counter_freq = 330000000UL; /* XXX: from idt */
+ mips_timer_init_params(platform_counter_freq, 1);
+ cninit();
+ printf("cmd line: ");
+ for (i=0; i < argc; i++)
+ printf("%s ", argv[i]);
+ printf("\n");
+ init_param2(physmem);
+ mips_cpu_init();
+ mutex_init();
+#ifdef DDB
+ kdb_init();
+#endif
+}
+
+/*
+ ****************************************************************************************
+ *
+ * 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;
+
+
+typedef struct {
+ uint32_t major_version;
+ uint32_t minor_version;
+
+ uint64_t stack_top;
+ uint64_t heap_base;
+ uint64_t heap_end;
+ uint64_t desc_vaddr;
+
+ uint32_t exception_base_addr;
+ uint32_t stack_size;
+ 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;
+} cvmx_bootinfo_t;
+
+uint32_t octeon_cpu_clock;
+uint64_t octeon_dram;
+uint32_t octeon_bd_ver = 0, octeon_cvmx_bd_ver = 0, octeon_board_rev_major, octeon_board_rev_minor, octeon_board_type;
+uint8_t octeon_mac_addr[6] = { 0 };
+int octeon_core_mask, octeon_mac_addr_count;
+int octeon_chip_rev_major = 0, octeon_chip_rev_minor = 0, octeon_chip_type = 0;
+
+extern int32_t app_descriptor_addr;
+static octeon_boot_descriptor_t *app_desc_ptr;
+static cvmx_bootinfo_t *cvmx_desc_ptr;
+
+#define OCTEON_BOARD_TYPE_NONE 0
+#define OCTEON_BOARD_TYPE_SIM 1
+
+#define OCTEON_CLOCK_MIN (100 * 1000 * 1000)
+#define OCTEON_CLOCK_MAX (800 * 1000 * 1000)
+#define OCTEON_DRAM_DEFAULT (256 * 1024 * 1024)
+#define OCTEON_DRAM_MIN 30
+#define OCTEON_DRAM_MAX 3000
+
+
+int
+octeon_board_real(void)
+{
+ if ((octeon_board_type == OCTEON_BOARD_TYPE_NONE) ||
+ (octeon_board_type == OCTEON_BOARD_TYPE_SIM) ||
+ !octeon_board_rev_major)
+ return 0;
+ return 1;
+}
+
+static void
+octeon_process_app_desc_ver_unknown(void)
+{
+ printf(" Unknown Boot-Descriptor: Using Defaults\n");
+
+ octeon_cpu_clock = OCTEON_CLOCK_DEFAULT;
+ octeon_dram = OCTEON_DRAM_DEFAULT;
+ octeon_board_rev_major = octeon_board_rev_minor = octeon_board_type = 0;
+ octeon_core_mask = 1;
+ octeon_cpu_clock = OCTEON_CLOCK_DEFAULT;
+ octeon_chip_type = octeon_chip_rev_major = octeon_chip_rev_minor = 0;
+ octeon_mac_addr[0] = 0x00; octeon_mac_addr[1] = 0x0f;
+ octeon_mac_addr[2] = 0xb7; octeon_mac_addr[3] = 0x10;
+ octeon_mac_addr[4] = 0x09; octeon_mac_addr[5] = 0x06;
+ octeon_mac_addr_count = 1;
+}
+
+static int
+octeon_process_app_desc_ver_6(void)
+{
+ /* XXX Why is 0x00000000ffffffffULL a bad value? */
+ if (app_desc_ptr->cvmx_desc_vaddr == 0 ||
+ app_desc_ptr->cvmx_desc_vaddr == 0xfffffffful) {
+ printf ("Bad cvmx_desc_ptr %p\n", cvmx_desc_ptr);
+ return 1;
+ }
+ cvmx_desc_ptr =
+ (cvmx_bootinfo_t *)(intptr_t)app_desc_ptr->cvmx_desc_vaddr;
+ cvmx_desc_ptr =
+ (cvmx_bootinfo_t *) ((intptr_t)cvmx_desc_ptr | MIPS_KSEG0_START);
+ octeon_cvmx_bd_ver = (cvmx_desc_ptr->major_version * 100) +
+ cvmx_desc_ptr->minor_version;
+ /* Too early for panic? */
+ if (cvmx_desc_ptr->major_version != 1) {
+ printf("Incompatible CVMX descriptor from bootloader: %d.%d %p\n",
+ (int) cvmx_desc_ptr->major_version,
+ (int) cvmx_desc_ptr->minor_version, cvmx_desc_ptr);
+ while (1); /* Never return */
+ return 1; /* Satisfy the compiler */
+ }
+
+ octeon_core_mask = cvmx_desc_ptr->core_mask;
+ octeon_cpu_clock = cvmx_desc_ptr->eclock_hz;
+ octeon_board_type = cvmx_desc_ptr->board_type;
+ octeon_board_rev_major = cvmx_desc_ptr->board_rev_major;
+ octeon_board_rev_minor = cvmx_desc_ptr->board_rev_minor;
+ octeon_chip_type = cvmx_desc_ptr->chip_type;
+ octeon_chip_rev_major = cvmx_desc_ptr->chip_rev_major;
+ octeon_chip_rev_minor = cvmx_desc_ptr->chip_rev_minor;
+ octeon_mac_addr[0] = cvmx_desc_ptr->mac_addr_base[0];
+ octeon_mac_addr[1] = cvmx_desc_ptr->mac_addr_base[1];
+ octeon_mac_addr[2] = cvmx_desc_ptr->mac_addr_base[2];
+ octeon_mac_addr[3] = cvmx_desc_ptr->mac_addr_base[3];
+ octeon_mac_addr[4] = cvmx_desc_ptr->mac_addr_base[4];
+ octeon_mac_addr[5] = cvmx_desc_ptr->mac_addr_base[5];
+ octeon_mac_addr_count = cvmx_desc_ptr->mac_addr_count;
+
+ if (app_desc_ptr->dram_size > 16*1024*1024)
+ octeon_dram = (uint64_t)app_desc_ptr->dram_size;
+ else
+ octeon_dram = (uint64_t)app_desc_ptr->dram_size << 20;
+ return 0;
+}
+
+static int
+octeon_process_app_desc_ver_3_4_5(void)
+{
+
+ octeon_cvmx_bd_ver = octeon_bd_ver;
+ octeon_core_mask = app_desc_ptr->core_mask;
+
+ if (app_desc_ptr->desc_version > 3)
+ octeon_cpu_clock = app_desc_ptr->eclock_hz;
+ else
+ octeon_cpu_clock = OCTEON_CLOCK_DEFAULT;
+ if (app_desc_ptr->dram_size > 16*1024*1024)
+ octeon_dram = (uint64_t)app_desc_ptr->dram_size;
+ else
+ octeon_dram = (uint64_t)app_desc_ptr->dram_size << 20;
+
+ if (app_desc_ptr->desc_version > 4) {
+ octeon_board_type = app_desc_ptr->board_type;
+ octeon_board_rev_major = app_desc_ptr->board_rev_major;
+ octeon_board_rev_minor = app_desc_ptr->board_rev_minor;
+ octeon_chip_type = app_desc_ptr->chip_type;
+ octeon_chip_rev_major = app_desc_ptr->chip_rev_major;
+ octeon_chip_rev_minor = app_desc_ptr->chip_rev_minor;
+
+ octeon_mac_addr[0] = app_desc_ptr->mac_addr_base[0];
+ octeon_mac_addr[1] = app_desc_ptr->mac_addr_base[1];
+ octeon_mac_addr[2] = app_desc_ptr->mac_addr_base[2];
+ octeon_mac_addr[3] = app_desc_ptr->mac_addr_base[3];
+ octeon_mac_addr[4] = app_desc_ptr->mac_addr_base[4];
+ octeon_mac_addr[5] = app_desc_ptr->mac_addr_base[5];
+ octeon_mac_addr_count = app_desc_ptr->mac_addr_count;
+ }
+ return 0;
+}
+
+
+static void
+octeon_boot_params_init(register_t ptr)
+{
+ int bad_desc = 1;
+
+ if (ptr != 0 && ptr < MAX_APP_DESC_ADDR) {
+ app_desc_ptr = (octeon_boot_descriptor_t *)(intptr_t)ptr;
+ octeon_bd_ver = app_desc_ptr->desc_version;
+ if ((octeon_bd_ver >= 3) && (octeon_bd_ver <= 5))
+ bad_desc = octeon_process_app_desc_ver_3_4_5();
+ else if (app_desc_ptr->desc_version == 6)
+ bad_desc = octeon_process_app_desc_ver_6();
+ }
+ if (bad_desc)
+ octeon_process_app_desc_ver_unknown();
+
+ printf("Boot Descriptor Ver: %u -> %u/%u",
+ octeon_bd_ver, octeon_cvmx_bd_ver/100, octeon_cvmx_bd_ver%100);
+ printf(" CPU clock: %uMHz\n", octeon_cpu_clock/1000000);
+ printf(" Dram: %u MB", (uint32_t)(octeon_dram >> 20));
+ printf(" Board Type: %u Revision: %u/%u\n",
+ octeon_board_type, octeon_board_rev_major, octeon_board_rev_minor);
+ printf(" Octeon Chip: %u Rev %u/%u",
+ octeon_chip_type, octeon_chip_rev_major, octeon_chip_rev_minor);
+
+ printf(" Mac Address %02X.%02X.%02X.%02X.%02X.%02X\n",
+ octeon_mac_addr[0], octeon_mac_addr[1], octeon_mac_addr[2],
+ octeon_mac_addr[3], octeon_mac_addr[4], octeon_mac_addr[5]);
+}
diff --git a/sys/mips/cavium/octeon_pcmap_regs.h b/sys/mips/cavium/octeon_pcmap_regs.h
new file mode 100644
index 0000000..1655566
--- /dev/null
+++ b/sys/mips/cavium/octeon_pcmap_regs.h
@@ -0,0 +1,1080 @@
+/*
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors."
+*/
+
+#ifndef __OCTEON_PCMAP_REGS_H__
+#define __OCTEON_PCMAP_REGS_H__
+
+#include "opt_cputype.h"
+
+#define OCTEON_CACHE_LINE_SIZE 0x80 /* 128 bytes cache line size */
+#define IS_OCTEON_ALIGNED(p) (!((u_long)(p) & 0x7f))
+#define OCTEON_ALIGN(p) (((u_long)(p) + ((OCTEON_CACHE_LINE_SIZE) - 1)) & ~((OCTEON_CACHE_LINE_SIZE) - 1))
+
+#ifndef LOCORE
+
+/* XXXimp: From Cavium's include/pcpu.h, need to port that over */
+#ifndef OCTEON_SMP
+#define OCTEON_CORE_ID 0
+#else
+extern struct pcpu *cpuid_to_pcpu[];
+#define OCTEON_CORE_ID (mips_rd_coreid())
+#endif
+
+/*
+ * Utility inlines & macros
+ */
+
+/* turn the variable name into a string */
+#define OCTEON_TMP_STR(x) OCTEON_TMP_STR2(x)
+#define OCTEON_TMP_STR2(x) #x
+
+#define OCTEON_PREFETCH_PREF0(address, offset) \
+ __asm __volatile ( ".set mips64\n" \
+ ".set noreorder\n" \
+ "pref 0, " OCTEON_TMP_STR(offset) "(%0)\n" \
+ ".set reorder\n" \
+ ".set mips0\n" \
+ : \
+ : "r" (address) );
+
+#define OCTEON_PREFETCH(address, offset) OCTEON_PREFETCH_PREF0(address,offset)
+
+#define OCTEON_PREFETCH0(address) OCTEON_PREFETCH(address, 0)
+#define OCTEON_PREFETCH128(address) OCTEON_PREFETCH(address, 128)
+
+#define OCTEON_SYNCIOBDMA __asm __volatile (".word 0x8f" : : :"memory")
+
+#define OCTEON_SYNCW __asm __volatile (".word 0x10f" : : )
+#define OCTEON_SYNCW __asm __volatile (".word 0x10f" : : )
+#define OCTEON_SYNCWS __asm __volatile (".word 0x14f" : : )
+
+//#if defined(__mips_n32) || defined(__mips_n64)
+#if defined(__not_used)
+
+static inline void oct_write64 (uint64_t csr_addr, uint64_t val64)
+{
+ uint64_t *ptr = (uint64_t *) csr_addr;
+ *ptr = val64;
+}
+
+static inline void oct_write64_int64 (uint64_t csr_addr, int64_t val64i)
+{
+ int64_t *ptr = (int64_t *) csr_addr;
+ *ptr = val64i;
+}
+
+static inline void oct_write8_x8 (uint64_t csr_addr, uint8_t val8)
+{
+ uint64_t *ptr = (uint64_t *) csr_addr;
+ *ptr = (uint64_t) val8;
+}
+
+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);
+}
+
+static inline uint8_t oct_read8 (uint64_t csr_addr)
+{
+ uint8_t *ptr = (uint8_t *) csr_addr;
+ return (*ptr);
+}
+
+static inline uint8_t oct_read16 (uint64_t csr_addr)
+{
+ uint16_t *ptr = (uint16_t *) csr_addr;
+ return (*ptr);
+}
+
+
+static inline uint32_t oct_read32 (uint64_t csr_addr)
+{
+ uint32_t *ptr = (uint32_t *) csr_addr;
+ return (*ptr);
+}
+
+static inline uint64_t oct_read64 (uint64_t csr_addr)
+{
+ uint64_t *ptr = (uint64_t *) csr_addr;
+ return (*ptr);
+}
+
+static inline int32_t oct_readint32 (uint64_t csr_addr)
+{
+ int32_t *ptr = (int32_t *) csr_addr;
+ return (*ptr);
+}
+
+
+
+#else
+
+
+/* ABI o32 */
+
+
+/*
+ * Read/write functions
+ */
+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 = (uint64_t)val64 >> 32;
+ uint32_t vall = val64;
+ uint32_t tmp1;
+ uint32_t tmp2;
+ uint32_t tmp3;
+
+ __asm __volatile (
+ ".set mips64\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 mips0\n"
+ : "=&r" (tmp1), "=&r" (tmp2), "=&r" (tmp3)
+ : "r" (valh), "r" (vall),
+ "r" (csr_addrh), "r" (csr_addrl)
+ );
+}
+
+static inline void oct_write64_int64 (uint64_t csr_addr, int64_t val64i)
+{
+ uint32_t csr_addrh = csr_addr >> 32;
+ uint32_t csr_addrl = csr_addr;
+ int32_t valh = (uint64_t)val64i >> 32;
+ int32_t vall = val64i;
+ uint32_t tmp1;
+ uint32_t tmp2;
+ uint32_t tmp3;
+
+ __asm __volatile (
+ ".set mips64\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 mips0\n"
+ : "=&r" (tmp1), "=&r" (tmp2), "=&r" (tmp3)
+ : "r" (valh), "r" (vall),
+ "r" (csr_addrh), "r" (csr_addrl)
+ );
+}
+
+
+/*
+ * oct_write8_x8
+ *
+ * 8 bit data write into IO Space. Written using an 8 bit bus io transaction
+ */
+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;
+
+ __asm __volatile (
+ ".set mips64\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 mips0\n"
+ : "=&r" (tmp1), "=&r" (tmp2)
+ : "r" (val8), "r" (csr_addrh), "r" (csr_addrl) );
+}
+
+/*
+ * oct_write8
+ *
+ * 8 bit data write into IO Space. Written using a 64 bit bus io transaction
+ */
+static inline void oct_write8 (uint64_t csr_addr, uint8_t val8)
+{
+#if 1
+ oct_write64(csr_addr, (uint64_t) val8);
+#else
+
+ uint32_t csr_addrh = csr_addr>>32;
+ uint32_t csr_addrl = csr_addr;
+ uint32_t tmp1;
+ uint32_t tmp2;
+
+ __asm __volatile (
+ ".set mips64\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 mips0\n"
+ : "=&r" (tmp1), "=&r" (tmp2)
+ : "r" (val8), "r" (csr_addrh), "r" (csr_addrl) );
+#endif
+}
+
+static inline void oct_write16 (uint64_t csr_addr, uint16_t val16)
+{
+#if 1
+ oct_write64(csr_addr, (uint64_t) val16);
+
+#else
+ uint32_t csr_addrh = csr_addr>>32;
+ uint32_t csr_addrl = csr_addr;
+ uint32_t tmp1;
+ uint32_t tmp2;
+
+ __asm __volatile (
+ ".set mips64\n"
+ "dsll %0, %3, 32\n"
+ "dsll %1, %4, 32\n"
+ "dsrl %1, %1, 32\n"
+ "or %0, %0, %1\n"
+ "sh %2, 0(%0)\n"
+ ".set mips0\n"
+ : "=&r" (tmp1), "=&r" (tmp2)
+ : "r" (val16), "r" (csr_addrh), "r" (csr_addrl) );
+#endif
+}
+
+static inline void oct_write32 (uint64_t csr_addr, uint32_t val32)
+{
+#if 1
+ oct_write64(csr_addr, (uint64_t) val32);
+#else
+
+ uint32_t csr_addrh = csr_addr>>32;
+ uint32_t csr_addrl = csr_addr;
+ uint32_t tmp1;
+ uint32_t tmp2;
+
+ __asm __volatile (
+ ".set mips64\n"
+ "dsll %0, %3, 32\n"
+ "dsll %1, %4, 32\n"
+ "dsrl %1, %1, 32\n"
+ "or %0, %0, %1\n"
+ "sw %2, 0(%0)\n"
+ ".set mips0\n"
+ : "=&r" (tmp1), "=&r" (tmp2)
+ : "r" (val32), "r" (csr_addrh), "r" (csr_addrl) );
+#endif
+}
+
+
+
+static inline uint8_t oct_read8 (uint64_t csr_addr)
+{
+ uint32_t csr_addrh = csr_addr>>32;
+ uint32_t csr_addrl = csr_addr;
+ uint32_t tmp1, tmp2;
+
+ __asm __volatile (
+ ".set mips64\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 mips0\n"
+ : "=&r" (tmp1), "=&r" (tmp2)
+ : "r" (csr_addrh), "r" (csr_addrl) );
+ return ((uint8_t) tmp2);
+}
+
+static inline uint8_t oct_read16 (uint64_t csr_addr)
+{
+ uint32_t csr_addrh = csr_addr>>32;
+ uint32_t csr_addrl = csr_addr;
+ uint32_t tmp1, tmp2;
+
+ __asm __volatile (
+ ".set mips64\n"
+ "dsll %1, %2, 32\n"
+ "dsll %0, %3, 32\n"
+ "dsrl %0, %0, 32\n"
+ "or %1, %1, %0\n"
+ "lh %1, 0(%1)\n"
+ ".set mips0\n"
+ : "=&r" (tmp1), "=&r" (tmp2)
+ : "r" (csr_addrh), "r" (csr_addrl) );
+ return ((uint16_t) tmp2);
+}
+
+
+static inline uint32_t oct_read32 (uint64_t csr_addr)
+{
+ uint32_t csr_addrh = csr_addr>>32;
+ uint32_t csr_addrl = csr_addr;
+ uint32_t val32;
+ uint32_t tmp;
+
+ __asm __volatile (
+ ".set mips64\n"
+ "dsll %0, %2, 32\n"
+ "dsll %1, %3, 32\n"
+ "dsrl %1, %1, 32\n"
+ "or %0, %0, %1\n"
+ "lw %0, 0(%0)\n"
+ ".set mips0\n"
+ : "=&r" (val32), "=&r" (tmp)
+ : "r" (csr_addrh), "r" (csr_addrl) );
+ return (val32);
+}
+
+
+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;
+
+ __asm __volatile (
+ ".set mips64\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 mips0\n"
+ : "=&r" (valh), "=&r" (vall)
+ : "r" (csr_addrh), "r" (csr_addrl)
+ );
+ return ((uint64_t)valh << 32) | vall;
+}
+
+
+static inline int32_t oct_readint32 (uint64_t csr_addr)
+{
+ uint32_t csr_addrh = csr_addr>>32;
+ uint32_t csr_addrl = csr_addr;
+ int32_t val32;
+ uint32_t tmp;
+
+ __asm __volatile (
+ ".set mips64\n"
+ "dsll %0, %2, 32\n"
+ "dsll %1, %3, 32\n"
+ "dsrl %1, %1, 32\n"
+ "or %0, %0, %1\n"
+ "lw %0, 0(%0)\n"
+ : "=&r" (val32), "=&r" (tmp)
+ : "r" (csr_addrh), "r" (csr_addrl) );
+ return (val32);
+}
+
+
+#endif
+
+
+#define OCTEON_HW_BASE ((volatile uint64_t *) 0L)
+#define OCTEON_REG_OFFSET (-4 * 1024ll) /* local scratchpad reg base */
+#define OCTEON_SCRATCH_BASE ((volatile uint8_t *)(OCTEON_HW_BASE + \
+ OCTEON_REG_OFFSET))
+
+#define OCTEON_SCR_SCRATCH 8
+#define OCTEON_SCRATCH_0 16
+#define OCTEON_SCRATCH_1 24
+#define OCTEON_SCRATCH_2 32
+
+
+static inline uint64_t oct_mf_chord (void)
+{
+ uint64_t dest;
+
+ __asm __volatile ( ".set push\n"
+ ".set noreorder\n"
+ ".set noat\n"
+ ".set mips64\n"
+ "dmfc2 $1, 0x400\n"
+ "move %0, $1\n"
+ ".set pop\n"
+ : "=r" (dest) : : "$1");
+ return dest;
+}
+
+
+#define MIPS64_DMFCz(cop,regnum,cp0reg,select) \
+ .word (0x40200000 | (cop << 25) | (regnum << 16) | (cp0reg << 11) | select)
+
+
+#define mips64_getcpz_xstr(s) mips64_getcpz_str(s)
+#define mips64_getcpz_str(s) #s
+
+#define mips64_dgetcpz(cop,cpzreg,sel,val_ptr) \
+ ({ __asm __volatile( \
+ ".set push\n" \
+ ".set mips3\n" \
+ ".set noreorder\n" \
+ ".set noat\n" \
+ mips64_getcpz_xstr(MIPS64_DMFCz(cop,1,cpzreg,sel)) "\n" \
+ "nop\n" \
+ "nop\n" \
+ "nop\n" \
+ "nop\n" \
+ "sd $1,0(%0)\n" \
+ ".set pop" \
+ : /* no outputs */ : "r" (val_ptr) : "$1"); \
+ })
+
+
+#define mips64_dgetcp2(cp2reg,sel,retval_ptr) \
+ mips64_dgetcpz(2,cp2reg,sel,retval_ptr)
+
+
+#define OCTEON_MF_CHORD(dest) mips64_dgetcp2(0x400, 0, &dest)
+
+
+
+#define OCTEON_RDHWR(result, regstr) \
+ __asm __volatile ( \
+ ".set mips3\n" \
+ "rdhwr %0,$" OCTEON_TMP_STR(regstr) "\n" \
+ ".set mips\n" \
+ : "=d" (result));
+
+#define CVMX_MF_CHORD(dest) OCTEON_RDHWR(dest, 30)
+
+#define OCTEON_CHORD_HEX(dest_ptr) \
+ ({ __asm __volatile( \
+ ".set push\n" \
+ ".set mips3\n" \
+ ".set noreorder\n" \
+ ".set noat\n" \
+ ".word 0x7c02f03b \n"\
+ "nop\n" \
+ "nop\n" \
+ "nop\n" \
+ "nop\n" \
+ "sd $2,0(%0)\n" \
+ ".set pop" \
+ : /* no outputs */ : "r" (dest_ptr) : "$2"); \
+ })
+
+
+
+#define OCTEON_MF_CHORD_BAD(dest) \
+ __asm __volatile ( \
+ ".set mips3\n" \
+ "dmfc2 %0, 0x400\n" \
+ ".set mips0\n" \
+ : "=&r" (dest) : )
+
+static inline uint64_t oct_scratch_read64 (uint64_t address)
+{
+ return(*((volatile uint64_t *)(OCTEON_SCRATCH_BASE + address)));
+}
+
+static inline void oct_scratch_write64 (uint64_t address, uint64_t value)
+{
+ *((volatile uint64_t *)(OCTEON_SCRATCH_BASE + address)) = value;
+}
+
+
+#define OCTEON_READ_CSR32(addr, val) \
+ addr_ptr = addr; \
+ oct_read_32_ptr(&addr_ptr, &val);
+
+#define OCTEON_WRITE_CSR32(addr, val, val_dummy) \
+ addr_ptr = addr; \
+ oct_write_32_ptr(&addr_ptr, &val); \
+ oct_read64(OCTEON_MIO_BOOT_BIST_STAT);
+
+
+
+/*
+ * Octeon Address Space Definitions
+ */
+typedef enum {
+ OCTEON_MIPS_SPACE_XKSEG = 3LL,
+ OCTEON_MIPS_SPACE_XKPHYS = 2LL,
+ OCTEON_MIPS_SPACE_XSSEG = 1LL,
+ OCTEON_MIPS_SPACE_XUSEG = 0LL
+} octeon_mips_space_t;
+
+typedef enum {
+ OCTEON_MIPS_XKSEG_SPACE_KSEG0 = 0LL,
+ OCTEON_MIPS_XKSEG_SPACE_KSEG1 = 1LL,
+ OCTEON_MIPS_XKSEG_SPACE_SSEG = 2LL,
+ OCTEON_MIPS_XKSEG_SPACE_KSEG3 = 3LL
+} octeon_mips_xkseg_space_t;
+
+
+/*
+***********************************************************************
+ * 32 bit mode alert
+ * The kseg0 calc below might fail in xkphys.
+ */
+
+/*
+ * We limit the allocated device physical blocks to low mem. So use Kseg0
+ */
+
+/*
+ * Need to go back to kernel to find v->p mappings & vice-versa
+ * We are getting non 1-1 mappings.
+ * #define OCTEON_PTR2PHYS(addr) ((unsigned long) addr & 0x7fffffff)
+ */
+#define OCTEON_PTR2PHYS(addr) octeon_ptr_to_phys(addr)
+
+
+
+/* PTR_SIZE == sizeof(uint32_t) */
+
+#ifdef ISA_MIPS32
+#define mipsx_addr_size uint32_t // u_int64
+#define MIPSX_ADDR_SIZE_KSEGX_BIT_SHIFT 30 // 62
+#define MIPSX_ADDR_SIZE_KSEGX_MASK_REMOVED 0x1fffffff // 0x1fffffff
+#else
+#define mipsx_addr_size uint64_t
+#define MIPSX_ADDR_SIZE_KSEGX_BIT_SHIFT 62
+#define MIPSX_ADDR_SIZE_KSEGX_MASK_REMOVED 0x1fffffffffffffff
+#endif
+
+
+#define octeon_ptr_to_phys(ptr) \
+ ((((mipsx_addr_size) ptr) >> MIPSX_ADDR_SIZE_KSEGX_BIT_SHIFT) == 2) ? \
+ ((mipsx_addr_size) ptr & MIPSX_ADDR_SIZE_KSEGX_MASK_REMOVED) : \
+ (vtophys(ptr))
+
+
+#ifdef CODE_FOR_64_BIT_NEEDED
+static inline mipsx_addr_size octeon_ptr_to_phys (void *ptr)
+{
+ if ((((mipsx_addr_size) ptr) >> MIPSX_ADDR_SIZE_KSEGX_BIT_SHIFT) == 2) {
+ /*
+ * KSEG0 based address ?
+ */
+ return ((mipsx_addr_size) ptr & MIPSX_ADDR_SIZE_KSEGX_MASK_REMOVED);
+ } else {
+ /*
+ * Ask kernel/vm to give us the phys translation.
+ */
+ return (vtophys(ptr));
+ }
+}
+#endif
+
+#define OCTEON_IO_SEG OCTEON_MIPS_SPACE_XKPHYS
+
+
+#define OCTEON_ADD_SEG(segment, add) ((((uint64_t)segment) << 62) | (add))
+
+#define OCTEON_ADD_IO_SEG(add) OCTEON_ADD_SEG(OCTEON_IO_SEG, (add))
+#define OCTEON_ADDR_DID(did) (OCTEON_ADDR_DIDSPACE(did) << 40)
+#define OCTEON_ADDR_DIDSPACE(did) (((OCTEON_IO_SEG) << 22) | ((1ULL) << 8) | (did))
+#define OCTEON_ADDR_FULL_DID(did,subdid) (((did) << 3) | (subdid))
+
+
+#define OCTEON_CIU_PP_RST OCTEON_ADD_IO_SEG(0x0001070000000700ull)
+#define OCTEON_OCTEON_DID_TAG 12ULL
+
+
+
+
+/*
+ * octeon_addr_t
+ */
+typedef union {
+ uint64_t word64;
+
+ struct {
+ octeon_mips_space_t R : 2;
+ uint64_t offset :62;
+ } sva; // mapped or unmapped virtual address
+
+ struct {
+ uint64_t zeroes :33;
+ uint64_t offset :31;
+ } suseg; // mapped USEG virtual addresses (typically)
+
+ struct {
+ uint64_t ones :33;
+ octeon_mips_xkseg_space_t sp : 2;
+ uint64_t offset :29;
+ } sxkseg; // mapped or unmapped virtual address
+
+ struct {
+ octeon_mips_space_t R :2; // CVMX_MIPS_SPACE_XKPHYS in this case
+ uint64_t cca : 3; // ignored by octeon
+ uint64_t mbz :10;
+ uint64_t pa :49; // physical address
+ } sxkphys; // physical address accessed through xkphys unmapped virtual address
+
+ struct {
+ uint64_t mbz :15;
+ uint64_t is_io : 1; // if set, the address is uncached and resides on MCB bus
+ uint64_t did : 8; // the hardware ignores this field when is_io==0, else device ID
+ uint64_t unaddr: 4; // the hardware ignores <39:36> in Octeon I
+ uint64_t offset :36;
+ } sphys; // physical address
+
+ struct {
+ uint64_t zeroes :24; // techically, <47:40> are dont-cares
+ uint64_t unaddr: 4; // the hardware ignores <39:36> in Octeon I
+ uint64_t offset :36;
+ } smem; // physical mem address
+
+ struct {
+ uint64_t mem_region :2;
+ uint64_t mbz :13;
+ uint64_t is_io : 1; // 1 in this case
+ uint64_t did : 8; // the hardware ignores this field when is_io==0, else device ID
+ uint64_t unaddr: 4; // the hardware ignores <39:36> in Octeon I
+ uint64_t offset :36;
+ } sio; // physical IO address
+
+ struct {
+ uint64_t didspace : 24;
+ uint64_t unused : 40;
+ } sfilldidspace;
+
+} octeon_addr_t;
+
+
+typedef union {
+ uint64_t word64;
+ struct {
+ uint32_t word32hi;
+ uint32_t word32lo;
+ } bits;
+} octeon_word_t;
+
+
+
+
+/*
+ * octeon_build_io_address
+ *
+ * Builds a memory address for I/O based on the Major 5bits and Sub DID 3bits
+ */
+static inline uint64_t octeon_build_io_address (uint64_t major_did,
+ uint64_t sub_did)
+{
+ return ((0x1ull << 48) | (major_did << 43) | (sub_did << 40));
+}
+
+/*
+ * octeon_build_mask
+ *
+ * Builds a bit mask given the required size in bits.
+ *
+ * @param bits Number of bits in the mask
+ * @return The mask
+ */
+static inline uint64_t octeon_build_mask (uint64_t bits)
+{
+ return ~((~0x0ull) << bits);
+}
+
+/*
+ * octeon_build_bits
+ *
+ * Perform mask and shift to place the supplied value into
+ * the supplied bit rage.
+ *
+ * Example: octeon_build_bits(39,24,value)
+ * <pre>
+ * 6 5 4 3 3 2 1
+ * 3 5 7 9 1 3 5 7 0
+ * +-------+-------+-------+-------+-------+-------+-------+------+
+ * 000000000000000000000000___________value000000000000000000000000
+ * </pre>
+ *
+ * @param high_bit Highest bit value can occupy (inclusive) 0-63
+ * @param low_bit Lowest bit value can occupy inclusive 0-high_bit
+ * @param value Value to use
+ * @return Value masked and shifted
+ */
+static inline uint64_t octeon_build_bits (uint64_t high_bit, uint64_t low_bit,
+ uint64_t value)
+{
+ return ((value & octeon_build_mask(high_bit - low_bit + 1)) << low_bit);
+}
+
+
+/********************** simple spinlocks ***************/
+typedef struct {
+ volatile uint32_t value;
+} octeon_spinlock_t;
+
+// note - macros not expanded in inline ASM, so values hardcoded
+#define OCTEON_SPINLOCK_UNLOCKED_VAL 0
+#define OCTEON_SPINLOCK_LOCKED_VAL 1
+
+/**
+ * Initialize a spinlock
+ *
+ * @param lock Lock to initialize
+ */
+static inline void octeon_spinlock_init(octeon_spinlock_t *lock)
+{
+ lock->value = OCTEON_SPINLOCK_UNLOCKED_VAL;
+}
+/**
+ * Releases lock
+ *
+ * @param lock pointer to lock structure
+ */
+static inline void octeon_spinlock_unlock(octeon_spinlock_t *lock)
+{
+ OCTEON_SYNCWS;
+
+ lock->value = 0;
+ OCTEON_SYNCWS;
+}
+
+/**
+ * Gets lock, spins until lock is taken
+ *
+ * @param lock pointer to lock structure
+ */
+static inline void octeon_spinlock_lock(octeon_spinlock_t *lock)
+{
+ unsigned int tmp;
+ __asm __volatile(
+ ".set noreorder \n"
+ "1: ll %1, %0 \n"
+ " bnez %1, 1b \n"
+ " li %1, 1 \n"
+ " sc %1, %0 \n"
+ " beqz %1, 1b \n"
+ " nop \n"
+ ".set reorder \n"
+ : "+m" (lock->value), "=&r" (tmp )
+ :
+ : "memory");
+}
+
+/********************** end simple spinlocks ***************/
+
+
+
+/* ------------------------------------------------------------------- *
+ * octeon_get_chipid() *
+ * ------------------------------------------------------------------- */
+#define OCTEON_CN31XX_CHIP 0x000d0100
+#define OCTEON_CN30XX_CHIP 0x000d0200
+#define OCTEON_CN3020_CHIP 0x000d0112
+#define OCTEON_CN5020_CHIP 0x000d0601
+
+static inline uint32_t octeon_get_chipid(void)
+{
+ uint32_t id;
+
+ __asm __volatile ("mfc0 %0, $15,0" : "=r" (id));
+
+ return (id);
+}
+
+
+static inline uint32_t octeon_get_except_base_reg (void)
+{
+ uint32_t tmp;
+
+ __asm volatile (
+ " .set mips64r2 \n"
+ " .set noreorder \n"
+ " mfc0 %0, $15, 1 \n"
+ " .set reorder \n"
+ : "=&r" (tmp) : );
+
+ return(tmp);
+}
+
+
+
+
+static inline unsigned int get_coremask (void)
+{
+ return(~(oct_read64(OCTEON_CIU_PP_RST)) & 0xffff);
+}
+
+
+static inline uint32_t octeon_get_core_num (void)
+{
+
+ return (0x3FF & octeon_get_except_base_reg());
+}
+
+
+static inline uint64_t octeon_get_cycle(void)
+{
+
+/* ABI == 32 */
+
+ uint32_t tmp_low, tmp_hi;
+
+ __asm __volatile (
+ " .set push \n"
+ " .set mips64r2 \n"
+ " .set noreorder \n"
+ " rdhwr %[tmpl], $31 \n"
+ " dadd %[tmph], %[tmpl], $0 \n"
+ " dsrl %[tmph], 32 \n"
+ " dsll %[tmpl], 32 \n"
+ " dsrl %[tmpl], 32 \n"
+ " .set pop \n"
+ : [tmpl] "=&r" (tmp_low), [tmph] "=&r" (tmp_hi) : );
+
+ return(((uint64_t)tmp_hi << 32) + tmp_low);
+}
+
+
+/**
+ * Wait for the specified number of cycle
+ *
+ * @param cycles
+ */
+static inline void octeon_wait (uint64_t cycles)
+{
+ uint64_t done = octeon_get_cycle() + cycles;
+
+ while (octeon_get_cycle() < done)
+ {
+ /* Spin */
+ }
+}
+
+
+
+/*
+ * 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_uart_write_byte(int uart_index, uint8_t ch);
+extern void octeon_uart_write_string(int uart_index, const char *str);
+extern void octeon_uart_write_hex(uint32_t wl);
+extern void octeon_uart_write_hex2(uint32_t wl, uint32_t wh);
+extern void octeon_wait_uart_flush(int uart_index, uint8_t ch);
+extern void octeon_uart_write_byte0(uint8_t ch);
+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 mips_disable_interrupt_controls(void);
+extern uint32_t octeon_cpu_clock;
+extern uint64_t octeon_dram;
+extern uint32_t octeon_bd_ver, octeon_board_rev_major, octeon_board_rev_minor, octeon_board_type;
+extern uint8_t octeon_mac_addr[6];
+extern int octeon_core_mask, octeon_mac_addr_count, octeon_chip_rev_major, octeon_chip_rev_minor, octeon_chip_type;
+extern void bzero_64(void *str, size_t len);
+extern void bzero_32(void *str, size_t len);
+extern void bzero_16(void *str, size_t len);
+extern void bzero_old(void *str, size_t len);
+extern void octeon_ciu_reset(void);
+extern void ciu_disable_intr(int core_num, int intx, int enx);
+extern void ciu_enable_interrupts (int core_num, int intx, int enx, uint64_t set_these_interrupt_bits, int ciu_ip);
+extern void ciu_clear_int_summary(int core_num, int intx, int enx, uint64_t write_bits);
+extern uint64_t ciu_get_int_summary(int core_num, int intx, int enx);
+extern void octeon_ciu_start_gtimer(int timer, u_int one_shot, uint64_t time_cycles);
+extern void octeon_ciu_stop_gtimer(int timer);
+extern int octeon_board_real(void);
+
+
+
+typedef union {
+ uint64_t word64;
+ struct {
+ uint64_t reserved : 27; /* Not used */
+ uint64_t one_shot : 1; /* Oneshot ? */
+ uint64_t len : 36; /* len of timer in clock cycles - 1 */
+ } bits;
+} octeon_ciu_gentimer;
+
+
+
+#endif /* LOCORE */
+
+
+/*
+ * R4K Address space definitions
+ */
+#define ADRSPC_K0BASE (0x80000000)
+#define ADRSPC_K0SIZE (0x20000000)
+#define ADRSPC_K1BASE (0xA0000000)
+#define ADRSPC_K1SIZE (0x20000000)
+#define ADRSPC_KSBASE (0xC0000000)
+#define ADRSPC_KSSIZE (0x20000000)
+#define ADRSPC_K3BASE (0xE0000000)
+#define ADRSPC_K3SIZE (0x20000000)
+#define ADRSPC_KUBASE (0x00000000)
+#define ADRSPC_KUSIZE (0x80000000)
+#define KSEG_MSB_ADDR 0xFFFFFFFF
+
+
+
+#define OCTEON_CLOCK_DEFAULT (500 * 1000 * 1000)
+
+
+/*
+ * Octeon Boot Bus BIST Status
+ * Mostly used for dummy read to ensure all prev I/Os are write-complete.
+ */
+#define OCTEON_MIO_BOOT_BIST_STAT 0x80011800000000F8ull
+
+/*
+ * Octeon UART unit
+ */
+#define OCTEON_MIO_UART0_THR 0x8001180000000840ull
+#define OCTEON_MIO_UART1_THR 0x8001180000000C40ull
+#define OCTEON_MIO_UART0_LSR 0x8001180000000828ull
+#define OCTEON_MIO_UART1_LSR 0x8001180000000C28ull
+#define OCTEON_MIO_UART0_RBR 0x8001180000000800ull
+#define OCTEON_MIO_UART1_RBR 0x8001180000000C00ull
+#define OCTEON_MIO_UART0_USR 0x8001180000000938ull
+#define OCTEON_MIO_UART1_USR 0x8001180000000D38ull
+#define OCTEON_MIO_ADDR_HI24 0x800118
+
+
+/*
+ * EBT3000 LED Unit
+ */
+#define OCTEON_CHAR_LED_BASE_ADDR (0x1d020000 | (0x1ffffffffull << 31))
+
+#define OCTEON_FPA_QUEUES 8
+
+/*
+ * Octeon FPA I/O Registers
+ */
+#define OCTEON_FPA_CTL_STATUS 0x8001180028000050ull
+#define OCTEON_FPA_FPF_SIZE 0x8001180028000058ull
+#define OCTEON_FPA_FPF_MARKS 0x8001180028000000ull
+#define OCTEON_FPA_INT_SUMMARY 0x8001180028000040ull
+#define OCTEON_FPA_INT_ENABLE 0x8001180028000048ull
+#define OCTEON_FPA_QUEUE_AVAILABLE 0x8001180028000098ull
+#define OCTEON_FPA_PAGE_INDEX 0x80011800280000f0ull
+
+/*
+ * Octeon PKO Unit
+ */
+#define OCTEON_PKO_REG_FLAGS 0x8001180050000000ull
+#define OCTEON_PKO_REG_READ_IDX 0x8001180050000008ull
+#define OCTEON_PKO_CMD_BUF 0x8001180050000010ull
+#define OCTEON_PKO_GMX_PORT_MODE 0x8001180050000018ull
+#define OCTEON_PKO_REG_CRC_ENABLE 0x8001180050000020ull
+#define OCTEON_PKO_QUEUE_MODE 0x8001180050000048ull
+#define OCTEON_PKO_MEM_QUEUE_PTRS 0x8001180050001000ull
+#define OCTEON_PKO_MEM_COUNT0 0x8001180050001080ull
+#define OCTEON_PKO_MEM_COUNT1 0x8001180050001088ull
+#define OCTEON_PKO_MEM_DEBUG0 0x8001180050001100ull
+#define OCTEON_PKO_MEM_DEBUG1 0x8001180050001108ull
+#define OCTEON_PKO_MEM_DEBUG2 0x8001180050001110ull
+#define OCTEON_PKO_MEM_DEBUG3 0x8001180050001118ull
+#define OCTEON_PKO_MEM_DEBUG4 0x8001180050001120ull
+#define OCTEON_PKO_MEM_DEBUG5 0x8001180050001128ull
+#define OCTEON_PKO_MEM_DEBUG6 0x8001180050001130ull
+#define OCTEON_PKO_MEM_DEBUG7 0x8001180050001138ull
+#define OCTEON_PKO_MEM_DEBUG8 0x8001180050001140ull
+#define OCTEON_PKO_MEM_DEBUG9 0x8001180050001148ull
+
+
+/*
+ * Octeon IPD Unit
+ */
+#define OCTEON_IPD_1ST_MBUFF_SKIP 0x80014F0000000000ull
+#define OCTEON_IPD_NOT_1ST_MBUFF_SKIP 0x80014F0000000008ull
+#define OCTEON_IPD_PACKET_MBUFF_SIZE 0x80014F0000000010ull
+#define OCTEON_IPD_1ST_NEXT_PTR_BACK 0x80014F0000000150ull
+#define OCTEON_IPD_2ND_NEXT_PTR_BACK 0x80014F0000000158ull
+#define OCTEON_IPD_WQE_FPA_QUEUE 0x80014F0000000020ull
+#define OCTEON_IPD_CTL_STATUS 0x80014F0000000018ull
+#define OCTEON_IPD_QOSX_RED_MARKS(queue) (0x80014F0000000178ull + ((queue) * 8))
+#define OCTEON_IPD_RED_Q_PARAM(queue) (0x80014F00000002E0ull + ((queue) * 8))
+#define OCTEON_IPD_PORT_BP_PAGE_COUNT(port) (0x80014F0000000028ull + ((port) * 8))
+#define OCTEON_IPD_BP_PORT_RED_END 0x80014F0000000328ull
+#define OCTEON_IPD_RED_PORT_ENABLE 0x80014F00000002D8ull
+
+/*
+ * Octeon CIU Unit
+ */
+#define OCTEON_CIU_ENABLE_BASE_ADDR 0x8001070000000200ull
+#define OCTEON_CIU_SUMMARY_BASE_ADDR 0x8001070000000000ull
+#define OCTEON_CIU_SUMMARY_INT1_ADDR 0x8001070000000108ull
+
+#define OCTEON_CIU_MBOX_SETX(offset) (0x8001070000000600ull+((offset)*8))
+#define OCTEON_CIU_MBOX_CLRX(offset) (0x8001070000000680ull+((offset)*8))
+#define OCTEON_CIU_ENABLE_MBOX_INTR 0x0000000300000000ull /* bits 32, 33 */
+
+#define CIU_MIPS_IP2 0
+#define CIU_MIPS_IP3 1
+
+#define CIU_INT_0 CIU_MIPS_IP2
+#define CIU_INT_1 CIU_MIPS_IP3
+
+#define CIU_EN_0 0
+#define CIU_EN_1 1
+
+#define CIU_THIS_CORE -1
+
+#define CIU_UART_BITS_UART0 (0x1ull << 34) // Bit 34
+#define CIU_UART_BITS_UART1 (0x1ull << 35) // Bit 35
+#define CIU_GENTIMER_BITS_ENABLE(timer) (0x1ull << (52 + (timer))) // Bit 52..55
+
+#define CIU_GENTIMER_NUM_0 0
+#define CIU_GENTIMER_NUM_1 1
+#define CIU_GENTIMER_NUM_2 2
+#define CIU_GENTIMER_NUM_3 3
+#define OCTEON_GENTIMER_ONESHOT 1
+#define OCTEON_GENTIMER_PERIODIC 0
+
+#define OCTEON_CIU_GENTIMER_ADDR(timer) (0x8001070000000480ull + ((timer) * 0x8))
+
+
+#define OCTEON_GENTIMER_LEN_1MS (0x7a120ull) /* Back of envelope. 500Mhz Octeon */ // FIXME IF WRONG
+#define OCTEON_GENTIMER_LEN_1SEC ((OCTEON_GENTIMER_LEN_1MS) * 1000)
+
+/*
+ * Physical Memory Banks
+ */
+/* 1st BANK */
+#define OCTEON_DRAM_FIRST_256_START 0x00000000ull
+#define OCTEON_DRAM_FIRST_256_END (0x10000000ull - 1ull)
+#define OCTEON_DRAM_RESERVED_END 0X1FFF000ULL /* 32 Meg Reserved for Mips Kernel MD Ops */
+#define OCTEON_DRAM_FIRST_BANK_SIZE (OCTEON_DRAM_FIRST_256_END - OCTEON_DRAM_FIRST_256_START + 1)
+
+/* 2nd BANK */
+#define OCTEON_DRAM_SECOND_256_START (0x0000000410000000ull)
+#define OCTEON_DRAM_SECOND_256_END (0x0000000420000000ull - 1ull) /* Requires 64 bit paddr */
+#define OCTEON_DRAM_SECOND_BANK_SIZE (OCTEON_DRAM_SECOND_256_END - OCTEON_DRAM_SECOND_256_START + 1ull)
+
+/* 3rd BANK */
+#define OCTEON_DRAM_ABOVE_512_START 0x20000000ull
+#define OCTEON_DRAM_ABOVE_512_END (0x0000000300000000ull - 1ull) /* To be calculated as remaining */
+#define OCTEON_DRAM_THIRD_BANK_SIZE (OCTEON_DRAM_ABOVE_512_END - OCTEON_DRAM_ABOVE_512_START + 1ull)
+
+#endif /* !OCTEON_PCMAP_REGS_H__ */
diff --git a/sys/mips/cavium/octeonreg.h b/sys/mips/cavium/octeonreg.h
new file mode 100644
index 0000000..44e927d
--- /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/std.octeon1 b/sys/mips/cavium/std.octeon1
new file mode 100644
index 0000000..c46d0c7
--- /dev/null
+++ b/sys/mips/cavium/std.octeon1
@@ -0,0 +1,22 @@
+# /*
+# * This product includes software developed by the University of
+# * California, Berkeley and its contributors."
+# */
+# $FreeBSD$
+#
+files "../octeon1/files.octeon1"
+
+#
+#
+#
+cpu CPU_MIPS4KC
+#device pci
+#device ata
+#device atadisk
+
+#device clock
+#device obio
+#device uart
+
+# Kludge
+options TARGET_OCTEON
diff --git a/sys/mips/cavium/uart_bus_octeonusart.c b/sys/mips/cavium/uart_bus_octeonusart.c
new file mode 100644
index 0000000..2f43c77
--- /dev/null
+++ b/sys/mips/cavium/uart_bus_octeonusart.c
@@ -0,0 +1,121 @@
+/*-
+ * 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>
+
+/*
+ * XXXMIPS:
+ */
+#include <mips/octeon1/octeonreg.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;
+
+#if 1
+ /*
+ * 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;
+ if (bus_space_map(sc->sc_bas.bst, OCTEON_UART0ADR, OCTEON_UART_SIZE,
+ 0, &sc->sc_bas.bsh) != 0)
+ return (ENXIO);
+#endif
+ 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 0000000..7b0cdbf
--- /dev/null
+++ b/sys/mips/cavium/uart_cpu_octeonusart.c
@@ -0,0 +1,191 @@
+/*-
+ * 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/octeon1/octeonreg.h>
+#include <mips/octeon1/octeon_pcmap_regs.h>
+
+bus_space_tag_t uart_bus_space_io;
+bus_space_tag_t uart_bus_space_mem;
+
+#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 <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>
+
+/*
+ * 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 << 3)));
+}
+
+static uint16_t
+ou_bs_r_2(void *t, bus_space_handle_t handle, bus_size_t offset)
+{
+
+ return (oct_read64(handle + (offset << 3)));
+}
+
+static uint32_t
+ou_bs_r_4(void *t, bus_space_handle_t handle, bus_size_t offset)
+{
+
+ return (oct_read64(handle + (offset << 3)));
+}
+
+static uint64_t
+ou_bs_r_8(void *t, bus_space_handle_t handle, bus_size_t offset)
+{
+
+ return (oct_read64(handle + (offset << 3)));
+}
+
+static void
+ou_bs_w_1(void *t, bus_space_handle_t bsh, bus_size_t offset, uint8_t value)
+{
+
+ oct_write64(bsh + (offset << 3), 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 << 3), 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 << 3), 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 << 3), value);
+}
+
+static 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;
+ if (bus_space_map(di->bas.bst, OCTEON_UART0ADR, OCTEON_UART_SIZE,
+ 0, &di->bas.bsh) != 0)
+ return (ENXIO);
+ di->bas.regshft = 0;
+ 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 0000000..3b96a68
--- /dev/null
+++ b/sys/mips/cavium/uart_dev_oct16550.c
@@ -0,0 +1,827 @@
+/*-
+ * 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/octeon1/octeon_pcmap_regs.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,
+ .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);
+
+ uint32_t status_bits = mips_rd_status();
+ mips_wr_status(status_bits & ~MIPS_SR_INT_IE);
+ /*
+ * Enable the interrupt in CIU. // UART-x2 @ IP2
+ */
+ ciu_enable_interrupts(0, CIU_INT_0, CIU_EN_0,
+ (!unit) ? CIU_UART_BITS_UART0 : CIU_UART_BITS_UART1, CIU_MIPS_IP2);
+ 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 = octeon_cpu_clock;
+
+ 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);
+}
OpenPOWER on IntegriCloud