aboutsummaryrefslogtreecommitdiff
path: root/drivers/dma
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/dma')
-rw-r--r--drivers/dma/edma.c486
1 files changed, 340 insertions, 146 deletions
diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c
index d4d71e60da1b..31722d436a42 100644
--- a/drivers/dma/edma.c
+++ b/drivers/dma/edma.c
@@ -201,13 +201,20 @@ struct edma_desc {
struct edma_cc;
+struct edma_tc {
+ struct device_node *node;
+ u16 id;
+};
+
struct edma_chan {
struct virt_dma_chan vchan;
struct list_head node;
struct edma_desc *edesc;
struct edma_cc *ecc;
+ struct edma_tc *tc;
int ch_num;
bool alloced;
+ bool hw_triggered;
int slot[EDMA_MAX_SLOTS];
int missed;
struct dma_slave_config cfg;
@@ -218,6 +225,7 @@ struct edma_cc {
struct edma_soc_info *info;
void __iomem *base;
int id;
+ bool legacy_mode;
/* eDMA3 resource information */
unsigned num_channels;
@@ -228,20 +236,16 @@ struct edma_cc {
bool chmap_exist;
enum dma_event_q default_queue;
- bool unused_chan_list_done;
- /* The slot_inuse bit for each PaRAM slot is clear unless the
- * channel is in use ... by ARM or DSP, for QDMA, or whatever.
+ /*
+ * The slot_inuse bit for each PaRAM slot is clear unless the slot is
+ * in use by Linux or if it is allocated to be used by DSP.
*/
unsigned long *slot_inuse;
- /* The channel_unused bit for each channel is clear unless
- * it is not being used on this platform. It uses a bit
- * of SOC-specific initialization code.
- */
- unsigned long *channel_unused;
-
struct dma_device dma_slave;
+ struct dma_device *dma_memcpy;
struct edma_chan *slave_chans;
+ struct edma_tc *tc_list;
int dummy_slot;
};
@@ -251,8 +255,17 @@ static const struct edmacc_param dummy_paramset = {
.ccnt = 1,
};
+#define EDMA_BINDING_LEGACY 0
+#define EDMA_BINDING_TPCC 1
static const struct of_device_id edma_of_ids[] = {
- { .compatible = "ti,edma3", },
+ {
+ .compatible = "ti,edma3",
+ .data = (void *)EDMA_BINDING_LEGACY,
+ },
+ {
+ .compatible = "ti,edma3-tpcc",
+ .data = (void *)EDMA_BINDING_TPCC,
+ },
{}
};
@@ -412,60 +425,6 @@ static void edma_set_chmap(struct edma_chan *echan, int slot)
}
}
-static int prepare_unused_channel_list(struct device *dev, void *data)
-{
- struct platform_device *pdev = to_platform_device(dev);
- struct edma_cc *ecc = data;
- int dma_req_min = EDMA_CTLR_CHAN(ecc->id, 0);
- int dma_req_max = dma_req_min + ecc->num_channels;
- int i, count;
- struct of_phandle_args dma_spec;
-
- if (dev->of_node) {
- struct platform_device *dma_pdev;
-
- count = of_property_count_strings(dev->of_node, "dma-names");
- if (count < 0)
- return 0;
- for (i = 0; i < count; i++) {
- if (of_parse_phandle_with_args(dev->of_node, "dmas",
- "#dma-cells", i,
- &dma_spec))
- continue;
-
- if (!of_match_node(edma_of_ids, dma_spec.np)) {
- of_node_put(dma_spec.np);
- continue;
- }
-
- dma_pdev = of_find_device_by_node(dma_spec.np);
- if (&dma_pdev->dev != ecc->dev)
- continue;
-
- clear_bit(EDMA_CHAN_SLOT(dma_spec.args[0]),
- ecc->channel_unused);
- of_node_put(dma_spec.np);
- }
- return 0;
- }
-
- /* For non-OF case */
- for (i = 0; i < pdev->num_resources; i++) {
- struct resource *res = &pdev->resource[i];
- int dma_req;
-
- if (!(res->flags & IORESOURCE_DMA))
- continue;
-
- dma_req = (int)res->start;
- if (dma_req >= dma_req_min && dma_req < dma_req_max)
- clear_bit(EDMA_CHAN_SLOT(pdev->resource[i].start),
- ecc->channel_unused);
- }
-
- return 0;
-}
-
static void edma_setup_interrupt(struct edma_chan *echan, bool enable)
{
struct edma_cc *ecc = echan->ecc;
@@ -617,7 +576,7 @@ static void edma_start(struct edma_chan *echan)
int j = (channel >> 5);
unsigned int mask = BIT(channel & 0x1f);
- if (test_bit(channel, ecc->channel_unused)) {
+ if (!echan->hw_triggered) {
/* EDMA channels without event association */
dev_dbg(ecc->dev, "ESR%d %08x\n", j,
edma_shadow0_read_array(ecc, SH_ESR, j));
@@ -734,20 +693,6 @@ static int edma_alloc_channel(struct edma_chan *echan,
struct edma_cc *ecc = echan->ecc;
int channel = EDMA_CHAN_SLOT(echan->ch_num);
- if (!ecc->unused_chan_list_done) {
- /*
- * Scan all the platform devices to find out the EDMA channels
- * used and clear them in the unused list, making the rest
- * available for ARM usage.
- */
- int ret = bus_for_each_dev(&platform_bus_type, NULL, ecc,
- prepare_unused_channel_list);
- if (ret < 0)
- return ret;
-
- ecc->unused_chan_list_done = true;
- }
-
/* ensure access through shadow region 0 */
edma_or_array2(ecc, EDMA_DRAE, 0, channel >> 5, BIT(channel & 0x1f));
@@ -899,7 +844,7 @@ static int edma_terminate_all(struct dma_chan *chan)
if (echan->edesc) {
edma_stop(echan);
/* Move the cyclic channel back to default queue */
- if (echan->edesc->cyclic)
+ if (!echan->tc && echan->edesc->cyclic)
edma_assign_channel_eventq(echan, EVENTQ_DEFAULT);
/*
* free the running request descriptor
@@ -1403,7 +1348,8 @@ static struct dma_async_tx_descriptor *edma_prep_dma_cyclic(
}
/* Place the cyclic channel to highest priority queue */
- edma_assign_channel_eventq(echan, EVENTQ_0);
+ if (!echan->tc)
+ edma_assign_channel_eventq(echan, EVENTQ_0);
return vchan_tx_prep(&echan->vchan, &edesc->vdesc, tx_flags);
}
@@ -1609,18 +1555,54 @@ static irqreturn_t dma_ccerr_handler(int irq, void *data)
return IRQ_HANDLED;
}
+static void edma_tc_set_pm_state(struct edma_tc *tc, bool enable)
+{
+ struct platform_device *tc_pdev;
+ int ret;
+
+ if (!tc)
+ return;
+
+ tc_pdev = of_find_device_by_node(tc->node);
+ if (!tc_pdev) {
+ pr_err("%s: TPTC device is not found\n", __func__);
+ return;
+ }
+ if (!pm_runtime_enabled(&tc_pdev->dev))
+ pm_runtime_enable(&tc_pdev->dev);
+
+ if (enable)
+ ret = pm_runtime_get_sync(&tc_pdev->dev);
+ else
+ ret = pm_runtime_put_sync(&tc_pdev->dev);
+
+ if (ret < 0)
+ pr_err("%s: pm_runtime_%s_sync() failed for %s\n", __func__,
+ enable ? "get" : "put", dev_name(&tc_pdev->dev));
+}
+
/* Alloc channel resources */
static int edma_alloc_chan_resources(struct dma_chan *chan)
{
struct edma_chan *echan = to_edma_chan(chan);
- struct device *dev = chan->device->dev;
+ struct edma_cc *ecc = echan->ecc;
+ struct device *dev = ecc->dev;
+ enum dma_event_q eventq_no = EVENTQ_DEFAULT;
int ret;
- ret = edma_alloc_channel(echan, EVENTQ_DEFAULT);
+ if (echan->tc) {
+ eventq_no = echan->tc->id;
+ } else if (ecc->tc_list) {
+ /* memcpy channel */
+ echan->tc = &ecc->tc_list[ecc->info->default_queue];
+ eventq_no = echan->tc->id;
+ }
+
+ ret = edma_alloc_channel(echan, eventq_no);
if (ret)
return ret;
- echan->slot[0] = edma_alloc_slot(echan->ecc, echan->ch_num);
+ echan->slot[0] = edma_alloc_slot(ecc, echan->ch_num);
if (echan->slot[0] < 0) {
dev_err(dev, "Entry slot allocation failed for channel %u\n",
EDMA_CHAN_SLOT(echan->ch_num));
@@ -1631,8 +1613,11 @@ static int edma_alloc_chan_resources(struct dma_chan *chan)
edma_set_chmap(echan, echan->slot[0]);
echan->alloced = true;
- dev_dbg(dev, "allocated channel %d for %u:%u\n", echan->ch_num,
- EDMA_CTLR(echan->ch_num), EDMA_CHAN_SLOT(echan->ch_num));
+ dev_dbg(dev, "Got eDMA channel %d for virt channel %d (%s trigger)\n",
+ EDMA_CHAN_SLOT(echan->ch_num), chan->chan_id,
+ echan->hw_triggered ? "HW" : "SW");
+
+ edma_tc_set_pm_state(echan->tc, true);
return 0;
@@ -1645,6 +1630,7 @@ err_slot:
static void edma_free_chan_resources(struct dma_chan *chan)
{
struct edma_chan *echan = to_edma_chan(chan);
+ struct device *dev = echan->ecc->dev;
int i;
/* Terminate transfers */
@@ -1669,7 +1655,12 @@ static void edma_free_chan_resources(struct dma_chan *chan)
echan->alloced = false;
}
- dev_dbg(chan->device->dev, "freeing channel for %u\n", echan->ch_num);
+ edma_tc_set_pm_state(echan->tc, false);
+ echan->tc = NULL;
+ echan->hw_triggered = false;
+
+ dev_dbg(dev, "Free eDMA channel %d for virt channel %d\n",
+ EDMA_CHAN_SLOT(echan->ch_num), chan->chan_id);
}
/* Send pending descriptor to hardware */
@@ -1756,41 +1747,90 @@ static enum dma_status edma_tx_status(struct dma_chan *chan,
return ret;
}
+static bool edma_is_memcpy_channel(int ch_num, u16 *memcpy_channels)
+{
+ s16 *memcpy_ch = memcpy_channels;
+
+ if (!memcpy_channels)
+ return false;
+ while (*memcpy_ch != -1) {
+ if (*memcpy_ch == ch_num)
+ return true;
+ memcpy_ch++;
+ }
+ return false;
+}
+
#define EDMA_DMA_BUSWIDTHS (BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | \
BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \
BIT(DMA_SLAVE_BUSWIDTH_3_BYTES) | \
BIT(DMA_SLAVE_BUSWIDTH_4_BYTES))
-static void edma_dma_init(struct edma_cc *ecc)
+static void edma_dma_init(struct edma_cc *ecc, bool legacy_mode)
{
- struct dma_device *ddev = &ecc->dma_slave;
+ struct dma_device *s_ddev = &ecc->dma_slave;
+ struct dma_device *m_ddev = NULL;
+ s16 *memcpy_channels = ecc->info->memcpy_channels;
int i, j;
- dma_cap_zero(ddev->cap_mask);
- dma_cap_set(DMA_SLAVE, ddev->cap_mask);
- dma_cap_set(DMA_CYCLIC, ddev->cap_mask);
- dma_cap_set(DMA_MEMCPY, ddev->cap_mask);
+ dma_cap_zero(s_ddev->cap_mask);
+ dma_cap_set(DMA_SLAVE, s_ddev->cap_mask);
+ dma_cap_set(DMA_CYCLIC, s_ddev->cap_mask);
+ if (ecc->legacy_mode && !memcpy_channels) {
+ dev_warn(ecc->dev,
+ "Legacy memcpy is enabled, things might not work\n");
- ddev->device_prep_slave_sg = edma_prep_slave_sg;
- ddev->device_prep_dma_cyclic = edma_prep_dma_cyclic;
- ddev->device_prep_dma_memcpy = edma_prep_dma_memcpy;
- ddev->device_alloc_chan_resources = edma_alloc_chan_resources;
- ddev->device_free_chan_resources = edma_free_chan_resources;
- ddev->device_issue_pending = edma_issue_pending;
- ddev->device_tx_status = edma_tx_status;
- ddev->device_config = edma_slave_config;
- ddev->device_pause = edma_dma_pause;
- ddev->device_resume = edma_dma_resume;
- ddev->device_terminate_all = edma_terminate_all;
-
- ddev->src_addr_widths = EDMA_DMA_BUSWIDTHS;
- ddev->dst_addr_widths = EDMA_DMA_BUSWIDTHS;
- ddev->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);
- ddev->residue_granularity = DMA_RESIDUE_GRANULARITY_BURST;
-
- ddev->dev = ecc->dev;
+ dma_cap_set(DMA_MEMCPY, s_ddev->cap_mask);
+ s_ddev->device_prep_dma_memcpy = edma_prep_dma_memcpy;
+ s_ddev->directions = BIT(DMA_MEM_TO_MEM);
+ }
- INIT_LIST_HEAD(&ddev->channels);
+ s_ddev->device_prep_slave_sg = edma_prep_slave_sg;
+ s_ddev->device_prep_dma_cyclic = edma_prep_dma_cyclic;
+ s_ddev->device_alloc_chan_resources = edma_alloc_chan_resources;
+ s_ddev->device_free_chan_resources = edma_free_chan_resources;
+ s_ddev->device_issue_pending = edma_issue_pending;
+ s_ddev->device_tx_status = edma_tx_status;
+ s_ddev->device_config = edma_slave_config;
+ s_ddev->device_pause = edma_dma_pause;
+ s_ddev->device_resume = edma_dma_resume;
+ s_ddev->device_terminate_all = edma_terminate_all;
+
+ s_ddev->src_addr_widths = EDMA_DMA_BUSWIDTHS;
+ s_ddev->dst_addr_widths = EDMA_DMA_BUSWIDTHS;
+ s_ddev->directions |= (BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV));
+ s_ddev->residue_granularity = DMA_RESIDUE_GRANULARITY_BURST;
+
+ s_ddev->dev = ecc->dev;
+ INIT_LIST_HEAD(&s_ddev->channels);
+
+ if (memcpy_channels) {
+ m_ddev = devm_kzalloc(ecc->dev, sizeof(*m_ddev), GFP_KERNEL);
+ ecc->dma_memcpy = m_ddev;
+
+ dma_cap_zero(m_ddev->cap_mask);
+ dma_cap_set(DMA_MEMCPY, m_ddev->cap_mask);
+
+ m_ddev->device_prep_dma_memcpy = edma_prep_dma_memcpy;
+ m_ddev->device_alloc_chan_resources = edma_alloc_chan_resources;
+ m_ddev->device_free_chan_resources = edma_free_chan_resources;
+ m_ddev->device_issue_pending = edma_issue_pending;
+ m_ddev->device_tx_status = edma_tx_status;
+ m_ddev->device_config = edma_slave_config;
+ m_ddev->device_pause = edma_dma_pause;
+ m_ddev->device_resume = edma_dma_resume;
+ m_ddev->device_terminate_all = edma_terminate_all;
+
+ m_ddev->src_addr_widths = EDMA_DMA_BUSWIDTHS;
+ m_ddev->dst_addr_widths = EDMA_DMA_BUSWIDTHS;
+ m_ddev->directions = BIT(DMA_MEM_TO_MEM);
+ m_ddev->residue_granularity = DMA_RESIDUE_GRANULARITY_BURST;
+
+ m_ddev->dev = ecc->dev;
+ INIT_LIST_HEAD(&m_ddev->channels);
+ } else if (!ecc->legacy_mode) {
+ dev_info(ecc->dev, "memcpy is disabled\n");
+ }
for (i = 0; i < ecc->num_channels; i++) {
struct edma_chan *echan = &ecc->slave_chans[i];
@@ -1798,7 +1838,10 @@ static void edma_dma_init(struct edma_cc *ecc)
echan->ecc = ecc;
echan->vchan.desc_free = edma_desc_free;
- vchan_init(&echan->vchan, ddev);
+ if (m_ddev && edma_is_memcpy_channel(i, memcpy_channels))
+ vchan_init(&echan->vchan, m_ddev);
+ else
+ vchan_init(&echan->vchan, s_ddev);
INIT_LIST_HEAD(&echan->node);
for (j = 0; j < EDMA_MAX_SLOTS; j++)
@@ -1921,7 +1964,8 @@ static int edma_xbar_event_map(struct device *dev, struct edma_soc_info *pdata,
return 0;
}
-static struct edma_soc_info *edma_setup_info_from_dt(struct device *dev)
+static struct edma_soc_info *edma_setup_info_from_dt(struct device *dev,
+ bool legacy_mode)
{
struct edma_soc_info *info;
struct property *prop;
@@ -1932,20 +1976,121 @@ static struct edma_soc_info *edma_setup_info_from_dt(struct device *dev)
if (!info)
return ERR_PTR(-ENOMEM);
- prop = of_find_property(dev->of_node, "ti,edma-xbar-event-map", &sz);
+ if (legacy_mode) {
+ prop = of_find_property(dev->of_node, "ti,edma-xbar-event-map",
+ &sz);
+ if (prop) {
+ ret = edma_xbar_event_map(dev, info, sz);
+ if (ret)
+ return ERR_PTR(ret);
+ }
+ return info;
+ }
+
+ /* Get the list of channels allocated to be used for memcpy */
+ prop = of_find_property(dev->of_node, "ti,edma-memcpy-channels", &sz);
+ if (prop) {
+ const char pname[] = "ti,edma-memcpy-channels";
+ size_t nelm = sz / sizeof(s16);
+ s16 *memcpy_ch;
+
+ memcpy_ch = devm_kcalloc(dev, nelm + 1, sizeof(s16),
+ GFP_KERNEL);
+ if (!memcpy_ch)
+ return ERR_PTR(-ENOMEM);
+
+ ret = of_property_read_u16_array(dev->of_node, pname,
+ (u16 *)memcpy_ch, nelm);
+ if (ret)
+ return ERR_PTR(ret);
+
+ memcpy_ch[nelm] = -1;
+ info->memcpy_channels = memcpy_ch;
+ }
+
+ prop = of_find_property(dev->of_node, "ti,edma-reserved-slot-ranges",
+ &sz);
if (prop) {
- ret = edma_xbar_event_map(dev, info, sz);
+ const char pname[] = "ti,edma-reserved-slot-ranges";
+ s16 (*rsv_slots)[2];
+ size_t nelm = sz / sizeof(*rsv_slots);
+ struct edma_rsv_info *rsv_info;
+
+ if (!nelm)
+ return info;
+
+ rsv_info = devm_kzalloc(dev, sizeof(*rsv_info), GFP_KERNEL);
+ if (!rsv_info)
+ return ERR_PTR(-ENOMEM);
+
+ rsv_slots = devm_kcalloc(dev, nelm + 1, sizeof(*rsv_slots),
+ GFP_KERNEL);
+ if (!rsv_slots)
+ return ERR_PTR(-ENOMEM);
+
+ ret = of_property_read_u16_array(dev->of_node, pname,
+ (u16 *)rsv_slots, nelm * 2);
if (ret)
return ERR_PTR(ret);
+
+ rsv_slots[nelm][0] = -1;
+ rsv_slots[nelm][1] = -1;
+ info->rsv = rsv_info;
+ info->rsv->rsv_slots = (const s16 (*)[2])rsv_slots;
}
return info;
}
+
+static struct dma_chan *of_edma_xlate(struct of_phandle_args *dma_spec,
+ struct of_dma *ofdma)
+{
+ struct edma_cc *ecc = ofdma->of_dma_data;
+ struct dma_chan *chan = NULL;
+ struct edma_chan *echan;
+ int i;
+
+ if (!ecc || dma_spec->args_count < 1)
+ return NULL;
+
+ for (i = 0; i < ecc->num_channels; i++) {
+ echan = &ecc->slave_chans[i];
+ if (echan->ch_num == dma_spec->args[0]) {
+ chan = &echan->vchan.chan;
+ break;
+ }
+ }
+
+ if (!chan)
+ return NULL;
+
+ if (echan->ecc->legacy_mode && dma_spec->args_count == 1)
+ goto out;
+
+ if (!echan->ecc->legacy_mode && dma_spec->args_count == 2 &&
+ dma_spec->args[1] < echan->ecc->num_tc) {
+ echan->tc = &echan->ecc->tc_list[dma_spec->args[1]];
+ goto out;
+ }
+
+ return NULL;
+out:
+ /* The channel is going to be used as HW synchronized */
+ echan->hw_triggered = true;
+ return dma_get_slave_channel(chan);
+}
#else
-static struct edma_soc_info *edma_setup_info_from_dt(struct device *dev)
+static struct edma_soc_info *edma_setup_info_from_dt(struct device *dev,
+ bool legacy_mode)
{
return ERR_PTR(-EINVAL);
}
+
+static struct dma_chan *of_edma_xlate(struct of_phandle_args *dma_spec,
+ struct of_dma *ofdma)
+{
+ return NULL;
+}
#endif
static int edma_probe(struct platform_device *pdev)
@@ -1953,7 +2098,6 @@ static int edma_probe(struct platform_device *pdev)
struct edma_soc_info *info = pdev->dev.platform_data;
s8 (*queue_priority_mapping)[2];
int i, off, ln;
- const s16 (*rsv_chans)[2];
const s16 (*rsv_slots)[2];
const s16 (*xbar_chans)[2];
int irq;
@@ -1962,10 +2106,17 @@ static int edma_probe(struct platform_device *pdev)
struct device_node *node = pdev->dev.of_node;
struct device *dev = &pdev->dev;
struct edma_cc *ecc;
+ bool legacy_mode = true;
int ret;
if (node) {
- info = edma_setup_info_from_dt(dev);
+ const struct of_device_id *match;
+
+ match = of_match_node(edma_of_ids, node);
+ if (match && (u32)match->data == EDMA_BINDING_TPCC)
+ legacy_mode = false;
+
+ info = edma_setup_info_from_dt(dev, legacy_mode);
if (IS_ERR(info)) {
dev_err(dev, "failed to get DT data\n");
return PTR_ERR(info);
@@ -1994,6 +2145,7 @@ static int edma_probe(struct platform_device *pdev)
ecc->dev = dev;
ecc->id = pdev->id;
+ ecc->legacy_mode = legacy_mode;
/* When booting with DT the pdev->id is -1 */
if (ecc->id < 0)
ecc->id = 0;
@@ -2024,12 +2176,6 @@ static int edma_probe(struct platform_device *pdev)
if (!ecc->slave_chans)
return -ENOMEM;
- ecc->channel_unused = devm_kcalloc(dev,
- BITS_TO_LONGS(ecc->num_channels),
- sizeof(unsigned long), GFP_KERNEL);
- if (!ecc->channel_unused)
- return -ENOMEM;
-
ecc->slot_inuse = devm_kcalloc(dev, BITS_TO_LONGS(ecc->num_slots),
sizeof(unsigned long), GFP_KERNEL);
if (!ecc->slot_inuse)
@@ -2040,20 +2186,7 @@ static int edma_probe(struct platform_device *pdev)
for (i = 0; i < ecc->num_slots; i++)
edma_write_slot(ecc, i, &dummy_paramset);
- /* Mark all channels as unused */
- memset(ecc->channel_unused, 0xff, sizeof(ecc->channel_unused));
-
if (info->rsv) {
- /* Clear the reserved channels in unused list */
- rsv_chans = info->rsv->rsv_chans;
- if (rsv_chans) {
- for (i = 0; rsv_chans[i][0] != -1; i++) {
- off = rsv_chans[i][0];
- ln = rsv_chans[i][1];
- clear_bits(off, ln, ecc->channel_unused);
- }
- }
-
/* Set the reserved slots in inuse list */
rsv_slots = info->rsv->rsv_slots;
if (rsv_slots) {
@@ -2070,7 +2203,6 @@ static int edma_probe(struct platform_device *pdev)
if (xbar_chans) {
for (i = 0; xbar_chans[i][1] != -1; i++) {
off = xbar_chans[i][1];
- clear_bits(off, 1, ecc->channel_unused);
}
}
@@ -2112,6 +2244,31 @@ static int edma_probe(struct platform_device *pdev)
queue_priority_mapping = info->queue_priority_mapping;
+ if (!ecc->legacy_mode) {
+ int lowest_priority = 0;
+ struct of_phandle_args tc_args;
+
+ ecc->tc_list = devm_kcalloc(dev, ecc->num_tc,
+ sizeof(*ecc->tc_list), GFP_KERNEL);
+ if (!ecc->tc_list)
+ return -ENOMEM;
+
+ for (i = 0;; i++) {
+ ret = of_parse_phandle_with_fixed_args(node, "ti,tptcs",
+ 1, i, &tc_args);
+ if (ret || i == ecc->num_tc)
+ break;
+
+ ecc->tc_list[i].node = tc_args.np;
+ ecc->tc_list[i].id = i;
+ queue_priority_mapping[i][1] = tc_args.args[0];
+ if (queue_priority_mapping[i][1] > lowest_priority) {
+ lowest_priority = queue_priority_mapping[i][1];
+ info->default_queue = i;
+ }
+ }
+ }
+
/* Event queue priority mapping */
for (i = 0; queue_priority_mapping[i][0] != -1; i++)
edma_assign_priority_to_queue(ecc, queue_priority_mapping[i][0],
@@ -2125,7 +2282,7 @@ static int edma_probe(struct platform_device *pdev)
ecc->info = info;
/* Init the dma device and channels */
- edma_dma_init(ecc);
+ edma_dma_init(ecc, legacy_mode);
for (i = 0; i < ecc->num_channels; i++) {
/* Assign all channels to the default queue */
@@ -2136,12 +2293,23 @@ static int edma_probe(struct platform_device *pdev)
}
ret = dma_async_device_register(&ecc->dma_slave);
- if (ret)
+ if (ret) {
+ dev_err(dev, "slave ddev registration failed (%d)\n", ret);
goto err_reg1;
+ }
+
+ if (ecc->dma_memcpy) {
+ ret = dma_async_device_register(ecc->dma_memcpy);
+ if (ret) {
+ dev_err(dev, "memcpy ddev registration failed (%d)\n",
+ ret);
+ dma_async_device_unregister(&ecc->dma_slave);
+ goto err_reg1;
+ }
+ }
if (node)
- of_dma_controller_register(node, of_dma_xlate_by_chan_id,
- &ecc->dma_slave);
+ of_dma_controller_register(node, of_edma_xlate, ecc);
dev_info(dev, "TI EDMA DMA engine driver\n");
@@ -2160,12 +2328,30 @@ static int edma_remove(struct platform_device *pdev)
if (dev->of_node)
of_dma_controller_free(dev->of_node);
dma_async_device_unregister(&ecc->dma_slave);
+ if (ecc->dma_memcpy)
+ dma_async_device_unregister(ecc->dma_memcpy);
edma_free_slot(ecc, ecc->dummy_slot);
return 0;
}
#ifdef CONFIG_PM_SLEEP
+static int edma_pm_suspend(struct device *dev)
+{
+ struct edma_cc *ecc = dev_get_drvdata(dev);
+ struct edma_chan *echan = ecc->slave_chans;
+ int i;
+
+ for (i = 0; i < ecc->num_channels; i++) {
+ if (echan[i].alloced) {
+ edma_setup_interrupt(&echan[i], false);
+ edma_tc_set_pm_state(echan[i].tc, false);
+ }
+ }
+
+ return 0;
+}
+
static int edma_pm_resume(struct device *dev)
{
struct edma_cc *ecc = dev_get_drvdata(dev);
@@ -2190,6 +2376,8 @@ static int edma_pm_resume(struct device *dev)
/* Set up channel -> slot mapping for the entry slot */
edma_set_chmap(&echan[i], echan[i].slot[0]);
+
+ edma_tc_set_pm_state(echan[i].tc, true);
}
}
@@ -2198,7 +2386,7 @@ static int edma_pm_resume(struct device *dev)
#endif
static const struct dev_pm_ops edma_pm_ops = {
- SET_LATE_SYSTEM_SLEEP_PM_OPS(NULL, edma_pm_resume)
+ SET_LATE_SYSTEM_SLEEP_PM_OPS(edma_pm_suspend, edma_pm_resume)
};
static struct platform_driver edma_driver = {
@@ -2213,12 +2401,18 @@ static struct platform_driver edma_driver = {
bool edma_filter_fn(struct dma_chan *chan, void *param)
{
+ bool match = false;
+
if (chan->device->dev->driver == &edma_driver.driver) {
struct edma_chan *echan = to_edma_chan(chan);
unsigned ch_req = *(unsigned *)param;
- return ch_req == echan->ch_num;
+ if (ch_req == echan->ch_num) {
+ /* The channel is going to be used as HW synchronized */
+ echan->hw_triggered = true;
+ match = true;
+ }
}
- return false;
+ return match;
}
EXPORT_SYMBOL(edma_filter_fn);