xref: /openbsd-src/sys/arch/arm64/dev/aplmca.c (revision fc405d53b73a2d73393cb97f684863d17b583e38)
1 /*	$OpenBSD: aplmca.c,v 1.6 2023/02/03 13:20:21 kettenis Exp $	*/
2 /*
3  * Copyright (c) 2022 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/audioio.h>
21 #include <sys/device.h>
22 #include <sys/malloc.h>
23 #include <sys/fcntl.h>
24 
25 #include <machine/bus.h>
26 #include <machine/fdt.h>
27 
28 #include <dev/ofw/openfirm.h>
29 #include <dev/ofw/ofw_clock.h>
30 #include <dev/ofw/ofw_misc.h>
31 #include <dev/ofw/ofw_power.h>
32 #include <dev/ofw/fdt.h>
33 
34 #include <dev/audio_if.h>
35 
36 #include <arm64/dev/apldma.h>
37 
38 /*
39  * This driver is based on preliminary device tree bindings and will
40  * almost certainly need changes once the official bindings land in
41  * mainline Linux.  Support for these preliminary bindings will be
42  * dropped as soon as official bindings are available.
43  */
44 
45 #define MCA_CL_STRIDE		0x4000
46 #define MCA_SW_STRIDE		0x8000
47 #define MCA_SERDES_TXA		0x0300
48 
49 #define MCA_STATUS(idx)			((idx) * MCA_CL_STRIDE + 0x0000)
50 #define  MCA_STATUS_MCLK_EN		(1 << 0)
51 #define MCA_MCLK_CONF(idx)		((idx) * MCA_CL_STRIDE + 0x0004)
52 #define  MCA_MCLK_CONF_DIV_MASK		(0xf << 8)
53 #define  MCA_MCLK_CONF_DIV_SHIFT	8
54 
55 #define MCA_SYNCGEN_STATUS(idx)		((idx) * MCA_CL_STRIDE + 0x0100)
56 #define  MCA_SYNCGEN_STATUS_EN		(1 << 0)
57 #define MCA_SYNCGEN_MCLK_SEL(idx)	((idx) * MCA_CL_STRIDE + 0x0104)
58 #define MCA_SYNCGEN_HI_PERIOD(idx)	((idx) * MCA_CL_STRIDE + 0x0108)
59 #define MCA_SYNCGEN_LO_PERIOD(idx)	((idx) * MCA_CL_STRIDE + 0x010c)
60 
61 #define MCA_SERDES_BASE(idx, off)	((idx) * MCA_CL_STRIDE + (off))
62 #define MCA_SERDES_STATUS(idx, off)	(MCA_SERDES_BASE(idx, off) + 0x0000)
63 #define  MCA_SERDES_STATUS_EN		(1 << 0)
64 #define  MCA_SERDES_STATUS_RST		(1 << 1)
65 #define MCA_SERDES_CONF(idx, off)	(MCA_SERDES_BASE(idx, off) + 0x0004)
66 #define MCA_SERDES_CONF_NSLOTS_MASK	(0xf << 0)
67 #define MCA_SERDES_CONF_NSLOTS_SHIFT	0
68 #define MCA_SERDES_CONF_WIDTH_MASK	(0x1f << 4)
69 #define MCA_SERDES_CONF_WIDTH_32BIT	(0x10 << 4)
70 #define MCA_SERDES_CONF_BCLK_POL	(1 << 10)
71 #define MCA_SERDES_CONF_MAGIC		(0x7 << 12)
72 #define MCA_SERDES_CONF_SYNC_SEL_MASK	(0x7 << 16)
73 #define MCA_SERDES_CONF_SYNC_SEL_SHIFT	16
74 #define MCA_SERDES_BITSTART(idx, off)	(MCA_SERDES_BASE(idx, off) + 0x0008)
75 #define MCA_SERDES_CHANMASK0(idx, off)	(MCA_SERDES_BASE(idx, off) + 0x000c)
76 #define MCA_SERDES_CHANMASK1(idx, off)	(MCA_SERDES_BASE(idx, off) + 0x0010)
77 #define MCA_SERDES_CHANMASK2(idx, off)	(MCA_SERDES_BASE(idx, off) + 0x0014)
78 #define MCA_SERDES_CHANMASK3(idx, off)	(MCA_SERDES_BASE(idx, off) + 0x0018)
79 
80 #define MCA_PORT_ENABLE(idx)		((idx) * MCA_CL_STRIDE + 0x0600)
81 #define  MCA_PORT_ENABLE_CLOCKS		(0x3 << 1)
82 #define  MCA_PORT_ENABLE_TX_DATA	(1 << 3)
83 #define MCA_PORT_CLOCK_SEL(idx)		((idx) * MCA_CL_STRIDE + 0x0604)
84 #define  MCA_PORT_CLOCK_SEL_SHIFT	8
85 #define MCA_PORT_DATA_SEL(idx)		((idx) * MCA_CL_STRIDE + 0x0608)
86 #define  MCA_PORT_DATA_SEL_TXA(idx)	(1 << ((idx) * 2))
87 #define  MCA_PORT_DATA_SEL_TXB(idx)	(2 << ((idx) * 2))
88 
89 #define MCA_DMA_ADAPTER_A(idx)		((idx) * MCA_SW_STRIDE + 0x0000)
90 #define MCA_DMA_ADAPTER_B(idx)		((idx) * MCA_SW_STRIDE + 0x4000)
91 #define  MCA_DMA_ADAPTER_TX_LSB_PAD_SHIFT	0
92 #define  MCA_DMA_ADAPTER_TX_NCHANS_SHIFT	5
93 #define  MCA_DMA_ADAPTER_NCHANS_SHIFT		20
94 
95 
96 #define HREAD4(sc, reg)							\
97 	(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
98 #define HWRITE4(sc, reg, val)						\
99 	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
100 #define HSET4(sc, reg, bits)						\
101 	HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits))
102 #define HCLR4(sc, reg, bits)						\
103 	HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits))
104 
105 struct aplmca_dai {
106 	struct aplmca_softc	*ad_sc;
107 	struct dai_device	ad_dai;
108 	int			ad_cluster;
109 
110 	struct apldma_channel	*ad_ac;
111 	void			*ad_pbuf;
112 };
113 
114 struct aplmca_softc {
115 	struct device		sc_dev;
116 	bus_space_tag_t		sc_iot;
117 	bus_space_handle_t	sc_ioh;
118 	bus_space_handle_t	sc_sw_ioh;
119 
120 	int			sc_node;
121 	uint32_t		sc_phandle;
122 
123 	int			sc_nclusters;
124 	struct aplmca_dai	*sc_ad;
125 };
126 
127 int	aplmca_set_format(void *, uint32_t, uint32_t, uint32_t);
128 int	aplmca_set_sysclk(void *, uint32_t);
129 
130 int	aplmca_open(void *, int);
131 int	aplmca_set_params(void *, int, int,
132 	    struct audio_params *, struct audio_params *);
133 void	*aplmca_allocm(void *, int, size_t, int, int);
134 void	aplmca_freem(void *, void *, int);
135 int	aplmca_trigger_output(void *, void *, void *, int,
136 	    void (*)(void *), void *, struct audio_params *);
137 int	aplmca_trigger_input(void *, void *, void *, int,
138 	    void (*)(void *), void *, struct audio_params *);
139 int	aplmca_halt_output(void *);
140 int	aplmca_halt_input(void *);
141 
142 const struct audio_hw_if aplmca_hw_if = {
143 	.open = aplmca_open,
144 	.set_params = aplmca_set_params,
145 	.allocm = aplmca_allocm,
146 	.freem = aplmca_freem,
147 	.trigger_output = aplmca_trigger_output,
148 	.trigger_input = aplmca_trigger_input,
149 	.halt_output = aplmca_halt_output,
150 	.halt_input = aplmca_halt_input,
151 };
152 
153 int	aplmca_match(struct device *, void *, void *);
154 void	aplmca_attach(struct device *, struct device *, void *);
155 
156 const struct cfattach aplmca_ca = {
157 	sizeof (struct aplmca_softc), aplmca_match, aplmca_attach
158 };
159 
160 struct cfdriver aplmca_cd = {
161 	NULL, "aplmca", DV_DULL
162 };
163 
164 int
165 aplmca_match(struct device *parent, void *match, void *aux)
166 {
167 	struct fdt_attach_args *faa = aux;
168 
169 	return OF_is_compatible(faa->fa_node, "apple,mca");
170 }
171 
172 void
173 aplmca_attach(struct device *parent, struct device *self, void *aux)
174 {
175 	struct aplmca_softc *sc = (struct aplmca_softc *)self;
176 	struct fdt_attach_args *faa = aux;
177 	int i;
178 
179 	if (faa->fa_nreg < 2) {
180 		printf(": no registers\n");
181 		return;
182 	}
183 
184 	sc->sc_iot = faa->fa_iot;
185 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
186 	    faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
187 		printf(": can't map registers\n");
188 		return;
189 	}
190 	if (bus_space_map(sc->sc_iot, faa->fa_reg[1].addr,
191 	    faa->fa_reg[1].size, 0, &sc->sc_sw_ioh)) {
192 		bus_space_unmap(sc->sc_iot, sc->sc_ioh, faa->fa_reg[0].size);
193 		printf(": can't map registers\n");
194 		return;
195 	}
196 
197 	sc->sc_node = faa->fa_node;
198 	sc->sc_phandle = OF_getpropint(faa->fa_node, "phandle", 0);
199 
200 	sc->sc_nclusters = OF_getpropint(faa->fa_node, "apple,nclusters", 6);
201 	sc->sc_ad = mallocarray(sc->sc_nclusters, sizeof(*sc->sc_ad),
202 	    M_DEVBUF, M_WAITOK | M_ZERO);
203 
204 	for (i = 0; i < sc->sc_nclusters; i++) {
205 		sc->sc_ad[i].ad_cluster = i;
206 		sc->sc_ad[i].ad_sc = sc;
207 		sc->sc_ad[i].ad_dai.dd_node = sc->sc_node;
208 		sc->sc_ad[i].ad_dai.dd_cookie = &sc->sc_ad[i];
209 		sc->sc_ad[i].ad_dai.dd_hw_if = &aplmca_hw_if;
210 		sc->sc_ad[i].ad_dai.dd_set_format = aplmca_set_format;
211 		sc->sc_ad[i].ad_dai.dd_set_sysclk = aplmca_set_sysclk;
212 	}
213 
214 	printf("\n");
215 
216 	power_domain_enable_idx(sc->sc_node, 0);
217 
218 	for (i = 0; i < sc->sc_nclusters; i++) {
219 		HCLR4(sc, MCA_SERDES_STATUS(i, MCA_SERDES_TXA),
220 		    MCA_SERDES_STATUS_EN);
221 		HCLR4(sc, MCA_SYNCGEN_STATUS(i), MCA_SYNCGEN_STATUS_EN);
222 		HCLR4(sc, MCA_STATUS(i), MCA_STATUS_MCLK_EN);
223 	}
224 }
225 
226 int
227 aplmca_dai_init(struct aplmca_softc *sc, int port)
228 {
229 	struct aplmca_dai *ad = &sc->sc_ad[port];
230 	uint32_t conf;
231 	char name[5];
232 	int idx;
233 
234 	/* Allocate DMA channel. */
235 	snprintf(name, sizeof(name), "tx%da", ad->ad_cluster);
236 	idx = OF_getindex(sc->sc_node, name, "dma-names");
237 	if (idx == -1)
238 		return ENOENT;
239 	ad->ad_ac = apldma_alloc_channel(idx);
240 	if (ad->ad_ac == NULL)
241 		return ENOENT;
242 
243 	power_domain_enable_idx(sc->sc_node, port + 1);
244 
245 	/* Basic SERDES configuration. */
246 	conf = HREAD4(sc, MCA_SERDES_CONF(ad->ad_cluster, MCA_SERDES_TXA));
247 	conf &= ~MCA_SERDES_CONF_SYNC_SEL_MASK;
248 	conf |= (ad->ad_cluster + 1) << MCA_SERDES_CONF_SYNC_SEL_SHIFT;
249 	conf |= MCA_SERDES_CONF_MAGIC;
250 	HWRITE4(sc, MCA_SERDES_CONF(ad->ad_cluster, MCA_SERDES_TXA), conf);
251 
252 	/* Output port configuration. */
253 	HWRITE4(sc, MCA_PORT_CLOCK_SEL(port),
254 	    (ad->ad_cluster + 1) << MCA_PORT_CLOCK_SEL_SHIFT);
255 	HWRITE4(sc, MCA_PORT_DATA_SEL(port),
256 	    MCA_PORT_DATA_SEL_TXA(ad->ad_cluster));
257 	HWRITE4(sc, MCA_PORT_ENABLE(port),
258 	    MCA_PORT_ENABLE_CLOCKS | MCA_PORT_ENABLE_TX_DATA);
259 
260 	return 0;
261 }
262 
263 void
264 aplmca_dai_link(struct aplmca_softc *sc, int master, int port)
265 {
266 	struct aplmca_dai *ad = &sc->sc_ad[master];
267 
268 	HWRITE4(sc, MCA_PORT_CLOCK_SEL(port),
269 	    (ad->ad_cluster + 1) << MCA_PORT_CLOCK_SEL_SHIFT);
270 	HWRITE4(sc, MCA_PORT_DATA_SEL(port),
271 	    MCA_PORT_DATA_SEL_TXA(ad->ad_cluster));
272 	HWRITE4(sc, MCA_PORT_ENABLE(port),
273 	    MCA_PORT_ENABLE_CLOCKS | MCA_PORT_ENABLE_TX_DATA);
274 }
275 
276 uint32_t *
277 aplmca_dai_next_dai(uint32_t *cells)
278 {
279 	uint32_t phandle = cells[0];
280 	int node, ncells;
281 
282 	node = OF_getnodebyphandle(phandle);
283 	if (node == 0)
284 		return NULL;
285 
286 	ncells = OF_getpropint(node, "#sound-dai-cells", 0);
287 	return cells + ncells + 1;
288 }
289 
290 struct dai_device *
291 aplmca_alloc_cluster(int node)
292 {
293 	struct aplmca_softc *sc = aplmca_cd.cd_devs[0];
294 	uint32_t *dais;
295 	uint32_t *dai;
296 	uint32_t ports[2];
297 	int nports = 0;
298 	int len, i;
299 
300 	len = OF_getproplen(node, "sound-dai");
301 	if (len != 2 * sizeof(uint32_t) && len != 4 * sizeof(uint32_t))
302 		return NULL;
303 
304 	dais = malloc(len, M_TEMP, M_WAITOK);
305 	OF_getpropintarray(node, "sound-dai", dais, len);
306 
307 	dai = dais;
308 	while (dai && dai < dais + (len / sizeof(uint32_t))) {
309 		if (dai[0] == sc->sc_phandle && nports < nitems(ports))
310 			ports[nports++] = dai[1];
311 		dai = aplmca_dai_next_dai(dai);
312 	}
313 
314 	free(dais, M_TEMP, len);
315 
316 	if (nports == 0)
317 		return NULL;
318 	for (i = 0; i < nports; i++) {
319 		if (ports[i] >= sc->sc_nclusters)
320 			return NULL;
321 	}
322 
323 	if (sc->sc_ad[ports[0]].ad_ac != NULL)
324 		return NULL;
325 
326 	/* Setup the primary cluster. */
327 	if (aplmca_dai_init(sc, ports[0]))
328 		return NULL;
329 
330 	/*
331 	 * Additional interfaces receive the same output as the
332 	 * primary interface by linking the output port to the primary
333 	 * cluster.
334 	 */
335 	for (i = 1; i < nports; i++)
336 		aplmca_dai_link(sc, ports[0], ports[i]);
337 
338 	return &sc->sc_ad[ports[0]].ad_dai;
339 }
340 
341 int
342 aplmca_set_format(void *cookie, uint32_t fmt, uint32_t pol,
343     uint32_t clk)
344 {
345 	struct aplmca_dai *ad = cookie;
346 	struct aplmca_softc *sc = ad->ad_sc;
347 	uint32_t conf;
348 
349 	conf = HREAD4(sc, MCA_SERDES_CONF(ad->ad_cluster, MCA_SERDES_TXA));
350 	conf &= ~MCA_SERDES_CONF_WIDTH_MASK;
351 	conf |= MCA_SERDES_CONF_WIDTH_32BIT;
352 
353 	switch (fmt) {
354 	case DAI_FORMAT_I2S:
355 		conf &= ~MCA_SERDES_CONF_BCLK_POL;
356 		break;
357 	case DAI_FORMAT_RJ:
358 	case DAI_FORMAT_LJ:
359 		conf |= MCA_SERDES_CONF_BCLK_POL;
360 		break;
361 	default:
362 		return EINVAL;
363 	}
364 
365 	if (pol & DAI_POLARITY_IB)
366 		conf ^= MCA_SERDES_CONF_BCLK_POL;
367 	if (pol & DAI_POLARITY_IF)
368 		return EINVAL;
369 
370 	if (!(clk & DAI_CLOCK_CBM) || !(clk & DAI_CLOCK_CFM))
371 		return EINVAL;
372 
373 	HWRITE4(sc, MCA_SERDES_CONF(ad->ad_cluster, MCA_SERDES_TXA), conf);
374 
375 	return 0;
376 }
377 
378 int
379 aplmca_set_sysclk(void *cookie, uint32_t rate)
380 {
381 	struct aplmca_dai *ad = cookie;
382 	struct aplmca_softc *sc = ad->ad_sc;
383 
384 	return clock_set_frequency_idx(sc->sc_node, ad->ad_cluster, rate);
385 }
386 
387 int
388 aplmca_open(void *cookie, int flags)
389 {
390 	if ((flags & (FWRITE | FREAD)) == (FWRITE | FREAD))
391 		return ENXIO;
392 
393 	return 0;
394 }
395 
396 int
397 aplmca_set_params(void *cookie, int setmode, int usemode,
398     struct audio_params *play, struct audio_params *rec)
399 {
400 	if (setmode & AUMODE_PLAY) {
401 		play->sample_rate = 48000;
402 		play->encoding = AUDIO_ENCODING_SLINEAR_LE;
403 		play->precision = 24;
404 		play->bps = 4;
405 		play->msb = 0;
406 		play->channels = 2;
407 	}
408 
409 	return 0;
410 }
411 
412 void *
413 aplmca_allocm(void *cookie, int direction, size_t size, int type,
414     int flags)
415 {
416 	struct aplmca_dai *ad = cookie;
417 
418 	if (direction == AUMODE_PLAY) {
419 		ad->ad_pbuf = apldma_allocm(ad->ad_ac, size, flags);
420 		return ad->ad_pbuf;
421 	}
422 
423 	return malloc(size, type, flags | M_ZERO);
424 }
425 
426 void
427 aplmca_freem(void *cookie, void *addr, int type)
428 {
429 	struct aplmca_dai *ad = cookie;
430 
431 	if (addr == ad->ad_pbuf) {
432 		apldma_freem(ad->ad_ac);
433 		return;
434 	}
435 
436 	free(addr, type, 0);
437 }
438 
439 int
440 aplmca_trigger_output(void *cookie, void *start, void *end, int blksize,
441     void (*intr)(void *), void *intrarg, struct audio_params *params)
442 {
443 	struct aplmca_dai *ad = cookie;
444 	struct aplmca_softc *sc = ad->ad_sc;
445 	uint32_t conf, period;
446 	int pad;
447 
448 	if (params->channels > 16)
449 		return EINVAL;
450 
451 	/* Finalize SERDES configuration. */
452 	conf = HREAD4(sc, MCA_SERDES_CONF(ad->ad_cluster, MCA_SERDES_TXA));
453 	conf &= ~MCA_SERDES_CONF_NSLOTS_MASK;
454 	conf |= ((params->channels - 1) << MCA_SERDES_CONF_NSLOTS_SHIFT);
455 	HWRITE4(sc, MCA_SERDES_CONF(ad->ad_cluster, MCA_SERDES_TXA), conf);
456 	HWRITE4(sc, MCA_SERDES_CHANMASK0(ad->ad_cluster, MCA_SERDES_TXA),
457 	    0xffffffff);
458 	HWRITE4(sc, MCA_SERDES_CHANMASK1(ad->ad_cluster, MCA_SERDES_TXA),
459 	    0xffffffff << params->channels);
460 	HWRITE4(sc, MCA_SERDES_CHANMASK2(ad->ad_cluster, MCA_SERDES_TXA),
461 	    0xffffffff);
462 	HWRITE4(sc, MCA_SERDES_CHANMASK3(ad->ad_cluster, MCA_SERDES_TXA),
463 	    0xffffffff << params->channels);
464 
465 	period = params->channels * 32;
466 	HWRITE4(sc, MCA_SYNCGEN_HI_PERIOD(ad->ad_cluster), period - 2);
467 	HWRITE4(sc, MCA_SYNCGEN_LO_PERIOD(ad->ad_cluster), 0);
468 	HWRITE4(sc, MCA_MCLK_CONF(ad->ad_cluster),
469 	    1 << MCA_MCLK_CONF_DIV_SHIFT);
470 
471 	clock_enable_idx(sc->sc_node, ad->ad_cluster);
472 
473 	HWRITE4(sc, MCA_SYNCGEN_MCLK_SEL(ad->ad_cluster),
474 	    ad->ad_cluster + 1);
475 
476 	HSET4(sc, MCA_STATUS(ad->ad_cluster), MCA_STATUS_MCLK_EN);
477 	HSET4(sc, MCA_SYNCGEN_STATUS(ad->ad_cluster),
478 	    MCA_SYNCGEN_STATUS_EN);
479 	HSET4(sc, MCA_SERDES_STATUS(ad->ad_cluster, MCA_SERDES_TXA),
480 	    MCA_SERDES_STATUS_EN);
481 
482 	pad = params->bps * 8 - params->precision;
483 	bus_space_write_4(sc->sc_iot, sc->sc_sw_ioh,
484 	    MCA_DMA_ADAPTER_A(ad->ad_cluster),
485 	    pad << MCA_DMA_ADAPTER_TX_LSB_PAD_SHIFT |
486 	    2 << MCA_DMA_ADAPTER_TX_NCHANS_SHIFT |
487 	    2 << MCA_DMA_ADAPTER_NCHANS_SHIFT);
488 
489 	return apldma_trigger_output(ad->ad_ac, start, end, blksize,
490 	    intr, intrarg, params);
491 }
492 
493 int
494 aplmca_trigger_input(void *cookie, void *start, void *end, int blksize,
495     void (*intr)(void *), void *intrarg, struct audio_params *params)
496 {
497 	printf("%s\n", __func__);
498 	return EIO;
499 }
500 
501 int
502 aplmca_halt_output(void *cookie)
503 {
504 	struct aplmca_dai *ad = cookie;
505 	struct aplmca_softc *sc = ad->ad_sc;
506 	int error;
507 
508 	error = apldma_halt_output(ad->ad_ac);
509 
510 	HCLR4(sc, MCA_SERDES_STATUS(ad->ad_cluster, MCA_SERDES_TXA),
511 	    MCA_SERDES_STATUS_EN);
512 	HCLR4(sc, MCA_SYNCGEN_STATUS(ad->ad_cluster),
513 	    MCA_SYNCGEN_STATUS_EN);
514 	HCLR4(sc, MCA_STATUS(ad->ad_cluster), MCA_STATUS_MCLK_EN);
515 
516 	clock_disable_idx(sc->sc_node, ad->ad_cluster);
517 
518 	return error;
519 }
520 
521 int
522 aplmca_halt_input(void *cookie)
523 {
524 	printf("%s\n", __func__);
525 	return 0;
526 }
527