1 /* $OpenBSD: qcsmptp.c,v 1.1 2023/05/19 21:26:10 patrick Exp $ */ 2 /* 3 * Copyright (c) 2023 Patrick Wildt <patrick@blueri.se> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/param.h> 19 #include <sys/systm.h> 20 #include <sys/device.h> 21 #include <sys/malloc.h> 22 #include <sys/atomic.h> 23 24 #include <machine/bus.h> 25 #include <machine/fdt.h> 26 27 #include <dev/ofw/openfirm.h> 28 #include <dev/ofw/ofw_misc.h> 29 #include <dev/ofw/fdt.h> 30 31 #define SMP2P_MAX_ENTRY 16 32 #define SMP2P_MAX_ENTRY_NAME 16 33 34 struct qcsmptp_smem_item { 35 uint32_t magic; 36 #define SMP2P_MAGIC 0x504d5324 37 uint8_t version; 38 #define SMP2P_VERSION 1 39 unsigned features:24; 40 #define SMP2P_FEATURE_SSR_ACK (1 << 0) 41 uint16_t local_pid; 42 uint16_t remote_pid; 43 uint16_t total_entries; 44 uint16_t valid_entries; 45 uint32_t flags; 46 #define SMP2P_FLAGS_RESTART_DONE (1 << 0) 47 #define SMP2P_FLAGS_RESTART_ACK (1 << 1) 48 49 struct { 50 uint8_t name[SMP2P_MAX_ENTRY_NAME]; 51 uint32_t value; 52 } entries[SMP2P_MAX_ENTRY]; 53 } __packed; 54 55 struct qcsmptp_intrhand { 56 TAILQ_ENTRY(qcsmptp_intrhand) ih_q; 57 int (*ih_func)(void *); 58 void *ih_arg; 59 void *ih_ic; 60 int ih_pin; 61 int ih_enabled; 62 }; 63 64 struct qcsmptp_interrupt_controller { 65 TAILQ_HEAD(,qcsmptp_intrhand) ic_intrq; 66 struct qcsmptp_softc *ic_sc; 67 struct interrupt_controller ic_ic; 68 }; 69 70 struct qcsmptp_entry { 71 TAILQ_ENTRY(qcsmptp_entry) e_q; 72 char *e_name; 73 int e_namelen; 74 uint32_t *e_value; 75 uint32_t e_last_value; 76 struct qcsmptp_interrupt_controller *e_ic; 77 }; 78 79 struct qcsmptp_softc { 80 struct device sc_dev; 81 int sc_node; 82 void *sc_ih; 83 84 uint16_t sc_local_pid; 85 uint16_t sc_remote_pid; 86 uint32_t sc_smem_id[2]; 87 88 struct qcsmptp_smem_item *sc_in; 89 struct qcsmptp_smem_item *sc_out; 90 91 TAILQ_HEAD(,qcsmptp_entry) sc_inboundq; 92 TAILQ_HEAD(,qcsmptp_entry) sc_outboundq; 93 94 int sc_negotiated; 95 int sc_ssr_ack_enabled; 96 int sc_ssr_ack; 97 98 uint16_t sc_valid_entries; 99 100 struct mbox_channel *sc_mc; 101 }; 102 103 int qcsmptp_match(struct device *, void *, void *); 104 void qcsmptp_attach(struct device *, struct device *, void *); 105 void qcsmptp_deferred(struct device *); 106 107 const struct cfattach qcsmptp_ca = { 108 sizeof (struct qcsmptp_softc), qcsmptp_match, qcsmptp_attach 109 }; 110 111 struct cfdriver qcsmptp_cd = { 112 NULL, "qcsmptp", DV_DULL 113 }; 114 115 int qcsmptp_intr(void *); 116 void *qcsmptp_intr_establish(void *, int *, int, struct cpu_info *, 117 int (*)(void *), void *, char *); 118 void qcsmptp_intr_disestablish(void *); 119 void qcsmptp_intr_enable(void *); 120 void qcsmptp_intr_disable(void *); 121 void qcsmptp_intr_barrier(void *); 122 123 extern int qcsmem_alloc(int, int, int); 124 extern void *qcsmem_get(int, int, int *); 125 126 int 127 qcsmptp_match(struct device *parent, void *match, void *aux) 128 { 129 struct fdt_attach_args *faa = aux; 130 131 return OF_is_compatible(faa->fa_node, "qcom,smp2p"); 132 } 133 134 void 135 qcsmptp_attach(struct device *parent, struct device *self, void *aux) 136 { 137 struct qcsmptp_softc *sc = (struct qcsmptp_softc *)self; 138 struct fdt_attach_args *faa = aux; 139 140 sc->sc_node = faa->fa_node; 141 142 TAILQ_INIT(&sc->sc_inboundq); 143 TAILQ_INIT(&sc->sc_outboundq); 144 145 sc->sc_ih = fdt_intr_establish(faa->fa_node, IPL_BIO, 146 qcsmptp_intr, sc, sc->sc_dev.dv_xname); 147 if (sc->sc_ih == NULL) { 148 printf(": can't establish interrupt\n"); 149 return; 150 } 151 152 sc->sc_local_pid = OF_getpropint(faa->fa_node, 153 "qcom,local-pid", 0xffff); 154 sc->sc_remote_pid = OF_getpropint(faa->fa_node, 155 "qcom,remote-pid", 0xffff); 156 if (sc->sc_local_pid == 0xffff || sc->sc_remote_pid == 0xffff) { 157 printf(": can't get pids\n"); 158 return; 159 } 160 161 if (OF_getpropintarray(faa->fa_node, "qcom,smem", sc->sc_smem_id, 162 sizeof(sc->sc_smem_id)) != sizeof(sc->sc_smem_id)) { 163 printf(": can't get smem property \n"); 164 return; 165 } 166 167 printf("\n"); 168 169 config_defer(self, qcsmptp_deferred); 170 } 171 172 void 173 qcsmptp_deferred(struct device *self) 174 { 175 struct qcsmptp_softc *sc = (struct qcsmptp_softc *)self; 176 struct qcsmptp_entry *e; 177 int node, size; 178 179 sc->sc_mc = mbox_channel_idx(sc->sc_node, 0, NULL); 180 if (sc->sc_mc == NULL) { 181 printf(": can't get mailbox\n"); 182 return; 183 } 184 185 if (qcsmem_alloc(sc->sc_remote_pid, sc->sc_smem_id[1], 186 sizeof(*sc->sc_out)) != 0) { 187 printf(": can't alloc smp2p item\n"); 188 return; 189 } 190 191 sc->sc_out = qcsmem_get(sc->sc_remote_pid, sc->sc_smem_id[1], NULL); 192 if (sc->sc_out == NULL) { 193 printf(": can't get smp2p item\n"); 194 return; 195 } 196 197 memset(sc->sc_out, 0, sizeof(*sc->sc_out)); 198 sc->sc_out->magic = SMP2P_MAGIC; 199 sc->sc_out->local_pid = sc->sc_local_pid; 200 sc->sc_out->remote_pid = sc->sc_remote_pid; 201 sc->sc_out->total_entries = SMP2P_MAX_ENTRY; 202 sc->sc_out->features = SMP2P_FEATURE_SSR_ACK; 203 membar_sync(); 204 sc->sc_out->version = SMP2P_VERSION; 205 mbox_send(sc->sc_mc, NULL, 0); 206 207 for (node = OF_child(sc->sc_node); node; node = OF_peer(node)) { 208 size = OF_getproplen(node, "qcom,entry-name"); 209 if (size <= 0) 210 continue; 211 e = malloc(sizeof(*e), M_DEVBUF, M_WAITOK | M_ZERO); 212 e->e_namelen = size; 213 e->e_name = malloc(e->e_namelen, M_DEVBUF, M_WAITOK); 214 OF_getprop(node, "qcom,entry-name", e->e_name, 215 e->e_namelen); 216 e->e_name[e->e_namelen - 1] = '\0'; 217 218 if (OF_getproplen(node, "interrupt-controller") == 0) { 219 struct qcsmptp_interrupt_controller *ic; 220 ic = malloc(sizeof(*ic), M_DEVBUF, M_WAITOK | M_ZERO); 221 TAILQ_INIT(&ic->ic_intrq); 222 ic->ic_sc = sc; 223 ic->ic_ic.ic_node = node; 224 ic->ic_ic.ic_cookie = ic; 225 ic->ic_ic.ic_establish = qcsmptp_intr_establish; 226 ic->ic_ic.ic_disestablish = qcsmptp_intr_disestablish; 227 ic->ic_ic.ic_enable = qcsmptp_intr_enable; 228 ic->ic_ic.ic_disable = qcsmptp_intr_disable; 229 ic->ic_ic.ic_barrier = qcsmptp_intr_barrier; 230 fdt_intr_register(&ic->ic_ic); 231 e->e_ic = ic; 232 TAILQ_INSERT_TAIL(&sc->sc_inboundq, e, e_q); 233 } else { 234 if (sc->sc_out->valid_entries >= SMP2P_MAX_ENTRY) 235 continue; 236 237 strlcpy(sc->sc_out->entries[sc->sc_out->valid_entries].name, 238 e->e_name, SMP2P_MAX_ENTRY_NAME); 239 e->e_value = &sc->sc_out->entries[sc->sc_out->valid_entries].value; 240 sc->sc_out->valid_entries++; 241 TAILQ_INSERT_TAIL(&sc->sc_outboundq, e, e_q); 242 243 /* TODO: provide as smem state */ 244 } 245 } 246 } 247 248 int 249 qcsmptp_intr(void *arg) 250 { 251 struct qcsmptp_softc *sc = arg; 252 struct qcsmptp_entry *e; 253 struct qcsmptp_intrhand *ih; 254 uint32_t changed, val; 255 int do_ack = 0, i; 256 257 /* Inbound item exists as soon as remoteproc is up. */ 258 if (sc->sc_in == NULL) 259 sc->sc_in = qcsmem_get(sc->sc_remote_pid, 260 sc->sc_smem_id[0], NULL); 261 if (sc->sc_in == NULL) { 262 printf("%s: can't get smp2p item\n", sc->sc_dev.dv_xname); 263 return 1; 264 } 265 266 /* Do initial feature negotiation if inbound is new. */ 267 if (!sc->sc_negotiated) { 268 if (sc->sc_in->version != sc->sc_out->version) 269 return 1; 270 sc->sc_out->features &= sc->sc_in->features; 271 if (sc->sc_out->features & SMP2P_FEATURE_SSR_ACK) 272 sc->sc_ssr_ack_enabled = 1; 273 sc->sc_negotiated = 1; 274 } 275 if (!sc->sc_negotiated) 276 return 1; 277 278 /* Use ACK mechanism if negotiated. */ 279 if (sc->sc_ssr_ack_enabled && 280 !!(sc->sc_in->flags & SMP2P_FLAGS_RESTART_DONE) != sc->sc_ssr_ack) 281 do_ack = 1; 282 283 /* Catch up on new inbound entries that got added in the meantime. */ 284 for (i = sc->sc_valid_entries; i < sc->sc_in->valid_entries; i++) { 285 TAILQ_FOREACH(e, &sc->sc_inboundq, e_q) { 286 if (strncmp(sc->sc_in->entries[i].name, e->e_name, 287 SMP2P_MAX_ENTRY_NAME) != 0) 288 continue; 289 e->e_value = &sc->sc_in->entries[i].value; 290 } 291 } 292 sc->sc_valid_entries = i; 293 294 /* For each inbound "interrupt controller". */ 295 TAILQ_FOREACH(e, &sc->sc_inboundq, e_q) { 296 if (e->e_value == NULL) 297 continue; 298 val = *e->e_value; 299 if (val == e->e_last_value) 300 continue; 301 changed = val ^ e->e_last_value; 302 e->e_last_value = val; 303 TAILQ_FOREACH(ih, &e->e_ic->ic_intrq, ih_q) { 304 if (!ih->ih_enabled) 305 continue; 306 if ((changed & (1 << ih->ih_pin)) == 0) 307 continue; 308 ih->ih_func(ih->ih_arg); 309 } 310 } 311 312 if (do_ack) { 313 sc->sc_ssr_ack = !sc->sc_ssr_ack; 314 if (sc->sc_ssr_ack) 315 sc->sc_out->flags |= SMP2P_FLAGS_RESTART_ACK; 316 else 317 sc->sc_out->flags &= ~SMP2P_FLAGS_RESTART_ACK; 318 membar_sync(); 319 mbox_send(sc->sc_mc, NULL, 0); 320 } 321 322 return 1; 323 } 324 325 void * 326 qcsmptp_intr_establish(void *cookie, int *cells, int ipl, 327 struct cpu_info *ci, int (*func)(void *), void *arg, char *name) 328 { 329 struct qcsmptp_interrupt_controller *ic = cookie; 330 struct qcsmptp_softc *sc = ic->ic_sc; 331 struct qcsmptp_intrhand *ih; 332 333 ih = malloc(sizeof(*ih), M_DEVBUF, M_WAITOK | M_ZERO); 334 ih->ih_func = func; 335 ih->ih_arg = arg; 336 ih->ih_ic = ic; 337 ih->ih_pin = cells[0]; 338 TAILQ_INSERT_TAIL(&ic->ic_intrq, ih, ih_q); 339 340 qcsmptp_intr_enable(ih); 341 342 if (ipl & IPL_WAKEUP) 343 intr_set_wakeup(sc->sc_ih); 344 345 return ih; 346 } 347 348 void 349 qcsmptp_intr_disestablish(void *cookie) 350 { 351 struct qcsmptp_intrhand *ih = cookie; 352 struct qcsmptp_interrupt_controller *ic = ih->ih_ic; 353 354 qcsmptp_intr_disable(ih); 355 356 TAILQ_REMOVE(&ic->ic_intrq, ih, ih_q); 357 free(ih, M_DEVBUF, sizeof(*ih)); 358 } 359 360 void 361 qcsmptp_intr_enable(void *cookie) 362 { 363 struct qcsmptp_intrhand *ih = cookie; 364 365 ih->ih_enabled = 1; 366 } 367 368 void 369 qcsmptp_intr_disable(void *cookie) 370 { 371 struct qcsmptp_intrhand *ih = cookie; 372 373 ih->ih_enabled = 0; 374 } 375 376 void 377 qcsmptp_intr_barrier(void *cookie) 378 { 379 struct qcsmptp_intrhand *ih = cookie; 380 struct qcsmptp_interrupt_controller *ic = ih->ih_ic; 381 struct qcsmptp_softc *sc = ic->ic_sc; 382 383 intr_barrier(sc->sc_ih); 384 } 385