diff options
| -rw-r--r-- | drivers/acpi/sleep.c | 13 | ||||
| -rw-r--r-- | drivers/cpufreq/Kconfig.arm | 10 | ||||
| -rw-r--r-- | drivers/cpufreq/brcmstb-avs-cpufreq.c | 323 |
3 files changed, 14 insertions, 332 deletions
diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index 99a1a650326d..974e58457697 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c | |||
| @@ -364,6 +364,19 @@ static const struct dmi_system_id acpisleep_dmi_table[] __initconst = { | |||
| 364 | DMI_MATCH(DMI_PRODUCT_NAME, "XPS 13 9360"), | 364 | DMI_MATCH(DMI_PRODUCT_NAME, "XPS 13 9360"), |
| 365 | }, | 365 | }, |
| 366 | }, | 366 | }, |
| 367 | /* | ||
| 368 | * ThinkPad X1 Tablet(2016) cannot do suspend-to-idle using | ||
| 369 | * the Low Power S0 Idle firmware interface (see | ||
| 370 | * https://bugzilla.kernel.org/show_bug.cgi?id=199057). | ||
| 371 | */ | ||
| 372 | { | ||
| 373 | .callback = init_no_lps0, | ||
| 374 | .ident = "ThinkPad X1 Tablet(2016)", | ||
| 375 | .matches = { | ||
| 376 | DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), | ||
| 377 | DMI_MATCH(DMI_PRODUCT_NAME, "20GGA00L00"), | ||
| 378 | }, | ||
| 379 | }, | ||
| 367 | {}, | 380 | {}, |
| 368 | }; | 381 | }; |
| 369 | 382 | ||
diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 7f56fe5183f2..de55c7d57438 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm | |||
| @@ -71,16 +71,6 @@ config ARM_BRCMSTB_AVS_CPUFREQ | |||
| 71 | 71 | ||
| 72 | Say Y, if you have a Broadcom SoC with AVS support for DFS or DVFS. | 72 | Say Y, if you have a Broadcom SoC with AVS support for DFS or DVFS. |
| 73 | 73 | ||
| 74 | config ARM_BRCMSTB_AVS_CPUFREQ_DEBUG | ||
| 75 | bool "Broadcom STB AVS CPUfreq driver sysfs debug capability" | ||
| 76 | depends on ARM_BRCMSTB_AVS_CPUFREQ | ||
| 77 | help | ||
| 78 | Enabling this option turns on debug support via sysfs under | ||
| 79 | /sys/kernel/debug/brcmstb-avs-cpufreq. It is possible to read all and | ||
| 80 | write some AVS mailbox registers through sysfs entries. | ||
| 81 | |||
| 82 | If in doubt, say N. | ||
| 83 | |||
| 84 | config ARM_EXYNOS5440_CPUFREQ | 74 | config ARM_EXYNOS5440_CPUFREQ |
| 85 | tristate "SAMSUNG EXYNOS5440" | 75 | tristate "SAMSUNG EXYNOS5440" |
| 86 | depends on SOC_EXYNOS5440 | 76 | depends on SOC_EXYNOS5440 |
diff --git a/drivers/cpufreq/brcmstb-avs-cpufreq.c b/drivers/cpufreq/brcmstb-avs-cpufreq.c index 6cdac1aaf23c..b07559b9ed99 100644 --- a/drivers/cpufreq/brcmstb-avs-cpufreq.c +++ b/drivers/cpufreq/brcmstb-avs-cpufreq.c | |||
| @@ -49,13 +49,6 @@ | |||
| 49 | #include <linux/platform_device.h> | 49 | #include <linux/platform_device.h> |
| 50 | #include <linux/semaphore.h> | 50 | #include <linux/semaphore.h> |
| 51 | 51 | ||
| 52 | #ifdef CONFIG_ARM_BRCMSTB_AVS_CPUFREQ_DEBUG | ||
| 53 | #include <linux/ctype.h> | ||
| 54 | #include <linux/debugfs.h> | ||
| 55 | #include <linux/slab.h> | ||
| 56 | #include <linux/uaccess.h> | ||
| 57 | #endif | ||
| 58 | |||
| 59 | /* Max number of arguments AVS calls take */ | 52 | /* Max number of arguments AVS calls take */ |
| 60 | #define AVS_MAX_CMD_ARGS 4 | 53 | #define AVS_MAX_CMD_ARGS 4 |
| 61 | /* | 54 | /* |
| @@ -182,88 +175,11 @@ struct private_data { | |||
| 182 | void __iomem *base; | 175 | void __iomem *base; |
| 183 | void __iomem *avs_intr_base; | 176 | void __iomem *avs_intr_base; |
| 184 | struct device *dev; | 177 | struct device *dev; |
| 185 | #ifdef CONFIG_ARM_BRCMSTB_AVS_CPUFREQ_DEBUG | ||
| 186 | struct dentry *debugfs; | ||
| 187 | #endif | ||
| 188 | struct completion done; | 178 | struct completion done; |
| 189 | struct semaphore sem; | 179 | struct semaphore sem; |
| 190 | struct pmap pmap; | 180 | struct pmap pmap; |
| 191 | }; | 181 | }; |
| 192 | 182 | ||
| 193 | #ifdef CONFIG_ARM_BRCMSTB_AVS_CPUFREQ_DEBUG | ||
| 194 | |||
| 195 | enum debugfs_format { | ||
| 196 | DEBUGFS_NORMAL, | ||
| 197 | DEBUGFS_FLOAT, | ||
| 198 | DEBUGFS_REV, | ||
| 199 | }; | ||
| 200 | |||
| 201 | struct debugfs_data { | ||
| 202 | struct debugfs_entry *entry; | ||
| 203 | struct private_data *priv; | ||
| 204 | }; | ||
| 205 | |||
| 206 | struct debugfs_entry { | ||
| 207 | char *name; | ||
| 208 | u32 offset; | ||
| 209 | fmode_t mode; | ||
| 210 | enum debugfs_format format; | ||
| 211 | }; | ||
| 212 | |||
| 213 | #define DEBUGFS_ENTRY(name, mode, format) { \ | ||
| 214 | #name, AVS_MBOX_##name, mode, format \ | ||
| 215 | } | ||
| 216 | |||
| 217 | /* | ||
| 218 | * These are used for debugfs only. Otherwise we use AVS_MBOX_PARAM() directly. | ||
| 219 | */ | ||
| 220 | #define AVS_MBOX_PARAM1 AVS_MBOX_PARAM(0) | ||
| 221 | #define AVS_MBOX_PARAM2 AVS_MBOX_PARAM(1) | ||
| 222 | #define AVS_MBOX_PARAM3 AVS_MBOX_PARAM(2) | ||
| 223 | #define AVS_MBOX_PARAM4 AVS_MBOX_PARAM(3) | ||
| 224 | |||
| 225 | /* | ||
| 226 | * This table stores the name, access permissions and offset for each hardware | ||
| 227 | * register and is used to generate debugfs entries. | ||
| 228 | */ | ||
| 229 | static struct debugfs_entry debugfs_entries[] = { | ||
| 230 | DEBUGFS_ENTRY(COMMAND, S_IWUSR, DEBUGFS_NORMAL), | ||
| 231 | DEBUGFS_ENTRY(STATUS, S_IWUSR, DEBUGFS_NORMAL), | ||
| 232 | DEBUGFS_ENTRY(VOLTAGE0, 0, DEBUGFS_FLOAT), | ||
| 233 | DEBUGFS_ENTRY(TEMP0, 0, DEBUGFS_FLOAT), | ||
| 234 | DEBUGFS_ENTRY(PV0, 0, DEBUGFS_FLOAT), | ||
| 235 | DEBUGFS_ENTRY(MV0, 0, DEBUGFS_FLOAT), | ||
| 236 | DEBUGFS_ENTRY(PARAM1, S_IWUSR, DEBUGFS_NORMAL), | ||
| 237 | DEBUGFS_ENTRY(PARAM2, S_IWUSR, DEBUGFS_NORMAL), | ||
| 238 | DEBUGFS_ENTRY(PARAM3, S_IWUSR, DEBUGFS_NORMAL), | ||
| 239 | DEBUGFS_ENTRY(PARAM4, S_IWUSR, DEBUGFS_NORMAL), | ||
| 240 | DEBUGFS_ENTRY(REVISION, 0, DEBUGFS_REV), | ||
| 241 | DEBUGFS_ENTRY(PSTATE, 0, DEBUGFS_NORMAL), | ||
| 242 | DEBUGFS_ENTRY(HEARTBEAT, 0, DEBUGFS_NORMAL), | ||
| 243 | DEBUGFS_ENTRY(MAGIC, S_IWUSR, DEBUGFS_NORMAL), | ||
| 244 | DEBUGFS_ENTRY(SIGMA_HVT, 0, DEBUGFS_NORMAL), | ||
| 245 | DEBUGFS_ENTRY(SIGMA_SVT, 0, DEBUGFS_NORMAL), | ||
| 246 | DEBUGFS_ENTRY(VOLTAGE1, 0, DEBUGFS_FLOAT), | ||
| 247 | DEBUGFS_ENTRY(TEMP1, 0, DEBUGFS_FLOAT), | ||
| 248 | DEBUGFS_ENTRY(PV1, 0, DEBUGFS_FLOAT), | ||
| 249 | DEBUGFS_ENTRY(MV1, 0, DEBUGFS_FLOAT), | ||
| 250 | DEBUGFS_ENTRY(FREQUENCY, 0, DEBUGFS_NORMAL), | ||
| 251 | }; | ||
| 252 | |||
| 253 | static int brcm_avs_target_index(struct cpufreq_policy *, unsigned int); | ||
| 254 | |||
| 255 | static char *__strtolower(char *s) | ||
| 256 | { | ||
| 257 | char *p; | ||
| 258 | |||
| 259 | for (p = s; *p; p++) | ||
| 260 | *p = tolower(*p); | ||
| 261 | |||
| 262 | return s; | ||
| 263 | } | ||
| 264 | |||
| 265 | #endif /* CONFIG_ARM_BRCMSTB_AVS_CPUFREQ_DEBUG */ | ||
| 266 | |||
| 267 | static void __iomem *__map_region(const char *name) | 183 | static void __iomem *__map_region(const char *name) |
| 268 | { | 184 | { |
| 269 | struct device_node *np; | 185 | struct device_node *np; |
| @@ -516,238 +432,6 @@ brcm_avs_get_freq_table(struct device *dev, struct private_data *priv) | |||
| 516 | return table; | 432 | return table; |
| 517 | } | 433 | } |
| 518 | 434 | ||
| 519 | #ifdef CONFIG_ARM_BRCMSTB_AVS_CPUFREQ_DEBUG | ||
| 520 | |||
| 521 | #define MANT(x) (unsigned int)(abs((x)) / 1000) | ||
| 522 | #define FRAC(x) (unsigned int)(abs((x)) - abs((x)) / 1000 * 1000) | ||
| 523 | |||
| 524 | static int brcm_avs_debug_show(struct seq_file *s, void *data) | ||
| 525 | { | ||
| 526 | struct debugfs_data *dbgfs = s->private; | ||
| 527 | void __iomem *base; | ||
| 528 | u32 val, offset; | ||
| 529 | |||
| 530 | if (!dbgfs) { | ||
| 531 | seq_puts(s, "No device pointer\n"); | ||
| 532 | return 0; | ||
| 533 | } | ||
| 534 | |||
| 535 | base = dbgfs->priv->base; | ||
| 536 | offset = dbgfs->entry->offset; | ||
| 537 | val = readl(base + offset); | ||
| 538 | switch (dbgfs->entry->format) { | ||
| 539 | case DEBUGFS_NORMAL: | ||
| 540 | seq_printf(s, "%u\n", val); | ||
| 541 | break; | ||
| 542 | case DEBUGFS_FLOAT: | ||
| 543 | seq_printf(s, "%d.%03d\n", MANT(val), FRAC(val)); | ||
| 544 | break; | ||
| 545 | case DEBUGFS_REV: | ||
| 546 | seq_printf(s, "%c.%c.%c.%c\n", (val >> 24 & 0xff), | ||
| 547 | (val >> 16 & 0xff), (val >> 8 & 0xff), | ||
| 548 | val & 0xff); | ||
| 549 | break; | ||
| 550 | } | ||
| 551 | seq_printf(s, "0x%08x\n", val); | ||
| 552 | |||
| 553 | return 0; | ||
| 554 | } | ||
| 555 | |||
| 556 | #undef MANT | ||
| 557 | #undef FRAC | ||
| 558 | |||
| 559 | static ssize_t brcm_avs_seq_write(struct file *file, const char __user *buf, | ||
| 560 | size_t size, loff_t *ppos) | ||
| 561 | { | ||
| 562 | struct seq_file *s = file->private_data; | ||
| 563 | struct debugfs_data *dbgfs = s->private; | ||
| 564 | struct private_data *priv = dbgfs->priv; | ||
| 565 | void __iomem *base, *avs_intr_base; | ||
| 566 | bool use_issue_command = false; | ||
| 567 | unsigned long val, offset; | ||
| 568 | char str[128]; | ||
| 569 | int ret; | ||
| 570 | char *str_ptr = str; | ||
| 571 | |||
| 572 | if (size >= sizeof(str)) | ||
| 573 | return -E2BIG; | ||
| 574 | |||
| 575 | memset(str, 0, sizeof(str)); | ||
| 576 | ret = copy_from_user(str, buf, size); | ||
| 577 | if (ret) | ||
| 578 | return ret; | ||
| 579 | |||
| 580 | base = priv->base; | ||
| 581 | avs_intr_base = priv->avs_intr_base; | ||
| 582 | offset = dbgfs->entry->offset; | ||
| 583 | /* | ||
| 584 | * Special case writing to "command" entry only: if the string starts | ||
| 585 | * with a 'c', we use the driver's __issue_avs_command() function. | ||
| 586 | * Otherwise, we perform a raw write. This should allow testing of raw | ||
| 587 | * access as well as using the higher level function. (Raw access | ||
| 588 | * doesn't clear the firmware return status after issuing the command.) | ||
| 589 | */ | ||
| 590 | if (str_ptr[0] == 'c' && offset == AVS_MBOX_COMMAND) { | ||
| 591 | use_issue_command = true; | ||
| 592 | str_ptr++; | ||
| 593 | } | ||
| 594 | if (kstrtoul(str_ptr, 0, &val) != 0) | ||
| 595 | return -EINVAL; | ||
| 596 | |||
| 597 | /* | ||
| 598 | * Setting the P-state is a special case. We need to update the CPU | ||
| 599 | * frequency we report. | ||
| 600 | */ | ||
| 601 | if (val == AVS_CMD_SET_PSTATE) { | ||
| 602 | struct cpufreq_policy *policy; | ||
| 603 | unsigned int pstate; | ||
| 604 | |||
| 605 | policy = cpufreq_cpu_get(smp_processor_id()); | ||
| 606 | /* Read back the P-state we are about to set */ | ||
| 607 | pstate = readl(base + AVS_MBOX_PARAM(0)); | ||
| 608 | if (use_issue_command) { | ||
| 609 | ret = brcm_avs_target_index(policy, pstate); | ||
| 610 | return ret ? ret : size; | ||
| 611 | } | ||
| 612 | policy->cur = policy->freq_table[pstate].frequency; | ||
| 613 | } | ||
| 614 | |||
| 615 | if (use_issue_command) { | ||
| 616 | ret = __issue_avs_command(priv, val, false, NULL); | ||
| 617 | } else { | ||
| 618 | /* Locking here is not perfect, but is only for debug. */ | ||
| 619 | ret = down_interruptible(&priv->sem); | ||
| 620 | if (ret) | ||
| 621 | return ret; | ||
| 622 | |||
| 623 | writel(val, base + offset); | ||
| 624 | /* We have to wake up the firmware to process a command. */ | ||
| 625 | if (offset == AVS_MBOX_COMMAND) | ||
| 626 | writel(AVS_CPU_L2_INT_MASK, | ||
| 627 | avs_intr_base + AVS_CPU_L2_SET0); | ||
| 628 | up(&priv->sem); | ||
| 629 | } | ||
| 630 | |||
| 631 | return ret ? ret : size; | ||
| 632 | } | ||
| 633 | |||
| 634 | static struct debugfs_entry *__find_debugfs_entry(const char *name) | ||
| 635 | { | ||
| 636 | int i; | ||
| 637 | |||
| 638 | for (i = 0; i < ARRAY_SIZE(debugfs_entries); i++) | ||
| 639 | if (strcasecmp(debugfs_entries[i].name, name) == 0) | ||
| 640 | return &debugfs_entries[i]; | ||
| 641 | |||
| 642 | return NULL; | ||
| 643 | } | ||
| 644 | |||
| 645 | static int brcm_avs_debug_open(struct inode *inode, struct file *file) | ||
| 646 | { | ||
| 647 | struct debugfs_data *data; | ||
| 648 | fmode_t fmode; | ||
| 649 | int ret; | ||
| 650 | |||
| 651 | /* | ||
| 652 | * seq_open(), which is called by single_open(), clears "write" access. | ||
| 653 | * We need write access to some files, so we preserve our access mode | ||
| 654 | * and restore it. | ||
| 655 | */ | ||
| 656 | fmode = file->f_mode; | ||
| 657 | /* | ||
| 658 | * Check access permissions even for root. We don't want to be writing | ||
| 659 | * to read-only registers. Access for regular users has already been | ||
| 660 | * checked by the VFS layer. | ||
| 661 | */ | ||
| 662 | if ((fmode & FMODE_WRITER) && !(inode->i_mode & S_IWUSR)) | ||
| 663 | return -EACCES; | ||
| 664 | |||
| 665 | data = kmalloc(sizeof(*data), GFP_KERNEL); | ||
| 666 | if (!data) | ||
| 667 | return -ENOMEM; | ||
| 668 | /* | ||
| 669 | * We use the same file system operations for all our debug files. To | ||
| 670 | * produce specific output, we look up the file name upon opening a | ||
| 671 | * debugfs entry and map it to a memory offset. This offset is then used | ||
| 672 | * in the generic "show" function to read a specific register. | ||
| 673 | */ | ||
| 674 | data->entry = __find_debugfs_entry(file->f_path.dentry->d_iname); | ||
| 675 | data->priv = inode->i_private; | ||
| 676 | |||
| 677 | ret = single_open(file, brcm_avs_debug_show, data); | ||
| 678 | if (ret) | ||
| 679 | kfree(data); | ||
| 680 | file->f_mode = fmode; | ||
| 681 | |||
| 682 | return ret; | ||
| 683 | } | ||
| 684 | |||
| 685 | static int brcm_avs_debug_release(struct inode *inode, struct file *file) | ||
| 686 | { | ||
| 687 | struct seq_file *seq_priv = file->private_data; | ||
| 688 | struct debugfs_data *data = seq_priv->private; | ||
| 689 | |||
| 690 | kfree(data); | ||
| 691 | return single_release(inode, file); | ||
| 692 | } | ||
| 693 | |||
| 694 | static const struct file_operations brcm_avs_debug_ops = { | ||
| 695 | .open = brcm_avs_debug_open, | ||
| 696 | .read = seq_read, | ||
| 697 | .write = brcm_avs_seq_write, | ||
| 698 | .llseek = seq_lseek, | ||
| 699 | .release = brcm_avs_debug_release, | ||
| 700 | }; | ||
| 701 | |||
| 702 | static void brcm_avs_cpufreq_debug_init(struct platform_device *pdev) | ||
| 703 | { | ||
| 704 | struct private_data *priv = platform_get_drvdata(pdev); | ||
| 705 | struct dentry *dir; | ||
| 706 | int i; | ||
| 707 | |||
| 708 | if (!priv) | ||
| 709 | return; | ||
| 710 | |||
| 711 | dir = debugfs_create_dir(BRCM_AVS_CPUFREQ_NAME, NULL); | ||
| 712 | if (IS_ERR_OR_NULL(dir)) | ||
| 713 | return; | ||
| 714 | priv->debugfs = dir; | ||
| 715 | |||
| 716 | for (i = 0; i < ARRAY_SIZE(debugfs_entries); i++) { | ||
| 717 | /* | ||
| 718 | * The DEBUGFS_ENTRY macro generates uppercase strings. We | ||
| 719 | * convert them to lowercase before creating the debugfs | ||
| 720 | * entries. | ||
| 721 | */ | ||
| 722 | char *entry = __strtolower(debugfs_entries[i].name); | ||
| 723 | fmode_t mode = debugfs_entries[i].mode; | ||
| 724 | |||
| 725 | if (!debugfs_create_file(entry, S_IFREG | S_IRUGO | mode, | ||
| 726 | dir, priv, &brcm_avs_debug_ops)) { | ||
| 727 | priv->debugfs = NULL; | ||
| 728 | debugfs_remove_recursive(dir); | ||
| 729 | break; | ||
| 730 | } | ||
| 731 | } | ||
| 732 | } | ||
| 733 | |||
| 734 | static void brcm_avs_cpufreq_debug_exit(struct platform_device *pdev) | ||
| 735 | { | ||
| 736 | struct private_data *priv = platform_get_drvdata(pdev); | ||
| 737 | |||
| 738 | if (priv && priv->debugfs) { | ||
| 739 | debugfs_remove_recursive(priv->debugfs); | ||
| 740 | priv->debugfs = NULL; | ||
| 741 | } | ||
| 742 | } | ||
| 743 | |||
| 744 | #else | ||
| 745 | |||
| 746 | static void brcm_avs_cpufreq_debug_init(struct platform_device *pdev) {} | ||
| 747 | static void brcm_avs_cpufreq_debug_exit(struct platform_device *pdev) {} | ||
| 748 | |||
| 749 | #endif /* CONFIG_ARM_BRCMSTB_AVS_CPUFREQ_DEBUG */ | ||
| 750 | |||
| 751 | /* | 435 | /* |
| 752 | * To ensure the right firmware is running we need to | 436 | * To ensure the right firmware is running we need to |
| 753 | * - check the MAGIC matches what we expect | 437 | * - check the MAGIC matches what we expect |
| @@ -1016,11 +700,8 @@ static int brcm_avs_cpufreq_probe(struct platform_device *pdev) | |||
| 1016 | return ret; | 700 | return ret; |
| 1017 | 701 | ||
| 1018 | brcm_avs_driver.driver_data = pdev; | 702 | brcm_avs_driver.driver_data = pdev; |
| 1019 | ret = cpufreq_register_driver(&brcm_avs_driver); | ||
| 1020 | if (!ret) | ||
| 1021 | brcm_avs_cpufreq_debug_init(pdev); | ||
| 1022 | 703 | ||
| 1023 | return ret; | 704 | return cpufreq_register_driver(&brcm_avs_driver); |
| 1024 | } | 705 | } |
| 1025 | 706 | ||
| 1026 | static int brcm_avs_cpufreq_remove(struct platform_device *pdev) | 707 | static int brcm_avs_cpufreq_remove(struct platform_device *pdev) |
| @@ -1032,8 +713,6 @@ static int brcm_avs_cpufreq_remove(struct platform_device *pdev) | |||
| 1032 | if (ret) | 713 | if (ret) |
| 1033 | return ret; | 714 | return ret; |
| 1034 | 715 | ||
| 1035 | brcm_avs_cpufreq_debug_exit(pdev); | ||
| 1036 | |||
| 1037 | priv = platform_get_drvdata(pdev); | 716 | priv = platform_get_drvdata(pdev); |
| 1038 | iounmap(priv->base); | 717 | iounmap(priv->base); |
| 1039 | iounmap(priv->avs_intr_base); | 718 | iounmap(priv->avs_intr_base); |
