Skip to content

Commit

Permalink
cpu/samd5x/periph_can: fix RX
Browse files Browse the repository at this point in the history
CAN required CLK_CANx_APB and CLK_CANx_APB to be running and will not
request any clock by itself. We can ensure both clocks to be running
by preventing the MCU from entering IDLE state.

The SAMD5x/SAME5x Family Data Sheet says in Section
"39.6.9 Sleep Mode Operation" says:

> The CAN can be configured to operate in any idle sleep mode. The CAN
> cannot operate in Standby sleep mode.
>
> [...]
>
> To leave low power mode, CLK_CANx_APB and GCLK_CANx must be active
> before writing CCCR.CSR to '0'. The CAN will acknowledge this by
> resetting CCCR.CSA = 0. Afterwards, the application can restart CAN
> communication by resetting bit CCCR.INIT.

tl;dr: At most SAM0_PM_IDLE is allowed while not shutting down the CAN
controller, but even that will pause communication (including RX).

Apparently, the CAN controller was never tested without also using the
USB peripheral, which kept the clocks running as side effect.

(cherry picked from commit b6ebdfe)
  • Loading branch information
maribu committed Feb 3, 2025
1 parent d06a7d9 commit 1ea14e8
Showing 1 changed file with 37 additions and 1 deletion.
38 changes: 37 additions & 1 deletion cpu/samd5x/periph/can.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@
#include <assert.h>
#include <string.h>

#include "bitfield.h"
#include "can/device.h"
#include "periph/can.h"
#include "periph/gpio.h"
#include "can/device.h"
#include "pm_layered.h"

#define ENABLE_DEBUG 0
#include "debug.h"
Expand Down Expand Up @@ -105,8 +107,36 @@ static const struct can_bittiming_const bittiming_const = {
.brp_inc = 1,
};

static BITFIELD(_power_state, 2);

static int _power_on(can_t *dev)
{
/* CAN required CLK_CANx_APB and GCLK_CANx to be running and will not
* request any clock by itself. We can ensure both clocks to be running
* by preventing the MCU from entering IDLE state.
*
* The SAMD5x/SAME5x Family Data Sheet says in Section
* "39.6.9 Sleep Mode Operation" says:
*
* > The CAN can be configured to operate in any idle sleep mode. The CAN
* > cannot operate in Standby sleep mode.
* >
* > [...]
* >
* > To leave low power mode, CLK_CANx_APB and GCLK_CANx must be active
* > before writing CCCR.CSR to '0'. The CAN will acknowledge this by
* > resetting CCCR.CSA = 0. Afterwards, the application can restart CAN
* > communication by resetting bit CCCR.INIT.
*
* tl;dr: At most SAM0_PM_IDLE is allowed while not shutting down the CAN
* controller, but even that will pause communication (including RX).
*/
unsigned can_idx = dev->conf->can == CAN1;
if (IS_USED(MODULE_PM_LAYERED) && !bf_isset(_power_state, can_idx)) {
pm_block(SAM0_PM_IDLE);
bf_set(_power_state, can_idx);
}

if (dev->conf->can == CAN0) {
DEBUG_PUTS("CAN0 controller is used");
MCLK->AHBMASK.reg |= MCLK_AHBMASK_CAN0;
Expand All @@ -125,6 +155,12 @@ static int _power_on(can_t *dev)

static int _power_off(can_t *dev)
{
unsigned can_idx = dev->conf->can == CAN1;
if (IS_USED(MODULE_PM_LAYERED) && bf_isset(_power_state, can_idx)) {
pm_block(SAM0_PM_IDLE);
bf_unset(_power_state, can_idx);
}

if (dev->conf->can == CAN0) {
DEBUG_PUTS("CAN0 controller is used");
MCLK->AHBMASK.reg &= ~MCLK_AHBMASK_CAN0;
Expand Down

0 comments on commit 1ea14e8

Please sign in to comment.