Linux Audio

Check our new training course

Embedded Linux Audio

Check our new training course
with Creative Commons CC-BY-SA
lecture materials

Bootlin logo

Elixir Cross Referencer

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);