|
@@ -2179,17 +2179,78 @@ static void d40_issue_pending(struct dma_chan *chan)
|
|
|
spin_unlock_irqrestore(&d40c->lock, flags);
|
|
|
}
|
|
|
|
|
|
+static int
|
|
|
+dma40_config_to_halfchannel(struct d40_chan *d40c,
|
|
|
+ struct stedma40_half_channel_info *info,
|
|
|
+ enum dma_slave_buswidth width,
|
|
|
+ u32 maxburst)
|
|
|
+{
|
|
|
+ enum stedma40_periph_data_width addr_width;
|
|
|
+ int psize;
|
|
|
+
|
|
|
+ switch (width) {
|
|
|
+ case DMA_SLAVE_BUSWIDTH_1_BYTE:
|
|
|
+ addr_width = STEDMA40_BYTE_WIDTH;
|
|
|
+ break;
|
|
|
+ case DMA_SLAVE_BUSWIDTH_2_BYTES:
|
|
|
+ addr_width = STEDMA40_HALFWORD_WIDTH;
|
|
|
+ break;
|
|
|
+ case DMA_SLAVE_BUSWIDTH_4_BYTES:
|
|
|
+ addr_width = STEDMA40_WORD_WIDTH;
|
|
|
+ break;
|
|
|
+ case DMA_SLAVE_BUSWIDTH_8_BYTES:
|
|
|
+ addr_width = STEDMA40_DOUBLEWORD_WIDTH;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ dev_err(d40c->base->dev,
|
|
|
+ "illegal peripheral address width "
|
|
|
+ "requested (%d)\n",
|
|
|
+ width);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (chan_is_logical(d40c)) {
|
|
|
+ if (maxburst >= 16)
|
|
|
+ psize = STEDMA40_PSIZE_LOG_16;
|
|
|
+ else if (maxburst >= 8)
|
|
|
+ psize = STEDMA40_PSIZE_LOG_8;
|
|
|
+ else if (maxburst >= 4)
|
|
|
+ psize = STEDMA40_PSIZE_LOG_4;
|
|
|
+ else
|
|
|
+ psize = STEDMA40_PSIZE_LOG_1;
|
|
|
+ } else {
|
|
|
+ if (maxburst >= 16)
|
|
|
+ psize = STEDMA40_PSIZE_PHY_16;
|
|
|
+ else if (maxburst >= 8)
|
|
|
+ psize = STEDMA40_PSIZE_PHY_8;
|
|
|
+ else if (maxburst >= 4)
|
|
|
+ psize = STEDMA40_PSIZE_PHY_4;
|
|
|
+ else
|
|
|
+ psize = STEDMA40_PSIZE_PHY_1;
|
|
|
+ }
|
|
|
+
|
|
|
+ info->data_width = addr_width;
|
|
|
+ info->psize = psize;
|
|
|
+ info->flow_ctrl = STEDMA40_NO_FLOW_CTRL;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
/* Runtime reconfiguration extension */
|
|
|
-static void d40_set_runtime_config(struct dma_chan *chan,
|
|
|
- struct dma_slave_config *config)
|
|
|
+static int d40_set_runtime_config(struct dma_chan *chan,
|
|
|
+ struct dma_slave_config *config)
|
|
|
{
|
|
|
struct d40_chan *d40c = container_of(chan, struct d40_chan, chan);
|
|
|
struct stedma40_chan_cfg *cfg = &d40c->dma_cfg;
|
|
|
- enum dma_slave_buswidth config_addr_width;
|
|
|
+ enum dma_slave_buswidth src_addr_width, dst_addr_width;
|
|
|
dma_addr_t config_addr;
|
|
|
- u32 config_maxburst;
|
|
|
- enum stedma40_periph_data_width addr_width;
|
|
|
- int psize;
|
|
|
+ u32 src_maxburst, dst_maxburst;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ src_addr_width = config->src_addr_width;
|
|
|
+ src_maxburst = config->src_maxburst;
|
|
|
+ dst_addr_width = config->dst_addr_width;
|
|
|
+ dst_maxburst = config->dst_maxburst;
|
|
|
|
|
|
if (config->direction == DMA_FROM_DEVICE) {
|
|
|
dma_addr_t dev_addr_rx =
|
|
@@ -2208,8 +2269,11 @@ static void d40_set_runtime_config(struct dma_chan *chan,
|
|
|
cfg->dir);
|
|
|
cfg->dir = STEDMA40_PERIPH_TO_MEM;
|
|
|
|
|
|
- config_addr_width = config->src_addr_width;
|
|
|
- config_maxburst = config->src_maxburst;
|
|
|
+ /* Configure the memory side */
|
|
|
+ if (dst_addr_width == DMA_SLAVE_BUSWIDTH_UNDEFINED)
|
|
|
+ dst_addr_width = src_addr_width;
|
|
|
+ if (dst_maxburst == 0)
|
|
|
+ dst_maxburst = src_maxburst;
|
|
|
|
|
|
} else if (config->direction == DMA_TO_DEVICE) {
|
|
|
dma_addr_t dev_addr_tx =
|
|
@@ -2228,68 +2292,39 @@ static void d40_set_runtime_config(struct dma_chan *chan,
|
|
|
cfg->dir);
|
|
|
cfg->dir = STEDMA40_MEM_TO_PERIPH;
|
|
|
|
|
|
- config_addr_width = config->dst_addr_width;
|
|
|
- config_maxburst = config->dst_maxburst;
|
|
|
-
|
|
|
+ /* Configure the memory side */
|
|
|
+ if (src_addr_width == DMA_SLAVE_BUSWIDTH_UNDEFINED)
|
|
|
+ src_addr_width = dst_addr_width;
|
|
|
+ if (src_maxburst == 0)
|
|
|
+ src_maxburst = dst_maxburst;
|
|
|
} else {
|
|
|
dev_err(d40c->base->dev,
|
|
|
"unrecognized channel direction %d\n",
|
|
|
config->direction);
|
|
|
- return;
|
|
|
+ return -EINVAL;
|
|
|
}
|
|
|
|
|
|
- switch (config_addr_width) {
|
|
|
- case DMA_SLAVE_BUSWIDTH_1_BYTE:
|
|
|
- addr_width = STEDMA40_BYTE_WIDTH;
|
|
|
- break;
|
|
|
- case DMA_SLAVE_BUSWIDTH_2_BYTES:
|
|
|
- addr_width = STEDMA40_HALFWORD_WIDTH;
|
|
|
- break;
|
|
|
- case DMA_SLAVE_BUSWIDTH_4_BYTES:
|
|
|
- addr_width = STEDMA40_WORD_WIDTH;
|
|
|
- break;
|
|
|
- case DMA_SLAVE_BUSWIDTH_8_BYTES:
|
|
|
- addr_width = STEDMA40_DOUBLEWORD_WIDTH;
|
|
|
- break;
|
|
|
- default:
|
|
|
+ if (src_maxburst * src_addr_width != dst_maxburst * dst_addr_width) {
|
|
|
dev_err(d40c->base->dev,
|
|
|
- "illegal peripheral address width "
|
|
|
- "requested (%d)\n",
|
|
|
- config->src_addr_width);
|
|
|
- return;
|
|
|
+ "src/dst width/maxburst mismatch: %d*%d != %d*%d\n",
|
|
|
+ src_maxburst,
|
|
|
+ src_addr_width,
|
|
|
+ dst_maxburst,
|
|
|
+ dst_addr_width);
|
|
|
+ return -EINVAL;
|
|
|
}
|
|
|
|
|
|
- if (chan_is_logical(d40c)) {
|
|
|
- if (config_maxburst >= 16)
|
|
|
- psize = STEDMA40_PSIZE_LOG_16;
|
|
|
- else if (config_maxburst >= 8)
|
|
|
- psize = STEDMA40_PSIZE_LOG_8;
|
|
|
- else if (config_maxburst >= 4)
|
|
|
- psize = STEDMA40_PSIZE_LOG_4;
|
|
|
- else
|
|
|
- psize = STEDMA40_PSIZE_LOG_1;
|
|
|
- } else {
|
|
|
- if (config_maxburst >= 16)
|
|
|
- psize = STEDMA40_PSIZE_PHY_16;
|
|
|
- else if (config_maxburst >= 8)
|
|
|
- psize = STEDMA40_PSIZE_PHY_8;
|
|
|
- else if (config_maxburst >= 4)
|
|
|
- psize = STEDMA40_PSIZE_PHY_4;
|
|
|
- else if (config_maxburst >= 2)
|
|
|
- psize = STEDMA40_PSIZE_PHY_2;
|
|
|
- else
|
|
|
- psize = STEDMA40_PSIZE_PHY_1;
|
|
|
- }
|
|
|
+ ret = dma40_config_to_halfchannel(d40c, &cfg->src_info,
|
|
|
+ src_addr_width,
|
|
|
+ src_maxburst);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
|
|
|
- /* Set up all the endpoint configs */
|
|
|
- cfg->src_info.data_width = addr_width;
|
|
|
- cfg->src_info.psize = psize;
|
|
|
- cfg->src_info.big_endian = false;
|
|
|
- cfg->src_info.flow_ctrl = STEDMA40_NO_FLOW_CTRL;
|
|
|
- cfg->dst_info.data_width = addr_width;
|
|
|
- cfg->dst_info.psize = psize;
|
|
|
- cfg->dst_info.big_endian = false;
|
|
|
- cfg->dst_info.flow_ctrl = STEDMA40_NO_FLOW_CTRL;
|
|
|
+ ret = dma40_config_to_halfchannel(d40c, &cfg->dst_info,
|
|
|
+ dst_addr_width,
|
|
|
+ dst_maxburst);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
|
|
|
/* Fill in register values */
|
|
|
if (chan_is_logical(d40c))
|
|
@@ -2302,12 +2337,14 @@ static void d40_set_runtime_config(struct dma_chan *chan,
|
|
|
d40c->runtime_addr = config_addr;
|
|
|
d40c->runtime_direction = config->direction;
|
|
|
dev_dbg(d40c->base->dev,
|
|
|
- "configured channel %s for %s, data width %d, "
|
|
|
- "maxburst %d bytes, LE, no flow control\n",
|
|
|
+ "configured channel %s for %s, data width %d/%d, "
|
|
|
+ "maxburst %d/%d elements, LE, no flow control\n",
|
|
|
dma_chan_name(chan),
|
|
|
(config->direction == DMA_FROM_DEVICE) ? "RX" : "TX",
|
|
|
- config_addr_width,
|
|
|
- config_maxburst);
|
|
|
+ src_addr_width, dst_addr_width,
|
|
|
+ src_maxburst, dst_maxburst);
|
|
|
+
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
static int d40_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
|
|
@@ -2328,9 +2365,8 @@ static int d40_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
|
|
|
case DMA_RESUME:
|
|
|
return d40_resume(d40c);
|
|
|
case DMA_SLAVE_CONFIG:
|
|
|
- d40_set_runtime_config(chan,
|
|
|
+ return d40_set_runtime_config(chan,
|
|
|
(struct dma_slave_config *) arg);
|
|
|
- return 0;
|
|
|
default:
|
|
|
break;
|
|
|
}
|