123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390 |
- /*
- * Copyright (c) 2006-2025 RT-Thread Development Team
- *
- * SPDX-License-Identifier: Apache-2.0
- *
- * spi driver for synopsys dw apb spi
- *
- * Change Logs:
- * Date Author Notes
- * 2025-01-03 ZhangJing first version
- */
- #include <rtconfig.h>
- #ifdef BSP_USING_DW_SPI
- #include <rtthread.h>
- #include <rtdevice.h>
- #include "drv_dw_spi.h"
- #include <rtdbg.h>
- #include <io.h>
- #ifdef RT_USING_SMART
- #include <ioremap.h>
- #endif
- #define SPI0_BUS_NAME "spi0"
- #define SPI0_BUS_DEVICE0_NAME "spi00"
- static struct dw_spi dw_spi_device =
- {
- .device_name = "spi0",
- .irq = DW_SPI_IRQ_BASE,
- .max_freq = DW_SPI_MAX_FREQ,
- .bits_per_word = 8,
- .cs = 0,
- .fifo_len = 0,
- .type = DW_SPI_CTRLR0_FRF_MOT,
- };
- static rt_uint32_t update_control_reg_16(struct dw_spi *dw_spi_dev)
- {
- return ((dw_spi_dev->bits_per_word - 1) << CTRLR0_DFS_SHIFT) |
- (dw_spi_dev->type << DW_SPI_CTRLR0_FRF_SHIFT) |
- (dw_spi_dev->mode << DW_SPI_CTRLR0_MODE_SHIFT) |
- (dw_spi_dev->tmode << DW_SPI_CTRLR0_TMOD_SHIFT);
- }
- static rt_uint32_t update_control_reg_32(struct dw_spi *dw_spi_dev)
- {
- return ((dw_spi_dev->bits_per_word - 1) << CTRLR0_DFS_32_SHIFT) |
- (dw_spi_dev->type << DW_SPI_CTRLR0_FRF_SHIFT) |
- (dw_spi_dev->mode << DW_SPI_CTRLR0_MODE_SHIFT) |
- (dw_spi_dev->tmode << DW_SPI_CTRLR0_TMOD_SHIFT);
- }
- static rt_err_t _dw_drv_spi_configure(struct rt_spi_device *device,
- struct rt_spi_configuration *config)
- {
- struct dw_spi *dw_spi_dev = (struct dw_spi *)(device->bus->parent.user_data);
- if (!dw_spi_dev)
- {
- LOG_E("SPI device is null");
- return -RT_ERROR;
- }
- struct dw_spi_regs *regs = dw_spi_dev->reg;
- writel(0, ®s->ssienr);
- writel(0, ®s->ser);
- writel(0, ®s->imr);
- writel(0, ®s->dmacr);
- rt_uint32_t ctrl = 0;
- switch (config->mode & (RT_SPI_CPHA | RT_SPI_CPOL))
- {
- case RT_SPI_MODE_0:
- ctrl |= 0;
- break;
- case RT_SPI_MODE_1:
- ctrl |= DW_SPI_CTRLR0_SCPH;
- break;
- case RT_SPI_MODE_2:
- ctrl |= DW_SPI_CTRLR0_SCPOL;
- break;
- case RT_SPI_MODE_3:
- ctrl |= DW_SPI_CTRLR0_SCPH | DW_SPI_CTRLR0_SCPOL;
- break;
- }
- dw_spi_dev->mode = ctrl;
- /* div = dw_spi_dev->max_freq / configuration->max_hz;*/
- rt_uint32_t div = 0;
- div = dw_spi_dev->max_freq / 15625000;
- if (div % 2)
- div++;
- /* TODO: */
- /* write clock divider register */
- writel(div, ®s->baudr);
- /* write TX threshold register */
- writel(0, ®s->txflr);
- /* write RX threshold register */
- writel(0, ®s->rxflr);
- /* enable spi */
- writel(1, ®s->ssienr);
- return RT_EOK;
- }
- static rt_uint32_t min3(rt_uint32_t a, rt_uint32_t b, rt_uint32_t c)
- {
- return (a < b) ? ((a < c) ? a : c) : ((b < c) ? b : c);
- }
- static inline rt_uint32_t tx_max(struct dw_spi *dw_spi_dev)
- {
- rt_uint32_t tx_left, tx_room, rxtx_gap;
- struct dw_spi_regs *regs = dw_spi_dev->reg;
- tx_left = dw_spi_dev->tx_len;
- tx_room = dw_spi_dev->fifo_len - (rt_uint32_t)(readl(®s->txflr));
- /*
- * Another concern is about the tx/rx mismatch, we
- * thought about using (priv->fifo_len - rxflr - txflr) as
- * one maximum value for tx, but it doesn't cover the
- * data which is out of tx/rx fifo and inside the
- * shift registers. So a control from sw point of
- * view is taken.
- */
- if (dw_spi_dev->rx != NULL && dw_spi_dev->tx != NULL)
- {
- rxtx_gap = dw_spi_dev->fifo_len - (dw_spi_dev->rx_len - dw_spi_dev->tx_len);
- return min3(tx_left, tx_room, (rt_uint32_t)(rxtx_gap));
- }
- else
- {
- return tx_left < tx_room ? tx_left : tx_room;
- }
- }
- static inline rt_uint32_t rx_max(struct dw_spi *dw_spi_dev)
- {
- rt_uint32_t rx_left = dw_spi_dev->rx_len;
- struct dw_spi_regs *regs = dw_spi_dev->reg;
- rt_uint32_t val = readl(®s->rxflr);
- return (rx_left < val) ? rx_left : val;
- }
- /* write data to spi */
- static void dw_spi_writer(struct dw_spi *dw_spi_dev)
- {
- rt_uint32_t max = tx_max(dw_spi_dev);
- rt_uint32_t txw = 0xFF;
- struct dw_spi_regs *regs = dw_spi_dev->reg;
- while (max--)
- {
- /* Set the tx word if the transfer's original "tx" is not null */
- if (dw_spi_dev->tx)
- {
- if (dw_spi_dev->bits_per_word == 8)
- txw = *(rt_uint8_t *)(dw_spi_dev->tx);
- else
- txw = *(rt_uint16_t *)(dw_spi_dev->tx);
- dw_spi_dev->tx += dw_spi_dev->bits_per_word >> 3;
- }
- writel(txw, ®s->dr);
- dw_spi_dev->tx_len--;
- }
- }
- /* read data from spi */
- static void dw_spi_reader(struct dw_spi *dw_spi_dev)
- {
- struct dw_spi_regs *regs = dw_spi_dev->reg;
- rt_uint32_t max = rx_max(dw_spi_dev);
- rt_uint16_t rxw;
- while (max--)
- {
- rxw = readl(®s->dr);
- if (dw_spi_dev->rx)
- {
- if (dw_spi_dev->bits_per_word == 8)
- *(rt_uint8_t *)(dw_spi_dev->rx) = rxw;
- else
- *(rt_uint16_t *)(dw_spi_dev->rx) = rxw;
- dw_spi_dev->rx += dw_spi_dev->bits_per_word >> 3;
- }
- dw_spi_dev->rx_len--;
- }
- }
- static void wait_for_idle(struct dw_spi *dw_spi_dev)
- {
- rt_uint32_t status;
- struct dw_spi_regs *regs = dw_spi_dev->reg;
- while (1)
- {
- status = readl(®s->sr);
- if (!(status & DW_SPI_SR_BUSY))
- {
- if (status & DW_SPI_SR_TX_EMPTY)
- {
- break;
- }
- }
- rt_thread_yield();
- }
- }
- static int dw_spi_poll_transfer(struct dw_spi *dw_spi_dev)
- {
- do
- {
- dw_spi_writer(dw_spi_dev);
- wait_for_idle(dw_spi_dev);
- dw_spi_reader(dw_spi_dev);
- }
- while (dw_spi_dev->rx_len && dw_spi_dev->tx_len);
- return 0;
- }
- static rt_ssize_t _dw_spixfer(struct rt_spi_device *device, struct rt_spi_message *message)
- {
- struct dw_spi *dw_spi_dev = (struct dw_spi *)(device->bus->parent.user_data);
- if (!dw_spi_dev)
- {
- LOG_E("SPI device is null");
- return -RT_ERROR;
- }
- dw_spi_dev->tx = (void *)message->send_buf;
- dw_spi_dev->tx_end = dw_spi_dev->tx + dw_spi_dev->tx_len;
- dw_spi_dev->rx = (void *)message->recv_buf;
- dw_spi_dev->rx_end = dw_spi_dev->rx + dw_spi_dev->rx_len;
- dw_spi_dev->tx_len = message->length;
- dw_spi_dev->rx_len = message->length;
- if (dw_spi_dev->tx && dw_spi_dev->rx)
- {
- /* set mode to Tx & Rx */
- dw_spi_dev->tmode = DW_SPI_CTRLR0_TMOD_TX_RX;
- }
- else if (dw_spi_dev->rx)
- {
- /* set mode to Rx only */
- dw_spi_dev->tmode = DW_SPI_CTRLR0_TMOD_RX;
- }
- else
- {
- /* set mode to Tx only */
- dw_spi_dev->tmode = DW_SPI_CTRLR0_TMOD_TX;
- }
- struct dw_spi_regs *regs = dw_spi_dev->reg;
- /* disable spi */
- writel(0, ®s->ssienr);
- LOG_D("dw_spixfer:tx 0x%x rx 0x%x len %d\n", dw_spi_dev->tx, dw_spi_dev->rx, message->length);
- rt_uint32_t cr0 = dw_spi_dev->update_cr0(dw_spi_dev);
- /* write control register */
- if (cr0 != readl(®s->ctrl0))
- {
- writel(cr0, ®s->ctrl0);
- }
- /* set slave select */
- writel(1 << (dw_spi_dev->cs), ®s->ser);
- /* enable spi */
- writel(1, ®s->ssienr);
- /* transfer data */
- dw_spi_poll_transfer(dw_spi_dev);
- /* wait for transfer complete */
- wait_for_idle(dw_spi_dev);
- return message->length;
- }
- const static struct rt_spi_ops dw_drv_spi_ops =
- {
- _dw_drv_spi_configure,
- _dw_spixfer,
- };
- static int dw_spi_hw_init(struct dw_spi *dw_spi_dev)
- {
- struct dw_spi_regs *regs = dw_spi_dev->reg;
- /* disable spi */
- writel(0, ®s->ssienr);
- /* disbale all interrupts */
- writel(0, ®s->imr);
- /* read and clear interrupt status */
- readl(®s->icr);
- /* set slave select */
- writel(0, ®s->ser);
- /* enable spi */
- writel(1, ®s->ssienr);
- dw_spi_dev->version = readl(®s->version);
- rt_kprintf("ssi_version_id=%c.%c%c%c\n",
- dw_spi_dev->version >> 24, dw_spi_dev->version >> 16,
- dw_spi_dev->version >> 8, dw_spi_dev->version);
- /* detect the FIFO depth if not set by interface driver */
- if (!dw_spi_dev->fifo_len)
- {
- uint32_t fifo;
- for (fifo = 1; fifo < 256; fifo++)
- {
- writel(fifo, ®s->txftlr);
- if (fifo != readl(®s->txftlr))
- {
- break;
- }
- }
- dw_spi_dev->fifo_len = (fifo == 1) ? 0 : fifo;
- writel(0, ®s->txftlr);
- rt_kprintf("fifo length is %d\n", dw_spi_dev->fifo_len);
- }
- rt_uint32_t cr0, tmp = readl(®s->ctrl0);
- writel(0, ®s->ssienr);
- writel(0xffffffff, ®s->ctrl0);
- cr0 = readl(®s->ctrl0);
- writel(tmp, ®s->ctrl0);
- writel(1, ®s->ssienr);
- if (cr0 & DW_SPI_CTRLR0_DFS_MASK)
- {
- dw_spi_device.update_cr0 = update_control_reg_16;
- }
- else
- {
- dw_spi_device.update_cr0 = update_control_reg_32;
- }
- return 0;
- }
- int rt_hw_dw_spi_init(void)
- {
- rt_err_t ret = RT_EOK;
- struct dw_spi_regs *reg = NULL;
- /* set reg base */
- #ifdef RT_USING_SMART
- reg = (struct dw_spi_regs *)rt_ioremap((void *)DW_SPI_BASE_ADDR, sizeof(struct dw_spi_regs));
- #else
- reg = (struct dw_spi_regs *)DW_SPI_BASE_ADDR;
- #endif
- LOG_D("%s: reg base %p\n", __func__, reg);
- if (!reg)
- return -RT_ERROR;
- dw_spi_device.reg = reg;
- dw_spi_device.spi_bus.parent.user_data = &dw_spi_device;
- dw_spi_device.tmode = 0; /* Tx & Rx */
- dw_spi_device.bits_per_word = 8;
- /* init spi */
- dw_spi_hw_init(&dw_spi_device);
- /* Register SPI bus*/
- ret = rt_spi_bus_register(&dw_spi_device.spi_bus, dw_spi_device.device_name, &dw_drv_spi_ops);
- if (ret == RT_EOK)
- {
- static struct rt_spi_device spi_device0;
- rt_spi_bus_attach_device(&spi_device0, SPI0_BUS_DEVICE0_NAME, SPI0_BUS_NAME, (void *)&dw_spi_device);
- }
- return ret;
- }
- INIT_BOARD_EXPORT(rt_hw_dw_spi_init);
- #endif /* BSP_USING_DW_SPI */
|