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 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26 #include <sys/types.h>
27 #include <sys/conf.h>
28 #include <sys/ddi.h>
29 #include <sys/sunddi.h>
30 #include <sys/modctl.h>
31 #include <sys/sunndi.h>
32 #include <sys/ddi_impldefs.h>
33 #include <sys/obpdefs.h>
34 #include <sys/cmn_err.h>
35 #include <sys/errno.h>
36 #include <sys/kmem.h>
37 #include <sys/debug.h>
38 #include <sys/sysmacros.h>
39 #include <sys/autoconf.h>
40 #include <sys/stat.h>
41 #include <sys/serengeti.h>
42 #include <sys/ssm.h>
43 #include <sys/sgsbbc_mailbox.h>
44 #include <sys/sgevents.h>
45 #include <sys/sysevent.h>
46 #include <sys/sysevent/dr.h>
47 #include <sys/sysevent/eventdefs.h>
48 #include <sys/ndi_impldefs.h>
49 #include <sys/ddifm.h>
50 #include <sys/ndifm.h>
51 #include <sys/sbd_ioctl.h>
52
53 /* Useful debugging Stuff */
54 #include <sys/nexusdebug.h>
55
56 /*
57 * module ssm.c
58 *
59 * This module is a nexus driver designed to support the ssm nexus driver
60 * and all children below it. This driver does not handle any of the
61 * DDI functions passed up to it by the ssm driver, but instead allows
62 * them to bubble up to the root node.
63 */
64
65
66 /*
67 * Function prototypes
68 */
69 extern int plat_max_boards();
70
71 static int
72 ssm_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result);
73
74 static int
75 ssm_attach(dev_info_t *, ddi_attach_cmd_t);
76
77 static int
78 ssm_detach(dev_info_t *, ddi_detach_cmd_t);
79
80 static int
81 ssm_open(dev_t *, int, int, cred_t *);
82
83 static int
84 ssm_close(dev_t, int, int, cred_t *);
85
86 static int
87 ssm_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
88
89 static int
90 ssm_ctlops(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *, void *);
91
92 static int
93 ssm_make_nodes(dev_info_t *dip, int instance, int ssm_nodeid);
94
95 static int
96 ssm_generate_event(int node, int board, int hint);
97
98 /*
99 * FMA error callback
100 * Register error handling callback with our parent. We will just call
101 * our children's error callbacks and return their status.
102 */
103 static int
104 ssm_err_callback(dev_info_t *dip, ddi_fm_error_t *derr, const void *impl_data);
105
106 /*
107 * fm_init busop to initialize our children
108 */
109 static int
110 ssm_fm_init_child(dev_info_t *dip, dev_info_t *tdip, int cap,
111 ddi_iblock_cookie_t *ibc);
112
113 /*
114 * init/fini routines to alloc/dealloc fm structures and
115 * register/unregister our callback.
116 */
117 static void
118 ssm_fm_init(struct ssm_soft_state *softsp);
119
120 static void
121 ssm_fm_fini(struct ssm_soft_state *softsp);
122
123 /*
124 * DR event handlers
125 * We want to register the event handlers once for all instances. In the
126 * other hand we have register them after the sbbc has been attached.
127 * event_initialize gives us the logic of only registering the events only
128 * once
129 */
130 int event_initialized = 0;
131 uint_t ssm_dr_event_handler(char *);
132
133 /*
134 * Event lock and state
135 */
136 static kmutex_t ssm_event_lock;
137 int ssm_event_state;
138
139 /*
140 * DR event msg and payload
141 */
142 static sbbc_msg_t event_msg;
143 static sg_system_fru_descriptor_t payload;
144
145 struct ssm_node2inst {
146 int nodeid; /* serengeti node #, NOT prom nodeid */
147 int inst;
148 struct ssm_node2inst *next;
149 };
150 static kmutex_t ssm_node2inst_lock;
151 static struct ssm_node2inst ssm_node2inst_map = {-1, -1, NULL};
152
153
154 /*
155 * Configuration data structures
156 */
157 static struct bus_ops ssm_bus_ops = {
158 BUSO_REV,
159 ddi_bus_map, /* map */
160 0, /* get_intrspec */
161 0, /* add_intrspec */
162 0, /* remove_intrspec */
163 i_ddi_map_fault, /* map_fault */
164 ddi_dma_map, /* dma_map */
165 ddi_dma_allochdl,
166 ddi_dma_freehdl,
167 ddi_dma_bindhdl,
168 ddi_dma_unbindhdl,
169 ddi_dma_flush,
170 ddi_dma_win,
171 ddi_dma_mctl, /* dma_ctl */
172 ssm_ctlops, /* ctl */
173 ddi_bus_prop_op, /* prop_op */
174 ndi_busop_get_eventcookie,
175 ndi_busop_add_eventcall,
176 ndi_busop_remove_eventcall,
177 ndi_post_event,
178 0,
179 0,
180 0,
181 ssm_fm_init_child,
182 NULL,
183 NULL,
184 NULL,
185 0,
186 i_ddi_intr_ops
187 };
188
189 static struct cb_ops ssm_cb_ops = {
190 ssm_open, /* open */
191 ssm_close, /* close */
192 nodev, /* strategy */
193 nodev, /* print */
194 nodev, /* dump */
195 nodev, /* read */
196 nodev, /* write */
197 ssm_ioctl, /* ioctl */
198 nodev, /* devmap */
199 nodev, /* mmap */
200 nodev, /* segmap */
201 nochpoll, /* poll */
202 ddi_prop_op, /* cb_prop_op */
203 NULL, /* streamtab */
204 D_NEW | D_MP | D_HOTPLUG, /* Driver compatibility flag */
205 CB_REV, /* rev */
206 nodev, /* int (*cb_aread)() */
207 nodev /* int (*cb_awrite)() */
208 };
209
210 static struct dev_ops ssm_ops = {
211 DEVO_REV, /* devo_rev, */
212 0, /* refcnt */
213 ssm_info, /* getinfo */
214 nulldev, /* identify */
215 nulldev, /* probe */
216 ssm_attach, /* attach */
217 ssm_detach, /* detach */
218 nulldev, /* reset */
219 &ssm_cb_ops, /* driver operations */
220 &ssm_bus_ops, /* bus_ops */
221 nulldev, /* power */
222 ddi_quiesce_not_needed, /* quiesce */
223 };
224
225 /*
226 * Driver globals
227 */
228 static void *ssm_softstates; /* ssm soft state hook */
229
230 extern struct mod_ops mod_driverops;
231
232 static struct modldrv modldrv = {
233 &mod_driverops, /* Type of module. This one is a driver */
234 "SSM Nexus", /* name of module */
235 &ssm_ops, /* driver ops */
236 };
237
238 static struct modlinkage modlinkage = {
239 MODREV_1, /* rev */
240 (void *)&modldrv,
241 NULL
242 };
243
244 static int ssm_loaded_sbd = FALSE;
245 kmutex_t ssm_lock;
246 static int init_child(dev_info_t *child);
247
248 /*
249 * These are the module initialization routines.
250 */
251
252 int
_init(void)253 _init(void)
254 {
255 int error;
256
257 #if defined(DEBUG)
258 debug_print_level = 0x0;
259 #endif
260
261 /* Initialize soft state pointer. */
262 if ((error = ddi_soft_state_init(&ssm_softstates,
263 sizeof (struct ssm_soft_state), SSM_MAX_INSTANCES)) != 0)
264 return (error);
265
266 /* Install the module. */
267 error = mod_install(&modlinkage);
268 if (error != 0)
269 ddi_soft_state_fini(&ssm_softstates);
270
271 mutex_init(&ssm_lock, NULL, MUTEX_DRIVER, NULL);
272
273 return (error);
274 }
275
276 int
_fini(void)277 _fini(void)
278 {
279 int error;
280
281 /* Remove the module. */
282 if ((error = mod_remove(&modlinkage)) != 0)
283 return (error);
284
285 /*
286 * Unregister the event handler
287 */
288 (void) sbbc_mbox_unreg_intr(MBOX_EVENT_GENERIC, ssm_dr_event_handler);
289 mutex_destroy(&ssm_event_lock);
290
291 /* Free the soft state info. */
292 ddi_soft_state_fini(&ssm_softstates);
293 mutex_destroy(&ssm_lock);
294
295 return (0);
296 }
297
298 int
_info(struct modinfo * modinfop)299 _info(struct modinfo *modinfop)
300 {
301 return (mod_info(&modlinkage, modinfop));
302 }
303
304 /* device driver entry points */
305
306 /*
307 * info entry point:
308 */
309
310 /* ARGSUSED */
311 static int
ssm_info(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)312 ssm_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
313 {
314 dev_t dev;
315 int instance;
316
317 if (infocmd == DDI_INFO_DEVT2INSTANCE) {
318 dev = (dev_t)arg;
319 instance = (getminor(dev) >> SSM_INSTANCE_SHIFT);
320 *result = (void *)(uintptr_t)instance;
321 return (DDI_SUCCESS);
322 }
323 return (DDI_FAILURE);
324 }
325
326 /*
327 * attach entry point:
328 */
329
330 static int
ssm_attach(dev_info_t * devi,ddi_attach_cmd_t cmd)331 ssm_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
332 {
333 int instance;
334 struct ssm_soft_state *softsp;
335 struct ssm_node2inst *prev, *sp, *tsp;
336
337 DPRINTF(SSM_ATTACH_DEBUG, ("ssm_attach\n"));
338
339 switch (cmd) {
340 case DDI_ATTACH:
341 break;
342
343 case DDI_RESUME:
344 return (DDI_SUCCESS);
345
346 default:
347 return (DDI_FAILURE);
348 }
349
350 instance = ddi_get_instance(devi);
351
352 if (ddi_soft_state_zalloc(ssm_softstates, instance) != DDI_SUCCESS)
353 return (DDI_FAILURE);
354
355 softsp = ddi_get_soft_state(ssm_softstates, instance);
356
357 /* Set the dip in the soft state */
358 softsp->dip = devi;
359 softsp->top_node = devi;
360 mutex_init(&softsp->ssm_sft_lock, NULL, MUTEX_DRIVER, NULL);
361
362 DPRINTF(SSM_ATTACH_DEBUG, ("ssm-%d: devi= 0x%p, softsp=0x%p\n",
363 instance, (void *)devi, (void *)softsp));
364
365 if ((softsp->ssm_nodeid = (int)ddi_getprop(DDI_DEV_T_ANY, softsp->dip,
366 DDI_PROP_DONTPASS, "nodeid", -1)) == -1) {
367 cmn_err(CE_WARN, "ssm%d: unable to retrieve %s property",
368 instance, "nodeid");
369 ddi_soft_state_free(ssm_softstates, instance);
370 return (DDI_FAILURE);
371 }
372
373 /* nothing to suspend/resume here */
374 (void) ddi_prop_create(DDI_DEV_T_NONE, devi, DDI_PROP_CANSLEEP,
375 "pm-hardware-state", (caddr_t)"no-suspend-resume",
376 strlen("no-suspend-resume") + 1);
377
378 #if DEBUG
379 if (ddi_create_minor_node(devi, "debug", S_IFCHR, instance,
380 DDI_NT_NEXUS, 0) != DDI_SUCCESS) {
381 ddi_soft_state_free(ssm_softstates, instance);
382 return (DDI_FAILURE);
383 }
384 #endif
385
386 if (ssm_make_nodes(devi, instance, softsp->ssm_nodeid)) {
387 cmn_err(CE_WARN, "ssm:%s:%d: failed to make nodes",
388 ddi_driver_name(devi), instance);
389 ddi_remove_minor_node(devi, NULL);
390 ddi_soft_state_free(ssm_softstates, instance);
391 return (DDI_FAILURE);
392 }
393 ssm_fm_init(softsp);
394 ddi_report_dev(devi);
395
396 if (event_initialized == 0) {
397 int rv;
398 /*
399 * Register DR event handler
400 */
401 mutex_init(&ssm_event_lock, NULL, MUTEX_DRIVER, NULL);
402 event_msg.msg_buf = (caddr_t)&payload;
403 event_msg.msg_len = sizeof (payload);
404
405 rv = sbbc_mbox_reg_intr(MBOX_EVENT_GENERIC,
406 ssm_dr_event_handler, &event_msg,
407 (uint_t *)&ssm_event_state, &ssm_event_lock);
408
409 if (rv == EINVAL)
410 event_initialized = 1;
411 }
412
413 /*
414 * Preallocate to avoid sleeping with ssm_node2inst_lock held -
415 * low level interrupts use this mutex.
416 */
417 tsp = kmem_zalloc(sizeof (struct ssm_node2inst), KM_SLEEP);
418
419 mutex_enter(&ssm_node2inst_lock);
420
421 for (prev = NULL, sp = &ssm_node2inst_map; sp != NULL;
422 prev = sp, sp = sp->next) {
423 ASSERT(sp->inst != instance);
424 ASSERT(sp->nodeid != softsp->ssm_nodeid);
425 if (sp->inst == -1)
426 break;
427 }
428
429 if (sp == NULL) {
430 ASSERT(prev->next == NULL);
431 sp = prev->next = tsp;
432 tsp = NULL;
433 sp->next = NULL;
434 }
435
436 sp->inst = instance;
437 sp->nodeid = softsp->ssm_nodeid;
438
439 mutex_exit(&ssm_node2inst_lock);
440
441 if (tsp != NULL)
442 kmem_free(tsp, sizeof (struct ssm_node2inst));
443
444 return (DDI_SUCCESS);
445 }
446
447 /*
448 * detach entry point:
449 */
450 /*ARGSUSED*/
451 static int
ssm_detach(dev_info_t * devi,ddi_detach_cmd_t cmd)452 ssm_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
453 {
454 int instance, rv;
455 int (*sbd_teardown_instance) (int, caddr_t);
456 ssm_sbdp_info_t sbdp_info;
457 struct ssm_soft_state *softsp;
458 struct ssm_node2inst *prev, *sp;
459
460 instance = ddi_get_instance(devi);
461 softsp = ddi_get_soft_state(ssm_softstates, instance);
462
463 if (softsp == NULL) {
464 cmn_err(CE_WARN,
465 "ssm_open bad instance number %d", instance);
466 return (ENXIO);
467 }
468
469 instance = ddi_get_instance(devi);
470
471 switch (cmd) {
472 case DDI_DETACH:
473 ddi_remove_minor_node(devi, NULL);
474
475 sbd_teardown_instance = (int (*) (int, caddr_t))
476 modlookup("misc/sbd", "sbd_teardown_instance");
477
478 if (!sbd_teardown_instance) {
479 cmn_err(CE_WARN, "cannot find sbd_teardown_instance");
480 return (DDI_FAILURE);
481 }
482
483 sbdp_info.instance = instance;
484 sbdp_info.wnode = softsp->ssm_nodeid;
485 rv = (*sbd_teardown_instance)(instance, (caddr_t)&sbdp_info);
486
487 if (rv != DDI_SUCCESS) {
488 cmn_err(CE_WARN, "cannot run sbd_teardown_instance");
489 return (DDI_FAILURE);
490 }
491 ssm_fm_fini(softsp);
492 mutex_destroy(&softsp->ssm_sft_lock);
493 ddi_soft_state_free(ssm_softstates, instance);
494
495 mutex_enter(&ssm_node2inst_lock);
496 for (prev = NULL, sp = &ssm_node2inst_map; sp != NULL;
497 prev = sp, sp = sp->next) {
498 /* Only the head of the list can persist if unused */
499 ASSERT(prev == NULL || sp->inst != -1);
500 if (sp->inst == instance)
501 break;
502 }
503 ASSERT(sp != NULL);
504
505 if (sp != &ssm_node2inst_map) {
506 prev->next = sp->next;
507 kmem_free(sp, sizeof (struct ssm_node2inst));
508 } else {
509 /*
510 * Invalidate the head element, but retain the rest
511 * of the list - "next" is still valid.
512 */
513
514 sp->nodeid = -1;
515 sp->inst = -1;
516 }
517 mutex_exit(&ssm_node2inst_lock);
518
519 return (DDI_SUCCESS);
520
521 case DDI_SUSPEND:
522 return (DDI_SUCCESS);
523
524 default:
525 return (DDI_FAILURE);
526 }
527 }
528
529 extern void make_ddi_ppd(dev_info_t *, struct ddi_parent_private_data **);
530 extern struct ddi_parent_private_data *init_regspec_64(dev_info_t *);
531
532 static int
name_child(dev_info_t * child,char * name,int namelen)533 name_child(dev_info_t *child, char *name, int namelen)
534 {
535 struct regspec *rp;
536 struct ddi_parent_private_data *pdptr;
537 int portid = 0;
538 int regbase = -1;
539 extern uint_t root_phys_addr_lo_mask;
540
541 make_ddi_ppd(child, &pdptr);
542 ddi_set_parent_data(child, pdptr);
543
544 name[0] = '\0';
545 if (sparc_pd_getnreg(child) == 0)
546 return (DDI_SUCCESS);
547
548 rp = sparc_pd_getreg(child, 0);
549
550 portid = ddi_prop_get_int(DDI_DEV_T_ANY, child,
551 DDI_PROP_DONTPASS, "portid", -1);
552 if (portid == -1) {
553 cmn_err(CE_WARN, "could not find portid property in %s",
554 DEVI(child)->devi_node_name);
555 } else {
556 regbase = rp->regspec_addr & root_phys_addr_lo_mask;
557 }
558 (void) snprintf(name, namelen, "%x,%x", portid, regbase);
559 return (DDI_SUCCESS);
560 }
561
562 static int
init_child(dev_info_t * child)563 init_child(dev_info_t *child)
564 {
565 char name[MAXNAMELEN];
566
567 (void) name_child(child, name, MAXNAMELEN);
568 ddi_set_name_addr(child, name);
569 if ((ndi_dev_is_persistent_node(child) == 0) &&
570 (ndi_merge_node(child, name_child) == DDI_SUCCESS)) {
571 impl_ddi_sunbus_removechild(child);
572 return (DDI_FAILURE);
573 }
574
575 (void) init_regspec_64(child);
576 return (DDI_SUCCESS);
577 }
578
579 /*
580 * Control ops entry point:
581 *
582 * Requests handled completely:
583 * DDI_CTLOPS_INITCHILD
584 * DDI_CTLOPS_UNINITCHILD
585 * DDI_CTLOPS_REPORTDEV
586 * All others are passed to the parent.
587 * The name of the ssm node is ssm@nodeid,0.
588 * ssm is the equivalent of rootnex.
589 */
590 static int
ssm_ctlops(dev_info_t * dip,dev_info_t * rdip,ddi_ctl_enum_t op,void * arg,void * result)591 ssm_ctlops(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t op, void *arg,
592 void *result)
593 {
594 int rval;
595
596 switch (op) {
597 case DDI_CTLOPS_INITCHILD: {
598 DPRINTF(SSM_CTLOPS_DEBUG, ("DDI_CTLOPS_INITCHILD\n"));
599 return (init_child((dev_info_t *)arg));
600 }
601
602 case DDI_CTLOPS_UNINITCHILD: {
603 DPRINTF(SSM_CTLOPS_DEBUG, ("DDI_CTLOPS_UNINITCHILD\n"));
604 impl_ddi_sunbus_removechild((dev_info_t *)arg);
605 return (DDI_SUCCESS);
606 }
607
608 case DDI_CTLOPS_REPORTDEV: {
609 char buf[80];
610 char *p = buf;
611 dev_info_t *parent;
612 int portid;
613
614 DPRINTF(SSM_CTLOPS_DEBUG, ("DDI_CTLOPS_REPORTDEV\n"));
615 parent = ddi_get_parent(rdip);
616
617 (void) sprintf(p, "%s%d at %s%d", DEVI(rdip)->devi_name,
618 DEVI(rdip)->devi_instance, ddi_get_name(parent),
619 ddi_get_instance(parent));
620 p += strlen(p);
621
622 /* Fetch Safari Extended Agent ID of this device. */
623 portid = (int)ddi_getprop(DDI_DEV_T_ANY, rdip,
624 DDI_PROP_DONTPASS, "portid", -1);
625
626 /*
627 * If this is one of the ssm children it will have
628 * portid property and its parent will be ssm.
629 * In this case report Node number and Safari id.
630 */
631 if (portid != -1 &&
632 strcmp("ssm", ddi_get_name(parent)) == 0) {
633 struct regspec *rp;
634 int node;
635 int safid;
636 int n;
637
638 rp = sparc_pd_getreg(rdip, 0);
639 n = sparc_pd_getnreg(rdip);
640 ASSERT(n > 0);
641
642 node = SG_PORTID_TO_NODEID(portid);
643 safid = SG_PORTID_TO_SAFARI_ID(portid);
644
645 (void) strcpy(p, ": ");
646 p += strlen(p);
647
648 (void) sprintf(p, "Node %d Safari id %d 0x%x%s",
649 node, safid,
650 rp->regspec_addr,
651 (n > 1 ? "" : " ..."));
652 p += strlen(p);
653 }
654
655 cmn_err(CE_CONT, "?%s\n", buf);
656 rval = DDI_SUCCESS;
657
658 break;
659 }
660
661 default:
662 rval = ddi_ctlops(dip, rdip, op, arg, result);
663
664 break;
665 }
666
667 return (rval);
668 }
669
670 /*ARGSUSED*/
671 static int
ssm_make_nodes(dev_info_t * dip,int instance,int ssm_nodeid)672 ssm_make_nodes(dev_info_t *dip, int instance, int ssm_nodeid)
673 {
674 int rv;
675 minor_t minor_num, bd;
676 auto char filename[20];
677
678 for (bd = 0; bd < plat_max_boards(); bd++) {
679 if (SG_BOARD_IS_CPU_TYPE(bd))
680 (void) sprintf(filename, "N%d.SB%d", ssm_nodeid, bd);
681 else
682 (void) sprintf(filename, "N%d.IB%d", ssm_nodeid, bd);
683
684 minor_num = (instance << SSM_INSTANCE_SHIFT) | bd;
685
686 rv = ddi_create_minor_node(dip, filename, S_IFCHR,
687 minor_num, DDI_NT_SBD_ATTACHMENT_POINT, NULL);
688 if (rv == DDI_FAILURE) {
689 cmn_err(CE_WARN,
690 "ssm_make_nodes:%d: failed to create "
691 "minor node (%s, 0x%x)",
692 instance, filename, minor_num);
693 return (-1);
694 }
695 }
696
697 return (0);
698 }
699
700
701 /* ARGSUSED */
702 static int
ssm_open(dev_t * devi,int flags,int otyp,cred_t * credp)703 ssm_open(dev_t *devi, int flags, int otyp, cred_t *credp)
704 {
705 struct ssm_soft_state *softsp;
706 minor_t board, instance;
707 int (*sbd_setup_instance)(int, dev_info_t *, int, int, caddr_t);
708 ssm_sbdp_info_t sbdp_info;
709 int rv;
710
711 instance = (getminor(*devi) >> SSM_INSTANCE_SHIFT);
712
713 softsp = ddi_get_soft_state(ssm_softstates, instance);
714 if (softsp == NULL) {
715 cmn_err(CE_WARN, "ssm_open bad instance number %d", instance);
716 return (ENXIO);
717 }
718
719 board = (getminor(*devi) & SSM_BOARD_MASK);
720
721 if (board < 0 || board > plat_max_boards()) {
722 return (ENXIO);
723 }
724
725 mutex_enter(&ssm_lock);
726 if (instance == 0 && ssm_loaded_sbd == FALSE) {
727
728 if (modload("misc", "sbd") == -1) {
729 cmn_err(CE_WARN, "ssm_open: cannot load sbd");
730 mutex_exit(&ssm_lock);
731 return (EIO);
732 }
733 ssm_loaded_sbd = TRUE;
734 }
735 mutex_exit(&ssm_lock);
736
737 mutex_enter(&softsp->ssm_sft_lock);
738 if (softsp->initialized == FALSE) {
739
740 if (softsp->top_node == NULL) {
741 cmn_err(CE_WARN, "cannot find ssm top dnode");
742 mutex_exit(&softsp->ssm_sft_lock);
743 return (EIO);
744 }
745
746 sbd_setup_instance = (int (*)(int, dev_info_t *, int, int,
747 caddr_t))modlookup("misc/sbd", "sbd_setup_instance");
748
749 if (!sbd_setup_instance) {
750 cmn_err(CE_WARN, "cannot find sbd_setup_instance");
751 mutex_exit(&softsp->ssm_sft_lock);
752 return (EIO);
753 }
754
755 sbdp_info.instance = instance;
756 sbdp_info.wnode = softsp->ssm_nodeid;
757
758 rv = (*sbd_setup_instance)(instance, softsp->top_node,
759 plat_max_boards(), softsp->ssm_nodeid,
760 (caddr_t)&sbdp_info);
761 if (rv != DDI_SUCCESS) {
762 cmn_err(CE_WARN, "cannot run sbd_setup_instance");
763 mutex_exit(&softsp->ssm_sft_lock);
764 return (EIO);
765 }
766 softsp->initialized = TRUE;
767 }
768 mutex_exit(&softsp->ssm_sft_lock);
769
770 return (DDI_SUCCESS);
771 }
772
773
774 /* ARGSUSED */
775 static int
ssm_close(dev_t dev,int flags,int otyp,cred_t * credp)776 ssm_close(dev_t dev, int flags, int otyp, cred_t *credp)
777 {
778 struct ssm_soft_state *softsp;
779 minor_t board, instance;
780
781 instance = (getminor(dev) >> SSM_INSTANCE_SHIFT);
782
783 softsp = ddi_get_soft_state(ssm_softstates, instance);
784 if (softsp == NULL)
785 return (ENXIO);
786
787 board = (getminor(dev) & SSM_BOARD_MASK);
788
789 if (board < 0 || board > plat_max_boards())
790 return (ENXIO);
791
792 return (DDI_SUCCESS);
793 }
794
795 /* ARGSUSED */
796 static int
ssm_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)797 ssm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
798 int *rvalp)
799 {
800 struct ssm_soft_state *softsp;
801 char *addr;
802 struct devctl_iocdata *dcp;
803 int instance, rv = 0;
804 int (*sbd_ioctl) (dev_t, int, intptr_t, int, char *);
805
806 instance = (getminor(dev) >> SSM_INSTANCE_SHIFT);
807 softsp = ddi_get_soft_state(ssm_softstates, instance);
808 if (softsp == NULL)
809 return (ENXIO);
810
811 switch (cmd) {
812
813 case DEVCTL_BUS_CONFIGURE:
814 /*
815 * read devctl ioctl data
816 */
817 if (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS)
818 return (EFAULT);
819
820 addr = ndi_dc_getaddr(dcp);
821 cmn_err(CE_NOTE,
822 "DEVCTL_BUS_CONFIGURE: device id is %s\n", addr);
823 ndi_dc_freehdl(dcp);
824 break;
825
826 case DEVCTL_BUS_UNCONFIGURE:
827 if (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS)
828 return (EFAULT);
829
830 addr = ndi_dc_getaddr(dcp);
831 cmn_err(CE_NOTE,
832 "DEVCTL_BUS_UNCONFIGURE: device id is %s\n", addr);
833 ndi_dc_freehdl(dcp);
834 break;
835
836 #ifdef DEBUG
837 case SSM_TEARDOWN_SBD: {
838 ssm_sbdp_info_t sbdp_info;
839 int (*sbd_teardown_instance) (int, caddr_t);
840 sbd_teardown_instance = (int (*) (int, caddr_t))
841 modlookup("misc/sbd", "sbd_teardown_instance");
842
843 if (!sbd_teardown_instance) {
844 cmn_err(CE_WARN, "cannot find sbd_teardown_instance");
845 return (EFAULT);
846 }
847
848 sbdp_info.instance = instance;
849 sbdp_info.wnode = softsp->ssm_nodeid;
850 rv = (*sbd_teardown_instance)(instance, (caddr_t)&sbdp_info);
851 if (rv != DDI_SUCCESS) {
852 cmn_err(CE_WARN, "cannot run sbd_teardown_instance");
853 return (EFAULT);
854 }
855
856 ssm_loaded_sbd = FALSE;
857 softsp->initialized = FALSE;
858 }
859 #endif
860
861
862 default: {
863 char event = 0;
864
865 sbd_ioctl = (int (*) (dev_t, int, intptr_t, int, char *))
866 modlookup("misc/sbd", "sbd_ioctl");
867
868 if (sbd_ioctl)
869 rv = (*sbd_ioctl) (dev, cmd, arg, mode, &event);
870 else {
871 cmn_err(CE_WARN, "cannot find sbd_ioctl");
872 return (ENXIO);
873 }
874 /*
875 * Check to see if we need to send an event
876 */
877 if (event == 1) {
878 int slot;
879 int hint = SE_NO_HINT;
880
881 if (rv == 0) {
882 if (cmd == SBD_CMD_CONNECT ||
883 cmd == SBD_CMD_CONFIGURE)
884 hint = SE_HINT_INSERT;
885 else if (cmd == SBD_CMD_UNCONFIGURE ||
886 cmd == SBD_CMD_DISCONNECT)
887 hint = SE_HINT_REMOVE;
888 }
889
890 slot = (getminor(dev) & SSM_BOARD_MASK);
891 (void) ssm_generate_event(softsp->ssm_nodeid, slot,
892 hint);
893 }
894 break;
895 }
896 }
897
898 return (rv);
899 }
900
901 void
ssm_get_attch_pnt(int node,int board,char * attach_pnt)902 ssm_get_attch_pnt(int node, int board, char *attach_pnt)
903 {
904 struct ssm_node2inst *sp;
905
906 /*
907 * Hold this mutex, until we are done so that ssm dip
908 * doesn't detach.
909 */
910 mutex_enter(&ssm_node2inst_lock);
911
912 for (sp = &ssm_node2inst_map; sp != NULL; sp = sp->next) {
913 if (sp->inst == -1)
914 continue;
915 if (sp->nodeid == node)
916 break;
917 }
918
919 if (sp == NULL) {
920 /* We didn't find the ssm dip, return failure */
921 attach_pnt[0] = '\0';
922 mutex_exit(&ssm_node2inst_lock);
923 return;
924 }
925
926 /*
927 * we have the instance, and the board, construct the attch pnt
928 */
929 if (SG_BOARD_IS_CPU_TYPE(board))
930 (void) sprintf(attach_pnt, "ssm%d:N%d.SB%d",
931 sp->inst, node, board);
932 else
933 (void) sprintf(attach_pnt, "ssm%d:N%d.IB%d",
934 sp->inst, node, board);
935
936 mutex_exit(&ssm_node2inst_lock);
937 }
938
939 /*
940 * Generate an event to sysevent
941 */
942 static int
ssm_generate_event(int node,int board,int hint)943 ssm_generate_event(int node, int board, int hint)
944 {
945 sysevent_t *ev;
946 sysevent_id_t eid;
947 int rv = 0;
948 sysevent_value_t evnt_val;
949 sysevent_attr_list_t *evnt_attr_list = NULL;
950 char attach_pnt[MAXPATHLEN];
951
952
953 attach_pnt[0] = '\0';
954 ssm_get_attch_pnt(node, board, attach_pnt);
955
956 if (attach_pnt[0] == '\0')
957 return (-1);
958
959 ev = sysevent_alloc(EC_DR, ESC_DR_AP_STATE_CHANGE, EP_DDI,
960 KM_SLEEP);
961 evnt_val.value_type = SE_DATA_TYPE_STRING;
962 evnt_val.value.sv_string = attach_pnt;
963
964 rv = sysevent_add_attr(&evnt_attr_list, DR_AP_ID, &evnt_val, KM_SLEEP);
965 if (rv != 0) {
966 cmn_err(CE_WARN, "Failed to add attr [%s] for %s event",
967 DR_AP_ID, EC_DR);
968 sysevent_free(ev);
969 return (rv);
970 }
971
972 /*
973 * Add the hint
974 */
975 evnt_val.value_type = SE_DATA_TYPE_STRING;
976 evnt_val.value.sv_string = SE_HINT2STR(hint);
977
978 rv = sysevent_add_attr(&evnt_attr_list, DR_HINT, &evnt_val, KM_SLEEP);
979 if (rv != 0) {
980 cmn_err(CE_WARN, "Failed to add attr [%s] for %s event",
981 DR_HINT, EC_DR);
982 sysevent_free_attr(evnt_attr_list);
983 sysevent_free(ev);
984 return (-1);
985 }
986
987 if (sysevent_attach_attributes(ev, evnt_attr_list) != 0) {
988 cmn_err(CE_WARN, "Failed to attach attr list for %s event",
989 EC_DR);
990 sysevent_free_attr(evnt_attr_list);
991 sysevent_free(ev);
992 return (-1);
993 }
994
995 rv = log_sysevent(ev, KM_NOSLEEP, &eid);
996 if (rv != 0) {
997 cmn_err(CE_WARN, "ssm_dr_event_handler: failed to log event");
998 }
999
1000 sysevent_free(ev);
1001
1002 return (rv);
1003 }
1004
1005 /*
1006 * DR Event Handler
1007 */
1008 uint_t
ssm_dr_event_handler(char * arg)1009 ssm_dr_event_handler(char *arg)
1010 {
1011 sg_system_fru_descriptor_t *fdp;
1012 int hint;
1013
1014
1015 fdp = (sg_system_fru_descriptor_t *)(((sbbc_msg_t *)arg)->msg_buf);
1016 if (fdp == NULL) {
1017 DPRINTF(SSM_EVENT_DEBUG,
1018 ("ssm_dr_event_handler: ARG is null\n"));
1019 return (DDI_INTR_CLAIMED);
1020 }
1021 #ifdef DEBUG
1022 DPRINTF(SSM_EVENT_DEBUG, ("ssm_dr_event_handler called\n"));
1023 DPRINTF(SSM_EVENT_DEBUG, ("\tnode\t%d\n", fdp->node));
1024 DPRINTF(SSM_EVENT_DEBUG, ("\tslot\t%d\n", fdp->slot));
1025 DPRINTF(SSM_EVENT_DEBUG, ("\tparent_hdl\t0x%lx\n", fdp->parent_hdl));
1026 DPRINTF(SSM_EVENT_DEBUG, ("\tchild_hdl\t0x%lx\n", fdp->child_hdl));
1027 DPRINTF(SSM_EVENT_DEBUG, ("\tevent_details\t%s\n",
1028 EVNT2STR(fdp->event_details)));
1029 #endif
1030
1031 switch (fdp->event_details) {
1032 case SG_EVT_BOARD_ABSENT:
1033 hint = SE_HINT_REMOVE;
1034 break;
1035 case SG_EVT_BOARD_PRESENT:
1036 hint = SE_HINT_INSERT;
1037 break;
1038 default:
1039 hint = SE_NO_HINT;
1040 break;
1041
1042 }
1043
1044 (void) ssm_generate_event(fdp->node, fdp->slot, hint);
1045
1046 return (DDI_INTR_CLAIMED);
1047 }
1048
1049 /*
1050 * Initialize our FMA resources
1051 */
1052 static void
ssm_fm_init(struct ssm_soft_state * softsp)1053 ssm_fm_init(struct ssm_soft_state *softsp)
1054 {
1055 softsp->ssm_fm_cap = DDI_FM_EREPORT_CAPABLE | DDI_FM_ERRCB_CAPABLE |
1056 DDI_FM_ACCCHK_CAPABLE | DDI_FM_DMACHK_CAPABLE;
1057
1058 /*
1059 * Request or capability level and get our parents capability
1060 * and ibc.
1061 */
1062 ddi_fm_init(softsp->dip, &softsp->ssm_fm_cap, &softsp->ssm_fm_ibc);
1063 ASSERT((softsp->ssm_fm_cap & DDI_FM_EREPORT_CAPABLE) &&
1064 (softsp->ssm_fm_cap & DDI_FM_ERRCB_CAPABLE));
1065 /*
1066 * Register error callback with our parent.
1067 */
1068 ddi_fm_handler_register(softsp->dip, ssm_err_callback, NULL);
1069 }
1070
1071 /*
1072 * Breakdown our FMA resources
1073 */
1074 static void
ssm_fm_fini(struct ssm_soft_state * softsp)1075 ssm_fm_fini(struct ssm_soft_state *softsp)
1076 {
1077 /*
1078 * Clean up allocated fm structures
1079 */
1080 ASSERT(softsp->ssm_fm_cap & DDI_FM_EREPORT_CAPABLE);
1081 ddi_fm_handler_unregister(softsp->dip);
1082 ddi_fm_fini(softsp->dip);
1083 }
1084
1085 /*
1086 * Initialize FMA resources for children devices. Called when
1087 * child calls ddi_fm_init().
1088 */
1089 /*ARGSUSED*/
1090 static int
ssm_fm_init_child(dev_info_t * dip,dev_info_t * tdip,int cap,ddi_iblock_cookie_t * ibc)1091 ssm_fm_init_child(dev_info_t *dip, dev_info_t *tdip, int cap,
1092 ddi_iblock_cookie_t *ibc)
1093 {
1094 struct ssm_soft_state *softsp = ddi_get_soft_state(ssm_softstates,
1095 ddi_get_instance(dip));
1096
1097 *ibc = softsp->ssm_fm_ibc;
1098 return (softsp->ssm_fm_cap);
1099 }
1100
1101 /*
1102 * FMA registered error callback
1103 */
1104 /*ARGSUSED*/
1105 static int
ssm_err_callback(dev_info_t * dip,ddi_fm_error_t * derr,const void * impl_data)1106 ssm_err_callback(dev_info_t *dip, ddi_fm_error_t *derr, const void *impl_data)
1107 {
1108 /* Call our children error handlers */
1109 return (ndi_fm_handler_dispatch(dip, NULL, derr));
1110 }
1111