Skip to content

Commit

Permalink
Merge branch 'topic/qcom' into for-linus
Browse files Browse the repository at this point in the history
  • Loading branch information
Vinod Koul committed Dec 14, 2016
2 parents 83cb0dc + 75ff766 commit 90644ad
Show file tree
Hide file tree
Showing 7 changed files with 280 additions and 106 deletions.
12 changes: 9 additions & 3 deletions Documentation/devicetree/bindings/dma/qcom_hidma_mgmt.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ memcpy and memset capabilities. It has been designed for virtualized
environments.

Each HIDMA HW instance consists of multiple DMA channels. These channels
share the same bandwidth. The bandwidth utilization can be parititioned
share the same bandwidth. The bandwidth utilization can be partitioned
among channels based on the priority and weight assignments.

There are only two priority levels and 15 weigh assignments possible.

Other parameters here determine how much of the system bus this HIDMA
instance can use like maximum read/write request and and number of bytes to
instance can use like maximum read/write request and number of bytes to
read/write in a single burst.

Main node required properties:
Expand Down Expand Up @@ -47,12 +47,18 @@ When the OS is not in control of the management interface (i.e. it's a guest),
the channel nodes appear on their own, not under a management node.

Required properties:
- compatible: must contain "qcom,hidma-1.0"
- compatible: must contain "qcom,hidma-1.0" for initial HW or "qcom,hidma-1.1"
for MSI capable HW.
- reg: Addresses for the transfer and event channel
- interrupts: Should contain the event interrupt
- desc-count: Number of asynchronous requests this channel can handle
- iommus: required a iommu node

Optional properties for MSI:
- msi-parent : See the generic MSI binding described in
devicetree/bindings/interrupt-controller/msi.txt for a description of the
msi-parent property.

Example:

Hypervisor OS configuration:
Expand Down
173 changes: 161 additions & 12 deletions drivers/dma/qcom/hidma.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
#include <linux/irq.h>
#include <linux/atomic.h>
#include <linux/pm_runtime.h>
#include <linux/msi.h>

#include "../dmaengine.h"
#include "hidma.h"
Expand All @@ -70,6 +71,7 @@
#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)
{
Expand Down Expand Up @@ -553,6 +555,17 @@ static irqreturn_t hidma_chirq_handler(int chirq, void *arg)
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)
{
Expand All @@ -567,27 +580,139 @@ static ssize_t hidma_show_values(struct device *dev,
return strlen(buf);
}

static int hidma_create_sysfs_entry(struct hidma_dev *dev, char *name,
int mode)
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 -ENOMEM;
return NULL;

name_copy = devm_kstrdup(dev->ddev.dev, name, GFP_KERNEL);
if (!name_copy)
return -ENOMEM;
return NULL;

attrs->attr.name = name_copy;
attrs->attr.mode = mode;
attrs->show = hidma_show_values;
sysfs_attr_init(&attrs->attr);

return device_create_file(dev->ddev.dev, attrs);
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)
Expand All @@ -599,6 +724,7 @@ static int hidma_probe(struct platform_device *pdev)
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);
Expand Down Expand Up @@ -660,6 +786,12 @@ static int hidma_probe(struct platform_device *pdev)
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);

Expand Down Expand Up @@ -688,10 +820,17 @@ static int hidma_probe(struct platform_device *pdev)
goto dmafree;
}

rc = devm_request_irq(&pdev->dev, chirq, hidma_chirq_handler, 0,
"qcom-hidma", dmadev->lldev);
if (rc)
goto uninit;
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);
Expand All @@ -705,14 +844,16 @@ static int hidma_probe(struct platform_device *pdev)
dmadev->irq = chirq;
tasklet_init(&dmadev->task, hidma_issue_task, (unsigned long)dmadev);
hidma_debug_init(dmadev);
hidma_create_sysfs_entry(dmadev, "chid", S_IRUGO);
hidma_sysfs_init(dmadev);
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_put_autosuspend(dmadev->ddev.dev);
return 0;

uninit:
if (msi)
hidma_free_msis(dmadev);

hidma_debug_uninit(dmadev);
hidma_ll_uninit(dmadev->lldev);
dmafree:
Expand All @@ -730,8 +871,13 @@ static int hidma_remove(struct platform_device *pdev)

pm_runtime_get_sync(dmadev->ddev.dev);
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);
hidma_sysfs_uninit(dmadev);
hidma_debug_uninit(dmadev);
hidma_ll_uninit(dmadev->lldev);
hidma_free(dmadev);
Expand All @@ -746,12 +892,15 @@ static int hidma_remove(struct platform_device *pdev)
#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);
Expand Down
9 changes: 8 additions & 1 deletion drivers/dma/qcom/hidma.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ struct hidma_tre {
};

struct hidma_lldev {
bool msi_support; /* flag indicating MSI support */
bool initialized; /* initialized flag */
u8 trch_state; /* trch_state of the device */
u8 evch_state; /* evch_state of the device */
Expand All @@ -58,7 +59,7 @@ struct hidma_lldev {
void __iomem *evca; /* Event Channel address */
struct hidma_tre
**pending_tre_list; /* Pointers to pending TREs */
s32 pending_tre_count; /* Number of TREs pending */
atomic_t pending_tre_count; /* Number of TREs pending */

void *tre_ring; /* TRE ring */
dma_addr_t tre_dma; /* TRE ring to be shared with HW */
Expand Down Expand Up @@ -114,6 +115,7 @@ struct hidma_dev {
int irq;
int chidx;
u32 nr_descriptors;
int msi_virqbase;

struct hidma_lldev *lldev;
void __iomem *dev_trca;
Expand All @@ -128,6 +130,9 @@ struct hidma_dev {
struct dentry *debugfs;
struct dentry *stats;

/* sysfs entry for the channel id */
struct device_attribute *chid_attrs;

/* Task delivering issue_pending */
struct tasklet_struct task;
};
Expand All @@ -145,12 +150,14 @@ int hidma_ll_disable(struct hidma_lldev *lldev);
int hidma_ll_enable(struct hidma_lldev *llhndl);
void hidma_ll_set_transfer_params(struct hidma_lldev *llhndl, u32 tre_ch,
dma_addr_t src, dma_addr_t dest, u32 len, u32 flags);
void hidma_ll_setup_irq(struct hidma_lldev *lldev, bool msi);
int hidma_ll_setup(struct hidma_lldev *lldev);
struct hidma_lldev *hidma_ll_init(struct device *dev, u32 max_channels,
void __iomem *trca, void __iomem *evca,
u8 chidx);
int hidma_ll_uninit(struct hidma_lldev *llhndl);
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,
u8 err_code);
int hidma_debug_init(struct hidma_dev *dmadev);
Expand Down
4 changes: 2 additions & 2 deletions drivers/dma/qcom/hidma_dbg.c
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ static void hidma_ll_devstats(struct seq_file *s, void *llhndl)
seq_printf(s, "tre_ring_handle=%pap\n", &lldev->tre_dma);
seq_printf(s, "tre_ring_size = 0x%x\n", lldev->tre_ring_size);
seq_printf(s, "tre_processed_off = 0x%x\n", lldev->tre_processed_off);
seq_printf(s, "pending_tre_count=%d\n", lldev->pending_tre_count);
seq_printf(s, "pending_tre_count=%d\n",
atomic_read(&lldev->pending_tre_count));
seq_printf(s, "evca=%p\n", lldev->evca);
seq_printf(s, "evre_ring=%p\n", lldev->evre_ring);
seq_printf(s, "evre_ring_handle=%pap\n", &lldev->evre_dma);
Expand Down Expand Up @@ -164,7 +165,6 @@ static const struct file_operations hidma_dma_fops = {
void hidma_debug_uninit(struct hidma_dev *dmadev)
{
debugfs_remove_recursive(dmadev->debugfs);
debugfs_remove_recursive(dmadev->stats);
}

int hidma_debug_init(struct hidma_dev *dmadev)
Expand Down
Loading

0 comments on commit 90644ad

Please sign in to comment.