1 /* $OpenBSD: apldcp.c,v 1.3 2024/08/18 10:50:22 kettenis Exp $ */
2 /*
3 * Copyright (c) 2023 Mark Kettenis <kettenis@openbsd.org>
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/pool.h>
22
23 #include <machine/intr.h>
24 #include <machine/bus.h>
25 #include <machine/fdt.h>
26
27 #include <dev/ofw/openfirm.h>
28 #include <dev/ofw/fdt.h>
29 #include <dev/ofw/ofw_power.h>
30 #include <dev/ofw/ofw_clock.h>
31
32 static const void *of_device_get_match_data(const struct device *);
33
34 #include "dcp.c"
35
36 struct apldcp_softc {
37 struct platform_device sc_dev;
38 };
39
40 int apldcp_match(struct device *, void *, void *);
41 void apldcp_attach(struct device *, struct device *, void *);
42 int apldcp_activate(struct device *, int);
43
44 const struct cfattach apldcp_ca = {
45 sizeof (struct apldcp_softc), apldcp_match, apldcp_attach,
46 NULL, apldcp_activate
47 };
48
49 struct cfdriver apldcp_cd = {
50 NULL, "apldcp", DV_DULL
51 };
52
53 int
apldcp_match(struct device * parent,void * match,void * aux)54 apldcp_match(struct device *parent, void *match, void *aux)
55 {
56 struct fdt_attach_args *faa = aux;
57
58 return OF_is_compatible(faa->fa_node, "apple,dcp") ||
59 OF_is_compatible(faa->fa_node, "apple,dcpext");
60 }
61
62 void
apldcp_attach(struct device * parent,struct device * self,void * aux)63 apldcp_attach(struct device *parent, struct device *self, void *aux)
64 {
65 struct apldcp_softc *sc = (struct apldcp_softc *)self;
66 struct fdt_attach_args *faa = aux;
67
68 power_domain_enable(faa->fa_node);
69 reset_deassert_all(faa->fa_node);
70
71 printf("\n");
72
73 sc->sc_dev.faa = faa;
74 platform_device_register(&sc->sc_dev);
75
76 dcp_platform_probe(&sc->sc_dev);
77 }
78
79 int
apldcp_activate(struct device * self,int act)80 apldcp_activate(struct device *self, int act)
81 {
82 int rv;
83
84 switch (act) {
85 case DVACT_QUIESCE:
86 rv = config_activate_children(self, act);
87 dcp_platform_suspend(self);
88 break;
89 case DVACT_WAKEUP:
90 dcp_platform_resume(self);
91 rv = config_activate_children(self, act);
92 break;
93 default:
94 rv = config_activate_children(self, act);
95 break;
96 }
97
98 return rv;
99 }
100
101 /*
102 * Linux RTKit interfaces.
103 */
104
105 #include <arm64/dev/rtkit.h>
106
107 struct apple_rtkit_task {
108 struct apple_rtkit_ep *rtkep;
109 struct task task;
110 uint64_t msg;
111 };
112
113 struct apple_rtkit_ep {
114 struct apple_rtkit *rtk;
115 uint8_t ep;
116 };
117
118 struct apple_rtkit {
119 struct rtkit_state *state;
120 struct apple_rtkit_ep ep[64];
121 void *cookie;
122 struct platform_device *pdev;
123 const struct apple_rtkit_ops *ops;
124 struct pool task_pool;
125 struct taskq *tq;
126 };
127
128 paddr_t
apple_rtkit_logmap(void * cookie,bus_addr_t addr)129 apple_rtkit_logmap(void *cookie, bus_addr_t addr)
130 {
131 struct apple_rtkit *rtk = cookie;
132 int idx, len, node;
133 uint32_t *phandles;
134 uint32_t iommu_addresses[5];
135 bus_addr_t start;
136 bus_size_t size;
137 uint64_t reg[2];
138
139 len = OF_getproplen(rtk->pdev->node, "memory-region");
140 idx = OF_getindex(rtk->pdev->node, "dcp_data", "memory-region-names");
141 if (idx < 0 || idx >= len / sizeof(uint32_t))
142 return addr;
143
144 phandles = malloc(len, M_TEMP, M_WAITOK | M_ZERO);
145 OF_getpropintarray(rtk->pdev->node, "memory-region",
146 phandles, len);
147 node = OF_getnodebyphandle(phandles[idx]);
148 free(phandles, M_TEMP, len);
149
150 if (node == 0)
151 return addr;
152
153 if (!OF_is_compatible(node, "apple,asc-mem"))
154 return addr;
155
156 if (OF_getpropint64array(node, "reg", reg, sizeof(reg)) != sizeof(reg))
157 return addr;
158
159 if (OF_getpropintarray(node, "iommu-addresses", iommu_addresses,
160 sizeof(iommu_addresses)) < sizeof(iommu_addresses))
161 return addr;
162 start = (uint64_t)iommu_addresses[1] << 32 | iommu_addresses[2];
163 size = (uint64_t)iommu_addresses[3] << 32 | iommu_addresses[4];
164 if (addr >= start && addr < start + size)
165 return reg[0] + (addr - start);
166
167 /* XXX some machines have truncated DVAs in "iommu-addresses" */
168 addr &= 0xffffffff;
169 if (addr >= start && addr < start + size)
170 return reg[0] + (addr - start);
171
172 return (paddr_t)-1;
173 }
174
175 void
apple_rtkit_do_recv(void * arg)176 apple_rtkit_do_recv(void *arg)
177 {
178 struct apple_rtkit_task *rtktask = arg;
179 struct apple_rtkit_ep *rtkep = rtktask->rtkep;
180 struct apple_rtkit *rtk = rtkep->rtk;
181
182 rtk->ops->recv_message(rtk->cookie, rtkep->ep, rtktask->msg);
183 pool_put(&rtk->task_pool, rtktask);
184 }
185
186 void
apple_rtkit_recv(void * cookie,uint64_t msg)187 apple_rtkit_recv(void *cookie, uint64_t msg)
188 {
189 struct apple_rtkit_ep *rtkep = cookie;
190 struct apple_rtkit *rtk = rtkep->rtk;
191 struct apple_rtkit_task *rtktask;
192
193 rtktask = pool_get(&rtk->task_pool, PR_NOWAIT | PR_ZERO);
194 KASSERT(rtktask != NULL);
195
196 rtktask->rtkep = rtkep;
197 rtktask->msg = msg;
198 task_set(&rtktask->task, apple_rtkit_do_recv, rtktask);
199 task_add(rtk->tq, &rtktask->task);
200 }
201
202 int
apple_rtkit_start_ep(struct apple_rtkit * rtk,uint8_t ep)203 apple_rtkit_start_ep(struct apple_rtkit *rtk, uint8_t ep)
204 {
205 struct apple_rtkit_ep *rtkep;
206 int error;
207
208 rtkep = &rtk->ep[ep];
209 rtkep->rtk = rtk;
210 rtkep->ep = ep;
211 error = rtkit_start_endpoint(rtk->state, ep, apple_rtkit_recv, rtkep);
212 return -error;
213 }
214
215 int
apple_rtkit_send_message(struct apple_rtkit * rtk,uint8_t ep,uint64_t msg,struct completion * completion,int atomic)216 apple_rtkit_send_message(struct apple_rtkit *rtk, uint8_t ep, uint64_t msg,
217 struct completion *completion, int atomic)
218 {
219 int error;
220
221 error = rtkit_send_endpoint(rtk->state, ep, msg);
222 return -error;
223 }
224
225 int
apple_rtkit_wake(struct apple_rtkit * rtk)226 apple_rtkit_wake(struct apple_rtkit *rtk)
227 {
228 int error;
229
230 error = rtkit_set_iop_pwrstate(rtk->state, RTKIT_MGMT_PWR_STATE_INIT);
231 if (error)
232 return -error;
233
234 error = rtkit_set_ap_pwrstate(rtk->state, RTKIT_MGMT_PWR_STATE_ON);
235 return -error;
236 }
237
238 struct apple_rtkit *
devm_apple_rtkit_init(struct device * dev,void * cookie,const char * mbox_name,int mbox_idx,const struct apple_rtkit_ops * ops)239 devm_apple_rtkit_init(struct device *dev, void *cookie,
240 const char *mbox_name, int mbox_idx, const struct apple_rtkit_ops *ops)
241 {
242 struct platform_device *pdev = (struct platform_device *)dev;
243 struct apple_rtkit *rtk;
244 struct rtkit *rk;
245
246 rtk = malloc(sizeof(*rtk), M_DEVBUF, M_WAITOK | M_ZERO);
247 rtk->tq = taskq_create("drmrtk", 1, IPL_HIGH, 0);
248 if (rtk->tq == NULL) {
249 free(rtk, M_DEVBUF, sizeof(*rtk));
250 return ERR_PTR(ENOMEM);
251 }
252
253 pool_init(&rtk->task_pool, sizeof(struct apple_rtkit_task), 0, IPL_TTY,
254 0, "apldcp_rtkit", NULL);
255
256 rk = malloc(sizeof(*rk), M_DEVBUF, M_WAITOK | M_ZERO);
257 rk->rk_cookie = rtk;
258 rk->rk_dmat = pdev->dmat;
259 rk->rk_logmap = apple_rtkit_logmap;
260
261 rtk->state = rtkit_init(pdev->node, mbox_name, 0, rk);
262 rtk->cookie = cookie;
263 rtk->pdev = pdev;
264 rtk->ops = ops;
265
266 return rtk;
267 }
268
269 static const void *
of_device_get_match_data(const struct device * dev)270 of_device_get_match_data(const struct device *dev)
271 {
272 struct platform_device *pdev = (struct platform_device *)dev;
273 int i;
274
275 for (i = 0; i < nitems(of_match); i++) {
276 if (OF_is_compatible(pdev->node, of_match[i].compatible))
277 return of_match[i].data;
278 }
279
280 return NULL;
281 }
282