mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-16 17:58:23 +00:00
0472fbfd32
log2 of the device block size serves as the shift value used to calculate the block number to read in file systems when implementing avaiable block sizes. It is needed quite often in file systems thus it is pre-calculated and stored in the block device descriptor. Signed-off-by: Egbert Eich <eich@suse.com>
725 lines
18 KiB
C
725 lines
18 KiB
C
/*
|
|
* Copyright (C) Procsys. All rights reserved.
|
|
* Author: Mushtaq Khan <mushtaq_k@procsys.com>
|
|
* <mushtaqk_921@yahoo.co.in>
|
|
*
|
|
* 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
|
|
*
|
|
* with the reference to ata_piix driver in kernel 2.4.32
|
|
*/
|
|
|
|
/*
|
|
* This file contains SATA controller and SATA drive initialization functions
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <asm/io.h>
|
|
#include <pci.h>
|
|
#include <command.h>
|
|
#include <config.h>
|
|
#include <asm/byteorder.h>
|
|
#include <part.h>
|
|
#include <ide.h>
|
|
#include <ata.h>
|
|
#include <sata.h>
|
|
|
|
#define DEBUG_SATA 0 /* For debug prints set DEBUG_SATA to 1 */
|
|
|
|
#define SATA_DECL
|
|
#define DRV_DECL /* For file specific declarations */
|
|
#include "ata_piix.h"
|
|
|
|
/* Macros realted to PCI */
|
|
#define PCI_SATA_BUS 0x00
|
|
#define PCI_SATA_DEV 0x1f
|
|
#define PCI_SATA_FUNC 0x02
|
|
|
|
#define PCI_SATA_BASE1 0x10
|
|
#define PCI_SATA_BASE2 0x14
|
|
#define PCI_SATA_BASE3 0x18
|
|
#define PCI_SATA_BASE4 0x1c
|
|
#define PCI_SATA_BASE5 0x20
|
|
#define PCI_PMR 0x90
|
|
#define PCI_PI 0x09
|
|
#define PCI_PCS 0x92
|
|
#define PCI_DMA_CTL 0x48
|
|
|
|
#define PORT_PRESENT (1<<0)
|
|
#define PORT_ENABLED (1<<4)
|
|
|
|
u32 bdf;
|
|
u32 iobase1; /* Primary cmd block */
|
|
u32 iobase2; /* Primary ctl block */
|
|
u32 iobase3; /* Sec cmd block */
|
|
u32 iobase4; /* sec ctl block */
|
|
u32 iobase5; /* BMDMA*/
|
|
|
|
int pci_sata_init(void)
|
|
{
|
|
u32 bus = PCI_SATA_BUS;
|
|
u32 dev = PCI_SATA_DEV;
|
|
u32 fun = PCI_SATA_FUNC;
|
|
u16 cmd = 0;
|
|
u8 lat = 0, pcibios_max_latency = 0xff;
|
|
u8 pmr; /* Port mapping reg */
|
|
u8 pi; /* Prgming Interface reg */
|
|
|
|
bdf = PCI_BDF(bus, dev, fun);
|
|
pci_read_config_dword(bdf, PCI_SATA_BASE1, &iobase1);
|
|
pci_read_config_dword(bdf, PCI_SATA_BASE2, &iobase2);
|
|
pci_read_config_dword(bdf, PCI_SATA_BASE3, &iobase3);
|
|
pci_read_config_dword(bdf, PCI_SATA_BASE4, &iobase4);
|
|
pci_read_config_dword(bdf, PCI_SATA_BASE5, &iobase5);
|
|
|
|
if ((iobase1 == 0xFFFFFFFF) || (iobase2 == 0xFFFFFFFF) ||
|
|
(iobase3 == 0xFFFFFFFF) || (iobase4 == 0xFFFFFFFF) ||
|
|
(iobase5 == 0xFFFFFFFF)) {
|
|
/* ERROR */
|
|
printf("error no base addr for SATA controller\n");
|
|
return 1;
|
|
}
|
|
|
|
iobase1 &= 0xFFFFFFFE;
|
|
iobase2 &= 0xFFFFFFFE;
|
|
iobase3 &= 0xFFFFFFFE;
|
|
iobase4 &= 0xFFFFFFFE;
|
|
iobase5 &= 0xFFFFFFFE;
|
|
|
|
/* check for mode */
|
|
pci_read_config_byte(bdf, PCI_PMR, &pmr);
|
|
if (pmr > 1) {
|
|
puts("combined mode not supported\n");
|
|
return 1;
|
|
}
|
|
|
|
pci_read_config_byte(bdf, PCI_PI, &pi);
|
|
if ((pi & 0x05) != 0x05) {
|
|
puts("Sata is in Legacy mode\n");
|
|
return 1;
|
|
} else
|
|
puts("sata is in Native mode\n");
|
|
|
|
/* MASTER CFG AND IO CFG */
|
|
pci_read_config_word(bdf, PCI_COMMAND, &cmd);
|
|
cmd |= PCI_COMMAND_MASTER | PCI_COMMAND_IO;
|
|
pci_write_config_word(bdf, PCI_COMMAND, cmd);
|
|
pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat);
|
|
|
|
if (lat < 16)
|
|
lat = (64 <= pcibios_max_latency) ? 64 : pcibios_max_latency;
|
|
else if (lat > pcibios_max_latency)
|
|
lat = pcibios_max_latency;
|
|
pci_write_config_byte(dev, PCI_LATENCY_TIMER, lat);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int sata_bus_probe(int port_no)
|
|
{
|
|
int orig_mask, mask;
|
|
u16 pcs;
|
|
|
|
mask = (PORT_PRESENT << port_no);
|
|
pci_read_config_word(bdf, PCI_PCS, &pcs);
|
|
orig_mask = (int) pcs & 0xff;
|
|
if ((orig_mask & mask) != mask)
|
|
return 0;
|
|
else
|
|
return 1;
|
|
}
|
|
|
|
int init_sata(int dev)
|
|
{
|
|
static int done;
|
|
u8 i, rv = 0;
|
|
|
|
if (!done)
|
|
done = 1;
|
|
else
|
|
return 0;
|
|
|
|
rv = pci_sata_init();
|
|
if (rv == 1) {
|
|
puts("pci initialization failed\n");
|
|
return 1;
|
|
}
|
|
|
|
port[0].port_no = 0;
|
|
port[0].ioaddr.cmd_addr = iobase1;
|
|
port[0].ioaddr.altstatus_addr = port[0].ioaddr.ctl_addr =
|
|
iobase2 | ATA_PCI_CTL_OFS;
|
|
port[0].ioaddr.bmdma_addr = iobase5;
|
|
|
|
port[1].port_no = 1;
|
|
port[1].ioaddr.cmd_addr = iobase3;
|
|
port[1].ioaddr.altstatus_addr = port[1].ioaddr.ctl_addr =
|
|
iobase4 | ATA_PCI_CTL_OFS;
|
|
port[1].ioaddr.bmdma_addr = iobase5 + 0x8;
|
|
|
|
for (i = 0; i < CONFIG_SYS_SATA_MAXBUS; i++)
|
|
sata_port(&port[i].ioaddr);
|
|
|
|
for (i = 0; i < CONFIG_SYS_SATA_MAXBUS; i++) {
|
|
if (!(sata_bus_probe(i))) {
|
|
port[i].port_state = 0;
|
|
printf("SATA#%d port is not present\n", i);
|
|
} else {
|
|
printf("SATA#%d port is present\n", i);
|
|
if (sata_bus_softreset(i))
|
|
port[i].port_state = 0;
|
|
else
|
|
port[i].port_state = 1;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < CONFIG_SYS_SATA_MAXBUS; i++) {
|
|
u8 j, devno;
|
|
|
|
if (port[i].port_state == 0)
|
|
continue;
|
|
for (j = 0; j < CONFIG_SYS_SATA_DEVS_PER_BUS; j++) {
|
|
sata_identify(i, j);
|
|
set_Feature_cmd(i, j);
|
|
devno = i * CONFIG_SYS_SATA_DEVS_PER_BUS + j;
|
|
if ((sata_dev_desc[devno].lba > 0) &&
|
|
(sata_dev_desc[devno].blksz > 0)) {
|
|
dev_print(&sata_dev_desc[devno]);
|
|
/* initialize partition type */
|
|
init_part(&sata_dev_desc[devno]);
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static inline u8 sata_inb(unsigned long ioaddr)
|
|
{
|
|
return inb(ioaddr);
|
|
}
|
|
|
|
static inline void sata_outb(unsigned char val, unsigned long ioaddr)
|
|
{
|
|
outb(val, ioaddr);
|
|
}
|
|
|
|
static void output_data(struct sata_ioports *ioaddr, ulong * sect_buf,
|
|
int words)
|
|
{
|
|
outsw(ioaddr->data_addr, sect_buf, words << 1);
|
|
}
|
|
|
|
static int input_data(struct sata_ioports *ioaddr, ulong * sect_buf, int words)
|
|
{
|
|
insw(ioaddr->data_addr, sect_buf, words << 1);
|
|
return 0;
|
|
}
|
|
|
|
static void sata_cpy(unsigned char *dst, unsigned char *src, unsigned int len)
|
|
{
|
|
unsigned char *end, *last;
|
|
|
|
last = dst;
|
|
end = src + len - 1;
|
|
|
|
/* reserve space for '\0' */
|
|
if (len < 2)
|
|
goto OUT;
|
|
|
|
/* skip leading white space */
|
|
while ((*src) && (src < end) && (*src == ' '))
|
|
++src;
|
|
|
|
/* copy string, omitting trailing white space */
|
|
while ((*src) && (src < end)) {
|
|
*dst++ = *src;
|
|
if (*src++ != ' ')
|
|
last = dst;
|
|
}
|
|
OUT:
|
|
*last = '\0';
|
|
}
|
|
|
|
int sata_bus_softreset(int num)
|
|
{
|
|
u8 dev = 0, status = 0, i;
|
|
|
|
port[num].dev_mask = 0;
|
|
|
|
for (i = 0; i < CONFIG_SYS_SATA_DEVS_PER_BUS; i++) {
|
|
if (!(sata_devchk(&port[num].ioaddr, i))) {
|
|
debug("dev_chk failed for dev#%d\n", i);
|
|
} else {
|
|
port[num].dev_mask |= (1 << i);
|
|
debug("dev_chk passed for dev#%d\n", i);
|
|
}
|
|
}
|
|
|
|
if (!(port[num].dev_mask)) {
|
|
printf("no devices on port%d\n", num);
|
|
return 1;
|
|
}
|
|
|
|
dev_select(&port[num].ioaddr, dev);
|
|
|
|
port[num].ctl_reg = 0x08; /* Default value of control reg */
|
|
sata_outb(port[num].ctl_reg, port[num].ioaddr.ctl_addr);
|
|
udelay(10);
|
|
sata_outb(port[num].ctl_reg | ATA_SRST, port[num].ioaddr.ctl_addr);
|
|
udelay(10);
|
|
sata_outb(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.
|
|
*/
|
|
mdelay(150);
|
|
status = sata_busy_wait(&port[num].ioaddr, ATA_BUSY, 300);
|
|
while ((status & ATA_BUSY)) {
|
|
mdelay(100);
|
|
status = sata_busy_wait(&port[num].ioaddr, ATA_BUSY, 3);
|
|
}
|
|
|
|
if (status & ATA_BUSY)
|
|
printf("ata%u is slow to respond,plz be patient\n", num);
|
|
|
|
while ((status & ATA_BUSY)) {
|
|
mdelay(100);
|
|
status = sata_chk_status(&port[num].ioaddr);
|
|
}
|
|
|
|
if (status & ATA_BUSY) {
|
|
printf("ata%u failed to respond : bus reset failed\n", num);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void sata_identify(int num, int dev)
|
|
{
|
|
u8 cmd = 0, status = 0;
|
|
u8 devno = num * CONFIG_SYS_SATA_DEVS_PER_BUS + dev;
|
|
u16 iobuf[ATA_SECT_SIZE];
|
|
u64 n_sectors = 0;
|
|
u8 mask = 0;
|
|
|
|
memset(iobuf, 0, sizeof(iobuf));
|
|
hd_driveid_t *iop = (hd_driveid_t *) iobuf;
|
|
|
|
if (dev == 0)
|
|
mask = 0x01;
|
|
else
|
|
mask = 0x02;
|
|
|
|
if (!(port[num].dev_mask & mask)) {
|
|
printf("dev%d is not present on port#%d\n", dev, num);
|
|
return;
|
|
}
|
|
|
|
printf("port=%d dev=%d\n", num, dev);
|
|
|
|
dev_select(&port[num].ioaddr, dev);
|
|
|
|
status = 0;
|
|
cmd = ATA_CMD_IDENT; /* Device Identify Command */
|
|
sata_outb(cmd, port[num].ioaddr.command_addr);
|
|
sata_inb(port[num].ioaddr.altstatus_addr);
|
|
udelay(10);
|
|
|
|
status = sata_busy_wait(&port[num].ioaddr, ATA_BUSY, 1000);
|
|
if (status & ATA_ERR) {
|
|
puts("\ndevice not responding\n");
|
|
port[num].dev_mask &= ~mask;
|
|
return;
|
|
}
|
|
|
|
input_data(&port[num].ioaddr, (ulong *) iobuf, ATA_SECTORWORDS);
|
|
|
|
debug("\nata%u: dev %u cfg 49:%04x 82:%04x 83:%04x 84:%04x85:%04x"
|
|
"86:%04x" "87:%04x 88:%04x\n", num, dev, iobuf[49],
|
|
iobuf[82], iobuf[83], iobuf[84], iobuf[85], iobuf[86],
|
|
iobuf[87], iobuf[88]);
|
|
|
|
/* 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);
|
|
ata_dump_id(iobuf);
|
|
|
|
if (ata_id_has_lba48(iobuf))
|
|
n_sectors = ata_id_u64(iobuf, 100);
|
|
else
|
|
n_sectors = ata_id_u32(iobuf, 60);
|
|
debug("no. of sectors %u\n", ata_id_u64(iobuf, 100));
|
|
debug("no. of sectors %u\n", ata_id_u32(iobuf, 60));
|
|
|
|
if (n_sectors == 0) {
|
|
port[num].dev_mask &= ~mask;
|
|
return;
|
|
}
|
|
|
|
sata_cpy((unsigned char *)sata_dev_desc[devno].revision, iop->fw_rev,
|
|
sizeof(sata_dev_desc[devno].revision));
|
|
sata_cpy((unsigned char *)sata_dev_desc[devno].vendor, iop->model,
|
|
sizeof(sata_dev_desc[devno].vendor));
|
|
sata_cpy((unsigned char *)sata_dev_desc[devno].product, iop->serial_no,
|
|
sizeof(sata_dev_desc[devno].product));
|
|
strswab(sata_dev_desc[devno].revision);
|
|
strswab(sata_dev_desc[devno].vendor);
|
|
|
|
if ((iop->config & 0x0080) == 0x0080)
|
|
sata_dev_desc[devno].removable = 1;
|
|
else
|
|
sata_dev_desc[devno].removable = 0;
|
|
|
|
sata_dev_desc[devno].lba = iop->lba_capacity;
|
|
debug("lba=0x%x", sata_dev_desc[devno].lba);
|
|
|
|
#ifdef CONFIG_LBA48
|
|
if (iop->command_set_2 & 0x0400) {
|
|
sata_dev_desc[devno].lba48 = 1;
|
|
lba = (unsigned long long) iop->lba48_capacity[0] |
|
|
((unsigned long long) iop->lba48_capacity[1] << 16) |
|
|
((unsigned long long) iop->lba48_capacity[2] << 32) |
|
|
((unsigned long long) iop->lba48_capacity[3] << 48);
|
|
} else {
|
|
sata_dev_desc[devno].lba48 = 0;
|
|
}
|
|
#endif
|
|
|
|
/* assuming HD */
|
|
sata_dev_desc[devno].type = DEV_TYPE_HARDDISK;
|
|
sata_dev_desc[devno].blksz = ATA_BLOCKSIZE;
|
|
sata_dev_desc[devno].log2blksz = LOG2(sata_dev_desc[devno].blksz);
|
|
sata_dev_desc[devno].lun = 0; /* just to fill something in... */
|
|
}
|
|
|
|
void set_Feature_cmd(int num, int dev)
|
|
{
|
|
u8 mask = 0x00, status = 0;
|
|
|
|
if (dev == 0)
|
|
mask = 0x01;
|
|
else
|
|
mask = 0x02;
|
|
|
|
if (!(port[num].dev_mask & mask)) {
|
|
debug("dev%d is not present on port#%d\n", dev, num);
|
|
return;
|
|
}
|
|
|
|
dev_select(&port[num].ioaddr, dev);
|
|
|
|
sata_outb(SETFEATURES_XFER, port[num].ioaddr.feature_addr);
|
|
sata_outb(XFER_PIO_4, port[num].ioaddr.nsect_addr);
|
|
sata_outb(0, port[num].ioaddr.lbal_addr);
|
|
sata_outb(0, port[num].ioaddr.lbam_addr);
|
|
sata_outb(0, port[num].ioaddr.lbah_addr);
|
|
|
|
sata_outb(ATA_DEVICE_OBS, port[num].ioaddr.device_addr);
|
|
sata_outb(ATA_CMD_SETF, port[num].ioaddr.command_addr);
|
|
|
|
udelay(50);
|
|
mdelay(150);
|
|
|
|
status = sata_busy_wait(&port[num].ioaddr, ATA_BUSY, 5000);
|
|
if ((status & (ATA_STAT_BUSY | ATA_STAT_ERR))) {
|
|
printf("Error : status 0x%02x\n", status);
|
|
port[num].dev_mask &= ~mask;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
int sata_devchk(struct sata_ioports *ioaddr, int dev)
|
|
{
|
|
u8 nsect, lbal;
|
|
|
|
dev_select(ioaddr, dev);
|
|
|
|
sata_outb(0x55, ioaddr->nsect_addr);
|
|
sata_outb(0xaa, ioaddr->lbal_addr);
|
|
|
|
sata_outb(0xaa, ioaddr->nsect_addr);
|
|
sata_outb(0x55, ioaddr->lbal_addr);
|
|
|
|
sata_outb(0x55, ioaddr->nsect_addr);
|
|
sata_outb(0xaa, ioaddr->lbal_addr);
|
|
|
|
nsect = sata_inb(ioaddr->nsect_addr);
|
|
lbal = sata_inb(ioaddr->lbal_addr);
|
|
|
|
if ((nsect == 0x55) && (lbal == 0xaa))
|
|
return 1; /* we found a device */
|
|
else
|
|
return 0; /* nothing found */
|
|
}
|
|
|
|
void dev_select(struct sata_ioports *ioaddr, int dev)
|
|
{
|
|
u8 tmp = 0;
|
|
|
|
if (dev == 0)
|
|
tmp = ATA_DEVICE_OBS;
|
|
else
|
|
tmp = ATA_DEVICE_OBS | ATA_DEV1;
|
|
|
|
sata_outb(tmp, ioaddr->device_addr);
|
|
sata_inb(ioaddr->altstatus_addr);
|
|
udelay(5);
|
|
}
|
|
|
|
u8 sata_busy_wait(struct sata_ioports *ioaddr, int bits, unsigned int max)
|
|
{
|
|
u8 status;
|
|
|
|
do {
|
|
udelay(1000);
|
|
status = sata_chk_status(ioaddr);
|
|
max--;
|
|
} while ((status & bits) && (max > 0));
|
|
|
|
return status;
|
|
}
|
|
|
|
u8 sata_chk_status(struct sata_ioports *ioaddr)
|
|
{
|
|
return sata_inb(ioaddr->status_addr);
|
|
}
|
|
|
|
|
|
ulong sata_read(int device, ulong blknr, lbaint_t blkcnt, void *buff)
|
|
{
|
|
ulong n = 0, *buffer = (ulong *)buff;
|
|
u8 dev = 0, num = 0, mask = 0, status = 0;
|
|
|
|
#ifdef CONFIG_LBA48
|
|
unsigned char lba48 = 0;
|
|
|
|
if (blknr & 0x0000fffff0000000) {
|
|
if (!sata_dev_desc[devno].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 / CONFIG_SYS_SATA_DEVS_PER_BUS;
|
|
/* dev on the port */
|
|
if (device >= CONFIG_SYS_SATA_DEVS_PER_BUS)
|
|
dev = device - CONFIG_SYS_SATA_DEVS_PER_BUS;
|
|
else
|
|
dev = device;
|
|
|
|
if (dev == 0)
|
|
mask = 0x01;
|
|
else
|
|
mask = 0x02;
|
|
|
|
if (!(port[num].dev_mask & mask)) {
|
|
printf("dev%d is not present on port#%d\n", dev, num);
|
|
return 0;
|
|
}
|
|
|
|
/* Select device */
|
|
dev_select(&port[num].ioaddr, dev);
|
|
|
|
status = sata_busy_wait(&port[num].ioaddr, ATA_BUSY, 500);
|
|
if (status & ATA_BUSY) {
|
|
printf("ata%u failed to respond\n", port[num].port_no);
|
|
return n;
|
|
}
|
|
while (blkcnt-- > 0) {
|
|
status = sata_busy_wait(&port[num].ioaddr, ATA_BUSY, 500);
|
|
if (status & ATA_BUSY) {
|
|
printf("ata%u failed to respond\n", 0);
|
|
return n;
|
|
}
|
|
#ifdef CONFIG_LBA48
|
|
if (lba48) {
|
|
/* write high bits */
|
|
sata_outb(0, port[num].ioaddr.nsect_addr);
|
|
sata_outb((blknr >> 24) & 0xFF,
|
|
port[num].ioaddr.lbal_addr);
|
|
sata_outb((blknr >> 32) & 0xFF,
|
|
port[num].ioaddr.lbam_addr);
|
|
sata_outb((blknr >> 40) & 0xFF,
|
|
port[num].ioaddr.lbah_addr);
|
|
}
|
|
#endif
|
|
sata_outb(1, port[num].ioaddr.nsect_addr);
|
|
sata_outb(((blknr) >> 0) & 0xFF,
|
|
port[num].ioaddr.lbal_addr);
|
|
sata_outb((blknr >> 8) & 0xFF, port[num].ioaddr.lbam_addr);
|
|
sata_outb((blknr >> 16) & 0xFF, port[num].ioaddr.lbah_addr);
|
|
|
|
#ifdef CONFIG_LBA48
|
|
if (lba48) {
|
|
sata_outb(ATA_LBA, port[num].ioaddr.device_addr);
|
|
sata_outb(ATA_CMD_READ_EXT,
|
|
port[num].ioaddr.command_addr);
|
|
} else
|
|
#endif
|
|
{
|
|
sata_outb(ATA_LBA | ((blknr >> 24) & 0xF),
|
|
port[num].ioaddr.device_addr);
|
|
sata_outb(ATA_CMD_READ,
|
|
port[num].ioaddr.command_addr);
|
|
}
|
|
|
|
mdelay(50);
|
|
/* may take up to 4 sec */
|
|
status = sata_busy_wait(&port[num].ioaddr, ATA_BUSY, 4000);
|
|
|
|
if ((status & (ATA_STAT_DRQ | ATA_STAT_BUSY | ATA_STAT_ERR))
|
|
!= ATA_STAT_DRQ) {
|
|
u8 err = 0;
|
|
|
|
printf("Error no DRQ dev %d blk %ld: sts 0x%02x\n",
|
|
device, (ulong) blknr, status);
|
|
err = sata_inb(port[num].ioaddr.error_addr);
|
|
printf("Error reg = 0x%x\n", err);
|
|
return n;
|
|
}
|
|
input_data(&port[num].ioaddr, buffer, ATA_SECTORWORDS);
|
|
sata_inb(port[num].ioaddr.altstatus_addr);
|
|
udelay(50);
|
|
|
|
++n;
|
|
++blknr;
|
|
buffer += ATA_SECTORWORDS;
|
|
}
|
|
return n;
|
|
}
|
|
|
|
ulong sata_write(int device, ulong blknr, lbaint_t blkcnt, const void *buff)
|
|
{
|
|
ulong n = 0, *buffer = (ulong *)buff;
|
|
unsigned char status = 0, num = 0, dev = 0, mask = 0;
|
|
|
|
#ifdef CONFIG_LBA48
|
|
unsigned char lba48 = 0;
|
|
|
|
if (blknr & 0x0000fffff0000000) {
|
|
if (!sata_dev_desc[devno].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 / CONFIG_SYS_SATA_DEVS_PER_BUS;
|
|
/* dev on the Port */
|
|
if (device >= CONFIG_SYS_SATA_DEVS_PER_BUS)
|
|
dev = device - CONFIG_SYS_SATA_DEVS_PER_BUS;
|
|
else
|
|
dev = device;
|
|
|
|
if (dev == 0)
|
|
mask = 0x01;
|
|
else
|
|
mask = 0x02;
|
|
|
|
/* Select device */
|
|
dev_select(&port[num].ioaddr, dev);
|
|
|
|
status = sata_busy_wait(&port[num].ioaddr, ATA_BUSY, 500);
|
|
if (status & ATA_BUSY) {
|
|
printf("ata%u failed to respond\n", port[num].port_no);
|
|
return n;
|
|
}
|
|
|
|
while (blkcnt-- > 0) {
|
|
status = sata_busy_wait(&port[num].ioaddr, ATA_BUSY, 500);
|
|
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 */
|
|
sata_outb(0, port[num].ioaddr.nsect_addr);
|
|
sata_outb((blknr >> 24) & 0xFF,
|
|
port[num].ioaddr.lbal_addr);
|
|
sata_outb((blknr >> 32) & 0xFF,
|
|
port[num].ioaddr.lbam_addr);
|
|
sata_outb((blknr >> 40) & 0xFF,
|
|
port[num].ioaddr.lbah_addr);
|
|
}
|
|
#endif
|
|
sata_outb(1, port[num].ioaddr.nsect_addr);
|
|
sata_outb((blknr >> 0) & 0xFF, port[num].ioaddr.lbal_addr);
|
|
sata_outb((blknr >> 8) & 0xFF, port[num].ioaddr.lbam_addr);
|
|
sata_outb((blknr >> 16) & 0xFF, port[num].ioaddr.lbah_addr);
|
|
#ifdef CONFIG_LBA48
|
|
if (lba48) {
|
|
sata_outb(ATA_LBA, port[num].ioaddr.device_addr);
|
|
sata_outb(ATA_CMD_WRITE_EXT,
|
|
port[num].ioaddr.command_addr);
|
|
} else
|
|
#endif
|
|
{
|
|
sata_outb(ATA_LBA | ((blknr >> 24) & 0xF),
|
|
port[num].ioaddr.device_addr);
|
|
sata_outb(ATA_CMD_WRITE,
|
|
port[num].ioaddr.command_addr);
|
|
}
|
|
|
|
mdelay(50);
|
|
/* may take up to 4 sec */
|
|
status = sata_busy_wait(&port[num].ioaddr, ATA_BUSY, 4000);
|
|
if ((status & (ATA_STAT_DRQ | ATA_STAT_BUSY | ATA_STAT_ERR))
|
|
!= ATA_STAT_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_SECTORWORDS);
|
|
sata_inb(port[num].ioaddr.altstatus_addr);
|
|
udelay(50);
|
|
|
|
++n;
|
|
++blknr;
|
|
buffer += ATA_SECTORWORDS;
|
|
}
|
|
return n;
|
|
}
|
|
|
|
int scan_sata(int dev)
|
|
{
|
|
return 0;
|
|
}
|