Skip to content
Open
Show file tree
Hide file tree
Changes from 5 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
64 changes: 37 additions & 27 deletions src/can2040.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// Copyright (C) 2022-2025 Kevin O'Connor <[email protected]>
//
// This file may be distributed under the terms of the GNU GPLv3 license.
// SPDX-License-Identifier: GPL-3.0-only
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Am I correct to assume it's GPL-3.0-only, and not -and-later?

Copy link
Author

@dsseng dsseng Jun 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Zephyr build system West uses SPDX IDs to keep track of license of each file compiled, so this is important to make sure whoever uses this driver in Zephyr (or as my out-of-tree integration package if upstreaming fails) can get your licensing information and stay compliant with it.

Glue code must be Apache for Zephyr codebase, but when linking build system will find SPDX tags and let the user know they use a GPL-licensed module, fetched from a separate repository at the user's decision.


#include <stdint.h> // uint32_t
#include <string.h> // memset
Expand All @@ -28,11 +29,16 @@

// Helper compiler definitions
#define barrier() __asm__ __volatile__("": : :"memory")
#define __DMB() __asm__ __volatile__("dmb 0xF": : :"memory")

// Zephyr already defines these helpers
// Avoid redefinition warnings
#ifndef __ZEPHYR__
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I appreciate your platform-agnostic approach, so I can make these macros individually ifndef-guarded to not introduce a Zephyr-specific thing

#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
#define __DMB() __asm__ __volatile__("dmb 0xF": : :"memory")
#endif

// Helper functions for writing to "io" memory
static inline void writel(void *addr, uint32_t val) {
Expand Down Expand Up @@ -374,9 +380,18 @@ pio_signal_clear_txpending(struct can2040 *cd)
pio_hw->irq = SI_TXPENDING >> 8;
}

void
can2040_ll_pio_set_clkdiv(struct can2040 *cd, uint32_t sys_clock, uint32_t bitrate)
{
pio_hw_t *pio_hw = cd->pio_hw;
uint32_t div = (256 / PIO_CLOCK_PER_BIT) * sys_clock / bitrate;
int i;
for (i=0; i<4; i++)
pio_hw->sm[i].clkdiv = div << PIO_SM0_CLKDIV_FRAC_LSB;
}

// Setup PIO state machines
static void
pio_sm_setup(struct can2040 *cd)
void can2040_ll_pio_sm_setup(struct can2040 *cd)
{
// Reset state machines
pio_hw_t *pio_hw = cd->pio_hw;
Expand Down Expand Up @@ -413,19 +428,16 @@ pio_setup(struct can2040 *cd, uint32_t sys_clock, uint32_t bitrate)
rp2040_clear_reset(rb);

// Setup and sync pio state machine clocks
pio_hw_t *pio_hw = cd->pio_hw;
uint32_t div = (256 / PIO_CLOCK_PER_BIT) * sys_clock / bitrate;
int i;
for (i=0; i<4; i++)
pio_hw->sm[i].clkdiv = div << PIO_SM0_CLKDIV_FRAC_LSB;
can2040_ll_pio_set_clkdiv(cd, sys_clock, bitrate);

// Configure gpiobase (on rp2350)
#if IS_RP2350
pio_hw_t *pio_hw = cd->pio_hw;
pio_hw->gpiobase = pio_gpiobase(cd);
#endif

// Configure state machines
pio_sm_setup(cd);
can2040_ll_pio_sm_setup(cd);

// Map Rx/Tx gpios
uint32_t pio_func = 6 + cd->pio_num;
Expand Down Expand Up @@ -1012,8 +1024,7 @@ enum {
};

// Reset any bits in the incoming parsing state
static void
data_state_clear_bits(struct can2040 *cd)
void can2040_ll_data_state_clear_bits(struct can2040 *cd)
{
cd->raw_bit_count = cd->unstuf.stuffed_bits = cd->unstuf.count_stuff = 0;
}
Expand All @@ -1027,13 +1038,12 @@ data_state_go_next(struct can2040 *cd, uint32_t state, uint32_t num_bits)
}

// Transition to the MS_DISCARD state - drop all bits until 6 passive bits
static void
data_state_go_discard(struct can2040 *cd)
void can2040_ll_data_state_go_discard(struct can2040 *cd)
{
if (pio_rx_check_stall(cd)) {
// CPU couldn't keep up for some read data - must reset pio state
data_state_clear_bits(cd);
pio_sm_setup(cd);
can2040_ll_data_state_clear_bits(cd);
can2040_ll_pio_sm_setup(cd);
report_callback_error(cd, 0);
}

Expand All @@ -1048,15 +1058,15 @@ static void
data_state_go_error(struct can2040 *cd)
{
cd->stats.parse_error++;
data_state_go_discard(cd);
can2040_ll_data_state_go_discard(cd);
}

// Received six dominant bits on the line
static void
data_state_line_error(struct can2040 *cd)
{
if (cd->parse_state == MS_DISCARD)
data_state_go_discard(cd);
can2040_ll_data_state_go_discard(cd);
else
data_state_go_error(cd);
}
Expand All @@ -1075,9 +1085,9 @@ data_state_line_passive(struct can2040 *cd)
uint32_t dom_bits = ~stuffed_bits;
if (!dom_bits) {
// Counter overflow in "sync" state machine - reset it
data_state_clear_bits(cd);
pio_sm_setup(cd);
data_state_go_discard(cd);
can2040_ll_data_state_clear_bits(cd);
can2040_ll_pio_sm_setup(cd);
can2040_ll_data_state_go_discard(cd);
return;
}

Expand All @@ -1087,7 +1097,7 @@ data_state_line_passive(struct can2040 *cd)
return;
}

data_state_go_discard(cd);
can2040_ll_data_state_go_discard(cd);
}

// Transition to MS_CRC state - await 16 bits of crc
Expand Down Expand Up @@ -1117,7 +1127,7 @@ data_state_go_data(struct can2040 *cd, uint32_t id, uint32_t data)
{
if (data & (0x03 << 4)) {
// Not a supported header
data_state_go_discard(cd);
can2040_ll_data_state_go_discard(cd);
return;
}
cd->parse_msg.data32[0] = cd->parse_msg.data32[1] = 0;
Expand Down Expand Up @@ -1246,7 +1256,7 @@ data_state_update_eof1(struct can2040 *cd, uint32_t data)
} else if (data >= 0x1c || (data >= 0x18 && report_is_not_in_tx(cd))) {
// Message fully transmitted - followed by "overload frame"
report_note_eof_success(cd);
data_state_go_discard(cd);
can2040_ll_data_state_go_discard(cd);
} else {
data_state_go_error(cd);
}
Expand All @@ -1256,7 +1266,7 @@ data_state_update_eof1(struct can2040 *cd, uint32_t data)
static void
data_state_update_discard(struct can2040 *cd, uint32_t data)
{
data_state_go_discard(cd);
can2040_ll_data_state_go_discard(cd);
}

// Update parsing state after reading the bits of the current field
Expand Down Expand Up @@ -1452,17 +1462,17 @@ can2040_start(struct can2040 *cd, uint32_t sys_clock, uint32_t bitrate
{
cd->gpio_rx = gpio_rx;
cd->gpio_tx = gpio_tx;
data_state_clear_bits(cd);
can2040_ll_data_state_clear_bits(cd);
pio_setup(cd, sys_clock, bitrate);
data_state_go_discard(cd);
can2040_ll_data_state_go_discard(cd);
}

// API function to stop can2040 code
void
can2040_stop(struct can2040 *cd)
{
pio_irq_disable(cd);
pio_sm_setup(cd);
can2040_ll_pio_sm_setup(cd);
}

// API function to access can2040 statistics
Expand Down
4 changes: 4 additions & 0 deletions src/can2040.h
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// Copyright (C) 2022-2025 Kevin O'Connor <[email protected]>
// SPDX-License-Identifier: GPL-3.0-only

#ifndef _CAN2040_H
#define _CAN2040_H

Expand Down Expand Up @@ -63,6 +66,7 @@ struct can2040 {
void *pio_hw;
uint32_t gpio_rx, gpio_tx;
can2040_rx_cb rx_cb;
void *rx_cb_user_data;
struct can2040_stats stats;

// Bit unstuffing
Expand Down
18 changes: 18 additions & 0 deletions src/can2040_ll.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Low-level definitions for CAN2040 functions
//
// Copyright (C) 2022-2025 Kevin O'Connor <[email protected]>
// Copyright (C) 2025 Dmitrii Sharshakov <[email protected]>
// SPDX-License-Identifier: GPL-3.0-only

#ifndef _CAN2040_LL_H
#define _CAN2040_LL_H

#include "can2040.h"
#include <stdint.h> // uint32_t

void can2040_ll_data_state_clear_bits(struct can2040 *cd);
void can2040_ll_data_state_go_discard(struct can2040 *cd);
void can2040_ll_pio_set_clkdiv(struct can2040 *cd, uint32_t sys_clock, uint32_t bitrate);
void can2040_ll_pio_sm_setup(struct can2040 *cd);
Comment on lines +13 to +16
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A different interfix than _ll_ can be thought of. Feel free to suggest, this was just first off the top of my head. These might also go unseparated from the "main" API if you wish


#endif // _CAN2040_LL_H
3 changes: 3 additions & 0 deletions zephyr/module.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
build:
cmake-ext: True
kconfig-ext: True