diff options
Diffstat (limited to 'arch/powerpc/sysdev/qe_lib/qe.c')
-rw-r--r-- | arch/powerpc/sysdev/qe_lib/qe.c | 356 |
1 files changed, 332 insertions, 24 deletions
diff --git a/arch/powerpc/sysdev/qe_lib/qe.c b/arch/powerpc/sysdev/qe_lib/qe.c index 3d57d3835b04..5ef844da9355 100644 --- a/arch/powerpc/sysdev/qe_lib/qe.c +++ b/arch/powerpc/sysdev/qe_lib/qe.c | |||
@@ -25,6 +25,7 @@ | |||
25 | #include <linux/module.h> | 25 | #include <linux/module.h> |
26 | #include <linux/delay.h> | 26 | #include <linux/delay.h> |
27 | #include <linux/ioport.h> | 27 | #include <linux/ioport.h> |
28 | #include <linux/crc32.h> | ||
28 | #include <asm/irq.h> | 29 | #include <asm/irq.h> |
29 | #include <asm/page.h> | 30 | #include <asm/page.h> |
30 | #include <asm/pgtable.h> | 31 | #include <asm/pgtable.h> |
@@ -64,17 +65,22 @@ static phys_addr_t qebase = -1; | |||
64 | phys_addr_t get_qe_base(void) | 65 | phys_addr_t get_qe_base(void) |
65 | { | 66 | { |
66 | struct device_node *qe; | 67 | struct device_node *qe; |
68 | unsigned int size; | ||
69 | const void *prop; | ||
67 | 70 | ||
68 | if (qebase != -1) | 71 | if (qebase != -1) |
69 | return qebase; | 72 | return qebase; |
70 | 73 | ||
71 | qe = of_find_node_by_type(NULL, "qe"); | 74 | qe = of_find_compatible_node(NULL, NULL, "fsl,qe"); |
72 | if (qe) { | 75 | if (!qe) { |
73 | unsigned int size; | 76 | qe = of_find_node_by_type(NULL, "qe"); |
74 | const void *prop = of_get_property(qe, "reg", &size); | 77 | if (!qe) |
75 | qebase = of_translate_address(qe, prop); | 78 | return qebase; |
76 | of_node_put(qe); | 79 | } |
77 | }; | 80 | |
81 | prop = of_get_property(qe, "reg", &size); | ||
82 | qebase = of_translate_address(qe, prop); | ||
83 | of_node_put(qe); | ||
78 | 84 | ||
79 | return qebase; | 85 | return qebase; |
80 | } | 86 | } |
@@ -152,34 +158,45 @@ static unsigned int brg_clk = 0; | |||
152 | unsigned int get_brg_clk(void) | 158 | unsigned int get_brg_clk(void) |
153 | { | 159 | { |
154 | struct device_node *qe; | 160 | struct device_node *qe; |
161 | unsigned int size; | ||
162 | const u32 *prop; | ||
163 | |||
155 | if (brg_clk) | 164 | if (brg_clk) |
156 | return brg_clk; | 165 | return brg_clk; |
157 | 166 | ||
158 | qe = of_find_node_by_type(NULL, "qe"); | 167 | qe = of_find_compatible_node(NULL, NULL, "fsl,qe"); |
159 | if (qe) { | 168 | if (!qe) { |
160 | unsigned int size; | 169 | qe = of_find_node_by_type(NULL, "qe"); |
161 | const u32 *prop = of_get_property(qe, "brg-frequency", &size); | 170 | if (!qe) |
162 | brg_clk = *prop; | 171 | return brg_clk; |
163 | of_node_put(qe); | 172 | } |
164 | }; | 173 | |
174 | prop = of_get_property(qe, "brg-frequency", &size); | ||
175 | if (!prop || size != sizeof(*prop)) | ||
176 | return brg_clk; | ||
177 | |||
178 | brg_clk = *prop; | ||
179 | of_node_put(qe); | ||
180 | |||
165 | return brg_clk; | 181 | return brg_clk; |
166 | } | 182 | } |
167 | 183 | ||
168 | /* Program the BRG to the given sampling rate and multiplier | 184 | /* Program the BRG to the given sampling rate and multiplier |
169 | * | 185 | * |
170 | * @brg: the BRG, 1-16 | 186 | * @brg: the BRG, QE_BRG1 - QE_BRG16 |
171 | * @rate: the desired sampling rate | 187 | * @rate: the desired sampling rate |
172 | * @multiplier: corresponds to the value programmed in GUMR_L[RDCR] or | 188 | * @multiplier: corresponds to the value programmed in GUMR_L[RDCR] or |
173 | * GUMR_L[TDCR]. E.g., if this BRG is the RX clock, and GUMR_L[RDCR]=01, | 189 | * GUMR_L[TDCR]. E.g., if this BRG is the RX clock, and GUMR_L[RDCR]=01, |
174 | * then 'multiplier' should be 8. | 190 | * then 'multiplier' should be 8. |
175 | * | ||
176 | * Also note that the value programmed into the BRGC register must be even. | ||
177 | */ | 191 | */ |
178 | void qe_setbrg(unsigned int brg, unsigned int rate, unsigned int multiplier) | 192 | int qe_setbrg(enum qe_clock brg, unsigned int rate, unsigned int multiplier) |
179 | { | 193 | { |
180 | u32 divisor, tempval; | 194 | u32 divisor, tempval; |
181 | u32 div16 = 0; | 195 | u32 div16 = 0; |
182 | 196 | ||
197 | if ((brg < QE_BRG1) || (brg > QE_BRG16)) | ||
198 | return -EINVAL; | ||
199 | |||
183 | divisor = get_brg_clk() / (rate * multiplier); | 200 | divisor = get_brg_clk() / (rate * multiplier); |
184 | 201 | ||
185 | if (divisor > QE_BRGC_DIVISOR_MAX + 1) { | 202 | if (divisor > QE_BRGC_DIVISOR_MAX + 1) { |
@@ -196,8 +213,43 @@ void qe_setbrg(unsigned int brg, unsigned int rate, unsigned int multiplier) | |||
196 | tempval = ((divisor - 1) << QE_BRGC_DIVISOR_SHIFT) | | 213 | tempval = ((divisor - 1) << QE_BRGC_DIVISOR_SHIFT) | |
197 | QE_BRGC_ENABLE | div16; | 214 | QE_BRGC_ENABLE | div16; |
198 | 215 | ||
199 | out_be32(&qe_immr->brg.brgc[brg - 1], tempval); | 216 | out_be32(&qe_immr->brg.brgc[brg - QE_BRG1], tempval); |
217 | |||
218 | return 0; | ||
219 | } | ||
220 | EXPORT_SYMBOL(qe_setbrg); | ||
221 | |||
222 | /* Convert a string to a QE clock source enum | ||
223 | * | ||
224 | * This function takes a string, typically from a property in the device | ||
225 | * tree, and returns the corresponding "enum qe_clock" value. | ||
226 | */ | ||
227 | enum qe_clock qe_clock_source(const char *source) | ||
228 | { | ||
229 | unsigned int i; | ||
230 | |||
231 | if (strcasecmp(source, "none") == 0) | ||
232 | return QE_CLK_NONE; | ||
233 | |||
234 | if (strncasecmp(source, "brg", 3) == 0) { | ||
235 | i = simple_strtoul(source + 3, NULL, 10); | ||
236 | if ((i >= 1) && (i <= 16)) | ||
237 | return (QE_BRG1 - 1) + i; | ||
238 | else | ||
239 | return QE_CLK_DUMMY; | ||
240 | } | ||
241 | |||
242 | if (strncasecmp(source, "clk", 3) == 0) { | ||
243 | i = simple_strtoul(source + 3, NULL, 10); | ||
244 | if ((i >= 1) && (i <= 24)) | ||
245 | return (QE_CLK1 - 1) + i; | ||
246 | else | ||
247 | return QE_CLK_DUMMY; | ||
248 | } | ||
249 | |||
250 | return QE_CLK_DUMMY; | ||
200 | } | 251 | } |
252 | EXPORT_SYMBOL(qe_clock_source); | ||
201 | 253 | ||
202 | /* Initialize SNUMs (thread serial numbers) according to | 254 | /* Initialize SNUMs (thread serial numbers) according to |
203 | * QE Module Control chapter, SNUM table | 255 | * QE Module Control chapter, SNUM table |
@@ -285,7 +337,7 @@ static rh_info_t qe_muram_info; | |||
285 | static void qe_muram_init(void) | 337 | static void qe_muram_init(void) |
286 | { | 338 | { |
287 | struct device_node *np; | 339 | struct device_node *np; |
288 | u32 address; | 340 | const u32 *address; |
289 | u64 size; | 341 | u64 size; |
290 | unsigned int flags; | 342 | unsigned int flags; |
291 | 343 | ||
@@ -298,11 +350,21 @@ static void qe_muram_init(void) | |||
298 | /* XXX: This is a subset of the available muram. It | 350 | /* XXX: This is a subset of the available muram. It |
299 | * varies with the processor and the microcode patches activated. | 351 | * varies with the processor and the microcode patches activated. |
300 | */ | 352 | */ |
301 | if ((np = of_find_node_by_name(NULL, "data-only")) != NULL) { | 353 | np = of_find_compatible_node(NULL, NULL, "fsl,qe-muram-data"); |
302 | address = *of_get_address(np, 0, &size, &flags); | 354 | if (!np) { |
303 | of_node_put(np); | 355 | np = of_find_node_by_name(NULL, "data-only"); |
304 | rh_attach_region(&qe_muram_info, address, (int) size); | 356 | if (!np) { |
357 | WARN_ON(1); | ||
358 | return; | ||
359 | } | ||
305 | } | 360 | } |
361 | |||
362 | address = of_get_address(np, 0, &size, &flags); | ||
363 | WARN_ON(!address); | ||
364 | |||
365 | of_node_put(np); | ||
366 | if (address) | ||
367 | rh_attach_region(&qe_muram_info, *address, (int)size); | ||
306 | } | 368 | } |
307 | 369 | ||
308 | /* This function returns an index into the MURAM area. | 370 | /* This function returns an index into the MURAM area. |
@@ -358,3 +420,249 @@ void *qe_muram_addr(unsigned long offset) | |||
358 | return (void *)&qe_immr->muram[offset]; | 420 | return (void *)&qe_immr->muram[offset]; |
359 | } | 421 | } |
360 | EXPORT_SYMBOL(qe_muram_addr); | 422 | EXPORT_SYMBOL(qe_muram_addr); |
423 | |||
424 | /* The maximum number of RISCs we support */ | ||
425 | #define MAX_QE_RISC 2 | ||
426 | |||
427 | /* Firmware information stored here for qe_get_firmware_info() */ | ||
428 | static struct qe_firmware_info qe_firmware_info; | ||
429 | |||
430 | /* | ||
431 | * Set to 1 if QE firmware has been uploaded, and therefore | ||
432 | * qe_firmware_info contains valid data. | ||
433 | */ | ||
434 | static int qe_firmware_uploaded; | ||
435 | |||
436 | /* | ||
437 | * Upload a QE microcode | ||
438 | * | ||
439 | * This function is a worker function for qe_upload_firmware(). It does | ||
440 | * the actual uploading of the microcode. | ||
441 | */ | ||
442 | static void qe_upload_microcode(const void *base, | ||
443 | const struct qe_microcode *ucode) | ||
444 | { | ||
445 | const __be32 *code = base + be32_to_cpu(ucode->code_offset); | ||
446 | unsigned int i; | ||
447 | |||
448 | if (ucode->major || ucode->minor || ucode->revision) | ||
449 | printk(KERN_INFO "qe-firmware: " | ||
450 | "uploading microcode '%s' version %u.%u.%u\n", | ||
451 | ucode->id, ucode->major, ucode->minor, ucode->revision); | ||
452 | else | ||
453 | printk(KERN_INFO "qe-firmware: " | ||
454 | "uploading microcode '%s'\n", ucode->id); | ||
455 | |||
456 | /* Use auto-increment */ | ||
457 | out_be32(&qe_immr->iram.iadd, be32_to_cpu(ucode->iram_offset) | | ||
458 | QE_IRAM_IADD_AIE | QE_IRAM_IADD_BADDR); | ||
459 | |||
460 | for (i = 0; i < be32_to_cpu(ucode->count); i++) | ||
461 | out_be32(&qe_immr->iram.idata, be32_to_cpu(code[i])); | ||
462 | } | ||
463 | |||
464 | /* | ||
465 | * Upload a microcode to the I-RAM at a specific address. | ||
466 | * | ||
467 | * See Documentation/powerpc/qe-firmware.txt for information on QE microcode | ||
468 | * uploading. | ||
469 | * | ||
470 | * Currently, only version 1 is supported, so the 'version' field must be | ||
471 | * set to 1. | ||
472 | * | ||
473 | * The SOC model and revision are not validated, they are only displayed for | ||
474 | * informational purposes. | ||
475 | * | ||
476 | * 'calc_size' is the calculated size, in bytes, of the firmware structure and | ||
477 | * all of the microcode structures, minus the CRC. | ||
478 | * | ||
479 | * 'length' is the size that the structure says it is, including the CRC. | ||
480 | */ | ||
481 | int qe_upload_firmware(const struct qe_firmware *firmware) | ||
482 | { | ||
483 | unsigned int i; | ||
484 | unsigned int j; | ||
485 | u32 crc; | ||
486 | size_t calc_size = sizeof(struct qe_firmware); | ||
487 | size_t length; | ||
488 | const struct qe_header *hdr; | ||
489 | |||
490 | if (!firmware) { | ||
491 | printk(KERN_ERR "qe-firmware: invalid pointer\n"); | ||
492 | return -EINVAL; | ||
493 | } | ||
494 | |||
495 | hdr = &firmware->header; | ||
496 | length = be32_to_cpu(hdr->length); | ||
497 | |||
498 | /* Check the magic */ | ||
499 | if ((hdr->magic[0] != 'Q') || (hdr->magic[1] != 'E') || | ||
500 | (hdr->magic[2] != 'F')) { | ||
501 | printk(KERN_ERR "qe-firmware: not a microcode\n"); | ||
502 | return -EPERM; | ||
503 | } | ||
504 | |||
505 | /* Check the version */ | ||
506 | if (hdr->version != 1) { | ||
507 | printk(KERN_ERR "qe-firmware: unsupported version\n"); | ||
508 | return -EPERM; | ||
509 | } | ||
510 | |||
511 | /* Validate some of the fields */ | ||
512 | if ((firmware->count < 1) || (firmware->count >= MAX_QE_RISC)) { | ||
513 | printk(KERN_ERR "qe-firmware: invalid data\n"); | ||
514 | return -EINVAL; | ||
515 | } | ||
516 | |||
517 | /* Validate the length and check if there's a CRC */ | ||
518 | calc_size += (firmware->count - 1) * sizeof(struct qe_microcode); | ||
519 | |||
520 | for (i = 0; i < firmware->count; i++) | ||
521 | /* | ||
522 | * For situations where the second RISC uses the same microcode | ||
523 | * as the first, the 'code_offset' and 'count' fields will be | ||
524 | * zero, so it's okay to add those. | ||
525 | */ | ||
526 | calc_size += sizeof(__be32) * | ||
527 | be32_to_cpu(firmware->microcode[i].count); | ||
528 | |||
529 | /* Validate the length */ | ||
530 | if (length != calc_size + sizeof(__be32)) { | ||
531 | printk(KERN_ERR "qe-firmware: invalid length\n"); | ||
532 | return -EPERM; | ||
533 | } | ||
534 | |||
535 | /* Validate the CRC */ | ||
536 | crc = be32_to_cpu(*(__be32 *)((void *)firmware + calc_size)); | ||
537 | if (crc != crc32(0, firmware, calc_size)) { | ||
538 | printk(KERN_ERR "qe-firmware: firmware CRC is invalid\n"); | ||
539 | return -EIO; | ||
540 | } | ||
541 | |||
542 | /* | ||
543 | * If the microcode calls for it, split the I-RAM. | ||
544 | */ | ||
545 | if (!firmware->split) | ||
546 | setbits16(&qe_immr->cp.cercr, QE_CP_CERCR_CIR); | ||
547 | |||
548 | if (firmware->soc.model) | ||
549 | printk(KERN_INFO | ||
550 | "qe-firmware: firmware '%s' for %u V%u.%u\n", | ||
551 | firmware->id, be16_to_cpu(firmware->soc.model), | ||
552 | firmware->soc.major, firmware->soc.minor); | ||
553 | else | ||
554 | printk(KERN_INFO "qe-firmware: firmware '%s'\n", | ||
555 | firmware->id); | ||
556 | |||
557 | /* | ||
558 | * The QE only supports one microcode per RISC, so clear out all the | ||
559 | * saved microcode information and put in the new. | ||
560 | */ | ||
561 | memset(&qe_firmware_info, 0, sizeof(qe_firmware_info)); | ||
562 | strcpy(qe_firmware_info.id, firmware->id); | ||
563 | qe_firmware_info.extended_modes = firmware->extended_modes; | ||
564 | memcpy(qe_firmware_info.vtraps, firmware->vtraps, | ||
565 | sizeof(firmware->vtraps)); | ||
566 | |||
567 | /* Loop through each microcode. */ | ||
568 | for (i = 0; i < firmware->count; i++) { | ||
569 | const struct qe_microcode *ucode = &firmware->microcode[i]; | ||
570 | |||
571 | /* Upload a microcode if it's present */ | ||
572 | if (ucode->code_offset) | ||
573 | qe_upload_microcode(firmware, ucode); | ||
574 | |||
575 | /* Program the traps for this processor */ | ||
576 | for (j = 0; j < 16; j++) { | ||
577 | u32 trap = be32_to_cpu(ucode->traps[j]); | ||
578 | |||
579 | if (trap) | ||
580 | out_be32(&qe_immr->rsp[i].tibcr[j], trap); | ||
581 | } | ||
582 | |||
583 | /* Enable traps */ | ||
584 | out_be32(&qe_immr->rsp[i].eccr, be32_to_cpu(ucode->eccr)); | ||
585 | } | ||
586 | |||
587 | qe_firmware_uploaded = 1; | ||
588 | |||
589 | return 0; | ||
590 | } | ||
591 | EXPORT_SYMBOL(qe_upload_firmware); | ||
592 | |||
593 | /* | ||
594 | * Get info on the currently-loaded firmware | ||
595 | * | ||
596 | * This function also checks the device tree to see if the boot loader has | ||
597 | * uploaded a firmware already. | ||
598 | */ | ||
599 | struct qe_firmware_info *qe_get_firmware_info(void) | ||
600 | { | ||
601 | static int initialized; | ||
602 | struct property *prop; | ||
603 | struct device_node *qe; | ||
604 | struct device_node *fw = NULL; | ||
605 | const char *sprop; | ||
606 | unsigned int i; | ||
607 | |||
608 | /* | ||
609 | * If we haven't checked yet, and a driver hasn't uploaded a firmware | ||
610 | * yet, then check the device tree for information. | ||
611 | */ | ||
612 | if (initialized || qe_firmware_uploaded) | ||
613 | return NULL; | ||
614 | |||
615 | initialized = 1; | ||
616 | |||
617 | /* | ||
618 | * Newer device trees have an "fsl,qe" compatible property for the QE | ||
619 | * node, but we still need to support older device trees. | ||
620 | */ | ||
621 | qe = of_find_compatible_node(NULL, NULL, "fsl,qe"); | ||
622 | if (!qe) { | ||
623 | qe = of_find_node_by_type(NULL, "qe"); | ||
624 | if (!qe) | ||
625 | return NULL; | ||
626 | } | ||
627 | |||
628 | /* Find the 'firmware' child node */ | ||
629 | for_each_child_of_node(qe, fw) { | ||
630 | if (strcmp(fw->name, "firmware") == 0) | ||
631 | break; | ||
632 | } | ||
633 | |||
634 | of_node_put(qe); | ||
635 | |||
636 | /* Did we find the 'firmware' node? */ | ||
637 | if (!fw) | ||
638 | return NULL; | ||
639 | |||
640 | qe_firmware_uploaded = 1; | ||
641 | |||
642 | /* Copy the data into qe_firmware_info*/ | ||
643 | sprop = of_get_property(fw, "id", NULL); | ||
644 | if (sprop) | ||
645 | strncpy(qe_firmware_info.id, sprop, | ||
646 | sizeof(qe_firmware_info.id) - 1); | ||
647 | |||
648 | prop = of_find_property(fw, "extended-modes", NULL); | ||
649 | if (prop && (prop->length == sizeof(u64))) { | ||
650 | const u64 *iprop = prop->value; | ||
651 | |||
652 | qe_firmware_info.extended_modes = *iprop; | ||
653 | } | ||
654 | |||
655 | prop = of_find_property(fw, "virtual-traps", NULL); | ||
656 | if (prop && (prop->length == 32)) { | ||
657 | const u32 *iprop = prop->value; | ||
658 | |||
659 | for (i = 0; i < ARRAY_SIZE(qe_firmware_info.vtraps); i++) | ||
660 | qe_firmware_info.vtraps[i] = iprop[i]; | ||
661 | } | ||
662 | |||
663 | of_node_put(fw); | ||
664 | |||
665 | return &qe_firmware_info; | ||
666 | } | ||
667 | EXPORT_SYMBOL(qe_get_firmware_info); | ||
668 | |||