[U-Boot] [PATCH] drivers: add the support for Silicon Image SATA controller
Aaron Williams
Aaron.Williams at cavium.com
Sat Aug 6 06:13:18 CEST 2011
Your driver is certainly a lot cleaner than mine... mine started up as a
really hacked up SATA driver made for U-Boot 1.1.1.
I did find a number of problems, however.
I have several suggestions.
1. On many MIPS platforms (not including ours) you need to flush the cache
before write operations and invalidate it before read operations.
2. On MIPS, pointers cannot be used as DMA addresses since U-Boot always runs
in mapped memory.
3. In sil_read_fis you are trying to access fis directly. In our case this is
not allowed and readl/writel must be used. Our platform requires 64-bit
addressing to access PCI BAR memory and must use the readl/writel routines.
4. When checking if a device is present you need to retry for some time.
Without this at least several of my drives fail to be detected and the result
is (No RDY). This happened with a WD740GD (SATA 1) drive.
5. In sata_init() you have:
debug("PCI command: %s\n", word); which is wrong.
I also get a few warnings:
sata_sil.c: In function 'init_sata':
sata_sil.c:570: warning: passing argument 2 of 'writel' makes pointer from
integer without a cast
sata_sil.c:572: warning: passing argument 2 of 'writel' makes pointer from
integer without a cast
sata_sil.c: In function 'scan_sata':
sata_sil.c:631: warning: passing argument 1 of 'readl' makes pointer from
integer without a cast
sata_sil.c:633: warning: passing argument 2 of 'writel' makes pointer from
integer without a cast
These are the writel's at the end of init_sata() and the middle of scan_sata()
which use sata_info.iobase[].
I've included my local patch to the driver. Note that
virt_to_bus()/pci_virt_to_mem() stuff is specific to our platform since we use
TLB mapping and sometimes PCI I/O remapping in our version of U-Boot.
I'm still experiencing problems with the SIL3124 controller but the SIL3132
works well.
-Aaron
On Friday, August 05, 2011 02:45:59 AM b29983 at freescale.com wrote:
> From: Tang Yuantian <B29983 at freescale.com>
>
> Add the Silicon Image series PCI Express to
> Serial ATA controller support, including Sil3132,
> Sil3131 and Sil3124.
> The SATA controller can be used to load kernel.
>
> The features list:
> - Supports 1-lane 2.5 Gbit/s PCI Express
> - Supports one/two/four independent Serial ATA channels
> - Supports Serial ATA Generation 2 transfer rate of 3.0 Gbit/s
> - Supports LBA28 and LBA48
>
> Signed-off-by: Tang Yuantian <b29983 at freescale.com>
> Tested-by: Lan Chunhe <b25806 at freescale.com>
> ---
> git tree: git://git.denx.de/u-boot.git
> branch: master
> Test platform:P1022DS, P4080DS
>
> drivers/block/Makefile | 1 +
> drivers/block/sata_sil.c | 683
> ++++++++++++++++++++++++++++++++++++++++++++++ drivers/block/sata_sil.h |
> 225 +++++++++++++++
> include/pci_ids.h | 5 +
> 4 files changed, 914 insertions(+), 0 deletions(-)
> create mode 100644 drivers/block/sata_sil.c
> create mode 100644 drivers/block/sata_sil.h
>
> diff --git a/drivers/block/Makefile b/drivers/block/Makefile
> index 2efe981..98560ef 100644
> --- a/drivers/block/Makefile
> +++ b/drivers/block/Makefile
> @@ -36,6 +36,7 @@ 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
> diff --git a/drivers/block/sata_sil.c b/drivers/block/sata_sil.c
> new file mode 100644
> index 0000000..26ac2b0
> --- /dev/null
> +++ b/drivers/block/sata_sil.c
> @@ -0,0 +1,683 @@
> +/*
> + * Copyright (C) 2011 Freescale Semiconductor, Inc.
> + * Author: Tang Yuantian <b29983 at 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 mdelay(n) udelay((n)*1000)
> +
> +extern block_dev_desc_t sata_dev_desc[CONFIG_SYS_SATA_MAX_DEVICE];
> +
> +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},
> + {}
> +};
> +/* port number */
> +static struct sata_info sata_info;
> +
> +static void sil_sata_dump_fis(struct sata_fis_d2h *s)
> +{
> + printf("---------------------\n");
> + 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);
> + printf("---------------------\n");
> +}
> +
> +static const char *sata_spd_string(unsigned int spd)
> +{
> + static const char * const spd_str[] = {
> + "1.5 Gbps",
> + "3.0 Gbps",
> + "6.0 Gbps",
> + };
> +
> + if (spd == 0 || (spd - 1) >= 3)
> + return "<unknown>";
> +
> + return spd_str[spd - 1];
> +}
> +
> +static u32 ata_wait_register(void __iomem *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);
> + mdelay(1);
> +}
> +
> +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 = (struct sil_sata *)sata_dev_desc[dev].priv;
> + void __iomem *port = sata->port;
> + struct sil_prb *prb;
> + prb = port + PORT_LRAM + tag * PORT_LRAM_SLOT_SZ;
> + memcpy(fis, &prb->fis, sizeof(struct sata_fis_h2d));
> +}
> +
> +static int sil_exec_cmd(int dev, struct sil_cmd_block *pcmd, int tag)
> +{
> + struct sil_sata *sata = (struct sil_sata *)sata_dev_desc[dev].priv;
> + void __iomem *port = sata->port;
> + u64 paddr = (ulong)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; /* timeout */
> + }
> +
> + return rc;
> +}
> +
> +static int sil_cmd_set_feature(int dev)
> +{
> + struct sil_sata *sata = (struct sil_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", ATA_CMD_SET_FEATURES);
> + sil_sata_dump_fis(&fis);
> + return 1;
> + }
> +
> + return 0;
> +}
> +
> +static int sil_cmd_identify_device(int dev, u16 *id)
> +{
> + 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((ulong)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: exe cmd(0x%x).\n", ATA_CMD_ID_ATA);
> + 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 = (struct sil_sata *)sata_dev_desc[dev].priv;
> + struct sata_fis_d2h fis;
> + void __iomem *port = sata->port;
> + int ret;
> +
> + /* put the port into known state */
> + if (sil_init_port(port)) {
> + printf("SRST: port not ready\n");
> + 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_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((u32)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: exe rw cmd.\n");
> + 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_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((u32)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: exe rw ext cmd.\n");
> + 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 = (struct sil_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 = (struct sil_sata *)sata_dev_desc[dev].priv;
> + return sata->wcache;
> +}
> +
> +static int sil_sata_get_flush(int dev)
> +{
> + struct sil_sata *sata = (struct sil_sata *)sata_dev_desc[dev].priv;
> + return sata->flush;
> +}
> +
> +static int sil_sata_get_flush_ext(int dev)
> +{
> + struct sil_sata *sata = (struct sil_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 = (struct sil_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 = (struct sil_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 number */
> + word &= 0xf;
> +
> + sata_info.portbase = sata_info.maxport;
> + sata_info.maxport = sata_info.portbase + word;
> +
> + /* 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: %s\n", word);
> + return 1;
> + }
> +
> + /* GPIO off */
> + writel(0, sata_info.iobase[0] + HOST_FLASH_CMD);
> + /* clear global reset & mask interrupts during initialization */
> + writel(0, 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 __iomem *port;
> + 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);
> +
> + sil_init_port(port);
> +
> + /* 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, 1000);
> + if (tmp & PORT_CS_PORT_RST)
> + printf("Err: Failed to clear port RST\n");
> + }
> +
> + /* configure port */
> + sil_config_port(port);
> +
> + tmp = readl(port + PORT_SSTATUS);
> + if ((tmp & 0xf) != 0x3) {
> + printf(" (No RDY)\n");
> + 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(sata_info.iobase[0] + HOST_CTRL);
> + tmp |= (1 << (dev - sata_info.portbase));
> + writel(tmp, sata_info.iobase[0] + HOST_CTRL);
> +
> + /* Save the private struct to block device struct */
> + sata_dev_desc[dev].priv = (void *)sata;
> + sata->port = port;
> + 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 support LBA48\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;
> +}
> diff --git a/drivers/block/sata_sil.h b/drivers/block/sata_sil.h
> new file mode 100644
> index 0000000..78c9a22
> --- /dev/null
> +++ b/drivers/block/sata_sil.h
> @@ -0,0 +1,225 @@
> +/*
> + * Copyright (C) 2011 Freescale Semiconductor, Inc.
> + * Author: Tang Yuantian <b29983 at 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
> +
> +/*
> + * SATA device driver struct for each dev
> + */
> +struct sil_sata {
> + char name[12];
> + void __iomem *port; /* the port base address */
> + int lba48;
> + u16 pio;
> + u16 mwdma;
> + u16 udma;
> + int wcache;
> + int flush;
> + int flush_ext;
> +};
> +
> +/* sata info for each controller */
> +struct sata_info {
> + ulong iobase[3];
> + 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
> diff --git a/include/pci_ids.h b/include/pci_ids.h
> index 02a6c6e..12e4c02 100644
> --- a/include/pci_ids.h
> +++ b/include/pci_ids.h
> @@ -2110,3 +2110,8 @@
>
> #define PCI_VENDOR_ID_ADMTEK 0x1317
> #define PCI_DEVICE_ID_ADMTEK_AN983B 0x0985
> +
> +#define PCI_VENDOR_ID_SILICONIMAGE 0x1095
> +#define PCI_DEVICE_ID_SIL3132 0x3132
> +#define PCI_DEVICE_ID_SIL3124 0x3124
> +#define PCI_DEVICE_ID_SIL3131 0x3131
-------------- next part --------------
A non-text attachment was scrubbed...
Name: sil2.diff
Type: text/x-patch
Size: 5629 bytes
Desc: not available
Url : http://lists.denx.de/pipermail/u-boot/attachments/20110805/82aec2a5/attachment.bin
More information about the U-Boot
mailing list