view usr/src/uts/intel/io/dktp/controller/ata/ata_disk.c @ 3652:fe555d5eb825

PSARC 2006/410 ATA Update FW support 6275400 x86: ATA drive firmware download facility
author yt160523
date Wed, 14 Feb 2007 19:20:08 -0800
parents 5903aece022d
children 4e7870932cb2
line wrap: on
line source

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

#include <sys/types.h>
#include <sys/dkio.h>
#include <sys/cdio.h>
#include <sys/file.h>

#include "ata_common.h"
#include "ata_disk.h"

/*
 * this typedef really should be in dktp/cmpkt.h
 */
typedef struct cmpkt cmpkt_t;


/*
 * DADA entry points
 */

static int ata_disk_abort(opaque_t ctl_data, cmpkt_t *pktp);
static int ata_disk_reset(opaque_t ctl_data, int level);
static int ata_disk_ioctl(opaque_t ctl_data, int cmd, intptr_t a, int flag);
static cmpkt_t *ata_disk_pktalloc(opaque_t ctl_data, int (*callback)(caddr_t),
    caddr_t arg);
static void ata_disk_pktfree(opaque_t ctl_data, cmpkt_t *pktp);
static cmpkt_t	*ata_disk_memsetup(opaque_t ctl_data, cmpkt_t *pktp,
    struct buf *bp, int (*callback)(caddr_t), caddr_t arg);
static void ata_disk_memfree(opaque_t ctl_data, cmpkt_t *pktp);
static cmpkt_t	*ata_disk_iosetup(opaque_t ctl_data, cmpkt_t *pktp);
static int ata_disk_transport(opaque_t ctl_data, cmpkt_t *pktp);

/*
 * DADA packet callbacks
 */

static void ata_disk_complete(ata_drv_t *ata_drvp, ata_pkt_t *ata_pktp,
    int do_callback);
static int ata_disk_intr(ata_ctl_t *ata_ctlp, ata_drv_t *ata_drvp,
    ata_pkt_t *ata_pktp);
static int ata_disk_intr_dma(ata_ctl_t *ata_ctlp, ata_drv_t *ata_drvp,
    ata_pkt_t *ata_pktp);
static int ata_disk_intr_pio_in(ata_ctl_t *ata_ctlp, ata_drv_t *ata_drvp,
    ata_pkt_t *ata_pktp);
static int ata_disk_intr_pio_out(ata_ctl_t *ata_ctlp, ata_drv_t *ata_drvp,
    ata_pkt_t *ata_pktp);
static int ata_disk_start(ata_ctl_t *ata_ctlp, ata_drv_t *ata_drvp,
    ata_pkt_t *ata_pktp);
static int ata_disk_start_dma_in(ata_ctl_t *ata_ctlp, ata_drv_t *ata_drvp,
    ata_pkt_t *ata_pktp);
static int ata_disk_start_dma_out(ata_ctl_t *ata_ctlp, ata_drv_t *ata_drvp,
    ata_pkt_t *ata_pktp);
static int ata_disk_start_pio_in(ata_ctl_t *ata_ctlp, ata_drv_t *ata_drvp,
    ata_pkt_t *ata_pktp);
static int ata_disk_start_pio_out(ata_ctl_t *ata_ctlp, ata_drv_t *ata_drvp,
    ata_pkt_t *ata_pktp);

/*
 * Local Function prototypes
 */

static int ata_disk_eject(ata_ctl_t *ata_ctlp, ata_drv_t *ata_drvp,
    ata_pkt_t *ata_pktp);
static void ata_disk_fake_inquiry(ata_drv_t *ata_drvp);
static void ata_disk_get_resid(ata_ctl_t *ata_ctlp, ata_drv_t *ata_drvp,
    ata_pkt_t *ata_pktp);
static int ata_disk_initialize_device_parameters(ata_ctl_t *ata_ctlp,
    ata_drv_t *ata_drvp);
static int ata_disk_lock(ata_ctl_t *ata_ctlp, ata_drv_t *ata_drvp,
    ata_pkt_t *ata_pktp);
static int ata_disk_set_multiple(ata_ctl_t *ata_ctlp, ata_drv_t *ata_drvp);
static void ata_disk_pio_xfer_data_in(ata_ctl_t *ata_ctlp, ata_pkt_t *ata_pktp);
static void ata_disk_pio_xfer_data_out(ata_ctl_t *ata_ctlp,
    ata_pkt_t *ata_pktp);
static void ata_disk_set_standby_timer(ata_ctl_t *ata_ctlp,
    ata_drv_t *ata_drvp);
static int ata_disk_recalibrate(ata_ctl_t *ata_ctlp, ata_drv_t *ata_drvp,
    ata_pkt_t *ata_pktp);
static int ata_disk_standby(ata_ctl_t *ata_ctlp, ata_drv_t *ata_drvp,
    ata_pkt_t *ata_pktp);
static int ata_disk_start_common(ata_ctl_t *ata_ctlp, ata_drv_t *ata_drvp,
    ata_pkt_t *ata_pktp);
static int ata_disk_state(ata_ctl_t *ata_ctlp, ata_drv_t *ata_drvp,
    ata_pkt_t *ata_pktp);
static int ata_disk_unlock(ata_ctl_t *ata_ctlp, ata_drv_t *ata_drvp,
    ata_pkt_t *ata_pktp);
static int ata_get_capacity(ata_drv_t *ata_drvp, uint64_t *capacity);
static void ata_fix_large_disk_geometry(ata_drv_t *ata_drvp);
static uint64_t	ata_calculate_28bits_capacity(ata_drv_t *ata_drvp);
static uint64_t	ata_calculate_48bits_capacity(ata_drv_t *ata_drvp);
static int ata_copy_dk_ioc_string(intptr_t arg, char *source, int length,
    int flag);
static void ata_set_write_cache(ata_ctl_t *ata_ctlp, ata_drv_t *ata_drvp);
static int ata_disk_update_fw(gtgt_t *gtgtp, ata_ctl_t *ata_ctlp,
    ata_drv_t *ata_drvp, caddr_t fwfile, uint_t size,
    uint8_t type, int flag);
static int ata_disk_set_feature_spinup(ata_ctl_t *ata_ctlp,
    ata_drv_t *ata_drvp, ata_pkt_t *ata_pktp);
static int ata_disk_id_update(ata_ctl_t *ata_ctlp,
    ata_drv_t *ata_drvp, ata_pkt_t *ata_pktp);


/*
 * Local static data
 */

uint_t	ata_disk_init_dev_parm_wait = 4 * 1000000;
uint_t	ata_disk_set_mult_wait = 4 * 1000000;
int	ata_disk_do_standby_timer = TRUE;

/* timeout value for device update firmware */
int	ata_disk_updatefw_time = 60;

/*
 * ata_write_cache == 1  force write cache on.
 * ata_write_cache == 0  do not modify write cache.  firmware defaults kept.
 * ata_write_cache == -1 force write cache off.
 */
int	ata_write_cache = 1;


static struct ctl_objops ata_disk_objops = {
	ata_disk_pktalloc,
	ata_disk_pktfree,
	ata_disk_memsetup,
	ata_disk_memfree,
	ata_disk_iosetup,
	ata_disk_transport,
	ata_disk_reset,
	ata_disk_abort,
	nulldev,
	nulldev,
	ata_disk_ioctl,
	0, 0
};



/*
 *
 * initialize the ata_disk sub-system
 *
 */

/*ARGSUSED*/
int
ata_disk_attach(
	ata_ctl_t *ata_ctlp)
{
	ADBG_TRACE(("ata_disk_init entered\n"));
	return (TRUE);
}



/*
 *
 * destroy the ata_disk sub-system
 *
 */

/*ARGSUSED*/
void
ata_disk_detach(
	ata_ctl_t *ata_ctlp)
{
	ADBG_TRACE(("ata_disk_destroy entered\n"));
}


/*
 * Test whether the disk can support Logical Block Addressing
 */

int
ata_test_lba_support(struct ata_id *aidp)
{
#ifdef __old_version__
	/*
	 * determine if the drive supports LBA mode
	 */
	if (aidp->ai_cap & ATAC_LBA_SUPPORT)
		return (TRUE);
#else
	/*
	 * Determine if the drive supports LBA mode
	 * LBA mode is mandatory on ATA-3 (or newer) drives but is
	 * optional on ATA-2 (or older) drives. On ATA-2 drives
	 * the ai_majorversion word should be 0xffff or 0x0000
	 * (version not reported).
	 */
	if (aidp->ai_majorversion != 0xffff &&
	    aidp->ai_majorversion >= (1 << 3)) {
		/* ATA-3 or better */
		return (TRUE);
	} else if (aidp->ai_cap & ATAC_LBA_SUPPORT) {
		/* ATA-2 LBA capability bit set */
		return (TRUE);
	} else {
		return (FALSE);
	}
#endif
}

/*
 * ATA-6 drives do not provide geometry information, so words
 * ai_heads, ai_sectors and ai_fixcyls may not be valid
 */
static void
ata_fixup_ata6_geometry(struct ata_id *aidp)
{
	/* check cylinders, heads, and sectors for valid values */
	if (aidp->ai_heads != 0 && aidp->ai_heads != 0xffff &&
	    aidp->ai_sectors != 0 && aidp->ai_sectors != 0xffff &&
	    aidp->ai_fixcyls != 0)
		return;		/* assume valid geometry - do nothing */

	/*
	 * Pre-set standard geometry values - they are not necessarily
	 * optimal for a given capacity
	 */
	aidp->ai_heads = 0x10;
	aidp->ai_sectors = 0x3f;
	aidp->ai_fixcyls = 1;
	/*
	 * The fixcyls value will get fixed up later in
	 * ata_fix_large_disk_geometry.
	 */
}

/*
 *
 * initialize the soft-structure for an ATA (non-PACKET) drive and
 * then configure the drive with the correct modes and options.
 *
 */

int
ata_disk_init_drive(
	ata_drv_t *ata_drvp)
{
	ata_ctl_t *ata_ctlp = ata_drvp->ad_ctlp;
	struct ata_id	*aidp = &ata_drvp->ad_id;
	struct ctl_obj	*ctlobjp;
	struct scsi_device	*devp;
	int 		len;
	int		val;
	int		mode;
	short		*chs;
	char 		buf[80];

	ADBG_TRACE(("ata_disk_init_drive entered\n"));

	/* ATA disks don't support LUNs */

	if (ata_drvp->ad_lun != 0)
		return (FALSE);

	/*
	 * set up drive structure
	 * ATA-6 drives do not provide geometry information, so words
	 * ai_heads, ai_sectors and ai_fixcyls may not be valid - they
	 * will be fixed later
	 */

	ata_drvp->ad_phhd = aidp->ai_heads;
	ata_drvp->ad_phsec = aidp->ai_sectors;
	ata_drvp->ad_drvrhd   = aidp->ai_heads;
	ata_drvp->ad_drvrsec  = aidp->ai_sectors;
	ata_drvp->ad_drvrcyl  = aidp->ai_fixcyls;
	ata_drvp->ad_acyl = 0;

	if (ata_test_lba_support(&ata_drvp->ad_id))
		ata_drvp->ad_drive_bits |= ATDH_LBA;

	/* Get capacity and check for 48-bit mode */
	mode = ata_get_capacity(ata_drvp, &ata_drvp->ad_capacity);
	if (mode == AD_EXT48) {
		ata_drvp->ad_flags |= AD_EXT48;
	}

	/* straighten out the geometry */
	(void) sprintf(buf, "SUNW-ata-%p-d%d-chs", (void *) ata_ctlp->ac_data,
		ata_drvp->ad_targ+1);
	if (ddi_getlongprop(DDI_DEV_T_ANY, ddi_root_node(), 0,
			buf, (caddr_t)&chs, &len) == DDI_PROP_SUCCESS) {
		/*
		 * if the number of sectors and heads in bios matches the
		 * physical geometry, then so should the number of cylinders
		 * this is to prevent the 1023 limit in the older bios's
		 * causing loss of space.
		 */
		if (chs[1] == (ata_drvp->ad_drvrhd - 1) &&
				chs[2] == ata_drvp->ad_drvrsec)
			/* Set chs[0] to zero-based number of cylinders. */
			chs[0] = aidp->ai_fixcyls - 1;
		else if (!(ata_drvp->ad_drive_bits & ATDH_LBA)) {
			/*
			 * if the the sector/heads do not match that of the
			 * bios and the drive does not support LBA. We go ahead
			 * and advertise the bios geometry but use the physical
			 * geometry for sector translation.
			 */
			cmn_err(CE_WARN, "!Disk 0x%p,%d: BIOS geometry "
				"different from physical, and no LBA support.",
				(void *)ata_ctlp->ac_data, ata_drvp->ad_targ);
		}

		/*
		 * chs[0,1] are zero-based; make them one-based.
		 */
		ata_drvp->ad_drvrcyl = chs[0] + 1;
		ata_drvp->ad_drvrhd = chs[1] + 1;
		ata_drvp->ad_drvrsec = chs[2];
		kmem_free(chs, len);
	} else {
		/*
		 * Property not present; this means that boot.bin has
		 * determined that the drive supports Int13 LBA.  Note
		 * this, but just return a geometry with a large
		 * cylinder count; this will be the signal for dadk to
		 * fail DKIOCG_VIRTGEOM.
		 * ad_drvr* are already set; just recalculate ad_drvrcyl
		 * from capacity.
		 */

		ata_drvp->ad_flags |= AD_INT13LBA;
		if (ata_drvp->ad_capacity != 0) {
			ata_drvp->ad_drvrcyl = ata_drvp->ad_capacity /
				(ata_drvp->ad_drvrhd * ata_drvp->ad_drvrsec);
		} else {
			/*
			 * Something's wrong; return something sure to
			 * fail the "cyls < 1024" test.  This will
			 * never make it out of the DKIOCG_VIRTGEOM
			 * call, so its total bogosity won't matter.
			 */
			ata_drvp->ad_drvrcyl = 1025;
			ata_drvp->ad_drvrhd = 1;
			ata_drvp->ad_drvrsec = 1;
		}
	}

	/* fix geometry for disks > 31GB, if needed */
	ata_fix_large_disk_geometry(ata_drvp);

	/*
	 * set up the scsi_device and ctl_obj structures
	 */
	devp = &ata_drvp->ad_device;
	ctlobjp = &ata_drvp->ad_ctl_obj;

	devp->sd_inq = &ata_drvp->ad_inquiry;
	devp->sd_address.a_hba_tran = (scsi_hba_tran_t *)ctlobjp;
	devp->sd_address.a_target = (ushort_t)ata_drvp->ad_targ;
	devp->sd_address.a_lun = (uchar_t)ata_drvp->ad_lun;
	mutex_init(&devp->sd_mutex, NULL, MUTEX_DRIVER, NULL);
	ata_drvp->ad_flags |= AD_MUTEX_INIT;

	/*
	 * DADA ops vectors and cookie
	 */
	ctlobjp->c_ops  = (struct ctl_objops *)&ata_disk_objops;

	/*
	 * this is filled in with gtgtp by ata_disk_bus_ctl(INITCHILD)
	 */
	ctlobjp->c_data = NULL;

	ctlobjp->c_ext  = &(ctlobjp->c_extblk);
	ctlobjp->c_extblk.c_ctldip = ata_ctlp->ac_dip;
	ctlobjp->c_extblk.c_targ   = ata_drvp->ad_targ;
	ctlobjp->c_extblk.c_blksz  = NBPSCTR;

	/*
	 * Get highest block factor supported by the drive.
	 * Some drives report 0 if read/write multiple not supported,
	 * adjust their blocking factor to 1.
	 */
	ata_drvp->ad_block_factor = aidp->ai_mult1 & 0xff;

	/*
	 * If a block factor property exists, use the smaller of the
	 * property value and the highest value the drive can support.
	 */
	(void) sprintf(buf, "drive%d_block_factor", ata_drvp->ad_targ);
	val = ddi_prop_get_int(DDI_DEV_T_ANY, ata_ctlp->ac_dip, 0, buf,
		ata_drvp->ad_block_factor);

	ata_drvp->ad_block_factor = (short)min(val, ata_drvp->ad_block_factor);

	if (ata_drvp->ad_block_factor == 0)
		ata_drvp->ad_block_factor = 1;

	if (!ata_disk_setup_parms(ata_ctlp, ata_drvp))
		return (FALSE);

	ata_disk_fake_inquiry(ata_drvp);

	return (TRUE);
}

/*
 * Test if a disk supports 48-bit (extended mode) addressing and
 * get disk capacity.
 * Return value:
 *	AD_EXT48 if 48-bit mode is available, 0 otherwise,
 *	capacity in sectors.
 * There are several indicators for 48-bit addressing.  If any of
 * them is missing, assume 28-bit (non-extended) addressing.
 */

static int
ata_get_capacity(ata_drv_t *ata_drvp, uint64_t *capacity)
{
	struct ata_id	*aidp = &ata_drvp->ad_id;
	uint64_t	cap28;	/* capacity in 28-bit mode */
	uint64_t	cap48;	/* capacity in 48-bit mode */

	/*
	 * First compute capacity in 28-bit mode, using 28-bit capacity
	 * words in IDENTIFY DEVICE response words
	 */
	cap28 = ata_calculate_28bits_capacity(ata_drvp);
	*capacity = cap28;

	/* No 48-bit mode before ATA 6 */
	if (!IS_ATA_VERSION_SUPPORTED(aidp, 6))
		return (0);

	/* Check that 48 bit addressing is supported & enabled */
	/* words 83 and 86 */
	if (!(aidp->ai_cmdset83 & ATACS_EXT48))
		return (0);
	if (!(aidp->ai_features86 & ATACS_EXT48))
		return (0);

	/*
	 * Drive supports ATA-6.  Since ATA-6 drives may not provide
	 * geometry info, pre-set standard geometry values
	 */
	ata_fixup_ata6_geometry(aidp);

	/* Compute 48-bit capacity */
	cap48 = ata_calculate_48bits_capacity(ata_drvp);

	/*
	 * If capacity is smaller then the maximum capacity addressable
	 * in 28-bit mode, just use 28-bit capacity value.
	 * We will use 28-bit addressing read/write commands.
	 */
	if (cap48 <= MAX_28BIT_CAPACITY)
		return (0);

	/*
	 * Capacity is too big for 28-bits addressing. But, to make
	 * sure that the drive implements ATA-6 correctly, the
	 * final check: cap28 should be MAX for 28-bit addressing.
	 * If it's not, we shouldn't use 48-bit mode, so return
	 * the capacity reported in 28-bit capacity words.
	 */
	if (cap28 != MAX_28BIT_CAPACITY)
		return (0);		/* not max, use 28-bit value */

	/*
	 * All is well so return 48-bit capacity indicator
	 */
	ADBG_INIT(("ATA: using 48-bit mode for capacity %llx blocks\n",
		(unsigned long long)cap48));

	*capacity = cap48;
	return (AD_EXT48);
}

/*
 * With the advent of disks that hold more than 31 GB, we run into a
 * limitation in the sizes of the fields that describe the geometry.
 * The cylinders, heads, and sectors-per-track are each described by a
 * 16-bit number -- both in the structure returned from IDENTIFY
 * DEVICE and in the structure returned from the DIOCTL_GETGEOM or
 * DIOCTL_GETPHYGEOM ioctl.
 *
 * The typical disk has 32 heads per cylinder and 63 sectors per
 * track.  A 16 bit field can contain up to 65535.  So the largest
 * disk that can be described in these fields is 65535 * 32 * 63 * 512
 * (bytes/sector), or about 31.5 GB.  The cylinder count gets truncated
 * when stored in a narrow field, so a 40GB disk appears to have only
 * 8 GB!
 *
 * The solution (for the time being at least) is to lie about the
 * geometry.  If the number of cylinders is too large to fit in 16
 * bits, we will halve the cylinders and double the heads, repeating
 * until we can fit the geometry into 3 shorts.
 * FUTURE ENHANCEMENT: If this ever isn't enough, we could
 * add another step to double sectors/track as well.
 */

static void
ata_fix_large_disk_geometry(
	ata_drv_t *ata_drvp)
{
	struct ata_id	*aidp = &ata_drvp->ad_id;

	/* no hope for large disks if LBA not supported */
	if (!(ata_drvp->ad_drive_bits & ATDH_LBA))
		return;

	/*
	 * Fix up the geometry to be returned by DIOCTL_GETGEOM.
	 * If number of cylinders > USHRT_MAX, double heads and
	 * halve cylinders until everything fits.
	 */
	while (ata_drvp->ad_drvrcyl > USHRT_MAX) {
		int tempheads;

		/* is there room in 16 bits to double the heads? */
		tempheads = 2 * ata_drvp->ad_drvrhd;
		if (tempheads > USHRT_MAX) {
			/*
			 * No room to double the heads.
			 * I give up, there's no way to represent this.
			 * Limit disk size.
			 */
			cmn_err(CE_WARN,
				"Disk is too large: "
					"Model %s, Serial# %s "
					"Approximating...\n",
				aidp->ai_model, aidp->ai_drvser);
			ata_drvp->ad_drvrcyl = USHRT_MAX;
			break;
		}

		/* OK, so double the heads and halve the cylinders */
		ata_drvp->ad_drvrcyl /= 2;
		ata_drvp->ad_drvrhd *= 2;
	}
}

/*
 * Calculate capacity using 28-bit capacity words from IDENTIFY DEVICE
 * return words
 */
uint64_t
ata_calculate_28bits_capacity(ata_drv_t *ata_drvp)
{
	/*
	 * Asked x3t13 for advice; this implements Hale Landis'
	 * response, minus the "use ATA_INIT_DEVPARMS".
	 * See "capacity.notes".
	 */

	/* some local shorthand/renaming to clarify the meaning */

	ushort_t curcyls_w54, curhds_w55, cursect_w56;
	uint32_t curcap_w57_58;

	if ((ata_drvp->ad_drive_bits & ATDH_LBA) != 0) {
		return ((uint64_t)(ata_drvp->ad_id.ai_addrsec[0] +
		    ata_drvp->ad_id.ai_addrsec[1] * 0x10000));
	}

	/*
	 * If we're not LBA, then first try to validate "current" values.
	 */

	curcyls_w54 = ata_drvp->ad_id.ai_curcyls;
	curhds_w55 = ata_drvp->ad_id.ai_curheads;
	cursect_w56 = ata_drvp->ad_id.ai_cursectrk;
	curcap_w57_58 = ata_drvp->ad_id.ai_cursccp[0] +
	    ata_drvp->ad_id.ai_cursccp[1] * 0x10000;

	if (((ata_drvp->ad_id.ai_validinfo & 1) == 1) &&
	    (curhds_w55 >= 1) && (curhds_w55 <= 16) &&
	    (cursect_w56 >= 1) && (cursect_w56 <= 63) &&
	    (curcap_w57_58 == curcyls_w54 * curhds_w55 * cursect_w56)) {
		return ((uint64_t)curcap_w57_58);
	}

	/*
	 * At this point, Hale recommends ATA_INIT_DEVPARMS.
	 * I don't want to do that, so simply use 1/3/6 as
	 * a final fallback, and continue to assume the BIOS
	 * has done whatever INIT_DEVPARMS are necessary.
	 */

	return ((uint64_t)(ata_drvp->ad_id.ai_fixcyls *
		ata_drvp->ad_id.ai_heads * ata_drvp->ad_id.ai_sectors));
}

/*
 * Calculate capacity using 48-bits capacity words from IDENTIFY DEVICE
 * return words
 */
uint64_t
ata_calculate_48bits_capacity(ata_drv_t *ata_drvp)
{
	uint64_t cap48 = 0;
	int i;

	for (i = 3;  i >= 0;  --i) {
		cap48 <<= 16;
		cap48 += ata_drvp->ad_id.ai_addrsecxt[i];
	}
	return (cap48);
}


/*
 *
 * Setup the drives Read/Write Multiple Blocking factor and the
 * current translation geometry. Necessary during attach and after
 * Software Resets.
 *
 */

int
ata_disk_setup_parms(
	ata_ctl_t *ata_ctlp,
	ata_drv_t *ata_drvp)
{

	/*
	 * program geometry info back to the drive
	 */
	if (!ata_disk_initialize_device_parameters(ata_ctlp, ata_drvp)) {
		return (FALSE);
	}

	/*
	 * Determine the blocking factor
	 */
	if (ata_drvp->ad_block_factor > 1) {
		/*
		 * Program the block factor into the drive. If this
		 * fails, then go back to using a block size of 1.
		 */
		if (!ata_disk_set_multiple(ata_ctlp, ata_drvp))
			ata_drvp->ad_block_factor = 1;
	}


	if (ata_drvp->ad_block_factor > 1) {
		ata_drvp->ad_rd_cmd = ATC_RDMULT;
		ata_drvp->ad_wr_cmd = ATC_WRMULT;
	} else {
		ata_drvp->ad_rd_cmd = ATC_RDSEC;
		ata_drvp->ad_wr_cmd = ATC_WRSEC;
	}

	ata_drvp->ad_bytes_per_block = ata_drvp->ad_block_factor << SCTRSHFT;

	ADBG_INIT(("set block factor for drive %d to %d\n",
			ata_drvp->ad_targ, ata_drvp->ad_block_factor));

	if (ata_disk_do_standby_timer)
		ata_disk_set_standby_timer(ata_ctlp, ata_drvp);

	ata_set_write_cache(ata_ctlp, ata_drvp);

	return (TRUE);
}


/*
 * Take the timeout value specified in the "standby" property
 * and convert from seconds to the magic parm expected by the
 * the drive. Then issue the IDLE command to set the drive's
 * internal standby timer.
 */

static void
ata_disk_set_standby_timer(
	ata_ctl_t *ata_ctlp,
	ata_drv_t *ata_drvp)
{
	uchar_t	parm;
	int	timeout = ata_ctlp->ac_standby_time;

	/*
	 * take the timeout value, specificed in seconds, and
	 * encode it into the proper command parm
	 */

	/*
	 * don't change it if no property specified or if
	 * the specified value is out of range
	 */
	if (timeout < 0 || timeout > (12 * 60 * 60))
		return;

	/* 1 to 1200 seconds (20 minutes) == N * 5 seconds */
	if (timeout <= (240 * 5))
		parm = (timeout + 4) / 5;

	/* 20 to 21 minutes == 21 minutes */
	else if (timeout <= (21 * 60))
		parm = 252;

	/* 21 minutes to 21 minutes 15 seconds == 21:15 */
	else if (timeout <= ((21 * 60) + 15))
		parm = 255;

	/* 21:15 to 330 minutes == N * 30 minutes */
	else if (timeout <= (11 * 30 * 60))
		parm = 240 + ((timeout + (30 * 60) - 1)/ (30 * 60));

	/* > 330 minutes == 8 to 12 hours */
	else
		parm = 253;

	(void) ata_command(ata_ctlp, ata_drvp, TRUE, FALSE, 5 * 1000000,
		    ATC_IDLE, 0, parm, 0, 0, 0, 0);
}



/*
 *
 * destroy an ata disk drive
 *
 */

void
ata_disk_uninit_drive(
	ata_drv_t *ata_drvp)
{
	struct scsi_device *devp = &ata_drvp->ad_device;

	ADBG_TRACE(("ata_disk_uninit_drive entered\n"));

	if (ata_drvp->ad_flags & AD_MUTEX_INIT)
		mutex_destroy(&devp->sd_mutex);
}




/*
 *
 * DADA compliant bus_ctl entry point
 *
 */

/*ARGSUSED*/
int
ata_disk_bus_ctl(
	dev_info_t	*d,
	dev_info_t	*r,
	ddi_ctl_enum_t	 o,
	void		*a,
	void		*v)
{
	ADBG_TRACE(("ata_disk_bus_ctl entered\n"));

	switch (o) {

	case DDI_CTLOPS_REPORTDEV:
	{
		int	targ;

		targ = ddi_prop_get_int(DDI_DEV_T_ANY, r, DDI_PROP_DONTPASS,
					"target", 0);
		cmn_err(CE_CONT, "?%s%d at %s%d target %d lun %d\n",
			ddi_driver_name(r), ddi_get_instance(r),
			ddi_driver_name(d), ddi_get_instance(d), targ, 0);
		return (DDI_SUCCESS);
	}
	case DDI_CTLOPS_INITCHILD:
	{
		dev_info_t	*cdip = (dev_info_t *)a;
		ata_drv_t	*ata_drvp;
		ata_ctl_t	*ata_ctlp;
		ata_tgt_t	*ata_tgtp;
		struct scsi_device *devp;
		struct ctl_obj	*ctlobjp;
		gtgt_t		*gtgtp;
		char		 name[MAXNAMELEN];

		/*
		 * save time by picking up ptr to drive struct left
		 * by ata_bus_ctl - isn't that convenient.
		 */
		ata_drvp = ddi_get_driver_private(cdip);
		ata_ctlp = ata_drvp->ad_ctlp;

		/* set up pointers to child dip */

		devp = &ata_drvp->ad_device;
		/*
		 * If sd_dev is set, it means that the target has already
		 * being initialized. The cdip is a duplicate node from
		 * reexpansion of driver.conf. Fail INITCHILD here.
		 */
		if (devp->sd_dev != NULL) {
			return (DDI_FAILURE);
		}
		devp->sd_dev = cdip;

		ctlobjp = &ata_drvp->ad_ctl_obj;
		ctlobjp->c_extblk.c_devdip = cdip;

		/*
		 * Create the "ata" property for use by the target driver
		 */
		if (!ata_prop_create(cdip, ata_drvp, "ata")) {
			return (DDI_FAILURE);
		}

		gtgtp = ghd_target_init(d, cdip, &ata_ctlp->ac_ccc,
					sizeof (ata_tgt_t), ata_ctlp,
					ata_drvp->ad_targ,
					ata_drvp->ad_lun);

		/* gt_tgt_private points to ata_tgt_t */
		ata_tgtp = GTGTP2ATATGTP(gtgtp);
		ata_tgtp->at_drvp = ata_drvp;
		ata_tgtp->at_dma_attr = ata_pciide_dma_attr;
		ata_tgtp->at_dma_attr.dma_attr_maxxfer =
				ata_ctlp->ac_max_transfer << SCTRSHFT;

		/* gtgtp is the opaque arg to all my entry points */
		ctlobjp->c_data = gtgtp;

		/* create device name */

		(void) sprintf(name, "%x,%x", ata_drvp->ad_targ,
			ata_drvp->ad_lun);
		ddi_set_name_addr(cdip, name);
		ddi_set_driver_private(cdip, devp);

		return (DDI_SUCCESS);
	}

	case DDI_CTLOPS_UNINITCHILD:
	{
		dev_info_t *cdip = (dev_info_t *)a;
		struct 	scsi_device *devp;
		struct	ctl_obj *ctlobjp;
		gtgt_t	*gtgtp;

		devp = ddi_get_driver_private(cdip);
		ctlobjp = (struct ctl_obj *)devp->sd_address.a_hba_tran;
		gtgtp = ctlobjp->c_data;

		ghd_target_free(d, cdip, &GTGTP2ATAP(gtgtp)->ac_ccc, gtgtp);

		ddi_set_driver_private(cdip, NULL);
		ddi_set_name_addr(cdip, NULL);
		return (DDI_SUCCESS);
	}

	default:
		return (DDI_FAILURE);
	}
}


/*
 *
 * DADA abort entry point - not currently used by dadk
 *
 */

/* ARGSUSED */
static int
ata_disk_abort(opaque_t ctl_data, cmpkt_t *pktp)
{
	ADBG_TRACE(("ata_disk_abort entered\n"));

	/* XXX - Note that this interface is currently not used by dadk */

	/*
	 *  GHD abort functions take a pointer to a scsi_address
	 *  and so they're unusable here.  The ata driver used to
	 *  return DDI_SUCCESS here without doing anything.  Its
	 *  seems that DDI_FAILURE is more appropriate.
	 */

	return (DDI_FAILURE);
}



/*
 *
 * DADA reset entry point - not currently used by dadk
 * (except in debug versions of driver)
 *
 */

/* ARGSUSED */
static int
ata_disk_reset(opaque_t ctl_data, int level)
{
	gtgt_t		*gtgtp = (gtgt_t *)ctl_data;
	ata_drv_t	*ata_drvp = GTGTP2ATADRVP(gtgtp);
	int		rc;

	ADBG_TRACE(("ata_disk_reset entered\n"));

	/* XXX - Note that this interface is currently not used by dadk */

	if (level == RESET_TARGET) {
		rc = ghd_tran_reset_target(&ata_drvp->ad_ctlp->ac_ccc, gtgtp,
			NULL);
	} else if (level == RESET_ALL) {
		rc = ghd_tran_reset_bus(&ata_drvp->ad_ctlp->ac_ccc, gtgtp,
					NULL);
	}

	return (rc ? DDI_SUCCESS : DDI_FAILURE);
}



/*
 *
 * DADA ioctl entry point
 *
 */

/* ARGSUSED */
static int
ata_disk_ioctl(opaque_t ctl_data, int cmd, intptr_t arg, int flag)
{
	gtgt_t		*gtgtp = (gtgt_t *)ctl_data;
	ata_ctl_t	*ata_ctlp = GTGTP2ATAP(gtgtp);
	ata_drv_t	*ata_drvp = GTGTP2ATADRVP(gtgtp);
	int		rc, rc2;
	struct tgdk_geom tgdk;
	int		wce;
	struct ata_id	*aidp = &ata_drvp->ad_id;
	dk_updatefw_t	updatefw;
#ifdef _MULTI_DATAMODEL
	dk_updatefw_32_t updatefw32;
#endif
	dk_disk_id_t	dk_disk_id;
	char		buf[80];
	int		i;


	ADBG_TRACE(("ata_disk_ioctl entered, cmd = %d\n", cmd));

	switch (cmd) {

	case DIOCTL_GETGEOM:
	case DIOCTL_GETPHYGEOM:
		tgdk.g_cyl = ata_drvp->ad_drvrcyl;
		tgdk.g_head = ata_drvp->ad_drvrhd;
		tgdk.g_sec = ata_drvp->ad_drvrsec;
		tgdk.g_acyl = ata_drvp->ad_acyl;
		tgdk.g_secsiz = 512;
		tgdk.g_cap = tgdk.g_cyl * tgdk.g_head * tgdk.g_sec;
		if (ddi_copyout(&tgdk, (caddr_t)arg, sizeof (tgdk), flag))
			return (EFAULT);
		return (0);

	case DCMD_UPDATE_GEOM:
/* ??? fix this to issue IDENTIFY DEVICE ??? */
/* might not be necessary since I don't know of any ATA/IDE that */
/* can change its geometry. On the other hand, ATAPI devices like the  */
/* LS-120 or PD/CD can change their geometry when new media is inserted */
		return (0);

	/* copy the model number into the caller's buffer */
	case DIOCTL_GETMODEL:
		rc = ata_copy_dk_ioc_string(arg, aidp->ai_model,
					sizeof (aidp->ai_model), flag);
		return (rc);

	/* copy the serial number into the caller's buffer */
	case DIOCTL_GETSERIAL:
		rc = ata_copy_dk_ioc_string(arg, aidp->ai_drvser,
					sizeof (aidp->ai_drvser),
					flag);
		return (rc);

	case DIOCTL_GETWCE:
		/*
		 * WCE is only supported in ATAPI-4 or higher, for
		 * lower rev devices, must assume write cache is
		 * enabled.
		 * NOTE: Since there is currently no Solaris mechanism
		 * to change the state of the Write Cache Enable feature,
		 * this code just checks the value of the WCE bit
		 * obtained at device init time.  If a mechanism
		 * is added to the driver to change WCE, this code
		 * must be updated appropriately.
		 */
		wce = (aidp->ai_majorversion == 0xffff) ||
			((aidp->ai_majorversion & ATAC_MAJVER_4) == 0) ||
			(aidp->ai_features85 & ATAC_FEATURES85_WCE) != 0;

		if (ddi_copyout(&wce, (caddr_t)arg, sizeof (wce), flag) != 0)
			return (EFAULT);

		return (0);

	case DCMD_GET_STATE:
		rc = ata_queue_cmd(ata_disk_state, NULL, ata_ctlp, ata_drvp,
			gtgtp);
		break;

	case DCMD_LOCK:
	case DKIOCLOCK:
		rc = ata_queue_cmd(ata_disk_lock, NULL, ata_ctlp, ata_drvp,
			gtgtp);
		break;

	case DCMD_UNLOCK:
	case DKIOCUNLOCK:
		rc = ata_queue_cmd(ata_disk_unlock, NULL, ata_ctlp, ata_drvp,
			gtgtp);
		break;

	case DCMD_START_MOTOR:
	case CDROMSTART:
		rc = ata_queue_cmd(ata_disk_recalibrate, NULL, ata_ctlp,
			ata_drvp, gtgtp);
		break;

	case DCMD_STOP_MOTOR:
	case CDROMSTOP:
		rc = ata_queue_cmd(ata_disk_standby, NULL, ata_ctlp, ata_drvp,
			gtgtp);
		break;

	case DKIOCEJECT:
	case CDROMEJECT:
		rc = ata_queue_cmd(ata_disk_eject, NULL, ata_ctlp, ata_drvp,
			gtgtp);
		break;

	case DKIOC_UPDATEFW:

		/*
		 * Call DOWNLOAD MICROCODE command to update device
		 * firmware.
		 *
		 * return value:
		 *   normal	0	Download microcode success
		 *   error	EFAULT	Bad address
		 *		ENXIO	No such device or address
		 *		EINVAL	Invalid argument
		 *		ENOMEM	Not enough core
		 *		ENOTSUP	Operation not supported
		 *		EIO	I/O error
		 *		EPERM	Not owner
		 */

		/*
		 * The following code deals with handling 32-bit request
		 * in 64-bit kernel.
		 */
#ifdef _MULTI_DATAMODEL
		if (ddi_model_convert_from(flag & FMODELS) ==
		    DDI_MODEL_ILP32) {
			if (ddi_copyin((void *)arg, &updatefw32,
			    sizeof (dk_updatefw_32_t), flag))
				return (EFAULT);

			updatefw.dku_ptrbuf =
			    (caddr_t)(uintptr_t)updatefw32.dku_ptrbuf;
			updatefw.dku_size = updatefw32.dku_size;
			updatefw.dku_type = updatefw32.dku_type;
		} else {
			if (ddi_copyin((void *)arg, &updatefw,
			    sizeof (dk_updatefw_t), flag))
				return (EFAULT);
		}
#else
		if (ddi_copyin((void *)arg, &updatefw,
		    sizeof (dk_updatefw_t), flag))
			return (EFAULT);
#endif
		rc = ata_disk_update_fw(gtgtp, ata_ctlp, ata_drvp,
		    updatefw.dku_ptrbuf, updatefw.dku_size,
		    updatefw.dku_type, flag);

		/*
		 * According to ATA8-ACS spec, the new microcode should
		 * become effective immediately after the transfer of the
		 * last data segment has completed, so here we will call
		 * IDENTIFY DEVICE command immediately to update
		 * ata_id content when success.
		 */
		if (rc == 0) {
			rc2 = ata_queue_cmd(ata_disk_id_update, NULL,
			    ata_ctlp, ata_drvp, gtgtp);
			if (rc2 != TRUE) {
				return (ENXIO);
			} else {
				/*
				 * Check whether the content of the IDENTIFY
				 * DEVICE data is incomplete, if yes, it's
				 * because the device supports the Power-up
				 * in Standby feature set, and we will first
				 * check word 2, and then decide whether need
				 * to call set feature to spin-up the device,
				 * and then call IDENTIFY DEVICE command again.
				 */
				aidp = &ata_drvp->ad_id;
				if (aidp->ai_config & ATA_ID_INCMPT) {
					if (aidp->ai_resv0 == 0x37c8 ||
					    aidp->ai_resv0 == 0x738c) {
						/* Spin-up the device */
						(void) ata_queue_cmd(
						    ata_disk_set_feature_spinup,
						    NULL,
						    ata_ctlp,
						    ata_drvp,
						    gtgtp);
					}

					/* Try to update ata_id again */
					rc2 = ata_queue_cmd(
					    ata_disk_id_update,
					    NULL,
					    ata_ctlp,
					    ata_drvp,
					    gtgtp);
					if (rc2 != TRUE) {
						return (ENXIO);
					} else {
						aidp = &ata_drvp->ad_id;
						if (aidp->ai_config &
						    ATA_ID_INCMPT)
							return (ENXIO);
					}
				}

				/*
				 * Dump the drive information.
				 */
				ATAPRT(("?\tUpdate firmware of %s device at "
				    "targ %d, lun %d lastlun 0x%x\n",
				    (ATAPIDRV(ata_drvp) ? "ATAPI":"IDE"),
				    ata_drvp->ad_targ, ata_drvp->ad_lun,
				    aidp->ai_lastlun));

				(void) strncpy(buf, aidp->ai_model,
				    sizeof (aidp->ai_model));
				buf[sizeof (aidp->ai_model)] = '\0';
				for (i = sizeof (aidp->ai_model) - 1;
				    buf[i] == ' '; i--)
					buf[i] = '\0';
				ATAPRT(("?\tmodel %s\n", buf));

				(void) strncpy(buf, aidp->ai_fw,
				    sizeof (aidp->ai_fw));
				buf[sizeof (aidp->ai_fw)] = '\0';
				for (i = sizeof (aidp->ai_fw) - 1;
				    buf[i] == ' '; i--)
					buf[i] = '\0';
				ATAPRT(("?\tfw %s\n", buf));
			}
		}
		return (rc);

	case DKIOC_GETDISKID:
		bzero(&dk_disk_id, sizeof (dk_disk_id_t));

		dk_disk_id.dkd_dtype = DKD_ATA_TYPE;

		/* Get the model number */
		(void) strncpy(dk_disk_id.disk_id.ata_disk_id.dkd_amodel,
		    aidp->ai_model, sizeof (aidp->ai_model));

		/* Get the firmware revision */
		(void) strncpy(dk_disk_id.disk_id.ata_disk_id.dkd_afwver,
		    aidp->ai_fw, sizeof (aidp->ai_fw));

		/* Get the serial number */
		(void) strncpy(dk_disk_id.disk_id.ata_disk_id.dkd_aserial,
		    aidp->ai_drvser, sizeof (aidp->ai_drvser));

		if (ddi_copyout(&dk_disk_id, (void *)arg,
		    sizeof (dk_disk_id_t), flag))
			return (EFAULT);
		else
			return (0);

	default:
		ADBG_WARN(("ata_disk_ioctl: unsupported cmd 0x%x\n", cmd));
		return (ENOTTY);
	}

	if (rc)
		return (0);
	return (ENXIO);

}


#ifdef ___not___used___
/*
 * Issue an ATA command to the drive using the packet already
 * allocated by the target driver
 */

int
ata_disk_do_ioctl(
	int	(*func)(ata_ctl_t *, ata_drv_t *, ata_pkt_t *),
	void	  *arg,
	ata_ctl_t *ata_ctlp,
	gtgt_t	  *gtgtp,
	cmpkt_t   *pktp)
{
	gcmd_t	  *gcmdp = CPKT2GCMD(pktp);
	ata_pkt_t *ata_pktp = GCMD2APKT(gcmdp);
	int	   rc;

	ata_pktp->ap_start = func;
	ata_pktp->ap_intr = NULL;
	ata_pktp->ap_complete = NULL;
	ata_pktp->ap_v_addr = (caddr_t)arg;

	/*
	 * add it to the queue, when it gets to the front the
	 * ap_start function is called.
	 */
	rc = ghd_transport(&ata_ctlp->ac_ccc, gcmdp, gcmdp->cmd_gtgtp,
		0, TRUE, NULL);

	if (rc != TRAN_ACCEPT) {
		/* this should never, ever happen */
		return (ENXIO);
	}

	if (ata_pktp->ap_flags & AP_ERROR)
		return (ENXIO);
	return (0);
}
#endif



/*
 *
 * DADA pktalloc entry point
 *
 */

/* ARGSUSED */
static cmpkt_t *
ata_disk_pktalloc(opaque_t ctl_data, int (*callback)(caddr_t), caddr_t arg)
{
	gtgt_t		*gtgtp = (gtgt_t *)ctl_data;
	ata_drv_t	*ata_drvp = GTGTP2ATADRVP(gtgtp);
	cmpkt_t		*pktp;
	ata_pkt_t	*ata_pktp;
	gcmd_t		*gcmdp;

	ADBG_TRACE(("ata_disk_pktalloc entered\n"));

	/*
	 * Allocate and  init the GHD gcmd_t structure and the
	 * DADA cmpkt and the ata_pkt
	 */
	if ((gcmdp = ghd_gcmd_alloc(gtgtp,
				    (sizeof (cmpkt_t) + sizeof (ata_pkt_t)),
				    (callback == DDI_DMA_SLEEP))) == NULL) {
		return ((cmpkt_t *)NULL);
	}
	ASSERT(gcmdp != NULL);

	ata_pktp = GCMD2APKT(gcmdp);
	ASSERT(ata_pktp != NULL);

	pktp = (cmpkt_t *)(ata_pktp + 1);

	pktp->cp_ctl_private = (void *)gcmdp;
	ata_pktp->ap_gcmdp = gcmdp;
	gcmdp->cmd_pktp = (void *)pktp;

	/*
	 * At this point the structures are linked like this:
	 *
	 *	(struct cmpkt) <--> (struct gcmd) <--> (struct ata_pkt)
	 */

	/* callback functions */

	ata_pktp->ap_start = ata_disk_start;
	ata_pktp->ap_intr = ata_disk_intr;
	ata_pktp->ap_complete = ata_disk_complete;

	/* other ata_pkt setup */

	ata_pktp->ap_bytes_per_block = ata_drvp->ad_bytes_per_block;

	/* cmpkt setup */

	pktp->cp_cdblen = 1;
	pktp->cp_cdbp   = (opaque_t)&ata_pktp->ap_cdb;
	pktp->cp_scbp   = (opaque_t)&ata_pktp->ap_scb;
	pktp->cp_scblen = 1;

	return (pktp);
}



/*
 *
 * DADA pktfree entry point
 *
 */

/* ARGSUSED */
static void
ata_disk_pktfree(opaque_t ctl_data, cmpkt_t *pktp)
{
	ata_pkt_t *ata_pktp = CPKT2APKT(pktp);

	ADBG_TRACE(("ata_disk_pktfree entered\n"));

	/* check not free already */

	ASSERT(!(ata_pktp->ap_flags & AP_FREE));
	ata_pktp->ap_flags = AP_FREE;

	ghd_gcmd_free(CPKT2GCMD(pktp));
}


/*
 *
 * DADA memsetup entry point
 *
 */

/* ARGSUSED */
static cmpkt_t *
ata_disk_memsetup(
	opaque_t ctl_data,
	cmpkt_t *pktp,
	struct buf *bp,
	int (*callback)(caddr_t),
	caddr_t arg)
{
	gtgt_t		*gtgtp = (gtgt_t *)ctl_data;
	ata_pkt_t	*ata_pktp = CPKT2APKT(pktp);
	gcmd_t		*gcmdp = APKT2GCMD(ata_pktp);
	int		flags;

	ADBG_TRACE(("ata_disk_memsetup entered\n"));

	ata_pktp->ap_sg_cnt = 0;

	if (bp->b_bcount == 0) {
		ata_pktp->ap_v_addr = NULL;
		return (pktp);
	}

	if (GTGTP2ATADRVP(gtgtp)->ad_pciide_dma != ATA_DMA_ON)
		goto skip_dma_setup;

	if (ata_dma_disabled)
		goto skip_dma_setup;

	/*
	 * The PCI-IDE DMA engine is brain-damaged and can't
	 * DMA non-aligned buffers.
	 */
	if (!(bp->b_flags & B_PAGEIO) &&
	    ((uintptr_t)bp->b_un.b_addr) & PCIIDE_PRDE_ADDR_MASK) {
		goto skip_dma_setup;
	}

	/*
	 * It also insists that the byte count must be even.
	 */
	if (bp->b_bcount & 1)
		goto skip_dma_setup;

	/* check direction for data transfer */
	if (bp->b_flags & B_READ) {
		flags = DDI_DMA_READ | DDI_DMA_PARTIAL;
	} else {
		flags = DDI_DMA_WRITE | DDI_DMA_PARTIAL;
	}

	/*
	 * Bind the DMA handle to the buf
	 */
	if (ghd_dma_buf_bind_attr(&GTGTP2ATAP(gtgtp)->ac_ccc, gcmdp, bp, flags,
			callback, arg, &GTGTP2ATATGTP(gtgtp)->at_dma_attr)) {
		ata_pktp->ap_v_addr = 0;
		return (pktp);
	}

skip_dma_setup:
	bp_mapin(bp);
	ata_pktp->ap_v_addr = bp->b_un.b_addr;
	return (pktp);
}



/*
 *
 * DADA memfree entry point
 *
 */

/*
 * 1157317 sez that drivers shouldn't call bp_mapout(), as either
 * biodone() or biowait() will end up doing it, but after they
 * call bp->b_iodone(), which is a necessary sequence for
 * Online Disk Suite.  However, the DDI group wants to rethink
 * bp_mapin()/bp_mapout() and how they should behave in the
 * presence of layered drivers, etc.  For the moment, fix
 * the OLDS problem by removing the bp_mapout() call.
 */

#define	BUG_1157317

/* ARGSUSED */
static void
ata_disk_memfree(opaque_t ctl_data, cmpkt_t *pktp)
{
	gcmd_t	*gcmdp = CPKT2GCMD(pktp);

	ADBG_TRACE(("ata_disk_memfree entered\n"));

	if (gcmdp->cmd_dma_handle)
		ghd_dmafree_attr(gcmdp);
#if !defined(BUG_1157317)
	else
		bp_mapout(pktp->cp_bp);
#endif
}



/*
 *
 * DADA iosetup entry point
 *
 */

static cmpkt_t *
ata_disk_iosetup(opaque_t ctl_data, cmpkt_t *pktp)
{
	gtgt_t		*gtgtp = (gtgt_t *)ctl_data;
	ata_drv_t	*ata_drvp = GTGTP2ATADRVP(gtgtp);
	ata_pkt_t	*ata_pktp = CPKT2APKT(pktp);
	gcmd_t		*gcmdp = APKT2GCMD(ata_pktp);
	uint_t		sec_count;
	daddr_t		start_sec;
	uint_t		byte_count;

	ADBG_TRACE(("ata_disk_iosetup entered\n"));

	/*
	 * Check for DCMD_FLUSH_CACHE (which does no I/O) and
	 * just do basic setup.
	 */
	if (pktp->cp_passthru == NULL &&
	    ata_pktp->ap_cdb == DCMD_FLUSH_CACHE) {
		ata_pktp->ap_cmd = ATC_FLUSH_CACHE;
		ata_pktp->ap_flags = 0;
		ata_pktp->ap_count = 0;
		ata_pktp->ap_startsec = 0;
		ata_pktp->ap_sg_cnt = 0;
		ata_pktp->ap_pciide_dma = FALSE;
		return (pktp);
	}

	/* check for error retry */
	if (ata_pktp->ap_flags & AP_ERROR) {
		/*
		 * this is a temporary work-around for dadk calling
		 * iosetup for retry. The correct
		 * solution is changing dadk to not to call iosetup
		 * for a retry.
		 * We do not apply the work-around for pio mode since
		 * that does not involve moving dma windows and reducing the
		 * sector count would work for pio mode on a retry
		 * for now.
		 */
		if (gcmdp->cmd_dma_handle != NULL) {
			ata_pktp->ap_flags = 0;
			return (NULL);
		}

		ata_pktp->ap_bytes_per_block = NBPSCTR;
		sec_count = 1;

		/*
		 * Since we are retrying the last read or write operation,
		 * restore the old values of the ap_v_addr and ap_resid.
		 * This assumes CTL_IOSETUP is called again on retry; if not,
		 * this needs to be done in CTL_TRANSPORT.
		 */
		if (ata_pktp->ap_flags & (AP_READ | AP_WRITE)) {
			ata_pktp->ap_v_addr = ata_pktp->ap_v_addr_sav;
			ata_pktp->ap_resid = ata_pktp->ap_resid_sav;
		}
	} else {
		/*
		 * Limit request to ac_max_transfer sectors.
		 * The value is specified by the user in the
		 * max_transfer property. It must be in the range 1 to 256.
		 * When max_transfer is 0x100 it is bigger than 8 bits.
		 * The spec says 0 represents 256 so it should be OK.
		 */
		sec_count = min((pktp->cp_bytexfer >> SCTRSHFT),
				ata_drvp->ad_ctlp->ac_max_transfer);
		/*
		 * Save the current values of ap_v_addr and ap_resid
		 * in case a retry operation happens. During a retry
		 * operation we need to restore these values.
		 */
		ata_pktp->ap_v_addr_sav = ata_pktp->ap_v_addr;
		ata_pktp->ap_resid_sav = ata_pktp->ap_resid;
	}

	/* reset flags */
	ata_pktp->ap_flags = 0;

#ifdef	DADKIO_RWCMD_READ
	start_sec = pktp->cp_passthru ? RWCMDP(pktp)->blkaddr : pktp->cp_srtsec;
#else
	start_sec = pktp->cp_srtsec;
#endif

	/*
	 * Setup the PCIDE Bus Master Scatter/Gather list
	 */
	ata_pktp->ap_sg_cnt = 0;
	ata_pktp->ap_pciide_dma = FALSE;
	if (gcmdp->cmd_dma_handle != NULL && sec_count != 0) {
		byte_count = sec_count << SCTRSHFT;
		if ((ghd_dmaget_attr(&GTGTP2ATAP(gtgtp)->ac_ccc, gcmdp,
			byte_count, ATA_DMA_NSEGS, &byte_count) == FALSE) ||
			(byte_count == 0)) {
			ADBG_ERROR(("ata_disk_iosetup: byte count zero\n"));
			return (NULL);
		}
		sec_count = byte_count >> SCTRSHFT;
	}

	/*
	 * In the non-48-bit mode addressing (CHS and LBA28) the sector
	 * count is a 8-bit value and the sector count 0 represents 256
	 * sectors.
	 * In the extended addressing (LBA48) the sector count is a 16-bit
	 * value, so max_transfer 0x100 cannot be truncated to 8-bits
	 * because this would represent a zero sector count.
	 */
	ata_pktp->ap_count = sec_count;
	if (!(ata_drvp->ad_flags & AD_EXT48)) {
		ata_pktp->ap_count &= 0xff;
	}
	ata_pktp->ap_startsec = start_sec;

#ifdef	DADKIO_RWCMD_READ
	if (pktp->cp_passthru) {
		switch (RWCMDP(pktp)->cmd) {
		case DADKIO_RWCMD_READ:
			if (ata_pktp->ap_sg_cnt) {
				ata_pktp->ap_cmd = ATC_READ_DMA;
				ata_pktp->ap_pciide_dma = TRUE;
				ata_pktp->ap_start = ata_disk_start_dma_in;
				ata_pktp->ap_intr = ata_disk_intr_dma;
			} else {
				ata_pktp->ap_cmd = ATC_RDSEC;
				ata_pktp->ap_start = ata_disk_start_pio_in;
				ata_pktp->ap_intr = ata_disk_intr_pio_in;
			}
			ata_pktp->ap_flags |= AP_READ;
			break;
		case DADKIO_RWCMD_WRITE:
			if (ata_pktp->ap_sg_cnt) {
				ata_pktp->ap_cmd = ATC_WRITE_DMA;
				ata_pktp->ap_pciide_dma = TRUE;
				ata_pktp->ap_start = ata_disk_start_dma_out;
				ata_pktp->ap_intr = ata_disk_intr_dma;
			} else {
				ata_pktp->ap_cmd = ATC_WRSEC;
				ata_pktp->ap_start = ata_disk_start_pio_out;
				ata_pktp->ap_intr = ata_disk_intr_pio_out;
			}
			ata_pktp->ap_flags |= AP_WRITE;
			break;
		}

		byte_count = RWCMDP(pktp)->buflen;
		pktp->cp_bytexfer = byte_count;
		pktp->cp_resid = byte_count;
		ata_pktp->ap_resid = byte_count;

		/*
		 * since we're not using READ/WRITE MULTIPLE, we
		 * should set bytes_per_block to one sector
		 * XXX- why wasn't this in the old driver??
		 */
		ata_pktp->ap_bytes_per_block = NBPSCTR;
	} else
#endif
	{
		byte_count = sec_count << SCTRSHFT;
		pktp->cp_bytexfer = byte_count;
		pktp->cp_resid = byte_count;
		ata_pktp->ap_resid = byte_count;

		/* setup the task file registers */

		switch (ata_pktp->ap_cdb) {
		case DCMD_READ:
			if (ata_pktp->ap_sg_cnt) {
				ata_pktp->ap_cmd = ATC_READ_DMA;
				ata_pktp->ap_pciide_dma = TRUE;
				ata_pktp->ap_start = ata_disk_start_dma_in;
				ata_pktp->ap_intr = ata_disk_intr_dma;
			} else {
				ata_pktp->ap_cmd = ata_drvp->ad_rd_cmd;
				ata_pktp->ap_start = ata_disk_start_pio_in;
				ata_pktp->ap_intr = ata_disk_intr_pio_in;
			}
			ata_pktp->ap_flags |= AP_READ;
			break;

		case DCMD_WRITE:
			if (ata_pktp->ap_sg_cnt) {
				ata_pktp->ap_cmd = ATC_WRITE_DMA;
				ata_pktp->ap_pciide_dma = TRUE;
				ata_pktp->ap_start = ata_disk_start_dma_out;
				ata_pktp->ap_intr = ata_disk_intr_dma;
			} else {
				ata_pktp->ap_cmd = ata_drvp->ad_wr_cmd;
				ata_pktp->ap_start = ata_disk_start_pio_out;
				ata_pktp->ap_intr = ata_disk_intr_pio_out;
			}
			ata_pktp->ap_flags |= AP_WRITE;
			break;

		default:
			ADBG_WARN(("ata_disk_iosetup: unknown command 0x%x\n",
					ata_pktp->ap_cdb));
			pktp = NULL;
			break;
		}
	}

	/* If 48-bit mode is used, convert command to 48-bit mode cmd */
	if (pktp != NULL && ata_drvp->ad_flags & AD_EXT48) {
		switch (ata_pktp->ap_cmd) {
		case ATC_RDSEC:
			ata_pktp->ap_cmd = ATC_RDSEC_EXT;
			break;
		case ATC_WRSEC:
			ata_pktp->ap_cmd = ATC_WRSEC_EXT;
			break;
		case ATC_RDMULT:
			ata_pktp->ap_cmd = ATC_RDMULT_EXT;
			break;
		case ATC_WRMULT:
			ata_pktp->ap_cmd = ATC_WRMULT_EXT;
			break;
		case ATC_READ_DMA:
			ata_pktp->ap_cmd = ATC_RDDMA_EXT;
			break;
		case ATC_WRITE_DMA:
			ata_pktp->ap_cmd = ATC_WRDMA_EXT;
			break;
		}
	}

	return (pktp);
}



/*
 *
 * DADA transport entry point
 *
 */

static int
ata_disk_transport(opaque_t ctl_data, cmpkt_t *pktp)
{
	gtgt_t		*gtgtp = (gtgt_t *)ctl_data;
	ata_drv_t	*ata_drvp = GTGTP2ATADRVP(gtgtp);
	ata_ctl_t	*ata_ctlp = ata_drvp->ad_ctlp;
	ata_pkt_t	*ata_pktp = CPKT2APKT(pktp);
	int		rc;
	int		polled = FALSE;

	ADBG_TRACE(("ata_disk_transport entered\n"));

	/* check for polling pkt */

	if (pktp->cp_flags & CPF_NOINTR) {
		polled = TRUE;
	}

	/* call ghd transport routine */

	rc = ghd_transport(&ata_ctlp->ac_ccc, APKT2GCMD(ata_pktp),
		gtgtp, pktp->cp_time, polled, NULL);

	/* see if pkt was not accepted */

	if (rc == TRAN_BUSY)
		return (CTL_SEND_BUSY);

	if (rc == TRAN_ACCEPT)
		return (CTL_SEND_SUCCESS);

	return (CTL_SEND_FAILURE);
}


/*
 *
 * routines to load the cylinder/head/sector/count
 * task file registers.
 *
 */
static void
ata_disk_load_regs_lba28(ata_pkt_t *ata_pktp, ata_drv_t *ata_drvp)
{
	ata_ctl_t	*ata_ctlp = ata_drvp->ad_ctlp;
	ddi_acc_handle_t io_hdl1 = ata_ctlp->ac_iohandle1;
	uint_t		lba;	/* LBA of first sector */

	lba = ata_pktp->ap_startsec;

	ddi_put8(io_hdl1, ata_ctlp->ac_count,
		ata_pktp->ap_count);
	ddi_put8(io_hdl1, ata_ctlp->ac_sect, lba);
	lba >>= 8;
	ddi_put8(io_hdl1, ata_ctlp->ac_lcyl, lba);
	lba >>= 8;
	ddi_put8(io_hdl1, ata_ctlp->ac_hcyl, lba);
	lba >>= 8;
	/*
	 * dev/head register can use only 4 bits
	 * must also include drive selector.
	 */
	lba = (lba & 0xf) | ata_drvp->ad_drive_bits;
	ddi_put8(io_hdl1,  ata_ctlp->ac_drvhd, lba);
}

/*
 * In 48-bit extended mode, the sector count is 16 bits wide, and the
 * LBA is 48 bits wide, as follows:
 * register	most recent	previous
 * name		value		value
 * --------	----------	---------
 * sector cnt	count(7:0)	count(15:8)
 * sector num	lba(7:0)	lba(31:24)
 * cyl low	lba(15:8)	lba(39:32)
 * cyl hi	lba(23:16)	lba(47:40)
 * device/head	111D0000	N/A
 *               ^ ^
 *               | |
 *               | +-- drive number
 *               |
 *               +-- indicates LBA
 *	The other two 1 bits are historical and are not used in 48bit
 *	extended mode.
 */
/*
 * WARNING:
 * dada framework passes starting sector as daddr_t type, thus
 * limiting reachable disk space in 32-bit x86 architecture to 1 terabyte.
 * Therefore high 16 bits of the 48-bits address can be and
 * are currently ignored.
 */
static void
ata_disk_load_regs_lba48(ata_pkt_t *ata_pktp, ata_drv_t *ata_drvp)
{
	ata_ctl_t	*ata_ctlp = ata_drvp->ad_ctlp;
	ddi_acc_handle_t io_hdl1 = ata_ctlp->ac_iohandle1;
	uint16_t	seccnt;		/* 16-bit sector count */
	uint_t		lbalow;		/* low-order 24 bits of LBA */
	uint_t		lbahi;		/* high-order 24 bits of LBA */

	seccnt = ata_pktp->ap_count;
	/* high-order 8 bits of lbalow never get used */
	lbalow = ata_pktp->ap_startsec;
	lbahi = ata_pktp->ap_startsec >> 24;

	ddi_put8(io_hdl1, ata_ctlp->ac_count, seccnt >> 8);
	ddi_put8(io_hdl1, ata_ctlp->ac_count, seccnt);
	/* Send the high-order half first */
	ddi_put8(io_hdl1, ata_ctlp->ac_sect, lbahi);
	lbahi >>= 8;
	ddi_put8(io_hdl1, ata_ctlp->ac_lcyl, lbahi);
	lbahi >>= 8;
	ddi_put8(io_hdl1, ata_ctlp->ac_hcyl, lbahi);
	/* Send the low-order half */
	ddi_put8(io_hdl1, ata_ctlp->ac_sect, lbalow);
	lbalow >>= 8;
	ddi_put8(io_hdl1, ata_ctlp->ac_lcyl, lbalow);
	lbalow >>= 8;
	ddi_put8(io_hdl1, ata_ctlp->ac_hcyl, lbalow);
	ddi_put8(io_hdl1,  ata_ctlp->ac_drvhd,
				ata_drvp->ad_drive_bits);
}

static void
ata_disk_load_regs_chs(ata_pkt_t *ata_pktp, ata_drv_t *ata_drvp)
{
	ata_ctl_t		*ata_ctlp = ata_drvp->ad_ctlp;
	ddi_acc_handle_t io_hdl1 = ata_ctlp->ac_iohandle1;
	uint_t			resid;
	uint_t			cyl;
	uchar_t			head;
	uchar_t			drvheads;
	uchar_t			drvsectors;

	drvheads = ata_drvp->ad_phhd;
	drvsectors = ata_drvp->ad_phsec;

	resid = ata_pktp->ap_startsec / drvsectors;
	head = (resid % drvheads) & 0xf;
	cyl = resid / drvheads;
			/* automatically truncate to char */
	ddi_put8(io_hdl1, ata_ctlp->ac_sect,
			(ata_pktp->ap_startsec % drvsectors) + 1);
	ddi_put8(io_hdl1, ata_ctlp->ac_count, ata_pktp->ap_count);
	ddi_put8(io_hdl1, ata_ctlp->ac_hcyl, (cyl >> 8));
		/* lcyl gets truncated to 8 bits */
	ddi_put8(io_hdl1, ata_ctlp->ac_lcyl, cyl);
	ddi_put8(io_hdl1,  ata_ctlp->ac_drvhd,
			ata_drvp->ad_drive_bits | head);
}


/*
 *
 * packet start callback routines
 *
 */

/* ARGSUSED */
static int
ata_disk_start_common(
	ata_ctl_t	*ata_ctlp,
	ata_drv_t	*ata_drvp,
	ata_pkt_t	*ata_pktp)
{
	ddi_acc_handle_t io_hdl1 = ata_ctlp->ac_iohandle1;
	ddi_acc_handle_t io_hdl2 = ata_ctlp->ac_iohandle2;

	ADBG_TRACE(("ata_disk_start_common entered\n"));

	ADBG_TRANSPORT(("ata_disk_start:\tpkt = 0x%p, pkt flags = 0x%x\n",
		ata_pktp, ata_pktp->ap_flags));
	ADBG_TRANSPORT(("\tcommand=0x%x, sect=0x%lx\n",
		ata_pktp->ap_cmd, ata_pktp->ap_startsec));
	ADBG_TRANSPORT(("\tcount=0x%x, drvhd = 0x%x\n",
		ata_pktp->ap_count, ata_drvp->ad_drive_bits));

	/*
	 * If AC_BSY_WAIT is set, wait for controller to not be busy,
	 * before issuing a command.  If AC_BSY_WAIT is not set,
	 * skip the wait.  This is important for laptops that do
	 * suspend/resume but do not correctly wait for the busy bit to
	 * drop after a resume.
	 *
	 * NOTE: this test for ATS_BSY is also needed if/when we
	 * implement the overlapped/queued command protocols. Currently,
	 * the overlap/queued feature is not supported so the test is
	 * conditional.
	 */
	if (ata_ctlp->ac_timing_flags & AC_BSY_WAIT) {
		if (!ata_wait(io_hdl2,  ata_ctlp->ac_ioaddr2,
				0, ATS_BSY, 5000000)) {
			ADBG_ERROR(("ata_disk_start: BUSY\n"));
			return (FALSE);
		}
	}

	ddi_put8(io_hdl1, ata_ctlp->ac_drvhd, ata_drvp->ad_drive_bits);
	ATA_DELAY_400NSEC(io_hdl2, ata_ctlp->ac_ioaddr2);

	/*
	 * make certain the drive selected
	 */
	if (!ata_wait(io_hdl2,  ata_ctlp->ac_ioaddr2,
			ATS_DRDY, ATS_BSY, 5 * 1000000)) {
		ADBG_ERROR(("ata_disk_start: select failed\n"));
		return (FALSE);
	}

	if (ata_pktp->ap_cmd == ATC_LOAD_FW) {

		/* the sector count is 16 bits wide */
		ddi_put8(io_hdl1, ata_ctlp->ac_count, ata_pktp->ap_count);
		ddi_put8(io_hdl1, ata_ctlp->ac_sect, ata_pktp->ap_count >> 8);
		ddi_put8(io_hdl1, ata_ctlp->ac_lcyl, ata_pktp->ap_startsec);
		ddi_put8(io_hdl1, ata_ctlp->ac_hcyl,
		    ata_pktp->ap_startsec >> 8);

		/* put subcommand for DOWNLOAD MICROCODE */
		ddi_put8(io_hdl1, ata_ctlp->ac_feature, ata_pktp->ap_bcount);
	} else {

		/*
		 * We use different methods for loading the task file
		 * registers, depending on whether the disk
		 * uses LBA or CHS addressing and whether 48-bit
		 * extended addressing is to be used.
		 */
		if (!(ata_drvp->ad_drive_bits & ATDH_LBA))
			ata_disk_load_regs_chs(ata_pktp, ata_drvp);
		else if (ata_drvp->ad_flags & AD_EXT48)
			ata_disk_load_regs_lba48(ata_pktp, ata_drvp);
		else
			ata_disk_load_regs_lba28(ata_pktp, ata_drvp);
		ddi_put8(io_hdl1, ata_ctlp->ac_feature, 0);
	}

	/*
	 * Always make certain interrupts are enabled. It's been reported
	 * (but not confirmed) that some notebook computers don't
	 * clear the interrupt disable bit after being resumed. The
	 * easiest way to fix this is to always clear the disable bit
	 * before every command.
	 */
	ddi_put8(io_hdl2, ata_ctlp->ac_devctl, ATDC_D3);
	return (TRUE);
}


/*
 *
 * Start a non-data ATA command (not DMA and not PIO):
 *
 */

static int
ata_disk_start(
	ata_ctl_t *ata_ctlp,
	ata_drv_t *ata_drvp,
	ata_pkt_t *ata_pktp)
{
	ddi_acc_handle_t io_hdl1 = ata_ctlp->ac_iohandle1;
	ddi_acc_handle_t io_hdl2 = ata_ctlp->ac_iohandle2;
	int		 rc;

	rc = ata_disk_start_common(ata_ctlp, ata_drvp, ata_pktp);

	if (!rc)
		return (ATA_FSM_RC_BUSY);

	/*
	 * This next one sets the controller in motion
	 */
	ddi_put8(io_hdl1, ata_ctlp->ac_cmd, ata_pktp->ap_cmd);

	/* wait for the busy bit to settle */
	ATA_DELAY_400NSEC(io_hdl2, ata_ctlp->ac_ioaddr2);

	return (ATA_FSM_RC_OKAY);
}



static int
ata_disk_start_dma_in(
	ata_ctl_t *ata_ctlp,
	ata_drv_t *ata_drvp,
	ata_pkt_t *ata_pktp)
{
	ddi_acc_handle_t io_hdl1 = ata_ctlp->ac_iohandle1;
	ddi_acc_handle_t io_hdl2 = ata_ctlp->ac_iohandle2;
	int		 rc;

	rc = ata_disk_start_common(ata_ctlp, ata_drvp, ata_pktp);

	if (!rc)
		return (ATA_FSM_RC_BUSY);

	/*
	 * Copy the Scatter/Gather list to the controller's
	 * Physical Region Descriptor Table
	 */
	ata_pciide_dma_setup(ata_ctlp, ata_pktp->ap_sg_list,
		ata_pktp->ap_sg_cnt);

	/*
	 * reset the PCIIDE Controller's interrupt and error status bits
	 */
	(void) ata_pciide_status_clear(ata_ctlp);

	/*
	 * This next one sets the drive in motion
	 */
	ddi_put8(io_hdl1, ata_ctlp->ac_cmd, ata_pktp->ap_cmd);

	/* wait for the drive's busy bit to settle */
	ATA_DELAY_400NSEC(io_hdl2, ata_ctlp->ac_ioaddr2);

	ata_pciide_dma_start(ata_ctlp, PCIIDE_BMICX_RWCON_WRITE_TO_MEMORY);

	return (ATA_FSM_RC_OKAY);
}



static int
ata_disk_start_dma_out(
	ata_ctl_t *ata_ctlp,
	ata_drv_t *ata_drvp,
	ata_pkt_t *ata_pktp)
{
	ddi_acc_handle_t io_hdl1 = ata_ctlp->ac_iohandle1;
	ddi_acc_handle_t io_hdl2 = ata_ctlp->ac_iohandle2;
	int		 rc;

	rc = ata_disk_start_common(ata_ctlp, ata_drvp, ata_pktp);

	if (!rc)
		return (ATA_FSM_RC_BUSY);

	/*
	 * Copy the Scatter/Gather list to the controller's
	 * Physical Region Descriptor Table
	 */
	ata_pciide_dma_setup(ata_ctlp, ata_pktp->ap_sg_list,
		ata_pktp->ap_sg_cnt);

	/*
	 * reset the PCIIDE Controller's interrupt and error status bits
	 */
	(void) ata_pciide_status_clear(ata_ctlp);

	/*
	 * This next one sets the drive in motion
	 */
	ddi_put8(io_hdl1, ata_ctlp->ac_cmd, ata_pktp->ap_cmd);

	/* wait for the drive's busy bit to settle */
	ATA_DELAY_400NSEC(io_hdl2, ata_ctlp->ac_ioaddr2);

	ata_pciide_dma_start(ata_ctlp, PCIIDE_BMICX_RWCON_READ_FROM_MEMORY);

	return (ATA_FSM_RC_OKAY);
}





/*
 *
 * Start a PIO data-in ATA command:
 *
 */

static int
ata_disk_start_pio_in(
	ata_ctl_t *ata_ctlp,
	ata_drv_t *ata_drvp,
	ata_pkt_t *ata_pktp)
{
	ddi_acc_handle_t io_hdl1 = ata_ctlp->ac_iohandle1;
	ddi_acc_handle_t io_hdl2 = ata_ctlp->ac_iohandle2;
	int		 rc;

	rc = ata_disk_start_common(ata_ctlp, ata_drvp, ata_pktp);

	if (!rc)
		return (ATA_FSM_RC_BUSY);
	/*
	 * This next one sets the controller in motion
	 */
	ddi_put8(io_hdl1, ata_ctlp->ac_cmd, ata_pktp->ap_cmd);

	/* wait for the busy bit to settle */
	ATA_DELAY_400NSEC(io_hdl2, ata_ctlp->ac_ioaddr2);

	return (ATA_FSM_RC_OKAY);
}




/*
 *
 * Start a PIO data-out ATA command:
 *
 */

static int
ata_disk_start_pio_out(
	ata_ctl_t *ata_ctlp,
	ata_drv_t *ata_drvp,
	ata_pkt_t *ata_pktp)
{
	ddi_acc_handle_t io_hdl1 = ata_ctlp->ac_iohandle1;
	ddi_acc_handle_t io_hdl2 = ata_ctlp->ac_iohandle2;
	int		 rc;

	ata_pktp->ap_wrt_count = 0;

	rc = ata_disk_start_common(ata_ctlp, ata_drvp, ata_pktp);

	if (!rc)
		return (ATA_FSM_RC_BUSY);
	/*
	 * This next one sets the controller in motion
	 */
	ddi_put8(io_hdl1, ata_ctlp->ac_cmd, ata_pktp->ap_cmd);

	/* wait for the busy bit to settle */
	ATA_DELAY_400NSEC(io_hdl2, ata_ctlp->ac_ioaddr2);

	/*
	 * Wait for the drive to assert DRQ to send the first chunk
	 * of data. Have to busy wait because there's no interrupt for
	 * the first chunk. This sucks (a lot of cycles) if the
	 * drive responds too slowly or if the wait loop granularity
	 * is too large. It's really bad if the drive is defective and
	 * the loop times out.
	 */

	if (!ata_wait3(io_hdl2, ata_ctlp->ac_ioaddr2,
			ATS_DRQ, ATS_BSY, /* okay */
			ATS_ERR, ATS_BSY, /* cmd failed */
			ATS_DF, ATS_BSY, /* drive failed */
			4000000)) {
		ADBG_WARN(("ata_disk_start_pio_out: no DRQ\n"));
		ata_pktp->ap_flags |= AP_ERROR;
		return (ATA_FSM_RC_INTR);
	}

	/*
	 * Tell the upper layer to fake a hardware interrupt which
	 * actually causes the first segment to be written to the drive.
	 */
	return (ATA_FSM_RC_INTR);
}



/*
 *
 * packet complete callback routine
 *
 */

static void
ata_disk_complete(
	ata_drv_t *ata_drvp,
	ata_pkt_t *ata_pktp,
	int do_callback)
{
	struct ata_id   *aidp = &ata_drvp->ad_id;
	cmpkt_t	*pktp;

	ADBG_TRACE(("ata_disk_complete entered\n"));
	ADBG_TRANSPORT(("ata_disk_complete: pkt = 0x%p\n", ata_pktp));

	pktp = APKT2CPKT(ata_pktp);

	/* update resid */

	pktp->cp_resid = ata_pktp->ap_resid;

	if (ata_pktp->ap_flags & AP_ERROR) {

		pktp->cp_reason = CPS_CHKERR;

		if (ata_pktp->ap_error & ATE_BBK_ICRC) {
			if (IS_ATA_VERSION_GE(aidp, 4))
				ata_pktp->ap_scb = DERR_ICRC;
			else
				ata_pktp->ap_scb = DERR_BBK;
		} else if (ata_pktp->ap_error & ATE_UNC)
			ata_pktp->ap_scb = DERR_UNC;
		else if (ata_pktp->ap_error & ATE_IDNF)
			ata_pktp->ap_scb = DERR_IDNF;
		else if (ata_pktp->ap_error & ATE_TKONF)
			ata_pktp->ap_scb = DERR_TKONF;
		else if (ata_pktp->ap_error & ATE_AMNF)
			ata_pktp->ap_scb = DERR_AMNF;
		else if (ata_pktp->ap_status & ATS_BSY)
			ata_pktp->ap_scb = DERR_BUSY;
		else if (ata_pktp->ap_status & ATS_DF)
			ata_pktp->ap_scb = DERR_DWF;
		else /* any unknown error	*/
			ata_pktp->ap_scb = DERR_ABORT;
	} else if (ata_pktp->ap_flags &
			(AP_ABORT|AP_TIMEOUT|AP_BUS_RESET)) {

		pktp->cp_reason = CPS_CHKERR;
		ata_pktp->ap_scb = DERR_ABORT;
	} else {
		pktp->cp_reason = CPS_SUCCESS;
		ata_pktp->ap_scb = DERR_SUCCESS;
	}

	/* callback */
	if (do_callback)
		(*pktp->cp_callback)(pktp);
}


/*
 *
 * Interrupt callbacks
 *
 */


/*
 *
 * ATA command, no data
 *
 */

/* ARGSUSED */
static int
ata_disk_intr(
	ata_ctl_t *ata_ctlp,
	ata_drv_t *ata_drvp,
	ata_pkt_t *ata_pktp)
{
	uchar_t		 status;

	ADBG_TRACE(("ata_disk_intr entered\n"));
	ADBG_TRANSPORT(("ata_disk_intr: pkt = 0x%p\n", ata_pktp));

	status = ata_get_status_clear_intr(ata_ctlp, ata_pktp);

	ASSERT((status & (ATS_BSY | ATS_DRQ)) == 0);

	/*
	 * check for errors
	 */

	if (status & (ATS_DF | ATS_ERR)) {
		ADBG_WARN(("ata_disk_intr: status 0x%x error 0x%x\n", status,
			ddi_get8(ata_ctlp->ac_iohandle1, ata_ctlp->ac_error)));
		ata_pktp->ap_flags |= AP_ERROR;
	}

	if (ata_pktp->ap_flags & AP_ERROR) {
		ata_pktp->ap_status = ddi_get8(ata_ctlp->ac_iohandle2,
			ata_ctlp->ac_altstatus);
		ata_pktp->ap_error = ddi_get8(ata_ctlp->ac_iohandle1,
			ata_ctlp->ac_error);
	}

	/* tell the upper layer this request is complete */
	return (ATA_FSM_RC_FINI);
}


/*
 *
 * ATA command, PIO data in
 *
 */

/* ARGSUSED */
static int
ata_disk_intr_pio_in(
	ata_ctl_t *ata_ctlp,
	ata_drv_t *ata_drvp,
	ata_pkt_t *ata_pktp)
{
	ddi_acc_handle_t io_hdl1 = ata_ctlp->ac_iohandle1;
	ddi_acc_handle_t io_hdl2 = ata_ctlp->ac_iohandle2;
	uchar_t		 status;

	ADBG_TRACE(("ata_disk_pio_in entered\n"));
	ADBG_TRANSPORT(("ata_disk_pio_in: pkt = 0x%p\n", ata_pktp));

	/*
	 * first make certain DRQ is asserted (and no errors)
	 */
	(void) ata_wait3(io_hdl2, ata_ctlp->ac_ioaddr2,
			ATS_DRQ, ATS_BSY, ATS_ERR, ATS_BSY, ATS_DF, ATS_BSY,
			4000000);

	status = ata_get_status_clear_intr(ata_ctlp, ata_pktp);

	if (status & ATS_BSY) {
		ADBG_WARN(("ata_disk_pio_in: BUSY\n"));
		ata_pktp->ap_flags |= AP_ERROR;
		ata_pktp->ap_status = ddi_get8(io_hdl2, ata_ctlp->ac_altstatus);
		ata_pktp->ap_error = ddi_get8(io_hdl1, ata_ctlp->ac_error);
		return (ATA_FSM_RC_BUSY);
	}

	/*
	 * record any errors
	 */
	if ((status & (ATS_DRQ | ATS_DF | ATS_ERR)) != ATS_DRQ) {
		ADBG_WARN(("ata_disk_pio_in: status 0x%x error 0x%x\n",
			status, ddi_get8(io_hdl1, ata_ctlp->ac_error)));
		ata_pktp->ap_flags |= AP_ERROR;
		ata_pktp->ap_status = ddi_get8(io_hdl2, ata_ctlp->ac_altstatus);
		ata_pktp->ap_error = ddi_get8(io_hdl1, ata_ctlp->ac_error);
	}

	/*
	 * read the next chunk of data (if any)
	 */
	if (status & ATS_DRQ) {
		ata_disk_pio_xfer_data_in(ata_ctlp, ata_pktp);
	}

	/*
	 * If that was the last chunk, wait for the device to clear DRQ
	 */
	if (ata_pktp->ap_resid == 0) {
		if (ata_wait(io_hdl2, ata_ctlp->ac_ioaddr2,
			0, (ATS_DRQ | ATS_BSY), 4000000)) {
			/* tell the upper layer this request is complete */
			return (ATA_FSM_RC_FINI);
		}

		ADBG_WARN(("ata_disk_pio_in: DRQ stuck\n"));
		ata_pktp->ap_flags |= AP_ERROR;
		ata_pktp->ap_status = ddi_get8(io_hdl2, ata_ctlp->ac_altstatus);
		ata_pktp->ap_error = ddi_get8(io_hdl1, ata_ctlp->ac_error);
	}

	/*
	 * check for errors
	 */
	if (ata_pktp->ap_flags & AP_ERROR) {
		return (ATA_FSM_RC_FINI);
	}

	/*
	 * If the read command isn't done yet,
	 * wait for the next interrupt.
	 */
	ADBG_TRACE(("ata_disk_pio_in: partial\n"));
	return (ATA_FSM_RC_OKAY);
}



/*
 *
 * ATA command, PIO data out
 *
 */

/* ARGSUSED */
static int
ata_disk_intr_pio_out(
	ata_ctl_t *ata_ctlp,
	ata_drv_t *ata_drvp,
	ata_pkt_t *ata_pktp)
{
	ddi_acc_handle_t io_hdl1 = ata_ctlp->ac_iohandle1;
	ddi_acc_handle_t io_hdl2 = ata_ctlp->ac_iohandle2;
	int		 tmp_count = ata_pktp->ap_wrt_count;
	uchar_t		 status;

	/*
	 * clear the IRQ
	 */
	status = ata_get_status_clear_intr(ata_ctlp, ata_pktp);

	ADBG_TRACE(("ata_disk_intr_pio_out entered\n"));
	ADBG_TRANSPORT(("ata_disk_intr_pio_out: pkt = 0x%p\n", ata_pktp));

	ASSERT(!(status & ATS_BSY));


	/*
	 * check for errors
	 */

	if (status & (ATS_DF | ATS_ERR)) {
		ADBG_WARN(("ata_disk_intr_pio_out: status 0x%x error 0x%x\n",
		status, ddi_get8(io_hdl1, ata_ctlp->ac_error)));
		ata_pktp->ap_flags |= AP_ERROR;
		ata_pktp->ap_status = ddi_get8(io_hdl2, ata_ctlp->ac_altstatus);
		ata_pktp->ap_error = ddi_get8(io_hdl1, ata_ctlp->ac_error);
		/* tell the upper layer this request is complete */
		return (ATA_FSM_RC_FINI);
	}


	/*
	 * last write was okay, bump the ptr and
	 * decr the resid count
	 */
	ata_pktp->ap_v_addr += tmp_count;
	ata_pktp->ap_resid -= tmp_count;

	/*
	 * check for final interrupt on write command
	 */
	if (ata_pktp->ap_resid <= 0) {
		/* tell the upper layer this request is complete */
		return (ATA_FSM_RC_FINI);
	}

	/*
	 * Perform the next data transfer
	 *
	 * First make certain DRQ is asserted and no error status.
	 * (I'm not certain but I think some drives might deassert BSY
	 * before asserting DRQ. This extra ata_wait3() will
	 * compensate for such drives).
	 *
	 */
	(void) ata_wait3(io_hdl2, ata_ctlp->ac_ioaddr2,
		ATS_DRQ, ATS_BSY, ATS_ERR, ATS_BSY, ATS_DF, ATS_BSY, 4000000);

	status = ddi_get8(io_hdl2, ata_ctlp->ac_altstatus);

	if (status & ATS_BSY) {
		/* this should never happen */
		ADBG_WARN(("ata_disk_intr_pio_out: BUSY\n"));
		ata_pktp->ap_flags |= AP_ERROR;
		ata_pktp->ap_status = ddi_get8(io_hdl2, ata_ctlp->ac_altstatus);
		ata_pktp->ap_error = ddi_get8(io_hdl1, ata_ctlp->ac_error);
		return (ATA_FSM_RC_BUSY);
	}

	/*
	 * bailout if any errors
	 */
	if ((status & (ATS_DRQ | ATS_DF | ATS_ERR)) != ATS_DRQ) {
		ADBG_WARN(("ata_disk_pio_out: status 0x%x error 0x%x\n",
			status, ddi_get8(io_hdl1, ata_ctlp->ac_error)));
		ata_pktp->ap_flags |= AP_ERROR;
		ata_pktp->ap_status = ddi_get8(io_hdl2, ata_ctlp->ac_altstatus);
		ata_pktp->ap_error = ddi_get8(io_hdl1, ata_ctlp->ac_error);
		return (ATA_FSM_RC_FINI);
	}

	/*
	 * write  the next chunk of data
	 */
	ADBG_TRACE(("ata_disk_intr_pio_out: write xfer\n"));
	ata_disk_pio_xfer_data_out(ata_ctlp, ata_pktp);

	/*
	 * Wait for the next interrupt before checking the transfer
	 * status and adjusting the transfer count.
	 *
	 */
	return (ATA_FSM_RC_OKAY);
}


/*
 *
 * ATA command, DMA data in/out
 *
 */

static int
ata_disk_intr_dma(
	ata_ctl_t *ata_ctlp,
	ata_drv_t *ata_drvp,
	ata_pkt_t *ata_pktp)
{
	ddi_acc_handle_t io_hdl1 = ata_ctlp->ac_iohandle1;
	ddi_acc_handle_t io_hdl2 = ata_ctlp->ac_iohandle2;
	uchar_t		 status;

	ADBG_TRACE(("ata_disk_intr_dma entered\n"));
	ADBG_TRANSPORT(("ata_disk_intr_dma: pkt = 0x%p\n", ata_pktp));

	/*
	 * halt the DMA engine
	 */
	ata_pciide_dma_stop(ata_ctlp);

	/*
	 * wait for the device to clear DRQ
	 */
	if (!ata_wait(io_hdl2, ata_ctlp->ac_ioaddr2,
			0, (ATS_DRQ | ATS_BSY), 4000000)) {
		ADBG_WARN(("ata_disk_intr_dma: DRQ stuck\n"));
		ata_pktp->ap_flags |= AP_ERROR;
		ata_pktp->ap_status = ddi_get8(io_hdl2, ata_ctlp->ac_altstatus);
		ata_pktp->ap_error = ddi_get8(io_hdl1, ata_ctlp->ac_error);
		return (ATA_FSM_RC_BUSY);
	}

	/*
	 * get the status and clear the IRQ, and check for DMA error
	 */
	status = ata_get_status_clear_intr(ata_ctlp, ata_pktp);

	/*
	 * check for drive errors
	 */

	if (status & (ATS_DF | ATS_ERR)) {
		ADBG_WARN(("ata_disk_intr_dma: status 0x%x error 0x%x\n",
			status, ddi_get8(io_hdl1, ata_ctlp->ac_error)));
		ata_pktp->ap_flags |= AP_ERROR;
		ata_pktp->ap_status = ddi_get8(io_hdl2, ata_ctlp->ac_altstatus);
		ata_pktp->ap_error = ddi_get8(io_hdl1, ata_ctlp->ac_error);
	}

	/*
	 * If there was a drive or DMA error, compute a resid count
	 */
	if (ata_pktp->ap_flags & AP_ERROR) {
		/*
		 * grab the last sector address from the drive regs
		 * and use that to compute the resid
		 */
		ata_disk_get_resid(ata_ctlp, ata_drvp, ata_pktp);
	} else {
		ata_pktp->ap_resid = 0;
	}

	/* tell the upper layer this request is complete */
	return (ATA_FSM_RC_FINI);
}


/*
 *
 * Low level PIO routine that transfers data from the drive
 *
 */

static void
ata_disk_pio_xfer_data_in(
	ata_ctl_t *ata_ctlp,
	ata_pkt_t *ata_pktp)
{
	ddi_acc_handle_t io_hdl1 = ata_ctlp->ac_iohandle1;
	ddi_acc_handle_t io_hdl2 = ata_ctlp->ac_iohandle2;
	int		 count;

	count = min(ata_pktp->ap_resid,
			ata_pktp->ap_bytes_per_block);

	ADBG_TRANSPORT(("ata_disk_pio_xfer_data_in: 0x%x bytes, addr = 0x%p\n",
			count, ata_pktp->ap_v_addr));

	/*
	 * read count bytes
	 */

	ASSERT(count != 0);

	ddi_rep_get16(io_hdl1, (ushort_t *)ata_pktp->ap_v_addr,
		ata_ctlp->ac_data, (count >> 1), DDI_DEV_NO_AUTOINCR);

	/* wait for the busy bit to settle */
	ATA_DELAY_400NSEC(io_hdl2, ata_ctlp->ac_ioaddr2);

	/*
	 * this read command completed okay, bump the ptr and
	 * decr the resid count now.
	 */
	ata_pktp->ap_v_addr += count;
	ata_pktp->ap_resid -= count;
}


/*
 *
 * Low level PIO routine that transfers data to the drive
 *
 */

static void
ata_disk_pio_xfer_data_out(
	ata_ctl_t *ata_ctlp,
	ata_pkt_t *ata_pktp)
{
	ddi_acc_handle_t io_hdl1 = ata_ctlp->ac_iohandle1;
	ddi_acc_handle_t io_hdl2 = ata_ctlp->ac_iohandle2;
	int		 count;

	count = min(ata_pktp->ap_resid,
			ata_pktp->ap_bytes_per_block);

	ADBG_TRANSPORT(("ata_disk_pio_xfer_data_out: 0x%x bytes, addr = 0x%p\n",
			count, ata_pktp->ap_v_addr));

	/*
	 * read or write count bytes
	 */

	ASSERT(count != 0);

	ddi_rep_put16(io_hdl1, (ushort_t *)ata_pktp->ap_v_addr,
		ata_ctlp->ac_data, (count >> 1), DDI_DEV_NO_AUTOINCR);

	/* wait for the busy bit to settle */
	ATA_DELAY_400NSEC(io_hdl2, ata_ctlp->ac_ioaddr2);

	/*
	 * save the count here so I can correctly adjust
	 * the ap_v_addr and ap_resid values at the next
	 * interrupt.
	 */
	ata_pktp->ap_wrt_count = count;
}


/*
 *
 * ATA Initialize Device Parameters (aka Set Params) command
 *
 * If the drive was put in some sort of CHS extended/logical geometry
 * mode by the BIOS, this function will reset it to its "native"
 * CHS geometry. This ensures that we don't run into any sort of
 * 1024 cylinder (or 65535 cylinder) limitation that may have been
 * created by a BIOS (or users) that chooses a bogus translated geometry.
 */

static int
ata_disk_initialize_device_parameters(
	ata_ctl_t *ata_ctlp,
	ata_drv_t *ata_drvp)
{
	int		 rc;

	rc = ata_command(ata_ctlp, ata_drvp, FALSE, FALSE,
			ata_disk_init_dev_parm_wait,
			ATC_SETPARAM,
			0, 			/* feature n/a */
			ata_drvp->ad_phsec,	/* max sector (1-based) */
			0,			/* sector n/a */
			(ata_drvp->ad_phhd -1),	/* max head (0-based) */
			0,			/* cyl_low n/a */
			0);			/* cyl_hi n/a */

	if (rc)
		return (TRUE);

	ADBG_ERROR(("ata_init_dev_parms: failed\n"));
	return (FALSE);
}



/*
 *
 * create fake inquiry data for DADA interface
 *
 */

static void
ata_disk_fake_inquiry(
	ata_drv_t *ata_drvp)
{
	struct ata_id *ata_idp = &ata_drvp->ad_id;
	struct scsi_inquiry *inqp = &ata_drvp->ad_inquiry;

	ADBG_TRACE(("ata_disk_fake_inquiry entered\n"));

	if (ata_idp->ai_config & ATA_ID_REM_DRV) /* ide removable bit */
		inqp->inq_rmb = 1;		/* scsi removable bit */

	(void) strncpy(inqp->inq_vid, "Gen-ATA ", sizeof (inqp->inq_vid));
	inqp->inq_dtype = DTYPE_DIRECT;
	inqp->inq_qual = DPQ_POSSIBLE;

	(void) strncpy(inqp->inq_pid, ata_idp->ai_model,
			sizeof (inqp->inq_pid));
	(void) strncpy(inqp->inq_revision, ata_idp->ai_fw,
			sizeof (inqp->inq_revision));
}

#define	LOOP_COUNT	10000


/*
 *
 * ATA Set Multiple Mode
 *
 */

static int
ata_disk_set_multiple(
	ata_ctl_t *ata_ctlp,
	ata_drv_t *ata_drvp)
{
	int		 rc;

	rc = ata_command(ata_ctlp, ata_drvp, TRUE, FALSE,
			ata_disk_set_mult_wait,
			ATC_SETMULT,
			0, 			/* feature n/a */
			ata_drvp->ad_block_factor, /* count */
			0,			/* sector n/a */
			0, 			/* head n/a */
			0,			/* cyl_low n/a */
			0);			/* cyl_hi n/a */

	if (rc) {
		return (TRUE);
	}

	ADBG_ERROR(("ata_disk_set_multiple: failed\n"));
	return (FALSE);
}


/*
 *
 * ATA Identify Device command
 *
 */

int
ata_disk_id(
	ddi_acc_handle_t io_hdl1,
	caddr_t		 ioaddr1,
	ddi_acc_handle_t io_hdl2,
	caddr_t		 ioaddr2,
	struct ata_id	*ata_idp)
{
	int	rc;

	ADBG_TRACE(("ata_disk_id entered\n"));

	rc = ata_id_common(ATC_ID_DEVICE, TRUE, io_hdl1, ioaddr1, io_hdl2,
		ioaddr2, ata_idp);

	if (!rc)
		return (FALSE);

	/*
	 * If the disk is a CF/Microdrive that works under ATA mode
	 * through CF<->ATA adapters, identify it as an ATA device
	 * and a non removable media.
	 */
	if (ata_idp->ai_config == ATA_ID_COMPACT_FLASH) {
		ata_idp->ai_config = ATA_ID_CF_TO_ATA;
	}

	if ((ata_idp->ai_config & ATAC_ATA_TYPE_MASK) != ATAC_ATA_TYPE)
		return (FALSE);

	if (ata_idp->ai_heads == 0 || ata_idp->ai_sectors == 0) {
		return (FALSE);
	}

	return (TRUE);
}

static daddr_t
ata_last_block_xferred_chs(ata_drv_t *ata_drvp)
{
	ata_ctl_t	*ata_ctlp = ata_drvp->ad_ctlp;
	ddi_acc_handle_t io_hdl1 = ata_ctlp->ac_iohandle1;
	uchar_t		 drvheads = ata_drvp->ad_phhd;
	uchar_t		 drvsectors = ata_drvp->ad_phsec;
	uchar_t		 sector;
	uchar_t		 head;
	uchar_t		 low_cyl;
	uchar_t		 hi_cyl;
	daddr_t		 lbastop;

	sector = ddi_get8(io_hdl1, ata_ctlp->ac_sect);
	head = ddi_get8(io_hdl1, ata_ctlp->ac_drvhd) & 0xf;
	low_cyl = ddi_get8(io_hdl1, ata_ctlp->ac_lcyl);
	hi_cyl = ddi_get8(io_hdl1, ata_ctlp->ac_hcyl);

	lbastop = low_cyl;
	lbastop |= (uint_t)hi_cyl << 8;
	lbastop *= (uint_t)drvheads;
	lbastop += (uint_t)head;
	lbastop *= (uint_t)drvsectors;
	lbastop += (uint_t)sector - 1;
	return (lbastop);
}

static daddr_t
ata_last_block_xferred_lba28(ata_ctl_t *ata_ctlp)
{
	ddi_acc_handle_t io_hdl1 = ata_ctlp->ac_iohandle1;
	daddr_t		lbastop;

	lbastop = ddi_get8(io_hdl1, ata_ctlp->ac_drvhd) & 0xf;
	lbastop <<= 8;
	lbastop += ddi_get8(io_hdl1, ata_ctlp->ac_hcyl);
	lbastop <<= 8;
	lbastop += ddi_get8(io_hdl1, ata_ctlp->ac_lcyl);
	lbastop <<= 8;
	lbastop += ddi_get8(io_hdl1, ata_ctlp->ac_sect);
	return (lbastop);
}

static daddr_t
ata_last_block_xferred_lba48(ata_ctl_t *ata_ctlp)
{
	ddi_acc_handle_t io_hdl1 = ata_ctlp->ac_iohandle1;
	ddi_acc_handle_t io_hdl2 = ata_ctlp->ac_iohandle2;
	daddr_t		lbastop;

	/* turn on HOB and read the high-order 24 bits */
	ddi_put8(io_hdl2, ata_ctlp->ac_devctl, (ATDC_D3 | ATDC_HOB));
	lbastop = ddi_get8(io_hdl1, ata_ctlp->ac_hcyl);
	lbastop <<= 8;
	lbastop += ddi_get8(io_hdl1, ata_ctlp->ac_lcyl);
	lbastop <<= 8;
	lbastop += ddi_get8(io_hdl1, ata_ctlp->ac_sect);
	lbastop <<= 8;

	/* Turn off HOB and read the low-order 24-bits */
	ddi_put8(io_hdl2, ata_ctlp->ac_devctl, (ATDC_D3));
	lbastop += ddi_get8(io_hdl1, ata_ctlp->ac_hcyl);
	lbastop <<= 8;
	lbastop += ddi_get8(io_hdl1, ata_ctlp->ac_lcyl);
	lbastop <<= 8;
	lbastop += ddi_get8(io_hdl1, ata_ctlp->ac_sect);
	return (lbastop);
}


/*
 *
 * Need to compute a value for ap_resid so that cp_resid can
 * be set by ata_disk_complete(). The cp_resid var is actually
 * misnamed. It's actually the offset to the block in which the
 * error occurred not the number of bytes transferred to the device.
 * At least that's how dadk actually uses the cp_resid when reporting
 * an error. In other words the sector that had the error and the
 * number of bytes transferred don't always indicate the same offset.
 * On top of that, when doing DMA transfers there's actually no
 * way to determine how many bytes have been transferred by the DMA
 * engine. On the other hand, the drive will report which sector
 * it faulted on. Using that address this routine computes the
 * number of residual bytes beyond that point which probably weren't
 * written to the drive (the drive is allowed to re-order sector
 * writes but on an ATA disk there's no way to deal with that
 * complication; in other words, the resid value calculated by
 * this routine is as good as we can manage).
 */

static void
ata_disk_get_resid(
	ata_ctl_t	*ata_ctlp,
	ata_drv_t	*ata_drvp,
	ata_pkt_t	*ata_pktp)
{
	uint_t		 lba_start;
	uint_t		 lba_stop;
	uint_t		 resid_bytes;
	uint_t		 resid_sectors;

	lba_start = ata_pktp->ap_startsec;

	if (ata_drvp->ad_flags & AD_EXT48)
		lba_stop = ata_last_block_xferred_lba48(ata_ctlp);
	else if (ata_drvp->ad_drive_bits & ATDH_LBA)
		lba_stop = ata_last_block_xferred_lba28(ata_ctlp);
	else /* CHS mode */
		lba_stop = ata_last_block_xferred_chs(ata_drvp);

	resid_sectors = lba_start + ata_pktp->ap_count - lba_stop;
	resid_bytes = resid_sectors << SCTRSHFT;

	ADBG_TRACE(("ata_disk_get_resid start 0x%x cnt 0x%x stop 0x%x\n",
		    lba_start, ata_pktp->ap_count, lba_stop));
	ata_pktp->ap_resid = resid_bytes;
}



/*
 * Removable media commands *
 */



/*
 * get the media status
 *
 * NOTE: the error handling case probably isn't correct but it
 * will have to do until someone gives me a drive to test this on.
 */
static int
ata_disk_state(
	ata_ctl_t	*ata_ctlp,
	ata_drv_t	*ata_drvp,
	ata_pkt_t	*ata_pktp)
{
	int	*statep = (int *)ata_pktp->ap_v_addr;
	uchar_t	 err;

	ADBG_TRACE(("ata_disk_state\n"));
	if (ata_command(ata_ctlp, ata_drvp, TRUE, TRUE, 5 * 1000000,
		    ATC_DOOR_LOCK, 0, 0, 0, 0, 0, 0)) {
		*statep = DKIO_INSERTED;
		return (ATA_FSM_RC_FINI);
	}

	err = ddi_get8(ata_ctlp->ac_iohandle1, ata_ctlp->ac_error);
	if (err & ATE_NM)
		*statep = DKIO_EJECTED;
	else
		*statep = DKIO_NONE;

	return (ATA_FSM_RC_FINI);
}

/*
 * eject the media
 */

static int
ata_disk_eject(
	ata_ctl_t	*ata_ctlp,
	ata_drv_t	*ata_drvp,
	ata_pkt_t	*ata_pktp)
{
	ADBG_TRACE(("ata_disk_eject\n"));
	if (ata_command(ata_ctlp, ata_drvp, TRUE, TRUE, 5 * 1000000,
			ATC_EJECT, 0, 0, 0, 0, 0, 0)) {
		return (ATA_FSM_RC_FINI);
	}
	ata_pktp->ap_flags |= AP_ERROR;
	return (ATA_FSM_RC_FINI);
}

/*
 * lock the drive
 *
 */
static int
ata_disk_lock(
	ata_ctl_t	*ata_ctlp,
	ata_drv_t	*ata_drvp,
	ata_pkt_t	*ata_pktp)
{
	ADBG_TRACE(("ata_disk_lock\n"));
	if (ata_command(ata_ctlp, ata_drvp, TRUE, TRUE, 5 * 1000000,
			ATC_DOOR_LOCK, 0, 0, 0, 0, 0, 0)) {
		return (ATA_FSM_RC_FINI);
	}
	ata_pktp->ap_flags |= AP_ERROR;
	return (ATA_FSM_RC_FINI);
}


/*
 * unlock the drive
 *
 */
static int
ata_disk_unlock(
	ata_ctl_t	*ata_ctlp,
	ata_drv_t	*ata_drvp,
	ata_pkt_t	*ata_pktp)
{
	ADBG_TRACE(("ata_disk_unlock\n"));
	if (ata_command(ata_ctlp, ata_drvp, TRUE, TRUE, 5 * 1000000,
			ATC_DOOR_UNLOCK, 0, 0, 0, 0, 0, 0)) {
		return (ATA_FSM_RC_FINI);
	}
	ata_pktp->ap_flags |= AP_ERROR;
	return (ATA_FSM_RC_FINI);
}


/*
 * put the drive into standby mode
 */
static int
ata_disk_standby(
	ata_ctl_t	*ata_ctlp,
	ata_drv_t	*ata_drvp,
	ata_pkt_t	*ata_pktp)
{
	ADBG_TRACE(("ata_disk_standby\n"));
	if (ata_command(ata_ctlp, ata_drvp, TRUE, TRUE, 5 * 1000000,
			ATC_STANDBY_IM, 0, 0, 0, 0, 0, 0)) {
		return (ATA_FSM_RC_FINI);
	}
	ata_pktp->ap_flags |= AP_ERROR;
	return (ATA_FSM_RC_FINI);
}


/*
 * Recalibrate
 *
 * Note the extra long timeout value. This is necessary in case
 * the drive was in standby mode and needs to spin up the media.
 *
 */
static int
ata_disk_recalibrate(
	ata_ctl_t	*ata_ctlp,
	ata_drv_t	*ata_drvp,
	ata_pkt_t	*ata_pktp)
{
	ADBG_TRACE(("ata_disk_recalibrate\n"));
	if (ata_command(ata_ctlp, ata_drvp, TRUE, TRUE, 31 * 1000000,
			ATC_RECAL, 0, 0, 0, 0, 0, 0)) {
		return (ATA_FSM_RC_FINI);
	}
	ata_pktp->ap_flags |= AP_ERROR;
	return (ATA_FSM_RC_FINI);
}

/*
 * Copy a string of bytes that were obtained by Identify Device into a
 * string buffer provided by the caller.
 *
 * 1. Determine the amount to copy.  This is the lesser of the
 *    length of the source string or the space available in the user's
 *    buffer.
 * 2. The true length of the source string is always returned to the
 *    caller in the size field of the argument.
 * 3. Copy the string, add a terminating NUL character at the end.
 */

static int
ata_copy_dk_ioc_string(intptr_t arg, char *source, int length, int flag)
{
	STRUCT_DECL(dadk_ioc_string, ds_arg);
	int			destsize;
	char			nulchar;
	caddr_t			outp;

	/*
	 * The ioctls that use this routine are only available to
	 * the kernel.
	 */
	if ((flag & FKIOCTL) == 0)
		return (EFAULT);

	STRUCT_INIT(ds_arg, flag & FMODELS);

	/* 1. determine size of user's buffer */
	if (ddi_copyin((caddr_t)arg, STRUCT_BUF(ds_arg), STRUCT_SIZE(ds_arg),
	    flag))
		return (EFAULT);
	destsize = STRUCT_FGET(ds_arg, is_size);
	if (destsize > length + 1)
		destsize = length + 1;

	/*
	 * 2. Return the copied length to the caller.  Note: for
	 * convenience, we actually copy the entire structure back out, not
	 * just the length.  We don't change the is_buf field, so this
	 * shouldn't break anything.
	 */
	STRUCT_FSET(ds_arg, is_size, length);
	if (ddi_copyout(STRUCT_BUF(ds_arg), (caddr_t)arg, STRUCT_SIZE(ds_arg),
	    flag))
		return (EFAULT);

	/* 3. copy the string and add a NULL terminator */
	outp = STRUCT_FGETP(ds_arg, is_buf);
	if (ddi_copyout(source, outp, destsize - 1, flag))
		return (EFAULT);
	nulchar = '\0';
	if (ddi_copyout(&nulchar, outp + (destsize - 1), 1, flag))
		return (EFAULT);
	return (0);
}

/*
 * Sun branded drives are shipped write cache disabled.  The default is to
 * force write write caching on.
 */
static void
ata_set_write_cache(ata_ctl_t *ata_ctlp, ata_drv_t *ata_drvp)
{
	char *path;

	if (ata_write_cache == 1) {
		if (ata_set_feature(ata_ctlp, ata_drvp, FC_WRITE_CACHE_ON, 0)
		    == FALSE) {
			path = kmem_alloc(MAXPATHLEN + 1, KM_NOSLEEP);
			if (path != NULL) {
				cmn_err(CE_WARN,
				    "%s unable to enable write cache targ=%d",
				    ddi_pathname(ata_ctlp->ac_dip, path),
				    ata_drvp->ad_targ);
				kmem_free(path, MAXPATHLEN + 1);
			}
		}
	} else if (ata_write_cache == -1) {
		if (ata_set_feature(ata_ctlp, ata_drvp, FC_WRITE_CACHE_OFF, 0)
		    == FALSE) {
			path = kmem_alloc(MAXPATHLEN + 1, KM_NOSLEEP);
			if (path != NULL) {
				cmn_err(CE_WARN,
				    "%s unable to disable write cache targ=%d",
				    ddi_pathname(ata_ctlp->ac_dip, path),
				    ata_drvp->ad_targ);
				kmem_free(path, MAXPATHLEN + 1);
			}
		}
	}
}

/*
 * Call set feature to spin-up the device.
 */
static int
ata_disk_set_feature_spinup(
	ata_ctl_t	*ata_ctlp,
	ata_drv_t	*ata_drvp,
	ata_pkt_t	*ata_pktp)
{
	int rc;

	ADBG_TRACE(("ata_disk_set_feature_spinup entered\n"));

	rc = ata_set_feature(ata_ctlp, ata_drvp, 0x07, 0);
	if (!rc)
		ata_pktp->ap_flags |= AP_ERROR;

	return (ATA_FSM_RC_FINI);
}

/*
 * Update device ata_id content - IDENTIFY DEVICE command.
 */
static int
ata_disk_id_update(
	ata_ctl_t	*ata_ctlp,
	ata_drv_t	*ata_drvp,
	ata_pkt_t	*ata_pktp)
{
	ddi_acc_handle_t io_hdl1 = ata_ctlp->ac_iohandle1;
	caddr_t		 ioaddr1 = ata_ctlp->ac_ioaddr1;
	ddi_acc_handle_t io_hdl2 = ata_ctlp->ac_iohandle2;
	caddr_t		 ioaddr2 = ata_ctlp->ac_ioaddr2;
	struct ata_id *aidp = &ata_drvp->ad_id;
	int rc;

	ADBG_TRACE(("ata_disk_id_update entered\n"));

	/*
	 * select the appropriate drive and LUN
	 */
	ddi_put8(io_hdl1, (uchar_t *)ioaddr1 + AT_DRVHD,
	    ata_drvp->ad_drive_bits);
	ATA_DELAY_400NSEC(io_hdl2, ioaddr2);

	/*
	 * make certain the drive is selected, and wait for not busy
	 */
	if (!ata_wait(io_hdl2, ioaddr2, ATS_DRDY, ATS_BSY, 5 * 1000000)) {
		ADBG_ERROR(("ata_disk_id_update: select failed\n"));
		ata_pktp->ap_flags |= AP_ERROR;
		return (ATA_FSM_RC_FINI);
	}

	rc = ata_disk_id(io_hdl1, ioaddr1, io_hdl2, ioaddr2, aidp);

	if (!rc) {
		ata_pktp->ap_flags |= AP_ERROR;
	} else {
		swab(aidp->ai_drvser, aidp->ai_drvser,
		    sizeof (aidp->ai_drvser));
		swab(aidp->ai_fw, aidp->ai_fw,
		    sizeof (aidp->ai_fw));
		swab(aidp->ai_model, aidp->ai_model,
		    sizeof (aidp->ai_model));
	}

	return (ATA_FSM_RC_FINI);
}

/*
 * Update device firmware.
 */
static int
ata_disk_update_fw(gtgt_t *gtgtp, ata_ctl_t *ata_ctlp,
	ata_drv_t *ata_drvp, caddr_t fwfile,
	uint_t size, uint8_t type, int flag)
{
	ata_pkt_t	*ata_pktp;
	gcmd_t		*gcmdp = NULL;
	caddr_t		fwfile_memp = NULL, tmp_fwfile_memp;
	uint_t		total_sec_count, sec_count, start_sec = 0;
	uint8_t		cmd_type;
	int		rc;

	/*
	 * First check whether DOWNLOAD MICROCODE command is supported
	 */
	if (!(ata_drvp->ad_id.ai_cmdset83 & 0x1)) {
		ADBG_ERROR(("drive doesn't support download "
		    "microcode command\n"));
		return (ENOTSUP);
	}

	switch (type) {
	case FW_TYPE_TEMP:
		cmd_type = ATCM_FW_TEMP;
		break;

	case FW_TYPE_PERM:
		cmd_type = ATCM_FW_PERM;
		break;

	default:
		return (EINVAL);
	}

	/* Temporary subcommand is obsolete in ATA/ATAPI-8 version */
	if (cmd_type == ATCM_FW_TEMP) {
		if (ata_drvp->ad_id.ai_majorversion & ATAC_MAJVER_8) {
			ADBG_ERROR(("Temporary use is obsolete in "
			    "ATA/ATAPI-8 version\n"));
			return (ENOTSUP);
		}
	}

	total_sec_count = size >> SCTRSHFT;
	if (total_sec_count > MAX_FWFILE_SIZE_ONECMD) {
		if (cmd_type == ATCM_FW_TEMP) {
			ADBG_ERROR(("firmware size: %x sectors is too large\n",
			    total_sec_count));
			return (EINVAL);
		} else {
			ADBG_WARN(("firmware size: %x sectors is larger than"
			    " one command, need to use the multicommand"
			    " subcommand\n", total_sec_count));

			cmd_type = ATCM_FW_MULTICMD;
			if (!(ata_drvp->ad_id.ai_padding2[15] & 0x10)) {
				ADBG_ERROR(("This drive doesn't support "
				    "the multicommand subcommand\n"));
				return (ENOTSUP);
			}
		}
	}

	fwfile_memp = kmem_zalloc(size, KM_SLEEP);

	if (ddi_copyin(fwfile, fwfile_memp, size, flag)) {
		ADBG_ERROR(("ata_disk_update_fw copyin failed\n"));
		rc = EFAULT;
		goto done;
	}

	tmp_fwfile_memp = fwfile_memp;

	for (; total_sec_count > 0; ) {
		if ((gcmdp == NULL) && !(gcmdp =
		    ghd_gcmd_alloc(gtgtp, sizeof (*ata_pktp), TRUE))) {
			ADBG_ERROR(("ata_disk_update_fw alloc failed\n"));
			rc = ENOMEM;
			goto done;
		}

		/* set the back ptr from the ata_pkt to the gcmd_t */
		ata_pktp = GCMD2APKT(gcmdp);
		ata_pktp->ap_gcmdp = gcmdp;
		ata_pktp->ap_hd = ata_drvp->ad_drive_bits;
		ata_pktp->ap_bytes_per_block = ata_drvp->ad_bytes_per_block;

		/* use PIO mode to update disk firmware */
		ata_pktp->ap_start = ata_disk_start_pio_out;
		ata_pktp->ap_intr = ata_disk_intr_pio_out;
		ata_pktp->ap_complete = NULL;

		ata_pktp->ap_cmd = ATC_LOAD_FW;
		/* use ap_bcount to set subcommand code */
		ata_pktp->ap_bcount = (size_t)cmd_type;
		ata_pktp->ap_pciide_dma = FALSE;
		ata_pktp->ap_sg_cnt = 0;

		sec_count = min(total_sec_count, MAX_FWFILE_SIZE_ONECMD);
		ata_pktp->ap_flags = 0;

		ata_pktp->ap_count = sec_count;
		ata_pktp->ap_startsec = start_sec;
		ata_pktp->ap_v_addr = tmp_fwfile_memp;
		ata_pktp->ap_resid = sec_count << SCTRSHFT;

		/* add it to the queue, and use POLL mode */
		rc = ghd_transport(&ata_ctlp->ac_ccc, gcmdp, gcmdp->cmd_gtgtp,
		    ata_disk_updatefw_time, TRUE, NULL);

		if (rc != TRAN_ACCEPT) {
			/* this should never, ever happen */
			rc = ENOTSUP;
			goto done;
		}

		if (ata_pktp->ap_flags & AP_ERROR) {
			if (ata_pktp->ap_error & ATE_ABORT) {
				rc = ENOTSUP;
			} else
				rc = EIO;
			goto done;

		} else {
			total_sec_count -= sec_count;
			tmp_fwfile_memp += sec_count << SCTRSHFT;
			start_sec += sec_count;
		}
	}

	rc = 0;
done:
	if (gcmdp != NULL)
		ghd_gcmd_free(gcmdp);

	kmem_free(fwfile_memp, size);

	return (rc);
}