xref: /openbsd/sys/dev/fdt/qccpu.c (revision 3bef86f7)
1 /*	$OpenBSD: qccpu.c,v 1.2 2023/07/01 18:59:11 drahn Exp $	*/
2 /*
3  * Copyright (c) 2023 Dale Rahn <drahn@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/param.h>
19 #include <sys/systm.h>
20 #include <sys/device.h>
21 #include <sys/sensors.h>
22 
23 #include <machine/intr.h>
24 #include <machine/bus.h>
25 #include <machine/fdt.h>
26 
27 #include <dev/ofw/openfirm.h>
28 #include <dev/ofw/ofw_clock.h>
29 #include <dev/ofw/fdt.h>
30 
31 #define CPUF_ENABLE		0x000
32 #define CPUF_DOMAIN_STATE	0x020
33 #define  CPUF_DOMAIN_STATE_LVAL_M	0xff
34 #define  CPUF_DOMAIN_STATE_LVAL_S	0
35 #define CPUF_DVCS_CTRL		0x0b0
36 #define  CPUF_DVCS_CTRL_PER_CORE	0x1
37 #define CPUF_FREQ_LUT		0x100
38 #define  CPUF_FREQ_LUT_SRC_M		0x1
39 #define  CPUF_FREQ_LUT_SRC_S		30
40 #define  CPUF_FREQ_LUT_CORES_M		0x7
41 #define  CPUF_FREQ_LUT_CORES_S		16
42 #define  CPUF_FREQ_LUT_LVAL_M		0xff
43 #define  CPUF_FREQ_LUT_LVAL_S		0
44 #define CPUF_VOLT_LUT		0x200
45 #define  CPUF_VOLT_LUT_IDX_M		0x2f
46 #define  CPUF_VOLT_LUT_IDX_S		16
47 #define  CPUF_VOLT_LUT_VOLT_M		0xfff
48 #define  CPUF_VOLT_LUT_VOLT_S		0
49 #define CPUF_PERF_STATE		0x320
50 #define LUT_ROW_SIZE  		4
51 
52 struct cpu_freq_tbl {
53 	uint32_t driver_data;
54 	uint32_t frequency;
55 };
56 
57 #define NUM_GROUP	2
58 #define MAX_LUT		40
59 
60 #define XO_FREQ_HZ	19200000
61 
62 struct qccpu_softc {
63 	struct device		sc_dev;
64 	bus_space_tag_t		sc_iot;
65 	bus_space_handle_t	sc_ioh[NUM_GROUP];
66 
67 	int			sc_node;
68 
69 	struct clock_device	sc_cd;
70 	uint32_t		sc_freq[NUM_GROUP][MAX_LUT];
71 	int			sc_num_lut[NUM_GROUP];
72 
73 	struct ksensordev       sc_sensordev;
74 	struct ksensor          sc_hz_sensor[NUM_GROUP];
75 };
76 
77 #define DEVNAME(sc) (sc)->sc_dev.dv_xname
78 
79 int	qccpu_match(struct device *, void *, void *);
80 void	qccpu_attach(struct device *, struct device *, void *);
81 void	qccpu_enable(void *, uint32_t *, int);
82 int	qccpu_set_frequency(void *, uint32_t *, uint32_t);
83 uint32_t qccpu_get_frequency(void *, uint32_t *);
84 uint32_t qccpu_lut_to_freq(struct qccpu_softc *, int, uint32_t);
85 uint32_t qccpu_lut_to_cores(struct qccpu_softc *, int, uint32_t);
86 void	qccpu_refresh_sensor(void *arg);
87 
88 void qccpu_collect_lut(struct qccpu_softc *sc, int);
89 
90 
91 const struct cfattach qccpu_ca = {
92 	sizeof (struct qccpu_softc), qccpu_match, qccpu_attach
93 };
94 
95 struct cfdriver qccpu_cd = {
96 	NULL, "qccpu", DV_DULL
97 };
98 
99 int
100 qccpu_match(struct device *parent, void *match, void *aux)
101 {
102 	struct fdt_attach_args *faa = aux;
103 
104 	return OF_is_compatible(faa->fa_node, "qcom,cpufreq-epss");
105 }
106 
107 void
108 qccpu_attach(struct device *parent, struct device *self, void *aux)
109 {
110 	struct qccpu_softc *sc = (struct qccpu_softc *)self;
111 	struct fdt_attach_args *faa = aux;
112 
113 	if (faa->fa_nreg < 2) {
114 		printf(": no registers\n");
115 		return;
116 	}
117 
118 	sc->sc_iot = faa->fa_iot;
119 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
120 	    faa->fa_reg[0].size, 0, &sc->sc_ioh[0])) {
121 		printf(": can't map registers (cluster0)\n");
122 		return;
123 	}
124 
125 	if (bus_space_map(sc->sc_iot, faa->fa_reg[1].addr,
126 	    faa->fa_reg[1].size, 0, &sc->sc_ioh[1])) {
127 		printf(": can't map registers (cluster1)\n");
128 		return;
129 	}
130 	sc->sc_node = faa->fa_node;
131 
132 	printf("\n");
133 
134 	qccpu_collect_lut(sc, 0);
135 	qccpu_collect_lut(sc, 1);
136 
137 	sc->sc_cd.cd_node = faa->fa_node;
138 	sc->sc_cd.cd_cookie = sc;
139 	sc->sc_cd.cd_get_frequency = qccpu_get_frequency;
140 	sc->sc_cd.cd_set_frequency = qccpu_set_frequency;
141 	clock_register(&sc->sc_cd);
142 
143 	strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
144 	    sizeof(sc->sc_sensordev.xname));
145 
146 	sc->sc_hz_sensor[0].type = SENSOR_FREQ;
147 	sensor_attach(&sc->sc_sensordev, &sc->sc_hz_sensor[0]);
148 	sc->sc_hz_sensor[1].type = SENSOR_FREQ;
149 	sensor_attach(&sc->sc_sensordev, &sc->sc_hz_sensor[1]);
150 	sensordev_install(&sc->sc_sensordev);
151 	sensor_task_register(sc, qccpu_refresh_sensor, 1);
152 }
153 
154 void
155 qccpu_collect_lut(struct qccpu_softc *sc, int group)
156 {
157 	int prev_freq = 0;
158 	uint32_t freq;
159 	int idx;
160 	bus_space_tag_t		iot = sc->sc_iot;
161 	bus_space_handle_t	ioh = sc->sc_ioh[group];
162 
163 	for (idx = 0; ; idx++) {
164 		freq = bus_space_read_4(iot, ioh,
165 		    CPUF_FREQ_LUT + idx * LUT_ROW_SIZE);
166 
167 		if (idx != 0 && prev_freq == freq) {
168 			sc->sc_num_lut[group] = idx;
169 			break;
170 		}
171 
172 		sc->sc_freq[group][idx] = freq;
173 
174 #ifdef DEBUG
175 		printf("%s: %d: %x %u\n", DEVNAME(sc), idx, freq,
176 		    qccpu_lut_to_freq(sc, idx, group));
177 #endif /* DEBUG */
178 
179 		prev_freq = freq;
180 		if (idx >= MAX_LUT-1)
181 			break;
182 	}
183 
184 	return;
185 }
186 
187 uint32_t
188 qccpu_get_frequency(void *cookie, uint32_t *cells)
189 {
190 	struct qccpu_softc *sc = cookie;
191 	bus_space_tag_t		iot = sc->sc_iot;
192 	bus_space_handle_t	ioh;
193 	uint32_t		lval;
194 	uint32_t		group;
195 
196 	if (cells[0] >= NUM_GROUP) {
197 		printf("%s: bad cell %d\n", __func__, cells[0]);
198 		return 0;
199 	}
200 	group = cells[0];
201 
202 	ioh = sc->sc_ioh[cells[0]];
203 
204 	lval = (bus_space_read_4(iot, ioh, CPUF_DOMAIN_STATE)
205 	    >> CPUF_DOMAIN_STATE_LVAL_S) & CPUF_DOMAIN_STATE_LVAL_M;
206 	return lval *XO_FREQ_HZ;
207 }
208 
209 int
210 qccpu_set_frequency(void *cookie, uint32_t *cells, uint32_t freq)
211 {
212 	struct qccpu_softc *sc = cookie;
213 	bus_space_tag_t		iot = sc->sc_iot;
214 	bus_space_handle_t	ioh;
215 	int			index = 0;
216 	int			numcores, i;
217 	uint32_t		group;
218 
219 	if (cells[0] >= NUM_GROUP) {
220 		printf("%s: bad cell %d\n", __func__, cells[0]);
221 		return 1;
222 	}
223 	group = cells[0];
224 
225 	ioh = sc->sc_ioh[group];
226 
227 	while (index < sc->sc_num_lut[group]) {
228 		if (freq == qccpu_lut_to_freq(sc, index, group))
229 			break;
230 
231 		if (freq < qccpu_lut_to_freq(sc, index, group)) {
232 			/* select next slower if not match, not zero */
233 			if (index != 0)
234 				index = index - 1;
235 			break;
236 		}
237 
238 		index++;
239 	}
240 
241 #ifdef DEBUG
242 	printf("%s called freq %u index %d\n", __func__, freq, index);
243 #endif /* DEBUG */
244 
245 	if ((bus_space_read_4(iot, ioh, CPUF_DVCS_CTRL) &
246 	    CPUF_DVCS_CTRL_PER_CORE) != 0)
247 		numcores = qccpu_lut_to_cores(sc, index, group);
248 	else
249 		numcores = 1;
250 	for (i = 0; i < numcores; i++)
251 		bus_space_write_4(iot, ioh, CPUF_PERF_STATE + i * 4, index);
252 
253 	return 0;
254 }
255 
256 uint32_t
257 qccpu_lut_to_freq(struct qccpu_softc *sc, int index, uint32_t group)
258 {
259 	return XO_FREQ_HZ *
260 	    ((sc->sc_freq[group][index] >> CPUF_FREQ_LUT_LVAL_S)
261 	     & CPUF_FREQ_LUT_LVAL_M);
262 }
263 
264 uint32_t
265 qccpu_lut_to_cores(struct qccpu_softc *sc, int index, uint32_t group)
266 {
267 	return ((sc->sc_freq[group][index] >> CPUF_FREQ_LUT_CORES_S)
268 	    & CPUF_FREQ_LUT_CORES_M);
269 }
270 
271 void
272 qccpu_refresh_sensor(void *arg)
273 {
274         struct qccpu_softc *sc = arg;
275 	bus_space_tag_t		iot = sc->sc_iot;
276 	bus_space_handle_t	ioh;
277 	int		 idx;
278 	uint32_t	 lval;
279 
280 	for (idx = 0; idx < NUM_GROUP; idx++) {
281 		ioh = sc->sc_ioh[idx];
282 
283 		lval = (bus_space_read_4(iot, ioh, CPUF_DOMAIN_STATE)
284 		    >> CPUF_DOMAIN_STATE_LVAL_S) & CPUF_DOMAIN_STATE_LVAL_M;
285 		sc->sc_hz_sensor[idx].value = 1000000ULL * lval * XO_FREQ_HZ;
286 	}
287 }
288