1fe37b482SAisheng Dong // SPDX-License-Identifier: GPL-2.0+ 2fe37b482SAisheng Dong /* 3fe37b482SAisheng Dong * Copyright 2018 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> 9fe37b482SAisheng Dong #include <linux/clk-provider.h> 10fe37b482SAisheng Dong #include <linux/err.h> 1177d8f306SDong Aisheng #include <linux/of_platform.h> 1277d8f306SDong Aisheng #include <linux/platform_device.h> 1377d8f306SDong Aisheng #include <linux/pm_domain.h> 1478edeb08SDong Aisheng #include <linux/pm_runtime.h> 15fe37b482SAisheng Dong #include <linux/slab.h> 16fe37b482SAisheng Dong 17fe37b482SAisheng Dong #include "clk-scu.h" 18fe37b482SAisheng Dong 193b9ea606SAnson Huang #define IMX_SIP_CPUFREQ 0xC2000001 203b9ea606SAnson Huang #define IMX_SIP_SET_CPUFREQ 0x00 213b9ea606SAnson Huang 22fe37b482SAisheng Dong static struct imx_sc_ipc *ccm_ipc_handle; 23550b562aSZou Wei static struct device_node *pd_np; 24220175cdSDong Aisheng static struct platform_driver imx_clk_scu_driver; 2577d8f306SDong Aisheng 2677d8f306SDong Aisheng struct imx_scu_clk_node { 2777d8f306SDong Aisheng const char *name; 2877d8f306SDong Aisheng u32 rsrc; 2977d8f306SDong Aisheng u8 clk_type; 3077d8f306SDong Aisheng const char * const *parents; 3177d8f306SDong Aisheng int num_parents; 3277d8f306SDong Aisheng 3377d8f306SDong Aisheng struct clk_hw *hw; 3477d8f306SDong Aisheng struct list_head node; 3577d8f306SDong Aisheng }; 3677d8f306SDong Aisheng 3777d8f306SDong Aisheng struct list_head imx_scu_clks[IMX_SC_R_LAST]; 38fe37b482SAisheng Dong 39fe37b482SAisheng Dong /* 40fe37b482SAisheng Dong * struct clk_scu - Description of one SCU clock 41fe37b482SAisheng Dong * @hw: the common clk_hw 42fe37b482SAisheng Dong * @rsrc_id: resource ID of this SCU clock 43fe37b482SAisheng Dong * @clk_type: type of this clock resource 44fe37b482SAisheng Dong */ 45fe37b482SAisheng Dong struct clk_scu { 46fe37b482SAisheng Dong struct clk_hw hw; 47fe37b482SAisheng Dong u16 rsrc_id; 48fe37b482SAisheng Dong u8 clk_type; 49d0409631SDong Aisheng 50d0409631SDong Aisheng /* for state save&restore */ 51d0409631SDong Aisheng bool is_enabled; 52d0409631SDong Aisheng u32 rate; 53fe37b482SAisheng Dong }; 54fe37b482SAisheng Dong 55fe37b482SAisheng Dong /* 56*5392c5deSDong Aisheng * struct clk_gpr_scu - Description of one SCU GPR clock 57*5392c5deSDong Aisheng * @hw: the common clk_hw 58*5392c5deSDong Aisheng * @rsrc_id: resource ID of this SCU clock 59*5392c5deSDong Aisheng * @gpr_id: GPR ID index to control the divider 60*5392c5deSDong Aisheng */ 61*5392c5deSDong Aisheng struct clk_gpr_scu { 62*5392c5deSDong Aisheng struct clk_hw hw; 63*5392c5deSDong Aisheng u16 rsrc_id; 64*5392c5deSDong Aisheng u8 gpr_id; 65*5392c5deSDong Aisheng u8 flags; 66*5392c5deSDong Aisheng bool gate_invert; 67*5392c5deSDong Aisheng }; 68*5392c5deSDong Aisheng 69*5392c5deSDong Aisheng #define to_clk_gpr_scu(_hw) container_of(_hw, struct clk_gpr_scu, hw) 70*5392c5deSDong Aisheng 71*5392c5deSDong Aisheng /* 72fe37b482SAisheng Dong * struct imx_sc_msg_req_set_clock_rate - clock set rate protocol 73fe37b482SAisheng Dong * @hdr: SCU protocol header 74fe37b482SAisheng Dong * @rate: rate to set 75fe37b482SAisheng Dong * @resource: clock resource to set rate 76fe37b482SAisheng Dong * @clk: clk type of this resource 77fe37b482SAisheng Dong * 78fe37b482SAisheng Dong * This structure describes the SCU protocol of clock rate set 79fe37b482SAisheng Dong */ 80fe37b482SAisheng Dong struct imx_sc_msg_req_set_clock_rate { 81fe37b482SAisheng Dong struct imx_sc_rpc_msg hdr; 82fe37b482SAisheng Dong __le32 rate; 83fe37b482SAisheng Dong __le16 resource; 84fe37b482SAisheng Dong u8 clk; 85a0ae04a2SLeonard Crestez } __packed __aligned(4); 86fe37b482SAisheng Dong 87fe37b482SAisheng Dong struct req_get_clock_rate { 88fe37b482SAisheng Dong __le16 resource; 89fe37b482SAisheng Dong u8 clk; 90a0ae04a2SLeonard Crestez } __packed __aligned(4); 91fe37b482SAisheng Dong 92fe37b482SAisheng Dong struct resp_get_clock_rate { 93fe37b482SAisheng Dong __le32 rate; 94fe37b482SAisheng Dong }; 95fe37b482SAisheng Dong 96fe37b482SAisheng Dong /* 97fe37b482SAisheng Dong * struct imx_sc_msg_get_clock_rate - clock get rate protocol 98fe37b482SAisheng Dong * @hdr: SCU protocol header 99fe37b482SAisheng Dong * @req: get rate request protocol 100fe37b482SAisheng Dong * @resp: get rate response protocol 101fe37b482SAisheng Dong * 102fe37b482SAisheng Dong * This structure describes the SCU protocol of clock rate get 103fe37b482SAisheng Dong */ 104fe37b482SAisheng Dong struct imx_sc_msg_get_clock_rate { 105fe37b482SAisheng Dong struct imx_sc_rpc_msg hdr; 106fe37b482SAisheng Dong union { 107fe37b482SAisheng Dong struct req_get_clock_rate req; 108fe37b482SAisheng Dong struct resp_get_clock_rate resp; 109fe37b482SAisheng Dong } data; 110fe37b482SAisheng Dong }; 111fe37b482SAisheng Dong 112fe37b482SAisheng Dong /* 113666aed2dSAisheng Dong * struct imx_sc_msg_get_clock_parent - clock get parent protocol 114666aed2dSAisheng Dong * @hdr: SCU protocol header 115666aed2dSAisheng Dong * @req: get parent request protocol 116666aed2dSAisheng Dong * @resp: get parent response protocol 117666aed2dSAisheng Dong * 118666aed2dSAisheng Dong * This structure describes the SCU protocol of clock get parent 119666aed2dSAisheng Dong */ 120666aed2dSAisheng Dong struct imx_sc_msg_get_clock_parent { 121666aed2dSAisheng Dong struct imx_sc_rpc_msg hdr; 122666aed2dSAisheng Dong union { 123666aed2dSAisheng Dong struct req_get_clock_parent { 124666aed2dSAisheng Dong __le16 resource; 125666aed2dSAisheng Dong u8 clk; 1268400ab88SLeonard Crestez } __packed __aligned(4) req; 127666aed2dSAisheng Dong struct resp_get_clock_parent { 128666aed2dSAisheng Dong u8 parent; 129666aed2dSAisheng Dong } resp; 130666aed2dSAisheng Dong } data; 131666aed2dSAisheng Dong }; 132666aed2dSAisheng Dong 133666aed2dSAisheng Dong /* 134666aed2dSAisheng Dong * struct imx_sc_msg_set_clock_parent - clock set parent protocol 135666aed2dSAisheng Dong * @hdr: SCU protocol header 136666aed2dSAisheng Dong * @req: set parent request protocol 137666aed2dSAisheng Dong * 138666aed2dSAisheng Dong * This structure describes the SCU protocol of clock set parent 139666aed2dSAisheng Dong */ 140666aed2dSAisheng Dong struct imx_sc_msg_set_clock_parent { 141666aed2dSAisheng Dong struct imx_sc_rpc_msg hdr; 142666aed2dSAisheng Dong __le16 resource; 143666aed2dSAisheng Dong u8 clk; 144666aed2dSAisheng Dong u8 parent; 145666aed2dSAisheng Dong } __packed; 146666aed2dSAisheng Dong 147666aed2dSAisheng Dong /* 148fe37b482SAisheng Dong * struct imx_sc_msg_req_clock_enable - clock gate protocol 149fe37b482SAisheng Dong * @hdr: SCU protocol header 150fe37b482SAisheng Dong * @resource: clock resource to gate 151fe37b482SAisheng Dong * @clk: clk type of this resource 152fe37b482SAisheng Dong * @enable: whether gate off the clock 153fe37b482SAisheng Dong * @autog: HW auto gate enable 154fe37b482SAisheng Dong * 155fe37b482SAisheng Dong * This structure describes the SCU protocol of clock gate 156fe37b482SAisheng Dong */ 157fe37b482SAisheng Dong struct imx_sc_msg_req_clock_enable { 158fe37b482SAisheng Dong struct imx_sc_rpc_msg hdr; 159fe37b482SAisheng Dong __le16 resource; 160fe37b482SAisheng Dong u8 clk; 161fe37b482SAisheng Dong u8 enable; 162fe37b482SAisheng Dong u8 autog; 163a0ae04a2SLeonard Crestez } __packed __aligned(4); 164fe37b482SAisheng Dong 165fe37b482SAisheng Dong static inline struct clk_scu *to_clk_scu(struct clk_hw *hw) 166fe37b482SAisheng Dong { 167fe37b482SAisheng Dong return container_of(hw, struct clk_scu, hw); 168fe37b482SAisheng Dong } 169fe37b482SAisheng Dong 17077d8f306SDong Aisheng int imx_clk_scu_init(struct device_node *np) 171fe37b482SAisheng Dong { 17277d8f306SDong Aisheng u32 clk_cells; 17377d8f306SDong Aisheng int ret, i; 17477d8f306SDong Aisheng 17577d8f306SDong Aisheng ret = imx_scu_get_handle(&ccm_ipc_handle); 17677d8f306SDong Aisheng if (ret) 17777d8f306SDong Aisheng return ret; 17877d8f306SDong Aisheng 17977d8f306SDong Aisheng of_property_read_u32(np, "#clock-cells", &clk_cells); 18077d8f306SDong Aisheng 18177d8f306SDong Aisheng if (clk_cells == 2) { 18277d8f306SDong Aisheng for (i = 0; i < IMX_SC_R_LAST; i++) 18377d8f306SDong Aisheng INIT_LIST_HEAD(&imx_scu_clks[i]); 18443d24796SDong Aisheng 18543d24796SDong Aisheng /* pd_np will be used to attach power domains later */ 18677d8f306SDong Aisheng pd_np = of_find_compatible_node(NULL, NULL, "fsl,scu-pd"); 18743d24796SDong Aisheng if (!pd_np) 18843d24796SDong Aisheng return -EINVAL; 18977d8f306SDong Aisheng } 19077d8f306SDong Aisheng 191220175cdSDong Aisheng return platform_driver_register(&imx_clk_scu_driver); 192fe37b482SAisheng Dong } 193fe37b482SAisheng Dong 194fe37b482SAisheng Dong /* 195fe37b482SAisheng Dong * clk_scu_recalc_rate - Get clock rate for a SCU clock 196fe37b482SAisheng Dong * @hw: clock to get rate for 197fe37b482SAisheng Dong * @parent_rate: parent rate provided by common clock framework, not used 198fe37b482SAisheng Dong * 199fe37b482SAisheng Dong * Gets the current clock rate of a SCU clock. Returns the current 200fe37b482SAisheng Dong * clock rate, or zero in failure. 201fe37b482SAisheng Dong */ 202fe37b482SAisheng Dong static unsigned long clk_scu_recalc_rate(struct clk_hw *hw, 203fe37b482SAisheng Dong unsigned long parent_rate) 204fe37b482SAisheng Dong { 205fe37b482SAisheng Dong struct clk_scu *clk = to_clk_scu(hw); 206fe37b482SAisheng Dong struct imx_sc_msg_get_clock_rate msg; 207fe37b482SAisheng Dong struct imx_sc_rpc_msg *hdr = &msg.hdr; 208fe37b482SAisheng Dong int ret; 209fe37b482SAisheng Dong 210fe37b482SAisheng Dong hdr->ver = IMX_SC_RPC_VERSION; 211fe37b482SAisheng Dong hdr->svc = IMX_SC_RPC_SVC_PM; 212fe37b482SAisheng Dong hdr->func = IMX_SC_PM_FUNC_GET_CLOCK_RATE; 213fe37b482SAisheng Dong hdr->size = 2; 214fe37b482SAisheng Dong 215fe37b482SAisheng Dong msg.data.req.resource = cpu_to_le16(clk->rsrc_id); 216fe37b482SAisheng Dong msg.data.req.clk = clk->clk_type; 217fe37b482SAisheng Dong 218fe37b482SAisheng Dong ret = imx_scu_call_rpc(ccm_ipc_handle, &msg, true); 219fe37b482SAisheng Dong if (ret) { 220fe37b482SAisheng Dong pr_err("%s: failed to get clock rate %d\n", 221fe37b482SAisheng Dong clk_hw_get_name(hw), ret); 222fe37b482SAisheng Dong return 0; 223fe37b482SAisheng Dong } 224fe37b482SAisheng Dong 225fe37b482SAisheng Dong return le32_to_cpu(msg.data.resp.rate); 226fe37b482SAisheng Dong } 227fe37b482SAisheng Dong 228fe37b482SAisheng Dong /* 229fe37b482SAisheng Dong * clk_scu_round_rate - Round clock rate for a SCU clock 230fe37b482SAisheng Dong * @hw: clock to round rate for 231fe37b482SAisheng Dong * @rate: rate to round 232fe37b482SAisheng Dong * @parent_rate: parent rate provided by common clock framework, not used 233fe37b482SAisheng Dong * 234fe37b482SAisheng Dong * Returns the current clock rate, or zero in failure. 235fe37b482SAisheng Dong */ 236fe37b482SAisheng Dong static long clk_scu_round_rate(struct clk_hw *hw, unsigned long rate, 237fe37b482SAisheng Dong unsigned long *parent_rate) 238fe37b482SAisheng Dong { 239fe37b482SAisheng Dong /* 240fe37b482SAisheng Dong * Assume we support all the requested rate and let the SCU firmware 241fe37b482SAisheng Dong * to handle the left work 242fe37b482SAisheng Dong */ 243fe37b482SAisheng Dong return rate; 244fe37b482SAisheng Dong } 245fe37b482SAisheng Dong 2463b9ea606SAnson Huang static int clk_scu_atf_set_cpu_rate(struct clk_hw *hw, unsigned long rate, 2473b9ea606SAnson Huang unsigned long parent_rate) 2483b9ea606SAnson Huang { 2493b9ea606SAnson Huang struct clk_scu *clk = to_clk_scu(hw); 2503b9ea606SAnson Huang struct arm_smccc_res res; 2513b9ea606SAnson Huang unsigned long cluster_id; 2523b9ea606SAnson Huang 2533b9ea606SAnson Huang if (clk->rsrc_id == IMX_SC_R_A35) 2543b9ea606SAnson Huang cluster_id = 0; 2553b9ea606SAnson Huang else 2563b9ea606SAnson Huang return -EINVAL; 2573b9ea606SAnson Huang 2583b9ea606SAnson Huang /* CPU frequency scaling can ONLY be done by ARM-Trusted-Firmware */ 2593b9ea606SAnson Huang arm_smccc_smc(IMX_SIP_CPUFREQ, IMX_SIP_SET_CPUFREQ, 2603b9ea606SAnson Huang cluster_id, rate, 0, 0, 0, 0, &res); 2613b9ea606SAnson Huang 2623b9ea606SAnson Huang return 0; 2633b9ea606SAnson Huang } 2643b9ea606SAnson Huang 265fe37b482SAisheng Dong /* 266fe37b482SAisheng Dong * clk_scu_set_rate - Set rate for a SCU clock 267fe37b482SAisheng Dong * @hw: clock to change rate for 268fe37b482SAisheng Dong * @rate: target rate for the clock 269fe37b482SAisheng Dong * @parent_rate: rate of the clock parent, not used for SCU clocks 270fe37b482SAisheng Dong * 271fe37b482SAisheng Dong * Sets a clock frequency for a SCU clock. Returns the SCU 272fe37b482SAisheng Dong * protocol status. 273fe37b482SAisheng Dong */ 274fe37b482SAisheng Dong static int clk_scu_set_rate(struct clk_hw *hw, unsigned long rate, 275fe37b482SAisheng Dong unsigned long parent_rate) 276fe37b482SAisheng Dong { 277fe37b482SAisheng Dong struct clk_scu *clk = to_clk_scu(hw); 278fe37b482SAisheng Dong struct imx_sc_msg_req_set_clock_rate msg; 279fe37b482SAisheng Dong struct imx_sc_rpc_msg *hdr = &msg.hdr; 280fe37b482SAisheng Dong 281fe37b482SAisheng Dong hdr->ver = IMX_SC_RPC_VERSION; 282fe37b482SAisheng Dong hdr->svc = IMX_SC_RPC_SVC_PM; 283fe37b482SAisheng Dong hdr->func = IMX_SC_PM_FUNC_SET_CLOCK_RATE; 284fe37b482SAisheng Dong hdr->size = 3; 285fe37b482SAisheng Dong 286fe37b482SAisheng Dong msg.rate = cpu_to_le32(rate); 287fe37b482SAisheng Dong msg.resource = cpu_to_le16(clk->rsrc_id); 288fe37b482SAisheng Dong msg.clk = clk->clk_type; 289fe37b482SAisheng Dong 290fe37b482SAisheng Dong return imx_scu_call_rpc(ccm_ipc_handle, &msg, true); 291fe37b482SAisheng Dong } 292fe37b482SAisheng Dong 293666aed2dSAisheng Dong static u8 clk_scu_get_parent(struct clk_hw *hw) 294666aed2dSAisheng Dong { 295666aed2dSAisheng Dong struct clk_scu *clk = to_clk_scu(hw); 296666aed2dSAisheng Dong struct imx_sc_msg_get_clock_parent msg; 297666aed2dSAisheng Dong struct imx_sc_rpc_msg *hdr = &msg.hdr; 298666aed2dSAisheng Dong int ret; 299666aed2dSAisheng Dong 300666aed2dSAisheng Dong hdr->ver = IMX_SC_RPC_VERSION; 301666aed2dSAisheng Dong hdr->svc = IMX_SC_RPC_SVC_PM; 302666aed2dSAisheng Dong hdr->func = IMX_SC_PM_FUNC_GET_CLOCK_PARENT; 303666aed2dSAisheng Dong hdr->size = 2; 304666aed2dSAisheng Dong 305666aed2dSAisheng Dong msg.data.req.resource = cpu_to_le16(clk->rsrc_id); 306666aed2dSAisheng Dong msg.data.req.clk = clk->clk_type; 307666aed2dSAisheng Dong 308666aed2dSAisheng Dong ret = imx_scu_call_rpc(ccm_ipc_handle, &msg, true); 309666aed2dSAisheng Dong if (ret) { 310666aed2dSAisheng Dong pr_err("%s: failed to get clock parent %d\n", 311666aed2dSAisheng Dong clk_hw_get_name(hw), ret); 312666aed2dSAisheng Dong return 0; 313666aed2dSAisheng Dong } 314666aed2dSAisheng Dong 315666aed2dSAisheng Dong return msg.data.resp.parent; 316666aed2dSAisheng Dong } 317666aed2dSAisheng Dong 318666aed2dSAisheng Dong static int clk_scu_set_parent(struct clk_hw *hw, u8 index) 319666aed2dSAisheng Dong { 320666aed2dSAisheng Dong struct clk_scu *clk = to_clk_scu(hw); 321666aed2dSAisheng Dong struct imx_sc_msg_set_clock_parent msg; 322666aed2dSAisheng Dong struct imx_sc_rpc_msg *hdr = &msg.hdr; 323666aed2dSAisheng Dong 324666aed2dSAisheng Dong hdr->ver = IMX_SC_RPC_VERSION; 325666aed2dSAisheng Dong hdr->svc = IMX_SC_RPC_SVC_PM; 326666aed2dSAisheng Dong hdr->func = IMX_SC_PM_FUNC_SET_CLOCK_PARENT; 327666aed2dSAisheng Dong hdr->size = 2; 328666aed2dSAisheng Dong 329666aed2dSAisheng Dong msg.resource = cpu_to_le16(clk->rsrc_id); 330666aed2dSAisheng Dong msg.clk = clk->clk_type; 331666aed2dSAisheng Dong msg.parent = index; 332666aed2dSAisheng Dong 333666aed2dSAisheng Dong return imx_scu_call_rpc(ccm_ipc_handle, &msg, true); 334666aed2dSAisheng Dong } 335666aed2dSAisheng Dong 336fe37b482SAisheng Dong static int sc_pm_clock_enable(struct imx_sc_ipc *ipc, u16 resource, 337fe37b482SAisheng Dong u8 clk, bool enable, bool autog) 338fe37b482SAisheng Dong { 339fe37b482SAisheng Dong struct imx_sc_msg_req_clock_enable msg; 340fe37b482SAisheng Dong struct imx_sc_rpc_msg *hdr = &msg.hdr; 341fe37b482SAisheng Dong 342fe37b482SAisheng Dong hdr->ver = IMX_SC_RPC_VERSION; 343fe37b482SAisheng Dong hdr->svc = IMX_SC_RPC_SVC_PM; 344fe37b482SAisheng Dong hdr->func = IMX_SC_PM_FUNC_CLOCK_ENABLE; 345fe37b482SAisheng Dong hdr->size = 3; 346fe37b482SAisheng Dong 347fe37b482SAisheng Dong msg.resource = cpu_to_le16(resource); 348fe37b482SAisheng Dong msg.clk = clk; 349fe37b482SAisheng Dong msg.enable = enable; 350fe37b482SAisheng Dong msg.autog = autog; 351fe37b482SAisheng Dong 352fe37b482SAisheng Dong return imx_scu_call_rpc(ccm_ipc_handle, &msg, true); 353fe37b482SAisheng Dong } 354fe37b482SAisheng Dong 355fe37b482SAisheng Dong /* 356fe37b482SAisheng Dong * clk_scu_prepare - Enable a SCU clock 357fe37b482SAisheng Dong * @hw: clock to enable 358fe37b482SAisheng Dong * 359fe37b482SAisheng Dong * Enable the clock at the DSC slice level 360fe37b482SAisheng Dong */ 361fe37b482SAisheng Dong static int clk_scu_prepare(struct clk_hw *hw) 362fe37b482SAisheng Dong { 363fe37b482SAisheng Dong struct clk_scu *clk = to_clk_scu(hw); 364fe37b482SAisheng Dong 365fe37b482SAisheng Dong return sc_pm_clock_enable(ccm_ipc_handle, clk->rsrc_id, 366fe37b482SAisheng Dong clk->clk_type, true, false); 367fe37b482SAisheng Dong } 368fe37b482SAisheng Dong 369fe37b482SAisheng Dong /* 370fe37b482SAisheng Dong * clk_scu_unprepare - Disable a SCU clock 371fe37b482SAisheng Dong * @hw: clock to enable 372fe37b482SAisheng Dong * 373fe37b482SAisheng Dong * Disable the clock at the DSC slice level 374fe37b482SAisheng Dong */ 375fe37b482SAisheng Dong static void clk_scu_unprepare(struct clk_hw *hw) 376fe37b482SAisheng Dong { 377fe37b482SAisheng Dong struct clk_scu *clk = to_clk_scu(hw); 378fe37b482SAisheng Dong int ret; 379fe37b482SAisheng Dong 380fe37b482SAisheng Dong ret = sc_pm_clock_enable(ccm_ipc_handle, clk->rsrc_id, 381fe37b482SAisheng Dong clk->clk_type, false, false); 382fe37b482SAisheng Dong if (ret) 383fe37b482SAisheng Dong pr_warn("%s: clk unprepare failed %d\n", clk_hw_get_name(hw), 384fe37b482SAisheng Dong ret); 385fe37b482SAisheng Dong } 386fe37b482SAisheng Dong 387fe37b482SAisheng Dong static const struct clk_ops clk_scu_ops = { 388fe37b482SAisheng Dong .recalc_rate = clk_scu_recalc_rate, 389fe37b482SAisheng Dong .round_rate = clk_scu_round_rate, 390fe37b482SAisheng Dong .set_rate = clk_scu_set_rate, 391666aed2dSAisheng Dong .get_parent = clk_scu_get_parent, 392666aed2dSAisheng Dong .set_parent = clk_scu_set_parent, 393fe37b482SAisheng Dong .prepare = clk_scu_prepare, 394fe37b482SAisheng Dong .unprepare = clk_scu_unprepare, 395fe37b482SAisheng Dong }; 396fe37b482SAisheng Dong 3973b9ea606SAnson Huang static const struct clk_ops clk_scu_cpu_ops = { 3983b9ea606SAnson Huang .recalc_rate = clk_scu_recalc_rate, 3993b9ea606SAnson Huang .round_rate = clk_scu_round_rate, 4003b9ea606SAnson Huang .set_rate = clk_scu_atf_set_cpu_rate, 4013b9ea606SAnson Huang .prepare = clk_scu_prepare, 4023b9ea606SAnson Huang .unprepare = clk_scu_unprepare, 4033b9ea606SAnson Huang }; 4043b9ea606SAnson Huang 4052f1a2c1dSDong Aisheng struct clk_hw *__imx_clk_scu(struct device *dev, const char *name, 4062f1a2c1dSDong Aisheng const char * const *parents, int num_parents, 4072f1a2c1dSDong Aisheng u32 rsrc_id, u8 clk_type) 408fe37b482SAisheng Dong { 409fe37b482SAisheng Dong struct clk_init_data init; 410fe37b482SAisheng Dong struct clk_scu *clk; 411fe37b482SAisheng Dong struct clk_hw *hw; 412fe37b482SAisheng Dong int ret; 413fe37b482SAisheng Dong 414fe37b482SAisheng Dong clk = kzalloc(sizeof(*clk), GFP_KERNEL); 415fe37b482SAisheng Dong if (!clk) 416fe37b482SAisheng Dong return ERR_PTR(-ENOMEM); 417fe37b482SAisheng Dong 418fe37b482SAisheng Dong clk->rsrc_id = rsrc_id; 419fe37b482SAisheng Dong clk->clk_type = clk_type; 420fe37b482SAisheng Dong 421fe37b482SAisheng Dong init.name = name; 422fe37b482SAisheng Dong init.ops = &clk_scu_ops; 4233b9ea606SAnson Huang if (rsrc_id == IMX_SC_R_A35) 4243b9ea606SAnson Huang init.ops = &clk_scu_cpu_ops; 4253b9ea606SAnson Huang else 4263b9ea606SAnson Huang init.ops = &clk_scu_ops; 427666aed2dSAisheng Dong init.parent_names = parents; 428666aed2dSAisheng Dong init.num_parents = num_parents; 429666aed2dSAisheng Dong 430fe37b482SAisheng Dong /* 431fe37b482SAisheng Dong * Note on MX8, the clocks are tightly coupled with power domain 432fe37b482SAisheng Dong * that once the power domain is off, the clock status may be 433fe37b482SAisheng Dong * lost. So we make it NOCACHE to let user to retrieve the real 434fe37b482SAisheng Dong * clock status from HW instead of using the possible invalid 435fe37b482SAisheng Dong * cached rate. 436fe37b482SAisheng Dong */ 437fe37b482SAisheng Dong init.flags = CLK_GET_RATE_NOCACHE; 438fe37b482SAisheng Dong clk->hw.init = &init; 439fe37b482SAisheng Dong 440fe37b482SAisheng Dong hw = &clk->hw; 4412f1a2c1dSDong Aisheng ret = clk_hw_register(dev, hw); 442fe37b482SAisheng Dong if (ret) { 443fe37b482SAisheng Dong kfree(clk); 444fe37b482SAisheng Dong hw = ERR_PTR(ret); 445054ef44eSJian Dong return hw; 446fe37b482SAisheng Dong } 447fe37b482SAisheng Dong 448d0409631SDong Aisheng if (dev) 449d0409631SDong Aisheng dev_set_drvdata(dev, clk); 450d0409631SDong Aisheng 451fe37b482SAisheng Dong return hw; 452fe37b482SAisheng Dong } 45377d8f306SDong Aisheng 45477d8f306SDong Aisheng struct clk_hw *imx_scu_of_clk_src_get(struct of_phandle_args *clkspec, 45577d8f306SDong Aisheng void *data) 45677d8f306SDong Aisheng { 45777d8f306SDong Aisheng unsigned int rsrc = clkspec->args[0]; 45877d8f306SDong Aisheng unsigned int idx = clkspec->args[1]; 45977d8f306SDong Aisheng struct list_head *scu_clks = data; 46077d8f306SDong Aisheng struct imx_scu_clk_node *clk; 46177d8f306SDong Aisheng 46277d8f306SDong Aisheng list_for_each_entry(clk, &scu_clks[rsrc], node) { 46377d8f306SDong Aisheng if (clk->clk_type == idx) 46477d8f306SDong Aisheng return clk->hw; 46577d8f306SDong Aisheng } 46677d8f306SDong Aisheng 46777d8f306SDong Aisheng return ERR_PTR(-ENODEV); 46877d8f306SDong Aisheng } 46977d8f306SDong Aisheng 47077d8f306SDong Aisheng static int imx_clk_scu_probe(struct platform_device *pdev) 47177d8f306SDong Aisheng { 47277d8f306SDong Aisheng struct device *dev = &pdev->dev; 47377d8f306SDong Aisheng struct imx_scu_clk_node *clk = dev_get_platdata(dev); 47477d8f306SDong Aisheng struct clk_hw *hw; 47578edeb08SDong Aisheng int ret; 47677d8f306SDong Aisheng 47778edeb08SDong Aisheng pm_runtime_set_suspended(dev); 47878edeb08SDong Aisheng pm_runtime_set_autosuspend_delay(dev, 50); 47978edeb08SDong Aisheng pm_runtime_use_autosuspend(&pdev->dev); 48078edeb08SDong Aisheng pm_runtime_enable(dev); 48178edeb08SDong Aisheng 48278edeb08SDong Aisheng ret = pm_runtime_get_sync(dev); 48378edeb08SDong Aisheng if (ret) { 48478edeb08SDong Aisheng pm_runtime_disable(dev); 48578edeb08SDong Aisheng return ret; 48678edeb08SDong Aisheng } 48778edeb08SDong Aisheng 48878edeb08SDong Aisheng hw = __imx_clk_scu(dev, clk->name, clk->parents, clk->num_parents, 48977d8f306SDong Aisheng clk->rsrc, clk->clk_type); 49078edeb08SDong Aisheng if (IS_ERR(hw)) { 49178edeb08SDong Aisheng pm_runtime_disable(dev); 49277d8f306SDong Aisheng return PTR_ERR(hw); 49378edeb08SDong Aisheng } 49477d8f306SDong Aisheng 49577d8f306SDong Aisheng clk->hw = hw; 49677d8f306SDong Aisheng list_add_tail(&clk->node, &imx_scu_clks[clk->rsrc]); 49777d8f306SDong Aisheng 49878edeb08SDong Aisheng pm_runtime_mark_last_busy(&pdev->dev); 49978edeb08SDong Aisheng pm_runtime_put_autosuspend(&pdev->dev); 50078edeb08SDong Aisheng 50177d8f306SDong Aisheng dev_dbg(dev, "register SCU clock rsrc:%d type:%d\n", clk->rsrc, 50277d8f306SDong Aisheng clk->clk_type); 50377d8f306SDong Aisheng 50477d8f306SDong Aisheng return 0; 50577d8f306SDong Aisheng } 50677d8f306SDong Aisheng 507d0409631SDong Aisheng static int __maybe_unused imx_clk_scu_suspend(struct device *dev) 508d0409631SDong Aisheng { 509d0409631SDong Aisheng struct clk_scu *clk = dev_get_drvdata(dev); 510d0409631SDong Aisheng 511d0409631SDong Aisheng clk->rate = clk_hw_get_rate(&clk->hw); 512d0409631SDong Aisheng clk->is_enabled = clk_hw_is_enabled(&clk->hw); 513d0409631SDong Aisheng 514d0409631SDong Aisheng if (clk->rate) 515d0409631SDong Aisheng dev_dbg(dev, "save rate %d\n", clk->rate); 516d0409631SDong Aisheng 517d0409631SDong Aisheng if (clk->is_enabled) 518d0409631SDong Aisheng dev_dbg(dev, "save enabled state\n"); 519d0409631SDong Aisheng 520d0409631SDong Aisheng return 0; 521d0409631SDong Aisheng } 522d0409631SDong Aisheng 523d0409631SDong Aisheng static int __maybe_unused imx_clk_scu_resume(struct device *dev) 524d0409631SDong Aisheng { 525d0409631SDong Aisheng struct clk_scu *clk = dev_get_drvdata(dev); 526d0409631SDong Aisheng int ret = 0; 527d0409631SDong Aisheng 528d0409631SDong Aisheng if (clk->rate) { 529d0409631SDong Aisheng ret = clk_scu_set_rate(&clk->hw, clk->rate, 0); 530d0409631SDong Aisheng dev_dbg(dev, "restore rate %d %s\n", clk->rate, 531d0409631SDong Aisheng !ret ? "success" : "failed"); 532d0409631SDong Aisheng } 533d0409631SDong Aisheng 534d0409631SDong Aisheng if (clk->is_enabled) { 535d0409631SDong Aisheng ret = clk_scu_prepare(&clk->hw); 536d0409631SDong Aisheng dev_dbg(dev, "restore enabled state %s\n", 537d0409631SDong Aisheng !ret ? "success" : "failed"); 538d0409631SDong Aisheng } 539d0409631SDong Aisheng 540d0409631SDong Aisheng return ret; 541d0409631SDong Aisheng } 542d0409631SDong Aisheng 543d0409631SDong Aisheng static const struct dev_pm_ops imx_clk_scu_pm_ops = { 544d0409631SDong Aisheng SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(imx_clk_scu_suspend, 545d0409631SDong Aisheng imx_clk_scu_resume) 546d0409631SDong Aisheng }; 547d0409631SDong Aisheng 54877d8f306SDong Aisheng static struct platform_driver imx_clk_scu_driver = { 54977d8f306SDong Aisheng .driver = { 55077d8f306SDong Aisheng .name = "imx-scu-clk", 55177d8f306SDong Aisheng .suppress_bind_attrs = true, 552d0409631SDong Aisheng .pm = &imx_clk_scu_pm_ops, 55377d8f306SDong Aisheng }, 55477d8f306SDong Aisheng .probe = imx_clk_scu_probe, 55577d8f306SDong Aisheng }; 55677d8f306SDong Aisheng 55777d8f306SDong Aisheng static int imx_clk_scu_attach_pd(struct device *dev, u32 rsrc_id) 55877d8f306SDong Aisheng { 55977d8f306SDong Aisheng struct of_phandle_args genpdspec = { 56077d8f306SDong Aisheng .np = pd_np, 56177d8f306SDong Aisheng .args_count = 1, 56277d8f306SDong Aisheng .args[0] = rsrc_id, 56377d8f306SDong Aisheng }; 56477d8f306SDong Aisheng 5650d5f1f47SDong Aisheng if (rsrc_id == IMX_SC_R_A35 || rsrc_id == IMX_SC_R_A53 || 5660d5f1f47SDong Aisheng rsrc_id == IMX_SC_R_A72) 5670d5f1f47SDong Aisheng return 0; 5680d5f1f47SDong Aisheng 56977d8f306SDong Aisheng return of_genpd_add_device(&genpdspec, dev); 57077d8f306SDong Aisheng } 57177d8f306SDong Aisheng 57277d8f306SDong Aisheng struct clk_hw *imx_clk_scu_alloc_dev(const char *name, 57377d8f306SDong Aisheng const char * const *parents, 57477d8f306SDong Aisheng int num_parents, u32 rsrc_id, u8 clk_type) 57577d8f306SDong Aisheng { 57677d8f306SDong Aisheng struct imx_scu_clk_node clk = { 57777d8f306SDong Aisheng .name = name, 57877d8f306SDong Aisheng .rsrc = rsrc_id, 57977d8f306SDong Aisheng .clk_type = clk_type, 58077d8f306SDong Aisheng .parents = parents, 58177d8f306SDong Aisheng .num_parents = num_parents, 58277d8f306SDong Aisheng }; 58377d8f306SDong Aisheng struct platform_device *pdev; 58477d8f306SDong Aisheng int ret; 58577d8f306SDong Aisheng 58677d8f306SDong Aisheng pdev = platform_device_alloc(name, PLATFORM_DEVID_NONE); 58777d8f306SDong Aisheng if (!pdev) { 58877d8f306SDong Aisheng pr_err("%s: failed to allocate scu clk dev rsrc %d type %d\n", 58977d8f306SDong Aisheng name, rsrc_id, clk_type); 59077d8f306SDong Aisheng return ERR_PTR(-ENOMEM); 59177d8f306SDong Aisheng } 59277d8f306SDong Aisheng 59377d8f306SDong Aisheng ret = platform_device_add_data(pdev, &clk, sizeof(clk)); 59477d8f306SDong Aisheng if (ret) { 59577d8f306SDong Aisheng platform_device_put(pdev); 59677d8f306SDong Aisheng return ERR_PTR(ret); 59777d8f306SDong Aisheng } 59877d8f306SDong Aisheng 59977d8f306SDong Aisheng pdev->driver_override = "imx-scu-clk"; 60077d8f306SDong Aisheng 60177d8f306SDong Aisheng ret = imx_clk_scu_attach_pd(&pdev->dev, rsrc_id); 60277d8f306SDong Aisheng if (ret) 60377d8f306SDong Aisheng pr_warn("%s: failed to attached the power domain %d\n", 60477d8f306SDong Aisheng name, ret); 60577d8f306SDong Aisheng 60677d8f306SDong Aisheng platform_device_add(pdev); 60777d8f306SDong Aisheng 60877d8f306SDong Aisheng /* For API backwards compatiblilty, simply return NULL for success */ 60977d8f306SDong Aisheng return NULL; 61077d8f306SDong Aisheng } 61177d8f306SDong Aisheng 61277d8f306SDong Aisheng void imx_clk_scu_unregister(void) 61377d8f306SDong Aisheng { 61477d8f306SDong Aisheng struct imx_scu_clk_node *clk; 61577d8f306SDong Aisheng int i; 61677d8f306SDong Aisheng 61777d8f306SDong Aisheng for (i = 0; i < IMX_SC_R_LAST; i++) { 61877d8f306SDong Aisheng list_for_each_entry(clk, &imx_scu_clks[i], node) { 61977d8f306SDong Aisheng clk_hw_unregister(clk->hw); 62077d8f306SDong Aisheng kfree(clk); 62177d8f306SDong Aisheng } 62277d8f306SDong Aisheng } 62377d8f306SDong Aisheng } 624*5392c5deSDong Aisheng 625*5392c5deSDong Aisheng static unsigned long clk_gpr_div_scu_recalc_rate(struct clk_hw *hw, 626*5392c5deSDong Aisheng unsigned long parent_rate) 627*5392c5deSDong Aisheng { 628*5392c5deSDong Aisheng struct clk_gpr_scu *clk = to_clk_gpr_scu(hw); 629*5392c5deSDong Aisheng unsigned long rate = 0; 630*5392c5deSDong Aisheng u32 val; 631*5392c5deSDong Aisheng int err; 632*5392c5deSDong Aisheng 633*5392c5deSDong Aisheng err = imx_sc_misc_get_control(ccm_ipc_handle, clk->rsrc_id, 634*5392c5deSDong Aisheng clk->gpr_id, &val); 635*5392c5deSDong Aisheng 636*5392c5deSDong Aisheng rate = val ? parent_rate / 2 : parent_rate; 637*5392c5deSDong Aisheng 638*5392c5deSDong Aisheng return err ? 0 : rate; 639*5392c5deSDong Aisheng } 640*5392c5deSDong Aisheng 641*5392c5deSDong Aisheng static long clk_gpr_div_scu_round_rate(struct clk_hw *hw, unsigned long rate, 642*5392c5deSDong Aisheng unsigned long *prate) 643*5392c5deSDong Aisheng { 644*5392c5deSDong Aisheng if (rate < *prate) 645*5392c5deSDong Aisheng rate = *prate / 2; 646*5392c5deSDong Aisheng else 647*5392c5deSDong Aisheng rate = *prate; 648*5392c5deSDong Aisheng 649*5392c5deSDong Aisheng return rate; 650*5392c5deSDong Aisheng } 651*5392c5deSDong Aisheng 652*5392c5deSDong Aisheng static int clk_gpr_div_scu_set_rate(struct clk_hw *hw, unsigned long rate, 653*5392c5deSDong Aisheng unsigned long parent_rate) 654*5392c5deSDong Aisheng { 655*5392c5deSDong Aisheng struct clk_gpr_scu *clk = to_clk_gpr_scu(hw); 656*5392c5deSDong Aisheng uint32_t val; 657*5392c5deSDong Aisheng int err; 658*5392c5deSDong Aisheng 659*5392c5deSDong Aisheng val = (rate < parent_rate) ? 1 : 0; 660*5392c5deSDong Aisheng err = imx_sc_misc_set_control(ccm_ipc_handle, clk->rsrc_id, 661*5392c5deSDong Aisheng clk->gpr_id, val); 662*5392c5deSDong Aisheng 663*5392c5deSDong Aisheng return err ? -EINVAL : 0; 664*5392c5deSDong Aisheng } 665*5392c5deSDong Aisheng 666*5392c5deSDong Aisheng static const struct clk_ops clk_gpr_div_scu_ops = { 667*5392c5deSDong Aisheng .recalc_rate = clk_gpr_div_scu_recalc_rate, 668*5392c5deSDong Aisheng .round_rate = clk_gpr_div_scu_round_rate, 669*5392c5deSDong Aisheng .set_rate = clk_gpr_div_scu_set_rate, 670*5392c5deSDong Aisheng }; 671*5392c5deSDong Aisheng 672*5392c5deSDong Aisheng static u8 clk_gpr_mux_scu_get_parent(struct clk_hw *hw) 673*5392c5deSDong Aisheng { 674*5392c5deSDong Aisheng struct clk_gpr_scu *clk = to_clk_gpr_scu(hw); 675*5392c5deSDong Aisheng u32 val = 0; 676*5392c5deSDong Aisheng 677*5392c5deSDong Aisheng imx_sc_misc_get_control(ccm_ipc_handle, clk->rsrc_id, 678*5392c5deSDong Aisheng clk->gpr_id, &val); 679*5392c5deSDong Aisheng 680*5392c5deSDong Aisheng return (u8)val; 681*5392c5deSDong Aisheng } 682*5392c5deSDong Aisheng 683*5392c5deSDong Aisheng static int clk_gpr_mux_scu_set_parent(struct clk_hw *hw, u8 index) 684*5392c5deSDong Aisheng { 685*5392c5deSDong Aisheng struct clk_gpr_scu *clk = to_clk_gpr_scu(hw); 686*5392c5deSDong Aisheng 687*5392c5deSDong Aisheng return imx_sc_misc_set_control(ccm_ipc_handle, clk->rsrc_id, 688*5392c5deSDong Aisheng clk->gpr_id, index); 689*5392c5deSDong Aisheng } 690*5392c5deSDong Aisheng 691*5392c5deSDong Aisheng static const struct clk_ops clk_gpr_mux_scu_ops = { 692*5392c5deSDong Aisheng .get_parent = clk_gpr_mux_scu_get_parent, 693*5392c5deSDong Aisheng .set_parent = clk_gpr_mux_scu_set_parent, 694*5392c5deSDong Aisheng }; 695*5392c5deSDong Aisheng 696*5392c5deSDong Aisheng static int clk_gpr_gate_scu_prepare(struct clk_hw *hw) 697*5392c5deSDong Aisheng { 698*5392c5deSDong Aisheng struct clk_gpr_scu *clk = to_clk_gpr_scu(hw); 699*5392c5deSDong Aisheng 700*5392c5deSDong Aisheng return imx_sc_misc_set_control(ccm_ipc_handle, clk->rsrc_id, 701*5392c5deSDong Aisheng clk->gpr_id, !clk->gate_invert); 702*5392c5deSDong Aisheng } 703*5392c5deSDong Aisheng 704*5392c5deSDong Aisheng static void clk_gpr_gate_scu_unprepare(struct clk_hw *hw) 705*5392c5deSDong Aisheng { 706*5392c5deSDong Aisheng struct clk_gpr_scu *clk = to_clk_gpr_scu(hw); 707*5392c5deSDong Aisheng int ret; 708*5392c5deSDong Aisheng 709*5392c5deSDong Aisheng ret = imx_sc_misc_set_control(ccm_ipc_handle, clk->rsrc_id, 710*5392c5deSDong Aisheng clk->gpr_id, clk->gate_invert); 711*5392c5deSDong Aisheng if (ret) 712*5392c5deSDong Aisheng pr_err("%s: clk unprepare failed %d\n", clk_hw_get_name(hw), 713*5392c5deSDong Aisheng ret); 714*5392c5deSDong Aisheng } 715*5392c5deSDong Aisheng 716*5392c5deSDong Aisheng static int clk_gpr_gate_scu_is_prepared(struct clk_hw *hw) 717*5392c5deSDong Aisheng { 718*5392c5deSDong Aisheng struct clk_gpr_scu *clk = to_clk_gpr_scu(hw); 719*5392c5deSDong Aisheng int ret; 720*5392c5deSDong Aisheng u32 val; 721*5392c5deSDong Aisheng 722*5392c5deSDong Aisheng ret = imx_sc_misc_get_control(ccm_ipc_handle, clk->rsrc_id, 723*5392c5deSDong Aisheng clk->gpr_id, &val); 724*5392c5deSDong Aisheng if (ret) 725*5392c5deSDong Aisheng return ret; 726*5392c5deSDong Aisheng 727*5392c5deSDong Aisheng return clk->gate_invert ? !val : val; 728*5392c5deSDong Aisheng } 729*5392c5deSDong Aisheng 730*5392c5deSDong Aisheng static const struct clk_ops clk_gpr_gate_scu_ops = { 731*5392c5deSDong Aisheng .prepare = clk_gpr_gate_scu_prepare, 732*5392c5deSDong Aisheng .unprepare = clk_gpr_gate_scu_unprepare, 733*5392c5deSDong Aisheng .is_prepared = clk_gpr_gate_scu_is_prepared, 734*5392c5deSDong Aisheng }; 735*5392c5deSDong Aisheng 736*5392c5deSDong Aisheng struct clk_hw *__imx_clk_gpr_scu(const char *name, const char * const *parent_name, 737*5392c5deSDong Aisheng int num_parents, u32 rsrc_id, u8 gpr_id, u8 flags, 738*5392c5deSDong Aisheng bool invert) 739*5392c5deSDong Aisheng { 740*5392c5deSDong Aisheng struct imx_scu_clk_node *clk_node; 741*5392c5deSDong Aisheng struct clk_gpr_scu *clk; 742*5392c5deSDong Aisheng struct clk_hw *hw; 743*5392c5deSDong Aisheng struct clk_init_data init; 744*5392c5deSDong Aisheng int ret; 745*5392c5deSDong Aisheng 746*5392c5deSDong Aisheng if (rsrc_id >= IMX_SC_R_LAST || gpr_id >= IMX_SC_C_LAST) 747*5392c5deSDong Aisheng return ERR_PTR(-EINVAL); 748*5392c5deSDong Aisheng 749*5392c5deSDong Aisheng clk_node = kzalloc(sizeof(*clk_node), GFP_KERNEL); 750*5392c5deSDong Aisheng if (!clk_node) 751*5392c5deSDong Aisheng return ERR_PTR(-ENOMEM); 752*5392c5deSDong Aisheng 753*5392c5deSDong Aisheng clk = kzalloc(sizeof(*clk), GFP_KERNEL); 754*5392c5deSDong Aisheng if (!clk) { 755*5392c5deSDong Aisheng kfree(clk_node); 756*5392c5deSDong Aisheng return ERR_PTR(-ENOMEM); 757*5392c5deSDong Aisheng } 758*5392c5deSDong Aisheng 759*5392c5deSDong Aisheng clk->rsrc_id = rsrc_id; 760*5392c5deSDong Aisheng clk->gpr_id = gpr_id; 761*5392c5deSDong Aisheng clk->flags = flags; 762*5392c5deSDong Aisheng clk->gate_invert = invert; 763*5392c5deSDong Aisheng 764*5392c5deSDong Aisheng if (flags & IMX_SCU_GPR_CLK_GATE) 765*5392c5deSDong Aisheng init.ops = &clk_gpr_gate_scu_ops; 766*5392c5deSDong Aisheng 767*5392c5deSDong Aisheng if (flags & IMX_SCU_GPR_CLK_DIV) 768*5392c5deSDong Aisheng init.ops = &clk_gpr_div_scu_ops; 769*5392c5deSDong Aisheng 770*5392c5deSDong Aisheng if (flags & IMX_SCU_GPR_CLK_MUX) 771*5392c5deSDong Aisheng init.ops = &clk_gpr_mux_scu_ops; 772*5392c5deSDong Aisheng 773*5392c5deSDong Aisheng init.flags = 0; 774*5392c5deSDong Aisheng init.name = name; 775*5392c5deSDong Aisheng init.parent_names = parent_name; 776*5392c5deSDong Aisheng init.num_parents = num_parents; 777*5392c5deSDong Aisheng 778*5392c5deSDong Aisheng clk->hw.init = &init; 779*5392c5deSDong Aisheng 780*5392c5deSDong Aisheng hw = &clk->hw; 781*5392c5deSDong Aisheng ret = clk_hw_register(NULL, hw); 782*5392c5deSDong Aisheng if (ret) { 783*5392c5deSDong Aisheng kfree(clk); 784*5392c5deSDong Aisheng kfree(clk_node); 785*5392c5deSDong Aisheng hw = ERR_PTR(ret); 786*5392c5deSDong Aisheng } else { 787*5392c5deSDong Aisheng clk_node->hw = hw; 788*5392c5deSDong Aisheng clk_node->clk_type = gpr_id; 789*5392c5deSDong Aisheng list_add_tail(&clk_node->node, &imx_scu_clks[rsrc_id]); 790*5392c5deSDong Aisheng } 791*5392c5deSDong Aisheng 792*5392c5deSDong Aisheng return hw; 793*5392c5deSDong Aisheng } 794