ata_disk.c revision 0f2c99a46e005b1add7df43157ee8516e585157a
/*
* 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);
}