2008-10-24 01:22:08 +04:00
/*
* TX4938 internal IDE driver
* Based on tx4939ide . c .
*
* This file is subject to the terms and conditions of the GNU General Public
* License . See the file " COPYING " in the main directory of this archive
* for more details .
*
* ( C ) Copyright TOSHIBA CORPORATION 2005 - 2007
*/
# include <linux/module.h>
# include <linux/types.h>
# include <linux/ide.h>
# include <linux/init.h>
# include <linux/platform_device.h>
# include <linux/io.h>
# include <asm/txx9/tx4938.h>
static void tx4938ide_tune_ebusc ( unsigned int ebus_ch ,
unsigned int gbus_clock ,
u8 pio )
{
struct ide_timing * t = ide_timing_find_mode ( XFER_PIO_0 + pio ) ;
u64 cr = __raw_readq ( & tx4938_ebuscptr - > cr [ ebus_ch ] ) ;
unsigned int sp = ( cr > > 4 ) & 3 ;
unsigned int clock = gbus_clock / ( 4 - sp ) ;
unsigned int cycle = 1000000000 / clock ;
2008-11-02 23:40:10 +03:00
unsigned int shwt ;
int wt ;
2008-10-24 01:22:08 +04:00
/* Minimum DIOx- active time */
wt = DIV_ROUND_UP ( t - > act8b , cycle ) - 2 ;
/* IORDY setup time: 35ns */
2008-11-02 23:40:10 +03:00
wt = max_t ( int , wt , DIV_ROUND_UP ( 35 , cycle ) ) ;
2008-10-24 01:22:08 +04:00
/* actual wait-cycle is max(wt & ~1, 1) */
if ( wt > 2 & & ( wt & 1 ) )
wt + + ;
wt & = ~ 1 ;
/* Address-valid to DIOR/DIOW setup */
shwt = DIV_ROUND_UP ( t - > setup , cycle ) ;
2008-11-02 23:40:09 +03:00
/* -DIOx recovery time (SHWT * 4) and cycle time requirement */
while ( ( shwt * 4 + wt + ( wt ? 2 : 3 ) ) * cycle < t - > cycle )
shwt + + ;
if ( shwt > 7 ) {
pr_warning ( " tx4938ide: SHWT violation (%d) \n " , shwt ) ;
shwt = 7 ;
}
2008-10-24 01:22:08 +04:00
pr_debug ( " tx4938ide: ebus %d, bus cycle %dns, WT %d, SHWT %d \n " ,
ebus_ch , cycle , wt , shwt ) ;
2008-11-02 23:40:09 +03:00
__raw_writeq ( ( cr & ~ 0x3f007ull ) | ( wt < < 12 ) | shwt ,
2008-10-24 01:22:08 +04:00
& tx4938_ebuscptr - > cr [ ebus_ch ] ) ;
}
static void tx4938ide_set_pio_mode ( ide_drive_t * drive , const u8 pio )
{
ide_hwif_t * hwif = drive - > hwif ;
struct tx4938ide_platform_info * pdata = hwif - > dev - > platform_data ;
u8 safe = pio ;
ide_drive_t * pair ;
pair = ide_get_pair_dev ( drive ) ;
if ( pair )
safe = min ( safe , ide_get_best_pio_mode ( pair , 255 , 5 ) ) ;
tx4938ide_tune_ebusc ( pdata - > ebus_ch , pdata - > gbus_clock , safe ) ;
}
# ifdef __BIG_ENDIAN
/* custom iops (independent from SWAP_IO_SPACE) */
static u8 tx4938ide_inb ( unsigned long port )
{
return __raw_readb ( ( void __iomem * ) port ) ;
}
static void tx4938ide_outb ( u8 value , unsigned long port )
{
__raw_writeb ( value , ( void __iomem * ) port ) ;
}
static void tx4938ide_tf_load ( ide_drive_t * drive , ide_task_t * task )
{
ide_hwif_t * hwif = drive - > hwif ;
struct ide_io_ports * io_ports = & hwif - > io_ports ;
struct ide_taskfile * tf = & task - > tf ;
u8 HIHI = task - > tf_flags & IDE_TFLAG_LBA48 ? 0xE0 : 0xEF ;
if ( task - > tf_flags & IDE_TFLAG_FLAGGED )
HIHI = 0xFF ;
if ( task - > tf_flags & IDE_TFLAG_OUT_DATA ) {
u16 data = ( tf - > hob_data < < 8 ) | tf - > data ;
/* no endian swap */
__raw_writew ( data , ( void __iomem * ) io_ports - > data_addr ) ;
}
if ( task - > tf_flags & IDE_TFLAG_OUT_HOB_FEATURE )
tx4938ide_outb ( tf - > hob_feature , io_ports - > feature_addr ) ;
if ( task - > tf_flags & IDE_TFLAG_OUT_HOB_NSECT )
tx4938ide_outb ( tf - > hob_nsect , io_ports - > nsect_addr ) ;
if ( task - > tf_flags & IDE_TFLAG_OUT_HOB_LBAL )
tx4938ide_outb ( tf - > hob_lbal , io_ports - > lbal_addr ) ;
if ( task - > tf_flags & IDE_TFLAG_OUT_HOB_LBAM )
tx4938ide_outb ( tf - > hob_lbam , io_ports - > lbam_addr ) ;
if ( task - > tf_flags & IDE_TFLAG_OUT_HOB_LBAH )
tx4938ide_outb ( tf - > hob_lbah , io_ports - > lbah_addr ) ;
if ( task - > tf_flags & IDE_TFLAG_OUT_FEATURE )
tx4938ide_outb ( tf - > feature , io_ports - > feature_addr ) ;
if ( task - > tf_flags & IDE_TFLAG_OUT_NSECT )
tx4938ide_outb ( tf - > nsect , io_ports - > nsect_addr ) ;
if ( task - > tf_flags & IDE_TFLAG_OUT_LBAL )
tx4938ide_outb ( tf - > lbal , io_ports - > lbal_addr ) ;
if ( task - > tf_flags & IDE_TFLAG_OUT_LBAM )
tx4938ide_outb ( tf - > lbam , io_ports - > lbam_addr ) ;
if ( task - > tf_flags & IDE_TFLAG_OUT_LBAH )
tx4938ide_outb ( tf - > lbah , io_ports - > lbah_addr ) ;
if ( task - > tf_flags & IDE_TFLAG_OUT_DEVICE )
tx4938ide_outb ( ( tf - > device & HIHI ) | drive - > select ,
io_ports - > device_addr ) ;
}
static void tx4938ide_tf_read ( ide_drive_t * drive , ide_task_t * task )
{
ide_hwif_t * hwif = drive - > hwif ;
struct ide_io_ports * io_ports = & hwif - > io_ports ;
struct ide_taskfile * tf = & task - > tf ;
if ( task - > tf_flags & IDE_TFLAG_IN_DATA ) {
u16 data ;
/* no endian swap */
data = __raw_readw ( ( void __iomem * ) io_ports - > data_addr ) ;
tf - > data = data & 0xff ;
tf - > hob_data = ( data > > 8 ) & 0xff ;
}
/* be sure we're looking at the low order bits */
tx4938ide_outb ( ATA_DEVCTL_OBS & ~ 0x80 , io_ports - > ctl_addr ) ;
if ( task - > tf_flags & IDE_TFLAG_IN_FEATURE )
tf - > feature = tx4938ide_inb ( io_ports - > feature_addr ) ;
if ( task - > tf_flags & IDE_TFLAG_IN_NSECT )
tf - > nsect = tx4938ide_inb ( io_ports - > nsect_addr ) ;
if ( task - > tf_flags & IDE_TFLAG_IN_LBAL )
tf - > lbal = tx4938ide_inb ( io_ports - > lbal_addr ) ;
if ( task - > tf_flags & IDE_TFLAG_IN_LBAM )
tf - > lbam = tx4938ide_inb ( io_ports - > lbam_addr ) ;
if ( task - > tf_flags & IDE_TFLAG_IN_LBAH )
tf - > lbah = tx4938ide_inb ( io_ports - > lbah_addr ) ;
if ( task - > tf_flags & IDE_TFLAG_IN_DEVICE )
tf - > device = tx4938ide_inb ( io_ports - > device_addr ) ;
if ( task - > tf_flags & IDE_TFLAG_LBA48 ) {
tx4938ide_outb ( ATA_DEVCTL_OBS | 0x80 , io_ports - > ctl_addr ) ;
if ( task - > tf_flags & IDE_TFLAG_IN_HOB_FEATURE )
tf - > hob_feature =
tx4938ide_inb ( io_ports - > feature_addr ) ;
if ( task - > tf_flags & IDE_TFLAG_IN_HOB_NSECT )
tf - > hob_nsect = tx4938ide_inb ( io_ports - > nsect_addr ) ;
if ( task - > tf_flags & IDE_TFLAG_IN_HOB_LBAL )
tf - > hob_lbal = tx4938ide_inb ( io_ports - > lbal_addr ) ;
if ( task - > tf_flags & IDE_TFLAG_IN_HOB_LBAM )
tf - > hob_lbam = tx4938ide_inb ( io_ports - > lbam_addr ) ;
if ( task - > tf_flags & IDE_TFLAG_IN_HOB_LBAH )
tf - > hob_lbah = tx4938ide_inb ( io_ports - > lbah_addr ) ;
}
}
static void tx4938ide_input_data_swap ( ide_drive_t * drive , struct request * rq ,
void * buf , unsigned int len )
{
unsigned long port = drive - > hwif - > io_ports . data_addr ;
unsigned short * ptr = buf ;
unsigned int count = ( len + 1 ) / 2 ;
while ( count - - )
* ptr + + = cpu_to_le16 ( __raw_readw ( ( void __iomem * ) port ) ) ;
2008-12-29 22:27:29 +03:00
__ide_flush_dcache_range ( ( unsigned long ) buf , roundup ( len , 2 ) ) ;
2008-10-24 01:22:08 +04:00
}
static void tx4938ide_output_data_swap ( ide_drive_t * drive , struct request * rq ,
void * buf , unsigned int len )
{
unsigned long port = drive - > hwif - > io_ports . data_addr ;
unsigned short * ptr = buf ;
unsigned int count = ( len + 1 ) / 2 ;
while ( count - - ) {
__raw_writew ( le16_to_cpu ( * ptr ) , ( void __iomem * ) port ) ;
ptr + + ;
}
2008-12-29 22:27:29 +03:00
__ide_flush_dcache_range ( ( unsigned long ) buf , roundup ( len , 2 ) ) ;
2008-10-24 01:22:08 +04:00
}
static const struct ide_tp_ops tx4938ide_tp_ops = {
. exec_command = ide_exec_command ,
. read_status = ide_read_status ,
. read_altstatus = ide_read_altstatus ,
. read_sff_dma_status = ide_read_sff_dma_status ,
. set_irq = ide_set_irq ,
. tf_load = tx4938ide_tf_load ,
. tf_read = tx4938ide_tf_read ,
. input_data = tx4938ide_input_data_swap ,
. output_data = tx4938ide_output_data_swap ,
} ;
# endif /* __BIG_ENDIAN */
static const struct ide_port_ops tx4938ide_port_ops = {
2009-01-02 18:12:46 +03:00
. set_pio_mode = tx4938ide_set_pio_mode ,
2008-10-24 01:22:08 +04:00
} ;
static const struct ide_port_info tx4938ide_port_info __initdata = {
2009-01-02 18:12:46 +03:00
. port_ops = & tx4938ide_port_ops ,
2008-10-24 01:22:08 +04:00
# ifdef __BIG_ENDIAN
2009-01-02 18:12:46 +03:00
. tp_ops = & tx4938ide_tp_ops ,
2008-10-24 01:22:08 +04:00
# endif
2009-01-02 18:12:46 +03:00
. host_flags = IDE_HFLAG_MMIO | IDE_HFLAG_NO_DMA ,
. pio_mask = ATA_PIO5 ,
2008-10-24 01:22:08 +04:00
} ;
static int __init tx4938ide_probe ( struct platform_device * pdev )
{
hw_regs_t hw ;
hw_regs_t * hws [ ] = { & hw , NULL , NULL , NULL } ;
struct ide_host * host ;
struct resource * res ;
struct tx4938ide_platform_info * pdata = pdev - > dev . platform_data ;
int irq , ret , i ;
2008-11-02 23:40:09 +03:00
unsigned long mapbase , mapctl ;
2008-10-24 01:22:08 +04:00
struct ide_port_info d = tx4938ide_port_info ;
irq = platform_get_irq ( pdev , 0 ) ;
if ( irq < 0 )
return - ENODEV ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! res )
return - ENODEV ;
if ( ! devm_request_mem_region ( & pdev - > dev , res - > start ,
res - > end - res - > start + 1 , " tx4938ide " ) )
return - EBUSY ;
mapbase = ( unsigned long ) devm_ioremap ( & pdev - > dev , res - > start ,
2008-11-02 23:40:09 +03:00
8 < < pdata - > ioport_shift ) ;
mapctl = ( unsigned long ) devm_ioremap ( & pdev - > dev ,
res - > start + 0x10000 +
( 6 < < pdata - > ioport_shift ) ,
1 < < pdata - > ioport_shift ) ;
if ( ! mapbase | | ! mapctl )
2008-10-24 01:22:08 +04:00
return - EBUSY ;
memset ( & hw , 0 , sizeof ( hw ) ) ;
if ( pdata - > ioport_shift ) {
unsigned long port = mapbase ;
2008-11-02 23:40:09 +03:00
unsigned long ctl = mapctl ;
2008-10-24 01:22:08 +04:00
hw . io_ports_array [ 0 ] = port ;
# ifdef __BIG_ENDIAN
port + + ;
2008-11-02 23:40:09 +03:00
ctl + + ;
2008-10-24 01:22:08 +04:00
# endif
for ( i = 1 ; i < = 7 ; i + + )
hw . io_ports_array [ i ] =
port + ( i < < pdata - > ioport_shift ) ;
2008-11-02 23:40:09 +03:00
hw . io_ports . ctl_addr = ctl ;
2008-10-24 01:22:08 +04:00
} else
2008-11-02 23:40:09 +03:00
ide_std_init_ports ( & hw , mapbase , mapctl ) ;
2008-10-24 01:22:08 +04:00
hw . irq = irq ;
hw . dev = & pdev - > dev ;
2008-11-02 23:40:09 +03:00
pr_info ( " TX4938 IDE interface (base %#lx, ctl %#lx, irq %d) \n " ,
mapbase , mapctl , hw . irq ) ;
2008-10-24 01:22:08 +04:00
if ( pdata - > gbus_clock )
tx4938ide_tune_ebusc ( pdata - > ebus_ch , pdata - > gbus_clock , 0 ) ;
else
d . port_ops = NULL ;
ret = ide_host_add ( & d , hws , & host ) ;
2008-11-02 23:40:09 +03:00
if ( ! ret )
platform_set_drvdata ( pdev , host ) ;
return ret ;
2008-10-24 01:22:08 +04:00
}
static int __exit tx4938ide_remove ( struct platform_device * pdev )
{
struct ide_host * host = platform_get_drvdata ( pdev ) ;
ide_host_remove ( host ) ;
return 0 ;
}
static struct platform_driver tx4938ide_driver = {
. driver = {
. name = " tx4938ide " ,
. owner = THIS_MODULE ,
} ,
. remove = __exit_p ( tx4938ide_remove ) ,
} ;
static int __init tx4938ide_init ( void )
{
return platform_driver_probe ( & tx4938ide_driver , tx4938ide_probe ) ;
}
static void __exit tx4938ide_exit ( void )
{
platform_driver_unregister ( & tx4938ide_driver ) ;
}
module_init ( tx4938ide_init ) ;
module_exit ( tx4938ide_exit ) ;
MODULE_DESCRIPTION ( " TX4938 internal IDE driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS ( " platform:tx4938ide " ) ;