blob: fbc7cf1f1e47552fb83e466dd57a4b306133028f [file] [log] [blame]
/*
* 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.
* Copyright (c) 2016 by Delphix. All rights reserved.
*/
/*
* 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 <sys/types.h>
#include <sys/inttypes.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/user.h>
#include <sys/buf.h>
#include <sys/file.h>
#include <sys/uio.h>
#include <sys/conf.h>
#include <sys/stat.h>
#include <sys/autoconf.h>
#include <sys/vtoc.h>
#include <sys/dkio.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/sunndi.h>
#include <sys/var.h>
#include <sys/callb.h>
#include <sys/open.h>
#include <sys/ddidmareq.h>
#include <sys/dma_engine.h>
#include <sys/kstat.h>
#include <sys/kmem.h>
#include <sys/modctl.h>
#include <sys/pci.h>
#include <sys/pci_impl.h>
#include <sys/pctypes.h>
#include <sys/pcmcia.h>
#include <sys/sservice.h>
#include <sys/note.h>
#include <sys/pcic_reg.h>
#include <sys/pcic_var.h>
#if defined(__i386) || defined(__amd64)
#include <sys/pci_cfgspace.h>
#endif
#if defined(__sparc)
#include <sys/pci/pci_nexus.h>
#endif
#include <sys/hotplug/hpcsvc.h>
#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 int32_t pcic_quiesce(dev_info_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,
pcic_quiesce, /* devo_quiesce */
};
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", /* 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);
}
/*
* quiesce(9E) entry point.
*
* This function is called when the system is single-threaded at high
* PIL with preemption disabled. Therefore, this function must not be
* blocked.
*
* This function returns DDI_SUCCESS on success, or DDI_FAILURE on failure.
* DDI_FAILURE indicates an error condition and should almost never happen.
*/
static int32_t
pcic_quiesce(dev_info_t *dip)
{
anp_t *anp = ddi_get_driver_private(dip);
pcicdev_t *pcic = anp->an_private;
int i;
for (i = 0; i < pcic->pc_numsockets; i++) {
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);
/* poweroff socket */
pcic_putb(pcic, i, PCIC_POWER_CONTROL, 0);
pcic_putcb(pcic, CB_CONTROL, 0);
}
return (DDI_SUCCESS);
}
/*
* 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 defined(__i386) || defined(__amd64)
pci_regspec_t *reg;
uchar_t bus, dev, func;
uint_t classcode;
int length;
#endif
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) {
/* functional intr routed by ExCA register */
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);
/* IRQ serialized interrupts */
cfg = ddi_get8(pcic->cfg_handle,
pcic->cfgaddr + PCIC_DEVCTL_REG);
cfg &= ~PCIC_DEVCTL_INTR_MASK;
cfg |= PCIC_DEVCTL_INTR_ISA;
ddi_put8(pcic->cfg_handle,
pcic->cfgaddr + PCIC_DEVCTL_REG,
cfg);
break;
}
/* CSC interrupt routed to PCI */
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);
#if defined(__i386) || defined(__amd64)
/*
* Some TI chips have 2 cardbus slots(function0 and
* function1), and others may have just 1 cardbus slot.
* The interrupt routing register is shared between the
* 2 functions and can only be accessed through
* function0. Here we check the presence of the second
* cardbus slot and do the right thing.
*/
if (ddi_getlongprop(DDI_DEV_T_ANY, pcic->dip,
DDI_PROP_DONTPASS, "reg", (caddr_t)&reg,
&length) != DDI_PROP_SUCCESS) {
cmn_err(CE_WARN,
"pcic_setup_adapter(), failed to"
" read reg property\n");
break;
}
bus = PCI_REG_BUS_G(reg->pci_phys_hi);
dev = PCI_REG_DEV_G(reg->pci_phys_hi);
func = PCI_REG_FUNC_G(reg->pci_phys_hi);
kmem_free((caddr_t)reg, length);
if (func != 0) {
break;
}
classcode = (*pci_getl_func)(bus, dev, 1,
PCI_CONF_REVID);
classcode >>= 8;
if (classcode != 0x060700 &&
classcode != 0x060500) {
break;
}
/* Parallel PCI interrupts only */
cfg = ddi_get8(pcic->cfg_handle,
pcic->cfgaddr + PCIC_DEVCTL_REG);
cfg &= ~PCIC_DEVCTL_INTR_MASK;
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);
#endif
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 the 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);