Add few updates to the STM32 SPI driver

Merge series from Clément Le Goffic <clement.legoffic@foss.st.com>:

This series aims to improve the STM32 SPI driver in different areas.
It adds SPI_READY mode, fixes an issue raised by a kernel bot,
add the ability to use DMA-MDMA chaining for RX and deprecate an ST bindings
vendor property.
This commit is contained in:
Mark Brown
2025-06-25 19:23:44 +01:00
3 changed files with 325 additions and 34 deletions

View File

@@ -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.

View File

@@ -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

View File

@@ -9,7 +9,9 @@
#include <linux/debugfs.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
#include <linux/genalloc.h>
#include <linux/interrupt.h>
#include <linux/iopoll.h>
#include <linux/module.h>
@@ -154,6 +156,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 +227,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 +241,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;
@@ -276,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);
@@ -323,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;
@@ -357,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 = {
@@ -415,6 +433,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,
@@ -878,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);
@@ -1091,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;
}
@@ -1111,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);
}
@@ -1172,15 +1203,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) &
@@ -1410,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);
@@ -1419,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
@@ -1430,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) ||
@@ -1472,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;
@@ -1509,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);
@@ -1520,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);
}
@@ -1702,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)) {
@@ -1768,6 +1961,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,
@@ -1790,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;
@@ -1871,6 +2071,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;
}
@@ -2069,9 +2272,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) {
@@ -2179,7 +2388,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;
@@ -2219,6 +2428,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);
@@ -2246,6 +2482,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);
@@ -2276,6 +2517,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);