diff --git a/bsp/cvitek/drivers/SConscript b/bsp/cvitek/drivers/SConscript index d67ffc5e39..91961af61a 100755 --- a/bsp/cvitek/drivers/SConscript +++ b/bsp/cvitek/drivers/SConscript @@ -31,6 +31,8 @@ if GetDepend('BSP_USING_WDT'): if GetDepend(['BSP_USING_SPI']): src += ['drv_spi.c'] + src += ['libraries/spi/dw_spi.c'] + CPPPATH += [cwd + r'/libraries/spi'] if GetDepend('BSP_USING_PWM'): src += ['drv_pwm.c'] diff --git a/bsp/cvitek/drivers/drv_spi.c b/bsp/cvitek/drivers/drv_spi.c index 3e88af06f6..e268fa89d0 100644 --- a/bsp/cvitek/drivers/drv_spi.c +++ b/bsp/cvitek/drivers/drv_spi.c @@ -1,217 +1,231 @@ /* - * Copyright (c) 2006-2023, RT-Thread Development Team + * Copyright (c) 2006-2024, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes - * 2024-03-28 qiujingbao first version + * 2024-03-28 qiujingbao first version + * 2024/06/08 flyingcys fix transmission failure */ +#include +#include +#include + +#include "board.h" #include "drv_spi.h" + #include "drv_pinmux.h" -#define DBG_TAG "drv.spi" -#define DBG_LVL DBG_INFO +#define DBG_LEVEL DBG_LOG #include +#define LOG_TAG "drv.spi" -static struct cv1800_spi cv1800_spi_obj[] = +struct _device_spi { -#ifdef BSP_USING_SPI - { - .spi_id = SPI2, - .device_name = "spi2", - .fifo_len = SPI_TXFTLR, - }, -#endif + struct rt_spi_bus spi_bus; + struct dw_spi dws; + char *device_name; }; -static struct spi_regs *get_spi_base(uint8_t spi_id) +static struct _device_spi _spi_obj[] = { - struct spi_regs *spi_base = NULL; - - switch (spi_id) +#ifdef BSP_USING_SPI0 { - case SPI0: - spi_base = (struct spi_regs *)SPI0_BASE; - break; - case SPI1: - spi_base = (struct spi_regs *)SPI1_BASE; - break; - case SPI2: - spi_base = (struct spi_regs *)SPI2_BASE; - break; - case SPI3: - spi_base = (struct spi_regs *)SPI3_BASE; - break; - } + .dws.regs = (void *)DW_SPI0_BASE, + .dws.irq = DW_SPI0_IRQn, + .dws.index = 0, + .device_name = "spi0", + }, +#endif /* BSP_USING_SPI0 */ +#ifdef BSP_USING_SPI1 + { + .dws.regs = (void *)DW_SPI1_BASE, + .dws.irq = DW_SPI1_IRQn, + .dws.index = 0, + .device_name = "spi1", + }, +#endif /* BSP_USING_SPI1 */ +#ifdef BSP_USING_SPI2 + { + .dws.regs = (void *)DW_SPI2_BASE, + .dws.irq = DW_SPI2_IRQn, + .dws.index = 0, + .device_name = "spi2", + }, +#endif /* BSP_USING_SPI2 */ +#ifdef BSP_USING_SPI3 + { + .dws.regs = (void *)DW_SPI3_BASE, + .dws.irq = DW_SPI3_IRQn, + .dws.index = 0, + .device_name = "spi3", + }, +#endif /* BSP_USING_SPI3 */ +}; - return spi_base; -} - -static rt_err_t drv_spi_configure(struct rt_spi_device *device, struct rt_spi_configuration *configuration) +static rt_err_t spi_configure(struct rt_spi_device *device, struct rt_spi_configuration *cfg) { - rt_err_t ret = RT_EOK; - struct cv1800_spi *spi_dev = RT_NULL; - uint32_t mode; - - spi_dev = (struct cv1800_spi *)(device->bus->parent.user_data); - - spi_dev->data_width = configuration->data_width; - - /* disable spi */ - spi_enable(spi_dev->reg, 0); - - /* clear irq */ - spi_clear_irq(spi_dev->reg, SPI_IRQ_MSAK); - - /* set clk */ - ret = spi_set_frequency(spi_dev->reg, configuration->max_hz); - if (ret) - return ret; - - /* set mode */ - ret = gen_spi_mode(configuration, &mode); - if (ret) - return ret; - - spi_set_mode(spi_dev->reg, mode); - - /* set cs */ - spi_enable_cs(spi_dev->reg, 0x1); - - spi_enable(spi_dev->reg, 0x1); - - mode = mmio_read_32((uintptr_t)&spi_dev->reg->spi_ctrl0); - LOG_D("mode: %x", mode); - mode = mmio_read_32((uintptr_t)&spi_dev->reg->spi_baudr); - LOG_D("spi_baudr: %x", mode); - - return ret; -} - -int hw_spi_recv(struct cv1800_spi *dev) { - uint32_t rever; - uint32_t tem; - int ret; - - rever = mmio_read_32((uintptr_t)&dev->reg->spi_rxflr); - ret = (int)rever; - - while (rever) - { - tem = mmio_read_32((uintptr_t)&dev->reg->spi_dr); - - if (dev->recv_buf < dev->recv_end) - { - if (dev->data_width == 8) - *(uint8_t *)(dev->recv_buf) = tem; - else - *(uint16_t *)(dev->recv_buf) = tem; - } - else - { - return 0; - } - - rever--; - dev->recv_buf += dev->data_width >> 3; - } - return ret; -} - -int hw_spi_send(struct cv1800_spi *dev) { - uint32_t txflr; - uint32_t max; - uint16_t value; - - txflr = mmio_read_32((uintptr_t)&dev->reg->spi_txflr); - max = dev->fifo_len - txflr; - - while (max) - { - if (dev->send_end - dev->send_buf) - { - if (dev->data_width == 8) - value = *(uint8_t *)(dev->send_buf); - else - value = *(uint16_t *)(dev->send_buf); - } - else - { - return 0; - } - - mmio_write_32((uintptr_t)&dev->reg->spi_dr, value); - dev->send_buf += dev->data_width >> 3; - max--; - } - - return 0; -} -static rt_ssize_t spixfer(struct rt_spi_device *device, struct rt_spi_message *message) { - int ret = 0; - struct cv1800_spi *spi_dev; - RT_ASSERT(device != RT_NULL); RT_ASSERT(device->bus != RT_NULL); - RT_ASSERT(message != RT_NULL); + RT_ASSERT(device->bus->parent.user_data != RT_NULL); + RT_ASSERT(cfg != RT_NULL); - spi_dev = (struct cv1800_spi *)(device->bus->parent.user_data); + rt_err_t ret = RT_EOK; + struct _device_spi *spi = (struct _device_spi *)device->bus->parent.user_data; + struct dw_spi *dws = &spi->dws; - if (message->send_buf != RT_NULL) + rt_uint8_t mode; + + LOG_D("spi_configure input"); + + /* set cs low when spi idle */ + writel(0, (void *)0x030001d0); + + if (cfg->mode & RT_SPI_SLAVE) { - spi_dev->send_buf = message->send_buf; - spi_dev->send_end = (void *)((uint8_t *)spi_dev->send_buf + message->length); + LOG_E("invalid mode: %d", cfg->mode); + return -RT_EINVAL; } - if (message->recv_buf != RT_NULL) + spi_reset_chip(dws); + spi_hw_init(dws); + spi_enable_chip(dws, 0); + + LOG_D("cfg->max_hz: %d", cfg->max_hz); + dw_spi_set_clock(dws, SPI_REF_CLK, cfg->max_hz); + + LOG_D("cfg->data_width: %d", cfg->data_width); + if (dw_spi_set_data_frame_len(dws, (uint32_t)cfg->data_width) < 0) { - spi_dev->recv_buf = message->recv_buf; - spi_dev->recv_end = (void *)((uint8_t *)spi_dev->recv_buf + message->length); + LOG_E("dw_spi_set_data_frame_len failed...\n"); + return -RT_ERROR; } - /* if user use their cs */ - if (message->cs_take && device->cs_pin != PIN_NONE) - rt_pin_write(device->cs_pin, PIN_LOW); - - if (message->send_buf) + LOG_D("cfg->mode: %08x", cfg->mode); + switch (cfg->mode & RT_SPI_MODE_3) { - while (spi_dev->send_buf != spi_dev->send_end) - { - hw_spi_send(spi_dev); - } + case RT_SPI_MODE_0: + mode = SPI_FORMAT_CPOL0_CPHA0; + break; - /* wait for complete */ - while (mmio_read_32((uintptr_t)&spi_dev->reg->spi_txflr)) {} + case RT_SPI_MODE_1: + mode = SPI_FORMAT_CPOL0_CPHA1; + break; - ret = message->length; + case RT_SPI_MODE_2: + mode = SPI_FORMAT_CPOL1_CPHA0; + break; + + case RT_SPI_MODE_3: + mode = SPI_FORMAT_CPOL1_CPHA1; + break; + + default: + LOG_E("spi configure mode error %x\n", cfg->mode); + break; } - if (message->recv_buf) - { - while (spi_dev->recv_buf != spi_dev->recv_end) - { - hw_spi_recv(spi_dev); - } + dw_spi_set_polarity_and_phase(dws, mode); - ret = message->length; - } + dw_spi_set_cs(dws, 1, 0); - if (message->cs_release && device->cs_pin != PIN_NONE) - rt_pin_write(device->cs_pin, PIN_HIGH); + spi_enable_chip(dws, 1); - return ret; + return RT_EOK; } -const static struct rt_spi_ops drv_spi_ops = +static rt_err_t dw_spi_transfer_one(struct dw_spi *dws, const void *tx_buf, void *rx_buf, uint32_t len, enum transfer_type tran_type) { - drv_spi_configure, - spixfer, + uint8_t imask = 0; + uint16_t txlevel = 0; + + dws->tx = NULL; + dws->tx_end = NULL; + dws->rx = NULL; + dws->rx_end = NULL; + + if (tx_buf != NULL) { + dws->tx = tx_buf; + dws->tx_end = dws->tx + len; + } + + if (rx_buf != NULL) { + dws->rx = rx_buf; + dws->rx_end = dws->rx + len; + } + + dws->rx_len = len / dws->n_bytes; + dws->tx_len = len / dws->n_bytes; + + spi_enable_chip(dws, 0); + + /* For poll mode just disable all interrupts */ + spi_mask_intr(dws, 0xff); + + /* set tran mode */ + set_tran_mode(dws); + + /* cs0 */ + dw_spi_set_cs(dws, true, 0); + + /* enable spi */ + spi_enable_chip(dws, 1); + + rt_hw_us_delay(10); + + if (tran_type == POLL_TRAN) + { + if (poll_transfer(dws) < 0) + return -RT_ERROR; + } + else + { + return -RT_ENOSYS; + } + + return RT_EOK; +} + +static rt_ssize_t spi_xfer(struct rt_spi_device *device, struct rt_spi_message *message) +{ + RT_ASSERT(device != RT_NULL); + RT_ASSERT(device->bus != RT_NULL); + RT_ASSERT(device->bus->parent.user_data != RT_NULL); + RT_ASSERT(message != RT_NULL); + + struct _device_spi *spi = (struct _device_spi *)device->bus->parent.user_data; + struct dw_spi *dws = &spi->dws; + int32_t ret = 0; + + if (message->send_buf && message->recv_buf) + { + ret = dw_spi_transfer_one(dws, message->send_buf, message->recv_buf, message->length, POLL_TRAN); + + } + else if (message->send_buf) + { + ret = dw_spi_transfer_one(dws, message->send_buf, RT_NULL, message->length, POLL_TRAN); + + } + else if (message->recv_buf) + { + ret = dw_spi_transfer_one(dws, RT_NULL, message->recv_buf, message->length, POLL_TRAN); + + } + + return message->length; +} + +static const struct rt_spi_ops _spi_ops = +{ + .configure = spi_configure, + .xfer = spi_xfer, }; #if defined(BOARD_TYPE_MILKV_DUO) || defined(BOARD_TYPE_MILKV_DUO_SPINOR) || defined(BOARD_TYPE_MILKV_DUO256M) || defined(BOARD_TYPE_MILKV_DUO256M_SPINOR) - +// For Duo / Duo 256m, only SPI2 are exported on board. #ifdef BSP_USING_SPI0 static const char *pinname_whitelist_spi0_sck[] = { NULL, @@ -283,54 +297,49 @@ static const char *pinname_whitelist_spi3_cs[] = { static void rt_hw_spi_pinmux_config() { #ifdef BSP_USING_SPI0 - pinmux_config(BSP_SPI0_SCK_PINNAME, SPI0_SCK, pinname_whitelist_spi0_sck); - pinmux_config(BSP_SPI0_SDO_PINNAME, SPI0_SDO, pinname_whitelist_spi0_sdo); - pinmux_config(BSP_SPI0_SDI_PINNAME, SPI0_SDI, pinname_whitelist_spi0_sdi); - pinmux_config(BSP_SPI0_CS_PINNAME, SPI0_CS_X, pinname_whitelist_spi0_cs); + pinmux_config(BSP_SPI0_SCK_PINNAME, SPI0_SCK, pinname_whitelist_spi0_sck); + pinmux_config(BSP_SPI0_SDO_PINNAME, SPI0_SDO, pinname_whitelist_spi0_sdo); + pinmux_config(BSP_SPI0_SDI_PINNAME, SPI0_SDI, pinname_whitelist_spi0_sdi); + pinmux_config(BSP_SPI0_CS_PINNAME, SPI0_CS_X, pinname_whitelist_spi0_cs); #endif /* BSP_USING_SPI0 */ #ifdef BSP_USING_SPI1 - pinmux_config(BSP_SPI1_SCK_PINNAME, SPI1_SCK, pinname_whitelist_spi1_sck); - pinmux_config(BSP_SPI1_SDO_PINNAME, SPI1_SDO, pinname_whitelist_spi1_sdo); - pinmux_config(BSP_SPI1_SDI_PINNAME, SPI1_SDI, pinname_whitelist_spi1_sdi); - pinmux_config(BSP_SPI1_CS_PINNAME, SPI1_CS_X, pinname_whitelist_spi1_cs); + pinmux_config(BSP_SPI1_SCK_PINNAME, SPI1_SCK, pinname_whitelist_spi1_sck); + pinmux_config(BSP_SPI1_SDO_PINNAME, SPI1_SDO, pinname_whitelist_spi1_sdo); + pinmux_config(BSP_SPI1_SDI_PINNAME, SPI1_SDI, pinname_whitelist_spi1_sdi); + pinmux_config(BSP_SPI1_CS_PINNAME, SPI1_CS_X, pinname_whitelist_spi1_cs); #endif /* BSP_USING_SPI1 */ #ifdef BSP_USING_SPI2 - pinmux_config(BSP_SPI2_SCK_PINNAME, SPI2_SCK, pinname_whitelist_spi2_sck); - pinmux_config(BSP_SPI2_SDO_PINNAME, SPI2_SDO, pinname_whitelist_spi2_sdo); - pinmux_config(BSP_SPI2_SDI_PINNAME, SPI2_SDI, pinname_whitelist_spi2_sdi); - pinmux_config(BSP_SPI2_CS_PINNAME, SPI2_CS_X, pinname_whitelist_spi2_cs); + pinmux_config(BSP_SPI2_SCK_PINNAME, SPI2_SCK, pinname_whitelist_spi2_sck); + pinmux_config(BSP_SPI2_SDO_PINNAME, SPI2_SDO, pinname_whitelist_spi2_sdo); + pinmux_config(BSP_SPI2_SDI_PINNAME, SPI2_SDI, pinname_whitelist_spi2_sdi); + pinmux_config(BSP_SPI2_CS_PINNAME, SPI2_CS_X, pinname_whitelist_spi2_cs); #endif /* BSP_USING_SPI2 */ #ifdef BSP_USING_SPI3 - pinmux_config(BSP_SPI3_SCK_PINNAME, SPI3_SCK, pinname_whitelist_spi3_sck); - pinmux_config(BSP_SPI3_SDO_PINNAME, SPI3_SDO, pinname_whitelist_spi3_sdo); - pinmux_config(BSP_SPI3_SDI_PINNAME, SPI3_SDI, pinname_whitelist_spi3_sdi); - pinmux_config(BSP_SPI3_CS_PINNAME, SPI3_CS_X, pinname_whitelist_spi3_cs); + pinmux_config(BSP_SPI3_SCK_PINNAME, SPI3_SCK, pinname_whitelist_spi3_sck); + pinmux_config(BSP_SPI3_SDO_PINNAME, SPI3_SDO, pinname_whitelist_spi3_sdo); + pinmux_config(BSP_SPI3_SDI_PINNAME, SPI3_SDI, pinname_whitelist_spi3_sdi); + pinmux_config(BSP_SPI3_CS_PINNAME, SPI3_CS_X, pinname_whitelist_spi3_cs); #endif /* BSP_USING_SPI3 */ } int rt_hw_spi_init(void) { rt_err_t ret = RT_EOK; - struct spi_regs *reg = NULL; + struct dw_spi *dws; rt_hw_spi_pinmux_config(); - for (rt_size_t i = 0; i < sizeof(cv1800_spi_obj) / sizeof(struct cv1800_spi); i++) { - /* set reg base addr */ - reg = get_spi_base(cv1800_spi_obj[i].spi_id); - if (!reg) - return -RT_ERROR; - - cv1800_spi_obj[i].reg = reg; - cv1800_spi_obj[i].spi_bus.parent.user_data = &cv1800_spi_obj[i]; - - /* register spix bus */ - ret = rt_spi_bus_register(&cv1800_spi_obj[i].spi_bus, cv1800_spi_obj[i].device_name, &drv_spi_ops); + for (rt_size_t i = 0; i < sizeof(_spi_obj) / sizeof(struct _device_spi); i++) + { + _spi_obj[i].spi_bus.parent.user_data = (void *)&_spi_obj[i]; + ret = rt_spi_bus_register(&_spi_obj[i].spi_bus, _spi_obj[i].device_name, &_spi_ops); } + RT_ASSERT(ret == RT_EOK); + return ret; } INIT_BOARD_EXPORT(rt_hw_spi_init); diff --git a/bsp/cvitek/drivers/drv_spi.h b/bsp/cvitek/drivers/drv_spi.h index 2e20b15b2e..9ee398371d 100644 --- a/bsp/cvitek/drivers/drv_spi.h +++ b/bsp/cvitek/drivers/drv_spi.h @@ -5,151 +5,24 @@ * * Change Logs: * Date Author Notes - * 2024-03-28 qiujingbao first version + * 2024-03-28 qiujingbao first version + * 2024/06/08 flyingcys fix transmission failure */ #ifndef __DRV_SPI_H__ #define __DRV_SPI_H__ -#include "rtdevice.h" -#include -#include - #include "mmio.h" -#include "pinctrl.h" +#include "dw_spi.h" -#define SPI0 0x0 -#define SPI1 0x1 -#define SPI2 0x2 -#define SPI3 0x3 +#define DW_SPI_REG_SIZE (0x10000UL) +#define DW_SPI0_BASE (0x04180000UL) +#define DW_SPI1_BASE (DW_SPI0_BASE + 1 * DW_SPI_REG_SIZE) +#define DW_SPI2_BASE (DW_SPI0_BASE + 2 * DW_SPI_REG_SIZE) +#define DW_SPI3_BASE (DW_SPI0_BASE + 3 * DW_SPI_REG_SIZE) -#define SPI0_BASE 0x04180000 -#define SPI1_BASE 0x04190000 -#define SPI2_BASE 0x041A0000 -#define SPI3_BASE 0x041B0000 - -#define SPI_IRQ_MSAK 0x3e -#define SPI_FREQUENCY 187500000 - -/* Transmit FiFO Threshold Level */ -#define SPI_TXFTLR 0xf - -#define SPI_CTRL0_DATA_FREAM_SHIFT 0 -#define SPI_CTRL0_FREAM_FORMAT_SHIFT 4 -#define SPI_CTRL0_CPHA_SHIFT 6 -#define SPI_CTRL0_CPOL_SHIFT 7 -#define SPI_CTRL0_TRANS_MODE 8 -#define SPI_CTRL0_LOOP_SHIFT 11 -#define SPI_CTRL0_CTRL_FREAM_SHIFT 12 - -struct cv1800_spi { - uint8_t spi_id; - char *device_name; - - uint8_t fifo_len; - uint8_t data_width; - - const void *send_buf; - void *recv_buf; - - const void *send_end; - void *recv_end; - - struct rt_spi_bus spi_bus; - struct spi_regs *reg; -}; - -struct spi_regs { - uint32_t spi_ctrl0; // 0x00 - uint32_t spi_ctrl1; // 0x04 - uint32_t spi_ssienr; // 0x08 - uint32_t spi_mwcr; // 0x0c - uint32_t spi_ser; // 0x10 - uint32_t spi_baudr; // 0x14 - uint32_t spi_txftlr; // 0x18 - uint32_t spi_rxftlr; // 0x1c - uint32_t spi_txflr; // 0x20 - uint32_t spi_rxflr; // 0x24 - uint32_t spi_sr; // 0x28 - uint32_t spi_imr; // 0x2c - uint32_t spi_isr; // 0x30 - uint32_t spi_risr; // 0x34 - uint32_t spi_txoicr; // 0x38 - uint32_t spi_rxoicr; // 0x3c - uint32_t spi_rxuicr; // 0x40 - uint32_t spi_msticr; // 0x44 - uint32_t spi_icr; // 0x48 - uint32_t spi_dmacr; // 0x4c - uint32_t spi_dmatdlr; // 0x50 - uint32_t spi_dmardlr; // 0x54 - uint32_t spi_idr; // 0x58 - uint32_t spi_version; // 0x5c - uint32_t spi_dr; // 0x60 - uint32_t spi_rx_sample_dly; // 0xf0 - uint32_t spi_cs_override; // 0xf4 -}; - -uint32_t gen_spi_mode(struct rt_spi_configuration *cfg, uint32_t *mode) -{ - uint32_t value = 0; - - if (cfg->data_width != 8 && cfg->data_width != 16) - return -1; - - value |= (cfg->data_width - 1) >> SPI_CTRL0_DATA_FREAM_SHIFT; - value |= cfg->mode >> SPI_CTRL0_CPHA_SHIFT; - - *mode = value; - return 0; -} - -/* set spi mode */ -static inline void spi_set_mode(struct spi_regs *reg, uint32_t mode) -{ - mmio_write_32((uintptr_t)®->spi_ctrl0, mode); -} - -/* clear irq */ -static inline void spi_clear_irq(struct spi_regs *reg, uint32_t mode) -{ - mmio_write_32((uintptr_t)®->spi_imr, mode); -} - -static inline void spi_enable_cs(struct spi_regs *reg, uint32_t enable) -{ - if (enable) - enable = 0x1; - else - enable = 0x0; - - mmio_write_32((uintptr_t)®->spi_ser, enable); -} - -/* set spi frequency*/ -static inline rt_err_t spi_set_frequency(struct spi_regs *reg, uint32_t speed) -{ - uint16_t value; - - /* The value of the BAUDR register must be an even number between 2-65534 */ - value = SPI_FREQUENCY / speed; - if (value % 2 != 0) - value++; - - if (value < 4 || value > 65534) - value = 4; - - mmio_write_32((uintptr_t)®->spi_baudr, value); - - return RT_EOK; -} - -static inline void spi_enable(struct spi_regs *reg, uint32_t enable) -{ - if (enable) - enable = 0x1; - else - enable = 0x0; - - mmio_write_32((uintptr_t)®->spi_ssienr, enable); -} +#define DW_SPI0_IRQn 54 +#define DW_SPI1_IRQn 55 +#define DW_SPI2_IRQn 56 +#define DW_SPI3_IRQn 56 #endif /* __DRV_SPI_H__ */ diff --git a/bsp/cvitek/drivers/libraries/spi/dw_spi.c b/bsp/cvitek/drivers/libraries/spi/dw_spi.c new file mode 100644 index 0000000000..1c098e15ba --- /dev/null +++ b/bsp/cvitek/drivers/libraries/spi/dw_spi.c @@ -0,0 +1,375 @@ +/* + * Copyright (C) Cvitek Co., Ltd. 2019-2020. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include + +#include "mmio.h" +#include "dw_spi.h" + +#include + +#ifdef SPI_DEBUG +#define SP_DEBUG_LOG printf +#else +#define SP_DEBUG_LOG +#endif + +/* Restart the controller, disable all interrupts, clean rx fifo */ +void spi_hw_init(struct dw_spi *dws) +{ + /* + * Try to detect the FIFO depth if not set by interface driver, + * the depth could be from 2 to 256 from HW spec + */ + if (!dws->fifo_len) { + uint32_t fifo; + + for (fifo = 1; fifo < 256; fifo++) { + dw_writel(dws, CVI_DW_SPI_TXFTLR, fifo); + if (fifo != dw_readl(dws, CVI_DW_SPI_TXFTLR)) + break; + } + dw_writel(dws, CVI_DW_SPI_TXFTLR, 0); + + dws->fifo_len = (fifo == 1) ? 0 : fifo; + + SP_DEBUG_LOG("Detected FIFO size: %u bytes\n", dws->fifo_len); + } +} + +uint32_t min3(uint32_t a, uint32_t b, uint32_t c) +{ + uint32_t tmp; + + tmp = (a < b) ? a : b; + return (tmp < c) ? tmp : c; +} + +static inline void cpu_relax(void) +{ + //asm volatile("" ::: "memory"); +} + +static inline uint32_t tx_max(struct dw_spi *dws) +{ + uint32_t tx_left, tx_room, rxtx_gap, temp; + cpu_relax(); + tx_left = dws->tx_len; + tx_room = dws->fifo_len - dw_readl(dws, CVI_DW_SPI_TXFLR); + + /* + * Another concern is about the tx/rx mismatch, we + * though to use (dws->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. + */ + + SP_DEBUG_LOG("tx left: %#x, tx room: %#x\n", tx_left, tx_room); + if (dws->rx != NULL && dws->tx != NULL) { + cpu_relax(); + rxtx_gap = dws->fifo_len - (dws->rx_len - dws->tx_len); + temp = min3(tx_left, tx_room, (uint32_t)(rxtx_gap)); + } else { + temp = tx_left < tx_room ? tx_left : tx_room; + } + + SP_DEBUG_LOG("temp: %#x\n", temp); + return temp; +} + +void dw_writer(struct dw_spi *dws) +{ + uint32_t max; + uint16_t txw = 0; + + max = tx_max(dws); + SP_DEBUG_LOG("max: %#x \n", max); + while (max--) { + if (dws->tx) { + if (dws->n_bytes == 1) + txw = *(uint8_t *)(dws->tx); + else + txw = *(uint16_t *)(dws->tx); + } + dw_writel(dws, CVI_DW_SPI_DR, txw); + dws->tx += dws->n_bytes; + --dws->tx_len; + } +} + +static inline uint32_t rx_max(struct dw_spi *dws) +{ + uint32_t temp; + uint32_t rx_left = dws->rx_len; + uint32_t data_in_fifo = dw_readl(dws, CVI_DW_SPI_RXFLR); + + temp = (rx_left < data_in_fifo ? rx_left : data_in_fifo); + SP_DEBUG_LOG("data_in_fifo:%u temp: %u\n", data_in_fifo, temp); + return temp; +} + +int dw_spi_check_status(struct dw_spi *dws, bool raw) +{ + uint32_t irq_status; + int ret = 0; + + if (raw) + irq_status = dw_readl(dws, CVI_DW_SPI_RISR); + else + irq_status = dw_readl(dws, CVI_DW_SPI_ISR); + + if (irq_status & CVI_SPI_INT_RXOI) { + SP_DEBUG_LOG("RX FIFO overflow detected\n"); + ret = -1; + } + + if (irq_status & CVI_SPI_INT_RXUI) { + SP_DEBUG_LOG("RX FIFO underflow detected\n"); + ret = -1; + } + + if (irq_status & CVI_SPI_INT_TXOI) { + SP_DEBUG_LOG("TX FIFO overflow detected\n"); + ret = -1; + } + + if (ret) + spi_reset_chip(dws); + + return ret; +} + +void dw_reader(struct dw_spi *dws) +{ + uint32_t max; + uint16_t rxw; + + max = rx_max(dws); + SP_DEBUG_LOG("max: %#x \n", max); + while (max--) { + rxw = dw_readl(dws, CVI_DW_SPI_DR); + if (dws->rx) { + if (dws->n_bytes == 1) + *(uint8_t *)(dws->rx) = rxw; + else + *(uint16_t *)(dws->rx) = rxw; + dws->rx += dws->n_bytes; + } + --dws->rx_len; + } +} + +int spi_delay_to_ns(struct spi_delay *_delay, struct dw_spi *dws) +{ + uint32_t delay = _delay->value; + uint32_t unit = _delay->unit; + uint32_t hz; + + if (!delay) + return 0; + + switch (unit) { + case SPI_DELAY_UNIT_USECS: + delay *= 1000; + break; + case SPI_DELAY_UNIT_NSECS: /* nothing to do here */ + break; + case SPI_DELAY_UNIT_SCK: + /* clock cycles need to be obtained from spi_transfer */ + if (!dws) + return -1; + /* if there is no effective speed know, then approximate + * by underestimating with half the requested hz + */ + hz = dws->speed_hz / 2; + if (!hz) + return -1; + delay *= DIV_ROUND_UP(1000000000, hz); + break; + default: + return -EINVAL; + } + + return delay; +} + +static void _spi_transfer_delay_ns(uint32_t ns) +{ + if (!ns) + return; + if (ns <= 1000) { + rt_hw_us_delay(1); + } else { + uint32_t us = DIV_ROUND_UP(ns, 1000); + rt_hw_us_delay(us); + } +} + +int spi_delay_exec(struct spi_delay *_delay, struct dw_spi *dws) +{ + int delay; + + if (!_delay) + return -1; + + delay = spi_delay_to_ns(_delay, dws); + if (delay < 0) + return delay; + + _spi_transfer_delay_ns(delay); + + return 0; +} + +int poll_transfer(struct dw_spi *dws) +{ + struct spi_delay delay; + uint16_t nbits; + delay.unit = SPI_DELAY_UNIT_SCK; + nbits = dws->n_bytes * BITS_PER_BYTE; + int ret = 0; + + do + { + dw_writer(dws); + cpu_relax(); + + delay.value = nbits * (dws->rx_len - dws->tx_len); + spi_delay_exec(&delay, dws); + dw_reader(dws); + cpu_relax(); + ret = dw_spi_check_status(dws, true); + if (ret) + return ret; + } while (dws->rx_len && dws->tx_len); + + return 0; +} + +void set_tran_mode(struct dw_spi *dws) +{ + uint32_t reg = dw_readl(dws, CVI_DW_SPI_CTRLR0); + uint8_t tmode; + + if (dws->rx && dws->tx) { + tmode = CVI_SPI_TMOD_TR; + } else if (dws->rx) { + tmode = CVI_SPI_TMOD_RO; + } else { + tmode = CVI_SPI_TMOD_TO; + } + reg &= ~CVI_SPI_TMOD_MASK; + reg |= (tmode << CVI_SPI_TMOD_OFFSET); + + dw_writel(dws, CVI_DW_SPI_CTRLR0, reg); +} + +void dw_spi_set_controller_mode(struct dw_spi *dws, uint8_t enable_master) +{ + /* do not support to switch controller mode, it is default master mode */ +} + +void dw_spi_set_cs(struct dw_spi *dws, bool enable, uint32_t index) +{ + uint32_t reg = dw_readl(dws, CVI_DW_SPI_SER); + + if (enable) + dw_writel(dws, CVI_DW_SPI_SER, reg | BIT(index)); + else + dw_writel(dws, CVI_DW_SPI_SER, reg & ~BIT(index)); +} + +void dw_spi_set_polarity_and_phase(struct dw_spi *dws, uint8_t format) +{ + uint32_t reg = dw_readl(dws, CVI_DW_SPI_CTRLR0); + reg &= ~(0x3 << 6); + + switch (format) { + case SPI_FORMAT_CPOL0_CPHA0: + reg |= (SPI_MODE_0 << 6); + break; + + case SPI_FORMAT_CPOL0_CPHA1: + reg |= (SPI_MODE_1 << 6); + break; + + case SPI_FORMAT_CPOL1_CPHA0: + reg |= (SPI_MODE_2 << 6); + break; + + case SPI_FORMAT_CPOL1_CPHA1: + reg |= (SPI_MODE_3 << 6); + break; + + default: + reg = dw_readl(dws, CVI_DW_SPI_CTRLR0); + break; + } + SP_DEBUG_LOG("set phase and polarity: %#x\n", reg); + dw_writel(dws, CVI_DW_SPI_CTRLR0, reg); +} + +uint32_t dw_spi_set_clock(struct dw_spi *dws, uint32_t clock_in, uint32_t clock_out) +{ + uint16_t div; + + div = (DIV_ROUND_UP(clock_in, clock_out) + 1) & 0xfffe; + dws->speed_hz = clock_in / div; + SP_DEBUG_LOG("clk div value is: %u, hz:%u\n", div, dws->speed_hz); + spi_set_clk(dws, div); + return dws->speed_hz; +} + +int dw_spi_set_data_frame_len(struct dw_spi *dws, uint32_t size) +{ + uint32_t temp = dw_readl(dws, CVI_DW_SPI_CTRLR0); + temp &= ~0xf; + + if (size == 8) { + dws->n_bytes = 1; + } else if (size == 16) { + dws->n_bytes = 2; + } else { + SP_DEBUG_LOG("do not support %u bit data!\n", size); + return -1; + } + temp |= (size - 1); + dw_writel(dws, CVI_DW_SPI_CTRLR0, temp); + SP_DEBUG_LOG("set data frame len: %#x\n", temp); + return 0; +} + +void dw_spi_show_regs(struct dw_spi *dws) +{ + SP_DEBUG_LOG("CTRLR0: \t0x%08x\n", dw_readl(dws, CVI_DW_SPI_CTRLR0)); + SP_DEBUG_LOG("CTRLR1: \t0x%08x\n", dw_readl(dws, CVI_DW_SPI_CTRLR1)); + SP_DEBUG_LOG("SSIENR: \t0x%08x\n", dw_readl(dws, CVI_DW_SPI_SSIENR)); + SP_DEBUG_LOG("SER: \t0x%08x\n", dw_readl(dws, CVI_DW_SPI_SER)); + SP_DEBUG_LOG("BAUDR: \t0x%08x\n", dw_readl(dws, CVI_DW_SPI_BAUDR)); + SP_DEBUG_LOG("TXFTLR: \t0x%08x\n", dw_readl(dws, CVI_DW_SPI_TXFTLR)); + SP_DEBUG_LOG("RXFTLR: \t0x%08x\n", dw_readl(dws, CVI_DW_SPI_RXFTLR)); + SP_DEBUG_LOG("TXFLR: \t0x%08x\n", dw_readl(dws, CVI_DW_SPI_TXFLR)); + SP_DEBUG_LOG("RXFLR: \t0x%08x\n", dw_readl(dws, CVI_DW_SPI_RXFLR)); + SP_DEBUG_LOG("SR: \t0x%08x\n", dw_readl(dws, CVI_DW_SPI_SR)); + SP_DEBUG_LOG("IMR: \t0x%08x\n", dw_readl(dws, CVI_DW_SPI_IMR)); + SP_DEBUG_LOG("ISR: \t0x%08x\n", dw_readl(dws, CVI_DW_SPI_ISR)); + SP_DEBUG_LOG("DMACR: \t0x%08x\n", dw_readl(dws, CVI_DW_SPI_DMACR)); + SP_DEBUG_LOG("DMATDLR: \t0x%08x\n", dw_readl(dws, CVI_DW_SPI_DMATDLR)); + SP_DEBUG_LOG("DMARDLR: \t0x%08x\n", dw_readl(dws, CVI_DW_SPI_DMARDLR)); +} diff --git a/bsp/cvitek/drivers/libraries/spi/dw_spi.h b/bsp/cvitek/drivers/libraries/spi/dw_spi.h new file mode 100644 index 0000000000..b5b6e6bc60 --- /dev/null +++ b/bsp/cvitek/drivers/libraries/spi/dw_spi.h @@ -0,0 +1,246 @@ +/* + * Copyright (C) Cvitek Co., Ltd. 2019-2020. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef __DW_SPI_HEADER_H__ +#define __DW_SPI_HEADER_H__ + +#include "stdint.h" +#include "stdbool.h" + +#define SPI_REGBASE 0x04180000 +#define SPI_REF_CLK 187500000 +#define MAX_SPI_NUM 4 + +#define CVI_DW_SPI_CTRLR0 0x00 +#define CVI_DW_SPI_CTRLR1 0x04 +#define CVI_DW_SPI_SSIENR 0x08 +#define CVI_DW_SPI_MWCR 0x0c +#define CVI_DW_SPI_SER 0x10 +#define CVI_DW_SPI_BAUDR 0x14 +#define CVI_DW_SPI_TXFTLR 0x18 +#define CVI_DW_SPI_RXFTLR 0x1c +#define CVI_DW_SPI_TXFLR 0x20 +#define CVI_DW_SPI_RXFLR 0x24 +#define CVI_DW_SPI_SR 0x28 +#define CVI_DW_SPI_IMR 0x2c +#define CVI_DW_SPI_ISR 0x30 +#define CVI_DW_SPI_RISR 0x34 +#define CVI_DW_SPI_TXOICR 0x38 +#define CVI_DW_SPI_RXOICR 0x3c +#define CVI_DW_SPI_RXUICR 0x40 +#define CVI_DW_SPI_MSTICR 0x44 +#define CVI_DW_SPI_ICR 0x48 +#define CVI_DW_SPI_DMACR 0x4c +#define CVI_DW_SPI_DMATDLR 0x50 +#define CVI_DW_SPI_DMARDLR 0x54 +#define CVI_DW_SPI_IDR 0x58 +#define CVI_DW_SPI_VERSION 0x5c +#define CVI_DW_SPI_DR 0x60 + +/* Bit fields in CTRLR0 */ +#define CVI_SPI_DFS_OFFSET 0 + +#define CVI_SPI_FRF_OFFSET 4 +#define CVI_SPI_FRF_SPI 0x0 +#define CVI_SPI_FRF_SSP 0x1 +#define CVI_SPI_FRF_MICROWIRE 0x2 +#define CVI_SPI_FRF_RESV 0x3 + +#define CVI_SPI_MODE_OFFSET 6 +#define CVI_SPI_SCPH_OFFSET 6 +#define CVI_SPI_SCOL_OFFSET 7 + +#define CVI_SPI_TMOD_OFFSET 8 +#define CVI_SPI_TMOD_MASK (0x3 << CVI_SPI_TMOD_OFFSET) +#define CVI_SPI_TMOD_TR 0x0 /* xmit & recv */ +#define CVI_SPI_TMOD_TO 0x1 /* xmit only */ +#define CVI_SPI_TMOD_RO 0x2 /* recv only */ +#define CVI_SPI_TMOD_EPROMREAD 0x3 /* eeprom read mode */ + +#define CVI_SPI_SLVOE_OFFSET 10 +#define CVI_SPI_SRL_OFFSET 11 +#define CVI_SPI_CFS_OFFSET 12 + +/* Bit fields in SR, 7 bits */ +#define CVI_SR_MASK 0x7f +#define CVI_SR_BUSY (1 << 0) +#define CVI_SR_TF_NOT_FULL (1 << 1) +#define CVI_SR_TF_EMPT (1 << 2) +#define CVI_SR_RF_NOT_EMPT (1 << 3) +#define CVI_SR_RF_FULL (1 << 4) +#define CVI_SR_TX_ERR (1 << 5) +#define SR_DCOL (1 << 6) + +/* Bit fields in ISR, IMR, RISR, 7 bits */ +#define CVI_SPI_INT_TXEI (1 << 0) +#define CVI_SPI_INT_TXOI (1 << 1) +#define CVI_SPI_INT_RXUI (1 << 2) +#define CVI_SPI_INT_RXOI (1 << 3) +#define CVI_SPI_INT_RXFI (1 << 4) +#define CVI_SPI_INT_MSTI (1 << 5) + +/* Bit fields in DMACR */ +#define CVI_SPI_DMA_RDMAE (1 << 0) +#define CVI_SPI_DMA_TDMAE (1 << 1) + +/* TX RX interrupt level threshold, max can be 256 */ +#define CVI_SPI_INT_THRESHOLD 32 +#define BITS_PER_BYTE 8 +#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) + +struct dw_spi { + void *regs; + int irq; + int index; + uint32_t fifo_len; /* depth of the FIFO buffer */ + uint16_t num_cs; /* supported slave numbers */ + uint32_t speed_hz; + /* Current message transfer state info */ + size_t len; + const void *tx; + const void *tx_end; + void *rx; + void *rx_end; + uint32_t rx_len; + uint32_t tx_len; + uint8_t n_bytes; /* current is a 1/2 bytes op */ + uint32_t dma_width; + int (*transfer_handler)(struct dw_spi *dws); + + /* Bus interface info */ + void *priv; +}; + +struct spi_delay { +#define SPI_DELAY_UNIT_USECS 0 +#define SPI_DELAY_UNIT_NSECS 1 +#define SPI_DELAY_UNIT_SCK 2 + uint16_t value; + uint8_t unit; +}; + +#define SPI_CPHA 0x01 +#define SPI_CPOL 0x02 + +#define SPI_MODE_0 (0|0) +#define SPI_MODE_1 (0|SPI_CPHA) +#define SPI_MODE_2 (SPI_CPOL|0) +#define SPI_MODE_3 (SPI_CPOL|SPI_CPHA) + +enum transfer_type { + POLL_TRAN = 0, + IRQ_TRAN, + DMA_TRAN, +}; + +enum dw_ssi_type { + SSI_MOTO_SPI = 0, + SSI_TI_SSP, + SSI_NS_MICROWIRE, +}; + +#define SPI_FORMAT_CPOL0_CPHA0 0 +#define SPI_FORMAT_CPOL0_CPHA1 1 +#define SPI_FORMAT_CPOL1_CPHA0 2 +#define SPI_FORMAT_CPOL1_CPHA1 3 + +#ifndef BIT +#define BIT(_n) ( 1 << (_n)) +#endif + +static void dw_writel(struct dw_spi *dws, uint32_t off, uint32_t val) +{ + writel(val, (dws->regs + off)); +} + +static uint32_t dw_readl(struct dw_spi *dws, uint32_t off) +{ + return readl(dws->regs + off); +} + +static inline void spi_enable_chip(struct dw_spi *dws, int enable) +{ + dw_writel(dws, CVI_DW_SPI_SSIENR, (enable ? 1 : 0)); +} + +static inline void spi_set_clk(struct dw_spi *dws, uint16_t div) +{ + dw_writel(dws, CVI_DW_SPI_BAUDR, div); +} + +/* Disable IRQ bits */ +static inline void spi_mask_intr(struct dw_spi *dws, uint32_t mask) +{ + uint32_t new_mask; + + new_mask = dw_readl(dws, CVI_DW_SPI_IMR) & ~mask; + dw_writel(dws, CVI_DW_SPI_IMR, new_mask); +} + +static inline uint32_t spi_get_status(struct dw_spi *dws) +{ + return dw_readl(dws, CVI_DW_SPI_SR); +} + +/* Enable IRQ bits */ +static inline void spi_umask_intr(struct dw_spi *dws, uint32_t mask) +{ + uint32_t new_mask; + + new_mask = dw_readl(dws, CVI_DW_SPI_IMR) | mask; + dw_writel(dws, CVI_DW_SPI_IMR, new_mask); +} + +static inline void spi_reset_chip(struct dw_spi *dws) +{ + spi_enable_chip(dws, 0); + spi_mask_intr(dws, 0xff); + dw_readl(dws, CVI_DW_SPI_ICR); + dw_writel(dws, CVI_DW_SPI_SER, 0); + spi_enable_chip(dws, 1); +} + +static inline void spi_enable_dma(struct dw_spi *dws, uint8_t is_tx, uint8_t op) +{ + /* 1: TDMAE, 0: RDMAE */ + uint32_t val = dw_readl(dws, CVI_DW_SPI_DMACR); + + if (op) + val |= 1 << (!!is_tx); + else + val &= ~(1 << (!!is_tx)); + + dw_writel(dws, CVI_DW_SPI_DMACR, val); +} + +static inline void spi_shutdown_chip(struct dw_spi *dws) +{ + spi_enable_chip(dws, 0); + spi_set_clk(dws, 0); +} + +void spi_hw_init(struct dw_spi *dws); +void dw_spi_set_controller_mode(struct dw_spi *dws, uint8_t enable_master); +void dw_spi_set_polarity_and_phase(struct dw_spi *dws, uint8_t format); +uint32_t dw_spi_set_clock(struct dw_spi *dws, uint32_t clock_in, uint32_t clock_out); +int dw_spi_set_data_frame_len(struct dw_spi *dws, uint32_t size); +void dw_spi_set_cs(struct dw_spi *dws, bool enable, uint32_t index); +void dw_reader(struct dw_spi *dws); +void dw_writer(struct dw_spi *dws); +void set_tran_mode(struct dw_spi *dws); +void dw_spi_show_regs(struct dw_spi *dws); +int poll_transfer(struct dw_spi *dws); +int dw_spi_check_status(struct dw_spi *dws, bool raw); +#endif