103831d35Sstevel /* 203831d35Sstevel * CDDL HEADER START 303831d35Sstevel * 403831d35Sstevel * The contents of this file are subject to the terms of the 503831d35Sstevel * Common Development and Distribution License (the "License"). 603831d35Sstevel * You may not use this file except in compliance with the License. 703831d35Sstevel * 803831d35Sstevel * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 903831d35Sstevel * or http://www.opensolaris.org/os/licensing. 1003831d35Sstevel * See the License for the specific language governing permissions 1103831d35Sstevel * and limitations under the License. 1203831d35Sstevel * 1303831d35Sstevel * When distributing Covered Code, include this CDDL HEADER in each 1403831d35Sstevel * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 1503831d35Sstevel * If applicable, add the following below this CDDL HEADER, with the 1603831d35Sstevel * fields enclosed by brackets "[]" replaced with your own identifying 1703831d35Sstevel * information: Portions Copyright [yyyy] [name of copyright owner] 1803831d35Sstevel * 1903831d35Sstevel * CDDL HEADER END 2003831d35Sstevel */ 2103831d35Sstevel 2203831d35Sstevel /* 23*07d06da5SSurya Prakki * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 2403831d35Sstevel * Use is subject to license terms. 2503831d35Sstevel */ 2603831d35Sstevel 2703831d35Sstevel /* 2803831d35Sstevel * Domain specific portion of the Starcat Management Network Driver 2903831d35Sstevel */ 3003831d35Sstevel 3103831d35Sstevel #include <sys/types.h> 3203831d35Sstevel #include <sys/proc.h> 3303831d35Sstevel #include <sys/kmem.h> 3403831d35Sstevel #include <sys/stat.h> 3503831d35Sstevel #include <sys/kstat.h> 3603831d35Sstevel #include <sys/ksynch.h> 3703831d35Sstevel #include <sys/stream.h> 3803831d35Sstevel #include <sys/dlpi.h> 3903831d35Sstevel #include <sys/stropts.h> 4003831d35Sstevel #include <sys/strsubr.h> 4103831d35Sstevel #include <sys/debug.h> 4203831d35Sstevel #include <sys/conf.h> 4303831d35Sstevel #include <sys/kstr.h> 4403831d35Sstevel #include <sys/errno.h> 4503831d35Sstevel #include <sys/ethernet.h> 4603831d35Sstevel #include <sys/byteorder.h> 4703831d35Sstevel #include <sys/ddi.h> 4803831d35Sstevel #include <sys/sunddi.h> 4903831d35Sstevel #include <sys/modctl.h> 5003831d35Sstevel #include <sys/strsun.h> 5103831d35Sstevel #include <sys/pci.h> 5203831d35Sstevel #include <sys/callb.h> 5303831d35Sstevel #include <sys/pci.h> 5403831d35Sstevel #include <sys/iosramio.h> 5503831d35Sstevel #include <sys/mboxsc.h> 5603831d35Sstevel #include <netinet/in.h> 5703831d35Sstevel #include <inet/common.h> 5803831d35Sstevel #include <inet/mi.h> 5903831d35Sstevel #include <inet/nd.h> 6003831d35Sstevel #include <sys/socket.h> 6103831d35Sstevel #include <netinet/igmp_var.h> 6203831d35Sstevel #include <netinet/ip6.h> 6303831d35Sstevel #include <netinet/icmp6.h> 6403831d35Sstevel #include <inet/ip.h> 6503831d35Sstevel #include <inet/ip6.h> 6603831d35Sstevel #include <sys/dman.h> 6703831d35Sstevel #include <sys/ddi_impldefs.h> 6803831d35Sstevel #include <sys/sunndi.h> 6903831d35Sstevel 7003831d35Sstevel #define MAN_SCHIZO_BINDING_NAME "pci108e,8001" 7103831d35Sstevel #define MAN_XMITS_BINDING_NAME "pci108e,8002" 7203831d35Sstevel 7303831d35Sstevel int man_is_on_domain = TRUE; 7403831d35Sstevel 7503831d35Sstevel /* 7603831d35Sstevel * Domain side function prototypes. 7703831d35Sstevel */ 7803831d35Sstevel int man_get_iosram(manc_t *); 7903831d35Sstevel int man_domain_configure(void); 8003831d35Sstevel int man_domain_deconfigure(void); 8103831d35Sstevel int man_path_discovery(void); 8203831d35Sstevel int man_dossc_switch(uint32_t); 8303831d35Sstevel int man_dr_attach(dev_info_t *); 8403831d35Sstevel int man_dr_detach(dev_info_t *); 8503831d35Sstevel static int man_dr_submit_work_wait(dev_info_t *, int); 8603831d35Sstevel static int man_find_devs(mi_path_t *, uchar_t); 8703831d35Sstevel static int man_dip_is_schizoxmits0_pcib(dev_info_t *, int *, int *); 8803831d35Sstevel static int man_dip_is_eri(dev_info_t *, man_dev_t *); 8903831d35Sstevel static int man_dip_is_attached(dev_info_t *); 9003831d35Sstevel static int man_get_eri_dev_info(dev_info_t *, man_dev_t *); 9103831d35Sstevel static int man_mbox_initialized = FALSE; 9203831d35Sstevel 9303831d35Sstevel /* 9403831d35Sstevel * Externs 9503831d35Sstevel */ 9603831d35Sstevel extern int man_pg_cmd(mi_path_t *, man_work_t *); 9703831d35Sstevel extern kmutex_t man_lock; 9803831d35Sstevel extern void *man_softstate; 9903831d35Sstevel extern man_work_t *man_work_alloc(int, int); 10003831d35Sstevel extern void man_work_free(man_work_t *); 10103831d35Sstevel extern void man_work_add(man_workq_t *, man_work_t *); 10203831d35Sstevel extern man_workq_t *man_bwork_q; 10303831d35Sstevel extern man_workq_t *man_iwork_q; 10403831d35Sstevel extern queue_t *man_ctl_wq; 10503831d35Sstevel 10603831d35Sstevel #if defined(DEBUG) 10703831d35Sstevel static void man_print_manc(manc_t *); 10803831d35Sstevel extern uint32_t man_debug; 10903831d35Sstevel #endif /* DEBUG */ 11003831d35Sstevel 11103831d35Sstevel int 11203831d35Sstevel man_domain_configure(void) 11303831d35Sstevel { 11403831d35Sstevel int status = 0; 11503831d35Sstevel 11603831d35Sstevel /* 11703831d35Sstevel * man_mbox_initialized is protected by inner perimiter lock. 11803831d35Sstevel */ 11903831d35Sstevel if (man_mbox_initialized == TRUE) 12003831d35Sstevel goto exit; 12103831d35Sstevel 12203831d35Sstevel status = mboxsc_init(IOSRAM_KEY_SCMD, MBOXSC_MBOX_IN, NULL); 12303831d35Sstevel 12403831d35Sstevel if (status != 0) { 12503831d35Sstevel cmn_err(CE_WARN, "man_domain_configure: failed to initialize" 12603831d35Sstevel " MBOXSC_MBOX_IN, errno = %d", status); 12703831d35Sstevel goto exit; 12803831d35Sstevel } 12903831d35Sstevel 13003831d35Sstevel status = mboxsc_init(IOSRAM_KEY_MDSC, MBOXSC_MBOX_OUT, NULL); 13103831d35Sstevel if (status != 0) { 132*07d06da5SSurya Prakki (void) mboxsc_fini(IOSRAM_KEY_SCMD); 13303831d35Sstevel cmn_err(CE_WARN, "man_domain_configure: failed to initialize" 13403831d35Sstevel " MBOXSC_MBOX_OUT, errno = %d", status); 13503831d35Sstevel goto exit; 13603831d35Sstevel } 13703831d35Sstevel 13803831d35Sstevel man_mbox_initialized = TRUE; 13903831d35Sstevel 14003831d35Sstevel status = man_path_discovery(); 14103831d35Sstevel if (status != 0) { 142*07d06da5SSurya Prakki (void) mboxsc_fini(IOSRAM_KEY_SCMD); 143*07d06da5SSurya Prakki (void) mboxsc_fini(IOSRAM_KEY_MDSC); 14403831d35Sstevel man_mbox_initialized = FALSE; 14503831d35Sstevel } 14603831d35Sstevel 14703831d35Sstevel exit: 14803831d35Sstevel return (status); 14903831d35Sstevel } 15003831d35Sstevel 15103831d35Sstevel /* 15203831d35Sstevel * Build pathgroup connecting a domain to the SSC. Only called on domains 15303831d35Sstevel * at first man_open. On the SSC, pathgroups are built by IOCTL requests 15403831d35Sstevel * from the MAN daemon (see man_ioctl and mand(1M)). 15503831d35Sstevel * 15603831d35Sstevel * Locks held 15703831d35Sstevel * - exclusive innerperim. 15803831d35Sstevel */ 15903831d35Sstevel int 16003831d35Sstevel man_path_discovery(void) 16103831d35Sstevel { 16203831d35Sstevel manc_t manc; 16303831d35Sstevel mi_path_t mpath; 16403831d35Sstevel int num_devs; 16503831d35Sstevel int status = 0; 16603831d35Sstevel int i; 16703831d35Sstevel 16803831d35Sstevel MAN_DBG(MAN_CONFIG, ("man_path_discovery:")); 16903831d35Sstevel 17003831d35Sstevel if (status = man_get_iosram(&manc)) { 17103831d35Sstevel goto exit; 17203831d35Sstevel } 17303831d35Sstevel 17403831d35Sstevel /* 17503831d35Sstevel * If manc_ip_type indicates MAN network is not enabled 17603831d35Sstevel * for this domain, then lets just bailout from here as if no 17703831d35Sstevel * devices were found. 17803831d35Sstevel */ 17903831d35Sstevel if ((manc.manc_ip_type != AF_INET) && 18003831d35Sstevel (manc.manc_ip_type != AF_INET6)) { 18103831d35Sstevel goto exit; 18203831d35Sstevel } 18303831d35Sstevel 18403831d35Sstevel MAN_DBGCALL(MAN_CONFIG, man_print_manc(&manc)); 18503831d35Sstevel 18603831d35Sstevel /* 18703831d35Sstevel * Extract SC ethernet address from IOSRAM. 18803831d35Sstevel */ 18903831d35Sstevel ether_copy(&manc.manc_sc_eaddr, &mpath.mip_eaddr); 19003831d35Sstevel 19103831d35Sstevel mpath.mip_pg_id = 0; /* SC is always pathgroup ID 0 */ 19203831d35Sstevel mpath.mip_man_ppa = 0; /* Domain only has one ppa, 0 */ 19303831d35Sstevel 19403831d35Sstevel /* 19503831d35Sstevel * Get list of present devices, and update man_paths[] as needed. 19603831d35Sstevel */ 19703831d35Sstevel num_devs = man_find_devs(&mpath, MAN_MAX_EXPANDERS); 19803831d35Sstevel if (num_devs <= 0) { 19903831d35Sstevel status = ENODEV; 20003831d35Sstevel goto exit; 20103831d35Sstevel } 20203831d35Sstevel 20303831d35Sstevel mpath.mip_cmd = MI_PATH_ASSIGN; 20403831d35Sstevel 20503831d35Sstevel mutex_enter(&man_lock); 20603831d35Sstevel status = man_pg_cmd(&mpath, NULL); 20703831d35Sstevel if (status) { 20803831d35Sstevel mutex_exit(&man_lock); 20903831d35Sstevel goto exit; 21003831d35Sstevel } 21103831d35Sstevel 21203831d35Sstevel /* 21303831d35Sstevel * Now activate the ethernet on the golden io board. 21403831d35Sstevel */ 21503831d35Sstevel for (i = 0; i < num_devs; i++) { 21603831d35Sstevel if (mpath.mip_devs[i].mdev_exp_id == manc.manc_golden_iob) 21703831d35Sstevel mpath.mip_devs[0] = mpath.mip_devs[i]; 21803831d35Sstevel } 21903831d35Sstevel mpath.mip_ndevs = 1; 22003831d35Sstevel mpath.mip_cmd = MI_PATH_ACTIVATE; 22103831d35Sstevel status = man_pg_cmd(&mpath, NULL); 22203831d35Sstevel mutex_exit(&man_lock); 22303831d35Sstevel 22403831d35Sstevel exit: 22503831d35Sstevel MAN_DBG(MAN_CONFIG, ("man_path_discovery: returns %d\n", status)); 22603831d35Sstevel 22703831d35Sstevel return (status); 22803831d35Sstevel } 22903831d35Sstevel 23003831d35Sstevel int 23103831d35Sstevel man_domain_deconfigure(void) 23203831d35Sstevel { 23303831d35Sstevel 234*07d06da5SSurya Prakki (void) mboxsc_fini(IOSRAM_KEY_SCMD); 235*07d06da5SSurya Prakki (void) mboxsc_fini(IOSRAM_KEY_MDSC); 23603831d35Sstevel /* 23703831d35Sstevel * We are about to unload and know that there are no open 23803831d35Sstevel * streams, so this change outside of the perimiter is ok. 23903831d35Sstevel */ 24003831d35Sstevel man_mbox_initialized = FALSE; 24103831d35Sstevel 24203831d35Sstevel return (0); 24303831d35Sstevel } 24403831d35Sstevel 24503831d35Sstevel /* 24603831d35Sstevel * Add a work request to the inner perimeter with the new eri device info. 24703831d35Sstevel */ 24803831d35Sstevel /* ARGSUSED */ 24903831d35Sstevel int 25003831d35Sstevel man_dr_attach(dev_info_t *dip) 25103831d35Sstevel { 25203831d35Sstevel man_t *manp; 25303831d35Sstevel man_work_t *wp; 25403831d35Sstevel int status = 0; 25503831d35Sstevel man_dev_t mdev; 25603831d35Sstevel 25703831d35Sstevel 25803831d35Sstevel mutex_enter(&man_lock); 25903831d35Sstevel manp = ddi_get_soft_state(man_softstate, 0); 26003831d35Sstevel if (manp == NULL || manp->man_pg == NULL) { 26103831d35Sstevel goto exit; 26203831d35Sstevel } 26303831d35Sstevel 26403831d35Sstevel if (man_get_eri_dev_info(dip, &mdev) == FALSE) { 26503831d35Sstevel status = ENODEV; 26603831d35Sstevel goto exit; 26703831d35Sstevel } 26803831d35Sstevel MAN_DBG(MAN_DR, ("man_dr_attach: dip major = %d instance =%d", 26903831d35Sstevel mdev.mdev_major, mdev.mdev_ppa)); 27003831d35Sstevel wp = man_work_alloc(MAN_WORK_DRATTACH, KM_NOSLEEP); 27103831d35Sstevel if (wp == NULL) { 27203831d35Sstevel status = ENOMEM; 27303831d35Sstevel goto exit; 27403831d35Sstevel } 27503831d35Sstevel 27603831d35Sstevel wp->mw_arg.a_man_ppa = 0; /* Domain only has one ppa, 0 */ 27703831d35Sstevel wp->mw_arg.a_pg_id = 0; /* SC is always pathgroup ID 0 */ 27803831d35Sstevel wp->mw_arg.a_sf_dev = mdev; 27903831d35Sstevel wp->mw_flags = MAN_WFLAGS_NOWAITER; 28003831d35Sstevel 28103831d35Sstevel man_work_add(man_iwork_q, wp); 28203831d35Sstevel 28303831d35Sstevel if (man_ctl_wq) 28403831d35Sstevel qenable(man_ctl_wq); 28503831d35Sstevel 28603831d35Sstevel exit: 28703831d35Sstevel mutex_exit(&man_lock); 28803831d35Sstevel 28903831d35Sstevel return (status); 29003831d35Sstevel } 29103831d35Sstevel 29203831d35Sstevel int 29303831d35Sstevel man_dr_detach(dev_info_t *dip) 29403831d35Sstevel { 29503831d35Sstevel man_t *manp; 29603831d35Sstevel int status = 0; 29703831d35Sstevel int retries = 0; 29803831d35Sstevel 29903831d35Sstevel 30003831d35Sstevel mutex_enter(&man_lock); 30103831d35Sstevel manp = ddi_get_soft_state(man_softstate, 0); 30203831d35Sstevel if (manp == NULL || manp->man_pg == NULL) { 30303831d35Sstevel mutex_exit(&man_lock); 30403831d35Sstevel goto exit; 30503831d35Sstevel } 30603831d35Sstevel mutex_exit(&man_lock); 30703831d35Sstevel 30803831d35Sstevel /* 30903831d35Sstevel * Arrange to have the detaching path switched if it is active. 31003831d35Sstevel * We will cv_wait_sig for the switch to complete if it is needed. 31103831d35Sstevel */ 31203831d35Sstevel again: 31303831d35Sstevel status = man_dr_submit_work_wait(dip, MAN_WORK_DRSWITCH); 31403831d35Sstevel if (status == EAGAIN && retries < manp->man_dr_retries) { 31503831d35Sstevel /* 31603831d35Sstevel * Delay a bit and retry. 31703831d35Sstevel */ 31803831d35Sstevel MAN_DBG(MAN_DR, 31903831d35Sstevel ("man_dr_detach(switch): EAGAIN - retrying...")); 32003831d35Sstevel retries++; 32103831d35Sstevel delay(drv_usectohz(manp->man_dr_delay)); 32203831d35Sstevel goto again; 32303831d35Sstevel } 32403831d35Sstevel 32503831d35Sstevel if (status) 32603831d35Sstevel goto exit; 32703831d35Sstevel 32803831d35Sstevel retries = 0; 32903831d35Sstevel 33003831d35Sstevel /* 33103831d35Sstevel * Detaching device no longer in use, remove it from our 33203831d35Sstevel * pathgroup. 33303831d35Sstevel */ 33403831d35Sstevel status = man_dr_submit_work_wait(dip, MAN_WORK_DRDETACH); 33503831d35Sstevel if (status == EAGAIN && retries < manp->man_dr_retries) { 33603831d35Sstevel MAN_DBG(MAN_DR, 33703831d35Sstevel ("man_dr_detach(detach): EAGAIN - retrying...")); 33803831d35Sstevel retries++; 33903831d35Sstevel goto again; 34003831d35Sstevel } 34103831d35Sstevel 34203831d35Sstevel exit: 34303831d35Sstevel MAN_DBG(MAN_DR, ("man_dr_detach: returns %d", status)); 34403831d35Sstevel return (status); 34503831d35Sstevel } 34603831d35Sstevel 34703831d35Sstevel static int 34803831d35Sstevel man_dr_submit_work_wait(dev_info_t *dip, int work_type) 34903831d35Sstevel { 35003831d35Sstevel man_work_t *wp; 35103831d35Sstevel int status = 0; 35203831d35Sstevel 35303831d35Sstevel wp = man_work_alloc(work_type, KM_NOSLEEP); 35403831d35Sstevel if (wp == NULL) { 35503831d35Sstevel status = ENOMEM; 35603831d35Sstevel goto exit; 35703831d35Sstevel } 35803831d35Sstevel 35903831d35Sstevel wp->mw_arg.a_man_ppa = 0; 36003831d35Sstevel wp->mw_arg.a_pg_id = 0; 3615c066ec2SJerry Gilliam wp->mw_arg.a_sf_dev.mdev_major = ddi_driver_major(dip); 36203831d35Sstevel wp->mw_arg.a_sf_dev.mdev_ppa = ddi_get_instance(dip); 36303831d35Sstevel 36403831d35Sstevel mutex_enter(&man_lock); 36503831d35Sstevel wp->mw_flags = MAN_WFLAGS_CVWAITER; 36603831d35Sstevel man_work_add(man_iwork_q, wp); 36703831d35Sstevel 36803831d35Sstevel /* TBD - change to ASSERT ? */ 36903831d35Sstevel if (man_ctl_wq) 37003831d35Sstevel qenable(man_ctl_wq); 37103831d35Sstevel 37203831d35Sstevel while (!(wp->mw_flags & MAN_WFLAGS_DONE)) { 37303831d35Sstevel if (!cv_wait_sig(&wp->mw_cv, &man_lock)) { 37403831d35Sstevel wp->mw_flags &= ~MAN_WFLAGS_CVWAITER; 37503831d35Sstevel status = EINTR; 37603831d35Sstevel break; 37703831d35Sstevel } 37803831d35Sstevel } 37903831d35Sstevel 38003831d35Sstevel /* 38103831d35Sstevel * Note that if cv_wait_sig() returns zero because a signal 38203831d35Sstevel * was received, MAN_WFLAGS_DONE may not be set. 38303831d35Sstevel * This will happen if man_dr_submit_work_wait() reacquires 38403831d35Sstevel * man_lock before man_iwork() can acquire man_lock just before 38503831d35Sstevel * signalling its work is complete. 38603831d35Sstevel * In this case, it is not necessary to call man_work_free() 38703831d35Sstevel * here because it will be called by man_iwork() because 38803831d35Sstevel * MAN_WFLAGS_CVWAITER was cleared. 38903831d35Sstevel * Should man_iwork() obtain man_lock to signal completion, 39003831d35Sstevel * MAN_WFLAGS_DONE will be set which will ensure man_work_free() 39103831d35Sstevel * is called here. 39203831d35Sstevel */ 39303831d35Sstevel if (wp->mw_flags & MAN_WFLAGS_DONE) { 39403831d35Sstevel status = wp->mw_status; 39503831d35Sstevel man_work_free(wp); 39603831d35Sstevel } 39703831d35Sstevel 39803831d35Sstevel mutex_exit(&man_lock); 39903831d35Sstevel 40003831d35Sstevel exit: 40103831d35Sstevel return (status); 40203831d35Sstevel } 40303831d35Sstevel 40403831d35Sstevel /* 40503831d35Sstevel * Notify SSC of switch request and wait for response. 40603831d35Sstevel */ 40703831d35Sstevel int 40803831d35Sstevel man_dossc_switch(uint32_t exp_id) 40903831d35Sstevel { 41003831d35Sstevel uint64_t req_tid; 41103831d35Sstevel uint32_t req_cmd; 41203831d35Sstevel uint64_t resp_tid; 41303831d35Sstevel uint32_t resp_cmd; 41403831d35Sstevel uint32_t type; 41503831d35Sstevel man_mbox_msg_t req; 41603831d35Sstevel man_mbox_msg_t resp; 41703831d35Sstevel uint32_t length; 41803831d35Sstevel int status = 0; 41903831d35Sstevel 42003831d35Sstevel /* 42103831d35Sstevel * There should be nothing in inbound mailbox. 42203831d35Sstevel */ 42303831d35Sstevel resp_tid = resp_cmd = type = 0; 42403831d35Sstevel length = sizeof (man_mbox_msg_t); 42503831d35Sstevel bzero((char *)&resp, sizeof (man_mbox_msg_t)); 42603831d35Sstevel while (mboxsc_getmsg(IOSRAM_KEY_SCMD, &type, &resp_cmd, &resp_tid, 42703831d35Sstevel &length, &resp, 0) == 0) { 42803831d35Sstevel 42903831d35Sstevel resp_tid = resp_cmd = type = 0; 43003831d35Sstevel length = sizeof (man_mbox_msg_t); 43103831d35Sstevel bzero((char *)&resp, sizeof (man_mbox_msg_t)); 43203831d35Sstevel 43303831d35Sstevel MAN_DBG(MAN_IOSRAM, ("man_dossc_switch: dumping message")); 43403831d35Sstevel MAN_DBG(MAN_IOSRAM, ("\tcommand = 0x%x", resp_cmd)); 43503831d35Sstevel } 43603831d35Sstevel 43703831d35Sstevel MAN_DBG(MAN_IOSRAM, ("man_dossc_switch: sending message")); 43803831d35Sstevel 43903831d35Sstevel bzero((char *)&req, sizeof (man_mbox_msg_t)); 44003831d35Sstevel req.mb_status = 0; 44103831d35Sstevel req.mb_exp_id = exp_id; 44203831d35Sstevel req_tid = 0; 44303831d35Sstevel req_cmd = MAN_WORK_SWITCH; 44403831d35Sstevel 44503831d35Sstevel status = mboxsc_putmsg(IOSRAM_KEY_MDSC, MBOXSC_MSG_REQUEST, 44603831d35Sstevel req_cmd, &req_tid, sizeof (man_mbox_msg_t), &req, 44703831d35Sstevel MAN_IOSRAM_TIMEOUT); 44803831d35Sstevel 44903831d35Sstevel if (status != 0) { 45003831d35Sstevel cmn_err(CE_WARN, "man_dossc_switch: mboxsc_putmsg failed," 45103831d35Sstevel " errno = %d", status); 45203831d35Sstevel goto exit; 45303831d35Sstevel } 45403831d35Sstevel 45503831d35Sstevel bzero((char *)&resp, sizeof (man_mbox_msg_t)); 45603831d35Sstevel 45703831d35Sstevel resp_tid = type = resp_cmd = 0; 45803831d35Sstevel length = sizeof (man_mbox_msg_t); 45903831d35Sstevel status = mboxsc_getmsg(IOSRAM_KEY_SCMD, &type, &resp_cmd, &resp_tid, 46003831d35Sstevel &length, (void *)&resp, MAN_IOSRAM_TIMEOUT); 46103831d35Sstevel if (status != 0) { 46203831d35Sstevel cmn_err(CE_WARN, "man_dossc_switch: mboxsc_getmsg failed," 46303831d35Sstevel " errno = %d", status); 46403831d35Sstevel goto exit; 46503831d35Sstevel } 46603831d35Sstevel 46703831d35Sstevel MAN_DBG(MAN_IOSRAM, ("man_dossc_switch: received message")); 46803831d35Sstevel 46903831d35Sstevel if (req_cmd != resp_cmd || req_tid != resp_tid) { 47003831d35Sstevel cmn_err(CE_WARN, "man_dossc_switch: failed," 47103831d35Sstevel " cmd/transid mismatch (%d, %d)/(%d, %d)", 47203831d35Sstevel req_cmd, resp_cmd, (int)req_tid, (int)resp_tid); 47303831d35Sstevel status = EINVAL; 47403831d35Sstevel goto exit; 47503831d35Sstevel } 47603831d35Sstevel 47703831d35Sstevel status = resp.mb_status; 47803831d35Sstevel if (status != 0) { 47903831d35Sstevel cmn_err(CE_WARN, "man_dossc_switch: failed errno == %d", 48003831d35Sstevel status); 48103831d35Sstevel } 48203831d35Sstevel exit: 48303831d35Sstevel return (status); 48403831d35Sstevel } 48503831d35Sstevel 48603831d35Sstevel 48703831d35Sstevel /* 48803831d35Sstevel * Read IOSRAM info. 48903831d35Sstevel */ 49003831d35Sstevel int 49103831d35Sstevel man_get_iosram(manc_t *mcp) 49203831d35Sstevel { 49303831d35Sstevel int status; 49403831d35Sstevel 49503831d35Sstevel if (mcp == NULL) 49603831d35Sstevel return (EINVAL); 49703831d35Sstevel 49803831d35Sstevel status = iosram_rd(IOSRAM_KEY_MANC, 0, sizeof (manc_t), (caddr_t)mcp); 49903831d35Sstevel if (status) { 50003831d35Sstevel cmn_err(CE_WARN, "man_get_iosram: iosram_rd failed" 50103831d35Sstevel " errno = %d\n", status); 50203831d35Sstevel return (status); 50303831d35Sstevel } 50403831d35Sstevel 50503831d35Sstevel MAN_DBG(MAN_PATH, ("man_get_iosram:")); 50603831d35Sstevel MAN_DBGCALL(MAN_PATH, man_print_manc(mcp)); 50703831d35Sstevel 50803831d35Sstevel if (mcp->manc_magic != IOSRAM_KEY_MANC) { 50903831d35Sstevel cmn_err(CE_WARN, "man_get_iosram: bad magic - got(0x%x)" 51003831d35Sstevel " expected(0x%x)\n", mcp->manc_magic, IOSRAM_KEY_MANC); 51103831d35Sstevel status = EIO; 51203831d35Sstevel } else if (mcp->manc_version != MANC_VERSION) { 51303831d35Sstevel cmn_err(CE_WARN, "man_get_iosram: version mismatch -" 51403831d35Sstevel " got(0x%x) expected(0x%x)\n", mcp->manc_version, 51503831d35Sstevel MANC_VERSION); 51603831d35Sstevel status = EIO; 51703831d35Sstevel } 51803831d35Sstevel 51903831d35Sstevel return (status); 52003831d35Sstevel } 52103831d35Sstevel 52203831d35Sstevel #if defined(MAN_NO_IOSRAM) 52303831d35Sstevel 52403831d35Sstevel static manc_t manc = { 52503831d35Sstevel IOSRAM_KEY_MANC, 52603831d35Sstevel MANC_VERSION, 52703831d35Sstevel 0, 52803831d35Sstevel AF_INET, 52903831d35Sstevel /* 0x10010102, Two */ 53003831d35Sstevel 0x10010103, /* Scot */ 53103831d35Sstevel 0xFF000000, /* Scot netmask */ 53203831d35Sstevel 0x10010101, /* SC 10.1.1.1 */ 53303831d35Sstevel {0}, /* AF_INET6 addrs */ 53403831d35Sstevel {0}, /* AF_INET6 addrs */ 53503831d35Sstevel {0}, 53603831d35Sstevel /* {0x8, 0x0, 0x20, 0x21, 0x44, 0x83}, Domain eaddr "two" */ 53703831d35Sstevel {0x8, 0x0, 0x20, 0x8f, 0x84, 0x63}, /* Domain eaddr "scot" */ 53803831d35Sstevel {0x8, 0x0, 0x20, 0x1f, 0xe3, 0x46}, /* SC eaddr "one" */ 53903831d35Sstevel 0x1, 54003831d35Sstevel 0x1 54103831d35Sstevel }; 54203831d35Sstevel 54303831d35Sstevel 54403831d35Sstevel /* 54503831d35Sstevel * Get IOSRAM info or release it. 54603831d35Sstevel */ 54703831d35Sstevel int 54803831d35Sstevel man_get_iosram(manc_t *mcp) 54903831d35Sstevel { 55003831d35Sstevel int status = 0; 55103831d35Sstevel 55203831d35Sstevel if (mcp == NULL) 55303831d35Sstevel return (EINVAL); 55403831d35Sstevel 55503831d35Sstevel *mcp = manc; 55603831d35Sstevel 55703831d35Sstevel if (mcp->manc_magic != IOSRAM_KEY_MANC) { 55803831d35Sstevel cmn_err(CE_WARN, "man_get_iosram: bad magic - got(0x%x)" 55903831d35Sstevel " expected(0x%x)\n", mcp->manc_magic, IOSRAM_KEY_MANC); 56003831d35Sstevel status = EIO; 56103831d35Sstevel } else if (mcp->manc_version != MANC_VERSION) { 56203831d35Sstevel cmn_err(CE_WARN, "man_get_iosram: version mismatch -" 56303831d35Sstevel " got(0x%x) expected(0x%x)\n", mcp->manc_version, 56403831d35Sstevel MANC_VERSION); 56503831d35Sstevel status = EIO; 56603831d35Sstevel } 56703831d35Sstevel 56803831d35Sstevel return (status); 56903831d35Sstevel } 57003831d35Sstevel #endif /* MAN_NO_IOSRAM */ 57103831d35Sstevel 57203831d35Sstevel /* 57303831d35Sstevel * Find all RIOs on the IO boards for the domain. We walk all the children 57403831d35Sstevel * of the root node looking for a PCI devinfo with a safari port ID of 57503831d35Sstevel * 0xDC that has a child with device ID of 3. This is gauranteed to be 57603831d35Sstevel * the network portion of the RIO by virtue of the way Starcats are 57703831d35Sstevel * physically built. 57803831d35Sstevel */ 57903831d35Sstevel static int 58003831d35Sstevel man_find_devs(mi_path_t *mipathp, uchar_t golden_iob) 58103831d35Sstevel { 58203831d35Sstevel dev_info_t *bus_dip; 58303831d35Sstevel dev_info_t *eri_dip; 58403831d35Sstevel dev_info_t *rdip, *pdip; 58503831d35Sstevel int exp_id; 58603831d35Sstevel int found = 0; 58703831d35Sstevel int circ; 58803831d35Sstevel int circ2; 58903831d35Sstevel man_dev_t ndev; 59003831d35Sstevel int xmits; 59103831d35Sstevel 59203831d35Sstevel MAN_DBG(MAN_PATH, ("man_find_devs: mdevpp(0x%p) golden_iob(%d)\n", 59303831d35Sstevel (void *)(mipathp), golden_iob)); 59403831d35Sstevel 59503831d35Sstevel /* 59603831d35Sstevel * Hold parent busy while walking its child list. 59703831d35Sstevel */ 59803831d35Sstevel rdip = ddi_root_node(); 59903831d35Sstevel ndi_devi_enter(rdip, &circ); 60003831d35Sstevel bus_dip = ddi_get_child(rdip); 60103831d35Sstevel 60203831d35Sstevel while (bus_dip != NULL) { 60303831d35Sstevel exp_id = -1; 60403831d35Sstevel xmits = 0; 60503831d35Sstevel if (man_dip_is_schizoxmits0_pcib(bus_dip, &exp_id, &xmits)) { 60603831d35Sstevel eri_dip = NULL; 60703831d35Sstevel pdip = bus_dip; 60803831d35Sstevel if (xmits) { 60903831d35Sstevel /* 61003831d35Sstevel * If this is XMITS0 PCI_B leaf, then the 61103831d35Sstevel * pci_pci bridge which is the only child, 61203831d35Sstevel * is the parent to MAN RIO. 61303831d35Sstevel */ 61403831d35Sstevel pdip = ddi_get_child(bus_dip); 61503831d35Sstevel if (pdip == NULL) { 61603831d35Sstevel bus_dip = ddi_get_next_sibling(bus_dip); 61703831d35Sstevel continue; 61803831d35Sstevel } 61903831d35Sstevel } 62003831d35Sstevel ndi_devi_enter(pdip, &circ2); 62103831d35Sstevel eri_dip = ddi_get_child(pdip); 62203831d35Sstevel while (eri_dip != NULL) { 62303831d35Sstevel MAN_DBG(MAN_PATH, ("man_find_devs: " 62403831d35Sstevel "eri_dip %s\n", 62503831d35Sstevel ddi_binding_name(eri_dip))); 62603831d35Sstevel if (man_dip_is_eri(eri_dip, &ndev) && 62703831d35Sstevel man_dip_is_attached(eri_dip)) { 62803831d35Sstevel 62903831d35Sstevel ASSERT(exp_id != -1); 63003831d35Sstevel ndev.mdev_exp_id = exp_id; 63103831d35Sstevel ndev.mdev_state = MDEV_ASSIGNED; 63203831d35Sstevel mipathp->mip_devs[found] = ndev; 63303831d35Sstevel found++; 63403831d35Sstevel 63503831d35Sstevel MAN_DBG(MAN_PATH, 63603831d35Sstevel ("man_find_devs: found eri maj(%d) " 63703831d35Sstevel "ppa(%d) on expander(%d)\n", 63803831d35Sstevel ndev.mdev_major, 63903831d35Sstevel ndev.mdev_ppa, exp_id)); 64003831d35Sstevel } 64103831d35Sstevel eri_dip = ddi_get_next_sibling(eri_dip); 64203831d35Sstevel } 64303831d35Sstevel ndi_devi_exit(pdip, circ2); 64403831d35Sstevel } 64503831d35Sstevel bus_dip = ddi_get_next_sibling(bus_dip); 64603831d35Sstevel } 64703831d35Sstevel ndi_devi_exit(rdip, circ); 64803831d35Sstevel 64903831d35Sstevel MAN_DBG(MAN_PATH, ("man_find_devs returns found = %d\n", found)); 65003831d35Sstevel 65103831d35Sstevel mipathp->mip_ndevs = found; 65203831d35Sstevel return (found); 65303831d35Sstevel } 65403831d35Sstevel 65503831d35Sstevel /* 65603831d35Sstevel * Verify if the dip passed is an instance of 'eri' and set 65703831d35Sstevel * the device info in mdevp. 65803831d35Sstevel */ 65903831d35Sstevel static int 66003831d35Sstevel man_get_eri_dev_info(dev_info_t *dip, man_dev_t *mdevp) 66103831d35Sstevel { 66203831d35Sstevel dev_info_t *parent_dip; 66303831d35Sstevel int exp_id; 66403831d35Sstevel int xmits; 66503831d35Sstevel char *name; 66603831d35Sstevel 66703831d35Sstevel ASSERT(dip != NULL); 66803831d35Sstevel /* 66903831d35Sstevel * Verify if the parent is schizo(xmits)0 and pci B leaf. 67003831d35Sstevel */ 67103831d35Sstevel if (((parent_dip = ddi_get_parent(dip)) == NULL) || 67203831d35Sstevel ((name = ddi_binding_name(parent_dip)) == NULL)) 67303831d35Sstevel return (FALSE); 67403831d35Sstevel if (strcmp(name, MAN_SCHIZO_BINDING_NAME) != 0) { 67503831d35Sstevel /* 67603831d35Sstevel * This RIO could be on XMITS, so get the dip to 67703831d35Sstevel * XMITS PCI Leaf. 67803831d35Sstevel */ 67903831d35Sstevel if ((parent_dip = ddi_get_parent(parent_dip)) == NULL) 68003831d35Sstevel return (FALSE); 68103831d35Sstevel if (((name = ddi_binding_name(parent_dip)) == NULL) || 68203831d35Sstevel (strcmp(name, MAN_XMITS_BINDING_NAME) != 0)) { 68303831d35Sstevel return (FALSE); 68403831d35Sstevel } 68503831d35Sstevel } 68603831d35Sstevel if (man_dip_is_schizoxmits0_pcib(parent_dip, &exp_id, &xmits) == FALSE) 68703831d35Sstevel return (FALSE); 68803831d35Sstevel 68903831d35Sstevel /* 69003831d35Sstevel * Make sure it is attached. 69103831d35Sstevel */ 69203831d35Sstevel if (man_dip_is_attached(dip) == FALSE) { 69303831d35Sstevel MAN_DBG(MAN_DR, ("man_get_eri_dev_info: " 694*07d06da5SSurya Prakki "dip 0x%p not attached\n", (void *)dip)); 69503831d35Sstevel return (FALSE); 69603831d35Sstevel } 69703831d35Sstevel mdevp->mdev_exp_id = exp_id; 69803831d35Sstevel mdevp->mdev_ppa = ddi_get_instance(dip); 6995c066ec2SJerry Gilliam mdevp->mdev_major = ddi_driver_major(dip); 70003831d35Sstevel mdevp->mdev_state = MDEV_ASSIGNED; 70103831d35Sstevel return (TRUE); 70203831d35Sstevel } 70303831d35Sstevel 70403831d35Sstevel /* 70503831d35Sstevel * MAN RIO is connected to SCHIZO/XMITS 0 and PCI_B Leaf. 70603831d35Sstevel * Incase of XMITS, it is actually connected to a PCI Bridge(21154) 70703831d35Sstevel * which is directly connected to the PCI_B leaf of XMITS0. 70803831d35Sstevel * 70903831d35Sstevel * This function verifies if the given dip is SCHIZO/XMITS 0 and 71003831d35Sstevel * PCI_B Leaf. This is done as follows: 71103831d35Sstevel * 71203831d35Sstevel * - Check the binding name to verify SCHIZO/XMITS. 71303831d35Sstevel * - Verify the Device type to be "pci". 71403831d35Sstevel * - Verify the PortID to be ending with 0x1C 71503831d35Sstevel * - Verify the the CSR base to be 0x70.0000. 71603831d35Sstevel */ 71703831d35Sstevel static int 71803831d35Sstevel man_dip_is_schizoxmits0_pcib(dev_info_t *dip, int *exp_id, int *xmits) 71903831d35Sstevel { 72003831d35Sstevel char dtype[MAN_DDI_BUFLEN]; 72103831d35Sstevel int portid; 72203831d35Sstevel uint_t pci_csr_base; 72303831d35Sstevel struct pci_phys_spec *regbuf = NULL; 72403831d35Sstevel int length = MAN_DDI_BUFLEN; 72503831d35Sstevel char *name; 72603831d35Sstevel 72703831d35Sstevel ASSERT(dip != NULL); 72803831d35Sstevel *exp_id = -1; 72903831d35Sstevel if ((name = ddi_binding_name(dip)) == NULL) 73003831d35Sstevel return (FALSE); 73103831d35Sstevel if (strcmp(name, MAN_SCHIZO_BINDING_NAME) == 0) { 73203831d35Sstevel MAN_DBG(MAN_PATH, ("man_dip_is_schizoxmits0_pcib: " 733*07d06da5SSurya Prakki "SCHIZO found 0x%p\n", (void *)dip)); 73403831d35Sstevel } else if (strcmp(name, MAN_XMITS_BINDING_NAME) == 0) { 73503831d35Sstevel *xmits = TRUE; 73603831d35Sstevel MAN_DBG(MAN_PATH, ("man_dip_is_schizoxmits0_pcib: " 737*07d06da5SSurya Prakki "XMITS found 0x%p\n", (void *)dip)); 73803831d35Sstevel } else 73903831d35Sstevel return (FALSE); 74003831d35Sstevel if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, 0, MAN_DEVTYPE_PROP, 74103831d35Sstevel (caddr_t)dtype, &length) == DDI_PROP_SUCCESS) { 74203831d35Sstevel 74303831d35Sstevel MAN_DBG(MAN_PATH, ("dtype: %s\n", dtype)); 74403831d35Sstevel if (strncmp(dtype, MAN_DEVTYPE_PCI, 3) != 0) 74503831d35Sstevel goto notfound; 74603831d35Sstevel 74703831d35Sstevel /* 74803831d35Sstevel * Get safari ID (DDI port ID). 74903831d35Sstevel */ 75003831d35Sstevel if ((portid = (int)ddi_getprop(DDI_DEV_T_ANY, dip, 0, 75103831d35Sstevel MAN_PORTID_PROP, -1)) == -1) { 75203831d35Sstevel 75303831d35Sstevel MAN_DBG(MAN_PATH, ("ddi_getpropp: failed\n")); 75403831d35Sstevel goto notfound; 75503831d35Sstevel } 75603831d35Sstevel 75703831d35Sstevel /* 75803831d35Sstevel * All schizo 0 safari IDs end in 0x1C. 75903831d35Sstevel */ 76003831d35Sstevel if ((portid & MAN_SCHIZO_MASK) != MAN_SCHIZO_0_ID) 76103831d35Sstevel goto notfound; 76203831d35Sstevel 76303831d35Sstevel /* 76403831d35Sstevel * All PCI nodes "B" are at configspace 0x70.0000 76503831d35Sstevel */ 76603831d35Sstevel if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 76703831d35Sstevel MAN_REG_PROP, (caddr_t)®buf, 76803831d35Sstevel &length) != DDI_PROP_SUCCESS) { 76903831d35Sstevel 77003831d35Sstevel MAN_DBG(MAN_PATH, ("ddi_getlongprop_buf: failed")); 77103831d35Sstevel goto notfound; 77203831d35Sstevel } 77303831d35Sstevel 77403831d35Sstevel pci_csr_base = regbuf[0].pci_phys_mid & PCI_CONF_ADDR_MASK; 77503831d35Sstevel kmem_free(regbuf, length); 77603831d35Sstevel if (pci_csr_base == MAN_PCI_B_CSR_BASE) { 77703831d35Sstevel 77803831d35Sstevel MAN_DBG(MAN_PATH, ("man_dip_is_schizoxmits0_pcib:" 77903831d35Sstevel " found PCI B at dip(0x%p)\n", (void *)dip)); 78003831d35Sstevel 78103831d35Sstevel *exp_id = portid >> 5; 78203831d35Sstevel return (TRUE); 78303831d35Sstevel } 78403831d35Sstevel } 78503831d35Sstevel 78603831d35Sstevel notfound: 78703831d35Sstevel return (FALSE); 78803831d35Sstevel } 78903831d35Sstevel 79003831d35Sstevel static int 79103831d35Sstevel man_dip_is_eri(dev_info_t *dip, man_dev_t *ndevp) 79203831d35Sstevel { 79303831d35Sstevel struct pci_phys_spec *regbuf = NULL; 79403831d35Sstevel int length = 0; 79503831d35Sstevel uint_t pci_device; 79603831d35Sstevel uint_t pci_function; 79703831d35Sstevel 79803831d35Sstevel MAN_DBG(MAN_PATH, ("man_dip_is_eri: dip(0x%p) ndevp(0x%p)\n", 79903831d35Sstevel (void *)dip, (void *)ndevp)); 80003831d35Sstevel if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 80103831d35Sstevel MAN_REG_PROP, (caddr_t)®buf, 80203831d35Sstevel &length) == DDI_PROP_SUCCESS) { 80303831d35Sstevel 80403831d35Sstevel pci_device = PCI_REG_DEV_G(regbuf->pci_phys_hi); 80503831d35Sstevel pci_function = PCI_REG_FUNC_G(regbuf->pci_phys_hi); 80603831d35Sstevel kmem_free(regbuf, length); 80703831d35Sstevel 80803831d35Sstevel /* 80903831d35Sstevel * The network function of the RIO ASIC will always 81003831d35Sstevel * be device 3 and function 1 ("network@3,1"). 81103831d35Sstevel */ 81203831d35Sstevel if (pci_device == 3 && pci_function == 1) { 81303831d35Sstevel ndevp->mdev_ppa = ddi_get_instance(dip); 8145c066ec2SJerry Gilliam ndevp->mdev_major = ddi_driver_major(dip); 81503831d35Sstevel 81603831d35Sstevel MAN_DBG(MAN_PATH, ("man_dip_is_eri: found eri maj(%d)" 81703831d35Sstevel " ppa(%d)\n", ndevp->mdev_major, ndevp->mdev_ppa)); 81803831d35Sstevel 81903831d35Sstevel return (TRUE); 82003831d35Sstevel } 82103831d35Sstevel } 82203831d35Sstevel 82303831d35Sstevel MAN_DBG(MAN_PATH, ("man_dip_is_eri: returns FALSE\n")); 82403831d35Sstevel 82503831d35Sstevel return (FALSE); 82603831d35Sstevel } 82703831d35Sstevel 82803831d35Sstevel static int 82903831d35Sstevel man_dip_is_attached(dev_info_t *dip) 83003831d35Sstevel { 83103831d35Sstevel int state; 83203831d35Sstevel 83303831d35Sstevel state = ddi_get_devstate(dip); 83403831d35Sstevel if (i_ddi_devi_attached(dip) || (state == DDI_DEVSTATE_UP)) { 83503831d35Sstevel /* 83603831d35Sstevel * The instance info is more important for us, 83703831d35Sstevel * so verify. 83803831d35Sstevel */ 83903831d35Sstevel if (ddi_get_instance(dip) >= 0) { 84003831d35Sstevel return (TRUE); 84103831d35Sstevel } 84203831d35Sstevel cmn_err(CE_WARN, "man_dip_is_attached: " 843*07d06da5SSurya Prakki "eri 0x%p instance is not set yet", (void *)dip); 84403831d35Sstevel 84503831d35Sstevel } 84603831d35Sstevel return (FALSE); 84703831d35Sstevel } 84803831d35Sstevel 84903831d35Sstevel #if defined(DEBUG) 85003831d35Sstevel static void 85103831d35Sstevel man_print_manc(manc_t *mcp) 85203831d35Sstevel { 85303831d35Sstevel cmn_err(CE_CONT, "\tmcp(0x%p)\n\n", (void *)mcp); 85403831d35Sstevel 85503831d35Sstevel if (mcp == NULL) 85603831d35Sstevel return; 85703831d35Sstevel 85803831d35Sstevel cmn_err(CE_CONT, "\tmagic: 0x%x\n", mcp->manc_magic); 85903831d35Sstevel cmn_err(CE_CONT, "\tversion: 0x%x\n", mcp->manc_version); 86003831d35Sstevel cmn_err(CE_CONT, "\tcsum: %d\n", mcp->manc_csum); 86103831d35Sstevel cmn_err(CE_CONT, "\tdom_eaddr: %s\n", 86203831d35Sstevel ether_sprintf(&mcp->manc_dom_eaddr)); 86303831d35Sstevel cmn_err(CE_CONT, "\tsc_eaddr: %s\n", 86403831d35Sstevel ether_sprintf(&mcp->manc_sc_eaddr)); 86503831d35Sstevel cmn_err(CE_CONT, "\tiob_bitmap: 0x%x\n", mcp->manc_iob_bitmap); 86603831d35Sstevel cmn_err(CE_CONT, "\tgolden_iob: %d\n", mcp->manc_golden_iob); 86703831d35Sstevel 86803831d35Sstevel } 86903831d35Sstevel 87003831d35Sstevel #endif /* DEBUG */ 871