From 563fd7672f0aa14120e76ed3f82906dbdcc9e375 Mon Sep 17 00:00:00 2001 From: Jon Lin Date: Sun, 25 Feb 2024 11:15:08 +0800 Subject: [PATCH 1/3] spi: rockchip-sfc: Using normal memory for dma Nornal memory CPU copy with cache invalidate is more efficient than uncache memory copy. Change-Id: I04e9e9a532bba14858b6f4baa969e3e848f6f032 Signed-off-by: Jon Lin --- drivers/spi/spi-rockchip-sfc.c | 60 ++++++++++++++++++++++++---------- 1 file changed, 42 insertions(+), 18 deletions(-) diff --git a/drivers/spi/spi-rockchip-sfc.c b/drivers/spi/spi-rockchip-sfc.c index d9e0fe501205..79bbd6b59a96 100644 --- a/drivers/spi/spi-rockchip-sfc.c +++ b/drivers/spi/spi-rockchip-sfc.c @@ -160,7 +160,7 @@ #define SFC_MAX_CHIPSELECT_NUM 2 #define SFC_MAX_IOSIZE_VER3 (512 * 31) -#define SFC_MAX_IOSIZE_VER4 (0xFFFFFFFFU) +#define SFC_MAX_IOSIZE_VER4 (0x10000U) /* Although up to 4GB, 64KB is enough with less mem reserved */ /* DMA is only enabled for large data transmission */ #define SFC_DMA_TRANS_THRETHOLD (0x40) @@ -222,6 +222,9 @@ static u16 rockchip_sfc_get_version(struct rockchip_sfc *sfc) static u32 rockchip_sfc_get_max_iosize(struct rockchip_sfc *sfc) { + if (sfc->version >= SFC_VER_4) + return SFC_MAX_IOSIZE_VER4; + return SFC_MAX_IOSIZE_VER3; } @@ -488,20 +491,46 @@ static int rockchip_sfc_xfer_data_dma(struct rockchip_sfc *sfc, const struct spi_mem_op *op, u32 len) { int ret; +#ifdef ROCKCHIP_SFC_VERBOSE + ktime_t start_time; + ktime_t end_time; + unsigned long us = 0; +#endif dev_dbg(sfc->dev, "sfc xfer_dma len=%x\n", len); - if (op->data.dir == SPI_MEM_DATA_OUT) + if (op->data.dir == SPI_MEM_DATA_OUT) { memcpy(sfc->buffer, op->data.buf.out, len); + dma_sync_single_for_device(sfc->dev, sfc->dma_buffer, len, DMA_TO_DEVICE); + } +#ifdef ROCKCHIP_SFC_VERBOSE + start_time = ktime_get(); +#endif ret = rockchip_sfc_fifo_transfer_dma(sfc, sfc->dma_buffer, len); if (!wait_for_completion_timeout(&sfc->cp, msecs_to_jiffies(2000))) { dev_err(sfc->dev, "DMA wait for transfer finish timeout\n"); ret = -ETIMEDOUT; } +#ifdef ROCKCHIP_SFC_VERBOSE + end_time = ktime_get(); + us = ktime_to_us(ktime_sub(end_time, start_time)); + dev_err(sfc->dev, "sfc io %d cost %ldus speed:%ldKB/S %llx\n", len, us, len * 1000 / us, sfc->dma_buffer); +#endif rockchip_sfc_irq_mask(sfc, SFC_IMR_DMA); - if (op->data.dir == SPI_MEM_DATA_IN) + +#ifdef ROCKCHIP_SFC_VERBOSE + start_time = ktime_get(); +#endif + if (op->data.dir == SPI_MEM_DATA_IN) { + dma_sync_single_for_cpu(sfc->dev, sfc->dma_buffer, len, DMA_FROM_DEVICE); memcpy(op->data.buf.in, sfc->buffer, len); + } +#ifdef ROCKCHIP_SFC_VERBOSE + end_time = ktime_get(); + us = ktime_to_us(ktime_sub(end_time, start_time)); + dev_err(sfc->dev, "sfc cp %d cost %ldus speed:%ldKB/S\n", len, us, len * 1000 / us); +#endif return ret; } @@ -780,20 +809,6 @@ static int rockchip_sfc_probe(struct platform_device *pdev) sfc->use_dma = !of_property_read_bool(sfc->dev->of_node, "rockchip,sfc-no-dma"); - if (sfc->use_dma) { - ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); - if (ret) { - dev_warn(dev, "Unable to set dma mask\n"); - return ret; - } - - sfc->buffer = dmam_alloc_coherent(dev, SFC_MAX_IOSIZE_VER3, - &sfc->dma_buffer, - GFP_KERNEL); - if (!sfc->buffer) - return -ENOMEM; - } - ret = clk_prepare_enable(sfc->hclk); if (ret) { dev_err(&pdev->dev, "Failed to enable ahb clk\n"); @@ -836,8 +851,8 @@ static int rockchip_sfc_probe(struct platform_device *pdev) if (ret) goto err_irq; - sfc->max_iosize = rockchip_sfc_get_max_iosize(sfc); sfc->version = rockchip_sfc_get_version(sfc); + sfc->max_iosize = rockchip_sfc_get_max_iosize(sfc); pm_runtime_set_autosuspend_delay(dev, ROCKCHIP_AUTOSUSPEND_DELAY); pm_runtime_use_autosuspend(dev); @@ -845,6 +860,13 @@ static int rockchip_sfc_probe(struct platform_device *pdev) pm_runtime_enable(dev); pm_runtime_get_noresume(dev); + if (sfc->use_dma) { + sfc->buffer = (u8 *)__get_free_pages(GFP_KERNEL | GFP_DMA32, get_order(sfc->max_iosize)); + if (!sfc->buffer) + return -ENOMEM; + sfc->dma_buffer = virt_to_phys(sfc->buffer); + } + ret = spi_register_master(master); if (ret) goto err_register; @@ -855,6 +877,7 @@ static int rockchip_sfc_probe(struct platform_device *pdev) return 0; err_register: + free_pages((unsigned long)sfc->buffer, get_order(sfc->max_iosize)); pm_runtime_disable(sfc->dev); pm_runtime_set_suspended(sfc->dev); pm_runtime_dont_use_autosuspend(sfc->dev); @@ -871,6 +894,7 @@ static int rockchip_sfc_remove(struct platform_device *pdev) struct spi_master *master = platform_get_drvdata(pdev); struct rockchip_sfc *sfc = platform_get_drvdata(pdev); + free_pages((unsigned long)sfc->buffer, get_order(sfc->max_iosize)); spi_unregister_master(master); clk_disable_unprepare(sfc->clk); From e26de7b6cee480dee4828a0f3921cca50a74bfca Mon Sep 17 00:00:00 2001 From: Jon Lin Date: Wed, 21 Feb 2024 21:25:34 +0800 Subject: [PATCH 2/3] spi: rockchip-sfc: Set the max speed depend on the IP version After SFC_VER_8, the FSPI support ddr mode, and the working clock of the controller is twice that of the IO clock. Change-Id: I66307ad96960ce7e4daaeab5cf6e191f0ee3778d Signed-off-by: Jon Lin --- drivers/spi/spi-rockchip-sfc.c | 44 +++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/drivers/spi/spi-rockchip-sfc.c b/drivers/spi/spi-rockchip-sfc.c index 79bbd6b59a96..fdbfd8b7ba68 100644 --- a/drivers/spi/spi-rockchip-sfc.c +++ b/drivers/spi/spi-rockchip-sfc.c @@ -115,6 +115,7 @@ #define SFC_VER_5 0x5 #define SFC_VER_6 0x6 #define SFC_VER_8 0x8 +#define SFC_VER_10 0x10 /* Delay line controller resiter */ #define SFC_DLL_CTRL0 0x3C @@ -168,8 +169,10 @@ /* Maximum clock values from datasheet suggest keeping clock value under * 150MHz. No minimum or average value is suggested. */ -#define SFC_MAX_SPEED (150 * 1000 * 1000) -#define SFC_DLL_THRESHOLD_RATE (50 * 1000 * 1000) +#define SFC_MAX_SPEED (150 * 1000 * 1000) +#define SFC_DLL_THRESHOLD_RATE (50 * 1000 * 1000) +#define SFC_X2_MAX_SPEED (300 * 1000 * 1000) +#define SFC_X2_DLL_THRESHOLD_RATE (100 * 1000 * 1000) #define SFC_DLL_TRANING_STEP 10 /* Training step */ #define SFC_DLL_TRANING_VALID_WINDOW 80 /* Valid DLL winbow */ @@ -228,6 +231,22 @@ static u32 rockchip_sfc_get_max_iosize(struct rockchip_sfc *sfc) return SFC_MAX_IOSIZE_VER3; } +static u32 rockchip_sfc_get_max_rate(struct rockchip_sfc *sfc) +{ + if (sfc->version >= SFC_VER_8) + return SFC_X2_MAX_SPEED; + + return SFC_MAX_SPEED; +} + +static u32 rockchip_sfc_get_max_dll_threshold(struct rockchip_sfc *sfc) +{ + if (sfc->version >= SFC_VER_8) + return SFC_X2_DLL_THRESHOLD_RATE; + + return SFC_DLL_THRESHOLD_RATE; +} + static u32 rockchip_sfc_get_max_dll_cells(struct rockchip_sfc *sfc) { switch (rockchip_sfc_get_version(sfc)) { @@ -595,14 +614,14 @@ static void rockchip_sfc_delay_lines_tuning(struct rockchip_sfc *sfc, struct spi bool dll_valid = false; u8 cs = mem->spi->chip_select; - clk_set_rate(sfc->clk, SFC_DLL_THRESHOLD_RATE); + clk_set_rate(sfc->clk, rockchip_sfc_get_max_dll_threshold(sfc)); op.data.buf.in = &id; rockchip_sfc_exec_op_bypass(sfc, mem, &op); if ((0xFF == id[0] && 0xFF == id[1]) || (0x00 == id[0] && 0x00 == id[1])) { dev_dbg(sfc->dev, "no dev, dll by pass\n"); clk_set_rate(sfc->clk, sfc->speed[cs]); - sfc->speed[cs] = SFC_DLL_THRESHOLD_RATE; + sfc->speed[cs] = rockchip_sfc_get_max_dll_threshold(sfc); return; } @@ -654,11 +673,11 @@ static void rockchip_sfc_delay_lines_tuning(struct rockchip_sfc *sfc, struct spi dev_err(sfc->dev, "%d %d dll training failed in %dMHz, reduce the frequency\n", left, right, sfc->speed[cs]); rockchip_sfc_set_delay_lines(sfc, 0, cs); - clk_set_rate(sfc->clk, SFC_DLL_THRESHOLD_RATE); - mem->spi->max_speed_hz = SFC_DLL_THRESHOLD_RATE; - sfc->cur_speed = SFC_DLL_THRESHOLD_RATE; + clk_set_rate(sfc->clk, rockchip_sfc_get_max_dll_threshold(sfc)); + mem->spi->max_speed_hz = rockchip_sfc_get_max_dll_threshold(sfc); + sfc->cur_speed = rockchip_sfc_get_max_dll_threshold(sfc); sfc->cur_real_speed = clk_get_rate(sfc->clk); - sfc->speed[cs] = SFC_DLL_THRESHOLD_RATE; + sfc->speed[cs] = rockchip_sfc_get_max_dll_threshold(sfc); } } @@ -684,7 +703,7 @@ static int rockchip_sfc_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op sfc->cur_speed = mem->spi->max_speed_hz; sfc->cur_real_speed = clk_get_rate(sfc->clk); if (rockchip_sfc_get_version(sfc) >= SFC_VER_4) { - if (clk_get_rate(sfc->clk) > SFC_DLL_THRESHOLD_RATE) + if (clk_get_rate(sfc->clk) > rockchip_sfc_get_max_dll_threshold(sfc)) rockchip_sfc_delay_lines_tuning(sfc, mem); else rockchip_sfc_set_delay_lines(sfc, 0, cs); @@ -770,8 +789,6 @@ static int rockchip_sfc_probe(struct platform_device *pdev) master->flags = SPI_MASTER_HALF_DUPLEX; master->mem_ops = &rockchip_sfc_mem_ops; master->dev.of_node = pdev->dev.of_node; - master->mode_bits = SPI_TX_QUAD | SPI_TX_DUAL | SPI_RX_QUAD | SPI_RX_DUAL; - master->max_speed_hz = SFC_MAX_SPEED; master->num_chipselect = SFC_MAX_CHIPSELECT_NUM; sfc = spi_master_get_devdata(master); @@ -854,6 +871,11 @@ static int rockchip_sfc_probe(struct platform_device *pdev) sfc->version = rockchip_sfc_get_version(sfc); sfc->max_iosize = rockchip_sfc_get_max_iosize(sfc); + master->mode_bits = SPI_TX_QUAD | SPI_TX_DUAL | SPI_RX_QUAD | SPI_RX_DUAL; + master->max_speed_hz = rockchip_sfc_get_max_rate(sfc); + if (sfc->version >= SFC_VER_8) + master->mode_bits |= SPI_TX_OCTAL | SPI_RX_OCTAL; + pm_runtime_set_autosuspend_delay(dev, ROCKCHIP_AUTOSUSPEND_DELAY); pm_runtime_use_autosuspend(dev); pm_runtime_set_active(dev); From 784e52a797a936672fdcb4b5ae32feae5fe47e71 Mon Sep 17 00:00:00 2001 From: Jon Lin Date: Thu, 22 Feb 2024 20:54:55 +0800 Subject: [PATCH 3/3] spi: rockchip-sfc: Support sfc-cs-gpio Change-Id: I24bfbf39ed7bea0596f299d3413159c7e2038ba8 Signed-off-by: Jon Lin --- drivers/spi/spi-rockchip-sfc.c | 81 ++++++++++++++++++++++++++++++++-- 1 file changed, 77 insertions(+), 4 deletions(-) diff --git a/drivers/spi/spi-rockchip-sfc.c b/drivers/spi/spi-rockchip-sfc.c index fdbfd8b7ba68..01dedba02fd7 100644 --- a/drivers/spi/spi-rockchip-sfc.c +++ b/drivers/spi/spi-rockchip-sfc.c @@ -23,6 +23,7 @@ #include #include #include +#include /* System control */ #define SFC_CTRL 0x0 @@ -195,6 +196,7 @@ struct rockchip_sfc { u32 max_iosize; u32 dll_cells[SFC_MAX_CHIPSELECT_NUM]; u16 version; + struct gpio_desc **cs_gpiods; }; static int rockchip_sfc_reset(struct rockchip_sfc *sfc) @@ -407,13 +409,13 @@ static int rockchip_sfc_xfer_setup(struct rockchip_sfc *sfc, /* set the Controller */ ctrl |= SFC_CTRL_PHASE_SEL_NEGETIVE; - cmd |= mem->spi->chip_select << SFC_CMD_CS_SHIFT; + cmd |= cs << SFC_CMD_CS_SHIFT; dev_dbg(sfc->dev, "sfc addr.nbytes=%x(x%d) dummy.nbytes=%x(x%d)\n", op->addr.nbytes, op->addr.buswidth, op->dummy.nbytes, op->dummy.buswidth); - dev_dbg(sfc->dev, "sfc ctrl=%x cmd=%x addr=%llx len=%x\n", - ctrl, cmd, op->addr.val, len); + dev_dbg(sfc->dev, "sfc ctrl=%x cmd=%x addr=%llx len=%x cs=%d\n", + ctrl, cmd, op->addr.val, len, cs); writel(ctrl, sfc->regbase + cs * SFC_CS1_REG_OFFSET + SFC_CTRL); writel(cmd, sfc->regbase + SFC_CMD); @@ -582,14 +584,27 @@ static int rockchip_sfc_xfer_done(struct rockchip_sfc *sfc, u32 timeout_us) return ret; } +static void rockchip_sfc_set_cs_gpio(struct rockchip_sfc *sfc, u8 cs, bool enable) +{ + if (sfc->cs_gpiods) { + if (has_acpi_companion(sfc->dev)) + gpiod_set_value_cansleep(sfc->cs_gpiods[cs], !enable); + else + /* Polarity handled by GPIO library */ + gpiod_set_value_cansleep(sfc->cs_gpiods[cs], enable); + } +} + static int rockchip_sfc_exec_op_bypass(struct rockchip_sfc *sfc, struct spi_mem *mem, const struct spi_mem_op *op) { u32 len = min_t(u32, op->data.nbytes, sfc->max_iosize); + u8 cs = mem->spi->chip_select; u32 ret; rockchip_sfc_adjust_op_work((struct spi_mem_op *)op); + rockchip_sfc_set_cs_gpio(sfc, cs, true); rockchip_sfc_xfer_setup(sfc, mem, op, len); ret = rockchip_sfc_xfer_data_poll(sfc, op, len); if (ret != len) { @@ -598,7 +613,10 @@ static int rockchip_sfc_exec_op_bypass(struct rockchip_sfc *sfc, return -EIO; } - return rockchip_sfc_xfer_done(sfc, 100000); + ret = rockchip_sfc_xfer_done(sfc, 100000); + rockchip_sfc_set_cs_gpio(sfc, cs, false); + + return ret; } static void rockchip_sfc_delay_lines_tuning(struct rockchip_sfc *sfc, struct spi_mem *mem) @@ -714,6 +732,7 @@ static int rockchip_sfc_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op } rockchip_sfc_adjust_op_work((struct spi_mem_op *)op); + rockchip_sfc_set_cs_gpio(sfc, cs, true); rockchip_sfc_xfer_setup(sfc, mem, op, len); if (len) { if (likely(sfc->use_dma) && len >= SFC_DMA_TRANS_THRETHOLD && !(len & 0x3)) { @@ -734,6 +753,7 @@ static int rockchip_sfc_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op ret = rockchip_sfc_xfer_done(sfc, 100000); out: + rockchip_sfc_set_cs_gpio(sfc, cs, false); pm_runtime_mark_last_busy(sfc->dev); pm_runtime_put_autosuspend(sfc->dev); @@ -773,6 +793,53 @@ static irqreturn_t rockchip_sfc_irq_handler(int irq, void *dev_id) return IRQ_NONE; } +static int rockchip_sfc_get_gpio_descs(struct spi_controller *ctlr, struct rockchip_sfc *sfc) +{ + int nb, i; + struct gpio_desc **cs; + struct device *dev = &ctlr->dev; + unsigned int num_cs_gpios = 0; + + nb = gpiod_count(dev, "sfc-cs"); + ctlr->num_chipselect = max_t(int, nb, ctlr->num_chipselect); + + if (nb == 0 || nb == -ENOENT) + return 0; + else if (nb < 0) + return nb; + + cs = devm_kcalloc(dev, ctlr->num_chipselect, sizeof(*cs), + GFP_KERNEL); + if (!cs) + return -ENOMEM; + sfc->cs_gpiods = cs; + + for (i = 0; i < nb; i++) { + cs[i] = devm_gpiod_get_index_optional(dev, "sfc-cs", i, + GPIOD_OUT_LOW); + if (IS_ERR(cs[i])) + return PTR_ERR(cs[i]); + + if (cs[i]) { + /* + * If we find a CS GPIO, name it after the device and + * chip select line. + */ + char *gpioname; + + gpioname = devm_kasprintf(dev, GFP_KERNEL, "%s CS%d", + dev_name(dev), i); + if (!gpioname) + return -ENOMEM; + gpiod_set_consumer_name(cs[i], gpioname); + num_cs_gpios++; + continue; + } + } + + return 0; +} + static int rockchip_sfc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -826,6 +893,12 @@ static int rockchip_sfc_probe(struct platform_device *pdev) sfc->use_dma = !of_property_read_bool(sfc->dev->of_node, "rockchip,sfc-no-dma"); + ret = rockchip_sfc_get_gpio_descs(master, sfc); + if (ret) { + dev_err(&pdev->dev, "Failed to get gpio_descs\n"); + return ret; + } + ret = clk_prepare_enable(sfc->hclk); if (ret) { dev_err(&pdev->dev, "Failed to enable ahb clk\n");