CV1800B: Added CVITEK-SPIF driver.

Signed-off-by: Yilin Sun <imi415@imi.moe>
This commit is contained in:
Yilin Sun 2023-09-10 21:26:40 +08:00
parent 636e87e455
commit a63830773f
Signed by: imi415
GPG Key ID: 17F01E106F9F5E0A
6 changed files with 550 additions and 3 deletions

View File

@ -34,6 +34,31 @@
i2c1_pins: i2c1 {
pinmux = <PINMUX(PAD_MIPIRX0N, IIC1_SCL)>, <PINMUX(PAD_MIPIRX1P, IIC1_SDA)>;
};
spif_pins: spif {
pinmux = <PINMUX(SPINOR_SCK, SPINOR_SCK)>, <PINMUX(SPINOR_MOSI, SPINOR_MOSI)>,
<PINMUX(SPINOR_MISO, SPINOR_MISO)>, <PINMUX(SPINOR_CS_X, SPINOR_CS_X)>,
<PINMUX(SPINOR_HOLD_X, SPINOR_HOLD_X)>, <PINMUX(SPINOR_WP_X, SPINOR_WP_X)>;
};
};
&spif {
#address-cells = <1>;
#size-cells = <0>;
pinctrl-0 = <&spif_pins>;
pinctrl-names = "default";
spi-max-frequency = <30000000>;
status = "okay";
nor-flash@0 {
#address-cells = <1>;
#size-cells = <1>;
compatible = "jedec,spi-nor";
reg=<0>;
spi-tx-bus-width = <4>;
spi-rx-bus-width = <4>;
spi-max-frequency = <30000000>;
};
};
&uart0 {

View File

@ -45,6 +45,13 @@
#clock-cells = <0>;
};
hsp_clk: hs-peri-clock {
compatible = "fixed-clock";
clock-output-names = "hsp_clk";
clock-frequency = <150000000>;
#clock-cells = <0>;
};
apb_clk: apb-clk-clock {
compatible = "fixed-clock";
clock-output-names = "apb_clk";
@ -81,6 +88,14 @@
pins = <75>;
};
spif: spi@10000000 {
compatible = "cvitek,cv1800b-spif", "cvitek,cvitek-spif";
reg = <0x0 0x10000000 0x0 0x10000000>;
interrupts = <95 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&hsp_clk>;
status = "disabled";
};
uart0: serial@04140000 {
compatible = "cvitek,cv1800b-uart", "snps,dw-apb-uart";
reg = <0x0 0x04140000 0x0 0x1000>;

View File

@ -6,6 +6,7 @@
#include <common.h>
#include <clk.h>
#include <dm.h>
#include <dm/device_compat.h>
#include <dm/pinctrl.h>
#include <dt-bindings/pinctrl/cv1800b-pinctrl.h>
#include <mapmem.h>
@ -34,7 +35,7 @@ static int cv1800b_pc_get_pins_count(struct udevice *dev)
{
struct cv1800b_pc_priv *priv = dev_get_priv(dev);
debug("%s: pin count: %d", __func__, priv->num_pins);
dev_dbg(dev, "pin count: %d", priv->num_pins);
return priv->num_pins;
}
@ -48,7 +49,7 @@ static int cv1800b_pc_pinmux_set(struct udevice *dev, u32 pinmux_group)
priv->pinmux->ctrl[pin] = (priv->pinmux->ctrl[pin] & ~(CV1800B_PC_FUNC)) | func;
debug("%s: pin %d, func: %d\n", __func__, pin, func);
dev_dbg(dev, "pin %d, func: %d\n", pin, func);
return 0;
}
@ -76,7 +77,7 @@ static int cv1800b_pc_probe(struct udevice *dev)
if(dev_read_u32(dev, "pins", &priv->num_pins) < 0)
return -EINVAL;
debug("%s: pinctrl@%p, %d pins\n", __func__, priv->pinmux, priv->num_pins);
dev_dbg(dev, "pinctrl@%p, %d pins\n", priv->pinmux, priv->num_pins);
return 0;
}

View File

@ -168,6 +168,12 @@ config CF_SPI
Enable the ColdFire SPI driver. This driver can be used on
some m68k SoCs.
config CVITEK_SPIF
bool "Cvitek SPIF controller driver"
help
Enable the Cvitek SPIF controller driver, which (almost always)
connects to an SPI NOR flash device.
config DAVINCI_SPI
bool "Davinci & Keystone SPI driver"
depends on ARCH_DAVINCI || ARCH_KEYSTONE

View File

@ -30,6 +30,7 @@ obj-$(CONFIG_BCM63XX_SPI) += bcm63xx_spi.o
obj-$(CONFIG_BCMSTB_SPI) += bcmstb_spi.o
obj-$(CONFIG_CF_SPI) += cf_spi.o
obj-$(CONFIG_CORTINA_SFLASH) += ca_sflash.o
obj-$(CONFIG_CVITEK_SPIF) += cvitek-spif.o
obj-$(CONFIG_DAVINCI_SPI) += davinci_spi.o
obj-$(CONFIG_DESIGNWARE_SPI) += designware_spi.o
obj-$(CONFIG_EXYNOS_SPI) += exynos_spi.o

499
drivers/spi/cvitek-spif.c Normal file
View File

@ -0,0 +1,499 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2023 Yilin Sun <imi415@imi.moe>
*/
#include <common.h>
#include <clk.h>
#include <dm.h>
#include <dm/device_compat.h>
#include <spi.h>
#include <spi-mem.h>
#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/iopoll.h>
#include <linux/ioport.h>
#include <linux/sizes.h>
#define CVI_SPIF_BUS_WIDTH_1 0x00
#define CVI_SPIF_BUS_WIDTH_2 0x01
#define CVI_SPIF_BUS_WIDTH_4 0x02
#define CVI_SPIF_FIFO_DEPTH 8U
/**
* SPIF SPI_CTRL register
*
*/
#define CVI_SPIF_SPI_CTRL_SCK_DIV_SHIFT 0
#define CVI_SPIF_SPI_CTRL_SCK_DIV_MASK GENMASK(10, 0) /* Clock divider, assume F/(x+1) */
#define CVI_SPIF_SPI_CTRL_CPHA_SHIFT 12
#define CVI_SPIF_SPI_CTRL_CPHA_MASK BIT(12) /* Clock phase */
#define CVI_SPIF_SPI_CTRL_CPOL_SHIFT 13
#define CVI_SPIF_SPI_CTRL_CPOL_MASK BIT(13) /* Clock polarity */
#define CVI_SPIF_SPI_CTRL_HOLD_OL_MASK BIT(14) /* HOLD output low?*/
#define CVI_SPIF_SPI_CTRL_WP_OL_MASK BIT(15) /* WP output low? */
#define CVI_SPIF_SPI_CTRL_FRAME_LEN_MASK BIT(16) /* Frame length */
#define CVI_SPIF_SPI_CTRL_LSB_FIRST_MASK BIT(20) /* LSB first SPI */
#define CVI_SPIF_SPI_CTRL_SRST_MASK BIT(21) /* Software Reset? */
/**
* SPIF CE_CTRL register
*
*/
#define CVI_SPIF_CE_CTRL_CEMANUAL_MASK BIT(0) /* Manual CE control value */
#define CVI_SPIF_CE_CTRL_CEMANUAL_EN_MASK BIT(1) /* Manual CE control enable */
/**
* SPIF DLY_CTRL register
*
*/
#define CVI_SPIF_DLY_CTRL_CET_MASK GENMASK(9, 8)
#define CVI_SPIF_DLY_CTRL_NEG_SAMPLE_MASK BIT(14)
/**
* SPIF DMMR register
*
*/
#define CVI_SPIF_DMMR_EN_MASK BIT(0) /* Direct memory-mapped mode enable */
/**
* SPIF TRANS_CSR register
*
*/
#define CVI_SPIF_TRANS_CSR_MODE_SHIFT 0
#define CVI_SPIF_TRANS_CSR_MODE_MASK GENMASK(1, 0) /* Mode, 01: Read, 10: Write */
#define CVI_SPIF_TRANS_CSR_MODE(x) \
((x << CVI_SPIF_TRANS_CSR_MODE_SHIFT) & CVI_SPIF_TRANS_CSR_MODE_MASK)
#define CVI_SPIF_TRANS_CSR_CONT_READ_MASK BIT(2) /* Continous mode read? */
#define CVI_SPIF_TRANS_CSR_FAST_MODE_MASK BIT(3) /* Fast mode (which)? */
#define CVI_SPIF_TRANS_CSR_BUS_WIDTH_SHIFT 4
#define CVI_SPIF_TRANS_CSR_BUS_WIDTH_MASK GENMASK(5, 4) /* Bus width, 2^x bits */
#define CVI_SPIF_TRANS_CSR_BUS_WIDTH(x) \
((x << CVI_SPIF_TRANS_CSR_BUS_WIDTH_SHIFT) & CVI_SPIF_TRANS_CSR_BUS_WIDTH_MASK)
#define CVI_SPIF_TRANS_CSR_DMA_EN_MASK BIT(6) /* DMA request enable? */
#define CVI_SPIF_TRANS_CSR_MISO_LVL_MASK BIT(7) /* Read MISO level? */
#define CVI_SPIF_TRANS_CSR_ADDR_BYTES_MASK GENMASK(10, 8) /* Address bytes? */
#define CVI_SPIF_TRANS_CSR_WITH_CMD_MASK BIT(11) /* ??? */
#define CVI_SPIF_TRANS_CSR_FIFO_TRIG_LVL_SHIFT 12
#define CVI_SPIF_TRANS_CSR_FIFO_TRIG_LVL_MASK GENMASK(13, 12) /* FIFO (IRQ?) trigger level, 2^x bytes */
#define CVI_SPIF_TRANS_CSR_FIFO_TRIG_LVL(x) \
((x << CVI_SPIF_TRANS_CSR_FIFO_TRIG_LVL_SHIFT) & CVI_SPIF_TRANS_CSR_FIFO_TRIG_LVL_MASK)
#define CVI_SPIF_TRANS_CSR_GO_BUSY_MASK BIT(15) /* Write 1 to GO? Self-cleared bit? */
#define CVI_SPIF_TRANS_CSR_DUMMY_MASK GENMASK(19, 16) /* Dummy count? */
#define CVI_SPIF_TRANS_CSR_4B_ADDR_MASK BIT(20) /* 4 bytes address */
#define CVI_SPIF_TRANS_CSR_4B_CMD_MASK BIT(21) /* 4 bytes command */
/**
* SPIF INT_STS register
*
*/
#define CVI_SPIF_INT_STS_TRANS_DONE_MASK BIT(0)
#define CVI_SPIF_INT_STS_RD_FIFO_MASK BIT(2)
/*
* Note: All registers are mapped into the XIP space:
* Read access:
* - If DMMR_EN = 1: Memory contents
* - If DMMR_EN = 0: Register contents
* Write access: Register contents (DMMR_EN ignored)
*
*/
struct cvitek_spif_regs {
u32 spi_ctrl; /* Offset: 0x00 */
u32 ce_ctrl; /* Offset: 0x04 */
u32 dly_ctrl; /* Offset: 0x08 */
u32 dmmr; /* Offset: 0x0C */
u32 trans_csr; /* Offset: 0x10 */
u32 trans_num; /* Offset: 0x14 */
u32 ff_port; /* Offset: 0x18 */
u32 reserved0; /* Offset: 0x1C */
u32 ff_pt; /* Offset: 0x20 */
u32 reserved1; /* Offset: 0x24 */
u32 int_sts; /* Offset: 0x28 */
u32 int_en; /* Offset: 0x2C */
};
struct cvitek_spif_xfer {
u8 width; /* Bus width, 0: 1bit, 1: 2bits, 2: 4bits */
union {
u8 *in;
const u8 *out;
} data;
u32 length; /* Length in bytes */
};
struct cvitek_spif_priv {
struct cvitek_spif_regs *regs;
u32 clk_rate;
unsigned int cs;
};
static int cvitek_spif_claim_bus(struct udevice *dev)
{
struct cvitek_spif_priv *priv = dev_get_priv(dev->parent);
struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
priv->cs = slave_plat->cs;
if(priv->cs > 0)
return -ENODEV;
clrbits_le32(&priv->regs->ce_ctrl, CVI_SPIF_CE_CTRL_CEMANUAL_EN_MASK);
dev_dbg(dev, "claim_bus cs: %d\n", priv->cs);
return 0;
}
static int cvitek_spif_release_bus(struct udevice *dev)
{
struct cvitek_spif_priv *priv = dev_get_priv(dev->parent);
clrbits_le32(&priv->regs->ce_ctrl, CVI_SPIF_CE_CTRL_CEMANUAL_EN_MASK);
dev_dbg(dev, "release_bus cs: %d\n", priv->cs);
return 0;
}
static int cvitek_spif_set_speed(struct udevice *bus, uint speed)
{
struct cvitek_spif_priv *priv = dev_get_priv(bus);
u8 div_val = DIV_ROUND_UP(priv->clk_rate, speed) - 1;
u32 dly_ctrl = CVI_SPIF_DLY_CTRL_CET_MASK;
clrsetbits_le32(&priv->regs->spi_ctrl,
CVI_SPIF_SPI_CTRL_SCK_DIV_MASK,
(div_val << CVI_SPIF_SPI_CTRL_SCK_DIV_SHIFT));
if(speed > 30000000)
dly_ctrl |= CVI_SPIF_DLY_CTRL_NEG_SAMPLE_MASK;
writel(dly_ctrl, &priv->regs->dly_ctrl);
dev_dbg(bus, "set_speed to %dHz, div: %d\n", speed, div_val);
return 0;
}
static int cvitek_spif_set_mode(struct udevice *bus, uint mode)
{
struct cvitek_spif_priv *priv = dev_get_priv(bus);
u32 mode_mask = 0U;
if(mode & SPI_CPHA)
mode_mask |= CVI_SPIF_SPI_CTRL_CPHA_MASK;
if(mode & SPI_CPOL)
mode_mask |= CVI_SPIF_SPI_CTRL_CPOL_MASK;
clrsetbits_le32(&priv->regs->spi_ctrl,
(CVI_SPIF_SPI_CTRL_CPOL_MASK | CVI_SPIF_SPI_CTRL_CPHA_MASK),
mode_mask);
dev_dbg(bus, "set_mode to %d\n", mode);
return 0;
}
static int cvitek_spif_read(struct cvitek_spif_priv *priv, struct cvitek_spif_xfer *xfer)
{
u32 trans_ctrl;
u32 i;
u8 width;
switch(xfer->width)
{
case 1:
width = CVI_SPIF_BUS_WIDTH_1;
break;
case 2:
width = CVI_SPIF_BUS_WIDTH_2;
break;
case 4:
width = CVI_SPIF_BUS_WIDTH_4;
break;
default:
return -EINVAL;
}
trans_ctrl |= CVI_SPIF_TRANS_CSR_FIFO_TRIG_LVL(3);
trans_ctrl |= CVI_SPIF_TRANS_CSR_BUS_WIDTH(width);
trans_ctrl |= CVI_SPIF_TRANS_CSR_MODE(1); /* RX mode */
/* Set up transfer mode */
writel(trans_ctrl, &priv->regs->trans_csr);
/* Write transfer length in bytes */
writel(xfer->length, &priv->regs->trans_num);
/* Reset FIFO pointer */
writel(0U, &priv->regs->ff_pt);
/* Start transfer */
setbits_le32(&priv->regs->trans_csr, CVI_SPIF_TRANS_CSR_GO_BUSY_MASK);
for(i = 0; i < xfer->length; i++)
{
/* Wait for FIFO not empty */
while((readl(&priv->regs->ff_pt) & 0x0F) == 0U)
;
xfer->data.in[i] = readb(&priv->regs->ff_port);
}
while((readl(&priv->regs->int_sts) & CVI_SPIF_INT_STS_TRANS_DONE_MASK) == 0U)
;
/* Clear interrupt status */
writeb(0U, &priv->regs->int_sts);
return 0;
}
static int cvitek_spif_write(struct cvitek_spif_priv *priv, struct cvitek_spif_xfer *xfer)
{
u32 trans_ctrl;
u32 i;
u8 width;
switch(xfer->width)
{
case 1:
width = CVI_SPIF_BUS_WIDTH_1;
break;
case 2:
width = CVI_SPIF_BUS_WIDTH_2;
break;
case 4:
width = CVI_SPIF_BUS_WIDTH_4;
break;
default:
return -EINVAL;
}
trans_ctrl |= CVI_SPIF_TRANS_CSR_FIFO_TRIG_LVL(3);
trans_ctrl |= CVI_SPIF_TRANS_CSR_BUS_WIDTH(width);
trans_ctrl |= CVI_SPIF_TRANS_CSR_MODE(2); /* TX mode */
/* Set up transfer mode */
writel(trans_ctrl, &priv->regs->trans_csr);
/* Write transfer length in bytes */
writel(xfer->length, &priv->regs->trans_num);
/* Reset FIFO pointer */
writel(0U, &priv->regs->ff_pt);
/* Start transfer */
setbits_le32(&priv->regs->trans_csr, CVI_SPIF_TRANS_CSR_GO_BUSY_MASK);
for(i = 0; i < xfer->length; i++)
{
/* Wait for FIFO not full */
while((readl(&priv->regs->ff_pt) & 0x0F) >= CVI_SPIF_FIFO_DEPTH)
;
writeb(xfer->data.out[i], &priv->regs->ff_port);
}
while((readl(&priv->regs->int_sts) & CVI_SPIF_INT_STS_TRANS_DONE_MASK) == 0U)
;
/* Clear interrupt status */
writeb(0U, &priv->regs->int_sts);
return 0;
}
static int cvitek_spif_exec_op(struct spi_slave *slave, const struct spi_mem_op *op)
{
struct cvitek_spif_priv *priv = dev_get_priv(slave->dev->parent);
struct cvitek_spif_xfer xfer;
u8 buf[4];
int ret;
int i;
/* Use manual CE control mode */
writel((CVI_SPIF_CE_CTRL_CEMANUAL_MASK | CVI_SPIF_CE_CTRL_CEMANUAL_EN_MASK), &priv->regs->ce_ctrl);
clrbits_le32(&priv->regs->ce_ctrl, CVI_SPIF_CE_CTRL_CEMANUAL_MASK);
if(op->cmd.nbytes)
{
if(op->cmd.nbytes == 1U)
buf[0] = op->cmd.opcode & 0xFFU;
else
{
buf[0] = (op->cmd.opcode >> 8U) & 0xFFU;
buf[1] = op->cmd.opcode & 0xFFU;
}
xfer.data.out = buf;
xfer.length = op->cmd.nbytes;
xfer.width = op->cmd.buswidth;
ret = cvitek_spif_write(priv, &xfer);
if(ret < 0)
return ret;
}
if(op->addr.nbytes)
{
if(op->addr.nbytes == 3U)
{
buf[0] = (op->addr.val >> 16U) & 0xFFU;
buf[1] = (op->addr.val >> 8U) & 0xFFU;
buf[2] = op->addr.val & 0xFFU;
}
else if(op->addr.nbytes == 4U)
{
buf[0] = (op->addr.val >> 24U) & 0xFFU;
buf[1] = (op->addr.val >> 16U) & 0xFFU;
buf[2] = (op->addr.val >> 8U) & 0xFFU;
buf[3] = op->addr.val & 0xFFU;
}
else
return -EINVAL;
xfer.data.out = buf;
xfer.length = op->addr.nbytes;
xfer.width = op->addr.buswidth;
ret = cvitek_spif_write(priv, &xfer);
if(ret < 0)
return ret;
}
if(op->dummy.nbytes)
{
xfer.data.in = buf;
xfer.length = 1;
xfer.width = op->dummy.buswidth;
for(i = 0; i < op->dummy.nbytes; i++)
{
ret = cvitek_spif_write(priv, &xfer);
if(ret < 0)
return ret;
}
}
if(op->data.nbytes && (op->data.dir != SPI_MEM_NO_DATA))
{
xfer.length = op->data.nbytes;
xfer.width = op->data.buswidth;
if(op->data.dir == SPI_MEM_DATA_IN)
{
xfer.data.in = op->data.buf.in;
ret = cvitek_spif_read(priv, &xfer);
}
if(op->data.dir == SPI_MEM_DATA_OUT)
{
xfer.data.out = op->data.buf.out;
ret = cvitek_spif_write(priv, &xfer);
}
if(ret < 0)
return ret;
}
setbits_le32(&priv->regs->ce_ctrl, CVI_SPIF_CE_CTRL_CEMANUAL_MASK);
return 0;
}
static int cvitek_spif_probe(struct udevice *bus)
{
int ret;
struct clk clk;
struct cvitek_spif_priv *priv = dev_get_priv(bus);
priv->regs = dev_read_addr_ptr(bus);
if(!priv->regs)
return -EINVAL;
ret = clk_get_by_index(bus, 0, &clk);
if(ret < 0)
return ret;
ret = clk_enable(&clk);
if(ret)
{
dev_err(bus, "failed to enable clock\n");
return ret;
}
priv->clk_rate = clk_get_rate(&clk);
if(!priv->clk_rate)
{
dev_err(bus, "failed to get clock rate\n");
clk_disable(&clk);
return -EINVAL;
}
/* Disable direct memory-mapped mode */
writeb(0U, &priv->regs->dmmr);
dev_dbg(bus, "cvitek-spi probed on addr %p\r\n", priv->regs);
return 0;
}
static const struct spi_controller_mem_ops cvitek_spif_mem_ops = {
.exec_op = cvitek_spif_exec_op,
};
static const struct dm_spi_ops cvitek_spif_ops = {
.claim_bus = cvitek_spif_claim_bus,
.release_bus = cvitek_spif_release_bus,
.set_speed = cvitek_spif_set_speed,
.set_mode = cvitek_spif_set_mode,
.mem_ops = &cvitek_spif_mem_ops,
};
static const struct udevice_id cvitek_spif_ids[] = {
{ .compatible = "cvitek,cvitek-spif" },
{ }
};
U_BOOT_DRIVER(cvitek_spif) = {
.name = "cvitek_spif",
.id = UCLASS_SPI,
.of_match = cvitek_spif_ids,
.ops = &cvitek_spif_ops,
.priv_auto = sizeof(struct cvitek_spif_priv),
.probe = cvitek_spif_probe,
};