icE1usb fw/gps: Use ericsson GPS 02/03 on aux uart

Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
Change-Id: Ia90d4e537445be2e037807c681d41e2d8f2759e2
diff --git a/firmware/ice40-riscv/icE1usb/Makefile b/firmware/ice40-riscv/icE1usb/Makefile
index f385408..513329c 100644
--- a/firmware/ice40-riscv/icE1usb/Makefile
+++ b/firmware/ice40-riscv/icE1usb/Makefile
@@ -57,6 +57,7 @@
 	ice1usb_proto.h \
 	i2c.h \
 	misc.h \
+	rs422.h \
 	usb_desc_ids.h \
 	usb_dev.h \
 	usb_e1.h \
@@ -72,6 +73,7 @@
 	gpsdo.c \
 	i2c.c \
 	misc.c \
+	rs422.c \
 	usb_desc_app.c \
 	usb_dev.c \
 	usb_e1.c \
diff --git a/firmware/ice40-riscv/icE1usb/config.h b/firmware/ice40-riscv/icE1usb/config.h
index a27a53c..99ebfb1 100644
--- a/firmware/ice40-riscv/icE1usb/config.h
+++ b/firmware/ice40-riscv/icE1usb/config.h
@@ -18,6 +18,7 @@
 #define MISC_BASE	0x88000000
 #define GPS_UART_BASE	0x89000000
 #define I2C_BASE	0x8a000000
+#define AUX_UART_BASE	0x8b000000
 
 /* Alias for common code */
 #define UART_BASE DBG_UART_BASE
diff --git a/firmware/ice40-riscv/icE1usb/gps.c b/firmware/ice40-riscv/icE1usb/gps.c
index c1ad3a8..6f8480f 100644
--- a/firmware/ice40-riscv/icE1usb/gps.c
+++ b/firmware/ice40-riscv/icE1usb/gps.c
@@ -13,6 +13,7 @@
 #include "console.h"
 #include "gps.h"
 #include "misc.h"
+#include "rs422.h"
 #include "usb_gps.h"
 #include "utils.h"
 
@@ -28,7 +29,7 @@
 #define GPS_UART_CSR_TX_EMPTY	(1 << 29)
 #define GPS_UART_DATA_EMPTY	(1 << 31)
 
-static volatile struct gps_uart * const gps_uart_regs = (void*)(GPS_UART_BASE);
+static volatile struct gps_uart * const gps_uart_regs = (void*)(AUX_UART_BASE);
 
 
 static struct {
@@ -159,37 +160,11 @@
 void
 _gps_parse_nmea(const char *nmea)
 {
-	/* Very basic parsing, we just look at GSA messages and consider
-	 * that if we have a 3D fix with PDOP < 5, the timing data should
-	 * be usable
-	 */
-	if (!strncmp(nmea, "$GPGSA", 6))
+	/* Very basic parsing, we just look at $PERC,GPsts message for
+	 * state 1 (survey mode) and 2 (position-hold) */
+	if (!strncmp(nmea, "$PERC,GPsts,", 12))
 	{
-		/* Check for Autonomous 3D fix (fixed field positions) */
-		if ((nmea[7] != 'A') || (nmea[9] != '3')) {
-			g_gps.fix.valid = false;
-			return;
-		}
-
-		/* Find PDOP */
-		const char *p = nmea;
-		for (int i=0; (i < 15) && (*p != '*'); i += (*(p++)==',') );
-
-		/* Is it low enough ? */
-		g_gps.fix.valid = (p[1] < '5');
-	}
-
-	/* Parse TXT ANTENNA Status */
-	if (!strncmp(nmea, "$GPTXT", 6) && nmea[13] == '0' && nmea[14] == '1')
-	{
-		if      (!strncmp(&nmea[16], "ANTENNA OK",    10))
-			g_gps.antenna = ANT_OK;
-		else if (!strncmp(&nmea[16], "ANTENNA OPEN",  12))
-			g_gps.antenna = ANT_OPEN;
-		else if (!strncmp(&nmea[16], "ANTENNA SHORT", 13))
-			g_gps.antenna = ANT_SHORT;
-		else
-			g_gps.antenna = ANT_UNKNOWN;
+		g_gps.fix.valid = (nmea[12] == '1') || (nmea[12] == '2');
 	}
 }
 
@@ -226,65 +201,31 @@
 void
 gps_init(void)
 {
-	int i;
+	uint32_t start_time = time_now_read();
+	bool init_ok;
 
 	/* State init */
 	memset(&g_gps, 0x00, sizeof(g_gps));
 
-	/* Configure reset gpio */
+	/* Hold onboard GPS in reset */
 	gpio_out(3, false);
 	gpio_dir(3, true);
 
-	/* Attempt reset sequence at 9600 baud and then 115200 baud */
-	for (i=0; i<2; i++)
-	{
-		uint32_t start_time = time_now_read();
-		bool init_ok;
+	/* Init RS422 board */
+	rs422_init();
 
-		/* Assert reset */
-		gpio_out(3, false);
+	/* Configure uart and empty buffer */
+	gps_uart_regs->csr = GPS_UART_CSR_DIV(9600);
+	_gps_empty(false);
 
-		/* Configure uart and empty buffer */
-		gps_uart_regs->csr = i ? GPS_UART_CSR_DIV(115200) : GPS_UART_CSR_DIV(9600);
-		_gps_empty(false);
-
-		/* Wait 100 ms */
-		delay(100);
-
-		/* Release reset line */
-		gpio_out(3, true);
-
-		/* Wait for first line of output as sign it's ready, timeout after 1s */
-		while (!time_elapsed(start_time, SYS_CLK_FREQ))
-			if ((init_ok = (_gps_query() != NULL)))
-				break;
-
-		if (init_ok) {
-			printf("[+] GPS ok at %d baud\n", i ? 115200 : 9600);
+	/* Wait for first line of output as sign it's ready, timeout after 10 s */
+	while (!time_elapsed(start_time, 10 * SYS_CLK_FREQ))
+		if ((init_ok = (_gps_query() != NULL)))
 			break;
-		}
-	}
 
-	/* Failed ? */
-	if (i == 2) {
+	if (init_ok) {
+		printf("[+] GPS ok\n");
+	} else {
 		printf("[!] GPS init failed\n");
-		return;
 	}
-
-	/* If success was at 9600 baud, need to speed up */
-	if (i == 0) {
-		/* Configure GPS to use serial at 115200 baud */
-		_gps_send("PCAS01,5");
-
-		/* Add dummy byte which will be mangled during baudrate switch ... */
-		gps_uart_regs->data = 0x00;
-		while (!(gps_uart_regs->csr & GPS_UART_CSR_TX_EMPTY));
-
-		/* Set uart to 115200 and empty uart buffer, line aligned */
-		gps_uart_regs->csr = GPS_UART_CSR_DIV(115200) ;
-		_gps_empty(true);
-	}
-
-	/* Configure GPS to be GPS-only (no GLONASS/BEIDOU) */
-	_gps_send("PCAS04,1");
 }
diff --git a/firmware/ice40-riscv/icE1usb/rs422.c b/firmware/ice40-riscv/icE1usb/rs422.c
new file mode 100644
index 0000000..4bdcb6c
--- /dev/null
+++ b/firmware/ice40-riscv/icE1usb/rs422.c
@@ -0,0 +1,96 @@
+/*
+ * rs422.c
+ *
+ * Copyright (C) 2022  Sylvain Munaut <tnt@246tNt.com>
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "console.h"
+#include "i2c.h"
+#include "misc.h"
+
+#include "config.h"
+
+
+/* Local side buffer control */
+#define BUF_TCA9534_ADDR	0x42
+#define BUF_TXD_RXEN_n		(1 << 0)
+#define BUF_TXD_TXEN_n		(1 << 1)
+#define BUF_PPS_RXEN_n		(1 << 4)
+#define BUF_PPS_TXEN_n		(1 << 5)
+#define BUF_RXD_RXEN_n		(1 << 6)
+#define BUF_RXD_TXEN_n		(1 << 7)
+
+/* Isolated side line driver control */
+#define LDRV_TCA9534_ADDR	0x40
+#define LDRV_TXD_RE_n		(1 << 0)
+#define LDRV_TXD_DE		(1 << 1)
+#define LDRV_RXD_RE_n		(1 << 2)
+#define LDRV_RXD_DE		(1 << 3)
+#define LDRV_PPS_DE		(1 << 4)
+#define LDRV_PPS_RE_n		(1 << 5)
+
+
+static void
+tca9534_set_out(uint8_t dev, uint8_t data)
+{
+	/* Check device */
+	if (!i2c_probe(dev)) {
+		printf("[1] Unable to configure TCA9534 at address %02x\n", dev);
+		return;
+	}
+
+	/* Output values */
+	i2c_write_reg(dev, 1, data);
+
+	/* No inversion */
+	i2c_write_reg(dev, 2, 0x00);
+
+	/* All pins as output to avoid floating pins */
+	i2c_write_reg(dev, 3, 0x00);
+}
+
+
+void
+rs422_init(void)
+{
+	/* Reset GPS receiver to free I2C bus */
+	gpio_out(3, false);
+	gpio_dir(3, true);
+
+	/* Configure:
+	 *  - TXD pair: Receive
+	 *  - RXD pair: Transmit
+	 *  - PPS pair: Receive
+	 */
+	tca9534_set_out(BUF_TCA9534_ADDR,
+		BUF_TXD_TXEN_n |
+		BUF_RXD_RXEN_n |
+		BUF_PPS_TXEN_n |
+		0
+	);
+
+	tca9534_set_out(LDRV_TCA9534_ADDR,
+		LDRV_RXD_RE_n |
+#if 0
+		/* We don't actually TX anything to the module and
+		 * this burns power ... */
+		LDRV_RXD_DE |
+#endif
+		0
+	);
+
+	/* Configure GPIO alt functions */
+		/* Aux UART RX */
+	gpio_sfn(0, true);
+
+		/* Aux UART TX */
+	gpio_sfn(1, true);
+
+		/* PPS input */
+	gpio_dir(2, false);
+	gpio_sfn(2, true);
+}
diff --git a/firmware/ice40-riscv/icE1usb/rs422.h b/firmware/ice40-riscv/icE1usb/rs422.h
new file mode 100644
index 0000000..333f2e1
--- /dev/null
+++ b/firmware/ice40-riscv/icE1usb/rs422.h
@@ -0,0 +1,13 @@
+/*
+ * rs422.h
+ *
+ * Copyright (C) 2022  Sylvain Munaut <tnt@246tNt.com>
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <stdbool.h>
+#include <stdint.h>
+
+void rs422_init(void);