summaryrefslogtreecommitdiffstats
path: root/bsps
diff options
context:
space:
mode:
authorSebastian Huber <sebastian.huber@embedded-brains.de>2018-03-23 15:54:12 +0100
committerSebastian Huber <sebastian.huber@embedded-brains.de>2018-03-26 10:40:42 +0200
commit0cab067f1cd3349907d4cc62e8812f0a5e146d75 (patch)
tree3f44dd4f8fa5aaca8814ec6c2aec8d1c1c77f414 /bsps
parentbsp/bfin: Move libcpu content to bsps (diff)
downloadrtems-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.c262
-rw-r--r--bsps/powerpc/gen83xx/dev/mpc83xx_i2cdrv.c668
-rw-r--r--bsps/powerpc/gen83xx/dev/mpc83xx_spidrv.c675
-rw-r--r--bsps/powerpc/shared/net/tsec.c1936
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,&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;
+ }
+}