Home | History | Annotate | Line # | Download | only in acpi
      1 /* $NetBSD: qcomsmptp.c,v 1.1 2024/12/30 12:31:10 jmcneill Exp $ */
      2 /*	$OpenBSD: qcsmptp.c,v 1.2 2023/07/04 14:32:21 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/qcomipcc.h>
     26 #include <dev/acpi/qcomsmem.h>
     27 #include <dev/acpi/qcomsmptp.h>
     28 
     29 #define SMP2P_MAX_ENTRY		16
     30 #define SMP2P_MAX_ENTRY_NAME	16
     31 
     32 struct qcsmptp_smem_item {
     33 	uint32_t magic;
     34 #define SMP2P_MAGIC			0x504d5324
     35 	uint8_t version;
     36 #define SMP2P_VERSION			1
     37 	unsigned features:24;
     38 #define SMP2P_FEATURE_SSR_ACK		(1 << 0)
     39 	uint16_t local_pid;
     40 	uint16_t remote_pid;
     41 	uint16_t total_entries;
     42 	uint16_t valid_entries;
     43 	uint32_t flags;
     44 #define SMP2P_FLAGS_RESTART_DONE	(1 << 0)
     45 #define SMP2P_FLAGS_RESTART_ACK		(1 << 1)
     46 
     47 	struct {
     48 		uint8_t name[SMP2P_MAX_ENTRY_NAME];
     49 		uint32_t value;
     50 	} entries[SMP2P_MAX_ENTRY];
     51 } __packed;
     52 
     53 struct qcsmptp_intrhand {
     54 	TAILQ_ENTRY(qcsmptp_intrhand) ih_q;
     55 	int (*ih_func)(void *);
     56 	void *ih_arg;
     57 	void *ih_ic;
     58 	int ih_pin;
     59 	int ih_enabled;
     60 };
     61 
     62 struct qcsmptp_interrupt_controller {
     63 	TAILQ_HEAD(,qcsmptp_intrhand) ic_intrq;
     64 	struct qcsmptp_softc *ic_sc;
     65 };
     66 
     67 struct qcsmptp_entry {
     68 	TAILQ_ENTRY(qcsmptp_entry) e_q;
     69 	const char *e_name;
     70 	uint32_t *e_value;
     71 	uint32_t e_last_value;
     72 	struct qcsmptp_interrupt_controller *e_ic;
     73 };
     74 
     75 struct qcsmptp_softc {
     76 	device_t		sc_dev;
     77 	void			*sc_ih;
     78 
     79 	uint16_t		sc_local_pid;
     80 	uint16_t		sc_remote_pid;
     81 	uint32_t		sc_smem_id[2];
     82 
     83 	struct qcsmptp_smem_item *sc_in;
     84 	struct qcsmptp_smem_item *sc_out;
     85 
     86 	TAILQ_HEAD(,qcsmptp_entry) sc_inboundq;
     87 	TAILQ_HEAD(,qcsmptp_entry) sc_outboundq;
     88 
     89 	int			sc_negotiated;
     90 	int			sc_ssr_ack_enabled;
     91 	int			sc_ssr_ack;
     92 
     93 	uint16_t		sc_valid_entries;
     94 
     95 	void			*sc_ipcc;
     96 };
     97 
     98 static struct qcsmptp_interrupt_controller *qcsmptp_ic = NULL;
     99 
    100 static int	qcsmptp_match(device_t, cfdata_t, void *);
    101 static void	qcsmptp_attach(device_t, device_t, void *);
    102 
    103 static int	qcsmptp_intr(void *);
    104 
    105 CFATTACH_DECL_NEW(qcomsmptp, sizeof(struct qcsmptp_softc),
    106     qcsmptp_match, qcsmptp_attach, NULL, NULL);
    107 
    108 #define IPCC_CLIENT_LPASS	3
    109 #define IPCC_MPROC_SIGNAL_SMP2P	2
    110 
    111 #define QCSMPTP_X1E_LOCAL_PID	0
    112 #define QCSMPTP_X1E_REMOTE_PID	2
    113 #define QCSMPTP_X1E_SMEM_ID0	443
    114 #define QCSMPTP_X1E_SMEM_ID1	429
    115 
    116 static const struct device_compatible_entry compat_data[] = {
    117 	{ .compat = "QCOM0C5C" },
    118 	DEVICE_COMPAT_EOL
    119 };
    120 
    121 static int
    122 qcsmptp_match(device_t parent, cfdata_t match, void *aux)
    123 {
    124 	struct acpi_attach_args *aa = aux;
    125 
    126 	return acpi_compatible_match(aa, compat_data);
    127 }
    128 
    129 static void
    130 qcsmptp_attach(device_t parent, device_t self, void *aux)
    131 {
    132 	struct qcsmptp_softc *sc = device_private(self);
    133 	struct qcsmptp_interrupt_controller *ic;
    134 	struct qcsmptp_entry *e;
    135 
    136 	sc->sc_dev = self;
    137 
    138 	TAILQ_INIT(&sc->sc_inboundq);
    139 	TAILQ_INIT(&sc->sc_outboundq);
    140 
    141 	sc->sc_ih = qcipcc_intr_establish(IPCC_CLIENT_LPASS,
    142 	    IPCC_MPROC_SIGNAL_SMP2P, IPL_VM, qcsmptp_intr, sc);
    143 	if (sc->sc_ih == NULL) {
    144 		aprint_error(": can't establish interrupt\n");
    145 		return;
    146 	}
    147 
    148 	sc->sc_local_pid = QCSMPTP_X1E_LOCAL_PID;
    149 	sc->sc_remote_pid = QCSMPTP_X1E_REMOTE_PID;
    150 	sc->sc_smem_id[0] = QCSMPTP_X1E_SMEM_ID0;
    151 	sc->sc_smem_id[1] = QCSMPTP_X1E_SMEM_ID1;
    152 
    153 	aprint_naive("\n");
    154 	aprint_normal("\n");
    155 
    156 	sc->sc_ipcc = qcipcc_channel(IPCC_CLIENT_LPASS,
    157 	    IPCC_MPROC_SIGNAL_SMP2P);
    158 	if (sc->sc_ipcc == NULL) {
    159 		aprint_error_dev(self, "can't get mailbox\n");
    160 		return;
    161 	}
    162 
    163 	if (qcsmem_alloc(sc->sc_remote_pid, sc->sc_smem_id[0],
    164 	    sizeof(*sc->sc_in)) != 0) {
    165 		aprint_error_dev(self, "can't alloc smp2p item\n");
    166 		return;
    167 	}
    168 
    169 	sc->sc_in = qcsmem_get(sc->sc_remote_pid, sc->sc_smem_id[0], NULL);
    170 	if (sc->sc_in == NULL) {
    171 		aprint_error_dev(self, "can't get smp2p item\n");
    172 		return;
    173 	}
    174 
    175 	if (qcsmem_alloc(sc->sc_remote_pid, sc->sc_smem_id[1],
    176 	    sizeof(*sc->sc_out)) != 0) {
    177 		aprint_error_dev(self, "can't alloc smp2p item\n");
    178 		return;
    179 	}
    180 
    181 	sc->sc_out = qcsmem_get(sc->sc_remote_pid, sc->sc_smem_id[1], NULL);
    182 	if (sc->sc_out == NULL) {
    183 		aprint_error_dev(self, "can't get smp2p item\n");
    184 		return;
    185 	}
    186 
    187 	qcsmem_memset(sc->sc_out, 0, sizeof(*sc->sc_out));
    188 	sc->sc_out->magic = SMP2P_MAGIC;
    189 	sc->sc_out->local_pid = sc->sc_local_pid;
    190 	sc->sc_out->remote_pid = sc->sc_remote_pid;
    191 	sc->sc_out->total_entries = SMP2P_MAX_ENTRY;
    192 	sc->sc_out->features = SMP2P_FEATURE_SSR_ACK;
    193 	membar_sync();
    194 	sc->sc_out->version = SMP2P_VERSION;
    195 	qcipcc_send(sc->sc_ipcc);
    196 
    197 	e = kmem_zalloc(sizeof(*e), KM_SLEEP);
    198 	e->e_name = "master-kernel";
    199 	e->e_value = &sc->sc_out->entries[sc->sc_out->valid_entries].value;
    200 	sc->sc_out->valid_entries++;
    201 	TAILQ_INSERT_TAIL(&sc->sc_outboundq, e, e_q);
    202 	/* TODO: provide as smem state */
    203 
    204 	e = kmem_zalloc(sizeof(*e), KM_SLEEP);
    205 	e->e_name = "slave-kernel";
    206 	ic = kmem_zalloc(sizeof(*ic), KM_SLEEP);
    207 	TAILQ_INIT(&ic->ic_intrq);
    208 	ic->ic_sc = sc;
    209 	e->e_ic = ic;
    210 	TAILQ_INSERT_TAIL(&sc->sc_inboundq, e, e_q);
    211 
    212 	qcsmptp_ic = ic;
    213 }
    214 
    215 static int
    216 qcsmptp_intr(void *arg)
    217 {
    218 	struct qcsmptp_softc *sc = arg;
    219 	struct qcsmptp_entry *e;
    220 	struct qcsmptp_intrhand *ih;
    221 	uint32_t changed, val;
    222 	int do_ack = 0, i;
    223 
    224 	/* Do initial feature negotiation if inbound is new. */
    225 	if (!sc->sc_negotiated) {
    226 		if (sc->sc_in->version != sc->sc_out->version)
    227 			return 1;
    228 		sc->sc_out->features &= sc->sc_in->features;
    229 		if (sc->sc_out->features & SMP2P_FEATURE_SSR_ACK)
    230 			sc->sc_ssr_ack_enabled = 1;
    231 		sc->sc_negotiated = 1;
    232 	}
    233 	if (!sc->sc_negotiated) {
    234 		return 1;
    235 	}
    236 
    237 	/* Use ACK mechanism if negotiated. */
    238 	if (sc->sc_ssr_ack_enabled &&
    239 	    !!(sc->sc_in->flags & SMP2P_FLAGS_RESTART_DONE) != sc->sc_ssr_ack)
    240 		do_ack = 1;
    241 
    242 	/* Catch up on new inbound entries that got added in the meantime. */
    243 	for (i = sc->sc_valid_entries; i < sc->sc_in->valid_entries; i++) {
    244 		TAILQ_FOREACH(e, &sc->sc_inboundq, e_q) {
    245 			if (strncmp(sc->sc_in->entries[i].name, e->e_name,
    246 			    SMP2P_MAX_ENTRY_NAME) != 0)
    247 				continue;
    248 			e->e_value = &sc->sc_in->entries[i].value;
    249 		}
    250 	}
    251 	sc->sc_valid_entries = i;
    252 
    253 	/* For each inbound "interrupt controller". */
    254 	TAILQ_FOREACH(e, &sc->sc_inboundq, e_q) {
    255 		if (e->e_value == NULL)
    256 			continue;
    257 		val = *e->e_value;
    258 		if (val == e->e_last_value)
    259 			continue;
    260 		changed = val ^ e->e_last_value;
    261 		e->e_last_value = val;
    262 		TAILQ_FOREACH(ih, &e->e_ic->ic_intrq, ih_q) {
    263 			if (!ih->ih_enabled)
    264 				continue;
    265 			if ((changed & (1 << ih->ih_pin)) == 0)
    266 				continue;
    267 			ih->ih_func(ih->ih_arg);
    268 		}
    269 	}
    270 
    271 	if (do_ack) {
    272 		sc->sc_ssr_ack = !sc->sc_ssr_ack;
    273 		if (sc->sc_ssr_ack)
    274 			sc->sc_out->flags |= SMP2P_FLAGS_RESTART_ACK;
    275 		else
    276 			sc->sc_out->flags &= ~SMP2P_FLAGS_RESTART_ACK;
    277 		membar_sync();
    278 		qcipcc_send(sc->sc_ipcc);
    279 	}
    280 
    281 	return 1;
    282 }
    283 
    284 void *
    285 qcsmptp_intr_establish(u_int pin, int (*func)(void *), void *arg)
    286 {
    287 	struct qcsmptp_interrupt_controller *ic = qcsmptp_ic;
    288 	struct qcsmptp_intrhand *ih;
    289 
    290 	if (ic == NULL) {
    291 		return NULL;
    292 	}
    293 
    294 	ih = kmem_zalloc(sizeof(*ih), KM_SLEEP);
    295 	ih->ih_func = func;
    296 	ih->ih_arg = arg;
    297 	ih->ih_ic = ic;
    298 	ih->ih_pin = pin;
    299 	TAILQ_INSERT_TAIL(&ic->ic_intrq, ih, ih_q);
    300 
    301 	qcsmptp_intr_enable(ih);
    302 
    303 	return ih;
    304 }
    305 
    306 void
    307 qcsmptp_intr_disestablish(void *cookie)
    308 {
    309 	struct qcsmptp_intrhand *ih = cookie;
    310 	struct qcsmptp_interrupt_controller *ic = ih->ih_ic;
    311 
    312 	qcsmptp_intr_disable(ih);
    313 
    314 	TAILQ_REMOVE(&ic->ic_intrq, ih, ih_q);
    315 	kmem_free(ih, sizeof(*ih));
    316 }
    317 
    318 void
    319 qcsmptp_intr_enable(void *cookie)
    320 {
    321 	struct qcsmptp_intrhand *ih = cookie;
    322 
    323 	ih->ih_enabled = 1;
    324 }
    325 
    326 void
    327 qcsmptp_intr_disable(void *cookie)
    328 {
    329 	struct qcsmptp_intrhand *ih = cookie;
    330 
    331 	ih->ih_enabled = 0;
    332 }
    333