From 5dbfd9053c22308a95531b86c9a2ff6b3be128d2 Mon Sep 17 00:00:00 2001 From: kettenis Date: Sun, 4 Aug 2024 15:30:08 +0000 Subject: [PATCH] Add battery status support for the x1e80100. This SoC needs a second firmware and requires us to shut down an initial "light" firmware that was presumably loaded bu the system firmware. For now the required firmwares are not packaged and need to be copied from the Windows install. ok patrick@ --- sys/dev/fdt/qcpas.c | 179 +++++++++++++++++++++++++++++--------------- sys/dev/fdt/qcscm.c | 29 ++++++- sys/sys/device.h | 4 +- 3 files changed, 148 insertions(+), 64 deletions(-) diff --git a/sys/dev/fdt/qcpas.c b/sys/dev/fdt/qcpas.c index c943a7db853..06bcc694699 100644 --- a/sys/dev/fdt/qcpas.c +++ b/sys/dev/fdt/qcpas.c @@ -1,4 +1,4 @@ -/* $OpenBSD: qcpas.c,v 1.3 2024/07/25 20:21:40 kettenis Exp $ */ +/* $OpenBSD: qcpas.c,v 1.4 2024/08/04 15:30:08 kettenis Exp $ */ /* * Copyright (c) 2023 Patrick Wildt * @@ -36,6 +36,11 @@ #include "apm.h" +extern int qcscm_pas_init_image(uint32_t, paddr_t); +extern int qcscm_pas_mem_setup(uint32_t, paddr_t, size_t); +extern int qcscm_pas_auth_and_reset(uint32_t); +extern int qcscm_pas_shutdown(uint32_t); + #define MDT_TYPE_MASK (7 << 24) #define MDT_TYPE_HASH (2 << 24) #define MDT_RELOCATABLE (1 << 27) @@ -65,15 +70,17 @@ struct qcpas_softc { void *sc_ih[6]; - paddr_t sc_mem_phys; - size_t sc_mem_size; - void *sc_mem_region; - vaddr_t sc_mem_reloc; + paddr_t sc_mem_phys[2]; + size_t sc_mem_size[2]; + void *sc_mem_region[2]; + vaddr_t sc_mem_reloc[2]; uint32_t sc_pas_id; + uint32_t sc_dtb_pas_id; + uint32_t sc_lite_pas_id; char *sc_load_state; - struct qcpas_dmamem *sc_metadata; + struct qcpas_dmamem *sc_metadata[2]; /* GLINK */ volatile uint32_t *sc_tx_tail; @@ -110,7 +117,7 @@ struct cfdriver qcpas_cd = { void qcpas_mountroot(struct device *); int qcpas_map_memory(struct qcpas_softc *); -int qcpas_mdt_init(struct qcpas_softc *, u_char *, size_t); +int qcpas_mdt_init(struct qcpas_softc *, int, u_char *, size_t); void qcpas_glink_attach(struct qcpas_softc *, int); struct qcpas_dmamem * @@ -130,7 +137,8 @@ qcpas_match(struct device *parent, void *match, void *aux) { struct fdt_attach_args *faa = aux; - return OF_is_compatible(faa->fa_node, "qcom,sc8280xp-adsp-pas"); + return OF_is_compatible(faa->fa_node, "qcom,sc8280xp-adsp-pas") || + OF_is_compatible(faa->fa_node, "qcom,x1e80100-adsp-pas"); } void @@ -164,6 +172,13 @@ qcpas_attach(struct device *parent, struct device *self, void *aux) sc->sc_pas_id = 30; } + if (OF_is_compatible(faa->fa_node, "qcom,x1e80100-adsp-pas")) { + sc->sc_pas_id = 1; + sc->sc_dtb_pas_id = 36; + sc->sc_lite_pas_id = 31; + sc->sc_load_state = "adsp"; + } + qcpas_intr_establish(sc, 0, "wdog", qcpas_intr_wdog); qcpas_intr_establish(sc, 1, "fatal", qcpas_intr_fatal); qcpas_intr_establish(sc, 2, "ready", qcpas_intr_ready); @@ -182,10 +197,11 @@ void qcpas_mountroot(struct device *self) { struct qcpas_softc *sc = (struct qcpas_softc *)self; - char fwname[64]; - size_t fwlen; - u_char *fw; + char fwname[128]; + size_t fwlen, dtb_fwlen; + u_char *fw, *dtb_fw; int node, ret; + int error; if (qcpas_map_memory(sc) != 0) return; @@ -195,12 +211,34 @@ qcpas_mountroot(struct device *self) OF_getprop(sc->sc_node, "firmware-name", fwname, sizeof(fwname)); fwname[sizeof(fwname) - 1] = '\0'; - if (loadfirmware(fwname, &fw, &fwlen) != 0) { - printf("%s: failed to load %s\n", - sc->sc_dev.dv_xname, fwname); + /* If we need a second firmware, make sure we have a name for it. */ + if (sc->sc_dtb_pas_id && strlen(fwname) == sizeof(fwname) - 1) + return; + + error = loadfirmware(fwname, &fw, &fwlen); + if (error) { + printf("%s: failed to load %s: %d\n", + sc->sc_dev.dv_xname, fwname, error); return; } + if (sc->sc_lite_pas_id) { + if (qcscm_pas_shutdown(sc->sc_lite_pas_id)) { + printf("%s: failed to shutdown lite firmare\n", + sc->sc_dev.dv_xname); + } + } + + if (sc->sc_dtb_pas_id) { + error = loadfirmware(fwname + strlen(fwname) + 1, + &dtb_fw, &dtb_fwlen); + if (error) { + printf("%s: failed to load %s: %d\n", + sc->sc_dev.dv_xname, fwname, error); + return; + } + } + if (sc->sc_load_state) { char buf[64]; snprintf(buf, sizeof(buf), @@ -217,7 +255,12 @@ qcpas_mountroot(struct device *self) power_domain_enable_all(sc->sc_node); clock_enable(sc->sc_node, "xo"); - ret = qcpas_mdt_init(sc, fw, fwlen); + if (sc->sc_dtb_pas_id) { + qcpas_mdt_init(sc, sc->sc_dtb_pas_id, dtb_fw, dtb_fwlen); + free(dtb_fw, M_DEVBUF, dtb_fwlen); + } + + ret = qcpas_mdt_init(sc, sc->sc_pas_id, fw, fwlen); free(fw, M_DEVBUF, fwlen); if (ret != 0) { printf("%s: failed to boot coprocessor\n", @@ -233,44 +276,49 @@ qcpas_mountroot(struct device *self) int qcpas_map_memory(struct qcpas_softc *sc) { - uint32_t phandle, reg[4]; + uint32_t memreg[2] = {}; + uint32_t reg[4]; size_t off; int node; + int i; - phandle = OF_getpropint(sc->sc_node, "memory-region", 0); - if (phandle == 0) - return EINVAL; - node = OF_getnodebyphandle(phandle); - if (node == 0) - return EINVAL; - if (OF_getpropintarray(node, "reg", reg, sizeof(reg)) != sizeof(reg)) + OF_getpropintarray(sc->sc_node, "memory-region", + memreg, sizeof(memreg)); + if (memreg[0] == 0) return EINVAL; - sc->sc_mem_phys = (uint64_t)reg[0] << 32 | reg[1]; - KASSERT((sc->sc_mem_phys & PAGE_MASK) == 0); - sc->sc_mem_size = (uint64_t)reg[2] << 32 | reg[3]; - KASSERT((sc->sc_mem_size & PAGE_MASK) == 0); + for (i = 0; i < nitems(memreg); i++) { + if (memreg[i] == 0) + break; + node = OF_getnodebyphandle(memreg[i]); + if (node == 0) + return EINVAL; + if (OF_getpropintarray(node, "reg", reg, + sizeof(reg)) != sizeof(reg)) + return EINVAL; - sc->sc_mem_region = km_alloc(sc->sc_mem_size, &kv_any, &kp_none, - &kd_nowait); - if (!sc->sc_mem_region) - return ENOMEM; + sc->sc_mem_phys[i] = (uint64_t)reg[0] << 32 | reg[1]; + KASSERT((sc->sc_mem_phys[i] & PAGE_MASK) == 0); + sc->sc_mem_size[i] = (uint64_t)reg[2] << 32 | reg[3]; + KASSERT((sc->sc_mem_size[i] & PAGE_MASK) == 0); - for (off = 0; off < sc->sc_mem_size; off += PAGE_SIZE) { - pmap_kenter_cache((vaddr_t)sc->sc_mem_region + off, - sc->sc_mem_phys + off, PROT_READ | PROT_WRITE, - PMAP_CACHE_DEV_NGNRNE); + sc->sc_mem_region[i] = km_alloc(sc->sc_mem_size[i], + &kv_any, &kp_none, &kd_nowait); + if (!sc->sc_mem_region[i]) + return ENOMEM; + + for (off = 0; off < sc->sc_mem_size[i]; off += PAGE_SIZE) { + pmap_kenter_cache((vaddr_t)sc->sc_mem_region[i] + off, + sc->sc_mem_phys[i] + off, PROT_READ | PROT_WRITE, + PMAP_CACHE_DEV_NGNRNE); + } } return 0; } -extern int qcscm_pas_init_image(uint32_t, paddr_t); -extern int qcscm_pas_mem_setup(uint32_t, paddr_t, size_t); -extern int qcscm_pas_auth_and_reset(uint32_t); - int -qcpas_mdt_init(struct qcpas_softc *sc, u_char *fw, size_t fwlen) +qcpas_mdt_init(struct qcpas_softc *sc, int pas_id, u_char *fw, size_t fwlen) { Elf32_Ehdr *ehdr; Elf32_Phdr *phdr; @@ -278,6 +326,12 @@ qcpas_mdt_init(struct qcpas_softc *sc, u_char *fw, size_t fwlen) int i, hashseg = 0, relocate = 0; int error; ssize_t off; + int idx; + + if (pas_id == sc->sc_dtb_pas_id) + idx = 1; + else + idx = 0; ehdr = (Elf32_Ehdr *)fw; phdr = (Elf32_Phdr *)&ehdr[1]; @@ -306,17 +360,17 @@ qcpas_mdt_init(struct qcpas_softc *sc, u_char *fw, size_t fwlen) if (!hashseg) return EINVAL; - sc->sc_metadata = qcpas_dmamem_alloc(sc, phdr[0].p_filesz + + sc->sc_metadata[idx] = qcpas_dmamem_alloc(sc, phdr[0].p_filesz + phdr[hashseg].p_filesz, PAGE_SIZE); - if (sc->sc_metadata == NULL) + if (sc->sc_metadata[idx] == NULL) return EINVAL; - memcpy(QCPAS_DMA_KVA(sc->sc_metadata), fw, phdr[0].p_filesz); + memcpy(QCPAS_DMA_KVA(sc->sc_metadata[idx]), fw, phdr[0].p_filesz); if (phdr[0].p_filesz + phdr[hashseg].p_filesz == fwlen) { - memcpy(QCPAS_DMA_KVA(sc->sc_metadata) + phdr[0].p_filesz, + memcpy(QCPAS_DMA_KVA(sc->sc_metadata[idx]) + phdr[0].p_filesz, fw + phdr[0].p_filesz, phdr[hashseg].p_filesz); } else if (phdr[hashseg].p_offset + phdr[hashseg].p_filesz <= fwlen) { - memcpy(QCPAS_DMA_KVA(sc->sc_metadata) + phdr[0].p_filesz, + memcpy(QCPAS_DMA_KVA(sc->sc_metadata[idx]) + phdr[0].p_filesz, fw + phdr[hashseg].p_offset, phdr[hashseg].p_filesz); } else { printf("%s: metadata split segment not supported\n", @@ -326,36 +380,36 @@ qcpas_mdt_init(struct qcpas_softc *sc, u_char *fw, size_t fwlen) membar_producer(); - if (qcscm_pas_init_image(sc->sc_pas_id, - QCPAS_DMA_DVA(sc->sc_metadata)) != 0) { + if (qcscm_pas_init_image(pas_id, + QCPAS_DMA_DVA(sc->sc_metadata[idx])) != 0) { printf("%s: init image failed\n", sc->sc_dev.dv_xname); - qcpas_dmamem_free(sc, sc->sc_metadata); + qcpas_dmamem_free(sc, sc->sc_metadata[idx]); return EINVAL; } - if (qcscm_pas_mem_setup(sc->sc_pas_id, - sc->sc_mem_phys, maxpa - minpa) != 0) { + if (qcscm_pas_mem_setup(pas_id, + sc->sc_mem_phys[idx], maxpa - minpa) != 0) { printf("%s: mem setup failed\n", sc->sc_dev.dv_xname); - qcpas_dmamem_free(sc, sc->sc_metadata); + qcpas_dmamem_free(sc, sc->sc_metadata[idx]); return EINVAL; } - sc->sc_mem_reloc = relocate ? minpa : sc->sc_mem_phys; + sc->sc_mem_reloc[idx] = relocate ? minpa : sc->sc_mem_phys[idx]; for (i = 0; i < ehdr->e_phnum; i++) { if ((phdr[i].p_flags & MDT_TYPE_MASK) == MDT_TYPE_HASH || phdr[i].p_type != PT_LOAD || phdr[i].p_memsz == 0) continue; - off = phdr[i].p_paddr - sc->sc_mem_reloc; - if (off < 0 || off + phdr[i].p_memsz > sc->sc_mem_size) + off = phdr[i].p_paddr - sc->sc_mem_reloc[idx]; + if (off < 0 || off + phdr[i].p_memsz > sc->sc_mem_size[0]) return EINVAL; if (phdr[i].p_filesz > phdr[i].p_memsz) return EINVAL; if (phdr[i].p_filesz && phdr[i].p_offset < fwlen && phdr[i].p_offset + phdr[i].p_filesz <= fwlen) { - memcpy(sc->sc_mem_region + off, fw + phdr[i].p_offset, - phdr[i].p_filesz); + memcpy(sc->sc_mem_region[idx] + off, + fw + phdr[i].p_offset, phdr[i].p_filesz); } else if (phdr[i].p_filesz) { printf("%s: firmware split segment not supported\n", sc->sc_dev.dv_xname); @@ -363,18 +417,21 @@ qcpas_mdt_init(struct qcpas_softc *sc, u_char *fw, size_t fwlen) } if (phdr[i].p_memsz > phdr[i].p_filesz) - memset(sc->sc_mem_region + off + phdr[i].p_filesz, 0, - phdr[i].p_memsz - phdr[i].p_filesz); + memset(sc->sc_mem_region[idx] + off + phdr[i].p_filesz, + 0, phdr[i].p_memsz - phdr[i].p_filesz); } membar_producer(); - if (qcscm_pas_auth_and_reset(sc->sc_pas_id) != 0) { + if (qcscm_pas_auth_and_reset(pas_id) != 0) { printf("%s: auth and reset failed\n", sc->sc_dev.dv_xname); - qcpas_dmamem_free(sc, sc->sc_metadata); + qcpas_dmamem_free(sc, sc->sc_metadata[idx]); return EINVAL; } + if (pas_id == sc->sc_dtb_pas_id) + return 0; + error = tsleep_nsec(sc, PWAIT, "qcpas", SEC_TO_NSEC(5)); if (error) { printf("%s: failed to receive ready signal\n", @@ -1219,7 +1276,7 @@ qcpas_pmic_rtr_recv(void *cookie, uint8_t *buf, int len) break; case BATTMGR_OPCODE_BAT_INFO: { struct battmgr_bat_info *bat; - if (len - sizeof(hdr) != sizeof(*bat)) { + if (len - sizeof(hdr) < sizeof(*bat)) { printf("%s: invalid battgmr bat info\n", __func__); return 0; diff --git a/sys/dev/fdt/qcscm.c b/sys/dev/fdt/qcscm.c index cb7d9d466d4..5aa411da758 100644 --- a/sys/dev/fdt/qcscm.c +++ b/sys/dev/fdt/qcscm.c @@ -1,4 +1,4 @@ -/* $OpenBSD: qcscm.c,v 1.8 2024/07/10 10:53:55 kettenis Exp $ */ +/* $OpenBSD: qcscm.c,v 1.9 2024/08/04 15:30:08 kettenis Exp $ */ /* * Copyright (c) 2022 Patrick Wildt * @@ -922,6 +922,33 @@ qcscm_pas_auth_and_reset(uint32_t peripheral) return ret; } +int +qcscm_pas_shutdown(uint32_t peripheral) +{ + struct qcscm_softc *sc = qcscm_sc; + uint64_t res[3]; + uint64_t args[1]; + uint32_t arginfo; + int ret; + + if (sc == NULL) + return ENXIO; + + arginfo = QCSCM_ARGINFO_NUM(nitems(args)); + arginfo |= QCSCM_ARGINFO_TYPE(0, QCSCM_ARGINFO_TYPE_VAL); + args[0] = peripheral; + + /* Make call into TEE */ + ret = qcscm_smc_call(sc, ARM_SMCCC_OWNER_SIP, QCSCM_SVC_PIL, + QCSCM_PIL_PAS_SHUTDOWN, arginfo, args, nitems(args), res); + + /* If the call succeeded, check the response status */ + if (ret == 0) + ret = res[0]; + + return ret; +} + /* DMA code */ struct qcscm_dmamem * qcscm_dmamem_alloc(struct qcscm_softc *sc, bus_size_t size, bus_size_t align) diff --git a/sys/sys/device.h b/sys/sys/device.h index 712bcb4e3ae..89ed62ebfae 100644 --- a/sys/sys/device.h +++ b/sys/sys/device.h @@ -1,4 +1,4 @@ -/* $OpenBSD: device.h,v 1.67 2024/05/28 09:40:40 kettenis Exp $ */ +/* $OpenBSD: device.h,v 1.68 2024/08/04 15:30:08 kettenis Exp $ */ /* $NetBSD: device.h,v 1.15 1996/04/09 20:55:24 cgd Exp $ */ /* @@ -231,7 +231,7 @@ void device_register(struct device *, void *); void device_register_wakeup(struct device *); int loadfirmware(const char *name, u_char **bufp, size_t *buflen); -#define FIRMWARE_MAX 15*1024*1024 +#define FIRMWARE_MAX 24*1024*1024 /* compatibility definitions */ #define config_found(d, a, p) config_found_sm((d), (a), (p), NULL) -- 2.20.1