#ifdef __RCSID
__RCSID("$NetBSD: uart.c,v 1.3 2022/04/11 21:23:07 andvar Exp $");
#endif
#include <inttypes.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <err.h>
#include <fcntl.h>
#include <string.h>
#include <limits.h>
#include <termios.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <dev/spi/spi_io.h>
#include <dev/ic/scmdreg.h>
#include "scmdctl.h"
#include "responses.h"
#define EXTERN
#include "uart.h"
static int uart_subtype = -1;
static int uart_spi_slave_addr = -1;
int
uart_clear(int fd, bool debug)
{
const char jcmd[4] = "qq\r\n";
char input;
int i;
if (uart_subtype == UART_IS_PURE_UART) {
i = write(fd,jcmd,4);
if (i == 4) {
i = read(fd,&input,1);
while (input != '>') {
if (debug)
fprintf(stderr,"uart_clear: %c\n",input);
i = read(fd,&input,1);
}
} else {
return EINVAL;
}
}
return 0;
}
static int
pure_uart_send_cmd(int fd, const char *s, char *ibuf, int len)
{
int i;
i = write(fd,s,len);
if (i == len) {
i = read(fd,ibuf,len);
return 0;
} else {
return EINVAL;
}
}
static int
uart_get_response(int fd, bool debug, char *obuf, int len)
{
int n,i;
char c;
memset(obuf,0,len);
n = 0;
i = read(fd,&c,1);
if (i == -1)
return EINVAL;
while (c != '>' && c != '\r' && n < len) {
obuf[n] = c;
if (debug)
fprintf(stderr,"uart_get_response: looking for EOL or NL: %d %d -%c-\n",i,n,c);
n++;
i = read(fd,&c,1);
}
if (c != '>') {
i = read(fd,&c,1);
if (i == -1)
return EINVAL;
while (c != '>') {
if (debug)
fprintf(stderr,"uart_get_response: draining: %d -%c-\n",i,c);
i = read(fd,&c,1);
if (i == -1)
return EINVAL;
}
}
return 0;
}
static int
uart_phy_read_register(int fd, bool debug, uint8_t reg, uint8_t *buf)
{
int err;
char cmdbuf[9];
char qbuf[10];
struct timespec ts;
struct spi_ioctl_transfer spi_t;
uint8_t b;
if (SCMD_IS_HOLE(reg)) {
*buf = SCMD_HOLE_VALUE;
return 0;
}
switch (uart_subtype) {
case UART_IS_PURE_UART:
sprintf(cmdbuf, "R%02X\r\n", reg);
err = pure_uart_send_cmd(fd, cmdbuf, qbuf, 5);
if (! err) {
err = uart_get_response(fd, debug, qbuf, 5);
*buf = (uint8_t)strtol(qbuf,NULL,16);
}
break;
case UART_IS_SPI_USERLAND:
spi_t.sit_addr = uart_spi_slave_addr;
reg = reg | 0x80;
spi_t.sit_send = ®
spi_t.sit_sendlen = 1;
spi_t.sit_recv = NULL;
spi_t.sit_recvlen = 0;
err = ioctl(fd,SPI_IOCTL_TRANSFER,&spi_t);
if (debug)
fprintf(stderr,"uart_phy_read_register: IOCTL UL SPI send err: %d ; reg: %02x ; xreg: %02x\n",
err,reg,reg & 0x7f);
if (err == -1)
return errno;
ts.tv_sec = 0;
ts.tv_nsec = 50;
nanosleep(&ts,NULL);
spi_t.sit_addr = uart_spi_slave_addr;
spi_t.sit_send = NULL;
spi_t.sit_sendlen = 0;
b = SCMD_HOLE_VALUE;
spi_t.sit_recv = &b;
spi_t.sit_recvlen = 1;
err = ioctl(fd,SPI_IOCTL_TRANSFER,&spi_t);
if (debug)
fprintf(stderr,"uart_phy_read_register: IOCTL UL SPI receive 1 err: %d ; b: %02x\n",
err,b);
if (err == -1)
return errno;
ts.tv_sec = 0;
ts.tv_nsec = 50;
nanosleep(&ts,NULL);
*buf = (uint8_t)b;
spi_t.sit_addr = uart_spi_slave_addr;
spi_t.sit_send = NULL;
spi_t.sit_sendlen = 0;
b = SCMD_HOLE_VALUE;
spi_t.sit_recv = &b;
spi_t.sit_recvlen = 1;
err = ioctl(fd,SPI_IOCTL_TRANSFER,&spi_t);
if (debug)
fprintf(stderr,"uart_phy_read_register: IOCTL UL SPI receive 2 err: %d ; b: %02x\n",
err,b);
if (err == -1)
return errno;
ts.tv_sec = 0;
ts.tv_nsec = 50;
nanosleep(&ts,NULL);
break;
default:
return EINVAL;
break;
}
return err;
}
static int
uart_phy_write_register(int fd, bool debug, uint8_t reg, uint8_t buf)
{
int err;
char cmdbuf[9];
char qbuf[10];
struct timespec ts;
struct spi_ioctl_transfer spi_t;
if (SCMD_IS_HOLE(reg)) {
return 0;
}
switch (uart_subtype) {
case UART_IS_PURE_UART:
sprintf(cmdbuf, "W%02X%02X\r\n", reg, buf);
err = pure_uart_send_cmd(fd, cmdbuf, qbuf, 7);
if (! err) {
err = uart_get_response(fd, debug, qbuf, 10);
}
break;
case UART_IS_SPI_USERLAND:
spi_t.sit_addr = uart_spi_slave_addr;
reg = reg & 0x7f;
spi_t.sit_send = ®
spi_t.sit_sendlen = 1;
spi_t.sit_recv = NULL;
spi_t.sit_recvlen = 0;
err = ioctl(fd,SPI_IOCTL_TRANSFER,&spi_t);
if (debug)
fprintf(stderr,"uart_phy_write_register: IOCTL UL SPI write send 1 err: %d ; reg: %02x ; xreg: %02x\n",
err,reg,reg & 0x7f);
if (err == -1)
return errno;
spi_t.sit_addr = uart_spi_slave_addr;
spi_t.sit_send = &buf;
spi_t.sit_sendlen = 1;
spi_t.sit_recv = NULL;
spi_t.sit_recvlen = 0;
err = ioctl(fd,SPI_IOCTL_TRANSFER,&spi_t);
if (debug)
fprintf(stderr,"uart_phy_write_register: IOCTL UL SPI write send 2 err: %d ; buf: %02x\n",
err,buf);
if (err == -1)
return errno;
ts.tv_sec = 0;
ts.tv_nsec = 50;
nanosleep(&ts,NULL);
break;
default:
return EINVAL;
break;
}
return err;
}
static int
uart_local_read_register(int fd, bool debug, uint8_t reg, uint8_t reg_end, uint8_t *r)
{
uint8_t b;
int err = 0;
for(int q = reg, g = 0; q <= reg_end; q++, g++) {
err = uart_phy_read_register(fd, debug, q, &b);
if (!err)
r[g] = b;
}
return err;
}
static int
uart_set_view_port(int fd, bool debug, int a_module, uint8_t vpi2creg)
{
int err;
uint8_t vpi2caddr = (SCMD_REMOTE_ADDR_LOW + a_module) - 1;
if (debug)
fprintf(stderr, "uart_set_view_port: View port addr: %02x ; View port register: %02x\n",
vpi2caddr, vpi2creg);
err = uart_phy_write_register(fd, debug, SCMD_REG_REM_ADDR, vpi2caddr);
if (! err)
err = uart_phy_write_register(fd, debug, SCMD_REG_REM_OFFSET, vpi2creg);
return err;
}
static int
uart_remote_read_register(int fd, bool debug, int a_module, uint8_t reg, uint8_t reg_end, uint8_t *r)
{
int err;
int c;
uint8_t b;
for(int q = reg, g = 0; q <= reg_end; q++, g++) {
err = uart_set_view_port(fd, debug, a_module, q);
if (err)
break;
b = 0xff;
err = uart_phy_write_register(fd, debug, SCMD_REG_REM_READ, b);
if (err)
break;
c = 0;
do {
sleep(1);
err = uart_phy_read_register(fd, debug, SCMD_REG_REM_READ, &b);
c++;
} while ((c < 10) && (b != 0x00) && (!err));
if (err)
break;
err = uart_phy_read_register(fd, debug, SCMD_REG_REM_DATA_RD, &b);
if (err)
break;
r[g] = b;
}
return err;
}
void
uart_set_subtype(int subt, int spi_s_addr)
{
uart_subtype = subt;
uart_spi_slave_addr = spi_s_addr;
return;
}
int
uart_read_register(int fd, bool debug, int a_module, uint8_t reg, uint8_t reg_end, uint8_t *r)
{
int err;
if (reg > SCMD_LAST_REG ||
reg_end > SCMD_LAST_REG)
return EINVAL;
if (reg_end < reg)
return EINVAL;
err = uart_clear(fd, debug);
if (! err) {
if (a_module == 0) {
err = uart_local_read_register(fd, debug, reg, reg_end, r);
} else {
err = uart_remote_read_register(fd, debug, a_module, reg, reg_end, r);
}
}
return err;
}
static int
uart_remote_write_register(int fd, bool debug, int a_module, uint8_t reg, uint8_t reg_v)
{
int err;
int c;
uint8_t b;
err = uart_set_view_port(fd, debug, a_module, reg);
if (! err) {
err = uart_phy_write_register(fd, debug, SCMD_REG_REM_DATA_WR, reg_v);
if (! err) {
b = 0xff;
err = uart_phy_write_register(fd, debug, SCMD_REG_REM_WRITE, b);
if (! err) {
c = 0;
do {
sleep(1);
err = uart_phy_read_register(fd, debug, SCMD_REG_REM_WRITE, &b);
c++;
} while ((c < 10) && (b != 0x00) && (!err));
}
}
}
return err;
}
int
uart_write_register(int fd, bool debug, int a_module, uint8_t reg, uint8_t reg_v)
{
int err;
if (reg > SCMD_LAST_REG)
return EINVAL;
err = uart_clear(fd, debug);
if (! err) {
if (a_module == 0) {
err = uart_phy_write_register(fd, debug, reg, reg_v);
} else {
err = uart_remote_write_register(fd, debug, a_module, reg, reg_v);
}
}
return err;
}
int
uart_ul_spi_read_one(int fd, bool debug)
{
int err = 0;
struct timespec ts;
struct spi_ioctl_transfer spi_t;
uint8_t b;
if (uart_subtype == UART_IS_SPI_USERLAND) {
spi_t.sit_addr = uart_spi_slave_addr;
spi_t.sit_send = NULL;
spi_t.sit_sendlen = 0;
b = SCMD_HOLE_VALUE;
spi_t.sit_recv = &b;
spi_t.sit_recvlen = 1;
err = ioctl(fd,SPI_IOCTL_TRANSFER,&spi_t);
if (debug)
fprintf(stderr,"uart_ul_spi_read_one: IOCTL UL SPI receive 1 err: %d ; b: %02x\n",
err,b);
if (err == -1)
return errno;
ts.tv_sec = 0;
ts.tv_nsec = 50;
nanosleep(&ts,NULL);
}
return err;
}