+// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) Freescale Semiconductor, Inc. 2006.
* Author: Jason Jin<Jason.jin@freescale.com>
* Zhang Wei<wei.zhang@freescale.com>
*
- * SPDX-License-Identifier: GPL-2.0+
- *
* with the reference on libata and ahci drvier in kernel
*
* This driver provides a SCSI interface to SATA.
*/
#include <common.h>
+#include <blk.h>
+#include <cpu_func.h>
+#include <log.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
#include <command.h>
#include <dm.h>
#define WAIT_MS_FLUSH 5000
#define WAIT_MS_LINKUP 200
+#define AHCI_CAP_S64A BIT(31)
+
__weak void __iomem *ahci_port_base(void __iomem *base, u32 port)
{
return base + 0x100 + (port * 0x80);
}
-
-static void ahci_setup_port(struct ahci_ioports *port, void __iomem *base,
- unsigned int port_idx)
-{
- base = ahci_port_base(base, port_idx);
-
- port->cmd_addr = base;
- port->scr_addr = base + PORT_SCR;
-}
-
-
#define msleep(a) udelay(a * 1000)
static void ahci_dcache_flush_range(unsigned long begin, unsigned long len)
return (i < timeout_msec) ? 0 : -1;
}
-int __weak ahci_link_up(struct ahci_uc_priv *uc_priv, u8 port)
+int __weak ahci_link_up(struct ahci_uc_priv *uc_priv, int port)
{
u32 tmp;
int j = 0;
static int ahci_host_init(struct ahci_uc_priv *uc_priv)
{
#if !defined(CONFIG_SCSI_AHCI_PLAT) && !defined(CONFIG_DM_SCSI)
-# ifdef CONFIG_DM_PCI
struct udevice *dev = uc_priv->dev;
- struct pci_child_platdata *pplat = dev_get_parent_platdata(dev);
-# else
- pci_dev_t pdev = uc_priv->dev;
- unsigned short vendor;
-# endif
+ struct pci_child_plat *pplat = dev_get_parent_plat(dev);
u16 tmp16;
#endif
void __iomem *mmio = uc_priv->mmio_base;
writel_with_flush(0xf, mmio + HOST_PORTS_IMPL);
#if !defined(CONFIG_SCSI_AHCI_PLAT) && !defined(CONFIG_DM_SCSI)
-# ifdef CONFIG_DM_PCI
if (pplat->vendor == PCI_VENDOR_ID_INTEL) {
u16 tmp16;
dm_pci_read_config16(dev, 0x92, &tmp16);
dm_pci_write_config16(dev, 0x92, tmp16 | 0xf);
}
-# else
- pci_read_config_word(pdev, PCI_VENDOR_ID, &vendor);
-
- if (vendor == PCI_VENDOR_ID_INTEL) {
- u16 tmp16;
- pci_read_config_word(pdev, 0x92, &tmp16);
- tmp16 |= 0xf;
- pci_write_config_word(pdev, 0x92, tmp16);
- }
-# endif
#endif
uc_priv->cap = readl(mmio + HOST_CAP);
uc_priv->port_map = readl(mmio + HOST_PORTS_IMPL);
debug("cap 0x%x port_map 0x%x n_ports %d\n",
uc_priv->cap, uc_priv->port_map, uc_priv->n_ports);
+#if !defined(CONFIG_DM_SCSI)
if (uc_priv->n_ports > CONFIG_SYS_SCSI_MAX_SCSI_ID)
uc_priv->n_ports = CONFIG_SYS_SCSI_MAX_SCSI_ID;
+#endif
for (i = 0; i < uc_priv->n_ports; i++) {
if (!(port_map & (1 << i)))
continue;
uc_priv->port[i].port_mmio = ahci_port_base(mmio, i);
port_mmio = (u8 *)uc_priv->port[i].port_mmio;
- ahci_setup_port(&uc_priv->port[i], mmio, i);
/* make sure port is not active */
tmp = readl(port_mmio + PORT_CMD);
debug("HOST_CTL 0x%x\n", tmp);
#if !defined(CONFIG_DM_SCSI)
#ifndef CONFIG_SCSI_AHCI_PLAT
-# ifdef CONFIG_DM_PCI
dm_pci_read_config16(dev, PCI_COMMAND, &tmp16);
tmp |= PCI_COMMAND_MASTER;
dm_pci_write_config16(dev, PCI_COMMAND, tmp16);
-# else
- pci_read_config_word(pdev, PCI_COMMAND, &tmp16);
- tmp |= PCI_COMMAND_MASTER;
- pci_write_config_word(pdev, PCI_COMMAND, tmp16);
-# endif
#endif
#endif
return 0;
static void ahci_print_info(struct ahci_uc_priv *uc_priv)
{
#if !defined(CONFIG_SCSI_AHCI_PLAT) && !defined(CONFIG_DM_SCSI)
-# if defined(CONFIG_DM_PCI)
struct udevice *dev = uc_priv->dev;
-# else
- pci_dev_t pdev = uc_priv->dev;
-# endif
u16 cc;
#endif
void __iomem *mmio = uc_priv->mmio_base;
#if defined(CONFIG_SCSI_AHCI_PLAT) || defined(CONFIG_DM_SCSI)
scc_s = "SATA";
#else
-# ifdef CONFIG_DM_PCI
dm_pci_read_config16(dev, 0x0a, &cc);
-# else
- pci_read_config_word(pdev, 0x0a, &cc);
-# endif
if (cc == 0x0101)
scc_s = "IDE";
else if (cc == 0x0106)
cap2 & (1 << 0) ? "boh " : "");
}
-#ifndef CONFIG_SCSI_AHCI_PLAT
-# if defined(CONFIG_DM_PCI) || defined(CONFIG_DM_SCSI)
+#if defined(CONFIG_DM_SCSI) || !defined(CONFIG_SCSI_AHCI_PLAT)
static int ahci_init_one(struct ahci_uc_priv *uc_priv, struct udevice *dev)
-# else
-static int ahci_init_one(struct ahci_uc_priv *uc_priv, pci_dev_t dev)
-# endif
{
#if !defined(CONFIG_DM_SCSI)
u16 vendor;
uc_priv->udma_mask = 0x7f; /*Fixme,assume to support UDMA6 */
#if !defined(CONFIG_DM_SCSI)
-#ifdef CONFIG_DM_PCI
- uc_priv->mmio_base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_5,
- PCI_REGION_MEM);
+ uc_priv->mmio_base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_5, 0, 0,
+ PCI_REGION_TYPE, PCI_REGION_MEM);
/* Take from kernel:
* JMicron-specific fixup:
if (vendor == 0x197b)
dm_pci_write_config8(dev, 0x41, 0xa1);
#else
- uc_priv->mmio_base = pci_map_bar(dev, PCI_BASE_ADDRESS_5,
- PCI_REGION_MEM);
-
- /* Take from kernel:
- * JMicron-specific fixup:
- * make sure we're in AHCI mode
- */
- pci_read_config_word(dev, PCI_VENDOR_ID, &vendor);
- if (vendor == 0x197b)
- pci_write_config_byte(dev, 0x41, 0xa1);
-#endif
-#else
- struct scsi_platdata *plat = dev_get_uclass_platdata(dev);
+ struct scsi_plat *plat = dev_get_uclass_plat(dev);
uc_priv->mmio_base = (void *)plat->base;
#endif
{
struct ahci_ioports *pp = &(uc_priv->port[port]);
struct ahci_sg *ahci_sg = pp->cmd_tbl_sg;
+ phys_addr_t pa = virt_to_phys(buf);
u32 sg_count;
int i;
}
for (i = 0; i < sg_count; i++) {
- ahci_sg->addr =
- cpu_to_le32((unsigned long) buf + i * MAX_DATA_BYTE_COUNT);
- ahci_sg->addr_hi = 0;
+ ahci_sg->addr = cpu_to_le32(lower_32_bits(pa));
+ ahci_sg->addr_hi = cpu_to_le32(upper_32_bits(pa));
+ if (ahci_sg->addr_hi && !(uc_priv->cap & AHCI_CAP_S64A)) {
+ printf("Error: DMA address too high\n");
+ return -1;
+ }
ahci_sg->flags_size = cpu_to_le32(0x3fffff &
- (buf_len < MAX_DATA_BYTE_COUNT
- ? (buf_len - 1)
- : (MAX_DATA_BYTE_COUNT - 1)));
+ (buf_len < MAX_DATA_BYTE_COUNT ?
+ (buf_len - 1) :
+ (MAX_DATA_BYTE_COUNT - 1)));
ahci_sg++;
buf_len -= MAX_DATA_BYTE_COUNT;
+ pa += MAX_DATA_BYTE_COUNT;
}
return sg_count;
}
-
static void ahci_fill_cmd_slot(struct ahci_ioports *pp, u32 opts)
{
+ phys_addr_t pa = virt_to_phys((void *)pp->cmd_tbl);
+
pp->cmd_slot->opts = cpu_to_le32(opts);
pp->cmd_slot->status = 0;
- pp->cmd_slot->tbl_addr = cpu_to_le32((u32)pp->cmd_tbl & 0xffffffff);
+ pp->cmd_slot->tbl_addr = cpu_to_le32(lower_32_bits(pa));
#ifdef CONFIG_PHYS_64BIT
- pp->cmd_slot->tbl_addr_hi =
- cpu_to_le32((u32)(((pp->cmd_tbl) >> 16) >> 16));
+ pp->cmd_slot->tbl_addr_hi = cpu_to_le32(upper_32_bits(pa));
#endif
}
{
struct ahci_ioports *pp = &(uc_priv->port[port]);
void __iomem *port_mmio = pp->port_mmio;
+ u64 dma_addr;
u32 port_status;
void __iomem *mem;
return -1;
}
- mem = malloc(AHCI_PORT_PRIV_DMA_SZ + 2048);
+ mem = memalign(2048, AHCI_PORT_PRIV_DMA_SZ);
if (!mem) {
free(pp);
printf("%s: No mem for table!\n", __func__);
return -ENOMEM;
}
-
- /* Aligned to 2048-bytes */
- mem = memalign(2048, AHCI_PORT_PRIV_DMA_SZ);
memset(mem, 0, AHCI_PORT_PRIV_DMA_SZ);
/*
pp->cmd_tbl_sg =
(struct ahci_sg *)(uintptr_t)virt_to_phys((void *)mem);
- writel_with_flush((unsigned long)pp->cmd_slot,
- port_mmio + PORT_LST_ADDR);
-
- writel_with_flush(pp->rx_fis, port_mmio + PORT_FIS_ADDR);
+ dma_addr = (ulong)pp->cmd_slot;
+ writel_with_flush(dma_addr, port_mmio + PORT_LST_ADDR);
+ writel_with_flush(dma_addr >> 32, port_mmio + PORT_LST_ADDR_HI);
+ dma_addr = (ulong)pp->rx_fis;
+ writel_with_flush(dma_addr, port_mmio + PORT_FIS_ADDR);
+ writel_with_flush(dma_addr >> 32, port_mmio + PORT_FIS_ADDR_HI);
#ifdef CONFIG_SUNXI_AHCI
sunxi_dma_init(port_mmio);
ahci_dcache_invalidate_range((unsigned long)buf,
(unsigned long)buf_len);
- debug("%s: %d byte transferred.\n", __func__, pp->cmd_slot->status);
+ debug("%s: %d byte transferred.\n", __func__,
+ le32_to_cpu(pp->cmd_slot->status));
return 0;
}
-
static char *ata_id_strcpy(u16 *target, u16 *src, int len)
{
int i;
{
struct ahci_uc_priv *uc_priv;
#ifdef CONFIG_DM_SCSI
- uc_priv = dev_get_uclass_priv(dev);
+ uc_priv = dev_get_uclass_priv(dev->parent);
#else
uc_priv = probe_ent;
#endif
linkmap = uc_priv->link_port_map;
- for (i = 0; i < CONFIG_SYS_SCSI_MAX_SCSI_ID; i++) {
+ for (i = 0; i < uc_priv->n_ports; i++) {
if (((linkmap >> i) & 0x01)) {
if (ahci_port_start(uc_priv, (u8) i)) {
printf("Can not start port %d\n", i);
return;
}
uc_priv = probe_ent;
-# if defined(CONFIG_DM_PCI)
struct udevice *dev;
int ret;
if (ret)
return;
ahci_init_one(uc_priv, dev);
-# else
- ahci_init_one(uc_priv, busdevfunc);
-# endif
#else
uc_priv = probe_ent;
#endif
#endif
#ifndef CONFIG_SCSI_AHCI_PLAT
-# if defined(CONFIG_DM_PCI) || defined(CONFIG_DM_SCSI)
-int achi_init_one_dm(struct udevice *dev)
+int ahci_init_one_dm(struct udevice *dev)
{
struct ahci_uc_priv *uc_priv = dev_get_uclass_priv(dev);
return ahci_init_one(uc_priv, dev);
}
#endif
-#endif
-int achi_start_ports_dm(struct udevice *dev)
+int ahci_start_ports_dm(struct udevice *dev)
{
struct ahci_uc_priv *uc_priv = dev_get_uclass_priv(dev);
return 0;
}
-int ahci_probe_scsi(struct udevice *ahci_dev)
+int ahci_probe_scsi(struct udevice *ahci_dev, ulong base)
{
-#ifdef CONFIG_SCSI_AHCI_PLAT
- return -ENOSYS; /* TODO(sjg@chromium.org): Support non-PCI AHCI */
-#else
struct ahci_uc_priv *uc_priv;
- struct scsi_platdata *uc_plat;
+ struct scsi_plat *uc_plat;
struct udevice *dev;
int ret;
device_find_first_child(ahci_dev, &dev);
if (!dev)
return -ENODEV;
- uc_plat = dev_get_uclass_platdata(dev);
- uc_plat->base = (ulong)dm_pci_map_bar(ahci_dev, PCI_BASE_ADDRESS_5,
- PCI_REGION_MEM);
+ uc_plat = dev_get_uclass_plat(dev);
+ uc_plat->base = base;
uc_plat->max_lun = 1;
uc_plat->max_id = 2;
- uc_priv = dev_get_uclass_priv(dev);
+
+ uc_priv = dev_get_uclass_priv(ahci_dev);
ret = ahci_init_one(uc_priv, dev);
if (ret)
return ret;
ret = ahci_start_ports(uc_priv);
if (ret)
return ret;
-#endif
+
+ /*
+ * scsi_scan_dev() scans devices up-to the number of max_id.
+ * Update max_id if the number of detected ports exceeds max_id.
+ * This allows SCSI to scan all detected ports.
+ */
+ uc_plat->max_id = max_t(unsigned long, uc_priv->n_ports,
+ uc_plat->max_id);
+ /* If port count is less than max_id, update max_id */
+ if (uc_priv->n_ports < uc_plat->max_id)
+ uc_plat->max_id = uc_priv->n_ports;
return 0;
}
+int ahci_probe_scsi_pci(struct udevice *ahci_dev)
+{
+ ulong base;
+ u16 vendor, device;
+
+ base = (ulong)dm_pci_map_bar(ahci_dev, PCI_BASE_ADDRESS_5, 0, 0,
+ PCI_REGION_TYPE, PCI_REGION_MEM);
+
+ /*
+ * Note:
+ * Right now, we have only one quirk here, which is not enough to
+ * introduce a new Kconfig option to select this. Once we have more
+ * quirks in this AHCI code, we should add a Kconfig option for
+ * this though.
+ */
+ dm_pci_read_config16(ahci_dev, PCI_VENDOR_ID, &vendor);
+ dm_pci_read_config16(ahci_dev, PCI_DEVICE_ID, &device);
+
+ if (vendor == PCI_VENDOR_ID_CAVIUM &&
+ device == PCI_DEVICE_ID_CAVIUM_SATA)
+ base = (uintptr_t)dm_pci_map_bar(ahci_dev, PCI_BASE_ADDRESS_0,
+ 0, 0, PCI_REGION_TYPE,
+ PCI_REGION_MEM);
+ return ahci_probe_scsi(ahci_dev, base);
+}
+
struct scsi_ops scsi_ops = {
.exec = ahci_scsi_exec,
.bus_reset = ahci_scsi_bus_reset,