bsp: cvitek: re-wrote spi driver

Rewrote the spi driver.

Reuse the driver code from https://github.com/sophgo/cvi_alios_open,
which is Apache 2.0 licenced.

Signed-off-by: flyingcys <flyingcys@163.com>
Reviewed-by: Chen Wang <unicorn_wang@outlook.com>
This commit is contained in:
flyingcys 2024-06-02 22:09:11 +08:00 committed by Chen Wang
parent 1d78d11567
commit 1ef2afbc93
5 changed files with 842 additions and 337 deletions

View File

@ -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']

View File

@ -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 <rtthread.h>
#include <rthw.h>
#include <rtdevice.h>
#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 <rtdbg.h>
#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);

View File

@ -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 <rthw.h>
#include <rtthread.h>
#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)&reg->spi_ctrl0, mode);
}
/* clear irq */
static inline void spi_clear_irq(struct spi_regs *reg, uint32_t mode)
{
mmio_write_32((uintptr_t)&reg->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)&reg->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)&reg->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)&reg->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__ */

View File

@ -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 <errno.h>
#include <string.h>
#include <stdio.h>
#include "mmio.h"
#include "dw_spi.h"
#include <rthw.h>
#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));
}

View File

@ -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