Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 | /*
* Copyright (c) 2023 Synopsys Inc. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/drivers/uart.h>
#include <zephyr/kernel.h>
#include <string.h>
#define DT_DRV_COMPAT snps_hostlink_uart
/* Only supported by HW and nSIM targets */
BUILD_ASSERT(!IS_ENABLED(CONFIG_QEMU_TARGET));
/* Only supported by ARC targets */
BUILD_ASSERT(IS_ENABLED(CONFIG_ARC));
#define HL_SYSCALL_OPEN 0
#define HL_SYSCALL_CLOSE 1
#define HL_SYSCALL_READ 2
#define HL_SYSCALL_WRITE 3
#define HL_SYSCALL_LSEEK 4
#define HL_SYSCALL_UNLINK 5
#define HL_SYSCALL_ISATTY 6
#define HL_SYSCALL_TMPNAM 7
#define HL_SYSCALL_GETENV 8
#define HL_SYSCALL_CLOCK 9
#define HL_SYSCALL_TIME 10
#define HL_SYSCALL_RENAME 11
#define HL_SYSCALL_ARGC 12
#define HL_SYSCALL_ARGV 13
#define HL_SYSCALL_RETCODE 14
#define HL_SYSCALL_ACCESS 15
#define HL_SYSCALL_GETPID 16
#define HL_SYSCALL_GETCWD 17
#define HL_SYSCALL_USER 18
#ifndef __noinline
#define __noinline __attribute__((noinline))
#endif /* __noinline */
#define HL_VERSION 1
/* "No message here" mark. */
#define HL_NOADDRESS 0xFFFFFFFF
/* TODO: if we want to carve some additional space we can use the actual maximum processor cache
* line size here (i.e 128)
*/
#define HL_MAX_DCACHE_LINE 256
/* Hostlink gateway structure. */
struct hl_hdr {
uint32_t version; /* Current version is 1. */
uint32_t target2host_addr; /* Packet address from target to host. */
uint32_t host2target_addr; /* Packet address from host to target. */
uint32_t buf_addr; /* Address for host to write answer. */
uint32_t payload_size; /* Buffer size without packet header. */
uint32_t options; /* For future use. */
uint32_t break_to_mon_addr; /* For future use. */
} __packed;
/* Hostlink packet header. */
struct hl_pkt_hdr {
uint32_t packet_id; /* Packet id. Always set to 1 here. */
uint32_t total_size; /* Size of packet including header. */
uint32_t priority; /* For future use. */
uint32_t type; /* For future use. */
uint32_t checksum; /* For future use. */
} __packed;
struct hl_packed_int {
volatile uint16_t type;
volatile uint16_t size;
volatile int32_t value;
} __packed;
struct hl_packed_short_buff {
volatile uint16_t type;
volatile uint16_t size;
volatile uint8_t payload_short[4];
} __packed;
BUILD_ASSERT(sizeof(struct hl_packed_int) == sizeof(struct hl_packed_short_buff));
struct hl_pkt_write_char_put {
struct hl_packed_int syscall_nr;
struct hl_packed_int fd;
struct hl_packed_short_buff buff;
struct hl_packed_int nbyte;
} __packed;
struct hl_pkt_write_char_get {
struct hl_packed_int byte_written;
struct hl_packed_int host_errno;
} __packed;
#define MAX_PKT_SZ MAX(sizeof(struct hl_pkt_write_char_put), sizeof(struct hl_pkt_write_char_get))
#define HL_HEADERS_SZ (sizeof(struct hl_hdr) + sizeof(struct hl_pkt_hdr))
BUILD_ASSERT(HL_HEADERS_SZ + MAX_PKT_SZ < HL_MAX_DCACHE_LINE);
union payload_u {
struct hl_pkt_write_char_put pkt_write_char_put;
struct hl_pkt_write_char_get pkt_write_char_get;
char reserved[HL_MAX_DCACHE_LINE - HL_HEADERS_SZ];
} __packed;
BUILD_ASSERT(sizeof(union payload_u) % 4 == 0);
/* Main hostlink structure. */
struct hl {
/* General hostlink information. */
volatile struct hl_hdr hdr;
/* Start of the hostlink buffer. */
volatile struct hl_pkt_hdr pkt_hdr;
/* Payload buffer */
volatile union payload_u payload;
} __aligned(HL_MAX_DCACHE_LINE) __packed;
/* In general we must exactly fit into one or multiple cache lines as we shouldn't share hostlink
* buffer (which is uncached) with any cached data
*/
BUILD_ASSERT(sizeof(struct hl) % HL_MAX_DCACHE_LINE == 0);
/* However, with current supported functionality we fit into one MAX cache line. If we add
* some features which require bigger payload buffer this might become not true.
*/
BUILD_ASSERT(sizeof(struct hl) == HL_MAX_DCACHE_LINE);
/* Main structure. Do not rename as nSIM simulator / MDB debugger looks for the '__HOSTLINK__'
* symbol. We need to keep it initialized so it won't be put into BSS (so we won't write with
* regular cached access in it).
*/
volatile struct hl __HOSTLINK__ = {
.hdr = {
.version = HL_VERSION,
.target2host_addr = HL_NOADDRESS
}
};
BUILD_ASSERT(sizeof(__HOSTLINK__) % HL_MAX_DCACHE_LINE == 0);
#if defined(__CCAC__)
#define HL_HAS_C_ACCESSORS 0
#elif defined(CONFIG_ISA_ARCV3)
#define HL_HAS_C_ACCESSORS 0
#else
#define HL_HAS_C_ACCESSORS 1
#endif
#if HL_HAS_C_ACCESSORS
#ifndef __uncached
#define __uncached __attribute__((uncached))
#endif /* __uncached */
static inline void hl_write32(volatile void *addr, uint32_t val)
{
*(volatile __uncached uint32_t *)addr = val;
}
static inline void hl_write16(volatile void *addr, uint16_t val)
{
*(volatile __uncached uint16_t *)addr = val;
}
static inline void hl_write8(volatile void *addr, uint8_t val)
{
*(volatile __uncached uint8_t *)addr = val;
}
static inline uint32_t hl_read32(volatile void *addr)
{
return *(volatile __uncached uint32_t *)addr;
}
static inline uint16_t hl_read16(volatile void *addr)
{
return *(volatile __uncached uint16_t *)addr;
}
#else
static inline void hl_write32(volatile void *addr, uint32_t val)
{
__asm__ __volatile__("st.di %0, [%1]" :: "r" (val), "r" (addr) : "memory");
}
static inline void hl_write16(volatile void *addr, uint16_t val)
{
__asm__ __volatile__("stb.di %0, [%1]" :: "r" (val), "r" (addr) : "memory");
}
static inline void hl_write8(volatile void *addr, uint8_t val)
{
__asm__ __volatile__("sth.di %0, [%1]" :: "r" (val), "r" (addr) : "memory");
}
static inline uint32_t hl_read32(volatile void *addr)
{
uint32_t w;
__asm__ __volatile__("ld.di %0, [%1]" : "=r" (w) : "r" (addr) : "memory");
return w;
}
static inline uint16_t hl_read16(volatile void *addr)
{
uint16_t w;
__asm__ __volatile__("ld.di %0, [%1]" : "=r" (w) : "r" (addr) : "memory");
return w;
}
#endif /* HL_HAS_C_ACCESSORS */
/* Get hostlink payload size (iochunk + reserved space). */
static uint32_t hl_payload_size(void)
{
return sizeof(__HOSTLINK__.payload);
}
#define ALIGN(x, y) (((x) + ((y) - 1)) & ~((y) - 1))
/* Fill hostlink packet header. */
static void hl_pkt_init(volatile struct hl_pkt_hdr *pkt, int size)
{
hl_write32(&pkt->packet_id, 1);
hl_write32(&pkt->total_size, ALIGN(size, 4) + sizeof(struct hl_pkt_hdr));
hl_write32(&pkt->priority, 0);
hl_write32(&pkt->type, 0);
hl_write32(&pkt->checksum, 0);
}
/* Send hostlink packet to the host. */
static void hl_static_send(size_t payload_used)
{
/* We are OK to cast pointer to uint32_t even on 64bit platforms as we support to build
* Zephyr on ARCv3 64bit only to lower 4GiB. Still we need to cast via uintptr_t to avoid
* compiler warnings.
*/
uint32_t buf_addr = (uint32_t)(uintptr_t)(&__HOSTLINK__.pkt_hdr);
hl_pkt_init(&__HOSTLINK__.pkt_hdr, payload_used);
hl_write32(&__HOSTLINK__.hdr.buf_addr, buf_addr);
hl_write32(&__HOSTLINK__.hdr.payload_size, hl_payload_size());
hl_write32(&__HOSTLINK__.hdr.host2target_addr, HL_NOADDRESS);
hl_write32(&__HOSTLINK__.hdr.version, HL_VERSION);
hl_write32(&__HOSTLINK__.hdr.options, 0);
hl_write32(&__HOSTLINK__.hdr.break_to_mon_addr, 0);
compiler_barrier();
/* This tells the debugger we have a command.
* It is responsibility of debugger to set this back to HL_NOADDRESS
* after receiving the packet.
* Please note that we don't wait here because some implementations
* use hl_blockedPeek() function as a signal that we send a messege.
*/
hl_write32(&__HOSTLINK__.hdr.target2host_addr, buf_addr);
compiler_barrier();
}
/*
* Wait for host response and return pointer to hostlink payload.
* Symbol hl_blockedPeek() is used by the simulator as message signal.
*/
static void __noinline _hl_blockedPeek(void)
{
while (hl_read32(&__HOSTLINK__.hdr.host2target_addr) == HL_NOADDRESS) {
/* TODO: Timeout. */
}
}
static void hl_static_recv(void)
{
compiler_barrier();
_hl_blockedPeek();
compiler_barrier();
}
/* Mark hostlink buffer as "No message here". */
static void hl_delete(void)
{
hl_write32(&__HOSTLINK__.hdr.target2host_addr, HL_NOADDRESS);
}
/* Parameter types. */
#define PAT_CHAR 1
#define PAT_SHORT 2
#define PAT_INT 3
#define PAT_STRING 4
/* For future use. */
#define PAT_INT64 5
static void hl_static_pack_int(volatile struct hl_packed_int *pack, int32_t value)
{
hl_write16(&pack->type, PAT_INT);
hl_write16(&pack->size, 4);
hl_write32(&pack->value, value);
}
static void hl_static_pack_char(volatile struct hl_packed_short_buff *pack, unsigned char c)
{
hl_write16(&pack->type, PAT_STRING);
hl_write16(&pack->size, 1);
hl_write8(&pack->payload_short, c);
}
static int hl_static_unpack_int(volatile struct hl_packed_int *pack, int32_t *value)
{
uint16_t type = hl_read16(&pack->type);
uint16_t size = hl_read16(&pack->size);
if (type != PAT_INT) {
return -1;
}
if (size != 4) {
return -1;
}
*value = hl_read32(&pack->value);
return 0;
}
static inline int32_t hl_write_char(int fd, const char c)
{
/*
* Format:
* in, int -> syscall (HL_SYSCALL_WRITE)
* in, int -> file descriptor
* in, ptr -> buffer
* in, int -> bytes number
* out, int -> bytes written
* out, int, host errno
*/
hl_static_pack_int(&__HOSTLINK__.payload.pkt_write_char_put.syscall_nr, HL_SYSCALL_WRITE);
hl_static_pack_int(&__HOSTLINK__.payload.pkt_write_char_put.fd, fd);
hl_static_pack_char(&__HOSTLINK__.payload.pkt_write_char_put.buff, c);
hl_static_pack_int(&__HOSTLINK__.payload.pkt_write_char_put.nbyte, 1);
hl_static_send(sizeof(struct hl_pkt_write_char_put));
hl_static_recv();
int32_t bwr = 0;
int ret = hl_static_unpack_int(&__HOSTLINK__.payload.pkt_write_char_get.byte_written, &bwr);
/* we can get host errno here with:
* hl_static_unpack_int(&__HOSTLINK__.pkt_write_char_get.host_errno, &host_errno);
* but we don't need it for UART emulation.
*/
if (bwr <= 0) {
ret = -1;
}
hl_delete();
return ret;
}
/**
* @brief Poll the device for input.
*
* @param dev UART device struct
* @param c Pointer to character
*
* @return 0 if a character arrived, -1 if the input buffer if empty.
*/
static int uart_hostlink_poll_in(const struct device *dev, unsigned char *c)
{
ARG_UNUSED(dev);
/* We plan to use hostlink for logging, so no much sense in poll_in implementation */
return -1;
}
/**
* @brief Output a character in polled mode.
*
* @param dev UART device struct
* @param c Character to send
*/
static void uart_hostlink_poll_out(const struct device *dev, unsigned char c)
{
ARG_UNUSED(dev);
hl_write_char(1, c);
}
static const struct uart_driver_api uart_hostlink_driver_api = {
.poll_in = uart_hostlink_poll_in,
.poll_out = uart_hostlink_poll_out,
};
DEVICE_DT_DEFINE(DT_NODELABEL(hostlink), NULL, NULL, NULL, NULL, PRE_KERNEL_1,
CONFIG_SERIAL_INIT_PRIORITY, &uart_hostlink_driver_api);
|