1/* $OpenBSD: qcaoss.c,v 1.1 2023/05/23 14:10:27 patrick 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 24#include <machine/bus.h> 25#include <machine/fdt.h> 26 27#include <dev/ofw/openfirm.h> 28#include <dev/ofw/ofw_misc.h> 29#include <dev/ofw/fdt.h> 30 31#define AOSS_DESC_MAGIC 0x0 32#define AOSS_DESC_VERSION 0x4 33#define AOSS_DESC_FEATURES 0x8 34#define AOSS_DESC_UCORE_LINK_STATE 0xc 35#define AOSS_DESC_UCORE_LINK_STATE_ACK 0x10 36#define AOSS_DESC_UCORE_CH_STATE 0x14 37#define AOSS_DESC_UCORE_CH_STATE_ACK 0x18 38#define AOSS_DESC_UCORE_MBOX_SIZE 0x1c 39#define AOSS_DESC_UCORE_MBOX_OFFSET 0x20 40#define AOSS_DESC_MCORE_LINK_STATE 0x24 41#define AOSS_DESC_MCORE_LINK_STATE_ACK 0x28 42#define AOSS_DESC_MCORE_CH_STATE 0x2c 43#define AOSS_DESC_MCORE_CH_STATE_ACK 0x30 44#define AOSS_DESC_MCORE_MBOX_SIZE 0x34 45#define AOSS_DESC_MCORE_MBOX_OFFSET 0x38 46 47#define AOSS_MAGIC 0x4d41494c 48#define AOSS_VERSION 1 49 50#define AOSS_STATE_UP (0xffffU << 0) 51#define AOSS_STATE_DOWN (0xffffU << 16) 52 53#define HREAD4(sc, reg) \ 54 (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))) 55#define HWRITE4(sc, reg, val) \ 56 bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) 57 58struct qcaoss_softc { 59 struct device sc_dev; 60 bus_space_tag_t sc_iot; 61 bus_space_handle_t sc_ioh; 62 63 size_t sc_offset; 64 size_t sc_size; 65 66 struct mbox_channel *sc_mc; 67}; 68 69struct qcaoss_softc *qcaoss_sc; 70 71int qcaoss_match(struct device *, void *, void *); 72void qcaoss_attach(struct device *, struct device *, void *); 73 74const struct cfattach qcaoss_ca = { 75 sizeof (struct qcaoss_softc), qcaoss_match, qcaoss_attach 76}; 77 78struct cfdriver qcaoss_cd = { 79 NULL, "qcaoss", DV_DULL 80}; 81 82int 83qcaoss_match(struct device *parent, void *match, void *aux) 84{ 85 struct fdt_attach_args *faa = aux; 86 87 return OF_is_compatible(faa->fa_node, "qcom,aoss-qmp"); 88} 89 90void 91qcaoss_attach(struct device *parent, struct device *self, void *aux) 92{ 93 struct qcaoss_softc *sc = (struct qcaoss_softc *)self; 94 struct fdt_attach_args *faa = aux; 95 int i; 96 97 if (faa->fa_nreg < 1) { 98 printf(": no registers\n"); 99 return; 100 } 101 102 sc->sc_iot = faa->fa_iot; 103 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, 104 faa->fa_reg[0].size, 0, &sc->sc_ioh)) { 105 printf(": can't map registers\n"); 106 return; 107 } 108 109 sc->sc_mc = mbox_channel_idx(faa->fa_node, 0, NULL); 110 if (sc->sc_mc == NULL) { 111 bus_space_unmap(sc->sc_iot, sc->sc_ioh, faa->fa_reg[0].size); 112 printf(": can't find mbox\n"); 113 return; 114 } 115 116 if (HREAD4(sc, AOSS_DESC_MAGIC) != AOSS_MAGIC || 117 HREAD4(sc, AOSS_DESC_VERSION) != AOSS_VERSION) { 118 printf(": invalid QMP info\n"); 119 return; 120 } 121 122 sc->sc_offset = HREAD4(sc, AOSS_DESC_MCORE_MBOX_OFFSET); 123 sc->sc_size = HREAD4(sc, AOSS_DESC_MCORE_MBOX_SIZE); 124 if (sc->sc_size == 0) { 125 printf(": invalid mailbox size\n"); 126 return; 127 } 128 129 HWRITE4(sc, AOSS_DESC_UCORE_LINK_STATE_ACK, 130 HREAD4(sc, AOSS_DESC_UCORE_LINK_STATE)); 131 132 HWRITE4(sc, AOSS_DESC_MCORE_LINK_STATE, AOSS_STATE_UP); 133 mbox_send(sc->sc_mc, NULL, 0); 134 135 for (i = 1000; i > 0; i--) { 136 if (HREAD4(sc, AOSS_DESC_MCORE_LINK_STATE_ACK) == AOSS_STATE_UP) 137 break; 138 delay(1000); 139 } 140 if (i == 0) { 141 printf(": didn't get link state ack\n"); 142 return; 143 } 144 145 HWRITE4(sc, AOSS_DESC_MCORE_CH_STATE, AOSS_STATE_UP); 146 mbox_send(sc->sc_mc, NULL, 0); 147 148 for (i = 1000; i > 0; i--) { 149 if (HREAD4(sc, AOSS_DESC_UCORE_CH_STATE) == AOSS_STATE_UP) 150 break; 151 delay(1000); 152 } 153 if (i == 0) { 154 printf(": didn't get open channel\n"); 155 return; 156 } 157 158 HWRITE4(sc, AOSS_DESC_UCORE_CH_STATE_ACK, AOSS_STATE_UP); 159 mbox_send(sc->sc_mc, NULL, 0); 160 161 for (i = 1000; i > 0; i--) { 162 if (HREAD4(sc, AOSS_DESC_MCORE_CH_STATE_ACK) == AOSS_STATE_UP) 163 break; 164 delay(1000); 165 } 166 if (i == 0) { 167 printf(": didn't get channel ack\n"); 168 return; 169 } 170 171 printf("\n"); 172 173 qcaoss_sc = sc; 174} 175 176int 177qcaoss_send(char *data, size_t len) 178{ 179 struct qcaoss_softc *sc = qcaoss_sc; 180 uint32_t reg; 181 int i; 182 183 if (sc == NULL) 184 return ENXIO; 185 186 if (data == NULL || sizeof(uint32_t) + len > sc->sc_size || 187 (len % sizeof(uint32_t)) != 0) 188 return EINVAL; 189 190 /* Write data first, needs to be 32-bit access. */ 191 for (i = 0; i < len; i += 4) { 192 memcpy(®, data + i, sizeof(reg)); 193 HWRITE4(sc, sc->sc_offset + sizeof(uint32_t) + i, reg); 194 } 195 196 /* Commit transaction by writing length. */ 197 HWRITE4(sc, sc->sc_offset, len); 198 199 /* Assert it's stored and inform peer. */ 200 KASSERT(HREAD4(sc, sc->sc_offset) == len); 201 mbox_send(sc->sc_mc, NULL, 0); 202 203 for (i = 1000; i > 0; i--) { 204 if (HREAD4(sc, sc->sc_offset) == 0) 205 break; 206 delay(1000); 207 } 208 if (i == 0) { 209 printf("%s: timeout sending message\n", sc->sc_dev.dv_xname); 210 HWRITE4(sc, sc->sc_offset, 0); 211 return ETIMEDOUT; 212 } 213 214 return 0; 215} 216