Merge from projects/mips to head by hand:

Copy the support files for the Octeon 1 CPU from sys/mips/octeon1 on
the projects/mips side to sys/mips/cavium on the head side to conform
to the other vendor code.  This code was contributed by Cavium to the
project and forward ported by Warner Losh, with some additional code
from Randal Stewart.

# I'll fix the building problems the move creates in a future commit.
This commit is contained in:
Warner Losh 2010-01-09 18:59:03 +00:00
commit 6b06709221
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=201921
25 changed files with 9292 additions and 0 deletions

View File

@ -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)

View File

@ -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)
{
}

View File

@ -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___ */

View File

@ -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);
}
}

View File

@ -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___ */

View File

@ -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);
}

View File

@ -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___ */

View File

@ -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__ */

View File

@ -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);
}

View File

@ -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___ */

File diff suppressed because it is too large Load Diff

View File

@ -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___ */

259
sys/mips/cavium/driveid.h Normal file
View File

@ -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__ */

View File

@ -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

182
sys/mips/cavium/obio.c Normal file
View File

@ -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);

58
sys/mips/cavium/obiovar.h Normal file
View File

@ -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_ */

View File

@ -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);

View File

@ -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__ */

View File

@ -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]);
}

File diff suppressed because it is too large Load Diff

247
sys/mips/cavium/octeonreg.h Normal file
View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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);
}

View File

@ -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);
}