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