2005-04-17 02:20:36 +04:00
/*
* Generic Macintosh NCR5380 driver
*
* Copyright 1998 , Michael Schmitz < mschmitz @ lbl . gov >
*
* derived in part from :
*/
/*
* Generic Generic NCR5380 driver
*
* Copyright 1995 , Russell King
*/
# include <linux/types.h>
# include <linux/delay.h>
# include <linux/module.h>
# include <linux/ioport.h>
# include <linux/init.h>
# include <linux/blkdev.h>
# include <linux/interrupt.h>
2014-11-12 08:12:07 +03:00
# include <linux/platform_device.h>
2005-04-17 02:20:36 +04:00
2014-11-12 08:12:07 +03:00
# include <asm/hwtest.h>
2005-04-17 02:20:36 +04:00
# include <asm/io.h>
# include <asm/macints.h>
2014-11-12 08:12:07 +03:00
# include <asm/setup.h>
2005-04-17 02:20:36 +04:00
# include <scsi/scsi_host.h>
2014-11-12 08:12:04 +03:00
/* Definitions for the core NCR5380 driver. */
2007-10-16 12:25:01 +04:00
# define PSEUDO_DMA
2014-11-12 08:12:07 +03:00
# define NCR5380_implementation_fields unsigned char *pdma_base
2014-11-12 08:12:04 +03:00
# define NCR5380_local_declare() struct Scsi_Host *_instance
# define NCR5380_setup(instance) _instance = instance
# define NCR5380_read(reg) macscsi_read(_instance, reg)
# define NCR5380_write(reg, value) macscsi_write(_instance, reg, value)
# define NCR5380_pread macscsi_pread
# define NCR5380_pwrite macscsi_pwrite
# define NCR5380_intr macscsi_intr
# define NCR5380_queue_command macscsi_queue_command
# define NCR5380_abort macscsi_abort
# define NCR5380_bus_reset macscsi_bus_reset
# define NCR5380_info macscsi_info
# define NCR5380_show_info macscsi_show_info
# define NCR5380_write_info macscsi_write_info
2005-04-17 02:20:36 +04:00
# include "NCR5380.h"
# define RESET_BOOT
static int setup_can_queue = - 1 ;
2014-11-12 08:12:05 +03:00
module_param ( setup_can_queue , int , 0 ) ;
2005-04-17 02:20:36 +04:00
static int setup_cmd_per_lun = - 1 ;
2014-11-12 08:12:05 +03:00
module_param ( setup_cmd_per_lun , int , 0 ) ;
2005-04-17 02:20:36 +04:00
static int setup_sg_tablesize = - 1 ;
2014-11-12 08:12:05 +03:00
module_param ( setup_sg_tablesize , int , 0 ) ;
2005-04-17 02:20:36 +04:00
static int setup_use_pdma = - 1 ;
2014-11-12 08:12:05 +03:00
module_param ( setup_use_pdma , int , 0 ) ;
2005-04-17 02:20:36 +04:00
static int setup_use_tagged_queuing = - 1 ;
2014-11-12 08:12:05 +03:00
module_param ( setup_use_tagged_queuing , int , 0 ) ;
2005-04-17 02:20:36 +04:00
static int setup_hostid = - 1 ;
2014-11-12 08:12:05 +03:00
module_param ( setup_hostid , int , 0 ) ;
2005-04-17 02:20:36 +04:00
/* Time (in jiffies) to wait after a reset; the SCSI standard calls for 250ms,
* we usually do 0.5 s to be on the safe side . But Toshiba CD - ROMs once more
* need ten times the standard value . . . */
# define TOSHIBA_DELAY
# ifdef TOSHIBA_DELAY
# define AFTER_RESET_DELAY (5*HZ / 2)
# else
# define AFTER_RESET_DELAY (HZ / 2)
# endif
/*
* NCR 5380 register access functions
*/
2014-11-12 08:12:07 +03:00
static inline char macscsi_read ( struct Scsi_Host * instance , int reg )
2005-04-17 02:20:36 +04:00
{
2014-11-12 08:12:07 +03:00
return in_8 ( instance - > base + ( reg < < 4 ) ) ;
2005-04-17 02:20:36 +04:00
}
2014-11-12 08:12:07 +03:00
static inline void macscsi_write ( struct Scsi_Host * instance , int reg , int value )
2005-04-17 02:20:36 +04:00
{
2014-11-12 08:12:07 +03:00
out_8 ( instance - > base + ( reg < < 4 ) , value ) ;
2005-04-17 02:20:36 +04:00
}
2014-11-12 08:12:05 +03:00
# ifndef MODULE
static int __init mac_scsi_setup ( char * str )
{
2005-04-17 02:20:36 +04:00
int ints [ 7 ] ;
2014-11-12 08:12:05 +03:00
( void ) get_options ( str , ARRAY_SIZE ( ints ) , ints ) ;
if ( ints [ 0 ] < 1 | | ints [ 0 ] > 6 ) {
pr_err ( " Usage: mac5380=<can_queue>[,<cmd_per_lun>[,<sg_tablesize>[,<hostid>[,<use_tags>[,<use_pdma>]]]]] \n " ) ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
2014-11-12 08:12:05 +03:00
if ( ints [ 0 ] > = 1 )
setup_can_queue = ints [ 1 ] ;
if ( ints [ 0 ] > = 2 )
setup_cmd_per_lun = ints [ 2 ] ;
if ( ints [ 0 ] > = 3 )
setup_sg_tablesize = ints [ 3 ] ;
if ( ints [ 0 ] > = 4 )
setup_hostid = ints [ 4 ] ;
if ( ints [ 0 ] > = 5 )
setup_use_tagged_queuing = ints [ 5 ] ;
if ( ints [ 0 ] > = 6 )
2005-04-17 02:20:36 +04:00
setup_use_pdma = ints [ 6 ] ;
return 1 ;
}
__setup ( " mac5380= " , mac_scsi_setup ) ;
2014-11-12 08:12:05 +03:00
# endif /* !MODULE */
2005-04-17 02:20:36 +04:00
# ifdef RESET_BOOT
/*
* Our ' bus reset on boot ' function
*/
static void mac_scsi_reset_boot ( struct Scsi_Host * instance )
{
unsigned long end ;
NCR5380_local_declare ( ) ;
NCR5380_setup ( instance ) ;
/*
* Do a SCSI reset to clean up the bus during initialization . No messing
* with the queues , interrupts , or locks necessary here .
*/
printk ( KERN_INFO " Macintosh SCSI: resetting the SCSI bus... " ) ;
/* get in phase */
NCR5380_write ( TARGET_COMMAND_REG ,
PHASE_SR_TO_TCR ( NCR5380_read ( STATUS_REG ) ) ) ;
/* assert RST */
NCR5380_write ( INITIATOR_COMMAND_REG , ICR_BASE | ICR_ASSERT_RST ) ;
/* The min. reset hold time is 25us, so 40us should be enough */
udelay ( 50 ) ;
/* reset RST and interrupt */
NCR5380_write ( INITIATOR_COMMAND_REG , ICR_BASE ) ;
NCR5380_read ( RESET_PARITY_INTERRUPT_REG ) ;
for ( end = jiffies + AFTER_RESET_DELAY ; time_before ( jiffies , end ) ; )
barrier ( ) ;
printk ( KERN_INFO " done \n " ) ;
}
# endif
2014-11-12 08:12:06 +03:00
# ifdef PSEUDO_DMA
2005-04-17 02:20:36 +04:00
/*
Pseudo - DMA : ( Ove Edlund )
The code attempts to catch bus errors that occur if one for example
" trips over the cable " .
XXX : Since bus errors in the PDMA routines never happen on my
computer , the bus error code is untested .
If the code works as intended , a bus error results in Pseudo - DMA
2014-10-03 05:42:17 +04:00
being disabled , meaning that the driver switches to slow handshake .
2005-04-17 02:20:36 +04:00
If bus errors are NOT extremely rare , this has to be changed .
*/
# define CP_IO_TO_MEM(s,d,len) \
__asm__ __volatile__ \
( " cmp.w #4,%2 \n " \
" bls 8f \n " \
" move.w %1,%%d0 \n " \
" neg.b %%d0 \n " \
" and.w #3,%%d0 \n " \
" sub.w %%d0,%2 \n " \
" bra 2f \n " \
" 1: move.b (%0),(%1)+ \n " \
" 2: dbf %%d0,1b \n " \
" move.w %2,%%d0 \n " \
" lsr.w #5,%%d0 \n " \
" bra 4f \n " \
" 3: move.l (%0),(%1)+ \n " \
" 31: move.l (%0),(%1)+ \n " \
" 32: move.l (%0),(%1)+ \n " \
" 33: move.l (%0),(%1)+ \n " \
" 34: move.l (%0),(%1)+ \n " \
" 35: move.l (%0),(%1)+ \n " \
" 36: move.l (%0),(%1)+ \n " \
" 37: move.l (%0),(%1)+ \n " \
" 4: dbf %%d0,3b \n " \
" move.w %2,%%d0 \n " \
" lsr.w #2,%%d0 \n " \
" and.w #7,%%d0 \n " \
" bra 6f \n " \
" 5: move.l (%0),(%1)+ \n " \
" 6: dbf %%d0,5b \n " \
" and.w #3,%2 \n " \
" bra 8f \n " \
" 7: move.b (%0),(%1)+ \n " \
" 8: dbf %2,7b \n " \
" moveq.l #0, %2 \n " \
" 9: \n " \
" .section .fixup, \" ax \" \n " \
" .even \n " \
" 90: moveq.l #1, %2 \n " \
" jra 9b \n " \
" .previous \n " \
" .section __ex_table, \" a \" \n " \
" .align 4 \n " \
" .long 1b,90b \n " \
" .long 3b,90b \n " \
" .long 31b,90b \n " \
" .long 32b,90b \n " \
" .long 33b,90b \n " \
" .long 34b,90b \n " \
" .long 35b,90b \n " \
" .long 36b,90b \n " \
" .long 37b,90b \n " \
" .long 5b,90b \n " \
" .long 7b,90b \n " \
" .previous " \
: " =a " ( s ) , " =a " ( d ) , " =d " ( len ) \
: " 0 " ( s ) , " 1 " ( d ) , " 2 " ( len ) \
: " d0 " )
2014-11-12 08:12:06 +03:00
static int macscsi_pread ( struct Scsi_Host * instance ,
unsigned char * dst , int len )
2005-04-17 02:20:36 +04:00
{
2014-11-12 08:12:07 +03:00
struct NCR5380_hostdata * hostdata = shost_priv ( instance ) ;
2014-11-12 08:12:06 +03:00
unsigned char * d ;
unsigned char * s ;
NCR5380_local_declare ( ) ;
NCR5380_setup ( instance ) ;
2014-11-12 08:12:07 +03:00
s = hostdata - > pdma_base + ( INPUT_DATA_REG < < 4 ) ;
2014-11-12 08:12:06 +03:00
d = dst ;
/* These conditions are derived from MacOS */
while ( ! ( NCR5380_read ( BUS_AND_STATUS_REG ) & BASR_DRQ ) & &
! ( NCR5380_read ( STATUS_REG ) & SR_REQ ) )
;
if ( ! ( NCR5380_read ( BUS_AND_STATUS_REG ) & BASR_DRQ ) & &
( NCR5380_read ( BUS_AND_STATUS_REG ) & BASR_PHASE_MATCH ) ) {
pr_err ( " Error in macscsi_pread \n " ) ;
return - 1 ;
}
CP_IO_TO_MEM ( s , d , len ) ;
if ( len ! = 0 ) {
pr_notice ( " Bus error in macscsi_pread \n " ) ;
return - 1 ;
}
return 0 ;
2005-04-17 02:20:36 +04:00
}
# define CP_MEM_TO_IO(s,d,len) \
__asm__ __volatile__ \
( " cmp.w #4,%2 \n " \
" bls 8f \n " \
" move.w %0,%%d0 \n " \
" neg.b %%d0 \n " \
" and.w #3,%%d0 \n " \
" sub.w %%d0,%2 \n " \
" bra 2f \n " \
" 1: move.b (%0)+,(%1) \n " \
" 2: dbf %%d0,1b \n " \
" move.w %2,%%d0 \n " \
" lsr.w #5,%%d0 \n " \
" bra 4f \n " \
" 3: move.l (%0)+,(%1) \n " \
" 31: move.l (%0)+,(%1) \n " \
" 32: move.l (%0)+,(%1) \n " \
" 33: move.l (%0)+,(%1) \n " \
" 34: move.l (%0)+,(%1) \n " \
" 35: move.l (%0)+,(%1) \n " \
" 36: move.l (%0)+,(%1) \n " \
" 37: move.l (%0)+,(%1) \n " \
" 4: dbf %%d0,3b \n " \
" move.w %2,%%d0 \n " \
" lsr.w #2,%%d0 \n " \
" and.w #7,%%d0 \n " \
" bra 6f \n " \
" 5: move.l (%0)+,(%1) \n " \
" 6: dbf %%d0,5b \n " \
" and.w #3,%2 \n " \
" bra 8f \n " \
" 7: move.b (%0)+,(%1) \n " \
" 8: dbf %2,7b \n " \
" moveq.l #0, %2 \n " \
" 9: \n " \
" .section .fixup, \" ax \" \n " \
" .even \n " \
" 90: moveq.l #1, %2 \n " \
" jra 9b \n " \
" .previous \n " \
" .section __ex_table, \" a \" \n " \
" .align 4 \n " \
" .long 1b,90b \n " \
" .long 3b,90b \n " \
" .long 31b,90b \n " \
" .long 32b,90b \n " \
" .long 33b,90b \n " \
" .long 34b,90b \n " \
" .long 35b,90b \n " \
" .long 36b,90b \n " \
" .long 37b,90b \n " \
" .long 5b,90b \n " \
" .long 7b,90b \n " \
" .previous " \
: " =a " ( s ) , " =a " ( d ) , " =d " ( len ) \
: " 0 " ( s ) , " 1 " ( d ) , " 2 " ( len ) \
: " d0 " )
2014-11-12 08:12:06 +03:00
static int macscsi_pwrite ( struct Scsi_Host * instance ,
unsigned char * src , int len )
2005-04-17 02:20:36 +04:00
{
2014-11-12 08:12:07 +03:00
struct NCR5380_hostdata * hostdata = shost_priv ( instance ) ;
2014-11-12 08:12:06 +03:00
unsigned char * s ;
unsigned char * d ;
2005-04-17 02:20:36 +04:00
2014-11-12 08:12:06 +03:00
NCR5380_local_declare ( ) ;
NCR5380_setup ( instance ) ;
s = src ;
2014-11-12 08:12:07 +03:00
d = hostdata - > pdma_base + ( OUTPUT_DATA_REG < < 4 ) ;
2014-11-12 08:12:06 +03:00
/* These conditions are derived from MacOS */
while ( ! ( NCR5380_read ( BUS_AND_STATUS_REG ) & BASR_DRQ ) & &
( ! ( NCR5380_read ( STATUS_REG ) & SR_REQ ) | |
( NCR5380_read ( BUS_AND_STATUS_REG ) & BASR_PHASE_MATCH ) ) )
;
if ( ! ( NCR5380_read ( BUS_AND_STATUS_REG ) & BASR_DRQ ) ) {
pr_err ( " Error in macscsi_pwrite \n " ) ;
return - 1 ;
}
CP_MEM_TO_IO ( s , d , len ) ;
if ( len ! = 0 ) {
pr_notice ( " Bus error in macscsi_pwrite \n " ) ;
return - 1 ;
}
return 0 ;
}
# endif
2005-04-17 02:20:36 +04:00
# include "NCR5380.c"
2014-11-12 08:12:07 +03:00
# define DRV_MODULE_NAME "mac_scsi"
# define PFX DRV_MODULE_NAME ": "
static struct scsi_host_template mac_scsi_template = {
. module = THIS_MODULE ,
. proc_name = DRV_MODULE_NAME ,
2013-03-31 09:15:54 +04:00
. show_info = macscsi_show_info ,
. write_info = macscsi_write_info ,
2005-04-17 02:20:36 +04:00
. name = " Macintosh NCR5380 SCSI " ,
. info = macscsi_info ,
. queuecommand = macscsi_queue_command ,
. eh_abort_handler = macscsi_abort ,
. eh_bus_reset_handler = macscsi_bus_reset ,
2014-11-12 08:12:00 +03:00
. can_queue = 16 ,
2005-04-17 02:20:36 +04:00
. this_id = 7 ,
. sg_tablesize = SG_ALL ,
2014-11-12 08:12:00 +03:00
. cmd_per_lun = 2 ,
2005-04-17 02:20:36 +04:00
. use_clustering = DISABLE_CLUSTERING
} ;
2014-11-12 08:12:07 +03:00
static int __init mac_scsi_probe ( struct platform_device * pdev )
{
struct Scsi_Host * instance ;
int error ;
int host_flags = 0 ;
struct resource * irq , * pio_mem , * pdma_mem = NULL ;
pio_mem = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! pio_mem )
return - ENODEV ;
# ifdef PSEUDO_DMA
pdma_mem = platform_get_resource ( pdev , IORESOURCE_MEM , 1 ) ;
# endif
irq = platform_get_resource ( pdev , IORESOURCE_IRQ , 0 ) ;
if ( ! hwreg_present ( ( unsigned char * ) pio_mem - > start +
( STATUS_REG < < 4 ) ) ) {
pr_info ( PFX " no device detected at %pap \n " , & pio_mem - > start ) ;
return - ENODEV ;
}
if ( setup_can_queue > 0 )
mac_scsi_template . can_queue = setup_can_queue ;
if ( setup_cmd_per_lun > 0 )
mac_scsi_template . cmd_per_lun = setup_cmd_per_lun ;
if ( setup_sg_tablesize > = 0 )
mac_scsi_template . sg_tablesize = setup_sg_tablesize ;
if ( setup_hostid > = 0 )
mac_scsi_template . this_id = setup_hostid & 7 ;
if ( setup_use_pdma < 0 )
setup_use_pdma = 0 ;
instance = scsi_host_alloc ( & mac_scsi_template ,
sizeof ( struct NCR5380_hostdata ) ) ;
if ( ! instance )
return - ENOMEM ;
instance - > base = pio_mem - > start ;
if ( irq )
instance - > irq = irq - > start ;
else
instance - > irq = NO_IRQ ;
if ( pdma_mem & & setup_use_pdma ) {
struct NCR5380_hostdata * hostdata = shost_priv ( instance ) ;
hostdata - > pdma_base = ( unsigned char * ) pdma_mem - > start ;
} else
host_flags | = FLAG_NO_PSEUDO_DMA ;
# ifdef RESET_BOOT
mac_scsi_reset_boot ( instance ) ;
# endif
2014-11-12 08:12:19 +03:00
# ifdef SUPPORT_TAGS
host_flags | = setup_use_tagged_queuing > 0 ? FLAG_TAGGED_QUEUING : 0 ;
# endif
2014-11-12 08:12:07 +03:00
NCR5380_init ( instance , host_flags ) ;
if ( instance - > irq ! = NO_IRQ ) {
error = request_irq ( instance - > irq , macscsi_intr , IRQF_SHARED ,
" NCR5380 " , instance ) ;
if ( error )
goto fail_irq ;
}
error = scsi_add_host ( instance , NULL ) ;
if ( error )
goto fail_host ;
platform_set_drvdata ( pdev , instance ) ;
scsi_scan_host ( instance ) ;
return 0 ;
fail_host :
if ( instance - > irq ! = NO_IRQ )
free_irq ( instance - > irq , instance ) ;
fail_irq :
NCR5380_exit ( instance ) ;
scsi_host_put ( instance ) ;
return error ;
}
static int __exit mac_scsi_remove ( struct platform_device * pdev )
{
struct Scsi_Host * instance = platform_get_drvdata ( pdev ) ;
scsi_remove_host ( instance ) ;
if ( instance - > irq ! = NO_IRQ )
free_irq ( instance - > irq , instance ) ;
NCR5380_exit ( instance ) ;
scsi_host_put ( instance ) ;
return 0 ;
}
static struct platform_driver mac_scsi_driver = {
. remove = __exit_p ( mac_scsi_remove ) ,
. driver = {
. name = DRV_MODULE_NAME ,
. owner = THIS_MODULE ,
} ,
} ;
2005-04-17 02:20:36 +04:00
2014-11-12 08:12:07 +03:00
module_platform_driver_probe ( mac_scsi_driver , mac_scsi_probe ) ;
2014-11-12 08:12:05 +03:00
2014-11-12 08:12:07 +03:00
MODULE_ALIAS ( " platform: " DRV_MODULE_NAME ) ;
2014-11-12 08:12:05 +03:00
MODULE_LICENSE ( " GPL " ) ;