2018-03-16 19:15:47 +03:00
// SPDX-License-Identifier: GPL-2.0
/*
* Amiga Gayle PATA controller driver
*
* Copyright ( c ) 2018 Samsung Electronics Co . , Ltd .
* http : //www.samsung.com
*
* Based on gayle . c :
*
* Created 12 Jul 1997 by Geert Uytterhoeven
*/
# include <linux/ata.h>
# include <linux/blkdev.h>
# include <linux/delay.h>
# include <linux/interrupt.h>
# include <linux/kernel.h>
# include <linux/libata.h>
# include <linux/mm.h>
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/zorro.h>
# include <scsi/scsi_cmnd.h>
# include <scsi/scsi_host.h>
# include <asm/amigahw.h>
# include <asm/amigaints.h>
# include <asm/amigayle.h>
# include <asm/setup.h>
# define DRV_NAME "pata_gayle"
# define DRV_VERSION "0.1.0"
# define GAYLE_CONTROL 0x101a
2023-03-22 22:53:59 +03:00
static const struct scsi_host_template pata_gayle_sht = {
2018-03-16 19:15:47 +03:00
ATA_PIO_SHT ( DRV_NAME ) ,
} ;
/* FIXME: is this needed? */
static unsigned int pata_gayle_data_xfer ( struct ata_queued_cmd * qc ,
unsigned char * buf ,
unsigned int buflen , int rw )
{
struct ata_device * dev = qc - > dev ;
struct ata_port * ap = dev - > link - > ap ;
void __iomem * data_addr = ap - > ioaddr . data_addr ;
unsigned int words = buflen > > 1 ;
/* Transfer multiple of 2 bytes */
if ( rw = = READ )
raw_insw ( ( u16 * ) data_addr , ( u16 * ) buf , words ) ;
else
raw_outsw ( ( u16 * ) data_addr , ( u16 * ) buf , words ) ;
/* Transfer trailing byte, if any. */
if ( unlikely ( buflen & 0x01 ) ) {
unsigned char pad [ 2 ] = { } ;
/* Point buf to the tail of buffer */
buf + = buflen - 1 ;
if ( rw = = READ ) {
raw_insw ( ( u16 * ) data_addr , ( u16 * ) pad , 1 ) ;
* buf = pad [ 0 ] ;
} else {
pad [ 0 ] = * buf ;
raw_outsw ( ( u16 * ) data_addr , ( u16 * ) pad , 1 ) ;
}
words + + ;
}
return words < < 1 ;
}
/*
* Provide our own set_mode ( ) as we don ' t want to change anything that has
* already been configured . .
*/
static int pata_gayle_set_mode ( struct ata_link * link ,
struct ata_device * * unused )
{
struct ata_device * dev ;
ata_for_each_dev ( dev , link , ENABLED ) {
/* We don't really care */
dev - > pio_mode = dev - > xfer_mode = XFER_PIO_0 ;
dev - > xfer_shift = ATA_SHIFT_PIO ;
dev - > flags | = ATA_DFLAG_PIO ;
ata_dev_info ( dev , " configured for PIO \n " ) ;
}
return 0 ;
}
static bool pata_gayle_irq_check ( struct ata_port * ap )
{
u8 ch ;
ch = z_readb ( ( unsigned long ) ap - > private_data ) ;
return ! ! ( ch & GAYLE_IRQ_IDE ) ;
}
static void pata_gayle_irq_clear ( struct ata_port * ap )
{
( void ) z_readb ( ( unsigned long ) ap - > ioaddr . status_addr ) ;
z_writeb ( 0x7c , ( unsigned long ) ap - > private_data ) ;
}
static struct ata_port_operations pata_gayle_a1200_ops = {
. inherits = & ata_sff_port_ops ,
. sff_data_xfer = pata_gayle_data_xfer ,
. sff_irq_check = pata_gayle_irq_check ,
. sff_irq_clear = pata_gayle_irq_clear ,
. cable_detect = ata_cable_unknown ,
. set_mode = pata_gayle_set_mode ,
} ;
static struct ata_port_operations pata_gayle_a4000_ops = {
. inherits = & ata_sff_port_ops ,
. sff_data_xfer = pata_gayle_data_xfer ,
. cable_detect = ata_cable_unknown ,
. set_mode = pata_gayle_set_mode ,
} ;
2023-11-05 18:00:37 +03:00
static int pata_gayle_init_one ( struct platform_device * pdev )
2018-03-16 19:15:47 +03:00
{
struct resource * res ;
struct gayle_ide_platform_data * pdata ;
struct ata_host * host ;
struct ata_port * ap ;
void __iomem * base ;
int ret ;
pdata = dev_get_platdata ( & pdev - > dev ) ;
dev_info ( & pdev - > dev , " Amiga Gayle IDE controller (A%u style) \n " ,
pdata - > explicit_ack ? 1200 : 4000 ) ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! res )
return - ENODEV ;
if ( ! devm_request_mem_region ( & pdev - > dev , res - > start ,
resource_size ( res ) , DRV_NAME ) ) {
pr_err ( DRV_NAME " : resources busy \n " ) ;
return - EBUSY ;
}
/* allocate host */
host = ata_host_alloc ( & pdev - > dev , 1 ) ;
if ( ! host )
return - ENOMEM ;
ap = host - > ports [ 0 ] ;
if ( pdata - > explicit_ack )
ap - > ops = & pata_gayle_a1200_ops ;
else
ap - > ops = & pata_gayle_a4000_ops ;
ap - > pio_mask = ATA_PIO4 ;
ap - > flags | = ATA_FLAG_SLAVE_POSS | ATA_FLAG_NO_IORDY ;
base = ZTWO_VADDR ( pdata - > base ) ;
ap - > ioaddr . data_addr = base ;
ap - > ioaddr . error_addr = base + 2 + 1 * 4 ;
ap - > ioaddr . feature_addr = base + 2 + 1 * 4 ;
ap - > ioaddr . nsect_addr = base + 2 + 2 * 4 ;
ap - > ioaddr . lbal_addr = base + 2 + 3 * 4 ;
ap - > ioaddr . lbam_addr = base + 2 + 4 * 4 ;
ap - > ioaddr . lbah_addr = base + 2 + 5 * 4 ;
ap - > ioaddr . device_addr = base + 2 + 6 * 4 ;
ap - > ioaddr . status_addr = base + 2 + 7 * 4 ;
ap - > ioaddr . command_addr = base + 2 + 7 * 4 ;
ap - > ioaddr . altstatus_addr = base + GAYLE_CONTROL ;
ap - > ioaddr . ctl_addr = base + GAYLE_CONTROL ;
ap - > private_data = ( void * ) ZTWO_VADDR ( pdata - > irqport ) ;
ata_port_desc ( ap , " cmd 0x%lx ctl 0x%lx " , pdata - > base ,
pdata - > base + GAYLE_CONTROL ) ;
ret = ata_host_activate ( host , IRQ_AMIGA_PORTS , ata_sff_interrupt ,
IRQF_SHARED , & pata_gayle_sht ) ;
if ( ret )
return ret ;
platform_set_drvdata ( pdev , host ) ;
return 0 ;
}
2023-11-05 18:00:39 +03:00
static void pata_gayle_remove_one ( struct platform_device * pdev )
2018-03-16 19:15:47 +03:00
{
struct ata_host * host = platform_get_drvdata ( pdev ) ;
ata_host_detach ( host ) ;
}
static struct platform_driver pata_gayle_driver = {
2023-11-05 18:00:37 +03:00
. probe = pata_gayle_init_one ,
2023-11-05 18:00:39 +03:00
. remove_new = pata_gayle_remove_one ,
2018-03-16 19:15:47 +03:00
. driver = {
. name = " amiga-gayle-ide " ,
} ,
} ;
2023-11-05 18:00:37 +03:00
module_platform_driver ( pata_gayle_driver ) ;
2018-03-16 19:15:47 +03:00
MODULE_AUTHOR ( " Bartlomiej Zolnierkiewicz " ) ;
MODULE_DESCRIPTION ( " low-level driver for Amiga Gayle PATA " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_ALIAS ( " platform:amiga-gayle-ide " ) ;
MODULE_VERSION ( DRV_VERSION ) ;