// SPDX-License-Identifier: GPL-2.0-only/* * CPPC (Collaborative Processor Performance Control) methods used by CPUfreq drivers. * * (C) Copyright 2014, 2015 Linaro Ltd. * Author: Ashwin Chaugule <ashwin.chaugule@linaro.org> * * CPPC describes a few methods for controlling CPU performance using * information from a per CPU table called CPC. This table is described in * the ACPI v5.0+ specification. The table consists of a list of * registers which may be memory mapped or hardware registers and also may * include some static integer values. * * CPU performance is on an abstract continuous scale as against a discretized * P-state scale which is tied to CPU frequency only. In brief, the basic * operation involves: * * - OS makes a CPU performance request. (Can provide min and max bounds) * * - Platform (such as BMC) is free to optimize request within requested bounds * depending on power/thermal budgets etc. * * - Platform conveys its decision back to OS * * The communication between OS and platform occurs through another medium * called (PCC) Platform Communication Channel. This is a generic mailbox like * mechanism which includes doorbell semantics to indicate register updates. * See drivers/mailbox/pcc.c for details on PCC. * * Finer details about the PCC and CPPC spec are available in the ACPI v5.1 and * above specifications. */ #define pr_fmt(fmt) "ACPI CPPC: " fmt #include <linux/delay.h>#include <linux/iopoll.h>#include <linux/ktime.h>#include <linux/rwsem.h>#include <linux/wait.h>#include <linux/topology.h> #include <acpi/cppc_acpi.h> struct cppc_pcc_data { struct mbox_chan *pcc_channel; void __iomem *pcc_comm_addr; bool pcc_channel_acquired; unsigned int deadline_us; unsigned int pcc_mpar, pcc_mrtt, pcc_nominal; bool pending_pcc_write_cmd; /* Any pending/batched PCC write cmds? */ bool platform_owns_pcc; /* Ownership of PCC subspace */ unsigned int pcc_write_cnt; /* Running count of PCC write commands */ /* * Lock to provide controlled access to the PCC channel. * * For performance critical usecases(currently cppc_set_perf) * We need to take read_lock and check if channel belongs to OSPM * before reading or writing to PCC subspace * We need to take write_lock before transferring the channel * ownership to the platform via a Doorbell * This allows us to batch a number of CPPC requests if they happen * to originate in about the same time * * For non-performance critical usecases(init) * Take write_lock for all purposes which gives exclusive access */ struct rw_semaphore pcc_lock; /* Wait queue for CPUs whose requests were batched */ wait_queue_head_t pcc_write_wait_q; ktime_t last_cmd_cmpl_time; ktime_t last_mpar_reset; int mpar_count; int refcount;}; /* Array to represent the PCC channel per subspace ID */static struct cppc_pcc_data *pcc_data[MAX_PCC_SUBSPACES];/* The cpu_pcc_subspace_idx contains per CPU subspace ID */static DEFINE_PER_CPU(int, cpu_pcc_subspace_idx); /* * The cpc_desc structure contains the ACPI register details * as described in the per CPU _CPC tables. The details * include the type of register (e.g. PCC, System IO, FFH etc.) * and destination addresses which lets us READ/WRITE CPU performance * information using the appropriate I/O methods. */static DEFINE_PER_CPU(struct cpc_desc *, cpc_desc_ptr); /* pcc mapped address + header size + offset within PCC subspace */#define GET_PCC_VADDR(offs, pcc_ss_id) (pcc_data[pcc_ss_id]->pcc_comm_addr + \ 0x8 + (offs)) /* Check if a CPC register is in PCC */#define CPC_IN_PCC(cpc) ((cpc)->type == ACPI_TYPE_BUFFER && \ (cpc)->cpc_entry.reg.space_id == \ ACPI_ADR_SPACE_PLATFORM_COMM) /* Evaluates to True if reg is a NULL register descriptor */#define IS_NULL_REG(reg) ((reg)->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY && \ (reg)->address == 0 && \ (reg)->bit_width == 0 && \ (reg)->bit_offset == 0 && \ (reg)->access_width == 0) /* Evaluates to True if an optional cpc field is supported */#define CPC_SUPPORTED(cpc) ((cpc)->type == ACPI_TYPE_INTEGER ? \ !!(cpc)->cpc_entry.int_value : \ !IS_NULL_REG(&(cpc)->cpc_entry.reg))/* * Arbitrary Retries in case the remote processor is slow to respond * to PCC commands. Keeping it high enough to cover emulators where * the processors run painfully slow. */#define NUM_RETRIES 500ULL #define define_one_cppc_ro(_name) \static struct kobj_attribute _name = \__ATTR(_name, 0444, show_##_name, NULL) #define to_cpc_desc(a) container_of(a, struct cpc_desc, kobj) #define show_cppc_data(access_fn, struct_name, member_name) \ static ssize_t show_##member_name(struct kobject *kobj, \ struct kobj_attribute *attr, char *buf) \ { \ struct cpc_desc *cpc_ptr = to_cpc_desc(kobj); \ struct struct_name st_name = {0}; \ int ret; \ \ ret = access_fn(cpc_ptr->cpu_id, &st_name); \ if (ret) \ return ret; \ \ return scnprintf(buf, PAGE_SIZE, "%llu\n", \ (u64)st_name.member_name); \ } \ define_one_cppc_ro(member_name) show_cppc_data(cppc_get_perf_caps, cppc_perf_caps, highest_perf);show_cppc_data(cppc_get_perf_caps, cppc_perf_caps, lowest_perf);show_cppc_data(cppc_get_perf_caps, cppc_perf_caps, nominal_perf);show_cppc_data(cppc_get_perf_caps, cppc_perf_caps, lowest_nonlinear_perf);show_cppc_data(cppc_get_perf_caps, cppc_perf_caps, lowest_freq);show_cppc_data(cppc_get_perf_caps, cppc_perf_caps, nominal_freq); show_cppc_data(cppc_get_perf_ctrs, cppc_perf_fb_ctrs, reference_perf);show_cppc_data(cppc_get_perf_ctrs, cppc_perf_fb_ctrs, wraparound_time); static ssize_t show_feedback_ctrs(struct kobject *kobj, struct kobj_attribute *attr, char *buf){ struct cpc_desc *cpc_ptr = to_cpc_desc(kobj); struct cppc_perf_fb_ctrs fb_ctrs = {0}; int ret; ret = cppc_get_perf_ctrs(cpc_ptr->cpu_id, &fb_ctrs); if (ret) return ret; return scnprintf(buf, PAGE_SIZE, "ref:%llu del:%llu\n", fb_ctrs.reference, fb_ctrs.delivered);}define_one_cppc_ro(feedback_ctrs); static struct attribute *cppc_attrs[] = { &feedback_ctrs.attr, &reference_perf.attr, &wraparound_time.attr, &highest_perf.attr, &lowest_perf.attr, &lowest_nonlinear_perf.attr, &nominal_perf.attr, &nominal_freq.attr, &lowest_freq.attr, NULL}; static struct kobj_type cppc_ktype = { .sysfs_ops = &kobj_sysfs_ops, .default_attrs = cppc_attrs,}; static int check_pcc_chan(int pcc_ss_id, bool chk_err_bit){ int ret, status; struct cppc_pcc_data *pcc_ss_data = pcc_data[pcc_ss_id]; struct acpi_pcct_shared_memory __iomem *generic_comm_base = pcc_ss_data->pcc_comm_addr; if (!pcc_ss_data->platform_owns_pcc) return 0; /* * Poll PCC status register every 3us(delay_us) for maximum of * deadline_us(timeout_us) until PCC command complete bit is set(cond) */ ret = readw_relaxed_poll_timeout(&generic_comm_base->status, status, status & PCC_CMD_COMPLETE_MASK, 3, pcc_ss_data->deadline_us); if (likely(!ret)) { pcc_ss_data->platform_owns_pcc = false; if (chk_err_bit && (status & PCC_ERROR_MASK)) ret = -EIO; } if (unlikely(ret)) pr_err("PCC check channel failed for ss: %d. ret=%d\n", pcc_ss_id, ret); return ret;} /* * This function transfers the ownership of the PCC to the platform * So it must be called while holding write_lock(pcc_lock) */static int send_pcc_cmd(int pcc_ss_id, u16 cmd){ int ret = -EIO, i; struct cppc_pcc_data *pcc_ss_data = pcc_data[pcc_ss_id]; struct acpi_pcct_shared_memory __iomem *generic_comm_base = pcc_ss_data->pcc_comm_addr; unsigned int time_delta; /* * For CMD_WRITE we know for a fact the caller should have checked * the channel before writing to PCC space */ if (cmd == CMD_READ) { /* * If there are pending cpc_writes, then we stole the channel * before write completion, so first send a WRITE command to * platform */ if (pcc_ss_data->pending_pcc_write_cmd) send_pcc_cmd(pcc_ss_id, CMD_WRITE); ret = check_pcc_chan(pcc_ss_id, false); if (ret) goto end; } else /* CMD_WRITE */ pcc_ss_data->pending_pcc_write_cmd = FALSE; /* * Handle the Minimum Request Turnaround Time(MRTT) * "The minimum amount of time that OSPM must wait after the completion * of a command before issuing the next command, in microseconds" */ if (pcc_ss_data->pcc_mrtt) { time_delta = ktime_us_delta(ktime_get(), pcc_ss_data->last_cmd_cmpl_time); if (pcc_ss_data->pcc_mrtt > time_delta) udelay(pcc_ss_data->pcc_mrtt - time_delta); } /* * Handle the non-zero Maximum Periodic Access Rate(MPAR) * "The maximum number of periodic requests that the subspace channel can * support, reported in commands per minute. 0 indicates no limitation." * * This parameter should be ideally zero or large enough so that it can * handle maximum number of requests that all the cores in the system can * collectively generate. If it is not, we will follow the spec and just * not send the request to the platform after hitting the MPAR limit in * any 60s window */ if (pcc_ss_data->pcc_mpar) { if (pcc_ss_data->mpar_count == 0) { time_delta = ktime_ms_delta(ktime_get(), pcc_ss_data->last_mpar_reset); if ((time_delta < 60 * MSEC_PER_SEC) && pcc_ss_data->last_mpar_reset) { pr_debug("PCC cmd for subspace %d not sent due to MPAR limit", pcc_ss_id); ret = -EIO; goto end; } pcc_ss_data->last_mpar_reset = ktime_get(); pcc_ss_data->mpar_count = pcc_ss_data->pcc_mpar; } pcc_ss_data->mpar_count--; } /* Write to the shared comm region. */ writew_relaxed(cmd, &generic_comm_base->command); /* Flip CMD COMPLETE bit */ writew_relaxed(0, &generic_comm_base->status); pcc_ss_data->platform_owns_pcc = true; /* Ring doorbell */ ret = mbox_send_message(pcc_ss_data->pcc_channel, &cmd); if (ret < 0) { pr_err("Err sending PCC mbox message. ss: %d cmd:%d, ret:%d\n", pcc_ss_id, cmd, ret); goto end; } /* wait for completion and check for PCC errro bit */ ret = check_pcc_chan(pcc_ss_id, true); if (pcc_ss_data->pcc_mrtt) pcc_ss_data->last_cmd_cmpl_time = ktime_get(); if (pcc_ss_data->pcc_channel->mbox->txdone_irq) mbox_chan_txdone(pcc_ss_data->pcc_channel, ret); else mbox_client_txdone(pcc_ss_data->pcc_channel, ret); end: if (cmd == CMD_WRITE) { if (unlikely(ret)) { for_each_possible_cpu(i) { struct cpc_desc *desc = per_cpu(cpc_desc_ptr, i); if (!desc) continue; if (desc->write_cmd_id == pcc_ss_data->pcc_write_cnt) desc->write_cmd_status = ret; } } pcc_ss_data->pcc_write_cnt++; wake_up_all(&pcc_ss_data->pcc_write_wait_q); } return ret;} static void cppc_chan_tx_done(struct mbox_client *cl, void *msg, int ret){ if (ret < 0) pr_debug("TX did not complete: CMD sent:%x, ret:%d\n", *(u16 *)msg, ret); else pr_debug("TX completed. CMD sent:%x, ret:%d\n", *(u16 *)msg, ret);} static struct mbox_client cppc_mbox_cl = { .tx_done = cppc_chan_tx_done, .knows_txdone = true,}; static int acpi_get_psd(struct cpc_desc *cpc_ptr, acpi_handle handle){ int result = -EFAULT; acpi_status status = AE_OK; struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; struct acpi_buffer format = {sizeof("NNNNN"), "NNNNN"}; struct acpi_buffer state = {0, NULL}; union acpi_object *psd = NULL; struct acpi_psd_package *pdomain; status = acpi_evaluate_object_typed(handle, "_PSD", NULL, &buffer, ACPI_TYPE_PACKAGE); if (status == AE_NOT_FOUND) /* _PSD is optional */ return 0; if (ACPI_FAILURE(status)) return -ENODEV; psd = buffer.pointer; if (!psd || psd->package.count != 1) { pr_debug("Invalid _PSD data\n"); goto end; } pdomain = &(cpc_ptr->domain_info); state.length = sizeof(struct acpi_psd_package); state.pointer = pdomain; status = acpi_extract_package(&(psd->package.elements[0]), &format, &state); if (ACPI_FAILURE(status)) { pr_debug("Invalid _PSD data for CPU:%d\n", cpc_ptr->cpu_id); goto end; } if (pdomain->num_entries != ACPI_PSD_REV0_ENTRIES) { pr_debug("Unknown _PSD:num_entries for CPU:%d\n", cpc_ptr->cpu_id); goto end; } if (pdomain->revision != ACPI_PSD_REV0_REVISION) { pr_debug("Unknown _PSD:revision for CPU: %d\n", cpc_ptr->cpu_id); goto end; } if (pdomain->coord_type != DOMAIN_COORD_TYPE_SW_ALL && pdomain->coord_type != DOMAIN_COORD_TYPE_SW_ANY && pdomain->coord_type != DOMAIN_COORD_TYPE_HW_ALL) { pr_debug("Invalid _PSD:coord_type for CPU:%d\n", cpc_ptr->cpu_id); goto end; } result = 0;end: kfree(buffer.pointer); return result;} bool acpi_cpc_valid(void){ struct cpc_desc *cpc_ptr; int cpu; for_each_possible_cpu(cpu) { cpc_ptr = per_cpu(cpc_desc_ptr, cpu); if (!cpc_ptr) return false; } return true;}EXPORT_SYMBOL_GPL(acpi_cpc_valid); /** * acpi_get_psd_map - Map the CPUs in the freq domain of a given cpu * @cpu: Find all CPUs that share a domain with cpu. * @cpu_data: Pointer to CPU specific CPPC data including PSD info. * * Return: 0 for success or negative value for err. */int acpi_get_psd_map(unsigned int cpu, struct cppc_cpudata *cpu_data){ struct cpc_desc *cpc_ptr, *match_cpc_ptr; struct acpi_psd_package *match_pdomain; struct acpi_psd_package *pdomain; int count_target, i; /* * Now that we have _PSD data from all CPUs, let's setup P-state * domain info. */ cpc_ptr = per_cpu(cpc_desc_ptr, cpu); if (!cpc_ptr) return -EFAULT; pdomain = &(cpc_ptr->domain_info); cpumask_set_cpu(cpu, cpu_data->shared_cpu_map); if (pdomain->num_processors <= 1) return 0; /* Validate the Domain info */ count_target = pdomain->num_processors; if (pdomain->coord_type == DOMAIN_COORD_TYPE_SW_ALL) cpu_data->shared_type = CPUFREQ_SHARED_TYPE_ALL; else if (pdomain->coord_type == DOMAIN_COORD_TYPE_HW_ALL) cpu_data->shared_type = CPUFREQ_SHARED_TYPE_HW; else if (pdomain->coord_type == DOMAIN_COORD_TYPE_SW_ANY) cpu_data->shared_type = CPUFREQ_SHARED_TYPE_ANY; for_each_possible_cpu(i) { if (i == cpu) continue; match_cpc_ptr = per_cpu(cpc_desc_ptr, i); if (!match_cpc_ptr) goto err_fault; match_pdomain = &(match_cpc_ptr->domain_info); if (match_pdomain->domain != pdomain->domain) continue; /* Here i and cpu are in the same domain */ if (match_pdomain->num_processors != count_target) goto err_fault; if (pdomain->coord_type != match_pdomain->coord_type) goto err_fault; cpumask_set_cpu(i, cpu_data->shared_cpu_map); } return 0; err_fault: /* Assume no coordination on any error parsing domain info */ cpumask_clear(cpu_data->shared_cpu_map); cpumask_set_cpu(cpu, cpu_data->shared_cpu_map); cpu_data->shared_type = CPUFREQ_SHARED_TYPE_NONE; return -EFAULT;}EXPORT_SYMBOL_GPL(acpi_get_psd_map); static int register_pcc_channel(int pcc_ss_idx){ struct acpi_pcct_hw_reduced *cppc_ss; u64 usecs_lat; if (pcc_ss_idx >= 0) { pcc_data[pcc_ss_idx]->pcc_channel =Lines 1–500 of 1394. Load more
// SPDX-License-Identifier: GPL-2.0-only /* * CPPC (Collaborative Processor Performance Control) methods used by CPUfreq drivers. * * (C) Copyright 2014, 2015 Linaro Ltd. * Author: Ashwin Chaugule <ashwin.chaugule@linaro.org> * * CPPC describes a few methods for controlling CPU performance using * information from a per CPU table called CPC. This table is described in * the ACPI v5.0+ specification. The table consists of a list of * registers which may be memory mapped or hardware registers and also may * include some static integer values. * * CPU performance is on an abstract continuous scale as against a discretized * P-state scale which is tied to CPU frequency only. In brief, the basic * operation involves: * * - OS makes a CPU performance request. (Can provide min and max bounds) * * - Platform (such as BMC) is free to optimize request within requested bounds * depending on power/thermal budgets etc. * * - Platform conveys its decision back to OS * * The communication between OS and platform occurs through another medium * called (PCC) Platform Communication Channel. This is a generic mailbox like * mechanism which includes doorbell semantics to indicate register updates. * See drivers/mailbox/pcc.c for details on PCC. * * Finer details about the PCC and CPPC spec are available in the ACPI v5.1 and * above specifications. */ #define pr_fmt(fmt) "ACPI CPPC: " fmt #include <linux/delay.h> #include <linux/iopoll.h> #include <linux/ktime.h> #include <linux/rwsem.h> #include <linux/wait.h> #include <linux/topology.h> #include <acpi/cppc_acpi.h> struct cppc_pcc_data { struct mbox_chan *pcc_channel; void __iomem *pcc_comm_addr; bool pcc_channel_acquired; unsigned int deadline_us; unsigned int pcc_mpar, pcc_mrtt, pcc_nominal; bool pending_pcc_write_cmd; /* Any pending/batched PCC write cmds? */ bool platform_owns_pcc; /* Ownership of PCC subspace */ unsigned int pcc_write_cnt; /* Running count of PCC write commands */ /* * Lock to provide controlled access to the PCC channel. * * For performance critical usecases(currently cppc_set_perf) * We need to take read_lock and check if channel belongs to OSPM * before reading or writing to PCC subspace * We need to take write_lock before transferring the channel * ownership to the platform via a Doorbell * This allows us to batch a number of CPPC requests if they happen * to originate in about the same time * * For non-performance critical usecases(init) * Take write_lock for all purposes which gives exclusive access */ struct rw_semaphore pcc_lock; /* Wait queue for CPUs whose requests were batched */ wait_queue_head_t pcc_write_wait_q; ktime_t last_cmd_cmpl_time; ktime_t last_mpar_reset; int mpar_count; int refcount; }; /* Array to represent the PCC channel per subspace ID */ static struct cppc_pcc_data *pcc_data[MAX_PCC_SUBSPACES]; /* The cpu_pcc_subspace_idx contains per CPU subspace ID */ static DEFINE_PER_CPU(int, cpu_pcc_subspace_idx); /* * The cpc_desc structure contains the ACPI register details * as described in the per CPU _CPC tables. The details * include the type of register (e.g. PCC, System IO, FFH etc.) * and destination addresses which lets us READ/WRITE CPU performance * information using the appropriate I/O methods. */ static DEFINE_PER_CPU(struct cpc_desc *, cpc_desc_ptr); /* pcc mapped address + header size + offset within PCC subspace */ #define GET_PCC_VADDR(offs, pcc_ss_id) (pcc_data[pcc_ss_id]->pcc_comm_addr + \ 0x8 + (offs)) /* Check if a CPC register is in PCC */ #define CPC_IN_PCC(cpc) ((cpc)->type == ACPI_TYPE_BUFFER && \ (cpc)->cpc_entry.reg.space_id == \ ACPI_ADR_SPACE_PLATFORM_COMM) /* Evaluates to True if reg is a NULL register descriptor */ #define IS_NULL_REG(reg) ((reg)->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY && \ (reg)->address == 0 && \ (reg)->bit_width == 0 && \ (reg)->bit_offset == 0 && \ (reg)->access_width == 0) /* Evaluates to True if an optional cpc field is supported */ #define CPC_SUPPORTED(cpc) ((cpc)->type == ACPI_TYPE_INTEGER ? \ !!(cpc)->cpc_entry.int_value : \ !IS_NULL_REG(&(cpc)->cpc_entry.reg)) /* * Arbitrary Retries in case the remote processor is slow to respond * to PCC commands. Keeping it high enough to cover emulators where * the processors run painfully slow. */ #define NUM_RETRIES 500ULL #define define_one_cppc_ro(_name) \ static struct kobj_attribute _name = \ __ATTR(_name, 0444, show_##_name, NULL) #define to_cpc_desc(a) container_of(a, struct cpc_desc, kobj) #define show_cppc_data(access_fn, struct_name, member_name) \ static ssize_t show_##member_name(struct kobject *kobj, \ struct kobj_attribute *attr, char *buf) \ { \ struct cpc_desc *cpc_ptr = to_cpc_desc(kobj); \ struct struct_name st_name = {0}; \ int ret; \ \ ret = access_fn(cpc_ptr->cpu_id, &st_name); \ if (ret) \ return ret; \ \ return scnprintf(buf, PAGE_SIZE, "%llu\n", \ (u64)st_name.member_name); \ } \ define_one_cppc_ro(member_name) show_cppc_data(cppc_get_perf_caps, cppc_perf_caps, highest_perf); show_cppc_data(cppc_get_perf_caps, cppc_perf_caps, lowest_perf); show_cppc_data(cppc_get_perf_caps, cppc_perf_caps, nominal_perf); show_cppc_data(cppc_get_perf_caps, cppc_perf_caps, lowest_nonlinear_perf); show_cppc_data(cppc_get_perf_caps, cppc_perf_caps, lowest_freq); show_cppc_data(cppc_get_perf_caps, cppc_perf_caps, nominal_freq); show_cppc_data(cppc_get_perf_ctrs, cppc_perf_fb_ctrs, reference_perf); show_cppc_data(cppc_get_perf_ctrs, cppc_perf_fb_ctrs, wraparound_time); static ssize_t show_feedback_ctrs(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { struct cpc_desc *cpc_ptr = to_cpc_desc(kobj); struct cppc_perf_fb_ctrs fb_ctrs = {0}; int ret; ret = cppc_get_perf_ctrs(cpc_ptr->cpu_id, &fb_ctrs); if (ret) return ret; return scnprintf(buf, PAGE_SIZE, "ref:%llu del:%llu\n", fb_ctrs.reference, fb_ctrs.delivered); } define_one_cppc_ro(feedback_ctrs); static struct attribute *cppc_attrs[] = { &feedback_ctrs.attr, &reference_perf.attr, &wraparound_time.attr, &highest_perf.attr, &lowest_perf.attr, &lowest_nonlinear_perf.attr, &nominal_perf.attr, &nominal_freq.attr, &lowest_freq.attr, NULL }; static struct kobj_type cppc_ktype = { .sysfs_ops = &kobj_sysfs_ops, .default_attrs = cppc_attrs, }; static int check_pcc_chan(int pcc_ss_id, bool chk_err_bit) { int ret, status; struct cppc_pcc_data *pcc_ss_data = pcc_data[pcc_ss_id]; struct acpi_pcct_shared_memory __iomem *generic_comm_base = pcc_ss_data->pcc_comm_addr; if (!pcc_ss_data->platform_owns_pcc) return 0; /* * Poll PCC status register every 3us(delay_us) for maximum of * deadline_us(timeout_us) until PCC command complete bit is set(cond) */ ret = readw_relaxed_poll_timeout(&generic_comm_base->status, status, status & PCC_CMD_COMPLETE_MASK, 3, pcc_ss_data->deadline_us); if (likely(!ret)) { pcc_ss_data->platform_owns_pcc = false; if (chk_err_bit && (status & PCC_ERROR_MASK)) ret = -EIO; } if (unlikely(ret)) pr_err("PCC check channel failed for ss: %d. ret=%d\n", pcc_ss_id, ret); return ret; } /* * This function transfers the ownership of the PCC to the platform * So it must be called while holding write_lock(pcc_lock) */ static int send_pcc_cmd(int pcc_ss_id, u16 cmd) { int ret = -EIO, i; struct cppc_pcc_data *pcc_ss_data = pcc_data[pcc_ss_id]; struct acpi_pcct_shared_memory __iomem *generic_comm_base = pcc_ss_data->pcc_comm_addr; unsigned int time_delta; /* * For CMD_WRITE we know for a fact the caller should have checked * the channel before writing to PCC space */ if (cmd == CMD_READ) { /* * If there are pending cpc_writes, then we stole the channel * before write completion, so first send a WRITE command to * platform */ if (pcc_ss_data->pending_pcc_write_cmd) send_pcc_cmd(pcc_ss_id, CMD_WRITE); ret = check_pcc_chan(pcc_ss_id, false); if (ret) goto end; } else /* CMD_WRITE */ pcc_ss_data->pending_pcc_write_cmd = FALSE; /* * Handle the Minimum Request Turnaround Time(MRTT) * "The minimum amount of time that OSPM must wait after the completion * of a command before issuing the next command, in microseconds" */ if (pcc_ss_data->pcc_mrtt) { time_delta = ktime_us_delta(ktime_get(), pcc_ss_data->last_cmd_cmpl_time); if (pcc_ss_data->pcc_mrtt > time_delta) udelay(pcc_ss_data->pcc_mrtt - time_delta); } /* * Handle the non-zero Maximum Periodic Access Rate(MPAR) * "The maximum number of periodic requests that the subspace channel can * support, reported in commands per minute. 0 indicates no limitation." * * This parameter should be ideally zero or large enough so that it can * handle maximum number of requests that all the cores in the system can * collectively generate. If it is not, we will follow the spec and just * not send the request to the platform after hitting the MPAR limit in * any 60s window */ if (pcc_ss_data->pcc_mpar) { if (pcc_ss_data->mpar_count == 0) { time_delta = ktime_ms_delta(ktime_get(), pcc_ss_data->last_mpar_reset); if ((time_delta < 60 * MSEC_PER_SEC) && pcc_ss_data->last_mpar_reset) { pr_debug("PCC cmd for subspace %d not sent due to MPAR limit", pcc_ss_id); ret = -EIO; goto end; } pcc_ss_data->last_mpar_reset = ktime_get(); pcc_ss_data->mpar_count = pcc_ss_data->pcc_mpar; } pcc_ss_data->mpar_count--; } /* Write to the shared comm region. */ writew_relaxed(cmd, &generic_comm_base->command); /* Flip CMD COMPLETE bit */ writew_relaxed(0, &generic_comm_base->status); pcc_ss_data->platform_owns_pcc = true; /* Ring doorbell */ ret = mbox_send_message(pcc_ss_data->pcc_channel, &cmd); if (ret < 0) { pr_err("Err sending PCC mbox message. ss: %d cmd:%d, ret:%d\n", pcc_ss_id, cmd, ret); goto end; } /* wait for completion and check for PCC errro bit */ ret = check_pcc_chan(pcc_ss_id, true); if (pcc_ss_data->pcc_mrtt) pcc_ss_data->last_cmd_cmpl_time = ktime_get(); if (pcc_ss_data->pcc_channel->mbox->txdone_irq) mbox_chan_txdone(pcc_ss_data->pcc_channel, ret); else mbox_client_txdone(pcc_ss_data->pcc_channel, ret); end: if (cmd == CMD_WRITE) { if (unlikely(ret)) { for_each_possible_cpu(i) { struct cpc_desc *desc = per_cpu(cpc_desc_ptr, i); if (!desc) continue; if (desc->write_cmd_id == pcc_ss_data->pcc_write_cnt) desc->write_cmd_status = ret; } } pcc_ss_data->pcc_write_cnt++; wake_up_all(&pcc_ss_data->pcc_write_wait_q); } return ret; } static void cppc_chan_tx_done(struct mbox_client *cl, void *msg, int ret) { if (ret < 0) pr_debug("TX did not complete: CMD sent:%x, ret:%d\n", *(u16 *)msg, ret); else pr_debug("TX completed. CMD sent:%x, ret:%d\n", *(u16 *)msg, ret); } static struct mbox_client cppc_mbox_cl = { .tx_done = cppc_chan_tx_done, .knows_txdone = true, }; static int acpi_get_psd(struct cpc_desc *cpc_ptr, acpi_handle handle) { int result = -EFAULT; acpi_status status = AE_OK; struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; struct acpi_buffer format = {sizeof("NNNNN"), "NNNNN"}; struct acpi_buffer state = {0, NULL}; union acpi_object *psd = NULL; struct acpi_psd_package *pdomain; status = acpi_evaluate_object_typed(handle, "_PSD", NULL, &buffer, ACPI_TYPE_PACKAGE); if (status == AE_NOT_FOUND) /* _PSD is optional */ return 0; if (ACPI_FAILURE(status)) return -ENODEV; psd = buffer.pointer; if (!psd || psd->package.count != 1) { pr_debug("Invalid _PSD data\n"); goto end; } pdomain = &(cpc_ptr->domain_info); state.length = sizeof(struct acpi_psd_package); state.pointer = pdomain; status = acpi_extract_package(&(psd->package.elements[0]), &format, &state); if (ACPI_FAILURE(status)) { pr_debug("Invalid _PSD data for CPU:%d\n", cpc_ptr->cpu_id); goto end; } if (pdomain->num_entries != ACPI_PSD_REV0_ENTRIES) { pr_debug("Unknown _PSD:num_entries for CPU:%d\n", cpc_ptr->cpu_id); goto end; } if (pdomain->revision != ACPI_PSD_REV0_REVISION) { pr_debug("Unknown _PSD:revision for CPU: %d\n", cpc_ptr->cpu_id); goto end; } if (pdomain->coord_type != DOMAIN_COORD_TYPE_SW_ALL && pdomain->coord_type != DOMAIN_COORD_TYPE_SW_ANY && pdomain->coord_type != DOMAIN_COORD_TYPE_HW_ALL) { pr_debug("Invalid _PSD:coord_type for CPU:%d\n", cpc_ptr->cpu_id); goto end; } result = 0; end: kfree(buffer.pointer); return result; } bool acpi_cpc_valid(void) { struct cpc_desc *cpc_ptr; int cpu; for_each_possible_cpu(cpu) { cpc_ptr = per_cpu(cpc_desc_ptr, cpu); if (!cpc_ptr) return false; } return true; } EXPORT_SYMBOL_GPL(acpi_cpc_valid); /** * acpi_get_psd_map - Map the CPUs in the freq domain of a given cpu * @cpu: Find all CPUs that share a domain with cpu. * @cpu_data: Pointer to CPU specific CPPC data including PSD info. * * Return: 0 for success or negative value for err. */ int acpi_get_psd_map(unsigned int cpu, struct cppc_cpudata *cpu_data) { struct cpc_desc *cpc_ptr, *match_cpc_ptr; struct acpi_psd_package *match_pdomain; struct acpi_psd_package *pdomain; int count_target, i; /* * Now that we have _PSD data from all CPUs, let's setup P-state * domain info. */ cpc_ptr = per_cpu(cpc_desc_ptr, cpu); if (!cpc_ptr) return -EFAULT; pdomain = &(cpc_ptr->domain_info); cpumask_set_cpu(cpu, cpu_data->shared_cpu_map); if (pdomain->num_processors <= 1) return 0; /* Validate the Domain info */ count_target = pdomain->num_processors; if (pdomain->coord_type == DOMAIN_COORD_TYPE_SW_ALL) cpu_data->shared_type = CPUFREQ_SHARED_TYPE_ALL; else if (pdomain->coord_type == DOMAIN_COORD_TYPE_HW_ALL) cpu_data->shared_type = CPUFREQ_SHARED_TYPE_HW; else if (pdomain->coord_type == DOMAIN_COORD_TYPE_SW_ANY) cpu_data->shared_type = CPUFREQ_SHARED_TYPE_ANY; for_each_possible_cpu(i) { if (i == cpu) continue; match_cpc_ptr = per_cpu(cpc_desc_ptr, i); if (!match_cpc_ptr) goto err_fault; match_pdomain = &(match_cpc_ptr->domain_info); if (match_pdomain->domain != pdomain->domain) continue; /* Here i and cpu are in the same domain */ if (match_pdomain->num_processors != count_target) goto err_fault; if (pdomain->coord_type != match_pdomain->coord_type) goto err_fault; cpumask_set_cpu(i, cpu_data->shared_cpu_map); } return 0; err_fault: /* Assume no coordination on any error parsing domain info */ cpumask_clear(cpu_data->shared_cpu_map); cpumask_set_cpu(cpu, cpu_data->shared_cpu_map); cpu_data->shared_type = CPUFREQ_SHARED_TYPE_NONE; return -EFAULT; } EXPORT_SYMBOL_GPL(acpi_get_psd_map); static int register_pcc_channel(int pcc_ss_idx) { struct acpi_pcct_hw_reduced *cppc_ss; u64 usecs_lat; if (pcc_ss_idx >= 0) { pcc_data[pcc_ss_idx]->pcc_channel =