1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * Copyright (C) 2024-2025 ARM Limited, All Rights Reserved.
4 */
5 #define pr_fmt(fmt) "GICv5 IWB: " fmt
6
7 #include <linux/init.h>
8 #include <linux/kernel.h>
9 #include <linux/msi.h>
10 #include <linux/of.h>
11 #include <linux/of_address.h>
12 #include <linux/of_platform.h>
13
14 #include <linux/irqchip.h>
15 #include <linux/irqchip/arm-gic-v5.h>
16
17 struct gicv5_iwb_chip_data {
18 void __iomem *iwb_base;
19 u16 nr_regs;
20 };
21
iwb_readl_relaxed(struct gicv5_iwb_chip_data * iwb_node,const u32 reg_offset)22 static u32 iwb_readl_relaxed(struct gicv5_iwb_chip_data *iwb_node, const u32 reg_offset)
23 {
24 return readl_relaxed(iwb_node->iwb_base + reg_offset);
25 }
26
iwb_writel_relaxed(struct gicv5_iwb_chip_data * iwb_node,const u32 val,const u32 reg_offset)27 static void iwb_writel_relaxed(struct gicv5_iwb_chip_data *iwb_node, const u32 val,
28 const u32 reg_offset)
29 {
30 writel_relaxed(val, iwb_node->iwb_base + reg_offset);
31 }
32
gicv5_iwb_wait_for_wenabler(struct gicv5_iwb_chip_data * iwb_node)33 static int gicv5_iwb_wait_for_wenabler(struct gicv5_iwb_chip_data *iwb_node)
34 {
35 return gicv5_wait_for_op_atomic(iwb_node->iwb_base, GICV5_IWB_WENABLE_STATUSR,
36 GICV5_IWB_WENABLE_STATUSR_IDLE, NULL);
37 }
38
__gicv5_iwb_set_wire_enable(struct gicv5_iwb_chip_data * iwb_node,u32 iwb_wire,bool enable)39 static int __gicv5_iwb_set_wire_enable(struct gicv5_iwb_chip_data *iwb_node,
40 u32 iwb_wire, bool enable)
41 {
42 u32 n = iwb_wire / 32;
43 u8 i = iwb_wire % 32;
44 u32 val;
45
46 if (n >= iwb_node->nr_regs) {
47 pr_err("IWB_WENABLER<n> is invalid for n=%u\n", n);
48 return -EINVAL;
49 }
50
51 /*
52 * Enable IWB wire/pin at this point
53 * Note: This is not the same as enabling the interrupt
54 */
55 val = iwb_readl_relaxed(iwb_node, GICV5_IWB_WENABLER + (4 * n));
56 if (enable)
57 val |= BIT(i);
58 else
59 val &= ~BIT(i);
60 iwb_writel_relaxed(iwb_node, val, GICV5_IWB_WENABLER + (4 * n));
61
62 return gicv5_iwb_wait_for_wenabler(iwb_node);
63 }
64
gicv5_iwb_enable_wire(struct gicv5_iwb_chip_data * iwb_node,u32 iwb_wire)65 static int gicv5_iwb_enable_wire(struct gicv5_iwb_chip_data *iwb_node,
66 u32 iwb_wire)
67 {
68 return __gicv5_iwb_set_wire_enable(iwb_node, iwb_wire, true);
69 }
70
gicv5_iwb_disable_wire(struct gicv5_iwb_chip_data * iwb_node,u32 iwb_wire)71 static int gicv5_iwb_disable_wire(struct gicv5_iwb_chip_data *iwb_node,
72 u32 iwb_wire)
73 {
74 return __gicv5_iwb_set_wire_enable(iwb_node, iwb_wire, false);
75 }
76
gicv5_iwb_irq_disable(struct irq_data * d)77 static void gicv5_iwb_irq_disable(struct irq_data *d)
78 {
79 struct gicv5_iwb_chip_data *iwb_node = irq_data_get_irq_chip_data(d);
80
81 gicv5_iwb_disable_wire(iwb_node, d->hwirq);
82 irq_chip_disable_parent(d);
83 }
84
gicv5_iwb_irq_enable(struct irq_data * d)85 static void gicv5_iwb_irq_enable(struct irq_data *d)
86 {
87 struct gicv5_iwb_chip_data *iwb_node = irq_data_get_irq_chip_data(d);
88
89 gicv5_iwb_enable_wire(iwb_node, d->hwirq);
90 irq_chip_enable_parent(d);
91 }
92
gicv5_iwb_set_type(struct irq_data * d,unsigned int type)93 static int gicv5_iwb_set_type(struct irq_data *d, unsigned int type)
94 {
95 struct gicv5_iwb_chip_data *iwb_node = irq_data_get_irq_chip_data(d);
96 u32 iwb_wire, n, wtmr;
97 u8 i;
98
99 iwb_wire = d->hwirq;
100 i = iwb_wire % 32;
101 n = iwb_wire / 32;
102
103 if (n >= iwb_node->nr_regs) {
104 pr_err_once("reg %u out of range\n", n);
105 return -EINVAL;
106 }
107
108 wtmr = iwb_readl_relaxed(iwb_node, GICV5_IWB_WTMR + (4 * n));
109
110 switch (type) {
111 case IRQ_TYPE_LEVEL_HIGH:
112 case IRQ_TYPE_LEVEL_LOW:
113 wtmr |= BIT(i);
114 break;
115 case IRQ_TYPE_EDGE_RISING:
116 case IRQ_TYPE_EDGE_FALLING:
117 wtmr &= ~BIT(i);
118 break;
119 default:
120 pr_debug("unexpected wire trigger mode");
121 return -EINVAL;
122 }
123
124 iwb_writel_relaxed(iwb_node, wtmr, GICV5_IWB_WTMR + (4 * n));
125
126 return 0;
127 }
128
gicv5_iwb_domain_set_desc(msi_alloc_info_t * alloc_info,struct msi_desc * desc)129 static void gicv5_iwb_domain_set_desc(msi_alloc_info_t *alloc_info, struct msi_desc *desc)
130 {
131 alloc_info->desc = desc;
132 alloc_info->hwirq = (u32)desc->data.icookie.value;
133 }
134
gicv5_iwb_irq_domain_translate(struct irq_domain * d,struct irq_fwspec * fwspec,irq_hw_number_t * hwirq,unsigned int * type)135 static int gicv5_iwb_irq_domain_translate(struct irq_domain *d, struct irq_fwspec *fwspec,
136 irq_hw_number_t *hwirq,
137 unsigned int *type)
138 {
139 if (!is_of_node(fwspec->fwnode))
140 return -EINVAL;
141
142 if (fwspec->param_count < 2)
143 return -EINVAL;
144
145 /*
146 * param[0] is be the wire
147 * param[1] is the interrupt type
148 */
149 *hwirq = fwspec->param[0];
150 *type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK;
151
152 return 0;
153 }
154
gicv5_iwb_write_msi_msg(struct irq_data * d,struct msi_msg * msg)155 static void gicv5_iwb_write_msi_msg(struct irq_data *d, struct msi_msg *msg) {}
156
157 static const struct msi_domain_template iwb_msi_template = {
158 .chip = {
159 .name = "GICv5-IWB",
160 .irq_mask = irq_chip_mask_parent,
161 .irq_unmask = irq_chip_unmask_parent,
162 .irq_enable = gicv5_iwb_irq_enable,
163 .irq_disable = gicv5_iwb_irq_disable,
164 .irq_eoi = irq_chip_eoi_parent,
165 .irq_set_type = gicv5_iwb_set_type,
166 .irq_write_msi_msg = gicv5_iwb_write_msi_msg,
167 .irq_set_affinity = irq_chip_set_affinity_parent,
168 .irq_get_irqchip_state = irq_chip_get_parent_state,
169 .irq_set_irqchip_state = irq_chip_set_parent_state,
170 .flags = IRQCHIP_SET_TYPE_MASKED |
171 IRQCHIP_SKIP_SET_WAKE |
172 IRQCHIP_MASK_ON_SUSPEND,
173 },
174
175 .ops = {
176 .set_desc = gicv5_iwb_domain_set_desc,
177 .msi_translate = gicv5_iwb_irq_domain_translate,
178 },
179
180 .info = {
181 .bus_token = DOMAIN_BUS_WIRED_TO_MSI,
182 .flags = MSI_FLAG_USE_DEV_FWNODE,
183 },
184
185 .alloc_info = {
186 .flags = MSI_ALLOC_FLAGS_FIXED_MSG_DATA,
187 },
188 };
189
gicv5_iwb_create_device_domain(struct device * dev,unsigned int size,struct gicv5_iwb_chip_data * iwb_node)190 static bool gicv5_iwb_create_device_domain(struct device *dev, unsigned int size,
191 struct gicv5_iwb_chip_data *iwb_node)
192 {
193 if (WARN_ON_ONCE(!dev->msi.domain))
194 return false;
195
196 return msi_create_device_irq_domain(dev, MSI_DEFAULT_DOMAIN,
197 &iwb_msi_template, size,
198 NULL, iwb_node);
199 }
200
201 static struct gicv5_iwb_chip_data *
gicv5_iwb_init_bases(void __iomem * iwb_base,struct platform_device * pdev)202 gicv5_iwb_init_bases(void __iomem *iwb_base, struct platform_device *pdev)
203 {
204 u32 nr_wires, idr0, cr0;
205 unsigned int n;
206 int ret;
207
208 struct gicv5_iwb_chip_data *iwb_node __free(kfree) = kzalloc(sizeof(*iwb_node),
209 GFP_KERNEL);
210 if (!iwb_node)
211 return ERR_PTR(-ENOMEM);
212
213 iwb_node->iwb_base = iwb_base;
214
215 idr0 = iwb_readl_relaxed(iwb_node, GICV5_IWB_IDR0);
216 nr_wires = (FIELD_GET(GICV5_IWB_IDR0_IW_RANGE, idr0) + 1) * 32;
217
218 cr0 = iwb_readl_relaxed(iwb_node, GICV5_IWB_CR0);
219 if (!FIELD_GET(GICV5_IWB_CR0_IWBEN, cr0)) {
220 dev_err(&pdev->dev, "IWB must be enabled in firmware\n");
221 return ERR_PTR(-EINVAL);
222 }
223
224 iwb_node->nr_regs = FIELD_GET(GICV5_IWB_IDR0_IW_RANGE, idr0) + 1;
225
226 for (n = 0; n < iwb_node->nr_regs; n++)
227 iwb_writel_relaxed(iwb_node, 0, GICV5_IWB_WENABLER + (sizeof(u32) * n));
228
229 ret = gicv5_iwb_wait_for_wenabler(iwb_node);
230 if (ret)
231 return ERR_PTR(ret);
232
233 if (!gicv5_iwb_create_device_domain(&pdev->dev, nr_wires, iwb_node))
234 return ERR_PTR(-ENOMEM);
235
236 return_ptr(iwb_node);
237 }
238
gicv5_iwb_device_probe(struct platform_device * pdev)239 static int gicv5_iwb_device_probe(struct platform_device *pdev)
240 {
241 struct gicv5_iwb_chip_data *iwb_node;
242 void __iomem *iwb_base;
243 struct resource *res;
244
245 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
246 if (!res)
247 return -EINVAL;
248
249 iwb_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
250 if (!iwb_base) {
251 dev_err(&pdev->dev, "failed to ioremap %pR\n", res);
252 return -ENOMEM;
253 }
254
255 iwb_node = gicv5_iwb_init_bases(iwb_base, pdev);
256 if (IS_ERR(iwb_node))
257 return PTR_ERR(iwb_node);
258
259 return 0;
260 }
261
262 static const struct of_device_id gicv5_iwb_of_match[] = {
263 { .compatible = "arm,gic-v5-iwb" },
264 { /* END */ }
265 };
266 MODULE_DEVICE_TABLE(of, gicv5_iwb_of_match);
267
268 static struct platform_driver gicv5_iwb_platform_driver = {
269 .driver = {
270 .name = "GICv5 IWB",
271 .of_match_table = gicv5_iwb_of_match,
272 .suppress_bind_attrs = true,
273 },
274 .probe = gicv5_iwb_device_probe,
275 };
276
277 module_platform_driver(gicv5_iwb_platform_driver);
278