123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320 |
- /*
- * Copyright (c) 2006-2023, RT-Thread Development Team
- *
- * SPDX-License-Identifier: Apache-2.0
- *
- * Change Logs:
- * Date Author Notes
- * 2022-07-30 Emuzit first version
- */
- #include <rthw.h>
- #include <rtdebug.h>
- #include <drivers/spi.h>
- #include <drivers/pin.h>
- #include "ch56x_spi.h"
- #include "ch56x_sys.h"
- #if !defined(BSP_USING_SPI0) && !defined(BSP_USING_SPI1)
- #error "Please define at least one SPIx"
- #endif
- struct spi_bus
- {
- struct rt_spi_bus parent;
- volatile struct spi_registers *reg_base;
- irq_number_t irqn;
- char *name;
- rt_base_t sck_pin;
- rt_base_t mosi_pin;
- rt_base_t miso_pin;
- };
- #ifdef BSP_USING_SPI0
- static struct spi_bus spi_bus_0 =
- {
- .reg_base = (struct spi_registers *)SPI0_REG_BASE,
- .irqn = SPI0_IRQn,
- .name = SPI0_BUS_NAME,
- .sck_pin = SPI0_SCK_PIN,
- .mosi_pin = SPI0_MOSI_PIN,
- .miso_pin = SPI0_MISO_PIN,
- };
- #endif
- #ifdef BSP_USING_SPI1
- static struct spi_bus spi_bus_1 =
- {
- .reg_base = (struct spi_registers *)SPI1_REG_BASE,
- .irqn = SPI1_IRQn,
- .name = SPI1_BUS_NAME,
- .sck_pin = SPI1_SCK_PIN,
- .mosi_pin = SPI1_MOSI_PIN,
- .miso_pin = SPI1_MISO_PIN,
- };
- #endif
- static uint8_t _spi_pin_config(struct rt_spi_device *device, struct rt_spi_configuration *config)
- {
- struct spi_bus *spi_bus = (struct spi_bus *)device->bus;
- uint8_t mode;
- /* RT_SPI_3WIRE means SI/SO pin shared */
- mode = config->mode & (RT_SPI_MASTER | RT_SPI_SLAVE | RT_SPI_3WIRE);
- if (mode == RT_SPI_MASTER)
- {
- mode = RB_SPI_MOSI_OE | RB_SPI_SCK_OE;
- rt_pin_mode(spi_bus->mosi_pin, PIN_MODE_OUTPUT);
- rt_pin_mode(spi_bus->sck_pin, PIN_MODE_OUTPUT);
- }
- else if (mode == RT_SPI_SLAVE)
- {
- mode = RB_SPI_MISO_OE | RB_SPI_MODE_SLAVE;
- rt_pin_mode(spi_bus->miso_pin, PIN_MODE_OUTPUT);
- }
- else if (mode == RT_SPI_MASTER | RT_SPI_3WIRE)
- {
- mode = RB_SPI_2WIRE_MOD | RB_SPI_MISO_OE | RB_SPI_SCK_OE;
- rt_pin_mode(spi_bus->miso_pin, PIN_MODE_INPUT);
- rt_pin_mode(spi_bus->sck_pin, PIN_MODE_OUTPUT);
- }
- else
- {
- mode = RB_SPI_2WIRE_MOD | RB_SPI_MISO_OE | RB_SPI_MODE_SLAVE;
- rt_pin_mode(spi_bus->miso_pin, PIN_MODE_INPUT);
- }
- return mode;
- }
- static rt_err_t spi_configure(struct rt_spi_device *device, struct rt_spi_configuration *config)
- {
- volatile struct spi_registers *sxreg;
- struct rt_spi_device *owner;
- union _spi_ctrl_mod ctrl_mod;
- uint8_t mode;
- uint32_t Fsys;
- uint32_t div;
- rt_base_t cs_pin;
- int inactive;
- RT_ASSERT(device != RT_NULL);
- /* ch56x SPI supports only 8-bit data */
- if (config->data_width != 8)
- return -RT_EINVAL;
- ctrl_mod.reg = _spi_pin_config(device, config);
- /* ch56x SPI supports only mode 0 & mode 3 */
- mode = config->mode & (RT_SPI_CPOL | RT_SPI_CPHA);
- if (mode == RT_SPI_MODE_0)
- ctrl_mod.mst_sck_mod = MST_SCK_MOD_0;
- else if (mode == RT_SPI_MODE_3)
- ctrl_mod.mst_sck_mod = MST_SCK_MOD_3;
- else
- return -RT_EINVAL;
- /* CLOCK_DIV is 8-bit, reject excessively low max_hz */
- Fsys = sys_hclk_get();
- if (config->max_hz < Fsys / 255)
- return -RT_EINVAL;
- /* minimum allowable CLOCK_DIV is 2 */
- div = (Fsys + config->max_hz - 1) / config->max_hz;
- if (div < 2)
- div = 2;
- sxreg = ((struct spi_bus *)device->bus)->reg_base;
- sxreg->CLOCK_DIV = div;
- mode = config->mode & (RT_SPI_MSB | RT_SPI_LSB);
- sxreg->CTRL_CFG.reg = (mode == RT_SPI_MSB) ? 0 : RB_SPI_BIT_ORDER;
- sxreg->INTER_EN.reg = 0;
- ctrl_mod.all_clear = 1;
- sxreg->CTRL_MOD.reg = ctrl_mod.reg;
- ctrl_mod.all_clear = 0;
- sxreg->CTRL_MOD.reg = ctrl_mod.reg;
- mode = config->mode & (RT_SPI_MASTER | RT_SPI_SLAVE);
- if (mode == RT_SPI_MASTER)
- {
- /* get bus owner before this configure */
- owner = device->bus->owner;
- if (owner && owner != device)
- {
- /* make sure predecessor's CS is deactived */
- inactive = (owner->config.mode & RT_SPI_CS_HIGH) ? PIN_LOW : PIN_HIGH;
- cs_pin = (rt_base_t)owner->parent.user_data;
- rt_pin_write(cs_pin, inactive);
- }
- /* bus owner is maintained by upper layer, do not update here */
- inactive = (config->mode & RT_SPI_CS_HIGH) ? PIN_LOW : PIN_HIGH;
- cs_pin = (rt_base_t)device->parent.user_data;
- rt_pin_write(cs_pin, inactive);
- rt_pin_mode(cs_pin, PIN_MODE_OUTPUT);
- }
- /* `config` is actually `device->config` : spi_core.c */
- //device->config = *config;
- return RT_EOK;
- }
- /**
- * @brief Transfer SPI data for single message.
- * Message traversing is done by rt_spi_message().
- *
- * @param device is pointer to the rt_spi_device device.
- *
- * @param message is a link list for data/control information,
- * only the first entry is processed.
- * Note: ch56x can't do SPI send & recv at the same time.
- *
- * @return `message->length1 if successful, 0 otherwise.
- */
- static rt_uint32_t spi_xfer(struct rt_spi_device *device, struct rt_spi_message *message)
- {
- struct spi_bus *spi_bus = (struct spi_bus *)device->bus;
- volatile struct spi_registers *sxreg = spi_bus->reg_base;
- union _spi_ctrl_mod ctrl_mod;
- uint8_t *data;
- uint32_t size;
- rt_base_t cs_pin;
- int cs_high;
- size = message->length;
- if (size == 0 || size > 4095)
- return 0;
- ctrl_mod.reg = sxreg->CTRL_MOD.reg | RB_SPI_ALL_CLEAR;
- /* ch56x can't do SPI send & recv at the same time */
- if (message->send_buf && !message->recv_buf)
- {
- data = (uint8_t *)message->send_buf;
- ctrl_mod.fifo_dir = SPI_FIFO_DIR_OUTPUT;
- }
- else if (!message->send_buf && message->recv_buf)
- {
- data = (uint8_t *)message->recv_buf;
- ctrl_mod.fifo_dir = SPI_FIFO_DIR_INPUT;
- }
- else
- {
- return 0;
- }
- sxreg->CTRL_MOD.reg = ctrl_mod.reg;
- ctrl_mod.all_clear = 0;
- sxreg->CTRL_MOD.reg = ctrl_mod.reg;
- /* set MISO pin direction to match xfer if shared SI/SO pin */
- if (device->config.mode & RT_SPI_3WIRE)
- {
- rt_base_t mode = message->send_buf ? PIN_MODE_OUTPUT : PIN_MODE_INPUT;
- rt_pin_mode(spi_bus->miso_pin, mode);
- }
- cs_pin = (rt_base_t)device->parent.user_data;
- cs_high = device->config.mode & RT_SPI_CS_HIGH;
- if (message->cs_take)
- {
- /* take/activate CS */
- rt_pin_write(cs_pin, cs_high ? PIN_HIGH : PIN_LOW);
- }
- sxreg->TOTAL_COUNT = size;
- if (size > SPI_FIFO_SIZE)
- {
- sxreg->DMA_BIG = (uint32_t)data;
- sxreg->DMA_END = (uint32_t)(data + size);
- sxreg->CTRL_CFG.dma_enable = 1;
- /* mark no need to read FIFO */
- size = 0;
- }
- else
- {
- if (message->send_buf)
- {
- /* keep sending, won't overflow */
- while (size)
- {
- sxreg->FIFO = *data++;
- size--;
- }
- }
- }
- /* wait for transfer done */
- while (sxreg->TOTAL_COUNT > 0);
- /* disable DMA, anyway */
- sxreg->CTRL_CFG.dma_enable = 0;
- /* non-DMA recv => read data from FIFO */
- if (size > 0)
- {
- while (size--)
- *data++ = sxreg->FIFO;
- }
- /* set MISO as input after xfer if shared SI/SO pin */
- if (device->config.mode & RT_SPI_3WIRE)
- {
- rt_pin_mode(spi_bus->miso_pin, PIN_MODE_INPUT);
- }
- if (message->cs_release)
- {
- /* release/deactivate CS */
- rt_pin_write(cs_pin, cs_high ? PIN_LOW : PIN_HIGH);
- }
- return message->length;
- }
- static const struct rt_spi_ops spi_ops =
- {
- .configure = spi_configure,
- .xfer = spi_xfer,
- };
- static int rt_hw_spi_init(void)
- {
- struct spi_bus *devices[2];
- rt_err_t res, ret = RT_EOK;
- int n = 0;
- #ifdef BSP_USING_SPI1
- devices[n++] = &spi_bus_1;
- #endif
- #ifdef BSP_USING_SPI0
- devices[n++] = &spi_bus_0;
- #endif
- while (--n >= 0)
- {
- struct spi_bus *spi_bus = devices[n];
- sys_clk_off_by_irqn(spi_bus->irqn, SYS_SLP_CLK_ON);
- res = rt_spi_bus_register(&spi_bus->parent, spi_bus->name, &spi_ops);
- if (res != RT_EOK)
- {
- ret = res;
- }
- };
- return ret;
- }
- INIT_DEVICE_EXPORT(rt_hw_spi_init);
|