zephyr/drivers/wifi/eswifi/eswifi_bus_spi.c
Carlos Stuart 75f77db432 include: misc: util.h: Rename min/max to MIN/MAX
There are issues using lowercase min and max macros when compiling a C++
application with a third-party toolchain such as GNU ARM Embedded when
using some STL headers i.e. <chrono>.

This is because there are actual C++ functions called min and max
defined in some of the STL headers and these macros interfere with them.
By changing the macros to UPPERCASE, which is consistent with almost all
other pre-processor macros this naming conflict is avoided.

All files that use these macros have been updated.

Signed-off-by: Carlos Stuart <carlosstuart1970@gmail.com>
2019-02-14 22:16:03 -05:00

261 lines
6.4 KiB
C

/**
* 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 = 0, 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 = 1000;
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,
};