// SPDX-License-Identifier: GPL-2.0+ /* * TI QSPI driver * * Copyright (C) 2013, Texas Instruments, Incorporated */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include DECLARE_GLOBAL_DATA_PTR; /* ti qpsi register bit masks */ #define QSPI_TIMEOUT 2000000 /* AM4372: QSPI gets SPI_GCLK from PRCM unit as PER_CLKOUTM2 divided by 4. */ #define QSPI_FCLK (192000000 / 4) #define QSPI_DRA7XX_FCLK 76800000 #define QSPI_WLEN_MAX_BITS 128 #define QSPI_WLEN_MAX_BYTES (QSPI_WLEN_MAX_BITS >> 3) #define QSPI_WLEN_MASK QSPI_WLEN(QSPI_WLEN_MAX_BITS) /* clock control */ #define QSPI_CLK_EN BIT(31) #define QSPI_CLK_DIV_MAX 0xffff /* command */ #define QSPI_EN_CS(n) (n << 28) #define QSPI_WLEN(n) ((n-1) << 19) #define QSPI_3_PIN BIT(18) #define QSPI_RD_SNGL BIT(16) #define QSPI_WR_SNGL (2 << 16) #define QSPI_INVAL (4 << 16) #define QSPI_RD_QUAD (7 << 16) /* device control */ #define QSPI_CKPHA(n) (1 << (2 + n*8)) #define QSPI_CSPOL(n) (1 << (1 + n*8)) #define QSPI_CKPOL(n) (1 << (n*8)) /* status */ #define QSPI_WC BIT(1) #define QSPI_BUSY BIT(0) #define QSPI_WC_BUSY (QSPI_WC | QSPI_BUSY) #define QSPI_XFER_DONE QSPI_WC #define MM_SWITCH 0x01 #define MEM_CS(cs) ((cs + 1) << 8) #define MEM_CS_UNSELECT 0xfffff8ff #define QSPI_SETUP0_READ_NORMAL (0x0 << 12) #define QSPI_SETUP0_READ_DUAL (0x1 << 12) #define QSPI_SETUP0_READ_QUAD (0x3 << 12) #define QSPI_SETUP0_ADDR_SHIFT (8) #define QSPI_SETUP0_DBITS_SHIFT (10) #define TI_QSPI_SETUP_REG(priv, cs) (&(priv)->base->setup0 + (cs)) /* ti qspi register set */ struct ti_qspi_regs { u32 pid; u32 pad0[3]; u32 sysconfig; u32 pad1[3]; u32 int_stat_raw; u32 int_stat_en; u32 int_en_set; u32 int_en_ctlr; u32 intc_eoi; u32 pad2[3]; u32 clk_ctrl; u32 dc; u32 cmd; u32 status; u32 data; u32 setup0; u32 setup1; u32 setup2; u32 setup3; u32 memswitch; u32 data1; u32 data2; u32 data3; }; /* ti qspi priv */ struct ti_qspi_priv { void *memory_map; size_t mmap_size; uint max_hz; u32 num_cs; struct ti_qspi_regs *base; void *ctrl_mod_mmap; ulong fclk; unsigned int mode; u32 cmd; u32 dc; }; static int ti_qspi_set_speed(struct udevice *bus, uint hz) { struct ti_qspi_priv *priv = dev_get_priv(bus); uint clk_div; if (!hz) clk_div = 0; else clk_div = DIV_ROUND_UP(priv->fclk, hz) - 1; /* truncate clk_div value to QSPI_CLK_DIV_MAX */ if (clk_div > QSPI_CLK_DIV_MAX) clk_div = QSPI_CLK_DIV_MAX; debug("ti_spi_set_speed: hz: %d, clock divider %d\n", hz, clk_div); /* disable SCLK */ writel(readl(&priv->base->clk_ctrl) & ~QSPI_CLK_EN, &priv->base->clk_ctrl); /* enable SCLK and program the clk divider */ writel(QSPI_CLK_EN | clk_div, &priv->base->clk_ctrl); return 0; } static void ti_qspi_cs_deactivate(struct ti_qspi_priv *priv) { writel(priv->cmd | QSPI_INVAL, &priv->base->cmd); /* dummy readl to ensure bus sync */ readl(&priv->base->cmd); } static void ti_qspi_ctrl_mode_mmap(void *ctrl_mod_mmap, int cs, bool enable) { u32 val; val = readl(ctrl_mod_mmap); if (enable) val |= MEM_CS(cs); else val &= MEM_CS_UNSELECT; writel(val, ctrl_mod_mmap); } static int ti_qspi_xfer(struct udevice *dev, unsigned int bitlen, const void *dout, void *din, unsigned long flags) { struct dm_spi_slave_plat *slave = dev_get_parent_plat(dev); struct ti_qspi_priv *priv; struct udevice *bus; uint words = bitlen >> 3; /* fixed 8-bit word length */ const uchar *txp = dout; uchar *rxp = din; uint status; int timeout; unsigned int cs = slave->cs; bus = dev->parent; priv = dev_get_priv(bus); if (cs > priv->num_cs) { debug("invalid qspi chip select\n"); return -EINVAL; } if (bitlen == 0) return -1; if (bitlen % 8) { debug("spi_xfer: Non byte aligned SPI transfer\n"); return -1; } /* Setup command reg */ priv->cmd = 0; priv->cmd |= QSPI_WLEN(8); priv->cmd |= QSPI_EN_CS(cs); if (priv->mode & SPI_3WIRE) priv->cmd |= QSPI_3_PIN; priv->cmd |= 0xfff; while (words) { u8 xfer_len = 0; if (txp) { u32 cmd = priv->cmd; if (words >= QSPI_WLEN_MAX_BYTES) { u32 *txbuf = (u32 *)txp; u32 data; data = cpu_to_be32(*txbuf++); writel(data, &priv->base->data3); data = cpu_to_be32(*txbuf++); writel(data, &priv->base->data2); data = cpu_to_be32(*txbuf++); writel(data, &priv->base->data1); data = cpu_to_be32(*txbuf++); writel(data, &priv->base->data); cmd &= ~QSPI_WLEN_MASK; cmd |= QSPI_WLEN(QSPI_WLEN_MAX_BITS); xfer_len = QSPI_WLEN_MAX_BYTES; } else { writeb(*txp, &priv->base->data); xfer_len = 1; } debug("tx cmd %08x dc %08x\n", cmd | QSPI_WR_SNGL, priv->dc); writel(cmd | QSPI_WR_SNGL, &priv->base->cmd); status = readl(&priv->base->status); timeout = QSPI_TIMEOUT; while ((status & QSPI_WC_BUSY) != QSPI_XFER_DONE) { if (--timeout < 0) { printf("spi_xfer: TX timeout!\n"); return -1; } status = readl(&priv->base->status); } txp += xfer_len; debug("tx done, status %08x\n", status); } if (rxp) { debug("rx cmd %08x dc %08x\n", ((u32)(priv->cmd | QSPI_RD_SNGL)), priv->dc); writel(priv->cmd | QSPI_RD_SNGL, &priv->base->cmd); status = readl(&priv->base->status); timeout = QSPI_TIMEOUT; while ((status & QSPI_WC_BUSY) != QSPI_XFER_DONE) { if (--timeout < 0) { printf("spi_xfer: RX timeout!\n"); return -1; } status = readl(&priv->base->status); } *rxp++ = readl(&priv->base->data); xfer_len = 1; debug("rx done, status %08x, read %02x\n", status, *(rxp-1)); } words -= xfer_len; } /* Terminate frame */ if (flags & SPI_XFER_END) ti_qspi_cs_deactivate(priv); return 0; } /* TODO: control from sf layer to here through dm-spi */ static void ti_qspi_copy_mmap(void *data, void *offset, size_t len) { #if defined(CONFIG_TI_EDMA3) && !defined(CONFIG_DMA) unsigned int addr = (unsigned int) (data); unsigned int edma_slot_num = 1; /* Invalidate the area, so no writeback into the RAM races with DMA */ invalidate_dcache_range(addr, addr + roundup(len, ARCH_DMA_MINALIGN)); /* enable edma3 clocks */ enable_edma3_clocks(); /* Call edma3 api to do actual DMA transfer */ edma3_transfer(EDMA3_BASE, edma_slot_num, data, offset, len); /* disable edma3 clocks */ disable_edma3_clocks(); #else memcpy_fromio(data, offset, len); #endif *((unsigned int *)offset) += len; } static void ti_qspi_setup_mmap_read(struct ti_qspi_priv *priv, int cs, u8 opcode, u8 data_nbits, u8 addr_width, u8 dummy_bytes) { u32 memval = opcode; switch (data_nbits) { case 4: memval |= QSPI_SETUP0_READ_QUAD; break; case 2: memval |= QSPI_SETUP0_READ_DUAL; break; default: memval |= QSPI_SETUP0_READ_NORMAL; break; } memval |= ((addr_width - 1) << QSPI_SETUP0_ADDR_SHIFT | dummy_bytes << QSPI_SETUP0_DBITS_SHIFT); writel(memval, TI_QSPI_SETUP_REG(priv, cs)); } static int ti_qspi_set_mode(struct udevice *bus, uint mode) { struct ti_qspi_priv *priv = dev_get_priv(bus); priv->dc = 0; if (mode & SPI_CPHA) priv->dc |= QSPI_CKPHA(0); if (mode & SPI_CPOL) priv->dc |= QSPI_CKPOL(0); if (mode & SPI_CS_HIGH) priv->dc |= QSPI_CSPOL(0); return 0; } static int ti_qspi_exec_mem_op(struct spi_slave *slave, const struct spi_mem_op *op) { struct dm_spi_slave_plat *slave_plat; struct ti_qspi_priv *priv; struct udevice *bus; u32 from = 0; int ret = 0; bus = slave->dev->parent; priv = dev_get_priv(bus); slave_plat = dev_get_parent_plat(slave->dev); /* Only optimize read path. */ if (!op->data.nbytes || op->data.dir != SPI_MEM_DATA_IN || !op->addr.nbytes || op->addr.nbytes > 4) return -ENOTSUPP; /* Address exceeds MMIO window size, fall back to regular mode. */ from = op->addr.val; if (from + op->data.nbytes > priv->mmap_size) return -ENOTSUPP; ti_qspi_setup_mmap_read(priv, slave_plat->cs, op->cmd.opcode, op->data.buswidth, op->addr.nbytes, op->dummy.nbytes); ti_qspi_copy_mmap((void *)op->data.buf.in, (void *)priv->memory_map + from, op->data.nbytes); return ret; } static int ti_qspi_claim_bus(struct udevice *dev) { struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev); struct ti_qspi_priv *priv; struct udevice *bus; bus = dev->parent; priv = dev_get_priv(bus); if (slave_plat->cs > priv->num_cs) { debug("invalid qspi chip select\n"); return -EINVAL; } writel(MM_SWITCH, &priv->base->memswitch); if (priv->ctrl_mod_mmap) ti_qspi_ctrl_mode_mmap(priv->ctrl_mod_mmap, slave_plat->cs, true); writel(priv->dc, &priv->base->dc); writel(0, &priv->base->cmd); writel(0, &priv->base->data); priv->dc <<= slave_plat->cs * 8; writel(priv->dc, &priv->base->dc); return 0; } static int ti_qspi_release_bus(struct udevice *dev) { struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev); struct ti_qspi_priv *priv; struct udevice *bus; bus = dev->parent; priv = dev_get_priv(bus); writel(~MM_SWITCH, &priv->base->memswitch); if (priv->ctrl_mod_mmap) ti_qspi_ctrl_mode_mmap(priv->ctrl_mod_mmap, slave_plat->cs, false); writel(0, &priv->base->dc); writel(0, &priv->base->cmd); writel(0, &priv->base->data); writel(0, TI_QSPI_SETUP_REG(priv, slave_plat->cs)); return 0; } static int ti_qspi_probe(struct udevice *bus) { struct ti_qspi_priv *priv = dev_get_priv(bus); priv->fclk = dev_get_driver_data(bus); return 0; } static void *map_syscon_chipselects(struct udevice *bus) { #if CONFIG_IS_ENABLED(SYSCON) struct udevice *syscon; struct regmap *regmap; const fdt32_t *cell; int len, err; err = uclass_get_device_by_phandle(UCLASS_SYSCON, bus, "syscon-chipselects", &syscon); if (err) { debug("%s: unable to find syscon device (%d)\n", __func__, err); return NULL; } regmap = syscon_get_regmap(syscon); if (IS_ERR(regmap)) { debug("%s: unable to find regmap (%ld)\n", __func__, PTR_ERR(regmap)); return NULL; } cell = fdt_getprop(gd->fdt_blob, dev_of_offset(bus), "syscon-chipselects", &len); if (len < 2*sizeof(fdt32_t)) { debug("%s: offset not available\n", __func__); return NULL; } return fdtdec_get_number(cell + 1, 1) + regmap_get_range(regmap, 0); #else fdt_addr_t addr; addr = devfdt_get_addr_index(bus, 2); return (addr == FDT_ADDR_T_NONE) ? NULL : map_physmem(addr, 0, MAP_NOCACHE); #endif } static int ti_qspi_of_to_plat(struct udevice *bus) { struct ti_qspi_priv *priv = dev_get_priv(bus); const void *blob = gd->fdt_blob; int node = dev_of_offset(bus); fdt_addr_t mmap_addr; fdt_addr_t mmap_size; priv->ctrl_mod_mmap = map_syscon_chipselects(bus); priv->base = map_physmem(dev_read_addr(bus), sizeof(struct ti_qspi_regs), MAP_NOCACHE); mmap_addr = devfdt_get_addr_size_index(bus, 1, &mmap_size); priv->memory_map = map_physmem(mmap_addr, mmap_size, MAP_NOCACHE); priv->mmap_size = mmap_size; priv->max_hz = dev_read_u32_default(bus, "spi-max-frequency", 0); if (!priv->max_hz) { debug("Error: Max frequency missing\n"); return -ENODEV; } priv->num_cs = fdtdec_get_int(blob, node, "num-cs", 4); debug("%s: regs=<0x%x>, max-frequency=%d\n", __func__, (int)priv->base, priv->max_hz); return 0; } static const struct spi_controller_mem_ops ti_qspi_mem_ops = { .exec_op = ti_qspi_exec_mem_op, }; static const struct dm_spi_ops ti_qspi_ops = { .claim_bus = ti_qspi_claim_bus, .release_bus = ti_qspi_release_bus, .xfer = ti_qspi_xfer, .set_speed = ti_qspi_set_speed, .set_mode = ti_qspi_set_mode, .mem_ops = &ti_qspi_mem_ops, }; static const struct udevice_id ti_qspi_ids[] = { { .compatible = "ti,dra7xxx-qspi", .data = QSPI_DRA7XX_FCLK}, { .compatible = "ti,am4372-qspi", .data = QSPI_FCLK}, { } }; U_BOOT_DRIVER(ti_qspi) = { .name = "ti_qspi", .id = UCLASS_SPI, .of_match = ti_qspi_ids, .ops = &ti_qspi_ops, .of_to_plat = ti_qspi_of_to_plat, .priv_auto = sizeof(struct ti_qspi_priv), .probe = ti_qspi_probe, };