Commit | Line | Data |
---|---|---|
2874c5fd | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
4dea2d1a SB |
2 | /* |
3 | * Copyright 2016-17 IBM Corp. | |
4dea2d1a SB |
4 | */ |
5 | ||
6 | #define pr_fmt(fmt) "vas: " fmt | |
7 | ||
8 | #include <linux/module.h> | |
9 | #include <linux/kernel.h> | |
10 | #include <linux/export.h> | |
11 | #include <linux/types.h> | |
12 | #include <linux/slab.h> | |
13 | #include <linux/platform_device.h> | |
14 | #include <linux/of_platform.h> | |
15 | #include <linux/of_address.h> | |
16 | #include <linux/of.h> | |
9774628a HM |
17 | #include <linux/irqdomain.h> |
18 | #include <linux/interrupt.h> | |
ca03258b | 19 | #include <asm/prom.h> |
c20e1e29 | 20 | #include <asm/xive.h> |
4dea2d1a SB |
21 | |
22 | #include "vas.h" | |
23 | ||
ece4e512 | 24 | DEFINE_MUTEX(vas_mutex); |
4dea2d1a SB |
25 | static LIST_HEAD(vas_instances); |
26 | ||
ca03258b SB |
27 | static DEFINE_PER_CPU(int, cpu_vas_id); |
28 | ||
0d17de03 HM |
29 | static int vas_irq_fault_window_setup(struct vas_instance *vinst) |
30 | { | |
9774628a HM |
31 | int rc = 0; |
32 | ||
9774628a | 33 | rc = request_threaded_irq(vinst->virq, vas_fault_handler, |
9dd31b11 | 34 | vas_fault_thread_fn, 0, vinst->name, vinst); |
9774628a HM |
35 | |
36 | if (rc) { | |
37 | pr_err("VAS[%d]: Request IRQ(%d) failed with %d\n", | |
38 | vinst->vas_id, vinst->virq, rc); | |
39 | goto out; | |
40 | } | |
41 | ||
42 | rc = vas_setup_fault_window(vinst); | |
43 | if (rc) | |
44 | free_irq(vinst->virq, vinst); | |
45 | ||
46 | out: | |
47 | return rc; | |
0d17de03 HM |
48 | } |
49 | ||
4dea2d1a SB |
50 | static int init_vas_instance(struct platform_device *pdev) |
51 | { | |
4dea2d1a | 52 | struct device_node *dn = pdev->dev.of_node; |
c20e1e29 HM |
53 | struct vas_instance *vinst; |
54 | struct xive_irq_data *xd; | |
55 | uint32_t chipid, hwirq; | |
56 | struct resource *res; | |
57 | int rc, cpu, vasid; | |
4dea2d1a SB |
58 | |
59 | rc = of_property_read_u32(dn, "ibm,vas-id", &vasid); | |
60 | if (rc) { | |
61 | pr_err("No ibm,vas-id property for %s?\n", pdev->name); | |
62 | return -ENODEV; | |
63 | } | |
64 | ||
c20e1e29 HM |
65 | rc = of_property_read_u32(dn, "ibm,chip-id", &chipid); |
66 | if (rc) { | |
67 | pr_err("No ibm,chip-id property for %s?\n", pdev->name); | |
68 | return -ENODEV; | |
69 | } | |
70 | ||
4dea2d1a SB |
71 | if (pdev->num_resources != 4) { |
72 | pr_err("Unexpected DT configuration for [%s, %d]\n", | |
73 | pdev->name, vasid); | |
74 | return -ENODEV; | |
75 | } | |
76 | ||
77 | vinst = kzalloc(sizeof(*vinst), GFP_KERNEL); | |
78 | if (!vinst) | |
79 | return -ENOMEM; | |
80 | ||
9dd31b11 CLG |
81 | vinst->name = kasprintf(GFP_KERNEL, "vas-%d", vasid); |
82 | if (!vinst->name) { | |
83 | kfree(vinst); | |
84 | return -ENOMEM; | |
85 | } | |
86 | ||
4dea2d1a SB |
87 | INIT_LIST_HEAD(&vinst->node); |
88 | ida_init(&vinst->ida); | |
89 | mutex_init(&vinst->mutex); | |
90 | vinst->vas_id = vasid; | |
91 | vinst->pdev = pdev; | |
92 | ||
93 | res = &pdev->resource[0]; | |
94 | vinst->hvwc_bar_start = res->start; | |
95 | ||
96 | res = &pdev->resource[1]; | |
97 | vinst->uwc_bar_start = res->start; | |
98 | ||
99 | res = &pdev->resource[2]; | |
100 | vinst->paste_base_addr = res->start; | |
101 | ||
102 | res = &pdev->resource[3]; | |
103 | if (res->end > 62) { | |
104 | pr_err("Bad 'paste_win_id_shift' in DT, %llx\n", res->end); | |
105 | goto free_vinst; | |
106 | } | |
107 | ||
108 | vinst->paste_win_id_shift = 63 - res->end; | |
109 | ||
c20e1e29 HM |
110 | hwirq = xive_native_alloc_irq_on_chip(chipid); |
111 | if (!hwirq) { | |
112 | pr_err("Inst%d: Unable to allocate global irq for chip %d\n", | |
113 | vinst->vas_id, chipid); | |
114 | return -ENOENT; | |
115 | } | |
116 | ||
117 | vinst->virq = irq_create_mapping(NULL, hwirq); | |
118 | if (!vinst->virq) { | |
119 | pr_err("Inst%d: Unable to map global irq %d\n", | |
120 | vinst->vas_id, hwirq); | |
121 | return -EINVAL; | |
122 | } | |
123 | ||
124 | xd = irq_get_handler_data(vinst->virq); | |
125 | if (!xd) { | |
126 | pr_err("Inst%d: Invalid virq %d\n", | |
127 | vinst->vas_id, vinst->virq); | |
128 | return -EINVAL; | |
129 | } | |
130 | ||
131 | vinst->irq_port = xd->trig_page; | |
132 | pr_devel("Initialized instance [%s, %d] paste_base 0x%llx paste_win_id_shift 0x%llx IRQ %d Port 0x%llx\n", | |
133 | pdev->name, vasid, vinst->paste_base_addr, | |
134 | vinst->paste_win_id_shift, vinst->virq, | |
135 | vinst->irq_port); | |
4dea2d1a | 136 | |
ca03258b SB |
137 | for_each_possible_cpu(cpu) { |
138 | if (cpu_to_chip_id(cpu) == of_get_ibm_chip_id(dn)) | |
139 | per_cpu(cpu_vas_id, cpu) = vasid; | |
140 | } | |
141 | ||
4dea2d1a SB |
142 | mutex_lock(&vas_mutex); |
143 | list_add(&vinst->node, &vas_instances); | |
144 | mutex_unlock(&vas_mutex); | |
145 | ||
9774628a | 146 | spin_lock_init(&vinst->fault_lock); |
0d17de03 HM |
147 | /* |
148 | * IRQ and fault handling setup is needed only for user space | |
149 | * send windows. | |
150 | */ | |
151 | if (vinst->virq) { | |
152 | rc = vas_irq_fault_window_setup(vinst); | |
153 | /* | |
154 | * Fault window is used only for user space send windows. | |
155 | * So if vinst->virq is NULL, tx_win_open returns -ENODEV | |
156 | * for user space. | |
157 | */ | |
158 | if (rc) | |
159 | vinst->virq = 0; | |
160 | } | |
161 | ||
ece4e512 SB |
162 | vas_instance_init_dbgdir(vinst); |
163 | ||
4dea2d1a SB |
164 | dev_set_drvdata(&pdev->dev, vinst); |
165 | ||
166 | return 0; | |
167 | ||
168 | free_vinst: | |
9dd31b11 | 169 | kfree(vinst->name); |
4dea2d1a SB |
170 | kfree(vinst); |
171 | return -ENODEV; | |
172 | ||
173 | } | |
174 | ||
175 | /* | |
176 | * Although this is read/used multiple times, it is written to only | |
177 | * during initialization. | |
178 | */ | |
179 | struct vas_instance *find_vas_instance(int vasid) | |
180 | { | |
181 | struct list_head *ent; | |
182 | struct vas_instance *vinst; | |
183 | ||
184 | mutex_lock(&vas_mutex); | |
ca03258b SB |
185 | |
186 | if (vasid == -1) | |
187 | vasid = per_cpu(cpu_vas_id, smp_processor_id()); | |
188 | ||
4dea2d1a SB |
189 | list_for_each(ent, &vas_instances) { |
190 | vinst = list_entry(ent, struct vas_instance, node); | |
191 | if (vinst->vas_id == vasid) { | |
192 | mutex_unlock(&vas_mutex); | |
193 | return vinst; | |
194 | } | |
195 | } | |
196 | mutex_unlock(&vas_mutex); | |
197 | ||
198 | pr_devel("Instance %d not found\n", vasid); | |
199 | return NULL; | |
200 | } | |
201 | ||
d4ef61b5 SB |
202 | int chip_to_vas_id(int chipid) |
203 | { | |
204 | int cpu; | |
205 | ||
206 | for_each_possible_cpu(cpu) { | |
207 | if (cpu_to_chip_id(cpu) == chipid) | |
208 | return per_cpu(cpu_vas_id, cpu); | |
209 | } | |
210 | return -1; | |
211 | } | |
62b49c42 | 212 | EXPORT_SYMBOL(chip_to_vas_id); |
d4ef61b5 | 213 | |
4dea2d1a SB |
214 | static int vas_probe(struct platform_device *pdev) |
215 | { | |
216 | return init_vas_instance(pdev); | |
217 | } | |
218 | ||
219 | static const struct of_device_id powernv_vas_match[] = { | |
220 | { .compatible = "ibm,vas",}, | |
221 | {}, | |
222 | }; | |
223 | ||
224 | static struct platform_driver vas_driver = { | |
225 | .driver = { | |
226 | .name = "vas", | |
227 | .of_match_table = powernv_vas_match, | |
228 | }, | |
229 | .probe = vas_probe, | |
230 | }; | |
231 | ||
232 | static int __init vas_init(void) | |
233 | { | |
234 | int found = 0; | |
235 | struct device_node *dn; | |
236 | ||
237 | platform_driver_register(&vas_driver); | |
238 | ||
239 | for_each_compatible_node(dn, NULL, "ibm,vas") { | |
240 | of_platform_device_create(dn, NULL, NULL); | |
241 | found++; | |
242 | } | |
243 | ||
45ddea8a SB |
244 | if (!found) { |
245 | platform_driver_unregister(&vas_driver); | |
4dea2d1a | 246 | return -ENODEV; |
45ddea8a | 247 | } |
4dea2d1a SB |
248 | |
249 | pr_devel("Found %d instances\n", found); | |
250 | ||
251 | return 0; | |
252 | } | |
253 | device_initcall(vas_init); |