Skip to content

Commit d5f88ac

Browse files
jones-drewjoergroedel
authored andcommitted
iommu/riscv: Add support for platform msi
Apply platform_device_msi_init_and_alloc_irqs() to add support for MSIs when the IOMMU is a platform device. Signed-off-by: Andrew Jones <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Joerg Roedel <[email protected]>
1 parent 78d4f34 commit d5f88ac

File tree

1 file changed

+84
-18
lines changed

1 file changed

+84
-18
lines changed

drivers/iommu/riscv/iommu-platform.c

Lines changed: 84 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,43 @@
1111
*/
1212

1313
#include <linux/kernel.h>
14+
#include <linux/msi.h>
15+
#include <linux/of_irq.h>
1416
#include <linux/of_platform.h>
1517
#include <linux/platform_device.h>
1618

1719
#include "iommu-bits.h"
1820
#include "iommu.h"
1921

22+
static void riscv_iommu_write_msi_msg(struct msi_desc *desc, struct msi_msg *msg)
23+
{
24+
struct device *dev = msi_desc_to_dev(desc);
25+
struct riscv_iommu_device *iommu = dev_get_drvdata(dev);
26+
u16 idx = desc->msi_index;
27+
u64 addr;
28+
29+
addr = ((u64)msg->address_hi << 32) | msg->address_lo;
30+
31+
if (addr != (addr & RISCV_IOMMU_MSI_CFG_TBL_ADDR)) {
32+
dev_err_once(dev,
33+
"uh oh, the IOMMU can't send MSIs to 0x%llx, sending to 0x%llx instead\n",
34+
addr, addr & RISCV_IOMMU_MSI_CFG_TBL_ADDR);
35+
}
36+
37+
addr &= RISCV_IOMMU_MSI_CFG_TBL_ADDR;
38+
39+
riscv_iommu_writeq(iommu, RISCV_IOMMU_REG_MSI_CFG_TBL_ADDR(idx), addr);
40+
riscv_iommu_writel(iommu, RISCV_IOMMU_REG_MSI_CFG_TBL_DATA(idx), msg->data);
41+
riscv_iommu_writel(iommu, RISCV_IOMMU_REG_MSI_CFG_TBL_CTRL(idx), 0);
42+
}
43+
2044
static int riscv_iommu_platform_probe(struct platform_device *pdev)
2145
{
46+
enum riscv_iommu_igs_settings igs;
2247
struct device *dev = &pdev->dev;
2348
struct riscv_iommu_device *iommu = NULL;
2449
struct resource *res = NULL;
25-
int vec;
50+
int vec, ret;
2651

2752
iommu = devm_kzalloc(dev, sizeof(*iommu), GFP_KERNEL);
2853
if (!iommu)
@@ -40,38 +65,79 @@ static int riscv_iommu_platform_probe(struct platform_device *pdev)
4065
iommu->caps = riscv_iommu_readq(iommu, RISCV_IOMMU_REG_CAPABILITIES);
4166
iommu->fctl = riscv_iommu_readl(iommu, RISCV_IOMMU_REG_FCTL);
4267

43-
/* For now we only support WSI */
44-
switch (FIELD_GET(RISCV_IOMMU_CAPABILITIES_IGS, iommu->caps)) {
45-
case RISCV_IOMMU_CAPABILITIES_IGS_WSI:
46-
case RISCV_IOMMU_CAPABILITIES_IGS_BOTH:
47-
break;
48-
default:
49-
return dev_err_probe(dev, -ENODEV,
50-
"unable to use wire-signaled interrupts\n");
51-
}
52-
5368
iommu->irqs_count = platform_irq_count(pdev);
5469
if (iommu->irqs_count <= 0)
5570
return dev_err_probe(dev, -ENODEV,
5671
"no IRQ resources provided\n");
5772
if (iommu->irqs_count > RISCV_IOMMU_INTR_COUNT)
5873
iommu->irqs_count = RISCV_IOMMU_INTR_COUNT;
5974

60-
for (vec = 0; vec < iommu->irqs_count; vec++)
61-
iommu->irqs[vec] = platform_get_irq(pdev, vec);
75+
igs = FIELD_GET(RISCV_IOMMU_CAPABILITIES_IGS, iommu->caps);
76+
switch (igs) {
77+
case RISCV_IOMMU_CAPABILITIES_IGS_BOTH:
78+
case RISCV_IOMMU_CAPABILITIES_IGS_MSI:
79+
if (is_of_node(dev->fwnode))
80+
of_msi_configure(dev, to_of_node(dev->fwnode));
81+
82+
if (!dev_get_msi_domain(dev)) {
83+
dev_warn(dev, "failed to find an MSI domain\n");
84+
goto msi_fail;
85+
}
86+
87+
ret = platform_device_msi_init_and_alloc_irqs(dev, iommu->irqs_count,
88+
riscv_iommu_write_msi_msg);
89+
if (ret) {
90+
dev_warn(dev, "failed to allocate MSIs\n");
91+
goto msi_fail;
92+
}
93+
94+
for (vec = 0; vec < iommu->irqs_count; vec++)
95+
iommu->irqs[vec] = msi_get_virq(dev, vec);
96+
97+
/* Enable message-signaled interrupts, fctl.WSI */
98+
if (iommu->fctl & RISCV_IOMMU_FCTL_WSI) {
99+
iommu->fctl ^= RISCV_IOMMU_FCTL_WSI;
100+
riscv_iommu_writel(iommu, RISCV_IOMMU_REG_FCTL, iommu->fctl);
101+
}
102+
103+
dev_info(dev, "using MSIs\n");
104+
break;
105+
106+
msi_fail:
107+
if (igs != RISCV_IOMMU_CAPABILITIES_IGS_BOTH) {
108+
return dev_err_probe(dev, -ENODEV,
109+
"unable to use wire-signaled interrupts\n");
110+
}
111+
112+
fallthrough;
62113

63-
/* Enable wire-signaled interrupts, fctl.WSI */
64-
if (!(iommu->fctl & RISCV_IOMMU_FCTL_WSI)) {
65-
iommu->fctl |= RISCV_IOMMU_FCTL_WSI;
66-
riscv_iommu_writel(iommu, RISCV_IOMMU_REG_FCTL, iommu->fctl);
114+
case RISCV_IOMMU_CAPABILITIES_IGS_WSI:
115+
for (vec = 0; vec < iommu->irqs_count; vec++)
116+
iommu->irqs[vec] = platform_get_irq(pdev, vec);
117+
118+
/* Enable wire-signaled interrupts, fctl.WSI */
119+
if (!(iommu->fctl & RISCV_IOMMU_FCTL_WSI)) {
120+
iommu->fctl |= RISCV_IOMMU_FCTL_WSI;
121+
riscv_iommu_writel(iommu, RISCV_IOMMU_REG_FCTL, iommu->fctl);
122+
}
123+
dev_info(dev, "using wire-signaled interrupts\n");
124+
break;
125+
default:
126+
return dev_err_probe(dev, -ENODEV, "invalid IGS\n");
67127
}
68128

69129
return riscv_iommu_init(iommu);
70130
};
71131

72132
static void riscv_iommu_platform_remove(struct platform_device *pdev)
73133
{
74-
riscv_iommu_remove(dev_get_drvdata(&pdev->dev));
134+
struct riscv_iommu_device *iommu = dev_get_drvdata(&pdev->dev);
135+
bool msi = !(iommu->fctl & RISCV_IOMMU_FCTL_WSI);
136+
137+
riscv_iommu_remove(iommu);
138+
139+
if (msi)
140+
platform_device_msi_free_irqs_all(&pdev->dev);
75141
};
76142

77143
static const struct of_device_id riscv_iommu_of_match[] = {

0 commit comments

Comments
 (0)