Merge branches 'acpi-cppc', 'acpi-misc', 'acpi-battery' and 'acpi-ac'
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>
Mon, 4 Jun 2018 08:43:34 +0000 (10:43 +0200)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Mon, 4 Jun 2018 08:43:34 +0000 (10:43 +0200)
* acpi-cppc:
  mailbox: PCC: erroneous error message when parsing ACPI PCCT
  ACPI / CPPC: Fix invalid PCC channel status errors
  ACPI / CPPC: Document CPPC sysfs interface
  cpufreq / CPPC: Support for CPPC v3
  ACPI / CPPC: Check for valid PCC subspace only if PCC is used
  ACPI / CPPC: Add support for CPPC v3

* acpi-misc:
  ACPI: Add missing prototype_for arch_post_acpi_subsys_init()
  ACPI: add missing newline to printk

* acpi-battery:
  ACPI / battery: Add quirk to avoid checking for PMIC with native driver
  ACPI / battery: Ignore AC state in handle_discharging on systems where it is broken
  ACPI / battery: Add handling for devices which wrongly report discharging state
  ACPI / battery: Remove initializer for unused ident dmi_system_id
  ACPI / AC: Remove initializer for unused ident dmi_system_id

* acpi-ac:
  ACPI / AC: Add quirk to avoid checking for PMIC with native driver

1  2  3  4  5 
drivers/acpi/ac.c
drivers/cpufreq/cppc_cpufreq.c

diff --combined drivers/acpi/ac.c
@@@@@@ -87,6 -87,6 -87,6 -87,6 -87,7 +87,7 @@@@@@ static int acpi_ac_open_fs(struct inod
     
     
     static int ac_sleep_before_get_state_ms;
++++ static int ac_check_pmic = 1;
     
     static struct acpi_driver acpi_ac_driver = {
        .name = "ac",
@@@@@@ -310,21 -310,21 -310,21 -310,21 -311,43 +311,43 @@@@@@ static int acpi_ac_battery_notify(struc
        return NOTIFY_OK;
     }
     
---- static int thinkpad_e530_quirk(const struct dmi_system_id *d)
++++ static int __init thinkpad_e530_quirk(const struct dmi_system_id *d)
     {
        ac_sleep_before_get_state_ms = 1000;
        return 0;
     }
     
---- static const struct dmi_system_id ac_dmi_table[] = {
++++ static int __init ac_do_not_check_pmic_quirk(const struct dmi_system_id *d)
++++ {
++++    ac_check_pmic = 0;
++++    return 0;
++++ }
++++ 
++++ static const struct dmi_system_id ac_dmi_table[]  __initconst = {
        {
+++ +   /* Thinkpad e530 */
        .callback = thinkpad_e530_quirk,
--- -   .ident = "thinkpad e530",
        .matches = {
                DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
                DMI_MATCH(DMI_PRODUCT_NAME, "32597CG"),
                },
        },
++++    {
++++            /* ECS EF20EA */
++++            .callback = ac_do_not_check_pmic_quirk,
++++            .matches = {
++++                    DMI_MATCH(DMI_PRODUCT_NAME, "EF20EA"),
++++            },
++++    },
++++    {
++++            /* Lenovo Ideapad Miix 320 */
++++            .callback = ac_do_not_check_pmic_quirk,
++++            .matches = {
++++              DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"),
++++              DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "80XF"),
++++              DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "Lenovo MIIX 320-10ICR"),
++++            },
++++    },
        {},
     };
     
@@@@@@ -384,7 -384,7 -384,7 -384,7 -407,6 +407,6 @@@@@@ end
                kfree(ac);
        }
     
----    dmi_check_system(ac_dmi_table);
        return result;
     }
     
@@@@@@ -442,13 -442,13 -442,13 -442,13 -464,17 +464,17 @@@@@@ static int __init acpi_ac_init(void
        if (acpi_disabled)
                return -ENODEV;
     
----    for (i = 0; i < ARRAY_SIZE(acpi_ac_blacklist); i++)
----            if (acpi_dev_present(acpi_ac_blacklist[i].hid, "1",
----                                 acpi_ac_blacklist[i].hrv)) {
----                    pr_info(PREFIX "AC: found native %s PMIC, not loading\n",
----                            acpi_ac_blacklist[i].hid);
----                    return -ENODEV;
----            }
++++    dmi_check_system(ac_dmi_table);
++++ 
++++    if (ac_check_pmic) {
++++            for (i = 0; i < ARRAY_SIZE(acpi_ac_blacklist); i++)
++++                    if (acpi_dev_present(acpi_ac_blacklist[i].hid, "1",
++++                                         acpi_ac_blacklist[i].hrv)) {
++++                            pr_info(PREFIX "AC: found native %s PMIC, not loading\n",
++++                                    acpi_ac_blacklist[i].hid);
++++                            return -ENODEV;
++++                    }
++++    }
     
     #ifdef CONFIG_ACPI_PROCFS_POWER
        acpi_ac_dir = acpi_lock_ac_dir();
      */
     static struct cppc_cpudata **all_cpu_data;
     
- ---/* Capture the max KHz from DMI */
- ---static u64 cppc_dmi_max_khz;
- ---
     /* Callback function used to retrieve the max frequency from DMI */
     static void cppc_find_dmi_mhz(const struct dmi_header *dm, void *private)
     {
@@@@@@ -75,6 -72,64 -75,6 -75,6 -75,6 +72,64 @@@@@@ static u64 cppc_get_dmi_max_khz(void
        return (1000 * mhz);
     }
     
+ +++/*
+ +++ * If CPPC lowest_freq and nominal_freq registers are exposed then we can
+ +++ * use them to convert perf to freq and vice versa
+ +++ *
+ +++ * If the perf/freq point lies between Nominal and Lowest, we can treat
+ +++ * (Low perf, Low freq) and (Nom Perf, Nom freq) as 2D co-ordinates of a line
+ +++ * and extrapolate the rest
+ +++ * For perf/freq > Nominal, we use the ratio perf:freq at Nominal for conversion
+ +++ */
+ +++static unsigned int cppc_cpufreq_perf_to_khz(struct cppc_cpudata *cpu,
+ +++                                   unsigned int perf)
+ +++{
+ +++   static u64 max_khz;
+ +++   struct cppc_perf_caps *caps = &cpu->perf_caps;
+ +++   u64 mul, div;
+ +++
+ +++   if (caps->lowest_freq && caps->nominal_freq) {
+ +++           if (perf >= caps->nominal_perf) {
+ +++                   mul = caps->nominal_freq;
+ +++                   div = caps->nominal_perf;
+ +++           } else {
+ +++                   mul = caps->nominal_freq - caps->lowest_freq;
+ +++                   div = caps->nominal_perf - caps->lowest_perf;
+ +++           }
+ +++   } else {
+ +++           if (!max_khz)
+ +++                   max_khz = cppc_get_dmi_max_khz();
+ +++           mul = max_khz;
+ +++           div = cpu->perf_caps.highest_perf;
+ +++   }
+ +++   return (u64)perf * mul / div;
+ +++}
+ +++
+ +++static unsigned int cppc_cpufreq_khz_to_perf(struct cppc_cpudata *cpu,
+ +++                                   unsigned int freq)
+ +++{
+ +++   static u64 max_khz;
+ +++   struct cppc_perf_caps *caps = &cpu->perf_caps;
+ +++   u64  mul, div;
+ +++
+ +++   if (caps->lowest_freq && caps->nominal_freq) {
+ +++           if (freq >= caps->nominal_freq) {
+ +++                   mul = caps->nominal_perf;
+ +++                   div = caps->nominal_freq;
+ +++           } else {
+ +++                   mul = caps->lowest_perf;
+ +++                   div = caps->lowest_freq;
+ +++           }
+ +++   } else {
+ +++           if (!max_khz)
+ +++                   max_khz = cppc_get_dmi_max_khz();
+ +++           mul = cpu->perf_caps.highest_perf;
+ +++           div = max_khz;
+ +++   }
+ +++
+ +++   return (u64)freq * mul / div;
+ +++}
+ +++
     static int cppc_cpufreq_set_target(struct cpufreq_policy *policy,
                unsigned int target_freq,
                unsigned int relation)
     
        cpu = all_cpu_data[policy->cpu];
     
- ---   desired_perf = (u64)target_freq * cpu->perf_caps.highest_perf / cppc_dmi_max_khz;
+ +++   desired_perf = cppc_cpufreq_khz_to_perf(cpu, target_freq);
        /* Return if it is exactly the same perf */
        if (desired_perf == cpu->perf_ctrls.desired_perf)
                return ret;
@@@@@@ -126,49 -181,6 -126,6 -126,6 -126,49 +181,49 @@@@@@ static void cppc_cpufreq_stop_cpu(struc
                                cpu->perf_caps.lowest_perf, cpu_num, ret);
     }
     
 +++ /*
 +++  * The PCC subspace describes the rate at which platform can accept commands
 +++  * on the shared PCC channel (including READs which do not count towards freq
 +++  * trasition requests), so ideally we need to use the PCC values as a fallback
 +++  * if we don't have a platform specific transition_delay_us
 +++  */
 +++ #ifdef CONFIG_ARM64
 +++ #include <asm/cputype.h>
 +++ 
 +++ static unsigned int cppc_cpufreq_get_transition_delay_us(int cpu)
 +++ {
 +++    unsigned long implementor = read_cpuid_implementor();
 +++    unsigned long part_num = read_cpuid_part_number();
 +++    unsigned int delay_us = 0;
 +++ 
 +++    switch (implementor) {
 +++    case ARM_CPU_IMP_QCOM:
 +++            switch (part_num) {
 +++            case QCOM_CPU_PART_FALKOR_V1:
 +++            case QCOM_CPU_PART_FALKOR:
 +++                    delay_us = 10000;
 +++                    break;
 +++            default:
 +++                    delay_us = cppc_get_transition_latency(cpu) / NSEC_PER_USEC;
 +++                    break;
 +++            }
 +++            break;
 +++    default:
 +++            delay_us = cppc_get_transition_latency(cpu) / NSEC_PER_USEC;
 +++            break;
 +++    }
 +++ 
 +++    return delay_us;
 +++ }
 +++ 
 +++ #else
 +++ 
 +++ static unsigned int cppc_cpufreq_get_transition_delay_us(int cpu)
 +++ {
 +++    return cppc_get_transition_latency(cpu) / NSEC_PER_USEC;
 +++ }
 +++ #endif
 +++ 
     static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
     {
        struct cppc_cpudata *cpu;
                return ret;
        }
     
- ---   cppc_dmi_max_khz = cppc_get_dmi_max_khz();
+ +++   /* Convert the lowest and nominal freq from MHz to KHz */
+ +++   cpu->perf_caps.lowest_freq *= 1000;
+ +++   cpu->perf_caps.nominal_freq *= 1000;
     
        /*
         * Set min to lowest nonlinear perf to avoid any efficiency penalty (see
         * Section 8.4.7.1.1.5 of ACPI 6.1 spec)
         */
- ---   policy->min = cpu->perf_caps.lowest_nonlinear_perf * cppc_dmi_max_khz /
- ---           cpu->perf_caps.highest_perf;
- ---   policy->max = cppc_dmi_max_khz;
+ +++   policy->min = cppc_cpufreq_perf_to_khz(cpu, cpu->perf_caps.lowest_nonlinear_perf);
+ +++   policy->max = cppc_cpufreq_perf_to_khz(cpu, cpu->perf_caps.highest_perf);
     
        /*
         * Set cpuinfo.min_freq to Lowest to make the full range of performance
         * available if userspace wants to use any perf between lowest & lowest
         * nonlinear perf
         */
- ---   policy->cpuinfo.min_freq = cpu->perf_caps.lowest_perf * cppc_dmi_max_khz /
- ---           cpu->perf_caps.highest_perf;
- ---   policy->cpuinfo.max_freq = cppc_dmi_max_khz;
+ +++   policy->cpuinfo.min_freq = cppc_cpufreq_perf_to_khz(cpu, cpu->perf_caps.lowest_perf);
+ +++   policy->cpuinfo.max_freq = cppc_cpufreq_perf_to_khz(cpu, cpu->perf_caps.highest_perf);
     
 ---    policy->transition_delay_us = cppc_get_transition_latency(cpu_num) /
 ---            NSEC_PER_USEC;
 +++    policy->transition_delay_us = cppc_cpufreq_get_transition_delay_us(cpu_num);
        policy->shared_type = cpu->shared_type;
     
        if (policy->shared_type == CPUFREQ_SHARED_TYPE_ANY) {
        cpu->cur_policy = policy;
     
        /* Set policy->cur to max now. The governors will adjust later. */
- ---   policy->cur = cppc_dmi_max_khz;
+ +++   policy->cur = cppc_cpufreq_perf_to_khz(cpu,
+ +++                                   cpu->perf_caps.highest_perf);
        cpu->perf_ctrls.desired_perf = cpu->perf_caps.highest_perf;
     
        ret = cppc_set_perf(cpu_num, &cpu->perf_ctrls);