1*fe62c203Spatrick /* $OpenBSD: qcaoss.c,v 1.1 2023/05/23 14:10:27 patrick Exp $ */
2*fe62c203Spatrick /*
3*fe62c203Spatrick * Copyright (c) 2023 Patrick Wildt <patrick@blueri.se>
4*fe62c203Spatrick *
5*fe62c203Spatrick * Permission to use, copy, modify, and distribute this software for any
6*fe62c203Spatrick * purpose with or without fee is hereby granted, provided that the above
7*fe62c203Spatrick * copyright notice and this permission notice appear in all copies.
8*fe62c203Spatrick *
9*fe62c203Spatrick * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10*fe62c203Spatrick * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11*fe62c203Spatrick * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12*fe62c203Spatrick * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13*fe62c203Spatrick * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14*fe62c203Spatrick * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15*fe62c203Spatrick * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16*fe62c203Spatrick */
17*fe62c203Spatrick
18*fe62c203Spatrick #include <sys/param.h>
19*fe62c203Spatrick #include <sys/systm.h>
20*fe62c203Spatrick #include <sys/device.h>
21*fe62c203Spatrick #include <sys/malloc.h>
22*fe62c203Spatrick #include <sys/atomic.h>
23*fe62c203Spatrick
24*fe62c203Spatrick #include <machine/bus.h>
25*fe62c203Spatrick #include <machine/fdt.h>
26*fe62c203Spatrick
27*fe62c203Spatrick #include <dev/ofw/openfirm.h>
28*fe62c203Spatrick #include <dev/ofw/ofw_misc.h>
29*fe62c203Spatrick #include <dev/ofw/fdt.h>
30*fe62c203Spatrick
31*fe62c203Spatrick #define AOSS_DESC_MAGIC 0x0
32*fe62c203Spatrick #define AOSS_DESC_VERSION 0x4
33*fe62c203Spatrick #define AOSS_DESC_FEATURES 0x8
34*fe62c203Spatrick #define AOSS_DESC_UCORE_LINK_STATE 0xc
35*fe62c203Spatrick #define AOSS_DESC_UCORE_LINK_STATE_ACK 0x10
36*fe62c203Spatrick #define AOSS_DESC_UCORE_CH_STATE 0x14
37*fe62c203Spatrick #define AOSS_DESC_UCORE_CH_STATE_ACK 0x18
38*fe62c203Spatrick #define AOSS_DESC_UCORE_MBOX_SIZE 0x1c
39*fe62c203Spatrick #define AOSS_DESC_UCORE_MBOX_OFFSET 0x20
40*fe62c203Spatrick #define AOSS_DESC_MCORE_LINK_STATE 0x24
41*fe62c203Spatrick #define AOSS_DESC_MCORE_LINK_STATE_ACK 0x28
42*fe62c203Spatrick #define AOSS_DESC_MCORE_CH_STATE 0x2c
43*fe62c203Spatrick #define AOSS_DESC_MCORE_CH_STATE_ACK 0x30
44*fe62c203Spatrick #define AOSS_DESC_MCORE_MBOX_SIZE 0x34
45*fe62c203Spatrick #define AOSS_DESC_MCORE_MBOX_OFFSET 0x38
46*fe62c203Spatrick
47*fe62c203Spatrick #define AOSS_MAGIC 0x4d41494c
48*fe62c203Spatrick #define AOSS_VERSION 1
49*fe62c203Spatrick
50*fe62c203Spatrick #define AOSS_STATE_UP (0xffffU << 0)
51*fe62c203Spatrick #define AOSS_STATE_DOWN (0xffffU << 16)
52*fe62c203Spatrick
53*fe62c203Spatrick #define HREAD4(sc, reg) \
54*fe62c203Spatrick (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
55*fe62c203Spatrick #define HWRITE4(sc, reg, val) \
56*fe62c203Spatrick bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
57*fe62c203Spatrick
58*fe62c203Spatrick struct qcaoss_softc {
59*fe62c203Spatrick struct device sc_dev;
60*fe62c203Spatrick bus_space_tag_t sc_iot;
61*fe62c203Spatrick bus_space_handle_t sc_ioh;
62*fe62c203Spatrick
63*fe62c203Spatrick size_t sc_offset;
64*fe62c203Spatrick size_t sc_size;
65*fe62c203Spatrick
66*fe62c203Spatrick struct mbox_channel *sc_mc;
67*fe62c203Spatrick };
68*fe62c203Spatrick
69*fe62c203Spatrick struct qcaoss_softc *qcaoss_sc;
70*fe62c203Spatrick
71*fe62c203Spatrick int qcaoss_match(struct device *, void *, void *);
72*fe62c203Spatrick void qcaoss_attach(struct device *, struct device *, void *);
73*fe62c203Spatrick
74*fe62c203Spatrick const struct cfattach qcaoss_ca = {
75*fe62c203Spatrick sizeof (struct qcaoss_softc), qcaoss_match, qcaoss_attach
76*fe62c203Spatrick };
77*fe62c203Spatrick
78*fe62c203Spatrick struct cfdriver qcaoss_cd = {
79*fe62c203Spatrick NULL, "qcaoss", DV_DULL
80*fe62c203Spatrick };
81*fe62c203Spatrick
82*fe62c203Spatrick int
qcaoss_match(struct device * parent,void * match,void * aux)83*fe62c203Spatrick qcaoss_match(struct device *parent, void *match, void *aux)
84*fe62c203Spatrick {
85*fe62c203Spatrick struct fdt_attach_args *faa = aux;
86*fe62c203Spatrick
87*fe62c203Spatrick return OF_is_compatible(faa->fa_node, "qcom,aoss-qmp");
88*fe62c203Spatrick }
89*fe62c203Spatrick
90*fe62c203Spatrick void
qcaoss_attach(struct device * parent,struct device * self,void * aux)91*fe62c203Spatrick qcaoss_attach(struct device *parent, struct device *self, void *aux)
92*fe62c203Spatrick {
93*fe62c203Spatrick struct qcaoss_softc *sc = (struct qcaoss_softc *)self;
94*fe62c203Spatrick struct fdt_attach_args *faa = aux;
95*fe62c203Spatrick int i;
96*fe62c203Spatrick
97*fe62c203Spatrick if (faa->fa_nreg < 1) {
98*fe62c203Spatrick printf(": no registers\n");
99*fe62c203Spatrick return;
100*fe62c203Spatrick }
101*fe62c203Spatrick
102*fe62c203Spatrick sc->sc_iot = faa->fa_iot;
103*fe62c203Spatrick if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
104*fe62c203Spatrick faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
105*fe62c203Spatrick printf(": can't map registers\n");
106*fe62c203Spatrick return;
107*fe62c203Spatrick }
108*fe62c203Spatrick
109*fe62c203Spatrick sc->sc_mc = mbox_channel_idx(faa->fa_node, 0, NULL);
110*fe62c203Spatrick if (sc->sc_mc == NULL) {
111*fe62c203Spatrick bus_space_unmap(sc->sc_iot, sc->sc_ioh, faa->fa_reg[0].size);
112*fe62c203Spatrick printf(": can't find mbox\n");
113*fe62c203Spatrick return;
114*fe62c203Spatrick }
115*fe62c203Spatrick
116*fe62c203Spatrick if (HREAD4(sc, AOSS_DESC_MAGIC) != AOSS_MAGIC ||
117*fe62c203Spatrick HREAD4(sc, AOSS_DESC_VERSION) != AOSS_VERSION) {
118*fe62c203Spatrick printf(": invalid QMP info\n");
119*fe62c203Spatrick return;
120*fe62c203Spatrick }
121*fe62c203Spatrick
122*fe62c203Spatrick sc->sc_offset = HREAD4(sc, AOSS_DESC_MCORE_MBOX_OFFSET);
123*fe62c203Spatrick sc->sc_size = HREAD4(sc, AOSS_DESC_MCORE_MBOX_SIZE);
124*fe62c203Spatrick if (sc->sc_size == 0) {
125*fe62c203Spatrick printf(": invalid mailbox size\n");
126*fe62c203Spatrick return;
127*fe62c203Spatrick }
128*fe62c203Spatrick
129*fe62c203Spatrick HWRITE4(sc, AOSS_DESC_UCORE_LINK_STATE_ACK,
130*fe62c203Spatrick HREAD4(sc, AOSS_DESC_UCORE_LINK_STATE));
131*fe62c203Spatrick
132*fe62c203Spatrick HWRITE4(sc, AOSS_DESC_MCORE_LINK_STATE, AOSS_STATE_UP);
133*fe62c203Spatrick mbox_send(sc->sc_mc, NULL, 0);
134*fe62c203Spatrick
135*fe62c203Spatrick for (i = 1000; i > 0; i--) {
136*fe62c203Spatrick if (HREAD4(sc, AOSS_DESC_MCORE_LINK_STATE_ACK) == AOSS_STATE_UP)
137*fe62c203Spatrick break;
138*fe62c203Spatrick delay(1000);
139*fe62c203Spatrick }
140*fe62c203Spatrick if (i == 0) {
141*fe62c203Spatrick printf(": didn't get link state ack\n");
142*fe62c203Spatrick return;
143*fe62c203Spatrick }
144*fe62c203Spatrick
145*fe62c203Spatrick HWRITE4(sc, AOSS_DESC_MCORE_CH_STATE, AOSS_STATE_UP);
146*fe62c203Spatrick mbox_send(sc->sc_mc, NULL, 0);
147*fe62c203Spatrick
148*fe62c203Spatrick for (i = 1000; i > 0; i--) {
149*fe62c203Spatrick if (HREAD4(sc, AOSS_DESC_UCORE_CH_STATE) == AOSS_STATE_UP)
150*fe62c203Spatrick break;
151*fe62c203Spatrick delay(1000);
152*fe62c203Spatrick }
153*fe62c203Spatrick if (i == 0) {
154*fe62c203Spatrick printf(": didn't get open channel\n");
155*fe62c203Spatrick return;
156*fe62c203Spatrick }
157*fe62c203Spatrick
158*fe62c203Spatrick HWRITE4(sc, AOSS_DESC_UCORE_CH_STATE_ACK, AOSS_STATE_UP);
159*fe62c203Spatrick mbox_send(sc->sc_mc, NULL, 0);
160*fe62c203Spatrick
161*fe62c203Spatrick for (i = 1000; i > 0; i--) {
162*fe62c203Spatrick if (HREAD4(sc, AOSS_DESC_MCORE_CH_STATE_ACK) == AOSS_STATE_UP)
163*fe62c203Spatrick break;
164*fe62c203Spatrick delay(1000);
165*fe62c203Spatrick }
166*fe62c203Spatrick if (i == 0) {
167*fe62c203Spatrick printf(": didn't get channel ack\n");
168*fe62c203Spatrick return;
169*fe62c203Spatrick }
170*fe62c203Spatrick
171*fe62c203Spatrick printf("\n");
172*fe62c203Spatrick
173*fe62c203Spatrick qcaoss_sc = sc;
174*fe62c203Spatrick }
175*fe62c203Spatrick
176*fe62c203Spatrick int
qcaoss_send(char * data,size_t len)177*fe62c203Spatrick qcaoss_send(char *data, size_t len)
178*fe62c203Spatrick {
179*fe62c203Spatrick struct qcaoss_softc *sc = qcaoss_sc;
180*fe62c203Spatrick uint32_t reg;
181*fe62c203Spatrick int i;
182*fe62c203Spatrick
183*fe62c203Spatrick if (sc == NULL)
184*fe62c203Spatrick return ENXIO;
185*fe62c203Spatrick
186*fe62c203Spatrick if (data == NULL || sizeof(uint32_t) + len > sc->sc_size ||
187*fe62c203Spatrick (len % sizeof(uint32_t)) != 0)
188*fe62c203Spatrick return EINVAL;
189*fe62c203Spatrick
190*fe62c203Spatrick /* Write data first, needs to be 32-bit access. */
191*fe62c203Spatrick for (i = 0; i < len; i += 4) {
192*fe62c203Spatrick memcpy(®, data + i, sizeof(reg));
193*fe62c203Spatrick HWRITE4(sc, sc->sc_offset + sizeof(uint32_t) + i, reg);
194*fe62c203Spatrick }
195*fe62c203Spatrick
196*fe62c203Spatrick /* Commit transaction by writing length. */
197*fe62c203Spatrick HWRITE4(sc, sc->sc_offset, len);
198*fe62c203Spatrick
199*fe62c203Spatrick /* Assert it's stored and inform peer. */
200*fe62c203Spatrick KASSERT(HREAD4(sc, sc->sc_offset) == len);
201*fe62c203Spatrick mbox_send(sc->sc_mc, NULL, 0);
202*fe62c203Spatrick
203*fe62c203Spatrick for (i = 1000; i > 0; i--) {
204*fe62c203Spatrick if (HREAD4(sc, sc->sc_offset) == 0)
205*fe62c203Spatrick break;
206*fe62c203Spatrick delay(1000);
207*fe62c203Spatrick }
208*fe62c203Spatrick if (i == 0) {
209*fe62c203Spatrick printf("%s: timeout sending message\n", sc->sc_dev.dv_xname);
210*fe62c203Spatrick HWRITE4(sc, sc->sc_offset, 0);
211*fe62c203Spatrick return ETIMEDOUT;
212*fe62c203Spatrick }
213*fe62c203Spatrick
214*fe62c203Spatrick return 0;
215*fe62c203Spatrick }
216