diff options
Diffstat (limited to 'drivers/dma/sh/rz-dmac.c')
| -rw-r--r-- | drivers/dma/sh/rz-dmac.c | 148 |
1 files changed, 115 insertions, 33 deletions
diff --git a/drivers/dma/sh/rz-dmac.c b/drivers/dma/sh/rz-dmac.c index 476847a4916b..1f687b08d6b8 100644 --- a/drivers/dma/sh/rz-dmac.c +++ b/drivers/dma/sh/rz-dmac.c @@ -9,10 +9,12 @@ * Copyright 2012 Javier Martin, Vista Silicon <javier.martin@vista-silicon.com> */ +#include <linux/bitfield.h> #include <linux/dma-mapping.h> #include <linux/dmaengine.h> #include <linux/interrupt.h> #include <linux/iopoll.h> +#include <linux/irqchip/irq-renesas-rzv2h.h> #include <linux/list.h> #include <linux/module.h> #include <linux/of.h> @@ -20,6 +22,7 @@ #include <linux/of_platform.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> +#include <linux/reset.h> #include <linux/slab.h> #include <linux/spinlock.h> @@ -66,8 +69,6 @@ struct rz_dmac_chan { struct rz_dmac_desc *desc; int descs_allocated; - enum dma_slave_buswidth src_word_size; - enum dma_slave_buswidth dst_word_size; dma_addr_t src_per_address; dma_addr_t dst_per_address; @@ -89,15 +90,24 @@ struct rz_dmac_chan { #define to_rz_dmac_chan(c) container_of(c, struct rz_dmac_chan, vc.chan) +struct rz_dmac_icu { + struct platform_device *pdev; + u8 dmac_index; +}; + struct rz_dmac { struct dma_device engine; + struct rz_dmac_icu icu; struct device *dev; + struct reset_control *rstc; void __iomem *base; void __iomem *ext_base; unsigned int n_channels; struct rz_dmac_chan *channels; + bool has_icu; + DECLARE_BITMAP(modules, 1024); }; @@ -145,8 +155,8 @@ struct rz_dmac { #define CHCFG_REQD BIT(3) #define CHCFG_SEL(bits) ((bits) & 0x07) #define CHCFG_MEM_COPY (0x80400008) -#define CHCFG_FILL_DDS(a) (((a) << 16) & GENMASK(19, 16)) -#define CHCFG_FILL_SDS(a) (((a) << 12) & GENMASK(15, 12)) +#define CHCFG_FILL_DDS_MASK GENMASK(19, 16) +#define CHCFG_FILL_SDS_MASK GENMASK(15, 12) #define CHCFG_FILL_TM(a) (((a) & BIT(5)) << 22) #define CHCFG_FILL_AM(a) (((a) & GENMASK(4, 2)) << 6) #define CHCFG_FILL_LVL(a) (((a) & BIT(1)) << 5) @@ -166,6 +176,9 @@ struct rz_dmac { #define RZ_DMAC_MAX_CHANNELS 16 #define DMAC_NR_LMDESC 64 +/* RZ/V2H ICU related */ +#define RZV2H_MAX_DMAC_INDEX 4 + /* * ----------------------------------------------------------------------------- * Device access @@ -323,7 +336,13 @@ static void rz_dmac_prepare_desc_for_memcpy(struct rz_dmac_chan *channel) lmdesc->chext = 0; lmdesc->header = HEADER_LV; - rz_dmac_set_dmars_register(dmac, channel->index, 0); + if (dmac->has_icu) { + rzv2h_icu_register_dma_req(dmac->icu.pdev, dmac->icu.dmac_index, + channel->index, + RZV2H_ICU_DMAC_REQ_NO_DEFAULT); + } else { + rz_dmac_set_dmars_register(dmac, channel->index, 0); + } channel->chcfg = chcfg; channel->chctrl = CHCTRL_STG | CHCTRL_SETEN; @@ -374,7 +393,13 @@ static void rz_dmac_prepare_descs_for_slave_sg(struct rz_dmac_chan *channel) channel->lmdesc.tail = lmdesc; - rz_dmac_set_dmars_register(dmac, channel->index, channel->mid_rid); + if (dmac->has_icu) { + rzv2h_icu_register_dma_req(dmac->icu.pdev, dmac->icu.dmac_index, + channel->index, channel->mid_rid); + } else { + rz_dmac_set_dmars_register(dmac, channel->index, channel->mid_rid); + } + channel->chctrl = CHCTRL_SETEN; } @@ -539,8 +564,8 @@ static int rz_dmac_terminate_all(struct dma_chan *chan) spin_lock_irqsave(&channel->vc.lock, flags); list_splice_tail_init(&channel->ld_active, &channel->ld_free); list_splice_tail_init(&channel->ld_queue, &channel->ld_free); - spin_unlock_irqrestore(&channel->vc.lock, flags); vchan_get_all_descriptors(&channel->vc, &head); + spin_unlock_irqrestore(&channel->vc.lock, flags); vchan_dma_desc_free_list(&channel->vc, &head); return 0; @@ -600,22 +625,25 @@ static int rz_dmac_config(struct dma_chan *chan, struct rz_dmac_chan *channel = to_rz_dmac_chan(chan); u32 val; - channel->src_per_address = config->src_addr; - channel->src_word_size = config->src_addr_width; channel->dst_per_address = config->dst_addr; - channel->dst_word_size = config->dst_addr_width; + channel->chcfg &= ~CHCFG_FILL_DDS_MASK; + if (channel->dst_per_address) { + val = rz_dmac_ds_to_val_mapping(config->dst_addr_width); + if (val == CHCFG_DS_INVALID) + return -EINVAL; - val = rz_dmac_ds_to_val_mapping(config->dst_addr_width); - if (val == CHCFG_DS_INVALID) - return -EINVAL; - - channel->chcfg |= CHCFG_FILL_DDS(val); + channel->chcfg |= FIELD_PREP(CHCFG_FILL_DDS_MASK, val); + } - val = rz_dmac_ds_to_val_mapping(config->src_addr_width); - if (val == CHCFG_DS_INVALID) - return -EINVAL; + channel->src_per_address = config->src_addr; + channel->chcfg &= ~CHCFG_FILL_SDS_MASK; + if (channel->src_per_address) { + val = rz_dmac_ds_to_val_mapping(config->src_addr_width); + if (val == CHCFG_DS_INVALID) + return -EINVAL; - channel->chcfg |= CHCFG_FILL_SDS(val); + channel->chcfg |= FIELD_PREP(CHCFG_FILL_SDS_MASK, val); + } return 0; } @@ -643,7 +671,13 @@ static void rz_dmac_device_synchronize(struct dma_chan *chan) if (ret < 0) dev_warn(dmac->dev, "DMA Timeout"); - rz_dmac_set_dmars_register(dmac, channel->index, 0); + if (dmac->has_icu) { + rzv2h_icu_register_dma_req(dmac->icu.pdev, dmac->icu.dmac_index, + channel->index, + RZV2H_ICU_DMAC_REQ_NO_DEFAULT); + } else { + rz_dmac_set_dmars_register(dmac, channel->index, 0); + } } /* @@ -744,7 +778,8 @@ static struct dma_chan *rz_dmac_of_xlate(struct of_phandle_args *dma_spec, dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); - return dma_request_channel(mask, rz_dmac_chan_filter, dma_spec); + return __dma_request_channel(&mask, rz_dmac_chan_filter, dma_spec, + ofdma->of_node); } /* @@ -754,11 +789,11 @@ static struct dma_chan *rz_dmac_of_xlate(struct of_phandle_args *dma_spec, static int rz_dmac_chan_probe(struct rz_dmac *dmac, struct rz_dmac_chan *channel, - unsigned int index) + u8 index) { struct platform_device *pdev = to_platform_device(dmac->dev); struct rz_lmdesc *lmdesc; - char pdev_irqname[5]; + char pdev_irqname[6]; char *irqname; int ret; @@ -766,7 +801,7 @@ static int rz_dmac_chan_probe(struct rz_dmac *dmac, channel->mid_rid = -EINVAL; /* Request the channel interrupt. */ - sprintf(pdev_irqname, "ch%u", index); + scnprintf(pdev_irqname, sizeof(pdev_irqname), "ch%u", index); channel->irq = platform_get_irq_byname(pdev, pdev_irqname); if (channel->irq < 0) return channel->irq; @@ -819,6 +854,38 @@ static int rz_dmac_chan_probe(struct rz_dmac *dmac, return 0; } +static int rz_dmac_parse_of_icu(struct device *dev, struct rz_dmac *dmac) +{ + struct device_node *np = dev->of_node; + struct of_phandle_args args; + uint32_t dmac_index; + int ret; + + ret = of_parse_phandle_with_fixed_args(np, "renesas,icu", 1, 0, &args); + if (ret == -ENOENT) + return 0; + if (ret) + return ret; + + dmac->has_icu = true; + + dmac->icu.pdev = of_find_device_by_node(args.np); + of_node_put(args.np); + if (!dmac->icu.pdev) { + dev_err(dev, "ICU device not found.\n"); + return -ENODEV; + } + + dmac_index = args.args[0]; + if (dmac_index > RZV2H_MAX_DMAC_INDEX) { + dev_err(dev, "DMAC index %u invalid.\n", dmac_index); + return -EINVAL; + } + dmac->icu.dmac_index = dmac_index; + + return 0; +} + static int rz_dmac_parse_of(struct device *dev, struct rz_dmac *dmac) { struct device_node *np = dev->of_node; @@ -835,7 +902,7 @@ static int rz_dmac_parse_of(struct device *dev, struct rz_dmac *dmac) return -EINVAL; } - return 0; + return rz_dmac_parse_of_icu(dev, dmac); } static int rz_dmac_probe(struct platform_device *pdev) @@ -844,9 +911,9 @@ static int rz_dmac_probe(struct platform_device *pdev) struct dma_device *engine; struct rz_dmac *dmac; int channel_num; - unsigned int i; int ret; int irq; + u8 i; dmac = devm_kzalloc(&pdev->dev, sizeof(*dmac), GFP_KERNEL); if (!dmac) @@ -869,9 +936,11 @@ static int rz_dmac_probe(struct platform_device *pdev) if (IS_ERR(dmac->base)) return PTR_ERR(dmac->base); - dmac->ext_base = devm_platform_ioremap_resource(pdev, 1); - if (IS_ERR(dmac->ext_base)) - return PTR_ERR(dmac->ext_base); + if (!dmac->has_icu) { + dmac->ext_base = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(dmac->ext_base)) + return PTR_ERR(dmac->ext_base); + } /* Register interrupt handler for error */ irq = platform_get_irq_byname(pdev, irqname); @@ -889,6 +958,11 @@ static int rz_dmac_probe(struct platform_device *pdev) /* Initialize the channels. */ INIT_LIST_HEAD(&dmac->engine.channels); + dmac->rstc = devm_reset_control_array_get_optional_exclusive(&pdev->dev); + if (IS_ERR(dmac->rstc)) + return dev_err_probe(&pdev->dev, PTR_ERR(dmac->rstc), + "failed to get resets\n"); + pm_runtime_enable(&pdev->dev); ret = pm_runtime_resume_and_get(&pdev->dev); if (ret < 0) { @@ -896,6 +970,10 @@ static int rz_dmac_probe(struct platform_device *pdev) goto err_pm_disable; } + ret = reset_control_deassert(dmac->rstc); + if (ret) + goto err_pm_runtime_put; + for (i = 0; i < dmac->n_channels; i++) { ret = rz_dmac_chan_probe(dmac, &dmac->channels[i], i); if (ret < 0) @@ -950,6 +1028,8 @@ err: channel->lmdesc.base_dma); } + reset_control_assert(dmac->rstc); +err_pm_runtime_put: pm_runtime_put(&pdev->dev); err_pm_disable: pm_runtime_disable(&pdev->dev); @@ -957,11 +1037,13 @@ err_pm_disable: return ret; } -static int rz_dmac_remove(struct platform_device *pdev) +static void rz_dmac_remove(struct platform_device *pdev) { struct rz_dmac *dmac = platform_get_drvdata(pdev); unsigned int i; + dma_async_device_unregister(&dmac->engine); + of_dma_controller_free(pdev->dev.of_node); for (i = 0; i < dmac->n_channels; i++) { struct rz_dmac_chan *channel = &dmac->channels[i]; @@ -970,15 +1052,15 @@ static int rz_dmac_remove(struct platform_device *pdev) channel->lmdesc.base, channel->lmdesc.base_dma); } - of_dma_controller_free(pdev->dev.of_node); - dma_async_device_unregister(&dmac->engine); + reset_control_assert(dmac->rstc); pm_runtime_put(&pdev->dev); pm_runtime_disable(&pdev->dev); - return 0; + platform_device_put(dmac->icu.pdev); } static const struct of_device_id of_rz_dmac_match[] = { + { .compatible = "renesas,r9a09g057-dmac", }, { .compatible = "renesas,rz-dmac", }, { /* Sentinel */ } }; |
