diff options
author | hotran <hotran@apm.com> | 2016-08-15 20:14:05 -0400 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2016-08-30 18:37:20 -0400 |
commit | aca314efb177274b458f7e72c5ff375c80a5c2d0 (patch) | |
tree | a69ab3a665eb9144b463823cedfbb15b82932bc3 | |
parent | 3eab887a55424fc2c27553b7bfe32330df83f7b8 (diff) |
mailbox: pcc: Support HW-Reduced Communication Subspace type 2
ACPI 6.1 has a PCC HW-Reduced Communication Subspace type 2 intended for
use on HW-Reduce ACPI Platform, which requires read-modify-write sequence
to acknowledge doorbell interrupt. This patch provides the implementation
for the Communication Subspace Type 2.
Signed-off-by: Hoan Tran <hotran@apm.com>
Reviewed-by: Prashanth Prakash <pprakash@codeaurora.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
-rw-r--r-- | drivers/mailbox/pcc.c | 316 |
1 files changed, 245 insertions, 71 deletions
diff --git a/drivers/mailbox/pcc.c b/drivers/mailbox/pcc.c index 043828d541f7..08c87fadca8c 100644 --- a/drivers/mailbox/pcc.c +++ b/drivers/mailbox/pcc.c | |||
@@ -59,6 +59,7 @@ | |||
59 | #include <linux/delay.h> | 59 | #include <linux/delay.h> |
60 | #include <linux/io.h> | 60 | #include <linux/io.h> |
61 | #include <linux/init.h> | 61 | #include <linux/init.h> |
62 | #include <linux/interrupt.h> | ||
62 | #include <linux/list.h> | 63 | #include <linux/list.h> |
63 | #include <linux/platform_device.h> | 64 | #include <linux/platform_device.h> |
64 | #include <linux/mailbox_controller.h> | 65 | #include <linux/mailbox_controller.h> |
@@ -68,11 +69,16 @@ | |||
68 | #include "mailbox.h" | 69 | #include "mailbox.h" |
69 | 70 | ||
70 | #define MAX_PCC_SUBSPACES 256 | 71 | #define MAX_PCC_SUBSPACES 256 |
72 | #define MBOX_IRQ_NAME "pcc-mbox" | ||
71 | 73 | ||
72 | static struct mbox_chan *pcc_mbox_channels; | 74 | static struct mbox_chan *pcc_mbox_channels; |
73 | 75 | ||
74 | /* Array of cached virtual address for doorbell registers */ | 76 | /* Array of cached virtual address for doorbell registers */ |
75 | static void __iomem **pcc_doorbell_vaddr; | 77 | static void __iomem **pcc_doorbell_vaddr; |
78 | /* Array of cached virtual address for doorbell ack registers */ | ||
79 | static void __iomem **pcc_doorbell_ack_vaddr; | ||
80 | /* Array of doorbell interrupts */ | ||
81 | static int *pcc_doorbell_irq; | ||
76 | 82 | ||
77 | static struct mbox_controller pcc_mbox_ctrl = {}; | 83 | static struct mbox_controller pcc_mbox_ctrl = {}; |
78 | /** | 84 | /** |
@@ -91,6 +97,132 @@ static struct mbox_chan *get_pcc_channel(int id) | |||
91 | return &pcc_mbox_channels[id]; | 97 | return &pcc_mbox_channels[id]; |
92 | } | 98 | } |
93 | 99 | ||
100 | /* | ||
101 | * PCC can be used with perf critical drivers such as CPPC | ||
102 | * So it makes sense to locally cache the virtual address and | ||
103 | * use it to read/write to PCC registers such as doorbell register | ||
104 | * | ||
105 | * The below read_register and write_registers are used to read and | ||
106 | * write from perf critical registers such as PCC doorbell register | ||
107 | */ | ||
108 | static int read_register(void __iomem *vaddr, u64 *val, unsigned int bit_width) | ||
109 | { | ||
110 | int ret_val = 0; | ||
111 | |||
112 | switch (bit_width) { | ||
113 | case 8: | ||
114 | *val = readb(vaddr); | ||
115 | break; | ||
116 | case 16: | ||
117 | *val = readw(vaddr); | ||
118 | break; | ||
119 | case 32: | ||
120 | *val = readl(vaddr); | ||
121 | break; | ||
122 | case 64: | ||
123 | *val = readq(vaddr); | ||
124 | break; | ||
125 | default: | ||
126 | pr_debug("Error: Cannot read register of %u bit width", | ||
127 | bit_width); | ||
128 | ret_val = -EFAULT; | ||
129 | break; | ||
130 | } | ||
131 | return ret_val; | ||
132 | } | ||
133 | |||
134 | static int write_register(void __iomem *vaddr, u64 val, unsigned int bit_width) | ||
135 | { | ||
136 | int ret_val = 0; | ||
137 | |||
138 | switch (bit_width) { | ||
139 | case 8: | ||
140 | writeb(val, vaddr); | ||
141 | break; | ||
142 | case 16: | ||
143 | writew(val, vaddr); | ||
144 | break; | ||
145 | case 32: | ||
146 | writel(val, vaddr); | ||
147 | break; | ||
148 | case 64: | ||
149 | writeq(val, vaddr); | ||
150 | break; | ||
151 | default: | ||
152 | pr_debug("Error: Cannot write register of %u bit width", | ||
153 | bit_width); | ||
154 | ret_val = -EFAULT; | ||
155 | break; | ||
156 | } | ||
157 | return ret_val; | ||
158 | } | ||
159 | |||
160 | /** | ||
161 | * pcc_map_interrupt - Map a PCC subspace GSI to a linux IRQ number | ||
162 | * @interrupt: GSI number. | ||
163 | * @flags: interrupt flags | ||
164 | * | ||
165 | * Returns: a valid linux IRQ number on success | ||
166 | * 0 or -EINVAL on failure | ||
167 | */ | ||
168 | static int pcc_map_interrupt(u32 interrupt, u32 flags) | ||
169 | { | ||
170 | int trigger, polarity; | ||
171 | |||
172 | if (!interrupt) | ||
173 | return 0; | ||
174 | |||
175 | trigger = (flags & ACPI_PCCT_INTERRUPT_MODE) ? ACPI_EDGE_SENSITIVE | ||
176 | : ACPI_LEVEL_SENSITIVE; | ||
177 | |||
178 | polarity = (flags & ACPI_PCCT_INTERRUPT_POLARITY) ? ACPI_ACTIVE_LOW | ||
179 | : ACPI_ACTIVE_HIGH; | ||
180 | |||
181 | return acpi_register_gsi(NULL, interrupt, trigger, polarity); | ||
182 | } | ||
183 | |||
184 | /** | ||
185 | * pcc_mbox_irq - PCC mailbox interrupt handler | ||
186 | */ | ||
187 | static irqreturn_t pcc_mbox_irq(int irq, void *p) | ||
188 | { | ||
189 | struct acpi_generic_address *doorbell_ack; | ||
190 | struct acpi_pcct_hw_reduced *pcct_ss; | ||
191 | struct mbox_chan *chan = p; | ||
192 | u64 doorbell_ack_preserve; | ||
193 | u64 doorbell_ack_write; | ||
194 | u64 doorbell_ack_val; | ||
195 | int ret; | ||
196 | |||
197 | pcct_ss = chan->con_priv; | ||
198 | |||
199 | mbox_chan_received_data(chan, NULL); | ||
200 | |||
201 | if (pcct_ss->header.type == ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE_TYPE2) { | ||
202 | struct acpi_pcct_hw_reduced_type2 *pcct2_ss = chan->con_priv; | ||
203 | u32 id = chan - pcc_mbox_channels; | ||
204 | |||
205 | doorbell_ack = &pcct2_ss->doorbell_ack_register; | ||
206 | doorbell_ack_preserve = pcct2_ss->ack_preserve_mask; | ||
207 | doorbell_ack_write = pcct2_ss->ack_write_mask; | ||
208 | |||
209 | ret = read_register(pcc_doorbell_ack_vaddr[id], | ||
210 | &doorbell_ack_val, | ||
211 | doorbell_ack->bit_width); | ||
212 | if (ret) | ||
213 | return IRQ_NONE; | ||
214 | |||
215 | ret = write_register(pcc_doorbell_ack_vaddr[id], | ||
216 | (doorbell_ack_val & doorbell_ack_preserve) | ||
217 | | doorbell_ack_write, | ||
218 | doorbell_ack->bit_width); | ||
219 | if (ret) | ||
220 | return IRQ_NONE; | ||
221 | } | ||
222 | |||
223 | return IRQ_HANDLED; | ||
224 | } | ||
225 | |||
94 | /** | 226 | /** |
95 | * pcc_mbox_request_channel - PCC clients call this function to | 227 | * pcc_mbox_request_channel - PCC clients call this function to |
96 | * request a pointer to their PCC subspace, from which they | 228 | * request a pointer to their PCC subspace, from which they |
@@ -135,6 +267,18 @@ struct mbox_chan *pcc_mbox_request_channel(struct mbox_client *cl, | |||
135 | if (chan->txdone_method == TXDONE_BY_POLL && cl->knows_txdone) | 267 | if (chan->txdone_method == TXDONE_BY_POLL && cl->knows_txdone) |
136 | chan->txdone_method |= TXDONE_BY_ACK; | 268 | chan->txdone_method |= TXDONE_BY_ACK; |
137 | 269 | ||
270 | if (pcc_doorbell_irq[subspace_id] > 0) { | ||
271 | int rc; | ||
272 | |||
273 | rc = devm_request_irq(dev, pcc_doorbell_irq[subspace_id], | ||
274 | pcc_mbox_irq, 0, MBOX_IRQ_NAME, chan); | ||
275 | if (unlikely(rc)) { | ||
276 | dev_err(dev, "failed to register PCC interrupt %d\n", | ||
277 | pcc_doorbell_irq[subspace_id]); | ||
278 | chan = ERR_PTR(rc); | ||
279 | } | ||
280 | } | ||
281 | |||
138 | spin_unlock_irqrestore(&chan->lock, flags); | 282 | spin_unlock_irqrestore(&chan->lock, flags); |
139 | 283 | ||
140 | return chan; | 284 | return chan; |
@@ -149,80 +293,30 @@ EXPORT_SYMBOL_GPL(pcc_mbox_request_channel); | |||
149 | */ | 293 | */ |
150 | void pcc_mbox_free_channel(struct mbox_chan *chan) | 294 | void pcc_mbox_free_channel(struct mbox_chan *chan) |
151 | { | 295 | { |
296 | u32 id = chan - pcc_mbox_channels; | ||
152 | unsigned long flags; | 297 | unsigned long flags; |
153 | 298 | ||
154 | if (!chan || !chan->cl) | 299 | if (!chan || !chan->cl) |
155 | return; | 300 | return; |
156 | 301 | ||
302 | if (id >= pcc_mbox_ctrl.num_chans) { | ||
303 | pr_debug("pcc_mbox_free_channel: Invalid mbox_chan passed\n"); | ||
304 | return; | ||
305 | } | ||
306 | |||
157 | spin_lock_irqsave(&chan->lock, flags); | 307 | spin_lock_irqsave(&chan->lock, flags); |
158 | chan->cl = NULL; | 308 | chan->cl = NULL; |
159 | chan->active_req = NULL; | 309 | chan->active_req = NULL; |
160 | if (chan->txdone_method == (TXDONE_BY_POLL | TXDONE_BY_ACK)) | 310 | if (chan->txdone_method == (TXDONE_BY_POLL | TXDONE_BY_ACK)) |
161 | chan->txdone_method = TXDONE_BY_POLL; | 311 | chan->txdone_method = TXDONE_BY_POLL; |
162 | 312 | ||
313 | if (pcc_doorbell_irq[id] > 0) | ||
314 | devm_free_irq(chan->mbox->dev, pcc_doorbell_irq[id], chan); | ||
315 | |||
163 | spin_unlock_irqrestore(&chan->lock, flags); | 316 | spin_unlock_irqrestore(&chan->lock, flags); |
164 | } | 317 | } |
165 | EXPORT_SYMBOL_GPL(pcc_mbox_free_channel); | 318 | EXPORT_SYMBOL_GPL(pcc_mbox_free_channel); |
166 | 319 | ||
167 | /* | ||
168 | * PCC can be used with perf critical drivers such as CPPC | ||
169 | * So it makes sense to locally cache the virtual address and | ||
170 | * use it to read/write to PCC registers such as doorbell register | ||
171 | * | ||
172 | * The below read_register and write_registers are used to read and | ||
173 | * write from perf critical registers such as PCC doorbell register | ||
174 | */ | ||
175 | static int read_register(void __iomem *vaddr, u64 *val, unsigned int bit_width) | ||
176 | { | ||
177 | int ret_val = 0; | ||
178 | |||
179 | switch (bit_width) { | ||
180 | case 8: | ||
181 | *val = readb(vaddr); | ||
182 | break; | ||
183 | case 16: | ||
184 | *val = readw(vaddr); | ||
185 | break; | ||
186 | case 32: | ||
187 | *val = readl(vaddr); | ||
188 | break; | ||
189 | case 64: | ||
190 | *val = readq(vaddr); | ||
191 | break; | ||
192 | default: | ||
193 | pr_debug("Error: Cannot read register of %u bit width", | ||
194 | bit_width); | ||
195 | ret_val = -EFAULT; | ||
196 | break; | ||
197 | } | ||
198 | return ret_val; | ||
199 | } | ||
200 | |||
201 | static int write_register(void __iomem *vaddr, u64 val, unsigned int bit_width) | ||
202 | { | ||
203 | int ret_val = 0; | ||
204 | |||
205 | switch (bit_width) { | ||
206 | case 8: | ||
207 | writeb(val, vaddr); | ||
208 | break; | ||
209 | case 16: | ||
210 | writew(val, vaddr); | ||
211 | break; | ||
212 | case 32: | ||
213 | writel(val, vaddr); | ||
214 | break; | ||
215 | case 64: | ||
216 | writeq(val, vaddr); | ||
217 | break; | ||
218 | default: | ||
219 | pr_debug("Error: Cannot write register of %u bit width", | ||
220 | bit_width); | ||
221 | ret_val = -EFAULT; | ||
222 | break; | ||
223 | } | ||
224 | return ret_val; | ||
225 | } | ||
226 | 320 | ||
227 | /** | 321 | /** |
228 | * pcc_send_data - Called from Mailbox Controller code. Used | 322 | * pcc_send_data - Called from Mailbox Controller code. Used |
@@ -296,8 +390,10 @@ static int parse_pcc_subspace(struct acpi_subtable_header *header, | |||
296 | if (pcc_mbox_ctrl.num_chans <= MAX_PCC_SUBSPACES) { | 390 | if (pcc_mbox_ctrl.num_chans <= MAX_PCC_SUBSPACES) { |
297 | pcct_ss = (struct acpi_pcct_hw_reduced *) header; | 391 | pcct_ss = (struct acpi_pcct_hw_reduced *) header; |
298 | 392 | ||
299 | if (pcct_ss->header.type != | 393 | if ((pcct_ss->header.type != |
300 | ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE) { | 394 | ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE) |
395 | && (pcct_ss->header.type != | ||
396 | ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE_TYPE2)) { | ||
301 | pr_err("Incorrect PCC Subspace type detected\n"); | 397 | pr_err("Incorrect PCC Subspace type detected\n"); |
302 | return -EINVAL; | 398 | return -EINVAL; |
303 | } | 399 | } |
@@ -307,6 +403,43 @@ static int parse_pcc_subspace(struct acpi_subtable_header *header, | |||
307 | } | 403 | } |
308 | 404 | ||
309 | /** | 405 | /** |
406 | * pcc_parse_subspace_irq - Parse the PCC IRQ and PCC ACK register | ||
407 | * There should be one entry per PCC client. | ||
408 | * @id: PCC subspace index. | ||
409 | * @pcct_ss: Pointer to the ACPI subtable header under the PCCT. | ||
410 | * | ||
411 | * Return: 0 for Success, else errno. | ||
412 | * | ||
413 | * This gets called for each entry in the PCC table. | ||
414 | */ | ||
415 | static int pcc_parse_subspace_irq(int id, | ||
416 | struct acpi_pcct_hw_reduced *pcct_ss) | ||
417 | { | ||
418 | pcc_doorbell_irq[id] = pcc_map_interrupt(pcct_ss->doorbell_interrupt, | ||
419 | (u32)pcct_ss->flags); | ||
420 | if (pcc_doorbell_irq[id] <= 0) { | ||
421 | pr_err("PCC GSI %d not registered\n", | ||
422 | pcct_ss->doorbell_interrupt); | ||
423 | return -EINVAL; | ||
424 | } | ||
425 | |||
426 | if (pcct_ss->header.type | ||
427 | == ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE_TYPE2) { | ||
428 | struct acpi_pcct_hw_reduced_type2 *pcct2_ss = (void *)pcct_ss; | ||
429 | |||
430 | pcc_doorbell_ack_vaddr[id] = acpi_os_ioremap( | ||
431 | pcct2_ss->doorbell_ack_register.address, | ||
432 | pcct2_ss->doorbell_ack_register.bit_width / 8); | ||
433 | if (!pcc_doorbell_ack_vaddr[id]) { | ||
434 | pr_err("Failed to ioremap PCC ACK register\n"); | ||
435 | return -ENOMEM; | ||
436 | } | ||
437 | } | ||
438 | |||
439 | return 0; | ||
440 | } | ||
441 | |||
442 | /** | ||
310 | * acpi_pcc_probe - Parse the ACPI tree for the PCCT. | 443 | * acpi_pcc_probe - Parse the ACPI tree for the PCCT. |
311 | * | 444 | * |
312 | * Return: 0 for Success, else errno. | 445 | * Return: 0 for Success, else errno. |
@@ -316,7 +449,9 @@ static int __init acpi_pcc_probe(void) | |||
316 | acpi_size pcct_tbl_header_size; | 449 | acpi_size pcct_tbl_header_size; |
317 | struct acpi_table_header *pcct_tbl; | 450 | struct acpi_table_header *pcct_tbl; |
318 | struct acpi_subtable_header *pcct_entry; | 451 | struct acpi_subtable_header *pcct_entry; |
319 | int count, i; | 452 | struct acpi_table_pcct *acpi_pcct_tbl; |
453 | int count, i, rc; | ||
454 | int sum = 0; | ||
320 | acpi_status status = AE_OK; | 455 | acpi_status status = AE_OK; |
321 | 456 | ||
322 | /* Search for PCCT */ | 457 | /* Search for PCCT */ |
@@ -333,37 +468,66 @@ static int __init acpi_pcc_probe(void) | |||
333 | sizeof(struct acpi_table_pcct), | 468 | sizeof(struct acpi_table_pcct), |
334 | ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE, | 469 | ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE, |
335 | parse_pcc_subspace, MAX_PCC_SUBSPACES); | 470 | parse_pcc_subspace, MAX_PCC_SUBSPACES); |
471 | sum += (count > 0) ? count : 0; | ||
472 | |||
473 | count = acpi_table_parse_entries(ACPI_SIG_PCCT, | ||
474 | sizeof(struct acpi_table_pcct), | ||
475 | ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE_TYPE2, | ||
476 | parse_pcc_subspace, MAX_PCC_SUBSPACES); | ||
477 | sum += (count > 0) ? count : 0; | ||
336 | 478 | ||
337 | if (count <= 0) { | 479 | if (sum == 0 || sum >= MAX_PCC_SUBSPACES) { |
338 | pr_err("Error parsing PCC subspaces from PCCT\n"); | 480 | pr_err("Error parsing PCC subspaces from PCCT\n"); |
339 | return -EINVAL; | 481 | return -EINVAL; |
340 | } | 482 | } |
341 | 483 | ||
342 | pcc_mbox_channels = kzalloc(sizeof(struct mbox_chan) * | 484 | pcc_mbox_channels = kzalloc(sizeof(struct mbox_chan) * |
343 | count, GFP_KERNEL); | 485 | sum, GFP_KERNEL); |
344 | |||
345 | if (!pcc_mbox_channels) { | 486 | if (!pcc_mbox_channels) { |
346 | pr_err("Could not allocate space for PCC mbox channels\n"); | 487 | pr_err("Could not allocate space for PCC mbox channels\n"); |
347 | return -ENOMEM; | 488 | return -ENOMEM; |
348 | } | 489 | } |
349 | 490 | ||
350 | pcc_doorbell_vaddr = kcalloc(count, sizeof(void *), GFP_KERNEL); | 491 | pcc_doorbell_vaddr = kcalloc(sum, sizeof(void *), GFP_KERNEL); |
351 | if (!pcc_doorbell_vaddr) { | 492 | if (!pcc_doorbell_vaddr) { |
352 | kfree(pcc_mbox_channels); | 493 | rc = -ENOMEM; |
353 | return -ENOMEM; | 494 | goto err_free_mbox; |
495 | } | ||
496 | |||
497 | pcc_doorbell_ack_vaddr = kcalloc(sum, sizeof(void *), GFP_KERNEL); | ||
498 | if (!pcc_doorbell_ack_vaddr) { | ||
499 | rc = -ENOMEM; | ||
500 | goto err_free_db_vaddr; | ||
501 | } | ||
502 | |||
503 | pcc_doorbell_irq = kcalloc(sum, sizeof(int), GFP_KERNEL); | ||
504 | if (!pcc_doorbell_irq) { | ||
505 | rc = -ENOMEM; | ||
506 | goto err_free_db_ack_vaddr; | ||
354 | } | 507 | } |
355 | 508 | ||
356 | /* Point to the first PCC subspace entry */ | 509 | /* Point to the first PCC subspace entry */ |
357 | pcct_entry = (struct acpi_subtable_header *) ( | 510 | pcct_entry = (struct acpi_subtable_header *) ( |
358 | (unsigned long) pcct_tbl + sizeof(struct acpi_table_pcct)); | 511 | (unsigned long) pcct_tbl + sizeof(struct acpi_table_pcct)); |
359 | 512 | ||
360 | for (i = 0; i < count; i++) { | 513 | acpi_pcct_tbl = (struct acpi_table_pcct *) pcct_tbl; |
514 | if (acpi_pcct_tbl->flags & ACPI_PCCT_DOORBELL) | ||
515 | pcc_mbox_ctrl.txdone_irq = true; | ||
516 | |||
517 | for (i = 0; i < sum; i++) { | ||
361 | struct acpi_generic_address *db_reg; | 518 | struct acpi_generic_address *db_reg; |
362 | struct acpi_pcct_hw_reduced *pcct_ss; | 519 | struct acpi_pcct_hw_reduced *pcct_ss; |
363 | pcc_mbox_channels[i].con_priv = pcct_entry; | 520 | pcc_mbox_channels[i].con_priv = pcct_entry; |
364 | 521 | ||
522 | pcct_ss = (struct acpi_pcct_hw_reduced *) pcct_entry; | ||
523 | |||
524 | if (pcc_mbox_ctrl.txdone_irq) { | ||
525 | rc = pcc_parse_subspace_irq(i, pcct_ss); | ||
526 | if (rc < 0) | ||
527 | goto err; | ||
528 | } | ||
529 | |||
365 | /* If doorbell is in system memory cache the virt address */ | 530 | /* If doorbell is in system memory cache the virt address */ |
366 | pcct_ss = (struct acpi_pcct_hw_reduced *)pcct_entry; | ||
367 | db_reg = &pcct_ss->doorbell_register; | 531 | db_reg = &pcct_ss->doorbell_register; |
368 | if (db_reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) | 532 | if (db_reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) |
369 | pcc_doorbell_vaddr[i] = acpi_os_ioremap(db_reg->address, | 533 | pcc_doorbell_vaddr[i] = acpi_os_ioremap(db_reg->address, |
@@ -372,11 +536,21 @@ static int __init acpi_pcc_probe(void) | |||
372 | ((unsigned long) pcct_entry + pcct_entry->length); | 536 | ((unsigned long) pcct_entry + pcct_entry->length); |
373 | } | 537 | } |
374 | 538 | ||
375 | pcc_mbox_ctrl.num_chans = count; | 539 | pcc_mbox_ctrl.num_chans = sum; |
376 | 540 | ||
377 | pr_info("Detected %d PCC Subspaces\n", pcc_mbox_ctrl.num_chans); | 541 | pr_info("Detected %d PCC Subspaces\n", pcc_mbox_ctrl.num_chans); |
378 | 542 | ||
379 | return 0; | 543 | return 0; |
544 | |||
545 | err: | ||
546 | kfree(pcc_doorbell_irq); | ||
547 | err_free_db_ack_vaddr: | ||
548 | kfree(pcc_doorbell_ack_vaddr); | ||
549 | err_free_db_vaddr: | ||
550 | kfree(pcc_doorbell_vaddr); | ||
551 | err_free_mbox: | ||
552 | kfree(pcc_mbox_channels); | ||
553 | return rc; | ||
380 | } | 554 | } |
381 | 555 | ||
382 | /** | 556 | /** |