icE1usb fw: Add Notify PPS on Carrier Detect option

This is disabled by default because turns out the kernel doesn't
actually support PPS on CD for USB-CDC devices :(

And this also increase the interrupt traffic ...

Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
Change-Id: Ie5d163434323a23912228003add9870fafefedf9
diff --git a/firmware/ice40-riscv/icE1usb/usb_desc_app.c b/firmware/ice40-riscv/icE1usb/usb_desc_app.c
index 25472e0..7dfc304 100644
--- a/firmware/ice40-riscv/icE1usb/usb_desc_app.c
+++ b/firmware/ice40-riscv/icE1usb/usb_desc_app.c
@@ -250,7 +250,11 @@
 			.bmAttributes		= 0x03,
 				/* Longest notif is SERIAL_STATE with 2 data bytes */
 			.wMaxPacketSize		= sizeof(struct usb_ctrl_req) + 2,
+#ifdef GPS_PPS_ON_CD
+			.bInterval		= 1,
+#else
 			.bInterval		= 0x40,
+#endif
 		},
 		.intf_data = {
 			.bLength		= sizeof(struct usb_intf_desc),
diff --git a/firmware/ice40-riscv/icE1usb/usb_gps.c b/firmware/ice40-riscv/icE1usb/usb_gps.c
index 1c10033..7ca89fd 100644
--- a/firmware/ice40-riscv/icE1usb/usb_gps.c
+++ b/firmware/ice40-riscv/icE1usb/usb_gps.c
@@ -17,6 +17,7 @@
 #include <no2usb/usb_cdc_proto.h>
 
 #include "console.h"
+#include "misc.h"
 #include "usb_desc_ids.h"
 
 
@@ -34,9 +35,23 @@
 		unsigned int rd;
 		char data[BUF_SIZE] __attribute__((aligned(4)));
 	} buf;
+
+#ifdef GPS_PPS_ON_CD
+	/* PPS tracking */
+	struct {
+		uint32_t last;
+		bool set;
+	} pps;
+#endif
 } g_usb_gps;
 
 
+struct usb_cdc_notif_serial_state {
+	struct usb_ctrl_req hdr;
+	uint16_t bits;
+};
+
+
 static void
 _usb_gps_set_active(bool active)
 {
@@ -120,6 +135,53 @@
 		else
 			ep_regs->bd[0].csr = 0;
 	}
+
+#ifdef GPS_PPS_ON_CD
+	/* IN EP CTL: Send PPS pulse */
+	ep_regs = &usb_ep_regs[USB_EP_GPS_CDC_CTL & 0x1f].in;
+
+	if ((ep_regs->bd[0].csr & USB_BD_STATE_MSK) != USB_BD_STATE_RDY_DATA)
+	{
+		/* Default request */
+		struct usb_cdc_notif_serial_state notif = {
+			.hdr = {
+				.bmRequestType = USB_REQ_READ | USB_REQ_TYPE_CLASS | USB_REQ_RCPT_INTF,
+				.bRequest      = USB_NOTIF_CDC_SERIAL_STATE,
+				.wValue        = 0,
+				.wIndex        = USB_INTF_GPS_CDC_CTL,
+				.wLength       = 2
+			},
+			.bits = 0x00
+		};
+
+		/* Check if PPS occurred */
+		uint32_t pps_now = time_pps_read();
+
+		if (pps_now != g_usb_gps.pps.last)
+		{
+			/* Update last */
+			g_usb_gps.pps.last = pps_now;
+
+			/* Queue CD Set */
+			notif.bits = 1;
+			usb_data_write(ep_regs->bd[0].ptr, &notif, 12);
+			ep_regs->bd[0].csr = USB_BD_STATE_RDY_DATA | USB_BD_LEN(10);
+
+			/* Need to clear in the future */
+			g_usb_gps.pps.set = true;
+		}
+		else if (g_usb_gps.pps.set)
+		{
+			/* Queue CD Clear */
+			notif.bits = 0;
+			usb_data_write(ep_regs->bd[0].ptr, &notif, 12);
+			ep_regs->bd[0].csr = USB_BD_STATE_RDY_DATA | USB_BD_LEN(10);
+
+			/* Cleared */
+			g_usb_gps.pps.set = false;
+		}
+	}
+#endif
 }