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, ®, 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, ®, 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(®, 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(®, 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