1*c95a3ae2Sjmcneill /* $NetBSD: qcomspmi.c,v 1.1 2024/12/30 12:31:10 jmcneill Exp $ */ 2*c95a3ae2Sjmcneill /* $OpenBSD: qcspmi.c,v 1.6 2024/08/14 10:54:58 mglocker Exp $ */ 3*c95a3ae2Sjmcneill /* 4*c95a3ae2Sjmcneill * Copyright (c) 2022 Patrick Wildt <patrick@blueri.se> 5*c95a3ae2Sjmcneill * 6*c95a3ae2Sjmcneill * Permission to use, copy, modify, and distribute this software for any 7*c95a3ae2Sjmcneill * purpose with or without fee is hereby granted, provided that the above 8*c95a3ae2Sjmcneill * copyright notice and this permission notice appear in all copies. 9*c95a3ae2Sjmcneill * 10*c95a3ae2Sjmcneill * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11*c95a3ae2Sjmcneill * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12*c95a3ae2Sjmcneill * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13*c95a3ae2Sjmcneill * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14*c95a3ae2Sjmcneill * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15*c95a3ae2Sjmcneill * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16*c95a3ae2Sjmcneill * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17*c95a3ae2Sjmcneill */ 18*c95a3ae2Sjmcneill 19*c95a3ae2Sjmcneill #include <sys/param.h> 20*c95a3ae2Sjmcneill #include <sys/kmem.h> 21*c95a3ae2Sjmcneill #include <sys/systm.h> 22*c95a3ae2Sjmcneill #include <sys/bus.h> 23*c95a3ae2Sjmcneill #include <sys/device.h> 24*c95a3ae2Sjmcneill 25*c95a3ae2Sjmcneill #include <dev/acpi/acpivar.h> 26*c95a3ae2Sjmcneill 27*c95a3ae2Sjmcneill /* SPMI commands */ 28*c95a3ae2Sjmcneill #define SPMI_CMD_EXT_WRITEL 0x30 29*c95a3ae2Sjmcneill #define SPMI_CMD_EXT_READL 0x38 30*c95a3ae2Sjmcneill 31*c95a3ae2Sjmcneill /* Core registers. */ 32*c95a3ae2Sjmcneill #define SPMI_VERSION 0x00 33*c95a3ae2Sjmcneill #define SPMI_VERSION_V2_MIN 0x20010000 34*c95a3ae2Sjmcneill #define SPMI_VERSION_V3_MIN 0x30000000 35*c95a3ae2Sjmcneill #define SPMI_VERSION_V5_MIN 0x50000000 36*c95a3ae2Sjmcneill #define SPMI_VERSION_V7_MIN 0x70000000 37*c95a3ae2Sjmcneill #define SPMI_ARB_APID_MAP(sc, x) ((sc)->sc_arb_apid_map + (x) * 0x4) 38*c95a3ae2Sjmcneill #define SPMI_ARB_APID_MAP_PPID_MASK 0xfff 39*c95a3ae2Sjmcneill #define SPMI_ARB_APID_MAP_PPID_SHIFT 8 40*c95a3ae2Sjmcneill #define SPMI_ARB_APID_MAP_IRQ_OWNER (1 << 14) 41*c95a3ae2Sjmcneill 42*c95a3ae2Sjmcneill /* Channel registers. */ 43*c95a3ae2Sjmcneill #define SPMI_CHAN_OFF(sc, x) ((sc)->sc_chan_stride * (x)) 44*c95a3ae2Sjmcneill #define SPMI_OBSV_OFF(sc, x, y) \ 45*c95a3ae2Sjmcneill ((sc)->sc_obsv_ee_stride * (x) + (sc)->sc_obsv_apid_stride * (y)) 46*c95a3ae2Sjmcneill #define SPMI_COMMAND 0x00 47*c95a3ae2Sjmcneill #define SPMI_COMMAND_OP_EXT_WRITEL (0 << 27) 48*c95a3ae2Sjmcneill #define SPMI_COMMAND_OP_EXT_READL (1 << 27) 49*c95a3ae2Sjmcneill #define SPMI_COMMAND_OP_EXT_WRITE (2 << 27) 50*c95a3ae2Sjmcneill #define SPMI_COMMAND_OP_RESET (3 << 27) 51*c95a3ae2Sjmcneill #define SPMI_COMMAND_OP_SLEEP (4 << 27) 52*c95a3ae2Sjmcneill #define SPMI_COMMAND_OP_SHUTDOWN (5 << 27) 53*c95a3ae2Sjmcneill #define SPMI_COMMAND_OP_WAKEUP (6 << 27) 54*c95a3ae2Sjmcneill #define SPMI_COMMAND_OP_AUTHENTICATE (7 << 27) 55*c95a3ae2Sjmcneill #define SPMI_COMMAND_OP_MSTR_READ (8 << 27) 56*c95a3ae2Sjmcneill #define SPMI_COMMAND_OP_MSTR_WRITE (9 << 27) 57*c95a3ae2Sjmcneill #define SPMI_COMMAND_OP_EXT_READ (13 << 27) 58*c95a3ae2Sjmcneill #define SPMI_COMMAND_OP_WRITE (14 << 27) 59*c95a3ae2Sjmcneill #define SPMI_COMMAND_OP_READ (15 << 27) 60*c95a3ae2Sjmcneill #define SPMI_COMMAND_OP_ZERO_WRITE (16 << 27) 61*c95a3ae2Sjmcneill #define SPMI_COMMAND_ADDR(x) (((x) & 0xff) << 4) 62*c95a3ae2Sjmcneill #define SPMI_COMMAND_LEN(x) (((x) & 0x7) << 0) 63*c95a3ae2Sjmcneill #define SPMI_CONFIG 0x04 64*c95a3ae2Sjmcneill #define SPMI_STATUS 0x08 65*c95a3ae2Sjmcneill #define SPMI_STATUS_DONE (1 << 0) 66*c95a3ae2Sjmcneill #define SPMI_STATUS_FAILURE (1 << 1) 67*c95a3ae2Sjmcneill #define SPMI_STATUS_DENIED (1 << 2) 68*c95a3ae2Sjmcneill #define SPMI_STATUS_DROPPED (1 << 3) 69*c95a3ae2Sjmcneill #define SPMI_WDATA0 0x10 70*c95a3ae2Sjmcneill #define SPMI_WDATA1 0x14 71*c95a3ae2Sjmcneill #define SPMI_RDATA0 0x18 72*c95a3ae2Sjmcneill #define SPMI_RDATA1 0x1c 73*c95a3ae2Sjmcneill #define SPMI_ACC_ENABLE 0x100 74*c95a3ae2Sjmcneill #define SPMI_ACC_ENABLE_BIT (1 << 0) 75*c95a3ae2Sjmcneill #define SPMI_IRQ_STATUS 0x104 76*c95a3ae2Sjmcneill #define SPMI_IRQ_CLEAR 0x108 77*c95a3ae2Sjmcneill 78*c95a3ae2Sjmcneill /* Intr registers */ 79*c95a3ae2Sjmcneill #define SPMI_OWNER_ACC_STATUS(sc, x, y) \ 80*c95a3ae2Sjmcneill ((sc)->sc_chan_stride * (x) + 0x4 * (y)) 81*c95a3ae2Sjmcneill 82*c95a3ae2Sjmcneill /* Config registers */ 83*c95a3ae2Sjmcneill #define SPMI_OWNERSHIP_TABLE(sc, x) ((sc)->sc_ownership_table + (x) * 0x4) 84*c95a3ae2Sjmcneill #define SPMI_OWNERSHIP_TABLE_OWNER(x) ((x) & 0x7) 85*c95a3ae2Sjmcneill 86*c95a3ae2Sjmcneill /* Misc */ 87*c95a3ae2Sjmcneill #define SPMI_MAX_PERIPH 1024 88*c95a3ae2Sjmcneill #define SPMI_MAX_PPID 4096 89*c95a3ae2Sjmcneill #define SPMI_PPID_TO_APID_VALID (1U << 15) 90*c95a3ae2Sjmcneill #define SPMI_PPID_TO_APID_MASK (0x7fff) 91*c95a3ae2Sjmcneill 92*c95a3ae2Sjmcneill /* Intr commands */ 93*c95a3ae2Sjmcneill #define INTR_RT_STS 0x10 94*c95a3ae2Sjmcneill #define INTR_SET_TYPE 0x11 95*c95a3ae2Sjmcneill #define INTR_POLARITY_HIGH 0x12 96*c95a3ae2Sjmcneill #define INTR_POLARITY_LOW 0x13 97*c95a3ae2Sjmcneill #define INTR_LATCHED_CLR 0x14 98*c95a3ae2Sjmcneill #define INTR_EN_SET 0x15 99*c95a3ae2Sjmcneill #define INTR_EN_CLR 0x16 100*c95a3ae2Sjmcneill #define INTR_LATCHED_STS 0x18 101*c95a3ae2Sjmcneill 102*c95a3ae2Sjmcneill #define HREAD4(sc, obj, reg) \ 103*c95a3ae2Sjmcneill bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, \ 104*c95a3ae2Sjmcneill (sc)->sc_data->regs[obj] + (reg)) 105*c95a3ae2Sjmcneill #define HWRITE4(sc, obj, reg, val) \ 106*c95a3ae2Sjmcneill bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, \ 107*c95a3ae2Sjmcneill (sc)->sc_data->regs[obj] + (reg), (val)) 108*c95a3ae2Sjmcneill 109*c95a3ae2Sjmcneill #define QCSPMI_REG_CORE 0 110*c95a3ae2Sjmcneill #define QCSPMI_REG_CHNLS 1 111*c95a3ae2Sjmcneill #define QCSPMI_REG_OBSRVR 2 112*c95a3ae2Sjmcneill #define QCSPMI_REG_INTR 3 113*c95a3ae2Sjmcneill #define QCSPMI_REG_CNFG 4 114*c95a3ae2Sjmcneill #define QCSPMI_REG_MAX 5 115*c95a3ae2Sjmcneill 116*c95a3ae2Sjmcneill struct qcspmi_apid { 117*c95a3ae2Sjmcneill uint16_t ppid; 118*c95a3ae2Sjmcneill uint8_t write_ee; 119*c95a3ae2Sjmcneill uint8_t irq_ee; 120*c95a3ae2Sjmcneill }; 121*c95a3ae2Sjmcneill 122*c95a3ae2Sjmcneill struct qcspmi_data { 123*c95a3ae2Sjmcneill bus_size_t regs[QCSPMI_REG_MAX]; 124*c95a3ae2Sjmcneill int ee; 125*c95a3ae2Sjmcneill }; 126*c95a3ae2Sjmcneill 127*c95a3ae2Sjmcneill struct qcspmi_softc { 128*c95a3ae2Sjmcneill device_t sc_dev; 129*c95a3ae2Sjmcneill 130*c95a3ae2Sjmcneill bus_space_tag_t sc_iot; 131*c95a3ae2Sjmcneill bus_space_handle_t sc_ioh; 132*c95a3ae2Sjmcneill 133*c95a3ae2Sjmcneill const struct qcspmi_data *sc_data; 134*c95a3ae2Sjmcneill 135*c95a3ae2Sjmcneill int sc_ee; 136*c95a3ae2Sjmcneill 137*c95a3ae2Sjmcneill struct qcspmi_apid sc_apid[SPMI_MAX_PERIPH]; 138*c95a3ae2Sjmcneill uint16_t sc_ppid_to_apid[SPMI_MAX_PPID]; 139*c95a3ae2Sjmcneill uint16_t sc_max_periph; 140*c95a3ae2Sjmcneill bus_size_t sc_chan_stride; 141*c95a3ae2Sjmcneill bus_size_t sc_obsv_ee_stride; 142*c95a3ae2Sjmcneill bus_size_t sc_obsv_apid_stride; 143*c95a3ae2Sjmcneill bus_size_t sc_arb_apid_map; 144*c95a3ae2Sjmcneill bus_size_t sc_ownership_table; 145*c95a3ae2Sjmcneill }; 146*c95a3ae2Sjmcneill 147*c95a3ae2Sjmcneill static int qcspmi_match(device_t, cfdata_t, void *); 148*c95a3ae2Sjmcneill static void qcspmi_attach(device_t, device_t, void *); 149*c95a3ae2Sjmcneill 150*c95a3ae2Sjmcneill int qcspmi_cmd_read(struct qcspmi_softc *, uint8_t, uint8_t, 151*c95a3ae2Sjmcneill uint16_t, void *, size_t); 152*c95a3ae2Sjmcneill int qcspmi_cmd_write(struct qcspmi_softc *, uint8_t, uint8_t, uint16_t, 153*c95a3ae2Sjmcneill const void *, size_t); 154*c95a3ae2Sjmcneill 155*c95a3ae2Sjmcneill CFATTACH_DECL_NEW(qcomspmi, sizeof(struct qcspmi_softc), 156*c95a3ae2Sjmcneill qcspmi_match, qcspmi_attach, NULL, NULL); 157*c95a3ae2Sjmcneill 158*c95a3ae2Sjmcneill static const struct qcspmi_data qcspmi_x1e_data = { 159*c95a3ae2Sjmcneill .ee = 0, 160*c95a3ae2Sjmcneill .regs = { 161*c95a3ae2Sjmcneill [QCSPMI_REG_CORE] = 0x0, 162*c95a3ae2Sjmcneill [QCSPMI_REG_CHNLS] = 0x100000, 163*c95a3ae2Sjmcneill [QCSPMI_REG_OBSRVR] = 0x40000, 164*c95a3ae2Sjmcneill [QCSPMI_REG_INTR] = 0xc0000, 165*c95a3ae2Sjmcneill [QCSPMI_REG_CNFG] = 0x2d000, 166*c95a3ae2Sjmcneill }, 167*c95a3ae2Sjmcneill }; 168*c95a3ae2Sjmcneill 169*c95a3ae2Sjmcneill static const struct device_compatible_entry compat_data[] = { 170*c95a3ae2Sjmcneill { .compat = "QCOM0C0B", .data = &qcspmi_x1e_data }, 171*c95a3ae2Sjmcneill DEVICE_COMPAT_EOL 172*c95a3ae2Sjmcneill }; 173*c95a3ae2Sjmcneill 174*c95a3ae2Sjmcneill static int 175*c95a3ae2Sjmcneill qcspmi_match(device_t parent, cfdata_t match, void *aux) 176*c95a3ae2Sjmcneill { 177*c95a3ae2Sjmcneill struct acpi_attach_args *aa = aux; 178*c95a3ae2Sjmcneill 179*c95a3ae2Sjmcneill return acpi_compatible_match(aa, compat_data); 180*c95a3ae2Sjmcneill } 181*c95a3ae2Sjmcneill 182*c95a3ae2Sjmcneill void 183*c95a3ae2Sjmcneill qcspmi_attach(device_t parent, device_t self, void *aux) 184*c95a3ae2Sjmcneill { 185*c95a3ae2Sjmcneill struct acpi_attach_args *aa = aux; 186*c95a3ae2Sjmcneill struct qcspmi_softc *sc = device_private(self); 187*c95a3ae2Sjmcneill struct qcspmi_apid *apid, *last_apid; 188*c95a3ae2Sjmcneill struct acpi_resources res; 189*c95a3ae2Sjmcneill struct acpi_mem *mem; 190*c95a3ae2Sjmcneill uint32_t val, ppid, irq_own; 191*c95a3ae2Sjmcneill ACPI_STATUS rv; 192*c95a3ae2Sjmcneill int error, i; 193*c95a3ae2Sjmcneill 194*c95a3ae2Sjmcneill rv = acpi_resource_parse(sc->sc_dev, aa->aa_node->ad_handle, "_CRS", 195*c95a3ae2Sjmcneill &res, &acpi_resource_parse_ops_default); 196*c95a3ae2Sjmcneill if (ACPI_FAILURE(rv)) { 197*c95a3ae2Sjmcneill return; 198*c95a3ae2Sjmcneill } 199*c95a3ae2Sjmcneill 200*c95a3ae2Sjmcneill mem = acpi_res_mem(&res, 0); 201*c95a3ae2Sjmcneill if (mem == NULL) { 202*c95a3ae2Sjmcneill aprint_error_dev(self, "couldn't find mem resource\n"); 203*c95a3ae2Sjmcneill goto done; 204*c95a3ae2Sjmcneill } 205*c95a3ae2Sjmcneill 206*c95a3ae2Sjmcneill sc->sc_dev = self; 207*c95a3ae2Sjmcneill sc->sc_data = acpi_compatible_lookup(aa, compat_data)->data; 208*c95a3ae2Sjmcneill sc->sc_iot = aa->aa_memt; 209*c95a3ae2Sjmcneill error = bus_space_map(sc->sc_iot, mem->ar_base, mem->ar_length, 0, 210*c95a3ae2Sjmcneill &sc->sc_ioh); 211*c95a3ae2Sjmcneill if (error != 0) { 212*c95a3ae2Sjmcneill aprint_error_dev(self, "couldn't map registers\n"); 213*c95a3ae2Sjmcneill goto done; 214*c95a3ae2Sjmcneill } 215*c95a3ae2Sjmcneill 216*c95a3ae2Sjmcneill /* Support only version 5 and 7 for now */ 217*c95a3ae2Sjmcneill val = HREAD4(sc, QCSPMI_REG_CORE, SPMI_VERSION); 218*c95a3ae2Sjmcneill if (val < SPMI_VERSION_V5_MIN) { 219*c95a3ae2Sjmcneill printf(": unsupported version 0x%08x\n", val); 220*c95a3ae2Sjmcneill return; 221*c95a3ae2Sjmcneill } 222*c95a3ae2Sjmcneill 223*c95a3ae2Sjmcneill if (val < SPMI_VERSION_V7_MIN) { 224*c95a3ae2Sjmcneill sc->sc_max_periph = 512; 225*c95a3ae2Sjmcneill sc->sc_chan_stride = 0x10000; 226*c95a3ae2Sjmcneill sc->sc_obsv_ee_stride = 0x10000; 227*c95a3ae2Sjmcneill sc->sc_obsv_apid_stride = 0x00080; 228*c95a3ae2Sjmcneill sc->sc_arb_apid_map = 0x00900; 229*c95a3ae2Sjmcneill sc->sc_ownership_table = 0x00700; 230*c95a3ae2Sjmcneill } else { 231*c95a3ae2Sjmcneill sc->sc_max_periph = 1024; 232*c95a3ae2Sjmcneill sc->sc_chan_stride = 0x01000; 233*c95a3ae2Sjmcneill sc->sc_obsv_ee_stride = 0x08000; 234*c95a3ae2Sjmcneill sc->sc_obsv_apid_stride = 0x00020; 235*c95a3ae2Sjmcneill sc->sc_arb_apid_map = 0x02000; 236*c95a3ae2Sjmcneill sc->sc_ownership_table = 0x00000; 237*c95a3ae2Sjmcneill } 238*c95a3ae2Sjmcneill 239*c95a3ae2Sjmcneill KASSERT(sc->sc_max_periph <= SPMI_MAX_PERIPH); 240*c95a3ae2Sjmcneill 241*c95a3ae2Sjmcneill sc->sc_ee = sc->sc_data->ee; 242*c95a3ae2Sjmcneill 243*c95a3ae2Sjmcneill for (i = 0; i < sc->sc_max_periph; i++) { 244*c95a3ae2Sjmcneill val = HREAD4(sc, QCSPMI_REG_CORE, SPMI_ARB_APID_MAP(sc, i)); 245*c95a3ae2Sjmcneill if (!val) 246*c95a3ae2Sjmcneill continue; 247*c95a3ae2Sjmcneill ppid = (val >> SPMI_ARB_APID_MAP_PPID_SHIFT) & 248*c95a3ae2Sjmcneill SPMI_ARB_APID_MAP_PPID_MASK; 249*c95a3ae2Sjmcneill irq_own = val & SPMI_ARB_APID_MAP_IRQ_OWNER; 250*c95a3ae2Sjmcneill val = HREAD4(sc, QCSPMI_REG_CNFG, SPMI_OWNERSHIP_TABLE(sc, i)); 251*c95a3ae2Sjmcneill apid = &sc->sc_apid[i]; 252*c95a3ae2Sjmcneill apid->write_ee = SPMI_OWNERSHIP_TABLE_OWNER(val); 253*c95a3ae2Sjmcneill apid->irq_ee = 0xff; 254*c95a3ae2Sjmcneill if (irq_own) 255*c95a3ae2Sjmcneill apid->irq_ee = apid->write_ee; 256*c95a3ae2Sjmcneill last_apid = &sc->sc_apid[sc->sc_ppid_to_apid[ppid] & 257*c95a3ae2Sjmcneill SPMI_PPID_TO_APID_MASK]; 258*c95a3ae2Sjmcneill if (!(sc->sc_ppid_to_apid[ppid] & SPMI_PPID_TO_APID_VALID) || 259*c95a3ae2Sjmcneill apid->write_ee == sc->sc_ee) { 260*c95a3ae2Sjmcneill sc->sc_ppid_to_apid[ppid] = SPMI_PPID_TO_APID_VALID | i; 261*c95a3ae2Sjmcneill } else if ((sc->sc_ppid_to_apid[ppid] & 262*c95a3ae2Sjmcneill SPMI_PPID_TO_APID_VALID) && irq_own && 263*c95a3ae2Sjmcneill last_apid->write_ee == sc->sc_ee) { 264*c95a3ae2Sjmcneill last_apid->irq_ee = apid->irq_ee; 265*c95a3ae2Sjmcneill } 266*c95a3ae2Sjmcneill } 267*c95a3ae2Sjmcneill 268*c95a3ae2Sjmcneill done: 269*c95a3ae2Sjmcneill acpi_resource_cleanup(&res); 270*c95a3ae2Sjmcneill } 271*c95a3ae2Sjmcneill 272*c95a3ae2Sjmcneill int 273*c95a3ae2Sjmcneill qcspmi_cmd_read(struct qcspmi_softc *sc, uint8_t sid, uint8_t cmd, 274*c95a3ae2Sjmcneill uint16_t addr, void *buf, size_t len) 275*c95a3ae2Sjmcneill { 276*c95a3ae2Sjmcneill uint8_t *cbuf = buf; 277*c95a3ae2Sjmcneill uint32_t reg; 278*c95a3ae2Sjmcneill uint16_t apid, ppid; 279*c95a3ae2Sjmcneill int bc = len - 1; 280*c95a3ae2Sjmcneill int i; 281*c95a3ae2Sjmcneill 282*c95a3ae2Sjmcneill if (len == 0 || len > 8) 283*c95a3ae2Sjmcneill return EINVAL; 284*c95a3ae2Sjmcneill 285*c95a3ae2Sjmcneill /* TODO: support more types */ 286*c95a3ae2Sjmcneill if (cmd != SPMI_CMD_EXT_READL) 287*c95a3ae2Sjmcneill return EINVAL; 288*c95a3ae2Sjmcneill 289*c95a3ae2Sjmcneill ppid = (sid << 8) | (addr >> 8); 290*c95a3ae2Sjmcneill if (!(sc->sc_ppid_to_apid[ppid] & SPMI_PPID_TO_APID_VALID)) 291*c95a3ae2Sjmcneill return ENXIO; 292*c95a3ae2Sjmcneill apid = sc->sc_ppid_to_apid[ppid] & SPMI_PPID_TO_APID_MASK; 293*c95a3ae2Sjmcneill 294*c95a3ae2Sjmcneill HWRITE4(sc, QCSPMI_REG_OBSRVR, 295*c95a3ae2Sjmcneill SPMI_OBSV_OFF(sc, sc->sc_ee, apid) + SPMI_COMMAND, 296*c95a3ae2Sjmcneill SPMI_COMMAND_OP_EXT_READL | SPMI_COMMAND_ADDR(addr) | 297*c95a3ae2Sjmcneill SPMI_COMMAND_LEN(bc)); 298*c95a3ae2Sjmcneill 299*c95a3ae2Sjmcneill for (i = 1000; i > 0; i--) { 300*c95a3ae2Sjmcneill reg = HREAD4(sc, QCSPMI_REG_OBSRVR, 301*c95a3ae2Sjmcneill SPMI_OBSV_OFF(sc, sc->sc_ee, apid) + SPMI_STATUS); 302*c95a3ae2Sjmcneill if (reg & SPMI_STATUS_DONE) 303*c95a3ae2Sjmcneill break; 304*c95a3ae2Sjmcneill if (reg & SPMI_STATUS_FAILURE) { 305*c95a3ae2Sjmcneill printf(": transaction failed\n"); 306*c95a3ae2Sjmcneill return EIO; 307*c95a3ae2Sjmcneill } 308*c95a3ae2Sjmcneill if (reg & SPMI_STATUS_DENIED) { 309*c95a3ae2Sjmcneill printf(": transaction denied\n"); 310*c95a3ae2Sjmcneill return EIO; 311*c95a3ae2Sjmcneill } 312*c95a3ae2Sjmcneill if (reg & SPMI_STATUS_DROPPED) { 313*c95a3ae2Sjmcneill printf(": transaction dropped\n"); 314*c95a3ae2Sjmcneill return EIO; 315*c95a3ae2Sjmcneill } 316*c95a3ae2Sjmcneill } 317*c95a3ae2Sjmcneill if (i == 0) { 318*c95a3ae2Sjmcneill printf("\n"); 319*c95a3ae2Sjmcneill return ETIMEDOUT; 320*c95a3ae2Sjmcneill } 321*c95a3ae2Sjmcneill 322*c95a3ae2Sjmcneill if (len > 0) { 323*c95a3ae2Sjmcneill reg = HREAD4(sc, QCSPMI_REG_OBSRVR, 324*c95a3ae2Sjmcneill SPMI_OBSV_OFF(sc, sc->sc_ee, apid) + SPMI_RDATA0); 325*c95a3ae2Sjmcneill memcpy(cbuf, ®, MIN(len, 4)); 326*c95a3ae2Sjmcneill cbuf += MIN(len, 4); 327*c95a3ae2Sjmcneill len -= MIN(len, 4); 328*c95a3ae2Sjmcneill } 329*c95a3ae2Sjmcneill if (len > 0) { 330*c95a3ae2Sjmcneill reg = HREAD4(sc, QCSPMI_REG_OBSRVR, 331*c95a3ae2Sjmcneill SPMI_OBSV_OFF(sc, sc->sc_ee, apid) + SPMI_RDATA1); 332*c95a3ae2Sjmcneill memcpy(cbuf, ®, MIN(len, 4)); 333*c95a3ae2Sjmcneill cbuf += MIN(len, 4); 334*c95a3ae2Sjmcneill len -= MIN(len, 4); 335*c95a3ae2Sjmcneill } 336*c95a3ae2Sjmcneill 337*c95a3ae2Sjmcneill return 0; 338*c95a3ae2Sjmcneill } 339*c95a3ae2Sjmcneill 340*c95a3ae2Sjmcneill int 341*c95a3ae2Sjmcneill qcspmi_cmd_write(struct qcspmi_softc *sc, uint8_t sid, uint8_t cmd, 342*c95a3ae2Sjmcneill uint16_t addr, const void *buf, size_t len) 343*c95a3ae2Sjmcneill { 344*c95a3ae2Sjmcneill const uint8_t *cbuf = buf; 345*c95a3ae2Sjmcneill uint32_t reg; 346*c95a3ae2Sjmcneill uint16_t apid, ppid; 347*c95a3ae2Sjmcneill int bc = len - 1; 348*c95a3ae2Sjmcneill int i; 349*c95a3ae2Sjmcneill 350*c95a3ae2Sjmcneill if (len == 0 || len > 8) 351*c95a3ae2Sjmcneill return EINVAL; 352*c95a3ae2Sjmcneill 353*c95a3ae2Sjmcneill /* TODO: support more types */ 354*c95a3ae2Sjmcneill if (cmd != SPMI_CMD_EXT_WRITEL) 355*c95a3ae2Sjmcneill return EINVAL; 356*c95a3ae2Sjmcneill 357*c95a3ae2Sjmcneill ppid = (sid << 8) | (addr >> 8); 358*c95a3ae2Sjmcneill if (!(sc->sc_ppid_to_apid[ppid] & SPMI_PPID_TO_APID_VALID)) 359*c95a3ae2Sjmcneill return ENXIO; 360*c95a3ae2Sjmcneill apid = sc->sc_ppid_to_apid[ppid] & SPMI_PPID_TO_APID_MASK; 361*c95a3ae2Sjmcneill 362*c95a3ae2Sjmcneill if (sc->sc_apid[apid].write_ee != sc->sc_ee) 363*c95a3ae2Sjmcneill return EPERM; 364*c95a3ae2Sjmcneill 365*c95a3ae2Sjmcneill if (len > 0) { 366*c95a3ae2Sjmcneill memcpy(®, cbuf, MIN(len, 4)); 367*c95a3ae2Sjmcneill HWRITE4(sc, QCSPMI_REG_CHNLS, SPMI_CHAN_OFF(sc, apid) + 368*c95a3ae2Sjmcneill SPMI_WDATA0, reg); 369*c95a3ae2Sjmcneill cbuf += MIN(len, 4); 370*c95a3ae2Sjmcneill len -= MIN(len, 4); 371*c95a3ae2Sjmcneill } 372*c95a3ae2Sjmcneill if (len > 0) { 373*c95a3ae2Sjmcneill memcpy(®, cbuf, MIN(len, 4)); 374*c95a3ae2Sjmcneill HWRITE4(sc, QCSPMI_REG_CHNLS, SPMI_CHAN_OFF(sc, apid) + 375*c95a3ae2Sjmcneill SPMI_WDATA1, reg); 376*c95a3ae2Sjmcneill cbuf += MIN(len, 4); 377*c95a3ae2Sjmcneill len -= MIN(len, 4); 378*c95a3ae2Sjmcneill } 379*c95a3ae2Sjmcneill 380*c95a3ae2Sjmcneill HWRITE4(sc, QCSPMI_REG_CHNLS, SPMI_CHAN_OFF(sc, apid) + SPMI_COMMAND, 381*c95a3ae2Sjmcneill SPMI_COMMAND_OP_EXT_WRITEL | SPMI_COMMAND_ADDR(addr) | 382*c95a3ae2Sjmcneill SPMI_COMMAND_LEN(bc)); 383*c95a3ae2Sjmcneill 384*c95a3ae2Sjmcneill for (i = 1000; i > 0; i--) { 385*c95a3ae2Sjmcneill reg = HREAD4(sc, QCSPMI_REG_CHNLS, SPMI_CHAN_OFF(sc, apid) + 386*c95a3ae2Sjmcneill SPMI_STATUS); 387*c95a3ae2Sjmcneill if (reg & SPMI_STATUS_DONE) 388*c95a3ae2Sjmcneill break; 389*c95a3ae2Sjmcneill } 390*c95a3ae2Sjmcneill if (i == 0) 391*c95a3ae2Sjmcneill return ETIMEDOUT; 392*c95a3ae2Sjmcneill 393*c95a3ae2Sjmcneill if (reg & SPMI_STATUS_FAILURE || 394*c95a3ae2Sjmcneill reg & SPMI_STATUS_DENIED || 395*c95a3ae2Sjmcneill reg & SPMI_STATUS_DROPPED) 396*c95a3ae2Sjmcneill return EIO; 397*c95a3ae2Sjmcneill 398*c95a3ae2Sjmcneill return 0; 399*c95a3ae2Sjmcneill } 400