From 1f96e95fde079b6eec0d59030d7828f9ded78d45 Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Wed, 30 Mar 2022 13:56:43 +0200 Subject: [PATCH] cpu/sam0_eth: implement SLEEP state This saves ~3 mA when the device is in SLEEP mode. --- cpu/sam0_common/periph/eth.c | 40 ++++++++++++++++++++++----- cpu/sam0_common/sam0_eth/eth-netdev.c | 21 ++++++++++++++ 2 files changed, 54 insertions(+), 7 deletions(-) diff --git a/cpu/sam0_common/periph/eth.c b/cpu/sam0_common/periph/eth.c index 8bc848125e..3b06ce21aa 100644 --- a/cpu/sam0_common/periph/eth.c +++ b/cpu/sam0_common/periph/eth.c @@ -92,6 +92,8 @@ static uint8_t rx_buf[ETH_RX_BUFFER_COUNT][ETH_RX_BUFFER_SIZE] __attribute__((a static uint8_t tx_buf[ETH_TX_BUFFER_COUNT][ETH_TX_BUFFER_SIZE] __attribute__((aligned(GMAC_BUF_ALIGNMENT))); extern sam0_eth_netdev_t _sam0_eth_dev; +static bool _is_sleeping; + /* Flush our reception buffers and reset reception internal mechanism, this function may be call from ISR context */ void sam0_clear_rx_buffers(void) @@ -104,6 +106,33 @@ void sam0_clear_rx_buffers(void) GMAC->RBQB.reg = (uint32_t) rx_desc; } +static void _enable_clock(void) +{ + /* Enable GMAC clocks */ + MCLK->AHBMASK.reg |= MCLK_AHBMASK_GMAC; + MCLK->APBCMASK.reg |= MCLK_APBCMASK_GMAC; +} + +static void _disable_clock(void) +{ + /* Disable GMAC clocks */ + MCLK->AHBMASK.reg &= ~MCLK_AHBMASK_GMAC; + MCLK->APBCMASK.reg &= ~MCLK_APBCMASK_GMAC; +} + +void sam0_eth_poweron(void) +{ + _enable_clock(); + sam0_clear_rx_buffers(); + _is_sleeping = false; +} + +void sam0_eth_poweroff(void) +{ + _is_sleeping = true; + _disable_clock(); +} + static void _init_desc_buf(void) { int i; @@ -175,6 +204,10 @@ int sam0_eth_send(const struct iolist *iolist) unsigned tx_len = 0; tx_curr = &tx_desc[tx_idx]; + if (_is_sleeping) { + return -ENOTSUP; + } + /* load packet data into TX buffer */ for (const iolist_t *iol = iolist; iol; iol = iol->iol_next) { if (tx_len + iol->iol_len > ETHERNET_MAX_LEN) { @@ -290,13 +323,6 @@ int sam0_eth_receive_blocking(char *data, unsigned max_len) return _try_receive(data, max_len, 1); } -static void _enable_clock(void) -{ - /* Enable GMAC clocks */ - MCLK->AHBMASK.reg |= MCLK_AHBMASK_GMAC; - MCLK->APBCMASK.reg |= MCLK_APBCMASK_GMAC; -} - int sam0_eth_init(void) { /* Enable clocks */ diff --git a/cpu/sam0_common/sam0_eth/eth-netdev.c b/cpu/sam0_common/sam0_eth/eth-netdev.c index e04616f9b0..48a1aa2fe8 100644 --- a/cpu/sam0_common/sam0_eth/eth-netdev.c +++ b/cpu/sam0_common/sam0_eth/eth-netdev.c @@ -36,6 +36,8 @@ /* Internal helpers */ extern int sam0_eth_init(void); +extern void sam0_eth_poweron(void); +extern void sam0_eth_poweroff(void); extern int sam0_eth_send(const struct iolist *iolist); extern int sam0_eth_receive_blocking(char *data, unsigned max_len); extern void sam0_eth_set_mac(const eui48_t *mac); @@ -101,6 +103,22 @@ static int _sam0_eth_get(netdev_t *netdev, netopt_t opt, void *val, size_t max_l return res; } +static int _set_state(netopt_state_t state) +{ + switch (state) { + case NETOPT_STATE_SLEEP: + sam0_eth_poweroff(); + break; + case NETOPT_STATE_IDLE: + sam0_eth_poweron(); + break; + default: + return -ENOTSUP; + } + + return sizeof(netopt_state_t); +} + static int _sam0_eth_set(netdev_t *netdev, netopt_t opt, const void *val, size_t max_len) { int res = -1; @@ -111,6 +129,9 @@ static int _sam0_eth_set(netdev_t *netdev, netopt_t opt, const void *val, size_t sam0_eth_set_mac((eui48_t *)val); res = ETHERNET_ADDR_LEN; break; + case NETOPT_STATE: + assert(max_len <= sizeof(netopt_state_t)); + return _set_state(*((const netopt_state_t *)val)); default: res = netdev_eth_set(netdev, opt, val, max_len); break;