2005-04-17 02:20:36 +04:00
/*
* Copyright ( C ) 1996 Linus Torvalds & author ( see below )
*/
/*
* ALI M14xx chipset EIDE controller
*
* Works for ALI M1439 / 1443 / 1445 / 1487 / 1489 chipsets .
*
* Adapted from code developed by derekn @ vw . ece . cmu . edu . - ml
* Derek ' s notes follow :
*
* I think the code should be pretty understandable ,
* but I ' ll be happy to ( try to ) answer questions .
*
* The critical part is in the setupDrive function . The initRegisters
* function doesn ' t seem to be necessary , but the DOS driver does it , so
* I threw it in .
*
* I ' ve only tested this on my system , which only has one disk . I posted
* it to comp . sys . linux . hardware , so maybe some other people will try it
* out .
*
* Derek Noonburg ( derekn @ ece . cmu . edu )
* 95 - sep - 26
*
* Update 96 - jul - 13 :
*
* I ' ve since upgraded to two disks and a CD - ROM , with no trouble , and
* I ' ve also heard from several others who have used it successfully .
* This driver appears to work with both the 1443 / 1445 and the 1487 / 1489
* chipsets . I ' ve added support for PIO mode 4 for the 1487. This
* seems to work just fine on the 1443 also , although I ' m not sure it ' s
* advertised as supporting mode 4. ( I ' ve been running a WDC AC21200 in
* mode 4 for a while now with no trouble . ) - Derek
*/
# include <linux/module.h>
# include <linux/types.h>
# include <linux/kernel.h>
# include <linux/delay.h>
# include <linux/timer.h>
# include <linux/mm.h>
# include <linux/ioport.h>
# include <linux/blkdev.h>
# include <linux/ide.h>
# include <linux/init.h>
# include <asm/io.h>
2008-04-27 00:25:18 +04:00
# define DRV_NAME "ali14xx"
2005-04-17 02:20:36 +04:00
/* port addresses for auto-detection */
# define ALI_NUM_PORTS 4
2012-10-05 04:11:48 +04:00
static const int ports [ ALI_NUM_PORTS ] __initconst =
2007-11-27 23:35:57 +03:00
{ 0x074 , 0x0f4 , 0x034 , 0x0e4 } ;
2005-04-17 02:20:36 +04:00
/* register initialization data */
typedef struct { u8 reg , data ; } RegInitializer ;
2012-10-05 04:11:48 +04:00
static const RegInitializer initData [ ] __initconst = {
2005-04-17 02:20:36 +04:00
{ 0x01 , 0x0f } , { 0x02 , 0x00 } , { 0x03 , 0x00 } , { 0x04 , 0x00 } ,
{ 0x05 , 0x00 } , { 0x06 , 0x00 } , { 0x07 , 0x2b } , { 0x0a , 0x0f } ,
{ 0x25 , 0x00 } , { 0x26 , 0x00 } , { 0x27 , 0x00 } , { 0x28 , 0x00 } ,
{ 0x29 , 0x00 } , { 0x2a , 0x00 } , { 0x2f , 0x00 } , { 0x2b , 0x00 } ,
{ 0x2c , 0x00 } , { 0x2d , 0x00 } , { 0x2e , 0x00 } , { 0x30 , 0x00 } ,
{ 0x31 , 0x00 } , { 0x32 , 0x00 } , { 0x33 , 0x00 } , { 0x34 , 0xff } ,
{ 0x35 , 0x03 } , { 0x00 , 0x00 }
} ;
/* timing parameter registers for each drive */
static struct { u8 reg1 , reg2 , reg3 , reg4 ; } regTab [ 4 ] = {
{ 0x03 , 0x26 , 0x04 , 0x27 } , /* drive 0 */
{ 0x05 , 0x28 , 0x06 , 0x29 } , /* drive 1 */
{ 0x2b , 0x30 , 0x2c , 0x31 } , /* drive 2 */
{ 0x2d , 0x32 , 0x2e , 0x33 } , /* drive 3 */
} ;
static int basePort ; /* base port address */
static int regPort ; /* port for register number */
static int dataPort ; /* port for register data */
static u8 regOn ; /* output to base port to access registers */
static u8 regOff ; /* output to base port to close registers */
/*------------------------------------------------------------------------*/
/*
* Read a controller register .
*/
2008-04-26 19:36:41 +04:00
static inline u8 inReg ( u8 reg )
2005-04-17 02:20:36 +04:00
{
outb_p ( reg , regPort ) ;
return inb ( dataPort ) ;
}
/*
* Write a controller register .
*/
2008-04-26 19:36:41 +04:00
static void outReg ( u8 data , u8 reg )
2005-04-17 02:20:36 +04:00
{
outb_p ( reg , regPort ) ;
outb_p ( data , dataPort ) ;
}
2007-10-20 02:32:35 +04:00
static DEFINE_SPINLOCK ( ali14xx_lock ) ;
2005-04-17 02:20:36 +04:00
/*
* Set PIO mode for the specified drive .
* This function computes timing parameters
* and sets controller registers accordingly .
*/
2010-01-19 12:44:41 +03:00
static void ali14xx_set_pio_mode ( ide_hwif_t * hwif , ide_drive_t * drive )
2005-04-17 02:20:36 +04:00
{
int driveNum ;
int time1 , time2 ;
u8 param1 , param2 , param3 , param4 ;
unsigned long flags ;
2008-07-15 23:21:46 +04:00
int bus_speed = ide_vlb_clk ? ide_vlb_clk : 50 ;
2010-01-19 12:44:41 +03:00
const u8 pio = drive - > pio_mode - XFER_PIO_0 ;
2008-07-16 22:33:37 +04:00
struct ide_timing * t = ide_timing_find_mode ( XFER_PIO_0 + pio ) ;
2005-04-17 02:20:36 +04:00
/* calculate timing, according to PIO mode */
2007-07-20 03:11:56 +04:00
time1 = ide_pio_cycle_time ( drive , pio ) ;
2008-07-16 22:33:37 +04:00
time2 = t - > active ;
2005-04-17 02:20:36 +04:00
param3 = param1 = ( time2 * bus_speed + 999 ) / 1000 ;
param4 = param2 = ( time1 * bus_speed + 999 ) / 1000 - param1 ;
if ( pio < 3 ) {
param3 + = 8 ;
param4 + = 8 ;
}
printk ( KERN_DEBUG " %s: PIO mode%d, t1=%dns, t2=%dns, cycles = %d+%d, %d+%d \n " ,
drive - > name , pio , time1 , time2 , param1 , param2 , param3 , param4 ) ;
/* stuff timing parameters into controller registers */
2008-10-13 23:39:40 +04:00
driveNum = ( drive - > hwif - > index < < 1 ) + ( drive - > dn & 1 ) ;
2007-10-20 02:32:35 +04:00
spin_lock_irqsave ( & ali14xx_lock , flags ) ;
2005-04-17 02:20:36 +04:00
outb_p ( regOn , basePort ) ;
outReg ( param1 , regTab [ driveNum ] . reg1 ) ;
outReg ( param2 , regTab [ driveNum ] . reg2 ) ;
outReg ( param3 , regTab [ driveNum ] . reg3 ) ;
outReg ( param4 , regTab [ driveNum ] . reg4 ) ;
outb_p ( regOff , basePort ) ;
2007-10-20 02:32:35 +04:00
spin_unlock_irqrestore ( & ali14xx_lock , flags ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Auto - detect the IDE controller port .
*/
2008-04-26 19:36:41 +04:00
static int __init findPort ( void )
2005-04-17 02:20:36 +04:00
{
int i ;
u8 t ;
unsigned long flags ;
local_irq_save ( flags ) ;
for ( i = 0 ; i < ALI_NUM_PORTS ; + + i ) {
basePort = ports [ i ] ;
regOff = inb ( basePort ) ;
for ( regOn = 0x30 ; regOn < = 0x33 ; + + regOn ) {
outb_p ( regOn , basePort ) ;
if ( inb ( basePort ) = = regOn ) {
regPort = basePort + 4 ;
dataPort = basePort + 8 ;
t = inReg ( 0 ) & 0xf0 ;
outb_p ( regOff , basePort ) ;
local_irq_restore ( flags ) ;
if ( t ! = 0x50 )
return 0 ;
return 1 ; /* success */
}
}
outb_p ( regOff , basePort ) ;
}
local_irq_restore ( flags ) ;
return 0 ;
}
/*
* Initialize controller registers with default values .
*/
2008-04-26 19:36:41 +04:00
static int __init initRegisters ( void )
{
2007-11-27 23:35:57 +03:00
const RegInitializer * p ;
2005-04-17 02:20:36 +04:00
u8 t ;
unsigned long flags ;
local_irq_save ( flags ) ;
outb_p ( regOn , basePort ) ;
for ( p = initData ; p - > reg ! = 0 ; + + p )
outReg ( p - > data , p - > reg ) ;
outb_p ( 0x01 , regPort ) ;
t = inb ( regPort ) & 0x01 ;
outb_p ( regOff , basePort ) ;
local_irq_restore ( flags ) ;
return t ;
}
2008-04-27 00:25:14 +04:00
static const struct ide_port_ops ali14xx_port_ops = {
. set_pio_mode = ali14xx_set_pio_mode ,
} ;
2008-02-02 21:56:31 +03:00
static const struct ide_port_info ali14xx_port_info = {
2008-04-27 00:25:18 +04:00
. name = DRV_NAME ,
2008-02-02 21:56:31 +03:00
. chipset = ide_ali14xx ,
2008-04-27 00:25:14 +04:00
. port_ops = & ali14xx_port_ops ,
2008-04-27 17:38:29 +04:00
. host_flags = IDE_HFLAG_NO_DMA ,
2008-02-02 21:56:31 +03:00
. pio_mask = ATA_PIO4 ,
} ;
2005-04-17 02:20:36 +04:00
static int __init ali14xx_probe ( void )
{
printk ( KERN_DEBUG " ali14xx: base=0x%03x, regOn=0x%02x. \n " ,
basePort , regOn ) ;
/* initialize controller registers */
if ( ! initRegisters ( ) ) {
printk ( KERN_ERR " ali14xx: Chip initialization failed. \n " ) ;
return 1 ;
}
2008-04-27 00:25:16 +04:00
return ide_legacy_device_add ( & ali14xx_port_info , 0 ) ;
2005-04-17 02:20:36 +04:00
}
2012-01-13 03:02:20 +04:00
static bool probe_ali14xx ;
2007-03-03 19:48:55 +03:00
module_param_named ( probe , probe_ali14xx , bool , 0 ) ;
MODULE_PARM_DESC ( probe , " probe for ALI M14xx chipsets " ) ;
2008-01-26 22:13:07 +03:00
static int __init ali14xx_init ( void )
2005-04-17 02:20:36 +04:00
{
2007-03-03 19:48:55 +03:00
if ( probe_ali14xx = = 0 )
goto out ;
2005-04-17 02:20:36 +04:00
/* auto-detect IDE controller port */
if ( findPort ( ) ) {
if ( ali14xx_probe ( ) )
return - ENODEV ;
return 0 ;
}
printk ( KERN_ERR " ali14xx: not found. \n " ) ;
2007-03-03 19:48:55 +03:00
out :
2005-04-17 02:20:36 +04:00
return - ENODEV ;
}
module_init ( ali14xx_init ) ;
MODULE_AUTHOR ( " see local file " ) ;
MODULE_DESCRIPTION ( " support of ALI 14XX IDE chipsets " ) ;
MODULE_LICENSE ( " GPL " ) ;