mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-26 22:52:18 +00:00
369d0aa967
judging from other printfs in the same file, it seems ata should be postpended with the interface number, not the address of the global port variable. Fixes this for current u-boot-mpc83xx tree: Configuring for MPC8349ITX board... sata_sil3114.c: In function 'sata_bus_softreset': sata_sil3114.c:99: warning: format '%u' expects type 'unsigned int', but argument 2 has type 'struct sata_port *' sata_sil3114.c:108: warning: format '%u' expects type 'unsigned int', but argument 2 has type 'struct sata_port *' Signed-off-by: Kim Phillips <kim.phillips@freescale.com>
839 lines
21 KiB
C
839 lines
21 KiB
C
/*
|
|
* Copyright (C) Excito Elektronik i Skåne AB, All rights reserved.
|
|
* Author: Tor Krill <tor@excito.com>
|
|
*
|
|
* 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 the Free Software Foundation; either version 2 of
|
|
* the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
|
|
* MA 02111-1307 USA
|
|
*
|
|
* This is a driver for Silicon Image sil3114 sata chip modelled on
|
|
* the ata_piix driver
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <pci.h>
|
|
#include <command.h>
|
|
#include <config.h>
|
|
#include <asm/byteorder.h>
|
|
#include <asm/io.h>
|
|
#include <ide.h>
|
|
#include <libata.h>
|
|
#include "sata_sil3114.h"
|
|
|
|
/* Convert sectorsize to wordsize */
|
|
#define ATA_SECTOR_WORDS (ATA_SECT_SIZE/2)
|
|
|
|
/* Forwards */
|
|
u8 sil3114_spin_up (int num);
|
|
u8 sil3114_spin_down (int num);
|
|
static int sata_bus_softreset (int num);
|
|
static void sata_identify (int num, int dev);
|
|
static u8 check_power_mode (int num);
|
|
static void sata_port (struct sata_ioports *ioport);
|
|
static void set_Feature_cmd (int num, int dev);
|
|
static u8 sata_busy_wait (struct sata_ioports *ioaddr, int bits,
|
|
unsigned int max, u8 usealtstatus);
|
|
static u8 sata_chk_status (struct sata_ioports *ioaddr, u8 usealtstatus);
|
|
static void msleep (int count);
|
|
|
|
static u32 iobase[6] = { 0, 0, 0, 0, 0, 0}; /* PCI BAR registers for device */
|
|
extern block_dev_desc_t sata_dev_desc[CONFIG_SYS_SATA_MAX_DEVICE];
|
|
|
|
static struct sata_port port[CONFIG_SYS_SATA_MAX_DEVICE];
|
|
|
|
static void output_data (struct sata_ioports *ioaddr, u16 * sect_buf, int words)
|
|
{
|
|
while (words--) {
|
|
__raw_writew (*sect_buf++, (void *)ioaddr->data_addr);
|
|
}
|
|
}
|
|
|
|
static int input_data (struct sata_ioports *ioaddr, u16 * sect_buf, int words)
|
|
{
|
|
while (words--) {
|
|
*sect_buf++ = __raw_readw ((void *)ioaddr->data_addr);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int sata_bus_softreset (int num)
|
|
{
|
|
u8 status = 0;
|
|
|
|
port[num].dev_mask = 1;
|
|
|
|
port[num].ctl_reg = 0x08; /*Default value of control reg */
|
|
writeb (port[num].ctl_reg, port[num].ioaddr.ctl_addr);
|
|
udelay (10);
|
|
writeb (port[num].ctl_reg | ATA_SRST, port[num].ioaddr.ctl_addr);
|
|
udelay (10);
|
|
writeb (port[num].ctl_reg, port[num].ioaddr.ctl_addr);
|
|
|
|
/* spec mandates ">= 2ms" before checking status.
|
|
* We wait 150ms, because that was the magic delay used for
|
|
* ATAPI devices in Hale Landis's ATADRVR, for the period of time
|
|
* between when the ATA command register is written, and then
|
|
* status is checked. Because waiting for "a while" before
|
|
* checking status is fine, post SRST, we perform this magic
|
|
* delay here as well.
|
|
*/
|
|
msleep (150);
|
|
status = sata_busy_wait (&port[num].ioaddr, ATA_BUSY, 300, 0);
|
|
while ((status & ATA_BUSY)) {
|
|
msleep (100);
|
|
status = sata_busy_wait (&port[num].ioaddr, ATA_BUSY, 3, 0);
|
|
}
|
|
|
|
if (status & ATA_BUSY) {
|
|
printf ("ata%u is slow to respond,plz be patient\n", num);
|
|
}
|
|
|
|
while ((status & ATA_BUSY)) {
|
|
msleep (100);
|
|
status = sata_chk_status (&port[num].ioaddr, 0);
|
|
}
|
|
|
|
if (status & ATA_BUSY) {
|
|
printf ("ata%u failed to respond : ", num);
|
|
printf ("bus reset failed\n");
|
|
port[num].dev_mask = 0;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void sata_identify (int num, int dev)
|
|
{
|
|
u8 cmd = 0, status = 0, devno = num;
|
|
u16 iobuf[ATA_SECTOR_WORDS];
|
|
u64 n_sectors = 0;
|
|
|
|
memset (iobuf, 0, sizeof (iobuf));
|
|
|
|
if (!(port[num].dev_mask & 0x01)) {
|
|
printf ("dev%d is not present on port#%d\n", dev, num);
|
|
return;
|
|
}
|
|
|
|
debug ("port=%d dev=%d\n", num, dev);
|
|
|
|
status = 0;
|
|
cmd = ATA_CMD_ID_ATA; /*Device Identify Command */
|
|
writeb (cmd, port[num].ioaddr.command_addr);
|
|
readb (port[num].ioaddr.altstatus_addr);
|
|
udelay (10);
|
|
|
|
status = sata_busy_wait (&port[num].ioaddr, ATA_BUSY, 1000, 0);
|
|
if (status & ATA_ERR) {
|
|
printf ("\ndevice not responding\n");
|
|
port[num].dev_mask &= ~0x01;
|
|
return;
|
|
}
|
|
|
|
input_data (&port[num].ioaddr, iobuf, ATA_SECTOR_WORDS);
|
|
|
|
ata_swap_buf_le16 (iobuf, ATA_SECTOR_WORDS);
|
|
|
|
debug ("Specific config: %x\n", iobuf[2]);
|
|
|
|
/* we require LBA and DMA support (bits 8 & 9 of word 49) */
|
|
if (!ata_id_has_dma (iobuf) || !ata_id_has_lba (iobuf)) {
|
|
debug ("ata%u: no dma/lba\n", num);
|
|
}
|
|
#ifdef DEBUG
|
|
ata_dump_id (iobuf);
|
|
#endif
|
|
n_sectors = ata_id_n_sectors (iobuf);
|
|
|
|
if (n_sectors == 0) {
|
|
port[num].dev_mask &= ~0x01;
|
|
return;
|
|
}
|
|
ata_id_c_string (iobuf, (unsigned char *)sata_dev_desc[devno].revision,
|
|
ATA_ID_FW_REV, sizeof (sata_dev_desc[devno].revision));
|
|
ata_id_c_string (iobuf, (unsigned char *)sata_dev_desc[devno].vendor,
|
|
ATA_ID_PROD, sizeof (sata_dev_desc[devno].vendor));
|
|
ata_id_c_string (iobuf, (unsigned char *)sata_dev_desc[devno].product,
|
|
ATA_ID_SERNO, sizeof (sata_dev_desc[devno].product));
|
|
|
|
/* TODO - atm we asume harddisk ie not removable */
|
|
sata_dev_desc[devno].removable = 0;
|
|
|
|
sata_dev_desc[devno].lba = (u32) n_sectors;
|
|
debug ("lba=0x%x\n", sata_dev_desc[devno].lba);
|
|
|
|
#ifdef CONFIG_LBA48
|
|
if (iobuf[83] & (1 << 10)) {
|
|
sata_dev_desc[devno].lba48 = 1;
|
|
} else {
|
|
sata_dev_desc[devno].lba48 = 0;
|
|
}
|
|
#endif
|
|
|
|
/* assuming HD */
|
|
sata_dev_desc[devno].type = DEV_TYPE_HARDDISK;
|
|
sata_dev_desc[devno].blksz = ATA_SECT_SIZE;
|
|
sata_dev_desc[devno].lun = 0; /* just to fill something in... */
|
|
}
|
|
|
|
static void set_Feature_cmd (int num, int dev)
|
|
{
|
|
u8 status = 0;
|
|
|
|
if (!(port[num].dev_mask & 0x01)) {
|
|
debug ("dev%d is not present on port#%d\n", dev, num);
|
|
return;
|
|
}
|
|
|
|
writeb (SETFEATURES_XFER, port[num].ioaddr.feature_addr);
|
|
writeb (XFER_PIO_4, port[num].ioaddr.nsect_addr);
|
|
writeb (0, port[num].ioaddr.lbal_addr);
|
|
writeb (0, port[num].ioaddr.lbam_addr);
|
|
writeb (0, port[num].ioaddr.lbah_addr);
|
|
|
|
writeb (ATA_DEVICE_OBS, port[num].ioaddr.device_addr);
|
|
writeb (ATA_CMD_SET_FEATURES, port[num].ioaddr.command_addr);
|
|
|
|
udelay (50);
|
|
msleep (150);
|
|
|
|
status = sata_busy_wait (&port[num].ioaddr, ATA_BUSY, 5000, 0);
|
|
if ((status & (ATA_BUSY | ATA_ERR))) {
|
|
printf ("Error : status 0x%02x\n", status);
|
|
port[num].dev_mask &= ~0x01;
|
|
}
|
|
}
|
|
|
|
u8 sil3114_spin_down (int num)
|
|
{
|
|
u8 status = 0;
|
|
|
|
debug ("Spin down disk\n");
|
|
|
|
if (!(port[num].dev_mask & 0x01)) {
|
|
debug ("Device ata%d is not present\n", num);
|
|
return 1;
|
|
}
|
|
|
|
if ((status = check_power_mode (num)) == 0x00) {
|
|
debug ("Already in standby\n");
|
|
return 0;
|
|
}
|
|
|
|
if (status == 0x01) {
|
|
printf ("Failed to check power mode on ata%d\n", num);
|
|
return 1;
|
|
}
|
|
|
|
if (!((status = sata_chk_status (&port[num].ioaddr, 0)) & ATA_DRDY)) {
|
|
printf ("Device ata%d not ready\n", num);
|
|
return 1;
|
|
}
|
|
|
|
writeb (0x00, port[num].ioaddr.feature_addr);
|
|
|
|
writeb (0x00, port[num].ioaddr.nsect_addr);
|
|
writeb (0x00, port[num].ioaddr.lbal_addr);
|
|
writeb (0x00, port[num].ioaddr.lbam_addr);
|
|
writeb (0x00, port[num].ioaddr.lbah_addr);
|
|
|
|
writeb (ATA_DEVICE_OBS, port[num].ioaddr.device_addr);
|
|
writeb (ATA_CMD_STANDBY, port[num].ioaddr.command_addr);
|
|
|
|
status = sata_busy_wait (&port[num].ioaddr, ATA_BUSY, 30000, 0);
|
|
if ((status & (ATA_BUSY | ATA_ERR))) {
|
|
printf ("Error waiting for disk spin down: status 0x%02x\n",
|
|
status);
|
|
port[num].dev_mask &= ~0x01;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
u8 sil3114_spin_up (int num)
|
|
{
|
|
u8 status = 0;
|
|
|
|
debug ("Spin up disk\n");
|
|
|
|
if (!(port[num].dev_mask & 0x01)) {
|
|
debug ("Device ata%d is not present\n", num);
|
|
return 1;
|
|
}
|
|
|
|
if ((status = check_power_mode (num)) != 0x00) {
|
|
if (status == 0x01) {
|
|
printf ("Failed to check power mode on ata%d\n", num);
|
|
return 1;
|
|
} else {
|
|
/* should be up and running already */
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (!((status = sata_chk_status (&port[num].ioaddr, 0)) & ATA_DRDY)) {
|
|
printf ("Device ata%d not ready\n", num);
|
|
return 1;
|
|
}
|
|
|
|
debug ("Stautus of device check: %d\n", status);
|
|
|
|
writeb (0x00, port[num].ioaddr.feature_addr);
|
|
|
|
writeb (0x00, port[num].ioaddr.nsect_addr);
|
|
writeb (0x00, port[num].ioaddr.lbal_addr);
|
|
writeb (0x00, port[num].ioaddr.lbam_addr);
|
|
writeb (0x00, port[num].ioaddr.lbah_addr);
|
|
|
|
writeb (ATA_DEVICE_OBS, port[num].ioaddr.device_addr);
|
|
writeb (ATA_CMD_IDLE, port[num].ioaddr.command_addr);
|
|
|
|
status = sata_busy_wait (&port[num].ioaddr, ATA_BUSY, 30000, 0);
|
|
if ((status & (ATA_BUSY | ATA_ERR))) {
|
|
printf ("Error waiting for disk spin up: status 0x%02x\n",
|
|
status);
|
|
port[num].dev_mask &= ~0x01;
|
|
return 1;
|
|
}
|
|
|
|
/* Wait for disk to enter Active state */
|
|
do {
|
|
msleep (10);
|
|
status = check_power_mode (num);
|
|
} while ((status == 0x00) || (status == 0x80));
|
|
|
|
if (status == 0x01) {
|
|
printf ("Falied waiting for disk to spin up\n");
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Return value is not the usual here
|
|
* 0x00 - Device stand by
|
|
* 0x01 - Operation failed
|
|
* 0x80 - Device idle
|
|
* 0xff - Device active
|
|
*/
|
|
static u8 check_power_mode (int num)
|
|
{
|
|
u8 status = 0;
|
|
u8 res = 0;
|
|
if (!(port[num].dev_mask & 0x01)) {
|
|
debug ("Device ata%d is not present\n", num);
|
|
return 1;
|
|
}
|
|
|
|
if (!(sata_chk_status (&port[num].ioaddr, 0) & ATA_DRDY)) {
|
|
printf ("Device ata%d not ready\n", num);
|
|
return 1;
|
|
}
|
|
|
|
writeb (0, port[num].ioaddr.feature_addr);
|
|
writeb (0, port[num].ioaddr.nsect_addr);
|
|
writeb (0, port[num].ioaddr.lbal_addr);
|
|
writeb (0, port[num].ioaddr.lbam_addr);
|
|
writeb (0, port[num].ioaddr.lbah_addr);
|
|
|
|
writeb (ATA_DEVICE_OBS, port[num].ioaddr.device_addr);
|
|
writeb (ATA_CMD_CHK_POWER, port[num].ioaddr.command_addr);
|
|
|
|
status = sata_busy_wait (&port[num].ioaddr, ATA_BUSY, 5000, 0);
|
|
if ((status & (ATA_BUSY | ATA_ERR))) {
|
|
printf
|
|
("Error waiting for check power mode complete : status 0x%02x\n",
|
|
status);
|
|
port[num].dev_mask &= ~0x01;
|
|
return 1;
|
|
}
|
|
res = readb (port[num].ioaddr.nsect_addr);
|
|
debug ("Check powermode: %d\n", res);
|
|
return res;
|
|
|
|
}
|
|
|
|
static void sata_port (struct sata_ioports *ioport)
|
|
{
|
|
ioport->data_addr = ioport->cmd_addr + ATA_REG_DATA;
|
|
ioport->error_addr = ioport->cmd_addr + ATA_REG_ERR;
|
|
ioport->feature_addr = ioport->cmd_addr + ATA_REG_FEATURE;
|
|
ioport->nsect_addr = ioport->cmd_addr + ATA_REG_NSECT;
|
|
ioport->lbal_addr = ioport->cmd_addr + ATA_REG_LBAL;
|
|
ioport->lbam_addr = ioport->cmd_addr + ATA_REG_LBAM;
|
|
ioport->lbah_addr = ioport->cmd_addr + ATA_REG_LBAH;
|
|
ioport->device_addr = ioport->cmd_addr + ATA_REG_DEVICE;
|
|
ioport->status_addr = ioport->cmd_addr + ATA_REG_STATUS;
|
|
ioport->command_addr = ioport->cmd_addr + ATA_REG_CMD;
|
|
}
|
|
|
|
static u8 wait_for_irq (int num, unsigned int max)
|
|
{
|
|
|
|
u32 port = iobase[5];
|
|
switch (num) {
|
|
case 0:
|
|
port += VND_TF_CNST_CH0;
|
|
break;
|
|
case 1:
|
|
port += VND_TF_CNST_CH1;
|
|
break;
|
|
case 2:
|
|
port += VND_TF_CNST_CH2;
|
|
break;
|
|
case 3:
|
|
port += VND_TF_CNST_CH3;
|
|
break;
|
|
default:
|
|
return 1;
|
|
}
|
|
|
|
do {
|
|
if (readl (port) & VND_TF_CNST_INTST) {
|
|
break;
|
|
}
|
|
udelay (1000);
|
|
max--;
|
|
} while ((max > 0));
|
|
|
|
return (max == 0);
|
|
}
|
|
|
|
static u8 sata_busy_wait (struct sata_ioports *ioaddr, int bits,
|
|
unsigned int max, u8 usealtstatus)
|
|
{
|
|
u8 status;
|
|
|
|
do {
|
|
if (!((status = sata_chk_status (ioaddr, usealtstatus)) & bits)) {
|
|
break;
|
|
}
|
|
udelay (1000);
|
|
max--;
|
|
} while ((status & bits) && (max > 0));
|
|
|
|
return status;
|
|
}
|
|
|
|
static u8 sata_chk_status (struct sata_ioports *ioaddr, u8 usealtstatus)
|
|
{
|
|
if (!usealtstatus) {
|
|
return readb (ioaddr->status_addr);
|
|
} else {
|
|
return readb (ioaddr->altstatus_addr);
|
|
}
|
|
}
|
|
|
|
static void msleep (int count)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < count; i++)
|
|
udelay (1000);
|
|
}
|
|
|
|
/* Read up to 255 sectors
|
|
*
|
|
* Returns sectors read
|
|
*/
|
|
static u8 do_one_read (int device, ulong block, u8 blkcnt, u16 * buff,
|
|
uchar lba48)
|
|
{
|
|
|
|
u8 sr = 0;
|
|
u8 status;
|
|
u64 blknr = (u64) block;
|
|
|
|
if (!(sata_chk_status (&port[device].ioaddr, 0) & ATA_DRDY)) {
|
|
printf ("Device ata%d not ready\n", device);
|
|
return 0;
|
|
}
|
|
|
|
/* Set up transfer */
|
|
#ifdef CONFIG_LBA48
|
|
if (lba48) {
|
|
/* write high bits */
|
|
writeb (0, port[device].ioaddr.nsect_addr);
|
|
writeb ((blknr >> 24) & 0xFF, port[device].ioaddr.lbal_addr);
|
|
writeb ((blknr >> 32) & 0xFF, port[device].ioaddr.lbam_addr);
|
|
writeb ((blknr >> 40) & 0xFF, port[device].ioaddr.lbah_addr);
|
|
}
|
|
#endif
|
|
writeb (blkcnt, port[device].ioaddr.nsect_addr);
|
|
writeb (((blknr) >> 0) & 0xFF, port[device].ioaddr.lbal_addr);
|
|
writeb ((blknr >> 8) & 0xFF, port[device].ioaddr.lbam_addr);
|
|
writeb ((blknr >> 16) & 0xFF, port[device].ioaddr.lbah_addr);
|
|
|
|
#ifdef CONFIG_LBA48
|
|
if (lba48) {
|
|
writeb (ATA_LBA, port[device].ioaddr.device_addr);
|
|
writeb (ATA_CMD_PIO_READ_EXT, port[device].ioaddr.command_addr);
|
|
} else
|
|
#endif
|
|
{
|
|
writeb (ATA_LBA | ((blknr >> 24) & 0xF),
|
|
port[device].ioaddr.device_addr);
|
|
writeb (ATA_CMD_PIO_READ, port[device].ioaddr.command_addr);
|
|
}
|
|
|
|
status = sata_busy_wait (&port[device].ioaddr, ATA_BUSY, 10000, 1);
|
|
|
|
if (status & ATA_BUSY) {
|
|
u8 err = 0;
|
|
|
|
printf ("Device %d not responding status %d\n", device, status);
|
|
err = readb (port[device].ioaddr.error_addr);
|
|
printf ("Error reg = 0x%x\n", err);
|
|
|
|
return (sr);
|
|
}
|
|
while (blkcnt--) {
|
|
|
|
if (wait_for_irq (device, 500)) {
|
|
printf ("ata%u irq failed\n", device);
|
|
return sr;
|
|
}
|
|
|
|
status = sata_chk_status (&port[device].ioaddr, 0);
|
|
if (status & ATA_ERR) {
|
|
printf ("ata%u error %d\n", device,
|
|
readb (port[device].ioaddr.error_addr));
|
|
return sr;
|
|
}
|
|
/* Read one sector */
|
|
input_data (&port[device].ioaddr, buff, ATA_SECTOR_WORDS);
|
|
buff += ATA_SECTOR_WORDS;
|
|
sr++;
|
|
|
|
}
|
|
return sr;
|
|
}
|
|
|
|
ulong sata_read (int device, ulong block, lbaint_t blkcnt, void *buff)
|
|
{
|
|
ulong n = 0, sread;
|
|
u16 *buffer = (u16 *) buff;
|
|
u8 status = 0;
|
|
u64 blknr = (u64) block;
|
|
unsigned char lba48 = 0;
|
|
|
|
#ifdef CONFIG_LBA48
|
|
if (blknr > 0xfffffff) {
|
|
if (!sata_dev_desc[device].lba48) {
|
|
printf ("Drive doesn't support 48-bit addressing\n");
|
|
return 0;
|
|
}
|
|
/* more than 28 bits used, use 48bit mode */
|
|
lba48 = 1;
|
|
}
|
|
#endif
|
|
|
|
while (blkcnt > 0) {
|
|
|
|
if (blkcnt > 255) {
|
|
sread = 255;
|
|
} else {
|
|
sread = blkcnt;
|
|
}
|
|
|
|
status = do_one_read (device, blknr, sread, buffer, lba48);
|
|
if (status != sread) {
|
|
printf ("Read failed\n");
|
|
return n;
|
|
}
|
|
|
|
blkcnt -= sread;
|
|
blknr += sread;
|
|
n += sread;
|
|
buffer += sread * ATA_SECTOR_WORDS;
|
|
}
|
|
return n;
|
|
}
|
|
|
|
ulong sata_write (int device, ulong block, lbaint_t blkcnt, const void *buff)
|
|
{
|
|
ulong n = 0;
|
|
u16 *buffer = (u16 *) buff;
|
|
unsigned char status = 0, num = 0;
|
|
u64 blknr = (u64) block;
|
|
#ifdef CONFIG_LBA48
|
|
unsigned char lba48 = 0;
|
|
|
|
if (blknr > 0xfffffff) {
|
|
if (!sata_dev_desc[device].lba48) {
|
|
printf ("Drive doesn't support 48-bit addressing\n");
|
|
return 0;
|
|
}
|
|
/* more than 28 bits used, use 48bit mode */
|
|
lba48 = 1;
|
|
}
|
|
#endif
|
|
/*Port Number */
|
|
num = device;
|
|
|
|
while (blkcnt-- > 0) {
|
|
status = sata_busy_wait (&port[num].ioaddr, ATA_BUSY, 500, 0);
|
|
if (status & ATA_BUSY) {
|
|
printf ("ata%u failed to respond\n", port[num].port_no);
|
|
return n;
|
|
}
|
|
#ifdef CONFIG_LBA48
|
|
if (lba48) {
|
|
/* write high bits */
|
|
writeb (0, port[num].ioaddr.nsect_addr);
|
|
writeb ((blknr >> 24) & 0xFF,
|
|
port[num].ioaddr.lbal_addr);
|
|
writeb ((blknr >> 32) & 0xFF,
|
|
port[num].ioaddr.lbam_addr);
|
|
writeb ((blknr >> 40) & 0xFF,
|
|
port[num].ioaddr.lbah_addr);
|
|
}
|
|
#endif
|
|
writeb (1, port[num].ioaddr.nsect_addr);
|
|
writeb ((blknr >> 0) & 0xFF, port[num].ioaddr.lbal_addr);
|
|
writeb ((blknr >> 8) & 0xFF, port[num].ioaddr.lbam_addr);
|
|
writeb ((blknr >> 16) & 0xFF, port[num].ioaddr.lbah_addr);
|
|
#ifdef CONFIG_LBA48
|
|
if (lba48) {
|
|
writeb (ATA_LBA, port[num].ioaddr.device_addr);
|
|
writeb (ATA_CMD_PIO_WRITE_EXT, port[num].ioaddr.command_addr);
|
|
} else
|
|
#endif
|
|
{
|
|
writeb (ATA_LBA | ((blknr >> 24) & 0xF),
|
|
port[num].ioaddr.device_addr);
|
|
writeb (ATA_CMD_PIO_WRITE, port[num].ioaddr.command_addr);
|
|
}
|
|
|
|
msleep (50);
|
|
/*may take up to 4 sec */
|
|
status = sata_busy_wait (&port[num].ioaddr, ATA_BUSY, 4000, 0);
|
|
if ((status & (ATA_DRQ | ATA_BUSY | ATA_ERR)) != ATA_DRQ) {
|
|
printf ("Error no DRQ dev %d blk %ld: sts 0x%02x\n",
|
|
device, (ulong) blknr, status);
|
|
return (n);
|
|
}
|
|
|
|
output_data (&port[num].ioaddr, buffer, ATA_SECTOR_WORDS);
|
|
readb (port[num].ioaddr.altstatus_addr);
|
|
udelay (50);
|
|
|
|
++n;
|
|
++blknr;
|
|
buffer += ATA_SECTOR_WORDS;
|
|
}
|
|
return n;
|
|
}
|
|
|
|
/* Driver implementation */
|
|
static u8 sil_get_device_cache_line (pci_dev_t pdev)
|
|
{
|
|
u8 cache_line = 0;
|
|
pci_read_config_byte (pdev, PCI_CACHE_LINE_SIZE, &cache_line);
|
|
return cache_line;
|
|
}
|
|
|
|
int init_sata (int dev)
|
|
{
|
|
static u8 init_done = 0;
|
|
static int res = 1;
|
|
pci_dev_t devno;
|
|
u8 cls = 0;
|
|
u16 cmd = 0;
|
|
u32 sconf = 0;
|
|
|
|
if (init_done) {
|
|
return res;
|
|
}
|
|
|
|
init_done = 1;
|
|
|
|
if ((devno = pci_find_device (SIL_VEND_ID, SIL3114_DEVICE_ID, 0)) == -1) {
|
|
res = 1;
|
|
return res;
|
|
}
|
|
|
|
/* Read out all BARs, even though we only use MMIO from BAR5 */
|
|
pci_read_config_dword (devno, PCI_BASE_ADDRESS_0, &iobase[0]);
|
|
pci_read_config_dword (devno, PCI_BASE_ADDRESS_1, &iobase[1]);
|
|
pci_read_config_dword (devno, PCI_BASE_ADDRESS_2, &iobase[2]);
|
|
pci_read_config_dword (devno, PCI_BASE_ADDRESS_3, &iobase[3]);
|
|
pci_read_config_dword (devno, PCI_BASE_ADDRESS_4, &iobase[4]);
|
|
pci_read_config_dword (devno, PCI_BASE_ADDRESS_5, &iobase[5]);
|
|
|
|
if ((iobase[0] == 0xFFFFFFFF) || (iobase[1] == 0xFFFFFFFF) ||
|
|
(iobase[2] == 0xFFFFFFFF) || (iobase[3] == 0xFFFFFFFF) ||
|
|
(iobase[4] == 0xFFFFFFFF) || (iobase[5] == 0xFFFFFFFF)) {
|
|
printf ("Error no base addr for SATA controller\n");
|
|
res = 1;
|
|
return res;
|
|
}
|
|
|
|
/* mask off unused bits */
|
|
iobase[0] &= 0xfffffffc;
|
|
iobase[1] &= 0xfffffff8;
|
|
iobase[2] &= 0xfffffffc;
|
|
iobase[3] &= 0xfffffff8;
|
|
iobase[4] &= 0xfffffff0;
|
|
iobase[5] &= 0xfffffc00;
|
|
|
|
/* from sata_sil in Linux kernel */
|
|
cls = sil_get_device_cache_line (devno);
|
|
if (cls) {
|
|
cls >>= 3;
|
|
cls++; /* cls = (line_size/8)+1 */
|
|
writel (cls << 8 | cls, iobase[5] + VND_FIFOCFG_CH0);
|
|
writel (cls << 8 | cls, iobase[5] + VND_FIFOCFG_CH1);
|
|
writel (cls << 8 | cls, iobase[5] + VND_FIFOCFG_CH2);
|
|
writel (cls << 8 | cls, iobase[5] + VND_FIFOCFG_CH3);
|
|
} else {
|
|
printf ("Cache line not set. Driver may not function\n");
|
|
}
|
|
|
|
/* Enable operation */
|
|
pci_read_config_word (devno, PCI_COMMAND, &cmd);
|
|
cmd |= PCI_COMMAND_MASTER | PCI_COMMAND_IO | PCI_COMMAND_MEMORY;
|
|
pci_write_config_word (devno, PCI_COMMAND, cmd);
|
|
|
|
/* Disable interrupt usage */
|
|
pci_read_config_dword (devno, VND_SYSCONFSTAT, &sconf);
|
|
sconf |= (VND_SYSCONFSTAT_CHN_0_INTBLOCK | VND_SYSCONFSTAT_CHN_1_INTBLOCK);
|
|
pci_write_config_dword (devno, VND_SYSCONFSTAT, sconf);
|
|
|
|
res = 0;
|
|
return res;
|
|
}
|
|
|
|
/* Check if device is connected to port */
|
|
int sata_bus_probe (int portno)
|
|
{
|
|
u32 port = iobase[5];
|
|
u32 val;
|
|
switch (portno) {
|
|
case 0:
|
|
port += VND_SSTATUS_CH0;
|
|
break;
|
|
case 1:
|
|
port += VND_SSTATUS_CH1;
|
|
break;
|
|
case 2:
|
|
port += VND_SSTATUS_CH2;
|
|
break;
|
|
case 3:
|
|
port += VND_SSTATUS_CH3;
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
val = readl (port);
|
|
if ((val & SATA_DET_PRES) == SATA_DET_PRES) {
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int sata_phy_reset (int portno)
|
|
{
|
|
u32 port = iobase[5];
|
|
u32 val;
|
|
switch (portno) {
|
|
case 0:
|
|
port += VND_SCONTROL_CH0;
|
|
break;
|
|
case 1:
|
|
port += VND_SCONTROL_CH1;
|
|
break;
|
|
case 2:
|
|
port += VND_SCONTROL_CH2;
|
|
break;
|
|
case 3:
|
|
port += VND_SCONTROL_CH3;
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
val = readl (port);
|
|
writel (val | SATA_SC_DET_RST, port);
|
|
msleep (150);
|
|
writel (val & ~SATA_SC_DET_RST, port);
|
|
return 0;
|
|
}
|
|
|
|
int scan_sata (int dev)
|
|
{
|
|
/* A bit brain dead, but the code has a legacy */
|
|
switch (dev) {
|
|
case 0:
|
|
port[0].port_no = 0;
|
|
port[0].ioaddr.cmd_addr = iobase[5] + VND_TF0_CH0;
|
|
port[0].ioaddr.altstatus_addr = port[0].ioaddr.ctl_addr =
|
|
(iobase[5] + VND_TF2_CH0) | ATA_PCI_CTL_OFS;
|
|
port[0].ioaddr.bmdma_addr = iobase[5] + VND_BMDMA_CH0;
|
|
break;
|
|
case 1:
|
|
port[1].port_no = 0;
|
|
port[1].ioaddr.cmd_addr = iobase[5] + VND_TF0_CH1;
|
|
port[1].ioaddr.altstatus_addr = port[1].ioaddr.ctl_addr =
|
|
(iobase[5] + VND_TF2_CH1) | ATA_PCI_CTL_OFS;
|
|
port[1].ioaddr.bmdma_addr = iobase[5] + VND_BMDMA_CH1;
|
|
break;
|
|
case 2:
|
|
port[2].port_no = 0;
|
|
port[2].ioaddr.cmd_addr = iobase[5] + VND_TF0_CH2;
|
|
port[2].ioaddr.altstatus_addr = port[2].ioaddr.ctl_addr =
|
|
(iobase[5] + VND_TF2_CH2) | ATA_PCI_CTL_OFS;
|
|
port[2].ioaddr.bmdma_addr = iobase[5] + VND_BMDMA_CH2;
|
|
break;
|
|
case 3:
|
|
port[3].port_no = 0;
|
|
port[3].ioaddr.cmd_addr = iobase[5] + VND_TF0_CH3;
|
|
port[3].ioaddr.altstatus_addr = port[3].ioaddr.ctl_addr =
|
|
(iobase[5] + VND_TF2_CH3) | ATA_PCI_CTL_OFS;
|
|
port[3].ioaddr.bmdma_addr = iobase[5] + VND_BMDMA_CH3;
|
|
break;
|
|
default:
|
|
printf ("Tried to scan unknown port: ata%d\n", dev);
|
|
return 1;
|
|
}
|
|
|
|
/* Initialize other registers */
|
|
sata_port (&port[dev].ioaddr);
|
|
|
|
/* Check for attached device */
|
|
if (!sata_bus_probe (dev)) {
|
|
port[dev].port_state = 0;
|
|
debug ("SATA#%d port is not present\n", dev);
|
|
} else {
|
|
debug ("SATA#%d port is present\n", dev);
|
|
if (sata_bus_softreset (dev)) {
|
|
/* soft reset failed, try a hard one */
|
|
sata_phy_reset (dev);
|
|
if (sata_bus_softreset (dev)) {
|
|
port[dev].port_state = 0;
|
|
} else {
|
|
port[dev].port_state = 1;
|
|
}
|
|
} else {
|
|
port[dev].port_state = 1;
|
|
}
|
|
}
|
|
if (port[dev].port_state == 1) {
|
|
/* Probe device and set xfer mode */
|
|
sata_identify (dev, 0);
|
|
set_Feature_cmd (dev, 0);
|
|
}
|
|
|
|
return 0;
|
|
}
|