xref: /netbsd-src/sys/dev/acpi/qcompep.c (revision 5b1af19431e96722c94723c98b101b0a8c0635b7)
1 /* $NetBSD: qcompep.c,v 1.2 2025/01/08 22:58:05 jmcneill Exp $ */
2 /*	$OpenBSD: qcaoss.c,v 1.1 2023/05/23 14:10:27 patrick Exp $	*/
3 /*      $OpenBSD: qccpucp.c,v 1.1 2024/11/16 21:17:54 tobhe Exp $       */
4 /*
5  * Copyright (c) 2023 Patrick Wildt <patrick@blueri.se>
6  * Copyright (c) 2024 Tobias Heider <tobhe@openbsd.org>
7  *
8  * Permission to use, copy, modify, and distribute this software for any
9  * purpose with or without fee is hereby granted, provided that the above
10  * copyright notice and this permission notice appear in all copies.
11  *
12  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19  */
20 
21 #include <sys/param.h>
22 #include <sys/systm.h>
23 #include <sys/device.h>
24 #include <sys/kmem.h>
25 
26 #include <dev/acpi/acpivar.h>
27 #include <dev/acpi/qcompep.h>
28 #include <dev/acpi/qcomipcc.h>
29 
30 #include <dev/ic/scmi.h>
31 
32 #define AOSS_DESC_MAGIC			0x0
33 #define AOSS_DESC_VERSION		0x4
34 #define AOSS_DESC_FEATURES		0x8
35 #define AOSS_DESC_UCORE_LINK_STATE	0xc
36 #define AOSS_DESC_UCORE_LINK_STATE_ACK	0x10
37 #define AOSS_DESC_UCORE_CH_STATE	0x14
38 #define AOSS_DESC_UCORE_CH_STATE_ACK	0x18
39 #define AOSS_DESC_UCORE_MBOX_SIZE	0x1c
40 #define AOSS_DESC_UCORE_MBOX_OFFSET	0x20
41 #define AOSS_DESC_MCORE_LINK_STATE	0x24
42 #define AOSS_DESC_MCORE_LINK_STATE_ACK	0x28
43 #define AOSS_DESC_MCORE_CH_STATE	0x2c
44 #define AOSS_DESC_MCORE_CH_STATE_ACK	0x30
45 #define AOSS_DESC_MCORE_MBOX_SIZE	0x34
46 #define AOSS_DESC_MCORE_MBOX_OFFSET	0x38
47 
48 #define AOSS_MAGIC			0x4d41494c
49 #define AOSS_VERSION			1
50 
51 #define AOSS_STATE_UP			(0xffffU << 0)
52 #define AOSS_STATE_DOWN			(0xffffU << 16)
53 
54 #define AOSSREAD4(sc, reg)						\
55 	bus_space_read_4((sc)->sc_iot, (sc)->sc_aoss_ioh, (reg))
56 #define AOSSWRITE4(sc, reg, val)					\
57 	bus_space_write_4((sc)->sc_iot, (sc)->sc_aoss_ioh, (reg), (val))
58 
59 #define CPUCP_REG_CMD(i)	(0x104 + ((i) * 8))
60 #define CPUCP_MASK_CMD		0xffffffffffffffffULL
61 #define CPUCP_REG_RX_MAP	0x4000
62 #define CPUCP_REG_RX_STAT	0x4400
63 #define CPUCP_REG_RX_CLEAR	0x4800
64 #define CPUCP_REG_RX_EN		0x4C00
65 
66 #define RXREAD8(sc, reg)						\
67 	(bus_space_read_8((sc)->sc_iot, (sc)->sc_cpucp_rx_ioh, (reg)))
68 #define RXWRITE8(sc, reg, val)						\
69 	bus_space_write_8((sc)->sc_iot, (sc)->sc_cpucp_rx_ioh, (reg), (val))
70 
71 #define TXWRITE4(sc, reg, val)						\
72 	bus_space_write_4((sc)->sc_iot, (sc)->sc_cpucp_tx_ioh, (reg), (val))
73 
74 
75 struct qcpep_data {
76 	bus_addr_t		aoss_base;
77 	bus_size_t		aoss_size;
78 	uint32_t		aoss_client_id;
79 	uint32_t		aoss_signal_id;
80 	bus_addr_t		cpucp_rx_base;
81 	bus_size_t		cpucp_rx_size;
82 	bus_addr_t		cpucp_tx_base;
83 	bus_size_t		cpucp_tx_size;
84 	bus_addr_t		cpucp_shmem_base;
85 	bus_size_t		cpucp_shmem_size;
86 };
87 
88 struct qcpep_softc {
89 	device_t		sc_dev;
90 	bus_space_tag_t		sc_iot;
91 
92 	const struct qcpep_data	*sc_data;
93 
94 	bus_space_handle_t	sc_aoss_ioh;
95 	size_t			sc_aoss_offset;
96 	size_t			sc_aoss_size;
97 	void *			sc_aoss_ipcc;
98 
99 	bus_space_handle_t	sc_cpucp_rx_ioh;
100 	bus_space_handle_t	sc_cpucp_tx_ioh;
101 
102 	struct scmi_softc	sc_scmi;
103 };
104 
105 struct qcpep_softc *qcpep_sc;
106 
107 static const struct qcpep_data qcpep_x1e_data = {
108 	.aoss_base = 0x0c300000,
109 	.aoss_size = 0x400,
110 	.aoss_client_id = 0,	/* IPCC_CLIENT_AOP */
111 	.aoss_signal_id = 0,	/* IPCC_MPROC_SIGNAL_GLINK_QMP */
112 	.cpucp_rx_base = 0x17430000,
113 	.cpucp_rx_size = 0x10000,
114 	.cpucp_tx_base = 0x18830000,
115 	.cpucp_tx_size = 0x10000,
116 	.cpucp_shmem_base = 0x18b4e000,
117 	.cpucp_shmem_size = 0x400,
118 };
119 
120 static const struct device_compatible_entry compat_data[] = {
121 	{ .compat = "QCOM0C17",		.data = &qcpep_x1e_data },
122 	DEVICE_COMPAT_EOL
123 };
124 
125 static int	qcpep_match(device_t, cfdata_t, void *);
126 static void	qcpep_attach(device_t, device_t, void *);
127 
128 CFATTACH_DECL_NEW(qcompep, sizeof(struct qcpep_softc),
129     qcpep_match, qcpep_attach, NULL, NULL);
130 
131 static int
132 qcpep_match(device_t parent, cfdata_t match, void *aux)
133 {
134 	struct acpi_attach_args *aa = aux;
135 
136 	return acpi_compatible_match(aa, compat_data);
137 }
138 
139 static void
140 qcpep_attach(device_t parent, device_t self, void *aux)
141 {
142 	struct qcpep_softc *sc = device_private(self);
143 	struct acpi_attach_args *aa = aux;
144 	CPU_INFO_ITERATOR cii;
145 	struct cpu_info *ci;
146 	struct acpi_resources res;
147 	uint8_t *scmi_shmem;
148 	ACPI_STATUS rv;
149 	int i, last_pkg;;
150 
151         rv = acpi_resource_parse(self, aa->aa_node->ad_handle,
152 	    "_CRS", &res, &acpi_resource_parse_ops_default);
153         if (ACPI_FAILURE(rv)) {
154                 return;
155         }
156 	acpi_resource_cleanup(&res);
157 
158 	sc->sc_dev = self;
159 	sc->sc_iot = aa->aa_memt;
160 	sc->sc_data = acpi_compatible_lookup(aa, compat_data)->data;
161 
162 	if (bus_space_map(sc->sc_iot, sc->sc_data->aoss_base,
163 	    sc->sc_data->aoss_size, BUS_SPACE_MAP_NONPOSTED, &sc->sc_aoss_ioh)) {
164 		aprint_error_dev(self, "couldn't map aoss registers\n");
165 		return;
166 	}
167 	if (bus_space_map(sc->sc_iot, sc->sc_data->cpucp_rx_base,
168 	    sc->sc_data->cpucp_rx_size, BUS_SPACE_MAP_NONPOSTED,
169 	    &sc->sc_cpucp_rx_ioh)) {
170 		aprint_error_dev(self, "couldn't map cpucp rx registers\n");
171 		return;
172 	}
173 	if (bus_space_map(sc->sc_iot, sc->sc_data->cpucp_tx_base,
174 	    sc->sc_data->cpucp_tx_size, BUS_SPACE_MAP_NONPOSTED,
175 	    &sc->sc_cpucp_tx_ioh)) {
176 		aprint_error_dev(self, "couldn't map cpucp tx registers\n");
177 		return;
178 	}
179 
180 	sc->sc_aoss_ipcc = qcipcc_channel(sc->sc_data->aoss_client_id,
181 					  sc->sc_data->aoss_signal_id);
182 	if (sc->sc_aoss_ipcc == NULL) {
183 		aprint_error_dev(self, "couldn't find ipcc mailbox\n");
184 		return;
185 	}
186 
187 	if (AOSSREAD4(sc, AOSS_DESC_MAGIC) != AOSS_MAGIC ||
188 	    AOSSREAD4(sc, AOSS_DESC_VERSION) != AOSS_VERSION) {
189 		aprint_error_dev(self, "invalid QMP info\n");
190 		return;
191 	}
192 
193 	sc->sc_aoss_offset = AOSSREAD4(sc, AOSS_DESC_MCORE_MBOX_OFFSET);
194 	sc->sc_aoss_size = AOSSREAD4(sc, AOSS_DESC_MCORE_MBOX_SIZE);
195 	if (sc->sc_aoss_size == 0) {
196 		aprint_error_dev(self, "invalid AOSS mailbox size\n");
197 		return;
198 	}
199 
200 	AOSSWRITE4(sc, AOSS_DESC_UCORE_LINK_STATE_ACK,
201 	    AOSSREAD4(sc, AOSS_DESC_UCORE_LINK_STATE));
202 
203 	AOSSWRITE4(sc, AOSS_DESC_MCORE_LINK_STATE, AOSS_STATE_UP);
204 	qcipcc_send(sc->sc_aoss_ipcc);
205 
206 	for (i = 1000; i > 0; i--) {
207 		if (AOSSREAD4(sc, AOSS_DESC_MCORE_LINK_STATE_ACK) == AOSS_STATE_UP)
208 			break;
209 		delay(1000);
210 	}
211 	if (i == 0) {
212 		aprint_error_dev(self, "didn't get link state ack\n");
213 		return;
214 	}
215 
216 	AOSSWRITE4(sc, AOSS_DESC_MCORE_CH_STATE, AOSS_STATE_UP);
217 	qcipcc_send(sc->sc_aoss_ipcc);
218 
219 	for (i = 1000; i > 0; i--) {
220 		if (AOSSREAD4(sc, AOSS_DESC_UCORE_CH_STATE) == AOSS_STATE_UP)
221 			break;
222 		delay(1000);
223 	}
224 	if (i == 0) {
225 		aprint_error_dev(self, "didn't get open channel\n");
226 		return;
227 	}
228 
229 	AOSSWRITE4(sc, AOSS_DESC_UCORE_CH_STATE_ACK, AOSS_STATE_UP);
230 	qcipcc_send(sc->sc_aoss_ipcc);
231 
232 	for (i = 1000; i > 0; i--) {
233 		if (AOSSREAD4(sc, AOSS_DESC_MCORE_CH_STATE_ACK) == AOSS_STATE_UP)
234 			break;
235 		delay(1000);
236 	}
237 	if (i == 0) {
238 		aprint_error_dev(self, "didn't get channel ack\n");
239 		return;
240 	}
241 
242 	RXWRITE8(sc, CPUCP_REG_RX_EN, 0);
243 	RXWRITE8(sc, CPUCP_REG_RX_CLEAR, 0);
244 	RXWRITE8(sc, CPUCP_REG_RX_MAP, 0);
245 	RXWRITE8(sc, CPUCP_REG_RX_MAP, CPUCP_MASK_CMD);
246 
247 	qcpep_sc = sc;
248 
249 	/* SCMI setup */
250 	scmi_shmem = AcpiOsMapMemory(sc->sc_data->cpucp_shmem_base,
251 	    sc->sc_data->cpucp_shmem_size);
252 	if (scmi_shmem == NULL) {
253 		aprint_error_dev(self, "couldn't map SCMI shared memory\n");
254 		return;
255 	}
256 
257 	sc->sc_scmi.sc_dev = self;
258 	sc->sc_scmi.sc_iot = sc->sc_iot;
259 	sc->sc_scmi.sc_shmem_tx = (struct scmi_shmem *)(scmi_shmem + 0x000);
260 	sc->sc_scmi.sc_shmem_rx = (struct scmi_shmem *)(scmi_shmem + 0x200);
261 	sc->sc_scmi.sc_mbox_tx = qccpucp_channel(0);
262 	sc->sc_scmi.sc_mbox_tx_send = qccpucp_send;
263 	sc->sc_scmi.sc_mbox_rx = qccpucp_channel(2);
264 	sc->sc_scmi.sc_mbox_rx_send = qccpucp_send;
265 	/* Build performance domain to CPU map. */
266 	sc->sc_scmi.sc_perf_ndmap = 0;
267 	last_pkg = -1;
268 	for (CPU_INFO_FOREACH(cii, ci)) {
269 		if (ci->ci_package_id != last_pkg) {
270 			sc->sc_scmi.sc_perf_ndmap++;
271 			last_pkg = ci->ci_package_id;
272 		}
273 	}
274 	sc->sc_scmi.sc_perf_dmap = kmem_zalloc(
275 	    sizeof(*sc->sc_scmi.sc_perf_dmap) * sc->sc_scmi.sc_perf_ndmap,
276 	    KM_SLEEP);
277 	last_pkg = -1;
278 	i = 0;
279 	for (CPU_INFO_FOREACH(cii, ci)) {
280 		if (ci->ci_package_id != last_pkg) {
281 			sc->sc_scmi.sc_perf_dmap[i].pm_domain = i;
282 			sc->sc_scmi.sc_perf_dmap[i].pm_ci = ci;
283 			last_pkg = ci->ci_package_id;
284 			i++;
285 		}
286 	}
287 	if (scmi_init_mbox(&sc->sc_scmi) != 0) {
288 		aprint_error_dev(self, "couldn't setup SCMI\n");
289 		return;
290 	}
291 	scmi_attach_perf(&sc->sc_scmi);
292 }
293 
294 int
295 qcaoss_send(char *data, size_t len)
296 {
297 	struct qcpep_softc *sc = qcpep_sc;
298 	uint32_t reg;
299 	int i;
300 
301 	if (sc == NULL)
302 		return ENXIO;
303 
304 	if (data == NULL || sizeof(uint32_t) + len > sc->sc_aoss_size ||
305 	    (len % sizeof(uint32_t)) != 0)
306 		return EINVAL;
307 
308 	/* Write data first, needs to be 32-bit access. */
309 	for (i = 0; i < len; i += 4) {
310 		memcpy(&reg, data + i, sizeof(reg));
311 		AOSSWRITE4(sc, sc->sc_aoss_offset + sizeof(uint32_t) + i, reg);
312 	}
313 
314 	/* Commit transaction by writing length. */
315 	AOSSWRITE4(sc, sc->sc_aoss_offset, len);
316 
317 	/* Assert it's stored and inform peer. */
318 	if (AOSSREAD4(sc, sc->sc_aoss_offset) != len) {
319 		device_printf(sc->sc_dev,
320 		    "aoss message readback failed\n");
321 	}
322 	qcipcc_send(sc->sc_aoss_ipcc);
323 
324 	for (i = 1000; i > 0; i--) {
325 		if (AOSSREAD4(sc, sc->sc_aoss_offset) == 0)
326 			break;
327 		delay(1000);
328 	}
329 	if (i == 0) {
330 		device_printf(sc->sc_dev, "timeout sending message\n");
331 		AOSSWRITE4(sc, sc->sc_aoss_offset, 0);
332 		return ETIMEDOUT;
333 	}
334 
335 	return 0;
336 }
337 
338 void *
339 qccpucp_channel(u_int id)
340 {
341 	struct qcpep_softc *sc = qcpep_sc;
342 	uint64_t val;
343 
344 	if (sc == NULL || id > 2) {
345 		return NULL;
346 	}
347 
348 	val = RXREAD8(sc, CPUCP_REG_RX_EN);
349 	val |= (1 << id);
350 	RXWRITE8(sc, CPUCP_REG_RX_EN, val);
351 
352 	return (void *)(uintptr_t)(id + 1);
353 }
354 
355 int
356 qccpucp_send(void *cookie)
357 {
358 	struct qcpep_softc *sc = qcpep_sc;
359 	uintptr_t id = (uintptr_t)cookie - 1;
360 
361 	TXWRITE4(sc, CPUCP_REG_CMD(id), 0);
362 
363 	return 0;
364 }
365