qcomsmptp.c revision 1.1 1 1.1 jmcneill /* $NetBSD: qcomsmptp.c,v 1.1 2024/12/30 12:31:10 jmcneill Exp $ */
2 1.1 jmcneill /* $OpenBSD: qcsmptp.c,v 1.2 2023/07/04 14:32:21 patrick Exp $ */
3 1.1 jmcneill /*
4 1.1 jmcneill * Copyright (c) 2023 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/systm.h>
21 1.1 jmcneill #include <sys/device.h>
22 1.1 jmcneill #include <sys/kmem.h>
23 1.1 jmcneill
24 1.1 jmcneill #include <dev/acpi/acpivar.h>
25 1.1 jmcneill #include <dev/acpi/qcomipcc.h>
26 1.1 jmcneill #include <dev/acpi/qcomsmem.h>
27 1.1 jmcneill #include <dev/acpi/qcomsmptp.h>
28 1.1 jmcneill
29 1.1 jmcneill #define SMP2P_MAX_ENTRY 16
30 1.1 jmcneill #define SMP2P_MAX_ENTRY_NAME 16
31 1.1 jmcneill
32 1.1 jmcneill struct qcsmptp_smem_item {
33 1.1 jmcneill uint32_t magic;
34 1.1 jmcneill #define SMP2P_MAGIC 0x504d5324
35 1.1 jmcneill uint8_t version;
36 1.1 jmcneill #define SMP2P_VERSION 1
37 1.1 jmcneill unsigned features:24;
38 1.1 jmcneill #define SMP2P_FEATURE_SSR_ACK (1 << 0)
39 1.1 jmcneill uint16_t local_pid;
40 1.1 jmcneill uint16_t remote_pid;
41 1.1 jmcneill uint16_t total_entries;
42 1.1 jmcneill uint16_t valid_entries;
43 1.1 jmcneill uint32_t flags;
44 1.1 jmcneill #define SMP2P_FLAGS_RESTART_DONE (1 << 0)
45 1.1 jmcneill #define SMP2P_FLAGS_RESTART_ACK (1 << 1)
46 1.1 jmcneill
47 1.1 jmcneill struct {
48 1.1 jmcneill uint8_t name[SMP2P_MAX_ENTRY_NAME];
49 1.1 jmcneill uint32_t value;
50 1.1 jmcneill } entries[SMP2P_MAX_ENTRY];
51 1.1 jmcneill } __packed;
52 1.1 jmcneill
53 1.1 jmcneill struct qcsmptp_intrhand {
54 1.1 jmcneill TAILQ_ENTRY(qcsmptp_intrhand) ih_q;
55 1.1 jmcneill int (*ih_func)(void *);
56 1.1 jmcneill void *ih_arg;
57 1.1 jmcneill void *ih_ic;
58 1.1 jmcneill int ih_pin;
59 1.1 jmcneill int ih_enabled;
60 1.1 jmcneill };
61 1.1 jmcneill
62 1.1 jmcneill struct qcsmptp_interrupt_controller {
63 1.1 jmcneill TAILQ_HEAD(,qcsmptp_intrhand) ic_intrq;
64 1.1 jmcneill struct qcsmptp_softc *ic_sc;
65 1.1 jmcneill };
66 1.1 jmcneill
67 1.1 jmcneill struct qcsmptp_entry {
68 1.1 jmcneill TAILQ_ENTRY(qcsmptp_entry) e_q;
69 1.1 jmcneill const char *e_name;
70 1.1 jmcneill uint32_t *e_value;
71 1.1 jmcneill uint32_t e_last_value;
72 1.1 jmcneill struct qcsmptp_interrupt_controller *e_ic;
73 1.1 jmcneill };
74 1.1 jmcneill
75 1.1 jmcneill struct qcsmptp_softc {
76 1.1 jmcneill device_t sc_dev;
77 1.1 jmcneill void *sc_ih;
78 1.1 jmcneill
79 1.1 jmcneill uint16_t sc_local_pid;
80 1.1 jmcneill uint16_t sc_remote_pid;
81 1.1 jmcneill uint32_t sc_smem_id[2];
82 1.1 jmcneill
83 1.1 jmcneill struct qcsmptp_smem_item *sc_in;
84 1.1 jmcneill struct qcsmptp_smem_item *sc_out;
85 1.1 jmcneill
86 1.1 jmcneill TAILQ_HEAD(,qcsmptp_entry) sc_inboundq;
87 1.1 jmcneill TAILQ_HEAD(,qcsmptp_entry) sc_outboundq;
88 1.1 jmcneill
89 1.1 jmcneill int sc_negotiated;
90 1.1 jmcneill int sc_ssr_ack_enabled;
91 1.1 jmcneill int sc_ssr_ack;
92 1.1 jmcneill
93 1.1 jmcneill uint16_t sc_valid_entries;
94 1.1 jmcneill
95 1.1 jmcneill void *sc_ipcc;
96 1.1 jmcneill };
97 1.1 jmcneill
98 1.1 jmcneill static struct qcsmptp_interrupt_controller *qcsmptp_ic = NULL;
99 1.1 jmcneill
100 1.1 jmcneill static int qcsmptp_match(device_t, cfdata_t, void *);
101 1.1 jmcneill static void qcsmptp_attach(device_t, device_t, void *);
102 1.1 jmcneill
103 1.1 jmcneill static int qcsmptp_intr(void *);
104 1.1 jmcneill
105 1.1 jmcneill CFATTACH_DECL_NEW(qcomsmptp, sizeof(struct qcsmptp_softc),
106 1.1 jmcneill qcsmptp_match, qcsmptp_attach, NULL, NULL);
107 1.1 jmcneill
108 1.1 jmcneill #define IPCC_CLIENT_LPASS 3
109 1.1 jmcneill #define IPCC_MPROC_SIGNAL_SMP2P 2
110 1.1 jmcneill
111 1.1 jmcneill #define QCSMPTP_X1E_LOCAL_PID 0
112 1.1 jmcneill #define QCSMPTP_X1E_REMOTE_PID 2
113 1.1 jmcneill #define QCSMPTP_X1E_SMEM_ID0 443
114 1.1 jmcneill #define QCSMPTP_X1E_SMEM_ID1 429
115 1.1 jmcneill
116 1.1 jmcneill static const struct device_compatible_entry compat_data[] = {
117 1.1 jmcneill { .compat = "QCOM0C5C" },
118 1.1 jmcneill DEVICE_COMPAT_EOL
119 1.1 jmcneill };
120 1.1 jmcneill
121 1.1 jmcneill static int
122 1.1 jmcneill qcsmptp_match(device_t parent, cfdata_t match, void *aux)
123 1.1 jmcneill {
124 1.1 jmcneill struct acpi_attach_args *aa = aux;
125 1.1 jmcneill
126 1.1 jmcneill return acpi_compatible_match(aa, compat_data);
127 1.1 jmcneill }
128 1.1 jmcneill
129 1.1 jmcneill static void
130 1.1 jmcneill qcsmptp_attach(device_t parent, device_t self, void *aux)
131 1.1 jmcneill {
132 1.1 jmcneill struct qcsmptp_softc *sc = device_private(self);
133 1.1 jmcneill struct qcsmptp_interrupt_controller *ic;
134 1.1 jmcneill struct qcsmptp_entry *e;
135 1.1 jmcneill
136 1.1 jmcneill sc->sc_dev = self;
137 1.1 jmcneill
138 1.1 jmcneill TAILQ_INIT(&sc->sc_inboundq);
139 1.1 jmcneill TAILQ_INIT(&sc->sc_outboundq);
140 1.1 jmcneill
141 1.1 jmcneill sc->sc_ih = qcipcc_intr_establish(IPCC_CLIENT_LPASS,
142 1.1 jmcneill IPCC_MPROC_SIGNAL_SMP2P, IPL_VM, qcsmptp_intr, sc);
143 1.1 jmcneill if (sc->sc_ih == NULL) {
144 1.1 jmcneill aprint_error(": can't establish interrupt\n");
145 1.1 jmcneill return;
146 1.1 jmcneill }
147 1.1 jmcneill
148 1.1 jmcneill sc->sc_local_pid = QCSMPTP_X1E_LOCAL_PID;
149 1.1 jmcneill sc->sc_remote_pid = QCSMPTP_X1E_REMOTE_PID;
150 1.1 jmcneill sc->sc_smem_id[0] = QCSMPTP_X1E_SMEM_ID0;
151 1.1 jmcneill sc->sc_smem_id[1] = QCSMPTP_X1E_SMEM_ID1;
152 1.1 jmcneill
153 1.1 jmcneill aprint_naive("\n");
154 1.1 jmcneill aprint_normal("\n");
155 1.1 jmcneill
156 1.1 jmcneill sc->sc_ipcc = qcipcc_channel(IPCC_CLIENT_LPASS,
157 1.1 jmcneill IPCC_MPROC_SIGNAL_SMP2P);
158 1.1 jmcneill if (sc->sc_ipcc == NULL) {
159 1.1 jmcneill aprint_error_dev(self, "can't get mailbox\n");
160 1.1 jmcneill return;
161 1.1 jmcneill }
162 1.1 jmcneill
163 1.1 jmcneill if (qcsmem_alloc(sc->sc_remote_pid, sc->sc_smem_id[0],
164 1.1 jmcneill sizeof(*sc->sc_in)) != 0) {
165 1.1 jmcneill aprint_error_dev(self, "can't alloc smp2p item\n");
166 1.1 jmcneill return;
167 1.1 jmcneill }
168 1.1 jmcneill
169 1.1 jmcneill sc->sc_in = qcsmem_get(sc->sc_remote_pid, sc->sc_smem_id[0], NULL);
170 1.1 jmcneill if (sc->sc_in == NULL) {
171 1.1 jmcneill aprint_error_dev(self, "can't get smp2p item\n");
172 1.1 jmcneill return;
173 1.1 jmcneill }
174 1.1 jmcneill
175 1.1 jmcneill if (qcsmem_alloc(sc->sc_remote_pid, sc->sc_smem_id[1],
176 1.1 jmcneill sizeof(*sc->sc_out)) != 0) {
177 1.1 jmcneill aprint_error_dev(self, "can't alloc smp2p item\n");
178 1.1 jmcneill return;
179 1.1 jmcneill }
180 1.1 jmcneill
181 1.1 jmcneill sc->sc_out = qcsmem_get(sc->sc_remote_pid, sc->sc_smem_id[1], NULL);
182 1.1 jmcneill if (sc->sc_out == NULL) {
183 1.1 jmcneill aprint_error_dev(self, "can't get smp2p item\n");
184 1.1 jmcneill return;
185 1.1 jmcneill }
186 1.1 jmcneill
187 1.1 jmcneill qcsmem_memset(sc->sc_out, 0, sizeof(*sc->sc_out));
188 1.1 jmcneill sc->sc_out->magic = SMP2P_MAGIC;
189 1.1 jmcneill sc->sc_out->local_pid = sc->sc_local_pid;
190 1.1 jmcneill sc->sc_out->remote_pid = sc->sc_remote_pid;
191 1.1 jmcneill sc->sc_out->total_entries = SMP2P_MAX_ENTRY;
192 1.1 jmcneill sc->sc_out->features = SMP2P_FEATURE_SSR_ACK;
193 1.1 jmcneill membar_sync();
194 1.1 jmcneill sc->sc_out->version = SMP2P_VERSION;
195 1.1 jmcneill qcipcc_send(sc->sc_ipcc);
196 1.1 jmcneill
197 1.1 jmcneill e = kmem_zalloc(sizeof(*e), KM_SLEEP);
198 1.1 jmcneill e->e_name = "master-kernel";
199 1.1 jmcneill e->e_value = &sc->sc_out->entries[sc->sc_out->valid_entries].value;
200 1.1 jmcneill sc->sc_out->valid_entries++;
201 1.1 jmcneill TAILQ_INSERT_TAIL(&sc->sc_outboundq, e, e_q);
202 1.1 jmcneill /* TODO: provide as smem state */
203 1.1 jmcneill
204 1.1 jmcneill e = kmem_zalloc(sizeof(*e), KM_SLEEP);
205 1.1 jmcneill e->e_name = "slave-kernel";
206 1.1 jmcneill ic = kmem_zalloc(sizeof(*ic), KM_SLEEP);
207 1.1 jmcneill TAILQ_INIT(&ic->ic_intrq);
208 1.1 jmcneill ic->ic_sc = sc;
209 1.1 jmcneill e->e_ic = ic;
210 1.1 jmcneill TAILQ_INSERT_TAIL(&sc->sc_inboundq, e, e_q);
211 1.1 jmcneill
212 1.1 jmcneill qcsmptp_ic = ic;
213 1.1 jmcneill }
214 1.1 jmcneill
215 1.1 jmcneill static int
216 1.1 jmcneill qcsmptp_intr(void *arg)
217 1.1 jmcneill {
218 1.1 jmcneill struct qcsmptp_softc *sc = arg;
219 1.1 jmcneill struct qcsmptp_entry *e;
220 1.1 jmcneill struct qcsmptp_intrhand *ih;
221 1.1 jmcneill uint32_t changed, val;
222 1.1 jmcneill int do_ack = 0, i;
223 1.1 jmcneill
224 1.1 jmcneill /* Do initial feature negotiation if inbound is new. */
225 1.1 jmcneill if (!sc->sc_negotiated) {
226 1.1 jmcneill if (sc->sc_in->version != sc->sc_out->version)
227 1.1 jmcneill return 1;
228 1.1 jmcneill sc->sc_out->features &= sc->sc_in->features;
229 1.1 jmcneill if (sc->sc_out->features & SMP2P_FEATURE_SSR_ACK)
230 1.1 jmcneill sc->sc_ssr_ack_enabled = 1;
231 1.1 jmcneill sc->sc_negotiated = 1;
232 1.1 jmcneill }
233 1.1 jmcneill if (!sc->sc_negotiated) {
234 1.1 jmcneill return 1;
235 1.1 jmcneill }
236 1.1 jmcneill
237 1.1 jmcneill /* Use ACK mechanism if negotiated. */
238 1.1 jmcneill if (sc->sc_ssr_ack_enabled &&
239 1.1 jmcneill !!(sc->sc_in->flags & SMP2P_FLAGS_RESTART_DONE) != sc->sc_ssr_ack)
240 1.1 jmcneill do_ack = 1;
241 1.1 jmcneill
242 1.1 jmcneill /* Catch up on new inbound entries that got added in the meantime. */
243 1.1 jmcneill for (i = sc->sc_valid_entries; i < sc->sc_in->valid_entries; i++) {
244 1.1 jmcneill TAILQ_FOREACH(e, &sc->sc_inboundq, e_q) {
245 1.1 jmcneill if (strncmp(sc->sc_in->entries[i].name, e->e_name,
246 1.1 jmcneill SMP2P_MAX_ENTRY_NAME) != 0)
247 1.1 jmcneill continue;
248 1.1 jmcneill e->e_value = &sc->sc_in->entries[i].value;
249 1.1 jmcneill }
250 1.1 jmcneill }
251 1.1 jmcneill sc->sc_valid_entries = i;
252 1.1 jmcneill
253 1.1 jmcneill /* For each inbound "interrupt controller". */
254 1.1 jmcneill TAILQ_FOREACH(e, &sc->sc_inboundq, e_q) {
255 1.1 jmcneill if (e->e_value == NULL)
256 1.1 jmcneill continue;
257 1.1 jmcneill val = *e->e_value;
258 1.1 jmcneill if (val == e->e_last_value)
259 1.1 jmcneill continue;
260 1.1 jmcneill changed = val ^ e->e_last_value;
261 1.1 jmcneill e->e_last_value = val;
262 1.1 jmcneill TAILQ_FOREACH(ih, &e->e_ic->ic_intrq, ih_q) {
263 1.1 jmcneill if (!ih->ih_enabled)
264 1.1 jmcneill continue;
265 1.1 jmcneill if ((changed & (1 << ih->ih_pin)) == 0)
266 1.1 jmcneill continue;
267 1.1 jmcneill ih->ih_func(ih->ih_arg);
268 1.1 jmcneill }
269 1.1 jmcneill }
270 1.1 jmcneill
271 1.1 jmcneill if (do_ack) {
272 1.1 jmcneill sc->sc_ssr_ack = !sc->sc_ssr_ack;
273 1.1 jmcneill if (sc->sc_ssr_ack)
274 1.1 jmcneill sc->sc_out->flags |= SMP2P_FLAGS_RESTART_ACK;
275 1.1 jmcneill else
276 1.1 jmcneill sc->sc_out->flags &= ~SMP2P_FLAGS_RESTART_ACK;
277 1.1 jmcneill membar_sync();
278 1.1 jmcneill qcipcc_send(sc->sc_ipcc);
279 1.1 jmcneill }
280 1.1 jmcneill
281 1.1 jmcneill return 1;
282 1.1 jmcneill }
283 1.1 jmcneill
284 1.1 jmcneill void *
285 1.1 jmcneill qcsmptp_intr_establish(u_int pin, int (*func)(void *), void *arg)
286 1.1 jmcneill {
287 1.1 jmcneill struct qcsmptp_interrupt_controller *ic = qcsmptp_ic;
288 1.1 jmcneill struct qcsmptp_intrhand *ih;
289 1.1 jmcneill
290 1.1 jmcneill if (ic == NULL) {
291 1.1 jmcneill return NULL;
292 1.1 jmcneill }
293 1.1 jmcneill
294 1.1 jmcneill ih = kmem_zalloc(sizeof(*ih), KM_SLEEP);
295 1.1 jmcneill ih->ih_func = func;
296 1.1 jmcneill ih->ih_arg = arg;
297 1.1 jmcneill ih->ih_ic = ic;
298 1.1 jmcneill ih->ih_pin = pin;
299 1.1 jmcneill TAILQ_INSERT_TAIL(&ic->ic_intrq, ih, ih_q);
300 1.1 jmcneill
301 1.1 jmcneill qcsmptp_intr_enable(ih);
302 1.1 jmcneill
303 1.1 jmcneill return ih;
304 1.1 jmcneill }
305 1.1 jmcneill
306 1.1 jmcneill void
307 1.1 jmcneill qcsmptp_intr_disestablish(void *cookie)
308 1.1 jmcneill {
309 1.1 jmcneill struct qcsmptp_intrhand *ih = cookie;
310 1.1 jmcneill struct qcsmptp_interrupt_controller *ic = ih->ih_ic;
311 1.1 jmcneill
312 1.1 jmcneill qcsmptp_intr_disable(ih);
313 1.1 jmcneill
314 1.1 jmcneill TAILQ_REMOVE(&ic->ic_intrq, ih, ih_q);
315 1.1 jmcneill kmem_free(ih, sizeof(*ih));
316 1.1 jmcneill }
317 1.1 jmcneill
318 1.1 jmcneill void
319 1.1 jmcneill qcsmptp_intr_enable(void *cookie)
320 1.1 jmcneill {
321 1.1 jmcneill struct qcsmptp_intrhand *ih = cookie;
322 1.1 jmcneill
323 1.1 jmcneill ih->ih_enabled = 1;
324 1.1 jmcneill }
325 1.1 jmcneill
326 1.1 jmcneill void
327 1.1 jmcneill qcsmptp_intr_disable(void *cookie)
328 1.1 jmcneill {
329 1.1 jmcneill struct qcsmptp_intrhand *ih = cookie;
330 1.1 jmcneill
331 1.1 jmcneill ih->ih_enabled = 0;
332 1.1 jmcneill }
333