Boot Linux faster!

Check our new training course

Boot Linux faster!

Check our new training course
and Creative Commons CC-BY-SA
lecture and lab materials

Bootlin logo

Elixir Cross Referencer

/**
 * Copyright (c) 2018 Linaro
 *
 * SPDX-License-Identifier: Apache-2.0
 */
#define LOG_LEVEL CONFIG_WIFI_LOG_LEVEL
#include <logging/log.h>
LOG_MODULE_REGISTER(wifi_eswifi_bus_spi);

#include <zephyr.h>
#include <kernel.h>
#include <device.h>
#include <string.h>
#include <errno.h>
#include <gpio.h>
#include <spi.h>

#include "eswifi.h"

#define ESWIFI_SPI_THREAD_STACK_SIZE 1024
K_THREAD_STACK_MEMBER(eswifi_spi_poll_stack, ESWIFI_SPI_THREAD_STACK_SIZE);

#define SPI_READ_CHUNK_SIZE 32

struct eswifi_spi_data {
	struct device *spi_dev;
	struct eswifi_gpio csn;
	struct eswifi_gpio dr;
	struct k_thread poll_thread;
	struct spi_config spi_cfg;
	struct spi_cs_control spi_cs;
};

static struct eswifi_spi_data eswifi_spi0; /* Static instance */

static bool eswifi_spi_cmddata_ready(struct eswifi_spi_data *spi)
{
	int value;

	gpio_pin_read(spi->dr.dev, spi->dr.pin, &value);

	return value ? true : false;
}

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(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 / 2; /* 16-bit words */
	spi_tx.buffers = spi_tx_buf;
	spi_tx.count = ARRAY_SIZE(spi_tx_buf);

	status = spi_write(spi->spi_dev, &spi->spi_cfg, &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 / 2; /* 16-bit words */
	spi_rx.buffers = spi_rx_buf;
	spi_rx.count = ARRAY_SIZE(spi_rx_buf);

	status = spi_read(spi->spi_dev, &spi->spi_cfg, &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 (csn)
	 * 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(spi->spi_dev, &spi->spi_cfg);

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(1);
	}

	/* Our device is flagged with SPI_HOLD_ON_CS|SPI_LOCK_ON, release */
	spi_release(spi->spi_dev, &spi->spi_cfg);

	LOG_DBG("success");

	return offset;
}

static void eswifi_spi_read_msg(struct eswifi_dev *eswifi)
{
	char cmd[] = "MR\r";
	char *rsp;
	int err;

	eswifi_lock(eswifi);

	err = eswifi_at_cmd_rsp(eswifi, cmd, &rsp);
	if (err < 0) {
		LOG_ERR("Unable to read msg %d", err);
	}

	eswifi_unlock(eswifi);
}

static void eswifi_spi_poll_thread(void *p1)
{
	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 */

	/* SPI DEV */
	spi->spi_dev = device_get_binding(DT_INVENTEK_ESWIFI_ESWIFI0_BUS_NAME);
	if (!spi->spi_dev) {
		LOG_ERR("Failed to initialize SPI driver");
		return -ENODEV;
	}

	/* SPI DATA READY PIN */
	spi->dr.dev = device_get_binding(
			DT_INVENTEK_ESWIFI_ESWIFI0_DATA_GPIOS_CONTROLLER);
	if (!spi->dr.dev) {
		LOG_ERR("Failed to initialize GPIO driver: %s",
			    DT_INVENTEK_ESWIFI_ESWIFI0_DATA_GPIOS_CONTROLLER);
		return -ENODEV;
	}
	spi->dr.pin = DT_INVENTEK_ESWIFI_ESWIFI0_DATA_GPIOS_PIN;
	gpio_pin_configure(spi->dr.dev, spi->dr.pin, GPIO_DIR_IN);


	/* SPI CONFIG/CS */
	spi->spi_cfg.frequency = DT_INVENTEK_ESWIFI_ESWIFI0_SPI_MAX_FREQUENCY;
	spi->spi_cfg.operation = (SPI_OP_MODE_MASTER | SPI_TRANSFER_MSB |
				  SPI_WORD_SET(16) | SPI_LINES_SINGLE |
				  SPI_HOLD_ON_CS | SPI_LOCK_ON);
	spi->spi_cfg.slave = DT_INVENTEK_ESWIFI_ESWIFI0_BASE_ADDRESS;
	spi->spi_cs.gpio_dev =
		device_get_binding(DT_INVENTEK_ESWIFI_ESWIFI0_CS_GPIO_CONTROLLER);
	spi->spi_cs.gpio_pin = DT_INVENTEK_ESWIFI_ESWIFI0_CS_GPIO_PIN;
	spi->spi_cs.delay = 1000U;
	spi->spi_cfg.cs = &spi->spi_cs;

	eswifi->bus_data = spi;

	LOG_DBG("success");

	k_thread_create(&spi->poll_thread, eswifi_spi_poll_stack,
			ESWIFI_SPI_THREAD_STACK_SIZE,
			(k_thread_entry_t)eswifi_spi_poll_thread, eswifi, NULL,
			NULL, K_PRIO_COOP(CONFIG_WIFI_ESWIFI_THREAD_PRIO), 0,
			K_NO_WAIT);

	return 0;
}

struct eswifi_bus_ops eswifi_bus_ops_spi = {
	.init = eswifi_spi_init,
	.request = eswifi_spi_request,
};