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 */ 53d0409631SDong Aisheng bool is_enabled; 54d0409631SDong Aisheng u32 rate; 55fe37b482SAisheng Dong }; 56fe37b482SAisheng Dong 57fe37b482SAisheng Dong /* 585392c5deSDong Aisheng * struct clk_gpr_scu - Description of one SCU GPR clock 595392c5deSDong Aisheng * @hw: the common clk_hw 605392c5deSDong Aisheng * @rsrc_id: resource ID of this SCU clock 615392c5deSDong Aisheng * @gpr_id: GPR ID index to control the divider 625392c5deSDong Aisheng */ 635392c5deSDong Aisheng struct clk_gpr_scu { 645392c5deSDong Aisheng struct clk_hw hw; 655392c5deSDong Aisheng u16 rsrc_id; 665392c5deSDong Aisheng u8 gpr_id; 675392c5deSDong Aisheng u8 flags; 685392c5deSDong Aisheng bool gate_invert; 695392c5deSDong Aisheng }; 705392c5deSDong Aisheng 715392c5deSDong Aisheng #define to_clk_gpr_scu(_hw) container_of(_hw, struct clk_gpr_scu, hw) 725392c5deSDong Aisheng 735392c5deSDong Aisheng /* 74fe37b482SAisheng Dong * struct imx_sc_msg_req_set_clock_rate - clock set rate protocol 75fe37b482SAisheng Dong * @hdr: SCU protocol header 76fe37b482SAisheng Dong * @rate: rate to set 77fe37b482SAisheng Dong * @resource: clock resource to set rate 78fe37b482SAisheng Dong * @clk: clk type of this resource 79fe37b482SAisheng Dong * 80fe37b482SAisheng Dong * This structure describes the SCU protocol of clock rate set 81fe37b482SAisheng Dong */ 82fe37b482SAisheng Dong struct imx_sc_msg_req_set_clock_rate { 83fe37b482SAisheng Dong struct imx_sc_rpc_msg hdr; 84fe37b482SAisheng Dong __le32 rate; 85fe37b482SAisheng Dong __le16 resource; 86fe37b482SAisheng Dong u8 clk; 87a0ae04a2SLeonard Crestez } __packed __aligned(4); 88fe37b482SAisheng Dong 89fe37b482SAisheng Dong struct req_get_clock_rate { 90fe37b482SAisheng Dong __le16 resource; 91fe37b482SAisheng Dong u8 clk; 92a0ae04a2SLeonard Crestez } __packed __aligned(4); 93fe37b482SAisheng Dong 94fe37b482SAisheng Dong struct resp_get_clock_rate { 95fe37b482SAisheng Dong __le32 rate; 96fe37b482SAisheng Dong }; 97fe37b482SAisheng Dong 98fe37b482SAisheng Dong /* 99fe37b482SAisheng Dong * struct imx_sc_msg_get_clock_rate - clock get rate protocol 100fe37b482SAisheng Dong * @hdr: SCU protocol header 101fe37b482SAisheng Dong * @req: get rate request protocol 102fe37b482SAisheng Dong * @resp: get rate response protocol 103fe37b482SAisheng Dong * 104fe37b482SAisheng Dong * This structure describes the SCU protocol of clock rate get 105fe37b482SAisheng Dong */ 106fe37b482SAisheng Dong struct imx_sc_msg_get_clock_rate { 107fe37b482SAisheng Dong struct imx_sc_rpc_msg hdr; 108fe37b482SAisheng Dong union { 109fe37b482SAisheng Dong struct req_get_clock_rate req; 110fe37b482SAisheng Dong struct resp_get_clock_rate resp; 111fe37b482SAisheng Dong } data; 112fe37b482SAisheng Dong }; 113fe37b482SAisheng Dong 114fe37b482SAisheng Dong /* 115666aed2dSAisheng Dong * struct imx_sc_msg_get_clock_parent - clock get parent protocol 116666aed2dSAisheng Dong * @hdr: SCU protocol header 117666aed2dSAisheng Dong * @req: get parent request protocol 118666aed2dSAisheng Dong * @resp: get parent response protocol 119666aed2dSAisheng Dong * 120666aed2dSAisheng Dong * This structure describes the SCU protocol of clock get parent 121666aed2dSAisheng Dong */ 122666aed2dSAisheng Dong struct imx_sc_msg_get_clock_parent { 123666aed2dSAisheng Dong struct imx_sc_rpc_msg hdr; 124666aed2dSAisheng Dong union { 125666aed2dSAisheng Dong struct req_get_clock_parent { 126666aed2dSAisheng Dong __le16 resource; 127666aed2dSAisheng Dong u8 clk; 1288400ab88SLeonard Crestez } __packed __aligned(4) req; 129666aed2dSAisheng Dong struct resp_get_clock_parent { 130666aed2dSAisheng Dong u8 parent; 131666aed2dSAisheng Dong } resp; 132666aed2dSAisheng Dong } data; 133666aed2dSAisheng Dong }; 134666aed2dSAisheng Dong 135666aed2dSAisheng Dong /* 136666aed2dSAisheng Dong * struct imx_sc_msg_set_clock_parent - clock set parent protocol 137666aed2dSAisheng Dong * @hdr: SCU protocol header 138666aed2dSAisheng Dong * @req: set parent request protocol 139666aed2dSAisheng Dong * 140666aed2dSAisheng Dong * This structure describes the SCU protocol of clock set parent 141666aed2dSAisheng Dong */ 142666aed2dSAisheng Dong struct imx_sc_msg_set_clock_parent { 143666aed2dSAisheng Dong struct imx_sc_rpc_msg hdr; 144666aed2dSAisheng Dong __le16 resource; 145666aed2dSAisheng Dong u8 clk; 146666aed2dSAisheng Dong u8 parent; 147666aed2dSAisheng Dong } __packed; 148666aed2dSAisheng Dong 149666aed2dSAisheng Dong /* 150fe37b482SAisheng Dong * struct imx_sc_msg_req_clock_enable - clock gate protocol 151fe37b482SAisheng Dong * @hdr: SCU protocol header 152fe37b482SAisheng Dong * @resource: clock resource to gate 153fe37b482SAisheng Dong * @clk: clk type of this resource 154fe37b482SAisheng Dong * @enable: whether gate off the clock 155fe37b482SAisheng Dong * @autog: HW auto gate enable 156fe37b482SAisheng Dong * 157fe37b482SAisheng Dong * This structure describes the SCU protocol of clock gate 158fe37b482SAisheng Dong */ 159fe37b482SAisheng Dong struct imx_sc_msg_req_clock_enable { 160fe37b482SAisheng Dong struct imx_sc_rpc_msg hdr; 161fe37b482SAisheng Dong __le16 resource; 162fe37b482SAisheng Dong u8 clk; 163fe37b482SAisheng Dong u8 enable; 164fe37b482SAisheng Dong u8 autog; 165a0ae04a2SLeonard Crestez } __packed __aligned(4); 166fe37b482SAisheng Dong 167fe37b482SAisheng Dong static inline struct clk_scu *to_clk_scu(struct clk_hw *hw) 168fe37b482SAisheng Dong { 169fe37b482SAisheng Dong return container_of(hw, struct clk_scu, hw); 170fe37b482SAisheng Dong } 171fe37b482SAisheng Dong 1725964012cSDong Aisheng static inline int imx_scu_clk_search_cmp(const void *rsrc, const void *rsrc_p) 1735964012cSDong Aisheng { 1745964012cSDong Aisheng return *(u32 *)rsrc - *(u32 *)rsrc_p; 1755964012cSDong Aisheng } 1765964012cSDong Aisheng 1775964012cSDong Aisheng static bool imx_scu_clk_is_valid(u32 rsrc_id) 1785964012cSDong Aisheng { 1795964012cSDong Aisheng void *p; 1805964012cSDong Aisheng 1815964012cSDong Aisheng if (!rsrc_table) 1825964012cSDong Aisheng return true; 1835964012cSDong Aisheng 1845964012cSDong Aisheng p = bsearch(&rsrc_id, rsrc_table->rsrc, rsrc_table->num, 1855964012cSDong Aisheng sizeof(rsrc_table->rsrc[0]), imx_scu_clk_search_cmp); 1865964012cSDong Aisheng 1875964012cSDong Aisheng return p != NULL; 1885964012cSDong Aisheng } 1895964012cSDong Aisheng 1905964012cSDong Aisheng int imx_clk_scu_init(struct device_node *np, 1915964012cSDong Aisheng const struct imx_clk_scu_rsrc_table *data) 192fe37b482SAisheng Dong { 19377d8f306SDong Aisheng u32 clk_cells; 19477d8f306SDong Aisheng int ret, i; 19577d8f306SDong Aisheng 19677d8f306SDong Aisheng ret = imx_scu_get_handle(&ccm_ipc_handle); 19777d8f306SDong Aisheng if (ret) 19877d8f306SDong Aisheng return ret; 19977d8f306SDong Aisheng 20077d8f306SDong Aisheng of_property_read_u32(np, "#clock-cells", &clk_cells); 20177d8f306SDong Aisheng 20277d8f306SDong Aisheng if (clk_cells == 2) { 20377d8f306SDong Aisheng for (i = 0; i < IMX_SC_R_LAST; i++) 20477d8f306SDong Aisheng INIT_LIST_HEAD(&imx_scu_clks[i]); 20543d24796SDong Aisheng 20643d24796SDong Aisheng /* pd_np will be used to attach power domains later */ 20777d8f306SDong Aisheng pd_np = of_find_compatible_node(NULL, NULL, "fsl,scu-pd"); 20843d24796SDong Aisheng if (!pd_np) 20943d24796SDong Aisheng return -EINVAL; 2105964012cSDong Aisheng 2115964012cSDong Aisheng rsrc_table = data; 21277d8f306SDong Aisheng } 21377d8f306SDong Aisheng 214220175cdSDong Aisheng return platform_driver_register(&imx_clk_scu_driver); 215fe37b482SAisheng Dong } 216fe37b482SAisheng Dong 217fe37b482SAisheng Dong /* 218fe37b482SAisheng Dong * clk_scu_recalc_rate - Get clock rate for a SCU clock 219fe37b482SAisheng Dong * @hw: clock to get rate for 220fe37b482SAisheng Dong * @parent_rate: parent rate provided by common clock framework, not used 221fe37b482SAisheng Dong * 222fe37b482SAisheng Dong * Gets the current clock rate of a SCU clock. Returns the current 223fe37b482SAisheng Dong * clock rate, or zero in failure. 224fe37b482SAisheng Dong */ 225fe37b482SAisheng Dong static unsigned long clk_scu_recalc_rate(struct clk_hw *hw, 226fe37b482SAisheng Dong unsigned long parent_rate) 227fe37b482SAisheng Dong { 228fe37b482SAisheng Dong struct clk_scu *clk = to_clk_scu(hw); 229fe37b482SAisheng Dong struct imx_sc_msg_get_clock_rate msg; 230fe37b482SAisheng Dong struct imx_sc_rpc_msg *hdr = &msg.hdr; 231fe37b482SAisheng Dong int ret; 232fe37b482SAisheng Dong 233fe37b482SAisheng Dong hdr->ver = IMX_SC_RPC_VERSION; 234fe37b482SAisheng Dong hdr->svc = IMX_SC_RPC_SVC_PM; 235fe37b482SAisheng Dong hdr->func = IMX_SC_PM_FUNC_GET_CLOCK_RATE; 236fe37b482SAisheng Dong hdr->size = 2; 237fe37b482SAisheng Dong 238fe37b482SAisheng Dong msg.data.req.resource = cpu_to_le16(clk->rsrc_id); 239fe37b482SAisheng Dong msg.data.req.clk = clk->clk_type; 240fe37b482SAisheng Dong 241fe37b482SAisheng Dong ret = imx_scu_call_rpc(ccm_ipc_handle, &msg, true); 242fe37b482SAisheng Dong if (ret) { 243fe37b482SAisheng Dong pr_err("%s: failed to get clock rate %d\n", 244fe37b482SAisheng Dong clk_hw_get_name(hw), ret); 245fe37b482SAisheng Dong return 0; 246fe37b482SAisheng Dong } 247fe37b482SAisheng Dong 248fe37b482SAisheng Dong return le32_to_cpu(msg.data.resp.rate); 249fe37b482SAisheng Dong } 250fe37b482SAisheng Dong 251fe37b482SAisheng Dong /* 252fe37b482SAisheng Dong * clk_scu_round_rate - Round clock rate for a SCU clock 253fe37b482SAisheng Dong * @hw: clock to round rate for 254fe37b482SAisheng Dong * @rate: rate to round 255fe37b482SAisheng Dong * @parent_rate: parent rate provided by common clock framework, not used 256fe37b482SAisheng Dong * 257fe37b482SAisheng Dong * Returns the current clock rate, or zero in failure. 258fe37b482SAisheng Dong */ 259fe37b482SAisheng Dong static long clk_scu_round_rate(struct clk_hw *hw, unsigned long rate, 260fe37b482SAisheng Dong unsigned long *parent_rate) 261fe37b482SAisheng Dong { 262fe37b482SAisheng Dong /* 263fe37b482SAisheng Dong * Assume we support all the requested rate and let the SCU firmware 264fe37b482SAisheng Dong * to handle the left work 265fe37b482SAisheng Dong */ 266fe37b482SAisheng Dong return rate; 267fe37b482SAisheng Dong } 268fe37b482SAisheng Dong 2693b9ea606SAnson Huang static int clk_scu_atf_set_cpu_rate(struct clk_hw *hw, unsigned long rate, 2703b9ea606SAnson Huang unsigned long parent_rate) 2713b9ea606SAnson Huang { 2723b9ea606SAnson Huang struct clk_scu *clk = to_clk_scu(hw); 2733b9ea606SAnson Huang struct arm_smccc_res res; 2743b9ea606SAnson Huang unsigned long cluster_id; 2753b9ea606SAnson Huang 2763b9ea606SAnson Huang if (clk->rsrc_id == IMX_SC_R_A35) 2773b9ea606SAnson Huang cluster_id = 0; 2783b9ea606SAnson Huang else 2793b9ea606SAnson Huang return -EINVAL; 2803b9ea606SAnson Huang 2813b9ea606SAnson Huang /* CPU frequency scaling can ONLY be done by ARM-Trusted-Firmware */ 2823b9ea606SAnson Huang arm_smccc_smc(IMX_SIP_CPUFREQ, IMX_SIP_SET_CPUFREQ, 2833b9ea606SAnson Huang cluster_id, rate, 0, 0, 0, 0, &res); 2843b9ea606SAnson Huang 2853b9ea606SAnson Huang return 0; 2863b9ea606SAnson Huang } 2873b9ea606SAnson Huang 288fe37b482SAisheng Dong /* 289fe37b482SAisheng Dong * clk_scu_set_rate - Set rate for a SCU clock 290fe37b482SAisheng Dong * @hw: clock to change rate for 291fe37b482SAisheng Dong * @rate: target rate for the clock 292fe37b482SAisheng Dong * @parent_rate: rate of the clock parent, not used for SCU clocks 293fe37b482SAisheng Dong * 294fe37b482SAisheng Dong * Sets a clock frequency for a SCU clock. Returns the SCU 295fe37b482SAisheng Dong * protocol status. 296fe37b482SAisheng Dong */ 297fe37b482SAisheng Dong static int clk_scu_set_rate(struct clk_hw *hw, unsigned long rate, 298fe37b482SAisheng Dong unsigned long parent_rate) 299fe37b482SAisheng Dong { 300fe37b482SAisheng Dong struct clk_scu *clk = to_clk_scu(hw); 301fe37b482SAisheng Dong struct imx_sc_msg_req_set_clock_rate msg; 302fe37b482SAisheng Dong struct imx_sc_rpc_msg *hdr = &msg.hdr; 303fe37b482SAisheng Dong 304fe37b482SAisheng Dong hdr->ver = IMX_SC_RPC_VERSION; 305fe37b482SAisheng Dong hdr->svc = IMX_SC_RPC_SVC_PM; 306fe37b482SAisheng Dong hdr->func = IMX_SC_PM_FUNC_SET_CLOCK_RATE; 307fe37b482SAisheng Dong hdr->size = 3; 308fe37b482SAisheng Dong 309fe37b482SAisheng Dong msg.rate = cpu_to_le32(rate); 310fe37b482SAisheng Dong msg.resource = cpu_to_le16(clk->rsrc_id); 311fe37b482SAisheng Dong msg.clk = clk->clk_type; 312fe37b482SAisheng Dong 313fe37b482SAisheng Dong return imx_scu_call_rpc(ccm_ipc_handle, &msg, true); 314fe37b482SAisheng Dong } 315fe37b482SAisheng Dong 316666aed2dSAisheng Dong static u8 clk_scu_get_parent(struct clk_hw *hw) 317666aed2dSAisheng Dong { 318666aed2dSAisheng Dong struct clk_scu *clk = to_clk_scu(hw); 319666aed2dSAisheng Dong struct imx_sc_msg_get_clock_parent msg; 320666aed2dSAisheng Dong struct imx_sc_rpc_msg *hdr = &msg.hdr; 321666aed2dSAisheng Dong int ret; 322666aed2dSAisheng Dong 323666aed2dSAisheng Dong hdr->ver = IMX_SC_RPC_VERSION; 324666aed2dSAisheng Dong hdr->svc = IMX_SC_RPC_SVC_PM; 325666aed2dSAisheng Dong hdr->func = IMX_SC_PM_FUNC_GET_CLOCK_PARENT; 326666aed2dSAisheng Dong hdr->size = 2; 327666aed2dSAisheng Dong 328666aed2dSAisheng Dong msg.data.req.resource = cpu_to_le16(clk->rsrc_id); 329666aed2dSAisheng Dong msg.data.req.clk = clk->clk_type; 330666aed2dSAisheng Dong 331666aed2dSAisheng Dong ret = imx_scu_call_rpc(ccm_ipc_handle, &msg, true); 332666aed2dSAisheng Dong if (ret) { 333666aed2dSAisheng Dong pr_err("%s: failed to get clock parent %d\n", 334666aed2dSAisheng Dong clk_hw_get_name(hw), ret); 335666aed2dSAisheng Dong return 0; 336666aed2dSAisheng Dong } 337666aed2dSAisheng Dong 338666aed2dSAisheng Dong return msg.data.resp.parent; 339666aed2dSAisheng Dong } 340666aed2dSAisheng Dong 341666aed2dSAisheng Dong static int clk_scu_set_parent(struct clk_hw *hw, u8 index) 342666aed2dSAisheng Dong { 343666aed2dSAisheng Dong struct clk_scu *clk = to_clk_scu(hw); 344666aed2dSAisheng Dong struct imx_sc_msg_set_clock_parent msg; 345666aed2dSAisheng Dong struct imx_sc_rpc_msg *hdr = &msg.hdr; 346666aed2dSAisheng Dong 347666aed2dSAisheng Dong hdr->ver = IMX_SC_RPC_VERSION; 348666aed2dSAisheng Dong hdr->svc = IMX_SC_RPC_SVC_PM; 349666aed2dSAisheng Dong hdr->func = IMX_SC_PM_FUNC_SET_CLOCK_PARENT; 350666aed2dSAisheng Dong hdr->size = 2; 351666aed2dSAisheng Dong 352666aed2dSAisheng Dong msg.resource = cpu_to_le16(clk->rsrc_id); 353666aed2dSAisheng Dong msg.clk = clk->clk_type; 354666aed2dSAisheng Dong msg.parent = index; 355666aed2dSAisheng Dong 356666aed2dSAisheng Dong return imx_scu_call_rpc(ccm_ipc_handle, &msg, true); 357666aed2dSAisheng Dong } 358666aed2dSAisheng Dong 359fe37b482SAisheng Dong static int sc_pm_clock_enable(struct imx_sc_ipc *ipc, u16 resource, 360fe37b482SAisheng Dong u8 clk, bool enable, bool autog) 361fe37b482SAisheng Dong { 362fe37b482SAisheng Dong struct imx_sc_msg_req_clock_enable msg; 363fe37b482SAisheng Dong struct imx_sc_rpc_msg *hdr = &msg.hdr; 364fe37b482SAisheng Dong 365fe37b482SAisheng Dong hdr->ver = IMX_SC_RPC_VERSION; 366fe37b482SAisheng Dong hdr->svc = IMX_SC_RPC_SVC_PM; 367fe37b482SAisheng Dong hdr->func = IMX_SC_PM_FUNC_CLOCK_ENABLE; 368fe37b482SAisheng Dong hdr->size = 3; 369fe37b482SAisheng Dong 370fe37b482SAisheng Dong msg.resource = cpu_to_le16(resource); 371fe37b482SAisheng Dong msg.clk = clk; 372fe37b482SAisheng Dong msg.enable = enable; 373fe37b482SAisheng Dong msg.autog = autog; 374fe37b482SAisheng Dong 375fe37b482SAisheng Dong return imx_scu_call_rpc(ccm_ipc_handle, &msg, true); 376fe37b482SAisheng Dong } 377fe37b482SAisheng Dong 378fe37b482SAisheng Dong /* 379fe37b482SAisheng Dong * clk_scu_prepare - Enable a SCU clock 380fe37b482SAisheng Dong * @hw: clock to enable 381fe37b482SAisheng Dong * 382fe37b482SAisheng Dong * Enable the clock at the DSC slice level 383fe37b482SAisheng Dong */ 384fe37b482SAisheng Dong static int clk_scu_prepare(struct clk_hw *hw) 385fe37b482SAisheng Dong { 386fe37b482SAisheng Dong struct clk_scu *clk = to_clk_scu(hw); 387fe37b482SAisheng Dong 388fe37b482SAisheng Dong return sc_pm_clock_enable(ccm_ipc_handle, clk->rsrc_id, 389fe37b482SAisheng Dong clk->clk_type, true, false); 390fe37b482SAisheng Dong } 391fe37b482SAisheng Dong 392fe37b482SAisheng Dong /* 393fe37b482SAisheng Dong * clk_scu_unprepare - Disable a SCU clock 394fe37b482SAisheng Dong * @hw: clock to enable 395fe37b482SAisheng Dong * 396fe37b482SAisheng Dong * Disable the clock at the DSC slice level 397fe37b482SAisheng Dong */ 398fe37b482SAisheng Dong static void clk_scu_unprepare(struct clk_hw *hw) 399fe37b482SAisheng Dong { 400fe37b482SAisheng Dong struct clk_scu *clk = to_clk_scu(hw); 401fe37b482SAisheng Dong int ret; 402fe37b482SAisheng Dong 403fe37b482SAisheng Dong ret = sc_pm_clock_enable(ccm_ipc_handle, clk->rsrc_id, 404fe37b482SAisheng Dong clk->clk_type, false, false); 405fe37b482SAisheng Dong if (ret) 406fe37b482SAisheng Dong pr_warn("%s: clk unprepare failed %d\n", clk_hw_get_name(hw), 407fe37b482SAisheng Dong ret); 408fe37b482SAisheng Dong } 409fe37b482SAisheng Dong 410fe37b482SAisheng Dong static const struct clk_ops clk_scu_ops = { 411fe37b482SAisheng Dong .recalc_rate = clk_scu_recalc_rate, 412fe37b482SAisheng Dong .round_rate = clk_scu_round_rate, 413fe37b482SAisheng Dong .set_rate = clk_scu_set_rate, 414666aed2dSAisheng Dong .get_parent = clk_scu_get_parent, 415666aed2dSAisheng Dong .set_parent = clk_scu_set_parent, 416fe37b482SAisheng Dong .prepare = clk_scu_prepare, 417fe37b482SAisheng Dong .unprepare = clk_scu_unprepare, 418fe37b482SAisheng Dong }; 419fe37b482SAisheng Dong 4203b9ea606SAnson Huang static const struct clk_ops clk_scu_cpu_ops = { 4213b9ea606SAnson Huang .recalc_rate = clk_scu_recalc_rate, 4223b9ea606SAnson Huang .round_rate = clk_scu_round_rate, 4233b9ea606SAnson Huang .set_rate = clk_scu_atf_set_cpu_rate, 4243b9ea606SAnson Huang .prepare = clk_scu_prepare, 4253b9ea606SAnson Huang .unprepare = clk_scu_unprepare, 4263b9ea606SAnson Huang }; 4273b9ea606SAnson Huang 428b192d040SGuoniu.zhou static const struct clk_ops clk_scu_pi_ops = { 429b192d040SGuoniu.zhou .recalc_rate = clk_scu_recalc_rate, 430b192d040SGuoniu.zhou .round_rate = clk_scu_round_rate, 431b192d040SGuoniu.zhou .set_rate = clk_scu_set_rate, 432b192d040SGuoniu.zhou }; 433b192d040SGuoniu.zhou 4342f1a2c1dSDong Aisheng struct clk_hw *__imx_clk_scu(struct device *dev, const char *name, 4352f1a2c1dSDong Aisheng const char * const *parents, int num_parents, 4362f1a2c1dSDong Aisheng u32 rsrc_id, u8 clk_type) 437fe37b482SAisheng Dong { 438fe37b482SAisheng Dong struct clk_init_data init; 439fe37b482SAisheng Dong struct clk_scu *clk; 440fe37b482SAisheng Dong struct clk_hw *hw; 441fe37b482SAisheng Dong int ret; 442fe37b482SAisheng Dong 443fe37b482SAisheng Dong clk = kzalloc(sizeof(*clk), GFP_KERNEL); 444fe37b482SAisheng Dong if (!clk) 445fe37b482SAisheng Dong return ERR_PTR(-ENOMEM); 446fe37b482SAisheng Dong 447fe37b482SAisheng Dong clk->rsrc_id = rsrc_id; 448fe37b482SAisheng Dong clk->clk_type = clk_type; 449fe37b482SAisheng Dong 450fe37b482SAisheng Dong init.name = name; 451fe37b482SAisheng Dong init.ops = &clk_scu_ops; 4523b9ea606SAnson Huang if (rsrc_id == IMX_SC_R_A35) 4533b9ea606SAnson Huang init.ops = &clk_scu_cpu_ops; 454b192d040SGuoniu.zhou else if (rsrc_id == IMX_SC_R_PI_0_PLL) 455b192d040SGuoniu.zhou init.ops = &clk_scu_pi_ops; 4563b9ea606SAnson Huang else 4573b9ea606SAnson Huang init.ops = &clk_scu_ops; 458666aed2dSAisheng Dong init.parent_names = parents; 459666aed2dSAisheng Dong init.num_parents = num_parents; 460666aed2dSAisheng Dong 461fe37b482SAisheng Dong /* 462fe37b482SAisheng Dong * Note on MX8, the clocks are tightly coupled with power domain 463fe37b482SAisheng Dong * that once the power domain is off, the clock status may be 464fe37b482SAisheng Dong * lost. So we make it NOCACHE to let user to retrieve the real 465fe37b482SAisheng Dong * clock status from HW instead of using the possible invalid 466fe37b482SAisheng Dong * cached rate. 467fe37b482SAisheng Dong */ 468fe37b482SAisheng Dong init.flags = CLK_GET_RATE_NOCACHE; 469fe37b482SAisheng Dong clk->hw.init = &init; 470fe37b482SAisheng Dong 471fe37b482SAisheng Dong hw = &clk->hw; 4722f1a2c1dSDong Aisheng ret = clk_hw_register(dev, hw); 473fe37b482SAisheng Dong if (ret) { 474fe37b482SAisheng Dong kfree(clk); 475fe37b482SAisheng Dong hw = ERR_PTR(ret); 476054ef44eSJian Dong return hw; 477fe37b482SAisheng Dong } 478fe37b482SAisheng Dong 479d0409631SDong Aisheng if (dev) 480d0409631SDong Aisheng dev_set_drvdata(dev, clk); 481d0409631SDong Aisheng 482fe37b482SAisheng Dong return hw; 483fe37b482SAisheng Dong } 48477d8f306SDong Aisheng 48577d8f306SDong Aisheng struct clk_hw *imx_scu_of_clk_src_get(struct of_phandle_args *clkspec, 48677d8f306SDong Aisheng void *data) 48777d8f306SDong Aisheng { 48877d8f306SDong Aisheng unsigned int rsrc = clkspec->args[0]; 48977d8f306SDong Aisheng unsigned int idx = clkspec->args[1]; 49077d8f306SDong Aisheng struct list_head *scu_clks = data; 49177d8f306SDong Aisheng struct imx_scu_clk_node *clk; 49277d8f306SDong Aisheng 49377d8f306SDong Aisheng list_for_each_entry(clk, &scu_clks[rsrc], node) { 49477d8f306SDong Aisheng if (clk->clk_type == idx) 49577d8f306SDong Aisheng return clk->hw; 49677d8f306SDong Aisheng } 49777d8f306SDong Aisheng 49877d8f306SDong Aisheng return ERR_PTR(-ENODEV); 49977d8f306SDong Aisheng } 50077d8f306SDong Aisheng 50177d8f306SDong Aisheng static int imx_clk_scu_probe(struct platform_device *pdev) 50277d8f306SDong Aisheng { 50377d8f306SDong Aisheng struct device *dev = &pdev->dev; 50477d8f306SDong Aisheng struct imx_scu_clk_node *clk = dev_get_platdata(dev); 50577d8f306SDong Aisheng struct clk_hw *hw; 50678edeb08SDong Aisheng int ret; 50777d8f306SDong Aisheng 50878edeb08SDong Aisheng pm_runtime_set_suspended(dev); 50978edeb08SDong Aisheng pm_runtime_set_autosuspend_delay(dev, 50); 51078edeb08SDong Aisheng pm_runtime_use_autosuspend(&pdev->dev); 51178edeb08SDong Aisheng pm_runtime_enable(dev); 51278edeb08SDong Aisheng 51378edeb08SDong Aisheng ret = pm_runtime_get_sync(dev); 51478edeb08SDong Aisheng if (ret) { 5156618b5aaSDong Aisheng pm_genpd_remove_device(dev); 51678edeb08SDong Aisheng pm_runtime_disable(dev); 51778edeb08SDong Aisheng return ret; 51878edeb08SDong Aisheng } 51978edeb08SDong Aisheng 52078edeb08SDong Aisheng hw = __imx_clk_scu(dev, clk->name, clk->parents, clk->num_parents, 52177d8f306SDong Aisheng clk->rsrc, clk->clk_type); 52278edeb08SDong Aisheng if (IS_ERR(hw)) { 52378edeb08SDong Aisheng pm_runtime_disable(dev); 52477d8f306SDong Aisheng return PTR_ERR(hw); 52578edeb08SDong Aisheng } 52677d8f306SDong Aisheng 52777d8f306SDong Aisheng clk->hw = hw; 52877d8f306SDong Aisheng list_add_tail(&clk->node, &imx_scu_clks[clk->rsrc]); 52977d8f306SDong Aisheng 53078edeb08SDong Aisheng pm_runtime_mark_last_busy(&pdev->dev); 53178edeb08SDong Aisheng pm_runtime_put_autosuspend(&pdev->dev); 53278edeb08SDong Aisheng 53377d8f306SDong Aisheng dev_dbg(dev, "register SCU clock rsrc:%d type:%d\n", clk->rsrc, 53477d8f306SDong Aisheng clk->clk_type); 53577d8f306SDong Aisheng 53677d8f306SDong Aisheng return 0; 53777d8f306SDong Aisheng } 53877d8f306SDong Aisheng 539d0409631SDong Aisheng static int __maybe_unused imx_clk_scu_suspend(struct device *dev) 540d0409631SDong Aisheng { 541d0409631SDong Aisheng struct clk_scu *clk = dev_get_drvdata(dev); 542aecf425fSDong Aisheng u32 rsrc_id = clk->rsrc_id; 543aecf425fSDong Aisheng 544aecf425fSDong Aisheng if ((rsrc_id == IMX_SC_R_A35) || (rsrc_id == IMX_SC_R_A53) || 545aecf425fSDong Aisheng (rsrc_id == IMX_SC_R_A72)) 546aecf425fSDong Aisheng return 0; 547d0409631SDong Aisheng 548d0409631SDong Aisheng clk->rate = clk_hw_get_rate(&clk->hw); 549d0409631SDong Aisheng clk->is_enabled = clk_hw_is_enabled(&clk->hw); 550d0409631SDong Aisheng 551d0409631SDong Aisheng if (clk->rate) 552d0409631SDong Aisheng dev_dbg(dev, "save rate %d\n", clk->rate); 553d0409631SDong Aisheng 554d0409631SDong Aisheng if (clk->is_enabled) 555d0409631SDong Aisheng dev_dbg(dev, "save enabled state\n"); 556d0409631SDong Aisheng 557d0409631SDong Aisheng return 0; 558d0409631SDong Aisheng } 559d0409631SDong Aisheng 560d0409631SDong Aisheng static int __maybe_unused imx_clk_scu_resume(struct device *dev) 561d0409631SDong Aisheng { 562d0409631SDong Aisheng struct clk_scu *clk = dev_get_drvdata(dev); 563aecf425fSDong Aisheng u32 rsrc_id = clk->rsrc_id; 564d0409631SDong Aisheng int ret = 0; 565d0409631SDong 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; 569aecf425fSDong Aisheng 570d0409631SDong Aisheng if (clk->rate) { 571d0409631SDong Aisheng ret = clk_scu_set_rate(&clk->hw, clk->rate, 0); 572d0409631SDong Aisheng dev_dbg(dev, "restore rate %d %s\n", clk->rate, 573d0409631SDong Aisheng !ret ? "success" : "failed"); 574d0409631SDong Aisheng } 575d0409631SDong Aisheng 576*a61cea83SDong Aisheng if (clk->is_enabled && rsrc_id != IMX_SC_R_PI_0_PLL) { 577d0409631SDong Aisheng ret = clk_scu_prepare(&clk->hw); 578d0409631SDong Aisheng dev_dbg(dev, "restore enabled state %s\n", 579d0409631SDong Aisheng !ret ? "success" : "failed"); 580d0409631SDong Aisheng } 581d0409631SDong Aisheng 582d0409631SDong Aisheng return ret; 583d0409631SDong Aisheng } 584d0409631SDong Aisheng 585d0409631SDong Aisheng static const struct dev_pm_ops imx_clk_scu_pm_ops = { 586d0409631SDong Aisheng SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(imx_clk_scu_suspend, 587d0409631SDong Aisheng imx_clk_scu_resume) 588d0409631SDong Aisheng }; 589d0409631SDong Aisheng 59077d8f306SDong Aisheng static struct platform_driver imx_clk_scu_driver = { 59177d8f306SDong Aisheng .driver = { 59277d8f306SDong Aisheng .name = "imx-scu-clk", 59377d8f306SDong Aisheng .suppress_bind_attrs = true, 594d0409631SDong Aisheng .pm = &imx_clk_scu_pm_ops, 59577d8f306SDong Aisheng }, 59677d8f306SDong Aisheng .probe = imx_clk_scu_probe, 59777d8f306SDong Aisheng }; 59877d8f306SDong Aisheng 59977d8f306SDong Aisheng static int imx_clk_scu_attach_pd(struct device *dev, u32 rsrc_id) 60077d8f306SDong Aisheng { 60177d8f306SDong Aisheng struct of_phandle_args genpdspec = { 60277d8f306SDong Aisheng .np = pd_np, 60377d8f306SDong Aisheng .args_count = 1, 60477d8f306SDong Aisheng .args[0] = rsrc_id, 60577d8f306SDong Aisheng }; 60677d8f306SDong Aisheng 6070d5f1f47SDong Aisheng if (rsrc_id == IMX_SC_R_A35 || rsrc_id == IMX_SC_R_A53 || 6080d5f1f47SDong Aisheng rsrc_id == IMX_SC_R_A72) 6090d5f1f47SDong Aisheng return 0; 6100d5f1f47SDong Aisheng 61177d8f306SDong Aisheng return of_genpd_add_device(&genpdspec, dev); 61277d8f306SDong Aisheng } 61377d8f306SDong Aisheng 61477d8f306SDong Aisheng struct clk_hw *imx_clk_scu_alloc_dev(const char *name, 61577d8f306SDong Aisheng const char * const *parents, 61677d8f306SDong Aisheng int num_parents, u32 rsrc_id, u8 clk_type) 61777d8f306SDong Aisheng { 61877d8f306SDong Aisheng struct imx_scu_clk_node clk = { 61977d8f306SDong Aisheng .name = name, 62077d8f306SDong Aisheng .rsrc = rsrc_id, 62177d8f306SDong Aisheng .clk_type = clk_type, 62277d8f306SDong Aisheng .parents = parents, 62377d8f306SDong Aisheng .num_parents = num_parents, 62477d8f306SDong Aisheng }; 62577d8f306SDong Aisheng struct platform_device *pdev; 62677d8f306SDong Aisheng int ret; 62777d8f306SDong Aisheng 6285964012cSDong Aisheng if (!imx_scu_clk_is_valid(rsrc_id)) 6295964012cSDong Aisheng return ERR_PTR(-EINVAL); 6305964012cSDong Aisheng 63177d8f306SDong Aisheng pdev = platform_device_alloc(name, PLATFORM_DEVID_NONE); 63277d8f306SDong Aisheng if (!pdev) { 63377d8f306SDong Aisheng pr_err("%s: failed to allocate scu clk dev rsrc %d type %d\n", 63477d8f306SDong Aisheng name, rsrc_id, clk_type); 63577d8f306SDong Aisheng return ERR_PTR(-ENOMEM); 63677d8f306SDong Aisheng } 63777d8f306SDong Aisheng 63877d8f306SDong Aisheng ret = platform_device_add_data(pdev, &clk, sizeof(clk)); 63977d8f306SDong Aisheng if (ret) { 64077d8f306SDong Aisheng platform_device_put(pdev); 64177d8f306SDong Aisheng return ERR_PTR(ret); 64277d8f306SDong Aisheng } 64377d8f306SDong Aisheng 64477d8f306SDong Aisheng pdev->driver_override = "imx-scu-clk"; 64577d8f306SDong Aisheng 64677d8f306SDong Aisheng ret = imx_clk_scu_attach_pd(&pdev->dev, rsrc_id); 64777d8f306SDong Aisheng if (ret) 64877d8f306SDong Aisheng pr_warn("%s: failed to attached the power domain %d\n", 64977d8f306SDong Aisheng name, ret); 65077d8f306SDong Aisheng 65177d8f306SDong Aisheng platform_device_add(pdev); 65277d8f306SDong Aisheng 65377d8f306SDong Aisheng /* For API backwards compatiblilty, simply return NULL for success */ 65477d8f306SDong Aisheng return NULL; 65577d8f306SDong Aisheng } 65677d8f306SDong Aisheng 65777d8f306SDong Aisheng void imx_clk_scu_unregister(void) 65877d8f306SDong Aisheng { 65977d8f306SDong Aisheng struct imx_scu_clk_node *clk; 66077d8f306SDong Aisheng int i; 66177d8f306SDong Aisheng 66277d8f306SDong Aisheng for (i = 0; i < IMX_SC_R_LAST; i++) { 66377d8f306SDong Aisheng list_for_each_entry(clk, &imx_scu_clks[i], node) { 66477d8f306SDong Aisheng clk_hw_unregister(clk->hw); 66577d8f306SDong Aisheng kfree(clk); 66677d8f306SDong Aisheng } 66777d8f306SDong Aisheng } 66877d8f306SDong Aisheng } 6695392c5deSDong Aisheng 6705392c5deSDong Aisheng static unsigned long clk_gpr_div_scu_recalc_rate(struct clk_hw *hw, 6715392c5deSDong Aisheng unsigned long parent_rate) 6725392c5deSDong Aisheng { 6735392c5deSDong Aisheng struct clk_gpr_scu *clk = to_clk_gpr_scu(hw); 6745392c5deSDong Aisheng unsigned long rate = 0; 6755392c5deSDong Aisheng u32 val; 6765392c5deSDong Aisheng int err; 6775392c5deSDong Aisheng 6785392c5deSDong Aisheng err = imx_sc_misc_get_control(ccm_ipc_handle, clk->rsrc_id, 6795392c5deSDong Aisheng clk->gpr_id, &val); 6805392c5deSDong Aisheng 6815392c5deSDong Aisheng rate = val ? parent_rate / 2 : parent_rate; 6825392c5deSDong Aisheng 6835392c5deSDong Aisheng return err ? 0 : rate; 6845392c5deSDong Aisheng } 6855392c5deSDong Aisheng 6865392c5deSDong Aisheng static long clk_gpr_div_scu_round_rate(struct clk_hw *hw, unsigned long rate, 6875392c5deSDong Aisheng unsigned long *prate) 6885392c5deSDong Aisheng { 6895392c5deSDong Aisheng if (rate < *prate) 6905392c5deSDong Aisheng rate = *prate / 2; 6915392c5deSDong Aisheng else 6925392c5deSDong Aisheng rate = *prate; 6935392c5deSDong Aisheng 6945392c5deSDong Aisheng return rate; 6955392c5deSDong Aisheng } 6965392c5deSDong Aisheng 6975392c5deSDong Aisheng static int clk_gpr_div_scu_set_rate(struct clk_hw *hw, unsigned long rate, 6985392c5deSDong Aisheng unsigned long parent_rate) 6995392c5deSDong Aisheng { 7005392c5deSDong Aisheng struct clk_gpr_scu *clk = to_clk_gpr_scu(hw); 7015392c5deSDong Aisheng uint32_t val; 7025392c5deSDong Aisheng int err; 7035392c5deSDong Aisheng 7045392c5deSDong Aisheng val = (rate < parent_rate) ? 1 : 0; 7055392c5deSDong Aisheng err = imx_sc_misc_set_control(ccm_ipc_handle, clk->rsrc_id, 7065392c5deSDong Aisheng clk->gpr_id, val); 7075392c5deSDong Aisheng 7085392c5deSDong Aisheng return err ? -EINVAL : 0; 7095392c5deSDong Aisheng } 7105392c5deSDong Aisheng 7115392c5deSDong Aisheng static const struct clk_ops clk_gpr_div_scu_ops = { 7125392c5deSDong Aisheng .recalc_rate = clk_gpr_div_scu_recalc_rate, 7135392c5deSDong Aisheng .round_rate = clk_gpr_div_scu_round_rate, 7145392c5deSDong Aisheng .set_rate = clk_gpr_div_scu_set_rate, 7155392c5deSDong Aisheng }; 7165392c5deSDong Aisheng 7175392c5deSDong Aisheng static u8 clk_gpr_mux_scu_get_parent(struct clk_hw *hw) 7185392c5deSDong Aisheng { 7195392c5deSDong Aisheng struct clk_gpr_scu *clk = to_clk_gpr_scu(hw); 7205392c5deSDong Aisheng u32 val = 0; 7215392c5deSDong Aisheng 7225392c5deSDong Aisheng imx_sc_misc_get_control(ccm_ipc_handle, clk->rsrc_id, 7235392c5deSDong Aisheng clk->gpr_id, &val); 7245392c5deSDong Aisheng 7255392c5deSDong Aisheng return (u8)val; 7265392c5deSDong Aisheng } 7275392c5deSDong Aisheng 7285392c5deSDong Aisheng static int clk_gpr_mux_scu_set_parent(struct clk_hw *hw, u8 index) 7295392c5deSDong Aisheng { 7305392c5deSDong Aisheng struct clk_gpr_scu *clk = to_clk_gpr_scu(hw); 7315392c5deSDong Aisheng 7325392c5deSDong Aisheng return imx_sc_misc_set_control(ccm_ipc_handle, clk->rsrc_id, 7335392c5deSDong Aisheng clk->gpr_id, index); 7345392c5deSDong Aisheng } 7355392c5deSDong Aisheng 7365392c5deSDong Aisheng static const struct clk_ops clk_gpr_mux_scu_ops = { 7375392c5deSDong Aisheng .get_parent = clk_gpr_mux_scu_get_parent, 7385392c5deSDong Aisheng .set_parent = clk_gpr_mux_scu_set_parent, 7395392c5deSDong Aisheng }; 7405392c5deSDong Aisheng 7415392c5deSDong Aisheng static int clk_gpr_gate_scu_prepare(struct clk_hw *hw) 7425392c5deSDong Aisheng { 7435392c5deSDong Aisheng struct clk_gpr_scu *clk = to_clk_gpr_scu(hw); 7445392c5deSDong Aisheng 7455392c5deSDong Aisheng return imx_sc_misc_set_control(ccm_ipc_handle, clk->rsrc_id, 7465392c5deSDong Aisheng clk->gpr_id, !clk->gate_invert); 7475392c5deSDong Aisheng } 7485392c5deSDong Aisheng 7495392c5deSDong Aisheng static void clk_gpr_gate_scu_unprepare(struct clk_hw *hw) 7505392c5deSDong Aisheng { 7515392c5deSDong Aisheng struct clk_gpr_scu *clk = to_clk_gpr_scu(hw); 7525392c5deSDong Aisheng int ret; 7535392c5deSDong Aisheng 7545392c5deSDong Aisheng ret = imx_sc_misc_set_control(ccm_ipc_handle, clk->rsrc_id, 7555392c5deSDong Aisheng clk->gpr_id, clk->gate_invert); 7565392c5deSDong Aisheng if (ret) 7575392c5deSDong Aisheng pr_err("%s: clk unprepare failed %d\n", clk_hw_get_name(hw), 7585392c5deSDong Aisheng ret); 7595392c5deSDong Aisheng } 7605392c5deSDong Aisheng 7615392c5deSDong Aisheng static int clk_gpr_gate_scu_is_prepared(struct clk_hw *hw) 7625392c5deSDong Aisheng { 7635392c5deSDong Aisheng struct clk_gpr_scu *clk = to_clk_gpr_scu(hw); 7645392c5deSDong Aisheng int ret; 7655392c5deSDong Aisheng u32 val; 7665392c5deSDong Aisheng 7675392c5deSDong Aisheng ret = imx_sc_misc_get_control(ccm_ipc_handle, clk->rsrc_id, 7685392c5deSDong Aisheng clk->gpr_id, &val); 7695392c5deSDong Aisheng if (ret) 7705392c5deSDong Aisheng return ret; 7715392c5deSDong Aisheng 7725392c5deSDong Aisheng return clk->gate_invert ? !val : val; 7735392c5deSDong Aisheng } 7745392c5deSDong Aisheng 7755392c5deSDong Aisheng static const struct clk_ops clk_gpr_gate_scu_ops = { 7765392c5deSDong Aisheng .prepare = clk_gpr_gate_scu_prepare, 7775392c5deSDong Aisheng .unprepare = clk_gpr_gate_scu_unprepare, 7785392c5deSDong Aisheng .is_prepared = clk_gpr_gate_scu_is_prepared, 7795392c5deSDong Aisheng }; 7805392c5deSDong Aisheng 7815392c5deSDong Aisheng struct clk_hw *__imx_clk_gpr_scu(const char *name, const char * const *parent_name, 7825392c5deSDong Aisheng int num_parents, u32 rsrc_id, u8 gpr_id, u8 flags, 7835392c5deSDong Aisheng bool invert) 7845392c5deSDong Aisheng { 7855392c5deSDong Aisheng struct imx_scu_clk_node *clk_node; 7865392c5deSDong Aisheng struct clk_gpr_scu *clk; 7875392c5deSDong Aisheng struct clk_hw *hw; 7885392c5deSDong Aisheng struct clk_init_data init; 7895392c5deSDong Aisheng int ret; 7905392c5deSDong Aisheng 7915392c5deSDong Aisheng if (rsrc_id >= IMX_SC_R_LAST || gpr_id >= IMX_SC_C_LAST) 7925392c5deSDong Aisheng return ERR_PTR(-EINVAL); 7935392c5deSDong Aisheng 7945392c5deSDong Aisheng clk_node = kzalloc(sizeof(*clk_node), GFP_KERNEL); 7955392c5deSDong Aisheng if (!clk_node) 7965392c5deSDong Aisheng return ERR_PTR(-ENOMEM); 7975392c5deSDong Aisheng 7985964012cSDong Aisheng if (!imx_scu_clk_is_valid(rsrc_id)) 7995964012cSDong Aisheng return ERR_PTR(-EINVAL); 8005964012cSDong Aisheng 8015392c5deSDong Aisheng clk = kzalloc(sizeof(*clk), GFP_KERNEL); 8025392c5deSDong Aisheng if (!clk) { 8035392c5deSDong Aisheng kfree(clk_node); 8045392c5deSDong Aisheng return ERR_PTR(-ENOMEM); 8055392c5deSDong Aisheng } 8065392c5deSDong Aisheng 8075392c5deSDong Aisheng clk->rsrc_id = rsrc_id; 8085392c5deSDong Aisheng clk->gpr_id = gpr_id; 8095392c5deSDong Aisheng clk->flags = flags; 8105392c5deSDong Aisheng clk->gate_invert = invert; 8115392c5deSDong Aisheng 8125392c5deSDong Aisheng if (flags & IMX_SCU_GPR_CLK_GATE) 8135392c5deSDong Aisheng init.ops = &clk_gpr_gate_scu_ops; 8145392c5deSDong Aisheng 8155392c5deSDong Aisheng if (flags & IMX_SCU_GPR_CLK_DIV) 8165392c5deSDong Aisheng init.ops = &clk_gpr_div_scu_ops; 8175392c5deSDong Aisheng 8185392c5deSDong Aisheng if (flags & IMX_SCU_GPR_CLK_MUX) 8195392c5deSDong Aisheng init.ops = &clk_gpr_mux_scu_ops; 8205392c5deSDong Aisheng 8215392c5deSDong Aisheng init.flags = 0; 8225392c5deSDong Aisheng init.name = name; 8235392c5deSDong Aisheng init.parent_names = parent_name; 8245392c5deSDong Aisheng init.num_parents = num_parents; 8255392c5deSDong Aisheng 8265392c5deSDong Aisheng clk->hw.init = &init; 8275392c5deSDong Aisheng 8285392c5deSDong Aisheng hw = &clk->hw; 8295392c5deSDong Aisheng ret = clk_hw_register(NULL, hw); 8305392c5deSDong Aisheng if (ret) { 8315392c5deSDong Aisheng kfree(clk); 8325392c5deSDong Aisheng kfree(clk_node); 8335392c5deSDong Aisheng hw = ERR_PTR(ret); 8345392c5deSDong Aisheng } else { 8355392c5deSDong Aisheng clk_node->hw = hw; 8365392c5deSDong Aisheng clk_node->clk_type = gpr_id; 8375392c5deSDong Aisheng list_add_tail(&clk_node->node, &imx_scu_clks[rsrc_id]); 8385392c5deSDong Aisheng } 8395392c5deSDong Aisheng 8405392c5deSDong Aisheng return hw; 8415392c5deSDong Aisheng } 842