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