diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-05-23 17:53:39 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-05-23 17:53:39 -0700 |
commit | e4f7bdc2ec0d0dcc27f7d70db27a620dfdc1f697 (patch) | |
tree | b034d89f25c2403ca17eaa356cb37c2d1cb3b803 /drivers/ata/libata-core.c | |
parent | 3159ee58d2133fbf9a710c21201ce717a8cf61a0 (diff) | |
parent | 6d1003ae8db228b74ef61536364cd2a1bd973dd8 (diff) |
Merge branch 'for-4.7-zac' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/libata
Pull libata ZAC support from Tejun Heo:
"This contains Zone ATA Command support for Shingled Magnetic Recording
devices.
In addition to sending the new commands down to the device, as ZAC
commands depend on getting a lot of responses from the device, piping
up responses is beefed up too. However, it doesn't involve changes to
libata core mechanism or its interaction with upper layers, so I'm not
expecting too many fallouts.
Kudos to Hannes for driving SMR support"
* 'for-4.7-zac' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/libata: (28 commits)
libata: support host-aware and host-managed ZAC devices
libata: support device-managed ZAC devices
libata: NCQ encapsulation for ZAC MANAGEMENT OUT
libata: Implement ZBC OUT translation
libata: implement ZBC IN translation
libata: fixup ZAC device disabling
libata-scsi: Generate sense code for disabled devices
libata-trace: decode subcommands
libata: Check log page directory before accessing pages
libata: Add command definitions for NCQ Encapsulation for READ LOG DMA EXT
libata: Separate out ata_dev_config_ncq_send_recv()
libata/libsas: Define ATA_CMD_NCQ_NON_DATA
libsas: enable FPDMA SEND/RECEIVE
libata: do not attempt to retrieve sense code twice
libata-scsi: Set information sense field for invalid parameter
libata-scsi: set bit pointer for sense code information
libata-scsi: Set field pointer in sense code
scsi: add scsi_set_sense_field_pointer()
libata: Implement control mode page to select sense format
libata-scsi: generate correct ATA pass-through sense
...
Diffstat (limited to 'drivers/ata/libata-core.c')
-rw-r--r-- | drivers/ata/libata-core.c | 215 |
1 files changed, 194 insertions, 21 deletions
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 6f33ace33daf..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; @@ -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; } |