/*
|
/*
|
ata.c -- ATA driver
|
ata.c -- ATA driver
|
Copyright (C) 2002 Richard Herveille, rherveille@opencores.org
|
Copyright (C) 2002 Richard Herveille, rherveille@opencores.org
|
|
|
This file is part of OpenRISC 1000 Reference Platform Monitor (ORPmon)
|
This file is part of OpenRISC 1000 Reference Platform Monitor (ORPmon)
|
|
|
This program is free software; you can redistribute it and/or modify
|
This program is free software; you can redistribute it and/or modify
|
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
the Free Software Foundation; either version 2 of the License, or
|
the Free Software Foundation; either version 2 of the License, or
|
(at your option) any later version
|
(at your option) any later version
|
|
|
This program is distributed in the hope that it will be useful,
|
This program is distributed in the hope that it will be useful,
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
GNU General Public License for more details.
|
GNU General Public License for more details.
|
|
|
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
along with this program; if not, write to the Free Software
|
along with this program; if not, write to the Free Software
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
*/
|
*/
|
|
|
#include <string.h>
|
#include <string.h>
|
|
|
/* get prototype for 'printf'
|
/* get prototype for 'printf'
|
get IN_CLOCK (from 'board.h')
|
get IN_CLOCK (from 'board.h')
|
*/
|
*/
|
#include "common.h"
|
#include "common.h"
|
#include "ata.h"
|
#include "ata.h"
|
|
|
|
|
/*
|
/*
|
static constants (yuck, yuck, yuck)
|
static constants (yuck, yuck, yuck)
|
*/
|
*/
|
static int usage[2] = {0, 0};
|
static int usage[2] = {0, 0};
|
static int hardsect_size[2] = {512, 512};
|
static int hardsect_size[2] = {512, 512};
|
static int current_pio_mode = 9999;
|
static int current_pio_mode = 9999;
|
|
|
/*
|
/*
|
local defines
|
local defines
|
*/
|
*/
|
#define TYPE(devid) (devid >> 4)
|
#define TYPE(devid) (devid >> 4)
|
#define REV(devid) (devid & 0xf)
|
#define REV(devid) (devid & 0xf)
|
|
|
#define BASE(inode) ( (unsigned long) (MAJOR(inode->i_rdev) << 24) )
|
#define BASE(inode) ( (unsigned long) (MAJOR(inode->i_rdev) << 24) )
|
#define DEVICE(inode) ( MINOR(inode->i_rdev) )
|
#define DEVICE(inode) ( MINOR(inode->i_rdev) )
|
|
|
#define IN_CLK_MHZ (IN_CLK / 1000000)
|
#define IN_CLK_MHZ (IN_CLK / 1000000)
|
#define TO_TICKS(time) ( ((time + (IN_CLK_MHZ >> 1)) / IN_CLK_MHZ) -1 )
|
#define TO_TICKS(time) ( ((time + (IN_CLK_MHZ >> 1)) / IN_CLK_MHZ) -1 )
|
|
|
|
|
/*
|
/*
|
A T A _ O P E N
|
A T A _ O P E N
|
|
|
opens ata device;
|
opens ata device;
|
- identifies ata-device-id
|
- identifies ata-device-id
|
- programs ata-host
|
- programs ata-host
|
|
|
MAJOR: atahost identification (base address)
|
MAJOR: atahost identification (base address)
|
MINOR: atadevice identification (MSB=0: device0, MSB=1: device1)
|
MINOR: atadevice identification (MSB=0: device0, MSB=1: device1)
|
*/
|
*/
|
int ata_open(struct inode *inode, struct file *filp)
|
int ata_open(struct inode *inode, struct file *filp)
|
{
|
{
|
unsigned int atahost, device;
|
unsigned int atahost, device;
|
unsigned short buf[256];
|
unsigned short buf[256];
|
unsigned short dev_pio;
|
unsigned short dev_pio;
|
unsigned long sectors;
|
unsigned long sectors;
|
|
|
char txt[41];
|
char txt[41];
|
|
|
printf( "Checking device-id\n" );
|
printf( "Checking device-id\n" );
|
/* check device-id (MINOR number) */
|
/* check device-id (MINOR number) */
|
if ( (device = DEVICE(inode)) > 1 )
|
if ( (device = DEVICE(inode)) > 1 )
|
return EOPENIDEV;
|
return EOPENIDEV;
|
|
|
printf( "Searching for hostcontroller: " );
|
printf( "Searching for hostcontroller: " );
|
/* get atahost id */
|
/* get atahost id */
|
atahost = ata_ioctl(inode, filp, ATA_IOCTL_IDENTIFY_HOST, 0);
|
atahost = ata_ioctl(inode, filp, ATA_IOCTL_IDENTIFY_HOST, 0);
|
if (atahost)
|
if (atahost)
|
{
|
{
|
printf( "OCIDEC-%1d, revision 0x%02X found at 0x%08lX.\n", TYPE(atahost), REV(atahost), BASE(inode) );
|
printf( "OCIDEC-%1d, revision 0x%02X found at 0x%08lX.\n", TYPE(atahost), REV(atahost), BASE(inode) );
|
}
|
}
|
else
|
else
|
{
|
{
|
printf( "No OCIDEC device found.\n" );
|
printf( "No OCIDEC device found.\n" );
|
return EOPENIHOST;
|
return EOPENIHOST;
|
}
|
}
|
|
|
|
|
/* check if the host has been opened already */
|
/* check if the host has been opened already */
|
if (!usage[device]++)
|
if (!usage[device]++)
|
{
|
{
|
printf( "Opening device for the first time\n" );
|
printf( "Opening device for the first time\n" );
|
/* no, take device out of reset */
|
/* no, take device out of reset */
|
ata_ioctl(inode, filp, ATA_IOCTL_SET_RST, CLR | ARG_HW_RST);
|
ata_ioctl(inode, filp, ATA_IOCTL_SET_RST, CLR | ARG_HW_RST);
|
ata_ioctl(inode, filp, ATA_IOCTL_SET_RST, CLR | ARG_SW_RST);
|
ata_ioctl(inode, filp, ATA_IOCTL_SET_RST, CLR | ARG_SW_RST);
|
|
|
/* program PIO timing registers (set to PIO mode 0) */
|
/* program PIO timing registers (set to PIO mode 0) */
|
ata_ioctl(inode, filp, ATA_IOCTL_SET_PIO, ARG_PIO0);
|
ata_ioctl(inode, filp, ATA_IOCTL_SET_PIO, ARG_PIO0);
|
|
|
/* enable host controller (for old OCIDEC cores) */
|
/* enable host controller (for old OCIDEC cores) */
|
ata_ioctl(inode, filp, ATA_IOCTL_ENABLE_HOST, 0);
|
ata_ioctl(inode, filp, ATA_IOCTL_ENABLE_HOST, 0);
|
}
|
}
|
|
|
/* select requested device */
|
/* select requested device */
|
ata_ioctl(inode, filp, ATA_IOCTL_SELECT_DEVICE, 0);
|
ata_ioctl(inode, filp, ATA_IOCTL_SELECT_DEVICE, 0);
|
|
|
printf( "Executing IdentifyDevice command\n" );
|
printf( "Executing IdentifyDevice command\n" );
|
/* identify requested ata-devices */
|
/* identify requested ata-devices */
|
ata_ioctl(inode, filp, ATA_IOCTL_EXEC_CMD, IDENTIFY_DEVICE);
|
ata_ioctl(inode, filp, ATA_IOCTL_EXEC_CMD, IDENTIFY_DEVICE);
|
|
|
/* read block from ata-device */
|
/* read block from ata-device */
|
buf[0] = 256;
|
buf[0] = 256;
|
ata_ioctl(inode, filp, ATA_IOCTL_READ, (unsigned long) buf);
|
ata_ioctl(inode, filp, ATA_IOCTL_READ, (unsigned long) buf);
|
|
|
/* check if a valid device was found */
|
/* check if a valid device was found */
|
/* bit 15 in identify_device word0 must be '0',
|
/* bit 15 in identify_device word0 must be '0',
|
or be equal to 0x848a (CFA feature set) */
|
or be equal to 0x848a (CFA feature set) */
|
if ( (buf[0] & 0x8000) && (buf[0] != 0x848a) )
|
if ( (buf[0] & 0x8000) && (buf[0] != 0x848a) )
|
{
|
{
|
ata_release(inode, filp);
|
ata_release(inode, filp);
|
return EOPENNODEV;
|
return EOPENNODEV;
|
}
|
}
|
|
|
/* Then check specific configuration word */
|
/* Then check specific configuration word */
|
if (buf[0] == 0x848a)
|
if (buf[0] == 0x848a)
|
printf( "Found CompactFLASH device.\n" );
|
printf( "Found CompactFLASH device.\n" );
|
else
|
else
|
switch(buf[2]) {
|
switch(buf[2]) {
|
case 0x37c8:
|
case 0x37c8:
|
/* device does require SET_FEATURES
|
/* device does require SET_FEATURES
|
IDENTIFY_DEVICE response is incomplete */
|
IDENTIFY_DEVICE response is incomplete */
|
case 0x738c:
|
case 0x738c:
|
/* device does require SET_FEATURES
|
/* device does require SET_FEATURES
|
IDENTIFY_DEVICE response is complete */
|
IDENTIFY_DEVICE response is complete */
|
ata_ioctl(inode, filp, ATA_IOCTL_SET_FEATURES, 0); //FIXME
|
ata_ioctl(inode, filp, ATA_IOCTL_SET_FEATURES, 0); //FIXME
|
break;
|
break;
|
case 0x8c73:
|
case 0x8c73:
|
/* device does not require SET_FEATURES
|
/* device does not require SET_FEATURES
|
IDENTIFY_DEVICE response is incomplete */
|
IDENTIFY_DEVICE response is incomplete */
|
case 0xc837:
|
case 0xc837:
|
/* device does not require SET_FEATURES
|
/* device does not require SET_FEATURES
|
IDENTIFY_DEVICE response is complete */
|
IDENTIFY_DEVICE response is complete */
|
break;
|
break;
|
|
|
default:
|
default:
|
ata_release(inode, filp);
|
ata_release(inode, filp);
|
return EOPENNODEV;
|
return EOPENNODEV;
|
}
|
}
|
|
|
/* checks ok */
|
/* checks ok */
|
|
|
/* display device model, serialnumber, and firmware revision */
|
/* display device model, serialnumber, and firmware revision */
|
memcpy(txt, &buf[27], 40);
|
memcpy(txt, &buf[27], 40);
|
txt[40] = '\0';
|
txt[40] = '\0';
|
printf( "Model : %s\n", txt );
|
printf( "Model : %s\n", txt );
|
|
|
memcpy(txt, &buf[10], 20);
|
memcpy(txt, &buf[10], 20);
|
txt[20] = '\0';
|
txt[20] = '\0';
|
printf( "Serial : %s\n", txt );
|
printf( "Serial : %s\n", txt );
|
|
|
memcpy( txt, &buf[23], 8);
|
memcpy( txt, &buf[23], 8);
|
txt[8] = '\0';
|
txt[8] = '\0';
|
printf( "Firmware rev: %s\n", txt );
|
printf( "Firmware rev: %s\n", txt );
|
|
|
/* display size in MBytes */
|
/* display size in MBytes */
|
sectors = (buf[61] << 16) | buf[60];
|
sectors = (buf[61] << 16) | buf[60];
|
printf( " %ld MBytes ", sectors >> 11 ); /* assuming 512bytes per sector */
|
printf( " %ld MBytes ", sectors >> 11 ); /* assuming 512bytes per sector */
|
|
|
/* get supported pio modes */
|
/* get supported pio modes */
|
dev_pio = buf[64];
|
dev_pio = buf[64];
|
|
|
/* program ocidec timing registers to highest possible pio mode */
|
/* program ocidec timing registers to highest possible pio mode */
|
if (dev_pio & PIO4)
|
if (dev_pio & PIO4)
|
{
|
{
|
printf("PIO-4 supported\n");
|
printf("PIO-4 supported\n");
|
ata_ioctl(inode, filp, ATA_IOCTL_SET_PIO, ARG_PIO4);
|
ata_ioctl(inode, filp, ATA_IOCTL_SET_PIO, ARG_PIO4);
|
}
|
}
|
else if (dev_pio & PIO3)
|
else if (dev_pio & PIO3)
|
{
|
{
|
printf("PIO-3 supported\n");
|
printf("PIO-3 supported\n");
|
ata_ioctl(inode, filp, ATA_IOCTL_SET_PIO, ARG_PIO3);
|
ata_ioctl(inode, filp, ATA_IOCTL_SET_PIO, ARG_PIO3);
|
}
|
}
|
else
|
else
|
{
|
{
|
printf("PIO-2 supported\n");
|
printf("PIO-2 supported\n");
|
ata_ioctl(inode, filp, ATA_IOCTL_SET_PIO, ARG_PIO2);
|
ata_ioctl(inode, filp, ATA_IOCTL_SET_PIO, ARG_PIO2);
|
}
|
}
|
|
|
return 0;
|
return 0;
|
}
|
}
|
|
|
|
|
/*
|
/*
|
A T A _ R E L E A S E
|
A T A _ R E L E A S E
|
|
|
closes ata device;
|
closes ata device;
|
*/
|
*/
|
int ata_release(struct inode *inode, struct file *filp)
|
int ata_release(struct inode *inode, struct file *filp)
|
{
|
{
|
/* decrease usage count */
|
/* decrease usage count */
|
if (!--usage[DEVICE(inode)])
|
if (!--usage[DEVICE(inode)])
|
{
|
{
|
/* reset atahost */
|
/* reset atahost */
|
ata_ioctl(inode, filp, ATA_IOCTL_SET_RST, SET | ARG_HW_RST);
|
ata_ioctl(inode, filp, ATA_IOCTL_SET_RST, SET | ARG_HW_RST);
|
}
|
}
|
|
|
return 0;
|
return 0;
|
}
|
}
|
|
|
|
|
/*
|
/*
|
A T A _ R E A D _ D P O R T
|
A T A _ R E A D _ D P O R T
|
|
|
closes ata device;
|
closes ata device;
|
*/
|
*/
|
int ata_read_dport(unsigned long base)
|
int ata_read_dport(unsigned long base)
|
{
|
{
|
/* device ready to transfer data ?? */
|
/* device ready to transfer data ?? */
|
while ( !ata_dev_datrdy(base) );
|
while ( !ata_dev_datrdy(base) );
|
|
|
return REG32(base + ATA_DR);
|
return REG32(base + ATA_DR);
|
}
|
}
|
|
|
|
|
/*
|
/*
|
A T A _ I O C T L
|
A T A _ I O C T L
|
*/
|
*/
|
int ata_ioctl(struct inode *inode, struct file *filp, unsigned command, unsigned long argument)
|
int ata_ioctl(struct inode *inode, struct file *filp, unsigned command, unsigned long argument)
|
{
|
{
|
unsigned long base = BASE(inode);
|
unsigned long base = BASE(inode);
|
|
|
switch (command)
|
switch (command)
|
{
|
{
|
case ATA_IOCTL_ENABLE_HOST:
|
case ATA_IOCTL_ENABLE_HOST:
|
/* enables the ATA host controller (sets IDE_EN bit) */
|
/* enables the ATA host controller (sets IDE_EN bit) */
|
{
|
{
|
REG32(base + ATA_CTRL) |= ATA_IDE_EN;
|
REG32(base + ATA_CTRL) |= ATA_IDE_EN;
|
return 0;
|
return 0;
|
}
|
}
|
|
|
|
|
case ATA_IOCTL_EXEC_CMD:
|
case ATA_IOCTL_EXEC_CMD:
|
/* sends a command to the ATA device */
|
/* sends a command to the ATA device */
|
{
|
{
|
while( ata_dev_busy(base) ); // wait for device ready
|
while( ata_dev_busy(base) ); // wait for device ready
|
ata_cmd(base) = argument & 0xff;
|
ata_cmd(base) = argument & 0xff;
|
return 0;
|
return 0;
|
}
|
}
|
|
|
|
|
case ATA_IOCTL_READ:
|
case ATA_IOCTL_READ:
|
{
|
{
|
unsigned short *buf_ptr = (short *)argument;
|
unsigned short *buf_ptr = (short *)argument;
|
int n, cnt;
|
int n, cnt;
|
|
|
cnt = buf_ptr[0];
|
cnt = buf_ptr[0];
|
for (n=0; n < cnt; n++)
|
for (n=0; n < cnt; n++)
|
{
|
{
|
while( !ata_dev_datrdy(base) ); // wait for data
|
while( !ata_dev_datrdy(base) ); // wait for data
|
*buf_ptr++ = ata_read_dport(base); // read data
|
*buf_ptr++ = ata_read_dport(base); // read data
|
}
|
}
|
|
|
return 0;
|
return 0;
|
}
|
}
|
|
|
|
|
case ATA_IOCTL_IDENTIFY_HOST:
|
case ATA_IOCTL_IDENTIFY_HOST:
|
/* reads OCIDEC status register configuration bits */
|
/* reads OCIDEC status register configuration bits */
|
return (REG32(base + ATA_STAT) >> 24);
|
return (REG32(base + ATA_STAT) >> 24);
|
|
|
|
|
case ATA_IOCTL_SELECT_DEVICE:
|
case ATA_IOCTL_SELECT_DEVICE:
|
/* selects ATA device */
|
/* selects ATA device */
|
{
|
{
|
while( ata_dev_busy(base) ); // wait for device ready
|
while( ata_dev_busy(base) ); // wait for device ready
|
|
|
/* sets the DEV bit in the device/head register */
|
/* sets the DEV bit in the device/head register */
|
if ( DEVICE(inode) )
|
if ( DEVICE(inode) )
|
REG32(base + ATA_DHR) |= ATA_DHR_DEV; /* select device1 */
|
REG32(base + ATA_DHR) |= ATA_DHR_DEV; /* select device1 */
|
else
|
else
|
REG32(base + ATA_DHR) &= ~ATA_DHR_DEV; /* select device0 */
|
REG32(base + ATA_DHR) &= ~ATA_DHR_DEV; /* select device0 */
|
|
|
return 0;
|
return 0;
|
}
|
}
|
|
|
|
|
case ATA_IOCTL_SET_FTE:
|
case ATA_IOCTL_SET_FTE:
|
{
|
{
|
/* set FTE bits */
|
/* set FTE bits */
|
if ( MINOR(inode->i_rdev) )
|
if ( MINOR(inode->i_rdev) )
|
if (argument)
|
if (argument)
|
REG32(base + ATA_CTRL) |= ATA_IORDY_FTE0;
|
REG32(base + ATA_CTRL) |= ATA_IORDY_FTE0;
|
else
|
else
|
REG32(base + ATA_CTRL) &= ~ATA_IORDY_FTE0;
|
REG32(base + ATA_CTRL) &= ~ATA_IORDY_FTE0;
|
else
|
else
|
if (argument)
|
if (argument)
|
REG32(base + ATA_CTRL) |= ATA_IORDY_FTE1;
|
REG32(base + ATA_CTRL) |= ATA_IORDY_FTE1;
|
else
|
else
|
REG32(base + ATA_CTRL) &= ~ATA_IORDY_FTE1;
|
REG32(base + ATA_CTRL) &= ~ATA_IORDY_FTE1;
|
|
|
return 0;
|
return 0;
|
}
|
}
|
|
|
|
|
case ATA_IOCTL_SET_PIO:
|
case ATA_IOCTL_SET_PIO:
|
/* programs the PIO timing registers */
|
/* programs the PIO timing registers */
|
{
|
{
|
unsigned long timing;
|
unsigned long timing;
|
unsigned short buf[256];
|
unsigned short buf[256];
|
|
|
/* do we need to read the identify_device block ?? */
|
/* do we need to read the identify_device block ?? */
|
if (argument & (ARG_PIO4 | ARG_PIO3) )
|
if (argument & (ARG_PIO4 | ARG_PIO3) )
|
{
|
{
|
/* identify requested ata-devices */
|
/* identify requested ata-devices */
|
ata_ioctl(inode, filp, ATA_IOCTL_EXEC_CMD, IDENTIFY_DEVICE);
|
ata_ioctl(inode, filp, ATA_IOCTL_EXEC_CMD, IDENTIFY_DEVICE);
|
|
|
/* read block from ata-device */
|
/* read block from ata-device */
|
buf[0] = 256;
|
buf[0] = 256;
|
ata_ioctl(inode, filp, ATA_IOCTL_READ, (unsigned long) buf);
|
ata_ioctl(inode, filp, ATA_IOCTL_READ, (unsigned long) buf);
|
}
|
}
|
|
|
/* program register transfer timing registers */
|
/* program register transfer timing registers */
|
/* set the slowest pio mode for register transfers */
|
/* set the slowest pio mode for register transfers */
|
if (argument < current_pio_mode)
|
if (argument < current_pio_mode)
|
{
|
{
|
switch (argument)
|
switch (argument)
|
{
|
{
|
case ARG_PIO4:
|
case ARG_PIO4:
|
if ( (buf[53] & 0x01) && buf[68] )
|
if ( (buf[53] & 0x01) && buf[68] )
|
timing = ata_calc_pio_timing(buf[68], PIO4_RT1, PIO4_RT2, PIO4_RT4, PIO4_RT2I, PIO4_RT9);
|
timing = ata_calc_pio_timing(buf[68], PIO4_RT1, PIO4_RT2, PIO4_RT4, PIO4_RT2I, PIO4_RT9);
|
else
|
else
|
timing = ata_calc_pio_timing(PIO4_RT0, PIO4_RT1, PIO4_RT2, PIO4_RT4, PIO4_RT2I, PIO4_RT9);
|
timing = ata_calc_pio_timing(PIO4_RT0, PIO4_RT1, PIO4_RT2, PIO4_RT4, PIO4_RT2I, PIO4_RT9);
|
break;
|
break;
|
|
|
case ARG_PIO3:
|
case ARG_PIO3:
|
if ( (buf[53] & 0x01) && buf[68] )
|
if ( (buf[53] & 0x01) && buf[68] )
|
timing = ata_calc_pio_timing(buf[68], PIO3_RT1, PIO3_RT2, PIO3_RT4, PIO3_RT2I, PIO3_RT9);
|
timing = ata_calc_pio_timing(buf[68], PIO3_RT1, PIO3_RT2, PIO3_RT4, PIO3_RT2I, PIO3_RT9);
|
else
|
else
|
timing = ata_calc_pio_timing(PIO3_RT0, PIO3_RT1, PIO3_RT2, PIO3_RT4, PIO3_RT2I, PIO3_RT9);
|
timing = ata_calc_pio_timing(PIO3_RT0, PIO3_RT1, PIO3_RT2, PIO3_RT4, PIO3_RT2I, PIO3_RT9);
|
break;
|
break;
|
|
|
case ARG_PIO2:
|
case ARG_PIO2:
|
timing = ata_calc_pio_timing(PIO2_RT0, PIO2_RT1, PIO2_RT2, PIO2_RT4, PIO2_RT2I, PIO2_RT9);
|
timing = ata_calc_pio_timing(PIO2_RT0, PIO2_RT1, PIO2_RT2, PIO2_RT4, PIO2_RT2I, PIO2_RT9);
|
break;
|
break;
|
|
|
case ARG_PIO1:
|
case ARG_PIO1:
|
timing = ata_calc_pio_timing(PIO1_RT0, PIO1_RT1, PIO1_RT2, PIO1_RT4, PIO1_RT2I, PIO1_RT9);
|
timing = ata_calc_pio_timing(PIO1_RT0, PIO1_RT1, PIO1_RT2, PIO1_RT4, PIO1_RT2I, PIO1_RT9);
|
break;
|
break;
|
|
|
default: /* PIO mode 0 */
|
default: /* PIO mode 0 */
|
timing = ata_calc_pio_timing(PIO0_RT0, PIO0_RT1, PIO0_RT2, PIO0_RT4, PIO0_RT2I, PIO0_RT9);
|
timing = ata_calc_pio_timing(PIO0_RT0, PIO0_RT1, PIO0_RT2, PIO0_RT4, PIO0_RT2I, PIO0_RT9);
|
break;
|
break;
|
}
|
}
|
|
|
/* program IORDY bit */
|
/* program IORDY bit */
|
if (argument & (ARG_PIO4 | ARG_PIO3) )
|
if (argument & (ARG_PIO4 | ARG_PIO3) )
|
REG32(base + ATA_CTRL) |= ATA_IORDY;
|
REG32(base + ATA_CTRL) |= ATA_IORDY;
|
else
|
else
|
REG32(base + ATA_CTRL) &= ~ATA_IORDY;
|
REG32(base + ATA_CTRL) &= ~ATA_IORDY;
|
|
|
/* program register transfer timing registers */
|
/* program register transfer timing registers */
|
REG32(base + ATA_PCTR) = timing;
|
REG32(base + ATA_PCTR) = timing;
|
|
|
current_pio_mode = argument;
|
current_pio_mode = argument;
|
}
|
}
|
|
|
/* Program data transfer timing registers */
|
/* Program data transfer timing registers */
|
if ( TYPE(ata_ioctl(inode, filp, ATA_IOCTL_IDENTIFY_HOST, 0)) > 1 )
|
if ( TYPE(ata_ioctl(inode, filp, ATA_IOCTL_IDENTIFY_HOST, 0)) > 1 )
|
{
|
{
|
switch (argument)
|
switch (argument)
|
{
|
{
|
case ARG_PIO4:
|
case ARG_PIO4:
|
if ( (buf[53] & 0x01) && buf[68] )
|
if ( (buf[53] & 0x01) && buf[68] )
|
timing = ata_calc_pio_timing(buf[68], PIO4_DT1, PIO4_DT2, PIO4_DT4, PIO4_DT2I, PIO4_DT9);
|
timing = ata_calc_pio_timing(buf[68], PIO4_DT1, PIO4_DT2, PIO4_DT4, PIO4_DT2I, PIO4_DT9);
|
else
|
else
|
timing = ata_calc_pio_timing(PIO4_DT0, PIO4_DT1, PIO4_DT2, PIO4_DT4, PIO4_DT2I, PIO4_DT9);
|
timing = ata_calc_pio_timing(PIO4_DT0, PIO4_DT1, PIO4_DT2, PIO4_DT4, PIO4_DT2I, PIO4_DT9);
|
break;
|
break;
|
|
|
case ARG_PIO3:
|
case ARG_PIO3:
|
if ( (buf[53] & 0x01) && buf[68] )
|
if ( (buf[53] & 0x01) && buf[68] )
|
timing = ata_calc_pio_timing(buf[68], PIO3_DT1, PIO3_DT2, PIO3_DT4, PIO3_DT2I, PIO3_DT9);
|
timing = ata_calc_pio_timing(buf[68], PIO3_DT1, PIO3_DT2, PIO3_DT4, PIO3_DT2I, PIO3_DT9);
|
else
|
else
|
timing = ata_calc_pio_timing(PIO3_DT0, PIO3_DT1, PIO3_DT2, PIO3_DT4, PIO3_DT2I, PIO3_DT9);
|
timing = ata_calc_pio_timing(PIO3_DT0, PIO3_DT1, PIO3_DT2, PIO3_DT4, PIO3_DT2I, PIO3_DT9);
|
break;
|
break;
|
|
|
case ARG_PIO2:
|
case ARG_PIO2:
|
timing = ata_calc_pio_timing(PIO2_DT0, PIO2_DT1, PIO2_DT2, PIO2_DT4, PIO2_DT2I, PIO2_DT9);
|
timing = ata_calc_pio_timing(PIO2_DT0, PIO2_DT1, PIO2_DT2, PIO2_DT4, PIO2_DT2I, PIO2_DT9);
|
break;
|
break;
|
|
|
case ARG_PIO1:
|
case ARG_PIO1:
|
timing = ata_calc_pio_timing(PIO1_DT0, PIO1_DT1, PIO1_DT2, PIO1_DT4, PIO1_DT2I, PIO1_DT9);
|
timing = ata_calc_pio_timing(PIO1_DT0, PIO1_DT1, PIO1_DT2, PIO1_DT4, PIO1_DT2I, PIO1_DT9);
|
break;
|
break;
|
|
|
default: /* PIO mode 0 */
|
default: /* PIO mode 0 */
|
timing = ata_calc_pio_timing(PIO0_DT0, PIO0_DT1, PIO0_DT2, PIO0_DT4, PIO0_DT2I, PIO0_DT9);
|
timing = ata_calc_pio_timing(PIO0_DT0, PIO0_DT1, PIO0_DT2, PIO0_DT4, PIO0_DT2I, PIO0_DT9);
|
break;
|
break;
|
}
|
}
|
|
|
/* program data transfer timing registers, set IORDY bit */
|
/* program data transfer timing registers, set IORDY bit */
|
if ( MINOR(inode->i_rdev) )
|
if ( MINOR(inode->i_rdev) )
|
{
|
{
|
REG32(base + ATA_PFTR1) = timing;
|
REG32(base + ATA_PFTR1) = timing;
|
|
|
/* program IORDY bit */
|
/* program IORDY bit */
|
if ( argument & (ARG_PIO4 | ARG_PIO3) )
|
if ( argument & (ARG_PIO4 | ARG_PIO3) )
|
REG32(base + ATA_CTRL) |= ATA_IORDY_FTE1;
|
REG32(base + ATA_CTRL) |= ATA_IORDY_FTE1;
|
else
|
else
|
REG32(base + ATA_CTRL) &= ~ATA_IORDY_FTE1;
|
REG32(base + ATA_CTRL) &= ~ATA_IORDY_FTE1;
|
}
|
}
|
else
|
else
|
{
|
{
|
REG32(base + ATA_PFTR0) = timing;
|
REG32(base + ATA_PFTR0) = timing;
|
|
|
/* program IORDY bit */
|
/* program IORDY bit */
|
if ( argument & (ARG_PIO4 | ARG_PIO3) )
|
if ( argument & (ARG_PIO4 | ARG_PIO3) )
|
REG32(base + ATA_CTRL) |= ATA_IORDY_FTE1;
|
REG32(base + ATA_CTRL) |= ATA_IORDY_FTE1;
|
else
|
else
|
REG32(base + ATA_CTRL) &= ~ATA_IORDY_FTE1;
|
REG32(base + ATA_CTRL) &= ~ATA_IORDY_FTE1;
|
}
|
}
|
|
|
/* enable data transfer timing */
|
/* enable data transfer timing */
|
ata_ioctl(inode, filp, ATA_IOCTL_SET_FTE, 1);
|
ata_ioctl(inode, filp, ATA_IOCTL_SET_FTE, 1);
|
}
|
}
|
|
|
return 0;
|
return 0;
|
}
|
}
|
|
|
|
|
case ATA_IOCTL_SET_RST:
|
case ATA_IOCTL_SET_RST:
|
/* sets or clears reset bits */
|
/* sets or clears reset bits */
|
if (argument & SET)
|
if (argument & SET)
|
switch (argument & ~SET) {
|
switch (argument & ~SET) {
|
case ARG_HW_RST: /* ata hardware reset */
|
case ARG_HW_RST: /* ata hardware reset */
|
REG32(base + ATA_CTRL) |= ATA_RST;
|
REG32(base + ATA_CTRL) |= ATA_RST;
|
return 0;
|
return 0;
|
|
|
case ARG_SW_RST: /* ata software reset */
|
case ARG_SW_RST: /* ata software reset */
|
REG32(base + ATA_DCR) |= ATA_DCR_RST;
|
REG32(base + ATA_DCR) |= ATA_DCR_RST;
|
return 0;
|
return 0;
|
|
|
case ARG_DEV_RST: /* ata device reset */
|
case ARG_DEV_RST: /* ata device reset */
|
REG32(base + ATA_CR) = DEVICE_RESET;
|
REG32(base + ATA_CR) = DEVICE_RESET;
|
return 0;
|
return 0;
|
|
|
default:
|
default:
|
return EIOCTLIARG;
|
return EIOCTLIARG;
|
}
|
}
|
else
|
else
|
{
|
{
|
switch(argument & ~SET) {
|
switch(argument & ~SET) {
|
case ARG_HW_RST: /* ata hardware reset */
|
case ARG_HW_RST: /* ata hardware reset */
|
REG32(base + ATA_CTRL) &= ~ATA_RST;
|
REG32(base + ATA_CTRL) &= ~ATA_RST;
|
return 0;
|
return 0;
|
|
|
case ARG_SW_RST: /* ata software reset */
|
case ARG_SW_RST: /* ata software reset */
|
REG32(base + ATA_DCR) &= ~ATA_DCR_RST;
|
REG32(base + ATA_DCR) &= ~ATA_DCR_RST;
|
return 0;
|
return 0;
|
|
|
case ARG_DEV_RST: /* ata device reset */
|
case ARG_DEV_RST: /* ata device reset */
|
return 0;
|
return 0;
|
|
|
default:
|
default:
|
return EIOCTLIARG;
|
return EIOCTLIARG;
|
}
|
}
|
}
|
}
|
|
|
|
|
default:
|
default:
|
printf( "FIXME: Unsupported IOCTL call %1d\n", command );
|
printf( "FIXME: Unsupported IOCTL call %1d\n", command );
|
return EINVAL;
|
return EINVAL;
|
}
|
}
|
}
|
}
|
|
|
|
|
unsigned long ata_calc_pio_timing(short t0, short t1, short t2, short t4, short t2i, short t9)
|
unsigned long ata_calc_pio_timing(short t0, short t1, short t2, short t4, short t2i, short t9)
|
{
|
{
|
int teoc;
|
int teoc;
|
|
|
teoc = t0 - t1 - t2;
|
teoc = t0 - t1 - t2;
|
|
|
if (teoc < 0)
|
if (teoc < 0)
|
teoc = 0;
|
teoc = 0;
|
|
|
if (t9 > teoc)
|
if (t9 > teoc)
|
teoc = t9;
|
teoc = t9;
|
|
|
if (t2i > teoc)
|
if (t2i > teoc)
|
teoc = t2i;
|
teoc = t2i;
|
|
|
t1 = TO_TICKS(t1);
|
t1 = TO_TICKS(t1);
|
t2 = TO_TICKS(t2);
|
t2 = TO_TICKS(t2);
|
t4 = TO_TICKS(t4);
|
t4 = TO_TICKS(t4);
|
teoc = TO_TICKS(teoc);
|
teoc = TO_TICKS(teoc);
|
|
|
return (teoc << ATA_TEOC) | (t4 << ATA_T4) | (t2 << ATA_T2) | (t1 << ATA_T1);
|
return (teoc << ATA_TEOC) | (t4 << ATA_T4) | (t2 << ATA_T2) | (t1 << ATA_T1);
|
}
|
}
|
|
|
|
|
/*
|
/*
|
A T A _ R E Q U E S T
|
A T A _ R E Q U E S T
|
*/
|
*/
|
int ata_request(struct inode *inode, struct file *filp, struct request *request)
|
int ata_request(struct inode *inode, struct file *filp, struct request *request)
|
{
|
{
|
unsigned int device = MINOR(inode->i_rdev);
|
unsigned int device = MINOR(inode->i_rdev);
|
unsigned long base = BASE(inode);
|
unsigned long base = BASE(inode);
|
unsigned int sector_size;
|
unsigned int sector_size;
|
|
|
/* get the sector size for the current device */
|
/* get the sector size for the current device */
|
sector_size = hardsect_size[device] >> 1;
|
sector_size = hardsect_size[device] >> 1;
|
|
|
/* set the device */
|
/* set the device */
|
ata_ioctl(inode, filp, ATA_IOCTL_SELECT_DEVICE, device);
|
ata_ioctl(inode, filp, ATA_IOCTL_SELECT_DEVICE, device);
|
|
|
/* get the base address */
|
/* get the base address */
|
base = BASE(inode);
|
base = BASE(inode);
|
|
|
switch (request->cmd) {
|
switch (request->cmd) {
|
case READ:
|
case READ:
|
{
|
{
|
register unsigned long i, n;
|
register unsigned long i, n;
|
register unsigned short rd_data;
|
register unsigned short rd_data;
|
register char *buf_ptr;
|
register char *buf_ptr;
|
|
|
/* program ata-device registers */
|
/* program ata-device registers */
|
REG32(base + ATA_DHR) = ((request->sector >> 24) & ATA_DHR_H) | ATA_DHR_LBA;
|
REG32(base + ATA_DHR) = ((request->sector >> 24) & ATA_DHR_H) | ATA_DHR_LBA;
|
REG32(base + ATA_CHR) = (request->sector >> 16) & 0xff;
|
REG32(base + ATA_CHR) = (request->sector >> 16) & 0xff;
|
REG32(base + ATA_CLR) = (request->sector >> 8) & 0xff;
|
REG32(base + ATA_CLR) = (request->sector >> 8) & 0xff;
|
REG32(base + ATA_SNR) = request->sector & 0xff;
|
REG32(base + ATA_SNR) = request->sector & 0xff;
|
|
|
REG32(base + ATA_SCR) = request->nr_sectors;
|
REG32(base + ATA_SCR) = request->nr_sectors;
|
|
|
/* send 'read_sector(s)' command */
|
/* send 'read_sector(s)' command */
|
REG32(base + ATA_CR) = READ_SECTORS;
|
REG32(base + ATA_CR) = READ_SECTORS;
|
|
|
/* check status & error registers */
|
/* check status & error registers */
|
if ( ata_astatus(base) & ATA_SR_ERR)
|
if ( ata_astatus(base) & ATA_SR_ERR)
|
return -1;
|
return -1;
|
|
|
/* read data from devices and store it in the buffer */
|
/* read data from devices and store it in the buffer */
|
buf_ptr = request->buffer;
|
buf_ptr = request->buffer;
|
|
|
for (n=0; n < request->nr_sectors; n++)
|
for (n=0; n < request->nr_sectors; n++)
|
for (i=0; i < sector_size; i++)
|
for (i=0; i < sector_size; i++)
|
{
|
{
|
rd_data = ata_read_dport(base);
|
rd_data = ata_read_dport(base);
|
|
|
/* the data is byte reversed, swap high & low bytes */
|
/* the data is byte reversed, swap high & low bytes */
|
*buf_ptr++ = rd_data;
|
*buf_ptr++ = rd_data;
|
*buf_ptr++ = rd_data >> 8;
|
*buf_ptr++ = rd_data >> 8;
|
}
|
}
|
|
|
return 0;
|
return 0;
|
}
|
}
|
|
|
|
|
case WRITE:
|
case WRITE:
|
/* check if device may be written to */
|
/* check if device may be written to */
|
if (filp->f_mode & FMODE_WRITE)
|
if (filp->f_mode & FMODE_WRITE)
|
{
|
{
|
/* do the write stuff */
|
/* do the write stuff */
|
}
|
}
|
else
|
else
|
return EINVAL;
|
return EINVAL;
|
|
|
|
|
default:
|
default:
|
return EINVAL;
|
return EINVAL;
|
}
|
}
|
|
|
return 0;
|
return 0;
|
}
|
}
|
|
|