mmc: dw_mmc: Add support for 64-bit IDMAC

Some DW MMC blocks (e.g. those on modern Exynos chips) support 64-bit
DMA addressing mode. 64-bit DW MMC variants differ from their 32-bit
counterparts:
  - the register layout is a bit different (because there are additional
    IDMAC registers present for storing upper part of 64-bit addresses)
  - DMA descriptor structure is bigger and different from 32-bit one

Introduce all necessary changes to enable support for 64-bit DMA capable
DW MMC blocks. Next changes were made:

  1. Check which DMA address mode is supported in current IP-core
     version. HCON register (bit 27) indicates whether it's 32-bit or
     64-bit addressing. Add boolean .dma_64bit_address field to struct
     dwmci_host and store the result there. dwmci_init_dma() function is
     introduced for doing so, which is called on driver's init.

  2. Add 64-bit DMA descriptor (struct dwmci_idmac64) and use it in
     dwmci_prepare_desc() in case if .dma_64bit_address field is true.
     A new dwmci_set_idma_desc64() function was added for populating that
     descriptor.

  3. Add registers for 64-bit DMA capable blocks. To make the access to
     IDMAC registers universal between 32-bit / 64-bit cases, a new
     struct dwmci_idmac_regs (and corresponding host->regs field) was
     introduced, which abstracts the hardware by being set to
     appropriate offset constants on init. All direct calls to IDMAC
     registers were correspondingly replaced by accessing host->regs.

  4. Allocate and use 64-bit DMA descriptors buffer in case when IDMAC
     is 64-bit capable. Extract all the code (except for the IDMAC
     descriptors buffer allocation) from dwmci_send_cmd() to
     dwmci_send_cmd_common(), so that it's possible to keep IDMAC
     buffer (either 32-bit or 64-bit) on stack during send_cmd routine.

The insights for this implementation were taken from Linux kernel DW MMC
driver.

Signed-off-by: Sam Protsenko <semen.protsenko@linaro.org>
Signed-off-by: Minkyu Kang <mk7.kang@samsung.com>
diff --git a/include/dwmmc.h b/include/dwmmc.h
index 7e4acf0..de18fda 100644
--- a/include/dwmmc.h
+++ b/include/dwmmc.h
@@ -44,12 +44,22 @@
 #define DWMCI_UHS_REG		0x074
 #define DWMCI_BMOD		0x080
 #define DWMCI_PLDMND		0x084
+#define DWMCI_DATA		0x200
+/* Registers to support IDMAC 32-bit address mode */
 #define DWMCI_DBADDR		0x088
 #define DWMCI_IDSTS		0x08C
 #define DWMCI_IDINTEN		0x090
 #define DWMCI_DSCADDR		0x094
 #define DWMCI_BUFADDR		0x098
-#define DWMCI_DATA		0x200
+/* Registers to support IDMAC 64-bit address mode */
+#define DWMCI_DBADDRL		0x088
+#define DWMCI_DBADDRU		0x08c
+#define DWMCI_IDSTS64		0x090
+#define DWMCI_IDINTEN64		0x094
+#define DWMCI_DSCADDRL		0x098
+#define DWMCI_DSCADDRU		0x09c
+#define DWMCI_BUFADDRL		0x0a0
+#define DWMCI_BUFADDRU		0x0a4
 
 /* Interrupt Mask register */
 #define DWMCI_INTMSK_ALL	0xffffffff
@@ -143,6 +153,29 @@
 #define DWMCI_QUIRK_DISABLE_SMU		(1 << 0)
 
 /**
+ * struct dwmci_idmac_regs - Offsets of IDMAC registers
+ *
+ * @dbaddrl:	Descriptor base address, lower 32 bits
+ * @dbaddru:	Descriptor base address, upper 32 bits
+ * @idsts:	Internal DMA status
+ * @idinten:	Internal DMA interrupt enable
+ * @dscaddrl:	IDMAC descriptor address, lower 32 bits
+ * @dscaddru:	IDMAC descriptor address, upper 32 bits
+ * @bufaddrl:	Current data buffer address, lower 32 bits
+ * @bufaddru:	Current data buffer address, upper 32 bits
+ */
+struct dwmci_idmac_regs {
+	u32 dbaddrl;
+	u32 dbaddru;
+	u32 idsts;
+	u32 idinten;
+	u32 dscaddrl;
+	u32 dscaddru;
+	u32 bufaddrl;
+	u32 bufaddru;
+};
+
+/**
  * struct dwmci_host - Information about a designware MMC host
  *
  * @name:	Device name
@@ -157,6 +190,8 @@
  * @fifoth_val:	Value for FIFOTH register (or 0 to leave unset)
  * @mmc:	Pointer to generic MMC structure for this device
  * @priv:	Private pointer for use by controller
+ * @dma_64bit_address: Whether DMA supports 64-bit address mode or not
+ * @regs:	Registers that can vary for different DW MMC block versions
  */
 struct dwmci_host {
 	const char *name;
@@ -196,6 +231,8 @@
 
 	/* use fifo mode to read and write data */
 	bool fifo_mode;
+	bool dma_64bit_address;
+	const struct dwmci_idmac_regs *regs;
 };
 
 static inline void dwmci_writel(struct dwmci_host *host, int reg, u32 val)