diff options
Diffstat (limited to 'drivers/clk/imx/clk-scu.c')
-rw-r--r-- | drivers/clk/imx/clk-scu.c | 123 |
1 files changed, 121 insertions, 2 deletions
diff --git a/drivers/clk/imx/clk-scu.c b/drivers/clk/imx/clk-scu.c index 7ccf7edfe11c..fbef740704d0 100644 --- a/drivers/clk/imx/clk-scu.c +++ b/drivers/clk/imx/clk-scu.c @@ -4,12 +4,17 @@ * Dong Aisheng <aisheng.dong@nxp.com> */ +#include <dt-bindings/firmware/imx/rsrc.h> +#include <linux/arm-smccc.h> #include <linux/clk-provider.h> #include <linux/err.h> #include <linux/slab.h> #include "clk-scu.h" +#define IMX_SIP_CPUFREQ 0xC2000001 +#define IMX_SIP_SET_CPUFREQ 0x00 + static struct imx_sc_ipc *ccm_ipc_handle; /* @@ -66,6 +71,41 @@ struct imx_sc_msg_get_clock_rate { }; /* + * struct imx_sc_msg_get_clock_parent - clock get parent protocol + * @hdr: SCU protocol header + * @req: get parent request protocol + * @resp: get parent response protocol + * + * This structure describes the SCU protocol of clock get parent + */ +struct imx_sc_msg_get_clock_parent { + struct imx_sc_rpc_msg hdr; + union { + struct req_get_clock_parent { + __le16 resource; + u8 clk; + } __packed req; + struct resp_get_clock_parent { + u8 parent; + } resp; + } data; +}; + +/* + * struct imx_sc_msg_set_clock_parent - clock set parent protocol + * @hdr: SCU protocol header + * @req: set parent request protocol + * + * This structure describes the SCU protocol of clock set parent + */ +struct imx_sc_msg_set_clock_parent { + struct imx_sc_rpc_msg hdr; + __le16 resource; + u8 clk; + u8 parent; +} __packed; + +/* * struct imx_sc_msg_req_clock_enable - clock gate protocol * @hdr: SCU protocol header * @resource: clock resource to gate @@ -145,6 +185,25 @@ static long clk_scu_round_rate(struct clk_hw *hw, unsigned long rate, return rate; } +static int clk_scu_atf_set_cpu_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_scu *clk = to_clk_scu(hw); + struct arm_smccc_res res; + unsigned long cluster_id; + + if (clk->rsrc_id == IMX_SC_R_A35) + cluster_id = 0; + else + return -EINVAL; + + /* CPU frequency scaling can ONLY be done by ARM-Trusted-Firmware */ + arm_smccc_smc(IMX_SIP_CPUFREQ, IMX_SIP_SET_CPUFREQ, + cluster_id, rate, 0, 0, 0, 0, &res); + + return 0; +} + /* * clk_scu_set_rate - Set rate for a SCU clock * @hw: clock to change rate for @@ -173,6 +232,49 @@ static int clk_scu_set_rate(struct clk_hw *hw, unsigned long rate, return imx_scu_call_rpc(ccm_ipc_handle, &msg, true); } +static u8 clk_scu_get_parent(struct clk_hw *hw) +{ + struct clk_scu *clk = to_clk_scu(hw); + struct imx_sc_msg_get_clock_parent msg; + struct imx_sc_rpc_msg *hdr = &msg.hdr; + int ret; + + hdr->ver = IMX_SC_RPC_VERSION; + hdr->svc = IMX_SC_RPC_SVC_PM; + hdr->func = IMX_SC_PM_FUNC_GET_CLOCK_PARENT; + hdr->size = 2; + + msg.data.req.resource = cpu_to_le16(clk->rsrc_id); + msg.data.req.clk = clk->clk_type; + + ret = imx_scu_call_rpc(ccm_ipc_handle, &msg, true); + if (ret) { + pr_err("%s: failed to get clock parent %d\n", + clk_hw_get_name(hw), ret); + return 0; + } + + return msg.data.resp.parent; +} + +static int clk_scu_set_parent(struct clk_hw *hw, u8 index) +{ + struct clk_scu *clk = to_clk_scu(hw); + struct imx_sc_msg_set_clock_parent msg; + struct imx_sc_rpc_msg *hdr = &msg.hdr; + + hdr->ver = IMX_SC_RPC_VERSION; + hdr->svc = IMX_SC_RPC_SVC_PM; + hdr->func = IMX_SC_PM_FUNC_SET_CLOCK_PARENT; + hdr->size = 2; + + msg.resource = cpu_to_le16(clk->rsrc_id); + msg.clk = clk->clk_type; + msg.parent = index; + + return imx_scu_call_rpc(ccm_ipc_handle, &msg, true); +} + static int sc_pm_clock_enable(struct imx_sc_ipc *ipc, u16 resource, u8 clk, bool enable, bool autog) { @@ -228,11 +330,22 @@ static const struct clk_ops clk_scu_ops = { .recalc_rate = clk_scu_recalc_rate, .round_rate = clk_scu_round_rate, .set_rate = clk_scu_set_rate, + .get_parent = clk_scu_get_parent, + .set_parent = clk_scu_set_parent, + .prepare = clk_scu_prepare, + .unprepare = clk_scu_unprepare, +}; + +static const struct clk_ops clk_scu_cpu_ops = { + .recalc_rate = clk_scu_recalc_rate, + .round_rate = clk_scu_round_rate, + .set_rate = clk_scu_atf_set_cpu_rate, .prepare = clk_scu_prepare, .unprepare = clk_scu_unprepare, }; -struct clk_hw *imx_clk_scu(const char *name, u32 rsrc_id, u8 clk_type) +struct clk_hw *__imx_clk_scu(const char *name, const char * const *parents, + int num_parents, u32 rsrc_id, u8 clk_type) { struct clk_init_data init; struct clk_scu *clk; @@ -248,7 +361,13 @@ struct clk_hw *imx_clk_scu(const char *name, u32 rsrc_id, u8 clk_type) init.name = name; init.ops = &clk_scu_ops; - init.num_parents = 0; + if (rsrc_id == IMX_SC_R_A35) + init.ops = &clk_scu_cpu_ops; + else + init.ops = &clk_scu_ops; + init.parent_names = parents; + init.num_parents = num_parents; + /* * Note on MX8, the clocks are tightly coupled with power domain * that once the power domain is off, the clock status may be |