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