mirror of https://github.com/wolfSSL/wolfBoot.git
118 lines
4.0 KiB
C
118 lines
4.0 KiB
C
/* uart_drv_imx95_m7.c
|
|
*
|
|
* Console for the Cortex-M7 on the NXP i.MX95.
|
|
*
|
|
* Copyright (C) 2026 wolfSSL Inc.
|
|
*
|
|
* This file is part of wolfBoot.
|
|
*
|
|
* wolfBoot is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* wolfBoot is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
|
|
*/
|
|
|
|
/* This is a shared-memory console, not a peripheral driver.
|
|
*
|
|
* The M7's LPUART is not routed to an accessible header on the Toradex SMARC
|
|
* Development Board: markers written to /dev/ttyLP1..3 from Linux appear on no
|
|
* host serial channel, and Linux owns all four LPUART instances anyway, so an
|
|
* M7 writing to one would contend with it. Rather than depend on unproven pin
|
|
* routing, the console is a ring buffer in the M7's own reserved DDR window,
|
|
* which the A55 cluster reads with the memtool helper.
|
|
*
|
|
* The buffer sits at the top of the M7's 16 MiB carveout (memory@80000000),
|
|
* clear of the wolfBoot partitions lower down (BOOT 0x80100000, UPDATE
|
|
* 0x80500000, SWAP 0x80900000). It deliberately does NOT use the RPMsg
|
|
* vdevbuffer carveout, which belongs to the RPMsg transport. The header plus
|
|
* the ring occupy exactly the 64 KiB page below the status block; see
|
|
* hal/imx95_m7.h, which holds the layout and asserts that they do not overlap.
|
|
*
|
|
* Reader contract: `wr` counts bytes ever written and never wraps within a run,
|
|
* so a reader tracks its own position and copies (wr - pos) bytes from
|
|
* data[pos % size]. `size` is published in the header rather than assumed.
|
|
* Overrun is possible and expected for very chatty output; the reader detects
|
|
* it when (wr - pos) > size and reports the gap rather than silently printing
|
|
* corrupt text.
|
|
*
|
|
* Publish ordering is enforced with DMB, not by `volatile`: volatile binds the
|
|
* compiler only, while the A55 reading this ring is a separate observer of a
|
|
* Normal-memory region the M7 may reorder stores to.
|
|
*/
|
|
|
|
#include <stdint.h>
|
|
#include "hal/imx95_m7.h"
|
|
|
|
struct console_hdr {
|
|
volatile uint32_t magic;
|
|
volatile uint32_t wr; /* total bytes written, monotonic */
|
|
volatile uint32_t size; /* == CONSOLE_SIZE */
|
|
volatile uint32_t rsvd;
|
|
};
|
|
|
|
#define CONSOLE_HDR ((struct console_hdr *)CONSOLE_BASE)
|
|
#define CONSOLE_DATA ((volatile uint8_t *)(CONSOLE_BASE + CONSOLE_HDR_SIZE))
|
|
|
|
int uart_init(uint32_t bitrate, uint8_t data, char parity, uint8_t stop)
|
|
{
|
|
struct console_hdr *h = CONSOLE_HDR;
|
|
|
|
(void)bitrate;
|
|
(void)data;
|
|
(void)parity;
|
|
(void)stop;
|
|
|
|
h->wr = 0;
|
|
h->size = CONSOLE_SIZE;
|
|
/* Magic last, and behind a barrier: a reader that samples mid-init sees no
|
|
* valid console rather than a valid magic with a stale size. */
|
|
DMB();
|
|
h->magic = CONSOLE_MAGIC;
|
|
imx95_dcache_clean((const void *)h, sizeof(*h));
|
|
return 0;
|
|
}
|
|
|
|
int uart_tx(const uint8_t c)
|
|
{
|
|
struct console_hdr *h = CONSOLE_HDR;
|
|
uint32_t w;
|
|
uint32_t idx;
|
|
|
|
if (h->magic != CONSOLE_MAGIC)
|
|
(void)uart_init(0, 0, 'N', 0);
|
|
|
|
w = h->wr;
|
|
idx = w % CONSOLE_SIZE;
|
|
CONSOLE_DATA[idx] = c;
|
|
imx95_dcache_clean((const void *)&CONSOLE_DATA[idx], 1);
|
|
/* Publish the byte before advancing the counter, so a reader never sees a
|
|
* count covering a byte that has not been stored yet. */
|
|
DMB();
|
|
h->wr = w + 1;
|
|
imx95_dcache_clean((const void *)h, sizeof(*h));
|
|
return 0;
|
|
}
|
|
|
|
int uart_rx(uint8_t *c)
|
|
{
|
|
(void)c;
|
|
return -1; /* console is write-only */
|
|
}
|
|
|
|
void uart_write(const char *buf, unsigned int len)
|
|
{
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < len; i++)
|
|
(void)uart_tx((const uint8_t)buf[i]);
|
|
}
|