cregit-Linux how code gets into the kernel

Release 4.11 drivers/scsi/esp_scsi.c

Directory: drivers/scsi
/* esp_scsi.c: ESP SCSI driver.
 *
 * Copyright (C) 2007 David S. Miller (davem@davemloft.net)
 */

#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/list.h>
#include <linux/completion.h>
#include <linux/kallsyms.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/irqreturn.h>

#include <asm/irq.h>
#include <asm/io.h>
#include <asm/dma.h>

#include <scsi/scsi.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_tcq.h>
#include <scsi/scsi_dbg.h>
#include <scsi/scsi_transport_spi.h>

#include "esp_scsi.h"


#define DRV_MODULE_NAME		"esp"

#define PFX DRV_MODULE_NAME ": "

#define DRV_VERSION		"2.000"

#define DRV_MODULE_RELDATE	"April 19, 2007"

/* SCSI bus reset settle time in seconds.  */

static int esp_bus_reset_settle = 3;


static u32 esp_debug;

#define ESP_DEBUG_INTR		0x00000001

#define ESP_DEBUG_SCSICMD	0x00000002

#define ESP_DEBUG_RESET		0x00000004

#define ESP_DEBUG_MSGIN		0x00000008

#define ESP_DEBUG_MSGOUT	0x00000010

#define ESP_DEBUG_CMDDONE	0x00000020

#define ESP_DEBUG_DISCONNECT	0x00000040

#define ESP_DEBUG_DATASTART	0x00000080

#define ESP_DEBUG_DATADONE	0x00000100

#define ESP_DEBUG_RECONNECT	0x00000200

#define ESP_DEBUG_AUTOSENSE	0x00000400

#define ESP_DEBUG_EVENT		0x00000800

#define ESP_DEBUG_COMMAND	0x00001000


#define esp_log_intr(f, a...) \
do {    if (esp_debug & ESP_DEBUG_INTR) \
                shost_printk(KERN_DEBUG, esp->host, f, ## a);   \
} while (0)


#define esp_log_reset(f, a...) \
do {    if (esp_debug & ESP_DEBUG_RESET) \
                shost_printk(KERN_DEBUG, esp->host, f, ## a);   \
} while (0)


#define esp_log_msgin(f, a...) \
do {    if (esp_debug & ESP_DEBUG_MSGIN) \
                shost_printk(KERN_DEBUG, esp->host, f, ## a);   \
} while (0)


#define esp_log_msgout(f, a...) \
do {    if (esp_debug & ESP_DEBUG_MSGOUT) \
                shost_printk(KERN_DEBUG, esp->host, f, ## a);   \
} while (0)


#define esp_log_cmddone(f, a...) \
do {    if (esp_debug & ESP_DEBUG_CMDDONE) \
                shost_printk(KERN_DEBUG, esp->host, f, ## a);   \
} while (0)


#define esp_log_disconnect(f, a...) \
do {    if (esp_debug & ESP_DEBUG_DISCONNECT) \
                shost_printk(KERN_DEBUG, esp->host, f, ## a);   \
} while (0)


#define esp_log_datastart(f, a...) \
do {    if (esp_debug & ESP_DEBUG_DATASTART) \
                shost_printk(KERN_DEBUG, esp->host, f, ## a);   \
} while (0)


#define esp_log_datadone(f, a...) \
do {    if (esp_debug & ESP_DEBUG_DATADONE) \
                shost_printk(KERN_DEBUG, esp->host, f, ## a);   \
} while (0)


#define esp_log_reconnect(f, a...) \
do {    if (esp_debug & ESP_DEBUG_RECONNECT) \
                shost_printk(KERN_DEBUG, esp->host, f, ## a);   \
} while (0)


#define esp_log_autosense(f, a...) \
do {    if (esp_debug & ESP_DEBUG_AUTOSENSE) \
                shost_printk(KERN_DEBUG, esp->host, f, ## a);   \
} while (0)


#define esp_log_event(f, a...) \
do {   if (esp_debug & ESP_DEBUG_EVENT) \
                shost_printk(KERN_DEBUG, esp->host, f, ## a);   \
} while (0)


#define esp_log_command(f, a...) \
do {   if (esp_debug & ESP_DEBUG_COMMAND)       \
                shost_printk(KERN_DEBUG, esp->host, f, ## a);   \
} while (0)


#define esp_read8(REG)		esp->ops->esp_read8(esp, REG)

#define esp_write8(VAL,REG)	esp->ops->esp_write8(esp, VAL, REG)


static void esp_log_fill_regs(struct esp *esp, struct esp_event_ent *p) { p->sreg = esp->sreg; p->seqreg = esp->seqreg; p->sreg2 = esp->sreg2; p->ireg = esp->ireg; p->select_state = esp->select_state; p->event = esp->event; }

Contributors

PersonTokensPropCommitsCommitProp
David S. Miller64100.00%1100.00%
Total64100.00%1100.00%


void scsi_esp_cmd(struct esp *esp, u8 val) { struct esp_event_ent *p; int idx = esp->esp_event_cur; p = &esp->esp_event_log[idx]; p->type = ESP_EVENT_TYPE_CMD; p->val = val; esp_log_fill_regs(esp, p); esp->esp_event_cur = (idx + 1) & (ESP_EVENT_LOG_SZ - 1); esp_log_command("cmd[%02x]\n", val); esp_write8(val, ESP_CMD); }

Contributors

PersonTokensPropCommitsCommitProp
David S. Miller7791.67%150.00%
Hannes Reinecke78.33%150.00%
Total84100.00%2100.00%

EXPORT_SYMBOL(scsi_esp_cmd);
static void esp_send_dma_cmd(struct esp *esp, int len, int max_len, int cmd) { if (esp->flags & ESP_FLAG_USE_FIFO) { int i; scsi_esp_cmd(esp, ESP_CMD_FLUSH); for (i = 0; i < len; i++) esp_write8(esp->command_block[i], ESP_FDATA); scsi_esp_cmd(esp, cmd); } else { if (esp->rev == FASHME) scsi_esp_cmd(esp, ESP_CMD_FLUSH); cmd |= ESP_CMD_DMA; esp->ops->send_dma_cmd(esp, esp->command_block_dma, len, max_len, 0, cmd); } }

Contributors

PersonTokensPropCommitsCommitProp
Hannes Reinecke115100.00%1100.00%
Total115100.00%1100.00%


static void esp_event(struct esp *esp, u8 val) { struct esp_event_ent *p; int idx = esp->esp_event_cur; p = &esp->esp_event_log[idx]; p->type = ESP_EVENT_TYPE_EVENT; p->val = val; esp_log_fill_regs(esp, p); esp->esp_event_cur = (idx + 1) & (ESP_EVENT_LOG_SZ - 1); esp->event = val; }

Contributors

PersonTokensPropCommitsCommitProp
David S. Miller77100.00%1100.00%
Total77100.00%1100.00%


static void esp_dump_cmd_log(struct esp *esp) { int idx = esp->esp_event_cur; int stop = idx; shost_printk(KERN_INFO, esp->host, "Dumping command log\n"); do { struct esp_event_ent *p = &esp->esp_event_log[idx]; shost_printk(KERN_INFO, esp->host, "ent[%d] %s val[%02x] sreg[%02x] seqreg[%02x] " "sreg2[%02x] ireg[%02x] ss[%02x] event[%02x]\n", idx, p->type == ESP_EVENT_TYPE_CMD ? "CMD" : "EVENT", p->val, p->sreg, p->seqreg, p->sreg2, p->ireg, p->select_state, p->event); idx = (idx + 1) & (ESP_EVENT_LOG_SZ - 1); } while (idx != stop); }

Contributors

PersonTokensPropCommitsCommitProp
David S. Miller9980.49%150.00%
Hannes Reinecke2419.51%150.00%
Total123100.00%2100.00%


static void esp_flush_fifo(struct esp *esp) { scsi_esp_cmd(esp, ESP_CMD_FLUSH); if (esp->rev == ESP236) { int lim = 1000; while (esp_read8(ESP_FFLAGS) & ESP_FF_FBYTES) { if (--lim == 0) { shost_printk(KERN_ALERT, esp->host, "ESP_FF_BYTES will not clear!\n"); break; } udelay(1); } } }

Contributors

PersonTokensPropCommitsCommitProp
David S. Miller6288.57%150.00%
Hannes Reinecke811.43%150.00%
Total70100.00%2100.00%


static void hme_read_fifo(struct esp *esp) { int fcnt = esp_read8(ESP_FFLAGS) & ESP_FF_FBYTES; int idx = 0; while (fcnt--) { esp->fifo[idx++] = esp_read8(ESP_FDATA); esp->fifo[idx++] = esp_read8(ESP_FDATA); } if (esp->sreg2 & ESP_STAT2_F1BYTE) { esp_write8(0, ESP_FDATA); esp->fifo[idx++] = esp_read8(ESP_FDATA); scsi_esp_cmd(esp, ESP_CMD_FLUSH); } esp->fifo_cnt = idx; }

Contributors

PersonTokensPropCommitsCommitProp
David S. Miller102100.00%1100.00%
Total102100.00%1100.00%


static void esp_set_all_config3(struct esp *esp, u8 val) { int i; for (i = 0; i < ESP_MAX_TARGET; i++) esp->target[i].esp_config3 = val; }

Contributors

PersonTokensPropCommitsCommitProp
David S. Miller41100.00%1100.00%
Total41100.00%1100.00%

/* Reset the ESP chip, _not_ the SCSI bus. */
static void esp_reset_esp(struct esp *esp) { u8 family_code, version; /* Now reset the ESP chip */ scsi_esp_cmd(esp, ESP_CMD_RC); scsi_esp_cmd(esp, ESP_CMD_NULL | ESP_CMD_DMA); if (esp->rev == FAST) esp_write8(ESP_CONFIG2_FENAB, ESP_CFG2); scsi_esp_cmd(esp, ESP_CMD_NULL | ESP_CMD_DMA); /* This is the only point at which it is reliable to read * the ID-code for a fast ESP chip variants. */ esp->max_period = ((35 * esp->ccycle) / 1000); if (esp->rev == FAST) { version = esp_read8(ESP_UID); family_code = (version & 0xf8) >> 3; if (family_code == 0x02) esp->rev = FAS236; else if (family_code == 0x0a) esp->rev = FASHME; /* Version is usually '5'. */ else esp->rev = FAS100A; esp->min_period = ((4 * esp->ccycle) / 1000); } else { esp->min_period = ((5 * esp->ccycle) / 1000); } if (esp->rev == FAS236) { /* * The AM53c974 chip returns the same ID as FAS236; * try to configure glitch eater. */ u8 config4 = ESP_CONFIG4_GE1; esp_write8(config4, ESP_CFG4); config4 = esp_read8(ESP_CFG4); if (config4 & ESP_CONFIG4_GE1) { esp->rev = PCSCSI; esp_write8(esp->config4, ESP_CFG4); } } esp->max_period = (esp->max_period + 3)>>2; esp->min_period = (esp->min_period + 3)>>2; esp_write8(esp->config1, ESP_CFG1); switch (esp->rev) { case ESP100: /* nothing to do */ break; case ESP100A: esp_write8(esp->config2, ESP_CFG2); break; case ESP236: /* Slow 236 */ esp_write8(esp->config2, ESP_CFG2); esp->prev_cfg3 = esp->target[0].esp_config3; esp_write8(esp->prev_cfg3, ESP_CFG3); break; case FASHME: esp->config2 |= (ESP_CONFIG2_HME32 | ESP_CONFIG2_HMEFENAB); /* fallthrough... */ case FAS236: case PCSCSI: /* Fast 236, AM53c974 or HME */ esp_write8(esp->config2, ESP_CFG2); if (esp->rev == FASHME) { u8 cfg3 = esp->target[0].esp_config3; cfg3 |= ESP_CONFIG3_FCLOCK | ESP_CONFIG3_OBPUSH; if (esp->scsi_id >= 8) cfg3 |= ESP_CONFIG3_IDBIT3; esp_set_all_config3(esp, cfg3); } else { u32 cfg3 = esp->target[0].esp_config3; cfg3 |= ESP_CONFIG3_FCLK; esp_set_all_config3(esp, cfg3); } esp->prev_cfg3 = esp->target[0].esp_config3; esp_write8(esp->prev_cfg3, ESP_CFG3); if (esp->rev == FASHME) { esp->radelay = 80; } else { if (esp->flags & ESP_FLAG_DIFFERENTIAL) esp->radelay = 0; else esp->radelay = 96; } break; case FAS100A: /* Fast 100a */ esp_write8(esp->config2, ESP_CFG2); esp_set_all_config3(esp, (esp->target[0].esp_config3 | ESP_CONFIG3_FCLOCK)); esp->prev_cfg3 = esp->target[0].esp_config3; esp_write8(esp->prev_cfg3, ESP_CFG3); esp->radelay = 32; break; default: break; } /* Reload the configuration registers */ esp_write8(esp->cfact, ESP_CFACT); esp->prev_stp = 0; esp_write8(esp->prev_stp, ESP_STP); esp->prev_soff = 0; esp_write8(esp->prev_soff, ESP_SOFF); esp_write8(esp->neg_defp, ESP_TIMEO); /* Eat any bitrot in the chip */ esp_read8(ESP_INTRPT); udelay(100); }

Contributors

PersonTokensPropCommitsCommitProp
David S. Miller55290.64%266.67%
Hannes Reinecke579.36%133.33%
Total609100.00%3100.00%


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; if (dir == DMA_NONE) return; spriv->u.num_sg = esp->ops->map_sg(esp, sg, scsi_sg_count(cmd), dir); 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; }

Contributors

PersonTokensPropCommitsCommitProp
David S. Miller12695.45%150.00%
FUJITA Tomonori64.55%150.00%
Total132100.00%2100.00%


static dma_addr_t esp_cur_dma_addr(struct esp_cmd_entry *ent, struct scsi_cmnd *cmd) { struct esp_cmd_priv *p = ESP_CMD_PRIV(cmd); if (ent->flags & ESP_CMD_FLAG_AUTOSENSE) { return ent->sense_dma + (ent->sense_ptr - cmd->sense_buffer); } return sg_dma_address(p->cur_sg) + (sg_dma_len(p->cur_sg) - p->cur_residue); }

Contributors

PersonTokensPropCommitsCommitProp
David S. Miller72100.00%1100.00%
Total72100.00%1100.00%


static unsigned int esp_cur_dma_len(struct esp_cmd_entry *ent, struct scsi_cmnd *cmd) { struct esp_cmd_priv *p = ESP_CMD_PRIV(cmd); if (ent->flags & ESP_CMD_FLAG_AUTOSENSE) { return SCSI_SENSE_BUFFERSIZE - (ent->sense_ptr - cmd->sense_buffer); } return p->cur_residue; }

Contributors

PersonTokensPropCommitsCommitProp
David S. Miller55100.00%1100.00%
Total55100.00%1100.00%


static void esp_advance_dma(struct esp *esp, struct esp_cmd_entry *ent, struct scsi_cmnd *cmd, unsigned int len) { struct esp_cmd_priv *p = ESP_CMD_PRIV(cmd); if (ent->flags & ESP_CMD_FLAG_AUTOSENSE) { ent->sense_ptr += len; return; } p->cur_residue -= len; p->tot_residue -= len; if (p->cur_residue < 0 || p->tot_residue < 0) { shost_printk(KERN_ERR, esp->host, "Data transfer overflow.\n"); shost_printk(KERN_ERR, esp->host, "cur_residue[%d] tot_residue[%d] len[%u]\n", p->cur_residue, p->tot_residue, len); p->cur_residue = 0; p->tot_residue = 0; } if (!p->cur_residue && p->tot_residue) { p->cur_sg++; p->cur_residue = sg_dma_len(p->cur_sg); } }

Contributors

PersonTokensPropCommitsCommitProp
David S. Miller13084.97%150.00%
Hannes Reinecke2315.03%150.00%
Total153100.00%2100.00%


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); }

Contributors

PersonTokensPropCommitsCommitProp
David S. Miller5995.16%150.00%
FUJITA Tomonori34.84%150.00%
Total62100.00%2100.00%


static void esp_save_pointers(struct esp *esp, struct esp_cmd_entry *ent) { struct scsi_cmnd *cmd = ent->cmd; struct esp_cmd_priv *spriv = ESP_CMD_PRIV(cmd); if (ent->flags & ESP_CMD_FLAG_AUTOSENSE) { ent->saved_sense_ptr = ent->sense_ptr; return; } ent->saved_cur_residue = spriv->cur_residue; ent->saved_cur_sg = spriv->cur_sg; ent->saved_tot_residue = spriv->tot_residue; }

Contributors

PersonTokensPropCommitsCommitProp
David S. Miller78100.00%1100.00%
Total78100.00%1100.00%


static void esp_restore_pointers(struct esp *esp, struct esp_cmd_entry *ent) { struct scsi_cmnd *cmd = ent->cmd; struct esp_cmd_priv *spriv = ESP_CMD_PRIV(cmd); if (ent->flags & ESP_CMD_FLAG_AUTOSENSE) { ent->sense_ptr = ent->saved_sense_ptr; return; } spriv->cur_residue = ent->saved_cur_residue; spriv->cur_sg = ent->saved_cur_sg; spriv->tot_residue = ent->saved_tot_residue; }

Contributors

PersonTokensPropCommitsCommitProp
David S. Miller78100.00%1100.00%
Total78100.00%1100.00%


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; } }

Contributors

PersonTokensPropCommitsCommitProp
David S. Miller54100.00%1100.00%
Total54100.00%1100.00%


static void esp_write_tgt_config3(struct esp *esp, int tgt) { if (esp->rev > ESP100A) { u8 val = esp->target[tgt].esp_config3; if (val != esp->prev_cfg3) { esp->prev_cfg3 = val; esp_write8(val, ESP_CFG3); } } }

Contributors

PersonTokensPropCommitsCommitProp
David S. Miller59100.00%1100.00%
Total59100.00%1100.00%


static void esp_write_tgt_sync(struct esp *esp, int tgt) { u8 off = esp->target[tgt].esp_offset; u8 per = esp->target[tgt].esp_period; if (off != esp->prev_soff) { esp->prev_soff = off; esp_write8(off, ESP_SOFF); } if (per != esp->prev_stp) { esp->prev_stp = per; esp_write8(per, ESP_STP); } }

Contributors

PersonTokensPropCommitsCommitProp
David S. Miller84100.00%1100.00%
Total84100.00%1100.00%


static u32 esp_dma_length_limit(struct esp *esp, u32 dma_addr, u32 dma_len) { if (esp->rev == FASHME) { /* Arbitrary segment boundaries, 24-bit counts. */ if (dma_len > (1U << 24)) dma_len = (1U << 24); } else { u32 base, end; /* ESP chip limits other variants by 16-bits of transfer * count. Actually on FAS100A and FAS236 we could get * 24-bits of transfer count by enabling ESP_CONFIG2_FENAB * in the ESP_CFG2 register but that causes other unwanted * changes so we don't use it currently. */ if (dma_len > (1U << 16)) dma_len = (1U << 16); /* All of the DMA variants hooked up to these chips * cannot handle crossing a 24-bit address boundary. */ base = dma_addr & ((1U << 24) - 1U); end = base + dma_len; if (end > (1U << 24)) end = (1U <<24); dma_len = end - base; } return dma_len; }

Contributors

PersonTokensPropCommitsCommitProp
David S. Miller121100.00%1100.00%
Total121100.00%1100.00%


static int esp_need_to_nego_wide(struct esp_target_data *tp) { struct scsi_target *target = tp->starget; return spi_width(target) != tp->nego_goal_width; }

Contributors

PersonTokensPropCommitsCommitProp
David S. Miller30100.00%1100.00%
Total30100.00%1100.00%


static int esp_need_to_nego_sync(struct esp_target_data *tp) { struct scsi_target *target = tp->starget; /* When offset is zero, period is "don't care". */ if (!spi_offset(target) && !tp->nego_goal_offset) return 0; if (spi_offset(target) == tp->nego_goal_offset && spi_period(target) == tp->nego_goal_period) return 0; return 1; }

Contributors

PersonTokensPropCommitsCommitProp
David S. Miller63100.00%1100.00%
Total63100.00%1100.00%


static int esp_alloc_lun_tag(struct esp_cmd_entry *ent, struct esp_lun_data *lp) { if (!ent->orig_tag[0]) { /* Non-tagged, slot already taken? */ if (lp->non_tagged_cmd) return -EBUSY; if (lp->hold) { /* We are being held by active tagged * commands. */ if (lp->num_tagged) return -EBUSY; /* Tagged commands completed, we can unplug * the queue and run this untagged command. */ lp->hold = 0; } else if (lp->num_tagged) { /* Plug the queue until num_tagged decreases * to zero in esp_free_lun_tag. */ lp->hold = 1; return -EBUSY; } lp->non_tagged_cmd = ent; return 0; } else { /* Tagged command, see if blocked by a * non-tagged one. */ if (lp->non_tagged_cmd || lp->hold) return -EBUSY; } BUG_ON(lp->tagged_cmds[ent->orig_tag[1]]); lp->tagged_cmds[ent->orig_tag[1]] = ent; lp->num_tagged++; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
David S. Miller149100.00%2100.00%
Total149100.00%2100.00%


static void esp_free_lun_tag(struct esp_cmd_entry *ent, struct esp_lun_data *lp) { if (ent->orig_tag[0]) { BUG_ON(lp->tagged_cmds[ent->orig_tag[1]] != ent); lp->tagged_cmds[ent->orig_tag[1]] = NULL; lp->num_tagged--; } else { BUG_ON(lp->non_tagged_cmd != ent); lp->non_tagged_cmd = NULL; } }

Contributors

PersonTokensPropCommitsCommitProp
David S. Miller81100.00%2100.00%
Total81100.00%2100.00%

/* 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 * to send out the REQUEST_SENSE automatically, but this was difficult * to get right especially in the presence of applications like smartd * which use SG_IO to send out their own REQUEST_SENSE commands. */
static void esp_autosense(struct esp *esp, struct esp_cmd_entry *ent) { struct scsi_cmnd *cmd = ent->cmd; struct scsi_device *dev = cmd->device; int tgt, lun; u8 *p, val; tgt = dev->id; lun = dev->lun; 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