/** * Copyright (c) 2018 Linaro * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT inventek_eswifi #include "eswifi_log.h" LOG_MODULE_DECLARE(LOG_MODULE_NAME); #include #include #include #include #include #include #include "eswifi.h" #define ESWIFI_SPI_THREAD_STACK_SIZE 1024 K_KERNEL_STACK_MEMBER(eswifi_spi_poll_stack, ESWIFI_SPI_THREAD_STACK_SIZE); #define SPI_READ_CHUNK_SIZE 32 struct eswifi_spi_config { struct gpio_dt_spec dr; struct spi_dt_spec bus; }; struct eswifi_spi_data { const struct eswifi_spi_config *cfg; struct k_thread poll_thread; }; static const struct eswifi_spi_config eswifi_config_spi0 = { .dr = GPIO_DT_SPEC_INST_GET(0, data_gpios), .bus = SPI_DT_SPEC_INST_GET(0, SPI_OP_MODE_MASTER | SPI_TRANSFER_MSB | SPI_WORD_SET(16) | SPI_HOLD_ON_CS | SPI_LOCK_ON, 1000U), }; static struct eswifi_spi_data eswifi_spi0; static bool eswifi_spi_cmddata_ready(struct eswifi_spi_data *spi) { return gpio_pin_get_dt(&spi->cfg->dr) > 0; } static int eswifi_spi_wait_cmddata_ready(struct eswifi_spi_data *spi) { unsigned int max_retries = 60 * 1000; /* 1 minute */ do { /* allow other threads to be scheduled */ k_sleep(K_MSEC(1)); } while (!eswifi_spi_cmddata_ready(spi) && --max_retries); return max_retries ? 0 : -ETIMEDOUT; } static int eswifi_spi_write(struct eswifi_dev *eswifi, char *data, size_t dlen) { struct eswifi_spi_data *spi = eswifi->bus_data; struct spi_buf spi_tx_buf[1]; struct spi_buf_set spi_tx; int status; spi_tx_buf[0].buf = data; spi_tx_buf[0].len = dlen; spi_tx.buffers = spi_tx_buf; spi_tx.count = ARRAY_SIZE(spi_tx_buf); status = spi_write_dt(&spi->cfg->bus, &spi_tx); if (status) { LOG_ERR("SPI write error %d", status); } else { status = dlen; } return status; } static int eswifi_spi_read(struct eswifi_dev *eswifi, char *data, size_t dlen) { struct eswifi_spi_data *spi = eswifi->bus_data; struct spi_buf spi_rx_buf[1]; struct spi_buf_set spi_rx; int status; spi_rx_buf[0].buf = data; spi_rx_buf[0].len = dlen; spi_rx.buffers = spi_rx_buf; spi_rx.count = ARRAY_SIZE(spi_rx_buf); status = spi_read_dt(&spi->cfg->bus, &spi_rx); if (status) { LOG_ERR("SPI read error %d", status); } else { status = dlen; } return status; } static int eswifi_spi_request(struct eswifi_dev *eswifi, char *cmd, size_t clen, char *rsp, size_t rlen) { struct eswifi_spi_data *spi = eswifi->bus_data; unsigned int offset = 0U, to_read = SPI_READ_CHUNK_SIZE; char tmp[2]; int err; LOG_DBG("cmd=%p (%u byte), rsp=%p (%u byte)", cmd, clen, rsp, rlen); /* * CMD/DATA protocol: * 1. Module raises data-ready when ready for **command phase** * 2. Host announces command start by lowering chip-select * 3. Host write the command (possibly several spi transfers) * 4. Host announces end of command by raising chip-select * 5. Module lowers data-ready signal * 6. Module raises data-ready to signal start of the **data phase** * 7. Host lowers chip-select * 8. Host fetch data as long as data-ready pin is up * 9. Module lowers data-ready to signal the end of the data Phase * 10. Host raises chip-select * * Note: * All commands to the eS-WiFi module must be post-padded with * 0x0A (Line Feed) to an even number of bytes. * All data from eS-WiFi module are post-padded with 0x15(NAK) to an * even number of bytes. */ if (!cmd) { goto data; } /* CMD/DATA READY signals the Command Phase */ err = eswifi_spi_wait_cmddata_ready(spi); if (err) { LOG_ERR("CMD ready timeout\n"); return err; } if (clen % 2) { /* Add post-padding if necessary */ /* cmd is a string so cmd[clen] is 0x00 */ cmd[clen] = 0x0a; clen++; } eswifi_spi_write(eswifi, cmd, clen); /* Our device is flagged with SPI_HOLD_ON_CS|SPI_LOCK_ON, release */ spi_release_dt(&spi->cfg->bus); data: /* CMD/DATA READY signals the Data Phase */ err = eswifi_spi_wait_cmddata_ready(spi); if (err) { LOG_ERR("DATA ready timeout\n"); return err; } while (eswifi_spi_cmddata_ready(spi) && to_read) { to_read = MIN(rlen - offset, to_read); memset(rsp + offset, 0, to_read); eswifi_spi_read(eswifi, rsp + offset, to_read); offset += to_read; k_yield(); } /* Flush remaining data if receiving buffer not large enough */ while (eswifi_spi_cmddata_ready(spi)) { eswifi_spi_read(eswifi, tmp, 2); k_sleep(K_MSEC(1)); } /* Our device is flagged with SPI_HOLD_ON_CS|SPI_LOCK_ON, release */ spi_release_dt(&spi->cfg->bus); LOG_DBG("success"); return offset; } static void eswifi_spi_read_msg(struct eswifi_dev *eswifi) { const char startstr[] = "[SOMA]"; const char endstr[] = "[EOMA]"; char cmd[] = "MR\r"; size_t msg_len; char *rsp; int ret; LOG_DBG(""); eswifi_lock(eswifi); ret = eswifi_at_cmd_rsp(eswifi, cmd, &rsp); if (ret < 0) { LOG_ERR("Unable to read msg %d", ret); eswifi_unlock(eswifi); return; } if (strncmp(rsp, startstr, sizeof(endstr) - 1)) { LOG_ERR("Malformed async msg"); eswifi_unlock(eswifi); return; } /* \r\n[SOMA]...[EOMA]\r\nOK\r\n> */ msg_len = ret - (sizeof(startstr) - 1) - (sizeof(endstr) - 1); if (msg_len > 0) { eswifi_async_msg(eswifi, rsp + sizeof(endstr) - 1, msg_len); } eswifi_unlock(eswifi); } static void eswifi_spi_poll_thread(void *p1, void *p2, void *p3) { ARG_UNUSED(p2); ARG_UNUSED(p3); struct eswifi_dev *eswifi = p1; while (1) { k_sleep(K_MSEC(1000)); eswifi_spi_read_msg(eswifi); } } int eswifi_spi_init(struct eswifi_dev *eswifi) { struct eswifi_spi_data *spi = &eswifi_spi0; /* Static instance */ const struct eswifi_spi_config *cfg = &eswifi_config_spi0; /* Static instance */ /* SPI DATA READY PIN */ if (!gpio_is_ready_dt(&cfg->dr)) { LOG_ERR("device %s is not ready", cfg->dr.port->name); return -ENODEV; } gpio_pin_configure_dt(&cfg->dr, GPIO_INPUT); /* SPI BUS */ if (!spi_is_ready_dt(&cfg->bus)) { LOG_ERR("SPI bus is not ready"); return -ENODEV; }; spi->cfg = cfg; eswifi->bus_data = spi; LOG_DBG("success"); k_thread_create(&spi->poll_thread, eswifi_spi_poll_stack, ESWIFI_SPI_THREAD_STACK_SIZE, eswifi_spi_poll_thread, eswifi, NULL, NULL, K_PRIO_COOP(CONFIG_WIFI_ESWIFI_THREAD_PRIO), 0, K_NO_WAIT); return 0; } static struct eswifi_bus_ops eswifi_bus_ops_spi = { .init = eswifi_spi_init, .request = eswifi_spi_request, }; struct eswifi_bus_ops *eswifi_get_bus(void) { return &eswifi_bus_ops_spi; }