summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLeifu Zhao <leifu.zhao@intel.com>2019-03-07 13:14:20 +0800
committerchrome-bot <chrome-bot@chromium.org>2019-04-02 07:00:55 -0700
commitd7aab8081efcd6187ae1704baa989cf973119380 (patch)
treeb8d143cb3217594b8c67fe69fa9d03494bc2bb39
parent049db3d36f15a3e3037c9e9a5fcea59d0c762ef8 (diff)
downloadchrome-ec-d7aab8081efcd6187ae1704baa989cf973119380.tar.gz
ish: add memory dma driver for ISH5
Arcada/ISH5 power management needs the support for dma between UMA and SRAM to do SRAM swap in and swap out. Add the dma driver and API which utilizes dma engine to perform dma transfer between UMA and SRAM. BUG=b:127723182 BRANCH=none TEST=tested on arcada Signed-off-by: Leifu Zhao <leifu.zhao@intel.com> Change-Id: I22172b176eed92d7f487641f9b5c79dfd04f602a Reviewed-on: https://chromium-review.googlesource.com/1507326 Commit-Ready: Leifu Zhao <leifu.zhao@intel.corp-partner.google.com> Tested-by: Jett Rink <jettrink@chromium.org> Reviewed-by: Leifu Zhao <leifu.zhao@intel.corp-partner.google.com> Reviewed-by: Jett Rink <jettrink@chromium.org>
-rw-r--r--board/arcada_ish/board.h3
-rw-r--r--chip/ish/build.mk1
-rw-r--r--chip/ish/dma.c223
-rw-r--r--chip/ish/ish_dma.h86
-rw-r--r--chip/ish/registers.h63
-rw-r--r--include/config.h3
6 files changed, 379 insertions, 0 deletions
diff --git a/board/arcada_ish/board.h b/board/arcada_ish/board.h
index 6ab130bb68..cdaf26e36a 100644
--- a/board/arcada_ish/board.h
+++ b/board/arcada_ish/board.h
@@ -52,6 +52,9 @@
#define CONFIG_TABLET_MODE
+/* DMA paging between SRAM and DRAM */
+#define CONFIG_DMA_PAGING
+
/* Host command over HECI */
#define CONFIG_HOSTCMD_HECI
diff --git a/chip/ish/build.mk b/chip/ish/build.mk
index 2af293e258..31827497b4 100644
--- a/chip/ish/build.mk
+++ b/chip/ish/build.mk
@@ -25,6 +25,7 @@ chip-$(CONFIG_HOSTCMD_HECI)+=host_command_heci.o
chip-$(CONFIG_HOSTCMD_HECI)+=heci.o system_state_subsys.o ipc_heci.o
chip-$(CONFIG_HID_HECI)+=hid_subsys.o
chip-$(CONFIG_HID_HECI)+=heci.o system_state_subsys.o ipc_heci.o
+chip-$(CONFIG_DMA_PAGING)+=dma.o
# location of the scripts and keys used to pack the SPI flash image
SCRIPTDIR:=./chip/${CHIP}/util
diff --git a/chip/ish/dma.c b/chip/ish/dma.c
new file mode 100644
index 0000000000..db4a9a2fc6
--- /dev/null
+++ b/chip/ish/dma.c
@@ -0,0 +1,223 @@
+/* Copyright 2019 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* DMA module for ISH */
+
+#include "common.h"
+#include "console.h"
+#include "registers.h"
+#include "ish_dma.h"
+#include "util.h"
+
+static int dma_init_called; /* If ish_dma_init is called */
+
+static int dma_poll(uint32_t addr, uint32_t expected, uint32_t mask)
+{
+ int retval = -1;
+ uint32_t counter = 0;
+
+ /*
+ * The timeout is approximately 2.2 seconds according to
+ * value of UINT32_MAX, 120MHZ ISH clock frequency and
+ * instruction count which is around 4.
+ */
+ while (counter < (UINT32_MAX / 64)) {
+ /* test condition */
+ if ((REG32(addr) & mask) == expected) {
+ retval = DMA_RC_OK;
+ break;
+ }
+ counter++;
+ }
+
+ return retval;
+}
+
+void ish_dma_ocp_timeout_disable(void)
+{
+ uint32_t ctrl = OCP_AGENT_CONTROL;
+
+ OCP_AGENT_CONTROL = ctrl & OCP_RESPONSE_TO_DISABLE;
+}
+
+static inline uint32_t interrupt_lock(void)
+{
+ uint32_t eflags = 0;
+ __asm__ volatile("pushfl;" /* save eflag value */
+ "popl %0;"
+ "cli;"
+ : "=r"(eflags)); /* shut off interrupts */
+ return eflags;
+}
+
+static inline void interrupt_unlock(uint32_t eflags)
+{
+ __asm__ volatile("pushl %0;" /* restore elfag values */
+ "popfl;"
+ :
+ : "r"(eflags));
+}
+
+void dma_configure_psize(void)
+{
+ /* Give chan0 512 bytes for high performance, and chan1 128 bytes. */
+ DMA_PSIZE_01 = DMA_PSIZE_UPDATE |
+ (DMA_PSIZE_CHAN1_SIZE << DMA_PSIZE_CHAN1_OFFSET) |
+ (DMA_PSIZE_CHAN0_SIZE << DMA_PSIZE_CHAN0_OFFSET);
+}
+
+void ish_dma_init(void)
+{
+ uint32_t uma_msb;
+
+ ish_dma_ocp_timeout_disable();
+
+ /* configure DMA partition size */
+ dma_configure_psize();
+
+ /* set DRAM address 32 MSB for DMA transactions on UMA */
+ uma_msb = IPC_UMA_RANGE_LOWER_1;
+ ish_dma_set_msb(PAGING_CHAN, uma_msb, uma_msb);
+
+ dma_init_called = 1;
+}
+
+int ish_dma_copy(uint32_t chan, uint32_t dst, uint32_t src, uint32_t length,
+ enum dma_mode mode)
+{
+ uint32_t chan_reg = DMA_REG_BASE + (DMA_CH_REGS_SIZE * chan);
+ int rc = DMA_RC_OK;
+ uint32_t eflags;
+ uint32_t chunk;
+
+ __asm__ volatile("\twbinvd\n"); /* Flush cache before dma start */
+
+ /* Bringup VNN power rail for accessing SoC fabric */
+ PMU_VNN_REQ = (1 << VNN_ID_DMA(chan));
+ while (!(PMU_VNN_REQ_ACK & PMU_VNN_REQ_ACK_STATUS))
+ continue;
+
+ /*
+ * shut off interrupts to assure no simultanious
+ * access to DMA registers
+ */
+ eflags = interrupt_lock();
+
+ MISC_CHID_CFG_REG = chan; /* Set channel to configure */
+
+ mode |= NON_SNOOP;
+ MISC_DMA_CTL_REG(chan) = mode; /* Set transfer direction */
+
+ DMA_CFG_REG = DMA_EN_MASK; /* Enable DMA module */
+ DMA_LLP(chan_reg) = 0; /* Linked lists are not used */
+ DMA_CTL_LOW(chan_reg) =
+ 0 /* Set transfer parameters */ |
+ (DMA_CTL_TT_FC_M2M_DMAC << DMA_CTL_TT_FC_SHIFT) |
+ (DMA_CTL_ADDR_INC << DMA_CTL_SINC_SHIFT) |
+ (DMA_CTL_ADDR_INC << DMA_CTL_DINC_SHIFT) |
+ (SRC_TR_WIDTH << DMA_CTL_SRC_TR_WIDTH_SHIFT) |
+ (DEST_TR_WIDTH << DMA_CTL_DST_TR_WIDTH_SHIFT) |
+ (SRC_BURST_SIZE << DMA_CTL_SRC_MSIZE_SHIFT) |
+ (DEST_BURST_SIZE << DMA_CTL_DEST_MSIZE_SHIFT);
+
+ interrupt_unlock(eflags);
+ while (length) {
+ chunk = (length > DMA_MAX_BLOCK_SIZE) ? DMA_MAX_BLOCK_SIZE
+ : length;
+
+ if (rc != DMA_RC_OK)
+ break;
+
+ eflags = interrupt_lock();
+ MISC_CHID_CFG_REG = chan; /* Set channel to configure */
+ DMA_CTL_HIGH(chan_reg) =
+ chunk; /* Set number of bytes to transfer */
+ DMA_DAR(chan_reg) = dst; /* Destination address */
+ DMA_SAR(chan_reg) = src; /* Source address */
+ DMA_EN_REG = DMA_CH_EN_BIT(chan) |
+ DMA_CH_EN_WE_BIT(chan); /* Enable the channel */
+ interrupt_unlock(eflags);
+
+ rc = ish_wait_for_dma_done(
+ chan); /* Wait for trans completion */
+
+ dst += chunk;
+ src += chunk;
+ length -= chunk;
+ }
+
+ /* Mark the DMA VNN power rail as no longer needed */
+ PMU_VNN_REQ = (1 << VNN_ID_DMA(chan));
+ return rc;
+}
+
+void ish_dma_disable(void)
+{
+ uint32_t channel;
+ uint32_t counter;
+
+ /* Disable DMA on per-channel basis. */
+ for (channel = 0; channel <= DMA_MAX_CHANNEL; channel++) {
+ MISC_CHID_CFG_REG = channel;
+ if (DMA_EN_REG & DMA_CH_EN_BIT(channel)) {
+ /* Write 0 to channel enable bit ... */
+ DMA_EN_REG = DMA_CH_EN_WE_BIT(channel);
+
+ /* Wait till it shuts up. */
+ counter = 0;
+ while ((DMA_EN_REG & DMA_CH_EN_BIT(channel)) &&
+ counter < (UINT32_MAX / 64))
+ counter++;
+ }
+ }
+ DMA_CLR_ERR_REG = 0xFFFFFFFF;
+ DMA_CLR_BLOCK_REG = 0xFFFFFFFF;
+
+ DMA_CFG_REG = 0; /* Disable DMA module */
+}
+
+int ish_wait_for_dma_done(uint32_t ch)
+{
+ return dma_poll(DMA_EN_REG_ADDR, 0, DMA_CH_EN_BIT(ch));
+}
+
+static int ish_dma_page_internal(uint32_t dst, uint32_t src, enum dma_mode mode)
+{
+ int rc;
+ uint32_t eflags = interrupt_lock();
+
+ if (!dma_init_called)
+ ish_dma_init();
+
+ /* Wait for DMA to be free */
+ rc = dma_poll(DMA_EN_REG_ADDR, 0,
+ DMA_CH_EN_BIT(PAGING_CHAN) | DMA_CH_EN_BIT(KERNEL_CHAN));
+
+ if (rc == DMA_RC_OK)
+ rc = ish_dma_copy(PAGING_CHAN, dst, src, PAGE_SIZE, mode);
+
+ interrupt_unlock(eflags);
+ return rc;
+}
+
+/* DMA page between DRAM and SRAM. */
+int ish_dma_page(uint32_t dst, uint32_t src, int page_in)
+{
+ int ret = 0;
+
+ ret = ish_dma_page_internal(dst, src,
+ (page_in ? UMA_TO_SRAM : SRAM_TO_UMA));
+
+ return ret;
+}
+
+void ish_dma_set_msb(uint32_t chan, uint32_t dst_msb, uint32_t src_msb)
+{
+ uint32_t eflags = interrupt_lock();
+ MISC_CHID_CFG_REG = chan; /* Set channel to configure */
+ MISC_SRC_FILLIN_DMA(chan) = src_msb;
+ MISC_DST_FILLIN_DMA(chan) = dst_msb;
+ interrupt_unlock(eflags);
+}
diff --git a/chip/ish/ish_dma.h b/chip/ish/ish_dma.h
new file mode 100644
index 0000000000..9033c9b419
--- /dev/null
+++ b/chip/ish/ish_dma.h
@@ -0,0 +1,86 @@
+/* Copyright 2019 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef __CROS_EC_ISH_DMA_H
+#define __CROS_EC_ISH_DMA_H
+
+/* DMA return codes */
+#define DMA_RC_OK 0 /* Success */
+#define DMA_RC_TO 1 /* Time out */
+#define DMA_RC_HW 2 /* HW error (OCP) */
+
+/* DMA channels */
+#define PAGING_CHAN 0
+#define KERNEL_CHAN 1
+
+#define DST_IS_DRAM (1 << 0)
+#define SRC_IS_DRAM (1 << 1)
+#define NON_SNOOP (1 << 2)
+
+/* ISH5 and on */
+#define RS0 0x0
+#define RS3 0x3
+#define RS_SRC_OFFSET 3
+#define RS_DST_OFFSET 5
+
+#define PAGE_SIZE 4096
+
+/**
+ * SRAM: ISH local static ram
+ * UMA: Protected system DRAM region dedicated for ISH
+ * HOST_DRAM: OS owned buffer in system DRAM
+ */
+enum dma_mode {
+ SRAM_TO_SRAM = 0,
+ SRAM_TO_UMA = DST_IS_DRAM | (RS3 << RS_DST_OFFSET),
+ UMA_TO_SRAM = SRC_IS_DRAM | (RS3 << RS_SRC_OFFSET),
+ HOST_DRAM_TO_SRAM = SRC_IS_DRAM | (RS0 << RS_SRC_OFFSET),
+ SRAM_TO_HOST_DRAM = DST_IS_DRAM | (RS0 << RS_DST_OFFSET)
+};
+
+/* Disable DMA engine */
+void ish_dma_disable(void);
+/* Initialize DMA engine */
+void ish_dma_init(void);
+/**
+ * Main DMA transfer function
+ *
+ * @param chan DMA channel
+ * @param dst Destination address
+ * @param src Source address
+ * @param length Transfer size
+ * @param mode Transfer mode
+ * @return DMA_RC_OK, or non-zero if error.
+ */
+int ish_dma_copy(uint32_t chan, uint32_t dst, uint32_t src, uint32_t length,
+ enum dma_mode mode);
+/**
+ * Set upper 32 bits address for DRAM
+ *
+ * @param chan DMA channel
+ * @param dst_msb Destination DRAM upper 32 bits address
+ * @param src_msb Source DRAM upper 32 bits address
+ */
+void ish_dma_set_msb(uint32_t chan, uint32_t dst_msb, uint32_t src_msb);
+/**
+ * Page granularity transfer between SRAM and DRAM
+ *
+ * @param dst Destination address
+ * @param src Source address
+ * @param page_in Is from DRAM to SRAM
+ * @return DMA_RC_OK, or non-zero if error.
+ */
+int ish_dma_page(uint32_t dst, uint32_t src,
+ int page_in); /* API for page manager/d0i3 task */
+/**
+ * Wait for DMA transfer finish
+ *
+ * @param chan DMA channel
+ * @return DMA_RC_OK, or non-zero if error.
+ */
+int ish_wait_for_dma_done(uint32_t ch);
+/* Disable OCP (Open Core Protocol) fabric time out */
+void ish_dma_ocp_timeout_disable(void);
+#endif
diff --git a/chip/ish/registers.h b/chip/ish/registers.h
index 834dc63b72..0f9e9d9a99 100644
--- a/chip/ish/registers.h
+++ b/chip/ish/registers.h
@@ -38,6 +38,9 @@ enum ish_i2c_port {
#define ISH_UART_BASE 0x00103000
#define ISH_GPIO_BASE 0x001F0000
#define ISH_PMU_BASE 0x00800000
+#define ISH_OCP_BASE 0x00700000
+#define ISH_MISC_BASE 0x00C00000
+#define ISH_DMA_BASE 0x00400000
#define ISH_CCU_BASE 0x00900000
#define ISH_IPC_BASE 0x00B00000
#define ISH_WDT_BASE 0xFDE00000
@@ -122,6 +125,10 @@ enum ish_i2c_port {
#define IPC_HOST2ISH_MSG_REGS (ISH_IPC_BASE + 0xE0)
#define IPC_ISH2HOST_DOORBELL (ISH_IPC_BASE + 0x54)
#define IPC_BUSY_CLEAR (ISH_IPC_BASE + 0x378)
+#define IPC_UMA_RANGE_LOWER_0 REG32(ISH_IPC_BASE + 0x380)
+#define IPC_UMA_RANGE_LOWER_1 REG32(ISH_IPC_BASE + 0x384)
+#define IPC_UMA_RANGE_UPPER_0 REG32(ISH_IPC_BASE + 0x388)
+#define IPC_UMA_RANGE_UPPER_1 REG32(ISH_IPC_BASE + 0x38C)
/* PMU Registers */
#define PMU_VNN_REQ REG32(ISH_PMU_BASE + 0x3c)
@@ -135,6 +142,62 @@ enum ish_i2c_port {
#define PMU_RST_PREP_AVAIL BIT(1)
#define PMU_RST_PREP_INT_MASK BIT(31)
+#define VNN_ID_DMA0 4
+#define VNN_ID_DMA(chan) (VNN_ID_DMA0 + chan)
+
+/* OCP registers */
+#define OCP_IOSF2OCP_BRIDGE (ISH_OCP_BASE + 0x9400)
+#define OCP_AGENT_CONTROL REG32(OCP_IOSF2OCP_BRIDGE + 0x20)
+#define OCP_RESPONSE_TO_DISABLE 0xFFFFF8FF
+
+/* MISC registers */
+#define MISC_REG_BASE ISH_MISC_BASE
+#define MISC_CHID_CFG_REG REG32(MISC_REG_BASE + 0x40)
+#define MISC_DMA_CTL_REG(ch) REG32(MISC_REG_BASE + (4 * (ch)))
+#define MISC_SRC_FILLIN_DMA(ch) REG32(MISC_REG_BASE + 0x20 + (4 * (ch)))
+#define MISC_DST_FILLIN_DMA(ch) REG32(MISC_REG_BASE + 0x80 + (4 * (ch)))
+
+/* DMA registers */
+#define DMA_REG_BASE ISH_DMA_BASE
+#define DMA_CH_REGS_SIZE 0x58
+#define DMA_CLR_BLOCK_REG REG32(DMA_REG_BASE + 0x340)
+#define DMA_CLR_ERR_REG REG32(DMA_REG_BASE + 0x358)
+#define DMA_EN_REG_ADDR (DMA_REG_BASE + 0x3A0)
+#define DMA_EN_REG REG32(DMA_EN_REG_ADDR)
+#define DMA_CFG_REG REG32(DMA_REG_BASE + 0x398)
+#define DMA_PSIZE_01 REG32(DMA_REG_BASE + 0x400)
+#define DMA_PSIZE_CHAN0_SIZE 512
+#define DMA_PSIZE_CHAN0_OFFSET 0
+#define DMA_PSIZE_CHAN1_SIZE 128
+#define DMA_PSIZE_CHAN1_OFFSET 13
+#define DMA_PSIZE_UPDATE (1 << 26)
+#define DMA_MAX_CHANNEL 4
+#define DMA_SAR(chan) REG32(chan + 0x000)
+#define DMA_DAR(chan) REG32(chan + 0x008)
+#define DMA_LLP(chan) REG32(chan + 0x010)
+#define DMA_CTL_LOW(chan) REG32(chan + 0x018)
+#define DMA_CTL_HIGH(chan) REG32(chan + 0x018 + 0x4)
+#define DMA_CTL_INT_EN_BIT 0
+#define DMA_CTL_INT_EN_MASK (1 << DMA_CTL_INT_EN_BIT)
+#define DMA_CTL_DST_TR_WIDTH_SHIFT 1
+#define DMA_CTL_SRC_TR_WIDTH_SHIFT 4
+#define DMA_CTL_DINC_SHIFT 7
+#define DMA_CTL_SINC_SHIFT 9
+#define DMA_CTL_ADDR_INC 0
+#define DMA_CTL_DEST_MSIZE_SHIFT 11
+#define DMA_CTL_SRC_MSIZE_SHIFT 14
+#define DMA_CTL_TT_FC_SHIFT 20
+#define DMA_CTL_TT_FC_M2M_DMAC 0
+#define DMA_EN_BIT 0
+#define DMA_EN_MASK (1 << DMA_EN_BIT)
+#define DMA_CH_EN_BIT(n) (1 << (n))
+#define DMA_CH_EN_WE_BIT(n) (1 << (8 + (n)))
+#define DMA_MAX_BLOCK_SIZE (4096)
+#define SRC_TR_WIDTH 2
+#define SRC_BURST_SIZE 3
+#define DEST_TR_WIDTH 2
+#define DEST_BURST_SIZE 3
+
/* CCU Registers */
#define CCU_TCG_EN REG32(ISH_CCU_BASE + 0x0)
#define CCU_BCG_EN REG32(ISH_CCU_BASE + 0x4)
diff --git a/include/config.h b/include/config.h
index cdc291cdda..6fb463b7b1 100644
--- a/include/config.h
+++ b/include/config.h
@@ -2411,6 +2411,9 @@
*/
#undef CONFIG_LOW_POWER_S0
+/* DMA paging between SRAM and DRAM */
+#undef CONFIG_DMA_PAGING
+
/*
* Enable HID subsystem using HECI on Intel ISH (Integrated Sensor Hub)
*/