[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20240418011353.1764672-4-wsadowski@marvell.com>
Date: Wed, 17 Apr 2024 18:13:50 -0700
From: Witold Sadowski <wsadowski@...vell.com>
To: <linux-kernel@...r.kernel.org>, <linux-spi@...r.kernel.org>,
<devicetree@...r.kernel.org>
CC: <broonie@...nel.org>, <robh@...nel.org>,
<krzysztof.kozlowski+dt@...aro.org>, <conor+dt@...nel.org>,
<pthombar@...ence.com>, Witold Sadowski <wsadowski@...vell.com>
Subject: [PATCH v3 3/5] spi: cadence: Add Marvell xSPI IP overlay changes
Add support for basic v2 Marvell overlay block. Support for basic
operation is added here: clock configuration, PHY configuration,
interrupt configuration(enabling)
Clock divider block is build on top of Cadence xSPI IP, and divides
external 800MHz clock. It allows only for a few different clock speeds
starting from 6.25MHz up to 200MHz.
PHY configuration can be read from device-tree, if parameter is not
present - safe defaults will be used..
In addition to handle interrupt propoerly driver must clear MSI-X
interrupt bit, in addition to clearing xSPI interrupt bit. Interrupt
masking must be disabled.
Signed-off-by: Witold Sadowski <wsadowski@...vell.com>
---
drivers/spi/spi-cadence-xspi.c | 326 ++++++++++++++++++++++++++++++++-
1 file changed, 318 insertions(+), 8 deletions(-)
diff --git a/drivers/spi/spi-cadence-xspi.c b/drivers/spi/spi-cadence-xspi.c
index cdce2e280f66..5d36f9177f3c 100644
--- a/drivers/spi/spi-cadence-xspi.c
+++ b/drivers/spi/spi-cadence-xspi.c
@@ -193,6 +193,44 @@
((op)->data.dir == SPI_MEM_DATA_IN) ? \
CDNS_XSPI_STIG_CMD_DIR_READ : CDNS_XSPI_STIG_CMD_DIR_WRITE))
+/*PHY default values*/
+#define REGS_DLL_PHY_CTRL 0x00000707
+#define CTB_RFILE_PHY_CTRL 0x00004000
+#define RFILE_PHY_TSEL 0x00000000
+#define RFILE_PHY_DQ_TIMING 0x00000101
+#define RFILE_PHY_DQS_TIMING 0x00700404
+#define RFILE_PHY_GATE_LPBK_CTRL 0x00200030
+#define RFILE_PHY_DLL_MASTER_CTRL 0x00800000
+#define RFILE_PHY_DLL_SLAVE_CTRL 0x0000ff01
+
+/*PHY config rtegisters*/
+#define CDNS_XSPI_RF_MINICTRL_REGS_DLL_PHY_CTRL 0x1034
+#define CDNS_XSPI_PHY_CTB_RFILE_PHY_CTRL 0x0080
+#define CDNS_XSPI_PHY_CTB_RFILE_PHY_TSEL 0x0084
+#define CDNS_XSPI_PHY_DATASLICE_RFILE_PHY_DQ_TIMING 0x0000
+#define CDNS_XSPI_PHY_DATASLICE_RFILE_PHY_DQS_TIMING 0x0004
+#define CDNS_XSPI_PHY_DATASLICE_RFILE_PHY_GATE_LPBK_CTRL 0x0008
+#define CDNS_XSPI_PHY_DATASLICE_RFILE_PHY_DLL_MASTER_CTRL 0x000c
+#define CDNS_XSPI_PHY_DATASLICE_RFILE_PHY_DLL_SLAVE_CTRL 0x0010
+#define CDNS_XSPI_DATASLICE_RFILE_PHY_DLL_OBS_REG_0 0x001c
+
+#define CDNS_XSPI_DLL_RST_N BIT(24)
+#define CDNS_XSPI_DLL_LOCK BIT(0)
+
+/* Marvell clock config register */
+#define CDNS_MRVL_XSPI_CLK_CTRL_AUX_REG 0x2020
+#define CDNS_MRVL_XSPI_CLK_ENABLE BIT(0)
+#define CDNS_MRVL_XSPI_CLK_DIV GENMASK(4, 1)
+#define CDNS_MRVL_XSPI_IRQ_ENABLE BIT(6)
+
+/* Marvell MSI-X clear interrupt register */
+#define CDNS_MRVL_XSPI_SPIX_INTR_AUX 0x2000
+#define CDNS_MRVL_MSIX_CLEAR_IRQ 0x01
+
+/* Marvell clock macros */
+#define CDNS_MRVL_XSPI_CLOCK_IO_HZ 800000000
+#define CDNS_MRVL_XSPI_CLOCK_DIVIDED(div) ((CDNS_MRVL_XSPI_CLOCK_IO_HZ) / (div))
+
enum cdns_xspi_stig_instr_type {
CDNS_XSPI_STIG_INSTR_TYPE_0,
CDNS_XSPI_STIG_INSTR_TYPE_1,
@@ -212,6 +250,7 @@ enum cdns_xspi_stig_cmd_dir {
struct cdns_xspi_dev {
struct platform_device *pdev;
struct device *dev;
+ bool mrvl_hw_overlay;
void __iomem *iobase;
void __iomem *auxbase;
@@ -232,6 +271,170 @@ struct cdns_xspi_dev {
u8 hw_num_banks;
};
+struct cdns_xspi_driver_data {
+ bool mrvl_hw_overlay;
+};
+
+static struct cdns_xspi_driver_data mrvl_driver_data = {
+ .mrvl_hw_overlay = true,
+};
+
+static struct cdns_xspi_driver_data cdns_driver_data = {
+ .mrvl_hw_overlay = false,
+};
+
+#define MRVL_DEFAULT_CLK 25000000
+
+const int cdns_mrvl_xspi_clk_div_list[] = {
+ 4, //0x0 = Divide by 4. SPI clock is 200 MHz.
+ 6, //0x1 = Divide by 6. SPI clock is 133.33 MHz.
+ 8, //0x2 = Divide by 8. SPI clock is 100 MHz.
+ 10, //0x3 = Divide by 10. SPI clock is 80 MHz.
+ 12, //0x4 = Divide by 12. SPI clock is 66.666 MHz.
+ 16, //0x5 = Divide by 16. SPI clock is 50 MHz.
+ 18, //0x6 = Divide by 18. SPI clock is 44.44 MHz.
+ 20, //0x7 = Divide by 20. SPI clock is 40 MHz.
+ 24, //0x8 = Divide by 24. SPI clock is 33.33 MHz.
+ 32, //0x9 = Divide by 32. SPI clock is 25 MHz.
+ 40, //0xA = Divide by 40. SPI clock is 20 MHz.
+ 50, //0xB = Divide by 50. SPI clock is 16 MHz.
+ 64, //0xC = Divide by 64. SPI clock is 12.5 MHz.
+ 128, //0xD = Divide by 128. SPI clock is 6.25 MHz.
+ -1 //End of list
+};
+
+static bool cdns_xspi_reset_dll(struct cdns_xspi_dev *cdns_xspi)
+{
+ u32 dll_cntrl = readl(cdns_xspi->iobase +
+ CDNS_XSPI_RF_MINICTRL_REGS_DLL_PHY_CTRL);
+ u32 dll_lock;
+
+ /*Reset DLL*/
+ dll_cntrl |= CDNS_XSPI_DLL_RST_N;
+ writel(dll_cntrl, cdns_xspi->iobase +
+ CDNS_XSPI_RF_MINICTRL_REGS_DLL_PHY_CTRL);
+
+ /*Wait for DLL lock*/
+ return readl_relaxed_poll_timeout(cdns_xspi->iobase +
+ CDNS_XSPI_INTR_STATUS_REG,
+ dll_lock, ((dll_lock & CDNS_XSPI_DLL_LOCK) == 1), 10, 10000);
+}
+
+static void cdns_configure_phy_register_io(struct cdns_xspi_dev *cdns_xspi,
+ const char *prop_name,
+ u64 default_value, u64 offset)
+{
+ struct device_node *node_prop = cdns_xspi->pdev->dev.of_node;
+ u64 phy_cfg;
+
+ if (of_property_read_u64(node_prop, prop_name, &phy_cfg))
+ phy_cfg = default_value;
+ writel(phy_cfg,
+ cdns_xspi->iobase + offset);
+}
+
+static void cdns_configure_phy_register_aux(struct cdns_xspi_dev *cdns_xspi,
+ const char *prop_name,
+ u64 default_value, u64 offset)
+{
+ struct device_node *node_prop = cdns_xspi->pdev->dev.of_node;
+ u64 phy_cfg;
+
+ if (of_property_read_u64(node_prop, "cdns,dll-phy-control", &phy_cfg))
+ phy_cfg = default_value;
+ writel(phy_cfg,
+ cdns_xspi->auxbase + offset);
+}
+
+//Static confiuration of PHY
+static bool cdns_xspi_configure_phy(struct cdns_xspi_dev *cdns_xspi)
+{
+ cdns_configure_phy_register_io(cdns_xspi,
+ "cdns,dll-phy-control",
+ REGS_DLL_PHY_CTRL,
+ CDNS_XSPI_RF_MINICTRL_REGS_DLL_PHY_CTRL);
+ cdns_configure_phy_register_aux(cdns_xspi,
+ "cdns,rfile-phy-control",
+ CTB_RFILE_PHY_CTRL,
+ CDNS_XSPI_PHY_CTB_RFILE_PHY_CTRL);
+ cdns_configure_phy_register_aux(cdns_xspi,
+ "cdns,rfile-phy-tsel",
+ RFILE_PHY_TSEL,
+ CDNS_XSPI_PHY_CTB_RFILE_PHY_TSEL);
+ cdns_configure_phy_register_aux(cdns_xspi,
+ "cdns,phy-dq-timing",
+ RFILE_PHY_DQ_TIMING,
+ CDNS_XSPI_PHY_DATASLICE_RFILE_PHY_DQ_TIMING);
+ cdns_configure_phy_register_aux(cdns_xspi,
+ "cdns,phy-dqs-timing",
+ RFILE_PHY_DQS_TIMING,
+ CDNS_XSPI_PHY_DATASLICE_RFILE_PHY_DQS_TIMING);
+ cdns_configure_phy_register_aux(cdns_xspi,
+ "cdns,phy-gate-lpbk-ctrl",
+ RFILE_PHY_GATE_LPBK_CTRL,
+ CDNS_XSPI_PHY_DATASLICE_RFILE_PHY_GATE_LPBK_CTRL);
+ cdns_configure_phy_register_aux(cdns_xspi,
+ "cdns,phy-dll-master-ctrl",
+ RFILE_PHY_DLL_MASTER_CTRL,
+ CDNS_XSPI_PHY_DATASLICE_RFILE_PHY_GATE_LPBK_CTRL);
+ cdns_configure_phy_register_aux(cdns_xspi,
+ "cdns,phy-dll-slave-ctrl",
+ RFILE_PHY_DLL_SLAVE_CTRL,
+ CDNS_XSPI_PHY_DATASLICE_RFILE_PHY_DLL_SLAVE_CTRL);
+
+ return cdns_xspi_reset_dll(cdns_xspi);
+}
+
+// Find max avalible clock
+static bool cdns_mrvl_xspi_setup_clock(struct cdns_xspi_dev *cdns_xspi,
+ int requested_clk)
+{
+ int i = 0;
+ int clk_val;
+ u32 clk_reg;
+ bool update_clk = false;
+
+ while (cdns_mrvl_xspi_clk_div_list[i] > 0) {
+ clk_val = CDNS_MRVL_XSPI_CLOCK_DIVIDED(
+ cdns_mrvl_xspi_clk_div_list[i]);
+ if (clk_val <= requested_clk)
+ break;
+ i++;
+ }
+
+ if (cdns_mrvl_xspi_clk_div_list[i] == -1) {
+ dev_info(cdns_xspi->dev,
+ "Unable to find clk div for CLK: %d - using 6.25MHz\n",
+ requested_clk);
+ i = 0x0D;
+ } else {
+ dev_dbg(cdns_xspi->dev, "Found clk div: %d, clk val: %d\n",
+ cdns_mrvl_xspi_clk_div_list[i],
+ CDNS_MRVL_XSPI_CLOCK_DIVIDED(
+ cdns_mrvl_xspi_clk_div_list[i]));
+ }
+
+ clk_reg = readl(cdns_xspi->auxbase + CDNS_MRVL_XSPI_CLK_CTRL_AUX_REG);
+
+ if (FIELD_GET(CDNS_MRVL_XSPI_CLK_DIV, clk_reg) != i) {
+ clk_reg &= ~CDNS_MRVL_XSPI_CLK_ENABLE;
+ writel(clk_reg,
+ cdns_xspi->auxbase + CDNS_MRVL_XSPI_CLK_CTRL_AUX_REG);
+ clk_reg = FIELD_PREP(CDNS_MRVL_XSPI_CLK_DIV, i);
+ clk_reg &= ~CDNS_MRVL_XSPI_CLK_DIV;
+ clk_reg |= FIELD_PREP(CDNS_MRVL_XSPI_CLK_DIV, i);
+ clk_reg |= CDNS_MRVL_XSPI_CLK_ENABLE;
+ clk_reg |= CDNS_MRVL_XSPI_IRQ_ENABLE;
+ update_clk = true;
+ }
+
+ if (update_clk)
+ writel(clk_reg,
+ cdns_xspi->auxbase + CDNS_MRVL_XSPI_CLK_CTRL_AUX_REG);
+
+ return update_clk;
+}
+
static int cdns_xspi_wait_for_controller_idle(struct cdns_xspi_dev *cdns_xspi)
{
u32 ctrl_stat;
@@ -295,6 +498,10 @@ static void cdns_xspi_set_interrupts(struct cdns_xspi_dev *cdns_xspi,
bool enabled)
{
u32 intr_enable;
+ u32 irq_status;
+
+ irq_status = readl(cdns_xspi->iobase + CDNS_XSPI_INTR_STATUS_REG);
+ writel(irq_status, cdns_xspi->iobase + CDNS_XSPI_INTR_STATUS_REG);
intr_enable = readl(cdns_xspi->iobase + CDNS_XSPI_INTR_ENABLE_REG);
if (enabled)
@@ -319,6 +526,9 @@ static int cdns_xspi_controller_init(struct cdns_xspi_dev *cdns_xspi)
return -EIO;
}
+ writel(FIELD_PREP(CDNS_XSPI_CTRL_WORK_MODE, CDNS_XSPI_WORK_MODE_STIG),
+ cdns_xspi->iobase + CDNS_XSPI_CTRL_CONFIG_REG);
+
ctrl_features = readl(cdns_xspi->iobase + CDNS_XSPI_CTRL_FEATURES_REG);
cdns_xspi->hw_num_banks = FIELD_GET(CDNS_XSPI_NUM_BANKS, ctrl_features);
cdns_xspi_set_interrupts(cdns_xspi, false);
@@ -326,6 +536,70 @@ static int cdns_xspi_controller_init(struct cdns_xspi_dev *cdns_xspi)
return 0;
}
+static void mrvl_ioreadq(void __iomem *addr, void *buf, int len)
+{
+ int i = 0;
+ int rcount = len / 8;
+ int rcount_nf = len % 8;
+ uint64_t tmp;
+ uint64_t *buf64 = (uint64_t *)buf;
+
+ if (((uint64_t)buf % 8) == 0) {
+ for (i = 0; i < rcount; i++)
+ *buf64++ = readq(addr);
+ } else {
+ for (i = 0; i < rcount; i++) {
+ tmp = readq(addr);
+ memcpy(buf+(i*8), &tmp, 8);
+ }
+ }
+
+ if (rcount_nf != 0) {
+ tmp = readq(addr);
+ memcpy(buf+(i*8), &tmp, rcount_nf);
+ }
+}
+
+static void mrvl_iowriteq(void __iomem *addr, const void *buf, int len)
+{
+ int i = 0;
+ int rcount = len / 8;
+ int rcount_nf = len % 8;
+ uint64_t tmp;
+ uint64_t *buf64 = (uint64_t *)buf;
+
+ if (((uint64_t)buf % 8) == 0) {
+ for (i = 0; i < rcount; i++)
+ writeq(*buf64++, addr);
+ } else {
+ for (i = 0; i < rcount; i++) {
+ memcpy(&tmp, buf+(i*8), 8);
+ writeq(tmp, addr);
+ }
+ }
+
+ if (rcount_nf != 0) {
+ memcpy(&tmp, buf+(i*8), rcount_nf);
+ writeq(tmp, addr);
+ }
+}
+
+static void cdns_xspi_sdma_memread(struct cdns_xspi_dev *cdns_xspi, int len)
+{
+ if (cdns_xspi->mrvl_hw_overlay)
+ mrvl_ioreadq(cdns_xspi->sdmabase, cdns_xspi->in_buffer, len);
+ else
+ ioread8_rep(cdns_xspi->sdmabase, cdns_xspi->in_buffer, len);
+}
+
+static void cdns_xspi_sdma_memwrite(struct cdns_xspi_dev *cdns_xspi, int len)
+{
+ if (cdns_xspi->mrvl_hw_overlay)
+ mrvl_iowriteq(cdns_xspi->sdmabase, cdns_xspi->out_buffer, len);
+ else
+ iowrite8_rep(cdns_xspi->sdmabase, cdns_xspi->out_buffer, len);
+}
+
static void cdns_xspi_sdma_handle(struct cdns_xspi_dev *cdns_xspi)
{
u32 sdma_size, sdma_trd_info;
@@ -337,13 +611,11 @@ static void cdns_xspi_sdma_handle(struct cdns_xspi_dev *cdns_xspi)
switch (sdma_dir) {
case CDNS_XSPI_SDMA_DIR_READ:
- ioread8_rep(cdns_xspi->sdmabase,
- cdns_xspi->in_buffer, sdma_size);
+ cdns_xspi_sdma_memread(cdns_xspi, sdma_size);
break;
case CDNS_XSPI_SDMA_DIR_WRITE:
- iowrite8_rep(cdns_xspi->sdmabase,
- cdns_xspi->out_buffer, sdma_size);
+ cdns_xspi_sdma_memwrite(cdns_xspi, sdma_size);
break;
}
}
@@ -421,6 +693,9 @@ static int cdns_xspi_mem_op(struct cdns_xspi_dev *cdns_xspi,
if (cdns_xspi->cur_cs != spi_get_chipselect(mem->spi, 0))
cdns_xspi->cur_cs = spi_get_chipselect(mem->spi, 0);
+ if (cdns_xspi->mrvl_hw_overlay)
+ cdns_mrvl_xspi_setup_clock(cdns_xspi, mem->spi->max_speed_hz);
+
return cdns_xspi_send_stig_command(cdns_xspi, op,
(dir != SPI_MEM_NO_DATA));
}
@@ -461,6 +736,10 @@ static irqreturn_t cdns_xspi_irq_handler(int this_irq, void *dev)
irq_status = readl(cdns_xspi->iobase + CDNS_XSPI_INTR_STATUS_REG);
writel(irq_status, cdns_xspi->iobase + CDNS_XSPI_INTR_STATUS_REG);
+ if (cdns_xspi->mrvl_hw_overlay)
+ writel(CDNS_MRVL_MSIX_CLEAR_IRQ,
+ cdns_xspi->auxbase + CDNS_MRVL_XSPI_SPIX_INTR_AUX);
+
if (irq_status &
(CDNS_XSPI_SDMA_ERROR | CDNS_XSPI_SDMA_TRIGGER |
CDNS_XSPI_STIG_DONE)) {
@@ -534,11 +813,23 @@ static void cdns_xspi_print_phy_config(struct cdns_xspi_dev *cdns_xspi)
readl(cdns_xspi->auxbase + CDNS_XSPI_CCP_PHY_DLL_SLAVE_CTRL));
}
+static int cdns_xspi_setup(struct spi_device *spi_dev)
+{
+ struct cdns_xspi_dev *cdns_xspi = spi_controller_get_devdata(
+ spi_dev->controller);
+
+ if (cdns_xspi->mrvl_hw_overlay)
+ cdns_mrvl_xspi_setup_clock(cdns_xspi, spi_dev->max_speed_hz);
+
+ return 0;
+}
+
static int cdns_xspi_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct spi_controller *host = NULL;
struct cdns_xspi_dev *cdns_xspi = NULL;
+ const struct cdns_xspi_driver_data *drv_data;
struct resource *res;
int ret;
@@ -550,10 +841,16 @@ static int cdns_xspi_probe(struct platform_device *pdev)
SPI_RX_DUAL | SPI_RX_QUAD | SPI_TX_OCTAL | SPI_RX_OCTAL |
SPI_MODE_0 | SPI_MODE_3;
+ drv_data = of_device_get_match_data(dev);
+
host->mem_ops = &cadence_xspi_mem_ops;
host->dev.of_node = pdev->dev.of_node;
+ host->dev.fwnode = pdev->dev.fwnode;
host->bus_num = -1;
+ if (drv_data->mrvl_hw_overlay)
+ host->setup = cdns_xspi_setup;
+
platform_set_drvdata(pdev, host);
cdns_xspi = spi_controller_get_devdata(host);
@@ -565,23 +862,27 @@ static int cdns_xspi_probe(struct platform_device *pdev)
init_completion(&cdns_xspi->auto_cmd_complete);
init_completion(&cdns_xspi->sdma_complete);
+ cdns_xspi->mrvl_hw_overlay = drv_data->mrvl_hw_overlay;
+
ret = cdns_xspi_of_get_plat_data(pdev);
if (ret)
return -ENODEV;
- cdns_xspi->iobase = devm_platform_ioremap_resource_byname(pdev, "io");
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ cdns_xspi->iobase = devm_ioremap_resource(dev, res);
if (IS_ERR(cdns_xspi->iobase)) {
dev_err(dev, "Failed to remap controller base address\n");
return PTR_ERR(cdns_xspi->iobase);
}
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sdma");
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
cdns_xspi->sdmabase = devm_ioremap_resource(dev, res);
if (IS_ERR(cdns_xspi->sdmabase))
return PTR_ERR(cdns_xspi->sdmabase);
cdns_xspi->sdmasize = resource_size(res);
- cdns_xspi->auxbase = devm_platform_ioremap_resource_byname(pdev, "aux");
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+ cdns_xspi->auxbase = devm_ioremap_resource(dev, res);
if (IS_ERR(cdns_xspi->auxbase)) {
dev_err(dev, "Failed to remap AUX address\n");
return PTR_ERR(cdns_xspi->auxbase);
@@ -598,8 +899,12 @@ static int cdns_xspi_probe(struct platform_device *pdev)
return ret;
}
- cdns_xspi_print_phy_config(cdns_xspi);
+ if (drv_data->mrvl_hw_overlay) {
+ cdns_mrvl_xspi_setup_clock(cdns_xspi, MRVL_DEFAULT_CLK);
+ cdns_xspi_configure_phy(cdns_xspi);
+ }
+ cdns_xspi_print_phy_config(cdns_xspi);
ret = cdns_xspi_controller_init(cdns_xspi);
if (ret) {
dev_err(dev, "Failed to initialize controller\n");
@@ -622,6 +927,11 @@ static int cdns_xspi_probe(struct platform_device *pdev)
static const struct of_device_id cdns_xspi_of_match[] = {
{
.compatible = "cdns,xspi-nor",
+ .data = &cdns_driver_data,
+ },
+ {
+ .compatible = "mrvl,xspi-nor",
+ .data = &mrvl_driver_data,
},
{ /* end of table */}
};
--
2.43.0
Powered by blists - more mailing lists