]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - drivers/scsi/esp_scsi.c
Merge tag 'drm-misc-fixes-2018-11-21' of git://anongit.freedesktop.org/drm/drm-misc...
[linux.git] / drivers / scsi / esp_scsi.c
index c3fc34b9964df75a610dd70a74bd1ca23ee147b8..ac7da9db7317318d3c4641fc2976ecbbc43a59fc 100644 (file)
@@ -369,19 +369,28 @@ static void esp_map_dma(struct esp *esp, struct scsi_cmnd *cmd)
 {
        struct esp_cmd_priv *spriv = ESP_CMD_PRIV(cmd);
        struct scatterlist *sg = scsi_sglist(cmd);
-       int dir = cmd->sc_data_direction;
-       int total, i;
+       int total = 0, i;
 
-       if (dir == DMA_NONE)
+       if (cmd->sc_data_direction == DMA_NONE)
                return;
 
-       spriv->u.num_sg = esp->ops->map_sg(esp, sg, scsi_sg_count(cmd), dir);
+       if (esp->flags & ESP_FLAG_NO_DMA_MAP) {
+               /*
+                * For pseudo DMA and PIO we need the virtual address instead of
+                * a dma address, so perform an identity mapping.
+                */
+               spriv->num_sg = scsi_sg_count(cmd);
+               for (i = 0; i < spriv->num_sg; i++) {
+                       sg[i].dma_address = (uintptr_t)sg_virt(&sg[i]);
+                       total += sg_dma_len(&sg[i]);
+               }
+       } else {
+               spriv->num_sg = scsi_dma_map(cmd);
+               for (i = 0; i < spriv->num_sg; i++)
+                       total += sg_dma_len(&sg[i]);
+       }
        spriv->cur_residue = sg_dma_len(sg);
        spriv->cur_sg = sg;
-
-       total = 0;
-       for (i = 0; i < spriv->u.num_sg; i++)
-               total += sg_dma_len(&sg[i]);
        spriv->tot_residue = total;
 }
 
@@ -441,13 +450,8 @@ static void esp_advance_dma(struct esp *esp, struct esp_cmd_entry *ent,
 
 static void esp_unmap_dma(struct esp *esp, struct scsi_cmnd *cmd)
 {
-       struct esp_cmd_priv *spriv = ESP_CMD_PRIV(cmd);
-       int dir = cmd->sc_data_direction;
-
-       if (dir == DMA_NONE)
-               return;
-
-       esp->ops->unmap_sg(esp, scsi_sglist(cmd), spriv->u.num_sg, dir);
+       if (!(esp->flags & ESP_FLAG_NO_DMA_MAP))
+               scsi_dma_unmap(cmd);
 }
 
 static void esp_save_pointers(struct esp *esp, struct esp_cmd_entry *ent)
@@ -478,17 +482,6 @@ static void esp_restore_pointers(struct esp *esp, struct esp_cmd_entry *ent)
        spriv->tot_residue = ent->saved_tot_residue;
 }
 
-static void esp_check_command_len(struct esp *esp, struct scsi_cmnd *cmd)
-{
-       if (cmd->cmd_len == 6 ||
-           cmd->cmd_len == 10 ||
-           cmd->cmd_len == 12) {
-               esp->flags &= ~ESP_FLAG_DOING_SLOWCMD;
-       } else {
-               esp->flags |= ESP_FLAG_DOING_SLOWCMD;
-       }
-}
-
 static void esp_write_tgt_config3(struct esp *esp, int tgt)
 {
        if (esp->rev > ESP100A) {
@@ -624,6 +617,26 @@ static void esp_free_lun_tag(struct esp_cmd_entry *ent,
        }
 }
 
+static void esp_map_sense(struct esp *esp, struct esp_cmd_entry *ent)
+{
+       ent->sense_ptr = ent->cmd->sense_buffer;
+       if (esp->flags & ESP_FLAG_NO_DMA_MAP) {
+               ent->sense_dma = (uintptr_t)ent->sense_ptr;
+               return;
+       }
+
+       ent->sense_dma = dma_map_single(esp->dev, ent->sense_ptr,
+                                       SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE);
+}
+
+static void esp_unmap_sense(struct esp *esp, struct esp_cmd_entry *ent)
+{
+       if (!(esp->flags & ESP_FLAG_NO_DMA_MAP))
+               dma_unmap_single(esp->dev, ent->sense_dma,
+                                SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE);
+       ent->sense_ptr = NULL;
+}
+
 /* When a contingent allegiance conditon is created, we force feed a
  * REQUEST_SENSE command to the device to fetch the sense data.  I
  * tried many other schemes, relying on the scsi error handling layer
@@ -645,12 +658,7 @@ static void esp_autosense(struct esp *esp, struct esp_cmd_entry *ent)
        if (!ent->sense_ptr) {
                esp_log_autosense("Doing auto-sense for tgt[%d] lun[%d]\n",
                                  tgt, lun);
-
-               ent->sense_ptr = cmd->sense_buffer;
-               ent->sense_dma = esp->ops->map_single(esp,
-                                                     ent->sense_ptr,
-                                                     SCSI_SENSE_BUFFERSIZE,
-                                                     DMA_FROM_DEVICE);
+               esp_map_sense(esp, ent);
        }
        ent->saved_sense_ptr = ent->sense_ptr;
 
@@ -717,10 +725,10 @@ static struct esp_cmd_entry *find_and_prep_issuable_command(struct esp *esp)
 static void esp_maybe_execute_command(struct esp *esp)
 {
        struct esp_target_data *tp;
-       struct esp_lun_data *lp;
        struct scsi_device *dev;
        struct scsi_cmnd *cmd;
        struct esp_cmd_entry *ent;
+       bool select_and_stop = false;
        int tgt, lun, i;
        u32 val, start_cmd;
        u8 *p;
@@ -743,7 +751,6 @@ static void esp_maybe_execute_command(struct esp *esp)
        tgt = dev->id;
        lun = dev->lun;
        tp = &esp->target[tgt];
-       lp = dev->hostdata;
 
        list_move(&ent->list, &esp->active_cmds);
 
@@ -752,7 +759,8 @@ static void esp_maybe_execute_command(struct esp *esp)
        esp_map_dma(esp, cmd);
        esp_save_pointers(esp, ent);
 
-       esp_check_command_len(esp, cmd);
+       if (!(cmd->cmd_len == 6 || cmd->cmd_len == 10 || cmd->cmd_len == 12))
+               select_and_stop = true;
 
        p = esp->command_block;
 
@@ -793,42 +801,22 @@ static void esp_maybe_execute_command(struct esp *esp)
                        tp->flags &= ~ESP_TGT_CHECK_NEGO;
                }
 
-               /* Process it like a slow command.  */
-               if (tp->flags & (ESP_TGT_NEGO_WIDE | ESP_TGT_NEGO_SYNC))
-                       esp->flags |= ESP_FLAG_DOING_SLOWCMD;
+               /* If there are multiple message bytes, use Select and Stop */
+               if (esp->msg_out_len)
+                       select_and_stop = true;
        }
 
 build_identify:
-       /* If we don't have a lun-data struct yet, we're probing
-        * so do not disconnect.  Also, do not disconnect unless
-        * we have a tag on this command.
-        */
-       if (lp && (tp->flags & ESP_TGT_DISCONNECT) && ent->tag[0])
-               *p++ = IDENTIFY(1, lun);
-       else
-               *p++ = IDENTIFY(0, lun);
+       *p++ = IDENTIFY(tp->flags & ESP_TGT_DISCONNECT, lun);
 
        if (ent->tag[0] && esp->rev == ESP100) {
                /* ESP100 lacks select w/atn3 command, use select
                 * and stop instead.
                 */
-               esp->flags |= ESP_FLAG_DOING_SLOWCMD;
+               select_and_stop = true;
        }
 
-       if (!(esp->flags & ESP_FLAG_DOING_SLOWCMD)) {
-               start_cmd = ESP_CMD_SELA;
-               if (ent->tag[0]) {
-                       *p++ = ent->tag[0];
-                       *p++ = ent->tag[1];
-
-                       start_cmd = ESP_CMD_SA3;
-               }
-
-               for (i = 0; i < cmd->cmd_len; i++)
-                       *p++ = cmd->cmnd[i];
-
-               esp->select_state = ESP_SELECT_BASIC;
-       } else {
+       if (select_and_stop) {
                esp->cmd_bytes_left = cmd->cmd_len;
                esp->cmd_bytes_ptr = &cmd->cmnd[0];
 
@@ -843,6 +831,19 @@ static void esp_maybe_execute_command(struct esp *esp)
 
                start_cmd = ESP_CMD_SELAS;
                esp->select_state = ESP_SELECT_MSGOUT;
+       } else {
+               start_cmd = ESP_CMD_SELA;
+               if (ent->tag[0]) {
+                       *p++ = ent->tag[0];
+                       *p++ = ent->tag[1];
+
+                       start_cmd = ESP_CMD_SA3;
+               }
+
+               for (i = 0; i < cmd->cmd_len; i++)
+                       *p++ = cmd->cmnd[i];
+
+               esp->select_state = ESP_SELECT_BASIC;
        }
        val = tgt;
        if (esp->rev == FASHME)
@@ -902,9 +903,7 @@ static void esp_cmd_is_done(struct esp *esp, struct esp_cmd_entry *ent,
        }
 
        if (ent->flags & ESP_CMD_FLAG_AUTOSENSE) {
-               esp->ops->unmap_single(esp, ent->sense_dma,
-                                      SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE);
-               ent->sense_ptr = NULL;
+               esp_unmap_sense(esp, ent);
 
                /* Restore the message/status bytes to what we actually
                 * saw originally.  Also, report that we are providing
@@ -965,7 +964,7 @@ static int esp_queuecommand_lck(struct scsi_cmnd *cmd, void (*done)(struct scsi_
        cmd->scsi_done = done;
 
        spriv = ESP_CMD_PRIV(cmd);
-       spriv->u.dma_addr = ~(dma_addr_t)0x0;
+       spriv->num_sg = 0;
 
        list_add_tail(&ent->list, &esp->queued_cmds);
 
@@ -1252,14 +1251,10 @@ static int esp_finish_select(struct esp *esp)
                        esp_unmap_dma(esp, cmd);
                        esp_free_lun_tag(ent, cmd->device->hostdata);
                        tp->flags &= ~(ESP_TGT_NEGO_SYNC | ESP_TGT_NEGO_WIDE);
-                       esp->flags &= ~ESP_FLAG_DOING_SLOWCMD;
                        esp->cmd_bytes_ptr = NULL;
                        esp->cmd_bytes_left = 0;
                } else {
-                       esp->ops->unmap_single(esp, ent->sense_dma,
-                                              SCSI_SENSE_BUFFERSIZE,
-                                              DMA_FROM_DEVICE);
-                       ent->sense_ptr = NULL;
+                       esp_unmap_sense(esp, ent);
                }
 
                /* Now that the state is unwound properly, put back onto
@@ -1303,9 +1298,8 @@ static int esp_finish_select(struct esp *esp)
                                esp_flush_fifo(esp);
                }
 
-               /* If we are doing a slow command, negotiation, etc.
-                * we'll do the right thing as we transition to the
-                * next phase.
+               /* If we are doing a Select And Stop command, negotiation, etc.
+                * we'll do the right thing as we transition to the next phase.
                 */
                esp_event(esp, ESP_EVENT_CHECK_PHASE);
                return 0;
@@ -1338,6 +1332,7 @@ static int esp_data_bytes_sent(struct esp *esp, struct esp_cmd_entry *ent,
 
        bytes_sent = esp->data_dma_len;
        bytes_sent -= ecount;
+       bytes_sent -= esp->send_cmd_residual;
 
        /*
         * The am53c974 has a DMA 'pecularity'. The doc states:
@@ -1358,7 +1353,7 @@ static int esp_data_bytes_sent(struct esp *esp, struct esp_cmd_entry *ent,
                        struct esp_cmd_priv *p = ESP_CMD_PRIV(cmd);
                        u8 *ptr;
 
-                       ptr = scsi_kmap_atomic_sg(p->cur_sg, p->u.num_sg,
+                       ptr = scsi_kmap_atomic_sg(p->cur_sg, p->num_sg,
                                                  &offset, &count);
                        if (likely(ptr)) {
                                *(ptr + offset) = bval;
@@ -2039,11 +2034,8 @@ static void esp_reset_cleanup_one(struct esp *esp, struct esp_cmd_entry *ent)
        esp_free_lun_tag(ent, cmd->device->hostdata);
        cmd->result = DID_RESET << 16;
 
-       if (ent->flags & ESP_CMD_FLAG_AUTOSENSE) {
-               esp->ops->unmap_single(esp, ent->sense_dma,
-                                      SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE);
-               ent->sense_ptr = NULL;
-       }
+       if (ent->flags & ESP_CMD_FLAG_AUTOSENSE)
+               esp_unmap_sense(esp, ent);
 
        cmd->scsi_done(cmd);
        list_del(&ent->list);
@@ -2382,7 +2374,7 @@ static const char *esp_chip_names[] = {
 
 static struct scsi_transport_template *esp_transport_template;
 
-int scsi_esp_register(struct esp *esp, struct device *dev)
+int scsi_esp_register(struct esp *esp)
 {
        static int instance;
        int err;
@@ -2402,10 +2394,10 @@ int scsi_esp_register(struct esp *esp, struct device *dev)
 
        esp_bootup_reset(esp);
 
-       dev_printk(KERN_INFO, dev, "esp%u: regs[%1p:%1p] irq[%u]\n",
+       dev_printk(KERN_INFO, esp->dev, "esp%u: regs[%1p:%1p] irq[%u]\n",
                   esp->host->unique_id, esp->regs, esp->dma_regs,
                   esp->host->irq);
-       dev_printk(KERN_INFO, dev,
+       dev_printk(KERN_INFO, esp->dev,
                   "esp%u: is a %s, %u MHz (ccf=%u), SCSI ID %u\n",
                   esp->host->unique_id, esp_chip_names[esp->rev],
                   esp->cfreq / 1000000, esp->cfact, esp->scsi_id);
@@ -2413,7 +2405,7 @@ int scsi_esp_register(struct esp *esp, struct device *dev)
        /* Let the SCSI bus reset settle. */
        ssleep(esp_bus_reset_settle);
 
-       err = scsi_add_host(esp->host, dev);
+       err = scsi_add_host(esp->host, esp->dev);
        if (err)
                return err;
 
@@ -2790,3 +2782,131 @@ MODULE_PARM_DESC(esp_debug,
 
 module_init(esp_init);
 module_exit(esp_exit);
+
+#ifdef CONFIG_SCSI_ESP_PIO
+static inline unsigned int esp_wait_for_fifo(struct esp *esp)
+{
+       int i = 500000;
+
+       do {
+               unsigned int fbytes = esp_read8(ESP_FFLAGS) & ESP_FF_FBYTES;
+
+               if (fbytes)
+                       return fbytes;
+
+               udelay(1);
+       } while (--i);
+
+       shost_printk(KERN_ERR, esp->host, "FIFO is empty. sreg [%02x]\n",
+                    esp_read8(ESP_STATUS));
+       return 0;
+}
+
+static inline int esp_wait_for_intr(struct esp *esp)
+{
+       int i = 500000;
+
+       do {
+               esp->sreg = esp_read8(ESP_STATUS);
+               if (esp->sreg & ESP_STAT_INTR)
+                       return 0;
+
+               udelay(1);
+       } while (--i);
+
+       shost_printk(KERN_ERR, esp->host, "IRQ timeout. sreg [%02x]\n",
+                    esp->sreg);
+       return 1;
+}
+
+#define ESP_FIFO_SIZE 16
+
+void esp_send_pio_cmd(struct esp *esp, u32 addr, u32 esp_count,
+                     u32 dma_count, int write, u8 cmd)
+{
+       u8 phase = esp->sreg & ESP_STAT_PMASK;
+
+       cmd &= ~ESP_CMD_DMA;
+       esp->send_cmd_error = 0;
+
+       if (write) {
+               u8 *dst = (u8 *)addr;
+               u8 mask = ~(phase == ESP_MIP ? ESP_INTR_FDONE : ESP_INTR_BSERV);
+
+               scsi_esp_cmd(esp, cmd);
+
+               while (1) {
+                       if (!esp_wait_for_fifo(esp))
+                               break;
+
+                       *dst++ = readb(esp->fifo_reg);
+                       --esp_count;
+
+                       if (!esp_count)
+                               break;
+
+                       if (esp_wait_for_intr(esp)) {
+                               esp->send_cmd_error = 1;
+                               break;
+                       }
+
+                       if ((esp->sreg & ESP_STAT_PMASK) != phase)
+                               break;
+
+                       esp->ireg = esp_read8(ESP_INTRPT);
+                       if (esp->ireg & mask) {
+                               esp->send_cmd_error = 1;
+                               break;
+                       }
+
+                       if (phase == ESP_MIP)
+                               esp_write8(ESP_CMD_MOK, ESP_CMD);
+
+                       esp_write8(ESP_CMD_TI, ESP_CMD);
+               }
+       } else {
+               unsigned int n = ESP_FIFO_SIZE;
+               u8 *src = (u8 *)addr;
+
+               scsi_esp_cmd(esp, ESP_CMD_FLUSH);
+
+               if (n > esp_count)
+                       n = esp_count;
+               writesb(esp->fifo_reg, src, n);
+               src += n;
+               esp_count -= n;
+
+               scsi_esp_cmd(esp, cmd);
+
+               while (esp_count) {
+                       if (esp_wait_for_intr(esp)) {
+                               esp->send_cmd_error = 1;
+                               break;
+                       }
+
+                       if ((esp->sreg & ESP_STAT_PMASK) != phase)
+                               break;
+
+                       esp->ireg = esp_read8(ESP_INTRPT);
+                       if (esp->ireg & ~ESP_INTR_BSERV) {
+                               esp->send_cmd_error = 1;
+                               break;
+                       }
+
+                       n = ESP_FIFO_SIZE -
+                           (esp_read8(ESP_FFLAGS) & ESP_FF_FBYTES);
+
+                       if (n > esp_count)
+                               n = esp_count;
+                       writesb(esp->fifo_reg, src, n);
+                       src += n;
+                       esp_count -= n;
+
+                       esp_write8(ESP_CMD_TI, ESP_CMD);
+               }
+       }
+
+       esp->send_cmd_residual = esp_count;
+}
+EXPORT_SYMBOL(esp_send_pio_cmd);
+#endif