static int ac_sleep_before_get_state_ms;
++++ static int ac_check_pmic = 1;
static struct acpi_driver acpi_ac_driver = {
.name = "ac",
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"),
++++ },
++++ },
{},
};
kfree(ac);
}
---- dmi_check_system(ac_dmi_table);
return result;
}
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)
{
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;
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);