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 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26
27 /*
28 * Starcat PCI SBBC Nexus Driver.
29 *
30 * This source code's compiled binary runs on both a Starcat System
31 * Controller (SSC) and a Starcat Domain. One of the SBBC hardware
32 * registers is read during attach(9e) in order to determine which
33 * environment the driver is executing on.
34 *
35 * On both the SSC and the Domain, this driver provides nexus driver
36 * services to its Device Tree children. Note that the children in
37 * each environment are not necessarily the same.
38 *
39 * This driver allows one concurrent open(2) of its associated device
40 * (/dev/sbbc0). The client uses the file descriptor to issue
41 * ioctl(2)'s in order to read and write from the 2MB (PCI) space
42 * reserved for "SBBC Internal Registers". Among other things,
43 * these registers consist of command/control/status registers for
44 * devices such as Console Bus, I2C, EPLD, IOSRAM, and JTAG. The 2MB
45 * space is very sparse; EINVAL is returned if a reserved or unaligned
46 * address is specified in the ioctl(2).
47 *
48 * Note that the 2MB region reserved for SBBC Internal Registers is
49 * a subset of the 128MB of PCI address space addressable by the SBBC
50 * ASIC. Address space outside of the 2MB (such as the 64MB reserved
51 * for the Console Bus) is not accessible via this driver.
52 *
53 * Also, note that the SBBC Internal Registers are only read and
54 * written by the SSC; no process on the Domain accesses these
55 * registers. As a result, the registers are unmapped (when running
56 * on the Domain) near the end of attach(9e) processing. This conserves
57 * kernel virtual address space resources (as one instance of the driver
58 * is created for each Domain-side IO assembly). (To be complete, only
59 * one instance of the driver is created on the SSC).
60 */
61
62 #include <sys/types.h>
63
64 #include <sys/conf.h> /* req. by dev_ops flags MTSAFE etc. */
65 #include <sys/ddi.h>
66 #include <sys/sunddi.h>
67 #include <sys/ddi_impldefs.h>
68 #include <sys/ddi_subrdefs.h>
69 #include <sys/pci.h>
70 #include <sys/pci/pci_nexus.h>
71 #include <sys/autoconf.h>
72 #include <sys/cmn_err.h>
73 #include <sys/param.h>
74 #include <sys/errno.h>
75 #include <sys/kmem.h>
76 #include <sys/debug.h>
77 #include <sys/sysmacros.h>
78 #include <sys/machsystm.h>
79 #include <sys/modctl.h>
80 #include <sys/stat.h>
81
82
83 #include <sys/sbbcreg.h> /* hw description */
84 #include <sys/sbbcvar.h> /* driver description */
85 #include <sys/sbbcio.h> /* ioctl description */
86
87 #define getprop(dip, name, addr, intp) \
88 ddi_getlongprop(DDI_DEV_T_ANY, (dip), DDI_PROP_DONTPASS, \
89 (name), (caddr_t)(addr), (intp))
90
91 /* driver entry point fn definitions */
92 static int sbbc_open(dev_t *, int, int, cred_t *);
93 static int sbbc_close(dev_t, int, int, cred_t *);
94 static int sbbc_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
95
96 /* configuration entry point fn definitions */
97 static int sbbc_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
98 static int sbbc_attach(dev_info_t *, ddi_attach_cmd_t);
99 static int sbbc_detach(dev_info_t *, ddi_detach_cmd_t);
100
101 /* local utility routines */
102 /*
103 * NOTE - sbbc_offset_valid contains detailed address information taken from
104 * the Serengeti Architecture Programmer's Reference Manual. If any
105 * changes are made to the SBBC registers, this routine may need to be
106 * updated.
107 */
108 static int sbbc_offset_valid(uint32_t offset);
109
110 /*
111 * function prototypes for bus ops routines:
112 */
113 static int sbbc_busmap(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
114 off_t offset, off_t len, caddr_t *addrp);
115 static int sbbc_ctlops(dev_info_t *dip, dev_info_t *rdip,
116 ddi_ctl_enum_t op, void *arg, void *result);
117
118 static int sbbc_intr_ops(dev_info_t *dip, dev_info_t *rdip,
119 ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result);
120 static int sbbc_add_intr_impl(dev_info_t *dip, dev_info_t *rdip,
121 ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result);
122 static int sbbc_remove_intr_impl(dev_info_t *dip, dev_info_t *rdip,
123 ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result);
124 static int sbbc_update_intr_state(dev_info_t *dip, dev_info_t *rdip,
125 ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result);
126
127 static int sbbc_apply_range(struct sbbcsoft *sbbc_p, dev_info_t *rdip,
128 sbbc_child_regspec_t *child_rp, pci_regspec_t *rp);
129
130 static int sbbc_init(struct sbbcsoft *);
131
132 static uint_t sbbc_intr_wrapper(caddr_t arg);
133
134 static int sbbc_get_ranges(struct sbbcsoft *);
135 static int sbbc_config4pci(struct sbbcsoft *);
136 static int sbbc_initchild(dev_info_t *, dev_info_t *, dev_info_t *);
137 static int sbbc_uninitchild(dev_info_t *, dev_info_t *);
138 static void sbbc_remove_reg_maps(struct sbbcsoft *);
139
140 /* debugging functions */
141 #ifdef DEBUG
142 uint32_t sbbc_dbg_flags = 0x0;
143 static void sbbc_dbg(uint32_t flag, dev_info_t *dip, char *fmt,
144 uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5);
145 static void sbbc_dump_devid(dev_info_t *, struct sbbcsoft *, int instance);
146 #endif
147
148 /*
149 * For tracing, allocate space for the trace buffer
150 */
151 #if defined(SBBC_TRACE)
152 struct sbbctrace sbbctrace_buffer[NSBBCTRACE+1];
153 struct sbbctrace *sbbctrace_ptr;
154 int sbbctrace_count;
155 #endif
156
157 /*
158 * Local declarations and variables
159 */
160
161 static void *sbbcsoft_statep;
162
163 /* Determines whether driver is executing on System Controller or Domain */
164 int sbbc_scmode = FALSE;
165
166 /*
167 * ops stuff.
168 */
169 static struct bus_ops sbbc_bus_ops = {
170 BUSO_REV,
171 sbbc_busmap,
172 0,
173 0,
174 0,
175 NULL, /* (*bus_map_fault)() */
176 ddi_no_dma_map,
177 ddi_no_dma_allochdl,
178 ddi_no_dma_freehdl, /* (*bus_dma_freehdl)() */
179 ddi_no_dma_bindhdl, /* (*bus_dma_bindhdl)() */
180 ddi_no_dma_unbindhdl, /* (*bus_dma_unbindhdl)() */
181 ddi_no_dma_flush, /* (*bus_dma_flush)() */
182 ddi_no_dma_win, /* (*bus_dma_win)() */
183 ddi_no_dma_mctl, /* (*bus_dma_ctl)() */
184 sbbc_ctlops,
185 ddi_bus_prop_op,
186 0, /* (*bus_get_eventcookie)(); */
187 0, /* (*bus_add_eventcall)(); */
188 0, /* (*bus_remove_eventcall)(); */
189 0, /* (*bus_post_event)(); */
190 0, /* (*bus_intr_ctl)(); */
191 0, /* (*bus_config)(); */
192 0, /* (*bus_unconfig)(); */
193 0, /* (*bus_fm_init)(); */
194 0, /* (*bus_fm_fini)(); */
195 0, /* (*bus_fm_access_enter)(); */
196 0, /* (*bus_fm_access_exit)(); */
197 0, /* (*bus_power)(); */
198 sbbc_intr_ops /* (*bus_intr_op)(); */
199 };
200
201 /*
202 * cb_ops
203 */
204 static struct cb_ops sbbc_cb_ops = {
205 sbbc_open, /* cb_open */
206 sbbc_close, /* cb_close */
207 nodev, /* cb_strategy */
208 nodev, /* cb_print */
209 nodev, /* cb_dump */
210 nodev, /* cb_read */
211 nodev, /* cb_write */
212 sbbc_ioctl, /* cb_ioctl */
213 nodev, /* cb_devmap */
214 nodev, /* cb_mmap */
215 nodev, /* cb_segmap */
216 nochpoll, /* cb_chpoll */
217 ddi_prop_op, /* cb_prop_op */
218 NULL, /* cb_stream */
219 (int)(D_NEW | D_MP) /* cb_flag */
220 };
221
222 /*
223 * Declare ops vectors for auto configuration.
224 */
225 struct dev_ops sbbc_ops = {
226 DEVO_REV, /* devo_rev */
227 0, /* devo_refcnt */
228 sbbc_getinfo, /* devo_getinfo */
229 nulldev, /* devo_identify */
230 nulldev, /* devo_probe */
231 sbbc_attach, /* devo_attach */
232 sbbc_detach, /* devo_detach */
233 nodev, /* devo_reset */
234 &sbbc_cb_ops, /* devo_cb_ops */
235 &sbbc_bus_ops, /* devo_bus_ops */
236 nulldev, /* devo_power */
237 ddi_quiesce_not_supported, /* devo_quiesce */
238 };
239
240 /*
241 * Loadable module support.
242 */
243 extern struct mod_ops mod_driverops;
244
245 static struct modldrv sbbcmodldrv = {
246 &mod_driverops, /* type of module - driver */
247 "PCI Sbbc Nexus Driver",
248 &sbbc_ops,
249 };
250
251 static struct modlinkage sbbcmodlinkage = {
252 MODREV_1,
253 &sbbcmodldrv,
254 NULL
255 };
256
257 int
_init(void)258 _init(void)
259 {
260 int error;
261
262 if ((error = ddi_soft_state_init(&sbbcsoft_statep,
263 sizeof (struct sbbcsoft), 1)) != 0)
264 return (error);
265 if ((error = mod_install(&sbbcmodlinkage)) != 0)
266 ddi_soft_state_fini(&sbbcsoft_statep);
267
268 return (error);
269 }
270
271 int
_fini(void)272 _fini(void)
273 {
274 int error;
275
276 if ((error = mod_remove(&sbbcmodlinkage)) == 0)
277 ddi_soft_state_fini(&sbbcsoft_statep);
278
279 return (error);
280 }
281
282 int
_info(struct modinfo * modinfop)283 _info(struct modinfo *modinfop)
284 {
285 return (mod_info(&sbbcmodlinkage, modinfop));
286 }
287
288 static int
sbbc_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)289 sbbc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
290 {
291 int instance;
292 char name[32];
293 struct sbbcsoft *sbbcsoftp;
294 struct ddi_device_acc_attr attr;
295 uint32_t sbbc_id_reg;
296
297 attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
298 attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
299 attr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC;
300
301 /* initialize tracing */
302 SBBCTRACEINIT();
303
304 SBBC_DBG0(SBBC_DBG_ATTACH, dip, "Attaching\n");
305
306 instance = ddi_get_instance(dip);
307 switch (cmd) {
308 case DDI_ATTACH:
309 break;
310 case DDI_RESUME:
311 if (!(sbbcsoftp =
312 ddi_get_soft_state(sbbcsoft_statep, instance))) {
313 cmn_err(CE_WARN, "sbbc_attach:resume: unable "
314 "to acquire sbbcsoftp for instance %d",
315 instance);
316 return (DDI_FAILURE);
317 }
318 mutex_enter(&sbbcsoftp->umutex);
319 if (!sbbcsoftp->suspended) {
320 mutex_exit(&sbbcsoftp->umutex);
321 return (DDI_FAILURE);
322 }
323 sbbcsoftp->suspended = 0;
324 mutex_exit(&sbbcsoftp->umutex);
325 return (DDI_SUCCESS);
326
327 default:
328 return (DDI_FAILURE);
329 }
330
331 if (ddi_soft_state_zalloc(sbbcsoft_statep, instance) != 0) {
332 cmn_err(CE_WARN, "sbbc_attach: Unable to allocate statep "
333 "for instance %d", instance);
334 return (DDI_FAILURE);
335 }
336
337 sbbcsoftp = ddi_get_soft_state(sbbcsoft_statep, instance);
338
339 if (sbbcsoftp == NULL) {
340 cmn_err(CE_WARN, "sbbc_attach: Unable to acquire "
341 "sbbcsoftp for instance %d", instance);
342 ddi_soft_state_free(sbbcsoft_statep, instance);
343 return (DDI_FAILURE);
344 }
345
346 sbbcsoftp->instance = instance;
347 sbbcsoftp->dip = dip;
348 sbbcsoftp->oflag = FALSE;
349
350 /*
351 * Read our ranges property from OBP to map children space.
352 * And setup the internal structure for a later use when
353 * a child gets initialized.
354 */
355 if (sbbc_get_ranges(sbbcsoftp)) {
356 cmn_err(CE_WARN, "sbbc_attach: Unable to read sbbc "
357 "ranges from OBP %d", instance);
358 ddi_soft_state_free(sbbcsoft_statep, instance);
359 return (DDI_FAILURE);
360 }
361
362 if (sbbc_config4pci(sbbcsoftp)) {
363 cmn_err(CE_WARN, "sbbc_attach: Unable to configure "
364 "sbbc on PCI %d", instance);
365 kmem_free(sbbcsoftp->rangep, sbbcsoftp->range_len);
366 ddi_soft_state_free(sbbcsoft_statep, instance);
367 return (DDI_FAILURE);
368 }
369
370 mutex_init(&sbbcsoftp->umutex, NULL, MUTEX_DRIVER, (void *)NULL);
371 mutex_init(&sbbcsoftp->sbbc_intr_mutex, NULL,
372 MUTEX_DRIVER, (void *)NULL);
373
374 /* Map SBBC's Internal Registers */
375 if (ddi_regs_map_setup(dip, 1, (caddr_t *)&sbbcsoftp->pci_sbbc_map,
376 offsetof(struct pci_sbbc, sbbc_internal_regs),
377 sizeof (struct sbbc_regs_map), &attr,
378 &sbbcsoftp->pci_sbbc_map_handle) != DDI_SUCCESS) {
379 cmn_err(CE_WARN, "(%d):sbbc_attach failed to map sbbc_reg",
380 instance);
381 goto failed;
382 }
383
384 SBBC_DBG1(SBBC_DBG_ATTACH, dip, "Mapped sbbc at %lx\n",
385 sbbcsoftp->pci_sbbc_map);
386 #ifdef DEBUG
387 sbbc_dump_devid(dip, sbbcsoftp, instance);
388 #endif
389 /*
390 * Read a hardware register to determine if we are executing on
391 * a Starcat System Controller or a Starcat Domain.
392 */
393 sbbc_id_reg = ddi_get32(sbbcsoftp->pci_sbbc_map_handle,
394 &sbbcsoftp->pci_sbbc_map->device_conf);
395
396 if (sbbc_id_reg & SBBC_SC_MODE) {
397 sbbc_scmode = TRUE;
398 SBBC_DBG1(SBBC_DBG_ATTACH, dip, "SBBC(%d) nexus running "
399 "in System Controller Mode.\n", instance);
400
401 /* initialize SBBC ASIC */
402 if (!sbbc_init(sbbcsoftp)) {
403 goto failed;
404 }
405 } else {
406 sbbc_scmode = FALSE;
407 SBBC_DBG1(SBBC_DBG_ATTACH, dip, "SBBC(%d) nexus "
408 "running in Domain Mode.\n", instance);
409
410 /* initialize SBBC ASIC before we unmap registers */
411 if (!sbbc_init(sbbcsoftp)) {
412 goto failed;
413 }
414
415 /*
416 * Access to SBBC registers is no longer needed. Unmap
417 * the registers to conserve kernel virtual address space.
418 */
419 SBBC_DBG1(SBBC_DBG_ATTACH, dip, "SBBC(%d): unmap "
420 "SBBC registers\n", instance);
421 sbbc_remove_reg_maps(sbbcsoftp);
422 sbbcsoftp->pci_sbbc_map = NULL;
423 }
424
425 (void) sprintf(name, "sbbc%d", instance);
426
427 if (ddi_create_minor_node(dip, name, S_IFCHR, instance, NULL,
428 0) == DDI_FAILURE) {
429 ddi_remove_minor_node(dip, NULL);
430 goto failed;
431 }
432
433 ddi_report_dev(dip);
434
435 SBBC_DBG0(SBBC_DBG_ATTACH, dip, "Attached successfully\n");
436
437 return (DDI_SUCCESS);
438
439 failed:
440 mutex_destroy(&sbbcsoftp->sbbc_intr_mutex);
441 mutex_destroy(&sbbcsoftp->umutex);
442
443 sbbc_remove_reg_maps(sbbcsoftp);
444 kmem_free(sbbcsoftp->rangep, sbbcsoftp->range_len);
445 ddi_soft_state_free(sbbcsoft_statep, instance);
446
447 SBBC_DBG0(SBBC_DBG_ATTACH, dip, "Attach failed\n");
448
449 return (DDI_FAILURE);
450 }
451
452 static int
sbbc_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)453 sbbc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
454 {
455 int instance;
456 struct sbbcsoft *sbbcsoftp;
457
458 SBBCTRACE(sbbc_detach, 'DETA', dip);
459
460 instance = ddi_get_instance(dip);
461
462 switch (cmd) {
463 case DDI_DETACH:
464 break;
465
466 case DDI_SUSPEND:
467 if (!(sbbcsoftp =
468 ddi_get_soft_state(sbbcsoft_statep, instance))) {
469 cmn_err(CE_WARN,
470 "sbbc_detach: unable to get softstate %p",
471 (void *)sbbcsoftp);
472 return (DDI_FAILURE);
473 }
474 mutex_enter(&sbbcsoftp->umutex);
475 if (sbbcsoftp->suspended) {
476 mutex_exit(&sbbcsoftp->umutex);
477 return (DDI_FAILURE);
478 }
479 sbbcsoftp->suspended = 1;
480 mutex_exit(&sbbcsoftp->umutex);
481 return (DDI_SUCCESS);
482
483 default:
484 return (DDI_FAILURE);
485 }
486
487 if (!(sbbcsoftp = ddi_get_soft_state(sbbcsoft_statep, instance))) {
488 cmn_err(CE_WARN, "sbbc_detach: unable to get softstate %p",
489 (void *)sbbcsoftp);
490 return (DDI_FAILURE);
491 }
492
493 ddi_remove_minor_node(dip, NULL);
494
495 mutex_destroy(&sbbcsoftp->sbbc_intr_mutex);
496 mutex_destroy(&sbbcsoftp->umutex);
497
498 sbbc_remove_reg_maps(sbbcsoftp);
499 kmem_free(sbbcsoftp->rangep, sbbcsoftp->range_len);
500
501 ddi_soft_state_free(sbbcsoft_statep, instance);
502
503 return (DDI_SUCCESS);
504
505 }
506
507
508 /*
509 * Translate child's address into parents.
510 */
511 static int
sbbc_busmap(dev_info_t * dip,dev_info_t * rdip,ddi_map_req_t * mp,off_t off,off_t len,caddr_t * addrp)512 sbbc_busmap(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
513 off_t off, off_t len, caddr_t *addrp)
514 {
515 struct sbbcsoft *sbbcsoftp;
516 sbbc_child_regspec_t *child_rp, *child_regs;
517 pci_regspec_t pci_reg;
518 ddi_map_req_t p_map_request;
519 int rnumber, i, n;
520 int rval = DDI_SUCCESS;
521 int instance;
522
523 SBBC_DBG4(SBBC_DBG_BUSMAP, dip,
524 "mapping child %s, type %llx, off %llx, len %llx\n",
525 ddi_driver_name(rdip), mp->map_type, off, len);
526
527 SBBCTRACE(sbbc_busmap, 'BMAP', mp);
528
529 /*
530 * Handle the mapping according to its type.
531 */
532 instance = ddi_get_instance(dip);
533 if (!(sbbcsoftp = ddi_get_soft_state(sbbcsoft_statep, instance)))
534 return (DDI_FAILURE);
535
536 switch (mp->map_type) {
537 case DDI_MT_REGSPEC:
538
539 /*
540 * We assume the register specification is in sbbc format.
541 * We must convert it into a PCI format regspec and pass
542 * the request to our parent.
543 */
544 child_rp = (sbbc_child_regspec_t *)mp->map_obj.rp;
545 break;
546
547 case DDI_MT_RNUMBER:
548
549 /*
550 * map_type 0
551 * Get the "reg" property from the device node and convert
552 * it to our parent's format.
553 */
554 rnumber = mp->map_obj.rnumber;
555
556 /* get the requester's reg property */
557 if (ddi_getlongprop(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS,
558 "reg", (caddr_t)&child_regs, &i) != DDI_SUCCESS) {
559 cmn_err(CE_WARN,
560 "SBBC: couldn't get %s ranges property %d",
561 ddi_get_name(sbbcsoftp->dip), instance);
562 return (DDI_ME_RNUMBER_RANGE);
563 }
564 n = i / sizeof (sbbc_child_regspec_t);
565
566 if (rnumber < 0 || rnumber >= n) {
567 kmem_free(child_regs, i);
568 return (DDI_ME_RNUMBER_RANGE);
569 }
570 child_rp = &child_regs[rnumber];
571 break;
572
573 default:
574 return (DDI_ME_INVAL);
575
576 }
577
578 /* Adjust our reg property with offset and length */
579 child_rp->addr_low += off;
580
581 if (len)
582 child_rp->size = len;
583
584 /*
585 * Combine this reg prop. into our parents PCI address using the ranges
586 * property.
587 */
588 rval = sbbc_apply_range(sbbcsoftp, rdip, child_rp, &pci_reg);
589
590 if (mp->map_type == DDI_MT_RNUMBER)
591 kmem_free(child_regs, i);
592
593 if (rval != DDI_SUCCESS)
594 return (rval);
595
596 p_map_request = *mp;
597 p_map_request.map_type = DDI_MT_REGSPEC;
598 p_map_request.map_obj.rp = (struct regspec *)&pci_reg;
599
600 /* Send it to PCI nexus to map into the PCI space */
601 rval = ddi_map(dip, &p_map_request, 0, 0, addrp);
602
603 return (rval);
604
605 }
606
607
608 /* new intr_ops structure */
609 static int
sbbc_intr_ops(dev_info_t * dip,dev_info_t * rdip,ddi_intr_op_t intr_op,ddi_intr_handle_impl_t * hdlp,void * result)610 sbbc_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op,
611 ddi_intr_handle_impl_t *hdlp, void *result)
612 {
613 int ret = DDI_SUCCESS;
614
615 switch (intr_op) {
616 case DDI_INTROP_GETCAP:
617 *(int *)result = DDI_INTR_FLAG_LEVEL;
618 break;
619 case DDI_INTROP_ALLOC:
620 *(int *)result = hdlp->ih_scratch1;
621 break;
622 case DDI_INTROP_FREE:
623 break;
624 case DDI_INTROP_GETPRI:
625 if (hdlp->ih_pri == 0) {
626 hdlp->ih_pri = 0x1;
627
628 cmn_err(CE_WARN, "%s%d assigning default interrupt "
629 "level %d for device %s%d", ddi_driver_name(dip),
630 ddi_get_instance(dip), hdlp->ih_pri,
631 ddi_driver_name(rdip), ddi_get_instance(rdip));
632 }
633
634 *(int *)result = hdlp->ih_pri;
635
636 break;
637 case DDI_INTROP_ADDISR:
638 ret = sbbc_add_intr_impl(dip, rdip, intr_op, hdlp, result);
639 break;
640 case DDI_INTROP_REMISR:
641 ret = sbbc_remove_intr_impl(dip, rdip, intr_op, hdlp, result);
642 break;
643 case DDI_INTROP_ENABLE:
644 ret = sbbc_update_intr_state(dip, rdip, intr_op, hdlp, &result);
645 break;
646 case DDI_INTROP_DISABLE:
647 ret = sbbc_update_intr_state(dip, rdip, intr_op, hdlp, &result);
648 break;
649 case DDI_INTROP_NINTRS:
650 case DDI_INTROP_NAVAIL:
651 *(int *)result = i_ddi_get_intx_nintrs(rdip);
652 break;
653 case DDI_INTROP_SUPPORTED_TYPES:
654 /* PCI nexus driver supports only fixed interrupts */
655 *(int *)result = i_ddi_get_intx_nintrs(rdip) ?
656 DDI_INTR_TYPE_FIXED : 0;
657 break;
658 default:
659 ret = DDI_ENOTSUP;
660 break;
661 }
662
663 return (ret);
664 }
665
666
667 static int
sbbc_add_intr_impl(dev_info_t * dip,dev_info_t * rdip,ddi_intr_op_t intr_op,ddi_intr_handle_impl_t * hdlp,void * result)668 sbbc_add_intr_impl(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op,
669 ddi_intr_handle_impl_t *hdlp, void *result)
670 {
671 sbbcsoft_t *sbbcsoftp;
672 sbbc_child_intr_t *childintr;
673 int instance, i, rval = DDI_SUCCESS;
674
675 SBBC_DBG2(SBBC_DBG_INTR, dip,
676 "add: rdip 0x%llx hdlp 0x%llx\n", rdip, hdlp);
677
678 /* insert the sbbc isr wrapper instead */
679 instance = ddi_get_instance(dip);
680 if (!(sbbcsoftp = ddi_get_soft_state(sbbcsoft_statep, instance)))
681 return (DDI_FAILURE);
682
683 childintr = kmem_zalloc(sizeof (struct sbbc_child_intr), KM_SLEEP);
684
685 childintr->name = ddi_get_name(rdip);
686 childintr->inum = hdlp->ih_inum;
687 childintr->intr_handler = hdlp->ih_cb_func;
688 childintr->arg1 = hdlp->ih_cb_arg1;
689 childintr->arg2 = hdlp->ih_cb_arg2;
690 childintr->status = SBBC_INTR_STATE_DISABLE;
691
692 for (i = 0; i < MAX_SBBC_DEVICES; i++) {
693 if (sbbcsoftp->child_intr[i] == NULL) {
694 sbbcsoftp->child_intr[i] = childintr;
695 break;
696 }
697 }
698
699 DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp,
700 (ddi_intr_handler_t *)sbbc_intr_wrapper,
701 (caddr_t)sbbcsoftp, NULL);
702
703 if ((rval = i_ddi_intr_ops(dip, rdip, intr_op,
704 hdlp, result)) != DDI_SUCCESS) {
705 cmn_err(CE_WARN, "sbbc%d: failed to add intr for %s",
706 instance, ddi_get_name(rdip));
707 kmem_free(childintr, sizeof (struct sbbc_child_intr));
708 if (i < MAX_SBBC_DEVICES)
709 sbbcsoftp->child_intr[i] = NULL;
710 }
711
712 /*
713 * Restore original interrupt handler
714 * and arguments in interrupt handle.
715 */
716 DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp, childintr->intr_handler,
717 childintr->arg1, childintr->arg2);
718
719 return (rval);
720 }
721
722 static int
sbbc_remove_intr_impl(dev_info_t * dip,dev_info_t * rdip,ddi_intr_op_t intr_op,ddi_intr_handle_impl_t * hdlp,void * result)723 sbbc_remove_intr_impl(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op,
724 ddi_intr_handle_impl_t *hdlp, void *result)
725 {
726 sbbcsoft_t *sbbcsoftp;
727 sbbc_child_intr_t *childintr;
728 int instance, i, rval = DDI_SUCCESS;
729
730 SBBC_DBG2(SBBC_DBG_INTR, dip,
731 "remove: rdip 0x%llx hdlp 0x%llx\n", rdip, hdlp);
732
733 instance = ddi_get_instance(dip);
734 if (!(sbbcsoftp = ddi_get_soft_state(sbbcsoft_statep, instance)))
735 return (DDI_FAILURE);
736
737 /* remove the sbbc isr wrapper instead */
738 for (i = 0; i < MAX_SBBC_DEVICES; i++) {
739 if (sbbcsoftp->child_intr[i]) {
740 childintr = sbbcsoftp->child_intr[i];
741 if (childintr->status == SBBC_INTR_STATE_DISABLE &&
742 childintr->name == ddi_get_name(rdip)) {
743 /* put back child's inum */
744 hdlp->ih_inum = childintr->inum;
745 break;
746 }
747 }
748 }
749
750 if (i >= MAX_SBBC_DEVICES) {
751 cmn_err(CE_WARN, "sbbc%d:obound failed to remove intr for %s",
752 instance, ddi_get_name(rdip));
753 return (DDI_FAILURE);
754 }
755
756 if ((rval = i_ddi_intr_ops(dip, rdip, intr_op,
757 hdlp, result)) != DDI_SUCCESS) {
758 cmn_err(CE_WARN, "sbbc%d: failed to remove intr for %s",
759 instance, ddi_get_name(rdip));
760 return (rval);
761 }
762
763 kmem_free(childintr, sizeof (struct sbbc_child_intr));
764 sbbcsoftp->child_intr[i] = NULL;
765
766 return (rval);
767 }
768
769
770 static int
sbbc_update_intr_state(dev_info_t * dip,dev_info_t * rdip,ddi_intr_op_t intr_op,ddi_intr_handle_impl_t * hdlp,void * result)771 sbbc_update_intr_state(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op,
772 ddi_intr_handle_impl_t *hdlp, void *result)
773 {
774 sbbcsoft_t *sbbcsoftp;
775 sbbc_child_intr_t *childintr;
776 int instance, i;
777 int ret = DDI_SUCCESS;
778
779 SBBC_DBG2(SBBC_DBG_INTR, dip, "sbbc_update_intr_state: "
780 "rdip 0x%llx hdlp 0x%llx state 0x%x\n", rdip, hdlp);
781
782 instance = ddi_get_instance(dip);
783 if (!(sbbcsoftp = ddi_get_soft_state(sbbcsoft_statep, instance)))
784 return (DDI_FAILURE);
785
786 for (i = 0; i < MAX_SBBC_DEVICES; i++) {
787 if (sbbcsoftp->child_intr[i]) {
788 childintr = sbbcsoftp->child_intr[i];
789 if (childintr->name == ddi_get_name(rdip))
790 break;
791 }
792 }
793
794 if (i >= MAX_SBBC_DEVICES) {
795 cmn_err(CE_WARN, "sbbc%d: failed to update intr state for %s",
796 instance, ddi_get_name(rdip));
797 return (DDI_FAILURE);
798 }
799
800 if ((ret = i_ddi_intr_ops(dip, rdip, intr_op,
801 hdlp, result)) != DDI_SUCCESS) {
802 cmn_err(CE_WARN, "sbbc%d: failed to update intr state for %s",
803 instance, ddi_get_name(rdip));
804 return (ret);
805 }
806
807 /* Update the interrupt state */
808 childintr->status = (intr_op == DDI_INTROP_ENABLE) ?
809 SBBC_INTR_STATE_ENABLE : SBBC_INTR_STATE_DISABLE;
810
811 return (ret);
812 }
813
814
815 /*
816 * This entry point is called before a child's probe or attach is called.
817 * The arg pointer points to child's dev_info_t structure.
818 */
819 static int
sbbc_ctlops(dev_info_t * dip,dev_info_t * rdip,ddi_ctl_enum_t op,void * arg,void * result)820 sbbc_ctlops(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t op,
821 void *arg, void *result)
822 {
823 sbbc_child_regspec_t *child_rp;
824 int i, n;
825
826 SBBC_DBG3(SBBC_DBG_CTLOPS, dip,
827 "Initializing %s, arg %x, op %x\n",
828 ddi_driver_name(rdip), arg, op);
829
830 SBBCTRACE(sbbc_ctlops, 'CTLO', arg);
831
832 switch (op) {
833 case DDI_CTLOPS_INITCHILD: {
834 return (sbbc_initchild(dip, rdip, (dev_info_t *)arg));
835 }
836
837 case DDI_CTLOPS_UNINITCHILD: {
838 return (sbbc_uninitchild(rdip, (dev_info_t *)arg));
839 }
840
841 case DDI_CTLOPS_REPORTDEV:
842
843 cmn_err(CE_CONT, "?%s%d at %s%d: offset %s\n",
844 ddi_driver_name(rdip), ddi_get_instance(rdip),
845 ddi_driver_name(dip), ddi_get_instance(dip),
846 ddi_get_name_addr(rdip));
847 return (DDI_SUCCESS);
848
849 case DDI_CTLOPS_REGSIZE:
850
851 if (getprop(rdip, "reg", &child_rp, &i) != DDI_SUCCESS) {
852 return (DDI_FAILURE);
853 }
854 n = i / sizeof (sbbc_child_regspec_t);
855 if (*(int *)arg < 0 || *(int *)arg >= n) {
856 kmem_free(child_rp, i);
857 return (DDI_FAILURE);
858 }
859 *((off_t *)result) = child_rp[*(int *)arg].size;
860 kmem_free(child_rp, i);
861 return (DDI_SUCCESS);
862
863 case DDI_CTLOPS_NREGS:
864
865 if (getprop(rdip, "reg", &child_rp, &i) != DDI_SUCCESS) {
866 return (DDI_FAILURE);
867 }
868 *((uint_t *)result) = i / sizeof (sbbc_child_regspec_t);
869 kmem_free(child_rp, i);
870 return (DDI_SUCCESS);
871 }
872
873 /*
874 * Now pass the request up to our parent.
875 */
876 SBBC_DBG0(SBBC_DBG_CTLOPS, dip, "Calling ddi_ctlops\n");
877
878 return (ddi_ctlops(dip, rdip, op, arg, result));
879 }
880
881
882 /*
883 * The following routine uses ranges property, that was read earlier, and
884 * takes child's reg property, and computes the complete address and size
885 * for the PCI parent to map.
886 */
887 static int
sbbc_apply_range(struct sbbcsoft * sbbc_p,dev_info_t * rdip,sbbc_child_regspec_t * child_rp,pci_regspec_t * rp)888 sbbc_apply_range(struct sbbcsoft *sbbc_p, dev_info_t *rdip,
889 sbbc_child_regspec_t *child_rp, pci_regspec_t *rp)
890 {
891 int b;
892 int rval = DDI_SUCCESS;
893 struct sbbc_pci_rangespec *rangep = sbbc_p->rangep;
894 int nrange = sbbc_p->range_cnt;
895
896 SBBC_DBG4(SBBC_DBG_MAPRANGES, rdip,
897 "Applying ranges for %s, rangep %llx, child_rp %llx, range %x\n",
898 ddi_driver_name(rdip), sbbc_p->rangep, child_rp, nrange);
899
900 SBBCTRACE(sbbc_apply_range, 'APPL', sbbc_p);
901
902 for (b = 0; b < nrange; ++b, ++rangep) {
903
904 /* Make sure the correct range is being mapped */
905 if (child_rp->addr_hi == rangep->sbbc_phys_hi)
906 /* See if we fit in this range */
907 if ((child_rp->addr_low >=
908 rangep->sbbc_phys_low) &&
909 ((child_rp->addr_low + child_rp->size - 1)
910 <= (rangep->sbbc_phys_low +
911 rangep->rng_size - 1))) {
912 uint_t addr_offset = child_rp->addr_low -
913 rangep->sbbc_phys_low;
914 /*
915 * Use the range entry to translate
916 * the SBBC physical address into the
917 * parents PCI space.
918 */
919 rp->pci_phys_hi =
920 rangep->pci_phys_hi;
921 rp->pci_phys_mid = rangep->pci_phys_mid;
922 rp->pci_phys_low =
923 rangep->pci_phys_low + addr_offset;
924 rp->pci_size_hi = 0;
925 rp->pci_size_low =
926 min(child_rp->size, (rangep->rng_size -
927 addr_offset));
928
929 break;
930 }
931 }
932
933 if (b == nrange) {
934 cmn_err(CE_WARN, "out_of_range %s", ddi_get_name(rdip));
935 return (DDI_ME_REGSPEC_RANGE);
936 }
937
938 return (rval);
939 }
940
941
942 /*
943 * The following routine reads sbbc's ranges property from OBP and sets up
944 * its soft structure with it.
945 */
946 static int
sbbc_get_ranges(struct sbbcsoft * sbbcsoftp)947 sbbc_get_ranges(struct sbbcsoft *sbbcsoftp)
948 {
949 struct sbbc_pci_rangespec *rangep;
950 int range_len, nrange;
951
952 if (ddi_getlongprop(DDI_DEV_T_ANY, sbbcsoftp->dip, DDI_PROP_DONTPASS,
953 "ranges", (caddr_t)&rangep, &range_len) != DDI_SUCCESS) {
954 cmn_err(CE_WARN, "SBBC: couldn't get %s ranges property %d",
955 ddi_get_name(sbbcsoftp->dip), sbbcsoftp->instance);
956 return (DDI_ME_REGSPEC_RANGE);
957 }
958
959 nrange = range_len / sizeof (struct sbbc_pci_rangespec);
960
961 if (!nrange) {
962 kmem_free(rangep, range_len);
963 return (DDI_FAILURE);
964 }
965
966 /* setup the soft structure with ranges info. */
967 sbbcsoftp->rangep = rangep;
968 sbbcsoftp->range_cnt = nrange;
969 sbbcsoftp->range_len = range_len;
970
971 return (DDI_SUCCESS);
972 }
973
974
975 /*
976 * Configure the SBBC for PCI
977 */
978 static int
sbbc_config4pci(struct sbbcsoft * sbbcsoftp)979 sbbc_config4pci(struct sbbcsoft *sbbcsoftp)
980 {
981 ddi_acc_handle_t conf_handle;
982 uint16_t comm, vendid, devid, stat;
983 uint8_t revid;
984
985 #ifdef DEBUG
986 if (sbbc_dbg_flags & SBBC_DBG_PCICONF) {
987 cmn_err(CE_CONT,
988 "sbbc_config4pci: sbbcsoftp %p\n", (void *)sbbcsoftp);
989 }
990 #endif
991 if (pci_config_setup(sbbcsoftp->dip, &conf_handle) != DDI_SUCCESS)
992 return (1);
993
994 vendid = pci_config_get16(conf_handle, PCI_CONF_VENID);
995 devid = pci_config_get16(conf_handle, PCI_CONF_DEVID);
996 comm = pci_config_get16(conf_handle, PCI_CONF_COMM);
997 stat = pci_config_get16(conf_handle, PCI_CONF_STAT);
998 revid = pci_config_get8(conf_handle, PCI_CONF_REVID);
999
1000 #ifdef DEBUG
1001 if (sbbc_dbg_flags & SBBC_DBG_PCICONF) {
1002 cmn_err(CE_CONT,
1003 "SBBC vendid %x, devid %x, comm %x, stat %x, revid %x\n",
1004 vendid, devid, comm, stat, revid);
1005 }
1006 #endif
1007 comm = (PCI_COMM_ME | PCI_COMM_MAE | PCI_COMM_SERR_ENABLE |
1008 PCI_COMM_PARITY_DETECT);
1009
1010 pci_config_put16(conf_handle, PCI_CONF_COMM, comm);
1011
1012 comm = pci_config_get16(conf_handle, PCI_CONF_COMM);
1013
1014 #ifdef DEBUG
1015 if (sbbc_dbg_flags & SBBC_DBG_PCICONF) {
1016 cmn_err(CE_CONT, "comm %x\n", comm);
1017 }
1018 #endif
1019 pci_config_teardown(&conf_handle);
1020
1021 return (0);
1022 }
1023
1024
1025 /* ARGSUSED0 */
1026 int
sbbc_getinfo(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)1027 sbbc_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
1028 {
1029 dev_t dev = (dev_t)arg;
1030 struct sbbcsoft *sbbcsoftp;
1031 int instance, ret;
1032
1033 instance = getminor(dev);
1034
1035 SBBCTRACE(sbbc_getinfo, 'GINF', instance);
1036
1037 switch (infocmd) {
1038 case DDI_INFO_DEVT2DEVINFO:
1039 sbbcsoftp = (struct sbbcsoft *)
1040 ddi_get_soft_state(sbbcsoft_statep, instance);
1041 if (sbbcsoftp == NULL) {
1042 *result = (void *) NULL;
1043 ret = DDI_FAILURE;
1044 } else {
1045 *result = sbbcsoftp->dip;
1046 ret = DDI_SUCCESS;
1047 }
1048 break;
1049 case DDI_INFO_DEVT2INSTANCE:
1050 *result = (void *)(uintptr_t)instance;
1051 ret = DDI_SUCCESS;
1052 break;
1053 default:
1054 ret = DDI_FAILURE;
1055 break;
1056 }
1057
1058 return (ret);
1059 }
1060
1061 /*ARGSUSED1*/
1062 static int
sbbc_open(dev_t * dev,int flag,int otype,cred_t * credp)1063 sbbc_open(dev_t *dev, int flag, int otype, cred_t *credp)
1064 {
1065 struct sbbcsoft *sbbcsoftp;
1066 int instance;
1067
1068 /* check privilege of caller process */
1069 if (drv_priv(credp)) {
1070 return (EPERM);
1071 }
1072
1073 instance = getminor(*dev);
1074 if (instance < 0)
1075 return (ENXIO);
1076 sbbcsoftp = (struct sbbcsoft *)ddi_get_soft_state(sbbcsoft_statep,
1077 instance);
1078 SBBCTRACE(sbbc_open, 'OPEN', sbbcsoftp);
1079
1080 if (sbbcsoftp == NULL)
1081 return (ENXIO);
1082
1083 mutex_enter(&sbbcsoftp->umutex);
1084
1085 /* check for exclusive access */
1086 if ((sbbcsoftp->oflag == TRUE)) {
1087 mutex_exit(&sbbcsoftp->umutex);
1088 return (EBUSY);
1089 }
1090 sbbcsoftp->oflag = TRUE;
1091
1092 mutex_exit(&sbbcsoftp->umutex);
1093
1094 return (0);
1095 }
1096
1097 /*ARGSUSED1*/
1098 static int
sbbc_close(dev_t dev,int flag,int otype,cred_t * credp)1099 sbbc_close(dev_t dev, int flag, int otype, cred_t *credp)
1100 {
1101 struct sbbcsoft *sbbcsoftp;
1102 int instance;
1103
1104 instance = getminor(dev);
1105 if (instance < 0)
1106 return (ENXIO);
1107 sbbcsoftp = (struct sbbcsoft *)ddi_get_soft_state(sbbcsoft_statep,
1108 instance);
1109 /* wait till all output activity has ceased */
1110
1111 mutex_enter(&sbbcsoftp->umutex);
1112
1113 SBBCTRACE(sbbc_close, 'CLOS', sbbcsoftp);
1114
1115 sbbcsoftp->oflag = FALSE;
1116
1117 mutex_exit(&sbbcsoftp->umutex);
1118
1119 return (0);
1120 }
1121
1122 /*ARGSUSED2*/
1123 static int
sbbc_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)1124 sbbc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
1125 int *rvalp)
1126 {
1127 struct sbbcsoft *sbbcsoftp;
1128
1129 SBBCTRACE(sbbc_ioctl, 'IOCT', arg);
1130
1131 sbbcsoftp = ddi_get_soft_state(sbbcsoft_statep, getminor(dev));
1132
1133 if (sbbcsoftp == NULL) {
1134 return (ENXIO);
1135 }
1136
1137 switch (cmd) {
1138 case SBBC_SBBCREG_WR:
1139 {
1140 struct ssc_sbbc_regio sbbcregs;
1141 uint64_t offset;
1142
1143 if (sbbc_scmode == FALSE) {
1144 /* then we're executing on Domain; Writes not allowed */
1145 return (EINVAL);
1146 }
1147
1148 if (arg == (intptr_t)NULL) {
1149 return (ENXIO);
1150 }
1151
1152 if (ddi_copyin((caddr_t)arg, (caddr_t)&sbbcregs,
1153 sizeof (struct ssc_sbbc_regio), mode)) {
1154 cmn_err(CE_WARN, "sbbc_ioctl: copyin failed arg %p",
1155 (void *)arg);
1156 return (EFAULT);
1157 }
1158
1159 /*
1160 * Bug #4287186: SBBC driver on cp1500 doesn't check length for
1161 * reads or writes
1162 * Note that I've also added a check to make sure the offset is
1163 * valid, since misaligned (i.e. not on 16-byte boundary)
1164 * accesses or accesses to "Reserved" register offsets are
1165 * treated as unmapped by the SBBC.
1166 */
1167 if ((sbbcregs.len != 4) ||
1168 !sbbc_offset_valid(sbbcregs.offset)) {
1169 return (EINVAL);
1170 }
1171
1172 offset = (uint64_t)sbbcsoftp->pci_sbbc_map;
1173 offset += sbbcregs.offset;
1174 ddi_put32(sbbcsoftp->pci_sbbc_map_handle, (uint32_t *)offset,
1175 sbbcregs.value);
1176 }
1177 break;
1178 case SBBC_SBBCREG_RD:
1179 {
1180 struct ssc_sbbc_regio sbbcregs;
1181 uint64_t offset;
1182
1183 if (sbbc_scmode == FALSE) {
1184 /* then we're executing on Domain; Reads not allowed */
1185 return (EINVAL);
1186 }
1187
1188 if (arg == (intptr_t)NULL) {
1189 return (ENXIO);
1190 }
1191
1192 if (ddi_copyin((caddr_t)arg, (caddr_t)&sbbcregs,
1193 sizeof (struct ssc_sbbc_regio), mode)) {
1194 cmn_err(CE_WARN, "sbbc_ioctl: copyin failed arg %p",
1195 (void *)arg);
1196 return (EFAULT);
1197 }
1198
1199 /*
1200 * Bug #4287186: SBBC driver on cp1500 doesn't check length for
1201 * reads or writes
1202 * Note that I've also added a check to make sure the offset is
1203 * valid, since misaligned (i.e. not on 16-byte boundary)
1204 * accesses or accesses to "Reserved" register offsets are
1205 * treated as unmapped by the SBBC.
1206 */
1207 if ((sbbcregs.len != 4) ||
1208 !sbbc_offset_valid(sbbcregs.offset)) {
1209 return (EINVAL);
1210 }
1211
1212 offset = (uint64_t)sbbcsoftp->pci_sbbc_map;
1213 offset += sbbcregs.offset;
1214
1215 sbbcregs.value = ddi_get32(sbbcsoftp->pci_sbbc_map_handle,
1216 (uint32_t *)offset);
1217
1218 if (ddi_copyout((caddr_t)&sbbcregs.value,
1219 &((struct ssc_sbbc_regio *)arg)->value,
1220 sbbcregs.len, mode)) {
1221 cmn_err(CE_WARN, "sbbc_ioctl:copyout failed arg %p",
1222 (void *)arg);
1223 return (EFAULT);
1224 }
1225 }
1226 break;
1227 default:
1228 cmn_err(CE_WARN, "sbbc_ioctl:Illegal command 0x%08x", cmd);
1229 return (ENOTTY);
1230 }
1231
1232 return (DDI_SUCCESS);
1233 }
1234
1235 static void
sbbc_remove_reg_maps(struct sbbcsoft * sbbcsoftp)1236 sbbc_remove_reg_maps(struct sbbcsoft *sbbcsoftp)
1237 {
1238 SBBCTRACE(sbbc_remove_reg_maps, 'RMAP', sbbcsoftp);
1239 if (sbbcsoftp->pci_sbbc_map_handle)
1240 ddi_regs_map_free(&sbbcsoftp->pci_sbbc_map_handle);
1241 }
1242
1243
1244 static int
sbbc_init(struct sbbcsoft * sbbcsoftp)1245 sbbc_init(struct sbbcsoft *sbbcsoftp)
1246 {
1247 /* Mask all the interrupts until we are ready. */
1248 ddi_put32(sbbcsoftp->pci_sbbc_map_handle,
1249 &sbbcsoftp->pci_sbbc_map->sys_intr_enable,
1250 0x00000000);
1251
1252 return (1);
1253 }
1254
1255 /*
1256 * The following routine is a generic routine to initialize any child of
1257 * sbbc nexus driver information into parent private data structure.
1258 */
1259 /* ARGSUSED0 */
1260 static int
sbbc_initchild(dev_info_t * dip,dev_info_t * rdip,dev_info_t * child)1261 sbbc_initchild(dev_info_t *dip, dev_info_t *rdip, dev_info_t *child)
1262 {
1263 sbbc_child_regspec_t *child_rp;
1264 int reglen, slot;
1265 char name[10];
1266
1267 SBBC_DBG1(SBBC_DBG_INITCHILD, dip, "Initializing %s\n",
1268 ddi_driver_name(rdip));
1269
1270 /*
1271 * Initialize a child
1272 * Set the address portion of the node name based on the
1273 * address/offset.
1274 */
1275 if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
1276 "reg", (caddr_t)&child_rp, ®len) != DDI_SUCCESS) {
1277 if (strcmp(ddi_node_name(child), "hotplug-controller") == 0) {
1278 slot = 1;
1279 (void) sprintf(name, "%x", slot);
1280 ddi_set_name_addr(child, name);
1281 return (DDI_SUCCESS);
1282 }
1283 return (DDI_FAILURE);
1284 }
1285
1286 SBBC_DBG3(SBBC_DBG_INITCHILD, dip, "hi 0x%x, low 0x%x, size 0x%x\n",
1287 child_rp->addr_hi, child_rp->addr_low, child_rp->size);
1288
1289 (void) sprintf(name, "%x,%x", child_rp->addr_hi, child_rp->addr_low);
1290
1291 /*
1292 * set child's addresses from the reg property into parent private
1293 * data structure.
1294 */
1295 ddi_set_name_addr(child, name);
1296 kmem_free(child_rp, reglen);
1297
1298 ddi_set_parent_data(child, NULL);
1299
1300 return (DDI_SUCCESS);
1301 }
1302
1303
1304 /* ARGSUSED0 */
1305 static int
sbbc_uninitchild(dev_info_t * rdip,dev_info_t * child)1306 sbbc_uninitchild(dev_info_t *rdip, dev_info_t *child)
1307 {
1308
1309 SBBC_DBG1(SBBC_DBG_UNINITCHILD, rdip, "Uninitializing %s\n",
1310 ddi_driver_name(rdip));
1311
1312 ddi_set_name_addr(child, NULL);
1313 ddi_remove_minor_node(child, NULL);
1314 impl_rem_dev_props(child);
1315
1316 return (DDI_SUCCESS);
1317
1318 }
1319
1320
1321 /*
1322 * The following routine is an interrupt service routine that is used
1323 * as a wrapper to all the children requiring interrupt services.
1324 */
1325 static uint_t
sbbc_intr_wrapper(caddr_t arg)1326 sbbc_intr_wrapper(caddr_t arg)
1327 {
1328
1329 struct sbbcsoft *sbbcsoftp = (struct sbbcsoft *)arg;
1330 int i, rval;
1331
1332 SBBC_DBG1(SBBC_DBG_INTR, sbbcsoftp->dip, "Isr arg 0x%llx\n", arg);
1333
1334 mutex_enter(&sbbcsoftp->sbbc_intr_mutex);
1335
1336 for (i = 0; i < MAX_SBBC_DEVICES; i++) {
1337 /*
1338 * Check the interrupt status reg. to determine the cause.
1339 */
1340 /*
1341 * Check the error status reg. to determine the cause.
1342 */
1343 if (sbbcsoftp->child_intr[i] &&
1344 sbbcsoftp->child_intr[i]->status ==
1345 SBBC_INTR_STATE_ENABLE) {
1346 /*
1347 * Dispatch the children interrupt service routines and
1348 * look for someone to claim.
1349 */
1350 rval = sbbcsoftp->child_intr[i]->intr_handler(
1351 sbbcsoftp->child_intr[i]->arg1,
1352 sbbcsoftp->child_intr[i]->arg2);
1353
1354 if (rval == DDI_INTR_CLAIMED) {
1355 mutex_exit(&sbbcsoftp->sbbc_intr_mutex);
1356 return (rval);
1357 }
1358 }
1359 }
1360
1361 mutex_exit(&sbbcsoftp->sbbc_intr_mutex);
1362
1363 /* for now do not claim since we know its not enabled */
1364 return (DDI_INTR_UNCLAIMED);
1365 }
1366
1367
1368 /*
1369 * This function checks an SBBC register offset to make sure that it is properly
1370 * aligned (i.e. on a 16-byte boundary) and that it corresponds to an accessible
1371 * register. Since the SBBC treates accesses to unaligned or reserved addresses
1372 * as unmapped, failing to check for these would leave a loophole that could be
1373 * used to crash the system.
1374 */
1375 static int
sbbc_offset_valid(uint32_t offset)1376 sbbc_offset_valid(uint32_t offset)
1377 {
1378 /*
1379 * Check for proper alignment first.
1380 */
1381 if ((offset % 16) != 0) {
1382 return (0);
1383 }
1384
1385 /*
1386 * Now start checking for the various reserved ranges.
1387 * While sticking a bunch of constants in the code (rather than
1388 * #define'd values) is usually best avoided, it would probably
1389 * do more harm than good here. These values were taken from the
1390 * Serengeti Architecture Programmer's Reference Manual dated
1391 * August 10, 1999, pages 2-99 through 2-103. While there are
1392 * various "clever" ways this check could be performed that would
1393 * be slightly more efficient, arranging the code in this fashion
1394 * should maximize maintainability.
1395 */
1396 if (((offset >= 0x001a0) && (offset <= 0x001ff)) ||
1397 ((offset >= 0x002a0) && (offset <= 0x002ff)) ||
1398 ((offset >= 0x00350) && (offset <= 0x003ff)) ||
1399 ((offset >= 0x00500) && (offset <= 0x00fff)) ||
1400 ((offset >= 0x01160) && (offset <= 0x011ff)) ||
1401 ((offset >= 0x01210) && (offset <= 0x017ff)) ||
1402 ((offset >= 0x01810) && (offset <= 0x01fff)) ||
1403 ((offset >= 0x02030) && (offset <= 0x022ff)) ||
1404 ((offset >= 0x02340) && (offset <= 0x03fff)) ||
1405 ((offset >= 0x04030) && (offset <= 0x05fff)) ||
1406 ((offset >= 0x060a0) && (offset <= 0x060ff)) ||
1407 (offset == 0x06120) ||
1408 ((offset >= 0x06190) && (offset <= 0x061ff)) ||
1409 ((offset >= 0x06230) && (offset <= 0x062f0)) ||
1410 (offset > 0x06320)) {
1411 return (0);
1412 }
1413
1414 return (1);
1415 }
1416
1417 #ifdef DEBUG
1418 void
sbbc_dbg(uint32_t flag,dev_info_t * dip,char * fmt,uintptr_t a1,uintptr_t a2,uintptr_t a3,uintptr_t a4,uintptr_t a5)1419 sbbc_dbg(uint32_t flag, dev_info_t *dip, char *fmt,
1420 uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5)
1421 {
1422 char *s = NULL;
1423
1424 if (sbbc_dbg_flags && ((sbbc_dbg_flags & flag) == flag)) {
1425 switch (flag) {
1426 case SBBC_DBG_ATTACH:
1427 s = "attach";
1428 break;
1429 case SBBC_DBG_DETACH:
1430 s = "detach";
1431 break;
1432 case SBBC_DBG_CTLOPS:
1433 s = "ctlops";
1434 break;
1435 case SBBC_DBG_INITCHILD:
1436 s = "initchild";
1437 break;
1438 case SBBC_DBG_UNINITCHILD:
1439 s = "uninitchild";
1440 break;
1441 case SBBC_DBG_BUSMAP:
1442 s = "busmap";
1443 break;
1444 case SBBC_DBG_INTR:
1445 s = "intr";
1446 break;
1447 case SBBC_DBG_INTROPS:
1448 s = "intr_ops";
1449 break;
1450 case SBBC_DBG_PCICONF:
1451 s = "pciconfig";
1452 break;
1453 case SBBC_DBG_MAPRANGES:
1454 s = "mapranges";
1455 break;
1456 case SBBC_DBG_PROPERTIES:
1457 s = "properties";
1458 break;
1459 case SBBC_DBG_OPEN:
1460 s = "open";
1461 break;
1462 case SBBC_DBG_CLOSE:
1463 s = "close";
1464 break;
1465 case SBBC_DBG_IOCTL:
1466 s = "ioctl";
1467 break;
1468 default:
1469 s = "Unknown debug flag";
1470 break;
1471 }
1472
1473 cmn_err(CE_CONT, "%s_%s(%d): ", ddi_driver_name(dip), s,
1474 ddi_get_instance(dip));
1475 cmn_err(CE_CONT, fmt, a1, a2, a3, a4, a5);
1476 }
1477 }
1478
1479 /*
1480 * Dump the SBBC chip's Device ID Register
1481 */
sbbc_dump_devid(dev_info_t * dip,struct sbbcsoft * sbbcsoftp,int instance)1482 static void sbbc_dump_devid(dev_info_t *dip, struct sbbcsoft *sbbcsoftp,
1483 int instance)
1484 {
1485 uint32_t sbbc_id_reg;
1486 uint16_t sbbc_id_reg_partid;
1487 uint16_t sbbc_id_reg_manfid;
1488
1489 sbbc_id_reg = ddi_get32(sbbcsoftp->pci_sbbc_map_handle,
1490 (uint32_t *)&sbbcsoftp->pci_sbbc_map->devid);
1491
1492 sbbc_id_reg_partid = ((sbbc_id_reg << 4) >> 16);
1493 sbbc_id_reg_manfid = ((sbbc_id_reg << 20) >> 21);
1494
1495 SBBC_DBG4(SBBC_DBG_ATTACH, dip,
1496 "FOUND SBBC(%d) Version %x, Partid %x, Manfid %x\n",
1497 instance, (sbbc_id_reg >> 28), sbbc_id_reg_partid,
1498 sbbc_id_reg_manfid);
1499 }
1500
1501 #endif /* DEBUG */
1502