| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679 | #include "common.h"#include "config.h"#include "fpga.h"#include "esplink.h"#include "xmalloc.h"#include <driver/gpio.h>#include <driver/spi_common.h>#include <driver/spi_master.h>#define PIN_FPGA_INT	 9#define PIN_FPGA_CS	10#define PIN_FPGA_IO0	11#define PIN_FPGA_CLK	12#define PIN_FPGA_IO1	13#define FPGA_SPI_HOST	FSPI	/* SPI2 */#define FPGA_PRIORITY	10#define FPGA_SVC_STACK	4096#define RTC_TIMESYNC_PERIOD (511*configTICK_RATE_HZ)static spi_bus_config_t spi_bus_config = {    .data0_io_num    = PIN_FPGA_IO0,    .data1_io_num    = PIN_FPGA_IO1,    .sclk_io_num     = PIN_FPGA_CLK,    .data2_io_num    = -1,    .data3_io_num    = -1,    .data4_io_num    = -1,    .data5_io_num    = -1,    .data6_io_num    = -1,    .data7_io_num    = -1,    .max_transfer_sz = 4096,    .flags           = SPICOMMON_BUSFLAG_MASTER | SPICOMMON_BUSFLAG_DUAL};#define FPGA_IOV_MAX	4static void ARDUINO_ISR_ATTR spi_callback(spi_transaction_t *);static const spi_device_interface_config_t spi_device_interface_config = {    .command_bits     =  8,    .address_bits     = 32,    .dummy_bits       =  0,    .mode             =  0,    .cs_ena_pretrans  =  0,    .cs_ena_posttrans =  0,    .clock_speed_hz   = SPI_MASTER_FREQ_40M,    .spics_io_num     = PIN_FPGA_CS,    .flags            = SPI_DEVICE_HALFDUPLEX,    .queue_size       = FPGA_IOV_MAX,    .post_cb          = spi_callback};static spi_device_handle_t spi_handle;static TaskHandle_t fpga_task;static TimerHandle_t fpga_timesync_timer;static SemaphoreHandle_t spi_mutex;static EventGroupHandle_t spi_done_evgroup;static volatile bool spi_abort_all;static struct esplink_head head;#define NOTIFY_INDEX	0#define NOTIFY_FPGA	(1 << 0)#define NOTIFY_ENABLE	(1 << 1)#define NOTIFY_DISABLE	(1 << 2)#define NOTIFY_TIMESYNC	(1 << 3)#if 0#define NOTIFY_SPI	(1 << 3)#define NOTIFY_RINGBUF	(1 << 4)#endifstatic uint32_t notify_poll_for(uint32_t flags){    return ulTaskNotifyValueClearIndexed(NULL, NOTIFY_INDEX, flags);}/* This supports multiple flags set */static uint32_t notify_wait_for(uint32_t flags){    uint32_t notify_value;    /* Already received? Might already have been waited for... */    notify_value = notify_poll_for(flags);    while (!(notify_value & flags)) {	xTaskNotifyWaitIndexed(NOTIFY_INDEX, 0, flags,			       ¬ify_value, portMAX_DELAY);    }    return notify_value;}static void ARDUINO_ISR_ATTR fpga_notify_from_isr(uint32_t flags){    BaseType_t wakeup = pdFALSE;    if (xTaskNotifyIndexedFromISR(fpga_task, NOTIFY_INDEX, flags, eSetBits,				  &wakeup) != pdFAIL)	portYIELD_FROM_ISR(wakeup);}static void fpga_notify_from_task(uint32_t flags){    xTaskNotifyIndexed(fpga_task, NOTIFY_INDEX, flags, eSetBits);}static void ARDUINO_ISR_ATTR fpga_interrupt(void){    fpga_notify_from_isr(NOTIFY_FPGA);}void fpga_timesync_trigger(void){    fpga_notify_from_task(NOTIFY_TIMESYNC);}static void ARDUINO_ISR_ATTR spi_callback(spi_transaction_t *t){    size_t flags = (size_t)t->user;    if (!flags)	return;    BaseType_t wakeup = pdFALSE;    if (xEventGroupSetBitsFromISR(spi_done_evgroup, (size_t)t->user,				  &wakeup) != pdFAIL)	portYIELD_FROM_ISR(wakeup);}static void fpga_service_task(void *);static EventGroupHandle_t fpga_service_evgroup;void fpga_service_enable(bool on){    uint32_t flag = on ? NOTIFY_ENABLE : NOTIFY_DISABLE;    fpga_notify_from_task(flag);    xEventGroupWaitBits(fpga_service_evgroup, flag, 0, pdTRUE, portMAX_DELAY);}esp_err_t fpga_service_init(void){    pinMode(PIN_FPGA_INT, INPUT);    setvar_bool(status_max80_fpga, false);    fpga_service_evgroup = null_check(xEventGroupCreate());    spi_mutex = null_check(xSemaphoreCreateRecursiveMutex());    spi_done_evgroup = null_check(xEventGroupCreate());    /* The ordering here attempts to avoid race conditions... */    if (xTaskCreate(fpga_service_task, "fpga_svc", FPGA_SVC_STACK, NULL,		    FPGA_PRIORITY, &fpga_task) != pdPASS)	return ESP_FAIL;    fpga_timesync_timer =      null_check(xTimerCreate("rtc_sync", RTC_TIMESYNC_PERIOD, pdTRUE, NULL,			      (TimerCallbackFunction_t)fpga_timesync_trigger));    esplink_init();    xEventGroupSetBits(fpga_service_evgroup, NOTIFY_DISABLE);    return ESP_OK;}static bool fpga_link_enable(void){    esp_err_t err;    if (spi_handle)	return true;		/* Already started */    xEventGroupClearBits(fpga_service_evgroup, NOTIFY_DISABLE);    err = spi_bus_initialize(FPGA_SPI_HOST, &spi_bus_config, SPI_DMA_CH_AUTO);    if (err)	goto init_fail;    err = spi_bus_add_device(FPGA_SPI_HOST, &spi_device_interface_config,			     &spi_handle);    if (err)	goto free_bus_fail;    /* Only device on this bus, so acquire it permanently */    err = spi_device_acquire_bus(spi_handle, portMAX_DELAY);    if (err)	goto release_bus_fail;    xEventGroupClearBits(spi_done_evgroup, EVENT_ALL_BITS);    pinMode(PIN_FPGA_INT, INPUT);    attachInterrupt(PIN_FPGA_INT, fpga_interrupt, FALLING);    xEventGroupSetBits(fpga_service_evgroup, NOTIFY_ENABLE);    xSemaphoreGiveRecursive(spi_mutex);    fpga_notify_from_task(NOTIFY_FPGA); /* In case FPGA_INT was already low */    goto done;release_bus_fail:    spi_bus_remove_device(spi_handle);    spi_handle = NULL;free_bus_fail:    spi_bus_free(FPGA_SPI_HOST);init_fail:    xEventGroupSetBits(fpga_service_evgroup, NOTIFY_DISABLE);done:    return !err;}static void fpga_link_disable(void){    if (!spi_handle)	return;			/* Already stopped */    xEventGroupClearBits(fpga_service_evgroup, NOTIFY_ENABLE);    xSemaphoreTakeRecursive(spi_mutex, portMAX_DELAY);    detachInterrupt(PIN_FPGA_INT);    spi_device_release_bus(spi_handle);    spi_bus_remove_device(spi_handle);    spi_bus_free(FPGA_SPI_HOST);    spi_handle = NULL;    xEventGroupSetBits(fpga_service_evgroup, NOTIFY_DISABLE);}static void fpga_poll_set_time(void);esp_err_t fpga_send_config(void){    char *buf = NULL;    size_t bufsize = head.cfg.buflen;    esp_err_t err = ESP_ERR_NO_MEM;    buf = xmalloc_dma(bufsize);    if (!buf)	goto fail;    if (sysvar_marshall(sysvar_null, sysvar_count, buf, &bufsize,			(uintptr_t)head.cfg.buf) != sysvar_count)	goto fail;    bufsize = (bufsize + 3) & ~3; /* Round to dword */    err = fpga_io_write(FPGA_CMD_IRQ(EL_DIRQ_CONFIG), head.cfg.buf,			buf, bufsize);fail:    if (buf)	free(buf);    printf("[FPGA] Configuration sent, %zu bytes, status = %u\n", bufsize, err);    return err;}static bool fpga_online(void){    fpga_io_read(FPGA_CMD_ACK(EL_UIRQ_READY), ESPLINK_HDR_ADDR,		 &head, sizeof head);    if (head.magic != ESPLINK_HEAD_MAGIC || head.hlen <= 8) {	printf("[FPGA] Bad header received, magic = 0x%08x len = %u\n",	       head.magic, head.hlen);	return false;    }    if (unlikely(head.hlen < sizeof head)) {	/* Clear any fields not provided */	memset((char *)&head + head.hlen, 0, sizeof head - head.hlen);    }    printf("[FPGA] Ready, board = %u.%u fixes %02x fpga %u\n",	   head.board.major, head.board.minor,	   head.board.fixes, head.board.fpga);    printf("[FPGA] online, signature \"%.*s\"\n",	   (int)(sizeof head.signature - 1), head.signature);    esplink_start(&head);    setvar_bool(status_max80_fpga, true);    xSemaphoreGiveRecursive(spi_mutex);    fpga_send_config();    xTimerStart(fpga_timesync_timer, portMAX_DELAY);    fpga_poll_set_time();    return true;}static void fpga_offline(void){    memset(&head, 0, sizeof head);    xSemaphoreTakeRecursive(spi_mutex, portMAX_DELAY);    xTimerStop(fpga_timesync_timer, portMAX_DELAY);    setvar_bool(status_max80_fpga, false);    esplink_start(NULL);	/* Stop esplink */}esp_err_t fpga_iov(const struct fpga_iov *iov, size_t niov){    spi_transaction_ext_t trans[FPGA_IOV_MAX];    size_t ntrans = 0;    if (niov > FPGA_IOV_MAX)	return ESP_ERR_INVALID_ARG;    for (size_t i = 0; i < niov; i++) {	const struct fpga_iov *iv = &iov[i];	if (!iv->len && !(iv->cmd & FPGA_CMD_NULL))	    continue;	spi_transaction_ext_t *t = &trans[ntrans];	memset(t, 0, sizeof *t);	t->base.flags =	    SPI_TRANS_MODE_DIO |	    SPI_TRANS_VARIABLE_DUMMY |	    SPI_TRANS_MULTILINE_CMD |	    SPI_TRANS_MULTILINE_ADDR;	t->base.cmd           = iv->cmd;	t->base.addr          = iv->iaddr;	if (iv->cmd & FPGA_CMD_RD) {	    t->base.rxlength  = iv->len << 3;	    t->base.rx_buffer = iv->rdata;	    /* Emulate partial word read by adding dummy bits for offset */	    t->dummy_bits     = 16 + ((iv->iaddr & 3) << 2);	    if (iv->cmd & FPGA_CMD_STATUS) {		/*		 * Include the status "dummy" bits		 * THIS REQUIRES THE REMOTE ADDRESS TO BE 32-BIT ALIGNED		 */		t->base.rxlength += 32;		t->dummy_bits    -= 16;	    }	} else {	    t->base.length    = iv->len << 3;	    t->base.tx_buffer = iv->wdata;	}	ntrans++;    }    if (!ntrans)	return ESP_OK;    esp_err_t err = ESP_OK;    xSemaphoreTakeRecursive(spi_mutex, portMAX_DELAY);    if (!spi_handle) {	err = ESP_FAIL;	goto fail;    }    xEventGroupClearBits(spi_done_evgroup, EVENT_ALL_BITS);    size_t tbit = 1;    for (size_t i = 0; i < ntrans; i++) {	spi_transaction_ext_t *t = &trans[i];	t->base.user = (void *)tbit;	err = spi_device_queue_trans(spi_handle, &t->base, portMAX_DELAY);	if (err) {	    ntrans = i;	    break;	}	tbit <<= 1;    }    if (likely(ntrans)) {	xEventGroupWaitBits(spi_done_evgroup, tbit-1, pdTRUE, pdTRUE,			    portMAX_DELAY);	while (ntrans--) {	    /* This is insanely stupid to have to do when not needed */	    spi_transaction_t *tp;	    spi_device_get_trans_result(spi_handle, &tp, 0);	}    }fail:    xSemaphoreGiveRecursive(spi_mutex);    return err;}esp_err_t fpga_io_write(unsigned int cmd, const volatile void *addr,			const void *data, size_t len){    struct fpga_iov iov;    iov.cmd   = cmd & ~FPGA_CMD_RD;    iov.addr  = addr;    iov.wdata = data;    iov.len   = len;    return fpga_iov(&iov, 1);}esp_err_t fpga_io_read(unsigned int cmd, const volatile void *addr,		       void *data, size_t len){    struct fpga_iov iov;    iov.cmd   = cmd | FPGA_CMD_RD;    iov.addr  = addr;    iov.rdata = data;    iov.len   = len;    return fpga_iov(&iov, 1);}/* * This should be executed after getting an EL_UIRQ_TIME notification; * do this in polling mode for best latency. */static void fpga_get_time(void){    esp_err_t err;    struct tm tm;    struct timeval tv;    struct tsbuf {	/* These two words are the status word normally considered "dummy" */	uint16_t status;	uint16_t tick;	struct esplink_timesync_buf get;    } tsbuf;    if (!head.tsync) {	fpga_io_status(FPGA_CMD_ACK(EL_UIRQ_TIME));	return;    }    spi_transaction_ext_t trans;    memset(&trans, 0, sizeof trans);    trans.base.flags =	SPI_TRANS_MODE_DIO |	SPI_TRANS_VARIABLE_DUMMY |	SPI_TRANS_MULTILINE_CMD |	SPI_TRANS_MULTILINE_ADDR;    trans.base.rxlength = sizeof tsbuf << 3;    trans.base.rx_buffer = &tsbuf;    trans.base.addr = (size_t)&head.tsync->get;    trans.base.cmd  = FPGA_CMD_RD | FPGA_CMD_ACK(EL_UIRQ_TIME);    xSemaphoreTakeRecursive(spi_mutex, portMAX_DELAY);    err = spi_device_polling_transmit(spi_handle, &trans.base);    xSemaphoreGiveRecursive(spi_mutex);    if (err)	return;    if (time_net_sync_status)	return;		 /* Ignore time from RTC if SNTP active now */    tm.tm_sec   = tsbuf.get.tm.sec2 << 1;    tm.tm_min   = tsbuf.get.tm.min;    tm.tm_hour  = tsbuf.get.tm.hour;    tm.tm_mday  = tsbuf.get.tm.mday;    tm.tm_mon   = tsbuf.get.tm.mon - 1;    tm.tm_year  = tsbuf.get.tm.year + 80;    tm.tm_isdst = -1;		/* Unknown */    /* The third term handles wraparounds due to delay in transit */    tv.tv_sec = mktime(&tm) + (tsbuf.tick >> 15) +	((tsbuf.get.tick >= tsbuf.tick) << 1);    tv.tv_usec = (((uint32_t)tsbuf.tick << 17) * UINT64_C(1000000)) >> 32;    settimeofday(&tv, NULL);    print_time("[FPGA] Time set from RTC: ", &tv);}static void fpga_poll_set_time(void){    if (!head.tsync)	return;    if (!time_net_sync_status) {	/* Poll for current time; will call fpga_get_time() later */	fpga_io_status(FPGA_CMD_IRQ(EL_DIRQ_TIME));	return;    }    /* Otherwise transmit time to set the RTC */    esp_err_t err;    struct timeval tv;    struct esplink_timesync_buf tset;    spi_transaction_t trans;    memset(&trans, 0, sizeof trans);    tset.update = 1;    xSemaphoreTakeRecursive(spi_mutex, portMAX_DELAY);    gettimeofday(&tv, NULL);    const struct tm *tm = localtime(&tv.tv_sec);    tset.tick = ((tv.tv_usec * ((1ULL << (32+15))/1000000+1)) >> 32)      + ((tv.tv_sec & 1) << 15);    tset.tm.sec2 = tm->tm_sec >> 1;    tset.tm.min  = tm->tm_min;    tset.tm.hour = tm->tm_hour;    tset.tm.mday = tm->tm_mday;    tset.tm.mon  = tm->tm_mon + 1;    tset.tm.year = tm->tm_year - 80;    trans.flags =	SPI_TRANS_MODE_DIO |	SPI_TRANS_MULTILINE_CMD |	SPI_TRANS_MULTILINE_ADDR;    trans.length = sizeof tset << 3;    trans.tx_buffer = &tset;    trans.addr = (size_t)&head.tsync->set;    trans.cmd  = FPGA_CMD_WR | FPGA_CMD_IRQ(EL_DIRQ_TIME) |	FPGA_CMD_ACK(EL_UIRQ_TIME);    err = spi_device_polling_transmit(spi_handle, &trans);    xSemaphoreGiveRecursive(spi_mutex);    if (err)	return;    print_time("[FPGA] RTC update: ", &tv);}/* * Get status in polling mode (small transaction, < 256 CPU cycles). * cmd typically would be IRQ/ACK bits. */uint32_t fpga_io_status(unsigned int cmd){    spi_transaction_t trans;    memset(&trans, 0, sizeof trans);    trans.flags =	SPI_TRANS_MODE_DIO |	SPI_TRANS_MULTILINE_CMD |	SPI_TRANS_MULTILINE_ADDR |	SPI_TRANS_USE_RXDATA;    trans.cmd = cmd | FPGA_CMD_RD;    trans.addr = 0;    trans.rxlength = 32;    esp_err_t err = ESP_OK;    xSemaphoreTakeRecursive(spi_mutex, portMAX_DELAY);    err = spi_device_polling_transmit(spi_handle, &trans);    xSemaphoreGiveRecursive(spi_mutex);    return err ? 0 : *(const uint32_t *)trans.rx_data;}static int fpga_read_func(token_t token, void *buf, size_t len){    const void **pp = token;    const char *p = *pp;    esp_err_t err;    err = fpga_io_read(0, p, buf, len);    if (err)	return -1;    p += len;    *pp = p;    return len;}static void fpga_ota_update(void){    struct esplink_ota ota;    fpga_io_read(0, head.ota, &ota, sizeof ota);    if (!ota.data)	return;    esp_update(fpga_read_func, &ota.data, ota.len);    fpga_io_status(FPGA_CMD_ACK(EL_UIRQ_OTA)|FPGA_CMD_IRQ(EL_DIRQ_DONE));    reboot_delayed();}static void fpga_service_task(void *dummy){    (void)dummy;    enum fpga_state {	FPGA_DISABLED,		/* FPGA services disabled */	FPGA_OFFLINE,		/* FPGA services enabled, waiting for FPGA */	FPGA_ONLINE		/* FPGA services active */    } fpga_state = FPGA_DISABLED;    fputs("[FPGA] Starting FPGA services task\n", stdout);    while (1) {	uint32_t notifiers = 0;	uint32_t status;	switch (fpga_state) {	case FPGA_DISABLED:	    notifiers = notify_wait_for(NOTIFY_ENABLE);	    if ((notifiers & NOTIFY_ENABLE) && fpga_link_enable()) {		fputs("[FPGA] Enabling FPGA services\n", stdout);		fpga_state = FPGA_OFFLINE;	    }	    break;	case FPGA_OFFLINE:	  status = fpga_io_status(FPGA_CMD_IRQ(EL_DIRQ_HELLO));	  printf("[FPGA] FPGA status flags = 0x%08x\n", status);	  if (!digitalRead(PIN_FPGA_INT)) {	      for (unsigned int i = 1; i < 8; i++) {		  if (status & (0x100 << i))		      status = fpga_io_status(FPGA_CMD_ACK(i));	      }	      if ((status & 0x301) == 0x300) {		  if (fpga_online()) {		      fpga_state = FPGA_ONLINE;		      break;		  }	      }	  }	  notifiers = notify_wait_for(NOTIFY_FPGA|NOTIFY_DISABLE);	  break;	case FPGA_ONLINE:	    notifiers = notify_wait_for(NOTIFY_FPGA|NOTIFY_DISABLE|					NOTIFY_TIMESYNC);	    if (notifiers & NOTIFY_DISABLE) {		fpga_offline();		break;	    }	    while (!digitalRead(PIN_FPGA_INT)) {		status = fpga_io_status(0);		if ((status & 0x301) != 0x100) {		    fpga_offline();		    printf("[FPGA] FPGA offline, status = 0x%08x\n", status);		    fpga_state = FPGA_OFFLINE;		    notifiers = 0;		    break;		}		if (status & (0x100 << EL_UIRQ_TIME))		    fpga_get_time();		if (status & (0x100 << EL_UIRQ_RINGBUF))		    esplink_poll();		if (status & (0x100 << EL_UIRQ_OTA))		    fpga_ota_update();		for (unsigned int i = 5; i < 8; i++) {		    if (status & (0x100 << i)) {			printf("[FPGA] Invalid interrupt %u received\n", i);			status = fpga_io_status(FPGA_CMD_ACK(i));		    }		}	    }	    if (notifiers & NOTIFY_TIMESYNC)		fpga_poll_set_time();	    break;	}	if (notifiers & NOTIFY_DISABLE) {	    fputs("[FPGA] Disabling FPGA services\n", stdout);	    fpga_link_disable();	    fpga_state = FPGA_DISABLED;	}    }}
 |