1fe37b482SAisheng Dong // SPDX-License-Identifier: GPL-2.0+ 2fe37b482SAisheng Dong /* 35964012cSDong Aisheng * Copyright 2018-2021 NXP 4fe37b482SAisheng Dong * Dong Aisheng <aisheng.dong@nxp.com> 5fe37b482SAisheng Dong */ 6fe37b482SAisheng Dong 73b9ea606SAnson Huang #include <dt-bindings/firmware/imx/rsrc.h> 83b9ea606SAnson Huang #include <linux/arm-smccc.h> 95964012cSDong Aisheng #include <linux/bsearch.h> 10fe37b482SAisheng Dong #include <linux/clk-provider.h> 11fe37b482SAisheng Dong #include <linux/err.h> 1277d8f306SDong Aisheng #include <linux/of_platform.h> 1377d8f306SDong Aisheng #include <linux/platform_device.h> 1477d8f306SDong Aisheng #include <linux/pm_domain.h> 1578edeb08SDong Aisheng #include <linux/pm_runtime.h> 16fe37b482SAisheng Dong #include <linux/slab.h> 17fe37b482SAisheng Dong 18fe37b482SAisheng Dong #include "clk-scu.h" 19fe37b482SAisheng Dong 203b9ea606SAnson Huang #define IMX_SIP_CPUFREQ 0xC2000001 213b9ea606SAnson Huang #define IMX_SIP_SET_CPUFREQ 0x00 223b9ea606SAnson Huang 23fe37b482SAisheng Dong static struct imx_sc_ipc *ccm_ipc_handle; 24550b562aSZou Wei static struct device_node *pd_np; 25220175cdSDong Aisheng static struct platform_driver imx_clk_scu_driver; 265964012cSDong Aisheng static const struct imx_clk_scu_rsrc_table *rsrc_table; 2777d8f306SDong Aisheng 2877d8f306SDong Aisheng struct imx_scu_clk_node { 2977d8f306SDong Aisheng const char *name; 3077d8f306SDong Aisheng u32 rsrc; 3177d8f306SDong Aisheng u8 clk_type; 3277d8f306SDong Aisheng const char * const *parents; 3377d8f306SDong Aisheng int num_parents; 3477d8f306SDong Aisheng 3577d8f306SDong Aisheng struct clk_hw *hw; 3677d8f306SDong Aisheng struct list_head node; 3777d8f306SDong Aisheng }; 3877d8f306SDong Aisheng 3977d8f306SDong Aisheng struct list_head imx_scu_clks[IMX_SC_R_LAST]; 40fe37b482SAisheng Dong 41fe37b482SAisheng Dong /* 42fe37b482SAisheng Dong * struct clk_scu - Description of one SCU clock 43fe37b482SAisheng Dong * @hw: the common clk_hw 44fe37b482SAisheng Dong * @rsrc_id: resource ID of this SCU clock 45fe37b482SAisheng Dong * @clk_type: type of this clock resource 46fe37b482SAisheng Dong */ 47fe37b482SAisheng Dong struct clk_scu { 48fe37b482SAisheng Dong struct clk_hw hw; 49fe37b482SAisheng Dong u16 rsrc_id; 50fe37b482SAisheng Dong u8 clk_type; 51d0409631SDong Aisheng 52d0409631SDong Aisheng /* for state save&restore */ 53a82327ccSDong Aisheng struct clk_hw *parent; 54a82327ccSDong Aisheng u8 parent_index; 55d0409631SDong Aisheng bool is_enabled; 56d0409631SDong Aisheng u32 rate; 57fe37b482SAisheng Dong }; 58fe37b482SAisheng Dong 59fe37b482SAisheng Dong /* 605392c5deSDong Aisheng * struct clk_gpr_scu - Description of one SCU GPR clock 615392c5deSDong Aisheng * @hw: the common clk_hw 625392c5deSDong Aisheng * @rsrc_id: resource ID of this SCU clock 635392c5deSDong Aisheng * @gpr_id: GPR ID index to control the divider 645392c5deSDong Aisheng */ 655392c5deSDong Aisheng struct clk_gpr_scu { 665392c5deSDong Aisheng struct clk_hw hw; 675392c5deSDong Aisheng u16 rsrc_id; 685392c5deSDong Aisheng u8 gpr_id; 695392c5deSDong Aisheng u8 flags; 705392c5deSDong Aisheng bool gate_invert; 715392c5deSDong Aisheng }; 725392c5deSDong Aisheng 735392c5deSDong Aisheng #define to_clk_gpr_scu(_hw) container_of(_hw, struct clk_gpr_scu, hw) 745392c5deSDong Aisheng 755392c5deSDong Aisheng /* 76fe37b482SAisheng Dong * struct imx_sc_msg_req_set_clock_rate - clock set rate protocol 77fe37b482SAisheng Dong * @hdr: SCU protocol header 78fe37b482SAisheng Dong * @rate: rate to set 79fe37b482SAisheng Dong * @resource: clock resource to set rate 80fe37b482SAisheng Dong * @clk: clk type of this resource 81fe37b482SAisheng Dong * 82fe37b482SAisheng Dong * This structure describes the SCU protocol of clock rate set 83fe37b482SAisheng Dong */ 84fe37b482SAisheng Dong struct imx_sc_msg_req_set_clock_rate { 85fe37b482SAisheng Dong struct imx_sc_rpc_msg hdr; 86fe37b482SAisheng Dong __le32 rate; 87fe37b482SAisheng Dong __le16 resource; 88fe37b482SAisheng Dong u8 clk; 89a0ae04a2SLeonard Crestez } __packed __aligned(4); 90fe37b482SAisheng Dong 91fe37b482SAisheng Dong struct req_get_clock_rate { 92fe37b482SAisheng Dong __le16 resource; 93fe37b482SAisheng Dong u8 clk; 94a0ae04a2SLeonard Crestez } __packed __aligned(4); 95fe37b482SAisheng Dong 96fe37b482SAisheng Dong struct resp_get_clock_rate { 97fe37b482SAisheng Dong __le32 rate; 98fe37b482SAisheng Dong }; 99fe37b482SAisheng Dong 100fe37b482SAisheng Dong /* 101fe37b482SAisheng Dong * struct imx_sc_msg_get_clock_rate - clock get rate protocol 102fe37b482SAisheng Dong * @hdr: SCU protocol header 103fe37b482SAisheng Dong * @req: get rate request protocol 104fe37b482SAisheng Dong * @resp: get rate response protocol 105fe37b482SAisheng Dong * 106fe37b482SAisheng Dong * This structure describes the SCU protocol of clock rate get 107fe37b482SAisheng Dong */ 108fe37b482SAisheng Dong struct imx_sc_msg_get_clock_rate { 109fe37b482SAisheng Dong struct imx_sc_rpc_msg hdr; 110fe37b482SAisheng Dong union { 111fe37b482SAisheng Dong struct req_get_clock_rate req; 112fe37b482SAisheng Dong struct resp_get_clock_rate resp; 113fe37b482SAisheng Dong } data; 114fe37b482SAisheng Dong }; 115fe37b482SAisheng Dong 116fe37b482SAisheng Dong /* 117666aed2dSAisheng Dong * struct imx_sc_msg_get_clock_parent - clock get parent protocol 118666aed2dSAisheng Dong * @hdr: SCU protocol header 119666aed2dSAisheng Dong * @req: get parent request protocol 120666aed2dSAisheng Dong * @resp: get parent response protocol 121666aed2dSAisheng Dong * 122666aed2dSAisheng Dong * This structure describes the SCU protocol of clock get parent 123666aed2dSAisheng Dong */ 124666aed2dSAisheng Dong struct imx_sc_msg_get_clock_parent { 125666aed2dSAisheng Dong struct imx_sc_rpc_msg hdr; 126666aed2dSAisheng Dong union { 127666aed2dSAisheng Dong struct req_get_clock_parent { 128666aed2dSAisheng Dong __le16 resource; 129666aed2dSAisheng Dong u8 clk; 1308400ab88SLeonard Crestez } __packed __aligned(4) req; 131666aed2dSAisheng Dong struct resp_get_clock_parent { 132666aed2dSAisheng Dong u8 parent; 133666aed2dSAisheng Dong } resp; 134666aed2dSAisheng Dong } data; 135666aed2dSAisheng Dong }; 136666aed2dSAisheng Dong 137666aed2dSAisheng Dong /* 138666aed2dSAisheng Dong * struct imx_sc_msg_set_clock_parent - clock set parent protocol 139666aed2dSAisheng Dong * @hdr: SCU protocol header 140666aed2dSAisheng Dong * @req: set parent request protocol 141666aed2dSAisheng Dong * 142666aed2dSAisheng Dong * This structure describes the SCU protocol of clock set parent 143666aed2dSAisheng Dong */ 144666aed2dSAisheng Dong struct imx_sc_msg_set_clock_parent { 145666aed2dSAisheng Dong struct imx_sc_rpc_msg hdr; 146666aed2dSAisheng Dong __le16 resource; 147666aed2dSAisheng Dong u8 clk; 148666aed2dSAisheng Dong u8 parent; 149666aed2dSAisheng Dong } __packed; 150666aed2dSAisheng Dong 151666aed2dSAisheng Dong /* 152fe37b482SAisheng Dong * struct imx_sc_msg_req_clock_enable - clock gate protocol 153fe37b482SAisheng Dong * @hdr: SCU protocol header 154fe37b482SAisheng Dong * @resource: clock resource to gate 155fe37b482SAisheng Dong * @clk: clk type of this resource 156fe37b482SAisheng Dong * @enable: whether gate off the clock 157fe37b482SAisheng Dong * @autog: HW auto gate enable 158fe37b482SAisheng Dong * 159fe37b482SAisheng Dong * This structure describes the SCU protocol of clock gate 160fe37b482SAisheng Dong */ 161fe37b482SAisheng Dong struct imx_sc_msg_req_clock_enable { 162fe37b482SAisheng Dong struct imx_sc_rpc_msg hdr; 163fe37b482SAisheng Dong __le16 resource; 164fe37b482SAisheng Dong u8 clk; 165fe37b482SAisheng Dong u8 enable; 166fe37b482SAisheng Dong u8 autog; 167a0ae04a2SLeonard Crestez } __packed __aligned(4); 168fe37b482SAisheng Dong 169fe37b482SAisheng Dong static inline struct clk_scu *to_clk_scu(struct clk_hw *hw) 170fe37b482SAisheng Dong { 171fe37b482SAisheng Dong return container_of(hw, struct clk_scu, hw); 172fe37b482SAisheng Dong } 173fe37b482SAisheng Dong 1745964012cSDong Aisheng static inline int imx_scu_clk_search_cmp(const void *rsrc, const void *rsrc_p) 1755964012cSDong Aisheng { 1765964012cSDong Aisheng return *(u32 *)rsrc - *(u32 *)rsrc_p; 1775964012cSDong Aisheng } 1785964012cSDong Aisheng 1795964012cSDong Aisheng static bool imx_scu_clk_is_valid(u32 rsrc_id) 1805964012cSDong Aisheng { 1815964012cSDong Aisheng void *p; 1825964012cSDong Aisheng 1835964012cSDong Aisheng if (!rsrc_table) 1845964012cSDong Aisheng return true; 1855964012cSDong Aisheng 1865964012cSDong Aisheng p = bsearch(&rsrc_id, rsrc_table->rsrc, rsrc_table->num, 1875964012cSDong Aisheng sizeof(rsrc_table->rsrc[0]), imx_scu_clk_search_cmp); 1885964012cSDong Aisheng 1895964012cSDong Aisheng return p != NULL; 1905964012cSDong Aisheng } 1915964012cSDong Aisheng 1925964012cSDong Aisheng int imx_clk_scu_init(struct device_node *np, 1935964012cSDong Aisheng const struct imx_clk_scu_rsrc_table *data) 194fe37b482SAisheng Dong { 19577d8f306SDong Aisheng u32 clk_cells; 19677d8f306SDong Aisheng int ret, i; 19777d8f306SDong Aisheng 19877d8f306SDong Aisheng ret = imx_scu_get_handle(&ccm_ipc_handle); 19977d8f306SDong Aisheng if (ret) 20077d8f306SDong Aisheng return ret; 20177d8f306SDong Aisheng 20277d8f306SDong Aisheng of_property_read_u32(np, "#clock-cells", &clk_cells); 20377d8f306SDong Aisheng 20477d8f306SDong Aisheng if (clk_cells == 2) { 20577d8f306SDong Aisheng for (i = 0; i < IMX_SC_R_LAST; i++) 20677d8f306SDong Aisheng INIT_LIST_HEAD(&imx_scu_clks[i]); 20743d24796SDong Aisheng 20843d24796SDong Aisheng /* pd_np will be used to attach power domains later */ 20977d8f306SDong Aisheng pd_np = of_find_compatible_node(NULL, NULL, "fsl,scu-pd"); 21043d24796SDong Aisheng if (!pd_np) 21143d24796SDong Aisheng return -EINVAL; 2125964012cSDong Aisheng 2135964012cSDong Aisheng rsrc_table = data; 21477d8f306SDong Aisheng } 21577d8f306SDong Aisheng 216220175cdSDong Aisheng return platform_driver_register(&imx_clk_scu_driver); 217fe37b482SAisheng Dong } 218fe37b482SAisheng Dong 219fe37b482SAisheng Dong /* 220fe37b482SAisheng Dong * clk_scu_recalc_rate - Get clock rate for a SCU clock 221fe37b482SAisheng Dong * @hw: clock to get rate for 222fe37b482SAisheng Dong * @parent_rate: parent rate provided by common clock framework, not used 223fe37b482SAisheng Dong * 224fe37b482SAisheng Dong * Gets the current clock rate of a SCU clock. Returns the current 225fe37b482SAisheng Dong * clock rate, or zero in failure. 226fe37b482SAisheng Dong */ 227fe37b482SAisheng Dong static unsigned long clk_scu_recalc_rate(struct clk_hw *hw, 228fe37b482SAisheng Dong unsigned long parent_rate) 229fe37b482SAisheng Dong { 230fe37b482SAisheng Dong struct clk_scu *clk = to_clk_scu(hw); 231fe37b482SAisheng Dong struct imx_sc_msg_get_clock_rate msg; 232fe37b482SAisheng Dong struct imx_sc_rpc_msg *hdr = &msg.hdr; 233fe37b482SAisheng Dong int ret; 234fe37b482SAisheng Dong 235fe37b482SAisheng Dong hdr->ver = IMX_SC_RPC_VERSION; 236fe37b482SAisheng Dong hdr->svc = IMX_SC_RPC_SVC_PM; 237fe37b482SAisheng Dong hdr->func = IMX_SC_PM_FUNC_GET_CLOCK_RATE; 238fe37b482SAisheng Dong hdr->size = 2; 239fe37b482SAisheng Dong 240fe37b482SAisheng Dong msg.data.req.resource = cpu_to_le16(clk->rsrc_id); 241fe37b482SAisheng Dong msg.data.req.clk = clk->clk_type; 242fe37b482SAisheng Dong 243fe37b482SAisheng Dong ret = imx_scu_call_rpc(ccm_ipc_handle, &msg, true); 244fe37b482SAisheng Dong if (ret) { 245fe37b482SAisheng Dong pr_err("%s: failed to get clock rate %d\n", 246fe37b482SAisheng Dong clk_hw_get_name(hw), ret); 247fe37b482SAisheng Dong return 0; 248fe37b482SAisheng Dong } 249fe37b482SAisheng Dong 250fe37b482SAisheng Dong return le32_to_cpu(msg.data.resp.rate); 251fe37b482SAisheng Dong } 252fe37b482SAisheng Dong 253fe37b482SAisheng Dong /* 254fe37b482SAisheng Dong * clk_scu_round_rate - Round clock rate for a SCU clock 255fe37b482SAisheng Dong * @hw: clock to round rate for 256fe37b482SAisheng Dong * @rate: rate to round 257fe37b482SAisheng Dong * @parent_rate: parent rate provided by common clock framework, not used 258fe37b482SAisheng Dong * 259fe37b482SAisheng Dong * Returns the current clock rate, or zero in failure. 260fe37b482SAisheng Dong */ 261fe37b482SAisheng Dong static long clk_scu_round_rate(struct clk_hw *hw, unsigned long rate, 262fe37b482SAisheng Dong unsigned long *parent_rate) 263fe37b482SAisheng Dong { 264fe37b482SAisheng Dong /* 265fe37b482SAisheng Dong * Assume we support all the requested rate and let the SCU firmware 266fe37b482SAisheng Dong * to handle the left work 267fe37b482SAisheng Dong */ 268fe37b482SAisheng Dong return rate; 269fe37b482SAisheng Dong } 270fe37b482SAisheng Dong 2713b9ea606SAnson Huang static int clk_scu_atf_set_cpu_rate(struct clk_hw *hw, unsigned long rate, 2723b9ea606SAnson Huang unsigned long parent_rate) 2733b9ea606SAnson Huang { 2743b9ea606SAnson Huang struct clk_scu *clk = to_clk_scu(hw); 2753b9ea606SAnson Huang struct arm_smccc_res res; 2763b9ea606SAnson Huang unsigned long cluster_id; 2773b9ea606SAnson Huang 278cd8bd2f3SAnson Huang if (clk->rsrc_id == IMX_SC_R_A35 || clk->rsrc_id == IMX_SC_R_A53) 2793b9ea606SAnson Huang cluster_id = 0; 280a43f6e8aSAnson Huang else if (clk->rsrc_id == IMX_SC_R_A72) 281a43f6e8aSAnson Huang cluster_id = 1; 2823b9ea606SAnson Huang else 2833b9ea606SAnson Huang return -EINVAL; 2843b9ea606SAnson Huang 2853b9ea606SAnson Huang /* CPU frequency scaling can ONLY be done by ARM-Trusted-Firmware */ 2863b9ea606SAnson Huang arm_smccc_smc(IMX_SIP_CPUFREQ, IMX_SIP_SET_CPUFREQ, 2873b9ea606SAnson Huang cluster_id, rate, 0, 0, 0, 0, &res); 2883b9ea606SAnson Huang 2893b9ea606SAnson Huang return 0; 2903b9ea606SAnson Huang } 2913b9ea606SAnson Huang 292fe37b482SAisheng Dong /* 293fe37b482SAisheng Dong * clk_scu_set_rate - Set rate for a SCU clock 294fe37b482SAisheng Dong * @hw: clock to change rate for 295fe37b482SAisheng Dong * @rate: target rate for the clock 296fe37b482SAisheng Dong * @parent_rate: rate of the clock parent, not used for SCU clocks 297fe37b482SAisheng Dong * 298fe37b482SAisheng Dong * Sets a clock frequency for a SCU clock. Returns the SCU 299fe37b482SAisheng Dong * protocol status. 300fe37b482SAisheng Dong */ 301fe37b482SAisheng Dong static int clk_scu_set_rate(struct clk_hw *hw, unsigned long rate, 302fe37b482SAisheng Dong unsigned long parent_rate) 303fe37b482SAisheng Dong { 304fe37b482SAisheng Dong struct clk_scu *clk = to_clk_scu(hw); 305fe37b482SAisheng Dong struct imx_sc_msg_req_set_clock_rate msg; 306fe37b482SAisheng Dong struct imx_sc_rpc_msg *hdr = &msg.hdr; 307fe37b482SAisheng Dong 308fe37b482SAisheng Dong hdr->ver = IMX_SC_RPC_VERSION; 309fe37b482SAisheng Dong hdr->svc = IMX_SC_RPC_SVC_PM; 310fe37b482SAisheng Dong hdr->func = IMX_SC_PM_FUNC_SET_CLOCK_RATE; 311fe37b482SAisheng Dong hdr->size = 3; 312fe37b482SAisheng Dong 313fe37b482SAisheng Dong msg.rate = cpu_to_le32(rate); 314fe37b482SAisheng Dong msg.resource = cpu_to_le16(clk->rsrc_id); 315fe37b482SAisheng Dong msg.clk = clk->clk_type; 316fe37b482SAisheng Dong 317fe37b482SAisheng Dong return imx_scu_call_rpc(ccm_ipc_handle, &msg, true); 318fe37b482SAisheng Dong } 319fe37b482SAisheng Dong 320666aed2dSAisheng Dong static u8 clk_scu_get_parent(struct clk_hw *hw) 321666aed2dSAisheng Dong { 322666aed2dSAisheng Dong struct clk_scu *clk = to_clk_scu(hw); 323666aed2dSAisheng Dong struct imx_sc_msg_get_clock_parent msg; 324666aed2dSAisheng Dong struct imx_sc_rpc_msg *hdr = &msg.hdr; 325666aed2dSAisheng Dong int ret; 326666aed2dSAisheng Dong 327666aed2dSAisheng Dong hdr->ver = IMX_SC_RPC_VERSION; 328666aed2dSAisheng Dong hdr->svc = IMX_SC_RPC_SVC_PM; 329666aed2dSAisheng Dong hdr->func = IMX_SC_PM_FUNC_GET_CLOCK_PARENT; 330666aed2dSAisheng Dong hdr->size = 2; 331666aed2dSAisheng Dong 332666aed2dSAisheng Dong msg.data.req.resource = cpu_to_le16(clk->rsrc_id); 333666aed2dSAisheng Dong msg.data.req.clk = clk->clk_type; 334666aed2dSAisheng Dong 335666aed2dSAisheng Dong ret = imx_scu_call_rpc(ccm_ipc_handle, &msg, true); 336666aed2dSAisheng Dong if (ret) { 337666aed2dSAisheng Dong pr_err("%s: failed to get clock parent %d\n", 338666aed2dSAisheng Dong clk_hw_get_name(hw), ret); 339666aed2dSAisheng Dong return 0; 340666aed2dSAisheng Dong } 341666aed2dSAisheng Dong 342a82327ccSDong Aisheng clk->parent_index = msg.data.resp.parent; 343a82327ccSDong Aisheng 344666aed2dSAisheng Dong return msg.data.resp.parent; 345666aed2dSAisheng Dong } 346666aed2dSAisheng Dong 347666aed2dSAisheng Dong static int clk_scu_set_parent(struct clk_hw *hw, u8 index) 348666aed2dSAisheng Dong { 349666aed2dSAisheng Dong struct clk_scu *clk = to_clk_scu(hw); 350666aed2dSAisheng Dong struct imx_sc_msg_set_clock_parent msg; 351666aed2dSAisheng Dong struct imx_sc_rpc_msg *hdr = &msg.hdr; 352a82327ccSDong Aisheng int ret; 353666aed2dSAisheng Dong 354666aed2dSAisheng Dong hdr->ver = IMX_SC_RPC_VERSION; 355666aed2dSAisheng Dong hdr->svc = IMX_SC_RPC_SVC_PM; 356666aed2dSAisheng Dong hdr->func = IMX_SC_PM_FUNC_SET_CLOCK_PARENT; 357666aed2dSAisheng Dong hdr->size = 2; 358666aed2dSAisheng Dong 359666aed2dSAisheng Dong msg.resource = cpu_to_le16(clk->rsrc_id); 360666aed2dSAisheng Dong msg.clk = clk->clk_type; 361666aed2dSAisheng Dong msg.parent = index; 362666aed2dSAisheng Dong 363a82327ccSDong Aisheng ret = imx_scu_call_rpc(ccm_ipc_handle, &msg, true); 364a82327ccSDong Aisheng if (ret) { 365a82327ccSDong Aisheng pr_err("%s: failed to set clock parent %d\n", 366a82327ccSDong Aisheng clk_hw_get_name(hw), ret); 367a82327ccSDong Aisheng return ret; 368a82327ccSDong Aisheng } 369a82327ccSDong Aisheng 370a82327ccSDong Aisheng clk->parent_index = index; 371a82327ccSDong Aisheng 372a82327ccSDong Aisheng return 0; 373666aed2dSAisheng Dong } 374666aed2dSAisheng Dong 375fe37b482SAisheng Dong static int sc_pm_clock_enable(struct imx_sc_ipc *ipc, u16 resource, 376fe37b482SAisheng Dong u8 clk, bool enable, bool autog) 377fe37b482SAisheng Dong { 378fe37b482SAisheng Dong struct imx_sc_msg_req_clock_enable msg; 379fe37b482SAisheng Dong struct imx_sc_rpc_msg *hdr = &msg.hdr; 380fe37b482SAisheng Dong 381fe37b482SAisheng Dong hdr->ver = IMX_SC_RPC_VERSION; 382fe37b482SAisheng Dong hdr->svc = IMX_SC_RPC_SVC_PM; 383fe37b482SAisheng Dong hdr->func = IMX_SC_PM_FUNC_CLOCK_ENABLE; 384fe37b482SAisheng Dong hdr->size = 3; 385fe37b482SAisheng Dong 386fe37b482SAisheng Dong msg.resource = cpu_to_le16(resource); 387fe37b482SAisheng Dong msg.clk = clk; 388fe37b482SAisheng Dong msg.enable = enable; 389fe37b482SAisheng Dong msg.autog = autog; 390fe37b482SAisheng Dong 391fe37b482SAisheng Dong return imx_scu_call_rpc(ccm_ipc_handle, &msg, true); 392fe37b482SAisheng Dong } 393fe37b482SAisheng Dong 394fe37b482SAisheng Dong /* 395fe37b482SAisheng Dong * clk_scu_prepare - Enable a SCU clock 396fe37b482SAisheng Dong * @hw: clock to enable 397fe37b482SAisheng Dong * 398fe37b482SAisheng Dong * Enable the clock at the DSC slice level 399fe37b482SAisheng Dong */ 400fe37b482SAisheng Dong static int clk_scu_prepare(struct clk_hw *hw) 401fe37b482SAisheng Dong { 402fe37b482SAisheng Dong struct clk_scu *clk = to_clk_scu(hw); 403fe37b482SAisheng Dong 404fe37b482SAisheng Dong return sc_pm_clock_enable(ccm_ipc_handle, clk->rsrc_id, 405fe37b482SAisheng Dong clk->clk_type, true, false); 406fe37b482SAisheng Dong } 407fe37b482SAisheng Dong 408fe37b482SAisheng Dong /* 409fe37b482SAisheng Dong * clk_scu_unprepare - Disable a SCU clock 410fe37b482SAisheng Dong * @hw: clock to enable 411fe37b482SAisheng Dong * 412fe37b482SAisheng Dong * Disable the clock at the DSC slice level 413fe37b482SAisheng Dong */ 414fe37b482SAisheng Dong static void clk_scu_unprepare(struct clk_hw *hw) 415fe37b482SAisheng Dong { 416fe37b482SAisheng Dong struct clk_scu *clk = to_clk_scu(hw); 417fe37b482SAisheng Dong int ret; 418fe37b482SAisheng Dong 419fe37b482SAisheng Dong ret = sc_pm_clock_enable(ccm_ipc_handle, clk->rsrc_id, 420fe37b482SAisheng Dong clk->clk_type, false, false); 421fe37b482SAisheng Dong if (ret) 422fe37b482SAisheng Dong pr_warn("%s: clk unprepare failed %d\n", clk_hw_get_name(hw), 423fe37b482SAisheng Dong ret); 424fe37b482SAisheng Dong } 425fe37b482SAisheng Dong 426fe37b482SAisheng Dong static const struct clk_ops clk_scu_ops = { 427fe37b482SAisheng Dong .recalc_rate = clk_scu_recalc_rate, 428fe37b482SAisheng Dong .round_rate = clk_scu_round_rate, 429fe37b482SAisheng Dong .set_rate = clk_scu_set_rate, 430666aed2dSAisheng Dong .get_parent = clk_scu_get_parent, 431666aed2dSAisheng Dong .set_parent = clk_scu_set_parent, 432fe37b482SAisheng Dong .prepare = clk_scu_prepare, 433fe37b482SAisheng Dong .unprepare = clk_scu_unprepare, 434fe37b482SAisheng Dong }; 435fe37b482SAisheng Dong 4363b9ea606SAnson Huang static const struct clk_ops clk_scu_cpu_ops = { 4373b9ea606SAnson Huang .recalc_rate = clk_scu_recalc_rate, 4383b9ea606SAnson Huang .round_rate = clk_scu_round_rate, 4393b9ea606SAnson Huang .set_rate = clk_scu_atf_set_cpu_rate, 4403b9ea606SAnson Huang .prepare = clk_scu_prepare, 4413b9ea606SAnson Huang .unprepare = clk_scu_unprepare, 4423b9ea606SAnson Huang }; 4433b9ea606SAnson Huang 444b192d040SGuoniu.zhou static const struct clk_ops clk_scu_pi_ops = { 445b192d040SGuoniu.zhou .recalc_rate = clk_scu_recalc_rate, 446b192d040SGuoniu.zhou .round_rate = clk_scu_round_rate, 447b192d040SGuoniu.zhou .set_rate = clk_scu_set_rate, 448b192d040SGuoniu.zhou }; 449b192d040SGuoniu.zhou 4502f1a2c1dSDong Aisheng struct clk_hw *__imx_clk_scu(struct device *dev, const char *name, 4512f1a2c1dSDong Aisheng const char * const *parents, int num_parents, 4522f1a2c1dSDong Aisheng u32 rsrc_id, u8 clk_type) 453fe37b482SAisheng Dong { 454fe37b482SAisheng Dong struct clk_init_data init; 455fe37b482SAisheng Dong struct clk_scu *clk; 456fe37b482SAisheng Dong struct clk_hw *hw; 457fe37b482SAisheng Dong int ret; 458fe37b482SAisheng Dong 459fe37b482SAisheng Dong clk = kzalloc(sizeof(*clk), GFP_KERNEL); 460fe37b482SAisheng Dong if (!clk) 461fe37b482SAisheng Dong return ERR_PTR(-ENOMEM); 462fe37b482SAisheng Dong 463fe37b482SAisheng Dong clk->rsrc_id = rsrc_id; 464fe37b482SAisheng Dong clk->clk_type = clk_type; 465fe37b482SAisheng Dong 466fe37b482SAisheng Dong init.name = name; 467fe37b482SAisheng Dong init.ops = &clk_scu_ops; 468a43f6e8aSAnson Huang if (rsrc_id == IMX_SC_R_A35 || rsrc_id == IMX_SC_R_A53 || rsrc_id == IMX_SC_R_A72) 4693b9ea606SAnson Huang init.ops = &clk_scu_cpu_ops; 470b192d040SGuoniu.zhou else if (rsrc_id == IMX_SC_R_PI_0_PLL) 471b192d040SGuoniu.zhou init.ops = &clk_scu_pi_ops; 4723b9ea606SAnson Huang else 4733b9ea606SAnson Huang init.ops = &clk_scu_ops; 474666aed2dSAisheng Dong init.parent_names = parents; 475666aed2dSAisheng Dong init.num_parents = num_parents; 476666aed2dSAisheng Dong 477fe37b482SAisheng Dong /* 478fe37b482SAisheng Dong * Note on MX8, the clocks are tightly coupled with power domain 479fe37b482SAisheng Dong * that once the power domain is off, the clock status may be 480fe37b482SAisheng Dong * lost. So we make it NOCACHE to let user to retrieve the real 481fe37b482SAisheng Dong * clock status from HW instead of using the possible invalid 482fe37b482SAisheng Dong * cached rate. 483fe37b482SAisheng Dong */ 484fe37b482SAisheng Dong init.flags = CLK_GET_RATE_NOCACHE; 485fe37b482SAisheng Dong clk->hw.init = &init; 486fe37b482SAisheng Dong 487fe37b482SAisheng Dong hw = &clk->hw; 4882f1a2c1dSDong Aisheng ret = clk_hw_register(dev, hw); 489fe37b482SAisheng Dong if (ret) { 490fe37b482SAisheng Dong kfree(clk); 491fe37b482SAisheng Dong hw = ERR_PTR(ret); 492054ef44eSJian Dong return hw; 493fe37b482SAisheng Dong } 494fe37b482SAisheng Dong 495d0409631SDong Aisheng if (dev) 496d0409631SDong Aisheng dev_set_drvdata(dev, clk); 497d0409631SDong Aisheng 498fe37b482SAisheng Dong return hw; 499fe37b482SAisheng Dong } 50077d8f306SDong Aisheng 50177d8f306SDong Aisheng struct clk_hw *imx_scu_of_clk_src_get(struct of_phandle_args *clkspec, 50277d8f306SDong Aisheng void *data) 50377d8f306SDong Aisheng { 50477d8f306SDong Aisheng unsigned int rsrc = clkspec->args[0]; 50577d8f306SDong Aisheng unsigned int idx = clkspec->args[1]; 50677d8f306SDong Aisheng struct list_head *scu_clks = data; 50777d8f306SDong Aisheng struct imx_scu_clk_node *clk; 50877d8f306SDong Aisheng 50977d8f306SDong Aisheng list_for_each_entry(clk, &scu_clks[rsrc], node) { 51077d8f306SDong Aisheng if (clk->clk_type == idx) 51177d8f306SDong Aisheng return clk->hw; 51277d8f306SDong Aisheng } 51377d8f306SDong Aisheng 51477d8f306SDong Aisheng return ERR_PTR(-ENODEV); 51577d8f306SDong Aisheng } 51677d8f306SDong Aisheng 51777d8f306SDong Aisheng static int imx_clk_scu_probe(struct platform_device *pdev) 51877d8f306SDong Aisheng { 51977d8f306SDong Aisheng struct device *dev = &pdev->dev; 52077d8f306SDong Aisheng struct imx_scu_clk_node *clk = dev_get_platdata(dev); 52177d8f306SDong Aisheng struct clk_hw *hw; 52278edeb08SDong Aisheng int ret; 52377d8f306SDong Aisheng 52418a50f82SNitin Garg if (!((clk->rsrc == IMX_SC_R_A35) || (clk->rsrc == IMX_SC_R_A53) || 52518a50f82SNitin Garg (clk->rsrc == IMX_SC_R_A72))) { 52678edeb08SDong Aisheng pm_runtime_set_suspended(dev); 52778edeb08SDong Aisheng pm_runtime_set_autosuspend_delay(dev, 50); 52878edeb08SDong Aisheng pm_runtime_use_autosuspend(&pdev->dev); 52978edeb08SDong Aisheng pm_runtime_enable(dev); 53078edeb08SDong Aisheng 531*bfd594b3SMiaoqian Lin ret = pm_runtime_resume_and_get(dev); 53278edeb08SDong Aisheng if (ret) { 5336618b5aaSDong Aisheng pm_genpd_remove_device(dev); 53478edeb08SDong Aisheng pm_runtime_disable(dev); 53578edeb08SDong Aisheng return ret; 53678edeb08SDong Aisheng } 53718a50f82SNitin Garg } 53878edeb08SDong Aisheng 53978edeb08SDong Aisheng hw = __imx_clk_scu(dev, clk->name, clk->parents, clk->num_parents, 54077d8f306SDong Aisheng clk->rsrc, clk->clk_type); 54178edeb08SDong Aisheng if (IS_ERR(hw)) { 54278edeb08SDong Aisheng pm_runtime_disable(dev); 54377d8f306SDong Aisheng return PTR_ERR(hw); 54478edeb08SDong Aisheng } 54577d8f306SDong Aisheng 54677d8f306SDong Aisheng clk->hw = hw; 54777d8f306SDong Aisheng list_add_tail(&clk->node, &imx_scu_clks[clk->rsrc]); 54877d8f306SDong Aisheng 54918a50f82SNitin Garg if (!((clk->rsrc == IMX_SC_R_A35) || (clk->rsrc == IMX_SC_R_A53) || 55018a50f82SNitin Garg (clk->rsrc == IMX_SC_R_A72))) { 55178edeb08SDong Aisheng pm_runtime_mark_last_busy(&pdev->dev); 55278edeb08SDong Aisheng pm_runtime_put_autosuspend(&pdev->dev); 55318a50f82SNitin Garg } 55478edeb08SDong Aisheng 55577d8f306SDong Aisheng dev_dbg(dev, "register SCU clock rsrc:%d type:%d\n", clk->rsrc, 55677d8f306SDong Aisheng clk->clk_type); 55777d8f306SDong Aisheng 55877d8f306SDong Aisheng return 0; 55977d8f306SDong Aisheng } 56077d8f306SDong Aisheng 561d0409631SDong Aisheng static int __maybe_unused imx_clk_scu_suspend(struct device *dev) 562d0409631SDong Aisheng { 563d0409631SDong Aisheng struct clk_scu *clk = dev_get_drvdata(dev); 564aecf425fSDong Aisheng u32 rsrc_id = clk->rsrc_id; 565aecf425fSDong Aisheng 566aecf425fSDong Aisheng if ((rsrc_id == IMX_SC_R_A35) || (rsrc_id == IMX_SC_R_A53) || 567aecf425fSDong Aisheng (rsrc_id == IMX_SC_R_A72)) 568aecf425fSDong Aisheng return 0; 569d0409631SDong Aisheng 570a82327ccSDong Aisheng clk->parent = clk_hw_get_parent(&clk->hw); 571a82327ccSDong Aisheng 5727487986cSAnson Huang /* DC SS needs to handle bypass clock using non-cached clock rate */ 5737487986cSAnson Huang if (clk->rsrc_id == IMX_SC_R_DC_0_VIDEO0 || 5747487986cSAnson Huang clk->rsrc_id == IMX_SC_R_DC_0_VIDEO1 || 5757487986cSAnson Huang clk->rsrc_id == IMX_SC_R_DC_1_VIDEO0 || 5767487986cSAnson Huang clk->rsrc_id == IMX_SC_R_DC_1_VIDEO1) 5777487986cSAnson Huang clk->rate = clk_scu_recalc_rate(&clk->hw, 0); 5787487986cSAnson Huang else 579d0409631SDong Aisheng clk->rate = clk_hw_get_rate(&clk->hw); 580d0409631SDong Aisheng clk->is_enabled = clk_hw_is_enabled(&clk->hw); 581d0409631SDong Aisheng 582a82327ccSDong Aisheng if (clk->parent) 583a82327ccSDong Aisheng dev_dbg(dev, "save parent %s idx %u\n", clk_hw_get_name(clk->parent), 584a82327ccSDong Aisheng clk->parent_index); 585a82327ccSDong Aisheng 586d0409631SDong Aisheng if (clk->rate) 587d0409631SDong Aisheng dev_dbg(dev, "save rate %d\n", clk->rate); 588d0409631SDong Aisheng 589d0409631SDong Aisheng if (clk->is_enabled) 590d0409631SDong Aisheng dev_dbg(dev, "save enabled state\n"); 591d0409631SDong Aisheng 592d0409631SDong Aisheng return 0; 593d0409631SDong Aisheng } 594d0409631SDong Aisheng 595d0409631SDong Aisheng static int __maybe_unused imx_clk_scu_resume(struct device *dev) 596d0409631SDong Aisheng { 597d0409631SDong Aisheng struct clk_scu *clk = dev_get_drvdata(dev); 598aecf425fSDong Aisheng u32 rsrc_id = clk->rsrc_id; 599d0409631SDong Aisheng int ret = 0; 600d0409631SDong Aisheng 601aecf425fSDong Aisheng if ((rsrc_id == IMX_SC_R_A35) || (rsrc_id == IMX_SC_R_A53) || 602aecf425fSDong Aisheng (rsrc_id == IMX_SC_R_A72)) 603aecf425fSDong Aisheng return 0; 604aecf425fSDong Aisheng 605a82327ccSDong Aisheng if (clk->parent) { 606a82327ccSDong Aisheng ret = clk_scu_set_parent(&clk->hw, clk->parent_index); 607a82327ccSDong Aisheng dev_dbg(dev, "restore parent %s idx %u %s\n", 608a82327ccSDong Aisheng clk_hw_get_name(clk->parent), 609a82327ccSDong Aisheng clk->parent_index, !ret ? "success" : "failed"); 610a82327ccSDong Aisheng } 611a82327ccSDong Aisheng 612d0409631SDong Aisheng if (clk->rate) { 613d0409631SDong Aisheng ret = clk_scu_set_rate(&clk->hw, clk->rate, 0); 614d0409631SDong Aisheng dev_dbg(dev, "restore rate %d %s\n", clk->rate, 615d0409631SDong Aisheng !ret ? "success" : "failed"); 616d0409631SDong Aisheng } 617d0409631SDong Aisheng 618a61cea83SDong Aisheng if (clk->is_enabled && rsrc_id != IMX_SC_R_PI_0_PLL) { 619d0409631SDong Aisheng ret = clk_scu_prepare(&clk->hw); 620d0409631SDong Aisheng dev_dbg(dev, "restore enabled state %s\n", 621d0409631SDong Aisheng !ret ? "success" : "failed"); 622d0409631SDong Aisheng } 623d0409631SDong Aisheng 624d0409631SDong Aisheng return ret; 625d0409631SDong Aisheng } 626d0409631SDong Aisheng 627d0409631SDong Aisheng static const struct dev_pm_ops imx_clk_scu_pm_ops = { 628d0409631SDong Aisheng SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(imx_clk_scu_suspend, 629d0409631SDong Aisheng imx_clk_scu_resume) 630d0409631SDong Aisheng }; 631d0409631SDong Aisheng 63277d8f306SDong Aisheng static struct platform_driver imx_clk_scu_driver = { 63377d8f306SDong Aisheng .driver = { 63477d8f306SDong Aisheng .name = "imx-scu-clk", 63577d8f306SDong Aisheng .suppress_bind_attrs = true, 636d0409631SDong Aisheng .pm = &imx_clk_scu_pm_ops, 63777d8f306SDong Aisheng }, 63877d8f306SDong Aisheng .probe = imx_clk_scu_probe, 63977d8f306SDong Aisheng }; 64077d8f306SDong Aisheng 64177d8f306SDong Aisheng static int imx_clk_scu_attach_pd(struct device *dev, u32 rsrc_id) 64277d8f306SDong Aisheng { 64377d8f306SDong Aisheng struct of_phandle_args genpdspec = { 64477d8f306SDong Aisheng .np = pd_np, 64577d8f306SDong Aisheng .args_count = 1, 64677d8f306SDong Aisheng .args[0] = rsrc_id, 64777d8f306SDong Aisheng }; 64877d8f306SDong Aisheng 6490d5f1f47SDong Aisheng if (rsrc_id == IMX_SC_R_A35 || rsrc_id == IMX_SC_R_A53 || 6500d5f1f47SDong Aisheng rsrc_id == IMX_SC_R_A72) 6510d5f1f47SDong Aisheng return 0; 6520d5f1f47SDong Aisheng 65377d8f306SDong Aisheng return of_genpd_add_device(&genpdspec, dev); 65477d8f306SDong Aisheng } 65577d8f306SDong Aisheng 65677d8f306SDong Aisheng struct clk_hw *imx_clk_scu_alloc_dev(const char *name, 65777d8f306SDong Aisheng const char * const *parents, 65877d8f306SDong Aisheng int num_parents, u32 rsrc_id, u8 clk_type) 65977d8f306SDong Aisheng { 66077d8f306SDong Aisheng struct imx_scu_clk_node clk = { 66177d8f306SDong Aisheng .name = name, 66277d8f306SDong Aisheng .rsrc = rsrc_id, 66377d8f306SDong Aisheng .clk_type = clk_type, 66477d8f306SDong Aisheng .parents = parents, 66577d8f306SDong Aisheng .num_parents = num_parents, 66677d8f306SDong Aisheng }; 66777d8f306SDong Aisheng struct platform_device *pdev; 66877d8f306SDong Aisheng int ret; 66977d8f306SDong Aisheng 6705964012cSDong Aisheng if (!imx_scu_clk_is_valid(rsrc_id)) 6715964012cSDong Aisheng return ERR_PTR(-EINVAL); 6725964012cSDong Aisheng 67377d8f306SDong Aisheng pdev = platform_device_alloc(name, PLATFORM_DEVID_NONE); 67477d8f306SDong Aisheng if (!pdev) { 67577d8f306SDong Aisheng pr_err("%s: failed to allocate scu clk dev rsrc %d type %d\n", 67677d8f306SDong Aisheng name, rsrc_id, clk_type); 67777d8f306SDong Aisheng return ERR_PTR(-ENOMEM); 67877d8f306SDong Aisheng } 67977d8f306SDong Aisheng 68077d8f306SDong Aisheng ret = platform_device_add_data(pdev, &clk, sizeof(clk)); 68177d8f306SDong Aisheng if (ret) { 68277d8f306SDong Aisheng platform_device_put(pdev); 68377d8f306SDong Aisheng return ERR_PTR(ret); 68477d8f306SDong Aisheng } 68577d8f306SDong Aisheng 68677d8f306SDong Aisheng pdev->driver_override = "imx-scu-clk"; 68777d8f306SDong Aisheng 68877d8f306SDong Aisheng ret = imx_clk_scu_attach_pd(&pdev->dev, rsrc_id); 68977d8f306SDong Aisheng if (ret) 69077d8f306SDong Aisheng pr_warn("%s: failed to attached the power domain %d\n", 69177d8f306SDong Aisheng name, ret); 69277d8f306SDong Aisheng 69377d8f306SDong Aisheng platform_device_add(pdev); 69477d8f306SDong Aisheng 69577d8f306SDong Aisheng /* For API backwards compatiblilty, simply return NULL for success */ 69677d8f306SDong Aisheng return NULL; 69777d8f306SDong Aisheng } 69877d8f306SDong Aisheng 69977d8f306SDong Aisheng void imx_clk_scu_unregister(void) 70077d8f306SDong Aisheng { 70177d8f306SDong Aisheng struct imx_scu_clk_node *clk; 70277d8f306SDong Aisheng int i; 70377d8f306SDong Aisheng 70477d8f306SDong Aisheng for (i = 0; i < IMX_SC_R_LAST; i++) { 70577d8f306SDong Aisheng list_for_each_entry(clk, &imx_scu_clks[i], node) { 70677d8f306SDong Aisheng clk_hw_unregister(clk->hw); 70777d8f306SDong Aisheng kfree(clk); 70877d8f306SDong Aisheng } 70977d8f306SDong Aisheng } 71077d8f306SDong Aisheng } 7115392c5deSDong Aisheng 7125392c5deSDong Aisheng static unsigned long clk_gpr_div_scu_recalc_rate(struct clk_hw *hw, 7135392c5deSDong Aisheng unsigned long parent_rate) 7145392c5deSDong Aisheng { 7155392c5deSDong Aisheng struct clk_gpr_scu *clk = to_clk_gpr_scu(hw); 7165392c5deSDong Aisheng unsigned long rate = 0; 7175392c5deSDong Aisheng u32 val; 7185392c5deSDong Aisheng int err; 7195392c5deSDong Aisheng 7205392c5deSDong Aisheng err = imx_sc_misc_get_control(ccm_ipc_handle, clk->rsrc_id, 7215392c5deSDong Aisheng clk->gpr_id, &val); 7225392c5deSDong Aisheng 7235392c5deSDong Aisheng rate = val ? parent_rate / 2 : parent_rate; 7245392c5deSDong Aisheng 7255392c5deSDong Aisheng return err ? 0 : rate; 7265392c5deSDong Aisheng } 7275392c5deSDong Aisheng 7285392c5deSDong Aisheng static long clk_gpr_div_scu_round_rate(struct clk_hw *hw, unsigned long rate, 7295392c5deSDong Aisheng unsigned long *prate) 7305392c5deSDong Aisheng { 7315392c5deSDong Aisheng if (rate < *prate) 7325392c5deSDong Aisheng rate = *prate / 2; 7335392c5deSDong Aisheng else 7345392c5deSDong Aisheng rate = *prate; 7355392c5deSDong Aisheng 7365392c5deSDong Aisheng return rate; 7375392c5deSDong Aisheng } 7385392c5deSDong Aisheng 7395392c5deSDong Aisheng static int clk_gpr_div_scu_set_rate(struct clk_hw *hw, unsigned long rate, 7405392c5deSDong Aisheng unsigned long parent_rate) 7415392c5deSDong Aisheng { 7425392c5deSDong Aisheng struct clk_gpr_scu *clk = to_clk_gpr_scu(hw); 7435392c5deSDong Aisheng uint32_t val; 7445392c5deSDong Aisheng int err; 7455392c5deSDong Aisheng 7465392c5deSDong Aisheng val = (rate < parent_rate) ? 1 : 0; 7475392c5deSDong Aisheng err = imx_sc_misc_set_control(ccm_ipc_handle, clk->rsrc_id, 7485392c5deSDong Aisheng clk->gpr_id, val); 7495392c5deSDong Aisheng 7505392c5deSDong Aisheng return err ? -EINVAL : 0; 7515392c5deSDong Aisheng } 7525392c5deSDong Aisheng 7535392c5deSDong Aisheng static const struct clk_ops clk_gpr_div_scu_ops = { 7545392c5deSDong Aisheng .recalc_rate = clk_gpr_div_scu_recalc_rate, 7555392c5deSDong Aisheng .round_rate = clk_gpr_div_scu_round_rate, 7565392c5deSDong Aisheng .set_rate = clk_gpr_div_scu_set_rate, 7575392c5deSDong Aisheng }; 7585392c5deSDong Aisheng 7595392c5deSDong Aisheng static u8 clk_gpr_mux_scu_get_parent(struct clk_hw *hw) 7605392c5deSDong Aisheng { 7615392c5deSDong Aisheng struct clk_gpr_scu *clk = to_clk_gpr_scu(hw); 7625392c5deSDong Aisheng u32 val = 0; 7635392c5deSDong Aisheng 7645392c5deSDong Aisheng imx_sc_misc_get_control(ccm_ipc_handle, clk->rsrc_id, 7655392c5deSDong Aisheng clk->gpr_id, &val); 7665392c5deSDong Aisheng 7675392c5deSDong Aisheng return (u8)val; 7685392c5deSDong Aisheng } 7695392c5deSDong Aisheng 7705392c5deSDong Aisheng static int clk_gpr_mux_scu_set_parent(struct clk_hw *hw, u8 index) 7715392c5deSDong Aisheng { 7725392c5deSDong Aisheng struct clk_gpr_scu *clk = to_clk_gpr_scu(hw); 7735392c5deSDong Aisheng 7745392c5deSDong Aisheng return imx_sc_misc_set_control(ccm_ipc_handle, clk->rsrc_id, 7755392c5deSDong Aisheng clk->gpr_id, index); 7765392c5deSDong Aisheng } 7775392c5deSDong Aisheng 7785392c5deSDong Aisheng static const struct clk_ops clk_gpr_mux_scu_ops = { 7795392c5deSDong Aisheng .get_parent = clk_gpr_mux_scu_get_parent, 7805392c5deSDong Aisheng .set_parent = clk_gpr_mux_scu_set_parent, 7815392c5deSDong Aisheng }; 7825392c5deSDong Aisheng 7835392c5deSDong Aisheng static int clk_gpr_gate_scu_prepare(struct clk_hw *hw) 7845392c5deSDong Aisheng { 7855392c5deSDong Aisheng struct clk_gpr_scu *clk = to_clk_gpr_scu(hw); 7865392c5deSDong Aisheng 7875392c5deSDong Aisheng return imx_sc_misc_set_control(ccm_ipc_handle, clk->rsrc_id, 7885392c5deSDong Aisheng clk->gpr_id, !clk->gate_invert); 7895392c5deSDong Aisheng } 7905392c5deSDong Aisheng 7915392c5deSDong Aisheng static void clk_gpr_gate_scu_unprepare(struct clk_hw *hw) 7925392c5deSDong Aisheng { 7935392c5deSDong Aisheng struct clk_gpr_scu *clk = to_clk_gpr_scu(hw); 7945392c5deSDong Aisheng int ret; 7955392c5deSDong Aisheng 7965392c5deSDong Aisheng ret = imx_sc_misc_set_control(ccm_ipc_handle, clk->rsrc_id, 7975392c5deSDong Aisheng clk->gpr_id, clk->gate_invert); 7985392c5deSDong Aisheng if (ret) 7995392c5deSDong Aisheng pr_err("%s: clk unprepare failed %d\n", clk_hw_get_name(hw), 8005392c5deSDong Aisheng ret); 8015392c5deSDong Aisheng } 8025392c5deSDong Aisheng 8035392c5deSDong Aisheng static int clk_gpr_gate_scu_is_prepared(struct clk_hw *hw) 8045392c5deSDong Aisheng { 8055392c5deSDong Aisheng struct clk_gpr_scu *clk = to_clk_gpr_scu(hw); 8065392c5deSDong Aisheng int ret; 8075392c5deSDong Aisheng u32 val; 8085392c5deSDong Aisheng 8095392c5deSDong Aisheng ret = imx_sc_misc_get_control(ccm_ipc_handle, clk->rsrc_id, 8105392c5deSDong Aisheng clk->gpr_id, &val); 8115392c5deSDong Aisheng if (ret) 8125392c5deSDong Aisheng return ret; 8135392c5deSDong Aisheng 8145392c5deSDong Aisheng return clk->gate_invert ? !val : val; 8155392c5deSDong Aisheng } 8165392c5deSDong Aisheng 8175392c5deSDong Aisheng static const struct clk_ops clk_gpr_gate_scu_ops = { 8185392c5deSDong Aisheng .prepare = clk_gpr_gate_scu_prepare, 8195392c5deSDong Aisheng .unprepare = clk_gpr_gate_scu_unprepare, 8205392c5deSDong Aisheng .is_prepared = clk_gpr_gate_scu_is_prepared, 8215392c5deSDong Aisheng }; 8225392c5deSDong Aisheng 8235392c5deSDong Aisheng struct clk_hw *__imx_clk_gpr_scu(const char *name, const char * const *parent_name, 8245392c5deSDong Aisheng int num_parents, u32 rsrc_id, u8 gpr_id, u8 flags, 8255392c5deSDong Aisheng bool invert) 8265392c5deSDong Aisheng { 8275392c5deSDong Aisheng struct imx_scu_clk_node *clk_node; 8285392c5deSDong Aisheng struct clk_gpr_scu *clk; 8295392c5deSDong Aisheng struct clk_hw *hw; 8305392c5deSDong Aisheng struct clk_init_data init; 8315392c5deSDong Aisheng int ret; 8325392c5deSDong Aisheng 8335392c5deSDong Aisheng if (rsrc_id >= IMX_SC_R_LAST || gpr_id >= IMX_SC_C_LAST) 8345392c5deSDong Aisheng return ERR_PTR(-EINVAL); 8355392c5deSDong Aisheng 8365392c5deSDong Aisheng clk_node = kzalloc(sizeof(*clk_node), GFP_KERNEL); 8375392c5deSDong Aisheng if (!clk_node) 8385392c5deSDong Aisheng return ERR_PTR(-ENOMEM); 8395392c5deSDong Aisheng 8402759f38bSXiaoke Wang if (!imx_scu_clk_is_valid(rsrc_id)) { 8412759f38bSXiaoke Wang kfree(clk_node); 8425964012cSDong Aisheng return ERR_PTR(-EINVAL); 8432759f38bSXiaoke Wang } 8445964012cSDong Aisheng 8455392c5deSDong Aisheng clk = kzalloc(sizeof(*clk), GFP_KERNEL); 8465392c5deSDong Aisheng if (!clk) { 8475392c5deSDong Aisheng kfree(clk_node); 8485392c5deSDong Aisheng return ERR_PTR(-ENOMEM); 8495392c5deSDong Aisheng } 8505392c5deSDong Aisheng 8515392c5deSDong Aisheng clk->rsrc_id = rsrc_id; 8525392c5deSDong Aisheng clk->gpr_id = gpr_id; 8535392c5deSDong Aisheng clk->flags = flags; 8545392c5deSDong Aisheng clk->gate_invert = invert; 8555392c5deSDong Aisheng 8565392c5deSDong Aisheng if (flags & IMX_SCU_GPR_CLK_GATE) 8575392c5deSDong Aisheng init.ops = &clk_gpr_gate_scu_ops; 8585392c5deSDong Aisheng 8595392c5deSDong Aisheng if (flags & IMX_SCU_GPR_CLK_DIV) 8605392c5deSDong Aisheng init.ops = &clk_gpr_div_scu_ops; 8615392c5deSDong Aisheng 8625392c5deSDong Aisheng if (flags & IMX_SCU_GPR_CLK_MUX) 8635392c5deSDong Aisheng init.ops = &clk_gpr_mux_scu_ops; 8645392c5deSDong Aisheng 8655392c5deSDong Aisheng init.flags = 0; 8665392c5deSDong Aisheng init.name = name; 8675392c5deSDong Aisheng init.parent_names = parent_name; 8685392c5deSDong Aisheng init.num_parents = num_parents; 8695392c5deSDong Aisheng 8705392c5deSDong Aisheng clk->hw.init = &init; 8715392c5deSDong Aisheng 8725392c5deSDong Aisheng hw = &clk->hw; 8735392c5deSDong Aisheng ret = clk_hw_register(NULL, hw); 8745392c5deSDong Aisheng if (ret) { 8755392c5deSDong Aisheng kfree(clk); 8765392c5deSDong Aisheng kfree(clk_node); 8775392c5deSDong Aisheng hw = ERR_PTR(ret); 8785392c5deSDong Aisheng } else { 8795392c5deSDong Aisheng clk_node->hw = hw; 8805392c5deSDong Aisheng clk_node->clk_type = gpr_id; 8815392c5deSDong Aisheng list_add_tail(&clk_node->node, &imx_scu_clks[rsrc_id]); 8825392c5deSDong Aisheng } 8835392c5deSDong Aisheng 8845392c5deSDong Aisheng return hw; 8855392c5deSDong Aisheng } 886