From 36d7e513dfa1526f0cb1baab782cda83a5b3e77f Mon Sep 17 00:00:00 2001 From: Heinrich Toews Date: Wed, 17 Dec 2025 18:53:37 +0100 Subject: [PATCH] mram no dtr Signed-off-by: Heinrich Toews --- .../dts/ti/k3-am623-pfc-750-84xx-wosm.dtsi | 1 + drivers/mtd/spi-nor/core.c | 4 +- drivers/spi/Kconfig | 7 + drivers/spi/Makefile | 1 + drivers/spi/cqspi_sysfs.h | 8 + drivers/spi/spi-cadence-quadspi.c | 37 ++- drivers/spi/spi-cadence-quadspi_sysfs.c | 237 ++++++++++++++++++ labgrid.basesoftware | 2 +- tools/exampels/dev_mem_access.c | 14 +- 9 files changed, 299 insertions(+), 12 deletions(-) create mode 100644 drivers/spi/cqspi_sysfs.h create mode 100644 drivers/spi/spi-cadence-quadspi_sysfs.c diff --git a/arch/arm64/boot/dts/ti/k3-am623-pfc-750-84xx-wosm.dtsi b/arch/arm64/boot/dts/ti/k3-am623-pfc-750-84xx-wosm.dtsi index d314b428c980..dd2dc2374a6d 100644 --- a/arch/arm64/boot/dts/ti/k3-am623-pfc-750-84xx-wosm.dtsi +++ b/arch/arm64/boot/dts/ti/k3-am623-pfc-750-84xx-wosm.dtsi @@ -182,6 +182,7 @@ }; &ospi0 { + compatible = "ti,am654-ospi"; pinctrl-names = "default"; pinctrl-0 = <&ospi0_pins_default>; status = "okay"; diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index e5871e5ac428..eb08d5695a68 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -101,7 +101,7 @@ void spi_nor_spimem_setup_op(const struct spi_nor *nor, * something like 4S-4D-4D, but SPI NOR can't. So, set all 4 * phases to either DTR or STR. */ - op->cmd.dtr = true; + op->cmd.dtr = false; op->addr.dtr = true; op->dummy.dtr = true; op->data.dtr = true; @@ -3180,7 +3180,7 @@ static int spi_nor_init(struct spi_nor *nor) { int err; - err = spi_nor_set_octal_dtr(nor, true); + err = spi_nor_set_octal_dtr(nor, false); if (err) { dev_dbg(nor->dev, "octal mode not supported\n"); return err; diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index c8f70d771ba3..a6a52a787b68 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -255,6 +255,13 @@ config SPI_CADENCE_QUADSPI device with a Cadence QSPI controller and want to access the Flash as an MTD device. +config SPI_CADENCE_QUADSPI_SYSFS + tristate "SYSFS interface for userspace Cadence Quad SPI Flash MMIO access" + depends on SPI_CADENCE_QUADSPI + help + Select to enable support for accessing MMIO ahb_base regian from userspace + through the sysfs interface. + config SPI_CADENCE_XSPI tristate "Cadence XSPI controller" depends on OF && HAS_IOMEM diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index b6d6f3c77533..80f506343930 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -37,6 +37,7 @@ obj-$(CONFIG_SPI_BITBANG) += spi-bitbang.o obj-$(CONFIG_SPI_BUTTERFLY) += spi-butterfly.o obj-$(CONFIG_SPI_CADENCE) += spi-cadence.o obj-$(CONFIG_SPI_CADENCE_QUADSPI) += spi-cadence-quadspi.o +obj-$(CONFIG_SPI_CADENCE_QUADSPI_SYSFS) += spi-cadence-quadspi_sysfs.o obj-$(CONFIG_SPI_CADENCE_XSPI) += spi-cadence-xspi.o obj-$(CONFIG_SPI_CLPS711X) += spi-clps711x.o obj-$(CONFIG_SPI_COLDFIRE_QSPI) += spi-coldfire-qspi.o diff --git a/drivers/spi/cqspi_sysfs.h b/drivers/spi/cqspi_sysfs.h new file mode 100644 index 000000000000..0cf0af1ed9a0 --- /dev/null +++ b/drivers/spi/cqspi_sysfs.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef CQSPI_SYSFS_H_ +#define CQSPI_SYSFS_H_ + +int cqspi_sysfs_register(struct platform_device *pdev); +void cqspi_sysfs_unregister(struct platform_device *pdev); + +#endif /* CQSPI_SYSFS_H_ */ diff --git a/drivers/spi/spi-cadence-quadspi.c b/drivers/spi/spi-cadence-quadspi.c index 3c0c1d5dc0fe..6051cfd14b56 100644 --- a/drivers/spi/spi-cadence-quadspi.c +++ b/drivers/spi/spi-cadence-quadspi.c @@ -31,6 +31,8 @@ #include #include +#include "cqspi_sysfs.h" + #define CQSPI_NAME "cadence-qspi" #define CQSPI_MAX_CHIPSELECT 16 @@ -385,7 +387,7 @@ static unsigned int cqspi_calc_dummy(const struct spi_mem_op *op) return dummy_clk; } -static int cqspi_wait_idle(struct cqspi_st *cqspi) +int cqspi_wait_idle(struct cqspi_st *cqspi) { const unsigned int poll_idle_retry = 3; unsigned int count = 0; @@ -705,6 +707,7 @@ static int cqspi_indirect_read_execute(struct cqspi_flash_pdata *f_pdata, u8 *rxbuf_end = rxbuf + n_rx; int ret = 0; + dev_info(dev, "%s:%d TWx: ahb_base (%p).\n", __func__, __LINE__, cqspi->ahb_base); writel(from_addr, reg_base + CQSPI_REG_INDIRECTRDSTARTADDR); writel(remaining, reg_base + CQSPI_REG_INDIRECTRDBYTES); @@ -792,6 +795,7 @@ static int cqspi_indirect_read_execute(struct cqspi_flash_pdata *f_pdata, /* Clear indirect completion status */ writel(CQSPI_REG_INDIRECTRD_DONE_MASK, reg_base + CQSPI_REG_INDIRECTRD); + dev_info(dev, "%s:%d TWx: ahb_base (%p).\n", __func__, __LINE__, cqspi->ahb_base); return 0; failrd: @@ -1019,6 +1023,7 @@ static int cqspi_indirect_write_execute(struct cqspi_flash_pdata *f_pdata, unsigned int write_bytes; int ret; + dev_info(dev, "%s:%d TWx: ahb_base (%p).\n", __func__, __LINE__, cqspi->ahb_base); writel(to_addr, reg_base + CQSPI_REG_INDIRECTWRSTARTADDR); writel(remaining, reg_base + CQSPI_REG_INDIRECTWRBYTES); @@ -1095,6 +1100,7 @@ static int cqspi_indirect_write_execute(struct cqspi_flash_pdata *f_pdata, cqspi_wait_idle(cqspi); + dev_info(dev, "%s:%d TWx: ahb_base (%p).\n", __func__, __LINE__, cqspi->ahb_base); return 0; failwr: @@ -1258,6 +1264,7 @@ static ssize_t cqspi_write(struct cqspi_flash_pdata *f_pdata, const struct spi_mem_op *op) { struct cqspi_st *cqspi = f_pdata->cqspi; + struct device *dev = &cqspi->pdev->dev; loff_t to = op->addr.val; size_t len = op->data.nbytes; const u_char *buf = op->data.buf.out; @@ -1267,6 +1274,10 @@ static ssize_t cqspi_write(struct cqspi_flash_pdata *f_pdata, if (ret) return ret; + dev_info(dev, "%s:%d TWx: ahb %p cmd.dtr %d use_direct_mode %d use_direct_mode_wr %d len %d to %u ahb_size %u.\n", + __func__, __LINE__, cqspi->ahb_base, op->cmd.dtr, + cqspi->use_direct_mode, cqspi->use_direct_mode_wr, + len, to, cqspi->ahb_size); /* * Some flashes like the Cypress Semper flash expect a dummy 4-byte * address (all 0s) with the read status register command in DTR mode. @@ -1277,14 +1288,13 @@ static ssize_t cqspi_write(struct cqspi_flash_pdata *f_pdata, */ if (!op->cmd.dtr && cqspi->use_direct_mode && cqspi->use_direct_mode_wr && ((to + len) <= cqspi->ahb_size)) { - pr_info("TWx: %s:%d - Writing <%lu> bytes with offset <%lld> to ahb_base <%p>\n", - __func__, __LINE__, len, to, cqspi->ahb_base + to); - print_hex_dump(KERN_INFO, "TWx write buf:", DUMP_PREFIX_OFFSET, - 16, 1, buf, len, true); + dev_info(dev, "%s:%d TWx: ahb_base (%p).\n", __func__, __LINE__, cqspi->ahb_base); memcpy_toio(cqspi->ahb_base + to, buf, len); return cqspi_wait_idle(cqspi); } + dev_info(dev, "%s:%d TWx: ahb_base (%p) op->cmd.dtr = %d.\n", __func__, __LINE__, cqspi->ahb_base, op->cmd.dtr); + return cqspi_indirect_write_execute(f_pdata, to, buf, len); } @@ -1308,7 +1318,9 @@ static int cqspi_direct_read_execute(struct cqspi_flash_pdata *f_pdata, dma_addr_t dma_dst; struct device *ddev; + dev_info(dev, "%s:%d TWx: ahb_base (%p).\n", __func__, __LINE__, cqspi->ahb_base); if (!cqspi->rx_chan || !virt_addr_valid(buf)) { + dev_info(dev, "%s:%d TWx: ahb_base (%p).\n", __func__, __LINE__, cqspi->ahb_base); memcpy_fromio(buf, cqspi->ahb_base + from, len); return 0; } @@ -1327,6 +1339,7 @@ static int cqspi_direct_read_execute(struct cqspi_flash_pdata *f_pdata, goto err_unmap; } + dev_info(dev, "%s:%d TWx: ahb_base (%p).\n", __func__, __LINE__, cqspi->ahb_base); tx->callback = cqspi_rx_dma_callback; tx->callback_param = cqspi; cookie = tx->tx_submit(tx); @@ -1351,6 +1364,7 @@ static int cqspi_direct_read_execute(struct cqspi_flash_pdata *f_pdata, err_unmap: dma_unmap_single(ddev, dma_dst, len, DMA_FROM_DEVICE); + dev_info(dev, "%s:%d TWx: ahb_base (%p).\n", __func__, __LINE__, cqspi->ahb_base); return ret; } @@ -1372,13 +1386,16 @@ static ssize_t cqspi_read(struct cqspi_flash_pdata *f_pdata, if (ret) return ret; + dev_info(dev, "%s:%d TWx: ahb_base (%p).\n", __func__, __LINE__, cqspi->ahb_base); if (cqspi->use_direct_mode && ((from + len) <= cqspi->ahb_size)) return cqspi_direct_read_execute(f_pdata, buf, from, len); + dev_info(dev, "%s:%d TWx: ahb_base (%p).\n", __func__, __LINE__, cqspi->ahb_base); if (cqspi->use_dma_read && ddata && ddata->indirect_read_dma && virt_addr_valid(buf) && ((dma_align & CQSPI_DMA_UNALIGN) == 0)) return ddata->indirect_read_dma(f_pdata, buf, from, len); + dev_info(dev, "%s:%d TWx: ahb_base (%p).\n", __func__, __LINE__, cqspi->ahb_base); return cqspi_indirect_read_execute(f_pdata, buf, from, len); } @@ -1610,7 +1627,7 @@ static const struct spi_controller_mem_ops cqspi_mem_ops = { }; static const struct spi_controller_mem_caps cqspi_mem_caps = { - .dtr = true, + .dtr = false, }; static int cqspi_setup_flash(struct cqspi_st *cqspi) @@ -1765,6 +1782,8 @@ static int cqspi_probe(struct platform_device *pdev) __func__, __LINE__, cqspi->ahb_base); + dev_info(dev, "TWx: ahb_base (%p).\n", cqspi->ahb_base); + pr_info("TWx: %s:%d mmap_phys_base %pad\n", __func__, __LINE__, &cqspi->mmap_phys_base); @@ -1829,7 +1848,7 @@ static int cqspi_probe(struct platform_device *pdev) host->max_speed_hz = cqspi->master_ref_clk_hz; /* write completion is supported by default */ - cqspi->wr_completion = true; + cqspi->wr_completion = false; ddata = of_device_get_match_data(dev); if (ddata) { @@ -1916,6 +1935,10 @@ static int cqspi_probe(struct platform_device *pdev) print_hex_dump(KERN_INFO, "TWx ahb_base:", DUMP_PREFIX_OFFSET, 16, 1, cqspi->ahb_base, 64, true); +#ifdef CONFIG_SPI_CADENCE_QUADSPI_SYSFS + cqspi_sysfs_register(pdev); +#endif + return 0; probe_setup_failed: diff --git a/drivers/spi/spi-cadence-quadspi_sysfs.c b/drivers/spi/spi-cadence-quadspi_sysfs.c new file mode 100644 index 000000000000..cb87b4c1d050 --- /dev/null +++ b/drivers/spi/spi-cadence-quadspi_sysfs.c @@ -0,0 +1,237 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * CQSPI Sysfs Register Access to MMIO (ahb_base) + * + */ + +#include +#include +#include + +#define CQSPI_MAX_CHIPSELECT 16 + +enum { + CLK_QSPI_APB = 0, + CLK_QSPI_AHB, + CLK_QSPI_NUM, +}; + +struct cqspi_flash_pdata { + struct cqspi_st *cqspi; + u32 clk_rate; + u32 read_delay; + u32 tshsl_ns; + u32 tsd2d_ns; + u32 tchsh_ns; + u32 tslch_ns; + u8 cs; +}; + +struct cqspi_st { + struct platform_device *pdev; + struct spi_controller *host; + struct clk *clk; + struct clk *clks[CLK_QSPI_NUM]; + unsigned int sclk; + + void __iomem *iobase; + void __iomem *ahb_base; + resource_size_t ahb_size; + struct completion transfer_complete; + + struct dma_chan *rx_chan; + struct completion rx_dma_complete; + dma_addr_t mmap_phys_base; + + int current_cs; + unsigned long master_ref_clk_hz; + bool is_decoded_cs; + u32 fifo_depth; + u32 fifo_width; + u32 num_chipselect; + bool rclk_en; + u32 trigger_address; + u32 wr_delay; + bool use_direct_mode; + bool use_direct_mode_wr; + struct cqspi_flash_pdata f_pdata[CQSPI_MAX_CHIPSELECT]; + bool use_dma_read; + u32 pd_dev_id; + bool wr_completion; + bool slow_sram; + bool apb_ahb_hazard; + + bool is_jh7110; /* Flag for StarFive JH7110 SoC */ +}; + +#include "cqspi_sysfs.h" + +int cqspi_wait_idle(struct cqspi_st *cqspi); + +struct cqspi_st *g_cqspi = NULL; + +static ssize_t cqspi_read_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + u32 reg, value; + int ret, bit; + char end; + loff_t from; + void __iomem *p; + u8 *tbuf; + + if (!g_cqspi) + return -EINVAL; + + /* Parse remaining parameters, reject extra parameters */ + ret = sscanf(buf, "%d %x%c", &bit, ®, &end); + if (ret < 1) { + dev_err(dev, "%s: Can't parse REG address\n", __func__); + return -EINVAL; + } + if (ret > 1 && end != '\n') { + dev_err(dev, "%s: Extra parameters\n", __func__); + return -EINVAL; + } + + dev_info(dev, "TWx: ahb_base (%p).\n", g_cqspi->ahb_base); + + from = (loff_t)reg; + p = g_cqspi->ahb_base + from; + + switch(bit) { + case 8: + // memcpy_fromio(buf, g_cqspi->ahb_base + from, len); + value = ioread8(p); + break; + case 16: + value = ioread16(p); + break; + case 32: + value = ioread32(p); + break; + case 99: + tbuf = (u8*) &value; + memcpy_fromio(tbuf, p, sizeof(value)); + + print_hex_dump(KERN_INFO, "TWx ahb_base:", DUMP_PREFIX_OFFSET, + 16, 1, g_cqspi->ahb_base, 64, true); + break; + } + + if (ret < 0) { + dev_err(dev, "%s: read failed.\n", __func__); + return ret; + } + + dev_info(dev, "%d-bit read: reg 0x%04x: 0x%08x (%p).\n", + bit, reg, value, p); + + return count; +} +static DEVICE_ATTR_WO(cqspi_read); + +static ssize_t cqspi_write_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + u32 reg, value; + int ret, bit; + char end; + loff_t to; + void __iomem *p; + u8 *tbuf; + + dev_dbg(dev, "%s:%d\n", __func__, __LINE__); + + if (!g_cqspi) + return -EINVAL; + + /* Parse remaining parameters, reject extra parameters */ + ret = sscanf(buf, "%d %x %x%c", &bit, ®, &value, &end); + if (ret < 1) { + dev_err(dev, "%s: Can't parse register command\n", __func__); + return -EINVAL; + } + if (ret > 1 && end != '\n') { + dev_err(dev, "%s: Extra parameters\n", __func__); + return -EINVAL; + } + + to = (loff_t)reg; + p = g_cqspi->ahb_base + to; + + switch(bit) { + case 8: + iowrite8((u8) value, p); + break; + case 16: + iowrite16((u16) value, p); + break; + case 32: + iowrite32((u32) value, p); + break; + case 99: + tbuf = (u8*) &value; + memcpy_toio(p, tbuf, sizeof(value)); + cqspi_wait_idle(g_cqspi); + + print_hex_dump(KERN_INFO, "TWx ahb_base:", DUMP_PREFIX_OFFSET, + 16, 1, g_cqspi->ahb_base, 64, true); + break; + } + + if (ret < 0) { + dev_err(dev, "%s: read failed.\n", __func__); + return ret; + } + + dev_info(dev, "%d-bit write: reg 0x%04x value 0x%08x (%p).\n", + bit, reg, value, p); + + return count; +} +static DEVICE_ATTR_WO(cqspi_write); + +int cqspi_sysfs_register(struct platform_device *pdev) +{ + struct device *dev; + int ret; + + dev_dbg(dev, "%s:%d\n", __func__, __LINE__); + + if (!pdev) + return -EINVAL; + + dev = &pdev->dev; + + ret = device_create_file(dev, &dev_attr_cqspi_read); + if (ret) + dev_err(dev, "Could not create <%s> attribute.\n", "cqspi_read"); + + ret = device_create_file(dev, &dev_attr_cqspi_write); + if (ret) + dev_err(dev, "Could not create <%s> attribute.\n", "cqspi_write"); + + g_cqspi = platform_get_drvdata(pdev); + + return ret; +} + +void cqspi_sysfs_unregister(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + + dev_dbg(dev, "%s:%d\n", __func__, __LINE__); + + if (!pdev) + return; + + dev = &pdev->dev; + + device_remove_file(dev, &dev_attr_cqspi_read); + device_remove_file(dev, &dev_attr_cqspi_write); + + g_cqspi = NULL; +} diff --git a/labgrid.basesoftware b/labgrid.basesoftware index f1a3c1f8bb11..705c63ebb290 160000 --- a/labgrid.basesoftware +++ b/labgrid.basesoftware @@ -1 +1 @@ -Subproject commit f1a3c1f8bb11d0944bb8fd84761ed34fd1cae40a +Subproject commit 705c63ebb290e1da8f8de0cc4d5ef5547e4528f0 diff --git a/tools/exampels/dev_mem_access.c b/tools/exampels/dev_mem_access.c index 58ffc856df06..843ac56981f3 100644 --- a/tools/exampels/dev_mem_access.c +++ b/tools/exampels/dev_mem_access.c @@ -10,6 +10,7 @@ int main(int argc, char *argv[]) { int fd; void *virtual_base; + void *vb; // volatile unsigned int *qspi_register; // oder Pointer auf den Speicherbereich if (argc != 2) { @@ -42,10 +43,18 @@ int main(int argc, char *argv[]) { return EXIT_FAILURE; } + vb = virtual_base; + + vb += 4; // Jetzt können Sie über den Pointer virtual_base auf die physische Adresse zugreifen // Beispiel: Lesen eines 32-Bit-Werts - unsigned int value = *((volatile unsigned int *) virtual_base); - printf("TWx: 0x%lx: 0x%x\n", virtual_base, value); + volatile unsigned int value = *((volatile unsigned int *) vb); + printf("TWx: 0x%lx: 0x%x\n", vb, value); + __sync_synchronize(); + vb += 4; + value = *((volatile unsigned int *) vb); + printf("TWx: 0x%lx: 0x%x\n", vb, value); + __sync_synchronize(); printf("Dump des Speichers ab virtueller Adresse %p (Groesse: %zu Bytes):\n", (void*)virtual_base, size); @@ -53,6 +62,7 @@ int main(int argc, char *argv[]) { // Speicherinhalt ausgeben (dumpen) unsigned char *ptr = virtual_base; for (size_t i = 0; i < size; i++) { + __sync_synchronize(); // Hexadezimale Ausgabe printf("%02x ", ptr[i]); if ((i + 1) % 16 == 0) { // Nach 16 Bytes eine neue Zeile