xref: /openbsd/sys/dev/fdt/qccpu.c (revision 4826055c)
1 /*	$OpenBSD: qccpu.c,v 1.4 2024/10/10 23:15:27 jsg 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 #define NUM_GROUP	2
53 #define MAX_LUT		40
54 
55 #define XO_FREQ_HZ	19200000
56 
57 struct qccpu_softc {
58 	struct device		sc_dev;
59 	bus_space_tag_t		sc_iot;
60 	bus_space_handle_t	sc_ioh[NUM_GROUP];
61 
62 	int			sc_node;
63 
64 	struct clock_device	sc_cd;
65 	uint32_t		sc_freq[NUM_GROUP][MAX_LUT];
66 	int			sc_num_lut[NUM_GROUP];
67 
68 	struct ksensordev       sc_sensordev;
69 	struct ksensor          sc_hz_sensor[NUM_GROUP];
70 };
71 
72 #define DEVNAME(sc) (sc)->sc_dev.dv_xname
73 
74 int	qccpu_match(struct device *, void *, void *);
75 void	qccpu_attach(struct device *, struct device *, void *);
76 int	qccpu_set_frequency(void *, uint32_t *, uint32_t);
77 uint32_t qccpu_get_frequency(void *, uint32_t *);
78 uint32_t qccpu_lut_to_freq(struct qccpu_softc *, int, uint32_t);
79 uint32_t qccpu_lut_to_cores(struct qccpu_softc *, int, uint32_t);
80 void	qccpu_refresh_sensor(void *arg);
81 
82 void qccpu_collect_lut(struct qccpu_softc *sc, int);
83 
84 
85 const struct cfattach qccpu_ca = {
86 	sizeof (struct qccpu_softc), qccpu_match, qccpu_attach
87 };
88 
89 struct cfdriver qccpu_cd = {
90 	NULL, "qccpu", DV_DULL
91 };
92 
93 int
qccpu_match(struct device * parent,void * match,void * aux)94 qccpu_match(struct device *parent, void *match, void *aux)
95 {
96 	struct fdt_attach_args *faa = aux;
97 
98 	return OF_is_compatible(faa->fa_node, "qcom,cpufreq-epss");
99 }
100 
101 void
qccpu_attach(struct device * parent,struct device * self,void * aux)102 qccpu_attach(struct device *parent, struct device *self, void *aux)
103 {
104 	struct qccpu_softc *sc = (struct qccpu_softc *)self;
105 	struct fdt_attach_args *faa = aux;
106 
107 	if (faa->fa_nreg < 2) {
108 		printf(": no registers\n");
109 		return;
110 	}
111 
112 	sc->sc_iot = faa->fa_iot;
113 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
114 	    faa->fa_reg[0].size, 0, &sc->sc_ioh[0])) {
115 		printf(": can't map registers (cluster0)\n");
116 		return;
117 	}
118 
119 	if (bus_space_map(sc->sc_iot, faa->fa_reg[1].addr,
120 	    faa->fa_reg[1].size, 0, &sc->sc_ioh[1])) {
121 		printf(": can't map registers (cluster1)\n");
122 		return;
123 	}
124 	sc->sc_node = faa->fa_node;
125 
126 	printf("\n");
127 
128 	qccpu_collect_lut(sc, 0);
129 	qccpu_collect_lut(sc, 1);
130 
131 	sc->sc_cd.cd_node = faa->fa_node;
132 	sc->sc_cd.cd_cookie = sc;
133 	sc->sc_cd.cd_get_frequency = qccpu_get_frequency;
134 	sc->sc_cd.cd_set_frequency = qccpu_set_frequency;
135 	clock_register(&sc->sc_cd);
136 
137 	strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
138 	    sizeof(sc->sc_sensordev.xname));
139 
140 	sc->sc_hz_sensor[0].type = SENSOR_FREQ;
141 	sensor_attach(&sc->sc_sensordev, &sc->sc_hz_sensor[0]);
142 	sc->sc_hz_sensor[1].type = SENSOR_FREQ;
143 	sensor_attach(&sc->sc_sensordev, &sc->sc_hz_sensor[1]);
144 	sensordev_install(&sc->sc_sensordev);
145 	sensor_task_register(sc, qccpu_refresh_sensor, 1);
146 }
147 
148 void
qccpu_collect_lut(struct qccpu_softc * sc,int group)149 qccpu_collect_lut(struct qccpu_softc *sc, int group)
150 {
151 	int prev_freq = 0;
152 	uint32_t freq;
153 	int idx;
154 	bus_space_tag_t		iot = sc->sc_iot;
155 	bus_space_handle_t	ioh = sc->sc_ioh[group];
156 
157 	for (idx = 0; ; idx++) {
158 		freq = bus_space_read_4(iot, ioh,
159 		    CPUF_FREQ_LUT + idx * LUT_ROW_SIZE);
160 
161 		if (idx != 0 && prev_freq == freq) {
162 			sc->sc_num_lut[group] = idx;
163 			break;
164 		}
165 
166 		sc->sc_freq[group][idx] = freq;
167 
168 #ifdef DEBUG
169 		printf("%s: %d: %x %u\n", DEVNAME(sc), idx, freq,
170 		    qccpu_lut_to_freq(sc, idx, group));
171 #endif /* DEBUG */
172 
173 		prev_freq = freq;
174 		if (idx >= MAX_LUT-1)
175 			break;
176 	}
177 
178 	return;
179 }
180 
181 uint32_t
qccpu_get_frequency(void * cookie,uint32_t * cells)182 qccpu_get_frequency(void *cookie, uint32_t *cells)
183 {
184 	struct qccpu_softc *sc = cookie;
185 	bus_space_tag_t		iot = sc->sc_iot;
186 	bus_space_handle_t	ioh;
187 	uint32_t		lval;
188 	uint32_t		group;
189 
190 	if (cells[0] >= NUM_GROUP) {
191 		printf("%s: bad cell %d\n", __func__, cells[0]);
192 		return 0;
193 	}
194 	group = cells[0];
195 
196 	ioh = sc->sc_ioh[cells[0]];
197 
198 	lval = (bus_space_read_4(iot, ioh, CPUF_DOMAIN_STATE)
199 	    >> CPUF_DOMAIN_STATE_LVAL_S) & CPUF_DOMAIN_STATE_LVAL_M;
200 	return lval *XO_FREQ_HZ;
201 }
202 
203 int
qccpu_set_frequency(void * cookie,uint32_t * cells,uint32_t freq)204 qccpu_set_frequency(void *cookie, uint32_t *cells, uint32_t freq)
205 {
206 	struct qccpu_softc *sc = cookie;
207 	bus_space_tag_t		iot = sc->sc_iot;
208 	bus_space_handle_t	ioh;
209 	int			index = 0;
210 	int			numcores, i;
211 	uint32_t		group;
212 
213 	if (cells[0] >= NUM_GROUP) {
214 		printf("%s: bad cell %d\n", __func__, cells[0]);
215 		return 1;
216 	}
217 	group = cells[0];
218 
219 	ioh = sc->sc_ioh[group];
220 
221 	while (index < sc->sc_num_lut[group]) {
222 		if (freq == qccpu_lut_to_freq(sc, index, group))
223 			break;
224 
225 		if (freq < qccpu_lut_to_freq(sc, index, group)) {
226 			/* select next slower if not match, not zero */
227 			if (index != 0)
228 				index = index - 1;
229 			break;
230 		}
231 
232 		index++;
233 	}
234 
235 #ifdef DEBUG
236 	printf("%s called freq %u index %d\n", __func__, freq, index);
237 #endif /* DEBUG */
238 
239 	if ((bus_space_read_4(iot, ioh, CPUF_DVCS_CTRL) &
240 	    CPUF_DVCS_CTRL_PER_CORE) != 0)
241 		numcores = qccpu_lut_to_cores(sc, index, group);
242 	else
243 		numcores = 1;
244 	for (i = 0; i < numcores; i++)
245 		bus_space_write_4(iot, ioh, CPUF_PERF_STATE + i * 4, index);
246 
247 	return 0;
248 }
249 
250 uint32_t
qccpu_lut_to_freq(struct qccpu_softc * sc,int index,uint32_t group)251 qccpu_lut_to_freq(struct qccpu_softc *sc, int index, uint32_t group)
252 {
253 	return XO_FREQ_HZ *
254 	    ((sc->sc_freq[group][index] >> CPUF_FREQ_LUT_LVAL_S)
255 	     & CPUF_FREQ_LUT_LVAL_M);
256 }
257 
258 uint32_t
qccpu_lut_to_cores(struct qccpu_softc * sc,int index,uint32_t group)259 qccpu_lut_to_cores(struct qccpu_softc *sc, int index, uint32_t group)
260 {
261 	return ((sc->sc_freq[group][index] >> CPUF_FREQ_LUT_CORES_S)
262 	    & CPUF_FREQ_LUT_CORES_M);
263 }
264 
265 void
qccpu_refresh_sensor(void * arg)266 qccpu_refresh_sensor(void *arg)
267 {
268         struct qccpu_softc *sc = arg;
269 	bus_space_tag_t		iot = sc->sc_iot;
270 	bus_space_handle_t	ioh;
271 	int		 idx;
272 	uint32_t	 lval;
273 
274 	for (idx = 0; idx < NUM_GROUP; idx++) {
275 		ioh = sc->sc_ioh[idx];
276 
277 		lval = (bus_space_read_4(iot, ioh, CPUF_DOMAIN_STATE)
278 		    >> CPUF_DOMAIN_STATE_LVAL_S) & CPUF_DOMAIN_STATE_LVAL_M;
279 		sc->sc_hz_sensor[idx].value = 1000000ULL * lval * XO_FREQ_HZ;
280 	}
281 }
282