Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions boards/seeeduino_arch-pro/Makefile.dep
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
ifneq (,$(filter netdev_default,$(USEMODULE)))
USEMODULE += lpc1768_eth
endif

ifneq (,$(filter saul_default,$(USEMODULE)))
USEMODULE += saul_gpio
endif
4 changes: 4 additions & 0 deletions boards/seeeduino_arch-pro/Makefile.features
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
CPU = lpc1768

# Put defined MCU peripherals here (in alphabetical order)
FEATURES_PROVIDED += periph_eth
FEATURES_PROVIDED += periph_gpio periph_gpio_irq
FEATURES_PROVIDED += periph_timer
FEATURES_PROVIDED += periph_uart

# Various other features (if any)
FEATURES_PROVIDED += lpc1768_eth
1 change: 1 addition & 0 deletions boards/seeeduino_arch-pro/doc.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ interface.
| UARTs | 3x USART |
| SPIs | 2x USART |
| I2Cs | 2x |
| Ethernet | 1 |
| Vcc | 2.4V - 3.6V |
| Datasheet | [Datasheet](https://www.nxp.com/docs/en/data-sheet/LPC1769_68_67_66_65_64_63.pdf)|
| Manual | [Manual](http://www.nxp.com/documents/user_manual/UM10360.pdf)|
Expand Down
20 changes: 19 additions & 1 deletion boards/seeeduino_arch-pro/include/periph_conf.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@
* @{
*
* @file
* @brief Peripheral MCU configuration for the Seeeduino Archo Pro board
* @brief Peripheral MCU configuration for the Seeeduino Arch Pro board
*
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
* @author Bas Stottelaar <basstottelaar@gmail.com>
*/

#include "mii.h"
#include "periph_cpu.h"
#include "vendor/conf.h"

Expand Down Expand Up @@ -74,6 +75,23 @@ static const uart_conf_t uart_config[] = {

/** @} */

/**
* @name Ethernet configuration
*
* The Seeeduino Arch Pro wires the LPC1768 EMAC to a TI DP83848 PHY at MIIM
* address 1. The 50 MHz RMII reference clock is supplied by an external
* oscillator whose enable line is driven by P1.27. A dedicated reset line for
* the PHY is available at P1.28.
* @{
*/
static const eth_conf_t eth_config = {
.phy_addr = 1, /* TI DP83848 */
.phy_en_pin = GPIO_PIN(1, 27),
.phy_rst_pin = GPIO_PIN(1, 28),
.speed = MII_BMCR_SPEED_100 | MII_BMCR_FULL_DPLX,
};
/** @} */

#ifdef __cplusplus
}
#endif
Expand Down
5 changes: 5 additions & 0 deletions cpu/lpc1768/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ MODULE = cpu
# add a list of subdirectories, that should also be build
DIRS = periph $(RIOTCPU)/cortexm_common

# add LPC1768 specific drivers, if enabled
ifneq (,$(filter lpc1768_eth,$(USEMODULE)))
DIRS += drivers/eth
endif

# (file triggers compiler bug. see #5775)
SRC_NOLTO += vectors.c

Expand Down
19 changes: 19 additions & 0 deletions cpu/lpc1768/Makefile.dep
Original file line number Diff line number Diff line change
@@ -1 +1,20 @@
ifneq (,$(filter lpc1768_eth_auto,$(USEMODULE)))
USEMODULE += lpc1768_eth_link_up
endif

ifneq (,$(filter lpc1768_eth,$(USEMODULE)))
FEATURES_REQUIRED += periph_eth

USEMODULE += iolist
USEMODULE += netdev_eth
USEMODULE += netdev_new_api
USEMODULE += ztimer_msec

# lwip IPv6 supports needs link up events to perform duplicate address
# detection
ifneq (,$(filter lwip_ipv6,$(USEMODULE)))
USEMODULE += lpc1768_eth_link_up
endif
endif

include $(RIOTCPU)/cortexm_common/Makefile.dep
3 changes: 3 additions & 0 deletions cpu/lpc1768/Makefile.include
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,7 @@ RAM_START_ADDR ?= 0x100000C8
ROM_LEN ?= 0x80000
RAM_LEN ?= 0x7f38 # 32K - 0xC8

# include LPC1768 specific driver headers
INCLUDES += -I$(RIOTCPU)/lpc1768/include/drivers

include $(RIOTMAKE)/arch/cortexm.inc.mk
3 changes: 3 additions & 0 deletions cpu/lpc1768/drivers/doc.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@defgroup cpu_lpc1768_drivers LPC1768 specific drivers
@ingroup cpu_lpc1768
@brief Specific drivers for the LPC1768 CPU.
3 changes: 3 additions & 0 deletions cpu/lpc1768/drivers/eth/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
MODULE = lpc1768_eth

include $(RIOTBASE)/Makefile.base
249 changes: 249 additions & 0 deletions cpu/lpc1768/drivers/eth/eth_netdev.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
/*
* SPDX-FileCopyrightText: 2026 Bas Stottelaar <basstottelaar@gmail.com>
* SPDX-License-Identifier: LGPL-2.1-only
*/

/**
* @ingroup cpu_lpc1768_eth
*
* @{
* @file
* @brief netdev driver for the LPC1768 EMAC peripheral
*
* @author Bas Stottelaar <basstottelaar@gmail.com>
* @}
*/

#include <assert.h>
#include <errno.h>

#include "config.h"
#include "kernel_defines.h"
#include "net/ethernet.h"
#include "net/eui_provider.h"
#include "net/eui48.h"
#include "net/netdev.h"
#include "net/netdev/eth.h"
#include "net/netopt.h"
#include "periph/eth.h"
#include "ztimer.h"

#include "lpc1768_eth_netdev.h"

#define ENABLE_DEBUG 0
#define ENABLE_TRACE 0
#include "debug.h"

#if ENABLE_TRACE
# define TRACE(...) DEBUG(__VA_ARGS__)
# define TRACE_PUTS(...) DEBUG_PUTS(__VA_ARGS__)
#else
# define TRACE(...)
# define TRACE_PUTS(...)
#endif

#define LINK_STATE (0x01)
#define LINK_STATE_NOTIFIED (0x02)
#define LINK_STATE_DOWN (0x00)
#define LINK_STATE_UP (0x01)
#define LINK_STATE_NOTIFIED_DOWN (LINK_STATE_NOTIFIED | LINK_STATE_DOWN)
#define LINK_STATE_NOTIFIED_UP (LINK_STATE_NOTIFIED | LINK_STATE_UP)

/**
* @brief Singleton netdev instance for the LPC1768 EMAC peripheral.
*
* This is used by the low-level ethernet driver to deliver ISR events.
*/
netdev_t *lpc1768_eth_netdev;

static int _tx_pending_len;

static ztimer_t _link_timer;
static uint8_t _link_state = LINK_STATE_NOTIFIED_DOWN;

static void _link_cb(void *arg)
{
netdev_t *netdev = arg;

bool up = lpc1768_eth_link_up();
uint8_t state = up ? LINK_STATE_UP : LINK_STATE_DOWN;

/* emit event if the link state has changed */
if ((_link_state & LINK_STATE) != state) {
_link_state = state;
netdev_trigger_event_isr(netdev);
}

ztimer_set(ZTIMER_MSEC, &_link_timer, CONFIG_LPC1768_ETH_LINK_POLL_MS);
}

static int _init(netdev_t *netdev)
{
eui48_t hwaddr;

DEBUG_PUTS("[eth-netdev] _init: netdev init");

netdev_eui48_get(netdev, &hwaddr);

int res = lpc1768_eth_init(hwaddr.uint8);

if (res < 0) {
DEBUG("[eth-netdev] _init: init failed (%d)\n", res);
return res;
}

if (IS_USED(MODULE_LPC1768_ETH_LINK_UP)) {
if (IS_USED(MODULE_LPC1768_ETH_AUTO)) {
/* start auto-negotiation of the link speed */
lpc1768_eth_start_auto_negotiation();
}

/* start timer to check link state */
_link_timer.callback = _link_cb;
_link_timer.arg = netdev;

ztimer_set(ZTIMER_MSEC, &_link_timer, CONFIG_LPC1768_ETH_LINK_POLL_MS);
}
else {
/* assume link is up */
netdev->event_callback(netdev, NETDEV_EVENT_LINK_UP);
}

return 0;
}

static int _send(netdev_t *netdev, const iolist_t *iolist)
{
(void)netdev;

TRACE("[eth-netdev] _send: sending packet, length=%" PRIuSIZE " bytes\n", iolist_size(iolist));

int res = lpc1768_eth_send(iolist);

if (res < 0) {
return res;
}

_tx_pending_len = res;

return 0;
}

static int _confirm_send(netdev_t *netdev, void *info)
{
(void)netdev;
(void)info;

if (!lpc1768_eth_tx_done()) {
return -EAGAIN;
}

return _tx_pending_len;
}

static int _recv(netdev_t *netdev, void *buf, size_t len, void *info)
{
(void)netdev;

TRACE("[eth-netdev] _recv: receiving frame, length=%" PRIuSIZE " bytes\n", len);

int res = lpc1768_eth_recv(buf, len, info);

/* signal event loop that there is more to consume, in case an interrupt
* was missed */
if (lpc1768_eth_rx_pending()) {
netdev_trigger_event_isr(netdev);
}

return res;
}

static void _isr(netdev_t *netdev)
{
#if IS_USED(MODULE_LPC1768_ETH_LINK_UP)
switch (_link_state) {
case LINK_STATE_UP:
DEBUG_PUTS("[eth-netdev] _isr: link up");
if (IS_USED(MODULE_LPC1768_ETH_AUTO)) {
/* complete auto-negotiation of the link */
lpc1768_eth_complete_auto_negotiation();
}
netdev->event_callback(netdev, NETDEV_EVENT_LINK_UP);
_link_state = LINK_STATE_NOTIFIED_UP;
return;
case LINK_STATE_DOWN:
DEBUG_PUTS("[eth-netdev] _isr: link down");
netdev->event_callback(netdev, NETDEV_EVENT_LINK_DOWN);
_link_state = LINK_STATE_NOTIFIED_DOWN;
return;
default:
break;
}
#endif

/* receive if necessary */
if (lpc1768_eth_rx_pending()) {
netdev->event_callback(netdev, NETDEV_EVENT_RX_COMPLETE);
}
}

static int _get(netdev_t *netdev, netopt_t opt, void *val, size_t max_len)
{
switch (opt) {
case NETOPT_ADDRESS:
assert(max_len >= ETHERNET_ADDR_LEN);
lpc1768_eth_get_mac((uint8_t *)val);
return ETHERNET_ADDR_LEN;
case NETOPT_LINK:
assert(max_len == sizeof(netopt_enable_t));
*(netopt_enable_t *)val = lpc1768_eth_link_up() ? NETOPT_ENABLE : NETOPT_DISABLE;
return sizeof(netopt_enable_t);
case NETOPT_PROMISCUOUSMODE:
assert(max_len == sizeof(netopt_enable_t));
*(netopt_enable_t *)val = lpc1768_eth_get_promiscuous() ? NETOPT_ENABLE : NETOPT_DISABLE;
return sizeof(netopt_enable_t);
default:
return netdev_eth_get(netdev, opt, val, max_len);
}
}

static int _set(netdev_t *netdev, netopt_t opt, const void *val, size_t len)
{
switch (opt) {
case NETOPT_ADDRESS:
assert(len >= ETHERNET_ADDR_LEN);
lpc1768_eth_set_mac((const uint8_t *)val);
return ETHERNET_ADDR_LEN;
case NETOPT_PROMISCUOUSMODE:
assert(len == sizeof(netopt_enable_t));
lpc1768_eth_set_promiscuous(*(const netopt_enable_t *)val == NETOPT_ENABLE);
return sizeof(netopt_enable_t);
default:
return netdev_eth_set(netdev, opt, val, len);
}
}

const netdev_driver_t lpc1768_eth_driver = {
.send = _send,
.confirm_send = _confirm_send,
.recv = _recv,
.init = _init,
.isr = _isr,
.get = _get,
.set = _set,
};

void lpc1768_eth_netdev_setup(netdev_t *netdev)
{
DEBUG_PUTS("[eth-netdev] lpc1768_eth_netdev_setup: registering netdev");

/* keep local netdev copy to invoke netdev ISR from ethernet ISR */
lpc1768_eth_netdev = netdev;

/* initialize netdev fields */
netdev->event_callback = NULL;
netdev->context = NULL;
netdev->driver = &lpc1768_eth_driver;

netdev_register(netdev, NETDEV_LPC1768_ETH, 0);
}
Loading
Loading