mram no dtr

Signed-off-by: Heinrich Toews <ht@twx-software.de>
This commit is contained in:
Heinrich Toews
2025-12-17 18:53:37 +01:00
parent 8752ec1da6
commit 36d7e513df
9 changed files with 299 additions and 12 deletions
@@ -182,6 +182,7 @@
};
&ospi0 {
compatible = "ti,am654-ospi";
pinctrl-names = "default";
pinctrl-0 = <&ospi0_pins_default>;
status = "okay";
+2 -2
View File
@@ -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;
+7
View File
@@ -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
+1
View File
@@ -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
+8
View File
@@ -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_ */
+30 -7
View File
@@ -31,6 +31,8 @@
#include <linux/timer.h>
#include <linux/string.h>
#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:
+237
View File
@@ -0,0 +1,237 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* CQSPI Sysfs Register Access to MMIO (ahb_base)
*
*/
#include <linux/platform_device.h>
#include <linux/device.h>
#include <linux/io.h>
#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, &reg, &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, &reg, &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;
}
+12 -2
View File
@@ -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