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