Home | History | Annotate | Line # | Download | only in acpi
qcompep.c revision 1.1
      1 /* $NetBSD: qcompep.c,v 1.1 2024/12/30 12:31:10 jmcneill Exp $ */
      2 /*	$OpenBSD: qcaoss.c,v 1.1 2023/05/23 14:10:27 patrick Exp $	*/
      3 /*
      4  * Copyright (c) 2023 Patrick Wildt <patrick (at) blueri.se>
      5  *
      6  * Permission to use, copy, modify, and distribute this software for any
      7  * purpose with or without fee is hereby granted, provided that the above
      8  * copyright notice and this permission notice appear in all copies.
      9  *
     10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     17  */
     18 
     19 #include <sys/param.h>
     20 #include <sys/systm.h>
     21 #include <sys/device.h>
     22 #include <sys/kmem.h>
     23 
     24 #include <dev/acpi/acpivar.h>
     25 #include <dev/acpi/qcompep.h>
     26 #include <dev/acpi/qcomipcc.h>
     27 
     28 #define AOSS_DESC_MAGIC			0x0
     29 #define AOSS_DESC_VERSION		0x4
     30 #define AOSS_DESC_FEATURES		0x8
     31 #define AOSS_DESC_UCORE_LINK_STATE	0xc
     32 #define AOSS_DESC_UCORE_LINK_STATE_ACK	0x10
     33 #define AOSS_DESC_UCORE_CH_STATE	0x14
     34 #define AOSS_DESC_UCORE_CH_STATE_ACK	0x18
     35 #define AOSS_DESC_UCORE_MBOX_SIZE	0x1c
     36 #define AOSS_DESC_UCORE_MBOX_OFFSET	0x20
     37 #define AOSS_DESC_MCORE_LINK_STATE	0x24
     38 #define AOSS_DESC_MCORE_LINK_STATE_ACK	0x28
     39 #define AOSS_DESC_MCORE_CH_STATE	0x2c
     40 #define AOSS_DESC_MCORE_CH_STATE_ACK	0x30
     41 #define AOSS_DESC_MCORE_MBOX_SIZE	0x34
     42 #define AOSS_DESC_MCORE_MBOX_OFFSET	0x38
     43 
     44 #define AOSS_MAGIC			0x4d41494c
     45 #define AOSS_VERSION			1
     46 
     47 #define AOSS_STATE_UP			(0xffffU << 0)
     48 #define AOSS_STATE_DOWN			(0xffffU << 16)
     49 
     50 #define AOSSREAD4(sc, reg)						\
     51 	bus_space_read_4((sc)->sc_iot, (sc)->sc_aoss_ioh, (reg))
     52 #define AOSSWRITE4(sc, reg, val)					\
     53 	bus_space_write_4((sc)->sc_iot, (sc)->sc_aoss_ioh, (reg), (val))
     54 
     55 struct qcpep_data {
     56 	bus_addr_t		aoss_base;
     57 	bus_size_t		aoss_size;
     58 	uint32_t		aoss_client_id;
     59 	uint32_t		aoss_signal_id;
     60 };
     61 
     62 struct qcpep_softc {
     63 	device_t		sc_dev;
     64 	bus_space_tag_t		sc_iot;
     65 
     66 	const struct qcpep_data	*sc_data;
     67 
     68 	bus_space_handle_t	sc_aoss_ioh;
     69 	size_t			sc_aoss_offset;
     70 	size_t			sc_aoss_size;
     71 	void *			sc_aoss_ipcc;
     72 };
     73 
     74 struct qcpep_softc *qcpep_sc;
     75 
     76 static const struct qcpep_data qcpep_x1e_data = {
     77 	.aoss_base = 0x0c300000,
     78 	.aoss_size = 0x400,
     79 	.aoss_client_id = 0,	/* IPCC_CLIENT_AOP */
     80 	.aoss_signal_id = 0,	/* IPCC_MPROC_SIGNAL_GLINK_QMP */
     81 };
     82 
     83 static const struct device_compatible_entry compat_data[] = {
     84 	{ .compat = "QCOM0C17",		.data = &qcpep_x1e_data },
     85 	DEVICE_COMPAT_EOL
     86 };
     87 
     88 static int	qcpep_match(device_t, cfdata_t, void *);
     89 static void	qcpep_attach(device_t, device_t, void *);
     90 
     91 CFATTACH_DECL_NEW(qcompep, sizeof(struct qcpep_softc),
     92     qcpep_match, qcpep_attach, NULL, NULL);
     93 
     94 static int
     95 qcpep_match(device_t parent, cfdata_t match, void *aux)
     96 {
     97 	struct acpi_attach_args *aa = aux;
     98 
     99 	return acpi_compatible_match(aa, compat_data);
    100 }
    101 
    102 static void
    103 qcpep_attach(device_t parent, device_t self, void *aux)
    104 {
    105 	struct qcpep_softc *sc = device_private(self);
    106 	struct acpi_attach_args *aa = aux;
    107 	struct acpi_resources res;
    108 	ACPI_STATUS rv;
    109 	int i;
    110 
    111         rv = acpi_resource_parse(self, aa->aa_node->ad_handle,
    112 	    "_CRS", &res, &acpi_resource_parse_ops_default);
    113         if (ACPI_FAILURE(rv)) {
    114                 return;
    115         }
    116 	acpi_resource_cleanup(&res);
    117 
    118 	sc->sc_dev = self;
    119 	sc->sc_iot = aa->aa_memt;
    120 	sc->sc_data = acpi_compatible_lookup(aa, compat_data)->data;
    121 
    122 	if (bus_space_map(sc->sc_iot, sc->sc_data->aoss_base,
    123 	    sc->sc_data->aoss_size, BUS_SPACE_MAP_NONPOSTED, &sc->sc_aoss_ioh)) {
    124 		aprint_error_dev(self, "couldn't map registers\n");
    125 		return;
    126 	}
    127 
    128 	sc->sc_aoss_ipcc = qcipcc_channel(sc->sc_data->aoss_client_id,
    129 					  sc->sc_data->aoss_signal_id);
    130 	if (sc->sc_aoss_ipcc == NULL) {
    131 		aprint_error_dev(self, "couldn't find ipcc mailbox\n");
    132 		return;
    133 	}
    134 
    135 	if (AOSSREAD4(sc, AOSS_DESC_MAGIC) != AOSS_MAGIC ||
    136 	    AOSSREAD4(sc, AOSS_DESC_VERSION) != AOSS_VERSION) {
    137 		aprint_error_dev(self, "invalid QMP info\n");
    138 		return;
    139 	}
    140 
    141 	sc->sc_aoss_offset = AOSSREAD4(sc, AOSS_DESC_MCORE_MBOX_OFFSET);
    142 	sc->sc_aoss_size = AOSSREAD4(sc, AOSS_DESC_MCORE_MBOX_SIZE);
    143 	if (sc->sc_aoss_size == 0) {
    144 		aprint_error_dev(self, "invalid AOSS mailbox size\n");
    145 		return;
    146 	}
    147 
    148 	AOSSWRITE4(sc, AOSS_DESC_UCORE_LINK_STATE_ACK,
    149 	    AOSSREAD4(sc, AOSS_DESC_UCORE_LINK_STATE));
    150 
    151 	AOSSWRITE4(sc, AOSS_DESC_MCORE_LINK_STATE, AOSS_STATE_UP);
    152 	qcipcc_send(sc->sc_aoss_ipcc);
    153 
    154 	for (i = 1000; i > 0; i--) {
    155 		if (AOSSREAD4(sc, AOSS_DESC_MCORE_LINK_STATE_ACK) == AOSS_STATE_UP)
    156 			break;
    157 		delay(1000);
    158 	}
    159 	if (i == 0) {
    160 		aprint_error_dev(self, "didn't get link state ack\n");
    161 		return;
    162 	}
    163 
    164 	AOSSWRITE4(sc, AOSS_DESC_MCORE_CH_STATE, AOSS_STATE_UP);
    165 	qcipcc_send(sc->sc_aoss_ipcc);
    166 
    167 	for (i = 1000; i > 0; i--) {
    168 		if (AOSSREAD4(sc, AOSS_DESC_UCORE_CH_STATE) == AOSS_STATE_UP)
    169 			break;
    170 		delay(1000);
    171 	}
    172 	if (i == 0) {
    173 		aprint_error_dev(self, "didn't get open channel\n");
    174 		return;
    175 	}
    176 
    177 	AOSSWRITE4(sc, AOSS_DESC_UCORE_CH_STATE_ACK, AOSS_STATE_UP);
    178 	qcipcc_send(sc->sc_aoss_ipcc);
    179 
    180 	for (i = 1000; i > 0; i--) {
    181 		if (AOSSREAD4(sc, AOSS_DESC_MCORE_CH_STATE_ACK) == AOSS_STATE_UP)
    182 			break;
    183 		delay(1000);
    184 	}
    185 	if (i == 0) {
    186 		aprint_error_dev(self, "didn't get channel ack\n");
    187 		return;
    188 	}
    189 
    190 	qcpep_sc = sc;
    191 }
    192 
    193 int
    194 qcaoss_send(char *data, size_t len)
    195 {
    196 	struct qcpep_softc *sc = qcpep_sc;
    197 	uint32_t reg;
    198 	int i;
    199 
    200 	if (sc == NULL)
    201 		return ENXIO;
    202 
    203 	if (data == NULL || sizeof(uint32_t) + len > sc->sc_aoss_size ||
    204 	    (len % sizeof(uint32_t)) != 0)
    205 		return EINVAL;
    206 
    207 	/* Write data first, needs to be 32-bit access. */
    208 	for (i = 0; i < len; i += 4) {
    209 		memcpy(&reg, data + i, sizeof(reg));
    210 		AOSSWRITE4(sc, sc->sc_aoss_offset + sizeof(uint32_t) + i, reg);
    211 	}
    212 
    213 	/* Commit transaction by writing length. */
    214 	AOSSWRITE4(sc, sc->sc_aoss_offset, len);
    215 
    216 	/* Assert it's stored and inform peer. */
    217 	if (AOSSREAD4(sc, sc->sc_aoss_offset) != len) {
    218 		device_printf(sc->sc_dev,
    219 		    "aoss message readback failed\n");
    220 	}
    221 	qcipcc_send(sc->sc_aoss_ipcc);
    222 
    223 	for (i = 1000; i > 0; i--) {
    224 		if (AOSSREAD4(sc, sc->sc_aoss_offset) == 0)
    225 			break;
    226 		delay(1000);
    227 	}
    228 	if (i == 0) {
    229 		device_printf(sc->sc_dev, "timeout sending message\n");
    230 		AOSSWRITE4(sc, sc->sc_aoss_offset, 0);
    231 		return ETIMEDOUT;
    232 	}
    233 
    234 	return 0;
    235 }
    236