2006-09-29 21:30:05 +04:00
/*
* pata_winbond . c - Winbond VLB ATA controllers
* ( C ) 2006 Red Hat < alan @ redhat . com >
*
* Support for the Winbond 83759 A when operating in advanced mode .
* Multichip mode is not currently supported .
*/
2006-12-11 19:14:06 +03:00
2006-09-29 21:30:05 +04:00
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/blkdev.h>
# include <linux/delay.h>
# include <scsi/scsi_host.h>
# include <linux/libata.h>
# include <linux/platform_device.h>
# define DRV_NAME "pata_winbond"
2007-05-22 04:26:38 +04:00
# define DRV_VERSION "0.0.3"
2006-09-29 21:30:05 +04:00
# define NR_HOST 4 /* Two winbond controllers, two channels each */
struct winbond_data {
unsigned long config ;
struct platform_device * platform_dev ;
} ;
static struct ata_host * winbond_host [ NR_HOST ] ;
static struct winbond_data winbond_data [ NR_HOST ] ;
static int nr_winbond_host ;
# ifdef MODULE
static int probe_winbond = 1 ;
# else
static int probe_winbond ;
# endif
2007-04-26 11:19:27 +04:00
static DEFINE_SPINLOCK ( winbond_lock ) ;
2006-09-29 21:30:05 +04:00
static void winbond_writecfg ( unsigned long port , u8 reg , u8 val )
{
unsigned long flags ;
spin_lock_irqsave ( & winbond_lock , flags ) ;
outb ( reg , port + 0x01 ) ;
outb ( val , port + 0x02 ) ;
spin_unlock_irqrestore ( & winbond_lock , flags ) ;
}
static u8 winbond_readcfg ( unsigned long port , u8 reg )
{
u8 val ;
unsigned long flags ;
spin_lock_irqsave ( & winbond_lock , flags ) ;
outb ( reg , port + 0x01 ) ;
val = inb ( port + 0x02 ) ;
spin_unlock_irqrestore ( & winbond_lock , flags ) ;
return val ;
}
static void winbond_set_piomode ( struct ata_port * ap , struct ata_device * adev )
{
struct ata_timing t ;
struct winbond_data * winbond = ap - > host - > private_data ;
int active , recovery ;
u8 reg ;
int timing = 0x88 + ( ap - > port_no * 4 ) + ( adev - > devno * 2 ) ;
reg = winbond_readcfg ( winbond - > config , 0x81 ) ;
2006-12-11 19:14:06 +03:00
2006-09-29 21:30:05 +04:00
/* Get the timing data in cycles */
if ( reg & 0x40 ) /* Fast VLB bus, assume 50MHz */
ata_timing_compute ( adev , adev - > pio_mode , & t , 20000 , 1000 ) ;
else
ata_timing_compute ( adev , adev - > pio_mode , & t , 30303 , 1000 ) ;
active = ( FIT ( t . active , 3 , 17 ) - 1 ) & 0x0F ;
recovery = ( FIT ( t . recover , 1 , 15 ) + 1 ) & 0x0F ;
timing = ( active < < 4 ) | recovery ;
winbond_writecfg ( winbond - > config , timing , reg ) ;
2006-12-11 19:14:06 +03:00
2006-09-29 21:30:05 +04:00
/* Load the setup timing */
2006-12-11 19:14:06 +03:00
2006-09-29 21:30:05 +04:00
reg = 0x35 ;
if ( adev - > class ! = ATA_DEV_ATA )
reg | = 0x08 ; /* FIFO off */
if ( ! ata_pio_need_iordy ( adev ) )
reg | = 0x02 ; /* IORDY off */
reg | = ( FIT ( t . setup , 0 , 3 ) < < 6 ) ;
winbond_writecfg ( winbond - > config , timing + 1 , reg ) ;
}
static void winbond_data_xfer ( struct ata_device * adev , unsigned char * buf , unsigned int buflen , int write_data )
{
struct ata_port * ap = adev - > ap ;
int slop = buflen & 3 ;
if ( ata_id_has_dword_io ( adev - > id ) ) {
if ( write_data )
2007-02-01 09:06:36 +03:00
iowrite32_rep ( ap - > ioaddr . data_addr , buf , buflen > > 2 ) ;
2006-09-29 21:30:05 +04:00
else
2007-02-01 09:06:36 +03:00
ioread32_rep ( ap - > ioaddr . data_addr , buf , buflen > > 2 ) ;
2006-09-29 21:30:05 +04:00
if ( unlikely ( slop ) ) {
u32 pad ;
if ( write_data ) {
memcpy ( & pad , buf + buflen - slop , slop ) ;
2007-02-01 09:06:36 +03:00
pad = le32_to_cpu ( pad ) ;
iowrite32 ( pad , ap - > ioaddr . data_addr ) ;
2006-09-29 21:30:05 +04:00
} else {
2007-02-01 09:06:36 +03:00
pad = ioread32 ( ap - > ioaddr . data_addr ) ;
pad = cpu_to_le16 ( pad ) ;
2006-09-29 21:30:05 +04:00
memcpy ( buf + buflen - slop , & pad , slop ) ;
}
}
} else
2007-02-01 09:06:36 +03:00
ata_data_xfer ( adev , buf , buflen , write_data ) ;
2006-09-29 21:30:05 +04:00
}
static struct scsi_host_template winbond_sht = {
. module = THIS_MODULE ,
. name = DRV_NAME ,
. ioctl = ata_scsi_ioctl ,
. queuecommand = ata_scsi_queuecmd ,
. can_queue = ATA_DEF_QUEUE ,
. this_id = ATA_SHT_THIS_ID ,
. sg_tablesize = LIBATA_MAX_PRD ,
. cmd_per_lun = ATA_SHT_CMD_PER_LUN ,
. emulated = ATA_SHT_EMULATED ,
. use_clustering = ATA_SHT_USE_CLUSTERING ,
. proc_name = DRV_NAME ,
. dma_boundary = ATA_DMA_BOUNDARY ,
. slave_configure = ata_scsi_slave_config ,
2006-11-29 06:10:46 +03:00
. slave_destroy = ata_scsi_slave_destroy ,
2006-09-29 21:30:05 +04:00
. bios_param = ata_std_bios_param ,
} ;
static struct ata_port_operations winbond_port_ops = {
. port_disable = ata_port_disable ,
. set_piomode = winbond_set_piomode ,
. tf_load = ata_tf_load ,
. tf_read = ata_tf_read ,
. check_status = ata_check_status ,
. exec_command = ata_exec_command ,
. dev_select = ata_std_dev_select ,
. freeze = ata_bmdma_freeze ,
. thaw = ata_bmdma_thaw ,
. error_handler = ata_bmdma_error_handler ,
. post_internal_cmd = ata_bmdma_post_internal_cmd ,
2007-03-27 09:43:42 +04:00
. cable_detect = ata_cable_40wire ,
2006-09-29 21:30:05 +04:00
. qc_prep = ata_qc_prep ,
. qc_issue = ata_qc_issue_prot ,
. data_xfer = winbond_data_xfer ,
. irq_clear = ata_bmdma_irq_clear ,
2007-01-26 10:27:58 +03:00
. irq_on = ata_irq_on ,
. irq_ack = ata_irq_ack ,
2006-09-29 21:30:05 +04:00
. port_start = ata_port_start ,
} ;
/**
* winbond_init_one - attach a winbond interface
* @ type : Type to display
* @ io : I / O port start
* @ irq : interrupt line
* @ fast : True if on a > 33 Mhz VLB
*
* Register a VLB bus IDE interface . Such interfaces are PIO and we
* assume do not support IRQ sharing .
*/
static __init int winbond_init_one ( unsigned long port )
{
struct platform_device * pdev ;
u8 reg ;
libata: convert the remaining PATA drivers to new init model
Convert pdc_adma, pata_cs5520, pata_isapnp, pata_ixp4xx_cf,
pata_legacy, pata_mpc52xx, pata_mpiix, pata_pcmcia, pata_pdc2027x,
pata_platform, pata_qdi, pata_scc and pata_winbond to new init model.
* init_one()'s now follow more consistent init order
* cs5520 now registers one host with two ports, not two hosts. If any
of the two ports are disabled, it's made dummy as other drivers do.
Tested pdc_adma and pata_legacy. Both are as broken as before. The
rest are compile tested only.
Signed-off-by: Tejun Heo <htejun@gmail.com>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
2007-04-17 18:44:08 +04:00
int i , rc ;
2006-09-29 21:30:05 +04:00
reg = winbond_readcfg ( port , 0x81 ) ;
reg | = 0x80 ; /* jumpered mode off */
winbond_writecfg ( port , 0x81 , reg ) ;
reg = winbond_readcfg ( port , 0x83 ) ;
reg | = 0xF0 ; /* local control */
winbond_writecfg ( port , 0x83 , reg ) ;
reg = winbond_readcfg ( port , 0x85 ) ;
reg | = 0xF0 ; /* programmable timing */
winbond_writecfg ( port , 0x85 , reg ) ;
reg = winbond_readcfg ( port , 0x81 ) ;
2006-12-11 19:14:06 +03:00
2006-09-29 21:30:05 +04:00
if ( ! ( reg & 0x03 ) ) /* Disabled */
return 0 ;
for ( i = 0 ; i < 2 ; i + + ) {
2007-02-01 09:06:36 +03:00
unsigned long cmd_port = 0x1F0 - ( 0x80 * i ) ;
libata: convert the remaining PATA drivers to new init model
Convert pdc_adma, pata_cs5520, pata_isapnp, pata_ixp4xx_cf,
pata_legacy, pata_mpc52xx, pata_mpiix, pata_pcmcia, pata_pdc2027x,
pata_platform, pata_qdi, pata_scc and pata_winbond to new init model.
* init_one()'s now follow more consistent init order
* cs5520 now registers one host with two ports, not two hosts. If any
of the two ports are disabled, it's made dummy as other drivers do.
Tested pdc_adma and pata_legacy. Both are as broken as before. The
rest are compile tested only.
Signed-off-by: Tejun Heo <htejun@gmail.com>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
2007-04-17 18:44:08 +04:00
struct ata_host * host ;
struct ata_port * ap ;
2007-02-01 09:06:36 +03:00
void __iomem * cmd_addr , * ctl_addr ;
2006-09-29 21:30:05 +04:00
libata: convert the remaining PATA drivers to new init model
Convert pdc_adma, pata_cs5520, pata_isapnp, pata_ixp4xx_cf,
pata_legacy, pata_mpc52xx, pata_mpiix, pata_pcmcia, pata_pdc2027x,
pata_platform, pata_qdi, pata_scc and pata_winbond to new init model.
* init_one()'s now follow more consistent init order
* cs5520 now registers one host with two ports, not two hosts. If any
of the two ports are disabled, it's made dummy as other drivers do.
Tested pdc_adma and pata_legacy. Both are as broken as before. The
rest are compile tested only.
Signed-off-by: Tejun Heo <htejun@gmail.com>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
2007-04-17 18:44:08 +04:00
if ( ! ( reg & ( 1 < < i ) ) )
continue ;
pdev = platform_device_register_simple ( DRV_NAME , nr_winbond_host , NULL , 0 ) ;
if ( IS_ERR ( pdev ) )
return PTR_ERR ( pdev ) ;
rc = - ENOMEM ;
host = ata_host_alloc ( & pdev - > dev , 1 ) ;
if ( ! host )
goto err_unregister ;
rc = - ENOMEM ;
cmd_addr = devm_ioport_map ( & pdev - > dev , cmd_port , 8 ) ;
ctl_addr = devm_ioport_map ( & pdev - > dev , cmd_port + 0x0206 , 1 ) ;
if ( ! cmd_addr | | ! ctl_addr )
goto err_unregister ;
ap = host - > ports [ 0 ] ;
ap - > ops = & winbond_port_ops ;
ap - > pio_mask = 0x1F ;
ap - > flags | = ATA_FLAG_SLAVE_POSS ;
ap - > ioaddr . cmd_addr = cmd_addr ;
ap - > ioaddr . altstatus_addr = ctl_addr ;
ap - > ioaddr . ctl_addr = ctl_addr ;
ata_std_ports ( & ap - > ioaddr ) ;
/* hook in a private data structure per channel */
host - > private_data = & winbond_data [ nr_winbond_host ] ;
winbond_data [ nr_winbond_host ] . config = port ;
winbond_data [ nr_winbond_host ] . platform_dev = pdev ;
/* activate */
rc = ata_host_activate ( host , 14 + i , ata_interrupt , 0 ,
& winbond_sht ) ;
if ( rc )
goto err_unregister ;
winbond_host [ nr_winbond_host + + ] = dev_get_drvdata ( & pdev - > dev ) ;
2006-09-29 21:30:05 +04:00
}
return 0 ;
libata: convert the remaining PATA drivers to new init model
Convert pdc_adma, pata_cs5520, pata_isapnp, pata_ixp4xx_cf,
pata_legacy, pata_mpc52xx, pata_mpiix, pata_pcmcia, pata_pdc2027x,
pata_platform, pata_qdi, pata_scc and pata_winbond to new init model.
* init_one()'s now follow more consistent init order
* cs5520 now registers one host with two ports, not two hosts. If any
of the two ports are disabled, it's made dummy as other drivers do.
Tested pdc_adma and pata_legacy. Both are as broken as before. The
rest are compile tested only.
Signed-off-by: Tejun Heo <htejun@gmail.com>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
2007-04-17 18:44:08 +04:00
err_unregister :
platform_device_unregister ( pdev ) ;
return rc ;
2006-09-29 21:30:05 +04:00
}
/**
* winbond_init - attach winbond interfaces
*
* Attach winbond IDE interfaces by scanning the ports it may occupy .
*/
static __init int winbond_init ( void )
{
static const unsigned long config [ 2 ] = { 0x130 , 0x1B0 } ;
int ct = 0 ;
int i ;
2006-12-11 19:14:06 +03:00
2006-09-29 21:30:05 +04:00
if ( probe_winbond = = 0 )
return - ENODEV ;
/*
* Check both base addresses
*/
for ( i = 0 ; i < 2 ; i + + ) {
if ( probe_winbond & ( 1 < < i ) ) {
int ret = 0 ;
unsigned long port = config [ i ] ;
if ( request_region ( port , 2 , " pata_winbond " ) ) {
ret = winbond_init_one ( port ) ;
if ( ret < = 0 )
release_region ( port , 2 ) ;
else ct + = ret ;
}
}
}
if ( ct ! = 0 )
return 0 ;
return - ENODEV ;
}
static __exit void winbond_exit ( void )
{
int i ;
for ( i = 0 ; i < nr_winbond_host ; i + + ) {
2007-01-20 10:00:28 +03:00
ata_host_detach ( winbond_host [ i ] ) ;
2006-09-29 21:30:05 +04:00
release_region ( winbond_data [ i ] . config , 2 ) ;
platform_device_unregister ( winbond_data [ i ] . platform_dev ) ;
}
}
MODULE_AUTHOR ( " Alan Cox " ) ;
MODULE_DESCRIPTION ( " low-level driver for Winbond VL ATA " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_VERSION ( DRV_VERSION ) ;
module_init ( winbond_init ) ;
module_exit ( winbond_exit ) ;
module_param ( probe_winbond , int , 0 ) ;