From: Kongyang Liu Date: Sat, 20 Apr 2024 07:08:23 +0000 (+0800) Subject: spi: cv1800b: Add spi nor flash controller driver for cv1800b SoC X-Git-Url: http://git.dujemihanovic.xyz/html/%7B%7B%20%28.OutputFormats.Get?a=commitdiff_plain;h=6c75bea76a76e2fd21f55107e86696321e427a51;p=u-boot.git spi: cv1800b: Add spi nor flash controller driver for cv1800b SoC Add spi nor flash controller driver for cv1800b SoC Signed-off-by: Kongyang Liu Reviewed-by: Leo Yu-Chi Liang --- diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 612434633b..35030ab355 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -168,6 +168,14 @@ config CF_SPI Enable the ColdFire SPI driver. This driver can be used on some m68k SoCs. +config CV1800B_SPIF + bool "Sophgo cv1800b SPI Flash Controller driver" + depends on SPI_MEM + help + Enable the Sophgo cv1800b SPI Flash Controller driver. This driver + can be used to access the SPI NOR flash on platforms embedding this + Sophgo cv1800b IP core. + config DAVINCI_SPI bool "Davinci & Keystone SPI driver" depends on ARCH_DAVINCI || ARCH_KEYSTONE diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 14bdb97f18..32d7bf7237 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -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_CV1800B_SPIF) += cv1800b_spif.o obj-$(CONFIG_DAVINCI_SPI) += davinci_spi.o obj-$(CONFIG_DESIGNWARE_SPI) += designware_spi.o obj-$(CONFIG_EXYNOS_SPI) += exynos_spi.o diff --git a/drivers/spi/cv1800b_spif.c b/drivers/spi/cv1800b_spif.c new file mode 100644 index 0000000000..9c077f3ff9 --- /dev/null +++ b/drivers/spi/cv1800b_spif.c @@ -0,0 +1,321 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2024, Kongyang Liu + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CV1800B_SPI_CTRL_SCK_DIV_MASK GENMASK(10, 0) +#define CV1800B_SPI_CTRL_CPHA BIT(12) +#define CV1800B_SPI_CTRL_CPOL BIT(13) + +#define CV1800B_SPI_CE_MANUAL BIT(0) +#define CV1800B_SPI_CE_MANUAL_EN BIT(1) +#define CV1800B_SPI_CE_ENABLE (CV1800B_SPI_CE_MANUAL | \ + CV1800B_SPI_CE_MANUAL_EN) +#define CV1800B_SPI_CE_DISABLE CV1800B_SPI_CE_MANUAL_EN +#define CV1800B_SPI_CE_HARDWARE 0 + +#define CV1800B_SPI_DLY_CTRL_NEG_SAMPLE BIT(14) + +#define CV1800B_SPI_TRAN_MODE_RX BIT(0) +#define CV1800B_SPI_TRAN_MODE_TX BIT(1) +#define CV1800B_SPI_TRAN_FAST_MODE BIT(3) +#define CV1800B_SPI_TRAN_BUS_WIDTH_1_BIT 0x0 +#define CV1800B_SPI_TRAN_BUS_WIDTH_2_BIT BIT(4) +#define CV1800B_SPI_TRAN_BUS_WIDTH_4_BIT BIT(5) +#define CV1800B_SPI_TRAN_ADDR_3_BYTES (3 << 8) +#define CV1800B_SPI_TRAN_ADDR_4_BYTES (4 << 8) +#define CV1800B_SPI_TRAN_WITH_CMD BIT(11) +#define CV1800B_SPI_TRAN_GO_BUSY BIT(15) +#define CV1800B_SPI_TRAN_DUMMY_CYC_MASK GENMASK(19, 16) +#define CV1800B_SPI_TRAN_DUMMY_CYC_OFFSET 16 +#define CV1800B_SPI_TRAN_BYTE4_EN BIT(20) +#define CV1800B_SPI_TRAN_BYTE4_CMD BIT(21) + +#define CV1800B_SPI_FF_PT_AVAILABLE_MASK GENMASK(3, 0) + +#define CV1800B_SPI_INT_TRAN_DONE BIT(0) +#define CV1800B_SPI_INT_RD_FIFO BIT(2) +#define CV1800B_SPI_INT_WR_FIFO BIT(3) + +#define CV1800B_FIFO_CAPACITY 8 +#define CV1800B_DEFAULT_DIV 4 + +struct cv1800b_spif_regs { + u32 spi_ctrl; + u32 ce_ctrl; + u32 dly_ctrl; + u32 dmmr_ctrl; + u32 tran_csr; + u32 tran_num; + u32 ff_port; + u32 reserved0; + u32 ff_pt; + u32 reserved1; + u32 int_sts; + u32 int_en; +}; + +struct cv1800b_spi_priv { + struct cv1800b_spif_regs *regs; + uint clk_freq; + uint mode; + int div; +}; + +static int cv1800b_spi_probe(struct udevice *bus) +{ + struct cv1800b_spi_priv *priv = dev_get_priv(bus); + struct clk clkdev; + int ret; + + priv->regs = (struct cv1800b_spif_regs *)dev_read_addr_ptr(bus); + if (priv->regs == 0) + return -EINVAL; + + ret = clk_get_by_index(bus, 0, &clkdev); + if (ret) + return ret; + priv->clk_freq = clk_get_rate(&clkdev); + + /* DMMR mode is enabled by default, disable it */ + writel(0, &priv->regs->dmmr_ctrl); + + return 0; +} + +static void cv1800b_spi_config_dmmr(struct cv1800b_spi_priv *priv, struct spi_nor *flash) +{ + struct cv1800b_spif_regs *regs = priv->regs; + u32 read_cmd = flash->read_opcode; + u32 val; + + val = CV1800B_SPI_TRAN_MODE_RX | CV1800B_SPI_TRAN_WITH_CMD; + + switch (read_cmd) { + case SPINOR_OP_READ_4B: + case SPINOR_OP_READ_FAST_4B: + case SPINOR_OP_READ_1_1_2_4B: + case SPINOR_OP_READ_1_1_4_4B: + val |= CV1800B_SPI_TRAN_ADDR_4_BYTES | + CV1800B_SPI_TRAN_BYTE4_EN | CV1800B_SPI_TRAN_BYTE4_CMD; + break; + case SPINOR_OP_READ: + case SPINOR_OP_READ_FAST: + case SPINOR_OP_READ_1_1_2: + case SPINOR_OP_READ_1_1_4: + val |= CV1800B_SPI_TRAN_ADDR_3_BYTES; + break; + } + + switch (read_cmd) { + case SPINOR_OP_READ_FAST: + case SPINOR_OP_READ_FAST_4B: + val |= CV1800B_SPI_TRAN_FAST_MODE; + break; + } + + switch (read_cmd) { + case SPINOR_OP_READ_1_1_2: + case SPINOR_OP_READ_1_1_2_4B: + val |= CV1800B_SPI_TRAN_BUS_WIDTH_2_BIT; + break; + case SPINOR_OP_READ_1_1_4: + case SPINOR_OP_READ_1_1_4_4B: + val |= CV1800B_SPI_TRAN_BUS_WIDTH_4_BIT; + break; + } + + val |= (flash->read_dummy & CV1800B_SPI_TRAN_DUMMY_CYC_MASK) + << CV1800B_SPI_TRAN_DUMMY_CYC_OFFSET; + writel(val, ®s->tran_csr); +} + +static void cv1800b_set_clk_div(struct cv1800b_spi_priv *priv, u32 div) +{ + struct cv1800b_spif_regs *regs = priv->regs; + u32 neg_sample = 0; + + clrsetbits_le32(®s->spi_ctrl, CV1800B_SPI_CTRL_SCK_DIV_MASK, div); + + if (div < CV1800B_DEFAULT_DIV) + neg_sample = CV1800B_SPI_DLY_CTRL_NEG_SAMPLE; + clrsetbits_le32(®s->dly_ctrl, CV1800B_SPI_DLY_CTRL_NEG_SAMPLE, neg_sample); +} + +static int cv1800b_spi_transfer(struct cv1800b_spi_priv *priv, + u8 *din, const u8 *dout, uint len, ulong flags) +{ + struct cv1800b_spif_regs *regs = priv->regs; + u32 tran_csr; + u32 xfer_size, off; + u32 fifo_cnt; + u32 interrupt_mask; + + if (din) { + /* Slow down on receiving */ + cv1800b_set_clk_div(priv, CV1800B_DEFAULT_DIV); + interrupt_mask = CV1800B_SPI_INT_RD_FIFO; + } else { + interrupt_mask = CV1800B_SPI_INT_WR_FIFO; + } + + writel(0, ®s->ff_pt); + writel(len, ®s->tran_num); + + tran_csr = CV1800B_SPI_TRAN_GO_BUSY; + if (din) { + tran_csr |= CV1800B_SPI_TRAN_MODE_RX; + } else { + tran_csr |= CV1800B_SPI_TRAN_MODE_TX; + if (!(flags & SPI_XFER_BEGIN) && (priv->mode & SPI_TX_QUAD)) + tran_csr |= CV1800B_SPI_TRAN_BUS_WIDTH_4_BIT; + } + writel(tran_csr, ®s->tran_csr); + + wait_for_bit_le32(®s->int_sts, interrupt_mask, true, 3000, false); + + off = 0; + while (off < len) { + xfer_size = min_t(u32, len - off, CV1800B_FIFO_CAPACITY); + + fifo_cnt = readl(®s->ff_pt) & CV1800B_SPI_FF_PT_AVAILABLE_MASK; + if (din) + xfer_size = min(xfer_size, fifo_cnt); + else + xfer_size = min(xfer_size, CV1800B_FIFO_CAPACITY - fifo_cnt); + + while (xfer_size--) { + if (din) + din[off++] = readb(®s->ff_port); + else + writeb(dout[off++], ®s->ff_port); + } + } + + wait_for_bit_le32(®s->int_sts, CV1800B_SPI_INT_TRAN_DONE, true, 3000, false); + writel(0, ®s->ff_pt); + clrbits_le32(®s->int_sts, CV1800B_SPI_INT_TRAN_DONE | interrupt_mask); + + if (din) + cv1800b_set_clk_div(priv, priv->div); + return 0; +} + +static int cv1800b_spi_xfer(struct udevice *dev, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + struct udevice *bus = dev->parent; + struct cv1800b_spi_priv *priv = dev_get_priv(bus); + struct cv1800b_spif_regs *regs = priv->regs; + + if (bitlen == 0) + goto out; + + if (bitlen % 8) { + flags |= SPI_XFER_END; + goto out; + } + + if (flags & SPI_XFER_BEGIN) + writel(CV1800B_SPI_CE_DISABLE, ®s->ce_ctrl); + + if (din || dout) + cv1800b_spi_transfer(priv, din, dout, bitlen / 8, flags); + +out: + if (flags & SPI_XFER_END) + writel(CV1800B_SPI_CE_ENABLE, ®s->ce_ctrl); + return 0; +} + +static int cv1800b_spi_set_speed(struct udevice *bus, uint speed) +{ + struct cv1800b_spi_priv *priv = dev_get_priv(bus); + + priv->div = DIV_ROUND_CLOSEST(priv->clk_freq, speed * 2) - 1; + if (priv->div <= 0) + priv->div = CV1800B_DEFAULT_DIV; + + cv1800b_set_clk_div(priv, priv->div); + + return 0; +} + +static int cv1800b_spi_set_mode(struct udevice *bus, uint mode) +{ + struct cv1800b_spi_priv *priv = dev_get_priv(bus); + struct cv1800b_spif_regs *regs = priv->regs; + u32 val = 0; + + if (mode & SPI_CPHA) + val |= CV1800B_SPI_CTRL_CPHA; + if (mode & SPI_CPOL) + val |= CV1800B_SPI_CTRL_CPOL; + clrsetbits_le32(®s->spi_ctrl, CV1800B_SPI_CTRL_CPHA | CV1800B_SPI_CTRL_CPOL, val); + + priv->mode = mode; + + return 0; +} + +static int cv1800b_spi_exec_op(struct spi_slave *slave, const struct spi_mem_op *op) +{ + struct udevice *bus = slave->dev->parent; + struct cv1800b_spi_priv *priv = dev_get_priv(bus); + struct cv1800b_spif_regs *regs = priv->regs; + struct spi_nor *flash = dev_get_uclass_priv(slave->dev); + u32 old_tran_csr; + + if (!(op->data.nbytes > 0 && op->data.dir == SPI_MEM_DATA_IN) || + !(op->addr.nbytes > 0 && op->addr.nbytes <= 4)) + return -ENOTSUPP; + + old_tran_csr = readl(®s->tran_csr); + writel(CV1800B_SPI_CE_HARDWARE, ®s->ce_ctrl); + + cv1800b_spi_config_dmmr(priv, flash); + + writel(1, ®s->dmmr_ctrl); + memcpy(op->data.buf.in, (void *)priv->regs + op->addr.val, op->data.nbytes); + writel(0, ®s->dmmr_ctrl); + + writel(CV1800B_SPI_CE_ENABLE, ®s->ce_ctrl); + writel(old_tran_csr, ®s->tran_csr); + + return 0; +} + +static const struct spi_controller_mem_ops cv1800b_spi_mem_ops = { + .exec_op = cv1800b_spi_exec_op, +}; + +static const struct dm_spi_ops cv1800b_spi_ops = { + .xfer = cv1800b_spi_xfer, + .mem_ops = &cv1800b_spi_mem_ops, + .set_speed = cv1800b_spi_set_speed, + .set_mode = cv1800b_spi_set_mode, +}; + +static const struct udevice_id cv1800b_spi_ids[] = { + { .compatible = "sophgo,cv1800b-spif" }, + { } +}; + +U_BOOT_DRIVER(cv1800b_spi) = { + .name = "cv1800b_spif", + .id = UCLASS_SPI, + .of_match = cv1800b_spi_ids, + .ops = &cv1800b_spi_ops, + .priv_auto = sizeof(struct cv1800b_spi_priv), + .probe = cv1800b_spi_probe, +};