From patchwork Tue May 6 14:10:32 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ashwin Chaugule X-Patchwork-Id: 29710 Return-Path: X-Original-To: linaro@patches.linaro.org Delivered-To: linaro@patches.linaro.org Received: from mail-yk0-f197.google.com (mail-yk0-f197.google.com [209.85.160.197]) by ip-10-151-82-157.ec2.internal (Postfix) with ESMTPS id 069AC202E6 for ; Tue, 6 May 2014 14:10:51 +0000 (UTC) Received: by mail-yk0-f197.google.com with SMTP id 19sf10790565ykq.4 for ; Tue, 06 May 2014 07:10:51 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:delivered-to:from:to:cc:subject :date:message-id:in-reply-to:references:x-original-sender :x-original-authentication-results:precedence:mailing-list:list-id :list-post:list-help:list-archive:list-unsubscribe; bh=zVZkW62q4gapeYdtLgeIz0MpJGS4ZC2i4Huvhy7Dy5Y=; b=M4B265SnWHEh4VZhOz8aO6HTCrUxwcl5lrR6OreIauWcjgwoCB7i6/uD5ZYWlbXpiK DBZb61uAHl/K/Zm4xdDhesbHPRipa5Y5qBUdc9m0h+FW0gxR2WiIyS/nQ4TpSob08L74 jFWk0fmOl4INYNNU58Z1DDFuwOwxbEBw95Vy1t0uUWu5vwfd4HakXPbIE0307eWwpvQ/ Ad2G52+16T8CPT2UeuKUb+pDb9hyBbtoAIQP4MTruoQespwFkR0/MDs2OLs3SQXNm17k RkBBqB4m/nRO+QOgq5e6ccQ52s98eWhbS5lG4Gd9yc+cdLLOQAAHMjyPsKqmc/5tJupd dPxA== X-Gm-Message-State: ALoCoQkCCaFjdjH3F+rlM4hW/9Yy0krFZMEAZzsZlZPCwR4HkuXjUUmQ591WCS4jk1GlzkH9P3Mh X-Received: by 10.236.149.193 with SMTP id x41mr1053299yhj.24.1399385451790; Tue, 06 May 2014 07:10:51 -0700 (PDT) MIME-Version: 1.0 X-BeenThere: patchwork-forward@linaro.org Received: by 10.140.22.145 with SMTP id 17ls2858770qgn.21.gmail; Tue, 06 May 2014 07:10:51 -0700 (PDT) X-Received: by 10.220.47.201 with SMTP id o9mr646422vcf.65.1399385451575; Tue, 06 May 2014 07:10:51 -0700 (PDT) Received: from mail-ve0-f180.google.com (mail-ve0-f180.google.com [209.85.128.180]) by mx.google.com with ESMTPS id fo15si2338775vcb.171.2014.05.06.07.10.51 for (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Tue, 06 May 2014 07:10:51 -0700 (PDT) Received-SPF: none (google.com: patch+caf_=patchwork-forward=linaro.org@linaro.org does not designate permitted sender hosts) client-ip=209.85.128.180; Received: by mail-ve0-f180.google.com with SMTP id db12so6928527veb.25 for ; Tue, 06 May 2014 07:10:51 -0700 (PDT) X-Received: by 10.52.237.228 with SMTP id vf4mr904285vdc.47.1399385451460; Tue, 06 May 2014 07:10:51 -0700 (PDT) X-Forwarded-To: patchwork-forward@linaro.org X-Forwarded-For: patch@linaro.org patchwork-forward@linaro.org Delivered-To: patches@linaro.org Received: by 10.220.221.72 with SMTP id ib8csp226882vcb; Tue, 6 May 2014 07:10:51 -0700 (PDT) X-Received: by 10.66.144.102 with SMTP id sl6mr6750010pab.96.1399385450660; Tue, 06 May 2014 07:10:50 -0700 (PDT) Received: from mail-pa0-f50.google.com (mail-pa0-f50.google.com [209.85.220.50]) by mx.google.com with ESMTPS id qf5si12079898pac.211.2014.05.06.07.10.50 for (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Tue, 06 May 2014 07:10:50 -0700 (PDT) Received-SPF: none (google.com: ashwin.chaugule@linaro.org does not designate permitted sender hosts) client-ip=209.85.220.50; Received: by mail-pa0-f50.google.com with SMTP id fb1so4105633pad.23 for ; Tue, 06 May 2014 07:10:50 -0700 (PDT) X-Received: by 10.66.122.1 with SMTP id lo1mr6846922pab.118.1399385450126; Tue, 06 May 2014 07:10:50 -0700 (PDT) Received: from linaro5.qualcomm.com (rrcs-67-52-130-30.west.biz.rr.com. [67.52.130.30]) by mx.google.com with ESMTPSA id oa3sm615845pbb.15.2014.05.06.07.10.47 for (version=TLSv1.1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Tue, 06 May 2014 07:10:48 -0700 (PDT) From: Ashwin Chaugule To: linaro-acpi@lists.linaro.org Cc: linda.knippers@hp.com, andanappa.mallikarjunappa.kori@hp.com, Thomas.Mingarelli@hp.com, Eden.Cardenas@hp.com, ilias.biris@linaro.org, patches@linaro.org, al.stone@linaro.org, hanjun.guo@linaro.org, graeme.gregory@linaro.org, andrea.gallo@linaro.org, Ashwin Chaugule Subject: [RFC 2/2] CPPC: Initial support for Collaborative Processor Performance Control Date: Tue, 6 May 2014 10:10:32 -0400 Message-Id: <1399385432-22157-3-git-send-email-ashwin.chaugule@linaro.org> X-Mailer: git-send-email 1.8.3.2 In-Reply-To: <1399385432-22157-1-git-send-email-ashwin.chaugule@linaro.org> References: <1399385432-22157-1-git-send-email-ashwin.chaugule@linaro.org> X-Removed-Original-Auth: Dkim didn't pass. X-Original-Sender: ashwin.chaugule@linaro.org X-Original-Authentication-Results: mx.google.com; spf=neutral (google.com: patch+caf_=patchwork-forward=linaro.org@linaro.org does not designate permitted sender hosts) smtp.mail=patch+caf_=patchwork-forward=linaro.org@linaro.org Precedence: list Mailing-list: list patchwork-forward@linaro.org; contact patchwork-forward+owners@linaro.org List-ID: X-Google-Group-Id: 836684582541 List-Post: , List-Help: , List-Archive: List-Unsubscribe: , Add initial support for CPPC as defined in the ACPI5.0a spec. Signed-off-by: Ashwin Chaugule --- arch/arm64/Kconfig | 2 + drivers/cpufreq/Kconfig | 11 +- drivers/cpufreq/Makefile | 1 + drivers/cpufreq/cppc-cpufreq.c | 298 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 311 insertions(+), 1 deletion(-) create mode 100644 drivers/cpufreq/cppc-cpufreq.c diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 94cc542..434d577 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -325,6 +325,8 @@ endmenu menu "CPU Power Management" +source "drivers/cpufreq/Kconfig" + source "drivers/cpuidle/Kconfig" endmenu diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig index 3a7202d..a2712a1 100644 --- a/drivers/cpufreq/Kconfig +++ b/drivers/cpufreq/Kconfig @@ -222,13 +222,22 @@ config GENERIC_CPUFREQ_CPU0 If in doubt, say N. +config CPPC_CPUFREQ + bool "CPPC CPUFreq driver" + depends on ACPI && ACPI_PCC + default n + help + CPPC is Collaborative Processor Performance Control. It allows the OS + to request CPU performance in an abstract manner and lets the platform + (e.g. BMC) interpret it in a way that is specific to that platform. + menu "x86 CPU frequency scaling drivers" depends on X86 source "drivers/cpufreq/Kconfig.x86" endmenu menu "ARM CPU frequency scaling drivers" -depends on ARM +depends on ARM || ARM64 source "drivers/cpufreq/Kconfig.arm" endmenu diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index 0fd80cb..08c2aa1 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_CPU_FREQ_GOV_COMMON) += cpufreq_governor.o obj-$(CONFIG_GENERIC_CPUFREQ_CPU0) += cpufreq-cpu0.o +obj-$(CONFIG_CPPC_CPUFREQ) += cppc-cpufreq.o ################################################################################## # x86 drivers. # Link order matters. K8 is preferred to ACPI because of firmware bugs in early diff --git a/drivers/cpufreq/cppc-cpufreq.c b/drivers/cpufreq/cppc-cpufreq.c new file mode 100644 index 0000000..437eb89 --- /dev/null +++ b/drivers/cpufreq/cppc-cpufreq.c @@ -0,0 +1,298 @@ +/* + * Copyright (C) 2014 Linaro Ltd. + * Author: Ashwin Chaugule + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include + +#define MAX_CPPC_PCC_ENT 15 +#define CPPC_EN 1 + +#define CMD_COMPLETE 1 + +/* PCC Commands used by CPPC */ +enum cppc_ppc_cmds { + CMD_READ, + CMD_WRITE, + RESERVED, +}; + +#define PCC_SUBSPACE_IDX 2 + +struct cpc_desc { + unsigned int num_entries; + unsigned int version; + struct acpi_generic_address pcc_regs[MAX_CPPC_PCC_ENT]; +}; + +/* These are indexes into the per-cpu pcc_regs[] */ +enum cppc_pcc_regs { + HIGHEST_PERF, // Highest Performance + NOMINAL_PERF, // Nominal Performance + LOW_NON_LINEAR_PERF, // Lowest Nonlinear Performance + LOWEST_PERF, // Lowest Performance + GUARANTEED_PERF, // Guaranteed Performance Register + DESIRED_PERF, // Desired Performance Register + MIN_PERF, // Minimum Performance Register + MAX_PERF, // Maximum Performance Register + PERF_REDUC_TOLERANCE, // Performance Reduction Tolerance Register + TIME_WINDOW, // Time Window Register + CTR_WRAP_TIME, // Counter Wraparound Time + NOMINAL_CTR, // Nominal Counter Register + DELIVERED_CTR, // Delivered Counter Register + PERF_LIMITED, // Performance Limited Register + ENABLE // Enable Register +}; + +static struct cpc_desc __percpu *cpc_desc; + +/* PCC Shared COMM region base address for this client */ +static u64 pcc_comm_base_addr; /* Returned by the Subspace structure */ +static void __iomem *comm_base_addr; /* For use after ioremap */ + +extern int get_pcc_comm_channel(u32 ss_idx, u64* addr, int *len); +extern u16 send_pcc_cmd(u8 cmd, u8 sci, u32 ss_idx, u64 * __iomem base_addr); + +static u64 past_delivered; +static u64 past_nominal; + +static unsigned int cppc_get_freq(unsigned int cpu) +{ + struct cpc_desc *current_cpu_cpc = per_cpu(cpc_desc, cpu); + u64 curr_delivered, curr_nominal, curr_perf; + u16 status; + + status = send_pcc_cmd(CMD_READ, 0, PCC_SUBSPACE_IDX, comm_base_addr); + if (status & CMD_COMPLETE) { + acpi_read(&curr_delivered, ¤t_cpu_cpc->pcc_regs[DELIVERED_CTR]); + acpi_read(&curr_nominal, ¤t_cpu_cpc->pcc_regs[NOMINAL_CTR]); + + /* XXX: Check for overflow regs. */ + curr_perf = (curr_nominal) * ((curr_delivered - past_delivered) + / (curr_nominal - past_nominal)); + } else { + pr_err("Failed to get Delivered Perf for CPU:%d\n", cpu); + return -EINVAL; + } + + return curr_perf; +} + +/* For each CPU, get its _CPC table and extract its Perf thresholds. */ +static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy) +{ + unsigned int cpu = policy->cpu; + struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL}; + union acpi_object *out_obj, *pcc_obj; + struct cpc_desc *current_cpu_cpc = per_cpu(cpc_desc, cpu); + struct acpi_generic_address *gas_t; + char proc_name[11]; + unsigned int num_ent, ret = 0, i; + acpi_handle handle; + acpi_status status; + u16 pcc_status; + + /* Search for this CPU's _CPC and populate its info. */ + sprintf(proc_name, "\\_SB.CPU%d", cpu); + + status = acpi_get_handle(NULL, proc_name, &handle); + if (ACPI_FAILURE(status)) { + ret = -ENODEV; + goto out_free; + } + + if (!acpi_has_method(handle, "_CPC")) { + ret = -ENODEV; + goto out_free; + } + + status = acpi_evaluate_object(handle, "_CPC", NULL, &output); + if (ACPI_FAILURE(status)) { + ret = -ENODEV; + goto out_free; + } + + out_obj = (union acpi_object *) output.pointer; + if (out_obj->type != ACPI_TYPE_PACKAGE) { + ret = -ENODEV; + goto out_free; + } + + num_ent = out_obj->package.count; + current_cpu_cpc->num_entries = num_ent; + + /* Iterate through each entry in _CPC */ + for (i=0; ipackage.elements[i]; + + if (pcc_obj->type != ACPI_TYPE_BUFFER) { + pr_err("Malformed PCC entry in CPC table\n"); + ret = -EINVAL; + goto out_free; + } + + gas_t = (struct acpi_generic_address *)pcc_obj->buffer.pointer; + + /* Get PCC parameters for each CPPC register. */ + current_cpu_cpc->pcc_regs[i] = (struct acpi_generic_address) { + .space_id = gas_t->space_id, + .bit_width = gas_t->bit_width, + .bit_offset = gas_t->bit_offset, + .access_width = gas_t->access_width, + /* PCC communication space begins 8 bytes after PCCT shared mem header */ + .address = (u64) (comm_base_addr + 8 + (u64) gas_t->address), + }; + } + + /* Get the MAX and MIN Thresholds for this CPU. */ + pcc_status = send_pcc_cmd(CMD_READ, 0, PCC_SUBSPACE_IDX, comm_base_addr); + if (pcc_status & CMD_COMPLETE) { + u64 max, min; + /*XXX: policy needs to be modified to take in all 64bits. */ + acpi_read(&max, ¤t_cpu_cpc->pcc_regs[HIGHEST_PERF]); + policy->max = (u32) max; + acpi_read(&min, ¤t_cpu_cpc->pcc_regs[LOWEST_PERF]); + policy->min = (u32) min; + } else { + ret = -ENODEV; + pr_err("Failed to get CPPC parameters for CPU:%d\n", cpu); + goto out_free; + } + + /* XXX: Is policy->related_cpus filled up via _PSD info? */ + + /* XXX: Populate this CPUs freq table data.*/ + /* Enable CPPC on this CPU */ + acpi_write(CPPC_EN, ¤t_cpu_cpc->pcc_regs[ENABLE]); + pcc_status = send_pcc_cmd(CMD_WRITE, 0, PCC_SUBSPACE_IDX, comm_base_addr); + if (pcc_status & CMD_COMPLETE) { + ret = -EINVAL; + pr_debug("Failed to init CPPC on CPU:%d\n", cpu); + } + +out_free: + kfree(output.pointer); + return ret; +} + +static int cppc_cpufreq_verify(struct cpufreq_policy *policy) +{ + cpufreq_verify_within_cpu_limits(policy); + return 0; +} + +static int cppc_cpufreq_target(struct cpufreq_policy *policy, + unsigned int target_freq, + unsigned int relation) +{ + unsigned int cpu = policy->cpu; + struct cpc_desc *current_cpu_cpc = per_cpu(cpc_desc, cpu); + struct cpufreq_freqs freqs; + u16 status; + + freqs.old = policy->cur; + freqs.new = target_freq; + + cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE); + + /* Set CPU Perf thresholds and current desired perf value. */ + acpi_write(policy->max, ¤t_cpu_cpc->pcc_regs[MAX_PERF]); + + acpi_write(policy->min, ¤t_cpu_cpc->pcc_regs[MIN_PERF]); + + acpi_write(target_freq, ¤t_cpu_cpc->pcc_regs[DESIRED_PERF]); + + status = send_pcc_cmd(CMD_WRITE, 0, PCC_SUBSPACE_IDX, comm_base_addr); + if (status & CMD_COMPLETE) { + pr_debug("Failed to set target CPU perf for CPU:%d, status:%d\n", + cpu, status); + return -EINVAL; + } + + return 0; +} + +static int cppc_cpufreq_cpu_exit(struct cpufreq_policy *policy) +{ + iounmap(comm_base_addr); + free_percpu(cpc_desc); + return 0; +} + +static struct cpufreq_driver cppc_cpufreq_driver = { + .get = cppc_get_freq, + .verify = cppc_cpufreq_verify, +/* XXX: setpolicy gives us a high and low range for CPU perf, + * but doesnt give what is current desired CPU perf value ? + */ +// .setpolicy = cppc_cpufreq_setpolicy, + .target = cppc_cpufreq_target, + .init = cppc_cpufreq_cpu_init, + .exit = cppc_cpufreq_cpu_exit, + .name = "cppc-cpufreq", +}; + +static int __init cppc_cpufreq_init(void) +{ + int ret; + int len; + + if (acpi_disabled) + return 0; + + /* Per CPU descriptors for _CPC. */ + cpc_desc = alloc_percpu(struct cpc_desc); + + if (!cpc_desc) { + ret = -ENOMEM; + pr_debug("No mem for CPC descriptors\n"); + goto out_err; + } + + /* PCC Subspace Communication region for CPPC. */ + ret = get_pcc_comm_channel(PCC_SUBSPACE_IDX, &pcc_comm_base_addr, &len); + + if (ret) { + pr_err("No PCC Communication channel found\n"); + ret = -ENODEV; + goto out_err; + } + + /* Base address returned from PCC subspace desc needs to ioremap'd. + * Used by the client to send/recv data from platform. + */ + comm_base_addr = ioremap_nocache(pcc_comm_base_addr, len); + + if (comm_base_addr) { + pr_err("Could not map PCC communicate channel\n"); + ret = -ENOMEM; + goto out_err; + } + + /* Plug into CPUFreq subsystem. */ + ret = cpufreq_register_driver(&cppc_cpufreq_driver); + return ret; + +out_err: + free_percpu(cpc_desc); + return ret; +} + +late_initcall(cppc_cpufreq_init); +/*XXX: Add kmod support */