mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-12 05:48:40 +00:00
dmaengine: qcom_hidma: add MSI support for interrupts
The interrupts can now be delivered as platform MSI interrupts on newer platforms. The code looks for a new OF and ACPI strings in order to enable the functionality. Signed-off-by: Sinan Kaya <okaya@codeaurora.org> Signed-off-by: Vinod Koul <vinod.koul@intel.com>
This commit is contained in:
parent
0e858f8d6f
commit
1c0e3e82a7
3 changed files with 147 additions and 6 deletions
|
@ -56,6 +56,7 @@
|
||||||
#include <linux/irq.h>
|
#include <linux/irq.h>
|
||||||
#include <linux/atomic.h>
|
#include <linux/atomic.h>
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
|
#include <linux/msi.h>
|
||||||
|
|
||||||
#include "../dmaengine.h"
|
#include "../dmaengine.h"
|
||||||
#include "hidma.h"
|
#include "hidma.h"
|
||||||
|
@ -70,6 +71,7 @@
|
||||||
#define HIDMA_ERR_INFO_SW 0xFF
|
#define HIDMA_ERR_INFO_SW 0xFF
|
||||||
#define HIDMA_ERR_CODE_UNEXPECTED_TERMINATE 0x0
|
#define HIDMA_ERR_CODE_UNEXPECTED_TERMINATE 0x0
|
||||||
#define HIDMA_NR_DEFAULT_DESC 10
|
#define HIDMA_NR_DEFAULT_DESC 10
|
||||||
|
#define HIDMA_MSI_INTS 11
|
||||||
|
|
||||||
static inline struct hidma_dev *to_hidma_dev(struct dma_device *dmadev)
|
static inline struct hidma_dev *to_hidma_dev(struct dma_device *dmadev)
|
||||||
{
|
{
|
||||||
|
@ -553,6 +555,15 @@ static irqreturn_t hidma_chirq_handler(int chirq, void *arg)
|
||||||
return hidma_ll_inthandler(chirq, lldev);
|
return hidma_ll_inthandler(chirq, lldev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
static ssize_t hidma_show_values(struct device *dev,
|
static ssize_t hidma_show_values(struct device *dev,
|
||||||
struct device_attribute *attr, char *buf)
|
struct device_attribute *attr, char *buf)
|
||||||
{
|
{
|
||||||
|
@ -590,6 +601,104 @@ static int hidma_create_sysfs_entry(struct hidma_dev *dev, char *name,
|
||||||
return device_create_file(dev->ddev.dev, attrs);
|
return device_create_file(dev->ddev.dev, 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)
|
static int hidma_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct hidma_dev *dmadev;
|
struct hidma_dev *dmadev;
|
||||||
|
@ -599,6 +708,7 @@ static int hidma_probe(struct platform_device *pdev)
|
||||||
void __iomem *evca;
|
void __iomem *evca;
|
||||||
void __iomem *trca;
|
void __iomem *trca;
|
||||||
int rc;
|
int rc;
|
||||||
|
bool msi;
|
||||||
|
|
||||||
pm_runtime_set_autosuspend_delay(&pdev->dev, HIDMA_AUTOSUSPEND_TIMEOUT);
|
pm_runtime_set_autosuspend_delay(&pdev->dev, HIDMA_AUTOSUSPEND_TIMEOUT);
|
||||||
pm_runtime_use_autosuspend(&pdev->dev);
|
pm_runtime_use_autosuspend(&pdev->dev);
|
||||||
|
@ -660,6 +770,12 @@ static int hidma_probe(struct platform_device *pdev)
|
||||||
dmadev->ddev.device_terminate_all = hidma_terminate_all;
|
dmadev->ddev.device_terminate_all = hidma_terminate_all;
|
||||||
dmadev->ddev.copy_align = 8;
|
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",
|
device_property_read_u32(&pdev->dev, "desc-count",
|
||||||
&dmadev->nr_descriptors);
|
&dmadev->nr_descriptors);
|
||||||
|
|
||||||
|
@ -688,10 +804,17 @@ static int hidma_probe(struct platform_device *pdev)
|
||||||
goto dmafree;
|
goto dmafree;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = devm_request_irq(&pdev->dev, chirq, hidma_chirq_handler, 0,
|
platform_set_drvdata(pdev, dmadev);
|
||||||
"qcom-hidma", dmadev->lldev);
|
if (msi)
|
||||||
if (rc)
|
rc = hidma_request_msi(dmadev, pdev);
|
||||||
goto uninit;
|
|
||||||
|
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);
|
INIT_LIST_HEAD(&dmadev->ddev.channels);
|
||||||
rc = hidma_chan_init(dmadev, 0);
|
rc = hidma_chan_init(dmadev, 0);
|
||||||
|
@ -707,12 +830,14 @@ static int hidma_probe(struct platform_device *pdev)
|
||||||
hidma_debug_init(dmadev);
|
hidma_debug_init(dmadev);
|
||||||
hidma_create_sysfs_entry(dmadev, "chid", S_IRUGO);
|
hidma_create_sysfs_entry(dmadev, "chid", S_IRUGO);
|
||||||
dev_info(&pdev->dev, "HI-DMA engine driver registration complete\n");
|
dev_info(&pdev->dev, "HI-DMA engine driver registration complete\n");
|
||||||
platform_set_drvdata(pdev, dmadev);
|
|
||||||
pm_runtime_mark_last_busy(dmadev->ddev.dev);
|
pm_runtime_mark_last_busy(dmadev->ddev.dev);
|
||||||
pm_runtime_put_autosuspend(dmadev->ddev.dev);
|
pm_runtime_put_autosuspend(dmadev->ddev.dev);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
uninit:
|
uninit:
|
||||||
|
if (msi)
|
||||||
|
hidma_free_msis(dmadev);
|
||||||
|
|
||||||
hidma_debug_uninit(dmadev);
|
hidma_debug_uninit(dmadev);
|
||||||
hidma_ll_uninit(dmadev->lldev);
|
hidma_ll_uninit(dmadev->lldev);
|
||||||
dmafree:
|
dmafree:
|
||||||
|
@ -730,7 +855,11 @@ static int hidma_remove(struct platform_device *pdev)
|
||||||
|
|
||||||
pm_runtime_get_sync(dmadev->ddev.dev);
|
pm_runtime_get_sync(dmadev->ddev.dev);
|
||||||
dma_async_device_unregister(&dmadev->ddev);
|
dma_async_device_unregister(&dmadev->ddev);
|
||||||
devm_free_irq(dmadev->ddev.dev, dmadev->irq, dmadev->lldev);
|
if (!dmadev->lldev->msi_support)
|
||||||
|
devm_free_irq(dmadev->ddev.dev, dmadev->irq, dmadev->lldev);
|
||||||
|
else
|
||||||
|
hidma_free_msis(dmadev);
|
||||||
|
|
||||||
tasklet_kill(&dmadev->task);
|
tasklet_kill(&dmadev->task);
|
||||||
hidma_debug_uninit(dmadev);
|
hidma_debug_uninit(dmadev);
|
||||||
hidma_ll_uninit(dmadev->lldev);
|
hidma_ll_uninit(dmadev->lldev);
|
||||||
|
@ -746,12 +875,14 @@ static int hidma_remove(struct platform_device *pdev)
|
||||||
#if IS_ENABLED(CONFIG_ACPI)
|
#if IS_ENABLED(CONFIG_ACPI)
|
||||||
static const struct acpi_device_id hidma_acpi_ids[] = {
|
static const struct acpi_device_id hidma_acpi_ids[] = {
|
||||||
{"QCOM8061"},
|
{"QCOM8061"},
|
||||||
|
{"QCOM8062"},
|
||||||
{},
|
{},
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static const struct of_device_id hidma_match[] = {
|
static const struct of_device_id hidma_match[] = {
|
||||||
{.compatible = "qcom,hidma-1.0",},
|
{.compatible = "qcom,hidma-1.0",},
|
||||||
|
{.compatible = "qcom,hidma-1.1",},
|
||||||
{},
|
{},
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, hidma_match);
|
MODULE_DEVICE_TABLE(of, hidma_match);
|
||||||
|
|
|
@ -115,6 +115,7 @@ struct hidma_dev {
|
||||||
int irq;
|
int irq;
|
||||||
int chidx;
|
int chidx;
|
||||||
u32 nr_descriptors;
|
u32 nr_descriptors;
|
||||||
|
int msi_virqbase;
|
||||||
|
|
||||||
struct hidma_lldev *lldev;
|
struct hidma_lldev *lldev;
|
||||||
void __iomem *dev_trca;
|
void __iomem *dev_trca;
|
||||||
|
@ -153,6 +154,7 @@ struct hidma_lldev *hidma_ll_init(struct device *dev, u32 max_channels,
|
||||||
u8 chidx);
|
u8 chidx);
|
||||||
int hidma_ll_uninit(struct hidma_lldev *llhndl);
|
int hidma_ll_uninit(struct hidma_lldev *llhndl);
|
||||||
irqreturn_t hidma_ll_inthandler(int irq, void *arg);
|
irqreturn_t hidma_ll_inthandler(int irq, void *arg);
|
||||||
|
irqreturn_t hidma_ll_inthandler_msi(int irq, void *arg, int cause);
|
||||||
void hidma_cleanup_pending_tre(struct hidma_lldev *llhndl, u8 err_info,
|
void hidma_cleanup_pending_tre(struct hidma_lldev *llhndl, u8 err_info,
|
||||||
u8 err_code);
|
u8 err_code);
|
||||||
int hidma_debug_init(struct hidma_dev *dmadev);
|
int hidma_debug_init(struct hidma_dev *dmadev);
|
||||||
|
|
|
@ -457,6 +457,14 @@ irqreturn_t hidma_ll_inthandler(int chirq, void *arg)
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
irqreturn_t hidma_ll_inthandler_msi(int chirq, void *arg, int cause)
|
||||||
|
{
|
||||||
|
struct hidma_lldev *lldev = arg;
|
||||||
|
|
||||||
|
hidma_ll_int_handler_internal(lldev, cause);
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
int hidma_ll_enable(struct hidma_lldev *lldev)
|
int hidma_ll_enable(struct hidma_lldev *lldev)
|
||||||
{
|
{
|
||||||
u32 val;
|
u32 val;
|
||||||
|
|
Loading…
Reference in a new issue