11
11
*/
12
12
13
13
#include <linux/kernel.h>
14
+ #include <linux/msi.h>
15
+ #include <linux/of_irq.h>
14
16
#include <linux/of_platform.h>
15
17
#include <linux/platform_device.h>
16
18
17
19
#include "iommu-bits.h"
18
20
#include "iommu.h"
19
21
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
+
20
44
static int riscv_iommu_platform_probe (struct platform_device * pdev )
21
45
{
46
+ enum riscv_iommu_igs_settings igs ;
22
47
struct device * dev = & pdev -> dev ;
23
48
struct riscv_iommu_device * iommu = NULL ;
24
49
struct resource * res = NULL ;
25
- int vec ;
50
+ int vec , ret ;
26
51
27
52
iommu = devm_kzalloc (dev , sizeof (* iommu ), GFP_KERNEL );
28
53
if (!iommu )
@@ -40,38 +65,79 @@ static int riscv_iommu_platform_probe(struct platform_device *pdev)
40
65
iommu -> caps = riscv_iommu_readq (iommu , RISCV_IOMMU_REG_CAPABILITIES );
41
66
iommu -> fctl = riscv_iommu_readl (iommu , RISCV_IOMMU_REG_FCTL );
42
67
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
-
53
68
iommu -> irqs_count = platform_irq_count (pdev );
54
69
if (iommu -> irqs_count <= 0 )
55
70
return dev_err_probe (dev , - ENODEV ,
56
71
"no IRQ resources provided\n" );
57
72
if (iommu -> irqs_count > RISCV_IOMMU_INTR_COUNT )
58
73
iommu -> irqs_count = RISCV_IOMMU_INTR_COUNT ;
59
74
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 ;
62
113
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" );
67
127
}
68
128
69
129
return riscv_iommu_init (iommu );
70
130
};
71
131
72
132
static void riscv_iommu_platform_remove (struct platform_device * pdev )
73
133
{
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 );
75
141
};
76
142
77
143
static const struct of_device_id riscv_iommu_of_match [] = {
0 commit comments