/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" /* * PCIC device/interrupt handler * The "pcic" driver handles the Intel 82365SL, Cirrus Logic * and Toshiba (and possibly other clones) PCMCIA adapter chip * sets. It implements a subset of Socket Services as defined * in the Solaris PCMCIA design documents */ /* * currently defined "properties" * * clock-frequency bus clock frequency * smi system management interrupt override * need-mult-irq need status IRQ for each pair of sockets * disable-audio don't route audio signal to speaker */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(__sparc) #include #endif #include #include "cardbus/cardbus.h" #define SOFTC_SIZE (sizeof (anp_t)) static int pcic_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); static int pcic_attach(dev_info_t *, ddi_attach_cmd_t); static int pcic_detach(dev_info_t *, ddi_detach_cmd_t); static uint_t pcic_intr(caddr_t, caddr_t); static int pcic_do_io_intr(pcicdev_t *, uint32_t); static int pcic_probe(dev_info_t *); static int pcic_open(dev_t *, int, int, cred_t *); static int pcic_close(dev_t, int, int, cred_t *); static int pcic_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); typedef struct pcm_regs pcm_regs_t; static void pcic_init_assigned(dev_info_t *); static int pcic_apply_avail_ranges(dev_info_t *, pcm_regs_t *, pci_regspec_t *, int); int pci_resource_setup_avail(dev_info_t *, pci_regspec_t *, int); /* * On x86 platforms the ddi_iobp_alloc(9F) and ddi_mem_alloc(9F) calls * are xlated into DMA ctlops. To make this nexus work on x86, we * need to have the default ddi_dma_mctl ctlops in the bus_ops * structure, just to pass the request to the parent. The correct * ctlops should be ddi_no_dma_mctl because so far we don't do DMA. */ static struct bus_ops pcmciabus_ops = { BUSO_REV, pcmcia_bus_map, NULL, NULL, NULL, i_ddi_map_fault, ddi_no_dma_map, ddi_no_dma_allochdl, ddi_no_dma_freehdl, ddi_no_dma_bindhdl, ddi_no_dma_unbindhdl, ddi_no_dma_flush, ddi_no_dma_win, ddi_dma_mctl, pcmcia_ctlops, pcmcia_prop_op, NULL, /* (*bus_get_eventcookie)(); */ NULL, /* (*bus_add_eventcall)(); */ NULL, /* (*bus_remove_eventcall)(); */ NULL, /* (*bus_post_event)(); */ NULL, /* (*bus_intr_ctl)(); */ NULL, /* (*bus_config)(); */ NULL, /* (*bus_unconfig)(); */ NULL, /* (*bus_fm_init)(); */ NULL, /* (*bus_fm_fini)(); */ NULL, /* (*bus_enter)() */ NULL, /* (*bus_exit)() */ NULL, /* (*bus_power)() */ pcmcia_intr_ops /* (*bus_intr_op)(); */ }; static struct cb_ops pcic_cbops = { pcic_open, pcic_close, nodev, nodev, nodev, nodev, nodev, pcic_ioctl, nodev, nodev, nodev, nochpoll, ddi_prop_op, NULL, #ifdef CARDBUS D_NEW | D_MP | D_HOTPLUG #else D_NEW | D_MP #endif }; static struct dev_ops pcic_devops = { DEVO_REV, 0, pcic_getinfo, nulldev, pcic_probe, pcic_attach, pcic_detach, nulldev, &pcic_cbops, &pcmciabus_ops, NULL }; void *pcic_soft_state_p = NULL; static int pcic_maxinst = -1; int pcic_do_insertion = 1; int pcic_do_removal = 1; struct irqmap { int irq; int count; } pcic_irq_map[16]; int pcic_debug = 0x0; static void pcic_err(dev_info_t *dip, int level, const char *fmt, ...); extern void cardbus_dump_pci_config(dev_info_t *dip); extern void cardbus_dump_socket(dev_info_t *dip); extern int cardbus_validate_iline(dev_info_t *dip, ddi_acc_handle_t handle); static void pcic_dump_debqueue(char *msg); #if defined(PCIC_DEBUG) static void xxdmp_all_regs(pcicdev_t *, int, uint32_t); #define pcic_mutex_enter(a) \ { \ pcic_err(NULL, 10, "Set lock at %d\n", __LINE__); \ mutex_enter(a); \ }; #define pcic_mutex_exit(a) \ { \ pcic_err(NULL, 10, "Clear lock at %d\n", __LINE__); \ mutex_exit(a); \ }; #else #define pcic_mutex_enter(a) mutex_enter(a) #define pcic_mutex_exit(a) mutex_exit(a) #endif #define PCIC_VCC_3VLEVEL 1 #define PCIC_VCC_5VLEVEL 2 #define PCIC_VCC_12LEVEL 3 /* bit patterns to select voltage levels */ int pcic_vpp_levels[13] = { 0, 0, 0, 1, /* 3.3V */ 0, 1, /* 5V */ 0, 0, 0, 0, 0, 0, 2 /* 12V */ }; uint8_t pcic_cbv_levels[13] = { 0, 0, 0, 3, /* 3.3V */ 0, 2, /* 5V */ 0, 0, 0, 0, 0, 0, 1 /* 12V */ }; struct power_entry pcic_power[4] = { { 0, VCC|VPP1|VPP2 }, { 33, /* 3.3Volt */ VCC|VPP1|VPP2 }, { 5*10, /* 5Volt */ VCC|VPP1|VPP2 /* currently only know about this */ }, { 12*10, /* 12Volt */ VPP1|VPP2 } }; /* * Base used to allocate ranges of PCI memory on x86 systems * Each instance gets a chunk above the base that is used to map * in the memory and I/O windows for that device. * Pages below the base are also allocated for the EXCA registers, * one per instance. */ #define PCIC_PCI_MEMCHUNK 0x1000000 static int pcic_wait_insert_time = 5000000; /* In micro-seconds */ static int pcic_debounce_time = 200000; /* In micro-seconds */ struct debounce { pcic_socket_t *pcs; clock_t expire; struct debounce *next; }; static struct debounce *pcic_deb_queue = NULL; static kmutex_t pcic_deb_mtx; static kcondvar_t pcic_deb_cv; static kthread_t *pcic_deb_threadid; static inthandler_t *pcic_handlers; static void pcic_setup_adapter(pcicdev_t *); static int pcic_change(pcicdev_t *, int); static int pcic_ll_reset(pcicdev_t *, int); static void pcic_mswait(pcicdev_t *, int, int); static boolean_t pcic_check_ready(pcicdev_t *, int); static void pcic_set_cdtimers(pcicdev_t *, int, uint32_t, int); static void pcic_ready_wait(pcicdev_t *, int); extern int pcmcia_get_intr(dev_info_t *, int); extern int pcmcia_return_intr(dev_info_t *, int); extern void pcmcia_cb_suspended(int); extern void pcmcia_cb_resumed(int); static int pcic_callback(dev_info_t *, int (*)(), int); static int pcic_inquire_adapter(dev_info_t *, inquire_adapter_t *); static int pcic_get_adapter(dev_info_t *, get_adapter_t *); static int pcic_get_page(dev_info_t *, get_page_t *); static int pcic_get_socket(dev_info_t *, get_socket_t *); static int pcic_get_status(dev_info_t *, get_ss_status_t *); static int pcic_get_window(dev_info_t *, get_window_t *); static int pcic_inquire_socket(dev_info_t *, inquire_socket_t *); static int pcic_inquire_window(dev_info_t *, inquire_window_t *); static int pcic_reset_socket(dev_info_t *, int, int); static int pcic_set_page(dev_info_t *, set_page_t *); static int pcic_set_window(dev_info_t *, set_window_t *); static int pcic_set_socket(dev_info_t *, set_socket_t *); static int pcic_set_interrupt(dev_info_t *, set_irq_handler_t *); static int pcic_clear_interrupt(dev_info_t *, clear_irq_handler_t *); static void pcic_pm_detection(void *); static void pcic_iomem_pci_ctl(ddi_acc_handle_t, uchar_t *, unsigned); static int clext_reg_read(pcicdev_t *, int, uchar_t); static void clext_reg_write(pcicdev_t *, int, uchar_t, uchar_t); static int pcic_calc_speed(pcicdev_t *, uint32_t); static int pcic_card_state(pcicdev_t *, pcic_socket_t *); static int pcic_find_pci_type(pcicdev_t *); static void pcic_82092_smiirq_ctl(pcicdev_t *, int, int, int); static void pcic_handle_cd_change(pcicdev_t *, pcic_socket_t *, uint8_t); static uint_t pcic_cd_softint(caddr_t, caddr_t); static uint8_t pcic_getb(pcicdev_t *, int, int); static void pcic_putb(pcicdev_t *, int, int, int8_t); static int pcic_set_vcc_level(pcicdev_t *, set_socket_t *); static uint_t pcic_softintr(caddr_t, caddr_t); static void pcic_debounce(pcic_socket_t *); static void pcic_do_resume(pcicdev_t *); static void *pcic_add_debqueue(pcic_socket_t *, int); static void pcic_rm_debqueue(void *); static void pcic_deb_thread(); static boolean_t pcic_load_cardbus(pcicdev_t *pcic, const pcic_socket_t *sockp); static void pcic_unload_cardbus(pcicdev_t *pcic, const pcic_socket_t *sockp); static uint32_t pcic_getcb(pcicdev_t *pcic, int reg); static void pcic_putcb(pcicdev_t *pcic, int reg, uint32_t value); static void pcic_cb_enable_intr(dev_info_t *); static void pcic_cb_disable_intr(dev_info_t *); static void pcic_enable_io_intr(pcicdev_t *pcic, int socket, int irq); static void pcic_disable_io_intr(pcicdev_t *pcic, int socket); static cb_nexus_cb_t pcic_cbnexus_ops = { pcic_cb_enable_intr, pcic_cb_disable_intr }; static int pcic_exca_powerctl(pcicdev_t *pcic, int socket, int powerlevel); static int pcic_cbus_powerctl(pcicdev_t *pcic, int socket); #if defined(__sparc) static int pcic_fault(enum pci_fault_ops op, void *arg); #endif /* * pcmcia interface operations structure * this is the private interface that is exported to the nexus */ pcmcia_if_t pcic_if_ops = { PCIF_MAGIC, PCIF_VERSION, pcic_callback, pcic_get_adapter, pcic_get_page, pcic_get_socket, pcic_get_status, pcic_get_window, pcic_inquire_adapter, pcic_inquire_socket, pcic_inquire_window, pcic_reset_socket, pcic_set_page, pcic_set_window, pcic_set_socket, pcic_set_interrupt, pcic_clear_interrupt, NULL, }; /* * chip type identification routines * this list of functions is searched until one of them succeeds * or all fail. i82365SL is assumed if failed. */ static int pcic_ci_cirrus(pcicdev_t *); static int pcic_ci_vadem(pcicdev_t *); static int pcic_ci_ricoh(pcicdev_t *); int (*pcic_ci_funcs[])(pcicdev_t *) = { pcic_ci_cirrus, pcic_ci_vadem, pcic_ci_ricoh, NULL }; static struct modldrv modldrv = { &mod_driverops, /* Type of module. This one is a driver */ "PCIC PCMCIA adapter driver %I%", /* Name of the module. */ &pcic_devops, /* driver ops */ }; static struct modlinkage modlinkage = { MODREV_1, (void *)&modldrv, NULL }; int _init() { int stat; /* Allocate soft state */ if ((stat = ddi_soft_state_init(&pcic_soft_state_p, SOFTC_SIZE, 2)) != DDI_SUCCESS) return (stat); if ((stat = mod_install(&modlinkage)) != 0) ddi_soft_state_fini(&pcic_soft_state_p); return (stat); } int _fini() { int stat = 0; if ((stat = mod_remove(&modlinkage)) != 0) return (stat); if (pcic_deb_threadid) { mutex_enter(&pcic_deb_mtx); pcic_deb_threadid = 0; while (!pcic_deb_threadid) cv_wait(&pcic_deb_cv, &pcic_deb_mtx); pcic_deb_threadid = 0; mutex_exit(&pcic_deb_mtx); mutex_destroy(&pcic_deb_mtx); cv_destroy(&pcic_deb_cv); } ddi_soft_state_fini(&pcic_soft_state_p); return (stat); } int _info(struct modinfo *modinfop) { return (mod_info(&modlinkage, modinfop)); } /* * pcic_getinfo() * provide instance/device information about driver */ /*ARGSUSED*/ static int pcic_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result) { anp_t *anp; int error = DDI_SUCCESS; minor_t minor; switch (cmd) { case DDI_INFO_DEVT2DEVINFO: minor = getminor((dev_t)arg); minor &= 0x7f; if (!(anp = ddi_get_soft_state(pcic_soft_state_p, minor))) *result = NULL; else *result = anp->an_dip; break; case DDI_INFO_DEVT2INSTANCE: minor = getminor((dev_t)arg); minor &= 0x7f; *result = (void *)((long)minor); break; default: error = DDI_FAILURE; break; } return (error); } static int pcic_probe(dev_info_t *dip) { int value; ddi_device_acc_attr_t attr; ddi_acc_handle_t handle; uchar_t *index, *data; if (ddi_dev_is_sid(dip) == DDI_SUCCESS) return (DDI_PROBE_DONTCARE); /* * find a PCIC device (any vendor) * while there can be up to 4 such devices in * a system, we currently only look for 1 * per probe. There will be up to 2 chips per * instance since they share I/O space */ attr.devacc_attr_version = DDI_DEVICE_ATTR_V0; attr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC; attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; if (ddi_regs_map_setup(dip, PCIC_ISA_CONTROL_REG_NUM, (caddr_t *)&index, PCIC_ISA_CONTROL_REG_OFFSET, PCIC_ISA_CONTROL_REG_LENGTH, &attr, &handle) != DDI_SUCCESS) return (DDI_PROBE_FAILURE); data = index + 1; #if defined(PCIC_DEBUG) if (pcic_debug) cmn_err(CE_CONT, "pcic_probe: entered\n"); if (pcic_debug) cmn_err(CE_CONT, "\tindex=%p\n", (void *)index); #endif ddi_put8(handle, index, PCIC_CHIP_REVISION); ddi_put8(handle, data, 0); value = ddi_get8(handle, data); #if defined(PCIC_DEBUG) if (pcic_debug) cmn_err(CE_CONT, "\tchip revision register = %x\n", value); #endif if ((value & PCIC_REV_MASK) >= PCIC_REV_LEVEL_LOW && (value & 0x30) == 0) { /* * we probably have a PCIC chip in the system * do a little more checking. If we find one, * reset everything in case of softboot */ ddi_put8(handle, index, PCIC_MAPPING_ENABLE); ddi_put8(handle, data, 0); value = ddi_get8(handle, data); #if defined(PCIC_DEBUG) if (pcic_debug) cmn_err(CE_CONT, "\tzero test = %x\n", value); #endif /* should read back as zero */ if (value == 0) { /* * we do have one and it is off the bus */ #if defined(PCIC_DEBUG) if (pcic_debug) cmn_err(CE_CONT, "pcic_probe: success\n"); #endif ddi_regs_map_free(&handle); return (DDI_PROBE_SUCCESS); } } #if defined(PCIC_DEBUG) if (pcic_debug) cmn_err(CE_CONT, "pcic_probe: failed\n"); #endif ddi_regs_map_free(&handle); return (DDI_PROBE_FAILURE); } /* * These are just defaults they can also be changed via a property in the * conf file. */ static int pci_config_reg_num = PCIC_PCI_CONFIG_REG_NUM; static int pci_control_reg_num = PCIC_PCI_CONTROL_REG_NUM; static int pcic_do_pcmcia_sr = 1; static int pcic_use_cbpwrctl = PCF_CBPWRCTL; /* * enable insertion/removal interrupt for 32bit cards */ static int cardbus_enable_cd_intr(dev_info_t *dip) { ddi_acc_handle_t iohandle; caddr_t ioaddr; ddi_device_acc_attr_t attr; attr.devacc_attr_version = DDI_DEVICE_ATTR_V0; attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC; attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; (void) ddi_regs_map_setup(dip, 1, (caddr_t *)&ioaddr, 0, 4096, &attr, &iohandle); /* CSC Interrupt: Card detect interrupt on */ ddi_put32(iohandle, (uint32_t *)(ioaddr+CB_STATUS_MASK), ddi_get32(iohandle, (uint32_t *)(ioaddr+CB_STATUS_MASK)) | CB_SE_CCDMASK); ddi_put32(iohandle, (uint32_t *)(ioaddr+CB_STATUS_EVENT), ddi_get32(iohandle, (uint32_t *)(ioaddr+CB_STATUS_EVENT))); ddi_regs_map_free(&iohandle); return (1); } /* * pcic_attach() * attach the PCIC (Intel 82365SL/CirrusLogic/Toshiba) driver * to the system. This is a child of "sysbus" since that is where * the hardware lives, but it provides services to the "pcmcia" * nexus driver. It gives a pointer back via its private data * structure which contains both the dip and socket services entry * points */ static int pcic_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) { anp_t *pcic_nexus; pcicdev_t *pcic; int irqlevel, value; int pci_cfrn, pci_ctrn; int i, j, smi, actual; char *typename; char bus_type[16] = "(unknown)"; int len = sizeof (bus_type); ddi_device_acc_attr_t attr; anp_t *anp = ddi_get_driver_private(dip); uint_t pri; #if defined(PCIC_DEBUG) if (pcic_debug) { cmn_err(CE_CONT, "pcic_attach: entered\n"); } #endif switch (cmd) { case DDI_ATTACH: break; case DDI_RESUME: pcic = anp->an_private; /* * for now, this is a simulated resume. * a real one may need different things. */ if (pcic != NULL && pcic->pc_flags & PCF_SUSPENDED) { mutex_enter(&pcic->pc_lock); /* should probe for new sockets showing up */ pcic_setup_adapter(pcic); pcic->pc_flags &= ~PCF_SUSPENDED; mutex_exit(&pcic->pc_lock); (void) pcmcia_begin_resume(dip); pcic_do_resume(pcic); #ifdef CARDBUS cardbus_restore_children(ddi_get_child(dip)); #endif /* * for complete implementation need END_RESUME (later) */ return (DDI_SUCCESS); } return (DDI_SUCCESS); default: return (DDI_FAILURE); } /* * Allocate soft state associated with this instance. */ if (ddi_soft_state_zalloc(pcic_soft_state_p, ddi_get_instance(dip)) != DDI_SUCCESS) { cmn_err(CE_CONT, "pcic%d: Unable to alloc state\n", ddi_get_instance(dip)); return (DDI_FAILURE); } pcic_nexus = ddi_get_soft_state(pcic_soft_state_p, ddi_get_instance(dip)); pcic = kmem_zalloc(sizeof (pcicdev_t), KM_SLEEP); pcic->dip = dip; pcic_nexus->an_dip = dip; pcic_nexus->an_if = &pcic_if_ops; pcic_nexus->an_private = pcic; pcic->pc_numpower = sizeof (pcic_power)/sizeof (pcic_power[0]); pcic->pc_power = pcic_power; pci_ctrn = ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_CANSLEEP, "pci-control-reg-number", pci_control_reg_num); pci_cfrn = ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_CANSLEEP, "pci-config-reg-number", pci_config_reg_num); ddi_set_driver_private(dip, pcic_nexus); /* * pcic->pc_irq is really the IPL level we want to run at * set the default values here and override from intr spec */ pcic->pc_irq = ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_CANSLEEP, "interrupt-priorities", -1); if (pcic->pc_irq == -1) { int actual; uint_t pri; ddi_intr_handle_t hdl; /* see if intrspec tells us different */ if (ddi_intr_alloc(dip, &hdl, DDI_INTR_TYPE_FIXED, 0, 1, &actual, DDI_INTR_ALLOC_NORMAL) == DDI_SUCCESS) { if (ddi_intr_get_pri(hdl, &pri) == DDI_SUCCESS) pcic->pc_irq = pri; else pcic->pc_irq = LOCK_LEVEL + 1; (void) ddi_intr_free(hdl); } } pcic_nexus->an_ipl = pcic->pc_irq; /* * Check our parent bus type. We do different things based on which * bus we're on. */ if (ddi_prop_op(DDI_DEV_T_ANY, ddi_get_parent(dip), PROP_LEN_AND_VAL_BUF, DDI_PROP_CANSLEEP, "device_type", (caddr_t)&bus_type[0], &len) != DDI_PROP_SUCCESS) { if (ddi_prop_op(DDI_DEV_T_ANY, ddi_get_parent(dip), PROP_LEN_AND_VAL_BUF, DDI_PROP_CANSLEEP, "bus-type", (caddr_t)&bus_type[0], &len) != DDI_PROP_SUCCESS) { cmn_err(CE_CONT, "pcic%d: can't find parent bus type\n", ddi_get_instance(dip)); kmem_free(pcic, sizeof (pcicdev_t)); ddi_soft_state_free(pcic_soft_state_p, ddi_get_instance(dip)); return (DDI_FAILURE); } } /* ddi_prop_op("device_type") */ if (strcmp(bus_type, DEVI_PCI_NEXNAME) == 0 || strcmp(bus_type, DEVI_PCIEX_NEXNAME) == 0) { pcic->pc_flags = PCF_PCIBUS; } else { cmn_err(CE_WARN, "!pcic%d: non-pci mode (%s) not supported, " "set BIOS to yenta mode if applicable\n", ddi_get_instance(dip), bus_type); kmem_free(pcic, sizeof (pcicdev_t)); ddi_soft_state_free(pcic_soft_state_p, ddi_get_instance(dip)); return (DDI_FAILURE); } if ((pcic->bus_speed = ddi_getprop(DDI_DEV_T_ANY, ddi_get_parent(dip), DDI_PROP_CANSLEEP, "clock-frequency", 0)) == 0) { if (pcic->pc_flags & PCF_PCIBUS) pcic->bus_speed = PCIC_PCI_DEF_SYSCLK; else pcic->bus_speed = PCIC_ISA_DEF_SYSCLK; } else { /* * OBP can declare the speed in Hz... */ if (pcic->bus_speed > 1000000) pcic->bus_speed /= 1000000; } /* ddi_prop_op("clock-frequency") */ pcic->pc_io_type = PCIC_IO_TYPE_82365SL; /* default mode */ #ifdef PCIC_DEBUG if (pcic_debug) { cmn_err(CE_CONT, "pcic%d: parent bus type = [%s], speed = %d MHz\n", ddi_get_instance(dip), bus_type, pcic->bus_speed); } #endif /* * The reg properties on a PCI node are different than those * on a non-PCI node. Handle that difference here. * If it turns out to be a CardBus chip, we have even more * differences. */ if (pcic->pc_flags & PCF_PCIBUS) { int class_code; #if defined(__i386) || defined(__amd64) pcic->pc_base = 0x1000000; pcic->pc_bound = (uint32_t)~0; pcic->pc_iobase = 0x1000; pcic->pc_iobound = 0xefff; #elif defined(__sparc) pcic->pc_base = 0x0; pcic->pc_bound = (uint32_t)~0; pcic->pc_iobase = 0x00000; pcic->pc_iobound = 0xffff; #endif /* usually need to get at config space so map first */ attr.devacc_attr_version = DDI_DEVICE_ATTR_V0; attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC; attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; if (ddi_regs_map_setup(dip, pci_cfrn, (caddr_t *)&pcic->cfgaddr, PCIC_PCI_CONFIG_REG_OFFSET, PCIC_PCI_CONFIG_REG_LENGTH, &attr, &pcic->cfg_handle) != DDI_SUCCESS) { cmn_err(CE_CONT, "pcic%d: unable to map config space" "regs\n", ddi_get_instance(dip)); kmem_free(pcic, sizeof (pcicdev_t)); return (DDI_FAILURE); } /* ddi_regs_map_setup */ class_code = ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_CANSLEEP|DDI_PROP_DONTPASS, "class-code", -1); #ifdef PCIC_DEBUG if (pcic_debug) { cmn_err(CE_CONT, "pcic_attach class_code=%x\n", class_code); } #endif switch (class_code) { case PCIC_PCI_CARDBUS: pcic->pc_flags |= PCF_CARDBUS; pcic->pc_io_type = PCIC_IO_TYPE_YENTA; /* * Get access to the adapter registers on the * PCI bus. A 4K memory page */ #if defined(PCIC_DEBUG) pcic_err(dip, 8, "Is Cardbus device\n"); if (pcic_debug) { int nr; long rs; (void) ddi_dev_nregs(dip, &nr); pcic_err(dip, 9, "\tdev, cfgaddr 0x%p," "cfghndl 0x%p nregs %d", (void *)pcic->cfgaddr, (void *)pcic->cfg_handle, nr); (void) ddi_dev_regsize(dip, PCIC_PCI_CONTROL_REG_NUM, &rs); pcic_err(dip, 9, "\tsize of reg %d is 0x%x\n", PCIC_PCI_CONTROL_REG_NUM, (int)rs); } #endif attr.devacc_attr_version = DDI_DEVICE_ATTR_V0; attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC; attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; if (ddi_regs_map_setup(dip, pci_ctrn, (caddr_t *)&pcic->ioaddr, PCIC_PCI_CONTROL_REG_OFFSET, PCIC_CB_CONTROL_REG_LENGTH, &attr, &pcic->handle) != DDI_SUCCESS) { cmn_err(CE_CONT, "pcic%d: unable to map PCI regs\n", ddi_get_instance(dip)); ddi_regs_map_free(&pcic->cfg_handle); kmem_free(pcic, sizeof (pcicdev_t)); return (DDI_FAILURE); } /* ddi_regs_map_setup */ /* * Find out the chip type - If we're on a PCI bus, * the adapter has that information in the PCI * config space. * Note that we call pcic_find_pci_type here since * it needs a valid mapped pcic->handle to * access some of the adapter registers in * some cases. */ if (pcic_find_pci_type(pcic) != DDI_SUCCESS) { ddi_regs_map_free(&pcic->handle); ddi_regs_map_free(&pcic->cfg_handle); kmem_free(pcic, sizeof (pcicdev_t)); cmn_err(CE_WARN, "pcic: %s: unsupported " "bridge\n", ddi_get_name_addr(dip)); return (DDI_FAILURE); } break; default: case PCIC_PCI_PCMCIA: /* * Get access to the adapter IO registers on the * PCI bus config space. */ attr.devacc_attr_version = DDI_DEVICE_ATTR_V0; attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC; attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; /* * We need a default mapping to the adapter's IO * control register space. For most adapters * that are of class PCIC_PCI_PCMCIA (or of * a default class) the control registers * will be using the 82365-type control/data * format. */ if (ddi_regs_map_setup(dip, pci_ctrn, (caddr_t *)&pcic->ioaddr, PCIC_PCI_CONTROL_REG_OFFSET, PCIC_PCI_CONTROL_REG_LENGTH, &attr, &pcic->handle) != DDI_SUCCESS) { cmn_err(CE_CONT, "pcic%d: unable to map PCI regs\n", ddi_get_instance(dip)); ddi_regs_map_free(&pcic->cfg_handle); kmem_free(pcic, sizeof (pcicdev_t)); return (DDI_FAILURE); } /* ddi_regs_map_setup */ /* * Find out the chip type - If we're on a PCI bus, * the adapter has that information in the PCI * config space. * Note that we call pcic_find_pci_type here since * it needs a valid mapped pcic->handle to * access some of the adapter registers in * some cases. */ if (pcic_find_pci_type(pcic) != DDI_SUCCESS) { ddi_regs_map_free(&pcic->handle); ddi_regs_map_free(&pcic->cfg_handle); kmem_free(pcic, sizeof (pcicdev_t)); cmn_err(CE_WARN, "pcic: %s: unsupported " "bridge\n", ddi_get_name_addr(dip)); return (DDI_FAILURE); } /* * Some PCI-PCMCIA(R2) adapters are Yenta-compliant * for extended registers even though they are * not CardBus adapters. For those adapters, * re-map pcic->handle to be large enough to * encompass the Yenta registers. */ switch (pcic->pc_type) { case PCIC_TI_PCI1031: ddi_regs_map_free(&pcic->handle); if (ddi_regs_map_setup(dip, PCIC_PCI_CONTROL_REG_NUM, (caddr_t *)&pcic->ioaddr, PCIC_PCI_CONTROL_REG_OFFSET, PCIC_CB_CONTROL_REG_LENGTH, &attr, &pcic->handle) != DDI_SUCCESS) { cmn_err(CE_CONT, "pcic%d: unable to map " "PCI regs\n", ddi_get_instance(dip)); ddi_regs_map_free(&pcic->cfg_handle); kmem_free(pcic, sizeof (pcicdev_t)); return (DDI_FAILURE); } /* ddi_regs_map_setup */ break; default: break; } /* switch (pcic->pc_type) */ break; } /* switch (class_code) */ } else { /* * We're not on a PCI bus, so assume an ISA bus type * register property. Get access to the adapter IO * registers on a non-PCI bus. */ attr.devacc_attr_version = DDI_DEVICE_ATTR_V0; attr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC; attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; pcic->mem_reg_num = PCIC_ISA_MEM_REG_NUM; pcic->io_reg_num = PCIC_ISA_IO_REG_NUM; if (ddi_regs_map_setup(dip, PCIC_ISA_CONTROL_REG_NUM, (caddr_t *)&pcic->ioaddr, PCIC_ISA_CONTROL_REG_OFFSET, PCIC_ISA_CONTROL_REG_LENGTH, &attr, &pcic->handle) != DDI_SUCCESS) { cmn_err(CE_CONT, "pcic%d: unable to map ISA registers\n", ddi_get_instance(dip)); kmem_free(pcic, sizeof (pcicdev_t)); return (DDI_FAILURE); } /* ddi_regs_map_setup */ /* ISA bus is limited to 24-bits, but not first 640K */ pcic->pc_base = 0xd0000; pcic->pc_bound = (uint32_t)~0; pcic->pc_iobase = 0x1000; pcic->pc_iobound = 0xefff; } /* !PCF_PCIBUS */ #ifdef PCIC_DEBUG if (pcic_debug) { cmn_err(CE_CONT, "pcic_attach pc_flags=%x pc_type=%x\n", pcic->pc_flags, pcic->pc_type); } #endif /* * Setup various adapter registers for the PCI case. For the * non-PCI case, find out the chip type. */ if (pcic->pc_flags & PCF_PCIBUS) { int iline; #if defined(__sparc) iline = 0; #else iline = cardbus_validate_iline(dip, pcic->cfg_handle); #endif /* set flags and socket counts based on chip type */ switch (pcic->pc_type) { uint32_t cfg; case PCIC_INTEL_i82092: cfg = ddi_get8(pcic->cfg_handle, pcic->cfgaddr + PCIC_82092_PCICON); /* we can only support 4 Socket version */ if (cfg & PCIC_82092_4_SOCKETS) { pcic->pc_numsockets = 4; pcic->pc_type = PCIC_INTEL_i82092; if (iline != 0xFF) pcic->pc_intr_mode = PCIC_INTR_MODE_PCI_1; else pcic->pc_intr_mode = PCIC_INTR_MODE_ISA; } else { cmn_err(CE_CONT, "pcic%d: Intel 82092 adapter " "in unsupported configuration: 0x%x", ddi_get_instance(pcic->dip), cfg); pcic->pc_numsockets = 0; } /* PCIC_82092_4_SOCKETS */ break; case PCIC_CL_PD6730: case PCIC_CL_PD6729: pcic->pc_intr_mode = PCIC_INTR_MODE_PCI_1; cfg = ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_CANSLEEP, "interrupts", 0); /* if not interrupt pin then must use ISA style IRQs */ if (cfg == 0 || iline == 0xFF) pcic->pc_intr_mode = PCIC_INTR_MODE_ISA; else { /* * we have the option to use PCI interrupts. * this might not be optimal but in some cases * is the only thing possible (sparc case). * we now deterine what is possible. */ pcic->pc_intr_mode = PCIC_INTR_MODE_PCI_1; } pcic->pc_numsockets = 2; pcic->pc_flags |= PCF_IO_REMAP; break; case PCIC_TI_PCI1031: /* this chip doesn't do CardBus but looks like one */ pcic->pc_flags &= ~PCF_CARDBUS; /* FALLTHROUGH */ default: pcic->pc_flags |= PCF_IO_REMAP; /* FALLTHROUGH */ /* indicate feature even if not supported */ pcic->pc_flags |= PCF_DMA | PCF_ZV; /* Not sure if these apply to all these chips */ pcic->pc_flags |= (PCF_VPPX|PCF_33VCAP); pcic->pc_flags |= pcic_use_cbpwrctl; pcic->pc_numsockets = 1; /* one per function */ if (iline != 0xFF) { uint8_t cfg; pcic->pc_intr_mode = PCIC_INTR_MODE_PCI_1; cfg = ddi_get8(pcic->cfg_handle, (pcic->cfgaddr + PCIC_BRIDGE_CTL_REG)); cfg &= (~PCIC_FUN_INT_MOD_ISA); ddi_put8(pcic->cfg_handle, (pcic->cfgaddr + PCIC_BRIDGE_CTL_REG), cfg); } else pcic->pc_intr_mode = PCIC_INTR_MODE_ISA; pcic->pc_io_type = PCIC_IOTYPE_YENTA; break; } } else { /* * We're not on a PCI bus so do some more * checking for adapter type here. * For the non-PCI bus case: * It could be any one of a number of different chips * If we can't determine anything else, it is assumed * to be an Intel 82365SL. The Cirrus Logic PD6710 * has an extension register that provides unique * identification. Toshiba chip isn't detailed as yet. */ /* Init the CL id mode */ pcic_putb(pcic, 0, PCIC_CHIP_INFO, 0); value = pcic_getb(pcic, 0, PCIC_CHIP_INFO); /* default to Intel i82365SL and then refine */ pcic->pc_type = PCIC_I82365SL; pcic->pc_chipname = PCIC_TYPE_I82365SL; for (value = 0; pcic_ci_funcs[value] != NULL; value++) { /* go until one succeeds or none left */ if (pcic_ci_funcs[value](pcic)) break; } /* any chip specific flags get set here */ switch (pcic->pc_type) { case PCIC_CL_PD6722: pcic->pc_flags |= PCF_DMA; } for (i = 0; i < PCIC_MAX_SOCKETS; i++) { /* * look for total number of sockets. * basically check each possible socket for * presence like in probe */ /* turn all windows off */ pcic_putb(pcic, i, PCIC_MAPPING_ENABLE, 0); value = pcic_getb(pcic, i, PCIC_MAPPING_ENABLE); /* * if a zero is read back, then this socket * might be present. It would be except for * some systems that map the secondary PCIC * chip space back to the first. */ if (value != 0) { /* definitely not so skip */ /* note: this is for Compaq support */ continue; } /* further tests */ value = pcic_getb(pcic, i, PCIC_CHIP_REVISION) & PCIC_REV_MASK; if (!(value >= PCIC_REV_LEVEL_LOW && value <= PCIC_REV_LEVEL_HI)) break; pcic_putb(pcic, i, PCIC_SYSMEM_0_STARTLOW, 0xaa); pcic_putb(pcic, i, PCIC_SYSMEM_1_STARTLOW, 0x55); value = pcic_getb(pcic, i, PCIC_SYSMEM_0_STARTLOW); j = pcic_getb(pcic, i, PCIC_SYSMEM_1_STARTLOW); if (value != 0xaa || j != 0x55) break; /* * at this point we know if we have hardware * of some type and not just the bus holding * a pattern for us. We still have to determine * the case where more than 2 sockets are * really the same due to peculiar mappings of * hardware. */ j = pcic->pc_numsockets++; pcic->pc_sockets[j].pcs_flags = 0; pcic->pc_sockets[j].pcs_io = pcic->ioaddr; pcic->pc_sockets[j].pcs_socket = i; /* put PC Card into RESET, just in case */ value = pcic_getb(pcic, i, PCIC_INTERRUPT); pcic_putb(pcic, i, PCIC_INTERRUPT, value & ~PCIC_RESET); } #if defined(PCIC_DEBUG) if (pcic_debug) cmn_err(CE_CONT, "num sockets = %d\n", pcic->pc_numsockets); #endif if (pcic->pc_numsockets == 0) { ddi_regs_map_free(&pcic->handle); kmem_free(pcic, sizeof (pcicdev_t)); return (DDI_FAILURE); } /* * need to think this through again in light of * Compaq not following the model that all the * chip vendors recommend. IBM 755 seems to be * afflicted as well. Basically, if the vendor * wired things wrong, socket 0 responds for socket 2 * accesses, etc. */ if (pcic->pc_numsockets > 2) { int count = pcic->pc_numsockets / 4; for (i = 0; i < count; i++) { /* put pattern into socket 0 */ pcic_putb(pcic, i, PCIC_SYSMEM_0_STARTLOW, 0x11); /* put pattern into socket 2 */ pcic_putb(pcic, i + 2, PCIC_SYSMEM_0_STARTLOW, 0x33); /* read back socket 0 */ value = pcic_getb(pcic, i, PCIC_SYSMEM_0_STARTLOW); /* read back chip 1 socket 0 */ j = pcic_getb(pcic, i + 2, PCIC_SYSMEM_0_STARTLOW); if (j == value) { pcic->pc_numsockets -= 2; } } } smi = 0xff; /* no more override */ if (ddi_getprop(DDI_DEV_T_NONE, dip, DDI_PROP_DONTPASS, "need-mult-irq", 0xffff) != 0xffff) pcic->pc_flags |= PCF_MULT_IRQ; } /* !PCF_PCIBUS */ /* * some platforms/busses need to have resources setup * this is temporary until a real resource allocator is * implemented. */ pcic_init_assigned(dip); typename = pcic->pc_chipname; #ifdef PCIC_DEBUG if (pcic_debug) { int nregs, nintrs; if (ddi_dev_nregs(dip, &nregs) != DDI_SUCCESS) nregs = 0; if (ddi_dev_nintrs(dip, &nintrs) != DDI_SUCCESS) nintrs = 0; cmn_err(CE_CONT, "pcic%d: %d register sets, %d interrupts\n", ddi_get_instance(dip), nregs, nintrs); nintrs = 0; while (nregs--) { off_t size; if (ddi_dev_regsize(dip, nintrs, &size) == DDI_SUCCESS) { cmn_err(CE_CONT, "\tregnum %d size %ld (0x%lx)" "bytes", nintrs, size, size); if (nintrs == (pcic->pc_io_type == PCIC_IO_TYPE_82365SL ? PCIC_ISA_CONTROL_REG_NUM : PCIC_PCI_CONTROL_REG_NUM)) cmn_err(CE_CONT, " mapped at: 0x%p\n", (void *)pcic->ioaddr); else cmn_err(CE_CONT, "\n"); } else { cmn_err(CE_CONT, "\tddi_dev_regsize(rnumber" "= %d) returns DDI_FAILURE\n", nintrs); } nintrs++; } /* while */ } /* if (pcic_debug) */ #endif cv_init(&pcic->pm_cv, NULL, CV_DRIVER, NULL); if (!ddi_getprop(DDI_DEV_T_NONE, dip, DDI_PROP_DONTPASS, "disable-audio", 0)) pcic->pc_flags |= PCF_AUDIO; if (ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_CANSLEEP, "disable-cardbus", 0)) pcic->pc_flags &= ~PCF_CARDBUS; (void) ddi_prop_update_string(DDI_DEV_T_NONE, dip, PCICPROP_CTL, typename); /* * Init all socket SMI levels to 0 (no SMI) */ for (i = 0; i < PCIC_MAX_SOCKETS; i++) { pcic->pc_sockets[i].pcs_smi = 0; pcic->pc_sockets[i].pcs_debounce_id = 0; pcic->pc_sockets[i].pcs_pcic = pcic; } pcic->pc_lastreg = -1; /* just to make sure we are in sync */ /* * Setup the IRQ handler(s) */ switch (pcic->pc_intr_mode) { int xx; case PCIC_INTR_MODE_ISA: /* * On a non-PCI bus, we just use whatever SMI IRQ level was * specified above, and the IO IRQ levels are allocated * dynamically. */ for (xx = 15, smi = 0; xx >= 0; xx--) { if (PCIC_IRQ(xx) & PCIC_AVAIL_IRQS) { smi = pcmcia_get_intr(dip, xx); if (smi >= 0) break; } } #if defined(PCIC_DEBUG) if (pcic_debug) cmn_err(CE_NOTE, "\tselected IRQ %d as SMI\n", smi); #endif /* init to same so share is easy */ for (i = 0; i < pcic->pc_numsockets; i++) pcic->pc_sockets[i].pcs_smi = smi; /* any special handling of IRQ levels */ if (pcic->pc_flags & PCF_MULT_IRQ) { for (i = 2; i < pcic->pc_numsockets; i++) { if ((i & 1) == 0) { int xx; for (xx = 15, smi = 0; xx >= 0; xx--) { if (PCIC_IRQ(xx) & PCIC_AVAIL_IRQS) { smi = pcmcia_get_intr(dip, xx); if (smi >= 0) break; } } } if (smi >= 0) pcic->pc_sockets[i].pcs_smi = smi; } } pcic->pc_intr_htblp = kmem_alloc(pcic->pc_numsockets * sizeof (ddi_intr_handle_t), KM_SLEEP); for (i = 0, irqlevel = -1; i < pcic->pc_numsockets; i++) { struct intrspec *ispecp; struct ddi_parent_private_data *pdp; if (irqlevel == pcic->pc_sockets[i].pcs_smi) continue; else { irqlevel = pcic->pc_sockets[i].pcs_smi; } /* * now convert the allocated IRQ into an intrspec * and ask our parent to add it. Don't use * the ddi_add_intr since we don't have a * default intrspec in all cases. * * note: this sort of violates DDI but we don't * get hardware intrspecs for many of the devices. * at the same time, we know how to allocate them * so we do the right thing. */ if (ddi_intr_alloc(dip, &pcic->pc_intr_htblp[i], DDI_INTR_TYPE_FIXED, 0, 1, &actual, DDI_INTR_ALLOC_NORMAL) != DDI_SUCCESS) { cmn_err(CE_WARN, "%s: ddi_intr_alloc failed", ddi_get_name(dip)); goto isa_exit1; } /* * See earlier note: * Since some devices don't have 'intrspec' * we make one up in rootnex. * * However, it is not properly initialized as * the data it needs is present in this driver * and there is no interface to pass that up. * Specially 'irqlevel' is very important and * it is part of pcic struct. * * Set 'intrspec' up here; otherwise adding the * interrupt will fail. */ pdp = ddi_get_parent_data(dip); ispecp = (struct intrspec *)&pdp->par_intr[0]; ispecp->intrspec_vec = irqlevel; ispecp->intrspec_pri = pcic->pc_irq; /* Stay compatible w/ PCMCIA */ pcic->pc_pri = (ddi_iblock_cookie_t) (uintptr_t)pcic->pc_irq; pcic->pc_dcookie.idev_priority = (uintptr_t)pcic->pc_pri; pcic->pc_dcookie.idev_vector = (ushort_t)irqlevel; (void) ddi_intr_set_pri(pcic->pc_intr_htblp[i], pcic->pc_irq); if (i == 0) { mutex_init(&pcic->intr_lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pcic->pc_irq)); mutex_init(&pcic->pc_lock, NULL, MUTEX_DRIVER, NULL); } if (ddi_intr_add_handler(pcic->pc_intr_htblp[i], pcic_intr, (caddr_t)pcic, NULL)) { cmn_err(CE_WARN, "%s: ddi_intr_add_handler failed", ddi_get_name(dip)); goto isa_exit2; } if (ddi_intr_enable(pcic->pc_intr_htblp[i])) { cmn_err(CE_WARN, "%s: ddi_intr_enable failed", ddi_get_name(dip)); for (j = i; j < 0; j--) (void) ddi_intr_remove_handler( pcic->pc_intr_htblp[j]); goto isa_exit2; } } break; case PCIC_INTR_MODE_PCI_1: case PCIC_INTR_MODE_PCI: /* * If we're on a PCI bus, we route all interrupts, both SMI * and IO interrupts, through a single interrupt line. * Assign the SMI IRQ level to the IO IRQ level here. */ pcic->pc_pci_intr_hdlp = kmem_alloc(sizeof (ddi_intr_handle_t), KM_SLEEP); if (ddi_intr_alloc(dip, pcic->pc_pci_intr_hdlp, DDI_INTR_TYPE_FIXED, 0, 1, &actual, DDI_INTR_ALLOC_NORMAL) != DDI_SUCCESS) goto pci_exit1; if (ddi_intr_get_pri(pcic->pc_pci_intr_hdlp[0], &pri) != DDI_SUCCESS) { (void) ddi_intr_free(pcic->pc_pci_intr_hdlp[0]); goto pci_exit1; } pcic->pc_pri = (void *)(uintptr_t)pri; mutex_init(&pcic->intr_lock, NULL, MUTEX_DRIVER, pcic->pc_pri); mutex_init(&pcic->pc_lock, NULL, MUTEX_DRIVER, NULL); if (ddi_intr_add_handler(pcic->pc_pci_intr_hdlp[0], pcic_intr, (caddr_t)pcic, NULL)) goto pci_exit2; if (ddi_intr_enable(pcic->pc_pci_intr_hdlp[0])) { (void) ddi_intr_remove_handler( pcic->pc_pci_intr_hdlp[0]); goto pci_exit2; } /* Stay compatible w/ PCMCIA */ pcic->pc_dcookie.idev_priority = (ushort_t)pri; /* init to same (PCI) so share is easy */ for (i = 0; i < pcic->pc_numsockets; i++) pcic->pc_sockets[i].pcs_smi = 0xF; /* any valid */ break; } /* * Setup the adapter hardware to some reasonable defaults. */ mutex_enter(&pcic->pc_lock); /* mark the driver state as attached */ pcic->pc_flags |= PCF_ATTACHED; pcic_setup_adapter(pcic); for (j = 0; j < pcic->pc_numsockets; j++) if (ddi_intr_add_softint(dip, &pcic->pc_sockets[j].pcs_cd_softint_hdl, PCIC_SOFTINT_PRI_VAL, pcic_cd_softint, (caddr_t)&pcic->pc_sockets[j]) != DDI_SUCCESS) goto pci_exit2; #if defined(PCIC_DEBUG) if (pcic_debug) cmn_err(CE_CONT, "type = %s sockets = %d\n", typename, pcic->pc_numsockets); #endif pcic_nexus->an_iblock = &pcic->pc_pri; pcic_nexus->an_idev = &pcic->pc_dcookie; mutex_exit(&pcic->pc_lock); #ifdef CARDBUS (void) cardbus_enable_cd_intr(dip); if (pcic_debug) { cardbus_dump_pci_config(dip); cardbus_dump_socket(dip); } /* * Give the Cardbus misc module a chance to do it's per-adapter * instance setup. Note that there is no corresponding detach() * call. */ if (pcic->pc_flags & PCF_CARDBUS) if (cardbus_attach(dip, &pcic_cbnexus_ops) != DDI_SUCCESS) { cmn_err(CE_CONT, "pcic_attach: cardbus_attach failed\n"); goto pci_exit2; } #endif /* * Give the PCMCIA misc module a chance to do it's per-adapter * instance setup. */ if ((i = pcmcia_attach(dip, pcic_nexus)) != DDI_SUCCESS) goto pci_exit2; if (pcic_maxinst == -1) { /* This assumes that all instances run at the same IPL. */ mutex_init(&pcic_deb_mtx, NULL, MUTEX_DRIVER, NULL); cv_init(&pcic_deb_cv, NULL, CV_DRIVER, NULL); pcic_deb_threadid = thread_create((caddr_t)NULL, 0, pcic_deb_thread, (caddr_t)NULL, 0, &p0, TS_RUN, v.v_maxsyspri - 2); } pcic_maxinst = max(pcic_maxinst, ddi_get_instance(dip)); /* * Setup a debounce timeout to do an initial card detect * and enable interrupts. */ for (j = 0; j < pcic->pc_numsockets; j++) { pcic->pc_sockets[j].pcs_debounce_id = pcic_add_debqueue(&pcic->pc_sockets[j], drv_usectohz(pcic_debounce_time)); } return (i); isa_exit2: mutex_destroy(&pcic->intr_lock); mutex_destroy(&pcic->pc_lock); for (j = i; j < 0; j--) (void) ddi_intr_free(pcic->pc_intr_htblp[j]); isa_exit1: (void) pcmcia_return_intr(dip, pcic->pc_sockets[i].pcs_smi); ddi_regs_map_free(&pcic->handle); if (pcic->pc_flags & PCF_PCIBUS) ddi_regs_map_free(&pcic->cfg_handle); kmem_free(pcic->pc_intr_htblp, pcic->pc_numsockets * sizeof (ddi_intr_handle_t)); kmem_free(pcic, sizeof (pcicdev_t)); return (DDI_FAILURE); pci_exit2: mutex_destroy(&pcic->intr_lock); mutex_destroy(&pcic->pc_lock); (void) ddi_intr_free(pcic->pc_pci_intr_hdlp[0]); pci_exit1: ddi_regs_map_free(&pcic->handle); if (pcic->pc_flags & PCF_PCIBUS) ddi_regs_map_free(&pcic->cfg_handle); kmem_free(pcic->pc_pci_intr_hdlp, sizeof (ddi_intr_handle_t)); kmem_free(pcic, sizeof (pcicdev_t)); return (DDI_FAILURE); } /* * pcic_detach() * request to detach from the system */ static int pcic_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) { anp_t *anp = ddi_get_driver_private(dip); pcicdev_t *pcic = anp->an_private; int i; switch (cmd) { case DDI_DETACH: /* don't detach if the nexus still talks to us */ if (pcic->pc_callback != NULL) return (DDI_FAILURE); /* kill off the pm simulation */ if (pcic->pc_pmtimer) (void) untimeout(pcic->pc_pmtimer); /* turn everything off for all sockets and chips */ for (i = 0; i < pcic->pc_numsockets; i++) { if (pcic->pc_sockets[i].pcs_debounce_id) pcic_rm_debqueue( pcic->pc_sockets[i].pcs_debounce_id); pcic->pc_sockets[i].pcs_debounce_id = 0; pcic_putb(pcic, i, PCIC_MANAGEMENT_INT, 0); pcic_putb(pcic, i, PCIC_CARD_DETECT, 0); pcic_putb(pcic, i, PCIC_MAPPING_ENABLE, 0); /* disable interrupts and put card into RESET */ pcic_putb(pcic, i, PCIC_INTERRUPT, 0); } (void) ddi_intr_disable(pcic->pc_pci_intr_hdlp[0]); (void) ddi_intr_remove_handler(pcic->pc_pci_intr_hdlp[0]); (void) ddi_intr_free(pcic->pc_pci_intr_hdlp[0]); kmem_free(pcic->pc_pci_intr_hdlp, sizeof (ddi_intr_handle_t)); pcic->pc_flags = 0; mutex_destroy(&pcic->pc_lock); mutex_destroy(&pcic->intr_lock); cv_destroy(&pcic->pm_cv); if (pcic->pc_flags & PCF_PCIBUS) ddi_regs_map_free(&pcic->cfg_handle); if (pcic->handle) ddi_regs_map_free(&pcic->handle); kmem_free(pcic, sizeof (pcicdev_t)); ddi_soft_state_free(pcic_soft_state_p, ddi_get_instance(dip)); return (DDI_SUCCESS); case DDI_SUSPEND: case DDI_PM_SUSPEND: /* * we got a suspend event (either real or imagined) * so notify the nexus proper that all existing cards * should go away. */ mutex_enter(&pcic->pc_lock); #ifdef CARDBUS if (pcic->pc_flags & PCF_CARDBUS) { for (i = 0; i < pcic->pc_numsockets; i++) { if ((pcic->pc_sockets[i].pcs_flags & (PCS_CARD_PRESENT|PCS_CARD_ISCARDBUS)) == (PCS_CARD_PRESENT|PCS_CARD_ISCARDBUS)) { pcmcia_cb_suspended( pcic->pc_sockets[i].pcs_socket); } } cardbus_save_children(ddi_get_child(dip)); } #endif /* turn everything off for all sockets and chips */ for (i = 0; i < pcic->pc_numsockets; i++) { if (pcic->pc_sockets[i].pcs_debounce_id) pcic_rm_debqueue( pcic->pc_sockets[i].pcs_debounce_id); pcic->pc_sockets[i].pcs_debounce_id = 0; pcic_putb(pcic, i, PCIC_MANAGEMENT_INT, 0); pcic_putb(pcic, i, PCIC_CARD_DETECT, 0); pcic_putb(pcic, i, PCIC_MAPPING_ENABLE, 0); /* disable interrupts and put card into RESET */ pcic_putb(pcic, i, PCIC_INTERRUPT, 0); pcic_putb(pcic, i, PCIC_POWER_CONTROL, 0); if (pcic->pc_flags & PCF_CBPWRCTL) pcic_putcb(pcic, CB_CONTROL, 0); if (pcic->pc_sockets[i].pcs_flags & PCS_CARD_PRESENT) { pcic->pc_sockets[i].pcs_flags = PCS_STARTING; /* * Because we are half way through a save * all this does is schedule a removal event * to cs for when the system comes back. * This doesn't actually matter. */ if (!pcic_do_pcmcia_sr && pcic_do_removal && pcic->pc_callback) { PC_CALLBACK(pcic->dip, pcic->pc_cb_arg, PCE_CARD_REMOVAL, pcic->pc_sockets[i].pcs_socket); } } } pcic->pc_flags |= PCF_SUSPENDED; mutex_exit(&pcic->pc_lock); /* * when true power management exists, save the adapter * state here to enable a recovery. For the emulation * condition, the state is gone */ return (DDI_SUCCESS); default: return (EINVAL); } } static uint32_t pcic_tisysctl_onbits = ((1<<27) | (1<<15) | (1<<14)); static uint32_t pcic_tisysctl_offbits = 0; static uint32_t pcic_default_latency = 0x40; static void pcic_setup_adapter(pcicdev_t *pcic) { int i; int value, flags; if (pcic->pc_flags & PCF_PCIBUS) { /* * all PCI-to-PCMCIA bus bridges need memory and I/O enabled */ flags = (PCIC_ENABLE_IO | PCIC_ENABLE_MEM); pcic_iomem_pci_ctl(pcic->cfg_handle, pcic->cfgaddr, flags); } /* enable each socket */ for (i = 0; i < pcic->pc_numsockets; i++) { pcic->pc_sockets[i].pcs_flags = 0; /* find out the socket capabilities (I/O vs memory) */ value = pcic_getb(pcic, i, PCIC_CHIP_REVISION) & PCIC_REV_ID_MASK; if (value == PCIC_REV_ID_IO || value == PCIC_REV_ID_BOTH) pcic->pc_sockets[i].pcs_flags |= PCS_SOCKET_IO; /* disable all windows just in case */ pcic_putb(pcic, i, PCIC_MAPPING_ENABLE, 0); switch (pcic->pc_type) { uint32_t cfg32; uint16_t cfg16; uint8_t cfg; /* enable extended registers for Vadem */ case PCIC_VADEM_VG469: case PCIC_VADEM: /* enable card status change interrupt for socket */ break; case PCIC_I82365SL: break; case PCIC_CL_PD6710: pcic_putb(pcic, 0, PCIC_MISC_CTL_2, PCIC_LED_ENABLE); break; /* * On the CL_6730, we need to set up the interrupt * signalling mode (PCI mode) and set the SMI and * IRQ interrupt lines to PCI/level-mode. */ case PCIC_CL_PD6730: switch (pcic->pc_intr_mode) { case PCIC_INTR_MODE_PCI_1: clext_reg_write(pcic, i, PCIC_CLEXT_MISC_CTL_3, ((clext_reg_read(pcic, i, PCIC_CLEXT_MISC_CTL_3) & ~PCIC_CLEXT_INT_PCI) | PCIC_CLEXT_INT_PCI)); clext_reg_write(pcic, i, PCIC_CLEXT_EXT_CTL_1, (PCIC_CLEXT_IRQ_LVL_MODE | PCIC_CLEXT_SMI_LVL_MODE)); cfg = PCIC_CL_LP_DYN_MODE; pcic_putb(pcic, i, PCIC_MISC_CTL_2, cfg); break; case PCIC_INTR_MODE_ISA: break; } break; /* * On the CL_6729, we set the SMI and IRQ interrupt * lines to PCI/level-mode. as well as program the * correct clock speed divider bit. */ case PCIC_CL_PD6729: switch (pcic->pc_intr_mode) { case PCIC_INTR_MODE_PCI_1: clext_reg_write(pcic, i, PCIC_CLEXT_EXT_CTL_1, (PCIC_CLEXT_IRQ_LVL_MODE | PCIC_CLEXT_SMI_LVL_MODE)); break; case PCIC_INTR_MODE_ISA: break; } if (pcic->bus_speed > PCIC_PCI_25MHZ && i == 0) { cfg = 0; cfg |= PCIC_CL_TIMER_CLK_DIV; pcic_putb(pcic, i, PCIC_MISC_CTL_2, cfg); } break; case PCIC_INTEL_i82092: cfg = PCIC_82092_EN_TIMING; if (pcic->bus_speed < PCIC_SYSCLK_33MHZ) cfg |= PCIC_82092_PCICLK_25MHZ; ddi_put8(pcic->cfg_handle, pcic->cfgaddr + PCIC_82092_PCICON, cfg); break; case PCIC_TI_PCI1130: case PCIC_TI_PCI1131: case PCIC_TI_PCI1250: case PCIC_TI_PCI1031: cfg = ddi_get8(pcic->cfg_handle, pcic->cfgaddr + PCIC_DEVCTL_REG); cfg &= ~PCIC_DEVCTL_INTR_MASK; switch (pcic->pc_intr_mode) { case PCIC_INTR_MODE_ISA: cfg |= PCIC_DEVCTL_INTR_ISA; break; } #ifdef PCIC_DEBUG if (pcic_debug) { cmn_err(CE_CONT, "pcic_setup_adapter: " "write reg 0x%x=%x \n", PCIC_DEVCTL_REG, cfg); } #endif ddi_put8(pcic->cfg_handle, pcic->cfgaddr + PCIC_DEVCTL_REG, cfg); cfg = ddi_get8(pcic->cfg_handle, pcic->cfgaddr + PCIC_CRDCTL_REG); cfg &= ~(PCIC_CRDCTL_PCIINTR|PCIC_CRDCTL_PCICSC| PCIC_CRDCTL_PCIFUNC); switch (pcic->pc_intr_mode) { case PCIC_INTR_MODE_PCI_1: cfg |= PCIC_CRDCTL_PCIINTR | PCIC_CRDCTL_PCICSC | PCIC_CRDCTL_PCIFUNC; pcic->pc_flags |= PCF_USE_SMI; break; } #ifdef PCIC_DEBUG if (pcic_debug) { cmn_err(CE_CONT, "pcic_setup_adapter: " " write reg 0x%x=%x \n", PCIC_CRDCTL_REG, cfg); } #endif ddi_put8(pcic->cfg_handle, pcic->cfgaddr + PCIC_CRDCTL_REG, cfg); break; case PCIC_TI_PCI1221: case PCIC_TI_PCI1225: cfg = ddi_get8(pcic->cfg_handle, pcic->cfgaddr + PCIC_DEVCTL_REG); cfg |= (PCIC_DEVCTL_INTR_DFLT | PCIC_DEVCTL_3VCAPABLE); #ifdef PCIC_DEBUG if (pcic_debug) { cmn_err(CE_CONT, "pcic_setup_adapter: " " write reg 0x%x=%x \n", PCIC_DEVCTL_REG, cfg); } #endif ddi_put8(pcic->cfg_handle, pcic->cfgaddr + PCIC_DEVCTL_REG, cfg); cfg = ddi_get8(pcic->cfg_handle, pcic->cfgaddr + PCIC_DIAG_REG); if (pcic->pc_type == PCIC_TI_PCI1225) { cfg |= (PCIC_DIAG_CSC | PCIC_DIAG_ASYNC); } else { cfg |= PCIC_DIAG_ASYNC; } pcic->pc_flags |= PCF_USE_SMI; #ifdef PCIC_DEBUG if (pcic_debug) { cmn_err(CE_CONT, "pcic_setup_adapter: " " write reg 0x%x=%x \n", PCIC_DIAG_REG, cfg); } #endif ddi_put8(pcic->cfg_handle, pcic->cfgaddr + PCIC_DIAG_REG, cfg); break; case PCIC_TI_PCI1520: case PCIC_TI_PCI1510: case PCIC_TI_VENDOR: if (pcic->pc_intr_mode == PCIC_INTR_MODE_ISA) { cfg = ddi_get8(pcic->cfg_handle, pcic->cfgaddr + PCIC_BRIDGE_CTL_REG); cfg |= PCIC_FUN_INT_MOD_ISA; ddi_put8(pcic->cfg_handle, pcic->cfgaddr + PCIC_BRIDGE_CTL_REG, cfg); } cfg = ddi_get8(pcic->cfg_handle, pcic->cfgaddr + PCIC_DEVCTL_REG); cfg &= ~PCIC_DEVCTL_INTR_MASK; if (pcic->pc_intr_mode == PCIC_INTR_MODE_ISA) cfg |= PCIC_DEVCTL_INTR_ISA; ddi_put8(pcic->cfg_handle, pcic->cfgaddr + PCIC_DEVCTL_REG, cfg); /* tie INTA and INTB together */ cfg = ddi_get8(pcic->cfg_handle, (pcic->cfgaddr + PCIC_SYSCTL_REG + 3)); cfg |= PCIC_SYSCTL_INTRTIE; ddi_put8(pcic->cfg_handle, (pcic->cfgaddr + PCIC_SYSCTL_REG + 3), cfg); cfg = ddi_get8(pcic->cfg_handle, pcic->cfgaddr + PCIC_DIAG_REG); cfg |= (PCIC_DIAG_CSC | PCIC_DIAG_ASYNC); ddi_put8(pcic->cfg_handle, pcic->cfgaddr + PCIC_DIAG_REG, cfg); break; case PCIC_TI_PCI1410: cfg = ddi_get8(pcic->cfg_handle, pcic->cfgaddr + PCIC_DIAG_REG); cfg |= (PCIC_DIAG_CSC | PCIC_DIAG_ASYNC); ddi_put8(pcic->cfg_handle, pcic->cfgaddr + PCIC_DIAG_REG, cfg); break; case PCIC_TOSHIBA_TOPIC100: case PCIC_TOSHIBA_TOPIC95: case PCIC_TOSHIBA_VENDOR: cfg = ddi_get8(pcic->cfg_handle, pcic->cfgaddr + PCIC_TOSHIBA_SLOT_CTL_REG); cfg |= (PCIC_TOSHIBA_SCR_SLOTON | PCIC_TOSHIBA_SCR_SLOTEN); cfg &= (~PCIC_TOSHIBA_SCR_PRT_MASK); cfg |= PCIC_TOSHIBA_SCR_PRT_3E2; ddi_put8(pcic->cfg_handle, pcic->cfgaddr + PCIC_TOSHIBA_SLOT_CTL_REG, cfg); cfg = ddi_get8(pcic->cfg_handle, pcic->cfgaddr + PCIC_TOSHIBA_INTR_CTL_REG); switch (pcic->pc_intr_mode) { case PCIC_INTR_MODE_ISA: cfg &= ~PCIC_TOSHIBA_ICR_SRC; ddi_put8(pcic->cfg_handle, pcic->cfgaddr + PCIC_TOSHIBA_INTR_CTL_REG, cfg); cfg = ddi_get8(pcic->cfg_handle, pcic->cfgaddr + PCIC_BRIDGE_CTL_REG); cfg |= PCIC_FUN_INT_MOD_ISA; ddi_put8(pcic->cfg_handle, pcic->cfgaddr + PCIC_BRIDGE_CTL_REG, cfg); break; case PCIC_INTR_MODE_PCI_1: cfg |= PCIC_TOSHIBA_ICR_SRC; cfg &= (~PCIC_TOSHIBA_ICR_PIN_MASK); cfg |= PCIC_TOSHIBA_ICR_PIN_INTA; ddi_put8(pcic->cfg_handle, pcic->cfgaddr + PCIC_TOSHIBA_INTR_CTL_REG, cfg); break; } break; case PCIC_O2MICRO_VENDOR: cfg32 = ddi_get32(pcic->cfg_handle, (uint32_t *)(pcic->cfgaddr + PCIC_O2MICRO_MISC_CTL)); switch (pcic->pc_intr_mode) { case PCIC_INTR_MODE_ISA: cfg32 |= (PCIC_O2MICRO_ISA_LEGACY | PCIC_O2MICRO_INT_MOD_PCI); ddi_put32(pcic->cfg_handle, (uint32_t *)(pcic->cfgaddr + PCIC_O2MICRO_MISC_CTL), cfg32); cfg = ddi_get8(pcic->cfg_handle, pcic->cfgaddr + PCIC_BRIDGE_CTL_REG); cfg |= PCIC_FUN_INT_MOD_ISA; ddi_put8(pcic->cfg_handle, pcic->cfgaddr + PCIC_BRIDGE_CTL_REG, cfg); break; case PCIC_INTR_MODE_PCI_1: cfg32 &= ~PCIC_O2MICRO_ISA_LEGACY; cfg32 |= PCIC_O2MICRO_INT_MOD_PCI; ddi_put32(pcic->cfg_handle, (uint32_t *)(pcic->cfgaddr + PCIC_O2MICRO_MISC_CTL), cfg32); break; } break; case PCIC_RICOH_VENDOR: if (pcic->pc_intr_mode == PCIC_INTR_MODE_ISA) { cfg16 = ddi_get16(pcic->cfg_handle, (uint16_t *)(pcic->cfgaddr + PCIC_RICOH_MISC_CTL_2)); cfg16 |= (PCIC_RICOH_CSC_INT_MOD | PCIC_RICOH_FUN_INT_MOD); ddi_put16(pcic->cfg_handle, (uint16_t *)(pcic->cfgaddr + PCIC_RICOH_MISC_CTL_2), cfg16); cfg16 = ddi_get16(pcic->cfg_handle, (uint16_t *)(pcic->cfgaddr + PCIC_RICOH_MISC_CTL)); cfg16 |= PCIC_RICOH_SIRQ_EN; ddi_put16(pcic->cfg_handle, (uint16_t *)(pcic->cfgaddr + PCIC_RICOH_MISC_CTL), cfg16); cfg = ddi_get8(pcic->cfg_handle, pcic->cfgaddr + PCIC_BRIDGE_CTL_REG); cfg |= PCIC_FUN_INT_MOD_ISA; ddi_put8(pcic->cfg_handle, pcic->cfgaddr + PCIC_BRIDGE_CTL_REG, cfg); } break; default: break; } /* switch */ /* * The default value in the EEPROM (loaded on reset) for * MFUNC0/MFUNC1 may be incorrect. Here we make sure that * MFUNC0 is connected to INTA, and MFUNC1 is connected to * INTB. This applies to all TI CardBus controllers. */ if ((pcic->pc_type >> 16) == PCIC_TI_VENDORID && pcic->pc_intr_mode == PCIC_INTR_MODE_PCI_1) { value = ddi_get32(pcic->cfg_handle, (uint32_t *)(pcic->cfgaddr + PCIC_MFROUTE_REG)); value &= ~0xff; ddi_put32(pcic->cfg_handle, (uint32_t *)(pcic->cfgaddr + PCIC_MFROUTE_REG), value|PCIC_TI_MFUNC_SEL); } /* setup general card status change interrupt */ switch (pcic->pc_type) { case PCIC_TI_PCI1225: case PCIC_TI_PCI1221: case PCIC_TI_PCI1031: case PCIC_TI_PCI1520: case PCIC_TI_PCI1410: pcic_putb(pcic, i, PCIC_MANAGEMENT_INT, PCIC_CHANGE_DEFAULT); break; default: if (pcic->pc_intr_mode == PCIC_INTR_MODE_PCI_1) { pcic_putb(pcic, i, PCIC_MANAGEMENT_INT, PCIC_CHANGE_DEFAULT); break; } else { pcic_putb(pcic, i, PCIC_MANAGEMENT_INT, PCIC_CHANGE_DEFAULT | (pcic->pc_sockets[i].pcs_smi << 4)); break; } } pcic->pc_flags |= PCF_INTRENAB; /* take card out of RESET */ pcic_putb(pcic, i, PCIC_INTERRUPT, PCIC_RESET); /* turn power off and let CS do this */ pcic_putb(pcic, i, PCIC_POWER_CONTROL, 0); /* final chip specific initialization */ switch (pcic->pc_type) { case PCIC_VADEM: pcic_putb(pcic, i, PCIC_VG_CONTROL, PCIC_VC_DELAYENABLE); pcic->pc_flags |= PCF_DEBOUNCE; /* FALLTHROUGH */ case PCIC_I82365SL: pcic_putb(pcic, i, PCIC_GLOBAL_CONTROL, PCIC_GC_CSC_WRITE); /* clear any pending interrupts */ value = pcic_getb(pcic, i, PCIC_CARD_STATUS_CHANGE); pcic_putb(pcic, i, PCIC_CARD_STATUS_CHANGE, value); break; /* The 82092 uses PCI config space to enable interrupts */ case PCIC_INTEL_i82092: pcic_82092_smiirq_ctl(pcic, i, PCIC_82092_CTL_SMI, PCIC_82092_INT_ENABLE); break; case PCIC_CL_PD6729: if (pcic->bus_speed >= PCIC_PCI_DEF_SYSCLK && i == 0) { value = pcic_getb(pcic, i, PCIC_MISC_CTL_2); pcic_putb(pcic, i, PCIC_MISC_CTL_2, value | PCIC_CL_TIMER_CLK_DIV); } break; } /* switch */ #if defined(PCIC_DEBUG) if (pcic_debug) cmn_err(CE_CONT, "socket %d value=%x, flags = %x (%s)\n", i, value, pcic->pc_sockets[i].pcs_flags, (pcic->pc_sockets[i].pcs_flags & PCS_CARD_PRESENT) ? "card present" : "no card"); #endif } } /* * pcic_intr(caddr_t, caddr_t) * interrupt handler for the PCIC style adapter * handles all basic interrupts and also checks * for status changes and notifies the nexus if * necessary * * On PCI bus adapters, also handles all card * IO interrupts. */ /*ARGSUSED*/ uint32_t pcic_intr(caddr_t arg1, caddr_t arg2) { pcicdev_t *pcic = (pcicdev_t *)arg1; int value = 0, i, ret = DDI_INTR_UNCLAIMED; uint8_t status; uint_t io_ints; #if defined(PCIC_DEBUG) pcic_err(pcic->dip, 0xf, "pcic_intr: enter pc_flags=0x%x PCF_ATTACHED=0x%x" " pc_numsockets=%d \n", pcic->pc_flags, PCF_ATTACHED, pcic->pc_numsockets); #endif if (!(pcic->pc_flags & PCF_ATTACHED)) return (DDI_INTR_UNCLAIMED); mutex_enter(&pcic->intr_lock); if (pcic->pc_flags & PCF_SUSPENDED) { mutex_exit(&pcic->intr_lock); return (ret); } /* * need to change to only ACK and touch the slot that * actually caused the interrupt. Currently everything * is acked * * we need to look at all known sockets to determine * what might have happened, so step through the list * of them */ /* * Set the bitmask for IO interrupts to initially include all sockets */ io_ints = (1 << pcic->pc_numsockets) - 1; for (i = 0; i < pcic->pc_numsockets; i++) { int card_type; pcic_socket_t *sockp; int value_cb = 0; sockp = &pcic->pc_sockets[i]; /* get the socket's I/O addresses */ if (sockp->pcs_flags & PCS_WAITING) { io_ints &= ~(1 << i); continue; } if (sockp->pcs_flags & PCS_CARD_IO) card_type = IF_IO; else card_type = IF_MEMORY; if (pcic->pc_io_type == PCIC_IO_TYPE_YENTA) value_cb = pcic_getcb(pcic, CB_STATUS_EVENT); value = pcic_change(pcic, i); if ((value != 0) || (value_cb != 0)) { int x = pcic->pc_cb_arg; ret = DDI_INTR_CLAIMED; #if defined(PCIC_DEBUG) pcic_err(pcic->dip, 0x9, "card_type = %d, value_cb = 0x%x\n", card_type, value_cb ? value_cb : pcic_getcb(pcic, CB_STATUS_EVENT)); if (pcic_debug) cmn_err(CE_CONT, "\tchange on socket %d (%x)\n", i, value); #endif /* find out what happened */ status = pcic_getb(pcic, i, PCIC_INTERFACE_STATUS); /* acknowledge the interrupt */ if (value_cb) pcic_putcb(pcic, CB_STATUS_EVENT, value_cb); if (value) pcic_putb(pcic, i, PCIC_CARD_STATUS_CHANGE, value); if (pcic->pc_callback == NULL) { /* if not callback handler, nothing to do */ continue; } /* Card Detect */ if (value & PCIC_CD_DETECT || value_cb & CB_PS_CCDMASK) { uint8_t irq; #if defined(PCIC_DEBUG) if (pcic_debug) cmn_err(CE_CONT, "\tcd_detect: status=%x," " flags=%x\n", status, sockp->pcs_flags); #else #ifdef lint if (status == 0) status++; #endif #endif /* * Turn off all interrupts for this socket here. */ irq = pcic_getb(pcic, sockp->pcs_socket, PCIC_MANAGEMENT_INT); irq &= ~PCIC_CHANGE_MASK; pcic_putb(pcic, sockp->pcs_socket, PCIC_MANAGEMENT_INT, irq); pcic_putcb(pcic, CB_STATUS_MASK, 0x0); /* * Put the socket in debouncing state so that * the leaf driver won't receive interrupts. * Crucial for handling surprise-removal. */ sockp->pcs_flags |= PCS_DEBOUNCING; if (!sockp->pcs_cd_softint_flg) { sockp->pcs_cd_softint_flg = 1; (void) ddi_intr_trigger_softint( sockp->pcs_cd_softint_hdl, NULL); } io_ints &= ~(1 << i); } /* PCIC_CD_DETECT */ /* Ready/Change Detect */ sockp->pcs_state ^= SBM_RDYBSY; if (card_type == IF_MEMORY && value & PCIC_RD_DETECT) { sockp->pcs_flags |= PCS_READY; PC_CALLBACK(pcic->dip, x, PCE_CARD_READY, i); } /* Battery Warn Detect */ if (card_type == IF_MEMORY && value & PCIC_BW_DETECT && !(sockp->pcs_state & SBM_BVD2)) { sockp->pcs_state |= SBM_BVD2; PC_CALLBACK(pcic->dip, x, PCE_CARD_BATTERY_WARN, i); } /* Battery Dead Detect */ if (value & PCIC_BD_DETECT) { /* * need to work out event if RI not enabled * and card_type == IF_IO */ if (card_type == IF_MEMORY && !(sockp->pcs_state & SBM_BVD1)) { sockp->pcs_state |= SBM_BVD1; PC_CALLBACK(pcic->dip, x, PCE_CARD_BATTERY_DEAD, i); } else { /* * information in pin replacement * register if one is available */ PC_CALLBACK(pcic->dip, x, PCE_CARD_STATUS_CHANGE, i); } /* IF_MEMORY */ } /* PCIC_BD_DETECT */ } /* if pcic_change */ /* * for any controllers that we can detect whether a socket * had an interrupt for the PC Card, we should sort that out * here. */ } /* for pc_numsockets */ /* * If we're on a PCI bus, we may need to cycle through each IO * interrupt handler that is registered since they all * share the same interrupt line. */ #if defined(PCIC_DEBUG) pcic_err(pcic->dip, 0xf, "pcic_intr: pc_intr_mode=%d pc_type=%x io_ints=0x%x\n", pcic->pc_intr_mode, pcic->pc_type, io_ints); #endif if (io_ints) { if (pcic_do_io_intr(pcic, io_ints) == DDI_INTR_CLAIMED) ret = DDI_INTR_CLAIMED; } mutex_exit(&pcic->intr_lock); #if defined(PCIC_DEBUG) pcic_err(pcic->dip, 0xf, "pcic_intr: ret=%d value=%d DDI_INTR_CLAIMED=%d\n", ret, value, DDI_INTR_CLAIMED); #endif return (ret); } /* * pcic_change() * check to see if this socket had a change in state * by checking the status change register */ static int pcic_change(pcicdev_t *pcic, int socket) { return (pcic_getb(pcic, socket, PCIC_CARD_STATUS_CHANGE)); } /* * pcic_do_io_intr - calls client interrupt handlers */ static int pcic_do_io_intr(pcicdev_t *pcic, uint32_t sockets) { inthandler_t *tmp; int ret = DDI_INTR_UNCLAIMED; #if defined(PCIC_DEBUG) pcic_err(pcic->dip, 0xf, "pcic_do_io_intr: pcic=%p sockets=%d irq_top=%p\n", (void *)pcic, (int)sockets, (void *)pcic->irq_top); #endif if (pcic->irq_top != NULL) { tmp = pcic->irq_current; do { int cur = pcic->irq_current->socket; pcic_socket_t *sockp = &pcic->pc_sockets[cur]; #if defined(PCIC_DEBUG) pcic_err(pcic->dip, 0xf, "\t pcs_flags=0x%x PCS_CARD_PRESENT=0x%x\n", sockp->pcs_flags, PCS_CARD_PRESENT); pcic_err(pcic->dip, 0xf, "\t sockets=%d cur=%d intr=%p arg1=%p " "arg2=%p\n", sockets, cur, (void *)pcic->irq_current->intr, pcic->irq_current->arg1, pcic->irq_current->arg2); #endif if ((sockp->pcs_flags & PCS_CARD_PRESENT) && !(sockp->pcs_flags & PCS_DEBOUNCING) && (sockets & (1 << cur))) { if ((*pcic->irq_current->intr)(pcic->irq_current->arg1, pcic->irq_current->arg2) == DDI_INTR_CLAIMED) ret = DDI_INTR_CLAIMED; #if defined(PCIC_DEBUG) pcic_err(pcic->dip, 0xf, "\t ret=%d DDI_INTR_CLAIMED=%d\n", ret, DDI_INTR_CLAIMED); #endif } if ((pcic->irq_current = pcic->irq_current->next) == NULL) pcic->irq_current = pcic->irq_top; } while (pcic->irq_current != tmp); if ((pcic->irq_current = pcic->irq_current->next) == NULL) pcic->irq_current = pcic->irq_top; } else { ret = DDI_INTR_UNCLAIMED; } #if defined(PCIC_DEBUG) pcic_err(pcic->dip, 0xf, "pcic_do_io_intr: exit ret=%d DDI_INTR_CLAIMED=%d\n", ret, DDI_INTR_CLAIMED); #endif return (ret); } /* * pcic_inquire_adapter() * SocketServices InquireAdapter function * get characteristics of the physical adapter */ /*ARGSUSED*/ static int pcic_inquire_adapter(dev_info_t *dip, inquire_adapter_t *config) { anp_t *anp = ddi_get_driver_private(dip); pcicdev_t *pcic = anp->an_private; config->NumSockets = pcic->pc_numsockets; config->NumWindows = pcic->pc_numsockets * PCIC_NUMWINSOCK; config->NumEDCs = 0; config->AdpCaps = 0; config->ActiveHigh = 0; config->ActiveLow = PCIC_AVAIL_IRQS; config->NumPower = pcic->pc_numpower; config->power_entry = pcic->pc_power; /* until we resolve this */ #if defined(PCIC_DEBUG) if (pcic_debug) { cmn_err(CE_CONT, "pcic_inquire_adapter:\n"); cmn_err(CE_CONT, "\tNumSockets=%d\n", config->NumSockets); cmn_err(CE_CONT, "\tNumWindows=%d\n", config->NumWindows); } #endif config->ResourceFlags = 0; switch (pcic->pc_intr_mode) { case PCIC_INTR_MODE_PCI_1: config->ResourceFlags |= RES_OWN_IRQ | RES_IRQ_NEXUS | RES_IRQ_SHAREABLE; break; } return (SUCCESS); } /* * pcic_callback() * The PCMCIA nexus calls us via this function * in order to set the callback function we are * to call the nexus with */ /*ARGSUSED*/ static int pcic_callback(dev_info_t *dip, int (*handler)(), int arg) { anp_t *anp = ddi_get_driver_private(dip); pcicdev_t *pcic = anp->an_private; if (handler != NULL) { pcic->pc_callback = handler; pcic->pc_cb_arg = arg; pcic->pc_flags |= PCF_CALLBACK; } else { pcic->pc_callback = NULL; pcic->pc_cb_arg = 0; pcic->pc_flags &= ~PCF_CALLBACK; } /* * we're now registered with the nexus * it is acceptable to do callbacks at this point. * don't call back from here though since it could block */ return (PC_SUCCESS); } /* * pcic_calc_speed (pcicdev_t *pcic, uint32_t speed) * calculate the speed bits from the specified memory speed * there may be more to do here */ static int pcic_calc_speed(pcicdev_t *pcic, uint32_t speed) { uint32_t wspeed = 1; /* assume 1 wait state when unknown */ uint32_t bspeed = PCIC_ISA_DEF_SYSCLK; switch (pcic->pc_type) { case PCIC_I82365SL: case PCIC_VADEM: case PCIC_VADEM_VG469: default: /* Intel chip wants it in waitstates */ wspeed = mhztons(PCIC_ISA_DEF_SYSCLK) * 3; if (speed <= wspeed) wspeed = 0; else if (speed <= (wspeed += mhztons(bspeed))) wspeed = 1; else if (speed <= (wspeed += mhztons(bspeed))) wspeed = 2; else wspeed = 3; wspeed <<= 6; /* put in right bit positions */ break; case PCIC_INTEL_i82092: wspeed = SYSMEM_82092_80NS; if (speed > 80) wspeed = SYSMEM_82092_100NS; if (speed > 100) wspeed = SYSMEM_82092_150NS; if (speed > 150) wspeed = SYSMEM_82092_200NS; if (speed > 200) wspeed = SYSMEM_82092_250NS; if (speed > 250) wspeed = SYSMEM_82092_600NS; wspeed <<= 5; /* put in right bit positions */ break; } /* switch */ return (wspeed); } /* * These values are taken from the PC Card Standard Electrical Specification. * Generally the larger value is taken if 2 are possible. */ static struct pcic_card_times { uint16_t cycle; /* Speed as found in the atribute space of he card. */ uint16_t setup; /* Corresponding address setup time. */ uint16_t width; /* Corresponding width, OE or WE. */ uint16_t hold; /* Corresponding data or address hold time. */ } pcic_card_times[] = { /* * Note: The rounded up times for 250, 200 & 150 have been increased * due to problems with the 3-Com ethernet cards (pcelx) on UBIIi. * See BugID 00663. */ /* * Rounded up times Original times from * that add up to the the PCMCIA Spec. * cycle time. */ {600, 180, 370, 140}, /* 100, 300, 70 */ {400, 120, 300, 90}, /* Made this one up */ {250, 100, 190, 70}, /* 30, 150, 30 */ {200, 80, 170, 70}, /* 20, 120, 30 */ {150, 50, 110, 40}, /* 20, 80, 20 */ {100, 40, 80, 40}, /* 10, 60, 15 */ {0, 10, 60, 15} /* 10, 60, 15 */ }; /* * pcic_set_cdtimers * This is specific to several Cirrus Logic chips */ static void pcic_set_cdtimers(pcicdev_t *pcic, int socket, uint32_t speed, int tset) { int cmd, set, rec, offset, clk_pulse; struct pcic_card_times *ctp; if ((tset == IOMEM_CLTIMER_SET_1) || (tset == SYSMEM_CLTIMER_SET_1)) offset = 3; else offset = 0; clk_pulse = mhztons(pcic->bus_speed); for (ctp = pcic_card_times; speed < ctp->cycle; ctp++); /* * Add (clk_pulse/2) and an extra 1 to account for rounding errors. */ set = ((ctp->setup + 10 + 1 + (clk_pulse/2))/clk_pulse) - 1; if (set < 0) set = 0; cmd = ((ctp->width + 10 + 1 + (clk_pulse/2))/clk_pulse) - 1; if (cmd < 0) cmd = 0; rec = ((ctp->hold + 10 + 1 + (clk_pulse/2))/clk_pulse) - 2; if (rec < 0) rec = 0; #if defined(PCIC_DEBUG) pcic_err(pcic->dip, 8, "pcic_set_cdtimers(%d, Timer Set %d)\n" "ct=%d, cp=%d, cmd=0x%x, setup=0x%x, rec=0x%x\n", (unsigned)speed, offset == 3 ? 1 : 0, ctp->cycle, clk_pulse, cmd, set, rec); #endif pcic_putb(pcic, socket, PCIC_TIME_COMMAND_0 + offset, cmd); pcic_putb(pcic, socket, PCIC_TIME_SETUP_0 + offset, set); pcic_putb(pcic, socket, PCIC_TIME_RECOVER_0 + offset, rec); } /* * pcic_set_window * essentially the same as the Socket Services specification * We use socket and not adapter since they are identifiable * but the rest is the same * * dip pcic driver's device information * window parameters for the request */ static int pcic_set_window(dev_info_t *dip, set_window_t *window) { anp_t *anp = ddi_get_driver_private(dip); pcicdev_t *pcic = anp->an_private; int select; int socket, pages, which, ret; pcic_socket_t *sockp = &pcic->pc_sockets[window->socket]; ra_return_t res; ndi_ra_request_t req; uint32_t base = window->base; #if defined(PCIC_DEBUG) if (pcic_debug) { cmn_err(CE_CONT, "pcic_set_window: entered\n"); cmn_err(CE_CONT, "\twindow=%d, socket=%d, WindowSize=%d, speed=%d\n", window->window, window->socket, window->WindowSize, window->speed); cmn_err(CE_CONT, "\tbase=%x, state=%x\n", (unsigned)window->base, (unsigned)window->state); } #endif /* * do some basic sanity checking on what we support * we don't do paged mode */ if (window->state & WS_PAGED) { cmn_err(CE_WARN, "pcic_set_window: BAD_ATTRIBUTE\n"); return (BAD_ATTRIBUTE); } /* * we don't care about previous mappings. * Card Services will deal with that so don't * even check */ socket = window->socket; if (!(window->state & WS_IO)) { int win, tmp; pcs_memwin_t *memp; #if defined(PCIC_DEBUG) if (pcic_debug) cmn_err(CE_CONT, "\twindow type is memory\n"); #endif /* this is memory window mapping */ win = window->window % PCIC_NUMWINSOCK; tmp = window->window / PCIC_NUMWINSOCK; /* only windows 2-6 can do memory mapping */ if (tmp != window->socket || win < PCIC_IOWINDOWS) { cmn_err(CE_CONT, "\tattempt to map to non-mem window\n"); return (BAD_WINDOW); } if (window->WindowSize == 0) window->WindowSize = MEM_MIN; else if ((window->WindowSize & (PCIC_PAGE-1)) != 0) { cmn_err(CE_WARN, "pcic_set_window: BAD_SIZE\n"); return (BAD_SIZE); } mutex_enter(&pcic->pc_lock); /* protect the registers */ memp = &sockp->pcs_windows[win].mem; memp->pcw_speed = window->speed; win -= PCIC_IOWINDOWS; /* put in right range */ if (window->WindowSize != memp->pcw_len) which = memp->pcw_len; else which = 0; if (window->state & WS_ENABLED) { uint32_t wspeed; #if defined(PCIC_DEBUG) if (pcic_debug) { cmn_err(CE_CONT, "\tbase=%x, win=%d\n", (unsigned)base, win); if (which) cmn_err(CE_CONT, "\tneed to remap window\n"); } #endif if (which && (memp->pcw_status & PCW_MAPPED)) { ddi_regs_map_free(&memp->pcw_handle); res.ra_addr_lo = memp->pcw_base; res.ra_len = memp->pcw_len; (void) pcmcia_free_mem(memp->res_dip, &res); memp->pcw_status &= ~(PCW_MAPPED|PCW_ENABLED); memp->pcw_hostmem = NULL; memp->pcw_base = NULL; memp->pcw_len = 0; } which = window->WindowSize >> PAGE_SHIFT; if (!(memp->pcw_status & PCW_MAPPED)) { ret = 0; memp->pcw_base = base; bzero(&req, sizeof (req)); req.ra_len = which << PAGE_SHIFT; req.ra_addr = (uint64_t)memp->pcw_base; req.ra_boundbase = pcic->pc_base; req.ra_boundlen = pcic->pc_bound; req.ra_flags = (memp->pcw_base ? NDI_RA_ALLOC_SPECIFIED : 0) | NDI_RA_ALLOC_BOUNDED; req.ra_align_mask = (PAGESIZE - 1) | (PCIC_PAGE - 1); #if defined(PCIC_DEBUG) pcic_err(dip, 8, "\tlen 0x%"PRIx64 "addr 0x%"PRIx64"bbase 0x%"PRIx64 " blen 0x%"PRIx64" flags 0x%x" " algn 0x%"PRIx64"\n", req.ra_len, req.ra_addr, req.ra_boundbase, req.ra_boundlen, req.ra_flags, req.ra_align_mask); #endif ret = pcmcia_alloc_mem(dip, &req, &res, &memp->res_dip); if (ret == DDI_FAILURE) { mutex_exit(&pcic->pc_lock); cmn_err(CE_WARN, "\tpcmcia_alloc_mem() failed\n"); return (BAD_SIZE); } memp->pcw_base = res.ra_addr_lo; base = memp->pcw_base; #if defined(PCIC_DEBUG) if (pcic_debug) cmn_err(CE_CONT, "\tsetwindow: new base=%x\n", (unsigned)memp->pcw_base); #endif memp->pcw_len = window->WindowSize; which = pcmcia_map_reg(pcic->dip, window->child, &res, (uint32_t)(window->state & 0xffff) | (window->socket << 16), (caddr_t *)&memp->pcw_hostmem, &memp->pcw_handle, &window->attr, NULL); if (which != DDI_SUCCESS) { cmn_err(CE_WARN, "\tpcmcia_map_reg() " "failed\n"); res.ra_addr_lo = memp->pcw_base; res.ra_len = memp->pcw_len; (void) pcmcia_free_mem(memp->res_dip, &res); mutex_exit(&pcic->pc_lock); return (BAD_WINDOW); } memp->pcw_status |= PCW_MAPPED; #if defined(PCIC_DEBUG) if (pcic_debug) cmn_err(CE_CONT, "\tmap=%x, hostmem=%p\n", which, (void *)memp->pcw_hostmem); #endif } else { base = memp->pcw_base; } /* report the handle back to caller */ window->handle = memp->pcw_handle; #if defined(PCIC_DEBUG) if (pcic_debug) { cmn_err(CE_CONT, "\twindow mapped to %x@%x len=%d\n", (unsigned)window->base, (unsigned)memp->pcw_base, memp->pcw_len); } #endif /* find the register set offset */ select = win * PCIC_MEM_1_OFFSET; #if defined(PCIC_DEBUG) if (pcic_debug) cmn_err(CE_CONT, "\tselect=%x\n", select); #endif /* * at this point, the register window indicator has * been converted to be an offset from the first * set of registers that are used for programming * the window mapping and the offset used to select * the correct set of registers to access the * specified socket. This allows basing everything * off the _0 window */ /* map the physical page base address */ which = (window->state & WS_16BIT) ? SYSMEM_DATA_16 : 0; which |= (window->speed <= MEM_SPEED_MIN) ? SYSMEM_ZERO_WAIT : 0; /* need to select register set */ select = PCIC_MEM_1_OFFSET * win; pcic_putb(pcic, socket, PCIC_SYSMEM_0_STARTLOW + select, SYSMEM_LOW(base)); pcic_putb(pcic, socket, PCIC_SYSMEM_0_STARTHI + select, SYSMEM_HIGH(base) | which); /* * Some adapters can decode window addresses greater * than 16-bits worth, so handle them here. */ switch (pcic->pc_type) { case PCIC_INTEL_i82092: pcic_putb(pcic, socket, PCIC_82092_CPAGE, SYSMEM_EXT(base)); break; case PCIC_CL_PD6729: case PCIC_CL_PD6730: clext_reg_write(pcic, socket, PCIC_CLEXT_MMAP0_UA + win, SYSMEM_EXT(base)); break; case PCIC_TI_PCI1130: /* * Note that the TI chip has one upper byte * per socket so all windows get bound to a * 16MB segment. This must be detected and * handled appropriately. We can detect that * it is done by seeing if the pc_base has * changed and changing when the register * is first set. This will force the bounds * to be correct. */ if (pcic->pc_bound == 0xffffffff) { pcic_putb(pcic, socket, PCIC_TI_WINDOW_PAGE_PCI, SYSMEM_EXT(base)); pcic->pc_base = SYSMEM_EXT(base) << 24; pcic->pc_bound = 0x1000000; } break; case PCIC_TI_PCI1031: case PCIC_TI_PCI1131: case PCIC_TI_PCI1250: case PCIC_TI_PCI1225: case PCIC_TI_PCI1221: case PCIC_SMC_34C90: case PCIC_CL_PD6832: case PCIC_RICOH_RL5C466: case PCIC_TI_PCI1410: case PCIC_ENE_1410: case PCIC_TI_PCI1510: case PCIC_TI_PCI1520: case PCIC_O2_OZ6912: case PCIC_TI_PCI1420: case PCIC_ENE_1420: case PCIC_TI_VENDOR: case PCIC_TOSHIBA_TOPIC100: case PCIC_TOSHIBA_TOPIC95: case PCIC_TOSHIBA_VENDOR: case PCIC_RICOH_VENDOR: case PCIC_O2MICRO_VENDOR: pcic_putb(pcic, socket, PCIC_YENTA_MEM_PAGE + win, SYSMEM_EXT(base)); break; default: cmn_err(CE_NOTE, "pcic_set_window: unknown " "cardbus vendor:0x%X\n", pcic->pc_type); pcic_putb(pcic, socket, PCIC_YENTA_MEM_PAGE + win, SYSMEM_EXT(base)); break; } /* switch */ /* * specify the length of the mapped range * we convert to pages (rounding up) so that * the hardware gets the right thing */ pages = (window->WindowSize+PCIC_PAGE-1)/PCIC_PAGE; /* * Setup this window's timing. */ switch (pcic->pc_type) { case PCIC_CL_PD6729: case PCIC_CL_PD6730: case PCIC_CL_PD6710: case PCIC_CL_PD6722: wspeed = SYSMEM_CLTIMER_SET_0; pcic_set_cdtimers(pcic, socket, window->speed, wspeed); break; case PCIC_INTEL_i82092: default: wspeed = pcic_calc_speed(pcic, window->speed); break; } /* switch */ #if defined(PCIC_DEBUG) if (pcic_debug) cmn_err(CE_CONT, "\twindow %d speed bits = %x for " "%dns\n", win, (unsigned)wspeed, window->speed); #endif pcic_putb(pcic, socket, PCIC_SYSMEM_0_STOPLOW + select, SYSMEM_LOW(base + (pages * PCIC_PAGE)-1)); wspeed |= SYSMEM_HIGH(base + (pages * PCIC_PAGE)-1); pcic_putb(pcic, socket, PCIC_SYSMEM_0_STOPHI + select, wspeed); /* * now map the card's memory pages - we start with page * 0 * we also default to AM -- set page might change it */ base = memp->pcw_base; pcic_putb(pcic, socket, PCIC_CARDMEM_0_LOW + select, CARDMEM_LOW(0 - (uint32_t)base)); pcic_putb(pcic, socket, PCIC_CARDMEM_0_HI + select, CARDMEM_HIGH(0 - (uint32_t)base) | CARDMEM_REG_ACTIVE); /* * enable the window even though redundant * and SetPage may do it again. */ select = pcic_getb(pcic, socket, PCIC_MAPPING_ENABLE); select |= SYSMEM_WINDOW(win); pcic_putb(pcic, socket, PCIC_MAPPING_ENABLE, select); memp->pcw_offset = 0; memp->pcw_status |= PCW_ENABLED; } else { /* * not only do we unmap the memory, the * window has been turned off. */ if (which && memp->pcw_status & PCW_MAPPED) { ddi_regs_map_free(&memp->pcw_handle); res.ra_addr_lo = memp->pcw_base; res.ra_len = memp->pcw_len; (void) pcmcia_free_mem(memp->res_dip, &res); memp->pcw_hostmem = NULL; memp->pcw_status &= ~PCW_MAPPED; } /* disable current mapping */ select = pcic_getb(pcic, socket, PCIC_MAPPING_ENABLE); select &= ~SYSMEM_WINDOW(win); pcic_putb(pcic, socket, PCIC_MAPPING_ENABLE, select); memp->pcw_status &= ~PCW_ENABLED; } memp->pcw_len = window->WindowSize; window->handle = memp->pcw_handle; #if defined(PCIC_DEBUG) if (pcic_debug) xxdmp_all_regs(pcic, window->socket, -1); #endif } else { /* * This is a request for an IO window */ int win, tmp; pcs_iowin_t *winp; /* I/O windows */ #if defined(PCIC_DEBUG) if (pcic_debug) cmn_err(CE_CONT, "\twindow type is I/O\n"); #endif /* only windows 0 and 1 can do I/O */ win = window->window % PCIC_NUMWINSOCK; tmp = window->window / PCIC_NUMWINSOCK; if (win >= PCIC_IOWINDOWS || tmp != window->socket) { cmn_err(CE_WARN, "\twindow is out of range (%d)\n", window->window); return (BAD_WINDOW); } mutex_enter(&pcic->pc_lock); /* protect the registers */ winp = &sockp->pcs_windows[win].io; winp->pcw_speed = window->speed; if (window->WindowSize != 1 && window->WindowSize & 1) { /* we don't want an odd-size window */ window->WindowSize++; } winp->pcw_len = window->WindowSize; if (window->state & WS_ENABLED) { if (winp->pcw_status & PCW_MAPPED) { ddi_regs_map_free(&winp->pcw_handle); res.ra_addr_lo = winp->pcw_base; res.ra_len = winp->pcw_len; (void) pcmcia_free_io(winp->res_dip, &res); winp->pcw_status &= ~(PCW_MAPPED|PCW_ENABLED); } /* * if the I/O address wasn't allocated, allocate * it now. If it was allocated, it better * be free to use. * The winp->pcw_offset value is set and used * later on if the particular adapter * that we're running on has the ability * to translate IO accesses to the card * (such as some adapters in the Cirrus * Logic family). */ winp->pcw_offset = 0; /* * Setup the request parameters for the * requested base and length. If * we're on an adapter that has * IO window offset registers, then * we don't need a specific base * address, just a length, and then * we'll cause the correct IO address * to be generated on the socket by * setting up the IO window offset * registers. * For adapters that support this capability, we * always use the IO window offset registers, * even if the passed base/length would be in * range. */ base = window->base; bzero(&req, sizeof (req)); req.ra_len = window->WindowSize; req.ra_addr = (uint64_t) ((pcic->pc_flags & PCF_IO_REMAP) ? 0 : base); req.ra_flags = (req.ra_addr) ? NDI_RA_ALLOC_SPECIFIED : 0; req.ra_flags |= NDI_RA_ALIGN_SIZE; /* need to rethink this */ req.ra_boundbase = pcic->pc_iobase; req.ra_boundlen = pcic->pc_iobound; req.ra_flags |= NDI_RA_ALLOC_BOUNDED; #if defined(PCIC_DEBUG) pcic_err(dip, 8, "\tlen 0x%"PRIx64" addr 0x%"PRIx64 "bbase 0x%"PRIx64 "blen 0x%"PRIx64" flags 0x%x algn 0x%" PRIx64"\n", req.ra_len, (uint64_t)req.ra_addr, req.ra_boundbase, req.ra_boundlen, req.ra_flags, req.ra_align_mask); #endif /* * Try to allocate the space. If we fail this, * return the appropriate error depending * on whether the caller specified a * specific base address or not. */ if (pcmcia_alloc_io(dip, &req, &res, &winp->res_dip) == DDI_FAILURE) { winp->pcw_status &= ~PCW_ENABLED; mutex_exit(&pcic->pc_lock); cmn_err(CE_WARN, "Failed to alloc I/O:\n" "\tlen 0x%" PRIx64 " addr 0x%" PRIx64 "bbase 0x%" PRIx64 "blen 0x%" PRIx64 "flags 0x%x" "algn 0x%" PRIx64 "\n", req.ra_len, req.ra_addr, req.ra_boundbase, req.ra_boundlen, req.ra_flags, req.ra_align_mask); return (base?BAD_BASE:BAD_SIZE); } /* pcmcia_alloc_io */ /* * Don't change the original base. Either we use * the offset registers below (PCF_IO_REMAP is set) * or it was allocated correctly anyway. */ winp->pcw_base = res.ra_addr_lo; #if defined(PCIC_DEBUG) pcic_err(dip, 8, "\tsetwindow: new base=%x orig base 0x%x\n", (unsigned)winp->pcw_base, base); #endif if ((which = pcmcia_map_reg(pcic->dip, window->child, &res, (uint32_t)(window->state & 0xffff) | (window->socket << 16), (caddr_t *)&winp->pcw_hostmem, &winp->pcw_handle, &window->attr, base)) != DDI_SUCCESS) { cmn_err(CE_WARN, "pcmcia_map_reg()" "failed\n"); res.ra_addr_lo = winp->pcw_base; res.ra_len = winp->pcw_len; (void) pcmcia_free_io(winp->res_dip, &res); mutex_exit(&pcic->pc_lock); return (BAD_WINDOW); } window->handle = winp->pcw_handle; winp->pcw_status |= PCW_MAPPED; /* find the register set offset */ select = win * PCIC_IO_OFFSET; #if defined(PCIC_DEBUG) if (pcic_debug) { cmn_err(CE_CONT, "\tenable: window=%d, select=%x, " "base=%x, handle=%p\n", win, select, (unsigned)window->base, (void *)window->handle); } #endif /* * at this point, the register window indicator has * been converted to be an offset from the first * set of registers that are used for programming * the window mapping and the offset used to select * the correct set of registers to access the * specified socket. This allows basing everything * off the _0 window */ /* map the I/O base in */ pcic_putb(pcic, socket, PCIC_IO_ADDR_0_STARTLOW + select, LOW_BYTE((uint32_t)winp->pcw_base)); pcic_putb(pcic, socket, PCIC_IO_ADDR_0_STARTHI + select, HIGH_BYTE((uint32_t)winp->pcw_base)); pcic_putb(pcic, socket, PCIC_IO_ADDR_0_STOPLOW + select, LOW_BYTE((uint32_t)winp->pcw_base + window->WindowSize - 1)); pcic_putb(pcic, socket, PCIC_IO_ADDR_0_STOPHI + select, HIGH_BYTE((uint32_t)winp->pcw_base + window->WindowSize - 1)); /* * We've got the requested IO space, now see if we * need to adjust the IO window offset registers * so that the correct IO address is generated * at the socket. If this window doesn't have * this capability, then we're all done setting * up the IO resources. */ if (pcic->pc_flags & PCF_IO_REMAP) { /* * Note that only 16 bits are used to program * the registers but leave 32 bits on pcw_offset * so that we can generate the original base * in get_window() */ winp->pcw_offset = (base - winp->pcw_base); pcic_putb(pcic, socket, PCIC_IO_OFFSET_LOW + (win * PCIC_IO_OFFSET_OFFSET), winp->pcw_offset & 0x0ff); pcic_putb(pcic, socket, PCIC_IO_OFFSET_HI + (win * PCIC_IO_OFFSET_OFFSET), (winp->pcw_offset >> 8) & 0x0ff); } /* PCF_IO_REMAP */ /* now get the other details (size, etc) right */ /* * Set the data size control bits here. Most of the * adapters will ignore IOMEM_16BIT when * IOMEM_IOCS16 is set, except for the Intel * 82092, which only pays attention to the * IOMEM_16BIT bit. Sigh... Intel can't even * make a proper clone of their own chip. * The 82092 also apparently can't set the timing * of I/O windows. */ which = (window->state & WS_16BIT) ? (IOMEM_16BIT | IOMEM_IOCS16) : 0; switch (pcic->pc_type) { case PCIC_CL_PD6729: case PCIC_CL_PD6730: case PCIC_CL_PD6710: case PCIC_CL_PD6722: case PCIC_CL_PD6832: /* * Select Timer Set 1 - this will take * effect when the PCIC_IO_CONTROL * register is written to later on; * the call to pcic_set_cdtimers * just sets up the timer itself. */ which |= IOMEM_CLTIMER_SET_1; pcic_set_cdtimers(pcic, socket, window->speed, IOMEM_CLTIMER_SET_1); which |= IOMEM_IOCS16; break; case PCIC_TI_PCI1031: if (window->state & WS_16BIT) which |= IOMEM_WAIT16; break; case PCIC_TI_PCI1130: if (window->state & WS_16BIT) which |= IOMEM_WAIT16; break; case PCIC_INTEL_i82092: break; default: if (window->speed > mhztons(pcic->bus_speed) * 3) which |= IOMEM_WAIT16; #ifdef notdef if (window->speed < mhztons(pcic->bus_speed) * 6) which |= IOMEM_ZERO_WAIT; #endif break; } /* switch (pc_type) */ /* * Setup the data width and timing */ select = pcic_getb(pcic, socket, PCIC_IO_CONTROL); select &= ~(PCIC_IO_WIN_MASK << (win * 4)); select |= IOMEM_SETWIN(win, which); pcic_putb(pcic, socket, PCIC_IO_CONTROL, select); /* * Enable the IO window */ select = pcic_getb(pcic, socket, PCIC_MAPPING_ENABLE); pcic_putb(pcic, socket, PCIC_MAPPING_ENABLE, select | IOMEM_WINDOW(win)); winp->pcw_status |= PCW_ENABLED; #if defined(PCIC_DEBUG) if (pcic_debug) { cmn_err(CE_CONT, "\twhich = %x, select = %x (%x)\n", which, select, IOMEM_SETWIN(win, which)); xxdmp_all_regs(pcic, window->socket * 0x40, 24); } #endif } else { /* * not only do we unmap the IO space, the * window has been turned off. */ if (winp->pcw_status & PCW_MAPPED) { ddi_regs_map_free(&winp->pcw_handle); res.ra_addr_lo = winp->pcw_base; res.ra_len = winp->pcw_len; (void) pcmcia_free_io(winp->res_dip, &res); winp->pcw_status &= ~PCW_MAPPED; } /* disable current mapping */ select = pcic_getb(pcic, socket, PCIC_MAPPING_ENABLE); pcic_putb(pcic, socket, PCIC_MAPPING_ENABLE, select &= ~IOMEM_WINDOW(win)); winp->pcw_status &= ~PCW_ENABLED; winp->pcw_base = 0; winp->pcw_len = 0; winp->pcw_offset = 0; window->base = 0; /* now make sure we don't accidentally re-enable */ /* find the register set offset */ select = win * PCIC_IO_OFFSET; pcic_putb(pcic, socket, PCIC_IO_ADDR_0_STARTLOW + select, 0); pcic_putb(pcic, socket, PCIC_IO_ADDR_0_STARTHI + select, 0); pcic_putb(pcic, socket, PCIC_IO_ADDR_0_STOPLOW + select, 0); pcic_putb(pcic, socket, PCIC_IO_ADDR_0_STOPHI + select, 0); } } mutex_exit(&pcic->pc_lock); return (SUCCESS); } /* * pcic_card_state() * compute the instantaneous Card State information */ static int pcic_card_state(pcicdev_t *pcic, pcic_socket_t *sockp) { int value, result; #if defined(PCIC_DEBUG) int orig_value; #endif mutex_enter(&pcic->pc_lock); /* protect the registers */ value = pcic_getb(pcic, sockp->pcs_socket, PCIC_INTERFACE_STATUS); #if defined(PCIC_DEBUG) orig_value = value; if (pcic_debug >= 8) cmn_err(CE_CONT, "pcic_card_state(%p) if status = %b for %d\n", (void *)sockp, value, "\020\1BVD1\2BVD2\3CD1\4CD2\5WP\6RDY\7PWR\10~GPI", sockp->pcs_socket); #endif /* * Lie to socket services if we are not ready. * This is when we are starting up or during debounce timeouts * or if the card is a cardbus card. */ if (!(sockp->pcs_flags & (PCS_STARTING|PCS_CARD_ISCARDBUS)) && !sockp->pcs_debounce_id && (value & PCIC_ISTAT_CD_MASK) == PCIC_CD_PRESENT_OK) { result = SBM_CD; if (value & PCIC_WRITE_PROTECT || !(value & PCIC_POWER_ON)) result |= SBM_WP; if (value & PCIC_POWER_ON) { if (value & PCIC_READY) result |= SBM_RDYBSY; value = (~value) & (PCIC_BVD1 | PCIC_BVD2); if (value & PCIC_BVD1) result |= SBM_BVD1; if (value & PCIC_BVD2) result |= SBM_BVD2; } } else result = 0; mutex_exit(&pcic->pc_lock); #if defined(PCIC_DEBUG) pcic_err(pcic->dip, 8, "pcic_card_state(%p) if status = %b for %d (rval=0x%x)\n", (void *) sockp, orig_value, "\020\1BVD1\2BVD2\3CD1\4CD2\5WP\6RDY\7PWR\10~GPI", sockp->pcs_socket, result); #endif return (result); } /* * pcic_set_page() * SocketServices SetPage function * set the page of PC Card memory that should be in the mapped * window */ /*ARGSUSED*/ static int pcic_set_page(dev_info_t *dip, set_page_t *page) { anp_t *anp = ddi_get_driver_private(dip); pcicdev_t *pcic = anp->an_private; int select; int which, socket, window; uint32_t base; pcs_memwin_t *memp; /* get real socket/window numbers */ window = page->window % PCIC_NUMWINSOCK; socket = page->window / PCIC_NUMWINSOCK; #if defined(PCIC_DEBUG) if (pcic_debug) { cmn_err(CE_CONT, "pcic_set_page: window=%d, socket=%d, page=%d\n", window, socket, page->page); } #endif /* only windows 2-6 work on memory */ if (window < PCIC_IOWINDOWS) return (BAD_WINDOW); /* only one page supported (but any size) */ if (page->page != 0) return (BAD_PAGE); mutex_enter(&pcic->pc_lock); /* protect the registers */ memp = &pcic->pc_sockets[socket].pcs_windows[window].mem; window -= PCIC_IOWINDOWS; #if defined(PCIC_DEBUG) if (pcic_debug) cmn_err(CE_CONT, "\tpcw_base=%x, pcw_hostmem=%p, pcw_len=%x\n", (uint32_t)memp->pcw_base, (void *)memp->pcw_hostmem, memp->pcw_len); #endif /* window must be enabled */ if (!(memp->pcw_status & PCW_ENABLED)) return (BAD_ATTRIBUTE); /* find the register set offset */ select = window * PCIC_MEM_1_OFFSET; #if defined(PCIC_DEBUG) if (pcic_debug) cmn_err(CE_CONT, "\tselect=%x\n", select); #endif /* * now map the card's memory pages - we start with page 0 */ which = 0; /* assume simple case */ if (page->state & PS_ATTRIBUTE) { which |= CARDMEM_REG_ACTIVE; memp->pcw_status |= PCW_ATTRIBUTE; } else { memp->pcw_status &= ~PCW_ATTRIBUTE; } /* * if caller says Write Protect, enforce it. */ if (page->state & PS_WP) { which |= CARDMEM_WRITE_PROTECT; memp->pcw_status |= PCW_WP; } else { memp->pcw_status &= ~PCW_WP; } #if defined(PCIC_DEBUG) if (pcic_debug) { cmn_err(CE_CONT, "\tmemory type = %s\n", (which & CARDMEM_REG_ACTIVE) ? "attribute" : "common"); if (which & CARDMEM_WRITE_PROTECT) cmn_err(CE_CONT, "\twrite protect\n"); cmn_err(CE_CONT, "\tpage offset=%x pcw_base=%x (%x)\n", (unsigned)page->offset, (unsigned)memp->pcw_base, (int)page->offset - (int)memp->pcw_base & 0xffffff); } #endif /* address computation based on 64MB range and not larger */ base = (uint32_t)memp->pcw_base & 0x3ffffff; pcic_putb(pcic, socket, PCIC_CARDMEM_0_LOW + select, CARDMEM_LOW((int)page->offset - (int)base)); (void) pcic_getb(pcic, socket, PCIC_CARDMEM_0_LOW + select); pcic_putb(pcic, socket, PCIC_CARDMEM_0_HI + select, CARDMEM_HIGH((int)page->offset - base) | which); (void) pcic_getb(pcic, socket, PCIC_CARDMEM_0_HI + select); /* * while not really necessary, this just makes sure * nothing turned the window off behind our backs */ which = pcic_getb(pcic, socket, PCIC_MAPPING_ENABLE); which |= SYSMEM_WINDOW(window); pcic_putb(pcic, socket, PCIC_MAPPING_ENABLE, which); (void) pcic_getb(pcic, socket, PCIC_MAPPING_ENABLE); memp->pcw_offset = (off_t)page->offset; #if defined(PCIC_DEBUG) if (pcic_debug) { cmn_err(CE_CONT, "\tbase=%p, *base=%x\n", (void *)memp->pcw_hostmem, (uint32_t)*memp->pcw_hostmem); xxdmp_all_regs(pcic, socket, -1); cmn_err(CE_CONT, "\tbase=%p, *base=%x\n", (void *)memp->pcw_hostmem, (uint32_t)*memp->pcw_hostmem); } #endif if (which & PCW_ATTRIBUTE) pcic_mswait(pcic, socket, 2); mutex_exit(&pcic->pc_lock); return (SUCCESS); } /* * pcic_set_vcc_level() * * set voltage based on adapter information * * this routine implements a limited solution for support of 3.3v cards. * the general solution, which would fully support the pcmcia spec * as far as allowing client drivers to request which voltage levels * to be set, requires more framework support and driver changes - ess */ static int pcic_set_vcc_level(pcicdev_t *pcic, set_socket_t *socket) { uint32_t socket_present_state; #if defined(PCIC_DEBUG) if (pcic_debug) { cmn_err(CE_CONT, "pcic_set_vcc_level(pcic=%p, VccLevel=%d)\n", (void *)pcic, socket->VccLevel); } #endif /* * check VccLevel * if this is zero, power is being turned off * if it is non-zero, power is being turned on. */ if (socket->VccLevel == 0) { return (0); } /* * range checking for sanity's sake */ if (socket->VccLevel >= pcic->pc_numpower) { return (BAD_VCC); } switch (pcic->pc_io_type) { /* * Yenta-compliant adapters have vcc info in the extended registers * Other adapters can be added as needed, but the 'default' case * has been left as it was previously so as not to break existing * adapters. */ case PCIC_IO_TYPE_YENTA: /* * Here we ignore the VccLevel passed in and read the * card type from the adapter socket present state register */ socket_present_state = ddi_get32(pcic->handle, (uint32_t *)(pcic->ioaddr + PCIC_PRESENT_STATE_REG)); #if defined(PCIC_DEBUG) if (pcic_debug) { cmn_err(CE_CONT, "socket present state = 0x%x\n", socket_present_state); } #endif switch (socket_present_state & PCIC_VCC_MASK) { case PCIC_VCC_3VCARD: /* fall through */ case PCIC_VCC_3VCARD|PCIC_VCC_5VCARD: socket->VccLevel = PCIC_VCC_3VLEVEL; return (POWER_3VCARD_ENABLE|POWER_OUTPUT_ENABLE); case PCIC_VCC_5VCARD: socket->VccLevel = PCIC_VCC_5VLEVEL; return (POWER_CARD_ENABLE|POWER_OUTPUT_ENABLE); default: /* * if no card is present, this can be the * case of a client making a SetSocket call * after card removal. In this case we return * the current power level */ return ((unsigned)ddi_get8(pcic->handle, pcic->ioaddr + CB_R2_OFFSET + PCIC_POWER_CONTROL)); } default: switch (socket->VccLevel) { case PCIC_VCC_3VLEVEL: return (BAD_VCC); case PCIC_VCC_5VLEVEL: /* enable Vcc */ return (POWER_CARD_ENABLE|POWER_OUTPUT_ENABLE); default: return (BAD_VCC); } } } /* * pcic_set_socket() * Socket Services SetSocket call * sets basic socket configuration */ static int pcic_set_socket(dev_info_t *dip, set_socket_t *socket) { anp_t *anp = ddi_get_driver_private(dip); pcicdev_t *pcic = anp->an_private; pcic_socket_t *sockp = &pcic->pc_sockets[socket->socket]; int irq, interrupt, mirq; int powerlevel = 0; int ind, value, orig_pwrctl; #if defined(PCIC_DEBUG) if (pcic_debug) { cmn_err(CE_CONT, "pcic_set_socket(dip=%p, socket=%d)" " Vcc=%d Vpp1=%d Vpp2=%d\n", (void *)dip, socket->socket, socket->VccLevel, socket->Vpp1Level, socket->Vpp2Level); } #endif /* * check VccLevel, etc. before setting mutex * if this is zero, power is being turned off * if it is non-zero, power is being turned on. * the default case is to assume Vcc only. */ /* this appears to be very implementation specific */ if (socket->Vpp1Level != socket->Vpp2Level) return (BAD_VPP); if (socket->VccLevel == 0 || !(sockp->pcs_flags & PCS_CARD_PRESENT)) { powerlevel = 0; sockp->pcs_vcc = 0; sockp->pcs_vpp1 = 0; sockp->pcs_vpp2 = 0; } else { #if defined(PCIC_DEBUG) pcic_err(dip, 9, "\tVcc=%d Vpp1Level=%d, Vpp2Level=%d\n", socket->VccLevel, socket->Vpp1Level, socket->Vpp2Level); #endif /* valid Vcc power level? */ if (socket->VccLevel >= pcic->pc_numpower) return (BAD_VCC); switch (pcic_power[socket->VccLevel].PowerLevel) { case 33: /* 3.3V */ case 60: /* for bad CIS in Option GPRS card */ if (!(pcic->pc_flags & PCF_33VCAP)) { cmn_err(CE_WARN, "%s%d: Bad Request for 3.3V " "(Controller incapable)\n", ddi_get_name(pcic->dip), ddi_get_instance(pcic->dip)); return (BAD_VCC); } /* FALLTHROUGH */ case 50: /* 5V */ if ((pcic->pc_io_type == PCIC_IO_TYPE_YENTA) && pcic_getcb(pcic, CB_PRESENT_STATE) & CB_PS_33VCARD) { /* * This is actually a 3.3V card. * Solaris Card Services * doesn't understand 3.3V * so we cheat and change * the setting to the one appropriate to 3.3V. * Note that this is the entry number * in the pcic_power[] array. */ sockp->pcs_vcc = PCIC_VCC_3VLEVEL; } else sockp->pcs_vcc = socket->VccLevel; break; default: return (BAD_VCC); } /* enable Vcc */ powerlevel = POWER_CARD_ENABLE; #if defined(PCIC_DEBUG) if (pcic_debug) { cmn_err(CE_CONT, "\tVcc=%d powerlevel=%x\n", socket->VccLevel, powerlevel); } #endif ind = 0; /* default index to 0 power */ if ((int)socket->Vpp1Level >= 0 && socket->Vpp1Level < pcic->pc_numpower) { if (!(pcic_power[socket->Vpp1Level].ValidSignals & VPP1)) { return (BAD_VPP); } ind = pcic_power[socket->Vpp1Level].PowerLevel/10; powerlevel |= pcic_vpp_levels[ind]; sockp->pcs_vpp1 = socket->Vpp1Level; } if ((int)socket->Vpp2Level >= 0 && socket->Vpp2Level < pcic->pc_numpower) { if (!(pcic_power[socket->Vpp2Level].ValidSignals & VPP2)) { return (BAD_VPP); } ind = pcic_power[socket->Vpp2Level].PowerLevel/10; powerlevel |= (pcic_vpp_levels[ind] << 2); sockp->pcs_vpp2 = socket->Vpp2Level; } if (pcic->pc_flags & PCF_VPPX) { /* * this adapter doesn't allow separate Vpp1/Vpp2 * if one is turned on, both are turned on and only * the Vpp1 bits should be set */ if (sockp->pcs_vpp2 != sockp->pcs_vpp1) { /* must be the same if one not zero */ if (sockp->pcs_vpp1 != 0 && sockp->pcs_vpp2 != 0) { cmn_err(CE_WARN, "%s%d: Bad Power Request " "(Vpp1/2 not the same)\n", ddi_get_name(pcic->dip), ddi_get_instance(pcic->dip)); return (BAD_VPP); } } powerlevel &= ~(3<<2); } #if defined(PCIC_DEBUG) if (pcic_debug) { cmn_err(CE_CONT, "\tpowerlevel=%x, ind=%x\n", powerlevel, ind); } #endif } mutex_enter(&pcic->pc_lock); /* protect the registers */ /* turn socket->IREQRouting off while programming */ interrupt = pcic_getb(pcic, socket->socket, PCIC_INTERRUPT); interrupt &= ~PCIC_INTR_MASK; if (pcic->pc_flags & PCF_USE_SMI) interrupt |= PCIC_INTR_ENABLE; pcic_putb(pcic, socket->socket, PCIC_INTERRUPT, interrupt); switch (pcic->pc_type) { case PCIC_INTEL_i82092: pcic_82092_smiirq_ctl(pcic, socket->socket, PCIC_82092_CTL_IRQ, PCIC_82092_INT_DISABLE); break; default: break; } /* switch */ /* the SCIntMask specifies events to detect */ mirq = pcic_getb(pcic, socket->socket, PCIC_MANAGEMENT_INT); #if defined(PCIC_DEBUG) if (pcic_debug) cmn_err(CE_CONT, "\tSCIntMask=%x, interrupt=%x, mirq=%x\n", socket->SCIntMask, interrupt, mirq); #endif mirq &= ~(PCIC_BD_DETECT|PCIC_BW_DETECT|PCIC_RD_DETECT); pcic_putb(pcic, socket->socket, PCIC_MANAGEMENT_INT, mirq & ~PCIC_CHANGE_MASK); /* save the mask we want to use */ sockp->pcs_intmask = socket->SCIntMask; /* * Until there is a card present it's not worth enabling * any interrupts except "Card detect". This is done * elsewhere in the driver so don't change things if * there is no card! */ if (sockp->pcs_flags & PCS_CARD_PRESENT) { /* now update the hardware to reflect events desired */ if (sockp->pcs_intmask & SBM_BVD1 || socket->IFType == IF_IO) mirq |= PCIC_BD_DETECT; if (sockp->pcs_intmask & SBM_BVD2) mirq |= PCIC_BW_DETECT; if (sockp->pcs_intmask & SBM_RDYBSY) mirq |= PCIC_RD_DETECT; if (sockp->pcs_intmask & SBM_CD) mirq |= PCIC_CD_DETECT; } if (sockp->pcs_flags & PCS_READY) { /* * card just came ready. * make sure enough time elapses * before touching it. */ sockp->pcs_flags &= ~PCS_READY; pcic_mswait(pcic, socket->socket, 10); } #if defined(PCIC_DEBUG) if (pcic_debug) { cmn_err(CE_CONT, "\tstatus change set to %x\n", mirq); } #endif switch (pcic->pc_type) { case PCIC_I82365SL: case PCIC_VADEM: case PCIC_VADEM_VG469: /* * The Intel version has different options. This is a * special case of GPI which might be used for eject */ irq = pcic_getb(pcic, socket->socket, PCIC_CARD_DETECT); if (sockp->pcs_intmask & (SBM_EJECT|SBM_INSERT) && pcic->pc_flags & PCF_GPI_EJECT) { irq |= PCIC_GPI_ENABLE; } else { irq &= ~PCIC_GPI_ENABLE; } pcic_putb(pcic, socket->socket, PCIC_CARD_DETECT, irq); break; case PCIC_CL_PD6710: case PCIC_CL_PD6722: if (socket->IFType == IF_IO) { pcic_putb(pcic, socket->socket, PCIC_MISC_CTL_2, 0x0); value = pcic_getb(pcic, socket->socket, PCIC_MISC_CTL_1); if (pcic->pc_flags & PCF_AUDIO) value |= PCIC_MC_SPEAKER_ENB; pcic_putb(pcic, socket->socket, PCIC_MISC_CTL_1, value); } else { value = pcic_getb(pcic, socket->socket, PCIC_MISC_CTL_1); value &= ~PCIC_MC_SPEAKER_ENB; pcic_putb(pcic, socket->socket, PCIC_MISC_CTL_1, value); } break; case PCIC_CL_PD6729: case PCIC_CL_PD6730: case PCIC_CL_PD6832: value = pcic_getb(pcic, socket->socket, PCIC_MISC_CTL_1); if ((socket->IFType == IF_IO) && (pcic->pc_flags & PCF_AUDIO)) { value |= PCIC_MC_SPEAKER_ENB; } else { value &= ~PCIC_MC_SPEAKER_ENB; } if (pcic_power[sockp->pcs_vcc].PowerLevel == 33) value |= PCIC_MC_3VCC; else value &= ~PCIC_MC_3VCC; pcic_putb(pcic, socket->socket, PCIC_MISC_CTL_1, value); break; case PCIC_O2_OZ6912: value = pcic_getcb(pcic, CB_MISCCTRL); if ((socket->IFType == IF_IO) && (pcic->pc_flags & PCF_AUDIO)) value |= (1<<25); else value &= ~(1<<25); pcic_putcb(pcic, CB_MISCCTRL, value); if (pcic_power[sockp->pcs_vcc].PowerLevel == 33) powerlevel |= 0x08; break; case PCIC_TI_PCI1250: case PCIC_TI_PCI1221: case PCIC_TI_PCI1225: case PCIC_TI_PCI1410: case PCIC_ENE_1410: case PCIC_TI_PCI1510: case PCIC_TI_PCI1520: case PCIC_TI_PCI1420: case PCIC_ENE_1420: value = ddi_get8(pcic->cfg_handle, pcic->cfgaddr + PCIC_CRDCTL_REG); if ((socket->IFType == IF_IO) && (pcic->pc_flags & PCF_AUDIO)) { value |= PCIC_CRDCTL_SPKR_ENBL; } else { value &= ~PCIC_CRDCTL_SPKR_ENBL; } ddi_put8(pcic->cfg_handle, pcic->cfgaddr + PCIC_CRDCTL_REG, value); if (pcic_power[sockp->pcs_vcc].PowerLevel == 33) powerlevel |= 0x08; break; } /* * ctlind processing -- we can ignore this * there aren't any outputs on the chip for this and * the GUI will display what it thinks is correct */ /* * If outputs are enabled and the power is going off * turn off outputs first. */ /* power setup -- if necessary */ orig_pwrctl = pcic_getb(pcic, socket->socket, PCIC_POWER_CONTROL); if ((orig_pwrctl & POWER_OUTPUT_ENABLE) && sockp->pcs_vcc == 0) { orig_pwrctl &= ~POWER_OUTPUT_ENABLE; pcic_putb(pcic, socket->socket, PCIC_POWER_CONTROL, orig_pwrctl); (void) pcic_getb(pcic, socket->socket, PCIC_POWER_CONTROL); } if (pcic->pc_flags & PCF_CBPWRCTL) { value = pcic_cbus_powerctl(pcic, socket->socket); powerlevel = 0; } else value = pcic_exca_powerctl(pcic, socket->socket, powerlevel); if (value != SUCCESS) { mutex_exit(&pcic->pc_lock); return (value); } /* * If outputs were disabled and the power is going on * turn on outputs afterwards. */ if (!(orig_pwrctl & POWER_OUTPUT_ENABLE) && sockp->pcs_vcc != 0) { orig_pwrctl = pcic_getb(pcic, socket->socket, PCIC_POWER_CONTROL); orig_pwrctl |= POWER_OUTPUT_ENABLE; pcic_putb(pcic, socket->socket, PCIC_POWER_CONTROL, orig_pwrctl); (void) pcic_getb(pcic, socket->socket, PCIC_POWER_CONTROL); } /* * Once we have done the power stuff can re-enable management * interrupts. */ pcic_putb(pcic, socket->socket, PCIC_MANAGEMENT_INT, mirq); #if defined(PCIC_DEBUG) pcic_err(dip, 8, "\tmanagement int set to %x pwrctl to 0x%x " "cbctl 0x%x\n", mirq, pcic_getb(pcic, socket->socket, PCIC_POWER_CONTROL), pcic_getcb(pcic, CB_CONTROL)); #endif /* irq processing */ if (socket->IFType == IF_IO) { /* IRQ only for I/O */ irq = socket->IREQRouting & PCIC_INTR_MASK; value = pcic_getb(pcic, socket->socket, PCIC_INTERRUPT); value &= ~PCIC_INTR_MASK; /* to enable I/O operation */ value |= PCIC_IO_CARD | PCIC_RESET; sockp->pcs_flags |= PCS_CARD_IO; if (irq != sockp->pcs_irq) { if (sockp->pcs_irq != 0) cmn_err(CE_CONT, "SetSocket: IRQ mismatch %x != %x!\n", irq, sockp->pcs_irq); else sockp->pcs_irq = irq; } irq = sockp->pcs_irq; pcic_putb(pcic, socket->socket, PCIC_INTERRUPT, value); if (socket->IREQRouting & IRQ_ENABLE) { pcic_enable_io_intr(pcic, socket->socket, irq); sockp->pcs_flags |= PCS_IRQ_ENABLED; } else { pcic_disable_io_intr(pcic, socket->socket); sockp->pcs_flags &= ~PCS_IRQ_ENABLED; } #if defined(PCIC_DEBUG) if (pcic_debug) { cmn_err(CE_CONT, "\tsocket type is I/O and irq %x is %s\n", irq, (socket->IREQRouting & IRQ_ENABLE) ? "enabled" : "not enabled"); xxdmp_all_regs(pcic, socket->socket, 20); } #endif } else { /* make sure I/O mode is off */ sockp->pcs_irq = 0; value = pcic_getb(pcic, socket->socket, PCIC_INTERRUPT); value &= ~PCIC_IO_CARD; pcic_putb(pcic, socket->socket, PCIC_INTERRUPT, value); pcic_disable_io_intr(pcic, socket->socket); sockp->pcs_flags &= ~(PCS_CARD_IO|PCS_IRQ_ENABLED); } sockp->pcs_state &= ~socket->State; mutex_exit(&pcic->pc_lock); return (SUCCESS); } /* * pcic_inquire_socket() * SocketServices InquireSocket function * returns basic characteristics of the socket */ /*ARGSUSED*/ static int pcic_inquire_socket(dev_info_t *dip, inquire_socket_t *socket) { anp_t *anp = ddi_get_driver_private(dip); pcicdev_t *pcic = anp->an_private; int value; socket->SCIntCaps = PCIC_DEFAULT_INT_CAPS; socket->SCRptCaps = PCIC_DEFAULT_RPT_CAPS; socket->CtlIndCaps = PCIC_DEFAULT_CTL_CAPS; value = pcic->pc_sockets[socket->socket].pcs_flags; socket->SocketCaps = (value & PCS_SOCKET_IO) ? IF_IO : IF_MEMORY; socket->ActiveHigh = 0; /* these are the usable IRQs */ socket->ActiveLow = 0xfff0; return (SUCCESS); } /* * pcic_inquire_window() * SocketServices InquireWindow function * returns detailed characteristics of the window * this is where windows get tied to sockets */ /*ARGSUSED*/ static int pcic_inquire_window(dev_info_t *dip, inquire_window_t *window) { int type, socket; type = window->window % PCIC_NUMWINSOCK; socket = window->window / PCIC_NUMWINSOCK; #if defined(PCIC_DEBUG) if (pcic_debug >= 8) cmn_err(CE_CONT, "pcic_inquire_window: window = %d/%d socket=%d\n", window->window, type, socket); #endif if (type < PCIC_IOWINDOWS) { window->WndCaps = WC_IO|WC_WAIT; type = IF_IO; } else { window->WndCaps = WC_COMMON|WC_ATTRIBUTE|WC_WAIT; type = IF_MEMORY; } /* initialize the socket map - one socket per window */ PR_ZERO(window->Sockets); PR_SET(window->Sockets, socket); if (type == IF_IO) { iowin_char_t *io; io = &window->iowin_char; io->IOWndCaps = WC_BASE|WC_SIZE|WC_WENABLE|WC_8BIT| WC_16BIT; io->FirstByte = (baseaddr_t)IOMEM_FIRST; io->LastByte = (baseaddr_t)IOMEM_LAST; io->MinSize = IOMEM_MIN; io->MaxSize = IOMEM_MAX; io->ReqGran = IOMEM_GRAN; io->AddrLines = IOMEM_DECODE; io->EISASlot = 0; } else { mem_win_char_t *mem; mem = &window->mem_win_char; mem->MemWndCaps = WC_BASE|WC_SIZE|WC_WENABLE|WC_8BIT| WC_16BIT|WC_WP; mem->FirstByte = (baseaddr_t)MEM_FIRST; mem->LastByte = (baseaddr_t)MEM_LAST; mem->MinSize = MEM_MIN; mem->MaxSize = MEM_MAX; mem->ReqGran = PCIC_PAGE; mem->ReqBase = 0; mem->ReqOffset = PCIC_PAGE; mem->Slowest = MEM_SPEED_MAX; mem->Fastest = MEM_SPEED_MIN; } return (SUCCESS); } /* * pcic_get_adapter() * SocketServices GetAdapter function * this is nearly a no-op. */ /*ARGSUSED*/ static int pcic_get_adapter(dev_info_t *dip, get_adapter_t *adapt) { anp_t *anp = ddi_get_driver_private(dip); pcicdev_t *pcic = anp->an_private; if (pcic->pc_flags & PCF_INTRENAB) adapt->SCRouting = IRQ_ENABLE; adapt->state = 0; return (SUCCESS); } /* * pcic_get_page() * SocketServices GetPage function * returns info about the window */ /*ARGSUSED*/ static int pcic_get_page(dev_info_t *dip, get_page_t *page) { anp_t *anp = ddi_get_driver_private(dip); pcicdev_t *pcic = anp->an_private; int socket, window; pcs_memwin_t *winp; socket = page->window / PCIC_NUMWINSOCK; window = page->window % PCIC_NUMWINSOCK; /* I/O windows are the first two */ if (window < PCIC_IOWINDOWS || socket >= pcic->pc_numsockets) { return (BAD_WINDOW); } winp = &pcic->pc_sockets[socket].pcs_windows[window].mem; if (page->page != 0) return (BAD_PAGE); page->state = 0; if (winp->pcw_status & PCW_ENABLED) page->state |= PS_ENABLED; if (winp->pcw_status & PCW_ATTRIBUTE) page->state |= PS_ATTRIBUTE; if (winp->pcw_status & PCW_WP) page->state |= PS_WP; page->offset = (off_t)winp->pcw_offset; return (SUCCESS); } /* * pcic_get_socket() * SocketServices GetSocket * returns information about the current socket setting */ /*ARGSUSED*/ static int pcic_get_socket(dev_info_t *dip, get_socket_t *socket) { anp_t *anp = ddi_get_driver_private(dip); pcicdev_t *pcic = anp->an_private; int socknum, irq_enabled; pcic_socket_t *sockp; socknum = socket->socket; sockp = &pcic->pc_sockets[socknum]; socket->SCIntMask = sockp->pcs_intmask; sockp->pcs_state = pcic_card_state(pcic, sockp); socket->state = sockp->pcs_state; if (socket->state & SBM_CD) { socket->VccLevel = sockp->pcs_vcc; socket->Vpp1Level = sockp->pcs_vpp1; socket->Vpp2Level = sockp->pcs_vpp2; irq_enabled = (sockp->pcs_flags & PCS_IRQ_ENABLED) ? IRQ_ENABLE : 0; socket->IRQRouting = sockp->pcs_irq | irq_enabled; socket->IFType = (sockp->pcs_flags & PCS_CARD_IO) ? IF_IO : IF_MEMORY; } else { socket->VccLevel = 0; socket->Vpp1Level = 0; socket->Vpp2Level = 0; socket->IRQRouting = 0; socket->IFType = IF_MEMORY; } socket->CtlInd = 0; /* no indicators */ return (SUCCESS); } /* * pcic_get_status() * SocketServices GetStatus * returns status information about the PC Card in * the selected socket */ /*ARGSUSED*/ static int pcic_get_status(dev_info_t *dip, get_ss_status_t *status) { anp_t *anp = ddi_get_driver_private(dip); pcicdev_t *pcic = anp->an_private; int socknum, irq_enabled; pcic_socket_t *sockp; socknum = status->socket; sockp = &pcic->pc_sockets[socknum]; status->CardState = pcic_card_state(pcic, sockp); status->SocketState = sockp->pcs_state; status->CtlInd = 0; /* no indicators */ if (sockp->pcs_flags & PCS_CARD_PRESENT) status->SocketState |= SBM_CD; if (status->CardState & SBM_CD) { irq_enabled = (sockp->pcs_flags & PCS_CARD_ENABLED) ? IRQ_ENABLE : 0; status->IRQRouting = sockp->pcs_irq | irq_enabled; status->IFType = (sockp->pcs_flags & PCS_CARD_IO) ? IF_IO : IF_MEMORY; } else { status->IRQRouting = 0; status->IFType = IF_MEMORY; } #if defined(PCIC_DEBUG) if (pcic_debug >= 8) cmn_err(CE_CONT, "pcic_get_status: socket=%d, CardState=%x," "SocketState=%x\n", socknum, status->CardState, status->SocketState); #endif switch (pcic->pc_type) { uint32_t present_state; case PCIC_TI_PCI1410: case PCIC_TI_PCI1520: case PCIC_TI_PCI1420: case PCIC_ENE_1420: case PCIC_TOSHIBA_TOPIC100: case PCIC_TOSHIBA_TOPIC95: case PCIC_TOSHIBA_VENDOR: case PCIC_O2MICRO_VENDOR: case PCIC_TI_VENDOR: case PCIC_RICOH_VENDOR: present_state = pcic_getcb(pcic, CB_PRESENT_STATE); if (present_state & PCIC_CB_CARD) status->IFType = IF_CARDBUS; #if defined(PCIC_DEBUG) if (pcic_debug >= 8) cmn_err(CE_CONT, "pcic_get_status: present_state=0x%x\n", present_state); #endif break; default: break; } return (SUCCESS); } /* * pcic_get_window() * SocketServices GetWindow function * returns state information about the specified window */ /*ARGSUSED*/ static int pcic_get_window(dev_info_t *dip, get_window_t *window) { anp_t *anp = ddi_get_driver_private(dip); pcicdev_t *pcic = anp->an_private; int socket, win; pcic_socket_t *sockp; pcs_memwin_t *winp; socket = window->window / PCIC_NUMWINSOCK; win = window->window % PCIC_NUMWINSOCK; #if defined(PCIC_DEBUG) if (pcic_debug) { cmn_err(CE_CONT, "pcic_get_window(socket=%d, window=%d)\n", socket, win); } #endif if (socket > pcic->pc_numsockets) return (BAD_WINDOW); sockp = &pcic->pc_sockets[socket]; winp = &sockp->pcs_windows[win].mem; window->socket = socket; window->size = winp->pcw_len; window->speed = winp->pcw_speed; window->handle = (ddi_acc_handle_t)winp->pcw_handle; window->base = (uint32_t)winp->pcw_base + winp->pcw_offset; if (win >= PCIC_IOWINDOWS) { window->state = 0; } else { window->state = WS_IO; } if (winp->pcw_status & PCW_ENABLED) window->state |= WS_ENABLED; if (winp->pcw_status & PCS_CARD_16BIT) window->state |= WS_16BIT; #if defined(PCIC_DEBUG) if (pcic_debug) cmn_err(CE_CONT, "\tsize=%d, speed=%d, base=%p, state=%x\n", window->size, (unsigned)window->speed, (void *)window->handle, window->state); #endif return (SUCCESS); } /* * pcic_ll_reset * low level reset * separated out so it can be called when already locked * * There are two variables that control the RESET timing: * pcic_prereset_time - time in mS before asserting RESET * pcic_reset_time - time in mS to assert RESET * */ int pcic_prereset_time = 1; int pcic_reset_time = 10; int pcic_postreset_time = 20; int pcic_vpp_is_vcc_during_reset = 0; static int pcic_ll_reset(pcicdev_t *pcic, int socket) { int windowbits, iobits; uint32_t pwr; /* save windows that were on */ windowbits = pcic_getb(pcic, socket, PCIC_MAPPING_ENABLE); if (pcic_reset_time == 0) return (windowbits); /* turn all windows off */ pcic_putb(pcic, socket, PCIC_MAPPING_ENABLE, 0); #if defined(PCIC_DEBUG) pcic_err(pcic->dip, 6, "pcic_ll_reset(socket %d) powerlevel=%x cbctl 0x%x cbps 0x%x\n", socket, pcic_getb(pcic, socket, PCIC_POWER_CONTROL), pcic_getcb(pcic, CB_CONTROL), pcic_getcb(pcic, CB_PRESENT_STATE)); #endif if (pcic_vpp_is_vcc_during_reset) { /* * Set VPP to VCC for the duration of the reset - for aironet * card. */ if (pcic->pc_flags & PCF_CBPWRCTL) { pwr = pcic_getcb(pcic, CB_CONTROL); pcic_putcb(pcic, CB_CONTROL, (pwr&~CB_C_VPPMASK)|CB_C_VPPVCC); (void) pcic_getcb(pcic, CB_CONTROL); } else { pwr = pcic_getb(pcic, socket, PCIC_POWER_CONTROL); pcic_putb(pcic, socket, PCIC_POWER_CONTROL, pwr | 1); (void) pcic_getb(pcic, socket, PCIC_POWER_CONTROL); } } if (pcic_prereset_time > 0) { pcic_err(pcic->dip, 8, "pcic_ll_reset pre_wait %d mS\n", pcic_prereset_time); pcic_mswait(pcic, socket, pcic_prereset_time); } /* turn interrupts off and start a reset */ pcic_err(pcic->dip, 8, "pcic_ll_reset turn interrupts off and start a reset\n"); iobits = pcic_getb(pcic, socket, PCIC_INTERRUPT); iobits &= ~(PCIC_INTR_MASK | PCIC_RESET); pcic_putb(pcic, socket, PCIC_INTERRUPT, iobits); (void) pcic_getb(pcic, socket, PCIC_INTERRUPT); switch (pcic->pc_type) { case PCIC_INTEL_i82092: pcic_82092_smiirq_ctl(pcic, socket, PCIC_82092_CTL_IRQ, PCIC_82092_INT_DISABLE); break; default: break; } /* switch */ pcic->pc_sockets[socket].pcs_state = 0; if (pcic_reset_time > 0) { pcic_err(pcic->dip, 8, "pcic_ll_reset reset_wait %d mS\n", pcic_reset_time); pcic_mswait(pcic, socket, pcic_reset_time); } pcic_err(pcic->dip, 8, "pcic_ll_reset take it out of reset now\n"); /* take it out of RESET now */ pcic_putb(pcic, socket, PCIC_INTERRUPT, PCIC_RESET | iobits); (void) pcic_getb(pcic, socket, PCIC_INTERRUPT); /* * can't access the card for 20ms, but we really don't * want to sit around that long. The pcic is still usable. * memory accesses must wait for RDY to come up. */ if (pcic_postreset_time > 0) { pcic_err(pcic->dip, 8, "pcic_ll_reset post_wait %d mS\n", pcic_postreset_time); pcic_mswait(pcic, socket, pcic_postreset_time); } if (pcic_vpp_is_vcc_during_reset > 1) { /* * Return VPP power to whatever it was before. */ if (pcic->pc_flags & PCF_CBPWRCTL) { pcic_putcb(pcic, CB_CONTROL, pwr); (void) pcic_getcb(pcic, CB_CONTROL); } else { pcic_putb(pcic, socket, PCIC_POWER_CONTROL, pwr); (void) pcic_getb(pcic, socket, PCIC_POWER_CONTROL); } } pcic_err(pcic->dip, 7, "pcic_ll_reset returning 0x%x\n", windowbits); return (windowbits); } /* * pcic_reset_socket() * SocketServices ResetSocket function * puts the PC Card in the socket into the RESET state * and then takes it out after the the cycle time * The socket is back to initial state when done */ static int pcic_reset_socket(dev_info_t *dip, int socket, int mode) { anp_t *anp = ddi_get_driver_private(dip); pcicdev_t *pcic = anp->an_private; int value; int i, mint; pcic_socket_t *sockp; #if defined(PCIC_DEBUG) if (pcic_debug >= 8) cmn_err(CE_CONT, "pcic_reset_socket(%p, %d, %d/%s)\n", (void *)dip, socket, mode, mode == RESET_MODE_FULL ? "full" : "partial"); #endif mutex_enter(&pcic->pc_lock); /* protect the registers */ /* Turn off management interupts. */ mint = pcic_getb(pcic, socket, PCIC_MANAGEMENT_INT); pcic_putb(pcic, socket, PCIC_MANAGEMENT_INT, mint & ~PCIC_CHANGE_MASK); sockp = &pcic->pc_sockets[socket]; value = pcic_ll_reset(pcic, socket); if (mode == RESET_MODE_FULL) { /* disable and unmap all mapped windows */ for (i = 0; i < PCIC_NUMWINSOCK; i++) { if (i < PCIC_IOWINDOWS) { if (sockp->pcs_windows[i].io.pcw_status & PCW_MAPPED) { pcs_iowin_t *io; io = &sockp->pcs_windows[i].io; io->pcw_status &= ~PCW_ENABLED; } } else { if (sockp->pcs_windows[i].mem.pcw_status & PCW_MAPPED) { pcs_memwin_t *mem; mem = &sockp->pcs_windows[i].mem; mem->pcw_status &= ~PCW_ENABLED; } } } } else { /* turn windows back on */ pcic_putb(pcic, socket, PCIC_MAPPING_ENABLE, value); /* wait the rest of the time here */ pcic_mswait(pcic, socket, 10); } pcic_putb(pcic, socket, PCIC_MANAGEMENT_INT, mint); mutex_exit(&pcic->pc_lock); return (SUCCESS); } /* * pcic_set_interrupt() * SocketServices SetInterrupt function */ static int pcic_set_interrupt(dev_info_t *dip, set_irq_handler_t *handler) { anp_t *anp = ddi_get_driver_private(dip); pcicdev_t *pcic = anp->an_private; int value = DDI_SUCCESS; inthandler_t *intr; #if defined(PCIC_DEBUG) if (pcic_debug) { cmn_err(CE_CONT, "pcic_set_interrupt: entered pc_intr_mode=0x%x\n", pcic->pc_intr_mode); cmn_err(CE_CONT, "\t irq_top=%p handler=%p handler_id=%x\n", (void *)pcic->irq_top, (void *)handler->handler, handler->handler_id); } #endif /* * If we're on a PCI bus, we route all IO IRQs through a single * PCI interrupt (typically INT A#) so we don't have to do * much other than add the caller to general interrupt handler * and set some state. */ intr = kmem_zalloc(sizeof (inthandler_t), KM_NOSLEEP); if (intr == NULL) return (NO_RESOURCE); switch (pcic->pc_intr_mode) { case PCIC_INTR_MODE_PCI_1: /* * We only allow above-lock-level IO IRQ handlers * in the PCI bus case. */ mutex_enter(&pcic->intr_lock); if (pcic->irq_top == NULL) { pcic->irq_top = intr; pcic->irq_current = pcic->irq_top; } else { while (pcic->irq_current->next != NULL) pcic->irq_current = pcic->irq_current->next; pcic->irq_current->next = intr; pcic->irq_current = pcic->irq_current->next; } pcic->irq_current->intr = (ddi_intr_handler_t *)handler->handler; pcic->irq_current->handler_id = handler->handler_id; pcic->irq_current->arg1 = handler->arg1; pcic->irq_current->arg2 = handler->arg2; pcic->irq_current->socket = handler->socket; mutex_exit(&pcic->intr_lock); handler->iblk_cookie = &pcic->pc_pri; handler->idev_cookie = &pcic->pc_dcookie; break; default: intr->intr = (ddi_intr_handler_t *)handler->handler; intr->handler_id = handler->handler_id; intr->arg1 = handler->arg1; intr->arg2 = handler->arg2; intr->socket = handler->socket; intr->irq = handler->irq; /* * need to revisit this to see if interrupts can be * shared someday. Note that IRQ is set in the common * code. */ mutex_enter(&pcic->pc_lock); if (pcic->pc_handlers == NULL) { pcic->pc_handlers = intr; intr->next = intr->prev = intr; } else { insque(intr, pcic->pc_handlers); } mutex_exit(&pcic->pc_lock); break; } /* * need to fill in cookies in event of multiple high priority * interrupt handlers on same IRQ */ #if defined(PCIC_DEBUG) if (pcic_debug) { cmn_err(CE_CONT, "pcic_set_interrupt: exit irq_top=%p value=%d\n", (void *)pcic->irq_top, value); } #endif if (value == DDI_SUCCESS) { return (SUCCESS); } else { return (BAD_IRQ); } } /* * pcic_clear_interrupt() * SocketServices ClearInterrupt function * * Interrupts for PCIC are complicated by the fact that we must * follow several different models for interrupts. * ISA: there is an interrupt per adapter and per socket and * they can't be shared. * PCI: some adapters have one PCI interrupt available while others * have up to 4. Solaris may or may not allow us to use more * than 1 so we essentially share them all at this point. * Hybrid: PCI bridge but interrupts wired to host interrupt controller. * This is like ISA but we have to fudge and create an intrspec * that PCI's parent understands and bypass the PCI nexus. * multifunction: this requires sharing the interrupts on a per-socket * basis. */ static int pcic_clear_interrupt(dev_info_t *dip, clear_irq_handler_t *handler) { anp_t *anp = ddi_get_driver_private(dip); pcicdev_t *pcic = anp->an_private; inthandler_t *intr, *prev, *current; int i; /* * If we're on a PCI bus, we route all IO IRQs through a single * PCI interrupt (typically INT A#) so we don't have to do * much other than remove the caller from the general * interrupt handler callout list. */ #if defined(PCIC_DEBUG) if (pcic_debug) { cmn_err(CE_CONT, "pcic_clear_interrupt: entered pc_intr_mode=0x%x\n", pcic->pc_intr_mode); cmn_err(CE_CONT, "\t irq_top=%p handler=%p handler_id=%x\n", (void *)pcic->irq_top, (void *)handler->handler, handler->handler_id); } #endif switch (pcic->pc_intr_mode) { case PCIC_INTR_MODE_PCI_1: mutex_enter(&pcic->intr_lock); if (pcic->irq_top == NULL) { mutex_exit(&pcic->intr_lock); return (BAD_IRQ); } intr = NULL; pcic->irq_current = pcic->irq_top; while ((pcic->irq_current != NULL) && (pcic->irq_current->handler_id != handler->handler_id)) { intr = pcic->irq_current; pcic->irq_current = pcic->irq_current->next; } if (pcic->irq_current == NULL) { mutex_exit(&pcic->intr_lock); return (BAD_IRQ); } if (intr != NULL) { intr->next = pcic->irq_current->next; } else { pcic->irq_top = pcic->irq_current->next; } current = pcic->irq_current; pcic->irq_current = pcic->irq_top; mutex_exit(&pcic->intr_lock); kmem_free(current, sizeof (inthandler_t)); break; default: mutex_enter(&pcic->pc_lock); intr = pcic_handlers; prev = (inthandler_t *)&pcic_handlers; while (intr != NULL) { if (intr->handler_id == handler->handler_id) { i = intr->irq & PCIC_INTR_MASK; if (--pcic_irq_map[i].count == 0) { /* multi-handler form */ (void) ddi_intr_disable(pcic->pc_intr_htblp[i]); (void) ddi_intr_remove_handler( pcic->pc_intr_htblp[i]); (void) ddi_intr_free(pcic->pc_intr_htblp[i]); (void) pcmcia_return_intr(pcic->dip, i); #if defined(PCIC_DEBUG) if (pcic_debug) { cmn_err(CE_CONT, "removing interrupt %d at %s " "priority\n", i, "high"); cmn_err(CE_CONT, "ddi_remove_intr(%p, %x, %p)\n", (void *)dip, 0, (void *)intr->iblk_cookie); } #endif } prev->next = intr->next; kmem_free(intr, sizeof (inthandler_t)); intr = prev->next; } else { prev = intr; intr = intr->next; } /* if (handler_id) */ } /* while */ mutex_exit(&pcic->pc_lock); } #if defined(PCIC_DEBUG) if (pcic_debug) { cmn_err(CE_CONT, "pcic_clear_interrupt: exit irq_top=%p\n", (void *)pcic->irq_top); } #endif return (SUCCESS); } struct intel_regs { char *name; int off; char *fmt; } iregs[] = { {"ident ", 0}, {"if-status ", 1, "\020\1BVD1\2BVD2\3CD1\4CD2\5WP\6RDY\7PWR\10~GPI"}, {"power ", 2, "\020\1Vpp1c0\2Vpp1c1\3Vpp2c0\4Vpp2c1\5PE\6AUTO" "\7DRD\10OE"}, {"cardstatus", 4, "\020\1BD\2BW\3RC\4CD\5GPI\6R1\7R2\010R3"}, {"enable ", 6, "\020\1MW0\2MW1\3MW2\4MW3\5MW4\6MEM16\7IO0\10IO1"}, {"cd-gcr ", 0x16, "\020\1MDI16\2CRE\3GPIE\4GPIT\5CDR\6S/W"}, {"GCR ", 0x1e, "\020\1PD\2LEVEL\3WCSC\4PLS14"}, {"int-gcr ", 3, "\020\5INTR\6IO\7~RST\10RI"}, {"management", 5, "\020\1BDE\2BWE\3RE\4CDE"}, {"volt-sense", 0x1f, "\020\1A_VS1\2A_VS2\3B_VS1\4B_VS2"}, {"volt-sel ", 0x2f, "\020\5EXTCONF\6BUSSELECT\7MIXEDV\10ISAV"}, {"VG ext A ", 0x3c, "\20\3IVS\4CABLE\5CSTEP\6TEST\7RIO"}, {"io-ctrl ", 7, "\020\1DS0\2IOCS0\3ZWS0\4WS0\5DS1\6IOS1\7ZWS1\10WS1"}, {"io0-slow ", 8}, {"io0-shi ", 9}, {"io0-elow ", 0xa}, {"io0-ehi ", 0xb}, {"io1-slow ", 0xc}, {"io1-shi ", 0xd}, {"io1-elow ", 0xe}, {"io1-ehi ", 0xf}, {"mem0-slow ", 0x10}, {"mem0-shi ", 0x11, "\020\7ZW\10DS"}, {"mem0-elow ", 0x12}, {"mem0-ehi ", 0x13, "\020\7WS0\10WS1"}, {"card0-low ", 0x14}, {"card0-hi ", 0x15, "\020\7AM\10WP"}, {"mem1-slow ", 0x18}, {"mem1-shi ", 0x19, "\020\7ZW\10DS"}, {"mem1-elow ", 0x1a}, {"mem1-ehi ", 0x1b, "\020\7WS0\10WS1"}, {"card1-low ", 0x1c}, {"card1-hi ", 0x1d, "\020\7AM\10WP"}, {"mem2-slow ", 0x20}, {"mem2-shi ", 0x21, "\020\7ZW\10DS"}, {"mem2-elow ", 0x22}, {"mem2-ehi ", 0x23, "\020\7WS0\10WS1"}, {"card2-low ", 0x24}, {"card2-hi ", 0x25, "\020\7AM\10WP"}, {"mem3-slow ", 0x28}, {"mem3-shi ", 0x29, "\020\7ZW\10DS"}, {"mem3-elow ", 0x2a}, {"mem3-ehi ", 0x2b, "\020\7WS0\10WS1"}, {"card3-low ", 0x2c}, {"card3-hi ", 0x2d, "\020\7AM\10WP"}, {"mem4-slow ", 0x30}, {"mem4-shi ", 0x31, "\020\7ZW\10DS"}, {"mem4-elow ", 0x32}, {"mem4-ehi ", 0x33, "\020\7WS0\10WS1"}, {"card4-low ", 0x34}, {"card4-hi ", 0x35, "\020\7AM\10WP"}, {"mpage0 ", 0x40}, {"mpage1 ", 0x41}, {"mpage2 ", 0x42}, {"mpage3 ", 0x43}, {"mpage4 ", 0x44}, {NULL}, }; static struct intel_regs cregs[] = { {"misc-ctl1 ", 0x16, "\20\2VCC3\3PMI\4PSI\5SPKR\10INPACK"}, {"fifo ", 0x17, "\20\6DIOP\7DMEMP\10EMPTY"}, {"misc-ctl2 ", 0x1e, "\20\1XCLK\2LOW\3SUSP\4CORE5V\5TCD\10RIOUT"}, {"chip-info ", 0x1f, "\20\6DUAL"}, {"IO-offlow0", 0x36}, {"IO-offhi0 ", 0x37}, {"IO-offlow1", 0x38}, {"IO-offhi1 ", 0x39}, NULL, }; static struct intel_regs cxregs[] = { {"ext-ctl-1 ", 0x03, "\20\1VCCLCK\2AUTOCLR\3LED\4INVIRQC\5INVIRQM\6PUC"}, {"misc-ctl3 ", 0x25, "\20\5HWSUSP"}, {"mem0-up ", 0x05}, {"mem1-up ", 0x06}, {"mem2-up ", 0x07}, {"mem3-up ", 0x08}, {"mem4-up ", 0x09}, {NULL} }; void xxdmp_cl_regs(pcicdev_t *pcic, int socket, uint32_t len) { int i, value, j; char buff[256]; char *fmt; cmn_err(CE_CONT, "--------- Cirrus Logic Registers --------\n"); for (buff[0] = '\0', i = 0; cregs[i].name != NULL && len-- != 0; i++) { int sval; if (cregs[i].off == PCIC_MISC_CTL_2) sval = 0; else sval = socket; value = pcic_getb(pcic, sval, cregs[i].off); if (i & 1) { if (cregs[i].fmt) fmt = "%s\t%s\t%b\n"; else fmt = "%s\t%s\t%x\n"; cmn_err(CE_CONT, fmt, buff, cregs[i].name, value, cregs[i].fmt); buff[0] = '\0'; } else { if (cregs[i].fmt) fmt = "\t%s\t%b"; else fmt = "\t%s\t%x"; (void) sprintf(buff, fmt, cregs[i].name, value, cregs[i].fmt); for (j = strlen(buff); j < 40; j++) buff[j] = ' '; buff[40] = '\0'; } } cmn_err(CE_CONT, "%s\n", buff); i = pcic_getb(pcic, socket, PCIC_TIME_SETUP_0); j = pcic_getb(pcic, socket, PCIC_TIME_SETUP_1); cmn_err(CE_CONT, "\tsetup-tim0\t%x\tsetup-tim1\t%x\n", i, j); i = pcic_getb(pcic, socket, PCIC_TIME_COMMAND_0); j = pcic_getb(pcic, socket, PCIC_TIME_COMMAND_1); cmn_err(CE_CONT, "\tcmd-tim0 \t%x\tcmd-tim1 \t%x\n", i, j); i = pcic_getb(pcic, socket, PCIC_TIME_RECOVER_0); j = pcic_getb(pcic, socket, PCIC_TIME_RECOVER_1); cmn_err(CE_CONT, "\trcvr-tim0 \t%x\trcvr-tim1 \t%x\n", i, j); cmn_err(CE_CONT, "--------- Extended Registers --------\n"); for (buff[0] = '\0', i = 0; cxregs[i].name != NULL && len-- != 0; i++) { value = clext_reg_read(pcic, socket, cxregs[i].off); if (i & 1) { if (cxregs[i].fmt) fmt = "%s\t%s\t%b\n"; else fmt = "%s\t%s\t%x\n"; cmn_err(CE_CONT, fmt, buff, cxregs[i].name, value, cxregs[i].fmt); buff[0] = '\0'; } else { if (cxregs[i].fmt) fmt = "\t%s\t%b"; else fmt = "\t%s\t%x"; (void) sprintf(buff, fmt, cxregs[i].name, value, cxregs[i].fmt); for (j = strlen(buff); j < 40; j++) buff[j] = ' '; buff[40] = '\0'; } } } #if defined(PCIC_DEBUG) static void xxdmp_all_regs(pcicdev_t *pcic, int socket, uint32_t len) { int i, value, j; char buff[256]; char *fmt; #if defined(PCIC_DEBUG) if (pcic_debug < 2) return; #endif cmn_err(CE_CONT, "----------- PCIC Registers for socket %d---------\n", socket); cmn_err(CE_CONT, "\tname value name value\n"); for (buff[0] = '\0', i = 0; iregs[i].name != NULL && len-- != 0; i++) { value = pcic_getb(pcic, socket, iregs[i].off); if (i & 1) { if (iregs[i].fmt) fmt = "%s\t%s\t%b\n"; else fmt = "%s\t%s\t%x\n"; cmn_err(CE_CONT, fmt, buff, iregs[i].name, value, iregs[i].fmt); buff[0] = '\0'; } else { if (iregs[i].fmt) fmt = "\t%s\t%b"; else fmt = "\t%s\t%x"; (void) sprintf(buff, fmt, iregs[i].name, value, iregs[i].fmt); for (j = strlen(buff); j < 40; j++) buff[j] = ' '; buff[40] = '\0'; } } switch (pcic->pc_type) { case PCIC_CL_PD6710: case PCIC_CL_PD6722: case PCIC_CL_PD6729: case PCIC_CL_PD6832: (void) xxdmp_cl_regs(pcic, socket, 0xFFFF); break; } cmn_err(CE_CONT, "%s\n", buff); } #endif /* * pcic_mswait(ms) * sleep ms milliseconds * call drv_usecwait once for each ms */ static void pcic_mswait(pcicdev_t *pcic, int socket, int ms) { if (ms) { pcic->pc_sockets[socket].pcs_flags |= PCS_WAITING; pcic_mutex_exit(&pcic->pc_lock); delay(drv_usectohz(ms*1000)); pcic_mutex_enter(&pcic->pc_lock); pcic->pc_sockets[socket].pcs_flags &= ~PCS_WAITING; } } /* * pcic_check_ready(pcic, index, off) * Wait for card to come ready * We only wait if the card is NOT in RESET * and power is on. */ static boolean_t pcic_check_ready(pcicdev_t *pcic, int socket) { int ifstate, intstate; intstate = pcic_getb(pcic, socket, PCIC_INTERRUPT); ifstate = pcic_getb(pcic, socket, PCIC_INTERFACE_STATUS); if ((intstate & PCIC_RESET) && ((ifstate & (PCIC_READY|PCIC_POWER_ON|PCIC_ISTAT_CD_MASK)) == (PCIC_READY|PCIC_POWER_ON|PCIC_CD_PRESENT_OK))) return (B_TRUE); #ifdef PCIC_DEBUG pcic_err(NULL, 5, "pcic_check_read: Card not ready, intstate = 0x%x, " "ifstate = 0x%x\n", intstate, ifstate); if (pcic_debug) { pcic_debug += 4; xxdmp_all_regs(pcic, socket, -1); pcic_debug -= 4; } #endif return (B_FALSE); } /* * Cirrus Logic extended register read/write routines */ static int clext_reg_read(pcicdev_t *pcic, int sn, uchar_t ext_reg) { int val; switch (pcic->pc_io_type) { case PCIC_IO_TYPE_YENTA: val = ddi_get8(pcic->handle, pcic->ioaddr + CB_CLEXT_OFFSET + ext_reg); break; default: pcic_putb(pcic, sn, PCIC_CL_EXINDEX, ext_reg); val = pcic_getb(pcic, sn, PCIC_CL_EXINDEX + 1); break; } return (val); } static void clext_reg_write(pcicdev_t *pcic, int sn, uchar_t ext_reg, uchar_t value) { switch (pcic->pc_io_type) { case PCIC_IO_TYPE_YENTA: ddi_put8(pcic->handle, pcic->ioaddr + CB_CLEXT_OFFSET + ext_reg, value); break; default: pcic_putb(pcic, sn, PCIC_CL_EXINDEX, ext_reg); pcic_putb(pcic, sn, PCIC_CL_EXINDEX + 1, value); break; } } /* * Misc PCI functions */ static void pcic_iomem_pci_ctl(ddi_acc_handle_t handle, uchar_t *cfgaddr, unsigned flags) { unsigned cmd; if (flags & (PCIC_ENABLE_IO | PCIC_ENABLE_MEM)) { cmd = ddi_get16(handle, (ushort_t *)(cfgaddr + 4)); if ((cmd & (PCI_COMM_IO|PCI_COMM_MAE)) == (PCI_COMM_IO|PCI_COMM_MAE)) return; if (flags & PCIC_ENABLE_IO) cmd |= PCI_COMM_IO; if (flags & PCIC_ENABLE_MEM) cmd |= PCI_COMM_MAE; ddi_put16(handle, (ushort_t *)(cfgaddr + 4), cmd); } /* if (PCIC_ENABLE_IO | PCIC_ENABLE_MEM) */ } /* * pcic_find_pci_type - Find and return PCI-PCMCIA adapter type */ static int pcic_find_pci_type(pcicdev_t *pcic) { uint32_t vend, device; vend = ddi_getprop(DDI_DEV_T_ANY, pcic->dip, DDI_PROP_CANSLEEP|DDI_PROP_DONTPASS, "vendor-id", -1); device = ddi_getprop(DDI_DEV_T_ANY, pcic->dip, DDI_PROP_CANSLEEP|DDI_PROP_DONTPASS, "device-id", -1); device = PCI_ID(vend, device); pcic->pc_type = device; pcic->pc_chipname = "PCI:unknown"; switch (device) { case PCIC_INTEL_i82092: pcic->pc_chipname = PCIC_TYPE_i82092; break; case PCIC_CL_PD6729: pcic->pc_chipname = PCIC_TYPE_PD6729; /* * Some 6730's incorrectly identify themselves * as a 6729, so we need to do some more tests * here to see if the device that's claiming * to be a 6729 is really a 6730. */ if ((clext_reg_read(pcic, 0, PCIC_CLEXT_MISC_CTL_3) & PCIC_CLEXT_MISC_CTL_3_REV_MASK) == 0) { pcic->pc_chipname = PCIC_TYPE_PD6730; pcic->pc_type = PCIC_CL_PD6730; } break; case PCIC_CL_PD6730: pcic->pc_chipname = PCIC_TYPE_PD6730; break; case PCIC_CL_PD6832: pcic->pc_chipname = PCIC_TYPE_PD6832; break; case PCIC_SMC_34C90: pcic->pc_chipname = PCIC_TYPE_34C90; break; case PCIC_TOSHIBA_TOPIC95: pcic->pc_chipname = PCIC_TYPE_TOPIC95; break; case PCIC_TOSHIBA_TOPIC100: pcic->pc_chipname = PCIC_TYPE_TOPIC100; break; case PCIC_TI_PCI1031: pcic->pc_chipname = PCIC_TYPE_PCI1031; break; case PCIC_TI_PCI1130: pcic->pc_chipname = PCIC_TYPE_PCI1130; break; case PCIC_TI_PCI1131: pcic->pc_chipname = PCIC_TYPE_PCI1131; break; case PCIC_TI_PCI1250: pcic->pc_chipname = PCIC_TYPE_PCI1250; break; case PCIC_TI_PCI1225: pcic->pc_chipname = PCIC_TYPE_PCI1225; break; case PCIC_TI_PCI1410: pcic->pc_chipname = PCIC_TYPE_PCI1410; break; case PCIC_TI_PCI1510: pcic->pc_chipname = PCIC_TYPE_PCI1510; break; case PCIC_TI_PCI1520: pcic->pc_chipname = PCIC_TYPE_PCI1520; break; case PCIC_TI_PCI1221: pcic->pc_chipname = PCIC_TYPE_PCI1221; break; case PCIC_TI_PCI1050: pcic->pc_chipname = PCIC_TYPE_PCI1050; break; case PCIC_ENE_1410: pcic->pc_chipname = PCIC_TYPE_1410; break; case PCIC_O2_OZ6912: pcic->pc_chipname = PCIC_TYPE_OZ6912; break; case PCIC_RICOH_RL5C466: pcic->pc_chipname = PCIC_TYPE_RL5C466; break; case PCIC_TI_PCI1420: pcic->pc_chipname = PCIC_TYPE_PCI1420; break; case PCIC_ENE_1420: pcic->pc_chipname = PCIC_TYPE_1420; break; default: switch (PCI_ID(vend, (uint32_t)0)) { case PCIC_TOSHIBA_VENDOR: pcic->pc_chipname = PCIC_TYPE_TOSHIBA; pcic->pc_type = PCIC_TOSHIBA_VENDOR; break; case PCIC_TI_VENDOR: pcic->pc_chipname = PCIC_TYPE_TI; pcic->pc_type = PCIC_TI_VENDOR; break; case PCIC_O2MICRO_VENDOR: pcic->pc_chipname = PCIC_TYPE_O2MICRO; pcic->pc_type = PCIC_O2MICRO_VENDOR; break; case PCIC_RICOH_VENDOR: pcic->pc_chipname = PCIC_TYPE_RICOH; pcic->pc_type = PCIC_RICOH_VENDOR; break; default: if (!(pcic->pc_flags & PCF_CARDBUS)) return (DDI_FAILURE); pcic->pc_chipname = PCIC_TYPE_YENTA; break; } } return (DDI_SUCCESS); } static void pcic_82092_smiirq_ctl(pcicdev_t *pcic, int socket, int intr, int state) { uchar_t ppirr = ddi_get8(pcic->cfg_handle, pcic->cfgaddr + PCIC_82092_PPIRR); uchar_t val; if (intr == PCIC_82092_CTL_SMI) { val = PCIC_82092_SMI_CTL(socket, PCIC_82092_INT_DISABLE); ppirr &= ~val; val = PCIC_82092_SMI_CTL(socket, state); ppirr |= val; } else { val = PCIC_82092_IRQ_CTL(socket, PCIC_82092_INT_DISABLE); ppirr &= ~val; val = PCIC_82092_IRQ_CTL(socket, state); ppirr |= val; } ddi_put8(pcic->cfg_handle, pcic->cfgaddr + PCIC_82092_PPIRR, ppirr); } static uint_t pcic_cd_softint(caddr_t arg1, caddr_t arg2) { pcic_socket_t *sockp = (pcic_socket_t *)arg1; uint_t rc = DDI_INTR_UNCLAIMED; _NOTE(ARGUNUSED(arg2)) mutex_enter(&sockp->pcs_pcic->pc_lock); if (sockp->pcs_cd_softint_flg) { uint8_t status; sockp->pcs_cd_softint_flg = 0; rc = DDI_INTR_CLAIMED; status = pcic_getb(sockp->pcs_pcic, sockp->pcs_socket, PCIC_INTERFACE_STATUS); pcic_handle_cd_change(sockp->pcs_pcic, sockp, status); } mutex_exit(&sockp->pcs_pcic->pc_lock); return (rc); } int pcic_debounce_cnt = PCIC_REM_DEBOUNCE_CNT; int pcic_debounce_intr_time = PCIC_REM_DEBOUNCE_TIME; int pcic_debounce_cnt_ok = PCIC_DEBOUNCE_OK_CNT; #ifdef CARDBUS static uint32_t pcic_cbps_on = 0; static uint32_t pcic_cbps_off = CB_PS_NOTACARD | CB_PS_CCDMASK | CB_PS_XVCARD | CB_PS_YVCARD; #else static uint32_t pcic_cbps_on = CB_PS_16BITCARD; static uint32_t pcic_cbps_off = CB_PS_NOTACARD | CB_PS_CCDMASK | CB_PS_CBCARD | CB_PS_XVCARD | CB_PS_YVCARD; #endif static void pcic_handle_cd_change(pcicdev_t *pcic, pcic_socket_t *sockp, uint8_t status) { boolean_t do_debounce = B_FALSE; int debounce_time = drv_usectohz(pcic_debounce_time); uint8_t irq; timeout_id_t debounce; /* * Always reset debounce but may need to check original state later. */ debounce = sockp->pcs_debounce_id; sockp->pcs_debounce_id = 0; /* * Check to see whether a card is present or not. There are * only two states that we are concerned with - the state * where both CD pins are asserted, which means that the * card is fully seated, and the state where neither CD * pin is asserted, which means that the card is not * present. * The CD signals are generally very noisy and cause a lot of * contact bounce as the card is being inserted and * removed, so we need to do some software debouncing. */ #ifdef PCIC_DEBUG pcic_err(pcic->dip, 6, "pcic%d handle_cd_change: socket %d card status 0x%x" " deb 0x%p\n", ddi_get_instance(pcic->dip), sockp->pcs_socket, status, debounce); #endif switch (status & PCIC_ISTAT_CD_MASK) { case PCIC_CD_PRESENT_OK: sockp->pcs_flags &= ~(PCS_CARD_REMOVED|PCS_CARD_CBREM); if (!(sockp->pcs_flags & PCS_CARD_PRESENT)) { uint32_t cbps; #ifdef PCIC_DEBUG pcic_err(pcic->dip, 8, "New card (0x%x)\n", sockp->pcs_flags); #endif cbps = pcic_getcb(pcic, CB_PRESENT_STATE); #ifdef PCIC_DEBUG pcic_err(pcic->dip, 8, "CBus PS (0x%x)\n", cbps); #endif /* * Check the CB bits are sane. */ if ((cbps & pcic_cbps_on) != pcic_cbps_on || cbps & pcic_cbps_off) { cmn_err(CE_WARN, "%s%d: Odd Cardbus Present State 0x%x\n", ddi_get_name(pcic->dip), ddi_get_instance(pcic->dip), cbps); pcic_putcb(pcic, CB_EVENT_FORCE, CB_EF_CVTEST); debounce = 0; debounce_time = drv_usectohz(1000000); } if (debounce) { sockp->pcs_flags |= PCS_CARD_PRESENT; if (pcic_do_insertion) { cbps = pcic_getcb(pcic, CB_PRESENT_STATE); if (cbps & CB_PS_16BITCARD) { pcic_err(pcic->dip, 8, "16 bit card inserted\n"); sockp->pcs_flags |= PCS_CARD_IS16BIT; /* calls pcm_adapter_callback() */ if (pcic->pc_callback) { (void) ddi_prop_update_string(DDI_DEV_T_NONE, pcic->dip, PCM_DEVICETYPE, "pccard"); PC_CALLBACK(pcic->dip, pcic->pc_cb_arg, PCE_CARD_INSERT, sockp->pcs_socket); } } else if (cbps & CB_PS_CBCARD) { pcic_err(pcic->dip, 8, "32 bit card inserted\n"); if (pcic->pc_flags & PCF_CARDBUS) { sockp->pcs_flags |= PCS_CARD_ISCARDBUS; #ifdef CARDBUS if (!pcic_load_cardbus(pcic, sockp)) { pcic_unload_cardbus(pcic, sockp); } #else cmn_err(CE_NOTE, "32 bit Cardbus not supported in" " this device driver\n"); #endif } else { /* * Ignore the card */ cmn_err(CE_NOTE, "32 bit Cardbus not supported on this" " device\n"); } } else { cmn_err(CE_NOTE, "Unsupported PCMCIA card inserted\n"); } } } else { do_debounce = B_TRUE; } } else { /* * It is possible to come through here if the system * starts up with cards already inserted. Do nothing * and don't worry about it. */ #ifdef PCIC_DEBUG pcic_err(pcic->dip, 5, "pcic%d: Odd card insertion indication on socket %d\n", ddi_get_instance(pcic->dip), sockp->pcs_socket); #endif } break; default: if (!(sockp->pcs_flags & PCS_CARD_PRESENT)) { /* * Someone has started to insert a card so delay a while. */ do_debounce = B_TRUE; break; } /* * Otherwise this is basically the same as not present * so fall through. */ /* FALLTHRU */ case 0: if (sockp->pcs_flags & PCS_CARD_PRESENT) { if (pcic->pc_flags & PCF_CBPWRCTL) { pcic_putcb(pcic, CB_CONTROL, 0); } else { pcic_putb(pcic, sockp->pcs_socket, PCIC_POWER_CONTROL, 0); (void) pcic_getb(pcic, sockp->pcs_socket, PCIC_POWER_CONTROL); } #ifdef PCIC_DEBUG pcic_err(pcic->dip, 8, "Card removed\n"); #endif sockp->pcs_flags &= ~PCS_CARD_PRESENT; if (sockp->pcs_flags & PCS_CARD_IS16BIT) { sockp->pcs_flags &= ~PCS_CARD_IS16BIT; if (pcic_do_removal && pcic->pc_callback) { PC_CALLBACK(pcic->dip, pcic->pc_cb_arg, PCE_CARD_REMOVAL, sockp->pcs_socket); } } if (sockp->pcs_flags & PCS_CARD_ISCARDBUS) { sockp->pcs_flags &= ~PCS_CARD_ISCARDBUS; sockp->pcs_flags |= PCS_CARD_CBREM; } sockp->pcs_flags |= PCS_CARD_REMOVED; do_debounce = B_TRUE; } if (debounce && (sockp->pcs_flags & PCS_CARD_REMOVED)) { if (sockp->pcs_flags & PCS_CARD_CBREM) { /* * Ensure that we do the unloading in the * debounce handler, that way we're not doing * nasty things in an interrupt handler. e.g. * a USB device will wait for data which will * obviously never come because we've * unplugged the device, but the wait will * wait forever because no interrupts can * come in... */ #ifdef CARDBUS pcic_unload_cardbus(pcic, sockp); /* pcic_dump_all(pcic); */ #endif sockp->pcs_flags &= ~PCS_CARD_CBREM; } sockp->pcs_flags &= ~PCS_CARD_REMOVED; } break; } /* switch */ if (do_debounce) { /* * Delay doing * anything for a while so that things can settle * down a little. Interrupts are already disabled. * Reset the state and we'll reevaluate the * whole kit 'n kaboodle when the timeout fires */ #ifdef PCIC_DEBUG pcic_err(pcic->dip, 8, "Queueing up debounce timeout for " "socket %d.%d\n", ddi_get_instance(pcic->dip), sockp->pcs_socket); #endif sockp->pcs_debounce_id = pcic_add_debqueue(sockp, debounce_time); /* * We bug out here without re-enabling interrupts. They will * be re-enabled when the debounce timeout swings through * here. */ return; } /* * Turn on Card detect interrupts. Other interrupts will be * enabled during set_socket calls. * * Note that set_socket only changes interrupt settings when there * is a card present. */ irq = pcic_getb(pcic, sockp->pcs_socket, PCIC_MANAGEMENT_INT); irq |= PCIC_CD_DETECT; pcic_putb(pcic, sockp->pcs_socket, PCIC_MANAGEMENT_INT, irq); pcic_putcb(pcic, CB_STATUS_MASK, CB_SE_CCDMASK); /* Out from debouncing state */ sockp->pcs_flags &= ~PCS_DEBOUNCING; pcic_err(pcic->dip, 7, "Leaving pcic_handle_cd_change\n"); } /* * pcic_getb() * get an I/O byte based on the yardware decode method */ static uint8_t pcic_getb(pcicdev_t *pcic, int socket, int reg) { int work; #if defined(PCIC_DEBUG) if (pcic_debug == 0x7fff) { cmn_err(CE_CONT, "pcic_getb0: pcic=%p socket=%d reg=%d\n", (void *)pcic, socket, reg); cmn_err(CE_CONT, "pcic_getb1: type=%d handle=%p ioaddr=%p \n", pcic->pc_io_type, (void *)pcic->handle, (void *)pcic->ioaddr); } #endif switch (pcic->pc_io_type) { case PCIC_IO_TYPE_YENTA: return (ddi_get8(pcic->handle, pcic->ioaddr + CB_R2_OFFSET + reg)); default: work = (socket * PCIC_SOCKET_1) | reg; ddi_put8(pcic->handle, pcic->ioaddr, work); return (ddi_get8(pcic->handle, pcic->ioaddr + 1)); } } static void pcic_putb(pcicdev_t *pcic, int socket, int reg, int8_t value) { int work; #if defined(PCIC_DEBUG) if (pcic_debug == 0x7fff) { cmn_err(CE_CONT, "pcic_putb0: pcic=%p socket=%d reg=%d value=%x \n", (void *)pcic, socket, reg, value); cmn_err(CE_CONT, "pcic_putb1: type=%d handle=%p ioaddr=%p \n", pcic->pc_io_type, (void *)pcic->handle, (void *)pcic->ioaddr); } #endif switch (pcic->pc_io_type) { case PCIC_IO_TYPE_YENTA: ddi_put8(pcic->handle, pcic->ioaddr + CB_R2_OFFSET + reg, value); break; default: work = (socket * PCIC_SOCKET_1) | reg; ddi_put8(pcic->handle, pcic->ioaddr, work); ddi_put8(pcic->handle, pcic->ioaddr + 1, value); break; } } /* * chip identification functions */ /* * chip identification: Cirrus Logic PD6710/6720/6722 */ static int pcic_ci_cirrus(pcicdev_t *pcic) { int value1, value2; /* Init the CL id mode */ value1 = pcic_getb(pcic, 0, PCIC_CHIP_INFO); pcic_putb(pcic, 0, PCIC_CHIP_INFO, 0); value1 = pcic_getb(pcic, 0, PCIC_CHIP_INFO); value2 = pcic_getb(pcic, 0, PCIC_CHIP_INFO); if ((value1 & PCIC_CI_ID) == PCIC_CI_ID && (value2 & PCIC_CI_ID) == 0) { /* chip is a Cirrus Logic and not Intel */ pcic->pc_type = PCIC_CL_PD6710; if (value1 & PCIC_CI_SLOTS) pcic->pc_chipname = PCIC_TYPE_PD6720; else pcic->pc_chipname = PCIC_TYPE_PD6710; /* now fine tune things just in case a 6722 */ value1 = clext_reg_read(pcic, 0, PCIC_CLEXT_DMASK_0); if (value1 == 0) { clext_reg_write(pcic, 0, PCIC_CLEXT_SCRATCH, 0x55); value1 = clext_reg_read(pcic, 0, PCIC_CLEXT_SCRATCH); if (value1 == 0x55) { pcic->pc_chipname = PCIC_TYPE_PD6722; pcic->pc_type = PCIC_CL_PD6722; clext_reg_write(pcic, 0, PCIC_CLEXT_SCRATCH, 0); } } return (1); } return (0); } /* * chip identification: Vadem (VG365/465/468/469) */ static void pcic_vadem_enable(pcicdev_t *pcic) { ddi_put8(pcic->handle, pcic->ioaddr, PCIC_VADEM_P1); ddi_put8(pcic->handle, pcic->ioaddr, PCIC_VADEM_P2); ddi_put8(pcic->handle, pcic->ioaddr, pcic->pc_lastreg); } static int pcic_ci_vadem(pcicdev_t *pcic) { int value; pcic_vadem_enable(pcic); value = pcic_getb(pcic, 0, PCIC_CHIP_REVISION); pcic_putb(pcic, 0, PCIC_CHIP_REVISION, 0xFF); if (pcic_getb(pcic, 0, PCIC_CHIP_REVISION) == (value | PCIC_VADEM_D3) || (pcic_getb(pcic, 0, PCIC_CHIP_REVISION) & PCIC_REV_MASK) == PCIC_VADEM_469) { int vadem, new; pcic_vadem_enable(pcic); vadem = pcic_getb(pcic, 0, PCIC_VG_DMA) & ~(PCIC_V_UNLOCK | PCIC_V_VADEMREV); new = vadem | (PCIC_V_VADEMREV|PCIC_V_UNLOCK); pcic_putb(pcic, 0, PCIC_VG_DMA, new); value = pcic_getb(pcic, 0, PCIC_CHIP_REVISION); /* want to lock but leave mouse or other on */ pcic_putb(pcic, 0, PCIC_VG_DMA, vadem); switch (value & PCIC_REV_MASK) { case PCIC_VADEM_365: pcic->pc_chipname = PCIC_VG_365; pcic->pc_type = PCIC_VADEM; break; case PCIC_VADEM_465: pcic->pc_chipname = PCIC_VG_465; pcic->pc_type = PCIC_VADEM; pcic->pc_flags |= PCF_1SOCKET; break; case PCIC_VADEM_468: pcic->pc_chipname = PCIC_VG_468; pcic->pc_type = PCIC_VADEM; break; case PCIC_VADEM_469: pcic->pc_chipname = PCIC_VG_469; pcic->pc_type = PCIC_VADEM_VG469; break; } return (1); } return (0); } /* * chip identification: Ricoh */ static int pcic_ci_ricoh(pcicdev_t *pcic) { int value; value = pcic_getb(pcic, 0, PCIC_RF_CHIP_IDENT); switch (value) { case PCIC_RF_296: pcic->pc_type = PCIC_RICOH; pcic->pc_chipname = PCIC_TYPE_RF5C296; return (1); case PCIC_RF_396: pcic->pc_type = PCIC_RICOH; pcic->pc_chipname = PCIC_TYPE_RF5C396; return (1); } return (0); } /* * set up available address spaces in busra */ static void pcic_init_assigned(dev_info_t *dip) { pcm_regs_t *pcic_avail_p; pci_regspec_t *pci_avail_p, *regs; int len, entries, rlen; dev_info_t *pdip; if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "available", (caddr_t)&pcic_avail_p, &len) == DDI_PROP_SUCCESS) { /* * found "available" property at the cardbus/pcmcia node * need to translate address space entries from pcmcia * format to pci format */ entries = len / sizeof (pcm_regs_t); pci_avail_p = kmem_alloc(sizeof (pci_regspec_t) * entries, KM_SLEEP); if (pcic_apply_avail_ranges(dip, pcic_avail_p, pci_avail_p, entries) == DDI_SUCCESS) (void) pci_resource_setup_avail(dip, pci_avail_p, entries); kmem_free(pcic_avail_p, len); kmem_free(pci_avail_p, entries * sizeof (pci_regspec_t)); return; } /* * "legacy" platforms will have "available" property in pci node */ for (pdip = ddi_get_parent(dip); pdip; pdip = ddi_get_parent(pdip)) { if (ddi_getlongprop(DDI_DEV_T_ANY, pdip, DDI_PROP_DONTPASS, "available", (caddr_t)&pci_avail_p, &len) == DDI_PROP_SUCCESS) { /* (void) pci_resource_setup(pdip); */ kmem_free(pci_avail_p, len); break; } } if (pdip == NULL) { int len; char bus_type[16] = "(unknown)"; dev_info_t *par; cmn_err(CE_CONT, "?pcic_init_assigned: no available property for pcmcia\n"); /* * This code is taken from pci_resource_setup() but does * not attempt to use the "available" property to populate * the ndi maps that are created. * The fact that we will actually * free some resource below (that was allocated by OBP) * should be enough to be going on with. */ for (par = dip; par != NULL; par = ddi_get_parent(par)) { len = sizeof (bus_type); if ((ddi_prop_op(DDI_DEV_T_ANY, par, PROP_LEN_AND_VAL_BUF, DDI_PROP_CANSLEEP | DDI_PROP_DONTPASS, "device_type", (caddr_t)&bus_type, &len) == DDI_SUCCESS) && (strcmp(bus_type, DEVI_PCI_NEXNAME) == 0 || strcmp(bus_type, DEVI_PCIEX_NEXNAME) == 0)) break; } if (par != NULL && (ndi_ra_map_setup(par, NDI_RA_TYPE_MEM) != NDI_SUCCESS || ndi_ra_map_setup(par, NDI_RA_TYPE_IO) != NDI_SUCCESS)) par = NULL; } else { #ifdef CARDBUS cardbus_bus_range_t *bus_range; int k; if (ddi_getlongprop(DDI_DEV_T_ANY, pdip, 0, "bus-range", (caddr_t)&bus_range, &k) == DDI_PROP_SUCCESS) { if (bus_range->lo != bus_range->hi) pcic_err(pdip, 9, "allowable bus range is " "%u->%u\n", bus_range->lo, bus_range->hi); else { pcic_err(pdip, 0, "!No spare PCI bus numbers, range is " "%u->%u, cardbus isn't usable\n", bus_range->lo, bus_range->hi); } kmem_free(bus_range, k); } else pcic_err(pdip, 0, "!No bus-range property seems to " "have been set up\n"); #endif /* * Have a valid parent with the "available" property */ (void) pci_resource_setup(pdip); } if ((strcmp(ddi_get_name(dip), "pcma") == 0) && ddi_getlongprop(DDI_DEV_T_NONE, dip, DDI_PROP_DONTPASS, "assigned-addresses", (caddr_t)®s, &rlen) == DDI_SUCCESS) { ra_return_t ra; /* * On the UltraBook IIi the ranges are assigned under * openboot. If we don't free them here the first I/O * space that can be used is up above 0x10000 which * doesn't work for this driver due to restrictions * on the PCI I/O addresses the controllers can cope with. * They are never going to be used by anything else * so free them up to the general pool. AG. */ pcic_err(dip, 1, "Free assigned addresses\n"); if ((PCI_REG_ADDR_G(regs[0].pci_phys_hi) == PCI_REG_ADDR_G(PCI_ADDR_MEM32)) && regs[0].pci_size_low == 0x1000000) { ra.ra_addr_lo = regs[0].pci_phys_low; ra.ra_len = regs[0].pci_size_low; (void) pcmcia_free_mem(dip, &ra); } if ((PCI_REG_ADDR_G(regs[1].pci_phys_hi) == PCI_REG_ADDR_G(PCI_ADDR_IO)) && (regs[1].pci_size_low == 0x8000 || regs[1].pci_size_low == 0x4000)) /* UB-IIi || UB-I */ { ra.ra_addr_lo = regs[1].pci_phys_low; ra.ra_len = regs[1].pci_size_low; (void) pcmcia_free_io(dip, &ra); } kmem_free((caddr_t)regs, rlen); } } /* * translate "available" from pcmcia format to pci format */ static int pcic_apply_avail_ranges(dev_info_t *dip, pcm_regs_t *pcic_p, pci_regspec_t *pci_p, int entries) { int i, range_len, range_entries; pcic_ranges_t *pcic_range_p; if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "ranges", (caddr_t)&pcic_range_p, &range_len) != DDI_PROP_SUCCESS) { cmn_err(CE_CONT, "?pcic_apply_avail_ranges: " "no ranges property for pcmcia\n"); return (DDI_FAILURE); } range_entries = range_len / sizeof (pcic_ranges_t); /* for each "available" entry to be translated */ for (i = 0; i < entries; i++, pcic_p++, pci_p++) { int j; pcic_ranges_t *range_p = pcic_range_p; pci_p->pci_phys_hi = -1u; /* default invalid value */ /* for each "ranges" entry to be searched */ for (j = 0; j < range_entries; j++, range_p++) { uint64_t range_end = range_p->pcic_range_caddrlo + range_p->pcic_range_size; uint64_t avail_end = pcic_p->phys_lo + pcic_p->phys_len; if ((range_p->pcic_range_caddrhi != pcic_p->phys_hi) || (range_p->pcic_range_caddrlo > pcic_p->phys_lo) || (range_end < avail_end)) continue; pci_p->pci_phys_hi = range_p->pcic_range_paddrhi; pci_p->pci_phys_mid = range_p->pcic_range_paddrmid; pci_p->pci_phys_low = range_p->pcic_range_paddrlo + (pcic_p->phys_lo - range_p->pcic_range_caddrlo); pci_p->pci_size_hi = 0; pci_p->pci_size_low = pcic_p->phys_len; } } kmem_free(pcic_range_p, range_len); return (DDI_SUCCESS); } static int pcic_open(dev_t *dev, int flag, int otyp, cred_t *cred) { #ifdef CARDBUS if (cardbus_is_cb_minor(*dev)) return (cardbus_open(dev, flag, otyp, cred)); #endif return (EINVAL); } static int pcic_close(dev_t dev, int flag, int otyp, cred_t *cred) { #ifdef CARDBUS if (cardbus_is_cb_minor(dev)) return (cardbus_close(dev, flag, otyp, cred)); #endif return (EINVAL); } static int pcic_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cred, int *rval) { #ifdef CARDBUS if (cardbus_is_cb_minor(dev)) return (cardbus_ioctl(dev, cmd, arg, mode, cred, rval)); #endif return (EINVAL); } static boolean_t pcic_load_cardbus(pcicdev_t *pcic, const pcic_socket_t *sockp) { uint32_t present_state; dev_info_t *dip = pcic->dip; set_socket_t s; get_socket_t g; boolean_t retval; unsigned vccLevel; pcic_err(dip, 8, "entering pcic_load_cardbus\n"); pcic_mutex_exit(&pcic->pc_lock); bzero(&s, sizeof (set_socket_t)); s.socket = sockp->pcs_socket; s.SCIntMask = SBM_CD|SBM_RDYBSY; s.IFType = IF_CARDBUS; s.State = (unsigned)~0; present_state = pcic_getcb(pcic, CB_PRESENT_STATE); if (present_state & PCIC_VCC_3VCARD) s.VccLevel = PCIC_VCC_3VLEVEL; else if (present_state & PCIC_VCC_5VCARD) s.VccLevel = PCIC_VCC_5VLEVEL; else { cmn_err(CE_CONT, "pcic_load_cardbus: unsupported card voltage\n"); goto failure; } vccLevel = s.VccLevel; s.Vpp1Level = s.Vpp2Level = 0; if (pcic_set_socket(dip, &s) != SUCCESS) goto failure; if (pcic_reset_socket(dip, sockp->pcs_socket, RESET_MODE_CARD_ONLY) != SUCCESS) goto failure; bzero(&g, sizeof (get_socket_t)); g.socket = sockp->pcs_socket; if (pcic_get_socket(dip, &g) != SUCCESS) goto failure; bzero(&s, sizeof (set_socket_t)); s.socket = sockp->pcs_socket; s.SCIntMask = SBM_CD; s.IREQRouting = g.IRQRouting; s.IFType = g.IFType; s.CtlInd = g.CtlInd; s.State = (unsigned)~0; s.VccLevel = vccLevel; s.Vpp1Level = s.Vpp2Level = 0; retval = pcic_set_socket(dip, &s); pcmcia_cb_resumed(s.socket); if (retval != SUCCESS) goto failure; retval = cardbus_load_cardbus(dip, sockp->pcs_socket, pcic->pc_base); goto exit; failure: retval = B_FALSE; exit: pcic_mutex_enter(&pcic->pc_lock); pcic_err(dip, 8, "exit pcic_load_cardbus (%s)\n", retval ? "success" : "failure"); return (retval); } static void pcic_unload_cardbus(pcicdev_t *pcic, const pcic_socket_t *sockp) { dev_info_t *dip = pcic->dip; set_socket_t s; pcic_mutex_exit(&pcic->pc_lock); cardbus_unload_cardbus(dip); bzero(&s, sizeof (set_socket_t)); s.socket = sockp->pcs_socket; s.SCIntMask = SBM_CD|SBM_RDYBSY; s.IREQRouting = 0; s.IFType = IF_MEMORY; s.CtlInd = 0; s.State = 0; s.VccLevel = s.Vpp1Level = s.Vpp2Level = 0; (void) pcic_set_socket(dip, &s); pcic_mutex_enter(&pcic->pc_lock); } static uint32_t pcic_getcb(pcicdev_t *pcic, int reg) { ASSERT(pcic->pc_io_type == PCIC_IO_TYPE_YENTA); return (ddi_get32(pcic->handle, (uint32_t *)(pcic->ioaddr + CB_CB_OFFSET + reg))); } static void pcic_putcb(pcicdev_t *pcic, int reg, uint32_t value) { ASSERT(pcic->pc_io_type == PCIC_IO_TYPE_YENTA); ddi_put32(pcic->handle, (uint32_t *)(pcic->ioaddr + CB_CB_OFFSET + reg), value); } static void pcic_enable_io_intr(pcicdev_t *pcic, int socket, int irq) { uint8_t value; uint16_t brdgctl; value = pcic_getb(pcic, socket, PCIC_INTERRUPT) & ~PCIC_INTR_MASK; pcic_putb(pcic, socket, PCIC_INTERRUPT, value | irq); switch (pcic->pc_type) { case PCIC_INTEL_i82092: pcic_82092_smiirq_ctl(pcic, socket, PCIC_82092_CTL_IRQ, PCIC_82092_INT_ENABLE); break; case PCIC_O2_OZ6912: value = pcic_getb(pcic, 0, PCIC_CENTDMA); value |= 0x8; pcic_putb(pcic, 0, PCIC_CENTDMA, value); break; case PCIC_CL_PD6832: case PCIC_TI_PCI1250: case PCIC_TI_PCI1221: case PCIC_TI_PCI1225: case PCIC_TI_PCI1410: case PCIC_ENE_1410: case PCIC_TI_PCI1510: case PCIC_TI_PCI1520: case PCIC_TI_PCI1420: case PCIC_ENE_1420: /* route card functional interrupts to PCI interrupts */ brdgctl = ddi_get16(pcic->cfg_handle, (uint16_t *)(pcic->cfgaddr + PCI_CBUS_BRIDGE_CTRL)); pcic_err(NULL, 1, "pcic_enable_io_intr brdgctl(0x%x) was: 0x%x\n", PCI_CBUS_BRIDGE_CTRL, brdgctl); brdgctl &= ~PCIC_BRDGCTL_INTR_MASK; ddi_put16(pcic->cfg_handle, (uint16_t *)(pcic->cfgaddr + PCI_CBUS_BRIDGE_CTRL), brdgctl); /* Flush the write */ (void) ddi_get16(pcic->cfg_handle, (uint16_t *)(pcic->cfgaddr + PCI_CBUS_BRIDGE_CTRL)); break; default: break; } } static void pcic_disable_io_intr(pcicdev_t *pcic, int socket) { uint8_t value; uint16_t brdgctl; value = pcic_getb(pcic, socket, PCIC_INTERRUPT) & ~PCIC_INTR_MASK; pcic_putb(pcic, socket, PCIC_INTERRUPT, value); switch (pcic->pc_type) { case PCIC_INTEL_i82092: pcic_82092_smiirq_ctl(pcic, socket, PCIC_82092_CTL_IRQ, PCIC_82092_INT_DISABLE); break; case PCIC_O2_OZ6912: value = pcic_getb(pcic, 0, PCIC_CENTDMA); value &= ~0x8; pcic_putb(pcic, 0, PCIC_CENTDMA, value); /* Flush the write */ (void) pcic_getb(pcic, 0, PCIC_CENTDMA); break; case PCIC_CL_PD6832: case PCIC_TI_PCI1250: case PCIC_TI_PCI1221: case PCIC_TI_PCI1225: case PCIC_TI_PCI1410: case PCIC_ENE_1410: case PCIC_TI_PCI1510: case PCIC_TI_PCI1520: case PCIC_TI_PCI1420: case PCIC_ENE_1420: /* * This maps I/O interrupts to ExCA which * have been turned off by the write to * PCIC_INTERRUPT above. It would appear to * be the only way to actually turn I/O Ints off * while retaining CS Ints. */ brdgctl = ddi_get16(pcic->cfg_handle, (uint16_t *)(pcic->cfgaddr + PCI_CBUS_BRIDGE_CTRL)); pcic_err(NULL, 1, "pcic_disable_io_intr brdgctl(0x%x) was: 0x%x\n", PCI_CBUS_BRIDGE_CTRL, brdgctl); brdgctl |= PCIC_BRDGCTL_INTR_MASK; ddi_put16(pcic->cfg_handle, (uint16_t *)(pcic->cfgaddr + PCI_CBUS_BRIDGE_CTRL), brdgctl); /* Flush the write */ (void) ddi_get16(pcic->cfg_handle, (uint16_t *)(pcic->cfgaddr + PCI_CBUS_BRIDGE_CTRL)); break; default: break; } } static void pcic_cb_enable_intr(dev_info_t *dip) { anp_t *anp = ddi_get_driver_private(dip); pcicdev_t *pcic = anp->an_private; mutex_enter(&pcic->pc_lock); pcic_enable_io_intr(pcic, 0, pcic->pc_sockets[0].pcs_irq); mutex_exit(&pcic->pc_lock); } static void pcic_cb_disable_intr(dev_info_t *dip) { anp_t *anp = ddi_get_driver_private(dip); pcicdev_t *pcic = anp->an_private; mutex_enter(&pcic->pc_lock); pcic_disable_io_intr(pcic, 0); mutex_exit(&pcic->pc_lock); } static int log_pci_cfg_err(ushort_t e, int bridge_secondary) { int nerr = 0; if (e & PCI_STAT_PERROR) { nerr++; cmn_err(CE_CONT, "detected parity error.\n"); } if (e & PCI_STAT_S_SYSERR) { nerr++; if (bridge_secondary) cmn_err(CE_CONT, "received system error.\n"); else cmn_err(CE_CONT, "signalled system error.\n"); } if (e & PCI_STAT_R_MAST_AB) { nerr++; cmn_err(CE_CONT, "received master abort.\n"); } if (e & PCI_STAT_R_TARG_AB) cmn_err(CE_CONT, "received target abort.\n"); if (e & PCI_STAT_S_TARG_AB) cmn_err(CE_CONT, "signalled target abort\n"); if (e & PCI_STAT_S_PERROR) { nerr++; cmn_err(CE_CONT, "signalled parity error\n"); } return (nerr); } #if defined(__sparc) static int pcic_fault(enum pci_fault_ops op, void *arg) { pcicdev_t *pcic = (pcicdev_t *)arg; ushort_t pci_cfg_stat = pci_config_get16(pcic->cfg_handle, PCI_CONF_STAT); ushort_t pci_cfg_sec_stat = pci_config_get16(pcic->cfg_handle, 0x16); char nm[24]; int nerr = 0; cardbus_dump_pci_config(pcic->dip); switch (op) { case FAULT_LOG: (void) sprintf(nm, "%s-%d", ddi_driver_name(pcic->dip), ddi_get_instance(pcic->dip)); cmn_err(CE_WARN, "%s: PCIC fault log start:\n", nm); cmn_err(CE_WARN, "%s: primary err (%x):\n", nm, pci_cfg_stat); nerr += log_pci_cfg_err(pci_cfg_stat, 0); cmn_err(CE_WARN, "%s: sec err (%x):\n", nm, pci_cfg_sec_stat); nerr += log_pci_cfg_err(pci_cfg_sec_stat, 1); cmn_err(CE_CONT, "%s: PCI fault log end.\n", nm); return (nerr); case FAULT_POKEFINI: case FAULT_RESET: pci_config_put16(pcic->cfg_handle, PCI_CONF_STAT, pci_cfg_stat); pci_config_put16(pcic->cfg_handle, 0x16, pci_cfg_sec_stat); break; case FAULT_POKEFLT: if (!(pci_cfg_stat & PCI_STAT_S_SYSERR)) return (1); if (!(pci_cfg_sec_stat & PCI_STAT_R_MAST_AB)) return (1); break; default: break; } return (DDI_SUCCESS); } #endif static void pcic_do_resume(pcicdev_t *pcic) { int i, interrupt; uint8_t cfg; #if defined(PCIC_DEBUG) pcic_err(NULL, 6, "pcic_do_resume(): entered\n"); #endif pcic_mutex_enter(&pcic->pc_lock); /* protect the registers */ for (i = 0; i < pcic->pc_numsockets; i++) { /* Enable interrupts on PCI if needs be */ interrupt = pcic_getb(pcic, i, PCIC_INTERRUPT); if (pcic->pc_flags & PCF_USE_SMI) interrupt |= PCIC_INTR_ENABLE; pcic_putb(pcic, i, PCIC_INTERRUPT, PCIC_RESET | interrupt); pcic->pc_sockets[i].pcs_debounce_id = pcic_add_debqueue(&pcic->pc_sockets[i], drv_usectohz(pcic_debounce_time)); } pcic_mutex_exit(&pcic->pc_lock); /* protect the registers */ if (pcic_do_pcmcia_sr) (void) pcmcia_wait_insert(pcic->dip); /* * The CardBus controller may be in RESET state after the * system is resumed from sleeping. The RESET bit is in * the Bridge Control register. This is true for all(TI, * Toshiba ToPIC95/97, RICOH, and O2Micro) CardBus * controllers. Need to clear the RESET bit explicitly. */ cfg = ddi_get8(pcic->cfg_handle, pcic->cfgaddr + PCIC_BRIDGE_CTL_REG); if (cfg & (1<<6)) { cfg &= ~(1<<6); ddi_put8(pcic->cfg_handle, pcic->cfgaddr + PCIC_BRIDGE_CTL_REG, cfg); cfg = ddi_get8(pcic->cfg_handle, pcic->cfgaddr + PCIC_BRIDGE_CTL_REG); if (cfg & (1<<6)) { pcic_err(pcic->dip, 1, "Failed to take pcic out of reset"); } } } static void pcic_debounce(pcic_socket_t *pcs) { uint8_t status, stschng; pcic_mutex_enter(&pcs->pcs_pcic->pc_lock); pcs->pcs_flags &= ~PCS_STARTING; stschng = pcic_getb(pcs->pcs_pcic, pcs->pcs_socket, PCIC_CARD_STATUS_CHANGE); status = pcic_getb(pcs->pcs_pcic, pcs->pcs_socket, PCIC_INTERFACE_STATUS); #ifdef PCIC_DEBUG pcic_err(pcs->pcs_pcic->dip, 8, "pcic_debounce(0x%p, dip=0x%p) socket %d st 0x%x " "chg 0x%x flg 0x%x\n", (void *)pcs, (void *) pcs->pcs_pcic->dip, pcs->pcs_socket, status, stschng, pcs->pcs_flags); #endif pcic_putb(pcs->pcs_pcic, pcs->pcs_socket, PCIC_CARD_STATUS_CHANGE, PCIC_CD_DETECT); pcic_handle_cd_change(pcs->pcs_pcic, pcs, status); pcic_mutex_exit(&pcs->pcs_pcic->pc_lock); } static void pcic_deb_thread() { callb_cpr_t cprinfo; struct debounce *debp; clock_t lastt; CALLB_CPR_INIT(&cprinfo, &pcic_deb_mtx, callb_generic_cpr, "pcic debounce thread"); mutex_enter(&pcic_deb_mtx); while (pcic_deb_threadid) { while (pcic_deb_queue) { #ifdef PCIC_DEBUG pcic_dump_debqueue("Thread"); #endif debp = pcic_deb_queue; (void) drv_getparm(LBOLT, &lastt); if (lastt >= debp->expire) { pcic_deb_queue = debp->next; mutex_exit(&pcic_deb_mtx); pcic_debounce(debp->pcs); mutex_enter(&pcic_deb_mtx); kmem_free(debp, sizeof (*debp)); } else { (void) cv_timedwait(&pcic_deb_cv, &pcic_deb_mtx, debp->expire); } } CALLB_CPR_SAFE_BEGIN(&cprinfo); cv_wait(&pcic_deb_cv, &pcic_deb_mtx); CALLB_CPR_SAFE_END(&cprinfo, &pcic_deb_mtx); } pcic_deb_threadid = (kthread_t *)1; cv_signal(&pcic_deb_cv); CALLB_CPR_EXIT(&cprinfo); /* Also exits the mutex */ thread_exit(); } static void * pcic_add_debqueue(pcic_socket_t *pcs, int clocks) { clock_t lbolt; struct debounce *dbp, **dbpp = &pcic_deb_queue; (void) drv_getparm(LBOLT, &lbolt); dbp = kmem_alloc(sizeof (struct debounce), KM_SLEEP); dbp->expire = lbolt + clocks; dbp->pcs = pcs; mutex_enter(&pcic_deb_mtx); while (*dbpp) { if (dbp->expire > (*dbpp)->expire) dbpp = &((*dbpp)->next); else break; } dbp->next = *dbpp; *dbpp = dbp; #ifdef PCIC_DEBUG pcic_dump_debqueue("Add"); #endif cv_signal(&pcic_deb_cv); mutex_exit(&pcic_deb_mtx); return (dbp); } static void pcic_rm_debqueue(void *id) { struct debounce *dbp, **dbpp = &pcic_deb_queue; dbp = (struct debounce *)id; mutex_enter(&pcic_deb_mtx); while (*dbpp) { if (*dbpp == dbp) { *dbpp = dbp->next; kmem_free(dbp, sizeof (*dbp)); #ifdef PCIC_DEBUG pcic_dump_debqueue("Remove"); #endif cv_signal(&pcic_deb_cv); mutex_exit(&pcic_deb_mtx); return; } dbpp = &((*dbpp)->next); } pcic_err(NULL, 6, "pcic: Failed to find debounce id 0x%p\n", id); mutex_exit(&pcic_deb_mtx); } static int pcic_powerdelay = 0; static int pcic_exca_powerctl(pcicdev_t *pcic, int socket, int powerlevel) { int ind, value, orig_pwrctl; /* power setup -- if necessary */ orig_pwrctl = pcic_getb(pcic, socket, PCIC_POWER_CONTROL); #if defined(PCIC_DEBUG) pcic_err(pcic->dip, 6, "pcic_exca_powerctl(socket %d) powerlevel=%x orig 0x%x\n", socket, powerlevel, orig_pwrctl); #endif /* Preserve the PCIC_OUTPUT_ENABLE (control lines output enable) bit. */ powerlevel = (powerlevel & ~POWER_OUTPUT_ENABLE) | (orig_pwrctl & POWER_OUTPUT_ENABLE); if (powerlevel != orig_pwrctl) { if (powerlevel & ~POWER_OUTPUT_ENABLE) { int ifs; /* * set power to socket * note that the powerlevel was calculated earlier */ pcic_putb(pcic, socket, PCIC_POWER_CONTROL, powerlevel); (void) pcic_getb(pcic, socket, PCIC_POWER_CONTROL); /* * this second write to the power control register * is needed to resolve a problem on * the IBM ThinkPad 750 * where the first write doesn't latch. * The second write appears to always work and * doesn't hurt the operation of other chips * so we can just use it -- this is good since we can't * determine what chip the 750 actually uses * (I suspect an early Ricoh). */ pcic_putb(pcic, socket, PCIC_POWER_CONTROL, powerlevel); value = pcic_getb(pcic, socket, PCIC_POWER_CONTROL); pcic_mswait(pcic, socket, pcic_powerdelay); #if defined(PCIC_DEBUG) pcic_err(pcic->dip, 8, "\tpowerlevel reg = %x (ifs %x)\n", value, pcic_getb(pcic, socket, PCIC_INTERFACE_STATUS)); pcic_err(pcic->dip, 8, "CBus regs: PS 0x%x, Control 0x%x\n", pcic_getcb(pcic, CB_PRESENT_STATE), pcic_getcb(pcic, CB_CONTROL)); #endif /* * since power was touched, make sure it says it * is on. This lets it become stable. */ for (ind = 0; ind < 20; ind++) { ifs = pcic_getb(pcic, socket, PCIC_INTERFACE_STATUS); if (ifs & PCIC_POWER_ON) break; else { pcic_putb(pcic, socket, PCIC_POWER_CONTROL, 0); (void) pcic_getb(pcic, socket, PCIC_POWER_CONTROL); pcic_mswait(pcic, socket, 40); if (ind == 10) { pcic_putcb(pcic, CB_EVENT_FORCE, CB_EF_CVTEST); pcic_mswait(pcic, socket, 100); } pcic_putb(pcic, socket, PCIC_POWER_CONTROL, powerlevel & ~POWER_OUTPUT_ENABLE); (void) pcic_getb(pcic, socket, PCIC_POWER_CONTROL); pcic_mswait(pcic, socket, pcic_powerdelay); pcic_putb(pcic, socket, PCIC_POWER_CONTROL, powerlevel); (void) pcic_getb(pcic, socket, PCIC_POWER_CONTROL); pcic_mswait(pcic, socket, pcic_powerdelay); } } if (!(ifs & PCIC_POWER_ON)) { cmn_err(CE_WARN, "pcic socket %d: Power didn't get turned" "on!\nif status 0x%x pwrc 0x%x(x%x) " "misc1 0x%x igc 0x%x ind %d\n", socket, ifs, pcic_getb(pcic, socket, PCIC_POWER_CONTROL), orig_pwrctl, pcic_getb(pcic, socket, PCIC_MISC_CTL_1), pcic_getb(pcic, socket, PCIC_INTERRUPT), ind); return (BAD_VCC); } #if defined(PCIC_DEBUG) pcic_err(pcic->dip, 8, "\tind = %d, if status %x pwrc 0x%x " "misc1 0x%x igc 0x%x\n", ind, ifs, pcic_getb(pcic, socket, PCIC_POWER_CONTROL), pcic_getb(pcic, socket, PCIC_MISC_CTL_1), pcic_getb(pcic, socket, PCIC_INTERRUPT)); #endif } else { /* explicitly turned off the power */ pcic_putb(pcic, socket, PCIC_POWER_CONTROL, powerlevel); (void) pcic_getb(pcic, socket, PCIC_POWER_CONTROL); } } return (SUCCESS); } static int pcic_cbdoreset_during_poweron = 1; static int pcic_cbus_powerctl(pcicdev_t *pcic, int socket) { uint32_t cbctl = 0, orig_cbctl, cbstev, cbps; int ind, iobits; pcic_socket_t *sockp = &pcic->pc_sockets[socket]; pcic_putcb(pcic, CB_STATUS_EVENT, CB_SE_POWER_CYCLE); ind = pcic_power[sockp->pcs_vpp1].PowerLevel/10; cbctl |= pcic_cbv_levels[ind]; ind = pcic_power[sockp->pcs_vcc].PowerLevel/10; cbctl |= (pcic_cbv_levels[ind]<<4); orig_cbctl = pcic_getcb(pcic, CB_CONTROL); #if defined(PCIC_DEBUG) pcic_err(pcic->dip, 6, "pcic_cbus_powerctl(socket %d) vcc %d vpp1 %d " "cbctl 0x%x->0x%x\n", socket, sockp->pcs_vcc, sockp->pcs_vpp1, orig_cbctl, cbctl); #endif if (cbctl != orig_cbctl) { if (pcic_cbdoreset_during_poweron && (orig_cbctl & (CB_C_VCCMASK|CB_C_VPPMASK)) == 0) { iobits = pcic_getb(pcic, socket, PCIC_INTERRUPT); pcic_putb(pcic, socket, PCIC_INTERRUPT, iobits & ~PCIC_RESET); } pcic_putcb(pcic, CB_CONTROL, cbctl); if ((cbctl & CB_C_VCCMASK) == (orig_cbctl & CB_C_VCCMASK)) { pcic_mswait(pcic, socket, pcic_powerdelay); return (SUCCESS); } for (ind = 0; ind < 20; ind++) { cbstev = pcic_getcb(pcic, CB_STATUS_EVENT); if (cbstev & CB_SE_POWER_CYCLE) { /* * delay 400 ms: though the standard defines that the Vcc * set-up time is 20 ms, some PC-Card bridge requires longer * duration. * Note: We should check the status AFTER the delay to give time * for things to stabilize. */ pcic_mswait(pcic, socket, 400); cbps = pcic_getcb(pcic, CB_PRESENT_STATE); if (cbctl && !(cbps & CB_PS_POWER_CYCLE)) { /* break; */ cmn_err(CE_WARN, "cbus_powerctl: power off??\n"); } if (cbctl & CB_PS_BADVCC) { cmn_err(CE_WARN, "cbus_powerctl: bad power request\n"); break; } #if defined(PCIC_DEBUG) pcic_err(pcic->dip, 8, "cbstev = 0x%x cbps = 0x%x cbctl 0x%x(0x%x)", cbstev, pcic_getcb(pcic, CB_PRESENT_STATE), cbctl, orig_cbctl); #endif if (pcic_cbdoreset_during_poweron && (orig_cbctl & (CB_C_VCCMASK|CB_C_VPPMASK)) == 0) { pcic_putb(pcic, socket, PCIC_INTERRUPT, iobits); } return (SUCCESS); } pcic_mswait(pcic, socket, 40); } if (pcic_cbdoreset_during_poweron && (orig_cbctl & (CB_C_VCCMASK|CB_C_VPPMASK)) == 0) { pcic_putb(pcic, socket, PCIC_INTERRUPT, iobits); } cmn_err(CE_WARN, "pcic socket %d: Power didn't get turned on/off!\n" "cbstev = 0x%x cbps = 0x%x cbctl 0x%x(0x%x) " "vcc %d vpp1 %d", socket, cbstev, pcic_getcb(pcic, CB_PRESENT_STATE), cbctl, orig_cbctl, sockp->pcs_vcc, sockp->pcs_vpp1); return (BAD_VCC); } return (SUCCESS); } static int pcic_do_pprintf = 0; static void pcic_dump_debqueue(char *msg) { struct debounce *debp = pcic_deb_queue; clock_t lbolt; (void) drv_getparm(LBOLT, &lbolt); pcic_err(NULL, 6, debp ? "pcic debounce list (%s) lbolt 0x%x:\n" : "pcic debounce_list (%s) EMPTY lbolt 0x%x\n", msg, lbolt); while (debp) { pcic_err(NULL, 6, "%p: exp 0x%x next 0x%p id 0x%p\n", (void *) debp, (int)debp->expire, (void *) debp->next, debp->pcs->pcs_debounce_id); debp = debp->next; } } /* PRINTFLIKE3 */ static void pcic_err(dev_info_t *dip, int level, const char *fmt, ...) { if (pcic_debug && (level <= pcic_debug)) { va_list adx; int instance; char buf[256]; const char *name; #if !defined(PCIC_DEBUG) int ce; char qmark = 0; if (level <= 3) ce = CE_WARN; else ce = CE_CONT; if (level == 4) qmark = 1; #endif if (dip) { instance = ddi_get_instance(dip); /* name = ddi_binding_name(dip); */ name = ddi_driver_name(dip); } else { instance = 0; name = ""; } va_start(adx, fmt); (void) vsprintf(buf, fmt, adx); va_end(adx); #if defined(PCIC_DEBUG) if (pcic_do_pprintf) { if (dip) { if (instance >= 0) prom_printf("%s(%d),0x%p: %s", name, instance, dip, buf); else prom_printf("%s,0x%p: %s", name, dip, buf); } else prom_printf(buf); } else { if (dip) { if (instance >= 0) cmn_err(CE_CONT, "%s(%d),0x%p: %s", name, instance, (void *) dip, buf); else cmn_err(CE_CONT, "%s,0x%p: %s", name, (void *) dip, buf); } else cmn_err(CE_CONT, buf); } #else if (dip) cmn_err(ce, qmark ? "?%s%d: %s" : "%s%d: %s", name, instance, buf); else cmn_err(ce, qmark ? "?%s" : buf, buf); #endif } }