From bfed44a1712a2272771a3022e6dd653c13b51ec2 Mon Sep 17 00:00:00 2001 From: Jolly Shah Date: Wed, 2 Jan 2019 12:27:00 -0800 Subject: [PATCH 01/20] zynqmp: pm: Implement PLL set parameter EEMI API This API will be used to set a parameter for the PLL. The parameter value that is set will have effect once the PLL mode is set to integer or fractional mode. Parameter values represent the values as defined in the Zynq MPSoC register reference manual ug1087. Signed-off-by: Mirela Simonovic Acked-by: Will Wong Signed-off-by: Jolly Shah --- plat/xilinx/zynqmp/pm_service/pm_api_sys.c | 31 +++++++++++++++++++++ plat/xilinx/zynqmp/pm_service/pm_api_sys.h | 4 +++ plat/xilinx/zynqmp/pm_service/pm_defs.h | 29 +++++++++++++++++++ plat/xilinx/zynqmp/pm_service/pm_svc_main.c | 4 +++ 4 files changed, 68 insertions(+) diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_sys.c b/plat/xilinx/zynqmp/pm_service/pm_api_sys.c index e85b2cee3..9bc11f26b 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_api_sys.c +++ b/plat/xilinx/zynqmp/pm_service/pm_api_sys.c @@ -1237,3 +1237,34 @@ enum pm_ret_status pm_fpga_read(uint32_t reg_numframes, address_high, readback_type); return pm_ipi_send_sync(primary_proc, payload, value, 1); } + +/* + * pm_pll_set_parameter() - Set the PLL parameter value + * @nid Node id of the target PLL + * @param_id ID of the PLL parameter + * @value Parameter value to be set + * + * Setting the parameter will have physical effect once the PLL mode is set to + * integer or fractional. + * + * @return Error if an argument is not valid or status as returned by the + * PM controller (PMU) + */ +enum pm_ret_status pm_pll_set_parameter(enum pm_node_id nid, + enum pm_pll_param param_id, + unsigned int value) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Check if given node ID is a PLL node */ + if (nid < NODE_APLL || nid > NODE_IOPLL) + return PM_RET_ERROR_ARGS; + + /* Check if parameter ID is valid and return an error if it's not */ + if (param_id >= PM_PLL_PARAM_MAX) + return PM_RET_ERROR_ARGS; + + /* Send request to the PMU */ + PM_PACK_PAYLOAD4(payload, PM_PLL_SET_PARAMETER, nid, param_id, value); + return pm_ipi_send_sync(primary_proc, payload, NULL, 0); +} diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_sys.h b/plat/xilinx/zynqmp/pm_service/pm_api_sys.h index fee91cdd9..acef4906c 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_api_sys.h +++ b/plat/xilinx/zynqmp/pm_service/pm_api_sys.h @@ -176,4 +176,8 @@ enum pm_ret_status pm_aes_engine(uint32_t address_high, uint32_t address_low, uint32_t *value); +enum pm_ret_status pm_pll_set_parameter(enum pm_node_id nid, + enum pm_pll_param param_id, + unsigned int value); + #endif /* PM_API_SYS_H */ diff --git a/plat/xilinx/zynqmp/pm_service/pm_defs.h b/plat/xilinx/zynqmp/pm_service/pm_defs.h index 7a0d97850..fe965c539 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_defs.h +++ b/plat/xilinx/zynqmp/pm_service/pm_defs.h @@ -92,6 +92,8 @@ enum pm_api_id { /* FPGA PL Readback */ PM_FPGA_READ, PM_SECURE_AES, + /* PLL control API functions */ + PM_PLL_SET_PARAMETER, PM_API_MAX }; @@ -265,4 +267,31 @@ enum pm_shutdown_subtype { PMF_SHUTDOWN_SUBTYPE_SYSTEM, }; +/** + * @PM_PLL_PARAM_DIV2: Enable for divide by 2 function inside the PLL + * @PM_PLL_PARAM_FBDIV: Feedback divisor integer portion for the PLL + * @PM_PLL_PARAM_DATA: Feedback divisor fractional portion for the PLL + * @PM_PLL_PARAM_PRE_SRC: Clock source for PLL input + * @PM_PLL_PARAM_POST_SRC: Clock source for PLL Bypass mode + * @PM_PLL_PARAM_LOCK_DLY: Lock circuit config settings for lock windowsize + * @PM_PLL_PARAM_LOCK_CNT: Lock circuit counter setting + * @PM_PLL_PARAM_LFHF: PLL loop filter high frequency capacitor control + * @PM_PLL_PARAM_CP: PLL charge pump control + * @PM_PLL_PARAM_RES: PLL loop filter resistor control + */ +enum pm_pll_param { + PM_PLL_PARAM_DIV2, + PM_PLL_PARAM_FBDIV, + PM_PLL_PARAM_DATA, + PM_PLL_PARAM_PRE_SRC, + PM_PLL_PARAM_POST_SRC, + PM_PLL_PARAM_LOCK_DLY, + PM_PLL_PARAM_LOCK_CNT, + PM_PLL_PARAM_LFHF, + PM_PLL_PARAM_CP, + PM_PLL_PARAM_RES, + PM_PLL_PARAM_MAX, +}; + + #endif /* PM_DEFS_H */ diff --git a/plat/xilinx/zynqmp/pm_service/pm_svc_main.c b/plat/xilinx/zynqmp/pm_service/pm_svc_main.c index 7790c979d..743011003 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_svc_main.c +++ b/plat/xilinx/zynqmp/pm_service/pm_svc_main.c @@ -563,6 +563,10 @@ uint64_t pm_smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, uint64_t x3, SMC_RET1(handle, (uint64_t)ret | ((uint64_t)value) << 32); } + case PM_PLL_SET_PARAMETER: + ret = pm_pll_set_parameter(pm_arg[0], pm_arg[1], pm_arg[2]); + SMC_RET1(handle, (uint64_t)ret); + default: WARN("Unimplemented PM Service Call: 0x%x\n", smc_fid); SMC_RET1(handle, SMC_UNK); From d833f64c90615b00e4fa1fd401bf521edb8b9909 Mon Sep 17 00:00:00 2001 From: Jolly Shah Date: Fri, 4 Jan 2019 11:28:38 -0800 Subject: [PATCH 02/20] zynqmp: pm: Implement PLL get parameter EEMI API This API will be used to get a parameter for the PLL. Parameter values represent the values as defined in the Zynq MPSoC register reference manual ug1087. Signed-off-by: Mirela Simonovic Acked-by: Will Wong Signed-off-by: Jolly Shah --- plat/xilinx/zynqmp/pm_service/pm_api_sys.c | 28 +++++++++++++++++++++ plat/xilinx/zynqmp/pm_service/pm_api_sys.h | 4 +++ plat/xilinx/zynqmp/pm_service/pm_defs.h | 1 + plat/xilinx/zynqmp/pm_service/pm_svc_main.c | 8 ++++++ 4 files changed, 41 insertions(+) diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_sys.c b/plat/xilinx/zynqmp/pm_service/pm_api_sys.c index 9bc11f26b..da1669114 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_api_sys.c +++ b/plat/xilinx/zynqmp/pm_service/pm_api_sys.c @@ -1268,3 +1268,31 @@ enum pm_ret_status pm_pll_set_parameter(enum pm_node_id nid, PM_PACK_PAYLOAD4(payload, PM_PLL_SET_PARAMETER, nid, param_id, value); return pm_ipi_send_sync(primary_proc, payload, NULL, 0); } + +/** + * pm_pll_get_parameter() - Get the PLL parameter value + * @nid Node id of the target PLL + * @param_id ID of the PLL parameter + * @value Location to store the parameter value + * + * @return Error if an argument is not valid or status as returned by the + * PM controller (PMU) + */ +enum pm_ret_status pm_pll_get_parameter(enum pm_node_id nid, + enum pm_pll_param param_id, + unsigned int *value) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Check if given node ID is a PLL node */ + if (nid < NODE_APLL || nid > NODE_IOPLL) + return PM_RET_ERROR_ARGS; + + /* Check if parameter ID is valid and return an error if it's not */ + if (param_id >= PM_PLL_PARAM_MAX) + return PM_RET_ERROR_ARGS; + + /* Send request to the PMU */ + PM_PACK_PAYLOAD3(payload, PM_PLL_GET_PARAMETER, nid, param_id); + return pm_ipi_send_sync(primary_proc, payload, value, 1); +} diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_sys.h b/plat/xilinx/zynqmp/pm_service/pm_api_sys.h index acef4906c..8ef210ac0 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_api_sys.h +++ b/plat/xilinx/zynqmp/pm_service/pm_api_sys.h @@ -180,4 +180,8 @@ enum pm_ret_status pm_pll_set_parameter(enum pm_node_id nid, enum pm_pll_param param_id, unsigned int value); +enum pm_ret_status pm_pll_get_parameter(enum pm_node_id nid, + enum pm_pll_param param_id, + unsigned int *value); + #endif /* PM_API_SYS_H */ diff --git a/plat/xilinx/zynqmp/pm_service/pm_defs.h b/plat/xilinx/zynqmp/pm_service/pm_defs.h index fe965c539..167a73331 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_defs.h +++ b/plat/xilinx/zynqmp/pm_service/pm_defs.h @@ -94,6 +94,7 @@ enum pm_api_id { PM_SECURE_AES, /* PLL control API functions */ PM_PLL_SET_PARAMETER, + PM_PLL_GET_PARAMETER, PM_API_MAX }; diff --git a/plat/xilinx/zynqmp/pm_service/pm_svc_main.c b/plat/xilinx/zynqmp/pm_service/pm_svc_main.c index 743011003..4e4a3ef6d 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_svc_main.c +++ b/plat/xilinx/zynqmp/pm_service/pm_svc_main.c @@ -567,6 +567,14 @@ uint64_t pm_smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, uint64_t x3, ret = pm_pll_set_parameter(pm_arg[0], pm_arg[1], pm_arg[2]); SMC_RET1(handle, (uint64_t)ret); + case PM_PLL_GET_PARAMETER: + { + uint32_t value; + + ret = pm_pll_get_parameter(pm_arg[0], pm_arg[1], &value); + SMC_RET1(handle, (uint64_t)ret | ((uint64_t)value << 32)); + } + default: WARN("Unimplemented PM Service Call: 0x%x\n", smc_fid); SMC_RET1(handle, SMC_UNK); From 5f1a5fee52b9bfb93f588325819cf706cf103d24 Mon Sep 17 00:00:00 2001 From: Jolly Shah Date: Fri, 4 Jan 2019 11:32:31 -0800 Subject: [PATCH 03/20] zynqmp: pm: Implement PLL set mode EEMI API This API will be used to set the PLL mode: reset (unlocked), integer or fractional (locked). If reset mode is set the PM controller will bypass the target PLL prior to asserting the reset. If integer or fractional mode is set the PM controller will program and trigger locking of the PLL. If success status is returned the PLL is locked and its bypass is deasserted. If fractional mode is set the fractional divider (data parameter) has to have a non-zero value prior to issuing pll set fractional mode. The caller need to ensure that the data parameter is properly set using pll get/set parameter EEMI API. Signed-off-by: Mirela Simonovic Acked-by: Will Wong Signed-off-by: Jolly Shah --- plat/xilinx/zynqmp/pm_service/pm_api_sys.c | 30 +++++++++++++++++++++ plat/xilinx/zynqmp/pm_service/pm_api_sys.h | 2 ++ plat/xilinx/zynqmp/pm_service/pm_defs.h | 12 +++++++++ plat/xilinx/zynqmp/pm_service/pm_svc_main.c | 4 +++ 4 files changed, 48 insertions(+) diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_sys.c b/plat/xilinx/zynqmp/pm_service/pm_api_sys.c index da1669114..b37512a23 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_api_sys.c +++ b/plat/xilinx/zynqmp/pm_service/pm_api_sys.c @@ -1296,3 +1296,33 @@ enum pm_ret_status pm_pll_get_parameter(enum pm_node_id nid, PM_PACK_PAYLOAD3(payload, PM_PLL_GET_PARAMETER, nid, param_id); return pm_ipi_send_sync(primary_proc, payload, value, 1); } + +/** + * pm_pll_set_mode() - Set the PLL mode + * @nid Node id of the target PLL + * @mode PLL mode to be set + * + * If reset mode is set the PM controller will first bypass the PLL and then + * assert the reset. If integer or fractional mode is set the PM controller will + * ensure that the complete PLL programming sequence is satisfied. After this + * function returns success the PLL is locked and its bypass is deasserted. + * + * @return Error if an argument is not valid or status as returned by the + * PM controller (PMU) + */ +enum pm_ret_status pm_pll_set_mode(enum pm_node_id nid, enum pm_pll_mode mode) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Check if given node ID is a PLL node */ + if (nid < NODE_APLL || nid > NODE_IOPLL) + return PM_RET_ERROR_ARGS; + + /* Check if PLL mode is valid */ + if (mode >= PM_PLL_MODE_MAX) + return PM_RET_ERROR_ARGS; + + /* Send request to the PMU */ + PM_PACK_PAYLOAD3(payload, PM_PLL_SET_MODE, nid, mode); + return pm_ipi_send_sync(primary_proc, payload, NULL, 0); +} diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_sys.h b/plat/xilinx/zynqmp/pm_service/pm_api_sys.h index 8ef210ac0..9b211f5f7 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_api_sys.h +++ b/plat/xilinx/zynqmp/pm_service/pm_api_sys.h @@ -184,4 +184,6 @@ enum pm_ret_status pm_pll_get_parameter(enum pm_node_id nid, enum pm_pll_param param_id, unsigned int *value); +enum pm_ret_status pm_pll_set_mode(enum pm_node_id nid, enum pm_pll_mode mode); + #endif /* PM_API_SYS_H */ diff --git a/plat/xilinx/zynqmp/pm_service/pm_defs.h b/plat/xilinx/zynqmp/pm_service/pm_defs.h index 167a73331..5927cafbb 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_defs.h +++ b/plat/xilinx/zynqmp/pm_service/pm_defs.h @@ -95,6 +95,7 @@ enum pm_api_id { /* PLL control API functions */ PM_PLL_SET_PARAMETER, PM_PLL_GET_PARAMETER, + PM_PLL_SET_MODE, PM_API_MAX }; @@ -294,5 +295,16 @@ enum pm_pll_param { PM_PLL_PARAM_MAX, }; +/** + * @PM_PLL_MODE_RESET: PLL is in reset (not locked) + * @PM_PLL_MODE_INTEGER: PLL is locked in integer mode + * @PM_PLL_MODE_FRACTIONAL: PLL is locked in fractional mode + */ +enum pm_pll_mode { + PM_PLL_MODE_RESET, + PM_PLL_MODE_INTEGER, + PM_PLL_MODE_FRACTIONAL, + PM_PLL_MODE_MAX, +}; #endif /* PM_DEFS_H */ diff --git a/plat/xilinx/zynqmp/pm_service/pm_svc_main.c b/plat/xilinx/zynqmp/pm_service/pm_svc_main.c index 4e4a3ef6d..881a59336 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_svc_main.c +++ b/plat/xilinx/zynqmp/pm_service/pm_svc_main.c @@ -575,6 +575,10 @@ uint64_t pm_smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, uint64_t x3, SMC_RET1(handle, (uint64_t)ret | ((uint64_t)value << 32)); } + case PM_PLL_SET_MODE: + ret = pm_pll_set_mode(pm_arg[0], pm_arg[1]); + SMC_RET1(handle, (uint64_t)ret); + default: WARN("Unimplemented PM Service Call: 0x%x\n", smc_fid); SMC_RET1(handle, SMC_UNK); From 202797934dcf4bf44b2ec4800ec1a7e1fcb9968b Mon Sep 17 00:00:00 2001 From: Jolly Shah Date: Fri, 4 Jan 2019 11:35:48 -0800 Subject: [PATCH 04/20] zynqmp: pm: Implement PLL get mode EEMI API This API will be used to get the currently configured PLL mode: reset (bypassed and unlocked), integer or fractional (locked). Signed-off-by: Mirela Simonovic Acked-by: Will Wong Signed-off-by: Jolly Shah --- plat/xilinx/zynqmp/pm_service/pm_api_sys.c | 21 +++++++++++++++++++++ plat/xilinx/zynqmp/pm_service/pm_api_sys.h | 1 + plat/xilinx/zynqmp/pm_service/pm_defs.h | 1 + plat/xilinx/zynqmp/pm_service/pm_svc_main.c | 8 ++++++++ 4 files changed, 31 insertions(+) diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_sys.c b/plat/xilinx/zynqmp/pm_service/pm_api_sys.c index b37512a23..171c7e6c9 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_api_sys.c +++ b/plat/xilinx/zynqmp/pm_service/pm_api_sys.c @@ -1326,3 +1326,24 @@ enum pm_ret_status pm_pll_set_mode(enum pm_node_id nid, enum pm_pll_mode mode) PM_PACK_PAYLOAD3(payload, PM_PLL_SET_MODE, nid, mode); return pm_ipi_send_sync(primary_proc, payload, NULL, 0); } + +/** + * pm_pll_get_mode() - Get the PLL mode + * @nid Node id of the target PLL + * @mode Location to store the mode of the PLL + * + * @return Error if an argument is not valid or status as returned by the + * PM controller (PMU) + */ +enum pm_ret_status pm_pll_get_mode(enum pm_node_id nid, enum pm_pll_mode *mode) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Check if given node ID is a PLL node */ + if (nid < NODE_APLL || nid > NODE_IOPLL) + return PM_RET_ERROR_ARGS; + + /* Send request to the PMU */ + PM_PACK_PAYLOAD2(payload, PM_PLL_GET_MODE, nid); + return pm_ipi_send_sync(primary_proc, payload, mode, 1); +} diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_sys.h b/plat/xilinx/zynqmp/pm_service/pm_api_sys.h index 9b211f5f7..bc96c1f9f 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_api_sys.h +++ b/plat/xilinx/zynqmp/pm_service/pm_api_sys.h @@ -185,5 +185,6 @@ enum pm_ret_status pm_pll_get_parameter(enum pm_node_id nid, unsigned int *value); enum pm_ret_status pm_pll_set_mode(enum pm_node_id nid, enum pm_pll_mode mode); +enum pm_ret_status pm_pll_get_mode(enum pm_node_id nid, enum pm_pll_mode *mode); #endif /* PM_API_SYS_H */ diff --git a/plat/xilinx/zynqmp/pm_service/pm_defs.h b/plat/xilinx/zynqmp/pm_service/pm_defs.h index 5927cafbb..7c2389385 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_defs.h +++ b/plat/xilinx/zynqmp/pm_service/pm_defs.h @@ -96,6 +96,7 @@ enum pm_api_id { PM_PLL_SET_PARAMETER, PM_PLL_GET_PARAMETER, PM_PLL_SET_MODE, + PM_PLL_GET_MODE, PM_API_MAX }; diff --git a/plat/xilinx/zynqmp/pm_service/pm_svc_main.c b/plat/xilinx/zynqmp/pm_service/pm_svc_main.c index 881a59336..a18f84506 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_svc_main.c +++ b/plat/xilinx/zynqmp/pm_service/pm_svc_main.c @@ -579,6 +579,14 @@ uint64_t pm_smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, uint64_t x3, ret = pm_pll_set_mode(pm_arg[0], pm_arg[1]); SMC_RET1(handle, (uint64_t)ret); + case PM_PLL_GET_MODE: + { + uint32_t mode; + + ret = pm_pll_get_mode(pm_arg[0], &mode); + SMC_RET1(handle, (uint64_t)ret | ((uint64_t)mode << 32)); + } + default: WARN("Unimplemented PM Service Call: 0x%x\n", smc_fid); SMC_RET1(handle, SMC_UNK); From 1e3fb352b7415ed53bdb6e439970870e3828bc64 Mon Sep 17 00:00:00 2001 From: Jolly Shah Date: Wed, 2 Jan 2019 12:40:17 -0800 Subject: [PATCH 05/20] zynqmp: pm: Get PLL fractional data using PLL get parameter EEMI API Fractional data should be get using PLL get parameter EEMI API. This stands for system-level communication (APU to PMU). Since linux already uses a specific IOCTL function to do this and we need to keep it that way, the pll clock ID given by linux has to be mapped to the pll node ID that is communicated at the system-level (argument of PLL get parameter API). With this modification the function pm_api_clk_get_pll_frac_data is removed from pm_api_clock.c/h because it became unused. The clock enum is defined as 'enum clock_id'. Signed-off-by: Mirela Simonovic Acked-by: Will Wong Signed-off-by: Jolly Shah --- plat/xilinx/zynqmp/pm_service/pm_api_clock.c | 100 +++++++++++++------ plat/xilinx/zynqmp/pm_service/pm_api_clock.h | 8 +- plat/xilinx/zynqmp/pm_service/pm_api_ioctl.c | 12 ++- 3 files changed, 83 insertions(+), 37 deletions(-) diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_clock.c b/plat/xilinx/zynqmp/pm_service/pm_api_clock.c index b175b78b3..723ad401d 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_api_clock.c +++ b/plat/xilinx/zynqmp/pm_service/pm_api_clock.c @@ -2488,6 +2488,74 @@ enum pm_ret_status pm_api_clock_get_attributes(unsigned int clock_id, return PM_RET_SUCCESS; } +/** + * struct pm_pll - PLL related data required to map IOCTL-based PLL control + * implemented by linux to system-level EEMI APIs + * @nid: PLL node ID + * @cid: PLL clock ID + */ +struct pm_pll { + const enum pm_node_id nid; + const enum clock_id cid; +}; + +static struct pm_pll pm_plls[] = { + { + .nid = NODE_IOPLL, + .cid = CLK_IOPLL_INT, + }, { + .nid = NODE_RPLL, + .cid = CLK_RPLL_INT, + }, { + .nid = NODE_APLL, + .cid = CLK_APLL_INT, + }, { + .nid = NODE_VPLL, + .cid = CLK_VPLL_INT, + }, { + .nid = NODE_DPLL, + .cid = CLK_DPLL_INT, + }, +}; + +/** + * pm_clock_get_pll() - Get PLL structure by PLL clock ID + * @clock_id Clock ID of the target PLL + * + * @return Pointer to PLL structure if found, NULL otherwise + */ +static struct pm_pll *pm_clock_get_pll(enum clock_id clock_id) +{ + uint32_t i; + + for (i = 0; i < ARRAY_SIZE(pm_plls); i++) { + if (pm_plls[i].cid == clock_id) + return &pm_plls[i]; + } + + return NULL; +} + +/** + * pm_clock_get_pll_node_id() - Get PLL node ID by PLL clock ID + * @clock_id Clock ID of the target PLL + * @node_id Location to store node ID of the target PLL + * + * @return PM_RET_SUCCESS if node ID is found, PM_RET_ERROR_ARGS otherwise + */ +enum pm_ret_status pm_clock_get_pll_node_id(enum clock_id clock_id, + enum pm_node_id *node_id) +{ + struct pm_pll *pll = pm_clock_get_pll(clock_id); + + if (pll) { + *node_id = pll->nid; + return PM_RET_SUCCESS; + } + + return PM_RET_ERROR_ARGS; +} + /** * pll_get_lockbit() - Returns lockbit index for pll id * @pll_id: Id of the pll @@ -3170,35 +3238,3 @@ enum pm_ret_status pm_api_clk_set_pll_frac_data(unsigned int pll, return ret; } - -/** - * pm_api_clk_get_pll_frac_data() - Get PLL fraction data - * @pll PLL id - * @data fraction data - * - * This function returns fraction data value. - * - * @return Returns status, either success or error+reason - */ -enum pm_ret_status pm_api_clk_get_pll_frac_data(unsigned int pll, - unsigned int *data) -{ - enum pm_ret_status ret = PM_RET_SUCCESS; - unsigned int val, reg; - - if (!pm_clock_valid(pll)) - return PM_RET_ERROR_ARGS; - - if (pm_clock_type(pll) != CLK_TYPE_OUTPUT) - return PM_RET_ERROR_NOTSUPPORTED; - - if (!ISPLL(pll)) - return PM_RET_ERROR_NOTSUPPORTED; - - reg = clocks[pll].control_reg + PLL_FRAC_OFFSET; - - ret = pm_mmio_read(reg, &val); - *data = (val & PLL_FRAC_DATA_MASK); - - return ret; -} diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_clock.h b/plat/xilinx/zynqmp/pm_service/pm_api_clock.h index f7cbdbaf0..ebada774b 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_api_clock.h +++ b/plat/xilinx/zynqmp/pm_service/pm_api_clock.h @@ -55,7 +55,7 @@ #define END_OF_CLK "END_OF_CLK" //CLock Ids -enum { +enum clock_id { CLK_IOPLL, CLK_RPLL, CLK_APLL, @@ -288,6 +288,10 @@ enum pm_ret_status pm_api_clock_get_parents(unsigned int clock_id, uint32_t *parents); enum pm_ret_status pm_api_clock_get_attributes(unsigned int clock_id, uint32_t *attr); + +enum pm_ret_status pm_clock_get_pll_node_id(enum clock_id clock_id, + enum pm_node_id *node_id); + enum pm_ret_status pm_api_clock_enable(unsigned int clock_id); enum pm_ret_status pm_api_clock_disable(unsigned int clock_id); enum pm_ret_status pm_api_clock_getstate(unsigned int clock_id, @@ -310,7 +314,5 @@ enum pm_ret_status pm_api_clk_get_pll_mode(unsigned int pll, unsigned int *mode); enum pm_ret_status pm_api_clk_set_pll_frac_data(unsigned int pll, unsigned int data); -enum pm_ret_status pm_api_clk_get_pll_frac_data(unsigned int pll, - unsigned int *data); #endif /* PM_API_CLOCK_H */ diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_ioctl.c b/plat/xilinx/zynqmp/pm_service/pm_api_ioctl.c index 16c08ae6d..87c3f7de9 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_api_ioctl.c +++ b/plat/xilinx/zynqmp/pm_service/pm_api_ioctl.c @@ -381,7 +381,7 @@ static enum pm_ret_status pm_ioctl_set_pll_frac_data /** * pm_ioctl_get_pll_frac_data() - Ioctl function for * getting pll fraction data - * @pll PLL id + * @pll PLL clock id * @data fraction data * * This function returns fraction data value. @@ -391,7 +391,15 @@ static enum pm_ret_status pm_ioctl_set_pll_frac_data static enum pm_ret_status pm_ioctl_get_pll_frac_data (unsigned int pll, unsigned int *data) { - return pm_api_clk_get_pll_frac_data(pll, data); + enum pm_node_id pll_nid; + enum pm_ret_status status; + + /* Get PLL node ID using PLL clock ID */ + status = pm_clock_get_pll_node_id(pll, &pll_nid); + if (status != PM_RET_SUCCESS) + return status; + + return pm_pll_get_parameter(pll_nid, PM_PLL_PARAM_DATA, data); } /** From cf1769b5922f725486ff1776d685eb5b68081ee7 Mon Sep 17 00:00:00 2001 From: Jolly Shah Date: Wed, 2 Jan 2019 12:42:56 -0800 Subject: [PATCH 06/20] zynqmp: pm: Set PLL fractional data using PLL set parameter EEMI API Fractional data should be set using PLL set parameter EEMI API. This stands for system-level communication (APU to PMU). Since linux already uses a specific IOCTL function to do this and we need to keep it that way, the pll clock ID given by linux has to be mapped to the pll node ID that is communicated at the system-level (argument of PLL set parameter API). With this modification the function pm_api_clk_set_pll_frac_data is removed from pm_api_clock.c/h because it became unused. Signed-off-by: Mirela Simonovic Acked-by: Will Wong Signed-off-by: Jolly Shah --- plat/xilinx/zynqmp/pm_service/pm_api_clock.c | 39 -------------------- plat/xilinx/zynqmp/pm_service/pm_api_clock.h | 2 - plat/xilinx/zynqmp/pm_service/pm_api_ioctl.c | 12 +++++- 3 files changed, 10 insertions(+), 43 deletions(-) diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_clock.c b/plat/xilinx/zynqmp/pm_service/pm_api_clock.c index 723ad401d..a09c9e5f7 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_api_clock.c +++ b/plat/xilinx/zynqmp/pm_service/pm_api_clock.c @@ -3199,42 +3199,3 @@ enum pm_ret_status pm_api_clk_get_pll_mode(unsigned int pll, return ret; } - -/** - * pm_api_clk_set_pll_frac_data() - Set PLL fraction data - * @pll PLL id - * @data fraction data - * - * This function sets fraction data. It is valid for fraction - * mode only. - * - * @return Returns status, either success or error+reason - */ -enum pm_ret_status pm_api_clk_set_pll_frac_data(unsigned int pll, - unsigned int data) -{ - enum pm_ret_status ret = PM_RET_SUCCESS; - unsigned int val, reg, mode = 0; - - if (!pm_clock_valid(pll)) - return PM_RET_ERROR_ARGS; - - if (pm_clock_type(pll) != CLK_TYPE_OUTPUT) - return PM_RET_ERROR_NOTSUPPORTED; - - if (!ISPLL(pll)) - return PM_RET_ERROR_NOTSUPPORTED; - - ret = pm_api_clk_get_pll_mode(pll, &mode); - if (ret != PM_RET_SUCCESS) - return ret; - if (mode == PLL_FRAC_MODE) { - reg = clocks[pll].control_reg + PLL_FRAC_OFFSET; - val = data << PLL_FRAC_DATA_SHIFT; - ret = pm_mmio_write(reg, PLL_FRAC_DATA_MASK, val); - } else { - return PM_RET_ERROR_ARGS; - } - - return ret; -} diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_clock.h b/plat/xilinx/zynqmp/pm_service/pm_api_clock.h index ebada774b..482662d7e 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_api_clock.h +++ b/plat/xilinx/zynqmp/pm_service/pm_api_clock.h @@ -312,7 +312,5 @@ enum pm_ret_status pm_api_clk_set_pll_mode(unsigned int pll, unsigned int mode); enum pm_ret_status pm_api_clk_get_pll_mode(unsigned int pll, unsigned int *mode); -enum pm_ret_status pm_api_clk_set_pll_frac_data(unsigned int pll, - unsigned int data); #endif /* PM_API_CLOCK_H */ diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_ioctl.c b/plat/xilinx/zynqmp/pm_service/pm_api_ioctl.c index 87c3f7de9..235d8824b 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_api_ioctl.c +++ b/plat/xilinx/zynqmp/pm_service/pm_api_ioctl.c @@ -364,7 +364,7 @@ static enum pm_ret_status pm_ioctl_get_pll_frac_mode /** * pm_ioctl_set_pll_frac_data() - Ioctl function for * setting pll fraction data - * @pll PLL id + * @pll PLL clock id * @data fraction data * * This function sets fraction data. @@ -375,7 +375,15 @@ static enum pm_ret_status pm_ioctl_get_pll_frac_mode static enum pm_ret_status pm_ioctl_set_pll_frac_data (unsigned int pll, unsigned int data) { - return pm_api_clk_set_pll_frac_data(pll, data); + enum pm_node_id pll_nid; + enum pm_ret_status status; + + /* Get PLL node ID using PLL clock ID */ + status = pm_clock_get_pll_node_id(pll, &pll_nid); + if (status != PM_RET_SUCCESS) + return status; + + return pm_pll_set_parameter(pll_nid, PM_PLL_PARAM_DATA, data); } /** From 8975f317e7608c832192b71531901602dc625484 Mon Sep 17 00:00:00 2001 From: Jolly Shah Date: Wed, 2 Jan 2019 12:46:46 -0800 Subject: [PATCH 07/20] zynqmp: pm: Buffer the PLL mode that is set using IOCTL API When linux calls pm_ioctl_set_pll_frac_mode() it doesn't expect the fractional mode to be changed in hardware. Furthermore, even before this patch setting the mode which is done by writing into register takes no effect until the PLL reset is deasserted, i.e. until linux "enables" the PLL. To adjust the code to system-level PLL EEMI API and avoid unnecessary IPIs that would otherwise be issued, we buffer the mode value set via IOCTL until the PLL mode really needs to be set. Signed-off-by: Mirela Simonovic Acked-by: Will Wong Signed-off-by: Jolly Shah --- plat/xilinx/zynqmp/pm_service/pm_api_clock.c | 38 +++++++------------- plat/xilinx/zynqmp/pm_service/pm_api_clock.h | 4 +-- plat/xilinx/zynqmp/pm_service/pm_api_ioctl.c | 4 +-- 3 files changed, 17 insertions(+), 29 deletions(-) diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_clock.c b/plat/xilinx/zynqmp/pm_service/pm_api_clock.c index a09c9e5f7..8cdc0e43e 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_api_clock.c +++ b/plat/xilinx/zynqmp/pm_service/pm_api_clock.c @@ -2493,10 +2493,12 @@ enum pm_ret_status pm_api_clock_get_attributes(unsigned int clock_id, * implemented by linux to system-level EEMI APIs * @nid: PLL node ID * @cid: PLL clock ID + * @mode: PLL mode currently set via IOCTL (PLL_FRAC_MODE/PLL_INT_MODE) */ struct pm_pll { const enum pm_node_id nid; const enum clock_id cid; + uint8_t mode; }; static struct pm_pll pm_plls[] = { @@ -3130,38 +3132,24 @@ enum pm_ret_status pm_api_clock_getparent(unsigned int clock_id, } /** - * pm_api_clk_set_pll_mode() - Set PLL mode - * @pll PLL id - * @mode Mode fraction/integar + * pm_clock_set_pll_mode() - Set PLL mode + * @clock_id PLL clock id + * @mode Mode fractional/integer * - * This function sets PLL mode. + * This function buffers/saves the PLL mode that is set. * - * @return Returns status, either success or error+reason + * @return Success if mode is buffered or error if an argument is invalid */ -enum pm_ret_status pm_api_clk_set_pll_mode(unsigned int pll, - unsigned int mode) +enum pm_ret_status pm_clock_set_pll_mode(enum clock_id clock_id, + unsigned int mode) { - enum pm_ret_status ret = PM_RET_SUCCESS; - unsigned int reg; + struct pm_pll *pll = pm_clock_get_pll(clock_id); - if (!pm_clock_valid(pll)) + if (!pll || (mode != PLL_FRAC_MODE && mode != PLL_INT_MODE)) return PM_RET_ERROR_ARGS; + pll->mode = mode; - if (pm_clock_type(pll) != CLK_TYPE_OUTPUT) - return PM_RET_ERROR_NOTSUPPORTED; - - if (!ISPLL(pll)) - return PM_RET_ERROR_NOTSUPPORTED; - - if (mode != PLL_FRAC_MODE && mode != PLL_INT_MODE) - return PM_RET_ERROR_ARGS; - - reg = clocks[pll].control_reg + PLL_FRAC_OFFSET; - - ret = pm_mmio_write(reg, PLL_FRAC_MODE_MASK, - mode << PLL_FRAC_MODE_SHIFT); - - return ret; + return PM_RET_SUCCESS; } /** diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_clock.h b/plat/xilinx/zynqmp/pm_service/pm_api_clock.h index 482662d7e..bf42d950f 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_api_clock.h +++ b/plat/xilinx/zynqmp/pm_service/pm_api_clock.h @@ -308,8 +308,8 @@ enum pm_ret_status pm_api_clock_setparent(unsigned int clock_id, unsigned int parent_idx); enum pm_ret_status pm_api_clock_getparent(unsigned int clock_id, unsigned int *parent_idx); -enum pm_ret_status pm_api_clk_set_pll_mode(unsigned int pll, - unsigned int mode); +enum pm_ret_status pm_clock_set_pll_mode(enum clock_id clock_id, + unsigned int mode); enum pm_ret_status pm_api_clk_get_pll_mode(unsigned int pll, unsigned int *mode); diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_ioctl.c b/plat/xilinx/zynqmp/pm_service/pm_api_ioctl.c index 235d8824b..284d9015c 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_api_ioctl.c +++ b/plat/xilinx/zynqmp/pm_service/pm_api_ioctl.c @@ -332,7 +332,7 @@ reset_release: /** * pm_ioctl_set_pll_frac_mode() - Ioctl function for * setting pll mode - * @pll PLL id + * @pll PLL clock id * @mode Mode fraction/integar * * This function sets PLL mode @@ -342,7 +342,7 @@ reset_release: static enum pm_ret_status pm_ioctl_set_pll_frac_mode (unsigned int pll, unsigned int mode) { - return pm_api_clk_set_pll_mode(pll, mode); + return pm_clock_set_pll_mode(pll, mode); } /** From a5ae5a72fa71ceb4e14d23dc478d451ae7cfe834 Mon Sep 17 00:00:00 2001 From: Jolly Shah Date: Wed, 2 Jan 2019 12:49:21 -0800 Subject: [PATCH 08/20] zynqmp: pm: Return the buffered PLL mode through IOCTL PLL get mode API When linux calls pm_ioctl_get_pll_frac_mode() it doesn't expect the actual mode to be read from hardware, but the value that it is intending to program. Therefore, we return the buffered value to linux. Signed-off-by: Mirela Simonovic Acked-by: Will Wong Signed-off-by: Jolly Shah --- plat/xilinx/zynqmp/pm_service/pm_api_clock.c | 37 ++++++-------------- plat/xilinx/zynqmp/pm_service/pm_api_clock.h | 4 +-- plat/xilinx/zynqmp/pm_service/pm_api_ioctl.c | 4 +-- 3 files changed, 15 insertions(+), 30 deletions(-) diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_clock.c b/plat/xilinx/zynqmp/pm_service/pm_api_clock.c index 8cdc0e43e..1fab38fac 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_api_clock.c +++ b/plat/xilinx/zynqmp/pm_service/pm_api_clock.c @@ -3153,37 +3153,22 @@ enum pm_ret_status pm_clock_set_pll_mode(enum clock_id clock_id, } /** - * pm_ioctl_get_pll_mode() - Get PLL mode - * @pll PLL id - * @mode Mode fraction/integar + * pm_clock_get_pll_mode() - Get PLL mode + * @clock_id PLL clock id + * @mode Location to store the mode (fractional/integer) * - * This function returns current PLL mode. + * This function returns buffered PLL mode. * - * @return Returns status, either success or error+reason + * @return Success if mode is stored or error if an argument is invalid */ -enum pm_ret_status pm_api_clk_get_pll_mode(unsigned int pll, - unsigned int *mode) +enum pm_ret_status pm_clock_get_pll_mode(enum clock_id clock_id, + unsigned int *mode) { - enum pm_ret_status ret = PM_RET_SUCCESS; - unsigned int val, reg; + struct pm_pll *pll = pm_clock_get_pll(clock_id); - if (!pm_clock_valid(pll)) + if (!pll || !mode) return PM_RET_ERROR_ARGS; + *mode = pll->mode; - if (pm_clock_type(pll) != CLK_TYPE_OUTPUT) - return PM_RET_ERROR_NOTSUPPORTED; - - if (!ISPLL(pll)) - return PM_RET_ERROR_NOTSUPPORTED; - - reg = clocks[pll].control_reg + PLL_FRAC_OFFSET; - - ret = pm_mmio_read(reg, &val); - val = val & PLL_FRAC_MODE_MASK; - if (val == 0) - *mode = PLL_INT_MODE; - else - *mode = PLL_FRAC_MODE; - - return ret; + return PM_RET_SUCCESS; } diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_clock.h b/plat/xilinx/zynqmp/pm_service/pm_api_clock.h index bf42d950f..3a70036f9 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_api_clock.h +++ b/plat/xilinx/zynqmp/pm_service/pm_api_clock.h @@ -310,7 +310,7 @@ enum pm_ret_status pm_api_clock_getparent(unsigned int clock_id, unsigned int *parent_idx); enum pm_ret_status pm_clock_set_pll_mode(enum clock_id clock_id, unsigned int mode); -enum pm_ret_status pm_api_clk_get_pll_mode(unsigned int pll, - unsigned int *mode); +enum pm_ret_status pm_clock_get_pll_mode(enum clock_id clock_id, + unsigned int *mode); #endif /* PM_API_CLOCK_H */ diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_ioctl.c b/plat/xilinx/zynqmp/pm_service/pm_api_ioctl.c index 284d9015c..3e368971b 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_api_ioctl.c +++ b/plat/xilinx/zynqmp/pm_service/pm_api_ioctl.c @@ -348,7 +348,7 @@ static enum pm_ret_status pm_ioctl_set_pll_frac_mode /** * pm_ioctl_get_pll_frac_mode() - Ioctl function for * getting pll mode - * @pll PLL id + * @pll PLL clock id * @mode Mode fraction/integar * * This function return current PLL mode @@ -358,7 +358,7 @@ static enum pm_ret_status pm_ioctl_set_pll_frac_mode static enum pm_ret_status pm_ioctl_get_pll_frac_mode (unsigned int pll, unsigned int *mode) { - return pm_api_clk_get_pll_mode(pll, mode); + return pm_clock_get_pll_mode(pll, mode); } /** From bd642dde65a51328ff2041a751550d19380f6658 Mon Sep 17 00:00:00 2001 From: Jolly Shah Date: Fri, 4 Jan 2019 11:45:59 -0800 Subject: [PATCH 09/20] zynqmp: pm: Reimplement clock enable EEMI API Clock enable EEMI API is reimplemented to use system-level clock and pll EEMI APIs rather than direct MMIO read/write accesses to clock and pll control registers. Since linux still uses clock enable API to trigger locking of the PLLs in the pm_clock_enable() implementation we need to workaround this by distinguishing two cases: 1) if the given clock ID corresponds to a PLL output clock ID; or 2) given clock ID is truly an on-chip clock that can be gated. For case 1) we'll call pm_api_clock_pll_enable() implemented in pm_api_clock.h/c. This function checks what is the buffered PLL mode and calls the system-level PLL set mode EEMI API with the buffered mode value specified as argument. Long term, if linux driver get fixed to use PLL EEMI API to control PLLs, this case could be removed from ATF. For case 2) we'll call the PMU to configure the clock gate. This is done using system-level clock enable EEMI API. Signed-off-by: Mirela Simonovic Acked-by: Will Wong Signed-off-by: Jolly Shah --- plat/xilinx/zynqmp/pm_service/pm_api_clock.c | 49 ++++++++++++-------- plat/xilinx/zynqmp/pm_service/pm_api_clock.h | 5 +- plat/xilinx/zynqmp/pm_service/pm_api_sys.c | 44 ++++++++++++++++-- 3 files changed, 74 insertions(+), 24 deletions(-) diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_clock.c b/plat/xilinx/zynqmp/pm_service/pm_api_clock.c index 1fab38fac..ee8b38738 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_api_clock.c +++ b/plat/xilinx/zynqmp/pm_service/pm_api_clock.c @@ -2526,7 +2526,7 @@ static struct pm_pll pm_plls[] = { * * @return Pointer to PLL structure if found, NULL otherwise */ -static struct pm_pll *pm_clock_get_pll(enum clock_id clock_id) +struct pm_pll *pm_clock_get_pll(enum clock_id clock_id) { uint32_t i; @@ -2679,32 +2679,24 @@ static enum pm_ret_status pm_api_clk_enable_disable(unsigned int clock_id, } /** - * pm_api_clock_enable() - Enable the clock for given id - * @clock_id: Id of the clock to be enabled + * pm_clock_pll_enable() - "Enable" the PLL clock (lock the PLL) + * @pll: PLL to be locked * - * This function is used by master to enable the clock - * including peripherals and PLL clocks. + * This function is used to map IOCTL/linux-based PLL handling to system-level + * EEMI APIs * - * Return: Returns status, either success or error+reason. + * Return: Error if the argument is not valid or status as returned by PMU */ -enum pm_ret_status pm_api_clock_enable(unsigned int clock_id) +enum pm_ret_status pm_clock_pll_enable(struct pm_pll *pll) { - enum pm_ret_status ret = PM_RET_SUCCESS; - - if (!pm_clock_valid(clock_id)) + if (!pll) return PM_RET_ERROR_ARGS; - if (pm_clock_type(clock_id) != CLK_TYPE_OUTPUT) - return PM_RET_ERROR_NOTSUPPORTED; + /* Set the PLL mode according to the buffered mode value */ + if (pll->mode == PLL_FRAC_MODE) + return pm_pll_set_mode(pll->nid, PM_PLL_MODE_FRACTIONAL); - /* - * PLL type clock should not enable explicitly. - * It is done by FSBL on boot-up and by PMUFW whenever required. - */ - if (!ISPLL(clock_id)) - ret = pm_api_clk_enable_disable(clock_id, 1); - - return ret; + return pm_pll_set_mode(pll->nid, PM_PLL_MODE_INTEGER); } /** @@ -3172,3 +3164,20 @@ enum pm_ret_status pm_clock_get_pll_mode(enum clock_id clock_id, return PM_RET_SUCCESS; } + +/** + * pm_clock_id_is_valid() - Check if given clock ID is valid + * @clock_id ID of the clock to be checked + * + * @return Returns success if clock_id is valid, otherwise an error + */ +enum pm_ret_status pm_clock_id_is_valid(unsigned int clock_id) +{ + if (!pm_clock_valid(clock_id)) + return PM_RET_ERROR_ARGS; + + if (pm_clock_type(clock_id) != CLK_TYPE_OUTPUT) + return PM_RET_ERROR_NOTSUPPORTED; + + return PM_RET_SUCCESS; +} diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_clock.h b/plat/xilinx/zynqmp/pm_service/pm_api_clock.h index 3a70036f9..ab7a8a405 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_api_clock.h +++ b/plat/xilinx/zynqmp/pm_service/pm_api_clock.h @@ -274,6 +274,8 @@ enum { #define TYPE_DIV2 5U #define TYPE_GATE 6U +struct pm_pll; +struct pm_pll *pm_clock_get_pll(enum clock_id clock_id); enum pm_ret_status pm_api_clock_get_name(unsigned int clock_id, char *name); enum pm_ret_status pm_api_clock_get_num_clocks(unsigned int *nclocks); @@ -291,8 +293,9 @@ enum pm_ret_status pm_api_clock_get_attributes(unsigned int clock_id, enum pm_ret_status pm_clock_get_pll_node_id(enum clock_id clock_id, enum pm_node_id *node_id); +enum pm_ret_status pm_clock_id_is_valid(unsigned int clock_id); -enum pm_ret_status pm_api_clock_enable(unsigned int clock_id); +enum pm_ret_status pm_clock_pll_enable(struct pm_pll *pll); enum pm_ret_status pm_api_clock_disable(unsigned int clock_id); enum pm_ret_status pm_api_clock_getstate(unsigned int clock_id, unsigned int *state); diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_sys.c b/plat/xilinx/zynqmp/pm_service/pm_api_sys.c index 171c7e6c9..e33761c84 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_api_sys.c +++ b/plat/xilinx/zynqmp/pm_service/pm_api_sys.c @@ -843,6 +843,36 @@ static enum pm_ret_status pm_clock_get_attributes(unsigned int clock_id, return pm_api_clock_get_attributes(clock_id, attr); } +/** + * pm_clock_gate() - Configure clock gate + * @clock_id Id of the clock to be configured + * @enable Flag 0=disable (gate the clock), !0=enable (activate the clock) + * + * @return Error if an argument is not valid or status as returned by the + * PM controller (PMU) + */ +static enum pm_ret_status pm_clock_gate(unsigned int clock_id, + unsigned char enable) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + enum pm_ret_status status; + enum pm_api_id api_id; + + /* Check if clock ID is valid and return an error if it is not */ + status = pm_clock_id_is_valid(clock_id); + if (status != PM_RET_SUCCESS) + return status; + + if (enable) + api_id = PM_CLOCK_ENABLE; + else + api_id = PM_CLOCK_DISABLE; + + /* Send request to the PMU */ + PM_PACK_PAYLOAD2(payload, api_id, clock_id); + return pm_ipi_send_sync(primary_proc, payload, NULL, 0); +} + /** * pm_clock_enable() - Enable the clock for given id * @clock_id: Id of the clock to be enabled @@ -850,12 +880,20 @@ static enum pm_ret_status pm_clock_get_attributes(unsigned int clock_id, * This function is used by master to enable the clock * including peripherals and PLL clocks. * - * Return: Returns status, either success or error+reason. + * @return: Error if an argument is not valid or status as returned by the + * pm_clock_gate */ - enum pm_ret_status pm_clock_enable(unsigned int clock_id) { - return pm_api_clock_enable(clock_id); + struct pm_pll *pll; + + /* First try to handle it as a PLL */ + pll = pm_clock_get_pll(clock_id); + if (pll) + return pm_clock_pll_enable(pll); + + /* It's an on-chip clock, PMU should configure clock's gate */ + return pm_clock_gate(clock_id, 1); } /** From d3a78ca46c253ca26e47ee2edf7372171aa066bb Mon Sep 17 00:00:00 2001 From: Jolly Shah Date: Wed, 2 Jan 2019 12:54:40 -0800 Subject: [PATCH 10/20] zynqmp: pm: Reimplement clock disable EEMI API Clock disable EEMI API is reimplemented to use system-level clock and pll EEMI APIs rather than direct MMIO read/write accesses to clock and pll control registers. Since linux still uses clock disable API to reset the PLL in the implementation of pm_clock_disable() we need to workaround this by distinguishing two cases: 1) if the given clock ID corresponds to a PLL output clock ID; or 2) given clock ID is truly an on-chip clock that can be gated. For case 1) we'll call pm_api_clock_pll_disable() implemented in pm_api_clock.h/c. This function will reset the PLL using the system-level PLL set mode EEMI API with the reset mode argument. For case 2) we'll call the PMU to configure the clock gate. This is done using system-level clock disable EEMI API. Functions that appear to be unused after this change is made are removed. Signed-off-by: Mirela Simonovic Acked-by: Will Wong Signed-off-by: Jolly Shah --- plat/xilinx/zynqmp/pm_service/pm_api_clock.c | 81 ++------------------ plat/xilinx/zynqmp/pm_service/pm_api_clock.h | 2 +- plat/xilinx/zynqmp/pm_service/pm_api_sys.c | 14 +++- 3 files changed, 20 insertions(+), 77 deletions(-) diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_clock.c b/plat/xilinx/zynqmp/pm_service/pm_api_clock.c index ee8b38738..542dbdcd6 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_api_clock.c +++ b/plat/xilinx/zynqmp/pm_service/pm_api_clock.c @@ -2626,58 +2626,6 @@ pm_api_pll_bypass_and_reset(unsigned int clock_id, unsigned int flag) return ret; } -/** - * pm_api_clk_enable_disable() - Enable/Disable the clock for given id - * @clock_id: Id of the clock to be enabled - * @enable: Enable(1)/Disable(0) - * - * This function is to enable/disable the clock which is not PLL. - * - * Return: Returns status, either success or error+reason. - */ -static enum pm_ret_status pm_api_clk_enable_disable(unsigned int clock_id, - unsigned int enable) -{ - enum pm_ret_status ret = PM_RET_SUCCESS; - struct pm_clock_node *nodes = *clocks[clock_id].nodes; - uint8_t num_nodes = clocks[clock_id].num_nodes; - unsigned int reg, val; - uint8_t i = 0; - uint8_t offset = NA_SHIFT, width = NA_WIDTH; - - if (clock_id == CLK_GEM0_TX || clock_id == CLK_GEM1_TX || - clock_id == CLK_GEM2_TX || clock_id == CLK_GEM3_TX) - reg = clocks[clock_id].status_reg; - else - reg = clocks[clock_id].control_reg; - - for (i = 0; i < num_nodes; i++) { - if (nodes->type == TYPE_GATE) { - offset = nodes->offset; - width = nodes->width; - break; - } - nodes++; - } - if (width == NA_WIDTH) - return PM_RET_ERROR_NOTSUPPORTED; - - ret = pm_mmio_read(reg, &val); - if (ret != PM_RET_SUCCESS) - return ret; - if ((val & BIT_MASK(offset, width)) == enable) - return PM_RET_SUCCESS; - - if (enable == 0) - val &= ~(BIT_MASK(offset, width)); - else - val |= BIT_MASK(offset, width); - - ret = pm_mmio_write(reg, BIT_MASK(offset, width), val); - - return ret; -} - /** * pm_clock_pll_enable() - "Enable" the PLL clock (lock the PLL) * @pll: PLL to be locked @@ -2700,33 +2648,20 @@ enum pm_ret_status pm_clock_pll_enable(struct pm_pll *pll) } /** - * pm_api_clock_disable - Disable the clock for given id - * @clock_id Id of the clock to be disable + * pm_clock_pll_disable - "Disable" the PLL clock (bypass/reset the PLL) + * @pll PLL to be bypassed/reset * - * This function is used by master to disable the clock - * including peripherals and PLL clocks. + * This function is used to map IOCTL/linux-based PLL handling to system-level + * EEMI APIs * - * Return: Returns status, either success or error+reason. + * Return: Error if the argument is not valid or status as returned by PMU */ - -enum pm_ret_status pm_api_clock_disable(unsigned int clock_id) +enum pm_ret_status pm_clock_pll_disable(struct pm_pll *pll) { - enum pm_ret_status ret = PM_RET_SUCCESS; - - if (!pm_clock_valid(clock_id)) + if (!pll) return PM_RET_ERROR_ARGS; - if (pm_clock_type(clock_id) != CLK_TYPE_OUTPUT) - return PM_RET_ERROR_NOTSUPPORTED; - - /* - * PLL type clock should not be disabled explicitly. - * It is done by PMUFW if required. - */ - if (!ISPLL(clock_id)) - ret = pm_api_clk_enable_disable(clock_id, 0); - - return ret; + return pm_pll_set_mode(pll->nid, PM_PLL_MODE_RESET); } /** diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_clock.h b/plat/xilinx/zynqmp/pm_service/pm_api_clock.h index ab7a8a405..5ec4d7496 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_api_clock.h +++ b/plat/xilinx/zynqmp/pm_service/pm_api_clock.h @@ -296,7 +296,7 @@ enum pm_ret_status pm_clock_get_pll_node_id(enum clock_id clock_id, enum pm_ret_status pm_clock_id_is_valid(unsigned int clock_id); enum pm_ret_status pm_clock_pll_enable(struct pm_pll *pll); -enum pm_ret_status pm_api_clock_disable(unsigned int clock_id); +enum pm_ret_status pm_clock_pll_disable(struct pm_pll *pll); enum pm_ret_status pm_api_clock_getstate(unsigned int clock_id, unsigned int *state); enum pm_ret_status pm_api_clock_setdivider(unsigned int clock_id, diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_sys.c b/plat/xilinx/zynqmp/pm_service/pm_api_sys.c index e33761c84..fe5828edd 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_api_sys.c +++ b/plat/xilinx/zynqmp/pm_service/pm_api_sys.c @@ -903,12 +903,20 @@ enum pm_ret_status pm_clock_enable(unsigned int clock_id) * This function is used by master to disable the clock * including peripherals and PLL clocks. * - * Return: Returns status, either success or error+reason. + * @return: Error if an argument is not valid or status as returned by the + * pm_clock_gate */ - enum pm_ret_status pm_clock_disable(unsigned int clock_id) { - return pm_api_clock_disable(clock_id); + struct pm_pll *pll; + + /* First try to handle it as a PLL */ + pll = pm_clock_get_pll(clock_id); + if (pll) + return pm_clock_pll_disable(pll); + + /* It's an on-chip clock, PMU should configure clock's gate */ + return pm_clock_gate(clock_id, 0); } /** From bd30503a9dc8f23d8ef7431efbc2f6c106ee326c Mon Sep 17 00:00:00 2001 From: Jolly Shah Date: Wed, 2 Jan 2019 12:55:41 -0800 Subject: [PATCH 11/20] zynqmp: pm: Reimplement clock get state (status) EEMI API Clock get state EEMI API is reimplemented to use system-level clock and pll EEMI APIs rather than direct MMIO read/write accesses to clock and pll control registers. Since linux is_enabled method for PLLs still uses clock get state API get the PLL state, in the implementation of pm_clock_getstate() we need to workaround this by distinguishing two cases: 1) if the given clock ID corresponds to a PLL output clock ID; or 2) given clock ID is truly an on-chip clock whose state of the gate should be returned. For case 1) we'll call pm_api_clock_pll_getstate() implemented in pm_api_clock.h/c. This function will query the PLL state from PMU using the system-level PLL get mode EEMI API. For case 2) we'll call the PMU to query the clock gate state using system-level clock get status EEMI API. Functions that appear to be unused after this change is made are removed. Signed-off-by: Mirela Simonovic Acked-by: Will Wong Signed-off-by: Jolly Shah --- plat/xilinx/zynqmp/pm_service/pm_api_clock.c | 99 ++++---------------- plat/xilinx/zynqmp/pm_service/pm_api_clock.h | 5 +- plat/xilinx/zynqmp/pm_service/pm_api_sys.c | 18 +++- 3 files changed, 40 insertions(+), 82 deletions(-) diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_clock.c b/plat/xilinx/zynqmp/pm_service/pm_api_clock.c index 542dbdcd6..536889ce1 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_api_clock.c +++ b/plat/xilinx/zynqmp/pm_service/pm_api_clock.c @@ -2665,94 +2665,35 @@ enum pm_ret_status pm_clock_pll_disable(struct pm_pll *pll) } /** - * pm_api_get_pll_state() - Get state of PLL - * @clock_id Id of the PLL - * @state State of PLL(1: Enable, 0: Reset) + * pm_clock_pll_get_state - Get state of the PLL + * @pll Pointer to the target PLL structure + * @state Location to store the state: 1/0 ("Enabled"/"Disabled") * - * This function is to check state of PLL. + * "Enable" actually means that the PLL is locked and its bypass is deasserted, + * "Disable" means that it is bypassed. + * + * Return: PM_RET_ERROR_ARGS error if the argument is not valid, success if + * returned state value is valid or an error if returned by PMU */ -static inline enum pm_ret_status pm_api_get_pll_state(unsigned int clock_id, - unsigned int *state) +enum pm_ret_status pm_clock_pll_get_state(struct pm_pll *pll, + unsigned int *state) { - enum pm_ret_status ret = PM_RET_SUCCESS; - unsigned int reg, val; + enum pm_ret_status status; + enum pm_pll_mode mode; - reg = clocks[clock_id].control_reg; - - ret = pm_mmio_read(reg, &val); - - /* state: - * 1 - PLL is enabled - * 0 - PLL is in reset state - */ - *state = !(val & PLLCTRL_RESET_MASK); - return ret; -} - -/** - * pm_api_get_clk_state() - Get the state of clock for given id - * @clock_id: Id of the clock to be enabled - * @state: Enable(1)/Disable(0) - * - * This function is to get state of the clock which is not PLL. - * - * Return: Returns status, either success or error+reason. - */ -static enum pm_ret_status pm_api_get_clk_state(unsigned int clock_id, - unsigned int *state) -{ - enum pm_ret_status ret = PM_RET_SUCCESS; - struct pm_clock_node *nodes = *clocks[clock_id].nodes; - uint8_t num_nodes = clocks[clock_id].num_nodes; - unsigned int reg, val; - uint8_t i = 0; - uint8_t offset = NA_SHIFT, width = NA_WIDTH; - - reg = clocks[clock_id].control_reg; - - for (i = 0; i < num_nodes; i++) { - if (nodes->type == TYPE_GATE) { - offset = nodes->offset; - width = nodes->width; - } - nodes++; - } - if (width == NA_WIDTH) - return PM_RET_ERROR_NOTSUPPORTED; - - ret = pm_mmio_read(reg, &val); - *state = (val & BIT_MASK(offset, width)) >> offset; - - return ret; -} - -/** - * pm_api_clock_getstate - Get the clock state for given id - * @clock_id Id of the clock to be queried - * @state 1/0 (Enabled/Disabled) - * - * This function is used by master to get the state of clock - * including peripherals and PLL clocks. - * - * Return: Returns status, either success or error+reason. - */ -enum pm_ret_status pm_api_clock_getstate(unsigned int clock_id, - unsigned int *state) -{ - enum pm_ret_status ret = PM_RET_SUCCESS; - - if (!pm_clock_valid(clock_id)) + if (!pll || !state) return PM_RET_ERROR_ARGS; - if (pm_clock_type(clock_id) != CLK_TYPE_OUTPUT) - return PM_RET_ERROR_NOTSUPPORTED; + status = pm_pll_get_mode(pll->nid, &mode); + if (status != PM_RET_SUCCESS) + return status; - if (ISPLL(clock_id)) - ret = pm_api_get_pll_state(clock_id, state); + if (mode == PM_PLL_MODE_RESET) + *state = 0; else - ret = pm_api_get_clk_state(clock_id, state); + *state = 1; - return ret; + return PM_RET_SUCCESS; } static enum pm_ret_status pm_api_clk_set_divider(unsigned int clock_id, diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_clock.h b/plat/xilinx/zynqmp/pm_service/pm_api_clock.h index 5ec4d7496..dfdaf895f 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_api_clock.h +++ b/plat/xilinx/zynqmp/pm_service/pm_api_clock.h @@ -297,8 +297,9 @@ enum pm_ret_status pm_clock_id_is_valid(unsigned int clock_id); enum pm_ret_status pm_clock_pll_enable(struct pm_pll *pll); enum pm_ret_status pm_clock_pll_disable(struct pm_pll *pll); -enum pm_ret_status pm_api_clock_getstate(unsigned int clock_id, - unsigned int *state); +enum pm_ret_status pm_clock_pll_get_state(struct pm_pll *pll, + unsigned int *state); + enum pm_ret_status pm_api_clock_setdivider(unsigned int clock_id, unsigned int divider); enum pm_ret_status pm_api_clock_getdivider(unsigned int clock_id, diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_sys.c b/plat/xilinx/zynqmp/pm_service/pm_api_sys.c index fe5828edd..49471dcac 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_api_sys.c +++ b/plat/xilinx/zynqmp/pm_service/pm_api_sys.c @@ -932,7 +932,23 @@ enum pm_ret_status pm_clock_disable(unsigned int clock_id) enum pm_ret_status pm_clock_getstate(unsigned int clock_id, unsigned int *state) { - return pm_api_clock_getstate(clock_id, state); + struct pm_pll *pll; + uint32_t payload[PAYLOAD_ARG_CNT]; + enum pm_ret_status status; + + /* First try to handle it as a PLL */ + pll = pm_clock_get_pll(clock_id); + if (pll) + return pm_clock_pll_get_state(pll, state); + + /* Check if clock ID is a valid on-chip clock */ + status = pm_clock_id_is_valid(clock_id); + if (status != PM_RET_SUCCESS) + return status; + + /* Send request to the PMU */ + PM_PACK_PAYLOAD2(payload, PM_CLOCK_GETSTATE, clock_id); + return pm_ipi_send_sync(primary_proc, payload, state, 1); } /** From 48dc44e3837ff4663d3c84afbc1837eb05c88553 Mon Sep 17 00:00:00 2001 From: Jolly Shah Date: Fri, 4 Jan 2019 11:49:46 -0800 Subject: [PATCH 12/20] zynqmp: pm: Reimplement clock set divider EEMI API Clock set divider EEMI API is reimplemented to use system-level clock set divider EEMI API rather than direct MMIO read/write accesses to clock control registers. Signed-off-by: Mirela Simonovic Acked-by: Will Wong Signed-off-by: Jolly Shah --- plat/xilinx/zynqmp/pm_service/pm_api_clock.c | 157 ------------------- plat/xilinx/zynqmp/pm_service/pm_api_clock.h | 3 - plat/xilinx/zynqmp/pm_service/pm_api_sys.c | 32 +++- plat/xilinx/zynqmp/pm_service/pm_defs.h | 9 ++ 4 files changed, 40 insertions(+), 161 deletions(-) diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_clock.c b/plat/xilinx/zynqmp/pm_service/pm_api_clock.c index 536889ce1..de849ee17 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_api_clock.c +++ b/plat/xilinx/zynqmp/pm_service/pm_api_clock.c @@ -2558,74 +2558,6 @@ enum pm_ret_status pm_clock_get_pll_node_id(enum clock_id clock_id, return PM_RET_ERROR_ARGS; } -/** - * pll_get_lockbit() - Returns lockbit index for pll id - * @pll_id: Id of the pll - * - * This function return the PLL_LOCKED bit index in - * pll status register accosiated with given pll id. - * - * Return: Returns bit index - */ -static int pll_get_lockbit(unsigned int pll_id) -{ - switch (pll_id) { - case CLK_APLL_INT: - case CLK_IOPLL_INT: - return 0; - case CLK_DPLL_INT: - case CLK_RPLL_INT: - return 1; - case CLK_VPLL_INT: - return 2; - default: - return -1; - } -} - -/** - * pm_api_pll_bypass_and_reset() - Bypass and reset PLL - * @clock_id: Id of the PLL - * - * This function is to bypass and reset PLL. - */ -static inline enum pm_ret_status -pm_api_pll_bypass_and_reset(unsigned int clock_id, unsigned int flag) -{ - enum pm_ret_status ret = PM_RET_SUCCESS; - unsigned int reg, val; - int lockbit; - - reg = clocks[clock_id].control_reg; - - if (flag & CLK_PLL_RESET_ASSERT) { - ret = pm_mmio_write(reg, PLLCTRL_BP_MASK, PLLCTRL_BP_MASK); - if (ret != PM_RET_SUCCESS) - return ret; - ret = pm_mmio_write(reg, PLLCTRL_RESET_MASK, - PLLCTRL_RESET_MASK); - if (ret != PM_RET_SUCCESS) - return ret; - } - if (flag & CLK_PLL_RESET_RELEASE) { - ret = pm_mmio_write(reg, PLLCTRL_RESET_MASK, - ~PLLCTRL_RESET_MASK); - if (ret != PM_RET_SUCCESS) - return ret; - - lockbit = pll_get_lockbit(clock_id); - do { - ret = pm_mmio_read(clocks[clock_id].status_reg, &val); - if (ret != PM_RET_SUCCESS) - return ret; - } while ((lockbit >= 0) && !(val & (1 << lockbit))); - - ret = pm_mmio_write(reg, PLLCTRL_BP_MASK, - ~(unsigned int)PLLCTRL_BP_MASK); - } - return ret; -} - /** * pm_clock_pll_enable() - "Enable" the PLL clock (lock the PLL) * @pll: PLL to be locked @@ -2696,95 +2628,6 @@ enum pm_ret_status pm_clock_pll_get_state(struct pm_pll *pll, return PM_RET_SUCCESS; } -static enum pm_ret_status pm_api_clk_set_divider(unsigned int clock_id, - uint32_t divider) -{ - enum pm_ret_status ret = PM_RET_SUCCESS; - struct pm_clock_node *nodes; - uint8_t num_nodes; - uint16_t div1, div2; - unsigned int reg, mask = 0, val = 0, i; - uint8_t div1_width = NA_WIDTH, div1_offset = NA_SHIFT; - uint8_t div2_width = NA_WIDTH, div2_offset = NA_SHIFT; - - div1 = (uint16_t)(divider & 0xFFFFU); - div2 = (uint16_t)((divider >> 16) & 0xFFFFU); - - reg = clocks[clock_id].control_reg; - - nodes = *clocks[clock_id].nodes; - num_nodes = clocks[clock_id].num_nodes; - for (i = 0; i < num_nodes; i++) { - if (nodes->type == TYPE_DIV1) { - div1_offset = nodes->offset; - div1_width = nodes->width; - } - if (nodes->type == TYPE_DIV2) { - div2_offset = nodes->offset; - div2_width = nodes->width; - } - nodes++; - } - - if (div1 != (uint16_t)-1) { - if (div1_width == NA_WIDTH) - return PM_RET_ERROR_NOTSUPPORTED; - val |= div1 << div1_offset; - mask |= BIT_MASK(div1_offset, div1_width); - } - if (div2 != (uint16_t)-1) { - if (div2_width == NA_WIDTH) - return PM_RET_ERROR_NOTSUPPORTED; - val |= div2 << div2_offset; - mask |= BIT_MASK(div2_offset, div2_width); - } - ret = pm_mmio_write(reg, mask, val); - - return ret; -} - -static enum pm_ret_status pm_api_pll_set_divider(unsigned int clock_id, - unsigned int divider) -{ - unsigned int reg = clocks[clock_id].control_reg; - enum pm_ret_status ret; - - pm_api_pll_bypass_and_reset(clock_id, CLK_PLL_RESET_ASSERT); - ret = pm_mmio_write(reg, PLL_FBDIV_MASK, divider << PLL_FBDIV_SHIFT); - pm_api_pll_bypass_and_reset(clock_id, CLK_PLL_RESET_RELEASE); - - return ret; -} - -/** - * pm_api_clock_setdivider - Set the clock divider for given id - * @clock_id Id of the clock - * @divider Divider value - * - * This function is used by master to set divider for any clock - * to achieve desired rate. - * - * Return: Returns status, either success or error+reason. - */ -enum pm_ret_status pm_api_clock_setdivider(unsigned int clock_id, - unsigned int divider) -{ - enum pm_ret_status ret; - - if (!pm_clock_valid(clock_id)) - return PM_RET_ERROR_ARGS; - - if (pm_clock_type(clock_id) != CLK_TYPE_OUTPUT) - return PM_RET_ERROR_NOTSUPPORTED; - - if (ISPLL(clock_id)) - ret = pm_api_pll_set_divider(clock_id, divider); - else - ret = pm_api_clk_set_divider(clock_id, divider); - - return ret; -} - static enum pm_ret_status pm_api_clk_get_divider(unsigned int clock_id, uint32_t *divider) { diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_clock.h b/plat/xilinx/zynqmp/pm_service/pm_api_clock.h index dfdaf895f..543f2ade1 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_api_clock.h +++ b/plat/xilinx/zynqmp/pm_service/pm_api_clock.h @@ -299,9 +299,6 @@ enum pm_ret_status pm_clock_pll_enable(struct pm_pll *pll); enum pm_ret_status pm_clock_pll_disable(struct pm_pll *pll); enum pm_ret_status pm_clock_pll_get_state(struct pm_pll *pll, unsigned int *state); - -enum pm_ret_status pm_api_clock_setdivider(unsigned int clock_id, - unsigned int divider); enum pm_ret_status pm_api_clock_getdivider(unsigned int clock_id, unsigned int *divider); enum pm_ret_status pm_api_clock_setrate(unsigned int clock_id, diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_sys.c b/plat/xilinx/zynqmp/pm_service/pm_api_sys.c index 49471dcac..91a146168 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_api_sys.c +++ b/plat/xilinx/zynqmp/pm_service/pm_api_sys.c @@ -964,7 +964,37 @@ enum pm_ret_status pm_clock_getstate(unsigned int clock_id, enum pm_ret_status pm_clock_setdivider(unsigned int clock_id, unsigned int divider) { - return pm_api_clock_setdivider(clock_id, divider); + enum pm_ret_status status; + enum pm_node_id nid; + enum pm_clock_div_id div_id; + uint32_t payload[PAYLOAD_ARG_CNT]; + const uint32_t div0 = 0xFFFF0000; + const uint32_t div1 = 0x0000FFFF; + uint32_t val; + + /* Get PLL node ID using PLL clock ID */ + status = pm_clock_get_pll_node_id(clock_id, &nid); + if (status == PM_RET_SUCCESS) + return pm_pll_set_parameter(nid, PM_PLL_PARAM_FBDIV, divider); + + /* Check if clock ID is a valid on-chip clock */ + status = pm_clock_id_is_valid(clock_id); + if (status != PM_RET_SUCCESS) + return status; + + if (div0 == (divider & div0)) { + div_id = PM_CLOCK_DIV0_ID; + val = divider & ~div0; + } else if (div1 == (divider & div1)) { + div_id = PM_CLOCK_DIV1_ID; + val = (divider & ~div1) >> 16; + } else { + return PM_RET_ERROR_ARGS; + } + + /* Send request to the PMU */ + PM_PACK_PAYLOAD4(payload, PM_CLOCK_SETDIVIDER, clock_id, div_id, val); + return pm_ipi_send_sync(primary_proc, payload, NULL, 0); } /** diff --git a/plat/xilinx/zynqmp/pm_service/pm_defs.h b/plat/xilinx/zynqmp/pm_service/pm_defs.h index 7c2389385..cae36c9d8 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_defs.h +++ b/plat/xilinx/zynqmp/pm_service/pm_defs.h @@ -308,4 +308,13 @@ enum pm_pll_mode { PM_PLL_MODE_MAX, }; +/** + * @PM_CLOCK_DIV0_ID: Clock divider 0 + * @PM_CLOCK_DIV1_ID: Clock divider 1 + */ +enum pm_clock_div_id { + PM_CLOCK_DIV0_ID, + PM_CLOCK_DIV1_ID, +}; + #endif /* PM_DEFS_H */ From b071dcd9245dd26aefad019bd9eb883c9619cc23 Mon Sep 17 00:00:00 2001 From: Jolly Shah Date: Fri, 4 Jan 2019 11:53:37 -0800 Subject: [PATCH 13/20] zynqmp: pm: Reimplement clock get divider EEMI API Clock get divider EEMI API is reimplemented to use system-level clock get divider EEMI API rather than direct MMIO read/write accesses to clock control registers. Signed-off-by: Mirela Simonovic Acked-by: Will Wong Signed-off-by: Jolly Shah --- plat/xilinx/zynqmp/pm_service/pm_api_clock.c | 113 +++++-------------- plat/xilinx/zynqmp/pm_service/pm_api_clock.h | 3 +- plat/xilinx/zynqmp/pm_service/pm_api_sys.c | 37 +++++- 3 files changed, 66 insertions(+), 87 deletions(-) diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_clock.c b/plat/xilinx/zynqmp/pm_service/pm_api_clock.c index de849ee17..e8053060b 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_api_clock.c +++ b/plat/xilinx/zynqmp/pm_service/pm_api_clock.c @@ -2628,90 +2628,6 @@ enum pm_ret_status pm_clock_pll_get_state(struct pm_pll *pll, return PM_RET_SUCCESS; } -static enum pm_ret_status pm_api_clk_get_divider(unsigned int clock_id, - uint32_t *divider) -{ - enum pm_ret_status ret = PM_RET_SUCCESS; - struct pm_clock_node *nodes; - uint8_t num_nodes; - unsigned int reg, val, i, div1 = 0, div2 = 0; - uint8_t div1_width = NA_WIDTH, div1_offset = NA_SHIFT; - uint8_t div2_width = NA_WIDTH, div2_offset = NA_SHIFT; - - reg = clocks[clock_id].control_reg; - - nodes = *clocks[clock_id].nodes; - num_nodes = clocks[clock_id].num_nodes; - for (i = 0; i < num_nodes; i++) { - if (nodes->type == TYPE_DIV1) { - div1_offset = nodes->offset; - div1_width = nodes->width; - } - if (nodes->type == TYPE_DIV2) { - div2_offset = nodes->offset; - div2_width = nodes->width; - } - nodes++; - } - - ret = pm_mmio_read(reg, &val); - - if (div1_width == NA_WIDTH) - return PM_RET_ERROR_ARGS; - - div1 = (val & BIT_MASK(div1_offset, div1_width)) >> div1_offset; - - if (div2_width != NA_WIDTH) - div2 = (val & BIT_MASK(div2_offset, div2_width)) >> div2_offset; - - *divider = div1 | (div2 << 16); - - return ret; -} - -static enum pm_ret_status pm_api_pll_get_divider(unsigned int clock_id, - unsigned int *divider) -{ - enum pm_ret_status ret = PM_RET_SUCCESS; - unsigned int reg, val; - - reg = clocks[clock_id].control_reg; - - ret = pm_mmio_read(reg, &val); - *divider = (val & PLL_FBDIV_MASK) >> PLL_FBDIV_SHIFT; - - return ret; -} - -/** - * pm_api_clock_getdivider - Get the clock divider for given id - * @clock_id Id of the clock - * @divider Divider value - * - * This function is used by master to get divider values - * for any clock. - * - * Return: Returns status, either success or error+reason. - */ -enum pm_ret_status pm_api_clock_getdivider(unsigned int clock_id, - unsigned int *divider) -{ - enum pm_ret_status ret; - - if (!pm_clock_valid(clock_id)) - return PM_RET_ERROR_ARGS; - - if (pm_clock_type(clock_id) != CLK_TYPE_OUTPUT) - return PM_RET_ERROR_NOTSUPPORTED; - - if (ISPLL(clock_id)) - ret = pm_api_pll_get_divider(clock_id, divider); - else - ret = pm_api_clk_get_divider(clock_id, divider); - - return ret; -} - /** * pm_api_clock_setrate - Set the clock rate for given id * @clock_id Id of the clock @@ -2900,3 +2816,32 @@ enum pm_ret_status pm_clock_id_is_valid(unsigned int clock_id) return PM_RET_SUCCESS; } + +/** + * pm_clock_has_div() - Check if the clock has divider with given ID + * @clock_id Clock ID + * @div_id Divider ID + * + * @return True(1)=clock has the divider, false(0)=otherwise + */ +uint8_t pm_clock_has_div(unsigned int clock_id, enum pm_clock_div_id div_id) +{ + uint32_t i; + struct pm_clock_node *nodes; + + if (clock_id >= CLK_MAX_OUTPUT_CLK) + return 0; + + nodes = *clocks[clock_id].nodes; + for (i = 0; i < clocks[clock_id].num_nodes; i++) { + if (nodes[i].type == TYPE_DIV1) { + if (div_id == PM_CLOCK_DIV0_ID) + return 1; + } else if (nodes[i].type == TYPE_DIV2) { + if (div_id == PM_CLOCK_DIV1_ID) + return 1; + } + } + + return 0; +} diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_clock.h b/plat/xilinx/zynqmp/pm_service/pm_api_clock.h index 543f2ade1..419690a14 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_api_clock.h +++ b/plat/xilinx/zynqmp/pm_service/pm_api_clock.h @@ -276,6 +276,7 @@ enum { struct pm_pll; struct pm_pll *pm_clock_get_pll(enum clock_id clock_id); +uint8_t pm_clock_has_div(unsigned int clock_id, enum pm_clock_div_id div_id); enum pm_ret_status pm_api_clock_get_name(unsigned int clock_id, char *name); enum pm_ret_status pm_api_clock_get_num_clocks(unsigned int *nclocks); @@ -299,8 +300,6 @@ enum pm_ret_status pm_clock_pll_enable(struct pm_pll *pll); enum pm_ret_status pm_clock_pll_disable(struct pm_pll *pll); enum pm_ret_status pm_clock_pll_get_state(struct pm_pll *pll, unsigned int *state); -enum pm_ret_status pm_api_clock_getdivider(unsigned int clock_id, - unsigned int *divider); enum pm_ret_status pm_api_clock_setrate(unsigned int clock_id, uint64_t rate); enum pm_ret_status pm_api_clock_getrate(unsigned int clock_id, diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_sys.c b/plat/xilinx/zynqmp/pm_service/pm_api_sys.c index 91a146168..402b7c688 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_api_sys.c +++ b/plat/xilinx/zynqmp/pm_service/pm_api_sys.c @@ -1010,7 +1010,42 @@ enum pm_ret_status pm_clock_setdivider(unsigned int clock_id, enum pm_ret_status pm_clock_getdivider(unsigned int clock_id, unsigned int *divider) { - return pm_api_clock_getdivider(clock_id, divider); + enum pm_ret_status status; + enum pm_node_id nid; + uint32_t payload[PAYLOAD_ARG_CNT]; + uint32_t val; + + /* Get PLL node ID using PLL clock ID */ + status = pm_clock_get_pll_node_id(clock_id, &nid); + if (status == PM_RET_SUCCESS) + return pm_pll_get_parameter(nid, PM_PLL_PARAM_FBDIV, divider); + + /* Check if clock ID is a valid on-chip clock */ + status = pm_clock_id_is_valid(clock_id); + if (status != PM_RET_SUCCESS) + return status; + + if (pm_clock_has_div(clock_id, PM_CLOCK_DIV0_ID)) { + /* Send request to the PMU to get div0 */ + PM_PACK_PAYLOAD3(payload, PM_CLOCK_GETDIVIDER, clock_id, + PM_CLOCK_DIV0_ID); + status = pm_ipi_send_sync(primary_proc, payload, &val, 1); + if (status != PM_RET_SUCCESS) + return status; + *divider = val; + } + + if (pm_clock_has_div(clock_id, PM_CLOCK_DIV1_ID)) { + /* Send request to the PMU to get div1 */ + PM_PACK_PAYLOAD3(payload, PM_CLOCK_GETDIVIDER, clock_id, + PM_CLOCK_DIV1_ID); + status = pm_ipi_send_sync(primary_proc, payload, &val, 1); + if (status != PM_RET_SUCCESS) + return status; + *divider |= val << 16; + } + + return status; } /** From 8bc945fbcc575b5ee52c15f0478cd66e2c84c10b Mon Sep 17 00:00:00 2001 From: Jolly Shah Date: Wed, 2 Jan 2019 13:39:30 -0800 Subject: [PATCH 14/20] zynqmp: pm: Cleanup for clock set/get rate EEMI API Clock set/get rate are not implemented and will likely never be. Remove empty function stubs. Signed-off-by: Mirela Simonovic Acked-by: Will Wong Signed-off-by: Jolly Shah --- plat/xilinx/zynqmp/pm_service/pm_api_clock.c | 31 -------------------- plat/xilinx/zynqmp/pm_service/pm_api_clock.h | 4 --- plat/xilinx/zynqmp/pm_service/pm_api_sys.c | 4 +-- 3 files changed, 2 insertions(+), 37 deletions(-) diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_clock.c b/plat/xilinx/zynqmp/pm_service/pm_api_clock.c index e8053060b..2e07779d3 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_api_clock.c +++ b/plat/xilinx/zynqmp/pm_service/pm_api_clock.c @@ -2628,37 +2628,6 @@ enum pm_ret_status pm_clock_pll_get_state(struct pm_pll *pll, return PM_RET_SUCCESS; } -/** - * pm_api_clock_setrate - Set the clock rate for given id - * @clock_id Id of the clock - * @rate Rate value in hz - * - * This function is used by master to set rate for any clock. - * - * Return: Returns status, either success or error+reason. - */ -enum pm_ret_status pm_api_clock_setrate(unsigned int clock_id, - uint64_t rate) -{ - return PM_RET_ERROR_NOTSUPPORTED; -} - -/** - * pm_api_clock_getrate - Get the clock rate for given id - * @clock_id Id of the clock - * @rate rate value in hz - * - * This function is used by master to get rate - * for any clock. - * - * Return: Returns status, either success or error+reason. - */ -enum pm_ret_status pm_api_clock_getrate(unsigned int clock_id, - uint64_t *rate) -{ - return PM_RET_ERROR_NOTSUPPORTED; -} - /** * pm_api_clock_setparent - Set the clock parent for given id * @clock_id Id of the clock diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_clock.h b/plat/xilinx/zynqmp/pm_service/pm_api_clock.h index 419690a14..c599f59fe 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_api_clock.h +++ b/plat/xilinx/zynqmp/pm_service/pm_api_clock.h @@ -300,10 +300,6 @@ enum pm_ret_status pm_clock_pll_enable(struct pm_pll *pll); enum pm_ret_status pm_clock_pll_disable(struct pm_pll *pll); enum pm_ret_status pm_clock_pll_get_state(struct pm_pll *pll, unsigned int *state); -enum pm_ret_status pm_api_clock_setrate(unsigned int clock_id, - uint64_t rate); -enum pm_ret_status pm_api_clock_getrate(unsigned int clock_id, - uint64_t *rate); enum pm_ret_status pm_api_clock_setparent(unsigned int clock_id, unsigned int parent_idx); enum pm_ret_status pm_api_clock_getparent(unsigned int clock_id, diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_sys.c b/plat/xilinx/zynqmp/pm_service/pm_api_sys.c index 402b7c688..eb54bcb71 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_api_sys.c +++ b/plat/xilinx/zynqmp/pm_service/pm_api_sys.c @@ -1060,7 +1060,7 @@ enum pm_ret_status pm_clock_getdivider(unsigned int clock_id, enum pm_ret_status pm_clock_setrate(unsigned int clock_id, uint64_t rate) { - return pm_api_clock_setrate(clock_id, rate); + return PM_RET_ERROR_NOTSUPPORTED; } /** @@ -1076,7 +1076,7 @@ enum pm_ret_status pm_clock_setrate(unsigned int clock_id, enum pm_ret_status pm_clock_getrate(unsigned int clock_id, uint64_t *rate) { - return pm_api_clock_getrate(clock_id, rate); + return PM_RET_ERROR_NOTSUPPORTED; } /** From be48511e79be546b112bca664fa466c808ee07c3 Mon Sep 17 00:00:00 2001 From: Jolly Shah Date: Fri, 4 Jan 2019 11:57:40 -0800 Subject: [PATCH 15/20] zynqmp: pm: Reimplement clock set parent EEMI API Clock set parent EEMI API is reimplemented to use system-level clock and pll EEMI APIs rather than direct MMIO read/write accesses to clock and pll control registers. Since linux still uses clock set parent API to set pre_src, post_src, div2 and bypass, in the implementation of pm_clock_set_parent() we need to workaround this by distinguishing two cases: 1) if the given clock ID corresponds to a PLL-related clock ID (*_PRE_SRC, *_POST_SRC, *_INT_MUX or *PLL clock IDs); or 2) given clock ID is truly an on-chip clock. For case 1) we'll map the call onto PLL set parameter EEMI API with the respective parameter ID. Since clock set parent interface to EL1/2 receives parent index (mux select value), the value is just passed to PMU. Functions that appear to be unused after this change is made are removed. Setting the parent of *PLL clocks, that actually model bypass, is not possible. This is already ensured by the existing clock model having the CLK_SET_RATE_NO_REPARENT flag. The API also doesn't allow changing the bypass parent. Bypass is controlled only by the PMU firmware. Signed-off-by: Mirela Simonovic Acked-by: Will Wong Signed-off-by: Jolly Shah --- plat/xilinx/zynqmp/pm_service/pm_api_clock.c | 112 ++++++++++++------- plat/xilinx/zynqmp/pm_service/pm_api_clock.h | 6 +- plat/xilinx/zynqmp/pm_service/pm_api_sys.c | 22 +++- 3 files changed, 94 insertions(+), 46 deletions(-) diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_clock.c b/plat/xilinx/zynqmp/pm_service/pm_api_clock.c index 2e07779d3..ca2f452b2 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_api_clock.c +++ b/plat/xilinx/zynqmp/pm_service/pm_api_clock.c @@ -2493,11 +2493,19 @@ enum pm_ret_status pm_api_clock_get_attributes(unsigned int clock_id, * implemented by linux to system-level EEMI APIs * @nid: PLL node ID * @cid: PLL clock ID + * @pre_src: Pre-source PLL clock ID + * @post_src: Post-source PLL clock ID + * @div2: DIV2 PLL clock ID + * @bypass: PLL output clock ID that maps to bypass select output * @mode: PLL mode currently set via IOCTL (PLL_FRAC_MODE/PLL_INT_MODE) */ struct pm_pll { const enum pm_node_id nid; const enum clock_id cid; + const enum clock_id pre_src; + const enum clock_id post_src; + const enum clock_id div2; + const enum clock_id bypass; uint8_t mode; }; @@ -2505,18 +2513,38 @@ static struct pm_pll pm_plls[] = { { .nid = NODE_IOPLL, .cid = CLK_IOPLL_INT, + .pre_src = CLK_IOPLL_PRE_SRC, + .post_src = CLK_IOPLL_POST_SRC, + .div2 = CLK_IOPLL_INT_MUX, + .bypass = CLK_IOPLL, }, { .nid = NODE_RPLL, .cid = CLK_RPLL_INT, + .pre_src = CLK_RPLL_PRE_SRC, + .post_src = CLK_RPLL_POST_SRC, + .div2 = CLK_RPLL_INT_MUX, + .bypass = CLK_RPLL, }, { .nid = NODE_APLL, .cid = CLK_APLL_INT, + .pre_src = CLK_APLL_PRE_SRC, + .post_src = CLK_APLL_POST_SRC, + .div2 = CLK_APLL_INT_MUX, + .bypass = CLK_APLL, }, { .nid = NODE_VPLL, .cid = CLK_VPLL_INT, + .pre_src = CLK_VPLL_PRE_SRC, + .post_src = CLK_VPLL_POST_SRC, + .div2 = CLK_VPLL_INT_MUX, + .bypass = CLK_VPLL, }, { .nid = NODE_DPLL, .cid = CLK_DPLL_INT, + .pre_src = CLK_DPLL_PRE_SRC, + .post_src = CLK_DPLL_POST_SRC, + .div2 = CLK_DPLL_INT_MUX, + .bypass = CLK_DPLL, }, }; @@ -2558,6 +2586,28 @@ enum pm_ret_status pm_clock_get_pll_node_id(enum clock_id clock_id, return PM_RET_ERROR_ARGS; } +/** + * pm_clock_get_pll_by_related_clk() - Get PLL structure by PLL-related clock ID + * @clock_id Clock ID + * + * @return Pointer to PLL structure if found, NULL otherwise + */ +struct pm_pll *pm_clock_get_pll_by_related_clk(enum clock_id clock_id) +{ + uint32_t i; + + for (i = 0; i < ARRAY_SIZE(pm_plls); i++) { + if (pm_plls[i].pre_src == clock_id || + pm_plls[i].post_src == clock_id || + pm_plls[i].div2 == clock_id || + pm_plls[i].bypass == clock_id) { + return &pm_plls[i]; + } + } + + return NULL; +} + /** * pm_clock_pll_enable() - "Enable" the PLL clock (lock the PLL) * @pll: PLL to be locked @@ -2629,54 +2679,34 @@ enum pm_ret_status pm_clock_pll_get_state(struct pm_pll *pll, } /** - * pm_api_clock_setparent - Set the clock parent for given id - * @clock_id Id of the clock - * @parent_idx parent index + * pm_clock_pll_set_parent - Set the clock parent for PLL-related clock id + * @pll Target PLL structure + * @clock_id Id of the clock + * @parent_index parent index (=mux select value) * - * This function is used by master to set parent for any clock. + * The whole clock-tree implementation relies on the fact that parent indexes + * match to the multiplexer select values. This function has to rely on that + * assumption as well => parent_index is actually the mux select value. * * Return: Returns status, either success or error+reason. */ -enum pm_ret_status pm_api_clock_setparent(unsigned int clock_id, - unsigned int parent_idx) +enum pm_ret_status pm_clock_pll_set_parent(struct pm_pll *pll, + enum clock_id clock_id, + unsigned int parent_index) { - enum pm_ret_status ret = PM_RET_SUCCESS; - struct pm_clock_node *nodes; - uint8_t num_nodes; - unsigned int reg, val; - int32_t *clk_parents; - unsigned int i = 0; - uint8_t offset = NA_SHIFT, width = NA_WIDTH; - - if (!pm_clock_valid(clock_id)) + if (!pll) return PM_RET_ERROR_ARGS; + if (pll->pre_src == clock_id) + return pm_pll_set_parameter(pll->nid, PM_PLL_PARAM_PRE_SRC, + parent_index); + if (pll->post_src == clock_id) + return pm_pll_set_parameter(pll->nid, PM_PLL_PARAM_POST_SRC, + parent_index); + if (pll->div2 == clock_id) + return pm_pll_set_parameter(pll->nid, PM_PLL_PARAM_DIV2, + parent_index); - if (pm_clock_type(clock_id) != CLK_TYPE_OUTPUT) - return PM_RET_ERROR_NOTSUPPORTED; - - clk_parents = *clocks[clock_id].parents; - - for (i = 0; i <= parent_idx; i++) - if (clk_parents[i] == CLK_NA_PARENT) - return PM_RET_ERROR_ARGS; - - nodes = *clocks[clock_id].nodes; - num_nodes = clocks[clock_id].num_nodes; - for (i = 0; i < num_nodes; i++) { - if (nodes->type == TYPE_MUX) { - offset = nodes->offset; - width = nodes->width; - } - nodes++; - } - if (width == NA_WIDTH) - return PM_RET_ERROR_NOTSUPPORTED; - - reg = clocks[clock_id].control_reg; - val = parent_idx << offset; - ret = pm_mmio_write(reg, BIT_MASK(offset, width), val); - - return ret; + return PM_RET_ERROR_ARGS; } /** diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_clock.h b/plat/xilinx/zynqmp/pm_service/pm_api_clock.h index c599f59fe..c406bbdfc 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_api_clock.h +++ b/plat/xilinx/zynqmp/pm_service/pm_api_clock.h @@ -276,6 +276,7 @@ enum { struct pm_pll; struct pm_pll *pm_clock_get_pll(enum clock_id clock_id); +struct pm_pll *pm_clock_get_pll_by_related_clk(enum clock_id clock_id); uint8_t pm_clock_has_div(unsigned int clock_id, enum pm_clock_div_id div_id); enum pm_ret_status pm_api_clock_get_name(unsigned int clock_id, char *name); @@ -300,8 +301,9 @@ enum pm_ret_status pm_clock_pll_enable(struct pm_pll *pll); enum pm_ret_status pm_clock_pll_disable(struct pm_pll *pll); enum pm_ret_status pm_clock_pll_get_state(struct pm_pll *pll, unsigned int *state); -enum pm_ret_status pm_api_clock_setparent(unsigned int clock_id, - unsigned int parent_idx); +enum pm_ret_status pm_clock_pll_set_parent(struct pm_pll *pll, + enum clock_id clock_id, + unsigned int parent_index); enum pm_ret_status pm_api_clock_getparent(unsigned int clock_id, unsigned int *parent_idx); enum pm_ret_status pm_clock_set_pll_mode(enum clock_id clock_id, diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_sys.c b/plat/xilinx/zynqmp/pm_service/pm_api_sys.c index eb54bcb71..3c10bdb9f 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_api_sys.c +++ b/plat/xilinx/zynqmp/pm_service/pm_api_sys.c @@ -1082,16 +1082,32 @@ enum pm_ret_status pm_clock_getrate(unsigned int clock_id, /** * pm_clock_setparent - Set the clock parent for given id * @clock_id: Id of the clock - * @parent_id: parent id + * @parent_index: Index of the parent clock into clock's parents array * * This function is used by master to set parent for any clock. * * Return: Returns status, either success or error+reason. */ enum pm_ret_status pm_clock_setparent(unsigned int clock_id, - unsigned int parent_id) + unsigned int parent_index) { - return pm_api_clock_setparent(clock_id, parent_id); + struct pm_pll *pll; + uint32_t payload[PAYLOAD_ARG_CNT]; + enum pm_ret_status status; + + /* First try to handle it as a PLL */ + pll = pm_clock_get_pll_by_related_clk(clock_id); + if (pll) + return pm_clock_pll_set_parent(pll, clock_id, parent_index); + + /* Check if clock ID is a valid on-chip clock */ + status = pm_clock_id_is_valid(clock_id); + if (status != PM_RET_SUCCESS) + return status; + + /* Send request to the PMU */ + PM_PACK_PAYLOAD3(payload, PM_CLOCK_SETPARENT, clock_id, parent_index); + return pm_ipi_send_sync(primary_proc, payload, NULL, 0); } /** From b6c56bdb0cc4e22dfad5b985bb7182207982df60 Mon Sep 17 00:00:00 2001 From: Jolly Shah Date: Wed, 2 Jan 2019 13:44:25 -0800 Subject: [PATCH 16/20] zynqmp: pm: Reimplement clock get parent EEMI API Clock get parent EEMI API is reimplemented to use system-level clock and pll EEMI APIs rather than direct MMIO read/write accesses to clock and pll control registers. Since linux still uses clock set parent API to get pre_src, post_src, div2 and bypasss, in the implementation of pm_clock_get_parent() we need to workaround this by distinguishing two cases: 1) if the given clock ID corresponds to a PLL-related clock ID (*_PRE_SRC, *_POST_SRC, *_INT_MUX or *_PLL clock IDs); or 2) given clock ID is truly an on-chip clock. For case 1) we'll map the call onto PLL-specific EEMI API with the respective parameter ID. For case 2) the call is passed to the PMU. Signed-off-by: Mirela Simonovic Acked-by: Will Wong Signed-off-by: Jolly Shah --- plat/xilinx/zynqmp/pm_service/pm_api_clock.c | 59 ++++++++------------ plat/xilinx/zynqmp/pm_service/pm_api_clock.h | 5 +- plat/xilinx/zynqmp/pm_service/pm_api_sys.c | 22 +++++++- 3 files changed, 44 insertions(+), 42 deletions(-) diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_clock.c b/plat/xilinx/zynqmp/pm_service/pm_api_clock.c index ca2f452b2..8c1579280 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_api_clock.c +++ b/plat/xilinx/zynqmp/pm_service/pm_api_clock.c @@ -2710,51 +2710,36 @@ enum pm_ret_status pm_clock_pll_set_parent(struct pm_pll *pll, } /** - * pm_api_clock_getparent - Get the clock parent for given id - * @clock_id Id of the clock - * @parent_idx parent index + * pm_clock_pll_get_parent - Get mux select value of PLL-related clock parent + * @pll Target PLL structure + * @clock_id Id of the clock + * @parent_index parent index (=mux select value) * - * This function is used by master to get parent index - * for any clock. + * This function is used by master to get parent index for PLL-related clock. * * Return: Returns status, either success or error+reason. */ -enum pm_ret_status pm_api_clock_getparent(unsigned int clock_id, - unsigned int *parent_idx) +enum pm_ret_status pm_clock_pll_get_parent(struct pm_pll *pll, + enum clock_id clock_id, + unsigned int *parent_index) { - enum pm_ret_status ret = PM_RET_SUCCESS; - struct pm_clock_node *nodes; - uint8_t num_nodes; - unsigned int reg, val; - uint8_t i = 0, offset = NA_SHIFT, width = NA_WIDTH; - - if (!pm_clock_valid(clock_id)) + if (!pll) return PM_RET_ERROR_ARGS; - - if (pm_clock_type(clock_id) != CLK_TYPE_OUTPUT) - return PM_RET_ERROR_NOTSUPPORTED; - - nodes = *clocks[clock_id].nodes; - num_nodes = clocks[clock_id].num_nodes; - - for (i = 0; i < num_nodes; i++) { - if (nodes->type == TYPE_MUX) { - offset = nodes->offset; - width = nodes->width; - } - nodes++; + if (pll->pre_src == clock_id) + return pm_pll_get_parameter(pll->nid, PM_PLL_PARAM_PRE_SRC, + parent_index); + if (pll->post_src == clock_id) + return pm_pll_get_parameter(pll->nid, PM_PLL_PARAM_POST_SRC, + parent_index); + if (pll->div2 == clock_id) + return pm_pll_get_parameter(pll->nid, PM_PLL_PARAM_DIV2, + parent_index); + if (pll->bypass == clock_id) { + *parent_index = 0; + return PM_RET_SUCCESS; } - if (width == NA_WIDTH) - return PM_RET_ERROR_NOTSUPPORTED; - reg = clocks[clock_id].control_reg; - ret = pm_mmio_read(reg, &val); - val >>= offset; - val &= ((1U << width) - 1); - - *parent_idx = val; - - return ret; + return PM_RET_ERROR_ARGS; } /** diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_clock.h b/plat/xilinx/zynqmp/pm_service/pm_api_clock.h index c406bbdfc..671c29fed 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_api_clock.h +++ b/plat/xilinx/zynqmp/pm_service/pm_api_clock.h @@ -304,8 +304,9 @@ enum pm_ret_status pm_clock_pll_get_state(struct pm_pll *pll, enum pm_ret_status pm_clock_pll_set_parent(struct pm_pll *pll, enum clock_id clock_id, unsigned int parent_index); -enum pm_ret_status pm_api_clock_getparent(unsigned int clock_id, - unsigned int *parent_idx); +enum pm_ret_status pm_clock_pll_get_parent(struct pm_pll *pll, + enum clock_id clock_id, + unsigned int *parent_index); enum pm_ret_status pm_clock_set_pll_mode(enum clock_id clock_id, unsigned int mode); enum pm_ret_status pm_clock_get_pll_mode(enum clock_id clock_id, diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_sys.c b/plat/xilinx/zynqmp/pm_service/pm_api_sys.c index 3c10bdb9f..daa78509b 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_api_sys.c +++ b/plat/xilinx/zynqmp/pm_service/pm_api_sys.c @@ -1113,7 +1113,7 @@ enum pm_ret_status pm_clock_setparent(unsigned int clock_id, /** * pm_clock_getparent - Get the clock parent for given id * @clock_id: Id of the clock - * @parent_id: parent id + * @parent_index: parent index * * This function is used by master to get parent index * for any clock. @@ -1121,9 +1121,25 @@ enum pm_ret_status pm_clock_setparent(unsigned int clock_id, * Return: Returns status, either success or error+reason. */ enum pm_ret_status pm_clock_getparent(unsigned int clock_id, - unsigned int *parent_id) + unsigned int *parent_index) { - return pm_api_clock_getparent(clock_id, parent_id); + struct pm_pll *pll; + uint32_t payload[PAYLOAD_ARG_CNT]; + enum pm_ret_status status; + + /* First try to handle it as a PLL */ + pll = pm_clock_get_pll_by_related_clk(clock_id); + if (pll) + return pm_clock_pll_get_parent(pll, clock_id, parent_index); + + /* Check if clock ID is a valid on-chip clock */ + status = pm_clock_id_is_valid(clock_id); + if (status != PM_RET_SUCCESS) + return status; + + /* Send request to the PMU */ + PM_PACK_PAYLOAD2(payload, PM_CLOCK_GETPARENT, clock_id); + return pm_ipi_send_sync(primary_proc, payload, parent_index, 1); } /** From 284b2f095bce33487fd6d3c3c11e77ef8d79dd3f Mon Sep 17 00:00:00 2001 From: Jolly Shah Date: Wed, 2 Jan 2019 13:45:53 -0800 Subject: [PATCH 17/20] zynqmp: pm: Fix model of ACPU clocks In the existing model for ACPU clock the mux, divider, and gate were represented as one clock and ACPU_HALF was modelled as child of ACPU clock. This is not correct. ACPU clock model contains only mux and the divider, and it has 2 children: ACPU_FULL and ACPU_HALF clocks which have only gates. The models of ACPU and ACPU_HALF clocks are fixed and ACPU_FULL clock is added. Signed-off-by: Mirela Simonovic Acked-by: Will Wong Signed-off-by: Jolly Shah --- plat/xilinx/zynqmp/pm_service/pm_api_clock.c | 37 +++++++++++++------- plat/xilinx/zynqmp/pm_service/pm_api_clock.h | 1 + 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_clock.c b/plat/xilinx/zynqmp/pm_service/pm_api_clock.c index 8c1579280..d91f4e467 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_api_clock.c +++ b/plat/xilinx/zynqmp/pm_service/pm_api_clock.c @@ -330,18 +330,6 @@ static struct pm_clock_node acpu_nodes[] = { .mult = NA_MULT, .div = NA_DIV, }, - { - .type = TYPE_GATE, - .offset = PERIPH_GATE_SHIFT, - .width = PERIPH_GATE_WIDTH, - .clkflags = CLK_SET_RATE_PARENT | - CLK_IGNORE_UNUSED | - CLK_IS_BASIC | - CLK_IS_CRITICAL, - .typeflags = NA_TYPE_FLAGS, - .mult = NA_MULT, - .div = NA_DIV, - }, }; static struct pm_clock_node generic_mux_div_nodes[] = { @@ -476,6 +464,20 @@ static struct pm_clock_node acpu_half_nodes[] = { }, }; +static struct pm_clock_node acpu_full_nodes[] = { + { + .type = TYPE_GATE, + .offset = 24, + .width = PERIPH_GATE_WIDTH, + .clkflags = CLK_IGNORE_UNUSED | + CLK_SET_RATE_PARENT | + CLK_IS_BASIC, + .typeflags = NA_TYPE_FLAGS, + .mult = NA_MULT, + .div = NA_DIV, + }, +}; + static struct pm_clock_node wdt_nodes[] = { { .type = TYPE_MUX, @@ -1205,6 +1207,17 @@ static struct pm_clock clocks[] = { .nodes = &acpu_nodes, .num_nodes = ARRAY_SIZE(acpu_nodes), }, + [CLK_ACPU_FULL] = { + .name = "acpu_full", + .control_reg = CRF_APB_ACPU_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_ACPU | PARENT_CLK_NODE2 << CLK_PARENTS_ID_LEN, + CLK_NA_PARENT + }), + .nodes = &acpu_full_nodes, + .num_nodes = ARRAY_SIZE(acpu_full_nodes), + }, [CLK_DBG_TRACE] = { .name = "dbg_trace", .control_reg = CRF_APB_DBG_TRACE_CTRL, diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_clock.h b/plat/xilinx/zynqmp/pm_service/pm_api_clock.h index 671c29fed..8d5e96788 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_api_clock.h +++ b/plat/xilinx/zynqmp/pm_service/pm_api_clock.h @@ -159,6 +159,7 @@ enum clock_id { CLK_VPLL_POST_SRC, CLK_CAN0_MIO, CLK_CAN1_MIO, + CLK_ACPU_FULL, END_OF_OUTPUT_CLKS, }; From bf8ffb38ccf2c9478c6908610f5b8b27386faba4 Mon Sep 17 00:00:00 2001 From: Jolly Shah Date: Wed, 2 Jan 2019 13:48:19 -0800 Subject: [PATCH 18/20] zynqmp: pm: Add ACPU_FULL and ACPU_HALF clocks in the invalid list These clocks are marked as invalid in order to prevent Linux from registering them. Note that despite clocks being marked as invalid a security issue still remains in place as there is nothing that prevents the non-secure world from gating these clocks and that way halt the whole APU subsystem. Signed-off-by: Mirela Simonovic Acked-by: Will Wong Signed-off-by: Jolly Shah --- plat/xilinx/zynqmp/pm_service/pm_api_clock.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_clock.c b/plat/xilinx/zynqmp/pm_service/pm_api_clock.c index d91f4e467..8901ea691 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_api_clock.c +++ b/plat/xilinx/zynqmp/pm_service/pm_api_clock.c @@ -2253,7 +2253,10 @@ static struct pm_ext_clock ext_clocks[] = { }; /* Array of clock which are invalid for this variant */ -static uint32_t pm_clk_invalid_list[] = {CLK_USB0, CLK_USB1, CLK_CSU_SPB}; +static uint32_t pm_clk_invalid_list[] = {CLK_USB0, CLK_USB1, CLK_CSU_SPB, + CLK_ACPU_FULL, + CLK_ACPU_HALF, +}; /** * pm_clock_valid - Check if clock is valid or not From 50e1b8fe79895c7f9971e432c6d29bc04c1b41b6 Mon Sep 17 00:00:00 2001 From: Jolly Shah Date: Fri, 4 Jan 2019 12:03:44 -0800 Subject: [PATCH 19/20] zynqmp: pm: Invalidate several clocks that Linux doesn't need to control Linux has no reason to use these system and debug clocks and therefore shouldn't access them. These clocks are marked as invalid in order to prevent Linux from registering and querying them. Note that despite clocks being marked as invalid a security issue still remains in place as there is nothing that prevents the non-secure world from gating these clocks and that way causing damage to the system. Signed-off-by: Mirela Simonovic Acked-by: Will Wong Signed-off-by: Jolly Shah --- plat/xilinx/zynqmp/pm_service/pm_api_clock.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_clock.c b/plat/xilinx/zynqmp/pm_service/pm_api_clock.c index 8901ea691..c49969f24 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_api_clock.c +++ b/plat/xilinx/zynqmp/pm_service/pm_api_clock.c @@ -2256,6 +2256,24 @@ static struct pm_ext_clock ext_clocks[] = { static uint32_t pm_clk_invalid_list[] = {CLK_USB0, CLK_USB1, CLK_CSU_SPB, CLK_ACPU_FULL, CLK_ACPU_HALF, + CLK_DBG_FPD, + CLK_DBG_LPD, + CLK_DBG_TRACE, + CLK_DBG_TSTMP, + CLK_DDR_REF, + CLK_TOPSW_MAIN, + CLK_TOPSW_LSBUS, + CLK_GTGREF0_REF, + CLK_LPD_SWITCH, + CLK_LPD_LSBUS, + CLK_CPU_R5, + CLK_CPU_R5_CORE, + CLK_CSU_SPB, + CLK_CSU_PLL, + CLK_PCAP, + CLK_IOU_SWITCH, + CLK_DLL_REF, + CLK_TIMESTAMP_REF, }; /** From ff966c27a6fcc7572744c92ff9963e59999a23a1 Mon Sep 17 00:00:00 2001 From: Jolly Shah Date: Wed, 2 Jan 2019 13:53:33 -0800 Subject: [PATCH 20/20] zynqmp: pm: Invalidate unused APLL_TO_LPD clock This clock does not drive any clock in LPD so there is no need for Linux to try to initialize it. Signed-off-by: Mirela Simonovic Acked-by: Will Wong Signed-off-by: Jolly Shah --- plat/xilinx/zynqmp/pm_service/pm_api_clock.c | 1 + 1 file changed, 1 insertion(+) diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_clock.c b/plat/xilinx/zynqmp/pm_service/pm_api_clock.c index c49969f24..10f749a0e 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_api_clock.c +++ b/plat/xilinx/zynqmp/pm_service/pm_api_clock.c @@ -2256,6 +2256,7 @@ static struct pm_ext_clock ext_clocks[] = { static uint32_t pm_clk_invalid_list[] = {CLK_USB0, CLK_USB1, CLK_CSU_SPB, CLK_ACPU_FULL, CLK_ACPU_HALF, + CLK_APLL_TO_LPD, CLK_DBG_FPD, CLK_DBG_LPD, CLK_DBG_TRACE,