1 /* $NetBSD: qcomsmptp.c,v 1.1 2024/12/30 12:31:10 jmcneill Exp $ */
2 /*        $OpenBSD: qcsmptp.c,v 1.2 2023/07/04 14:32:21 patrick Exp $ */
3 /*
4  * Copyright (c) 2023 Patrick Wildt <patrick@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
qcsmptp_match(device_t parent,cfdata_t match,void * aux)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
qcsmptp_attach(device_t parent,device_t self,void * aux)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
qcsmptp_intr(void * arg)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 *
qcsmptp_intr_establish(u_int pin,int (* func)(void *),void * arg)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
qcsmptp_intr_disestablish(void * cookie)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
qcsmptp_intr_enable(void * cookie)319 qcsmptp_intr_enable(void *cookie)
320 {
321           struct qcsmptp_intrhand *ih = cookie;
322 
323           ih->ih_enabled = 1;
324 }
325 
326 void
qcsmptp_intr_disable(void * cookie)327 qcsmptp_intr_disable(void *cookie)
328 {
329           struct qcsmptp_intrhand *ih = cookie;
330 
331           ih->ih_enabled = 0;
332 }
333