2021-02-08 21:14:29 +01:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2020 Hubert Miś
|
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "ft8xx_drv.h"
|
|
|
|
|
includes: prefer <zephyr/kernel.h> over <zephyr/zephyr.h>
As of today <zephyr/zephyr.h> is 100% equivalent to <zephyr/kernel.h>.
This patch proposes to then include <zephyr/kernel.h> instead of
<zephyr/zephyr.h> since it is more clear that you are including the
Kernel APIs and (probably) nothing else. <zephyr/zephyr.h> sounds like a
catch-all header that may be confusing. Most applications need to
include a bunch of other things to compile, e.g. driver headers or
subsystem headers like BT, logging, etc.
The idea of a catch-all header in Zephyr is probably not feasible
anyway. Reason is that Zephyr is not a library, like it could be for
example `libpython`. Zephyr provides many utilities nowadays: a kernel,
drivers, subsystems, etc and things will likely grow. A catch-all header
would be massive, difficult to keep up-to-date. It is also likely that
an application will only build a small subset. Note that subsystem-level
headers may use a catch-all approach to make things easier, though.
NOTE: This patch is **NOT** removing the header, just removing its usage
in-tree. I'd advocate for its deprecation (add a #warning on it), but I
understand many people will have concerns.
Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
2022-08-25 09:58:46 +02:00
|
|
|
#include <zephyr/kernel.h>
|
2022-05-06 10:25:46 +02:00
|
|
|
#include <zephyr/drivers/gpio.h>
|
|
|
|
#include <zephyr/drivers/spi.h>
|
|
|
|
#include <zephyr/logging/log.h>
|
2021-02-08 21:14:29 +01:00
|
|
|
|
|
|
|
#define LOG_MODULE_NAME ft8xx_drv
|
|
|
|
LOG_MODULE_REGISTER(LOG_MODULE_NAME);
|
|
|
|
|
|
|
|
#define DT_DRV_COMPAT ftdi_ft800
|
|
|
|
#define NODE_ID DT_INST(0, DT_DRV_COMPAT)
|
|
|
|
|
|
|
|
/* SPI device */
|
2022-06-28 23:57:59 -05:00
|
|
|
static const struct spi_dt_spec spi = SPI_DT_SPEC_INST_GET(0,
|
|
|
|
SPI_WORD_SET(8) | SPI_OP_MODE_MASTER,
|
|
|
|
0);
|
2021-02-08 21:14:29 +01:00
|
|
|
|
|
|
|
/* GPIO int line */
|
2022-06-29 00:04:19 -05:00
|
|
|
static const struct gpio_dt_spec irq_gpio = GPIO_DT_SPEC_INST_GET(0, irq_gpios);
|
2021-02-08 21:14:29 +01:00
|
|
|
|
|
|
|
static struct gpio_callback irq_cb_data;
|
|
|
|
|
|
|
|
__weak void ft8xx_drv_irq_triggered(const struct device *dev,
|
|
|
|
struct gpio_callback *cb, uint32_t pins)
|
|
|
|
{
|
|
|
|
/* Intentionally empty */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Protocol details */
|
|
|
|
#define ADDR_SIZE 3
|
|
|
|
#define DUMMY_READ_SIZE 1
|
|
|
|
#define COMMAND_SIZE 3
|
|
|
|
#define MAX_READ_LEN (UINT16_MAX - ADDR_SIZE - DUMMY_READ_SIZE)
|
|
|
|
#define MAX_WRITE_LEN (UINT16_MAX - ADDR_SIZE)
|
|
|
|
|
|
|
|
#define READ_OP 0x00
|
|
|
|
#define WRITE_OP 0x80
|
|
|
|
#define COMMAND_OP 0x40
|
|
|
|
|
|
|
|
static void insert_addr(uint32_t addr, uint8_t *buff)
|
|
|
|
{
|
|
|
|
buff[0] = (addr >> 16) & 0x3f;
|
|
|
|
buff[1] = (addr >> 8) & 0xff;
|
|
|
|
buff[2] = (addr) & 0xff;
|
|
|
|
}
|
|
|
|
|
|
|
|
int ft8xx_drv_init(void)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
2022-12-03 14:49:05 +01:00
|
|
|
if (!spi_is_ready_dt(&spi)) {
|
2022-06-28 23:57:59 -05:00
|
|
|
LOG_ERR("SPI bus %s not ready", spi.bus->name);
|
2021-02-08 21:14:29 +01:00
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* TODO: Verify if such entry in DTS is present.
|
|
|
|
* If not, use polling mode.
|
|
|
|
*/
|
2023-08-26 13:43:58 +10:00
|
|
|
if (!gpio_is_ready_dt(&irq_gpio)) {
|
2022-06-29 00:04:19 -05:00
|
|
|
LOG_ERR("GPIO device %s is not ready", irq_gpio.port->name);
|
2021-02-08 21:14:29 +01:00
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
2022-06-29 00:04:19 -05:00
|
|
|
ret = gpio_pin_configure_dt(&irq_gpio, GPIO_INPUT);
|
2021-02-08 21:14:29 +01:00
|
|
|
if (ret != 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2022-06-29 00:04:19 -05:00
|
|
|
ret = gpio_pin_interrupt_configure_dt(&irq_gpio, GPIO_INT_EDGE_TO_ACTIVE);
|
2021-02-08 21:14:29 +01:00
|
|
|
if (ret != 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2022-06-29 00:04:19 -05:00
|
|
|
gpio_init_callback(&irq_cb_data, ft8xx_drv_irq_triggered, BIT(irq_gpio.pin));
|
|
|
|
gpio_add_callback(irq_gpio.port, &irq_cb_data);
|
2021-02-08 21:14:29 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int ft8xx_drv_write(uint32_t address, const uint8_t *data, unsigned int length)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
uint8_t addr_buf[ADDR_SIZE];
|
|
|
|
|
|
|
|
insert_addr(address, addr_buf);
|
|
|
|
addr_buf[0] |= WRITE_OP;
|
|
|
|
|
|
|
|
struct spi_buf tx[] = {
|
|
|
|
{
|
|
|
|
.buf = addr_buf,
|
|
|
|
.len = sizeof(addr_buf),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
/* Discard const, it is implicit for TX buffer */
|
|
|
|
.buf = (uint8_t *)data,
|
|
|
|
.len = length,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
struct spi_buf_set tx_bufs = {
|
|
|
|
.buffers = tx,
|
|
|
|
.count = 2,
|
|
|
|
};
|
|
|
|
|
2022-06-28 23:57:59 -05:00
|
|
|
ret = spi_write_dt(&spi, &tx_bufs);
|
2021-02-08 21:14:29 +01:00
|
|
|
if (ret < 0) {
|
|
|
|
LOG_ERR("SPI write error: %d", ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int ft8xx_drv_read(uint32_t address, uint8_t *data, unsigned int length)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
uint8_t dummy_read_buf[ADDR_SIZE + DUMMY_READ_SIZE];
|
|
|
|
uint8_t addr_buf[ADDR_SIZE];
|
|
|
|
|
|
|
|
insert_addr(address, addr_buf);
|
|
|
|
addr_buf[0] |= READ_OP;
|
|
|
|
|
|
|
|
struct spi_buf tx = {
|
|
|
|
.buf = addr_buf,
|
|
|
|
.len = sizeof(addr_buf),
|
|
|
|
};
|
|
|
|
|
|
|
|
struct spi_buf_set tx_bufs = {
|
|
|
|
.buffers = &tx,
|
|
|
|
.count = 1,
|
|
|
|
};
|
|
|
|
|
|
|
|
struct spi_buf rx[] = {
|
|
|
|
{
|
|
|
|
.buf = dummy_read_buf,
|
|
|
|
.len = sizeof(dummy_read_buf),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.buf = data,
|
|
|
|
.len = length,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
struct spi_buf_set rx_bufs = {
|
|
|
|
.buffers = rx,
|
|
|
|
.count = 2,
|
|
|
|
};
|
|
|
|
|
2022-06-28 23:57:59 -05:00
|
|
|
ret = spi_transceive_dt(&spi, &tx_bufs, &rx_bufs);
|
2021-02-08 21:14:29 +01:00
|
|
|
if (ret < 0) {
|
|
|
|
LOG_ERR("SPI transceive error: %d", ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int ft8xx_drv_command(uint8_t command)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
/* Most commands include COMMAND_OP bit. ACTIVE power mode command is
|
|
|
|
* an exception with value 0x00.
|
|
|
|
*/
|
|
|
|
uint8_t cmd_buf[COMMAND_SIZE] = {command, 0, 0};
|
|
|
|
|
|
|
|
struct spi_buf tx = {
|
|
|
|
.buf = cmd_buf,
|
|
|
|
.len = sizeof(cmd_buf),
|
|
|
|
};
|
|
|
|
|
|
|
|
struct spi_buf_set tx_bufs = {
|
|
|
|
.buffers = &tx,
|
|
|
|
.count = 1,
|
|
|
|
};
|
|
|
|
|
2022-06-28 23:57:59 -05:00
|
|
|
ret = spi_write_dt(&spi, &tx_bufs);
|
2021-02-08 21:14:29 +01:00
|
|
|
if (ret < 0) {
|
|
|
|
LOG_ERR("SPI command error: %d", ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|