Home | History | Annotate | Line # | Download | only in scmdctl
      1 /*	$NetBSD: uart.c,v 1.3 2022/04/11 21:23:07 andvar Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2021 Brad Spencer <brad (at) 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
     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
     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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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