xref: /linux/drivers/acpi/riscv/cppc.c (revision 79790b6818e96c58fe2bffee1b418c16e64e7b80)
1*30f3ffbeSSunil V L // SPDX-License-Identifier: GPL-2.0-only
2*30f3ffbeSSunil V L /*
3*30f3ffbeSSunil V L  * Implement CPPC FFH helper routines for RISC-V.
4*30f3ffbeSSunil V L  *
5*30f3ffbeSSunil V L  * Copyright (C) 2024 Ventana Micro Systems Inc.
6*30f3ffbeSSunil V L  */
7*30f3ffbeSSunil V L 
8*30f3ffbeSSunil V L #include <acpi/cppc_acpi.h>
9*30f3ffbeSSunil V L #include <asm/csr.h>
10*30f3ffbeSSunil V L #include <asm/sbi.h>
11*30f3ffbeSSunil V L 
12*30f3ffbeSSunil V L #define SBI_EXT_CPPC 0x43505043
13*30f3ffbeSSunil V L 
14*30f3ffbeSSunil V L /* CPPC interfaces defined in SBI spec */
15*30f3ffbeSSunil V L #define SBI_CPPC_PROBE			0x0
16*30f3ffbeSSunil V L #define SBI_CPPC_READ			0x1
17*30f3ffbeSSunil V L #define SBI_CPPC_READ_HI		0x2
18*30f3ffbeSSunil V L #define SBI_CPPC_WRITE			0x3
19*30f3ffbeSSunil V L 
20*30f3ffbeSSunil V L /* RISC-V FFH definitions from RISC-V FFH spec */
21*30f3ffbeSSunil V L #define FFH_CPPC_TYPE(r)		(((r) & GENMASK_ULL(63, 60)) >> 60)
22*30f3ffbeSSunil V L #define FFH_CPPC_SBI_REG(r)		((r) & GENMASK(31, 0))
23*30f3ffbeSSunil V L #define FFH_CPPC_CSR_NUM(r)		((r) & GENMASK(11, 0))
24*30f3ffbeSSunil V L 
25*30f3ffbeSSunil V L #define FFH_CPPC_SBI			0x1
26*30f3ffbeSSunil V L #define FFH_CPPC_CSR			0x2
27*30f3ffbeSSunil V L 
28*30f3ffbeSSunil V L struct sbi_cppc_data {
29*30f3ffbeSSunil V L 	u64 val;
30*30f3ffbeSSunil V L 	u32 reg;
31*30f3ffbeSSunil V L 	struct sbiret ret;
32*30f3ffbeSSunil V L };
33*30f3ffbeSSunil V L 
34*30f3ffbeSSunil V L static bool cppc_ext_present;
35*30f3ffbeSSunil V L 
sbi_cppc_init(void)36*30f3ffbeSSunil V L static int __init sbi_cppc_init(void)
37*30f3ffbeSSunil V L {
38*30f3ffbeSSunil V L 	if (sbi_spec_version >= sbi_mk_version(2, 0) &&
39*30f3ffbeSSunil V L 	    sbi_probe_extension(SBI_EXT_CPPC) > 0) {
40*30f3ffbeSSunil V L 		pr_info("SBI CPPC extension detected\n");
41*30f3ffbeSSunil V L 		cppc_ext_present = true;
42*30f3ffbeSSunil V L 	} else {
43*30f3ffbeSSunil V L 		pr_info("SBI CPPC extension NOT detected!!\n");
44*30f3ffbeSSunil V L 		cppc_ext_present = false;
45*30f3ffbeSSunil V L 	}
46*30f3ffbeSSunil V L 
47*30f3ffbeSSunil V L 	return 0;
48*30f3ffbeSSunil V L }
49*30f3ffbeSSunil V L device_initcall(sbi_cppc_init);
50*30f3ffbeSSunil V L 
sbi_cppc_read(void * read_data)51*30f3ffbeSSunil V L static void sbi_cppc_read(void *read_data)
52*30f3ffbeSSunil V L {
53*30f3ffbeSSunil V L 	struct sbi_cppc_data *data = (struct sbi_cppc_data *)read_data;
54*30f3ffbeSSunil V L 
55*30f3ffbeSSunil V L 	data->ret = sbi_ecall(SBI_EXT_CPPC, SBI_CPPC_READ,
56*30f3ffbeSSunil V L 			      data->reg, 0, 0, 0, 0, 0);
57*30f3ffbeSSunil V L }
58*30f3ffbeSSunil V L 
sbi_cppc_write(void * write_data)59*30f3ffbeSSunil V L static void sbi_cppc_write(void *write_data)
60*30f3ffbeSSunil V L {
61*30f3ffbeSSunil V L 	struct sbi_cppc_data *data = (struct sbi_cppc_data *)write_data;
62*30f3ffbeSSunil V L 
63*30f3ffbeSSunil V L 	data->ret = sbi_ecall(SBI_EXT_CPPC, SBI_CPPC_WRITE,
64*30f3ffbeSSunil V L 			      data->reg, data->val, 0, 0, 0, 0);
65*30f3ffbeSSunil V L }
66*30f3ffbeSSunil V L 
cppc_ffh_csr_read(void * read_data)67*30f3ffbeSSunil V L static void cppc_ffh_csr_read(void *read_data)
68*30f3ffbeSSunil V L {
69*30f3ffbeSSunil V L 	struct sbi_cppc_data *data = (struct sbi_cppc_data *)read_data;
70*30f3ffbeSSunil V L 
71*30f3ffbeSSunil V L 	switch (data->reg) {
72*30f3ffbeSSunil V L 	/* Support only TIME CSR for now */
73*30f3ffbeSSunil V L 	case CSR_TIME:
74*30f3ffbeSSunil V L 		data->ret.value = csr_read(CSR_TIME);
75*30f3ffbeSSunil V L 		data->ret.error = 0;
76*30f3ffbeSSunil V L 		break;
77*30f3ffbeSSunil V L 	default:
78*30f3ffbeSSunil V L 		data->ret.error = -EINVAL;
79*30f3ffbeSSunil V L 		break;
80*30f3ffbeSSunil V L 	}
81*30f3ffbeSSunil V L }
82*30f3ffbeSSunil V L 
cppc_ffh_csr_write(void * write_data)83*30f3ffbeSSunil V L static void cppc_ffh_csr_write(void *write_data)
84*30f3ffbeSSunil V L {
85*30f3ffbeSSunil V L 	struct sbi_cppc_data *data = (struct sbi_cppc_data *)write_data;
86*30f3ffbeSSunil V L 
87*30f3ffbeSSunil V L 	data->ret.error = -EINVAL;
88*30f3ffbeSSunil V L }
89*30f3ffbeSSunil V L 
90*30f3ffbeSSunil V L /*
91*30f3ffbeSSunil V L  * Refer to drivers/acpi/cppc_acpi.c for the description of the functions
92*30f3ffbeSSunil V L  * below.
93*30f3ffbeSSunil V L  */
cpc_ffh_supported(void)94*30f3ffbeSSunil V L bool cpc_ffh_supported(void)
95*30f3ffbeSSunil V L {
96*30f3ffbeSSunil V L 	return true;
97*30f3ffbeSSunil V L }
98*30f3ffbeSSunil V L 
cpc_read_ffh(int cpu,struct cpc_reg * reg,u64 * val)99*30f3ffbeSSunil V L int cpc_read_ffh(int cpu, struct cpc_reg *reg, u64 *val)
100*30f3ffbeSSunil V L {
101*30f3ffbeSSunil V L 	struct sbi_cppc_data data;
102*30f3ffbeSSunil V L 
103*30f3ffbeSSunil V L 	if (WARN_ON_ONCE(irqs_disabled()))
104*30f3ffbeSSunil V L 		return -EPERM;
105*30f3ffbeSSunil V L 
106*30f3ffbeSSunil V L 	if (FFH_CPPC_TYPE(reg->address) == FFH_CPPC_SBI) {
107*30f3ffbeSSunil V L 		if (!cppc_ext_present)
108*30f3ffbeSSunil V L 			return -EINVAL;
109*30f3ffbeSSunil V L 
110*30f3ffbeSSunil V L 		data.reg = FFH_CPPC_SBI_REG(reg->address);
111*30f3ffbeSSunil V L 
112*30f3ffbeSSunil V L 		smp_call_function_single(cpu, sbi_cppc_read, &data, 1);
113*30f3ffbeSSunil V L 
114*30f3ffbeSSunil V L 		*val = data.ret.value;
115*30f3ffbeSSunil V L 
116*30f3ffbeSSunil V L 		return (data.ret.error) ? sbi_err_map_linux_errno(data.ret.error) : 0;
117*30f3ffbeSSunil V L 	} else if (FFH_CPPC_TYPE(reg->address) == FFH_CPPC_CSR) {
118*30f3ffbeSSunil V L 		data.reg = FFH_CPPC_CSR_NUM(reg->address);
119*30f3ffbeSSunil V L 
120*30f3ffbeSSunil V L 		smp_call_function_single(cpu, cppc_ffh_csr_read, &data, 1);
121*30f3ffbeSSunil V L 
122*30f3ffbeSSunil V L 		*val = data.ret.value;
123*30f3ffbeSSunil V L 
124*30f3ffbeSSunil V L 		return (data.ret.error) ? sbi_err_map_linux_errno(data.ret.error) : 0;
125*30f3ffbeSSunil V L 	}
126*30f3ffbeSSunil V L 
127*30f3ffbeSSunil V L 	return -EINVAL;
128*30f3ffbeSSunil V L }
129*30f3ffbeSSunil V L 
cpc_write_ffh(int cpu,struct cpc_reg * reg,u64 val)130*30f3ffbeSSunil V L int cpc_write_ffh(int cpu, struct cpc_reg *reg, u64 val)
131*30f3ffbeSSunil V L {
132*30f3ffbeSSunil V L 	struct sbi_cppc_data data;
133*30f3ffbeSSunil V L 
134*30f3ffbeSSunil V L 	if (WARN_ON_ONCE(irqs_disabled()))
135*30f3ffbeSSunil V L 		return -EPERM;
136*30f3ffbeSSunil V L 
137*30f3ffbeSSunil V L 	if (FFH_CPPC_TYPE(reg->address) == FFH_CPPC_SBI) {
138*30f3ffbeSSunil V L 		if (!cppc_ext_present)
139*30f3ffbeSSunil V L 			return -EINVAL;
140*30f3ffbeSSunil V L 
141*30f3ffbeSSunil V L 		data.reg = FFH_CPPC_SBI_REG(reg->address);
142*30f3ffbeSSunil V L 		data.val = val;
143*30f3ffbeSSunil V L 
144*30f3ffbeSSunil V L 		smp_call_function_single(cpu, sbi_cppc_write, &data, 1);
145*30f3ffbeSSunil V L 
146*30f3ffbeSSunil V L 		return (data.ret.error) ? sbi_err_map_linux_errno(data.ret.error) : 0;
147*30f3ffbeSSunil V L 	} else if (FFH_CPPC_TYPE(reg->address) == FFH_CPPC_CSR) {
148*30f3ffbeSSunil V L 		data.reg = FFH_CPPC_CSR_NUM(reg->address);
149*30f3ffbeSSunil V L 		data.val = val;
150*30f3ffbeSSunil V L 
151*30f3ffbeSSunil V L 		smp_call_function_single(cpu, cppc_ffh_csr_write, &data, 1);
152*30f3ffbeSSunil V L 
153*30f3ffbeSSunil V L 		return (data.ret.error) ? sbi_err_map_linux_errno(data.ret.error) : 0;
154*30f3ffbeSSunil V L 	}
155*30f3ffbeSSunil V L 
156*30f3ffbeSSunil V L 	return -EINVAL;
157*30f3ffbeSSunil V L }
158