This series makes edma use configuration

information available within the IP instead
 of reading it from platform data or DT. Some
 other useful clean-ups are included too.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1.4.10 (GNU/Linux)
 
 iQIcBAABAgAGBQJTgGMBAAoJEGFBu2jqvgRN/FAP/2fl2dcX8aYTVY+H7y0r4I65
 IpUGqOPktztUI0UgeDtlt1p+B4SBfOIGTP8Zx6PaDD6TQGM2GLv4HFS5yfpVF9vb
 OMbCanwHz0GED6UKBbUJQEhaebrTv/M5jdsNrwJ9qMrB7zP7zr1bTDj3G6QJD4+D
 n8yUu+HPXD9wtAh263aRPxRFeNfBhJIJtBF3b4HV52KJ0pCPmrwvqpFIahl63AsF
 Lrdbt200ZeHFVzkIANN0SSjlUDF9towTCo2ok1VaF0U7JJ1J3WMOJpT/zcrzyYsK
 A8Ts8znb/ml7JF94z4Th/dE714QyQcfX/u76skOPRsZUZ9BcQDEX0jNpP3Elgh47
 hw8yYj+BJRoCs7tE6o4WpIlCVLgRQglZeyUkTbl4hOAAmvIffoj9MSRk1LDJ/WaC
 s3PbT/93KgCOgP/iY0WsSlOPJGGQPHNEB+0O90qHL/e6fh9Kqt3dX1Sf9fj/ZFQ5
 x66qTJkDk3SkRzyWFIUyROlEH2toamHKHhQ5v1LUxSPJCekUl3PTUS3GiZtS+PmS
 85NSJWwq1Hv9xowKqVS3kkH6gLPkmTTB0uec138RQ/AMHp7SA9DCkShvheyvt1wM
 LaPuAmWBci4cALzFo1aeEUEa6ij5Waew0Nm5r9L3kJvMqBCjdLG16DnzXUShFgOq
 THYpsGPUdvdUi+JPdps0
 =AWLc
 -----END PGP SIGNATURE-----

Merge tag 'davinci-for-v3.16/edma' of git://git.kernel.org/pub/scm/linux/kernel/git/nsekhar/linux-davinci into next/drivers

Merge "DaVinci EDMA clean-up for v3.16" from Sekhar Nori:

This series makes edma use configuration information available within
the IP instead of reading it from platform data or DT. Some other useful
clean-ups are included too.

* tag 'davinci-for-v3.16/edma' of git://git.kernel.org/pub/scm/linux/kernel/git/nsekhar/linux-davinci: (34 commits)
  ARM: edma: Remove redundant/unused parameters from edma_soc_info
  ARM: davinci: Remove redundant/unused parameters for edma
  ARM: dts: am4372: Remove obsolete properties from edma node
  ARM: dts: am33xx: Remove obsolete properties from edma node
  dt/bindings: ti,edma: Remove redundant properties from documentation
  ARM: edma: Get IP configuration from HW (number of channels, tc, etc)
  ARM: edma: Save number of regions from pdata to struct edma
  ARM: edma: Remove num_cc member from struct edma
  ARM: edma: Remove queue_tc_mapping data from edma_soc_info
  ARM: davinci: Remove eDMA3 queue_tc_mapping data from edma_soc_info
  ARM: edma: Do not change TC -> Queue mapping, leave it to default.
  ARM: edma: Take the number of tc from edma_soc_info (pdata)
  ARM: edma: No need to clean the pdata in edma_of_parse_dt()
  ARM: edma: Clean up and simplify the code around irq request
  dmaengine: edma: update DMA memcpy to use new param element
  dmaengine: edma: Document variables used for residue accounting
  dmaengine: edma: Provide granular accounting
  dmaengine: edma: Make reading the position of active channels work
  dmaengine: edma: Store transfer data in edma_desc and edma_pset
  dmaengine: edma: Create private pset struct
  ...

Signed-off-by: Olof Johansson <olof@lixom.net>
This commit is contained in:
Olof Johansson 2014-05-26 14:59:05 -07:00
commit 3a5e23cf9e
11 changed files with 371 additions and 295 deletions

View File

@ -2,11 +2,8 @@ TI EDMA
Required properties:
- compatible : "ti,edma3"
- ti,edma-regions: Number of regions
- ti,edma-slots: Number of slots
- #dma-cells: Should be set to <1>
Clients should use a single channel number per DMA request.
- dma-channels: Specify total DMA channels per CC
- reg: Memory map for accessing module
- interrupt-parent: Interrupt controller the interrupt is routed through
- interrupts: Exactly 3 interrupts need to be specified in the order:
@ -17,6 +14,13 @@ Optional properties:
- ti,hwmods: Name of the hwmods associated to the EDMA
- ti,edma-xbar-event-map: Crossbar event to channel map
Deprecated properties:
Listed here in case one wants to boot an old kernel with new DTB. These
properties might need to be added to the new DTS files.
- ti,edma-regions: Number of regions
- ti,edma-slots: Number of slots
- dma-channels: Specify total DMA channels per CC
Example:
edma: edma@49000000 {
@ -26,9 +30,6 @@ edma: edma@49000000 {
compatible = "ti,edma3";
ti,hwmods = "tpcc", "tptc0", "tptc1", "tptc2";
#dma-cells = <1>;
dma-channels = <64>;
ti,edma-regions = <4>;
ti,edma-slots = <256>;
ti,edma-xbar-event-map = /bits/ 16 <1 12
2 13>;
};

View File

@ -147,9 +147,6 @@
<0x44e10f90 0x40>;
interrupts = <12 13 14>;
#dma-cells = <1>;
dma-channels = <64>;
ti,edma-regions = <4>;
ti,edma-slots = <256>;
};
gpio0: gpio@44e07000 {

View File

@ -108,9 +108,6 @@
<GIC_SPI 13 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 14 IRQ_TYPE_LEVEL_HIGH>;
#dma-cells = <1>;
dma-channels = <64>;
ti,edma-regions = <4>;
ti,edma-slots = <256>;
};
uart0: serial@44e09000 {

View File

@ -102,7 +102,13 @@
#define PARM_OFFSET(param_no) (EDMA_PARM + ((param_no) << 5))
#define EDMA_DCHMAP 0x0100 /* 64 registers */
#define CHMAP_EXIST BIT(24)
/* CCCFG register */
#define GET_NUM_DMACH(x) (x & 0x7) /* bits 0-2 */
#define GET_NUM_PAENTRY(x) ((x & 0x7000) >> 12) /* bits 12-14 */
#define GET_NUM_EVQUE(x) ((x & 0x70000) >> 16) /* bits 16-18 */
#define GET_NUM_REGN(x) ((x & 0x300000) >> 20) /* bits 20-21 */
#define CHMAP_EXIST BIT(24)
#define EDMA_MAX_DMACH 64
#define EDMA_MAX_PARAMENTRY 512
@ -233,7 +239,6 @@ struct edma {
unsigned num_region;
unsigned num_slots;
unsigned num_tc;
unsigned num_cc;
enum dma_event_q default_queue;
/* list of channels with no even trigger; terminated by "-1" */
@ -290,12 +295,6 @@ static void map_dmach_queue(unsigned ctlr, unsigned ch_no,
~(0x7 << bit), queue_no << bit);
}
static void __init map_queue_tc(unsigned ctlr, int queue_no, int tc_no)
{
int bit = queue_no * 4;
edma_modify(ctlr, EDMA_QUETCMAP, ~(0x7 << bit), ((tc_no & 0x7) << bit));
}
static void __init assign_priority_to_queue(unsigned ctlr, int queue_no,
int priority)
{
@ -994,29 +993,23 @@ void edma_set_dest(unsigned slot, dma_addr_t dest_port,
EXPORT_SYMBOL(edma_set_dest);
/**
* edma_get_position - returns the current transfer points
* edma_get_position - returns the current transfer point
* @slot: parameter RAM slot being examined
* @src: pointer to source port position
* @dst: pointer to destination port position
* @dst: true selects the dest position, false the source
*
* Returns current source and destination addresses for a particular
* parameter RAM slot. Its channel should not be active when this is called.
* Returns the position of the current active slot
*/
void edma_get_position(unsigned slot, dma_addr_t *src, dma_addr_t *dst)
dma_addr_t edma_get_position(unsigned slot, bool dst)
{
struct edmacc_param temp;
unsigned ctlr;
u32 offs, ctlr = EDMA_CTLR(slot);
ctlr = EDMA_CTLR(slot);
slot = EDMA_CHAN_SLOT(slot);
edma_read_slot(EDMA_CTLR_CHAN(ctlr, slot), &temp);
if (src != NULL)
*src = temp.src;
if (dst != NULL)
*dst = temp.dst;
offs = PARM_OFFSET(slot);
offs += dst ? PARM_DST : PARM_SRC;
return edma_read(ctlr, offs);
}
EXPORT_SYMBOL(edma_get_position);
/**
* edma_set_src_index - configure DMA source address indexing
@ -1421,6 +1414,67 @@ void edma_clear_event(unsigned channel)
}
EXPORT_SYMBOL(edma_clear_event);
static int edma_setup_from_hw(struct device *dev, struct edma_soc_info *pdata,
struct edma *edma_cc)
{
int i;
u32 value, cccfg;
s8 (*queue_priority_map)[2];
/* Decode the eDMA3 configuration from CCCFG register */
cccfg = edma_read(0, EDMA_CCCFG);
value = GET_NUM_REGN(cccfg);
edma_cc->num_region = BIT(value);
value = GET_NUM_DMACH(cccfg);
edma_cc->num_channels = BIT(value + 1);
value = GET_NUM_PAENTRY(cccfg);
edma_cc->num_slots = BIT(value + 4);
value = GET_NUM_EVQUE(cccfg);
edma_cc->num_tc = value + 1;
dev_dbg(dev, "eDMA3 HW configuration (cccfg: 0x%08x):\n", cccfg);
dev_dbg(dev, "num_region: %u\n", edma_cc->num_region);
dev_dbg(dev, "num_channel: %u\n", edma_cc->num_channels);
dev_dbg(dev, "num_slot: %u\n", edma_cc->num_slots);
dev_dbg(dev, "num_tc: %u\n", edma_cc->num_tc);
/* Nothing need to be done if queue priority is provided */
if (pdata->queue_priority_mapping)
return 0;
/*
* Configure TC/queue priority as follows:
* Q0 - priority 0
* Q1 - priority 1
* Q2 - priority 2
* ...
* The meaning of priority numbers: 0 highest priority, 7 lowest
* priority. So Q0 is the highest priority queue and the last queue has
* the lowest priority.
*/
queue_priority_map = devm_kzalloc(dev,
(edma_cc->num_tc + 1) * sizeof(s8),
GFP_KERNEL);
if (!queue_priority_map)
return -ENOMEM;
for (i = 0; i < edma_cc->num_tc; i++) {
queue_priority_map[i][0] = i;
queue_priority_map[i][1] = i;
}
queue_priority_map[i][0] = -1;
queue_priority_map[i][1] = -1;
pdata->queue_priority_mapping = queue_priority_map;
pdata->default_queue = 0;
return 0;
}
#if IS_ENABLED(CONFIG_OF) && IS_ENABLED(CONFIG_DMADEVICES)
static int edma_xbar_event_map(struct device *dev, struct device_node *node,
@ -1471,65 +1525,16 @@ static int edma_of_parse_dt(struct device *dev,
struct device_node *node,
struct edma_soc_info *pdata)
{
int ret = 0, i;
u32 value;
int ret = 0;
struct property *prop;
size_t sz;
struct edma_rsv_info *rsv_info;
s8 (*queue_tc_map)[2], (*queue_priority_map)[2];
memset(pdata, 0, sizeof(struct edma_soc_info));
ret = of_property_read_u32(node, "dma-channels", &value);
if (ret < 0)
return ret;
pdata->n_channel = value;
ret = of_property_read_u32(node, "ti,edma-regions", &value);
if (ret < 0)
return ret;
pdata->n_region = value;
ret = of_property_read_u32(node, "ti,edma-slots", &value);
if (ret < 0)
return ret;
pdata->n_slot = value;
pdata->n_cc = 1;
rsv_info = devm_kzalloc(dev, sizeof(struct edma_rsv_info), GFP_KERNEL);
if (!rsv_info)
return -ENOMEM;
pdata->rsv = rsv_info;
queue_tc_map = devm_kzalloc(dev, 8*sizeof(s8), GFP_KERNEL);
if (!queue_tc_map)
return -ENOMEM;
for (i = 0; i < 3; i++) {
queue_tc_map[i][0] = i;
queue_tc_map[i][1] = i;
}
queue_tc_map[i][0] = -1;
queue_tc_map[i][1] = -1;
pdata->queue_tc_mapping = queue_tc_map;
queue_priority_map = devm_kzalloc(dev, 8*sizeof(s8), GFP_KERNEL);
if (!queue_priority_map)
return -ENOMEM;
for (i = 0; i < 3; i++) {
queue_priority_map[i][0] = i;
queue_priority_map[i][1] = i;
}
queue_priority_map[i][0] = -1;
queue_priority_map[i][1] = -1;
pdata->queue_priority_mapping = queue_priority_map;
pdata->default_queue = 0;
prop = of_find_property(node, "ti,edma-xbar-event-map", &sz);
if (prop)
ret = edma_xbar_event_map(dev, node, pdata, sz);
@ -1556,6 +1561,7 @@ static struct edma_soc_info *edma_setup_info_from_dt(struct device *dev,
return ERR_PTR(ret);
dma_cap_set(DMA_SLAVE, edma_filter_info.dma_cap);
dma_cap_set(DMA_CYCLIC, edma_filter_info.dma_cap);
of_dma_controller_register(dev->of_node, of_dma_simple_xlate,
&edma_filter_info);
@ -1574,7 +1580,6 @@ static int edma_probe(struct platform_device *pdev)
struct edma_soc_info **info = pdev->dev.platform_data;
struct edma_soc_info *ninfo[EDMA_MAX_CC] = {NULL};
s8 (*queue_priority_mapping)[2];
s8 (*queue_tc_mapping)[2];
int i, j, off, ln, found = 0;
int status = -1;
const s16 (*rsv_chans)[2];
@ -1585,7 +1590,6 @@ static int edma_probe(struct platform_device *pdev)
struct resource *r[EDMA_MAX_CC] = {NULL};
struct resource res[EDMA_MAX_CC];
char res_name[10];
char irq_name[10];
struct device_node *node = pdev->dev.of_node;
struct device *dev = &pdev->dev;
int ret;
@ -1650,12 +1654,10 @@ static int edma_probe(struct platform_device *pdev)
if (!edma_cc[j])
return -ENOMEM;
edma_cc[j]->num_channels = min_t(unsigned, info[j]->n_channel,
EDMA_MAX_DMACH);
edma_cc[j]->num_slots = min_t(unsigned, info[j]->n_slot,
EDMA_MAX_PARAMENTRY);
edma_cc[j]->num_cc = min_t(unsigned, info[j]->n_cc,
EDMA_MAX_CC);
/* Get eDMA3 configuration from IP */
ret = edma_setup_from_hw(dev, info[j], edma_cc[j]);
if (ret)
return ret;
edma_cc[j]->default_queue = info[j]->default_queue;
@ -1707,14 +1709,21 @@ static int edma_probe(struct platform_device *pdev)
if (node) {
irq[j] = irq_of_parse_and_map(node, 0);
err_irq[j] = irq_of_parse_and_map(node, 2);
} else {
char irq_name[10];
sprintf(irq_name, "edma%d", j);
irq[j] = platform_get_irq_byname(pdev, irq_name);
sprintf(irq_name, "edma%d_err", j);
err_irq[j] = platform_get_irq_byname(pdev, irq_name);
}
edma_cc[j]->irq_res_start = irq[j];
status = devm_request_irq(&pdev->dev, irq[j],
dma_irq_handler, 0, "edma",
&pdev->dev);
edma_cc[j]->irq_res_end = err_irq[j];
status = devm_request_irq(dev, irq[j], dma_irq_handler, 0,
"edma", dev);
if (status < 0) {
dev_dbg(&pdev->dev,
"devm_request_irq %d failed --> %d\n",
@ -1722,16 +1731,8 @@ static int edma_probe(struct platform_device *pdev)
return status;
}
if (node) {
err_irq[j] = irq_of_parse_and_map(node, 2);
} else {
sprintf(irq_name, "edma%d_err", j);
err_irq[j] = platform_get_irq_byname(pdev, irq_name);
}
edma_cc[j]->irq_res_end = err_irq[j];
status = devm_request_irq(&pdev->dev, err_irq[j],
dma_ccerr_handler, 0,
"edma_error", &pdev->dev);
status = devm_request_irq(dev, err_irq[j], dma_ccerr_handler, 0,
"edma_error", dev);
if (status < 0) {
dev_dbg(&pdev->dev,
"devm_request_irq %d failed --> %d\n",
@ -1742,14 +1743,8 @@ static int edma_probe(struct platform_device *pdev)
for (i = 0; i < edma_cc[j]->num_channels; i++)
map_dmach_queue(j, i, info[j]->default_queue);
queue_tc_mapping = info[j]->queue_tc_mapping;
queue_priority_mapping = info[j]->queue_priority_mapping;
/* Event queue to TC mapping */
for (i = 0; queue_tc_mapping[i][0] != -1; i++)
map_queue_tc(j, queue_tc_mapping[i][0],
queue_tc_mapping[i][1]);
/* Event queue priority mapping */
for (i = 0; queue_priority_mapping[i][0] != -1; i++)
assign_priority_to_queue(j,
@ -1762,7 +1757,7 @@ static int edma_probe(struct platform_device *pdev)
if (edma_read(j, EDMA_CCCFG) & CHMAP_EXIST)
map_dmach_param(j);
for (i = 0; i < info[j]->n_region; i++) {
for (i = 0; i < edma_cc[j]->num_region; i++) {
edma_write_array2(j, EDMA_DRAE, i, 0, 0x0);
edma_write_array2(j, EDMA_DRAE, i, 1, 0x0);
edma_write_array(j, EDMA_QRAE, i, 0x0);

View File

@ -134,13 +134,6 @@ struct platform_device da8xx_serial_device[] = {
}
};
static s8 da8xx_queue_tc_mapping[][2] = {
/* {event queue no, TC no} */
{0, 0},
{1, 1},
{-1, -1}
};
static s8 da8xx_queue_priority_mapping[][2] = {
/* {event queue no, Priority} */
{0, 3},
@ -148,12 +141,6 @@ static s8 da8xx_queue_priority_mapping[][2] = {
{-1, -1}
};
static s8 da850_queue_tc_mapping[][2] = {
/* {event queue no, TC no} */
{0, 0},
{-1, -1}
};
static s8 da850_queue_priority_mapping[][2] = {
/* {event queue no, Priority} */
{0, 3},
@ -161,12 +148,6 @@ static s8 da850_queue_priority_mapping[][2] = {
};
static struct edma_soc_info da830_edma_cc0_info = {
.n_channel = 32,
.n_region = 4,
.n_slot = 128,
.n_tc = 2,
.n_cc = 1,
.queue_tc_mapping = da8xx_queue_tc_mapping,
.queue_priority_mapping = da8xx_queue_priority_mapping,
.default_queue = EVENTQ_1,
};
@ -177,22 +158,10 @@ static struct edma_soc_info *da830_edma_info[EDMA_MAX_CC] = {
static struct edma_soc_info da850_edma_cc_info[] = {
{
.n_channel = 32,
.n_region = 4,
.n_slot = 128,
.n_tc = 2,
.n_cc = 1,
.queue_tc_mapping = da8xx_queue_tc_mapping,
.queue_priority_mapping = da8xx_queue_priority_mapping,
.default_queue = EVENTQ_1,
},
{
.n_channel = 32,
.n_region = 4,
.n_slot = 128,
.n_tc = 1,
.n_cc = 1,
.queue_tc_mapping = da850_queue_tc_mapping,
.queue_priority_mapping = da850_queue_priority_mapping,
.default_queue = EVENTQ_0,
},

View File

@ -568,14 +568,6 @@ static u8 dm355_default_priorities[DAVINCI_N_AINTC_IRQ] = {
/*----------------------------------------------------------------------*/
static s8
queue_tc_mapping[][2] = {
/* {event queue no, TC no} */
{0, 0},
{1, 1},
{-1, -1},
};
static s8
queue_priority_mapping[][2] = {
/* {event queue no, Priority} */
@ -585,12 +577,6 @@ queue_priority_mapping[][2] = {
};
static struct edma_soc_info edma_cc0_info = {
.n_channel = 64,
.n_region = 4,
.n_slot = 128,
.n_tc = 2,
.n_cc = 1,
.queue_tc_mapping = queue_tc_mapping,
.queue_priority_mapping = queue_priority_mapping,
.default_queue = EVENTQ_1,
};

View File

@ -852,16 +852,6 @@ static u8 dm365_default_priorities[DAVINCI_N_AINTC_IRQ] = {
};
/* Four Transfer Controllers on DM365 */
static s8
dm365_queue_tc_mapping[][2] = {
/* {event queue no, TC no} */
{0, 0},
{1, 1},
{2, 2},
{3, 3},
{-1, -1},
};
static s8
dm365_queue_priority_mapping[][2] = {
/* {event queue no, Priority} */
@ -873,12 +863,6 @@ dm365_queue_priority_mapping[][2] = {
};
static struct edma_soc_info edma_cc0_info = {
.n_channel = 64,
.n_region = 4,
.n_slot = 256,
.n_tc = 4,
.n_cc = 1,
.queue_tc_mapping = dm365_queue_tc_mapping,
.queue_priority_mapping = dm365_queue_priority_mapping,
.default_queue = EVENTQ_3,
};

View File

@ -498,14 +498,6 @@ static u8 dm644x_default_priorities[DAVINCI_N_AINTC_IRQ] = {
/*----------------------------------------------------------------------*/
static s8
queue_tc_mapping[][2] = {
/* {event queue no, TC no} */
{0, 0},
{1, 1},
{-1, -1},
};
static s8
queue_priority_mapping[][2] = {
/* {event queue no, Priority} */
@ -515,12 +507,6 @@ queue_priority_mapping[][2] = {
};
static struct edma_soc_info edma_cc0_info = {
.n_channel = 64,
.n_region = 4,
.n_slot = 128,
.n_tc = 2,
.n_cc = 1,
.queue_tc_mapping = queue_tc_mapping,
.queue_priority_mapping = queue_priority_mapping,
.default_queue = EVENTQ_1,
};

View File

@ -532,16 +532,6 @@ static u8 dm646x_default_priorities[DAVINCI_N_AINTC_IRQ] = {
/*----------------------------------------------------------------------*/
/* Four Transfer Controllers on DM646x */
static s8
dm646x_queue_tc_mapping[][2] = {
/* {event queue no, TC no} */
{0, 0},
{1, 1},
{2, 2},
{3, 3},
{-1, -1},
};
static s8
dm646x_queue_priority_mapping[][2] = {
/* {event queue no, Priority} */
@ -553,12 +543,6 @@ dm646x_queue_priority_mapping[][2] = {
};
static struct edma_soc_info edma_cc0_info = {
.n_channel = 64,
.n_region = 6, /* 0-1, 4-7 */
.n_slot = 512,
.n_tc = 4,
.n_cc = 1,
.queue_tc_mapping = dm646x_queue_tc_mapping,
.queue_priority_mapping = dm646x_queue_priority_mapping,
.default_queue = EVENTQ_1,
};

View File

@ -57,14 +57,48 @@
#define EDMA_MAX_SLOTS MAX_NR_SG
#define EDMA_DESCRIPTORS 16
struct edma_pset {
u32 len;
dma_addr_t addr;
struct edmacc_param param;
};
struct edma_desc {
struct virt_dma_desc vdesc;
struct list_head node;
enum dma_transfer_direction direction;
int cyclic;
int absync;
int pset_nr;
struct edma_chan *echan;
int processed;
struct edmacc_param pset[0];
/*
* The following 4 elements are used for residue accounting.
*
* - processed_stat: the number of SG elements we have traversed
* so far to cover accounting. This is updated directly to processed
* during edma_callback and is always <= processed, because processed
* refers to the number of pending transfer (programmed to EDMA
* controller), where as processed_stat tracks number of transfers
* accounted for so far.
*
* - residue: The amount of bytes we have left to transfer for this desc
*
* - residue_stat: The residue in bytes of data we have covered
* so far for accounting. This is updated directly to residue
* during callbacks to keep it current.
*
* - sg_len: Tracks the length of the current intermediate transfer,
* this is required to update the residue during intermediate transfer
* completion callback.
*/
int processed_stat;
u32 sg_len;
u32 residue;
u32 residue_stat;
struct edma_pset pset[0];
};
struct edma_cc;
@ -136,12 +170,14 @@ static void edma_execute(struct edma_chan *echan)
/* Find out how many left */
left = edesc->pset_nr - edesc->processed;
nslots = min(MAX_NR_SG, left);
edesc->sg_len = 0;
/* Write descriptor PaRAM set(s) */
for (i = 0; i < nslots; i++) {
j = i + edesc->processed;
edma_write_slot(echan->slot[i], &edesc->pset[j]);
dev_dbg(echan->vchan.chan.device->dev,
edma_write_slot(echan->slot[i], &edesc->pset[j].param);
edesc->sg_len += edesc->pset[j].len;
dev_vdbg(echan->vchan.chan.device->dev,
"\n pset[%d]:\n"
" chnum\t%d\n"
" slot\t%d\n"
@ -154,14 +190,14 @@ static void edma_execute(struct edma_chan *echan)
" cidx\t%08x\n"
" lkrld\t%08x\n",
j, echan->ch_num, echan->slot[i],
edesc->pset[j].opt,
edesc->pset[j].src,
edesc->pset[j].dst,
edesc->pset[j].a_b_cnt,
edesc->pset[j].ccnt,
edesc->pset[j].src_dst_bidx,
edesc->pset[j].src_dst_cidx,
edesc->pset[j].link_bcntrld);
edesc->pset[j].param.opt,
edesc->pset[j].param.src,
edesc->pset[j].param.dst,
edesc->pset[j].param.a_b_cnt,
edesc->pset[j].param.ccnt,
edesc->pset[j].param.src_dst_bidx,
edesc->pset[j].param.src_dst_cidx,
edesc->pset[j].param.link_bcntrld);
/* Link to the previous slot if not the last set */
if (i != (nslots - 1))
edma_link(echan->slot[i], echan->slot[i+1]);
@ -183,7 +219,8 @@ static void edma_execute(struct edma_chan *echan)
}
if (edesc->processed <= MAX_NR_SG) {
dev_dbg(dev, "first transfer starting %d\n", echan->ch_num);
dev_dbg(dev, "first transfer starting on channel %d\n",
echan->ch_num);
edma_start(echan->ch_num);
} else {
dev_dbg(dev, "chan: %d: completed %d elements, resuming\n",
@ -197,7 +234,7 @@ static void edma_execute(struct edma_chan *echan)
* MAX_NR_SG
*/
if (echan->missed) {
dev_dbg(dev, "missed event in execute detected\n");
dev_dbg(dev, "missed event on channel %d\n", echan->ch_num);
edma_clean_channel(echan->ch_num);
edma_stop(echan->ch_num);
edma_start(echan->ch_num);
@ -242,6 +279,26 @@ static int edma_slave_config(struct edma_chan *echan,
return 0;
}
static int edma_dma_pause(struct edma_chan *echan)
{
/* Pause/Resume only allowed with cyclic mode */
if (!echan->edesc->cyclic)
return -EINVAL;
edma_pause(echan->ch_num);
return 0;
}
static int edma_dma_resume(struct edma_chan *echan)
{
/* Pause/Resume only allowed with cyclic mode */
if (!echan->edesc->cyclic)
return -EINVAL;
edma_resume(echan->ch_num);
return 0;
}
static int edma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
unsigned long arg)
{
@ -257,6 +314,14 @@ static int edma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
config = (struct dma_slave_config *)arg;
ret = edma_slave_config(echan, config);
break;
case DMA_PAUSE:
ret = edma_dma_pause(echan);
break;
case DMA_RESUME:
ret = edma_dma_resume(echan);
break;
default:
ret = -ENOSYS;
}
@ -275,18 +340,23 @@ static int edma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
* @dma_length: Total length of the DMA transfer
* @direction: Direction of the transfer
*/
static int edma_config_pset(struct dma_chan *chan, struct edmacc_param *pset,
static int edma_config_pset(struct dma_chan *chan, struct edma_pset *epset,
dma_addr_t src_addr, dma_addr_t dst_addr, u32 burst,
enum dma_slave_buswidth dev_width, unsigned int dma_length,
enum dma_transfer_direction direction)
{
struct edma_chan *echan = to_edma_chan(chan);
struct device *dev = chan->device->dev;
struct edmacc_param *param = &epset->param;
int acnt, bcnt, ccnt, cidx;
int src_bidx, dst_bidx, src_cidx, dst_cidx;
int absync;
acnt = dev_width;
/* src/dst_maxburst == 0 is the same case as src/dst_maxburst == 1 */
if (!burst)
burst = 1;
/*
* If the maxburst is equal to the fifo width, use
* A-synced transfers. This allows for large contiguous
@ -337,41 +407,50 @@ static int edma_config_pset(struct dma_chan *chan, struct edmacc_param *pset,
cidx = acnt * bcnt;
}
epset->len = dma_length;
if (direction == DMA_MEM_TO_DEV) {
src_bidx = acnt;
src_cidx = cidx;
dst_bidx = 0;
dst_cidx = 0;
epset->addr = src_addr;
} else if (direction == DMA_DEV_TO_MEM) {
src_bidx = 0;
src_cidx = 0;
dst_bidx = acnt;
dst_cidx = cidx;
epset->addr = dst_addr;
} else if (direction == DMA_MEM_TO_MEM) {
src_bidx = acnt;
src_cidx = cidx;
dst_bidx = acnt;
dst_cidx = cidx;
} else {
dev_err(dev, "%s: direction not implemented yet\n", __func__);
return -EINVAL;
}
pset->opt = EDMA_TCC(EDMA_CHAN_SLOT(echan->ch_num));
param->opt = EDMA_TCC(EDMA_CHAN_SLOT(echan->ch_num));
/* Configure A or AB synchronized transfers */
if (absync)
pset->opt |= SYNCDIM;
param->opt |= SYNCDIM;
pset->src = src_addr;
pset->dst = dst_addr;
param->src = src_addr;
param->dst = dst_addr;
pset->src_dst_bidx = (dst_bidx << 16) | src_bidx;
pset->src_dst_cidx = (dst_cidx << 16) | src_cidx;
param->src_dst_bidx = (dst_bidx << 16) | src_bidx;
param->src_dst_cidx = (dst_cidx << 16) | src_cidx;
pset->a_b_cnt = bcnt << 16 | acnt;
pset->ccnt = ccnt;
param->a_b_cnt = bcnt << 16 | acnt;
param->ccnt = ccnt;
/*
* Only time when (bcntrld) auto reload is required is for
* A-sync case, and in this case, a requirement of reload value
* of SZ_64K-1 only is assured. 'link' is initially set to NULL
* and then later will be populated by edma_execute.
*/
pset->link_bcntrld = 0xffffffff;
param->link_bcntrld = 0xffffffff;
return absync;
}
@ -401,23 +480,26 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg(
dev_width = echan->cfg.dst_addr_width;
burst = echan->cfg.dst_maxburst;
} else {
dev_err(dev, "%s: bad direction?\n", __func__);
dev_err(dev, "%s: bad direction: %d\n", __func__, direction);
return NULL;
}
if (dev_width == DMA_SLAVE_BUSWIDTH_UNDEFINED) {
dev_err(dev, "Undefined slave buswidth\n");
dev_err(dev, "%s: Undefined slave buswidth\n", __func__);
return NULL;
}
edesc = kzalloc(sizeof(*edesc) + sg_len *
sizeof(edesc->pset[0]), GFP_ATOMIC);
if (!edesc) {
dev_dbg(dev, "Failed to allocate a descriptor\n");
dev_err(dev, "%s: Failed to allocate a descriptor\n", __func__);
return NULL;
}
edesc->pset_nr = sg_len;
edesc->residue = 0;
edesc->direction = direction;
edesc->echan = echan;
/* Allocate a PaRAM slot, if needed */
nslots = min_t(unsigned, MAX_NR_SG, sg_len);
@ -429,7 +511,8 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg(
EDMA_SLOT_ANY);
if (echan->slot[i] < 0) {
kfree(edesc);
dev_err(dev, "Failed to allocate slot\n");
dev_err(dev, "%s: Failed to allocate slot\n",
__func__);
return NULL;
}
}
@ -452,16 +535,56 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg(
}
edesc->absync = ret;
edesc->residue += sg_dma_len(sg);
/* If this is the last in a current SG set of transactions,
enable interrupts so that next set is processed */
if (!((i+1) % MAX_NR_SG))
edesc->pset[i].opt |= TCINTEN;
edesc->pset[i].param.opt |= TCINTEN;
/* If this is the last set, enable completion interrupt flag */
if (i == sg_len - 1)
edesc->pset[i].opt |= TCINTEN;
edesc->pset[i].param.opt |= TCINTEN;
}
edesc->residue_stat = edesc->residue;
return vchan_tx_prep(&echan->vchan, &edesc->vdesc, tx_flags);
}
struct dma_async_tx_descriptor *edma_prep_dma_memcpy(
struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
size_t len, unsigned long tx_flags)
{
int ret;
struct edma_desc *edesc;
struct device *dev = chan->device->dev;
struct edma_chan *echan = to_edma_chan(chan);
if (unlikely(!echan || !len))
return NULL;
edesc = kzalloc(sizeof(*edesc) + sizeof(edesc->pset[0]), GFP_ATOMIC);
if (!edesc) {
dev_dbg(dev, "Failed to allocate a descriptor\n");
return NULL;
}
edesc->pset_nr = 1;
ret = edma_config_pset(chan, &edesc->pset[0], src, dest, 1,
DMA_SLAVE_BUSWIDTH_4_BYTES, len, DMA_MEM_TO_MEM);
if (ret < 0)
return NULL;
edesc->absync = ret;
/*
* Enable intermediate transfer chaining to re-trigger channel
* on completion of every TR, and enable transfer-completion
* interrupt on completion of the whole transfer.
*/
edesc->pset[0].param.opt |= ITCCHEN;
edesc->pset[0].param.opt |= TCINTEN;
return vchan_tx_prep(&echan->vchan, &edesc->vdesc, tx_flags);
}
@ -493,12 +616,12 @@ static struct dma_async_tx_descriptor *edma_prep_dma_cyclic(
dev_width = echan->cfg.dst_addr_width;
burst = echan->cfg.dst_maxburst;
} else {
dev_err(dev, "%s: bad direction?\n", __func__);
dev_err(dev, "%s: bad direction: %d\n", __func__, direction);
return NULL;
}
if (dev_width == DMA_SLAVE_BUSWIDTH_UNDEFINED) {
dev_err(dev, "Undefined slave buswidth\n");
dev_err(dev, "%s: Undefined slave buswidth\n", __func__);
return NULL;
}
@ -523,16 +646,18 @@ static struct dma_async_tx_descriptor *edma_prep_dma_cyclic(
edesc = kzalloc(sizeof(*edesc) + nslots *
sizeof(edesc->pset[0]), GFP_ATOMIC);
if (!edesc) {
dev_dbg(dev, "Failed to allocate a descriptor\n");
dev_err(dev, "%s: Failed to allocate a descriptor\n", __func__);
return NULL;
}
edesc->cyclic = 1;
edesc->pset_nr = nslots;
edesc->residue = edesc->residue_stat = buf_len;
edesc->direction = direction;
edesc->echan = echan;
dev_dbg(dev, "%s: nslots=%d\n", __func__, nslots);
dev_dbg(dev, "%s: period_len=%d\n", __func__, period_len);
dev_dbg(dev, "%s: buf_len=%d\n", __func__, buf_len);
dev_dbg(dev, "%s: channel=%d nslots=%d period_len=%zu buf_len=%zu\n",
__func__, echan->ch_num, nslots, period_len, buf_len);
for (i = 0; i < nslots; i++) {
/* Allocate a PaRAM slot, if needed */
@ -542,7 +667,8 @@ static struct dma_async_tx_descriptor *edma_prep_dma_cyclic(
EDMA_SLOT_ANY);
if (echan->slot[i] < 0) {
kfree(edesc);
dev_err(dev, "Failed to allocate slot\n");
dev_err(dev, "%s: Failed to allocate slot\n",
__func__);
return NULL;
}
}
@ -566,8 +692,8 @@ static struct dma_async_tx_descriptor *edma_prep_dma_cyclic(
else
src_addr += period_len;
dev_dbg(dev, "%s: Configure period %d of buf:\n", __func__, i);
dev_dbg(dev,
dev_vdbg(dev, "%s: Configure period %d of buf:\n", __func__, i);
dev_vdbg(dev,
"\n pset[%d]:\n"
" chnum\t%d\n"
" slot\t%d\n"
@ -580,14 +706,14 @@ static struct dma_async_tx_descriptor *edma_prep_dma_cyclic(
" cidx\t%08x\n"
" lkrld\t%08x\n",
i, echan->ch_num, echan->slot[i],
edesc->pset[i].opt,
edesc->pset[i].src,
edesc->pset[i].dst,
edesc->pset[i].a_b_cnt,
edesc->pset[i].ccnt,
edesc->pset[i].src_dst_bidx,
edesc->pset[i].src_dst_cidx,
edesc->pset[i].link_bcntrld);
edesc->pset[i].param.opt,
edesc->pset[i].param.src,
edesc->pset[i].param.dst,
edesc->pset[i].param.a_b_cnt,
edesc->pset[i].param.ccnt,
edesc->pset[i].param.src_dst_bidx,
edesc->pset[i].param.src_dst_cidx,
edesc->pset[i].param.link_bcntrld);
edesc->absync = ret;
@ -595,7 +721,7 @@ static struct dma_async_tx_descriptor *edma_prep_dma_cyclic(
* Enable interrupts for every period because callback
* has to be called for every period.
*/
edesc->pset[i].opt |= TCINTEN;
edesc->pset[i].param.opt |= TCINTEN;
}
return vchan_tx_prep(&echan->vchan, &edesc->vdesc, tx_flags);
@ -606,7 +732,6 @@ static void edma_callback(unsigned ch_num, u16 ch_status, void *data)
struct edma_chan *echan = data;
struct device *dev = echan->vchan.chan.device->dev;
struct edma_desc *edesc;
unsigned long flags;
struct edmacc_param p;
edesc = echan->edesc;
@ -617,27 +742,34 @@ static void edma_callback(unsigned ch_num, u16 ch_status, void *data)
switch (ch_status) {
case EDMA_DMA_COMPLETE:
spin_lock_irqsave(&echan->vchan.lock, flags);
spin_lock(&echan->vchan.lock);
if (edesc) {
if (edesc->cyclic) {
vchan_cyclic_callback(&edesc->vdesc);
} else if (edesc->processed == edesc->pset_nr) {
dev_dbg(dev, "Transfer complete, stopping channel %d\n", ch_num);
edesc->residue = 0;
edma_stop(echan->ch_num);
vchan_cookie_complete(&edesc->vdesc);
edma_execute(echan);
} else {
dev_dbg(dev, "Intermediate transfer complete on channel %d\n", ch_num);
/* Update statistics for tx_status */
edesc->residue -= edesc->sg_len;
edesc->residue_stat = edesc->residue;
edesc->processed_stat = edesc->processed;
edma_execute(echan);
}
}
spin_unlock_irqrestore(&echan->vchan.lock, flags);
spin_unlock(&echan->vchan.lock);
break;
case EDMA_DMA_CC_ERROR:
spin_lock_irqsave(&echan->vchan.lock, flags);
spin_lock(&echan->vchan.lock);
edma_read_slot(EDMA_CHAN_SLOT(echan->slot[0]), &p);
@ -668,7 +800,7 @@ static void edma_callback(unsigned ch_num, u16 ch_status, void *data)
edma_trigger_channel(echan->ch_num);
}
spin_unlock_irqrestore(&echan->vchan.lock, flags);
spin_unlock(&echan->vchan.lock);
break;
default:
@ -704,7 +836,7 @@ static int edma_alloc_chan_resources(struct dma_chan *chan)
echan->alloced = true;
echan->slot[0] = echan->ch_num;
dev_dbg(dev, "allocated channel for %u:%u\n",
dev_dbg(dev, "allocated channel %d for %u:%u\n", echan->ch_num,
EDMA_CTLR(echan->ch_num), EDMA_CHAN_SLOT(echan->ch_num));
return 0;
@ -756,23 +888,52 @@ static void edma_issue_pending(struct dma_chan *chan)
spin_unlock_irqrestore(&echan->vchan.lock, flags);
}
static size_t edma_desc_size(struct edma_desc *edesc)
static u32 edma_residue(struct edma_desc *edesc)
{
bool dst = edesc->direction == DMA_DEV_TO_MEM;
struct edma_pset *pset = edesc->pset;
dma_addr_t done, pos;
int i;
size_t size;
if (edesc->absync)
for (size = i = 0; i < edesc->pset_nr; i++)
size += (edesc->pset[i].a_b_cnt & 0xffff) *
(edesc->pset[i].a_b_cnt >> 16) *
edesc->pset[i].ccnt;
else
size = (edesc->pset[0].a_b_cnt & 0xffff) *
(edesc->pset[0].a_b_cnt >> 16) +
(edesc->pset[0].a_b_cnt & 0xffff) *
(SZ_64K - 1) * edesc->pset[0].ccnt;
/*
* We always read the dst/src position from the first RamPar
* pset. That's the one which is active now.
*/
pos = edma_get_position(edesc->echan->slot[0], dst);
return size;
/*
* Cyclic is simple. Just subtract pset[0].addr from pos.
*
* We never update edesc->residue in the cyclic case, so we
* can tell the remaining room to the end of the circular
* buffer.
*/
if (edesc->cyclic) {
done = pos - pset->addr;
edesc->residue_stat = edesc->residue - done;
return edesc->residue_stat;
}
/*
* For SG operation we catch up with the last processed
* status.
*/
pset += edesc->processed_stat;
for (i = edesc->processed_stat; i < edesc->processed; i++, pset++) {
/*
* If we are inside this pset address range, we know
* this is the active one. Get the current delta and
* stop walking the psets.
*/
if (pos >= pset->addr && pos < pset->addr + pset->len)
return edesc->residue_stat - (pos - pset->addr);
/* Otherwise mark it done and update residue_stat. */
edesc->processed_stat++;
edesc->residue_stat -= pset->len;
}
return edesc->residue_stat;
}
/* Check request completion status */
@ -790,13 +951,10 @@ static enum dma_status edma_tx_status(struct dma_chan *chan,
return ret;
spin_lock_irqsave(&echan->vchan.lock, flags);
vdesc = vchan_find_desc(&echan->vchan, cookie);
if (vdesc) {
txstate->residue = edma_desc_size(to_edma_desc(&vdesc->tx));
} else if (echan->edesc && echan->edesc->vdesc.tx.cookie == cookie) {
struct edma_desc *edesc = echan->edesc;
txstate->residue = edma_desc_size(edesc);
}
if (echan->edesc && echan->edesc->vdesc.tx.cookie == cookie)
txstate->residue = edma_residue(echan->edesc);
else if ((vdesc = vchan_find_desc(&echan->vchan, cookie)))
txstate->residue = to_edma_desc(&vdesc->tx)->residue;
spin_unlock_irqrestore(&echan->vchan.lock, flags);
return ret;
@ -822,18 +980,43 @@ static void __init edma_chan_init(struct edma_cc *ecc,
}
}
#define EDMA_DMA_BUSWIDTHS (BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | \
BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \
BIT(DMA_SLAVE_BUSWIDTH_4_BYTES))
static int edma_dma_device_slave_caps(struct dma_chan *dchan,
struct dma_slave_caps *caps)
{
caps->src_addr_widths = EDMA_DMA_BUSWIDTHS;
caps->dstn_addr_widths = EDMA_DMA_BUSWIDTHS;
caps->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);
caps->cmd_pause = true;
caps->cmd_terminate = true;
caps->residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR;
return 0;
}
static void edma_dma_init(struct edma_cc *ecc, struct dma_device *dma,
struct device *dev)
{
dma->device_prep_slave_sg = edma_prep_slave_sg;
dma->device_prep_dma_cyclic = edma_prep_dma_cyclic;
dma->device_prep_dma_memcpy = edma_prep_dma_memcpy;
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_tx_status = edma_tx_status;
dma->device_control = edma_control;
dma->device_slave_caps = edma_dma_device_slave_caps;
dma->dev = dev;
/*
* code using dma memcpy must make sure alignment of
* length is at dma->copy_align boundary.
*/
dma->copy_align = DMA_SLAVE_BUSWIDTH_4_BYTES;
INIT_LIST_HEAD(&dma->channels);
}
@ -861,6 +1044,8 @@ static int edma_probe(struct platform_device *pdev)
dma_cap_zero(ecc->dma_slave.cap_mask);
dma_cap_set(DMA_SLAVE, ecc->dma_slave.cap_mask);
dma_cap_set(DMA_CYCLIC, ecc->dma_slave.cap_mask);
dma_cap_set(DMA_MEMCPY, ecc->dma_slave.cap_mask);
edma_dma_init(ecc, &ecc->dma_slave, &pdev->dev);

View File

@ -43,15 +43,15 @@
/* PaRAM slots are laid out like this */
struct edmacc_param {
unsigned int opt;
unsigned int src;
unsigned int a_b_cnt;
unsigned int dst;
unsigned int src_dst_bidx;
unsigned int link_bcntrld;
unsigned int src_dst_cidx;
unsigned int ccnt;
};
u32 opt;
u32 src;
u32 a_b_cnt;
u32 dst;
u32 src_dst_bidx;
u32 link_bcntrld;
u32 src_dst_cidx;
u32 ccnt;
} __packed;
/* fields in edmacc_param.opt */
#define SAM BIT(0)
@ -130,7 +130,7 @@ void edma_set_src(unsigned slot, dma_addr_t src_port,
enum address_mode mode, enum fifo_width);
void edma_set_dest(unsigned slot, dma_addr_t dest_port,
enum address_mode mode, enum fifo_width);
void edma_get_position(unsigned slot, dma_addr_t *src, dma_addr_t *dst);
dma_addr_t edma_get_position(unsigned slot, bool dst);
void edma_set_src_index(unsigned slot, s16 src_bidx, s16 src_cidx);
void edma_set_dest_index(unsigned slot, s16 dest_bidx, s16 dest_cidx);
void edma_set_transfer_params(unsigned slot, u16 acnt, u16 bcnt, u16 ccnt,
@ -158,13 +158,6 @@ struct edma_rsv_info {
/* platform_data for EDMA driver */
struct edma_soc_info {
/* how many dma resources of each type */
unsigned n_channel;
unsigned n_region;
unsigned n_slot;
unsigned n_tc;
unsigned n_cc;
/*
* Default queue is expected to be a low-priority queue.
* This way, long transfers on the default queue started
@ -175,7 +168,6 @@ struct edma_soc_info {
/* Resource reservation for other cores */
struct edma_rsv_info *rsv;
s8 (*queue_tc_mapping)[2];
s8 (*queue_priority_mapping)[2];
const s16 (*xbar_chans)[2];
};