summaryrefslogtreecommitdiffstats
path: root/drivers/acpi/riscv/cppc.c
blob: 4cdff387deff6cf9da1d80dc0411af3b5406a79f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Implement CPPC FFH helper routines for RISC-V.
 *
 * Copyright (C) 2024 Ventana Micro Systems Inc.
 */

#include <acpi/cppc_acpi.h>
#include <asm/csr.h>
#include <asm/sbi.h>

#define SBI_EXT_CPPC 0x43505043

/* CPPC interfaces defined in SBI spec */
#define SBI_CPPC_PROBE			0x0
#define SBI_CPPC_READ			0x1
#define SBI_CPPC_READ_HI		0x2
#define SBI_CPPC_WRITE			0x3

/* RISC-V FFH definitions from RISC-V FFH spec */
#define FFH_CPPC_TYPE(r)		(((r) & GENMASK_ULL(63, 60)) >> 60)
#define FFH_CPPC_SBI_REG(r)		((r) & GENMASK(31, 0))
#define FFH_CPPC_CSR_NUM(r)		((r) & GENMASK(11, 0))

#define FFH_CPPC_SBI			0x1
#define FFH_CPPC_CSR			0x2

struct sbi_cppc_data {
	u64 val;
	u32 reg;
	struct sbiret ret;
};

static bool cppc_ext_present;

static int __init sbi_cppc_init(void)
{
	if (sbi_spec_version >= sbi_mk_version(2, 0) &&
	    sbi_probe_extension(SBI_EXT_CPPC) > 0) {
		pr_info("SBI CPPC extension detected\n");
		cppc_ext_present = true;
	} else {
		pr_info("SBI CPPC extension NOT detected!!\n");
		cppc_ext_present = false;
	}

	return 0;
}
device_initcall(sbi_cppc_init);

static void sbi_cppc_read(void *read_data)
{
	struct sbi_cppc_data *data = (struct sbi_cppc_data *)read_data;

	data->ret = sbi_ecall(SBI_EXT_CPPC, SBI_CPPC_READ,
			      data->reg, 0, 0, 0, 0, 0);
}

static void sbi_cppc_write(void *write_data)
{
	struct sbi_cppc_data *data = (struct sbi_cppc_data *)write_data;

	data->ret = sbi_ecall(SBI_EXT_CPPC, SBI_CPPC_WRITE,
			      data->reg, data->val, 0, 0, 0, 0);
}

static void cppc_ffh_csr_read(void *read_data)
{
	struct sbi_cppc_data *data = (struct sbi_cppc_data *)read_data;

	switch (data->reg) {
	/* Support only TIME CSR for now */
	case CSR_TIME:
		data->ret.value = csr_read(CSR_TIME);
		data->ret.error = 0;
		break;
	default:
		data->ret.error = -EINVAL;
		break;
	}
}

static void cppc_ffh_csr_write(void *write_data)
{
	struct sbi_cppc_data *data = (struct sbi_cppc_data *)write_data;

	data->ret.error = -EINVAL;
}

/*
 * Refer to drivers/acpi/cppc_acpi.c for the description of the functions
 * below.
 */
bool cpc_ffh_supported(void)
{
	return true;
}

int cpc_read_ffh(int cpu, struct cpc_reg *reg, u64 *val)
{
	struct sbi_cppc_data data;

	if (WARN_ON_ONCE(irqs_disabled()))
		return -EPERM;

	if (FFH_CPPC_TYPE(reg->address) == FFH_CPPC_SBI) {
		if (!cppc_ext_present)
			return -EINVAL;

		data.reg = FFH_CPPC_SBI_REG(reg->address);

		smp_call_function_single(cpu, sbi_cppc_read, &data, 1);

		*val = data.ret.value;

		return (data.ret.error) ? sbi_err_map_linux_errno(data.ret.error) : 0;
	} else if (FFH_CPPC_TYPE(reg->address) == FFH_CPPC_CSR) {
		data.reg = FFH_CPPC_CSR_NUM(reg->address);

		smp_call_function_single(cpu, cppc_ffh_csr_read, &data, 1);

		*val = data.ret.value;

		return (data.ret.error) ? sbi_err_map_linux_errno(data.ret.error) : 0;
	}

	return -EINVAL;
}

int cpc_write_ffh(int cpu, struct cpc_reg *reg, u64 val)
{
	struct sbi_cppc_data data;

	if (WARN_ON_ONCE(irqs_disabled()))
		return -EPERM;

	if (FFH_CPPC_TYPE(reg->address) == FFH_CPPC_SBI) {
		if (!cppc_ext_present)
			return -EINVAL;

		data.reg = FFH_CPPC_SBI_REG(reg->address);
		data.val = val;

		smp_call_function_single(cpu, sbi_cppc_write, &data, 1);

		return (data.ret.error) ? sbi_err_map_linux_errno(data.ret.error) : 0;
	} else if (FFH_CPPC_TYPE(reg->address) == FFH_CPPC_CSR) {
		data.reg = FFH_CPPC_CSR_NUM(reg->address);
		data.val = val;

		smp_call_function_single(cpu, cppc_ffh_csr_write, &data, 1);

		return (data.ret.error) ? sbi_err_map_linux_errno(data.ret.error) : 0;
	}

	return -EINVAL;
}