mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-28 12:30:58 +00:00
143 lines
5.3 KiB
C
143 lines
5.3 KiB
C
#include "net_builtin.h"
|
|
|
|
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_MCXW71) && \
|
|
MG_ENABLE_DRIVER_MCXW71
|
|
|
|
// ZLL, 802.15.4 link layer. See MCXW71 reference manual and NXP
|
|
// ieee_802_15_4 PHY sources: PhyPacketProcessor.c, PhyPlmeData.c, PhyISR.c
|
|
struct mg_zll {
|
|
volatile uint32_t IRQSTS, PHY_CTRL, EVENT_TMR, TIMESTAMP, T1CMP, T2CMP,
|
|
T2PRIMECMP, T3CMP, T4CMP, PA_PWR, CHANNEL_NUM0, LQI_AND_RSSI,
|
|
MACSHORTADDRS0, MACLONGADDRS0_LSB, MACLONGADDRS0_MSB, RX_FRAME_FILTER,
|
|
CCA_LQI_CTRL, CCA2_CTRL, RESERVED_0, DSM_CTRL, RESERVED_1,
|
|
MACSHORTADDRS1, MACLONGADDRS1_LSB, MACLONGADDRS1_MSB, DUAL_PAN_CTRL,
|
|
CHANNEL_NUM1, SAM_CTRL, SAM_TABLE, SAM_MATCH, SAM_FREE_IDX,
|
|
SEQ_CTRL_STS, ACKDELAY, FILTERFAIL_CODE, RX_WTR_MARK, RESERVED_2,
|
|
SLOT_PRELOAD, SEQ_STATE, TMR_PRESCALE, LENIENCY_LSB, LENIENCY_MSB,
|
|
PART_ID, COEX_CTRL, COEX_PRIORITY, ENHACK_CTRL0;
|
|
};
|
|
|
|
// TrustZone: code built for the secure state (-mcmse) uses the secure alias
|
|
// of the radio registers, 0x5xxxxxxx. Otherwise, the non-secure alias
|
|
#if defined(__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE & 2)
|
|
#define MGI_ZLL ((struct mg_zll *) 0x58A01000)
|
|
#define MGI_PKT_RAM ((volatile uint8_t *) 0x58A08000)
|
|
#else
|
|
#define MGI_ZLL ((struct mg_zll *) 0x48A01000)
|
|
#define MGI_PKT_RAM ((volatile uint8_t *) 0x48A08000)
|
|
#endif
|
|
|
|
#define MGI_TX_PB MGI_PKT_RAM // Length byte (incl. FCS), then frame
|
|
#define MGI_RX_PB (MGI_PKT_RAM + 0x80) // Received frame, no length byte
|
|
|
|
#define MGI_IRQSTS_SEQIRQ MG_BIT(0)
|
|
#define MGI_IRQSTS_RXIRQ MG_BIT(2)
|
|
#define MGI_IRQSTS_RX_LEN(x) (((x) >> 24) & 0x7f) // Frame length incl. FCS
|
|
#define MGI_PHY_CTRL_XCVSEQ 7U
|
|
#define MGI_PHY_CTRL_ALL_IRQ_MASKS 0x8007ff00U // Bits 8..18, 31: mask all IRQs
|
|
#define MGI_SEQ_STATE_MASK 0x1fU
|
|
#define MGI_SEQ_STATE_RX_WU 0x10U // RX warm up
|
|
#define MGI_SEQ_CTRL_NO_RX_RECYCLE MG_BIT(5)
|
|
#define MGI_FRAME_FILTER 0x70bU // Beacon, data, command; versions 0..2
|
|
|
|
enum { MGI_SEQ_IDLE = 0, MGI_SEQ_RX = 1, MGI_SEQ_TX = 2 }; // PHY_CTRL[XCVSEQ]
|
|
#define MGI_TX_TIMEOUT 400U // Symbols, 16 us: 127 bytes
|
|
|
|
// Symbol timer, 16 us, 24 bits
|
|
static uint32_t mg_mcxw71_symbols(void) {
|
|
return MGI_ZLL->EVENT_TMR >> 8;
|
|
}
|
|
|
|
// Wait for the sequence to end, up to MGI_TX_TIMEOUT symbols
|
|
static bool mg_mcxw71_wait(void) {
|
|
uint32_t start = mg_mcxw71_symbols();
|
|
while (((mg_mcxw71_symbols() - start) & 0xffffffU) < MGI_TX_TIMEOUT) {
|
|
if (MGI_ZLL->IRQSTS & MGI_IRQSTS_SEQIRQ) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Stop the current sequence, wait for the sequencer to go idle. See PhyAbort()
|
|
static void mg_mcxw71_idle(void) {
|
|
if (MGI_ZLL->PHY_CTRL & MGI_PHY_CTRL_XCVSEQ) {
|
|
while ((MGI_ZLL->SEQ_STATE & MGI_SEQ_STATE_MASK) == MGI_SEQ_STATE_RX_WU)
|
|
(void) 0;
|
|
MGI_ZLL->PHY_CTRL &= ~MGI_PHY_CTRL_XCVSEQ;
|
|
while (MGI_ZLL->SEQ_STATE & MGI_SEQ_STATE_MASK) (void) 0;
|
|
}
|
|
MGI_ZLL->IRQSTS = MGI_ZLL->IRQSTS; // Clear all flags, write 1 to clear
|
|
}
|
|
|
|
// Start a new sequence: MGI_SEQ_RX or MGI_SEQ_TX
|
|
static void mg_mcxw71_start(uint32_t seq) {
|
|
MGI_ZLL->IRQSTS = MGI_ZLL->IRQSTS;
|
|
MGI_ZLL->PHY_CTRL |= seq;
|
|
}
|
|
|
|
static bool mg_mcxw71_init(struct mg_tcpip_if *ifp) {
|
|
struct mg_l2_802154_data *d =
|
|
(struct mg_l2_802154_data *) ifp->driver_data;
|
|
if (d == NULL || d->channel < 11 || d->channel > 26) {
|
|
MG_ERROR(("Set driver_data, channel 11..26"));
|
|
return false;
|
|
}
|
|
MGI_ZLL->SEQ_CTRL_STS |= MGI_SEQ_CTRL_NO_RX_RECYCLE;
|
|
MGI_ZLL->PHY_CTRL = MGI_PHY_CTRL_ALL_IRQ_MASKS; // Mask all IRQs, we poll
|
|
MGI_ZLL->IRQSTS = MGI_ZLL->IRQSTS;
|
|
MGI_ZLL->SAM_CTRL = 0;
|
|
MGI_ZLL->RX_FRAME_FILTER = MGI_FRAME_FILTER;
|
|
MGI_ZLL->TMR_PRESCALE = 5; // 16 us timebase, one symbol
|
|
MGI_ZLL->CHANNEL_NUM0 = d->channel;
|
|
MGI_ZLL->MACSHORTADDRS0 = (0xfffeU << 16) | d->pan_id; // Filter by PAN ID
|
|
MGI_ZLL->PA_PWR = 0x3f; // PA slices, max output power
|
|
mg_mcxw71_start(MGI_SEQ_RX);
|
|
return true;
|
|
}
|
|
|
|
// Send one radio frame: MAC header and payload, no FCS
|
|
static size_t mg_mcxw71_send(const uint8_t *p, size_t len,
|
|
struct mg_tcpip_if *ifp) {
|
|
size_t i;
|
|
bool ok;
|
|
mg_mcxw71_idle();
|
|
MGI_TX_PB[0] = (uint8_t) (len + 2); // FCS is added by hardware
|
|
for (i = 0; i < len; i++) MGI_TX_PB[i + 1] = p[i]; // 8-bit access only
|
|
mg_mcxw71_start(MGI_SEQ_TX);
|
|
ok = mg_mcxw71_wait();
|
|
mg_mcxw71_idle();
|
|
mg_mcxw71_start(MGI_SEQ_RX);
|
|
(void) ifp;
|
|
return ok ? len : 0;
|
|
}
|
|
|
|
static size_t mg_mcxw71_tx(const void *buf, size_t len,
|
|
struct mg_tcpip_if *ifp) {
|
|
return mg_l2_802154_tx(ifp, buf, len, mg_mcxw71_send);
|
|
}
|
|
|
|
static size_t mg_mcxw71_rx(void *buf, size_t len, struct mg_tcpip_if *ifp) {
|
|
uint32_t irqsts = MGI_ZLL->IRQSTS;
|
|
uint8_t f[125];
|
|
size_t i, n = 0;
|
|
if ((irqsts & MGI_IRQSTS_SEQIRQ) == 0) return 0; // Still receiving
|
|
if (irqsts & MGI_IRQSTS_RXIRQ) {
|
|
n = MGI_IRQSTS_RX_LEN(irqsts);
|
|
n = n >= 2 ? n - 2 : 0; // Strip FCS
|
|
if (n > sizeof(f)) n = sizeof(f);
|
|
for (i = 0; i < n; i++) f[i] = MGI_RX_PB[i];
|
|
}
|
|
mg_mcxw71_idle();
|
|
mg_mcxw71_start(MGI_SEQ_RX);
|
|
return mg_l2_802154_rx(ifp, f, n, buf, len);
|
|
}
|
|
|
|
static bool mg_mcxw71_poll(struct mg_tcpip_if *ifp, bool s1) {
|
|
(void) ifp, (void) s1;
|
|
return true; // Radio link is always up
|
|
}
|
|
|
|
struct mg_tcpip_driver mg_tcpip_driver_mcxw71 = {mg_mcxw71_init, mg_mcxw71_tx,
|
|
mg_mcxw71_rx, mg_mcxw71_poll};
|
|
|
|
#endif
|