diff options
Diffstat (limited to 'drivers/ata/libata-core.c')
-rw-r--r-- | drivers/ata/libata-core.c | 237 |
1 files changed, 207 insertions, 30 deletions
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 55e257c268dd..6be7770f68e9 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -66,6 +66,7 @@ #include <scsi/scsi_host.h> #include <linux/libata.h> #include <asm/byteorder.h> +#include <asm/unaligned.h> #include <linux/cdrom.h> #include <linux/ratelimit.h> #include <linux/pm_runtime.h> @@ -695,7 +696,7 @@ static int ata_rwcmd_protocol(struct ata_taskfile *tf, struct ata_device *dev) * RETURNS: * Block address read from @tf. */ -u64 ata_tf_read_block(struct ata_taskfile *tf, struct ata_device *dev) +u64 ata_tf_read_block(const struct ata_taskfile *tf, struct ata_device *dev) { u64 block = 0; @@ -720,7 +721,7 @@ u64 ata_tf_read_block(struct ata_taskfile *tf, struct ata_device *dev) if (!sect) { ata_dev_warn(dev, "device reported invalid CHS sector 0\n"); - sect = 1; /* oh well */ + return U64_MAX; } block = (cyl * dev->heads + head) * dev->sectors + sect - 1; @@ -884,7 +885,7 @@ unsigned long ata_pack_xfermask(unsigned long pio_mask, * @udma_mask: resulting udma_mask * * Unpack @xfer_mask into @pio_mask, @mwdma_mask and @udma_mask. - * Any NULL distination masks will be ignored. + * Any NULL destination masks will be ignored. */ void ata_unpack_xfermask(unsigned long xfer_mask, unsigned long *pio_mask, unsigned long *mwdma_mask, unsigned long *udma_mask) @@ -2079,6 +2080,81 @@ static inline u8 ata_dev_knobble(struct ata_device *dev) return ((ap->cbl == ATA_CBL_SATA) && (!ata_id_is_sata(dev->id))); } +static void ata_dev_config_ncq_send_recv(struct ata_device *dev) +{ + struct ata_port *ap = dev->link->ap; + unsigned int err_mask; + int log_index = ATA_LOG_NCQ_SEND_RECV * 2; + u16 log_pages; + + err_mask = ata_read_log_page(dev, ATA_LOG_DIRECTORY, + 0, ap->sector_buf, 1); + if (err_mask) { + ata_dev_dbg(dev, + "failed to get Log Directory Emask 0x%x\n", + err_mask); + return; + } + log_pages = get_unaligned_le16(&ap->sector_buf[log_index]); + if (!log_pages) { + ata_dev_warn(dev, + "NCQ Send/Recv Log not supported\n"); + return; + } + err_mask = ata_read_log_page(dev, ATA_LOG_NCQ_SEND_RECV, + 0, ap->sector_buf, 1); + if (err_mask) { + ata_dev_dbg(dev, + "failed to get NCQ Send/Recv Log Emask 0x%x\n", + err_mask); + } else { + u8 *cmds = dev->ncq_send_recv_cmds; + + dev->flags |= ATA_DFLAG_NCQ_SEND_RECV; + memcpy(cmds, ap->sector_buf, ATA_LOG_NCQ_SEND_RECV_SIZE); + + if (dev->horkage & ATA_HORKAGE_NO_NCQ_TRIM) { + ata_dev_dbg(dev, "disabling queued TRIM support\n"); + cmds[ATA_LOG_NCQ_SEND_RECV_DSM_OFFSET] &= + ~ATA_LOG_NCQ_SEND_RECV_DSM_TRIM; + } + } +} + +static void ata_dev_config_ncq_non_data(struct ata_device *dev) +{ + struct ata_port *ap = dev->link->ap; + unsigned int err_mask; + int log_index = ATA_LOG_NCQ_NON_DATA * 2; + u16 log_pages; + + err_mask = ata_read_log_page(dev, ATA_LOG_DIRECTORY, + 0, ap->sector_buf, 1); + if (err_mask) { + ata_dev_dbg(dev, + "failed to get Log Directory Emask 0x%x\n", + err_mask); + return; + } + log_pages = get_unaligned_le16(&ap->sector_buf[log_index]); + if (!log_pages) { + ata_dev_warn(dev, + "NCQ Send/Recv Log not supported\n"); + return; + } + err_mask = ata_read_log_page(dev, ATA_LOG_NCQ_NON_DATA, + 0, ap->sector_buf, 1); + if (err_mask) { + ata_dev_dbg(dev, + "failed to get NCQ Non-Data Log Emask 0x%x\n", + err_mask); + } else { + u8 *cmds = dev->ncq_non_data_cmds; + + memcpy(cmds, ap->sector_buf, ATA_LOG_NCQ_NON_DATA_SIZE); + } +} + static int ata_dev_config_ncq(struct ata_device *dev, char *desc, size_t desc_sz) { @@ -2123,29 +2199,125 @@ static int ata_dev_config_ncq(struct ata_device *dev, snprintf(desc, desc_sz, "NCQ (depth %d/%d)%s", hdepth, ddepth, aa_desc); - if ((ap->flags & ATA_FLAG_FPDMA_AUX) && - ata_id_has_ncq_send_and_recv(dev->id)) { - err_mask = ata_read_log_page(dev, ATA_LOG_NCQ_SEND_RECV, - 0, ap->sector_buf, 1); - if (err_mask) { - ata_dev_dbg(dev, - "failed to get NCQ Send/Recv Log Emask 0x%x\n", - err_mask); - } else { - u8 *cmds = dev->ncq_send_recv_cmds; + if ((ap->flags & ATA_FLAG_FPDMA_AUX)) { + if (ata_id_has_ncq_send_and_recv(dev->id)) + ata_dev_config_ncq_send_recv(dev); + if (ata_id_has_ncq_non_data(dev->id)) + ata_dev_config_ncq_non_data(dev); + } - dev->flags |= ATA_DFLAG_NCQ_SEND_RECV; - memcpy(cmds, ap->sector_buf, ATA_LOG_NCQ_SEND_RECV_SIZE); + return 0; +} - if (dev->horkage & ATA_HORKAGE_NO_NCQ_TRIM) { - ata_dev_dbg(dev, "disabling queued TRIM support\n"); - cmds[ATA_LOG_NCQ_SEND_RECV_DSM_OFFSET] &= - ~ATA_LOG_NCQ_SEND_RECV_DSM_TRIM; - } +static void ata_dev_config_sense_reporting(struct ata_device *dev) +{ + unsigned int err_mask; + + if (!ata_id_has_sense_reporting(dev->id)) + return; + + if (ata_id_sense_reporting_enabled(dev->id)) + return; + + err_mask = ata_dev_set_feature(dev, SETFEATURE_SENSE_DATA, 0x1); + if (err_mask) { + ata_dev_dbg(dev, + "failed to enable Sense Data Reporting, Emask 0x%x\n", + err_mask); + } +} + +static void ata_dev_config_zac(struct ata_device *dev) +{ + struct ata_port *ap = dev->link->ap; + unsigned int err_mask; + u8 *identify_buf = ap->sector_buf; + int log_index = ATA_LOG_SATA_ID_DEV_DATA * 2, i, found = 0; + u16 log_pages; + + dev->zac_zones_optimal_open = U32_MAX; + dev->zac_zones_optimal_nonseq = U32_MAX; + dev->zac_zones_max_open = U32_MAX; + + /* + * Always set the 'ZAC' flag for Host-managed devices. + */ + if (dev->class == ATA_DEV_ZAC) + dev->flags |= ATA_DFLAG_ZAC; + else if (ata_id_zoned_cap(dev->id) == 0x01) + /* + * Check for host-aware devices. + */ + dev->flags |= ATA_DFLAG_ZAC; + + if (!(dev->flags & ATA_DFLAG_ZAC)) + return; + + /* + * Read Log Directory to figure out if IDENTIFY DEVICE log + * is supported. + */ + err_mask = ata_read_log_page(dev, ATA_LOG_DIRECTORY, + 0, ap->sector_buf, 1); + if (err_mask) { + ata_dev_info(dev, + "failed to get Log Directory Emask 0x%x\n", + err_mask); + return; + } + log_pages = get_unaligned_le16(&ap->sector_buf[log_index]); + if (log_pages == 0) { + ata_dev_warn(dev, + "ATA Identify Device Log not supported\n"); + return; + } + /* + * Read IDENTIFY DEVICE data log, page 0, to figure out + * if page 9 is supported. + */ + err_mask = ata_read_log_page(dev, ATA_LOG_SATA_ID_DEV_DATA, 0, + identify_buf, 1); + if (err_mask) { + ata_dev_info(dev, + "failed to get Device Identify Log Emask 0x%x\n", + err_mask); + return; + } + log_pages = identify_buf[8]; + for (i = 0; i < log_pages; i++) { + if (identify_buf[9 + i] == ATA_LOG_ZONED_INFORMATION) { + found++; + break; } } + if (!found) { + ata_dev_warn(dev, + "ATA Zoned Information Log not supported\n"); + return; + } - return 0; + /* + * Read IDENTIFY DEVICE data log, page 9 (Zoned-device information) + */ + err_mask = ata_read_log_page(dev, ATA_LOG_SATA_ID_DEV_DATA, + ATA_LOG_ZONED_INFORMATION, + identify_buf, 1); + if (!err_mask) { + u64 zoned_cap, opt_open, opt_nonseq, max_open; + + zoned_cap = get_unaligned_le64(&identify_buf[8]); + if ((zoned_cap >> 63)) + dev->zac_zoned_cap = (zoned_cap & 1); + opt_open = get_unaligned_le64(&identify_buf[24]); + if ((opt_open >> 63)) + dev->zac_zones_optimal_open = (u32)opt_open; + opt_nonseq = get_unaligned_le64(&identify_buf[32]); + if ((opt_nonseq >> 63)) + dev->zac_zones_optimal_nonseq = (u32)opt_nonseq; + max_open = get_unaligned_le64(&identify_buf[40]); + if ((max_open >> 63)) + dev->zac_zones_max_open = (u32)max_open; + } } /** @@ -2370,7 +2542,8 @@ int ata_dev_configure(struct ata_device *dev) dev->devslp_timing[i] = sata_setting[j]; } } - + ata_dev_config_sense_reporting(dev); + ata_dev_config_zac(dev); dev->cdb_len = 16; } @@ -3399,7 +3572,7 @@ int ata_do_set_mode(struct ata_link *link, struct ata_device **r_failed_dev) * EH context. * * RETURNS: - * 0 if @linke is ready before @deadline; otherwise, -errno. + * 0 if @link is ready before @deadline; otherwise, -errno. */ int ata_wait_ready(struct ata_link *link, unsigned long deadline, int (*check_ready)(struct ata_link *link)) @@ -3480,7 +3653,7 @@ int ata_wait_ready(struct ata_link *link, unsigned long deadline, * EH context. * * RETURNS: - * 0 if @linke is ready before @deadline; otherwise, -errno. + * 0 if @link is ready before @deadline; otherwise, -errno. */ int ata_wait_after_reset(struct ata_link *link, unsigned long deadline, int (*check_ready)(struct ata_link *link)) @@ -3493,7 +3666,7 @@ int ata_wait_after_reset(struct ata_link *link, unsigned long deadline, /** * sata_link_debounce - debounce SATA phy status * @link: ATA link to debounce SATA phy status for - * @params: timing parameters { interval, duratinon, timeout } in msec + * @params: timing parameters { interval, duration, timeout } in msec * @deadline: deadline jiffies for the operation * * Make sure SStatus of @link reaches stable state, determined by @@ -3563,7 +3736,7 @@ int sata_link_debounce(struct ata_link *link, const unsigned long *params, /** * sata_link_resume - resume SATA link * @link: ATA link to resume SATA - * @params: timing parameters { interval, duratinon, timeout } in msec + * @params: timing parameters { interval, duration, timeout } in msec * @deadline: deadline jiffies for the operation * * Resume SATA phy @link and debounce it. @@ -3746,7 +3919,7 @@ int ata_std_prereset(struct ata_link *link, unsigned long deadline) /** * sata_link_hardreset - reset link via SATA phy reset * @link: link to reset - * @timing: timing parameters { interval, duratinon, timeout } in msec + * @timing: timing parameters { interval, duration, timeout } in msec * @deadline: deadline jiffies for the operation * @online: optional out parameter indicating link onlineness * @check_ready: optional callback to check link readiness @@ -4528,6 +4701,7 @@ unsigned int ata_dev_set_feature(struct ata_device *dev, u8 enable, u8 feature) { struct ata_taskfile tf; unsigned int err_mask; + unsigned long timeout = 0; /* set up set-features taskfile */ DPRINTK("set features - SATA features\n"); @@ -4539,7 +4713,10 @@ unsigned int ata_dev_set_feature(struct ata_device *dev, u8 enable, u8 feature) tf.protocol = ATA_PROT_NODATA; tf.nsect = feature; - err_mask = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0, 0); + if (enable == SETFEATURES_SPINUP) + timeout = ata_probe_timeout ? + ata_probe_timeout * 1000 : SETFEATURES_SPINUP_TIMEOUT; + err_mask = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0, timeout); DPRINTK("EXIT, err_mask=%x\n", err_mask); return err_mask; @@ -6208,7 +6385,7 @@ int ata_host_register(struct ata_host *host, struct scsi_host_template *sht) * * After allocating an ATA host and initializing it, most libata * LLDs perform three steps to activate the host - start host, - * request IRQ and register it. This helper takes necessasry + * request IRQ and register it. This helper takes necessary * arguments and performs the three steps in one go. * * An invalid IRQ skips the IRQ registration and expects the host to @@ -6261,7 +6438,7 @@ int ata_host_activate(struct ata_host *host, int irq, } /** - * ata_port_detach - Detach ATA port in prepration of device removal + * ata_port_detach - Detach ATA port in preparation of device removal * @ap: ATA port to be detached * * Detach all ATA devices and the associated SCSI devices of @ap; |