+// SPDX-License-Identifier: GPL-2.0+
/*
* TI QSPI driver
*
* Copyright (C) 2013, Texas Instruments, Incorporated
- *
- * SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <asm/omap_gpio.h>
#include <asm/omap_common.h>
#include <asm/ti-common/ti-edma3.h>
+#include <linux/kernel.h>
+#include <regmap.h>
+#include <syscon.h>
DECLARE_GLOBAL_DATA_PTR;
/* ti qpsi register bit masks */
#define QSPI_TIMEOUT 2000000
-#define QSPI_FCLK 192000000
+#define QSPI_FCLK 192000000
+#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
#endif
struct ti_qspi_regs *base;
void *ctrl_mod_mmap;
+ ulong fclk;
unsigned int mode;
u32 cmd;
u32 dc;
{
uint clk_div;
- debug("ti_spi_set_speed: hz: %d, clock divider %d\n", hz, clk_div);
-
if (!hz)
clk_div = 0;
else
- clk_div = (QSPI_FCLK / hz) - 1;
+ 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);
-
- /* assign clk_div values */
- if (clk_div < 0)
- clk_div = 0;
- else if (clk_div > QSPI_CLK_DIV_MAX)
- clk_div = QSPI_CLK_DIV_MAX;
-
- /* enable SCLK */
+ /* enable SCLK and program the clk divider */
writel(QSPI_CLK_EN | clk_div, &priv->base->clk_ctrl);
}
priv->cmd |= QSPI_3_PIN;
priv->cmd |= 0xfff;
-/* FIXME: This delay is required for successfull
- * completion of read/write/erase. Once its root
- * caused, it will be remove from the driver.
- */
-#ifdef CONFIG_AM43XX
- udelay(100);
-#endif
- while (words--) {
+ while (words) {
+ u8 xfer_len = 0;
+
if (txp) {
- debug("tx cmd %08x dc %08x data %02x\n",
- priv->cmd | QSPI_WR_SNGL, priv->dc, *txp);
- writel(*txp++, &priv->base->data);
- writel(priv->cmd | QSPI_WR_SNGL,
- &priv->base->cmd);
+ 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) {
}
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);
- #ifdef CONFIG_DRA7XX
- udelay(500);
- #endif
writel(priv->cmd | QSPI_RD_SNGL, &priv->base->cmd);
status = readl(&priv->base->status);
timeout = QSPI_TIMEOUT;
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 */
QSPI_SETUP0_NUM_D_BYTES_8_BITS |
QSPI_SETUP0_READ_QUAD | QSPI_CMD_WRITE |
QSPI_NUM_DUMMY_BITS);
- slave->mode_rx = SPI_RX_QUAD;
+ slave->mode |= SPI_RX_QUAD;
#else
memval |= QSPI_CMD_READ | QSPI_SETUP0_NUM_A_BYTES |
QSPI_SETUP0_NUM_D_BYTES_NO_BITS |
priv->base = (struct ti_qspi_regs *)QSPI_BASE;
priv->mode = mode;
-#if defined(CONFIG_DRA7XX) || defined(CONFIG_AM57XX)
+#if defined(CONFIG_DRA7XX)
priv->ctrl_mod_mmap = (void *)CORE_CTRL_IO;
priv->slave.memory_map = (void *)MMAP_START_ADDR_DRA;
+ priv->fclk = QSPI_DRA7XX_FCLK;
#else
priv->slave.memory_map = (void *)MMAP_START_ADDR_AM43x;
+ priv->fclk = QSPI_FCLK;
#endif
ti_spi_set_speed(priv, max_hz);
bool enable)
{
u32 memval;
- u32 mode = slave->mode_rx & (SPI_RX_QUAD | SPI_RX_DUAL);
+ u32 mode = slave->mode & (SPI_RX_QUAD | SPI_RX_DUAL);
if (!enable) {
writel(0, &priv->base->setup0);
memval |= QSPI_CMD_READ_QUAD;
memval |= QSPI_SETUP0_NUM_D_BYTES_8_BITS;
memval |= QSPI_SETUP0_READ_QUAD;
- slave->mode_rx = SPI_RX_QUAD;
+ slave->mode |= SPI_RX_QUAD;
break;
case SPI_RX_DUAL:
memval |= QSPI_CMD_READ_DUAL;
static int ti_qspi_probe(struct udevice *bus)
{
- /* Nothing to do in probe */
+ 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_ofdata_to_platdata(struct udevice *bus)
{
struct ti_qspi_priv *priv = dev_get_priv(bus);
const void *blob = gd->fdt_blob;
- int node = bus->of_offset;
- fdt_addr_t addr;
- void *mmap;
+ int node = dev_of_offset(bus);
- priv->base = map_physmem(dev_get_addr(bus), sizeof(struct ti_qspi_regs),
- MAP_NOCACHE);
- priv->memory_map = map_physmem(dev_get_addr_index(bus, 1), 0,
+ priv->ctrl_mod_mmap = map_syscon_chipselects(bus);
+ priv->base = map_physmem(devfdt_get_addr(bus),
+ sizeof(struct ti_qspi_regs), MAP_NOCACHE);
+ priv->memory_map = map_physmem(devfdt_get_addr_index(bus, 1), 0,
MAP_NOCACHE);
- addr = dev_get_addr_index(bus, 2);
- mmap = map_physmem(dev_get_addr_index(bus, 2), 0, MAP_NOCACHE);
- priv->ctrl_mod_mmap = (addr == FDT_ADDR_T_NONE) ? NULL : mmap;
priv->max_hz = fdtdec_get_int(blob, node, "spi-max-frequency", -1);
if (priv->max_hz < 0) {
};
static const struct udevice_id ti_qspi_ids[] = {
- { .compatible = "ti,dra7xxx-qspi" },
- { .compatible = "ti,am4372-qspi" },
+ { .compatible = "ti,dra7xxx-qspi", .data = QSPI_DRA7XX_FCLK},
+ { .compatible = "ti,am4372-qspi", .data = QSPI_FCLK},
{ }
};