pcmcia.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"
/*
* PCMCIA NEXUS
* The PCMCIA module is a generalized interface for
* implementing PCMCIA nexus drivers. It preserves
* the logical socket name space while allowing multiple
* instances of the hardware to be properly represented
* in the device tree.
*
* The nexus also exports events to an event manager
* driver if it has registered.
*/
#include <sys/autoconf.h>
#include <sys/sservice.h>
#ifdef sparc
#include <sys/ddi_subrdefs.h>
#include <sys/mach_intr.h>
#endif
/* some bus specific stuff */
/* need PCI regspec size for worst case at present */
typedef struct pcmcia_logical_socket {
int ls_socket; /* adapter's socket number */
struct pcmcia_adapter *ls_adapter;
int ls_functions;
int ls_intrrefs;
int ls_error; /* error for CS return */
/*
* entry points used by the true nexus
*/
int, char *, caddr_t, int *);
/*
* prototypes used internally by the nexus and sometimes Card Services
*/
int SocketServices(int function, ...);
extern void *(*cis_parser)(int, ...);
ra_return_t *);
static int (*pcmcia_card_services)(int, ...) = NULL;
/*
* that the nexus common code maintains.
*/
int pcmcia_num_adapters;
int pcmcia_num_sockets;
int pcmcia_num_windows;
int pcmcia_num_power;
/*
* Mapping of the device "type" to names acceptable to
* the DDI
*/
static char *pcmcia_dev_type[] = {
"multifunction",
"byte",
"serial",
"parallel",
"block",
"display",
"network",
"block",
"byte"
};
char *pcmcia_default_pm_mode = "parental-suspend-resume";
/*
* generic names from the approved list:
* disk tape pci sbus scsi token-ring isa keyboard display mouse
* audio ethernet timer memory parallel serial rtc nvram scanner
* floppy(controller) fddi isdn atm ide pccard video-in video-out
* in some cases there will need to be device class dependent names.
* network -> ethernet, token-ring, etc.
* this list is a first guess and is used when all else fails.
*/
char *pcmcia_generic_names[] = {
"multifunction",
"memory",
"serial",
"parallel",
"disk",
"video", /* no spec for video-out yet */
"network",
"aims",
"scsi",
"security"
};
#define PCM_GENNAME_SIZE (sizeof (pcmcia_generic_names) / \
sizeof (char *))
#define PCMCIA_MAP_IO 0x0
#define PCMCIA_MAP_MEM 0x1
/*
* The following should be 2^^n - 1
*/
#define PCMCIA_SOCKET_BITS 0x7f
#ifdef PCMCIA_DEBUG
int pcmcia_debug = 0x0;
static void pcmcia_dump_minors(dev_info_t *);
#endif
int pcmcia_timer_id;
/*
* XXX - See comments in cs.c
*/
extern struct pc_socket_services pc_socket_services;
/* some function declarations */
static int pcm_adapter_callback(dev_info_t *, int, int, int);
extern void pcmcia_find_cards(anp_t *);
extern void pcmcia_merge_power(struct power_entry *);
extern void pcmcia_do_resume(int, pcmcia_logical_socket_t *);
extern void pcmcia_resume(int, pcmcia_logical_socket_t *);
extern void pcmcia_do_suspend(int, pcmcia_logical_socket_t *);
extern void pcm_event_manager(int, int, void *);
static void pcmcia_create_dev_info(int);
static int pcmcia_create_device(ss_make_device_node_t *);
void pcmcia_fix_string(char *str);
static int pcmcia_merge_conf(dev_info_t *);
void pcmcia_free_resources(dev_info_t *);
int pcmcia_get_intr(dev_info_t *, int);
int pcmcia_return_intr(dev_info_t *, int);
dev_info_t **);
extern int cs_init(void);
extern int cs_deinit(void);
extern void cisp_init(void);
extern void cis_deinit(void);
/*
* non-DDI compliant functions are listed here
* some will be declared while others that have
* entries in .h files. All will be commented on.
*
* with declarations:
* ddi_add_child
* ddi_binding_name
* ddi_bus_prop_op
* ddi_ctlops
* ddi_find_devinfo
* ddi_get_name_addr
* ddi_get_parent_data
* ddi_hold_installed_driver
* ddi_name_to_major
* ddi_node_name
* ddi_pathname
* ddi_rele_driver
* ddi_set_name_addr
* ddi_set_parent_data
* ddi_unorphan_devs
* i_ddi_bind_node_to_driver
* i_ddi_bind_node_to_driver
* i_ddi_bus_map
* i_ddi_map_fault
* i_ddi_mem_alloc
* i_ddi_mem_alloc
* i_ddi_mem_free
* i_ddi_mem_free
* modload
* modunload
*/
extern void ddi_unorphan_devs(major_t);
/* Card&Socket Services entry points */
static int GetCookiesAndDip(sservice_t *);
static int SSGetAdapter(get_adapter_t *);
static int SSGetPage(get_page_t *);
static int SSGetSocket(get_socket_t *);
static int SSGetStatus(get_ss_status_t *);
static int SSGetWindow(get_window_t *);
static int SSInquireAdapter(inquire_adapter_t *);
static int SSInquireSocket(inquire_socket_t *);
static int SSInquireWindow(inquire_window_t *);
static int SSResetSocket(int, int);
static int SSSetPage(set_page_t *);
static int SSSetSocket(set_socket_t *);
static int SSSetWindow(set_window_t *);
static int SSSetIRQHandler(set_irq_handler_t *);
static int SSClearIRQHandler(clear_irq_handler_t *);
&mod_miscops, /* Type of module. This one is a driver */
"PCMCIA Nexus Support %I%", /* Name of the module. */
};
static struct modlinkage modlinkage = {
};
int
_init()
{
int ret;
cisp_init();
if (cs_init() != CS_SUCCESS) {
if (cs_deinit() != CS_SUCCESS)
return (-1);
}
}
return (ret);
}
int
_fini()
{
int ret;
cis_deinit();
if (cs_deinit() != CS_SUCCESS) {
}
}
return (ret);
}
int
{
}
extern pri_t minclsyspri;
/*
* pcmcia_attach()
* the attach routine must make sure that everything needed is present
* including real hardware. The sequence of events is:
* attempt to load all adapter drivers
* attempt to load Card Services (which _depends_on pcmcia)
* initialize logical sockets
* report the nexus exists
*/
int
{
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug) {
}
#endif
pcmcia_dip = dip;
if (pcmcia_num_adapters == 0) {
cis_parser = (void *(*)(int, ...)) CISParser;
/* tell CS we are up with basic init level */
}
PCM_DEVICETYPE, "pccard");
/*
* now setup any power management stuff necessary.
* we do it here in order to ensure that all PC Card nexi
* implement it.
*/
} else {
}
/*
* setup the info necessary for Card Services/SocketServices
* and notify CS when ready.
*/
/* exit mutex so CS can run for any cards found */
/*
* make sure the devices are identified before
* returning. We do this by checking each socket to see if
* a card is present. If there is one, and there isn't a dip,
* we can't be done. We scan the list of sockets doing the
* check. if we aren't done, wait for a condition variable to
* wakeup.
* Because we can miss a wakeup and because things can
* take time, we do eventually give up and have a timeout.
*/
count++) {
done = 1;
/* block CS while checking so we don't miss anything */
for (i = 0; i < pcmcia_num_sockets; i++) {
if (pcmcia_sockets[i] == NULL)
continue;
done = 0;
}
}
}
/* only wait if we aren't done with this set */
if (!done) {
}
}
return (DDI_SUCCESS);
}
/*
* pcmcia_detach
* unload everything and then detach the nexus
*/
/* ARGSUSED */
int
{
switch (cmd) {
case DDI_DETACH:
return (DDI_SUCCESS);
/*
* resume from a checkpoint
* We don't do anything special here since the adapter
* driver will generate resume events that we intercept
* and convert to insert events.
*/
case DDI_SUSPEND:
case DDI_PM_SUSPEND:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
}
/*
* card_services_error()
* used to make 2.4/2.5 drivers get an error when
* they try to initialize.
*/
static int
{
return (CS_BAD_VERSION);
}
static int (*cs_error_ptr)() = card_services_error;
/*
* pcmcia_ctlops
* handle the nexus control operations for the cases where
* a PC Card driver gets called and we need to modify the
* devinfo structure or otherwise do bus specific operations
*/
int
{
int e;
char name[64];
struct pcmcia_parent_private *ppd;
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug) {
(void *)result);
}
#endif
switch (ctlop) {
case DDI_CTLOPS_REPORTDEV:
if (rdip == (dev_info_t *)0)
return (DDI_FAILURE);
else
PCM_DEV_SOCKET, -1)));
return (DDI_SUCCESS);
case DDI_CTLOPS_INITCHILD:
/*
* we get control here before the child is called.
* we can change things if necessary. This is where
* the CardServices hook gets planted.
*/
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug) {
(void *)arg);
}
#endif
ppd = (struct pcmcia_parent_private *)
return (DDI_FAILURE);
return (DDI_FAILURE);
return (DDI_SUCCESS);
}
/*
* We don't want driver.conf files that stay in
* pseudo device form. It is acceptable to have
* .conf files add properties only.
*/
return (DDI_FAILURE);
}
#if defined(PCMCIA_DEBUG)
" function=%x, active=%x, flags=%x\n",
}
#endif
/*
* make sure names are relative to socket number
*/
if (ppd->ppd_function > 0) {
int sock;
int func;
} else {
}
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug)
"nodeid: %x @%s\n",
if (pcmcia_debug > 1)
#endif
return (DDI_SUCCESS);
case DDI_CTLOPS_UNINITCHILD:
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug) {
(void *)arg);
}
#endif
return (DDI_SUCCESS);
case DDI_CTLOPS_SLAVEONLY:
/* PCMCIA devices can't ever be busmaster until CardBus */
ppd = (struct pcmcia_parent_private *)
return (DDI_FAILURE); /* at most */
return (DDI_SUCCESS);
case DDI_CTLOPS_SIDDEV:
/* in general this is true. */
return (DDI_SUCCESS);
case DDI_CTLOPS_NREGS:
ppd = (struct pcmcia_parent_private *)
else
return (DDI_SUCCESS);
case DDI_CTLOPS_REGSIZE:
ppd = (struct pcmcia_parent_private *)
else
return (DDI_SUCCESS);
case DDI_CTLOPS_POWER:
ppd = (struct pcmcia_parent_private *)
return (DDI_FAILURE);
/*
* if this is not present, don't bother (claim success)
* since it is already in the right state. Don't
* do any resume either since the card insertion will
* happen independently.
*/
if (!ppd->ppd_active)
return (DDI_SUCCESS);
for (e = 0; e < pcmcia_num_adapters; e++)
if (pcmcia_adapters[e] ==
break;
if (e == pcmcia_num_adapters)
return (DDI_FAILURE);
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug) {
}
#endif
e = ppd->ppd_socket;
switch (pm->request_type) {
case PMR_SUSPEND:
if (!(pcmcia_sockets[e]->ls_flags &
PCS_SUSPENDED)) {
pcmcia_sockets[e]);
}
return (DDI_SUCCESS);
case PMR_RESUME:
/* for now, we just succeed since the rest is done */
return (DDI_SUCCESS);
case PMR_SET_POWER:
/*
* not sure how to handle power control
* for now, we let the child handle it itself
*/
break;
default:
break;
}
return (DDI_FAILURE);
/* These CTLOPS will need to be implemented for new form */
/* let CardServices know about this */
case DDI_CTLOPS_DETACH:
return (DDI_SUCCESS);
case DDI_CTLOPS_ATTACH:
return (DDI_SUCCESS);
default:
/* if we don't understand, pass up the tree */
/* most things default to general ops */
}
}
struct pcmcia_props {
char *name;
int len;
int prop;
} pcmcia_internal_props[] = {
{ PCM_DEV_ACTIVE, 0, PCMCIA_PROP_ACTIVE },
{ PCM_DEV_R2TYPE, 0, PCMCIA_PROP_R2TYPE },
{ PCM_DEV_CARDBUS, 0, PCMCIA_PROP_CARDBUS },
{ CS_PROP, sizeof (void *), PCMCIA_PROP_OLDCS },
{ "reg", 0, PCMCIA_PROP_REG },
{ "interrupts", sizeof (int), PCMCIA_PROP_INTR },
{ "pm-hardware-state", 0, PCMCIA_PROP_DEFAULT_PM },
};
/*
* pcmcia_prop_decode(name)
* decode the name and determine if this is a property
* we construct on the fly, one we have on the prop list
* or one that requires calling the CIS code.
*/
static int
pcmcia_prop_decode(char *name)
{
int i;
return (PCMCIA_PROP_CIS);
for (i = 0; i < (sizeof (pcmcia_internal_props) /
sizeof (struct pcmcia_props)); i++) {
return (i);
}
return (PCMCIA_PROP_UNKNOWN);
}
/*
* pcmcia_prop_op()
* we don't have properties in PROM per se so look for them
* only in the devinfo node. Future may allow us to find
* certain CIS tuples via this interface if a user asks for
* a property of the form "cistpl-<tuplename>" but not yet.
*
* The addition of 1275 properties adds to the necessity.
*/
int
{
struct pcmcia_parent_private *ppd;
default:
return (DDI_PROP_NOT_FOUND);
/* note that proplen may get modified */
case PCMCIA_PROP_DEFAULT_PM:
break;
case PCMCIA_PROP_OLDCS:
break;
case PCMCIA_PROP_REG:
break;
case PCMCIA_PROP_INTR:
break;
/* the next set are boolean values */
case PCMCIA_PROP_ACTIVE:
if (!ppd->ppd_active) {
return (DDI_PROP_NOT_FOUND);
}
break;
case PCMCIA_PROP_R2TYPE:
return (DDI_PROP_NOT_FOUND);
break;
case PCMCIA_PROP_CARDBUS:
return (DDI_PROP_NOT_FOUND);
break;
}
break;
case PCMCIA_PROP_CIS:
/*
* once we have the lookup code in place
* it is sufficient to break out of the switch
* once proplen and propptr are set.
* The common prop_op code deals with the rest.
*/
case PCMCIA_PROP_UNKNOWN:
}
/* just the length */
return (DDI_PROP_SUCCESS);
}
switch (prop_op) {
case PROP_LEN_AND_VAL_ALLOC:
if (mod_flags & DDI_PROP_CANSLEEP)
else
flags = KM_NOSLEEP;
return (DDI_PROP_NO_MEMORY);
break;
case PROP_LEN_AND_VAL_BUF:
return (DDI_PROP_BUF_TOO_SMALL);
break;
default:
break;
}
if (proplen > 0)
return (DDI_PROP_SUCCESS);
}
struct regspec *
{
struct pcmcia_parent_private *ppd;
return (NULL);
}
struct regspec *
{
struct pcmcia_parent_private *ppd;
return (NULL);
return (NULL);
return (NULL);
else
}
int
{
struct pcmcia_parent_private *ppd;
int i;
return (-1);
return (i);
}
return (i);
}
return (-1);
}
int
{
/* check for register number */
case DDI_MT_REGSPEC:
/*
* when using regspec, must not be relocatable
* and should be from assigned space.
*/
return (DDI_FAILURE);
break;
case DDI_MT_RNUMBER:
return (DDI_FAILURE);
break;
default:
return (DDI_ME_INVAL);
}
/* basic sanity checks */
default:
return (DDI_ME_UNIMPLEMENTED);
case DDI_MO_UNMAP:
return (DDI_FAILURE);
break;
case DDI_MO_MAP_LOCKED:
case DDI_MO_MAP_HANDLE:
panic("unsupported bus operation");
/*NOTREACHED*/
}
/*
* we need a private copy for manipulation and
* calculation of the correct ranges
*/
/*
* for now this is an error. What does it really mean
* to ask for an offset from an address that hasn't
* been allocated yet.
*/
return (DDI_ME_INVAL);
}
if (len != 0) {
return (DDI_ME_INVAL);
}
}
/*
* basic sanity is checked so now make sure
* we can actually allocate something for this
* request and then convert to a "standard"
*/
case PC_REG_SPACE_IO:
break;
case PC_REG_SPACE_MEMORY:
break;
default:
/* not a valid register type */
return (DDI_FAILURE);
}
ret.ra_addr_hi = 0;
(check == PCA_RES_NEED_IO) ?
case DDI_MO_UNMAP:
break;
default:
break;
}
}
/*
* pcmcia_cons_regspec()
* based on parent's bus type, construct a regspec that is usable
* by that parent to map the resource into the system.
*/
#define PTYPE_PCI 1
#define PTYPE_ISA 0
struct regspec *
{
if (pdip != ddi_root_node()) {
/* we're not a child of root so find out what */
len = sizeof (device_type);
/* check things out */
}
}
switch (ptype) {
case PTYPE_PCI:
/* XXX need to look at carefully */
} else {
bus = 0;
}
if (type == PCMCIA_MAP_IO)
pcireg->pci_size_hi = 0;
break;
default:
/* default case is to use struct regspec */
break;
}
}
/*
* pcmcia_init_adapter
* Initialize the per-adapter structures and check to see if
* there are possible other instances coming.
*/
void
{
int i, n;
i = pcmcia_num_adapters++;
KM_SLEEP);
/* should this be pca_winshift??? */
pcmcia_adapters[i]->pca_number = i;
pcmcia_adapters[i]->
/* resources - assume worst case and fix from there */
/* indicate first socket not initialized */
/* need to know interrupt limitations */
} else
/* power entries for adapter */
pcmcia_adapters[i]->pca_power =
pcmcia_adapters[i]->pca_numpower =
/* now setup the per socket info */
sock++) {
n = sock + pcmcia_num_sockets;
else {
n = ddi_get_instance(sockdrv);
}
/* make sure we know first socket on adapter */
pcmcia_adapters[i]->pca_first_socket = n;
/*
* the number of sockets is weird.
* we might have only two sockets but
* due to persistence of instances we
* will need to call them something other
* than 0 and 1. So, we use the largest
* instance number as the number and
* have some that just don't get used.
*/
if (n >= pcmcia_num_sockets)
pcmcia_num_sockets = n + 1;
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug) {
"pcmcia_init: new socket added %d "
"(%d)\n",
n, pcmcia_num_sockets);
}
#endif
pcmcia_sockets[n] =
kmem_zalloc(sizeof (pcmcia_logical_socket_t),
KM_SLEEP);
pcmcia_sockets[n]->ls_adapter =
pcmcia_adapters[i];
pcmcia_sockets[n]->ls_cs_events = 0L;
/* Prototype of intrspec */
pcmcia_sockets[n]->ls_intr_pri =
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug)
"phys sock %d, log sock %d\n",
sock, n);
#endif
}
/* now setup the per window information */
n = win + pcmcia_num_windows;
pcmcia_windows[n] =
kmem_zalloc(sizeof (pcmcia_logical_window_t),
KM_SLEEP);
pcmcia_windows[n]->lw_adapter =
pcmcia_adapters[i];
}
pcm_adapter_callback, i);
/* now tell CS about each socket */
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug) {
"pcmcia_init: notify CS socket %d "
"sockp=%p\n",
}
#endif
PCS_SOCKET_ADDED)) {
/* skip the ones that are done already */
continue;
}
CS_SUCCESS) {
/* flag socket as broken */
} else {
}
}
}
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug) {
for (i = 0; i < pcmcia_num_sockets; i++) {
if (pcmcia_sockets[i] == NULL)
continue;
"\t%d: phys sock=%d, if=%p, adapt=%p\n",
i, pcmcia_sockets[i]->ls_socket,
(void *)pcmcia_sockets[i]->ls_if,
(void *)pcmcia_sockets[i]->ls_adapter);
}
for (i = 0; i < pcmcia_num_windows; i++) {
"\t%d: phys_window=%d, if=%p, adapt=%p\n",
i, pcmcia_windows[i]->lw_window,
(void *)pcmcia_windows[i]->lw_if,
(void *)pcmcia_windows[i]->lw_adapter);
}
for (n = 0; n < pcmcia_num_power; n++)
"\t\tPowerLevel: %d\tValidSignals: %x\n",
}
#endif
}
/*
* pcmcia_find_cards()
* check the adapter to see if there are cards present at
* driver attach time. If there are, generate an artificial
* card insertion event to get CS running and the PC Card ultimately
* identified.
*/
void
{
int i;
for (i = 0; i < pcmcia_num_sockets; i++) {
if (pcmcia_sockets[i] &&
/* check the status */
(void) cs_event(PCE_CARD_INSERT, i, 0);
delay(1);
}
}
}
}
/*
* pcmcia_number_socket(dip, adapt)
* we determine socket number by creating a driver for each
* socket on the adapter and then forcing it to attach. This
* results in an instance being assigned which becomes the
* logical socket number. If it fails, then we are the first
* set of sockets and renumbering occurs later. We do this
* one socket at a time and return the dev_info_t so the
* instance number can be used.
*/
{
struct pcmcia_parent_private *ppd;
&child) == NDI_SUCCESS) {
KM_SLEEP);
(void) ndi_devi_free(child);
}
}
return (child);
}
/*
* pcm_phys_to_log_socket()
* from an adapter and socket number return the logical socket
*/
int
{
register pcmcia_logical_socket_t *sockp;
int i;
for (i = 0, sockp = pcmcia_sockets[0];
continue;
break;
}
if (i >= pcmcia_num_sockets) {
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug)
#endif
return (-1);
}
return (i); /* want logical socket */
}
/*
* pcm_adapter_callback()
* this function is called back by the adapter driver at interrupt time.
* It is here that events should get generated for the event manager if it
* is present. It would also be the time where a device information
* tree could be constructed for a card that was added in if we
* choose to create them dynamically.
*/
#if defined(PCMCIA_DEBUG)
char *cblist[] = {
"removal",
"insert",
"ready",
"battery-warn",
"battery-dead",
"status-change",
"write-protect", "reset", "unlock", "client-info", "eject-complete",
"eject-request", "erase-complete", "exclusive-complete",
"exclusive-request", "insert-complete", "insert-request",
"reset-complete", "reset-request", "timer-expired",
"resume", "suspend"
};
#endif
/*ARGSUSED*/
static int
{
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug) {
}
#endif
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug)
#endif
return (1);
}
/* get the logical socket since that is what CS knows */
if (socket == -1) {
return (0);
}
switch (event) {
case -1: /* special case of adapter going away */
case PCE_CARD_INSERT:
break;
case PCE_CARD_REMOVAL:
/* disable interrupts at this point */
/* remove children that never attached */
break;
case PCE_PM_RESUME:
/* event = PCE_CARD_INSERT; */
break;
case PCE_PM_SUSPEND:
/* event = PCE_CARD_REMOVAL; */
break;
default:
/* nothing to do */
break;
}
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug) {
"\tevent %d, event mask=%x, match=%x (log socket=%d)\n",
(int)sockp->ls_cs_events,
}
#endif
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug)
"with event=%d\n",
(void *)pcmcia_cs_event, event);
#endif
}
/* let the event manager(s) know about the event */
return (0);
}
/*
* pcm_event_manager()
* checks for registered management driver callback handlers
* if there are any, call them if the event warrants it
*/
void
{
struct pcmcia_mif *mif;
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug)
"pcm_event_manager: event=%d, mif_events=%x"
" (tst:%d)\n",
#endif
}
}
}
/*
* pcm_search_devinfo(dev_info_t *, pcm_device_info *, int)
* search for an immediate child node to the nexus and not siblings of nexus
* and not grandchildren. We follow the same sequence that name binding
* follows so we match same class of device (modem == modem) and don't
* have to depend on features that might not exist.
*/
{
char bf[256];
struct pcmcia_parent_private *ppd;
int circular;
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug)
"pcm_search_devinfo: socket=%x [%s|%s|%s] pd_flags=%x\n",
#endif
/* do searches in compatible property order */
int ppd_socket;
ppd = (struct pcmcia_parent_private *)
#if defined(PCMCIA_DEBUG)
#endif
continue;
}
ppd->ppd_function);
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug) {
}
#endif
socket == ppd_socket)
break;
}
(PCM_NAME_1275 | PCM_MULTI_FUNCTION)) {
info->pd_function);
break;
}
info->pd_bind_name) == 0 &&
socket == ppd_socket)
break;
}
socket == ppd_socket)
break;
}
info->pd_generic_name) == 0 &&
socket == ppd_socket)
break;
}
"pccard,memory") == 0 &&
socket == ppd_socket)
break;
}
}
return (dip);
}
/*
* pcm_find_devinfo()
* this is a wrapper around DDI calls to "find" any
* devinfo node and then from there find the one associated
* with the socket
*/
{
return (NULL);
/*
* we have at least a base level dip
* see if there is one (this or a sibling)
* that has the correct socket number
* if there is, return that one else
* NULL so a new one is created
*/
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug)
"(instance=%d, socket=%d, name=%s)\n",
PCM_DEV_SOCKET, -1),
ddi_get_name(dip));
#endif
#if defined(PCMCIA_DEBUG)
ddi_get_name(dip));
#endif
return (dip);
}
/*
* pcm_find_parent_dip(socket)
* find the correct parent dip for this logical socket
*/
{
return (NULL);
}
/*
* pcmcia_set_em_handler()
* This is called by the management and event driver to tell
* the nexus what to call. Multiple drivers are allowed
* but normally only one will exist.
*/
int
{
/* NULL means remove the handler based on the ID */
if (pcmcia_mif_handlers == NULL)
return (0);
} else {
for (mif = pcmcia_mif_handlers;
;
}
}
} else {
if (pcmcia_num_adapters == 0) {
return (ENXIO);
}
if (elen > EM_EVENTSIZE)
return (EINVAL);
mif = (struct pcmcia_mif *)
kmem_zalloc(sizeof (struct pcmcia_mif),
return (ENOSPC);
*cs = (void *)pcmcia_card_services;
*ss = (void *)SocketServices;
}
}
return (0);
}
/*
* pcm_fix_bits(uchar_t *data, int num, int dir)
* shift socket bits left(0) or right(0)
* This is used when mapping logical and physical
*/
void
{
int i;
if (dir == 0) {
/* LEFT */
}
} else {
/* RIGHT */
}
}
}
{
}
return (mask);
}
int
{
int i;
if (val == 0)
return (0);
for (i = 0; i < 32; i++)
if (val > (1 << i))
return (i);
return (0);
}
#if defined(PCMCIA_DEBUG)
char *ssfuncs[128] = {
"GetAdapter", "GetPage", "GetSocket", "GetStatus", "GetWindow",
"InquireAdapter", "InquireSocket", "InquireWindow", "ResetSocket",
"SetPage", "SetAdapter", "SetSocket", "SetWindow", "SetIRQHandler",
"ClearIRQHandler",
"CSIsActiveDip",
"CSInitDev", "CSRegister", "CSCISInit", "CSUnregister",
"CISGetAddress", "CISSetAddress", "CSCardRemoved", "CSGetCookiesAndDip"
};
#endif
/*
* SocketServices
* general entrypoint for Card Services to find
* Socket Services. Finding the entry requires
* a _depends_on[] relationship.
*
* In some cases, the work is done locally but usually
* the parameters are adjusted and the adapter driver
* code asked to do the work.
*/
int
SocketServices(int function, ...)
{
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug > 1)
#endif
switch (function) {
case CSRegister:
case CISGetAddress:
case CISSetAddress:
"pcmcia: CSRegister (%x, %x, %p, %p) *ERROR*",
(void *)reg->cs_card_services,
break;
}
switch (function) {
case CISGetAddress:
break;
case CISSetAddress:
break;
case CSRegister:
break;
}
break;
case CSUnregister:
break;
case CSCISInit:
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug)
"CSCISInit: CIS is initialized on socket %d\n",
(int)args[0]);
#endif
/*
* now that the CIS has been parsed (there may not
* be one but the work is done) we can create the
* device information structures.
*
* we serialize the node creation to avoid problems
*/
break;
case CSInitDev:
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug)
#endif
/*
* this is where we create the /devices entries
* that let us out into the world
*/
break;
case CSCardRemoved:
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug)
"CSCardRemoved! (socket=%d)\n", (int)args[0]);
#endif
if (socket >= pcmcia_num_sockets)
break;
"pcmcia: bad socket = %x", socket);
break;
}
/*
* break the association of dip and socket
* for all functions on that socket
*/
struct pcmcia_parent_private *ppd;
ppd = (struct pcmcia_parent_private *)
ppd->ppd_active = 0;
(void) ndi_devi_offline(dip,
}
#if defined(PCMCIA_DEBUG)
else {
if (pcmcia_debug)
"CardRemoved: no "
"dip present "
"on socket %d!\n",
(int)args[0]);
}
#endif
}
} else {
}
break;
case CSGetCookiesAndDip:
else
error = BAD_SOCKET;
break;
case CSGetActiveDip:
/*
* get the dip associated with the card currently
* in the specified socket
*/
break;
/*
* the remaining entries are SocketServices calls
*/
case SS_GetAdapter:
break;
case SS_GetPage:
break;
case SS_GetSocket:
break;
case SS_GetStatus:
break;
case SS_GetWindow:
break;
case SS_InquireAdapter:
break;
case SS_InquireSocket:
break;
case SS_InquireWindow:
break;
case SS_ResetSocket:
break;
case SS_SetPage:
break;
case SS_SetSocket:
break;
case SS_SetWindow:
break;
case SS_SetIRQHandler:
break;
case SS_ClearIRQHandler:
clear_irq_handler_t *));
break;
default:
break;
}
return (error);
}
/*
* pcmcia_merge_power()
* The adapters may have different power tables so it
* is necessary to construct a single power table that
* can be used throughout the system. The result is
* a merger of all capabilities. The nexus adds
* power table entries one at a time.
*/
void
{
int i;
struct power_entry pwr;
for (i = 0; i < pcmcia_num_power; i++) {
if (pwr.ValidSignals ==
return;
} else {
/* partial match */
pwr.ValidSignals &=
}
}
}
/* what's left becomes a new entry */
if (pcmcia_num_power == PCMCIA_MAX_POWER)
return;
}
/*
* pcmcia_do_suspend()
* tell CS that a suspend has happened by passing a
* card removal event. Then cleanup the socket state
* to fake the cards being removed so resume works
*/
void
{
struct pcmcia_adapter *adapt;
int i;
#ifdef XXX
if (pcmcia_cs_event == NULL) {
return;
}
#endif
return;
}
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug) {
}
#endif
return;
/*
* If there is a card in the socket, then we need to send
* everyone a PCE_CARD_REMOVAL event, and remove the
* card active property.
*/
for (i = 0; i < sockp->ls_functions; i++) {
struct pcmcia_parent_private *ppd;
ppd = (struct pcmcia_parent_private *)
}
#if 0
#endif
}
if (pcmcia_cs_event &&
}
}
/*
* pcmcia_do_resume()
* tell CS that a suspend has happened by passing a
* card removal event. Then cleanup the socket state
* to fake the cards being removed so resume works
*/
void
{
struct pcmcia_adapter *adapt;
#ifdef XXX
if (pcmcia_cs_event == NULL) {
return;
}
#endif
return;
}
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug) {
}
#endif
SUCCESS) {
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug)
#endif
#if 0
/* now have socket info -- do we have events? */
if (pcmcia_cs_event &&
}
/* we should have card removed from CS soon */
}
#else
if (pcmcia_cs_event &&
}
#endif
}
}
/*
* pcmcia_map_power_set()
* Given a power table entry and level, find it in the
* master table and return the index in the adapter table.
*/
static int
{
int plevel, i;
/* mask = pcmcia_power_table[level].ValidSignals; */
for (i = 0; i < adapt->pca_numpower; i++)
return (i);
return (0);
}
/*
* pcmcia_map_power_get()
* Given an adapter power entry, find the appropriate index
* in the master table.
*/
static int
{
int plevel, i;
/* mask = pwr[level].ValidSignals; */
for (i = 0; i < pcmcia_num_power; i++)
return (i);
return (0);
}
/*
* XXX - SS really needs a way to allow the caller to express
* interest in PCE_CARD_STATUS_CHANGE events.
*/
static uint32_t
pcm_event_map[32] = {
};
static int
{
int i;
if (eventmask & (1 << i)) {
mask |= pcm_event_map[i];
eventmask &= ~(1 << i);
}
}
return (mask);
}
/*
* PCMCIA Generic Naming Support
*
* With 2.6, PCMCIA naming moves to the 1275 and generic naming model.
* Consequently, the whole naming mechanism is to be changed. This is
* not backward compatible with the current names but that isn't a problem
* due to so few drivers existing.
*
* For cards with a device_id tuple, a generic name will be used.
* if there is no device_id, then the 1275 name will be used if possible.
* The 1275 name is of the form pccardNNNN,MMMM from the manfid tuple.
* if there is not manfid tuple, an attempt will be made to bind the
* node to the version_1 strings.
*
* In all cases, a "compatible" property is created with a number
* of names. The most generic name will be last in the list.
*/
/*
* pcmcia_fix_string()
* want to avoid special characters in alias strings so convert
* to something innocuous
*/
void
pcmcia_fix_string(char *str)
{
switch (*str) {
case ' ':
case '\t':
*str = '_';
break;
}
}
}
void
{
int i;
/* get MANFID if it exists -- this is most important form */
tuple.Attributes = 0;
SUCCESS) {
&manfid);
if (i == SUCCESS) {
}
} else {
tuple.Attributes = 0;
SUCCESS) {
&jedec);
if (i == SUCCESS) {
}
}
}
}
void
{
int which = 0;
/* Version 1 strings */
tuple.Attributes = 0;
if (!which &&
SUCCESS) {
if (i == SUCCESS) {
sizeof (info->pd_vers1_name))
break;
if (space) {
}
/* strip trailing spaces off of string */
len > 0)
len--;
space = 1;
}
}
}
}
int
{
int ret = 0;
tuple->Attributes = 0;
break;
}
ret = 1;
break;
}
tuple->Attributes = 0;
}
return (ret);
}
char *pcmcia_lan_types[] = {
"arcnet",
"ethernet",
"token-ring",
"localtalk",
"fddi",
"atm",
"wireless",
"reserved"
};
void
{
int which = 0;
int i;
tuple.Attributes = 0;
SUCCESS) {
/*
* need to make sure that CISTPL_FUNCID is not
* present in both a global and local CIS for MF
* cards. 3COM seems to do this erroneously
*/
ltuple.Attributes = 0;
SUCCESS) {
/* this is the per-function funcid */
}
}
if (i == SUCCESS) {
/* in case no function extension */
else
"class,%x",
}
case TPLFUNC_LAN:
if (which) {
&tuple,
&funce, TPLFUNC_LAN);
if (i == SUCCESS) {
if (i > sizeof (pcmcia_lan_types) /
sizeof (char *)) {
break;
}
pcmcia_lan_types[i]);
}
}
break;
case TPLFUNC_VIDEO:
#ifdef future_pcmcia_spec
if (which) {
&tuple,
&funce, TPLFUNC_VIDEO);
if (i == SUCCESS) {
if (i > sizeof (pcmcia_lan_types) /
sizeof (char *)) {
break;
}
pcmcia_lan_types[i]);
}
}
#endif
break;
}
} else {
/* if no FUNCID, do we have CONFIG */
tuple.Attributes = 0;
}
}
}
/*
* pcmcia_add_compatible()
* add the cached compatible property list.
*/
void
{
int length = 0, i;
char buff[MAXNAMELEN];
char *compat_name[8];
int ci = 0;
}
(PCM_NAME_1275 | PCM_MULTI_FUNCTION)) {
info->pd_function);
}
}
/* no generic without "pccard" */
} else {
/* first pccard,generic-name */
}
/* now the simple generic name */
}
char *mem = "pccard,memory";
/*
* I/O cards are required to have a config tuple.
* there are some that violate the spec and don't
* but it is most likely that this is a memory card
* so tag it as such. "memory" is more general
* than other things so needs to come last.
*/
}
if (ci == 0)
return;
for (i = 0; i < ci; i++)
}
/*
* CIS parsing and other PC Card specific code
*/
/*
* pcmcia_get_mem_regs()
*/
static int
{
int num_regs = 0;
int space;
/*
* current plan for reg spec:
* device_a will be accumulated to determine max size of
* attribute memory. device for common. Then config
* tuples to get a worst case I/O size.
*/
if (type == CISTPL_DEVICE)
&device);
else
&device);
if (ret == CS_SUCCESS) {
curr_base = 0;
ret++) {
/* need to order these for real mem first */
/* how to represent types??? */
PC_REG_PHYS_HI(0, 0,
0);
num_regs++;
} else {
/*
* NULL device is a "hole"
*/
curr_base +=
}
}
}
}
return (num_regs);
}
/*
*
*/
static int
int pctype)
{
int num_regs = 0;
int found = 0;
tuple.Attributes = 0;
curr_base = 0;
len = 0;
return (0);
}
curr = 0;
tuple.Attributes = 0;
&tuple) == CS_SUCCESS) {
CS_SUCCESS) {
/* we have an I/O entry */
if (len != 0)
if (curr_len == 0)
/* we have potential relocation */
int mask;
/* more accurate length */
0,
0);
num_regs++;
found = 2;
break;
}
}
curr++;
found = 1;
}
if (found == 2)
break;
} else {
/* no I/O range so just a mask */
0,
0);
num_regs++;
regs++;
/* quit on "good" entry */
break;
}
/* was this the last CFTABLE Entry? */
break;
}
}
}
if (found == 1) {
/*
* have some non-relocatable values
* so we include them all for now
*/
info->pd_function, 0);
regs++;
num_regs++;
}
}
}
return (num_regs);
}
/*
* pcmcia_create_regs()
* create a valid set of regspecs for the card
* The first one is always for CIS access and naming
*/
/*ARGSUSED*/
static void
struct pcmcia_parent_private *ppd)
{
int num_regs = 0;
int len;
int bustype;
/* always have a CIS map */
info->pd_function, 0);
} else {
/* always have a CIS map */
info->pd_function, 0);
}
num_regs++;
/*
* need to search CIS for other memory instances
*/
/* special case of memory only card without CIS */
info->pd_function, 0);
num_regs++;
} else {
/*
* on the card and represent them here.
*/
/* now look for an I/O space to configure */
bustype);
}
}
/*
* pcmcia_need_intr()
* check to see if an interrupt tuple exists.
* existence means we need one in the intrspec.
*/
static int
{
int i;
tuple.Attributes = 0;
return (0);
}
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug) {
}
#endif
return (0);
}
tuple.Attributes = 0;
&tuple) != CS_SUCCESS) {
break;
}
CS_SUCCESS) {
break;
}
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug)
#endif
return (1);
}
return (0);
}
/*
* pcmcia_num_funcs()
* look for a CISTPL_LONGLINK_MFC
* if there is one, return the number of functions
* if there isn't one, then there is one function
*/
static int
{
int count = 1;
tuple.Attributes = 0;
/* this is a multifunction card */
CISTPL_LONGLINK_MFC) == CS_SUCCESS) {
}
}
return (count);
}
/*
* pcmcia_create_dev_info(socket)
* either find or create the device information structure
* for the card(s) just inserted. We don't care about removal yet.
* In any case, we will only do this at CS request
*/
static void
{
struct pcm_device_info card_info;
int i;
static int handle_def = 0;
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug)
socket);
#endif
/*
* before we can do anything else, we need the parent
* devinfo of the socket. This gets things in the right
* place in the device tree.
*/
return;
/* Card Services calls needed to get CIS info */
if (handle_def == 0) {
®) != CS_SUCCESS) {
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug)
"pcmcia: RegisterClient failed\n");
#endif
return;
}
handle_def++;
}
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug)
"pcmcia_create_dev_info: handle = %x\n",
#endif
card_info.pd_function = 0;
/* no CIS means memory */
} else {
/*
* how many functions on the card?
* we need to know and then we do one
* child node for each function using
* the function specific tuples.
*/
if (functions > 1) {
}
for (i = 0; i < functions; i++) {
register int flags;
card_info.pd_function = i;
/*
* new name construction
*/
if (functions != 1) {
/* need per function handle */
card_info.pd_function = i;
/* get new handle */
}
if (!(flags & PCM_NAME_1275)) {
if (flags & PCM_NAME_VERS1) {
',';
sizeof (PCMDEV_NAMEPREF),
sizeof (PCMDEV_NAMEPREF));
} else {
/*
* have a CIS but not the right info
* so treat as generic "pccard"
*/
"pccard,memory");
"pccard,memory");
}
}
}
return;
}
}
/*
* pcmcia_init_devinfo()
* if there isn't a device info structure, create one
* if there is, we don't do much.
*
* Note: this will need updating as 1275 finalizes their spec.
*/
static void
{
int unit;
char *name;
struct pcmcia_parent_private *ppd;
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug)
#endif
/*
* find out if there is already an instance of this
* device. We don't want to create a new one unnecessarily
*/
/* it already exist but isn't a .conf file */
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug)
ddi_get_name(dip));
#endif
(void) ndi_prop_update_string(DDI_DEV_T_NONE,
ppd = (struct pcmcia_parent_private *)
dip;
ppd->ppd_active = 0;
}
} else {
char *dtype;
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug)
#endif
else
&dip) !=
NDI_SUCCESS) {
"pcmcia: unable to create device [%s](%d)\n",
return;
}
/*
* construct the "compatible" property if the device
* has a generic name
*/
KM_SLEEP);
/*
* add the "socket" property
* the value of this property contains the logical PCMCIA
* socket number the device has been inserted in, along
* with the function # if the device is part of a
* multi-function device.
*/
/*
* determine all the properties we need for PPD
* then create the properties
*/
/* socket is unique */
sizeof (struct pcm_regs) / sizeof (int));
ppd->ppd_intrspec =
}
/* set parent private - our own format */
/* init the device type */
(sizeof (char *))))
else
dtype = "unknown";
(void) ndi_prop_update_string(DDI_DEV_T_NONE,
/* set PC Card as active and present in socket */
dip;
/*
* We should not call ndi_devi_online here if
* pcmcia attach is in progress. This causes a deadlock.
*/
if (pcmcia_dip != dip) {
if (ndi_devi_online_async(dip, 0)
!= NDI_SUCCESS) {
(void) ndi_devi_free(dip);
return;
}
}
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug)
#endif
}
/*
* inform the event manager that a child was added
* to the device tree.
*/
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug > 1) {
}
#endif
}
/*
* free any allocated parent-private data
*/
static void
{
}
/*
* pcmcia only allocates 1 intrspec today
*/
}
}
/*
* pcmcia_get_devinfo(socket)
* entry point to allow finding the device info structure
* for a given logical socket. Used by event manager
*/
pcmcia_get_devinfo(int socket)
{
if (pcmcia_sockets[socket])
return ((dev_info_t *)NULL);
}
/*
* CSGetCookiesAndDip()
* get info needed by CS to setup soft interrupt handler and provide
* socket-specific adapter information
*/
static int
{
int sock;
if (sock >= pcmcia_num_sockets ||
return (BAD_SOCKET);
/*
* Setup the adapter info for Card Services
*/
return (SUCCESS);
}
/*
* Note:
* The following functions that start with 'SS'
* implement SocketServices interfaces. They
* the adapter specific number based on the general
* value that CardServices uses.
*
* See the descriptions in SocketServices for
* details. Also refer to specific adapter drivers
* for implementation reference.
*/
static int
{
int n;
for (n = 0; n < pcmcia_num_adapters; n++) {
}
return (SUCCESS);
}
static int
{
return (BAD_WINDOW);
}
&newpage);
}
return (retval);
}
static int
{
if (sock > pcmcia_num_sockets ||
return (BAD_SOCKET);
}
&newsocket);
VCC);
VPP1);
VPP2);
}
return (retval);
}
static int
{
if (sock > pcmcia_num_sockets ||
return (BAD_SOCKET);
}
&newstat);
}
return (retval);
}
static int
{
&newwin);
}
return (retval);
}
/*
* SSInquireAdapter()
* Get the capabilities of the "generic" adapter
* we are exporting to CS.
*/
static int
{
/*
* notes: Adapter Capabilities are going to be difficult to
* determine with reliability. Fortunately, most of them
* don't matter under Solaris or can be handled transparently
*/
/*
* interrupts need a little work. For x86, the valid IRQs will
* be restricted to those that the system has exported to the nexus.
* for SPARC, it will be the DoRight values.
*/
adapter->ActiveHigh = 0;
return (SUCCESS);
}
static int
{
if (sock > pcmcia_num_sockets ||
return (BAD_SOCKET);
&newsocket);
}
return (retval);
}
static int
{
int slide;
if (win > pcmcia_num_windows)
return (BAD_WINDOW);
&newwin);
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug > 1)
#endif
/* just in case */
/*
* note that sockets are relative to the adapter.
* we have to adjust the bits to show a logical
* version.
*/
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug > 1) {
}
#endif
}
return (retval);
}
static int
{
if (socket >= pcmcia_num_sockets ||
return (BAD_SOCKET);
}
static int
{
if (window > pcmcia_num_windows) {
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug > 1)
#endif
return (BAD_WINDOW);
}
}
#if defined(PCMCIA_DEBUG)
#endif
return (retval);
}
static int
{
if (window > pcmcia_num_windows)
return (BAD_WINDOW);
if (socket > pcmcia_num_sockets ||
return (BAD_SOCKET);
}
}
return (retval);
}
static int
{
if (sock > pcmcia_num_sockets ||
return (BAD_SOCKET);
}
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug > 1)
#endif
&newsock);
VCC);
VPP1);
VPP2);
} else {
}
}
return (retval);
}
/*
* SSSetIRQHandler()
* arrange for IRQ to be allocated if appropriate and always
* arrange that PC Card interrupt handlers get called.
*/
static int
{
struct pcmcia_parent_private *ppd;
if (sock > pcmcia_num_sockets ||
return (BAD_SOCKET);
}
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug) {
}
#endif
if (retval == DDI_SUCCESS) {
} else {
}
return (retval);
}
/*
* SSClearIRQHandler()
* Arrange to have the interrupt handler specified removed
* from the interrupt list.
*/
static int
{
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug) {
"SSClearIRQHandler: socket=%x, function=%x\n",
"\thandler(%p): socket=%x, id=%x\n",
}
#endif
if (sock > pcmcia_num_sockets ||
return (BAD_SOCKET);
}
if (dip) {
return (SUCCESS);
}
return (BAD_SOCKET);
}
/*
* pcm_pathname()
* make a partial path from dip.
*
* XXX - we now use ddi_get_name_addr to get the "address" portion
* of the name; that way, we only have to modify the name creation
* algorithm in one place
*/
static void
{
}
/*
* pcmcia_create_device()
* create the /devices entries for the driver
* it is assumed that the PC Card driver will do a
* RegisterClient for each subdevice.
* The device type string is encoded here to match
* the standardized names when possible.
* XXX - note that we may need to provide a way for the
* caller to specify the complete name string that
* we pass to ddi_set_name_addr
*/
static int
{
struct pcm_make_dev device;
/*
* Now that we have the name, create it.
*/
0)) != DDI_SUCCESS) {
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug)
"pcmcia_create_device: failed "
"create\n");
#endif
return (BAD_ATTRIBUTE);
}
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug)
"pcmcia_create_device: created %s "
"from %s [%s]\n",
#endif
PCM_EVENT_MORE : 0;
-1);
-1);
0);
}
/*
* we send an event for ALL devices created.
* To do otherwise ties us to using drvconfig
* forever. There are relatively few devices
* ever created so no need to do otherwise.
* The existence of the event manager must never
* be visible to a PCMCIA device driver.
*/
return (err);
}
/*
* pcmcia_get_minors()
* We need to traverse the minor node list of the
* dip if there are any. This takes two passes;
* one to get the count and buffer size and the
* other to actually copy the data into the buffer.
* The framework requires that the dip be locked
* during this time to avoid breakage as well as the
* driver being locked.
*/
int
{
int count = 0;
struct ddi_minor_data *dp;
struct pcm_make_dev *md;
int socket;
PCM_DEV_SOCKET, -1);
count++; /* have one more */
}
/* we now know how many nodes to allocate */
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug > 1) {
"pcmcia_get_minors: name=%s,"
"socket=%d, stype=%x, "
"ntype=%s, dev_t=%x",
"\tbind name = %s\n",
}
#endif
/* no more */
}
} else {
count = 0;
}
}
return (count);
}
#if defined(PCMCIA_DEBUG)
static void
{
int count = 0;
struct ddi_minor_data *dp;
dev_info_t *np;
PCM_DEV_SOCKET, -1);
if (major != -1) {
char *cf2 = "";
char *cur = "";
cf2 = "DS_READY";
cur = "CUR";
(struct ddi_minor_data *)NULL) {
count++; /* have one more */
}
"socket=%d, stype=%x, "
"ntype=%s, dev_t=%x",
unit,
}
}
}
}
}
#endif
/*
* experimental merging code
* what are the things that we should merge on?
* match something by name in the "compatible" property
* restrict to a specific "socket"
* restrict to a specific "instance"
*/
/*ARGSUSED*/
static int
{
return (0); /* merge failed */
}
/*
* pcmcia_mfc_intr()
* Multifunction Card interrupt handler
* While some adapters share interrupts at the lowest
* level, some can't. In order to be consistent, we
* split multifunction cards out with this intercept and
* allow the low level to do what is best for it.
* the arg is a pcmcia_socket structure and all interrupts
* are per-socket in this case. We also have the option
* to optimize if the cards support it. It also means
* that we can use the INTRACK mode if it proves desirable
*/
/*ARGSUSED*/
static uint32_t
{
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug > 1) {
" ls_inthandlers=%p\n"
"\t ls_flags=0x%x PCS_IRQ_ENABLED=0x%x \n",
}
#endif
return (DDI_INTR_UNCLAIMED);
done++;
}
}
if (sockp->ls_inthandlers)
}
/*
* pcmcia_power(dip)
* control power for nexus and children
*/
int
{
#if 0
int i;
/*
* for now, we only have one component. Should there be one per-socket?
* the level is only one (power on or off)
*/
return (DDI_FAILURE);
for (i = 0; i < pcic->pc_numsockets; i++) {
if (pcic->pc_callback)
(level == 0) ? PCE_PM_SUSPEND :
i);
}
#else
return (DDI_FAILURE);
#endif
}
void
{
int i;
for (i = 0; i < pcmcia_num_adapters; i++) {
adapt = pcmcia_adapters[i];
break;
}
}
return;
for (i = 0; i < adapt->pca_numsockets; i++) {
int s;
s = adapt->pca_first_socket + i;
if (pcmcia_sockets[s]->ls_flags &
(1 << PCE_PM_RESUME)) {
(void) cs_event(PCE_PM_RESUME, s, 0);
}
(void) cs_event(PCE_CARD_REMOVAL, s, 0);
}
}
}
/*
* mark a cardbus card as "suspended" in the pcmcia module
*/
void
{
}
/*
* mark a cardbus card as "resumed" in the pcmcia module
*/
void
pcmcia_cb_resumed(int socket)
{
#ifdef PCMCIA_DEBUG
if (pcmcia_debug) {
}
#endif
}
}
void
{
for (i = 0; i < pcmcia_num_adapters; i++) {
adapt = pcmcia_adapters[i];
break;
}
}
return;
done = 1;
for (i = 0; i < adapt->pca_numsockets; i++) {
int s;
s = adapt->pca_first_socket + i;
for (f = 0; f < PCMCIA_MAX_FUNCTIONS; f++)
if (pcmcia_sockets[s] &&
pcmcia_sockets[s]->ls_flags &
#ifdef PCMCIA_DEBUG
if (pcmcia_debug) {
"pcmcia_wait_insert: "
"socket in SUSPENDED state");
}
#endif
done = 0;
break;
}
}
if (!done) {
tm = ddi_get_lbolt();
(void) cv_timedwait(&pcmcia_condvar,
} else {
tries = 0;
}
}
if (tries == 0) {
}
}
int
{
struct pcmcia_parent_private *ppd;
int result;
return (DDI_FAILURE);
struct pcm_regs *p;
/* need I/O */
/*
* We want to find an IO regspec. When we
* find one, it either has to match
* the caller's requested base address
* or it has to be relocatable.
* We match on the requested base address
* rather than the allocated base
* address so that we handle the case
* of adapters that have IO window base
* relocation registers.
*/
if ((p->phys_hi &
break;
} else {
/* need memory */
if (p->phys_hi &
break;
}
}
return (DDI_FAILURE);
return (DDI_FAILURE);
}
} else {
}
/*
* Allocate and initialize the common elements of data access handle.
*/
/*
* Set up the mapping request and call to parent.
*/
if (result != DDI_SUCCESS) {
} else {
}
return (result);
}
struct pcmcia_adapter *
{
int i;
for (i = 0; i < pcmcia_num_adapters; i++) {
if (pcmcia_adapters[i] &&
return (pcmcia_adapters[i]);
}
}
return (NULL);
}
void
{
struct pcmcia_parent_private *ppd;
if (ppd) {
if (ret) {
#ifdef PCMCIA_DEBUG
"%s=<%x,%x>",
#else
"%s=<%x,%x>",
#endif
}
} else {
int i;
if (i == 0) {
}
}
}
}
int
{
}
int
{
}
static boolean_t
{
return (B_FALSE);
"class-code", 0xff);
if (class == PPB_SUBTRACTIVE) {
return (B_TRUE);
}
return (B_FALSE);
}
/*
* pcmcia_pci_alloc()
* allocate mem or I/O resource from the ancestor of the cardbus bridge.
* First start from the parent node. If the parent is a subtractive
* decode bridge and it does not have the requested resource, go up the
* device tree to find the resource.
*
* dip the parent node of the cardbus bridge
*
* res_dip returns a pointer to the node from which the
* resource is obtained. *res_dip could point to
* the parent or a higher level ancestor. *res_dip
* should be saved by the caller and later passed
* to pcmcia_ra_free();
*/
int
{
== NDI_FAILURE) ||
((base >> 32) != 0)) {
if (is_subtractv(dip)) {
} else {
ret->ra_addr_hi = 0;
ret->ra_addr_lo = 0;
return (DDI_FAILURE);
}
}
ret->ra_addr_hi = 0;
return (DDI_SUCCESS);
}
int
{
/*
* Allocate space from busra resource list
* should not return an address > 32 bits
*/
== NDI_FAILURE) ||
((base >> 32) != 0)) {
} else {
ret->ra_addr_hi = 0;
return (DDI_SUCCESS);
}
}
int
{
}
int
{
}
int
{
return (DDI_FAILURE);
return (DDI_SUCCESS);
} else {
return (DDI_FAILURE);
}
}
/*
* when the low level device configuration does resource assignment
* (devconf) then free the allocated resources so we can reassign them
* later. Walk the child list to get them.
*/
void
{
int len;
int circular;
/* do searches in compatible property order */
len = 0;
"assigned-addresses",
&len) == DDI_PROP_SUCCESS) {
/*
* if there are assigned resources at this point,
* then the OBP or devconf have assigned them and
* they need to be freed.
*/
}
}
}
/*
* this is the equivalent of pcm_get_intr using ra_allocs.
* returns -1 if failed, otherwise returns the allocated irq.
* The input request, if less than zero it means not a specific
* irq requested. If larger then 0 then we are requesting that specific
* irq
*/
int
{
int err;
base = 0;
len = 1;
if (request >= 0) {
}
req.ra_boundbase = 0;
if (err == NDI_FAILURE) {
return (-1);
} else {
return ((int)base);
}
}
int
{
NDI_RA_PASS)) == NDI_SUCCESS) {
return (0);
} else
return (-1);
}
#ifdef sparc
int
{
struct pcmcia_parent_private *ppd;
struct pcmcia_adapter *adapt;
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug) {
"pcmcia_add_intr_impl() entered "
"dip=%p rdip=%p hdlp=%p \n",
}
#endif
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug) {
" ppd_flags=0X%x PPD_CARD_MULTI=0X%x\n"
" ppd_intrspec=%p ls_inthandlers=%p\n",
(void *) ppd->ppd_intrspec,
(void *)sockp->ls_inthandlers);
}
#endif
/*
* calculate IPL level when we support multiple levels
*/
return (DDI_FAILURE);
}
/*
* check if multifunction and do the right thing
* we put an intercept in between the mfc handler and
* us so we can catch and process. We might be able
* to optimize this depending on the card features
* (a future option).
*/
/*
* note that the first function is a special
* case since it sets things up. We fall through
* to the lower code and get the hardware set up.
* subsequent times we just lock the list and insert
* the handler and all is well.
*/
return (DDI_FAILURE);
}
/*
* replace first function handler with
* the mfc handler
*/
} else {
return (DDI_SUCCESS);
}
}
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug) {
}
#endif
/* set default IPL then check for override */
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug) {
" handler_id=0X%x handler=%p arg1=%p arg2=%p\n",
}
#endif
SUCCESS) {
return (DDI_FAILURE);
}
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug) {
" iblk_cookie=%p idev_cookie=%p\n"
" ls_flags=0X%x PCS_COOKIES_VALID=0X%x\n",
(void *)handler.iblk_cookie,
(void *)handler.idev_cookie,
}
#endif
}
return (DDI_SUCCESS);
}
void
{
struct pcmcia_parent_private *ppd;
int socket;
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug) {
" dip=%p rdip=%p hdlp=%p\n",
}
#endif
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug) {
" ls_inthandlers=%p ls_intrspec=%p\n",
(void *)sockp->ls_inthandlers,
(void *)&sockp->ls_intrspec);
}
#endif
/* first handle the multifunction case since it is simple */
/* we must be MFC */
int remhandler = 0;
/* Check if there is only one handler left */
remhandler++;
}
} else {
int done;
done++;
if (intr->handler_id ==
done++;
/*
* If we're about to remove the
* handler at the head of
* the list, make the next
* handler in line the head.
*/
break;
} /* handler_id */
} /* for */
} /* intr->next */
if (!remhandler) {
return;
}
/* need to get the dip that was used to add the handler */
}
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug) {
" pispec=%p rdip=%p\n",
}
#endif
}
/* Consolidated interrupt processing interface */
/*ARGSUSED*/
int
{
int ret = DDI_SUCCESS;
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug) {
(int)intr_op);
}
#endif
switch (intr_op) {
case DDI_INTROP_GETCAP:
*(int *)result = DDI_INTR_FLAG_LEVEL;
break;
case DDI_INTROP_SETCAP:
ret = DDI_ENOTSUP;
break;
case DDI_INTROP_ALLOC:
break;
case DDI_INTROP_FREE:
break;
case DDI_INTROP_GETPRI:
return (DDI_FAILURE);
break;
case DDI_INTROP_SETPRI:
break;
case DDI_INTROP_ADDISR:
break;
case DDI_INTROP_REMISR:
break;
case DDI_INTROP_ENABLE:
case DDI_INTROP_DISABLE:
break;
case DDI_INTROP_NINTRS:
case DDI_INTROP_NAVAIL:
break;
/* PCI nexus driver supports only fixed interrupts */
DDI_INTR_TYPE_FIXED : 0;
break;
default:
ret = DDI_ENOTSUP;
break;
}
return (ret);
}
/*
* pcmcia_intr_get_ispec:
* This is mostly copied from older 'pcmcia_get_intrspec' function
*/
static struct intrspec *
{
int socket;
struct pcmcia_parent_private *ppd;
return (NULL);
return (NULL);
return (NULL);
return (NULL);
return (intrspec);
}
static struct intrspec *
{
int socket;
struct pcmcia_adapter *adapt;
struct pcmcia_parent_private *ppd;
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug)
"dip=0x%p rdip=0x%p hdlp=0x%p\n",
#endif /* PCMCIA_DEBUG */
return (ispecp);
}
/*
* check if multifunction and do the right thing
* we put an intercept in between the mfc handler and us so we can
* catch and process. We might be able to optimize this depending
* on the card features (a future option).
*/
/*
* note that the first function is a special case since it
* sets things up. We fall through to the lower code and
* get the hardware set up. Subsequent times we just lock
* the list and insert the handler and all is well.
*/
return (NULL);
}
} else {
}
return (ispecp);
}
/*
* Do we need to allocate an IRQ at this point or not?
*/
int i, irq;
/*
* this adapter needs IRQ allocations
* this is only necessary if it is the first function on the
* card being setup. The socket will keep the allocation info
*/
/* all functions use same intrspec except mfc handler */
/*
* We treat this special in order to allow things to
* work properly for MFC cards. The intrspec for the
* mfc dispatcher is intercepted and taken from the
* logical socket in order to not be trying to
* multiplex the meaning when ENABLE is called.
*/
}
/* find available and usable IRQ level */
}
}
if (irq < 0) {
return (NULL);
}
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug)
#endif /* PCMCIA_DEBUG */
return (ispecp);
}
/* set default IPL then check for override */
return (ispecp);
}
static int
{
int irq = 0; /* default case */
struct pcmcia_adapter *adapt;
struct pcmcia_parent_private *ppd;
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug)
"dip=0x%p rdip=0x%p hdlp=0x%p\n",
#endif /* PCMCIA_DEBUG */
&sockp->ls_intrspec) {
/* Only one handler. So, call ddi_add_intr on it */
if (ret == DDI_FAILURE) {
return (ret);
}
}
return (DDI_SUCCESS);
}
/* XXX: remove it later as this is done in _add_isr as well */
/* Enable interrupts */
if (ret != DDI_SUCCESS)
return (ret);
}
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug)
#endif /* PCMCIA_DEBUG */
/* set default IPL then check for override */
SUCCESS) {
return (DDI_FAILURE);
}
}
return (DDI_SUCCESS);
}
/* ARGSUSED */
static void
{
int done, remhandler = 0;
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug)
"dip=0x%p rdip=0x%p hdlp=0x%p\n",
#endif /* PCMCIA_DEBUG */
/* first handle the multifunction case since it is simple */
&sockp->ls_intrspec) {
/* Check if there is only one handler left */
remhandler++;
}
} else {
done++;
if (intr->handler_id ==
done++;
/*
* If we're about to remove the handler
* at the head of the list, make the
* next handler in line the head.
*/
break;
} /* handler_id */
} /* end of for */
} /* end of if intr->next */
if (!remhandler) {
return;
}
}
sockp->ls_intr_vec = 0;
ispecp->intrspec_vec = 0;
}
}
static void
{
struct pcmcia_adapter *adapt;
struct pcmcia_parent_private *ppd;
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug)
"dip=0x%p rdip=0x%p hdlp=0x%p\n",
#endif /* PCMCIA_DEBUG */
/* Check if there is only one handler left */
/*
* need to get the dip that was
* used to add the handler
*/
} else {
/* Don't call cleanup if list still has members */
return;
}
}
if (ihdl_plat_datap->ip_ispecp ==
parent = ddi_root_node();
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug)
"INTROP_DISABLE returned %x\n", ret);
#endif /* PCMCIA_DEBUG */
} else {
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug)
"CLEAR_IRQ returned %x\n", ret);
#endif /* PCMCIA_DEBUG */
}
}
/* Consolidated interrupt processing interface */
int
{
#if defined(PCMCIA_DEBUG)
if (pcmcia_debug)
"dip=0x%p rdip=0x%p op=0x%x hdlp=0x%p\n",
#endif /* PCMCIA_DEBUG */
switch (intr_op) {
*(int *)result = 0;
return (DDI_FAILURE);
}
*(int *)result = DDI_INTR_TYPE_FIXED;
break;
case DDI_INTROP_GETCAP:
*(int *)result = DDI_INTR_FLAG_LEVEL;
break;
case DDI_INTROP_NINTRS:
case DDI_INTROP_NAVAIL:
if (i_ddi_get_intx_nintrs(rdip) == 0) {
*(int *)result = 0;
return (DDI_FAILURE);
}
break;
case DDI_INTROP_ALLOC:
return (DDI_FAILURE);
break;
case DDI_INTROP_FREE:
break;
case DDI_INTROP_GETPRI:
*(int *)result = 0;
return (DDI_FAILURE);
}
break;
case DDI_INTROP_SETPRI:
if (*(int *)result > LOCK_LEVEL)
return (DDI_FAILURE);
break;
case DDI_INTROP_ADDISR:
return (DDI_FAILURE);
break;
case DDI_INTROP_REMISR:
break;
case DDI_INTROP_ENABLE:
return (DDI_FAILURE);
break;
case DDI_INTROP_DISABLE:
break;
default:
return (DDI_ENOTSUP);
}
return (DDI_SUCCESS);
}
#endif