1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /*
28 * Purpose: Driver for the Creative P16X AC97 audio controller
29 */
30 /*
31 *
32 * Copyright (C) 4Front Technologies 1996-2009.
33 *
34 * This software is released under CDDL 1.0 source license.
35 * See the COPYING file included in the main directory of this source
36 * distribution for the license terms and conditions.
37 */
38
39 #include <sys/types.h>
40 #include <sys/modctl.h>
41 #include <sys/kmem.h>
42 #include <sys/conf.h>
43 #include <sys/ddi.h>
44 #include <sys/sunddi.h>
45 #include <sys/pci.h>
46 #include <sys/note.h>
47 #include <sys/audio/audio_driver.h>
48 #include <sys/audio/ac97.h>
49
50 #include "audiop16x.h"
51
52 /*
53 * These boards use an AC'97 codec, but don't have all of the
54 * various outputs that the AC'97 codec can offer. We just
55 * suppress them for now.
56 */
57 static char *p16x_remove_ac97[] = {
58 AUDIO_CTRL_ID_BEEP,
59 AUDIO_CTRL_ID_VIDEO,
60 AUDIO_CTRL_ID_MICSRC,
61 AUDIO_CTRL_ID_SPEAKER,
62 AUDIO_CTRL_ID_SPKSRC,
63 NULL
64 };
65
66 static struct ddi_device_acc_attr dev_attr = {
67 DDI_DEVICE_ATTR_V0,
68 DDI_STRUCTURE_LE_ACC,
69 DDI_STRICTORDER_ACC
70 };
71
72 static struct ddi_device_acc_attr buf_attr = {
73 DDI_DEVICE_ATTR_V0,
74 DDI_NEVERSWAP_ACC,
75 DDI_STRICTORDER_ACC
76 };
77
78 static ddi_dma_attr_t dma_attr_buf = {
79 DMA_ATTR_V0, /* version number */
80 0x00000000, /* low DMA address range */
81 0xffffffff, /* high DMA address range */
82 0xfffffffe, /* DMA counter register */
83 4, /* DMA address alignment */
84 0x3c, /* DMA burstsizes */
85 4, /* min effective DMA size */
86 0xffffffff, /* max DMA xfer size */
87 0xffffffff, /* segment boundary */
88 1, /* s/g length */
89 4, /* granularity of device */
90 0 /* Bus specific DMA flags */
91 };
92
93 static int p16x_attach(dev_info_t *);
94 static int p16x_resume(dev_info_t *);
95 static int p16x_detach(p16x_dev_t *);
96 static int p16x_suspend(p16x_dev_t *);
97
98 static int p16x_open(void *, int, unsigned *, caddr_t *);
99 static void p16x_close(void *);
100 static int p16x_start(void *);
101 static void p16x_stop(void *);
102 static int p16x_format(void *);
103 static int p16x_channels(void *);
104 static int p16x_rate(void *);
105 static uint64_t p16x_count(void *);
106 static void p16x_sync(void *, unsigned);
107 static void p16x_chinfo(void *, int, unsigned *, unsigned *);
108
109 static uint16_t p16x_read_ac97(void *, uint8_t);
110 static void p16x_write_ac97(void *, uint8_t, uint16_t);
111 static int p16x_alloc_port(p16x_dev_t *, int);
112 static void p16x_destroy(p16x_dev_t *);
113 static void p16x_hwinit(p16x_dev_t *);
114
115 static audio_engine_ops_t p16x_engine_ops = {
116 AUDIO_ENGINE_VERSION,
117 p16x_open,
118 p16x_close,
119 p16x_start,
120 p16x_stop,
121 p16x_count,
122 p16x_format,
123 p16x_channels,
124 p16x_rate,
125 p16x_sync,
126 NULL,
127 p16x_chinfo,
128 NULL
129 };
130
131 static unsigned int
read_reg(p16x_dev_t * dev,int reg,int chn)132 read_reg(p16x_dev_t *dev, int reg, int chn)
133 {
134 unsigned int val;
135
136 mutex_enter(&dev->mutex);
137 OUTL(dev, (reg << 16) | (chn & 0xffff), PTR); /* Pointer */
138 val = INL(dev, DR); /* Data */
139 mutex_exit(&dev->mutex);
140
141 return (val);
142 }
143
144 static void
write_reg(p16x_dev_t * dev,int reg,int chn,unsigned int value)145 write_reg(p16x_dev_t *dev, int reg, int chn, unsigned int value)
146 {
147
148 mutex_enter(&dev->mutex);
149 OUTL(dev, (reg << 16) | (chn & 0xffff), PTR); /* Pointer */
150 OUTL(dev, value, DR); /* Data */
151 mutex_exit(&dev->mutex);
152 }
153
154 static void
set_reg_bits(p16x_dev_t * dev,int reg,int chn,unsigned int mask)155 set_reg_bits(p16x_dev_t *dev, int reg, int chn, unsigned int mask)
156 {
157 unsigned int val;
158 mutex_enter(&dev->mutex);
159 OUTL(dev, (reg << 16) | (chn & 0xffff), PTR); /* Pointer */
160 val = INL(dev, DR); /* Data */
161 val |= mask;
162 OUTL(dev, val, DR); /* Data */
163 mutex_exit(&dev->mutex);
164 }
165
166 static void
clear_reg_bits(p16x_dev_t * dev,int reg,int chn,unsigned int mask)167 clear_reg_bits(p16x_dev_t *dev, int reg, int chn, unsigned int mask)
168 {
169 unsigned int val;
170 mutex_enter(&dev->mutex);
171 OUTL(dev, (reg << 16) | (chn & 0xffff), PTR); /* Pointer */
172 val = INL(dev, DR); /* Data */
173 val &= ~(mask);
174 OUTL(dev, val, DR); /* Data */
175 mutex_exit(&dev->mutex);
176 }
177
178 static uint16_t
p16x_read_ac97(void * arg,uint8_t index)179 p16x_read_ac97(void *arg, uint8_t index)
180 {
181 p16x_dev_t *dev = arg;
182 uint16_t value;
183 int i;
184
185 OUTB(dev, index, AC97A);
186 for (i = 0; i < 10000; i++)
187 if (INB(dev, AC97A) & 0x80)
188 break;
189 value = INW(dev, AC97D);
190 return (value);
191 }
192
193 static void
p16x_write_ac97(void * arg,uint8_t index,uint16_t data)194 p16x_write_ac97(void *arg, uint8_t index, uint16_t data)
195 {
196 p16x_dev_t *dev = arg;
197 unsigned int i;
198
199 OUTB(dev, index, AC97A);
200 for (i = 0; i < 10000; i++)
201 if (INB(dev, AC97A) & 0x80)
202 break;
203 OUTW(dev, data, AC97D);
204 }
205
206 /*
207 * Audio routines
208 */
209
210 int
p16x_open(void * arg,int flag,uint_t * nframes,caddr_t * bufp)211 p16x_open(void *arg, int flag, uint_t *nframes, caddr_t *bufp)
212 {
213 p16x_port_t *port = arg;
214
215 _NOTE(ARGUNUSED(flag));
216
217 port->count = 0;
218 *nframes = port->buf_frames;
219 *bufp = port->buf_kaddr;
220
221 return (0);
222 }
223
224 void
p16x_close(void * arg)225 p16x_close(void *arg)
226 {
227 _NOTE(ARGUNUSED(arg));
228 }
229
230 int
p16x_start(void * arg)231 p16x_start(void *arg)
232 {
233 p16x_port_t *port = arg;
234 p16x_dev_t *dev = port->dev;
235
236 port->offset = 0;
237
238 if (port->port_num == P16X_REC) {
239 write_reg(dev, CRFA, 0, 0);
240 write_reg(dev, CRCAV, 0, 0);
241
242 /* Enable rec channel */
243 set_reg_bits(dev, SA, 0, 0x100);
244 } else {
245 for (int i = 0; i < 3; i++) {
246 write_reg(dev, PTBA, i, 0);
247 write_reg(dev, PTBS, i, 0);
248 write_reg(dev, PTCA, i, 0);
249 write_reg(dev, PFEA, i, 0);
250 write_reg(dev, CPFA, i, 0);
251 write_reg(dev, CPCAV, i, 0);
252 }
253
254 /* Enable play channel */
255 set_reg_bits(dev, SA, 0, 0x7);
256 }
257
258 return (0);
259 }
260
261 void
p16x_stop(void * arg)262 p16x_stop(void *arg)
263 {
264 p16x_port_t *port = arg;
265 p16x_dev_t *dev = port->dev;
266
267 if (port->port_num == P16X_REC) {
268 /* Disable rec channel */
269 clear_reg_bits(dev, SA, 0, 0x100);
270
271 } else {
272 /* Disable Play channel */
273 clear_reg_bits(dev, SA, 0, 0x7);
274 }
275 }
276
277 int
p16x_format(void * arg)278 p16x_format(void *arg)
279 {
280 _NOTE(ARGUNUSED(arg));
281
282 return (AUDIO_FORMAT_S16_LE);
283 }
284
285 int
p16x_channels(void * arg)286 p16x_channels(void *arg)
287 {
288 p16x_port_t *port = arg;
289
290 return (port->nchan);
291 }
292
293 int
p16x_rate(void * arg)294 p16x_rate(void *arg)
295 {
296 _NOTE(ARGUNUSED(arg));
297
298 return (48000);
299 }
300
301 void
p16x_sync(void * arg,unsigned nframes)302 p16x_sync(void *arg, unsigned nframes)
303 {
304 p16x_port_t *port = arg;
305 _NOTE(ARGUNUSED(nframes));
306
307 (void) ddi_dma_sync(port->buf_dmah, 0, 0, port->syncdir);
308 }
309
310 uint64_t
p16x_count(void * arg)311 p16x_count(void *arg)
312 {
313 p16x_port_t *port = arg;
314 p16x_dev_t *dev = port->dev;
315 uint64_t val;
316 uint32_t offset, n;
317
318 if (port->port_num == P16X_PLAY) {
319 offset = read_reg(dev, CPFA, 0);
320 } else {
321 offset = read_reg(dev, CRFA, 0);
322 }
323
324 /* get the offset, and switch to frames */
325 offset /= (2 * sizeof (uint16_t));
326
327 if (offset >= port->offset) {
328 n = offset - port->offset;
329 } else {
330 n = offset + (port->buf_frames - port->offset);
331 }
332 port->offset = offset;
333 port->count += n;
334 val = port->count;
335
336 return (val);
337 }
338
339 static void
p16x_chinfo(void * arg,int chan,unsigned * offset,unsigned * incr)340 p16x_chinfo(void *arg, int chan, unsigned *offset, unsigned *incr)
341 {
342 p16x_port_t *port = arg;
343 unsigned mult;
344
345 if (port->port_num == P16X_PLAY) {
346 switch (chan) {
347 case 0: /* left front */
348 case 1: /* right front */
349 mult = 0;
350 break;
351 case 2: /* center */
352 case 3: /* lfe */
353 mult = 2;
354 break;
355 case 4: /* left surround */
356 case 5: /* right surround */
357 mult = 1;
358 break;
359 }
360 *offset = (port->buf_frames * 2 * mult) + (chan % 2);
361 *incr = 2;
362 } else {
363 *offset = chan;
364 *incr = 2;
365 }
366 }
367
368 /* private implementation bits */
369
370 int
p16x_alloc_port(p16x_dev_t * dev,int num)371 p16x_alloc_port(p16x_dev_t *dev, int num)
372 {
373 p16x_port_t *port;
374 size_t len;
375 ddi_dma_cookie_t cookie;
376 uint_t count;
377 int dir;
378 unsigned caps;
379 audio_dev_t *adev;
380
381 adev = dev->adev;
382 port = kmem_zalloc(sizeof (*port), KM_SLEEP);
383 dev->port[num] = port;
384 port->dev = dev;
385
386 switch (num) {
387 case P16X_REC:
388 port->syncdir = DDI_DMA_SYNC_FORKERNEL;
389 caps = ENGINE_INPUT_CAP;
390 dir = DDI_DMA_READ;
391 port->port_num = P16X_REC;
392 port->nchan = 2;
393 break;
394 case P16X_PLAY:
395 port->syncdir = DDI_DMA_SYNC_FORDEV;
396 caps = ENGINE_OUTPUT_CAP;
397 dir = DDI_DMA_WRITE;
398 port->port_num = P16X_PLAY;
399 port->nchan = 6;
400 break;
401 default:
402 return (DDI_FAILURE);
403 }
404
405 /*
406 * NB: The device operates in pairs of dwords at a time, for
407 * performance reasons. So make sure that our buffer is
408 * arranged as a whole number of these. The value below gives
409 * a reasonably large buffer so we can support a deep
410 * playahead if we need to (and we should avoid input
411 * overruns.)
412 */
413 port->buf_frames = 4096;
414 port->buf_size = port->buf_frames * port->nchan * sizeof (uint16_t);
415
416 /* now allocate buffers */
417 if (ddi_dma_alloc_handle(dev->dip, &dma_attr_buf, DDI_DMA_SLEEP, NULL,
418 &port->buf_dmah) != DDI_SUCCESS) {
419 audio_dev_warn(adev, "failed to allocate BUF handle");
420 return (DDI_FAILURE);
421 }
422
423 if (ddi_dma_mem_alloc(port->buf_dmah, port->buf_size,
424 &buf_attr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
425 &port->buf_kaddr, &len, &port->buf_acch) != DDI_SUCCESS) {
426 audio_dev_warn(adev, "failed to allocate BUF memory");
427 return (DDI_FAILURE);
428 }
429
430 if (ddi_dma_addr_bind_handle(port->buf_dmah, NULL, port->buf_kaddr,
431 len, DDI_DMA_CONSISTENT | dir, DDI_DMA_SLEEP, NULL, &cookie,
432 &count) != DDI_SUCCESS) {
433 audio_dev_warn(adev, "failed binding BUF DMA handle");
434 return (DDI_FAILURE);
435 }
436 port->buf_paddr = cookie.dmac_address;
437
438 port->engine = audio_engine_alloc(&p16x_engine_ops, caps);
439 if (port->engine == NULL) {
440 audio_dev_warn(adev, "audio_engine_alloc failed");
441 return (DDI_FAILURE);
442 }
443
444 audio_engine_set_private(port->engine, port);
445 audio_dev_add_engine(adev, port->engine);
446
447 return (DDI_SUCCESS);
448 }
449
450 void
p16x_destroy(p16x_dev_t * dev)451 p16x_destroy(p16x_dev_t *dev)
452 {
453 mutex_destroy(&dev->mutex);
454
455 for (int i = 0; i < P16X_NUM_PORT; i++) {
456 p16x_port_t *port = dev->port[i];
457 if (!port)
458 continue;
459 if (port->engine) {
460 audio_dev_remove_engine(dev->adev, port->engine);
461 audio_engine_free(port->engine);
462 }
463 if (port->buf_paddr) {
464 (void) ddi_dma_unbind_handle(port->buf_dmah);
465 }
466 if (port->buf_acch) {
467 ddi_dma_mem_free(&port->buf_acch);
468 }
469 if (port->buf_dmah) {
470 ddi_dma_free_handle(&port->buf_dmah);
471 }
472 kmem_free(port, sizeof (*port));
473 }
474
475 if (dev->ac97 != NULL) {
476 ac97_free(dev->ac97);
477 }
478 if (dev->adev != NULL) {
479 audio_dev_free(dev->adev);
480 }
481 if (dev->regsh != NULL) {
482 ddi_regs_map_free(&dev->regsh);
483 }
484 if (dev->pcih != NULL) {
485 pci_config_teardown(&dev->pcih);
486 }
487 kmem_free(dev, sizeof (*dev));
488 }
489
490 void
p16x_hwinit(p16x_dev_t * dev)491 p16x_hwinit(p16x_dev_t *dev)
492 {
493 p16x_port_t *port;
494 uint32_t paddr;
495 uint32_t chunksz;
496 int i;
497
498 for (i = 0; i < 3; i++) {
499 write_reg(dev, PTBA, i, 0);
500 write_reg(dev, PTBS, i, 0);
501 write_reg(dev, PTCA, i, 0);
502 write_reg(dev, PFEA, i, 0);
503 write_reg(dev, CPFA, i, 0);
504 write_reg(dev, CPCAV, i, 0);
505 write_reg(dev, CRFA, i, 0);
506 write_reg(dev, CRCAV, i, 0);
507 }
508 write_reg(dev, SCS0, 0, 0x02108504);
509 write_reg(dev, SCS1, 0, 0x02108504);
510 write_reg(dev, SCS2, 0, 0x02108504);
511
512 /* set the spdif/analog combo jack to analog out */
513 write_reg(dev, SPC, 0, 0x00000700);
514 write_reg(dev, EA_aux, 0, 0x0001003f);
515
516 port = dev->port[P16X_REC];
517 /* Set physical address of the DMA buffer */
518 write_reg(dev, RFBA, 0, port->buf_paddr);
519 write_reg(dev, RFBS, 0, (port->buf_size) << 16);
520
521 /* Set physical address of the DMA buffer */
522 port = dev->port[P16X_PLAY];
523 paddr = port->buf_paddr;
524 chunksz = port->buf_frames * 4;
525 write_reg(dev, PFBA, 0, paddr);
526 write_reg(dev, PFBS, 0, chunksz << 16);
527 paddr += chunksz;
528 write_reg(dev, PFBA, 1, paddr);
529 write_reg(dev, PFBS, 1, chunksz << 16);
530 paddr += chunksz;
531 write_reg(dev, PFBA, 2, paddr);
532 write_reg(dev, PFBS, 2, chunksz << 16);
533
534 OUTL(dev, 0x1080, GPIO); /* GPIO */
535 /* Clear any pending interrupts */
536 OUTL(dev, INTR_ALL, IP);
537 OUTL(dev, 0, IE);
538 OUTL(dev, 0x9, HC); /* Enable audio */
539 }
540
541 int
p16x_attach(dev_info_t * dip)542 p16x_attach(dev_info_t *dip)
543 {
544 uint16_t vendor, device;
545 p16x_dev_t *dev;
546 ddi_acc_handle_t pcih;
547
548 dev = kmem_zalloc(sizeof (*dev), KM_SLEEP);
549 dev->dip = dip;
550 ddi_set_driver_private(dip, dev);
551
552 mutex_init(&dev->mutex, NULL, MUTEX_DRIVER, NULL);
553
554 if ((dev->adev = audio_dev_alloc(dip, 0)) == NULL) {
555 cmn_err(CE_WARN, "audio_dev_alloc failed");
556 goto error;
557 }
558
559 if (pci_config_setup(dip, &pcih) != DDI_SUCCESS) {
560 audio_dev_warn(dev->adev, "pci_config_setup failed");
561 goto error;
562 }
563 dev->pcih = pcih;
564
565 vendor = pci_config_get16(pcih, PCI_CONF_VENID);
566 device = pci_config_get16(pcih, PCI_CONF_DEVID);
567 if (vendor != CREATIVE_VENDOR_ID ||
568 device != SB_P16X_ID) {
569 audio_dev_warn(dev->adev, "Hardware not recognized "
570 "(vendor=%x, dev=%x)", vendor, device);
571 goto error;
572 }
573
574 /* set PCI command register */
575 pci_config_put16(pcih, PCI_CONF_COMM,
576 pci_config_get16(pcih, PCI_CONF_COMM) |
577 PCI_COMM_MAE | PCI_COMM_IO);
578
579
580 if ((ddi_regs_map_setup(dip, 1, &dev->base, 0, 0, &dev_attr,
581 &dev->regsh)) != DDI_SUCCESS) {
582 audio_dev_warn(dev->adev, "failed to map registers");
583 goto error;
584 }
585
586 audio_dev_set_description(dev->adev, "Creative Sound Blaster Live!");
587 audio_dev_set_version(dev->adev, "SBO200");
588
589 if ((p16x_alloc_port(dev, P16X_PLAY) != DDI_SUCCESS) ||
590 (p16x_alloc_port(dev, P16X_REC) != DDI_SUCCESS)) {
591 goto error;
592 }
593
594 p16x_hwinit(dev);
595
596 dev->ac97 = ac97_allocate(dev->adev, dip,
597 p16x_read_ac97, p16x_write_ac97, dev);
598 if (dev->ac97 == NULL) {
599 audio_dev_warn(dev->adev, "failed to allocate ac97 handle");
600 goto error;
601 }
602
603 ac97_probe_controls(dev->ac97);
604
605 /* remove the AC'97 controls we don't want to expose */
606 for (int i = 0; p16x_remove_ac97[i]; i++) {
607 ac97_ctrl_t *ctrl;
608 ctrl = ac97_control_find(dev->ac97, p16x_remove_ac97[i]);
609 if (ctrl != NULL) {
610 ac97_control_unregister(ctrl);
611 }
612 }
613
614 ac97_register_controls(dev->ac97);
615
616 if (audio_dev_register(dev->adev) != DDI_SUCCESS) {
617 audio_dev_warn(dev->adev, "unable to register with framework");
618 goto error;
619 }
620
621 ddi_report_dev(dip);
622
623 return (DDI_SUCCESS);
624
625 error:
626 p16x_destroy(dev);
627 return (DDI_FAILURE);
628 }
629
630 int
p16x_resume(dev_info_t * dip)631 p16x_resume(dev_info_t *dip)
632 {
633 p16x_dev_t *dev;
634
635 dev = ddi_get_driver_private(dip);
636
637 p16x_hwinit(dev);
638
639 ac97_reset(dev->ac97);
640
641 audio_dev_resume(dev->adev);
642
643 return (DDI_SUCCESS);
644 }
645
646 int
p16x_detach(p16x_dev_t * dev)647 p16x_detach(p16x_dev_t *dev)
648 {
649 if (audio_dev_unregister(dev->adev) != DDI_SUCCESS)
650 return (DDI_FAILURE);
651
652 p16x_destroy(dev);
653 return (DDI_SUCCESS);
654 }
655
656 int
p16x_suspend(p16x_dev_t * dev)657 p16x_suspend(p16x_dev_t *dev)
658 {
659 audio_dev_suspend(dev->adev);
660
661 return (DDI_SUCCESS);
662 }
663
664 static int p16x_ddi_attach(dev_info_t *, ddi_attach_cmd_t);
665 static int p16x_ddi_detach(dev_info_t *, ddi_detach_cmd_t);
666 static int p16x_ddi_quiesce(dev_info_t *);
667
668 static struct dev_ops p16x_dev_ops = {
669 DEVO_REV, /* rev */
670 0, /* refcnt */
671 NULL, /* getinfo */
672 nulldev, /* identify */
673 nulldev, /* probe */
674 p16x_ddi_attach, /* attach */
675 p16x_ddi_detach, /* detach */
676 nodev, /* reset */
677 NULL, /* cb_ops */
678 NULL, /* bus_ops */
679 NULL, /* power */
680 p16x_ddi_quiesce, /* quiesce */
681 };
682
683 static struct modldrv p16x_modldrv = {
684 &mod_driverops, /* drv_modops */
685 "Creative P16X Audio", /* linkinfo */
686 &p16x_dev_ops, /* dev_ops */
687 };
688
689 static struct modlinkage modlinkage = {
690 MODREV_1,
691 { &p16x_modldrv, NULL }
692 };
693
694 int
_init(void)695 _init(void)
696 {
697 int rv;
698
699 audio_init_ops(&p16x_dev_ops, P16X_NAME);
700 if ((rv = mod_install(&modlinkage)) != 0) {
701 audio_fini_ops(&p16x_dev_ops);
702 }
703 return (rv);
704 }
705
706 int
_fini(void)707 _fini(void)
708 {
709 int rv;
710
711 if ((rv = mod_remove(&modlinkage)) == 0) {
712 audio_fini_ops(&p16x_dev_ops);
713 }
714 return (rv);
715 }
716
717 int
_info(struct modinfo * modinfop)718 _info(struct modinfo *modinfop)
719 {
720 return (mod_info(&modlinkage, modinfop));
721 }
722
723 int
p16x_ddi_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)724 p16x_ddi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
725 {
726 switch (cmd) {
727 case DDI_ATTACH:
728 return (p16x_attach(dip));
729
730 case DDI_RESUME:
731 return (p16x_resume(dip));
732
733 default:
734 return (DDI_FAILURE);
735 }
736 }
737
738 int
p16x_ddi_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)739 p16x_ddi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
740 {
741 p16x_dev_t *dev;
742
743 dev = ddi_get_driver_private(dip);
744
745 switch (cmd) {
746 case DDI_DETACH:
747 return (p16x_detach(dev));
748
749 case DDI_SUSPEND:
750 return (p16x_suspend(dev));
751
752 default:
753 return (DDI_FAILURE);
754 }
755 }
756
757 int
p16x_ddi_quiesce(dev_info_t * dip)758 p16x_ddi_quiesce(dev_info_t *dip)
759 {
760 p16x_dev_t *dev;
761
762 dev = ddi_get_driver_private(dip);
763
764 write_reg(dev, SA, 0, 0);
765 OUTL(dev, 0x01, HC);
766
767 return (DDI_SUCCESS);
768 }
769