2019-05-19 15:08:20 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2005-04-17 02:20:36 +04:00
# include <linux/types.h>
# include <linux/mm.h>
# include <linux/ioport.h>
# include <linux/init.h>
2009-04-05 15:02:45 +04:00
# include <linux/slab.h>
2005-04-17 02:20:36 +04:00
# include <linux/spinlock.h>
# include <linux/interrupt.h>
2009-04-05 15:02:45 +04:00
# include <linux/platform_device.h>
2022-06-30 06:33:00 +03:00
# include <linux/dma-mapping.h>
2011-05-27 17:47:43 +04:00
# include <linux/module.h>
2005-04-17 02:20:36 +04:00
# include <asm/page.h>
# include <asm/amigaints.h>
# include <asm/amigahw.h>
2022-02-18 22:50:34 +03:00
# include <scsi/scsi.h>
# include <scsi/scsi_cmnd.h>
# include <scsi/scsi_device.h>
# include <scsi/scsi_eh.h>
# include <scsi/scsi_tcq.h>
2005-04-17 02:20:36 +04:00
# include "wd33c93.h"
# include "a3000.h"
2009-03-04 23:06:08 +03:00
2010-04-12 23:55:15 +04:00
struct a3000_hostdata {
struct WD33C93_hostdata wh ;
struct a3000_scsiregs * regs ;
2022-06-30 06:33:00 +03:00
struct device * dev ;
2010-04-12 23:55:15 +04:00
} ;
2022-06-30 06:33:00 +03:00
# define DMA_DIR(d) ((d == DATA_OUT_DIR) ? DMA_TO_DEVICE : DMA_FROM_DEVICE)
2009-05-17 15:35:07 +04:00
static irqreturn_t a3000_intr ( int irq , void * data )
2005-04-17 02:20:36 +04:00
{
2009-05-17 15:35:07 +04:00
struct Scsi_Host * instance = data ;
2010-04-12 23:55:15 +04:00
struct a3000_hostdata * hdata = shost_priv ( instance ) ;
unsigned int status = hdata - > regs - > ISTR ;
2005-04-17 02:20:36 +04:00
unsigned long flags ;
if ( ! ( status & ISTR_INT_P ) )
return IRQ_NONE ;
2010-04-04 13:00:35 +04:00
if ( status & ISTR_INTS ) {
2009-05-17 15:35:07 +04:00
spin_lock_irqsave ( instance - > host_lock , flags ) ;
wd33c93_intr ( instance ) ;
spin_unlock_irqrestore ( instance - > host_lock , flags ) ;
2005-04-17 02:20:36 +04:00
return IRQ_HANDLED ;
}
2019-10-18 06:18:38 +03:00
pr_warn ( " Non-serviced A3000 SCSI-interrupt? ISTR = %02x \n " , status ) ;
2005-04-17 02:20:36 +04:00
return IRQ_NONE ;
}
2006-09-13 01:49:33 +04:00
static int dma_setup ( struct scsi_cmnd * cmd , int dir_in )
2005-04-17 02:20:36 +04:00
{
2022-02-18 22:51:15 +03:00
struct scsi_pointer * scsi_pointer = WD33C93_scsi_pointer ( cmd ) ;
2022-06-30 06:33:00 +03:00
unsigned long len = scsi_pointer - > this_residual ;
2009-05-17 15:35:07 +04:00
struct Scsi_Host * instance = cmd - > device - > host ;
2010-04-12 23:55:15 +04:00
struct a3000_hostdata * hdata = shost_priv ( instance ) ;
struct WD33C93_hostdata * wh = & hdata - > wh ;
struct a3000_scsiregs * regs = hdata - > regs ;
2010-04-04 13:00:35 +04:00
unsigned short cntr = CNTR_PDMD | CNTR_INTEN ;
2022-06-30 06:33:00 +03:00
dma_addr_t addr ;
addr = dma_map_single ( hdata - > dev , scsi_pointer - > ptr ,
len , DMA_DIR ( dir_in ) ) ;
if ( dma_mapping_error ( hdata - > dev , addr ) ) {
dev_warn ( hdata - > dev , " cannot map SCSI data block %p \n " ,
scsi_pointer - > ptr ) ;
return 1 ;
}
scsi_pointer - > dma_handle = addr ;
2010-04-04 13:00:35 +04:00
/*
* if the physical address has the wrong alignment , or if
* physical address is bad , or if it is a write and at the
* end of a physical memory chunk , then allocate a bounce
* buffer
2022-06-30 06:33:00 +03:00
* MSch 20220629 - only wrong alignment tested - bounce
* buffer returned by kmalloc is guaranteed to be aligned
2010-04-04 13:00:35 +04:00
*/
if ( addr & A3000_XFER_MASK ) {
2022-06-30 06:33:00 +03:00
WARN_ONCE ( 1 , " Invalid alignment for DMA! " ) ;
/* drop useless mapping */
dma_unmap_single ( hdata - > dev , scsi_pointer - > dma_handle ,
scsi_pointer - > this_residual ,
DMA_DIR ( dir_in ) ) ;
2022-02-18 22:51:15 +03:00
wh - > dma_bounce_len = ( scsi_pointer - > this_residual + 511 ) & ~ 0x1ff ;
2010-04-12 23:55:15 +04:00
wh - > dma_bounce_buffer = kmalloc ( wh - > dma_bounce_len ,
GFP_KERNEL ) ;
2010-04-04 13:00:35 +04:00
/* can't allocate memory; use PIO */
2010-04-12 23:55:15 +04:00
if ( ! wh - > dma_bounce_buffer ) {
wh - > dma_bounce_len = 0 ;
2022-06-30 06:33:00 +03:00
scsi_pointer - > dma_handle = ( dma_addr_t ) NULL ;
2010-04-04 13:00:35 +04:00
return 1 ;
}
if ( ! dir_in ) {
/* copy to bounce buffer for a write */
2022-02-18 22:51:15 +03:00
memcpy ( wh - > dma_bounce_buffer , scsi_pointer - > ptr ,
scsi_pointer - > this_residual ) ;
2010-04-04 13:00:35 +04:00
}
2022-06-30 06:33:00 +03:00
addr = dma_map_single ( hdata - > dev , scsi_pointer - > ptr ,
len , DMA_DIR ( dir_in ) ) ;
if ( dma_mapping_error ( hdata - > dev , addr ) ) {
dev_warn ( hdata - > dev ,
" cannot map SCSI data block %p \n " ,
scsi_pointer - > ptr ) ;
return 1 ;
}
scsi_pointer - > dma_handle = addr ;
2005-04-17 02:20:36 +04:00
}
2010-04-04 13:00:35 +04:00
/* setup dma direction */
if ( ! dir_in )
cntr | = CNTR_DDIR ;
2005-04-17 02:20:36 +04:00
2010-04-04 13:00:35 +04:00
/* remember direction */
2010-04-12 23:55:15 +04:00
wh - > dma_dir = dir_in ;
2005-04-17 02:20:36 +04:00
2009-05-17 14:17:23 +04:00
regs - > CNTR = cntr ;
2005-04-17 02:20:36 +04:00
2010-04-04 13:00:35 +04:00
/* setup DMA *physical* address */
2009-05-17 14:17:23 +04:00
regs - > ACR = addr ;
2005-04-17 02:20:36 +04:00
2022-06-30 06:33:00 +03:00
/* no more cache flush here - dma_map_single() takes care */
2005-04-17 02:20:36 +04:00
2010-04-04 13:00:35 +04:00
/* start DMA */
mb ( ) ; /* make sure setup is completed */
2009-05-17 14:17:23 +04:00
regs - > ST_DMA = 1 ;
2010-04-04 13:00:35 +04:00
mb ( ) ; /* make sure DMA has started before next IO */
2005-04-17 02:20:36 +04:00
2010-04-04 13:00:35 +04:00
/* return success */
return 0 ;
2005-04-17 02:20:36 +04:00
}
2006-09-13 01:49:33 +04:00
static void dma_stop ( struct Scsi_Host * instance , struct scsi_cmnd * SCpnt ,
int status )
2005-04-17 02:20:36 +04:00
{
2022-02-18 22:51:15 +03:00
struct scsi_pointer * scsi_pointer = WD33C93_scsi_pointer ( SCpnt ) ;
2010-04-12 23:55:15 +04:00
struct a3000_hostdata * hdata = shost_priv ( instance ) ;
struct WD33C93_hostdata * wh = & hdata - > wh ;
struct a3000_scsiregs * regs = hdata - > regs ;
2010-04-04 13:00:39 +04:00
2010-04-04 13:00:35 +04:00
/* disable SCSI interrupts */
unsigned short cntr = CNTR_PDMD ;
2010-04-12 23:55:15 +04:00
if ( ! wh - > dma_dir )
2010-04-04 13:00:35 +04:00
cntr | = CNTR_DDIR ;
2009-05-17 14:17:23 +04:00
regs - > CNTR = cntr ;
2010-04-04 13:00:35 +04:00
mb ( ) ; /* make sure CNTR is updated before next IO */
/* flush if we were reading */
2010-04-12 23:55:15 +04:00
if ( wh - > dma_dir ) {
2009-05-17 14:17:23 +04:00
regs - > FLUSH = 1 ;
2010-04-04 13:00:35 +04:00
mb ( ) ; /* don't allow prefetch */
2009-05-17 14:17:23 +04:00
while ( ! ( regs - > ISTR & ISTR_FE_FLG ) )
2010-04-04 13:00:35 +04:00
barrier ( ) ;
mb ( ) ; /* no IO until FLUSH is done */
}
/* clear a possible interrupt */
/* I think that this CINT is only necessary if you are
* using the terminal count features . HM 7 Mar 1994
*/
2009-05-17 14:17:23 +04:00
regs - > CINT = 1 ;
2010-04-04 13:00:35 +04:00
/* stop DMA */
2009-05-17 14:17:23 +04:00
regs - > SP_DMA = 1 ;
2010-04-04 13:00:35 +04:00
mb ( ) ; /* make sure DMA is stopped before next IO */
/* restore the CONTROL bits (minus the direction flag) */
2009-05-17 14:17:23 +04:00
regs - > CNTR = CNTR_PDMD | CNTR_INTEN ;
2010-04-04 13:00:35 +04:00
mb ( ) ; /* make sure CNTR is updated before next IO */
2022-06-30 06:33:00 +03:00
dma_unmap_single ( hdata - > dev , scsi_pointer - > dma_handle ,
scsi_pointer - > this_residual ,
DMA_DIR ( wh - > dma_dir ) ) ;
2010-04-04 13:00:35 +04:00
/* copy from a bounce buffer, if necessary */
2010-04-12 23:55:15 +04:00
if ( status & & wh - > dma_bounce_buffer ) {
2010-04-04 13:00:35 +04:00
if ( SCpnt ) {
2010-04-12 23:55:15 +04:00
if ( wh - > dma_dir & & SCpnt )
2022-02-18 22:51:15 +03:00
memcpy ( scsi_pointer - > ptr , wh - > dma_bounce_buffer ,
scsi_pointer - > this_residual ) ;
2010-04-12 23:55:15 +04:00
kfree ( wh - > dma_bounce_buffer ) ;
wh - > dma_bounce_buffer = NULL ;
wh - > dma_bounce_len = 0 ;
2010-04-04 13:00:35 +04:00
} else {
2010-04-12 23:55:15 +04:00
kfree ( wh - > dma_bounce_buffer ) ;
wh - > dma_bounce_buffer = NULL ;
wh - > dma_bounce_len = 0 ;
2010-04-04 13:00:35 +04:00
}
2005-04-17 02:20:36 +04:00
}
}
2023-03-22 22:54:10 +03:00
static const struct scsi_host_template amiga_a3000_scsi_template = {
2009-04-05 15:02:45 +04:00
. module = THIS_MODULE ,
. name = " Amiga 3000 built-in SCSI " ,
2013-03-31 08:30:35 +04:00
. show_info = wd33c93_show_info ,
. write_info = wd33c93_write_info ,
2009-04-05 15:02:45 +04:00
. proc_name = " A3000 " ,
. queuecommand = wd33c93_queuecommand ,
. eh_abort_handler = wd33c93_abort ,
. eh_host_reset_handler = wd33c93_host_reset ,
. can_queue = CAN_QUEUE ,
. this_id = 7 ,
. sg_tablesize = SG_ALL ,
. cmd_per_lun = CMD_PER_LUN ,
2022-02-18 22:51:15 +03:00
. cmd_size = sizeof ( struct scsi_pointer ) ,
2009-04-05 15:02:45 +04:00
} ;
static int __init amiga_a3000_scsi_probe ( struct platform_device * pdev )
2005-04-17 02:20:36 +04:00
{
2009-04-05 15:02:45 +04:00
struct resource * res ;
2009-05-17 15:35:07 +04:00
struct Scsi_Host * instance ;
2009-04-05 15:02:45 +04:00
int error ;
2009-05-17 23:05:53 +04:00
struct a3000_scsiregs * regs ;
2009-04-05 15:02:45 +04:00
wd33c93_regs wdregs ;
2010-04-12 23:55:15 +04:00
struct a3000_hostdata * hdata ;
2010-04-04 13:00:35 +04:00
2022-06-30 06:33:00 +03:00
if ( dma_set_mask_and_coherent ( & pdev - > dev , DMA_BIT_MASK ( 32 ) ) ) {
dev_warn ( & pdev - > dev , " cannot use 32 bit DMA \n " ) ;
return - ENODEV ;
}
2009-04-05 15:02:45 +04:00
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! res )
return - ENODEV ;
2010-04-04 13:00:35 +04:00
2009-04-05 15:02:45 +04:00
if ( ! request_mem_region ( res - > start , resource_size ( res ) , " wd33c93 " ) )
return - EBUSY ;
2010-04-04 13:00:35 +04:00
2009-04-05 15:02:45 +04:00
instance = scsi_host_alloc ( & amiga_a3000_scsi_template ,
2010-04-12 23:55:15 +04:00
sizeof ( struct a3000_hostdata ) ) ;
2009-04-05 15:02:45 +04:00
if ( ! instance ) {
error = - ENOMEM ;
goto fail_alloc ;
}
2010-04-04 13:00:35 +04:00
2009-05-17 15:35:07 +04:00
instance - > irq = IRQ_AMIGA_PORTS ;
2009-04-05 15:02:45 +04:00
2011-01-09 13:03:43 +03:00
regs = ZTWO_VADDR ( res - > start ) ;
2009-05-17 14:17:23 +04:00
regs - > DAWR = DAWR_A3000 ;
2009-04-05 15:02:45 +04:00
2009-05-17 14:17:23 +04:00
wdregs . SASR = & regs - > SASR ;
wdregs . SCMD = & regs - > SCMD ;
2009-04-05 15:02:45 +04:00
2009-05-17 15:35:07 +04:00
hdata = shost_priv ( instance ) ;
2022-06-30 06:33:00 +03:00
hdata - > dev = & pdev - > dev ;
2010-04-12 23:55:15 +04:00
hdata - > wh . no_sync = 0xff ;
hdata - > wh . fast = 0 ;
hdata - > wh . dma_mode = CTRL_DMA ;
hdata - > regs = regs ;
2009-04-05 15:02:45 +04:00
2009-05-17 15:35:07 +04:00
wd33c93_init ( instance , wdregs , dma_setup , dma_stop , WD33C93_FS_12_15 ) ;
2009-04-05 15:02:45 +04:00
error = request_irq ( IRQ_AMIGA_PORTS , a3000_intr , IRQF_SHARED ,
" A3000 SCSI " , instance ) ;
if ( error )
2010-04-04 13:00:35 +04:00
goto fail_irq ;
2009-04-05 15:02:45 +04:00
2009-05-17 14:17:23 +04:00
regs - > CNTR = CNTR_PDMD | CNTR_INTEN ;
2010-04-04 13:00:35 +04:00
2009-04-05 15:02:45 +04:00
error = scsi_add_host ( instance , NULL ) ;
if ( error )
goto fail_host ;
2005-04-17 02:20:36 +04:00
2009-04-05 15:02:45 +04:00
platform_set_drvdata ( pdev , instance ) ;
scsi_scan_host ( instance ) ;
2010-04-04 13:00:35 +04:00
return 0 ;
2009-04-05 15:02:45 +04:00
fail_host :
free_irq ( IRQ_AMIGA_PORTS , instance ) ;
fail_irq :
scsi_host_put ( instance ) ;
fail_alloc :
release_mem_region ( res - > start , resource_size ( res ) ) ;
return error ;
2005-04-17 02:20:36 +04:00
}
2023-12-03 19:05:46 +03:00
static void __exit amiga_a3000_scsi_remove ( struct platform_device * pdev )
2005-04-17 02:20:36 +04:00
{
2009-04-05 15:02:45 +04:00
struct Scsi_Host * instance = platform_get_drvdata ( pdev ) ;
2010-04-12 23:55:15 +04:00
struct a3000_hostdata * hdata = shost_priv ( instance ) ;
2009-04-05 15:02:45 +04:00
struct resource * res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
2005-05-28 15:56:31 +04:00
2010-04-12 23:55:15 +04:00
hdata - > regs - > CNTR = 0 ;
2009-04-05 15:02:45 +04:00
scsi_remove_host ( instance ) ;
free_irq ( IRQ_AMIGA_PORTS , instance ) ;
scsi_host_put ( instance ) ;
release_mem_region ( res - > start , resource_size ( res ) ) ;
2005-04-17 02:20:36 +04:00
}
2009-04-05 15:02:45 +04:00
static struct platform_driver amiga_a3000_scsi_driver = {
2023-12-03 19:05:46 +03:00
. remove_new = __exit_p ( amiga_a3000_scsi_remove ) ,
2009-04-05 15:02:45 +04:00
. driver = {
. name = " amiga-a3000-scsi " ,
} ,
2005-04-17 02:20:36 +04:00
} ;
2013-05-02 10:30:31 +04:00
module_platform_driver_probe ( amiga_a3000_scsi_driver , amiga_a3000_scsi_probe ) ;
2005-04-17 02:20:36 +04:00
2009-04-05 15:02:45 +04:00
MODULE_DESCRIPTION ( " Amiga 3000 built-in SCSI " ) ;
2005-04-17 02:20:36 +04:00
MODULE_LICENSE ( " GPL " ) ;
2009-04-05 15:02:45 +04:00
MODULE_ALIAS ( " platform:amiga-a3000-scsi " ) ;