pcic.c revision 11c2b4c0e543fe2e1e5910cde1f4422cc3218160
/*
* 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
* 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"
/*
* 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/inttypes.h>
#include <sys/autoconf.h>
#include <sys/ddidmareq.h>
#include <sys/dma_engine.h>
#include <sys/pci_impl.h>
#include <sys/sservice.h>
#include <sys/pcic_reg.h>
#include <sys/pcic_var.h>
#if defined(__sparc)
#endif
#define SOFTC_SIZE (sizeof (anp_t))
static int pcic_probe(dev_info_t *);
typedef struct pcm_regs pcm_regs_t;
static void pcic_init_assigned(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 = {
NULL,
NULL,
NULL,
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 = {
NULL,
#ifdef CARDBUS
#else
#endif
};
static struct dev_ops pcic_devops = {
0,
};
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_dump_debqueue(char *msg);
#if defined(PCIC_DEBUG)
#define pcic_mutex_enter(a) \
{ \
mutex_enter(a); \
};
#define pcic_mutex_exit(a) \
{ \
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 */
};
0, 0, 0,
3, /* 3.3V */
0,
2, /* 5V */
0, 0, 0, 0, 0, 0,
1 /* 12V */
};
{
},
{
33, /* 3.3Volt */
},
{
5*10, /* 5Volt */
},
{
12*10, /* 12Volt */
}
};
/*
* 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
struct debounce {
};
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 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_reset_socket(dev_info_t *, int, int);
static void pcic_pm_detection(void *);
static int pcic_find_pci_type(pcicdev_t *);
static void pcic_82092_smiirq_ctl(pcicdev_t *, int, int, int);
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 void pcic_cb_enable_intr(dev_info_t *);
static void pcic_cb_disable_intr(dev_info_t *);
static cb_nexus_cb_t pcic_cbnexus_ops = {
};
#if defined(__sparc)
#endif
/*
* pcmcia interface operations structure
* this is the private interface that is exported to the nexus
*/
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 *) = {
};
&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 = {
};
int
_init()
{
int stat;
/* Allocate soft state */
return (stat);
return (stat);
}
int
_fini()
{
int stat = 0;
return (stat);
if (pcic_deb_threadid) {
pcic_deb_threadid = 0;
while (!pcic_deb_threadid)
pcic_deb_threadid = 0;
}
return (stat);
}
int
{
}
/*
* pcic_getinfo()
*/
/*ARGSUSED*/
static int
{
int error = DDI_SUCCESS;
switch (cmd) {
case DDI_INFO_DEVT2DEVINFO:
minor &= 0x7f;
else
break;
case DDI_INFO_DEVT2INSTANCE:
minor &= 0x7f;
break;
default:
error = DDI_FAILURE;
break;
}
return (error);
}
static int
{
int value;
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
*/
return (DDI_PROBE_FAILURE);
#if defined(PCIC_DEBUG)
if (pcic_debug)
if (pcic_debug)
#endif
#if defined(PCIC_DEBUG)
if (pcic_debug)
#endif
(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
*/
#if defined(PCIC_DEBUG)
if (pcic_debug)
#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)
#endif
return (DDI_PROBE_SUCCESS);
}
}
#if defined(PCIC_DEBUG)
if (pcic_debug)
#endif
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;
/*
*/
static int
{
0,
4096,
/* CSC Interrupt: Card detect interrupt on */
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
{
char *typename;
#if defined(PCIC_DEBUG)
if (pcic_debug) {
}
#endif
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
/*
* for now, this is a simulated resume.
* a real one may need different things.
*/
/* should probe for new sockets showing up */
(void) pcmcia_begin_resume(dip);
#ifdef CARDBUS
#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.
*/
return (DDI_FAILURE);
}
"pci-control-reg-number", pci_control_reg_num);
"pci-config-reg-number", pci_config_reg_num);
/*
* pcic->pc_irq is really the IPL level we want to run at
* set the default values here and override from intr spec
*/
"interrupt-priorities", -1);
int actual;
/* see if intrspec tells us different */
else
(void) ddi_intr_free(hdl);
}
}
/*
* Check our parent bus type. We do different things based on which
* bus we're on.
*/
"pcic%d: can't find parent bus type\n",
return (DDI_FAILURE);
}
} /* ddi_prop_op("device_type") */
} else {
"set BIOS to yenta mode if applicable\n",
return (DDI_FAILURE);
}
"clock-frequency", 0)) == 0) {
else
} else {
/*
* OBP can declare the speed in Hz...
*/
} /* ddi_prop_op("clock-frequency") */
#ifdef PCIC_DEBUG
if (pcic_debug) {
"pcic%d: parent bus type = [%s], speed = %d MHz\n",
}
#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.
*/
int class_code;
#endif
/* usually need to get at config space so map first */
&attr,
&pcic->cfg_handle) !=
DDI_SUCCESS) {
"pcic%d: unable to map config space"
"regs\n",
return (DDI_FAILURE);
} /* ddi_regs_map_setup */
"class-code", -1);
#ifdef PCIC_DEBUG
if (pcic_debug) {
}
#endif
switch (class_code) {
case PCIC_PCI_CARDBUS:
/*
* Get access to the adapter registers on the
* PCI bus. A 4K memory page
*/
#if defined(PCIC_DEBUG)
if (pcic_debug) {
int nr;
long rs;
"cfghndl 0x%p nregs %d",
(void) ddi_dev_regsize(dip,
PCIC_PCI_CONTROL_REG_NUM, (int)rs);
}
#endif
DDI_SUCCESS) {
"pcic%d: unable to map PCI regs\n",
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.
*/
"bridge\n",
return (DDI_FAILURE);
}
break;
default:
case PCIC_PCI_PCMCIA:
/*
* Get access to the adapter IO registers on the
* PCI bus config space.
*/
/*
* 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
* format.
*/
&attr,
"pcic%d: unable to map PCI regs\n",
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.
*/
"bridge\n",
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.
*/
case PCIC_TI_PCI1031:
if (ddi_regs_map_setup(dip,
&attr,
"pcic%d: unable to map "
"PCI regs\n",
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,
"pcic%d: unable to map ISA registers\n",
return (DDI_FAILURE);
} /* ddi_regs_map_setup */
/* ISA bus is limited to 24-bits, but not first 640K */
} /* !PCF_PCIBUS */
#ifdef PCIC_DEBUG
if (pcic_debug) {
}
#endif
/*
* Setup various adapter registers for the PCI case. For the
* non-PCI case, find out the chip type.
*/
int iline;
#if defined(__sparc)
iline = 0;
#else
#endif
/* set flags and socket counts based on chip type */
case PCIC_INTEL_i82092:
/* we can only support 4 Socket version */
if (cfg & PCIC_82092_4_SOCKETS) {
if (iline != 0xFF)
else
} else {
"pcic%d: Intel 82092 adapter "
"in unsupported configuration: 0x%x",
pcic->pc_numsockets = 0;
} /* PCIC_82092_4_SOCKETS */
break;
case PCIC_CL_PD6730:
case PCIC_CL_PD6729:
"interrupts", 0);
/* if not interrupt pin then must use ISA style IRQs */
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.
*/
}
break;
case PCIC_TI_PCI1031:
/* this chip doesn't do CardBus but looks like one */
/* FALLTHROUGH */
default:
/* FALLTHROUGH */
/* indicate feature even if not supported */
/* Not sure if these apply to all these chips */
if (iline != 0xFF) {
cfg &= (~PCIC_FUN_INT_MOD_ISA);
}
else
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 */
/* default to Intel i82365SL and then refine */
/* go until one succeeds or none left */
break;
}
/* any chip specific flags get set here */
case PCIC_CL_PD6722:
}
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 */
/*
* 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 */
if (!(value >= PCIC_REV_LEVEL_LOW &&
value <= PCIC_REV_LEVEL_HI))
break;
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++;
/* put PC Card into RESET, just in case */
value & ~PCIC_RESET);
}
#if defined(PCIC_DEBUG)
if (pcic_debug)
#endif
if (pcic->pc_numsockets == 0) {
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.
*/
for (i = 0; i < count; i++) {
/* put pattern into socket 0 */
PCIC_SYSMEM_0_STARTLOW, 0x11);
/* put pattern into socket 2 */
PCIC_SYSMEM_0_STARTLOW, 0x33);
/* read back socket 0 */
/* read back chip 1 socket 0 */
if (j == value) {
}
}
}
DDI_PROP_DONTPASS, "need-mult-irq",
0xffff) != 0xffff)
} /* !PCF_PCIBUS */
/*
* this is temporary until a real resource allocator is
* implemented.
*/
#ifdef PCIC_DEBUG
if (pcic_debug) {
nregs = 0;
nintrs = 0;
"pcic%d: %d register sets, %d interrupts\n",
nintrs = 0;
while (nregs--) {
DDI_SUCCESS) {
"\tregnum %d size %ld (0x%lx)"
"bytes",
if (nintrs ==
" mapped at: 0x%p\n",
else
} else {
"\tddi_dev_regsize(rnumber"
"= %d) returns DDI_FAILURE\n",
nintrs);
}
nintrs++;
} /* while */
} /* if (pcic_debug) */
#endif
"disable-audio", 0))
"disable-cardbus", 0))
typename);
/*
* Init all socket SMI levels to 0 (no SMI)
*/
for (i = 0; i < PCIC_MAX_SOCKETS; i++) {
}
/*
* 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.
*/
if (smi >= 0)
break;
}
}
#if defined(PCIC_DEBUG)
if (pcic_debug)
#endif
/* init to same so share is easy */
for (i = 0; i < pcic->pc_numsockets; i++)
/* any special handling of IRQ levels */
if ((i & 1) == 0) {
int xx;
smi =
xx);
if (smi >= 0)
break;
}
}
}
if (smi >= 0)
}
}
sizeof (ddi_intr_handle_t), KM_SLEEP);
struct ddi_parent_private_data *pdp;
continue;
else {
}
/*
* 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.
*/
DDI_INTR_ALLOC_NORMAL) != DDI_SUCCESS) {
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.
*/
/* Stay compatible w/ PCMCIA */
if (i == 0) {
NULL);
}
"%s: ddi_intr_add_handler failed",
ddi_get_name(dip));
goto isa_exit2;
}
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.
*/
KM_SLEEP);
goto pci_exit1;
&pri) != DDI_SUCCESS) {
goto pci_exit1;
}
goto pci_exit2;
(void) ddi_intr_remove_handler(
pcic->pc_pci_intr_hdlp[0]);
goto pci_exit2;
}
/* Stay compatible w/ PCMCIA */
/* init to same (PCI) so share is easy */
for (i = 0; i < pcic->pc_numsockets; i++)
break;
}
/*
* Setup the adapter hardware to some reasonable defaults.
*/
/* mark the driver state as attached */
for (j = 0; j < pcic->pc_numsockets; j++)
if (ddi_intr_add_softint(dip,
goto pci_exit2;
#if defined(PCIC_DEBUG)
if (pcic_debug)
#endif
#ifdef CARDBUS
(void) cardbus_enable_cd_intr(dip);
if (pcic_debug) {
}
/*
* Give the Cardbus misc module a chance to do it's per-adapter
* instance setup. Note that there is no corresponding detach()
* call.
*/
"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.
*/
goto pci_exit2;
if (pcic_maxinst == -1) {
/* This assumes that all instances run at the same IPL. */
v.v_maxsyspri - 2);
}
/*
* Setup a debounce timeout to do an initial card detect
* and enable interrupts.
*/
for (j = 0; j < pcic->pc_numsockets; j++) {
}
return (i);
for (j = i; j < 0; j--)
sizeof (ddi_intr_handle_t));
return (DDI_FAILURE);
return (DDI_FAILURE);
}
/*
* pcic_detach()
* request to detach from the system
*/
static int
{
int i;
switch (cmd) {
case DDI_DETACH:
/* don't detach if the nexus still talks to us */
return (DDI_FAILURE);
/* kill off the pm simulation */
if (pcic->pc_pmtimer)
/* turn everything off for all sockets and chips */
for (i = 0; i < pcic->pc_numsockets; i++) {
/* disable interrupts and put card into RESET */
}
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.
*/
#ifdef CARDBUS
for (i = 0; i < pcic->pc_numsockets; i++) {
}
}
}
#endif
/* turn everything off for all sockets and chips */
for (i = 0; i < pcic->pc_numsockets; i++) {
/* disable interrupts and put card into RESET */
/*
* 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) {
}
}
}
/*
* 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_offbits = 0;
static void
{
int i;
/*
* all PCI-to-PCMCIA bus bridges need memory and I/O enabled
*/
}
/* enable each socket */
for (i = 0; i < pcic->pc_numsockets; i++) {
/* find out the socket capabilities (I/O vs memory) */
/* disable all windows just in case */
/* 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:
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_read(pcic, i,
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:
break;
case PCIC_INTR_MODE_ISA:
break;
}
cfg = 0;
}
break;
case PCIC_INTEL_i82092:
break;
case PCIC_TI_PCI1130:
case PCIC_TI_PCI1131:
case PCIC_TI_PCI1250:
case PCIC_TI_PCI1031:
cfg &= ~PCIC_DEVCTL_INTR_MASK;
switch (pcic->pc_intr_mode) {
case PCIC_INTR_MODE_ISA:
break;
}
#ifdef PCIC_DEBUG
if (pcic_debug) {
"write reg 0x%x=%x \n",
}
#endif
cfg);
switch (pcic->pc_intr_mode) {
case PCIC_INTR_MODE_PCI_1:
cfg |= PCIC_CRDCTL_PCIINTR |
break;
}
#ifdef PCIC_DEBUG
if (pcic_debug) {
" write reg 0x%x=%x \n",
}
#endif
cfg);
break;
case PCIC_TI_PCI1221:
case PCIC_TI_PCI1225:
#ifdef PCIC_DEBUG
if (pcic_debug) {
" write reg 0x%x=%x \n",
}
#endif
} else {
cfg |= PCIC_DIAG_ASYNC;
}
#ifdef PCIC_DEBUG
if (pcic_debug) {
" write reg 0x%x=%x \n",
PCIC_DIAG_REG, cfg);
}
#endif
break;
case PCIC_TI_PCI1520:
case PCIC_TI_PCI1510:
case PCIC_TI_VENDOR:
cfg);
}
cfg &= ~PCIC_DEVCTL_INTR_MASK;
cfg);
/* tie INTA and INTB together */
break;
case PCIC_TI_PCI1410:
break;
case PCIC_TOSHIBA_TOPIC100:
case PCIC_TOSHIBA_TOPIC95:
case PCIC_TOSHIBA_VENDOR:
cfg |= (PCIC_TOSHIBA_SCR_SLOTON |
cfg &= (~PCIC_TOSHIBA_SCR_PRT_MASK);
switch (pcic->pc_intr_mode) {
case PCIC_INTR_MODE_ISA:
cfg &= ~PCIC_TOSHIBA_ICR_SRC;
cfg);
break;
case PCIC_INTR_MODE_PCI_1:
cfg &= (~PCIC_TOSHIBA_ICR_PIN_MASK);
break;
}
break;
case PCIC_O2MICRO_VENDOR:
switch (pcic->pc_intr_mode) {
case PCIC_INTR_MODE_ISA:
cfg32 |= (PCIC_O2MICRO_ISA_LEGACY |
cfg32);
cfg);
break;
case PCIC_INTR_MODE_PCI_1:
cfg32);
break;
}
break;
case PCIC_RICOH_VENDOR:
cfg16 |= (PCIC_RICOH_CSC_INT_MOD |
cfg16);
cfg16);
cfg);
}
break;
default:
break;
} /* switch */
/*
* The default value in the EEPROM (loaded on reset) for
* MFUNC0 is connected to INTA, and MFUNC1 is connected to
* INTB. This applies to all TI CardBus controllers.
*/
value &= ~0xff;
}
/* setup general card status change interrupt */
case PCIC_TI_PCI1225:
case PCIC_TI_PCI1221:
case PCIC_TI_PCI1031:
case PCIC_TI_PCI1520:
case PCIC_TI_PCI1410:
break;
default:
if (pcic->pc_intr_mode ==
break;
} else {
break;
}
}
/* take card out of RESET */
/* turn power off and let CS do this */
/* final chip specific initialization */
case PCIC_VADEM:
/* FALLTHROUGH */
case PCIC_I82365SL:
/* clear any pending interrupts */
break;
/* The 82092 uses PCI config space to enable interrupts */
case PCIC_INTEL_i82092:
break;
case PCIC_CL_PD6729:
}
break;
} /* switch */
#if defined(PCIC_DEBUG)
if (pcic_debug)
"socket %d value=%x, flags = %x (%s)\n",
"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*/
{
#if defined(PCIC_DEBUG)
"pcic_intr: enter pc_flags=0x%x PCF_ATTACHED=0x%x"
" pc_numsockets=%d \n",
#endif
return (DDI_INTR_UNCLAIMED);
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
*/
for (i = 0; i < pcic->pc_numsockets; i++) {
int card_type;
int value_cb = 0;
/* get the socket's I/O addresses */
io_ints &= ~(1 << i);
continue;
}
else
#if defined(PCIC_DEBUG)
"card_type = %d, value_cb = 0x%x\n",
if (pcic_debug)
"\tchange on socket %d (%x)\n", i,
value);
#endif
/* find out what happened */
/* acknowledge the interrupt */
if (value_cb)
if (value)
value);
/* if not callback handler, nothing to do */
continue;
}
/* Card Detect */
if (value & PCIC_CD_DETECT ||
value_cb & CB_PS_CCDMASK) {
#if defined(PCIC_DEBUG)
if (pcic_debug)
"\tcd_detect: status=%x,"
" flags=%x\n",
#else
#ifdef lint
if (status == 0)
status++;
#endif
#endif
/*
* Turn off all interrupts for this socket here.
*/
irq &= ~PCIC_CHANGE_MASK;
/*
* Put the socket in debouncing state so that
* the leaf driver won't receive interrupts.
* Crucial for handling surprise-removal.
*/
if (!sockp->pcs_cd_softint_flg) {
(void) ddi_intr_trigger_softint(
}
io_ints &= ~(1 << i);
} /* PCIC_CD_DETECT */
}
/* Battery Warn Detect */
value & PCIC_BW_DETECT &&
}
/* Battery Dead Detect */
if (value & PCIC_BD_DETECT) {
/*
* need to work out event if RI not enabled
* and card_type == IF_IO
*/
i);
} else {
/*
* information in pin replacement
* register if one is available
*/
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_intr: pc_intr_mode=%d pc_type=%x io_ints=0x%x\n",
#endif
if (io_ints) {
}
#if defined(PCIC_DEBUG)
"pcic_intr: ret=%d value=%d DDI_INTR_CLAIMED=%d\n",
#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_do_io_intr - calls client interrupt handlers
*/
static int
{
int ret = DDI_INTR_UNCLAIMED;
#if defined(PCIC_DEBUG)
"pcic_do_io_intr: pcic=%p sockets=%d irq_top=%p\n",
#endif
do {
#if defined(PCIC_DEBUG)
"\t pcs_flags=0x%x PCS_CARD_PRESENT=0x%x\n",
"\t sockets=%d cur=%d intr=%p arg1=%p "
"arg2=%p\n",
#endif
#if defined(PCIC_DEBUG)
"\t ret=%d DDI_INTR_CLAIMED=%d\n",
#endif
}
} else {
}
#if defined(PCIC_DEBUG)
"pcic_do_io_intr: exit ret=%d DDI_INTR_CLAIMED=%d\n",
#endif
return (ret);
}
/*
* pcic_inquire_adapter()
* SocketServices InquireAdapter function
* get characteristics of the physical adapter
*/
/*ARGSUSED*/
static int
{
config->ActiveHigh = 0;
#if defined(PCIC_DEBUG)
if (pcic_debug) {
}
#endif
config->ResourceFlags = 0;
switch (pcic->pc_intr_mode) {
case PCIC_INTR_MODE_PCI_1:
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
{
} else {
}
/*
* 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
{
case PCIC_I82365SL:
case PCIC_VADEM:
case PCIC_VADEM_VG469:
default:
/* Intel chip wants it in waitstates */
wspeed = 0;
wspeed = 1;
wspeed = 2;
else
wspeed = 3;
break;
case PCIC_INTEL_i82092:
if (speed > 80)
if (speed > 100)
if (speed > 150)
if (speed > 200)
if (speed > 250)
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 {
} 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
{
struct pcic_card_times *ctp;
offset = 3;
else
offset = 0;
/*
* Add (clk_pulse/2) and an extra 1 to account for rounding errors.
*/
if (set < 0)
set = 0;
if (cmd < 0)
cmd = 0;
if (rec < 0)
rec = 0;
#if defined(PCIC_DEBUG)
"ct=%d, cp=%d, cmd=0x%x, setup=0x%x, rec=0x%x\n",
#endif
}
/*
* 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
{
int select;
#if defined(PCIC_DEBUG)
if (pcic_debug) {
"\twindow=%d, socket=%d, WindowSize=%d, speed=%d\n",
}
#endif
/*
* do some basic sanity checking on what we support
* we don't do paged mode
*/
return (BAD_ATTRIBUTE);
}
/*
* we don't care about previous mappings.
* Card Services will deal with that so don't
* even check
*/
#if defined(PCIC_DEBUG)
if (pcic_debug)
#endif
/* this is memory window mapping */
/* only windows 2-6 can do memory mapping */
"\tattempt to map to non-mem window\n");
return (BAD_WINDOW);
}
if (window->WindowSize == 0)
return (BAD_SIZE);
}
else
which = 0;
#if defined(PCIC_DEBUG)
if (pcic_debug) {
"\tbase=%x, win=%d\n", (unsigned)base,
win);
if (which)
"\tneed to remap window\n");
}
#endif
}
ret = 0;
NDI_RA_ALLOC_SPECIFIED : 0) |
#if defined(PCIC_DEBUG)
"\tlen 0x%"PRIx64
#endif
if (ret == DDI_FAILURE) {
"\tpcmcia_alloc_mem() failed\n");
return (BAD_SIZE);
}
#if defined(PCIC_DEBUG)
if (pcic_debug)
"\tsetwindow: new base=%x\n",
#endif
&res,
0xffff) |
&memp->pcw_handle,
if (which != DDI_SUCCESS) {
"failed\n");
return (BAD_WINDOW);
}
#if defined(PCIC_DEBUG)
if (pcic_debug)
"\tmap=%x, hostmem=%p\n",
(void *)memp->pcw_hostmem);
#endif
} else {
}
/* report the handle back to caller */
#if defined(PCIC_DEBUG)
if (pcic_debug) {
"\twindow mapped to %x@%x len=%d\n",
}
#endif
/* find the register set offset */
#if defined(PCIC_DEBUG)
if (pcic_debug)
#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 */
SYSMEM_ZERO_WAIT : 0;
/* need to select register set */
SYSMEM_LOW(base));
/*
* Some adapters can decode window addresses greater
* than 16-bits worth, so handle them here.
*/
case PCIC_INTEL_i82092:
SYSMEM_EXT(base));
break;
case PCIC_CL_PD6729:
case PCIC_CL_PD6730:
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.
*/
SYSMEM_EXT(base));
}
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:
SYSMEM_EXT(base));
break;
default:
"cardbus vendor:0x%X\n",
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
*/
/*
* Setup this window's timing.
*/
case PCIC_CL_PD6729:
case PCIC_CL_PD6730:
case PCIC_CL_PD6710:
case PCIC_CL_PD6722:
wspeed);
break;
case PCIC_INTEL_i82092:
default:
break;
} /* switch */
#if defined(PCIC_DEBUG)
if (pcic_debug)
"\twindow %d speed bits = %x for "
"%dns\n",
#endif
wspeed);
/*
* now map the card's memory pages - we start with page
* 0
* we also default to AM -- set page might change it
*/
/*
* enable the window even though redundant
* and SetPage may do it again.
*/
memp->pcw_offset = 0;
} else {
/*
* not only do we unmap the memory, the
* window has been turned off.
*/
}
/* disable current mapping */
}
#if defined(PCIC_DEBUG)
if (pcic_debug)
#endif
} else {
/*
* This is a request for an IO window
*/
/* I/O windows */
#if defined(PCIC_DEBUG)
if (pcic_debug)
#endif
/* only windows 0 and 1 can do I/O */
"\twindow is out of range (%d)\n",
return (BAD_WINDOW);
}
/* we don't want an odd-size window */
window->WindowSize++;
}
}
/*
* 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,
* range.
*/
/* need to rethink this */
#if defined(PCIC_DEBUG)
"bbase 0x%"PRIx64
PRIx64"\n",
#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.
*/
"bbase 0x%" PRIx64
} /* 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.
*/
#if defined(PCIC_DEBUG)
"\tsetwindow: new base=%x orig base 0x%x\n",
#endif
&res,
0xffff) |
&winp->pcw_handle,
base)) != DDI_SUCCESS) {
"failed\n");
return (BAD_WINDOW);
}
/* find the register set offset */
#if defined(PCIC_DEBUG)
if (pcic_debug) {
"\tenable: window=%d, select=%x, "
"base=%x, handle=%p\n",
}
#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 */
/*
* 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.
*/
/*
* 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()
*/
(win * PCIC_IO_OFFSET_OFFSET),
(win * PCIC_IO_OFFSET_OFFSET),
} /* 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.
*/
(IOMEM_16BIT | IOMEM_IOCS16) : 0;
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_IOCS16;
break;
case PCIC_TI_PCI1031:
which |= IOMEM_WAIT16;
break;
case PCIC_TI_PCI1130:
which |= IOMEM_WAIT16;
break;
case PCIC_INTEL_i82092:
break;
default:
which |= IOMEM_WAIT16;
#ifdef notdef
which |= IOMEM_ZERO_WAIT;
#endif
break;
} /* switch (pc_type) */
/*
* Setup the data width and timing
*/
/*
* Enable the IO window
*/
#if defined(PCIC_DEBUG)
if (pcic_debug) {
"\twhich = %x, select = %x (%x)\n",
}
#endif
} else {
/*
* not only do we unmap the IO space, the
* window has been turned off.
*/
}
/* disable current mapping */
winp->pcw_offset = 0;
/* now make sure we don't accidentally re-enable */
/* find the register set offset */
PCIC_IO_ADDR_0_STARTLOW + select, 0);
PCIC_IO_ADDR_0_STARTHI + select, 0);
PCIC_IO_ADDR_0_STOPLOW + select, 0);
PCIC_IO_ADDR_0_STOPHI + select, 0);
}
}
return (SUCCESS);
}
/*
* pcic_card_state()
* compute the instantaneous Card State information
*/
static int
{
#if defined(PCIC_DEBUG)
int orig_value;
#endif
#if defined(PCIC_DEBUG)
orig_value = value;
if (pcic_debug >= 8)
(void *)sockp,
"\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.
*/
!sockp->pcs_debounce_id &&
if (value & PCIC_POWER_ON) {
if (value & PCIC_READY)
result |= SBM_RDYBSY;
}
} else
result = 0;
#if defined(PCIC_DEBUG)
"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",
#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
{
int select;
#if defined(PCIC_DEBUG)
if (pcic_debug) {
"pcic_set_page: window=%d, socket=%d, page=%d\n",
}
#endif
/* only windows 2-6 work on memory */
if (window < PCIC_IOWINDOWS)
return (BAD_WINDOW);
/* only one page supported (but any size) */
return (BAD_PAGE);
window -= PCIC_IOWINDOWS;
#if defined(PCIC_DEBUG)
if (pcic_debug)
#endif
/* window must be enabled */
return (BAD_ATTRIBUTE);
/* find the register set offset */
#if defined(PCIC_DEBUG)
if (pcic_debug)
#endif
/*
* now map the card's memory pages - we start with page 0
*/
which = 0; /* assume simple case */
} else {
}
/*
* if caller says Write Protect, enforce it.
*/
} else {
}
#if defined(PCIC_DEBUG)
if (pcic_debug) {
if (which & CARDMEM_WRITE_PROTECT)
}
#endif
/* address computation based on 64MB range and not larger */
/*
* while not really necessary, this just makes sure
* nothing turned the window off behind our backs
*/
#if defined(PCIC_DEBUG)
if (pcic_debug) {
(void *)memp->pcw_hostmem,
(void *)memp->pcw_hostmem,
}
#endif
if (which & PCW_ATTRIBUTE)
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
{
#if defined(PCIC_DEBUG)
if (pcic_debug) {
"pcic_set_vcc_level(pcic=%p, VccLevel=%d)\n",
}
#endif
/*
* check VccLevel
* if this is zero, power is being turned off
* if it is non-zero, power is being turned on.
*/
return (0);
}
/*
* range checking for sanity's sake
*/
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
*/
#if defined(PCIC_DEBUG)
if (pcic_debug) {
"socket present state = 0x%x\n",
}
#endif
switch (socket_present_state & PCIC_VCC_MASK) {
case PCIC_VCC_3VCARD:
/* fall through */
return
case PCIC_VCC_5VCARD:
return
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
*/
}
default:
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
{
int powerlevel = 0;
#if defined(PCIC_DEBUG)
if (pcic_debug) {
"pcic_set_socket(dip=%p, socket=%d)"
" Vcc=%d Vpp1=%d Vpp2=%d\n", (void *)dip,
}
#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 */
return (BAD_VPP);
powerlevel = 0;
} else {
#if defined(PCIC_DEBUG)
#endif
/* valid Vcc power level? */
return (BAD_VCC);
case 33: /* 3.3V */
case 60: /* for bad CIS in Option GPRS card */
"%s%d: Bad Request for 3.3V "
"(Controller incapable)\n",
return (BAD_VCC);
}
/* FALLTHROUGH */
case 50: /* 5V */
/*
* 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.
*/
} else
break;
default:
return (BAD_VCC);
}
/* enable Vcc */
#if defined(PCIC_DEBUG)
if (pcic_debug) {
}
#endif
ind = 0; /* default index to 0 power */
& VPP1)) {
return (BAD_VPP);
}
}
& VPP2)) {
return (BAD_VPP);
}
}
/*
* if one is turned on, both are turned on and only
* the Vpp1 bits should be set
*/
/* must be the same if one not zero */
"%s%d: Bad Power Request "
"(Vpp1/2 not the same)\n",
return (BAD_VPP);
}
}
}
#if defined(PCIC_DEBUG)
if (pcic_debug) {
powerlevel, ind);
}
#endif
}
/* turn socket->IREQRouting off while programming */
interrupt &= ~PCIC_INTR_MASK;
case PCIC_INTEL_i82092:
break;
default:
break;
} /* switch */
/* the SCIntMask specifies events to detect */
#if defined(PCIC_DEBUG)
if (pcic_debug)
"\tSCIntMask=%x, interrupt=%x, mirq=%x\n",
#endif
mirq & ~PCIC_CHANGE_MASK);
/* save the mask we want to use */
/*
* 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!
*/
/* now update the hardware to reflect events desired */
mirq |= PCIC_BD_DETECT;
mirq |= PCIC_BW_DETECT;
mirq |= PCIC_RD_DETECT;
mirq |= PCIC_CD_DETECT;
}
/*
* card just came ready.
* make sure enough time elapses
* before touching it.
*/
}
#if defined(PCIC_DEBUG)
if (pcic_debug) {
}
#endif
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_GPI_ENABLE;
} else {
irq &= ~PCIC_GPI_ENABLE;
}
break;
case PCIC_CL_PD6710:
case PCIC_CL_PD6722:
value);
} else {
value &= ~PCIC_MC_SPEAKER_ENB;
value);
}
break;
case PCIC_CL_PD6729:
case PCIC_CL_PD6730:
case PCIC_CL_PD6832:
} else {
value &= ~PCIC_MC_SPEAKER_ENB;
}
value |= PCIC_MC_3VCC;
else
value &= ~PCIC_MC_3VCC;
break;
case PCIC_O2_OZ6912:
else
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:
} else {
}
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 */
}
powerlevel = 0;
} else
return (value);
}
/*
* If outputs were disabled and the power is going on
* turn on outputs afterwards.
*/
}
/*
* Once we have done the power stuff can re-enable management
* interrupts.
*/
#if defined(PCIC_DEBUG)
"cbctl 0x%x\n",
#endif
/* irq processing */
/* IRQ only for I/O */
value &= ~PCIC_INTR_MASK;
/* to enable I/O operation */
"SetSocket: IRQ mismatch %x != %x!\n",
else
}
} else {
}
#if defined(PCIC_DEBUG)
if (pcic_debug) {
"\tsocket type is I/O and irq %x is %s\n", irq,
"enabled" : "not enabled");
}
#endif
} else {
/* make sure I/O mode is off */
value &= ~PCIC_IO_CARD;
}
return (SUCCESS);
}
/*
* pcic_inquire_socket()
* SocketServices InquireSocket function
* returns basic characteristics of the socket
*/
/*ARGSUSED*/
static int
{
int value;
socket->ActiveHigh = 0;
/* these are the usable IRQs */
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
{
#if defined(PCIC_DEBUG)
if (pcic_debug >= 8)
"pcic_inquire_window: window = %d/%d socket=%d\n",
#endif
if (type < PCIC_IOWINDOWS) {
} else {
}
/* initialize the socket map - one socket per window */
} else {
}
return (SUCCESS);
}
/*
* pcic_get_adapter()
* SocketServices GetAdapter function
* this is nearly a no-op.
*/
/*ARGSUSED*/
static int
{
return (SUCCESS);
}
/*
* pcic_get_page()
* SocketServices GetPage function
* returns info about the window
*/
/*ARGSUSED*/
static int
{
/* I/O windows are the first two */
return (BAD_WINDOW);
}
return (BAD_PAGE);
return (SUCCESS);
}
/*
* pcic_get_socket()
* SocketServices GetSocket
* returns information about the current socket setting
*/
/*ARGSUSED*/
static int
{
int socknum, irq_enabled;
IRQ_ENABLE : 0;
} else {
socket->IRQRouting = 0;
}
return (SUCCESS);
}
/*
* pcic_get_status()
* SocketServices GetStatus
* returns status information about the PC Card in
* the selected socket
*/
/*ARGSUSED*/
static int
{
int socknum, irq_enabled;
IRQ_ENABLE : 0;
} else {
status->IRQRouting = 0;
}
#if defined(PCIC_DEBUG)
if (pcic_debug >= 8)
"SocketState=%x\n",
#endif
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:
if (present_state & PCIC_CB_CARD)
#if defined(PCIC_DEBUG)
if (pcic_debug >= 8)
#endif
break;
default:
break;
}
return (SUCCESS);
}
/*
* pcic_get_window()
* SocketServices GetWindow function
* returns state information about the specified window
*/
/*ARGSUSED*/
static int
{
#if defined(PCIC_DEBUG)
if (pcic_debug) {
}
#endif
return (BAD_WINDOW);
if (win >= PCIC_IOWINDOWS) {
} else {
}
#if defined(PCIC_DEBUG)
if (pcic_debug)
#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
{
int windowbits, iobits;
/* save windows that were on */
if (pcic_reset_time == 0)
return (windowbits);
/* turn all windows off */
#if defined(PCIC_DEBUG)
"pcic_ll_reset(socket %d) powerlevel=%x cbctl 0x%x cbps 0x%x\n",
#endif
if (pcic_vpp_is_vcc_during_reset) {
/*
* Set VPP to VCC for the duration of the reset - for aironet
* card.
*/
} else {
pwr | 1);
}
}
if (pcic_prereset_time > 0) {
}
/* turn interrupts off and start a reset */
"pcic_ll_reset turn interrupts off and start a reset\n");
case PCIC_INTEL_i82092:
break;
default:
break;
} /* switch */
if (pcic_reset_time > 0) {
}
/* take it out of RESET now */
/*
* 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) {
}
if (pcic_vpp_is_vcc_during_reset > 1) {
/*
* Return VPP power to whatever it was before.
*/
} else {
}
}
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
{
int value;
int i, mint;
#if defined(PCIC_DEBUG)
if (pcic_debug >= 8)
#endif
/* Turn off management interupts. */
if (mode == RESET_MODE_FULL) {
/* disable and unmap all mapped windows */
for (i = 0; i < PCIC_NUMWINSOCK; i++) {
if (i < PCIC_IOWINDOWS) {
PCW_MAPPED) {
}
} else {
PCW_MAPPED) {
}
}
}
} else {
/* turn windows back on */
/* wait the rest of the time here */
}
return (SUCCESS);
}
/*
* pcic_set_interrupt()
* SocketServices SetInterrupt function
*/
static int
{
int value = DDI_SUCCESS;
#if defined(PCIC_DEBUG)
if (pcic_debug) {
"pcic_set_interrupt: entered pc_intr_mode=0x%x\n",
pcic->pc_intr_mode);
"\t irq_top=%p handler=%p handler_id=%x\n",
}
#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.
*/
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.
*/
} else {
}
break;
default:
/*
* need to revisit this to see if interrupts can be
* shared someday. Note that IRQ is set in the common
* code.
*/
} else {
}
break;
}
/*
* need to fill in cookies in event of multiple high priority
* interrupt handlers on same IRQ
*/
#if defined(PCIC_DEBUG)
if (pcic_debug) {
"pcic_set_interrupt: exit irq_top=%p value=%d\n",
}
#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
{
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) {
"pcic_clear_interrupt: entered pc_intr_mode=0x%x\n",
pcic->pc_intr_mode);
"\t irq_top=%p handler=%p handler_id=%x\n",
}
#endif
switch (pcic->pc_intr_mode) {
case PCIC_INTR_MODE_PCI_1:
return (BAD_IRQ);
}
handler->handler_id)) {
}
return (BAD_IRQ);
}
} else {
}
break;
default:
if (--pcic_irq_map[i].count == 0) {
/* multi-handler form */
(void) ddi_intr_remove_handler(
pcic->pc_intr_htblp[i]);
#if defined(PCIC_DEBUG)
if (pcic_debug) {
"removing interrupt %d at %s "
"priority\n", i, "high");
"ddi_remove_intr(%p, %x, %p)\n",
(void *)dip,
0,
(void *)intr->iblk_cookie);
}
#endif
}
} else {
} /* if (handler_id) */
} /* while */
}
#if defined(PCIC_DEBUG)
if (pcic_debug) {
"pcic_clear_interrupt: exit irq_top=%p\n",
}
#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
{
int i, value, j;
char buff[256];
char *fmt;
int sval;
sval = 0;
else
if (i & 1) {
fmt = "%s\t%s\t%b\n";
else
fmt = "%s\t%s\t%x\n";
buff[0] = '\0';
} else {
fmt = "\t%s\t%b";
else
fmt = "\t%s\t%x";
buff[j] = ' ';
}
}
if (i & 1) {
fmt = "%s\t%s\t%b\n";
else
fmt = "%s\t%s\t%x\n";
buff[0] = '\0';
} else {
fmt = "\t%s\t%b";
else
fmt = "\t%s\t%x";
buff[j] = ' ';
}
}
}
#if defined(PCIC_DEBUG)
static void
{
int i, value, j;
char buff[256];
char *fmt;
#if defined(PCIC_DEBUG)
if (pcic_debug < 2)
return;
#endif
"----------- PCIC Registers for socket %d---------\n",
socket);
"\tname value name value\n");
if (i & 1) {
fmt = "%s\t%s\t%b\n";
else
fmt = "%s\t%s\t%x\n";
buff[0] = '\0';
} else {
fmt = "\t%s\t%b";
else
fmt = "\t%s\t%x";
buff[j] = ' ';
}
}
case PCIC_CL_PD6710:
case PCIC_CL_PD6722:
case PCIC_CL_PD6729:
case PCIC_CL_PD6832:
break;
}
}
#endif
/*
* pcic_mswait(ms)
* sleep ms milliseconds
* call drv_usecwait once for each ms
*/
static void
{
if (ms) {
}
}
/*
* 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
{
if ((intstate & PCIC_RESET) &&
return (B_TRUE);
#ifdef PCIC_DEBUG
if (pcic_debug) {
pcic_debug += 4;
pcic_debug -= 4;
}
#endif
return (B_FALSE);
}
/*
*/
static int
{
int val;
switch (pcic->pc_io_type) {
case PCIC_IO_TYPE_YENTA:
break;
default:
break;
}
return (val);
}
static void
{
switch (pcic->pc_io_type) {
case PCIC_IO_TYPE_YENTA:
break;
default:
break;
}
}
/*
* Misc PCI functions
*/
static void
{
unsigned cmd;
return;
if (flags & PCIC_ENABLE_IO)
cmd |= PCI_COMM_IO;
if (flags & PCIC_ENABLE_MEM)
cmd |= PCI_COMM_MAE;
} /* if (PCIC_ENABLE_IO | PCIC_ENABLE_MEM) */
}
/*
* pcic_find_pci_type - Find and return PCI-PCMCIA adapter type
*/
static int
{
"vendor-id", -1);
"device-id", -1);
switch (device) {
case PCIC_INTEL_i82092:
break;
case PCIC_CL_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.
*/
0) {
}
break;
case PCIC_CL_PD6730:
break;
case PCIC_CL_PD6832:
break;
case PCIC_SMC_34C90:
break;
case PCIC_TOSHIBA_TOPIC95:
break;
case PCIC_TOSHIBA_TOPIC100:
break;
case PCIC_TI_PCI1031:
break;
case PCIC_TI_PCI1130:
break;
case PCIC_TI_PCI1131:
break;
case PCIC_TI_PCI1250:
break;
case PCIC_TI_PCI1225:
break;
case PCIC_TI_PCI1410:
break;
case PCIC_TI_PCI1510:
break;
case PCIC_TI_PCI1520:
break;
case PCIC_TI_PCI1221:
break;
case PCIC_TI_PCI1050:
break;
case PCIC_ENE_1410:
break;
case PCIC_O2_OZ6912:
break;
case PCIC_RICOH_RL5C466:
break;
case PCIC_TI_PCI1420:
break;
case PCIC_ENE_1420:
break;
default:
case PCIC_TOSHIBA_VENDOR:
break;
case PCIC_TI_VENDOR:
break;
case PCIC_O2MICRO_VENDOR:
break;
case PCIC_RICOH_VENDOR:
break;
default:
return (DDI_FAILURE);
break;
}
}
return (DDI_SUCCESS);
}
static void
{
if (intr == PCIC_82092_CTL_SMI) {
} else {
}
ppirr);
}
static uint_t
{
if (sockp->pcs_cd_softint_flg) {
sockp->pcs_cd_softint_flg = 0;
}
return (rc);
}
#ifdef CARDBUS
static uint32_t pcic_cbps_on = 0;
#else
#endif
static void
{
/*
* Always reset debounce but may need to check original state later.
*/
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%d handle_cd_change: socket %d card status 0x%x"
#endif
switch (status & PCIC_ISTAT_CD_MASK) {
case PCIC_CD_PRESENT_OK:
#ifdef PCIC_DEBUG
#endif
#ifdef PCIC_DEBUG
#endif
/*
* Check the CB bits are sane.
*/
cbps & pcic_cbps_off) {
"%s%d: Odd Cardbus Present State 0x%x\n",
cbps);
debounce = 0;
}
if (debounce) {
if (pcic_do_insertion) {
if (cbps & CB_PS_16BITCARD) {
/* calls pcm_adapter_callback() */
if (pcic->pc_callback) {
(void) ddi_prop_update_string(DDI_DEV_T_NONE,
"pccard");
sockp->pcs_socket);
}
} else if (cbps & CB_PS_CBCARD) {
#ifdef CARDBUS
}
#else
"32 bit Cardbus not supported in"
" this device driver\n");
#endif
} else {
/*
* Ignore the card
*/
"32 bit Cardbus not supported on this"
" device\n");
}
} else {
"Unsupported PCMCIA card inserted\n");
}
}
} else {
}
} 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%d: Odd card insertion indication on socket %d\n",
sockp->pcs_socket);
#endif
}
break;
default:
/*
* Someone has started to insert a card so delay a while.
*/
break;
}
/*
* Otherwise this is basically the same as not present
* so fall through.
*/
/* FALLTHRU */
case 0:
} else {
}
#ifdef PCIC_DEBUG
#endif
}
}
}
}
/*
* 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_dump_all(pcic); */
#endif
}
}
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
"socket %d.%d\n",
sockp->pcs_socket);
#endif
/*
* 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_CD_DETECT;
/* Out from debouncing state */
}
/*
* pcic_getb()
* get an I/O byte based on the yardware decode method
*/
static uint8_t
{
int work;
#if defined(PCIC_DEBUG)
if (pcic_debug == 0x7fff) {
}
#endif
switch (pcic->pc_io_type) {
case PCIC_IO_TYPE_YENTA:
default:
}
}
static void
{
int work;
#if defined(PCIC_DEBUG)
if (pcic_debug == 0x7fff) {
"pcic_putb0: pcic=%p socket=%d reg=%d value=%x \n",
"pcic_putb1: type=%d handle=%p ioaddr=%p \n",
}
#endif
switch (pcic->pc_io_type) {
case PCIC_IO_TYPE_YENTA:
value);
break;
default:
break;
}
}
/*
* chip identification functions
*/
/*
* chip identification: Cirrus Logic PD6710/6720/6722
*/
static int
{
/* Init the CL id mode */
(value2 & PCIC_CI_ID) == 0) {
/* chip is a Cirrus Logic and not Intel */
if (value1 & PCIC_CI_SLOTS)
else
/* now fine tune things just in case a 6722 */
if (value1 == 0) {
if (value1 == 0x55) {
}
}
return (1);
}
return (0);
}
/*
* chip identification: Vadem (VG365/465/468/469)
*/
static void
{
}
static int
{
int value;
(value | PCIC_VADEM_D3) ||
~(PCIC_V_UNLOCK | PCIC_V_VADEMREV);
/* want to lock but leave mouse or other on */
switch (value & PCIC_REV_MASK) {
case PCIC_VADEM_365:
break;
case PCIC_VADEM_465:
break;
case PCIC_VADEM_468:
break;
case PCIC_VADEM_469:
break;
}
return (1);
}
return (0);
}
/*
* chip identification: Ricoh
*/
static int
{
int value;
switch (value) {
case PCIC_RF_296:
return (1);
case PCIC_RF_396:
return (1);
}
return (0);
}
/*
* set up available address spaces in busra
*/
static void
{
/*
* need to translate address space entries from pcmcia
* format to pci format
*/
KM_SLEEP);
entries) == DDI_SUCCESS)
entries);
return;
}
/*
* "legacy" platforms will have "available" property in pci node
*/
/* (void) pci_resource_setup(pdip); */
break;
}
}
int len;
"?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.
*/
"device_type",
break;
}
} else {
#ifdef CARDBUS
int k;
else {
"!No spare PCI bus numbers, range is "
"%u->%u, cardbus isn't usable\n",
}
} else
"have been set up\n");
#endif
/*
* Have a valid parent with the "available" property
*/
(void) pci_resource_setup(pdip);
}
"assigned-addresses",
/*
* 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.
*/
}
{
}
}
}
/*
* translate "available" from pcmcia format to pci format
*/
static int
{
int i, range_len, range_entries;
"no ranges property for pcmcia\n");
return (DDI_FAILURE);
}
/* for each "available" entry to be translated */
int j;
/* for each "ranges" entry to be searched */
for (j = 0; j < range_entries; j++, range_p++) {
continue;
pci_p->pci_size_hi = 0;
}
}
return (DDI_SUCCESS);
}
static int
{
#ifdef CARDBUS
if (cardbus_is_cb_minor(*dev))
#endif
return (EINVAL);
}
static int
{
#ifdef CARDBUS
if (cardbus_is_cb_minor(dev))
#endif
return (EINVAL);
}
static int
int *rval)
{
#ifdef CARDBUS
if (cardbus_is_cb_minor(dev))
#endif
return (EINVAL);
}
static boolean_t
{
set_socket_t s;
get_socket_t g;
unsigned vccLevel;
bzero(&s, sizeof (set_socket_t));
s.IFType = IF_CARDBUS;
s.State = (unsigned)~0;
if (present_state & PCIC_VCC_3VCARD)
s.VccLevel = PCIC_VCC_3VLEVEL;
else if (present_state & PCIC_VCC_5VCARD)
s.VccLevel = PCIC_VCC_5VLEVEL;
else {
"pcic_load_cardbus: unsupported card voltage\n");
goto failure;
}
goto failure;
goto failure;
bzero(&g, sizeof (get_socket_t));
goto failure;
bzero(&s, sizeof (set_socket_t));
s.IREQRouting = g.IRQRouting;
s.State = (unsigned)~0;
goto failure;
goto exit;
exit:
return (retval);
}
static void
{
set_socket_t s;
bzero(&s, sizeof (set_socket_t));
s.IREQRouting = 0;
s.CtlInd = 0;
s.State = 0;
(void) pcic_set_socket(dip, &s);
}
static uint32_t
{
}
static void
{
}
static void
{
case PCIC_INTEL_i82092:
break;
case PCIC_O2_OZ6912:
value |= 0x8;
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 */
"pcic_enable_io_intr brdgctl(0x%x) was: 0x%x\n",
brdgctl);
/* Flush the write */
break;
default:
break;
}
}
static void
{
case PCIC_INTEL_i82092:
break;
case PCIC_O2_OZ6912:
value &= ~0x8;
/* Flush the write */
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.
*/
"pcic_disable_io_intr brdgctl(0x%x) was: 0x%x\n",
brdgctl);
/* Flush the write */
break;
default:
break;
}
}
static void
{
}
static void
{
pcic_disable_io_intr(pcic, 0);
}
static int
{
int nerr = 0;
if (e & PCI_STAT_PERROR) {
nerr++;
}
if (e & PCI_STAT_S_SYSERR) {
nerr++;
if (bridge_secondary)
else
}
if (e & PCI_STAT_R_MAST_AB) {
nerr++;
}
if (e & PCI_STAT_R_TARG_AB)
if (e & PCI_STAT_S_TARG_AB)
if (e & PCI_STAT_S_PERROR) {
nerr++;
}
return (nerr);
}
#if defined(__sparc)
static int
{
char nm[24];
int nerr = 0;
switch (op) {
case FAULT_LOG:
return (nerr);
case FAULT_POKEFINI:
case FAULT_RESET:
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
{
int i, interrupt;
#if defined(PCIC_DEBUG)
#endif
for (i = 0; i < pcic->pc_numsockets; i++) {
/* Enable interrupts on PCI if needs be */
PCIC_RESET | interrupt);
}
if (pcic_do_pcmcia_sr)
/*
* 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);
}
}
}
static void
{
#ifdef PCIC_DEBUG
"pcic_debounce(0x%p, dip=0x%p) socket %d st 0x%x "
"chg 0x%x flg 0x%x\n",
#endif
}
static void
{
callb_generic_cpr, "pcic debounce thread");
while (pcic_deb_threadid) {
while (pcic_deb_queue) {
#ifdef PCIC_DEBUG
pcic_dump_debqueue("Thread");
#endif
} else {
(void) cv_timedwait(&pcic_deb_cv,
}
}
}
thread_exit();
}
static void *
{
while (*dbpp) {
else
break;
}
#ifdef PCIC_DEBUG
pcic_dump_debqueue("Add");
#endif
return (dbp);
}
static void
pcic_rm_debqueue(void *id)
{
while (*dbpp) {
#ifdef PCIC_DEBUG
pcic_dump_debqueue("Remove");
#endif
return;
}
}
}
static int pcic_powerdelay = 0;
static int
{
/* power setup -- if necessary */
#if defined(PCIC_DEBUG)
"pcic_exca_powerctl(socket %d) powerlevel=%x orig 0x%x\n",
#endif
/* Preserve the PCIC_OUTPUT_ENABLE (control lines output enable) bit. */
if (powerlevel != orig_pwrctl) {
if (powerlevel & ~POWER_OUTPUT_ENABLE) {
int ifs;
/*
* set power to socket
* note that the powerlevel was calculated earlier
*/
/*
* 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).
*/
#if defined(PCIC_DEBUG)
"\tpowerlevel reg = %x (ifs %x)\n",
"CBus regs: PS 0x%x, Control 0x%x\n",
#endif
/*
* since power was touched, make sure it says it
* is on. This lets it become stable.
*/
if (ifs & PCIC_POWER_ON)
break;
else {
PCIC_POWER_CONTROL, 0);
if (ind == 10) {
}
}
}
if (!(ifs & PCIC_POWER_ON)) {
"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",
ind);
return (BAD_VCC);
}
#if defined(PCIC_DEBUG)
"\tind = %d, if status %x pwrc 0x%x "
"misc1 0x%x igc 0x%x\n",
#endif
} else {
/* explicitly turned off the power */
}
}
return (SUCCESS);
}
static int pcic_cbdoreset_during_poweron = 1;
static int
{
#if defined(PCIC_DEBUG)
"pcic_cbus_powerctl(socket %d) vcc %d vpp1 %d "
"cbctl 0x%x->0x%x\n",
#endif
if (cbctl != orig_cbctl) {
}
return (SUCCESS);
}
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.
*/
/* break; */
}
if (cbctl & CB_PS_BADVCC) {
break;
}
#if defined(PCIC_DEBUG)
"cbstev = 0x%x cbps = 0x%x cbctl 0x%x(0x%x)",
cbctl, orig_cbctl);
#endif
}
return (SUCCESS);
}
}
}
"cbstev = 0x%x cbps = 0x%x cbctl 0x%x(0x%x) "
return (BAD_VCC);
}
return (SUCCESS);
}
static int pcic_do_pprintf = 0;
static void
pcic_dump_debqueue(char *msg)
{
while (debp) {
}
}
/* PRINTFLIKE3 */
static void
{
int instance;
char buf[256];
const char *name;
#if !defined(PCIC_DEBUG)
int ce;
char qmark = 0;
if (level <= 3)
else
if (level == 4)
qmark = 1;
#endif
if (dip) {
/* name = ddi_binding_name(dip); */
} else {
instance = 0;
name = "";
}
#if defined(PCIC_DEBUG)
if (pcic_do_pprintf) {
if (dip) {
if (instance >= 0)
else
prom_printf("%s,0x%p: %s",
} else
} else {
if (dip) {
if (instance >= 0)
else
} else
}
#else
if (dip)
else
#endif
}
}