return 0;
}
-static void dma_exit(struct device *dev, struct lpi2c_imx_dma *dma)
-{
- if (dma->chan_rx)
- dma_release_channel(dma->chan_rx);
-
- if (dma->chan_tx)
- dma_release_channel(dma->chan_tx);
-
- devm_kfree(dev, dma);
-}
-
static int lpi2c_dma_init(struct device *dev, dma_addr_t phy_addr)
{
struct lpi2c_imx_struct *lpi2c_imx = dev_get_drvdata(dev);
struct lpi2c_imx_dma *dma;
+ void *group;
int ret;
- dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL);
- if (!dma)
+ /*
+ * Open a devres group so that all resources allocated within
+ * this function can be released together if DMA init fails but
+ * probe continues in PIO mode.
+ */
+ group = devres_open_group(dev, NULL, GFP_KERNEL);
+ if (!group)
return -ENOMEM;
+ dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL);
+ if (!dma) {
+ ret = -ENOMEM;
+ goto release_group;
+ }
+
dma->phy_addr = phy_addr;
/* Prepare for TX DMA: */
- dma->chan_tx = dma_request_chan(dev, "tx");
+ dma->chan_tx = devm_dma_request_chan(dev, "tx");
if (IS_ERR(dma->chan_tx)) {
ret = PTR_ERR(dma->chan_tx);
if (ret != -ENODEV && ret != -EPROBE_DEFER)
dev_err(dev, "can't request DMA tx channel (%d)\n", ret);
- dma->chan_tx = NULL;
- goto dma_exit;
+ goto release_group;
}
/* Prepare for RX DMA: */
- dma->chan_rx = dma_request_chan(dev, "rx");
+ dma->chan_rx = devm_dma_request_chan(dev, "rx");
if (IS_ERR(dma->chan_rx)) {
ret = PTR_ERR(dma->chan_rx);
if (ret != -ENODEV && ret != -EPROBE_DEFER)
dev_err(dev, "can't request DMA rx channel (%d)\n", ret);
- dma->chan_rx = NULL;
- goto dma_exit;
+ goto release_group;
}
+ /*
+ * DMA init succeeded. Remove the group marker but keep all resources
+ * bound to the device, they will be freed at device removal.
+ */
+ devres_remove_group(dev, group);
+
lpi2c_imx->can_use_dma = true;
lpi2c_imx->dma = dma;
return 0;
-dma_exit:
- dma_exit(dev, dma);
+release_group:
+ /*
+ * DMA init failed. Release ALL resources allocated inside this
+ * group (dma memory, TX channel if already acquired, etc.) so
+ * that a successful PIO-mode probe does not hold unused resources
+ * for the entire device lifetime.
+ */
+ devres_release_group(dev, group);
return ret;
}