xref: /openbsd/sys/dev/fdt/qcipcc.c (revision 3bef86f7)
1 /*	$OpenBSD: qcipcc.c,v 1.2 2023/05/19 20:54:55 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 
23 #include <machine/bus.h>
24 #include <machine/fdt.h>
25 
26 #include <dev/ofw/openfirm.h>
27 #include <dev/ofw/ofw_misc.h>
28 #include <dev/ofw/fdt.h>
29 
30 #define IPCC_SEND_ID			0x0c
31 #define IPCC_RECV_ID			0x10
32 #define IPCC_RECV_SIGNAL_ENABLE		0x14
33 #define IPCC_RECV_SIGNAL_DISABLE	0x18
34 #define IPCC_RECV_SIGNAL_CLEAR		0x1c
35 
36 #define IPCC_SIGNAL_ID_SHIFT	0
37 #define IPCC_SIGNAL_ID_MASK	0xffff
38 #define IPCC_CLIENT_ID_SHIFT	16
39 #define IPCC_CLIENT_ID_MASK	0xffff
40 
41 #define HREAD4(sc, reg)							\
42 	(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
43 #define HWRITE4(sc, reg, val)						\
44 	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
45 
46 struct qcipcc_intrhand {
47 	TAILQ_ENTRY(qcipcc_intrhand) ih_q;
48 	int (*ih_func)(void *);
49 	void *ih_arg;
50 	void *ih_sc;
51 	uint16_t ih_client_id;
52 	uint16_t ih_signal_id;
53 };
54 
55 struct qcipcc_softc {
56 	struct device		sc_dev;
57 	bus_space_tag_t		sc_iot;
58 	bus_space_handle_t	sc_ioh;
59 
60 	void			*sc_ih;
61 
62 	struct interrupt_controller sc_ic;
63 	TAILQ_HEAD(,qcipcc_intrhand) sc_intrq;
64 
65 	struct mbox_device	sc_md;
66 };
67 
68 struct qcipcc_channel {
69 	struct qcipcc_softc	*ch_sc;
70 	uint32_t		ch_client_id;
71 	uint32_t		ch_signal_id;
72 };
73 
74 int	qcipcc_match(struct device *, void *, void *);
75 void	qcipcc_attach(struct device *, struct device *, void *);
76 
77 const struct cfattach qcipcc_ca = {
78 	sizeof (struct qcipcc_softc), qcipcc_match, qcipcc_attach
79 };
80 
81 struct cfdriver qcipcc_cd = {
82 	NULL, "qcipcc", DV_DULL
83 };
84 
85 int	qcipcc_intr(void *);
86 void	*qcipcc_intr_establish(void *, int *, int, struct cpu_info *,
87 	    int (*)(void *), void *, char *);
88 void	qcipcc_intr_disestablish(void *);
89 void	qcipcc_intr_enable(void *);
90 void	qcipcc_intr_disable(void *);
91 void	qcipcc_intr_barrier(void *);
92 
93 void	*qcipcc_channel(void *, uint32_t *, struct mbox_client *);
94 int	qcipcc_send(void *, const void *, size_t);
95 
96 int
97 qcipcc_match(struct device *parent, void *match, void *aux)
98 {
99 	struct fdt_attach_args *faa = aux;
100 
101 	return OF_is_compatible(faa->fa_node, "qcom,ipcc");
102 }
103 
104 void
105 qcipcc_attach(struct device *parent, struct device *self, void *aux)
106 {
107 	struct qcipcc_softc *sc = (struct qcipcc_softc *)self;
108 	struct fdt_attach_args *faa = aux;
109 
110 	if (faa->fa_nreg < 1) {
111 		printf(": no registers\n");
112 		return;
113 	}
114 
115 	sc->sc_iot = faa->fa_iot;
116 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
117 	    faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
118 		printf(": can't map registers\n");
119 		return;
120 	}
121 
122 	TAILQ_INIT(&sc->sc_intrq);
123 
124 	sc->sc_ih = fdt_intr_establish(faa->fa_node, IPL_BIO,
125 	    qcipcc_intr, sc, sc->sc_dev.dv_xname);
126 	if (sc->sc_ih == NULL) {
127 		printf(": can't establish interrupt\n");
128 		return;
129 	}
130 
131 	printf("\n");
132 
133 	sc->sc_ic.ic_node = faa->fa_node;
134 	sc->sc_ic.ic_cookie = sc;
135 	sc->sc_ic.ic_establish = qcipcc_intr_establish;
136 	sc->sc_ic.ic_disestablish = qcipcc_intr_disestablish;
137 	sc->sc_ic.ic_enable = qcipcc_intr_enable;
138 	sc->sc_ic.ic_disable = qcipcc_intr_disable;
139 	sc->sc_ic.ic_barrier = qcipcc_intr_barrier;
140 	fdt_intr_register(&sc->sc_ic);
141 
142 	sc->sc_md.md_node = faa->fa_node;
143 	sc->sc_md.md_cookie = sc;
144 	sc->sc_md.md_channel = qcipcc_channel;
145 	sc->sc_md.md_send = qcipcc_send;
146 	mbox_register(&sc->sc_md);
147 }
148 
149 int
150 qcipcc_intr(void *arg)
151 {
152 	struct qcipcc_softc *sc = arg;
153 	struct qcipcc_intrhand *ih;
154 	uint16_t client_id, signal_id;
155 	uint32_t reg;
156 	int handled = 0;
157 
158 	while ((reg = HREAD4(sc, IPCC_RECV_ID)) != ~0) {
159 		HWRITE4(sc, IPCC_RECV_SIGNAL_CLEAR, reg);
160 
161 		client_id = (reg >> IPCC_CLIENT_ID_SHIFT) &
162 		    IPCC_CLIENT_ID_MASK;
163 		signal_id = (reg >> IPCC_SIGNAL_ID_SHIFT) &
164 		    IPCC_SIGNAL_ID_MASK;
165 
166 		TAILQ_FOREACH(ih, &sc->sc_intrq, ih_q) {
167 			if (ih->ih_client_id != client_id ||
168 			    ih->ih_signal_id != signal_id)
169 				continue;
170 			ih->ih_func(ih->ih_arg);
171 			handled = 1;
172 		}
173 	}
174 
175 	return handled;
176 }
177 
178 void *
179 qcipcc_intr_establish(void *cookie, int *cells, int ipl,
180     struct cpu_info *ci, int (*func)(void *), void *arg, char *name)
181 {
182 	struct qcipcc_softc *sc = cookie;
183 	struct qcipcc_intrhand *ih;
184 
185 	ih = malloc(sizeof(*ih), M_DEVBUF, M_WAITOK | M_ZERO);
186 	ih->ih_func = func;
187 	ih->ih_arg = arg;
188 	ih->ih_sc = sc;
189 	ih->ih_client_id = cells[0] & IPCC_CLIENT_ID_MASK;
190 	ih->ih_signal_id = cells[1] & IPCC_SIGNAL_ID_MASK;
191 	TAILQ_INSERT_TAIL(&sc->sc_intrq, ih, ih_q);
192 
193 	qcipcc_intr_enable(ih);
194 
195 	if (ipl & IPL_WAKEUP)
196 		intr_set_wakeup(sc->sc_ih);
197 
198 	return ih;
199 }
200 
201 void
202 qcipcc_intr_disestablish(void *cookie)
203 {
204 	struct qcipcc_intrhand *ih = cookie;
205 	struct qcipcc_softc *sc = ih->ih_sc;
206 
207 	qcipcc_intr_disable(ih);
208 
209 	TAILQ_REMOVE(&sc->sc_intrq, ih, ih_q);
210 	free(ih, M_DEVBUF, sizeof(*ih));
211 }
212 
213 void
214 qcipcc_intr_enable(void *cookie)
215 {
216 	struct qcipcc_intrhand *ih = cookie;
217 	struct qcipcc_softc *sc = ih->ih_sc;
218 
219 	HWRITE4(sc, IPCC_RECV_SIGNAL_ENABLE,
220 	    (ih->ih_client_id << IPCC_CLIENT_ID_SHIFT) |
221 	    (ih->ih_signal_id << IPCC_SIGNAL_ID_SHIFT));
222 }
223 
224 void
225 qcipcc_intr_disable(void *cookie)
226 {
227 	struct qcipcc_intrhand *ih = cookie;
228 	struct qcipcc_softc *sc = ih->ih_sc;
229 
230 	HWRITE4(sc, IPCC_RECV_SIGNAL_DISABLE,
231 	    (ih->ih_client_id << IPCC_CLIENT_ID_SHIFT) |
232 	    (ih->ih_signal_id << IPCC_SIGNAL_ID_SHIFT));
233 }
234 
235 void
236 qcipcc_intr_barrier(void *cookie)
237 {
238 	struct qcipcc_intrhand *ih = cookie;
239 	struct qcipcc_softc *sc = ih->ih_sc;
240 
241 	intr_barrier(sc->sc_ih);
242 }
243 
244 void *
245 qcipcc_channel(void *cookie, uint32_t *cells, struct mbox_client *mc)
246 {
247 	struct qcipcc_softc *sc = cookie;
248 	struct qcipcc_channel *ch;
249 
250 	ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK);
251 	ch->ch_sc = sc;
252 	ch->ch_client_id = cells[0] & IPCC_CLIENT_ID_MASK;
253 	ch->ch_signal_id = cells[1] & IPCC_SIGNAL_ID_MASK;
254 
255 	return ch;
256 }
257 
258 int
259 qcipcc_send(void *cookie, const void *data, size_t len)
260 {
261 	struct qcipcc_channel *ch = cookie;
262 	struct qcipcc_softc *sc = ch->ch_sc;
263 
264 	HWRITE4(sc, IPCC_SEND_ID,
265 	    (ch->ch_client_id << IPCC_CLIENT_ID_SHIFT) |
266 	    (ch->ch_signal_id << IPCC_SIGNAL_ID_SHIFT));
267 
268 	return 0;
269 }
270