Add a function to set the tap delay for the clk phase of the sd host controller. Signed-off-by: Steffen Trumtrar <s.trumtrar@xxxxxxxxxxxxxx> --- 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