2005-04-17 02:20:36 +04:00
/*
* Copyright ( C ) 1998 - 2000 Andreas S . Krebs ( akrebs @ altavista . net ) , Maintainer
* Copyright ( C ) 1998 - 2002 Andre Hedrick < andre @ linux - ide . org > , Integrator
*
* CYPRESS CY82C693 chipset IDE controller
*
* The CY82C693 chipset is used on Digital ' s PC - Alpha 164 SX boards .
* Writing the driver was quite simple , since most of the job is
2008-04-26 19:36:42 +04:00
* done by the generic pci - ide support .
2005-04-17 02:20:36 +04:00
* The hard part was finding the CY82C693 ' s datasheet on Cypress ' s
* web page : - ( . But Altavista solved this problem : - ) .
*
*
* Notes :
* - I recently got a 16.8 G IBM DTTA , so I was able to test it with
* a large and fast disk - the results look great , so I ' d say the
* driver is working fine : - )
2008-04-26 19:36:42 +04:00
* hdparm - t reports 8.17 MB / sec at about 6 % CPU usage for the DTTA
* - this is my first linux driver , so there ' s probably a lot of room
2005-04-17 02:20:36 +04:00
* for optimizations and bug fixing , so feel free to do it .
2008-04-26 19:36:42 +04:00
* - if using PIO mode it ' s a good idea to set the PIO mode and
2005-04-17 02:20:36 +04:00
* 32 - bit I / O support ( if possible ) , e . g . hdparm - p2 - c1 / dev / hda
* - I had some problems with my IBM DHEA with PIO modes < 2
* ( lost interrupts ) ? ? ? ? ?
* - first tests with DMA look okay , they seem to work , but there is a
* problem with sound - the BusMaster IDE TimeOut should fixed this
*
* Ancient History :
* AMH @ 1999 - 08 - 24 : v0 .34 init_cy82c693_chip moved to pci_init_cy82c693
* ASK @ 1999 - 01 - 23 : v0 .33 made a few minor code clean ups
* removed DMA clock speed setting by default
* added boot message
* ASK @ 1998 - 11 - 01 : v0 .32 added support to set BusMaster IDE TimeOut
* added support to set DMA Controller Clock Speed
* ASK @ 1998 - 10 - 31 : v0 .31 fixed problem with setting to high DMA modes
* on some drives .
* ASK @ 1998 - 10 - 29 : v0 .3 added support to set DMA modes
* ASK @ 1998 - 10 - 28 : v0 .2 added support to set PIO modes
* ASK @ 1998 - 10 - 27 : v0 .1 first version - chipset detection
*
*/
# include <linux/module.h>
# include <linux/types.h>
# include <linux/pci.h>
# include <linux/ide.h>
# include <linux/init.h>
# include <asm/io.h>
2008-07-25 00:53:32 +04:00
# define DRV_NAME "cy82c693"
2005-04-17 02:20:36 +04:00
/*
* The following are used to debug the driver .
*/
# define CY82C693_DEBUG_INFO 0
/*
* NOTE : the value for busmaster timeout is tricky and I got it by
* trial and error ! By using a to low value will cause DMA timeouts
* and drop IDE performance , and by using a to high value will cause
* audio playback to scatter .
* If you know a better value or how to calc it , please let me know .
*/
/* twice the value written in cy82c693ub datasheet */
# define BUSMASTER_TIMEOUT 0x50
/*
* the value above was tested on my machine and it seems to work okay
*/
/* here are the offset definitions for the registers */
# define CY82_IDE_CMDREG 0x04
# define CY82_IDE_ADDRSETUP 0x48
# define CY82_IDE_MASTER_IOR 0x4C
# define CY82_IDE_MASTER_IOW 0x4D
# define CY82_IDE_SLAVE_IOR 0x4E
# define CY82_IDE_SLAVE_IOW 0x4F
# define CY82_IDE_MASTER_8BIT 0x50
# define CY82_IDE_SLAVE_8BIT 0x51
# define CY82_INDEX_PORT 0x22
# define CY82_DATA_PORT 0x23
# define CY82_INDEX_CHANNEL0 0x30
# define CY82_INDEX_CHANNEL1 0x31
# define CY82_INDEX_TIMEOUT 0x32
/* the min and max PCI bus speed in MHz - from datasheet */
# define CY82C963_MIN_BUS_SPEED 25
# define CY82C963_MAX_BUS_SPEED 33
/* the struct for the PIO mode timings */
typedef struct pio_clocks_s {
u8 address_time ; /* Address setup (clocks) */
u8 time_16r ; /* clocks for 16bit IOR (0xF0=Active/data, 0x0F=Recovery) */
u8 time_16w ; /* clocks for 16bit IOW (0xF0=Active/data, 0x0F=Recovery) */
u8 time_8 ; /* clocks for 8bit (0xF0=Active/data, 0x0F=Recovery) */
} pio_clocks_t ;
/*
* calc clocks using bus_speed
* returns ( rounded up ) time in bus clocks for time in ns
*/
2008-04-26 19:36:42 +04:00
static int calc_clk ( int time , int bus_speed )
2005-04-17 02:20:36 +04:00
{
int clocks ;
2008-04-26 19:36:42 +04:00
clocks = ( time * bus_speed + 999 ) / 1000 - 1 ;
2005-04-17 02:20:36 +04:00
if ( clocks < 0 )
clocks = 0 ;
if ( clocks > 0x0F )
clocks = 0x0F ;
return clocks ;
}
/*
* compute the values for the clock registers for PIO
* mode and pci_clk [ MHz ] speed
*
* NOTE : for mode 0 , 1 and 2 drives 8 - bit IDE command control registers are used
* for mode 3 and 4 drives 8 and 16 - bit timings are the same
*
2008-04-26 19:36:42 +04:00
*/
static void compute_clocks ( u8 pio , pio_clocks_t * p_pclk )
2005-04-17 02:20:36 +04:00
{
2008-07-16 22:33:38 +04:00
struct ide_timing * t = ide_timing_find_mode ( XFER_PIO_0 + pio ) ;
2005-04-17 02:20:36 +04:00
int clk1 , clk2 ;
2008-07-15 23:21:46 +04:00
int bus_speed = ide_pci_clk ? ide_pci_clk : 33 ;
2005-04-17 02:20:36 +04:00
/* we don't check against CY82C693's min and max speed,
* so you can play with the idebus = xx parameter
*/
/* let's calc the address setup time clocks */
2008-07-16 22:33:38 +04:00
p_pclk - > address_time = ( u8 ) calc_clk ( t - > setup , bus_speed ) ;
2005-04-17 02:20:36 +04:00
/* let's calc the active and recovery time clocks */
2008-07-16 22:33:38 +04:00
clk1 = calc_clk ( t - > active , bus_speed ) ;
2005-04-17 02:20:36 +04:00
/* calc recovery timing */
2008-07-16 22:33:38 +04:00
clk2 = t - > cycle - t - > active - t - > setup ;
2005-04-17 02:20:36 +04:00
clk2 = calc_clk ( clk2 , bus_speed ) ;
clk1 = ( clk1 < < 4 ) | clk2 ; /* combine active and recovery clocks */
/* note: we use the same values for 16bit IOR and IOW
2008-04-26 19:36:42 +04:00
* those are all the same , since I don ' t have other
2005-04-17 02:20:36 +04:00
* timings than those from ide - lib . c
*/
p_pclk - > time_16r = ( u8 ) clk1 ;
p_pclk - > time_16w = ( u8 ) clk1 ;
/* what are good values for 8bit ?? */
p_pclk - > time_8 = ( u8 ) clk1 ;
}
/*
* set DMA mode a specific channel for CY82C693
*/
2008-01-26 22:13:00 +03:00
static void cy82c693_set_dma_mode ( ide_drive_t * drive , const u8 mode )
2005-04-17 02:20:36 +04:00
{
2008-01-26 22:13:00 +03:00
ide_hwif_t * hwif = drive - > hwif ;
u8 single = ( mode & 0x10 ) > > 4 , index = 0 , data = 0 ;
2005-04-17 02:20:36 +04:00
2008-01-26 22:13:00 +03:00
index = hwif - > channel ? CY82_INDEX_CHANNEL1 : CY82_INDEX_CHANNEL0 ;
2005-04-17 02:20:36 +04:00
2008-01-26 22:13:00 +03:00
data = ( mode & 3 ) | ( single < < 2 ) ;
2005-04-17 02:20:36 +04:00
2007-02-17 04:40:25 +03:00
outb ( index , CY82_INDEX_PORT ) ;
outb ( data , CY82_DATA_PORT ) ;
2005-04-17 02:20:36 +04:00
# if CY82C693_DEBUG_INFO
printk ( KERN_INFO " %s (ch=%d, dev=%d): set DMA mode to %d (single=%d) \n " ,
2008-10-13 23:39:40 +04:00
drive - > name , hwif - > channel , drive - > dn & 1 , mode & 3 , single ) ;
2005-04-17 02:20:36 +04:00
# endif /* CY82C693_DEBUG_INFO */
2008-04-26 19:36:42 +04:00
/*
2005-04-17 02:20:36 +04:00
* note : below we set the value for Bus Master IDE TimeOut Register
* I ' m not absolutly sure what this does , but it solved my problem
* with IDE DMA and sound , so I now can play sound and work with
* my IDE driver at the same time : - )
*
* If you know the correct ( best ) value for this register please
* let me know - ASK
*/
data = BUSMASTER_TIMEOUT ;
2007-02-17 04:40:25 +03:00
outb ( CY82_INDEX_TIMEOUT , CY82_INDEX_PORT ) ;
outb ( data , CY82_DATA_PORT ) ;
2005-04-17 02:20:36 +04:00
2008-04-26 19:36:42 +04:00
# if CY82C693_DEBUG_INFO
printk ( KERN_INFO " %s: Set IDE Bus Master TimeOut Register to 0x%X \n " ,
2005-04-17 02:20:36 +04:00
drive - > name , data ) ;
# endif /* CY82C693_DEBUG_INFO */
}
2007-10-12 01:54:00 +04:00
static void cy82c693_set_pio_mode ( ide_drive_t * drive , const u8 pio )
2005-04-17 02:20:36 +04:00
{
2009-01-06 19:20:52 +03:00
ide_hwif_t * hwif = drive - > hwif ;
2008-02-02 01:09:31 +03:00
struct pci_dev * dev = to_pci_dev ( hwif - > dev ) ;
2005-04-17 02:20:36 +04:00
pio_clocks_t pclk ;
unsigned int addrCtrl ;
/* select primary or secondary channel */
if ( hwif - > index > 0 ) { /* drive is on the secondary channel */
2006-10-03 12:14:35 +04:00
dev = pci_get_slot ( dev - > bus , dev - > devfn + 1 ) ;
2005-04-17 02:20:36 +04:00
if ( ! dev ) {
printk ( KERN_ERR " %s: tune_drive: "
" Cannot find secondary interface! \n " ,
drive - > name ) ;
return ;
}
}
/* let's calc the values for this PIO mode */
compute_clocks ( pio , & pclk ) ;
/* now let's write the clocks registers */
2008-10-13 23:39:40 +04:00
if ( ( drive - > dn & 1 ) = = 0 ) {
2005-04-17 02:20:36 +04:00
/*
* set master drive
* address setup control register
* is 32 bit ! ! !
2008-04-26 19:36:42 +04:00
*/
2005-04-17 02:20:36 +04:00
pci_read_config_dword ( dev , CY82_IDE_ADDRSETUP , & addrCtrl ) ;
2008-04-26 19:36:42 +04:00
2005-04-17 02:20:36 +04:00
addrCtrl & = ( ~ 0xF ) ;
addrCtrl | = ( unsigned int ) pclk . address_time ;
pci_write_config_dword ( dev , CY82_IDE_ADDRSETUP , addrCtrl ) ;
/* now let's set the remaining registers */
pci_write_config_byte ( dev , CY82_IDE_MASTER_IOR , pclk . time_16r ) ;
pci_write_config_byte ( dev , CY82_IDE_MASTER_IOW , pclk . time_16w ) ;
pci_write_config_byte ( dev , CY82_IDE_MASTER_8BIT , pclk . time_8 ) ;
2008-04-26 19:36:42 +04:00
2005-04-17 02:20:36 +04:00
addrCtrl & = 0xF ;
} else {
/*
* set slave drive
* address setup control register
* is 32 bit ! ! !
2008-04-26 19:36:42 +04:00
*/
2005-04-17 02:20:36 +04:00
pci_read_config_dword ( dev , CY82_IDE_ADDRSETUP , & addrCtrl ) ;
addrCtrl & = ( ~ 0xF0 ) ;
addrCtrl | = ( ( unsigned int ) pclk . address_time < < 4 ) ;
pci_write_config_dword ( dev , CY82_IDE_ADDRSETUP , addrCtrl ) ;
/* now let's set the remaining registers */
pci_write_config_byte ( dev , CY82_IDE_SLAVE_IOR , pclk . time_16r ) ;
pci_write_config_byte ( dev , CY82_IDE_SLAVE_IOW , pclk . time_16w ) ;
pci_write_config_byte ( dev , CY82_IDE_SLAVE_8BIT , pclk . time_8 ) ;
addrCtrl > > = 4 ;
addrCtrl & = 0xF ;
2008-04-26 19:36:42 +04:00
}
2005-04-17 02:20:36 +04:00
# if CY82C693_DEBUG_INFO
printk ( KERN_INFO " %s (ch=%d, dev=%d): set PIO timing to "
" (addr=0x%X, ior=0x%X, iow=0x%X, 8bit=0x%X) \n " ,
2008-10-13 23:39:40 +04:00
drive - > name , hwif - > channel , drive - > dn & 1 ,
2005-04-17 02:20:36 +04:00
addrCtrl , pclk . time_16r , pclk . time_16w , pclk . time_8 ) ;
# endif /* CY82C693_DEBUG_INFO */
}
2005-11-10 01:07:56 +03:00
static void __devinit init_iops_cy82c693 ( ide_hwif_t * hwif )
2005-04-17 02:20:36 +04:00
{
2007-10-26 22:31:15 +04:00
static ide_hwif_t * primary ;
2008-02-02 01:09:31 +03:00
struct pci_dev * dev = to_pci_dev ( hwif - > dev ) ;
2007-10-26 22:31:15 +04:00
2008-02-02 01:09:31 +03:00
if ( PCI_FUNC ( dev - > devfn ) = = 1 )
2005-04-17 02:20:36 +04:00
primary = hwif ;
else {
hwif - > mate = primary ;
hwif - > channel = 1 ;
}
}
2008-04-27 00:25:14 +04:00
static const struct ide_port_ops cy82c693_port_ops = {
. set_pio_mode = cy82c693_set_pio_mode ,
. set_dma_mode = cy82c693_set_dma_mode ,
} ;
2007-10-20 02:32:34 +04:00
static const struct ide_port_info cy82c693_chipset __devinitdata = {
2008-07-25 00:53:32 +04:00
. name = DRV_NAME ,
2007-02-17 04:40:24 +03:00
. init_iops = init_iops_cy82c693 ,
2008-04-27 00:25:14 +04:00
. port_ops = & cy82c693_port_ops ,
2008-04-26 19:36:38 +04:00
. host_flags = IDE_HFLAG_SINGLE ,
2007-07-20 03:11:59 +04:00
. pio_mask = ATA_PIO4 ,
2008-01-26 22:13:00 +03:00
. swdma_mask = ATA_SWDMA2 ,
. mwdma_mask = ATA_MWDMA2 ,
2005-04-17 02:20:36 +04:00
} ;
static int __devinit cy82c693_init_one ( struct pci_dev * dev , const struct pci_device_id * id )
{
struct pci_dev * dev2 ;
int ret = - ENODEV ;
/* CY82C693 is more than only a IDE controller.
Function 1 is primary IDE channel , function 2 - secondary . */
2008-04-26 19:36:42 +04:00
if ( ( dev - > class > > 8 ) = = PCI_CLASS_STORAGE_IDE & &
2005-04-17 02:20:36 +04:00
PCI_FUNC ( dev - > devfn ) = = 1 ) {
2006-10-03 12:14:35 +04:00
dev2 = pci_get_slot ( dev - > bus , dev - > devfn + 1 ) ;
2008-07-25 00:53:14 +04:00
ret = ide_pci_init_two ( dev , dev2 , & cy82c693_chipset , NULL ) ;
2008-07-25 00:53:21 +04:00
if ( ret )
pci_dev_put ( dev2 ) ;
2005-04-17 02:20:36 +04:00
}
return ret ;
}
2008-07-25 00:53:21 +04:00
static void __devexit cy82c693_remove ( struct pci_dev * dev )
{
struct ide_host * host = pci_get_drvdata ( dev ) ;
struct pci_dev * dev2 = host - > dev [ 1 ] ? to_pci_dev ( host - > dev [ 1 ] ) : NULL ;
ide_pci_remove ( dev ) ;
pci_dev_put ( dev2 ) ;
}
2007-10-17 00:29:56 +04:00
static const struct pci_device_id cy82c693_pci_tbl [ ] = {
{ PCI_VDEVICE ( CONTAQ , PCI_DEVICE_ID_CONTAQ_82C693 ) , 0 } ,
2005-04-17 02:20:36 +04:00
{ 0 , } ,
} ;
MODULE_DEVICE_TABLE ( pci , cy82c693_pci_tbl ) ;
2008-10-13 23:39:41 +04:00
static struct pci_driver cy82c693_pci_driver = {
2005-04-17 02:20:36 +04:00
. name = " Cypress_IDE " ,
. id_table = cy82c693_pci_tbl ,
. probe = cy82c693_init_one ,
2008-08-18 23:40:03 +04:00
. remove = __devexit_p ( cy82c693_remove ) ,
2008-10-11 00:39:32 +04:00
. suspend = ide_pci_suspend ,
. resume = ide_pci_resume ,
2005-04-17 02:20:36 +04:00
} ;
2007-01-27 15:46:56 +03:00
static int __init cy82c693_ide_init ( void )
2005-04-17 02:20:36 +04:00
{
2008-10-13 23:39:41 +04:00
return ide_pci_register_driver ( & cy82c693_pci_driver ) ;
2005-04-17 02:20:36 +04:00
}
2008-07-25 00:53:21 +04:00
static void __exit cy82c693_ide_exit ( void )
{
2008-10-13 23:39:41 +04:00
pci_unregister_driver ( & cy82c693_pci_driver ) ;
2008-07-25 00:53:21 +04:00
}
2005-04-17 02:20:36 +04:00
module_init ( cy82c693_ide_init ) ;
2008-07-25 00:53:21 +04:00
module_exit ( cy82c693_ide_exit ) ;
2005-04-17 02:20:36 +04:00
MODULE_AUTHOR ( " Andreas Krebs, Andre Hedrick " ) ;
MODULE_DESCRIPTION ( " PCI driver module for the Cypress CY82C693 IDE " ) ;
MODULE_LICENSE ( " GPL " ) ;