mtip32xx: add trim support
[firefly-linux-kernel-4.4.55.git] / drivers / block / mtip32xx / mtip32xx.c
index 99c2cf461be38a77c832c6c35f9b0cc92a6a7ca5..b7579b76b4a1d9e2076676fb451e6476d8722115 100644 (file)
@@ -1519,6 +1519,12 @@ static int mtip_get_identify(struct mtip_port *port, void __user *user_buffer)
        }
 #endif
 
+       /* Demux ID.DRAT & ID.RZAT to determine trim support */
+       if (port->identify[69] & (1 << 14) && port->identify[69] & (1 << 5))
+               port->dd->trim_supp = true;
+       else
+               port->dd->trim_supp = false;
+
        /* Set the identify buffer as valid. */
        port->identify_valid = 1;
 
@@ -1705,6 +1711,81 @@ static int mtip_get_smart_attr(struct mtip_port *port, unsigned int id,
        return rv;
 }
 
+/*
+ * Trim unused sectors
+ *
+ * @dd         pointer to driver_data structure
+ * @lba                starting lba
+ * @len                # of 512b sectors to trim
+ *
+ * return value
+ *      -ENOMEM                Out of dma memory
+ *      -EINVAL                Invalid parameters passed in, trim not supported
+ *      -EIO           Error submitting trim request to hw
+ */
+int mtip_send_trim(struct driver_data *dd, unsigned int lba, unsigned int len)
+{
+       int i, rv = 0;
+       u64 tlba, tlen, sect_left;
+       struct mtip_trim_entry *buf;
+       dma_addr_t dma_addr;
+       struct host_to_dev_fis fis;
+
+       if (!len || dd->trim_supp == false)
+               return -EINVAL;
+
+       /* Trim request too big */
+       WARN_ON(len > (MTIP_MAX_TRIM_ENTRY_LEN * MTIP_MAX_TRIM_ENTRIES));
+
+       /* Trim request not aligned on 4k boundary */
+       WARN_ON(len % 8 != 0);
+
+       /* Warn if vu_trim structure is too big */
+       WARN_ON(sizeof(struct mtip_trim) > ATA_SECT_SIZE);
+
+       /* Allocate a DMA buffer for the trim structure */
+       buf = dmam_alloc_coherent(&dd->pdev->dev, ATA_SECT_SIZE, &dma_addr,
+                                                               GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+       memset(buf, 0, ATA_SECT_SIZE);
+
+       for (i = 0, sect_left = len, tlba = lba;
+                       i < MTIP_MAX_TRIM_ENTRIES && sect_left;
+                       i++) {
+               tlen = (sect_left >= MTIP_MAX_TRIM_ENTRY_LEN ?
+                                       MTIP_MAX_TRIM_ENTRY_LEN :
+                                       sect_left);
+               buf[i].lba = __force_bit2int cpu_to_le32(tlba);
+               buf[i].range = __force_bit2int cpu_to_le16(tlen);
+               tlba += tlen;
+               sect_left -= tlen;
+       }
+       WARN_ON(sect_left != 0);
+
+       /* Build the fis */
+       memset(&fis, 0, sizeof(struct host_to_dev_fis));
+       fis.type       = 0x27;
+       fis.opts       = 1 << 7;
+       fis.command    = 0xfb;
+       fis.features   = 0x60;
+       fis.sect_count = 1;
+       fis.device     = ATA_DEVICE_OBS;
+
+       if (mtip_exec_internal_command(dd->port,
+                                       &fis,
+                                       5,
+                                       dma_addr,
+                                       ATA_SECT_SIZE,
+                                       0,
+                                       GFP_KERNEL,
+                                       MTIP_TRIM_TIMEOUT_MS) < 0)
+               rv = -EIO;
+
+       dmam_free_coherent(&dd->pdev->dev, ATA_SECT_SIZE, buf, dma_addr);
+       return rv;
+}
+
 /*
  * Get the drive capacity.
  *
@@ -3675,6 +3756,12 @@ static void mtip_make_request(struct request_queue *queue, struct bio *bio)
                }
        }
 
+       if (unlikely(bio->bi_rw & REQ_DISCARD)) {
+               bio_endio(bio, mtip_send_trim(dd, bio->bi_sector,
+                                               bio_sectors(bio)));
+               return;
+       }
+
        if (unlikely(!bio_has_data(bio))) {
                blk_queue_flush(queue, 0);
                bio_endio(bio, 0);
@@ -3817,6 +3904,15 @@ skip_create_disk:
         */
        blk_queue_flush(dd->queue, 0);
 
+       /* Signal trim support */
+       if (dd->trim_supp == true) {
+               set_bit(QUEUE_FLAG_DISCARD, &dd->queue->queue_flags);
+               dd->queue->limits.discard_granularity = 4096;
+               blk_queue_max_discard_sectors(dd->queue,
+                       MTIP_MAX_TRIM_ENTRY_LEN * MTIP_MAX_TRIM_ENTRIES);
+               dd->queue->limits.discard_zeroes_data = 0;
+       }
+
        /* Set the capacity of the device in 512 byte sectors. */
        if (!(mtip_hw_get_capacity(dd, &capacity))) {
                dev_warn(&dd->pdev->dev,