Merge branch 'for-4.7-zac' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/libata
[linux-2.6-microblaze.git] / drivers / ata / libata-core.c
index 6f33ace..6be7770 100644 (file)
@@ -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;
        }