Add a function to set the tap delay for the clk phase of the sd host
controller.

Signed-off-by: Steffen Trumtrar <s.trumt...@pengutronix.de>
Reviewed-by: Ahmad Fatoum <a.fat...@pengutronix.de>
---
 arch/arm/mach-zynqmp/firmware-zynqmp.c | 42 ++++++++++++++++++++++++++++++++++
 include/mach/zynqmp/firmware-zynqmp.h  | 23 +++++++++++++++++++
 2 files changed, 65 insertions(+)

diff --git a/arch/arm/mach-zynqmp/firmware-zynqmp.c 
b/arch/arm/mach-zynqmp/firmware-zynqmp.c
index b383ed6f00..039a46e767 100644
--- a/arch/arm/mach-zynqmp/firmware-zynqmp.c
+++ b/arch/arm/mach-zynqmp/firmware-zynqmp.c
@@ -48,6 +48,7 @@ enum pm_ret_status {
 
 enum pm_api_id {
        PM_GET_API_VERSION = 1,
+       PM_MMIO_WRITE = 19,
        PM_FPGA_LOAD = 22,
        PM_FPGA_GET_STATUS,
        PM_IOCTL = 34,
@@ -511,6 +512,47 @@ static int zynqmp_pm_ioctl(u32 node_id, u32 ioctl_id, u32 
arg1, u32 arg2,
                                   arg1, arg2, out);
 }
 
+/**
+ * zynqmp_pm_set_sd_tapdelay() -  Set tap delay for the SD device
+ *
+ * @node_id:   Node ID of the device
+ * @type:      Type of tap delay to set (input/output)
+ * @value:     Value to set fot the tap delay
+ *
+ * This function sets input/output tap delay for the SD device.
+ *
+ * Return:     Returns status, either success or error+reason
+ */
+int zynqmp_pm_set_sd_tapdelay(u32 node_id, u32 type, u32 value)
+{
+       u32 reg = (type == PM_TAPDELAY_INPUT) ? SD_ITAPDLY : SD_OTAPDLYSEL;
+       u32 mask = (node_id == NODE_SD_0) ? GENMASK(15, 0) : GENMASK(31, 16);
+
+       if (value) {
+               return zynqmp_pm_invoke_fn(PM_IOCTL, node_id,
+                                          IOCTL_SET_SD_TAPDELAY,
+                                          type, value, NULL);
+       }
+
+       /*
+        * Work around completely misdesigned firmware API on Xilinx ZynqMP.
+        * The IOCTL_SET_SD_TAPDELAY firmware call allows the caller to only
+        * ever set IOU_SLCR SD_ITAPDLY Register SD0_ITAPDLYENA/SD1_ITAPDLYENA
+        * bits, but there is no matching call to clear those bits. If those
+        * bits are not cleared, SDMMC tuning may fail.
+        *
+        * Luckily, there are PM_MMIO_READ/PM_MMIO_WRITE calls which seem to
+        * allow complete unrestricted access to all address space, including
+        * IOU_SLCR SD_ITAPDLY Register and all the other registers, access
+        * to which was supposed to be protected by the current firmware API.
+        *
+        * Use PM_MMIO_READ/PM_MMIO_WRITE to re-implement the missing counter
+        * part of IOCTL_SET_SD_TAPDELAY which clears SDx_ITAPDLYENA bits.
+        */
+       return zynqmp_pm_invoke_fn(PM_MMIO_WRITE, reg, mask, 0, 0, NULL);
+}
+EXPORT_SYMBOL_GPL(zynqmp_pm_set_sd_tapdelay);
+
 /**
  * zynqmp_pm_sd_dll_reset() - Reset DLL logic
  *
diff --git a/include/mach/zynqmp/firmware-zynqmp.h 
b/include/mach/zynqmp/firmware-zynqmp.h
index 630677285f..00c63058f4 100644
--- a/include/mach/zynqmp/firmware-zynqmp.h
+++ b/include/mach/zynqmp/firmware-zynqmp.h
@@ -27,6 +27,10 @@
 
 #define ZYNQMP_PCAP_STATUS_FPGA_DONE   BIT(3)
 
+/* ZynqMP SD tap delay tuning */
+#define SD_ITAPDLY     0xFF180314
+#define SD_OTAPDLYSEL  0xFF180318
+
 enum pm_ioctl_id {
        IOCTL_GET_RPU_OPER_MODE = 0,
        IOCTL_SET_RPU_OPER_MODE = 1,
@@ -80,6 +84,22 @@ struct zynqmp_pm_query_data {
        u32 arg3;
 };
 
+enum pm_node_id {
+       NODE_SD_0 = 39,
+       NODE_SD_1 = 40,
+};
+
+enum tap_delay_type {
+       PM_TAPDELAY_INPUT = 0,
+       PM_TAPDELAY_OUTPUT = 1,
+};
+
+enum dll_reset_type {
+       PM_DLL_RESET_ASSERT = 0,
+       PM_DLL_RESET_RELEASE = 1,
+       PM_DLL_RESET_PULSE = 2,
+};
+
 struct zynqmp_eemi_ops {
        int (*get_api_version)(u32 *version);
        int (*query_data)(struct zynqmp_pm_query_data qdata, u32 *out);
@@ -99,6 +119,9 @@ struct zynqmp_eemi_ops {
 
 const struct zynqmp_eemi_ops *zynqmp_pm_get_eemi_ops(void);
 
+int zynqmp_pm_set_sd_tapdelay(u32 node_id, u32 type, u32 value);
+int zynqmp_pm_sd_dll_reset(u32 node_id, u32 type);
+
 int zynqmp_pm_write_ggs(u32 index, u32 value);
 int zynqmp_pm_read_ggs(u32 index, u32 *value);
 int zynqmp_pm_write_pggs(u32 index, u32 value);

-- 
2.40.1


Reply via email to