From 87a14a96bc323aff824fad8cdbe61b78eff22255 Mon Sep 17 00:00:00 2001 From: James Clark Date: Thu, 22 May 2025 15:51:33 +0100 Subject: [PATCH 01/62] spi: spi-fsl-dspi: Re-use one volatile regmap for both device types max_register overrides anything in the volatile ranges, so we can get away with sharing the same one for both types. In a later commit we'll add more devices so this avoids adding even more duplication. Also replace the max_register magic numbers with their register definitions so it's clearer what's going on. No functional changes. Reviewed-by: Vladimir Oltean Signed-off-by: James Clark Link: https://patch.msgid.link/20250522-james-nxp-spi-v2-4-bea884630cfb@linaro.org Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 863781ba6c16..09b2b25ed274 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -1209,6 +1209,7 @@ static const struct regmap_range dspi_volatile_ranges[] = { regmap_reg_range(SPI_MCR, SPI_TCR), regmap_reg_range(SPI_SR, SPI_SR), regmap_reg_range(SPI_PUSHR, SPI_RXFR3), + regmap_reg_range(SPI_SREX, SPI_SREX), }; static const struct regmap_access_table dspi_volatile_table = { @@ -1220,31 +1221,19 @@ static const struct regmap_config dspi_regmap_config = { .reg_bits = 32, .val_bits = 32, .reg_stride = 4, - .max_register = 0x88, + .max_register = SPI_RXFR3, .volatile_table = &dspi_volatile_table, .rd_table = &dspi_access_table, .wr_table = &dspi_access_table, }; -static const struct regmap_range dspi_xspi_volatile_ranges[] = { - regmap_reg_range(SPI_MCR, SPI_TCR), - regmap_reg_range(SPI_SR, SPI_SR), - regmap_reg_range(SPI_PUSHR, SPI_RXFR3), - regmap_reg_range(SPI_SREX, SPI_SREX), -}; - -static const struct regmap_access_table dspi_xspi_volatile_table = { - .yes_ranges = dspi_xspi_volatile_ranges, - .n_yes_ranges = ARRAY_SIZE(dspi_xspi_volatile_ranges), -}; - static const struct regmap_config dspi_xspi_regmap_config[] = { { .reg_bits = 32, .val_bits = 32, .reg_stride = 4, - .max_register = 0x13c, - .volatile_table = &dspi_xspi_volatile_table, + .max_register = SPI_SREX, + .volatile_table = &dspi_volatile_table, .rd_table = &dspi_access_table, .wr_table = &dspi_access_table, }, From 1672b0653212cecf11be9ef55bc2a2fabe0fa2ca Mon Sep 17 00:00:00 2001 From: James Clark Date: Thu, 22 May 2025 15:51:34 +0100 Subject: [PATCH 02/62] spi: spi-fsl-dspi: Define regmaps per device Refactor the regmaps so they can be defined per device rather than programmatically. This will allow us to add two new regmaps for S32G in a later commit. No functional changes. Reviewed-by: Vladimir Oltean Signed-off-by: James Clark Link: https://patch.msgid.link/20250522-james-nxp-spi-v2-5-bea884630cfb@linaro.org Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 136 ++++++++++++++++++++----------------- 1 file changed, 74 insertions(+), 62 deletions(-) diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 09b2b25ed274..437a8db9fa2b 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -122,6 +122,7 @@ struct fsl_dspi_devtype_data { enum dspi_trans_mode trans_mode; u8 max_clock_factor; int fifo_size; + const struct regmap_config *regmap; }; enum { @@ -137,60 +138,130 @@ enum { VF610, }; +static const struct regmap_range dspi_yes_ranges[] = { + regmap_reg_range(SPI_MCR, SPI_MCR), + regmap_reg_range(SPI_TCR, SPI_CTAR(3)), + regmap_reg_range(SPI_SR, SPI_TXFR3), + regmap_reg_range(SPI_RXFR0, SPI_RXFR3), + regmap_reg_range(SPI_CTARE(0), SPI_CTARE(3)), + regmap_reg_range(SPI_SREX, SPI_SREX), +}; + +static const struct regmap_access_table dspi_access_table = { + .yes_ranges = dspi_yes_ranges, + .n_yes_ranges = ARRAY_SIZE(dspi_yes_ranges), +}; + +static const struct regmap_range dspi_volatile_ranges[] = { + regmap_reg_range(SPI_MCR, SPI_TCR), + regmap_reg_range(SPI_SR, SPI_SR), + regmap_reg_range(SPI_PUSHR, SPI_RXFR3), + regmap_reg_range(SPI_SREX, SPI_SREX), +}; + +static const struct regmap_access_table dspi_volatile_table = { + .yes_ranges = dspi_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(dspi_volatile_ranges), +}; + +enum { + DSPI_REGMAP, + DSPI_XSPI_REGMAP, + DSPI_PUSHR, +}; + +static const struct regmap_config dspi_regmap_config[] = { + [DSPI_REGMAP] = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = SPI_RXFR3, + .volatile_table = &dspi_volatile_table, + .rd_table = &dspi_access_table, + .wr_table = &dspi_access_table, + }, + [DSPI_XSPI_REGMAP] = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = SPI_SREX, + .volatile_table = &dspi_volatile_table, + .rd_table = &dspi_access_table, + .wr_table = &dspi_access_table, + }, + [DSPI_PUSHR] = { + .name = "pushr", + .reg_bits = 16, + .val_bits = 16, + .reg_stride = 2, + .max_register = 0x2, + }, +}; + static const struct fsl_dspi_devtype_data devtype_data[] = { [VF610] = { .trans_mode = DSPI_DMA_MODE, .max_clock_factor = 2, .fifo_size = 4, + .regmap = &dspi_regmap_config[DSPI_REGMAP], }, [LS1021A] = { /* Has A-011218 DMA erratum */ .trans_mode = DSPI_XSPI_MODE, .max_clock_factor = 8, .fifo_size = 4, + .regmap = &dspi_regmap_config[DSPI_XSPI_REGMAP], }, [LS1012A] = { /* Has A-011218 DMA erratum */ .trans_mode = DSPI_XSPI_MODE, .max_clock_factor = 8, .fifo_size = 16, + .regmap = &dspi_regmap_config[DSPI_XSPI_REGMAP], }, [LS1028A] = { .trans_mode = DSPI_XSPI_MODE, .max_clock_factor = 8, .fifo_size = 4, + .regmap = &dspi_regmap_config[DSPI_XSPI_REGMAP], }, [LS1043A] = { /* Has A-011218 DMA erratum */ .trans_mode = DSPI_XSPI_MODE, .max_clock_factor = 8, .fifo_size = 16, + .regmap = &dspi_regmap_config[DSPI_XSPI_REGMAP], }, [LS1046A] = { /* Has A-011218 DMA erratum */ .trans_mode = DSPI_XSPI_MODE, .max_clock_factor = 8, .fifo_size = 16, + .regmap = &dspi_regmap_config[DSPI_XSPI_REGMAP], }, [LS2080A] = { .trans_mode = DSPI_XSPI_MODE, .max_clock_factor = 8, .fifo_size = 4, + .regmap = &dspi_regmap_config[DSPI_XSPI_REGMAP], }, [LS2085A] = { .trans_mode = DSPI_XSPI_MODE, .max_clock_factor = 8, .fifo_size = 4, + .regmap = &dspi_regmap_config[DSPI_XSPI_REGMAP], }, [LX2160A] = { .trans_mode = DSPI_XSPI_MODE, .max_clock_factor = 8, .fifo_size = 4, + .regmap = &dspi_regmap_config[DSPI_XSPI_REGMAP], }, [MCF5441X] = { .trans_mode = DSPI_DMA_MODE, .max_clock_factor = 8, .fifo_size = 16, + .regmap = &dspi_regmap_config[DSPI_REGMAP], }, }; @@ -1191,61 +1262,6 @@ static int dspi_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(dspi_pm, dspi_suspend, dspi_resume); -static const struct regmap_range dspi_yes_ranges[] = { - regmap_reg_range(SPI_MCR, SPI_MCR), - regmap_reg_range(SPI_TCR, SPI_CTAR(3)), - regmap_reg_range(SPI_SR, SPI_TXFR3), - regmap_reg_range(SPI_RXFR0, SPI_RXFR3), - regmap_reg_range(SPI_CTARE(0), SPI_CTARE(3)), - regmap_reg_range(SPI_SREX, SPI_SREX), -}; - -static const struct regmap_access_table dspi_access_table = { - .yes_ranges = dspi_yes_ranges, - .n_yes_ranges = ARRAY_SIZE(dspi_yes_ranges), -}; - -static const struct regmap_range dspi_volatile_ranges[] = { - regmap_reg_range(SPI_MCR, SPI_TCR), - regmap_reg_range(SPI_SR, SPI_SR), - regmap_reg_range(SPI_PUSHR, SPI_RXFR3), - regmap_reg_range(SPI_SREX, SPI_SREX), -}; - -static const struct regmap_access_table dspi_volatile_table = { - .yes_ranges = dspi_volatile_ranges, - .n_yes_ranges = ARRAY_SIZE(dspi_volatile_ranges), -}; - -static const struct regmap_config dspi_regmap_config = { - .reg_bits = 32, - .val_bits = 32, - .reg_stride = 4, - .max_register = SPI_RXFR3, - .volatile_table = &dspi_volatile_table, - .rd_table = &dspi_access_table, - .wr_table = &dspi_access_table, -}; - -static const struct regmap_config dspi_xspi_regmap_config[] = { - { - .reg_bits = 32, - .val_bits = 32, - .reg_stride = 4, - .max_register = SPI_SREX, - .volatile_table = &dspi_volatile_table, - .rd_table = &dspi_access_table, - .wr_table = &dspi_access_table, - }, - { - .name = "pushr", - .reg_bits = 16, - .val_bits = 16, - .reg_stride = 2, - .max_register = 0x2, - }, -}; - static int dspi_init(struct fsl_dspi *dspi) { unsigned int mcr; @@ -1305,7 +1321,6 @@ static int dspi_target_abort(struct spi_controller *host) static int dspi_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; - const struct regmap_config *regmap_config; struct fsl_dspi_platform_data *pdata; struct spi_controller *ctlr; int ret, cs_num, bus_num = -1; @@ -1388,11 +1403,8 @@ static int dspi_probe(struct platform_device *pdev) goto out_ctlr_put; } - if (dspi->devtype_data->trans_mode == DSPI_XSPI_MODE) - regmap_config = &dspi_xspi_regmap_config[0]; - else - regmap_config = &dspi_regmap_config; - dspi->regmap = devm_regmap_init_mmio(&pdev->dev, base, regmap_config); + dspi->regmap = devm_regmap_init_mmio(&pdev->dev, base, + dspi->devtype_data->regmap); if (IS_ERR(dspi->regmap)) { dev_err(&pdev->dev, "failed to init regmap: %ld\n", PTR_ERR(dspi->regmap)); @@ -1403,7 +1415,7 @@ static int dspi_probe(struct platform_device *pdev) if (dspi->devtype_data->trans_mode == DSPI_XSPI_MODE) { dspi->regmap_pushr = devm_regmap_init_mmio( &pdev->dev, base + SPI_PUSHR, - &dspi_xspi_regmap_config[1]); + &dspi_regmap_config[DSPI_PUSHR]); if (IS_ERR(dspi->regmap_pushr)) { dev_err(&pdev->dev, "failed to init pushr regmap: %ld\n", From 70c0b17ee344b0c14b88e6b5b1db6abe2fa84218 Mon Sep 17 00:00:00 2001 From: Larisa Grigore Date: Thu, 22 May 2025 15:51:35 +0100 Subject: [PATCH 03/62] spi: spi-fsl-dspi: Add config and regmaps for S32G platforms S32G adds SPI_{T,R}XFR4 and extends SPI_CTAR registers to 5. Add the new regmaps, configs and bits. dspi_volatile_ranges gets SPI_{T,R}XFR4 added which affects all platforms, however they are further limited by dspi_yes_ranges. Signed-off-by: Larisa Grigore Signed-off-by: James Clark Link: https://patch.msgid.link/20250522-james-nxp-spi-v2-6-bea884630cfb@linaro.org Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 39 +++++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 437a8db9fa2b..10e511ba1cd8 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -35,7 +35,7 @@ #define SPI_TCR 0x08 #define SPI_TCR_GET_TCNT(x) (((x) & GENMASK(31, 16)) >> 16) -#define SPI_CTAR(x) (0x0c + (((x) & GENMASK(1, 0)) * 4)) +#define SPI_CTAR(x) (0x0c + (((x) & GENMASK(2, 0)) * 4)) #define SPI_CTAR_FMSZ(x) (((x) << 27) & GENMASK(30, 27)) #define SPI_CTAR_CPOL BIT(26) #define SPI_CTAR_CPHA BIT(25) @@ -93,12 +93,14 @@ #define SPI_TXFR1 0x40 #define SPI_TXFR2 0x44 #define SPI_TXFR3 0x48 +#define SPI_TXFR4 0x4C #define SPI_RXFR0 0x7c #define SPI_RXFR1 0x80 #define SPI_RXFR2 0x84 #define SPI_RXFR3 0x88 +#define SPI_RXFR4 0x8C -#define SPI_CTARE(x) (0x11c + (((x) & GENMASK(1, 0)) * 4)) +#define SPI_CTARE(x) (0x11c + (((x) & GENMASK(2, 0)) * 4)) #define SPI_CTARE_FMSZE(x) (((x) & 0x1) << 16) #define SPI_CTARE_DTCP(x) ((x) & 0x7ff) @@ -136,6 +138,7 @@ enum { LX2160A, MCF5441X, VF610, + S32G, }; static const struct regmap_range dspi_yes_ranges[] = { @@ -147,15 +150,29 @@ static const struct regmap_range dspi_yes_ranges[] = { regmap_reg_range(SPI_SREX, SPI_SREX), }; +static const struct regmap_range s32g_dspi_yes_ranges[] = { + regmap_reg_range(SPI_MCR, SPI_MCR), + regmap_reg_range(SPI_TCR, SPI_CTAR(5)), + regmap_reg_range(SPI_SR, SPI_TXFR4), + regmap_reg_range(SPI_RXFR0, SPI_RXFR4), + regmap_reg_range(SPI_CTARE(0), SPI_CTARE(5)), + regmap_reg_range(SPI_SREX, SPI_SREX), +}; + static const struct regmap_access_table dspi_access_table = { .yes_ranges = dspi_yes_ranges, .n_yes_ranges = ARRAY_SIZE(dspi_yes_ranges), }; +static const struct regmap_access_table s32g_dspi_access_table = { + .yes_ranges = s32g_dspi_yes_ranges, + .n_yes_ranges = ARRAY_SIZE(s32g_dspi_yes_ranges), +}; + static const struct regmap_range dspi_volatile_ranges[] = { regmap_reg_range(SPI_MCR, SPI_TCR), regmap_reg_range(SPI_SR, SPI_SR), - regmap_reg_range(SPI_PUSHR, SPI_RXFR3), + regmap_reg_range(SPI_PUSHR, SPI_RXFR4), regmap_reg_range(SPI_SREX, SPI_SREX), }; @@ -167,6 +184,7 @@ static const struct regmap_access_table dspi_volatile_table = { enum { DSPI_REGMAP, DSPI_XSPI_REGMAP, + S32G_DSPI_XSPI_REGMAP, DSPI_PUSHR, }; @@ -189,6 +207,15 @@ static const struct regmap_config dspi_regmap_config[] = { .rd_table = &dspi_access_table, .wr_table = &dspi_access_table, }, + [S32G_DSPI_XSPI_REGMAP] = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = SPI_SREX, + .volatile_table = &dspi_volatile_table, + .wr_table = &s32g_dspi_access_table, + .rd_table = &s32g_dspi_access_table, + }, [DSPI_PUSHR] = { .name = "pushr", .reg_bits = 16, @@ -263,6 +290,12 @@ static const struct fsl_dspi_devtype_data devtype_data[] = { .fifo_size = 16, .regmap = &dspi_regmap_config[DSPI_REGMAP], }, + [S32G] = { + .trans_mode = DSPI_XSPI_MODE, + .max_clock_factor = 1, + .fifo_size = 5, + .regmap = &dspi_regmap_config[S32G_DSPI_XSPI_REGMAP], + }, }; struct fsl_dspi_dma { From e7397e4d3b161ed8a57648a9ac03df7902958682 Mon Sep 17 00:00:00 2001 From: Marius Trifu Date: Thu, 22 May 2025 15:51:36 +0100 Subject: [PATCH 04/62] spi: spi-fsl-dspi: Use spi_alloc_target for target spi_alloc_target should be used for target devices. This also sets ctlr->target automatically so delete that line. Signed-off-by: Marius Trifu Signed-off-by: Larisa Grigore Signed-off-by: James Clark Link: https://patch.msgid.link/20250522-james-nxp-spi-v2-7-bea884630cfb@linaro.org Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 10e511ba1cd8..814a92b8064e 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -1366,7 +1366,10 @@ static int dspi_probe(struct platform_device *pdev) if (!dspi) return -ENOMEM; - ctlr = spi_alloc_host(&pdev->dev, 0); + if (of_property_read_bool(np, "spi-slave")) + ctlr = spi_alloc_target(&pdev->dev, 0); + else + ctlr = spi_alloc_host(&pdev->dev, 0); if (!ctlr) return -ENOMEM; @@ -1405,9 +1408,6 @@ static int dspi_probe(struct platform_device *pdev) of_property_read_u32(np, "bus-num", &bus_num); ctlr->bus_num = bus_num; - if (of_property_read_bool(np, "spi-slave")) - ctlr->target = true; - dspi->devtype_data = of_device_get_match_data(&pdev->dev); if (!dspi->devtype_data) { dev_err(&pdev->dev, "can't get devtype_data\n"); From cac7e5054115fcc41b1cb050af8e8971f7c9b22b Mon Sep 17 00:00:00 2001 From: Larisa Grigore Date: Thu, 22 May 2025 15:51:37 +0100 Subject: [PATCH 05/62] spi: spi-fsl-dspi: Avoid setup_accel logic for DMA transfers Repacking multiple smaller words into larger ones to make use of the full FIFO doesn't save anything in DMA mode, so don't bother doing it. Signed-off-by: Larisa Grigore Signed-off-by: James Clark Link: https://patch.msgid.link/20250522-james-nxp-spi-v2-8-bea884630cfb@linaro.org Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 814a92b8064e..24a51267cb4d 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -850,8 +850,12 @@ static void dspi_setup_accel(struct fsl_dspi *dspi) struct spi_transfer *xfer = dspi->cur_transfer; bool odd = !!(dspi->len & 1); - /* No accel for frames not multiple of 8 bits at the moment */ - if (xfer->bits_per_word % 8) + /* + * No accel for DMA transfers or frames not multiples of 8 bits at the + * moment. + */ + if (dspi->devtype_data->trans_mode == DSPI_DMA_MODE || + xfer->bits_per_word % 8) goto no_accel; if (!odd && dspi->len <= dspi->devtype_data->fifo_size * 2) { @@ -860,10 +864,7 @@ static void dspi_setup_accel(struct fsl_dspi *dspi) dspi->oper_bits_per_word = 8; } else { /* Start off with maximum supported by hardware */ - if (dspi->devtype_data->trans_mode == DSPI_XSPI_MODE) - dspi->oper_bits_per_word = 32; - else - dspi->oper_bits_per_word = 16; + dspi->oper_bits_per_word = 32; /* * And go down only if the buffer can't be sent with From 870d6fda18d590df88beac9b0504f810807a5ed6 Mon Sep 17 00:00:00 2001 From: Larisa Grigore Date: Thu, 22 May 2025 15:51:38 +0100 Subject: [PATCH 06/62] spi: spi-fsl-dspi: Use DMA for S32G controller in target mode Switch to DMA for target mode otherwise the controller is too slow to feed TX FIFO and UNDERFLOW occurs frequently. DMA can work only with 8 and 16 bits per word. 32bits per word is not supported, this is a hardware limitation, so we keep the controller mode in TCFQ mode. Signed-off-by: Larisa Grigore Signed-off-by: Ciprian Marian Costea Signed-off-by: James Clark Link: https://patch.msgid.link/20250522-james-nxp-spi-v2-9-bea884630cfb@linaro.org Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 24a51267cb4d..db5a2ed66f68 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -139,6 +139,7 @@ enum { MCF5441X, VF610, S32G, + S32G_TARGET, }; static const struct regmap_range dspi_yes_ranges[] = { @@ -183,6 +184,7 @@ static const struct regmap_access_table dspi_volatile_table = { enum { DSPI_REGMAP, + S32G_DSPI_REGMAP, DSPI_XSPI_REGMAP, S32G_DSPI_XSPI_REGMAP, DSPI_PUSHR, @@ -198,6 +200,15 @@ static const struct regmap_config dspi_regmap_config[] = { .rd_table = &dspi_access_table, .wr_table = &dspi_access_table, }, + [S32G_DSPI_REGMAP] = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = SPI_RXFR4, + .volatile_table = &dspi_volatile_table, + .wr_table = &s32g_dspi_access_table, + .rd_table = &s32g_dspi_access_table, + }, [DSPI_XSPI_REGMAP] = { .reg_bits = 32, .val_bits = 32, @@ -296,6 +307,12 @@ static const struct fsl_dspi_devtype_data devtype_data[] = { .fifo_size = 5, .regmap = &dspi_regmap_config[S32G_DSPI_XSPI_REGMAP], }, + [S32G_TARGET] = { + .trans_mode = DSPI_DMA_MODE, + .max_clock_factor = 1, + .fifo_size = 5, + .regmap = &dspi_regmap_config[S32G_DSPI_REGMAP], + }, }; struct fsl_dspi_dma { @@ -351,6 +368,12 @@ struct fsl_dspi { void (*dev_to_host)(struct fsl_dspi *dspi, u32 rxdata); }; +static bool is_s32g_dspi(struct fsl_dspi *data) +{ + return data->devtype_data == &devtype_data[S32G] || + data->devtype_data == &devtype_data[S32G_TARGET]; +} + static void dspi_native_host_to_dev(struct fsl_dspi *dspi, u32 *txdata) { switch (dspi->oper_word_size) { @@ -1426,6 +1449,9 @@ static int dspi_probe(struct platform_device *pdev) dspi->pushr_tx = 0; } + if (spi_controller_is_target(ctlr) && is_s32g_dspi(dspi)) + dspi->devtype_data = &devtype_data[S32G_TARGET]; + if (dspi->devtype_data->trans_mode == DSPI_XSPI_MODE) ctlr->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); else From c5412ec5f687732f9722bd0f94f9632ad78f4c52 Mon Sep 17 00:00:00 2001 From: Larisa Grigore Date: Thu, 22 May 2025 15:51:39 +0100 Subject: [PATCH 07/62] spi: spi-fsl-dspi: Reinitialize DSPI regs after resuming for S32G After resuming, DSPI registers (MCR and SR) need to be reinitialized for S32G platforms. Signed-off-by: Larisa Grigore Signed-off-by: James Clark Link: https://patch.msgid.link/20250522-james-nxp-spi-v2-10-bea884630cfb@linaro.org Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 77 +++++++++++++++++++++----------------- 1 file changed, 42 insertions(+), 35 deletions(-) diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index db5a2ed66f68..a3efe1bd3b37 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -1284,41 +1284,6 @@ static const struct of_device_id fsl_dspi_dt_ids[] = { }; MODULE_DEVICE_TABLE(of, fsl_dspi_dt_ids); -#ifdef CONFIG_PM_SLEEP -static int dspi_suspend(struct device *dev) -{ - struct fsl_dspi *dspi = dev_get_drvdata(dev); - - if (dspi->irq) - disable_irq(dspi->irq); - spi_controller_suspend(dspi->ctlr); - clk_disable_unprepare(dspi->clk); - - pinctrl_pm_select_sleep_state(dev); - - return 0; -} - -static int dspi_resume(struct device *dev) -{ - struct fsl_dspi *dspi = dev_get_drvdata(dev); - int ret; - - pinctrl_pm_select_default_state(dev); - - ret = clk_prepare_enable(dspi->clk); - if (ret) - return ret; - spi_controller_resume(dspi->ctlr); - if (dspi->irq) - enable_irq(dspi->irq); - - return 0; -} -#endif /* CONFIG_PM_SLEEP */ - -static SIMPLE_DEV_PM_OPS(dspi_pm, dspi_suspend, dspi_resume); - static int dspi_init(struct fsl_dspi *dspi) { unsigned int mcr; @@ -1354,6 +1319,48 @@ static int dspi_init(struct fsl_dspi *dspi) return 0; } +#ifdef CONFIG_PM_SLEEP +static int dspi_suspend(struct device *dev) +{ + struct fsl_dspi *dspi = dev_get_drvdata(dev); + + if (dspi->irq) + disable_irq(dspi->irq); + spi_controller_suspend(dspi->ctlr); + clk_disable_unprepare(dspi->clk); + + pinctrl_pm_select_sleep_state(dev); + + return 0; +} + +static int dspi_resume(struct device *dev) +{ + struct fsl_dspi *dspi = dev_get_drvdata(dev); + int ret; + + pinctrl_pm_select_default_state(dev); + + ret = clk_prepare_enable(dspi->clk); + if (ret) + return ret; + spi_controller_resume(dspi->ctlr); + + ret = dspi_init(dspi); + if (ret) { + dev_err(dev, "failed to initialize dspi during resume\n"); + return ret; + } + + if (dspi->irq) + enable_irq(dspi->irq); + + return 0; +} +#endif /* CONFIG_PM_SLEEP */ + +static SIMPLE_DEV_PM_OPS(dspi_pm, dspi_suspend, dspi_resume); + static int dspi_target_abort(struct spi_controller *host) { struct fsl_dspi *dspi = spi_controller_get_devdata(host); From 0cb9ca1187b311db21288a79ec7b98121f730354 Mon Sep 17 00:00:00 2001 From: Andra-Teodora Ilie Date: Thu, 22 May 2025 15:51:40 +0100 Subject: [PATCH 08/62] spi: spi-fsl-dspi: Enable modified transfer protocol on S32G S32G supports modified transfer protocol where both host and target devices sample later in the SCK period than in Classic SPI mode to allow the logic to tolerate more delays in device pads and board traces. Set MTFE bit in MCR register for frequencies higher than 25MHz. Signed-off-by: Andra-Teodora Ilie Signed-off-by: Bogdan-Gabriel Roman Signed-off-by: Larisa Grigore Signed-off-by: James Clark Link: https://patch.msgid.link/20250522-james-nxp-spi-v2-11-bea884630cfb@linaro.org Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 45 +++++++++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index a3efe1bd3b37..01af641fa757 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -24,6 +24,7 @@ #define SPI_MCR 0x00 #define SPI_MCR_HOST BIT(31) +#define SPI_MCR_MTFE BIT(26) #define SPI_MCR_PCSIS(x) ((x) << 16) #define SPI_MCR_CLR_TXF BIT(11) #define SPI_MCR_CLR_RXF BIT(10) @@ -37,6 +38,7 @@ #define SPI_CTAR(x) (0x0c + (((x) & GENMASK(2, 0)) * 4)) #define SPI_CTAR_FMSZ(x) (((x) << 27) & GENMASK(30, 27)) +#define SPI_CTAR_DBR BIT(31) #define SPI_CTAR_CPOL BIT(26) #define SPI_CTAR_CPHA BIT(25) #define SPI_CTAR_LSBFE BIT(24) @@ -111,6 +113,8 @@ #define DMA_COMPLETION_TIMEOUT msecs_to_jiffies(3000) +#define SPI_25MHZ 25000000 + struct chip_data { u32 ctar_val; }; @@ -346,6 +350,7 @@ struct fsl_dspi { const void *tx; void *rx; u16 tx_cmd; + bool mtf_enabled; const struct fsl_dspi_devtype_data *devtype_data; struct completion xfer_done; @@ -722,7 +727,7 @@ static void dspi_release_dma(struct fsl_dspi *dspi) } static void hz_to_spi_baud(char *pbr, char *br, int speed_hz, - unsigned long clkrate) + unsigned long clkrate, bool mtf_enabled) { /* Valid baud rate pre-scaler values */ int pbr_tbl[4] = {2, 3, 5, 7}; @@ -739,7 +744,13 @@ static void hz_to_spi_baud(char *pbr, char *br, int speed_hz, for (i = 0; i < ARRAY_SIZE(brs); i++) for (j = 0; j < ARRAY_SIZE(pbr_tbl); j++) { - scale = brs[i] * pbr_tbl[j]; + if (mtf_enabled) { + /* In MTF mode DBR=1 so frequency is doubled */ + scale = (brs[i] * pbr_tbl[j]) / 2; + } else { + scale = brs[i] * pbr_tbl[j]; + } + if (scale >= scale_needed) { if (scale < minscale) { minscale = scale; @@ -1146,6 +1157,20 @@ static int dspi_transfer_one_message(struct spi_controller *ctlr, return status; } +static int dspi_set_mtf(struct fsl_dspi *dspi) +{ + if (spi_controller_is_target(dspi->ctlr)) + return 0; + + if (dspi->mtf_enabled) + regmap_update_bits(dspi->regmap, SPI_MCR, SPI_MCR_MTFE, + SPI_MCR_MTFE); + else + regmap_update_bits(dspi->regmap, SPI_MCR, SPI_MCR_MTFE, 0); + + return 0; +} + static int dspi_setup(struct spi_device *spi) { struct fsl_dspi *dspi = spi_controller_get_devdata(spi->controller); @@ -1204,7 +1229,16 @@ static int dspi_setup(struct spi_device *spi) cs_sck_delay, sck_cs_delay); clkrate = clk_get_rate(dspi->clk); - hz_to_spi_baud(&pbr, &br, spi->max_speed_hz, clkrate); + + if (is_s32g_dspi(dspi) && spi->max_speed_hz > SPI_25MHZ) + dspi->mtf_enabled = true; + else + dspi->mtf_enabled = false; + + dspi_set_mtf(dspi); + + hz_to_spi_baud(&pbr, &br, spi->max_speed_hz, clkrate, + dspi->mtf_enabled); /* Set PCS to SCK delay scale values */ ns_delay_scale(&pcssck, &cssck, cs_sck_delay, clkrate); @@ -1226,6 +1260,9 @@ static int dspi_setup(struct spi_device *spi) SPI_CTAR_PBR(pbr) | SPI_CTAR_BR(br); + if (dspi->mtf_enabled) + chip->ctar_val |= SPI_CTAR_DBR; + if (spi->mode & SPI_LSB_FIRST) chip->ctar_val |= SPI_CTAR_LSBFE; } @@ -1352,6 +1389,8 @@ static int dspi_resume(struct device *dev) return ret; } + dspi_set_mtf(dspi); + if (dspi->irq) enable_irq(dspi->irq); From be47ecfecf5a6f16d028fd572410251b502692bf Mon Sep 17 00:00:00 2001 From: Ciprian Marian Costea Date: Thu, 22 May 2025 15:51:41 +0100 Subject: [PATCH 09/62] dt-bindings: spi: dspi: Add S32G support Document S32G compatible strings. 's32g2' and 's32g3' use the same driver so 's32g2' must follow 's32g3'. The SPI controller supports target mode when the "spi-slave" flag is used so add an example. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Ciprian Marian Costea Signed-off-by: Larisa Grigore Signed-off-by: James Clark Link: https://patch.msgid.link/20250522-james-nxp-spi-v2-12-bea884630cfb@linaro.org Signed-off-by: Mark Brown --- .../devicetree/bindings/spi/fsl,dspi.yaml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Documentation/devicetree/bindings/spi/fsl,dspi.yaml b/Documentation/devicetree/bindings/spi/fsl,dspi.yaml index bf9cce53c48d..8dbda1ffb5eb 100644 --- a/Documentation/devicetree/bindings/spi/fsl,dspi.yaml +++ b/Documentation/devicetree/bindings/spi/fsl,dspi.yaml @@ -23,6 +23,7 @@ properties: - fsl,ls2080a-dspi - fsl,ls2085a-dspi - fsl,lx2160a-dspi + - nxp,s32g2-dspi - items: - enum: - fsl,ls1012a-dspi @@ -37,6 +38,9 @@ properties: - items: - const: fsl,lx2160a-dspi - const: fsl,ls2085a-dspi + - items: + - const: nxp,s32g3-dspi + - const: nxp,s32g2-dspi reg: maxItems: 1 @@ -114,3 +118,17 @@ examples: spi-cs-hold-delay-ns = <50>; }; }; + # S32G3 in target mode + - | + spi@401d4000 { + compatible = "nxp,s32g3-dspi", "nxp,s32g2-dspi"; + reg = <0x401d4000 0x1000>; + interrupts = ; + clocks = <&clks 26>; + clock-names = "dspi"; + spi-num-chipselects = <8>; + bus-num = <0>; + dmas = <&edma0 0 7>, <&edma0 0 8>; + dma-names = "tx", "rx"; + spi-slave; + }; From 9a30e332c36c52e92e5316b4a012d45284dedeb5 Mon Sep 17 00:00:00 2001 From: Ciprian Marian Costea Date: Thu, 22 May 2025 15:51:42 +0100 Subject: [PATCH 10/62] spi: spi-fsl-dspi: Enable support for S32G platforms Add compatible for S32G platforms, allowing DSPI to be used. Add a depends for ARCH_NXP which can replace LAYERSCAPE and also includes the new ARCH_S32 for S32G. Similarly, ARCH_MXC can replace SOC_VF610 || SOC_LS1021A which should avoid updating this for every new sub-platform in the future. Signed-off-by: Ciprian Marian Costea Signed-off-by: Stoica Cosmin-Stefan Signed-off-by: Dan Nica Signed-off-by: Larisa Grigore Signed-off-by: Stefan-Gabriel Mirea Signed-off-by: James Clark Link: https://patch.msgid.link/20250522-james-nxp-spi-v2-13-bea884630cfb@linaro.org Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 4 ++-- drivers/spi/spi-fsl-dspi.c | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index c51da3fc3604..60eb65c927b1 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -647,10 +647,10 @@ config SPI_FSL_SPI config SPI_FSL_DSPI tristate "Freescale DSPI controller" select REGMAP_MMIO - depends on SOC_VF610 || SOC_LS1021A || ARCH_LAYERSCAPE || M5441x || COMPILE_TEST + depends on ARCH_MXC || ARCH_NXP || M54541x || COMPILE_TEST help This enables support for the Freescale DSPI controller in master - mode. VF610, LS1021A and ColdFire platforms uses the controller. + mode. S32, VF610, LS1021A and ColdFire platforms uses the controller. config SPI_FSL_ESPI tristate "Freescale eSPI controller" diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 01af641fa757..04c88d090c4d 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -1316,6 +1316,9 @@ static const struct of_device_id fsl_dspi_dt_ids[] = { }, { .compatible = "fsl,lx2160a-dspi", .data = &devtype_data[LX2160A], + }, { + .compatible = "nxp,s32g2-dspi", + .data = &devtype_data[S32G], }, { /* sentinel */ } }; From 6c1ca9928ed48499f75101057079b92072077d44 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Sun, 25 May 2025 16:15:25 +0200 Subject: [PATCH 11/62] spi: spi-qpic-snand: use NANDC_STEP_SIZE consistently Change the qcom_spi_read_page_ecc() function to use NANDC_STEP_SIZE instead of a magic number while calculating the data size to keep it consistent with other functions like qcom_spi_program_{raw,ecc,oob} and qcom_spi_read_cw_{raw,page_oob}. No functional changes. Signed-off-by: Gabor Juhos Reviewed-by: Md Sadre Alam Link: https://patch.msgid.link/20250525-qpic-snand-nandc_step_size-v1-1-6039e9bfe1c6@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-qpic-snand.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-qpic-snand.c b/drivers/spi/spi-qpic-snand.c index 77d9cc65477a..1e51a7ecc3d0 100644 --- a/drivers/spi/spi-qpic-snand.c +++ b/drivers/spi/spi-qpic-snand.c @@ -835,7 +835,7 @@ static int qcom_spi_read_page_ecc(struct qcom_nand_controller *snandc, int data_size, oob_size; if (i == (num_cw - 1)) { - data_size = 512 - ((num_cw - 1) << 2); + data_size = NANDC_STEP_SIZE - ((num_cw - 1) << 2); oob_size = (num_cw << 2) + ecc_cfg->ecc_bytes_hw + ecc_cfg->spare_bytes; } else { From f73dc37ebf45573349aee0aae168e8dc3d13ecee Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Thu, 29 May 2025 19:35:44 +0200 Subject: [PATCH 12/62] spi: spi-qpic-snand: remove 'qpic_snand_op' structure The 'qpic_snand_op' structure is used only in the qcom_spi_send_cmdaddr() function as a type of a local variable. Additionally, the sole purpose of that variable is to keep some interim values before those gets passed as arguments for cpu_to_le32() calls. In order to simplify the code, remove the definition of the structure along with the local variable, and use the corresponding values directly as parameters for cpu_to_le32() calls. No functional changes intended. Signed-off-by: Gabor Juhos Link: https://patch.msgid.link/20250529-qpic-snand-remove-qpic_snand_op-v1-1-6e42b772d748@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-qpic-snand.c | 35 +++++++++-------------------------- 1 file changed, 9 insertions(+), 26 deletions(-) diff --git a/drivers/spi/spi-qpic-snand.c b/drivers/spi/spi-qpic-snand.c index 1e51a7ecc3d0..ca55f9bcd17b 100644 --- a/drivers/spi/spi-qpic-snand.c +++ b/drivers/spi/spi-qpic-snand.c @@ -59,12 +59,6 @@ #define OOB_BUF_SIZE 128 #define ecceng_to_qspi(eng) container_of(eng, struct qpic_spi_nand, ecc_eng) -struct qpic_snand_op { - u32 cmd_reg; - u32 addr1_reg; - u32 addr2_reg; -}; - struct snandc_read_status { __le32 snandc_flash; __le32 snandc_buffer; @@ -1294,7 +1288,6 @@ static int qcom_spi_write_page(struct qcom_nand_controller *snandc, static int qcom_spi_send_cmdaddr(struct qcom_nand_controller *snandc, const struct spi_mem_op *op) { - struct qpic_snand_op s_op = {}; u32 cmd; int ret, opcode; @@ -1302,34 +1295,24 @@ static int qcom_spi_send_cmdaddr(struct qcom_nand_controller *snandc, if (ret < 0) return ret; - s_op.cmd_reg = cmd; - s_op.addr1_reg = op->addr.val; - s_op.addr2_reg = 0; - opcode = op->cmd.opcode; switch (opcode) { case SPINAND_WRITE_EN: return 0; case SPINAND_PROGRAM_EXECUTE: - s_op.addr1_reg = op->addr.val << 16; - s_op.addr2_reg = op->addr.val >> 16 & 0xff; - snandc->qspi->addr1 = cpu_to_le32(s_op.addr1_reg); - snandc->qspi->addr2 = cpu_to_le32(s_op.addr2_reg); + snandc->qspi->addr1 = cpu_to_le32(op->addr.val << 16); + snandc->qspi->addr2 = cpu_to_le32(op->addr.val >> 16 & 0xff); snandc->qspi->cmd = cpu_to_le32(cmd); return qcom_spi_program_execute(snandc, op); case SPINAND_READ: - s_op.addr1_reg = (op->addr.val << 16); - s_op.addr2_reg = op->addr.val >> 16 & 0xff; - snandc->qspi->addr1 = cpu_to_le32(s_op.addr1_reg); - snandc->qspi->addr2 = cpu_to_le32(s_op.addr2_reg); + snandc->qspi->addr1 = cpu_to_le32(op->addr.val << 16); + snandc->qspi->addr2 = cpu_to_le32(op->addr.val >> 16 & 0xff); snandc->qspi->cmd = cpu_to_le32(cmd); return 0; case SPINAND_ERASE: - s_op.addr2_reg = (op->addr.val >> 16) & 0xffff; - s_op.addr1_reg = op->addr.val; - snandc->qspi->addr1 = cpu_to_le32(s_op.addr1_reg << 16); - snandc->qspi->addr2 = cpu_to_le32(s_op.addr2_reg); + snandc->qspi->addr1 = cpu_to_le32(op->addr.val << 16); + snandc->qspi->addr2 = cpu_to_le32(op->addr.val >> 16 & 0xffff); snandc->qspi->cmd = cpu_to_le32(cmd); return qcom_spi_block_erase(snandc); default: @@ -1341,10 +1324,10 @@ static int qcom_spi_send_cmdaddr(struct qcom_nand_controller *snandc, qcom_clear_read_regs(snandc); qcom_clear_bam_transaction(snandc); - snandc->regs->cmd = cpu_to_le32(s_op.cmd_reg); + snandc->regs->cmd = cpu_to_le32(cmd); snandc->regs->exec = cpu_to_le32(1); - snandc->regs->addr0 = cpu_to_le32(s_op.addr1_reg); - snandc->regs->addr1 = cpu_to_le32(s_op.addr2_reg); + snandc->regs->addr0 = cpu_to_le32(op->addr.val); + snandc->regs->addr1 = cpu_to_le32(0); qcom_write_reg_dma(snandc, &snandc->regs->cmd, NAND_FLASH_CMD, 3, NAND_BAM_NEXT_SGL); qcom_write_reg_dma(snandc, &snandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); From 6b500757aef0b5b639253508cf93eb8134a2d340 Mon Sep 17 00:00:00 2001 From: Frank Li Date: Wed, 28 May 2025 18:28:20 -0400 Subject: [PATCH 13/62] spi: dt-bindings: mxs-spi: allow clocks properpty Allow clocks property to fix below CHECK_DTB warnings: arch/arm/boot/dts/nxp/mxs/imx28-btt3-0.dtb: spi@80014000 (fsl,imx28-spi): Unevaluated properties are not allowed ('clocks' was unexpected) Signed-off-by: Frank Li Link: https://patch.msgid.link/20250528222821.728544-1-Frank.Li@nxp.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/spi/mxs-spi.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/spi/mxs-spi.yaml b/Documentation/devicetree/bindings/spi/mxs-spi.yaml index e2512166c1cd..0cf8e7269ba9 100644 --- a/Documentation/devicetree/bindings/spi/mxs-spi.yaml +++ b/Documentation/devicetree/bindings/spi/mxs-spi.yaml @@ -24,6 +24,9 @@ properties: interrupts: maxItems: 1 + clocks: + maxItems: 1 + dmas: maxItems: 1 From c459262159f39e6e6336797feb975799344b749b Mon Sep 17 00:00:00 2001 From: Thangaraj Samynathan Date: Mon, 26 May 2025 16:19:08 +0530 Subject: [PATCH 14/62] spi: spi-pci1xxxx: Add support for 25MHz Clock frequency in C0 Adds support for 25MHz clock frequency. Support for this frequency is added in C0. Signed-off-by: Thangaraj Samynathan Link: https://patch.msgid.link/20250526104908.404564-1-thangaraj.s@microchip.com Signed-off-by: Mark Brown --- drivers/spi/spi-pci1xxxx.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi-pci1xxxx.c b/drivers/spi/spi-pci1xxxx.c index c6489e90b8b9..9a7b86a5ec75 100644 --- a/drivers/spi/spi-pci1xxxx.c +++ b/drivers/spi/spi-pci1xxxx.c @@ -23,6 +23,7 @@ #define SYS_FREQ_DEFAULT (62500000) #define PCI1XXXX_SPI_MAX_CLOCK_HZ (30000000) +#define PCI1XXXX_SPI_CLK_25MHZ (25000000) #define PCI1XXXX_SPI_CLK_20MHZ (20000000) #define PCI1XXXX_SPI_CLK_15MHZ (15000000) #define PCI1XXXX_SPI_CLK_12MHZ (12000000) @@ -318,12 +319,14 @@ static void pci1xxxx_spi_set_cs(struct spi_device *spi, bool enable) writel(regval, par->reg_base + SPI_MST_CTL_REG_OFFSET(p->hw_inst)); } -static u8 pci1xxxx_get_clock_div(u32 hz) +static u8 pci1xxxx_get_clock_div(struct pci1xxxx_spi *par, u32 hz) { u8 val = 0; if (hz >= PCI1XXXX_SPI_MAX_CLOCK_HZ) val = 2; + else if (par->dev_rev >= 0xC0 && hz >= PCI1XXXX_SPI_CLK_25MHZ) + val = 1; else if ((hz < PCI1XXXX_SPI_MAX_CLOCK_HZ) && (hz >= PCI1XXXX_SPI_CLK_20MHZ)) val = 3; else if ((hz < PCI1XXXX_SPI_CLK_20MHZ) && (hz >= PCI1XXXX_SPI_CLK_15MHZ)) @@ -423,7 +426,7 @@ static int pci1xxxx_spi_transfer_with_io(struct spi_controller *spi_ctlr, p->spi_xfer_in_progress = true; p->bytes_recvd = 0; - clkdiv = pci1xxxx_get_clock_div(xfer->speed_hz); + clkdiv = pci1xxxx_get_clock_div(par, xfer->speed_hz); tx_buf = xfer->tx_buf; rx_buf = xfer->rx_buf; transfer_len = xfer->len; @@ -492,7 +495,7 @@ static int pci1xxxx_spi_transfer_with_dma(struct spi_controller *spi_ctlr, } p->xfer = xfer; p->mode = spi->mode; - p->clkdiv = pci1xxxx_get_clock_div(xfer->speed_hz); + p->clkdiv = pci1xxxx_get_clock_div(par, xfer->speed_hz); p->bytes_recvd = 0; p->rx_buf = xfer->rx_buf; regval = readl(par->reg_base + SPI_MST_EVENT_REG_OFFSET(p->hw_inst)); From 414145b4cf6c64831aa497f0ec6cc1ca2d76c1d2 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Wed, 11 Jun 2025 13:07:46 +0200 Subject: [PATCH 15/62] spi: dt-bindings: mediatek,spi-mt65xx: Add support for MT6991/MT8196 SPI Add support for the SPI IPM controller found on MediaTek's MT6991 (Dimensity) and MT8196 (Kompanio) SoCs, with both having the same controller IP, hence being fully compatible with each other. Signed-off-by: AngeloGioacchino Del Regno Link: https://patch.msgid.link/20250611110747.458090-1-angelogioacchino.delregno@collabora.com Signed-off-by: Mark Brown --- .../devicetree/bindings/spi/mediatek,spi-mt65xx.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Documentation/devicetree/bindings/spi/mediatek,spi-mt65xx.yaml b/Documentation/devicetree/bindings/spi/mediatek,spi-mt65xx.yaml index ed17815263a8..3bf3eb1f8728 100644 --- a/Documentation/devicetree/bindings/spi/mediatek,spi-mt65xx.yaml +++ b/Documentation/devicetree/bindings/spi/mediatek,spi-mt65xx.yaml @@ -39,6 +39,10 @@ properties: - mediatek,mt7988-spi-single - mediatek,mt8188-spi-ipm - const: mediatek,spi-ipm + - items: + - enum: + - mediatek,mt8196-spi + - const: mediatek,mt6991-spi - items: - enum: - mediatek,mt2701-spi @@ -46,6 +50,7 @@ properties: - mediatek,mt6589-spi - mediatek,mt6765-spi - mediatek,mt6893-spi + - mediatek,mt6991-spi - mediatek,mt7622-spi - mediatek,mt8135-spi - mediatek,mt8173-spi From 6cafcc53eb5fffd9b9bdfde700bb9bad21e98ed3 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Wed, 11 Jun 2025 13:07:47 +0200 Subject: [PATCH 16/62] spi: spi-mt65xx: Add support for MT6991 Dimensity 9400 SPI IPM Add support for the SPI IPM controller found in the MediaTek Dimensity 9400 (MT6991) SoC. As a note, this is the same SPI IPM Controller IP found on the MT8196 Kompanio counterpart. Signed-off-by: AngeloGioacchino Del Regno Link: https://patch.msgid.link/20250611110747.458090-2-angelogioacchino.delregno@collabora.com Signed-off-by: Mark Brown --- drivers/spi/spi-mt65xx.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/spi/spi-mt65xx.c b/drivers/spi/spi-mt65xx.c index 4b0a1c0db041..a6032d44771b 100644 --- a/drivers/spi/spi-mt65xx.c +++ b/drivers/spi/spi-mt65xx.c @@ -220,6 +220,14 @@ static const struct mtk_spi_compatible mt6893_compat = { .no_need_unprepare = true, }; +static const struct mtk_spi_compatible mt6991_compat = { + .need_pad_sel = true, + .must_tx = true, + .enhance_timing = true, + .dma_ext = true, + .ipm_design = true, +}; + /* * A piece of default chip info unless the platform * supplies it. @@ -245,6 +253,9 @@ static const struct of_device_id mtk_spi_of_match[] = { { .compatible = "mediatek,mt6765-spi", .data = (void *)&mt6765_compat, }, + { .compatible = "mediatek,mt6991-spi", + .data = (void *)&mt6991_compat, + }, { .compatible = "mediatek,mt7622-spi", .data = (void *)&mt7622_compat, }, From dce4bc30f42d313b4dc5832316196411b7f07ad0 Mon Sep 17 00:00:00 2001 From: Lukas Bulwahn Date: Mon, 16 Jun 2025 11:19:55 +0200 Subject: [PATCH 17/62] spi: spi-fsl-dspi: Revert unintended dependency change in config SPI_FSL_DSPI Commit 9a30e332c36c ("spi: spi-fsl-dspi: Enable support for S32G platforms") reworks the dependencies of config SPI_FSL_DSPI, but introduces a typo changing the dependency to M5441x to a dependency on a non-existing config M54541x. Revert the unintended change to depend on the config M5441x. Fixes: 9a30e332c36c ("spi: spi-fsl-dspi: Enable support for S32G platforms") Signed-off-by: Lukas Bulwahn Reviewed-by: James Clark Link: https://patch.msgid.link/20250616091955.20547-1-lukas.bulwahn@redhat.com Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 60eb65c927b1..f2d2295a5501 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -647,7 +647,7 @@ config SPI_FSL_SPI config SPI_FSL_DSPI tristate "Freescale DSPI controller" select REGMAP_MMIO - depends on ARCH_MXC || ARCH_NXP || M54541x || COMPILE_TEST + depends on ARCH_MXC || ARCH_NXP || M5441x || COMPILE_TEST help This enables support for the Freescale DSPI controller in master mode. S32, VF610, LS1021A and ColdFire platforms uses the controller. From 5fc2c383125c2b4b6037e02ad8796b776b25e6d0 Mon Sep 17 00:00:00 2001 From: Shiji Yang Date: Wed, 18 Jun 2025 22:53:29 +0800 Subject: [PATCH 18/62] spi: falcon: mark falcon_sflash_xfer() as static Fix the following missing-prototypes build warning: drivers/spi/spi-falcon.c:97:5: error: no previous prototype for 'falcon_sflash_xfer' [-Werror=missing-prototypes] 97 | int falcon_sflash_xfer(struct spi_device *spi, struct spi_transfer *t, | ^~~~~~~~~~~~~~~~~~ Signed-off-by: Shiji Yang Link: https://patch.msgid.link/OSBPR01MB16705BE87E549B6210CD6BCABC72A@OSBPR01MB1670.jpnprd01.prod.outlook.com Signed-off-by: Mark Brown --- drivers/spi/spi-falcon.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-falcon.c b/drivers/spi/spi-falcon.c index 84279058f0f1..faa893f83dc5 100644 --- a/drivers/spi/spi-falcon.c +++ b/drivers/spi/spi-falcon.c @@ -94,8 +94,9 @@ struct falcon_sflash { struct spi_controller *host; }; -int falcon_sflash_xfer(struct spi_device *spi, struct spi_transfer *t, - unsigned long flags) +static int +falcon_sflash_xfer(struct spi_device *spi, struct spi_transfer *t, + unsigned long flags) { struct device *dev = &spi->dev; struct falcon_sflash *priv = spi_controller_get_devdata(spi->controller); From 76f03ce1c6f22805ecf689b1f3ecfb56582eddd5 Mon Sep 17 00:00:00 2001 From: Conor Dooley Date: Fri, 20 Jun 2025 14:28:24 +0100 Subject: [PATCH 19/62] spi: microchip-core-qspi: set min_speed_hz during probe The controller's minimum possible bus clock is 1/30 the rate of the input clock. Naively set the minimum bus clock speed the controller is capable of during probe, assuming that the rate will never reduce further. Signed-off-by: Conor Dooley Link: https://patch.msgid.link/20250620-drained-widen-ac311bd5f172@spud Signed-off-by: Mark Brown --- drivers/spi/spi-microchip-core-qspi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/spi/spi-microchip-core-qspi.c b/drivers/spi/spi-microchip-core-qspi.c index fa828fcaaef2..111ae6519ff4 100644 --- a/drivers/spi/spi-microchip-core-qspi.c +++ b/drivers/spi/spi-microchip-core-qspi.c @@ -562,6 +562,7 @@ static int mchp_coreqspi_probe(struct platform_device *pdev) ctlr->mode_bits = SPI_CPOL | SPI_CPHA | SPI_RX_DUAL | SPI_RX_QUAD | SPI_TX_DUAL | SPI_TX_QUAD; ctlr->dev.of_node = np; + ctlr->min_speed_hz = clk_get_rate(qspi->clk) / 30; ret = devm_spi_register_controller(&pdev->dev, ctlr); if (ret) From 75ca45c472dac206df2ebbc1c0f1f9c3bbdace50 Mon Sep 17 00:00:00 2001 From: Conor Dooley Date: Fri, 20 Jun 2025 14:28:25 +0100 Subject: [PATCH 20/62] spi: microchip-core-qspi: remove unused param from mchp_coreqspi_write_op() "word" is unused in mchp_coreqspi_write_op(), so delete it. Signed-off-by: Conor Dooley Link: https://patch.msgid.link/20250620-starry-excusably-25e6be957d9d@spud Signed-off-by: Mark Brown --- drivers/spi/spi-microchip-core-qspi.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/spi/spi-microchip-core-qspi.c b/drivers/spi/spi-microchip-core-qspi.c index 111ae6519ff4..67ff5f8aa84d 100644 --- a/drivers/spi/spi-microchip-core-qspi.c +++ b/drivers/spi/spi-microchip-core-qspi.c @@ -194,7 +194,7 @@ static inline void mchp_coreqspi_read_op(struct mchp_coreqspi *qspi) } } -static inline void mchp_coreqspi_write_op(struct mchp_coreqspi *qspi, bool word) +static inline void mchp_coreqspi_write_op(struct mchp_coreqspi *qspi) { u32 control, data; @@ -415,7 +415,7 @@ static int mchp_coreqspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *o qspi->rxbuf = NULL; qspi->tx_len = op->cmd.nbytes; qspi->rx_len = 0; - mchp_coreqspi_write_op(qspi, false); + mchp_coreqspi_write_op(qspi); } qspi->txbuf = &opaddr[0]; @@ -426,7 +426,7 @@ static int mchp_coreqspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *o qspi->rxbuf = NULL; qspi->tx_len = op->addr.nbytes; qspi->rx_len = 0; - mchp_coreqspi_write_op(qspi, false); + mchp_coreqspi_write_op(qspi); } if (op->data.nbytes) { @@ -435,7 +435,7 @@ static int mchp_coreqspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *o qspi->rxbuf = NULL; qspi->rx_len = 0; qspi->tx_len = op->data.nbytes; - mchp_coreqspi_write_op(qspi, true); + mchp_coreqspi_write_op(qspi); } else { qspi->txbuf = NULL; qspi->rxbuf = (u8 *)op->data.buf.in; From 8f9cf02c8852837923f1cdacfcc92e138513325c Mon Sep 17 00:00:00 2001 From: Cyril Jean Date: Fri, 20 Jun 2025 14:28:26 +0100 Subject: [PATCH 21/62] spi: microchip-core-qspi: Add regular transfers The driver for CoreQSPI only supports memory operations at present, so add support for regular transfers so that the SD card slot and ADC on the BeagleV Fire can be used. Signed-off-by: Cyril Jean Co-developed-by: Conor Dooley Signed-off-by: Conor Dooley Link: https://patch.msgid.link/20250620-splice-shelter-310771564886@spud Signed-off-by: Mark Brown --- drivers/spi/spi-microchip-core-qspi.c | 217 +++++++++++++++++++++++--- 1 file changed, 199 insertions(+), 18 deletions(-) diff --git a/drivers/spi/spi-microchip-core-qspi.c b/drivers/spi/spi-microchip-core-qspi.c index 67ff5f8aa84d..d13a9b755c7f 100644 --- a/drivers/spi/spi-microchip-core-qspi.c +++ b/drivers/spi/spi-microchip-core-qspi.c @@ -222,6 +222,87 @@ static inline void mchp_coreqspi_write_op(struct mchp_coreqspi *qspi) } } +static inline void mchp_coreqspi_write_read_op(struct mchp_coreqspi *qspi) +{ + u32 control, data; + + qspi->rx_len = qspi->tx_len; + + control = readl_relaxed(qspi->regs + REG_CONTROL); + control |= CONTROL_FLAGSX4; + writel_relaxed(control, qspi->regs + REG_CONTROL); + + while (qspi->tx_len >= 4) { + while (readl_relaxed(qspi->regs + REG_STATUS) & STATUS_TXFIFOFULL) + ; + + data = qspi->txbuf ? *((u32 *)qspi->txbuf) : 0xaa; + if (qspi->txbuf) + qspi->txbuf += 4; + qspi->tx_len -= 4; + writel_relaxed(data, qspi->regs + REG_X4_TX_DATA); + + /* + * The rx FIFO is twice the size of the tx FIFO, so there is + * no requirement to block transmission if receive data is not + * ready, and it is fine to let the tx FIFO completely fill + * without reading anything from the rx FIFO. Once the tx FIFO + * has been filled and becomes non-full due to a transmission + * occurring there will always be something to receive. + * IOW, this is safe as TX_FIFO_SIZE + 4 < 2 * TX_FIFO_SIZE + */ + if (qspi->rx_len >= 4) { + if (readl_relaxed(qspi->regs + REG_STATUS) & STATUS_RXAVAILABLE) { + data = readl_relaxed(qspi->regs + REG_X4_RX_DATA); + *(u32 *)qspi->rxbuf = data; + qspi->rxbuf += 4; + qspi->rx_len -= 4; + } + } + } + + /* + * Since transmission is not being blocked by clearing the rx FIFO, + * loop here until all received data "leaked" by the loop above has + * been dealt with. + */ + while (qspi->rx_len >= 4) { + while (readl_relaxed(qspi->regs + REG_STATUS) & STATUS_RXFIFOEMPTY) + ; + data = readl_relaxed(qspi->regs + REG_X4_RX_DATA); + *(u32 *)qspi->rxbuf = data; + qspi->rxbuf += 4; + qspi->rx_len -= 4; + } + + /* + * Since rx_len and tx_len must be < 4 bytes at this point, there's no + * concern about overflowing the rx or tx FIFOs any longer. It's + * therefore safe to loop over the remainder of the transmit data before + * handling the remaining receive data. + */ + if (!qspi->tx_len) + return; + + control &= ~CONTROL_FLAGSX4; + writel_relaxed(control, qspi->regs + REG_CONTROL); + + while (qspi->tx_len--) { + while (readl_relaxed(qspi->regs + REG_STATUS) & STATUS_TXFIFOFULL) + ; + data = qspi->txbuf ? *qspi->txbuf : 0xaa; + qspi->txbuf++; + writel_relaxed(data, qspi->regs + REG_TX_DATA); + } + + while (qspi->rx_len--) { + while (readl_relaxed(qspi->regs + REG_STATUS) & STATUS_RXFIFOEMPTY) + ; + data = readl_relaxed(qspi->regs + REG_RX_DATA); + *qspi->rxbuf++ = (data & 0xFF); + } +} + static void mchp_coreqspi_enable_ints(struct mchp_coreqspi *qspi) { u32 mask = IEN_TXDONE | @@ -266,7 +347,7 @@ static irqreturn_t mchp_coreqspi_isr(int irq, void *dev_id) } static int mchp_coreqspi_setup_clock(struct mchp_coreqspi *qspi, struct spi_device *spi, - const struct spi_mem_op *op) + u32 max_freq) { unsigned long clk_hz; u32 control, baud_rate_val = 0; @@ -275,11 +356,11 @@ static int mchp_coreqspi_setup_clock(struct mchp_coreqspi *qspi, struct spi_devi if (!clk_hz) return -EINVAL; - baud_rate_val = DIV_ROUND_UP(clk_hz, 2 * op->max_freq); + baud_rate_val = DIV_ROUND_UP(clk_hz, 2 * max_freq); if (baud_rate_val > MAX_DIVIDER || baud_rate_val < MIN_DIVIDER) { dev_err(&spi->dev, "could not configure the clock for spi clock %d Hz & system clock %ld Hz\n", - op->max_freq, clk_hz); + max_freq, clk_hz); return -EINVAL; } @@ -367,23 +448,13 @@ static inline void mchp_coreqspi_config_op(struct mchp_coreqspi *qspi, const str writel_relaxed(frames, qspi->regs + REG_FRAMES); } -static int mchp_qspi_wait_for_ready(struct spi_mem *mem) +static int mchp_coreqspi_wait_for_ready(struct mchp_coreqspi *qspi) { - struct mchp_coreqspi *qspi = spi_controller_get_devdata - (mem->spi->controller); u32 status; - int ret; - ret = readl_poll_timeout(qspi->regs + REG_STATUS, status, + return readl_poll_timeout(qspi->regs + REG_STATUS, status, (status & STATUS_READY), 0, TIMEOUT_MS); - if (ret) { - dev_err(&mem->spi->dev, - "Timeout waiting on QSPI ready.\n"); - return -ETIMEDOUT; - } - - return ret; } static int mchp_coreqspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) @@ -396,11 +467,13 @@ static int mchp_coreqspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *o int err, i; mutex_lock(&qspi->op_lock); - err = mchp_qspi_wait_for_ready(mem); - if (err) + err = mchp_coreqspi_wait_for_ready(qspi); + if (err) { + dev_err(&mem->spi->dev, "Timeout waiting on QSPI ready.\n"); goto error; + } - err = mchp_coreqspi_setup_clock(qspi, mem->spi, op); + err = mchp_coreqspi_setup_clock(qspi, mem->spi, op->max_freq); if (err) goto error; @@ -515,6 +588,109 @@ static const struct spi_controller_mem_caps mchp_coreqspi_mem_caps = { .per_op_freq = true, }; +static int mchp_coreqspi_unprepare_message(struct spi_controller *ctlr, struct spi_message *m) +{ + struct mchp_coreqspi *qspi = spi_controller_get_devdata(ctlr); + + /* + * This delay is required for the driver to function correctly, + * but no explanation has been determined for why it is required. + */ + udelay(750); + + mutex_unlock(&qspi->op_lock); + + return 0; +} + +static int mchp_coreqspi_prepare_message(struct spi_controller *ctlr, struct spi_message *m) +{ + struct mchp_coreqspi *qspi = spi_controller_get_devdata(ctlr); + struct spi_transfer *t = NULL; + u32 control, frames; + u32 total_bytes = 0, cmd_bytes = 0, idle_cycles = 0; + int ret; + bool quad = false, dual = false; + + mutex_lock(&qspi->op_lock); + ret = mchp_coreqspi_wait_for_ready(qspi); + if (ret) { + mutex_unlock(&qspi->op_lock); + dev_err(&ctlr->dev, "Timeout waiting on QSPI ready.\n"); + return ret; + } + + ret = mchp_coreqspi_setup_clock(qspi, m->spi, m->spi->max_speed_hz); + if (ret) { + mutex_unlock(&qspi->op_lock); + return ret; + } + + control = readl_relaxed(qspi->regs + REG_CONTROL); + control &= ~(CONTROL_MODE12_MASK | CONTROL_MODE0); + writel_relaxed(control, qspi->regs + REG_CONTROL); + + reinit_completion(&qspi->data_completion); + + list_for_each_entry(t, &m->transfers, transfer_list) { + total_bytes += t->len; + if (!cmd_bytes && !(t->tx_buf && t->rx_buf)) + cmd_bytes = t->len; + if (!t->rx_buf) + cmd_bytes = total_bytes; + if (t->tx_nbits == SPI_NBITS_QUAD || t->rx_nbits == SPI_NBITS_QUAD) + quad = true; + else if (t->tx_nbits == SPI_NBITS_DUAL || t->rx_nbits == SPI_NBITS_DUAL) + dual = true; + } + + control = readl_relaxed(qspi->regs + REG_CONTROL); + if (quad) { + control |= (CONTROL_MODE0 | CONTROL_MODE12_EX_RW); + } else if (dual) { + control &= ~CONTROL_MODE0; + control |= CONTROL_MODE12_FULL; + } else { + control &= ~(CONTROL_MODE12_MASK | CONTROL_MODE0); + } + writel_relaxed(control, qspi->regs + REG_CONTROL); + + frames = total_bytes & BYTESUPPER_MASK; + writel_relaxed(frames, qspi->regs + REG_FRAMESUP); + frames = total_bytes & BYTESLOWER_MASK; + frames |= cmd_bytes << FRAMES_CMDBYTES_SHIFT; + frames |= idle_cycles << FRAMES_IDLE_SHIFT; + control = readl_relaxed(qspi->regs + REG_CONTROL); + if (control & CONTROL_MODE12_MASK) + frames |= (1 << FRAMES_SHIFT); + + frames |= FRAMES_FLAGWORD; + writel_relaxed(frames, qspi->regs + REG_FRAMES); + + return 0; +}; + +static int mchp_coreqspi_transfer_one(struct spi_controller *ctlr, struct spi_device *spi, + struct spi_transfer *t) +{ + struct mchp_coreqspi *qspi = spi_controller_get_devdata(ctlr); + + qspi->tx_len = t->len; + + if (t->tx_buf) + qspi->txbuf = (u8 *)t->tx_buf; + + if (!t->rx_buf) { + mchp_coreqspi_write_op(qspi); + } else { + qspi->rxbuf = (u8 *)t->rx_buf; + qspi->rx_len = t->len; + mchp_coreqspi_write_read_op(qspi); + } + + return 0; +} + static int mchp_coreqspi_probe(struct platform_device *pdev) { struct spi_controller *ctlr; @@ -563,6 +739,11 @@ static int mchp_coreqspi_probe(struct platform_device *pdev) SPI_TX_DUAL | SPI_TX_QUAD; ctlr->dev.of_node = np; ctlr->min_speed_hz = clk_get_rate(qspi->clk) / 30; + ctlr->prepare_message = mchp_coreqspi_prepare_message; + ctlr->unprepare_message = mchp_coreqspi_unprepare_message; + ctlr->transfer_one = mchp_coreqspi_transfer_one; + ctlr->num_chipselect = 2; + ctlr->use_gpio_descriptors = true; ret = devm_spi_register_controller(&pdev->dev, ctlr); if (ret) From 3e36c822506d924894ff7de549b9377d3114c2d7 Mon Sep 17 00:00:00 2001 From: Thangaraj Samynathan Date: Tue, 24 Jun 2025 09:00:28 +0530 Subject: [PATCH 22/62] spi: spi-pci1xxxx: Add support for per-instance DMA interrupt vectors Add support for dedicated DMA interrupt vectors for each SPI hardware instance in the pci1xxxx driver. This improves scalability and interrupt handling for systems using multiple SPI instances with DMA. Introduce a constant `NUM_VEC_PER_INST` to define the number of IRQ vectors per instance (main, DMA write, DMA read). Update the `pci1xxxx_spi_internal` structure to use an IRQ array. Refactor IRQ allocation and DMA initialization logic: - Assign separate IRQ vectors for DMA read and write interrupts. - Split the original DMA ISR into two handlers: `pci1xxxx_spi_isr_dma_rd` and `pci1xxxx_spi_isr_dma_wr`. - Configure IMWR registers per instance using cached MSI data. - Move DMA register configuration into a new helper function, `pci1xxxx_spi_dma_config()`. Invoke the DMA initialization after all instances are configured to ensure correct IRQ vector mapping. Signed-off-by: Thangaraj Samynathan Link: https://patch.msgid.link/20250624033028.74389-1-thangaraj.s@microchip.com Signed-off-by: Mark Brown --- drivers/spi/spi-pci1xxxx.c | 212 +++++++++++++++++++++++++------------ 1 file changed, 144 insertions(+), 68 deletions(-) diff --git a/drivers/spi/spi-pci1xxxx.c b/drivers/spi/spi-pci1xxxx.c index a6c8bf228288..f44fe5841139 100644 --- a/drivers/spi/spi-pci1xxxx.c +++ b/drivers/spi/spi-pci1xxxx.c @@ -97,8 +97,8 @@ #define SPI_DMA_CH1_DONE_INT BIT(1) #define SPI_DMA_CH0_ABORT_INT BIT(16) #define SPI_DMA_CH1_ABORT_INT BIT(17) -#define SPI_DMA_DONE_INT_MASK (SPI_DMA_CH0_DONE_INT | SPI_DMA_CH1_DONE_INT) -#define SPI_DMA_ABORT_INT_MASK (SPI_DMA_CH0_ABORT_INT | SPI_DMA_CH1_ABORT_INT) +#define SPI_DMA_DONE_INT_MASK(x) (1 << (x)) +#define SPI_DMA_ABORT_INT_MASK(x) (1 << (16 + (x))) #define DMA_CH_CONTROL_LIE BIT(3) #define DMA_CH_CONTROL_RIE BIT(4) #define DMA_INTR_EN (DMA_CH_CONTROL_RIE | DMA_CH_CONTROL_LIE) @@ -132,10 +132,12 @@ #define SPI_SUSPEND_CONFIG 0x101 #define SPI_RESUME_CONFIG 0x203 +#define NUM_VEC_PER_INST 3 + struct pci1xxxx_spi_internal { u8 hw_inst; u8 clkdiv; - int irq; + int irq[NUM_VEC_PER_INST]; int mode; bool spi_xfer_in_progress; void *rx_buf; @@ -193,6 +195,9 @@ static const struct pci_device_id pci1xxxx_spi_pci_id_table[] = { MODULE_DEVICE_TABLE(pci, pci1xxxx_spi_pci_id_table); +static irqreturn_t pci1xxxx_spi_isr_dma_rd(int irq, void *dev); +static irqreturn_t pci1xxxx_spi_isr_dma_wr(int irq, void *dev); + static int pci1xxxx_set_sys_lock(struct pci1xxxx_spi *par) { writel(SPI_SYSLOCK, par->reg_base + SPI_SYSLOCK_REG); @@ -213,13 +218,16 @@ static void pci1xxxx_release_sys_lock(struct pci1xxxx_spi *par) writel(0x0, par->reg_base + SPI_SYSLOCK_REG); } -static int pci1xxxx_check_spi_can_dma(struct pci1xxxx_spi *spi_bus, int irq) +static int pci1xxxx_check_spi_can_dma(struct pci1xxxx_spi *spi_bus, int hw_inst, int num_vector) { struct pci_dev *pdev = spi_bus->dev; u32 pf_num; u32 regval; int ret; + if (num_vector != hw_inst * NUM_VEC_PER_INST) + return -EOPNOTSUPP; + /* * DEV REV Registers is a system register, HW Syslock bit * should be acquired before accessing the register @@ -247,16 +255,6 @@ static int pci1xxxx_check_spi_can_dma(struct pci1xxxx_spi *spi_bus, int irq) if (spi_bus->dev_rev < 0xC0 || pf_num) return -EOPNOTSUPP; - /* - * DMA Supported only with MSI Interrupts - * One of the SPI instance's MSI vector address and data - * is used for DMA Interrupt - */ - if (!irq_get_msi_desc(irq)) { - dev_warn(&pdev->dev, "Error MSI Interrupt not supported, will operate in PIO mode\n"); - return -EOPNOTSUPP; - } - spi_bus->dma_offset_bar = pcim_iomap(pdev, 2, pci_resource_len(pdev, 2)); if (!spi_bus->dma_offset_bar) { dev_warn(&pdev->dev, "Error failed to map dma bar, will operate in PIO mode\n"); @@ -273,29 +271,90 @@ static int pci1xxxx_check_spi_can_dma(struct pci1xxxx_spi *spi_bus, int irq) return 0; } -static int pci1xxxx_spi_dma_init(struct pci1xxxx_spi *spi_bus, int irq) +static void pci1xxxx_spi_dma_config(struct pci1xxxx_spi *spi_bus) { + struct pci1xxxx_spi_internal *spi_sub_ptr; + u8 iter, irq_index; struct msi_msg msi; + u32 regval; + u16 data; + + irq_index = spi_bus->total_hw_instances; + for (iter = 0; iter < spi_bus->total_hw_instances; iter++) { + spi_sub_ptr = spi_bus->spi_int[iter]; + get_cached_msi_msg(spi_sub_ptr->irq[1], &msi); + if (iter == 0) { + writel(msi.address_hi, spi_bus->dma_offset_bar + + SPI_DMA_INTR_IMWR_WDONE_HIGH); + writel(msi.address_hi, spi_bus->dma_offset_bar + + SPI_DMA_INTR_IMWR_WABORT_HIGH); + writel(msi.address_hi, spi_bus->dma_offset_bar + + SPI_DMA_INTR_IMWR_RDONE_HIGH); + writel(msi.address_hi, spi_bus->dma_offset_bar + + SPI_DMA_INTR_IMWR_RABORT_HIGH); + writel(msi.address_lo, spi_bus->dma_offset_bar + + SPI_DMA_INTR_IMWR_WDONE_LOW); + writel(msi.address_lo, spi_bus->dma_offset_bar + + SPI_DMA_INTR_IMWR_WABORT_LOW); + writel(msi.address_lo, spi_bus->dma_offset_bar + + SPI_DMA_INTR_IMWR_RDONE_LOW); + writel(msi.address_lo, spi_bus->dma_offset_bar + + SPI_DMA_INTR_IMWR_RABORT_LOW); + writel(0, spi_bus->dma_offset_bar + SPI_DMA_INTR_WR_IMWR_DATA); + writel(0, spi_bus->dma_offset_bar + SPI_DMA_INTR_RD_IMWR_DATA); + } + regval = readl(spi_bus->dma_offset_bar + SPI_DMA_INTR_WR_IMWR_DATA); + data = msi.data + irq_index; + writel((regval | (data << (iter * 16))), spi_bus->dma_offset_bar + + SPI_DMA_INTR_WR_IMWR_DATA); + regval = readl(spi_bus->dma_offset_bar + SPI_DMA_INTR_WR_IMWR_DATA); + irq_index++; + + data = msi.data + irq_index; + regval = readl(spi_bus->dma_offset_bar + SPI_DMA_INTR_RD_IMWR_DATA); + writel(regval | (data << (iter * 16)), spi_bus->dma_offset_bar + + SPI_DMA_INTR_RD_IMWR_DATA); + regval = readl(spi_bus->dma_offset_bar + SPI_DMA_INTR_RD_IMWR_DATA); + irq_index++; + } +} + +static int pci1xxxx_spi_dma_init(struct pci1xxxx_spi *spi_bus, int hw_inst, int num_vector) +{ + struct pci1xxxx_spi_internal *spi_sub_ptr; + u8 iter, irq_index; int ret; - ret = pci1xxxx_check_spi_can_dma(spi_bus, irq); + irq_index = hw_inst; + ret = pci1xxxx_check_spi_can_dma(spi_bus, hw_inst, num_vector); if (ret) return ret; spin_lock_init(&spi_bus->dma_reg_lock); - get_cached_msi_msg(irq, &msi); writel(SPI_DMA_ENGINE_EN, spi_bus->dma_offset_bar + SPI_DMA_GLOBAL_WR_ENGINE_EN); writel(SPI_DMA_ENGINE_EN, spi_bus->dma_offset_bar + SPI_DMA_GLOBAL_RD_ENGINE_EN); - writel(msi.address_hi, spi_bus->dma_offset_bar + SPI_DMA_INTR_IMWR_WDONE_HIGH); - writel(msi.address_hi, spi_bus->dma_offset_bar + SPI_DMA_INTR_IMWR_WABORT_HIGH); - writel(msi.address_hi, spi_bus->dma_offset_bar + SPI_DMA_INTR_IMWR_RDONE_HIGH); - writel(msi.address_hi, spi_bus->dma_offset_bar + SPI_DMA_INTR_IMWR_RABORT_HIGH); - writel(msi.address_lo, spi_bus->dma_offset_bar + SPI_DMA_INTR_IMWR_WDONE_LOW); - writel(msi.address_lo, spi_bus->dma_offset_bar + SPI_DMA_INTR_IMWR_WABORT_LOW); - writel(msi.address_lo, spi_bus->dma_offset_bar + SPI_DMA_INTR_IMWR_RDONE_LOW); - writel(msi.address_lo, spi_bus->dma_offset_bar + SPI_DMA_INTR_IMWR_RABORT_LOW); - writel(msi.data, spi_bus->dma_offset_bar + SPI_DMA_INTR_WR_IMWR_DATA); - writel(msi.data, spi_bus->dma_offset_bar + SPI_DMA_INTR_RD_IMWR_DATA); + + for (iter = 0; iter < hw_inst; iter++) { + spi_sub_ptr = spi_bus->spi_int[iter]; + spi_sub_ptr->irq[1] = pci_irq_vector(spi_bus->dev, irq_index); + ret = devm_request_irq(&spi_bus->dev->dev, spi_sub_ptr->irq[1], + pci1xxxx_spi_isr_dma_wr, PCI1XXXX_IRQ_FLAGS, + pci_name(spi_bus->dev), spi_sub_ptr); + if (ret < 0) + return ret; + + irq_index++; + + spi_sub_ptr->irq[2] = pci_irq_vector(spi_bus->dev, irq_index); + ret = devm_request_irq(&spi_bus->dev->dev, spi_sub_ptr->irq[2], + pci1xxxx_spi_isr_dma_rd, PCI1XXXX_IRQ_FLAGS, + pci_name(spi_bus->dev), spi_sub_ptr); + if (ret < 0) + return ret; + + irq_index++; + } + pci1xxxx_spi_dma_config(spi_bus); dma_set_max_seg_size(&spi_bus->dev->dev, PCI1XXXX_SPI_BUFFER_SIZE); spi_bus->can_dma = true; return 0; @@ -401,13 +460,13 @@ static void pci1xxxx_spi_setup(struct pci1xxxx_spi *par, u8 hw_inst, u32 mode, writel(regval, par->reg_base + SPI_MST_CTL_REG_OFFSET(hw_inst)); } -static void pci1xxxx_start_spi_xfer(struct pci1xxxx_spi_internal *p, u8 hw_inst) +static void pci1xxxx_start_spi_xfer(struct pci1xxxx_spi_internal *p) { u32 regval; - regval = readl(p->parent->reg_base + SPI_MST_CTL_REG_OFFSET(hw_inst)); + regval = readl(p->parent->reg_base + SPI_MST_CTL_REG_OFFSET(p->hw_inst)); regval |= SPI_MST_CTL_GO; - writel(regval, p->parent->reg_base + SPI_MST_CTL_REG_OFFSET(hw_inst)); + writel(regval, p->parent->reg_base + SPI_MST_CTL_REG_OFFSET(p->hw_inst)); } static int pci1xxxx_spi_transfer_with_io(struct spi_controller *spi_ctlr, @@ -451,7 +510,7 @@ static int pci1xxxx_spi_transfer_with_io(struct spi_controller *spi_ctlr, &tx_buf[bytes_transfered], len); bytes_transfered += len; pci1xxxx_spi_setup(par, p->hw_inst, spi->mode, clkdiv, len); - pci1xxxx_start_spi_xfer(p, p->hw_inst); + pci1xxxx_start_spi_xfer(p); /* Wait for DMA_TERM interrupt */ result = wait_for_completion_timeout(&p->spi_xfer_done, @@ -627,7 +686,7 @@ static void pci1xxxx_spi_setup_next_dma_transfer(struct pci1xxxx_spi_internal *p } } -static irqreturn_t pci1xxxx_spi_isr_dma(int irq, void *dev) +static irqreturn_t pci1xxxx_spi_isr_dma_rd(int irq, void *dev) { struct pci1xxxx_spi_internal *p = dev; irqreturn_t spi_int_fired = IRQ_NONE; @@ -637,36 +696,53 @@ static irqreturn_t pci1xxxx_spi_isr_dma(int irq, void *dev) spin_lock_irqsave(&p->parent->dma_reg_lock, flags); /* Clear the DMA RD INT and start spi xfer*/ regval = readl(p->parent->dma_offset_bar + SPI_DMA_INTR_RD_STS); - if (regval & SPI_DMA_DONE_INT_MASK) { - if (regval & SPI_DMA_CH0_DONE_INT) - pci1xxxx_start_spi_xfer(p, SPI0); - if (regval & SPI_DMA_CH1_DONE_INT) - pci1xxxx_start_spi_xfer(p, SPI1); - spi_int_fired = IRQ_HANDLED; + if (regval) { + if (regval & SPI_DMA_DONE_INT_MASK(p->hw_inst)) { + pci1xxxx_start_spi_xfer(p); + spi_int_fired = IRQ_HANDLED; + } + if (regval & SPI_DMA_ABORT_INT_MASK(p->hw_inst)) { + p->dma_aborted_rd = true; + spi_int_fired = IRQ_HANDLED; + } } - if (regval & SPI_DMA_ABORT_INT_MASK) { - p->dma_aborted_rd = true; - spi_int_fired = IRQ_HANDLED; - } - writel(regval, p->parent->dma_offset_bar + SPI_DMA_INTR_RD_CLR); + writel((SPI_DMA_DONE_INT_MASK(p->hw_inst) | SPI_DMA_ABORT_INT_MASK(p->hw_inst)), + p->parent->dma_offset_bar + SPI_DMA_INTR_RD_CLR); + spin_unlock_irqrestore(&p->parent->dma_reg_lock, flags); + return spi_int_fired; +} +static irqreturn_t pci1xxxx_spi_isr_dma_wr(int irq, void *dev) +{ + struct pci1xxxx_spi_internal *p = dev; + irqreturn_t spi_int_fired = IRQ_NONE; + unsigned long flags; + u32 regval; + + spin_lock_irqsave(&p->parent->dma_reg_lock, flags); /* Clear the DMA WR INT */ regval = readl(p->parent->dma_offset_bar + SPI_DMA_INTR_WR_STS); - if (regval & SPI_DMA_DONE_INT_MASK) { - if (regval & SPI_DMA_CH0_DONE_INT) - pci1xxxx_spi_setup_next_dma_transfer(p->parent->spi_int[SPI0]); - - if (regval & SPI_DMA_CH1_DONE_INT) - pci1xxxx_spi_setup_next_dma_transfer(p->parent->spi_int[SPI1]); - - spi_int_fired = IRQ_HANDLED; + if (regval) { + if (regval & SPI_DMA_DONE_INT_MASK(p->hw_inst)) { + pci1xxxx_spi_setup_next_dma_transfer(p); + spi_int_fired = IRQ_HANDLED; + } + if (regval & SPI_DMA_ABORT_INT_MASK(p->hw_inst)) { + p->dma_aborted_wr = true; + spi_int_fired = IRQ_HANDLED; + } } - if (regval & SPI_DMA_ABORT_INT_MASK) { - p->dma_aborted_wr = true; - spi_int_fired = IRQ_HANDLED; - } - writel(regval, p->parent->dma_offset_bar + SPI_DMA_INTR_WR_CLR); + writel((SPI_DMA_DONE_INT_MASK(p->hw_inst) | SPI_DMA_ABORT_INT_MASK(p->hw_inst)), + p->parent->dma_offset_bar + SPI_DMA_INTR_WR_CLR); spin_unlock_irqrestore(&p->parent->dma_reg_lock, flags); + return spi_int_fired; +} + +static irqreturn_t pci1xxxx_spi_isr_dma(int irq, void *dev) +{ + struct pci1xxxx_spi_internal *p = dev; + irqreturn_t spi_int_fired = IRQ_NONE; + u32 regval; /* Clear the SPI GO_BIT Interrupt */ regval = readl(p->parent->reg_base + SPI_MST_EVENT_REG_OFFSET(p->hw_inst)); @@ -764,7 +840,7 @@ static int pci1xxxx_spi_probe(struct pci_dev *pdev, const struct pci_device_id * if (!spi_bus->reg_base) return -EINVAL; - num_vector = pci_alloc_irq_vectors(pdev, 1, hw_inst_cnt, + num_vector = pci_alloc_irq_vectors(pdev, 1, hw_inst_cnt * NUM_VEC_PER_INST, PCI_IRQ_INTX | PCI_IRQ_MSI); if (num_vector < 0) { dev_err(&pdev->dev, "Error allocating MSI vectors\n"); @@ -778,27 +854,23 @@ static int pci1xxxx_spi_probe(struct pci_dev *pdev, const struct pci_device_id * regval &= ~SPI_INTR; writel(regval, spi_bus->reg_base + SPI_MST_EVENT_MASK_REG_OFFSET(spi_sub_ptr->hw_inst)); - spi_sub_ptr->irq = pci_irq_vector(pdev, 0); + spi_sub_ptr->irq[0] = pci_irq_vector(pdev, 0); if (num_vector >= hw_inst_cnt) - ret = devm_request_irq(&pdev->dev, spi_sub_ptr->irq, + ret = devm_request_irq(&pdev->dev, spi_sub_ptr->irq[0], pci1xxxx_spi_isr, PCI1XXXX_IRQ_FLAGS, pci_name(pdev), spi_sub_ptr); else - ret = devm_request_irq(&pdev->dev, spi_sub_ptr->irq, + ret = devm_request_irq(&pdev->dev, spi_sub_ptr->irq[0], pci1xxxx_spi_shared_isr, PCI1XXXX_IRQ_FLAGS | IRQF_SHARED, pci_name(pdev), spi_bus); if (ret < 0) { dev_err(&pdev->dev, "Unable to request irq : %d", - spi_sub_ptr->irq); + spi_sub_ptr->irq[0]); return -ENODEV; } - ret = pci1xxxx_spi_dma_init(spi_bus, spi_sub_ptr->irq); - if (ret && ret != -EOPNOTSUPP) - return ret; - /* This register is only applicable for 1st instance */ regval = readl(spi_bus->reg_base + SPI_PCI_CTRL_REG_OFFSET(0)); if (!only_sec_inst) @@ -820,13 +892,13 @@ static int pci1xxxx_spi_probe(struct pci_dev *pdev, const struct pci_device_id * writel(regval, spi_bus->reg_base + SPI_MST_EVENT_MASK_REG_OFFSET(spi_sub_ptr->hw_inst)); if (num_vector >= hw_inst_cnt) { - spi_sub_ptr->irq = pci_irq_vector(pdev, iter); - ret = devm_request_irq(&pdev->dev, spi_sub_ptr->irq, + spi_sub_ptr->irq[0] = pci_irq_vector(pdev, iter); + ret = devm_request_irq(&pdev->dev, spi_sub_ptr->irq[0], pci1xxxx_spi_isr, PCI1XXXX_IRQ_FLAGS, pci_name(pdev), spi_sub_ptr); if (ret < 0) { dev_err(&pdev->dev, "Unable to request irq : %d", - spi_sub_ptr->irq); + spi_sub_ptr->irq[0]); return -ENODEV; } } @@ -849,6 +921,10 @@ static int pci1xxxx_spi_probe(struct pci_dev *pdev, const struct pci_device_id * if (ret) return ret; } + ret = pci1xxxx_spi_dma_init(spi_bus, hw_inst_cnt, num_vector); + if (ret && ret != -EOPNOTSUPP) + return ret; + pci_set_drvdata(pdev, spi_bus); return 0; From e4feefa5c71912ebfcb97a3dbe2b021fd1cea9d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Le=20Goffic?= Date: Mon, 16 Jun 2025 11:21:02 +0200 Subject: [PATCH 23/62] spi: stm32: Add SPI_READY mode to spi controller MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The spi ready functionality is supported by our peripheral in host and target modes on STM32MP2x SoCs. Update our spi controller driver to allow devices to use this functionality. Signed-off-by: Clément Le Goffic Link: https://patch.msgid.link/20250616-spi-upstream-v1-1-7e8593f3f75d@foss.st.com Signed-off-by: Mark Brown --- drivers/spi/spi-stm32.c | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/drivers/spi/spi-stm32.c b/drivers/spi/spi-stm32.c index da3517d7102d..2bcd4a43676d 100644 --- a/drivers/spi/spi-stm32.c +++ b/drivers/spi/spi-stm32.c @@ -154,6 +154,9 @@ /* STM32H7_SPI_I2SCFGR bit fields */ #define STM32H7_SPI_I2SCFGR_I2SMOD BIT(0) +/* STM32MP25_SPICFG2 bit fields */ +#define STM32MP25_SPI_CFG2_RDIOM BIT(13) + /* STM32MP25 SPI registers bit fields */ #define STM32MP25_SPI_HWCFGR1 0x3F0 @@ -222,6 +225,7 @@ struct stm32_spi_reg { * @rx: SPI RX data register * @tx: SPI TX data register * @fullcfg: SPI full or limited feature set register + * @rdy_en: SPI ready feature register */ struct stm32_spi_regspec { const struct stm32_spi_reg en; @@ -235,6 +239,7 @@ struct stm32_spi_regspec { const struct stm32_spi_reg rx; const struct stm32_spi_reg tx; const struct stm32_spi_reg fullcfg; + const struct stm32_spi_reg rdy_en; }; struct stm32_spi; @@ -415,6 +420,8 @@ static const struct stm32_spi_regspec stm32mp25_spi_regspec = { .tx = { STM32H7_SPI_TXDR }, .fullcfg = { STM32MP25_SPI_HWCFGR1, STM32MP25_SPI_HWCFGR1_FULLCFG }, + + .rdy_en = { STM32H7_SPI_CFG2, STM32MP25_SPI_CFG2_RDIOM }, }; static inline void stm32_spi_set_bits(struct stm32_spi *spi, @@ -1172,15 +1179,21 @@ static int stm32_spi_prepare_msg(struct spi_controller *ctrl, else clrb |= spi->cfg->regs->cs_high.mask; - dev_dbg(spi->dev, "cpol=%d cpha=%d lsb_first=%d cs_high=%d\n", + if (spi_dev->mode & SPI_READY) + setb |= spi->cfg->regs->rdy_en.mask; + else + clrb |= spi->cfg->regs->rdy_en.mask; + + dev_dbg(spi->dev, "cpol=%d cpha=%d lsb_first=%d cs_high=%d rdy=%d\n", !!(spi_dev->mode & SPI_CPOL), !!(spi_dev->mode & SPI_CPHA), !!(spi_dev->mode & SPI_LSB_FIRST), - !!(spi_dev->mode & SPI_CS_HIGH)); + !!(spi_dev->mode & SPI_CS_HIGH), + !!(spi_dev->mode & SPI_READY)); spin_lock_irqsave(&spi->lock, flags); - /* CPOL, CPHA and LSB FIRST bits have common register */ + /* CPOL, CPHA, LSB FIRST, CS_HIGH and RDY_EN bits have common register */ if (clrb || setb) writel_relaxed( (readl_relaxed(spi->base + spi->cfg->regs->cpol.reg) & @@ -1768,6 +1781,13 @@ static int stm32_spi_transfer_one_setup(struct stm32_spi *spi, spi->cur_bpw = transfer->bits_per_word; spi->cfg->set_bpw(spi); + if (spi_dev->mode & SPI_READY && spi->cur_bpw < 8) { + writel_relaxed(readl_relaxed(spi->base + spi->cfg->regs->rdy_en.reg) & + ~spi->cfg->regs->rdy_en.mask, + spi->base + spi->cfg->regs->rdy_en.reg); + dev_dbg(spi->dev, "RDY logic disabled as bits per word < 8\n"); + } + /* Update spi->cur_speed with real clock speed */ if (STM32_SPI_HOST_MODE(spi)) { mbr = stm32_spi_prepare_mbr(spi, transfer->speed_hz, @@ -2179,7 +2199,7 @@ static int stm32_spi_probe(struct platform_device *pdev) ctrl->auto_runtime_pm = true; ctrl->bus_num = pdev->id; ctrl->mode_bits = SPI_CPHA | SPI_CPOL | SPI_CS_HIGH | SPI_LSB_FIRST | - SPI_3WIRE; + SPI_3WIRE | SPI_READY; ctrl->bits_per_word_mask = spi->cfg->get_bpw_mask(spi); ctrl->max_speed_hz = spi->clk_rate / spi->cfg->baud_rate_div_min; ctrl->min_speed_hz = spi->clk_rate / spi->cfg->baud_rate_div_max; From 21f1c800f6620e43f31dfd76709dbac8ebaa5a16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Le=20Goffic?= Date: Mon, 16 Jun 2025 11:21:03 +0200 Subject: [PATCH 24/62] spi: stm32: Check for cfg availability in stm32_spi_probe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The stm32_spi_probe function now includes a check to ensure that the pointer returned by of_device_get_match_data is not NULL before accessing its members. This resolves a warning where a potential NULL pointer dereference could occur when accessing cfg->has_device_mode. Before accessing the 'has_device_mode' member, we verify that 'cfg' is not NULL. If 'cfg' is NULL, an error message is logged. This change ensures that the driver does not attempt to access configuration data if it is not available, thus preventing a potential system crash due to a NULL pointer dereference. Signed-off-by: Clément Le Goffic Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202310191831.MLwx1c6x-lkp@intel.com/ Fixes: fee681646fc8 ("spi: stm32: disable device mode with st,stm32f4-spi compatible") Link: https://patch.msgid.link/20250616-spi-upstream-v1-2-7e8593f3f75d@foss.st.com Signed-off-by: Mark Brown --- drivers/spi/spi-stm32.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-stm32.c b/drivers/spi/spi-stm32.c index 2bcd4a43676d..8b61caf770a2 100644 --- a/drivers/spi/spi-stm32.c +++ b/drivers/spi/spi-stm32.c @@ -2089,9 +2089,15 @@ static int stm32_spi_probe(struct platform_device *pdev) struct resource *res; struct reset_control *rst; struct device_node *np = pdev->dev.of_node; + const struct stm32_spi_cfg *cfg; bool device_mode; int ret; - const struct stm32_spi_cfg *cfg = of_device_get_match_data(&pdev->dev); + + cfg = of_device_get_match_data(&pdev->dev); + if (!cfg) { + dev_err(&pdev->dev, "Failed to get match data for platform\n"); + return -ENODEV; + } device_mode = of_property_read_bool(np, "spi-slave"); if (!cfg->has_device_mode && device_mode) { From d17dd2f1d8a1d919e39c6302b024f135a2f90773 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Le=20Goffic?= Date: Mon, 16 Jun 2025 11:21:05 +0200 Subject: [PATCH 25/62] spi: stm32: use STM32 DMA with STM32 MDMA to enhance DDR use MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The STM32 DMA doesn't have the ability to generate convenient burst transfer on the DDR, ensuring the best load of the AXI & DDR. To avoid this bad load of the AXI & DDR, STM32 MDMA can be used to transfer data to the DDR, being triggered by STM32 DMA channel transfer completion. An SRAM buffer is used between DMA and MDMA. So the MDMA always does MEM_TO_MEM transfers (from/to SRAM to/from DDR), and the DMA uses SRAM instead of DDR with DEV_TO_MEM transfers. SPI RX DMA (DEV_TO_MEM) becomes: SPI RX FIFO ==DMA==> SRAM ==MDMA==> DDR In RX (DEV_TO_MEM), EOT interrupt is used to pause the DMA channel (which will raise a transfer complete) to trigger the MDMA to flush the SRAM (when transfer length is not aligned on SRAM period). TX remains on the former implementation. Signed-off-by: Clément Le Goffic Link: https://patch.msgid.link/20250616-spi-upstream-v1-4-7e8593f3f75d@foss.st.com Signed-off-by: Mark Brown --- drivers/spi/spi-stm32.c | 251 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 228 insertions(+), 23 deletions(-) diff --git a/drivers/spi/spi-stm32.c b/drivers/spi/spi-stm32.c index 8b61caf770a2..8581f24c111f 100644 --- a/drivers/spi/spi-stm32.c +++ b/drivers/spi/spi-stm32.c @@ -9,7 +9,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -328,6 +330,11 @@ struct stm32_spi_cfg { * @dma_rx: dma channel for RX transfer * @phys_addr: SPI registers physical base address * @device_mode: the controller is configured as SPI device + * @sram_pool: SRAM pool for DMA transfers + * @sram_rx_buf_size: size of SRAM buffer for RX transfer + * @sram_rx_buf: SRAM buffer for RX transfer + * @sram_dma_rx_buf: SRAM buffer physical address for RX transfer + * @mdma_rx: MDMA channel for RX transfer */ struct stm32_spi { struct device *dev; @@ -362,6 +369,12 @@ struct stm32_spi { dma_addr_t phys_addr; bool device_mode; + + struct gen_pool *sram_pool; + size_t sram_rx_buf_size; + void *sram_rx_buf; + dma_addr_t sram_dma_rx_buf; + struct dma_chan *mdma_rx; }; static const struct stm32_spi_regspec stm32fx_spi_regspec = { @@ -885,8 +898,11 @@ static void stm32h7_spi_disable(struct stm32_spi *spi) if (spi->cur_usedma && spi->dma_tx) dmaengine_terminate_async(spi->dma_tx); - if (spi->cur_usedma && spi->dma_rx) + if (spi->cur_usedma && spi->dma_rx) { dmaengine_terminate_async(spi->dma_rx); + if (spi->mdma_rx) + dmaengine_terminate_async(spi->mdma_rx); + } stm32_spi_clr_bits(spi, STM32H7_SPI_CR1, STM32H7_SPI_CR1_SPE); @@ -1098,10 +1114,13 @@ static irqreturn_t stm32h7_spi_irq_thread(int irq, void *dev_id) } if (sr & STM32H7_SPI_SR_EOT) { + dev_dbg(spi->dev, "End of transfer\n"); if (!spi->cur_usedma && (spi->rx_buf && (spi->rx_len > 0))) stm32h7_spi_read_rxfifo(spi); if (!spi->cur_usedma || - (spi->cur_comm == SPI_SIMPLEX_TX || spi->cur_comm == SPI_3WIRE_TX)) + (spi->cur_comm == SPI_SIMPLEX_TX || spi->cur_comm == SPI_3WIRE_TX) || + (spi->mdma_rx && (spi->cur_comm == SPI_SIMPLEX_RX || + spi->cur_comm == SPI_FULL_DUPLEX))) end = true; } @@ -1118,6 +1137,11 @@ static irqreturn_t stm32h7_spi_irq_thread(int irq, void *dev_id) spin_unlock_irqrestore(&spi->lock, flags); if (end) { + if (spi->cur_usedma && spi->mdma_rx) { + dmaengine_pause(spi->dma_rx); + /* Wait for callback */ + return IRQ_HANDLED; + } stm32h7_spi_disable(spi); spi_finalize_current_transfer(ctrl); } @@ -1423,6 +1447,8 @@ static void stm32h7_spi_transfer_one_dma_start(struct stm32_spi *spi) /* Enable the interrupts */ if (spi->cur_comm == SPI_SIMPLEX_TX || spi->cur_comm == SPI_3WIRE_TX) ier |= STM32H7_SPI_IER_EOTIE | STM32H7_SPI_IER_TXTFIE; + if (spi->mdma_rx && (spi->cur_comm == SPI_SIMPLEX_RX || spi->cur_comm == SPI_FULL_DUPLEX)) + ier |= STM32H7_SPI_IER_EOTIE; stm32_spi_set_bits(spi, STM32H7_SPI_IER, ier); @@ -1432,6 +1458,119 @@ static void stm32h7_spi_transfer_one_dma_start(struct stm32_spi *spi) stm32_spi_set_bits(spi, STM32H7_SPI_CR1, STM32H7_SPI_CR1_CSTART); } +/** + * stm32_spi_prepare_rx_dma_mdma_chaining - Prepare RX DMA and MDMA chaining + * @spi: pointer to the spi controller data structure + * @xfer: pointer to the spi transfer + * @rx_dma_conf: pointer to the DMA configuration for RX channel + * @rx_dma_desc: pointer to the RX DMA descriptor + * @rx_mdma_desc: pointer to the RX MDMA descriptor + * + * It must return 0 if the chaining is possible or an error code if not. + */ +static int stm32_spi_prepare_rx_dma_mdma_chaining(struct stm32_spi *spi, + struct spi_transfer *xfer, + struct dma_slave_config *rx_dma_conf, + struct dma_async_tx_descriptor **rx_dma_desc, + struct dma_async_tx_descriptor **rx_mdma_desc) +{ + struct dma_slave_config rx_mdma_conf = {0}; + u32 sram_period, nents = 0, spi_s_len; + struct sg_table dma_sgt, mdma_sgt; + struct scatterlist *spi_s, *s; + dma_addr_t dma_buf; + int i, ret; + + sram_period = spi->sram_rx_buf_size / 2; + + /* Configure MDMA RX channel */ + rx_mdma_conf.direction = rx_dma_conf->direction; + rx_mdma_conf.src_addr = spi->sram_dma_rx_buf; + rx_mdma_conf.peripheral_config = rx_dma_conf->peripheral_config; + rx_mdma_conf.peripheral_size = rx_dma_conf->peripheral_size; + dmaengine_slave_config(spi->mdma_rx, &rx_mdma_conf); + + /* Count the number of entries needed */ + for_each_sg(xfer->rx_sg.sgl, spi_s, xfer->rx_sg.nents, i) + if (sg_dma_len(spi_s) > sram_period) + nents += DIV_ROUND_UP(sg_dma_len(spi_s), sram_period); + else + nents++; + + /* Prepare DMA slave_sg DBM transfer DEV_TO_MEM (RX>MEM=SRAM) */ + ret = sg_alloc_table(&dma_sgt, nents, GFP_ATOMIC); + if (ret) + return ret; + + spi_s = xfer->rx_sg.sgl; + spi_s_len = sg_dma_len(spi_s); + dma_buf = spi->sram_dma_rx_buf; + for_each_sg(dma_sgt.sgl, s, dma_sgt.nents, i) { + size_t bytes = min_t(size_t, spi_s_len, sram_period); + + sg_dma_len(s) = bytes; + sg_dma_address(s) = dma_buf; + spi_s_len -= bytes; + + if (!spi_s_len && sg_next(spi_s)) { + spi_s = sg_next(spi_s); + spi_s_len = sg_dma_len(spi_s); + dma_buf = spi->sram_dma_rx_buf; + } else { /* DMA configured in DBM: it will swap between the SRAM periods */ + if (i & 1) + dma_buf += sram_period; + else + dma_buf = spi->sram_dma_rx_buf; + } + } + + *rx_dma_desc = dmaengine_prep_slave_sg(spi->dma_rx, dma_sgt.sgl, + dma_sgt.nents, rx_dma_conf->direction, + DMA_PREP_INTERRUPT); + sg_free_table(&dma_sgt); + + if (!rx_dma_desc) + return -EINVAL; + + /* Prepare MDMA slave_sg transfer MEM_TO_MEM (SRAM>DDR) */ + ret = sg_alloc_table(&mdma_sgt, nents, GFP_ATOMIC); + if (ret) { + rx_dma_desc = NULL; + return ret; + } + + spi_s = xfer->rx_sg.sgl; + spi_s_len = sg_dma_len(spi_s); + dma_buf = sg_dma_address(spi_s); + for_each_sg(mdma_sgt.sgl, s, mdma_sgt.nents, i) { + size_t bytes = min_t(size_t, spi_s_len, sram_period); + + sg_dma_len(s) = bytes; + sg_dma_address(s) = dma_buf; + spi_s_len -= bytes; + + if (!spi_s_len && sg_next(spi_s)) { + spi_s = sg_next(spi_s); + spi_s_len = sg_dma_len(spi_s); + dma_buf = sg_dma_address(spi_s); + } else { + dma_buf += bytes; + } + } + + *rx_mdma_desc = dmaengine_prep_slave_sg(spi->mdma_rx, mdma_sgt.sgl, + mdma_sgt.nents, rx_mdma_conf.direction, + DMA_PREP_INTERRUPT); + sg_free_table(&mdma_sgt); + + if (!rx_mdma_desc) { + rx_dma_desc = NULL; + return -EINVAL; + } + + return 0; +} + /** * stm32_spi_transfer_one_dma - transfer a single spi_transfer using DMA * @spi: pointer to the spi controller data structure @@ -1443,38 +1582,43 @@ static void stm32h7_spi_transfer_one_dma_start(struct stm32_spi *spi) static int stm32_spi_transfer_one_dma(struct stm32_spi *spi, struct spi_transfer *xfer) { + struct dma_async_tx_descriptor *rx_mdma_desc = NULL, *rx_dma_desc = NULL; + struct dma_async_tx_descriptor *tx_dma_desc = NULL; struct dma_slave_config tx_dma_conf, rx_dma_conf; - struct dma_async_tx_descriptor *tx_dma_desc, *rx_dma_desc; unsigned long flags; + int ret = 0; spin_lock_irqsave(&spi->lock, flags); - rx_dma_desc = NULL; if (spi->rx_buf && spi->dma_rx) { stm32_spi_dma_config(spi, spi->dma_rx, &rx_dma_conf, DMA_DEV_TO_MEM); - dmaengine_slave_config(spi->dma_rx, &rx_dma_conf); + if (spi->mdma_rx) { + rx_dma_conf.peripheral_size = 1; + dmaengine_slave_config(spi->dma_rx, &rx_dma_conf); - /* Enable Rx DMA request */ - stm32_spi_set_bits(spi, spi->cfg->regs->dma_rx_en.reg, - spi->cfg->regs->dma_rx_en.mask); - - rx_dma_desc = dmaengine_prep_slave_sg( - spi->dma_rx, xfer->rx_sg.sgl, - xfer->rx_sg.nents, - rx_dma_conf.direction, - DMA_PREP_INTERRUPT); + ret = stm32_spi_prepare_rx_dma_mdma_chaining(spi, xfer, &rx_dma_conf, + &rx_dma_desc, &rx_mdma_desc); + if (ret) { /* RX DMA MDMA chaining not possible, fallback to DMA only */ + rx_dma_conf.peripheral_config = 0; + rx_dma_desc = NULL; + } + } + if (!rx_dma_desc) { + dmaengine_slave_config(spi->dma_rx, &rx_dma_conf); + rx_dma_desc = dmaengine_prep_slave_sg(spi->dma_rx, xfer->rx_sg.sgl, + xfer->rx_sg.nents, + rx_dma_conf.direction, + DMA_PREP_INTERRUPT); + } } - tx_dma_desc = NULL; if (spi->tx_buf && spi->dma_tx) { stm32_spi_dma_config(spi, spi->dma_tx, &tx_dma_conf, DMA_MEM_TO_DEV); dmaengine_slave_config(spi->dma_tx, &tx_dma_conf); - - tx_dma_desc = dmaengine_prep_slave_sg( - spi->dma_tx, xfer->tx_sg.sgl, - xfer->tx_sg.nents, - tx_dma_conf.direction, - DMA_PREP_INTERRUPT); + tx_dma_desc = dmaengine_prep_slave_sg(spi->dma_tx, xfer->tx_sg.sgl, + xfer->tx_sg.nents, + tx_dma_conf.direction, + DMA_PREP_INTERRUPT); } if ((spi->tx_buf && spi->dma_tx && !tx_dma_desc) || @@ -1485,9 +1629,25 @@ static int stm32_spi_transfer_one_dma(struct stm32_spi *spi, goto dma_desc_error; if (rx_dma_desc) { - rx_dma_desc->callback = spi->cfg->dma_rx_cb; - rx_dma_desc->callback_param = spi; + if (rx_mdma_desc) { + rx_mdma_desc->callback = spi->cfg->dma_rx_cb; + rx_mdma_desc->callback_param = spi; + } else { + rx_dma_desc->callback = spi->cfg->dma_rx_cb; + rx_dma_desc->callback_param = spi; + } + /* Enable Rx DMA request */ + stm32_spi_set_bits(spi, spi->cfg->regs->dma_rx_en.reg, + spi->cfg->regs->dma_rx_en.mask); + if (rx_mdma_desc) { + if (dma_submit_error(dmaengine_submit(rx_mdma_desc))) { + dev_err(spi->dev, "Rx MDMA submit failed\n"); + goto dma_desc_error; + } + /* Enable Rx MDMA channel */ + dma_async_issue_pending(spi->mdma_rx); + } if (dma_submit_error(dmaengine_submit(rx_dma_desc))) { dev_err(spi->dev, "Rx DMA submit failed\n"); goto dma_desc_error; @@ -1522,6 +1682,8 @@ static int stm32_spi_transfer_one_dma(struct stm32_spi *spi, return 1; dma_submit_error: + if (spi->mdma_rx) + dmaengine_terminate_sync(spi->mdma_rx); if (spi->dma_rx) dmaengine_terminate_sync(spi->dma_rx); @@ -1533,6 +1695,9 @@ static int stm32_spi_transfer_one_dma(struct stm32_spi *spi, dev_info(spi->dev, "DMA issue: fall back to irq transfer\n"); + if (spi->sram_rx_buf) + memset(spi->sram_rx_buf, 0, spi->sram_rx_buf_size); + spi->cur_usedma = false; return spi->cfg->transfer_one_irq(spi); } @@ -1891,6 +2056,9 @@ static int stm32_spi_unprepare_msg(struct spi_controller *ctrl, spi->cfg->disable(spi); + if (spi->sram_rx_buf) + memset(spi->sram_rx_buf, 0, spi->sram_rx_buf_size); + return 0; } @@ -2245,6 +2413,33 @@ static int stm32_spi_probe(struct platform_device *pdev) if (spi->dma_tx || spi->dma_rx) ctrl->can_dma = stm32_spi_can_dma; + spi->sram_pool = of_gen_pool_get(pdev->dev.of_node, "sram", 0); + if (spi->sram_pool) { + spi->sram_rx_buf_size = gen_pool_size(spi->sram_pool); + dev_info(&pdev->dev, "SRAM pool: %zu KiB for RX DMA/MDMA chaining\n", + spi->sram_rx_buf_size / 1024); + spi->sram_rx_buf = gen_pool_dma_zalloc(spi->sram_pool, spi->sram_rx_buf_size, + &spi->sram_dma_rx_buf); + if (!spi->sram_rx_buf) { + dev_err(&pdev->dev, "failed to allocate SRAM buffer\n"); + } else { + spi->mdma_rx = dma_request_chan(spi->dev, "rxm2m"); + if (IS_ERR(spi->mdma_rx)) { + ret = PTR_ERR(spi->mdma_rx); + spi->mdma_rx = NULL; + if (ret == -EPROBE_DEFER) { + goto err_pool_free; + } else { + gen_pool_free(spi->sram_pool, + (unsigned long)spi->sram_rx_buf, + spi->sram_rx_buf_size); + dev_warn(&pdev->dev, + "failed to request rx mdma channel, DMA only\n"); + } + } + } + } + pm_runtime_set_autosuspend_delay(&pdev->dev, STM32_SPI_AUTOSUSPEND_DELAY); pm_runtime_use_autosuspend(&pdev->dev); @@ -2272,6 +2467,11 @@ static int stm32_spi_probe(struct platform_device *pdev) pm_runtime_put_noidle(&pdev->dev); pm_runtime_set_suspended(&pdev->dev); pm_runtime_dont_use_autosuspend(&pdev->dev); + + if (spi->mdma_rx) + dma_release_channel(spi->mdma_rx); +err_pool_free: + gen_pool_free(spi->sram_pool, (unsigned long)spi->sram_rx_buf, spi->sram_rx_buf_size); err_dma_release: if (spi->dma_tx) dma_release_channel(spi->dma_tx); @@ -2302,6 +2502,11 @@ static void stm32_spi_remove(struct platform_device *pdev) dma_release_channel(ctrl->dma_tx); if (ctrl->dma_rx) dma_release_channel(ctrl->dma_rx); + if (spi->mdma_rx) + dma_release_channel(spi->mdma_rx); + if (spi->sram_rx_buf) + gen_pool_free(spi->sram_pool, (unsigned long)spi->sram_rx_buf, + spi->sram_rx_buf_size); clk_disable_unprepare(spi->clk); From 4956bf44524394211ca80aa04d0c9e1e9bb0219d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Le=20Goffic?= Date: Mon, 16 Jun 2025 11:21:06 +0200 Subject: [PATCH 26/62] spi: stm32: deprecate `st,spi-midi-ns` property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `st,spi-midi-ns` property, which was used to set a nanosecond delay between transferred words, is now deprecated. This functionality is now supported by the SPI framework through the `spi_transfer` struct's `word_delay` variable. Therefore, the private `st,spi-midi-ns` property is no longer needed and has been deprecated in favor of the generic solution. Signed-off-by: Clément Le Goffic Link: https://patch.msgid.link/20250616-spi-upstream-v1-5-7e8593f3f75d@foss.st.com Signed-off-by: Mark Brown --- drivers/spi/spi-stm32.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/drivers/spi/spi-stm32.c b/drivers/spi/spi-stm32.c index 8581f24c111f..3d20f09f1ae7 100644 --- a/drivers/spi/spi-stm32.c +++ b/drivers/spi/spi-stm32.c @@ -283,7 +283,7 @@ struct stm32_spi_cfg { int (*config)(struct stm32_spi *spi); void (*set_bpw)(struct stm32_spi *spi); int (*set_mode)(struct stm32_spi *spi, unsigned int comm_type); - void (*set_data_idleness)(struct stm32_spi *spi, u32 length); + void (*set_data_idleness)(struct stm32_spi *spi, struct spi_transfer *xfer); int (*set_number_of_data)(struct stm32_spi *spi, u32 length); void (*write_tx)(struct stm32_spi *spi); void (*read_rx)(struct stm32_spi *spi); @@ -1880,11 +1880,26 @@ static int stm32h7_spi_set_mode(struct stm32_spi *spi, unsigned int comm_type) * stm32h7_spi_data_idleness - configure minimum time delay inserted between two * consecutive data frames in host mode * @spi: pointer to the spi controller data structure - * @len: transfer len + * @xfer: pointer to spi transfer */ -static void stm32h7_spi_data_idleness(struct stm32_spi *spi, u32 len) +static void stm32h7_spi_data_idleness(struct stm32_spi *spi, struct spi_transfer *xfer) { u32 cfg2_clrb = 0, cfg2_setb = 0; + u32 len = xfer->len; + u32 spi_delay_ns; + + spi_delay_ns = spi_delay_to_ns(&xfer->word_delay, xfer); + + if (spi->cur_midi != 0) { + dev_warn(spi->dev, "st,spi-midi-ns DT property is deprecated\n"); + if (spi_delay_ns) { + dev_warn(spi->dev, "Overriding st,spi-midi-ns with word_delay_ns %d\n", + spi_delay_ns); + spi->cur_midi = spi_delay_ns; + } + } else { + spi->cur_midi = spi_delay_ns; + } cfg2_clrb |= STM32H7_SPI_CFG2_MIDI; if ((len > 1) && (spi->cur_midi > 0)) { @@ -1975,7 +1990,7 @@ static int stm32_spi_transfer_one_setup(struct stm32_spi *spi, spi->cur_comm = comm_type; if (STM32_SPI_HOST_MODE(spi) && spi->cfg->set_data_idleness) - spi->cfg->set_data_idleness(spi, transfer->len); + spi->cfg->set_data_idleness(spi, transfer); if (spi->cur_bpw <= 8) nb_words = transfer->len; From bd60f94a3eb4f80cb66c9687d640554fd0c579d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Le=20Goffic?= Date: Mon, 16 Jun 2025 11:21:04 +0200 Subject: [PATCH 27/62] spi: dt-bindings: stm32: update bindings with SPI Rx DMA-MDMA chaining MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add MDMA channel, and new sram property which are mandatory to enable SPI Rx DMA-MDMA chaining. Signed-off-by: Clément Le Goffic Link: https://patch.msgid.link/20250616-spi-upstream-v1-3-7e8593f3f75d@foss.st.com Signed-off-by: Mark Brown --- .../devicetree/bindings/spi/st,stm32-spi.yaml | 48 ++++++++++++++++++- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/spi/st,stm32-spi.yaml b/Documentation/devicetree/bindings/spi/st,stm32-spi.yaml index 76e43c0ce36c..ca880a226afa 100644 --- a/Documentation/devicetree/bindings/spi/st,stm32-spi.yaml +++ b/Documentation/devicetree/bindings/spi/st,stm32-spi.yaml @@ -18,6 +18,38 @@ maintainers: allOf: - $ref: spi-controller.yaml# + - if: + properties: + compatible: + contains: + const: st,stm32f4-spi + + then: + properties: + st,spi-midi-ns: false + sram: false + dmas: + maxItems: 2 + dma-names: + items: + - const: rx + - const: tx + + - if: + properties: + compatible: + contains: + const: st,stm32mp25-spi + + then: + properties: + sram: false + dmas: + maxItems: 2 + dma-names: + items: + - const: rx + - const: tx properties: compatible: @@ -41,16 +73,28 @@ properties: dmas: description: | - DMA specifiers for tx and rx dma. DMA fifo mode must be used. See - the STM32 DMA controllers bindings Documentation/devicetree/bindings/dma/stm32/*.yaml. + DMA specifiers for tx and rx channels. DMA fifo mode must be used. See + the STM32 DMA bindings Documentation/devicetree/bindings/dma/stm32/st,*dma.yaml + minItems: 2 items: - description: rx DMA channel - description: tx DMA channel + - description: rxm2m MDMA channel dma-names: + minItems: 2 items: - const: rx - const: tx + - const: rxm2m + + sram: + $ref: /schemas/types.yaml#/definitions/phandle + description: | + Phandles to a reserved SRAM region which is used as temporary + storage memory between DMA and MDMA engines. + The region should be defined as child node of the AHB SRAM node + as per the generic bindings in Documentation/devicetree/bindings/sram/sram.yaml access-controllers: minItems: 1 From 9a944494c299fabf3cc781798eb7c02a0bece364 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Le=20Goffic?= Date: Mon, 16 Jun 2025 11:21:07 +0200 Subject: [PATCH 28/62] spi: dt-bindings: stm32: deprecate `st,spi-midi-ns` property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The vendor `st,spi-midi-ns` property is no longer needed and has been deprecated in favor of a generic solution. Signed-off-by: Clément Le Goffic Link: https://patch.msgid.link/20250616-spi-upstream-v1-6-7e8593f3f75d@foss.st.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/spi/spi-peripheral-props.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/spi/spi-peripheral-props.yaml b/Documentation/devicetree/bindings/spi/spi-peripheral-props.yaml index 8fc17e16efb2..8b6e8fc009db 100644 --- a/Documentation/devicetree/bindings/spi/spi-peripheral-props.yaml +++ b/Documentation/devicetree/bindings/spi/spi-peripheral-props.yaml @@ -115,6 +115,7 @@ properties: maxItems: 4 st,spi-midi-ns: + deprecated: true description: | Only for STM32H7, (Master Inter-Data Idleness) minimum time delay in nanoseconds inserted between two consecutive data frames. From 08bf1663c21a3e815eda28fa242d84c945ca3b94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bence=20Cs=C3=B3k=C3=A1s?= Date: Tue, 10 Jun 2025 10:22:53 +0200 Subject: [PATCH 29/62] dmaengine: Add devm_dma_request_chan() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Expand the arsenal of devm functions for DMA devices, this time for requesting channels. Signed-off-by: Bence Csókás Link: https://lore.kernel.org/r/20250610082256.400492-2-csokas.bence@prolan.hu Signed-off-by: Vinod Koul --- drivers/dma/dmaengine.c | 30 ++++++++++++++++++++++++++++++ include/linux/dmaengine.h | 7 +++++++ 2 files changed, 37 insertions(+) diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index 758fcd0546d8..ca13cd39330b 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -926,6 +926,36 @@ void dma_release_channel(struct dma_chan *chan) } EXPORT_SYMBOL_GPL(dma_release_channel); +static void dmaenginem_release_channel(void *chan) +{ + dma_release_channel(chan); +} + +/** + * devm_dma_request_chan - try to allocate an exclusive slave channel + * @dev: pointer to client device structure + * @name: slave channel name + * + * Returns pointer to appropriate DMA channel on success or an error pointer. + * + * The operation is managed and will be undone on driver detach. + */ + +struct dma_chan *devm_dma_request_chan(struct device *dev, const char *name) +{ + struct dma_chan *chan = dma_request_chan(dev, name); + int ret = 0; + + if (!IS_ERR(chan)) + ret = devm_add_action_or_reset(dev, dmaenginem_release_channel, chan); + + if (ret) + return ERR_PTR(ret); + + return chan; +} +EXPORT_SYMBOL_GPL(devm_dma_request_chan); + /** * dmaengine_get - register interest in dma_channels */ diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index bb146c5ac3e4..6de7c05d6bd8 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -1524,6 +1524,7 @@ struct dma_chan *__dma_request_channel(const dma_cap_mask_t *mask, struct dma_chan *dma_request_chan(struct device *dev, const char *name); struct dma_chan *dma_request_chan_by_mask(const dma_cap_mask_t *mask); +struct dma_chan *devm_dma_request_chan(struct device *dev, const char *name); void dma_release_channel(struct dma_chan *chan); int dma_get_slave_caps(struct dma_chan *chan, struct dma_slave_caps *caps); @@ -1560,6 +1561,12 @@ static inline struct dma_chan *dma_request_chan_by_mask( { return ERR_PTR(-ENODEV); } + +static inline struct dma_chan *devm_dma_request_chan(struct device *dev, const char *name) +{ + return ERR_PTR(-ENODEV); +} + static inline void dma_release_channel(struct dma_chan *chan) { } From 2555691165a0285a4617230fed859f20dcc51608 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bence=20Cs=C3=B3k=C3=A1s?= Date: Tue, 10 Jun 2025 10:22:54 +0200 Subject: [PATCH 30/62] spi: atmel-quadspi: Use `devm_dma_request_chan()` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Leave releasing of DMA channels up to the devm facilities. This way we can eliminate the rest of the "goto ladder". Signed-off-by: Bence Csókás Link: https://patch.msgid.link/20250610082256.400492-3-csokas.bence@prolan.hu Signed-off-by: Mark Brown --- drivers/spi/atmel-quadspi.c | 48 ++++++++++--------------------------- 1 file changed, 13 insertions(+), 35 deletions(-) diff --git a/drivers/spi/atmel-quadspi.c b/drivers/spi/atmel-quadspi.c index 2f6797324227..fc555c0ce52e 100644 --- a/drivers/spi/atmel-quadspi.c +++ b/drivers/spi/atmel-quadspi.c @@ -1285,18 +1285,21 @@ static int atmel_qspi_dma_init(struct spi_controller *ctrl) struct atmel_qspi *aq = spi_controller_get_devdata(ctrl); int ret; - aq->rx_chan = dma_request_chan(&aq->pdev->dev, "rx"); + aq->rx_chan = devm_dma_request_chan(&aq->pdev->dev, "rx"); if (IS_ERR(aq->rx_chan)) { ret = dev_err_probe(&aq->pdev->dev, PTR_ERR(aq->rx_chan), "RX DMA channel is not available\n"); - goto null_rx_chan; + aq->rx_chan = NULL; + return ret; } - aq->tx_chan = dma_request_chan(&aq->pdev->dev, "tx"); + aq->tx_chan = devm_dma_request_chan(&aq->pdev->dev, "tx"); if (IS_ERR(aq->tx_chan)) { ret = dev_err_probe(&aq->pdev->dev, PTR_ERR(aq->tx_chan), "TX DMA channel is not available\n"); - goto release_rx_chan; + aq->rx_chan = NULL; + aq->tx_chan = NULL; + return ret; } ctrl->dma_rx = aq->rx_chan; @@ -1307,21 +1310,6 @@ static int atmel_qspi_dma_init(struct spi_controller *ctrl) dma_chan_name(aq->tx_chan), dma_chan_name(aq->rx_chan)); return 0; - -release_rx_chan: - dma_release_channel(aq->rx_chan); - aq->tx_chan = NULL; -null_rx_chan: - aq->rx_chan = NULL; - return ret; -} - -static void atmel_qspi_dma_release(struct atmel_qspi *aq) -{ - if (aq->rx_chan) - dma_release_channel(aq->rx_chan); - if (aq->tx_chan) - dma_release_channel(aq->tx_chan); } static const struct atmel_qspi_ops atmel_qspi_ops = { @@ -1426,14 +1414,13 @@ static int atmel_qspi_probe(struct platform_device *pdev) /* Request the IRQ */ irq = platform_get_irq(pdev, 0); - if (irq < 0) { - err = irq; - goto dma_release; - } + if (irq < 0) + return irq; + err = devm_request_irq(&pdev->dev, irq, atmel_qspi_interrupt, 0, dev_name(&pdev->dev), aq); if (err) - goto dma_release; + return err; pm_runtime_set_autosuspend_delay(&pdev->dev, 500); pm_runtime_use_autosuspend(&pdev->dev); @@ -1442,22 +1429,16 @@ static int atmel_qspi_probe(struct platform_device *pdev) err = atmel_qspi_init(aq); if (err) - goto dma_release; + return err; err = spi_register_controller(ctrl); if (err) - goto dma_release; + return err; pm_runtime_mark_last_busy(&pdev->dev); pm_runtime_put_autosuspend(&pdev->dev); return 0; - -dma_release: - if (aq->caps->has_dma) - atmel_qspi_dma_release(aq); - - return err; } static int atmel_qspi_sama7g5_suspend(struct atmel_qspi *aq) @@ -1507,9 +1488,6 @@ static void atmel_qspi_remove(struct platform_device *pdev) ret = pm_runtime_get_sync(&pdev->dev); if (ret >= 0) { - if (aq->caps->has_dma) - atmel_qspi_dma_release(aq); - if (aq->caps->has_gclk) { ret = atmel_qspi_sama7g5_suspend(aq); if (ret) From ac4c064f67d3cdf9118b9b09c1e3b28b6c10a7ea Mon Sep 17 00:00:00 2001 From: Frank Li Date: Wed, 25 Jun 2025 17:52:54 -0400 Subject: [PATCH 31/62] spi: dt-bindings: add nxp,lpc3220-spi.yaml Add lpc3220 spi controller binding doc to fix below CHECK_DTBS warning: arch/arm/boot/dts/nxp/lpc/lpc3250-ea3250.dtb: /ahb/apb/spi@20088000: failed to match any schema with compatible: ['nxp,lpc3220-spi'] Signed-off-by: Frank Li Link: https://patch.msgid.link/20250625215255.2640538-1-Frank.Li@nxp.com Signed-off-by: Mark Brown --- .../bindings/spi/nxp,lpc3220-spi.yaml | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 Documentation/devicetree/bindings/spi/nxp,lpc3220-spi.yaml diff --git a/Documentation/devicetree/bindings/spi/nxp,lpc3220-spi.yaml b/Documentation/devicetree/bindings/spi/nxp,lpc3220-spi.yaml new file mode 100644 index 000000000000..d5f780912f21 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/nxp,lpc3220-spi.yaml @@ -0,0 +1,44 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/spi/nxp,lpc3220-spi.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NXP LPC3220 SPI controller + +maintainers: + - Frank Li + +properties: + compatible: + enum: + - nxp,lpc3220-spi + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + +allOf: + - $ref: spi-controller.yaml# + +unevaluatedProperties: false + +required: + - compatible + - reg + - clocks + +examples: + - | + #include + + spi@20088000 { + compatible = "nxp,lpc3220-spi"; + reg = <0x20088000 0x1000>; + clocks = <&clk LPC32XX_CLK_SPI1>; + #address-cells = <1>; + #size-cells = <0>; + }; + From 7e1c28fbf235791cb5046fafdac5bc16fe8e788d Mon Sep 17 00:00:00 2001 From: Thangaraj Samynathan Date: Mon, 30 Jun 2025 13:02:33 +0530 Subject: [PATCH 32/62] spi: spi-pci1xxxx: enable concurrent DMA read/write across SPI transfers Refactor the pci1xxxx SPI driver to allow overlapping DMA read and write operations across SPI transfers. This improves throughput and reduces idle time between SPI transactions. Transfer sequence: - Start with a DMA read to load TX data from host to device buffer. - After DMA read completes, trigger the SPI transfer. - On SPI completion: - Start DMA write to copy received data from RX buffer to host. - Start the next DMA read to prepare TX data for the following transfer. - Begin the next SPI transfer after both DMA write and read complete. To implement this sequence, the following changes were made: - Added dma_completion_count to track and synchronize DMA completions. - Split DMA setup into separate functions for TX (read) and RX (write). - Introduced separate spinlocks for safe access to RD and WR DMA registers. This new flow enables efficient pipelining by overlapping data preparation and completion stages, leading to better SPI transfer performance and utilization of DMA engines. Signed-off-by: Thangaraj Samynathan Link: https://patch.msgid.link/20250630073233.7356-1-thangaraj.s@microchip.com Signed-off-by: Mark Brown --- drivers/spi/spi-pci1xxxx.c | 82 ++++++++++++++++++++++---------------- 1 file changed, 47 insertions(+), 35 deletions(-) diff --git a/drivers/spi/spi-pci1xxxx.c b/drivers/spi/spi-pci1xxxx.c index f44fe5841139..8577a19705de 100644 --- a/drivers/spi/spi-pci1xxxx.c +++ b/drivers/spi/spi-pci1xxxx.c @@ -140,6 +140,7 @@ struct pci1xxxx_spi_internal { int irq[NUM_VEC_PER_INST]; int mode; bool spi_xfer_in_progress; + atomic_t dma_completion_count; void *rx_buf; bool dma_aborted_rd; u32 bytes_recvd; @@ -163,8 +164,10 @@ struct pci1xxxx_spi { u8 dev_rev; void __iomem *reg_base; void __iomem *dma_offset_bar; - /* lock to safely access the DMA registers in isr */ - spinlock_t dma_reg_lock; + /* lock to safely access the DMA RD registers in isr */ + spinlock_t dma_rd_reg_lock; + /* lock to safely access the DMA RD registers in isr */ + spinlock_t dma_wr_reg_lock; bool can_dma; struct pci1xxxx_spi_internal *spi_int[] __counted_by(total_hw_instances); }; @@ -330,7 +333,8 @@ static int pci1xxxx_spi_dma_init(struct pci1xxxx_spi *spi_bus, int hw_inst, int if (ret) return ret; - spin_lock_init(&spi_bus->dma_reg_lock); + spin_lock_init(&spi_bus->dma_rd_reg_lock); + spin_lock_init(&spi_bus->dma_wr_reg_lock); writel(SPI_DMA_ENGINE_EN, spi_bus->dma_offset_bar + SPI_DMA_GLOBAL_WR_ENGINE_EN); writel(SPI_DMA_ENGINE_EN, spi_bus->dma_offset_bar + SPI_DMA_GLOBAL_RD_ENGINE_EN); @@ -464,6 +468,7 @@ static void pci1xxxx_start_spi_xfer(struct pci1xxxx_spi_internal *p) { u32 regval; + atomic_set(&p->dma_completion_count, 0); regval = readl(p->parent->reg_base + SPI_MST_CTL_REG_OFFSET(p->hw_inst)); regval |= SPI_MST_CTL_GO; writel(regval, p->parent->reg_base + SPI_MST_CTL_REG_OFFSET(p->hw_inst)); @@ -536,7 +541,6 @@ static int pci1xxxx_spi_transfer_with_dma(struct spi_controller *spi_ctlr, { struct pci1xxxx_spi_internal *p = spi_controller_get_devdata(spi_ctlr); struct pci1xxxx_spi *par = p->parent; - dma_addr_t rx_dma_addr = 0; dma_addr_t tx_dma_addr = 0; int ret = 0; u32 regval; @@ -545,6 +549,7 @@ static int pci1xxxx_spi_transfer_with_dma(struct spi_controller *spi_ctlr, p->tx_sgl = xfer->tx_sg.sgl; p->rx_sgl = xfer->rx_sg.sgl; p->rx_buf = xfer->rx_buf; + atomic_set(&p->dma_completion_count, 1); regval = readl(par->reg_base + SPI_MST_EVENT_REG_OFFSET(p->hw_inst)); writel(regval, par->reg_base + SPI_MST_EVENT_REG_OFFSET(p->hw_inst)); @@ -561,13 +566,9 @@ static int pci1xxxx_spi_transfer_with_dma(struct spi_controller *spi_ctlr, writel(regval, par->reg_base + SPI_MST_EVENT_REG_OFFSET(p->hw_inst)); tx_dma_addr = sg_dma_address(p->tx_sgl); - rx_dma_addr = sg_dma_address(p->rx_sgl); p->tx_sgl_len = sg_dma_len(p->tx_sgl); - p->rx_sgl_len = sg_dma_len(p->rx_sgl); pci1xxxx_spi_setup(par, p->hw_inst, p->mode, p->clkdiv, p->tx_sgl_len); pci1xxxx_spi_setup_dma_to_io(p, (tx_dma_addr), p->tx_sgl_len); - if (rx_dma_addr) - pci1xxxx_spi_setup_dma_from_io(p, rx_dma_addr, p->rx_sgl_len); writel(p->hw_inst, par->dma_offset_bar + SPI_DMA_RD_DOORBELL_REG); reinit_completion(&p->spi_xfer_done); @@ -657,32 +658,33 @@ static irqreturn_t pci1xxxx_spi_isr_io(int irq, void *dev) return spi_int_fired; } -static void pci1xxxx_spi_setup_next_dma_transfer(struct pci1xxxx_spi_internal *p) +static void pci1xxxx_spi_setup_next_dma_to_io_transfer(struct pci1xxxx_spi_internal *p) { dma_addr_t tx_dma_addr = 0; - dma_addr_t rx_dma_addr = 0; u32 prev_len; p->tx_sgl = sg_next(p->tx_sgl); - if (p->rx_sgl) - p->rx_sgl = sg_next(p->rx_sgl); - if (!p->tx_sgl) { - /* Clear xfer_done */ - complete(&p->spi_xfer_done); - } else { + if (p->tx_sgl) { tx_dma_addr = sg_dma_address(p->tx_sgl); prev_len = p->tx_sgl_len; p->tx_sgl_len = sg_dma_len(p->tx_sgl); + pci1xxxx_spi_setup_dma_to_io(p, tx_dma_addr, p->tx_sgl_len); + writel(p->hw_inst, p->parent->dma_offset_bar + SPI_DMA_RD_DOORBELL_REG); if (prev_len != p->tx_sgl_len) pci1xxxx_spi_setup(p->parent, p->hw_inst, p->mode, p->clkdiv, p->tx_sgl_len); - pci1xxxx_spi_setup_dma_to_io(p, tx_dma_addr, p->tx_sgl_len); - if (p->rx_sgl) { - rx_dma_addr = sg_dma_address(p->rx_sgl); - p->rx_sgl_len = sg_dma_len(p->rx_sgl); - pci1xxxx_spi_setup_dma_from_io(p, rx_dma_addr, p->rx_sgl_len); - } - writel(p->hw_inst, p->parent->dma_offset_bar + SPI_DMA_RD_DOORBELL_REG); + } +} + +static void pci1xxxx_spi_setup_next_dma_from_io_transfer(struct pci1xxxx_spi_internal *p) +{ + dma_addr_t rx_dma_addr = 0; + + if (p->rx_sgl) { + rx_dma_addr = sg_dma_address(p->rx_sgl); + p->rx_sgl_len = sg_dma_len(p->rx_sgl); + pci1xxxx_spi_setup_dma_from_io(p, rx_dma_addr, p->rx_sgl_len); + writel(p->hw_inst, p->parent->dma_offset_bar + SPI_DMA_WR_DOORBELL_REG); } } @@ -693,22 +695,24 @@ static irqreturn_t pci1xxxx_spi_isr_dma_rd(int irq, void *dev) unsigned long flags; u32 regval; - spin_lock_irqsave(&p->parent->dma_reg_lock, flags); /* Clear the DMA RD INT and start spi xfer*/ regval = readl(p->parent->dma_offset_bar + SPI_DMA_INTR_RD_STS); if (regval) { if (regval & SPI_DMA_DONE_INT_MASK(p->hw_inst)) { - pci1xxxx_start_spi_xfer(p); + /* Start the SPI transfer only if both DMA read and write are completed */ + if (atomic_inc_return(&p->dma_completion_count) == 2) + pci1xxxx_start_spi_xfer(p); spi_int_fired = IRQ_HANDLED; } if (regval & SPI_DMA_ABORT_INT_MASK(p->hw_inst)) { p->dma_aborted_rd = true; spi_int_fired = IRQ_HANDLED; } + spin_lock_irqsave(&p->parent->dma_rd_reg_lock, flags); + writel((SPI_DMA_DONE_INT_MASK(p->hw_inst) | SPI_DMA_ABORT_INT_MASK(p->hw_inst)), + p->parent->dma_offset_bar + SPI_DMA_INTR_RD_CLR); + spin_unlock_irqrestore(&p->parent->dma_rd_reg_lock, flags); } - writel((SPI_DMA_DONE_INT_MASK(p->hw_inst) | SPI_DMA_ABORT_INT_MASK(p->hw_inst)), - p->parent->dma_offset_bar + SPI_DMA_INTR_RD_CLR); - spin_unlock_irqrestore(&p->parent->dma_reg_lock, flags); return spi_int_fired; } @@ -719,22 +723,29 @@ static irqreturn_t pci1xxxx_spi_isr_dma_wr(int irq, void *dev) unsigned long flags; u32 regval; - spin_lock_irqsave(&p->parent->dma_reg_lock, flags); /* Clear the DMA WR INT */ regval = readl(p->parent->dma_offset_bar + SPI_DMA_INTR_WR_STS); if (regval) { if (regval & SPI_DMA_DONE_INT_MASK(p->hw_inst)) { - pci1xxxx_spi_setup_next_dma_transfer(p); spi_int_fired = IRQ_HANDLED; + if (sg_is_last(p->rx_sgl)) { + complete(&p->spi_xfer_done); + } else { + p->rx_sgl = sg_next(p->rx_sgl); + if (atomic_inc_return(&p->dma_completion_count) == 2) + pci1xxxx_start_spi_xfer(p); + } + } if (regval & SPI_DMA_ABORT_INT_MASK(p->hw_inst)) { p->dma_aborted_wr = true; spi_int_fired = IRQ_HANDLED; } + spin_lock_irqsave(&p->parent->dma_wr_reg_lock, flags); + writel((SPI_DMA_DONE_INT_MASK(p->hw_inst) | SPI_DMA_ABORT_INT_MASK(p->hw_inst)), + p->parent->dma_offset_bar + SPI_DMA_INTR_WR_CLR); + spin_unlock_irqrestore(&p->parent->dma_wr_reg_lock, flags); } - writel((SPI_DMA_DONE_INT_MASK(p->hw_inst) | SPI_DMA_ABORT_INT_MASK(p->hw_inst)), - p->parent->dma_offset_bar + SPI_DMA_INTR_WR_CLR); - spin_unlock_irqrestore(&p->parent->dma_reg_lock, flags); return spi_int_fired; } @@ -747,10 +758,11 @@ static irqreturn_t pci1xxxx_spi_isr_dma(int irq, void *dev) /* Clear the SPI GO_BIT Interrupt */ regval = readl(p->parent->reg_base + SPI_MST_EVENT_REG_OFFSET(p->hw_inst)); if (regval & SPI_INTR) { - writel(p->hw_inst, p->parent->dma_offset_bar + SPI_DMA_WR_DOORBELL_REG); + pci1xxxx_spi_setup_next_dma_from_io_transfer(p); + pci1xxxx_spi_setup_next_dma_to_io_transfer(p); spi_int_fired = IRQ_HANDLED; + writel(regval, p->parent->reg_base + SPI_MST_EVENT_REG_OFFSET(p->hw_inst)); } - writel(regval, p->parent->reg_base + SPI_MST_EVENT_REG_OFFSET(p->hw_inst)); return spi_int_fired; } From f4d8438e6a402ad40cf4ccb6e2d2417d9ed47821 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Le=20Goffic?= Date: Mon, 30 Jun 2025 14:59:23 +0200 Subject: [PATCH 33/62] spi: stm32: fix sram pool free in probe error path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a test to check whether the sram_pool is NULL before freeing it. Fixes: d17dd2f1d8a1 ("spi: stm32: use STM32 DMA with STM32 MDMA to enhance DDR use") Reported-by: Dan Carpenter Acked-by: Alain Volmat Signed-off-by: Clément Le Goffic Link: https://patch.msgid.link/20250630-spi-fix-v2-1-4680939e2a3e@foss.st.com Signed-off-by: Mark Brown --- drivers/spi/spi-stm32.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-stm32.c b/drivers/spi/spi-stm32.c index 3d20f09f1ae7..858470a2cab5 100644 --- a/drivers/spi/spi-stm32.c +++ b/drivers/spi/spi-stm32.c @@ -2486,7 +2486,9 @@ static int stm32_spi_probe(struct platform_device *pdev) if (spi->mdma_rx) dma_release_channel(spi->mdma_rx); err_pool_free: - gen_pool_free(spi->sram_pool, (unsigned long)spi->sram_rx_buf, spi->sram_rx_buf_size); + if (spi->sram_pool) + gen_pool_free(spi->sram_pool, (unsigned long)spi->sram_rx_buf, + spi->sram_rx_buf_size); err_dma_release: if (spi->dma_tx) dma_release_channel(spi->dma_tx); From c4f2c05ab02952c9a56067aeb700ded95b183570 Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Mon, 30 Jun 2025 10:12:53 +0200 Subject: [PATCH 34/62] spi: stm32: fix pointer-to-pointer variables usage In stm32_spi_prepare_rx_dma_mdma_chaining() both rx_dma_desc and rx_mdma_desc are passed as pointer-to-pointer arguments. The goal is to pass back to the caller the value returned by dmaengine_prep_slave_sg(), when it is not NULL. However, these variables are wrongly handled as simple pointers during later assignments and checks. Fix this behaviour by introducing two pointer variables which can then be treated accordingly. Fixes: d17dd2f1d8a1 ("spi: stm32: use STM32 DMA with STM32 MDMA to enhance DDR use") Addresses-Coverity-ID: 1644715 ("Null pointer dereferences (REVERSE_INULL)") Signed-off-by: Antonio Quartulli Reviewed-by: Clement LE GOFFIC Link: https://patch.msgid.link/20250630081253.17294-1-antonio@mandelbit.com Signed-off-by: Mark Brown --- drivers/spi/spi-stm32.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/drivers/spi/spi-stm32.c b/drivers/spi/spi-stm32.c index 858470a2cab5..9b3bc0c908bf 100644 --- a/drivers/spi/spi-stm32.c +++ b/drivers/spi/spi-stm32.c @@ -1474,6 +1474,8 @@ static int stm32_spi_prepare_rx_dma_mdma_chaining(struct stm32_spi *spi, struct dma_async_tx_descriptor **rx_dma_desc, struct dma_async_tx_descriptor **rx_mdma_desc) { + struct dma_async_tx_descriptor *_mdma_desc = *rx_mdma_desc; + struct dma_async_tx_descriptor *_dma_desc = *rx_dma_desc; struct dma_slave_config rx_mdma_conf = {0}; u32 sram_period, nents = 0, spi_s_len; struct sg_table dma_sgt, mdma_sgt; @@ -1524,18 +1526,18 @@ static int stm32_spi_prepare_rx_dma_mdma_chaining(struct stm32_spi *spi, } } - *rx_dma_desc = dmaengine_prep_slave_sg(spi->dma_rx, dma_sgt.sgl, - dma_sgt.nents, rx_dma_conf->direction, - DMA_PREP_INTERRUPT); + _dma_desc = dmaengine_prep_slave_sg(spi->dma_rx, dma_sgt.sgl, + dma_sgt.nents, rx_dma_conf->direction, + DMA_PREP_INTERRUPT); sg_free_table(&dma_sgt); - if (!rx_dma_desc) + if (!_dma_desc) return -EINVAL; /* Prepare MDMA slave_sg transfer MEM_TO_MEM (SRAM>DDR) */ ret = sg_alloc_table(&mdma_sgt, nents, GFP_ATOMIC); if (ret) { - rx_dma_desc = NULL; + _dma_desc = NULL; return ret; } @@ -1558,13 +1560,13 @@ static int stm32_spi_prepare_rx_dma_mdma_chaining(struct stm32_spi *spi, } } - *rx_mdma_desc = dmaengine_prep_slave_sg(spi->mdma_rx, mdma_sgt.sgl, - mdma_sgt.nents, rx_mdma_conf.direction, - DMA_PREP_INTERRUPT); + _mdma_desc = dmaengine_prep_slave_sg(spi->mdma_rx, mdma_sgt.sgl, + mdma_sgt.nents, rx_mdma_conf.direction, + DMA_PREP_INTERRUPT); sg_free_table(&mdma_sgt); - if (!rx_mdma_desc) { - rx_dma_desc = NULL; + if (!_mdma_desc) { + _dma_desc = NULL; return -EINVAL; } From 244bc18e5f1875401a4af87d2eae3f9376d9d720 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 30 Jun 2025 14:35:25 -0500 Subject: [PATCH 35/62] spi: stm32: delete stray tabs in stm32h7_spi_data_idleness() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These lines were indented one tab more than they should be. Delete the stray tabs. Signed-off-by: Dan Carpenter Acked-by: Alain Volmat Reviewed-by: Clément Le Goffic Link: https://patch.msgid.link/2033b9fa-7b0f-4617-b94e-7b0a51c5c4b1@sabinyo.mountain Signed-off-by: Mark Brown --- drivers/spi/spi-stm32.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-stm32.c b/drivers/spi/spi-stm32.c index 9b3bc0c908bf..a29c67024d00 100644 --- a/drivers/spi/spi-stm32.c +++ b/drivers/spi/spi-stm32.c @@ -1897,8 +1897,8 @@ static void stm32h7_spi_data_idleness(struct stm32_spi *spi, struct spi_transfer if (spi_delay_ns) { dev_warn(spi->dev, "Overriding st,spi-midi-ns with word_delay_ns %d\n", spi_delay_ns); - spi->cur_midi = spi_delay_ns; - } + spi->cur_midi = spi_delay_ns; + } } else { spi->cur_midi = spi_delay_ns; } From e47a324d6f07c9ef252cfce1f14cfa5110cbed99 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Fri, 27 Jun 2025 18:40:04 -0500 Subject: [PATCH 36/62] dt-bindings: trigger-source: add ADI Util Sigma-Delta SPI Add new binding for the ADI Util Sigma-Delta SPI FPGA IP Core. This is used to trigger a SPI offload based on a RDY signal from the ADC while masking out other signals on the same line. Reviewed-by: Rob Herring (Arm) Signed-off-by: David Lechner Link: https://patch.msgid.link/20250627-iio-adc-ad7173-add-spi-offload-support-v2-8-f49c55599113@baylibre.com Signed-off-by: Mark Brown --- .../adi,util-sigma-delta-spi.yaml | 49 +++++++++++++++++++ MAINTAINERS | 5 ++ 2 files changed, 54 insertions(+) create mode 100644 Documentation/devicetree/bindings/trigger-source/adi,util-sigma-delta-spi.yaml diff --git a/Documentation/devicetree/bindings/trigger-source/adi,util-sigma-delta-spi.yaml b/Documentation/devicetree/bindings/trigger-source/adi,util-sigma-delta-spi.yaml new file mode 100644 index 000000000000..ea466179551c --- /dev/null +++ b/Documentation/devicetree/bindings/trigger-source/adi,util-sigma-delta-spi.yaml @@ -0,0 +1,49 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright (c) 2025 Analog Devices, Inc. +# Copyright (c) 2025 BayLibre, SAS + +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/trigger-source/adi,util-sigma-delta-spi.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices Util Sigma-Delta SPI IP Core + +maintainers: + - David Lechner + +description: + The Util Sigma-Delta SPI is an FPGA IP core from Analog Devices that provides + a SPI offload trigger from the RDY signal of the combined DOUT/RDY pin of + the sigma-delta family of ADCs. + https://analogdevicesinc.github.io/hdl/library/util_sigma_delta_spi/index.html + +properties: + compatible: + const: adi,util-sigma-delta-spi + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + '#trigger-source-cells': + const: 0 + +required: + - compatible + - reg + - clocks + - '#trigger-source-cells' + +additionalProperties: false + +examples: + - | + trigger@40000 { + reg = <0x40000 0x1000>; + compatible = "adi,util-sigma-delta-spi"; + clocks = <&clk 0>; + #trigger-source-cells = <0>; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 4bac4ea21b64..d7b5e8572279 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -25235,6 +25235,11 @@ W: https://github.com/srcres258/linux-doc T: git git://github.com/srcres258/linux-doc.git doc-zh-tw F: Documentation/translations/zh_TW/ +TRIGGER SOURCE - ADI UTIL SIGMA DELTA SPI +M: David Lechner +S: Maintained +F: Documentation/devicetree/bindings/trigger-source/adi,util-sigma-delta-spi.yaml + TRIGGER SOURCE - PWM M: David Lechner S: Maintained From 3fcd3d2fe44dc9dfca20b6aed117f314a50ba0ff Mon Sep 17 00:00:00 2001 From: David Lechner Date: Fri, 27 Jun 2025 18:40:05 -0500 Subject: [PATCH 37/62] spi: offload trigger: add ADI Util Sigma-Delta SPI driver Add a new driver for the ADI Util Sigma-Delta SPI FPGA IP core. This is used to trigger a SPI offload based on a RDY signal from an ADC while masking out other signals on the same line. Signed-off-by: David Lechner Link: https://patch.msgid.link/20250627-iio-adc-ad7173-add-spi-offload-support-v2-9-f49c55599113@baylibre.com Signed-off-by: Mark Brown --- MAINTAINERS | 2 +- drivers/spi/Kconfig | 5 ++ drivers/spi/Makefile | 1 + ...spi-offload-trigger-adi-util-sigma-delta.c | 59 +++++++++++++++++++ 4 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 drivers/spi/spi-offload-trigger-adi-util-sigma-delta.c diff --git a/MAINTAINERS b/MAINTAINERS index d7b5e8572279..d0d5b822a1f7 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -23413,7 +23413,7 @@ F: include/linux/mtd/spi-nor.h SPI OFFLOAD R: David Lechner -F: drivers/spi/spi-offload-trigger-pwm.c +F: drivers/spi/spi-offload-trigger-*.c F: drivers/spi/spi-offload.c F: include/linux/spi/offload/ K: spi_offload diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index c51da3fc3604..e69f060d3875 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -1355,6 +1355,11 @@ if SPI_OFFLOAD comment "SPI Offload triggers" +config SPI_OFFLOAD_TRIGGER_ADI_UTIL_SD + tristate "SPI offload trigger using ADI sigma-delta utility" + help + SPI offload trigger from ADI sigma-delta utility FPGA IP block. + config SPI_OFFLOAD_TRIGGER_PWM tristate "SPI offload trigger using PWM" depends on PWM diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 4ea89f6fc531..51f9f16ed734 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -170,3 +170,4 @@ obj-$(CONFIG_SPI_SLAVE_SYSTEM_CONTROL) += spi-slave-system-control.o # SPI offload triggers obj-$(CONFIG_SPI_OFFLOAD_TRIGGER_PWM) += spi-offload-trigger-pwm.o +obj-$(CONFIG_SPI_OFFLOAD_TRIGGER_ADI_UTIL_SD) += spi-offload-trigger-adi-util-sigma-delta.o diff --git a/drivers/spi/spi-offload-trigger-adi-util-sigma-delta.c b/drivers/spi/spi-offload-trigger-adi-util-sigma-delta.c new file mode 100644 index 000000000000..035d088d4d33 --- /dev/null +++ b/drivers/spi/spi-offload-trigger-adi-util-sigma-delta.c @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2025 Analog Devices Inc. + * Copyright (C) 2025 BayLibre, SAS + */ + +#include +#include +#include +#include +#include +#include +#include + +static bool adi_util_sigma_delta_match(struct spi_offload_trigger *trigger, + enum spi_offload_trigger_type type, + u64 *args, u32 nargs) +{ + return type == SPI_OFFLOAD_TRIGGER_DATA_READY && nargs == 0; +} + +static const struct spi_offload_trigger_ops adi_util_sigma_delta_ops = { + .match = adi_util_sigma_delta_match, +}; + +static int adi_util_sigma_delta_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct spi_offload_trigger_info info = { + .fwnode = dev_fwnode(dev), + .ops = &adi_util_sigma_delta_ops, + }; + struct clk *clk; + + clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(clk)) + return dev_err_probe(dev, PTR_ERR(clk), "Failed to get clock\n"); + + return devm_spi_offload_trigger_register(dev, &info); +} + +static const struct of_device_id adi_util_sigma_delta_of_match_table[] = { + { .compatible = "adi,util-sigma-delta-spi", }, + { } +}; +MODULE_DEVICE_TABLE(of, adi_util_sigma_delta_of_match_table); + +static struct platform_driver adi_util_sigma_delta_driver = { + .probe = adi_util_sigma_delta_probe, + .driver = { + .name = "adi-util-sigma-delta-spi", + .of_match_table = adi_util_sigma_delta_of_match_table, + }, +}; +module_platform_driver(adi_util_sigma_delta_driver); + +MODULE_AUTHOR("David Lechner "); +MODULE_DESCRIPTION("ADI Sigma-Delta SPI offload trigger utility driver"); +MODULE_LICENSE("GPL"); From 0dc7e656ddd54c3267b7cc18c1ac8ec1297ed02f Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Wed, 2 Jul 2025 14:35:23 +0200 Subject: [PATCH 38/62] mtd: nand: qpic-common: add defines for ECC_MODE values Add defines for the values of the ECC_MODE field of the NAND_DEV0_ECC_CFG register and change both the 'qcom-nandc' and 'spi-qpic-snand' drivers to use those instead of magic numbers. No functional changes. This is in preparation for adding 8 bit ECC strength support for the 'spi-qpic-snand' driver. Reviewed-by: Md Sadre Alam Signed-off-by: Gabor Juhos Acked-by: Miquel Raynal Link: https://patch.msgid.link/20250702-qpic-snand-8bit-ecc-v2-1-ae2c17a30bb7@gmail.com Signed-off-by: Mark Brown --- drivers/mtd/nand/raw/qcom_nandc.c | 6 +++--- drivers/spi/spi-qpic-snand.c | 2 +- include/linux/mtd/nand-qpic-common.h | 2 ++ 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index 1003cf118c01..4dd6f1a4e797 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -1379,7 +1379,7 @@ static int qcom_nand_attach_chip(struct nand_chip *chip) struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); int cwperpage, bad_block_byte, ret; bool wide_bus; - int ecc_mode = 1; + int ecc_mode = ECC_MODE_8BIT; /* controller only supports 512 bytes data steps */ ecc->size = NANDC_STEP_SIZE; @@ -1400,7 +1400,7 @@ static int qcom_nand_attach_chip(struct nand_chip *chip) if (ecc->strength >= 8) { /* 8 bit ECC defaults to BCH ECC on all platforms */ host->bch_enabled = true; - ecc_mode = 1; + ecc_mode = ECC_MODE_8BIT; if (wide_bus) { host->ecc_bytes_hw = 14; @@ -1420,7 +1420,7 @@ static int qcom_nand_attach_chip(struct nand_chip *chip) if (nandc->props->ecc_modes & ECC_BCH_4BIT) { /* BCH */ host->bch_enabled = true; - ecc_mode = 0; + ecc_mode = ECC_MODE_4BIT; if (wide_bus) { host->ecc_bytes_hw = 8; diff --git a/drivers/spi/spi-qpic-snand.c b/drivers/spi/spi-qpic-snand.c index ca55f9bcd17b..7219bcaf4055 100644 --- a/drivers/spi/spi-qpic-snand.c +++ b/drivers/spi/spi-qpic-snand.c @@ -343,7 +343,7 @@ static int qcom_spi_ecc_init_ctx_pipelined(struct nand_device *nand) FIELD_PREP(ECC_SW_RESET, 0) | FIELD_PREP(ECC_NUM_DATA_BYTES_MASK, ecc_cfg->cw_data) | FIELD_PREP(ECC_FORCE_CLK_OPEN, 1) | - FIELD_PREP(ECC_MODE_MASK, 0) | + FIELD_PREP(ECC_MODE_MASK, ECC_MODE_4BIT) | FIELD_PREP(ECC_PARITY_SIZE_BYTES_BCH_MASK, ecc_cfg->ecc_bytes_hw); ecc_cfg->ecc_buf_cfg = FIELD_PREP(NUM_STEPS_MASK, 0x203); diff --git a/include/linux/mtd/nand-qpic-common.h b/include/linux/mtd/nand-qpic-common.h index e8462deda6db..0d944db363cd 100644 --- a/include/linux/mtd/nand-qpic-common.h +++ b/include/linux/mtd/nand-qpic-common.h @@ -101,6 +101,8 @@ #define ECC_SW_RESET BIT(1) #define ECC_MODE 4 #define ECC_MODE_MASK GENMASK(5, 4) +#define ECC_MODE_4BIT 0 +#define ECC_MODE_8BIT 1 #define ECC_PARITY_SIZE_BYTES_BCH 8 #define ECC_PARITY_SIZE_BYTES_BCH_MASK GENMASK(12, 8) #define ECC_NUM_DATA_BYTES 16 From 913bf8d50cbd144c87e9660b591781179182ff59 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Wed, 2 Jul 2025 14:35:24 +0200 Subject: [PATCH 39/62] spi: spi-qpic-snand: add support for 8 bits ECC strength Even though the hardware supports 8 bits ECC strength, but that is not handled in the driver yet. This change adds the missing bits in order to allow using the driver with chips which require 8 bits ECC strength. No functional changes intended with regard to the existing 4 bits ECC strength support. Tested on an IPQ9574 platform using a GigaDevice GD5F2GM7REYIG chip. Signed-off-by: Gabor Juhos Link: https://patch.msgid.link/20250702-qpic-snand-8bit-ecc-v2-2-ae2c17a30bb7@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-qpic-snand.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/drivers/spi/spi-qpic-snand.c b/drivers/spi/spi-qpic-snand.c index 7219bcaf4055..b711c8be42c1 100644 --- a/drivers/spi/spi-qpic-snand.c +++ b/drivers/spi/spi-qpic-snand.c @@ -277,9 +277,22 @@ static int qcom_spi_ecc_init_ctx_pipelined(struct nand_device *nand) goto err_free_ecc_cfg; } - if (ecc_cfg->strength != 4) { + switch (ecc_cfg->strength) { + case 4: + ecc_cfg->ecc_mode = ECC_MODE_4BIT; + ecc_cfg->ecc_bytes_hw = 7; + ecc_cfg->spare_bytes = 4; + break; + + case 8: + ecc_cfg->ecc_mode = ECC_MODE_8BIT; + ecc_cfg->ecc_bytes_hw = 13; + ecc_cfg->spare_bytes = 2; + break; + + default: dev_err(snandc->dev, - "only 4 bits ECC strength is supported\n"); + "only 4 or 8 bits ECC strength is supported\n"); ret = -EOPNOTSUPP; goto err_free_ecc_cfg; } @@ -296,8 +309,6 @@ static int qcom_spi_ecc_init_ctx_pipelined(struct nand_device *nand) nand->ecc.ctx.priv = ecc_cfg; snandc->qspi->mtd = mtd; - ecc_cfg->ecc_bytes_hw = 7; - ecc_cfg->spare_bytes = 4; ecc_cfg->bbm_size = 1; ecc_cfg->bch_enabled = true; ecc_cfg->bytes = ecc_cfg->ecc_bytes_hw + ecc_cfg->spare_bytes + ecc_cfg->bbm_size; @@ -343,7 +354,7 @@ static int qcom_spi_ecc_init_ctx_pipelined(struct nand_device *nand) FIELD_PREP(ECC_SW_RESET, 0) | FIELD_PREP(ECC_NUM_DATA_BYTES_MASK, ecc_cfg->cw_data) | FIELD_PREP(ECC_FORCE_CLK_OPEN, 1) | - FIELD_PREP(ECC_MODE_MASK, ECC_MODE_4BIT) | + FIELD_PREP(ECC_MODE_MASK, ecc_cfg->ecc_mode) | FIELD_PREP(ECC_PARITY_SIZE_BYTES_BCH_MASK, ecc_cfg->ecc_bytes_hw); ecc_cfg->ecc_buf_cfg = FIELD_PREP(NUM_STEPS_MASK, 0x203); From 7105fdd54a14bee49371b39374a61b3c967d74cb Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Wed, 2 Jul 2025 17:26:42 -0500 Subject: [PATCH 40/62] spi: dt-bindings: Convert marvell,orion-spi to DT schema Convert the Marvell Orion SPI binding to schema. Update compatible strings to what is in use. Generally, "marvell,orion-spi" is a fallback compatible, but newer variants only use "marvell,armada-380-spi". Mark cell-index as deprecated and not required as some instances don't use it already. Signed-off-by: Rob Herring (Arm) Link: https://patch.msgid.link/20250702222643.2761617-1-robh@kernel.org Signed-off-by: Mark Brown --- .../bindings/spi/marvell,orion-spi.yaml | 102 ++++++++++++++++++ .../devicetree/bindings/spi/spi-orion.txt | 79 -------------- 2 files changed, 102 insertions(+), 79 deletions(-) create mode 100644 Documentation/devicetree/bindings/spi/marvell,orion-spi.yaml delete mode 100644 Documentation/devicetree/bindings/spi/spi-orion.txt diff --git a/Documentation/devicetree/bindings/spi/marvell,orion-spi.yaml b/Documentation/devicetree/bindings/spi/marvell,orion-spi.yaml new file mode 100644 index 000000000000..7f5ec1d7f59b --- /dev/null +++ b/Documentation/devicetree/bindings/spi/marvell,orion-spi.yaml @@ -0,0 +1,102 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/spi/marvell,orion-spi.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Marvell Orion SPI controller + +maintainers: + - Andrew Lunn + - Gregory CLEMENT + +allOf: + - $ref: /schemas/spi/spi-controller.yaml# + +properties: + compatible: + oneOf: + - enum: + - marvell,orion-spi + - marvell,armada-380-spi # For ap80x and cp11x + - items: + - enum: + - marvell,armada-370-spi + - marvell,armada-375-spi + - marvell,armada-380-spi + - marvell,armada-390-spi + - marvell,armada-xp-spi + - const: marvell,orion-spi + + cell-index: + description: Instance id for the SPI controller + deprecated: true + + reg: + minItems: 1 + items: + - description: control registers + - description: CS0 MBUS target/attribute registers for direct mode + - description: CS1 MBUS target/attribute registers for direct mode + - description: CS2 MBUS target/attribute registers for direct mode + - description: CS3 MBUS target/attribute registers for direct mode + - description: CS4 MBUS target/attribute registers for direct mode + - description: CS5 MBUS target/attribute registers for direct mode + - description: CS6 MBUS target/attribute registers for direct mode + - description: CS7 MBUS target/attribute registers for direct mode + + clocks: + minItems: 1 + maxItems: 2 + + clock-names: + items: + - const: core + - const: axi + + interrupts: + maxItems: 1 + +required: + - compatible + - reg + - clocks + +unevaluatedProperties: false + +examples: + - | + spi@10600 { + compatible = "marvell,orion-spi"; + #address-cells = <1>; + #size-cells = <0>; + cell-index = <0>; + reg = <0x10600 0x28>; + clocks = <&coreclk 0>; + interrupts = <23>; + }; + - | + #define MBUS_ID(target,attributes) (((target) << 24) | ((attributes) << 16)) + + bus { + #address-cells = <2>; + #size-cells = <1>; + + spi@10600 { + compatible = "marvell,orion-spi"; + #address-cells = <1>; + #size-cells = <0>; + cell-index = <0>; + reg = , /* control */ + , /* CS0 */ + , /* CS1 */ + , /* CS2 */ + , /* CS3 */ + , /* CS4 */ + , /* CS5 */ + , /* CS6 */ + ; /* CS7 */ + clocks = <&coreclk 0>; + interrupts = <23>; + }; + }; diff --git a/Documentation/devicetree/bindings/spi/spi-orion.txt b/Documentation/devicetree/bindings/spi/spi-orion.txt deleted file mode 100644 index 8434a65fc12a..000000000000 --- a/Documentation/devicetree/bindings/spi/spi-orion.txt +++ /dev/null @@ -1,79 +0,0 @@ -Marvell Orion SPI device - -Required properties: -- compatible : should be on of the following: - - "marvell,orion-spi" for the Orion, mv78x00, Kirkwood and Dove SoCs - - "marvell,armada-370-spi", for the Armada 370 SoCs - - "marvell,armada-375-spi", for the Armada 375 SoCs - - "marvell,armada-380-spi", for the Armada 38x SoCs - - "marvell,armada-390-spi", for the Armada 39x SoCs - - "marvell,armada-xp-spi", for the Armada XP SoCs -- reg : offset and length of the register set for the device. - This property can optionally have additional entries to configure - the SPI direct access mode that some of the Marvell SoCs support - additionally to the normal indirect access (PIO) mode. The values - for the MBus "target" and "attribute" are defined in the Marvell - SoC "Functional Specifications" Manual in the chapter "Marvell - Core Processor Address Decoding". - The eight register sets following the control registers refer to - chip-select lines 0 through 7 respectively. -- cell-index : Which of multiple SPI controllers is this. -- clocks : pointers to the reference clocks for this device, the first - one is the one used for the clock on the spi bus, the - second one is optional and is the clock used for the - functional part of the controller - -Optional properties: -- interrupts : Is currently not used. -- clock-names : names of used clocks, mandatory if the second clock is - used, the name must be "core", and "axi" (the latter - is only for Armada 7K/8K). - - -Example: - spi@10600 { - compatible = "marvell,orion-spi"; - #address-cells = <1>; - #size-cells = <0>; - cell-index = <0>; - reg = <0x10600 0x28>; - interrupts = <23>; - }; - -Example with SPI direct mode support (optionally): - spi0: spi@10600 { - compatible = "marvell,orion-spi"; - #address-cells = <1>; - #size-cells = <0>; - cell-index = <0>; - reg = , /* control */ - , /* CS0 */ - , /* CS1 */ - , /* CS2 */ - , /* CS3 */ - , /* CS4 */ - , /* CS5 */ - , /* CS6 */ - ; /* CS7 */ - interrupts = <23>; - }; - -To enable the direct mode, the board specific 'ranges' property in the -'soc' node needs to add the entries for the desired SPI controllers -and its chip-selects that are used in the direct mode instead of PIO -mode. Here an example for this (SPI controller 0, device 1 and SPI -controller 1, device 2 are used in direct mode. All other SPI device -are used in the default indirect (PIO) mode): - soc { - /* - * Enable the SPI direct access by configuring an entry - * here in the board-specific ranges property - */ - ranges = , /* internal regs */ - , /* BootROM */ - , /* SPI0-DEV1 */ - ; /* SPI1-DEV2 */ - -For further information on the MBus bindings, please see the MBus -DT documentation: -Documentation/devicetree/bindings/bus/mvebu-mbus.txt From 2fca750160f29015ab1109bb478537a4e415f7cd Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 4 Jul 2025 10:54:47 +0300 Subject: [PATCH 41/62] spi: Remove redundant pm_runtime_mark_last_busy() calls pm_runtime_put_autosuspend(), pm_runtime_put_sync_autosuspend(), pm_runtime_autosuspend() and pm_request_autosuspend() now include a call to pm_runtime_mark_last_busy(). Remove the now-reduntant explicit call to pm_runtime_mark_last_busy(). Signed-off-by: Sakari Ailus Link: https://patch.msgid.link/20250704075447.3221784-1-sakari.ailus@linux.intel.com Signed-off-by: Mark Brown --- drivers/spi/atmel-quadspi.c | 5 ----- drivers/spi/spi-cadence-quadspi.c | 2 -- drivers/spi/spi-cadence.c | 1 - drivers/spi/spi-fsl-espi.c | 2 -- drivers/spi/spi-fsl-lpspi.c | 2 -- drivers/spi/spi-imx.c | 3 --- drivers/spi/spi-mtk-nor.c | 1 - drivers/spi/spi-nxp-fspi.c | 1 - drivers/spi/spi-omap2-mcspi.c | 3 --- drivers/spi/spi-rockchip-sfc.c | 3 --- drivers/spi/spi-s3c64xx.c | 3 --- drivers/spi/spi-sprd.c | 1 - drivers/spi/spi-stm32-ospi.c | 7 ------- drivers/spi/spi-stm32-qspi.c | 7 ------- drivers/spi/spi-stm32.c | 2 -- drivers/spi/spi-ti-qspi.c | 2 -- drivers/spi/spi-zynqmp-gqspi.c | 1 - drivers/spi/spi.c | 3 --- 18 files changed, 49 deletions(-) diff --git a/drivers/spi/atmel-quadspi.c b/drivers/spi/atmel-quadspi.c index 2f6797324227..d730d7c6b527 100644 --- a/drivers/spi/atmel-quadspi.c +++ b/drivers/spi/atmel-quadspi.c @@ -965,7 +965,6 @@ static int atmel_qspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) err = aq->ops->transfer(mem, op, offset); pm_runtime_put: - pm_runtime_mark_last_busy(&aq->pdev->dev); pm_runtime_put_autosuspend(&aq->pdev->dev); return err; } @@ -1168,7 +1167,6 @@ static int atmel_qspi_setup(struct spi_device *spi) aq->scr |= QSPI_SCR_SCBR(scbr); atmel_qspi_write(aq->scr, aq, QSPI_SCR); - pm_runtime_mark_last_busy(ctrl->dev.parent); pm_runtime_put_autosuspend(ctrl->dev.parent); return 0; @@ -1230,7 +1228,6 @@ static int atmel_qspi_set_cs_timing(struct spi_device *spi) aq->mr |= QSPI_MR_DLYBCT(cs_hold) | QSPI_MR_DLYCS(cs_inactive); atmel_qspi_write(aq->mr, aq, QSPI_MR); - pm_runtime_mark_last_busy(ctrl->dev.parent); pm_runtime_put_autosuspend(ctrl->dev.parent); return 0; @@ -1448,7 +1445,6 @@ static int atmel_qspi_probe(struct platform_device *pdev) if (err) goto dma_release; - pm_runtime_mark_last_busy(&pdev->dev); pm_runtime_put_autosuspend(&pdev->dev); return 0; @@ -1582,7 +1578,6 @@ static int __maybe_unused atmel_qspi_resume(struct device *dev) atmel_qspi_write(aq->scr, aq, QSPI_SCR); - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); return 0; diff --git a/drivers/spi/spi-cadence-quadspi.c b/drivers/spi/spi-cadence-quadspi.c index fe0f122f07b0..420e4fc55b9a 100644 --- a/drivers/spi/spi-cadence-quadspi.c +++ b/drivers/spi/spi-cadence-quadspi.c @@ -1469,7 +1469,6 @@ static int cqspi_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op) ret = cqspi_mem_process(mem, op); - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); if (ret) @@ -1975,7 +1974,6 @@ static int cqspi_probe(struct platform_device *pdev) goto probe_setup_failed; } - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); return 0; diff --git a/drivers/spi/spi-cadence.c b/drivers/spi/spi-cadence.c index 9e56bde87768..5ae09b21d23a 100644 --- a/drivers/spi/spi-cadence.c +++ b/drivers/spi/spi-cadence.c @@ -662,7 +662,6 @@ static int cdns_spi_probe(struct platform_device *pdev) /* Set to default valid value */ ctlr->max_speed_hz = xspi->clk_rate / 4; xspi->speed_hz = ctlr->max_speed_hz; - pm_runtime_mark_last_busy(&pdev->dev); pm_runtime_put_autosuspend(&pdev->dev); } else { ctlr->mode_bits |= SPI_NO_CS; diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index 6a73eaa34cf7..f2f1d3298e6c 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -513,7 +513,6 @@ static int fsl_espi_setup(struct spi_device *spi) fsl_espi_setup_transfer(spi, NULL); - pm_runtime_mark_last_busy(espi->dev); pm_runtime_put_autosuspend(espi->dev); return 0; @@ -726,7 +725,6 @@ static int fsl_espi_probe(struct device *dev, struct resource *mem, dev_info(dev, "irq = %u\n", irq); - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); return 0; diff --git a/drivers/spi/spi-fsl-lpspi.c b/drivers/spi/spi-fsl-lpspi.c index 5e3818445234..67d4000c3cef 100644 --- a/drivers/spi/spi-fsl-lpspi.c +++ b/drivers/spi/spi-fsl-lpspi.c @@ -233,7 +233,6 @@ static int lpspi_unprepare_xfer_hardware(struct spi_controller *controller) struct fsl_lpspi_data *fsl_lpspi = spi_controller_get_devdata(controller); - pm_runtime_mark_last_busy(fsl_lpspi->dev); pm_runtime_put_autosuspend(fsl_lpspi->dev); return 0; @@ -966,7 +965,6 @@ static int fsl_lpspi_probe(struct platform_device *pdev) goto free_dma; } - pm_runtime_mark_last_busy(fsl_lpspi->dev); pm_runtime_put_autosuspend(fsl_lpspi->dev); return 0; diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index c93d80a4d734..155ddeb8fcd4 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -1748,7 +1748,6 @@ spi_imx_prepare_message(struct spi_controller *controller, struct spi_message *m ret = spi_imx->devtype_data->prepare_message(spi_imx, msg); if (ret) { - pm_runtime_mark_last_busy(spi_imx->dev); pm_runtime_put_autosuspend(spi_imx->dev); } @@ -1760,7 +1759,6 @@ spi_imx_unprepare_message(struct spi_controller *controller, struct spi_message { struct spi_imx_data *spi_imx = spi_controller_get_devdata(controller); - pm_runtime_mark_last_busy(spi_imx->dev); pm_runtime_put_autosuspend(spi_imx->dev); return 0; } @@ -1933,7 +1931,6 @@ static int spi_imx_probe(struct platform_device *pdev) goto out_register_controller; } - pm_runtime_mark_last_busy(spi_imx->dev); pm_runtime_put_autosuspend(spi_imx->dev); return ret; diff --git a/drivers/spi/spi-mtk-nor.c b/drivers/spi/spi-mtk-nor.c index 85ab5ce96c4d..5cc4632e13d7 100644 --- a/drivers/spi/spi-mtk-nor.c +++ b/drivers/spi/spi-mtk-nor.c @@ -918,7 +918,6 @@ static int mtk_nor_probe(struct platform_device *pdev) if (ret < 0) goto err_probe; - pm_runtime_mark_last_busy(&pdev->dev); pm_runtime_put_autosuspend(&pdev->dev); dev_info(&pdev->dev, "spi frequency: %d Hz\n", sp->spi_freq); diff --git a/drivers/spi/spi-nxp-fspi.c b/drivers/spi/spi-nxp-fspi.c index e63c77e41823..c7d4827f1bf1 100644 --- a/drivers/spi/spi-nxp-fspi.c +++ b/drivers/spi/spi-nxp-fspi.c @@ -968,7 +968,6 @@ static int nxp_fspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) /* Invalidate the data in the AHB buffer. */ nxp_fspi_invalid(f); - pm_runtime_mark_last_busy(f->dev); pm_runtime_put_autosuspend(f->dev); return err; diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 70bb74b3bd9c..6dc58a30804a 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -272,7 +272,6 @@ static void omap2_mcspi_set_cs(struct spi_device *spi, bool enable) mcspi_write_chconf0(spi, l); - pm_runtime_mark_last_busy(mcspi->dev); pm_runtime_put_autosuspend(mcspi->dev); } } @@ -1102,7 +1101,6 @@ static int omap2_mcspi_setup(struct spi_device *spi) if (ret && initial_setup) omap2_mcspi_cleanup(spi); - pm_runtime_mark_last_busy(mcspi->dev); pm_runtime_put_autosuspend(mcspi->dev); return ret; @@ -1379,7 +1377,6 @@ static int omap2_mcspi_controller_setup(struct omap2_mcspi *mcspi) ctx->wakeupenable = OMAP2_MCSPI_WAKEUPENABLE_WKEN; omap2_mcspi_set_mode(ctlr); - pm_runtime_mark_last_busy(mcspi->dev); pm_runtime_put_autosuspend(mcspi->dev); return 0; } diff --git a/drivers/spi/spi-rockchip-sfc.c b/drivers/spi/spi-rockchip-sfc.c index f3fe10eddb6a..9eba5c0a60f2 100644 --- a/drivers/spi/spi-rockchip-sfc.c +++ b/drivers/spi/spi-rockchip-sfc.c @@ -565,7 +565,6 @@ static int rockchip_sfc_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op ret = rockchip_sfc_xfer_done(sfc, 100000); out: - pm_runtime_mark_last_busy(sfc->dev); pm_runtime_put_autosuspend(sfc->dev); return ret; @@ -712,7 +711,6 @@ static int rockchip_sfc_probe(struct platform_device *pdev) if (ret) goto err_register; - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); return 0; @@ -799,7 +797,6 @@ static int rockchip_sfc_resume(struct device *dev) rockchip_sfc_init(sfc); - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); return 0; diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 9c47f5741c5f..b1567243ae19 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -1045,14 +1045,12 @@ static int s3c64xx_spi_setup(struct spi_device *spi) } } - pm_runtime_mark_last_busy(&sdd->pdev->dev); pm_runtime_put_autosuspend(&sdd->pdev->dev); s3c64xx_spi_set_cs(spi, false); return 0; setup_exit: - pm_runtime_mark_last_busy(&sdd->pdev->dev); pm_runtime_put_autosuspend(&sdd->pdev->dev); /* setup() returns with device de-selected */ s3c64xx_spi_set_cs(spi, false); @@ -1384,7 +1382,6 @@ static int s3c64xx_spi_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "\tIOmem=[%pR]\tFIFO %dbytes\n", mem_res, sdd->fifo_depth); - pm_runtime_mark_last_busy(&pdev->dev); pm_runtime_put_autosuspend(&pdev->dev); return 0; diff --git a/drivers/spi/spi-sprd.c b/drivers/spi/spi-sprd.c index ae794058b381..ad75f5f0f2bf 100644 --- a/drivers/spi/spi-sprd.c +++ b/drivers/spi/spi-sprd.c @@ -982,7 +982,6 @@ static int sprd_spi_probe(struct platform_device *pdev) if (ret) goto err_rpm_put; - pm_runtime_mark_last_busy(&pdev->dev); pm_runtime_put_autosuspend(&pdev->dev); return 0; diff --git a/drivers/spi/spi-stm32-ospi.c b/drivers/spi/spi-stm32-ospi.c index 4ab7e86f4bd5..2829535e5cd4 100644 --- a/drivers/spi/spi-stm32-ospi.c +++ b/drivers/spi/spi-stm32-ospi.c @@ -547,7 +547,6 @@ static int stm32_ospi_poll_status(struct spi_mem *mem, ret = stm32_ospi_send(mem->spi, op); mutex_unlock(&ospi->lock); - pm_runtime_mark_last_busy(ospi->dev); pm_runtime_put_autosuspend(ospi->dev); return ret; @@ -571,7 +570,6 @@ static int stm32_ospi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) ret = stm32_ospi_send(mem->spi, op); mutex_unlock(&ospi->lock); - pm_runtime_mark_last_busy(ospi->dev); pm_runtime_put_autosuspend(ospi->dev); return ret; @@ -628,7 +626,6 @@ static ssize_t stm32_ospi_dirmap_read(struct spi_mem_dirmap_desc *desc, ret = stm32_ospi_send(desc->mem->spi, &op); mutex_unlock(&ospi->lock); - pm_runtime_mark_last_busy(ospi->dev); pm_runtime_put_autosuspend(ospi->dev); return ret ?: len; @@ -713,7 +710,6 @@ static int stm32_ospi_transfer_one_message(struct spi_controller *ctrl, msg->status = ret; spi_finalize_current_message(ctrl); - pm_runtime_mark_last_busy(ospi->dev); pm_runtime_put_autosuspend(ospi->dev); return ret; @@ -750,7 +746,6 @@ static int stm32_ospi_setup(struct spi_device *spi) mutex_unlock(&ospi->lock); - pm_runtime_mark_last_busy(ospi->dev); pm_runtime_put_autosuspend(ospi->dev); return 0; @@ -953,7 +948,6 @@ static int stm32_ospi_probe(struct platform_device *pdev) goto err_pm_resume; } - pm_runtime_mark_last_busy(ospi->dev); pm_runtime_put_autosuspend(ospi->dev); return 0; @@ -1032,7 +1026,6 @@ static int __maybe_unused stm32_ospi_resume(struct device *dev) writel_relaxed(ospi->cr_reg, regs_base + OSPI_CR); writel_relaxed(ospi->dcr_reg, regs_base + OSPI_DCR1); - pm_runtime_mark_last_busy(ospi->dev); pm_runtime_put_autosuspend(ospi->dev); return 0; diff --git a/drivers/spi/spi-stm32-qspi.c b/drivers/spi/spi-stm32-qspi.c index 9691197bbf5a..f2d19f1c5ab1 100644 --- a/drivers/spi/spi-stm32-qspi.c +++ b/drivers/spi/spi-stm32-qspi.c @@ -463,7 +463,6 @@ static int stm32_qspi_poll_status(struct spi_mem *mem, const struct spi_mem_op * ret = stm32_qspi_send(mem->spi, op); mutex_unlock(&qspi->lock); - pm_runtime_mark_last_busy(qspi->dev); pm_runtime_put_autosuspend(qspi->dev); return ret; @@ -487,7 +486,6 @@ static int stm32_qspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) ret = stm32_qspi_send(mem->spi, op); mutex_unlock(&qspi->lock); - pm_runtime_mark_last_busy(qspi->dev); pm_runtime_put_autosuspend(qspi->dev); return ret; @@ -543,7 +541,6 @@ static ssize_t stm32_qspi_dirmap_read(struct spi_mem_dirmap_desc *desc, ret = stm32_qspi_send(desc->mem->spi, &op); mutex_unlock(&qspi->lock); - pm_runtime_mark_last_busy(qspi->dev); pm_runtime_put_autosuspend(qspi->dev); return ret ?: len; @@ -627,7 +624,6 @@ static int stm32_qspi_transfer_one_message(struct spi_controller *ctrl, msg->status = ret; spi_finalize_current_message(ctrl); - pm_runtime_mark_last_busy(qspi->dev); pm_runtime_put_autosuspend(qspi->dev); return ret; @@ -684,7 +680,6 @@ static int stm32_qspi_setup(struct spi_device *spi) writel_relaxed(qspi->dcr_reg, qspi->io_base + QSPI_DCR); mutex_unlock(&qspi->lock); - pm_runtime_mark_last_busy(qspi->dev); pm_runtime_put_autosuspend(qspi->dev); return 0; @@ -858,7 +853,6 @@ static int stm32_qspi_probe(struct platform_device *pdev) if (ret) goto err_pm_runtime_free; - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); return 0; @@ -938,7 +932,6 @@ static int __maybe_unused stm32_qspi_resume(struct device *dev) writel_relaxed(qspi->cr_reg, qspi->io_base + QSPI_CR); writel_relaxed(qspi->dcr_reg, qspi->io_base + QSPI_DCR); - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); return 0; diff --git a/drivers/spi/spi-stm32.c b/drivers/spi/spi-stm32.c index da3517d7102d..d819ed4aa4fd 100644 --- a/drivers/spi/spi-stm32.c +++ b/drivers/spi/spi-stm32.c @@ -2233,7 +2233,6 @@ static int stm32_spi_probe(struct platform_device *pdev) goto err_pm_disable; } - pm_runtime_mark_last_busy(&pdev->dev); pm_runtime_put_autosuspend(&pdev->dev); dev_info(&pdev->dev, "driver initialized (%s mode)\n", @@ -2342,7 +2341,6 @@ static int __maybe_unused stm32_spi_resume(struct device *dev) spi->cfg->config(spi); - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); return 0; diff --git a/drivers/spi/spi-ti-qspi.c b/drivers/spi/spi-ti-qspi.c index a284d2794586..0b7eaccbc797 100644 --- a/drivers/spi/spi-ti-qspi.c +++ b/drivers/spi/spi-ti-qspi.c @@ -158,7 +158,6 @@ static int ti_qspi_setup(struct spi_device *spi) return ret; } - pm_runtime_mark_last_busy(qspi->dev); ret = pm_runtime_put_autosuspend(qspi->dev); if (ret < 0) { dev_err(qspi->dev, "pm_runtime_put_autosuspend() failed\n"); @@ -195,7 +194,6 @@ static void ti_qspi_setup_clk(struct ti_qspi *qspi, u32 speed_hz) ctx_reg->clkctrl = clk_ctrl_new; } - pm_runtime_mark_last_busy(qspi->dev); pm_runtime_put_autosuspend(qspi->dev); } diff --git a/drivers/spi/spi-zynqmp-gqspi.c b/drivers/spi/spi-zynqmp-gqspi.c index 595b6dc10845..502fd5eccc83 100644 --- a/drivers/spi/spi-zynqmp-gqspi.c +++ b/drivers/spi/spi-zynqmp-gqspi.c @@ -1330,7 +1330,6 @@ static int zynqmp_qspi_probe(struct platform_device *pdev) goto clk_dis_all; } - pm_runtime_mark_last_busy(&pdev->dev); pm_runtime_put_autosuspend(&pdev->dev); return 0; diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 1bc0fdbb1bd7..91413cc0936a 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1723,7 +1723,6 @@ EXPORT_SYMBOL_GPL(spi_finalize_current_transfer); static void spi_idle_runtime_pm(struct spi_controller *ctlr) { if (ctlr->auto_runtime_pm) { - pm_runtime_mark_last_busy(ctlr->dev.parent); pm_runtime_put_autosuspend(ctlr->dev.parent); } } @@ -3856,7 +3855,6 @@ static int spi_set_cs_timing(struct spi_device *spi) } status = spi->controller->set_cs_timing(spi); - pm_runtime_mark_last_busy(parent); pm_runtime_put_autosuspend(parent); } else { status = spi->controller->set_cs_timing(spi); @@ -3991,7 +3989,6 @@ int spi_setup(struct spi_device *spi) status = 0; spi_set_cs(spi, false, true); - pm_runtime_mark_last_busy(spi->controller->dev.parent); pm_runtime_put_autosuspend(spi->controller->dev.parent); } else { spi_set_cs(spi, false, true); From defe01abfb7f5c5bd53c723b8577d4fcd64faa5a Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Tue, 8 Jul 2025 21:16:37 -0500 Subject: [PATCH 42/62] spi: stm32-ospi: Use of_reserved_mem_region_to_resource() for "memory-region" Use the newly added of_reserved_mem_region_to_resource() function to handle "memory-region" properties. Signed-off-by: Rob Herring (Arm) Link: https://patch.msgid.link/20250709021638.2047365-1-robh@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-stm32-ospi.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/drivers/spi/spi-stm32-ospi.c b/drivers/spi/spi-stm32-ospi.c index 2829535e5cd4..72baa402a2c3 100644 --- a/drivers/spi/spi-stm32-ospi.c +++ b/drivers/spi/spi-stm32-ospi.c @@ -766,9 +766,7 @@ static int stm32_ospi_get_resources(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct stm32_ospi *ospi = platform_get_drvdata(pdev); - struct resource *res; - struct reserved_mem *rmem = NULL; - struct device_node *node; + struct resource *res, _res; int ret; ospi->regs_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); @@ -820,17 +818,13 @@ static int stm32_ospi_get_resources(struct platform_device *pdev) goto err_dma; } - node = of_parse_phandle(dev->of_node, "memory-region", 0); - if (node) - rmem = of_reserved_mem_lookup(node); - of_node_put(node); - - if (rmem) { - ospi->mm_size = rmem->size; - ospi->mm_base = devm_ioremap(dev, rmem->base, rmem->size); + res = &_res; + ret = of_reserved_mem_region_to_resource(dev->of_node, 0, res); + if (!ret) { + ospi->mm_size = resource_size(res); + ospi->mm_base = devm_ioremap_resource(dev, res); if (!ospi->mm_base) { - dev_err(dev, "unable to map memory region: %pa+%pa\n", - &rmem->base, &rmem->size); + dev_err(dev, "unable to map memory region: %pR\n", res); ret = -ENOMEM; goto err_dma; } From 469d7ea8e99124e4def5d98f928ffb4a659aa1c8 Mon Sep 17 00:00:00 2001 From: Darshan Rathod Date: Thu, 10 Jul 2025 04:50:57 +0000 Subject: [PATCH 43/62] spi: xilinx: Fix block comment style and minor cleanups This patch fixes block comment style issues and minor code cleanups as reported by checkpatch.pl. Signed-off-by: Darshan Rathod Link: https://patch.msgid.link/20250710045058.1325-1-darshanrathod475@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-xilinx.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-xilinx.c b/drivers/spi/spi-xilinx.c index ded709b2b459..d59cc8a18484 100644 --- a/drivers/spi/spi-xilinx.c +++ b/drivers/spi/spi-xilinx.c @@ -89,8 +89,8 @@ struct xilinx_spi { u8 bytes_per_word; int buffer_size; /* buffer size in words */ u32 cs_inactive; /* Level of the CS pins when inactive*/ - unsigned int (*read_fn)(void __iomem *); - void (*write_fn)(u32, void __iomem *); + unsigned int (*read_fn)(void __iomem *addr); + void (*write_fn)(u32 val, void __iomem *addr); }; static void xspi_write32(u32 val, void __iomem *addr) @@ -251,6 +251,7 @@ static int xilinx_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t) if (xspi->irq >= 0 && (xspi->force_irq || remaining_words > xspi->buffer_size)) { u32 isr; + use_irq = true; /* Inhibit irq to avoid spurious irqs on tx_empty*/ cr = xspi->read_fn(xspi->regs + XSPI_CR_OFFSET); From 3106db4ead939a70d332a66214eccce6805b991f Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 9 Jul 2025 21:02:33 +0200 Subject: [PATCH 44/62] spi: sh-msiof: Convert to DEFINE_SIMPLE_DEV_PM_OPS() Convert the Renesas SuperH MSIOF driver from SIMPLE_DEV_PM_OPS() to DEFINE_SIMPLE_DEV_PM_OPS() and pm_sleep_ptr(). This lets us drop the check for CONFIG_PM_SLEEP without impacting code size, while increasing build coverage. Signed-off-by: Geert Uytterhoeven Link: https://patch.msgid.link/108c136f2cab9aa8bc8ac90d14a05e66fb87deb0.1752087740.git.geert+renesas@glider.be Signed-off-by: Mark Brown --- drivers/spi/spi-sh-msiof.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index 94a867967e02..b695870fae8c 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -1320,7 +1320,6 @@ static const struct platform_device_id spi_driver_ids[] = { }; MODULE_DEVICE_TABLE(platform, spi_driver_ids); -#ifdef CONFIG_PM_SLEEP static int sh_msiof_spi_suspend(struct device *dev) { struct sh_msiof_spi_priv *p = dev_get_drvdata(dev); @@ -1335,12 +1334,8 @@ static int sh_msiof_spi_resume(struct device *dev) return spi_controller_resume(p->ctlr); } -static SIMPLE_DEV_PM_OPS(sh_msiof_spi_pm_ops, sh_msiof_spi_suspend, - sh_msiof_spi_resume); -#define DEV_PM_OPS (&sh_msiof_spi_pm_ops) -#else -#define DEV_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(sh_msiof_spi_pm_ops, sh_msiof_spi_suspend, + sh_msiof_spi_resume); static struct platform_driver sh_msiof_spi_drv = { .probe = sh_msiof_spi_probe, @@ -1348,7 +1343,7 @@ static struct platform_driver sh_msiof_spi_drv = { .id_table = spi_driver_ids, .driver = { .name = "spi_sh_msiof", - .pm = DEV_PM_OPS, + .pm = pm_sleep_ptr(&sh_msiof_spi_pm_ops), .of_match_table = of_match_ptr(sh_msiof_match), }, }; From 7d61715c58a39edc5f74fc7366487726fc223530 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 9 Jul 2025 21:02:09 +0200 Subject: [PATCH 45/62] spi: rspi: Convert to DEFINE_SIMPLE_DEV_PM_OPS() Convert the Renesas RSPI/QSPI driver from SIMPLE_DEV_PM_OPS() to DEFINE_SIMPLE_DEV_PM_OPS() and pm_sleep_ptr(). This lets us drop the check for CONFIG_PM_SLEEP without impacting code size, while increasing build coverage. Signed-off-by: Geert Uytterhoeven Link: https://patch.msgid.link/0b64c1c3803e6d3eeb3ae9cd8921d4fe67f37118.1752087701.git.geert+renesas@glider.be Signed-off-by: Mark Brown --- drivers/spi/spi-rspi.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index 92faaf614f8e..8e1d911b88b5 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -1404,7 +1404,6 @@ static const struct platform_device_id spi_driver_ids[] = { MODULE_DEVICE_TABLE(platform, spi_driver_ids); -#ifdef CONFIG_PM_SLEEP static int rspi_suspend(struct device *dev) { struct rspi_data *rspi = dev_get_drvdata(dev); @@ -1419,11 +1418,7 @@ static int rspi_resume(struct device *dev) return spi_controller_resume(rspi->ctlr); } -static SIMPLE_DEV_PM_OPS(rspi_pm_ops, rspi_suspend, rspi_resume); -#define DEV_PM_OPS &rspi_pm_ops -#else -#define DEV_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(rspi_pm_ops, rspi_suspend, rspi_resume); static struct platform_driver rspi_driver = { .probe = rspi_probe, @@ -1431,7 +1426,7 @@ static struct platform_driver rspi_driver = { .id_table = spi_driver_ids, .driver = { .name = "renesas_spi", - .pm = DEV_PM_OPS, + .pm = pm_sleep_ptr(&rspi_pm_ops), .of_match_table = of_match_ptr(rspi_of_match), }, }; From 6f8584a4826f01a55d3d0c4bbad5961f1de52fc9 Mon Sep 17 00:00:00 2001 From: Raphael Gallais-Pou Date: Mon, 9 Jun 2025 23:21:09 +0200 Subject: [PATCH 46/62] spi: st: Switch from CONFIG_PM_SLEEP guards to pm_sleep_ptr() Letting the compiler remove these functions when the kernel is built without CONFIG_PM_SLEEP support is simpler and less error prone than the use of #ifdef based kernel configuration guards. Signed-off-by: Raphael Gallais-Pou Link: https://patch.msgid.link/20250609-update_pm_macro-v1-1-819a53ef0eed@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-st-ssc4.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/drivers/spi/spi-st-ssc4.c b/drivers/spi/spi-st-ssc4.c index 4cff976ab16f..49ab4c515156 100644 --- a/drivers/spi/spi-st-ssc4.c +++ b/drivers/spi/spi-st-ssc4.c @@ -378,8 +378,7 @@ static void spi_st_remove(struct platform_device *pdev) pinctrl_pm_select_sleep_state(&pdev->dev); } -#ifdef CONFIG_PM -static int spi_st_runtime_suspend(struct device *dev) +static int __maybe_unused spi_st_runtime_suspend(struct device *dev) { struct spi_controller *host = dev_get_drvdata(dev); struct spi_st *spi_st = spi_controller_get_devdata(host); @@ -392,7 +391,7 @@ static int spi_st_runtime_suspend(struct device *dev) return 0; } -static int spi_st_runtime_resume(struct device *dev) +static int __maybe_unused spi_st_runtime_resume(struct device *dev) { struct spi_controller *host = dev_get_drvdata(dev); struct spi_st *spi_st = spi_controller_get_devdata(host); @@ -403,10 +402,8 @@ static int spi_st_runtime_resume(struct device *dev) return ret; } -#endif -#ifdef CONFIG_PM_SLEEP -static int spi_st_suspend(struct device *dev) +static int __maybe_unused spi_st_suspend(struct device *dev) { struct spi_controller *host = dev_get_drvdata(dev); int ret; @@ -418,7 +415,7 @@ static int spi_st_suspend(struct device *dev) return pm_runtime_force_suspend(dev); } -static int spi_st_resume(struct device *dev) +static int __maybe_unused spi_st_resume(struct device *dev) { struct spi_controller *host = dev_get_drvdata(dev); int ret; @@ -429,7 +426,6 @@ static int spi_st_resume(struct device *dev) return pm_runtime_force_resume(dev); } -#endif static const struct dev_pm_ops spi_st_pm = { SET_SYSTEM_SLEEP_PM_OPS(spi_st_suspend, spi_st_resume) @@ -445,7 +441,7 @@ MODULE_DEVICE_TABLE(of, stm_spi_match); static struct platform_driver spi_st_driver = { .driver = { .name = "spi-st", - .pm = &spi_st_pm, + .pm = pm_sleep_ptr(&spi_st_pm), .of_match_table = of_match_ptr(stm_spi_match), }, .probe = spi_st_probe, From d5255ae7ec48ac1f702e95b472801dbb7bf1e97f Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Tue, 15 Jul 2025 15:27:10 -0500 Subject: [PATCH 47/62] spi: dt-bindings: spi-mux: Drop "spi-max-frequency" as required There's little reason to require the SPI mux to define a maximum bus frequency as the muxing is just the chip select and devices still define their maximum freq. In fact, several users don't set "spi-max-frequency" which caused warnings. Signed-off-by: Rob Herring (Arm) Reviewed-by: Chris Packham Link: https://patch.msgid.link/20250715202711.1882103-1-robh@kernel.org Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/spi/spi-mux.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/Documentation/devicetree/bindings/spi/spi-mux.yaml b/Documentation/devicetree/bindings/spi/spi-mux.yaml index fb2a6039928c..b1e2a97be699 100644 --- a/Documentation/devicetree/bindings/spi/spi-mux.yaml +++ b/Documentation/devicetree/bindings/spi/spi-mux.yaml @@ -46,7 +46,6 @@ properties: required: - compatible - reg - - spi-max-frequency - mux-controls unevaluatedProperties: false From d929cc75e9791def049a90998aaab8934196131c Mon Sep 17 00:00:00 2001 From: Darshan Rathod Date: Wed, 16 Jul 2025 09:59:05 +0000 Subject: [PATCH 48/62] spi: gpio: Use explicit 'unsigned int' for parameter types The C standard allows 'unsigned' as a shorthand for 'unsigned int'. For improved code clarity and consistency with the prevailing kernel coding style, replace the shorthand with the more explicit 'unsigned int' type for function parameters. This is a purely stylistic cleanup and has no functional impact on the generated code. Signed-off-by: Darshan Rathod Link: https://patch.msgid.link/20250716095906.21812-1-darshanrathod475@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-gpio.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/spi/spi-gpio.c b/drivers/spi/spi-gpio.c index ea5f1b10b79e..c8dadb532c40 100644 --- a/drivers/spi/spi-gpio.c +++ b/drivers/spi/spi-gpio.c @@ -104,7 +104,7 @@ static inline int getmiso(const struct spi_device *spi) */ static u32 spi_gpio_txrx_word_mode0(struct spi_device *spi, - unsigned nsecs, u32 word, u8 bits, unsigned flags) + unsigned int nsecs, u32 word, u8 bits, unsigned int flags) { if (unlikely(spi->mode & SPI_LSB_FIRST)) return bitbang_txrx_le_cpha0(spi, nsecs, 0, flags, word, bits); @@ -113,7 +113,7 @@ static u32 spi_gpio_txrx_word_mode0(struct spi_device *spi, } static u32 spi_gpio_txrx_word_mode1(struct spi_device *spi, - unsigned nsecs, u32 word, u8 bits, unsigned flags) + unsigned int nsecs, u32 word, u8 bits, unsigned int flags) { if (unlikely(spi->mode & SPI_LSB_FIRST)) return bitbang_txrx_le_cpha1(spi, nsecs, 0, flags, word, bits); @@ -122,7 +122,7 @@ static u32 spi_gpio_txrx_word_mode1(struct spi_device *spi, } static u32 spi_gpio_txrx_word_mode2(struct spi_device *spi, - unsigned nsecs, u32 word, u8 bits, unsigned flags) + unsigned int nsecs, u32 word, u8 bits, unsigned int flags) { if (unlikely(spi->mode & SPI_LSB_FIRST)) return bitbang_txrx_le_cpha0(spi, nsecs, 1, flags, word, bits); @@ -131,7 +131,7 @@ static u32 spi_gpio_txrx_word_mode2(struct spi_device *spi, } static u32 spi_gpio_txrx_word_mode3(struct spi_device *spi, - unsigned nsecs, u32 word, u8 bits, unsigned flags) + unsigned int nsecs, u32 word, u8 bits, unsigned int flags) { if (unlikely(spi->mode & SPI_LSB_FIRST)) return bitbang_txrx_le_cpha1(spi, nsecs, 1, flags, word, bits); @@ -150,7 +150,7 @@ static u32 spi_gpio_txrx_word_mode3(struct spi_device *spi, */ static u32 spi_gpio_spec_txrx_word_mode0(struct spi_device *spi, - unsigned nsecs, u32 word, u8 bits, unsigned flags) + unsigned int nsecs, u32 word, u8 bits, unsigned int flags) { flags = spi->controller->flags; if (unlikely(spi->mode & SPI_LSB_FIRST)) @@ -160,7 +160,7 @@ static u32 spi_gpio_spec_txrx_word_mode0(struct spi_device *spi, } static u32 spi_gpio_spec_txrx_word_mode1(struct spi_device *spi, - unsigned nsecs, u32 word, u8 bits, unsigned flags) + unsigned int nsecs, u32 word, u8 bits, unsigned int flags) { flags = spi->controller->flags; if (unlikely(spi->mode & SPI_LSB_FIRST)) @@ -170,7 +170,7 @@ static u32 spi_gpio_spec_txrx_word_mode1(struct spi_device *spi, } static u32 spi_gpio_spec_txrx_word_mode2(struct spi_device *spi, - unsigned nsecs, u32 word, u8 bits, unsigned flags) + unsigned int nsecs, u32 word, u8 bits, unsigned int flags) { flags = spi->controller->flags; if (unlikely(spi->mode & SPI_LSB_FIRST)) @@ -180,7 +180,7 @@ static u32 spi_gpio_spec_txrx_word_mode2(struct spi_device *spi, } static u32 spi_gpio_spec_txrx_word_mode3(struct spi_device *spi, - unsigned nsecs, u32 word, u8 bits, unsigned flags) + unsigned int nsecs, u32 word, u8 bits, unsigned int flags) { flags = spi->controller->flags; if (unlikely(spi->mode & SPI_LSB_FIRST)) From 951a6d8d41289b86a564ee5563ededa702b62b1b Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 15 Jul 2025 18:01:39 -0500 Subject: [PATCH 49/62] spi: stm32-ospi: Fix NULL vs IS_ERR() bug in stm32_ospi_get_resources() This code was changed from using devm_ioremap() which returns NULL to using devm_ioremap_resource() which returns error pointers. Update the error checking to match. Fixes: defe01abfb7f ("spi: stm32-ospi: Use of_reserved_mem_region_to_resource() for "memory-region"") Signed-off-by: Dan Carpenter Link: https://patch.msgid.link/fb2a26a2-119b-4b5a-8d44-b29e2c736081@sabinyo.mountain Signed-off-by: Mark Brown --- drivers/spi/spi-stm32-ospi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-stm32-ospi.c b/drivers/spi/spi-stm32-ospi.c index 72baa402a2c3..f36fd36da269 100644 --- a/drivers/spi/spi-stm32-ospi.c +++ b/drivers/spi/spi-stm32-ospi.c @@ -823,9 +823,9 @@ static int stm32_ospi_get_resources(struct platform_device *pdev) if (!ret) { ospi->mm_size = resource_size(res); ospi->mm_base = devm_ioremap_resource(dev, res); - if (!ospi->mm_base) { + if (IS_ERR(ospi->mm_base)) { dev_err(dev, "unable to map memory region: %pR\n", res); - ret = -ENOMEM; + ret = PTR_ERR(ospi->mm_base); goto err_dma; } From aad2f87cbcab56b322109d26d7b11842a09df91f Mon Sep 17 00:00:00 2001 From: Heiko Schocher Date: Sat, 19 Jul 2025 08:33:52 +0200 Subject: [PATCH 50/62] dt-bindings: trivial-devices: Document ABB sensors Add documentation for spi based ABB sensors, which are currently operated from userspace. Signed-off-by: Heiko Schocher Link: https://patch.msgid.link/20250719063355.73111-2-hs@denx.de Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/trivial-devices.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/trivial-devices.yaml b/Documentation/devicetree/bindings/trivial-devices.yaml index 27930708ccd5..25260c2b81df 100644 --- a/Documentation/devicetree/bindings/trivial-devices.yaml +++ b/Documentation/devicetree/bindings/trivial-devices.yaml @@ -30,6 +30,8 @@ properties: items: # Entries are sorted alphanumerically by the compatible - enum: + # ABB register based spi sensors + - abb,spi-sensor # Acbel fsg032 power supply - acbel,fsg032 # SMBus/I2C Digital Temperature Sensor in 6-Pin SOT with SMBus Alert and Over Temperature Pin From d60f7cab7c04944a79af16caa43c141e780a59c6 Mon Sep 17 00:00:00 2001 From: Heiko Schocher Date: Sat, 19 Jul 2025 08:33:53 +0200 Subject: [PATCH 51/62] spi: spidev: Add an entry for the ABB spi sensors This sensors are currently controlled from userspace, ideally we will add full drivers in the future. Signed-off-by: Heiko Schocher Link: https://patch.msgid.link/20250719063355.73111-3-hs@denx.de Signed-off-by: Mark Brown --- drivers/spi/spidev.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index 6108959c28d9..5300c942a2a4 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c @@ -703,6 +703,7 @@ static const struct class spidev_class = { * spidev_dt_ids array below. Both arrays are kept in the same ordering. */ static const struct spi_device_id spidev_spi_ids[] = { + { .name = /* abb */ "spi-sensor" }, { .name = /* cisco */ "spi-petra" }, { .name = /* dh */ "dhcom-board" }, { .name = /* elgin */ "jg10309-01" }, @@ -735,6 +736,7 @@ static int spidev_of_check(struct device *dev) } static const struct of_device_id spidev_dt_ids[] = { + { .compatible = "abb,spi-sensor", .data = &spidev_of_check }, { .compatible = "cisco,spi-petra", .data = &spidev_of_check }, { .compatible = "dh,dhcom-board", .data = &spidev_of_check }, { .compatible = "elgin,jg10309-01", .data = &spidev_of_check }, From 1f590fa4b93dd7c7daaa4e09d8381ac2aab3853c Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Fri, 11 Jul 2025 18:32:52 +0200 Subject: [PATCH 52/62] spi: spi-qpic-snand: simplify bad block marker duplication Due to the expectations of the SPINAND code, the driver duplicates the bad block markers during raw OOB reads. It has been implemented by using two if statements, and due to the opposite conditions one of conditional codepaths always runs. Since the effect of both codepaths is the same, remove the if statements and use a single line solution instead. Also add a note about why the duplication is required. No functional changes intended. Signed-off-by: Gabor Juhos Link: https://patch.msgid.link/20250711-qpic-snand-simplify-bbm-copy-v1-1-dd2608325f72@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-qpic-snand.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/spi/spi-qpic-snand.c b/drivers/spi/spi-qpic-snand.c index c49bf7079808..0cfa0d960fd3 100644 --- a/drivers/spi/spi-qpic-snand.c +++ b/drivers/spi/spi-qpic-snand.c @@ -613,10 +613,16 @@ static int qcom_spi_read_last_cw(struct qcom_nand_controller *snandc, bbpos = mtd->writesize - ecc_cfg->cw_size * (num_cw - 1); - if (snandc->data_buffer[bbpos] == 0xff) - snandc->data_buffer[bbpos + 1] = 0xff; - if (snandc->data_buffer[bbpos] != 0xff) - snandc->data_buffer[bbpos + 1] = snandc->data_buffer[bbpos]; + /* + * TODO: The SPINAND code expects two bad block marker bytes + * at the beginning of the OOB area, but the OOB layout used by + * the driver has only one. Duplicate that for now in order to + * avoid certain blocks to be marked as bad. + * + * This can be removed once single-byte bad block marker support + * gets implemented in the SPINAND code. + */ + snandc->data_buffer[bbpos + 1] = snandc->data_buffer[bbpos]; memcpy(op->data.buf.in, snandc->data_buffer + bbpos, op->data.nbytes); From 7438379cfc47046f7507dfdb54906acf56288b9f Mon Sep 17 00:00:00 2001 From: Zixian Zeng Date: Sun, 20 Jul 2025 16:31:43 +0800 Subject: [PATCH 53/62] spi: dt-bindings: spi-sg2044-nor: Change SOPHGO SG2042 With further testing, directly using the spi-sg2044-nor driver on SG2042 does not work. SG2042 is found to lack full compatibility with SG2044. SG2044 has OPT register and it's necessary to write but SG2042 does not. Due to other possible hardware detail differences, it is better to bind SG2042 independently. Fixes: 8450f1e0d3d0 ("spi: dt-bindings: spi-sg2044-nor: Add SOPHGO SG2042") Signed-off-by: Zixian Zeng Acked-by: Rob Herring (Arm) Reviewed-by: Chen Wang & Tested-by: Chen Wang Link: https://patch.msgid.link/20250720-sfg-spifmc-v4-1-033188ad801e@gmail.com Reviewed-by: Chen Wang & Tested-by: Chen Wang Signed-off-by: Mark Brown --- .../devicetree/bindings/spi/spi-sg2044-nor.yaml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/Documentation/devicetree/bindings/spi/spi-sg2044-nor.yaml b/Documentation/devicetree/bindings/spi/spi-sg2044-nor.yaml index 66e54dedab14..0e7ead763705 100644 --- a/Documentation/devicetree/bindings/spi/spi-sg2044-nor.yaml +++ b/Documentation/devicetree/bindings/spi/spi-sg2044-nor.yaml @@ -14,12 +14,9 @@ allOf: properties: compatible: - oneOf: - - const: sophgo,sg2044-spifmc-nor - - items: - - enum: - - sophgo,sg2042-spifmc-nor - - const: sophgo,sg2044-spifmc-nor + enum: + - sophgo,sg2042-spifmc-nor + - sophgo,sg2044-spifmc-nor reg: maxItems: 1 From 5653b4f8840801d9d141ba6a6c10e7cc15040c5b Mon Sep 17 00:00:00 2001 From: Zixian Zeng Date: Sun, 20 Jul 2025 16:31:44 +0800 Subject: [PATCH 54/62] spi: spi-sg2044-nor: Add configurable chip_info SG2044 and SG2042 have similar SPI-NOR flash controller design, but have incompatibility which causes existing driver not working on SG2042: 1. SPI-NOR flash controller on SG2042 have no OPT register. 2. FIFO trigger level on SG2042 should be strictly less than 8. So introduce a new configurable chip_info structure to hold the different configuration. Link: https://github.com/sophgo/sophgo-doc/blob/main/SG2042/TRM/source/SPI-flash.rst Signed-off-by: Zixian Zeng Reviewed-by: Chen Wang & Tested-by: Chen Wang Link: https://patch.msgid.link/20250720-sfg-spifmc-v4-2-033188ad801e@gmail.com Reviewed-by: Chen Wang & Tested-by: Chen Wang Signed-off-by: Mark Brown --- drivers/spi/spi-sg2044-nor.c | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi-sg2044-nor.c b/drivers/spi/spi-sg2044-nor.c index a59aa3fc55d2..0ef569eb28b7 100644 --- a/drivers/spi/spi-sg2044-nor.c +++ b/drivers/spi/spi-sg2044-nor.c @@ -84,12 +84,18 @@ #define SPIFMC_MAX_READ_SIZE 0x10000 +struct sg204x_spifmc_chip_info { + bool has_opt_reg; + u32 rd_fifo_int_trigger_level; +}; + struct sg2044_spifmc { struct spi_controller *ctrl; void __iomem *io_base; struct device *dev; struct mutex lock; struct clk *clk; + const struct sg204x_spifmc_chip_info *chip_info; }; static int sg2044_spifmc_wait_int(struct sg2044_spifmc *spifmc, u8 int_type) @@ -139,7 +145,7 @@ static ssize_t sg2044_spifmc_read_64k(struct sg2044_spifmc *spifmc, reg = sg2044_spifmc_init_reg(spifmc); reg |= (op->addr.nbytes + op->dummy.nbytes) << SPIFMC_TRAN_CSR_ADDR_BYTES_SHIFT; - reg |= SPIFMC_TRAN_CSR_FIFO_TRG_LVL_8_BYTE; + reg |= spifmc->chip_info->rd_fifo_int_trigger_level; reg |= SPIFMC_TRAN_CSR_WITH_CMD; reg |= SPIFMC_TRAN_CSR_TRAN_MODE_RX; @@ -335,7 +341,8 @@ static ssize_t sg2044_spifmc_trans_reg(struct sg2044_spifmc *spifmc, reg |= SPIFMC_TRAN_CSR_TRAN_MODE_RX; reg |= SPIFMC_TRAN_CSR_TRAN_MODE_TX; - writel(SPIFMC_OPT_DISABLE_FIFO_FLUSH, spifmc->io_base + SPIFMC_OPT); + if (spifmc->chip_info->has_opt_reg) + writel(SPIFMC_OPT_DISABLE_FIFO_FLUSH, spifmc->io_base + SPIFMC_OPT); } else { /* * If write values to the Status Register, @@ -457,6 +464,11 @@ static int sg2044_spifmc_probe(struct platform_device *pdev) ret = devm_mutex_init(dev, &spifmc->lock); if (ret) return ret; + spifmc->chip_info = device_get_match_data(&pdev->dev); + if (!spifmc->chip_info) { + dev_err(&pdev->dev, "Failed to get specific chip info\n"); + return -EINVAL; + } sg2044_spifmc_init(spifmc); sg2044_spifmc_init_reg(spifmc); @@ -468,8 +480,13 @@ static int sg2044_spifmc_probe(struct platform_device *pdev) return 0; } +static const struct sg204x_spifmc_chip_info sg2044_chip_info = { + .has_opt_reg = true, + .rd_fifo_int_trigger_level = SPIFMC_TRAN_CSR_FIFO_TRG_LVL_8_BYTE, +}; + static const struct of_device_id sg2044_spifmc_match[] = { - { .compatible = "sophgo,sg2044-spifmc-nor" }, + { .compatible = "sophgo,sg2044-spifmc-nor", .data = &sg2044_chip_info }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, sg2044_spifmc_match); From f6b159431697c903da1418e70c825faa0cddbdae Mon Sep 17 00:00:00 2001 From: Zixian Zeng Date: Sun, 20 Jul 2025 16:31:45 +0800 Subject: [PATCH 55/62] spi: spi-sg2044-nor: Add SPI-NOR controller for SG2042 Add support for SOPHGO SG2042 SPI-NOR flash controller. Signed-off-by: Zixian Zeng Reviewed-by: Chen Wang & Tested-by: Chen Wang Link: https://patch.msgid.link/20250720-sfg-spifmc-v4-3-033188ad801e@gmail.com Reviewed-by: Chen Wang & Tested-by: Chen Wang Signed-off-by: Mark Brown --- drivers/spi/spi-sg2044-nor.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/spi/spi-sg2044-nor.c b/drivers/spi/spi-sg2044-nor.c index 0ef569eb28b7..af48b1fcda93 100644 --- a/drivers/spi/spi-sg2044-nor.c +++ b/drivers/spi/spi-sg2044-nor.c @@ -485,8 +485,14 @@ static const struct sg204x_spifmc_chip_info sg2044_chip_info = { .rd_fifo_int_trigger_level = SPIFMC_TRAN_CSR_FIFO_TRG_LVL_8_BYTE, }; +static const struct sg204x_spifmc_chip_info sg2042_chip_info = { + .has_opt_reg = false, + .rd_fifo_int_trigger_level = SPIFMC_TRAN_CSR_FIFO_TRG_LVL_1_BYTE, +}; + static const struct of_device_id sg2044_spifmc_match[] = { { .compatible = "sophgo,sg2044-spifmc-nor", .data = &sg2044_chip_info }, + { .compatible = "sophgo,sg2042-spifmc-nor", .data = &sg2042_chip_info }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, sg2044_spifmc_match); From 78d35a20783941c8ba5cf912349728c6e1bee84b Mon Sep 17 00:00:00 2001 From: Sunny Luo Date: Fri, 18 Jul 2025 09:52:16 +0800 Subject: [PATCH 56/62] spi: dt-bindings: Add binding document of Amlogic SPISG controller The SPISG is a new communication oriented SPI controller of Amlogic, which supports PIO, block DMA and scatter-gather DMA three operation modes. Signed-off-by: Sunny Luo Acked-by: Conor Dooley Signed-off-by: Xianwei Zhao Link: https://patch.msgid.link/20250718-spisg-v5-1-b8f0f1eb93a2@amlogic.com Signed-off-by: Mark Brown --- .../bindings/spi/amlogic,a4-spisg.yaml | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 Documentation/devicetree/bindings/spi/amlogic,a4-spisg.yaml diff --git a/Documentation/devicetree/bindings/spi/amlogic,a4-spisg.yaml b/Documentation/devicetree/bindings/spi/amlogic,a4-spisg.yaml new file mode 100644 index 000000000000..9bfb8089f7ea --- /dev/null +++ b/Documentation/devicetree/bindings/spi/amlogic,a4-spisg.yaml @@ -0,0 +1,59 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright (C) 2025 Amlogic, Inc. All rights reserved +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/spi/amlogic,a4-spisg.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Amlogic SPI Scatter-Gather Controller + +maintainers: + - Xianwei Zhao + - Sunny Luo + +allOf: + - $ref: spi-controller.yaml# + +properties: + compatible: + const: amlogic,a4-spisg + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 2 + + clock-names: + items: + - const: core + - const: pclk + + resets: + maxItems: 1 + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + +unevaluatedProperties: false + +examples: + - | + #include + spi@50000 { + compatible = "amlogic,a4-spisg"; + reg = <0x50000 0x38>; + interrupts = ; + clocks = <&clkc 37>, + <&clkc 93>; + clock-names = "core", "pclk"; + #address-cells = <1>; + #size-cells = <0>; + }; From cef9991e04aed3305c61c392e880f6e01a0c2ea4 Mon Sep 17 00:00:00 2001 From: Sunny Luo Date: Fri, 18 Jul 2025 09:52:17 +0800 Subject: [PATCH 57/62] spi: Add Amlogic SPISG driver Introduced support for the new SPI IP (SPISG) driver. The SPISG is a communication-oriented SPI controller from Amlogic,supporting three operation modes: PIO, block DMA, and scatter-gather DMA. Due to there is no FIFO, PIO mode can only transfer one word at a time, which is extremely slow. Therefore, this mode was not implemented. Signed-off-by: Sunny Luo Signed-off-by: Xianwei Zhao Link: https://patch.msgid.link/20250718-spisg-v5-2-b8f0f1eb93a2@amlogic.com Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 9 + drivers/spi/Makefile | 1 + drivers/spi/spi-amlogic-spisg.c | 888 ++++++++++++++++++++++++++++++++ 3 files changed, 898 insertions(+) create mode 100644 drivers/spi/spi-amlogic-spisg.c diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index c51da3fc3604..e11341df2ecf 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -99,6 +99,15 @@ config SPI_AMLOGIC_SPIFC_A1 This enables master mode support for the SPIFC (SPI flash controller) available in Amlogic A1 (A113L SoC). +config SPI_AMLOGIC_SPISG + tristate "Amlogic SPISG controller" + depends on COMMON_CLK + depends on ARCH_MESON || COMPILE_TEST + help + This enables master mode support for the SPISG (SPI scatter-gather + communication controller), which is available on platforms such as + Amlogic A4 SoCs. + config SPI_APPLE tristate "Apple SoC SPI Controller platform driver" depends on ARCH_APPLE || COMPILE_TEST diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 4ea89f6fc531..b74e3104d71f 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_SPI_ALTERA) += spi-altera-platform.o obj-$(CONFIG_SPI_ALTERA_CORE) += spi-altera-core.o obj-$(CONFIG_SPI_ALTERA_DFL) += spi-altera-dfl.o obj-$(CONFIG_SPI_AMLOGIC_SPIFC_A1) += spi-amlogic-spifc-a1.o +obj-$(CONFIG_SPI_AMLOGIC_SPISG) += spi-amlogic-spisg.o obj-$(CONFIG_SPI_APPLE) += spi-apple.o obj-$(CONFIG_SPI_AR934X) += spi-ar934x.o obj-$(CONFIG_SPI_ARMADA_3700) += spi-armada-3700.o diff --git a/drivers/spi/spi-amlogic-spisg.c b/drivers/spi/spi-amlogic-spisg.c new file mode 100644 index 000000000000..48a5325b4db7 --- /dev/null +++ b/drivers/spi/spi-amlogic-spisg.c @@ -0,0 +1,888 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Driver for Amlogic SPI communication Scatter-Gather Controller + * + * Copyright (C) 2025 Amlogic, Inc. All rights reserved + * + * Author: Sunny Luo + * Author: Xianwei Zhao + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Register Map */ +#define SPISG_REG_CFG_READY 0x00 + +#define SPISG_REG_CFG_SPI 0x04 +#define CFG_BUS64_EN BIT(0) +#define CFG_SLAVE_EN BIT(1) +#define CFG_SLAVE_SELECT GENMASK(3, 2) +#define CFG_SFLASH_WP BIT(4) +#define CFG_SFLASH_HD BIT(5) +/* start on vsync rising */ +#define CFG_HW_POS BIT(6) +/* start on vsync falling */ +#define CFG_HW_NEG BIT(7) + +#define SPISG_REG_CFG_START 0x08 +#define CFG_BLOCK_NUM GENMASK(19, 0) +#define CFG_BLOCK_SIZE GENMASK(22, 20) +#define CFG_DATA_COMMAND BIT(23) +#define CFG_OP_MODE GENMASK(25, 24) +#define CFG_RXD_MODE GENMASK(27, 26) +#define CFG_TXD_MODE GENMASK(29, 28) +#define CFG_EOC BIT(30) +#define CFG_PEND BIT(31) + +#define SPISG_REG_CFG_BUS 0x0C +#define CFG_CLK_DIV GENMASK(7, 0) +#define CLK_DIV_WIDTH 8 +#define CFG_RX_TUNING GENMASK(11, 8) +#define CFG_TX_TUNING GENMASK(15, 12) +#define CFG_CS_SETUP GENMASK(19, 16) +#define CFG_LANE GENMASK(21, 20) +#define CFG_HALF_DUPLEX BIT(22) +#define CFG_B_L_ENDIAN BIT(23) +#define CFG_DC_MODE BIT(24) +#define CFG_NULL_CTL BIT(25) +#define CFG_DUMMY_CTL BIT(26) +#define CFG_READ_TURN GENMASK(28, 27) +#define CFG_KEEP_SS BIT(29) +#define CFG_CPHA BIT(30) +#define CFG_CPOL BIT(31) + +#define SPISG_REG_PIO_TX_DATA_L 0x10 +#define SPISG_REG_PIO_TX_DATA_H 0x14 +#define SPISG_REG_PIO_RX_DATA_L 0x18 +#define SPISG_REG_PIO_RX_DATA_H 0x1C +#define SPISG_REG_MEM_TX_ADDR_L 0x10 +#define SPISG_REG_MEM_TX_ADDR_H 0x14 +#define SPISG_REG_MEM_RX_ADDR_L 0x18 +#define SPISG_REG_MEM_RX_ADDR_H 0x1C +#define SPISG_REG_DESC_LIST_L 0x20 +#define SPISG_REG_DESC_LIST_H 0x24 +#define LIST_DESC_PENDING BIT(31) +#define SPISG_REG_DESC_CURRENT_L 0x28 +#define SPISG_REG_DESC_CURRENT_H 0x2c +#define SPISG_REG_IRQ_STS 0x30 +#define SPISG_REG_IRQ_ENABLE 0x34 +#define IRQ_RCH_DESC_EOC BIT(0) +#define IRQ_RCH_DESC_INVALID BIT(1) +#define IRQ_RCH_DESC_RESP BIT(2) +#define IRQ_RCH_DATA_RESP BIT(3) +#define IRQ_WCH_DESC_EOC BIT(4) +#define IRQ_WCH_DESC_INVALID BIT(5) +#define IRQ_WCH_DESC_RESP BIT(6) +#define IRQ_WCH_DATA_RESP BIT(7) +#define IRQ_DESC_ERR BIT(8) +#define IRQ_SPI_READY BIT(9) +#define IRQ_DESC_DONE BIT(10) +#define IRQ_DESC_CHAIN_DONE BIT(11) + +#define SPISG_MAX_REG 0x40 + +#define SPISG_BLOCK_MAX 0x100000 + +#define SPISG_OP_MODE_WRITE_CMD 0 +#define SPISG_OP_MODE_READ_STS 1 +#define SPISG_OP_MODE_WRITE 2 +#define SPISG_OP_MODE_READ 3 + +#define SPISG_DATA_MODE_NONE 0 +#define SPISG_DATA_MODE_PIO 1 +#define SPISG_DATA_MODE_MEM 2 +#define SPISG_DATA_MODE_SG 3 + +#define SPISG_CLK_DIV_MAX 256 +/* recommended by specification */ +#define SPISG_CLK_DIV_MIN 4 +#define DIV_NUM (SPISG_CLK_DIV_MAX - SPISG_CLK_DIV_MIN + 1) + +#define SPISG_PCLK_RATE_MIN 24000000 + +#define SPISG_SINGLE_SPI 0 +#define SPISG_DUAL_SPI 1 +#define SPISG_QUAD_SPI 2 + +struct spisg_sg_link { +#define LINK_ADDR_VALID BIT(0) +#define LINK_ADDR_EOC BIT(1) +#define LINK_ADDR_IRQ BIT(2) +#define LINK_ADDR_ACT GENMASK(5, 3) +#define LINK_ADDR_RING BIT(6) +#define LINK_ADDR_LEN GENMASK(31, 8) + u32 addr; + u32 addr1; +}; + +struct spisg_descriptor { + u32 cfg_start; + u32 cfg_bus; + u64 tx_paddr; + u64 rx_paddr; +}; + +struct spisg_descriptor_extra { + struct spisg_sg_link *tx_ccsg; + struct spisg_sg_link *rx_ccsg; + int tx_ccsg_len; + int rx_ccsg_len; +}; + +struct spisg_device { + struct spi_controller *controller; + struct platform_device *pdev; + struct regmap *map; + struct clk *core; + struct clk *pclk; + struct clk *sclk; + struct clk_div_table *tbl; + struct completion completion; + u32 status; + u32 speed_hz; + u32 effective_speed_hz; + u32 bytes_per_word; + u32 cfg_spi; + u32 cfg_start; + u32 cfg_bus; +}; + +static int spi_delay_to_sclk(u32 slck_speed_hz, struct spi_delay *delay) +{ + u32 ns; + + if (!delay) + return 0; + + if (delay->unit == SPI_DELAY_UNIT_SCK) + return delay->value; + + ns = spi_delay_to_ns(delay, NULL); + if (ns < 0) + return 0; + + return DIV_ROUND_UP_ULL(slck_speed_hz * ns, NSEC_PER_SEC); +} + +static inline u32 aml_spisg_sem_down_read(struct spisg_device *spisg) +{ + u32 ret; + + regmap_read(spisg->map, SPISG_REG_CFG_READY, &ret); + if (ret) + regmap_write(spisg->map, SPISG_REG_CFG_READY, 0); + + return ret; +} + +static inline void aml_spisg_sem_up_write(struct spisg_device *spisg) +{ + regmap_write(spisg->map, SPISG_REG_CFG_READY, 1); +} + +static int aml_spisg_set_speed(struct spisg_device *spisg, uint speed_hz) +{ + u32 cfg_bus; + + if (!speed_hz || speed_hz == spisg->speed_hz) + return 0; + + spisg->speed_hz = speed_hz; + clk_set_rate(spisg->sclk, speed_hz); + /* Store the div for the descriptor mode */ + regmap_read(spisg->map, SPISG_REG_CFG_BUS, &cfg_bus); + spisg->cfg_bus &= ~CFG_CLK_DIV; + spisg->cfg_bus |= cfg_bus & CFG_CLK_DIV; + spisg->effective_speed_hz = clk_get_rate(spisg->sclk); + dev_dbg(&spisg->pdev->dev, + "desired speed %dHz, effective speed %dHz\n", + speed_hz, spisg->effective_speed_hz); + + return 0; +} + +static bool aml_spisg_can_dma(struct spi_controller *ctlr, + struct spi_device *spi, + struct spi_transfer *xfer) +{ + return true; +} + +static void aml_spisg_sg_xlate(struct sg_table *sgt, struct spisg_sg_link *ccsg) +{ + struct scatterlist *sg; + int i; + + for_each_sg(sgt->sgl, sg, sgt->nents, i) { + ccsg->addr = FIELD_PREP(LINK_ADDR_VALID, 1) | + FIELD_PREP(LINK_ADDR_RING, 0) | + FIELD_PREP(LINK_ADDR_EOC, sg_is_last(sg)) | + FIELD_PREP(LINK_ADDR_LEN, sg_dma_len(sg)); + ccsg->addr1 = (u32)sg_dma_address(sg); + ccsg++; + } +} + +static int nbits_to_lane[] = { + SPISG_SINGLE_SPI, + SPISG_SINGLE_SPI, + SPISG_DUAL_SPI, + -EINVAL, + SPISG_QUAD_SPI +}; + +static int aml_spisg_setup_transfer(struct spisg_device *spisg, + struct spi_transfer *xfer, + struct spisg_descriptor *desc, + struct spisg_descriptor_extra *exdesc) +{ + int block_size, blocks; + struct device *dev = &spisg->pdev->dev; + struct spisg_sg_link *ccsg; + int ccsg_len; + dma_addr_t paddr; + int ret; + + memset(desc, 0, sizeof(*desc)); + if (exdesc) + memset(exdesc, 0, sizeof(*exdesc)); + aml_spisg_set_speed(spisg, xfer->speed_hz); + xfer->effective_speed_hz = spisg->effective_speed_hz; + + desc->cfg_start = spisg->cfg_start; + desc->cfg_bus = spisg->cfg_bus; + + block_size = xfer->bits_per_word >> 3; + blocks = xfer->len / block_size; + + desc->cfg_start |= FIELD_PREP(CFG_EOC, 0); + desc->cfg_bus |= FIELD_PREP(CFG_KEEP_SS, !xfer->cs_change); + desc->cfg_bus |= FIELD_PREP(CFG_NULL_CTL, 0); + + if (xfer->tx_buf || xfer->tx_dma) { + desc->cfg_bus |= FIELD_PREP(CFG_LANE, nbits_to_lane[xfer->tx_nbits]); + desc->cfg_start |= FIELD_PREP(CFG_OP_MODE, SPISG_OP_MODE_WRITE); + } + if (xfer->rx_buf || xfer->rx_dma) { + desc->cfg_bus |= FIELD_PREP(CFG_LANE, nbits_to_lane[xfer->rx_nbits]); + desc->cfg_start |= FIELD_PREP(CFG_OP_MODE, SPISG_OP_MODE_READ); + } + + if (FIELD_GET(CFG_OP_MODE, desc->cfg_start) == SPISG_OP_MODE_READ_STS) { + desc->cfg_start |= FIELD_PREP(CFG_BLOCK_SIZE, blocks) | + FIELD_PREP(CFG_BLOCK_NUM, 1); + } else { + blocks = min_t(int, blocks, SPISG_BLOCK_MAX); + desc->cfg_start |= FIELD_PREP(CFG_BLOCK_SIZE, block_size & 0x7) | + FIELD_PREP(CFG_BLOCK_NUM, blocks); + } + + if (xfer->tx_sg.nents && xfer->tx_sg.sgl) { + ccsg_len = xfer->tx_sg.nents * sizeof(struct spisg_sg_link); + ccsg = kzalloc(ccsg_len, GFP_KERNEL | GFP_DMA); + if (!ccsg) { + dev_err(dev, "alloc tx_ccsg failed\n"); + return -ENOMEM; + } + + aml_spisg_sg_xlate(&xfer->tx_sg, ccsg); + paddr = dma_map_single(dev, (void *)ccsg, + ccsg_len, DMA_TO_DEVICE); + ret = dma_mapping_error(dev, paddr); + if (ret) { + kfree(ccsg); + dev_err(dev, "tx ccsg map failed\n"); + return ret; + } + + desc->tx_paddr = paddr; + desc->cfg_start |= FIELD_PREP(CFG_TXD_MODE, SPISG_DATA_MODE_SG); + exdesc->tx_ccsg = ccsg; + exdesc->tx_ccsg_len = ccsg_len; + dma_sync_sgtable_for_device(spisg->controller->cur_tx_dma_dev, + &xfer->tx_sg, DMA_TO_DEVICE); + } else if (xfer->tx_buf || xfer->tx_dma) { + paddr = xfer->tx_dma; + if (!paddr) { + paddr = dma_map_single(dev, (void *)xfer->tx_buf, + xfer->len, DMA_TO_DEVICE); + ret = dma_mapping_error(dev, paddr); + if (ret) { + dev_err(dev, "tx buf map failed\n"); + return ret; + } + } + desc->tx_paddr = paddr; + desc->cfg_start |= FIELD_PREP(CFG_TXD_MODE, SPISG_DATA_MODE_MEM); + } + + if (xfer->rx_sg.nents && xfer->rx_sg.sgl) { + ccsg_len = xfer->rx_sg.nents * sizeof(struct spisg_sg_link); + ccsg = kzalloc(ccsg_len, GFP_KERNEL | GFP_DMA); + if (!ccsg) { + dev_err(dev, "alloc rx_ccsg failed\n"); + return -ENOMEM; + } + + aml_spisg_sg_xlate(&xfer->rx_sg, ccsg); + paddr = dma_map_single(dev, (void *)ccsg, + ccsg_len, DMA_TO_DEVICE); + ret = dma_mapping_error(dev, paddr); + if (ret) { + kfree(ccsg); + dev_err(dev, "rx ccsg map failed\n"); + return ret; + } + + desc->rx_paddr = paddr; + desc->cfg_start |= FIELD_PREP(CFG_RXD_MODE, SPISG_DATA_MODE_SG); + exdesc->rx_ccsg = ccsg; + exdesc->rx_ccsg_len = ccsg_len; + dma_sync_sgtable_for_device(spisg->controller->cur_rx_dma_dev, + &xfer->rx_sg, DMA_FROM_DEVICE); + } else if (xfer->rx_buf || xfer->rx_dma) { + paddr = xfer->rx_dma; + if (!paddr) { + paddr = dma_map_single(dev, xfer->rx_buf, + xfer->len, DMA_FROM_DEVICE); + ret = dma_mapping_error(dev, paddr); + if (ret) { + dev_err(dev, "rx buf map failed\n"); + return ret; + } + } + + desc->rx_paddr = paddr; + desc->cfg_start |= FIELD_PREP(CFG_RXD_MODE, SPISG_DATA_MODE_MEM); + } + + return 0; +} + +static void aml_spisg_cleanup_transfer(struct spisg_device *spisg, + struct spi_transfer *xfer, + struct spisg_descriptor *desc, + struct spisg_descriptor_extra *exdesc) +{ + struct device *dev = &spisg->pdev->dev; + + if (desc->tx_paddr) { + if (FIELD_GET(CFG_TXD_MODE, desc->cfg_start) == SPISG_DATA_MODE_SG) { + dma_unmap_single(dev, (dma_addr_t)desc->tx_paddr, + exdesc->tx_ccsg_len, DMA_TO_DEVICE); + kfree(exdesc->tx_ccsg); + dma_sync_sgtable_for_cpu(spisg->controller->cur_tx_dma_dev, + &xfer->tx_sg, DMA_TO_DEVICE); + } else if (!xfer->tx_dma) { + dma_unmap_single(dev, (dma_addr_t)desc->tx_paddr, + xfer->len, DMA_TO_DEVICE); + } + } + + if (desc->rx_paddr) { + if (FIELD_GET(CFG_RXD_MODE, desc->cfg_start) == SPISG_DATA_MODE_SG) { + dma_unmap_single(dev, (dma_addr_t)desc->rx_paddr, + exdesc->rx_ccsg_len, DMA_TO_DEVICE); + kfree(exdesc->rx_ccsg); + dma_sync_sgtable_for_cpu(spisg->controller->cur_rx_dma_dev, + &xfer->rx_sg, DMA_FROM_DEVICE); + } else if (!xfer->rx_dma) { + dma_unmap_single(dev, (dma_addr_t)desc->rx_paddr, + xfer->len, DMA_FROM_DEVICE); + } + } +} + +static void aml_spisg_setup_null_desc(struct spisg_device *spisg, + struct spisg_descriptor *desc, + u32 n_sclk) +{ + /* unit is the last xfer sclk */ + desc->cfg_start = spisg->cfg_start; + desc->cfg_bus = spisg->cfg_bus; + + desc->cfg_start |= FIELD_PREP(CFG_OP_MODE, SPISG_OP_MODE_WRITE) | + FIELD_PREP(CFG_BLOCK_SIZE, 1) | + FIELD_PREP(CFG_BLOCK_NUM, DIV_ROUND_UP(n_sclk, 8)); + + desc->cfg_bus |= FIELD_PREP(CFG_NULL_CTL, 1); +} + +static void aml_spisg_pending(struct spisg_device *spisg, + dma_addr_t desc_paddr, + bool trig, + bool irq_en) +{ + u32 desc_l, desc_h, cfg_spi, irq_enable; + +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + desc_l = (u64)desc_paddr & 0xffffffff; + desc_h = (u64)desc_paddr >> 32; +#else + desc_l = desc_paddr & 0xffffffff; + desc_h = 0; +#endif + + cfg_spi = spisg->cfg_spi; + if (trig) + cfg_spi |= CFG_HW_POS; + else + desc_h |= LIST_DESC_PENDING; + + irq_enable = IRQ_RCH_DESC_INVALID | IRQ_RCH_DESC_RESP | + IRQ_RCH_DATA_RESP | IRQ_WCH_DESC_INVALID | + IRQ_WCH_DESC_RESP | IRQ_WCH_DATA_RESP | + IRQ_DESC_ERR | IRQ_DESC_CHAIN_DONE; + regmap_write(spisg->map, SPISG_REG_IRQ_ENABLE, irq_en ? irq_enable : 0); + regmap_write(spisg->map, SPISG_REG_CFG_SPI, cfg_spi); + regmap_write(spisg->map, SPISG_REG_DESC_LIST_L, desc_l); + regmap_write(spisg->map, SPISG_REG_DESC_LIST_H, desc_h); +} + +static irqreturn_t aml_spisg_irq(int irq, void *data) +{ + struct spisg_device *spisg = (void *)data; + u32 sts; + + spisg->status = 0; + regmap_read(spisg->map, SPISG_REG_IRQ_STS, &sts); + regmap_write(spisg->map, SPISG_REG_IRQ_STS, sts); + if (sts & (IRQ_RCH_DESC_INVALID | + IRQ_RCH_DESC_RESP | + IRQ_RCH_DATA_RESP | + IRQ_WCH_DESC_INVALID | + IRQ_WCH_DESC_RESP | + IRQ_WCH_DATA_RESP | + IRQ_DESC_ERR)) + spisg->status = sts; + else if (sts & IRQ_DESC_CHAIN_DONE) + spisg->status = 0; + else + return IRQ_NONE; + + complete(&spisg->completion); + + return IRQ_HANDLED; +} + +static int aml_spisg_transfer_one_message(struct spi_controller *ctlr, + struct spi_message *msg) +{ + struct spisg_device *spisg = spi_controller_get_devdata(ctlr); + struct device *dev = &spisg->pdev->dev; + unsigned long long ms = 0; + struct spi_transfer *xfer; + struct spisg_descriptor *descs, *desc; + struct spisg_descriptor_extra *exdescs, *exdesc; + dma_addr_t descs_paddr; + int desc_num = 1, descs_len; + u32 cs_hold_in_sclk = 0; + int ret = -EIO; + + if (!aml_spisg_sem_down_read(spisg)) { + spi_finalize_current_message(ctlr); + dev_err(dev, "controller busy\n"); + return -EBUSY; + } + + /* calculate the desc num for all xfer */ + list_for_each_entry(xfer, &msg->transfers, transfer_list) + desc_num++; + + /* alloc descriptor/extra-descriptor table */ + descs = kcalloc(desc_num, sizeof(*desc) + sizeof(*exdesc), + GFP_KERNEL | GFP_DMA); + if (!descs) { + spi_finalize_current_message(ctlr); + aml_spisg_sem_up_write(spisg); + return -ENOMEM; + } + descs_len = sizeof(*desc) * desc_num; + exdescs = (struct spisg_descriptor_extra *)(descs + desc_num); + + /* config descriptor for each xfer */ + desc = descs; + exdesc = exdescs; + list_for_each_entry(xfer, &msg->transfers, transfer_list) { + ret = aml_spisg_setup_transfer(spisg, xfer, desc, exdesc); + if (ret) { + dev_err(dev, "config descriptor failed\n"); + goto end; + } + + /* calculate cs-setup delay with the first xfer speed */ + if (list_is_first(&xfer->transfer_list, &msg->transfers)) + desc->cfg_bus |= FIELD_PREP(CFG_CS_SETUP, + spi_delay_to_sclk(xfer->effective_speed_hz, &msg->spi->cs_setup)); + + /* calculate cs-hold delay with the last xfer speed */ + if (list_is_last(&xfer->transfer_list, &msg->transfers)) + cs_hold_in_sclk = + spi_delay_to_sclk(xfer->effective_speed_hz, &msg->spi->cs_hold); + + desc++; + exdesc++; + ms += DIV_ROUND_UP_ULL(8LL * MSEC_PER_SEC * xfer->len, + xfer->effective_speed_hz); + } + + if (cs_hold_in_sclk) + /* additional null-descriptor to achieve the cs-hold delay */ + aml_spisg_setup_null_desc(spisg, desc, cs_hold_in_sclk); + else + desc--; + + desc->cfg_bus |= FIELD_PREP(CFG_KEEP_SS, 0); + desc->cfg_start |= FIELD_PREP(CFG_EOC, 1); + + /* some tolerances */ + ms += ms + 20; + if (ms > UINT_MAX) + ms = UINT_MAX; + + descs_paddr = dma_map_single(dev, (void *)descs, + descs_len, DMA_TO_DEVICE); + ret = dma_mapping_error(dev, descs_paddr); + if (ret) { + dev_err(dev, "desc table map failed\n"); + goto end; + } + + reinit_completion(&spisg->completion); + aml_spisg_pending(spisg, descs_paddr, false, true); + if (wait_for_completion_timeout(&spisg->completion, + spi_controller_is_target(spisg->controller) ? + MAX_SCHEDULE_TIMEOUT : msecs_to_jiffies(ms))) + ret = spisg->status ? -EIO : 0; + else + ret = -ETIMEDOUT; + + dma_unmap_single(dev, descs_paddr, descs_len, DMA_TO_DEVICE); +end: + desc = descs; + exdesc = exdescs; + list_for_each_entry(xfer, &msg->transfers, transfer_list) + aml_spisg_cleanup_transfer(spisg, xfer, desc++, exdesc++); + kfree(descs); + + if (!ret) + msg->actual_length = msg->frame_length; + msg->status = ret; + spi_finalize_current_message(ctlr); + aml_spisg_sem_up_write(spisg); + + return ret; +} + +static int aml_spisg_prepare_message(struct spi_controller *ctlr, + struct spi_message *message) +{ + struct spisg_device *spisg = spi_controller_get_devdata(ctlr); + struct spi_device *spi = message->spi; + + if (!spi->bits_per_word || spi->bits_per_word % 8) { + dev_err(&spisg->pdev->dev, "invalid wordlen %d\n", spi->bits_per_word); + return -EINVAL; + } + + spisg->bytes_per_word = spi->bits_per_word >> 3; + + spisg->cfg_spi &= ~CFG_SLAVE_SELECT; + spisg->cfg_spi |= FIELD_PREP(CFG_SLAVE_SELECT, spi_get_chipselect(spi, 0)); + + spisg->cfg_bus &= ~(CFG_CPOL | CFG_CPHA | CFG_B_L_ENDIAN | CFG_HALF_DUPLEX); + spisg->cfg_bus |= FIELD_PREP(CFG_CPOL, !!(spi->mode & SPI_CPOL)) | + FIELD_PREP(CFG_CPHA, !!(spi->mode & SPI_CPHA)) | + FIELD_PREP(CFG_B_L_ENDIAN, !!(spi->mode & SPI_LSB_FIRST)) | + FIELD_PREP(CFG_HALF_DUPLEX, !!(spi->mode & SPI_3WIRE)); + + return 0; +} + +static int aml_spisg_setup(struct spi_device *spi) +{ + if (!spi->controller_state) + spi->controller_state = spi_controller_get_devdata(spi->controller); + + return 0; +} + +static void aml_spisg_cleanup(struct spi_device *spi) +{ + spi->controller_state = NULL; +} + +static int aml_spisg_target_abort(struct spi_controller *ctlr) +{ + struct spisg_device *spisg = spi_controller_get_devdata(ctlr); + + spisg->status = 0; + regmap_write(spisg->map, SPISG_REG_DESC_LIST_H, 0); + complete(&spisg->completion); + + return 0; +} + +static int aml_spisg_clk_init(struct spisg_device *spisg, void __iomem *base) +{ + struct device *dev = &spisg->pdev->dev; + struct clk_init_data init; + struct clk_divider *div; + struct clk_div_table *tbl; + char name[32]; + int ret, i; + + spisg->core = devm_clk_get_enabled(dev, "core"); + if (IS_ERR_OR_NULL(spisg->core)) { + dev_err(dev, "core clock request failed\n"); + return PTR_ERR(spisg->core); + } + + spisg->pclk = devm_clk_get_enabled(dev, "pclk"); + if (IS_ERR_OR_NULL(spisg->pclk)) { + dev_err(dev, "pclk clock request failed\n"); + return PTR_ERR(spisg->pclk); + } + + clk_set_min_rate(spisg->pclk, SPISG_PCLK_RATE_MIN); + + clk_disable_unprepare(spisg->pclk); + + tbl = devm_kzalloc(dev, sizeof(struct clk_div_table) * (DIV_NUM + 1), GFP_KERNEL); + if (!tbl) + return -ENOMEM; + + for (i = 0; i < DIV_NUM; i++) { + tbl[i].val = i + SPISG_CLK_DIV_MIN - 1; + tbl[i].div = i + SPISG_CLK_DIV_MIN; + } + spisg->tbl = tbl; + + div = devm_kzalloc(dev, sizeof(*div), GFP_KERNEL); + if (!div) + return -ENOMEM; + + div->flags = CLK_DIVIDER_ROUND_CLOSEST; + div->reg = base + SPISG_REG_CFG_BUS; + div->shift = __bf_shf(CFG_CLK_DIV); + div->width = CLK_DIV_WIDTH; + div->table = tbl; + + /* Register value should not be outside of the table */ + regmap_update_bits(spisg->map, SPISG_REG_CFG_BUS, CFG_CLK_DIV, + FIELD_PREP(CFG_CLK_DIV, SPISG_CLK_DIV_MIN - 1)); + + /* Register clk-divider */ + snprintf(name, sizeof(name), "%s_div", dev_name(dev)); + init.name = name; + init.ops = &clk_divider_ops; + init.flags = CLK_SET_RATE_PARENT; + init.parent_data = &(const struct clk_parent_data) { + .fw_name = "pclk", + }; + init.num_parents = 1; + div->hw.init = &init; + ret = devm_clk_hw_register(dev, &div->hw); + if (ret) { + dev_err(dev, "clock registration failed\n"); + return ret; + } + + spisg->sclk = devm_clk_hw_get_clk(dev, &div->hw, NULL); + if (IS_ERR_OR_NULL(spisg->sclk)) { + dev_err(dev, "get clock failed\n"); + return PTR_ERR(spisg->sclk); + } + + clk_prepare_enable(spisg->sclk); + + return 0; +} + +static int aml_spisg_probe(struct platform_device *pdev) +{ + struct spi_controller *ctlr; + struct spisg_device *spisg; + struct device *dev = &pdev->dev; + void __iomem *base; + int ret, irq; + + const struct regmap_config aml_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = SPISG_MAX_REG, + }; + + if (of_property_read_bool(dev->of_node, "spi-slave")) + ctlr = spi_alloc_target(dev, sizeof(*spisg)); + else + ctlr = spi_alloc_host(dev, sizeof(*spisg)); + if (!ctlr) + return dev_err_probe(dev, -ENOMEM, "controller allocation failed\n"); + + spisg = spi_controller_get_devdata(ctlr); + spisg->controller = ctlr; + + spisg->pdev = pdev; + platform_set_drvdata(pdev, spisg); + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return dev_err_probe(dev, PTR_ERR(base), "resource ioremap failed\n"); + + spisg->map = devm_regmap_init_mmio(dev, base, &aml_regmap_config); + if (IS_ERR(spisg->map)) + return dev_err_probe(dev, PTR_ERR(spisg->map), "regmap init failed\n"); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + ret = irq; + goto out_controller; + } + + ret = device_reset_optional(dev); + if (ret) + return dev_err_probe(dev, ret, "reset dev failed\n"); + + ret = aml_spisg_clk_init(spisg, base); + if (ret) + return dev_err_probe(dev, ret, "clock init failed\n"); + + spisg->cfg_spi = 0; + spisg->cfg_start = 0; + spisg->cfg_bus = 0; + + spisg->cfg_spi = FIELD_PREP(CFG_SFLASH_WP, 1) | + FIELD_PREP(CFG_SFLASH_HD, 1); + if (spi_controller_is_target(ctlr)) { + spisg->cfg_spi |= FIELD_PREP(CFG_SLAVE_EN, 1); + spisg->cfg_bus = FIELD_PREP(CFG_TX_TUNING, 0xf); + } + /* default pending */ + spisg->cfg_start = FIELD_PREP(CFG_PEND, 1); + + pm_runtime_set_active(&spisg->pdev->dev); + pm_runtime_enable(&spisg->pdev->dev); + pm_runtime_resume_and_get(&spisg->pdev->dev); + + ctlr->num_chipselect = 4; + ctlr->dev.of_node = pdev->dev.of_node; + ctlr->mode_bits = SPI_CPHA | SPI_CPOL | SPI_LSB_FIRST | + SPI_3WIRE | SPI_TX_QUAD | SPI_RX_QUAD; + ctlr->max_speed_hz = 1000 * 1000 * 100; + ctlr->min_speed_hz = 1000 * 10; + ctlr->setup = aml_spisg_setup; + ctlr->cleanup = aml_spisg_cleanup; + ctlr->prepare_message = aml_spisg_prepare_message; + ctlr->transfer_one_message = aml_spisg_transfer_one_message; + ctlr->target_abort = aml_spisg_target_abort; + ctlr->can_dma = aml_spisg_can_dma; + ctlr->max_dma_len = SPISG_BLOCK_MAX; + ctlr->auto_runtime_pm = true; + + dma_set_max_seg_size(&pdev->dev, SPISG_BLOCK_MAX); + + ret = devm_request_irq(&pdev->dev, irq, aml_spisg_irq, 0, NULL, spisg); + if (ret) { + dev_err(&pdev->dev, "irq request failed\n"); + goto out_clk; + } + + ret = devm_spi_register_controller(dev, ctlr); + if (ret) { + dev_err(&pdev->dev, "spi controller registration failed\n"); + goto out_clk; + } + + init_completion(&spisg->completion); + + pm_runtime_put(&spisg->pdev->dev); + + return 0; +out_clk: + if (spisg->core) + clk_disable_unprepare(spisg->core); + clk_disable_unprepare(spisg->pclk); +out_controller: + spi_controller_put(ctlr); + + return ret; +} + +static void aml_spisg_remove(struct platform_device *pdev) +{ + struct spisg_device *spisg = platform_get_drvdata(pdev); + + if (!pm_runtime_suspended(&pdev->dev)) { + pinctrl_pm_select_sleep_state(&spisg->pdev->dev); + clk_disable_unprepare(spisg->core); + clk_disable_unprepare(spisg->pclk); + } +} + +static int spisg_suspend_runtime(struct device *dev) +{ + struct spisg_device *spisg = dev_get_drvdata(dev); + + pinctrl_pm_select_sleep_state(&spisg->pdev->dev); + clk_disable_unprepare(spisg->sclk); + clk_disable_unprepare(spisg->core); + + return 0; +} + +static int spisg_resume_runtime(struct device *dev) +{ + struct spisg_device *spisg = dev_get_drvdata(dev); + + clk_prepare_enable(spisg->core); + clk_prepare_enable(spisg->sclk); + pinctrl_pm_select_default_state(&spisg->pdev->dev); + + return 0; +} + +static const struct dev_pm_ops amlogic_spisg_pm_ops = { + .runtime_suspend = spisg_suspend_runtime, + .runtime_resume = spisg_resume_runtime, +}; + +static const struct of_device_id amlogic_spisg_of_match[] = { + { + .compatible = "amlogic,a4-spisg", + }, + + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, amlogic_spisg_of_match); + +static struct platform_driver amlogic_spisg_driver = { + .probe = aml_spisg_probe, + .remove = aml_spisg_remove, + .driver = { + .name = "amlogic-spisg", + .of_match_table = amlogic_spisg_of_match, + .pm = &amlogic_spisg_pm_ops, + }, +}; + +module_platform_driver(amlogic_spisg_driver); + +MODULE_DESCRIPTION("Amlogic SPI Scatter-Gather Controller driver"); +MODULE_AUTHOR("Sunny Luo "); +MODULE_LICENSE("GPL"); From 0ef2a9779e9decee52a85bc393309b3e068a74a6 Mon Sep 17 00:00:00 2001 From: Xianwei Zhao Date: Fri, 18 Jul 2025 09:52:18 +0800 Subject: [PATCH 58/62] MAINTAINERS: Add an entry for Amlogic spi driver Add Amlogic spi entry to MAINTAINERS to clarify the maintainers. Signed-off-by: Xianwei Zhao Link: https://patch.msgid.link/20250718-spisg-v5-3-b8f0f1eb93a2@amlogic.com Signed-off-by: Mark Brown --- MAINTAINERS | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 10850512c118..f04f5c3e100b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1306,6 +1306,15 @@ S: Maintained F: Documentation/devicetree/bindings/rtc/amlogic,a4-rtc.yaml F: drivers/rtc/rtc-amlogic-a4.c +AMLOGIC SPISG DRIVER +M: Sunny Luo +M: Xianwei Zhao +L: linux-amlogic@lists.infradead.org +L: linux-spi@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/spi/amlogic,a4-spisg.yaml +F: drivers/spi/spi-amlogic-spisg.c + AMPHENOL CHIPCAP 2 DRIVER M: Javier Carrasco L: linux-hwmon@vger.kernel.org From 44b91d61c505863b8ae90b7094aee5ca0dce808f Mon Sep 17 00:00:00 2001 From: Fabrizio Castro Date: Fri, 4 Jul 2025 17:20:34 +0100 Subject: [PATCH 59/62] spi: dt-bindings: Document the RZ/V2H(P) RSPI Add dt-bindings for the RSPI IP found inside the Renesas RZ/V2H(P) SoC. Signed-off-by: Fabrizio Castro Reviewed-by: Rob Herring (Arm) Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20250704162036.468765-2-fabrizio.castro.jz@renesas.com Signed-off-by: Mark Brown --- .../bindings/spi/renesas,rzv2h-rspi.yaml | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 Documentation/devicetree/bindings/spi/renesas,rzv2h-rspi.yaml diff --git a/Documentation/devicetree/bindings/spi/renesas,rzv2h-rspi.yaml b/Documentation/devicetree/bindings/spi/renesas,rzv2h-rspi.yaml new file mode 100644 index 000000000000..ab27fefc3c3a --- /dev/null +++ b/Documentation/devicetree/bindings/spi/renesas,rzv2h-rspi.yaml @@ -0,0 +1,96 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/spi/renesas,rzv2h-rspi.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Renesas RZ/V2H(P) Renesas Serial Peripheral Interface (RSPI) + +maintainers: + - Fabrizio Castro + +allOf: + - $ref: spi-controller.yaml# + +properties: + compatible: + const: renesas,r9a09g057-rspi # RZ/V2H(P) + + reg: + maxItems: 1 + + interrupts: + items: + - description: Idle Interrupt + - description: Error Interrupt + - description: Communication End Interrupt + - description: Receive Buffer Full Interrupt + - description: Transmit Buffer Empty Interrupt + + interrupt-names: + items: + - const: idle + - const: error + - const: end + - const: rx + - const: tx + + clocks: + maxItems: 3 + + clock-names: + items: + - const: pclk + - const: pclk_sfr + - const: tclk + + resets: + maxItems: 2 + + reset-names: + items: + - const: presetn + - const: tresetn + + power-domains: + maxItems: 1 + +required: + - compatible + - reg + - interrupts + - interrupt-names + - clocks + - clock-names + - resets + - reset-names + - power-domains + - '#address-cells' + - '#size-cells' + +unevaluatedProperties: false + +examples: + - | + #include + #include + spi@12800800 { + compatible = "renesas,r9a09g057-rspi"; + + reg = <0x12800800 0x400>; + interrupts = , + , + , + , + ; + interrupt-names = "idle", "error", "end", "rx", "tx"; + clocks = <&cpg CPG_MOD 0x5a>, + <&cpg CPG_MOD 0x5b>, + <&cpg CPG_MOD 0x5c>; + clock-names = "pclk", "pclk_sfr", "tclk"; + resets = <&cpg 0x7f>, <&cpg 0x80>; + reset-names = "presetn", "tresetn"; + power-domains = <&cpg>; + #address-cells = <1>; + #size-cells = <0>; + }; From 8b61c8919dff080d83415523cd68f2fef03ccfc7 Mon Sep 17 00:00:00 2001 From: Fabrizio Castro Date: Fri, 4 Jul 2025 17:20:35 +0100 Subject: [PATCH 60/62] spi: Add driver for the RZ/V2H(P) RSPI IP The Renesas RZ/V2H(P) RSPI IP supports 4-wire and 3-wire serial communications in both host role and target role. It can use a DMA, but the I/O can also be driven by the processor. RX-only, TX-only, and RX-TX operations are available in DMA mode, while in processor I/O mode it only RX-TX operations are supported. Add a driver to support 4-wire serial communications as host role in processor I/O mode. Signed-off-by: Fabrizio Castro Link: https://patch.msgid.link/20250704162036.468765-3-fabrizio.castro.jz@renesas.com Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 8 + drivers/spi/Makefile | 1 + drivers/spi/spi-rzv2h-rspi.c | 466 +++++++++++++++++++++++++++++++++++ 3 files changed, 475 insertions(+) create mode 100644 drivers/spi/spi-rzv2h-rspi.c diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index c51da3fc3604..937e441b9410 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -923,6 +923,14 @@ config SPI_RSPI help SPI driver for Renesas RSPI and QSPI blocks. +config SPI_RZV2H_RSPI + tristate "Renesas RZ/V2H RSPI controller" + depends on ARCH_RENESAS || COMPILE_TEST + help + RSPI driver for the Renesas RZ/V2H Serial Peripheral Interface (RSPI). + RSPI supports both SPI host and SPI target roles. This option only + enables the SPI host role. + config SPI_RZV2M_CSI tristate "Renesas RZ/V2M CSI controller" depends on ARCH_RENESAS || COMPILE_TEST diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 4ea89f6fc531..c19d02653b8a 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -126,6 +126,7 @@ obj-$(CONFIG_MACH_REALTEK_RTL) += spi-realtek-rtl.o obj-$(CONFIG_SPI_REALTEK_SNAND) += spi-realtek-rtl-snand.o obj-$(CONFIG_SPI_RPCIF) += spi-rpc-if.o obj-$(CONFIG_SPI_RSPI) += spi-rspi.o +obj-$(CONFIG_SPI_RZV2H_RSPI) += spi-rzv2h-rspi.o obj-$(CONFIG_SPI_RZV2M_CSI) += spi-rzv2m-csi.o obj-$(CONFIG_SPI_S3C64XX) += spi-s3c64xx.o obj-$(CONFIG_SPI_SC18IS602) += spi-sc18is602.o diff --git a/drivers/spi/spi-rzv2h-rspi.c b/drivers/spi/spi-rzv2h-rspi.c new file mode 100644 index 000000000000..dcc431ba60a9 --- /dev/null +++ b/drivers/spi/spi-rzv2h-rspi.c @@ -0,0 +1,466 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Renesas RZ/V2H Renesas Serial Peripheral Interface (RSPI) + * + * Copyright (C) 2025 Renesas Electronics Corporation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Registers */ +#define RSPI_SPDR 0x00 +#define RSPI_SPCR 0x08 +#define RSPI_SSLP 0x10 +#define RSPI_SPBR 0x11 +#define RSPI_SPSCR 0x13 +#define RSPI_SPCMD 0x14 +#define RSPI_SPDCR2 0x44 +#define RSPI_SPSR 0x52 +#define RSPI_SPSRC 0x6a +#define RSPI_SPFCR 0x6c + +/* Register SPCR */ +#define RSPI_SPCR_MSTR BIT(30) +#define RSPI_SPCR_SPRIE BIT(17) +#define RSPI_SPCR_SCKASE BIT(12) +#define RSPI_SPCR_SPE BIT(0) + +/* Register SPBR */ +#define RSPI_SPBR_SPR_MIN 0 +#define RSPI_SPBR_SPR_MAX 255 + +/* Register SPCMD */ +#define RSPI_SPCMD_SSLA GENMASK(25, 24) +#define RSPI_SPCMD_SPB GENMASK(20, 16) +#define RSPI_SPCMD_LSBF BIT(12) +#define RSPI_SPCMD_SSLKP BIT(7) +#define RSPI_SPCMD_BRDV GENMASK(3, 2) +#define RSPI_SPCMD_CPOL BIT(1) +#define RSPI_SPCMD_CPHA BIT(0) + +#define RSPI_SPCMD_BRDV_MIN 0 +#define RSPI_SPCMD_BRDV_MAX 3 + +/* Register SPDCR2 */ +#define RSPI_SPDCR2_TTRG GENMASK(11, 8) +#define RSPI_SPDCR2_RTRG GENMASK(3, 0) +#define RSPI_FIFO_SIZE 16 + +/* Register SPSR */ +#define RSPI_SPSR_SPRF BIT(15) + +/* Register RSPI_SPSRC */ +#define RSPI_SPSRC_CLEAR 0xfd80 + +#define RSPI_RESET_NUM 2 +#define RSPI_CLK_NUM 3 + +struct rzv2h_rspi_priv { + struct reset_control_bulk_data resets[RSPI_RESET_NUM]; + struct spi_controller *controller; + void __iomem *base; + struct clk *tclk; + wait_queue_head_t wait; + unsigned int bytes_per_word; + u32 freq; + u16 status; +}; + +#define RZV2H_RSPI_TX(func, type) \ +static inline void rzv2h_rspi_tx_##type(struct rzv2h_rspi_priv *rspi, \ + const void *txbuf, \ + unsigned int index) { \ + type buf = 0; \ + \ + if (txbuf) \ + buf = ((type *)txbuf)[index]; \ + \ + func(buf, rspi->base + RSPI_SPDR); \ +} + +#define RZV2H_RSPI_RX(func, type) \ +static inline void rzv2h_rspi_rx_##type(struct rzv2h_rspi_priv *rspi, \ + void *rxbuf, \ + unsigned int index) { \ + type buf = func(rspi->base + RSPI_SPDR); \ + \ + if (rxbuf) \ + ((type *)rxbuf)[index] = buf; \ +} + +RZV2H_RSPI_TX(writel, u32) +RZV2H_RSPI_TX(writew, u16) +RZV2H_RSPI_TX(writeb, u8) +RZV2H_RSPI_RX(readl, u32) +RZV2H_RSPI_RX(readw, u16) +RZV2H_RSPI_RX(readl, u8) + +static void rzv2h_rspi_reg_rmw(const struct rzv2h_rspi_priv *rspi, + int reg_offs, u32 bit_mask, u32 value) +{ + u32 tmp; + + value <<= __ffs(bit_mask); + tmp = (readl(rspi->base + reg_offs) & ~bit_mask) | value; + writel(tmp, rspi->base + reg_offs); +} + +static inline void rzv2h_rspi_spe_disable(const struct rzv2h_rspi_priv *rspi) +{ + rzv2h_rspi_reg_rmw(rspi, RSPI_SPCR, RSPI_SPCR_SPE, 0); +} + +static inline void rzv2h_rspi_spe_enable(const struct rzv2h_rspi_priv *rspi) +{ + rzv2h_rspi_reg_rmw(rspi, RSPI_SPCR, RSPI_SPCR_SPE, 1); +} + +static inline void rzv2h_rspi_clear_fifos(const struct rzv2h_rspi_priv *rspi) +{ + writeb(1, rspi->base + RSPI_SPFCR); +} + +static inline void rzv2h_rspi_clear_all_irqs(struct rzv2h_rspi_priv *rspi) +{ + writew(RSPI_SPSRC_CLEAR, rspi->base + RSPI_SPSRC); + rspi->status = 0; +} + +static irqreturn_t rzv2h_rx_irq_handler(int irq, void *data) +{ + struct rzv2h_rspi_priv *rspi = data; + + rspi->status = readw(rspi->base + RSPI_SPSR); + wake_up(&rspi->wait); + + return IRQ_HANDLED; +} + +static inline int rzv2h_rspi_wait_for_interrupt(struct rzv2h_rspi_priv *rspi, + u32 wait_mask) +{ + return wait_event_timeout(rspi->wait, (rspi->status & wait_mask), + HZ) == 0 ? -ETIMEDOUT : 0; +} + +static void rzv2h_rspi_send(struct rzv2h_rspi_priv *rspi, const void *txbuf, + unsigned int index) +{ + switch (rspi->bytes_per_word) { + case 4: + rzv2h_rspi_tx_u32(rspi, txbuf, index); + break; + case 2: + rzv2h_rspi_tx_u16(rspi, txbuf, index); + break; + default: + rzv2h_rspi_tx_u8(rspi, txbuf, index); + } +} + +static int rzv2h_rspi_receive(struct rzv2h_rspi_priv *rspi, void *rxbuf, + unsigned int index) +{ + int ret; + + ret = rzv2h_rspi_wait_for_interrupt(rspi, RSPI_SPSR_SPRF); + if (ret) + return ret; + + switch (rspi->bytes_per_word) { + case 4: + rzv2h_rspi_rx_u32(rspi, rxbuf, index); + break; + case 2: + rzv2h_rspi_rx_u16(rspi, rxbuf, index); + break; + default: + rzv2h_rspi_rx_u8(rspi, rxbuf, index); + } + + return 0; +} + +static int rzv2h_rspi_transfer_one(struct spi_controller *controller, + struct spi_device *spi, + struct spi_transfer *transfer) +{ + struct rzv2h_rspi_priv *rspi = spi_controller_get_devdata(controller); + unsigned int words_to_transfer, i; + int ret = 0; + + transfer->effective_speed_hz = rspi->freq; + words_to_transfer = transfer->len / rspi->bytes_per_word; + + for (i = 0; i < words_to_transfer; i++) { + rzv2h_rspi_clear_all_irqs(rspi); + + rzv2h_rspi_send(rspi, transfer->tx_buf, i); + + ret = rzv2h_rspi_receive(rspi, transfer->rx_buf, i); + if (ret) + break; + } + + rzv2h_rspi_clear_all_irqs(rspi); + + if (ret) + transfer->error = SPI_TRANS_FAIL_IO; + + spi_finalize_current_transfer(controller); + + return ret; +} + +static inline u32 rzv2h_rspi_calc_bitrate(unsigned long tclk_rate, u8 spr, + u8 brdv) +{ + return DIV_ROUND_UP(tclk_rate, (2 * (spr + 1) * (1 << brdv))); +} + +static u32 rzv2h_rspi_setup_clock(struct rzv2h_rspi_priv *rspi, u32 hz) +{ + unsigned long tclk_rate; + int spr; + u8 brdv; + + /* + * From the manual: + * Bit rate = f(RSPI_n_TCLK)/(2*(n+1)*2^(N)) + * + * Where: + * * RSPI_n_TCLK is fixed to 200MHz on V2H + * * n = SPR - is RSPI_SPBR.SPR (from 0 to 255) + * * N = BRDV - is RSPI_SPCMD.BRDV (from 0 to 3) + */ + tclk_rate = clk_get_rate(rspi->tclk); + for (brdv = RSPI_SPCMD_BRDV_MIN; brdv <= RSPI_SPCMD_BRDV_MAX; brdv++) { + spr = DIV_ROUND_UP(tclk_rate, hz * (1 << (brdv + 1))); + spr--; + if (spr >= RSPI_SPBR_SPR_MIN && spr <= RSPI_SPBR_SPR_MAX) + goto clock_found; + } + + return 0; + +clock_found: + rzv2h_rspi_reg_rmw(rspi, RSPI_SPCMD, RSPI_SPCMD_BRDV, brdv); + writeb(spr, rspi->base + RSPI_SPBR); + + return rzv2h_rspi_calc_bitrate(tclk_rate, spr, brdv); +} + +static int rzv2h_rspi_prepare_message(struct spi_controller *ctlr, + struct spi_message *message) +{ + struct rzv2h_rspi_priv *rspi = spi_controller_get_devdata(ctlr); + const struct spi_device *spi = message->spi; + struct spi_transfer *xfer; + u32 speed_hz = U32_MAX; + u8 bits_per_word; + u32 conf32; + u16 conf16; + + /* Make sure SPCR.SPE is 0 before amending the configuration */ + rzv2h_rspi_spe_disable(rspi); + + /* Configure the device to work in "host" mode */ + conf32 = RSPI_SPCR_MSTR; + + /* Auto-stop function */ + conf32 |= RSPI_SPCR_SCKASE; + + /* SPI receive buffer full interrupt enable */ + conf32 |= RSPI_SPCR_SPRIE; + + writel(conf32, rspi->base + RSPI_SPCR); + + /* Use SPCMD0 only */ + writeb(0x0, rspi->base + RSPI_SPSCR); + + /* Setup mode */ + conf32 = FIELD_PREP(RSPI_SPCMD_CPOL, !!(spi->mode & SPI_CPOL)); + conf32 |= FIELD_PREP(RSPI_SPCMD_CPHA, !!(spi->mode & SPI_CPHA)); + conf32 |= FIELD_PREP(RSPI_SPCMD_LSBF, !!(spi->mode & SPI_LSB_FIRST)); + conf32 |= FIELD_PREP(RSPI_SPCMD_SSLKP, 1); + conf32 |= FIELD_PREP(RSPI_SPCMD_SSLA, spi_get_chipselect(spi, 0)); + writel(conf32, rspi->base + RSPI_SPCMD); + if (spi->mode & SPI_CS_HIGH) + writeb(BIT(spi_get_chipselect(spi, 0)), rspi->base + RSPI_SSLP); + else + writeb(0, rspi->base + RSPI_SSLP); + + /* Setup FIFO thresholds */ + conf16 = FIELD_PREP(RSPI_SPDCR2_TTRG, RSPI_FIFO_SIZE - 1); + conf16 |= FIELD_PREP(RSPI_SPDCR2_RTRG, 0); + writew(conf16, rspi->base + RSPI_SPDCR2); + + rzv2h_rspi_clear_fifos(rspi); + + list_for_each_entry(xfer, &message->transfers, transfer_list) { + if (!xfer->speed_hz) + continue; + + speed_hz = min(xfer->speed_hz, speed_hz); + bits_per_word = xfer->bits_per_word; + } + + if (speed_hz == U32_MAX) + return -EINVAL; + + rspi->bytes_per_word = roundup_pow_of_two(BITS_TO_BYTES(bits_per_word)); + rzv2h_rspi_reg_rmw(rspi, RSPI_SPCMD, RSPI_SPCMD_SPB, bits_per_word - 1); + + rspi->freq = rzv2h_rspi_setup_clock(rspi, speed_hz); + if (!rspi->freq) + return -EINVAL; + + rzv2h_rspi_spe_enable(rspi); + + return 0; +} + +static int rzv2h_rspi_unprepare_message(struct spi_controller *ctlr, + struct spi_message *message) +{ + struct rzv2h_rspi_priv *rspi = spi_controller_get_devdata(ctlr); + + rzv2h_rspi_spe_disable(rspi); + + return 0; +} + +static int rzv2h_rspi_probe(struct platform_device *pdev) +{ + struct spi_controller *controller; + struct device *dev = &pdev->dev; + struct rzv2h_rspi_priv *rspi; + struct clk_bulk_data *clks; + unsigned long tclk_rate; + int irq_rx, ret, i; + + controller = devm_spi_alloc_host(dev, sizeof(*rspi)); + if (!controller) + return -ENOMEM; + + rspi = spi_controller_get_devdata(controller); + platform_set_drvdata(pdev, rspi); + + rspi->controller = controller; + + rspi->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(rspi->base)) + return PTR_ERR(rspi->base); + + ret = devm_clk_bulk_get_all_enabled(dev, &clks); + if (ret != RSPI_CLK_NUM) + return dev_err_probe(dev, ret >= 0 ? -EINVAL : ret, + "cannot get clocks\n"); + for (i = 0; i < RSPI_CLK_NUM; i++) { + if (!strcmp(clks[i].id, "tclk")) { + rspi->tclk = clks[i].clk; + break; + } + } + + if (!rspi->tclk) + return dev_err_probe(dev, -EINVAL, "Failed to get tclk\n"); + + tclk_rate = clk_get_rate(rspi->tclk); + + rspi->resets[0].id = "presetn"; + rspi->resets[1].id = "tresetn"; + ret = devm_reset_control_bulk_get_exclusive(dev, RSPI_RESET_NUM, + rspi->resets); + if (ret) + return dev_err_probe(dev, ret, "cannot get resets\n"); + + irq_rx = platform_get_irq_byname(pdev, "rx"); + if (irq_rx < 0) + return dev_err_probe(dev, irq_rx, "cannot get IRQ 'rx'\n"); + + ret = reset_control_bulk_deassert(RSPI_RESET_NUM, rspi->resets); + if (ret) + return dev_err_probe(dev, ret, "failed to deassert resets\n"); + + init_waitqueue_head(&rspi->wait); + + ret = devm_request_irq(dev, irq_rx, rzv2h_rx_irq_handler, 0, + dev_name(dev), rspi); + if (ret) { + dev_err(dev, "cannot request `rx` IRQ\n"); + goto quit_resets; + } + + controller->mode_bits = SPI_CPHA | SPI_CPOL | SPI_CS_HIGH | + SPI_LSB_FIRST; + controller->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); + controller->prepare_message = rzv2h_rspi_prepare_message; + controller->unprepare_message = rzv2h_rspi_unprepare_message; + controller->num_chipselect = 4; + controller->transfer_one = rzv2h_rspi_transfer_one; + controller->min_speed_hz = rzv2h_rspi_calc_bitrate(tclk_rate, + RSPI_SPBR_SPR_MAX, + RSPI_SPCMD_BRDV_MAX); + controller->max_speed_hz = rzv2h_rspi_calc_bitrate(tclk_rate, + RSPI_SPBR_SPR_MIN, + RSPI_SPCMD_BRDV_MIN); + + device_set_node(&controller->dev, dev_fwnode(dev)); + + ret = spi_register_controller(controller); + if (ret) { + dev_err(dev, "register controller failed\n"); + goto quit_resets; + } + + return 0; + +quit_resets: + reset_control_bulk_assert(RSPI_RESET_NUM, rspi->resets); + + return ret; +} + +static void rzv2h_rspi_remove(struct platform_device *pdev) +{ + struct rzv2h_rspi_priv *rspi = platform_get_drvdata(pdev); + + spi_unregister_controller(rspi->controller); + + reset_control_bulk_assert(RSPI_RESET_NUM, rspi->resets); +} + +static const struct of_device_id rzv2h_rspi_match[] = { + { .compatible = "renesas,r9a09g057-rspi" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, rzv2h_rspi_match); + +static struct platform_driver rzv2h_rspi_drv = { + .probe = rzv2h_rspi_probe, + .remove = rzv2h_rspi_remove, + .driver = { + .name = "rzv2h_rspi", + .of_match_table = rzv2h_rspi_match, + }, +}; +module_platform_driver(rzv2h_rspi_drv); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Fabrizio Castro "); +MODULE_DESCRIPTION("Renesas RZ/V2H(P) Serial Peripheral Interface Driver"); From 87aa3c8d8c4aa2e2567fe04126d14eb9fde815e5 Mon Sep 17 00:00:00 2001 From: Jakub Czapiga Date: Fri, 25 Jul 2025 12:25:42 +0000 Subject: [PATCH 61/62] spi: intel: Allow writeable MTD partition with module param The MTD device is blocked from writing to the SPI-NOR chip if any region of it is write-protected, even if "writeable=1" module parameter is set. Add ability to bypass this behaviour by introducing new module parameter "ignore_protestion_status" which allows to rely on the write protection mechanism of SPI-NOR chip itself, which most modern chips (since the 1990'+) have already implemented. Any erase/write operations performed on the write-protected section will be rejected by the chip. Signed-off-by: Jakub Czapiga Link: https://patch.msgid.link/20250725122542.2633334-1-czapiga@google.com Signed-off-by: Mark Brown --- drivers/spi/spi-intel.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi-intel.c b/drivers/spi/spi-intel.c index 5d5a546c62ea..13bbb2133507 100644 --- a/drivers/spi/spi-intel.c +++ b/drivers/spi/spi-intel.c @@ -189,6 +189,11 @@ struct intel_spi_mem_op { static bool writeable; module_param(writeable, bool, 0); MODULE_PARM_DESC(writeable, "Enable write access to SPI flash chip (default=0)"); +static bool ignore_protection_status; +module_param(ignore_protection_status, bool, 0); +MODULE_PARM_DESC( + ignore_protection_status, + "Do not block SPI flash chip write access even if it is write-protected (default=0)"); static void intel_spi_dump_regs(struct intel_spi *ispi) { @@ -1248,13 +1253,15 @@ static void intel_spi_fill_partition(struct intel_spi *ispi, continue; /* - * If any of the regions have protection bits set, make the - * whole partition read-only to be on the safe side. + * If any of the regions have protection bits set and + * the ignore protection status parameter is not set, + * make the whole partition read-only to be on the safe side. * * Also if the user did not ask the chip to be writeable * mask the bit too. */ - if (!writeable || intel_spi_is_protected(ispi, base, limit)) { + if (!writeable || (!ignore_protection_status && + intel_spi_is_protected(ispi, base, limit))) { part->mask_flags |= MTD_WRITEABLE; ispi->protected = true; } From 2d442a0c781403702de27ccfbc4bb233721585f5 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 25 Jul 2025 18:17:01 +0100 Subject: [PATCH 62/62] spi: SPISG: Fix less than zero comparison on a u32 variable The check for ns < 0 is always false because variable ns is a u32 which is not a signed type. Fix this by making ns a s32 type. Fixes: cef9991e04ae ("spi: Add Amlogic SPISG driver") Signed-off-by: Colin Ian King Link: https://patch.msgid.link/20250725171701.839927-1-colin.i.king@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-amlogic-spisg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-amlogic-spisg.c b/drivers/spi/spi-amlogic-spisg.c index 48a5325b4db7..2ab8bdf2a676 100644 --- a/drivers/spi/spi-amlogic-spisg.c +++ b/drivers/spi/spi-amlogic-spisg.c @@ -163,7 +163,7 @@ struct spisg_device { static int spi_delay_to_sclk(u32 slck_speed_hz, struct spi_delay *delay) { - u32 ns; + s32 ns; if (!delay) return 0;