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+
2044static 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
72132static 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
77143static const struct of_device_id riscv_iommu_of_match [] = {
0 commit comments