1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58#include <linux/acpi.h>
59#include <linux/delay.h>
60#include <linux/io.h>
61#include <linux/init.h>
62#include <linux/list.h>
63#include <linux/platform_device.h>
64#include <linux/mailbox_controller.h>
65#include <linux/mailbox_client.h>
66
67#include "mailbox.h"
68
69#define MAX_PCC_SUBSPACES 256
70
71static struct mbox_chan *pcc_mbox_channels;
72
73static struct mbox_controller pcc_mbox_ctrl = {};
74
75
76
77
78
79
80
81
82static struct mbox_chan *get_pcc_channel(int id)
83{
84 struct mbox_chan *pcc_chan;
85
86 if (id < 0 || id > pcc_mbox_ctrl.num_chans)
87 return ERR_PTR(-ENOENT);
88
89 pcc_chan = (struct mbox_chan *)
90 (unsigned long) pcc_mbox_channels +
91 (id * sizeof(*pcc_chan));
92
93 return pcc_chan;
94}
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109struct mbox_chan *pcc_mbox_request_channel(struct mbox_client *cl,
110 int subspace_id)
111{
112 struct device *dev = pcc_mbox_ctrl.dev;
113 struct mbox_chan *chan;
114 unsigned long flags;
115
116
117
118
119
120
121
122
123 chan = get_pcc_channel(subspace_id);
124
125 if (!chan || chan->cl) {
126 dev_err(dev, "Channel not found for idx: %d\n", subspace_id);
127 return ERR_PTR(-EBUSY);
128 }
129
130 spin_lock_irqsave(&chan->lock, flags);
131 chan->msg_free = 0;
132 chan->msg_count = 0;
133 chan->active_req = NULL;
134 chan->cl = cl;
135 init_completion(&chan->tx_complete);
136
137 if (chan->txdone_method == TXDONE_BY_POLL && cl->knows_txdone)
138 chan->txdone_method |= TXDONE_BY_ACK;
139
140 spin_unlock_irqrestore(&chan->lock, flags);
141
142 return chan;
143}
144EXPORT_SYMBOL_GPL(pcc_mbox_request_channel);
145
146
147
148
149
150
151
152void pcc_mbox_free_channel(struct mbox_chan *chan)
153{
154 unsigned long flags;
155
156 if (!chan || !chan->cl)
157 return;
158
159 spin_lock_irqsave(&chan->lock, flags);
160 chan->cl = NULL;
161 chan->active_req = NULL;
162 if (chan->txdone_method == (TXDONE_BY_POLL | TXDONE_BY_ACK))
163 chan->txdone_method = TXDONE_BY_POLL;
164
165 spin_unlock_irqrestore(&chan->lock, flags);
166}
167EXPORT_SYMBOL_GPL(pcc_mbox_free_channel);
168
169
170
171
172
173
174
175
176
177
178
179
180
181static int pcc_send_data(struct mbox_chan *chan, void *data)
182{
183 struct acpi_pcct_hw_reduced *pcct_ss = chan->con_priv;
184 struct acpi_generic_address doorbell;
185 u64 doorbell_preserve;
186 u64 doorbell_val;
187 u64 doorbell_write;
188
189 doorbell = pcct_ss->doorbell_register;
190 doorbell_preserve = pcct_ss->preserve_mask;
191 doorbell_write = pcct_ss->write_mask;
192
193
194 acpi_read(&doorbell_val, &doorbell);
195 acpi_write((doorbell_val & doorbell_preserve) | doorbell_write,
196 &doorbell);
197
198 return 0;
199}
200
201static struct mbox_chan_ops pcc_chan_ops = {
202 .send_data = pcc_send_data,
203};
204
205
206
207
208
209
210
211
212
213
214
215static int parse_pcc_subspace(struct acpi_subtable_header *header,
216 const unsigned long end)
217{
218 struct acpi_pcct_hw_reduced *pcct_ss;
219
220 if (pcc_mbox_ctrl.num_chans <= MAX_PCC_SUBSPACES) {
221 pcct_ss = (struct acpi_pcct_hw_reduced *) header;
222
223 if (pcct_ss->header.type !=
224 ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE) {
225 pr_err("Incorrect PCC Subspace type detected\n");
226 return -EINVAL;
227 }
228 }
229
230 return 0;
231}
232
233
234
235
236
237
238static int __init acpi_pcc_probe(void)
239{
240 acpi_size pcct_tbl_header_size;
241 struct acpi_table_header *pcct_tbl;
242 struct acpi_subtable_header *pcct_entry;
243 int count, i;
244 acpi_status status = AE_OK;
245
246
247 status = acpi_get_table_with_size(ACPI_SIG_PCCT, 0,
248 &pcct_tbl,
249 &pcct_tbl_header_size);
250
251 if (ACPI_FAILURE(status) || !pcct_tbl) {
252 pr_warn("PCCT header not found.\n");
253 return -ENODEV;
254 }
255
256 count = acpi_table_parse_entries(ACPI_SIG_PCCT,
257 sizeof(struct acpi_table_pcct),
258 ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE,
259 parse_pcc_subspace, MAX_PCC_SUBSPACES);
260
261 if (count <= 0) {
262 pr_err("Error parsing PCC subspaces from PCCT\n");
263 return -EINVAL;
264 }
265
266 pcc_mbox_channels = kzalloc(sizeof(struct mbox_chan) *
267 count, GFP_KERNEL);
268
269 if (!pcc_mbox_channels) {
270 pr_err("Could not allocate space for PCC mbox channels\n");
271 return -ENOMEM;
272 }
273
274
275 pcct_entry = (struct acpi_subtable_header *) (
276 (unsigned long) pcct_tbl + sizeof(struct acpi_table_pcct));
277
278 for (i = 0; i < count; i++) {
279 pcc_mbox_channels[i].con_priv = pcct_entry;
280 pcct_entry = (struct acpi_subtable_header *)
281 ((unsigned long) pcct_entry + pcct_entry->length);
282 }
283
284 pcc_mbox_ctrl.num_chans = count;
285
286 pr_info("Detected %d PCC Subspaces\n", pcc_mbox_ctrl.num_chans);
287
288 return 0;
289}
290
291
292
293
294
295
296
297
298
299
300
301
302static int pcc_mbox_probe(struct platform_device *pdev)
303{
304 int ret = 0;
305
306 pcc_mbox_ctrl.chans = pcc_mbox_channels;
307 pcc_mbox_ctrl.ops = &pcc_chan_ops;
308 pcc_mbox_ctrl.dev = &pdev->dev;
309
310 pr_info("Registering PCC driver as Mailbox controller\n");
311 ret = mbox_controller_register(&pcc_mbox_ctrl);
312
313 if (ret) {
314 pr_err("Err registering PCC as Mailbox controller: %d\n", ret);
315 ret = -ENODEV;
316 }
317
318 return ret;
319}
320
321struct platform_driver pcc_mbox_driver = {
322 .probe = pcc_mbox_probe,
323 .driver = {
324 .name = "PCCT",
325 .owner = THIS_MODULE,
326 },
327};
328
329static int __init pcc_init(void)
330{
331 int ret;
332 struct platform_device *pcc_pdev;
333
334 if (acpi_disabled)
335 return -ENODEV;
336
337
338 ret = acpi_pcc_probe();
339
340 if (ret) {
341 pr_debug("ACPI PCC probe failed.\n");
342 return -ENODEV;
343 }
344
345 pcc_pdev = platform_create_bundle(&pcc_mbox_driver,
346 pcc_mbox_probe, NULL, 0, NULL, 0);
347
348 if (IS_ERR(pcc_pdev)) {
349 pr_debug("Err creating PCC platform bundle\n");
350 return PTR_ERR(pcc_pdev);
351 }
352
353 return 0;
354}
355device_initcall(pcc_init);
356