162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * CPPC (Collaborative Processor Performance Control) methods used by CPUfreq drivers.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * (C) Copyright 2014, 2015 Linaro Ltd.
662306a36Sopenharmony_ci * Author: Ashwin Chaugule <ashwin.chaugule@linaro.org>
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * CPPC describes a few methods for controlling CPU performance using
962306a36Sopenharmony_ci * information from a per CPU table called CPC. This table is described in
1062306a36Sopenharmony_ci * the ACPI v5.0+ specification. The table consists of a list of
1162306a36Sopenharmony_ci * registers which may be memory mapped or hardware registers and also may
1262306a36Sopenharmony_ci * include some static integer values.
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci * CPU performance is on an abstract continuous scale as against a discretized
1562306a36Sopenharmony_ci * P-state scale which is tied to CPU frequency only. In brief, the basic
1662306a36Sopenharmony_ci * operation involves:
1762306a36Sopenharmony_ci *
1862306a36Sopenharmony_ci * - OS makes a CPU performance request. (Can provide min and max bounds)
1962306a36Sopenharmony_ci *
2062306a36Sopenharmony_ci * - Platform (such as BMC) is free to optimize request within requested bounds
2162306a36Sopenharmony_ci *   depending on power/thermal budgets etc.
2262306a36Sopenharmony_ci *
2362306a36Sopenharmony_ci * - Platform conveys its decision back to OS
2462306a36Sopenharmony_ci *
2562306a36Sopenharmony_ci * The communication between OS and platform occurs through another medium
2662306a36Sopenharmony_ci * called (PCC) Platform Communication Channel. This is a generic mailbox like
2762306a36Sopenharmony_ci * mechanism which includes doorbell semantics to indicate register updates.
2862306a36Sopenharmony_ci * See drivers/mailbox/pcc.c for details on PCC.
2962306a36Sopenharmony_ci *
3062306a36Sopenharmony_ci * Finer details about the PCC and CPPC spec are available in the ACPI v5.1 and
3162306a36Sopenharmony_ci * above specifications.
3262306a36Sopenharmony_ci */
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#define pr_fmt(fmt)	"ACPI CPPC: " fmt
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci#include <linux/delay.h>
3762306a36Sopenharmony_ci#include <linux/iopoll.h>
3862306a36Sopenharmony_ci#include <linux/ktime.h>
3962306a36Sopenharmony_ci#include <linux/rwsem.h>
4062306a36Sopenharmony_ci#include <linux/wait.h>
4162306a36Sopenharmony_ci#include <linux/topology.h>
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci#include <acpi/cppc_acpi.h>
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistruct cppc_pcc_data {
4662306a36Sopenharmony_ci	struct pcc_mbox_chan *pcc_channel;
4762306a36Sopenharmony_ci	void __iomem *pcc_comm_addr;
4862306a36Sopenharmony_ci	bool pcc_channel_acquired;
4962306a36Sopenharmony_ci	unsigned int deadline_us;
5062306a36Sopenharmony_ci	unsigned int pcc_mpar, pcc_mrtt, pcc_nominal;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	bool pending_pcc_write_cmd;	/* Any pending/batched PCC write cmds? */
5362306a36Sopenharmony_ci	bool platform_owns_pcc;		/* Ownership of PCC subspace */
5462306a36Sopenharmony_ci	unsigned int pcc_write_cnt;	/* Running count of PCC write commands */
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	/*
5762306a36Sopenharmony_ci	 * Lock to provide controlled access to the PCC channel.
5862306a36Sopenharmony_ci	 *
5962306a36Sopenharmony_ci	 * For performance critical usecases(currently cppc_set_perf)
6062306a36Sopenharmony_ci	 *	We need to take read_lock and check if channel belongs to OSPM
6162306a36Sopenharmony_ci	 * before reading or writing to PCC subspace
6262306a36Sopenharmony_ci	 *	We need to take write_lock before transferring the channel
6362306a36Sopenharmony_ci	 * ownership to the platform via a Doorbell
6462306a36Sopenharmony_ci	 *	This allows us to batch a number of CPPC requests if they happen
6562306a36Sopenharmony_ci	 * to originate in about the same time
6662306a36Sopenharmony_ci	 *
6762306a36Sopenharmony_ci	 * For non-performance critical usecases(init)
6862306a36Sopenharmony_ci	 *	Take write_lock for all purposes which gives exclusive access
6962306a36Sopenharmony_ci	 */
7062306a36Sopenharmony_ci	struct rw_semaphore pcc_lock;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	/* Wait queue for CPUs whose requests were batched */
7362306a36Sopenharmony_ci	wait_queue_head_t pcc_write_wait_q;
7462306a36Sopenharmony_ci	ktime_t last_cmd_cmpl_time;
7562306a36Sopenharmony_ci	ktime_t last_mpar_reset;
7662306a36Sopenharmony_ci	int mpar_count;
7762306a36Sopenharmony_ci	int refcount;
7862306a36Sopenharmony_ci};
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci/* Array to represent the PCC channel per subspace ID */
8162306a36Sopenharmony_cistatic struct cppc_pcc_data *pcc_data[MAX_PCC_SUBSPACES];
8262306a36Sopenharmony_ci/* The cpu_pcc_subspace_idx contains per CPU subspace ID */
8362306a36Sopenharmony_cistatic DEFINE_PER_CPU(int, cpu_pcc_subspace_idx);
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci/*
8662306a36Sopenharmony_ci * The cpc_desc structure contains the ACPI register details
8762306a36Sopenharmony_ci * as described in the per CPU _CPC tables. The details
8862306a36Sopenharmony_ci * include the type of register (e.g. PCC, System IO, FFH etc.)
8962306a36Sopenharmony_ci * and destination addresses which lets us READ/WRITE CPU performance
9062306a36Sopenharmony_ci * information using the appropriate I/O methods.
9162306a36Sopenharmony_ci */
9262306a36Sopenharmony_cistatic DEFINE_PER_CPU(struct cpc_desc *, cpc_desc_ptr);
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci/* pcc mapped address + header size + offset within PCC subspace */
9562306a36Sopenharmony_ci#define GET_PCC_VADDR(offs, pcc_ss_id) (pcc_data[pcc_ss_id]->pcc_comm_addr + \
9662306a36Sopenharmony_ci						0x8 + (offs))
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci/* Check if a CPC register is in PCC */
9962306a36Sopenharmony_ci#define CPC_IN_PCC(cpc) ((cpc)->type == ACPI_TYPE_BUFFER &&		\
10062306a36Sopenharmony_ci				(cpc)->cpc_entry.reg.space_id ==	\
10162306a36Sopenharmony_ci				ACPI_ADR_SPACE_PLATFORM_COMM)
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci/* Check if a CPC register is in SystemMemory */
10462306a36Sopenharmony_ci#define CPC_IN_SYSTEM_MEMORY(cpc) ((cpc)->type == ACPI_TYPE_BUFFER &&	\
10562306a36Sopenharmony_ci				(cpc)->cpc_entry.reg.space_id ==	\
10662306a36Sopenharmony_ci				ACPI_ADR_SPACE_SYSTEM_MEMORY)
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci/* Check if a CPC register is in SystemIo */
10962306a36Sopenharmony_ci#define CPC_IN_SYSTEM_IO(cpc) ((cpc)->type == ACPI_TYPE_BUFFER &&	\
11062306a36Sopenharmony_ci				(cpc)->cpc_entry.reg.space_id ==	\
11162306a36Sopenharmony_ci				ACPI_ADR_SPACE_SYSTEM_IO)
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci/* Evaluates to True if reg is a NULL register descriptor */
11462306a36Sopenharmony_ci#define IS_NULL_REG(reg) ((reg)->space_id ==  ACPI_ADR_SPACE_SYSTEM_MEMORY && \
11562306a36Sopenharmony_ci				(reg)->address == 0 &&			\
11662306a36Sopenharmony_ci				(reg)->bit_width == 0 &&		\
11762306a36Sopenharmony_ci				(reg)->bit_offset == 0 &&		\
11862306a36Sopenharmony_ci				(reg)->access_width == 0)
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci/* Evaluates to True if an optional cpc field is supported */
12162306a36Sopenharmony_ci#define CPC_SUPPORTED(cpc) ((cpc)->type == ACPI_TYPE_INTEGER ?		\
12262306a36Sopenharmony_ci				!!(cpc)->cpc_entry.int_value :		\
12362306a36Sopenharmony_ci				!IS_NULL_REG(&(cpc)->cpc_entry.reg))
12462306a36Sopenharmony_ci/*
12562306a36Sopenharmony_ci * Arbitrary Retries in case the remote processor is slow to respond
12662306a36Sopenharmony_ci * to PCC commands. Keeping it high enough to cover emulators where
12762306a36Sopenharmony_ci * the processors run painfully slow.
12862306a36Sopenharmony_ci */
12962306a36Sopenharmony_ci#define NUM_RETRIES 500ULL
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci#define OVER_16BTS_MASK ~0xFFFFULL
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci#define define_one_cppc_ro(_name)		\
13462306a36Sopenharmony_cistatic struct kobj_attribute _name =		\
13562306a36Sopenharmony_ci__ATTR(_name, 0444, show_##_name, NULL)
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci#define to_cpc_desc(a) container_of(a, struct cpc_desc, kobj)
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci#define show_cppc_data(access_fn, struct_name, member_name)		\
14062306a36Sopenharmony_ci	static ssize_t show_##member_name(struct kobject *kobj,		\
14162306a36Sopenharmony_ci				struct kobj_attribute *attr, char *buf)	\
14262306a36Sopenharmony_ci	{								\
14362306a36Sopenharmony_ci		struct cpc_desc *cpc_ptr = to_cpc_desc(kobj);		\
14462306a36Sopenharmony_ci		struct struct_name st_name = {0};			\
14562306a36Sopenharmony_ci		int ret;						\
14662306a36Sopenharmony_ci									\
14762306a36Sopenharmony_ci		ret = access_fn(cpc_ptr->cpu_id, &st_name);		\
14862306a36Sopenharmony_ci		if (ret)						\
14962306a36Sopenharmony_ci			return ret;					\
15062306a36Sopenharmony_ci									\
15162306a36Sopenharmony_ci		return sysfs_emit(buf, "%llu\n",		\
15262306a36Sopenharmony_ci				(u64)st_name.member_name);		\
15362306a36Sopenharmony_ci	}								\
15462306a36Sopenharmony_ci	define_one_cppc_ro(member_name)
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_cishow_cppc_data(cppc_get_perf_caps, cppc_perf_caps, highest_perf);
15762306a36Sopenharmony_cishow_cppc_data(cppc_get_perf_caps, cppc_perf_caps, lowest_perf);
15862306a36Sopenharmony_cishow_cppc_data(cppc_get_perf_caps, cppc_perf_caps, nominal_perf);
15962306a36Sopenharmony_cishow_cppc_data(cppc_get_perf_caps, cppc_perf_caps, lowest_nonlinear_perf);
16062306a36Sopenharmony_cishow_cppc_data(cppc_get_perf_caps, cppc_perf_caps, lowest_freq);
16162306a36Sopenharmony_cishow_cppc_data(cppc_get_perf_caps, cppc_perf_caps, nominal_freq);
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_cishow_cppc_data(cppc_get_perf_ctrs, cppc_perf_fb_ctrs, reference_perf);
16462306a36Sopenharmony_cishow_cppc_data(cppc_get_perf_ctrs, cppc_perf_fb_ctrs, wraparound_time);
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_cistatic ssize_t show_feedback_ctrs(struct kobject *kobj,
16762306a36Sopenharmony_ci		struct kobj_attribute *attr, char *buf)
16862306a36Sopenharmony_ci{
16962306a36Sopenharmony_ci	struct cpc_desc *cpc_ptr = to_cpc_desc(kobj);
17062306a36Sopenharmony_ci	struct cppc_perf_fb_ctrs fb_ctrs = {0};
17162306a36Sopenharmony_ci	int ret;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	ret = cppc_get_perf_ctrs(cpc_ptr->cpu_id, &fb_ctrs);
17462306a36Sopenharmony_ci	if (ret)
17562306a36Sopenharmony_ci		return ret;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	return sysfs_emit(buf, "ref:%llu del:%llu\n",
17862306a36Sopenharmony_ci			fb_ctrs.reference, fb_ctrs.delivered);
17962306a36Sopenharmony_ci}
18062306a36Sopenharmony_cidefine_one_cppc_ro(feedback_ctrs);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_cistatic struct attribute *cppc_attrs[] = {
18362306a36Sopenharmony_ci	&feedback_ctrs.attr,
18462306a36Sopenharmony_ci	&reference_perf.attr,
18562306a36Sopenharmony_ci	&wraparound_time.attr,
18662306a36Sopenharmony_ci	&highest_perf.attr,
18762306a36Sopenharmony_ci	&lowest_perf.attr,
18862306a36Sopenharmony_ci	&lowest_nonlinear_perf.attr,
18962306a36Sopenharmony_ci	&nominal_perf.attr,
19062306a36Sopenharmony_ci	&nominal_freq.attr,
19162306a36Sopenharmony_ci	&lowest_freq.attr,
19262306a36Sopenharmony_ci	NULL
19362306a36Sopenharmony_ci};
19462306a36Sopenharmony_ciATTRIBUTE_GROUPS(cppc);
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_cistatic const struct kobj_type cppc_ktype = {
19762306a36Sopenharmony_ci	.sysfs_ops = &kobj_sysfs_ops,
19862306a36Sopenharmony_ci	.default_groups = cppc_groups,
19962306a36Sopenharmony_ci};
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_cistatic int check_pcc_chan(int pcc_ss_id, bool chk_err_bit)
20262306a36Sopenharmony_ci{
20362306a36Sopenharmony_ci	int ret, status;
20462306a36Sopenharmony_ci	struct cppc_pcc_data *pcc_ss_data = pcc_data[pcc_ss_id];
20562306a36Sopenharmony_ci	struct acpi_pcct_shared_memory __iomem *generic_comm_base =
20662306a36Sopenharmony_ci		pcc_ss_data->pcc_comm_addr;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	if (!pcc_ss_data->platform_owns_pcc)
20962306a36Sopenharmony_ci		return 0;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	/*
21262306a36Sopenharmony_ci	 * Poll PCC status register every 3us(delay_us) for maximum of
21362306a36Sopenharmony_ci	 * deadline_us(timeout_us) until PCC command complete bit is set(cond)
21462306a36Sopenharmony_ci	 */
21562306a36Sopenharmony_ci	ret = readw_relaxed_poll_timeout(&generic_comm_base->status, status,
21662306a36Sopenharmony_ci					status & PCC_CMD_COMPLETE_MASK, 3,
21762306a36Sopenharmony_ci					pcc_ss_data->deadline_us);
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	if (likely(!ret)) {
22062306a36Sopenharmony_ci		pcc_ss_data->platform_owns_pcc = false;
22162306a36Sopenharmony_ci		if (chk_err_bit && (status & PCC_ERROR_MASK))
22262306a36Sopenharmony_ci			ret = -EIO;
22362306a36Sopenharmony_ci	}
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	if (unlikely(ret))
22662306a36Sopenharmony_ci		pr_err("PCC check channel failed for ss: %d. ret=%d\n",
22762306a36Sopenharmony_ci		       pcc_ss_id, ret);
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	return ret;
23062306a36Sopenharmony_ci}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci/*
23362306a36Sopenharmony_ci * This function transfers the ownership of the PCC to the platform
23462306a36Sopenharmony_ci * So it must be called while holding write_lock(pcc_lock)
23562306a36Sopenharmony_ci */
23662306a36Sopenharmony_cistatic int send_pcc_cmd(int pcc_ss_id, u16 cmd)
23762306a36Sopenharmony_ci{
23862306a36Sopenharmony_ci	int ret = -EIO, i;
23962306a36Sopenharmony_ci	struct cppc_pcc_data *pcc_ss_data = pcc_data[pcc_ss_id];
24062306a36Sopenharmony_ci	struct acpi_pcct_shared_memory __iomem *generic_comm_base =
24162306a36Sopenharmony_ci		pcc_ss_data->pcc_comm_addr;
24262306a36Sopenharmony_ci	unsigned int time_delta;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	/*
24562306a36Sopenharmony_ci	 * For CMD_WRITE we know for a fact the caller should have checked
24662306a36Sopenharmony_ci	 * the channel before writing to PCC space
24762306a36Sopenharmony_ci	 */
24862306a36Sopenharmony_ci	if (cmd == CMD_READ) {
24962306a36Sopenharmony_ci		/*
25062306a36Sopenharmony_ci		 * If there are pending cpc_writes, then we stole the channel
25162306a36Sopenharmony_ci		 * before write completion, so first send a WRITE command to
25262306a36Sopenharmony_ci		 * platform
25362306a36Sopenharmony_ci		 */
25462306a36Sopenharmony_ci		if (pcc_ss_data->pending_pcc_write_cmd)
25562306a36Sopenharmony_ci			send_pcc_cmd(pcc_ss_id, CMD_WRITE);
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci		ret = check_pcc_chan(pcc_ss_id, false);
25862306a36Sopenharmony_ci		if (ret)
25962306a36Sopenharmony_ci			goto end;
26062306a36Sopenharmony_ci	} else /* CMD_WRITE */
26162306a36Sopenharmony_ci		pcc_ss_data->pending_pcc_write_cmd = FALSE;
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	/*
26462306a36Sopenharmony_ci	 * Handle the Minimum Request Turnaround Time(MRTT)
26562306a36Sopenharmony_ci	 * "The minimum amount of time that OSPM must wait after the completion
26662306a36Sopenharmony_ci	 * of a command before issuing the next command, in microseconds"
26762306a36Sopenharmony_ci	 */
26862306a36Sopenharmony_ci	if (pcc_ss_data->pcc_mrtt) {
26962306a36Sopenharmony_ci		time_delta = ktime_us_delta(ktime_get(),
27062306a36Sopenharmony_ci					    pcc_ss_data->last_cmd_cmpl_time);
27162306a36Sopenharmony_ci		if (pcc_ss_data->pcc_mrtt > time_delta)
27262306a36Sopenharmony_ci			udelay(pcc_ss_data->pcc_mrtt - time_delta);
27362306a36Sopenharmony_ci	}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	/*
27662306a36Sopenharmony_ci	 * Handle the non-zero Maximum Periodic Access Rate(MPAR)
27762306a36Sopenharmony_ci	 * "The maximum number of periodic requests that the subspace channel can
27862306a36Sopenharmony_ci	 * support, reported in commands per minute. 0 indicates no limitation."
27962306a36Sopenharmony_ci	 *
28062306a36Sopenharmony_ci	 * This parameter should be ideally zero or large enough so that it can
28162306a36Sopenharmony_ci	 * handle maximum number of requests that all the cores in the system can
28262306a36Sopenharmony_ci	 * collectively generate. If it is not, we will follow the spec and just
28362306a36Sopenharmony_ci	 * not send the request to the platform after hitting the MPAR limit in
28462306a36Sopenharmony_ci	 * any 60s window
28562306a36Sopenharmony_ci	 */
28662306a36Sopenharmony_ci	if (pcc_ss_data->pcc_mpar) {
28762306a36Sopenharmony_ci		if (pcc_ss_data->mpar_count == 0) {
28862306a36Sopenharmony_ci			time_delta = ktime_ms_delta(ktime_get(),
28962306a36Sopenharmony_ci						    pcc_ss_data->last_mpar_reset);
29062306a36Sopenharmony_ci			if ((time_delta < 60 * MSEC_PER_SEC) && pcc_ss_data->last_mpar_reset) {
29162306a36Sopenharmony_ci				pr_debug("PCC cmd for subspace %d not sent due to MPAR limit",
29262306a36Sopenharmony_ci					 pcc_ss_id);
29362306a36Sopenharmony_ci				ret = -EIO;
29462306a36Sopenharmony_ci				goto end;
29562306a36Sopenharmony_ci			}
29662306a36Sopenharmony_ci			pcc_ss_data->last_mpar_reset = ktime_get();
29762306a36Sopenharmony_ci			pcc_ss_data->mpar_count = pcc_ss_data->pcc_mpar;
29862306a36Sopenharmony_ci		}
29962306a36Sopenharmony_ci		pcc_ss_data->mpar_count--;
30062306a36Sopenharmony_ci	}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	/* Write to the shared comm region. */
30362306a36Sopenharmony_ci	writew_relaxed(cmd, &generic_comm_base->command);
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	/* Flip CMD COMPLETE bit */
30662306a36Sopenharmony_ci	writew_relaxed(0, &generic_comm_base->status);
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	pcc_ss_data->platform_owns_pcc = true;
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	/* Ring doorbell */
31162306a36Sopenharmony_ci	ret = mbox_send_message(pcc_ss_data->pcc_channel->mchan, &cmd);
31262306a36Sopenharmony_ci	if (ret < 0) {
31362306a36Sopenharmony_ci		pr_err("Err sending PCC mbox message. ss: %d cmd:%d, ret:%d\n",
31462306a36Sopenharmony_ci		       pcc_ss_id, cmd, ret);
31562306a36Sopenharmony_ci		goto end;
31662306a36Sopenharmony_ci	}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	/* wait for completion and check for PCC error bit */
31962306a36Sopenharmony_ci	ret = check_pcc_chan(pcc_ss_id, true);
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	if (pcc_ss_data->pcc_mrtt)
32262306a36Sopenharmony_ci		pcc_ss_data->last_cmd_cmpl_time = ktime_get();
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	if (pcc_ss_data->pcc_channel->mchan->mbox->txdone_irq)
32562306a36Sopenharmony_ci		mbox_chan_txdone(pcc_ss_data->pcc_channel->mchan, ret);
32662306a36Sopenharmony_ci	else
32762306a36Sopenharmony_ci		mbox_client_txdone(pcc_ss_data->pcc_channel->mchan, ret);
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ciend:
33062306a36Sopenharmony_ci	if (cmd == CMD_WRITE) {
33162306a36Sopenharmony_ci		if (unlikely(ret)) {
33262306a36Sopenharmony_ci			for_each_possible_cpu(i) {
33362306a36Sopenharmony_ci				struct cpc_desc *desc = per_cpu(cpc_desc_ptr, i);
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci				if (!desc)
33662306a36Sopenharmony_ci					continue;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci				if (desc->write_cmd_id == pcc_ss_data->pcc_write_cnt)
33962306a36Sopenharmony_ci					desc->write_cmd_status = ret;
34062306a36Sopenharmony_ci			}
34162306a36Sopenharmony_ci		}
34262306a36Sopenharmony_ci		pcc_ss_data->pcc_write_cnt++;
34362306a36Sopenharmony_ci		wake_up_all(&pcc_ss_data->pcc_write_wait_q);
34462306a36Sopenharmony_ci	}
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	return ret;
34762306a36Sopenharmony_ci}
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_cistatic void cppc_chan_tx_done(struct mbox_client *cl, void *msg, int ret)
35062306a36Sopenharmony_ci{
35162306a36Sopenharmony_ci	if (ret < 0)
35262306a36Sopenharmony_ci		pr_debug("TX did not complete: CMD sent:%x, ret:%d\n",
35362306a36Sopenharmony_ci				*(u16 *)msg, ret);
35462306a36Sopenharmony_ci	else
35562306a36Sopenharmony_ci		pr_debug("TX completed. CMD sent:%x, ret:%d\n",
35662306a36Sopenharmony_ci				*(u16 *)msg, ret);
35762306a36Sopenharmony_ci}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_cistatic struct mbox_client cppc_mbox_cl = {
36062306a36Sopenharmony_ci	.tx_done = cppc_chan_tx_done,
36162306a36Sopenharmony_ci	.knows_txdone = true,
36262306a36Sopenharmony_ci};
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_cistatic int acpi_get_psd(struct cpc_desc *cpc_ptr, acpi_handle handle)
36562306a36Sopenharmony_ci{
36662306a36Sopenharmony_ci	int result = -EFAULT;
36762306a36Sopenharmony_ci	acpi_status status = AE_OK;
36862306a36Sopenharmony_ci	struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
36962306a36Sopenharmony_ci	struct acpi_buffer format = {sizeof("NNNNN"), "NNNNN"};
37062306a36Sopenharmony_ci	struct acpi_buffer state = {0, NULL};
37162306a36Sopenharmony_ci	union acpi_object  *psd = NULL;
37262306a36Sopenharmony_ci	struct acpi_psd_package *pdomain;
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	status = acpi_evaluate_object_typed(handle, "_PSD", NULL,
37562306a36Sopenharmony_ci					    &buffer, ACPI_TYPE_PACKAGE);
37662306a36Sopenharmony_ci	if (status == AE_NOT_FOUND)	/* _PSD is optional */
37762306a36Sopenharmony_ci		return 0;
37862306a36Sopenharmony_ci	if (ACPI_FAILURE(status))
37962306a36Sopenharmony_ci		return -ENODEV;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	psd = buffer.pointer;
38262306a36Sopenharmony_ci	if (!psd || psd->package.count != 1) {
38362306a36Sopenharmony_ci		pr_debug("Invalid _PSD data\n");
38462306a36Sopenharmony_ci		goto end;
38562306a36Sopenharmony_ci	}
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	pdomain = &(cpc_ptr->domain_info);
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	state.length = sizeof(struct acpi_psd_package);
39062306a36Sopenharmony_ci	state.pointer = pdomain;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	status = acpi_extract_package(&(psd->package.elements[0]),
39362306a36Sopenharmony_ci		&format, &state);
39462306a36Sopenharmony_ci	if (ACPI_FAILURE(status)) {
39562306a36Sopenharmony_ci		pr_debug("Invalid _PSD data for CPU:%d\n", cpc_ptr->cpu_id);
39662306a36Sopenharmony_ci		goto end;
39762306a36Sopenharmony_ci	}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	if (pdomain->num_entries != ACPI_PSD_REV0_ENTRIES) {
40062306a36Sopenharmony_ci		pr_debug("Unknown _PSD:num_entries for CPU:%d\n", cpc_ptr->cpu_id);
40162306a36Sopenharmony_ci		goto end;
40262306a36Sopenharmony_ci	}
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	if (pdomain->revision != ACPI_PSD_REV0_REVISION) {
40562306a36Sopenharmony_ci		pr_debug("Unknown _PSD:revision for CPU: %d\n", cpc_ptr->cpu_id);
40662306a36Sopenharmony_ci		goto end;
40762306a36Sopenharmony_ci	}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	if (pdomain->coord_type != DOMAIN_COORD_TYPE_SW_ALL &&
41062306a36Sopenharmony_ci	    pdomain->coord_type != DOMAIN_COORD_TYPE_SW_ANY &&
41162306a36Sopenharmony_ci	    pdomain->coord_type != DOMAIN_COORD_TYPE_HW_ALL) {
41262306a36Sopenharmony_ci		pr_debug("Invalid _PSD:coord_type for CPU:%d\n", cpc_ptr->cpu_id);
41362306a36Sopenharmony_ci		goto end;
41462306a36Sopenharmony_ci	}
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	result = 0;
41762306a36Sopenharmony_ciend:
41862306a36Sopenharmony_ci	kfree(buffer.pointer);
41962306a36Sopenharmony_ci	return result;
42062306a36Sopenharmony_ci}
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_cibool acpi_cpc_valid(void)
42362306a36Sopenharmony_ci{
42462306a36Sopenharmony_ci	struct cpc_desc *cpc_ptr;
42562306a36Sopenharmony_ci	int cpu;
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	if (acpi_disabled)
42862306a36Sopenharmony_ci		return false;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	for_each_present_cpu(cpu) {
43162306a36Sopenharmony_ci		cpc_ptr = per_cpu(cpc_desc_ptr, cpu);
43262306a36Sopenharmony_ci		if (!cpc_ptr)
43362306a36Sopenharmony_ci			return false;
43462306a36Sopenharmony_ci	}
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	return true;
43762306a36Sopenharmony_ci}
43862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(acpi_cpc_valid);
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_cibool cppc_allow_fast_switch(void)
44162306a36Sopenharmony_ci{
44262306a36Sopenharmony_ci	struct cpc_register_resource *desired_reg;
44362306a36Sopenharmony_ci	struct cpc_desc *cpc_ptr;
44462306a36Sopenharmony_ci	int cpu;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	for_each_possible_cpu(cpu) {
44762306a36Sopenharmony_ci		cpc_ptr = per_cpu(cpc_desc_ptr, cpu);
44862306a36Sopenharmony_ci		desired_reg = &cpc_ptr->cpc_regs[DESIRED_PERF];
44962306a36Sopenharmony_ci		if (!CPC_IN_SYSTEM_MEMORY(desired_reg) &&
45062306a36Sopenharmony_ci				!CPC_IN_SYSTEM_IO(desired_reg))
45162306a36Sopenharmony_ci			return false;
45262306a36Sopenharmony_ci	}
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	return true;
45562306a36Sopenharmony_ci}
45662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cppc_allow_fast_switch);
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci/**
45962306a36Sopenharmony_ci * acpi_get_psd_map - Map the CPUs in the freq domain of a given cpu
46062306a36Sopenharmony_ci * @cpu: Find all CPUs that share a domain with cpu.
46162306a36Sopenharmony_ci * @cpu_data: Pointer to CPU specific CPPC data including PSD info.
46262306a36Sopenharmony_ci *
46362306a36Sopenharmony_ci *	Return: 0 for success or negative value for err.
46462306a36Sopenharmony_ci */
46562306a36Sopenharmony_ciint acpi_get_psd_map(unsigned int cpu, struct cppc_cpudata *cpu_data)
46662306a36Sopenharmony_ci{
46762306a36Sopenharmony_ci	struct cpc_desc *cpc_ptr, *match_cpc_ptr;
46862306a36Sopenharmony_ci	struct acpi_psd_package *match_pdomain;
46962306a36Sopenharmony_ci	struct acpi_psd_package *pdomain;
47062306a36Sopenharmony_ci	int count_target, i;
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	/*
47362306a36Sopenharmony_ci	 * Now that we have _PSD data from all CPUs, let's setup P-state
47462306a36Sopenharmony_ci	 * domain info.
47562306a36Sopenharmony_ci	 */
47662306a36Sopenharmony_ci	cpc_ptr = per_cpu(cpc_desc_ptr, cpu);
47762306a36Sopenharmony_ci	if (!cpc_ptr)
47862306a36Sopenharmony_ci		return -EFAULT;
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	pdomain = &(cpc_ptr->domain_info);
48162306a36Sopenharmony_ci	cpumask_set_cpu(cpu, cpu_data->shared_cpu_map);
48262306a36Sopenharmony_ci	if (pdomain->num_processors <= 1)
48362306a36Sopenharmony_ci		return 0;
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	/* Validate the Domain info */
48662306a36Sopenharmony_ci	count_target = pdomain->num_processors;
48762306a36Sopenharmony_ci	if (pdomain->coord_type == DOMAIN_COORD_TYPE_SW_ALL)
48862306a36Sopenharmony_ci		cpu_data->shared_type = CPUFREQ_SHARED_TYPE_ALL;
48962306a36Sopenharmony_ci	else if (pdomain->coord_type == DOMAIN_COORD_TYPE_HW_ALL)
49062306a36Sopenharmony_ci		cpu_data->shared_type = CPUFREQ_SHARED_TYPE_HW;
49162306a36Sopenharmony_ci	else if (pdomain->coord_type == DOMAIN_COORD_TYPE_SW_ANY)
49262306a36Sopenharmony_ci		cpu_data->shared_type = CPUFREQ_SHARED_TYPE_ANY;
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	for_each_possible_cpu(i) {
49562306a36Sopenharmony_ci		if (i == cpu)
49662306a36Sopenharmony_ci			continue;
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci		match_cpc_ptr = per_cpu(cpc_desc_ptr, i);
49962306a36Sopenharmony_ci		if (!match_cpc_ptr)
50062306a36Sopenharmony_ci			goto err_fault;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci		match_pdomain = &(match_cpc_ptr->domain_info);
50362306a36Sopenharmony_ci		if (match_pdomain->domain != pdomain->domain)
50462306a36Sopenharmony_ci			continue;
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci		/* Here i and cpu are in the same domain */
50762306a36Sopenharmony_ci		if (match_pdomain->num_processors != count_target)
50862306a36Sopenharmony_ci			goto err_fault;
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci		if (pdomain->coord_type != match_pdomain->coord_type)
51162306a36Sopenharmony_ci			goto err_fault;
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci		cpumask_set_cpu(i, cpu_data->shared_cpu_map);
51462306a36Sopenharmony_ci	}
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	return 0;
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_cierr_fault:
51962306a36Sopenharmony_ci	/* Assume no coordination on any error parsing domain info */
52062306a36Sopenharmony_ci	cpumask_clear(cpu_data->shared_cpu_map);
52162306a36Sopenharmony_ci	cpumask_set_cpu(cpu, cpu_data->shared_cpu_map);
52262306a36Sopenharmony_ci	cpu_data->shared_type = CPUFREQ_SHARED_TYPE_NONE;
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	return -EFAULT;
52562306a36Sopenharmony_ci}
52662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(acpi_get_psd_map);
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_cistatic int register_pcc_channel(int pcc_ss_idx)
52962306a36Sopenharmony_ci{
53062306a36Sopenharmony_ci	struct pcc_mbox_chan *pcc_chan;
53162306a36Sopenharmony_ci	u64 usecs_lat;
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	if (pcc_ss_idx >= 0) {
53462306a36Sopenharmony_ci		pcc_chan = pcc_mbox_request_channel(&cppc_mbox_cl, pcc_ss_idx);
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci		if (IS_ERR(pcc_chan)) {
53762306a36Sopenharmony_ci			pr_err("Failed to find PCC channel for subspace %d\n",
53862306a36Sopenharmony_ci			       pcc_ss_idx);
53962306a36Sopenharmony_ci			return -ENODEV;
54062306a36Sopenharmony_ci		}
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci		pcc_data[pcc_ss_idx]->pcc_channel = pcc_chan;
54362306a36Sopenharmony_ci		/*
54462306a36Sopenharmony_ci		 * cppc_ss->latency is just a Nominal value. In reality
54562306a36Sopenharmony_ci		 * the remote processor could be much slower to reply.
54662306a36Sopenharmony_ci		 * So add an arbitrary amount of wait on top of Nominal.
54762306a36Sopenharmony_ci		 */
54862306a36Sopenharmony_ci		usecs_lat = NUM_RETRIES * pcc_chan->latency;
54962306a36Sopenharmony_ci		pcc_data[pcc_ss_idx]->deadline_us = usecs_lat;
55062306a36Sopenharmony_ci		pcc_data[pcc_ss_idx]->pcc_mrtt = pcc_chan->min_turnaround_time;
55162306a36Sopenharmony_ci		pcc_data[pcc_ss_idx]->pcc_mpar = pcc_chan->max_access_rate;
55262306a36Sopenharmony_ci		pcc_data[pcc_ss_idx]->pcc_nominal = pcc_chan->latency;
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci		pcc_data[pcc_ss_idx]->pcc_comm_addr =
55562306a36Sopenharmony_ci			acpi_os_ioremap(pcc_chan->shmem_base_addr,
55662306a36Sopenharmony_ci					pcc_chan->shmem_size);
55762306a36Sopenharmony_ci		if (!pcc_data[pcc_ss_idx]->pcc_comm_addr) {
55862306a36Sopenharmony_ci			pr_err("Failed to ioremap PCC comm region mem for %d\n",
55962306a36Sopenharmony_ci			       pcc_ss_idx);
56062306a36Sopenharmony_ci			return -ENOMEM;
56162306a36Sopenharmony_ci		}
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci		/* Set flag so that we don't come here for each CPU. */
56462306a36Sopenharmony_ci		pcc_data[pcc_ss_idx]->pcc_channel_acquired = true;
56562306a36Sopenharmony_ci	}
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	return 0;
56862306a36Sopenharmony_ci}
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci/**
57162306a36Sopenharmony_ci * cpc_ffh_supported() - check if FFH reading supported
57262306a36Sopenharmony_ci *
57362306a36Sopenharmony_ci * Check if the architecture has support for functional fixed hardware
57462306a36Sopenharmony_ci * read/write capability.
57562306a36Sopenharmony_ci *
57662306a36Sopenharmony_ci * Return: true for supported, false for not supported
57762306a36Sopenharmony_ci */
57862306a36Sopenharmony_cibool __weak cpc_ffh_supported(void)
57962306a36Sopenharmony_ci{
58062306a36Sopenharmony_ci	return false;
58162306a36Sopenharmony_ci}
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci/**
58462306a36Sopenharmony_ci * cpc_supported_by_cpu() - check if CPPC is supported by CPU
58562306a36Sopenharmony_ci *
58662306a36Sopenharmony_ci * Check if the architectural support for CPPC is present even
58762306a36Sopenharmony_ci * if the _OSC hasn't prescribed it
58862306a36Sopenharmony_ci *
58962306a36Sopenharmony_ci * Return: true for supported, false for not supported
59062306a36Sopenharmony_ci */
59162306a36Sopenharmony_cibool __weak cpc_supported_by_cpu(void)
59262306a36Sopenharmony_ci{
59362306a36Sopenharmony_ci	return false;
59462306a36Sopenharmony_ci}
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci/**
59762306a36Sopenharmony_ci * pcc_data_alloc() - Allocate the pcc_data memory for pcc subspace
59862306a36Sopenharmony_ci * @pcc_ss_id: PCC Subspace index as in the PCC client ACPI package.
59962306a36Sopenharmony_ci *
60062306a36Sopenharmony_ci * Check and allocate the cppc_pcc_data memory.
60162306a36Sopenharmony_ci * In some processor configurations it is possible that same subspace
60262306a36Sopenharmony_ci * is shared between multiple CPUs. This is seen especially in CPUs
60362306a36Sopenharmony_ci * with hardware multi-threading support.
60462306a36Sopenharmony_ci *
60562306a36Sopenharmony_ci * Return: 0 for success, errno for failure
60662306a36Sopenharmony_ci */
60762306a36Sopenharmony_cistatic int pcc_data_alloc(int pcc_ss_id)
60862306a36Sopenharmony_ci{
60962306a36Sopenharmony_ci	if (pcc_ss_id < 0 || pcc_ss_id >= MAX_PCC_SUBSPACES)
61062306a36Sopenharmony_ci		return -EINVAL;
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	if (pcc_data[pcc_ss_id]) {
61362306a36Sopenharmony_ci		pcc_data[pcc_ss_id]->refcount++;
61462306a36Sopenharmony_ci	} else {
61562306a36Sopenharmony_ci		pcc_data[pcc_ss_id] = kzalloc(sizeof(struct cppc_pcc_data),
61662306a36Sopenharmony_ci					      GFP_KERNEL);
61762306a36Sopenharmony_ci		if (!pcc_data[pcc_ss_id])
61862306a36Sopenharmony_ci			return -ENOMEM;
61962306a36Sopenharmony_ci		pcc_data[pcc_ss_id]->refcount++;
62062306a36Sopenharmony_ci	}
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	return 0;
62362306a36Sopenharmony_ci}
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci/*
62662306a36Sopenharmony_ci * An example CPC table looks like the following.
62762306a36Sopenharmony_ci *
62862306a36Sopenharmony_ci *  Name (_CPC, Package() {
62962306a36Sopenharmony_ci *      17,							// NumEntries
63062306a36Sopenharmony_ci *      1,							// Revision
63162306a36Sopenharmony_ci *      ResourceTemplate() {Register(PCC, 32, 0, 0x120, 2)},	// Highest Performance
63262306a36Sopenharmony_ci *      ResourceTemplate() {Register(PCC, 32, 0, 0x124, 2)},	// Nominal Performance
63362306a36Sopenharmony_ci *      ResourceTemplate() {Register(PCC, 32, 0, 0x128, 2)},	// Lowest Nonlinear Performance
63462306a36Sopenharmony_ci *      ResourceTemplate() {Register(PCC, 32, 0, 0x12C, 2)},	// Lowest Performance
63562306a36Sopenharmony_ci *      ResourceTemplate() {Register(PCC, 32, 0, 0x130, 2)},	// Guaranteed Performance Register
63662306a36Sopenharmony_ci *      ResourceTemplate() {Register(PCC, 32, 0, 0x110, 2)},	// Desired Performance Register
63762306a36Sopenharmony_ci *      ResourceTemplate() {Register(SystemMemory, 0, 0, 0, 0)},
63862306a36Sopenharmony_ci *      ...
63962306a36Sopenharmony_ci *      ...
64062306a36Sopenharmony_ci *      ...
64162306a36Sopenharmony_ci *  }
64262306a36Sopenharmony_ci * Each Register() encodes how to access that specific register.
64362306a36Sopenharmony_ci * e.g. a sample PCC entry has the following encoding:
64462306a36Sopenharmony_ci *
64562306a36Sopenharmony_ci *  Register (
64662306a36Sopenharmony_ci *      PCC,	// AddressSpaceKeyword
64762306a36Sopenharmony_ci *      8,	// RegisterBitWidth
64862306a36Sopenharmony_ci *      8,	// RegisterBitOffset
64962306a36Sopenharmony_ci *      0x30,	// RegisterAddress
65062306a36Sopenharmony_ci *      9,	// AccessSize (subspace ID)
65162306a36Sopenharmony_ci *  )
65262306a36Sopenharmony_ci */
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci#ifndef arch_init_invariance_cppc
65562306a36Sopenharmony_cistatic inline void arch_init_invariance_cppc(void) { }
65662306a36Sopenharmony_ci#endif
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci/**
65962306a36Sopenharmony_ci * acpi_cppc_processor_probe - Search for per CPU _CPC objects.
66062306a36Sopenharmony_ci * @pr: Ptr to acpi_processor containing this CPU's logical ID.
66162306a36Sopenharmony_ci *
66262306a36Sopenharmony_ci *	Return: 0 for success or negative value for err.
66362306a36Sopenharmony_ci */
66462306a36Sopenharmony_ciint acpi_cppc_processor_probe(struct acpi_processor *pr)
66562306a36Sopenharmony_ci{
66662306a36Sopenharmony_ci	struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL};
66762306a36Sopenharmony_ci	union acpi_object *out_obj, *cpc_obj;
66862306a36Sopenharmony_ci	struct cpc_desc *cpc_ptr;
66962306a36Sopenharmony_ci	struct cpc_reg *gas_t;
67062306a36Sopenharmony_ci	struct device *cpu_dev;
67162306a36Sopenharmony_ci	acpi_handle handle = pr->handle;
67262306a36Sopenharmony_ci	unsigned int num_ent, i, cpc_rev;
67362306a36Sopenharmony_ci	int pcc_subspace_id = -1;
67462306a36Sopenharmony_ci	acpi_status status;
67562306a36Sopenharmony_ci	int ret = -ENODATA;
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	if (!osc_sb_cppc2_support_acked) {
67862306a36Sopenharmony_ci		pr_debug("CPPC v2 _OSC not acked\n");
67962306a36Sopenharmony_ci		if (!cpc_supported_by_cpu())
68062306a36Sopenharmony_ci			return -ENODEV;
68162306a36Sopenharmony_ci	}
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci	/* Parse the ACPI _CPC table for this CPU. */
68462306a36Sopenharmony_ci	status = acpi_evaluate_object_typed(handle, "_CPC", NULL, &output,
68562306a36Sopenharmony_ci			ACPI_TYPE_PACKAGE);
68662306a36Sopenharmony_ci	if (ACPI_FAILURE(status)) {
68762306a36Sopenharmony_ci		ret = -ENODEV;
68862306a36Sopenharmony_ci		goto out_buf_free;
68962306a36Sopenharmony_ci	}
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	out_obj = (union acpi_object *) output.pointer;
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	cpc_ptr = kzalloc(sizeof(struct cpc_desc), GFP_KERNEL);
69462306a36Sopenharmony_ci	if (!cpc_ptr) {
69562306a36Sopenharmony_ci		ret = -ENOMEM;
69662306a36Sopenharmony_ci		goto out_buf_free;
69762306a36Sopenharmony_ci	}
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	/* First entry is NumEntries. */
70062306a36Sopenharmony_ci	cpc_obj = &out_obj->package.elements[0];
70162306a36Sopenharmony_ci	if (cpc_obj->type == ACPI_TYPE_INTEGER)	{
70262306a36Sopenharmony_ci		num_ent = cpc_obj->integer.value;
70362306a36Sopenharmony_ci		if (num_ent <= 1) {
70462306a36Sopenharmony_ci			pr_debug("Unexpected _CPC NumEntries value (%d) for CPU:%d\n",
70562306a36Sopenharmony_ci				 num_ent, pr->id);
70662306a36Sopenharmony_ci			goto out_free;
70762306a36Sopenharmony_ci		}
70862306a36Sopenharmony_ci	} else {
70962306a36Sopenharmony_ci		pr_debug("Unexpected _CPC NumEntries entry type (%d) for CPU:%d\n",
71062306a36Sopenharmony_ci			 cpc_obj->type, pr->id);
71162306a36Sopenharmony_ci		goto out_free;
71262306a36Sopenharmony_ci	}
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	/* Second entry should be revision. */
71562306a36Sopenharmony_ci	cpc_obj = &out_obj->package.elements[1];
71662306a36Sopenharmony_ci	if (cpc_obj->type == ACPI_TYPE_INTEGER)	{
71762306a36Sopenharmony_ci		cpc_rev = cpc_obj->integer.value;
71862306a36Sopenharmony_ci	} else {
71962306a36Sopenharmony_ci		pr_debug("Unexpected _CPC Revision entry type (%d) for CPU:%d\n",
72062306a36Sopenharmony_ci			 cpc_obj->type, pr->id);
72162306a36Sopenharmony_ci		goto out_free;
72262306a36Sopenharmony_ci	}
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	if (cpc_rev < CPPC_V2_REV) {
72562306a36Sopenharmony_ci		pr_debug("Unsupported _CPC Revision (%d) for CPU:%d\n", cpc_rev,
72662306a36Sopenharmony_ci			 pr->id);
72762306a36Sopenharmony_ci		goto out_free;
72862306a36Sopenharmony_ci	}
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	/*
73162306a36Sopenharmony_ci	 * Disregard _CPC if the number of entries in the return pachage is not
73262306a36Sopenharmony_ci	 * as expected, but support future revisions being proper supersets of
73362306a36Sopenharmony_ci	 * the v3 and only causing more entries to be returned by _CPC.
73462306a36Sopenharmony_ci	 */
73562306a36Sopenharmony_ci	if ((cpc_rev == CPPC_V2_REV && num_ent != CPPC_V2_NUM_ENT) ||
73662306a36Sopenharmony_ci	    (cpc_rev == CPPC_V3_REV && num_ent != CPPC_V3_NUM_ENT) ||
73762306a36Sopenharmony_ci	    (cpc_rev > CPPC_V3_REV && num_ent <= CPPC_V3_NUM_ENT)) {
73862306a36Sopenharmony_ci		pr_debug("Unexpected number of _CPC return package entries (%d) for CPU:%d\n",
73962306a36Sopenharmony_ci			 num_ent, pr->id);
74062306a36Sopenharmony_ci		goto out_free;
74162306a36Sopenharmony_ci	}
74262306a36Sopenharmony_ci	if (cpc_rev > CPPC_V3_REV) {
74362306a36Sopenharmony_ci		num_ent = CPPC_V3_NUM_ENT;
74462306a36Sopenharmony_ci		cpc_rev = CPPC_V3_REV;
74562306a36Sopenharmony_ci	}
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	cpc_ptr->num_entries = num_ent;
74862306a36Sopenharmony_ci	cpc_ptr->version = cpc_rev;
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci	/* Iterate through remaining entries in _CPC */
75162306a36Sopenharmony_ci	for (i = 2; i < num_ent; i++) {
75262306a36Sopenharmony_ci		cpc_obj = &out_obj->package.elements[i];
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci		if (cpc_obj->type == ACPI_TYPE_INTEGER)	{
75562306a36Sopenharmony_ci			cpc_ptr->cpc_regs[i-2].type = ACPI_TYPE_INTEGER;
75662306a36Sopenharmony_ci			cpc_ptr->cpc_regs[i-2].cpc_entry.int_value = cpc_obj->integer.value;
75762306a36Sopenharmony_ci		} else if (cpc_obj->type == ACPI_TYPE_BUFFER) {
75862306a36Sopenharmony_ci			gas_t = (struct cpc_reg *)
75962306a36Sopenharmony_ci				cpc_obj->buffer.pointer;
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci			/*
76262306a36Sopenharmony_ci			 * The PCC Subspace index is encoded inside
76362306a36Sopenharmony_ci			 * the CPC table entries. The same PCC index
76462306a36Sopenharmony_ci			 * will be used for all the PCC entries,
76562306a36Sopenharmony_ci			 * so extract it only once.
76662306a36Sopenharmony_ci			 */
76762306a36Sopenharmony_ci			if (gas_t->space_id == ACPI_ADR_SPACE_PLATFORM_COMM) {
76862306a36Sopenharmony_ci				if (pcc_subspace_id < 0) {
76962306a36Sopenharmony_ci					pcc_subspace_id = gas_t->access_width;
77062306a36Sopenharmony_ci					if (pcc_data_alloc(pcc_subspace_id))
77162306a36Sopenharmony_ci						goto out_free;
77262306a36Sopenharmony_ci				} else if (pcc_subspace_id != gas_t->access_width) {
77362306a36Sopenharmony_ci					pr_debug("Mismatched PCC ids in _CPC for CPU:%d\n",
77462306a36Sopenharmony_ci						 pr->id);
77562306a36Sopenharmony_ci					goto out_free;
77662306a36Sopenharmony_ci				}
77762306a36Sopenharmony_ci			} else if (gas_t->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
77862306a36Sopenharmony_ci				if (gas_t->address) {
77962306a36Sopenharmony_ci					void __iomem *addr;
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci					if (!osc_cpc_flexible_adr_space_confirmed) {
78262306a36Sopenharmony_ci						pr_debug("Flexible address space capability not supported\n");
78362306a36Sopenharmony_ci						if (!cpc_supported_by_cpu())
78462306a36Sopenharmony_ci							goto out_free;
78562306a36Sopenharmony_ci					}
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci					addr = ioremap(gas_t->address, gas_t->bit_width/8);
78862306a36Sopenharmony_ci					if (!addr)
78962306a36Sopenharmony_ci						goto out_free;
79062306a36Sopenharmony_ci					cpc_ptr->cpc_regs[i-2].sys_mem_vaddr = addr;
79162306a36Sopenharmony_ci				}
79262306a36Sopenharmony_ci			} else if (gas_t->space_id == ACPI_ADR_SPACE_SYSTEM_IO) {
79362306a36Sopenharmony_ci				if (gas_t->access_width < 1 || gas_t->access_width > 3) {
79462306a36Sopenharmony_ci					/*
79562306a36Sopenharmony_ci					 * 1 = 8-bit, 2 = 16-bit, and 3 = 32-bit.
79662306a36Sopenharmony_ci					 * SystemIO doesn't implement 64-bit
79762306a36Sopenharmony_ci					 * registers.
79862306a36Sopenharmony_ci					 */
79962306a36Sopenharmony_ci					pr_debug("Invalid access width %d for SystemIO register in _CPC\n",
80062306a36Sopenharmony_ci						 gas_t->access_width);
80162306a36Sopenharmony_ci					goto out_free;
80262306a36Sopenharmony_ci				}
80362306a36Sopenharmony_ci				if (gas_t->address & OVER_16BTS_MASK) {
80462306a36Sopenharmony_ci					/* SystemIO registers use 16-bit integer addresses */
80562306a36Sopenharmony_ci					pr_debug("Invalid IO port %llu for SystemIO register in _CPC\n",
80662306a36Sopenharmony_ci						 gas_t->address);
80762306a36Sopenharmony_ci					goto out_free;
80862306a36Sopenharmony_ci				}
80962306a36Sopenharmony_ci				if (!osc_cpc_flexible_adr_space_confirmed) {
81062306a36Sopenharmony_ci					pr_debug("Flexible address space capability not supported\n");
81162306a36Sopenharmony_ci					if (!cpc_supported_by_cpu())
81262306a36Sopenharmony_ci						goto out_free;
81362306a36Sopenharmony_ci				}
81462306a36Sopenharmony_ci			} else {
81562306a36Sopenharmony_ci				if (gas_t->space_id != ACPI_ADR_SPACE_FIXED_HARDWARE || !cpc_ffh_supported()) {
81662306a36Sopenharmony_ci					/* Support only PCC, SystemMemory, SystemIO, and FFH type regs. */
81762306a36Sopenharmony_ci					pr_debug("Unsupported register type (%d) in _CPC\n",
81862306a36Sopenharmony_ci						 gas_t->space_id);
81962306a36Sopenharmony_ci					goto out_free;
82062306a36Sopenharmony_ci				}
82162306a36Sopenharmony_ci			}
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci			cpc_ptr->cpc_regs[i-2].type = ACPI_TYPE_BUFFER;
82462306a36Sopenharmony_ci			memcpy(&cpc_ptr->cpc_regs[i-2].cpc_entry.reg, gas_t, sizeof(*gas_t));
82562306a36Sopenharmony_ci		} else {
82662306a36Sopenharmony_ci			pr_debug("Invalid entry type (%d) in _CPC for CPU:%d\n",
82762306a36Sopenharmony_ci				 i, pr->id);
82862306a36Sopenharmony_ci			goto out_free;
82962306a36Sopenharmony_ci		}
83062306a36Sopenharmony_ci	}
83162306a36Sopenharmony_ci	per_cpu(cpu_pcc_subspace_idx, pr->id) = pcc_subspace_id;
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	/*
83462306a36Sopenharmony_ci	 * Initialize the remaining cpc_regs as unsupported.
83562306a36Sopenharmony_ci	 * Example: In case FW exposes CPPC v2, the below loop will initialize
83662306a36Sopenharmony_ci	 * LOWEST_FREQ and NOMINAL_FREQ regs as unsupported
83762306a36Sopenharmony_ci	 */
83862306a36Sopenharmony_ci	for (i = num_ent - 2; i < MAX_CPC_REG_ENT; i++) {
83962306a36Sopenharmony_ci		cpc_ptr->cpc_regs[i].type = ACPI_TYPE_INTEGER;
84062306a36Sopenharmony_ci		cpc_ptr->cpc_regs[i].cpc_entry.int_value = 0;
84162306a36Sopenharmony_ci	}
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	/* Store CPU Logical ID */
84562306a36Sopenharmony_ci	cpc_ptr->cpu_id = pr->id;
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci	/* Parse PSD data for this CPU */
84862306a36Sopenharmony_ci	ret = acpi_get_psd(cpc_ptr, handle);
84962306a36Sopenharmony_ci	if (ret)
85062306a36Sopenharmony_ci		goto out_free;
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci	/* Register PCC channel once for all PCC subspace ID. */
85362306a36Sopenharmony_ci	if (pcc_subspace_id >= 0 && !pcc_data[pcc_subspace_id]->pcc_channel_acquired) {
85462306a36Sopenharmony_ci		ret = register_pcc_channel(pcc_subspace_id);
85562306a36Sopenharmony_ci		if (ret)
85662306a36Sopenharmony_ci			goto out_free;
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci		init_rwsem(&pcc_data[pcc_subspace_id]->pcc_lock);
85962306a36Sopenharmony_ci		init_waitqueue_head(&pcc_data[pcc_subspace_id]->pcc_write_wait_q);
86062306a36Sopenharmony_ci	}
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci	/* Everything looks okay */
86362306a36Sopenharmony_ci	pr_debug("Parsed CPC struct for CPU: %d\n", pr->id);
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci	/* Add per logical CPU nodes for reading its feedback counters. */
86662306a36Sopenharmony_ci	cpu_dev = get_cpu_device(pr->id);
86762306a36Sopenharmony_ci	if (!cpu_dev) {
86862306a36Sopenharmony_ci		ret = -EINVAL;
86962306a36Sopenharmony_ci		goto out_free;
87062306a36Sopenharmony_ci	}
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	/* Plug PSD data into this CPU's CPC descriptor. */
87362306a36Sopenharmony_ci	per_cpu(cpc_desc_ptr, pr->id) = cpc_ptr;
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	ret = kobject_init_and_add(&cpc_ptr->kobj, &cppc_ktype, &cpu_dev->kobj,
87662306a36Sopenharmony_ci			"acpi_cppc");
87762306a36Sopenharmony_ci	if (ret) {
87862306a36Sopenharmony_ci		per_cpu(cpc_desc_ptr, pr->id) = NULL;
87962306a36Sopenharmony_ci		kobject_put(&cpc_ptr->kobj);
88062306a36Sopenharmony_ci		goto out_free;
88162306a36Sopenharmony_ci	}
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci	arch_init_invariance_cppc();
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci	kfree(output.pointer);
88662306a36Sopenharmony_ci	return 0;
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ciout_free:
88962306a36Sopenharmony_ci	/* Free all the mapped sys mem areas for this CPU */
89062306a36Sopenharmony_ci	for (i = 2; i < cpc_ptr->num_entries; i++) {
89162306a36Sopenharmony_ci		void __iomem *addr = cpc_ptr->cpc_regs[i-2].sys_mem_vaddr;
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci		if (addr)
89462306a36Sopenharmony_ci			iounmap(addr);
89562306a36Sopenharmony_ci	}
89662306a36Sopenharmony_ci	kfree(cpc_ptr);
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ciout_buf_free:
89962306a36Sopenharmony_ci	kfree(output.pointer);
90062306a36Sopenharmony_ci	return ret;
90162306a36Sopenharmony_ci}
90262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(acpi_cppc_processor_probe);
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci/**
90562306a36Sopenharmony_ci * acpi_cppc_processor_exit - Cleanup CPC structs.
90662306a36Sopenharmony_ci * @pr: Ptr to acpi_processor containing this CPU's logical ID.
90762306a36Sopenharmony_ci *
90862306a36Sopenharmony_ci * Return: Void
90962306a36Sopenharmony_ci */
91062306a36Sopenharmony_civoid acpi_cppc_processor_exit(struct acpi_processor *pr)
91162306a36Sopenharmony_ci{
91262306a36Sopenharmony_ci	struct cpc_desc *cpc_ptr;
91362306a36Sopenharmony_ci	unsigned int i;
91462306a36Sopenharmony_ci	void __iomem *addr;
91562306a36Sopenharmony_ci	int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, pr->id);
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci	if (pcc_ss_id >= 0 && pcc_data[pcc_ss_id]) {
91862306a36Sopenharmony_ci		if (pcc_data[pcc_ss_id]->pcc_channel_acquired) {
91962306a36Sopenharmony_ci			pcc_data[pcc_ss_id]->refcount--;
92062306a36Sopenharmony_ci			if (!pcc_data[pcc_ss_id]->refcount) {
92162306a36Sopenharmony_ci				pcc_mbox_free_channel(pcc_data[pcc_ss_id]->pcc_channel);
92262306a36Sopenharmony_ci				kfree(pcc_data[pcc_ss_id]);
92362306a36Sopenharmony_ci				pcc_data[pcc_ss_id] = NULL;
92462306a36Sopenharmony_ci			}
92562306a36Sopenharmony_ci		}
92662306a36Sopenharmony_ci	}
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci	cpc_ptr = per_cpu(cpc_desc_ptr, pr->id);
92962306a36Sopenharmony_ci	if (!cpc_ptr)
93062306a36Sopenharmony_ci		return;
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci	/* Free all the mapped sys mem areas for this CPU */
93362306a36Sopenharmony_ci	for (i = 2; i < cpc_ptr->num_entries; i++) {
93462306a36Sopenharmony_ci		addr = cpc_ptr->cpc_regs[i-2].sys_mem_vaddr;
93562306a36Sopenharmony_ci		if (addr)
93662306a36Sopenharmony_ci			iounmap(addr);
93762306a36Sopenharmony_ci	}
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci	kobject_put(&cpc_ptr->kobj);
94062306a36Sopenharmony_ci	kfree(cpc_ptr);
94162306a36Sopenharmony_ci}
94262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(acpi_cppc_processor_exit);
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_ci/**
94562306a36Sopenharmony_ci * cpc_read_ffh() - Read FFH register
94662306a36Sopenharmony_ci * @cpunum:	CPU number to read
94762306a36Sopenharmony_ci * @reg:	cppc register information
94862306a36Sopenharmony_ci * @val:	place holder for return value
94962306a36Sopenharmony_ci *
95062306a36Sopenharmony_ci * Read bit_width bits from a specified address and bit_offset
95162306a36Sopenharmony_ci *
95262306a36Sopenharmony_ci * Return: 0 for success and error code
95362306a36Sopenharmony_ci */
95462306a36Sopenharmony_ciint __weak cpc_read_ffh(int cpunum, struct cpc_reg *reg, u64 *val)
95562306a36Sopenharmony_ci{
95662306a36Sopenharmony_ci	return -ENOTSUPP;
95762306a36Sopenharmony_ci}
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci/**
96062306a36Sopenharmony_ci * cpc_write_ffh() - Write FFH register
96162306a36Sopenharmony_ci * @cpunum:	CPU number to write
96262306a36Sopenharmony_ci * @reg:	cppc register information
96362306a36Sopenharmony_ci * @val:	value to write
96462306a36Sopenharmony_ci *
96562306a36Sopenharmony_ci * Write value of bit_width bits to a specified address and bit_offset
96662306a36Sopenharmony_ci *
96762306a36Sopenharmony_ci * Return: 0 for success and error code
96862306a36Sopenharmony_ci */
96962306a36Sopenharmony_ciint __weak cpc_write_ffh(int cpunum, struct cpc_reg *reg, u64 val)
97062306a36Sopenharmony_ci{
97162306a36Sopenharmony_ci	return -ENOTSUPP;
97262306a36Sopenharmony_ci}
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci/*
97562306a36Sopenharmony_ci * Since cpc_read and cpc_write are called while holding pcc_lock, it should be
97662306a36Sopenharmony_ci * as fast as possible. We have already mapped the PCC subspace during init, so
97762306a36Sopenharmony_ci * we can directly write to it.
97862306a36Sopenharmony_ci */
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_cistatic int cpc_read(int cpu, struct cpc_register_resource *reg_res, u64 *val)
98162306a36Sopenharmony_ci{
98262306a36Sopenharmony_ci	void __iomem *vaddr = NULL;
98362306a36Sopenharmony_ci	int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpu);
98462306a36Sopenharmony_ci	struct cpc_reg *reg = &reg_res->cpc_entry.reg;
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_ci	if (reg_res->type == ACPI_TYPE_INTEGER) {
98762306a36Sopenharmony_ci		*val = reg_res->cpc_entry.int_value;
98862306a36Sopenharmony_ci		return 0;
98962306a36Sopenharmony_ci	}
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci	*val = 0;
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci	if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_IO) {
99462306a36Sopenharmony_ci		u32 width = 8 << (reg->access_width - 1);
99562306a36Sopenharmony_ci		u32 val_u32;
99662306a36Sopenharmony_ci		acpi_status status;
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci		status = acpi_os_read_port((acpi_io_address)reg->address,
99962306a36Sopenharmony_ci					   &val_u32, width);
100062306a36Sopenharmony_ci		if (ACPI_FAILURE(status)) {
100162306a36Sopenharmony_ci			pr_debug("Error: Failed to read SystemIO port %llx\n",
100262306a36Sopenharmony_ci				 reg->address);
100362306a36Sopenharmony_ci			return -EFAULT;
100462306a36Sopenharmony_ci		}
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci		*val = val_u32;
100762306a36Sopenharmony_ci		return 0;
100862306a36Sopenharmony_ci	} else if (reg->space_id == ACPI_ADR_SPACE_PLATFORM_COMM && pcc_ss_id >= 0)
100962306a36Sopenharmony_ci		vaddr = GET_PCC_VADDR(reg->address, pcc_ss_id);
101062306a36Sopenharmony_ci	else if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY)
101162306a36Sopenharmony_ci		vaddr = reg_res->sys_mem_vaddr;
101262306a36Sopenharmony_ci	else if (reg->space_id == ACPI_ADR_SPACE_FIXED_HARDWARE)
101362306a36Sopenharmony_ci		return cpc_read_ffh(cpu, reg, val);
101462306a36Sopenharmony_ci	else
101562306a36Sopenharmony_ci		return acpi_os_read_memory((acpi_physical_address)reg->address,
101662306a36Sopenharmony_ci				val, reg->bit_width);
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci	switch (reg->bit_width) {
101962306a36Sopenharmony_ci	case 8:
102062306a36Sopenharmony_ci		*val = readb_relaxed(vaddr);
102162306a36Sopenharmony_ci		break;
102262306a36Sopenharmony_ci	case 16:
102362306a36Sopenharmony_ci		*val = readw_relaxed(vaddr);
102462306a36Sopenharmony_ci		break;
102562306a36Sopenharmony_ci	case 32:
102662306a36Sopenharmony_ci		*val = readl_relaxed(vaddr);
102762306a36Sopenharmony_ci		break;
102862306a36Sopenharmony_ci	case 64:
102962306a36Sopenharmony_ci		*val = readq_relaxed(vaddr);
103062306a36Sopenharmony_ci		break;
103162306a36Sopenharmony_ci	default:
103262306a36Sopenharmony_ci		pr_debug("Error: Cannot read %u bit width from PCC for ss: %d\n",
103362306a36Sopenharmony_ci			 reg->bit_width, pcc_ss_id);
103462306a36Sopenharmony_ci		return -EFAULT;
103562306a36Sopenharmony_ci	}
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_ci	return 0;
103862306a36Sopenharmony_ci}
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_cistatic int cpc_write(int cpu, struct cpc_register_resource *reg_res, u64 val)
104162306a36Sopenharmony_ci{
104262306a36Sopenharmony_ci	int ret_val = 0;
104362306a36Sopenharmony_ci	void __iomem *vaddr = NULL;
104462306a36Sopenharmony_ci	int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpu);
104562306a36Sopenharmony_ci	struct cpc_reg *reg = &reg_res->cpc_entry.reg;
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_ci	if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_IO) {
104862306a36Sopenharmony_ci		u32 width = 8 << (reg->access_width - 1);
104962306a36Sopenharmony_ci		acpi_status status;
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_ci		status = acpi_os_write_port((acpi_io_address)reg->address,
105262306a36Sopenharmony_ci					    (u32)val, width);
105362306a36Sopenharmony_ci		if (ACPI_FAILURE(status)) {
105462306a36Sopenharmony_ci			pr_debug("Error: Failed to write SystemIO port %llx\n",
105562306a36Sopenharmony_ci				 reg->address);
105662306a36Sopenharmony_ci			return -EFAULT;
105762306a36Sopenharmony_ci		}
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci		return 0;
106062306a36Sopenharmony_ci	} else if (reg->space_id == ACPI_ADR_SPACE_PLATFORM_COMM && pcc_ss_id >= 0)
106162306a36Sopenharmony_ci		vaddr = GET_PCC_VADDR(reg->address, pcc_ss_id);
106262306a36Sopenharmony_ci	else if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY)
106362306a36Sopenharmony_ci		vaddr = reg_res->sys_mem_vaddr;
106462306a36Sopenharmony_ci	else if (reg->space_id == ACPI_ADR_SPACE_FIXED_HARDWARE)
106562306a36Sopenharmony_ci		return cpc_write_ffh(cpu, reg, val);
106662306a36Sopenharmony_ci	else
106762306a36Sopenharmony_ci		return acpi_os_write_memory((acpi_physical_address)reg->address,
106862306a36Sopenharmony_ci				val, reg->bit_width);
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci	switch (reg->bit_width) {
107162306a36Sopenharmony_ci	case 8:
107262306a36Sopenharmony_ci		writeb_relaxed(val, vaddr);
107362306a36Sopenharmony_ci		break;
107462306a36Sopenharmony_ci	case 16:
107562306a36Sopenharmony_ci		writew_relaxed(val, vaddr);
107662306a36Sopenharmony_ci		break;
107762306a36Sopenharmony_ci	case 32:
107862306a36Sopenharmony_ci		writel_relaxed(val, vaddr);
107962306a36Sopenharmony_ci		break;
108062306a36Sopenharmony_ci	case 64:
108162306a36Sopenharmony_ci		writeq_relaxed(val, vaddr);
108262306a36Sopenharmony_ci		break;
108362306a36Sopenharmony_ci	default:
108462306a36Sopenharmony_ci		pr_debug("Error: Cannot write %u bit width to PCC for ss: %d\n",
108562306a36Sopenharmony_ci			 reg->bit_width, pcc_ss_id);
108662306a36Sopenharmony_ci		ret_val = -EFAULT;
108762306a36Sopenharmony_ci		break;
108862306a36Sopenharmony_ci	}
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_ci	return ret_val;
109162306a36Sopenharmony_ci}
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_cistatic int cppc_get_perf(int cpunum, enum cppc_regs reg_idx, u64 *perf)
109462306a36Sopenharmony_ci{
109562306a36Sopenharmony_ci	struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpunum);
109662306a36Sopenharmony_ci	struct cpc_register_resource *reg;
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci	if (!cpc_desc) {
109962306a36Sopenharmony_ci		pr_debug("No CPC descriptor for CPU:%d\n", cpunum);
110062306a36Sopenharmony_ci		return -ENODEV;
110162306a36Sopenharmony_ci	}
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_ci	reg = &cpc_desc->cpc_regs[reg_idx];
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci	if (CPC_IN_PCC(reg)) {
110662306a36Sopenharmony_ci		int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpunum);
110762306a36Sopenharmony_ci		struct cppc_pcc_data *pcc_ss_data = NULL;
110862306a36Sopenharmony_ci		int ret = 0;
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci		if (pcc_ss_id < 0)
111162306a36Sopenharmony_ci			return -EIO;
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ci		pcc_ss_data = pcc_data[pcc_ss_id];
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_ci		down_write(&pcc_ss_data->pcc_lock);
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_ci		if (send_pcc_cmd(pcc_ss_id, CMD_READ) >= 0)
111862306a36Sopenharmony_ci			cpc_read(cpunum, reg, perf);
111962306a36Sopenharmony_ci		else
112062306a36Sopenharmony_ci			ret = -EIO;
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_ci		up_write(&pcc_ss_data->pcc_lock);
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci		return ret;
112562306a36Sopenharmony_ci	}
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ci	cpc_read(cpunum, reg, perf);
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_ci	return 0;
113062306a36Sopenharmony_ci}
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_ci/**
113362306a36Sopenharmony_ci * cppc_get_desired_perf - Get the desired performance register value.
113462306a36Sopenharmony_ci * @cpunum: CPU from which to get desired performance.
113562306a36Sopenharmony_ci * @desired_perf: Return address.
113662306a36Sopenharmony_ci *
113762306a36Sopenharmony_ci * Return: 0 for success, -EIO otherwise.
113862306a36Sopenharmony_ci */
113962306a36Sopenharmony_ciint cppc_get_desired_perf(int cpunum, u64 *desired_perf)
114062306a36Sopenharmony_ci{
114162306a36Sopenharmony_ci	return cppc_get_perf(cpunum, DESIRED_PERF, desired_perf);
114262306a36Sopenharmony_ci}
114362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cppc_get_desired_perf);
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci/**
114662306a36Sopenharmony_ci * cppc_get_nominal_perf - Get the nominal performance register value.
114762306a36Sopenharmony_ci * @cpunum: CPU from which to get nominal performance.
114862306a36Sopenharmony_ci * @nominal_perf: Return address.
114962306a36Sopenharmony_ci *
115062306a36Sopenharmony_ci * Return: 0 for success, -EIO otherwise.
115162306a36Sopenharmony_ci */
115262306a36Sopenharmony_ciint cppc_get_nominal_perf(int cpunum, u64 *nominal_perf)
115362306a36Sopenharmony_ci{
115462306a36Sopenharmony_ci	return cppc_get_perf(cpunum, NOMINAL_PERF, nominal_perf);
115562306a36Sopenharmony_ci}
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci/**
115862306a36Sopenharmony_ci * cppc_get_epp_perf - Get the epp register value.
115962306a36Sopenharmony_ci * @cpunum: CPU from which to get epp preference value.
116062306a36Sopenharmony_ci * @epp_perf: Return address.
116162306a36Sopenharmony_ci *
116262306a36Sopenharmony_ci * Return: 0 for success, -EIO otherwise.
116362306a36Sopenharmony_ci */
116462306a36Sopenharmony_ciint cppc_get_epp_perf(int cpunum, u64 *epp_perf)
116562306a36Sopenharmony_ci{
116662306a36Sopenharmony_ci	return cppc_get_perf(cpunum, ENERGY_PERF, epp_perf);
116762306a36Sopenharmony_ci}
116862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cppc_get_epp_perf);
116962306a36Sopenharmony_ci
117062306a36Sopenharmony_ci/**
117162306a36Sopenharmony_ci * cppc_get_perf_caps - Get a CPU's performance capabilities.
117262306a36Sopenharmony_ci * @cpunum: CPU from which to get capabilities info.
117362306a36Sopenharmony_ci * @perf_caps: ptr to cppc_perf_caps. See cppc_acpi.h
117462306a36Sopenharmony_ci *
117562306a36Sopenharmony_ci * Return: 0 for success with perf_caps populated else -ERRNO.
117662306a36Sopenharmony_ci */
117762306a36Sopenharmony_ciint cppc_get_perf_caps(int cpunum, struct cppc_perf_caps *perf_caps)
117862306a36Sopenharmony_ci{
117962306a36Sopenharmony_ci	struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpunum);
118062306a36Sopenharmony_ci	struct cpc_register_resource *highest_reg, *lowest_reg,
118162306a36Sopenharmony_ci		*lowest_non_linear_reg, *nominal_reg, *guaranteed_reg,
118262306a36Sopenharmony_ci		*low_freq_reg = NULL, *nom_freq_reg = NULL;
118362306a36Sopenharmony_ci	u64 high, low, guaranteed, nom, min_nonlinear, low_f = 0, nom_f = 0;
118462306a36Sopenharmony_ci	int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpunum);
118562306a36Sopenharmony_ci	struct cppc_pcc_data *pcc_ss_data = NULL;
118662306a36Sopenharmony_ci	int ret = 0, regs_in_pcc = 0;
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci	if (!cpc_desc) {
118962306a36Sopenharmony_ci		pr_debug("No CPC descriptor for CPU:%d\n", cpunum);
119062306a36Sopenharmony_ci		return -ENODEV;
119162306a36Sopenharmony_ci	}
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci	highest_reg = &cpc_desc->cpc_regs[HIGHEST_PERF];
119462306a36Sopenharmony_ci	lowest_reg = &cpc_desc->cpc_regs[LOWEST_PERF];
119562306a36Sopenharmony_ci	lowest_non_linear_reg = &cpc_desc->cpc_regs[LOW_NON_LINEAR_PERF];
119662306a36Sopenharmony_ci	nominal_reg = &cpc_desc->cpc_regs[NOMINAL_PERF];
119762306a36Sopenharmony_ci	low_freq_reg = &cpc_desc->cpc_regs[LOWEST_FREQ];
119862306a36Sopenharmony_ci	nom_freq_reg = &cpc_desc->cpc_regs[NOMINAL_FREQ];
119962306a36Sopenharmony_ci	guaranteed_reg = &cpc_desc->cpc_regs[GUARANTEED_PERF];
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci	/* Are any of the regs PCC ?*/
120262306a36Sopenharmony_ci	if (CPC_IN_PCC(highest_reg) || CPC_IN_PCC(lowest_reg) ||
120362306a36Sopenharmony_ci		CPC_IN_PCC(lowest_non_linear_reg) || CPC_IN_PCC(nominal_reg) ||
120462306a36Sopenharmony_ci		CPC_IN_PCC(low_freq_reg) || CPC_IN_PCC(nom_freq_reg)) {
120562306a36Sopenharmony_ci		if (pcc_ss_id < 0) {
120662306a36Sopenharmony_ci			pr_debug("Invalid pcc_ss_id\n");
120762306a36Sopenharmony_ci			return -ENODEV;
120862306a36Sopenharmony_ci		}
120962306a36Sopenharmony_ci		pcc_ss_data = pcc_data[pcc_ss_id];
121062306a36Sopenharmony_ci		regs_in_pcc = 1;
121162306a36Sopenharmony_ci		down_write(&pcc_ss_data->pcc_lock);
121262306a36Sopenharmony_ci		/* Ring doorbell once to update PCC subspace */
121362306a36Sopenharmony_ci		if (send_pcc_cmd(pcc_ss_id, CMD_READ) < 0) {
121462306a36Sopenharmony_ci			ret = -EIO;
121562306a36Sopenharmony_ci			goto out_err;
121662306a36Sopenharmony_ci		}
121762306a36Sopenharmony_ci	}
121862306a36Sopenharmony_ci
121962306a36Sopenharmony_ci	cpc_read(cpunum, highest_reg, &high);
122062306a36Sopenharmony_ci	perf_caps->highest_perf = high;
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci	cpc_read(cpunum, lowest_reg, &low);
122362306a36Sopenharmony_ci	perf_caps->lowest_perf = low;
122462306a36Sopenharmony_ci
122562306a36Sopenharmony_ci	cpc_read(cpunum, nominal_reg, &nom);
122662306a36Sopenharmony_ci	perf_caps->nominal_perf = nom;
122762306a36Sopenharmony_ci
122862306a36Sopenharmony_ci	if (guaranteed_reg->type != ACPI_TYPE_BUFFER  ||
122962306a36Sopenharmony_ci	    IS_NULL_REG(&guaranteed_reg->cpc_entry.reg)) {
123062306a36Sopenharmony_ci		perf_caps->guaranteed_perf = 0;
123162306a36Sopenharmony_ci	} else {
123262306a36Sopenharmony_ci		cpc_read(cpunum, guaranteed_reg, &guaranteed);
123362306a36Sopenharmony_ci		perf_caps->guaranteed_perf = guaranteed;
123462306a36Sopenharmony_ci	}
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_ci	cpc_read(cpunum, lowest_non_linear_reg, &min_nonlinear);
123762306a36Sopenharmony_ci	perf_caps->lowest_nonlinear_perf = min_nonlinear;
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_ci	if (!high || !low || !nom || !min_nonlinear)
124062306a36Sopenharmony_ci		ret = -EFAULT;
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_ci	/* Read optional lowest and nominal frequencies if present */
124362306a36Sopenharmony_ci	if (CPC_SUPPORTED(low_freq_reg))
124462306a36Sopenharmony_ci		cpc_read(cpunum, low_freq_reg, &low_f);
124562306a36Sopenharmony_ci
124662306a36Sopenharmony_ci	if (CPC_SUPPORTED(nom_freq_reg))
124762306a36Sopenharmony_ci		cpc_read(cpunum, nom_freq_reg, &nom_f);
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_ci	perf_caps->lowest_freq = low_f;
125062306a36Sopenharmony_ci	perf_caps->nominal_freq = nom_f;
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_ci
125362306a36Sopenharmony_ciout_err:
125462306a36Sopenharmony_ci	if (regs_in_pcc)
125562306a36Sopenharmony_ci		up_write(&pcc_ss_data->pcc_lock);
125662306a36Sopenharmony_ci	return ret;
125762306a36Sopenharmony_ci}
125862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cppc_get_perf_caps);
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_ci/**
126162306a36Sopenharmony_ci * cppc_perf_ctrs_in_pcc - Check if any perf counters are in a PCC region.
126262306a36Sopenharmony_ci *
126362306a36Sopenharmony_ci * CPPC has flexibility about how CPU performance counters are accessed.
126462306a36Sopenharmony_ci * One of the choices is PCC regions, which can have a high access latency. This
126562306a36Sopenharmony_ci * routine allows callers of cppc_get_perf_ctrs() to know this ahead of time.
126662306a36Sopenharmony_ci *
126762306a36Sopenharmony_ci * Return: true if any of the counters are in PCC regions, false otherwise
126862306a36Sopenharmony_ci */
126962306a36Sopenharmony_cibool cppc_perf_ctrs_in_pcc(void)
127062306a36Sopenharmony_ci{
127162306a36Sopenharmony_ci	int cpu;
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_ci	for_each_present_cpu(cpu) {
127462306a36Sopenharmony_ci		struct cpc_register_resource *ref_perf_reg;
127562306a36Sopenharmony_ci		struct cpc_desc *cpc_desc;
127662306a36Sopenharmony_ci
127762306a36Sopenharmony_ci		cpc_desc = per_cpu(cpc_desc_ptr, cpu);
127862306a36Sopenharmony_ci
127962306a36Sopenharmony_ci		if (CPC_IN_PCC(&cpc_desc->cpc_regs[DELIVERED_CTR]) ||
128062306a36Sopenharmony_ci		    CPC_IN_PCC(&cpc_desc->cpc_regs[REFERENCE_CTR]) ||
128162306a36Sopenharmony_ci		    CPC_IN_PCC(&cpc_desc->cpc_regs[CTR_WRAP_TIME]))
128262306a36Sopenharmony_ci			return true;
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci
128562306a36Sopenharmony_ci		ref_perf_reg = &cpc_desc->cpc_regs[REFERENCE_PERF];
128662306a36Sopenharmony_ci
128762306a36Sopenharmony_ci		/*
128862306a36Sopenharmony_ci		 * If reference perf register is not supported then we should
128962306a36Sopenharmony_ci		 * use the nominal perf value
129062306a36Sopenharmony_ci		 */
129162306a36Sopenharmony_ci		if (!CPC_SUPPORTED(ref_perf_reg))
129262306a36Sopenharmony_ci			ref_perf_reg = &cpc_desc->cpc_regs[NOMINAL_PERF];
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_ci		if (CPC_IN_PCC(ref_perf_reg))
129562306a36Sopenharmony_ci			return true;
129662306a36Sopenharmony_ci	}
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci	return false;
129962306a36Sopenharmony_ci}
130062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cppc_perf_ctrs_in_pcc);
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_ci/**
130362306a36Sopenharmony_ci * cppc_get_perf_ctrs - Read a CPU's performance feedback counters.
130462306a36Sopenharmony_ci * @cpunum: CPU from which to read counters.
130562306a36Sopenharmony_ci * @perf_fb_ctrs: ptr to cppc_perf_fb_ctrs. See cppc_acpi.h
130662306a36Sopenharmony_ci *
130762306a36Sopenharmony_ci * Return: 0 for success with perf_fb_ctrs populated else -ERRNO.
130862306a36Sopenharmony_ci */
130962306a36Sopenharmony_ciint cppc_get_perf_ctrs(int cpunum, struct cppc_perf_fb_ctrs *perf_fb_ctrs)
131062306a36Sopenharmony_ci{
131162306a36Sopenharmony_ci	struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpunum);
131262306a36Sopenharmony_ci	struct cpc_register_resource *delivered_reg, *reference_reg,
131362306a36Sopenharmony_ci		*ref_perf_reg, *ctr_wrap_reg;
131462306a36Sopenharmony_ci	int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpunum);
131562306a36Sopenharmony_ci	struct cppc_pcc_data *pcc_ss_data = NULL;
131662306a36Sopenharmony_ci	u64 delivered, reference, ref_perf, ctr_wrap_time;
131762306a36Sopenharmony_ci	int ret = 0, regs_in_pcc = 0;
131862306a36Sopenharmony_ci
131962306a36Sopenharmony_ci	if (!cpc_desc) {
132062306a36Sopenharmony_ci		pr_debug("No CPC descriptor for CPU:%d\n", cpunum);
132162306a36Sopenharmony_ci		return -ENODEV;
132262306a36Sopenharmony_ci	}
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_ci	delivered_reg = &cpc_desc->cpc_regs[DELIVERED_CTR];
132562306a36Sopenharmony_ci	reference_reg = &cpc_desc->cpc_regs[REFERENCE_CTR];
132662306a36Sopenharmony_ci	ref_perf_reg = &cpc_desc->cpc_regs[REFERENCE_PERF];
132762306a36Sopenharmony_ci	ctr_wrap_reg = &cpc_desc->cpc_regs[CTR_WRAP_TIME];
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_ci	/*
133062306a36Sopenharmony_ci	 * If reference perf register is not supported then we should
133162306a36Sopenharmony_ci	 * use the nominal perf value
133262306a36Sopenharmony_ci	 */
133362306a36Sopenharmony_ci	if (!CPC_SUPPORTED(ref_perf_reg))
133462306a36Sopenharmony_ci		ref_perf_reg = &cpc_desc->cpc_regs[NOMINAL_PERF];
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_ci	/* Are any of the regs PCC ?*/
133762306a36Sopenharmony_ci	if (CPC_IN_PCC(delivered_reg) || CPC_IN_PCC(reference_reg) ||
133862306a36Sopenharmony_ci		CPC_IN_PCC(ctr_wrap_reg) || CPC_IN_PCC(ref_perf_reg)) {
133962306a36Sopenharmony_ci		if (pcc_ss_id < 0) {
134062306a36Sopenharmony_ci			pr_debug("Invalid pcc_ss_id\n");
134162306a36Sopenharmony_ci			return -ENODEV;
134262306a36Sopenharmony_ci		}
134362306a36Sopenharmony_ci		pcc_ss_data = pcc_data[pcc_ss_id];
134462306a36Sopenharmony_ci		down_write(&pcc_ss_data->pcc_lock);
134562306a36Sopenharmony_ci		regs_in_pcc = 1;
134662306a36Sopenharmony_ci		/* Ring doorbell once to update PCC subspace */
134762306a36Sopenharmony_ci		if (send_pcc_cmd(pcc_ss_id, CMD_READ) < 0) {
134862306a36Sopenharmony_ci			ret = -EIO;
134962306a36Sopenharmony_ci			goto out_err;
135062306a36Sopenharmony_ci		}
135162306a36Sopenharmony_ci	}
135262306a36Sopenharmony_ci
135362306a36Sopenharmony_ci	cpc_read(cpunum, delivered_reg, &delivered);
135462306a36Sopenharmony_ci	cpc_read(cpunum, reference_reg, &reference);
135562306a36Sopenharmony_ci	cpc_read(cpunum, ref_perf_reg, &ref_perf);
135662306a36Sopenharmony_ci
135762306a36Sopenharmony_ci	/*
135862306a36Sopenharmony_ci	 * Per spec, if ctr_wrap_time optional register is unsupported, then the
135962306a36Sopenharmony_ci	 * performance counters are assumed to never wrap during the lifetime of
136062306a36Sopenharmony_ci	 * platform
136162306a36Sopenharmony_ci	 */
136262306a36Sopenharmony_ci	ctr_wrap_time = (u64)(~((u64)0));
136362306a36Sopenharmony_ci	if (CPC_SUPPORTED(ctr_wrap_reg))
136462306a36Sopenharmony_ci		cpc_read(cpunum, ctr_wrap_reg, &ctr_wrap_time);
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_ci	if (!delivered || !reference ||	!ref_perf) {
136762306a36Sopenharmony_ci		ret = -EFAULT;
136862306a36Sopenharmony_ci		goto out_err;
136962306a36Sopenharmony_ci	}
137062306a36Sopenharmony_ci
137162306a36Sopenharmony_ci	perf_fb_ctrs->delivered = delivered;
137262306a36Sopenharmony_ci	perf_fb_ctrs->reference = reference;
137362306a36Sopenharmony_ci	perf_fb_ctrs->reference_perf = ref_perf;
137462306a36Sopenharmony_ci	perf_fb_ctrs->wraparound_time = ctr_wrap_time;
137562306a36Sopenharmony_ciout_err:
137662306a36Sopenharmony_ci	if (regs_in_pcc)
137762306a36Sopenharmony_ci		up_write(&pcc_ss_data->pcc_lock);
137862306a36Sopenharmony_ci	return ret;
137962306a36Sopenharmony_ci}
138062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cppc_get_perf_ctrs);
138162306a36Sopenharmony_ci
138262306a36Sopenharmony_ci/*
138362306a36Sopenharmony_ci * Set Energy Performance Preference Register value through
138462306a36Sopenharmony_ci * Performance Controls Interface
138562306a36Sopenharmony_ci */
138662306a36Sopenharmony_ciint cppc_set_epp_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls, bool enable)
138762306a36Sopenharmony_ci{
138862306a36Sopenharmony_ci	int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpu);
138962306a36Sopenharmony_ci	struct cpc_register_resource *epp_set_reg;
139062306a36Sopenharmony_ci	struct cpc_register_resource *auto_sel_reg;
139162306a36Sopenharmony_ci	struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu);
139262306a36Sopenharmony_ci	struct cppc_pcc_data *pcc_ss_data = NULL;
139362306a36Sopenharmony_ci	int ret;
139462306a36Sopenharmony_ci
139562306a36Sopenharmony_ci	if (!cpc_desc) {
139662306a36Sopenharmony_ci		pr_debug("No CPC descriptor for CPU:%d\n", cpu);
139762306a36Sopenharmony_ci		return -ENODEV;
139862306a36Sopenharmony_ci	}
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_ci	auto_sel_reg = &cpc_desc->cpc_regs[AUTO_SEL_ENABLE];
140162306a36Sopenharmony_ci	epp_set_reg = &cpc_desc->cpc_regs[ENERGY_PERF];
140262306a36Sopenharmony_ci
140362306a36Sopenharmony_ci	if (CPC_IN_PCC(epp_set_reg) || CPC_IN_PCC(auto_sel_reg)) {
140462306a36Sopenharmony_ci		if (pcc_ss_id < 0) {
140562306a36Sopenharmony_ci			pr_debug("Invalid pcc_ss_id for CPU:%d\n", cpu);
140662306a36Sopenharmony_ci			return -ENODEV;
140762306a36Sopenharmony_ci		}
140862306a36Sopenharmony_ci
140962306a36Sopenharmony_ci		if (CPC_SUPPORTED(auto_sel_reg)) {
141062306a36Sopenharmony_ci			ret = cpc_write(cpu, auto_sel_reg, enable);
141162306a36Sopenharmony_ci			if (ret)
141262306a36Sopenharmony_ci				return ret;
141362306a36Sopenharmony_ci		}
141462306a36Sopenharmony_ci
141562306a36Sopenharmony_ci		if (CPC_SUPPORTED(epp_set_reg)) {
141662306a36Sopenharmony_ci			ret = cpc_write(cpu, epp_set_reg, perf_ctrls->energy_perf);
141762306a36Sopenharmony_ci			if (ret)
141862306a36Sopenharmony_ci				return ret;
141962306a36Sopenharmony_ci		}
142062306a36Sopenharmony_ci
142162306a36Sopenharmony_ci		pcc_ss_data = pcc_data[pcc_ss_id];
142262306a36Sopenharmony_ci
142362306a36Sopenharmony_ci		down_write(&pcc_ss_data->pcc_lock);
142462306a36Sopenharmony_ci		/* after writing CPC, transfer the ownership of PCC to platform */
142562306a36Sopenharmony_ci		ret = send_pcc_cmd(pcc_ss_id, CMD_WRITE);
142662306a36Sopenharmony_ci		up_write(&pcc_ss_data->pcc_lock);
142762306a36Sopenharmony_ci	} else {
142862306a36Sopenharmony_ci		ret = -ENOTSUPP;
142962306a36Sopenharmony_ci		pr_debug("_CPC in PCC is not supported\n");
143062306a36Sopenharmony_ci	}
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_ci	return ret;
143362306a36Sopenharmony_ci}
143462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cppc_set_epp_perf);
143562306a36Sopenharmony_ci
143662306a36Sopenharmony_ci/**
143762306a36Sopenharmony_ci * cppc_get_auto_sel_caps - Read autonomous selection register.
143862306a36Sopenharmony_ci * @cpunum : CPU from which to read register.
143962306a36Sopenharmony_ci * @perf_caps : struct where autonomous selection register value is updated.
144062306a36Sopenharmony_ci */
144162306a36Sopenharmony_ciint cppc_get_auto_sel_caps(int cpunum, struct cppc_perf_caps *perf_caps)
144262306a36Sopenharmony_ci{
144362306a36Sopenharmony_ci	struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpunum);
144462306a36Sopenharmony_ci	struct cpc_register_resource *auto_sel_reg;
144562306a36Sopenharmony_ci	u64  auto_sel;
144662306a36Sopenharmony_ci
144762306a36Sopenharmony_ci	if (!cpc_desc) {
144862306a36Sopenharmony_ci		pr_debug("No CPC descriptor for CPU:%d\n", cpunum);
144962306a36Sopenharmony_ci		return -ENODEV;
145062306a36Sopenharmony_ci	}
145162306a36Sopenharmony_ci
145262306a36Sopenharmony_ci	auto_sel_reg = &cpc_desc->cpc_regs[AUTO_SEL_ENABLE];
145362306a36Sopenharmony_ci
145462306a36Sopenharmony_ci	if (!CPC_SUPPORTED(auto_sel_reg))
145562306a36Sopenharmony_ci		pr_warn_once("Autonomous mode is not unsupported!\n");
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_ci	if (CPC_IN_PCC(auto_sel_reg)) {
145862306a36Sopenharmony_ci		int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpunum);
145962306a36Sopenharmony_ci		struct cppc_pcc_data *pcc_ss_data = NULL;
146062306a36Sopenharmony_ci		int ret = 0;
146162306a36Sopenharmony_ci
146262306a36Sopenharmony_ci		if (pcc_ss_id < 0)
146362306a36Sopenharmony_ci			return -ENODEV;
146462306a36Sopenharmony_ci
146562306a36Sopenharmony_ci		pcc_ss_data = pcc_data[pcc_ss_id];
146662306a36Sopenharmony_ci
146762306a36Sopenharmony_ci		down_write(&pcc_ss_data->pcc_lock);
146862306a36Sopenharmony_ci
146962306a36Sopenharmony_ci		if (send_pcc_cmd(pcc_ss_id, CMD_READ) >= 0) {
147062306a36Sopenharmony_ci			cpc_read(cpunum, auto_sel_reg, &auto_sel);
147162306a36Sopenharmony_ci			perf_caps->auto_sel = (bool)auto_sel;
147262306a36Sopenharmony_ci		} else {
147362306a36Sopenharmony_ci			ret = -EIO;
147462306a36Sopenharmony_ci		}
147562306a36Sopenharmony_ci
147662306a36Sopenharmony_ci		up_write(&pcc_ss_data->pcc_lock);
147762306a36Sopenharmony_ci
147862306a36Sopenharmony_ci		return ret;
147962306a36Sopenharmony_ci	}
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_ci	return 0;
148262306a36Sopenharmony_ci}
148362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cppc_get_auto_sel_caps);
148462306a36Sopenharmony_ci
148562306a36Sopenharmony_ci/**
148662306a36Sopenharmony_ci * cppc_set_auto_sel - Write autonomous selection register.
148762306a36Sopenharmony_ci * @cpu    : CPU to which to write register.
148862306a36Sopenharmony_ci * @enable : the desired value of autonomous selection resiter to be updated.
148962306a36Sopenharmony_ci */
149062306a36Sopenharmony_ciint cppc_set_auto_sel(int cpu, bool enable)
149162306a36Sopenharmony_ci{
149262306a36Sopenharmony_ci	int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpu);
149362306a36Sopenharmony_ci	struct cpc_register_resource *auto_sel_reg;
149462306a36Sopenharmony_ci	struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu);
149562306a36Sopenharmony_ci	struct cppc_pcc_data *pcc_ss_data = NULL;
149662306a36Sopenharmony_ci	int ret = -EINVAL;
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_ci	if (!cpc_desc) {
149962306a36Sopenharmony_ci		pr_debug("No CPC descriptor for CPU:%d\n", cpu);
150062306a36Sopenharmony_ci		return -ENODEV;
150162306a36Sopenharmony_ci	}
150262306a36Sopenharmony_ci
150362306a36Sopenharmony_ci	auto_sel_reg = &cpc_desc->cpc_regs[AUTO_SEL_ENABLE];
150462306a36Sopenharmony_ci
150562306a36Sopenharmony_ci	if (CPC_IN_PCC(auto_sel_reg)) {
150662306a36Sopenharmony_ci		if (pcc_ss_id < 0) {
150762306a36Sopenharmony_ci			pr_debug("Invalid pcc_ss_id\n");
150862306a36Sopenharmony_ci			return -ENODEV;
150962306a36Sopenharmony_ci		}
151062306a36Sopenharmony_ci
151162306a36Sopenharmony_ci		if (CPC_SUPPORTED(auto_sel_reg)) {
151262306a36Sopenharmony_ci			ret = cpc_write(cpu, auto_sel_reg, enable);
151362306a36Sopenharmony_ci			if (ret)
151462306a36Sopenharmony_ci				return ret;
151562306a36Sopenharmony_ci		}
151662306a36Sopenharmony_ci
151762306a36Sopenharmony_ci		pcc_ss_data = pcc_data[pcc_ss_id];
151862306a36Sopenharmony_ci
151962306a36Sopenharmony_ci		down_write(&pcc_ss_data->pcc_lock);
152062306a36Sopenharmony_ci		/* after writing CPC, transfer the ownership of PCC to platform */
152162306a36Sopenharmony_ci		ret = send_pcc_cmd(pcc_ss_id, CMD_WRITE);
152262306a36Sopenharmony_ci		up_write(&pcc_ss_data->pcc_lock);
152362306a36Sopenharmony_ci	} else {
152462306a36Sopenharmony_ci		ret = -ENOTSUPP;
152562306a36Sopenharmony_ci		pr_debug("_CPC in PCC is not supported\n");
152662306a36Sopenharmony_ci	}
152762306a36Sopenharmony_ci
152862306a36Sopenharmony_ci	return ret;
152962306a36Sopenharmony_ci}
153062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cppc_set_auto_sel);
153162306a36Sopenharmony_ci
153262306a36Sopenharmony_ci/**
153362306a36Sopenharmony_ci * cppc_set_enable - Set to enable CPPC on the processor by writing the
153462306a36Sopenharmony_ci * Continuous Performance Control package EnableRegister field.
153562306a36Sopenharmony_ci * @cpu: CPU for which to enable CPPC register.
153662306a36Sopenharmony_ci * @enable: 0 - disable, 1 - enable CPPC feature on the processor.
153762306a36Sopenharmony_ci *
153862306a36Sopenharmony_ci * Return: 0 for success, -ERRNO or -EIO otherwise.
153962306a36Sopenharmony_ci */
154062306a36Sopenharmony_ciint cppc_set_enable(int cpu, bool enable)
154162306a36Sopenharmony_ci{
154262306a36Sopenharmony_ci	int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpu);
154362306a36Sopenharmony_ci	struct cpc_register_resource *enable_reg;
154462306a36Sopenharmony_ci	struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu);
154562306a36Sopenharmony_ci	struct cppc_pcc_data *pcc_ss_data = NULL;
154662306a36Sopenharmony_ci	int ret = -EINVAL;
154762306a36Sopenharmony_ci
154862306a36Sopenharmony_ci	if (!cpc_desc) {
154962306a36Sopenharmony_ci		pr_debug("No CPC descriptor for CPU:%d\n", cpu);
155062306a36Sopenharmony_ci		return -EINVAL;
155162306a36Sopenharmony_ci	}
155262306a36Sopenharmony_ci
155362306a36Sopenharmony_ci	enable_reg = &cpc_desc->cpc_regs[ENABLE];
155462306a36Sopenharmony_ci
155562306a36Sopenharmony_ci	if (CPC_IN_PCC(enable_reg)) {
155662306a36Sopenharmony_ci
155762306a36Sopenharmony_ci		if (pcc_ss_id < 0)
155862306a36Sopenharmony_ci			return -EIO;
155962306a36Sopenharmony_ci
156062306a36Sopenharmony_ci		ret = cpc_write(cpu, enable_reg, enable);
156162306a36Sopenharmony_ci		if (ret)
156262306a36Sopenharmony_ci			return ret;
156362306a36Sopenharmony_ci
156462306a36Sopenharmony_ci		pcc_ss_data = pcc_data[pcc_ss_id];
156562306a36Sopenharmony_ci
156662306a36Sopenharmony_ci		down_write(&pcc_ss_data->pcc_lock);
156762306a36Sopenharmony_ci		/* after writing CPC, transfer the ownership of PCC to platfrom */
156862306a36Sopenharmony_ci		ret = send_pcc_cmd(pcc_ss_id, CMD_WRITE);
156962306a36Sopenharmony_ci		up_write(&pcc_ss_data->pcc_lock);
157062306a36Sopenharmony_ci		return ret;
157162306a36Sopenharmony_ci	}
157262306a36Sopenharmony_ci
157362306a36Sopenharmony_ci	return cpc_write(cpu, enable_reg, enable);
157462306a36Sopenharmony_ci}
157562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cppc_set_enable);
157662306a36Sopenharmony_ci
157762306a36Sopenharmony_ci/**
157862306a36Sopenharmony_ci * cppc_set_perf - Set a CPU's performance controls.
157962306a36Sopenharmony_ci * @cpu: CPU for which to set performance controls.
158062306a36Sopenharmony_ci * @perf_ctrls: ptr to cppc_perf_ctrls. See cppc_acpi.h
158162306a36Sopenharmony_ci *
158262306a36Sopenharmony_ci * Return: 0 for success, -ERRNO otherwise.
158362306a36Sopenharmony_ci */
158462306a36Sopenharmony_ciint cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls)
158562306a36Sopenharmony_ci{
158662306a36Sopenharmony_ci	struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu);
158762306a36Sopenharmony_ci	struct cpc_register_resource *desired_reg, *min_perf_reg, *max_perf_reg;
158862306a36Sopenharmony_ci	int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpu);
158962306a36Sopenharmony_ci	struct cppc_pcc_data *pcc_ss_data = NULL;
159062306a36Sopenharmony_ci	int ret = 0;
159162306a36Sopenharmony_ci
159262306a36Sopenharmony_ci	if (!cpc_desc) {
159362306a36Sopenharmony_ci		pr_debug("No CPC descriptor for CPU:%d\n", cpu);
159462306a36Sopenharmony_ci		return -ENODEV;
159562306a36Sopenharmony_ci	}
159662306a36Sopenharmony_ci
159762306a36Sopenharmony_ci	desired_reg = &cpc_desc->cpc_regs[DESIRED_PERF];
159862306a36Sopenharmony_ci	min_perf_reg = &cpc_desc->cpc_regs[MIN_PERF];
159962306a36Sopenharmony_ci	max_perf_reg = &cpc_desc->cpc_regs[MAX_PERF];
160062306a36Sopenharmony_ci
160162306a36Sopenharmony_ci	/*
160262306a36Sopenharmony_ci	 * This is Phase-I where we want to write to CPC registers
160362306a36Sopenharmony_ci	 * -> We want all CPUs to be able to execute this phase in parallel
160462306a36Sopenharmony_ci	 *
160562306a36Sopenharmony_ci	 * Since read_lock can be acquired by multiple CPUs simultaneously we
160662306a36Sopenharmony_ci	 * achieve that goal here
160762306a36Sopenharmony_ci	 */
160862306a36Sopenharmony_ci	if (CPC_IN_PCC(desired_reg) || CPC_IN_PCC(min_perf_reg) || CPC_IN_PCC(max_perf_reg)) {
160962306a36Sopenharmony_ci		if (pcc_ss_id < 0) {
161062306a36Sopenharmony_ci			pr_debug("Invalid pcc_ss_id\n");
161162306a36Sopenharmony_ci			return -ENODEV;
161262306a36Sopenharmony_ci		}
161362306a36Sopenharmony_ci		pcc_ss_data = pcc_data[pcc_ss_id];
161462306a36Sopenharmony_ci		down_read(&pcc_ss_data->pcc_lock); /* BEGIN Phase-I */
161562306a36Sopenharmony_ci		if (pcc_ss_data->platform_owns_pcc) {
161662306a36Sopenharmony_ci			ret = check_pcc_chan(pcc_ss_id, false);
161762306a36Sopenharmony_ci			if (ret) {
161862306a36Sopenharmony_ci				up_read(&pcc_ss_data->pcc_lock);
161962306a36Sopenharmony_ci				return ret;
162062306a36Sopenharmony_ci			}
162162306a36Sopenharmony_ci		}
162262306a36Sopenharmony_ci		/*
162362306a36Sopenharmony_ci		 * Update the pending_write to make sure a PCC CMD_READ will not
162462306a36Sopenharmony_ci		 * arrive and steal the channel during the switch to write lock
162562306a36Sopenharmony_ci		 */
162662306a36Sopenharmony_ci		pcc_ss_data->pending_pcc_write_cmd = true;
162762306a36Sopenharmony_ci		cpc_desc->write_cmd_id = pcc_ss_data->pcc_write_cnt;
162862306a36Sopenharmony_ci		cpc_desc->write_cmd_status = 0;
162962306a36Sopenharmony_ci	}
163062306a36Sopenharmony_ci
163162306a36Sopenharmony_ci	cpc_write(cpu, desired_reg, perf_ctrls->desired_perf);
163262306a36Sopenharmony_ci
163362306a36Sopenharmony_ci	/*
163462306a36Sopenharmony_ci	 * Only write if min_perf and max_perf not zero. Some drivers pass zero
163562306a36Sopenharmony_ci	 * value to min and max perf, but they don't mean to set the zero value,
163662306a36Sopenharmony_ci	 * they just don't want to write to those registers.
163762306a36Sopenharmony_ci	 */
163862306a36Sopenharmony_ci	if (perf_ctrls->min_perf)
163962306a36Sopenharmony_ci		cpc_write(cpu, min_perf_reg, perf_ctrls->min_perf);
164062306a36Sopenharmony_ci	if (perf_ctrls->max_perf)
164162306a36Sopenharmony_ci		cpc_write(cpu, max_perf_reg, perf_ctrls->max_perf);
164262306a36Sopenharmony_ci
164362306a36Sopenharmony_ci	if (CPC_IN_PCC(desired_reg) || CPC_IN_PCC(min_perf_reg) || CPC_IN_PCC(max_perf_reg))
164462306a36Sopenharmony_ci		up_read(&pcc_ss_data->pcc_lock);	/* END Phase-I */
164562306a36Sopenharmony_ci	/*
164662306a36Sopenharmony_ci	 * This is Phase-II where we transfer the ownership of PCC to Platform
164762306a36Sopenharmony_ci	 *
164862306a36Sopenharmony_ci	 * Short Summary: Basically if we think of a group of cppc_set_perf
164962306a36Sopenharmony_ci	 * requests that happened in short overlapping interval. The last CPU to
165062306a36Sopenharmony_ci	 * come out of Phase-I will enter Phase-II and ring the doorbell.
165162306a36Sopenharmony_ci	 *
165262306a36Sopenharmony_ci	 * We have the following requirements for Phase-II:
165362306a36Sopenharmony_ci	 *     1. We want to execute Phase-II only when there are no CPUs
165462306a36Sopenharmony_ci	 * currently executing in Phase-I
165562306a36Sopenharmony_ci	 *     2. Once we start Phase-II we want to avoid all other CPUs from
165662306a36Sopenharmony_ci	 * entering Phase-I.
165762306a36Sopenharmony_ci	 *     3. We want only one CPU among all those who went through Phase-I
165862306a36Sopenharmony_ci	 * to run phase-II
165962306a36Sopenharmony_ci	 *
166062306a36Sopenharmony_ci	 * If write_trylock fails to get the lock and doesn't transfer the
166162306a36Sopenharmony_ci	 * PCC ownership to the platform, then one of the following will be TRUE
166262306a36Sopenharmony_ci	 *     1. There is at-least one CPU in Phase-I which will later execute
166362306a36Sopenharmony_ci	 * write_trylock, so the CPUs in Phase-I will be responsible for
166462306a36Sopenharmony_ci	 * executing the Phase-II.
166562306a36Sopenharmony_ci	 *     2. Some other CPU has beaten this CPU to successfully execute the
166662306a36Sopenharmony_ci	 * write_trylock and has already acquired the write_lock. We know for a
166762306a36Sopenharmony_ci	 * fact it (other CPU acquiring the write_lock) couldn't have happened
166862306a36Sopenharmony_ci	 * before this CPU's Phase-I as we held the read_lock.
166962306a36Sopenharmony_ci	 *     3. Some other CPU executing pcc CMD_READ has stolen the
167062306a36Sopenharmony_ci	 * down_write, in which case, send_pcc_cmd will check for pending
167162306a36Sopenharmony_ci	 * CMD_WRITE commands by checking the pending_pcc_write_cmd.
167262306a36Sopenharmony_ci	 * So this CPU can be certain that its request will be delivered
167362306a36Sopenharmony_ci	 *    So in all cases, this CPU knows that its request will be delivered
167462306a36Sopenharmony_ci	 * by another CPU and can return
167562306a36Sopenharmony_ci	 *
167662306a36Sopenharmony_ci	 * After getting the down_write we still need to check for
167762306a36Sopenharmony_ci	 * pending_pcc_write_cmd to take care of the following scenario
167862306a36Sopenharmony_ci	 *    The thread running this code could be scheduled out between
167962306a36Sopenharmony_ci	 * Phase-I and Phase-II. Before it is scheduled back on, another CPU
168062306a36Sopenharmony_ci	 * could have delivered the request to Platform by triggering the
168162306a36Sopenharmony_ci	 * doorbell and transferred the ownership of PCC to platform. So this
168262306a36Sopenharmony_ci	 * avoids triggering an unnecessary doorbell and more importantly before
168362306a36Sopenharmony_ci	 * triggering the doorbell it makes sure that the PCC channel ownership
168462306a36Sopenharmony_ci	 * is still with OSPM.
168562306a36Sopenharmony_ci	 *   pending_pcc_write_cmd can also be cleared by a different CPU, if
168662306a36Sopenharmony_ci	 * there was a pcc CMD_READ waiting on down_write and it steals the lock
168762306a36Sopenharmony_ci	 * before the pcc CMD_WRITE is completed. send_pcc_cmd checks for this
168862306a36Sopenharmony_ci	 * case during a CMD_READ and if there are pending writes it delivers
168962306a36Sopenharmony_ci	 * the write command before servicing the read command
169062306a36Sopenharmony_ci	 */
169162306a36Sopenharmony_ci	if (CPC_IN_PCC(desired_reg) || CPC_IN_PCC(min_perf_reg) || CPC_IN_PCC(max_perf_reg)) {
169262306a36Sopenharmony_ci		if (down_write_trylock(&pcc_ss_data->pcc_lock)) {/* BEGIN Phase-II */
169362306a36Sopenharmony_ci			/* Update only if there are pending write commands */
169462306a36Sopenharmony_ci			if (pcc_ss_data->pending_pcc_write_cmd)
169562306a36Sopenharmony_ci				send_pcc_cmd(pcc_ss_id, CMD_WRITE);
169662306a36Sopenharmony_ci			up_write(&pcc_ss_data->pcc_lock);	/* END Phase-II */
169762306a36Sopenharmony_ci		} else
169862306a36Sopenharmony_ci			/* Wait until pcc_write_cnt is updated by send_pcc_cmd */
169962306a36Sopenharmony_ci			wait_event(pcc_ss_data->pcc_write_wait_q,
170062306a36Sopenharmony_ci				   cpc_desc->write_cmd_id != pcc_ss_data->pcc_write_cnt);
170162306a36Sopenharmony_ci
170262306a36Sopenharmony_ci		/* send_pcc_cmd updates the status in case of failure */
170362306a36Sopenharmony_ci		ret = cpc_desc->write_cmd_status;
170462306a36Sopenharmony_ci	}
170562306a36Sopenharmony_ci	return ret;
170662306a36Sopenharmony_ci}
170762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cppc_set_perf);
170862306a36Sopenharmony_ci
170962306a36Sopenharmony_ci/**
171062306a36Sopenharmony_ci * cppc_get_transition_latency - returns frequency transition latency in ns
171162306a36Sopenharmony_ci * @cpu_num: CPU number for per_cpu().
171262306a36Sopenharmony_ci *
171362306a36Sopenharmony_ci * ACPI CPPC does not explicitly specify how a platform can specify the
171462306a36Sopenharmony_ci * transition latency for performance change requests. The closest we have
171562306a36Sopenharmony_ci * is the timing information from the PCCT tables which provides the info
171662306a36Sopenharmony_ci * on the number and frequency of PCC commands the platform can handle.
171762306a36Sopenharmony_ci *
171862306a36Sopenharmony_ci * If desired_reg is in the SystemMemory or SystemIo ACPI address space,
171962306a36Sopenharmony_ci * then assume there is no latency.
172062306a36Sopenharmony_ci */
172162306a36Sopenharmony_ciunsigned int cppc_get_transition_latency(int cpu_num)
172262306a36Sopenharmony_ci{
172362306a36Sopenharmony_ci	/*
172462306a36Sopenharmony_ci	 * Expected transition latency is based on the PCCT timing values
172562306a36Sopenharmony_ci	 * Below are definition from ACPI spec:
172662306a36Sopenharmony_ci	 * pcc_nominal- Expected latency to process a command, in microseconds
172762306a36Sopenharmony_ci	 * pcc_mpar   - The maximum number of periodic requests that the subspace
172862306a36Sopenharmony_ci	 *              channel can support, reported in commands per minute. 0
172962306a36Sopenharmony_ci	 *              indicates no limitation.
173062306a36Sopenharmony_ci	 * pcc_mrtt   - The minimum amount of time that OSPM must wait after the
173162306a36Sopenharmony_ci	 *              completion of a command before issuing the next command,
173262306a36Sopenharmony_ci	 *              in microseconds.
173362306a36Sopenharmony_ci	 */
173462306a36Sopenharmony_ci	unsigned int latency_ns = 0;
173562306a36Sopenharmony_ci	struct cpc_desc *cpc_desc;
173662306a36Sopenharmony_ci	struct cpc_register_resource *desired_reg;
173762306a36Sopenharmony_ci	int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpu_num);
173862306a36Sopenharmony_ci	struct cppc_pcc_data *pcc_ss_data;
173962306a36Sopenharmony_ci
174062306a36Sopenharmony_ci	cpc_desc = per_cpu(cpc_desc_ptr, cpu_num);
174162306a36Sopenharmony_ci	if (!cpc_desc)
174262306a36Sopenharmony_ci		return CPUFREQ_ETERNAL;
174362306a36Sopenharmony_ci
174462306a36Sopenharmony_ci	desired_reg = &cpc_desc->cpc_regs[DESIRED_PERF];
174562306a36Sopenharmony_ci	if (CPC_IN_SYSTEM_MEMORY(desired_reg) || CPC_IN_SYSTEM_IO(desired_reg))
174662306a36Sopenharmony_ci		return 0;
174762306a36Sopenharmony_ci	else if (!CPC_IN_PCC(desired_reg))
174862306a36Sopenharmony_ci		return CPUFREQ_ETERNAL;
174962306a36Sopenharmony_ci
175062306a36Sopenharmony_ci	if (pcc_ss_id < 0)
175162306a36Sopenharmony_ci		return CPUFREQ_ETERNAL;
175262306a36Sopenharmony_ci
175362306a36Sopenharmony_ci	pcc_ss_data = pcc_data[pcc_ss_id];
175462306a36Sopenharmony_ci	if (pcc_ss_data->pcc_mpar)
175562306a36Sopenharmony_ci		latency_ns = 60 * (1000 * 1000 * 1000 / pcc_ss_data->pcc_mpar);
175662306a36Sopenharmony_ci
175762306a36Sopenharmony_ci	latency_ns = max(latency_ns, pcc_ss_data->pcc_nominal * 1000);
175862306a36Sopenharmony_ci	latency_ns = max(latency_ns, pcc_ss_data->pcc_mrtt * 1000);
175962306a36Sopenharmony_ci
176062306a36Sopenharmony_ci	return latency_ns;
176162306a36Sopenharmony_ci}
176262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cppc_get_transition_latency);
1763