/*
|
/*
|
* hosts.c Copyright (C) 1992 Drew Eckhardt
|
* hosts.c Copyright (C) 1992 Drew Eckhardt
|
* Copyright (C) 1993, 1994, 1995 Eric Youngdale
|
* Copyright (C) 1993, 1994, 1995 Eric Youngdale
|
*
|
*
|
* mid to lowlevel SCSI driver interface
|
* mid to lowlevel SCSI driver interface
|
* Initial versions: Drew Eckhardt
|
* Initial versions: Drew Eckhardt
|
* Subsequent revisions: Eric Youngdale
|
* Subsequent revisions: Eric Youngdale
|
*
|
*
|
* <drew@colorado.edu>
|
* <drew@colorado.edu>
|
*/
|
*/
|
|
|
|
|
/*
|
/*
|
* This file contains the medium level SCSI
|
* This file contains the medium level SCSI
|
* host interface initialization, as well as the scsi_hosts array of SCSI
|
* host interface initialization, as well as the scsi_hosts array of SCSI
|
* hosts currently present in the system.
|
* hosts currently present in the system.
|
*/
|
*/
|
|
|
/*
|
/*
|
* Don't import our own symbols, as this would severely mess up our
|
* Don't import our own symbols, as this would severely mess up our
|
* symbol tables.
|
* symbol tables.
|
*/
|
*/
|
#define _SCSI_SYMS_VER_
|
#define _SCSI_SYMS_VER_
|
#define __NO_VERSION__
|
#define __NO_VERSION__
|
#include <linux/module.h>
|
#include <linux/module.h>
|
|
|
#include <linux/config.h>
|
#include <linux/config.h>
|
#include <linux/blk.h>
|
#include <linux/blk.h>
|
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
#include <linux/string.h>
|
#include <linux/string.h>
|
#include <linux/mm.h>
|
#include <linux/mm.h>
|
#include <linux/proc_fs.h>
|
#include <linux/proc_fs.h>
|
|
|
#include "scsi.h"
|
#include "scsi.h"
|
|
|
#ifndef NULL
|
#ifndef NULL
|
#define NULL 0L
|
#define NULL 0L
|
#endif
|
#endif
|
|
|
#define HOSTS_C
|
#define HOSTS_C
|
|
|
#include "hosts.h"
|
#include "hosts.h"
|
|
|
#ifdef CONFIG_SCSI_ACORNSCSI_3
|
#ifdef CONFIG_SCSI_ACORNSCSI_3
|
#include "acornscsi.h"
|
#include "acornscsi.h"
|
#endif
|
#endif
|
|
|
#ifdef CONFIG_SCSI_CUMANA_1
|
#ifdef CONFIG_SCSI_CUMANA_1
|
#include "cumana_1.h"
|
#include "cumana_1.h"
|
#endif
|
#endif
|
|
|
#ifdef CONFIG_SCSI_OAK1
|
#ifdef CONFIG_SCSI_OAK1
|
#include "oak.h"
|
#include "oak.h"
|
#endif
|
#endif
|
|
|
#ifdef CONFIG_SCSI_ECOSCSI
|
#ifdef CONFIG_SCSI_ECOSCSI
|
#include "ecoscsi.h"
|
#include "ecoscsi.h"
|
#endif
|
#endif
|
|
|
#ifdef CONFIG_SCSI_POWERTECSCSI
|
#ifdef CONFIG_SCSI_POWERTECSCSI
|
#include "powertec.h"
|
#include "powertec.h"
|
#endif
|
#endif
|
|
|
#ifdef CONFIG_SCSI_EESOXSCSI
|
#ifdef CONFIG_SCSI_EESOXSCSI
|
#include "eesox.h"
|
#include "eesox.h"
|
#endif
|
#endif
|
|
|
#ifdef CONFIG_BLK_DEV_IDESCSI
|
#ifdef CONFIG_BLK_DEV_IDESCSI
|
#include "ide-scsi.h"
|
#include "ide-scsi.h"
|
#endif
|
#endif
|
|
|
#ifdef CONFIG_SCSI_DEBUG
|
#ifdef CONFIG_SCSI_DEBUG
|
#include "scsi_debug.h"
|
#include "scsi_debug.h"
|
#endif
|
#endif
|
|
|
/*
|
/*
|
static const char RCSid[] = "$Header: /home/marcus/revision_ctrl_test/oc_cvs/cvs/or1k/rc203soc/sw/uClinux/arch/armnommu/drivers/scsi/hosts.c,v 1.1 2005-12-20 09:33:29 jcastillo Exp $";
|
static const char RCSid[] = "$Header: /home/marcus/revision_ctrl_test/oc_cvs/cvs/or1k/rc203soc/sw/uClinux/arch/armnommu/drivers/scsi/hosts.c,v 1.1 2005-12-20 09:33:29 jcastillo Exp $";
|
*/
|
*/
|
|
|
/*
|
/*
|
* The scsi host entries should be in the order you wish the
|
* The scsi host entries should be in the order you wish the
|
* cards to be detected. A driver may appear more than once IFF
|
* cards to be detected. A driver may appear more than once IFF
|
* it can deal with being detected (and therefore initialized)
|
* it can deal with being detected (and therefore initialized)
|
* with more than one simultaneous host number, can handle being
|
* with more than one simultaneous host number, can handle being
|
* reentrant, etc.
|
* reentrant, etc.
|
*
|
*
|
* They may appear in any order, as each SCSI host is told which host
|
* They may appear in any order, as each SCSI host is told which host
|
* number it is during detection.
|
* number it is during detection.
|
*/
|
*/
|
|
|
/* This is a placeholder for controllers that are not configured into
|
/* This is a placeholder for controllers that are not configured into
|
* the system - we do this to ensure that the controller numbering is
|
* the system - we do this to ensure that the controller numbering is
|
* always consistent, no matter how the kernel is configured. */
|
* always consistent, no matter how the kernel is configured. */
|
|
|
#define NO_CONTROLLER {NULL, NULL, NULL, NULL, NULL, NULL, NULL, \
|
#define NO_CONTROLLER {NULL, NULL, NULL, NULL, NULL, NULL, NULL, \
|
NULL, NULL, 0, 0, 0, 0, 0, 0}
|
NULL, NULL, 0, 0, 0, 0, 0, 0}
|
|
|
/*
|
/*
|
* When figure is run, we don't want to link to any object code. Since
|
* When figure is run, we don't want to link to any object code. Since
|
* the macro for each host will contain function pointers, we cannot
|
* the macro for each host will contain function pointers, we cannot
|
* use it and instead must use a "blank" that does no such
|
* use it and instead must use a "blank" that does no such
|
* idiocy.
|
* idiocy.
|
*/
|
*/
|
|
|
Scsi_Host_Template * scsi_hosts = NULL;
|
Scsi_Host_Template * scsi_hosts = NULL;
|
|
|
static Scsi_Host_Template builtin_scsi_hosts[] =
|
static Scsi_Host_Template builtin_scsi_hosts[] =
|
{
|
{
|
#ifdef CONFIG_SCSI_ACORNSCSI_3
|
#ifdef CONFIG_SCSI_ACORNSCSI_3
|
ACORNSCSI_3,
|
ACORNSCSI_3,
|
#endif
|
#endif
|
#ifdef CONFIG_SCSI_CUMANA_1
|
#ifdef CONFIG_SCSI_CUMANA_1
|
CUMANA_NCR5380,
|
CUMANA_NCR5380,
|
#endif
|
#endif
|
#ifdef CONFIG_SCSI_OAK1
|
#ifdef CONFIG_SCSI_OAK1
|
OAK_NCR5380,
|
OAK_NCR5380,
|
#endif
|
#endif
|
#ifdef CONFIG_SCSI_ECOSCSI
|
#ifdef CONFIG_SCSI_ECOSCSI
|
ECOSCSI_NCR5380,
|
ECOSCSI_NCR5380,
|
#endif
|
#endif
|
#ifdef CONFIG_BLK_DEV_IDESCSI
|
#ifdef CONFIG_BLK_DEV_IDESCSI
|
IDESCSI,
|
IDESCSI,
|
#endif
|
#endif
|
#ifdef CONFIG_SCSI_POWERTECSCSI
|
#ifdef CONFIG_SCSI_POWERTECSCSI
|
POWERTECSCSI,
|
POWERTECSCSI,
|
#endif
|
#endif
|
#ifdef CONFIG_SCSI_EESOXSCSI
|
#ifdef CONFIG_SCSI_EESOXSCSI
|
EESOXSCSI,
|
EESOXSCSI,
|
#endif
|
#endif
|
#ifdef CONFIG_SCSI_DEBUG
|
#ifdef CONFIG_SCSI_DEBUG
|
SCSI_DEBUG,
|
SCSI_DEBUG,
|
#endif
|
#endif
|
};
|
};
|
|
|
#define MAX_SCSI_HOSTS (sizeof(builtin_scsi_hosts) / sizeof(Scsi_Host_Template))
|
#define MAX_SCSI_HOSTS (sizeof(builtin_scsi_hosts) / sizeof(Scsi_Host_Template))
|
|
|
|
|
/*
|
/*
|
* Our semaphores and timeout counters, where size depends on
|
* Our semaphores and timeout counters, where size depends on
|
* MAX_SCSI_HOSTS here.
|
* MAX_SCSI_HOSTS here.
|
*/
|
*/
|
|
|
struct Scsi_Host * scsi_hostlist = NULL;
|
struct Scsi_Host * scsi_hostlist = NULL;
|
struct Scsi_Device_Template * scsi_devicelist = NULL;
|
struct Scsi_Device_Template * scsi_devicelist = NULL;
|
|
|
int max_scsi_hosts = 0;
|
int max_scsi_hosts = 0;
|
int next_scsi_host = 0;
|
int next_scsi_host = 0;
|
|
|
void
|
void
|
scsi_unregister(struct Scsi_Host * sh){
|
scsi_unregister(struct Scsi_Host * sh){
|
struct Scsi_Host * shpnt;
|
struct Scsi_Host * shpnt;
|
|
|
if(scsi_hostlist == sh)
|
if(scsi_hostlist == sh)
|
scsi_hostlist = sh->next;
|
scsi_hostlist = sh->next;
|
else {
|
else {
|
shpnt = scsi_hostlist;
|
shpnt = scsi_hostlist;
|
while(shpnt->next != sh) shpnt = shpnt->next;
|
while(shpnt->next != sh) shpnt = shpnt->next;
|
shpnt->next = shpnt->next->next;
|
shpnt->next = shpnt->next->next;
|
}
|
}
|
|
|
/* If we are removing the last host registered, it is safe to reuse
|
/* If we are removing the last host registered, it is safe to reuse
|
* its host number (this avoids "holes" at boot time) (DB)
|
* its host number (this avoids "holes" at boot time) (DB)
|
*/
|
*/
|
if (max_scsi_hosts == next_scsi_host)
|
if (max_scsi_hosts == next_scsi_host)
|
max_scsi_hosts--;
|
max_scsi_hosts--;
|
|
|
next_scsi_host--;
|
next_scsi_host--;
|
scsi_init_free((char *) sh, sizeof(struct Scsi_Host) + sh->extra_bytes);
|
scsi_init_free((char *) sh, sizeof(struct Scsi_Host) + sh->extra_bytes);
|
}
|
}
|
|
|
/* We call this when we come across a new host adapter. We only do this
|
/* We call this when we come across a new host adapter. We only do this
|
* once we are 100% sure that we want to use this host adapter - it is a
|
* once we are 100% sure that we want to use this host adapter - it is a
|
* pain to reverse this, so we try to avoid it
|
* pain to reverse this, so we try to avoid it
|
*/
|
*/
|
|
|
struct Scsi_Host * scsi_register(Scsi_Host_Template * tpnt, int j){
|
struct Scsi_Host * scsi_register(Scsi_Host_Template * tpnt, int j){
|
struct Scsi_Host * retval, *shpnt;
|
struct Scsi_Host * retval, *shpnt;
|
retval = (struct Scsi_Host *)scsi_init_malloc(sizeof(struct Scsi_Host) + j,
|
retval = (struct Scsi_Host *)scsi_init_malloc(sizeof(struct Scsi_Host) + j,
|
(tpnt->unchecked_isa_dma && j ? GFP_DMA : 0) | GFP_ATOMIC);
|
(tpnt->unchecked_isa_dma && j ? GFP_DMA : 0) | GFP_ATOMIC);
|
retval->host_busy = 0;
|
retval->host_busy = 0;
|
retval->block = NULL;
|
retval->block = NULL;
|
retval->wish_block = 0;
|
retval->wish_block = 0;
|
if(j > 0xffff) panic("Too many extra bytes requested\n");
|
if(j > 0xffff) panic("Too many extra bytes requested\n");
|
retval->extra_bytes = j;
|
retval->extra_bytes = j;
|
retval->loaded_as_module = scsi_loadable_module_flag;
|
retval->loaded_as_module = scsi_loadable_module_flag;
|
retval->host_no = max_scsi_hosts++; /* never reuse host_no (DB) */
|
retval->host_no = max_scsi_hosts++; /* never reuse host_no (DB) */
|
next_scsi_host++;
|
next_scsi_host++;
|
retval->host_queue = NULL;
|
retval->host_queue = NULL;
|
retval->host_wait = NULL;
|
retval->host_wait = NULL;
|
retval->last_reset = 0;
|
retval->last_reset = 0;
|
retval->irq = 0;
|
retval->irq = 0;
|
retval->dma_channel = 0xff;
|
retval->dma_channel = 0xff;
|
|
|
/* These three are default values which can be overridden */
|
/* These three are default values which can be overridden */
|
retval->max_channel = 0;
|
retval->max_channel = 0;
|
retval->max_id = 8;
|
retval->max_id = 8;
|
retval->max_lun = 8;
|
retval->max_lun = 8;
|
|
|
retval->unique_id = 0;
|
retval->unique_id = 0;
|
retval->io_port = 0;
|
retval->io_port = 0;
|
retval->hostt = tpnt;
|
retval->hostt = tpnt;
|
retval->next = NULL;
|
retval->next = NULL;
|
#ifdef DEBUG
|
#ifdef DEBUG
|
printk("Register %x %x: %d\n", (int)retval, (int)retval->hostt, j);
|
printk("Register %x %x: %d\n", (int)retval, (int)retval->hostt, j);
|
#endif
|
#endif
|
|
|
/* The next six are the default values which can be overridden
|
/* The next six are the default values which can be overridden
|
* if need be */
|
* if need be */
|
retval->this_id = tpnt->this_id;
|
retval->this_id = tpnt->this_id;
|
retval->can_queue = tpnt->can_queue;
|
retval->can_queue = tpnt->can_queue;
|
retval->sg_tablesize = tpnt->sg_tablesize;
|
retval->sg_tablesize = tpnt->sg_tablesize;
|
retval->cmd_per_lun = tpnt->cmd_per_lun;
|
retval->cmd_per_lun = tpnt->cmd_per_lun;
|
retval->unchecked_isa_dma = tpnt->unchecked_isa_dma;
|
retval->unchecked_isa_dma = tpnt->unchecked_isa_dma;
|
retval->use_clustering = tpnt->use_clustering;
|
retval->use_clustering = tpnt->use_clustering;
|
|
|
retval->select_queue_depths = NULL;
|
retval->select_queue_depths = NULL;
|
|
|
if(!scsi_hostlist)
|
if(!scsi_hostlist)
|
scsi_hostlist = retval;
|
scsi_hostlist = retval;
|
else
|
else
|
{
|
{
|
shpnt = scsi_hostlist;
|
shpnt = scsi_hostlist;
|
while(shpnt->next) shpnt = shpnt->next;
|
while(shpnt->next) shpnt = shpnt->next;
|
shpnt->next = retval;
|
shpnt->next = retval;
|
}
|
}
|
|
|
return retval;
|
return retval;
|
}
|
}
|
|
|
int
|
int
|
scsi_register_device(struct Scsi_Device_Template * sdpnt)
|
scsi_register_device(struct Scsi_Device_Template * sdpnt)
|
{
|
{
|
if(sdpnt->next) panic("Device already registered");
|
if(sdpnt->next) panic("Device already registered");
|
sdpnt->next = scsi_devicelist;
|
sdpnt->next = scsi_devicelist;
|
scsi_devicelist = sdpnt;
|
scsi_devicelist = sdpnt;
|
return 0;
|
return 0;
|
}
|
}
|
|
|
unsigned int scsi_init()
|
unsigned int scsi_init()
|
{
|
{
|
static int called = 0;
|
static int called = 0;
|
int i, pcount;
|
int i, pcount;
|
Scsi_Host_Template * tpnt;
|
Scsi_Host_Template * tpnt;
|
struct Scsi_Host * shpnt;
|
struct Scsi_Host * shpnt;
|
const char * name;
|
const char * name;
|
|
|
if(called) return 0;
|
if(called) return 0;
|
|
|
called = 1;
|
called = 1;
|
for (tpnt = &builtin_scsi_hosts[0], i = 0; i < MAX_SCSI_HOSTS; ++i, tpnt++)
|
for (tpnt = &builtin_scsi_hosts[0], i = 0; i < MAX_SCSI_HOSTS; ++i, tpnt++)
|
{
|
{
|
/*
|
/*
|
* Initialize our semaphores. -1 is interpreted to mean
|
* Initialize our semaphores. -1 is interpreted to mean
|
* "inactive" - where as 0 will indicate a time out condition.
|
* "inactive" - where as 0 will indicate a time out condition.
|
*/
|
*/
|
|
|
pcount = next_scsi_host;
|
pcount = next_scsi_host;
|
if ((tpnt->detect) &&
|
if ((tpnt->detect) &&
|
(tpnt->present =
|
(tpnt->present =
|
tpnt->detect(tpnt)))
|
tpnt->detect(tpnt)))
|
{
|
{
|
/* The only time this should come up is when people use
|
/* The only time this should come up is when people use
|
* some kind of patched driver of some kind or another. */
|
* some kind of patched driver of some kind or another. */
|
if(pcount == next_scsi_host) {
|
if(pcount == next_scsi_host) {
|
if(tpnt->present > 1)
|
if(tpnt->present > 1)
|
panic("Failure to register low-level scsi driver");
|
panic("Failure to register low-level scsi driver");
|
/* The low-level driver failed to register a driver. We
|
/* The low-level driver failed to register a driver. We
|
* can do this now. */
|
* can do this now. */
|
scsi_register(tpnt,0);
|
scsi_register(tpnt,0);
|
}
|
}
|
tpnt->next = scsi_hosts;
|
tpnt->next = scsi_hosts;
|
scsi_hosts = tpnt;
|
scsi_hosts = tpnt;
|
|
|
/* Add the driver to /proc/scsi */
|
/* Add the driver to /proc/scsi */
|
#if CONFIG_PROC_FS
|
#if CONFIG_PROC_FS
|
build_proc_dir_entries(tpnt);
|
build_proc_dir_entries(tpnt);
|
#endif
|
#endif
|
}
|
}
|
}
|
}
|
|
|
for(shpnt=scsi_hostlist; shpnt; shpnt = shpnt->next)
|
for(shpnt=scsi_hostlist; shpnt; shpnt = shpnt->next)
|
{
|
{
|
if(shpnt->hostt->info)
|
if(shpnt->hostt->info)
|
name = shpnt->hostt->info(shpnt);
|
name = shpnt->hostt->info(shpnt);
|
else
|
else
|
name = shpnt->hostt->name;
|
name = shpnt->hostt->name;
|
printk ("scsi%d : %s\n", /* And print a little message */
|
printk ("scsi%d : %s\n", /* And print a little message */
|
shpnt->host_no, name);
|
shpnt->host_no, name);
|
}
|
}
|
|
|
printk ("scsi : %d host%s.\n", next_scsi_host,
|
printk ("scsi : %d host%s.\n", next_scsi_host,
|
(next_scsi_host == 1) ? "" : "s");
|
(next_scsi_host == 1) ? "" : "s");
|
|
|
scsi_make_blocked_list();
|
scsi_make_blocked_list();
|
|
|
/* Now attach the high level drivers */
|
/* Now attach the high level drivers */
|
#ifdef CONFIG_BLK_DEV_SD
|
#ifdef CONFIG_BLK_DEV_SD
|
scsi_register_device(&sd_template);
|
scsi_register_device(&sd_template);
|
#endif
|
#endif
|
#ifdef CONFIG_BLK_DEV_SR
|
#ifdef CONFIG_BLK_DEV_SR
|
scsi_register_device(&sr_template);
|
scsi_register_device(&sr_template);
|
#endif
|
#endif
|
#ifdef CONFIG_CHR_DEV_ST
|
#ifdef CONFIG_CHR_DEV_ST
|
scsi_register_device(&st_template);
|
scsi_register_device(&st_template);
|
#endif
|
#endif
|
#ifdef CONFIG_CHR_DEV_SG
|
#ifdef CONFIG_CHR_DEV_SG
|
scsi_register_device(&sg_template);
|
scsi_register_device(&sg_template);
|
#endif
|
#endif
|
|
|
#if 0
|
#if 0
|
max_scsi_hosts = next_scsi_host;
|
max_scsi_hosts = next_scsi_host;
|
#endif
|
#endif
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/*
|
/*
|
* Overrides for Emacs so that we follow Linus's tabbing style.
|
* Overrides for Emacs so that we follow Linus's tabbing style.
|
* Emacs will notice this stuff at the end of the file and automatically
|
* Emacs will notice this stuff at the end of the file and automatically
|
* adjust the settings for this buffer only. This must remain at the end
|
* adjust the settings for this buffer only. This must remain at the end
|
* of the file.
|
* of the file.
|
* ---------------------------------------------------------------------------
|
* ---------------------------------------------------------------------------
|
* Local variables:
|
* Local variables:
|
* c-indent-level: 4
|
* c-indent-level: 4
|
* c-brace-imaginary-offset: 0
|
* c-brace-imaginary-offset: 0
|
* c-brace-offset: -4
|
* c-brace-offset: -4
|
* c-argdecl-indent: 4
|
* c-argdecl-indent: 4
|
* c-label-offset: -4
|
* c-label-offset: -4
|
* c-continued-statement-offset: 4
|
* c-continued-statement-offset: 4
|
* c-continued-brace-offset: 0
|
* c-continued-brace-offset: 0
|
* indent-tabs-mode: nil
|
* indent-tabs-mode: nil
|
* tab-width: 8
|
* tab-width: 8
|
* End:
|
* End:
|
*/
|
*/
|
|
|