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@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