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>
2009-03-27 14:46:26 +03:00
# include <asm/ide.h>
2008-10-24 01:22:08 +04:00
# 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 ] ) ;
}
2010-01-19 12:44:41 +03:00
static void tx4938ide_set_pio_mode ( ide_hwif_t * hwif , ide_drive_t * drive )
2008-10-24 01:22:08 +04:00
{
2013-07-30 12:16:47 +04:00
struct tx4938ide_platform_info * pdata = dev_get_platdata ( hwif - > dev ) ;
2010-01-19 12:44:41 +03:00
u8 safe = drive - > pio_mode - XFER_PIO_0 ;
2008-10-24 01:22:08 +04:00
ide_drive_t * pair ;
pair = ide_get_pair_dev ( drive ) ;
if ( pair )
2010-06-05 03:19:23 +04:00
safe = min_t ( u8 , safe , pair - > pio_mode - XFER_PIO_0 ) ;
2008-10-24 01:22:08 +04:00
tx4938ide_tune_ebusc ( pdata - > ebus_ch , pdata - > gbus_clock , safe ) ;
}
# ifdef __BIG_ENDIAN
/* custom iops (independent from SWAP_IO_SPACE) */
2009-03-27 14:46:38 +03:00
static void tx4938ide_input_data_swap ( ide_drive_t * drive , struct ide_cmd * cmd ,
2008-10-24 01:22:08 +04:00
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
}
2009-03-27 14:46:38 +03:00
static void tx4938ide_output_data_swap ( ide_drive_t * drive , struct ide_cmd * cmd ,
2008-10-24 01:22:08 +04:00
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 ,
2009-03-31 22:15:30 +04:00
. write_devctl = ide_write_devctl ,
2008-10-24 01:22:08 +04:00
2009-03-31 22:15:32 +04:00
. dev_select = ide_dev_select ,
2009-04-08 16:12:52 +04:00
. tf_load = ide_tf_load ,
. tf_read = ide_tf_read ,
2008-10-24 01:22:08 +04:00
. 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
} ;
2012-10-05 04:11:48 +04:00
static const struct ide_port_info tx4938ide_port_info __initconst = {
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 ,
2009-01-02 18:12:47 +03:00
. chipset = ide_generic ,
2008-10-24 01:22:08 +04:00
} ;
static int __init tx4938ide_probe ( struct platform_device * pdev )
{
2009-05-17 21:12:25 +04:00
struct ide_hw hw , * hws [ ] = { & hw } ;
2008-10-24 01:22:08 +04:00
struct ide_host * host ;
struct resource * res ;
2013-07-30 12:16:47 +04:00
struct tx4938ide_platform_info * pdata = dev_get_platdata ( & pdev - > dev ) ;
2008-10-24 01:22:08 +04:00
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 ,
2009-11-23 21:31:18 +03:00
resource_size ( res ) , " tx4938ide " ) )
2008-10-24 01:22:08 +04:00
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 ;
2009-05-17 21:12:24 +04:00
ret = ide_host_add ( & d , hws , 1 , & 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 ) ,
} ;
2013-03-04 20:25:46 +04:00
module_platform_driver_probe ( tx4938ide_driver , tx4938ide_probe ) ;
2008-10-24 01:22:08 +04:00
MODULE_DESCRIPTION ( " TX4938 internal IDE driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS ( " platform:tx4938ide " ) ;