[PATCH] dmaengine: Add hisilicon k3 DMA engine driver
zhangfei gao
zhangfei.gao at gmail.com
Tue Jun 25 01:34:02 EDT 2013
On Fri, Jun 21, 2013 at 6:40 PM, Vinod Koul <vinod.koul at intel.com> wrote:
> On Mon, Jun 17, 2013 at 12:54:32PM +0800, Zhangfei Gao wrote:
>> Add dmaengine driver for hisilicon k3 platform based on virt_dma
>>
>> Signed-off-by: Zhangfei Gao <zhangfei.gao at linaro.org>
>> Tested-by: Kai Yang <jean.yangkai at huawei.com>
>> ---
> [snip]
>
>> +#define to_k3_dma(dmadev) container_of(dmadev, struct k3_dma_dev, slave)
>> +
>> +static struct k3_dma_chan *to_k3_chan(struct dma_chan *chan)
>> +{
>> + return container_of(chan, struct k3_dma_chan, vc.chan);
>> +}
>> +
>> +static void terminate_chan(struct k3_dma_phy *phy, struct k3_dma_dev *d)
> namespace pls
Thanks Vinod for careful review, will add namespace in v2.
>> +static void k3_dma_tasklet(unsigned long arg)
>> +{
>> + struct k3_dma_dev *d = (struct k3_dma_dev *)arg;
>> + struct k3_dma_phy *p;
>> + struct k3_dma_chan *c;
>> + unsigned pch, pch_alloc = 0;
>> +
>> + dev_dbg(d->slave.dev, "tasklet enter\n");
>> +
>> + /* check new dma request of running channel in vc->desc_issued */
>> + list_for_each_entry(c, &d->slave.channels, vc.chan.device_node) {
> this should use _safe, you might be adding a new txn while executing this
OK,
>> +static struct dma_async_tx_descriptor *k3_dma_prep_memcpy(
>> + struct dma_chan *chan, dma_addr_t dst, dma_addr_t src,
>> + size_t len, unsigned long flags)
>> +{
>> + struct k3_dma_chan *c = to_k3_chan(chan);
>> + struct k3_dma_dev *d = to_k3_dma(chan->device);
>> + struct k3_dma_desc_sw *ds;
>> + size_t copy = 0;
>> + int num_desc = 0;
>> +
>> + if (!len)
>> + return NULL;
>> +
>> + ds = kzalloc(sizeof(struct k3_dma_desc_sw), GFP_NOWAIT);
> sizeof (* ds) would be a better approach
OK, thanks
>> +static int k3_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
>> + unsigned long arg)
>> +{
>> + struct k3_dma_chan *c = to_k3_chan(chan);
>> + struct k3_dma_dev *d = to_k3_dma(chan->device);
>> + struct dma_slave_config *cfg = (void *)arg;
>> + struct k3_dma_phy *p = NULL;
>> + unsigned long flags;
>> + u32 maxburst = 0, val = 0;
>> + enum dma_slave_buswidth width = DMA_SLAVE_BUSWIDTH_UNDEFINED;
>> + LIST_HEAD(head);
>> +
>> + switch (cmd) {
>> + case DMA_SLAVE_CONFIG:
>> + if (cfg == NULL)
>> + return -EINVAL;
>> + c->dir = cfg->direction;
>> + if (c->dir == DMA_DEV_TO_MEM) {
>> + c->ccfg = CCFG_DSTINCR;
>> + c->dev_addr = cfg->src_addr;
>> + maxburst = cfg->src_maxburst;
>> + width = cfg->src_addr_width;
>> + } else if (c->dir == DMA_MEM_TO_DEV) {
>> + c->ccfg = CCFG_SRCINCR;
>> + c->dev_addr = cfg->dst_addr;
>> + maxburst = cfg->dst_maxburst;
>> + width = cfg->dst_addr_width;
>> + }
> looks like this could use some empty line above
>> +
>> + if (width == DMA_SLAVE_BUSWIDTH_1_BYTE)
>> + val = 0;
>> + else if (width == DMA_SLAVE_BUSWIDTH_2_BYTES)
>> + val = 1;
>> + else if (width == DMA_SLAVE_BUSWIDTH_4_BYTES)
>> + val = 2;
>> + else if (width == DMA_SLAVE_BUSWIDTH_8_BYTES)
>> + val = 3;
> and perhpas a switch case here or better a get_width macro
Will use switch instead for efficiency.
>> +
>> + INIT_LIST_HEAD(&d->slave.channels);
>> + dma_cap_set(DMA_SLAVE, d->slave.cap_mask);
>> + dma_cap_set(DMA_MEMCPY, d->slave.cap_mask);
>> + dma_cap_set(DMA_SLAVE, d->slave.cap_mask);
> DMA_SLAVE set twice?
Thanks for point out
>
>> + d->slave.dev = &op->dev;
>> + d->slave.device_alloc_chan_resources = k3_dma_alloc_chan_resources;
>> + d->slave.device_free_chan_resources = k3_dma_free_chan_resources;
>> + d->slave.device_tx_status = k3_dma_tx_status;
>> + d->slave.device_prep_dma_memcpy = k3_dma_prep_memcpy;
>> + d->slave.device_prep_slave_sg = k3_dma_prep_slave_sg;
>> + d->slave.device_issue_pending = k3_dma_issue_pending;
>> + d->slave.device_control = k3_dma_control;
>> + d->slave.copy_align = DMA_ALIGN;
>> + d->slave.chancnt = dma_channels;
>> +
>> + /* init virtual channel */
>> + for (i = 0; i < dma_channels; i++) {
>> + struct k3_dma_chan *c;
>> +
>> + c = devm_kzalloc(&op->dev,
>> + sizeof(struct k3_dma_chan), GFP_KERNEL);
>> + if (c == NULL)
>> + return -ENOMEM;
>> +
>> + INIT_LIST_HEAD(&c->node);
>> + c->vc.desc_free = k3_dma_free_desc;
>> + vchan_init(&c->vc, &d->slave);
>> + }
>> +
>> + /* Enable clock before accessing registers */
>> + ret = clk_prepare_enable(d->clk);
>> + if (ret < 0) {
>> + dev_err(&op->dev, "clk_prepare_enable failed: %d\n", ret);
>> + return -EINVAL;
>> + }
>> +
>> + trigger_dma(d, true);
> is this turning on dma, if so why at probe?
Will change the confusing name to enable_dma
>> +#ifdef CONFIG_PM
> PM_SLEEP?
>
>> +static int k3_dma_suspend(struct device *dev)
>> +{
>> + struct k3_dma_dev *d = dev_get_drvdata(dev);
>> + u32 stat = 0;
>> +
>> + stat = get_chan_stat(d);
>> + if (stat) {
>> + dev_warn(d->slave.dev,
>> + "chan %d is running fail to suspend\n", stat);
>> + return -1;
>> + }
>> + trigger_dma(d, false);
>> + clk_disable_unprepare(d->clk);
>> + return 0;
>> +}
>> +
>> +static int k3_dma_resume(struct device *dev)
>> +{
>> + struct k3_dma_dev *d = dev_get_drvdata(dev);
>> + int ret = 0;
>> +
>> + ret = clk_prepare_enable(d->clk);
>> + if (ret < 0) {
>> + dev_err(d->slave.dev, "clk_prepare_enable failed: %d\n", ret);
>> + return -EINVAL;
>> + }
>> + trigger_dma(d, true);
>> + return 0;
>> +}
>> +#else
>> +#define k3_dma_suspend NULL
>> +#define k3_dma_resume NULL
>> +#endif
> you can use SET_SYSTEM_SLEEP_PM_OPS macro instead
Make sense.
More information about the linux-arm-kernel
mailing list