Add the Freescale on-chip SATA controller driver to u-boot, The SATA controller is used on the 837x and 8315 targets, The driver can be used to load kernel, fs and dtb. The features list: - 1.5/3 Gbps link speed - LBA48, LBA28 support - DMA and FPDMA support - Two ports support Signed-off-by: Dave Liu <daveliu@freescale.com>master
parent
bede87f4c8
commit
fd0b1fe3c3
@ -0,0 +1,916 @@ |
||||
/*
|
||||
* Copyright (C) 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 |
||||
*/ |
||||
|
||||
#include <common.h> |
||||
#include <command.h> |
||||
#include <asm/io.h> |
||||
#include <malloc.h> |
||||
#include <libata.h> |
||||
#include <fis.h> |
||||
#include "fsl_sata.h" |
||||
|
||||
extern block_dev_desc_t sata_dev_desc[CFG_SATA_MAX_DEVICE]; |
||||
|
||||
#ifndef CFG_SATA1_FLAGS |
||||
#define CFG_SATA1_FLAGS FLAGS_DMA |
||||
#endif |
||||
#ifndef CFG_SATA2_FLAGS |
||||
#define CFG_SATA2_FLAGS FLAGS_DMA |
||||
#endif |
||||
|
||||
static struct fsl_sata_info fsl_sata_info[] = { |
||||
#ifdef CONFIG_SATA1 |
||||
{CFG_SATA1, CFG_SATA1_FLAGS}, |
||||
#else |
||||
{0, 0}, |
||||
#endif |
||||
#ifdef CONFIG_SATA2 |
||||
{CFG_SATA2, CFG_SATA2_FLAGS}, |
||||
#else |
||||
{0, 0}, |
||||
#endif |
||||
}; |
||||
|
||||
static inline void mdelay(unsigned long msec) |
||||
{ |
||||
unsigned long i; |
||||
for (i = 0; i < msec; i++) |
||||
udelay(1000); |
||||
} |
||||
|
||||
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 sfis *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 > (CFG_SATA_MAX_DEVICE - 1)) { |
||||
printf("the sata index %d is out of ranges\n\r", dev); |
||||
return -1; |
||||
} |
||||
|
||||
/* 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); |
||||
|
||||
/* 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; |
||||
} |
||||
|
||||
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 cfis *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 sfis *)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 cfis *cfis, |
||||
int tag, u8 *buffer, u32 len) |
||||
{ |
||||
return 0; |
||||
} |
||||
|
||||
static int fsl_sata_exec_cmd(struct fsl_sata *sata, struct cfis *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=\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; |
||||
struct cfis *cfis; |
||||
|
||||
cfis = (struct cfis *)&h2d; |
||||
memset((void *)cfis, 0, sizeof(struct cfis)); |
||||
|
||||
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; |
||||
struct cfis *cfis; |
||||
u8 udma_cap; |
||||
|
||||
cfis = (struct cfis *)&h2d; |
||||
memset((void *)cfis, 0, sizeof(struct cfis)); |
||||
|
||||
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; |
||||
struct cfis *cfis; |
||||
u32 block; |
||||
|
||||
block = start; |
||||
cfis = (struct cfis *)&h2d; |
||||
|
||||
memset((void *)cfis, 0, sizeof(struct cfis)); |
||||
|
||||
cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D; |
||||
cfis->pm_port_c = 0x80; /* is command */ |
||||
cfis->command = (is_write) ? ATA_CMD_WRITE_DMA : ATA_CMD_READ_DMA; |
||||
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; |
||||
struct cfis *cfis; |
||||
|
||||
cfis = (struct cfis *)&h2d; |
||||
|
||||
memset((void *)cfis, 0, sizeof(struct cfis)); |
||||
|
||||
cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D; |
||||
cfis->pm_port_c = 0x80; /* is command */ |
||||
cfis->command = ATA_CMD_FLUSH_CACHE; |
||||
|
||||
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; |
||||
struct cfis *cfis; |
||||
u64 block; |
||||
|
||||
block = (u64)start; |
||||
cfis = (struct cfis *)&h2d; |
||||
|
||||
memset((void *)cfis, 0, sizeof(struct cfis)); |
||||
|
||||
cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D; |
||||
cfis->pm_port_c = 0x80; /* is command */ |
||||
|
||||
cfis->command = (is_write) ? ATA_CMD_WRITE_DMA_EXT |
||||
: ATA_CMD_READ_DMA_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; |
||||
struct cfis *cfis; |
||||
int ncq_channel; |
||||
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; |
||||
cfis = (struct cfis *)&h2d; |
||||
|
||||
memset((void *)cfis, 0, sizeof(struct cfis)); |
||||
|
||||
cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D; |
||||
cfis->pm_port_c = 0x80; /* is command */ |
||||
|
||||
cfis->command = (is_write) ? ATA_CMD_WRITE_FPDMA_QUEUED |
||||
: ATA_CMD_READ_FPDMA_QUEUED; |
||||
|
||||
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; |
||||
struct cfis *cfis; |
||||
|
||||
cfis = (struct cfis *)&h2d; |
||||
|
||||
memset((void *)cfis, 0, sizeof(struct cfis)); |
||||
|
||||
cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D; |
||||
cfis->pm_port_c = 0x80; /* is command */ |
||||
cfis->command = ATA_CMD_FLUSH_CACHE_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; |
||||
|
||||
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, u32 blknr, u32 blkcnt, void *buffer) |
||||
{ |
||||
u32 rc; |
||||
|
||||
if (sata_dev_desc[dev].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; |
||||
|
||||
/* Check if support LBA48 */ |
||||
if (ata_id_has_lba48(id)) { |
||||
sata_dev_desc[dev].lba48 = 1; |
||||
debug("Device support LBA48\n\r"); |
||||
} |
||||
|
||||
/* 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,374 @@ |
||||
/*
|
||||
* 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_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 or called indirect descriptor flag */ |
||||
#define PRD_ENTRY_DATA_SNOOP 0x00400000 /* Snoop enable for all data associated with the PRD entry */ |
||||
#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 |
||||
|
||||
/*
|
||||
* CFIS - Command FIS, which is H2D register FIS, the struct defination |
||||
* of Non-Queued command is different than NCQ command. see them is sata2.h |
||||
*/ |
||||
typedef struct cfis { |
||||
u8 fis_type; |
||||
u8 pm_port_c; |
||||
u8 command; |
||||
u8 features; |
||||
u8 lba_low; |
||||
u8 lba_mid; |
||||
u8 lba_high; |
||||
u8 device; |
||||
u8 lba_low_exp; |
||||
u8 lba_mid_exp; |
||||
u8 lba_high_exp; |
||||
u8 features_exp; |
||||
u8 sector_count; |
||||
u8 sector_count_exp; |
||||
u8 res1; |
||||
u8 control; |
||||
u8 res2[4]; |
||||
} __attribute__ ((packed)) cfis_t; |
||||
|
||||
/*
|
||||
* SFIS - Status FIS, which is D2H register FIS. |
||||
*/ |
||||
typedef struct sfis { |
||||
u8 fis_type; |
||||
u8 pm_port_i; |
||||
u8 status; |
||||
u8 error; |
||||
u8 lba_low; |
||||
u8 lba_mid; |
||||
u8 lba_high; |
||||
u8 device; |
||||
u8 lba_low_exp; |
||||
u8 lba_mid_exp; |
||||
u8 lba_high_exp; |
||||
u8 res1; |
||||
u8 sector_count; |
||||
u8 sector_count_exp; |
||||
u8 res2[2]; |
||||
u8 res3[4]; |
||||
} __attribute__ ((packed)) sfis_t; |
||||
|
||||
/*
|
||||
* 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__ */ |
Loading…
Reference in new issue