xref: /linux/drivers/irqchip/irq-sg2042-msi.c (revision 7a9b709e7cc5ce1ffb84ce07bf6d157e1de758df)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * SG2042 MSI Controller
4  *
5  * Copyright (C) 2024 Sophgo Technology Inc.
6  * Copyright (C) 2024 Chen Wang <unicorn_wang@outlook.com>
7  */
8 
9 #include <linux/cleanup.h>
10 #include <linux/io.h>
11 #include <linux/irq.h>
12 #include <linux/irqdomain.h>
13 #include <linux/kernel.h>
14 #include <linux/module.h>
15 #include <linux/msi.h>
16 #include <linux/platform_device.h>
17 #include <linux/property.h>
18 #include <linux/slab.h>
19 
20 #include "irq-msi-lib.h"
21 
22 #define SG2042_MAX_MSI_VECTOR	32
23 
24 struct sg2042_msi_chipdata {
25 	void __iomem	*reg_clr;	// clear reg, see TRM, 10.1.33, GP_INTR0_CLR
26 
27 	phys_addr_t	doorbell_addr;	// see TRM, 10.1.32, GP_INTR0_SET
28 
29 	u32		irq_first;	// The vector number that MSIs starts
30 	u32		num_irqs;	// The number of vectors for MSIs
31 
32 	DECLARE_BITMAP(msi_map, SG2042_MAX_MSI_VECTOR);
33 	struct mutex	msi_map_lock;	// lock for msi_map
34 };
35 
36 static int sg2042_msi_allocate_hwirq(struct sg2042_msi_chipdata *data, int num_req)
37 {
38 	int first;
39 
40 	guard(mutex)(&data->msi_map_lock);
41 	first = bitmap_find_free_region(data->msi_map, data->num_irqs,
42 					get_count_order(num_req));
43 	return first >= 0 ? first : -ENOSPC;
44 }
45 
46 static void sg2042_msi_free_hwirq(struct sg2042_msi_chipdata *data, int hwirq, int num_req)
47 {
48 	guard(mutex)(&data->msi_map_lock);
49 	bitmap_release_region(data->msi_map, hwirq, get_count_order(num_req));
50 }
51 
52 static void sg2042_msi_irq_ack(struct irq_data *d)
53 {
54 	struct sg2042_msi_chipdata *data  = irq_data_get_irq_chip_data(d);
55 	int bit_off = d->hwirq;
56 
57 	writel(1 << bit_off, data->reg_clr);
58 
59 	irq_chip_ack_parent(d);
60 }
61 
62 static void sg2042_msi_irq_compose_msi_msg(struct irq_data *d, struct msi_msg *msg)
63 {
64 	struct sg2042_msi_chipdata *data = irq_data_get_irq_chip_data(d);
65 
66 	msg->address_hi = upper_32_bits(data->doorbell_addr);
67 	msg->address_lo = lower_32_bits(data->doorbell_addr);
68 	msg->data = 1 << d->hwirq;
69 }
70 
71 static const struct irq_chip sg2042_msi_middle_irq_chip = {
72 	.name			= "SG2042 MSI",
73 	.irq_ack		= sg2042_msi_irq_ack,
74 	.irq_mask		= irq_chip_mask_parent,
75 	.irq_unmask		= irq_chip_unmask_parent,
76 #ifdef CONFIG_SMP
77 	.irq_set_affinity	= irq_chip_set_affinity_parent,
78 #endif
79 	.irq_compose_msi_msg	= sg2042_msi_irq_compose_msi_msg,
80 };
81 
82 static int sg2042_msi_parent_domain_alloc(struct irq_domain *domain, unsigned int virq, int hwirq)
83 {
84 	struct sg2042_msi_chipdata *data = domain->host_data;
85 	struct irq_fwspec fwspec;
86 	struct irq_data *d;
87 	int ret;
88 
89 	fwspec.fwnode = domain->parent->fwnode;
90 	fwspec.param_count = 2;
91 	fwspec.param[0] = data->irq_first + hwirq;
92 	fwspec.param[1] = IRQ_TYPE_EDGE_RISING;
93 
94 	ret = irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec);
95 	if (ret)
96 		return ret;
97 
98 	d = irq_domain_get_irq_data(domain->parent, virq);
99 	return d->chip->irq_set_type(d, IRQ_TYPE_EDGE_RISING);
100 }
101 
102 static int sg2042_msi_middle_domain_alloc(struct irq_domain *domain, unsigned int virq,
103 					  unsigned int nr_irqs, void *args)
104 {
105 	struct sg2042_msi_chipdata *data = domain->host_data;
106 	int hwirq, err, i;
107 
108 	hwirq = sg2042_msi_allocate_hwirq(data, nr_irqs);
109 	if (hwirq < 0)
110 		return hwirq;
111 
112 	for (i = 0; i < nr_irqs; i++) {
113 		err = sg2042_msi_parent_domain_alloc(domain, virq + i, hwirq + i);
114 		if (err)
115 			goto err_hwirq;
116 
117 		irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
118 					      &sg2042_msi_middle_irq_chip, data);
119 	}
120 
121 	return 0;
122 
123 err_hwirq:
124 	sg2042_msi_free_hwirq(data, hwirq, nr_irqs);
125 	irq_domain_free_irqs_parent(domain, virq, i);
126 
127 	return err;
128 }
129 
130 static void sg2042_msi_middle_domain_free(struct irq_domain *domain, unsigned int virq,
131 					  unsigned int nr_irqs)
132 {
133 	struct irq_data *d = irq_domain_get_irq_data(domain, virq);
134 	struct sg2042_msi_chipdata *data = irq_data_get_irq_chip_data(d);
135 
136 	irq_domain_free_irqs_parent(domain, virq, nr_irqs);
137 	sg2042_msi_free_hwirq(data, d->hwirq, nr_irqs);
138 }
139 
140 static const struct irq_domain_ops sg2042_msi_middle_domain_ops = {
141 	.alloc	= sg2042_msi_middle_domain_alloc,
142 	.free	= sg2042_msi_middle_domain_free,
143 	.select	= msi_lib_irq_domain_select,
144 };
145 
146 #define SG2042_MSI_FLAGS_REQUIRED (MSI_FLAG_USE_DEF_DOM_OPS |	\
147 				   MSI_FLAG_USE_DEF_CHIP_OPS)
148 
149 #define SG2042_MSI_FLAGS_SUPPORTED MSI_GENERIC_FLAGS_MASK
150 
151 static const struct msi_parent_ops sg2042_msi_parent_ops = {
152 	.required_flags		= SG2042_MSI_FLAGS_REQUIRED,
153 	.supported_flags	= SG2042_MSI_FLAGS_SUPPORTED,
154 	.chip_flags		= MSI_CHIP_FLAG_SET_ACK,
155 	.bus_select_mask	= MATCH_PCI_MSI,
156 	.bus_select_token	= DOMAIN_BUS_NEXUS,
157 	.prefix			= "SG2042-",
158 	.init_dev_msi_info	= msi_lib_init_dev_msi_info,
159 };
160 
161 static int sg2042_msi_init_domains(struct sg2042_msi_chipdata *data,
162 				   struct irq_domain *plic_domain, struct device *dev)
163 {
164 	struct fwnode_handle *fwnode = dev_fwnode(dev);
165 	struct irq_domain *middle_domain;
166 
167 	middle_domain = irq_domain_create_hierarchy(plic_domain, 0, data->num_irqs, fwnode,
168 						    &sg2042_msi_middle_domain_ops, data);
169 	if (!middle_domain) {
170 		pr_err("Failed to create the MSI middle domain\n");
171 		return -ENOMEM;
172 	}
173 
174 	irq_domain_update_bus_token(middle_domain, DOMAIN_BUS_NEXUS);
175 
176 	middle_domain->flags |= IRQ_DOMAIN_FLAG_MSI_PARENT;
177 	middle_domain->msi_parent_ops = &sg2042_msi_parent_ops;
178 
179 	return 0;
180 }
181 
182 static int sg2042_msi_probe(struct platform_device *pdev)
183 {
184 	struct fwnode_reference_args args = { };
185 	struct sg2042_msi_chipdata *data;
186 	struct device *dev = &pdev->dev;
187 	struct irq_domain *plic_domain;
188 	struct resource *res;
189 	int ret;
190 
191 	data = devm_kzalloc(dev, sizeof(struct sg2042_msi_chipdata), GFP_KERNEL);
192 	if (!data)
193 		return -ENOMEM;
194 
195 	data->reg_clr = devm_platform_ioremap_resource_byname(pdev, "clr");
196 	if (IS_ERR(data->reg_clr)) {
197 		dev_err(dev, "Failed to map clear register\n");
198 		return PTR_ERR(data->reg_clr);
199 	}
200 
201 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "doorbell");
202 	if (!res) {
203 		dev_err(dev, "Failed get resource from set\n");
204 		return -EINVAL;
205 	}
206 	data->doorbell_addr = res->start;
207 
208 	ret = fwnode_property_get_reference_args(dev_fwnode(dev), "msi-ranges",
209 						 "#interrupt-cells", 0, 0, &args);
210 	if (ret) {
211 		dev_err(dev, "Unable to parse MSI vec base\n");
212 		return ret;
213 	}
214 	fwnode_handle_put(args.fwnode);
215 
216 	ret = fwnode_property_get_reference_args(dev_fwnode(dev), "msi-ranges", NULL,
217 						 args.nargs + 1, 0, &args);
218 	if (ret) {
219 		dev_err(dev, "Unable to parse MSI vec number\n");
220 		return ret;
221 	}
222 
223 	plic_domain = irq_find_matching_fwnode(args.fwnode, DOMAIN_BUS_ANY);
224 	fwnode_handle_put(args.fwnode);
225 	if (!plic_domain) {
226 		pr_err("Failed to find the PLIC domain\n");
227 		return -ENXIO;
228 	}
229 
230 	data->irq_first = (u32)args.args[0];
231 	data->num_irqs = (u32)args.args[args.nargs - 1];
232 
233 	mutex_init(&data->msi_map_lock);
234 
235 	return sg2042_msi_init_domains(data, plic_domain, dev);
236 }
237 
238 static const struct of_device_id sg2042_msi_of_match[] = {
239 	{ .compatible	= "sophgo,sg2042-msi" },
240 	{ }
241 };
242 
243 static struct platform_driver sg2042_msi_driver = {
244 	.driver = {
245 		.name		= "sg2042-msi",
246 		.of_match_table	= sg2042_msi_of_match,
247 	},
248 	.probe = sg2042_msi_probe,
249 };
250 builtin_platform_driver(sg2042_msi_driver);
251