From 9a7b8e002e331d0599127f16613c32f425a14f2c Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 9 May 2012 17:09:13 +0200 Subject: dmaengine: add an shdma-base library This patch extracts code from shdma.c, that does not directly deal with hardware implementation details and can be re-used with diverse DMA controller variants, found on SH-based SoCs. Signed-off-by: Guennadi Liakhovetski Cc: Sascha Hauer Signed-off-by: Vinod Koul --- include/linux/shdma-base.h | 123 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 include/linux/shdma-base.h (limited to 'include/linux/shdma-base.h') diff --git a/include/linux/shdma-base.h b/include/linux/shdma-base.h new file mode 100644 index 00000000000..83efd1332b3 --- /dev/null +++ b/include/linux/shdma-base.h @@ -0,0 +1,123 @@ +/* + * Dmaengine driver base library for DMA controllers, found on SH-based SoCs + * + * extracted from shdma.c and headers + * + * Copyright (C) 2011-2012 Guennadi Liakhovetski + * Copyright (C) 2009 Nobuhiro Iwamatsu + * Copyright (C) 2009 Renesas Solutions, Inc. All rights reserved. + * Copyright (C) 2007 Freescale Semiconductor, Inc. All rights reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + */ + +#ifndef SHDMA_BASE_H +#define SHDMA_BASE_H + +#include +#include +#include +#include + +/** + * shdma_pm_state - DMA channel PM state + * SHDMA_PM_ESTABLISHED: either idle or during data transfer + * SHDMA_PM_BUSY: during the transfer preparation, when we have to + * drop the lock temporarily + * SHDMA_PM_PENDING: transfers pending + */ +enum shdma_pm_state { + SHDMA_PM_ESTABLISHED, + SHDMA_PM_BUSY, + SHDMA_PM_PENDING, +}; + +struct device; + +/* + * Drivers, using this library are expected to embed struct shdma_dev, + * struct shdma_chan, struct shdma_desc, and struct shdma_slave + * in their respective device, channel, descriptor and slave objects. + */ + +struct shdma_slave { + unsigned int slave_id; +}; + +struct shdma_desc { + struct list_head node; + struct dma_async_tx_descriptor async_tx; + enum dma_transfer_direction direction; + dma_cookie_t cookie; + int chunks; + int mark; +}; + +struct shdma_chan { + spinlock_t chan_lock; /* Channel operation lock */ + struct list_head ld_queue; /* Link descriptors queue */ + struct list_head ld_free; /* Free link descriptors */ + struct dma_chan dma_chan; /* DMA channel */ + struct device *dev; /* Channel device */ + void *desc; /* buffer for descriptor array */ + int desc_num; /* desc count */ + size_t max_xfer_len; /* max transfer length */ + int id; /* Raw id of this channel */ + int irq; /* Channel IRQ */ + enum shdma_pm_state pm_state; +}; + +/** + * struct shdma_ops - simple DMA driver operations + * desc_completed: return true, if this is the descriptor, that just has + * completed (atomic) + * halt_channel: stop DMA channel operation (atomic) + * channel_busy: return true, if the channel is busy (atomic) + * slave_addr: return slave DMA address + * desc_setup: set up the hardware specific descriptor portion (atomic) + * set_slave: bind channel to a slave + * setup_xfer: configure channel hardware for operation (atomic) + * start_xfer: start the DMA transfer (atomic) + * embedded_desc: return Nth struct shdma_desc pointer from the + * descriptor array + * chan_irq: process channel IRQ, return true if a transfer has + * completed (atomic) + */ +struct shdma_ops { + bool (*desc_completed)(struct shdma_chan *, struct shdma_desc *); + void (*halt_channel)(struct shdma_chan *); + bool (*channel_busy)(struct shdma_chan *); + dma_addr_t (*slave_addr)(struct shdma_chan *); + int (*desc_setup)(struct shdma_chan *, struct shdma_desc *, + dma_addr_t, dma_addr_t, size_t *); + int (*set_slave)(struct shdma_chan *, struct shdma_slave *); + void (*setup_xfer)(struct shdma_chan *, struct shdma_slave *); + void (*start_xfer)(struct shdma_chan *, struct shdma_desc *); + struct shdma_desc *(*embedded_desc)(void *, int); + bool (*chan_irq)(struct shdma_chan *, int); +}; + +struct shdma_dev { + struct dma_device dma_dev; + struct shdma_chan **schan; + const struct shdma_ops *ops; + size_t desc_size; +}; + +#define shdma_for_each_chan(c, d, i) for (i = 0, c = (d)->schan[0]; \ + i < (d)->dma_dev.chancnt; c = (d)->schan[++i]) + +int shdma_request_irq(struct shdma_chan *, int, + unsigned long, const char *); +void shdma_free_irq(struct shdma_chan *); +bool shdma_reset(struct shdma_dev *sdev); +void shdma_chan_probe(struct shdma_dev *sdev, + struct shdma_chan *schan, int id); +void shdma_chan_remove(struct shdma_chan *schan); +int shdma_init(struct device *dev, struct shdma_dev *sdev, + int chan_num); +void shdma_cleanup(struct shdma_dev *sdev); + +#endif -- cgit v1.2.3-70-g09d2 From ecf90fbbdc66cde6f5fa25d88541112b9baac459 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 5 Jul 2012 12:29:40 +0200 Subject: dmaengine: shdma: prepare to stop using struct dma_chan::private Using struct dma_chan::private is deprecated. To update the shdma driver to stop using it we first have to eliminate internal runtime uses of it. After that we will also be able to stop using it for channel configuration. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Vinod Koul --- drivers/dma/sh/shdma-base.c | 9 +++++---- drivers/dma/sh/shdma.c | 24 ++++++++++-------------- drivers/dma/sh/shdma.h | 2 ++ include/linux/sh_dma.h | 2 -- include/linux/shdma-base.h | 1 + 5 files changed, 18 insertions(+), 20 deletions(-) (limited to 'include/linux/shdma-base.h') diff --git a/drivers/dma/sh/shdma-base.c b/drivers/dma/sh/shdma-base.c index ff060d0da90..f75ebfa735c 100644 --- a/drivers/dma/sh/shdma-base.c +++ b/drivers/dma/sh/shdma-base.c @@ -76,7 +76,7 @@ static dma_cookie_t shdma_tx_submit(struct dma_async_tx_descriptor *tx) container_of(tx, struct shdma_desc, async_tx), *last = desc; struct shdma_chan *schan = to_shdma_chan(tx->chan); - struct shdma_slave *slave = tx->chan->private; + struct shdma_slave *slave = schan->slave; dma_async_tx_callback callback = tx->callback; dma_cookie_t cookie; bool power_up; @@ -208,6 +208,7 @@ static int shdma_alloc_chan_resources(struct dma_chan *chan) goto edescalloc; } schan->desc_num = NR_DESCS_PER_CHANNEL; + schan->slave = slave; for (i = 0; i < NR_DESCS_PER_CHANNEL; i++) { desc = ops->embedded_desc(schan->desc, i); @@ -365,9 +366,9 @@ static void shdma_free_chan_resources(struct dma_chan *chan) if (!list_empty(&schan->ld_queue)) shdma_chan_ld_cleanup(schan, true); - if (chan->private) { + if (schan->slave) { /* The caller is holding dma_list_mutex */ - struct shdma_slave *slave = chan->private; + struct shdma_slave *slave = schan->slave; clear_bit(slave->slave_id, shdma_slave_used); chan->private = NULL; } @@ -558,7 +559,7 @@ static struct dma_async_tx_descriptor *shdma_prep_slave_sg( struct shdma_chan *schan = to_shdma_chan(chan); struct shdma_dev *sdev = to_shdma_dev(schan->dma_chan.device); const struct shdma_ops *ops = sdev->ops; - struct shdma_slave *slave = chan->private; + struct shdma_slave *slave = schan->slave; dma_addr_t slave_addr; if (!chan) diff --git a/drivers/dma/sh/shdma.c b/drivers/dma/sh/shdma.c index f06906f281a..9f0a2e507ac 100644 --- a/drivers/dma/sh/shdma.c +++ b/drivers/dma/sh/shdma.c @@ -291,10 +291,8 @@ static void sh_dmae_setup_xfer(struct shdma_chan *schan, shdma_chan); if (sslave) { - struct sh_dmae_slave *slave = container_of(sslave, - struct sh_dmae_slave, shdma_slave); const struct sh_dmae_slave_config *cfg = - slave->config; + sh_chan->config; dmae_set_dmars(sh_chan, cfg->mid_rid); dmae_set_chcr(sh_chan, cfg->chcr); @@ -326,13 +324,11 @@ static int sh_dmae_set_slave(struct shdma_chan *schan, { struct sh_dmae_chan *sh_chan = container_of(schan, struct sh_dmae_chan, shdma_chan); - struct sh_dmae_slave *slave = container_of(sslave, struct sh_dmae_slave, - shdma_slave); const struct sh_dmae_slave_config *cfg = dmae_find_slave(sh_chan, sslave->slave_id); if (!cfg) return -ENODEV; - slave->config = cfg; + sh_chan->config = cfg; return 0; } @@ -579,13 +575,12 @@ static int sh_dmae_resume(struct device *dev) for (i = 0; i < shdev->pdata->channel_num; i++) { struct sh_dmae_chan *sh_chan = shdev->chan[i]; - struct sh_dmae_slave *param = sh_chan->shdma_chan.dma_chan.private; if (!sh_chan->shdma_chan.desc_num) continue; - if (param) { - const struct sh_dmae_slave_config *cfg = param->config; + if (sh_chan->shdma_chan.slave) { + const struct sh_dmae_slave_config *cfg = sh_chan->config; dmae_set_dmars(sh_chan, cfg->mid_rid); dmae_set_chcr(sh_chan, cfg->chcr); } else { @@ -609,14 +604,15 @@ const struct dev_pm_ops sh_dmae_pm = { static dma_addr_t sh_dmae_slave_addr(struct shdma_chan *schan) { - struct sh_dmae_slave *param = schan->dma_chan.private; + struct sh_dmae_chan *sh_chan = container_of(schan, + struct sh_dmae_chan, shdma_chan); /* - * Implicit BUG_ON(!param) - * if (param != NULL), this is a successfully requested slave channel, - * therefore param->config != NULL too. + * Implicit BUG_ON(!sh_chan->config) + * This is an exclusive slave DMA operation, may only be called after a + * successful slave configuration. */ - return param->config->addr; + return sh_chan->config->addr; } static struct shdma_desc *sh_dmae_embedded_desc(void *buf, int i) diff --git a/drivers/dma/sh/shdma.h b/drivers/dma/sh/shdma.h index 840e47d1c86..9314e93225d 100644 --- a/drivers/dma/sh/shdma.h +++ b/drivers/dma/sh/shdma.h @@ -13,6 +13,7 @@ #ifndef __DMA_SHDMA_H #define __DMA_SHDMA_H +#include #include #include #include @@ -25,6 +26,7 @@ struct device; struct sh_dmae_chan { struct shdma_chan shdma_chan; + const struct sh_dmae_slave_config *config; /* Slave DMA configuration */ int xmit_shift; /* log_2(bytes_per_xfer) */ u32 __iomem *base; char dev_id[16]; /* unique name per DMAC of channel */ diff --git a/include/linux/sh_dma.h b/include/linux/sh_dma.h index 7c8ca41e60e..a79f10a3224 100644 --- a/include/linux/sh_dma.h +++ b/include/linux/sh_dma.h @@ -20,8 +20,6 @@ struct device; /* Used by slave DMA clients to request DMA to/from a specific peripheral */ struct sh_dmae_slave { struct shdma_slave shdma_slave; /* Set by the platform */ - struct device *dma_dev; /* Set by the platform */ - const struct sh_dmae_slave_config *config; /* Set by the driver */ }; /* diff --git a/include/linux/shdma-base.h b/include/linux/shdma-base.h index 83efd1332b3..c3a19e9c20c 100644 --- a/include/linux/shdma-base.h +++ b/include/linux/shdma-base.h @@ -66,6 +66,7 @@ struct shdma_chan { size_t max_xfer_len; /* max transfer length */ int id; /* Raw id of this channel */ int irq; /* Channel IRQ */ + struct shdma_slave *slave; /* Client data for slave DMA */ enum shdma_pm_state pm_state; }; -- cgit v1.2.3-70-g09d2 From c2cdb7e4d16394fc51dc5c2c5b3e7c3733bdfaac Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 5 Jul 2012 12:29:41 +0200 Subject: dma: sh: use an integer slave ID to improve API compatibility Initially struct shdma_slave has been introduced with the only member - an unsigned slave ID - to describe common properties of DMA slaves in an extensible way. However, experience shows, that a slave ID is indeed the only parameter, needed to identify DMA slaves. This is also, what is used by the core dmaengine API in struct dma_slave_config. We switch to using the slave_id directly, instead of passing a pointer to struct shdma_slave to improve compatibility with the core. We also make the slave_id signed for easier error checking. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Vinod Koul --- drivers/dma/sh/shdma-base.c | 25 +++++++++++++------------ drivers/dma/sh/shdma.c | 12 ++++++------ include/linux/sh_dma.h | 8 ++++---- include/linux/shdma-base.h | 8 ++++---- 4 files changed, 27 insertions(+), 26 deletions(-) (limited to 'include/linux/shdma-base.h') diff --git a/drivers/dma/sh/shdma-base.c b/drivers/dma/sh/shdma-base.c index f75ebfa735c..73db282a143 100644 --- a/drivers/dma/sh/shdma-base.c +++ b/drivers/dma/sh/shdma-base.c @@ -76,7 +76,6 @@ static dma_cookie_t shdma_tx_submit(struct dma_async_tx_descriptor *tx) container_of(tx, struct shdma_desc, async_tx), *last = desc; struct shdma_chan *schan = to_shdma_chan(tx->chan); - struct shdma_slave *slave = schan->slave; dma_async_tx_callback callback = tx->callback; dma_cookie_t cookie; bool power_up; @@ -138,7 +137,7 @@ static dma_cookie_t shdma_tx_submit(struct dma_async_tx_descriptor *tx) * Make it int then, on error remove chunks from the * queue again */ - ops->setup_xfer(schan, slave); + ops->setup_xfer(schan, schan->slave_id); if (schan->pm_state == SHDMA_PM_PENDING) shdma_chan_xfer_ld_queue(schan); @@ -186,7 +185,7 @@ static int shdma_alloc_chan_resources(struct dma_chan *chan) * never runs concurrently with itself or free_chan_resources. */ if (slave) { - if (slave->slave_id >= slave_num) { + if (slave->slave_id < 0 || slave->slave_id >= slave_num) { ret = -EINVAL; goto evalid; } @@ -196,9 +195,13 @@ static int shdma_alloc_chan_resources(struct dma_chan *chan) goto etestused; } - ret = ops->set_slave(schan, slave); + ret = ops->set_slave(schan, slave->slave_id); if (ret < 0) goto esetslave; + + schan->slave_id = slave->slave_id; + } else { + schan->slave_id = -EINVAL; } schan->desc = kcalloc(NR_DESCS_PER_CHANNEL, @@ -208,7 +211,6 @@ static int shdma_alloc_chan_resources(struct dma_chan *chan) goto edescalloc; } schan->desc_num = NR_DESCS_PER_CHANNEL; - schan->slave = slave; for (i = 0; i < NR_DESCS_PER_CHANNEL; i++) { desc = ops->embedded_desc(schan->desc, i); @@ -366,10 +368,9 @@ static void shdma_free_chan_resources(struct dma_chan *chan) if (!list_empty(&schan->ld_queue)) shdma_chan_ld_cleanup(schan, true); - if (schan->slave) { + if (schan->slave_id >= 0) { /* The caller is holding dma_list_mutex */ - struct shdma_slave *slave = schan->slave; - clear_bit(slave->slave_id, shdma_slave_used); + clear_bit(schan->slave_id, shdma_slave_used); chan->private = NULL; } @@ -559,7 +560,7 @@ static struct dma_async_tx_descriptor *shdma_prep_slave_sg( struct shdma_chan *schan = to_shdma_chan(chan); struct shdma_dev *sdev = to_shdma_dev(schan->dma_chan.device); const struct shdma_ops *ops = sdev->ops; - struct shdma_slave *slave = schan->slave; + int slave_id = schan->slave_id; dma_addr_t slave_addr; if (!chan) @@ -568,9 +569,9 @@ static struct dma_async_tx_descriptor *shdma_prep_slave_sg( BUG_ON(!schan->desc_num); /* Someone calling slave DMA on a generic channel? */ - if (!slave || !sg_len) { - dev_warn(schan->dev, "%s: bad parameter: %p, %d, %d\n", - __func__, slave, sg_len, slave ? slave->slave_id : -1); + if (slave_id < 0 || !sg_len) { + dev_warn(schan->dev, "%s: bad parameter: len=%d, id=%d\n", + __func__, sg_len, slave_id); return NULL; } diff --git a/drivers/dma/sh/shdma.c b/drivers/dma/sh/shdma.c index 9f0a2e507ac..9a10d8bbdef 100644 --- a/drivers/dma/sh/shdma.c +++ b/drivers/dma/sh/shdma.c @@ -285,12 +285,12 @@ static bool sh_dmae_channel_busy(struct shdma_chan *schan) } static void sh_dmae_setup_xfer(struct shdma_chan *schan, - struct shdma_slave *sslave) + int slave_id) { struct sh_dmae_chan *sh_chan = container_of(schan, struct sh_dmae_chan, shdma_chan); - if (sslave) { + if (slave_id >= 0) { const struct sh_dmae_slave_config *cfg = sh_chan->config; @@ -302,7 +302,7 @@ static void sh_dmae_setup_xfer(struct shdma_chan *schan, } static const struct sh_dmae_slave_config *dmae_find_slave( - struct sh_dmae_chan *sh_chan, unsigned int slave_id) + struct sh_dmae_chan *sh_chan, int slave_id) { struct sh_dmae_device *shdev = to_sh_dev(sh_chan); struct sh_dmae_pdata *pdata = shdev->pdata; @@ -320,11 +320,11 @@ static const struct sh_dmae_slave_config *dmae_find_slave( } static int sh_dmae_set_slave(struct shdma_chan *schan, - struct shdma_slave *sslave) + int slave_id) { struct sh_dmae_chan *sh_chan = container_of(schan, struct sh_dmae_chan, shdma_chan); - const struct sh_dmae_slave_config *cfg = dmae_find_slave(sh_chan, sslave->slave_id); + const struct sh_dmae_slave_config *cfg = dmae_find_slave(sh_chan, slave_id); if (!cfg) return -ENODEV; @@ -579,7 +579,7 @@ static int sh_dmae_resume(struct device *dev) if (!sh_chan->shdma_chan.desc_num) continue; - if (sh_chan->shdma_chan.slave) { + if (sh_chan->shdma_chan.slave_id >= 0) { const struct sh_dmae_slave_config *cfg = sh_chan->config; dmae_set_dmars(sh_chan, cfg->mid_rid); dmae_set_chcr(sh_chan, cfg->chcr); diff --git a/include/linux/sh_dma.h b/include/linux/sh_dma.h index a79f10a3224..4e83f3e034f 100644 --- a/include/linux/sh_dma.h +++ b/include/linux/sh_dma.h @@ -27,10 +27,10 @@ struct sh_dmae_slave { * a certain peripheral */ struct sh_dmae_slave_config { - unsigned int slave_id; - dma_addr_t addr; - u32 chcr; - char mid_rid; + int slave_id; + dma_addr_t addr; + u32 chcr; + char mid_rid; }; struct sh_dmae_channel { diff --git a/include/linux/shdma-base.h b/include/linux/shdma-base.h index c3a19e9c20c..6263ad2e742 100644 --- a/include/linux/shdma-base.h +++ b/include/linux/shdma-base.h @@ -43,7 +43,7 @@ struct device; */ struct shdma_slave { - unsigned int slave_id; + int slave_id; }; struct shdma_desc { @@ -66,7 +66,7 @@ struct shdma_chan { size_t max_xfer_len; /* max transfer length */ int id; /* Raw id of this channel */ int irq; /* Channel IRQ */ - struct shdma_slave *slave; /* Client data for slave DMA */ + int slave_id; /* Client ID for slave DMA */ enum shdma_pm_state pm_state; }; @@ -93,8 +93,8 @@ struct shdma_ops { dma_addr_t (*slave_addr)(struct shdma_chan *); int (*desc_setup)(struct shdma_chan *, struct shdma_desc *, dma_addr_t, dma_addr_t, size_t *); - int (*set_slave)(struct shdma_chan *, struct shdma_slave *); - void (*setup_xfer)(struct shdma_chan *, struct shdma_slave *); + int (*set_slave)(struct shdma_chan *, int); + void (*setup_xfer)(struct shdma_chan *, int); void (*start_xfer)(struct shdma_chan *, struct shdma_desc *); struct shdma_desc *(*embedded_desc)(void *, int); bool (*chan_irq)(struct shdma_chan *, int); -- cgit v1.2.3-70-g09d2 From 1ff8df4f5388ad66bd7d0199b5839a2e3345c055 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 5 Jul 2012 12:29:42 +0200 Subject: dma: sh: provide a migration path for slave drivers to stop using .private This patch extends the sh dmaengine driver to support the preferred channel selection and configuration method, instead of using the "private" field from struct dma_chan. We add a standard filter function to be used by slave drivers instead of implementing their own ones, and add support for the DMA_SLAVE_CONFIG control operation, which must accompany the new channel selection method. We still support the legacy .private channel allocation method to cater for a smooth driver migration. Signed-off-by: Guennadi Liakhovetski [applied a trvial checkpath fix] Signed-off-by: Vinod Koul --- drivers/dma/sh/shdma-base.c | 114 ++++++++++++++++++++++++++++++++++---------- drivers/dma/sh/shdma.c | 5 +- include/linux/sh_dma.h | 2 + include/linux/shdma-base.h | 2 +- 4 files changed, 95 insertions(+), 28 deletions(-) (limited to 'include/linux/shdma-base.h') diff --git a/drivers/dma/sh/shdma-base.c b/drivers/dma/sh/shdma-base.c index 73db282a143..27f5c781fd7 100644 --- a/drivers/dma/sh/shdma-base.c +++ b/drivers/dma/sh/shdma-base.c @@ -171,6 +171,65 @@ static struct shdma_desc *shdma_get_desc(struct shdma_chan *schan) return NULL; } +static int shdma_setup_slave(struct shdma_chan *schan, int slave_id) +{ + struct shdma_dev *sdev = to_shdma_dev(schan->dma_chan.device); + const struct shdma_ops *ops = sdev->ops; + int ret; + + if (slave_id < 0 || slave_id >= slave_num) + return -EINVAL; + + if (test_and_set_bit(slave_id, shdma_slave_used)) + return -EBUSY; + + ret = ops->set_slave(schan, slave_id, false); + if (ret < 0) { + clear_bit(slave_id, shdma_slave_used); + return ret; + } + + schan->slave_id = slave_id; + + return 0; +} + +/* + * This is the standard shdma filter function to be used as a replacement to the + * "old" method, using the .private pointer. If for some reason you allocate a + * channel without slave data, use something like ERR_PTR(-EINVAL) as a filter + * parameter. If this filter is used, the slave driver, after calling + * dma_request_channel(), will also have to call dmaengine_slave_config() with + * .slave_id, .direction, and either .src_addr or .dst_addr set. + * NOTE: this filter doesn't support multiple DMAC drivers with the DMA_SLAVE + * capability! If this becomes a requirement, hardware glue drivers, using this + * services would have to provide their own filters, which first would check + * the device driver, similar to how other DMAC drivers, e.g., sa11x0-dma.c, do + * this, and only then, in case of a match, call this common filter. + */ +bool shdma_chan_filter(struct dma_chan *chan, void *arg) +{ + struct shdma_chan *schan = to_shdma_chan(chan); + struct shdma_dev *sdev = to_shdma_dev(schan->dma_chan.device); + const struct shdma_ops *ops = sdev->ops; + int slave_id = (int)arg; + int ret; + + if (slave_id < 0) + /* No slave requested - arbitrary channel */ + return true; + + if (slave_id >= slave_num) + return false; + + ret = ops->set_slave(schan, slave_id, true); + if (ret < 0) + return false; + + return true; +} +EXPORT_SYMBOL(shdma_chan_filter); + static int shdma_alloc_chan_resources(struct dma_chan *chan) { struct shdma_chan *schan = to_shdma_chan(chan); @@ -185,21 +244,10 @@ static int shdma_alloc_chan_resources(struct dma_chan *chan) * never runs concurrently with itself or free_chan_resources. */ if (slave) { - if (slave->slave_id < 0 || slave->slave_id >= slave_num) { - ret = -EINVAL; - goto evalid; - } - - if (test_and_set_bit(slave->slave_id, shdma_slave_used)) { - ret = -EBUSY; - goto etestused; - } - - ret = ops->set_slave(schan, slave->slave_id); + /* Legacy mode: .private is set in filter */ + ret = shdma_setup_slave(schan, slave->slave_id); if (ret < 0) goto esetslave; - - schan->slave_id = slave->slave_id; } else { schan->slave_id = -EINVAL; } @@ -228,8 +276,6 @@ edescalloc: if (slave) esetslave: clear_bit(slave->slave_id, shdma_slave_used); -etestused: -evalid: chan->private = NULL; return ret; } @@ -587,22 +633,40 @@ static int shdma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, struct shdma_chan *schan = to_shdma_chan(chan); struct shdma_dev *sdev = to_shdma_dev(chan->device); const struct shdma_ops *ops = sdev->ops; + struct dma_slave_config *config; unsigned long flags; - - /* Only supports DMA_TERMINATE_ALL */ - if (cmd != DMA_TERMINATE_ALL) - return -ENXIO; + int ret; if (!chan) return -EINVAL; - spin_lock_irqsave(&schan->chan_lock, flags); - - ops->halt_channel(schan); - - spin_unlock_irqrestore(&schan->chan_lock, flags); + switch (cmd) { + case DMA_TERMINATE_ALL: + spin_lock_irqsave(&schan->chan_lock, flags); + ops->halt_channel(schan); + spin_unlock_irqrestore(&schan->chan_lock, flags); - shdma_chan_ld_cleanup(schan, true); + shdma_chan_ld_cleanup(schan, true); + break; + case DMA_SLAVE_CONFIG: + /* + * So far only .slave_id is used, but the slave drivers are + * encouraged to also set a transfer direction and an address. + */ + if (!arg) + return -EINVAL; + /* + * We could lock this, but you shouldn't be configuring the + * channel, while using it... + */ + config = (struct dma_slave_config *)arg; + ret = shdma_setup_slave(schan, config->slave_id); + if (ret < 0) + return ret; + break; + default: + return -ENXIO; + } return 0; } diff --git a/drivers/dma/sh/shdma.c b/drivers/dma/sh/shdma.c index 9a10d8bbdef..027c9be9765 100644 --- a/drivers/dma/sh/shdma.c +++ b/drivers/dma/sh/shdma.c @@ -320,7 +320,7 @@ static const struct sh_dmae_slave_config *dmae_find_slave( } static int sh_dmae_set_slave(struct shdma_chan *schan, - int slave_id) + int slave_id, bool try) { struct sh_dmae_chan *sh_chan = container_of(schan, struct sh_dmae_chan, shdma_chan); @@ -328,7 +328,8 @@ static int sh_dmae_set_slave(struct shdma_chan *schan, if (!cfg) return -ENODEV; - sh_chan->config = cfg; + if (!try) + sh_chan->config = cfg; return 0; } diff --git a/include/linux/sh_dma.h b/include/linux/sh_dma.h index 4e83f3e034f..b64d6bec6f9 100644 --- a/include/linux/sh_dma.h +++ b/include/linux/sh_dma.h @@ -99,4 +99,6 @@ struct sh_dmae_pdata { #define CHCR_TE 0x00000002 #define CHCR_IE 0x00000004 +bool shdma_chan_filter(struct dma_chan *chan, void *arg); + #endif diff --git a/include/linux/shdma-base.h b/include/linux/shdma-base.h index 6263ad2e742..93f9821554b 100644 --- a/include/linux/shdma-base.h +++ b/include/linux/shdma-base.h @@ -93,7 +93,7 @@ struct shdma_ops { dma_addr_t (*slave_addr)(struct shdma_chan *); int (*desc_setup)(struct shdma_chan *, struct shdma_desc *, dma_addr_t, dma_addr_t, size_t *); - int (*set_slave)(struct shdma_chan *, int); + int (*set_slave)(struct shdma_chan *, int, bool); void (*setup_xfer)(struct shdma_chan *, int); void (*start_xfer)(struct shdma_chan *, struct shdma_desc *); struct shdma_desc *(*embedded_desc)(void *, int); -- cgit v1.2.3-70-g09d2