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