linux/drivers/scsi/esp_scsi.c

// SPDX-License-Identifier: GPL-2.0-only
/* esp_scsi.c: ESP SCSI driver.
 *
 * Copyright (C) 2007 David S. Miller ([email protected])
 */

#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
#define PFX
#define DRV_VERSION
#define DRV_MODULE_RELDATE

/* SCSI bus reset settle time in seconds.  */
static int esp_bus_reset_settle =;

static u32 esp_debug;
#define ESP_DEBUG_INTR
#define ESP_DEBUG_SCSICMD
#define ESP_DEBUG_RESET
#define ESP_DEBUG_MSGIN
#define ESP_DEBUG_MSGOUT
#define ESP_DEBUG_CMDDONE
#define ESP_DEBUG_DISCONNECT
#define ESP_DEBUG_DATASTART
#define ESP_DEBUG_DATADONE
#define ESP_DEBUG_RECONNECT
#define ESP_DEBUG_AUTOSENSE
#define ESP_DEBUG_EVENT
#define ESP_DEBUG_COMMAND

#define esp_log_intr(f, a...)

#define esp_log_reset(f, a...)

#define esp_log_msgin(f, a...)

#define esp_log_msgout(f, a...)

#define esp_log_cmddone(f, a...)

#define esp_log_disconnect(f, a...)

#define esp_log_datastart(f, a...)

#define esp_log_datadone(f, a...)

#define esp_log_reconnect(f, a...)

#define esp_log_autosense(f, a...)

#define esp_log_event(f, a...)

#define esp_log_command(f, a...)

#define esp_read8(REG)
#define esp_write8(VAL,REG)

static void esp_log_fill_regs(struct esp *esp,
			      struct esp_event_ent *p)
{}

void scsi_esp_cmd(struct esp *esp, u8 val)
{}
EXPORT_SYMBOL();

static void esp_send_dma_cmd(struct esp *esp, int len, int max_len, int cmd)
{}

static void esp_event(struct esp *esp, u8 val)
{}

static void esp_dump_cmd_log(struct esp *esp)
{}

static void esp_flush_fifo(struct esp *esp)
{}

static void hme_read_fifo(struct esp *esp)
{}

static void esp_set_all_config3(struct esp *esp, u8 val)
{}

/* Reset the ESP chip, _not_ the SCSI bus. */
static void esp_reset_esp(struct esp *esp)
{}

static void esp_map_dma(struct esp *esp, struct scsi_cmnd *cmd)
{}

static dma_addr_t esp_cur_dma_addr(struct esp_cmd_entry *ent,
				   struct scsi_cmnd *cmd)
{}

static unsigned int esp_cur_dma_len(struct esp_cmd_entry *ent,
				    struct scsi_cmnd *cmd)
{}

static void esp_advance_dma(struct esp *esp, struct esp_cmd_entry *ent,
			    struct scsi_cmnd *cmd, unsigned int len)
{}

static void esp_unmap_dma(struct esp *esp, struct scsi_cmnd *cmd)
{}

static void esp_save_pointers(struct esp *esp, struct esp_cmd_entry *ent)
{}

static void esp_restore_pointers(struct esp *esp, struct esp_cmd_entry *ent)
{}

static void esp_write_tgt_config3(struct esp *esp, int tgt)
{}

static void esp_write_tgt_sync(struct esp *esp, int tgt)
{}

static u32 esp_dma_length_limit(struct esp *esp, u32 dma_addr, u32 dma_len)
{}

static int esp_need_to_nego_wide(struct esp_target_data *tp)
{}

static int esp_need_to_nego_sync(struct esp_target_data *tp)
{}

static int esp_alloc_lun_tag(struct esp_cmd_entry *ent,
			     struct esp_lun_data *lp)
{}

static void esp_free_lun_tag(struct esp_cmd_entry *ent,
			     struct esp_lun_data *lp)
{}

static void esp_map_sense(struct esp *esp, struct esp_cmd_entry *ent)
{}

static void esp_unmap_sense(struct esp *esp, struct esp_cmd_entry *ent)
{}

/* When a contingent allegiance condition 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)
{}

static struct esp_cmd_entry *find_and_prep_issuable_command(struct esp *esp)
{}

static void esp_maybe_execute_command(struct esp *esp)
{}

static struct esp_cmd_entry *esp_get_ent(struct esp *esp)
{}

static void esp_put_ent(struct esp *esp, struct esp_cmd_entry *ent)
{}

static void esp_cmd_is_done(struct esp *esp, struct esp_cmd_entry *ent,
			    struct scsi_cmnd *cmd, unsigned char host_byte)
{}

static void esp_event_queue_full(struct esp *esp, struct esp_cmd_entry *ent)
{}

static int esp_queuecommand_lck(struct scsi_cmnd *cmd)
{}

static DEF_SCSI_QCMD(esp_queuecommand)

static int esp_check_gross_error(struct esp *esp)
{}

static int esp_check_spur_intr(struct esp *esp)
{}

static void esp_schedule_reset(struct esp *esp)
{}

/* In order to avoid having to add a special half-reconnected state
 * into the driver we just sit here and poll through the rest of
 * the reselection process to get the tag message bytes.
 */
static struct esp_cmd_entry *esp_reconnect_with_tag(struct esp *esp,
						    struct esp_lun_data *lp)
{}

static int esp_reconnect(struct esp *esp)
{}

static int esp_finish_select(struct esp *esp)
{}

static int esp_data_bytes_sent(struct esp *esp, struct esp_cmd_entry *ent,
			       struct scsi_cmnd *cmd)
{}

static void esp_setsync(struct esp *esp, struct esp_target_data *tp,
			u8 scsi_period, u8 scsi_offset,
			u8 esp_stp, u8 esp_soff)
{}

static void esp_msgin_reject(struct esp *esp)
{}

static void esp_msgin_sdtr(struct esp *esp, struct esp_target_data *tp)
{}

static void esp_msgin_wdtr(struct esp *esp, struct esp_target_data *tp)
{}

static void esp_msgin_extended(struct esp *esp)
{}

/* Analyze msgin bytes received from target so far.  Return non-zero
 * if there are more bytes needed to complete the message.
 */
static int esp_msgin_process(struct esp *esp)
{}

static int esp_process_event(struct esp *esp)
{}

static void esp_reset_cleanup_one(struct esp *esp, struct esp_cmd_entry *ent)
{}

static void esp_clear_hold(struct scsi_device *dev, void *data)
{}

static void esp_reset_cleanup(struct esp *esp)
{}

/* Runs under host->lock */
static void __esp_interrupt(struct esp *esp)
{}

irqreturn_t scsi_esp_intr(int irq, void *dev_id)
{}
EXPORT_SYMBOL();

static void esp_get_revision(struct esp *esp)
{}

static void esp_init_swstate(struct esp *esp)
{}

/* This places the ESP into a known state at boot time. */
static void esp_bootup_reset(struct esp *esp)
{}

static void esp_set_clock_params(struct esp *esp)
{}

static const char *esp_chip_names[] =;

static struct scsi_transport_template *esp_transport_template;

int scsi_esp_register(struct esp *esp)
{}
EXPORT_SYMBOL();

void scsi_esp_unregister(struct esp *esp)
{}
EXPORT_SYMBOL();

static int esp_target_alloc(struct scsi_target *starget)
{}

static void esp_target_destroy(struct scsi_target *starget)
{}

static int esp_slave_alloc(struct scsi_device *dev)
{}

static int esp_slave_configure(struct scsi_device *dev)
{}

static void esp_slave_destroy(struct scsi_device *dev)
{}

static int esp_eh_abort_handler(struct scsi_cmnd *cmd)
{}

static int esp_eh_bus_reset_handler(struct scsi_cmnd *cmd)
{}

/* All bets are off, reset the entire device.  */
static int esp_eh_host_reset_handler(struct scsi_cmnd *cmd)
{}

static const char *esp_info(struct Scsi_Host *host)
{}

const struct scsi_host_template scsi_esp_template =;
EXPORT_SYMBOL();

static void esp_get_signalling(struct Scsi_Host *host)
{}

static void esp_set_offset(struct scsi_target *target, int offset)
{}

static void esp_set_period(struct scsi_target *target, int period)
{}

static void esp_set_width(struct scsi_target *target, int width)
{}

static struct spi_function_template esp_transport_ops =;

static int __init esp_init(void)
{}

static void __exit esp_exit(void)
{}

MODULE_DESCRIPTION();
MODULE_AUTHOR();
MODULE_LICENSE();
MODULE_VERSION();

module_param(esp_bus_reset_settle, int, 0);
MODULE_PARM_DESC();

module_param(esp_debug, int, 0);
MODULE_PARM_DESC();

module_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

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