diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-03-23 15:54:12 +0100 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-03-26 10:40:42 +0200 |
commit | 0cab067f1cd3349907d4cc62e8812f0a5e146d75 (patch) | |
tree | 3f44dd4f8fa5aaca8814ec6c2aec8d1c1c77f414 /bsps | |
parent | bsp/bfin: Move libcpu content to bsps (diff) | |
download | rtems-0cab067f1cd3349907d4cc62e8812f0a5e146d75.tar.bz2 |
bsps/powerpc: Move libcpu content to bsps
This patch is a part of the BSP source reorganization.
Update #3285.
Diffstat (limited to 'bsps')
-rw-r--r-- | bsps/powerpc/gen83xx/dev/gtm.c | 262 | ||||
-rw-r--r-- | bsps/powerpc/gen83xx/dev/mpc83xx_i2cdrv.c | 668 | ||||
-rw-r--r-- | bsps/powerpc/gen83xx/dev/mpc83xx_spidrv.c | 675 | ||||
-rw-r--r-- | bsps/powerpc/shared/net/tsec.c | 1936 |
4 files changed, 3541 insertions, 0 deletions
diff --git a/bsps/powerpc/gen83xx/dev/gtm.c b/bsps/powerpc/gen83xx/dev/gtm.c new file mode 100644 index 0000000000..2f13a1d8cd --- /dev/null +++ b/bsps/powerpc/gen83xx/dev/gtm.c @@ -0,0 +1,262 @@ +/** + * @file + * + * @brief Source file for timer functions. + */ + +/* + * Copyright (c) 2008 + * Embedded Brains GmbH + * Obere Lagerstr. 30 + * D-82178 Puchheim + * Germany + * rtems@embedded-brains.de + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#include <rtems/bspIo.h> + +#include <mpc83xx/mpc83xx.h> +#include <mpc83xx/gtm.h> + +#define RTEMS_STATUS_CHECKS_USE_PRINTK + +#include <rtems/status-checks.h> + +#define MPC83XX_GTM_CHECK_INDEX( timer) \ + if (( timer) < 0 || ( timer) >= MPC83XX_GTM_NUMBER) { \ + return RTEMS_INVALID_NUMBER; \ + } + +#define GTM_MODULE(timer) ((timer)/4) +#define GTM_MODULE_TIMER(timer) ((timer)%4) +#define GTM_HIGH(timer) (GTM_MODULE_TIMER(timer)/2) +#define GTM_LOW(timer) (GTM_MODULE_TIMER(timer)%2) + +#define MPC83XX_GTM_CLOCK_MASK MPC83XX_GTM_CLOCK_EXTERN + +static const uint8_t mpc83xx_gmt_interrupt_vector_table [MPC83XX_GTM_NUMBER] = { 90, 78, 84, 72, 91, 79, 85, 73 }; + +rtems_status_code mpc83xx_gtm_initialize( int timer, int clock) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + rtems_interrupt_level level; + + unsigned mask = 0xfU << (GTM_LOW(timer) * 4); + unsigned flags = 0x3U << (GTM_LOW(timer) * 4); + uint8_t reg = 0; + + MPC83XX_GTM_CHECK_INDEX( timer); + + rtems_interrupt_disable( level); + + reg = mpc83xx.gtm [GTM_MODULE(timer)].gtcfr [GTM_HIGH(timer)].reg; + mpc83xx.gtm [GTM_MODULE(timer)].gtcfr [GTM_HIGH(timer)].reg = + (uint8_t) ((reg & ~mask) | flags); + + mpc83xx.gtm [GTM_MODULE(timer)] + .gt_tim_regs [GTM_HIGH(timer)] + .gtmdr [GTM_LOW(timer)] = 0; + + rtems_interrupt_enable( level); + + sc = mpc83xx_gtm_set_clock( timer, clock); + RTEMS_CHECK_SC( sc, "Set clock"); + + sc = mpc83xx_gtm_set_value( timer, 0); + RTEMS_CHECK_SC( sc, "Set value"); + + sc = mpc83xx_gtm_set_reference( timer, 0); + RTEMS_CHECK_SC( sc, "Set reference"); + + sc = mpc83xx_gtm_set_prescale( timer, 0); + RTEMS_CHECK_SC( sc, "Set prescale"); + + return RTEMS_SUCCESSFUL; +} + +rtems_status_code mpc83xx_gtm_enable_restart( int timer, bool enable) +{ + rtems_interrupt_level level; + MPC83XX_GTM_CHECK_INDEX( timer); + + rtems_interrupt_disable( level); + + if (enable) { + mpc83xx.gtm [GTM_MODULE(timer)] + .gt_tim_regs [GTM_HIGH(timer)] + .gtmdr [GTM_LOW(timer)] |= 0x0008; + } else { + mpc83xx.gtm [GTM_MODULE(timer)] + .gt_tim_regs [GTM_HIGH(timer)] + .gtmdr [GTM_LOW(timer)] &= ~0x0008; + } + + rtems_interrupt_enable( level); + + return RTEMS_SUCCESSFUL; +} + +rtems_status_code mpc83xx_gtm_set_clock( int timer, int clock) +{ + rtems_interrupt_level level; + uint16_t reg = 0; + + MPC83XX_GTM_CHECK_INDEX( timer); + + if (clock & ~MPC83XX_GTM_CLOCK_MASK) { + return RTEMS_INVALID_CLOCK; + } + + rtems_interrupt_disable( level); + + reg = mpc83xx.gtm [GTM_MODULE(timer)] + .gt_tim_regs [GTM_HIGH(timer)] + .gtmdr [GTM_LOW(timer)]; + mpc83xx.gtm [GTM_MODULE(timer)] + .gt_tim_regs [GTM_HIGH(timer)] + .gtmdr [GTM_LOW(timer)] = (reg & ~MPC83XX_GTM_CLOCK_MASK) | clock; + + rtems_interrupt_enable( level); + + return RTEMS_SUCCESSFUL; +} + +rtems_status_code mpc83xx_gtm_get_clock( int timer, int *clock) +{ + MPC83XX_GTM_CHECK_INDEX( timer); + + *clock = mpc83xx.gtm [GTM_MODULE(timer)] + .gt_tim_regs [GTM_HIGH(timer)] + .gtmdr [GTM_LOW(timer)] & MPC83XX_GTM_CLOCK_MASK; + + return RTEMS_SUCCESSFUL; +} + +rtems_status_code mpc83xx_gtm_start( int timer) +{ + rtems_interrupt_level level; + uint8_t flags = 0x2 << (GTM_LOW(timer) * 4); + + MPC83XX_GTM_CHECK_INDEX( timer); + + rtems_interrupt_disable( level); + mpc83xx.gtm [GTM_MODULE(timer)] +.gtcfr [GTM_HIGH(timer)].reg &= ~flags; + rtems_interrupt_enable( level); + + return RTEMS_SUCCESSFUL; +} + +rtems_status_code mpc83xx_gtm_stop( int timer) +{ + rtems_interrupt_level level; + uint8_t flags = 0x2 << (GTM_LOW(timer) * 4); + + MPC83XX_GTM_CHECK_INDEX( timer); + + rtems_interrupt_disable( level); + mpc83xx.gtm [GTM_MODULE(timer)].gtcfr [GTM_HIGH(timer)].reg |= flags; + rtems_interrupt_enable( level); + + return RTEMS_SUCCESSFUL; +} + +rtems_status_code mpc83xx_gtm_set_value( int timer, uint16_t value) +{ + MPC83XX_GTM_CHECK_INDEX( timer); + + mpc83xx.gtm [GTM_MODULE(timer)].gt_tim_regs [GTM_HIGH(timer)].gtcnr [GTM_LOW(timer)] = value; + + return RTEMS_SUCCESSFUL; +} + +rtems_status_code mpc83xx_gtm_get_value( int timer, uint16_t *value) +{ + MPC83XX_GTM_CHECK_INDEX( timer); + + *value = mpc83xx.gtm [GTM_MODULE(timer)].gt_tim_regs [GTM_HIGH(timer)].gtcnr [GTM_LOW(timer)]; + + return RTEMS_SUCCESSFUL; +} + +rtems_status_code mpc83xx_gtm_set_reference( int timer, uint16_t reference) +{ + MPC83XX_GTM_CHECK_INDEX( timer); + + mpc83xx.gtm [GTM_MODULE(timer)].gt_tim_regs [GTM_HIGH(timer)].gtrfr [GTM_LOW(timer)] = reference; + + return RTEMS_SUCCESSFUL; +} + +rtems_status_code mpc83xx_gtm_get_reference( int timer, uint16_t *reference) +{ + MPC83XX_GTM_CHECK_INDEX( timer); + + *reference = mpc83xx.gtm [GTM_MODULE(timer)].gt_tim_regs [GTM_HIGH(timer)].gtrfr [GTM_LOW(timer)]; + + return RTEMS_SUCCESSFUL; +} + +rtems_status_code mpc83xx_gtm_set_prescale( int timer, uint8_t prescale) +{ + MPC83XX_GTM_CHECK_INDEX( timer); + + mpc83xx.gtm [GTM_MODULE(timer)].gtpsr [GTM_MODULE_TIMER(timer)] = prescale; + + return RTEMS_SUCCESSFUL; +} + +rtems_status_code mpc83xx_gtm_get_prescale( int timer, uint8_t *prescale) +{ + MPC83XX_GTM_CHECK_INDEX( timer); + + *prescale = mpc83xx.gtm [GTM_MODULE(timer)].gtpsr [GTM_MODULE_TIMER(timer)]; + + return RTEMS_SUCCESSFUL; +} + +rtems_status_code mpc83xx_gtm_interrupt_get_vector( int timer, rtems_vector_number *vector) +{ + MPC83XX_GTM_CHECK_INDEX( timer); + + *vector = mpc83xx_gmt_interrupt_vector_table [timer]; + + return RTEMS_SUCCESSFUL; +} + +rtems_status_code mpc83xx_gtm_interrupt_enable( int timer) +{ + rtems_interrupt_level level; + MPC83XX_GTM_CHECK_INDEX( timer); + + rtems_interrupt_disable( level); + mpc83xx.gtm [GTM_MODULE(timer)].gt_tim_regs [GTM_HIGH(timer)].gtmdr [GTM_LOW(timer)] |= 0x0010; + rtems_interrupt_enable( level); + + return RTEMS_SUCCESSFUL; +} + +rtems_status_code mpc83xx_gtm_interrupt_disable( int timer) +{ + rtems_interrupt_level level; + MPC83XX_GTM_CHECK_INDEX( timer); + + rtems_interrupt_disable( level); + mpc83xx.gtm [GTM_MODULE(timer)].gt_tim_regs [GTM_HIGH(timer)].gtmdr [GTM_LOW(timer)] &= ~0x0010; + rtems_interrupt_enable( level); + + return RTEMS_SUCCESSFUL; +} + +rtems_status_code mpc83xx_gtm_interrupt_clear( int timer) +{ + MPC83XX_GTM_CHECK_INDEX( timer); + + mpc83xx.gtm [GTM_MODULE(timer)].gtevr [GTM_MODULE_TIMER(timer)] = 0x0002; + + return RTEMS_SUCCESSFUL; +} diff --git a/bsps/powerpc/gen83xx/dev/mpc83xx_i2cdrv.c b/bsps/powerpc/gen83xx/dev/mpc83xx_i2cdrv.c new file mode 100644 index 0000000000..b4c3d477f7 --- /dev/null +++ b/bsps/powerpc/gen83xx/dev/mpc83xx_i2cdrv.c @@ -0,0 +1,668 @@ +/*===============================================================*\ +| Project: RTEMS support for MPC83xx | ++-----------------------------------------------------------------+ +| Copyright (c) 2007 | +| Embedded Brains GmbH | +| Obere Lagerstr. 30 | +| D-82178 Puchheim | +| Germany | +| rtems@embedded-brains.de | ++-----------------------------------------------------------------+ +| The license and distribution terms for this file may be | +| found in the file LICENSE in this distribution or at | +| | +| http://www.rtems.org/license/LICENSE. | +| | ++-----------------------------------------------------------------+ +| this file contains the MPC83xx I2C driver | +\*===============================================================*/ +#include <stdlib.h> +#include <bsp.h> +#include <bsp/irq.h> +#include <mpc83xx/mpc83xx_i2cdrv.h> +#include <rtems/error.h> +#include <rtems/bspIo.h> +#include <errno.h> +#include <rtems/libi2c.h> + +#undef DEBUG + +#if defined(LIBBSP_POWERPC_GEN83XX_BSP_H) + #define I2CCR_MEN (1 << 7) /* module enable */ +#elif defined(LIBBSP_POWERPC_MPC55XXEVB_BSP_H) + #define I2CCR_MDIS (1 << 7) /* module disable */ +#endif +#define I2CCR_MIEN (1 << 6) /* module interrupt enable */ +#define I2CCR_MSTA (1 << 5) /* 0->1 generates a start condiiton, 1->0 a stop */ +#define I2CCR_MTX (1 << 4) /* 0 = receive mode, 1 = transmit mode */ +#define I2CCR_TXAK (1 << 3) /* 0 = send ack 1 = send nak during receive */ +#define I2CCR_RSTA (1 << 2) /* 1 = send repeated start condition */ +#define I2CCR_BCST (1 << 0) /* 0 = disable 1 = enable broadcast accept */ + +#define I2CSR_MCF (1 << 7) /* data transfer (0=transfer in progres) */ +#define I2CSR_MAAS (1 << 6) /* addessed as slave */ +#define I2CSR_MBB (1 << 5) /* bus busy */ +#define I2CSR_MAL (1 << 4) /* arbitration lost */ +#define I2CSR_BCSTM (1 << 3) /* broadcast match */ +#define I2CSR_SRW (1 << 2) /* slave read/write */ +#define I2CSR_MIF (1 << 1) /* module interrupt */ +#define I2CSR_RXAK (1 << 0) /* receive acknowledge */ + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static rtems_status_code mpc83xx_i2c_find_clock_divider +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| determine proper divider value | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + uint8_t *result, /* result value */ + int divider /* requested divider */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| o = ok or error code | +\*=========================================================================*/ +{ + int i; + int fdr_val; + rtems_status_code sc = RTEMS_SUCCESSFUL; + struct { + int divider; + int fdr_val; + } dividers[] ={ +#if defined(LIBBSP_POWERPC_GEN83XX_BSP_H) + { 256,0x20 }, { 288,0x21 }, { 320,0x22 }, { 352,0x23 }, + { 384,0x00 }, { 416,0x01 }, { 448,0x25 }, { 480,0x02 }, + { 512,0x26 }, { 576,0x03 }, { 640,0x04 }, { 704,0x05 }, + { 768,0x29 }, { 832,0x06 }, { 896,0x2a }, { 1024,0x07 }, + { 1152,0x08 }, { 1280,0x09 }, { 1536,0x0A }, { 1792,0x2E }, + { 1920,0x0B }, { 2048,0x2F }, { 2304,0x0C }, { 2560,0x0D }, + { 3072,0x0E }, { 3584,0x32 }, { 3840,0x0F }, { 4096,0x33 }, + { 4608,0x10 }, { 5120,0x11 }, { 6144,0x12 }, { 7168,0x36 }, + { 7680,0x13 }, { 8192,0x37 }, { 9216,0x14 }, {10240,0x15 }, + {12288,0x16 }, {14336,0x3A }, {15360,0x17 }, {16384,0x3B }, + {18432,0x18 }, {20480,0x19 }, {24576,0x1A }, {28672,0x3E }, + {30720,0x1B }, {32768,0x3F }, {36864,0x1C }, {40960,0x1D }, + {49152,0x1E }, {61440,0x1F } +#elif defined(LIBBSP_POWERPC_MPC55XXEVB_BSP_H) + { 768, 0x31 } +#endif + }; + + if (divider <= 0) { + sc = RTEMS_INVALID_NUMBER; + } + + if (sc == RTEMS_SUCCESSFUL) { + sc = RTEMS_INVALID_NUMBER; + for (i = 0, fdr_val = -1; i < sizeof(dividers)/sizeof(dividers[0]); i++) { + fdr_val = dividers[i].fdr_val; + if (dividers[i].divider >= divider) + { + sc = RTEMS_SUCCESSFUL; + *result = fdr_val; + break; + } + } + } + return sc; +} + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static int mpc83xx_i2c_wait +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| wait for i2c to become idle | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + mpc83xx_i2c_softc_t *softc_ptr, /* handle */ + uint8_t desired_status, /* desired status word */ + uint8_t status_mask /* status word mask */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| o = ok or error code | +\*=========================================================================*/ +{ + uint8_t act_status; + rtems_status_code rc; + uint32_t tout; + +#if defined(DEBUG) + printk("mpc83xx_i2c_wait called... "); +#endif + + if (softc_ptr->initialized) { + /* + * enable interrupt mask + */ + softc_ptr->reg_ptr->i2ccr |= I2CCR_MIEN; + rc = rtems_semaphore_obtain(softc_ptr->irq_sema_id,RTEMS_WAIT,100); + if (rc != RTEMS_SUCCESSFUL) { + return rc; + } + } + else { + tout = 0; + do { + if (tout++ > 1000000) { +#if defined(DEBUG) + printk("... exit with RTEMS_TIMEOUT\r\n"); +#endif + return RTEMS_TIMEOUT; + } + } while (!(softc_ptr->reg_ptr->i2csr & I2CSR_MIF)); + } + softc_ptr->reg_ptr->i2ccr &= ~I2CCR_MIEN; + + act_status = softc_ptr->reg_ptr->i2csr; + if ((act_status & status_mask) != desired_status) { +#if defined(DEBUG) + printk("... exit with RTEMS_IO_ERROR\r\n"); +#endif + return RTEMS_IO_ERROR; + } +#if defined(DEBUG) + printk("... exit OK\r\n"); +#endif + return RTEMS_SUCCESSFUL; +} + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static void mpc83xx_i2c_irq_handler +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| handle interrupts | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + rtems_irq_hdl_param handle /* handle, is softc_ptr structure */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| <none> | +\*=========================================================================*/ +{ + mpc83xx_i2c_softc_t *softc_ptr = (mpc83xx_i2c_softc_t *)handle; + + /* + * clear IRQ flag + */ + #if defined(LIBBSP_POWERPC_GEN83XX_BSP_H) + softc_ptr->reg_ptr->i2csr &= ~I2CSR_MIF; + #elif defined(LIBBSP_POWERPC_MPC55XXEVB_BSP_H) + softc_ptr->reg_ptr->i2csr = I2CSR_MIF; + #endif + + /* + * disable interrupt mask + */ + softc_ptr->reg_ptr->i2ccr &= ~I2CCR_MIEN; + if (softc_ptr->initialized) { + rtems_semaphore_release(softc_ptr->irq_sema_id); + } +} + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static void mpc83xx_i2c_irq_on_off +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| enable/disable interrupts (void, handled at different position) | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + const + rtems_irq_connect_data *irq_conn_data /* irq connect data */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| <none> | +\*=========================================================================*/ +{ +} + + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static int mpc83xx_i2c_irq_isOn +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| check state of interrupts, void, done differently | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + const + rtems_irq_connect_data *irq_conn_data /* irq connect data */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| TRUE, if enabled | +\*=========================================================================*/ +{ + return (TRUE); +} + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static void mpc83xx_i2c_install_irq_handler +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| (un-)install the interrupt handler | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + mpc83xx_i2c_softc_t *softc_ptr, /* ptr to control structure */ + int install /* TRUE: install, FALSE: remove */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| <none> | +\*=========================================================================*/ +{ + rtems_status_code rc = RTEMS_SUCCESSFUL; + + rtems_irq_connect_data irq_conn_data = { + softc_ptr->irq_number, + mpc83xx_i2c_irq_handler, /* rtems_irq_hdl */ + (rtems_irq_hdl_param)softc_ptr, /* (rtems_irq_hdl_param) */ + mpc83xx_i2c_irq_on_off, /* (rtems_irq_enable) */ + mpc83xx_i2c_irq_on_off, /* (rtems_irq_disable) */ + mpc83xx_i2c_irq_isOn /* (rtems_irq_is_enabled) */ + }; + + /* + * (un-)install handler for I2C device + */ + if (install) { + /* + * create semaphore for IRQ synchronization + */ + rc = rtems_semaphore_create(rtems_build_name('i','2','c','s'), + 0, + RTEMS_FIFO + | RTEMS_SIMPLE_BINARY_SEMAPHORE, + 0, + &softc_ptr->irq_sema_id); + if (rc != RTEMS_SUCCESSFUL) { + rtems_panic("I2C: cannot create semaphore"); + } + if (!BSP_install_rtems_irq_handler (&irq_conn_data)) { + rtems_panic("I2C: cannot install IRQ handler"); + } + } + else { + if (!BSP_remove_rtems_irq_handler (&irq_conn_data)) { + rtems_panic("I2C: cannot uninstall IRQ handler"); + } + /* + * delete sync semaphore + */ + if (softc_ptr->irq_sema_id != 0) { + rc = rtems_semaphore_delete(softc_ptr->irq_sema_id); + if (rc != RTEMS_SUCCESSFUL) { + rtems_panic("I2C: cannot delete semaphore"); + } + } + } +} + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static rtems_status_code mpc83xx_i2c_init +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| initialize the driver | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + rtems_libi2c_bus_t *bh /* bus specifier structure */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| o = ok or error code | +\*=========================================================================*/ +{ + mpc83xx_i2c_softc_t *softc_ptr = &(((mpc83xx_i2c_desc_t *)(bh))->softc); + uint8_t fdr_val; + int errval; +#if defined(DEBUG) + printk("mpc83xx_i2c_init called... "); +#endif + /* + * init HW registers + */ + /* + * init frequency divider to 100kHz + */ + errval = mpc83xx_i2c_find_clock_divider(&fdr_val, + softc_ptr->base_frq/100000); + if (errval != 0) { + return errval; + } + softc_ptr->reg_ptr->i2cfdr = fdr_val; + /* + * init digital filter sampling rate + */ + softc_ptr->reg_ptr->i2cdfsrr = 0x10 ; /* no special filtering needed */ + /* + * set own slave address to broadcast (0x00) + */ + softc_ptr->reg_ptr->i2cadr = 0x00 ; + + /* + * set control register to module enable + */ + #if defined(LIBBSP_POWERPC_GEN83XX_BSP_H) + softc_ptr->reg_ptr->i2ccr = I2CCR_MEN; + #elif defined(LIBBSP_POWERPC_MPC55XXEVB_BSP_H) + softc_ptr->reg_ptr->i2ccr = 0; + #endif + + /* + * init interrupt stuff + */ + mpc83xx_i2c_install_irq_handler(softc_ptr,TRUE); + + /* + * mark, that we have initialized + */ + softc_ptr->initialized = TRUE; +#if defined(DEBUG) + printk("... exit OK\r\n"); +#endif + return RTEMS_SUCCESSFUL; +} + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static rtems_status_code mpc83xx_i2c_send_start +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| send a start condition to bus | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + rtems_libi2c_bus_t *bh /* bus specifier structure */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| o = ok or error code | +\*=========================================================================*/ +{ + mpc83xx_i2c_softc_t *softc_ptr = &(((mpc83xx_i2c_desc_t *)(bh))->softc); + +#if defined(DEBUG) + printk("mpc83xx_i2c_send_start called... "); +#endif + if (0 != (softc_ptr->reg_ptr->i2ccr & I2CCR_MSTA)) { + /* + * already started, so send a "repeated start" + */ + softc_ptr->reg_ptr->i2ccr |= I2CCR_RSTA; + } + else { + softc_ptr->reg_ptr->i2ccr |= I2CCR_MSTA; + } + +#if defined(DEBUG) + printk("... exit OK\r\n"); +#endif + return 0; +} + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static rtems_status_code mpc83xx_i2c_send_stop +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| send a stop condition to bus | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + rtems_libi2c_bus_t *bh /* bus specifier structure */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| o = ok or error code | +\*=========================================================================*/ +{ + mpc83xx_i2c_softc_t *softc_ptr = &(((mpc83xx_i2c_desc_t *)(bh))->softc); + +#if defined(DEBUG) + printk("mpc83xx_i2c_send_stop called... "); +#endif + softc_ptr->reg_ptr->i2ccr &= ~I2CCR_MSTA; + /* + * wait, 'til stop has been executed + */ + while (0 != (softc_ptr->reg_ptr->i2csr & I2CSR_MBB)) { + rtems_task_wake_after(RTEMS_YIELD_PROCESSOR); + } +#if defined(DEBUG) + printk("... exit OK\r\n"); +#endif + return 0; +} + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static rtems_status_code mpc83xx_i2c_send_addr +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| address a slave device on the bus | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + rtems_libi2c_bus_t *bh, /* bus specifier structure */ + uint32_t addr, /* address to send on bus */ + int rw /* 0=write,1=read */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| o = ok or error code | +\*=========================================================================*/ +{ + mpc83xx_i2c_softc_t *softc_ptr = &(((mpc83xx_i2c_desc_t *)(bh))->softc); + uint8_t addr_byte; + rtems_status_code rc; + +#if defined(DEBUG) + printk("mpc83xx_i2c_send_addr called... "); +#endif + softc_ptr->reg_ptr->i2ccr |= I2CCR_MTX; + /* + * determine, whether short or long address is needed, determine rd/wr + */ + if (addr > 0x7f) { + addr_byte = (0xf0 + | ((addr >> 7) & 0x06) + | ((rw) ? 1 : 0)); + /* + * send first byte + */ + softc_ptr->reg_ptr->i2cdr = addr_byte; + /* + * wait for successful transfer + */ + rc = mpc83xx_i2c_wait(softc_ptr, I2CSR_MCF, I2CSR_MCF | I2CSR_RXAK); + if (rc != RTEMS_SUCCESSFUL) { +#if defined(DEBUG) + printk("... exit rc=%d\r\n",rc); +#endif + return rc; + } + } + /* + * send (final) byte + */ + addr_byte = ((addr << 1) + | ((rw) ? 1 : 0)); + + softc_ptr->reg_ptr->i2cdr = addr_byte; + /* + * wait for successful transfer + */ + rc = mpc83xx_i2c_wait(softc_ptr, I2CSR_MCF, I2CSR_MCF | I2CSR_RXAK); + +#if defined(DEBUG) + printk("... exit rc=%d\r\n",rc); +#endif + return rc; +} + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static int mpc83xx_i2c_read_bytes +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| receive some bytes from I2C device | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + rtems_libi2c_bus_t *bh, /* bus specifier structure */ + unsigned char *buf, /* buffer to store bytes */ + int len /* number of bytes to receive */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| number of bytes received or (negative) error code | +\*=========================================================================*/ +{ + mpc83xx_i2c_softc_t *softc_ptr = &(((mpc83xx_i2c_desc_t *)(bh))->softc); + rtems_status_code rc; + unsigned char *p = buf; + +#if defined(DEBUG) + printk("mpc83xx_i2c_read_bytes called... "); +#endif + softc_ptr->reg_ptr->i2ccr &= ~I2CCR_MTX; + softc_ptr->reg_ptr->i2ccr &= ~I2CCR_TXAK; + /* + * FIXME: do we need to deactivate TXAK from the start, + * when only one byte is to be received? + */ + /* + * we need a dummy transfer here to start the first read + */ + softc_ptr->reg_ptr->i2cdr; + + while (len-- > 0) { + if (len == 0) { + /* + * last byte is not acknowledged + */ + softc_ptr->reg_ptr->i2ccr |= I2CCR_TXAK; + } + /* + * wait 'til end of transfer + */ + rc = mpc83xx_i2c_wait(softc_ptr, I2CSR_MCF, I2CSR_MCF); + if (rc != RTEMS_SUCCESSFUL) { +#if defined(DEBUG) + printk("... exit rc=%d\r\n",-rc); +#endif + return -rc; + } + *p++ = softc_ptr->reg_ptr->i2cdr; + + } + + /* + * wait 'til end of last transfer + */ + rc = mpc83xx_i2c_wait(softc_ptr, I2CSR_MCF, I2CSR_MCF); + +#if defined(DEBUG) + printk("... exit OK, rc=%d\r\n",p-buf); +#endif + return p - buf; +} + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static int mpc83xx_i2c_write_bytes +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| send some bytes to I2C device | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + rtems_libi2c_bus_t *bh, /* bus specifier structure */ + unsigned char *buf, /* buffer to send */ + int len /* number of bytes to send */ + +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| number of bytes sent or (negative) error code | +\*=========================================================================*/ +{ + mpc83xx_i2c_softc_t *softc_ptr = &(((mpc83xx_i2c_desc_t *)(bh))->softc); + rtems_status_code rc; + unsigned char *p = buf; + +#if defined(DEBUG) + printk("mpc83xx_i2c_write_bytes called... "); +#endif + softc_ptr->reg_ptr->i2ccr = + (softc_ptr->reg_ptr->i2ccr & ~I2CCR_TXAK) | I2CCR_MTX; + while (len-- > 0) { + int rxack = len != 0 ? I2CSR_RXAK : 0; + + softc_ptr->reg_ptr->i2cdr = *p++; + /* + * wait 'til end of transfer + */ + rc = mpc83xx_i2c_wait(softc_ptr, I2CSR_MCF, I2CSR_MCF | rxack); + if (rc != RTEMS_SUCCESSFUL) { +#if defined(DEBUG) + printk("... exit rc=%d\r\n",-rc); +#endif + return -rc; + } + } +#if defined(DEBUG) + printk("... exit OK, rc=%d\r\n",p-buf); +#endif + return p - buf; +} + +rtems_libi2c_bus_ops_t mpc83xx_i2c_ops = { + .init = mpc83xx_i2c_init, + .send_start = mpc83xx_i2c_send_start, + .send_stop = mpc83xx_i2c_send_stop, + .send_addr = mpc83xx_i2c_send_addr, + .read_bytes = mpc83xx_i2c_read_bytes, + .write_bytes = mpc83xx_i2c_write_bytes, +}; + diff --git a/bsps/powerpc/gen83xx/dev/mpc83xx_spidrv.c b/bsps/powerpc/gen83xx/dev/mpc83xx_spidrv.c new file mode 100644 index 0000000000..5534b4b72a --- /dev/null +++ b/bsps/powerpc/gen83xx/dev/mpc83xx_spidrv.c @@ -0,0 +1,675 @@ +/*===============================================================*\ +| Project: RTEMS support for MPC83xx | ++-----------------------------------------------------------------+ +| Copyright (c) 2007 | +| Embedded Brains GmbH | +| Obere Lagerstr. 30 | +| D-82178 Puchheim | +| Germany | +| rtems@embedded-brains.de | ++-----------------------------------------------------------------+ +| The license and distribution terms for this file may be | +| found in the file LICENSE in this distribution or at | +| | +| http://www.rtems.org/license/LICENSE. | +| | ++-----------------------------------------------------------------+ +| this file contains the MPC83xx SPI driver | +| NOTE: it uses the same API as the I2C driver | +\*===============================================================*/ +#include <stdlib.h> +#include <bsp.h> +#include <bsp/irq.h> +#include <mpc83xx/mpc83xx.h> +#include <mpc83xx/mpc83xx_spidrv.h> +#include <rtems/error.h> +#include <rtems/bspIo.h> +#include <errno.h> +#include <rtems/libi2c.h> + +#undef DEBUG + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static rtems_status_code mpc83xx_spi_baud_to_mode +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| determine proper divider value | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + uint32_t baudrate, /* desired baudrate */ + uint32_t base_frq, /* input frequency */ + uint32_t *spimode /* result value */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| o = ok or error code | +\*=========================================================================*/ +{ + uint32_t divider; + uint32_t tmpmode = 0; + /* + * determine clock divider and DIV16 bit + */ + divider = (base_frq+baudrate-1)/baudrate; + if (divider > 64) { + tmpmode = MPC83XX_SPIMODE_DIV16; + divider /= 16; + } + if ((divider < 1) || + (divider > 64)) { + return RTEMS_INVALID_NUMBER; + } + else { + tmpmode |= MPC83XX_SPIMODE_PM(divider/4-1); + } + *spimode = tmpmode; + return RTEMS_SUCCESSFUL; +} + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static rtems_status_code mpc83xx_spi_char_mode +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| determine proper value for character size | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + mpc83xx_spi_softc_t *softc_ptr, /* handle */ + uint32_t bits_per_char, /* bits per character */ + bool lsb_first, /* TRUE: send LSB first */ + uint32_t *spimode /* result value */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| o = ok or error code | +\*=========================================================================*/ +{ + uint32_t tmpmode; + + if (bits_per_char == 32) { + tmpmode = 0; + softc_ptr->bytes_per_char = 4; + softc_ptr->bit_shift = 0; + } + else { + if (lsb_first) { + /* + * non-reversed data (LSB first): 4..16 bits valid + * always aligned to bit 16 of data register + */ + if ((bits_per_char >= 4) && + (bits_per_char <= 16)) { + tmpmode = MPC83XX_SPIMODE_LEN( bits_per_char-1); + softc_ptr->bytes_per_char = (bits_per_char > 8) ? 2 : 1; + softc_ptr->bit_shift = 16-bits_per_char; + } + else { + return RTEMS_INVALID_NUMBER; + } + } + else { + /* + * reversed data (MSB first): only 8/16/32 bits valid, + * always in lowest bits of data register + */ + if (bits_per_char == 8) { + tmpmode = MPC83XX_SPIMODE_LEN(8-1); + softc_ptr->bytes_per_char = 1; + softc_ptr->bit_shift = 0; + } + else if (bits_per_char == 16) { + tmpmode = MPC83XX_SPIMODE_LEN(16-1); + softc_ptr->bytes_per_char = 2; + softc_ptr->bit_shift = 0; + } + else { + return RTEMS_INVALID_NUMBER; + } + } + } + + *spimode = tmpmode; + return 0; +} + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static int mpc83xx_spi_wait +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| wait for spi to become idle | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + mpc83xx_spi_softc_t *softc_ptr, /* handle */ + uint32_t irq_mask, /* irq mask to use */ + uint32_t desired_status, /* desired status word */ + uint32_t status_mask /* status word mask */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| o = ok or error code | +\*=========================================================================*/ +{ + uint32_t act_status; + rtems_status_code rc; + uint32_t tout; + +#if defined(DEBUG) + printk("mpc83xx_spi_wait called... "); +#endif + if (softc_ptr->initialized) { + /* + * allow interrupts, when receiver is not empty + */ + softc_ptr->reg_ptr->spim = irq_mask; + rc = rtems_semaphore_obtain(softc_ptr->irq_sema_id,RTEMS_WAIT,100); + if (rc != RTEMS_SUCCESSFUL) { + return rc; + } + } + else { + tout = 0; + do { + if (tout++ > 1000000) { +#if defined(DEBUG) + printk("... exit with RTEMS_TIMEOUT\r\n"); +#endif + return RTEMS_TIMEOUT; + } + /* + * wait for SPI to terminate + */ + } while (!(softc_ptr->reg_ptr->spie & MPC83XX_SPIE_NE)); + } + + act_status = softc_ptr->reg_ptr->spie; + if ((act_status & status_mask)!= desired_status) { +#if defined(DEBUG) + printk("... exit with RTEMS_IO_ERROR," + "act_status=0x%04x,mask=0x%04x,desired_status=0x%04x\r\n", + act_status,status_mask,desired_status); +#endif + return RTEMS_IO_ERROR; + } +#if defined(DEBUG) + printk("... exit OK\r\n"); +#endif + return RTEMS_SUCCESSFUL; +} + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static void mpc83xx_spi_irq_handler +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| handle interrupts | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + rtems_irq_hdl_param handle /* handle, is softc_ptr structure */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| <none> | +\*=========================================================================*/ +{ + mpc83xx_spi_softc_t *softc_ptr = (mpc83xx_spi_softc_t *)handle; + + /* + * disable interrupt mask + */ + softc_ptr->reg_ptr->spim = 0; + if (softc_ptr->initialized) { + rtems_semaphore_release(softc_ptr->irq_sema_id); + } +} + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static void mpc83xx_spi_irq_on_off +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| enable/disable interrupts (void, handled at different position) | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + const + rtems_irq_connect_data *irq_conn_data /* irq connect data */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| <none> | +\*=========================================================================*/ +{ +} + + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static int mpc83xx_spi_irq_isOn +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| check state of interrupts, void, done differently | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + const + rtems_irq_connect_data *irq_conn_data /* irq connect data */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| TRUE, if enabled | +\*=========================================================================*/ +{ + return (TRUE); +} + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static void mpc83xx_spi_install_irq_handler +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| (un-)install the interrupt handler | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + mpc83xx_spi_softc_t *softc_ptr, /* ptr to control structure */ + int install /* TRUE: install, FALSE: remove */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| <none> | +\*=========================================================================*/ +{ + rtems_status_code rc = RTEMS_SUCCESSFUL; + + rtems_irq_connect_data irq_conn_data = { + softc_ptr->irq_number, + mpc83xx_spi_irq_handler, /* rtems_irq_hdl */ + (rtems_irq_hdl_param)softc_ptr, /* (rtems_irq_hdl_param) */ + mpc83xx_spi_irq_on_off, /* (rtems_irq_enable) */ + mpc83xx_spi_irq_on_off, /* (rtems_irq_disable) */ + mpc83xx_spi_irq_isOn /* (rtems_irq_is_enabled) */ + }; + + /* + * (un-)install handler for SPI device + */ + if (install) { + /* + * create semaphore for IRQ synchronization + */ + rc = rtems_semaphore_create(rtems_build_name('s','p','i','s'), + 0, + RTEMS_FIFO + | RTEMS_SIMPLE_BINARY_SEMAPHORE, + 0, + &softc_ptr->irq_sema_id); + if (rc != RTEMS_SUCCESSFUL) { + rtems_panic("SPI: cannot create semaphore"); + } + if (!BSP_install_rtems_irq_handler (&irq_conn_data)) { + rtems_panic("SPI: cannot install IRQ handler"); + } + } + else { + if (!BSP_remove_rtems_irq_handler (&irq_conn_data)) { + rtems_panic("SPI: cannot uninstall IRQ handler"); + } + /* + * delete sync semaphore + */ + if (softc_ptr->irq_sema_id != 0) { + rc = rtems_semaphore_delete(softc_ptr->irq_sema_id); + if (rc != RTEMS_SUCCESSFUL) { + rtems_panic("SPI: cannot delete semaphore"); + } + } + } +} + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +rtems_status_code mpc83xx_spi_init +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| initialize the driver | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + rtems_libi2c_bus_t *bh /* bus specifier structure */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| o = ok or error code | +\*=========================================================================*/ +{ + mpc83xx_spi_softc_t *softc_ptr = &(((mpc83xx_spi_desc_t *)(bh))->softc); +#if defined(DEBUG) + printk("mpc83xx_spi_init called... "); +#endif + /* + * init HW registers: + */ + /* + * FIXME: set default mode in SPIM + */ + + /* + * init interrupt stuff + */ + mpc83xx_spi_install_irq_handler(softc_ptr,TRUE); + + /* + * mark, that we have initialized + */ + softc_ptr->initialized = TRUE; +#if defined(DEBUG) + printk("... exit OK\r\n"); +#endif + return RTEMS_SUCCESSFUL; +} + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +int mpc83xx_spi_read_write_bytes +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| transmit/receive some bytes from SPI device | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + rtems_libi2c_bus_t *bh, /* bus specifier structure */ + unsigned char *rbuf, /* buffer to store bytes */ + const unsigned char *tbuf, /* buffer to send bytes */ + int len /* number of bytes to transceive */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| number of bytes received or (negative) error code | +\*=========================================================================*/ +{ + mpc83xx_spi_softc_t *softc_ptr = &(((mpc83xx_spi_desc_t *)(bh))->softc); + rtems_status_code rc; + int bc = 0; + int bytes_per_char = softc_ptr->bytes_per_char; + int bit_shift = softc_ptr->bit_shift; + uint32_t spird_val; + +#if defined(DEBUG) + printk("mpc83xx_spi_read_write_bytes called... "); +#endif + + while (len > bytes_per_char-1) { + len -= bytes_per_char; + /* + * mark last byte in SPCOM + */ +#if defined(USE_LAST_BIT) + softc_ptr->reg_ptr->spcom = (len < bytes_per_char) ? MPC83XX_SPCOM_LST : 0; +#else + softc_ptr->reg_ptr->spcom = 0; +#endif + if (tbuf == NULL) { + /* + * perform idle char write to read byte + */ + softc_ptr->reg_ptr->spitd = softc_ptr->idle_char << bit_shift; + } + else { + switch(bytes_per_char) { + case 1: + softc_ptr->reg_ptr->spitd = (*(uint8_t *)tbuf) << bit_shift; + break; + case 2: + softc_ptr->reg_ptr->spitd = (*(uint16_t *)tbuf) << bit_shift; + break; + case 4: + softc_ptr->reg_ptr->spitd = (*(uint32_t *)tbuf) << bit_shift; + break; + } + tbuf += softc_ptr->bytes_per_char; + } + /* + * wait 'til end of transfer + */ +#if defined(USE_LAST_BIT) + rc = mpc83xx_spi_wait(softc_ptr, + ((len == 0) + ? MPC83XX_SPIE_LT + : MPC83XX_SPIE_NE), + ((len == 0) + ? MPC83XX_SPIE_LT + : MPC83XX_SPIE_NF) + | MPC83XX_SPIE_NE, + MPC83XX_SPIE_LT + | MPC83XX_SPIE_OV + | MPC83XX_SPIE_UN + | MPC83XX_SPIE_NE + | MPC83XX_SPIE_NF); + if (len == 0) { + /* + * clear the "last transfer complete" event + */ + softc_ptr->reg_ptr->spie = MPC83XX_SPIE_LT; + } +#else + rc = mpc83xx_spi_wait(softc_ptr, + MPC83XX_SPIE_NE, + MPC83XX_SPIE_NF + | MPC83XX_SPIE_NE, + MPC83XX_SPIE_OV + | MPC83XX_SPIE_UN + | MPC83XX_SPIE_NE + | MPC83XX_SPIE_NF); +#endif + if (rc != RTEMS_SUCCESSFUL) { +#if defined(DEBUG) + printk("... exit rc=%d\r\n",-rc); +#endif + return -rc; + } + spird_val = softc_ptr->reg_ptr->spird; + if (rbuf != NULL) { + switch(bytes_per_char) { + case 1: + (*(uint8_t *)rbuf) = spird_val >> bit_shift; + break; + case 2: + (*(uint16_t *)rbuf) = spird_val >> bit_shift; + break; + case 4: + (*(uint32_t *)rbuf) = spird_val >> bit_shift; + break; + } + rbuf += bytes_per_char; + } + bc += bytes_per_char; + } +#if defined(DEBUG) + printk("... exit OK, rc=%d\r\n",bc); +#endif + return bc; +} + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +int mpc83xx_spi_read_bytes +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| receive some bytes from SPI device | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + rtems_libi2c_bus_t *bh, /* bus specifier structure */ + unsigned char *buf, /* buffer to store bytes */ + int len /* number of bytes to receive */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| number of bytes received or (negative) error code | +\*=========================================================================*/ +{ + return mpc83xx_spi_read_write_bytes(bh,buf,NULL,len); +} + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +int mpc83xx_spi_write_bytes +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| send some bytes to SPI device | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + rtems_libi2c_bus_t *bh, /* bus specifier structure */ + unsigned char *buf, /* buffer to send */ + int len /* number of bytes to send */ + +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| number of bytes sent or (negative) error code | +\*=========================================================================*/ +{ + return mpc83xx_spi_read_write_bytes(bh,NULL,buf,len); +} + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +rtems_status_code mpc83xx_spi_set_tfr_mode +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| set SPI to desired baudrate/clock mode/character mode | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + rtems_libi2c_bus_t *bh, /* bus specifier structure */ + const rtems_libi2c_tfr_mode_t *tfr_mode /* transfer mode info */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| rtems_status_code | +\*=========================================================================*/ +{ + mpc83xx_spi_softc_t *softc_ptr = &(((mpc83xx_spi_desc_t *)(bh))->softc); + uint32_t spimode_baud,spimode; + rtems_status_code rc = RTEMS_SUCCESSFUL; + + /* Set idle character */ + softc_ptr->idle_char = tfr_mode->idle_char; + + /* + * FIXME: set proper mode + */ + if (rc == RTEMS_SUCCESSFUL) { + rc = mpc83xx_spi_baud_to_mode(tfr_mode->baudrate, + softc_ptr->base_frq, + &spimode_baud); + } + if (rc == RTEMS_SUCCESSFUL) { + rc = mpc83xx_spi_char_mode(softc_ptr, + tfr_mode->bits_per_char, + tfr_mode->lsb_first, + &spimode); + } + if (rc == RTEMS_SUCCESSFUL) { + spimode |= spimode_baud; + spimode |= MPC83XX_SPIMODE_M_S; /* set master mode */ + if (!tfr_mode->lsb_first) { + spimode |= MPC83XX_SPIMODE_REV; + } + if (tfr_mode->clock_inv) { + spimode |= MPC83XX_SPIMODE_CI; + } + if (tfr_mode->clock_phs) { + spimode |= MPC83XX_SPIMODE_CP; + } + } + + if (rc == RTEMS_SUCCESSFUL) { + /* + * disable SPI + */ + softc_ptr->reg_ptr->spmode &= ~MPC83XX_SPIMODE_EN; + /* + * set new mode and reenable SPI + */ + softc_ptr->reg_ptr->spmode = spimode | MPC83XX_SPIMODE_EN; + } + return rc; +} + + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +int mpc83xx_spi_ioctl +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| perform selected ioctl function for SPI | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + rtems_libi2c_bus_t *bh, /* bus specifier structure */ + int cmd, /* ioctl command code */ + void *arg /* additional argument array */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| rtems_status_code | +\*=========================================================================*/ +{ + int ret_val = -1; + + switch(cmd) { + case RTEMS_LIBI2C_IOCTL_SET_TFRMODE: + ret_val = + -mpc83xx_spi_set_tfr_mode(bh, + (const rtems_libi2c_tfr_mode_t *)arg); + break; + case RTEMS_LIBI2C_IOCTL_READ_WRITE: + ret_val = + mpc83xx_spi_read_write_bytes(bh, + ((rtems_libi2c_read_write_t *)arg)->rd_buf, + ((rtems_libi2c_read_write_t *)arg)->wr_buf, + ((rtems_libi2c_read_write_t *)arg)->byte_cnt); + break; + default: + ret_val = -RTEMS_NOT_DEFINED; + break; + } + return ret_val; +} + + + diff --git a/bsps/powerpc/shared/net/tsec.c b/bsps/powerpc/shared/net/tsec.c new file mode 100644 index 0000000000..ea1c29052e --- /dev/null +++ b/bsps/powerpc/shared/net/tsec.c @@ -0,0 +1,1936 @@ +/*===============================================================*\ +| Project: RTEMS support for MPC83xx | ++-----------------------------------------------------------------+ +| Copyright (c) 2007 | +| Embedded Brains GmbH | +| Obere Lagerstr. 30 | +| D-82178 Puchheim | +| Germany | +| rtems@embedded-brains.de | ++-----------------------------------------------------------------+ +| The license and distribution terms for this file may be | +| found in the file LICENSE in this distribution or at | +| | +| http://www.rtems.org/license/LICENSE. | +| | ++-----------------------------------------------------------------+ +| this file contains the MPC83xx TSEC networking driver | +\*===============================================================*/ + +#define __INSIDE_RTEMS_BSD_TCPIP_STACK__ + +#include <stdlib.h> +#include <bsp.h> +#include <bsp/irq.h> +#include <bsp/tsec.h> +#include <rtems/error.h> +#include <rtems/bspIo.h> +#include <rtems/rtems_bsdnet.h> +#include <rtems/rtems_mii_ioctl.h> +#include <errno.h> + +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <sys/mbuf.h> +#include <net/if.h> +#include <net/if_arp.h> +#include <netinet/in.h> +#include <netinet/if_ether.h> +#include <stdio.h> + +#define CLREVENT_IN_IRQ + +#define TSEC_WATCHDOG_TIMEOUT 5 /* check media every 5 seconds */ + +#ifdef DEBUG + #define PF(fmt, ...) printk("%s: " fmt, __func__, ##__VA_ARGS__) +#else + #define PF(...) +#endif + +/* + * Device data + */ +struct tsec_struct { + struct arpcom arpcom; + int acceptBroadcast; + + /* + * HW links: (filled from rtems_bsdnet_ifconfig + */ + volatile tsec_registers *reg_ptr; /* pointer to TSEC register block */ + volatile tsec_registers *mdio_ptr; /* pointer to TSEC register block which is responsible for MDIO communication */ + rtems_irq_number irq_num_tx; + rtems_irq_number irq_num_rx; + rtems_irq_number irq_num_err; + + rtems_interrupt_lock lock; + + /* + * BD management + */ + int rxBdCount; + int txBdCount; + PQBufferDescriptor_t *Rx_Frst_BD; + PQBufferDescriptor_t *Rx_Last_BD; + PQBufferDescriptor_t *Rx_NxtUsed_BD; /* First BD, which is in Use */ + PQBufferDescriptor_t *Rx_NxtFill_BD; /* BD to be filled next */ + struct mbuf **Rx_mBuf_Ptr; /* Storage for mbufs */ + + PQBufferDescriptor_t *Tx_Frst_BD; + PQBufferDescriptor_t *Tx_Last_BD; + PQBufferDescriptor_t *Tx_NxtUsed_BD; /* First BD, which is in Use */ + PQBufferDescriptor_t *Tx_NxtFill_BD; /* BD to be filled next */ + struct mbuf **Tx_mBuf_Ptr; /* Storage for mbufs */ + /* + * Daemon IDs + */ + rtems_id rxDaemonTid; + rtems_id txDaemonTid; + + /* + * MDIO/Phy info + */ + struct rtems_mdio_info mdio_info; + int phy_default; + int media_state; /* (last detected) state of media */ + /* + * statistic counters Rx + */ + unsigned long rxInterrupts; + unsigned long rxErrors; + /* + * statistic counters Tx + */ + unsigned long txInterrupts; + unsigned long txErrors; + }; + +static struct tsec_struct tsec_driver[TSEC_COUNT]; + +/* + * default numbers for buffers + */ +#define RX_BUF_COUNT 64 +#define TX_BUF_COUNT 64 + +/* + * mask for all Tx interrupts + */ +#define IEVENT_TXALL (TSEC_IEVENT_GTSC \ + | TSEC_IEVENT_TXC \ + /*| TSEC_IEVENT_TXB*/ \ + | TSEC_IEVENT_TXF ) + +/* + * mask for all Rx interrupts + */ +#define IEVENT_RXALL (TSEC_IEVENT_RXC \ + /* | TSEC_IEVENT_RXB */ \ + | TSEC_IEVENT_GRSC \ + | TSEC_IEVENT_RXF ) + +/* + * mask for all Error interrupts + */ +#define IEVENT_ERRALL (TSEC_IEVENT_BABR \ + | TSEC_IEVENT_BSY \ + | TSEC_IEVENT_EBERR \ + | TSEC_IEVENT_MSRO \ + | TSEC_IEVENT_BABT \ + | TSEC_IEVENT_TXE \ + | TSEC_IEVENT_LC \ + | TSEC_IEVENT_CRL_XDA \ + | TSEC_IEVENT_XFUN ) + +static void TSEC_IMASK_SET(struct tsec_struct *sc, uint32_t mask, uint32_t val) +{ + volatile uint32_t *reg = &sc->reg_ptr->imask; + rtems_interrupt_lock_context lock_context; + + rtems_interrupt_lock_acquire(&sc->lock, &lock_context); + *reg = (*reg & ~mask) | (val & mask); + rtems_interrupt_lock_release(&sc->lock, &lock_context); +} + +#define TSEC_ALIGN_BUFFER(buf,align) \ + ((void *)( (((uint32_t)(buf))+(align)-1) \ + -(((uint32_t)(buf))+(align)-1)%align)) + +/* + * RTEMS event used by interrupt handler to signal daemons. + * This must *not* be the same event used by the TCP/IP task synchronization. + */ +#define INTERRUPT_EVENT RTEMS_EVENT_1 +#define FATAL_INT_EVENT RTEMS_EVENT_3 + +/* + * RTEMS event used to start transmit daemon. + * This must not be the same as INTERRUPT_EVENT. + */ +#define START_TRANSMIT_EVENT RTEMS_EVENT_2 + +static int tsec_ioctl +( + struct ifnet *ifp, /* interface information */ + ioctl_command_t command, /* ioctl command code */ + caddr_t data /* optional data */ + ); + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static void tsec_hwinit +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| initialize hardware register | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + struct tsec_struct *sc /* control structure */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| <none> | +\*=========================================================================*/ +{ + volatile tsec_registers *reg_ptr = sc->reg_ptr; /* pointer to TSEC registers*/ + uint8_t *mac_addr; + size_t i; + + /* Clear interrupt mask and all pending events */ + reg_ptr->imask = 0; + reg_ptr->ievent = 0xffffffff; + + /* + * init ECNTL register + * - clear statistics counters + * - enable statistics + * NOTE: do not clear bits set in BSP init function + */ + reg_ptr->ecntrl = ((reg_ptr->ecntrl & ~TSEC_ECNTRL_AUTOZ) + | TSEC_ECNTRL_CLRCNT + | TSEC_ECNTRL_STEN + | TSEC_ECNTRL_R100M); + + /* + * init DMA control register: + * - enable snooping + * - write BD status before interrupt request + * - do not poll TxBD, but wait for TSTAT[THLT] to be written + */ + reg_ptr->dmactrl = (TSEC_DMACTL_TDSEN + | TSEC_DMACTL_TBDSEN + | TSEC_DMACTL_WWR + | TSEC_DMACTL_WOP); + + /* + * init Attribute register: + * - enable read snooping for data and BD + */ + reg_ptr->attr = (TSEC_ATTR_RDSEN + | TSEC_ATTR_RBDSEN); + + + reg_ptr->mrblr = MCLBYTES-64; /* take care of buffer size lost + * due to alignment */ + + /* + * init EDIS register: disable all error reportings + */ + reg_ptr->edis = (TSEC_EDIS_BSYDIS | + TSEC_EDIS_EBERRDIS | + TSEC_EDIS_TXEDIS | + TSEC_EDIS_LCDIS | + TSEC_EDIS_CRLXDADIS | + TSEC_EDIS_FUNDIS); + /* + * init minimum frame length register + */ + reg_ptr->minflr = 64; + /* + * init maximum frame length register + */ + reg_ptr->maxfrm = 1536; + /* + * define physical address of TBI + */ + reg_ptr->tbipa = 0x1e; + /* + * init transmit interrupt coalescing register + */ + reg_ptr->txic = (TSEC_TXIC_ICEN + | TSEC_TXIC_ICFCT(2) + | TSEC_TXIC_ICTT(32)); + /* + * init receive interrupt coalescing register + */ +#if 0 + reg_ptr->rxic = (TSEC_RXIC_ICEN + | TSEC_RXIC_ICFCT(2) + | TSEC_RXIC_ICTT(32)); +#else + reg_ptr->rxic = 0; +#endif + /* + * init MACCFG1 register + */ + reg_ptr->maccfg1 = (TSEC_MACCFG1_RX_FLOW + | TSEC_MACCFG1_TX_FLOW); + + /* + * init MACCFG2 register + */ + reg_ptr->maccfg2 = ((reg_ptr->maccfg2 & TSEC_MACCFG2_IFMODE_MSK) + | TSEC_MACCFG2_IFMODE_BYT + | TSEC_MACCFG2_PRELEN( 7) + | TSEC_MACCFG2_FULLDUPLEX); + + /* + * init station address register + */ + mac_addr = sc->arpcom.ac_enaddr; + + reg_ptr->macstnaddr[0] = ((mac_addr[5] << 24) + | (mac_addr[4] << 16) + | (mac_addr[3] << 8) + | (mac_addr[2] << 0)); + reg_ptr->macstnaddr[1] = ((mac_addr[1] << 24) + | (mac_addr[0] << 16)); + /* + * clear hash filters + */ + for (i = 0; + i < sizeof(reg_ptr->iaddr)/sizeof(reg_ptr->iaddr[0]); + i++) { + reg_ptr->iaddr[i] = 0; + } + for (i = 0; + i < sizeof(reg_ptr->gaddr)/sizeof(reg_ptr->gaddr[0]); + i++) { + reg_ptr->gaddr[i] = 0; + } +} + +/***************************************************************************\ +| MII Management access functions | +\***************************************************************************/ + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static void tsec_mdio_init +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| initialize the MIIM interface | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + struct tsec_struct *sc /* control structure */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| <none> | +\*=========================================================================*/ +{ + static const uint8_t divider [] = { 32, 32, 48, 64, 80, 112, 160, 224 }; + size_t n = sizeof(divider) / sizeof(divider [0]); + size_t i = 0; + uint32_t mii_clock = UINT32_MAX; + uint32_t tsec_system_clock = BSP_bus_frequency / 2; + + /* Set TSEC registers for MDIO communication */ + + /* + * set clock divider + */ + for (i = 0; i < n && mii_clock > 2500000; ++i) { + mii_clock = tsec_system_clock / divider [i]; + } + + sc->mdio_ptr->miimcfg = i; +} + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static int tsec_mdio_read +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| read register of a phy | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + int phy, /* PHY number to access or -1 */ + void *uarg, /* unit argument */ + unsigned reg, /* register address */ + uint32_t *pval /* ptr to read buffer */ + ) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| 0, if ok, else error | +\*=========================================================================*/ +{ + struct tsec_struct *sc = uarg;/* control structure */ + + /* pointer to TSEC registers */ + volatile tsec_registers *reg_ptr = sc->mdio_ptr; + PF("%u\n", reg); + + /* + * make sure we work with a valid phy + */ + if (phy == -1) { + phy = sc->phy_default; + } + if ( (phy < 0) || (phy > 31)) { + /* + * invalid phy number + */ + return EINVAL; + } + /* + * set PHY/reg address + */ + reg_ptr->miimadd = (TSEC_MIIMADD_PHY(phy) + | TSEC_MIIMADD_REGADDR(reg)); + /* + * start read cycle + */ + reg_ptr->miimcom = 0; + reg_ptr->miimcom = TSEC_MIIMCOM_READ; + + /* + * wait for cycle to terminate + */ + do { + rtems_task_wake_after(2); + } while (0 != (reg_ptr->miimind & TSEC_MIIMIND_BUSY)); + reg_ptr->miimcom = 0; + /* + * fetch read data, if available + */ + if (pval != NULL) { + *pval = reg_ptr->miimstat; + } + return 0; +} + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static int tsec_mdio_write +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| write register of a phy | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + int phy, /* PHY number to access or -1 */ + void *uarg, /* unit argument */ + unsigned reg, /* register address */ + uint32_t val /* write value */ + ) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| 0, if ok, else error | +\*=========================================================================*/ +{ + struct tsec_struct *sc = uarg;/* control structure */ + + /* pointer to TSEC registers */ + volatile tsec_registers *reg_ptr = sc->mdio_ptr; + PF("%u\n", reg); + + /* + * make sure we work with a valid phy + */ + if (phy == -1) { + /* + * set default phy number: 0 for TSEC1, 1 for TSEC2 + */ + phy = sc->phy_default; + } + if ( (phy < 0) || (phy > 31)) { + /* + * invalid phy number + */ + return EINVAL; + } + /* + * set PHY/reg address + */ + reg_ptr->miimadd = (TSEC_MIIMADD_PHY(phy) + | TSEC_MIIMADD_REGADDR(reg)); + /* + * start write cycle + */ + reg_ptr->miimcon = val; + + /* + * wait for cycle to terminate + */ + do { + rtems_task_wake_after(2); + } while (0 != (reg_ptr->miimind & TSEC_MIIMIND_BUSY)); + reg_ptr->miimcom = 0; + return 0; +} + + +/***************************************************************************\ +| RX receive functions | +\***************************************************************************/ + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static rtems_event_set tsec_rx_wait_for_events +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| handle all rx events | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + struct tsec_struct *sc, /* control structure */ + rtems_event_set event_mask /* events to wait for */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| event set received | +\*=========================================================================*/ +{ + rtems_event_set events; /* events received */ + /* + * enable Rx interrupts, make sure this is not interrupted :-) + */ + TSEC_IMASK_SET(sc,IEVENT_RXALL,~0); + + /* + * wait for events to come in + */ + rtems_bsdnet_event_receive(event_mask, + RTEMS_EVENT_ANY | RTEMS_WAIT, + RTEMS_NO_TIMEOUT, + &events); + return events; +} + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static void mpc83xx_rxbd_alloc_clear +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| allocate space for Rx BDs, clear them | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + struct tsec_struct *sc /* control structure */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| <none> | +\*=========================================================================*/ +{ + char *alloc_ptr; + PQBufferDescriptor_t *BD_ptr; + /* + * allocate proper space for Rx BDs + */ + alloc_ptr = calloc((sc->rxBdCount+1),sizeof(PQBufferDescriptor_t)); + if (alloc_ptr == NULL) { + rtems_panic("TSEC: cannot allocate space for Rx BDs"); + } + alloc_ptr = (void *)((uint32_t )((alloc_ptr + (sizeof(PQBufferDescriptor_t)-1))) + & ~(sizeof(PQBufferDescriptor_t)-1)); + /* + * store pointers to certain positions in BD chain + */ + sc->Rx_Last_BD = ((PQBufferDescriptor_t *)alloc_ptr)+sc->rxBdCount-1; + sc->Rx_Frst_BD = (PQBufferDescriptor_t *)alloc_ptr; + sc->Rx_NxtUsed_BD = sc->Rx_Frst_BD; + sc->Rx_NxtFill_BD = sc->Rx_Frst_BD; + + /* + * clear all BDs + */ + for (BD_ptr = sc->Rx_Frst_BD; + BD_ptr <= sc->Rx_Last_BD; + BD_ptr++) { + BD_ptr->status = 0; + } + /* + * Init BD chain registers + */ + sc->reg_ptr->rbase = (uint32_t) (sc->Rx_Frst_BD); +} + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static void tsec_receive_packets +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| process any received packets | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + struct tsec_struct *sc /* control structure */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| <none> | +\*=========================================================================*/ +{ + PQBufferDescriptor_t *BD_ptr; + struct mbuf *m,*n; + bool finished = false; + uint16_t status; + struct ether_header *eh; + int bd_idx; + + BD_ptr = sc->Rx_NxtUsed_BD; + + while ((0 == ((status = BD_ptr->status) & BD_EMPTY)) && + !finished && + (BD_ptr->buffer != NULL)) { + /* + * get mbuf associated with BD + */ + bd_idx = BD_ptr - sc->Rx_Frst_BD; + m = sc->Rx_mBuf_Ptr[bd_idx]; + sc->Rx_mBuf_Ptr[bd_idx] = NULL; + + /* + * Check that packet is valid + */ + if ((status & (BD_LAST | + BD_FIRST_IN_FRAME | + BD_LONG | + BD_NONALIGNED | + BD_CRC_ERROR | + BD_OVERRUN )) + == (BD_LAST | + BD_FIRST_IN_FRAME ) ) { + /* + * send mbuf of this buffer to ether_input() + */ + m->m_len = m->m_pkthdr.len = (BD_ptr->length + - sizeof(uint32_t) + - sizeof(struct ether_header)); + eh = mtod(m, struct ether_header *); + m->m_data += sizeof(struct ether_header); + PF("RX[%08x] (%i)\n", BD_ptr, m->m_len); + ether_input(&sc->arpcom.ac_if,eh,m); + } + else { + /* + * throw away mbuf + */ + MFREE(m,n); + (void) n; + } + /* + * mark buffer as non-allocated (for refill) + */ + BD_ptr->buffer = NULL; + /* + * Advance BD_ptr to next BD + */ + BD_ptr = ((BD_ptr == sc->Rx_Last_BD) + ? sc->Rx_Frst_BD + : BD_ptr+1); + } + sc->Rx_NxtUsed_BD = BD_ptr; +} + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static void tsec_refill_rxbds +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| link new buffers to rx BDs | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + struct tsec_struct *sc /* control structure */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| <none> | +\*=========================================================================*/ +{ + PQBufferDescriptor_t *BD_ptr; + struct mbuf *m,*n; + bool finished = false; + int bd_idx; + + BD_ptr = sc->Rx_NxtFill_BD; + while ((BD_ptr->buffer == NULL) && + !finished) { + /* + * get new mbuf and attach a cluster + */ + MGETHDR(m,M_DONTWAIT,MT_DATA); + if (m != NULL) { + MCLGET(m,M_DONTWAIT); + if ((m->m_flags & M_EXT) == 0) { + MFREE(m,n); + (void) n; + m = NULL; + } + } + if (m == NULL) { + finished = true; + } + else { + bd_idx = BD_ptr - sc->Rx_Frst_BD; + sc->Rx_mBuf_Ptr[bd_idx] = m; + + m->m_pkthdr.rcvif= &sc->arpcom.ac_if; + m->m_data = TSEC_ALIGN_BUFFER(m->m_ext.ext_buf,64); + BD_ptr->buffer = m->m_data; + BD_ptr->length = 0; + BD_ptr->status = (BD_EMPTY + | BD_INTERRUPT + | ((BD_ptr == sc->Rx_Last_BD) + ? BD_WRAP + : 0)); + /* + * Advance BD_ptr to next BD + */ + BD_ptr = ((BD_ptr == sc->Rx_Last_BD) + ? sc->Rx_Frst_BD + : BD_ptr+1); + } + } + sc->Rx_NxtFill_BD = BD_ptr; +} + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static void tsec_rxDaemon +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| handle all rx buffers and events | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + void * arg /* argument, is sc structure ptr */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| <none> | +\*=========================================================================*/ +{ + struct tsec_struct *sc = + (struct tsec_struct *)arg; + bool finished = false; +#if !defined(CLREVENT_IN_IRQ) + uint32_t irq_events; +#endif + /* + * enable Rx in MACCFG1 register + */ + sc->reg_ptr->maccfg1 |= TSEC_MACCFG1_RXEN; + while (!finished) { + /* + * fetch MBufs, associate them to RxBDs + */ + tsec_refill_rxbds(sc); + /* + * wait for events to come in + */ + tsec_rx_wait_for_events(sc,INTERRUPT_EVENT); +#if !defined(CLREVENT_IN_IRQ) + /* + * clear any pending RX events + */ + irq_events = sc->reg_ptr->ievent & IEVENT_RXALL; + sc->reg_ptr->ievent = irq_events; +#endif + /* + * fetch any completed buffers/packets received + * and stuff them into the TCP/IP Stack + */ + tsec_receive_packets(sc); + } + /* + * disable Rx in MACCFG1 register + */ + sc->reg_ptr->maccfg1 &= ~TSEC_MACCFG1_RXEN; + /* + * terminate daemon + */ + sc->rxDaemonTid = 0; + rtems_task_delete(RTEMS_SELF); +} + +/***************************************************************************\ +| TX Transmit functions | +\***************************************************************************/ + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static void mpc83xx_txbd_alloc_clear +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| allocate space for Tx BDs, clear them | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + struct tsec_struct *sc /* control structure */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| <none> | +\*=========================================================================*/ +{ + char *alloc_ptr; + PQBufferDescriptor_t *BD_ptr; + /* + * allocate proper space for Tx BDs + */ + alloc_ptr = calloc((sc->txBdCount+1),sizeof(PQBufferDescriptor_t)); + if (alloc_ptr == NULL) { + rtems_panic("TSEC: cannot allocate space for Tx BDs"); + } + alloc_ptr = (void *)((uint32_t )((alloc_ptr + (sizeof(PQBufferDescriptor_t)-1))) + & ~(sizeof(PQBufferDescriptor_t)-1)); + /* + * store pointers to certain positions in BD chain + */ + sc->Tx_Last_BD = ((PQBufferDescriptor_t *)alloc_ptr)+sc->txBdCount-1; + sc->Tx_Frst_BD = (PQBufferDescriptor_t *)alloc_ptr; + sc->Tx_NxtUsed_BD = sc->Tx_Frst_BD; + sc->Tx_NxtFill_BD = sc->Tx_Frst_BD; + + /* + * clear all BDs + */ + for (BD_ptr = sc->Tx_Frst_BD; + BD_ptr <= sc->Tx_Last_BD; + BD_ptr++) { + BD_ptr->status = 0; + } + /* + * Init BD chain registers + */ + sc->reg_ptr->tbase = (uint32_t)(sc->Tx_Frst_BD); +} + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static void tsec_tx_start +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| start transmission | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ +struct ifnet *ifp +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| <none> | +\*=========================================================================*/ +{ + struct tsec_struct *sc = ifp->if_softc; + + ifp->if_flags |= IFF_OACTIVE; + + rtems_bsdnet_event_send (sc->txDaemonTid, START_TRANSMIT_EVENT); +} + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static rtems_event_set tsec_tx_wait_for_events +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| handle all tx events | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + struct tsec_struct *sc, /* control structure */ + rtems_event_set event_mask /* events to wait for */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| event set received | +\*=========================================================================*/ +{ + rtems_event_set events; /* events received */ + /* + * enable Tx interrupts, make sure this is not interrupted :-) + */ + TSEC_IMASK_SET(sc,IEVENT_TXALL,~0); + + /* + * wait for events to come in + */ + rtems_bsdnet_event_receive(event_mask, + RTEMS_EVENT_ANY | RTEMS_WAIT, + RTEMS_NO_TIMEOUT, + &events); + return events; +} + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static void tsec_tx_retire +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| handle all tx events | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + struct tsec_struct *sc /* control structure */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| <none> | +\*=========================================================================*/ +{ + PQBufferDescriptor_t *RetBD; + RetBD = sc->Tx_NxtUsed_BD; + int bd_idx; + struct mbuf *m,*n; + /* + * check next BDs to be empty + */ + while ((RetBD->buffer != NULL) /* BD is filled */ + && (0 == (RetBD->status & BD_READY ))) {/* BD no longer ready*/ + + bd_idx = RetBD - sc->Tx_Frst_BD; + m = sc->Tx_mBuf_Ptr[bd_idx]; + sc->Tx_mBuf_Ptr[bd_idx] = NULL; + + MFREE(m,n); + (void) n; + RetBD->buffer = NULL; + /* + * Advance CurrBD to next BD + */ + RetBD = ((RetBD == sc->Tx_Last_BD) + ? sc->Tx_Frst_BD + : RetBD+1); + } + sc->Tx_NxtUsed_BD = RetBD; +} + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static void tsec_sendpacket +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| handle all tx events | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + struct tsec_struct *sc, /* control structure */ + struct mbuf *m /* start of packet to send */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| <none> | +\*=========================================================================*/ +{ + PQBufferDescriptor_t *FrstBD = NULL; + PQBufferDescriptor_t *CurrBD; + uint16_t status; + struct mbuf *l = NULL; /* ptr to last non-freed (non-empty) mbuf */ + int bd_idx; + /* + * get next Tx BD + */ + CurrBD = sc->Tx_NxtFill_BD; + while (m) { + if(m->m_len == 0) { + /* + * Just toss empty mbufs + */ + struct mbuf *n; + MFREE(m, n); + m = n; + if(l != NULL) { + l->m_next = m; + } + } + else { + /* + * this mbuf is non-empty, so send it + */ + /* + * Is CurrBD still in Use/not yet retired? + */ + while (CurrBD->buffer != NULL) { + /* + * Then try to retire it + * and to return its mbuf + */ + tsec_tx_retire(sc); + if (CurrBD->buffer != NULL) { + /* + * Wait for anything to happen... + */ + tsec_tx_wait_for_events(sc,INTERRUPT_EVENT); + } + } + status = ((BD_PAD_CRC | BD_TX_CRC) + | ((m->m_next == NULL) + ? BD_LAST | BD_INTERRUPT + : 0) + | ((CurrBD == sc->Tx_Last_BD) ? BD_WRAP : 0)); + + /* + * link buffer to BD + */ + CurrBD->buffer = mtod(m, void *); + CurrBD->length = (uint32_t)m->m_len; + l = m; /* remember: we use this mbuf */ + PF("TX[%08x] (%i)\n", CurrBD, m->m_len); + + bd_idx = CurrBD - sc->Tx_Frst_BD; + sc->Tx_mBuf_Ptr[bd_idx] = m; + + m = m->m_next; /* advance to next mbuf of this packet */ + /* + * is this the first BD of the packet? + * then don't set it to "READY" state, + * and remember this BD position + */ + if (FrstBD == NULL) { + FrstBD = CurrBD; + } + else { + status |= BD_READY; + } + CurrBD->status = status; + /* + * Advance CurrBD to next BD + */ + CurrBD = ((CurrBD == sc->Tx_Last_BD) + ? sc->Tx_Frst_BD + : CurrBD+1); + } + } + /* + * mbuf chain of this packet + * has been translated + * to BD chain, so set first BD ready now + */ + if (FrstBD != NULL) { + FrstBD->status |= BD_READY; + } + sc->Tx_NxtFill_BD = CurrBD; + /* + * wake up transmitter (clear TSTAT[THLT]) + */ + sc->reg_ptr->tstat = TSEC_TSTAT_THLT; +} + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static void tsec_txDaemon +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| handle all tx events | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + void * arg /* argument, is sc structure ptr */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| <none> | +\*=========================================================================*/ +{ + struct tsec_struct *sc = + (struct tsec_struct *)arg; + struct ifnet *ifp = &sc->arpcom.ac_if; + struct mbuf *m; + bool finished = false; +#if !defined(CLREVENT_IN_IRQ) + uint32_t irq_events; +#endif + + /* + * enable Tx in MACCFG1 register + * FIXME: make this irq save + */ + sc->reg_ptr->maccfg1 |= TSEC_MACCFG1_TXEN; + while (!finished) { + /* + * wait for events to come in + */ + tsec_tx_wait_for_events(sc, + START_TRANSMIT_EVENT + | INTERRUPT_EVENT); +#if !defined(CLREVENT_IN_IRQ) + /* + * clear any pending TX events + */ + irq_events = sc->reg_ptr->ievent & IEVENT_TXALL; + sc->reg_ptr->ievent = irq_events; +#endif + /* + * retire any sent tx BDs + */ + tsec_tx_retire(sc); + /* + * Send packets till queue is empty + */ + do { + /* + * Get the next mbuf chain to transmit. + */ + IF_DEQUEUE(&ifp->if_snd, m); + + if (m) { + tsec_sendpacket(sc,m); + } + } while (m != NULL); + + ifp->if_flags &= ~IFF_OACTIVE; + } + /* + * disable Tx in MACCFG1 register + */ + sc->reg_ptr->maccfg1 &= ~TSEC_MACCFG1_TXEN; + /* + * terminate daemon + */ + sc->txDaemonTid = 0; + rtems_task_delete(RTEMS_SELF); +} + +/***************************************************************************\ +| Interrupt handlers and management routines | +\***************************************************************************/ + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static void tsec_tx_irq_handler +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| handle tx interrupts | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + rtems_irq_hdl_param handle /* handle, is sc structure ptr */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| <none> | +\*=========================================================================*/ +{ + struct tsec_struct *sc = + (struct tsec_struct *)handle; +#if defined(CLREVENT_IN_IRQ) + uint32_t irq_events; +#endif + + PF("TXIRQ\n"); + sc->txInterrupts++; + /* + * disable tx interrupts + */ + TSEC_IMASK_SET(sc,IEVENT_TXALL,0); + +#if defined(CLREVENT_IN_IRQ) + /* + * clear any pending TX events + */ + irq_events = sc->reg_ptr->ievent & IEVENT_TXALL; + sc->reg_ptr->ievent = irq_events; +#endif + /* + * wake up tx Daemon + */ + rtems_bsdnet_event_send(sc->txDaemonTid, INTERRUPT_EVENT); +} + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static void tsec_rx_irq_handler +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| handle rx interrupts | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + rtems_irq_hdl_param handle /* handle, is sc structure */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| <none> | +\*=========================================================================*/ +{ + struct tsec_struct *sc = + (struct tsec_struct *)handle; +#if defined(CLREVENT_IN_IRQ) + uint32_t irq_events; +#endif + + sc->rxInterrupts++; + PF("RXIRQ\n"); + /* + * disable rx interrupts + */ + TSEC_IMASK_SET(sc,IEVENT_RXALL,0); +#if defined(CLREVENT_IN_IRQ) + /* + * clear any pending RX events + */ + irq_events = sc->reg_ptr->ievent & IEVENT_RXALL; + sc->reg_ptr->ievent = irq_events; +#endif + /* + * wake up rx Daemon< + */ + rtems_bsdnet_event_send(sc->rxDaemonTid, INTERRUPT_EVENT); +} + + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static void tsec_err_irq_handler +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| handle error interrupts | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + rtems_irq_hdl_param handle /* handle, is sc structure */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| <none> | +\*=========================================================================*/ +{ + struct tsec_struct *sc = + (struct tsec_struct *)handle; + PF("ERIRQ\n"); + /* + * clear error events in IEVENT + */ + sc->reg_ptr->ievent = IEVENT_ERRALL; + /* + * has Rx been stopped? then restart it + */ + if (0 != (sc->reg_ptr->rstat & TSEC_RSTAT_QHLT)) { + sc->rxErrors++; + sc->reg_ptr->rstat = TSEC_RSTAT_QHLT; + } + /* + * has Tx been stopped? then restart it + */ + if (0 != (sc->reg_ptr->tstat & TSEC_TSTAT_THLT)) { + sc->txErrors++; + sc->reg_ptr->tstat = TSEC_TSTAT_THLT; + } +} + + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static uint32_t tsec_irq_mask +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| determine irq mask for given interrupt number | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + int irqnum, + struct tsec_struct *sc +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| interrupt mask (for ievent/imask register) | +\*=========================================================================*/ +{ + return ((irqnum == sc->irq_num_tx) + ? IEVENT_TXALL + : ((irqnum == sc->irq_num_rx) + ? IEVENT_RXALL + : ((irqnum == sc->irq_num_err) + ? IEVENT_ERRALL + : 0))); +} +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static void tsec_irq_on +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| enable interrupts in TSEC mask register | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + const + rtems_irq_connect_data *irq_conn_data /* irq connect data */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| <none> | +\*=========================================================================*/ +{ + struct tsec_struct *sc = + (struct tsec_struct *)(irq_conn_data->handle); + + TSEC_IMASK_SET(sc, + tsec_irq_mask(irq_conn_data->name,sc), + ~0); +} + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static void tsec_irq_off +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| disable TX interrupts in TSEC mask register | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + const + rtems_irq_connect_data *irq_conn_data /* irq connect data */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| <none> | +\*=========================================================================*/ +{ + struct tsec_struct *sc = + (struct tsec_struct *)irq_conn_data->handle; + + TSEC_IMASK_SET(sc, + tsec_irq_mask(irq_conn_data->name,sc), + 0); +} + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static int tsec_irq_isOn +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| check state of interrupts in TSEC mask register | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + const + rtems_irq_connect_data *irq_conn_data /* irq connect data */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| <none> | +\*=========================================================================*/ +{ + struct tsec_struct *sc = + (struct tsec_struct *)irq_conn_data->handle; + + return (0 != (sc->reg_ptr->imask + & tsec_irq_mask(irq_conn_data->name,sc))); +} + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static void tsec_install_irq_handlers +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| (un-)install the interrupt handlers | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + struct tsec_struct *sc, /* ptr to control structure */ + bool install /* true: install, false: remove */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| <none> | +\*=========================================================================*/ +{ + size_t i; + + rtems_irq_connect_data irq_conn_data[3] = { + { + sc->irq_num_tx, + tsec_tx_irq_handler, /* rtems_irq_hdl */ + (rtems_irq_hdl_param)sc, /* (rtems_irq_hdl_param) */ + tsec_irq_on, /* (rtems_irq_enable) */ + tsec_irq_off, /* (rtems_irq_disable) */ + tsec_irq_isOn /* (rtems_irq_is_enabled) */ + },{ + sc->irq_num_rx, + tsec_rx_irq_handler, /* rtems_irq_hdl */ + (rtems_irq_hdl_param)sc, /* (rtems_irq_hdl_param) */ + tsec_irq_on, /* (rtems_irq_enable) */ + tsec_irq_off, /* (rtems_irq_disable) */ + tsec_irq_isOn /* (rtems_irq_is_enabled) */ + },{ + sc->irq_num_err, + tsec_err_irq_handler, /* rtems_irq_hdl */ + (rtems_irq_hdl_param)sc, /* (rtems_irq_hdl_param) */ + tsec_irq_on, /* (rtems_irq_enable) */ + tsec_irq_off, /* (rtems_irq_disable) */ + tsec_irq_isOn /* (rtems_irq_is_enabled) */ + } + }; + + /* + * (un-)install handler for Tx/Rx/Error + */ + for (i = 0; + i < sizeof(irq_conn_data)/sizeof(irq_conn_data[0]); + i++) { + if (install) { + if (!BSP_install_rtems_irq_handler (&irq_conn_data[i])) { + rtems_panic("TSEC: cannot install IRQ handler"); + } + } + else { + if (!BSP_remove_rtems_irq_handler (&irq_conn_data[i])) { + rtems_panic("TSEC: cannot uninstall IRQ handler"); + } + } + } +} + +/***************************************************************************\ +| Initialization and interface routines | +\***************************************************************************/ + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static void tsec_init +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| initialize the driver and the hardware | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + void *arg /* argument pointer, contains *sc */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| zero, if success | +\*=========================================================================*/ +{ + struct tsec_struct *sc = (struct tsec_struct *)arg; + struct ifnet *ifp = &sc->arpcom.ac_if; + /* + * check, whether device is not yet running + */ + if (0 == sc->rxDaemonTid) { + /* + * allocate rx/tx BDs + */ + mpc83xx_rxbd_alloc_clear(sc); + mpc83xx_txbd_alloc_clear(sc); + /* + * allocate storage for mbuf ptrs + */ + sc->Rx_mBuf_Ptr = calloc(sc->rxBdCount,sizeof(struct mbuf *)); + sc->Tx_mBuf_Ptr = calloc(sc->txBdCount,sizeof(struct mbuf *)); + if ((sc->Rx_mBuf_Ptr == NULL) || + (sc->Tx_mBuf_Ptr == NULL)) { + rtems_panic("TSEC: cannot allocate buffers for mbuf management"); + + } + + /* + * initialize TSEC hardware: + * - set interrupt coalescing to BDCount/8, Time of 8 frames + * - enable DMA snooping + */ + tsec_hwinit(sc); + /* + * init access to phys + */ + tsec_mdio_init(sc); + /* + * Start driver tasks + */ + sc->txDaemonTid = rtems_bsdnet_newproc("TStx", + 4096, + tsec_txDaemon, + sc); + sc->rxDaemonTid = rtems_bsdnet_newproc("TSrx", 4096, + tsec_rxDaemon, + sc); + /* + * install interrupt handlers + */ + tsec_install_irq_handlers(sc,true); + } + /* + * Set flags appropriately + */ + if(ifp->if_flags & IFF_PROMISC) { + sc->reg_ptr->rctrl |= TSEC_RCTRL_PROM; + } + else { + sc->reg_ptr->rctrl &= ~TSEC_RCTRL_PROM; + } + +#if defined(MPC83XX_BOARD_HSC_CM01) + /* + * for HSC CM01: we need to configure the PHY to use maximum skew adjust + */ + + tsec_mdio_write(-1,sc,23,0x0100); +#endif + + /* + * init timer so the "watchdog function gets called periodically + */ + ifp->if_timer = 1; + /* + * Tell the world that we're running. + */ + ifp->if_flags |= IFF_RUNNING; +} + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static void tsec_off +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| deinitialize the driver and the hardware | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + struct tsec_struct *sc /* ptr to control structure */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| <none> | +\*=========================================================================*/ +{ + /* + * deinitialize driver? + */ +} + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static void tsec_stats +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| print statistics | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + struct tsec_struct *sc /* ptr to control structure */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| <none> | +\*=========================================================================*/ +{ + if (sc->phy_default >= 0) { + int media; + int result; + /* + * fetch/print media info + */ + media = IFM_MAKEWORD(0,0,0,sc->phy_default); /* fetch from default phy */ + + result = tsec_ioctl(&(sc->arpcom.ac_if), + SIOCGIFMEDIA, + (caddr_t)&media); + if (result == 0) { + rtems_ifmedia2str(media,NULL,0); + printf ("\n"); + } else { + printf ("PHY communication error\n"); + } + } +#if 0 /* print all PHY registers */ + { + int reg; + uint32_t reg_val; + printf("****** PHY register values****\n"); + for (reg = 0;reg <= 31;reg++) { + tsec_mdio_read(-1,sc,reg,®_val); + printf("%02d:0x%04x%c",reg,reg_val, + (((reg % 4) == 3) ? '\n' : ' ')); + } + } +#endif + /* + * print some statistics + */ + printf (" Rx Interrupts:%-8lu", sc->rxInterrupts); + printf (" Rx Errors:%-8lu", sc->rxErrors); + printf (" Rx packets:%-8lu\n", + sc->reg_ptr->rmon_mib[TSEC_RMON_RPKT]); + printf (" Rx broadcasts:%-8lu", + sc->reg_ptr->rmon_mib[TSEC_RMON_RBCA]); + printf (" Rx multicasts:%-8lu", + sc->reg_ptr->rmon_mib[TSEC_RMON_RMCA]); + printf (" Giant:%-8lu\n", + sc->reg_ptr->rmon_mib[TSEC_RMON_ROVR]); + printf (" Non-octet:%-8lu", + sc->reg_ptr->rmon_mib[TSEC_RMON_RALN]); + printf (" Bad CRC:%-8lu", + sc->reg_ptr->rmon_mib[TSEC_RMON_RFCS]); + printf (" Overrun:%-8lu\n", + sc->reg_ptr->rmon_mib[TSEC_RMON_RDRP]); + + printf (" Tx Interrupts:%-8lu", sc->txInterrupts); + printf (" Tx Errors:%-8lu", sc->txErrors); + printf (" Tx packets:%-8lu\n", + sc->reg_ptr->rmon_mib[TSEC_RMON_TPKT]); + printf (" Deferred:%-8lu", + sc->reg_ptr->rmon_mib[TSEC_RMON_TDFR]); + printf (" Late Collision:%-8lu", + sc->reg_ptr->rmon_mib[TSEC_RMON_TLCL]); + printf ("Retransmit Limit:%-8lu\n", + sc->reg_ptr->rmon_mib[TSEC_RMON_TEDF]); + printf (" Underrun:%-8lu\n", + sc->reg_ptr->rmon_mib[TSEC_RMON_TUND]); +} + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static int tsec_ioctl +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| perform io control functions | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + struct ifnet *ifp, /* interface information */ + ioctl_command_t command, /* ioctl command code */ + caddr_t data /* optional data */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| zero, if success | +\*=========================================================================*/ +{ + struct tsec_struct *sc = ifp->if_softc; + int error = 0; + + switch(command) { + /* + * access PHY via MII + */ + case SIOCGIFMEDIA: + case SIOCSIFMEDIA: + rtems_mii_ioctl (&(sc->mdio_info),sc,command,(void *)data); + break; + case SIOCGIFADDR: + case SIOCSIFADDR: + /* + * pass through to general ether_ioctl + */ + ether_ioctl(ifp, command, data); + break; + + case SIOCSIFFLAGS: + /* + * adjust active state + */ + if (ifp->if_flags & IFF_RUNNING) { + tsec_off(sc); + } + if (ifp->if_flags & IFF_UP) { + tsec_init(sc); + } + break; + + case SIO_RTEMS_SHOW_STATS: + /* + * show interface statistics + */ + tsec_stats(sc); + break; + + /* + * All sorts of multicast commands need to be added here! + */ + default: + error = EINVAL; + break; + } + + return error; +} + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static int tsec_mode_adapt +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| init the PHY and adapt TSEC settings | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + struct ifnet *ifp +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| 0, if success | +\*=========================================================================*/ +{ + int result = 0; + struct tsec_struct *sc = ifp->if_softc; + int media = IFM_MAKEWORD( 0, 0, 0, sc->phy_default); + + /* In case no PHY is available stop now */ + if (sc->phy_default < 0) { + return 0; + } + + /* + * fetch media status + */ + result = tsec_ioctl(ifp,SIOCGIFMEDIA,(caddr_t)&media); + if (result != 0) { + return result; + } + + /* + * status is unchanged? then do nothing + */ + if (media == sc->media_state) { + return 0; + } + /* + * otherwise: for the first call, try to negotiate mode + */ + if (sc->media_state == 0) { + /* + * set media status: set auto negotiation -> start auto-negotiation + */ + media = IFM_MAKEWORD(0,IFM_AUTO,0,sc->phy_default); + result = tsec_ioctl(ifp,SIOCSIFMEDIA,(caddr_t)&media); + if (result != 0) { + return result; + } + /* + * check auto-negotiation status + */ + media = IFM_MAKEWORD(0,0,0,sc->phy_default); + result = tsec_ioctl(ifp,SIOCGIFMEDIA,(caddr_t)&media); + if (result != 0 || IFM_NONE == IFM_SUBTYPE(media)) { + return result; + } + } + + /* + * now set HW according to media results: + */ + /* + * if we are 1000MBit, then switch IF to byte mode + */ + if (IFM_1000_T == IFM_SUBTYPE(media)) { + sc->reg_ptr->maccfg2 = + ((sc->reg_ptr->maccfg2 & ~TSEC_MACCFG2_IFMODE_MSK) + | TSEC_MACCFG2_IFMODE_BYT); + } + else { + sc->reg_ptr->maccfg2 = + ((sc->reg_ptr->maccfg2 & ~TSEC_MACCFG2_IFMODE_MSK) + | TSEC_MACCFG2_IFMODE_NIB); + } + /* + * if we are 10MBit, then switch rate to 10M + */ + if (IFM_10_T == IFM_SUBTYPE(media)) { + sc->reg_ptr->ecntrl &= ~TSEC_ECNTRL_R100M; + } + else { + sc->reg_ptr->ecntrl |= TSEC_ECNTRL_R100M; + } + /* + * if we are half duplex then switch to half duplex + */ + if (0 == (IFM_FDX & IFM_OPTIONS(media))) { + sc->reg_ptr->maccfg2 &= ~TSEC_MACCFG2_FULLDUPLEX; + } + else { + sc->reg_ptr->maccfg2 |= TSEC_MACCFG2_FULLDUPLEX; + } + /* + * store current media state for future compares + */ + sc->media_state = media; + + return 0; +} + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static void tsec_watchdog +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| periodically poll the PHY. if mode has changed, | +| then adjust the TSEC settings | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + struct ifnet *ifp +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| 1, if success | +\*=========================================================================*/ +{ + tsec_mode_adapt(ifp); + ifp->if_timer = TSEC_WATCHDOG_TIMEOUT; +} + +static int tsec_driver_attach(struct rtems_bsdnet_ifconfig *config) +{ + tsec_config *tsec_cfg = config->drv_ctrl; + int unitNumber = tsec_cfg->unit_number; + char *unitName = tsec_cfg->unit_name; + struct tsec_struct *sc; + struct ifnet *ifp; + + /* + * Is driver free? + */ + if ((unitNumber <= 0) || (unitNumber > TSEC_COUNT)) { + + printk ("Bad TSEC unit number.\n"); + return 0; + + } + + sc = &tsec_driver[unitNumber - 1]; + ifp = &sc->arpcom.ac_if; + + if(ifp->if_softc != NULL) { + printk ("Driver already in use.\n"); + return 0; + } + + /* + * Process options + */ + if(config->hardware_address) { + memcpy(sc->arpcom.ac_enaddr, config->hardware_address, ETHER_ADDR_LEN); + } + else { + rtems_panic("TSEC: No Ethernet address specified!\n"); + } + + sc->rxBdCount = (config->rbuf_count > 0) ? config->rbuf_count : RX_BUF_COUNT; + sc->txBdCount = (config->xbuf_count > 0) ? config->xbuf_count : TX_BUF_COUNT; + sc->acceptBroadcast = !config->ignore_broadcast; + + /* get pointer to TSEC register block */ + sc->reg_ptr = tsec_cfg->reg_ptr; + sc->mdio_ptr = tsec_cfg->mdio_ptr; + + /* IRQ numbers */ + sc->irq_num_tx = tsec_cfg->irq_num_tx; + sc->irq_num_rx = tsec_cfg->irq_num_rx; + sc->irq_num_err = tsec_cfg->irq_num_err; + + /* + * setup info about mdio interface + */ + sc->mdio_info.mdio_r = tsec_mdio_read; + sc->mdio_info.mdio_w = tsec_mdio_write; + sc->mdio_info.has_gmii = 1; /* we support gigabit IF */ + + /* PHY address */ + sc->phy_default = tsec_cfg->phy_default; + + /* + * Set up network interface values + */ + ifp->if_softc = sc; + ifp->if_unit = unitNumber; + ifp->if_name = unitName; + ifp->if_mtu = (config->mtu > 0) ? config->mtu : ETHERMTU; + ifp->if_init = tsec_init; + ifp->if_ioctl = tsec_ioctl; + ifp->if_start = tsec_tx_start; + ifp->if_output = ether_output; + ifp->if_watchdog = tsec_watchdog; /* XXX: timer is set in "init" */ + + ifp->if_flags = (config->ignore_broadcast) ? 0 : IFF_BROADCAST; + /*ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX;*/ + + if(ifp->if_snd.ifq_maxlen == 0) { + ifp->if_snd.ifq_maxlen = ifqmaxlen; + } + + /* + * Attach the interface + */ + if_attach(ifp); + + ether_ifattach(ifp); + + return 1; +} + +int tsec_driver_attach_detach( + struct rtems_bsdnet_ifconfig *config, + int attaching +) +{ + if (attaching) { + return tsec_driver_attach(config); + } else { + return 0; + } +} |