2005-11-10 01:58:16 +03:00
/*
* linux / drivers / ide / pci / cs5535 . c
*
* Copyright ( C ) 2004 - 2005 Advanced Micro Devices , Inc .
2007-08-02 01:46:45 +04:00
* Copyright ( C ) 2007 Bartlomiej Zolnierkiewicz
2005-11-10 01:58:16 +03:00
*
* History :
* 09 / 20 / 2005 - Jaya Kumar < jayakumar . ide @ gmail . com >
* - Reworked tuneproc , set_drive , misc mods to prep for mainline
* - Work was sponsored by CIS ( M ) Sdn Bhd .
* Ported to Kernel 2.6 .11 on June 26 , 2005 by
* Wolfgang Zuleger < wolfgang . zuleger @ gmx . de >
* Alexander Kiausch < alex . kiausch @ t - online . de >
* Originally developed by AMD for 2.4 / 2.6
*
* Development of this chipset driver was funded
* by the nice folks at National Semiconductor / AMD .
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation .
*
* Documentation :
* CS5535 documentation available from AMD
*/
# include <linux/module.h>
# include <linux/pci.h>
# include <linux/ide.h>
# include "ide-timing.h"
# define MSR_ATAC_BASE 0x51300000
# define ATAC_GLD_MSR_CAP (MSR_ATAC_BASE+0)
# define ATAC_GLD_MSR_CONFIG (MSR_ATAC_BASE+0x01)
# define ATAC_GLD_MSR_SMI (MSR_ATAC_BASE+0x02)
# define ATAC_GLD_MSR_ERROR (MSR_ATAC_BASE+0x03)
# define ATAC_GLD_MSR_PM (MSR_ATAC_BASE+0x04)
# define ATAC_GLD_MSR_DIAG (MSR_ATAC_BASE+0x05)
# define ATAC_IO_BAR (MSR_ATAC_BASE+0x08)
# define ATAC_RESET (MSR_ATAC_BASE+0x10)
# define ATAC_CH0D0_PIO (MSR_ATAC_BASE+0x20)
# define ATAC_CH0D0_DMA (MSR_ATAC_BASE+0x21)
# define ATAC_CH0D1_PIO (MSR_ATAC_BASE+0x22)
# define ATAC_CH0D1_DMA (MSR_ATAC_BASE+0x23)
# define ATAC_PCI_ABRTERR (MSR_ATAC_BASE+0x24)
# define ATAC_BM0_CMD_PRIM 0x00
# define ATAC_BM0_STS_PRIM 0x02
# define ATAC_BM0_PRD 0x04
# define CS5535_CABLE_DETECT 0x48
/* Format I PIO settings. We seperate out cmd and data for safer timings */
static unsigned int cs5535_pio_cmd_timings [ 5 ] =
{ 0xF7F4 , 0x53F3 , 0x13F1 , 0x5131 , 0x1131 } ;
static unsigned int cs5535_pio_dta_timings [ 5 ] =
{ 0xF7F4 , 0xF173 , 0x8141 , 0x5131 , 0x1131 } ;
static unsigned int cs5535_mwdma_timings [ 3 ] =
{ 0x7F0FFFF3 , 0x7F035352 , 0x7f024241 } ;
static unsigned int cs5535_udma_timings [ 5 ] =
{ 0x7F7436A1 , 0x7F733481 , 0x7F723261 , 0x7F713161 , 0x7F703061 } ;
/* Macros to check if the register is the reset value - reset value is an
invalid timing and indicates the register has not been set previously */
# define CS5535_BAD_PIO(timings) ( (timings&~0x80000000UL) == 0x00009172 )
# define CS5535_BAD_DMA(timings) ( (timings & 0x000FFFFF) == 0x00077771 )
/****
* cs5535_set_speed - Configure the chipset to the new speed
* @ drive : Drive to set up
* @ speed : desired speed
*
* cs5535_set_speed ( ) configures the chipset to a new speed .
*/
static void cs5535_set_speed ( ide_drive_t * drive , u8 speed )
{
u32 reg = 0 , dummy ;
int unit = drive - > select . b . unit ;
/* Set the PIO timings */
if ( ( speed & XFER_MODE ) = = XFER_PIO ) {
2007-08-02 01:46:45 +04:00
ide_drive_t * pair = & drive - > hwif - > drives [ drive - > dn ^ 1 ] ;
u8 cmd , pioa ;
2005-11-10 01:58:16 +03:00
2007-08-02 01:46:45 +04:00
cmd = pioa = speed - XFER_PIO_0 ;
if ( pair - > present ) {
u8 piob = ide_get_best_pio_mode ( pair , 255 , 4 ) ;
if ( piob < cmd )
cmd = piob ;
}
2005-11-10 01:58:16 +03:00
/* Write the speed of the current drive */
reg = ( cs5535_pio_cmd_timings [ cmd ] < < 16 ) |
cs5535_pio_dta_timings [ pioa ] ;
wrmsr ( unit ? ATAC_CH0D1_PIO : ATAC_CH0D0_PIO , reg , 0 ) ;
/* And if nessesary - change the speed of the other drive */
rdmsr ( unit ? ATAC_CH0D0_PIO : ATAC_CH0D1_PIO , reg , dummy ) ;
if ( ( ( reg > > 16 ) & cs5535_pio_cmd_timings [ cmd ] ) ! =
cs5535_pio_cmd_timings [ cmd ] ) {
reg & = 0x0000FFFF ;
reg | = cs5535_pio_cmd_timings [ cmd ] < < 16 ;
wrmsr ( unit ? ATAC_CH0D0_PIO : ATAC_CH0D1_PIO , reg , 0 ) ;
}
/* Set bit 31 of the DMA register for PIO format 1 timings */
rdmsr ( unit ? ATAC_CH0D1_DMA : ATAC_CH0D0_DMA , reg , dummy ) ;
wrmsr ( unit ? ATAC_CH0D1_DMA : ATAC_CH0D0_DMA ,
reg | 0x80000000UL , 0 ) ;
} else {
rdmsr ( unit ? ATAC_CH0D1_DMA : ATAC_CH0D0_DMA , reg , dummy ) ;
reg & = 0x80000000UL ; /* Preserve the PIO format bit */
2007-08-02 01:46:42 +04:00
if ( speed > = XFER_UDMA_0 & & speed < = XFER_UDMA_4 )
2005-11-10 01:58:16 +03:00
reg | = cs5535_udma_timings [ speed - XFER_UDMA_0 ] ;
else if ( speed > = XFER_MW_DMA_0 & & speed < = XFER_MW_DMA_2 )
reg | = cs5535_mwdma_timings [ speed - XFER_MW_DMA_0 ] ;
else
return ;
wrmsr ( unit ? ATAC_CH0D1_DMA : ATAC_CH0D0_DMA , reg , 0 ) ;
}
}
/****
* cs5535_set_drive - Configure the drive to the new speed
* @ drive : Drive to set up
* @ speed : desired speed
*
* cs5535_set_drive ( ) configures the drive and the chipset to a
* new speed . It also can be called by upper layers .
*/
static int cs5535_set_drive ( ide_drive_t * drive , u8 speed )
{
2007-05-10 02:01:08 +04:00
speed = ide_rate_filter ( drive , speed ) ;
2005-11-10 01:58:16 +03:00
ide_config_drive_speed ( drive , speed ) ;
cs5535_set_speed ( drive , speed ) ;
return 0 ;
}
/****
* cs5535_tuneproc - PIO setup
* @ drive : drive to set up
* @ pio : mode to use ( 255 for ' best possible ' )
*
* A callback from the upper layers for PIO - only tuning .
*/
2007-08-02 01:46:45 +04:00
static void cs5535_tuneproc ( ide_drive_t * drive , u8 pio )
2005-11-10 01:58:16 +03:00
{
2007-08-02 01:46:45 +04:00
pio = ide_get_best_pio_mode ( drive , pio , 4 ) ;
ide_config_drive_speed ( drive , XFER_PIO_0 + pio ) ;
cs5535_set_speed ( drive , XFER_PIO_0 + pio ) ;
2005-11-10 01:58:16 +03:00
}
static int cs5535_dma_check ( ide_drive_t * drive )
{
drive - > init_speed = 0 ;
2007-05-10 02:01:09 +04:00
if ( ide_tune_dma ( drive ) )
2007-02-17 04:40:26 +03:00
return 0 ;
2005-11-10 01:58:16 +03:00
2007-08-02 01:46:45 +04:00
if ( ide_use_fast_pio ( drive ) )
cs5535_tuneproc ( drive , 255 ) ;
2007-02-17 04:40:25 +03:00
2007-02-17 04:40:26 +03:00
return - 1 ;
2005-11-10 01:58:16 +03:00
}
static u8 __devinit cs5535_cable_detect ( struct pci_dev * dev )
{
u8 bit ;
/* if a 80 wire cable was detected */
pci_read_config_byte ( dev , CS5535_CABLE_DETECT , & bit ) ;
2007-07-10 01:17:58 +04:00
return ( bit & 1 ) ? ATA_CBL_PATA80 : ATA_CBL_PATA40 ;
2005-11-10 01:58:16 +03:00
}
/****
* init_hwif_cs5535 - Initialize one ide cannel
* @ hwif : Channel descriptor
*
* This gets invoked by the IDE driver once for each channel . It
* performs channel - specific pre - initialization before drive probing .
*
*/
static void __devinit init_hwif_cs5535 ( ide_hwif_t * hwif )
{
int i ;
hwif - > autodma = 0 ;
hwif - > tuneproc = & cs5535_tuneproc ;
hwif - > speedproc = & cs5535_set_drive ;
hwif - > ide_dma_check = & cs5535_dma_check ;
hwif - > atapi_dma = 1 ;
hwif - > ultra_mask = 0x1F ;
hwif - > mwdma_mask = 0x07 ;
2007-07-10 01:17:58 +04:00
hwif - > cbl = cs5535_cable_detect ( hwif - > pci_dev ) ;
2005-11-10 01:58:16 +03:00
if ( ! noautodma )
hwif - > autodma = 1 ;
/* just setting autotune and not worrying about bios timings */
for ( i = 0 ; i < 2 ; i + + ) {
hwif - > drives [ i ] . autotune = 1 ;
hwif - > drives [ i ] . autodma = hwif - > autodma ;
}
}
static ide_pci_device_t cs5535_chipset __devinitdata = {
. name = " CS5535 " ,
. init_hwif = init_hwif_cs5535 ,
. autodma = AUTODMA ,
. bootable = ON_BOARD ,
2007-07-20 03:11:55 +04:00
. host_flags = IDE_HFLAG_SINGLE ,
2007-07-20 03:11:59 +04:00
. pio_mask = ATA_PIO4 ,
2005-11-10 01:58:16 +03:00
} ;
static int __devinit cs5535_init_one ( struct pci_dev * dev ,
const struct pci_device_id * id )
{
return ide_setup_pci_device ( dev , & cs5535_chipset ) ;
}
static struct pci_device_id cs5535_pci_tbl [ ] =
{
{ PCI_VENDOR_ID_NS , PCI_DEVICE_ID_NS_CS5535_IDE , PCI_ANY_ID ,
PCI_ANY_ID , 0 , 0 , 0 } ,
{ 0 , } ,
} ;
MODULE_DEVICE_TABLE ( pci , cs5535_pci_tbl ) ;
static struct pci_driver driver = {
. name = " CS5535_IDE " ,
. id_table = cs5535_pci_tbl ,
. probe = cs5535_init_one ,
} ;
static int __init cs5535_ide_init ( void )
{
return ide_pci_register_driver ( & driver ) ;
}
module_init ( cs5535_ide_init ) ;
MODULE_AUTHOR ( " AMD " ) ;
MODULE_DESCRIPTION ( " PCI driver module for AMD/NS CS5535 IDE " ) ;
MODULE_LICENSE ( " GPL " ) ;