xref: /netbsd-src/usr.bin/scmdctl/uart.c (revision d1ee79bf8cff65cf48cf8f970ed2599ce8e33e16)
1 /*	$NetBSD: uart.c,v 1.3 2022/04/11 21:23:07 andvar Exp $	*/
2 
3 /*
4  * Copyright (c) 2021 Brad Spencer <brad@anduin.eldar.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #ifdef __RCSID
20 __RCSID("$NetBSD: uart.c,v 1.3 2022/04/11 21:23:07 andvar Exp $");
21 #endif
22 
23 /* Functions that know how to talk to a SCMD using the uart tty
24  * mode or via SPI userland, which ends up being mostly the same.
25  *
26  * Some of this is the same stuff that the kernel scmd(4) driver
27  * ends up doing.
28  */
29 
30 #include <inttypes.h>
31 #include <stdbool.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <unistd.h>
35 #include <err.h>
36 #include <fcntl.h>
37 #include <string.h>
38 #include <limits.h>
39 #include <termios.h>
40 #include <errno.h>
41 #include <sys/ioctl.h>
42 #include <sys/time.h>
43 #include <dev/spi/spi_io.h>
44 
45 #include <dev/ic/scmdreg.h>
46 
47 #include "scmdctl.h"
48 #include "responses.h"
49 
50 #define EXTERN
51 #include "uart.h"
52 
53 
54 static int uart_subtype = -1;
55 static int uart_spi_slave_addr = -1;
56 
57 /* The uart tty mode of the SCMD device is useful for human or
58  * machine use.  However you can't really know what state it is in
59  * so send some junk and look for '>' character indicating a new
60  * command can be entered.  Usually this won't be needed, but
61  * you can never know when it is.
62  */
63 int
uart_clear(int fd,bool debug)64 uart_clear(int fd, bool debug)
65 {
66 	const char jcmd[4] = "qq\r\n";
67 	char input;
68 	int i;
69 
70 	if (uart_subtype == UART_IS_PURE_UART) {
71 		i = write(fd,jcmd,4);
72 		if (i == 4) {
73 			i = read(fd,&input,1);
74 			while (input != '>') {
75 				if (debug)
76 					fprintf(stderr,"uart_clear: %c\n",input);
77 				i = read(fd,&input,1);
78 			}
79 		} else {
80 			return EINVAL;
81 		}
82 	}
83 
84 	return 0;
85 }
86 
87 /* The SCMD device will echo back the characters in uart tty mode.
88  * Eat them here.
89  */
90 static int
pure_uart_send_cmd(int fd,const char * s,char * ibuf,int len)91 pure_uart_send_cmd(int fd, const char *s, char *ibuf, int len)
92 {
93 	int i;
94 
95 	i = write(fd,s,len);
96 	if (i == len) {
97 		i = read(fd,ibuf,len);
98 		return 0;
99 	} else {
100 		return EINVAL;
101 	}
102 }
103 
104 /* In pure uart tty mode, the command is sent as text and we are
105  * looking for '>'.  There is not a lot that can go wrong, but
106  * noise on the line is one of them, and that really is not detected here.
107  * This is probably the least reliable method of speaking to a SCMD
108  * device.
109  */
110 static int
uart_get_response(int fd,bool debug,char * obuf,int len)111 uart_get_response(int fd, bool debug, char *obuf, int len)
112 {
113 	int n,i;
114 	char c;
115 
116 	memset(obuf,0,len);
117 	n = 0;
118 	i = read(fd,&c,1);
119 	if (i == -1)
120 		return EINVAL;
121 	while (c != '>' && c != '\r' && n < len) {
122 		obuf[n] = c;
123 		if (debug)
124 			fprintf(stderr,"uart_get_response: looking for EOL or NL: %d %d -%c-\n",i,n,c);
125 		n++;
126 		i = read(fd,&c,1);
127 	}
128 
129 	if (c != '>') {
130 		i = read(fd,&c,1);
131 		if (i == -1)
132 			return EINVAL;
133 		while (c != '>') {
134 			if (debug)
135 				fprintf(stderr,"uart_get_response: draining: %d -%c-\n",i,c);
136 			i = read(fd,&c,1);
137 			if (i == -1)
138 				return EINVAL;
139 		}
140 	}
141 
142 	return 0;
143 }
144 
145 /* This handles the two uart cases.  Either pure tty uart or SPI
146  * userland.  The first uses text commands and the second is binary,
147  * but has the strange read situation that scmd(4) has.
148  */
149 static int
uart_phy_read_register(int fd,bool debug,uint8_t reg,uint8_t * buf)150 uart_phy_read_register(int fd, bool debug, uint8_t reg, uint8_t *buf)
151 {
152 	int err;
153 	char cmdbuf[9];
154 	char qbuf[10];
155 	struct timespec ts;
156 	struct spi_ioctl_transfer spi_t;
157 	uint8_t b;
158 
159 	if (SCMD_IS_HOLE(reg)) {
160 		*buf = SCMD_HOLE_VALUE;
161 		return 0;
162 	}
163 
164 	switch (uart_subtype) {
165 	case UART_IS_PURE_UART:
166 		sprintf(cmdbuf, "R%02X\r\n", reg);
167 		err = pure_uart_send_cmd(fd, cmdbuf, qbuf, 5);
168 		if (! err) {
169 			err = uart_get_response(fd, debug, qbuf, 5);
170 			*buf = (uint8_t)strtol(qbuf,NULL,16);
171 		}
172 		break;
173 	case UART_IS_SPI_USERLAND:
174 		spi_t.sit_addr = uart_spi_slave_addr;
175 		reg = reg | 0x80;
176 		spi_t.sit_send = &reg;
177 		spi_t.sit_sendlen = 1;
178 		spi_t.sit_recv = NULL;
179 		spi_t.sit_recvlen = 0;
180 
181 		err = ioctl(fd,SPI_IOCTL_TRANSFER,&spi_t);
182 		if (debug)
183 			fprintf(stderr,"uart_phy_read_register: IOCTL UL SPI send err: %d ; reg: %02x ; xreg: %02x\n",
184 			    err,reg,reg & 0x7f);
185 
186 		if (err == -1)
187 			return errno;
188 
189 		ts.tv_sec = 0;
190 		ts.tv_nsec = 50;
191 		nanosleep(&ts,NULL);
192 
193 		spi_t.sit_addr = uart_spi_slave_addr;
194 		spi_t.sit_send = NULL;
195 		spi_t.sit_sendlen = 0;
196 		b = SCMD_HOLE_VALUE;
197 		spi_t.sit_recv = &b;
198 		spi_t.sit_recvlen = 1;
199 
200 		err = ioctl(fd,SPI_IOCTL_TRANSFER,&spi_t);
201 		if (debug)
202 			fprintf(stderr,"uart_phy_read_register: IOCTL UL SPI receive 1 err: %d ; b: %02x\n",
203 			    err,b);
204 
205 		if (err == -1)
206 			return errno;
207 
208 		ts.tv_sec = 0;
209 		ts.tv_nsec = 50;
210 		nanosleep(&ts,NULL);
211 
212 		*buf = (uint8_t)b;
213 
214 		/* Bogus read that is needed */
215 		spi_t.sit_addr = uart_spi_slave_addr;
216 		spi_t.sit_send = NULL;
217 		spi_t.sit_sendlen = 0;
218 		b = SCMD_HOLE_VALUE;
219 		spi_t.sit_recv = &b;
220 		spi_t.sit_recvlen = 1;
221 
222 		err = ioctl(fd,SPI_IOCTL_TRANSFER,&spi_t);
223 		if (debug)
224 			fprintf(stderr,"uart_phy_read_register: IOCTL UL SPI receive 2 err: %d ; b: %02x\n",
225 			    err,b);
226 
227 		if (err == -1)
228 			return errno;
229 
230 		ts.tv_sec = 0;
231 		ts.tv_nsec = 50;
232 		nanosleep(&ts,NULL);
233 
234 		break;
235 	default:
236 		return EINVAL;
237 		break;
238 	}
239 
240 	return err;
241 }
242 
243 /* Like read, this handles the two uart cases. */
244 static int
uart_phy_write_register(int fd,bool debug,uint8_t reg,uint8_t buf)245 uart_phy_write_register(int fd, bool debug, uint8_t reg, uint8_t buf)
246 {
247 	int err;
248 	char cmdbuf[9];
249 	char qbuf[10];
250 	struct timespec ts;
251 	struct spi_ioctl_transfer spi_t;
252 
253 	if (SCMD_IS_HOLE(reg)) {
254 		return 0;
255 	}
256 
257 	switch (uart_subtype) {
258 	case UART_IS_PURE_UART:
259 		sprintf(cmdbuf, "W%02X%02X\r\n", reg, buf);
260 		err = pure_uart_send_cmd(fd, cmdbuf, qbuf, 7);
261 		if (! err) {
262 			err = uart_get_response(fd, debug, qbuf, 10);
263 		}
264 		break;
265 	case UART_IS_SPI_USERLAND:
266 		spi_t.sit_addr = uart_spi_slave_addr;
267 		reg = reg & 0x7f;
268 		spi_t.sit_send = &reg;
269 		spi_t.sit_sendlen = 1;
270 		spi_t.sit_recv = NULL;
271 		spi_t.sit_recvlen = 0;
272 
273 		err = ioctl(fd,SPI_IOCTL_TRANSFER,&spi_t);
274 		if (debug)
275 			fprintf(stderr,"uart_phy_write_register: IOCTL UL SPI write send 1 err: %d ; reg: %02x ; xreg: %02x\n",
276 			    err,reg,reg & 0x7f);
277 
278 		if (err == -1)
279 			return errno;
280 
281 		spi_t.sit_addr = uart_spi_slave_addr;
282 		spi_t.sit_send = &buf;
283 		spi_t.sit_sendlen = 1;
284 		spi_t.sit_recv = NULL;
285 		spi_t.sit_recvlen = 0;
286 
287 		err = ioctl(fd,SPI_IOCTL_TRANSFER,&spi_t);
288 		if (debug)
289 			fprintf(stderr,"uart_phy_write_register: IOCTL UL SPI write send 2 err: %d ; buf: %02x\n",
290 			    err,buf);
291 
292 		if (err == -1)
293 			return errno;
294 
295 		ts.tv_sec = 0;
296 		ts.tv_nsec = 50;
297 		nanosleep(&ts,NULL);
298 
299 		break;
300 	default:
301 		return EINVAL;
302 		break;
303 	}
304 
305 	return err;
306 }
307 
308 static int
uart_local_read_register(int fd,bool debug,uint8_t reg,uint8_t reg_end,uint8_t * r)309 uart_local_read_register(int fd, bool debug, uint8_t reg, uint8_t reg_end, uint8_t *r)
310 {
311 	uint8_t b;
312 	int err = 0;
313 
314 	for(int q = reg, g = 0; q <= reg_end; q++, g++) {
315 		err = uart_phy_read_register(fd, debug, q, &b);
316 		if (!err)
317 			r[g] = b;
318 	}
319 
320 	return err;
321 }
322 
323 /* When speaking to a SCMD device in any uart mode the view port for
324  * chained slave modules has to be handled in userland.  This is similar
325  * to what the scmd(4) kernel driver ends up doing, but is much slower.
326  */
327 static int
uart_set_view_port(int fd,bool debug,int a_module,uint8_t vpi2creg)328 uart_set_view_port(int fd, bool debug, int a_module, uint8_t vpi2creg)
329 {
330 	int err;
331 	uint8_t vpi2caddr = (SCMD_REMOTE_ADDR_LOW + a_module) - 1;
332 
333 	if (debug)
334 		fprintf(stderr, "uart_set_view_port: View port addr: %02x ; View port register: %02x\n",
335 		    vpi2caddr, vpi2creg);
336 
337 	err = uart_phy_write_register(fd, debug, SCMD_REG_REM_ADDR, vpi2caddr);
338 	if (! err)
339 		err = uart_phy_write_register(fd, debug, SCMD_REG_REM_OFFSET, vpi2creg);
340 
341 	return err;
342 }
343 
344 static int
uart_remote_read_register(int fd,bool debug,int a_module,uint8_t reg,uint8_t reg_end,uint8_t * r)345 uart_remote_read_register(int fd, bool debug, int a_module, uint8_t reg, uint8_t reg_end, uint8_t *r)
346 {
347 	int err;
348 	int c;
349 	uint8_t b;
350 
351 	for(int q = reg, g = 0; q <= reg_end; q++, g++) {
352 		err = uart_set_view_port(fd, debug, a_module, q);
353 		if (err)
354 			break;
355 
356 		b = 0xff; /* you can write anything here.. it doesn't matter */
357 		err = uart_phy_write_register(fd, debug, SCMD_REG_REM_READ, b);
358 		if (err)
359 			break;
360 
361 		/* So ...  there is no way to really know that the data is ready and
362 		 * there is no way to know if there was an error in the master module reading
363 		 * the data from the slave module.  The data sheet says wait 5ms.. so we will
364 		 * wait a bit and see if the register cleared, but don't wait forever...  I
365 		 * can't see how it would not be possible to read junk at times.
366 		 */
367 		c = 0;
368 		do {
369 			sleep(1);
370 			err = uart_phy_read_register(fd, debug, SCMD_REG_REM_READ, &b);
371 			c++;
372 		} while ((c < 10) && (b != 0x00) && (!err));
373 
374 		/* We can only hope that whatever was read from the slave module is there */
375 		if (err)
376 			break;
377 		err = uart_phy_read_register(fd, debug, SCMD_REG_REM_DATA_RD, &b);
378 		if (err)
379 			break;
380 		r[g] = b;
381 	}
382 
383 	return err;
384 }
385 
386 void
uart_set_subtype(int subt,int spi_s_addr)387 uart_set_subtype(int subt, int spi_s_addr)
388 {
389 	uart_subtype = subt;
390 	uart_spi_slave_addr = spi_s_addr;
391 
392 	return;
393 }
394 
395 /* Unlike scmd(4) local reads and remote module reads are done very
396  * differently.
397  */
398 int
uart_read_register(int fd,bool debug,int a_module,uint8_t reg,uint8_t reg_end,uint8_t * r)399 uart_read_register(int fd, bool debug, int a_module, uint8_t reg, uint8_t reg_end, uint8_t *r)
400 {
401 	int err;
402 
403 	if (reg > SCMD_LAST_REG ||
404 	    reg_end > SCMD_LAST_REG)
405 		return EINVAL;
406 
407 	if (reg_end < reg)
408 		return EINVAL;
409 
410 	err = uart_clear(fd, debug);
411 	if (! err) {
412 		if (a_module == 0) {
413 			err = uart_local_read_register(fd, debug, reg, reg_end, r);
414 		} else {
415 			err = uart_remote_read_register(fd, debug, a_module, reg, reg_end, r);
416 		}
417 	}
418 
419 	return err;
420 }
421 
422 static int
uart_remote_write_register(int fd,bool debug,int a_module,uint8_t reg,uint8_t reg_v)423 uart_remote_write_register(int fd, bool debug, int a_module, uint8_t reg, uint8_t reg_v)
424 {
425 	int err;
426 	int c;
427 	uint8_t b;
428 
429 	err = uart_set_view_port(fd, debug, a_module, reg);
430 	if (! err) {
431 		/* We just sort of send this write off and wait to see if the register
432 		 * clears.  There really isn't any indication that the data made it to the
433 		 * slave modules.
434 		 */
435 		err = uart_phy_write_register(fd, debug, SCMD_REG_REM_DATA_WR, reg_v);
436 		if (! err) {
437 			b = 0xff; /* you can write anything here.. it doesn't matter */
438 			err = uart_phy_write_register(fd, debug, SCMD_REG_REM_WRITE, b);
439 			if (! err) {
440 				c = 0;
441 				do {
442 					sleep(1);
443 					err = uart_phy_read_register(fd, debug, SCMD_REG_REM_WRITE, &b);
444 					c++;
445 				} while ((c < 10) && (b != 0x00) && (!err));
446 			}
447 		}
448 	}
449 
450 	return err;
451 }
452 
453 /* Like reads, writes are done very differently between scmd(4) and
454  * the uart modes.
455  */
456 int
uart_write_register(int fd,bool debug,int a_module,uint8_t reg,uint8_t reg_v)457 uart_write_register(int fd, bool debug, int a_module, uint8_t reg, uint8_t reg_v)
458 {
459 	int err;
460 
461 	if (reg > SCMD_LAST_REG)
462 		return EINVAL;
463 
464 	err = uart_clear(fd, debug);
465 	if (! err) {
466 		if (a_module == 0) {
467 			err = uart_phy_write_register(fd, debug, reg, reg_v);
468 		} else {
469 			err = uart_remote_write_register(fd, debug, a_module, reg, reg_v);
470 		}
471 	}
472 
473 	return err;
474 }
475 
476 /* This is a special ability to do a single SPI receive that has the
477  * hope of resyncing the device should it get out of sync in SPI mode.
478  * This will work for either SPI userland mode or scmd(4) when attached
479  * to the SPI bus as you can still write to /dev/spiN then too.
480  */
481 int
uart_ul_spi_read_one(int fd,bool debug)482 uart_ul_spi_read_one(int fd, bool debug)
483 {
484 	int err = 0;
485 	struct timespec ts;
486 	struct spi_ioctl_transfer spi_t;
487 	uint8_t b;
488 
489 	if (uart_subtype == UART_IS_SPI_USERLAND) {
490 		spi_t.sit_addr = uart_spi_slave_addr;
491 		spi_t.sit_send = NULL;
492 		spi_t.sit_sendlen = 0;
493 		b = SCMD_HOLE_VALUE;
494 		spi_t.sit_recv = &b;
495 		spi_t.sit_recvlen = 1;
496 
497 		err = ioctl(fd,SPI_IOCTL_TRANSFER,&spi_t);
498 		if (debug)
499 			fprintf(stderr,"uart_ul_spi_read_one: IOCTL UL SPI receive 1 err: %d ; b: %02x\n",
500 			    err,b);
501 
502 		if (err == -1)
503 			return errno;
504 
505 		ts.tv_sec = 0;
506 		ts.tv_nsec = 50;
507 		nanosleep(&ts,NULL);
508 	}
509 
510 	return err;
511 }
512