123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985 |
- /*
- * Qualcomm Technologies HIDMA DMA engine interface
- *
- * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
- /*
- * Copyright (C) Freescale Semicondutor, Inc. 2007, 2008.
- * Copyright (C) Semihalf 2009
- * Copyright (C) Ilya Yanok, Emcraft Systems 2010
- * Copyright (C) Alexander Popov, Promcontroller 2014
- *
- * Written by Piotr Ziecik <kosmo@semihalf.com>. Hardware description
- * (defines, structures and comments) was taken from MPC5121 DMA driver
- * written by Hongjun Chen <hong-jun.chen@freescale.com>.
- *
- * Approved as OSADL project by a majority of OSADL members and funded
- * by OSADL membership fees in 2009; for details see www.osadl.org.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * The full GNU General Public License is included in this distribution in the
- * file called COPYING.
- */
- /* Linux Foundation elects GPLv2 license only. */
- #include <linux/dmaengine.h>
- #include <linux/dma-mapping.h>
- #include <linux/list.h>
- #include <linux/module.h>
- #include <linux/platform_device.h>
- #include <linux/slab.h>
- #include <linux/spinlock.h>
- #include <linux/of_dma.h>
- #include <linux/property.h>
- #include <linux/delay.h>
- #include <linux/acpi.h>
- #include <linux/irq.h>
- #include <linux/atomic.h>
- #include <linux/pm_runtime.h>
- #include <linux/msi.h>
- #include "../dmaengine.h"
- #include "hidma.h"
- /*
- * Default idle time is 2 seconds. This parameter can
- * be overridden by changing the following
- * /sys/bus/platform/devices/QCOM8061:<xy>/power/autosuspend_delay_ms
- * during kernel boot.
- */
- #define HIDMA_AUTOSUSPEND_TIMEOUT 2000
- #define HIDMA_ERR_INFO_SW 0xFF
- #define HIDMA_ERR_CODE_UNEXPECTED_TERMINATE 0x0
- #define HIDMA_NR_DEFAULT_DESC 10
- #define HIDMA_MSI_INTS 11
- static inline struct hidma_dev *to_hidma_dev(struct dma_device *dmadev)
- {
- return container_of(dmadev, struct hidma_dev, ddev);
- }
- static inline
- struct hidma_dev *to_hidma_dev_from_lldev(struct hidma_lldev **_lldevp)
- {
- return container_of(_lldevp, struct hidma_dev, lldev);
- }
- static inline struct hidma_chan *to_hidma_chan(struct dma_chan *dmach)
- {
- return container_of(dmach, struct hidma_chan, chan);
- }
- static inline
- struct hidma_desc *to_hidma_desc(struct dma_async_tx_descriptor *t)
- {
- return container_of(t, struct hidma_desc, desc);
- }
- static void hidma_free(struct hidma_dev *dmadev)
- {
- INIT_LIST_HEAD(&dmadev->ddev.channels);
- }
- static unsigned int nr_desc_prm;
- module_param(nr_desc_prm, uint, 0644);
- MODULE_PARM_DESC(nr_desc_prm, "number of descriptors (default: 0)");
- /* process completed descriptors */
- static void hidma_process_completed(struct hidma_chan *mchan)
- {
- struct dma_device *ddev = mchan->chan.device;
- struct hidma_dev *mdma = to_hidma_dev(ddev);
- struct dma_async_tx_descriptor *desc;
- dma_cookie_t last_cookie;
- struct hidma_desc *mdesc;
- struct hidma_desc *next;
- unsigned long irqflags;
- struct list_head list;
- INIT_LIST_HEAD(&list);
- /* Get all completed descriptors */
- spin_lock_irqsave(&mchan->lock, irqflags);
- list_splice_tail_init(&mchan->completed, &list);
- spin_unlock_irqrestore(&mchan->lock, irqflags);
- /* Execute callbacks and run dependencies */
- list_for_each_entry_safe(mdesc, next, &list, node) {
- enum dma_status llstat;
- struct dmaengine_desc_callback cb;
- struct dmaengine_result result;
- desc = &mdesc->desc;
- last_cookie = desc->cookie;
- llstat = hidma_ll_status(mdma->lldev, mdesc->tre_ch);
- spin_lock_irqsave(&mchan->lock, irqflags);
- if (llstat == DMA_COMPLETE) {
- mchan->last_success = last_cookie;
- result.result = DMA_TRANS_NOERROR;
- } else {
- result.result = DMA_TRANS_ABORTED;
- }
- dma_cookie_complete(desc);
- spin_unlock_irqrestore(&mchan->lock, irqflags);
- dmaengine_desc_get_callback(desc, &cb);
- dma_run_dependencies(desc);
- spin_lock_irqsave(&mchan->lock, irqflags);
- list_move(&mdesc->node, &mchan->free);
- spin_unlock_irqrestore(&mchan->lock, irqflags);
- dmaengine_desc_callback_invoke(&cb, &result);
- }
- }
- /*
- * Called once for each submitted descriptor.
- * PM is locked once for each descriptor that is currently
- * in execution.
- */
- static void hidma_callback(void *data)
- {
- struct hidma_desc *mdesc = data;
- struct hidma_chan *mchan = to_hidma_chan(mdesc->desc.chan);
- struct dma_device *ddev = mchan->chan.device;
- struct hidma_dev *dmadev = to_hidma_dev(ddev);
- unsigned long irqflags;
- bool queued = false;
- spin_lock_irqsave(&mchan->lock, irqflags);
- if (mdesc->node.next) {
- /* Delete from the active list, add to completed list */
- list_move_tail(&mdesc->node, &mchan->completed);
- queued = true;
- /* calculate the next running descriptor */
- mchan->running = list_first_entry(&mchan->active,
- struct hidma_desc, node);
- }
- spin_unlock_irqrestore(&mchan->lock, irqflags);
- hidma_process_completed(mchan);
- if (queued) {
- pm_runtime_mark_last_busy(dmadev->ddev.dev);
- pm_runtime_put_autosuspend(dmadev->ddev.dev);
- }
- }
- static int hidma_chan_init(struct hidma_dev *dmadev, u32 dma_sig)
- {
- struct hidma_chan *mchan;
- struct dma_device *ddev;
- mchan = devm_kzalloc(dmadev->ddev.dev, sizeof(*mchan), GFP_KERNEL);
- if (!mchan)
- return -ENOMEM;
- ddev = &dmadev->ddev;
- mchan->dma_sig = dma_sig;
- mchan->dmadev = dmadev;
- mchan->chan.device = ddev;
- dma_cookie_init(&mchan->chan);
- INIT_LIST_HEAD(&mchan->free);
- INIT_LIST_HEAD(&mchan->prepared);
- INIT_LIST_HEAD(&mchan->active);
- INIT_LIST_HEAD(&mchan->completed);
- INIT_LIST_HEAD(&mchan->queued);
- spin_lock_init(&mchan->lock);
- list_add_tail(&mchan->chan.device_node, &ddev->channels);
- dmadev->ddev.chancnt++;
- return 0;
- }
- static void hidma_issue_task(unsigned long arg)
- {
- struct hidma_dev *dmadev = (struct hidma_dev *)arg;
- pm_runtime_get_sync(dmadev->ddev.dev);
- hidma_ll_start(dmadev->lldev);
- }
- static void hidma_issue_pending(struct dma_chan *dmach)
- {
- struct hidma_chan *mchan = to_hidma_chan(dmach);
- struct hidma_dev *dmadev = mchan->dmadev;
- unsigned long flags;
- struct hidma_desc *qdesc, *next;
- int status;
- spin_lock_irqsave(&mchan->lock, flags);
- list_for_each_entry_safe(qdesc, next, &mchan->queued, node) {
- hidma_ll_queue_request(dmadev->lldev, qdesc->tre_ch);
- list_move_tail(&qdesc->node, &mchan->active);
- }
- if (!mchan->running) {
- struct hidma_desc *desc = list_first_entry(&mchan->active,
- struct hidma_desc,
- node);
- mchan->running = desc;
- }
- spin_unlock_irqrestore(&mchan->lock, flags);
- /* PM will be released in hidma_callback function. */
- status = pm_runtime_get(dmadev->ddev.dev);
- if (status < 0)
- tasklet_schedule(&dmadev->task);
- else
- hidma_ll_start(dmadev->lldev);
- }
- static inline bool hidma_txn_is_success(dma_cookie_t cookie,
- dma_cookie_t last_success, dma_cookie_t last_used)
- {
- if (last_success <= last_used) {
- if ((cookie <= last_success) || (cookie > last_used))
- return true;
- } else {
- if ((cookie <= last_success) && (cookie > last_used))
- return true;
- }
- return false;
- }
- static enum dma_status hidma_tx_status(struct dma_chan *dmach,
- dma_cookie_t cookie,
- struct dma_tx_state *txstate)
- {
- struct hidma_chan *mchan = to_hidma_chan(dmach);
- enum dma_status ret;
- ret = dma_cookie_status(dmach, cookie, txstate);
- if (ret == DMA_COMPLETE) {
- bool is_success;
- is_success = hidma_txn_is_success(cookie, mchan->last_success,
- dmach->cookie);
- return is_success ? ret : DMA_ERROR;
- }
- if (mchan->paused && (ret == DMA_IN_PROGRESS)) {
- unsigned long flags;
- dma_cookie_t runcookie;
- spin_lock_irqsave(&mchan->lock, flags);
- if (mchan->running)
- runcookie = mchan->running->desc.cookie;
- else
- runcookie = -EINVAL;
- if (runcookie == cookie)
- ret = DMA_PAUSED;
- spin_unlock_irqrestore(&mchan->lock, flags);
- }
- return ret;
- }
- /*
- * Submit descriptor to hardware.
- * Lock the PM for each descriptor we are sending.
- */
- static dma_cookie_t hidma_tx_submit(struct dma_async_tx_descriptor *txd)
- {
- struct hidma_chan *mchan = to_hidma_chan(txd->chan);
- struct hidma_dev *dmadev = mchan->dmadev;
- struct hidma_desc *mdesc;
- unsigned long irqflags;
- dma_cookie_t cookie;
- pm_runtime_get_sync(dmadev->ddev.dev);
- if (!hidma_ll_isenabled(dmadev->lldev)) {
- pm_runtime_mark_last_busy(dmadev->ddev.dev);
- pm_runtime_put_autosuspend(dmadev->ddev.dev);
- return -ENODEV;
- }
- pm_runtime_mark_last_busy(dmadev->ddev.dev);
- pm_runtime_put_autosuspend(dmadev->ddev.dev);
- mdesc = container_of(txd, struct hidma_desc, desc);
- spin_lock_irqsave(&mchan->lock, irqflags);
- /* Move descriptor to queued */
- list_move_tail(&mdesc->node, &mchan->queued);
- /* Update cookie */
- cookie = dma_cookie_assign(txd);
- spin_unlock_irqrestore(&mchan->lock, irqflags);
- return cookie;
- }
- static int hidma_alloc_chan_resources(struct dma_chan *dmach)
- {
- struct hidma_chan *mchan = to_hidma_chan(dmach);
- struct hidma_dev *dmadev = mchan->dmadev;
- struct hidma_desc *mdesc, *tmp;
- unsigned long irqflags;
- LIST_HEAD(descs);
- unsigned int i;
- int rc = 0;
- if (mchan->allocated)
- return 0;
- /* Alloc descriptors for this channel */
- for (i = 0; i < dmadev->nr_descriptors; i++) {
- mdesc = kzalloc(sizeof(struct hidma_desc), GFP_NOWAIT);
- if (!mdesc) {
- rc = -ENOMEM;
- break;
- }
- dma_async_tx_descriptor_init(&mdesc->desc, dmach);
- mdesc->desc.tx_submit = hidma_tx_submit;
- rc = hidma_ll_request(dmadev->lldev, mchan->dma_sig,
- "DMA engine", hidma_callback, mdesc,
- &mdesc->tre_ch);
- if (rc) {
- dev_err(dmach->device->dev,
- "channel alloc failed at %u\n", i);
- kfree(mdesc);
- break;
- }
- list_add_tail(&mdesc->node, &descs);
- }
- if (rc) {
- /* return the allocated descriptors */
- list_for_each_entry_safe(mdesc, tmp, &descs, node) {
- hidma_ll_free(dmadev->lldev, mdesc->tre_ch);
- kfree(mdesc);
- }
- return rc;
- }
- spin_lock_irqsave(&mchan->lock, irqflags);
- list_splice_tail_init(&descs, &mchan->free);
- mchan->allocated = true;
- spin_unlock_irqrestore(&mchan->lock, irqflags);
- return 1;
- }
- static struct dma_async_tx_descriptor *
- hidma_prep_dma_memcpy(struct dma_chan *dmach, dma_addr_t dest, dma_addr_t src,
- size_t len, unsigned long flags)
- {
- struct hidma_chan *mchan = to_hidma_chan(dmach);
- struct hidma_desc *mdesc = NULL;
- struct hidma_dev *mdma = mchan->dmadev;
- unsigned long irqflags;
- /* Get free descriptor */
- spin_lock_irqsave(&mchan->lock, irqflags);
- if (!list_empty(&mchan->free)) {
- mdesc = list_first_entry(&mchan->free, struct hidma_desc, node);
- list_del(&mdesc->node);
- }
- spin_unlock_irqrestore(&mchan->lock, irqflags);
- if (!mdesc)
- return NULL;
- mdesc->desc.flags = flags;
- hidma_ll_set_transfer_params(mdma->lldev, mdesc->tre_ch,
- src, dest, len, flags,
- HIDMA_TRE_MEMCPY);
- /* Place descriptor in prepared list */
- spin_lock_irqsave(&mchan->lock, irqflags);
- list_add_tail(&mdesc->node, &mchan->prepared);
- spin_unlock_irqrestore(&mchan->lock, irqflags);
- return &mdesc->desc;
- }
- static struct dma_async_tx_descriptor *
- hidma_prep_dma_memset(struct dma_chan *dmach, dma_addr_t dest, int value,
- size_t len, unsigned long flags)
- {
- struct hidma_chan *mchan = to_hidma_chan(dmach);
- struct hidma_desc *mdesc = NULL;
- struct hidma_dev *mdma = mchan->dmadev;
- unsigned long irqflags;
- /* Get free descriptor */
- spin_lock_irqsave(&mchan->lock, irqflags);
- if (!list_empty(&mchan->free)) {
- mdesc = list_first_entry(&mchan->free, struct hidma_desc, node);
- list_del(&mdesc->node);
- }
- spin_unlock_irqrestore(&mchan->lock, irqflags);
- if (!mdesc)
- return NULL;
- mdesc->desc.flags = flags;
- hidma_ll_set_transfer_params(mdma->lldev, mdesc->tre_ch,
- value, dest, len, flags,
- HIDMA_TRE_MEMSET);
- /* Place descriptor in prepared list */
- spin_lock_irqsave(&mchan->lock, irqflags);
- list_add_tail(&mdesc->node, &mchan->prepared);
- spin_unlock_irqrestore(&mchan->lock, irqflags);
- return &mdesc->desc;
- }
- static int hidma_terminate_channel(struct dma_chan *chan)
- {
- struct hidma_chan *mchan = to_hidma_chan(chan);
- struct hidma_dev *dmadev = to_hidma_dev(mchan->chan.device);
- struct hidma_desc *tmp, *mdesc;
- unsigned long irqflags;
- LIST_HEAD(list);
- int rc;
- pm_runtime_get_sync(dmadev->ddev.dev);
- /* give completed requests a chance to finish */
- hidma_process_completed(mchan);
- spin_lock_irqsave(&mchan->lock, irqflags);
- mchan->last_success = 0;
- list_splice_init(&mchan->active, &list);
- list_splice_init(&mchan->prepared, &list);
- list_splice_init(&mchan->completed, &list);
- list_splice_init(&mchan->queued, &list);
- spin_unlock_irqrestore(&mchan->lock, irqflags);
- /* this suspends the existing transfer */
- rc = hidma_ll_disable(dmadev->lldev);
- if (rc) {
- dev_err(dmadev->ddev.dev, "channel did not pause\n");
- goto out;
- }
- /* return all user requests */
- list_for_each_entry_safe(mdesc, tmp, &list, node) {
- struct dma_async_tx_descriptor *txd = &mdesc->desc;
- dma_descriptor_unmap(txd);
- dmaengine_desc_get_callback_invoke(txd, NULL);
- dma_run_dependencies(txd);
- /* move myself to free_list */
- list_move(&mdesc->node, &mchan->free);
- }
- rc = hidma_ll_enable(dmadev->lldev);
- out:
- pm_runtime_mark_last_busy(dmadev->ddev.dev);
- pm_runtime_put_autosuspend(dmadev->ddev.dev);
- return rc;
- }
- static int hidma_terminate_all(struct dma_chan *chan)
- {
- struct hidma_chan *mchan = to_hidma_chan(chan);
- struct hidma_dev *dmadev = to_hidma_dev(mchan->chan.device);
- int rc;
- rc = hidma_terminate_channel(chan);
- if (rc)
- return rc;
- /* reinitialize the hardware */
- pm_runtime_get_sync(dmadev->ddev.dev);
- rc = hidma_ll_setup(dmadev->lldev);
- pm_runtime_mark_last_busy(dmadev->ddev.dev);
- pm_runtime_put_autosuspend(dmadev->ddev.dev);
- return rc;
- }
- static void hidma_free_chan_resources(struct dma_chan *dmach)
- {
- struct hidma_chan *mchan = to_hidma_chan(dmach);
- struct hidma_dev *mdma = mchan->dmadev;
- struct hidma_desc *mdesc, *tmp;
- unsigned long irqflags;
- LIST_HEAD(descs);
- /* terminate running transactions and free descriptors */
- hidma_terminate_channel(dmach);
- spin_lock_irqsave(&mchan->lock, irqflags);
- /* Move data */
- list_splice_tail_init(&mchan->free, &descs);
- /* Free descriptors */
- list_for_each_entry_safe(mdesc, tmp, &descs, node) {
- hidma_ll_free(mdma->lldev, mdesc->tre_ch);
- list_del(&mdesc->node);
- kfree(mdesc);
- }
- mchan->allocated = 0;
- spin_unlock_irqrestore(&mchan->lock, irqflags);
- }
- static int hidma_pause(struct dma_chan *chan)
- {
- struct hidma_chan *mchan;
- struct hidma_dev *dmadev;
- mchan = to_hidma_chan(chan);
- dmadev = to_hidma_dev(mchan->chan.device);
- if (!mchan->paused) {
- pm_runtime_get_sync(dmadev->ddev.dev);
- if (hidma_ll_disable(dmadev->lldev))
- dev_warn(dmadev->ddev.dev, "channel did not stop\n");
- mchan->paused = true;
- pm_runtime_mark_last_busy(dmadev->ddev.dev);
- pm_runtime_put_autosuspend(dmadev->ddev.dev);
- }
- return 0;
- }
- static int hidma_resume(struct dma_chan *chan)
- {
- struct hidma_chan *mchan;
- struct hidma_dev *dmadev;
- int rc = 0;
- mchan = to_hidma_chan(chan);
- dmadev = to_hidma_dev(mchan->chan.device);
- if (mchan->paused) {
- pm_runtime_get_sync(dmadev->ddev.dev);
- rc = hidma_ll_enable(dmadev->lldev);
- if (!rc)
- mchan->paused = false;
- else
- dev_err(dmadev->ddev.dev,
- "failed to resume the channel");
- pm_runtime_mark_last_busy(dmadev->ddev.dev);
- pm_runtime_put_autosuspend(dmadev->ddev.dev);
- }
- return rc;
- }
- static irqreturn_t hidma_chirq_handler(int chirq, void *arg)
- {
- struct hidma_lldev *lldev = arg;
- /*
- * All interrupts are request driven.
- * HW doesn't send an interrupt by itself.
- */
- return hidma_ll_inthandler(chirq, lldev);
- }
- #ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN
- static irqreturn_t hidma_chirq_handler_msi(int chirq, void *arg)
- {
- struct hidma_lldev **lldevp = arg;
- struct hidma_dev *dmadev = to_hidma_dev_from_lldev(lldevp);
- return hidma_ll_inthandler_msi(chirq, *lldevp,
- 1 << (chirq - dmadev->msi_virqbase));
- }
- #endif
- static ssize_t hidma_show_values(struct device *dev,
- struct device_attribute *attr, char *buf)
- {
- struct platform_device *pdev = to_platform_device(dev);
- struct hidma_dev *mdev = platform_get_drvdata(pdev);
- buf[0] = 0;
- if (strcmp(attr->attr.name, "chid") == 0)
- sprintf(buf, "%d\n", mdev->chidx);
- return strlen(buf);
- }
- static inline void hidma_sysfs_uninit(struct hidma_dev *dev)
- {
- device_remove_file(dev->ddev.dev, dev->chid_attrs);
- }
- static struct device_attribute*
- hidma_create_sysfs_entry(struct hidma_dev *dev, char *name, int mode)
- {
- struct device_attribute *attrs;
- char *name_copy;
- attrs = devm_kmalloc(dev->ddev.dev, sizeof(struct device_attribute),
- GFP_KERNEL);
- if (!attrs)
- return NULL;
- name_copy = devm_kstrdup(dev->ddev.dev, name, GFP_KERNEL);
- if (!name_copy)
- return NULL;
- attrs->attr.name = name_copy;
- attrs->attr.mode = mode;
- attrs->show = hidma_show_values;
- sysfs_attr_init(&attrs->attr);
- return attrs;
- }
- static int hidma_sysfs_init(struct hidma_dev *dev)
- {
- dev->chid_attrs = hidma_create_sysfs_entry(dev, "chid", S_IRUGO);
- if (!dev->chid_attrs)
- return -ENOMEM;
- return device_create_file(dev->ddev.dev, dev->chid_attrs);
- }
- #ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN
- static void hidma_write_msi_msg(struct msi_desc *desc, struct msi_msg *msg)
- {
- struct device *dev = msi_desc_to_dev(desc);
- struct hidma_dev *dmadev = dev_get_drvdata(dev);
- if (!desc->platform.msi_index) {
- writel(msg->address_lo, dmadev->dev_evca + 0x118);
- writel(msg->address_hi, dmadev->dev_evca + 0x11C);
- writel(msg->data, dmadev->dev_evca + 0x120);
- }
- }
- #endif
- static void hidma_free_msis(struct hidma_dev *dmadev)
- {
- #ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN
- struct device *dev = dmadev->ddev.dev;
- struct msi_desc *desc;
- /* free allocated MSI interrupts above */
- for_each_msi_entry(desc, dev)
- devm_free_irq(dev, desc->irq, &dmadev->lldev);
- platform_msi_domain_free_irqs(dev);
- #endif
- }
- static int hidma_request_msi(struct hidma_dev *dmadev,
- struct platform_device *pdev)
- {
- #ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN
- int rc;
- struct msi_desc *desc;
- struct msi_desc *failed_desc = NULL;
- rc = platform_msi_domain_alloc_irqs(&pdev->dev, HIDMA_MSI_INTS,
- hidma_write_msi_msg);
- if (rc)
- return rc;
- for_each_msi_entry(desc, &pdev->dev) {
- if (!desc->platform.msi_index)
- dmadev->msi_virqbase = desc->irq;
- rc = devm_request_irq(&pdev->dev, desc->irq,
- hidma_chirq_handler_msi,
- 0, "qcom-hidma-msi",
- &dmadev->lldev);
- if (rc) {
- failed_desc = desc;
- break;
- }
- }
- if (rc) {
- /* free allocated MSI interrupts above */
- for_each_msi_entry(desc, &pdev->dev) {
- if (desc == failed_desc)
- break;
- devm_free_irq(&pdev->dev, desc->irq,
- &dmadev->lldev);
- }
- } else {
- /* Add callback to free MSIs on teardown */
- hidma_ll_setup_irq(dmadev->lldev, true);
- }
- if (rc)
- dev_warn(&pdev->dev,
- "failed to request MSI irq, falling back to wired IRQ\n");
- return rc;
- #else
- return -EINVAL;
- #endif
- }
- static bool hidma_msi_capable(struct device *dev)
- {
- struct acpi_device *adev = ACPI_COMPANION(dev);
- const char *of_compat;
- int ret = -EINVAL;
- if (!adev || acpi_disabled) {
- ret = device_property_read_string(dev, "compatible",
- &of_compat);
- if (ret)
- return false;
- ret = strcmp(of_compat, "qcom,hidma-1.1");
- } else {
- #ifdef CONFIG_ACPI
- ret = strcmp(acpi_device_hid(adev), "QCOM8062");
- #endif
- }
- return ret == 0;
- }
- static int hidma_probe(struct platform_device *pdev)
- {
- struct hidma_dev *dmadev;
- struct resource *trca_resource;
- struct resource *evca_resource;
- int chirq;
- void __iomem *evca;
- void __iomem *trca;
- int rc;
- bool msi;
- pm_runtime_set_autosuspend_delay(&pdev->dev, HIDMA_AUTOSUSPEND_TIMEOUT);
- pm_runtime_use_autosuspend(&pdev->dev);
- pm_runtime_set_active(&pdev->dev);
- pm_runtime_enable(&pdev->dev);
- trca_resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- trca = devm_ioremap_resource(&pdev->dev, trca_resource);
- if (IS_ERR(trca)) {
- rc = -ENOMEM;
- goto bailout;
- }
- evca_resource = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- evca = devm_ioremap_resource(&pdev->dev, evca_resource);
- if (IS_ERR(evca)) {
- rc = -ENOMEM;
- goto bailout;
- }
- /*
- * This driver only handles the channel IRQs.
- * Common IRQ is handled by the management driver.
- */
- chirq = platform_get_irq(pdev, 0);
- if (chirq < 0) {
- rc = -ENODEV;
- goto bailout;
- }
- dmadev = devm_kzalloc(&pdev->dev, sizeof(*dmadev), GFP_KERNEL);
- if (!dmadev) {
- rc = -ENOMEM;
- goto bailout;
- }
- INIT_LIST_HEAD(&dmadev->ddev.channels);
- spin_lock_init(&dmadev->lock);
- dmadev->ddev.dev = &pdev->dev;
- pm_runtime_get_sync(dmadev->ddev.dev);
- dma_cap_set(DMA_MEMCPY, dmadev->ddev.cap_mask);
- dma_cap_set(DMA_MEMSET, dmadev->ddev.cap_mask);
- if (WARN_ON(!pdev->dev.dma_mask)) {
- rc = -ENXIO;
- goto dmafree;
- }
- dmadev->dev_evca = evca;
- dmadev->evca_resource = evca_resource;
- dmadev->dev_trca = trca;
- dmadev->trca_resource = trca_resource;
- dmadev->ddev.device_prep_dma_memcpy = hidma_prep_dma_memcpy;
- dmadev->ddev.device_prep_dma_memset = hidma_prep_dma_memset;
- dmadev->ddev.device_alloc_chan_resources = hidma_alloc_chan_resources;
- dmadev->ddev.device_free_chan_resources = hidma_free_chan_resources;
- dmadev->ddev.device_tx_status = hidma_tx_status;
- dmadev->ddev.device_issue_pending = hidma_issue_pending;
- dmadev->ddev.device_pause = hidma_pause;
- dmadev->ddev.device_resume = hidma_resume;
- dmadev->ddev.device_terminate_all = hidma_terminate_all;
- dmadev->ddev.copy_align = 8;
- /*
- * Determine the MSI capability of the platform. Old HW doesn't
- * support MSI.
- */
- msi = hidma_msi_capable(&pdev->dev);
- device_property_read_u32(&pdev->dev, "desc-count",
- &dmadev->nr_descriptors);
- if (nr_desc_prm) {
- dev_info(&pdev->dev, "overriding number of descriptors as %d\n",
- nr_desc_prm);
- dmadev->nr_descriptors = nr_desc_prm;
- }
- if (!dmadev->nr_descriptors)
- dmadev->nr_descriptors = HIDMA_NR_DEFAULT_DESC;
- dmadev->chidx = readl(dmadev->dev_trca + 0x28);
- /* Set DMA mask to 64 bits. */
- rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
- if (rc) {
- dev_warn(&pdev->dev, "unable to set coherent mask to 64");
- rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
- if (rc)
- goto dmafree;
- }
- dmadev->lldev = hidma_ll_init(dmadev->ddev.dev,
- dmadev->nr_descriptors, dmadev->dev_trca,
- dmadev->dev_evca, dmadev->chidx);
- if (!dmadev->lldev) {
- rc = -EPROBE_DEFER;
- goto dmafree;
- }
- platform_set_drvdata(pdev, dmadev);
- if (msi)
- rc = hidma_request_msi(dmadev, pdev);
- if (!msi || rc) {
- hidma_ll_setup_irq(dmadev->lldev, false);
- rc = devm_request_irq(&pdev->dev, chirq, hidma_chirq_handler,
- 0, "qcom-hidma", dmadev->lldev);
- if (rc)
- goto uninit;
- }
- INIT_LIST_HEAD(&dmadev->ddev.channels);
- rc = hidma_chan_init(dmadev, 0);
- if (rc)
- goto uninit;
- rc = dma_async_device_register(&dmadev->ddev);
- if (rc)
- goto uninit;
- dmadev->irq = chirq;
- tasklet_init(&dmadev->task, hidma_issue_task, (unsigned long)dmadev);
- hidma_debug_init(dmadev);
- hidma_sysfs_init(dmadev);
- dev_info(&pdev->dev, "HI-DMA engine driver registration complete\n");
- pm_runtime_mark_last_busy(dmadev->ddev.dev);
- pm_runtime_put_autosuspend(dmadev->ddev.dev);
- return 0;
- uninit:
- if (msi)
- hidma_free_msis(dmadev);
- hidma_debug_uninit(dmadev);
- hidma_ll_uninit(dmadev->lldev);
- dmafree:
- if (dmadev)
- hidma_free(dmadev);
- bailout:
- pm_runtime_put_sync(&pdev->dev);
- pm_runtime_disable(&pdev->dev);
- return rc;
- }
- static void hidma_shutdown(struct platform_device *pdev)
- {
- struct hidma_dev *dmadev = platform_get_drvdata(pdev);
- dev_info(dmadev->ddev.dev, "HI-DMA engine shutdown\n");
- pm_runtime_get_sync(dmadev->ddev.dev);
- if (hidma_ll_disable(dmadev->lldev))
- dev_warn(dmadev->ddev.dev, "channel did not stop\n");
- pm_runtime_mark_last_busy(dmadev->ddev.dev);
- pm_runtime_put_autosuspend(dmadev->ddev.dev);
- }
- static int hidma_remove(struct platform_device *pdev)
- {
- struct hidma_dev *dmadev = platform_get_drvdata(pdev);
- pm_runtime_get_sync(dmadev->ddev.dev);
- dma_async_device_unregister(&dmadev->ddev);
- if (!dmadev->lldev->msi_support)
- devm_free_irq(dmadev->ddev.dev, dmadev->irq, dmadev->lldev);
- else
- hidma_free_msis(dmadev);
- tasklet_kill(&dmadev->task);
- hidma_sysfs_uninit(dmadev);
- hidma_debug_uninit(dmadev);
- hidma_ll_uninit(dmadev->lldev);
- hidma_free(dmadev);
- dev_info(&pdev->dev, "HI-DMA engine removed\n");
- pm_runtime_put_sync_suspend(&pdev->dev);
- pm_runtime_disable(&pdev->dev);
- return 0;
- }
- #if IS_ENABLED(CONFIG_ACPI)
- static const struct acpi_device_id hidma_acpi_ids[] = {
- {"QCOM8061"},
- {"QCOM8062"},
- {},
- };
- MODULE_DEVICE_TABLE(acpi, hidma_acpi_ids);
- #endif
- static const struct of_device_id hidma_match[] = {
- {.compatible = "qcom,hidma-1.0",},
- {.compatible = "qcom,hidma-1.1",},
- {},
- };
- MODULE_DEVICE_TABLE(of, hidma_match);
- static struct platform_driver hidma_driver = {
- .probe = hidma_probe,
- .remove = hidma_remove,
- .shutdown = hidma_shutdown,
- .driver = {
- .name = "hidma",
- .of_match_table = hidma_match,
- .acpi_match_table = ACPI_PTR(hidma_acpi_ids),
- },
- };
- module_platform_driver(hidma_driver);
- MODULE_LICENSE("GPL v2");
|