11708Sstevel /*
21708Sstevel * CDDL HEADER START
31708Sstevel *
41708Sstevel * The contents of this file are subject to the terms of the
51708Sstevel * Common Development and Distribution License (the "License").
61708Sstevel * You may not use this file except in compliance with the License.
71708Sstevel *
81708Sstevel * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91708Sstevel * or http://www.opensolaris.org/os/licensing.
101708Sstevel * See the License for the specific language governing permissions
111708Sstevel * and limitations under the License.
121708Sstevel *
131708Sstevel * When distributing Covered Code, include this CDDL HEADER in each
141708Sstevel * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151708Sstevel * If applicable, add the following below this CDDL HEADER, with the
161708Sstevel * fields enclosed by brackets "[]" replaced with your own identifying
171708Sstevel * information: Portions Copyright [yyyy] [name of copyright owner]
181708Sstevel *
191708Sstevel * CDDL HEADER END
201708Sstevel */
211708Sstevel
221708Sstevel /*
23*7656SSherry.Moore@Sun.COM * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
241708Sstevel * Use is subject to license terms.
251708Sstevel */
261708Sstevel
271708Sstevel
281708Sstevel #include <sys/types.h>
291708Sstevel #include <sys/conf.h>
301708Sstevel #include <sys/ddi.h>
311708Sstevel #include <sys/sunddi.h>
321708Sstevel #include <sys/ddi_impldefs.h>
331708Sstevel #include <sys/ddi_subrdefs.h>
341708Sstevel #include <sys/pci.h>
351708Sstevel #include <sys/pci/pci_nexus.h>
361708Sstevel #include <sys/autoconf.h>
371708Sstevel #include <sys/cmn_err.h>
381708Sstevel #include <sys/errno.h>
391708Sstevel #include <sys/kmem.h>
401708Sstevel #include <sys/debug.h>
411708Sstevel #include <sys/sysmacros.h>
421708Sstevel #include <sys/acebus.h>
431708Sstevel
441708Sstevel #ifdef DEBUG
451708Sstevel static uint_t acebus_debug_flags = 0;
461708Sstevel #endif
471708Sstevel
481708Sstevel /*
491708Sstevel * The values of the following variables are used to initialize
501708Sstevel * the cache line size and latency timer registers in the ebus
511708Sstevel * configuration header. Variables are used instead of constants
521708Sstevel * to allow tuning from the /etc/system file.
531708Sstevel */
541708Sstevel static uint8_t acebus_cache_line_size = 0x10; /* 64 bytes */
551708Sstevel static uint8_t acebus_latency_timer = 0x40; /* 64 PCI cycles */
561708Sstevel
571708Sstevel /*
581708Sstevel * function prototypes for bus ops routines:
591708Sstevel */
601708Sstevel static int
611708Sstevel acebus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
621708Sstevel off_t offset, off_t len, caddr_t *addrp);
631708Sstevel static int
641708Sstevel acebus_ctlops(dev_info_t *dip, dev_info_t *rdip,
651708Sstevel ddi_ctl_enum_t op, void *arg, void *result);
661708Sstevel static int
671708Sstevel acebus_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op,
681708Sstevel ddi_intr_handle_impl_t *hdlp, void *result);
691708Sstevel
701708Sstevel /*
711708Sstevel * function prototypes for dev ops routines:
721708Sstevel */
731708Sstevel static int acebus_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
741708Sstevel static int acebus_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
751708Sstevel
761708Sstevel /*
771708Sstevel * general function prototypes:
781708Sstevel */
791708Sstevel static int acebus_config(ebus_devstate_t *ebus_p);
801708Sstevel static int acebus_apply_range(ebus_devstate_t *ebus_p, dev_info_t *rdip,
811708Sstevel ebus_regspec_t *ebus_rp, pci_regspec_t *rp);
821708Sstevel static int acebus_get_ranges_prop(ebus_devstate_t *ebus_p);
831708Sstevel #ifdef ACEBUS_HOTPLUG
841708Sstevel static int acebus_update_props(ebus_devstate_t *ebus_p);
851708Sstevel static int acebus_set_imap(dev_info_t *dip);
861708Sstevel #endif
871708Sstevel
881708Sstevel #define getprop(dip, name, addr, intp) \
891708Sstevel ddi_getlongprop(DDI_DEV_T_ANY, (dip), DDI_PROP_DONTPASS, \
901708Sstevel (name), (caddr_t)(addr), (intp))
911708Sstevel
921708Sstevel /*
931708Sstevel * bus ops and dev ops structures:
941708Sstevel */
951708Sstevel static struct bus_ops acebus_bus_ops = {
961708Sstevel BUSO_REV,
971708Sstevel acebus_map,
981708Sstevel NULL,
991708Sstevel NULL,
1001708Sstevel NULL,
1011708Sstevel i_ddi_map_fault,
1021708Sstevel ddi_dma_map,
1031708Sstevel ddi_dma_allochdl,
1041708Sstevel ddi_dma_freehdl,
1051708Sstevel ddi_dma_bindhdl,
1061708Sstevel ddi_dma_unbindhdl,
1071708Sstevel ddi_dma_flush,
1081708Sstevel ddi_dma_win,
1091708Sstevel ddi_dma_mctl,
1101708Sstevel acebus_ctlops,
1111708Sstevel ddi_bus_prop_op,
1121708Sstevel 0, /* (*bus_get_eventcookie)(); */
1131708Sstevel 0, /* (*bus_add_eventcall)(); */
1141708Sstevel 0, /* (*bus_remove_eventcall)(); */
1151708Sstevel 0, /* (*bus_post_event)(); */
1161708Sstevel 0, /* (*bus_intr_ctl)(); */
1171708Sstevel NULL, /* (*bus_config)(); */
1181708Sstevel NULL, /* (*bus_unconfig)(); */
1191708Sstevel NULL, /* (*bus_fm_init)(); */
1201708Sstevel NULL, /* (*bus_fm_fini)(); */
1211708Sstevel NULL, /* (*bus_fm_access_enter)(); */
1221708Sstevel NULL, /* (*bus_fm_access_fini)(); */
1231708Sstevel NULL, /* (*bus_power)(); */
1241708Sstevel acebus_intr_ops /* (*bus_intr_op)(); */
1251708Sstevel };
1261708Sstevel
1271708Sstevel static struct dev_ops acebus_ops = {
1281708Sstevel DEVO_REV,
1291708Sstevel 0,
1301708Sstevel ddi_no_info,
1311708Sstevel nulldev,
1321708Sstevel nulldev,
1331708Sstevel acebus_attach,
1341708Sstevel acebus_detach,
1351708Sstevel nodev,
1361708Sstevel (struct cb_ops *)0,
137*7656SSherry.Moore@Sun.COM &acebus_bus_ops,
138*7656SSherry.Moore@Sun.COM NULL,
139*7656SSherry.Moore@Sun.COM ddi_quiesce_not_supported, /* devo_quiesce */
1401708Sstevel };
1411708Sstevel
1421708Sstevel /*
1431708Sstevel * module definitions:
1441708Sstevel */
1451708Sstevel #include <sys/modctl.h>
1461708Sstevel extern struct mod_ops mod_driverops;
1471708Sstevel
1481708Sstevel static struct modldrv modldrv = {
1491708Sstevel &mod_driverops, /* Type of module. This one is a driver */
150*7656SSherry.Moore@Sun.COM "Alarm Card ebus nexus", /* Name of module. */
1511708Sstevel &acebus_ops, /* driver ops */
1521708Sstevel };
1531708Sstevel
1541708Sstevel static struct modlinkage modlinkage = {
1551708Sstevel MODREV_1, (void *)&modldrv, NULL
1561708Sstevel };
1571708Sstevel
1581708Sstevel /*
1591708Sstevel * driver global data:
1601708Sstevel */
1611708Sstevel static void *per_acebus_state; /* per-ebus soft state pointer */
1621708Sstevel
1631708Sstevel
1641708Sstevel int
_init(void)1651708Sstevel _init(void)
1661708Sstevel {
1671708Sstevel int e;
1681708Sstevel
1691708Sstevel /*
1701708Sstevel * Initialize per-ebus soft state pointer.
1711708Sstevel */
1721708Sstevel e = ddi_soft_state_init(&per_acebus_state, sizeof (ebus_devstate_t), 1);
1731708Sstevel if (e != 0)
1741708Sstevel return (e);
1751708Sstevel
1761708Sstevel /*
1771708Sstevel * Install the module.
1781708Sstevel */
1791708Sstevel e = mod_install(&modlinkage);
1801708Sstevel if (e != 0)
1811708Sstevel ddi_soft_state_fini(&per_acebus_state);
1821708Sstevel return (e);
1831708Sstevel }
1841708Sstevel
1851708Sstevel int
_fini(void)1861708Sstevel _fini(void)
1871708Sstevel {
1881708Sstevel int e;
1891708Sstevel
1901708Sstevel /*
1911708Sstevel * Remove the module.
1921708Sstevel */
1931708Sstevel e = mod_remove(&modlinkage);
1941708Sstevel if (e != 0)
1951708Sstevel return (e);
1961708Sstevel
1971708Sstevel /*
1981708Sstevel * Free the soft state info.
1991708Sstevel */
2001708Sstevel ddi_soft_state_fini(&per_acebus_state);
2011708Sstevel return (e);
2021708Sstevel }
2031708Sstevel
2041708Sstevel int
_info(struct modinfo * modinfop)2051708Sstevel _info(struct modinfo *modinfop)
2061708Sstevel {
2071708Sstevel return (mod_info(&modlinkage, modinfop));
2081708Sstevel }
2091708Sstevel
2101708Sstevel /* device driver entry points */
2111708Sstevel
2121708Sstevel /*
2131708Sstevel * attach entry point:
2141708Sstevel *
2151708Sstevel * normal attach:
2161708Sstevel *
2171708Sstevel * create soft state structure (dip, reg, nreg and state fields)
2181708Sstevel * map in configuration header
2191708Sstevel * make sure device is properly configured
2201708Sstevel * report device
2211708Sstevel */
2221708Sstevel static int
acebus_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)2231708Sstevel acebus_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
2241708Sstevel {
2251708Sstevel ebus_devstate_t *ebus_p; /* per ebus state pointer */
2261708Sstevel int instance;
2271708Sstevel
2281708Sstevel DBG1(D_ATTACH, NULL, "dip=%x\n", dip);
2291708Sstevel switch (cmd) {
2301708Sstevel case DDI_ATTACH:
2311708Sstevel
2321708Sstevel /*
2331708Sstevel * Allocate soft state for this instance.
2341708Sstevel */
2351708Sstevel instance = ddi_get_instance(dip);
2361708Sstevel if (ddi_soft_state_zalloc(per_acebus_state, instance)
237*7656SSherry.Moore@Sun.COM != DDI_SUCCESS) {
2381708Sstevel DBG(D_ATTACH, NULL, "failed to alloc soft state\n");
2391708Sstevel return (DDI_FAILURE);
2401708Sstevel }
2411708Sstevel ebus_p = get_acebus_soft_state(instance);
2421708Sstevel ebus_p->dip = dip;
2431708Sstevel
2441708Sstevel /*
2451708Sstevel * Make sure the master enable and memory access enable
2461708Sstevel * bits are set in the config command register.
2471708Sstevel */
2481708Sstevel if (!acebus_config(ebus_p)) {
2491708Sstevel free_acebus_soft_state(instance);
2501708Sstevel return (DDI_FAILURE);
2511708Sstevel }
2521708Sstevel
2531708Sstevel (void) ddi_prop_create(DDI_DEV_T_NONE, dip,
254*7656SSherry.Moore@Sun.COM DDI_PROP_CANSLEEP, "no-dma-interrupt-sync", NULL, 0);
2551708Sstevel /* Get our ranges property for mapping child registers. */
2561708Sstevel if (acebus_get_ranges_prop(ebus_p) != DDI_SUCCESS) {
2571708Sstevel free_acebus_soft_state(instance);
2581708Sstevel return (DDI_FAILURE);
2591708Sstevel }
2601708Sstevel
2611708Sstevel /*
2621708Sstevel * Make the state as attached and report the device.
2631708Sstevel */
2641708Sstevel ebus_p->state = ATTACHED;
2651708Sstevel ddi_report_dev(dip);
2661708Sstevel DBG(D_ATTACH, ebus_p, "returning\n");
2671708Sstevel return (DDI_SUCCESS);
2681708Sstevel
2691708Sstevel case DDI_RESUME:
2701708Sstevel
2711708Sstevel instance = ddi_get_instance(dip);
2721708Sstevel ebus_p = get_acebus_soft_state(instance);
2731708Sstevel
2741708Sstevel /*
2751708Sstevel * Make sure the master enable and memory access enable
2761708Sstevel * bits are set in the config command register.
2771708Sstevel */
2781708Sstevel if (!acebus_config(ebus_p)) {
2791708Sstevel free_acebus_soft_state(instance);
2801708Sstevel return (DDI_FAILURE);
2811708Sstevel }
2821708Sstevel
2831708Sstevel ebus_p->state = RESUMED;
2841708Sstevel return (DDI_SUCCESS);
2851708Sstevel }
2861708Sstevel return (DDI_FAILURE);
2871708Sstevel }
2881708Sstevel
2891708Sstevel /*
2901708Sstevel * detach entry point:
2911708Sstevel */
2921708Sstevel static int
acebus_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)2931708Sstevel acebus_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
2941708Sstevel {
2951708Sstevel int instance = ddi_get_instance(dip);
2961708Sstevel ebus_devstate_t *ebus_p = get_acebus_soft_state(instance);
2971708Sstevel
2981708Sstevel switch (cmd) {
2991708Sstevel case DDI_DETACH:
3001708Sstevel DBG1(D_DETACH, ebus_p, "DDI_DETACH dip=%p\n", dip);
3011708Sstevel ddi_prop_remove_all(dip);
3021708Sstevel kmem_free(ebus_p->rangep, ebus_p->range_cnt *
3031708Sstevel sizeof (struct ebus_pci_rangespec));
3041708Sstevel free_acebus_soft_state(instance);
3051708Sstevel return (DDI_SUCCESS);
3061708Sstevel
3071708Sstevel case DDI_SUSPEND:
3081708Sstevel DBG1(D_DETACH, ebus_p, "DDI_SUSPEND dip=%p\n", dip);
3091708Sstevel ebus_p->state = SUSPENDED;
3101708Sstevel return (DDI_SUCCESS);
3111708Sstevel }
3121708Sstevel return (DDI_FAILURE);
3131708Sstevel }
3141708Sstevel
3151708Sstevel
3161708Sstevel static int
acebus_get_ranges_prop(ebus_devstate_t * ebus_p)3171708Sstevel acebus_get_ranges_prop(ebus_devstate_t *ebus_p)
3181708Sstevel {
3191708Sstevel struct ebus_pci_rangespec *rangep;
3201708Sstevel int nrange, range_len;
3211708Sstevel
3221708Sstevel if (ddi_getlongprop(DDI_DEV_T_ANY, ebus_p->dip, DDI_PROP_DONTPASS,
3231708Sstevel "ranges", (caddr_t)&rangep, &range_len) != DDI_SUCCESS) {
3241708Sstevel
3251708Sstevel cmn_err(CE_WARN, "%s%d: can't get ranges property",
3261708Sstevel ddi_get_name(ebus_p->dip), ddi_get_instance(ebus_p->dip));
3271708Sstevel return (DDI_ME_REGSPEC_RANGE);
3281708Sstevel }
3291708Sstevel
3301708Sstevel nrange = range_len / sizeof (struct ebus_pci_rangespec);
3311708Sstevel
3321708Sstevel if (nrange == 0) {
3331708Sstevel kmem_free(rangep, range_len);
3341708Sstevel return (DDI_FAILURE);
3351708Sstevel }
3361708Sstevel
3371708Sstevel #ifdef DEBUG
338*7656SSherry.Moore@Sun.COM {
339*7656SSherry.Moore@Sun.COM int i;
3401708Sstevel
341*7656SSherry.Moore@Sun.COM for (i = 0; i < nrange; i++) {
342*7656SSherry.Moore@Sun.COM DBG5(D_MAP, ebus_p,
343*7656SSherry.Moore@Sun.COM "ebus range addr 0x%x.0x%x PCI range "
344*7656SSherry.Moore@Sun.COM "addr 0x%x.0x%x.0x%x ", rangep[i].ebus_phys_hi,
3451708Sstevel rangep[i].ebus_phys_low, rangep[i].pci_phys_hi,
3461708Sstevel rangep[i].pci_phys_mid, rangep[i].pci_phys_low);
347*7656SSherry.Moore@Sun.COM DBG1(D_MAP, ebus_p, "Size 0x%x\n", rangep[i].rng_size);
348*7656SSherry.Moore@Sun.COM }
3491708Sstevel }
3501708Sstevel #endif /* DEBUG */
3511708Sstevel
3521708Sstevel ebus_p->rangep = rangep;
3531708Sstevel ebus_p->range_cnt = nrange;
3541708Sstevel
3551708Sstevel return (DDI_SUCCESS);
3561708Sstevel }
3571708Sstevel
3581708Sstevel
3591708Sstevel /* bus driver entry points */
3601708Sstevel
3611708Sstevel /*
3621708Sstevel * bus map entry point:
3631708Sstevel *
3641708Sstevel * if map request is for an rnumber
3651708Sstevel * get the corresponding regspec from device node
3661708Sstevel * build a new regspec in our parent's format
3671708Sstevel * build a new map_req with the new regspec
3681708Sstevel * call up the tree to complete the mapping
3691708Sstevel */
3701708Sstevel static int
acebus_map(dev_info_t * dip,dev_info_t * rdip,ddi_map_req_t * mp,off_t off,off_t len,caddr_t * addrp)3711708Sstevel acebus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
3721708Sstevel off_t off, off_t len, caddr_t *addrp)
3731708Sstevel {
3741708Sstevel ebus_devstate_t *ebus_p = get_acebus_soft_state(ddi_get_instance(dip));
3751708Sstevel ebus_regspec_t *ebus_rp, *ebus_regs;
3761708Sstevel pci_regspec_t pci_reg;
3771708Sstevel ddi_map_req_t p_map_request;
3781708Sstevel int rnumber, i, n;
3791708Sstevel int rval = DDI_SUCCESS;
3801708Sstevel
3811708Sstevel /*
3821708Sstevel * Handle the mapping according to its type.
3831708Sstevel */
3841708Sstevel DBG4(D_MAP, ebus_p, "rdip=%s%d: off=%x len=%x\n",
3851708Sstevel ddi_get_name(rdip), ddi_get_instance(rdip), off, len);
3861708Sstevel switch (mp->map_type) {
3871708Sstevel case DDI_MT_REGSPEC:
3881708Sstevel
3891708Sstevel /*
3901708Sstevel * We assume the register specification is in ebus format.
3911708Sstevel * We must convert it into a PCI format regspec and pass
3921708Sstevel * the request to our parent.
3931708Sstevel */
3941708Sstevel DBG3(D_MAP, ebus_p, "rdip=%s%d: REGSPEC - handlep=%x\n",
395*7656SSherry.Moore@Sun.COM ddi_get_name(rdip), ddi_get_instance(rdip),
396*7656SSherry.Moore@Sun.COM mp->map_handlep);
3971708Sstevel ebus_rp = (ebus_regspec_t *)mp->map_obj.rp;
3981708Sstevel break;
3991708Sstevel
4001708Sstevel case DDI_MT_RNUMBER:
4011708Sstevel
4021708Sstevel /*
4031708Sstevel * Get the "reg" property from the device node and convert
4041708Sstevel * it to our parent's format.
4051708Sstevel */
4061708Sstevel rnumber = mp->map_obj.rnumber;
4071708Sstevel DBG4(D_MAP, ebus_p, "rdip=%s%d: rnumber=%x handlep=%x\n",
408*7656SSherry.Moore@Sun.COM ddi_get_name(rdip), ddi_get_instance(rdip),
409*7656SSherry.Moore@Sun.COM rnumber, mp->map_handlep);
4101708Sstevel
4111708Sstevel if (getprop(rdip, "reg", &ebus_regs, &i) != DDI_SUCCESS) {
4121708Sstevel DBG(D_MAP, ebus_p, "can't get reg property\n");
4131708Sstevel return (DDI_ME_RNUMBER_RANGE);
4141708Sstevel }
4151708Sstevel n = i / sizeof (ebus_regspec_t);
4161708Sstevel
4171708Sstevel if (rnumber < 0 || rnumber >= n) {
4181708Sstevel DBG(D_MAP, ebus_p, "rnumber out of range\n");
4191708Sstevel return (DDI_ME_RNUMBER_RANGE);
4201708Sstevel }
4211708Sstevel ebus_rp = &ebus_regs[rnumber];
4221708Sstevel break;
4231708Sstevel
4241708Sstevel default:
4251708Sstevel return (DDI_ME_INVAL);
4261708Sstevel
4271708Sstevel }
4281708Sstevel
4291708Sstevel /* Adjust our reg property with offset and length */
4301708Sstevel ebus_rp->addr_low += off;
4311708Sstevel if (len)
4321708Sstevel ebus_rp->size = len;
4331708Sstevel
4341708Sstevel /*
4351708Sstevel * Now we have a copy the "reg" entry we're attempting to map.
4361708Sstevel * Translate this into our parents PCI address using the ranges
4371708Sstevel * property.
4381708Sstevel */
4391708Sstevel rval = acebus_apply_range(ebus_p, rdip, ebus_rp, &pci_reg);
4401708Sstevel
4411708Sstevel if (mp->map_type == DDI_MT_RNUMBER)
4421708Sstevel kmem_free((caddr_t)ebus_regs, i);
4431708Sstevel
4441708Sstevel if (rval != DDI_SUCCESS)
4451708Sstevel return (rval);
4461708Sstevel
4471708Sstevel #ifdef ACEBUS_HOTPLUG
4481708Sstevel /*
4491708Sstevel * The map operation provides a translated (not a re-assigned, or
4501708Sstevel * relocated) ebus address for the child in its address space(range).
4511708Sstevel * Ebus address space is relocatible but its child address space
4521708Sstevel * is not. As specified by their 'reg' properties, they reside
4531708Sstevel * at a fixed offset in their parent's (ebus's) space.
4541708Sstevel *
4551708Sstevel * By setting this bit, we will not run into HostPCI nexus
4561708Sstevel * trying to relocate a translated ebus address (which is already
4571708Sstevel * relocated) and failing the operation.
4581708Sstevel * The reason for doing this here is that the PCI hotplug configurator
4591708Sstevel * always marks the ebus space as relocatible (unlike OBP) and that
4601708Sstevel * information is implied for the child too, which is wrong.
4611708Sstevel */
4621708Sstevel pci_reg.pci_phys_hi |= PCI_RELOCAT_B;
4631708Sstevel #endif
4641708Sstevel #ifdef DEBUG
4651708Sstevel DBG5(D_MAP, ebus_p, "(%x,%x,%x)(%x,%x)\n",
466*7656SSherry.Moore@Sun.COM pci_reg.pci_phys_hi,
467*7656SSherry.Moore@Sun.COM pci_reg.pci_phys_mid,
468*7656SSherry.Moore@Sun.COM pci_reg.pci_phys_low,
469*7656SSherry.Moore@Sun.COM pci_reg.pci_size_hi,
470*7656SSherry.Moore@Sun.COM pci_reg.pci_size_low);
4711708Sstevel #endif
4721708Sstevel
4731708Sstevel p_map_request = *mp;
4741708Sstevel p_map_request.map_type = DDI_MT_REGSPEC;
4751708Sstevel p_map_request.map_obj.rp = (struct regspec *)&pci_reg;
4761708Sstevel rval = ddi_map(dip, &p_map_request, 0, 0, addrp);
4771708Sstevel DBG1(D_MAP, ebus_p, "parent returned %x\n", rval);
4781708Sstevel return (rval);
4791708Sstevel }
4801708Sstevel
4811708Sstevel
4821708Sstevel static int
acebus_apply_range(ebus_devstate_t * ebus_p,dev_info_t * rdip,ebus_regspec_t * ebus_rp,pci_regspec_t * rp)4831708Sstevel acebus_apply_range(ebus_devstate_t *ebus_p, dev_info_t *rdip,
4841708Sstevel ebus_regspec_t *ebus_rp, pci_regspec_t *rp)
4851708Sstevel {
4861708Sstevel int b;
4871708Sstevel int rval = DDI_SUCCESS;
4881708Sstevel struct ebus_pci_rangespec *rangep = ebus_p->rangep;
4891708Sstevel int nrange = ebus_p->range_cnt;
4901708Sstevel static const char out_of_range[] =
4911708Sstevel "Out of range register specification from device node <%s>";
4921708Sstevel
4931708Sstevel DBG3(D_MAP, ebus_p, "Range Matching Addr 0x%x.%x size 0x%x\n",
4941708Sstevel ebus_rp->addr_hi, ebus_rp->addr_low, ebus_rp->size);
4951708Sstevel
4961708Sstevel for (b = 0; b < nrange; ++b, ++rangep) {
4971708Sstevel
4981708Sstevel /* Check for the correct space */
4991708Sstevel if (ebus_rp->addr_hi == rangep->ebus_phys_hi)
5001708Sstevel /* See if we fit in this range */
5011708Sstevel if ((ebus_rp->addr_low >=
5021708Sstevel rangep->ebus_phys_low) &&
5031708Sstevel ((ebus_rp->addr_low + ebus_rp->size - 1)
504*7656SSherry.Moore@Sun.COM <= (rangep->ebus_phys_low +
505*7656SSherry.Moore@Sun.COM rangep->rng_size - 1))) {
5061708Sstevel uint_t addr_offset = ebus_rp->addr_low -
5071708Sstevel rangep->ebus_phys_low;
5081708Sstevel /*
5091708Sstevel * Use the range entry to translate
5101708Sstevel * the EBUS physical address into the
5111708Sstevel * parents PCI space.
5121708Sstevel */
5131708Sstevel rp->pci_phys_hi =
5141708Sstevel rangep->pci_phys_hi;
5151708Sstevel rp->pci_phys_mid = rangep->pci_phys_mid;
5161708Sstevel rp->pci_phys_low =
5171708Sstevel rangep->pci_phys_low + addr_offset;
5181708Sstevel rp->pci_size_hi = 0;
5191708Sstevel rp->pci_size_low =
5201708Sstevel min(ebus_rp->size, (rangep->rng_size -
521*7656SSherry.Moore@Sun.COM addr_offset));
5221708Sstevel
5231708Sstevel DBG2(D_MAP, ebus_p, "Child hi0x%x lo0x%x ",
5241708Sstevel rangep->ebus_phys_hi,
5251708Sstevel rangep->ebus_phys_low);
5261708Sstevel DBG4(D_MAP, ebus_p, "Parent hi0x%x "
527*7656SSherry.Moore@Sun.COM "mid0x%x lo0x%x size 0x%x\n",
528*7656SSherry.Moore@Sun.COM rangep->pci_phys_hi,
529*7656SSherry.Moore@Sun.COM rangep->pci_phys_mid,
530*7656SSherry.Moore@Sun.COM rangep->pci_phys_low,
531*7656SSherry.Moore@Sun.COM rangep->rng_size);
5321708Sstevel
5331708Sstevel break;
5341708Sstevel }
5351708Sstevel }
5361708Sstevel
5371708Sstevel if (b == nrange) {
5381708Sstevel cmn_err(CE_WARN, out_of_range, ddi_get_name(rdip));
5391708Sstevel return (DDI_ME_REGSPEC_RANGE);
5401708Sstevel }
5411708Sstevel
5421708Sstevel return (rval);
5431708Sstevel }
5441708Sstevel
5451708Sstevel
5461708Sstevel /*
5471708Sstevel * control ops entry point:
5481708Sstevel *
5491708Sstevel * Requests handled completely:
5501708Sstevel * DDI_CTLOPS_INITCHILD
5511708Sstevel * DDI_CTLOPS_UNINITCHILD
5521708Sstevel * DDI_CTLOPS_REPORTDEV
5531708Sstevel * DDI_CTLOPS_REGSIZE
5541708Sstevel * DDI_CTLOPS_NREGS
5551708Sstevel *
5561708Sstevel * All others passed to parent.
5571708Sstevel */
5581708Sstevel static int
acebus_ctlops(dev_info_t * dip,dev_info_t * rdip,ddi_ctl_enum_t op,void * arg,void * result)5591708Sstevel acebus_ctlops(dev_info_t *dip, dev_info_t *rdip,
5601708Sstevel ddi_ctl_enum_t op, void *arg, void *result)
5611708Sstevel {
5621708Sstevel #ifdef DEBUG
5631708Sstevel ebus_devstate_t *ebus_p = get_acebus_soft_state(ddi_get_instance(dip));
5641708Sstevel #endif
5651708Sstevel ebus_regspec_t *ebus_rp;
5661708Sstevel int32_t reglen;
5671708Sstevel int i, n;
5681708Sstevel char name[10];
5691708Sstevel
5701708Sstevel switch (op) {
5711708Sstevel case DDI_CTLOPS_INITCHILD: {
5721708Sstevel dev_info_t *child = (dev_info_t *)arg;
5731708Sstevel /*
5741708Sstevel * Set the address portion of the node name based on the
5751708Sstevel * address/offset.
5761708Sstevel */
5771708Sstevel DBG2(D_CTLOPS, ebus_p, "DDI_CTLOPS_INITCHILD: rdip=%s%d\n",
5781708Sstevel ddi_get_name(child), ddi_get_instance(child));
5791708Sstevel
5801708Sstevel if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
5811708Sstevel "reg", (caddr_t)&ebus_rp, ®len) != DDI_SUCCESS) {
5821708Sstevel
5831708Sstevel DBG(D_CTLOPS, ebus_p, "can't get reg property\n");
5841708Sstevel return (DDI_FAILURE);
5851708Sstevel
5861708Sstevel }
5871708Sstevel
5881708Sstevel (void) sprintf(name, "%x,%x", ebus_rp->addr_hi,
5891708Sstevel ebus_rp->addr_low);
5901708Sstevel ddi_set_name_addr(child, name);
5911708Sstevel kmem_free((caddr_t)ebus_rp, reglen);
5921708Sstevel
5931708Sstevel ddi_set_parent_data(child, NULL);
5941708Sstevel
5951708Sstevel return (DDI_SUCCESS);
5961708Sstevel
5971708Sstevel }
5981708Sstevel
5991708Sstevel case DDI_CTLOPS_UNINITCHILD:
6001708Sstevel DBG2(D_CTLOPS, ebus_p, "DDI_CTLOPS_UNINITCHILD: rdip=%s%d\n",
601*7656SSherry.Moore@Sun.COM ddi_get_name((dev_info_t *)arg),
602*7656SSherry.Moore@Sun.COM ddi_get_instance((dev_info_t *)arg));
6031708Sstevel ddi_set_name_addr((dev_info_t *)arg, NULL);
6041708Sstevel ddi_remove_minor_node((dev_info_t *)arg, NULL);
6051708Sstevel impl_rem_dev_props((dev_info_t *)arg);
6061708Sstevel return (DDI_SUCCESS);
6071708Sstevel
6081708Sstevel case DDI_CTLOPS_REPORTDEV:
6091708Sstevel
6101708Sstevel DBG2(D_CTLOPS, ebus_p, "DDI_CTLOPS_REPORTDEV: rdip=%s%d\n",
611*7656SSherry.Moore@Sun.COM ddi_get_name(rdip), ddi_get_instance(rdip));
6121708Sstevel cmn_err(CE_CONT, "?%s%d at %s%d: offset %s\n",
613*7656SSherry.Moore@Sun.COM ddi_driver_name(rdip), ddi_get_instance(rdip),
614*7656SSherry.Moore@Sun.COM ddi_driver_name(dip), ddi_get_instance(dip),
615*7656SSherry.Moore@Sun.COM ddi_get_name_addr(rdip));
6161708Sstevel return (DDI_SUCCESS);
6171708Sstevel
6181708Sstevel case DDI_CTLOPS_REGSIZE:
6191708Sstevel
6201708Sstevel DBG2(D_CTLOPS, ebus_p, "DDI_CTLOPS_REGSIZE: rdip=%s%d\n",
621*7656SSherry.Moore@Sun.COM ddi_get_name(rdip), ddi_get_instance(rdip));
6221708Sstevel if (getprop(rdip, "reg", &ebus_rp, &i) != DDI_SUCCESS) {
6231708Sstevel DBG(D_CTLOPS, ebus_p, "can't get reg property\n");
6241708Sstevel return (DDI_FAILURE);
6251708Sstevel }
6261708Sstevel n = i / sizeof (ebus_regspec_t);
6271708Sstevel if (*(int *)arg < 0 || *(int *)arg >= n) {
6281708Sstevel DBG(D_MAP, ebus_p, "rnumber out of range\n");
6291708Sstevel kmem_free((caddr_t)ebus_rp, i);
6301708Sstevel return (DDI_FAILURE);
6311708Sstevel }
6321708Sstevel *((off_t *)result) = ebus_rp[*(int *)arg].size;
6331708Sstevel kmem_free((caddr_t)ebus_rp, i);
6341708Sstevel return (DDI_SUCCESS);
6351708Sstevel
6361708Sstevel case DDI_CTLOPS_NREGS:
6371708Sstevel
6381708Sstevel DBG2(D_CTLOPS, ebus_p, "DDI_CTLOPS_NREGS: rdip=%s%d\n",
639*7656SSherry.Moore@Sun.COM ddi_get_name(rdip), ddi_get_instance(rdip));
6401708Sstevel if (getprop(rdip, "reg", &ebus_rp, &i) != DDI_SUCCESS) {
6411708Sstevel DBG(D_CTLOPS, ebus_p, "can't get reg property\n");
6421708Sstevel return (DDI_FAILURE);
6431708Sstevel }
6441708Sstevel *((uint_t *)result) = i / sizeof (ebus_regspec_t);
6451708Sstevel kmem_free((caddr_t)ebus_rp, i);
6461708Sstevel return (DDI_SUCCESS);
6471708Sstevel }
6481708Sstevel
6491708Sstevel /*
6501708Sstevel * Now pass the request up to our parent.
6511708Sstevel */
6521708Sstevel DBG2(D_CTLOPS, ebus_p, "passing request to parent: rdip=%s%d\n",
653*7656SSherry.Moore@Sun.COM ddi_get_name(rdip), ddi_get_instance(rdip));
6541708Sstevel return (ddi_ctlops(dip, rdip, op, arg, result));
6551708Sstevel }
6561708Sstevel
6571708Sstevel struct ebus_string_to_pil {
6581708Sstevel int8_t *string;
6591708Sstevel uint32_t pil;
6601708Sstevel };
6611708Sstevel
6621708Sstevel static struct ebus_string_to_pil acebus_name_to_pil[] = {{"SUNW,CS4231", 9},
6631708Sstevel {"fdthree", 8},
6641708Sstevel {"ecpp", 3},
6651708Sstevel {"su", 12},
6661708Sstevel {"se", 12},
6671708Sstevel {"power", 14}};
6681708Sstevel
6691708Sstevel static struct ebus_string_to_pil acebus_device_type_to_pil[] = {{"serial", 12},
6701708Sstevel {"block", 8}};
6711708Sstevel
6721708Sstevel static int
acebus_intr_ops(dev_info_t * dip,dev_info_t * rdip,ddi_intr_op_t intr_op,ddi_intr_handle_impl_t * hdlp,void * result)6731708Sstevel acebus_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op,
6741708Sstevel ddi_intr_handle_impl_t *hdlp, void *result)
6751708Sstevel {
6761708Sstevel #ifdef DEBUG
6771708Sstevel ebus_devstate_t *ebus_p = get_acebus_soft_state(ddi_get_instance(dip));
6781708Sstevel #endif
6791708Sstevel int8_t *name, *device_type;
6801708Sstevel int32_t i, max_children, max_device_types, len;
6811708Sstevel
6821708Sstevel /*
6831708Sstevel * NOTE: These ops below will never be supported in this nexus
6841708Sstevel * driver, hence they always return immediately.
6851708Sstevel */
6861708Sstevel switch (intr_op) {
6871708Sstevel case DDI_INTROP_GETCAP:
6881708Sstevel *(int *)result = DDI_INTR_FLAG_LEVEL;
6891708Sstevel return (DDI_SUCCESS);
6903920Srameshc case DDI_INTROP_SUPPORTED_TYPES:
6913920Srameshc *(int *)result = i_ddi_get_intx_nintrs(rdip) ?
6923920Srameshc DDI_INTR_TYPE_FIXED : 0;
6933920Srameshc return (DDI_SUCCESS);
6941708Sstevel case DDI_INTROP_SETCAP:
6951708Sstevel case DDI_INTROP_SETMASK:
6961708Sstevel case DDI_INTROP_CLRMASK:
6971708Sstevel case DDI_INTROP_GETPENDING:
6981708Sstevel return (DDI_ENOTSUP);
6991708Sstevel default:
7001708Sstevel break;
7011708Sstevel }
7021708Sstevel
7033920Srameshc if (hdlp->ih_pri)
7041708Sstevel goto done;
7051708Sstevel
7061708Sstevel /*
7071708Sstevel * This is a hack to set the PIL for the devices under ebus.
7081708Sstevel * We first look up a device by it's specific name, if we can't
7091708Sstevel * match the name, we try and match it's device_type property.
7101708Sstevel * Lastly we default a PIL level of 1.
7111708Sstevel */
7121708Sstevel DBG1(D_INTR, ebus_p, "ebus_p %p\n", ebus_p);
7131708Sstevel
7141708Sstevel name = ddi_get_name(rdip);
7151708Sstevel max_children = sizeof (acebus_name_to_pil) /
7161708Sstevel sizeof (struct ebus_string_to_pil);
7171708Sstevel
7181708Sstevel for (i = 0; i < max_children; i++) {
7191708Sstevel if (strcmp(acebus_name_to_pil[i].string, name) == 0) {
7201708Sstevel DBG2(D_INTR, ebus_p, "child name %s; match PIL %d\n",
7211708Sstevel acebus_name_to_pil[i].string,
7221708Sstevel acebus_name_to_pil[i].pil);
7231708Sstevel
7241708Sstevel hdlp->ih_pri = acebus_name_to_pil[i].pil;
7251708Sstevel goto done;
7261708Sstevel }
7271708Sstevel }
7281708Sstevel
7291708Sstevel if (ddi_getlongprop(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS,
7301708Sstevel "device_type", (caddr_t)&device_type, &len) == DDI_SUCCESS) {
7311708Sstevel
7321708Sstevel max_device_types = sizeof (acebus_device_type_to_pil) /
7331708Sstevel sizeof (struct ebus_string_to_pil);
7341708Sstevel
7351708Sstevel for (i = 0; i < max_device_types; i++) {
7361708Sstevel if (strcmp(acebus_device_type_to_pil[i].string,
7371708Sstevel device_type) == 0) {
7381708Sstevel DBG2(D_INTR, ebus_p,
7391708Sstevel "Device type %s; match PIL %d\n",
7401708Sstevel acebus_device_type_to_pil[i].string,
7411708Sstevel acebus_device_type_to_pil[i].pil);
7421708Sstevel
7431708Sstevel hdlp->ih_pri = acebus_device_type_to_pil[i].pil;
7441708Sstevel break;
7451708Sstevel }
7461708Sstevel }
7471708Sstevel
7481708Sstevel kmem_free(device_type, len);
7491708Sstevel }
7501708Sstevel
7511708Sstevel /*
7521708Sstevel * If we get here, we need to set a default value
7531708Sstevel * for the PIL.
7541708Sstevel */
7551708Sstevel if (hdlp->ih_pri == 0) {
7561708Sstevel hdlp->ih_pri = 1;
7571708Sstevel cmn_err(CE_WARN, "%s%d assigning default interrupt level %d "
7581708Sstevel "for device %s%d", ddi_driver_name(dip),
7591708Sstevel ddi_get_instance(dip), hdlp->ih_pri, ddi_driver_name(rdip),
7601708Sstevel ddi_get_instance(rdip));
7611708Sstevel }
7621708Sstevel
7631708Sstevel done:
7641708Sstevel /* Pass up the request to our parent. */
7651708Sstevel return (i_ddi_intr_ops(dip, rdip, intr_op, hdlp, result));
7661708Sstevel }
7671708Sstevel
7681708Sstevel
7691708Sstevel static int
acebus_config(ebus_devstate_t * ebus_p)7701708Sstevel acebus_config(ebus_devstate_t *ebus_p)
7711708Sstevel {
7721708Sstevel ddi_acc_handle_t conf_handle;
7731708Sstevel uint16_t comm;
7741708Sstevel #ifdef ACEBUS_HOTPLUG
7751708Sstevel int tcr_reg;
7761708Sstevel caddr_t csr_io;
7771708Sstevel ddi_device_acc_attr_t csr_attr = { /* CSR map attributes */
7781708Sstevel DDI_DEVICE_ATTR_V0,
7791708Sstevel DDI_STRUCTURE_LE_ACC,
7801708Sstevel DDI_STRICTORDER_ACC
7811708Sstevel };
7821708Sstevel ddi_acc_handle_t csr_handle;
7831708Sstevel #endif
7841708Sstevel
7851708Sstevel /*
7861708Sstevel * Make sure the master enable and memory access enable
7871708Sstevel * bits are set in the config command register.
7881708Sstevel */
7891708Sstevel if (pci_config_setup(ebus_p->dip, &conf_handle) != DDI_SUCCESS)
7901708Sstevel return (0);
7911708Sstevel
7921708Sstevel comm = pci_config_get16(conf_handle, PCI_CONF_COMM),
7931708Sstevel #ifdef DEBUG
7941708Sstevel DBG1(D_ATTACH, ebus_p, "command register was 0x%x\n", comm);
7951708Sstevel #endif
7961708Sstevel comm |= (PCI_COMM_ME|PCI_COMM_MAE|PCI_COMM_SERR_ENABLE|
7971708Sstevel PCI_COMM_PARITY_DETECT);
7981708Sstevel pci_config_put16(conf_handle, PCI_CONF_COMM, comm),
7991708Sstevel #ifdef DEBUG
8001708Sstevel DBG1(D_MAP, ebus_p, "command register is now 0x%x\n",
801*7656SSherry.Moore@Sun.COM pci_config_get16(conf_handle, PCI_CONF_COMM));
8021708Sstevel #endif
8031708Sstevel pci_config_put8(conf_handle, PCI_CONF_CACHE_LINESZ,
8041708Sstevel (uchar_t)acebus_cache_line_size);
8051708Sstevel pci_config_put8(conf_handle, PCI_CONF_LATENCY_TIMER,
8061708Sstevel (uchar_t)acebus_latency_timer);
8071708Sstevel pci_config_teardown(&conf_handle);
8081708Sstevel
8091708Sstevel #ifdef ACEBUS_HOTPLUG
8101708Sstevel if (acebus_update_props(ebus_p) != DDI_SUCCESS) {
8111708Sstevel cmn_err(CE_WARN, "%s%d: Could not update special properties.",
8121708Sstevel ddi_driver_name(ebus_p->dip),
8131708Sstevel ddi_get_instance(ebus_p->dip));
8141708Sstevel return (0);
8151708Sstevel }
8161708Sstevel
8171708Sstevel if (ddi_regs_map_setup(ebus_p->dip, CSR_IO_RINDEX,
8181708Sstevel (caddr_t *)&csr_io, 0, CSR_SIZE, &csr_attr,
8191708Sstevel &csr_handle) != DDI_SUCCESS) {
8201708Sstevel cmn_err(CE_WARN, "%s%d: Could not map Ebus CSR.",
8211708Sstevel ddi_driver_name(ebus_p->dip),
8221708Sstevel ddi_get_instance(ebus_p->dip));
8231708Sstevel }
8241708Sstevel #ifdef DEBUG
8251708Sstevel if (acebus_debug_flags) {
8261708Sstevel DBG3(D_ATTACH, ebus_p, "tcr[123] = %x,%x,%x\n",
827*7656SSherry.Moore@Sun.COM ddi_get32(csr_handle, (uint32_t *)((caddr_t)csr_io +
828*7656SSherry.Moore@Sun.COM TCR1_OFF)),
829*7656SSherry.Moore@Sun.COM ddi_get32(csr_handle, (uint32_t *)((caddr_t)csr_io +
830*7656SSherry.Moore@Sun.COM TCR2_OFF)),
831*7656SSherry.Moore@Sun.COM ddi_get32(csr_handle, (uint32_t *)((caddr_t)csr_io +
832*7656SSherry.Moore@Sun.COM TCR3_OFF)));
8331708Sstevel DBG2(D_ATTACH, ebus_p, "pmd-aux=%x, freq-aux=%x\n",
834*7656SSherry.Moore@Sun.COM ddi_get32(csr_handle, (uint32_t *)((caddr_t)csr_io +
835*7656SSherry.Moore@Sun.COM PMD_AUX_OFF)),
836*7656SSherry.Moore@Sun.COM ddi_get32(csr_handle, (uint32_t *)((caddr_t)csr_io +
837*7656SSherry.Moore@Sun.COM FREQ_AUX_OFF)));
8381708Sstevel #ifdef ACEBUS_DEBUG
8391708Sstevel for (comm = 0; comm < 4; comm++)
8401708Sstevel prom_printf("dcsr%d=%x, dacr%d=%x, dbcr%d=%x\n", comm,
841*7656SSherry.Moore@Sun.COM ddi_get32(csr_handle, (uint32_t *)((caddr_t)csr_io +
842*7656SSherry.Moore@Sun.COM 0x700000+(0x2000*comm))), comm,
843*7656SSherry.Moore@Sun.COM ddi_get32(csr_handle, (uint32_t *)((caddr_t)csr_io +
844*7656SSherry.Moore@Sun.COM 0x700000+(0x2000*comm)+4)), comm,
845*7656SSherry.Moore@Sun.COM ddi_get32(csr_handle, (uint32_t *)((caddr_t)csr_io +
846*7656SSherry.Moore@Sun.COM 0x700000+(0x2000*comm)+8)));
8471708Sstevel #endif
8481708Sstevel } /* acebus_debug_flags */
8491708Sstevel #endif
8501708Sstevel /* If TCR registers are not initialized, initialize them here */
8511708Sstevel tcr_reg = ddi_get32(csr_handle, (uint32_t *)((caddr_t)csr_io +
852*7656SSherry.Moore@Sun.COM TCR1_OFF));
8531708Sstevel if ((tcr_reg == 0) || (tcr_reg == -1))
8541708Sstevel ddi_put32(csr_handle, (uint32_t *)((caddr_t)csr_io + TCR1_OFF),
855*7656SSherry.Moore@Sun.COM TCR1_REGVAL);
8561708Sstevel tcr_reg = ddi_get32(csr_handle, (uint32_t *)((caddr_t)csr_io +
857*7656SSherry.Moore@Sun.COM TCR2_OFF));
8581708Sstevel if ((tcr_reg == 0) || (tcr_reg == -1))
8591708Sstevel ddi_put32(csr_handle, (uint32_t *)((caddr_t)csr_io + TCR2_OFF),
860*7656SSherry.Moore@Sun.COM TCR2_REGVAL);
8611708Sstevel tcr_reg = ddi_get32(csr_handle, (uint32_t *)((caddr_t)csr_io +
862*7656SSherry.Moore@Sun.COM TCR3_OFF));
8631708Sstevel if ((tcr_reg == 0) || (tcr_reg == -1))
8641708Sstevel ddi_put32(csr_handle, (uint32_t *)((caddr_t)csr_io + TCR3_OFF),
865*7656SSherry.Moore@Sun.COM TCR3_REGVAL);
8661708Sstevel #ifdef DEBUG
8671708Sstevel if (acebus_debug_flags) {
8681708Sstevel DBG3(D_ATTACH, ebus_p, "wrote tcr[123] = %x,%x,%x\n",
869*7656SSherry.Moore@Sun.COM ddi_get32(csr_handle, (uint32_t *)((caddr_t)csr_io +
870*7656SSherry.Moore@Sun.COM TCR1_OFF)),
871*7656SSherry.Moore@Sun.COM ddi_get32(csr_handle, (uint32_t *)((caddr_t)csr_io +
872*7656SSherry.Moore@Sun.COM TCR2_OFF)),
873*7656SSherry.Moore@Sun.COM ddi_get32(csr_handle, (uint32_t *)((caddr_t)csr_io +
874*7656SSherry.Moore@Sun.COM TCR3_OFF)));
8751708Sstevel }
8761708Sstevel #endif
8771708Sstevel
8781708Sstevel ddi_regs_map_free(&csr_handle);
8791708Sstevel #endif /* ACEBUS_HOTPLUG */
8801708Sstevel return (1); /* return success */
8811708Sstevel }
8821708Sstevel
8831708Sstevel #ifdef DEBUG
8841708Sstevel extern void prom_printf(const char *, ...);
8851708Sstevel
8861708Sstevel static void
acebus_debug(uint_t flag,ebus_devstate_t * ebus_p,char * fmt,uintptr_t a1,uintptr_t a2,uintptr_t a3,uintptr_t a4,uintptr_t a5)8871708Sstevel acebus_debug(uint_t flag, ebus_devstate_t *ebus_p, char *fmt,
8881708Sstevel uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5)
8891708Sstevel {
8901708Sstevel char *s;
8911708Sstevel
8921708Sstevel if (acebus_debug_flags & flag) {
8931708Sstevel switch (flag) {
8941708Sstevel case D_ATTACH:
8951708Sstevel s = "attach"; break;
8961708Sstevel case D_DETACH:
8971708Sstevel s = "detach"; break;
8981708Sstevel case D_MAP:
8991708Sstevel s = "map"; break;
9001708Sstevel case D_CTLOPS:
9011708Sstevel s = "ctlops"; break;
9021708Sstevel case D_INTR:
9031708Sstevel s = "intr"; break;
9041708Sstevel }
9051708Sstevel if (ebus_p)
9061708Sstevel cmn_err(CE_CONT, "%s%d: %s: ",
907*7656SSherry.Moore@Sun.COM ddi_get_name(ebus_p->dip),
908*7656SSherry.Moore@Sun.COM ddi_get_instance(ebus_p->dip), s);
9091708Sstevel else
9101708Sstevel cmn_err(CE_CONT, "ebus: ");
9111708Sstevel cmn_err(CE_CONT, fmt, a1, a2, a3, a4, a5);
9121708Sstevel }
9131708Sstevel }
9141708Sstevel #endif
9151708Sstevel
9161708Sstevel #ifdef ACEBUS_HOTPLUG
9171708Sstevel #define EBUS_CHILD_PHYS_LOW_RANGE 0x10
9181708Sstevel #define EBUS_CHILD_PHYS_HI_RANGE 0x14
9191708Sstevel
9201708Sstevel static int
acebus_update_props(ebus_devstate_t * ebus_p)9211708Sstevel acebus_update_props(ebus_devstate_t *ebus_p)
9221708Sstevel {
9231708Sstevel dev_info_t *dip = ebus_p->dip;
9241708Sstevel struct ebus_pci_rangespec er[2], *erp;
9251708Sstevel pci_regspec_t *pci_rp, *prp;
9261708Sstevel int length, rnums, imask[3], i, found = 0;
9271708Sstevel
9281708Sstevel /*
9291708Sstevel * If "ranges" property is found, then the device is initialized
9301708Sstevel * by OBP, hence simply return.
9311708Sstevel * Otherwise we create all the properties here.
9321708Sstevel */
9331708Sstevel if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
9341708Sstevel "ranges", (int **)&erp, (uint_t *)&length) == DDI_PROP_SUCCESS) {
9351708Sstevel ddi_prop_free(erp);
9361708Sstevel return (DDI_SUCCESS);
9371708Sstevel }
9381708Sstevel
9391708Sstevel /*
9401708Sstevel * interrupt-map is the only property that comes from a .conf file.
9411708Sstevel * Since it doesn't have the nodeid field set, it must be done here.
9421708Sstevel * Other properties can come from OBP or created here.
9431708Sstevel */
9441708Sstevel if (acebus_set_imap(dip) != DDI_SUCCESS) {
9451708Sstevel return (DDI_FAILURE);
9461708Sstevel }
9471708Sstevel
9481708Sstevel /*
9491708Sstevel * Create the "ranges" property.
9501708Sstevel * Ebus has BAR0 and BAR1 allocated (both in memory space).
9511708Sstevel * Other BARs are 0.
9521708Sstevel * Hence there are 2 memory ranges it operates in. (one for each BAR).
9531708Sstevel * ie. there are 2 entries in its ranges property.
9541708Sstevel */
9551708Sstevel if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip,
956*7656SSherry.Moore@Sun.COM DDI_PROP_DONTPASS, "assigned-addresses",
957*7656SSherry.Moore@Sun.COM (int **)&pci_rp, (uint_t *)&length) != DDI_PROP_SUCCESS) {
9581708Sstevel cmn_err(CE_WARN, "%s%d: Could not get assigned-addresses",
959*7656SSherry.Moore@Sun.COM ddi_driver_name(dip), ddi_get_instance(dip));
9601708Sstevel return (DDI_FAILURE);
9611708Sstevel }
9621708Sstevel /*
9631708Sstevel * Create the 1st mem range in which it operates corresponding
9641708Sstevel * to BAR0
9651708Sstevel */
9661708Sstevel er[0].ebus_phys_hi = EBUS_CHILD_PHYS_LOW_RANGE;
9671708Sstevel rnums = (length * sizeof (int))/sizeof (pci_regspec_t);
9681708Sstevel for (i = 0; i < rnums; i++) {
9691708Sstevel prp = pci_rp + i;
9701708Sstevel if (PCI_REG_REG_G(prp->pci_phys_hi) == er[0].ebus_phys_hi) {
9711708Sstevel found = 1;
9721708Sstevel break;
9731708Sstevel }
9741708Sstevel }
9751708Sstevel if (!found) {
9761708Sstevel cmn_err(CE_WARN, "No assigned space for memory range 0.");
9771708Sstevel ddi_prop_free(pci_rp);
9781708Sstevel return (DDI_FAILURE);
9791708Sstevel }
9801708Sstevel found = 0;
9811708Sstevel er[0].ebus_phys_low = 0;
9821708Sstevel er[0].pci_phys_hi = prp->pci_phys_hi;
9831708Sstevel er[0].pci_phys_mid = prp->pci_phys_mid;
9841708Sstevel er[0].pci_phys_low = prp->pci_phys_low;
9851708Sstevel er[0].rng_size = prp->pci_size_low;
9861708Sstevel
9871708Sstevel /*
9881708Sstevel * Create the 2nd mem range in which it operates corresponding
9891708Sstevel * to BAR1
9901708Sstevel */
9911708Sstevel er[1].ebus_phys_hi = EBUS_CHILD_PHYS_HI_RANGE;
9921708Sstevel for (i = 0; i < rnums; i++) {
9931708Sstevel prp = pci_rp + i;
9941708Sstevel if (PCI_REG_REG_G(prp->pci_phys_hi) == er[1].ebus_phys_hi) {
9951708Sstevel found = 1;
9961708Sstevel break;
9971708Sstevel }
9981708Sstevel }
9991708Sstevel if (!found) {
10001708Sstevel cmn_err(CE_WARN, "No assigned space for memory range 1.");
10011708Sstevel ddi_prop_free(pci_rp);
10021708Sstevel return (DDI_FAILURE);
10031708Sstevel }
10041708Sstevel er[1].ebus_phys_low = 0;
10051708Sstevel er[1].pci_phys_hi = prp->pci_phys_hi;
10061708Sstevel er[1].pci_phys_mid = prp->pci_phys_mid;
10071708Sstevel er[1].pci_phys_low = prp->pci_phys_low;
10081708Sstevel er[1].rng_size = prp->pci_size_low;
10091708Sstevel
10101708Sstevel ddi_prop_free(pci_rp);
10111708Sstevel length = sizeof (er) / sizeof (int);
10121708Sstevel if (ddi_prop_update_int_array(DDI_DEV_T_NONE, dip,
10131708Sstevel "ranges", (int *)er, length) != DDI_PROP_SUCCESS) {
10141708Sstevel cmn_err(CE_WARN, "%s%d: Could not create ranges property",
1015*7656SSherry.Moore@Sun.COM ddi_driver_name(dip), ddi_get_instance(dip));
10161708Sstevel return (DDI_FAILURE);
10171708Sstevel }
10181708Sstevel /* The following properties are as defined by PCI 1275 bindings. */
10191708Sstevel if (ddi_prop_update_int(DDI_DEV_T_NONE, dip,
10201708Sstevel "#address-cells", 2) != DDI_PROP_SUCCESS)
10211708Sstevel return (DDI_FAILURE);
10221708Sstevel if (ddi_prop_update_int(DDI_DEV_T_NONE, dip,
10231708Sstevel "#size-cells", 1) != DDI_PROP_SUCCESS)
10241708Sstevel return (DDI_FAILURE);
10251708Sstevel if (ddi_prop_update_int(DDI_DEV_T_NONE, dip,
10261708Sstevel "#interrupt-cells", 1) != DDI_PROP_SUCCESS)
10271708Sstevel return (DDI_FAILURE);
10281708Sstevel
10291708Sstevel imask[0] = 0x1f;
10301708Sstevel imask[1] = 0x00ffffff;
10311708Sstevel imask[2] = 0x00000003;
10321708Sstevel length = sizeof (imask) / sizeof (int);
10331708Sstevel if (ddi_prop_update_int_array(DDI_DEV_T_NONE, dip,
10341708Sstevel "interrupt-map-mask", (int *)imask, length) != DDI_PROP_SUCCESS) {
10351708Sstevel cmn_err(CE_WARN, "%s%d: Could not update imap mask property",
10361708Sstevel ddi_driver_name(dip), ddi_get_instance(dip));
10371708Sstevel return (DDI_FAILURE);
10381708Sstevel }
10391708Sstevel
10401708Sstevel return (DDI_SUCCESS);
10411708Sstevel }
10421708Sstevel
10431708Sstevel /*
10441708Sstevel * This function takes in the ac-interrupt-map property from the .conf file,
10451708Sstevel * fills in the 'nodeid' information and then creates the 'interrupt-map'
10461708Sstevel * property.
10471708Sstevel */
10481708Sstevel static int
acebus_set_imap(dev_info_t * dip)10491708Sstevel acebus_set_imap(dev_info_t *dip)
10501708Sstevel {
10511708Sstevel int *imapp, *timapp, length, num, i, default_ival = 0;
10521708Sstevel dev_info_t *tdip = dip;
10531708Sstevel int *port_id, imap_ok = 1;
10541708Sstevel int ilength;
10551708Sstevel int acebus_default_se_imap[5];
10561708Sstevel
10571708Sstevel /*
10581708Sstevel * interrupt-map is specified via .conf file in hotplug mode,
10591708Sstevel * since the child configuration is static.
10601708Sstevel * It could even be hardcoded in the driver.
10611708Sstevel */
10621708Sstevel if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
10631708Sstevel "ac-interrupt-map", (int **)&imapp, (uint_t *)&ilength) !=
10641708Sstevel DDI_PROP_SUCCESS) {
10651708Sstevel /* assume default implementation */
10661708Sstevel acebus_default_se_imap[0] = 0x14;
10671708Sstevel acebus_default_se_imap[1] = 0x400000;
10681708Sstevel acebus_default_se_imap[2] = 1;
10691708Sstevel acebus_default_se_imap[3] = 0;
10701708Sstevel acebus_default_se_imap[4] = 2;
10711708Sstevel imapp = acebus_default_se_imap;
10721708Sstevel ilength = 5;
10731708Sstevel default_ival = 1;
10741708Sstevel }
10751708Sstevel num = ilength / 5; /* there are 5 integer cells in our property */
10761708Sstevel timapp = imapp;
10771708Sstevel for (i = 0; i < num; i++) {
10781708Sstevel if (*(timapp+i*5+3) == 0)
10791708Sstevel imap_ok = 0;
10801708Sstevel }
10811708Sstevel if (imap_ok) {
10821708Sstevel if (!default_ival)
10831708Sstevel ddi_prop_free(imapp);
10841708Sstevel return (DDI_SUCCESS);
10851708Sstevel }
10861708Sstevel
10871708Sstevel while (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, tdip,
10881708Sstevel DDI_PROP_DONTPASS, "upa-portid", (int **)&port_id,
10891708Sstevel (uint_t *)&length) != DDI_PROP_SUCCESS) {
10901708Sstevel tdip = ddi_get_parent(tdip);
10911708Sstevel if (tdip == NULL) {
10921708Sstevel cmn_err(CE_WARN, "%s%d: Could not get imap parent",
1093*7656SSherry.Moore@Sun.COM ddi_driver_name(dip), ddi_get_instance(dip));
10941708Sstevel if (!default_ival)
10951708Sstevel ddi_prop_free(imapp);
10961708Sstevel return (DDI_FAILURE);
10971708Sstevel }
10981708Sstevel }
10991708Sstevel timapp = imapp;
11001708Sstevel for (i = 0; i < num; i++) {
11011708Sstevel *(timapp+i*5+3) = ddi_get_nodeid(tdip);
11021708Sstevel }
11031708Sstevel
11041708Sstevel if (ddi_prop_update_int_array(DDI_DEV_T_NONE, dip,
1105*7656SSherry.Moore@Sun.COM "interrupt-map", imapp, ilength) != DDI_PROP_SUCCESS) {
11061708Sstevel cmn_err(CE_WARN, "%s%d: Could not update AC imap property",
1107*7656SSherry.Moore@Sun.COM ddi_driver_name(dip), ddi_get_instance(dip));
11081708Sstevel if (!default_ival)
11091708Sstevel ddi_prop_free(imapp);
11101708Sstevel return (DDI_FAILURE);
11111708Sstevel }
11121708Sstevel if (!default_ival)
11131708Sstevel ddi_prop_free(imapp);
11141708Sstevel return (DDI_SUCCESS);
11151708Sstevel }
11161708Sstevel #endif /* ACEBUS_HOTPLUG */
1117