| /* |
| * Copyright (c) 2020 Hubert Miś |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include "ft8xx_drv.h" |
| |
| #include <zephyr.h> |
| #include <drivers/gpio.h> |
| #include <drivers/spi.h> |
| #include <logging/log.h> |
| |
| #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 */ |
| static const struct device *spi_ft8xx_dev; |
| static struct spi_cs_control cs_ctrl; |
| static const struct spi_config spi_cfg = { |
| .frequency = 8000000UL, |
| .operation = SPI_WORD_SET(8) | SPI_OP_MODE_MASTER, |
| .cs = &cs_ctrl, |
| }; |
| |
| /* GPIO int line */ |
| #define IRQ_PIN DT_GPIO_PIN(NODE_ID, irq_gpios) |
| static const struct device *int_ft8xx_dev; |
| |
| 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; |
| |
| cs_ctrl = (struct spi_cs_control){ |
| .gpio_dev = device_get_binding( |
| DT_SPI_DEV_CS_GPIOS_LABEL(NODE_ID)), |
| .gpio_pin = DT_SPI_DEV_CS_GPIOS_PIN(NODE_ID), |
| .gpio_dt_flags = DT_SPI_DEV_CS_GPIOS_FLAGS(NODE_ID), |
| .delay = 0, |
| }; |
| |
| spi_ft8xx_dev = device_get_binding(DT_BUS_LABEL(NODE_ID)); |
| if (!spi_ft8xx_dev) { |
| return -ENODEV; |
| } |
| |
| /* TODO: Verify if such entry in DTS is present. |
| * If not, use polling mode. |
| */ |
| int_ft8xx_dev = device_get_binding(DT_GPIO_LABEL(NODE_ID, irq_gpios)); |
| if (!int_ft8xx_dev) { |
| return -ENODEV; |
| } |
| |
| ret = gpio_pin_configure(int_ft8xx_dev, IRQ_PIN, |
| GPIO_INPUT | DT_GPIO_FLAGS(NODE_ID, irq_gpios)); |
| if (ret != 0) { |
| return ret; |
| } |
| |
| ret = gpio_pin_interrupt_configure(int_ft8xx_dev, IRQ_PIN, |
| GPIO_INT_EDGE_TO_ACTIVE); |
| if (ret != 0) { |
| return ret; |
| } |
| |
| gpio_init_callback(&irq_cb_data, ft8xx_drv_irq_triggered, BIT(IRQ_PIN)); |
| gpio_add_callback(int_ft8xx_dev, &irq_cb_data); |
| |
| 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, |
| }; |
| |
| ret = spi_write(spi_ft8xx_dev, &spi_cfg, &tx_bufs); |
| 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, |
| }; |
| |
| ret = spi_transceive(spi_ft8xx_dev, &spi_cfg, &tx_bufs, &rx_bufs); |
| 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, |
| }; |
| |
| ret = spi_write(spi_ft8xx_dev, &spi_cfg, &tx_bufs); |
| if (ret < 0) { |
| LOG_ERR("SPI command error: %d", ret); |
| } |
| |
| return ret; |
| } |