#include <sys/types.h>
#include <sys/dkio.h>
#include <sys/cdio.h>
#include <sys/file.h>
#include "ata_common.h"
#include "ata_disk.h"
typedef struct cmpkt cmpkt_t;
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);
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);
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);
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;
int ata_disk_updatefw_time = 60;
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
};
int
ata_disk_attach(
ata_ctl_t *ata_ctlp)
{
ADBG_TRACE(("ata_disk_init entered\n"));
return (TRUE);
}
void
ata_disk_detach(
ata_ctl_t *ata_ctlp)
{
ADBG_TRACE(("ata_disk_destroy entered\n"));
}
int
ata_test_lba_support(struct ata_id *aidp)
{
#ifdef __old_version__
if (aidp->ai_cap & ATAC_LBA_SUPPORT)
return (TRUE);
#else
if (aidp->ai_majorversion != 0xffff &&
aidp->ai_majorversion >= (1 << 3)) {
return (TRUE);
} else if (aidp->ai_cap & ATAC_LBA_SUPPORT) {
return (TRUE);
} else {
return (FALSE);
}
#endif
}
static void
ata_fixup_ata6_geometry(struct ata_id *aidp)
{
if (aidp->ai_heads != 0 && aidp->ai_heads != 0xffff &&
aidp->ai_sectors != 0 && aidp->ai_sectors != 0xffff &&
aidp->ai_fixcyls != 0)
return;
aidp->ai_heads = 0x10;
aidp->ai_sectors = 0x3f;
aidp->ai_fixcyls = 1;
}
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"));
if (ata_drvp->ad_lun != 0)
return (FALSE);
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;
mode = ata_get_capacity(ata_drvp, &ata_drvp->ad_capacity);
if (mode == AD_EXT48) {
ata_drvp->ad_flags |= AD_EXT48;
}
(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 (chs[1] == (ata_drvp->ad_drvrhd - 1) &&
chs[2] == ata_drvp->ad_drvrsec) {
chs[0] = aidp->ai_fixcyls - 1;
} else if (!(ata_drvp->ad_drive_bits & ATDH_LBA)) {
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);
}
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 {
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 {
ata_drvp->ad_drvrcyl = 1025;
ata_drvp->ad_drvrhd = 1;
ata_drvp->ad_drvrsec = 1;
}
}
ata_fix_large_disk_geometry(ata_drvp);
devp = kmem_zalloc(scsi_device_size(), KM_SLEEP);
ata_drvp->ad_device = devp;
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;
ctlobjp->c_ops = (struct ctl_objops *)&ata_disk_objops;
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;
ata_drvp->ad_block_factor = aidp->ai_mult1 & 0xff;
(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)) {
ata_drvp->ad_device = NULL;
kmem_free(devp, scsi_device_size());
return (FALSE);
}
ata_disk_fake_inquiry(ata_drvp);
return (TRUE);
}
static int
ata_get_capacity(ata_drv_t *ata_drvp, uint64_t *capacity)
{
struct ata_id *aidp = &ata_drvp->ad_id;
uint64_t cap28;
uint64_t cap48;
cap28 = ata_calculate_28bits_capacity(ata_drvp);
*capacity = cap28;
if (!IS_ATA_VERSION_SUPPORTED(aidp, 6) &&
!(ata_drvp->ad_flags & AD_BLLBA48))
return (0);
if (!(aidp->ai_cmdset83 & ATACS_EXT48))
return (0);
if (!(aidp->ai_features86 & ATACS_EXT48))
return (0);
ata_fixup_ata6_geometry(aidp);
cap48 = ata_calculate_48bits_capacity(ata_drvp);
if (cap48 <= MAX_28BIT_CAPACITY)
return (0);
if (cap28 != MAX_28BIT_CAPACITY)
return (0);
ADBG_INIT(("ATA: using 48-bit mode for capacity %llx blocks\n",
(unsigned long long)cap48));
*capacity = cap48;
return (AD_EXT48);
}
static void
ata_fix_large_disk_geometry(
ata_drv_t *ata_drvp)
{
struct ata_id *aidp = &ata_drvp->ad_id;
if (!(ata_drvp->ad_drive_bits & ATDH_LBA))
return;
while (ata_drvp->ad_drvrcyl > USHRT_MAX) {
int tempheads;
tempheads = 2 * ata_drvp->ad_drvrhd;
if (tempheads > USHRT_MAX) {
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;
}
ata_drvp->ad_drvrcyl /= 2;
ata_drvp->ad_drvrhd *= 2;
}
}
uint64_t
ata_calculate_28bits_capacity(ata_drv_t *ata_drvp)
{
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));
}
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);
}
return ((uint64_t)(ata_drvp->ad_id.ai_fixcyls *
ata_drvp->ad_id.ai_heads * ata_drvp->ad_id.ai_sectors));
}
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);
}
int
ata_disk_setup_parms(
ata_ctl_t *ata_ctlp,
ata_drv_t *ata_drvp)
{
if (!ata_disk_initialize_device_parameters(ata_ctlp, ata_drvp)) {
return (FALSE);
}
if (ata_drvp->ad_block_factor > 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);
}
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;
if (timeout < 0 || timeout > (12 * 60 * 60))
return;
if (timeout <= (240 * 5))
parm = (timeout + 4) / 5;
else if (timeout <= (21 * 60))
parm = 252;
else if (timeout <= ((21 * 60) + 15))
parm = 255;
else if (timeout <= (11 * 30 * 60))
parm = 240 + ((timeout + (30 * 60) - 1)/ (30 * 60));
else
parm = 253;
(void) ata_command(ata_ctlp, ata_drvp, TRUE, FALSE, 5 * 1000000,
ATC_IDLE, 0, parm, 0, 0, 0, 0);
}
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 (devp) {
if (ata_drvp->ad_flags & AD_MUTEX_INIT)
mutex_destroy(&devp->sd_mutex);
ata_drvp->ad_device = NULL;
kmem_free(devp, scsi_device_size());
}
}
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];
ata_drvp = ddi_get_driver_private(cdip);
ata_ctlp = ata_drvp->ad_ctlp;
devp = ata_drvp->ad_device;
if ((devp == NULL) || (devp->sd_dev != NULL)) {
return (DDI_FAILURE);
}
devp->sd_dev = cdip;
ctlobjp = &ata_drvp->ad_ctl_obj;
ctlobjp->c_extblk.c_devdip = cdip;
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);
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;
ctlobjp->c_data = gtgtp;
(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, >GTP2ATAP(gtgtp)->ac_ccc, gtgtp);
ddi_set_driver_private(cdip, NULL);
ddi_set_name_addr(cdip, NULL);
return (DDI_SUCCESS);
}
default:
return (DDI_FAILURE);
}
}
static int
ata_disk_abort(opaque_t ctl_data, cmpkt_t *pktp)
{
ADBG_TRACE(("ata_disk_abort entered\n"));
return (DDI_FAILURE);
}
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"));
switch (level) {
case RESET_TARGET:
rc = ghd_tran_reset_target(&ata_drvp->ad_ctlp->ac_ccc, gtgtp,
NULL);
break;
case RESET_ALL:
rc = ghd_tran_reset_bus(&ata_drvp->ad_ctlp->ac_ccc, gtgtp,
NULL);
break;
default:
rc = 0;
}
return (rc ? DDI_SUCCESS : DDI_FAILURE);
}
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 = (diskaddr_t)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:
return (0);
case DIOCTL_GETMODEL:
rc = ata_copy_dk_ioc_string(arg, aidp->ai_model,
sizeof (aidp->ai_model), flag);
return (rc);
case DIOCTL_GETSERIAL:
rc = ata_copy_dk_ioc_string(arg, aidp->ai_drvser,
sizeof (aidp->ai_drvser), flag);
return (rc);
case DIOCTL_GETWCE:
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:
#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);
if (rc == 0) {
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) {
if (aidp->ai_resv0 == 0x37c8 ||
aidp->ai_resv0 == 0x738c) {
(void) ata_queue_cmd(
ata_disk_set_feature_spinup,
NULL,
ata_ctlp,
ata_drvp,
gtgtp);
}
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);
}
}
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;
(void) strncpy(dk_disk_id.disk_id.ata_disk_id.dkd_amodel,
aidp->ai_model, sizeof (aidp->ai_model));
(void) strncpy(dk_disk_id.disk_id.ata_disk_id.dkd_afwver,
aidp->ai_fw, sizeof (aidp->ai_fw));
(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___
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;
rc = ghd_transport(&ata_ctlp->ac_ccc, gcmdp, gcmdp->cmd_gtgtp,
0, TRUE, NULL);
if (rc != TRAN_ACCEPT) {
return (ENXIO);
}
if (ata_pktp->ap_flags & AP_ERROR)
return (ENXIO);
return (0);
}
#endif
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"));
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;
ata_pktp->ap_start = ata_disk_start;
ata_pktp->ap_intr = ata_disk_intr;
ata_pktp->ap_complete = ata_disk_complete;
ata_pktp->ap_bytes_per_block = ata_drvp->ad_bytes_per_block;
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);
}
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"));
ASSERT(!(ata_pktp->ap_flags & AP_FREE));
ata_pktp->ap_flags = AP_FREE;
ghd_gcmd_free(CPKT2GCMD(pktp));
}
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;
if (!(bp->b_flags & B_PAGEIO) &&
((uintptr_t)bp->b_un.b_addr) & PCIIDE_PRDE_ADDR_MASK) {
goto skip_dma_setup;
}
if (bp->b_bcount & 1)
goto skip_dma_setup;
if (bp->b_flags & B_READ) {
flags = DDI_DMA_READ | DDI_DMA_PARTIAL;
} else {
flags = DDI_DMA_WRITE | DDI_DMA_PARTIAL;
}
if (ghd_dma_buf_bind_attr(>GTP2ATAP(gtgtp)->ac_ccc, gcmdp, bp, flags,
callback, arg, >GTP2ATATGTP(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);
}
#define BUG_1157317
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
}
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"));
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);
}
if (ata_pktp->ap_flags & AP_ERROR) {
if (gcmdp->cmd_dma_handle != NULL) {
ata_pktp->ap_flags = 0;
return (NULL);
}
ata_pktp->ap_bytes_per_block = NBPSCTR;
sec_count = 1;
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 {
sec_count = min((pktp->cp_bytexfer >> SCTRSHFT),
ata_drvp->ad_ctlp->ac_max_transfer);
ata_pktp->ap_v_addr_sav = ata_pktp->ap_v_addr;
ata_pktp->ap_resid_sav = ata_pktp->ap_resid;
}
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
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(>GTP2ATAP(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;
}
ata_pktp->ap_count = (ushort_t)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;
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;
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 (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);
}
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"));
if (pktp->cp_flags & CPF_NOINTR) {
polled = TRUE;
}
rc = ghd_transport(&ata_ctlp->ac_ccc, APKT2GCMD(ata_pktp),
gtgtp, pktp->cp_time, polled, NULL);
if (rc == TRAN_BUSY)
return (CTL_SEND_BUSY);
if (rc == TRAN_ACCEPT)
return (CTL_SEND_SUCCESS);
return (CTL_SEND_FAILURE);
}
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 = 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;
lba = (lba & 0xf) | ata_drvp->ad_drive_bits;
ddi_put8(io_hdl1, ata_ctlp->ac_drvhd, lba);
}
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;
uint_t lbalow;
uint_t lbahi;
seccnt = ata_pktp->ap_count;
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);
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);
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;
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));
ddi_put8(io_hdl1, ata_ctlp->ac_lcyl, cyl);
ddi_put8(io_hdl1, ata_ctlp->ac_drvhd, ata_drvp->ad_drive_bits | head);
}
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 (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_nsecwait(400);
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) {
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);
ddi_put8(io_hdl1, ata_ctlp->ac_feature, ata_pktp->ap_bcount);
} else {
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);
}
ddi_put8(io_hdl2, ata_ctlp->ac_devctl, ATDC_D3);
return (TRUE);
}
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;
int rc;
rc = ata_disk_start_common(ata_ctlp, ata_drvp, ata_pktp);
if (!rc)
return (ATA_FSM_RC_BUSY);
ddi_put8(io_hdl1, ata_ctlp->ac_cmd, ata_pktp->ap_cmd);
ata_nsecwait(400);
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;
int rc;
rc = ata_disk_start_common(ata_ctlp, ata_drvp, ata_pktp);
if (!rc)
return (ATA_FSM_RC_BUSY);
ata_pciide_dma_setup(ata_ctlp, ata_pktp->ap_sg_list,
ata_pktp->ap_sg_cnt);
(void) ata_pciide_status_clear(ata_ctlp);
ddi_put8(io_hdl1, ata_ctlp->ac_cmd, ata_pktp->ap_cmd);
ata_nsecwait(400);
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;
int rc;
rc = ata_disk_start_common(ata_ctlp, ata_drvp, ata_pktp);
if (!rc)
return (ATA_FSM_RC_BUSY);
ata_pciide_dma_setup(ata_ctlp, ata_pktp->ap_sg_list,
ata_pktp->ap_sg_cnt);
(void) ata_pciide_status_clear(ata_ctlp);
ddi_put8(io_hdl1, ata_ctlp->ac_cmd, ata_pktp->ap_cmd);
ata_nsecwait(400);
ata_pciide_dma_start(ata_ctlp, PCIIDE_BMICX_RWCON_READ_FROM_MEMORY);
return (ATA_FSM_RC_OKAY);
}
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;
int rc;
rc = ata_disk_start_common(ata_ctlp, ata_drvp, ata_pktp);
if (!rc)
return (ATA_FSM_RC_BUSY);
ddi_put8(io_hdl1, ata_ctlp->ac_cmd, ata_pktp->ap_cmd);
ata_nsecwait(400);
return (ATA_FSM_RC_OKAY);
}
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);
ddi_put8(io_hdl1, ata_ctlp->ac_cmd, ata_pktp->ap_cmd);
ata_nsecwait(400);
if (!ata_wait3(io_hdl2, ata_ctlp->ac_ioaddr2,
ATS_DRQ, ATS_BSY,
ATS_ERR, ATS_BSY,
ATS_DF, ATS_BSY,
4000000)) {
ADBG_WARN(("ata_disk_start_pio_out: no DRQ\n"));
ata_pktp->ap_flags |= AP_ERROR;
return (ATA_FSM_RC_INTR);
}
return (ATA_FSM_RC_INTR);
}
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);
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
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;
}
if (do_callback)
(*pktp->cp_callback)(pktp);
}
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);
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);
}
return (ATA_FSM_RC_FINI);
}
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));
(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);
}
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);
}
if (status & ATS_DRQ) {
ata_disk_pio_xfer_data_in(ata_ctlp, ata_pktp);
}
if (ata_pktp->ap_resid == 0) {
if (ata_wait(io_hdl2, ata_ctlp->ac_ioaddr2,
0, (ATS_DRQ | ATS_BSY), 4000000)) {
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);
}
if (ata_pktp->ap_flags & AP_ERROR) {
return (ATA_FSM_RC_FINI);
}
ADBG_TRACE(("ata_disk_pio_in: partial\n"));
return (ATA_FSM_RC_OKAY);
}
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;
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));
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);
return (ATA_FSM_RC_FINI);
}
ata_pktp->ap_v_addr += tmp_count;
ata_pktp->ap_resid -= tmp_count;
if (ata_pktp->ap_resid == 0) {
return (ATA_FSM_RC_FINI);
}
(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) {
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);
}
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);
}
ADBG_TRACE(("ata_disk_intr_pio_out: write xfer\n"));
ata_disk_pio_xfer_data_out(ata_ctlp, ata_pktp);
return (ATA_FSM_RC_OKAY);
}
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));
ata_pciide_dma_stop(ata_ctlp);
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);
}
status = ata_get_status_clear_intr(ata_ctlp, ata_pktp);
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 (ata_pktp->ap_flags & AP_ERROR) {
ata_disk_get_resid(ata_ctlp, ata_drvp, ata_pktp);
} else {
ata_pktp->ap_resid = 0;
}
return (ATA_FSM_RC_FINI);
}
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;
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));
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);
ata_nsecwait(400);
ata_pktp->ap_v_addr += count;
ata_pktp->ap_resid -= count;
}
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;
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));
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);
ata_nsecwait(400);
ata_pktp->ap_wrt_count = count;
}
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,
ata_drvp->ad_phsec,
0,
(ata_drvp->ad_phhd -1),
0,
0);
if (rc)
return (TRUE);
ADBG_ERROR(("ata_init_dev_parms: failed\n"));
return (FALSE);
}
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)
inqp->inq_rmb = 1;
(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
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,
ata_drvp->ad_block_factor,
0,
0,
0,
0);
if (rc) {
return (TRUE);
}
ADBG_ERROR(("ata_disk_set_multiple: failed\n"));
return (FALSE);
}
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 (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;
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;
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);
}
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
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;
}
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
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;
if ((flag & FKIOCTL) == 0)
return (EFAULT);
STRUCT_INIT(ds_arg, flag & FMODELS);
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;
STRUCT_FSET(ds_arg, is_size, length);
if (ddi_copyout(STRUCT_BUF(ds_arg), (caddr_t)arg, STRUCT_SIZE(ds_arg),
flag))
return (EFAULT);
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);
}
static void
ata_set_write_cache(ata_ctl_t *ata_ctlp, ata_drv_t *ata_drvp)
{
char *path;
if (!(IS_WRITE_CACHE_SUPPORTED(ata_drvp->ad_id)))
return;
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);
}
}
}
}
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);
}
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"));
ddi_put8(io_hdl1, (uchar_t *)ioaddr1 + AT_DRVHD,
ata_drvp->ad_drive_bits);
ata_nsecwait(400);
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);
}
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;
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);
}
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;
}
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;
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;
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 = (ushort_t)sec_count;
ata_pktp->ap_startsec = start_sec;
ata_pktp->ap_v_addr = tmp_fwfile_memp;
ata_pktp->ap_resid = sec_count << SCTRSHFT;
rc = ghd_transport(&ata_ctlp->ac_ccc, gcmdp, gcmdp->cmd_gtgtp,
ata_disk_updatefw_time, TRUE, NULL);
if (rc != TRAN_ACCEPT) {
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);
}