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:
parent
ccdb64ad45
commit
59bc57d5d5
7254 changed files with 1810270 additions and 7 deletions
|
@ -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
|
||||
|
||||
#########################################################################
|
746
root/package/utils/sysupgrade-helper/src/drivers/block/ahci.c
Normal file
746
root/package/utils/sysupgrade-helper/src/drivers/block/ahci.c
Normal 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*/
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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__ */
|
|
@ -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;
|
||||
}
|
|
@ -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__ */
|
|
@ -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(®->hcontrol);
|
||||
val32 &= ~HCONTROL_ONOFF;
|
||||
val32 |= HCONTROL_FORCE_OFFLINE;
|
||||
out_le32(®->hcontrol, val32);
|
||||
|
||||
/* Wait the controller offline */
|
||||
ata_wait_register(®->hstatus, HSTATUS_ONOFF, 0, 1000);
|
||||
|
||||
/* Set the command header base address to CHBA register to tell DMA */
|
||||
out_le32(®->chba, (u32)cmd_hdr & ~0x3);
|
||||
|
||||
/* Snoop for the command header */
|
||||
val32 = in_le32(®->hcontrol);
|
||||
val32 |= HCONTROL_HDR_SNOOP;
|
||||
out_le32(®->hcontrol, val32);
|
||||
|
||||
/* Disable all of interrupts */
|
||||
val32 = in_le32(®->hcontrol);
|
||||
val32 &= ~HCONTROL_INT_EN_ALL;
|
||||
out_le32(®->hcontrol, val32);
|
||||
|
||||
/* Clear all of interrupts */
|
||||
val32 = in_le32(®->hstatus);
|
||||
out_le32(®->hstatus, val32);
|
||||
|
||||
/* Set the ICC, no interrupt coalescing */
|
||||
out_le32(®->icc, 0x01000000);
|
||||
|
||||
/* No PM attatched, the SATA device direct connect */
|
||||
out_le32(®->cqpmp, 0);
|
||||
|
||||
/* Clear SError register */
|
||||
val32 = in_le32(®->serror);
|
||||
out_le32(®->serror, val32);
|
||||
|
||||
/* Clear CER register */
|
||||
val32 = in_le32(®->cer);
|
||||
out_le32(®->cer, val32);
|
||||
|
||||
/* Clear DER register */
|
||||
val32 = in_le32(®->der);
|
||||
out_le32(®->der, val32);
|
||||
|
||||
/* No device detection or initialization action requested */
|
||||
out_le32(®->scontrol, 0x00000300);
|
||||
|
||||
/* Configure the transport layer, default value */
|
||||
out_le32(®->transcfg, 0x08000016);
|
||||
|
||||
/* Configure the link layer, default value */
|
||||
out_le32(®->linkcfg, 0x0000ff34);
|
||||
|
||||
/* Bring the controller online */
|
||||
val32 = in_le32(®->hcontrol);
|
||||
val32 |= HCONTROL_ONOFF;
|
||||
out_le32(®->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(®->hstatus, HSTATUS_PHY_RDY,
|
||||
HSTATUS_PHY_RDY, 500);
|
||||
|
||||
/* Check PHYRDY */
|
||||
val32 = in_le32(®->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(®->hstatus, HSTATUS_SIGNATURE,
|
||||
HSTATUS_SIGNATURE, 10000);
|
||||
|
||||
if (val32 & HSTATUS_SIGNATURE) {
|
||||
sig = in_le32(®->sig);
|
||||
debug("Signature updated, the sig =%08x\n\r", sig);
|
||||
sata->ata_device_type = ata_dev_classify(sig);
|
||||
}
|
||||
|
||||
/* Check the speed */
|
||||
val32 = in_le32(®->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(®->scontrol);
|
||||
scontrol = (scontrol & 0x0f0) | 0x304;
|
||||
out_le32(®->scontrol, scontrol);
|
||||
|
||||
/* No speed strict */
|
||||
scontrol = in_le32(®->scontrol);
|
||||
scontrol = scontrol & ~0x0f0;
|
||||
out_le32(®->scontrol, scontrol);
|
||||
|
||||
/* Issue PHY wake/reset, Hardware_reset_asserted */
|
||||
scontrol = in_le32(®->scontrol);
|
||||
scontrol = (scontrol & 0x0f0) | 0x301;
|
||||
out_le32(®->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(®->scontrol);
|
||||
scontrol = (scontrol & 0x0f0) | 0x300;
|
||||
out_le32(®->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(®->cqr));
|
||||
printf("CAR: %08x\n\r", in_le32(®->car));
|
||||
printf("CCR: %08x\n\r", in_le32(®->ccr));
|
||||
printf("CER: %08x\n\r", in_le32(®->cer));
|
||||
printf("CQR: %08x\n\r", in_le32(®->cqr));
|
||||
printf("DER: %08x\n\r", in_le32(®->der));
|
||||
printf("CHBA: %08x\n\r", in_le32(®->chba));
|
||||
printf("HStatus: %08x\n\r", in_le32(®->hstatus));
|
||||
printf("HControl: %08x\n\r", in_le32(®->hcontrol));
|
||||
printf("CQPMP: %08x\n\r", in_le32(®->cqpmp));
|
||||
printf("SIG: %08x\n\r", in_le32(®->sig));
|
||||
printf("ICC: %08x\n\r", in_le32(®->icc));
|
||||
printf("SStatus: %08x\n\r", in_le32(®->sstatus));
|
||||
printf("SError: %08x\n\r", in_le32(®->serror));
|
||||
printf("SControl: %08x\n\r", in_le32(®->scontrol));
|
||||
printf("SNotification: %08x\n\r", in_le32(®->snotification));
|
||||
printf("TransCfg: %08x\n\r", in_le32(®->transcfg));
|
||||
printf("TransStatus: %08x\n\r", in_le32(®->transstatus));
|
||||
printf("LinkCfg: %08x\n\r", in_le32(®->linkcfg));
|
||||
printf("LinkCfg1: %08x\n\r", in_le32(®->linkcfg1));
|
||||
printf("LinkCfg2: %08x\n\r", in_le32(®->linkcfg2));
|
||||
printf("LinkStatus: %08x\n\r", in_le32(®->linkstatus));
|
||||
printf("LinkStatus1: %08x\n\r", in_le32(®->linkstatus1));
|
||||
printf("PhyCtrlCfg: %08x\n\r", in_le32(®->phyctrlcfg));
|
||||
printf("SYSPR: %08x\n\r", in_be32(®->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(®->cqpmp, val32);
|
||||
|
||||
/* Wait no active */
|
||||
if (ata_wait_register(®->car, (1 << tag), 0, 10000))
|
||||
printf("Wait no active time out\n\r");
|
||||
|
||||
/* Issue command */
|
||||
if (!(in_le32(®->cqr) & (1 << tag))) {
|
||||
val32 = 1 << tag;
|
||||
out_le32(®->cqr, val32);
|
||||
}
|
||||
|
||||
/* Wait command completed for 10s */
|
||||
if (ata_wait_register(®->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(®->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(®->der);
|
||||
out_le32(®->cer, val32);
|
||||
out_le32(®->der, der);
|
||||
}
|
||||
|
||||
/* Clear complete flags */
|
||||
val32 = in_le32(®->ccr);
|
||||
out_le32(®->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;
|
||||
}
|
|
@ -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__ */
|
|
@ -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;
|
||||
}
|
|
@ -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 */
|
158
root/package/utils/sysupgrade-helper/src/drivers/block/libata.c
Normal file
158
root/package/utils/sysupgrade-helper/src/drivers/block/libata.c
Normal 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]);
|
||||
}
|
|
@ -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;
|
||||
}
|
142
root/package/utils/sysupgrade-helper/src/drivers/block/mxc_ata.c
Normal file
142
root/package/utils/sysupgrade-helper/src/drivers/block/mxc_ata.c
Normal 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;
|
||||
}
|
1200
root/package/utils/sysupgrade-helper/src/drivers/block/pata_bfin.c
Normal file
1200
root/package/utils/sysupgrade-helper/src/drivers/block/pata_bfin.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -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
|
2077
root/package/utils/sysupgrade-helper/src/drivers/block/sata_dwc.c
Normal file
2077
root/package/utils/sysupgrade-helper/src/drivers/block/sata_dwc.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
107
root/package/utils/sysupgrade-helper/src/drivers/block/sil680.c
Normal file
107
root/package/utils/sysupgrade-helper/src/drivers/block/sil680.c
Normal 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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue