diff options
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); +} |