/* * Copyright (c) 2020 Hubert Miś * * SPDX-License-Identifier: Apache-2.0 */ #include "ft8xx_drv.h" #include #include #include #include #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 spi_dt_spec spi = SPI_DT_SPEC_INST_GET(0, SPI_WORD_SET(8) | SPI_OP_MODE_MASTER, 0); /* GPIO int line */ static const struct gpio_dt_spec irq_gpio = GPIO_DT_SPEC_INST_GET(0, irq_gpios); 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; if (!spi_is_ready_dt(&spi)) { LOG_ERR("SPI bus %s not ready", spi.bus->name); return -ENODEV; } /* TODO: Verify if such entry in DTS is present. * If not, use polling mode. */ if (!gpio_is_ready_dt(&irq_gpio)) { LOG_ERR("GPIO device %s is not ready", irq_gpio.port->name); return -ENODEV; } ret = gpio_pin_configure_dt(&irq_gpio, GPIO_INPUT); if (ret != 0) { return ret; } ret = gpio_pin_interrupt_configure_dt(&irq_gpio, GPIO_INT_EDGE_TO_ACTIVE); if (ret != 0) { return ret; } gpio_init_callback(&irq_cb_data, ft8xx_drv_irq_triggered, BIT(irq_gpio.pin)); gpio_add_callback(irq_gpio.port, &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_dt(&spi, &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_dt(&spi, &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_dt(&spi, &tx_bufs); if (ret < 0) { LOG_ERR("SPI command error: %d", ret); } return ret; }