1/* $OpenBSD: qcpas.c,v 1.2 2023/07/01 15:50:18 drahn 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#include <sys/exec_elf.h> 24#include <sys/task.h> 25 26#include <machine/apmvar.h> 27#include <machine/bus.h> 28#include <machine/fdt.h> 29#include <uvm/uvm_extern.h> 30 31#include <dev/ofw/openfirm.h> 32#include <dev/ofw/ofw_clock.h> 33#include <dev/ofw/ofw_misc.h> 34#include <dev/ofw/ofw_power.h> 35#include <dev/ofw/fdt.h> 36 37#include "apm.h" 38 39#define MDT_TYPE_MASK (7 << 24) 40#define MDT_TYPE_HASH (2 << 24) 41#define MDT_RELOCATABLE (1 << 27) 42 43#define HREAD4(sc, reg) \ 44 (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))) 45#define HWRITE4(sc, reg, val) \ 46 bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) 47 48struct qcpas_dmamem { 49 bus_dmamap_t tdm_map; 50 bus_dma_segment_t tdm_seg; 51 size_t tdm_size; 52 caddr_t tdm_kva; 53}; 54#define QCPAS_DMA_MAP(_tdm) ((_tdm)->tdm_map) 55#define QCPAS_DMA_LEN(_tdm) ((_tdm)->tdm_size) 56#define QCPAS_DMA_DVA(_tdm) ((_tdm)->tdm_map->dm_segs[0].ds_addr) 57#define QCPAS_DMA_KVA(_tdm) ((void *)(_tdm)->tdm_kva) 58 59struct qcpas_softc { 60 struct device sc_dev; 61 bus_space_tag_t sc_iot; 62 bus_space_handle_t sc_ioh; 63 bus_dma_tag_t sc_dmat; 64 int sc_node; 65 66 void *sc_ih[6]; 67 68 paddr_t sc_mem_phys; 69 size_t sc_mem_size; 70 void *sc_mem_region; 71 vaddr_t sc_mem_reloc; 72 73 uint32_t sc_pas_id; 74 char *sc_load_state; 75 76 struct qcpas_dmamem *sc_metadata; 77 78 /* GLINK */ 79 volatile uint32_t *sc_tx_tail; 80 volatile uint32_t *sc_tx_head; 81 volatile uint32_t *sc_rx_tail; 82 volatile uint32_t *sc_rx_head; 83 84 uint32_t sc_tx_off; 85 uint32_t sc_rx_off; 86 87 uint8_t *sc_tx_fifo; 88 int sc_tx_fifolen; 89 uint8_t *sc_rx_fifo; 90 int sc_rx_fifolen; 91 void *sc_glink_ih; 92 93 struct mbox_channel *sc_mc; 94 95 struct task sc_glink_rx; 96 uint32_t sc_glink_max_channel; 97 TAILQ_HEAD(,qcpas_glink_channel) sc_glink_channels; 98}; 99 100int qcpas_match(struct device *, void *, void *); 101void qcpas_attach(struct device *, struct device *, void *); 102 103const struct cfattach qcpas_ca = { 104 sizeof (struct qcpas_softc), qcpas_match, qcpas_attach 105}; 106 107struct cfdriver qcpas_cd = { 108 NULL, "qcpas", DV_DULL 109}; 110 111void qcpas_mountroot(struct device *); 112int qcpas_map_memory(struct qcpas_softc *); 113int qcpas_mdt_init(struct qcpas_softc *, u_char *, size_t); 114void qcpas_glink_attach(struct qcpas_softc *, int); 115 116struct qcpas_dmamem * 117 qcpas_dmamem_alloc(struct qcpas_softc *, bus_size_t, bus_size_t); 118void qcpas_dmamem_free(struct qcpas_softc *, struct qcpas_dmamem *); 119 120void qcpas_intr_establish(struct qcpas_softc *, int, char *, void *); 121int qcpas_intr_wdog(void *); 122int qcpas_intr_fatal(void *); 123int qcpas_intr_ready(void *); 124int qcpas_intr_handover(void *); 125int qcpas_intr_stop_ack(void *); 126int qcpas_intr_shutdown_ack(void *); 127 128int 129qcpas_match(struct device *parent, void *match, void *aux) 130{ 131 struct fdt_attach_args *faa = aux; 132 133 return OF_is_compatible(faa->fa_node, "qcom,sc8280xp-adsp-pas"); 134} 135 136void 137qcpas_attach(struct device *parent, struct device *self, void *aux) 138{ 139 struct qcpas_softc *sc = (struct qcpas_softc *)self; 140 struct fdt_attach_args *faa = aux; 141 142 if (faa->fa_nreg < 1) { 143 printf(": no registers\n"); 144 return; 145 } 146 147 sc->sc_iot = faa->fa_iot; 148 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, 149 faa->fa_reg[0].size, 0, &sc->sc_ioh)) { 150 printf(": can't map registers\n"); 151 return; 152 } 153 sc->sc_dmat = faa->fa_dmat; 154 sc->sc_node = faa->fa_node; 155 156 if (OF_is_compatible(faa->fa_node, "qcom,sc8280xp-adsp-pas")) { 157 sc->sc_pas_id = 1; 158 sc->sc_load_state = "adsp"; 159 } 160 if (OF_is_compatible(faa->fa_node, "qcom,sc8280xp-nsp0-pas")) { 161 sc->sc_pas_id = 18; 162 } 163 if (OF_is_compatible(faa->fa_node, "qcom,sc8280xp-nsp1-pas")) { 164 sc->sc_pas_id = 30; 165 } 166 167 qcpas_intr_establish(sc, 0, "wdog", qcpas_intr_wdog); 168 qcpas_intr_establish(sc, 1, "fatal", qcpas_intr_fatal); 169 qcpas_intr_establish(sc, 2, "ready", qcpas_intr_ready); 170 qcpas_intr_establish(sc, 3, "handover", qcpas_intr_handover); 171 qcpas_intr_establish(sc, 4, "stop-ack", qcpas_intr_stop_ack); 172 qcpas_intr_establish(sc, 5, "shutdown-ack", qcpas_intr_shutdown_ack); 173 174 printf("\n"); 175 176 config_mountroot(self, qcpas_mountroot); 177} 178 179extern int qcaoss_send(char *, size_t); 180 181void 182qcpas_mountroot(struct device *self) 183{ 184 struct qcpas_softc *sc = (struct qcpas_softc *)self; 185 char fwname[64]; 186 size_t fwlen; 187 u_char *fw; 188 int node, ret; 189 190 if (qcpas_map_memory(sc) != 0) 191 return; 192 193 if (OF_getproplen(sc->sc_node, "firmware-name") <= 0) 194 return; 195 OF_getprop(sc->sc_node, "firmware-name", fwname, sizeof(fwname)); 196 fwname[sizeof(fwname) - 1] = '\0'; 197 198 if (loadfirmware(fwname, &fw, &fwlen) != 0) { 199 printf("%s: failed to load %s\n", 200 sc->sc_dev.dv_xname, fwname); 201 return; 202 } 203 204 if (sc->sc_load_state) { 205 char buf[64]; 206 snprintf(buf, sizeof(buf), 207 "{class: image, res: load_state, name: %s, val: on}", 208 sc->sc_load_state); 209 ret = qcaoss_send(buf, sizeof(buf)); 210 if (ret != 0) { 211 printf("%s: failed to toggle load state\n", 212 sc->sc_dev.dv_xname); 213 return; 214 } 215 } 216 217 power_domain_enable_all(sc->sc_node); 218 clock_enable(sc->sc_node, "xo"); 219 220 ret = qcpas_mdt_init(sc, fw, fwlen); 221 free(fw, M_DEVBUF, fwlen); 222 if (ret != 0) { 223 printf("%s: failed to boot coprocessor\n", 224 sc->sc_dev.dv_xname); 225 return; 226 } 227 228 node = OF_getnodebyname(sc->sc_node, "glink-edge"); 229 if (node) 230 qcpas_glink_attach(sc, node); 231} 232 233int 234qcpas_map_memory(struct qcpas_softc *sc) 235{ 236 uint32_t phandle, reg[4]; 237 size_t off; 238 int node; 239 240 phandle = OF_getpropint(sc->sc_node, "memory-region", 0); 241 if (phandle == 0) 242 return EINVAL; 243 node = OF_getnodebyphandle(phandle); 244 if (node == 0) 245 return EINVAL; 246 if (OF_getpropintarray(node, "reg", reg, sizeof(reg)) != sizeof(reg)) 247 return EINVAL; 248 249 sc->sc_mem_phys = (uint64_t)reg[0] << 32 | reg[1]; 250 KASSERT((sc->sc_mem_phys & PAGE_MASK) == 0); 251 sc->sc_mem_size = (uint64_t)reg[2] << 32 | reg[3]; 252 KASSERT((sc->sc_mem_size & PAGE_MASK) == 0); 253 254 sc->sc_mem_region = km_alloc(sc->sc_mem_size, &kv_any, &kp_none, 255 &kd_nowait); 256 if (!sc->sc_mem_region) 257 return ENOMEM; 258 259 for (off = 0; off < sc->sc_mem_size; off += PAGE_SIZE) { 260 pmap_kenter_cache((vaddr_t)sc->sc_mem_region + off, 261 sc->sc_mem_phys + off, PROT_READ | PROT_WRITE, 262 PMAP_CACHE_DEV_NGNRNE); 263 } 264 265 return 0; 266} 267 268extern int qcscm_pas_init_image(uint32_t, paddr_t); 269extern int qcscm_pas_mem_setup(uint32_t, paddr_t, size_t); 270extern int qcscm_pas_auth_and_reset(uint32_t); 271 272int 273qcpas_mdt_init(struct qcpas_softc *sc, u_char *fw, size_t fwlen) 274{ 275 Elf32_Ehdr *ehdr; 276 Elf32_Phdr *phdr; 277 paddr_t minpa = -1, maxpa = 0; 278 int i, hashseg = 0, relocate = 0; 279 int error; 280 ssize_t off; 281 282 ehdr = (Elf32_Ehdr *)fw; 283 phdr = (Elf32_Phdr *)&ehdr[1]; 284 285 if (ehdr->e_phnum < 2 || phdr[0].p_type == PT_LOAD) 286 return EINVAL; 287 288 for (i = 0; i < ehdr->e_phnum; i++) { 289 if ((phdr[i].p_flags & MDT_TYPE_MASK) == MDT_TYPE_HASH) { 290 if (i > 0 && !hashseg) 291 hashseg = i; 292 continue; 293 } 294 if (phdr[i].p_type != PT_LOAD || phdr[i].p_memsz == 0) 295 continue; 296 if (phdr[i].p_flags & MDT_RELOCATABLE) 297 relocate = 1; 298 if (phdr[i].p_paddr < minpa) 299 minpa = phdr[i].p_paddr; 300 if (phdr[i].p_paddr + phdr[i].p_memsz > maxpa) 301 maxpa = 302 roundup(phdr[i].p_paddr + phdr[i].p_memsz, 303 PAGE_SIZE); 304 } 305 306 if (!hashseg) 307 return EINVAL; 308 309 sc->sc_metadata = qcpas_dmamem_alloc(sc, phdr[0].p_filesz + 310 phdr[hashseg].p_filesz, PAGE_SIZE); 311 if (sc->sc_metadata == NULL) 312 return EINVAL; 313 314 memcpy(QCPAS_DMA_KVA(sc->sc_metadata), fw, phdr[0].p_filesz); 315 if (phdr[0].p_filesz + phdr[hashseg].p_filesz == fwlen) { 316 memcpy(QCPAS_DMA_KVA(sc->sc_metadata) + phdr[0].p_filesz, 317 fw + phdr[0].p_filesz, phdr[hashseg].p_filesz); 318 } else if (phdr[hashseg].p_offset + phdr[hashseg].p_filesz <= fwlen) { 319 memcpy(QCPAS_DMA_KVA(sc->sc_metadata) + phdr[0].p_filesz, 320 fw + phdr[hashseg].p_offset, phdr[hashseg].p_filesz); 321 } else { 322 printf("%s: metadata split segment not supported\n", 323 sc->sc_dev.dv_xname); 324 return EINVAL; 325 } 326 327 membar_producer(); 328 329 if (qcscm_pas_init_image(sc->sc_pas_id, 330 QCPAS_DMA_DVA(sc->sc_metadata)) != 0) { 331 printf("%s: init image failed\n", sc->sc_dev.dv_xname); 332 qcpas_dmamem_free(sc, sc->sc_metadata); 333 return EINVAL; 334 } 335 336 if (qcscm_pas_mem_setup(sc->sc_pas_id, 337 sc->sc_mem_phys, maxpa - minpa) != 0) { 338 printf("%s: mem setup failed\n", sc->sc_dev.dv_xname); 339 qcpas_dmamem_free(sc, sc->sc_metadata); 340 return EINVAL; 341 } 342 343 sc->sc_mem_reloc = relocate ? minpa : sc->sc_mem_phys; 344 345 for (i = 0; i < ehdr->e_phnum; i++) { 346 if ((phdr[i].p_flags & MDT_TYPE_MASK) == MDT_TYPE_HASH || 347 phdr[i].p_type != PT_LOAD || phdr[i].p_memsz == 0) 348 continue; 349 off = phdr[i].p_paddr - sc->sc_mem_reloc; 350 if (off < 0 || off + phdr[i].p_memsz > sc->sc_mem_size) 351 return EINVAL; 352 if (phdr[i].p_filesz > phdr[i].p_memsz) 353 return EINVAL; 354 355 if (phdr[i].p_filesz && phdr[i].p_offset < fwlen && 356 phdr[i].p_offset + phdr[i].p_filesz <= fwlen) { 357 memcpy(sc->sc_mem_region + off, fw + phdr[i].p_offset, 358 phdr[i].p_filesz); 359 } else if (phdr[i].p_filesz) { 360 printf("%s: firmware split segment not supported\n", 361 sc->sc_dev.dv_xname); 362 return EINVAL; 363 } 364 365 if (phdr[i].p_memsz > phdr[i].p_filesz) 366 memset(sc->sc_mem_region + off + phdr[i].p_filesz, 0, 367 phdr[i].p_memsz - phdr[i].p_filesz); 368 } 369 370 membar_producer(); 371 372 if (qcscm_pas_auth_and_reset(sc->sc_pas_id) != 0) { 373 printf("%s: auth and reset failed\n", sc->sc_dev.dv_xname); 374 qcpas_dmamem_free(sc, sc->sc_metadata); 375 return EINVAL; 376 } 377 378 error = tsleep_nsec(sc, PWAIT, "qcpas", SEC_TO_NSEC(5)); 379 if (error) { 380 printf("%s: failed to receive ready signal\n", 381 sc->sc_dev.dv_xname); 382 return error; 383 } 384 385 /* XXX: free metadata ? */ 386 387 return 0; 388} 389 390struct qcpas_dmamem * 391qcpas_dmamem_alloc(struct qcpas_softc *sc, bus_size_t size, bus_size_t align) 392{ 393 struct qcpas_dmamem *tdm; 394 int nsegs; 395 396 tdm = malloc(sizeof(*tdm), M_DEVBUF, M_WAITOK | M_ZERO); 397 tdm->tdm_size = size; 398 399 if (bus_dmamap_create(sc->sc_dmat, size, 1, size, 0, 400 BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW, &tdm->tdm_map) != 0) 401 goto tdmfree; 402 403 if (bus_dmamem_alloc_range(sc->sc_dmat, size, align, 0, 404 &tdm->tdm_seg, 1, &nsegs, BUS_DMA_WAITOK, 0, 0xffffffff) != 0) 405 goto destroy; 406 407 if (bus_dmamem_map(sc->sc_dmat, &tdm->tdm_seg, nsegs, size, 408 &tdm->tdm_kva, BUS_DMA_WAITOK | BUS_DMA_COHERENT) != 0) 409 goto free; 410 411 if (bus_dmamap_load(sc->sc_dmat, tdm->tdm_map, tdm->tdm_kva, size, 412 NULL, BUS_DMA_WAITOK) != 0) 413 goto unmap; 414 415 bzero(tdm->tdm_kva, size); 416 417 return (tdm); 418 419unmap: 420 bus_dmamem_unmap(sc->sc_dmat, tdm->tdm_kva, size); 421free: 422 bus_dmamem_free(sc->sc_dmat, &tdm->tdm_seg, 1); 423destroy: 424 bus_dmamap_destroy(sc->sc_dmat, tdm->tdm_map); 425tdmfree: 426 free(tdm, M_DEVBUF, 0); 427 428 return (NULL); 429} 430 431void 432qcpas_dmamem_free(struct qcpas_softc *sc, struct qcpas_dmamem *tdm) 433{ 434 bus_dmamem_unmap(sc->sc_dmat, tdm->tdm_kva, tdm->tdm_size); 435 bus_dmamem_free(sc->sc_dmat, &tdm->tdm_seg, 1); 436 bus_dmamap_destroy(sc->sc_dmat, tdm->tdm_map); 437 free(tdm, M_DEVBUF, 0); 438} 439 440void 441qcpas_intr_establish(struct qcpas_softc *sc, int i, char *name, void *handler) 442{ 443 int idx; 444 445 idx = OF_getindex(sc->sc_node, name, "interrupt-names"); 446 if (idx >= 0) 447 sc->sc_ih[i] = 448 fdt_intr_establish_idx(sc->sc_node, idx, IPL_BIO, 449 handler, sc, sc->sc_dev.dv_xname); 450} 451 452int 453qcpas_intr_wdog(void *cookie) 454{ 455 return 0; 456} 457 458int 459qcpas_intr_fatal(void *cookie) 460{ 461 return 0; 462} 463 464int 465qcpas_intr_ready(void *cookie) 466{ 467 struct qcpas_softc *sc = cookie; 468 469 wakeup(sc); 470 return 0; 471} 472 473int 474qcpas_intr_handover(void *cookie) 475{ 476 return 0; 477} 478 479int 480qcpas_intr_stop_ack(void *cookie) 481{ 482 return 0; 483} 484 485int 486qcpas_intr_shutdown_ack(void *cookie) 487{ 488 return 0; 489} 490 491/* GLINK */ 492 493#define SMEM_GLINK_NATIVE_XPRT_DESCRIPTOR 478 494#define SMEM_GLINK_NATIVE_XPRT_FIFO_0 479 495#define SMEM_GLINK_NATIVE_XPRT_FIFO_1 480 496 497struct glink_msg { 498 uint16_t cmd; 499 uint16_t param1; 500 uint32_t param2; 501 uint8_t data[]; 502} __packed; 503 504struct qcpas_glink_intent_pair { 505 uint32_t size; 506 uint32_t iid; 507} __packed; 508 509struct qcpas_glink_intent { 510 TAILQ_ENTRY(qcpas_glink_intent) it_q; 511 uint32_t it_id; 512 uint32_t it_size; 513 int it_inuse; 514}; 515 516struct qcpas_glink_channel { 517 TAILQ_ENTRY(qcpas_glink_channel) ch_q; 518 struct qcpas_softc *ch_sc; 519 struct qcpas_glink_protocol *ch_proto; 520 uint32_t ch_rcid; 521 uint32_t ch_lcid; 522 uint32_t ch_max_intent; 523 TAILQ_HEAD(,qcpas_glink_intent) ch_l_intents; 524 TAILQ_HEAD(,qcpas_glink_intent) ch_r_intents; 525}; 526 527#define GLINK_CMD_VERSION 0 528#define GLINK_CMD_VERSION_ACK 1 529#define GLINK_VERSION 1 530#define GLINK_FEATURE_INTENT_REUSE (1 << 0) 531#define GLINK_CMD_OPEN 2 532#define GLINK_CMD_CLOSE 3 533#define GLINK_CMD_OPEN_ACK 4 534#define GLINK_CMD_INTENT 5 535#define GLINK_CMD_RX_DONE 6 536#define GLINK_CMD_RX_INTENT_REQ 7 537#define GLINK_CMD_RX_INTENT_REQ_ACK 8 538#define GLINK_CMD_TX_DATA 9 539#define GLINK_CMD_CLOSE_ACK 11 540#define GLINK_CMD_TX_DATA_CONT 12 541#define GLINK_CMD_READ_NOTIF 13 542#define GLINK_CMD_RX_DONE_W_REUSE 14 543 544void qcpas_glink_recv(void *); 545int qcpas_glink_intr(void *); 546 547void qcpas_glink_tx(struct qcpas_softc *, uint8_t *, int); 548void qcpas_glink_tx_commit(struct qcpas_softc *); 549void qcpas_glink_rx(struct qcpas_softc *, uint8_t *, int); 550void qcpas_glink_rx_commit(struct qcpas_softc *); 551 552void qcpas_glink_send(void *, void *, int); 553 554extern int qcsmem_alloc(int, int, int); 555extern void *qcsmem_get(int, int, int *); 556 557int qcpas_pmic_rtr_init(void *); 558int qcpas_pmic_rtr_recv(void *, uint8_t *, int); 559int qcpas_pmic_rtr_apminfo(struct apm_power_info *); 560 561struct qcpas_glink_protocol { 562 char *name; 563 int (*init)(void *cookie); 564 int (*recv)(void *cookie, uint8_t *buf, int len); 565} qcpas_glink_protocols[] = { 566 { "PMIC_RTR_ADSP_APPS", qcpas_pmic_rtr_init , qcpas_pmic_rtr_recv }, 567}; 568 569void 570qcpas_glink_attach(struct qcpas_softc *sc, int node) 571{ 572 uint32_t remote; 573 uint32_t *descs; 574 int size; 575 576 remote = OF_getpropint(node, "qcom,remote-pid", -1); 577 if (remote == -1) 578 return; 579 580 if (qcsmem_alloc(remote, SMEM_GLINK_NATIVE_XPRT_DESCRIPTOR, 32) != 0 || 581 qcsmem_alloc(remote, SMEM_GLINK_NATIVE_XPRT_FIFO_0, 16384) != 0) 582 return; 583 584 descs = qcsmem_get(remote, SMEM_GLINK_NATIVE_XPRT_DESCRIPTOR, &size); 585 if (descs == NULL || size != 32) 586 return; 587 588 sc->sc_tx_tail = &descs[0]; 589 sc->sc_tx_head = &descs[1]; 590 sc->sc_rx_tail = &descs[2]; 591 sc->sc_rx_head = &descs[3]; 592 593 sc->sc_tx_fifo = qcsmem_get(remote, SMEM_GLINK_NATIVE_XPRT_FIFO_0, 594 &sc->sc_tx_fifolen); 595 if (sc->sc_tx_fifo == NULL) 596 return; 597 sc->sc_rx_fifo = qcsmem_get(remote, SMEM_GLINK_NATIVE_XPRT_FIFO_1, 598 &sc->sc_rx_fifolen); 599 if (sc->sc_rx_fifo == NULL) 600 return; 601 602 sc->sc_mc = mbox_channel_idx(node, 0, NULL); 603 if (sc->sc_mc == NULL) 604 return; 605 606 TAILQ_INIT(&sc->sc_glink_channels); 607 task_set(&sc->sc_glink_rx, qcpas_glink_recv, sc); 608 609 sc->sc_glink_ih = fdt_intr_establish(node, IPL_BIO, 610 qcpas_glink_intr, sc, sc->sc_dev.dv_xname); 611 if (sc->sc_glink_ih == NULL) 612 return; 613 614 /* Expect peer to send initial message */ 615} 616 617void 618qcpas_glink_rx(struct qcpas_softc *sc, uint8_t *buf, int len) 619{ 620 uint32_t head, tail; 621 int avail; 622 623 head = *sc->sc_rx_head; 624 tail = *sc->sc_rx_tail + sc->sc_rx_off; 625 if (tail >= sc->sc_rx_fifolen) 626 tail -= sc->sc_rx_fifolen; 627 628 /* Checked by caller */ 629 KASSERT(head != tail); 630 631 if (head >= tail) 632 avail = head - tail; 633 else 634 avail = (sc->sc_rx_fifolen - tail) + head; 635 636 /* Dumb, but should do. */ 637 KASSERT(avail >= len); 638 639 while (len > 0) { 640 *buf = sc->sc_rx_fifo[tail]; 641 tail++; 642 if (tail >= sc->sc_rx_fifolen) 643 tail -= sc->sc_rx_fifolen; 644 buf++; 645 sc->sc_rx_off++; 646 len--; 647 } 648} 649 650void 651qcpas_glink_rx_commit(struct qcpas_softc *sc) 652{ 653 uint32_t tail; 654 655 tail = *sc->sc_rx_tail + roundup(sc->sc_rx_off, 8); 656 if (tail >= sc->sc_rx_fifolen) 657 tail -= sc->sc_rx_fifolen; 658 659 membar_producer(); 660 *sc->sc_rx_tail = tail; 661 sc->sc_rx_off = 0; 662} 663 664void 665qcpas_glink_tx(struct qcpas_softc *sc, uint8_t *buf, int len) 666{ 667 uint32_t head, tail; 668 int avail; 669 670 head = *sc->sc_tx_head + sc->sc_tx_off; 671 if (head >= sc->sc_tx_fifolen) 672 head -= sc->sc_tx_fifolen; 673 tail = *sc->sc_tx_tail; 674 675 if (head < tail) 676 avail = tail - head; 677 else 678 avail = (sc->sc_rx_fifolen - head) + tail; 679 680 /* Dumb, but should do. */ 681 KASSERT(avail >= len); 682 683 while (len > 0) { 684 sc->sc_tx_fifo[head] = *buf; 685 head++; 686 if (head >= sc->sc_tx_fifolen) 687 head -= sc->sc_tx_fifolen; 688 buf++; 689 sc->sc_tx_off++; 690 len--; 691 } 692} 693 694void 695qcpas_glink_tx_commit(struct qcpas_softc *sc) 696{ 697 uint32_t head; 698 699 head = *sc->sc_tx_head + roundup(sc->sc_tx_off, 8); 700 if (head >= sc->sc_tx_fifolen) 701 head -= sc->sc_tx_fifolen; 702 703 membar_producer(); 704 *sc->sc_tx_head = head; 705 sc->sc_tx_off = 0; 706 mbox_send(sc->sc_mc, NULL, 0); 707} 708 709void 710qcpas_glink_send(void *cookie, void *buf, int len) 711{ 712 struct qcpas_glink_channel *ch = cookie; 713 struct qcpas_softc *sc = ch->ch_sc; 714 struct qcpas_glink_intent *it; 715 struct glink_msg msg; 716 uint32_t chunk_size, left_size; 717 718 TAILQ_FOREACH(it, &ch->ch_r_intents, it_q) { 719 if (!it->it_inuse) 720 break; 721 if (it->it_size < len) 722 continue; 723 } 724 if (it == NULL) { 725 printf("%s: all intents in use\n", 726 sc->sc_dev.dv_xname); 727 return; 728 } 729 it->it_inuse = 1; 730 731 msg.cmd = GLINK_CMD_TX_DATA; 732 msg.param1 = ch->ch_lcid; 733 msg.param2 = it->it_id; 734 735 chunk_size = len; 736 left_size = 0; 737 738 qcpas_glink_tx(sc, (char *)&msg, sizeof(msg)); 739 qcpas_glink_tx(sc, (char *)&chunk_size, sizeof(chunk_size)); 740 qcpas_glink_tx(sc, (char *)&left_size, sizeof(left_size)); 741 qcpas_glink_tx(sc, buf, len); 742 qcpas_glink_tx_commit(sc); 743} 744 745void 746qcpas_glink_recv_version(struct qcpas_softc *sc, uint32_t version, 747 uint32_t features) 748{ 749 struct glink_msg msg; 750 751 if (version != GLINK_VERSION) { 752 printf("%s: unsupported glink version %u\n", 753 sc->sc_dev.dv_xname, version); 754 return; 755 } 756 757 msg.cmd = GLINK_CMD_VERSION_ACK; 758 msg.param1 = GLINK_VERSION; 759 msg.param2 = features & GLINK_FEATURE_INTENT_REUSE; 760 761 qcpas_glink_tx(sc, (char *)&msg, sizeof(msg)); 762 qcpas_glink_tx_commit(sc); 763} 764 765void 766qcpas_glink_recv_open(struct qcpas_softc *sc, uint32_t rcid, uint32_t namelen) 767{ 768 struct qcpas_glink_protocol *proto = NULL; 769 struct qcpas_glink_channel *ch; 770 struct glink_msg msg; 771 char *name; 772 int i, err; 773 774 name = malloc(namelen, M_TEMP, M_WAITOK); 775 qcpas_glink_rx(sc, name, namelen); 776 qcpas_glink_rx_commit(sc); 777 778 TAILQ_FOREACH(ch, &sc->sc_glink_channels, ch_q) { 779 if (ch->ch_rcid == rcid) { 780 printf("%s: duplicate open for %s\n", 781 sc->sc_dev.dv_xname, name); 782 free(name, M_TEMP, namelen); 783 return; 784 } 785 } 786 787 for (i = 0; i < nitems(qcpas_glink_protocols); i++) { 788 if (strcmp(qcpas_glink_protocols[i].name, name) != 0) 789 continue; 790 proto = &qcpas_glink_protocols[i]; 791 break; 792 } 793 if (proto == NULL) { 794 free(name, M_TEMP, namelen); 795 return; 796 } 797 798 /* Assume we can leave HW dangling if proto init fails */ 799 err = proto->init(NULL); 800 if (err) { 801 free(name, M_TEMP, namelen); 802 return; 803 } 804 805 ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO); 806 ch->ch_sc = sc; 807 ch->ch_proto = proto; 808 ch->ch_rcid = rcid; 809 ch->ch_lcid = ++sc->sc_glink_max_channel; 810 TAILQ_INIT(&ch->ch_l_intents); 811 TAILQ_INIT(&ch->ch_r_intents); 812 TAILQ_INSERT_TAIL(&sc->sc_glink_channels, ch, ch_q); 813 814 msg.cmd = GLINK_CMD_OPEN_ACK; 815 msg.param1 = ch->ch_rcid; 816 msg.param2 = 0; 817 818 qcpas_glink_tx(sc, (char *)&msg, sizeof(msg)); 819 qcpas_glink_tx_commit(sc); 820 821 msg.cmd = GLINK_CMD_OPEN; 822 msg.param1 = ch->ch_lcid; 823 msg.param2 = strlen(name) + 1; 824 825 qcpas_glink_tx(sc, (char *)&msg, sizeof(msg)); 826 qcpas_glink_tx(sc, name, strlen(name) + 1); 827 qcpas_glink_tx_commit(sc); 828 829 free(name, M_TEMP, namelen); 830} 831 832void 833qcpas_glink_recv_open_ack(struct qcpas_softc *sc, uint32_t lcid) 834{ 835 struct qcpas_glink_channel *ch; 836 struct glink_msg msg; 837 struct qcpas_glink_intent_pair intent; 838 int i; 839 840 TAILQ_FOREACH(ch, &sc->sc_glink_channels, ch_q) { 841 if (ch->ch_lcid == lcid) 842 break; 843 } 844 if (ch == NULL) { 845 printf("%s: unknown channel %u for OPEN_ACK\n", 846 sc->sc_dev.dv_xname, lcid); 847 return; 848 } 849 850 /* Respond with default intent now that channel is open */ 851 for (i = 0; i < 5; i++) { 852 struct qcpas_glink_intent *it; 853 854 it = malloc(sizeof(*it), M_DEVBUF, M_WAITOK | M_ZERO); 855 it->it_id = ++ch->ch_max_intent; 856 it->it_size = 1024; 857 TAILQ_INSERT_TAIL(&ch->ch_l_intents, it, it_q); 858 859 msg.cmd = GLINK_CMD_INTENT; 860 msg.param1 = ch->ch_lcid; 861 msg.param2 = 1; 862 intent.size = it->it_size; 863 intent.iid = it->it_id; 864 } 865 866 qcpas_glink_tx(sc, (char *)&msg, sizeof(msg)); 867 qcpas_glink_tx(sc, (char *)&intent, sizeof(intent)); 868 qcpas_glink_tx_commit(sc); 869} 870 871void 872qcpas_glink_recv_intent(struct qcpas_softc *sc, uint32_t rcid, uint32_t count) 873{ 874 struct qcpas_glink_intent_pair *intents; 875 struct qcpas_glink_channel *ch; 876 struct qcpas_glink_intent *it; 877 int i; 878 879 intents = malloc(sizeof(*intents) * count, M_TEMP, M_WAITOK); 880 qcpas_glink_rx(sc, (char *)intents, sizeof(*intents) * count); 881 qcpas_glink_rx_commit(sc); 882 883 TAILQ_FOREACH(ch, &sc->sc_glink_channels, ch_q) { 884 if (ch->ch_rcid == rcid) 885 break; 886 } 887 if (ch == NULL) { 888 printf("%s: unknown channel %u for INTENT\n", 889 sc->sc_dev.dv_xname, rcid); 890 free(intents, M_TEMP, sizeof(*intents) * count); 891 return; 892 } 893 894 for (i = 0; i < count; i++) { 895 it = malloc(sizeof(*it), M_DEVBUF, M_WAITOK | M_ZERO); 896 it->it_id = intents[i].iid; 897 it->it_size = intents[i].size; 898 TAILQ_INSERT_TAIL(&ch->ch_r_intents, it, it_q); 899 } 900 901 free(intents, M_TEMP, sizeof(*intents) * count); 902} 903 904void 905qcpas_glink_recv_tx_data(struct qcpas_softc *sc, uint32_t rcid, uint32_t liid) 906{ 907 struct qcpas_glink_channel *ch; 908 struct qcpas_glink_intent *it; 909 struct glink_msg msg; 910 uint32_t chunk_size, left_size; 911 char *buf; 912 913 qcpas_glink_rx(sc, (char *)&chunk_size, sizeof(chunk_size)); 914 qcpas_glink_rx(sc, (char *)&left_size, sizeof(left_size)); 915 qcpas_glink_rx_commit(sc); 916 917 buf = malloc(chunk_size, M_TEMP, M_WAITOK); 918 qcpas_glink_rx(sc, buf, chunk_size); 919 qcpas_glink_rx_commit(sc); 920 921 TAILQ_FOREACH(ch, &sc->sc_glink_channels, ch_q) { 922 if (ch->ch_rcid == rcid) 923 break; 924 } 925 if (ch == NULL) { 926 printf("%s: unknown channel %u for TX_DATA\n", 927 sc->sc_dev.dv_xname, rcid); 928 free(buf, M_TEMP, chunk_size); 929 return; 930 } 931 932 TAILQ_FOREACH(it, &ch->ch_l_intents, it_q) { 933 if (it->it_id == liid) 934 break; 935 } 936 if (it == NULL) { 937 printf("%s: unknown intent %u for TX_DATA\n", 938 sc->sc_dev.dv_xname, liid); 939 free(buf, M_TEMP, chunk_size); 940 return; 941 } 942 943 /* FIXME: handle message chunking */ 944 KASSERT(left_size == 0); 945 946 ch->ch_proto->recv(ch, buf, chunk_size); 947 free(buf, M_TEMP, chunk_size); 948 949 if (!left_size) { 950 msg.cmd = GLINK_CMD_RX_DONE_W_REUSE; 951 msg.param1 = ch->ch_lcid; 952 msg.param2 = it->it_id; 953 954 qcpas_glink_tx(sc, (char *)&msg, sizeof(msg)); 955 qcpas_glink_tx_commit(sc); 956 } 957} 958 959void 960qcpas_glink_recv_rx_done(struct qcpas_softc *sc, uint32_t rcid, uint32_t riid, 961 int reuse) 962{ 963 struct qcpas_glink_channel *ch; 964 struct qcpas_glink_intent *it; 965 966 TAILQ_FOREACH(ch, &sc->sc_glink_channels, ch_q) { 967 if (ch->ch_rcid == rcid) 968 break; 969 } 970 if (ch == NULL) { 971 printf("%s: unknown channel %u for RX_DONE\n", 972 sc->sc_dev.dv_xname, rcid); 973 return; 974 } 975 976 TAILQ_FOREACH(it, &ch->ch_r_intents, it_q) { 977 if (it->it_id == riid) 978 break; 979 } 980 if (it == NULL) { 981 printf("%s: unknown intent %u for RX_DONE\n", 982 sc->sc_dev.dv_xname, riid); 983 return; 984 } 985 986 /* FIXME: handle non-reuse */ 987 KASSERT(reuse); 988 989 KASSERT(it->it_inuse); 990 it->it_inuse = 0; 991} 992 993void 994qcpas_glink_recv(void *cookie) 995{ 996 struct qcpas_softc *sc = cookie; 997 struct glink_msg msg; 998 999 while (*sc->sc_rx_tail != *sc->sc_rx_head) { 1000 membar_consumer(); 1001 qcpas_glink_rx(sc, (uint8_t *)&msg, sizeof(msg)); 1002 qcpas_glink_rx_commit(sc); 1003 1004 switch (msg.cmd) { 1005 case GLINK_CMD_VERSION: 1006 qcpas_glink_recv_version(sc, msg.param1, msg.param2); 1007 break; 1008 case GLINK_CMD_OPEN: 1009 qcpas_glink_recv_open(sc, msg.param1, msg.param2); 1010 break; 1011 case GLINK_CMD_OPEN_ACK: 1012 qcpas_glink_recv_open_ack(sc, msg.param1); 1013 break; 1014 case GLINK_CMD_INTENT: 1015 qcpas_glink_recv_intent(sc, msg.param1, msg.param2); 1016 break; 1017 case GLINK_CMD_RX_INTENT_REQ: 1018 /* Nothing to do so far */ 1019 break; 1020 case GLINK_CMD_TX_DATA: 1021 qcpas_glink_recv_tx_data(sc, msg.param1, msg.param2); 1022 break; 1023 case GLINK_CMD_RX_DONE: 1024 qcpas_glink_recv_rx_done(sc, msg.param1, msg.param2, 0); 1025 break; 1026 case GLINK_CMD_RX_DONE_W_REUSE: 1027 qcpas_glink_recv_rx_done(sc, msg.param1, msg.param2, 1); 1028 break; 1029 default: 1030 printf("%s: unknown cmd %u\n", __func__, msg.cmd); 1031 return; 1032 } 1033 } 1034} 1035 1036int 1037qcpas_glink_intr(void *cookie) 1038{ 1039 struct qcpas_softc *sc = cookie; 1040 1041 task_add(systq, &sc->sc_glink_rx); 1042 return 1; 1043} 1044 1045/* GLINK PMIC Router */ 1046 1047struct pmic_glink_hdr { 1048 uint32_t owner; 1049#define PMIC_GLINK_OWNER_BATTMGR 32778 1050#define PMIC_GLINK_OWNER_USBC 32779 1051#define PMIC_GLINK_OWNER_USBC_PAN 32780 1052 uint32_t type; 1053#define PMIC_GLINK_TYPE_REQ_RESP 1 1054#define PMIC_GLINK_TYPE_NOTIFY 2 1055 uint32_t opcode; 1056}; 1057 1058#define BATTMGR_OPCODE_BAT_STATUS 0x1 1059#define BATTMGR_OPCODR_REQUEST_NOTIFICATION 0x4 1060#define BATTMGR_OPCODE_NOTIF 0x7 1061#define BATTMGR_OPCODE_BAT_INFO 0x9 1062#define BATTMGR_OPCODE_BAT_DISCHARGE_TIME 0xc 1063#define BATTMGR_OPCODE_BAT_CHARGE_TIME 0xd 1064 1065#define BATTMGR_NOTIF_BAT_PROPERTY 0x30 1066#define BATTMGR_NOTIF_USB_PROPERTY 0x32 1067#define BATTMGR_NOTIF_WLS_PROPERTY 0x34 1068#define BATTMGR_NOTIF_BAT_STATUS 0x80 1069#define BATTMGR_NOTIF_BAT_INFO 0x81 1070 1071#define BATTMGR_CHEMISTRY_LEN 4 1072#define BATTMGR_STRING_LEN 128 1073 1074struct battmgr_bat_info { 1075 uint32_t power_unit; 1076 uint32_t design_capacity; 1077 uint32_t last_full_capacity; 1078 uint32_t battery_tech; 1079 uint32_t design_voltage; 1080 uint32_t capacity_low; 1081 uint32_t capacity_warning; 1082 uint32_t cycle_count; 1083 uint32_t accuracy; 1084 uint32_t max_sample_time_ms; 1085 uint32_t min_sample_time_ms; 1086 uint32_t max_average_interval_ms; 1087 uint32_t min_averae_interval_ms; 1088 uint32_t capacity_granularity1; 1089 uint32_t capacity_granularity2; 1090 uint32_t swappable; 1091 uint32_t capabilities; 1092 char model_number[BATTMGR_STRING_LEN]; 1093 char serial_number[BATTMGR_STRING_LEN]; 1094 char battery_type[BATTMGR_STRING_LEN]; 1095 char oem_info[BATTMGR_STRING_LEN]; 1096 char battery_chemistry[BATTMGR_CHEMISTRY_LEN]; 1097 char uid[BATTMGR_STRING_LEN]; 1098 uint32_t critical_bias; 1099 uint8_t day; 1100 uint8_t month; 1101 uint16_t year; 1102 uint32_t battery_id; 1103}; 1104 1105struct battmgr_bat_status { 1106 uint32_t battery_state; 1107#define BATTMGR_BAT_STATE_DISCHARGE (1 << 0) 1108#define BATTMGR_BAT_STATE_CHARGING (1 << 1) 1109#define BATTMGR_BAT_STATE_CRITICAL_LOW (1 << 2) 1110 uint32_t capacity; 1111 uint32_t rate; 1112 uint32_t battery_voltage; 1113 uint32_t power_state; 1114#define BATTMGR_PWR_STATE_AC_ON (1 << 0) 1115 uint32_t charging_source; 1116#define BATTMGR_CHARGING_SOURCE_AC 1 1117#define BATTMGR_CHARGING_SOURCE_USB 2 1118#define BATTMGR_CHARGING_SOURCE_WIRELESS 3 1119 uint32_t temperature; 1120}; 1121 1122void 1123qcpas_pmic_rtr_battmgr_req_info(void *cookie) 1124{ 1125 struct { 1126 struct pmic_glink_hdr hdr; 1127 uint32_t battery_id; 1128 } msg; 1129 1130 msg.hdr.owner = PMIC_GLINK_OWNER_BATTMGR; 1131 msg.hdr.type = PMIC_GLINK_TYPE_REQ_RESP; 1132 msg.hdr.opcode = BATTMGR_OPCODE_BAT_INFO; 1133 msg.battery_id = 0; 1134 qcpas_glink_send(cookie, &msg, sizeof(msg)); 1135} 1136 1137void 1138qcpas_pmic_rtr_battmgr_req_status(void *cookie) 1139{ 1140 struct { 1141 struct pmic_glink_hdr hdr; 1142 uint32_t battery_id; 1143 } msg; 1144 1145 msg.hdr.owner = PMIC_GLINK_OWNER_BATTMGR; 1146 msg.hdr.type = PMIC_GLINK_TYPE_REQ_RESP; 1147 msg.hdr.opcode = BATTMGR_OPCODE_BAT_STATUS; 1148 msg.battery_id = 0; 1149 qcpas_glink_send(cookie, &msg, sizeof(msg)); 1150} 1151 1152#if NAPM > 0 1153struct apm_power_info qcpas_pmic_rtr_apm_power_info; 1154uint32_t qcpas_pmic_rtr_last_full_capacity; 1155#endif 1156 1157int 1158qcpas_pmic_rtr_init(void *cookie) 1159{ 1160#if NAPM > 0 1161 struct apm_power_info *info; 1162 1163 info = &qcpas_pmic_rtr_apm_power_info; 1164 info->battery_state = APM_BATT_UNKNOWN; 1165 info->ac_state = APM_AC_UNKNOWN; 1166 info->battery_life = 0; 1167 info->minutes_left = -1; 1168 1169 apm_setinfohook(qcpas_pmic_rtr_apminfo); 1170#endif 1171 return 0; 1172} 1173 1174int 1175qcpas_pmic_rtr_recv(void *cookie, uint8_t *buf, int len) 1176{ 1177 struct pmic_glink_hdr hdr; 1178 uint32_t notification; 1179 extern int hw_power; 1180 1181 if (len < sizeof(hdr)) { 1182 printf("%s: pmic glink message too small\n", 1183 __func__); 1184 return 0; 1185 } 1186 1187 memcpy(&hdr, buf, sizeof(hdr)); 1188 1189 switch (hdr.owner) { 1190 case PMIC_GLINK_OWNER_BATTMGR: 1191 switch (hdr.opcode) { 1192 case BATTMGR_OPCODE_NOTIF: 1193 if (len - sizeof(hdr) != sizeof(uint32_t)) { 1194 printf("%s: invalid battgmr notification\n", 1195 __func__); 1196 return 0; 1197 } 1198 memcpy(¬ification, buf + sizeof(hdr), 1199 sizeof(uint32_t)); 1200 switch (notification) { 1201 case BATTMGR_NOTIF_BAT_INFO: 1202 qcpas_pmic_rtr_battmgr_req_info(cookie); 1203 /* FALLTHROUGH */ 1204 case BATTMGR_NOTIF_BAT_STATUS: 1205 case BATTMGR_NOTIF_BAT_PROPERTY: 1206 qcpas_pmic_rtr_battmgr_req_status(cookie); 1207 break; 1208 default: 1209 printf("%s: unknown battmgr notification" 1210 " 0x%02x\n", __func__, notification); 1211 break; 1212 } 1213 break; 1214 case BATTMGR_OPCODE_BAT_INFO: { 1215 struct battmgr_bat_info *bat; 1216 if (len - sizeof(hdr) != sizeof(*bat)) { 1217 printf("%s: invalid battgmr bat info\n", 1218 __func__); 1219 return 0; 1220 } 1221 bat = malloc(sizeof(*bat), M_TEMP, M_WAITOK); 1222 memcpy((void *)bat, buf + sizeof(hdr), sizeof(*bat)); 1223#if NAPM > 0 1224 qcpas_pmic_rtr_last_full_capacity = 1225 bat->last_full_capacity; 1226#endif 1227 free(bat, M_TEMP, sizeof(*bat)); 1228 break; 1229 } 1230 case BATTMGR_OPCODE_BAT_STATUS: { 1231 struct battmgr_bat_status *bat; 1232#if NAPM > 0 1233 struct apm_power_info *info; 1234#endif 1235 if (len - sizeof(hdr) != sizeof(*bat)) { 1236 printf("%s: invalid battgmr bat status\n", 1237 __func__); 1238 return 0; 1239 } 1240#if NAPM > 0 1241 /* Needs BAT_INFO fist */ 1242 if (!qcpas_pmic_rtr_last_full_capacity) 1243 return 0; 1244#endif 1245 bat = malloc(sizeof(*bat), M_TEMP, M_WAITOK); 1246 memcpy((void *)bat, buf + sizeof(hdr), sizeof(*bat)); 1247#if NAPM > 0 1248 info = &qcpas_pmic_rtr_apm_power_info; 1249 info->battery_life = ((bat->capacity * 100) / 1250 qcpas_pmic_rtr_last_full_capacity); 1251 if (info->battery_life > 50) 1252 info->battery_state = APM_BATT_HIGH; 1253 else if (info->battery_life > 25) 1254 info->battery_state = APM_BATT_LOW; 1255 else 1256 info->battery_state = APM_BATT_CRITICAL; 1257 if (bat->battery_state & BATTMGR_BAT_STATE_CHARGING) 1258 info->battery_state = APM_BATT_CHARGING; 1259 else if (bat->battery_state & BATTMGR_BAT_STATE_CRITICAL_LOW) 1260 info->battery_state = APM_BATT_CRITICAL; 1261 1262 if (bat->power_state & BATTMGR_PWR_STATE_AC_ON) { 1263 info->ac_state = APM_AC_ON; 1264 hw_power = 1; 1265 } else { 1266 info->ac_state = APM_AC_OFF; 1267 hw_power = 0; 1268 } 1269#endif 1270 free(bat, M_TEMP, sizeof(*bat)); 1271 break; 1272 } 1273 default: 1274 printf("%s: unknown battmgr opcode 0x%02x\n", 1275 __func__, hdr.opcode); 1276 break; 1277 } 1278 break; 1279 default: 1280 printf("%s: unknown pmic glink owner 0x%04x\n", 1281 __func__, hdr.owner); 1282 break; 1283 } 1284 1285 return 0; 1286} 1287 1288#if NAPM > 0 1289int 1290qcpas_pmic_rtr_apminfo(struct apm_power_info *info) 1291{ 1292 memcpy(info, &qcpas_pmic_rtr_apm_power_info, sizeof(*info)); 1293 1294 return 0; 1295} 1296#endif 1297