1
0
Fork 0
mirror of https://github.com/Ysurac/openmptcprouter.git synced 2025-03-09 15:40:20 +00:00

Fix for RUTX platform

This commit is contained in:
Ycarus (Yannick Chabanois) 2022-03-28 18:17:07 +02:00
parent ccdb64ad45
commit 59bc57d5d5
7254 changed files with 1810270 additions and 7 deletions

View file

@ -0,0 +1,60 @@
#
# (C) Copyright 2000-2007
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
#
# See file CREDITS for list of people who contributed to this
# project.
#
# 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
#
include $(TOPDIR)/config.mk
LIB := $(obj)libblock.o
COBJS-$(CONFIG_SCSI_AHCI) += ahci.o
COBJS-$(CONFIG_ATA_PIIX) += ata_piix.o
COBJS-$(CONFIG_DWC_AHSATA) += dwc_ahsata.o
COBJS-$(CONFIG_FSL_SATA) += fsl_sata.o
COBJS-$(CONFIG_IDE_FTIDE020) += ftide020.o
COBJS-$(CONFIG_LIBATA) += libata.o
COBJS-$(CONFIG_MVSATA_IDE) += mvsata_ide.o
COBJS-$(CONFIG_MX51_PATA) += mxc_ata.o
COBJS-$(CONFIG_PATA_BFIN) += pata_bfin.o
COBJS-$(CONFIG_SATA_DWC) += sata_dwc.o
COBJS-$(CONFIG_SATA_SIL3114) += sata_sil3114.o
COBJS-$(CONFIG_SATA_SIL) += sata_sil.o
COBJS-$(CONFIG_IDE_SIL680) += sil680.o
COBJS-$(CONFIG_SCSI_SYM53C8XX) += sym53c8xx.o
COBJS-$(CONFIG_SYSTEMACE) += systemace.o
COBJS := $(COBJS-y)
SRCS := $(COBJS:.o=.c)
OBJS := $(addprefix $(obj),$(COBJS))
all: $(LIB)
$(LIB): $(obj).depend $(OBJS)
$(call cmd_link_o_target, $(OBJS))
#########################################################################
# defines $(obj).depend target
include $(SRCTREE)/rules.mk
sinclude $(obj).depend
#########################################################################

View file

@ -0,0 +1,746 @@
/*
* Copyright (C) Freescale Semiconductor, Inc. 2006.
* Author: Jason Jin<Jason.jin@freescale.com>
* Zhang Wei<wei.zhang@freescale.com>
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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 on libata and ahci drvier in kernel
*
*/
#include <common.h>
#include <command.h>
#include <pci.h>
#include <asm/processor.h>
#include <asm/errno.h>
#include <asm/io.h>
#include <malloc.h>
#include <scsi.h>
#include <ata.h>
#include <linux/ctype.h>
#include <ahci.h>
struct ahci_probe_ent *probe_ent = NULL;
hd_driveid_t *ataid[AHCI_MAX_PORTS];
#define writel_with_flush(a,b) do { writel(a,b); readl(b); } while (0)
static inline u32 ahci_port_base(u32 base, u32 port)
{
return base + 0x100 + (port * 0x80);
}
static void ahci_setup_port(struct ahci_ioports *port, unsigned long base,
unsigned int port_idx)
{
base = ahci_port_base(base, port_idx);
port->cmd_addr = base;
port->scr_addr = base + PORT_SCR;
}
#define msleep(a) udelay(a * 1000)
#define ssleep(a) msleep(a * 1000)
static int waiting_for_cmd_completed(volatile u8 *offset,
int timeout_msec,
u32 sign)
{
int i;
u32 status;
for (i = 0; ((status = readl(offset)) & sign) && i < timeout_msec; i++)
msleep(1);
return (i < timeout_msec) ? 0 : -1;
}
static int ahci_host_init(struct ahci_probe_ent *probe_ent)
{
#ifndef CONFIG_SCSI_AHCI_PLAT
pci_dev_t pdev = probe_ent->dev;
u16 tmp16;
unsigned short vendor;
#endif
volatile u8 *mmio = (volatile u8 *)probe_ent->mmio_base;
u32 tmp, cap_save;
int i, j;
volatile u8 *port_mmio;
cap_save = readl(mmio + HOST_CAP);
cap_save &= ((1 << 28) | (1 << 17));
cap_save |= (1 << 27);
/* global controller reset */
tmp = readl(mmio + HOST_CTL);
if ((tmp & HOST_RESET) == 0)
writel_with_flush(tmp | HOST_RESET, mmio + HOST_CTL);
/* reset must complete within 1 second, or
* the hardware should be considered fried.
*/
ssleep(1);
tmp = readl(mmio + HOST_CTL);
if (tmp & HOST_RESET) {
debug("controller reset failed (0x%x)\n", tmp);
return -1;
}
writel_with_flush(HOST_AHCI_EN, mmio + HOST_CTL);
writel(cap_save, mmio + HOST_CAP);
writel_with_flush(0xf, mmio + HOST_PORTS_IMPL);
#ifndef CONFIG_SCSI_AHCI_PLAT
pci_read_config_word(pdev, PCI_VENDOR_ID, &vendor);
if (vendor == PCI_VENDOR_ID_INTEL) {
u16 tmp16;
pci_read_config_word(pdev, 0x92, &tmp16);
tmp16 |= 0xf;
pci_write_config_word(pdev, 0x92, tmp16);
}
#endif
probe_ent->cap = readl(mmio + HOST_CAP);
probe_ent->port_map = readl(mmio + HOST_PORTS_IMPL);
probe_ent->n_ports = (probe_ent->cap & 0x1f) + 1;
debug("cap 0x%x port_map 0x%x n_ports %d\n",
probe_ent->cap, probe_ent->port_map, probe_ent->n_ports);
for (i = 0; i < probe_ent->n_ports; i++) {
probe_ent->port[i].port_mmio = ahci_port_base((u32) mmio, i);
port_mmio = (u8 *) probe_ent->port[i].port_mmio;
ahci_setup_port(&probe_ent->port[i], (unsigned long)mmio, i);
/* make sure port is not active */
tmp = readl(port_mmio + PORT_CMD);
if (tmp & (PORT_CMD_LIST_ON | PORT_CMD_FIS_ON |
PORT_CMD_FIS_RX | PORT_CMD_START)) {
tmp &= ~(PORT_CMD_LIST_ON | PORT_CMD_FIS_ON |
PORT_CMD_FIS_RX | PORT_CMD_START);
writel_with_flush(tmp, port_mmio + PORT_CMD);
/* spec says 500 msecs for each bit, so
* this is slightly incorrect.
*/
msleep(500);
}
writel(PORT_CMD_SPIN_UP, port_mmio + PORT_CMD);
j = 0;
while (j < 100) {
msleep(10);
tmp = readl(port_mmio + PORT_SCR_STAT);
if ((tmp & 0xf) == 0x3)
break;
j++;
}
tmp = readl(port_mmio + PORT_SCR_ERR);
debug("PORT_SCR_ERR 0x%x\n", tmp);
writel(tmp, port_mmio + PORT_SCR_ERR);
/* ack any pending irq events for this port */
tmp = readl(port_mmio + PORT_IRQ_STAT);
debug("PORT_IRQ_STAT 0x%x\n", tmp);
if (tmp)
writel(tmp, port_mmio + PORT_IRQ_STAT);
writel(1 << i, mmio + HOST_IRQ_STAT);
/* set irq mask (enables interrupts) */
writel(DEF_PORT_IRQ, port_mmio + PORT_IRQ_MASK);
/*register linkup ports */
tmp = readl(port_mmio + PORT_SCR_STAT);
debug("Port %d status: 0x%x\n", i, tmp);
if ((tmp & 0xf) == 0x03)
probe_ent->link_port_map |= (0x01 << i);
}
tmp = readl(mmio + HOST_CTL);
debug("HOST_CTL 0x%x\n", tmp);
writel(tmp | HOST_IRQ_EN, mmio + HOST_CTL);
tmp = readl(mmio + HOST_CTL);
debug("HOST_CTL 0x%x\n", tmp);
#ifndef CONFIG_SCSI_AHCI_PLAT
pci_read_config_word(pdev, PCI_COMMAND, &tmp16);
tmp |= PCI_COMMAND_MASTER;
pci_write_config_word(pdev, PCI_COMMAND, tmp16);
#endif
return 0;
}
static void ahci_print_info(struct ahci_probe_ent *probe_ent)
{
#ifndef CONFIG_SCSI_AHCI_PLAT
pci_dev_t pdev = probe_ent->dev;
u16 cc;
#endif
volatile u8 *mmio = (volatile u8 *)probe_ent->mmio_base;
u32 vers, cap, impl, speed;
const char *speed_s;
const char *scc_s;
vers = readl(mmio + HOST_VERSION);
cap = probe_ent->cap;
impl = probe_ent->port_map;
speed = (cap >> 20) & 0xf;
if (speed == 1)
speed_s = "1.5";
else if (speed == 2)
speed_s = "3";
else
speed_s = "?";
#ifdef CONFIG_SCSI_AHCI_PLAT
scc_s = "SATA";
#else
pci_read_config_word(pdev, 0x0a, &cc);
if (cc == 0x0101)
scc_s = "IDE";
else if (cc == 0x0106)
scc_s = "SATA";
else if (cc == 0x0104)
scc_s = "RAID";
else
scc_s = "unknown";
#endif
printf("AHCI %02x%02x.%02x%02x "
"%u slots %u ports %s Gbps 0x%x impl %s mode\n",
(vers >> 24) & 0xff,
(vers >> 16) & 0xff,
(vers >> 8) & 0xff,
vers & 0xff,
((cap >> 8) & 0x1f) + 1, (cap & 0x1f) + 1, speed_s, impl, scc_s);
printf("flags: "
"%s%s%s%s%s%s"
"%s%s%s%s%s%s%s\n",
cap & (1 << 31) ? "64bit " : "",
cap & (1 << 30) ? "ncq " : "",
cap & (1 << 28) ? "ilck " : "",
cap & (1 << 27) ? "stag " : "",
cap & (1 << 26) ? "pm " : "",
cap & (1 << 25) ? "led " : "",
cap & (1 << 24) ? "clo " : "",
cap & (1 << 19) ? "nz " : "",
cap & (1 << 18) ? "only " : "",
cap & (1 << 17) ? "pmp " : "",
cap & (1 << 15) ? "pio " : "",
cap & (1 << 14) ? "slum " : "",
cap & (1 << 13) ? "part " : "");
}
#ifndef CONFIG_SCSI_AHCI_PLAT
static int ahci_init_one(pci_dev_t pdev)
{
u16 vendor;
int rc;
memset((void *)ataid, 0, sizeof(hd_driveid_t *) * AHCI_MAX_PORTS);
probe_ent = malloc(sizeof(struct ahci_probe_ent));
memset(probe_ent, 0, sizeof(struct ahci_probe_ent));
probe_ent->dev = pdev;
probe_ent->host_flags = ATA_FLAG_SATA
| ATA_FLAG_NO_LEGACY
| ATA_FLAG_MMIO
| ATA_FLAG_PIO_DMA
| ATA_FLAG_NO_ATAPI;
probe_ent->pio_mask = 0x1f;
probe_ent->udma_mask = 0x7f; /*Fixme,assume to support UDMA6 */
probe_ent->mmio_base = (u32)pci_map_bar(pdev, AHCI_PCI_BAR,
PCI_REGION_MEM);
/* Take from kernel:
* JMicron-specific fixup:
* make sure we're in AHCI mode
*/
pci_read_config_word(pdev, PCI_VENDOR_ID, &vendor);
if (vendor == 0x197b)
pci_write_config_byte(pdev, 0x41, 0xa1);
/* initialize adapter */
rc = ahci_host_init(probe_ent);
if (rc)
goto err_out;
ahci_print_info(probe_ent);
return 0;
err_out:
return rc;
}
#endif
#define MAX_DATA_BYTE_COUNT (4*1024*1024)
static int ahci_fill_sg(u8 port, unsigned char *buf, int buf_len)
{
struct ahci_ioports *pp = &(probe_ent->port[port]);
struct ahci_sg *ahci_sg = pp->cmd_tbl_sg;
u32 sg_count;
int i;
sg_count = ((buf_len - 1) / MAX_DATA_BYTE_COUNT) + 1;
if (sg_count > AHCI_MAX_SG) {
printf("Error:Too much sg!\n");
return -1;
}
for (i = 0; i < sg_count; i++) {
ahci_sg->addr =
cpu_to_le32((u32) buf + i * MAX_DATA_BYTE_COUNT);
ahci_sg->addr_hi = 0;
ahci_sg->flags_size = cpu_to_le32(0x3fffff &
(buf_len < MAX_DATA_BYTE_COUNT
? (buf_len - 1)
: (MAX_DATA_BYTE_COUNT - 1)));
ahci_sg++;
buf_len -= MAX_DATA_BYTE_COUNT;
}
return sg_count;
}
static void ahci_fill_cmd_slot(struct ahci_ioports *pp, u32 opts)
{
pp->cmd_slot->opts = cpu_to_le32(opts);
pp->cmd_slot->status = 0;
pp->cmd_slot->tbl_addr = cpu_to_le32(pp->cmd_tbl & 0xffffffff);
pp->cmd_slot->tbl_addr_hi = 0;
}
static void ahci_set_feature(u8 port)
{
struct ahci_ioports *pp = &(probe_ent->port[port]);
volatile u8 *port_mmio = (volatile u8 *)pp->port_mmio;
u32 cmd_fis_len = 5; /* five dwords */
u8 fis[20];
/*set feature */
memset(fis, 0, 20);
fis[0] = 0x27;
fis[1] = 1 << 7;
fis[2] = ATA_CMD_SETF;
fis[3] = SETFEATURES_XFER;
fis[12] = __ilog2(probe_ent->udma_mask + 1) + 0x40 - 0x01;
memcpy((unsigned char *)pp->cmd_tbl, fis, 20);
ahci_fill_cmd_slot(pp, cmd_fis_len);
writel(1, port_mmio + PORT_CMD_ISSUE);
readl(port_mmio + PORT_CMD_ISSUE);
if (waiting_for_cmd_completed(port_mmio + PORT_CMD_ISSUE, 150, 0x1)) {
printf("set feature error!\n");
}
}
static int ahci_port_start(u8 port)
{
struct ahci_ioports *pp = &(probe_ent->port[port]);
volatile u8 *port_mmio = (volatile u8 *)pp->port_mmio;
u32 port_status;
u32 mem;
debug("Enter start port: %d\n", port);
port_status = readl(port_mmio + PORT_SCR_STAT);
debug("Port %d status: %x\n", port, port_status);
if ((port_status & 0xf) != 0x03) {
printf("No Link on this port!\n");
return -1;
}
mem = (u32) malloc(AHCI_PORT_PRIV_DMA_SZ + 2048);
if (!mem) {
free(pp);
printf("No mem for table!\n");
return -ENOMEM;
}
mem = (mem + 0x800) & (~0x7ff); /* Aligned to 2048-bytes */
memset((u8 *) mem, 0, AHCI_PORT_PRIV_DMA_SZ);
/*
* First item in chunk of DMA memory: 32-slot command table,
* 32 bytes each in size
*/
pp->cmd_slot = (struct ahci_cmd_hdr *)mem;
debug("cmd_slot = %p\n", pp->cmd_slot);
mem += (AHCI_CMD_SLOT_SZ + 224);
/*
* Second item: Received-FIS area
*/
pp->rx_fis = mem;
mem += AHCI_RX_FIS_SZ;
/*
* Third item: data area for storing a single command
* and its scatter-gather table
*/
pp->cmd_tbl = mem;
debug("cmd_tbl_dma = 0x%x\n", pp->cmd_tbl);
mem += AHCI_CMD_TBL_HDR;
pp->cmd_tbl_sg = (struct ahci_sg *)mem;
writel_with_flush((u32) pp->cmd_slot, port_mmio + PORT_LST_ADDR);
writel_with_flush(pp->rx_fis, port_mmio + PORT_FIS_ADDR);
writel_with_flush(PORT_CMD_ICC_ACTIVE | PORT_CMD_FIS_RX |
PORT_CMD_POWER_ON | PORT_CMD_SPIN_UP |
PORT_CMD_START, port_mmio + PORT_CMD);
debug("Exit start port %d\n", port);
return 0;
}
static int get_ahci_device_data(u8 port, u8 *fis, int fis_len, u8 *buf,
int buf_len)
{
struct ahci_ioports *pp = &(probe_ent->port[port]);
volatile u8 *port_mmio = (volatile u8 *)pp->port_mmio;
u32 opts;
u32 port_status;
int sg_count;
debug("Enter get_ahci_device_data: for port %d\n", port);
if (port > probe_ent->n_ports) {
printf("Invaild port number %d\n", port);
return -1;
}
port_status = readl(port_mmio + PORT_SCR_STAT);
if ((port_status & 0xf) != 0x03) {
debug("No Link on port %d!\n", port);
return -1;
}
memcpy((unsigned char *)pp->cmd_tbl, fis, fis_len);
sg_count = ahci_fill_sg(port, buf, buf_len);
opts = (fis_len >> 2) | (sg_count << 16);
ahci_fill_cmd_slot(pp, opts);
writel_with_flush(1, port_mmio + PORT_CMD_ISSUE);
if (waiting_for_cmd_completed(port_mmio + PORT_CMD_ISSUE, 150, 0x1)) {
printf("timeout exit!\n");
return -1;
}
debug("get_ahci_device_data: %d byte transferred.\n",
pp->cmd_slot->status);
return 0;
}
static char *ata_id_strcpy(u16 *target, u16 *src, int len)
{
int i;
for (i = 0; i < len / 2; i++)
target[i] = swab16(src[i]);
return (char *)target;
}
static void dump_ataid(hd_driveid_t *ataid)
{
debug("(49)ataid->capability = 0x%x\n", ataid->capability);
debug("(53)ataid->field_valid =0x%x\n", ataid->field_valid);
debug("(63)ataid->dma_mword = 0x%x\n", ataid->dma_mword);
debug("(64)ataid->eide_pio_modes = 0x%x\n", ataid->eide_pio_modes);
debug("(75)ataid->queue_depth = 0x%x\n", ataid->queue_depth);
debug("(80)ataid->major_rev_num = 0x%x\n", ataid->major_rev_num);
debug("(81)ataid->minor_rev_num = 0x%x\n", ataid->minor_rev_num);
debug("(82)ataid->command_set_1 = 0x%x\n", ataid->command_set_1);
debug("(83)ataid->command_set_2 = 0x%x\n", ataid->command_set_2);
debug("(84)ataid->cfsse = 0x%x\n", ataid->cfsse);
debug("(85)ataid->cfs_enable_1 = 0x%x\n", ataid->cfs_enable_1);
debug("(86)ataid->cfs_enable_2 = 0x%x\n", ataid->cfs_enable_2);
debug("(87)ataid->csf_default = 0x%x\n", ataid->csf_default);
debug("(88)ataid->dma_ultra = 0x%x\n", ataid->dma_ultra);
debug("(93)ataid->hw_config = 0x%x\n", ataid->hw_config);
}
/*
* SCSI INQUIRY command operation.
*/
static int ata_scsiop_inquiry(ccb *pccb)
{
u8 hdr[] = {
0,
0,
0x5, /* claim SPC-3 version compatibility */
2,
95 - 4,
};
u8 fis[20];
u8 *tmpid;
u8 port;
/* Clean ccb data buffer */
memset(pccb->pdata, 0, pccb->datalen);
memcpy(pccb->pdata, hdr, sizeof(hdr));
if (pccb->datalen <= 35)
return 0;
memset(fis, 0, 20);
/* Construct the FIS */
fis[0] = 0x27; /* Host to device FIS. */
fis[1] = 1 << 7; /* Command FIS. */
fis[2] = ATA_CMD_IDENT; /* Command byte. */
/* Read id from sata */
port = pccb->target;
if (!(tmpid = malloc(sizeof(hd_driveid_t))))
return -ENOMEM;
if (get_ahci_device_data(port, (u8 *) & fis, 20,
tmpid, sizeof(hd_driveid_t))) {
debug("scsi_ahci: SCSI inquiry command failure.\n");
return -EIO;
}
if (ataid[port])
free(ataid[port]);
ataid[port] = (hd_driveid_t *) tmpid;
memcpy(&pccb->pdata[8], "ATA ", 8);
ata_id_strcpy((u16 *) &pccb->pdata[16], (u16 *)ataid[port]->model, 16);
ata_id_strcpy((u16 *) &pccb->pdata[32], (u16 *)ataid[port]->fw_rev, 4);
dump_ataid(ataid[port]);
return 0;
}
/*
* SCSI READ10 command operation.
*/
static int ata_scsiop_read10(ccb * pccb)
{
u32 len = 0;
u8 fis[20];
len = (((u32) pccb->cmd[7]) << 8) | ((u32) pccb->cmd[8]);
/* For 10-byte and 16-byte SCSI R/W commands, transfer
* length 0 means transfer 0 block of data.
* However, for ATA R/W commands, sector count 0 means
* 256 or 65536 sectors, not 0 sectors as in SCSI.
*
* WARNING: one or two older ATA drives treat 0 as 0...
*/
if (!len)
return 0;
memset(fis, 0, 20);
/* Construct the FIS */
fis[0] = 0x27; /* Host to device FIS. */
fis[1] = 1 << 7; /* Command FIS. */
fis[2] = ATA_CMD_RD_DMA; /* Command byte. */
/* LBA address, only support LBA28 in this driver */
fis[4] = pccb->cmd[5];
fis[5] = pccb->cmd[4];
fis[6] = pccb->cmd[3];
fis[7] = (pccb->cmd[2] & 0x0f) | 0xe0;
/* Sector Count */
fis[12] = pccb->cmd[8];
fis[13] = pccb->cmd[7];
/* Read from ahci */
if (get_ahci_device_data(pccb->target, (u8 *) & fis, 20,
pccb->pdata, pccb->datalen)) {
debug("scsi_ahci: SCSI READ10 command failure.\n");
return -EIO;
}
return 0;
}
/*
* SCSI READ CAPACITY10 command operation.
*/
static int ata_scsiop_read_capacity10(ccb *pccb)
{
u32 cap;
if (!ataid[pccb->target]) {
printf("scsi_ahci: SCSI READ CAPACITY10 command failure. "
"\tNo ATA info!\n"
"\tPlease run SCSI commmand INQUIRY firstly!\n");
return -EPERM;
}
cap = le32_to_cpu(ataid[pccb->target]->lba_capacity);
memcpy(pccb->pdata, &cap, sizeof(cap));
pccb->pdata[4] = pccb->pdata[5] = 0;
pccb->pdata[6] = 512 >> 8;
pccb->pdata[7] = 512 & 0xff;
return 0;
}
/*
* SCSI TEST UNIT READY command operation.
*/
static int ata_scsiop_test_unit_ready(ccb *pccb)
{
return (ataid[pccb->target]) ? 0 : -EPERM;
}
int scsi_exec(ccb *pccb)
{
int ret;
switch (pccb->cmd[0]) {
case SCSI_READ10:
ret = ata_scsiop_read10(pccb);
break;
case SCSI_RD_CAPAC:
ret = ata_scsiop_read_capacity10(pccb);
break;
case SCSI_TST_U_RDY:
ret = ata_scsiop_test_unit_ready(pccb);
break;
case SCSI_INQUIRY:
ret = ata_scsiop_inquiry(pccb);
break;
default:
printf("Unsupport SCSI command 0x%02x\n", pccb->cmd[0]);
return FALSE;
}
if (ret) {
debug("SCSI command 0x%02x ret errno %d\n", pccb->cmd[0], ret);
return FALSE;
}
return TRUE;
}
void scsi_low_level_init(int busdevfunc)
{
int i;
u32 linkmap;
#ifndef CONFIG_SCSI_AHCI_PLAT
ahci_init_one(busdevfunc);
#endif
linkmap = probe_ent->link_port_map;
for (i = 0; i < CONFIG_SYS_SCSI_MAX_SCSI_ID; i++) {
if (((linkmap >> i) & 0x01)) {
if (ahci_port_start((u8) i)) {
printf("Can not start port %d\n", i);
continue;
}
ahci_set_feature((u8) i);
}
}
}
#ifdef CONFIG_SCSI_AHCI_PLAT
int ahci_init(u32 base)
{
int i, rc = 0;
u32 linkmap;
memset(ataid, 0, sizeof(ataid));
probe_ent = malloc(sizeof(struct ahci_probe_ent));
memset(probe_ent, 0, sizeof(struct ahci_probe_ent));
probe_ent->host_flags = ATA_FLAG_SATA
| ATA_FLAG_NO_LEGACY
| ATA_FLAG_MMIO
| ATA_FLAG_PIO_DMA
| ATA_FLAG_NO_ATAPI;
probe_ent->pio_mask = 0x1f;
probe_ent->udma_mask = 0x7f; /*Fixme,assume to support UDMA6 */
probe_ent->mmio_base = base;
/* initialize adapter */
rc = ahci_host_init(probe_ent);
if (rc)
goto err_out;
ahci_print_info(probe_ent);
linkmap = probe_ent->link_port_map;
for (i = 0; i < CONFIG_SYS_SCSI_MAX_SCSI_ID; i++) {
if (((linkmap >> i) & 0x01)) {
if (ahci_port_start((u8) i)) {
printf("Can not start port %d\n", i);
continue;
}
ahci_set_feature((u8) i);
}
}
err_out:
return rc;
}
#endif
void scsi_bus_reset(void)
{
/*Not implement*/
}
void scsi_print_error(ccb * pccb)
{
/*The ahci error info can be read in the ahci driver*/
}

View file

@ -0,0 +1,756 @@
/*
* 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>
extern block_dev_desc_t sata_dev_desc[CONFIG_SYS_SATA_MAX_DEVICE];
extern int sata_curr_device;
#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 = 0; /*Primary cmd block */
u32 iobase2 = 0; /*Primary ctl block */
u32 iobase3 = 0; /*Sec cmd block */
u32 iobase4 = 0; /*sec ctl block */
u32 iobase5 = 0; /*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)) {
printf ("error no base addr for SATA controller\n");
return 1;
/*ERROR*/}
iobase1 &= 0xFFFFFFFE;
iobase2 &= 0xFFFFFFFE;
iobase3 &= 0xFFFFFFFE;
iobase4 &= 0xFFFFFFFE;
iobase5 &= 0xFFFFFFFE;
/*check for mode */
pci_read_config_byte (bdf, PCI_PMR, &pmr);
if (pmr > 1) {
printf ("combined mode not supported\n");
return 1;
}
pci_read_config_byte (bdf, PCI_PI, &pi);
if ((pi & 0x05) != 0x05) {
printf ("Sata is in Legacy mode\n");
return 1;
} else {
printf ("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 = 0;
u8 i, rv = 0;
if (!done)
done = 1;
else
return 0;
rv = pci_sata_init ();
if (rv == 1) {
printf ("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]);
if (sata_curr_device < 0)
sata_curr_device =
i * CONFIG_SYS_SATA_DEVS_PER_BUS + j;
}
}
}
return 0;
}
static u8 __inline__
sata_inb (unsigned long ioaddr)
{
return inb (ioaddr);
}
static void __inline__
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))) {
PRINTF ("dev_chk failed for dev#%d\n", i);
} else {
port[num].dev_mask |= (1 << i);
PRINTF ("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.
*/
msleep (150);
status = sata_busy_wait (&port[num].ioaddr, ATA_BUSY, 300);
while ((status & ATA_BUSY)) {
msleep (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)) {
msleep (100);
status = sata_chk_status (&port[num].ioaddr);
}
if (status & ATA_BUSY) {
printf ("ata%u failed to respond : ", num);
printf ("bus reset failed\n");
return 1;
}
return 0;
}
void
sata_identify (int num, int dev)
{
u8 cmd = 0, status = 0, 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) {
printf ("\ndevice not responding\n");
port[num].dev_mask &= ~mask;
return;
}
input_data (&port[num].ioaddr, (ulong *) iobuf, ATA_SECTORWORDS);
PRINTF ("\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)) {
PRINTF ("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);
}
PRINTF ("no. of sectors %u\n", ata_id_u64 (iobuf, 100));
PRINTF ("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;
PRINTF ("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].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)) {
PRINTF ("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);
msleep (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);
}
void
msleep (int count)
{
int i;
for (i = 0; i < count; i++)
udelay (1000);
}
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);
}
msleep (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, 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);
}
msleep (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;
}

View file

@ -0,0 +1,81 @@
#ifndef __ATA_PIIX_H__
#define __ATA_PIIX_H__
#if (DEBUG_SATA)
#define PRINTF(fmt,args...) printf (fmt ,##args)
#else
#define PRINTF(fmt,args...)
#endif
struct sata_ioports {
unsigned long cmd_addr;
unsigned long data_addr;
unsigned long error_addr;
unsigned long feature_addr;
unsigned long nsect_addr;
unsigned long lbal_addr;
unsigned long lbam_addr;
unsigned long lbah_addr;
unsigned long device_addr;
unsigned long status_addr;
unsigned long command_addr;
unsigned long altstatus_addr;
unsigned long ctl_addr;
unsigned long bmdma_addr;
unsigned long scr_addr;
};
struct sata_port {
unsigned char port_no; /* primary=0, secondary=1 */
struct sata_ioports ioaddr; /* ATA cmd/ctl/dma reg blks */
unsigned char ctl_reg;
unsigned char last_ctl;
unsigned char port_state; /* 1-port is available and */
/* 0-port is not available */
unsigned char dev_mask;
};
/***********SATA LIBRARY SPECIFIC DEFINITIONS AND DECLARATIONS**************/
#ifdef SATA_DECL /*SATA library specific declarations */
inline void
ata_dump_id (u16 * id)
{
PRINTF ("49 = 0x%04x "
"53 = 0x%04x "
"63 = 0x%04x "
"64 = 0x%04x "
"75 = 0x%04x \n", id[49], id[53], id[63], id[64], id[75]);
PRINTF ("80 = 0x%04x "
"81 = 0x%04x "
"82 = 0x%04x "
"83 = 0x%04x "
"84 = 0x%04x \n", id[80], id[81], id[82], id[83], id[84]);
PRINTF ("88 = 0x%04x " "93 = 0x%04x\n", id[88], id[93]);
}
#endif
#ifdef SATA_DECL /*SATA library specific declarations */
int sata_bus_softreset (int num);
void sata_identify (int num, int dev);
void sata_port (struct sata_ioports *ioport);
void set_Feature_cmd (int num, int dev);
int sata_devchk (struct sata_ioports *ioaddr, int dev);
void dev_select (struct sata_ioports *ioaddr, int dev);
u8 sata_busy_wait (struct sata_ioports *ioaddr, int bits, unsigned int max);
u8 sata_chk_status (struct sata_ioports *ioaddr);
ulong sata_read (int device, ulong blknr,lbaint_t blkcnt, void * buffer);
ulong sata_write (int device,ulong blknr, lbaint_t blkcnt, void * buffer);
void msleep (int count);
#endif
/************DRIVER SPECIFIC DEFINITIONS AND DECLARATIONS**************/
#ifdef DRV_DECL /*Driver specific declaration */
int init_sata (int dev);
#endif
#ifdef DRV_DECL /*Defines Driver Specific variables */
struct sata_port port[CONFIG_SYS_SATA_MAXBUS];
#endif
#endif /* __ATA_PIIX_H__ */

View file

@ -0,0 +1,969 @@
/*
* Copyright (C) 2010-2011 Freescale Semiconductor, Inc.
* Terry Lv <r65388@freescale.com>
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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.
*
*/
#include <libata.h>
#include <ahci.h>
#include <fis.h>
#include <common.h>
#include <malloc.h>
#include <linux/ctype.h>
#include <asm/errno.h>
#include <asm/io.h>
#include <linux/bitops.h>
#include <asm/arch/clock.h>
#include "dwc_ahsata.h"
struct sata_port_regs {
u32 clb;
u32 clbu;
u32 fb;
u32 fbu;
u32 is;
u32 ie;
u32 cmd;
u32 res1[1];
u32 tfd;
u32 sig;
u32 ssts;
u32 sctl;
u32 serr;
u32 sact;
u32 ci;
u32 sntf;
u32 res2[1];
u32 dmacr;
u32 res3[1];
u32 phycr;
u32 physr;
};
struct sata_host_regs {
u32 cap;
u32 ghc;
u32 is;
u32 pi;
u32 vs;
u32 ccc_ctl;
u32 ccc_ports;
u32 res1[2];
u32 cap2;
u32 res2[30];
u32 bistafr;
u32 bistcr;
u32 bistfctr;
u32 bistsr;
u32 bistdecr;
u32 res3[2];
u32 oobr;
u32 res4[8];
u32 timer1ms;
u32 res5[1];
u32 gparam1r;
u32 gparam2r;
u32 pparamr;
u32 testr;
u32 versionr;
u32 idr;
};
#define MAX_DATA_BYTES_PER_SG (4 * 1024 * 1024)
#define MAX_BYTES_PER_TRANS (AHCI_MAX_SG * MAX_DATA_BYTES_PER_SG)
#define writel_with_flush(a, b) do { writel(a, b); readl(b); } while (0)
static int is_ready;
static inline u32 ahci_port_base(u32 base, u32 port)
{
return base + 0x100 + (port * 0x80);
}
static int waiting_for_cmd_completed(u8 *offset,
int timeout_msec,
u32 sign)
{
int i;
u32 status;
for (i = 0;
((status = readl(offset)) & sign) && i < timeout_msec;
++i)
mdelay(1);
return (i < timeout_msec) ? 0 : -1;
}
static int ahci_setup_oobr(struct ahci_probe_ent *probe_ent,
int clk)
{
struct sata_host_regs *host_mmio =
(struct sata_host_regs *)probe_ent->mmio_base;
writel(SATA_HOST_OOBR_WE, &(host_mmio->oobr));
writel(0x02060b14, &(host_mmio->oobr));
return 0;
}
static int ahci_host_init(struct ahci_probe_ent *probe_ent)
{
u32 tmp, cap_save, num_ports;
int i, j, timeout = 1000;
struct sata_port_regs *port_mmio = NULL;
struct sata_host_regs *host_mmio =
(struct sata_host_regs *)probe_ent->mmio_base;
int clk = mxc_get_clock(MXC_SATA_CLK);
cap_save = readl(&(host_mmio->cap));
cap_save |= SATA_HOST_CAP_SSS;
/* global controller reset */
tmp = readl(&(host_mmio->ghc));
if ((tmp & SATA_HOST_GHC_HR) == 0)
writel_with_flush(tmp | SATA_HOST_GHC_HR, &(host_mmio->ghc));
while ((readl(&(host_mmio->ghc)) & SATA_HOST_GHC_HR)
&& --timeout)
;
if (timeout <= 0) {
debug("controller reset failed (0x%x)\n", tmp);
return -1;
}
/* Set timer 1ms */
writel(clk / 1000, &(host_mmio->timer1ms));
ahci_setup_oobr(probe_ent, 0);
writel_with_flush(SATA_HOST_GHC_AE, &(host_mmio->ghc));
writel(cap_save, &(host_mmio->cap));
num_ports = (cap_save & SATA_HOST_CAP_NP_MASK) + 1;
writel_with_flush((1 << num_ports) - 1,
&(host_mmio->pi));
/*
* Determine which Ports are implemented by the DWC_ahsata,
* by reading the PI register. This bit map value aids the
* software to determine how many Ports are available and
* which Port registers need to be initialized.
*/
probe_ent->cap = readl(&(host_mmio->cap));
probe_ent->port_map = readl(&(host_mmio->pi));
/* Determine how many command slots the HBA supports */
probe_ent->n_ports =
(probe_ent->cap & SATA_HOST_CAP_NP_MASK) + 1;
debug("cap 0x%x port_map 0x%x n_ports %d\n",
probe_ent->cap, probe_ent->port_map, probe_ent->n_ports);
for (i = 0; i < probe_ent->n_ports; i++) {
probe_ent->port[i].port_mmio =
ahci_port_base((u32)host_mmio, i);
port_mmio =
(struct sata_port_regs *)probe_ent->port[i].port_mmio;
/* Ensure that the DWC_ahsata is in idle state */
tmp = readl(&(port_mmio->cmd));
/*
* When P#CMD.ST, P#CMD.CR, P#CMD.FRE and P#CMD.FR
* are all cleared, the Port is in an idle state.
*/
if (tmp & (SATA_PORT_CMD_CR | SATA_PORT_CMD_FR |
SATA_PORT_CMD_FRE | SATA_PORT_CMD_ST)) {
/*
* System software places a Port into the idle state by
* clearing P#CMD.ST and waiting for P#CMD.CR to return
* 0 when read.
*/
tmp &= ~SATA_PORT_CMD_ST;
writel_with_flush(tmp, &(port_mmio->cmd));
/*
* spec says 500 msecs for each bit, so
* this is slightly incorrect.
*/
mdelay(500);
timeout = 1000;
while ((readl(&(port_mmio->cmd)) & SATA_PORT_CMD_CR)
&& --timeout)
;
if (timeout <= 0) {
debug("port reset failed (0x%x)\n", tmp);
return -1;
}
}
/* Spin-up device */
tmp = readl(&(port_mmio->cmd));
writel((tmp | SATA_PORT_CMD_SUD), &(port_mmio->cmd));
/* Wait for spin-up to finish */
timeout = 1000;
while (!(readl(&(port_mmio->cmd)) | SATA_PORT_CMD_SUD)
&& --timeout)
;
if (timeout <= 0) {
debug("Spin-Up can't finish!\n");
return -1;
}
for (j = 0; j < 100; ++j) {
mdelay(10);
tmp = readl(&(port_mmio->ssts));
if (((tmp & SATA_PORT_SSTS_DET_MASK) == 0x3) ||
((tmp & SATA_PORT_SSTS_DET_MASK) == 0x1))
break;
}
/* Wait for COMINIT bit 26 (DIAG_X) in SERR */
timeout = 1000;
while (!(readl(&(port_mmio->serr)) | SATA_PORT_SERR_DIAG_X)
&& --timeout)
;
if (timeout <= 0) {
debug("Can't find DIAG_X set!\n");
return -1;
}
/*
* For each implemented Port, clear the P#SERR
* register, by writing ones to each implemented\
* bit location.
*/
tmp = readl(&(port_mmio->serr));
debug("P#SERR 0x%x\n",
tmp);
writel(tmp, &(port_mmio->serr));
/* Ack any pending irq events for this port */
tmp = readl(&(host_mmio->is));
debug("IS 0x%x\n", tmp);
if (tmp)
writel(tmp, &(host_mmio->is));
writel(1 << i, &(host_mmio->is));
/* set irq mask (enables interrupts) */
writel(DEF_PORT_IRQ, &(port_mmio->ie));
/* register linkup ports */
tmp = readl(&(port_mmio->ssts));
debug("Port %d status: 0x%x\n", i, tmp);
if ((tmp & SATA_PORT_SSTS_DET_MASK) == 0x03)
probe_ent->link_port_map |= (0x01 << i);
}
tmp = readl(&(host_mmio->ghc));
debug("GHC 0x%x\n", tmp);
writel(tmp | SATA_HOST_GHC_IE, &(host_mmio->ghc));
tmp = readl(&(host_mmio->ghc));
debug("GHC 0x%x\n", tmp);
return 0;
}
static void ahci_print_info(struct ahci_probe_ent *probe_ent)
{
struct sata_host_regs *host_mmio =
(struct sata_host_regs *)probe_ent->mmio_base;
u32 vers, cap, impl, speed;
const char *speed_s;
const char *scc_s;
vers = readl(&(host_mmio->vs));
cap = probe_ent->cap;
impl = probe_ent->port_map;
speed = (cap & SATA_HOST_CAP_ISS_MASK)
>> SATA_HOST_CAP_ISS_OFFSET;
if (speed == 1)
speed_s = "1.5";
else if (speed == 2)
speed_s = "3";
else
speed_s = "?";
scc_s = "SATA";
printf("AHCI %02x%02x.%02x%02x "
"%u slots %u ports %s Gbps 0x%x impl %s mode\n",
(vers >> 24) & 0xff,
(vers >> 16) & 0xff,
(vers >> 8) & 0xff,
vers & 0xff,
((cap >> 8) & 0x1f) + 1,
(cap & 0x1f) + 1,
speed_s,
impl,
scc_s);
printf("flags: "
"%s%s%s%s%s%s"
"%s%s%s%s%s%s%s\n",
cap & (1 << 31) ? "64bit " : "",
cap & (1 << 30) ? "ncq " : "",
cap & (1 << 28) ? "ilck " : "",
cap & (1 << 27) ? "stag " : "",
cap & (1 << 26) ? "pm " : "",
cap & (1 << 25) ? "led " : "",
cap & (1 << 24) ? "clo " : "",
cap & (1 << 19) ? "nz " : "",
cap & (1 << 18) ? "only " : "",
cap & (1 << 17) ? "pmp " : "",
cap & (1 << 15) ? "pio " : "",
cap & (1 << 14) ? "slum " : "",
cap & (1 << 13) ? "part " : "");
}
static int ahci_init_one(int pdev)
{
int rc;
struct ahci_probe_ent *probe_ent = NULL;
probe_ent = malloc(sizeof(struct ahci_probe_ent));
memset(probe_ent, 0, sizeof(struct ahci_probe_ent));
probe_ent->dev = pdev;
probe_ent->host_flags = ATA_FLAG_SATA
| ATA_FLAG_NO_LEGACY
| ATA_FLAG_MMIO
| ATA_FLAG_PIO_DMA
| ATA_FLAG_NO_ATAPI;
probe_ent->mmio_base = CONFIG_DWC_AHSATA_BASE_ADDR;
/* initialize adapter */
rc = ahci_host_init(probe_ent);
if (rc)
goto err_out;
ahci_print_info(probe_ent);
/* Save the private struct to block device struct */
sata_dev_desc[pdev].priv = (void *)probe_ent;
return 0;
err_out:
return rc;
}
static int ahci_fill_sg(struct ahci_probe_ent *probe_ent,
u8 port, unsigned char *buf, int buf_len)
{
struct ahci_ioports *pp = &(probe_ent->port[port]);
struct ahci_sg *ahci_sg = pp->cmd_tbl_sg;
u32 sg_count, max_bytes;
int i;
max_bytes = MAX_DATA_BYTES_PER_SG;
sg_count = ((buf_len - 1) / max_bytes) + 1;
if (sg_count > AHCI_MAX_SG) {
printf("Error:Too much sg!\n");
return -1;
}
for (i = 0; i < sg_count; i++) {
ahci_sg->addr =
cpu_to_le32((u32)buf + i * max_bytes);
ahci_sg->addr_hi = 0;
ahci_sg->flags_size = cpu_to_le32(0x3fffff &
(buf_len < max_bytes
? (buf_len - 1)
: (max_bytes - 1)));
ahci_sg++;
buf_len -= max_bytes;
}
return sg_count;
}
static void ahci_fill_cmd_slot(struct ahci_ioports *pp, u32 cmd_slot, u32 opts)
{
struct ahci_cmd_hdr *cmd_hdr = (struct ahci_cmd_hdr *)(pp->cmd_slot +
AHCI_CMD_SLOT_SZ * cmd_slot);
memset(cmd_hdr, 0, AHCI_CMD_SLOT_SZ);
cmd_hdr->opts = cpu_to_le32(opts);
cmd_hdr->status = 0;
cmd_hdr->tbl_addr = cpu_to_le32(pp->cmd_tbl & 0xffffffff);
cmd_hdr->tbl_addr_hi = 0;
}
#define AHCI_GET_CMD_SLOT(c) ((c) ? ffs(c) : 0)
static int ahci_exec_ata_cmd(struct ahci_probe_ent *probe_ent,
u8 port, struct sata_fis_h2d *cfis,
u8 *buf, u32 buf_len, s32 is_write)
{
struct ahci_ioports *pp = &(probe_ent->port[port]);
struct sata_port_regs *port_mmio =
(struct sata_port_regs *)pp->port_mmio;
u32 opts;
int sg_count = 0, cmd_slot = 0;
cmd_slot = AHCI_GET_CMD_SLOT(readl(&(port_mmio->ci)));
if (32 == cmd_slot) {
printf("Can't find empty command slot!\n");
return 0;
}
/* Check xfer length */
if (buf_len > MAX_BYTES_PER_TRANS) {
printf("Max transfer length is %dB\n\r",
MAX_BYTES_PER_TRANS);
return 0;
}
memcpy((u8 *)(pp->cmd_tbl), cfis, sizeof(struct sata_fis_h2d));
if (buf && buf_len)
sg_count = ahci_fill_sg(probe_ent, port, buf, buf_len);
opts = (sizeof(struct sata_fis_h2d) >> 2) | (sg_count << 16);
if (is_write)
opts |= 0x40;
ahci_fill_cmd_slot(pp, cmd_slot, opts);
writel_with_flush(1 << cmd_slot, &(port_mmio->ci));
if (waiting_for_cmd_completed((u8 *)&(port_mmio->ci),
10000, 0x1 << cmd_slot)) {
printf("timeout exit!\n");
return -1;
}
debug("ahci_exec_ata_cmd: %d byte transferred.\n",
pp->cmd_slot->status);
return buf_len;
}
static void ahci_set_feature(u8 dev, u8 port)
{
struct ahci_probe_ent *probe_ent =
(struct ahci_probe_ent *)sata_dev_desc[dev].priv;
struct sata_fis_h2d h2d, *cfis = &h2d;
memset(cfis, 0, sizeof(struct sata_fis_h2d));
cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D;
cfis->pm_port_c = 1 << 7;
cfis->command = ATA_CMD_SET_FEATURES;
cfis->features = SETFEATURES_XFER;
cfis->sector_count = ffs(probe_ent->udma_mask + 1) + 0x3e;
ahci_exec_ata_cmd(probe_ent, port, cfis, NULL, 0, READ_CMD);
}
static int ahci_port_start(struct ahci_probe_ent *probe_ent,
u8 port)
{
struct ahci_ioports *pp = &(probe_ent->port[port]);
struct sata_port_regs *port_mmio =
(struct sata_port_regs *)pp->port_mmio;
u32 port_status;
u32 mem;
int timeout = 10000000;
debug("Enter start port: %d\n", port);
port_status = readl(&(port_mmio->ssts));
debug("Port %d status: %x\n", port, port_status);
if ((port_status & 0xf) != 0x03) {
printf("No Link on this port!\n");
return -1;
}
mem = (u32)malloc(AHCI_PORT_PRIV_DMA_SZ + 1024);
if (!mem) {
free(pp);
printf("No mem for table!\n");
return -ENOMEM;
}
mem = (mem + 0x400) & (~0x3ff); /* Aligned to 1024-bytes */
memset((u8 *)mem, 0, AHCI_PORT_PRIV_DMA_SZ);
/*
* First item in chunk of DMA memory: 32-slot command table,
* 32 bytes each in size
*/
pp->cmd_slot = (struct ahci_cmd_hdr *)mem;
debug("cmd_slot = 0x%x\n", (unsigned int) pp->cmd_slot);
mem += (AHCI_CMD_SLOT_SZ * DWC_AHSATA_MAX_CMD_SLOTS);
/*
* Second item: Received-FIS area, 256-Byte aligned
*/
pp->rx_fis = mem;
mem += AHCI_RX_FIS_SZ;
/*
* Third item: data area for storing a single command
* and its scatter-gather table
*/
pp->cmd_tbl = mem;
debug("cmd_tbl_dma = 0x%x\n", pp->cmd_tbl);
mem += AHCI_CMD_TBL_HDR;
writel_with_flush(0x00004444, &(port_mmio->dmacr));
pp->cmd_tbl_sg = (struct ahci_sg *)mem;
writel_with_flush((u32)pp->cmd_slot, &(port_mmio->clb));
writel_with_flush(pp->rx_fis, &(port_mmio->fb));
/* Enable FRE */
writel_with_flush((SATA_PORT_CMD_FRE | readl(&(port_mmio->cmd))),
&(port_mmio->cmd));
/* Wait device ready */
while ((readl(&(port_mmio->tfd)) & (SATA_PORT_TFD_STS_ERR |
SATA_PORT_TFD_STS_DRQ | SATA_PORT_TFD_STS_BSY))
&& --timeout)
;
if (timeout <= 0) {
debug("Device not ready for BSY, DRQ and"
"ERR in TFD!\n");
return -1;
}
writel_with_flush(PORT_CMD_ICC_ACTIVE | PORT_CMD_FIS_RX |
PORT_CMD_POWER_ON | PORT_CMD_SPIN_UP |
PORT_CMD_START, &(port_mmio->cmd));
debug("Exit start port %d\n", port);
return 0;
}
int init_sata(int dev)
{
int i;
u32 linkmap;
struct ahci_probe_ent *probe_ent = NULL;
if (dev < 0 || dev > (CONFIG_SYS_SATA_MAX_DEVICE - 1)) {
printf("The sata index %d is out of ranges\n\r", dev);
return -1;
}
ahci_init_one(dev);
probe_ent = (struct ahci_probe_ent *)sata_dev_desc[dev].priv;
linkmap = probe_ent->link_port_map;
if (0 == linkmap) {
printf("No port device detected!\n");
return 1;
}
for (i = 0; i < probe_ent->n_ports; i++) {
if ((linkmap >> i) && ((linkmap >> i) & 0x01)) {
if (ahci_port_start(probe_ent, (u8)i)) {
printf("Can not start port %d\n", i);
return 1;
}
probe_ent->hard_port_no = i;
break;
}
}
return 0;
}
static void dwc_ahsata_print_info(int dev)
{
block_dev_desc_t *pdev = &(sata_dev_desc[dev]);
printf("SATA Device Info:\n\r");
#ifdef CONFIG_SYS_64BIT_LBA
printf("S/N: %s\n\rProduct model number: %s\n\r"
"Firmware version: %s\n\rCapacity: %lld sectors\n\r",
pdev->product, pdev->vendor, pdev->revision, pdev->lba);
#else
printf("S/N: %s\n\rProduct model number: %s\n\r"
"Firmware version: %s\n\rCapacity: %ld sectors\n\r",
pdev->product, pdev->vendor, pdev->revision, pdev->lba);
#endif
}
static void dwc_ahsata_identify(int dev, u16 *id)
{
struct ahci_probe_ent *probe_ent =
(struct ahci_probe_ent *)sata_dev_desc[dev].priv;
struct sata_fis_h2d h2d, *cfis = &h2d;
u8 port = probe_ent->hard_port_no;
memset(cfis, 0, sizeof(struct sata_fis_h2d));
cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D;
cfis->pm_port_c = 0x80; /* is command */
cfis->command = ATA_CMD_ID_ATA;
ahci_exec_ata_cmd(probe_ent, port, cfis,
(u8 *)id, ATA_ID_WORDS * 2, READ_CMD);
ata_swap_buf_le16(id, ATA_ID_WORDS);
}
static void dwc_ahsata_xfer_mode(int dev, u16 *id)
{
struct ahci_probe_ent *probe_ent =
(struct ahci_probe_ent *)sata_dev_desc[dev].priv;
probe_ent->pio_mask = id[ATA_ID_PIO_MODES];
probe_ent->udma_mask = id[ATA_ID_UDMA_MODES];
debug("pio %04x, udma %04x\n\r",
probe_ent->pio_mask, probe_ent->udma_mask);
}
static u32 dwc_ahsata_rw_cmd(int dev, u32 start, u32 blkcnt,
u8 *buffer, int is_write)
{
struct ahci_probe_ent *probe_ent =
(struct ahci_probe_ent *)sata_dev_desc[dev].priv;
struct sata_fis_h2d h2d, *cfis = &h2d;
u8 port = probe_ent->hard_port_no;
u32 block;
block = start;
memset(cfis, 0, sizeof(struct sata_fis_h2d));
cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D;
cfis->pm_port_c = 0x80; /* is command */
cfis->command = (is_write) ? ATA_CMD_WRITE : ATA_CMD_READ;
cfis->device = ATA_LBA;
cfis->device |= (block >> 24) & 0xf;
cfis->lba_high = (block >> 16) & 0xff;
cfis->lba_mid = (block >> 8) & 0xff;
cfis->lba_low = block & 0xff;
cfis->sector_count = (u8)(blkcnt & 0xff);
if (ahci_exec_ata_cmd(probe_ent, port, cfis,
buffer, ATA_SECT_SIZE * blkcnt, is_write) > 0)
return blkcnt;
else
return 0;
}
void dwc_ahsata_flush_cache(int dev)
{
struct ahci_probe_ent *probe_ent =
(struct ahci_probe_ent *)sata_dev_desc[dev].priv;
struct sata_fis_h2d h2d, *cfis = &h2d;
u8 port = probe_ent->hard_port_no;
memset(cfis, 0, sizeof(struct sata_fis_h2d));
cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D;
cfis->pm_port_c = 0x80; /* is command */
cfis->command = ATA_CMD_FLUSH;
ahci_exec_ata_cmd(probe_ent, port, cfis, NULL, 0, 0);
}
static u32 dwc_ahsata_rw_cmd_ext(int dev, u32 start, lbaint_t blkcnt,
u8 *buffer, int is_write)
{
struct ahci_probe_ent *probe_ent =
(struct ahci_probe_ent *)sata_dev_desc[dev].priv;
struct sata_fis_h2d h2d, *cfis = &h2d;
u8 port = probe_ent->hard_port_no;
u64 block;
block = (u64)start;
memset(cfis, 0, sizeof(struct sata_fis_h2d));
cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D;
cfis->pm_port_c = 0x80; /* is command */
cfis->command = (is_write) ? ATA_CMD_WRITE_EXT
: ATA_CMD_READ_EXT;
cfis->lba_high_exp = (block >> 40) & 0xff;
cfis->lba_mid_exp = (block >> 32) & 0xff;
cfis->lba_low_exp = (block >> 24) & 0xff;
cfis->lba_high = (block >> 16) & 0xff;
cfis->lba_mid = (block >> 8) & 0xff;
cfis->lba_low = block & 0xff;
cfis->device = ATA_LBA;
cfis->sector_count_exp = (blkcnt >> 8) & 0xff;
cfis->sector_count = blkcnt & 0xff;
if (ahci_exec_ata_cmd(probe_ent, port, cfis, buffer,
ATA_SECT_SIZE * blkcnt, is_write) > 0)
return blkcnt;
else
return 0;
}
u32 dwc_ahsata_rw_ncq_cmd(int dev, u32 start, lbaint_t blkcnt,
u8 *buffer, int is_write)
{
struct ahci_probe_ent *probe_ent =
(struct ahci_probe_ent *)sata_dev_desc[dev].priv;
struct sata_fis_h2d h2d, *cfis = &h2d;
u8 port = probe_ent->hard_port_no;
u64 block;
if (sata_dev_desc[dev].lba48 != 1) {
printf("execute FPDMA command on non-LBA48 hard disk\n\r");
return -1;
}
block = (u64)start;
memset(cfis, 0, sizeof(struct sata_fis_h2d));
cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D;
cfis->pm_port_c = 0x80; /* is command */
cfis->command = (is_write) ? ATA_CMD_FPDMA_WRITE
: ATA_CMD_FPDMA_READ;
cfis->lba_high_exp = (block >> 40) & 0xff;
cfis->lba_mid_exp = (block >> 32) & 0xff;
cfis->lba_low_exp = (block >> 24) & 0xff;
cfis->lba_high = (block >> 16) & 0xff;
cfis->lba_mid = (block >> 8) & 0xff;
cfis->lba_low = block & 0xff;
cfis->device = ATA_LBA;
cfis->features_exp = (blkcnt >> 8) & 0xff;
cfis->features = blkcnt & 0xff;
/* Use the latest queue */
ahci_exec_ata_cmd(probe_ent, port, cfis,
buffer, ATA_SECT_SIZE * blkcnt, is_write);
return blkcnt;
}
void dwc_ahsata_flush_cache_ext(int dev)
{
struct ahci_probe_ent *probe_ent =
(struct ahci_probe_ent *)sata_dev_desc[dev].priv;
struct sata_fis_h2d h2d, *cfis = &h2d;
u8 port = probe_ent->hard_port_no;
memset(cfis, 0, sizeof(struct sata_fis_h2d));
cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D;
cfis->pm_port_c = 0x80; /* is command */
cfis->command = ATA_CMD_FLUSH_EXT;
ahci_exec_ata_cmd(probe_ent, port, cfis, NULL, 0, 0);
}
static void dwc_ahsata_init_wcache(int dev, u16 *id)
{
struct ahci_probe_ent *probe_ent =
(struct ahci_probe_ent *)sata_dev_desc[dev].priv;
if (ata_id_has_wcache(id) && ata_id_wcache_enabled(id))
probe_ent->flags |= SATA_FLAG_WCACHE;
if (ata_id_has_flush(id))
probe_ent->flags |= SATA_FLAG_FLUSH;
if (ata_id_has_flush_ext(id))
probe_ent->flags |= SATA_FLAG_FLUSH_EXT;
}
u32 ata_low_level_rw_lba48(int dev, u32 blknr, lbaint_t blkcnt,
void *buffer, int is_write)
{
u32 start, blks;
u8 *addr;
int max_blks;
start = blknr;
blks = blkcnt;
addr = (u8 *)buffer;
max_blks = ATA_MAX_SECTORS_LBA48;
do {
if (blks > max_blks) {
if (max_blks != dwc_ahsata_rw_cmd_ext(dev, start,
max_blks, addr, is_write))
return 0;
start += max_blks;
blks -= max_blks;
addr += ATA_SECT_SIZE * max_blks;
} else {
if (blks != dwc_ahsata_rw_cmd_ext(dev, start,
blks, addr, is_write))
return 0;
start += blks;
blks = 0;
addr += ATA_SECT_SIZE * blks;
}
} while (blks != 0);
return blkcnt;
}
u32 ata_low_level_rw_lba28(int dev, u32 blknr, lbaint_t blkcnt,
void *buffer, int is_write)
{
u32 start, blks;
u8 *addr;
int max_blks;
start = blknr;
blks = blkcnt;
addr = (u8 *)buffer;
max_blks = ATA_MAX_SECTORS;
do {
if (blks > max_blks) {
if (max_blks != dwc_ahsata_rw_cmd(dev, start,
max_blks, addr, is_write))
return 0;
start += max_blks;
blks -= max_blks;
addr += ATA_SECT_SIZE * max_blks;
} else {
if (blks != dwc_ahsata_rw_cmd(dev, start,
blks, addr, is_write))
return 0;
start += blks;
blks = 0;
addr += ATA_SECT_SIZE * blks;
}
} while (blks != 0);
return blkcnt;
}
/*
* SATA interface between low level driver and command layer
*/
ulong sata_read(int dev, unsigned long blknr, lbaint_t blkcnt, void *buffer)
{
u32 rc;
if (sata_dev_desc[dev].lba48)
rc = ata_low_level_rw_lba48(dev, blknr, blkcnt,
buffer, READ_CMD);
else
rc = ata_low_level_rw_lba28(dev, blknr, blkcnt,
buffer, READ_CMD);
return rc;
}
ulong sata_write(int dev, unsigned long blknr, lbaint_t blkcnt, void *buffer)
{
u32 rc;
struct ahci_probe_ent *probe_ent =
(struct ahci_probe_ent *)sata_dev_desc[dev].priv;
u32 flags = probe_ent->flags;
if (sata_dev_desc[dev].lba48) {
rc = ata_low_level_rw_lba48(dev, blknr, blkcnt,
buffer, WRITE_CMD);
if ((flags & SATA_FLAG_WCACHE) &&
(flags & SATA_FLAG_FLUSH_EXT))
dwc_ahsata_flush_cache_ext(dev);
} else {
rc = ata_low_level_rw_lba28(dev, blknr, blkcnt,
buffer, WRITE_CMD);
if ((flags & SATA_FLAG_WCACHE) &&
(flags & SATA_FLAG_FLUSH))
dwc_ahsata_flush_cache(dev);
}
return rc;
}
int scan_sata(int dev)
{
u8 serial[ATA_ID_SERNO_LEN + 1] = { 0 };
u8 firmware[ATA_ID_FW_REV_LEN + 1] = { 0 };
u8 product[ATA_ID_PROD_LEN + 1] = { 0 };
u16 *id;
u64 n_sectors;
struct ahci_probe_ent *probe_ent =
(struct ahci_probe_ent *)sata_dev_desc[dev].priv;
u8 port = probe_ent->hard_port_no;
block_dev_desc_t *pdev = &(sata_dev_desc[dev]);
id = (u16 *)malloc(ATA_ID_WORDS * 2);
if (!id) {
printf("id malloc failed\n\r");
return -1;
}
/* Identify device to get information */
dwc_ahsata_identify(dev, id);
/* Serial number */
ata_id_c_string(id, serial, ATA_ID_SERNO, sizeof(serial));
memcpy(pdev->product, serial, sizeof(serial));
/* Firmware version */
ata_id_c_string(id, firmware, ATA_ID_FW_REV, sizeof(firmware));
memcpy(pdev->revision, firmware, sizeof(firmware));
/* Product model */
ata_id_c_string(id, product, ATA_ID_PROD, sizeof(product));
memcpy(pdev->vendor, product, sizeof(product));
/* Totoal sectors */
n_sectors = ata_id_n_sectors(id);
pdev->lba = (u32)n_sectors;
pdev->type = DEV_TYPE_HARDDISK;
pdev->blksz = ATA_SECT_SIZE;
pdev->lun = 0 ;
/* Check if support LBA48 */
if (ata_id_has_lba48(id)) {
pdev->lba48 = 1;
debug("Device support LBA48\n\r");
}
/* Get the NCQ queue depth from device */
probe_ent->flags &= (~SATA_FLAG_Q_DEP_MASK);
probe_ent->flags |= ata_id_queue_depth(id);
/* Get the xfer mode from device */
dwc_ahsata_xfer_mode(dev, id);
/* Get the write cache status from device */
dwc_ahsata_init_wcache(dev, id);
/* Set the xfer mode to highest speed */
ahci_set_feature(dev, port);
free((void *)id);
dwc_ahsata_print_info(dev);
is_ready = 1;
return 0;
}

View file

@ -0,0 +1,335 @@
/*
* Copyright (C) 2010 Freescale Semiconductor, Inc.
* Terry Lv <r65388@freescale.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
*/
#ifndef __FSL_SATA_H__
#define __FSL_SATA_H__
#define DWC_AHSATA_MAX_CMD_SLOTS 32
/* Max host controller numbers */
#define SATA_HC_MAX_NUM 4
/* Max command queue depth per host controller */
#define DWC_AHSATA_HC_MAX_CMD 32
/* Max port number per host controller */
#define SATA_HC_MAX_PORT 16
/* Generic Host Register */
/* HBA Capabilities Register */
#define SATA_HOST_CAP_S64A 0x80000000
#define SATA_HOST_CAP_SNCQ 0x40000000
#define SATA_HOST_CAP_SSNTF 0x20000000
#define SATA_HOST_CAP_SMPS 0x10000000
#define SATA_HOST_CAP_SSS 0x08000000
#define SATA_HOST_CAP_SALP 0x04000000
#define SATA_HOST_CAP_SAL 0x02000000
#define SATA_HOST_CAP_SCLO 0x01000000
#define SATA_HOST_CAP_ISS_MASK 0x00f00000
#define SATA_HOST_CAP_ISS_OFFSET 20
#define SATA_HOST_CAP_SNZO 0x00080000
#define SATA_HOST_CAP_SAM 0x00040000
#define SATA_HOST_CAP_SPM 0x00020000
#define SATA_HOST_CAP_PMD 0x00008000
#define SATA_HOST_CAP_SSC 0x00004000
#define SATA_HOST_CAP_PSC 0x00002000
#define SATA_HOST_CAP_NCS 0x00001f00
#define SATA_HOST_CAP_CCCS 0x00000080
#define SATA_HOST_CAP_EMS 0x00000040
#define SATA_HOST_CAP_SXS 0x00000020
#define SATA_HOST_CAP_NP_MASK 0x0000001f
/* Global HBA Control Register */
#define SATA_HOST_GHC_AE 0x80000000
#define SATA_HOST_GHC_IE 0x00000002
#define SATA_HOST_GHC_HR 0x00000001
/* Interrupt Status Register */
/* Ports Implemented Register */
/* AHCI Version Register */
#define SATA_HOST_VS_MJR_MASK 0xffff0000
#define SATA_HOST_VS_MJR_OFFSET 16
#define SATA_HOST_VS_MJR_MNR 0x0000ffff
/* Command Completion Coalescing Control */
#define SATA_HOST_CCC_CTL_TV_MASK 0xffff0000
#define SATA_HOST_CCC_CTL_TV_OFFSET 16
#define SATA_HOST_CCC_CTL_CC_MASK 0x0000ff00
#define SATA_HOST_CCC_CTL_CC_OFFSET 8
#define SATA_HOST_CCC_CTL_INT_MASK 0x000000f8
#define SATA_HOST_CCC_CTL_INT_OFFSET 3
#define SATA_HOST_CCC_CTL_EN 0x00000001
/* Command Completion Coalescing Ports */
/* HBA Capabilities Extended Register */
#define SATA_HOST_CAP2_APST 0x00000004
/* BIST Activate FIS Register */
#define SATA_HOST_BISTAFR_NCP_MASK 0x0000ff00
#define SATA_HOST_BISTAFR_NCP_OFFSET 8
#define SATA_HOST_BISTAFR_PD_MASK 0x000000ff
#define SATA_HOST_BISTAFR_PD_OFFSET 0
/* BIST Control Register */
#define SATA_HOST_BISTCR_FERLB 0x00100000
#define SATA_HOST_BISTCR_TXO 0x00040000
#define SATA_HOST_BISTCR_CNTCLR 0x00020000
#define SATA_HOST_BISTCR_NEALB 0x00010000
#define SATA_HOST_BISTCR_LLC_MASK 0x00000700
#define SATA_HOST_BISTCR_LLC_OFFSET 8
#define SATA_HOST_BISTCR_ERREN 0x00000040
#define SATA_HOST_BISTCR_FLIP 0x00000020
#define SATA_HOST_BISTCR_PV 0x00000010
#define SATA_HOST_BISTCR_PATTERN_MASK 0x0000000f
#define SATA_HOST_BISTCR_PATTERN_OFFSET 0
/* BIST FIS Count Register */
/* BIST Status Register */
#define SATA_HOST_BISTSR_FRAMERR_MASK 0x0000ffff
#define SATA_HOST_BISTSR_FRAMERR_OFFSET 0
#define SATA_HOST_BISTSR_BRSTERR_MASK 0x00ff0000
#define SATA_HOST_BISTSR_BRSTERR_OFFSET 16
/* BIST DWORD Error Count Register */
/* OOB Register*/
#define SATA_HOST_OOBR_WE 0x80000000
#define SATA_HOST_OOBR_cwMin_MASK 0x7f000000
#define SATA_HOST_OOBR_cwMAX_MASK 0x00ff0000
#define SATA_HOST_OOBR_ciMin_MASK 0x0000ff00
#define SATA_HOST_OOBR_ciMax_MASK 0x000000ff
/* Timer 1-ms Register */
/* Global Parameter 1 Register */
#define SATA_HOST_GPARAM1R_ALIGN_M 0x80000000
#define SATA_HOST_GPARAM1R_RX_BUFFER 0x40000000
#define SATA_HOST_GPARAM1R_PHY_DATA_MASK 0x30000000
#define SATA_HOST_GPARAM1R_PHY_RST 0x08000000
#define SATA_HOST_GPARAM1R_PHY_CTRL_MASK 0x07e00000
#define SATA_HOST_GPARAM1R_PHY_STAT_MASK 0x001f8000
#define SATA_HOST_GPARAM1R_LATCH_M 0x00004000
#define SATA_HOST_GPARAM1R_BIST_M 0x00002000
#define SATA_HOST_GPARAM1R_PHY_TYPE 0x00001000
#define SATA_HOST_GPARAM1R_RETURN_ERR 0x00000400
#define SATA_HOST_GPARAM1R_AHB_ENDIAN_MASK 0x00000300
#define SATA_HOST_GPARAM1R_S_HADDR 0X00000080
#define SATA_HOST_GPARAM1R_M_HADDR 0X00000040
/* Global Parameter 2 Register */
#define SATA_HOST_GPARAM2R_DEV_CP 0x00004000
#define SATA_HOST_GPARAM2R_DEV_MP 0x00002000
#define SATA_HOST_GPARAM2R_DEV_ENCODE_M 0x00001000
#define SATA_HOST_GPARAM2R_RXOOB_CLK_M 0x00000800
#define SATA_HOST_GPARAM2R_RXOOB_M 0x00000400
#define SATA_HOST_GPARAM2R_TX_OOB_M 0x00000200
#define SATA_HOST_GPARAM2R_RXOOB_CLK_MASK 0x000001ff
/* Port Parameter Register */
#define SATA_HOST_PPARAMR_TX_MEM_M 0x00000200
#define SATA_HOST_PPARAMR_TX_MEM_S 0x00000100
#define SATA_HOST_PPARAMR_RX_MEM_M 0x00000080
#define SATA_HOST_PPARAMR_RX_MEM_S 0x00000040
#define SATA_HOST_PPARAMR_TXFIFO_DEPTH_MASK 0x00000038
#define SATA_HOST_PPARAMR_RXFIFO_DEPTH_MASK 0x00000007
/* Test Register */
#define SATA_HOST_TESTR_PSEL_MASK 0x00070000
#define SATA_HOST_TESTR_TEST_IF 0x00000001
/* Port Register Descriptions */
/* Port# Command List Base Address Register */
#define SATA_PORT_CLB_CLB_MASK 0xfffffc00
/* Port# Command List Base Address Upper 32-Bits Register */
/* Port# FIS Base Address Register */
#define SATA_PORT_FB_FB_MASK 0xfffffff0
/* Port# FIS Base Address Upper 32-Bits Register */
/* Port# Interrupt Status Register */
#define SATA_PORT_IS_CPDS 0x80000000
#define SATA_PORT_IS_TFES 0x40000000
#define SATA_PORT_IS_HBFS 0x20000000
#define SATA_PORT_IS_HBDS 0x10000000
#define SATA_PORT_IS_IFS 0x08000000
#define SATA_PORT_IS_INFS 0x04000000
#define SATA_PORT_IS_OFS 0x01000000
#define SATA_PORT_IS_IPMS 0x00800000
#define SATA_PORT_IS_PRCS 0x00400000
#define SATA_PORT_IS_DMPS 0x00000080
#define SATA_PORT_IS_PCS 0x00000040
#define SATA_PORT_IS_DPS 0x00000020
#define SATA_PORT_IS_UFS 0x00000010
#define SATA_PORT_IS_SDBS 0x00000008
#define SATA_PORT_IS_DSS 0x00000004
#define SATA_PORT_IS_PSS 0x00000002
#define SATA_PORT_IS_DHRS 0x00000001
/* Port# Interrupt Enable Register */
#define SATA_PORT_IE_CPDE 0x80000000
#define SATA_PORT_IE_TFEE 0x40000000
#define SATA_PORT_IE_HBFE 0x20000000
#define SATA_PORT_IE_HBDE 0x10000000
#define SATA_PORT_IE_IFE 0x08000000
#define SATA_PORT_IE_INFE 0x04000000
#define SATA_PORT_IE_OFE 0x01000000
#define SATA_PORT_IE_IPME 0x00800000
#define SATA_PORT_IE_PRCE 0x00400000
#define SATA_PORT_IE_DMPE 0x00000080
#define SATA_PORT_IE_PCE 0x00000040
#define SATA_PORT_IE_DPE 0x00000020
#define SATA_PORT_IE_UFE 0x00000010
#define SATA_PORT_IE_SDBE 0x00000008
#define SATA_PORT_IE_DSE 0x00000004
#define SATA_PORT_IE_PSE 0x00000002
#define SATA_PORT_IE_DHRE 0x00000001
/* Port# Command Register */
#define SATA_PORT_CMD_ICC_MASK 0xf0000000
#define SATA_PORT_CMD_ASP 0x08000000
#define SATA_PORT_CMD_ALPE 0x04000000
#define SATA_PORT_CMD_DLAE 0x02000000
#define SATA_PORT_CMD_ATAPI 0x01000000
#define SATA_PORT_CMD_APSTE 0x00800000
#define SATA_PORT_CMD_ESP 0x00200000
#define SATA_PORT_CMD_CPD 0x00100000
#define SATA_PORT_CMD_MPSP 0x00080000
#define SATA_PORT_CMD_HPCP 0x00040000
#define SATA_PORT_CMD_PMA 0x00020000
#define SATA_PORT_CMD_CPS 0x00010000
#define SATA_PORT_CMD_CR 0x00008000
#define SATA_PORT_CMD_FR 0x00004000
#define SATA_PORT_CMD_MPSS 0x00002000
#define SATA_PORT_CMD_CCS_MASK 0x00001f00
#define SATA_PORT_CMD_FRE 0x00000010
#define SATA_PORT_CMD_CLO 0x00000008
#define SATA_PORT_CMD_POD 0x00000004
#define SATA_PORT_CMD_SUD 0x00000002
#define SATA_PORT_CMD_ST 0x00000001
/* Port# Task File Data Register */
#define SATA_PORT_TFD_ERR_MASK 0x0000ff00
#define SATA_PORT_TFD_STS_MASK 0x000000ff
#define SATA_PORT_TFD_STS_ERR 0x00000001
#define SATA_PORT_TFD_STS_DRQ 0x00000008
#define SATA_PORT_TFD_STS_BSY 0x00000080
/* Port# Signature Register */
/* Port# Serial ATA Status {SStatus} Register */
#define SATA_PORT_SSTS_IPM_MASK 0x00000f00
#define SATA_PORT_SSTS_SPD_MASK 0x000000f0
#define SATA_PORT_SSTS_DET_MASK 0x0000000f
/* Port# Serial ATA Control {SControl} Register */
#define SATA_PORT_SCTL_IPM_MASK 0x00000f00
#define SATA_PORT_SCTL_SPD_MASK 0x000000f0
#define SATA_PORT_SCTL_DET_MASK 0x0000000f
/* Port# Serial ATA Error {SError} Register */
#define SATA_PORT_SERR_DIAG_X 0x04000000
#define SATA_PORT_SERR_DIAG_F 0x02000000
#define SATA_PORT_SERR_DIAG_T 0x01000000
#define SATA_PORT_SERR_DIAG_S 0x00800000
#define SATA_PORT_SERR_DIAG_H 0x00400000
#define SATA_PORT_SERR_DIAG_C 0x00200000
#define SATA_PORT_SERR_DIAG_D 0x00100000
#define SATA_PORT_SERR_DIAG_B 0x00080000
#define SATA_PORT_SERR_DIAG_W 0x00040000
#define SATA_PORT_SERR_DIAG_I 0x00020000
#define SATA_PORT_SERR_DIAG_N 0x00010000
#define SATA_PORT_SERR_ERR_E 0x00000800
#define SATA_PORT_SERR_ERR_P 0x00000400
#define SATA_PORT_SERR_ERR_C 0x00000200
#define SATA_PORT_SERR_ERR_T 0x00000100
#define SATA_PORT_SERR_ERR_M 0x00000002
#define SATA_PORT_SERR_ERR_I 0x00000001
/* Port# Serial ATA Active {SActive} Register */
/* Port# Command Issue Register */
/* Port# Serial ATA Notification Register */
/* Port# DMA Control Register */
#define SATA_PORT_DMACR_RXABL_MASK 0x0000f000
#define SATA_PORT_DMACR_TXABL_MASK 0x00000f00
#define SATA_PORT_DMACR_RXTS_MASK 0x000000f0
#define SATA_PORT_DMACR_TXTS_MASK 0x0000000f
/* Port# PHY Control Register */
/* Port# PHY Status Register */
#define SATA_HC_CMD_HDR_ENTRY_SIZE sizeof(struct cmd_hdr_entry)
/* DW0
*/
#define CMD_HDR_DI_CFL_MASK 0x0000001f
#define CMD_HDR_DI_CFL_OFFSET 0
#define CMD_HDR_DI_A 0x00000020
#define CMD_HDR_DI_W 0x00000040
#define CMD_HDR_DI_P 0x00000080
#define CMD_HDR_DI_R 0x00000100
#define CMD_HDR_DI_B 0x00000200
#define CMD_HDR_DI_C 0x00000400
#define CMD_HDR_DI_PMP_MASK 0x0000f000
#define CMD_HDR_DI_PMP_OFFSET 12
#define CMD_HDR_DI_PRDTL 0xffff0000
#define CMD_HDR_DI_PRDTL_OFFSET 16
/* prde_fis_len
*/
#define CMD_HDR_PRD_ENTRY_SHIFT 16
#define CMD_HDR_PRD_ENTRY_MASK 0x003f0000
#define CMD_HDR_FIS_LEN_SHIFT 2
/* attribute
*/
#define CMD_HDR_ATTR_RES 0x00000800 /* Reserved bit, should be 1 */
#define CMD_HDR_ATTR_VBIST 0x00000400 /* Vendor BIST */
/* Snoop enable for all descriptor */
#define CMD_HDR_ATTR_SNOOP 0x00000200
#define CMD_HDR_ATTR_FPDMA 0x00000100 /* FPDMA queued command */
#define CMD_HDR_ATTR_RESET 0x00000080 /* Reset - a SRST or device reset */
/* BIST - require the host to enter BIST mode */
#define CMD_HDR_ATTR_BIST 0x00000040
#define CMD_HDR_ATTR_ATAPI 0x00000020 /* ATAPI command */
#define CMD_HDR_ATTR_TAG 0x0000001f /* TAG mask */
#define FLAGS_DMA 0x00000000
#define FLAGS_FPDMA 0x00000001
#define SATA_FLAG_Q_DEP_MASK 0x0000000f
#define SATA_FLAG_WCACHE 0x00000100
#define SATA_FLAG_FLUSH 0x00000200
#define SATA_FLAG_FLUSH_EXT 0x00000400
#define READ_CMD 0
#define WRITE_CMD 1
extern block_dev_desc_t sata_dev_desc[CONFIG_SYS_SATA_MAX_DEVICE];
#endif /* __FSL_SATA_H__ */

View file

@ -0,0 +1,919 @@
/*
* Copyright (C) 2008,2010 Freescale Semiconductor, Inc.
* Dave Liu <daveliu@freescale.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
*/
#include <common.h>
#include <command.h>
#include <asm/io.h>
#include <asm/processor.h>
#include <asm/fsl_serdes.h>
#include <malloc.h>
#include <libata.h>
#include <fis.h>
#include "fsl_sata.h"
extern block_dev_desc_t sata_dev_desc[CONFIG_SYS_SATA_MAX_DEVICE];
#ifndef CONFIG_SYS_SATA1_FLAGS
#define CONFIG_SYS_SATA1_FLAGS FLAGS_DMA
#endif
#ifndef CONFIG_SYS_SATA2_FLAGS
#define CONFIG_SYS_SATA2_FLAGS FLAGS_DMA
#endif
static struct fsl_sata_info fsl_sata_info[] = {
#ifdef CONFIG_SATA1
{CONFIG_SYS_SATA1, CONFIG_SYS_SATA1_FLAGS},
#else
{0, 0},
#endif
#ifdef CONFIG_SATA2
{CONFIG_SYS_SATA2, CONFIG_SYS_SATA2_FLAGS},
#else
{0, 0},
#endif
};
static inline void sdelay(unsigned long sec)
{
unsigned long i;
for (i = 0; i < sec; i++)
mdelay(1000);
}
void dprint_buffer(unsigned char *buf, int len)
{
int i, j;
i = 0;
j = 0;
printf("\n\r");
for (i = 0; i < len; i++) {
printf("%02x ", *buf++);
j++;
if (j == 16) {
printf("\n\r");
j = 0;
}
}
printf("\n\r");
}
static void fsl_sata_dump_sfis(struct sata_fis_d2h *s)
{
printf("Status FIS dump:\n\r");
printf("fis_type: %02x\n\r", s->fis_type);
printf("pm_port_i: %02x\n\r", s->pm_port_i);
printf("status: %02x\n\r", s->status);
printf("error: %02x\n\r", s->error);
printf("lba_low: %02x\n\r", s->lba_low);
printf("lba_mid: %02x\n\r", s->lba_mid);
printf("lba_high: %02x\n\r", s->lba_high);
printf("device: %02x\n\r", s->device);
printf("lba_low_exp: %02x\n\r", s->lba_low_exp);
printf("lba_mid_exp: %02x\n\r", s->lba_mid_exp);
printf("lba_high_exp: %02x\n\r", s->lba_high_exp);
printf("res1: %02x\n\r", s->res1);
printf("sector_count: %02x\n\r", s->sector_count);
printf("sector_count_exp: %02x\n\r", s->sector_count_exp);
}
static int ata_wait_register(volatile unsigned *addr, u32 mask,
u32 val, u32 timeout_msec)
{
int i;
u32 temp;
for (i = 0; (((temp = in_le32(addr)) & mask) != val)
&& i < timeout_msec; i++)
mdelay(1);
return (i < timeout_msec) ? 0 : -1;
}
int init_sata(int dev)
{
u32 length, align;
cmd_hdr_tbl_t *cmd_hdr;
u32 cda;
u32 val32;
fsl_sata_reg_t *reg;
u32 sig;
int i;
fsl_sata_t *sata;
if (dev < 0 || dev > (CONFIG_SYS_SATA_MAX_DEVICE - 1)) {
printf("the sata index %d is out of ranges\n\r", dev);
return -1;
}
#ifdef CONFIG_MPC85xx
if ((dev == 0) && (!is_serdes_configured(SATA1))) {
printf("SATA%d [dev = %d] is not enabled\n", dev+1, dev);
return -1;
}
if ((dev == 1) && (!is_serdes_configured(SATA2))) {
printf("SATA%d [dev = %d] is not enabled\n", dev+1, dev);
return -1;
}
#endif
/* Allocate SATA device driver struct */
sata = (fsl_sata_t *)malloc(sizeof(fsl_sata_t));
if (!sata) {
printf("alloc the sata device struct failed\n\r");
return -1;
}
/* Zero all of the device driver struct */
memset((void *)sata, 0, sizeof(fsl_sata_t));
/* Save the private struct to block device struct */
sata_dev_desc[dev].priv = (void *)sata;
sprintf(sata->name, "SATA%d", dev);
/* Set the controller register base address to device struct */
reg = (fsl_sata_reg_t *)(fsl_sata_info[dev].sata_reg_base);
sata->reg_base = reg;
/* Allocate the command header table, 4 bytes aligned */
length = sizeof(struct cmd_hdr_tbl);
align = SATA_HC_CMD_HDR_TBL_ALIGN;
sata->cmd_hdr_tbl_offset = (void *)malloc(length + align);
if (!sata) {
printf("alloc the command header failed\n\r");
return -1;
}
cmd_hdr = (cmd_hdr_tbl_t *)(((u32)sata->cmd_hdr_tbl_offset + align)
& ~(align - 1));
sata->cmd_hdr = cmd_hdr;
/* Zero all of the command header table */
memset((void *)sata->cmd_hdr_tbl_offset, 0, length + align);
/* Allocate command descriptor for all command */
length = sizeof(struct cmd_desc) * SATA_HC_MAX_CMD;
align = SATA_HC_CMD_DESC_ALIGN;
sata->cmd_desc_offset = (void *)malloc(length + align);
if (!sata->cmd_desc_offset) {
printf("alloc the command descriptor failed\n\r");
return -1;
}
sata->cmd_desc = (cmd_desc_t *)(((u32)sata->cmd_desc_offset + align)
& ~(align - 1));
/* Zero all of command descriptor */
memset((void *)sata->cmd_desc_offset, 0, length + align);
/* Link the command descriptor to command header */
for (i = 0; i < SATA_HC_MAX_CMD; i++) {
cda = ((u32)sata->cmd_desc + SATA_HC_CMD_DESC_SIZE * i)
& ~(CMD_HDR_CDA_ALIGN - 1);
cmd_hdr->cmd_slot[i].cda = cpu_to_le32(cda);
}
/* To have safe state, force the controller offline */
val32 = in_le32(&reg->hcontrol);
val32 &= ~HCONTROL_ONOFF;
val32 |= HCONTROL_FORCE_OFFLINE;
out_le32(&reg->hcontrol, val32);
/* Wait the controller offline */
ata_wait_register(&reg->hstatus, HSTATUS_ONOFF, 0, 1000);
/* Set the command header base address to CHBA register to tell DMA */
out_le32(&reg->chba, (u32)cmd_hdr & ~0x3);
/* Snoop for the command header */
val32 = in_le32(&reg->hcontrol);
val32 |= HCONTROL_HDR_SNOOP;
out_le32(&reg->hcontrol, val32);
/* Disable all of interrupts */
val32 = in_le32(&reg->hcontrol);
val32 &= ~HCONTROL_INT_EN_ALL;
out_le32(&reg->hcontrol, val32);
/* Clear all of interrupts */
val32 = in_le32(&reg->hstatus);
out_le32(&reg->hstatus, val32);
/* Set the ICC, no interrupt coalescing */
out_le32(&reg->icc, 0x01000000);
/* No PM attatched, the SATA device direct connect */
out_le32(&reg->cqpmp, 0);
/* Clear SError register */
val32 = in_le32(&reg->serror);
out_le32(&reg->serror, val32);
/* Clear CER register */
val32 = in_le32(&reg->cer);
out_le32(&reg->cer, val32);
/* Clear DER register */
val32 = in_le32(&reg->der);
out_le32(&reg->der, val32);
/* No device detection or initialization action requested */
out_le32(&reg->scontrol, 0x00000300);
/* Configure the transport layer, default value */
out_le32(&reg->transcfg, 0x08000016);
/* Configure the link layer, default value */
out_le32(&reg->linkcfg, 0x0000ff34);
/* Bring the controller online */
val32 = in_le32(&reg->hcontrol);
val32 |= HCONTROL_ONOFF;
out_le32(&reg->hcontrol, val32);
mdelay(100);
/* print sata device name */
if (!dev)
printf("%s ", sata->name);
else
printf(" %s ", sata->name);
/* Wait PHY RDY signal changed for 500ms */
ata_wait_register(&reg->hstatus, HSTATUS_PHY_RDY,
HSTATUS_PHY_RDY, 500);
/* Check PHYRDY */
val32 = in_le32(&reg->hstatus);
if (val32 & HSTATUS_PHY_RDY) {
sata->link = 1;
} else {
sata->link = 0;
printf("(No RDY)\n\r");
return -1;
}
/* Wait for signature updated, which is 1st D2H */
ata_wait_register(&reg->hstatus, HSTATUS_SIGNATURE,
HSTATUS_SIGNATURE, 10000);
if (val32 & HSTATUS_SIGNATURE) {
sig = in_le32(&reg->sig);
debug("Signature updated, the sig =%08x\n\r", sig);
sata->ata_device_type = ata_dev_classify(sig);
}
/* Check the speed */
val32 = in_le32(&reg->sstatus);
if ((val32 & SSTATUS_SPD_MASK) == SSTATUS_SPD_GEN1)
printf("(1.5 Gbps)\n\r");
else if ((val32 & SSTATUS_SPD_MASK) == SSTATUS_SPD_GEN2)
printf("(3 Gbps)\n\r");
return 0;
}
/* Hardware reset, like Power-on and COMRESET */
void fsl_sata_hardware_reset(u32 reg_base)
{
fsl_sata_reg_t *reg = (fsl_sata_reg_t *)reg_base;
u32 scontrol;
/* Disable the SATA interface and put PHY offline */
scontrol = in_le32(&reg->scontrol);
scontrol = (scontrol & 0x0f0) | 0x304;
out_le32(&reg->scontrol, scontrol);
/* No speed strict */
scontrol = in_le32(&reg->scontrol);
scontrol = scontrol & ~0x0f0;
out_le32(&reg->scontrol, scontrol);
/* Issue PHY wake/reset, Hardware_reset_asserted */
scontrol = in_le32(&reg->scontrol);
scontrol = (scontrol & 0x0f0) | 0x301;
out_le32(&reg->scontrol, scontrol);
mdelay(100);
/* Resume PHY, COMRESET negated, the device initialize hardware
* and execute diagnostics, send good status-signature to host,
* which is D2H register FIS, and then the device enter idle state.
*/
scontrol = in_le32(&reg->scontrol);
scontrol = (scontrol & 0x0f0) | 0x300;
out_le32(&reg->scontrol, scontrol);
mdelay(100);
return;
}
static void fsl_sata_dump_regs(fsl_sata_reg_t *reg)
{
printf("\n\rSATA: %08x\n\r", (u32)reg);
printf("CQR: %08x\n\r", in_le32(&reg->cqr));
printf("CAR: %08x\n\r", in_le32(&reg->car));
printf("CCR: %08x\n\r", in_le32(&reg->ccr));
printf("CER: %08x\n\r", in_le32(&reg->cer));
printf("CQR: %08x\n\r", in_le32(&reg->cqr));
printf("DER: %08x\n\r", in_le32(&reg->der));
printf("CHBA: %08x\n\r", in_le32(&reg->chba));
printf("HStatus: %08x\n\r", in_le32(&reg->hstatus));
printf("HControl: %08x\n\r", in_le32(&reg->hcontrol));
printf("CQPMP: %08x\n\r", in_le32(&reg->cqpmp));
printf("SIG: %08x\n\r", in_le32(&reg->sig));
printf("ICC: %08x\n\r", in_le32(&reg->icc));
printf("SStatus: %08x\n\r", in_le32(&reg->sstatus));
printf("SError: %08x\n\r", in_le32(&reg->serror));
printf("SControl: %08x\n\r", in_le32(&reg->scontrol));
printf("SNotification: %08x\n\r", in_le32(&reg->snotification));
printf("TransCfg: %08x\n\r", in_le32(&reg->transcfg));
printf("TransStatus: %08x\n\r", in_le32(&reg->transstatus));
printf("LinkCfg: %08x\n\r", in_le32(&reg->linkcfg));
printf("LinkCfg1: %08x\n\r", in_le32(&reg->linkcfg1));
printf("LinkCfg2: %08x\n\r", in_le32(&reg->linkcfg2));
printf("LinkStatus: %08x\n\r", in_le32(&reg->linkstatus));
printf("LinkStatus1: %08x\n\r", in_le32(&reg->linkstatus1));
printf("PhyCtrlCfg: %08x\n\r", in_le32(&reg->phyctrlcfg));
printf("SYSPR: %08x\n\r", in_be32(&reg->syspr));
}
static int fsl_ata_exec_ata_cmd(struct fsl_sata *sata, struct sata_fis_h2d *cfis,
int is_ncq, int tag, u8 *buffer, u32 len)
{
cmd_hdr_entry_t *cmd_hdr;
cmd_desc_t *cmd_desc;
sata_fis_h2d_t *h2d;
prd_entry_t *prde;
u32 ext_c_ddc;
u32 prde_count;
u32 val32;
u32 ttl;
fsl_sata_reg_t *reg = sata->reg_base;
int i;
/* Check xfer length */
if (len > SATA_HC_MAX_XFER_LEN) {
printf("max transfer length is 64MB\n\r");
return 0;
}
/* Setup the command descriptor */
cmd_desc = sata->cmd_desc + tag;
/* Get the pointer cfis of command descriptor */
h2d = (sata_fis_h2d_t *)cmd_desc->cfis;
/* Zero the cfis of command descriptor */
memset((void *)h2d, 0, SATA_HC_CMD_DESC_CFIS_SIZE);
/* Copy the cfis from user to command descriptor */
h2d->fis_type = cfis->fis_type;
h2d->pm_port_c = cfis->pm_port_c;
h2d->command = cfis->command;
h2d->features = cfis->features;
h2d->features_exp = cfis->features_exp;
h2d->lba_low = cfis->lba_low;
h2d->lba_mid = cfis->lba_mid;
h2d->lba_high = cfis->lba_high;
h2d->lba_low_exp = cfis->lba_low_exp;
h2d->lba_mid_exp = cfis->lba_mid_exp;
h2d->lba_high_exp = cfis->lba_high_exp;
if (!is_ncq) {
h2d->sector_count = cfis->sector_count;
h2d->sector_count_exp = cfis->sector_count_exp;
} else { /* NCQ */
h2d->sector_count = (u8)(tag << 3);
}
h2d->device = cfis->device;
h2d->control = cfis->control;
/* Setup the PRD table */
prde = (prd_entry_t *)cmd_desc->prdt;
memset((void *)prde, 0, sizeof(struct prdt));
prde_count = 0;
ttl = len;
for (i = 0; i < SATA_HC_MAX_PRD_DIRECT; i++) {
if (!len)
break;
prde->dba = cpu_to_le32((u32)buffer & ~0x3);
debug("dba = %08x\n\r", (u32)buffer);
if (len < PRD_ENTRY_MAX_XFER_SZ) {
ext_c_ddc = PRD_ENTRY_DATA_SNOOP | len;
debug("ext_c_ddc1 = %08x, len = %08x\n\r", ext_c_ddc, len);
prde->ext_c_ddc = cpu_to_le32(ext_c_ddc);
prde_count++;
prde++;
break;
} else {
ext_c_ddc = PRD_ENTRY_DATA_SNOOP; /* 4M bytes */
debug("ext_c_ddc2 = %08x, len = %08x\n\r", ext_c_ddc, len);
prde->ext_c_ddc = cpu_to_le32(ext_c_ddc);
buffer += PRD_ENTRY_MAX_XFER_SZ;
len -= PRD_ENTRY_MAX_XFER_SZ;
prde_count++;
prde++;
}
}
/* Setup the command slot of cmd hdr */
cmd_hdr = (cmd_hdr_entry_t *)&sata->cmd_hdr->cmd_slot[tag];
cmd_hdr->cda = cpu_to_le32((u32)cmd_desc & ~0x3);
val32 = prde_count << CMD_HDR_PRD_ENTRY_SHIFT;
val32 |= sizeof(sata_fis_h2d_t);
cmd_hdr->prde_fis_len = cpu_to_le32(val32);
cmd_hdr->ttl = cpu_to_le32(ttl);
if (!is_ncq) {
val32 = CMD_HDR_ATTR_RES | CMD_HDR_ATTR_SNOOP;
} else {
val32 = CMD_HDR_ATTR_RES | CMD_HDR_ATTR_SNOOP | CMD_HDR_ATTR_FPDMA;
}
tag &= CMD_HDR_ATTR_TAG;
val32 |= tag;
debug("attribute = %08x\n\r", val32);
cmd_hdr->attribute = cpu_to_le32(val32);
/* Make sure cmd desc and cmd slot valid before commmand issue */
sync();
/* PMP*/
val32 = (u32)(h2d->pm_port_c & 0x0f);
out_le32(&reg->cqpmp, val32);
/* Wait no active */
if (ata_wait_register(&reg->car, (1 << tag), 0, 10000))
printf("Wait no active time out\n\r");
/* Issue command */
if (!(in_le32(&reg->cqr) & (1 << tag))) {
val32 = 1 << tag;
out_le32(&reg->cqr, val32);
}
/* Wait command completed for 10s */
if (ata_wait_register(&reg->ccr, (1 << tag), (1 << tag), 10000)) {
if (!is_ncq)
printf("Non-NCQ command time out\n\r");
else
printf("NCQ command time out\n\r");
}
val32 = in_le32(&reg->cer);
if (val32) {
u32 der;
fsl_sata_dump_sfis((struct sata_fis_d2h *)cmd_desc->sfis);
printf("CE at device\n\r");
fsl_sata_dump_regs(reg);
der = in_le32(&reg->der);
out_le32(&reg->cer, val32);
out_le32(&reg->der, der);
}
/* Clear complete flags */
val32 = in_le32(&reg->ccr);
out_le32(&reg->ccr, val32);
return len;
}
static int fsl_ata_exec_reset_cmd(struct fsl_sata *sata, struct sata_fis_h2d *cfis,
int tag, u8 *buffer, u32 len)
{
return 0;
}
static int fsl_sata_exec_cmd(struct fsl_sata *sata, struct sata_fis_h2d *cfis,
enum cmd_type command_type, int tag, u8 *buffer, u32 len)
{
int rc;
if (tag > SATA_HC_MAX_CMD || tag < 0) {
printf("tag is out of range, tag=%d\n\r", tag);
return -1;
}
switch (command_type) {
case CMD_ATA:
rc = fsl_ata_exec_ata_cmd(sata, cfis, 0, tag, buffer, len);
return rc;
case CMD_RESET:
rc = fsl_ata_exec_reset_cmd(sata, cfis, tag, buffer, len);
return rc;
case CMD_NCQ:
rc = fsl_ata_exec_ata_cmd(sata, cfis, 1, tag, buffer, len);
return rc;
case CMD_ATAPI:
case CMD_VENDOR_BIST:
case CMD_BIST:
printf("not support now\n\r");
return -1;
default:
break;
}
return -1;
}
static void fsl_sata_identify(int dev, u16 *id)
{
fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv;
struct sata_fis_h2d h2d, *cfis = &h2d;
memset(cfis, 0, sizeof(struct sata_fis_h2d));
cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D;
cfis->pm_port_c = 0x80; /* is command */
cfis->command = ATA_CMD_ID_ATA;
fsl_sata_exec_cmd(sata, cfis, CMD_ATA, 0, (u8 *)id, ATA_ID_WORDS * 2);
ata_swap_buf_le16(id, ATA_ID_WORDS);
}
static void fsl_sata_xfer_mode(int dev, u16 *id)
{
fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv;
sata->pio = id[ATA_ID_PIO_MODES];
sata->mwdma = id[ATA_ID_MWDMA_MODES];
sata->udma = id[ATA_ID_UDMA_MODES];
debug("pio %04x, mwdma %04x, udma %04x\n\r", sata->pio, sata->mwdma, sata->udma);
}
static void fsl_sata_set_features(int dev)
{
fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv;
struct sata_fis_h2d h2d, *cfis = &h2d;
u8 udma_cap;
memset(cfis, 0, sizeof(struct sata_fis_h2d));
cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D;
cfis->pm_port_c = 0x80; /* is command */
cfis->command = ATA_CMD_SET_FEATURES;
cfis->features = SETFEATURES_XFER;
/* First check the device capablity */
udma_cap = (u8)(sata->udma & 0xff);
debug("udma_cap %02x\n\r", udma_cap);
if (udma_cap == ATA_UDMA6)
cfis->sector_count = XFER_UDMA_6;
if (udma_cap == ATA_UDMA5)
cfis->sector_count = XFER_UDMA_5;
if (udma_cap == ATA_UDMA4)
cfis->sector_count = XFER_UDMA_4;
if (udma_cap == ATA_UDMA3)
cfis->sector_count = XFER_UDMA_3;
fsl_sata_exec_cmd(sata, cfis, CMD_ATA, 0, NULL, 0);
}
static u32 fsl_sata_rw_cmd(int dev, u32 start, u32 blkcnt, u8 *buffer, int is_write)
{
fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv;
struct sata_fis_h2d h2d, *cfis = &h2d;
u32 block;
block = start;
memset(cfis, 0, sizeof(struct sata_fis_h2d));
cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D;
cfis->pm_port_c = 0x80; /* is command */
cfis->command = (is_write) ? ATA_CMD_WRITE : ATA_CMD_READ;
cfis->device = ATA_LBA;
cfis->device |= (block >> 24) & 0xf;
cfis->lba_high = (block >> 16) & 0xff;
cfis->lba_mid = (block >> 8) & 0xff;
cfis->lba_low = block & 0xff;
cfis->sector_count = (u8)(blkcnt & 0xff);
fsl_sata_exec_cmd(sata, cfis, CMD_ATA, 0, buffer, ATA_SECT_SIZE * blkcnt);
return blkcnt;
}
void fsl_sata_flush_cache(int dev)
{
fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv;
struct sata_fis_h2d h2d, *cfis = &h2d;
memset(cfis, 0, sizeof(struct sata_fis_h2d));
cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D;
cfis->pm_port_c = 0x80; /* is command */
cfis->command = ATA_CMD_FLUSH;
fsl_sata_exec_cmd(sata, cfis, CMD_ATA, 0, NULL, 0);
}
static u32 fsl_sata_rw_cmd_ext(int dev, u32 start, u32 blkcnt, u8 *buffer, int is_write)
{
fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv;
struct sata_fis_h2d h2d, *cfis = &h2d;
u64 block;
block = (u64)start;
memset(cfis, 0, sizeof(struct sata_fis_h2d));
cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D;
cfis->pm_port_c = 0x80; /* is command */
cfis->command = (is_write) ? ATA_CMD_WRITE_EXT
: ATA_CMD_READ_EXT;
cfis->lba_high_exp = (block >> 40) & 0xff;
cfis->lba_mid_exp = (block >> 32) & 0xff;
cfis->lba_low_exp = (block >> 24) & 0xff;
cfis->lba_high = (block >> 16) & 0xff;
cfis->lba_mid = (block >> 8) & 0xff;
cfis->lba_low = block & 0xff;
cfis->device = ATA_LBA;
cfis->sector_count_exp = (blkcnt >> 8) & 0xff;
cfis->sector_count = blkcnt & 0xff;
fsl_sata_exec_cmd(sata, cfis, CMD_ATA, 0, buffer, ATA_SECT_SIZE * blkcnt);
return blkcnt;
}
u32 fsl_sata_rw_ncq_cmd(int dev, u32 start, u32 blkcnt, u8 *buffer, int is_write)
{
fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv;
struct sata_fis_h2d h2d, *cfis = &h2d;
int ncq_channel;
u64 block;
if (sata->lba48 != 1) {
printf("execute FPDMA command on non-LBA48 hard disk\n\r");
return -1;
}
block = (u64)start;
memset(cfis, 0, sizeof(struct sata_fis_h2d));
cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D;
cfis->pm_port_c = 0x80; /* is command */
cfis->command = (is_write) ? ATA_CMD_FPDMA_WRITE
: ATA_CMD_FPDMA_READ;
cfis->lba_high_exp = (block >> 40) & 0xff;
cfis->lba_mid_exp = (block >> 32) & 0xff;
cfis->lba_low_exp = (block >> 24) & 0xff;
cfis->lba_high = (block >> 16) & 0xff;
cfis->lba_mid = (block >> 8) & 0xff;
cfis->lba_low = block & 0xff;
cfis->device = ATA_LBA;
cfis->features_exp = (blkcnt >> 8) & 0xff;
cfis->features = blkcnt & 0xff;
if (sata->queue_depth >= SATA_HC_MAX_CMD)
ncq_channel = SATA_HC_MAX_CMD - 1;
else
ncq_channel = sata->queue_depth - 1;
/* Use the latest queue */
fsl_sata_exec_cmd(sata, cfis, CMD_NCQ, ncq_channel, buffer, ATA_SECT_SIZE * blkcnt);
return blkcnt;
}
void fsl_sata_flush_cache_ext(int dev)
{
fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv;
struct sata_fis_h2d h2d, *cfis = &h2d;
memset(cfis, 0, sizeof(struct sata_fis_h2d));
cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D;
cfis->pm_port_c = 0x80; /* is command */
cfis->command = ATA_CMD_FLUSH_EXT;
fsl_sata_exec_cmd(sata, cfis, CMD_ATA, 0, NULL, 0);
}
/* Software reset, set SRST of the Device Control register */
void fsl_sata_software_reset(int dev)
{
return;
}
static void fsl_sata_init_wcache(int dev, u16 *id)
{
fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv;
if (ata_id_has_wcache(id) && ata_id_wcache_enabled(id))
sata->wcache = 1;
if (ata_id_has_flush(id))
sata->flush = 1;
if (ata_id_has_flush_ext(id))
sata->flush_ext = 1;
}
static int fsl_sata_get_wcache(int dev)
{
fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv;
return sata->wcache;
}
static int fsl_sata_get_flush(int dev)
{
fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv;
return sata->flush;
}
static int fsl_sata_get_flush_ext(int dev)
{
fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv;
return sata->flush_ext;
}
u32 ata_low_level_rw_lba48(int dev, u32 blknr, u32 blkcnt, void *buffer, int is_write)
{
u32 start, blks;
u8 *addr;
int max_blks;
start = blknr;
blks = blkcnt;
addr = (u8 *)buffer;
max_blks = ATA_MAX_SECTORS_LBA48;
do {
if (blks > max_blks) {
if (fsl_sata_info[dev].flags != FLAGS_FPDMA)
fsl_sata_rw_cmd_ext(dev, start, max_blks, addr, is_write);
else
fsl_sata_rw_ncq_cmd(dev, start, max_blks, addr, is_write);
start += max_blks;
blks -= max_blks;
addr += ATA_SECT_SIZE * max_blks;
} else {
if (fsl_sata_info[dev].flags != FLAGS_FPDMA)
fsl_sata_rw_cmd_ext(dev, start, blks, addr, is_write);
else
fsl_sata_rw_ncq_cmd(dev, start, blks, addr, is_write);
start += blks;
blks = 0;
addr += ATA_SECT_SIZE * blks;
}
} while (blks != 0);
return blkcnt;
}
u32 ata_low_level_rw_lba28(int dev, u32 blknr, u32 blkcnt, void *buffer, int is_write)
{
u32 start, blks;
u8 *addr;
int max_blks;
start = blknr;
blks = blkcnt;
addr = (u8 *)buffer;
max_blks = ATA_MAX_SECTORS;
do {
if (blks > max_blks) {
fsl_sata_rw_cmd(dev, start, max_blks, addr, is_write);
start += max_blks;
blks -= max_blks;
addr += ATA_SECT_SIZE * max_blks;
} else {
fsl_sata_rw_cmd(dev, start, blks, addr, is_write);
start += blks;
blks = 0;
addr += ATA_SECT_SIZE * blks;
}
} while (blks != 0);
return blkcnt;
}
/*
* SATA interface between low level driver and command layer
*/
ulong sata_read(int dev, u32 blknr, u32 blkcnt, void *buffer)
{
u32 rc;
fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv;
if (sata->lba48)
rc = ata_low_level_rw_lba48(dev, blknr, blkcnt, buffer, READ_CMD);
else
rc = ata_low_level_rw_lba28(dev, blknr, blkcnt, buffer, READ_CMD);
return rc;
}
ulong sata_write(int dev, u32 blknr, u32 blkcnt, void *buffer)
{
u32 rc;
fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv;
if (sata->lba48) {
rc = ata_low_level_rw_lba48(dev, blknr, blkcnt, buffer, WRITE_CMD);
if (fsl_sata_get_wcache(dev) && fsl_sata_get_flush_ext(dev))
fsl_sata_flush_cache_ext(dev);
} else {
rc = ata_low_level_rw_lba28(dev, blknr, blkcnt, buffer, WRITE_CMD);
if (fsl_sata_get_wcache(dev) && fsl_sata_get_flush(dev))
fsl_sata_flush_cache(dev);
}
return rc;
}
int scan_sata(int dev)
{
fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv;
unsigned char serial[ATA_ID_SERNO_LEN + 1];
unsigned char firmware[ATA_ID_FW_REV_LEN + 1];
unsigned char product[ATA_ID_PROD_LEN + 1];
u16 *id;
u64 n_sectors;
/* if no detected link */
if (!sata->link)
return -1;
id = (u16 *)malloc(ATA_ID_WORDS * 2);
if (!id) {
printf("id malloc failed\n\r");
return -1;
}
/* Identify device to get information */
fsl_sata_identify(dev, id);
/* Serial number */
ata_id_c_string(id, serial, ATA_ID_SERNO, sizeof(serial));
memcpy(sata_dev_desc[dev].product, serial, sizeof(serial));
/* Firmware version */
ata_id_c_string(id, firmware, ATA_ID_FW_REV, sizeof(firmware));
memcpy(sata_dev_desc[dev].revision, firmware, sizeof(firmware));
/* Product model */
ata_id_c_string(id, product, ATA_ID_PROD, sizeof(product));
memcpy(sata_dev_desc[dev].vendor, product, sizeof(product));
/* Totoal sectors */
n_sectors = ata_id_n_sectors(id);
sata_dev_desc[dev].lba = (u32)n_sectors;
#ifdef CONFIG_LBA48
/* Check if support LBA48 */
if (ata_id_has_lba48(id)) {
sata->lba48 = 1;
debug("Device support LBA48\n\r");
} else
debug("Device supports LBA28\n\r");
#endif
/* Get the NCQ queue depth from device */
sata->queue_depth = ata_id_queue_depth(id);
/* Get the xfer mode from device */
fsl_sata_xfer_mode(dev, id);
/* Get the write cache status from device */
fsl_sata_init_wcache(dev, id);
/* Set the xfer mode to highest speed */
fsl_sata_set_features(dev);
#ifdef DEBUG
fsl_sata_identify(dev, id);
ata_dump_id(id);
#endif
free((void *)id);
return 0;
}

View file

@ -0,0 +1,333 @@
/*
* Copyright (C) 2007-2008 Freescale Semiconductor, Inc.
* Dave Liu <daveliu@freescale.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
*/
#ifndef __FSL_SATA_H__
#define __FSL_SATA_H__
#define SATA_HC_MAX_NUM 4 /* Max host controller numbers */
#define SATA_HC_MAX_CMD 16 /* Max command queue depth per host controller */
#define SATA_HC_MAX_PORT 16 /* Max port number per host controller */
/*
* SATA Host Controller Registers
*/
typedef struct fsl_sata_reg {
/* SATA command registers */
u32 cqr; /* Command queue register */
u8 res1[0x4];
u32 car; /* Command active register */
u8 res2[0x4];
u32 ccr; /* Command completed register */
u8 res3[0x4];
u32 cer; /* Command error register */
u8 res4[0x4];
u32 der; /* Device error register */
u32 chba; /* Command header base address */
u32 hstatus; /* Host status register */
u32 hcontrol; /* Host control register */
u32 cqpmp; /* Port number queue register */
u32 sig; /* Signature register */
u32 icc; /* Interrupt coalescing control register */
u8 res5[0xc4];
/* SATA supperset registers */
u32 sstatus; /* SATA interface status register */
u32 serror; /* SATA interface error register */
u32 scontrol; /* SATA interface control register */
u32 snotification; /* SATA interface notification register */
u8 res6[0x30];
/* SATA control status registers */
u32 transcfg; /* Transport layer configuration */
u32 transstatus; /* Transport layer status */
u32 linkcfg; /* Link layer configuration */
u32 linkcfg1; /* Link layer configuration1 */
u32 linkcfg2; /* Link layer configuration2 */
u32 linkstatus; /* Link layer status */
u32 linkstatus1; /* Link layer status1 */
u32 phyctrlcfg; /* PHY control configuration */
u8 res7[0x2b0];
/* SATA system control registers */
u32 syspr; /* System priority register - big endian */
u8 res8[0xbec];
} __attribute__ ((packed)) fsl_sata_reg_t;
/* HStatus register
*/
#define HSTATUS_ONOFF 0x80000000 /* Online/offline status */
#define HSTATUS_FORCE_OFFLINE 0x40000000 /* In process going offline */
#define HSTATUS_BIST_ERR 0x20000000
/* Fatal error */
#define HSTATUS_MASTER_ERR 0x00004000
#define HSTATUS_DATA_UNDERRUN 0x00002000
#define HSTATUS_DATA_OVERRUN 0x00001000
#define HSTATUS_CRC_ERR_TX 0x00000800
#define HSTATUS_CRC_ERR_RX 0x00000400
#define HSTATUS_FIFO_OVERFLOW_TX 0x00000200
#define HSTATUS_FIFO_OVERFLOW_RX 0x00000100
#define HSTATUS_FATAL_ERR_ALL (HSTATUS_MASTER_ERR | \
HSTATUS_DATA_UNDERRUN | \
HSTATUS_DATA_OVERRUN | \
HSTATUS_CRC_ERR_TX | \
HSTATUS_CRC_ERR_RX | \
HSTATUS_FIFO_OVERFLOW_TX | \
HSTATUS_FIFO_OVERFLOW_RX)
/* Interrupt status */
#define HSTATUS_FATAL_ERR 0x00000020
#define HSTATUS_PHY_RDY 0x00000010
#define HSTATUS_SIGNATURE 0x00000008
#define HSTATUS_SNOTIFY 0x00000004
#define HSTATUS_DEVICE_ERR 0x00000002
#define HSTATUS_CMD_COMPLETE 0x00000001
/* HControl register
*/
#define HCONTROL_ONOFF 0x80000000 /* Online or offline request */
#define HCONTROL_FORCE_OFFLINE 0x40000000 /* Force offline request */
#define HCONTROL_ENTERPRISE_EN 0x10000000 /* Enterprise mode enabled */
#define HCONTROL_HDR_SNOOP 0x00000400 /* Command header snoop */
#define HCONTROL_PMP_ATTACHED 0x00000200 /* Port multiplier attached */
/* Interrupt enable */
#define HCONTROL_FATAL_ERR 0x00000020
#define HCONTROL_PHY_RDY 0x00000010
#define HCONTROL_SIGNATURE 0x00000008
#define HCONTROL_SNOTIFY 0x00000004
#define HCONTROL_DEVICE_ERR 0x00000002
#define HCONTROL_CMD_COMPLETE 0x00000001
#define HCONTROL_INT_EN_ALL (HCONTROL_FATAL_ERR | \
HCONTROL_PHY_RDY | \
HCONTROL_SIGNATURE | \
HCONTROL_SNOTIFY | \
HCONTROL_DEVICE_ERR | \
HCONTROL_CMD_COMPLETE)
/* SStatus register
*/
#define SSTATUS_IPM_MASK 0x00000780
#define SSTATUS_IPM_NOPRESENT 0x00000000
#define SSTATUS_IPM_ACTIVE 0x00000080
#define SSTATUS_IPM_PATIAL 0x00000100
#define SSTATUS_IPM_SLUMBER 0x00000300
#define SSTATUS_SPD_MASK 0x000000f0
#define SSTATUS_SPD_GEN1 0x00000010
#define SSTATUS_SPD_GEN2 0x00000020
#define SSTATUS_DET_MASK 0x0000000f
#define SSTATUS_DET_NODEVICE 0x00000000
#define SSTATUS_DET_DISCONNECT 0x00000001
#define SSTATUS_DET_CONNECT 0x00000003
#define SSTATUS_DET_PHY_OFFLINE 0x00000004
/* SControl register
*/
#define SCONTROL_SPM_MASK 0x0000f000
#define SCONTROL_SPM_GO_PARTIAL 0x00001000
#define SCONTROL_SPM_GO_SLUMBER 0x00002000
#define SCONTROL_SPM_GO_ACTIVE 0x00004000
#define SCONTROL_IPM_MASK 0x00000f00
#define SCONTROL_IPM_NO_RESTRICT 0x00000000
#define SCONTROL_IPM_PARTIAL 0x00000100
#define SCONTROL_IPM_SLUMBER 0x00000200
#define SCONTROL_IPM_PART_SLUM 0x00000300
#define SCONTROL_SPD_MASK 0x000000f0
#define SCONTROL_SPD_NO_RESTRICT 0x00000000
#define SCONTROL_SPD_GEN1 0x00000010
#define SCONTROL_SPD_GEN2 0x00000020
#define SCONTROL_DET_MASK 0x0000000f
#define SCONTROL_DET_HRESET 0x00000001
#define SCONTROL_DET_DISABLE 0x00000004
/* TransCfg register
*/
#define TRANSCFG_DFIS_SIZE_SHIFT 16
#define TRANSCFG_RX_WATER_MARK_MASK 0x0000001f
/* PhyCtrlCfg register
*/
#define PHYCTRLCFG_FPRFTI_MASK 0x00000018
#define PHYCTRLCFG_LOOPBACK_MASK 0x0000000e
/*
* Command Header Entry
*/
typedef struct cmd_hdr_entry {
u32 cda; /* Command Descriptor Address, 4 bytes aligned */
u32 prde_fis_len; /* Number of PRD entries and FIS length */
u32 ttl; /* Total transfer length */
u32 attribute; /* the attribute of command */
} __attribute__ ((packed)) cmd_hdr_entry_t;
#define SATA_HC_CMD_HDR_ENTRY_SIZE sizeof(struct cmd_hdr_entry)
/* cda
*/
#define CMD_HDR_CDA_ALIGN 4
/* prde_fis_len
*/
#define CMD_HDR_PRD_ENTRY_SHIFT 16
#define CMD_HDR_PRD_ENTRY_MASK 0x003f0000
#define CMD_HDR_FIS_LEN_SHIFT 2
/* attribute
*/
#define CMD_HDR_ATTR_RES 0x00000800 /* Reserved bit, should be 1 */
#define CMD_HDR_ATTR_VBIST 0x00000400 /* Vendor BIST */
#define CMD_HDR_ATTR_SNOOP 0x00000200 /* Snoop enable for all descriptor */
#define CMD_HDR_ATTR_FPDMA 0x00000100 /* FPDMA queued command */
#define CMD_HDR_ATTR_RESET 0x00000080 /* Reset - a SRST or device reset */
#define CMD_HDR_ATTR_BIST 0x00000040 /* BIST - require the host to enter BIST mode */
#define CMD_HDR_ATTR_ATAPI 0x00000020 /* ATAPI command */
#define CMD_HDR_ATTR_TAG 0x0000001f /* TAG mask */
/* command type
*/
enum cmd_type {
CMD_VENDOR_BIST,
CMD_BIST,
CMD_RESET, /* SRST or device reset */
CMD_ATAPI,
CMD_NCQ,
CMD_ATA, /* None of all above */
};
/*
* Command Header Table
*/
typedef struct cmd_hdr_tbl {
cmd_hdr_entry_t cmd_slot[SATA_HC_MAX_CMD];
} __attribute__ ((packed)) cmd_hdr_tbl_t;
#define SATA_HC_CMD_HDR_TBL_SIZE sizeof(struct cmd_hdr_tbl)
#define SATA_HC_CMD_HDR_TBL_ALIGN 4
/*
* PRD entry - Physical Region Descriptor entry
*/
typedef struct prd_entry {
u32 dba; /* Data base address, 4 bytes aligned */
u32 res1;
u32 res2;
u32 ext_c_ddc; /* Indirect PRD flags, snoop and data word count */
} __attribute__ ((packed)) prd_entry_t;
#define SATA_HC_CMD_DESC_PRD_SIZE sizeof(struct prd_entry)
/* dba
*/
#define PRD_ENTRY_DBA_ALIGN 4
/* ext_c_ddc
*/
#define PRD_ENTRY_EXT 0x80000000 /* extension flag */
#ifdef CONFIG_FSL_SATA_V2
#define PRD_ENTRY_DATA_SNOOP 0x10000000 /* Data snoop enable */
#else
#define PRD_ENTRY_DATA_SNOOP 0x00400000 /* Data snoop enable */
#endif
#define PRD_ENTRY_LEN_MASK 0x003fffff /* Data word count */
#define PRD_ENTRY_MAX_XFER_SZ (PRD_ENTRY_LEN_MASK + 1)
/*
* This SATA host controller supports a max of 16 direct PRD entries, but if use
* chained indirect PRD entries, then the contollers supports upto a max of 63
* entries including direct and indirect PRD entries.
* The PRDT is an array of 63 PRD entries contigiously, but the PRD entries#15
* will be setup as an indirect descriptor, pointing to it's next (contigious)
* PRD entries#16.
*/
#define SATA_HC_MAX_PRD 63 /* Max PRD entry numbers per command */
#define SATA_HC_MAX_PRD_DIRECT 16 /* Direct PRDT entries */
#define SATA_HC_MAX_PRD_USABLE (SATA_HC_MAX_PRD - 1)
#define SATA_HC_MAX_XFER_LEN 0x4000000
/*
* PRDT - Physical Region Descriptor Table
*/
typedef struct prdt {
prd_entry_t prdt[SATA_HC_MAX_PRD];
} __attribute__ ((packed)) prdt_t;
/*
* Command Descriptor
*/
#define SATA_HC_CMD_DESC_CFIS_SIZE 32 /* bytes */
#define SATA_HC_CMD_DESC_SFIS_SIZE 32 /* bytes */
#define SATA_HC_CMD_DESC_ACMD_SIZE 16 /* bytes */
#define SATA_HC_CMD_DESC_RES 16 /* bytes */
typedef struct cmd_desc {
u8 cfis[SATA_HC_CMD_DESC_CFIS_SIZE];
u8 sfis[SATA_HC_CMD_DESC_SFIS_SIZE];
u8 acmd[SATA_HC_CMD_DESC_ACMD_SIZE];
u8 res[SATA_HC_CMD_DESC_RES];
prd_entry_t prdt[SATA_HC_MAX_PRD];
} __attribute__ ((packed)) cmd_desc_t;
#define SATA_HC_CMD_DESC_SIZE sizeof(struct cmd_desc)
#define SATA_HC_CMD_DESC_ALIGN 4
/*
* SATA device driver info
*/
typedef struct fsl_sata_info {
u32 sata_reg_base;
u32 flags;
} fsl_sata_info_t;
#define FLAGS_DMA 0x00000000
#define FLAGS_FPDMA 0x00000001
/*
* SATA device driver struct
*/
typedef struct fsl_sata {
char name[12];
fsl_sata_reg_t *reg_base; /* the base address of controller register */
void *cmd_hdr_tbl_offset; /* alloc address of command header table */
cmd_hdr_tbl_t *cmd_hdr; /* aligned address of command header table */
void *cmd_desc_offset; /* alloc address of command descriptor */
cmd_desc_t *cmd_desc; /* aligned address of command descriptor */
int link; /* PHY link status */
/* device attribute */
int ata_device_type; /* device type */
int lba48;
int queue_depth; /* Max NCQ queue depth */
u16 pio;
u16 mwdma;
u16 udma;
int wcache;
int flush;
int flush_ext;
} fsl_sata_t;
#define READ_CMD 0
#define WRITE_CMD 1
#endif /* __FSL_SATA_H__ */

View file

@ -0,0 +1,364 @@
/*
* Faraday FTIDE020 ATA Controller (AHB)
*
* (C) Copyright 2011 Andes Technology
* Greentime Hu <greentime@andestech.com>
* Macpaul Lin <macpaul@andestech.com>
* Kuo-Wei Chou <kwchou@andestech.com>
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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
*
*/
/* ftide020.c - ide support functions for the FTIDE020_S controller */
#include <config.h>
#include <common.h>
#include <ata.h>
#include <ide.h>
#include <asm/io.h>
#include <api_public.h>
#include "ftide020.h"
/* base address */
#define FTIDE_BASE CONFIG_SYS_ATA_BASE_ADDR
/*
* data address - The CMD and DATA use the same FIFO in FTIDE020_S
* FTIDE_DATA = CONFIG_SYS_ATA_BASE_ADDR + CONFIG_SYS_ATA_DATA_OFFSET
* = &ftide020->rw_fifo
*/
#define FTIDE_DATA (&ftide020->rw_fifo)
/* command and data I/O macros */
/* 0x0 - DATA FIFO */
#define WRITE_DATA(x) outl((x), &ftide020->rw_fifo) /* 0x00 */
#define READ_DATA() inl(&ftide020->rw_fifo) /* 0x00 */
/* 0x04 - R: Status Reg, W: CMD_FIFO */
#define WRITE_CMD(x) outl((x), &ftide020->cmd_fifo) /* 0x04 */
#define READ_STATUS() inl(&ftide020->cmd_fifo) /* 0x04 */
void ftide_set_device(int cx8, int dev)
{
static struct ftide020_s *ftide020 = (struct ftide020_s *) FTIDE_BASE;
WRITE_CMD(SET_DEV_CMD | IDE_SET_CX8(cx8) | dev);
}
unsigned char ide_read_register(int dev, unsigned int port)
{
static struct ftide020_s *ftide020 = (struct ftide020_s *) FTIDE_BASE;
ftide_set_device(0, dev);
WRITE_CMD(READ_REG_CMD | IDE_REG_CS_READ(CONFIG_IDE_REG_CS) |
IDE_REG_DA_WRITE(port));
return READ_DATA() & 0xff;
}
void ide_write_register(int dev, unsigned int port, unsigned char val)
{
static struct ftide020_s *ftide020 = (struct ftide020_s *) FTIDE_BASE;
ftide_set_device(0, dev);
WRITE_CMD(WRITE_REG_CMD | IDE_REG_CS_WRITE(CONFIG_IDE_REG_CS) |
IDE_REG_DA_WRITE(port) | val);
}
void ide_write_data(int dev, ulong *sect_buf, int words)
{
static struct ftide020_s *ftide020 = (struct ftide020_s *) FTIDE_BASE;
ftide_set_device(0, dev);
WRITE_CMD(WRITE_DATA_CMD | ((words << 2) - 1));
/* block write */
outsl(FTIDE_DATA, sect_buf, words);
}
void ide_read_data(int dev, ulong *sect_buf, int words)
{
static struct ftide020_s *ftide020 = (struct ftide020_s *) FTIDE_BASE;
ftide_set_device(0, dev);
WRITE_CMD(READ_DATA_CMD | ((words << 2) - 1));
/* block read */
insl(FTIDE_DATA, sect_buf, words);
}
void ftide_dfifo_ready(ulong *time)
{
static struct ftide020_s *ftide020 = (struct ftide020_s *) FTIDE_BASE;
while (!(READ_STATUS() & STATUS_RFE)) {
if (*time-- == 0)
break;
udelay(100);
}
}
extern ulong ide_bus_offset[CONFIG_SYS_IDE_MAXBUS];
/* Reset_IDE_controller */
static void reset_ide_controller(void)
{
static struct ftide020_s *ftide020 = (struct ftide020_s *) FTIDE_BASE;
unsigned int val;
val = inl(&ftide020->cr);
val |= CONTROL_RST;
outl(val, &ftide020->cr);
/* wait until reset OK, this is poor HW design */
mdelay(50);
val &= ~(CONTROL_RST);
outl(val, &ftide020->cr);
mdelay(50);
val |= CONTROL_SRST;
outl(val, &ftide020->cr);
/* wait until reset OK, this is poor HW design */
mdelay(50);
val &= ~(CONTROL_SRST);
outl(val, &ftide020->cr);
/* IORDY enable for PIO, for 2 device */
val |= (CONTROL_IRE0 | CONTROL_IRE1);
outl(val, &ftide020->cr);
}
/* IDE clock frequence */
uint ftide_clock_freq(void)
{
/*
* todo: To aquire dynamic system frequency is dependend on the power
* management unit which the ftide020 is connected to. In current,
* there are only few PMU supports in u-boot.
* So this function is wait for future enhancement.
*/
return 100;
}
/* Calculate Timing Registers */
static unsigned int timing_cal(u16 t0, u16 t1, u16 t2, u16 t4)
{
unsigned int val, ahb_ns = 8;
u8 TEOC, T1, T2, T4;
T1 = (u8) (t1 / ahb_ns);
if ((T1 * ahb_ns) == t1)
T1--;
T2 = (u8) (t2 / ahb_ns);
if ((T2 * ahb_ns) == t2)
T2--;
T4 = (u8) (t4 / ahb_ns);
if ((T4 * ahb_ns) == t4)
T4--;
TEOC = (u8) (t0 / ahb_ns);
if ((TEOC * ahb_ns) == t0)
TEOC--;
TEOC = ((TEOC > (T1 + T2 + T4)) ? (TEOC - (T1 + T2 + T4)) : 0);
/*
* Here the fields in data timing registers in PIO mode
* is accessed the same way as command timing registers.
*/
val = DT_REG_PIO_T1(T1) |
DT_REG_PIO_T2(T2) |
DT_REG_PIO_T4(T4) |
DT_REG_PIO_TEOC(TEOC);
return val;
}
/* Set Timing Register */
static unsigned int set_mode_timing(u8 dev, u8 id, u8 mode)
{
static struct ftide020_s *ftide020 = (struct ftide020_s *) FTIDE_BASE;
u16 t0, t1, t2, t4;
u8 tcyc, tcvs, tmli, tenv, tack, trp;
unsigned int val, sysclk = 8;
if (id >= TATOL_TIMING)
return 0;
sysclk = ftide_clock_freq();
switch (id) {
case CMD_TIMING:
if (mode < REG_MODE) {
t0 = REG_ACCESS_TIMING[REG_T0][mode];
t1 = REG_ACCESS_TIMING[REG_T1][mode];
t2 = REG_ACCESS_TIMING[REG_T2][mode];
t4 = REG_ACCESS_TIMING[REG_T4][mode];
val = timing_cal(t0, t1, t2, t4);
outl(val, (dev ? &ftide020->ctrd1 : &ftide020->ctrd0));
return 1;
} else
return 0;
case PIO_TIMING:
if (mode < PIO_MODE) {
t0 = PIO_ACCESS_TIMING[PIO_T0][mode];
t1 = PIO_ACCESS_TIMING[PIO_T1][mode];
t2 = PIO_ACCESS_TIMING[PIO_T2][mode];
t4 = PIO_ACCESS_TIMING[PIO_T4][mode];
val = timing_cal(t0, t1, t2, t4);
outl(val, (dev ? &ftide020->dtrd1 : &ftide020->dtrd0));
return 1;
} else
return 0;
case DMA_TIMING:
if (mode < UDMA_MODE) {
/*
* 0.999 is ceiling
* for tcyc, tcvs, tmli, tenv, trp, tack
*/
tcyc = (u8) (((UDMA_ACCESS_TIMING[UDMA_TCYC][mode] \
* sysclk) + 9990) / 10000);
tcvs = (u8) (((UDMA_ACCESS_TIMING[UDMA_TCVS][mode] \
* sysclk) + 9990) / 10000);
tmli = (u8) (((UDMA_ACCESS_TIMING[UDMA_TMLI][mode] \
* sysclk) + 9990) / 10000);
tenv = (u8) (((UDMA_ACCESS_TIMING[UDMA_TENV][mode] \
* sysclk) + 9990) / 10000);
trp = (u8) (((UDMA_ACCESS_TIMING[UDMA_TRP][mode] \
* sysclk) + 9990) / 10000);
tack = (u8) (((UDMA_ACCESS_TIMING[UDMA_TACK][mode] \
* sysclk) + 9990) / 10000);
val = DT_REG_UDMA_TENV((tenv > 0) ? (tenv - 1) : 0) |
DT_REG_UDMA_TMLI((tmli > 0) ? (tmli - 1) : 0) |
DT_REG_UDMA_TCYC((tcyc > 0) ? (tcyc - 1) : 0) |
DT_REG_UDMA_TACK((tack > 0) ? (tack - 1) : 0) |
DT_REG_UDMA_TCVS((tcvs > 0) ? (tcvs - 1) : 0) |
DT_REG_UDMA_TRP((trp > 0) ? (trp - 1) : 0);
outl(val, (dev ? &ftide020->dtrd1 : &ftide020->dtrd0));
return 1;
} else
return 0;
default:
return 0;
}
}
static void ftide_read_hwrev(void)
{
static struct ftide020_s *ftide020 = (struct ftide020_s *) FTIDE_BASE;
unsigned int rev;
rev = inl(&ftide020->revision);
}
static int ftide_controller_probe(void)
{
static struct ftide020_s *ftide020 = (struct ftide020_s *) FTIDE_BASE;
unsigned int bak;
bak = inl(&ftide020->ctrd1);
/* probing by using shorter setup time */
outl(CONFIG_CTRD1_PROBE_T1, &ftide020->ctrd1);
if ((inl(&ftide020->ctrd1) & 0xff) != CONFIG_CTRD1_PROBE_T1) {
outl(bak, &ftide020->ctrd1);
return 0;
}
/* probing by using longer setup time */
outl(CONFIG_CTRD1_PROBE_T2, &ftide020->ctrd1);
if ((inl(&ftide020->ctrd1) & 0xff) != CONFIG_CTRD1_PROBE_T2) {
outl(bak, &ftide020->ctrd1);
return 0;
}
outl(bak, &ftide020->ctrd1);
return 1;
}
/* ide_preinit() was migrated from linux driver ide_probe_for_ftide() */
int ide_preinit(void)
{
static struct ftide020_s *ftide020 = (struct ftide020_s *) FTIDE_BASE;
int status;
unsigned int val;
int i;
status = 1;
for (i = 0; i < CONFIG_SYS_IDE_MAXBUS; i++)
ide_bus_offset[i] = -ATA_STATUS;
/* auto-detect IDE controller */
if (ftide_controller_probe()) {
printf("FTIDE020_S\n");
} else {
printf("FTIDE020_S ATA controller not found.\n");
return API_ENODEV;
}
/* check HW IP revision */
ftide_read_hwrev();
/* set FIFO threshold */
outl(((WRITE_FIFO - RX_THRESH) << 16) | RX_THRESH, &ftide020->dmatirr);
/* set Device_0 PIO_4 timing */
set_mode_timing(0, CMD_TIMING, REG_MODE4);
set_mode_timing(0, PIO_TIMING, PIO_MODE4);
/* set Device_1 PIO_4 timing */
set_mode_timing(1, CMD_TIMING, REG_MODE4);
set_mode_timing(1, PIO_TIMING, PIO_MODE4);
/* from E-bios */
/* little endian */
outl(0x0, &ftide020->cr);
mdelay(10);
outl(0x0fff0fff, &ftide020->ahbtr);
mdelay(10);
/* Enable controller Interrupt */
val = inl(&ftide020->cr);
/* Enable: IDE IRQ, IDE Terminate ERROR IRQ, AHB Timeout error IRQ */
val |= (CONTROL_IIE | CONTROL_TERIE | CONTROL_AERIE);
outl(val, &ftide020->cr);
status = 0;
return status;
}
void ide_set_reset(int flag)
{
debug("ide_set_reset()\n");
reset_ide_controller();
return;
}

View file

@ -0,0 +1,283 @@
/*
* Faraday FTIDE020_s ATA Controller (AHB)
*
* (C) Copyright 2011 Andes Technology
* Greentime Hu <greentime@andestech.com>
* Macpaul Lin <macpaul@andestech.com>
* Kuo-Wei Chou <kwchou@andestech.com>
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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
*
*/
#ifndef __FTIDE020_H
#define __FTIDE020_H
/* ftide020.h - ide support functions for the FTIDE020_S controller */
/* ATA controller register offset */
struct ftide020_s {
unsigned int rw_fifo; /* 0x00 - READ/WRITE FIFO */
unsigned int cmd_fifo; /* 0x04 - R: Status Reg, W: CMD_FIFO */
unsigned int cr; /* 0x08 - Control Reg */
unsigned int dmatirr; /* 0x0c - DMA Threshold/Interrupt Reg */
unsigned int ctrd0; /* 0x10 - Command Timing Reg Device 0 */
unsigned int dtrd0; /* 0x14 - Data Timing Reg Device 0 */
unsigned int ctrd1; /* 0x18 - Command Timing Reg Device 1 */
unsigned int dtrd1; /* 0x1c - Data Timing Reg Device 1 */
unsigned int ahbtr; /* 0x20 - AHB Timeout Reg */
unsigned int RESVD0; /* 0x24 */
unsigned int RESVD1; /* 0x28 */
unsigned int RESVD2; /* 0x2c */
unsigned int f_cfifo; /* 0x30 - Feature Info of CMD_FIFO */
unsigned int f_wfifo; /* 0x34 - Feature Info of WRITE_FIFO */
unsigned int f_rfifo; /* 0x3c - Feature Info of READ_FIFO */
unsigned int revision; /* 0x38 - Revision No. of FTIDE020_S */
};
/* reference parameters */
#define CONFIG_IDE_REG_CS 0x2 /* ref: ATA spec chaper 10, table 42 */
#define CONFIG_CTRD1_PROBE_T1 0x2
#define CONFIG_CTRD1_PROBE_T2 0x5
/* status register - 0x04 */
#define STATUS_CSEL (1 << 0) /* CSEL */
#define STATUS_CS(x) (((x) >> 1) & 0x3) /* CS#[1:0] */
#define STATUS_DMACK (1 << 3) /* DMACK# */
#define STATUS_DMARQ (1 << 4) /* DMA req */
#define STATUS_INTRQ (1 << 5) /* INT req */
#define STATUS_DIOR (1 << 6) /* DIOR */
#define STATUS_IORDY (1 << 7) /* I/O ready */
#define STATUS_DIOW (1 << 8) /* DIOW# */
#define STATUS_PDIAG (1 << 9) /* PDIAG */
#define STATUS_DASP (1 << 10) /* DASP# */
#define STATUS_DEV (1 << 11) /* selected device */
#define STATUS_PIO (1 << 12) /* PIO in progress */
#define STATUS_DMA (1 << 13) /* DMA in progress */
#define STATUS_WFE (1 << 14) /* write fifo full */
#define STATUS_RFE (1 << 15) /* read fifo empty */
#define STATUS_COUNTER(x) (((x) >> 16) & 0x3fff) /* data tx counter */
#define STATUS_ERR (1 << 30) /* trasfer terminated */
#define STATUS_AER (1 << 31) /* AHB timeout indicate */
/* Control register - 0x08 */
#define CONTROL_TYPE_PIO 0x0
#define CONTROL_TYPE_UDMA 0x1
/* Device 0 */
#define CONTROL_TYP0(x) (((x) & 0x7) << 0)
#define CONTROL_IRE0 (1 << 3) /* enable IORDY for PIO */
#define CONTROL_RESVD_DW0 (1 << 4) /* Reserved - DW0 ? */
#define CONTROL_E0 (1 << 5) /* E0: 1: Big Endian */
#define CONTROL_RESVD_WP0 (1 << 6) /* Reserved - WP0 ? */
#define CONTROL_RESVD_SE0 (1 << 7) /* Reserved - SE0 ? */
#define CONTROL_RESVD_ECC0 (1 << 8) /* Reserved - ECC0 ? */
#define CONTROL_RAEIE (1 << 9) /* IRQ - read fifo almost full */
#define CONTROL_RNEIE (1 << 10) /* IRQ - read fifo not empty */
#define CONTROL_WAFIE (1 << 11) /* IRQ - write fifo almost empty */
#define CONTROL_WNFIE (1 << 12) /* IRQ - write fifo not full */
#define CONTROL_RESVD_FIRQ (1 << 13) /* RESERVED - FIRQ ? */
#define CONTROL_AERIE (1 << 14) /* IRQ - AHB timeout error */
#define CONTROL_IIE (1 << 15) /* IDE IRQ enable */
/* Device 1 */
#define CONTROL_TYP1(x) (((x) & 0x7) << 16)
#define CONTROL_IRE1 (1 << 19) /* enable IORDY for PIO */
#define CONTROL_RESVD_DW1 (1 << 20) /* Reserved - DW1 ? */
#define CONTROL_E1 (1 << 21) /* E1: 1: Big Endian */
#define CONTROL_RESVD_WP1 (1 << 22) /* Reserved - WP1 ? */
#define CONTROL_RESVD_SE1 (1 << 23) /* Reserved - SE1 ? */
#define CONTROL_RESVD_ECC1 (1 << 24) /* Reserved - ECC1 ? */
#define CONTROL_DRE (1 << 25) /* DMA receive enable */
#define CONTROL_DTE (1 << 26) /* DMA transmit enable */
#define CONTRIL_RESVD (1 << 27)
#define CONTROL_TERIE (1 << 28) /* transfer terminate error IRQ */
#define CONTROL_T (1 << 29) /* terminate current operation */
#define CONTROL_SRST (1 << 30) /* IDE soft reset */
#define CONTROL_RST (1 << 31) /* IDE hardware reset */
/* IRQ register - 0x0c */
#define IRQ_RXTHRESH(x) (((x) & 0x3ff) << 0) /* Read FIFO threshold */
#define IRQ_RFAEIRQ (1 << 10) /* Read FIFO almost full intr req */
#define IRQ_RFNEIRQ (1 << 11) /* Read FIFO not empty intr req */
#define IRQ_WFAFIRQ (1 << 12) /* Write FIFO almost empty int req */
#define IRQ_WFNFIRQ (1 << 13) /* Write FIFO not full intr req */
#define IRQ_RESVD_FIRQ (1 << 14) /* Reserved - FIRQ ? */
#define IRQ_IIRQ (1 << 15) /* IDE device interrupt request */
#define IRQ_TXTHRESH(x) (((x) & 0x3ff) << 16) /* Write FIFO thershold */
#define IRQ_TERMERR (1 << 28) /* Transfer termination indication */
#define IRQ_AHBERR (1 << 29) /* AHB Timeout indication */
/* Command Timing Register 0-1: ctrd (0x10, 0x18) */
#define CT_REG_T1(x) (((x) & 0xff) << 0) /* setup time of addressed */
#define CT_REG_T2(x) (((x) & 0xff) << 8) /* pluse width of DIOR/DIOW */
#define CT_REG_T4(x) (((x) & 0xff) << 16) /* data hold time */
#define CT_REG_TEOC(x) (((x) & 0xff) << 24) /* time to the end of a cycle */
/* Data Timing Register 0-1: dtrd (0x14, 0x1c) */
/*
* PIO mode:
* b(0:7) DT_REG_PIO_T1: the setup time of addressed
* b(8:15) DT_REG_PIO_T2: the pluse width of DIOR/DIOW
* b(16:23) DT_REG_PIO_T4: data hold time
* b(24:31) DT_REG_PIO_TEOC: the time to the end of a cycle
*/
#define DT_REG_PIO_T1(x) (((x) & 0xff) << 0)
#define DT_REG_PIO_T2(x) (((x) & 0xff) << 8)
#define DT_REG_PIO_T4(x) (((x) & 0xff) << 16)
#define DT_REG_PIO_TEOC(x) (((x) & 0xff) << 24)
/*
* UDMA mode:
* b(0:3) DT_REG_UDMA_TENV: the envelope time
* b(4:7) DT_REG_UDMA_TMLI: interlock time
* b(8:15) DT_REG_UDMA_TCYC: cycle time - data time
* b(16:19) DT_REG_UDMA_TACK: setup and hold time of DMACK
* b(23:30) DT_REG_UDMA_TCVS: setup time of CRC
* b(24:31) DT_REG_UDMA_TRP: time to ready to pause
*/
#define DT_REG_UDMA_TENV(x) (((x) & 0xf) << 0)
#define DT_REG_UDMA_TMLI(x) (((x) & 0xf) << 4)
#define DT_REG_UDMA_TCYC(x) (((x) & 0xff) << 8)
#define DT_REG_UDMA_TACK(x) (((x) & 0xf) << 16)
#define DT_REG_UDMA_TCVS(x) (((x) & 0xf) << 20)
#define DT_REG_UDMA_TRP(x) (((x) & 0xff) << 24)
/* ftide020_s command formats */
/* read: IDE Register (CF1) */
#define IDE_REG_OPCODE_READ (1 << 13) /* 0x2000 */
#define IDE_REG_CS_READ(x) (((x) & 0x3) << 11)
#define IDE_REG_DA_READ(x) (((x) & 0x7) << 8)
#define IDE_REG_CMD_READ(x) 0x0 /* fixed value */
/* write: IDE Register (CF2) */
#define IDE_REG_OPCODE_WRITE (0x5 << 13) /* 0xA000 */
#define IDE_REG_CS_WRITE(x) (((x) & 0x3) << 11)
#define IDE_REG_DA_WRITE(x) (((x) & 0x7) << 8)
/* b(0:7) IDE_REG_CMD_WRITE(x): Actual ATA command or data */
#define IDE_REG_CMD_WRITE(x) (((x) & 0xff) << 0)
/* read/write data: PIO/UDMA (CF3) */
#define IDE_DATA_WRITE (1 << 15) /* read: 0, write: 1 */
#define IDE_DATA_OPCODE (0x2 << 13) /* device data access opcode */
/* b(0:12) IDE_DATA_COUNTER(x): Number of transfers minus 1 */
#define IDE_DATA_COUNTER(x) (((x) & 0x1fff) << 0)
/* set device: (CF4) */
#define IDE_SET_OPCODE (0x2740 << 2) /* [15:2], 0x9d00 */
/* CF3 counter value: 0: Tx in bytes, 1: in blocks (each block is 8 bytes) */
#define IDE_SET_CX8(x) (((x) & 0x1) << 1)
#define IDE_SET_DEV(x) (((x) & 0x1) << 0) /* 0: Master, 1: Slave */
/*
* IDE command bit definition
* This section is designed for minor hardware revision compatibility.
*/
#define READ_REG_CMD IDE_REG_OPCODE_READ /* 0x2000 */
#define WRITE_REG_CMD IDE_REG_OPCODE_WRITE /* 0xA000 */
#define READ_DATA_CMD IDE_DATA_OPCODE /* 0x4000 */
#define WRITE_DATA_CMD (IDE_DATA_OPCODE | IDE_DATA_WRITE) /* 0xC000 */
#define SET_DEV_CMD IDE_SET_OPCODE /* 0x9D00 */
#define TATOL_TIMING 3
#define CMD_TIMING 0
#define PIO_TIMING 1
#define DMA_TIMING 2
/* Timing Parameters */
/* Register Access Timing Parameters */
#define REG_PARAMETER 4
#define REG_T0 0
#define REG_T1 1
#define REG_T2 2
#define REG_T4 3
#define REG_MODE 5
#define REG_MODE0 0
#define REG_MODE1 1
#define REG_MODE2 2
#define REG_MODE3 3
#define REG_MODE4 4
/* PIO Access Timing Parameters */
#define PIO_PARAMETER 4
#define PIO_T0 0
#define PIO_T1 1
#define PIO_T2 2
#define PIO_T4 3
#define PIO_MODE 5
#define PIO_MODE0 0
#define PIO_MODE1 1
#define PIO_MODE2 2
#define PIO_MODE3 3
#define PIO_MODE4 4
/* UDMA Access Timing Parameters */
#define UDMA_PARAMETER 6
#define UDMA_TCYC 0
#define UDMA_TCVS 1
#define UDMA_TMLI 2
#define UDMA_TENV 3
#define UDMA_TRP 4
#define UDMA_TACK 5
#define UDMA_MODE 7
#define UDMA_MODE0 0
#define UDMA_MODE1 1
#define UDMA_MODE2 2
#define UDMA_MODE3 3
#define UDMA_MODE4 4
#define UDMA_MODE5 5
#define UDMA_MODE6 6
/*
* RX_THRESH:
* hardware limitation: max = 8, should support 1,4,8,16,32,64,128,256
*/
#define RX_THRESH 8
#define WRITE_FIFO 32 /* Hardwired value */
/* Time Table */
unsigned int REG_ACCESS_TIMING[REG_PARAMETER][REG_MODE] = {
{600, 383, 330, 180, 120},
{70, 50, 30, 30, 25},
{290, 290, 290, 80, 70},
{30, 20, 15, 10, 10},
};
unsigned int PIO_ACCESS_TIMING[PIO_PARAMETER][PIO_MODE] = {
{600, 383, 240, 180, 120},
{70, 50, 30, 30, 25},
{165, 125, 100, 80, 70},
{30, 20, 15, 10, 10},
};
unsigned int UDMA_ACCESS_TIMING[UDMA_PARAMETER][UDMA_MODE] = {
{1120, 730, 540, 390, 250, 168, 130}, /* 10X */
{700, 480, 310, 200, 67, 100, 100}, /* 10X */
{200, 200, 200, 200, 200, 200, 200}, /* 10X */
{200, 200, 200, 200, 200, 200, 200}, /* 10X */
{1600, 1250, 1000, 1000, 1000, 850, 850}, /* 10X */
{200, 200, 200, 200, 200, 200, 200}, /* 10X */
};
#endif /* __FTIDE020_H */

View file

@ -0,0 +1,158 @@
/*
* Copyright (C) 2008 Freescale Semiconductor, Inc.
* Dave Liu <daveliu@freescale.com>
* port from the libata of linux kernel
*
* 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
*
*/
#include <libata.h>
u64 ata_id_n_sectors(u16 *id)
{
if (ata_id_has_lba(id)) {
if (ata_id_has_lba48(id))
return ata_id_u64(id, ATA_ID_LBA48_SECTORS);
else
return ata_id_u32(id, ATA_ID_LBA_SECTORS);
} else {
return 0;
}
}
u32 ata_dev_classify(u32 sig)
{
u8 lbam, lbah;
lbam = (sig >> 16) & 0xff;
lbah = (sig >> 24) & 0xff;
if (((lbam == 0) && (lbah == 0)) ||
((lbam == 0x3c) && (lbah == 0xc3)))
return ATA_DEV_ATA;
if ((lbam == 0x14) && (lbah == 0xeb))
return ATA_DEV_ATAPI;
if ((lbam == 0x69) && (lbah == 0x96))
return ATA_DEV_PMP;
return ATA_DEV_UNKNOWN;
}
static void ata_id_string(const u16 *id, unsigned char *s,
unsigned int ofs, unsigned int len)
{
unsigned int c;
while (len > 0) {
c = id[ofs] >> 8;
*s = c;
s++;
c = id[ofs] & 0xff;
*s = c;
s++;
ofs++;
len -= 2;
}
}
void ata_id_c_string(const u16 *id, unsigned char *s,
unsigned int ofs, unsigned int len)
{
unsigned char *p;
ata_id_string(id, s, ofs, len - 1);
p = s + strnlen((char *)s, len - 1);
while (p > s && p[-1] == ' ')
p--;
*p = '\0';
}
void ata_dump_id(u16 *id)
{
unsigned char serial[ATA_ID_SERNO_LEN + 1];
unsigned char firmware[ATA_ID_FW_REV_LEN + 1];
unsigned char product[ATA_ID_PROD_LEN + 1];
u64 n_sectors;
/* Serial number */
ata_id_c_string(id, serial, ATA_ID_SERNO, sizeof(serial));
printf("S/N: %s\n\r", serial);
/* Firmware version */
ata_id_c_string(id, firmware, ATA_ID_FW_REV, sizeof(firmware));
printf("Firmware version: %s\n\r", firmware);
/* Product model */
ata_id_c_string(id, product, ATA_ID_PROD, sizeof(product));
printf("Product model number: %s\n\r", product);
/* Total sectors of device */
n_sectors = ata_id_n_sectors(id);
printf("Capablity: %lld sectors\n\r", n_sectors);
printf ("id[49]: capabilities = 0x%04x\n"
"id[53]: field valid = 0x%04x\n"
"id[63]: mwdma = 0x%04x\n"
"id[64]: pio = 0x%04x\n"
"id[75]: queue depth = 0x%04x\n",
id[49],
id[53],
id[63],
id[64],
id[75]);
printf ("id[76]: sata capablity = 0x%04x\n"
"id[78]: sata features supported = 0x%04x\n"
"id[79]: sata features enable = 0x%04x\n",
id[76],
id[78],
id[79]);
printf ("id[80]: major version = 0x%04x\n"
"id[81]: minor version = 0x%04x\n"
"id[82]: command set supported 1 = 0x%04x\n"
"id[83]: command set supported 2 = 0x%04x\n"
"id[84]: command set extension = 0x%04x\n",
id[80],
id[81],
id[82],
id[83],
id[84]);
printf ("id[85]: command set enable 1 = 0x%04x\n"
"id[86]: command set enable 2 = 0x%04x\n"
"id[87]: command set default = 0x%04x\n"
"id[88]: udma = 0x%04x\n"
"id[93]: hardware reset result = 0x%04x\n",
id[85],
id[86],
id[87],
id[88],
id[93]);
}
void ata_swap_buf_le16(u16 *buf, unsigned int buf_words)
{
unsigned int i;
for (i = 0; i < buf_words; i++)
buf[i] = le16_to_cpu(buf[i]);
}

View file

@ -0,0 +1,174 @@
/*
* Copyright (C) 2010 Albert ARIBAUD <albert.u.boot@aribaud.net>
*
* Written-by: Albert ARIBAUD <albert.u.boot@aribaud.net>
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include <common.h>
#include <asm/io.h>
#if defined(CONFIG_ORION5X)
#include <asm/arch/orion5x.h>
#elif defined(CONFIG_KIRKWOOD)
#include <asm/arch/kirkwood.h>
#endif
/* SATA port registers */
struct mvsata_port_registers {
u32 reserved0[10];
u32 edma_cmd;
u32 reserved1[181];
/* offset 0x300 : ATA Interface registers */
u32 sstatus;
u32 serror;
u32 scontrol;
u32 ltmode;
u32 phymode3;
u32 phymode4;
u32 reserved2[5];
u32 phymode1;
u32 phymode2;
u32 bist_cr;
u32 bist_dw1;
u32 bist_dw2;
u32 serrorintrmask;
};
/*
* Sanity checks:
* - to compile at all, we need CONFIG_SYS_ATA_BASE_ADDR.
* - for ide_preinit to make sense, we need at least one of
* CONFIG_SYS_ATA_IDE0_OFFSET or CONFIG_SYS_ATA_IDE0_OFFSET;
* - for inde_preinit to be called, we need CONFIG_IDE_PREINIT.
* Fail with an explanation message if these conditions are not met.
* This is particularly important for CONFIG_IDE_PREINIT, because
* its lack would not cause a build error.
*/
#if !defined(CONFIG_SYS_ATA_BASE_ADDR)
#error CONFIG_SYS_ATA_BASE_ADDR must be defined
#elif !defined(CONFIG_SYS_ATA_IDE0_OFFSET) \
&& !defined(CONFIG_SYS_ATA_IDE1_OFFSET)
#error CONFIG_SYS_ATA_IDE0_OFFSET or CONFIG_SYS_ATA_IDE1_OFFSET \
must be defined
#elif !defined(CONFIG_IDE_PREINIT)
#error CONFIG_IDE_PREINIT must be defined
#endif
/*
* Masks and values for SControl DETection and Interface Power Management,
* and for SStatus DETection.
*/
#define MVSATA_EDMA_CMD_ATA_RST 0x00000004
#define MVSATA_SCONTROL_DET_MASK 0x0000000F
#define MVSATA_SCONTROL_DET_NONE 0x00000000
#define MVSATA_SCONTROL_DET_INIT 0x00000001
#define MVSATA_SCONTROL_IPM_MASK 0x00000F00
#define MVSATA_SCONTROL_IPM_NO_LP_ALLOWED 0x00000300
#define MVSATA_SCONTROL_MASK \
(MVSATA_SCONTROL_DET_MASK|MVSATA_SCONTROL_IPM_MASK)
#define MVSATA_PORT_INIT \
(MVSATA_SCONTROL_DET_INIT|MVSATA_SCONTROL_IPM_NO_LP_ALLOWED)
#define MVSATA_PORT_USE \
(MVSATA_SCONTROL_DET_NONE|MVSATA_SCONTROL_IPM_NO_LP_ALLOWED)
#define MVSATA_SSTATUS_DET_MASK 0x0000000F
#define MVSATA_SSTATUS_DET_DEVCOMM 0x00000003
/*
* Status codes to return to client callers. Currently, callers ignore
* exact value and only care for zero or nonzero, so no need to make this
* public, it is only #define'd for clarity.
* If/when standard negative codes are implemented in U-boot, then these
* #defines should be moved to, or replaced by ones from, the common list
* of status codes.
*/
#define MVSATA_STATUS_OK 0
#define MVSATA_STATUS_TIMEOUT -1
/*
* Initialize one MVSATAHC port: set SControl's IPM to "always active"
* and DET to "reset", then wait for SStatus's DET to become "device and
* comm ok" (or time out after 50 us if no device), then set SControl's
* DET back to "no action".
*/
static int mvsata_ide_initialize_port(struct mvsata_port_registers *port)
{
u32 control;
u32 status;
u32 timeleft = 10000; /* wait at most 10 ms for SATA reset to complete */
/* Hard reset */
writel(MVSATA_EDMA_CMD_ATA_RST, &port->edma_cmd);
udelay(25); /* taken from original marvell port */
writel(0, &port->edma_cmd);
/* Set control IPM to 3 (no low power) and DET to 1 (initialize) */
control = readl(&port->scontrol);
control = (control & ~MVSATA_SCONTROL_MASK) | MVSATA_PORT_INIT;
writel(control, &port->scontrol);
/* Toggle control DET back to 0 (normal operation) */
control = (control & ~MVSATA_SCONTROL_MASK) | MVSATA_PORT_USE;
writel(control, &port->scontrol);
/* wait for status DET to become 3 (device and communication OK) */
while (--timeleft) {
status = readl(&port->sstatus) & MVSATA_SSTATUS_DET_MASK;
if (status == MVSATA_SSTATUS_DET_DEVCOMM)
break;
udelay(1);
}
/* return success or time-out error depending on time left */
if (!timeleft)
return MVSATA_STATUS_TIMEOUT;
return MVSATA_STATUS_OK;
}
/*
* ide_preinit() will be called by ide_init in cmd_ide.c and will
* reset the MVSTATHC ports needed by the board.
*/
int ide_preinit(void)
{
int ret = MVSATA_STATUS_TIMEOUT;
int status;
/* Enable ATA port 0 (could be SATA port 0 or 1) if declared */
#if defined(CONFIG_SYS_ATA_IDE0_OFFSET)
status = mvsata_ide_initialize_port(
(struct mvsata_port_registers *)
(CONFIG_SYS_ATA_BASE_ADDR + CONFIG_SYS_ATA_IDE0_OFFSET));
if (status == MVSATA_STATUS_OK)
ret = MVSATA_STATUS_OK;
#endif
/* Enable ATA port 1 (could be SATA port 0 or 1) if declared */
#if defined(CONFIG_SYS_ATA_IDE1_OFFSET)
status = mvsata_ide_initialize_port(
(struct mvsata_port_registers *)
(CONFIG_SYS_ATA_BASE_ADDR + CONFIG_SYS_ATA_IDE1_OFFSET));
if (status == MVSATA_STATUS_OK)
ret = MVSATA_STATUS_OK;
#endif
/* Return success if at least one port initialization succeeded */
return ret;
}

View file

@ -0,0 +1,142 @@
/*
* Freescale iMX51 ATA driver
*
* Copyright (C) 2010 Marek Vasut <marek.vasut@gmail.com>
*
* Based on code by:
* Mahesh Mahadevan <mahesh.mahadevan@freescale.com>
*
* Based on code from original FSL ATA driver, which is
* part of eCos, the Embedded Configurable Operating System.
* Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
*
* 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
*/
#include <common.h>
#include <command.h>
#include <config.h>
#include <asm/byteorder.h>
#include <asm/io.h>
#include <ide.h>
#include <asm/arch/imx-regs.h>
#include <asm/arch/clock.h>
/* MXC ATA register offsets */
struct mxc_ata_config_regs {
u8 time_off; /* 0x00 */
u8 time_on;
u8 time_1;
u8 time_2w;
u8 time_2r;
u8 time_ax;
u8 time_pio_rdx;
u8 time_4;
u8 time_9;
u8 time_m;
u8 time_jn;
u8 time_d;
u8 time_k;
u8 time_ack;
u8 time_env;
u8 time_udma_rdx;
u8 time_zah; /* 0x10 */
u8 time_mlix;
u8 time_dvh;
u8 time_dzfs;
u8 time_dvs;
u8 time_cvh;
u8 time_ss;
u8 time_cyc;
u32 fifo_data_32; /* 0x18 */
u32 fifo_data_16;
u32 fifo_fill;
u32 ata_control;
u32 interrupt_pending;
u32 interrupt_enable;
u32 interrupt_clear;
u32 fifo_alarm;
};
struct mxc_data_hdd_regs {
u32 drive_data; /* 0xa0 */
u32 drive_features;
u32 drive_sector_count;
u32 drive_sector_num;
u32 drive_cyl_low;
u32 drive_cyl_high;
u32 drive_dev_head;
u32 command;
u32 status;
u32 alt_status;
};
/* PIO timing table */
#define NR_PIO_SPECS 5
static uint16_t pio_t1[NR_PIO_SPECS] = { 70, 50, 30, 30, 25 };
static uint16_t pio_t2_8[NR_PIO_SPECS] = { 290, 290, 290, 80, 70 };
static uint16_t pio_t4[NR_PIO_SPECS] = { 30, 20, 15, 10, 10 };
static uint16_t pio_t9[NR_PIO_SPECS] = { 20, 15, 10, 10, 10 };
static uint16_t pio_tA[NR_PIO_SPECS] = { 50, 50, 50, 50, 50 };
#define REG2OFF(reg) ((((uint32_t)reg) & 0x3) * 8)
static void set_ata_bus_timing(unsigned char mode)
{
uint32_t T = 1000000000 / mxc_get_clock(MXC_IPG_CLK);
struct mxc_ata_config_regs *ata_regs;
ata_regs = (struct mxc_ata_config_regs *)CONFIG_SYS_ATA_BASE_ADDR;
if (mode >= NR_PIO_SPECS)
return;
/* Write TIME_OFF/ON/1/2W */
writeb(3, &ata_regs->time_off);
writeb(3, &ata_regs->time_on);
writeb((pio_t1[mode] + T) / T, &ata_regs->time_1);
writeb((pio_t2_8[mode] + T) / T, &ata_regs->time_2w);
/* Write TIME_2R/AX/RDX/4 */
writeb((pio_t2_8[mode] + T) / T, &ata_regs->time_2r);
writeb((pio_tA[mode] + T) / T + 2, &ata_regs->time_ax);
writeb(1, &ata_regs->time_pio_rdx);
writeb((pio_t4[mode] + T) / T, &ata_regs->time_4);
/* Write TIME_9 ; the rest of timing registers is irrelevant for PIO */
writeb((pio_t9[mode] + T) / T, &ata_regs->time_9);
}
int ide_preinit(void)
{
struct mxc_ata_config_regs *ata_regs;
ata_regs = (struct mxc_ata_config_regs *)CONFIG_SYS_ATA_BASE_ADDR;
/* 46.3.3.4 @ FSL iMX51 manual */
/* FIFO normal op., drive reset */
writel(0x80, &ata_regs->ata_control);
/* FIFO normal op., drive not reset */
writel(0xc0, &ata_regs->ata_control);
/* Configure the PIO timing */
set_ata_bus_timing(CONFIG_MXC_ATA_PIO_MODE);
/* 46.3.3.4 @ FSL iMX51 manual */
/* Drive not reset, IORDY handshake */
writel(0x41, &ata_regs->ata_control);
return 0;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,173 @@
/*
* Driver for Blackfin on-chip ATAPI controller.
*
* Enter bugs at http://blackfin.uclinux.org/
*
* Copyright (c) 2008 Analog Devices Inc.
*
* Licensed under the GPL-2 or later.
*/
#ifndef PATA_BFIN_H
#define PATA_BFIN_H
#include <asm/blackfin_local.h>
struct ata_ioports {
unsigned long cmd_addr;
unsigned long data_addr;
unsigned long error_addr;
unsigned long feature_addr;
unsigned long nsect_addr;
unsigned long lbal_addr;
unsigned long lbam_addr;
unsigned long lbah_addr;
unsigned long device_addr;
unsigned long status_addr;
unsigned long command_addr;
unsigned long altstatus_addr;
unsigned long ctl_addr;
unsigned long bmdma_addr;
unsigned long scr_addr;
};
struct ata_port {
unsigned int port_no; /* primary=0, secondary=1 */
struct ata_ioports ioaddr; /* ATA cmd/ctl/dma reg blks */
unsigned long flag;
unsigned int ata_mode;
unsigned char ctl_reg;
unsigned char last_ctl;
unsigned char dev_mask;
};
extern block_dev_desc_t sata_dev_desc[CONFIG_SYS_SATA_MAX_DEVICE];
#define DRV_NAME "pata-bfin"
#define DRV_VERSION "0.9"
#define __iomem
#define ATA_REG_CTRL 0x0E
#define ATA_REG_ALTSTATUS ATA_REG_CTRL
#define ATA_TMOUT_BOOT 30000
#define ATA_TMOUT_BOOT_QUICK 7000
#define PATA_BFIN_WAIT_TIMEOUT 10000
#define PATA_DEV_NUM_PER_PORT 2
/* These are the offset of the controller's registers */
#define ATAPI_OFFSET_CONTROL 0x00
#define ATAPI_OFFSET_STATUS 0x04
#define ATAPI_OFFSET_DEV_ADDR 0x08
#define ATAPI_OFFSET_DEV_TXBUF 0x0c
#define ATAPI_OFFSET_DEV_RXBUF 0x10
#define ATAPI_OFFSET_INT_MASK 0x14
#define ATAPI_OFFSET_INT_STATUS 0x18
#define ATAPI_OFFSET_XFER_LEN 0x1c
#define ATAPI_OFFSET_LINE_STATUS 0x20
#define ATAPI_OFFSET_SM_STATE 0x24
#define ATAPI_OFFSET_TERMINATE 0x28
#define ATAPI_OFFSET_PIO_TFRCNT 0x2c
#define ATAPI_OFFSET_DMA_TFRCNT 0x30
#define ATAPI_OFFSET_UMAIN_TFRCNT 0x34
#define ATAPI_OFFSET_UDMAOUT_TFRCNT 0x38
#define ATAPI_OFFSET_REG_TIM_0 0x40
#define ATAPI_OFFSET_PIO_TIM_0 0x44
#define ATAPI_OFFSET_PIO_TIM_1 0x48
#define ATAPI_OFFSET_MULTI_TIM_0 0x50
#define ATAPI_OFFSET_MULTI_TIM_1 0x54
#define ATAPI_OFFSET_MULTI_TIM_2 0x58
#define ATAPI_OFFSET_ULTRA_TIM_0 0x60
#define ATAPI_OFFSET_ULTRA_TIM_1 0x64
#define ATAPI_OFFSET_ULTRA_TIM_2 0x68
#define ATAPI_OFFSET_ULTRA_TIM_3 0x6c
#define ATAPI_GET_CONTROL(base)\
bfin_read16(base + ATAPI_OFFSET_CONTROL)
#define ATAPI_SET_CONTROL(base, val)\
bfin_write16(base + ATAPI_OFFSET_CONTROL, val)
#define ATAPI_GET_STATUS(base)\
bfin_read16(base + ATAPI_OFFSET_STATUS)
#define ATAPI_GET_DEV_ADDR(base)\
bfin_read16(base + ATAPI_OFFSET_DEV_ADDR)
#define ATAPI_SET_DEV_ADDR(base, val)\
bfin_write16(base + ATAPI_OFFSET_DEV_ADDR, val)
#define ATAPI_GET_DEV_TXBUF(base)\
bfin_read16(base + ATAPI_OFFSET_DEV_TXBUF)
#define ATAPI_SET_DEV_TXBUF(base, val)\
bfin_write16(base + ATAPI_OFFSET_DEV_TXBUF, val)
#define ATAPI_GET_DEV_RXBUF(base)\
bfin_read16(base + ATAPI_OFFSET_DEV_RXBUF)
#define ATAPI_SET_DEV_RXBUF(base, val)\
bfin_write16(base + ATAPI_OFFSET_DEV_RXBUF, val)
#define ATAPI_GET_INT_MASK(base)\
bfin_read16(base + ATAPI_OFFSET_INT_MASK)
#define ATAPI_SET_INT_MASK(base, val)\
bfin_write16(base + ATAPI_OFFSET_INT_MASK, val)
#define ATAPI_GET_INT_STATUS(base)\
bfin_read16(base + ATAPI_OFFSET_INT_STATUS)
#define ATAPI_SET_INT_STATUS(base, val)\
bfin_write16(base + ATAPI_OFFSET_INT_STATUS, val)
#define ATAPI_GET_XFER_LEN(base)\
bfin_read16(base + ATAPI_OFFSET_XFER_LEN)
#define ATAPI_SET_XFER_LEN(base, val)\
bfin_write16(base + ATAPI_OFFSET_XFER_LEN, val)
#define ATAPI_GET_LINE_STATUS(base)\
bfin_read16(base + ATAPI_OFFSET_LINE_STATUS)
#define ATAPI_GET_SM_STATE(base)\
bfin_read16(base + ATAPI_OFFSET_SM_STATE)
#define ATAPI_GET_TERMINATE(base)\
bfin_read16(base + ATAPI_OFFSET_TERMINATE)
#define ATAPI_SET_TERMINATE(base, val)\
bfin_write16(base + ATAPI_OFFSET_TERMINATE, val)
#define ATAPI_GET_PIO_TFRCNT(base)\
bfin_read16(base + ATAPI_OFFSET_PIO_TFRCNT)
#define ATAPI_GET_DMA_TFRCNT(base)\
bfin_read16(base + ATAPI_OFFSET_DMA_TFRCNT)
#define ATAPI_GET_UMAIN_TFRCNT(base)\
bfin_read16(base + ATAPI_OFFSET_UMAIN_TFRCNT)
#define ATAPI_GET_UDMAOUT_TFRCNT(base)\
bfin_read16(base + ATAPI_OFFSET_UDMAOUT_TFRCNT)
#define ATAPI_GET_REG_TIM_0(base)\
bfin_read16(base + ATAPI_OFFSET_REG_TIM_0)
#define ATAPI_SET_REG_TIM_0(base, val)\
bfin_write16(base + ATAPI_OFFSET_REG_TIM_0, val)
#define ATAPI_GET_PIO_TIM_0(base)\
bfin_read16(base + ATAPI_OFFSET_PIO_TIM_0)
#define ATAPI_SET_PIO_TIM_0(base, val)\
bfin_write16(base + ATAPI_OFFSET_PIO_TIM_0, val)
#define ATAPI_GET_PIO_TIM_1(base)\
bfin_read16(base + ATAPI_OFFSET_PIO_TIM_1)
#define ATAPI_SET_PIO_TIM_1(base, val)\
bfin_write16(base + ATAPI_OFFSET_PIO_TIM_1, val)
#define ATAPI_GET_MULTI_TIM_0(base)\
bfin_read16(base + ATAPI_OFFSET_MULTI_TIM_0)
#define ATAPI_SET_MULTI_TIM_0(base, val)\
bfin_write16(base + ATAPI_OFFSET_MULTI_TIM_0, val)
#define ATAPI_GET_MULTI_TIM_1(base)\
bfin_read16(base + ATAPI_OFFSET_MULTI_TIM_1)
#define ATAPI_SET_MULTI_TIM_1(base, val)\
bfin_write16(base + ATAPI_OFFSET_MULTI_TIM_1, val)
#define ATAPI_GET_MULTI_TIM_2(base)\
bfin_read16(base + ATAPI_OFFSET_MULTI_TIM_2)
#define ATAPI_SET_MULTI_TIM_2(base, val)\
bfin_write16(base + ATAPI_OFFSET_MULTI_TIM_2, val)
#define ATAPI_GET_ULTRA_TIM_0(base)\
bfin_read16(base + ATAPI_OFFSET_ULTRA_TIM_0)
#define ATAPI_SET_ULTRA_TIM_0(base, val)\
bfin_write16(base + ATAPI_OFFSET_ULTRA_TIM_0, val)
#define ATAPI_GET_ULTRA_TIM_1(base)\
bfin_read16(base + ATAPI_OFFSET_ULTRA_TIM_1)
#define ATAPI_SET_ULTRA_TIM_1(base, val)\
bfin_write16(base + ATAPI_OFFSET_ULTRA_TIM_1, val)
#define ATAPI_GET_ULTRA_TIM_2(base)\
bfin_read16(base + ATAPI_OFFSET_ULTRA_TIM_2)
#define ATAPI_SET_ULTRA_TIM_2(base, val)\
bfin_write16(base + ATAPI_OFFSET_ULTRA_TIM_2, val)
#define ATAPI_GET_ULTRA_TIM_3(base)\
bfin_read16(base + ATAPI_OFFSET_ULTRA_TIM_3)
#define ATAPI_SET_ULTRA_TIM_3(base, val)\
bfin_write16(base + ATAPI_OFFSET_ULTRA_TIM_3, val)
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,477 @@
/*
* sata_dwc.h
*
* Synopsys DesignWare Cores (DWC) SATA host driver
*
* Author: Mark Miesfeld <mmiesfeld@amcc.com>
*
* Ported from 2.6.19.2 to 2.6.25/26 by Stefan Roese <sr@denx.de>
* Copyright 2008 DENX Software Engineering
*
* Based on versions provided by AMCC and Synopsys which are:
* Copyright 2006 Applied Micro Circuits Corporation
* COPYRIGHT (C) 2005 SYNOPSYS, INC. ALL RIGHTS RESERVED
*
* 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.
*
*/
/*
* SATA support based on the chip canyonlands.
*
* 04-17-2009
* The local version of this driver for the canyonlands board
* does not use interrupts but polls the chip instead.
*/
#ifndef _SATA_DWC_H_
#define _SATA_DWC_H_
#define __U_BOOT__
#define HZ 100
#define READ 0
#define WRITE 1
enum {
ATA_READID_POSTRESET = (1 << 0),
ATA_DNXFER_PIO = 0,
ATA_DNXFER_DMA = 1,
ATA_DNXFER_40C = 2,
ATA_DNXFER_FORCE_PIO = 3,
ATA_DNXFER_FORCE_PIO0 = 4,
ATA_DNXFER_QUIET = (1 << 31),
};
enum hsm_task_states {
HSM_ST_IDLE,
HSM_ST_FIRST,
HSM_ST,
HSM_ST_LAST,
HSM_ST_ERR,
};
#define ATA_SHORT_PAUSE ((HZ >> 6) + 1)
struct ata_queued_cmd {
struct ata_port *ap;
struct ata_device *dev;
struct ata_taskfile tf;
u8 cdb[ATAPI_CDB_LEN];
unsigned long flags;
unsigned int tag;
unsigned int n_elem;
int dma_dir;
unsigned int sect_size;
unsigned int nbytes;
unsigned int extrabytes;
unsigned int curbytes;
unsigned int err_mask;
struct ata_taskfile result_tf;
void *private_data;
#ifndef __U_BOOT__
void *lldd_task;
#endif
unsigned char *pdata;
};
typedef void (*ata_qc_cb_t) (struct ata_queued_cmd *qc);
#define ATA_TAG_POISON 0xfafbfcfdU
enum {
LIBATA_MAX_PRD = ATA_MAX_PRD / 2,
LIBATA_DUMB_MAX_PRD = ATA_MAX_PRD / 4,
ATA_MAX_PORTS = 8,
ATA_DEF_QUEUE = 1,
ATA_MAX_QUEUE = 32,
ATA_TAG_INTERNAL = ATA_MAX_QUEUE - 1,
ATA_MAX_BUS = 2,
ATA_DEF_BUSY_WAIT = 10000,
ATAPI_MAX_DRAIN = 16 << 10,
ATA_SHT_EMULATED = 1,
ATA_SHT_CMD_PER_LUN = 1,
ATA_SHT_THIS_ID = -1,
ATA_SHT_USE_CLUSTERING = 1,
ATA_DFLAG_LBA = (1 << 0),
ATA_DFLAG_LBA48 = (1 << 1),
ATA_DFLAG_CDB_INTR = (1 << 2),
ATA_DFLAG_NCQ = (1 << 3),
ATA_DFLAG_FLUSH_EXT = (1 << 4),
ATA_DFLAG_ACPI_PENDING = (1 << 5),
ATA_DFLAG_ACPI_FAILED = (1 << 6),
ATA_DFLAG_AN = (1 << 7),
ATA_DFLAG_HIPM = (1 << 8),
ATA_DFLAG_DIPM = (1 << 9),
ATA_DFLAG_DMADIR = (1 << 10),
ATA_DFLAG_CFG_MASK = (1 << 12) - 1,
ATA_DFLAG_PIO = (1 << 12),
ATA_DFLAG_NCQ_OFF = (1 << 13),
ATA_DFLAG_SPUNDOWN = (1 << 14),
ATA_DFLAG_SLEEPING = (1 << 15),
ATA_DFLAG_DUBIOUS_XFER = (1 << 16),
ATA_DFLAG_INIT_MASK = (1 << 24) - 1,
ATA_DFLAG_DETACH = (1 << 24),
ATA_DFLAG_DETACHED = (1 << 25),
ATA_LFLAG_HRST_TO_RESUME = (1 << 0),
ATA_LFLAG_SKIP_D2H_BSY = (1 << 1),
ATA_LFLAG_NO_SRST = (1 << 2),
ATA_LFLAG_ASSUME_ATA = (1 << 3),
ATA_LFLAG_ASSUME_SEMB = (1 << 4),
ATA_LFLAG_ASSUME_CLASS = ATA_LFLAG_ASSUME_ATA | ATA_LFLAG_ASSUME_SEMB,
ATA_LFLAG_NO_RETRY = (1 << 5),
ATA_LFLAG_DISABLED = (1 << 6),
ATA_FLAG_SLAVE_POSS = (1 << 0),
ATA_FLAG_SATA = (1 << 1),
ATA_FLAG_NO_LEGACY = (1 << 2),
ATA_FLAG_MMIO = (1 << 3),
ATA_FLAG_SRST = (1 << 4),
ATA_FLAG_SATA_RESET = (1 << 5),
ATA_FLAG_NO_ATAPI = (1 << 6),
ATA_FLAG_PIO_DMA = (1 << 7),
ATA_FLAG_PIO_LBA48 = (1 << 8),
ATA_FLAG_PIO_POLLING = (1 << 9),
ATA_FLAG_NCQ = (1 << 10),
ATA_FLAG_DEBUGMSG = (1 << 13),
ATA_FLAG_IGN_SIMPLEX = (1 << 15),
ATA_FLAG_NO_IORDY = (1 << 16),
ATA_FLAG_ACPI_SATA = (1 << 17),
ATA_FLAG_AN = (1 << 18),
ATA_FLAG_PMP = (1 << 19),
ATA_FLAG_IPM = (1 << 20),
ATA_FLAG_DISABLED = (1 << 23),
ATA_PFLAG_EH_PENDING = (1 << 0),
ATA_PFLAG_EH_IN_PROGRESS = (1 << 1),
ATA_PFLAG_FROZEN = (1 << 2),
ATA_PFLAG_RECOVERED = (1 << 3),
ATA_PFLAG_LOADING = (1 << 4),
ATA_PFLAG_UNLOADING = (1 << 5),
ATA_PFLAG_SCSI_HOTPLUG = (1 << 6),
ATA_PFLAG_INITIALIZING = (1 << 7),
ATA_PFLAG_RESETTING = (1 << 8),
ATA_PFLAG_SUSPENDED = (1 << 17),
ATA_PFLAG_PM_PENDING = (1 << 18),
ATA_QCFLAG_ACTIVE = (1 << 0),
ATA_QCFLAG_DMAMAP = (1 << 1),
ATA_QCFLAG_IO = (1 << 3),
ATA_QCFLAG_RESULT_TF = (1 << 4),
ATA_QCFLAG_CLEAR_EXCL = (1 << 5),
ATA_QCFLAG_QUIET = (1 << 6),
ATA_QCFLAG_FAILED = (1 << 16),
ATA_QCFLAG_SENSE_VALID = (1 << 17),
ATA_QCFLAG_EH_SCHEDULED = (1 << 18),
ATA_HOST_SIMPLEX = (1 << 0),
ATA_HOST_STARTED = (1 << 1),
ATA_TMOUT_BOOT = 30 * 100,
ATA_TMOUT_BOOT_QUICK = 7 * 100,
ATA_TMOUT_INTERNAL = 30 * 100,
ATA_TMOUT_INTERNAL_QUICK = 5 * 100,
/* FIXME: GoVault needs 2s but we can't afford that without
* parallel probing. 800ms is enough for iVDR disk
* HHD424020F7SV00. Increase to 2secs when parallel probing
* is in place.
*/
ATA_TMOUT_FF_WAIT = 4 * 100 / 5,
BUS_UNKNOWN = 0,
BUS_DMA = 1,
BUS_IDLE = 2,
BUS_NOINTR = 3,
BUS_NODATA = 4,
BUS_TIMER = 5,
BUS_PIO = 6,
BUS_EDD = 7,
BUS_IDENTIFY = 8,
BUS_PACKET = 9,
PORT_UNKNOWN = 0,
PORT_ENABLED = 1,
PORT_DISABLED = 2,
/* encoding various smaller bitmaps into a single
* unsigned long bitmap
*/
ATA_NR_PIO_MODES = 7,
ATA_NR_MWDMA_MODES = 5,
ATA_NR_UDMA_MODES = 8,
ATA_SHIFT_PIO = 0,
ATA_SHIFT_MWDMA = ATA_SHIFT_PIO + ATA_NR_PIO_MODES,
ATA_SHIFT_UDMA = ATA_SHIFT_MWDMA + ATA_NR_MWDMA_MODES,
ATA_DMA_PAD_SZ = 4,
ATA_ERING_SIZE = 32,
ATA_DEFER_LINK = 1,
ATA_DEFER_PORT = 2,
ATA_EH_DESC_LEN = 80,
ATA_EH_REVALIDATE = (1 << 0),
ATA_EH_SOFTRESET = (1 << 1),
ATA_EH_HARDRESET = (1 << 2),
ATA_EH_ENABLE_LINK = (1 << 3),
ATA_EH_LPM = (1 << 4),
ATA_EH_RESET_MASK = ATA_EH_SOFTRESET | ATA_EH_HARDRESET,
ATA_EH_PERDEV_MASK = ATA_EH_REVALIDATE,
ATA_EHI_HOTPLUGGED = (1 << 0),
ATA_EHI_RESUME_LINK = (1 << 1),
ATA_EHI_NO_AUTOPSY = (1 << 2),
ATA_EHI_QUIET = (1 << 3),
ATA_EHI_DID_SOFTRESET = (1 << 16),
ATA_EHI_DID_HARDRESET = (1 << 17),
ATA_EHI_PRINTINFO = (1 << 18),
ATA_EHI_SETMODE = (1 << 19),
ATA_EHI_POST_SETMODE = (1 << 20),
ATA_EHI_DID_RESET = ATA_EHI_DID_SOFTRESET | ATA_EHI_DID_HARDRESET,
ATA_EHI_RESET_MODIFIER_MASK = ATA_EHI_RESUME_LINK,
ATA_EH_MAX_TRIES = 5,
ATA_PROBE_MAX_TRIES = 3,
ATA_EH_DEV_TRIES = 3,
ATA_EH_PMP_TRIES = 5,
ATA_EH_PMP_LINK_TRIES = 3,
SATA_PMP_SCR_TIMEOUT = 250,
/* Horkage types. May be set by libata or controller on drives
(some horkage may be drive/controller pair dependant */
ATA_HORKAGE_DIAGNOSTIC = (1 << 0),
ATA_HORKAGE_NODMA = (1 << 1),
ATA_HORKAGE_NONCQ = (1 << 2),
ATA_HORKAGE_MAX_SEC_128 = (1 << 3),
ATA_HORKAGE_BROKEN_HPA = (1 << 4),
ATA_HORKAGE_SKIP_PM = (1 << 5),
ATA_HORKAGE_HPA_SIZE = (1 << 6),
ATA_HORKAGE_IPM = (1 << 7),
ATA_HORKAGE_IVB = (1 << 8),
ATA_HORKAGE_STUCK_ERR = (1 << 9),
ATA_DMA_MASK_ATA = (1 << 0),
ATA_DMA_MASK_ATAPI = (1 << 1),
ATA_DMA_MASK_CFA = (1 << 2),
ATAPI_READ = 0,
ATAPI_WRITE = 1,
ATAPI_READ_CD = 2,
ATAPI_PASS_THRU = 3,
ATAPI_MISC = 4,
};
enum ata_completion_errors {
AC_ERR_DEV = (1 << 0),
AC_ERR_HSM = (1 << 1),
AC_ERR_TIMEOUT = (1 << 2),
AC_ERR_MEDIA = (1 << 3),
AC_ERR_ATA_BUS = (1 << 4),
AC_ERR_HOST_BUS = (1 << 5),
AC_ERR_SYSTEM = (1 << 6),
AC_ERR_INVALID = (1 << 7),
AC_ERR_OTHER = (1 << 8),
AC_ERR_NODEV_HINT = (1 << 9),
AC_ERR_NCQ = (1 << 10),
};
enum ata_xfer_mask {
ATA_MASK_PIO = ((1LU << ATA_NR_PIO_MODES) - 1) << ATA_SHIFT_PIO,
ATA_MASK_MWDMA = ((1LU << ATA_NR_MWDMA_MODES) - 1) << ATA_SHIFT_MWDMA,
ATA_MASK_UDMA = ((1LU << ATA_NR_UDMA_MODES) - 1) << ATA_SHIFT_UDMA,
};
struct ata_port_info {
#ifndef __U_BOOT__
struct scsi_host_template *sht;
#endif
unsigned long flags;
unsigned long link_flags;
unsigned long pio_mask;
unsigned long mwdma_mask;
unsigned long udma_mask;
#ifndef __U_BOOT__
const struct ata_port_operations *port_ops;
void *private_data;
#endif
};
struct ata_ioports {
void __iomem *cmd_addr;
void __iomem *data_addr;
void __iomem *error_addr;
void __iomem *feature_addr;
void __iomem *nsect_addr;
void __iomem *lbal_addr;
void __iomem *lbam_addr;
void __iomem *lbah_addr;
void __iomem *device_addr;
void __iomem *status_addr;
void __iomem *command_addr;
void __iomem *altstatus_addr;
void __iomem *ctl_addr;
#ifndef __U_BOOT__
void __iomem *bmdma_addr;
#endif
void __iomem *scr_addr;
};
struct ata_host {
#ifndef __U_BOOT__
void __iomem * const *iomap;
void *private_data;
const struct ata_port_operations *ops;
unsigned long flags;
struct ata_port *simplex_claimed;
#endif
unsigned int n_ports;
struct ata_port *ports[0];
};
#ifndef __U_BOOT__
struct ata_port_stats {
unsigned long unhandled_irq;
unsigned long idle_irq;
unsigned long rw_reqbuf;
};
#endif
struct ata_device {
struct ata_link *link;
unsigned int devno;
unsigned long flags;
unsigned int horkage;
#ifndef __U_BOOT__
struct scsi_device *sdev;
#ifdef CONFIG_ATA_ACPI
acpi_handle acpi_handle;
union acpi_object *gtf_cache;
#endif
#endif
u64 n_sectors;
unsigned int class;
union {
u16 id[ATA_ID_WORDS];
u32 gscr[SATA_PMP_GSCR_DWORDS];
};
#ifndef __U_BOOT__
u8 pio_mode;
u8 dma_mode;
u8 xfer_mode;
unsigned int xfer_shift;
#endif
unsigned int multi_count;
unsigned int max_sectors;
unsigned int cdb_len;
#ifndef __U_BOOT__
unsigned long pio_mask;
unsigned long mwdma_mask;
#endif
unsigned long udma_mask;
u16 cylinders;
u16 heads;
u16 sectors;
#ifndef __U_BOOT__
int spdn_cnt;
#endif
};
enum dma_data_direction {
DMA_BIDIRECTIONAL = 0,
DMA_TO_DEVICE = 1,
DMA_FROM_DEVICE = 2,
DMA_NONE = 3,
};
struct ata_link {
struct ata_port *ap;
int pmp;
unsigned int active_tag;
u32 sactive;
unsigned int flags;
unsigned int hw_sata_spd_limit;
#ifndef __U_BOOT__
unsigned int sata_spd_limit;
unsigned int sata_spd;
struct ata_device device[2];
#endif
};
struct ata_port {
unsigned long flags;
unsigned int pflags;
unsigned int print_id;
unsigned int port_no;
struct ata_ioports ioaddr;
u8 ctl;
u8 last_ctl;
unsigned int pio_mask;
unsigned int mwdma_mask;
unsigned int udma_mask;
unsigned int cbl;
struct ata_queued_cmd qcmd[ATA_MAX_QUEUE];
unsigned long qc_allocated;
unsigned int qc_active;
int nr_active_links;
struct ata_link link;
#ifndef __U_BOOT__
int nr_pmp_links;
struct ata_link *pmp_link;
#endif
struct ata_link *excl_link;
int nr_pmp_links;
#ifndef __U_BOOT__
struct ata_port_stats stats;
struct device *dev;
u32 msg_enable;
#endif
struct ata_host *host;
void *port_task_data;
unsigned int hsm_task_state;
void *private_data;
unsigned char *pdata;
};
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#endif

View file

@ -0,0 +1,721 @@
/*
* Copyright (C) 2011 Freescale Semiconductor, Inc.
* Author: Tang Yuantian <b29983@freescale.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
*/
#include <common.h>
#include <pci.h>
#include <command.h>
#include <asm/byteorder.h>
#include <malloc.h>
#include <asm/io.h>
#include <fis.h>
#include <libata.h>
#include "sata_sil.h"
/* Convert sectorsize to wordsize */
#define ATA_SECTOR_WORDS (ATA_SECT_SIZE/2)
#define virt_to_bus(devno, v) pci_virt_to_mem(devno, (void *) (v))
static struct sata_info sata_info;
static struct pci_device_id supported[] = {
{PCI_VENDOR_ID_SILICONIMAGE, PCI_DEVICE_ID_SIL3131},
{PCI_VENDOR_ID_SILICONIMAGE, PCI_DEVICE_ID_SIL3132},
{PCI_VENDOR_ID_SILICONIMAGE, PCI_DEVICE_ID_SIL3124},
{}
};
static void sil_sata_dump_fis(struct sata_fis_d2h *s)
{
printf("Status FIS dump:\n");
printf("fis_type: %02x\n", s->fis_type);
printf("pm_port_i: %02x\n", s->pm_port_i);
printf("status: %02x\n", s->status);
printf("error: %02x\n", s->error);
printf("lba_low: %02x\n", s->lba_low);
printf("lba_mid: %02x\n", s->lba_mid);
printf("lba_high: %02x\n", s->lba_high);
printf("device: %02x\n", s->device);
printf("lba_low_exp: %02x\n", s->lba_low_exp);
printf("lba_mid_exp: %02x\n", s->lba_mid_exp);
printf("lba_high_exp: %02x\n", s->lba_high_exp);
printf("res1: %02x\n", s->res1);
printf("sector_count: %02x\n", s->sector_count);
printf("sector_count_exp: %02x\n", s->sector_count_exp);
}
static const char *sata_spd_string(unsigned int speed)
{
static const char * const spd_str[] = {
"1.5 Gbps",
"3.0 Gbps",
"6.0 Gbps",
};
if ((speed - 1) > 2)
return "<unknown>";
return spd_str[speed - 1];
}
static u32 ata_wait_register(void *reg, u32 mask,
u32 val, int timeout_msec)
{
u32 tmp;
tmp = readl(reg);
while ((tmp & mask) == val && timeout_msec > 0) {
mdelay(1);
timeout_msec--;
tmp = readl(reg);
}
return tmp;
}
static void sil_config_port(void *port)
{
/* configure IRQ WoC */
writel(PORT_CS_IRQ_WOC, port + PORT_CTRL_CLR);
/* zero error counters. */
writew(0x8000, port + PORT_DECODE_ERR_THRESH);
writew(0x8000, port + PORT_CRC_ERR_THRESH);
writew(0x8000, port + PORT_HSHK_ERR_THRESH);
writew(0x0000, port + PORT_DECODE_ERR_CNT);
writew(0x0000, port + PORT_CRC_ERR_CNT);
writew(0x0000, port + PORT_HSHK_ERR_CNT);
/* always use 64bit activation */
writel(PORT_CS_32BIT_ACTV, port + PORT_CTRL_CLR);
/* clear port multiplier enable and resume bits */
writel(PORT_CS_PMP_EN | PORT_CS_PMP_RESUME, port + PORT_CTRL_CLR);
}
static int sil_init_port(void *port)
{
u32 tmp;
writel(PORT_CS_INIT, port + PORT_CTRL_STAT);
ata_wait_register(port + PORT_CTRL_STAT,
PORT_CS_INIT, PORT_CS_INIT, 100);
tmp = ata_wait_register(port + PORT_CTRL_STAT,
PORT_CS_RDY, 0, 100);
if ((tmp & (PORT_CS_INIT | PORT_CS_RDY)) != PORT_CS_RDY)
return 1;
return 0;
}
static void sil_read_fis(int dev, int tag, struct sata_fis_d2h *fis)
{
struct sil_sata *sata = sata_dev_desc[dev].priv;
void *port = sata->port;
struct sil_prb *prb;
int i;
u32 *src, *dst;
prb = port + PORT_LRAM + tag * PORT_LRAM_SLOT_SZ;
src = (u32 *)&prb->fis;
dst = (u32 *)fis;
for (i = 0; i < sizeof(struct sata_fis_h2d); i += 4)
*dst++ = readl(src++);
}
static int sil_exec_cmd(int dev, struct sil_cmd_block *pcmd, int tag)
{
struct sil_sata *sata = sata_dev_desc[dev].priv;
void *port = sata->port;
u64 paddr = virt_to_bus(sata->devno, pcmd);
u32 irq_mask, irq_stat;
int rc;
writel(PORT_IRQ_COMPLETE | PORT_IRQ_ERROR, port + PORT_IRQ_ENABLE_CLR);
/* better to add momery barrior here */
writel((u32)paddr, port + PORT_CMD_ACTIVATE + tag * 8);
writel((u64)paddr >> 32, port + PORT_CMD_ACTIVATE + tag * 8 + 4);
irq_mask = (PORT_IRQ_COMPLETE | PORT_IRQ_ERROR) << PORT_IRQ_RAW_SHIFT;
irq_stat = ata_wait_register(port + PORT_IRQ_STAT, irq_mask,
0, 10000);
/* clear IRQs */
writel(irq_mask, port + PORT_IRQ_STAT);
irq_stat >>= PORT_IRQ_RAW_SHIFT;
if (irq_stat & PORT_IRQ_COMPLETE)
rc = 0;
else {
/* force port into known state */
sil_init_port(port);
if (irq_stat & PORT_IRQ_ERROR)
rc = 1; /* error */
else
rc = 2; /* busy */
}
return rc;
}
static int sil_cmd_set_feature(int dev)
{
struct sil_sata *sata = sata_dev_desc[dev].priv;
struct sil_cmd_block cmdb, *pcmd = &cmdb;
struct sata_fis_d2h fis;
u8 udma_cap;
int ret;
memset((void *)&cmdb, 0, sizeof(struct sil_cmd_block));
pcmd->prb.fis.fis_type = SATA_FIS_TYPE_REGISTER_H2D;
pcmd->prb.fis.pm_port_c = (1 << 7);
pcmd->prb.fis.command = ATA_CMD_SET_FEATURES;
pcmd->prb.fis.features = SETFEATURES_XFER;
/* First check the device capablity */
udma_cap = (u8)(sata->udma & 0xff);
debug("udma_cap %02x\n", udma_cap);
if (udma_cap == ATA_UDMA6)
pcmd->prb.fis.sector_count = XFER_UDMA_6;
if (udma_cap == ATA_UDMA5)
pcmd->prb.fis.sector_count = XFER_UDMA_5;
if (udma_cap == ATA_UDMA4)
pcmd->prb.fis.sector_count = XFER_UDMA_4;
if (udma_cap == ATA_UDMA3)
pcmd->prb.fis.sector_count = XFER_UDMA_3;
ret = sil_exec_cmd(dev, pcmd, 0);
if (ret) {
sil_read_fis(dev, 0, &fis);
printf("Err: exe cmd(0x%x).\n",
readl(sata->port + PORT_SERROR));
sil_sata_dump_fis(&fis);
return 1;
}
return 0;
}
static int sil_cmd_identify_device(int dev, u16 *id)
{
struct sil_sata *sata = sata_dev_desc[dev].priv;
struct sil_cmd_block cmdb, *pcmd = &cmdb;
struct sata_fis_d2h fis;
int ret;
memset((void *)&cmdb, 0, sizeof(struct sil_cmd_block));
pcmd->prb.ctrl = cpu_to_le16(PRB_CTRL_PROTOCOL);
pcmd->prb.prot = cpu_to_le16(PRB_PROT_READ);
pcmd->prb.fis.fis_type = SATA_FIS_TYPE_REGISTER_H2D;
pcmd->prb.fis.pm_port_c = (1 << 7);
pcmd->prb.fis.command = ATA_CMD_ID_ATA;
pcmd->sge.addr = cpu_to_le64(virt_to_bus(sata->devno, id));
pcmd->sge.cnt = cpu_to_le32(sizeof(id[0]) * ATA_ID_WORDS);
pcmd->sge.flags = cpu_to_le32(SGE_TRM);
ret = sil_exec_cmd(dev, pcmd, 0);
if (ret) {
sil_read_fis(dev, 0, &fis);
printf("Err: id cmd(0x%x).\n", readl(sata->port + PORT_SERROR));
sil_sata_dump_fis(&fis);
return 1;
}
ata_swap_buf_le16(id, ATA_ID_WORDS);
return 0;
}
static int sil_cmd_soft_reset(int dev)
{
struct sil_cmd_block cmdb, *pcmd = &cmdb;
struct sil_sata *sata = sata_dev_desc[dev].priv;
struct sata_fis_d2h fis;
void *port = sata->port;
int ret;
/* put the port into known state */
if (sil_init_port(port)) {
printf("SRST: port %d not ready\n", dev);
return 1;
}
memset((void *)&cmdb, 0, sizeof(struct sil_cmd_block));
pcmd->prb.ctrl = cpu_to_le16(PRB_CTRL_SRST);
pcmd->prb.fis.fis_type = SATA_FIS_TYPE_REGISTER_H2D;
pcmd->prb.fis.pm_port_c = 0xf;
ret = sil_exec_cmd(dev, &cmdb, 0);
if (ret) {
sil_read_fis(dev, 0, &fis);
printf("SRST cmd error.\n");
sil_sata_dump_fis(&fis);
return 1;
}
return 0;
}
static ulong sil_sata_rw_cmd(int dev, ulong start, ulong blkcnt,
u8 *buffer, int is_write)
{
struct sil_sata *sata = sata_dev_desc[dev].priv;
struct sil_cmd_block cmdb, *pcmd = &cmdb;
struct sata_fis_d2h fis;
u64 block;
int ret;
block = (u64)start;
memset(pcmd, 0, sizeof(struct sil_cmd_block));
pcmd->prb.ctrl = cpu_to_le16(PRB_CTRL_PROTOCOL);
pcmd->prb.fis.fis_type = SATA_FIS_TYPE_REGISTER_H2D;
pcmd->prb.fis.pm_port_c = (1 << 7);
if (is_write) {
pcmd->prb.fis.command = ATA_CMD_WRITE;
pcmd->prb.prot = cpu_to_le16(PRB_PROT_WRITE);
} else {
pcmd->prb.fis.command = ATA_CMD_READ;
pcmd->prb.prot = cpu_to_le16(PRB_PROT_READ);
}
pcmd->prb.fis.device = ATA_LBA;
pcmd->prb.fis.device |= (block >> 24) & 0xf;
pcmd->prb.fis.lba_high = (block >> 16) & 0xff;
pcmd->prb.fis.lba_mid = (block >> 8) & 0xff;
pcmd->prb.fis.lba_low = block & 0xff;
pcmd->prb.fis.sector_count = (u8)blkcnt & 0xff;
pcmd->sge.addr = cpu_to_le64(virt_to_bus(sata->devno, buffer));
pcmd->sge.cnt = cpu_to_le32(blkcnt * ATA_SECT_SIZE);
pcmd->sge.flags = cpu_to_le32(SGE_TRM);
ret = sil_exec_cmd(dev, pcmd, 0);
if (ret) {
sil_read_fis(dev, 0, &fis);
printf("Err: rw cmd(0x%08x).\n",
readl(sata->port + PORT_SERROR));
sil_sata_dump_fis(&fis);
return 1;
}
return blkcnt;
}
static ulong sil_sata_rw_cmd_ext(int dev, ulong start, ulong blkcnt,
u8 *buffer, int is_write)
{
struct sil_sata *sata = sata_dev_desc[dev].priv;
struct sil_cmd_block cmdb, *pcmd = &cmdb;
struct sata_fis_d2h fis;
u64 block;
int ret;
block = (u64)start;
memset(pcmd, 0, sizeof(struct sil_cmd_block));
pcmd->prb.ctrl = cpu_to_le16(PRB_CTRL_PROTOCOL);
pcmd->prb.fis.fis_type = SATA_FIS_TYPE_REGISTER_H2D;
pcmd->prb.fis.pm_port_c = (1 << 7);
if (is_write) {
pcmd->prb.fis.command = ATA_CMD_WRITE_EXT;
pcmd->prb.prot = cpu_to_le16(PRB_PROT_WRITE);
} else {
pcmd->prb.fis.command = ATA_CMD_READ_EXT;
pcmd->prb.prot = cpu_to_le16(PRB_PROT_READ);
}
pcmd->prb.fis.lba_high_exp = (block >> 40) & 0xff;
pcmd->prb.fis.lba_mid_exp = (block >> 32) & 0xff;
pcmd->prb.fis.lba_low_exp = (block >> 24) & 0xff;
pcmd->prb.fis.lba_high = (block >> 16) & 0xff;
pcmd->prb.fis.lba_mid = (block >> 8) & 0xff;
pcmd->prb.fis.lba_low = block & 0xff;
pcmd->prb.fis.device = ATA_LBA;
pcmd->prb.fis.sector_count_exp = (blkcnt >> 8) & 0xff;
pcmd->prb.fis.sector_count = blkcnt & 0xff;
pcmd->sge.addr = cpu_to_le64(virt_to_bus(sata->devno, buffer));
pcmd->sge.cnt = cpu_to_le32(blkcnt * ATA_SECT_SIZE);
pcmd->sge.flags = cpu_to_le32(SGE_TRM);
ret = sil_exec_cmd(dev, pcmd, 0);
if (ret) {
sil_read_fis(dev, 0, &fis);
printf("Err: rw ext cmd(0x%08x).\n",
readl(sata->port + PORT_SERROR));
sil_sata_dump_fis(&fis);
return 1;
}
return blkcnt;
}
ulong sil_sata_rw_lba28(int dev, ulong blknr, lbaint_t blkcnt,
void *buffer, int is_write)
{
ulong start, blks, max_blks;
u8 *addr;
start = blknr;
blks = blkcnt;
addr = (u8 *)buffer;
max_blks = ATA_MAX_SECTORS;
do {
if (blks > max_blks) {
sil_sata_rw_cmd(dev, start, max_blks, addr, is_write);
start += max_blks;
blks -= max_blks;
addr += ATA_SECT_SIZE * max_blks;
} else {
sil_sata_rw_cmd(dev, start, blks, addr, is_write);
start += blks;
blks = 0;
addr += ATA_SECT_SIZE * blks;
}
} while (blks != 0);
return blkcnt;
}
ulong sil_sata_rw_lba48(int dev, ulong blknr, lbaint_t blkcnt,
void *buffer, int is_write)
{
ulong start, blks, max_blks;
u8 *addr;
start = blknr;
blks = blkcnt;
addr = (u8 *)buffer;
max_blks = ATA_MAX_SECTORS_LBA48;
do {
if (blks > max_blks) {
sil_sata_rw_cmd_ext(dev, start, max_blks,
addr, is_write);
start += max_blks;
blks -= max_blks;
addr += ATA_SECT_SIZE * max_blks;
} else {
sil_sata_rw_cmd_ext(dev, start, blks,
addr, is_write);
start += blks;
blks = 0;
addr += ATA_SECT_SIZE * blks;
}
} while (blks != 0);
return blkcnt;
}
void sil_sata_cmd_flush_cache(int dev)
{
struct sil_cmd_block cmdb, *pcmd = &cmdb;
memset((void *)pcmd, 0, sizeof(struct sil_cmd_block));
pcmd->prb.fis.fis_type = SATA_FIS_TYPE_REGISTER_H2D;
pcmd->prb.fis.pm_port_c = (1 << 7);
pcmd->prb.fis.command = ATA_CMD_FLUSH;
sil_exec_cmd(dev, pcmd, 0);
}
void sil_sata_cmd_flush_cache_ext(int dev)
{
struct sil_cmd_block cmdb, *pcmd = &cmdb;
memset((void *)pcmd, 0, sizeof(struct sil_cmd_block));
pcmd->prb.fis.fis_type = SATA_FIS_TYPE_REGISTER_H2D;
pcmd->prb.fis.pm_port_c = (1 << 7);
pcmd->prb.fis.command = ATA_CMD_FLUSH_EXT;
sil_exec_cmd(dev, pcmd, 0);
}
static void sil_sata_init_wcache(int dev, u16 *id)
{
struct sil_sata *sata = sata_dev_desc[dev].priv;
if (ata_id_has_wcache(id) && ata_id_wcache_enabled(id))
sata->wcache = 1;
if (ata_id_has_flush(id))
sata->flush = 1;
if (ata_id_has_flush_ext(id))
sata->flush_ext = 1;
}
static int sil_sata_get_wcache(int dev)
{
struct sil_sata *sata = sata_dev_desc[dev].priv;
return sata->wcache;
}
static int sil_sata_get_flush(int dev)
{
struct sil_sata *sata = sata_dev_desc[dev].priv;
return sata->flush;
}
static int sil_sata_get_flush_ext(int dev)
{
struct sil_sata *sata = sata_dev_desc[dev].priv;
return sata->flush_ext;
}
/*
* SATA interface between low level driver and command layer
*/
ulong sata_read(int dev, ulong blknr, lbaint_t blkcnt, void *buffer)
{
struct sil_sata *sata = sata_dev_desc[dev].priv;
ulong rc;
if (sata->lba48)
rc = sil_sata_rw_lba48(dev, blknr, blkcnt, buffer, READ_CMD);
else
rc = sil_sata_rw_lba28(dev, blknr, blkcnt, buffer, READ_CMD);
return rc;
}
/*
* SATA interface between low level driver and command layer
*/
ulong sata_write(int dev, ulong blknr, lbaint_t blkcnt, void *buffer)
{
struct sil_sata *sata = sata_dev_desc[dev].priv;
ulong rc;
if (sata->lba48) {
rc = sil_sata_rw_lba48(dev, blknr, blkcnt, buffer, WRITE_CMD);
if (sil_sata_get_wcache(dev) && sil_sata_get_flush_ext(dev))
sil_sata_cmd_flush_cache_ext(dev);
} else {
rc = sil_sata_rw_lba28(dev, blknr, blkcnt, buffer, WRITE_CMD);
if (sil_sata_get_wcache(dev) && sil_sata_get_flush(dev))
sil_sata_cmd_flush_cache(dev);
}
return rc;
}
/*
* SATA interface between low level driver and command layer
*/
int init_sata(int dev)
{
static int init_done, idx;
pci_dev_t devno;
u16 word;
if (init_done == 1 && dev < sata_info.maxport)
return 1;
init_done = 1;
/* Find PCI device(s) */
devno = pci_find_devices(supported, idx++);
if (devno == -1)
return 1;
pci_read_config_word(devno, PCI_DEVICE_ID, &word);
/* get the port count */
word &= 0xf;
sata_info.portbase = sata_info.maxport;
sata_info.maxport = sata_info.portbase + word;
sata_info.devno = devno;
/* Read out all BARs */
sata_info.iobase[0] = (ulong)pci_map_bar(devno,
PCI_BASE_ADDRESS_0, PCI_REGION_MEM);
sata_info.iobase[1] = (ulong)pci_map_bar(devno,
PCI_BASE_ADDRESS_2, PCI_REGION_MEM);
sata_info.iobase[2] = (ulong)pci_map_bar(devno,
PCI_BASE_ADDRESS_4, PCI_REGION_MEM);
/* mask out the unused bits */
sata_info.iobase[0] &= 0xffffff80;
sata_info.iobase[1] &= 0xfffffc00;
sata_info.iobase[2] &= 0xffffff80;
/* Enable Bus Mastering and memory region */
pci_write_config_word(devno, PCI_COMMAND,
PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
/* Check if mem accesses and Bus Mastering are enabled. */
pci_read_config_word(devno, PCI_COMMAND, &word);
if (!(word & PCI_COMMAND_MEMORY) ||
(!(word & PCI_COMMAND_MASTER))) {
printf("Error: Can not enable MEM access or Bus Mastering.\n");
debug("PCI command: %04x\n", word);
return 1;
}
/* GPIO off */
writel(0, (void *)(sata_info.iobase[0] + HOST_FLASH_CMD));
/* clear global reset & mask interrupts during initialization */
writel(0, (void *)(sata_info.iobase[0] + HOST_CTRL));
return 0;
}
/*
* SATA interface between low level driver and command layer
*/
int scan_sata(int dev)
{
unsigned char serial[ATA_ID_SERNO_LEN + 1];
unsigned char firmware[ATA_ID_FW_REV_LEN + 1];
unsigned char product[ATA_ID_PROD_LEN + 1];
struct sil_sata *sata;
void *port;
int cnt;
u16 *id;
u32 tmp;
if (dev >= sata_info.maxport) {
printf("SATA#%d is not present\n", dev);
return 1;
}
printf("SATA#%d\n", dev);
port = (void *)sata_info.iobase[1] +
PORT_REGS_SIZE * (dev - sata_info.portbase);
/* Initial PHY setting */
writel(0x20c, port + PORT_PHY_CFG);
/* clear port RST */
tmp = readl(port + PORT_CTRL_STAT);
if (tmp & PORT_CS_PORT_RST) {
writel(PORT_CS_PORT_RST, port + PORT_CTRL_CLR);
tmp = ata_wait_register(port + PORT_CTRL_STAT,
PORT_CS_PORT_RST, PORT_CS_PORT_RST, 100);
if (tmp & PORT_CS_PORT_RST)
printf("Err: Failed to clear port RST\n");
}
/* Check if device is present */
for (cnt = 0; cnt < 100; cnt++) {
tmp = readl(port + PORT_SSTATUS);
if ((tmp & 0xF) == 0x3)
break;
mdelay(1);
}
tmp = readl(port + PORT_SSTATUS);
if ((tmp & 0xf) != 0x3) {
printf(" (No RDY)\n");
return 1;
}
/* Wait for port ready */
tmp = ata_wait_register(port + PORT_CTRL_STAT,
PORT_CS_RDY, PORT_CS_RDY, 100);
if ((tmp & PORT_CS_RDY) != PORT_CS_RDY) {
printf("%d port not ready.\n", dev);
return 1;
}
/* configure port */
sil_config_port(port);
/* Reset port */
writel(PORT_CS_DEV_RST, port + PORT_CTRL_STAT);
readl(port + PORT_CTRL_STAT);
tmp = ata_wait_register(port + PORT_CTRL_STAT, PORT_CS_DEV_RST,
PORT_CS_DEV_RST, 100);
if (tmp & PORT_CS_DEV_RST) {
printf("%d port reset failed.\n", dev);
return 1;
}
sata = (struct sil_sata *)malloc(sizeof(struct sil_sata));
if (!sata) {
printf("%d no memory.\n", dev);
return 1;
}
memset((void *)sata, 0, sizeof(struct sil_sata));
/* turn on port interrupt */
tmp = readl((void *)(sata_info.iobase[0] + HOST_CTRL));
tmp |= (1 << (dev - sata_info.portbase));
writel(tmp, (void *)(sata_info.iobase[0] + HOST_CTRL));
/* Save the private struct to block device struct */
sata_dev_desc[dev].priv = (void *)sata;
sata->port = port;
sata->devno = sata_info.devno;
sprintf(sata->name, "SATA#%d", dev);
sil_cmd_soft_reset(dev);
tmp = readl(port + PORT_SSTATUS);
tmp = (tmp >> 4) & 0xf;
printf(" (%s)\n", sata_spd_string(tmp));
id = (u16 *)malloc(ATA_ID_WORDS * 2);
if (!id) {
printf("Id malloc failed\n");
free((void *)sata);
return 1;
}
sil_cmd_identify_device(dev, id);
#ifdef CONFIG_LBA48
/* Check if support LBA48 */
if (ata_id_has_lba48(id)) {
sata_dev_desc[dev].lba48 = 1;
sata->lba48 = 1;
debug("Device supports LBA48\n");
} else
debug("Device supports LBA28\n");
#endif
/* Serial number */
ata_id_c_string(id, serial, ATA_ID_SERNO, sizeof(serial));
memcpy(sata_dev_desc[dev].product, serial, sizeof(serial));
/* Firmware version */
ata_id_c_string(id, firmware, ATA_ID_FW_REV, sizeof(firmware));
memcpy(sata_dev_desc[dev].revision, firmware, sizeof(firmware));
/* Product model */
ata_id_c_string(id, product, ATA_ID_PROD, sizeof(product));
memcpy(sata_dev_desc[dev].vendor, product, sizeof(product));
/* Totoal sectors */
sata_dev_desc[dev].lba = ata_id_n_sectors(id);
sil_sata_init_wcache(dev, id);
sil_cmd_set_feature(dev);
#ifdef DEBUG
sil_cmd_identify_device(dev, id);
ata_dump_id(id);
#endif
free((void *)id);
return 0;
}

View file

@ -0,0 +1,229 @@
/*
* Copyright (C) 2011 Freescale Semiconductor, Inc.
* Author: Tang Yuantian <b29983@freescale.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
*/
#ifndef SATA_SIL3132_H
#define SATA_SIL3132_H
#define READ_CMD 0
#define WRITE_CMD 1
extern block_dev_desc_t sata_dev_desc[CONFIG_SYS_SATA_MAX_DEVICE];
/*
* SATA device driver struct for each dev
*/
struct sil_sata {
char name[12];
void *port; /* the port base address */
int lba48;
u16 pio;
u16 mwdma;
u16 udma;
pci_dev_t devno;
int wcache;
int flush;
int flush_ext;
};
/* sata info for each controller */
struct sata_info {
ulong iobase[3];
pci_dev_t devno;
int portbase;
int maxport;
};
/*
* Scatter gather entry (SGE),MUST 8 bytes aligned
*/
struct sil_sge {
__le64 addr;
__le32 cnt;
__le32 flags;
} __attribute__ ((aligned(8), packed));
/*
* Port request block, MUST 8 bytes aligned
*/
struct sil_prb {
__le16 ctrl;
__le16 prot;
__le32 rx_cnt;
struct sata_fis_h2d fis;
} __attribute__ ((aligned(8), packed));
struct sil_cmd_block {
struct sil_prb prb;
struct sil_sge sge;
};
enum {
HOST_SLOT_STAT = 0x00, /* 32 bit slot stat * 4 */
HOST_CTRL = 0x40,
HOST_IRQ_STAT = 0x44,
HOST_PHY_CFG = 0x48,
HOST_BIST_CTRL = 0x50,
HOST_BIST_PTRN = 0x54,
HOST_BIST_STAT = 0x58,
HOST_MEM_BIST_STAT = 0x5c,
HOST_FLASH_CMD = 0x70,
/* 8 bit regs */
HOST_FLASH_DATA = 0x74,
HOST_TRANSITION_DETECT = 0x75,
HOST_GPIO_CTRL = 0x76,
HOST_I2C_ADDR = 0x78, /* 32 bit */
HOST_I2C_DATA = 0x7c,
HOST_I2C_XFER_CNT = 0x7e,
HOST_I2C_CTRL = 0x7f,
/* HOST_SLOT_STAT bits */
HOST_SSTAT_ATTN = (1 << 31),
/* HOST_CTRL bits */
HOST_CTRL_M66EN = (1 << 16), /* M66EN PCI bus signal */
HOST_CTRL_TRDY = (1 << 17), /* latched PCI TRDY */
HOST_CTRL_STOP = (1 << 18), /* latched PCI STOP */
HOST_CTRL_DEVSEL = (1 << 19), /* latched PCI DEVSEL */
HOST_CTRL_REQ64 = (1 << 20), /* latched PCI REQ64 */
HOST_CTRL_GLOBAL_RST = (1 << 31), /* global reset */
/*
* Port registers
* (8192 bytes @ +0x0000, +0x2000, +0x4000 and +0x6000 @ BAR2)
*/
PORT_REGS_SIZE = 0x2000,
PORT_LRAM = 0x0000, /* 31 LRAM slots and PMP regs */
PORT_LRAM_SLOT_SZ = 0x0080, /* 32 bytes PRB + 2 SGE, ACT... */
PORT_PMP = 0x0f80, /* 8 bytes PMP * 16 (128 bytes) */
PORT_PMP_STATUS = 0x0000, /* port device status offset */
PORT_PMP_QACTIVE = 0x0004, /* port device QActive offset */
PORT_PMP_SIZE = 0x0008, /* 8 bytes per PMP */
/* 32 bit regs */
PORT_CTRL_STAT = 0x1000, /* write: ctrl-set, read: stat */
PORT_CTRL_CLR = 0x1004, /* write: ctrl-clear */
PORT_IRQ_STAT = 0x1008, /* high: status, low: interrupt */
PORT_IRQ_ENABLE_SET = 0x1010, /* write: enable-set */
PORT_IRQ_ENABLE_CLR = 0x1014, /* write: enable-clear */
PORT_ACTIVATE_UPPER_ADDR = 0x101c,
PORT_EXEC_FIFO = 0x1020, /* command execution fifo */
PORT_CMD_ERR = 0x1024, /* command error number */
PORT_FIS_CFG = 0x1028,
PORT_FIFO_THRES = 0x102c,
/* 16 bit regs */
PORT_DECODE_ERR_CNT = 0x1040,
PORT_DECODE_ERR_THRESH = 0x1042,
PORT_CRC_ERR_CNT = 0x1044,
PORT_CRC_ERR_THRESH = 0x1046,
PORT_HSHK_ERR_CNT = 0x1048,
PORT_HSHK_ERR_THRESH = 0x104a,
/* 32 bit regs */
PORT_PHY_CFG = 0x1050,
PORT_SLOT_STAT = 0x1800,
PORT_CMD_ACTIVATE = 0x1c00, /* 64 bit cmd activate * 31 */
PORT_CONTEXT = 0x1e04,
PORT_EXEC_DIAG = 0x1e00, /* 32bit exec diag * 16 */
PORT_PSD_DIAG = 0x1e40, /* 32bit psd diag * 16 */
PORT_SCONTROL = 0x1f00,
PORT_SSTATUS = 0x1f04,
PORT_SERROR = 0x1f08,
PORT_SACTIVE = 0x1f0c,
/* PORT_CTRL_STAT bits */
PORT_CS_PORT_RST = (1 << 0), /* port reset */
PORT_CS_DEV_RST = (1 << 1), /* device reset */
PORT_CS_INIT = (1 << 2), /* port initialize */
PORT_CS_IRQ_WOC = (1 << 3), /* interrupt write one to clear */
PORT_CS_CDB16 = (1 << 5), /* 0=12b cdb, 1=16b cdb */
PORT_CS_PMP_RESUME = (1 << 6), /* PMP resume */
PORT_CS_32BIT_ACTV = (1 << 10), /* 32-bit activation */
PORT_CS_PMP_EN = (1 << 13), /* port multiplier enable */
PORT_CS_RDY = (1 << 31), /* port ready to accept commands */
/* PORT_IRQ_STAT/ENABLE_SET/CLR */
/* bits[11:0] are masked */
PORT_IRQ_COMPLETE = (1 << 0), /* command(s) completed */
PORT_IRQ_ERROR = (1 << 1), /* command execution error */
PORT_IRQ_PORTRDY_CHG = (1 << 2), /* port ready change */
PORT_IRQ_PWR_CHG = (1 << 3), /* power management change */
PORT_IRQ_PHYRDY_CHG = (1 << 4), /* PHY ready change */
PORT_IRQ_COMWAKE = (1 << 5), /* COMWAKE received */
PORT_IRQ_UNK_FIS = (1 << 6), /* unknown FIS received */
PORT_IRQ_DEV_XCHG = (1 << 7), /* device exchanged */
PORT_IRQ_8B10B = (1 << 8), /* 8b/10b decode error threshold */
PORT_IRQ_CRC = (1 << 9), /* CRC error threshold */
PORT_IRQ_HANDSHAKE = (1 << 10), /* handshake error threshold */
PORT_IRQ_SDB_NOTIFY = (1 << 11), /* SDB notify received */
DEF_PORT_IRQ = PORT_IRQ_COMPLETE | PORT_IRQ_ERROR |
PORT_IRQ_PHYRDY_CHG | PORT_IRQ_DEV_XCHG |
PORT_IRQ_UNK_FIS | PORT_IRQ_SDB_NOTIFY,
/* bits[27:16] are unmasked (raw) */
PORT_IRQ_RAW_SHIFT = 16,
PORT_IRQ_MASKED_MASK = 0x7ff,
PORT_IRQ_RAW_MASK = (0x7ff << PORT_IRQ_RAW_SHIFT),
/* ENABLE_SET/CLR specific, intr steering - 2 bit field */
PORT_IRQ_STEER_SHIFT = 30,
PORT_IRQ_STEER_MASK = (3 << PORT_IRQ_STEER_SHIFT),
/* PORT_CMD_ERR constants */
PORT_CERR_DEV = 1, /* Error bit in D2H Register FIS */
PORT_CERR_SDB = 2, /* Error bit in SDB FIS */
PORT_CERR_DATA = 3, /* Error in data FIS not detected by dev */
PORT_CERR_SEND = 4, /* Initial cmd FIS transmission failure */
PORT_CERR_INCONSISTENT = 5, /* Protocol mismatch */
PORT_CERR_DIRECTION = 6, /* Data direction mismatch */
PORT_CERR_UNDERRUN = 7, /* Ran out of SGEs while writing */
PORT_CERR_OVERRUN = 8, /* Ran out of SGEs while reading */
/* bits of PRB control field */
PRB_CTRL_PROTOCOL = (1 << 0), /* override def. ATA protocol */
PRB_CTRL_PACKET_READ = (1 << 4), /* PACKET cmd read */
PRB_CTRL_PACKET_WRITE = (1 << 5), /* PACKET cmd write */
PRB_CTRL_NIEN = (1 << 6), /* Mask completion irq */
PRB_CTRL_SRST = (1 << 7), /* Soft reset request (ign BSY?) */
/* PRB protocol field */
PRB_PROT_PACKET = (1 << 0),
PRB_PROT_TCQ = (1 << 1),
PRB_PROT_NCQ = (1 << 2),
PRB_PROT_READ = (1 << 3),
PRB_PROT_WRITE = (1 << 4),
PRB_PROT_TRANSPARENT = (1 << 5),
/*
* Other constants
*/
SGE_TRM = (1 << 31), /* Last SGE in chain */
SGE_LNK = (1 << 30), /* linked list
Points to SGT, not SGE */
SGE_DRD = (1 << 29), /* discard data read (/dev/null)
data address ignored */
CMD_ERR = 0x21,
};
#endif

View file

@ -0,0 +1,843 @@
/*
* 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%lx\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;
#if (CONFIG_SYS_SATA_MAX_DEVICE >= 1)
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;
#elif (CONFIG_SYS_SATA_MAX_DEVICE >= 2)
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;
#elif (CONFIG_SYS_SATA_MAX_DEVICE >= 3)
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;
#endif
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;
}

View file

@ -0,0 +1,147 @@
/*
* 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
*/
#ifndef SATA_SIL3114_H
#define SATA_SIL3114_H
struct sata_ioports {
unsigned long cmd_addr;
unsigned long data_addr;
unsigned long error_addr;
unsigned long feature_addr;
unsigned long nsect_addr;
unsigned long lbal_addr;
unsigned long lbam_addr;
unsigned long lbah_addr;
unsigned long device_addr;
unsigned long status_addr;
unsigned long command_addr;
unsigned long altstatus_addr;
unsigned long ctl_addr;
unsigned long bmdma_addr;
unsigned long scr_addr;
};
struct sata_port {
unsigned char port_no; /* primary=0, secondary=1 */
struct sata_ioports ioaddr; /* ATA cmd/ctl/dma reg blks */
unsigned char ctl_reg;
unsigned char last_ctl;
unsigned char port_state; /* 1-port is available and */
/* 0-port is not available */
unsigned char dev_mask;
};
/* Missing ata defines */
#define ATA_CMD_STANDBY 0xE2
#define ATA_CMD_STANDBYNOW1 0xE0
#define ATA_CMD_IDLE 0xE3
#define ATA_CMD_IDLEIMMEDIATE 0xE1
/* Defines for SIL3114 chip */
/* PCI defines */
#define SIL_VEND_ID 0x1095
#define SIL3114_DEVICE_ID 0x3114
/* some vendor specific registers */
#define VND_SYSCONFSTAT 0x88 /* System Configuration Status and Command */
#define VND_SYSCONFSTAT_CHN_0_INTBLOCK (1<<22)
#define VND_SYSCONFSTAT_CHN_1_INTBLOCK (1<<23)
#define VND_SYSCONFSTAT_CHN_2_INTBLOCK (1<<24)
#define VND_SYSCONFSTAT_CHN_3_INTBLOCK (1<<25)
/* internal registers mapped by BAR5 */
/* SATA Control*/
#define VND_SCONTROL_CH0 0x100
#define VND_SCONTROL_CH1 0x180
#define VND_SCONTROL_CH2 0x300
#define VND_SCONTROL_CH3 0x380
#define SATA_SC_IPM_T2P (1<<16)
#define SATA_SC_IPM_T2S (2<<16)
#define SATA_SC_SPD_1_5 (1<<4)
#define SATA_SC_SPD_3_0 (2<<4)
#define SATA_SC_DET_RST (1) /* ATA Reset sequence */
#define SATA_SC_DET_PDIS (4) /* PHY Disable */
/* SATA Status */
#define VND_SSTATUS_CH0 0x104
#define VND_SSTATUS_CH1 0x184
#define VND_SSTATUS_CH2 0x304
#define VND_SSTATUS_CH3 0x384
#define SATA_SS_IPM_ACTIVE (1<<8)
#define SATA_SS_IPM_PARTIAL (2<<8)
#define SATA_SS_IPM_SLUMBER (6<<8)
#define SATA_SS_SPD_1_5 (1<<4)
#define SATA_SS_SPD_3_0 (2<<4)
#define SATA_DET_P_NOPHY (1) /* Device presence but no PHY connection established */
#define SATA_DET_PRES (3) /* Device presence and active PHY */
#define SATA_DET_OFFLINE (4) /* Device offline or in loopback mode */
/* Task file registers in BAR5 mapping */
#define VND_TF0_CH0 0x80
#define VND_TF0_CH1 0xc0
#define VND_TF0_CH2 0x280
#define VND_TF0_CH3 0x2c0
#define VND_TF1_CH0 0x88
#define VND_TF1_CH1 0xc8
#define VND_TF1_CH2 0x288
#define VND_TF1_CH3 0x2c8
#define VND_TF2_CH0 0x88
#define VND_TF2_CH1 0xc8
#define VND_TF2_CH2 0x288
#define VND_TF2_CH3 0x2c8
#define VND_BMDMA_CH0 0x00
#define VND_BMDMA_CH1 0x08
#define VND_BMDMA_CH2 0x200
#define VND_BMDMA_CH3 0x208
#define VND_BMDMA2_CH0 0x10
#define VND_BMDMA2_CH1 0x18
#define VND_BMDMA2_CH2 0x210
#define VND_BMDMA2_CH3 0x218
/* FIFO control */
#define VND_FIFOCFG_CH0 0x40
#define VND_FIFOCFG_CH1 0x44
#define VND_FIFOCFG_CH2 0x240
#define VND_FIFOCFG_CH3 0x244
/* Task File configuration and status */
#define VND_TF_CNST_CH0 0xa0
#define VND_TF_CNST_CH1 0xe0
#define VND_TF_CNST_CH2 0x2a0
#define VND_TF_CNST_CH3 0x2e0
#define VND_TF_CNST_BFCMD (1<<1)
#define VND_TF_CNST_CHNRST (1<<2)
#define VND_TF_CNST_VDMA (1<<10)
#define VND_TF_CNST_INTST (1<<11)
#define VND_TF_CNST_WDTO (1<<12)
#define VND_TF_CNST_WDEN (1<<13)
#define VND_TF_CNST_WDIEN (1<<14)
/* for testing */
#define VND_SSDR 0x04c /* System Software Data Register */
#define VND_FMACS 0x050 /* Flash Memory Address control and status */
#endif

View file

@ -0,0 +1,107 @@
/*
* (C) Copyright 2007
* Gary Jennejohn, DENX Software Engineering, garyj@denx.de.
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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
*
*/
/* sil680.c - ide support functions for the Sil0680A controller */
/*
* The following parameters must be defined in the configuration file
* of the target board:
*
* #define CONFIG_IDE_SIL680
*
* #define CONFIG_PCI_PNP
* NOTE it may also be necessary to define this if the default of 8 is
* incorrect for the target board (e.g. the sequoia board requires 0).
* #define CONFIG_SYS_PCI_CACHE_LINE_SIZE 0
*
* #define CONFIG_CMD_IDE
* #undef CONFIG_IDE_8xx_DIRECT
* #undef CONFIG_IDE_LED
* #undef CONFIG_IDE_RESET
* #define CONFIG_IDE_PREINIT
* #define CONFIG_SYS_IDE_MAXBUS 2 - modify to suit
* #define CONFIG_SYS_IDE_MAXDEVICE (CONFIG_SYS_IDE_MAXBUS*2) - modify to suit
* #define CONFIG_SYS_ATA_BASE_ADDR 0
* #define CONFIG_SYS_ATA_IDE0_OFFSET 0
* #define CONFIG_SYS_ATA_IDE1_OFFSET 0
* #define CONFIG_SYS_ATA_DATA_OFFSET 0
* #define CONFIG_SYS_ATA_REG_OFFSET 0
* #define CONFIG_SYS_ATA_ALT_OFFSET 0x0004
*
* The mapping for PCI IO-space.
* NOTE this is the value for the sequoia board. Modify to suit.
* #define CONFIG_SYS_PCI0_IO_SPACE 0xE8000000
*/
#include <common.h>
#include <ata.h>
#include <ide.h>
#include <pci.h>
extern ulong ide_bus_offset[CONFIG_SYS_IDE_MAXBUS];
int ide_preinit (void)
{
int status;
pci_dev_t devbusfn;
int l;
status = 1;
for (l = 0; l < CONFIG_SYS_IDE_MAXBUS; l++) {
ide_bus_offset[l] = -ATA_STATUS;
}
devbusfn = pci_find_device (0x1095, 0x0680, 0);
if (devbusfn != -1) {
status = 0;
pci_read_config_dword (devbusfn, PCI_BASE_ADDRESS_0,
(u32 *) &ide_bus_offset[0]);
ide_bus_offset[0] &= 0xfffffff8;
ide_bus_offset[0] += CONFIG_SYS_PCI0_IO_SPACE;
pci_read_config_dword (devbusfn, PCI_BASE_ADDRESS_2,
(u32 *) &ide_bus_offset[1]);
ide_bus_offset[1] &= 0xfffffff8;
ide_bus_offset[1] += CONFIG_SYS_PCI0_IO_SPACE;
/* init various things - taken from the Linux driver */
/* set PIO mode */
pci_write_config_byte(devbusfn, 0x80, 0x00);
pci_write_config_byte(devbusfn, 0x84, 0x00);
/* IDE0 */
pci_write_config_byte(devbusfn, 0xA1, 0x02);
pci_write_config_word(devbusfn, 0xA2, 0x328A);
pci_write_config_dword(devbusfn, 0xA4, 0x62DD62DD);
pci_write_config_dword(devbusfn, 0xA8, 0x43924392);
pci_write_config_dword(devbusfn, 0xAC, 0x40094009);
/* IDE1 */
pci_write_config_byte(devbusfn, 0xB1, 0x02);
pci_write_config_word(devbusfn, 0xB2, 0x328A);
pci_write_config_dword(devbusfn, 0xB4, 0x62DD62DD);
pci_write_config_dword(devbusfn, 0xB8, 0x43924392);
pci_write_config_dword(devbusfn, 0xBC, 0x40094009);
}
return (status);
}
void ide_set_reset (int flag) {
return;
}

View file

@ -0,0 +1,868 @@
/*
* (C) Copyright 2001
* Denis Peter, MPL AG Switzerland, d.peter@mpl.ch.
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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
* partly derived from
* linux/drivers/scsi/sym53c8xx.c
*
*/
/*
* SCSI support based on the chip sym53C810.
*
* 09-19-2001 Andreas Heppel, Sysgo RTS GmbH <aheppel@sysgo.de>
* The local version of this driver for the BAB750 board does not
* use interrupts but polls the chip instead (see the call of
* 'handle_scsi_int()' in 'scsi_issue()'.
*/
#include <common.h>
#include <command.h>
#include <pci.h>
#include <asm/processor.h>
#include <sym53c8xx.h>
#include <scsi.h>
#undef SYM53C8XX_DEBUG
#ifdef SYM53C8XX_DEBUG
#define PRINTF(fmt,args...) printf (fmt ,##args)
#else
#define PRINTF(fmt,args...)
#endif
#if defined(CONFIG_CMD_SCSI) && defined(CONFIG_SCSI_SYM53C8XX)
#undef SCSI_SINGLE_STEP
/*
* Single Step is only used for debug purposes
*/
#ifdef SCSI_SINGLE_STEP
static unsigned long start_script_select;
static unsigned long start_script_msgout;
static unsigned long start_script_msgin;
static unsigned long start_script_msg_ext;
static unsigned long start_script_cmd;
static unsigned long start_script_data_in;
static unsigned long start_script_data_out;
static unsigned long start_script_status;
static unsigned long start_script_complete;
static unsigned long start_script_error;
static unsigned long start_script_reselection;
static unsigned int len_script_select;
static unsigned int len_script_msgout;
static unsigned int len_script_msgin;
static unsigned int len_script_msg_ext;
static unsigned int len_script_cmd;
static unsigned int len_script_data_in;
static unsigned int len_script_data_out;
static unsigned int len_script_status;
static unsigned int len_script_complete;
static unsigned int len_script_error;
static unsigned int len_script_reselection;
#endif
static unsigned short scsi_int_mask; /* shadow register for SCSI related interrupts */
static unsigned char script_int_mask; /* shadow register for SCRIPT related interrupts */
static unsigned long script_select[8]; /* script for selection */
static unsigned long script_msgout[8]; /* script for message out phase (NOT USED) */
static unsigned long script_msgin[14]; /* script for message in phase */
static unsigned long script_msg_ext[32]; /* script for message in phase when more than 1 byte message */
static unsigned long script_cmd[18]; /* script for command phase */
static unsigned long script_data_in[8]; /* script for data in phase */
static unsigned long script_data_out[8]; /* script for data out phase */
static unsigned long script_status[6]; /* script for status phase */
static unsigned long script_complete[10]; /* script for complete */
static unsigned long script_reselection[4]; /* script for reselection (NOT USED) */
static unsigned long script_error[2]; /* script for error handling */
static unsigned long int_stat[3]; /* interrupt status */
static unsigned long scsi_mem_addr; /* base memory address =SCSI_MEM_ADDRESS; */
#define bus_to_phys(a) pci_mem_to_phys(busdevfunc, (unsigned long) (a))
#define phys_to_bus(a) pci_phys_to_mem(busdevfunc, (unsigned long) (a))
#define SCSI_MAX_RETRY 3 /* number of retries in scsi_issue() */
#define SCSI_MAX_RETRY_NOT_READY 10 /* number of retries when device is not ready */
#define SCSI_NOT_READY_TIME_OUT 500 /* timeout per retry when not ready */
/*********************************************************************************
* forward declerations
*/
void scsi_chip_init(void);
void handle_scsi_int(void);
/********************************************************************************
* reports SCSI errors to the user
*/
void scsi_print_error (ccb * pccb)
{
int i;
printf ("SCSI Error: Target %d LUN %d Command %02X\n", pccb->target,
pccb->lun, pccb->cmd[0]);
printf (" CCB: ");
for (i = 0; i < pccb->cmdlen; i++)
printf ("%02X ", pccb->cmd[i]);
printf ("(len=%d)\n", pccb->cmdlen);
printf (" Cntrl: ");
switch (pccb->contr_stat) {
case SIR_COMPLETE:
printf ("Complete (no Error)\n");
break;
case SIR_SEL_ATN_NO_MSG_OUT:
printf ("Selected with ATN no MSG out phase\n");
break;
case SIR_CMD_OUT_ILL_PH:
printf ("Command out illegal phase\n");
break;
case SIR_MSG_RECEIVED:
printf ("MSG received Error\n");
break;
case SIR_DATA_IN_ERR:
printf ("Data in Error\n");
break;
case SIR_DATA_OUT_ERR:
printf ("Data out Error\n");
break;
case SIR_SCRIPT_ERROR:
printf ("Script Error\n");
break;
case SIR_MSG_OUT_NO_CMD:
printf ("MSG out no Command phase\n");
break;
case SIR_MSG_OVER7:
printf ("MSG in over 7 bytes\n");
break;
case INT_ON_FY:
printf ("Interrupt on fly\n");
break;
case SCSI_SEL_TIME_OUT:
printf ("SCSI Selection Timeout\n");
break;
case SCSI_HNS_TIME_OUT:
printf ("SCSI Handshake Timeout\n");
break;
case SCSI_MA_TIME_OUT:
printf ("SCSI Phase Error\n");
break;
case SCSI_UNEXP_DIS:
printf ("SCSI unexpected disconnect\n");
break;
default:
printf ("unknown status %lx\n", pccb->contr_stat);
break;
}
printf (" Sense: SK %x (", pccb->sense_buf[2] & 0x0f);
switch (pccb->sense_buf[2] & 0xf) {
case SENSE_NO_SENSE:
printf ("No Sense)");
break;
case SENSE_RECOVERED_ERROR:
printf ("Recovered Error)");
break;
case SENSE_NOT_READY:
printf ("Not Ready)");
break;
case SENSE_MEDIUM_ERROR:
printf ("Medium Error)");
break;
case SENSE_HARDWARE_ERROR:
printf ("Hardware Error)");
break;
case SENSE_ILLEGAL_REQUEST:
printf ("Illegal request)");
break;
case SENSE_UNIT_ATTENTION:
printf ("Unit Attention)");
break;
case SENSE_DATA_PROTECT:
printf ("Data Protect)");
break;
case SENSE_BLANK_CHECK:
printf ("Blank check)");
break;
case SENSE_VENDOR_SPECIFIC:
printf ("Vendor specific)");
break;
case SENSE_COPY_ABORTED:
printf ("Copy aborted)");
break;
case SENSE_ABORTED_COMMAND:
printf ("Aborted Command)");
break;
case SENSE_VOLUME_OVERFLOW:
printf ("Volume overflow)");
break;
case SENSE_MISCOMPARE:
printf ("Misscompare\n");
break;
default:
printf ("Illegal Sensecode\n");
break;
}
printf (" ASC %x ASCQ %x\n", pccb->sense_buf[12],
pccb->sense_buf[13]);
printf (" Status: ");
switch (pccb->status) {
case S_GOOD:
printf ("Good\n");
break;
case S_CHECK_COND:
printf ("Check condition\n");
break;
case S_COND_MET:
printf ("Condition Met\n");
break;
case S_BUSY:
printf ("Busy\n");
break;
case S_INT:
printf ("Intermediate\n");
break;
case S_INT_COND_MET:
printf ("Intermediate condition met\n");
break;
case S_CONFLICT:
printf ("Reservation conflict\n");
break;
case S_TERMINATED:
printf ("Command terminated\n");
break;
case S_QUEUE_FULL:
printf ("Task set full\n");
break;
default:
printf ("unknown: %02X\n", pccb->status);
break;
}
}
/******************************************************************************
* sets-up the SCSI controller
* the base memory address is retrived via the pci_read_config_dword
*/
void scsi_low_level_init(int busdevfunc)
{
unsigned int cmd;
unsigned int addr;
unsigned char vec;
pci_read_config_byte(busdevfunc, PCI_INTERRUPT_LINE, &vec);
pci_read_config_dword(busdevfunc, PCI_BASE_ADDRESS_1, &addr);
addr = bus_to_phys(addr & ~0xf);
/*
* Enable bus mastering in case this has not been done, yet.
*/
pci_read_config_dword(busdevfunc, PCI_COMMAND, &cmd);
cmd |= PCI_COMMAND_MASTER;
pci_write_config_dword(busdevfunc, PCI_COMMAND, cmd);
scsi_mem_addr = addr;
scsi_chip_init();
scsi_bus_reset();
}
/************************************************************************************
* Low level Part of SCSI Driver
*/
/*
* big-endian -> little endian conversion for the script
*/
unsigned long swap_script(unsigned long val)
{
unsigned long tmp;
tmp = ((val>>24)&0xff) | ((val>>8)&0xff00) | ((val<<8)&0xff0000) | ((val<<24)&0xff000000);
return tmp;
}
void scsi_write_byte(ulong offset,unsigned char val)
{
out8(scsi_mem_addr+offset,val);
}
unsigned char scsi_read_byte(ulong offset)
{
return(in8(scsi_mem_addr+offset));
}
/********************************************************************************
* interrupt handler
*/
void handle_scsi_int(void)
{
unsigned char stat,stat1,stat2;
unsigned short sstat;
int i;
#ifdef SCSI_SINGLE_STEP
unsigned long tt;
#endif
stat=scsi_read_byte(ISTAT);
if((stat & DIP)==DIP) { /* DMA Interrupt pending */
stat1=scsi_read_byte(DSTAT);
#ifdef SCSI_SINGLE_STEP
if((stat1 & SSI)==SSI) {
tt=in32r(scsi_mem_addr+DSP);
if(((tt)>=start_script_select) && ((tt)<start_script_select+len_script_select)) {
printf("select %d\n",(tt-start_script_select)>>2);
goto end_single;
}
if(((tt)>=start_script_msgout) && ((tt)<start_script_msgout+len_script_msgout)) {
printf("msgout %d\n",(tt-start_script_msgout)>>2);
goto end_single;
}
if(((tt)>=start_script_msgin) && ((tt)<start_script_msgin+len_script_msgin)) {
printf("msgin %d\n",(tt-start_script_msgin)>>2);
goto end_single;
}
if(((tt)>=start_script_msg_ext) && ((tt)<start_script_msg_ext+len_script_msg_ext)) {
printf("msgin_ext %d\n",(tt-start_script_msg_ext)>>2);
goto end_single;
}
if(((tt)>=start_script_cmd) && ((tt)<start_script_cmd+len_script_cmd)) {
printf("cmd %d\n",(tt-start_script_cmd)>>2);
goto end_single;
}
if(((tt)>=start_script_data_in) && ((tt)<start_script_data_in+len_script_data_in)) {
printf("data_in %d\n",(tt-start_script_data_in)>>2);
goto end_single;
}
if(((tt)>=start_script_data_out) && ((tt)<start_script_data_out+len_script_data_out)) {
printf("data_out %d\n",(tt-start_script_data_out)>>2);
goto end_single;
}
if(((tt)>=start_script_status) && ((tt)<start_script_status+len_script_status)) {
printf("status %d\n",(tt-start_script_status)>>2);
goto end_single;
}
if(((tt)>=start_script_complete) && ((tt)<start_script_complete+len_script_complete)) {
printf("complete %d\n",(tt-start_script_complete)>>2);
goto end_single;
}
if(((tt)>=start_script_error) && ((tt)<start_script_error+len_script_error)) {
printf("error %d\n",(tt-start_script_error)>>2);
goto end_single;
}
if(((tt)>=start_script_reselection) && ((tt)<start_script_reselection+len_script_reselection)) {
printf("reselection %d\n",(tt-start_script_reselection)>>2);
goto end_single;
}
printf("sc: %lx\n",tt);
end_single:
stat2=scsi_read_byte(DCNTL);
stat2|=STD;
scsi_write_byte(DCNTL,stat2);
}
#endif
if((stat1 & SIR)==SIR) /* script interrupt */
{
int_stat[0]=in32(scsi_mem_addr+DSPS);
}
if((stat1 & DFE)==0) { /* fifo not epmty */
scsi_write_byte(CTEST3,CLF); /* Clear DMA FIFO */
stat2=scsi_read_byte(STEST3);
scsi_write_byte(STEST3,(stat2 | CSF)); /* Clear SCSI FIFO */
}
}
if((stat & SIP)==SIP) { /* scsi interrupt */
sstat = (unsigned short)scsi_read_byte(SIST+1);
sstat <<=8;
sstat |= (unsigned short)scsi_read_byte(SIST);
for(i=0;i<3;i++) {
if(int_stat[i]==0)
break; /* found an empty int status */
}
int_stat[i]=SCSI_INT_STATE | sstat;
stat1=scsi_read_byte(DSTAT);
if((stat1 & DFE)==0) { /* fifo not epmty */
scsi_write_byte(CTEST3,CLF); /* Clear DMA FIFO */
stat2=scsi_read_byte(STEST3);
scsi_write_byte(STEST3,(stat2 | CSF)); /* Clear SCSI FIFO */
}
}
if((stat & INTF)==INTF) { /* interrupt on Fly */
scsi_write_byte(ISTAT,stat); /* clear it */
for(i=0;i<3;i++) {
if(int_stat[i]==0)
break; /* found an empty int status */
}
int_stat[i]=INT_ON_FY;
}
}
void scsi_bus_reset(void)
{
unsigned char t;
int i;
int end = CONFIG_SYS_SCSI_SPIN_UP_TIME*1000;
t=scsi_read_byte(SCNTL1);
scsi_write_byte(SCNTL1,(t | CRST));
udelay(50);
scsi_write_byte(SCNTL1,t);
puts("waiting for devices to spin up");
for(i=0;i<end;i++) {
udelay(1000); /* give the devices time to spin up */
if (i % 1000 == 0)
putc('.');
}
putc('\n');
scsi_chip_init(); /* reinit the chip ...*/
}
void scsi_int_enable(void)
{
scsi_write_byte(SIEN,(unsigned char)scsi_int_mask);
scsi_write_byte(SIEN+1,(unsigned char)(scsi_int_mask>>8));
scsi_write_byte(DIEN,script_int_mask);
}
void scsi_write_dsp(unsigned long start)
{
#ifdef SCSI_SINGLE_STEP
unsigned char t;
#endif
out32r(scsi_mem_addr + DSP,start);
#ifdef SCSI_SINGLE_STEP
t=scsi_read_byte(DCNTL);
t|=STD;
scsi_write_byte(DCNTL,t);
#endif
}
/* only used for debug purposes */
void scsi_print_script(void)
{
printf("script_select @ 0x%08lX\n",(unsigned long)&script_select[0]);
printf("script_msgout @ 0x%08lX\n",(unsigned long)&script_msgout[0]);
printf("script_msgin @ 0x%08lX\n",(unsigned long)&script_msgin[0]);
printf("script_msgext @ 0x%08lX\n",(unsigned long)&script_msg_ext[0]);
printf("script_cmd @ 0x%08lX\n",(unsigned long)&script_cmd[0]);
printf("script_data_in @ 0x%08lX\n",(unsigned long)&script_data_in[0]);
printf("script_data_out @ 0x%08lX\n",(unsigned long)&script_data_out[0]);
printf("script_status @ 0x%08lX\n",(unsigned long)&script_status[0]);
printf("script_complete @ 0x%08lX\n",(unsigned long)&script_complete[0]);
printf("script_error @ 0x%08lX\n",(unsigned long)&script_error[0]);
}
void scsi_set_script(ccb *pccb)
{
int busdevfunc = pccb->priv;
int i;
i=0;
script_select[i++]=swap_script(SCR_REG_REG(GPREG, SCR_AND, 0xfe));
script_select[i++]=0; /* LED ON */
script_select[i++]=swap_script(SCR_CLR(SCR_TRG)); /* select initiator mode */
script_select[i++]=0;
/* script_select[i++]=swap_script(SCR_SEL_ABS_ATN | pccb->target << 16); */
script_select[i++]=swap_script(SCR_SEL_ABS | pccb->target << 16);
script_select[i++]=swap_script(phys_to_bus(&script_cmd[4])); /* error handling */
script_select[i++]=swap_script(SCR_JUMP); /* next section */
/* script_select[i++]=swap_script((unsigned long)&script_msgout[0]); */ /* message out */
script_select[i++]=swap_script(phys_to_bus(&script_cmd[0])); /* command out */
#ifdef SCSI_SINGLE_STEP
start_script_select=(unsigned long)&script_select[0];
len_script_select=i*4;
#endif
i=0;
script_msgout[i++]=swap_script(SCR_INT ^ IFFALSE (WHEN (SCR_MSG_OUT)));
script_msgout[i++]=SIR_SEL_ATN_NO_MSG_OUT;
script_msgout[i++]=swap_script( SCR_MOVE_ABS(1) ^ SCR_MSG_OUT);
script_msgout[i++]=swap_script(phys_to_bus(&pccb->msgout[0]));
script_msgout[i++]=swap_script(SCR_JUMP ^ IFTRUE (WHEN (SCR_COMMAND))); /* if Command phase */
script_msgout[i++]=swap_script(phys_to_bus(&script_cmd[0])); /* switch to command */
script_msgout[i++]=swap_script(SCR_INT); /* interrupt if not */
script_msgout[i++]=SIR_MSG_OUT_NO_CMD;
#ifdef SCSI_SINGLE_STEP
start_script_msgout=(unsigned long)&script_msgout[0];
len_script_msgout=i*4;
#endif
i=0;
script_cmd[i++]=swap_script(SCR_MOVE_ABS(pccb->cmdlen) ^ SCR_COMMAND);
script_cmd[i++]=swap_script(phys_to_bus(&pccb->cmd[0]));
script_cmd[i++]=swap_script(SCR_JUMP ^ IFTRUE (WHEN (SCR_MSG_IN))); /* message in ? */
script_cmd[i++]=swap_script(phys_to_bus(&script_msgin[0]));
script_cmd[i++]=swap_script(SCR_JUMP ^ IFTRUE (IF (SCR_DATA_OUT))); /* data out ? */
script_cmd[i++]=swap_script(phys_to_bus(&script_data_out[0]));
script_cmd[i++]=swap_script(SCR_JUMP ^ IFTRUE (IF (SCR_DATA_IN))); /* data in ? */
script_cmd[i++]=swap_script(phys_to_bus(&script_data_in[0]));
script_cmd[i++]=swap_script(SCR_JUMP ^ IFTRUE (IF (SCR_STATUS))); /* status ? */
script_cmd[i++]=swap_script(phys_to_bus(&script_status[0]));
script_cmd[i++]=swap_script(SCR_JUMP ^ IFTRUE (IF (SCR_COMMAND))); /* command ? */
script_cmd[i++]=swap_script(phys_to_bus(&script_cmd[0]));
script_cmd[i++]=swap_script(SCR_JUMP ^ IFTRUE (IF (SCR_MSG_OUT))); /* message out ? */
script_cmd[i++]=swap_script(phys_to_bus(&script_msgout[0]));
script_cmd[i++]=swap_script(SCR_JUMP ^ IFTRUE (IF (SCR_MSG_IN))); /* just for error handling message in ? */
script_cmd[i++]=swap_script(phys_to_bus(&script_msgin[0]));
script_cmd[i++]=swap_script(SCR_INT); /* interrupt if not */
script_cmd[i++]=SIR_CMD_OUT_ILL_PH;
#ifdef SCSI_SINGLE_STEP
start_script_cmd=(unsigned long)&script_cmd[0];
len_script_cmd=i*4;
#endif
i=0;
script_data_out[i++]=swap_script(SCR_MOVE_ABS(pccb->datalen)^ SCR_DATA_OUT); /* move */
script_data_out[i++]=swap_script(phys_to_bus(pccb->pdata)); /* pointer to buffer */
script_data_out[i++]=swap_script(SCR_JUMP ^ IFTRUE (WHEN (SCR_STATUS)));
script_data_out[i++]=swap_script(phys_to_bus(&script_status[0]));
script_data_out[i++]=swap_script(SCR_INT);
script_data_out[i++]=SIR_DATA_OUT_ERR;
#ifdef SCSI_SINGLE_STEP
start_script_data_out=(unsigned long)&script_data_out[0];
len_script_data_out=i*4;
#endif
i=0;
script_data_in[i++]=swap_script(SCR_MOVE_ABS(pccb->datalen)^ SCR_DATA_IN); /* move */
script_data_in[i++]=swap_script(phys_to_bus(pccb->pdata)); /* pointer to buffer */
script_data_in[i++]=swap_script(SCR_JUMP ^ IFTRUE (WHEN (SCR_STATUS)));
script_data_in[i++]=swap_script(phys_to_bus(&script_status[0]));
script_data_in[i++]=swap_script(SCR_INT);
script_data_in[i++]=SIR_DATA_IN_ERR;
#ifdef SCSI_SINGLE_STEP
start_script_data_in=(unsigned long)&script_data_in[0];
len_script_data_in=i*4;
#endif
i=0;
script_msgin[i++]=swap_script(SCR_MOVE_ABS (1) ^ SCR_MSG_IN);
script_msgin[i++]=swap_script(phys_to_bus(&pccb->msgin[0]));
script_msgin[i++]=swap_script(SCR_JUMP ^ IFTRUE (DATA (M_COMPLETE)));
script_msgin[i++]=swap_script(phys_to_bus(&script_complete[0]));
script_msgin[i++]=swap_script(SCR_JUMP ^ IFTRUE (DATA (M_DISCONNECT)));
script_msgin[i++]=swap_script(phys_to_bus(&script_complete[0]));
script_msgin[i++]=swap_script(SCR_JUMP ^ IFTRUE (DATA (M_SAVE_DP)));
script_msgin[i++]=swap_script(phys_to_bus(&script_complete[0]));
script_msgin[i++]=swap_script(SCR_JUMP ^ IFTRUE (DATA (M_RESTORE_DP)));
script_msgin[i++]=swap_script(phys_to_bus(&script_complete[0]));
script_msgin[i++]=swap_script(SCR_JUMP ^ IFTRUE (DATA (M_EXTENDED)));
script_msgin[i++]=swap_script(phys_to_bus(&script_msg_ext[0]));
script_msgin[i++]=swap_script(SCR_INT);
script_msgin[i++]=SIR_MSG_RECEIVED;
#ifdef SCSI_SINGLE_STEP
start_script_msgin=(unsigned long)&script_msgin[0];
len_script_msgin=i*4;
#endif
i=0;
script_msg_ext[i++]=swap_script(SCR_CLR (SCR_ACK)); /* clear ACK */
script_msg_ext[i++]=0;
script_msg_ext[i++]=swap_script(SCR_MOVE_ABS (1) ^ SCR_MSG_IN); /* assuming this is the msg length */
script_msg_ext[i++]=swap_script(phys_to_bus(&pccb->msgin[1]));
script_msg_ext[i++]=swap_script(SCR_JUMP ^ IFFALSE (IF (SCR_MSG_IN)));
script_msg_ext[i++]=swap_script(phys_to_bus(&script_complete[0])); /* no more bytes */
script_msg_ext[i++]=swap_script(SCR_MOVE_ABS (1) ^ SCR_MSG_IN); /* next */
script_msg_ext[i++]=swap_script(phys_to_bus(&pccb->msgin[2]));
script_msg_ext[i++]=swap_script(SCR_JUMP ^ IFFALSE (IF (SCR_MSG_IN)));
script_msg_ext[i++]=swap_script(phys_to_bus(&script_complete[0])); /* no more bytes */
script_msg_ext[i++]=swap_script(SCR_MOVE_ABS (1) ^ SCR_MSG_IN); /* next */
script_msg_ext[i++]=swap_script(phys_to_bus(&pccb->msgin[3]));
script_msg_ext[i++]=swap_script(SCR_JUMP ^ IFFALSE (IF (SCR_MSG_IN)));
script_msg_ext[i++]=swap_script(phys_to_bus(&script_complete[0])); /* no more bytes */
script_msg_ext[i++]=swap_script(SCR_MOVE_ABS (1) ^ SCR_MSG_IN); /* next */
script_msg_ext[i++]=swap_script(phys_to_bus(&pccb->msgin[4]));
script_msg_ext[i++]=swap_script(SCR_JUMP ^ IFFALSE (IF (SCR_MSG_IN)));
script_msg_ext[i++]=swap_script(phys_to_bus(&script_complete[0])); /* no more bytes */
script_msg_ext[i++]=swap_script(SCR_MOVE_ABS (1) ^ SCR_MSG_IN); /* next */
script_msg_ext[i++]=swap_script(phys_to_bus(&pccb->msgin[5]));
script_msg_ext[i++]=swap_script(SCR_JUMP ^ IFFALSE (IF (SCR_MSG_IN)));
script_msg_ext[i++]=swap_script(phys_to_bus(&script_complete[0])); /* no more bytes */
script_msg_ext[i++]=swap_script(SCR_MOVE_ABS (1) ^ SCR_MSG_IN); /* next */
script_msg_ext[i++]=swap_script(phys_to_bus(&pccb->msgin[6]));
script_msg_ext[i++]=swap_script(SCR_JUMP ^ IFFALSE (IF (SCR_MSG_IN)));
script_msg_ext[i++]=swap_script(phys_to_bus(&script_complete[0])); /* no more bytes */
script_msg_ext[i++]=swap_script(SCR_MOVE_ABS (1) ^ SCR_MSG_IN); /* next */
script_msg_ext[i++]=swap_script(phys_to_bus(&pccb->msgin[7]));
script_msg_ext[i++]=swap_script(SCR_JUMP ^ IFFALSE (IF (SCR_MSG_IN)));
script_msg_ext[i++]=swap_script(phys_to_bus(&script_complete[0])); /* no more bytes */
script_msg_ext[i++]=swap_script(SCR_INT);
script_msg_ext[i++]=SIR_MSG_OVER7;
#ifdef SCSI_SINGLE_STEP
start_script_msg_ext=(unsigned long)&script_msg_ext[0];
len_script_msg_ext=i*4;
#endif
i=0;
script_status[i++]=swap_script(SCR_MOVE_ABS (1) ^ SCR_STATUS);
script_status[i++]=swap_script(phys_to_bus(&pccb->status));
script_status[i++]=swap_script(SCR_JUMP ^ IFTRUE (WHEN (SCR_MSG_IN)));
script_status[i++]=swap_script(phys_to_bus(&script_msgin[0]));
script_status[i++]=swap_script(SCR_INT);
script_status[i++]=SIR_STATUS_ILL_PH;
#ifdef SCSI_SINGLE_STEP
start_script_status=(unsigned long)&script_status[0];
len_script_status=i*4;
#endif
i=0;
script_complete[i++]=swap_script(SCR_REG_REG (SCNTL2, SCR_AND, 0x7f));
script_complete[i++]=0;
script_complete[i++]=swap_script(SCR_CLR (SCR_ACK|SCR_ATN));
script_complete[i++]=0;
script_complete[i++]=swap_script(SCR_WAIT_DISC);
script_complete[i++]=0;
script_complete[i++]=swap_script(SCR_REG_REG(GPREG, SCR_OR, 0x01));
script_complete[i++]=0; /* LED OFF */
script_complete[i++]=swap_script(SCR_INT);
script_complete[i++]=SIR_COMPLETE;
#ifdef SCSI_SINGLE_STEP
start_script_complete=(unsigned long)&script_complete[0];
len_script_complete=i*4;
#endif
i=0;
script_error[i++]=swap_script(SCR_INT); /* interrupt if error */
script_error[i++]=SIR_SCRIPT_ERROR;
#ifdef SCSI_SINGLE_STEP
start_script_error=(unsigned long)&script_error[0];
len_script_error=i*4;
#endif
i=0;
script_reselection[i++]=swap_script(SCR_CLR (SCR_TRG)); /* target status */
script_reselection[i++]=0;
script_reselection[i++]=swap_script(SCR_WAIT_RESEL);
script_reselection[i++]=swap_script(phys_to_bus(&script_select[0])); /* len = 4 */
#ifdef SCSI_SINGLE_STEP
start_script_reselection=(unsigned long)&script_reselection[0];
len_script_reselection=i*4;
#endif
}
void scsi_issue(ccb *pccb)
{
int busdevfunc = pccb->priv;
int i;
unsigned short sstat;
int retrycnt; /* retry counter */
for(i=0;i<3;i++)
int_stat[i]=0; /* delete all int status */
/* struct pccb must be set-up correctly */
retrycnt=0;
PRINTF("ID %d issue cmd %02X\n",pccb->target,pccb->cmd[0]);
pccb->trans_bytes=0; /* no bytes transfered yet */
scsi_set_script(pccb); /* fill in SCRIPT */
scsi_int_mask=STO | UDC | MA; /* | CMP; / * Interrupts which are enabled */
script_int_mask=0xff; /* enable all Ints */
scsi_int_enable();
scsi_write_dsp(phys_to_bus(&script_select[0])); /* start script */
/* now we have to wait for IRQs */
retry:
/*
* This version of the driver is _not_ interrupt driven,
* but polls the chip's interrupt registers (ISTAT, DSTAT).
*/
while(int_stat[0]==0)
handle_scsi_int();
if(int_stat[0]==SIR_COMPLETE) {
if(pccb->msgin[0]==M_DISCONNECT) {
PRINTF("Wait for reselection\n");
for(i=0;i<3;i++)
int_stat[i]=0; /* delete all int status */
scsi_write_dsp(phys_to_bus(&script_reselection[0])); /* start reselection script */
goto retry;
}
pccb->contr_stat=SIR_COMPLETE;
return;
}
if((int_stat[0] & SCSI_INT_STATE)==SCSI_INT_STATE) { /* scsi interrupt */
sstat=(unsigned short)int_stat[0];
if((sstat & STO)==STO) { /* selection timeout */
pccb->contr_stat=SCSI_SEL_TIME_OUT;
scsi_write_byte(GPREG,0x01);
PRINTF("ID: %X Selection Timeout\n",pccb->target);
return;
}
if((sstat & UDC)==UDC) { /* unexpected disconnect */
pccb->contr_stat=SCSI_UNEXP_DIS;
scsi_write_byte(GPREG,0x01);
PRINTF("ID: %X Unexpected Disconnect\n",pccb->target);
return;
}
if((sstat & RSL)==RSL) { /* reselection */
pccb->contr_stat=SCSI_UNEXP_DIS;
scsi_write_byte(GPREG,0x01);
PRINTF("ID: %X Unexpected Disconnect\n",pccb->target);
return;
}
if(((sstat & MA)==MA)||((sstat & HTH)==HTH)) { /* phase missmatch */
if(retrycnt<SCSI_MAX_RETRY) {
pccb->trans_bytes=pccb->datalen -
((unsigned long)scsi_read_byte(DBC) |
((unsigned long)scsi_read_byte(DBC+1)<<8) |
((unsigned long)scsi_read_byte(DBC+2)<<16));
for(i=0;i<3;i++)
int_stat[i]=0; /* delete all int status */
retrycnt++;
PRINTF("ID: %X Phase Missmatch Retry %d Phase %02X transfered %lx\n",
pccb->target,retrycnt,scsi_read_byte(SBCL),pccb->trans_bytes);
scsi_write_dsp(phys_to_bus(&script_cmd[4])); /* start retry script */
goto retry;
}
if((sstat & MA)==MA)
pccb->contr_stat=SCSI_MA_TIME_OUT;
else
pccb->contr_stat=SCSI_HNS_TIME_OUT;
PRINTF("Phase Missmatch stat %lx\n",pccb->contr_stat);
return;
} /* no phase int */
/* if((sstat & CMP)==CMP) {
pccb->contr_stat=SIR_COMPLETE;
return;
}
*/
PRINTF("SCSI INT %lX\n",int_stat[0]);
pccb->contr_stat=int_stat[0];
return;
} /* end scsi int */
PRINTF("SCRIPT INT %lX phase %02X\n",int_stat[0],scsi_read_byte(SBCL));
pccb->contr_stat=int_stat[0];
return;
}
int scsi_exec(ccb *pccb)
{
unsigned char tmpcmd[16],tmpstat;
int i,retrycnt,t;
unsigned long transbytes,datalen;
unsigned char *tmpptr;
retrycnt=0;
retry:
scsi_issue(pccb);
if(pccb->contr_stat!=SIR_COMPLETE)
return FALSE;
if(pccb->status==S_GOOD)
return TRUE;
if(pccb->status==S_CHECK_COND) { /* check condition */
for(i=0;i<16;i++)
tmpcmd[i]=pccb->cmd[i];
pccb->cmd[0]=SCSI_REQ_SENSE;
pccb->cmd[1]=pccb->lun<<5;
pccb->cmd[2]=0;
pccb->cmd[3]=0;
pccb->cmd[4]=14;
pccb->cmd[5]=0;
pccb->cmdlen=6;
pccb->msgout[0]=SCSI_IDENTIFY;
transbytes=pccb->trans_bytes;
tmpptr=pccb->pdata;
pccb->pdata = &pccb->sense_buf[0];
datalen=pccb->datalen;
pccb->datalen=14;
tmpstat=pccb->status;
scsi_issue(pccb);
for(i=0;i<16;i++)
pccb->cmd[i]=tmpcmd[i];
pccb->trans_bytes=transbytes;
pccb->pdata=tmpptr;
pccb->datalen=datalen;
pccb->status=tmpstat;
PRINTF("Request_sense sense key %x ASC %x ASCQ %x\n",pccb->sense_buf[2]&0x0f,
pccb->sense_buf[12],pccb->sense_buf[13]);
switch(pccb->sense_buf[2]&0xf) {
case SENSE_NO_SENSE:
case SENSE_RECOVERED_ERROR:
/* seems to be ok */
return TRUE;
break;
case SENSE_NOT_READY:
if((pccb->sense_buf[12]!=0x04)||(pccb->sense_buf[13]!=0x01)) {
/* if device is not in process of becoming ready */
return FALSE;
break;
} /* else fall through */
case SENSE_UNIT_ATTENTION:
if(retrycnt<SCSI_MAX_RETRY_NOT_READY) {
PRINTF("Target %d not ready, retry %d\n",pccb->target,retrycnt);
for(t=0;t<SCSI_NOT_READY_TIME_OUT;t++)
udelay(1000); /* 1sec wait */
retrycnt++;
goto retry;
}
PRINTF("Target %d not ready, %d retried\n",pccb->target,retrycnt);
return FALSE;
default:
return FALSE;
}
}
PRINTF("Status = %X\n",pccb->status);
return FALSE;
}
void scsi_chip_init(void)
{
/* first we issue a soft reset */
scsi_write_byte(ISTAT,SRST);
udelay(1000);
scsi_write_byte(ISTAT,0);
/* setup chip */
scsi_write_byte(SCNTL0,0xC0); /* full arbitration no start, no message, parity disabled, master */
scsi_write_byte(SCNTL1,0x00);
scsi_write_byte(SCNTL2,0x00);
#ifndef CONFIG_SYS_SCSI_SYM53C8XX_CCF /* config value for none 40 MHz clocks */
scsi_write_byte(SCNTL3,0x13); /* synchronous clock 40/4=10MHz, asynchronous 40MHz */
#else
scsi_write_byte(SCNTL3,CONFIG_SYS_SCSI_SYM53C8XX_CCF); /* config value for none 40 MHz clocks */
#endif
scsi_write_byte(SCID,0x47); /* ID=7, enable reselection */
scsi_write_byte(SXFER,0x00); /* synchronous transfer period 10MHz, asynchronous */
scsi_write_byte(SDID,0x00); /* targed SCSI ID = 0 */
scsi_int_mask=0x0000; /* no Interrupt is enabled */
script_int_mask=0x00;
scsi_int_enable();
scsi_write_byte(GPREG,0x01); /* GPIO0 is LED (off) */
scsi_write_byte(GPCNTL,0x0E); /* GPIO0 is Output */
scsi_write_byte(STIME0,0x08); /* handshake timer disabled, selection timeout 512msec */
scsi_write_byte(RESPID,0x80); /* repond only to the own ID (reselection) */
scsi_write_byte(STEST1,0x00); /* not isolated, SCLK is used */
scsi_write_byte(STEST2,0x00); /* no Lowlevel Mode? */
scsi_write_byte(STEST3,0x80); /* enable tolerANT */
scsi_write_byte(CTEST3,0x04); /* clear FIFO */
scsi_write_byte(CTEST4,0x00);
scsi_write_byte(CTEST5,0x00);
#ifdef SCSI_SINGLE_STEP
/* scsi_write_byte(DCNTL,IRQM | SSM); */
scsi_write_byte(DCNTL,IRQD | SSM);
scsi_write_byte(DMODE,MAN);
#else
/* scsi_write_byte(DCNTL,IRQM); */
scsi_write_byte(DCNTL,IRQD);
scsi_write_byte(DMODE,0x00);
#endif
}
#endif

View file

@ -0,0 +1,257 @@
/*
* Copyright (c) 2004 Picture Elements, Inc.
* Stephen Williams (XXXXXXXXXXXXXXXX)
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form 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
*/
/*
* The Xilinx SystemACE chip support is activated by defining
* CONFIG_SYSTEMACE to turn on support, and CONFIG_SYS_SYSTEMACE_BASE
* to set the base address of the device. This code currently
* assumes that the chip is connected via a byte-wide bus.
*
* The CONFIG_SYSTEMACE also adds to fat support the device class
* "ace" that allows the user to execute "fatls ace 0" and the
* like. This works by making the systemace_get_dev function
* available to cmd_fat.c:get_dev and filling in a block device
* description that has all the bits needed for FAT support to
* read sectors.
*
* According to Xilinx technical support, before accessing the
* SystemACE CF you need to set the following control bits:
* FORCECFGMODE : 1
* CFGMODE : 0
* CFGSTART : 0
*/
#include <common.h>
#include <command.h>
#include <systemace.h>
#include <part.h>
#include <asm/io.h>
/*
* The ace_readw and writew functions read/write 16bit words, but the
* offset value is the BYTE offset as most used in the Xilinx
* datasheet for the SystemACE chip. The CONFIG_SYS_SYSTEMACE_BASE is defined
* to be the base address for the chip, usually in the local
* peripheral bus.
*/
#if (CONFIG_SYS_SYSTEMACE_WIDTH == 8)
#if !defined(__BIG_ENDIAN)
#define ace_readw(off) ((readb(CONFIG_SYS_SYSTEMACE_BASE+off)<<8) | \
(readb(CONFIG_SYS_SYSTEMACE_BASE+off+1)))
#define ace_writew(val, off) {writeb(val>>8, CONFIG_SYS_SYSTEMACE_BASE+off); \
writeb(val, CONFIG_SYS_SYSTEMACE_BASE+off+1);}
#else
#define ace_readw(off) ((readb(CONFIG_SYS_SYSTEMACE_BASE+off)) | \
(readb(CONFIG_SYS_SYSTEMACE_BASE+off+1)<<8))
#define ace_writew(val, off) {writeb(val, CONFIG_SYS_SYSTEMACE_BASE+off); \
writeb(val>>8, CONFIG_SYS_SYSTEMACE_BASE+off+1);}
#endif
#else
#define ace_readw(off) (in16(CONFIG_SYS_SYSTEMACE_BASE+off))
#define ace_writew(val, off) (out16(CONFIG_SYS_SYSTEMACE_BASE+off,val))
#endif
/* */
static unsigned long systemace_read(int dev, unsigned long start,
unsigned long blkcnt, void *buffer);
static block_dev_desc_t systemace_dev = { 0 };
static int get_cf_lock(void)
{
int retry = 10;
/* CONTROLREG = LOCKREG */
unsigned val = ace_readw(0x18);
val |= 0x0002;
ace_writew((val & 0xffff), 0x18);
/* Wait for MPULOCK in STATUSREG[15:0] */
while (!(ace_readw(0x04) & 0x0002)) {
if (retry < 0)
return -1;
udelay(100000);
retry -= 1;
}
return 0;
}
static void release_cf_lock(void)
{
unsigned val = ace_readw(0x18);
val &= ~(0x0002);
ace_writew((val & 0xffff), 0x18);
}
#ifdef CONFIG_PARTITIONS
block_dev_desc_t *systemace_get_dev(int dev)
{
/* The first time through this, the systemace_dev object is
not yet initialized. In that case, fill it in. */
if (systemace_dev.blksz == 0) {
systemace_dev.if_type = IF_TYPE_UNKNOWN;
systemace_dev.dev = 0;
systemace_dev.part_type = PART_TYPE_UNKNOWN;
systemace_dev.type = DEV_TYPE_HARDDISK;
systemace_dev.blksz = 512;
systemace_dev.removable = 1;
systemace_dev.block_read = systemace_read;
/*
* Ensure the correct bus mode (8/16 bits) gets enabled
*/
ace_writew(CONFIG_SYS_SYSTEMACE_WIDTH == 8 ? 0 : 0x0001, 0);
init_part(&systemace_dev);
}
return &systemace_dev;
}
#endif
/*
* This function is called (by dereferencing the block_read pointer in
* the dev_desc) to read blocks of data. The return value is the
* number of blocks read. A zero return indicates an error.
*/
static unsigned long systemace_read(int dev, unsigned long start,
unsigned long blkcnt, void *buffer)
{
int retry;
unsigned blk_countdown;
unsigned char *dp = buffer;
unsigned val;
if (get_cf_lock() < 0) {
unsigned status = ace_readw(0x04);
/* If CFDETECT is false, card is missing. */
if (!(status & 0x0010)) {
printf("** CompactFlash card not present. **\n");
return 0;
}
printf("**** ACE locked away from me (STATUSREG=%04x)\n",
status);
return 0;
}
#ifdef DEBUG_SYSTEMACE
printf("... systemace read %lu sectors at %lu\n", blkcnt, start);
#endif
retry = 2000;
for (;;) {
val = ace_readw(0x04);
/* If CFDETECT is false, card is missing. */
if (!(val & 0x0010)) {
printf("**** ACE CompactFlash not found.\n");
release_cf_lock();
return 0;
}
/* If RDYFORCMD, then we are ready to go. */
if (val & 0x0100)
break;
if (retry < 0) {
printf("**** SystemACE not ready.\n");
release_cf_lock();
return 0;
}
udelay(1000);
retry -= 1;
}
/* The SystemACE can only transfer 256 sectors at a time, so
limit the current chunk of sectors. The blk_countdown
variable is the number of sectors left to transfer. */
blk_countdown = blkcnt;
while (blk_countdown > 0) {
unsigned trans = blk_countdown;
if (trans > 256)
trans = 256;
#ifdef DEBUG_SYSTEMACE
printf("... transfer %lu sector in a chunk\n", trans);
#endif
/* Write LBA block address */
ace_writew((start >> 0) & 0xffff, 0x10);
ace_writew((start >> 16) & 0x0fff, 0x12);
/* NOTE: in the Write Sector count below, a count of 0
causes a transfer of 256, so &0xff gives the right
value for whatever transfer count we want. */
/* Write sector count | ReadMemCardData. */
ace_writew((trans & 0xff) | 0x0300, 0x14);
/*
* For FPGA configuration via SystemACE is reset unacceptable
* CFGDONE bit in STATUSREG is not set to 1.
*/
#ifndef SYSTEMACE_CONFIG_FPGA
/* Reset the configruation controller */
val = ace_readw(0x18);
val |= 0x0080;
ace_writew(val, 0x18);
#endif
retry = trans * 16;
while (retry > 0) {
int idx;
/* Wait for buffer to become ready. */
while (!(ace_readw(0x04) & 0x0020)) {
udelay(100);
}
/* Read 16 words of 2bytes from the sector buffer. */
for (idx = 0; idx < 16; idx += 1) {
unsigned short val = ace_readw(0x40);
*dp++ = val & 0xff;
*dp++ = (val >> 8) & 0xff;
}
retry -= 1;
}
/* Clear the configruation controller reset */
val = ace_readw(0x18);
val &= ~0x0080;
ace_writew(val, 0x18);
/* Count the blocks we transfer this time. */
start += trans;
blk_countdown -= trans;
}
release_cf_lock();
return blkcnt;
}