ahci: flush / invalidate dcache around SATA commands
Exynos5 automatically performs DMA when the SATA controller executes commands. This adds the necessary dcache-to-memory flush & invalidation calls to allow the DMA to properly function. Signed-off-by: Taylor Hutt <thutt@chromium.org> Signed-off-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
parent
64738e8ae8
commit
90b276f6a2
|
@ -70,6 +70,39 @@ static void ahci_setup_port(struct ahci_ioports *port, unsigned long base,
|
||||||
|
|
||||||
#define msleep(a) udelay(a * 1000)
|
#define msleep(a) udelay(a * 1000)
|
||||||
|
|
||||||
|
static void ahci_dcache_flush_range(unsigned begin, unsigned len)
|
||||||
|
{
|
||||||
|
const unsigned long start = begin;
|
||||||
|
const unsigned long end = start + len;
|
||||||
|
|
||||||
|
debug("%s: flush dcache: [%#lx, %#lx)\n", __func__, start, end);
|
||||||
|
flush_dcache_range(start, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SATA controller DMAs to physical RAM. Ensure data from the
|
||||||
|
* controller is invalidated from dcache; next access comes from
|
||||||
|
* physical RAM.
|
||||||
|
*/
|
||||||
|
static void ahci_dcache_invalidate_range(unsigned begin, unsigned len)
|
||||||
|
{
|
||||||
|
const unsigned long start = begin;
|
||||||
|
const unsigned long end = start + len;
|
||||||
|
|
||||||
|
debug("%s: invalidate dcache: [%#lx, %#lx)\n", __func__, start, end);
|
||||||
|
invalidate_dcache_range(start, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ensure data for SATA controller is flushed out of dcache and
|
||||||
|
* written to physical memory.
|
||||||
|
*/
|
||||||
|
static void ahci_dcache_flush_sata_cmd(struct ahci_ioports *pp)
|
||||||
|
{
|
||||||
|
ahci_dcache_flush_range((unsigned long)pp->cmd_slot,
|
||||||
|
AHCI_PORT_PRIV_DMA_SZ);
|
||||||
|
}
|
||||||
|
|
||||||
static int waiting_for_cmd_completed(volatile u8 *offset,
|
static int waiting_for_cmd_completed(volatile u8 *offset,
|
||||||
int timeout_msec,
|
int timeout_msec,
|
||||||
u32 sign)
|
u32 sign)
|
||||||
|
@ -392,6 +425,7 @@ static void ahci_set_feature(u8 port)
|
||||||
|
|
||||||
memcpy((unsigned char *)pp->cmd_tbl, fis, sizeof(fis));
|
memcpy((unsigned char *)pp->cmd_tbl, fis, sizeof(fis));
|
||||||
ahci_fill_cmd_slot(pp, cmd_fis_len);
|
ahci_fill_cmd_slot(pp, cmd_fis_len);
|
||||||
|
ahci_dcache_flush_sata_cmd(pp);
|
||||||
writel(1, port_mmio + PORT_CMD_ISSUE);
|
writel(1, port_mmio + PORT_CMD_ISSUE);
|
||||||
readl(port_mmio + PORT_CMD_ISSUE);
|
readl(port_mmio + PORT_CMD_ISSUE);
|
||||||
|
|
||||||
|
@ -496,12 +530,17 @@ static int ahci_device_data_io(u8 port, u8 *fis, int fis_len, u8 *buf,
|
||||||
opts = (fis_len >> 2) | (sg_count << 16) | (is_write << 6);
|
opts = (fis_len >> 2) | (sg_count << 16) | (is_write << 6);
|
||||||
ahci_fill_cmd_slot(pp, opts);
|
ahci_fill_cmd_slot(pp, opts);
|
||||||
|
|
||||||
|
ahci_dcache_flush_sata_cmd(pp);
|
||||||
|
ahci_dcache_flush_range((unsigned)buf, (unsigned)buf_len);
|
||||||
|
|
||||||
writel_with_flush(1, port_mmio + PORT_CMD_ISSUE);
|
writel_with_flush(1, port_mmio + PORT_CMD_ISSUE);
|
||||||
|
|
||||||
if (waiting_for_cmd_completed(port_mmio + PORT_CMD_ISSUE, 150, 0x1)) {
|
if (waiting_for_cmd_completed(port_mmio + PORT_CMD_ISSUE, 150, 0x1)) {
|
||||||
printf("timeout exit!\n");
|
printf("timeout exit!\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ahci_dcache_invalidate_range((unsigned)buf, (unsigned)buf_len);
|
||||||
debug("%s: %d byte transferred.\n", __func__, pp->cmd_slot->status);
|
debug("%s: %d byte transferred.\n", __func__, pp->cmd_slot->status);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
Loading…
Reference in New Issue