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 /* 2319397407SSherry Moore * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 2403831d35Sstevel * Use is subject to license terms. 2503831d35Sstevel */ 26*cd21e7c5SGarrett D'Amore /* 27*cd21e7c5SGarrett D'Amore * Copyright 2012 Garrett D'Amore <garrett@damore.org>. All rights reserved. 28*cd21e7c5SGarrett D'Amore */ 2903831d35Sstevel 3003831d35Sstevel 3103831d35Sstevel #include <sys/types.h> 3203831d35Sstevel #include <sys/conf.h> 3303831d35Sstevel #include <sys/ddi.h> 3403831d35Sstevel #include <sys/sunddi.h> 3503831d35Sstevel #include <sys/ddi_impldefs.h> 3603831d35Sstevel #include <sys/ddi_subrdefs.h> 3703831d35Sstevel #include <sys/pci.h> 3803831d35Sstevel #include <sys/pci/pci_nexus.h> 3903831d35Sstevel #include <sys/autoconf.h> 4003831d35Sstevel #include <sys/cmn_err.h> 4103831d35Sstevel #include <sys/errno.h> 4203831d35Sstevel #include <sys/kmem.h> 4303831d35Sstevel #include <sys/debug.h> 4403831d35Sstevel #include <sys/sysmacros.h> 4503831d35Sstevel #include <sys/acebus.h> 4603831d35Sstevel 4703831d35Sstevel #ifdef DEBUG 4803831d35Sstevel static uint_t acebus_debug_flags = 0; 4903831d35Sstevel #endif 5003831d35Sstevel 5103831d35Sstevel /* 5203831d35Sstevel * The values of the following variables are used to initialize 5303831d35Sstevel * the cache line size and latency timer registers in the ebus 5403831d35Sstevel * configuration header. Variables are used instead of constants 5503831d35Sstevel * to allow tuning from the /etc/system file. 5603831d35Sstevel */ 5703831d35Sstevel static uint8_t acebus_cache_line_size = 0x10; /* 64 bytes */ 5803831d35Sstevel static uint8_t acebus_latency_timer = 0x40; /* 64 PCI cycles */ 5903831d35Sstevel 6003831d35Sstevel /* 6103831d35Sstevel * function prototypes for bus ops routines: 6203831d35Sstevel */ 6303831d35Sstevel static int 6403831d35Sstevel acebus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp, 6503831d35Sstevel off_t offset, off_t len, caddr_t *addrp); 6603831d35Sstevel static int 6703831d35Sstevel acebus_ctlops(dev_info_t *dip, dev_info_t *rdip, 6803831d35Sstevel ddi_ctl_enum_t op, void *arg, void *result); 6903831d35Sstevel static int 7003831d35Sstevel acebus_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op, 7103831d35Sstevel ddi_intr_handle_impl_t *hdlp, void *result); 7203831d35Sstevel 7303831d35Sstevel /* 7403831d35Sstevel * function prototypes for dev ops routines: 7503831d35Sstevel */ 7603831d35Sstevel static int acebus_attach(dev_info_t *dip, ddi_attach_cmd_t cmd); 7703831d35Sstevel static int acebus_detach(dev_info_t *dip, ddi_detach_cmd_t cmd); 7803831d35Sstevel 7903831d35Sstevel /* 8003831d35Sstevel * general function prototypes: 8103831d35Sstevel */ 8203831d35Sstevel static int acebus_config(ebus_devstate_t *ebus_p); 8303831d35Sstevel static int acebus_apply_range(ebus_devstate_t *ebus_p, dev_info_t *rdip, 8403831d35Sstevel ebus_regspec_t *ebus_rp, pci_regspec_t *rp); 8503831d35Sstevel static int acebus_get_ranges_prop(ebus_devstate_t *ebus_p); 8603831d35Sstevel #ifdef ACEBUS_HOTPLUG 8703831d35Sstevel static int acebus_update_props(ebus_devstate_t *ebus_p); 8803831d35Sstevel static int acebus_set_imap(dev_info_t *dip); 8903831d35Sstevel #endif 9003831d35Sstevel 9103831d35Sstevel #define getprop(dip, name, addr, intp) \ 9203831d35Sstevel ddi_getlongprop(DDI_DEV_T_ANY, (dip), DDI_PROP_DONTPASS, \ 9303831d35Sstevel (name), (caddr_t)(addr), (intp)) 9403831d35Sstevel 9503831d35Sstevel /* 9603831d35Sstevel * bus ops and dev ops structures: 9703831d35Sstevel */ 9803831d35Sstevel static struct bus_ops acebus_bus_ops = { 9903831d35Sstevel BUSO_REV, 10003831d35Sstevel acebus_map, 10103831d35Sstevel NULL, 10203831d35Sstevel NULL, 10303831d35Sstevel NULL, 10403831d35Sstevel i_ddi_map_fault, 105*cd21e7c5SGarrett D'Amore NULL, 10603831d35Sstevel ddi_dma_allochdl, 10703831d35Sstevel ddi_dma_freehdl, 10803831d35Sstevel ddi_dma_bindhdl, 10903831d35Sstevel ddi_dma_unbindhdl, 11003831d35Sstevel ddi_dma_flush, 11103831d35Sstevel ddi_dma_win, 11203831d35Sstevel ddi_dma_mctl, 11303831d35Sstevel acebus_ctlops, 11403831d35Sstevel ddi_bus_prop_op, 11503831d35Sstevel 0, /* (*bus_get_eventcookie)(); */ 11603831d35Sstevel 0, /* (*bus_add_eventcall)(); */ 11703831d35Sstevel 0, /* (*bus_remove_eventcall)(); */ 11803831d35Sstevel 0, /* (*bus_post_event)(); */ 11903831d35Sstevel 0, /* (*bus_intr_ctl)(); */ 12003831d35Sstevel NULL, /* (*bus_config)(); */ 12103831d35Sstevel NULL, /* (*bus_unconfig)(); */ 12203831d35Sstevel NULL, /* (*bus_fm_init)(); */ 12303831d35Sstevel NULL, /* (*bus_fm_fini)(); */ 12403831d35Sstevel NULL, /* (*bus_fm_access_enter)(); */ 12503831d35Sstevel NULL, /* (*bus_fm_access_fini)(); */ 12603831d35Sstevel NULL, /* (*bus_power)(); */ 12703831d35Sstevel acebus_intr_ops /* (*bus_intr_op)(); */ 12803831d35Sstevel }; 12903831d35Sstevel 13003831d35Sstevel static struct dev_ops acebus_ops = { 13103831d35Sstevel DEVO_REV, 13203831d35Sstevel 0, 13303831d35Sstevel ddi_no_info, 13403831d35Sstevel nulldev, 13503831d35Sstevel nulldev, 13603831d35Sstevel acebus_attach, 13703831d35Sstevel acebus_detach, 13803831d35Sstevel nodev, 13903831d35Sstevel (struct cb_ops *)0, 14019397407SSherry Moore &acebus_bus_ops, 14119397407SSherry Moore NULL, 14219397407SSherry Moore ddi_quiesce_not_supported, /* devo_quiesce */ 14303831d35Sstevel }; 14403831d35Sstevel 14503831d35Sstevel /* 14603831d35Sstevel * module definitions: 14703831d35Sstevel */ 14803831d35Sstevel #include <sys/modctl.h> 14903831d35Sstevel extern struct mod_ops mod_driverops; 15003831d35Sstevel 15103831d35Sstevel static struct modldrv modldrv = { 15203831d35Sstevel &mod_driverops, /* Type of module. This one is a driver */ 15319397407SSherry Moore "Alarm Card ebus nexus", /* Name of module. */ 15403831d35Sstevel &acebus_ops, /* driver ops */ 15503831d35Sstevel }; 15603831d35Sstevel 15703831d35Sstevel static struct modlinkage modlinkage = { 15803831d35Sstevel MODREV_1, (void *)&modldrv, NULL 15903831d35Sstevel }; 16003831d35Sstevel 16103831d35Sstevel /* 16203831d35Sstevel * driver global data: 16303831d35Sstevel */ 16403831d35Sstevel static void *per_acebus_state; /* per-ebus soft state pointer */ 16503831d35Sstevel 16603831d35Sstevel 16703831d35Sstevel int 16803831d35Sstevel _init(void) 16903831d35Sstevel { 17003831d35Sstevel int e; 17103831d35Sstevel 17203831d35Sstevel /* 17303831d35Sstevel * Initialize per-ebus soft state pointer. 17403831d35Sstevel */ 17503831d35Sstevel e = ddi_soft_state_init(&per_acebus_state, sizeof (ebus_devstate_t), 1); 17603831d35Sstevel if (e != 0) 17703831d35Sstevel return (e); 17803831d35Sstevel 17903831d35Sstevel /* 18003831d35Sstevel * Install the module. 18103831d35Sstevel */ 18203831d35Sstevel e = mod_install(&modlinkage); 18303831d35Sstevel if (e != 0) 18403831d35Sstevel ddi_soft_state_fini(&per_acebus_state); 18503831d35Sstevel return (e); 18603831d35Sstevel } 18703831d35Sstevel 18803831d35Sstevel int 18903831d35Sstevel _fini(void) 19003831d35Sstevel { 19103831d35Sstevel int e; 19203831d35Sstevel 19303831d35Sstevel /* 19403831d35Sstevel * Remove the module. 19503831d35Sstevel */ 19603831d35Sstevel e = mod_remove(&modlinkage); 19703831d35Sstevel if (e != 0) 19803831d35Sstevel return (e); 19903831d35Sstevel 20003831d35Sstevel /* 20103831d35Sstevel * Free the soft state info. 20203831d35Sstevel */ 20303831d35Sstevel ddi_soft_state_fini(&per_acebus_state); 20403831d35Sstevel return (e); 20503831d35Sstevel } 20603831d35Sstevel 20703831d35Sstevel int 20803831d35Sstevel _info(struct modinfo *modinfop) 20903831d35Sstevel { 21003831d35Sstevel return (mod_info(&modlinkage, modinfop)); 21103831d35Sstevel } 21203831d35Sstevel 21303831d35Sstevel /* device driver entry points */ 21403831d35Sstevel 21503831d35Sstevel /* 21603831d35Sstevel * attach entry point: 21703831d35Sstevel * 21803831d35Sstevel * normal attach: 21903831d35Sstevel * 22003831d35Sstevel * create soft state structure (dip, reg, nreg and state fields) 22103831d35Sstevel * map in configuration header 22203831d35Sstevel * make sure device is properly configured 22303831d35Sstevel * report device 22403831d35Sstevel */ 22503831d35Sstevel static int 22603831d35Sstevel acebus_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 22703831d35Sstevel { 22803831d35Sstevel ebus_devstate_t *ebus_p; /* per ebus state pointer */ 22903831d35Sstevel int instance; 23003831d35Sstevel 23103831d35Sstevel DBG1(D_ATTACH, NULL, "dip=%x\n", dip); 23203831d35Sstevel switch (cmd) { 23303831d35Sstevel case DDI_ATTACH: 23403831d35Sstevel 23503831d35Sstevel /* 23603831d35Sstevel * Allocate soft state for this instance. 23703831d35Sstevel */ 23803831d35Sstevel instance = ddi_get_instance(dip); 23903831d35Sstevel if (ddi_soft_state_zalloc(per_acebus_state, instance) 24003831d35Sstevel != DDI_SUCCESS) { 24103831d35Sstevel DBG(D_ATTACH, NULL, "failed to alloc soft state\n"); 24203831d35Sstevel return (DDI_FAILURE); 24303831d35Sstevel } 24403831d35Sstevel ebus_p = get_acebus_soft_state(instance); 24503831d35Sstevel ebus_p->dip = dip; 24603831d35Sstevel 24703831d35Sstevel /* 24803831d35Sstevel * Make sure the master enable and memory access enable 24903831d35Sstevel * bits are set in the config command register. 25003831d35Sstevel */ 25103831d35Sstevel if (!acebus_config(ebus_p)) { 25203831d35Sstevel free_acebus_soft_state(instance); 25303831d35Sstevel return (DDI_FAILURE); 25403831d35Sstevel } 25503831d35Sstevel 25603831d35Sstevel (void) ddi_prop_create(DDI_DEV_T_NONE, dip, 25703831d35Sstevel DDI_PROP_CANSLEEP, "no-dma-interrupt-sync", NULL, 0); 25803831d35Sstevel /* Get our ranges property for mapping child registers. */ 25903831d35Sstevel if (acebus_get_ranges_prop(ebus_p) != DDI_SUCCESS) { 26003831d35Sstevel free_acebus_soft_state(instance); 26103831d35Sstevel return (DDI_FAILURE); 26203831d35Sstevel } 26303831d35Sstevel 26403831d35Sstevel /* 26503831d35Sstevel * Make the state as attached and report the device. 26603831d35Sstevel */ 26703831d35Sstevel ebus_p->state = ATTACHED; 26803831d35Sstevel ddi_report_dev(dip); 26903831d35Sstevel DBG(D_ATTACH, ebus_p, "returning\n"); 27003831d35Sstevel return (DDI_SUCCESS); 27103831d35Sstevel 27203831d35Sstevel case DDI_RESUME: 27303831d35Sstevel 27403831d35Sstevel instance = ddi_get_instance(dip); 27503831d35Sstevel ebus_p = get_acebus_soft_state(instance); 27603831d35Sstevel 27703831d35Sstevel /* 27803831d35Sstevel * Make sure the master enable and memory access enable 27903831d35Sstevel * bits are set in the config command register. 28003831d35Sstevel */ 28103831d35Sstevel if (!acebus_config(ebus_p)) { 28203831d35Sstevel free_acebus_soft_state(instance); 28303831d35Sstevel return (DDI_FAILURE); 28403831d35Sstevel } 28503831d35Sstevel 28603831d35Sstevel ebus_p->state = RESUMED; 28703831d35Sstevel return (DDI_SUCCESS); 28803831d35Sstevel } 28903831d35Sstevel return (DDI_FAILURE); 29003831d35Sstevel } 29103831d35Sstevel 29203831d35Sstevel /* 29303831d35Sstevel * detach entry point: 29403831d35Sstevel */ 29503831d35Sstevel static int 29603831d35Sstevel acebus_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 29703831d35Sstevel { 29803831d35Sstevel int instance = ddi_get_instance(dip); 29903831d35Sstevel ebus_devstate_t *ebus_p = get_acebus_soft_state(instance); 30003831d35Sstevel 30103831d35Sstevel switch (cmd) { 30203831d35Sstevel case DDI_DETACH: 30303831d35Sstevel DBG1(D_DETACH, ebus_p, "DDI_DETACH dip=%p\n", dip); 30403831d35Sstevel ddi_prop_remove_all(dip); 30503831d35Sstevel kmem_free(ebus_p->rangep, ebus_p->range_cnt * 30603831d35Sstevel sizeof (struct ebus_pci_rangespec)); 30703831d35Sstevel free_acebus_soft_state(instance); 30803831d35Sstevel return (DDI_SUCCESS); 30903831d35Sstevel 31003831d35Sstevel case DDI_SUSPEND: 31103831d35Sstevel DBG1(D_DETACH, ebus_p, "DDI_SUSPEND dip=%p\n", dip); 31203831d35Sstevel ebus_p->state = SUSPENDED; 31303831d35Sstevel return (DDI_SUCCESS); 31403831d35Sstevel } 31503831d35Sstevel return (DDI_FAILURE); 31603831d35Sstevel } 31703831d35Sstevel 31803831d35Sstevel 31903831d35Sstevel static int 32003831d35Sstevel acebus_get_ranges_prop(ebus_devstate_t *ebus_p) 32103831d35Sstevel { 32203831d35Sstevel struct ebus_pci_rangespec *rangep; 32303831d35Sstevel int nrange, range_len; 32403831d35Sstevel 32503831d35Sstevel if (ddi_getlongprop(DDI_DEV_T_ANY, ebus_p->dip, DDI_PROP_DONTPASS, 32603831d35Sstevel "ranges", (caddr_t)&rangep, &range_len) != DDI_SUCCESS) { 32703831d35Sstevel 32803831d35Sstevel cmn_err(CE_WARN, "%s%d: can't get ranges property", 32903831d35Sstevel ddi_get_name(ebus_p->dip), ddi_get_instance(ebus_p->dip)); 33003831d35Sstevel return (DDI_ME_REGSPEC_RANGE); 33103831d35Sstevel } 33203831d35Sstevel 33303831d35Sstevel nrange = range_len / sizeof (struct ebus_pci_rangespec); 33403831d35Sstevel 33503831d35Sstevel if (nrange == 0) { 33603831d35Sstevel kmem_free(rangep, range_len); 33703831d35Sstevel return (DDI_FAILURE); 33803831d35Sstevel } 33903831d35Sstevel 34003831d35Sstevel #ifdef DEBUG 34119397407SSherry Moore { 34203831d35Sstevel int i; 34303831d35Sstevel 34403831d35Sstevel for (i = 0; i < nrange; i++) { 34519397407SSherry Moore DBG5(D_MAP, ebus_p, 34619397407SSherry Moore "ebus range addr 0x%x.0x%x PCI range " 34703831d35Sstevel "addr 0x%x.0x%x.0x%x ", rangep[i].ebus_phys_hi, 34803831d35Sstevel rangep[i].ebus_phys_low, rangep[i].pci_phys_hi, 34903831d35Sstevel rangep[i].pci_phys_mid, rangep[i].pci_phys_low); 35003831d35Sstevel DBG1(D_MAP, ebus_p, "Size 0x%x\n", rangep[i].rng_size); 35103831d35Sstevel } 35203831d35Sstevel } 35303831d35Sstevel #endif /* DEBUG */ 35403831d35Sstevel 35503831d35Sstevel ebus_p->rangep = rangep; 35603831d35Sstevel ebus_p->range_cnt = nrange; 35703831d35Sstevel 35803831d35Sstevel return (DDI_SUCCESS); 35903831d35Sstevel } 36003831d35Sstevel 36103831d35Sstevel 36203831d35Sstevel /* bus driver entry points */ 36303831d35Sstevel 36403831d35Sstevel /* 36503831d35Sstevel * bus map entry point: 36603831d35Sstevel * 36703831d35Sstevel * if map request is for an rnumber 36803831d35Sstevel * get the corresponding regspec from device node 36903831d35Sstevel * build a new regspec in our parent's format 37003831d35Sstevel * build a new map_req with the new regspec 37103831d35Sstevel * call up the tree to complete the mapping 37203831d35Sstevel */ 37303831d35Sstevel static int 37403831d35Sstevel acebus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp, 37503831d35Sstevel off_t off, off_t len, caddr_t *addrp) 37603831d35Sstevel { 37703831d35Sstevel ebus_devstate_t *ebus_p = get_acebus_soft_state(ddi_get_instance(dip)); 37803831d35Sstevel ebus_regspec_t *ebus_rp, *ebus_regs; 37903831d35Sstevel pci_regspec_t pci_reg; 38003831d35Sstevel ddi_map_req_t p_map_request; 38103831d35Sstevel int rnumber, i, n; 38203831d35Sstevel int rval = DDI_SUCCESS; 38303831d35Sstevel 38403831d35Sstevel /* 38503831d35Sstevel * Handle the mapping according to its type. 38603831d35Sstevel */ 38703831d35Sstevel DBG4(D_MAP, ebus_p, "rdip=%s%d: off=%x len=%x\n", 38803831d35Sstevel ddi_get_name(rdip), ddi_get_instance(rdip), off, len); 38903831d35Sstevel switch (mp->map_type) { 39003831d35Sstevel case DDI_MT_REGSPEC: 39103831d35Sstevel 39203831d35Sstevel /* 39303831d35Sstevel * We assume the register specification is in ebus format. 39403831d35Sstevel * We must convert it into a PCI format regspec and pass 39503831d35Sstevel * the request to our parent. 39603831d35Sstevel */ 39703831d35Sstevel DBG3(D_MAP, ebus_p, "rdip=%s%d: REGSPEC - handlep=%x\n", 39803831d35Sstevel ddi_get_name(rdip), ddi_get_instance(rdip), 39903831d35Sstevel mp->map_handlep); 40003831d35Sstevel ebus_rp = (ebus_regspec_t *)mp->map_obj.rp; 40103831d35Sstevel break; 40203831d35Sstevel 40303831d35Sstevel case DDI_MT_RNUMBER: 40403831d35Sstevel 40503831d35Sstevel /* 40603831d35Sstevel * Get the "reg" property from the device node and convert 40703831d35Sstevel * it to our parent's format. 40803831d35Sstevel */ 40903831d35Sstevel rnumber = mp->map_obj.rnumber; 41003831d35Sstevel DBG4(D_MAP, ebus_p, "rdip=%s%d: rnumber=%x handlep=%x\n", 41103831d35Sstevel ddi_get_name(rdip), ddi_get_instance(rdip), 41203831d35Sstevel rnumber, mp->map_handlep); 41303831d35Sstevel 41403831d35Sstevel if (getprop(rdip, "reg", &ebus_regs, &i) != DDI_SUCCESS) { 41503831d35Sstevel DBG(D_MAP, ebus_p, "can't get reg property\n"); 41603831d35Sstevel return (DDI_ME_RNUMBER_RANGE); 41703831d35Sstevel } 41803831d35Sstevel n = i / sizeof (ebus_regspec_t); 41903831d35Sstevel 42003831d35Sstevel if (rnumber < 0 || rnumber >= n) { 42103831d35Sstevel DBG(D_MAP, ebus_p, "rnumber out of range\n"); 42203831d35Sstevel return (DDI_ME_RNUMBER_RANGE); 42303831d35Sstevel } 42403831d35Sstevel ebus_rp = &ebus_regs[rnumber]; 42503831d35Sstevel break; 42603831d35Sstevel 42703831d35Sstevel default: 42803831d35Sstevel return (DDI_ME_INVAL); 42903831d35Sstevel 43003831d35Sstevel } 43103831d35Sstevel 43203831d35Sstevel /* Adjust our reg property with offset and length */ 43303831d35Sstevel ebus_rp->addr_low += off; 43403831d35Sstevel if (len) 43503831d35Sstevel ebus_rp->size = len; 43603831d35Sstevel 43703831d35Sstevel /* 43803831d35Sstevel * Now we have a copy the "reg" entry we're attempting to map. 43903831d35Sstevel * Translate this into our parents PCI address using the ranges 44003831d35Sstevel * property. 44103831d35Sstevel */ 44203831d35Sstevel rval = acebus_apply_range(ebus_p, rdip, ebus_rp, &pci_reg); 44303831d35Sstevel 44403831d35Sstevel if (mp->map_type == DDI_MT_RNUMBER) 44503831d35Sstevel kmem_free((caddr_t)ebus_regs, i); 44603831d35Sstevel 44703831d35Sstevel if (rval != DDI_SUCCESS) 44803831d35Sstevel return (rval); 44903831d35Sstevel 45003831d35Sstevel #ifdef ACEBUS_HOTPLUG 45103831d35Sstevel /* 45203831d35Sstevel * The map operation provides a translated (not a re-assigned, or 45303831d35Sstevel * relocated) ebus address for the child in its address space(range). 45403831d35Sstevel * Ebus address space is relocatible but its child address space 45503831d35Sstevel * is not. As specified by their 'reg' properties, they reside 45603831d35Sstevel * at a fixed offset in their parent's (ebus's) space. 45703831d35Sstevel * 45803831d35Sstevel * By setting this bit, we will not run into HostPCI nexus 45903831d35Sstevel * trying to relocate a translated ebus address (which is already 46003831d35Sstevel * relocated) and failing the operation. 46103831d35Sstevel * The reason for doing this here is that the PCI hotplug configurator 46203831d35Sstevel * always marks the ebus space as relocatible (unlike OBP) and that 46303831d35Sstevel * information is implied for the child too, which is wrong. 46403831d35Sstevel */ 46503831d35Sstevel pci_reg.pci_phys_hi |= PCI_RELOCAT_B; 46603831d35Sstevel #endif 46703831d35Sstevel #ifdef DEBUG 46803831d35Sstevel DBG5(D_MAP, ebus_p, "(%x,%x,%x)(%x,%x)\n", 46903831d35Sstevel pci_reg.pci_phys_hi, 47003831d35Sstevel pci_reg.pci_phys_mid, 47103831d35Sstevel pci_reg.pci_phys_low, 47203831d35Sstevel pci_reg.pci_size_hi, 47303831d35Sstevel pci_reg.pci_size_low); 47403831d35Sstevel #endif 47503831d35Sstevel 47603831d35Sstevel p_map_request = *mp; 47703831d35Sstevel p_map_request.map_type = DDI_MT_REGSPEC; 47803831d35Sstevel p_map_request.map_obj.rp = (struct regspec *)&pci_reg; 47903831d35Sstevel rval = ddi_map(dip, &p_map_request, 0, 0, addrp); 48003831d35Sstevel DBG1(D_MAP, ebus_p, "parent returned %x\n", rval); 48103831d35Sstevel return (rval); 48203831d35Sstevel } 48303831d35Sstevel 48403831d35Sstevel 48503831d35Sstevel static int 48603831d35Sstevel acebus_apply_range(ebus_devstate_t *ebus_p, dev_info_t *rdip, 48703831d35Sstevel ebus_regspec_t *ebus_rp, pci_regspec_t *rp) 48803831d35Sstevel { 48903831d35Sstevel int b; 49003831d35Sstevel int rval = DDI_SUCCESS; 49103831d35Sstevel struct ebus_pci_rangespec *rangep = ebus_p->rangep; 49203831d35Sstevel int nrange = ebus_p->range_cnt; 49303831d35Sstevel static const char out_of_range[] = 49403831d35Sstevel "Out of range register specification from device node <%s>"; 49503831d35Sstevel 49603831d35Sstevel DBG3(D_MAP, ebus_p, "Range Matching Addr 0x%x.%x size 0x%x\n", 49703831d35Sstevel ebus_rp->addr_hi, ebus_rp->addr_low, ebus_rp->size); 49803831d35Sstevel 49903831d35Sstevel for (b = 0; b < nrange; ++b, ++rangep) { 50003831d35Sstevel 50103831d35Sstevel /* Check for the correct space */ 50203831d35Sstevel if (ebus_rp->addr_hi == rangep->ebus_phys_hi) 50303831d35Sstevel /* See if we fit in this range */ 50403831d35Sstevel if ((ebus_rp->addr_low >= 50503831d35Sstevel rangep->ebus_phys_low) && 50603831d35Sstevel ((ebus_rp->addr_low + ebus_rp->size - 1) 50703831d35Sstevel <= (rangep->ebus_phys_low + 50803831d35Sstevel rangep->rng_size - 1))) { 50903831d35Sstevel uint_t addr_offset = ebus_rp->addr_low - 51003831d35Sstevel rangep->ebus_phys_low; 51103831d35Sstevel /* 51203831d35Sstevel * Use the range entry to translate 51303831d35Sstevel * the EBUS physical address into the 51403831d35Sstevel * parents PCI space. 51503831d35Sstevel */ 51603831d35Sstevel rp->pci_phys_hi = 51703831d35Sstevel rangep->pci_phys_hi; 51803831d35Sstevel rp->pci_phys_mid = rangep->pci_phys_mid; 51903831d35Sstevel rp->pci_phys_low = 52003831d35Sstevel rangep->pci_phys_low + addr_offset; 52103831d35Sstevel rp->pci_size_hi = 0; 52203831d35Sstevel rp->pci_size_low = 52303831d35Sstevel min(ebus_rp->size, (rangep->rng_size - 52403831d35Sstevel addr_offset)); 52503831d35Sstevel 52603831d35Sstevel DBG2(D_MAP, ebus_p, "Child hi0x%x lo0x%x ", 52703831d35Sstevel rangep->ebus_phys_hi, 52803831d35Sstevel rangep->ebus_phys_low); 52903831d35Sstevel DBG4(D_MAP, ebus_p, "Parent hi0x%x " 53003831d35Sstevel "mid0x%x lo0x%x size 0x%x\n", 53103831d35Sstevel rangep->pci_phys_hi, 53203831d35Sstevel rangep->pci_phys_mid, 53303831d35Sstevel rangep->pci_phys_low, 53403831d35Sstevel rangep->rng_size); 53503831d35Sstevel 53603831d35Sstevel break; 53703831d35Sstevel } 53803831d35Sstevel } 53903831d35Sstevel 54003831d35Sstevel if (b == nrange) { 54103831d35Sstevel cmn_err(CE_WARN, out_of_range, ddi_get_name(rdip)); 54203831d35Sstevel return (DDI_ME_REGSPEC_RANGE); 54303831d35Sstevel } 54403831d35Sstevel 54503831d35Sstevel return (rval); 54603831d35Sstevel } 54703831d35Sstevel 54803831d35Sstevel 54903831d35Sstevel /* 55003831d35Sstevel * control ops entry point: 55103831d35Sstevel * 55203831d35Sstevel * Requests handled completely: 55303831d35Sstevel * DDI_CTLOPS_INITCHILD 55403831d35Sstevel * DDI_CTLOPS_UNINITCHILD 55503831d35Sstevel * DDI_CTLOPS_REPORTDEV 55603831d35Sstevel * DDI_CTLOPS_REGSIZE 55703831d35Sstevel * DDI_CTLOPS_NREGS 55803831d35Sstevel * 55903831d35Sstevel * All others passed to parent. 56003831d35Sstevel */ 56103831d35Sstevel static int 56203831d35Sstevel acebus_ctlops(dev_info_t *dip, dev_info_t *rdip, 56303831d35Sstevel ddi_ctl_enum_t op, void *arg, void *result) 56403831d35Sstevel { 56503831d35Sstevel #ifdef DEBUG 56603831d35Sstevel ebus_devstate_t *ebus_p = get_acebus_soft_state(ddi_get_instance(dip)); 56703831d35Sstevel #endif 56803831d35Sstevel ebus_regspec_t *ebus_rp; 56903831d35Sstevel int32_t reglen; 57003831d35Sstevel int i, n; 57103831d35Sstevel char name[10]; 57203831d35Sstevel 57303831d35Sstevel switch (op) { 57403831d35Sstevel case DDI_CTLOPS_INITCHILD: { 57503831d35Sstevel dev_info_t *child = (dev_info_t *)arg; 57603831d35Sstevel /* 57703831d35Sstevel * Set the address portion of the node name based on the 57803831d35Sstevel * address/offset. 57903831d35Sstevel */ 58003831d35Sstevel DBG2(D_CTLOPS, ebus_p, "DDI_CTLOPS_INITCHILD: rdip=%s%d\n", 58103831d35Sstevel ddi_get_name(child), ddi_get_instance(child)); 58203831d35Sstevel 58303831d35Sstevel if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, 58403831d35Sstevel "reg", (caddr_t)&ebus_rp, ®len) != DDI_SUCCESS) { 58503831d35Sstevel 58603831d35Sstevel DBG(D_CTLOPS, ebus_p, "can't get reg property\n"); 58703831d35Sstevel return (DDI_FAILURE); 58803831d35Sstevel 58903831d35Sstevel } 59003831d35Sstevel 59103831d35Sstevel (void) sprintf(name, "%x,%x", ebus_rp->addr_hi, 59203831d35Sstevel ebus_rp->addr_low); 59303831d35Sstevel ddi_set_name_addr(child, name); 59403831d35Sstevel kmem_free((caddr_t)ebus_rp, reglen); 59503831d35Sstevel 59603831d35Sstevel ddi_set_parent_data(child, NULL); 59703831d35Sstevel 59803831d35Sstevel return (DDI_SUCCESS); 59903831d35Sstevel 60003831d35Sstevel } 60103831d35Sstevel 60203831d35Sstevel case DDI_CTLOPS_UNINITCHILD: 60303831d35Sstevel DBG2(D_CTLOPS, ebus_p, "DDI_CTLOPS_UNINITCHILD: rdip=%s%d\n", 60403831d35Sstevel ddi_get_name((dev_info_t *)arg), 60503831d35Sstevel ddi_get_instance((dev_info_t *)arg)); 60603831d35Sstevel ddi_set_name_addr((dev_info_t *)arg, NULL); 60703831d35Sstevel ddi_remove_minor_node((dev_info_t *)arg, NULL); 60803831d35Sstevel impl_rem_dev_props((dev_info_t *)arg); 60903831d35Sstevel return (DDI_SUCCESS); 61003831d35Sstevel 61103831d35Sstevel case DDI_CTLOPS_REPORTDEV: 61203831d35Sstevel 61303831d35Sstevel DBG2(D_CTLOPS, ebus_p, "DDI_CTLOPS_REPORTDEV: rdip=%s%d\n", 61403831d35Sstevel ddi_get_name(rdip), ddi_get_instance(rdip)); 61503831d35Sstevel cmn_err(CE_CONT, "?%s%d at %s%d: offset %s\n", 61603831d35Sstevel ddi_driver_name(rdip), ddi_get_instance(rdip), 61703831d35Sstevel ddi_driver_name(dip), ddi_get_instance(dip), 61803831d35Sstevel ddi_get_name_addr(rdip)); 61903831d35Sstevel return (DDI_SUCCESS); 62003831d35Sstevel 62103831d35Sstevel case DDI_CTLOPS_REGSIZE: 62203831d35Sstevel 62303831d35Sstevel DBG2(D_CTLOPS, ebus_p, "DDI_CTLOPS_REGSIZE: rdip=%s%d\n", 62403831d35Sstevel ddi_get_name(rdip), ddi_get_instance(rdip)); 62503831d35Sstevel if (getprop(rdip, "reg", &ebus_rp, &i) != DDI_SUCCESS) { 62603831d35Sstevel DBG(D_CTLOPS, ebus_p, "can't get reg property\n"); 62703831d35Sstevel return (DDI_FAILURE); 62803831d35Sstevel } 62903831d35Sstevel n = i / sizeof (ebus_regspec_t); 63003831d35Sstevel if (*(int *)arg < 0 || *(int *)arg >= n) { 63103831d35Sstevel DBG(D_MAP, ebus_p, "rnumber out of range\n"); 63203831d35Sstevel kmem_free((caddr_t)ebus_rp, i); 63303831d35Sstevel return (DDI_FAILURE); 63403831d35Sstevel } 63503831d35Sstevel *((off_t *)result) = ebus_rp[*(int *)arg].size; 63603831d35Sstevel kmem_free((caddr_t)ebus_rp, i); 63703831d35Sstevel return (DDI_SUCCESS); 63803831d35Sstevel 63903831d35Sstevel case DDI_CTLOPS_NREGS: 64003831d35Sstevel 64103831d35Sstevel DBG2(D_CTLOPS, ebus_p, "DDI_CTLOPS_NREGS: rdip=%s%d\n", 64203831d35Sstevel ddi_get_name(rdip), ddi_get_instance(rdip)); 64303831d35Sstevel if (getprop(rdip, "reg", &ebus_rp, &i) != DDI_SUCCESS) { 64403831d35Sstevel DBG(D_CTLOPS, ebus_p, "can't get reg property\n"); 64503831d35Sstevel return (DDI_FAILURE); 64603831d35Sstevel } 64703831d35Sstevel *((uint_t *)result) = i / sizeof (ebus_regspec_t); 64803831d35Sstevel kmem_free((caddr_t)ebus_rp, i); 64903831d35Sstevel return (DDI_SUCCESS); 65003831d35Sstevel } 65103831d35Sstevel 65203831d35Sstevel /* 65303831d35Sstevel * Now pass the request up to our parent. 65403831d35Sstevel */ 65503831d35Sstevel DBG2(D_CTLOPS, ebus_p, "passing request to parent: rdip=%s%d\n", 65603831d35Sstevel ddi_get_name(rdip), ddi_get_instance(rdip)); 65703831d35Sstevel return (ddi_ctlops(dip, rdip, op, arg, result)); 65803831d35Sstevel } 65903831d35Sstevel 66003831d35Sstevel struct ebus_string_to_pil { 66103831d35Sstevel int8_t *string; 66203831d35Sstevel uint32_t pil; 66303831d35Sstevel }; 66403831d35Sstevel 66503831d35Sstevel static struct ebus_string_to_pil acebus_name_to_pil[] = {{"SUNW,CS4231", 9}, 66603831d35Sstevel {"fdthree", 8}, 66703831d35Sstevel {"ecpp", 3}, 66803831d35Sstevel {"su", 12}, 66903831d35Sstevel {"se", 12}, 67003831d35Sstevel {"power", 14}}; 67103831d35Sstevel 67203831d35Sstevel static struct ebus_string_to_pil acebus_device_type_to_pil[] = {{"serial", 12}, 67303831d35Sstevel {"block", 8}}; 67403831d35Sstevel 67503831d35Sstevel static int 67603831d35Sstevel acebus_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op, 67703831d35Sstevel ddi_intr_handle_impl_t *hdlp, void *result) 67803831d35Sstevel { 67903831d35Sstevel #ifdef DEBUG 68003831d35Sstevel ebus_devstate_t *ebus_p = get_acebus_soft_state(ddi_get_instance(dip)); 68103831d35Sstevel #endif 68203831d35Sstevel int8_t *name, *device_type; 68303831d35Sstevel int32_t i, max_children, max_device_types, len; 68403831d35Sstevel 68503831d35Sstevel /* 68603831d35Sstevel * NOTE: These ops below will never be supported in this nexus 68703831d35Sstevel * driver, hence they always return immediately. 68803831d35Sstevel */ 68903831d35Sstevel switch (intr_op) { 69003831d35Sstevel case DDI_INTROP_GETCAP: 69103831d35Sstevel *(int *)result = DDI_INTR_FLAG_LEVEL; 69203831d35Sstevel return (DDI_SUCCESS); 693fd715b00Srameshc case DDI_INTROP_SUPPORTED_TYPES: 694fd715b00Srameshc *(int *)result = i_ddi_get_intx_nintrs(rdip) ? 695fd715b00Srameshc DDI_INTR_TYPE_FIXED : 0; 696fd715b00Srameshc return (DDI_SUCCESS); 69703831d35Sstevel case DDI_INTROP_SETCAP: 69803831d35Sstevel case DDI_INTROP_SETMASK: 69903831d35Sstevel case DDI_INTROP_CLRMASK: 70003831d35Sstevel case DDI_INTROP_GETPENDING: 70103831d35Sstevel return (DDI_ENOTSUP); 70203831d35Sstevel default: 70303831d35Sstevel break; 70403831d35Sstevel } 70503831d35Sstevel 706fd715b00Srameshc if (hdlp->ih_pri) 70703831d35Sstevel goto done; 70803831d35Sstevel 70903831d35Sstevel /* 71003831d35Sstevel * This is a hack to set the PIL for the devices under ebus. 71103831d35Sstevel * We first look up a device by it's specific name, if we can't 71203831d35Sstevel * match the name, we try and match it's device_type property. 71303831d35Sstevel * Lastly we default a PIL level of 1. 71403831d35Sstevel */ 71503831d35Sstevel DBG1(D_INTR, ebus_p, "ebus_p %p\n", ebus_p); 71603831d35Sstevel 71703831d35Sstevel name = ddi_get_name(rdip); 71803831d35Sstevel max_children = sizeof (acebus_name_to_pil) / 71903831d35Sstevel sizeof (struct ebus_string_to_pil); 72003831d35Sstevel 72103831d35Sstevel for (i = 0; i < max_children; i++) { 72203831d35Sstevel if (strcmp(acebus_name_to_pil[i].string, name) == 0) { 72303831d35Sstevel DBG2(D_INTR, ebus_p, "child name %s; match PIL %d\n", 72403831d35Sstevel acebus_name_to_pil[i].string, 72503831d35Sstevel acebus_name_to_pil[i].pil); 72603831d35Sstevel 72703831d35Sstevel hdlp->ih_pri = acebus_name_to_pil[i].pil; 72803831d35Sstevel goto done; 72903831d35Sstevel } 73003831d35Sstevel } 73103831d35Sstevel 73203831d35Sstevel if (ddi_getlongprop(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS, 73303831d35Sstevel "device_type", (caddr_t)&device_type, &len) == DDI_SUCCESS) { 73403831d35Sstevel 73503831d35Sstevel max_device_types = sizeof (acebus_device_type_to_pil) / 73603831d35Sstevel sizeof (struct ebus_string_to_pil); 73703831d35Sstevel 73803831d35Sstevel for (i = 0; i < max_device_types; i++) { 73903831d35Sstevel if (strcmp(acebus_device_type_to_pil[i].string, 74003831d35Sstevel device_type) == 0) { 74103831d35Sstevel DBG2(D_INTR, ebus_p, 74203831d35Sstevel "Device type %s; match PIL %d\n", 74303831d35Sstevel acebus_device_type_to_pil[i].string, 74403831d35Sstevel acebus_device_type_to_pil[i].pil); 74503831d35Sstevel 74603831d35Sstevel hdlp->ih_pri = acebus_device_type_to_pil[i].pil; 74703831d35Sstevel break; 74803831d35Sstevel } 74903831d35Sstevel } 75003831d35Sstevel 75103831d35Sstevel kmem_free(device_type, len); 75203831d35Sstevel } 75303831d35Sstevel 75403831d35Sstevel /* 75503831d35Sstevel * If we get here, we need to set a default value 75603831d35Sstevel * for the PIL. 75703831d35Sstevel */ 75803831d35Sstevel if (hdlp->ih_pri == 0) { 75903831d35Sstevel hdlp->ih_pri = 1; 76003831d35Sstevel cmn_err(CE_WARN, "%s%d assigning default interrupt level %d " 76103831d35Sstevel "for device %s%d", ddi_driver_name(dip), 76203831d35Sstevel ddi_get_instance(dip), hdlp->ih_pri, ddi_driver_name(rdip), 76303831d35Sstevel ddi_get_instance(rdip)); 76403831d35Sstevel } 76503831d35Sstevel 76603831d35Sstevel done: 76703831d35Sstevel /* Pass up the request to our parent. */ 76803831d35Sstevel return (i_ddi_intr_ops(dip, rdip, intr_op, hdlp, result)); 76903831d35Sstevel } 77003831d35Sstevel 77103831d35Sstevel 77203831d35Sstevel static int 77303831d35Sstevel acebus_config(ebus_devstate_t *ebus_p) 77403831d35Sstevel { 77503831d35Sstevel ddi_acc_handle_t conf_handle; 77603831d35Sstevel uint16_t comm; 77703831d35Sstevel #ifdef ACEBUS_HOTPLUG 77803831d35Sstevel int tcr_reg; 77903831d35Sstevel caddr_t csr_io; 78003831d35Sstevel ddi_device_acc_attr_t csr_attr = { /* CSR map attributes */ 78103831d35Sstevel DDI_DEVICE_ATTR_V0, 78203831d35Sstevel DDI_STRUCTURE_LE_ACC, 78303831d35Sstevel DDI_STRICTORDER_ACC 78403831d35Sstevel }; 78503831d35Sstevel ddi_acc_handle_t csr_handle; 78603831d35Sstevel #endif 78703831d35Sstevel 78803831d35Sstevel /* 78903831d35Sstevel * Make sure the master enable and memory access enable 79003831d35Sstevel * bits are set in the config command register. 79103831d35Sstevel */ 79203831d35Sstevel if (pci_config_setup(ebus_p->dip, &conf_handle) != DDI_SUCCESS) 79303831d35Sstevel return (0); 79403831d35Sstevel 79503831d35Sstevel comm = pci_config_get16(conf_handle, PCI_CONF_COMM), 79603831d35Sstevel #ifdef DEBUG 79703831d35Sstevel DBG1(D_ATTACH, ebus_p, "command register was 0x%x\n", comm); 79803831d35Sstevel #endif 79903831d35Sstevel comm |= (PCI_COMM_ME|PCI_COMM_MAE|PCI_COMM_SERR_ENABLE| 80003831d35Sstevel PCI_COMM_PARITY_DETECT); 80103831d35Sstevel pci_config_put16(conf_handle, PCI_CONF_COMM, comm), 80203831d35Sstevel #ifdef DEBUG 80303831d35Sstevel DBG1(D_MAP, ebus_p, "command register is now 0x%x\n", 80403831d35Sstevel pci_config_get16(conf_handle, PCI_CONF_COMM)); 80503831d35Sstevel #endif 80603831d35Sstevel pci_config_put8(conf_handle, PCI_CONF_CACHE_LINESZ, 80703831d35Sstevel (uchar_t)acebus_cache_line_size); 80803831d35Sstevel pci_config_put8(conf_handle, PCI_CONF_LATENCY_TIMER, 80903831d35Sstevel (uchar_t)acebus_latency_timer); 81003831d35Sstevel pci_config_teardown(&conf_handle); 81103831d35Sstevel 81203831d35Sstevel #ifdef ACEBUS_HOTPLUG 81303831d35Sstevel if (acebus_update_props(ebus_p) != DDI_SUCCESS) { 81403831d35Sstevel cmn_err(CE_WARN, "%s%d: Could not update special properties.", 81503831d35Sstevel ddi_driver_name(ebus_p->dip), 81603831d35Sstevel ddi_get_instance(ebus_p->dip)); 81703831d35Sstevel return (0); 81803831d35Sstevel } 81903831d35Sstevel 82003831d35Sstevel if (ddi_regs_map_setup(ebus_p->dip, CSR_IO_RINDEX, 82103831d35Sstevel (caddr_t *)&csr_io, 0, CSR_SIZE, &csr_attr, 82203831d35Sstevel &csr_handle) != DDI_SUCCESS) { 82303831d35Sstevel cmn_err(CE_WARN, "%s%d: Could not map Ebus CSR.", 82403831d35Sstevel ddi_driver_name(ebus_p->dip), 82503831d35Sstevel ddi_get_instance(ebus_p->dip)); 82603831d35Sstevel } 82703831d35Sstevel #ifdef DEBUG 82803831d35Sstevel if (acebus_debug_flags) { 82903831d35Sstevel DBG3(D_ATTACH, ebus_p, "tcr[123] = %x,%x,%x\n", 83003831d35Sstevel ddi_get32(csr_handle, (uint32_t *)((caddr_t)csr_io + 83103831d35Sstevel TCR1_OFF)), 83203831d35Sstevel ddi_get32(csr_handle, (uint32_t *)((caddr_t)csr_io + 83303831d35Sstevel TCR2_OFF)), 83403831d35Sstevel ddi_get32(csr_handle, (uint32_t *)((caddr_t)csr_io + 83503831d35Sstevel TCR3_OFF))); 83603831d35Sstevel DBG2(D_ATTACH, ebus_p, "pmd-aux=%x, freq-aux=%x\n", 83703831d35Sstevel ddi_get32(csr_handle, (uint32_t *)((caddr_t)csr_io + 83803831d35Sstevel PMD_AUX_OFF)), 83903831d35Sstevel ddi_get32(csr_handle, (uint32_t *)((caddr_t)csr_io + 84003831d35Sstevel FREQ_AUX_OFF))); 84103831d35Sstevel #ifdef ACEBUS_DEBUG 84203831d35Sstevel for (comm = 0; comm < 4; comm++) 84303831d35Sstevel prom_printf("dcsr%d=%x, dacr%d=%x, dbcr%d=%x\n", comm, 84403831d35Sstevel ddi_get32(csr_handle, (uint32_t *)((caddr_t)csr_io + 84503831d35Sstevel 0x700000+(0x2000*comm))), comm, 84603831d35Sstevel ddi_get32(csr_handle, (uint32_t *)((caddr_t)csr_io + 84703831d35Sstevel 0x700000+(0x2000*comm)+4)), comm, 84803831d35Sstevel ddi_get32(csr_handle, (uint32_t *)((caddr_t)csr_io + 84903831d35Sstevel 0x700000+(0x2000*comm)+8))); 85003831d35Sstevel #endif 85103831d35Sstevel } /* acebus_debug_flags */ 85203831d35Sstevel #endif 85303831d35Sstevel /* If TCR registers are not initialized, initialize them here */ 85403831d35Sstevel tcr_reg = ddi_get32(csr_handle, (uint32_t *)((caddr_t)csr_io + 85503831d35Sstevel TCR1_OFF)); 85603831d35Sstevel if ((tcr_reg == 0) || (tcr_reg == -1)) 85703831d35Sstevel ddi_put32(csr_handle, (uint32_t *)((caddr_t)csr_io + TCR1_OFF), 85803831d35Sstevel TCR1_REGVAL); 85903831d35Sstevel tcr_reg = ddi_get32(csr_handle, (uint32_t *)((caddr_t)csr_io + 86003831d35Sstevel TCR2_OFF)); 86103831d35Sstevel if ((tcr_reg == 0) || (tcr_reg == -1)) 86203831d35Sstevel ddi_put32(csr_handle, (uint32_t *)((caddr_t)csr_io + TCR2_OFF), 86303831d35Sstevel TCR2_REGVAL); 86403831d35Sstevel tcr_reg = ddi_get32(csr_handle, (uint32_t *)((caddr_t)csr_io + 86503831d35Sstevel TCR3_OFF)); 86603831d35Sstevel if ((tcr_reg == 0) || (tcr_reg == -1)) 86703831d35Sstevel ddi_put32(csr_handle, (uint32_t *)((caddr_t)csr_io + TCR3_OFF), 86803831d35Sstevel TCR3_REGVAL); 86903831d35Sstevel #ifdef DEBUG 87003831d35Sstevel if (acebus_debug_flags) { 87103831d35Sstevel DBG3(D_ATTACH, ebus_p, "wrote tcr[123] = %x,%x,%x\n", 87203831d35Sstevel ddi_get32(csr_handle, (uint32_t *)((caddr_t)csr_io + 87303831d35Sstevel TCR1_OFF)), 87403831d35Sstevel ddi_get32(csr_handle, (uint32_t *)((caddr_t)csr_io + 87503831d35Sstevel TCR2_OFF)), 87603831d35Sstevel ddi_get32(csr_handle, (uint32_t *)((caddr_t)csr_io + 87703831d35Sstevel TCR3_OFF))); 87803831d35Sstevel } 87903831d35Sstevel #endif 88003831d35Sstevel 88103831d35Sstevel ddi_regs_map_free(&csr_handle); 88203831d35Sstevel #endif /* ACEBUS_HOTPLUG */ 88303831d35Sstevel return (1); /* return success */ 88403831d35Sstevel } 88503831d35Sstevel 88603831d35Sstevel #ifdef DEBUG 88703831d35Sstevel extern void prom_printf(const char *, ...); 88803831d35Sstevel 88903831d35Sstevel static void 89003831d35Sstevel acebus_debug(uint_t flag, ebus_devstate_t *ebus_p, char *fmt, 89103831d35Sstevel uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5) 89203831d35Sstevel { 89303831d35Sstevel char *s; 89403831d35Sstevel 89503831d35Sstevel if (acebus_debug_flags & flag) { 89603831d35Sstevel switch (flag) { 89703831d35Sstevel case D_ATTACH: 89803831d35Sstevel s = "attach"; break; 89903831d35Sstevel case D_DETACH: 90003831d35Sstevel s = "detach"; break; 90103831d35Sstevel case D_MAP: 90203831d35Sstevel s = "map"; break; 90303831d35Sstevel case D_CTLOPS: 90403831d35Sstevel s = "ctlops"; break; 90503831d35Sstevel case D_INTR: 90603831d35Sstevel s = "intr"; break; 90703831d35Sstevel } 90803831d35Sstevel if (ebus_p) 90903831d35Sstevel cmn_err(CE_CONT, "%s%d: %s: ", 91003831d35Sstevel ddi_get_name(ebus_p->dip), 91103831d35Sstevel ddi_get_instance(ebus_p->dip), s); 91203831d35Sstevel else 91303831d35Sstevel cmn_err(CE_CONT, "ebus: "); 91403831d35Sstevel cmn_err(CE_CONT, fmt, a1, a2, a3, a4, a5); 91503831d35Sstevel } 91603831d35Sstevel } 91703831d35Sstevel #endif 91803831d35Sstevel 91903831d35Sstevel #ifdef ACEBUS_HOTPLUG 92003831d35Sstevel #define EBUS_CHILD_PHYS_LOW_RANGE 0x10 92103831d35Sstevel #define EBUS_CHILD_PHYS_HI_RANGE 0x14 92203831d35Sstevel 92303831d35Sstevel static int 92403831d35Sstevel acebus_update_props(ebus_devstate_t *ebus_p) 92503831d35Sstevel { 92603831d35Sstevel dev_info_t *dip = ebus_p->dip; 92703831d35Sstevel struct ebus_pci_rangespec er[2], *erp; 92803831d35Sstevel pci_regspec_t *pci_rp, *prp; 92903831d35Sstevel int length, rnums, imask[3], i, found = 0; 93003831d35Sstevel 93103831d35Sstevel /* 93203831d35Sstevel * If "ranges" property is found, then the device is initialized 93303831d35Sstevel * by OBP, hence simply return. 93403831d35Sstevel * Otherwise we create all the properties here. 93503831d35Sstevel */ 93603831d35Sstevel if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 93703831d35Sstevel "ranges", (int **)&erp, (uint_t *)&length) == DDI_PROP_SUCCESS) { 93803831d35Sstevel ddi_prop_free(erp); 93903831d35Sstevel return (DDI_SUCCESS); 94003831d35Sstevel } 94103831d35Sstevel 94203831d35Sstevel /* 94303831d35Sstevel * interrupt-map is the only property that comes from a .conf file. 94403831d35Sstevel * Since it doesn't have the nodeid field set, it must be done here. 94503831d35Sstevel * Other properties can come from OBP or created here. 94603831d35Sstevel */ 94703831d35Sstevel if (acebus_set_imap(dip) != DDI_SUCCESS) { 94803831d35Sstevel return (DDI_FAILURE); 94903831d35Sstevel } 95003831d35Sstevel 95103831d35Sstevel /* 95203831d35Sstevel * Create the "ranges" property. 95303831d35Sstevel * Ebus has BAR0 and BAR1 allocated (both in memory space). 95403831d35Sstevel * Other BARs are 0. 95503831d35Sstevel * Hence there are 2 memory ranges it operates in. (one for each BAR). 95603831d35Sstevel * ie. there are 2 entries in its ranges property. 95703831d35Sstevel */ 95803831d35Sstevel if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, 95903831d35Sstevel DDI_PROP_DONTPASS, "assigned-addresses", 96019397407SSherry Moore (int **)&pci_rp, (uint_t *)&length) != DDI_PROP_SUCCESS) { 96103831d35Sstevel cmn_err(CE_WARN, "%s%d: Could not get assigned-addresses", 96203831d35Sstevel ddi_driver_name(dip), ddi_get_instance(dip)); 96303831d35Sstevel return (DDI_FAILURE); 96403831d35Sstevel } 96503831d35Sstevel /* 96603831d35Sstevel * Create the 1st mem range in which it operates corresponding 96703831d35Sstevel * to BAR0 96803831d35Sstevel */ 96903831d35Sstevel er[0].ebus_phys_hi = EBUS_CHILD_PHYS_LOW_RANGE; 97003831d35Sstevel rnums = (length * sizeof (int))/sizeof (pci_regspec_t); 97103831d35Sstevel for (i = 0; i < rnums; i++) { 97203831d35Sstevel prp = pci_rp + i; 97303831d35Sstevel if (PCI_REG_REG_G(prp->pci_phys_hi) == er[0].ebus_phys_hi) { 97403831d35Sstevel found = 1; 97503831d35Sstevel break; 97603831d35Sstevel } 97703831d35Sstevel } 97803831d35Sstevel if (!found) { 97903831d35Sstevel cmn_err(CE_WARN, "No assigned space for memory range 0."); 98003831d35Sstevel ddi_prop_free(pci_rp); 98103831d35Sstevel return (DDI_FAILURE); 98203831d35Sstevel } 98303831d35Sstevel found = 0; 98403831d35Sstevel er[0].ebus_phys_low = 0; 98503831d35Sstevel er[0].pci_phys_hi = prp->pci_phys_hi; 98603831d35Sstevel er[0].pci_phys_mid = prp->pci_phys_mid; 98703831d35Sstevel er[0].pci_phys_low = prp->pci_phys_low; 98803831d35Sstevel er[0].rng_size = prp->pci_size_low; 98903831d35Sstevel 99003831d35Sstevel /* 99103831d35Sstevel * Create the 2nd mem range in which it operates corresponding 99203831d35Sstevel * to BAR1 99303831d35Sstevel */ 99403831d35Sstevel er[1].ebus_phys_hi = EBUS_CHILD_PHYS_HI_RANGE; 99503831d35Sstevel for (i = 0; i < rnums; i++) { 99603831d35Sstevel prp = pci_rp + i; 99703831d35Sstevel if (PCI_REG_REG_G(prp->pci_phys_hi) == er[1].ebus_phys_hi) { 99803831d35Sstevel found = 1; 99903831d35Sstevel break; 100003831d35Sstevel } 100103831d35Sstevel } 100203831d35Sstevel if (!found) { 100303831d35Sstevel cmn_err(CE_WARN, "No assigned space for memory range 1."); 100403831d35Sstevel ddi_prop_free(pci_rp); 100503831d35Sstevel return (DDI_FAILURE); 100603831d35Sstevel } 100703831d35Sstevel er[1].ebus_phys_low = 0; 100803831d35Sstevel er[1].pci_phys_hi = prp->pci_phys_hi; 100903831d35Sstevel er[1].pci_phys_mid = prp->pci_phys_mid; 101003831d35Sstevel er[1].pci_phys_low = prp->pci_phys_low; 101103831d35Sstevel er[1].rng_size = prp->pci_size_low; 101203831d35Sstevel 101303831d35Sstevel ddi_prop_free(pci_rp); 101403831d35Sstevel length = sizeof (er) / sizeof (int); 101503831d35Sstevel if (ddi_prop_update_int_array(DDI_DEV_T_NONE, dip, 101603831d35Sstevel "ranges", (int *)er, length) != DDI_PROP_SUCCESS) { 101703831d35Sstevel cmn_err(CE_WARN, "%s%d: Could not create ranges property", 101803831d35Sstevel ddi_driver_name(dip), ddi_get_instance(dip)); 101903831d35Sstevel return (DDI_FAILURE); 102003831d35Sstevel } 102103831d35Sstevel /* The following properties are as defined by PCI 1275 bindings. */ 102203831d35Sstevel if (ddi_prop_update_int(DDI_DEV_T_NONE, dip, 102303831d35Sstevel "#address-cells", 2) != DDI_PROP_SUCCESS) 102403831d35Sstevel return (DDI_FAILURE); 102503831d35Sstevel if (ddi_prop_update_int(DDI_DEV_T_NONE, dip, 102603831d35Sstevel "#size-cells", 1) != DDI_PROP_SUCCESS) 102703831d35Sstevel return (DDI_FAILURE); 102803831d35Sstevel if (ddi_prop_update_int(DDI_DEV_T_NONE, dip, 102903831d35Sstevel "#interrupt-cells", 1) != DDI_PROP_SUCCESS) 103003831d35Sstevel return (DDI_FAILURE); 103103831d35Sstevel 103203831d35Sstevel imask[0] = 0x1f; 103303831d35Sstevel imask[1] = 0x00ffffff; 103403831d35Sstevel imask[2] = 0x00000003; 103503831d35Sstevel length = sizeof (imask) / sizeof (int); 103603831d35Sstevel if (ddi_prop_update_int_array(DDI_DEV_T_NONE, dip, 103703831d35Sstevel "interrupt-map-mask", (int *)imask, length) != DDI_PROP_SUCCESS) { 103803831d35Sstevel cmn_err(CE_WARN, "%s%d: Could not update imap mask property", 103903831d35Sstevel ddi_driver_name(dip), ddi_get_instance(dip)); 104003831d35Sstevel return (DDI_FAILURE); 104103831d35Sstevel } 104203831d35Sstevel 104303831d35Sstevel return (DDI_SUCCESS); 104403831d35Sstevel } 104503831d35Sstevel 104603831d35Sstevel /* 104703831d35Sstevel * This function takes in the ac-interrupt-map property from the .conf file, 104803831d35Sstevel * fills in the 'nodeid' information and then creates the 'interrupt-map' 104903831d35Sstevel * property. 105003831d35Sstevel */ 105103831d35Sstevel static int 105203831d35Sstevel acebus_set_imap(dev_info_t *dip) 105303831d35Sstevel { 105403831d35Sstevel int *imapp, *timapp, length, num, i, default_ival = 0; 105503831d35Sstevel dev_info_t *tdip = dip; 105603831d35Sstevel int *port_id, imap_ok = 1; 105703831d35Sstevel int ilength; 105803831d35Sstevel int acebus_default_se_imap[5]; 105903831d35Sstevel 106003831d35Sstevel /* 106103831d35Sstevel * interrupt-map is specified via .conf file in hotplug mode, 106203831d35Sstevel * since the child configuration is static. 106303831d35Sstevel * It could even be hardcoded in the driver. 106403831d35Sstevel */ 106503831d35Sstevel if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 106603831d35Sstevel "ac-interrupt-map", (int **)&imapp, (uint_t *)&ilength) != 106703831d35Sstevel DDI_PROP_SUCCESS) { 106803831d35Sstevel /* assume default implementation */ 106903831d35Sstevel acebus_default_se_imap[0] = 0x14; 107003831d35Sstevel acebus_default_se_imap[1] = 0x400000; 107103831d35Sstevel acebus_default_se_imap[2] = 1; 107203831d35Sstevel acebus_default_se_imap[3] = 0; 107303831d35Sstevel acebus_default_se_imap[4] = 2; 107403831d35Sstevel imapp = acebus_default_se_imap; 107503831d35Sstevel ilength = 5; 107603831d35Sstevel default_ival = 1; 107703831d35Sstevel } 107803831d35Sstevel num = ilength / 5; /* there are 5 integer cells in our property */ 107903831d35Sstevel timapp = imapp; 108003831d35Sstevel for (i = 0; i < num; i++) { 108103831d35Sstevel if (*(timapp+i*5+3) == 0) 108203831d35Sstevel imap_ok = 0; 108303831d35Sstevel } 108403831d35Sstevel if (imap_ok) { 108503831d35Sstevel if (!default_ival) 108603831d35Sstevel ddi_prop_free(imapp); 108703831d35Sstevel return (DDI_SUCCESS); 108803831d35Sstevel } 108903831d35Sstevel 109003831d35Sstevel while (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, tdip, 109103831d35Sstevel DDI_PROP_DONTPASS, "upa-portid", (int **)&port_id, 109203831d35Sstevel (uint_t *)&length) != DDI_PROP_SUCCESS) { 109303831d35Sstevel tdip = ddi_get_parent(tdip); 109403831d35Sstevel if (tdip == NULL) { 109503831d35Sstevel cmn_err(CE_WARN, "%s%d: Could not get imap parent", 109603831d35Sstevel ddi_driver_name(dip), ddi_get_instance(dip)); 109703831d35Sstevel if (!default_ival) 109803831d35Sstevel ddi_prop_free(imapp); 109903831d35Sstevel return (DDI_FAILURE); 110003831d35Sstevel } 110103831d35Sstevel } 110203831d35Sstevel timapp = imapp; 110303831d35Sstevel for (i = 0; i < num; i++) { 110403831d35Sstevel *(timapp+i*5+3) = ddi_get_nodeid(tdip); 110503831d35Sstevel } 110603831d35Sstevel 110703831d35Sstevel if (ddi_prop_update_int_array(DDI_DEV_T_NONE, dip, 110803831d35Sstevel "interrupt-map", imapp, ilength) != DDI_PROP_SUCCESS) { 110903831d35Sstevel cmn_err(CE_WARN, "%s%d: Could not update AC imap property", 111003831d35Sstevel ddi_driver_name(dip), ddi_get_instance(dip)); 111103831d35Sstevel if (!default_ival) 111203831d35Sstevel ddi_prop_free(imapp); 111303831d35Sstevel return (DDI_FAILURE); 111403831d35Sstevel } 111503831d35Sstevel if (!default_ival) 111603831d35Sstevel ddi_prop_free(imapp); 111703831d35Sstevel return (DDI_SUCCESS); 111803831d35Sstevel } 111903831d35Sstevel #endif /* ACEBUS_HOTPLUG */ 1120