summaryrefslogtreecommitdiffstats
path: root/c/src/lib/libbsp/arm/shared/lpc/network/lpc-ethernet.c
diff options
context:
space:
mode:
Diffstat (limited to 'c/src/lib/libbsp/arm/shared/lpc/network/lpc-ethernet.c')
-rw-r--r--c/src/lib/libbsp/arm/shared/lpc/network/lpc-ethernet.c661
1 files changed, 419 insertions, 242 deletions
diff --git a/c/src/lib/libbsp/arm/shared/lpc/network/lpc-ethernet.c b/c/src/lib/libbsp/arm/shared/lpc/network/lpc-ethernet.c
index 56df8d4fef..3b2d5b6fa5 100644
--- a/c/src/lib/libbsp/arm/shared/lpc/network/lpc-ethernet.c
+++ b/c/src/lib/libbsp/arm/shared/lpc/network/lpc-ethernet.c
@@ -7,7 +7,7 @@
*/
/*
- * Copyright (c) 2009-2011 embedded brains GmbH. All rights reserved.
+ * Copyright (c) 2009-2012 embedded brains GmbH. All rights reserved.
*
* embedded brains GmbH
* Obere Lagerstr. 30
@@ -28,6 +28,7 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
+#include <assert.h>
#include <rtems.h>
#include <rtems/rtems_bsdnet.h>
@@ -50,8 +51,6 @@
#include <bsp/lpc-ethernet-config.h>
#include <bsp/utility.h>
-#include <rtems/status-checks.h>
-
#if MCLBYTES > (2 * 1024)
#error "MCLBYTES to large"
#endif
@@ -254,10 +253,12 @@ static volatile lpc_eth_controller *const lpc_eth =
#define LPC_ETH_EVENT_INITIALIZE RTEMS_EVENT_1
-#define LPC_ETH_EVENT_START RTEMS_EVENT_2
+#define LPC_ETH_EVENT_TXSTART RTEMS_EVENT_2
#define LPC_ETH_EVENT_INTERRUPT RTEMS_EVENT_3
+#define LPC_ETH_EVENT_STOP RTEMS_EVENT_4
+
/* Status */
#define LPC_ETH_INTERRUPT_RECEIVE \
@@ -292,10 +293,9 @@ static volatile lpc_eth_controller *const lpc_eth =
#endif
typedef enum {
- LPC_ETH_NOT_INITIALIZED,
- LPC_ETH_INITIALIZED,
- LPC_ETH_STARTED,
- LPC_ETH_RUNNING
+ LPC_ETH_STATE_NOT_INITIALIZED = 0,
+ LPC_ETH_STATE_DOWN,
+ LPC_ETH_STATE_UP
} lpc_eth_state;
typedef struct {
@@ -317,6 +317,7 @@ typedef struct {
unsigned receive_interrupts;
unsigned transmitted_frames;
unsigned transmit_interrupts;
+ unsigned receive_drop_errors;
unsigned receive_overrun_errors;
unsigned receive_fragment_errors;
unsigned receive_crc_errors;
@@ -332,13 +333,40 @@ typedef struct {
unsigned transmit_no_descriptor_errors;
unsigned transmit_overflow_errors;
unsigned transmit_fatal_errors;
+ uint32_t phy_id;
+ rtems_vector_number interrupt_number;
+ rtems_id control_task;
} lpc_eth_driver_entry;
-static lpc_eth_driver_entry lpc_eth_driver_data = {
- .state = LPC_ETH_NOT_INITIALIZED,
- .receive_task = RTEMS_ID_NONE,
- .transmit_task = RTEMS_ID_NONE
-};
+static lpc_eth_driver_entry lpc_eth_driver_data;
+
+static void lpc_eth_control_request_complete(const lpc_eth_driver_entry *e)
+{
+ rtems_status_code sc = rtems_event_transient_send(e->control_task);
+ assert(sc == RTEMS_SUCCESSFUL);
+}
+
+static void lpc_eth_control_request(
+ lpc_eth_driver_entry *e,
+ rtems_id task,
+ rtems_event_set event
+)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ uint32_t nest_count = 0;
+
+ e->control_task = rtems_task_self();
+
+ sc = rtems_bsdnet_event_send(task, event);
+ assert(sc == RTEMS_SUCCESSFUL);
+
+ nest_count = rtems_bsdnet_semaphore_release_recursive();
+ sc = rtems_event_transient_receive(RTEMS_WAIT, RTEMS_NO_TIMEOUT);
+ assert(sc == RTEMS_SUCCESSFUL);
+ rtems_bsdnet_semaphore_obtain_recursive(nest_count);
+
+ e->control_task = 0;
+}
static inline uint32_t lpc_eth_increment(
uint32_t value,
@@ -528,25 +556,32 @@ static void lpc_eth_receive_task(void *arg)
uint32_t const index_max = e->rx_unit_count - 1;
uint32_t produce_index = 0;
uint32_t consume_index = 0;
- uint32_t receive_index = 0;
LPC_ETH_PRINTF("%s\n", __func__);
/* Main event loop */
while (true) {
- bool wait_for_mbuf = false;
-
/* Wait for events */
sc = rtems_bsdnet_event_receive(
- LPC_ETH_EVENT_INITIALIZE | LPC_ETH_EVENT_INTERRUPT,
+ LPC_ETH_EVENT_INITIALIZE
+ | LPC_ETH_EVENT_STOP
+ | LPC_ETH_EVENT_INTERRUPT,
RTEMS_EVENT_ANY | RTEMS_WAIT,
RTEMS_NO_TIMEOUT,
&events
);
- RTEMS_CLEANUP_SC(sc, cleanup, "wait for events");
+ assert(sc == RTEMS_SUCCESSFUL);
LPC_ETH_PRINTF("rx: wake up: 0x%08" PRIx32 "\n", events);
+ /* Stop receiver? */
+ if ((events & LPC_ETH_EVENT_STOP) != 0) {
+ lpc_eth_control_request_complete(e);
+
+ /* Wait for events */
+ continue;
+ }
+
/* Initialize receiver? */
if ((events & LPC_ETH_EVENT_INITIALIZE) != 0) {
/* Disable receive interrupts */
@@ -592,7 +627,6 @@ static void lpc_eth_receive_task(void *arg)
/* Initialize indices */
produce_index = lpc_eth->rxproduceindex;
consume_index = lpc_eth->rxconsumeindex;
- receive_index = consume_index;
/* Enable receiver */
lpc_eth->command |= ETH_CMD_RX_ENABLE;
@@ -600,6 +634,8 @@ static void lpc_eth_receive_task(void *arg)
/* Enable receive interrupts */
lpc_eth_enable_receive_interrupts();
+ lpc_eth_control_request_complete(e);
+
/* Wait for events */
continue;
}
@@ -611,49 +647,47 @@ static void lpc_eth_receive_task(void *arg)
/* Get current produce index */
produce_index = lpc_eth->rxproduceindex;
- if (receive_index != produce_index) {
+ if (consume_index != produce_index) {
uint32_t stat = 0;
- /* Fragment mbuf */
- struct mbuf *m = mbufs [receive_index];
-
/* Fragment status */
rtems_cache_invalidate_multiple_data_lines(
- (void *) &status [receive_index],
+ (void *) &status [consume_index],
sizeof(status [0])
);
- stat = status [receive_index].info;
-
- /* Remove mbuf from table */
- mbufs [receive_index] = NULL;
+ stat = status [consume_index].info;
if (
(stat & ETH_RX_STAT_LAST_FLAG) != 0
&& (stat & LPC_ETH_RX_STAT_ERRORS) == 0
) {
- /* Ethernet header */
- struct ether_header *eh = mtod(m, struct ether_header *);
+ /* Received mbuf */
+ struct mbuf *m = mbufs [consume_index];
- /* Discard Ethernet header and CRC */
- int sz = (int) (stat & ETH_RX_STAT_RXSIZE_MASK) + 1
- - ETHER_HDR_LEN - ETHER_CRC_LEN;
+ if (lpc_eth_add_new_mbuf(ifp, desc, mbufs, consume_index, false)) {
+ /* Ethernet header */
+ struct ether_header *eh = mtod(m, struct ether_header *);
- /* Update mbuf */
- m->m_len = sz;
- m->m_pkthdr.len = sz;
- m->m_data = mtod(m, char *) + ETHER_HDR_LEN;
+ /* Discard Ethernet header and CRC */
+ int sz = (int) (stat & ETH_RX_STAT_RXSIZE_MASK) + 1
+ - ETHER_HDR_LEN - ETHER_CRC_LEN;
- LPC_ETH_PRINTF("rx: %02" PRIu32 ": %u\n", receive_index, sz);
+ /* Update mbuf */
+ m->m_len = sz;
+ m->m_pkthdr.len = sz;
+ m->m_data = mtod(m, char *) + ETHER_HDR_LEN;
- /* Hand over */
- ether_input(ifp, eh, m);
+ LPC_ETH_PRINTF("rx: %02" PRIu32 ": %u\n", consume_index, sz);
- /* Increment received frames counter */
- ++e->received_frames;
- } else {
- /* Release mbuf */
- m_free(m);
+ /* Hand over */
+ ether_input(ifp, eh, m);
+ /* Increment received frames counter */
+ ++e->received_frames;
+ } else {
+ ++e->receive_drop_errors;
+ }
+ } else {
/* Update error counters */
if ((stat & ETH_RX_STAT_OVERRUN) != 0) {
++e->receive_overrun_errors;
@@ -678,49 +712,16 @@ static void lpc_eth_receive_task(void *arg)
}
}
- /* Increment receive index */
- receive_index = lpc_eth_increment(receive_index, index_max);
+ /* Increment and update consume index */
+ consume_index = lpc_eth_increment(consume_index, index_max);
+ lpc_eth->rxconsumeindex = consume_index;
} else {
/* Nothing to do, enable receive interrupts */
lpc_eth_enable_receive_interrupts();
break;
}
}
-
- /* Wait for mbuf? */
- wait_for_mbuf =
- lpc_eth_increment(produce_index, index_max) == consume_index;
-
- /* Fill queue with new mbufs */
- while (consume_index != produce_index) {
- /* Add new mbuf to queue */
- if (
- !lpc_eth_add_new_mbuf(ifp, desc, mbufs, consume_index, wait_for_mbuf)
- ) {
- break;
- }
-
- /* We wait for at most one mbuf */
- wait_for_mbuf = false;
-
- /* Increment consume index */
- consume_index = lpc_eth_increment(consume_index, index_max);
-
- /* Update consume indices */
- lpc_eth->rxconsumeindex = consume_index;
- }
}
-
-cleanup:
-
- /* Clear task ID */
- e->receive_task = RTEMS_ID_NONE;
-
- /* Release network semaphore */
- rtems_bsdnet_semaphore_release();
-
- /* Terminate self */
- (void) rtems_task_delete(RTEMS_SELF);
}
static struct mbuf *lpc_eth_next_fragment(
@@ -811,16 +812,25 @@ static void lpc_eth_transmit_task(void *arg)
/* Wait for events */
sc = rtems_bsdnet_event_receive(
LPC_ETH_EVENT_INITIALIZE
- | LPC_ETH_EVENT_START
+ | LPC_ETH_EVENT_STOP
+ | LPC_ETH_EVENT_TXSTART
| LPC_ETH_EVENT_INTERRUPT,
RTEMS_EVENT_ANY | RTEMS_WAIT,
RTEMS_NO_TIMEOUT,
&events
);
- RTEMS_CLEANUP_SC(sc, cleanup, "wait for events");
+ assert(sc == RTEMS_SUCCESSFUL);
LPC_ETH_PRINTF("tx: wake up: 0x%08" PRIx32 "\n", events);
+ /* Stop transmitter? */
+ if ((events & LPC_ETH_EVENT_STOP) != 0) {
+ lpc_eth_control_request_complete(e);
+
+ /* Wait for events */
+ continue;
+ }
+
/* Initialize transmitter? */
if ((events & LPC_ETH_EVENT_INITIALIZE) != 0) {
/* Disable transmit interrupts */
@@ -869,6 +879,8 @@ static void lpc_eth_transmit_task(void *arg)
/* Enable transmitter */
lpc_eth->command |= ETH_CMD_TX_ENABLE;
+
+ lpc_eth_control_request_complete(e);
}
/* Free consumed fragments */
@@ -1065,30 +1077,26 @@ static void lpc_eth_transmit_task(void *arg)
lpc_eth_enable_transmit_interrupts();
}
}
-
-cleanup:
-
- /* Clear task ID */
- e->transmit_task = RTEMS_ID_NONE;
-
- /* Release network semaphore */
- rtems_bsdnet_semaphore_release();
-
- /* Terminate self */
- (void) rtems_task_delete(RTEMS_SELF);
}
-static void lpc_eth_mdio_wait_for_not_busy(void)
+static int lpc_eth_mdio_wait_for_not_busy(void)
{
- while ((lpc_eth->mind & ETH_MIND_BUSY) != 0) {
- rtems_task_wake_after(2);
+ rtems_interval one_second = rtems_clock_get_ticks_per_second();
+ rtems_interval i = 0;
+
+ while ((lpc_eth->mind & ETH_MIND_BUSY) != 0 && i < one_second) {
+ rtems_task_wake_after(1);
+ ++i;
}
+
+ return i != one_second ? 0 : ETIMEDOUT;
}
static uint32_t lpc_eth_mdio_read_anlpar(void)
{
uint32_t madr = ETH_MADR_REG(MII_ANLPAR) | ETH_MADR_PHY(DEFAULT_PHY);
uint32_t anlpar = 0;
+ int eno = 0;
if (lpc_eth->madr != madr) {
lpc_eth->madr = madr;
@@ -1099,9 +1107,10 @@ static uint32_t lpc_eth_mdio_read_anlpar(void)
lpc_eth->mcmd = ETH_MCMD_READ;
}
- lpc_eth_mdio_wait_for_not_busy();
-
- anlpar = lpc_eth->mrdd;
+ eno = lpc_eth_mdio_wait_for_not_busy();
+ if (eno == 0) {
+ anlpar = lpc_eth->mrdd;
+ }
/* Start next read */
lpc_eth->mcmd = 0;
@@ -1111,7 +1120,7 @@ static uint32_t lpc_eth_mdio_read_anlpar(void)
}
static int lpc_eth_mdio_read(
- int phy __attribute__((unused)),
+ int phy,
void *arg __attribute__((unused)),
unsigned reg,
uint32_t *val
@@ -1123,8 +1132,11 @@ static int lpc_eth_mdio_read(
lpc_eth->madr = ETH_MADR_REG(reg) | ETH_MADR_PHY(DEFAULT_PHY);
lpc_eth->mcmd = 0;
lpc_eth->mcmd = ETH_MCMD_READ;
- lpc_eth_mdio_wait_for_not_busy();
- *val = lpc_eth->mrdd;
+ eno = lpc_eth_mdio_wait_for_not_busy();
+
+ if (eno == 0) {
+ *val = lpc_eth->mrdd;
+ }
} else {
eno = EINVAL;
}
@@ -1133,7 +1145,7 @@ static int lpc_eth_mdio_read(
}
static int lpc_eth_mdio_write(
- int phy __attribute__((unused)),
+ int phy,
void *arg __attribute__((unused)),
unsigned reg,
uint32_t val
@@ -1144,7 +1156,7 @@ static int lpc_eth_mdio_write(
if (phy == -1 || phy == 0) {
lpc_eth->madr = ETH_MADR_REG(reg) | ETH_MADR_PHY(DEFAULT_PHY);
lpc_eth->mwtd = val;
- lpc_eth_mdio_wait_for_not_busy();
+ eno = lpc_eth_mdio_wait_for_not_busy();
} else {
eno = EINVAL;
}
@@ -1152,110 +1164,269 @@ static int lpc_eth_mdio_write(
return eno;
}
-static void lpc_eth_interface_init(void *arg)
+static int lpc_eth_phy_get_id(uint32_t *id)
{
+ uint32_t id1 = 0;
+ int eno = lpc_eth_mdio_read(DEFAULT_PHY, NULL, MII_PHYIDR1, &id1);
+
+ if (eno == 0) {
+ uint32_t id2 = 0;
+
+ eno = lpc_eth_mdio_read(DEFAULT_PHY, NULL, MII_PHYIDR2, &id2);
+ if (eno == 0) {
+ *id = (id1 << 16) | (id2 & 0xfff0);
+ }
+ }
+
+ return eno;
+}
+
+#define PHY_KSZ80X1RNL 0x221550
+
+typedef struct {
+ unsigned reg;
+ uint32_t set;
+ uint32_t clear;
+} lpc_eth_phy_action;
+
+static int lpc_eth_phy_set_and_clear(
+ const lpc_eth_phy_action *actions,
+ size_t n
+)
+{
+ int eno = 0;
+ size_t i;
+
+ for (i = 0; eno == 0 && i < n; ++i) {
+ const lpc_eth_phy_action *action = &actions [i];
+ uint32_t val;
+
+ eno = lpc_eth_mdio_read(DEFAULT_PHY, NULL, action->reg, &val);
+ if (eno == 0) {
+ val |= action->set;
+ val &= ~action->clear;
+ eno = lpc_eth_mdio_write(DEFAULT_PHY, NULL, action->reg, val);
+ }
+ }
+
+ return eno;
+}
+
+static const lpc_eth_phy_action lpc_eth_phy_up_action_default [] = {
+ { MII_BMCR, 0, BMCR_PDOWN },
+ { MII_BMCR, BMCR_RESET, 0 },
+ { MII_BMCR, BMCR_AUTOEN, 0 }
+};
+
+static const lpc_eth_phy_action lpc_eth_phy_up_pre_action_KSZ80X1RNL [] = {
+ /* Disable slow oscillator mode */
+ { 0x11, 0, 0x10 }
+};
+
+static const lpc_eth_phy_action lpc_eth_phy_up_post_action_KSZ80X1RNL [] = {
+ /* Enable energy detect power down (EDPD) mode */
+ { 0x18, 0x0800, 0 },
+ /* Turn PLL of automatically in EDPD mode */
+ { 0x10, 0x10, 0 }
+};
+
+static int lpc_eth_phy_up(lpc_eth_driver_entry *e)
+{
+ int eno = lpc_eth_phy_get_id(&e->phy_id);
+
+ if (eno == 0) {
+ switch (e->phy_id) {
+ case PHY_KSZ80X1RNL:
+ eno = lpc_eth_phy_set_and_clear(
+ &lpc_eth_phy_up_pre_action_KSZ80X1RNL [0],
+ RTEMS_ARRAY_SIZE(lpc_eth_phy_up_pre_action_KSZ80X1RNL)
+ );
+ break;
+ case 0:
+ case 0xfffffff0:
+ eno = EIO;
+ break;
+ default:
+ break;
+ }
+
+ if (eno == 0) {
+ eno = lpc_eth_phy_set_and_clear(
+ &lpc_eth_phy_up_action_default [0],
+ RTEMS_ARRAY_SIZE(lpc_eth_phy_up_action_default)
+ );
+ }
+
+ if (eno == 0) {
+ switch (e->phy_id) {
+ case PHY_KSZ80X1RNL:
+ eno = lpc_eth_phy_set_and_clear(
+ &lpc_eth_phy_up_post_action_KSZ80X1RNL [0],
+ RTEMS_ARRAY_SIZE(lpc_eth_phy_up_post_action_KSZ80X1RNL)
+ );
+ break;
+ default:
+ break;
+ }
+ }
+ } else {
+ e->phy_id = 0;
+ }
+
+ return eno;
+}
+
+static const lpc_eth_phy_action lpc_eth_phy_down_action_default [] = {
+ { MII_BMCR, BMCR_PDOWN, 0 }
+};
+
+static const lpc_eth_phy_action lpc_eth_phy_down_post_action_KSZ80X1RNL [] = {
+ /* Enable slow oscillator mode */
+ { 0x11, 0x10, 0 }
+};
+
+static void lpc_eth_phy_down(lpc_eth_driver_entry *e)
+{
+ int eno = lpc_eth_phy_set_and_clear(
+ &lpc_eth_phy_down_action_default [0],
+ RTEMS_ARRAY_SIZE(lpc_eth_phy_down_action_default)
+ );
+
+ if (eno == 0) {
+ switch (e->phy_id) {
+ case PHY_KSZ80X1RNL:
+ eno = lpc_eth_phy_set_and_clear(
+ &lpc_eth_phy_down_post_action_KSZ80X1RNL [0],
+ RTEMS_ARRAY_SIZE(lpc_eth_phy_down_post_action_KSZ80X1RNL)
+ );
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static void lpc_eth_soft_reset(void)
+{
+ lpc_eth->command = 0x38;
+ lpc_eth->mac1 = 0xcf00;
+ lpc_eth->mac1 = 0x0;
+}
+
+static int lpc_eth_up_or_down(lpc_eth_driver_entry *e, bool up)
+{
+ int eno = 0;
rtems_status_code sc = RTEMS_SUCCESSFUL;
- lpc_eth_driver_entry *e = (lpc_eth_driver_entry *) arg;
struct ifnet *ifp = &e->arpcom.ac_if;
- LPC_ETH_PRINTF("%s\n", __func__);
+ if (up && e->state == LPC_ETH_STATE_DOWN) {
- if (e->state == LPC_ETH_INITIALIZED) {
lpc_eth_config_module_enable();
- /* Soft reset */
-
- /* Do soft reset */
- lpc_eth->command = 0x38;
- lpc_eth->mac1 = 0xcf00;
- lpc_eth->mac1 = 0x0;
-
/* Initialize PHY */
lpc_eth->mcfg = ETH_MCFG_CLOCK_SELECT(0x7);
- /* TODO */
-
- /* Reinitialize registers */
- lpc_eth->mac2 = 0x31;
- lpc_eth->ipgt = 0x15;
- lpc_eth->ipgr = 0x12;
- lpc_eth->clrt = 0x370f;
- lpc_eth->maxf = 0x0600;
- lpc_eth->supp = ETH_SUPP_SPEED;
- lpc_eth->test = 0;
- #ifdef LPC_ETH_CONFIG_RMII
- lpc_eth->command = 0x0600;
- #else
- lpc_eth->command = 0x0400;
- #endif
- lpc_eth->intenable = 0;
- lpc_eth->intclear = 0x30ff;
- lpc_eth->powerdown = 0;
-
- /* MAC address */
- lpc_eth->sa0 = ((uint32_t) e->arpcom.ac_enaddr [5] << 8)
- | (uint32_t) e->arpcom.ac_enaddr [4];
- lpc_eth->sa1 = ((uint32_t) e->arpcom.ac_enaddr [3] << 8)
- | (uint32_t) e->arpcom.ac_enaddr [2];
- lpc_eth->sa2 = ((uint32_t) e->arpcom.ac_enaddr [1] << 8)
- | (uint32_t) e->arpcom.ac_enaddr [0];
-
- /* Enable receiver */
- lpc_eth->mac1 = 0x03;
-
- /* Start receive task */
- if (e->receive_task == RTEMS_ID_NONE) {
- e->receive_task = rtems_bsdnet_newproc(
- "ntrx",
- 4096,
- lpc_eth_receive_task,
- e
- );
- sc = rtems_bsdnet_event_send(e->receive_task, LPC_ETH_EVENT_INITIALIZE);
- RTEMS_SYSLOG_ERROR_SC(sc, "send receive initialize event");
- }
+ eno = lpc_eth_phy_up(e);
+
+ if (eno == 0) {
+ /*
+ * We must have a valid external clock from the PHY at this point,
+ * otherwise the system bus hangs and only a watchdog reset helps.
+ */
+ lpc_eth_soft_reset();
+
+ /* Reinitialize registers */
+ lpc_eth->mac2 = 0x31;
+ lpc_eth->ipgt = 0x15;
+ lpc_eth->ipgr = 0x12;
+ lpc_eth->clrt = 0x370f;
+ lpc_eth->maxf = 0x0600;
+ lpc_eth->supp = ETH_SUPP_SPEED;
+ lpc_eth->test = 0;
+ #ifdef LPC_ETH_CONFIG_RMII
+ lpc_eth->command = 0x0600;
+ #else
+ lpc_eth->command = 0x0400;
+ #endif
+ lpc_eth->intenable = ETH_INT_RX_OVERRUN | ETH_INT_TX_UNDERRUN;
+ lpc_eth->intclear = 0x30ff;
+ lpc_eth->powerdown = 0;
+
+ /* MAC address */
+ lpc_eth->sa0 = ((uint32_t) e->arpcom.ac_enaddr [5] << 8)
+ | (uint32_t) e->arpcom.ac_enaddr [4];
+ lpc_eth->sa1 = ((uint32_t) e->arpcom.ac_enaddr [3] << 8)
+ | (uint32_t) e->arpcom.ac_enaddr [2];
+ lpc_eth->sa2 = ((uint32_t) e->arpcom.ac_enaddr [1] << 8)
+ | (uint32_t) e->arpcom.ac_enaddr [0];
- /* Start transmit task */
- if (e->transmit_task == RTEMS_ID_NONE) {
- e->transmit_task = rtems_bsdnet_newproc(
- "nttx",
- 4096,
- lpc_eth_transmit_task,
+ /* Enable receiver */
+ lpc_eth->mac1 = 0x03;
+
+ /* Initialize tasks */
+ lpc_eth_control_request(e, e->receive_task, LPC_ETH_EVENT_INITIALIZE);
+ lpc_eth_control_request(e, e->transmit_task, LPC_ETH_EVENT_INITIALIZE);
+
+ /* Install interrupt handler */
+ sc = rtems_interrupt_handler_install(
+ e->interrupt_number,
+ "Ethernet",
+ RTEMS_INTERRUPT_UNIQUE,
+ lpc_eth_interrupt_handler,
e
);
- sc = rtems_bsdnet_event_send(e->transmit_task, LPC_ETH_EVENT_INITIALIZE);
- RTEMS_SYSLOG_ERROR_SC(sc, "send transmit initialize event");
- }
+ assert(sc == RTEMS_SUCCESSFUL);
- /* Change state */
- if (
- e->receive_task != RTEMS_ID_NONE && e->transmit_task != RTEMS_ID_NONE
- ) {
- e->state = LPC_ETH_STARTED;
+ /* Start watchdog timer */
+ ifp->if_timer = 1;
+
+ /* Change state */
+ e->state = LPC_ETH_STATE_UP;
}
- }
- if (e->state == LPC_ETH_STARTED) {
- /* Enable fatal interrupts */
- lpc_eth->intenable = ETH_INT_RX_OVERRUN | ETH_INT_TX_UNDERRUN;
+ if (eno != 0) {
+ ifp->if_flags &= ~IFF_UP;
+ }
+ } else if (!up && e->state == LPC_ETH_STATE_UP) {
+ /* Remove interrupt handler */
+ sc = rtems_interrupt_handler_remove(
+ e->interrupt_number,
+ lpc_eth_interrupt_handler,
+ e
+ );
+ assert(sc == RTEMS_SUCCESSFUL);
- /* Enable promiscous mode */
- lpc_eth_enable_promiscous_mode((ifp->if_flags & IFF_PROMISC) != 0);
+ /* Stop tasks */
+ lpc_eth_control_request(e, e->receive_task, LPC_ETH_EVENT_STOP);
+ lpc_eth_control_request(e, e->transmit_task, LPC_ETH_EVENT_STOP);
- /* Start watchdog timer */
- ifp->if_timer = 1;
+ lpc_eth_soft_reset();
+ lpc_eth_phy_down(e);
+ lpc_eth_config_module_disable();
- /* Set interface to running state */
- ifp->if_flags |= IFF_RUNNING;
+ /* Stop watchdog timer */
+ ifp->if_timer = 0;
/* Change state */
- e->state = LPC_ETH_RUNNING;
+ e->state = LPC_ETH_STATE_DOWN;
}
+
+ return eno;
+}
+
+static void lpc_eth_interface_init(void *arg)
+{
+ /* Nothing to do */
}
static void lpc_eth_interface_stats(lpc_eth_driver_entry *e)
{
- int media = IFM_MAKEWORD(0, 0, 0, 0);
- int eno = rtems_mii_ioctl(&e->mdio, e, SIOCGIFMEDIA, &media);
+ int eno = EIO;
+ int media = 0;
+
+ if (e->state == LPC_ETH_STATE_UP) {
+ media = IFM_MAKEWORD(0, 0, 0, 0);
+ eno = rtems_mii_ioctl(&e->mdio, e, SIOCGIFMEDIA, &media);
+ }
rtems_bsdnet_semaphore_release();
@@ -1268,6 +1439,7 @@ static void lpc_eth_interface_stats(lpc_eth_driver_entry *e)
printf("receive interrupts: %u\n", e->receive_interrupts);
printf("transmitted frames: %u\n", e->transmitted_frames);
printf("transmit interrupts: %u\n", e->transmit_interrupts);
+ printf("receive drop errors: %u\n", e->receive_drop_errors);
printf("receive overrun errors: %u\n", e->receive_overrun_errors);
printf("receive fragment errors: %u\n", e->receive_fragment_errors);
printf("receive CRC errors: %u\n", e->receive_crc_errors);
@@ -1358,12 +1530,9 @@ static int lpc_eth_interface_ioctl(
ether_ioctl(ifp, cmd, data);
break;
case SIOCSIFFLAGS:
- if (ifp->if_flags & IFF_RUNNING) {
- /* TODO: off */
- }
- if (ifp->if_flags & IFF_UP) {
- ifp->if_flags |= IFF_RUNNING;
- /* TODO: init */
+ eno = lpc_eth_up_or_down(e, (ifp->if_flags & IFF_UP) != 0);
+ if (eno == 0 && (ifp->if_flags & IFF_UP) != 0) {
+ lpc_eth_enable_promiscous_mode((ifp->if_flags & IFF_PROMISC) != 0);
}
break;
case SIOCADDMULTI:
@@ -1388,46 +1557,51 @@ static void lpc_eth_interface_start(struct ifnet *ifp)
ifp->if_flags |= IFF_OACTIVE;
- sc = rtems_bsdnet_event_send(e->transmit_task, LPC_ETH_EVENT_START);
- RTEMS_SYSLOG_ERROR_SC(sc, "send transmit start event");
+ if (e->state == LPC_ETH_STATE_UP) {
+ sc = rtems_bsdnet_event_send(e->transmit_task, LPC_ETH_EVENT_TXSTART);
+ assert(sc == RTEMS_SUCCESSFUL);
+ }
}
-static void lpc_eth_interface_watchdog(struct ifnet *ifp __attribute__((unused)))
+static void lpc_eth_interface_watchdog(struct ifnet *ifp)
{
lpc_eth_driver_entry *e = (lpc_eth_driver_entry *) ifp->if_softc;
- uint32_t anlpar = lpc_eth_mdio_read_anlpar();
-
- if (e->anlpar != anlpar) {
- bool full_duplex = false;
- bool speed = false;
-
- e->anlpar = anlpar;
-
- if ((anlpar & ANLPAR_TX_FD) != 0) {
- full_duplex = true;
- speed = true;
- } else if ((anlpar & ANLPAR_T4) != 0) {
- speed = true;
- } else if ((anlpar & ANLPAR_TX) != 0) {
- speed = true;
- } else if ((anlpar & ANLPAR_10_FD) != 0) {
- full_duplex = true;
- }
- if (full_duplex) {
- lpc_eth->mac2 |= ETH_MAC2_FULL_DUPLEX;
- } else {
- lpc_eth->mac2 &= ~ETH_MAC2_FULL_DUPLEX;
- }
+ if (e->state == LPC_ETH_STATE_UP) {
+ uint32_t anlpar = lpc_eth_mdio_read_anlpar();
- if (speed) {
- lpc_eth->supp |= ETH_SUPP_SPEED;
- } else {
- lpc_eth->supp &= ~ETH_SUPP_SPEED;
+ if (e->anlpar != anlpar) {
+ bool full_duplex = false;
+ bool speed = false;
+
+ e->anlpar = anlpar;
+
+ if ((anlpar & ANLPAR_TX_FD) != 0) {
+ full_duplex = true;
+ speed = true;
+ } else if ((anlpar & ANLPAR_T4) != 0) {
+ speed = true;
+ } else if ((anlpar & ANLPAR_TX) != 0) {
+ speed = true;
+ } else if ((anlpar & ANLPAR_10_FD) != 0) {
+ full_duplex = true;
+ }
+
+ if (full_duplex) {
+ lpc_eth->mac2 |= ETH_MAC2_FULL_DUPLEX;
+ } else {
+ lpc_eth->mac2 &= ~ETH_MAC2_FULL_DUPLEX;
+ }
+
+ if (speed) {
+ lpc_eth->supp |= ETH_SUPP_SPEED;
+ } else {
+ lpc_eth->supp &= ~ETH_SUPP_SPEED;
+ }
}
- }
- ifp->if_timer = WATCHDOG_TIMEOUT;
+ ifp->if_timer = WATCHDOG_TIMEOUT;
+ }
}
static unsigned lpc_eth_fixup_unit_count(int count, int default_value, int max)
@@ -1444,7 +1618,6 @@ static unsigned lpc_eth_fixup_unit_count(int count, int default_value, int max)
static int lpc_eth_attach(struct rtems_bsdnet_ifconfig *config)
{
- rtems_status_code sc = RTEMS_SUCCESSFUL;
lpc_eth_driver_entry *e = &lpc_eth_driver_data;
struct ifnet *ifp = &e->arpcom.ac_if;
char *unit_name = NULL;
@@ -1455,17 +1628,16 @@ static int lpc_eth_attach(struct rtems_bsdnet_ifconfig *config)
/* Check parameter */
if (unit_index < 0) {
- RTEMS_SYSLOG_ERROR("parse error for interface name\n");
return 0;
}
if (unit_index != 0) {
- RTEMS_DO_CLEANUP(cleanup, "unexpected unit number");
+ goto cleanup;
}
if (config->hardware_address == NULL) {
- RTEMS_DO_CLEANUP(cleanup, "MAC address missing");
+ goto cleanup;
}
- if (e->state != LPC_ETH_NOT_INITIALIZED) {
- RTEMS_DO_CLEANUP(cleanup, "already attached");
+ if (e->state != LPC_ETH_STATE_NOT_INITIALIZED) {
+ goto cleanup;
}
/* MDIO */
@@ -1496,18 +1668,8 @@ static int lpc_eth_attach(struct rtems_bsdnet_ifconfig *config)
);
config->xbuf_count = (int) e->tx_unit_count;
- /* Disable interrupts */
- lpc_eth->intenable = 0;
-
- /* Install interrupt handler */
- sc = rtems_interrupt_handler_install(
- config->irno,
- "Ethernet",
- RTEMS_INTERRUPT_UNIQUE,
- lpc_eth_interrupt_handler,
- e
- );
- RTEMS_CLEANUP_SC(sc, cleanup, "install interrupt handler");
+ /* Remember interrupt number */
+ e->interrupt_number = config->irno;
/* Copy MAC address */
memcpy(e->arpcom.ac_enaddr, config->hardware_address, ETHER_ADDR_LEN);
@@ -1524,7 +1686,7 @@ static int lpc_eth_attach(struct rtems_bsdnet_ifconfig *config)
+ LPC_ETH_CONFIG_TX_BUF_SIZE);
table_area = lpc_eth_config_alloc_table_area(table_area_size);
if (table_area == NULL) {
- RTEMS_DO_CLEANUP(cleanup, "no memory for table area");
+ goto cleanup;
}
memset(table_area, 0, table_area_size);
@@ -1565,8 +1727,23 @@ static int lpc_eth_attach(struct rtems_bsdnet_ifconfig *config)
ifp->if_snd.ifq_maxlen = ifqmaxlen;
ifp->if_timer = 0;
+ /* Create tasks */
+ e->receive_task = rtems_bsdnet_newproc(
+ "ntrx",
+ 4096,
+ lpc_eth_receive_task,
+ e
+ );
+ e->transmit_task = rtems_bsdnet_newproc(
+ "nttx",
+ 4096,
+ lpc_eth_transmit_task,
+ e
+ );
+
/* Change status */
- e->state = LPC_ETH_INITIALIZED;
+ ifp->if_flags |= IFF_RUNNING;
+ e->state = LPC_ETH_STATE_DOWN;
/* Attach the interface */
if_attach(ifp);