// 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, };