pcata.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/sysmacros.h>
#include <sys/dditypes.h>
/*
* PCMCIA and DDI related header files
*/
0, 0, 0, NULL };
/*
* pcata_event - this is the event handler
*/
int
{
int retcode = CS_UNSUPPORTED_EVENT;
#ifdef ATA_DEBUG
if (pcata_debug & DPCM) {
(void) csx_Event2Text(&event2text);
priority);
}
#endif
/*
* Find out which event we got and do the appropriate thing
*/
switch (event) {
break;
case CS_EVENT_CARD_INSERTION:
/* if this is NOT low priority, ignore it */
if ((priority & CS_EVENT_PRI_LOW) == 0)
break;
if (retcode == CS_SUCCESS) {
}
}
/* kick start any threads that were blocked */
break;
/*
* Note that we get two CS_EVENT_CARD_REMOVAL events -
* one at high priority and the other at low priority.
* This is determined by the setting of the
* CS_EVENT_CARD_REMOVAL_LOWP bit in either of the
* event masks.
* (See the call to RegisterClient).
* We handle the CS_EVENT_PM_SUSPEND event the same
* way that we handle a CS_EVENT_CARD_REMOVAL event
* since if we're being asked to suspend, then we
* can't tell if the same card is inserted after
* a resume.
*/
case CS_EVENT_CARD_REMOVAL:
break;
case CS_EVENT_PM_SUSPEND:
break;
}
return (retcode);
}
/*
* at card insertion allocate I/O addresses
*/
int
{
int ret;
/*
* Read CIS and setup the variables.
*/
if (ret != CS_SUCCESS) {
"socket %d unable to get CIS information\n",
return (ret);
}
/*
* Parse CIS data
*/
while (cft) {
/* skip config index 0 (memory mapped config) */
if (cft->p.config_index) {
/*
* Allocate IO resources.
*/
io_req.Attributes2 = 0;
}
if (ret == CS_SUCCESS) {
/* found a good IO range */
break;
}
}
}
/*
* save all the CIS data
*/
if (cft) {
}
}
/* release config table entries list */
/*
* if we could not find a usable set of address
*/
if (!cft) {
return (CS_GENERAL_FAILURE);
}
#ifdef ATA_DEBUG
if (pcata_debug & DINIT) {
"\npresent mask: 0x%x\n"
"PRR pin mask: 0x%x\n"
"major_revision: 0x%x\n"
"minor_revision: 0x%x\n"
"manufacturer_id: 0x%x\n"
"card_id: 0x%x\n"
"config_base: 0x%x\n"
"config_index: 0x%x\n",
"\nata_vcc: %u\n"
"ata_vpp1: %u\n"
"ata_vpp2: %u\n"
"addr_lines: %u\n"
"ata_base[0]: %u\n"
"ata_length[0]: %u\n"
"ata_base[1]: %u\n"
"ata_length[1]: %u\n",
cis_vars->ata_length[0],
}
#endif
#ifdef ATA_DEBUG
if (pcata_debug & DPCM) {
"_ci_ioaddr: socket %d base_0 0x%x len_0 0x%x"
" base_1 0x%x len_1 0x%x\n",
}
#endif
return (CS_SUCCESS);
}
/*
* The card must be inserted before some things can be done
* 1) determine the I/O address space
* 2) determine the IRQ
* 3) configure the card
*/
static int
{
int ret;
int i;
#ifdef ATA_DEBUG
if (pcata_debug & DPCM)
#endif
/*
* Allocate io address space
*/
if (ret != CS_SUCCESS) {
return (ret);
}
}
/*
* Allocate an IRQ.
*/
softp->intr_pending = 0;
if (ret != CS_SUCCESS) {
#ifdef ATA_DEBUG
#endif
return (ret);
}
}
/*
* Initialize high level interrupt mutex.
*/
}
/*
* Configure the card.
*/
config_req.Attributes = 0;
config_req.Status = 0;
config_req.Copy = 0;
&config_req);
if (ret != CS_SUCCESS) {
#ifdef ATA_DEBUG
"socket %d RequestConfiguration failed %s\n",
#endif
return (ret);
}
}
#ifdef ATA_DEBUG
if (pcata_debug & DPCM)
#endif
/*
* check the disk (every .05 sec) to see if it is ready
*/
for (i = 0; i < PCATA_READY_TIMEOUT; i += 50000) {
break;
drv_usecwait(50000);
}
/* the disk is NOT ready */
return (CS_GENERAL_FAILURE);
}
/*
* create the device tree
*/
return (CS_GENERAL_FAILURE);
}
}
/*
* enable interrupts thru the CSX context
*/
if (ret != CS_SUCCESS) {
return (CS_GENERAL_FAILURE);
}
/* XXXX - for Volume Manager */
if (softp->checkmedia_flag) {
softp->checkmedia_flag = 0;
#ifdef ATA_DEBUG
if (pcata_debug & DVOLD) {
"\tdoing cv_broadcast - "
}
#endif
}
return (CS_SUCCESS);
}
/*
* this function may be called by several different threads simultaneously
* the normal calling sequence is
*/
int
{
}
/*
* Wait for minor nodes to be created before returning from attach,
* with a 5 sec. timeout to avoid hangs should an error occur.
*/
void
{
break;
}
}
int
{
int ret;
#ifdef ATA_DEBUG
if (pcata_debug & DENT)
priority);
#endif
/*
* If we're being called at high priority, we can't do much more
* than note that the card went away.
*/
if (priority & CS_EVENT_PRI_HIGH)
return (CS_SUCCESS);
/*
* If the device was open at the time the card was removed
* we set the ejected_while_mounted flag until all instances of the
* device are closed.
* If the device is mounted by vold it will remain open when
* the card is removed. If the card is inserted again it will
* be mounted again by vold.
*/
else {
int i;
for (i = 0; i < NUM_PARTS; i++) {
}
}
if (softp->ejected_while_mounted) {
"Card is ejected & "
"Data integrity is not guaranteed",
}
/* XXXX - for Volume Manager */
if (softp->checkmedia_flag) {
softp->checkmedia_flag = 0;
#ifdef ATA_DEBUG
if (pcata_debug & DVOLD) {
"pcata_card_removal: socket %d \n"
"\tdoing cv_broadcast - "
"softp->media_state of DKIO_EJECTED\n",
}
#endif
}
/*
* Release card configuration.
*/
&release_config)) != CS_SUCCESS) {
"%s\n",
} /* ReleaseConfiguration */
} /* PCATA_REQUESTCONFIG */
/*
* Release allocated IRQ resources.
*/
if (ret != CS_SUCCESS) {
} /* ReleaseIRQ */
} /* PCATA_REQUESTIRQ */
}
/*
* Release allocated IO resources.
*/
&io_req)) != CS_SUCCESS) {
"ReleaseIO failed %s\n",
} /* ReleaseIO */
} /* PCATA_REQUESTIO */
/*
* Remove all the device nodes. We don't have to explictly
* specify the names if we want Card Services to remove
* all of the devices.
* Note that when you call RemoveDeviceNode with the Action
* argument set to REMOVE_ALL_DEVICE_NODES, the
* NumDevNodes must be zero.
*/
&remove_device_node)) != CS_SUCCESS) {
"RemoveDeviceNode failed %s\n",
} /* RemoveDeviceNode */
} /* PCATA_MAKEDEVICENODE */
return (CS_SUCCESS);
}
static void
{
int drive;
/*
* free ab_active
*/
}
}
/* release any packets queued on the controller */
/* first free the packets */
}
}
/* release the unit structures */
}
/*
* now free the atarpbuf memory
* It is poor code practice to use artificial number of drives,
* but we need to be consistant with the rest of the code, hence the
* drive=1 value.
*/
(sizeof (struct atarpbuf) +
sizeof (struct scsi_inquiry)));
}
}
}
/*
* pcata_parse_cis - gets CIS information to configure the card.
*
* returns: CS_SUCCESS - if CIS information retreived correctly
* CS_OUT_OF_RESOURCE - if no memory for cftable entry
* {various CS return codes} - if problem getting CIS information
*/
int
{
int ret, last_config_index;
dcft = &default_cftable;
/*
* Clear the PCATA_VALID_IO_INFO flags here.
* These will be set if necessary as we parse the CIS and
* check the manufacturer specific overrides later on.
*/
/*
* Clear the CIS information structure.
*/
/*
* CISTPL_CONFIG processing. Search for the first config tuple
* so that we can get a pointer to the card's configuration
* registers. If this tuple is not found, there's no point
* in searching for anything else.
*/
tuple.Attributes = 0;
&tuple)) != CS_SUCCESS) {
return (ret);
} /* GetFirstTuple */
/*
* We shouldn't ever fail parsing this tuple. If we do,
* there's probably an internal error in the CIS parser.
*/
if (ret != CS_SUCCESS) {
return (ret);
}
/*
* This is the last CISTPL_CFTABLE_ENTRY tuple index that
* we need to look at.
*/
if (cistpl_config.nr) {
} else {
"CISTPL_CONFIG no configuration registers"
return (CS_BAD_CIS);
} /* if (cistpl_config.nr) */
/*
* CISTPL_VERS_1 processing. The information from this tuple is
* mainly used for display purposes.
*/
tuple.Attributes = 0;
if (ret != CS_SUCCESS) {
/*
* It's OK not to find the tuple if it's not in the CIS, but
* this test will catch other errors.
*/
if (ret != CS_NO_MORE_ITEMS) {
return (ret);
}
} else {
/*
* We shouldn't ever fail parsing this tuple. If we do,
* there's probably an internal error in the CIS parser.
*/
return (ret);
} else {
int i;
/*
* The first byte of the unused prod_strings will be
* NULL since we did a bzero(cis_vars) above.
*/
for (i = 0; i < cistpl_vers_1.ns; i++)
cistpl_vers_1.pi[i]);
} /* csx_Parse_CISTPL_VERS_1 */
} /* GetFirstTuple */
/*
* CISTPL_CFTABLE_ENTRY processing. Search for the first config tuple
* so that we can get a card configuration. If this tuple is not
* found, there's no point in searching for anything else.
*/
tuple.Attributes = 0;
&tuple)) != CS_SUCCESS) {
"_parse_cis: socket %d CISTPL_CFTABLE_ENTRY "
return (ret);
} /* GetFirstTuple */
/*
* Clear the default values.
*/
/*
* Some cards don't provide enough information
* in their CIS to allow us to configure them
* using CIS information alone, so we have to
* set some default values here.
*/
/*
* Loop through the CISTPL_CFTABLE_ENTRY tuple until we find a
* valid configuration.
*/
do {
if (!ocft) {
return (CS_OUT_OF_RESOURCE);
}
if (!*cftable) {
} else {
}
sizeof (struct cistpl_cftable_entry_t));
/*
* We shouldn't ever fail parsing this tuple. If we do,
* there's probably an internal error in the CIS parser.
*/
if ((ret = csx_Parse_CISTPL_CFTABLE_ENTRY(
&cistpl_cftable_entry)) != CS_SUCCESS) {
return (ret);
} else {
int default_cftable;
/*
* See if this tuple has default values that we
* should save. If so, copy the default values
* that we've seen so far into the current cftable
* structure.
*/
if (cistpl_cftable_entry.flags &
default_cftable = 1;
} else {
default_cftable = 0;
}
sizeof (pcata_cftable_params_t));
if (cistpl_cftable_entry.flags &
if (default_cftable)
}
if (cistpl_cftable_entry.flags &
struct cistpl_cftable_entry_pd_t *pd;
if (default_cftable)
} /* CISTPL_CFTABLE_PD_EXISTS */
} /* CISTPL_CFTABLE_TPCE_FS_PWR_VCC */
if (default_cftable)
} /* CISTPL_CFTABLE_PD_EXISTS */
} /* CISTPL_CFTABLE_TPCE_FS_PWR_VPP1 */
if (default_cftable)
} /* CISTPL_CFTABLE_PD_EXISTS */
} /* CISTPL_CFTABLE_TPCE_FS_PWR_VPP2 */
} /* CISTPL_CFTABLE_TPCE_FS_PWR */
if (cistpl_cftable_entry.flags &
if (default_cftable)
#ifdef ATA_DEBUG
if (pcata_debug & DPCM)
"CS says ranges present: %d\n",
#endif
cft->p.ata_length[0] =
}
if (default_cftable) {
dcft->p.ata_length[0] =
(uint32_t)
(uint32_t)
}
}
#ifdef ATA_DEBUG
if (pcata_debug & DPCM) {
"CS 1st io range: 0x%x+%d\n",
"CS 2nd io range: 0x%x+%d\n",
}
#endif
} else {
/*
* If there's no IO ranges for this
* configuration, then we need to calculate
* the length of the IO space by using the
* number of IO address lines value.
* Or we can set the base to zero and the
* length to 0xf.
*/
cft->p.ata_length[0] =
} /* CISTPL_CFTABLE_TPCE_FS_IO_RANGE */
} /* io->ranges */
} /* CISTPL_CFTABLE_TPCE_FS_IO */
} /* csx_Parse_CISTPL_CFTABLE_ENTRY */
&tuple)) == CS_SUCCESS));
#ifdef ATA_DEBUG
if (pcata_debug) {
"\n====== cftable entry ======\n"
"desireability: 0x%x\n"
" config_index: 0x%x\n"
" addr_lines: 0x%x\n"
" length[0]: 0x%x\n"
" length[1]: 0x%x\n"
" pin: 0x%x\n",
"\n ata_vcc: %d\n"
" ata_vpp1: %d\n"
" ata_vpp2: %d\n"
" ata_base[0]: 0x%p\n"
" ata_base[1]: 0x%p\n"
"====\n",
}
}
#endif
/*
* If GetNextTuple gave us any error code other than
* CS_NO_MORE_ITEMS, it means that there is probably
* an internal error in the CIS parser.
*/
return (ret); /* this is a real error */
}
/*
* CISTPL_FUNCID and CISTPL_FUNCE processing
*/
tuple.Attributes = 0;
&tuple)) != CS_SUCCESS) {
/*
* It's OK not to find the tuple if it's not in the CIS, but
* this test will catch other errors.
*/
if (ret != CS_NO_MORE_ITEMS) {
return (ret);
}
} else {
do {
sizeof (struct cistpl_funcid_t));
if ((ret = csx_Parse_CISTPL_FUNCID(
return (ret);
}
&tuple)) == CS_SUCCESS) {
sizeof (cistpl_funce_t));
/*
* Function extention parsing needs to be added
* for pcata in cardservices. Function
* extention is required by spec but not used
* in the code.
*/
if ((ret = csx_Parse_CISTPL_FUNCE(
&tuple, &cistpl_funce,
cistpl_funcid.function)) ==
CS_SUCCESS) {
}
}
&tuple)) == CS_SUCCESS);
} /* GetFirstTuple */
/*
* CISTPL_MANFID processing. The information from this tuple is
* used to augment the information we get from the
* CISTPL_FUNCID and CISTPL_FUNCE tuples.
*/
tuple.Attributes = 0;
&tuple)) != CS_SUCCESS) {
/*
* It's OK not to find the tuple if it's not in the CIS, but
* this test will catch other errors.
*/
if (ret != CS_NO_MORE_ITEMS) {
return (ret);
}
} else {
return (ret);
} else {
} /* csx_Parse_CISTPL_MANFID */
} /* GetFirstTuple */
return (CS_SUCCESS);
}
void
{
while (cft) {
}
while (ocft) {
}
}
char *
pcata_CS_etext(int ret)
{
static error2text_t cft;
(void) csx_Error2Text(&cft);
}
/*
* pcata_getinfo() - this routine translates the dip info dev_t and
* vice versa.
*
* Returns: DDI_SUCCESS, if successful.
* DDI_FAILURE, if unsuccessful.
*/
/* ARGSUSED */
int
{
int ret;
switch (cmd) {
case DDI_INFO_DEVT2DEVINFO:
case DDI_INFO_DEVT2INSTANCE:
if (ret != CS_SUCCESS) {
#ifdef ATA_DEBUG
"socket %d CS_DD_Info failed %s (0x%x)\n",
ret);
#endif
return (DDI_FAILURE);
}
switch (cmd) {
case DDI_INFO_DEVT2DEVINFO:
if (softp) {
}
break;
case DDI_INFO_DEVT2INSTANCE:
break;
} /* switch */
break;
default:
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
static int
{
#ifdef ATA_DEBUG
if (pcata_debug & DENT) {
}
#endif
return (DDI_NOT_WELL_FORMED);
if (!unitp) {
return (DDI_NOT_WELL_FORMED);
}
#ifdef ATA_DEBUG
if (pcata_debug & DINIT)
"targ = %d cyl = %d acyl = %d head = %d sec = %d\n",
targ,
#endif
return (DDI_SUCCESS);
}
static int
{
int ret;
#ifdef ATA_DEBUG
if (pcata_debug & DPCM) {
}
#endif
if (!(CARD_PRESENT_VALID(softp))) {
goto err;
}
/* setup card */
softp->ab_status_flag = 0;
/*
* port addresses
*/
/*
* Future work second arg should not be hard coded (# of drives per
* socket).
* Right now in PCMCIA we have one disk per target,
* if and when we have disks that have multiple targets
* in the same socket (unlikely) then we will have multiple
* disks per socket.
*/
goto err;
}
if (pcata_set_rw_multiple(softp, 0)) {
goto err;
}
if (ret != DDI_SUCCESS)
goto err;
goto err;
}
goto err;
}
/*
* Initialise the Partition table so that pcata_strategy can
* successfully read the actual vtoc information.
*/
goto err;
}
return (DDI_SUCCESS);
err:
return (DDI_FAILURE);
}
/* probably want to replace this with struct devnode_desc */
static struct driver_minor_data {
char *name;
int minor;
int type;
} id_minor_data[] = {
{ "a", 0, S_IFBLK},
{ "a,raw", 0, S_IFCHR},
};
/*
* create the device nodes
*/
static int
{
struct driver_minor_data *dmdp;
int ret;
sizeof (id_minor_data)/sizeof (*id_minor_data);
kmem_zalloc(sizeof (devnode_desc_t) *
#ifdef ATA_DEBUG
if (pcata_debug & DIO) {
}
#endif
dmdp < (id_minor_data +
sizeof (id_minor_data) / sizeof (id_minor_data[0]));
/*
* Later on need to incorporate the target number
* Right now in PCMCIA we have one disk per target,
* if and when we have disks that have multiple targets
* in the same socket (unlikely) then we will have multiple
* disks per socket.
*/
#ifdef ATA_DEBUG
if (pcata_debug & DMKDEV) {
"_create_device_node: "
"socket %d minor = %d minor_num = %d\n",
}
#endif
}
if (ret != CS_SUCCESS) {
"socket %d MakeDeviceNode failed %s (0x%x)\n",
}
/*
* We don't need this structure anymore since we've
* created the devices. If we need to keep
* track of the devices that we've created
* for some reason, then you' want to keep
* this structure and the make_device_node_t
* structure around in a global data area.
*/
return (ret);
}