@@ -65,6 +65,7 @@ static struct ata_device *__ata_scsi_find_dev(struct ata_port *ap,
const struct scsi_device *scsidev);
static struct ata_device *ata_scsi_find_dev(struct ata_port *ap,
const struct scsi_device *scsidev);
+static void scsi_16_lba_len(const u8 *cdb, u64 *plba, u32 *plen);
#define RW_RECOVERY_MPAGE 0x1
#define RW_RECOVERY_MPAGE_LEN 12
@@ -1442,6 +1443,160 @@ static unsigned int ata_scsi_flush_xlat(struct ata_queued_cmd *qc)
}
/**
+ * ata_scsi_zone_command_xlat - Translate SCSI Reset Write Pointer command
+ * @qc: Storage for translated ATA taskfile
+ *
+ * Sets up an ATA taskfile to issue Reset Write Pointers Ext command.
+ * May need change when zac specs is available.
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host lock)
+ *
+ * RETURNS:
+ * Zero on success, non-zero on error.
+ */
+static unsigned int ata_scsi_zone_command_xlat(struct ata_queued_cmd *qc)
+{
+ struct scsi_cmnd *scmd = qc->scsicmd;
+ struct ata_taskfile *tf = &qc->tf;
+ const u8 *cdb = scmd->cmnd;
+ u8 sa; /* service action */
+ u8 all_bit;
+
+ if (scmd->cmd_len < 16)
+ goto invalid_fld;
+
+ sa = cdb[1] & 0x1f;
+
+ if (!(sa == ATA_SUBCMD_CLOSE_ZONES ||
+ sa == ATA_SUBCMD_FINISH_ZONES ||
+ sa == ATA_SUBCMD_OPEN_ZONES ||
+ sa == ATA_SUBCMD_RESET_WP))
+ goto invalid_fld;
+
+ all_bit = cdb[14] & 0x01;
+ if (!all_bit) {
+ struct ata_device *dev = qc->dev;
+ u64 max_lba = dev->n_sectors; /* Maximal LBA supported */
+ u64 slba;
+ u32 slen;
+
+ scsi_16_lba_len(cdb, &slba, &slen);
+ if (slba > max_lba) {
+ ata_dev_err(dev,
+ "Zone start LBA %llu > %llu (Max LBA)\n",
+ slba, max_lba);
+ goto out_of_range;
+ }
+
+ tf->hob_lbah = (slba >> 40) & 0xff;
+ tf->hob_lbam = (slba >> 32) & 0xff;
+ tf->hob_lbal = (slba >> 24) & 0xff;
+ tf->lbah = (slba >> 16) & 0xff;
+ tf->lbam = (slba >> 8) & 0xff;
+ tf->lbal = slba & 0xff;
+ }
+
+
+ tf->flags |= ATA_TFLAG_DEVICE | ATA_TFLAG_LBA48;
+ tf->protocol = ATA_PROT_NODATA;
+
+ tf->command = ATA_CMD_ZONE_MAN_OUT;
+ tf->feature = sa;
+ tf->hob_feature = all_bit;
+
+ return 0;
+
+ invalid_fld:
+ ata_scsi_set_sense(scmd, ILLEGAL_REQUEST, 0x24, 0x0);
+ /* "Invalid field in cbd" */
+ return 1;
+ out_of_range:
+ ata_scsi_set_sense(scmd, ILLEGAL_REQUEST, 0x21, 0x0);
+ /* LBA out of range */
+ return 1;
+}
+
+/**
+ * ata_scsi_report_zones_xlat - Translate SCSI Report Zones command
+ * @qc: Storage for translated ATA taskfile
+ *
+ * Sets up an ATA taskfile to issue Report Zones Ext command.
+ * May need change when zac specs is updated.
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host lock)
+ *
+ * RETURNS:
+ * Zero on success, non-zero on error.
+ */
+static unsigned int ata_scsi_report_zones_xlat(struct ata_queued_cmd *qc)
+{
+ struct ata_device *dev = qc->dev;
+ struct scsi_cmnd *scmd = qc->scsicmd;
+ struct ata_taskfile *tf = &qc->tf;
+ const u8 *cdb = scmd->cmnd;
+ u64 max_lba = dev->n_sectors; /* Maximal LBA supported */
+ u64 slba; /* Start LBA in scsi command */
+ u32 alloc_len; /* Alloc length (in bytes) */
+ u8 reporting_option;
+
+ if (scmd->cmd_len < 16) {
+ ata_dev_err(dev, "ZAC Error: Command length is less than 16\n");
+ goto invalid_fld;
+ }
+ if (unlikely(!dev->dma_mode)) {
+ ata_dev_err(dev, "ZAC Error: No DMA mode is set\n");
+ goto invalid_fld;
+ }
+ if (!scsi_sg_count(scmd)) {
+ ata_dev_err(dev, "ZAC Error: SCSI sg count is zero\n");
+ goto invalid_fld;
+ }
+ scsi_16_lba_len(cdb, &slba, &alloc_len);
+ if (slba > max_lba) {
+ ata_dev_err(dev, "Zone start LBA %llu > %llu (Max LBA)\n",
+ slba, max_lba);
+ goto out_of_range;
+ }
+
+ reporting_option = cdb[14] & 0x3f;
+
+ tf->flags |= ATA_TFLAG_DEVICE | ATA_TFLAG_LBA48 | ATA_TFLAG_ISADDR;
+ tf->protocol = ATA_PROT_DMA;
+
+ tf->command = ATA_CMD_ZONE_MAN_IN;
+
+ tf->hob_lbah = (slba >> 40) & 0xff;
+ tf->hob_lbam = (slba >> 32) & 0xff;
+ tf->hob_lbal = (slba >> 24) & 0xff;
+ tf->lbah = (slba >> 16) & 0xff;
+ tf->lbam = (slba >> 8) & 0xff;
+ tf->lbal = slba & 0xff;
+
+ tf->feature = 0x00;
+ tf->hob_feature = reporting_option;
+
+ alloc_len /= 512; /* bytes in scsi, blocks in ata */
+ tf->nsect = alloc_len & 0xff;
+ tf->hob_nsect = alloc_len >> 8;
+
+ ata_qc_set_pc_nbytes(qc);
+
+ return 0;
+
+ invalid_fld:
+ ata_scsi_set_sense(scmd, ILLEGAL_REQUEST, 0x24, 0x0);
+ /* "Invalid field in cbd" */
+ return 1;
+ out_of_range:
+ ata_scsi_set_sense(scmd, ILLEGAL_REQUEST, 0x21, 0x0);
+ /* LBA out of range */
+ return 1;
+}
+
+
+/**
* scsi_6_lba_len - Get LBA and transfer length
* @cdb: SCSI command to translate
*
@@ -2232,12 +2387,17 @@ static unsigned int ata_scsiop_inq_b1(struct ata_scsi_args *args, u8 *rbuf)
{
int form_factor = ata_id_form_factor(args->id);
int media_rotation_rate = ata_id_rotation_rate(args->id);
+ bool zac_ha = ata_drive_zac_ha(args->id);
rbuf[1] = 0xb1;
rbuf[3] = 0x3c;
rbuf[4] = media_rotation_rate >> 8;
rbuf[5] = media_rotation_rate;
rbuf[7] = form_factor;
+ if (zac_ha) {
+ rbuf[8] &= 0xcf;
+ rbuf[8] |= 0x10; /* SBC4: 0x01 for zoned host aware device */
+ }
return 0;
}
@@ -3421,6 +3581,13 @@ static inline ata_xlat_func_t ata_get_xlat_func(struct ata_device *dev, u8 cmd)
case START_STOP:
return ata_scsi_start_stop_xlat;
+
+ case ZBC_ACTION:
+ return ata_scsi_zone_command_xlat;
+
+ case ZBC_REPORT_ZONES:
+ return ata_scsi_report_zones_xlat;
+ break;
}
return NULL;
@@ -305,6 +305,17 @@ enum {
/* marked obsolete in the ATA/ATAPI-7 spec */
ATA_CMD_RESTORE = 0x10,
+ /* ZAC commands */
+ ATA_CMD_ZONE_MAN_OUT = 0x9F,
+
+ ATA_SUBCMD_CLOSE_ZONES = 0x01,
+ ATA_SUBCMD_FINISH_ZONES = 0x02,
+ ATA_SUBCMD_OPEN_ZONES = 0x03,
+ ATA_SUBCMD_RESET_WP = 0x04,
+
+ ATA_CMD_ZONE_MAN_IN = 0x4A,
+ ATA_SUBCMD_REP_ZONES = 0x00,
+
/* Subcmds for ATA_CMD_FPDMA_SEND */
ATA_SUBCMD_FPDMA_SEND_DSM = 0x00,
ATA_SUBCMD_FPDMA_SEND_WR_LOG_DMA_EXT = 0x02,
@@ -899,6 +910,13 @@ static inline bool ata_drive_40wire_relaxed(const u16 *dev_id)
return true;
}
+static inline bool ata_drive_zac_ha(const u16 *dev_id)
+{
+ if ((dev_id[69] & 0x0003) == 0x0001)
+ return true;
+ return false;
+}
+
static inline int atapi_cdb_len(const u16 *dev_id)
{
u16 tmp = dev_id[ATA_ID_CONFIG] & 0x3;
@@ -115,6 +115,10 @@
#define VERIFY_16 0x8f
#define SYNCHRONIZE_CACHE_16 0x91
#define WRITE_SAME_16 0x93
+/* Op codes for Zoned Block Commands */
+#define ZBC_ACTION 0x94
+#define ZBC_REPORT_ZONES 0x95
+
#define SERVICE_ACTION_BIDIRECTIONAL 0x9d
#define SERVICE_ACTION_IN_16 0x9e
#define SERVICE_ACTION_OUT_16 0x9f
@@ -157,6 +161,16 @@
#define ATA_16 0x85 /* 16-byte pass-thru */
#define ATA_12 0xa1 /* 12-byte pass-thru */
+/* ZBC_ACTION: Values for T-10 ZBC sub action */
+#define ZBC_SA_ZONE_CLOSE 0x01
+#define ZBC_SA_ZONE_FINISH 0x02
+#define ZBC_SA_ZONE_OPEN 0x03
+#define ZBC_SA_RESET_WP 0x04
+
+/* ZBC_REPORT_ZONES: Default report option. T-10 ZBC report zones */
+#define ZBC_REPORT_OPT 0x00
+
+
/* Vendor specific CDBs start here */
#define VENDOR_SPECIFIC_CDB 0xc0
Provide SCSI <-> ATA translation layer for ZBC commands: - Report Zones - Open, Close, Reset and Finish zones Signed-off-by: Shaun Tancheff <shaun.tancheff@seagate.com> --- drivers/ata/libata-scsi.c | 167 ++++++++++++++++++++++++++++++++++++++++++++++ include/linux/ata.h | 18 +++++ include/scsi/scsi_proto.h | 14 ++++ 3 files changed, 199 insertions(+)