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