mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-17 10:18:38 +00:00
41575d8e4c
This construct is quite long-winded. In earlier days it made some sense since auto-allocation was a strange concept. But with driver model now used pretty universally, we can shorten this to 'auto'. This reduces verbosity and makes it easier to read. Coincidentally it also ensures that every declaration is on one line, thus making dtoc's job easier. Signed-off-by: Simon Glass <sjg@chromium.org>
927 lines
22 KiB
C
927 lines
22 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* Copyright (C) 2011 Freescale Semiconductor, Inc.
|
|
* Copyright 2019 NXP
|
|
* Author: Tang Yuantian <b29983@freescale.com>
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <cpu_func.h>
|
|
#include <log.h>
|
|
#include <pci.h>
|
|
#include <command.h>
|
|
#include <asm/byteorder.h>
|
|
#include <malloc.h>
|
|
#include <asm/io.h>
|
|
#include <fis.h>
|
|
#include <sata.h>
|
|
#include <libata.h>
|
|
#include <sata.h>
|
|
#include <linux/delay.h>
|
|
|
|
#if CONFIG_IS_ENABLED(BLK)
|
|
#include <dm.h>
|
|
#include <blk.h>
|
|
#include <dm/device-internal.h>
|
|
#endif
|
|
|
|
#include "sata_sil.h"
|
|
|
|
#ifdef CONFIG_DM_PCI
|
|
#define virt_to_bus(devno, v) dm_pci_virt_to_mem(devno, (void *) (v))
|
|
#else
|
|
#define virt_to_bus(devno, v) pci_virt_to_mem(devno, (void *) (v))
|
|
#endif
|
|
|
|
/* just compatible ahci_ops */
|
|
struct sil_ops {
|
|
int *rev0;
|
|
int *rev1;
|
|
int (*scan)(struct udevice *dev);
|
|
};
|
|
|
|
static struct sata_info sata_info;
|
|
|
|
static struct pci_device_id supported[] = {
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_SILICONIMAGE, PCI_DEVICE_ID_SIL3131) },
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_SILICONIMAGE, PCI_DEVICE_ID_SIL3132) },
|
|
{ PCI_DEVICE(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(struct sil_sata *sata, int tag,
|
|
struct sata_fis_d2h *fis)
|
|
{
|
|
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(struct sil_sata *sata, struct sil_cmd_block *pcmd,
|
|
int tag)
|
|
{
|
|
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(struct sil_sata *sata)
|
|
{
|
|
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(sata, pcmd, 0);
|
|
if (ret) {
|
|
sil_read_fis(sata, 0, &fis);
|
|
printf("Err: exe cmd(0x%x).\n",
|
|
readl(sata->port + PORT_SERROR));
|
|
sil_sata_dump_fis(&fis);
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void sil_sata_init_wcache(struct sil_sata *sata, u16 *id)
|
|
{
|
|
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 void sil_sata_set_feature_by_id(struct sil_sata *sata, u16 *id)
|
|
{
|
|
#ifdef CONFIG_LBA48
|
|
/* Check if support LBA48 */
|
|
if (ata_id_has_lba48(id)) {
|
|
sata->lba48 = 1;
|
|
debug("Device supports LBA48\n");
|
|
} else {
|
|
debug("Device supports LBA28\n");
|
|
}
|
|
#endif
|
|
|
|
sil_sata_init_wcache(sata, id);
|
|
sil_cmd_set_feature(sata);
|
|
}
|
|
|
|
static int sil_cmd_identify_device(struct sil_sata *sata, 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(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(sata, pcmd, 0);
|
|
if (ret) {
|
|
sil_read_fis(sata, 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(struct sil_sata *sata)
|
|
{
|
|
struct sil_cmd_block cmdb, *pcmd = &cmdb;
|
|
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", sata->id);
|
|
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(sata, &cmdb, 0);
|
|
if (ret) {
|
|
sil_read_fis(sata, 0, &fis);
|
|
printf("SRST cmd error.\n");
|
|
sil_sata_dump_fis(&fis);
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static ulong sil_sata_rw_cmd(struct sil_sata *sata, 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(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(sata, pcmd, 0);
|
|
if (ret) {
|
|
sil_read_fis(sata, 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(struct sil_sata *sata, 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(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(sata, pcmd, 0);
|
|
if (ret) {
|
|
sil_read_fis(sata, 0, &fis);
|
|
printf("Err: rw ext cmd(0x%08x).\n",
|
|
readl(sata->port + PORT_SERROR));
|
|
sil_sata_dump_fis(&fis);
|
|
return 1;
|
|
}
|
|
|
|
return blkcnt;
|
|
}
|
|
|
|
static ulong sil_sata_rw_lba28(struct sil_sata *sata, ulong blknr,
|
|
lbaint_t blkcnt, const 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(sata, start, max_blks, addr, is_write);
|
|
start += max_blks;
|
|
blks -= max_blks;
|
|
addr += ATA_SECT_SIZE * max_blks;
|
|
} else {
|
|
sil_sata_rw_cmd(sata, start, blks, addr, is_write);
|
|
start += blks;
|
|
blks = 0;
|
|
addr += ATA_SECT_SIZE * blks;
|
|
}
|
|
} while (blks != 0);
|
|
|
|
return blkcnt;
|
|
}
|
|
|
|
static ulong sil_sata_rw_lba48(struct sil_sata *sata, ulong blknr,
|
|
lbaint_t blkcnt, const 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(sata, start, max_blks,
|
|
addr, is_write);
|
|
start += max_blks;
|
|
blks -= max_blks;
|
|
addr += ATA_SECT_SIZE * max_blks;
|
|
} else {
|
|
sil_sata_rw_cmd_ext(sata, start, blks,
|
|
addr, is_write);
|
|
start += blks;
|
|
blks = 0;
|
|
addr += ATA_SECT_SIZE * blks;
|
|
}
|
|
} while (blks != 0);
|
|
|
|
return blkcnt;
|
|
}
|
|
|
|
static void sil_sata_cmd_flush_cache(struct sil_sata *sata)
|
|
{
|
|
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(sata, pcmd, 0);
|
|
}
|
|
|
|
static void sil_sata_cmd_flush_cache_ext(struct sil_sata *sata)
|
|
{
|
|
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(sata, pcmd, 0);
|
|
}
|
|
|
|
/*
|
|
* SATA interface between low level driver and command layer
|
|
*/
|
|
#if !CONFIG_IS_ENABLED(BLK)
|
|
ulong sata_read(int dev, ulong blknr, lbaint_t blkcnt, void *buffer)
|
|
{
|
|
struct sil_sata *sata = (struct sil_sata *)sata_dev_desc[dev].priv;
|
|
#else
|
|
static ulong sata_read(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt,
|
|
void *buffer)
|
|
{
|
|
struct sil_sata_priv *priv = dev_get_platdata(dev);
|
|
int port_number = priv->port_num;
|
|
struct sil_sata *sata = priv->sil_sata_desc[port_number];
|
|
#endif
|
|
ulong rc;
|
|
|
|
if (sata->lba48)
|
|
rc = sil_sata_rw_lba48(sata, blknr, blkcnt, buffer, READ_CMD);
|
|
else
|
|
rc = sil_sata_rw_lba28(sata, blknr, blkcnt, buffer, READ_CMD);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
* SATA interface between low level driver and command layer
|
|
*/
|
|
#if !CONFIG_IS_ENABLED(BLK)
|
|
ulong sata_write(int dev, ulong blknr, lbaint_t blkcnt, const void *buffer)
|
|
{
|
|
struct sil_sata *sata = (struct sil_sata *)sata_dev_desc[dev].priv;
|
|
#else
|
|
ulong sata_write(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt,
|
|
const void *buffer)
|
|
{
|
|
struct sil_sata_priv *priv = dev_get_platdata(dev);
|
|
int port_number = priv->port_num;
|
|
struct sil_sata *sata = priv->sil_sata_desc[port_number];
|
|
#endif
|
|
ulong rc;
|
|
|
|
if (sata->lba48) {
|
|
rc = sil_sata_rw_lba48(sata, blknr, blkcnt, buffer, WRITE_CMD);
|
|
if (sata->wcache && sata->flush_ext)
|
|
sil_sata_cmd_flush_cache_ext(sata);
|
|
} else {
|
|
rc = sil_sata_rw_lba28(sata, blknr, blkcnt, buffer, WRITE_CMD);
|
|
if (sata->wcache && sata->flush)
|
|
sil_sata_cmd_flush_cache(sata);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
#if !CONFIG_IS_ENABLED(BLK)
|
|
static int sil_init_sata(int dev)
|
|
{
|
|
#else
|
|
static int sil_init_sata(struct udevice *uc_dev, int dev)
|
|
{
|
|
struct sil_sata_priv *priv = dev_get_platdata(uc_dev);
|
|
#endif
|
|
struct sil_sata *sata;
|
|
void *port;
|
|
u32 tmp;
|
|
int cnt;
|
|
|
|
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));
|
|
|
|
/* Save the private struct to block device struct */
|
|
#if !CONFIG_IS_ENABLED(BLK)
|
|
sata_dev_desc[dev].priv = (void *)sata;
|
|
sata->devno = sata_info.devno;
|
|
#else
|
|
priv->sil_sata_desc[dev] = sata;
|
|
priv->port_num = dev;
|
|
#ifdef CONFIG_DM_PCI
|
|
sata->devno = uc_dev->parent;
|
|
#else
|
|
sata->devno = sata_info.devno;
|
|
#endif /* CONFIG_DM_PCI */
|
|
#endif
|
|
sata->id = dev;
|
|
sata->port = port;
|
|
sprintf(sata->name, "SATA#%d", dev);
|
|
sil_cmd_soft_reset(sata);
|
|
tmp = readl(port + PORT_SSTATUS);
|
|
tmp = (tmp >> 4) & 0xf;
|
|
printf(" (%s)\n", sata_spd_string(tmp));
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if !CONFIG_IS_ENABLED(BLK)
|
|
/*
|
|
* 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)
|
|
goto init_start;
|
|
|
|
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 = 0;
|
|
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);
|
|
|
|
/* mask out the unused bits */
|
|
sata_info.iobase[0] &= 0xffffff80;
|
|
sata_info.iobase[1] &= 0xfffffc00;
|
|
|
|
/* 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));
|
|
|
|
init_start:
|
|
return sil_init_sata(dev);
|
|
}
|
|
|
|
int reset_sata(int dev)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* SATA interface between low level driver and command layer
|
|
*/
|
|
int scan_sata(int dev)
|
|
{
|
|
struct sil_sata *sata = (struct sil_sata *)sata_dev_desc[dev].priv;
|
|
#else
|
|
static int scan_sata(struct udevice *blk_dev, int dev)
|
|
{
|
|
struct blk_desc *desc = dev_get_uclass_platdata(blk_dev);
|
|
struct sil_sata_priv *priv = dev_get_platdata(blk_dev);
|
|
struct sil_sata *sata = priv->sil_sata_desc[dev];
|
|
#endif
|
|
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;
|
|
|
|
id = (u16 *)malloc(ATA_ID_WORDS * 2);
|
|
if (!id) {
|
|
printf("Id malloc failed\n");
|
|
return 1;
|
|
}
|
|
sil_cmd_identify_device(sata, id);
|
|
|
|
sil_sata_set_feature_by_id(sata, id);
|
|
|
|
/* Serial number */
|
|
ata_id_c_string(id, serial, ATA_ID_SERNO, sizeof(serial));
|
|
|
|
/* Firmware version */
|
|
ata_id_c_string(id, firmware, ATA_ID_FW_REV, sizeof(firmware));
|
|
|
|
/* Product model */
|
|
ata_id_c_string(id, product, ATA_ID_PROD, sizeof(product));
|
|
|
|
#if !CONFIG_IS_ENABLED(BLK)
|
|
memcpy(sata_dev_desc[dev].product, serial, sizeof(serial));
|
|
memcpy(sata_dev_desc[dev].revision, firmware, sizeof(firmware));
|
|
memcpy(sata_dev_desc[dev].vendor, product, sizeof(product));
|
|
/* Totoal sectors */
|
|
sata_dev_desc[dev].lba = ata_id_n_sectors(id);
|
|
#ifdef CONFIG_LBA48
|
|
sata_dev_desc[dev].lba48 = sata->lba48;
|
|
#endif
|
|
#else
|
|
memcpy(desc->product, serial, sizeof(serial));
|
|
memcpy(desc->revision, firmware, sizeof(firmware));
|
|
memcpy(desc->vendor, product, sizeof(product));
|
|
desc->lba = ata_id_n_sectors(id);
|
|
#ifdef CONFIG_LBA48
|
|
desc->lba48 = sata->lba48;
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef DEBUG
|
|
ata_dump_id(id);
|
|
#endif
|
|
free((void *)id);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if CONFIG_IS_ENABLED(BLK)
|
|
static const struct blk_ops sata_sil_blk_ops = {
|
|
.read = sata_read,
|
|
.write = sata_write,
|
|
};
|
|
|
|
U_BOOT_DRIVER(sata_sil_driver) = {
|
|
.name = "sata_sil_blk",
|
|
.id = UCLASS_BLK,
|
|
.ops = &sata_sil_blk_ops,
|
|
.platdata_auto = sizeof(struct sil_sata_priv),
|
|
};
|
|
|
|
static int sil_unbind_device(struct udevice *dev)
|
|
{
|
|
int ret;
|
|
|
|
ret = device_remove(dev, DM_REMOVE_NORMAL);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = device_unbind(dev);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int sil_pci_probe(struct udevice *dev)
|
|
{
|
|
struct udevice *blk;
|
|
int failed_number;
|
|
char sata_name[10];
|
|
pci_dev_t devno;
|
|
u16 word;
|
|
int ret;
|
|
int i;
|
|
|
|
failed_number = 0;
|
|
|
|
/* Get PCI device number */
|
|
devno = dm_pci_get_bdf(dev);
|
|
if (devno == -1)
|
|
return 1;
|
|
|
|
dm_pci_read_config16(dev, PCI_DEVICE_ID, &word);
|
|
|
|
/* get the port count */
|
|
word &= 0xf;
|
|
|
|
sata_info.portbase = 0;
|
|
sata_info.maxport = sata_info.portbase + word;
|
|
sata_info.devno = devno;
|
|
|
|
/* Read out all BARs */
|
|
sata_info.iobase[0] = (ulong)dm_pci_map_bar(dev,
|
|
PCI_BASE_ADDRESS_0, PCI_REGION_MEM);
|
|
sata_info.iobase[1] = (ulong)dm_pci_map_bar(dev,
|
|
PCI_BASE_ADDRESS_2, PCI_REGION_MEM);
|
|
|
|
/* mask out the unused bits */
|
|
sata_info.iobase[0] &= 0xffffff80;
|
|
sata_info.iobase[1] &= 0xfffffc00;
|
|
|
|
/* Enable Bus Mastering and memory region */
|
|
dm_pci_write_config16(dev, PCI_COMMAND,
|
|
PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
|
|
|
|
/* Check if mem accesses and Bus Mastering are enabled. */
|
|
dm_pci_read_config16(dev, 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));
|
|
|
|
for (i = sata_info.portbase; i < sata_info.maxport; i++) {
|
|
snprintf(sata_name, sizeof(sata_name), "sil_sata%d", i);
|
|
ret = blk_create_devicef(dev, "sata_sil_blk", sata_name,
|
|
IF_TYPE_SATA, -1, 512, 0, &blk);
|
|
if (ret) {
|
|
debug("Can't create device\n");
|
|
return ret;
|
|
}
|
|
|
|
ret = sil_init_sata(blk, i);
|
|
if (ret) {
|
|
ret = sil_unbind_device(blk);
|
|
if (ret)
|
|
return ret;
|
|
|
|
failed_number++;
|
|
continue;
|
|
}
|
|
|
|
ret = scan_sata(blk, i);
|
|
if (ret) {
|
|
ret = sil_unbind_device(blk);
|
|
if (ret)
|
|
return ret;
|
|
|
|
failed_number++;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (failed_number == sata_info.maxport)
|
|
return -ENODEV;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
static int sil_pci_remove(struct udevice *dev)
|
|
{
|
|
int i;
|
|
struct sil_sata *sata;
|
|
struct sil_sata_priv *priv;
|
|
|
|
priv = dev_get_priv(dev);
|
|
|
|
for (i = sata_info.portbase; i < sata_info.maxport; i++) {
|
|
sata = priv->sil_sata_desc[i];
|
|
if (sata)
|
|
free(sata);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int sata_sil_scan(struct udevice *dev)
|
|
{
|
|
/* Nothing to do here */
|
|
|
|
return 0;
|
|
}
|
|
|
|
struct sil_ops sata_sil_ops = {
|
|
.scan = sata_sil_scan,
|
|
};
|
|
|
|
static const struct udevice_id sil_pci_ids[] = {
|
|
{ .compatible = "sil-pci-sample" },
|
|
{ }
|
|
};
|
|
|
|
U_BOOT_DRIVER(sil_ahci_pci) = {
|
|
.name = "sil_ahci_pci",
|
|
.id = UCLASS_AHCI,
|
|
.of_match = sil_pci_ids,
|
|
.ops = &sata_sil_ops,
|
|
.probe = sil_pci_probe,
|
|
.remove = sil_pci_remove,
|
|
.priv_auto = sizeof(struct sil_sata_priv),
|
|
};
|
|
|
|
U_BOOT_PCI_DEVICE(sil_ahci_pci, supported);
|
|
#endif
|