From 9137c1bfb960c183b41beeb067aae10317da30ca Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Tue, 16 Jun 2026 12:48:50 +0100 Subject: [PATCH 1/2] misc: rp1-pio: Ensure transfers are configured Fail transfers if they haven't first been configured. Signed-off-by: Phil Elwell --- drivers/misc/rp1-pio.c | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/drivers/misc/rp1-pio.c b/drivers/misc/rp1-pio.c index f848e5f9d84323..9c8bd4a2bf426a 100644 --- a/drivers/misc/rp1-pio.c +++ b/drivers/misc/rp1-pio.c @@ -613,7 +613,7 @@ static int rp1_pio_sm_config_xfer_internal(struct rp1_pio_client *client, uint s struct dma_slave_config config = {}; struct dma_slave_caps dma_caps; phys_addr_t fifo_addr; - struct dma_info *dma; + struct dma_info *dma = NULL; uint32_t dma_mask; char chan_name[4]; int ret = 0; @@ -627,14 +627,17 @@ static int rp1_pio_sm_config_xfer_internal(struct rp1_pio_client *client, uint s dma_mask = 1 << (sm * 2 + dir); - dma = &pio->dma_configs[sm][dir]; - spin_lock(&pio->lock); - if (pio->claimed_dmas & dma_mask) - rp1_pio_sm_dma_free(dev, dma); - pio->claimed_dmas |= dma_mask; - client->claimed_dmas |= dma_mask; + if (!(pio->claimed_dmas & dma_mask & ~client->claimed_dmas)) { + dma = &pio->dma_configs[sm][dir]; + if (client->claimed_dmas & dma_mask) + rp1_pio_sm_dma_free(dev, dma); + pio->claimed_dmas |= dma_mask; + client->claimed_dmas |= dma_mask; + } spin_unlock(&pio->lock); + if (!dma) + return -EBUSY; dma->buf_size = buf_size; /* Round up the allocations */ @@ -873,13 +876,20 @@ static int rp1_pio_sm_xfer_data32_user(struct rp1_pio_client *client, void *para { struct rp1_pio_sm_xfer_data32_args *args = param; struct rp1_pio_device *pio = client->pio; - struct dma_info *dma; + struct dma_info *dma = NULL; + uint32_t dma_mask; if (args->sm >= RP1_PIO_SMS_COUNT || args->dir >= RP1_PIO_DIR_COUNT || !args->data_bytes || !args->data) return -EINVAL; - dma = &pio->dma_configs[args->sm][args->dir]; + dma_mask = 1 << (args->sm * 2 + args->dir); + spin_lock(&pio->lock); + if (client->claimed_dmas & dma_mask) + dma = &pio->dma_configs[args->sm][args->dir]; + spin_unlock(&pio->lock); + if (!dma) + return -EINVAL; if (args->dir == RP1_PIO_DIR_TO_SM) return rp1_pio_sm_tx_user(pio, dma, args->data, args->data_bytes); From 7c84b834c7807f00b320dcb912249e4e2c9f758d Mon Sep 17 00:00:00 2001 From: Nicolai Buchwitz Date: Tue, 21 Jul 2026 19:51:03 +0200 Subject: [PATCH 2/2] misc: rp1-pio: release DMA channel outside pio->lock rp1_pio_sm_dma_free() ends in dma_release_channel(), which sleeps while removing the sysfs links. Running it under pio->lock in SM_CONFIG_XFER triggers "BUG: scheduling while atomic" and hangs the machine when a client keeps reconfiguring a transfer. The claim bit stays set for this client across the unlock, so the channel remains reserved against other clients while it is released. Fixes: 0b7a604d73f3 ("misc: Add RP1 PIO driver") Link: https://github.com/raspberrypi/linux/issues/7512 Signed-off-by: Nicolai Buchwitz --- drivers/misc/rp1-pio.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/misc/rp1-pio.c b/drivers/misc/rp1-pio.c index 9c8bd4a2bf426a..c590465123dd56 100644 --- a/drivers/misc/rp1-pio.c +++ b/drivers/misc/rp1-pio.c @@ -609,11 +609,12 @@ static int rp1_pio_sm_config_xfer_internal(struct rp1_pio_client *client, uint s struct rp1_pio_sm_set_dmactrl_args set_dmactrl_args; struct rp1_pio_device *pio = client->pio; struct platform_device *pdev = pio->pdev; - struct device *dev = &pdev->dev; struct dma_slave_config config = {}; + struct device *dev = &pdev->dev; struct dma_slave_caps dma_caps; - phys_addr_t fifo_addr; struct dma_info *dma = NULL; + bool reconfigure = false; + phys_addr_t fifo_addr; uint32_t dma_mask; char chan_name[4]; int ret = 0; @@ -631,7 +632,7 @@ static int rp1_pio_sm_config_xfer_internal(struct rp1_pio_client *client, uint s if (!(pio->claimed_dmas & dma_mask & ~client->claimed_dmas)) { dma = &pio->dma_configs[sm][dir]; if (client->claimed_dmas & dma_mask) - rp1_pio_sm_dma_free(dev, dma); + reconfigure = true; pio->claimed_dmas |= dma_mask; client->claimed_dmas |= dma_mask; } @@ -639,6 +640,10 @@ static int rp1_pio_sm_config_xfer_internal(struct rp1_pio_client *client, uint s if (!dma) return -EBUSY; + /* dma_release_channel() sleeps, so free the old channel outside the lock. */ + if (reconfigure) + rp1_pio_sm_dma_free(dev, dma); + dma->buf_size = buf_size; /* Round up the allocations */ buf_size = ROUND_UP(buf_size, PAGE_SIZE);