1*0Sstevel@tonic-gate /* 2*0Sstevel@tonic-gate * CDDL HEADER START 3*0Sstevel@tonic-gate * 4*0Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*0Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*0Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*0Sstevel@tonic-gate * with the License. 8*0Sstevel@tonic-gate * 9*0Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*0Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*0Sstevel@tonic-gate * See the License for the specific language governing permissions 12*0Sstevel@tonic-gate * and limitations under the License. 13*0Sstevel@tonic-gate * 14*0Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*0Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*0Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*0Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*0Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*0Sstevel@tonic-gate * 20*0Sstevel@tonic-gate * CDDL HEADER END 21*0Sstevel@tonic-gate */ 22*0Sstevel@tonic-gate /* 23*0Sstevel@tonic-gate * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24*0Sstevel@tonic-gate * Use is subject to license terms. 25*0Sstevel@tonic-gate */ 26*0Sstevel@tonic-gate 27*0Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 28*0Sstevel@tonic-gate 29*0Sstevel@tonic-gate #include <sys/types.h> 30*0Sstevel@tonic-gate #include <sys/conf.h> 31*0Sstevel@tonic-gate #include <sys/ddi.h> 32*0Sstevel@tonic-gate #include <sys/sunddi.h> 33*0Sstevel@tonic-gate #include <sys/ddi_impldefs.h> 34*0Sstevel@tonic-gate #include <sys/ddi_subrdefs.h> 35*0Sstevel@tonic-gate #include <sys/pci.h> 36*0Sstevel@tonic-gate #include <sys/autoconf.h> 37*0Sstevel@tonic-gate #include <sys/cmn_err.h> 38*0Sstevel@tonic-gate #include <sys/errno.h> 39*0Sstevel@tonic-gate #include <sys/kmem.h> 40*0Sstevel@tonic-gate #include <sys/debug.h> 41*0Sstevel@tonic-gate #include <sys/sysmacros.h> 42*0Sstevel@tonic-gate #include <sys/ebus.h> 43*0Sstevel@tonic-gate #include <sys/open.h> 44*0Sstevel@tonic-gate #include <sys/stat.h> 45*0Sstevel@tonic-gate #include <sys/file.h> 46*0Sstevel@tonic-gate #include <sys/sunndi.h> 47*0Sstevel@tonic-gate 48*0Sstevel@tonic-gate #ifdef DEBUG 49*0Sstevel@tonic-gate uint64_t ebus_debug_flags = 0; 50*0Sstevel@tonic-gate #endif 51*0Sstevel@tonic-gate 52*0Sstevel@tonic-gate /* 53*0Sstevel@tonic-gate * The values of the following variables are used to initialize 54*0Sstevel@tonic-gate * the cache line size and latency timer registers in the ebus 55*0Sstevel@tonic-gate * configuration header. Variables are used instead of constants 56*0Sstevel@tonic-gate * to allow tuning from the /etc/system file. 57*0Sstevel@tonic-gate */ 58*0Sstevel@tonic-gate static uint8_t ebus_cache_line_size = 0x10; /* 64 bytes */ 59*0Sstevel@tonic-gate static uint8_t ebus_latency_timer = 0x40; /* 64 PCI cycles */ 60*0Sstevel@tonic-gate 61*0Sstevel@tonic-gate /* 62*0Sstevel@tonic-gate * function prototypes for bus ops routines: 63*0Sstevel@tonic-gate */ 64*0Sstevel@tonic-gate static int 65*0Sstevel@tonic-gate ebus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp, 66*0Sstevel@tonic-gate off_t offset, off_t len, caddr_t *addrp); 67*0Sstevel@tonic-gate static int 68*0Sstevel@tonic-gate ebus_ctlops(dev_info_t *dip, dev_info_t *rdip, 69*0Sstevel@tonic-gate ddi_ctl_enum_t op, void *arg, void *result); 70*0Sstevel@tonic-gate static int 71*0Sstevel@tonic-gate ebus_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op, 72*0Sstevel@tonic-gate ddi_intr_handle_impl_t *hdlp, void *result); 73*0Sstevel@tonic-gate 74*0Sstevel@tonic-gate /* 75*0Sstevel@tonic-gate * function prototypes for dev ops routines: 76*0Sstevel@tonic-gate */ 77*0Sstevel@tonic-gate static int ebus_attach(dev_info_t *dip, ddi_attach_cmd_t cmd); 78*0Sstevel@tonic-gate static int ebus_detach(dev_info_t *dip, ddi_detach_cmd_t cmd); 79*0Sstevel@tonic-gate static int ebus_info(dev_info_t *dip, ddi_info_cmd_t infocmd, 80*0Sstevel@tonic-gate void *arg, void **result); 81*0Sstevel@tonic-gate 82*0Sstevel@tonic-gate /* 83*0Sstevel@tonic-gate * general function prototypes: 84*0Sstevel@tonic-gate */ 85*0Sstevel@tonic-gate static int ebus_config(ebus_devstate_t *ebus_p); 86*0Sstevel@tonic-gate static int ebus_apply_range(ebus_devstate_t *ebus_p, dev_info_t *rdip, 87*0Sstevel@tonic-gate ebus_regspec_t *ebus_rp, pci_regspec_t *rp); 88*0Sstevel@tonic-gate static int febus_apply_range(ebus_devstate_t *ebus_p, dev_info_t *rdip, 89*0Sstevel@tonic-gate ebus_regspec_t *ebus_rp, struct regspec *rp); 90*0Sstevel@tonic-gate int get_ranges_prop(ebus_devstate_t *ebus_p); 91*0Sstevel@tonic-gate 92*0Sstevel@tonic-gate #define getprop(dip, name, addr, intp) \ 93*0Sstevel@tonic-gate ddi_getlongprop(DDI_DEV_T_NONE, (dip), DDI_PROP_DONTPASS, \ 94*0Sstevel@tonic-gate (name), (caddr_t)(addr), (intp)) 95*0Sstevel@tonic-gate 96*0Sstevel@tonic-gate static int ebus_open(dev_t *devp, int flags, int otyp, cred_t *credp); 97*0Sstevel@tonic-gate static int ebus_close(dev_t dev, int flags, int otyp, cred_t *credp); 98*0Sstevel@tonic-gate static int ebus_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, 99*0Sstevel@tonic-gate cred_t *credp, int *rvalp); 100*0Sstevel@tonic-gate struct cb_ops ebus_cb_ops = { 101*0Sstevel@tonic-gate ebus_open, /* open */ 102*0Sstevel@tonic-gate ebus_close, /* close */ 103*0Sstevel@tonic-gate nodev, /* strategy */ 104*0Sstevel@tonic-gate nodev, /* print */ 105*0Sstevel@tonic-gate nodev, /* dump */ 106*0Sstevel@tonic-gate nodev, /* read */ 107*0Sstevel@tonic-gate nodev, /* write */ 108*0Sstevel@tonic-gate ebus_ioctl, /* ioctl */ 109*0Sstevel@tonic-gate nodev, /* devmap */ 110*0Sstevel@tonic-gate nodev, /* mmap */ 111*0Sstevel@tonic-gate nodev, /* segmap */ 112*0Sstevel@tonic-gate nochpoll, /* poll */ 113*0Sstevel@tonic-gate ddi_prop_op, /* cb_prop_op */ 114*0Sstevel@tonic-gate NULL, /* streamtab */ 115*0Sstevel@tonic-gate D_NEW | D_MP | D_HOTPLUG, /* Driver compatibility flag */ 116*0Sstevel@tonic-gate CB_REV, /* rev */ 117*0Sstevel@tonic-gate nodev, /* int (*cb_aread)() */ 118*0Sstevel@tonic-gate nodev /* int (*cb_awrite)() */ 119*0Sstevel@tonic-gate }; 120*0Sstevel@tonic-gate 121*0Sstevel@tonic-gate /* 122*0Sstevel@tonic-gate * bus ops and dev ops structures: 123*0Sstevel@tonic-gate */ 124*0Sstevel@tonic-gate static struct bus_ops ebus_bus_ops = { 125*0Sstevel@tonic-gate BUSO_REV, 126*0Sstevel@tonic-gate ebus_map, 127*0Sstevel@tonic-gate NULL, 128*0Sstevel@tonic-gate NULL, 129*0Sstevel@tonic-gate NULL, 130*0Sstevel@tonic-gate i_ddi_map_fault, 131*0Sstevel@tonic-gate ddi_dma_map, 132*0Sstevel@tonic-gate ddi_dma_allochdl, 133*0Sstevel@tonic-gate ddi_dma_freehdl, 134*0Sstevel@tonic-gate ddi_dma_bindhdl, 135*0Sstevel@tonic-gate ddi_dma_unbindhdl, 136*0Sstevel@tonic-gate ddi_dma_flush, 137*0Sstevel@tonic-gate ddi_dma_win, 138*0Sstevel@tonic-gate ddi_dma_mctl, 139*0Sstevel@tonic-gate ebus_ctlops, 140*0Sstevel@tonic-gate ddi_bus_prop_op, 141*0Sstevel@tonic-gate ndi_busop_get_eventcookie, 142*0Sstevel@tonic-gate ndi_busop_add_eventcall, 143*0Sstevel@tonic-gate ndi_busop_remove_eventcall, 144*0Sstevel@tonic-gate ndi_post_event, 145*0Sstevel@tonic-gate 0, 146*0Sstevel@tonic-gate 0, 147*0Sstevel@tonic-gate 0, 148*0Sstevel@tonic-gate 0, 149*0Sstevel@tonic-gate 0, 150*0Sstevel@tonic-gate 0, 151*0Sstevel@tonic-gate 0, 152*0Sstevel@tonic-gate 0, 153*0Sstevel@tonic-gate ebus_intr_ops 154*0Sstevel@tonic-gate }; 155*0Sstevel@tonic-gate 156*0Sstevel@tonic-gate static struct dev_ops ebus_ops = { 157*0Sstevel@tonic-gate DEVO_REV, 158*0Sstevel@tonic-gate 0, 159*0Sstevel@tonic-gate ebus_info, 160*0Sstevel@tonic-gate nulldev, 161*0Sstevel@tonic-gate nulldev, 162*0Sstevel@tonic-gate ebus_attach, 163*0Sstevel@tonic-gate ebus_detach, 164*0Sstevel@tonic-gate nodev, 165*0Sstevel@tonic-gate &ebus_cb_ops, 166*0Sstevel@tonic-gate &ebus_bus_ops 167*0Sstevel@tonic-gate }; 168*0Sstevel@tonic-gate 169*0Sstevel@tonic-gate /* 170*0Sstevel@tonic-gate * module definitions: 171*0Sstevel@tonic-gate */ 172*0Sstevel@tonic-gate #include <sys/modctl.h> 173*0Sstevel@tonic-gate extern struct mod_ops mod_driverops; 174*0Sstevel@tonic-gate 175*0Sstevel@tonic-gate static struct modldrv modldrv = { 176*0Sstevel@tonic-gate &mod_driverops, /* Type of module. This one is a driver */ 177*0Sstevel@tonic-gate "ebus nexus driver %I%", /* Name of module. */ 178*0Sstevel@tonic-gate &ebus_ops, /* driver ops */ 179*0Sstevel@tonic-gate }; 180*0Sstevel@tonic-gate 181*0Sstevel@tonic-gate static struct modlinkage modlinkage = { 182*0Sstevel@tonic-gate MODREV_1, (void *)&modldrv, NULL 183*0Sstevel@tonic-gate }; 184*0Sstevel@tonic-gate 185*0Sstevel@tonic-gate /* 186*0Sstevel@tonic-gate * driver global data: 187*0Sstevel@tonic-gate */ 188*0Sstevel@tonic-gate static void *per_ebus_state; /* per-ebus soft state pointer */ 189*0Sstevel@tonic-gate 190*0Sstevel@tonic-gate 191*0Sstevel@tonic-gate int 192*0Sstevel@tonic-gate _init(void) 193*0Sstevel@tonic-gate { 194*0Sstevel@tonic-gate int e; 195*0Sstevel@tonic-gate 196*0Sstevel@tonic-gate /* 197*0Sstevel@tonic-gate * Initialize per-ebus soft state pointer. 198*0Sstevel@tonic-gate */ 199*0Sstevel@tonic-gate e = ddi_soft_state_init(&per_ebus_state, sizeof (ebus_devstate_t), 1); 200*0Sstevel@tonic-gate if (e != 0) 201*0Sstevel@tonic-gate return (e); 202*0Sstevel@tonic-gate 203*0Sstevel@tonic-gate /* 204*0Sstevel@tonic-gate * Install the module. 205*0Sstevel@tonic-gate */ 206*0Sstevel@tonic-gate e = mod_install(&modlinkage); 207*0Sstevel@tonic-gate if (e != 0) 208*0Sstevel@tonic-gate ddi_soft_state_fini(&per_ebus_state); 209*0Sstevel@tonic-gate return (e); 210*0Sstevel@tonic-gate } 211*0Sstevel@tonic-gate 212*0Sstevel@tonic-gate int 213*0Sstevel@tonic-gate _fini(void) 214*0Sstevel@tonic-gate { 215*0Sstevel@tonic-gate int e; 216*0Sstevel@tonic-gate 217*0Sstevel@tonic-gate /* 218*0Sstevel@tonic-gate * Remove the module. 219*0Sstevel@tonic-gate */ 220*0Sstevel@tonic-gate e = mod_remove(&modlinkage); 221*0Sstevel@tonic-gate if (e != 0) 222*0Sstevel@tonic-gate return (e); 223*0Sstevel@tonic-gate 224*0Sstevel@tonic-gate /* 225*0Sstevel@tonic-gate * Free the soft state info. 226*0Sstevel@tonic-gate */ 227*0Sstevel@tonic-gate ddi_soft_state_fini(&per_ebus_state); 228*0Sstevel@tonic-gate return (e); 229*0Sstevel@tonic-gate } 230*0Sstevel@tonic-gate 231*0Sstevel@tonic-gate int 232*0Sstevel@tonic-gate _info(struct modinfo *modinfop) 233*0Sstevel@tonic-gate { 234*0Sstevel@tonic-gate return (mod_info(&modlinkage, modinfop)); 235*0Sstevel@tonic-gate } 236*0Sstevel@tonic-gate 237*0Sstevel@tonic-gate /* device driver entry points */ 238*0Sstevel@tonic-gate 239*0Sstevel@tonic-gate /*ARGSUSED*/ 240*0Sstevel@tonic-gate static int 241*0Sstevel@tonic-gate ebus_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 242*0Sstevel@tonic-gate { 243*0Sstevel@tonic-gate ebus_devstate_t *ebus_p; /* per ebus state pointer */ 244*0Sstevel@tonic-gate int instance; 245*0Sstevel@tonic-gate 246*0Sstevel@tonic-gate instance = getminor((dev_t)arg); 247*0Sstevel@tonic-gate ebus_p = get_ebus_soft_state(instance); 248*0Sstevel@tonic-gate 249*0Sstevel@tonic-gate switch (infocmd) { 250*0Sstevel@tonic-gate default: 251*0Sstevel@tonic-gate return (DDI_FAILURE); 252*0Sstevel@tonic-gate 253*0Sstevel@tonic-gate case DDI_INFO_DEVT2INSTANCE: 254*0Sstevel@tonic-gate *result = (void *)instance; 255*0Sstevel@tonic-gate return (DDI_SUCCESS); 256*0Sstevel@tonic-gate 257*0Sstevel@tonic-gate case DDI_INFO_DEVT2DEVINFO: 258*0Sstevel@tonic-gate if (ebus_p == NULL) 259*0Sstevel@tonic-gate return (DDI_FAILURE); 260*0Sstevel@tonic-gate *result = (void *)ebus_p->dip; 261*0Sstevel@tonic-gate return (DDI_SUCCESS); 262*0Sstevel@tonic-gate } 263*0Sstevel@tonic-gate } 264*0Sstevel@tonic-gate 265*0Sstevel@tonic-gate /* 266*0Sstevel@tonic-gate * attach entry point: 267*0Sstevel@tonic-gate * 268*0Sstevel@tonic-gate * normal attach: 269*0Sstevel@tonic-gate * 270*0Sstevel@tonic-gate * create soft state structure (dip, reg, nreg and state fields) 271*0Sstevel@tonic-gate * map in configuration header 272*0Sstevel@tonic-gate * make sure device is properly configured 273*0Sstevel@tonic-gate * report device 274*0Sstevel@tonic-gate */ 275*0Sstevel@tonic-gate static int 276*0Sstevel@tonic-gate ebus_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 277*0Sstevel@tonic-gate { 278*0Sstevel@tonic-gate ebus_devstate_t *ebus_p; /* per ebus state pointer */ 279*0Sstevel@tonic-gate int instance; 280*0Sstevel@tonic-gate 281*0Sstevel@tonic-gate DBG1(D_ATTACH, NULL, "dip=%p\n", dip); 282*0Sstevel@tonic-gate 283*0Sstevel@tonic-gate switch (cmd) { 284*0Sstevel@tonic-gate case DDI_ATTACH: 285*0Sstevel@tonic-gate 286*0Sstevel@tonic-gate /* 287*0Sstevel@tonic-gate * Allocate soft state for this instance. 288*0Sstevel@tonic-gate */ 289*0Sstevel@tonic-gate instance = ddi_get_instance(dip); 290*0Sstevel@tonic-gate if (ddi_soft_state_zalloc(per_ebus_state, instance) 291*0Sstevel@tonic-gate != DDI_SUCCESS) { 292*0Sstevel@tonic-gate DBG(D_ATTACH, NULL, "failed to alloc soft state\n"); 293*0Sstevel@tonic-gate return (DDI_FAILURE); 294*0Sstevel@tonic-gate } 295*0Sstevel@tonic-gate ebus_p = get_ebus_soft_state(instance); 296*0Sstevel@tonic-gate ebus_p->dip = dip; 297*0Sstevel@tonic-gate mutex_init(&ebus_p->ebus_mutex, NULL, MUTEX_DRIVER, NULL); 298*0Sstevel@tonic-gate ebus_p->ebus_soft_state = EBUS_SOFT_STATE_CLOSED; 299*0Sstevel@tonic-gate 300*0Sstevel@tonic-gate /* Set ebus type field based on ddi name info */ 301*0Sstevel@tonic-gate if (strcmp(ddi_get_name(dip), "jbus-ebus") == 0) { 302*0Sstevel@tonic-gate ebus_p->type = FEBUS_TYPE; 303*0Sstevel@tonic-gate } else { 304*0Sstevel@tonic-gate ebus_p->type = EBUS_TYPE; 305*0Sstevel@tonic-gate } 306*0Sstevel@tonic-gate 307*0Sstevel@tonic-gate (void) ddi_prop_create(DDI_DEV_T_NONE, dip, 308*0Sstevel@tonic-gate DDI_PROP_CANSLEEP, "no-dma-interrupt-sync", NULL, 0); 309*0Sstevel@tonic-gate /* Get our ranges property for mapping child registers. */ 310*0Sstevel@tonic-gate if (get_ranges_prop(ebus_p) != DDI_SUCCESS) { 311*0Sstevel@tonic-gate mutex_destroy(&ebus_p->ebus_mutex); 312*0Sstevel@tonic-gate free_ebus_soft_state(instance); 313*0Sstevel@tonic-gate return (DDI_FAILURE); 314*0Sstevel@tonic-gate } 315*0Sstevel@tonic-gate 316*0Sstevel@tonic-gate /* 317*0Sstevel@tonic-gate * create minor node for devctl interfaces 318*0Sstevel@tonic-gate */ 319*0Sstevel@tonic-gate if (ddi_create_minor_node(dip, "devctl", S_IFCHR, instance, 320*0Sstevel@tonic-gate DDI_NT_NEXUS, 0) != DDI_SUCCESS) { 321*0Sstevel@tonic-gate mutex_destroy(&ebus_p->ebus_mutex); 322*0Sstevel@tonic-gate free_ebus_soft_state(instance); 323*0Sstevel@tonic-gate return (DDI_FAILURE); 324*0Sstevel@tonic-gate } 325*0Sstevel@tonic-gate /* 326*0Sstevel@tonic-gate * Make sure the master enable and memory access enable 327*0Sstevel@tonic-gate * bits are set in the config command register. 328*0Sstevel@tonic-gate */ 329*0Sstevel@tonic-gate if (ebus_p->type == EBUS_TYPE) { 330*0Sstevel@tonic-gate if (!ebus_config(ebus_p)) { 331*0Sstevel@tonic-gate ddi_remove_minor_node(dip, "devctl"); 332*0Sstevel@tonic-gate mutex_destroy(&ebus_p->ebus_mutex); 333*0Sstevel@tonic-gate free_ebus_soft_state(instance); 334*0Sstevel@tonic-gate return (DDI_FAILURE); 335*0Sstevel@tonic-gate } 336*0Sstevel@tonic-gate } 337*0Sstevel@tonic-gate 338*0Sstevel@tonic-gate /* 339*0Sstevel@tonic-gate * Make the pci_report_pmcap() call only for RIO 340*0Sstevel@tonic-gate * implementations. 341*0Sstevel@tonic-gate */ 342*0Sstevel@tonic-gate if (IS_RIO(dip)) { 343*0Sstevel@tonic-gate (void) pci_report_pmcap(dip, PCI_PM_IDLESPEED, 344*0Sstevel@tonic-gate (void *)EBUS_4MHZ); 345*0Sstevel@tonic-gate } 346*0Sstevel@tonic-gate 347*0Sstevel@tonic-gate /* 348*0Sstevel@tonic-gate * Make the state as attached and report the device. 349*0Sstevel@tonic-gate */ 350*0Sstevel@tonic-gate ebus_p->state = ATTACHED; 351*0Sstevel@tonic-gate ddi_report_dev(dip); 352*0Sstevel@tonic-gate DBG(D_ATTACH, ebus_p, "returning\n"); 353*0Sstevel@tonic-gate 354*0Sstevel@tonic-gate return (DDI_SUCCESS); 355*0Sstevel@tonic-gate 356*0Sstevel@tonic-gate case DDI_RESUME: 357*0Sstevel@tonic-gate 358*0Sstevel@tonic-gate instance = ddi_get_instance(dip); 359*0Sstevel@tonic-gate ebus_p = get_ebus_soft_state(instance); 360*0Sstevel@tonic-gate 361*0Sstevel@tonic-gate /* 362*0Sstevel@tonic-gate * Make sure the master enable and memory access enable 363*0Sstevel@tonic-gate * bits are set in the config command register. 364*0Sstevel@tonic-gate */ 365*0Sstevel@tonic-gate if (ebus_p->type == EBUS_TYPE) { 366*0Sstevel@tonic-gate if (!ebus_config(ebus_p)) { 367*0Sstevel@tonic-gate free_ebus_soft_state(instance); 368*0Sstevel@tonic-gate return (DDI_FAILURE); 369*0Sstevel@tonic-gate } 370*0Sstevel@tonic-gate } 371*0Sstevel@tonic-gate 372*0Sstevel@tonic-gate ebus_p->state = RESUMED; 373*0Sstevel@tonic-gate return (DDI_SUCCESS); 374*0Sstevel@tonic-gate } 375*0Sstevel@tonic-gate return (DDI_FAILURE); 376*0Sstevel@tonic-gate } 377*0Sstevel@tonic-gate 378*0Sstevel@tonic-gate /* 379*0Sstevel@tonic-gate * detach entry point: 380*0Sstevel@tonic-gate */ 381*0Sstevel@tonic-gate static int 382*0Sstevel@tonic-gate ebus_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 383*0Sstevel@tonic-gate { 384*0Sstevel@tonic-gate int instance = ddi_get_instance(dip); 385*0Sstevel@tonic-gate ebus_devstate_t *ebus_p = get_ebus_soft_state(instance); 386*0Sstevel@tonic-gate 387*0Sstevel@tonic-gate switch (cmd) { 388*0Sstevel@tonic-gate case DDI_DETACH: 389*0Sstevel@tonic-gate DBG1(D_DETACH, ebus_p, "DDI_DETACH dip=%p\n", dip); 390*0Sstevel@tonic-gate 391*0Sstevel@tonic-gate switch (ebus_p->type) { 392*0Sstevel@tonic-gate case EBUS_TYPE: 393*0Sstevel@tonic-gate kmem_free(ebus_p->rangespec.rangep, ebus_p->range_cnt * 394*0Sstevel@tonic-gate sizeof (struct ebus_pci_rangespec)); 395*0Sstevel@tonic-gate break; 396*0Sstevel@tonic-gate case FEBUS_TYPE: 397*0Sstevel@tonic-gate kmem_free(ebus_p->rangespec.ferangep, 398*0Sstevel@tonic-gate ebus_p->range_cnt * 399*0Sstevel@tonic-gate sizeof (struct febus_rangespec)); 400*0Sstevel@tonic-gate break; 401*0Sstevel@tonic-gate default: 402*0Sstevel@tonic-gate DBG(D_ATTACH, NULL, "failed to recognize ebus type\n"); 403*0Sstevel@tonic-gate return (DDI_FAILURE); 404*0Sstevel@tonic-gate } 405*0Sstevel@tonic-gate 406*0Sstevel@tonic-gate ddi_remove_minor_node(dip, "devctl"); 407*0Sstevel@tonic-gate mutex_destroy(&ebus_p->ebus_mutex); 408*0Sstevel@tonic-gate free_ebus_soft_state(instance); 409*0Sstevel@tonic-gate return (DDI_SUCCESS); 410*0Sstevel@tonic-gate 411*0Sstevel@tonic-gate case DDI_SUSPEND: 412*0Sstevel@tonic-gate DBG1(D_DETACH, ebus_p, "DDI_SUSPEND dip=%p\n", dip); 413*0Sstevel@tonic-gate ebus_p->state = SUSPENDED; 414*0Sstevel@tonic-gate return (DDI_SUCCESS); 415*0Sstevel@tonic-gate } 416*0Sstevel@tonic-gate DBG(D_ATTACH, NULL, "failed to recognize ebus detach command\n"); 417*0Sstevel@tonic-gate return (DDI_FAILURE); 418*0Sstevel@tonic-gate } 419*0Sstevel@tonic-gate 420*0Sstevel@tonic-gate 421*0Sstevel@tonic-gate int 422*0Sstevel@tonic-gate get_ranges_prop(ebus_devstate_t *ebus_p) 423*0Sstevel@tonic-gate { 424*0Sstevel@tonic-gate int nrange, range_len; 425*0Sstevel@tonic-gate struct ebus_pci_rangespec *rangep; 426*0Sstevel@tonic-gate struct febus_rangespec *ferangep; 427*0Sstevel@tonic-gate 428*0Sstevel@tonic-gate switch (ebus_p->type) { 429*0Sstevel@tonic-gate case EBUS_TYPE: 430*0Sstevel@tonic-gate if (ddi_getlongprop(DDI_DEV_T_ANY, 431*0Sstevel@tonic-gate ebus_p->dip, DDI_PROP_DONTPASS, 432*0Sstevel@tonic-gate "ranges", (caddr_t)&rangep, 433*0Sstevel@tonic-gate &range_len) != DDI_SUCCESS) { 434*0Sstevel@tonic-gate cmn_err(CE_WARN, "Can't get %s ranges property", 435*0Sstevel@tonic-gate ddi_get_name(ebus_p->dip)); 436*0Sstevel@tonic-gate return (DDI_ME_REGSPEC_RANGE); 437*0Sstevel@tonic-gate } 438*0Sstevel@tonic-gate 439*0Sstevel@tonic-gate nrange = range_len / sizeof (struct ebus_pci_rangespec); 440*0Sstevel@tonic-gate 441*0Sstevel@tonic-gate if (nrange == 0) { 442*0Sstevel@tonic-gate kmem_free(rangep, range_len); 443*0Sstevel@tonic-gate DBG(D_ATTACH, NULL, "range is equal to zero\n"); 444*0Sstevel@tonic-gate return (DDI_FAILURE); 445*0Sstevel@tonic-gate } 446*0Sstevel@tonic-gate 447*0Sstevel@tonic-gate #ifdef DEBUG 448*0Sstevel@tonic-gate { 449*0Sstevel@tonic-gate int i; 450*0Sstevel@tonic-gate 451*0Sstevel@tonic-gate for (i = 0; i < nrange; i++) { 452*0Sstevel@tonic-gate DBG5(D_MAP, ebus_p, 453*0Sstevel@tonic-gate "ebus range addr 0x%x.0x%x PCI range " 454*0Sstevel@tonic-gate "addr 0x%x.0x%x.0x%x ", 455*0Sstevel@tonic-gate rangep[i].ebus_phys_hi, 456*0Sstevel@tonic-gate rangep[i].ebus_phys_low, 457*0Sstevel@tonic-gate rangep[i].pci_phys_hi, 458*0Sstevel@tonic-gate rangep[i].pci_phys_mid, 459*0Sstevel@tonic-gate rangep[i].pci_phys_low); 460*0Sstevel@tonic-gate DBG1(D_MAP, ebus_p, 461*0Sstevel@tonic-gate "Size 0x%x\n", rangep[i].rng_size); 462*0Sstevel@tonic-gate } 463*0Sstevel@tonic-gate } 464*0Sstevel@tonic-gate #endif /* DEBUG */ 465*0Sstevel@tonic-gate 466*0Sstevel@tonic-gate ebus_p->rangespec.rangep = rangep; 467*0Sstevel@tonic-gate ebus_p->range_cnt = nrange; 468*0Sstevel@tonic-gate return (DDI_SUCCESS); 469*0Sstevel@tonic-gate 470*0Sstevel@tonic-gate case FEBUS_TYPE: 471*0Sstevel@tonic-gate if (ddi_getlongprop(DDI_DEV_T_ANY, ebus_p->dip, 472*0Sstevel@tonic-gate DDI_PROP_DONTPASS, "ranges", 473*0Sstevel@tonic-gate (caddr_t)&ferangep, &range_len) != DDI_SUCCESS) { 474*0Sstevel@tonic-gate cmn_err(CE_WARN, "Can't get %s ranges property", 475*0Sstevel@tonic-gate ddi_get_name(ebus_p->dip)); 476*0Sstevel@tonic-gate return (DDI_ME_REGSPEC_RANGE); 477*0Sstevel@tonic-gate } 478*0Sstevel@tonic-gate 479*0Sstevel@tonic-gate nrange = range_len / sizeof (struct febus_rangespec); 480*0Sstevel@tonic-gate 481*0Sstevel@tonic-gate if (nrange == 0) { 482*0Sstevel@tonic-gate kmem_free(ferangep, range_len); 483*0Sstevel@tonic-gate return (DDI_FAILURE); 484*0Sstevel@tonic-gate } 485*0Sstevel@tonic-gate 486*0Sstevel@tonic-gate #ifdef DEBUG 487*0Sstevel@tonic-gate { 488*0Sstevel@tonic-gate int i; 489*0Sstevel@tonic-gate 490*0Sstevel@tonic-gate for (i = 0; i < nrange; i++) { 491*0Sstevel@tonic-gate DBG4(D_MAP, ebus_p, 492*0Sstevel@tonic-gate "ebus range addr 0x%x.0x%x" 493*0Sstevel@tonic-gate " Parent range " 494*0Sstevel@tonic-gate "addr 0x%x.0x%x ", 495*0Sstevel@tonic-gate ferangep[i].febus_phys_hi, 496*0Sstevel@tonic-gate ferangep[i].febus_phys_low, 497*0Sstevel@tonic-gate ferangep[i].parent_phys_hi, 498*0Sstevel@tonic-gate ferangep[i].parent_phys_low); 499*0Sstevel@tonic-gate DBG1(D_MAP, ebus_p, "Size 0x%x\n", 500*0Sstevel@tonic-gate ferangep[i].rng_size); 501*0Sstevel@tonic-gate } 502*0Sstevel@tonic-gate } 503*0Sstevel@tonic-gate #endif /* DEBUG */ 504*0Sstevel@tonic-gate ebus_p->rangespec.ferangep = ferangep; 505*0Sstevel@tonic-gate ebus_p->range_cnt = nrange; 506*0Sstevel@tonic-gate return (DDI_SUCCESS); 507*0Sstevel@tonic-gate 508*0Sstevel@tonic-gate default: 509*0Sstevel@tonic-gate DBG(D_MAP, NULL, "failed to recognize ebus type\n"); 510*0Sstevel@tonic-gate return (DDI_FAILURE); 511*0Sstevel@tonic-gate } 512*0Sstevel@tonic-gate } 513*0Sstevel@tonic-gate 514*0Sstevel@tonic-gate /* bus driver entry points */ 515*0Sstevel@tonic-gate 516*0Sstevel@tonic-gate /* 517*0Sstevel@tonic-gate * bus map entry point: 518*0Sstevel@tonic-gate * 519*0Sstevel@tonic-gate * if map request is for an rnumber 520*0Sstevel@tonic-gate * get the corresponding regspec from device node 521*0Sstevel@tonic-gate * build a new regspec in our parent's format 522*0Sstevel@tonic-gate * build a new map_req with the new regspec 523*0Sstevel@tonic-gate * call up the tree to complete the mapping 524*0Sstevel@tonic-gate */ 525*0Sstevel@tonic-gate static int 526*0Sstevel@tonic-gate ebus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp, 527*0Sstevel@tonic-gate off_t off, off_t len, caddr_t *addrp) 528*0Sstevel@tonic-gate { 529*0Sstevel@tonic-gate ebus_devstate_t *ebus_p = get_ebus_soft_state(ddi_get_instance(dip)); 530*0Sstevel@tonic-gate ebus_regspec_t *ebus_rp, *ebus_regs; 531*0Sstevel@tonic-gate struct regspec reg; 532*0Sstevel@tonic-gate pci_regspec_t pci_reg; 533*0Sstevel@tonic-gate ddi_map_req_t p_map_request; 534*0Sstevel@tonic-gate int rnumber, i, n; 535*0Sstevel@tonic-gate int rval = DDI_SUCCESS; 536*0Sstevel@tonic-gate 537*0Sstevel@tonic-gate /* 538*0Sstevel@tonic-gate * Handle the mapping according to its type. 539*0Sstevel@tonic-gate */ 540*0Sstevel@tonic-gate DBG4(D_MAP, ebus_p, "rdip=%s%d: off=%x len=%x\n", 541*0Sstevel@tonic-gate ddi_get_name(rdip), ddi_get_instance(rdip), off, len); 542*0Sstevel@tonic-gate switch (mp->map_type) { 543*0Sstevel@tonic-gate case DDI_MT_REGSPEC: 544*0Sstevel@tonic-gate 545*0Sstevel@tonic-gate /* 546*0Sstevel@tonic-gate * We assume the register specification is in ebus format. 547*0Sstevel@tonic-gate * We must convert it into a PCI format regspec and pass 548*0Sstevel@tonic-gate * the request to our parent. 549*0Sstevel@tonic-gate */ 550*0Sstevel@tonic-gate DBG3(D_MAP, ebus_p, "rdip=%s%d: REGSPEC - handlep=%p\n", 551*0Sstevel@tonic-gate ddi_get_name(rdip), ddi_get_instance(rdip), 552*0Sstevel@tonic-gate mp->map_handlep); 553*0Sstevel@tonic-gate ebus_rp = (ebus_regspec_t *)mp->map_obj.rp; 554*0Sstevel@tonic-gate break; 555*0Sstevel@tonic-gate 556*0Sstevel@tonic-gate case DDI_MT_RNUMBER: 557*0Sstevel@tonic-gate 558*0Sstevel@tonic-gate /* 559*0Sstevel@tonic-gate * Get the "reg" property from the device node and convert 560*0Sstevel@tonic-gate * it to our parent's format. 561*0Sstevel@tonic-gate */ 562*0Sstevel@tonic-gate rnumber = mp->map_obj.rnumber; 563*0Sstevel@tonic-gate DBG4(D_MAP, ebus_p, "rdip=%s%d: rnumber=%x handlep=%p\n", 564*0Sstevel@tonic-gate ddi_get_name(rdip), ddi_get_instance(rdip), 565*0Sstevel@tonic-gate rnumber, mp->map_handlep); 566*0Sstevel@tonic-gate 567*0Sstevel@tonic-gate if (getprop(rdip, "reg", &ebus_regs, &i) != DDI_SUCCESS) { 568*0Sstevel@tonic-gate DBG(D_MAP, ebus_p, "can't get reg property\n"); 569*0Sstevel@tonic-gate return (DDI_ME_RNUMBER_RANGE); 570*0Sstevel@tonic-gate } 571*0Sstevel@tonic-gate n = i / sizeof (ebus_regspec_t); 572*0Sstevel@tonic-gate 573*0Sstevel@tonic-gate if (rnumber < 0 || rnumber >= n) { 574*0Sstevel@tonic-gate DBG(D_MAP, ebus_p, "rnumber out of range\n"); 575*0Sstevel@tonic-gate return (DDI_ME_RNUMBER_RANGE); 576*0Sstevel@tonic-gate } 577*0Sstevel@tonic-gate ebus_rp = &ebus_regs[rnumber]; 578*0Sstevel@tonic-gate break; 579*0Sstevel@tonic-gate 580*0Sstevel@tonic-gate default: 581*0Sstevel@tonic-gate return (DDI_ME_INVAL); 582*0Sstevel@tonic-gate 583*0Sstevel@tonic-gate } 584*0Sstevel@tonic-gate 585*0Sstevel@tonic-gate /* Adjust our reg property with offset and length */ 586*0Sstevel@tonic-gate ebus_rp->addr_low += off; 587*0Sstevel@tonic-gate if (len) 588*0Sstevel@tonic-gate ebus_rp->size = len; 589*0Sstevel@tonic-gate 590*0Sstevel@tonic-gate /* 591*0Sstevel@tonic-gate * Now we have a copy the "reg" entry we're attempting to map. 592*0Sstevel@tonic-gate * Translate this into our parents PCI address using the ranges 593*0Sstevel@tonic-gate * property. 594*0Sstevel@tonic-gate */ 595*0Sstevel@tonic-gate switch (ebus_p->type) { 596*0Sstevel@tonic-gate case EBUS_TYPE: 597*0Sstevel@tonic-gate rval = ebus_apply_range(ebus_p, rdip, ebus_rp, &pci_reg); 598*0Sstevel@tonic-gate break; 599*0Sstevel@tonic-gate case FEBUS_TYPE: 600*0Sstevel@tonic-gate rval = febus_apply_range(ebus_p, rdip, ebus_rp, ®); 601*0Sstevel@tonic-gate break; 602*0Sstevel@tonic-gate default: 603*0Sstevel@tonic-gate DBG(D_MAP, NULL, "failed to recognize ebus type\n"); 604*0Sstevel@tonic-gate rval = DDI_FAILURE; 605*0Sstevel@tonic-gate } 606*0Sstevel@tonic-gate 607*0Sstevel@tonic-gate if (mp->map_type == DDI_MT_RNUMBER) 608*0Sstevel@tonic-gate kmem_free(ebus_regs, i); 609*0Sstevel@tonic-gate 610*0Sstevel@tonic-gate if (rval != DDI_SUCCESS) 611*0Sstevel@tonic-gate return (rval); 612*0Sstevel@tonic-gate 613*0Sstevel@tonic-gate #ifdef DEBUG 614*0Sstevel@tonic-gate switch (ebus_p->type) { 615*0Sstevel@tonic-gate case EBUS_TYPE: 616*0Sstevel@tonic-gate DBG5(D_MAP, ebus_p, "(%x,%x,%x)(%x,%x)\n", 617*0Sstevel@tonic-gate pci_reg.pci_phys_hi, 618*0Sstevel@tonic-gate pci_reg.pci_phys_mid, 619*0Sstevel@tonic-gate pci_reg.pci_phys_low, 620*0Sstevel@tonic-gate pci_reg.pci_size_hi, 621*0Sstevel@tonic-gate pci_reg.pci_size_low); 622*0Sstevel@tonic-gate break; 623*0Sstevel@tonic-gate 624*0Sstevel@tonic-gate case FEBUS_TYPE: 625*0Sstevel@tonic-gate DBG3(D_MAP, ebus_p, "%x,%x,%x\n", 626*0Sstevel@tonic-gate reg.regspec_bustype, 627*0Sstevel@tonic-gate reg.regspec_addr, 628*0Sstevel@tonic-gate reg.regspec_size); 629*0Sstevel@tonic-gate break; 630*0Sstevel@tonic-gate } 631*0Sstevel@tonic-gate #endif 632*0Sstevel@tonic-gate 633*0Sstevel@tonic-gate p_map_request = *mp; 634*0Sstevel@tonic-gate p_map_request.map_type = DDI_MT_REGSPEC; 635*0Sstevel@tonic-gate 636*0Sstevel@tonic-gate switch (ebus_p->type) { 637*0Sstevel@tonic-gate case EBUS_TYPE: 638*0Sstevel@tonic-gate p_map_request.map_obj.rp = (struct regspec *)&pci_reg; 639*0Sstevel@tonic-gate break; 640*0Sstevel@tonic-gate case FEBUS_TYPE: 641*0Sstevel@tonic-gate p_map_request.map_obj.rp = ® 642*0Sstevel@tonic-gate break; 643*0Sstevel@tonic-gate default: 644*0Sstevel@tonic-gate DBG(D_MAP, NULL, "failed to recognize ebus type\n"); 645*0Sstevel@tonic-gate return (DDI_FAILURE); 646*0Sstevel@tonic-gate } 647*0Sstevel@tonic-gate 648*0Sstevel@tonic-gate rval = ddi_map(dip, &p_map_request, 0, 0, addrp); 649*0Sstevel@tonic-gate DBG1(D_MAP, ebus_p, "parent returned %x\n", rval); 650*0Sstevel@tonic-gate return (rval); 651*0Sstevel@tonic-gate } 652*0Sstevel@tonic-gate 653*0Sstevel@tonic-gate 654*0Sstevel@tonic-gate static int 655*0Sstevel@tonic-gate ebus_apply_range(ebus_devstate_t *ebus_p, dev_info_t *rdip, 656*0Sstevel@tonic-gate ebus_regspec_t *ebus_rp, pci_regspec_t *rp) 657*0Sstevel@tonic-gate { 658*0Sstevel@tonic-gate int b; 659*0Sstevel@tonic-gate int rval = DDI_SUCCESS; 660*0Sstevel@tonic-gate struct ebus_pci_rangespec *rangep = ebus_p->rangespec.rangep; 661*0Sstevel@tonic-gate int nrange = ebus_p->range_cnt; 662*0Sstevel@tonic-gate static char out_of_range[] = 663*0Sstevel@tonic-gate "Out of range register specification from device node <%s>"; 664*0Sstevel@tonic-gate 665*0Sstevel@tonic-gate DBG3(D_MAP, ebus_p, "Range Matching Addr 0x%x.%x size 0x%x\n", 666*0Sstevel@tonic-gate ebus_rp->addr_hi, ebus_rp->addr_low, ebus_rp->size); 667*0Sstevel@tonic-gate 668*0Sstevel@tonic-gate for (b = 0; b < nrange; ++b, ++rangep) { 669*0Sstevel@tonic-gate 670*0Sstevel@tonic-gate /* Check for the correct space */ 671*0Sstevel@tonic-gate if (ebus_rp->addr_hi == rangep->ebus_phys_hi) 672*0Sstevel@tonic-gate /* See if we fit in this range */ 673*0Sstevel@tonic-gate if ((ebus_rp->addr_low >= 674*0Sstevel@tonic-gate rangep->ebus_phys_low) && 675*0Sstevel@tonic-gate ((ebus_rp->addr_low + ebus_rp->size - 1) 676*0Sstevel@tonic-gate <= (rangep->ebus_phys_low + 677*0Sstevel@tonic-gate rangep->rng_size - 1))) { 678*0Sstevel@tonic-gate uint_t addr_offset = ebus_rp->addr_low - 679*0Sstevel@tonic-gate rangep->ebus_phys_low; 680*0Sstevel@tonic-gate /* 681*0Sstevel@tonic-gate * Use the range entry to translate 682*0Sstevel@tonic-gate * the EBUS physical address into the 683*0Sstevel@tonic-gate * parents PCI space. 684*0Sstevel@tonic-gate */ 685*0Sstevel@tonic-gate rp->pci_phys_hi = 686*0Sstevel@tonic-gate rangep->pci_phys_hi; 687*0Sstevel@tonic-gate rp->pci_phys_mid = rangep->pci_phys_mid; 688*0Sstevel@tonic-gate rp->pci_phys_low = 689*0Sstevel@tonic-gate rangep->pci_phys_low + addr_offset; 690*0Sstevel@tonic-gate rp->pci_size_hi = 0; 691*0Sstevel@tonic-gate rp->pci_size_low = 692*0Sstevel@tonic-gate min(ebus_rp->size, (rangep->rng_size - 693*0Sstevel@tonic-gate addr_offset)); 694*0Sstevel@tonic-gate 695*0Sstevel@tonic-gate DBG2(D_MAP, ebus_p, "Child hi0x%x lo0x%x ", 696*0Sstevel@tonic-gate rangep->ebus_phys_hi, 697*0Sstevel@tonic-gate rangep->ebus_phys_low); 698*0Sstevel@tonic-gate DBG4(D_MAP, ebus_p, "Parent hi0x%x " 699*0Sstevel@tonic-gate "mid0x%x lo0x%x size 0x%x\n", 700*0Sstevel@tonic-gate rangep->pci_phys_hi, 701*0Sstevel@tonic-gate rangep->pci_phys_mid, 702*0Sstevel@tonic-gate rangep->pci_phys_low, 703*0Sstevel@tonic-gate rangep->rng_size); 704*0Sstevel@tonic-gate 705*0Sstevel@tonic-gate break; 706*0Sstevel@tonic-gate } 707*0Sstevel@tonic-gate } 708*0Sstevel@tonic-gate 709*0Sstevel@tonic-gate if (b == nrange) { 710*0Sstevel@tonic-gate cmn_err(CE_WARN, out_of_range, ddi_get_name(rdip)); 711*0Sstevel@tonic-gate return (DDI_ME_REGSPEC_RANGE); 712*0Sstevel@tonic-gate } 713*0Sstevel@tonic-gate 714*0Sstevel@tonic-gate return (rval); 715*0Sstevel@tonic-gate } 716*0Sstevel@tonic-gate 717*0Sstevel@tonic-gate static int 718*0Sstevel@tonic-gate febus_apply_range(ebus_devstate_t *ebus_p, dev_info_t *rdip, 719*0Sstevel@tonic-gate ebus_regspec_t *ebus_rp, struct regspec *rp) { 720*0Sstevel@tonic-gate int b; 721*0Sstevel@tonic-gate int rval = DDI_SUCCESS; 722*0Sstevel@tonic-gate struct febus_rangespec *rangep = ebus_p->rangespec.ferangep; 723*0Sstevel@tonic-gate int nrange = ebus_p->range_cnt; 724*0Sstevel@tonic-gate static char out_of_range[] = 725*0Sstevel@tonic-gate "Out of range register specification from device node <%s>"; 726*0Sstevel@tonic-gate 727*0Sstevel@tonic-gate DBG3(D_MAP, ebus_p, "Range Matching Addr 0x%x.%x size 0x%x\n", 728*0Sstevel@tonic-gate ebus_rp->addr_hi, ebus_rp->addr_low, ebus_rp->size); 729*0Sstevel@tonic-gate 730*0Sstevel@tonic-gate for (b = 0; b < nrange; ++b, ++rangep) { 731*0Sstevel@tonic-gate /* Check for the correct space */ 732*0Sstevel@tonic-gate if (ebus_rp->addr_hi == rangep->febus_phys_hi) 733*0Sstevel@tonic-gate /* See if we fit in this range */ 734*0Sstevel@tonic-gate if ((ebus_rp->addr_low >= 735*0Sstevel@tonic-gate rangep->febus_phys_low) && 736*0Sstevel@tonic-gate ((ebus_rp->addr_low + ebus_rp->size - 1) 737*0Sstevel@tonic-gate <= (rangep->febus_phys_low + 738*0Sstevel@tonic-gate rangep->rng_size - 1))) { 739*0Sstevel@tonic-gate uint_t addr_offset = ebus_rp->addr_low - 740*0Sstevel@tonic-gate rangep->febus_phys_low; 741*0Sstevel@tonic-gate 742*0Sstevel@tonic-gate /* 743*0Sstevel@tonic-gate * Use the range entry to translate 744*0Sstevel@tonic-gate * the FEBUS physical address into the 745*0Sstevel@tonic-gate * parents space. 746*0Sstevel@tonic-gate */ 747*0Sstevel@tonic-gate rp->regspec_bustype = 748*0Sstevel@tonic-gate rangep->parent_phys_hi; 749*0Sstevel@tonic-gate rp->regspec_addr = 750*0Sstevel@tonic-gate rangep->parent_phys_low + addr_offset; 751*0Sstevel@tonic-gate rp->regspec_size = 752*0Sstevel@tonic-gate min(ebus_rp->size, (rangep->rng_size - 753*0Sstevel@tonic-gate addr_offset)); 754*0Sstevel@tonic-gate 755*0Sstevel@tonic-gate DBG2(D_MAP, ebus_p, "Child hi0x%x lo0x%x ", 756*0Sstevel@tonic-gate rangep->febus_phys_hi, 757*0Sstevel@tonic-gate rangep->febus_phys_low); 758*0Sstevel@tonic-gate DBG3(D_MAP, ebus_p, "Parent hi0x%x " 759*0Sstevel@tonic-gate "lo0x%x size 0x%x\n", 760*0Sstevel@tonic-gate rangep->parent_phys_hi, 761*0Sstevel@tonic-gate rangep->parent_phys_low, 762*0Sstevel@tonic-gate rangep->rng_size); 763*0Sstevel@tonic-gate 764*0Sstevel@tonic-gate break; 765*0Sstevel@tonic-gate } 766*0Sstevel@tonic-gate } 767*0Sstevel@tonic-gate 768*0Sstevel@tonic-gate if (b == nrange) { 769*0Sstevel@tonic-gate cmn_err(CE_WARN, out_of_range, ddi_get_name(rdip)); 770*0Sstevel@tonic-gate return (DDI_ME_REGSPEC_RANGE); 771*0Sstevel@tonic-gate } 772*0Sstevel@tonic-gate 773*0Sstevel@tonic-gate return (rval); 774*0Sstevel@tonic-gate } 775*0Sstevel@tonic-gate 776*0Sstevel@tonic-gate 777*0Sstevel@tonic-gate static int 778*0Sstevel@tonic-gate ebus_name_child(dev_info_t *child, char *name, int namelen) 779*0Sstevel@tonic-gate { 780*0Sstevel@tonic-gate ebus_regspec_t *ebus_rp; 781*0Sstevel@tonic-gate int reglen; 782*0Sstevel@tonic-gate 783*0Sstevel@tonic-gate /* 784*0Sstevel@tonic-gate * Get the address portion of the node name based on the 785*0Sstevel@tonic-gate * address/offset. 786*0Sstevel@tonic-gate */ 787*0Sstevel@tonic-gate if (ddi_getlongprop(DDI_DEV_T_NONE, child, DDI_PROP_DONTPASS, 788*0Sstevel@tonic-gate "reg", (caddr_t)&ebus_rp, ®len) != DDI_SUCCESS) { 789*0Sstevel@tonic-gate return (DDI_FAILURE); 790*0Sstevel@tonic-gate } 791*0Sstevel@tonic-gate 792*0Sstevel@tonic-gate (void) snprintf(name, namelen, "%x,%x", ebus_rp->addr_hi, 793*0Sstevel@tonic-gate ebus_rp->addr_low); 794*0Sstevel@tonic-gate kmem_free(ebus_rp, reglen); 795*0Sstevel@tonic-gate 796*0Sstevel@tonic-gate return (DDI_SUCCESS); 797*0Sstevel@tonic-gate } 798*0Sstevel@tonic-gate 799*0Sstevel@tonic-gate /* 800*0Sstevel@tonic-gate * control ops entry point: 801*0Sstevel@tonic-gate * 802*0Sstevel@tonic-gate * Requests handled completely: 803*0Sstevel@tonic-gate * DDI_CTLOPS_INITCHILD 804*0Sstevel@tonic-gate * DDI_CTLOPS_UNINITCHILD 805*0Sstevel@tonic-gate * DDI_CTLOPS_REPORTDEV 806*0Sstevel@tonic-gate * DDI_CTLOPS_REGSIZE 807*0Sstevel@tonic-gate * DDI_CTLOPS_NREGS 808*0Sstevel@tonic-gate * 809*0Sstevel@tonic-gate * All others passed to parent. 810*0Sstevel@tonic-gate */ 811*0Sstevel@tonic-gate static int 812*0Sstevel@tonic-gate ebus_ctlops(dev_info_t *dip, dev_info_t *rdip, 813*0Sstevel@tonic-gate ddi_ctl_enum_t op, void *arg, void *result) 814*0Sstevel@tonic-gate { 815*0Sstevel@tonic-gate #ifdef DEBUG 816*0Sstevel@tonic-gate ebus_devstate_t *ebus_p = get_ebus_soft_state(ddi_get_instance(dip)); 817*0Sstevel@tonic-gate #endif 818*0Sstevel@tonic-gate ebus_regspec_t *ebus_rp; 819*0Sstevel@tonic-gate int i, n; 820*0Sstevel@tonic-gate char name[10]; 821*0Sstevel@tonic-gate 822*0Sstevel@tonic-gate switch (op) { 823*0Sstevel@tonic-gate case DDI_CTLOPS_INITCHILD: { 824*0Sstevel@tonic-gate dev_info_t *child = (dev_info_t *)arg; 825*0Sstevel@tonic-gate /* 826*0Sstevel@tonic-gate * Set the address portion of the node name based on the 827*0Sstevel@tonic-gate * address/offset. 828*0Sstevel@tonic-gate */ 829*0Sstevel@tonic-gate DBG2(D_CTLOPS, ebus_p, "DDI_CTLOPS_INITCHILD: rdip=%s%d\n", 830*0Sstevel@tonic-gate ddi_get_name(child), ddi_get_instance(child)); 831*0Sstevel@tonic-gate 832*0Sstevel@tonic-gate if (ebus_name_child(child, name, 10) != DDI_SUCCESS) { 833*0Sstevel@tonic-gate DBG(D_CTLOPS, ebus_p, "can't name child\n"); 834*0Sstevel@tonic-gate return (DDI_FAILURE); 835*0Sstevel@tonic-gate } 836*0Sstevel@tonic-gate 837*0Sstevel@tonic-gate ddi_set_name_addr(child, name); 838*0Sstevel@tonic-gate ddi_set_parent_data(child, NULL); 839*0Sstevel@tonic-gate return (DDI_SUCCESS); 840*0Sstevel@tonic-gate } 841*0Sstevel@tonic-gate 842*0Sstevel@tonic-gate case DDI_CTLOPS_UNINITCHILD: 843*0Sstevel@tonic-gate DBG2(D_CTLOPS, ebus_p, "DDI_CTLOPS_UNINITCHILD: rdip=%s%d\n", 844*0Sstevel@tonic-gate ddi_get_name((dev_info_t *)arg), 845*0Sstevel@tonic-gate ddi_get_instance((dev_info_t *)arg)); 846*0Sstevel@tonic-gate ddi_set_name_addr((dev_info_t *)arg, NULL); 847*0Sstevel@tonic-gate ddi_remove_minor_node((dev_info_t *)arg, NULL); 848*0Sstevel@tonic-gate impl_rem_dev_props((dev_info_t *)arg); 849*0Sstevel@tonic-gate return (DDI_SUCCESS); 850*0Sstevel@tonic-gate 851*0Sstevel@tonic-gate case DDI_CTLOPS_REPORTDEV: 852*0Sstevel@tonic-gate 853*0Sstevel@tonic-gate DBG2(D_CTLOPS, ebus_p, "DDI_CTLOPS_REPORTDEV: rdip=%s%d\n", 854*0Sstevel@tonic-gate ddi_get_name(rdip), ddi_get_instance(rdip)); 855*0Sstevel@tonic-gate cmn_err(CE_CONT, "?%s%d at %s%d: offset %s\n", 856*0Sstevel@tonic-gate ddi_driver_name(rdip), ddi_get_instance(rdip), 857*0Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip), 858*0Sstevel@tonic-gate ddi_get_name_addr(rdip)); 859*0Sstevel@tonic-gate return (DDI_SUCCESS); 860*0Sstevel@tonic-gate 861*0Sstevel@tonic-gate case DDI_CTLOPS_REGSIZE: 862*0Sstevel@tonic-gate 863*0Sstevel@tonic-gate DBG2(D_CTLOPS, ebus_p, "DDI_CTLOPS_REGSIZE: rdip=%s%d\n", 864*0Sstevel@tonic-gate ddi_get_name(rdip), ddi_get_instance(rdip)); 865*0Sstevel@tonic-gate if (getprop(rdip, "reg", &ebus_rp, &i) != DDI_SUCCESS) { 866*0Sstevel@tonic-gate DBG(D_CTLOPS, ebus_p, "can't get reg property\n"); 867*0Sstevel@tonic-gate return (DDI_FAILURE); 868*0Sstevel@tonic-gate } 869*0Sstevel@tonic-gate n = i / sizeof (ebus_regspec_t); 870*0Sstevel@tonic-gate if (*(int *)arg < 0 || *(int *)arg >= n) { 871*0Sstevel@tonic-gate DBG(D_MAP, ebus_p, "rnumber out of range\n"); 872*0Sstevel@tonic-gate kmem_free(ebus_rp, i); 873*0Sstevel@tonic-gate return (DDI_FAILURE); 874*0Sstevel@tonic-gate } 875*0Sstevel@tonic-gate *((off_t *)result) = ebus_rp[*(int *)arg].size; 876*0Sstevel@tonic-gate kmem_free(ebus_rp, i); 877*0Sstevel@tonic-gate return (DDI_SUCCESS); 878*0Sstevel@tonic-gate 879*0Sstevel@tonic-gate case DDI_CTLOPS_NREGS: 880*0Sstevel@tonic-gate 881*0Sstevel@tonic-gate DBG2(D_CTLOPS, ebus_p, "DDI_CTLOPS_NREGS: rdip=%s%d\n", 882*0Sstevel@tonic-gate ddi_get_name(rdip), ddi_get_instance(rdip)); 883*0Sstevel@tonic-gate if (getprop(rdip, "reg", &ebus_rp, &i) != DDI_SUCCESS) { 884*0Sstevel@tonic-gate DBG(D_CTLOPS, ebus_p, "can't get reg property\n"); 885*0Sstevel@tonic-gate return (DDI_FAILURE); 886*0Sstevel@tonic-gate } 887*0Sstevel@tonic-gate *((uint_t *)result) = i / sizeof (ebus_regspec_t); 888*0Sstevel@tonic-gate kmem_free(ebus_rp, i); 889*0Sstevel@tonic-gate return (DDI_SUCCESS); 890*0Sstevel@tonic-gate } 891*0Sstevel@tonic-gate 892*0Sstevel@tonic-gate /* 893*0Sstevel@tonic-gate * Now pass the request up to our parent. 894*0Sstevel@tonic-gate */ 895*0Sstevel@tonic-gate DBG2(D_CTLOPS, ebus_p, "passing request to parent: rdip=%s%d\n", 896*0Sstevel@tonic-gate ddi_get_name(rdip), ddi_get_instance(rdip)); 897*0Sstevel@tonic-gate return (ddi_ctlops(dip, rdip, op, arg, result)); 898*0Sstevel@tonic-gate } 899*0Sstevel@tonic-gate 900*0Sstevel@tonic-gate struct ebus_string_to_pil { 901*0Sstevel@tonic-gate int8_t *string; 902*0Sstevel@tonic-gate uint32_t pil; 903*0Sstevel@tonic-gate }; 904*0Sstevel@tonic-gate 905*0Sstevel@tonic-gate static struct ebus_string_to_pil ebus_name_to_pil[] = {{"SUNW,CS4231", 9}, 906*0Sstevel@tonic-gate {"audio", 9}, 907*0Sstevel@tonic-gate {"fdthree", 8}, 908*0Sstevel@tonic-gate {"floppy", 8}, 909*0Sstevel@tonic-gate {"ecpp", 3}, 910*0Sstevel@tonic-gate {"parallel", 3}, 911*0Sstevel@tonic-gate {"su", 12}, 912*0Sstevel@tonic-gate {"se", 12}, 913*0Sstevel@tonic-gate {"serial", 12}, 914*0Sstevel@tonic-gate {"power", 14}}; 915*0Sstevel@tonic-gate 916*0Sstevel@tonic-gate static struct ebus_string_to_pil ebus_device_type_to_pil[] = {{"serial", 12}, 917*0Sstevel@tonic-gate {"block", 8}}; 918*0Sstevel@tonic-gate 919*0Sstevel@tonic-gate static int 920*0Sstevel@tonic-gate ebus_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op, 921*0Sstevel@tonic-gate ddi_intr_handle_impl_t *hdlp, void *result) 922*0Sstevel@tonic-gate { 923*0Sstevel@tonic-gate #ifdef DEBUG 924*0Sstevel@tonic-gate ebus_devstate_t *ebus_p = get_ebus_soft_state(ddi_get_instance(dip)); 925*0Sstevel@tonic-gate #endif 926*0Sstevel@tonic-gate ddi_ispec_t *ip = (ddi_ispec_t *)hdlp->ih_private; 927*0Sstevel@tonic-gate int32_t i, max_children, max_device_types, len; 928*0Sstevel@tonic-gate char *name_p, *device_type_p; 929*0Sstevel@tonic-gate 930*0Sstevel@tonic-gate DBG1(D_INTR, ebus_p, "ip 0x%p\n", ip); 931*0Sstevel@tonic-gate 932*0Sstevel@tonic-gate /* 933*0Sstevel@tonic-gate * NOTE: These ops below will never be supported in this nexus 934*0Sstevel@tonic-gate * driver, hence they always return immediately. 935*0Sstevel@tonic-gate */ 936*0Sstevel@tonic-gate switch (intr_op) { 937*0Sstevel@tonic-gate case DDI_INTROP_GETCAP: 938*0Sstevel@tonic-gate *(int *)result = 0; 939*0Sstevel@tonic-gate return (DDI_SUCCESS); 940*0Sstevel@tonic-gate case DDI_INTROP_SETCAP: 941*0Sstevel@tonic-gate case DDI_INTROP_SETMASK: 942*0Sstevel@tonic-gate case DDI_INTROP_CLRMASK: 943*0Sstevel@tonic-gate case DDI_INTROP_GETPENDING: 944*0Sstevel@tonic-gate return (DDI_ENOTSUP); 945*0Sstevel@tonic-gate default: 946*0Sstevel@tonic-gate break; 947*0Sstevel@tonic-gate } 948*0Sstevel@tonic-gate 949*0Sstevel@tonic-gate if ((intr_op == DDI_INTROP_SUPPORTED_TYPES) || ip->is_pil) 950*0Sstevel@tonic-gate goto done; 951*0Sstevel@tonic-gate 952*0Sstevel@tonic-gate /* 953*0Sstevel@tonic-gate * This is a hack to set the PIL for the devices under ebus. 954*0Sstevel@tonic-gate * We first look up a device by it's specific name, if we can't 955*0Sstevel@tonic-gate * match the name, we try and match it's device_type property. 956*0Sstevel@tonic-gate * Lastly we default a PIL level of 1. 957*0Sstevel@tonic-gate */ 958*0Sstevel@tonic-gate name_p = ddi_node_name(rdip); 959*0Sstevel@tonic-gate max_children = sizeof (ebus_name_to_pil) / 960*0Sstevel@tonic-gate sizeof (struct ebus_string_to_pil); 961*0Sstevel@tonic-gate 962*0Sstevel@tonic-gate for (i = 0; i < max_children; i++) { 963*0Sstevel@tonic-gate if (strcmp(ebus_name_to_pil[i].string, name_p) == 0) { 964*0Sstevel@tonic-gate DBG2(D_INTR, ebus_p, "child name %s; match PIL %d\n", 965*0Sstevel@tonic-gate ebus_name_to_pil[i].string, 966*0Sstevel@tonic-gate ebus_name_to_pil[i].pil); 967*0Sstevel@tonic-gate 968*0Sstevel@tonic-gate ip->is_pil = ebus_name_to_pil[i].pil; 969*0Sstevel@tonic-gate goto done; 970*0Sstevel@tonic-gate } 971*0Sstevel@tonic-gate } 972*0Sstevel@tonic-gate 973*0Sstevel@tonic-gate if (ddi_getlongprop(DDI_DEV_T_NONE, rdip, DDI_PROP_DONTPASS, 974*0Sstevel@tonic-gate "device_type", (caddr_t)&device_type_p, &len) == DDI_SUCCESS) { 975*0Sstevel@tonic-gate 976*0Sstevel@tonic-gate max_device_types = sizeof (ebus_device_type_to_pil) / 977*0Sstevel@tonic-gate sizeof (struct ebus_string_to_pil); 978*0Sstevel@tonic-gate 979*0Sstevel@tonic-gate for (i = 0; i < max_device_types; i++) { 980*0Sstevel@tonic-gate if (strcmp(ebus_device_type_to_pil[i].string, 981*0Sstevel@tonic-gate device_type_p) == 0) { 982*0Sstevel@tonic-gate DBG2(D_INTR, ebus_p, "Device type %s; match " 983*0Sstevel@tonic-gate "PIL %d\n", ebus_device_type_to_pil[i]. 984*0Sstevel@tonic-gate string, ebus_device_type_to_pil[i].pil); 985*0Sstevel@tonic-gate 986*0Sstevel@tonic-gate ip->is_pil = ebus_device_type_to_pil[i].pil; 987*0Sstevel@tonic-gate break; 988*0Sstevel@tonic-gate } 989*0Sstevel@tonic-gate } 990*0Sstevel@tonic-gate 991*0Sstevel@tonic-gate kmem_free(device_type_p, len); 992*0Sstevel@tonic-gate } 993*0Sstevel@tonic-gate 994*0Sstevel@tonic-gate /* 995*0Sstevel@tonic-gate * If we get here, we need to set a default value 996*0Sstevel@tonic-gate * for the PIL. 997*0Sstevel@tonic-gate */ 998*0Sstevel@tonic-gate if (ip->is_pil == 0) { 999*0Sstevel@tonic-gate ip->is_pil = 1; 1000*0Sstevel@tonic-gate 1001*0Sstevel@tonic-gate cmn_err(CE_WARN, "%s%d assigning default interrupt level %d " 1002*0Sstevel@tonic-gate "for device %s%d", ddi_get_name(dip), ddi_get_instance(dip), 1003*0Sstevel@tonic-gate ip->is_pil, ddi_get_name(rdip), ddi_get_instance(rdip)); 1004*0Sstevel@tonic-gate } 1005*0Sstevel@tonic-gate 1006*0Sstevel@tonic-gate done: 1007*0Sstevel@tonic-gate /* Pass up the request to our parent. */ 1008*0Sstevel@tonic-gate return (i_ddi_intr_ops(dip, rdip, intr_op, hdlp, result)); 1009*0Sstevel@tonic-gate } 1010*0Sstevel@tonic-gate 1011*0Sstevel@tonic-gate 1012*0Sstevel@tonic-gate static int 1013*0Sstevel@tonic-gate ebus_config(ebus_devstate_t *ebus_p) 1014*0Sstevel@tonic-gate { 1015*0Sstevel@tonic-gate ddi_acc_handle_t conf_handle; 1016*0Sstevel@tonic-gate uint16_t comm; 1017*0Sstevel@tonic-gate 1018*0Sstevel@tonic-gate /* 1019*0Sstevel@tonic-gate * Make sure the master enable and memory access enable 1020*0Sstevel@tonic-gate * bits are set in the config command register. 1021*0Sstevel@tonic-gate */ 1022*0Sstevel@tonic-gate if (pci_config_setup(ebus_p->dip, &conf_handle) != DDI_SUCCESS) 1023*0Sstevel@tonic-gate return (0); 1024*0Sstevel@tonic-gate 1025*0Sstevel@tonic-gate comm = pci_config_get16(conf_handle, PCI_CONF_COMM), 1026*0Sstevel@tonic-gate #ifdef DEBUG 1027*0Sstevel@tonic-gate DBG1(D_MAP, ebus_p, "command register was 0x%x\n", comm); 1028*0Sstevel@tonic-gate #endif 1029*0Sstevel@tonic-gate comm |= (PCI_COMM_ME|PCI_COMM_MAE|PCI_COMM_SERR_ENABLE| 1030*0Sstevel@tonic-gate PCI_COMM_PARITY_DETECT); 1031*0Sstevel@tonic-gate pci_config_put16(conf_handle, PCI_CONF_COMM, comm), 1032*0Sstevel@tonic-gate #ifdef DEBUG 1033*0Sstevel@tonic-gate DBG1(D_MAP, ebus_p, "command register is now 0x%x\n", comm); 1034*0Sstevel@tonic-gate #endif 1035*0Sstevel@tonic-gate pci_config_put8(conf_handle, PCI_CONF_CACHE_LINESZ, 1036*0Sstevel@tonic-gate (uchar_t)ebus_cache_line_size); 1037*0Sstevel@tonic-gate pci_config_put8(conf_handle, PCI_CONF_LATENCY_TIMER, 1038*0Sstevel@tonic-gate (uchar_t)ebus_latency_timer); 1039*0Sstevel@tonic-gate pci_config_teardown(&conf_handle); 1040*0Sstevel@tonic-gate return (1); 1041*0Sstevel@tonic-gate } 1042*0Sstevel@tonic-gate 1043*0Sstevel@tonic-gate #ifdef DEBUG 1044*0Sstevel@tonic-gate extern void prom_printf(const char *, ...); 1045*0Sstevel@tonic-gate 1046*0Sstevel@tonic-gate static void 1047*0Sstevel@tonic-gate ebus_debug(uint_t flag, ebus_devstate_t *ebus_p, char *fmt, 1048*0Sstevel@tonic-gate uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5) 1049*0Sstevel@tonic-gate { 1050*0Sstevel@tonic-gate char *s; 1051*0Sstevel@tonic-gate 1052*0Sstevel@tonic-gate if (ebus_debug_flags & flag) { 1053*0Sstevel@tonic-gate switch (flag) { 1054*0Sstevel@tonic-gate case D_ATTACH: 1055*0Sstevel@tonic-gate s = "attach"; break; 1056*0Sstevel@tonic-gate case D_DETACH: 1057*0Sstevel@tonic-gate s = "detach"; break; 1058*0Sstevel@tonic-gate case D_MAP: 1059*0Sstevel@tonic-gate s = "map"; break; 1060*0Sstevel@tonic-gate case D_CTLOPS: 1061*0Sstevel@tonic-gate s = "ctlops"; break; 1062*0Sstevel@tonic-gate case D_INTR: 1063*0Sstevel@tonic-gate s = "intr"; break; 1064*0Sstevel@tonic-gate } 1065*0Sstevel@tonic-gate if (ebus_p) 1066*0Sstevel@tonic-gate cmn_err(CE_CONT, "%s%d: %s: ", 1067*0Sstevel@tonic-gate ddi_get_name(ebus_p->dip), 1068*0Sstevel@tonic-gate ddi_get_instance(ebus_p->dip), s); 1069*0Sstevel@tonic-gate else 1070*0Sstevel@tonic-gate cmn_err(CE_CONT, "ebus: "); 1071*0Sstevel@tonic-gate cmn_err(CE_CONT, fmt, a1, a2, a3, a4, a5); 1072*0Sstevel@tonic-gate } 1073*0Sstevel@tonic-gate } 1074*0Sstevel@tonic-gate #endif 1075*0Sstevel@tonic-gate 1076*0Sstevel@tonic-gate /* ARGSUSED3 */ 1077*0Sstevel@tonic-gate static int 1078*0Sstevel@tonic-gate ebus_open(dev_t *devp, int flags, int otyp, cred_t *credp) 1079*0Sstevel@tonic-gate { 1080*0Sstevel@tonic-gate ebus_devstate_t *ebus_p; 1081*0Sstevel@tonic-gate 1082*0Sstevel@tonic-gate /* 1083*0Sstevel@tonic-gate * Make sure the open is for the right file type. 1084*0Sstevel@tonic-gate */ 1085*0Sstevel@tonic-gate if (otyp != OTYP_CHR) 1086*0Sstevel@tonic-gate return (EINVAL); 1087*0Sstevel@tonic-gate 1088*0Sstevel@tonic-gate /* 1089*0Sstevel@tonic-gate * Get the soft state structure for the device. 1090*0Sstevel@tonic-gate */ 1091*0Sstevel@tonic-gate ebus_p = get_ebus_soft_state(getminor(*devp)); 1092*0Sstevel@tonic-gate if (ebus_p == NULL) 1093*0Sstevel@tonic-gate return (ENXIO); 1094*0Sstevel@tonic-gate 1095*0Sstevel@tonic-gate /* 1096*0Sstevel@tonic-gate * Handle the open by tracking the device state. 1097*0Sstevel@tonic-gate */ 1098*0Sstevel@tonic-gate mutex_enter(&ebus_p->ebus_mutex); 1099*0Sstevel@tonic-gate if (flags & FEXCL) { 1100*0Sstevel@tonic-gate if (ebus_p->ebus_soft_state != EBUS_SOFT_STATE_CLOSED) { 1101*0Sstevel@tonic-gate mutex_exit(&ebus_p->ebus_mutex); 1102*0Sstevel@tonic-gate return (EBUSY); 1103*0Sstevel@tonic-gate } 1104*0Sstevel@tonic-gate ebus_p->ebus_soft_state = EBUS_SOFT_STATE_OPEN_EXCL; 1105*0Sstevel@tonic-gate } else { 1106*0Sstevel@tonic-gate if (ebus_p->ebus_soft_state == EBUS_SOFT_STATE_OPEN_EXCL) { 1107*0Sstevel@tonic-gate mutex_exit(&ebus_p->ebus_mutex); 1108*0Sstevel@tonic-gate return (EBUSY); 1109*0Sstevel@tonic-gate } 1110*0Sstevel@tonic-gate ebus_p->ebus_soft_state = EBUS_SOFT_STATE_OPEN; 1111*0Sstevel@tonic-gate } 1112*0Sstevel@tonic-gate mutex_exit(&ebus_p->ebus_mutex); 1113*0Sstevel@tonic-gate return (0); 1114*0Sstevel@tonic-gate } 1115*0Sstevel@tonic-gate 1116*0Sstevel@tonic-gate 1117*0Sstevel@tonic-gate /* ARGSUSED */ 1118*0Sstevel@tonic-gate static int 1119*0Sstevel@tonic-gate ebus_close(dev_t dev, int flags, int otyp, cred_t *credp) 1120*0Sstevel@tonic-gate { 1121*0Sstevel@tonic-gate ebus_devstate_t *ebus_p; 1122*0Sstevel@tonic-gate 1123*0Sstevel@tonic-gate if (otyp != OTYP_CHR) 1124*0Sstevel@tonic-gate return (EINVAL); 1125*0Sstevel@tonic-gate 1126*0Sstevel@tonic-gate ebus_p = get_ebus_soft_state(getminor(dev)); 1127*0Sstevel@tonic-gate if (ebus_p == NULL) 1128*0Sstevel@tonic-gate return (ENXIO); 1129*0Sstevel@tonic-gate 1130*0Sstevel@tonic-gate mutex_enter(&ebus_p->ebus_mutex); 1131*0Sstevel@tonic-gate ebus_p->ebus_soft_state = EBUS_SOFT_STATE_CLOSED; 1132*0Sstevel@tonic-gate mutex_exit(&ebus_p->ebus_mutex); 1133*0Sstevel@tonic-gate return (0); 1134*0Sstevel@tonic-gate } 1135*0Sstevel@tonic-gate 1136*0Sstevel@tonic-gate 1137*0Sstevel@tonic-gate /* 1138*0Sstevel@tonic-gate * ebus_ioctl: devctl hotplug controls 1139*0Sstevel@tonic-gate */ 1140*0Sstevel@tonic-gate /* ARGSUSED */ 1141*0Sstevel@tonic-gate static int 1142*0Sstevel@tonic-gate ebus_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, 1143*0Sstevel@tonic-gate int *rvalp) 1144*0Sstevel@tonic-gate { 1145*0Sstevel@tonic-gate ebus_devstate_t *ebus_p; 1146*0Sstevel@tonic-gate dev_info_t *self; 1147*0Sstevel@tonic-gate struct devctl_iocdata *dcp; 1148*0Sstevel@tonic-gate uint_t bus_state; 1149*0Sstevel@tonic-gate int rv = 0; 1150*0Sstevel@tonic-gate 1151*0Sstevel@tonic-gate ebus_p = get_ebus_soft_state(getminor(dev)); 1152*0Sstevel@tonic-gate if (ebus_p == NULL) 1153*0Sstevel@tonic-gate return (ENXIO); 1154*0Sstevel@tonic-gate 1155*0Sstevel@tonic-gate self = ebus_p->dip; 1156*0Sstevel@tonic-gate 1157*0Sstevel@tonic-gate /* 1158*0Sstevel@tonic-gate * We can use the generic implementation for these ioctls 1159*0Sstevel@tonic-gate */ 1160*0Sstevel@tonic-gate switch (cmd) { 1161*0Sstevel@tonic-gate case DEVCTL_DEVICE_GETSTATE: 1162*0Sstevel@tonic-gate case DEVCTL_DEVICE_ONLINE: 1163*0Sstevel@tonic-gate case DEVCTL_DEVICE_OFFLINE: 1164*0Sstevel@tonic-gate case DEVCTL_BUS_GETSTATE: 1165*0Sstevel@tonic-gate return (ndi_devctl_ioctl(self, cmd, arg, mode, 0)); 1166*0Sstevel@tonic-gate } 1167*0Sstevel@tonic-gate 1168*0Sstevel@tonic-gate /* 1169*0Sstevel@tonic-gate * read devctl ioctl data 1170*0Sstevel@tonic-gate */ 1171*0Sstevel@tonic-gate if (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS) 1172*0Sstevel@tonic-gate return (EFAULT); 1173*0Sstevel@tonic-gate 1174*0Sstevel@tonic-gate switch (cmd) { 1175*0Sstevel@tonic-gate 1176*0Sstevel@tonic-gate case DEVCTL_DEVICE_RESET: 1177*0Sstevel@tonic-gate rv = ENOTSUP; 1178*0Sstevel@tonic-gate break; 1179*0Sstevel@tonic-gate 1180*0Sstevel@tonic-gate case DEVCTL_BUS_QUIESCE: 1181*0Sstevel@tonic-gate if (ndi_get_bus_state(self, &bus_state) == NDI_SUCCESS) 1182*0Sstevel@tonic-gate if (bus_state == BUS_QUIESCED) 1183*0Sstevel@tonic-gate break; 1184*0Sstevel@tonic-gate (void) ndi_set_bus_state(self, BUS_QUIESCED); 1185*0Sstevel@tonic-gate break; 1186*0Sstevel@tonic-gate 1187*0Sstevel@tonic-gate case DEVCTL_BUS_UNQUIESCE: 1188*0Sstevel@tonic-gate if (ndi_get_bus_state(self, &bus_state) == NDI_SUCCESS) 1189*0Sstevel@tonic-gate if (bus_state == BUS_ACTIVE) 1190*0Sstevel@tonic-gate break; 1191*0Sstevel@tonic-gate (void) ndi_set_bus_state(self, BUS_ACTIVE); 1192*0Sstevel@tonic-gate break; 1193*0Sstevel@tonic-gate 1194*0Sstevel@tonic-gate case DEVCTL_BUS_RESET: 1195*0Sstevel@tonic-gate rv = ENOTSUP; 1196*0Sstevel@tonic-gate break; 1197*0Sstevel@tonic-gate 1198*0Sstevel@tonic-gate case DEVCTL_BUS_RESETALL: 1199*0Sstevel@tonic-gate rv = ENOTSUP; 1200*0Sstevel@tonic-gate break; 1201*0Sstevel@tonic-gate 1202*0Sstevel@tonic-gate default: 1203*0Sstevel@tonic-gate rv = ENOTTY; 1204*0Sstevel@tonic-gate } 1205*0Sstevel@tonic-gate 1206*0Sstevel@tonic-gate ndi_dc_freehdl(dcp); 1207*0Sstevel@tonic-gate return (rv); 1208*0Sstevel@tonic-gate } 1209