dmaengine: add slave sg transfer capabilities api
Subsystems (2)
v3 cover dmaengine: add slave sg transfer capabilities api
Changes since v2:
- Change to a separate slave sg specific api. Drop the generic per-channel capabilities api that is not used.
Changes since v1:
- Use the existing dma_transaction_type enums instead of adding the mostly duplicated dmaengine_apis enums
This series adds a new dmaengine api, dma_get_slave_sg_caps(), which may be used by a client driver to get slave SG transfer capabilities for a particular channel. At this time, these include the max number of segments and max length of a segment that a channel can handle for a SG transfer.
Along with the API implementation, this series implements the backend device_slave_sg_caps() in the EDMA DMA Engine driver and converts the davinci_mmc driver to use dma_get_slave_sg_caps() to replace hardcoded limits.
This is tested on the AM1808-EVM.
Matt Porter (3):
dmaengine: add dma_get_slave_sg_caps()
dma: edma: add device_slave_sg_caps() support
mmc: davinci: get SG segment limits with dma_get_slave_sg_caps()
drivers/dma/edma.c | 17 ++++++++++++ drivers/mmc/host/davinci_mmc.c | 37 ++++++++------------------ include/linux/dmaengine.h | 40 +++++++++++++++++++++++++++++ include/linux/platform_data/mmc-davinci.h | 3 --- 4 files changed, 67 insertions(+), 30 deletions(-)
Signature · 1 line
1.7.9.5
PATCH v3 1/3 dmaengine: add dma_get_slave_sg_caps()
Add a dmaengine API to retrieve slave SG transfer capabilities.
The API is optionally implemented by dmaengine drivers and when unimplemented will return a NULL pointer. A client driver using this API provides the required dma channel, address width, and burst size of the transfer. dma_get_slave_sg_caps() returns an SG caps structure with the maximum number and size of SG segments that the given channel can handle.
--- include/linux/dmaengine.h | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+)
include/linux/dmaengine.h +40 -0
@@ -371,6 +371,19 @@ struct dma_slave_config {unsigned int slave_id; }; +/* struct dma_slave_sg_caps - expose SG transfer capability of a +* channel. +* +* @max_seg_nr: maximum number of SG segments supported on a SG/SLAVE +* channel (0 for no maximum or not a SG/SLAVE channel) +* @max_seg_len: maximum length of SG segments supported on a SG/SLAVE +* channel (0 for no maximum or not a SG/SLAVE channel) +*/ +struct dma_slave_sg_caps { +u32 max_seg_nr; +u32 max_seg_len; +}; +static inline const char *dma_chan_name (struct dma_chan *chan) {return dev_name (&chan->dev->device);
@@ -534,6 +547,7 @@ struct dma_tx_state {* struct with auxiliary transfer status information, otherwise the call * will just return a simple status code * @device_issue_pending: push pending transactions to hardware +* @device_slave_sg_caps: return the slave SG capabilities */ struct dma_device {
@@ -602,6 +616,9 @@ struct dma_device {dma_cookie_t cookie, struct dma_tx_state *txstate);void (*device_issue_pending)(struct dma_chan *chan); +struct dma_slave_sg_caps *(*device_slave_sg_caps)( +struct dma_chan *chan,enum dma_slave_buswidth addr_width, +u32 maxburst); }; static inline int dmaengine_device_control(struct dma_chan *chan,
@@ -969,6 +986,29 @@ dma_set_tx_state(struct dma_tx_state *st, dma_cookie_t last, dma_cookie_t used, } } +/** +* dma_get_slave_sg_caps - get DMAC SG transfer capabilities +* @chan: target DMA channel +* @addr_width: address width of the DMA transfer +* @maxburst: maximum DMA transfer burst size +* +* Get SG transfer capabilities for a specified channel. If the dmaengine +* driver does not implement SG transfer capabilities then NULL is +* returned. +*/ +static inline struct dma_slave_sg_caps +*dma_get_slave_sg_caps (struct dma_chan *chan, +enum dma_slave_buswidth addr_width, +u32 maxburst) +{ +if (chan->device->device_slave_sg_caps) +return chan->device->device_slave_sg_caps (chan, + addr_width, + maxburst); + +return NULL ; +} +enum dma_status dma_sync_wait (struct dma_chan *chan,dma_cookie_t cookie);#ifdef CONFIG_DMA_ENGINE enum dma_status dma_wait_for_async_tx (struct dma_async_tx_descriptor *tx);
Signature · 1 line
1.7.9.5
Re: [PATCH v3 1/3] dmaengine: add dma_get_slave_sg_caps()
Quoted 8 lines · Matt Porter
Add a dmaengine API to retrieve slave SG transfer capabilities.
The API is optionally implemented by dmaengine drivers and when unimplemented will return a NULL pointer. A client driver using this API provides the required dma channel, address width, and burst size of the transfer.
dma_get_slave_sg_caps()returns an SG caps structure with the maximum number and size of SG segments that the given channel can handle.
Okay this sounds much better :-)
few points though:
- you added API for caps, but is actually calculating for given configuration the max allowed range. IMHO that is not caps, perhaps renaming to get_max_sg /some_better_name would be more apt.
- Still I like the idea of caps, but it should give H/W support capablity. If you want to add that, pls develop on same line...
--
~Vinod
Quoted 81 lines
Signed-off-by Matt Porter <[email protected]>--- include/linux/dmaengine.h | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+)include/linux/dmaengine.h +40
-0@@ -371,6 +371,19 @@ struct dma_slave_config {unsigned int slave_id; }; +/* struct dma_slave_sg_caps - expose SG transfer capability of a +* channel. +* +* @max_seg_nr: maximum number of SG segments supported on a SG/SLAVE +* channel (0 for no maximum or not a SG/SLAVE channel) +* @max_seg_len: maximum length of SG segments supported on a SG/SLAVE +* channel (0 for no maximum or not a SG/SLAVE channel) +*/ +struct dma_slave_sg_caps { +u32 max_seg_nr; +u32 max_seg_len; +}; +static inline const char *dma_chan_name (struct dma_chan *chan) {return dev_name (&chan->dev->device);@@ -534,6 +547,7 @@ struct dma_tx_state {* struct with auxiliary transfer status information, otherwise the call * will just return a simple status code * @device_issue_pending: push pending transactions to hardware +* @device_slave_sg_caps: return the slave SG capabilities */ struct dma_device {@@ -602,6 +616,9 @@ struct dma_device {dma_cookie_t cookie, struct dma_tx_state *txstate);void (*device_issue_pending)(struct dma_chan *chan); +struct dma_slave_sg_caps *(*device_slave_sg_caps)( +struct dma_chan *chan,enum dma_slave_buswidth addr_width, +u32 maxburst); }; static inline int dmaengine_device_control(struct dma_chan *chan,@@ -969,6 +986,29 @@ dma_set_tx_state(struct dma_tx_state *st, dma_cookie_t last, dma_cookie_t used, } } +/** +* dma_get_slave_sg_caps - get DMAC SG transfer capabilities +* @chan: target DMA channel +* @addr_width: address width of the DMA transfer +* @maxburst: maximum DMA transfer burst size +* +* Get SG transfer capabilities for a specified channel. If the dmaengine +* driver does not implement SG transfer capabilities then NULL is +* returned. +*/ +static inline struct dma_slave_sg_caps +*dma_get_slave_sg_caps (struct dma_chan *chan, +enum dma_slave_buswidth addr_width, +u32 maxburst) +{ +if (chan->device->device_slave_sg_caps) +return chan->device->device_slave_sg_caps (chan, + addr_width, + maxburst); + +return NULL ; +} +enum dma_status dma_sync_wait (struct dma_chan *chan,dma_cookie_t cookie);#ifdef CONFIG_DMA_ENGINE enum dma_status dma_wait_for_async_tx (struct dma_async_tx_descriptor *tx);Signature · 1 line
1.7.9.5
A message that is not in the archive (sent off-list or not yet received): [email protected]
Re: [PATCH v3 1/3] dmaengine: add dma_get_slave_sg_caps()
Quoted 9 lines · Vinod Koul
Quoted 8 lines · Matt Porter
Add a dmaengine API to retrieve slave SG transfer capabilities.
The API is optionally implemented by dmaengine drivers and when unimplemented will return a NULL pointer. A client driver using this API provides the required dma channel, address width, and burst size of the transfer.
dma_get_slave_sg_caps()returns an SG caps structure with the maximum number and size of SG segments that the given channel can handle.
Quoted 6 lines
Okay this sounds much better :-)
few points though:
- you added API for caps, but is actually calculating for given configuration the max allowed range. IMHO that is not caps, perhaps renaming toget_max_sg/some_better_name would be more apt.
I went with get_slave_sg_limits(), seemed pretty descriptive. Just posted v4 with that change.
Quoted 2 lines
- Still I like the idea of caps, but it should give H/W support capablity. If you want to add that, pls develop on same line...
Ok, seems like a good separate submission.
-Matt
PATCH v3 2/3 dma: edma: add device_slave_sg_caps() support
In reply to Matt PorterImplement device_slave_sg_caps().
EDMA has a finite set of PaRAM slots available for linking a multi-segment SG transfer. In order to prevent any one channel from consuming all PaRAM slots to fulfill a large SG transfer, the driver reports a static per-channel max number of SG segments it will handle.
The maximum size of an SG segment is limited by the addr_width and maxburst of a given transfer request. These values are provided by the client driver and used to calculate and return the maximum segment length.
--- drivers/dma/edma.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
drivers/dma/edma.c +17 -0
@@ -72,6 +72,7 @@ struct edma_chan {dma_addr_t addr;int addr_width;int maxburst; +struct dma_slave_sg_caps sg_caps; };struct edma_cc {
@@ -463,6 +464,20 @@ static void edma_issue_pending(struct dma_chan *chan)spin_unlock_irqrestore (&echan->vchan.lock, flags); } +static struct dma_slave_sg_caps +*edma_get_slave_sg_caps (struct dma_chan *chan, +enum dma_slave_buswidth addr_width, +u32 maxburst) +{ +struct edma_chan *echan; + + echan =to_edma_chan (chan); + echan->sg_caps.max_seg_len = + (SZ_64K -1 ) * addr_width * maxburst; + +return &echan->sg_caps; +} +static size_t edma_desc_size (struct edma_desc *edesc) {int i;
@@ -522,6 +537,7 @@ static void __init edma_chan_init(struct edma_cc *ecc, echan->ch_num =EDMA_CTLR_CHAN (ecc->ctlr, i); echan->ecc = ecc; echan->vchan.desc_free = edma_desc_free; + echan->sg_caps.max_seg_nr =MAX_NR_SG ;vchan_init (&echan->vchan, dma);
@@ -538,6 +554,7 @@ static void edma_dma_init(struct edma_cc *ecc, struct dma_device *dma, dma->device_alloc_chan_resources = edma_alloc_chan_resources; dma->device_free_chan_resources = edma_free_chan_resources; dma->device_issue_pending = edma_issue_pending; + dma->device_slave_sg_caps = edma_get_slave_sg_caps; dma->device_tx_status = edma_tx_status; dma->device_control = edma_control; dma->dev = dev;
Signature · 1 line
1.7.9.5
PATCH v3 3/3 mmc: davinci: get SG segment limits with dma_get_slave_sg_caps()
In reply to Matt PorterReplace the hardcoded values used to set max_segs/max_seg_size with a dma_get_slave_sg_caps() query to the dmaengine driver.
--- drivers/mmc/host/davinci_mmc.c | 37 ++++++++--------------------- include/linux/platform_data/mmc-davinci.h | 3 --- 2 files changed, 10 insertions(+), 30 deletions(-)
drivers/mmc/host/davinci_mmc.c +10 -27
@@ -144,18 +144,6 @@/* MMCSD Init clock in Hz in opendrain mode */ #define MMCSD_INIT_CLOCK 200000 -/* -* One scatterlist dma "segment" is at most MAX_CCNT rw_threshold units, -* and we handle up to MAX_NR_SG segments. MMC_BLOCK_BOUNCE kicks in only -* for drivers with max_segs == 1, making the segments bigger (64KB) -* than the page or two that's otherwise typical. nr_sg (passed from -* platform data) == 16 gives at least the same throughput boost, using -* EDMA transfer linkage instead of spending CPU time copying pages. -*/ -#define MAX_CCNT ((1 << 16) - 1) - -#define MAX_NR_SG 16 -static unsigned rw_threshold =32 ;module_param (rw_threshold, uint,S_IRUGO ); MODULE_PARM_DESC(rw_threshold,
@@ -216,8 +204,6 @@ struct mmc_davinci_host {u8 version;/* for ns in one cycle calculation */ unsigned ns_in_one_cycle; -/* Number of sg segments */ -u8 nr_sg;#ifdef CONFIG_CPU_FREQ struct notifier_block freq_transition;#endif
@@ -1165,6 +1151,7 @@ static int __init davinci_mmcsd_probe(struct platform_device *pdev)struct resource *r, *mem =NULL ;int ret =0 , irq =0 ;size_t mem_size; +struct dma_slave_sg_caps *dma_sg_caps;/* REVISIT: when we're fully converted, fail if pdata is NULL */
@@ -1214,12 +1201,6 @@ static int __init davinci_mmcsd_probe(struct platform_device *pdev)init_mmcsd_host (host); -if (pdata->nr_sg) - host->nr_sg = pdata->nr_sg -1 ; - -if (host->nr_sg >MAX_NR_SG || !host->nr_sg) - host->nr_sg =MAX_NR_SG ; - host->use_dma = use_dma; host->mmc_irq = irq; host->sdio_irq =platform_get_irq (pdev,1 );
@@ -1248,14 +1229,16 @@ static int __init davinci_mmcsd_probe(struct platform_device *pdev) mmc->caps |= pdata->caps; mmc->ocr_avail =MMC_VDD_32_33 |MMC_VDD_33_34 ; -/* With no iommu coalescing pages, each phys_seg is a hw_seg. -* Each hw_seg uses one EDMA parameter RAM slot, always one -* channel and then usually some linked slots. -*/ - mmc->max_segs =MAX_NR_SG ; +/* Just check one channel for the DMA SG limits */ + dma_sg_caps =dma_get_slave_sg_caps ( + host->dma_tx, +DMA_SLAVE_BUSWIDTH_4_BYTES , + rw_threshold /DMA_SLAVE_BUSWIDTH_4_BYTES ); -/* EDMA limit per hw segment (one or two MBytes) */ - mmc->max_seg_size =MAX_CCNT * rw_threshold; +if (dma_sg_caps) { + mmc->max_segs = dma_sg_caps->max_seg_nr; + mmc->max_seg_size = dma_sg_caps->max_seg_len; + }/* MMC/SD controller limits for multiblock requests */ mmc->max_blk_size =4095 ;/* BLEN is 12 bits */
include/linux/platform_data/mmc-davinci.h +0 -3
@@ -25,9 +25,6 @@ struct davinci_mmc_config {/* Version of the MMC/SD controller */ u8 version; - -/* Number of sg segments */ -u8 nr_sg; };void davinci_setup_mmc (int module,struct davinci_mmc_config *config);
Signature · 1 line
1.7.9.5