// SPDX-License-Identifier: GPL-2.0 /* * Zynq UltraScale+ MPSoC clock controller * * Copyright (C) 2016-2019 Xilinx * * Based on drivers/clk/zynq/clkc.c */ #include #include #include #include #include #include #include #include #include "clk-zynqmp.h" #define MAX_PARENT 100 #define MAX_NODES 6 #define MAX_NAME_LEN 50 /* Flags for parents */ #define PARENT_CLK_SELF 0 #define PARENT_CLK_NODE1 1 #define PARENT_CLK_NODE2 2 #define PARENT_CLK_NODE3 3 #define PARENT_CLK_NODE4 4 #define PARENT_CLK_EXTERNAL 5 #define END_OF_CLK_NAME "END_OF_CLK" #define END_OF_TOPOLOGY_NODE 1 #define END_OF_PARENTS 1 #define RESERVED_CLK_NAME "" #define CLK_GET_NAME_RESP_LEN 16 #define CLK_GET_TOPOLOGY_RESP_WORDS 3 #define CLK_GET_PARENTS_RESP_WORDS 3 #define CLK_GET_ATTR_RESP_WORDS 1 enum clk_type { CLK_TYPE_OUTPUT, CLK_TYPE_EXTERNAL, }; /** * struct clock_parent - Clock parent * @name: Parent name * @id: Parent clock ID * @flag: Parent flags */ struct clock_parent { char name[MAX_NAME_LEN]; int id; u32 flag; }; /** * struct zynqmp_clock - Clock * @clk_name: Clock name * @valid: Validity flag of clock * @type: Clock type (Output/External) * @node: Clock topology nodes * @num_nodes: Number of nodes present in topology * @parent: Parent of clock * @num_parents: Number of parents of clock * @clk_id: Clock id */ struct zynqmp_clock { char clk_name[MAX_NAME_LEN]; u32 valid; enum clk_type type; struct clock_topology node[MAX_NODES]; u32 num_nodes; struct clock_parent parent[MAX_PARENT]; u32 num_parents; u32 clk_id; }; struct name_resp { char name[CLK_GET_NAME_RESP_LEN]; }; struct topology_resp { #define CLK_TOPOLOGY_TYPE GENMASK(3, 0) #define CLK_TOPOLOGY_CUSTOM_TYPE_FLAGS GENMASK(7, 4) #define CLK_TOPOLOGY_FLAGS GENMASK(23, 8) #define CLK_TOPOLOGY_TYPE_FLAGS GENMASK(31, 24) u32 topology[CLK_GET_TOPOLOGY_RESP_WORDS]; }; struct parents_resp { #define NA_PARENT 0xFFFFFFFF #define DUMMY_PARENT 0xFFFFFFFE #define CLK_PARENTS_ID GENMASK(15, 0) #define CLK_PARENTS_FLAGS GENMASK(31, 16) u32 parents[CLK_GET_PARENTS_RESP_WORDS]; }; struct attr_resp { #define CLK_ATTR_VALID BIT(0) #define CLK_ATTR_TYPE BIT(2) #define CLK_ATTR_NODE_INDEX GENMASK(13, 0) #define CLK_ATTR_NODE_TYPE GENMASK(19, 14) #define CLK_ATTR_NODE_SUBCLASS GENMASK(25, 20) #define CLK_ATTR_NODE_CLASS GENMASK(31, 26) u32 attr[CLK_GET_ATTR_RESP_WORDS]; }; static const char clk_type_postfix[][10] = { [TYPE_INVALID] = "", [TYPE_MUX] = "_mux", [TYPE_GATE] = "", [TYPE_DIV1] = "_div1", [TYPE_DIV2] = "_div2", [TYPE_FIXEDFACTOR] = "_ff", [TYPE_PLL] = "" }; static struct clk_hw *(* const clk_topology[]) (const char *name, u32 clk_id, const char * const *parents, u8 num_parents, const struct clock_topology *nodes) = { [TYPE_INVALID] = NULL, [TYPE_MUX] = zynqmp_clk_register_mux, [TYPE_PLL] = zynqmp_clk_register_pll, [TYPE_FIXEDFACTOR] = zynqmp_clk_register_fixed_factor, [TYPE_DIV1] = zynqmp_clk_register_divider, [TYPE_DIV2] = zynqmp_clk_register_divider, [TYPE_GATE] = zynqmp_clk_register_gate }; static struct zynqmp_clock *clock; static struct clk_hw_onecell_data *zynqmp_data; static unsigned int clock_max_idx; /** * zynqmp_is_valid_clock() - Check whether clock is valid or not * @clk_id: Clock index * * Return: 1 if clock is valid, 0 if clock is invalid else error code */ static inline int zynqmp_is_valid_clock(u32 clk_id) { if (clk_id >= clock_max_idx) return -ENODEV; return clock[clk_id].valid; } /** * zynqmp_get_clock_name() - Get name of clock from Clock index * @clk_id: Clock index * @clk_name: Name of clock * * Return: 0 on success else error code */ static int zynqmp_get_clock_name(u32 clk_id, char *clk_name) { int ret; ret = zynqmp_is_valid_clock(clk_id); if (ret == 1) { strscpy(clk_name, clock[clk_id].clk_name, MAX_NAME_LEN); return 0; } return ret == 0 ? -EINVAL : ret; } /** * zynqmp_get_clock_type() - Get type of clock * @clk_id: Clock index * @type: Clock type: CLK_TYPE_OUTPUT or CLK_TYPE_EXTERNAL * * Return: 0 on success else error code */ static int zynqmp_get_clock_type(u32 clk_id, u32 *type) { int ret; ret = zynqmp_is_valid_clock(clk_id); if (ret == 1) { *type = clock[clk_id].type; return 0; } return ret == 0 ? -EINVAL : ret; } /** * zynqmp_pm_clock_get_num_clocks() - Get number of clocks in system * @nclocks: Number of clocks in system/board. * * Call firmware API to get number of clocks. * * Return: 0 on success else error code. */ static int zynqmp_pm_clock_get_num_clocks(u32 *nclocks) { struct zynqmp_pm_query_data qdata = {0}; u32 ret_payload[PAYLOAD_ARG_CNT]; int ret; qdata.qid = PM_QID_CLOCK_GET_NUM_CLOCKS; ret = zynqmp_pm_query_data(qdata, ret_payload); *nclocks = ret_payload[1]; return ret; } /** * zynqmp_pm_clock_get_name() - Get the name of clock for given id * @clock_id: ID of the clock to be queried * @response: Name of the clock with the given id * * This function is used to get name of clock specified by given * clock ID. * * Return: 0 on success else error+reason */ static int zynqmp_pm_clock_get_name(u32 clock_id, struct name_resp *response) { struct zynqmp_pm_query_data qdata = {0}; u32 ret_payload[PAYLOAD_ARG_CNT]; int ret; qdata.qid = PM_QID_CLOCK_GET_NAME; qdata.arg1 = clock_id; ret = zynqmp_pm_query_data(qdata, ret_payload); if (ret) return ret; memcpy(response, ret_payload, sizeof(*response)); return 0; } /** * zynqmp_pm_clock_get_topology() - Get the topology of clock for given id * @clock_id: ID of the clock to be queried * @index: Node index of clock topology * @response: Buffer used for the topology response * * This function is used to get topology information for the clock * specified by given clock ID. * * This API will return 3 node of topology with a single response. To get * other nodes, master should call same API in loop with new * index till error is returned. E.g First call should have * index 0 which will return nodes 0,1 and 2. Next call, index * should be 3 which will return nodes 3,4 and 5 and so on. * * Return: 0 on success else error+reason */ static int zynqmp_pm_clock_get_topology(u32 clock_id, u32 index, struct topology_resp *response) { struct zynqmp_pm_query_data qdata = {0}; u32 ret_payload[PAYLOAD_ARG_CNT]; int ret; qdata.qid = PM_QID_CLOCK_GET_TOPOLOGY; qdata.arg1 = clock_id; qdata.arg2 = index; ret = zynqmp_pm_query_data(qdata, ret_payload); memcpy(response, &ret_payload[1], sizeof(*response)); return ret; } unsigned long zynqmp_clk_map_common_ccf_flags(const u32 zynqmp_flag) { unsigned long ccf_flag = 0; if (zynqmp_flag & ZYNQMP_CLK_SET_RATE_GATE) ccf_flag |= CLK_SET_RATE_GATE; if (zynqmp_flag & ZYNQMP_CLK_SET_PARENT_GATE) ccf_flag |= CLK_SET_PARENT_GATE; if (zynqmp_flag & ZYNQMP_CLK_SET_RATE_PARENT) ccf_flag |= CLK_SET_RATE_PARENT; if (zynqmp_flag & ZYNQMP_CLK_IGNORE_UNUSED) ccf_flag |= CLK_IGNORE_UNUSED; if (zynqmp_flag & ZYNQMP_CLK_SET_RATE_NO_REPARENT) ccf_flag |= CLK_SET_RATE_NO_REPARENT; if (zynqmp_flag & ZYNQMP_CLK_IS_CRITICAL) ccf_flag |= CLK_IS_CRITICAL; return ccf_flag; } /** * zynqmp_clk_register_fixed_factor() - Register fixed factor with the * clock framework * @name: Name of this clock * @clk_id: Clock ID * @parents: Name of this clock's parents * @num_parents: Number of parents * @nodes: Clock topology node * * Return: clock hardware to the registered clock */ struct clk_hw *zynqmp_clk_register_fixed_factor(const char *name, u32 clk_id, const char * const *parents, u8 num_parents, const struct clock_topology *nodes) { u32 mult, div; struct clk_hw *hw; struct zynqmp_pm_query_data qdata = {0}; u32 ret_payload[PAYLOAD_ARG_CNT]; int ret; unsigned long flag; qdata.qid = PM_QID_CLOCK_GET_FIXEDFACTOR_PARAMS; qdata.arg1 = clk_id; ret = zynqmp_pm_query_data(qdata, ret_payload); if (ret) return ERR_PTR(ret); mult = ret_payload[1]; div = ret_payload[2]; flag = zynqmp_clk_map_common_ccf_flags(nodes->flag); hw = clk_hw_register_fixed_factor(NULL, name, parents[0], flag, mult, div); return hw; } /** * zynqmp_pm_clock_get_parents() - Get the first 3 parents of clock for given id * @clock_id: Clock ID * @index: Parent index * @response: Parents of the given clock * * This function is used to get 3 parents for the clock specified by * given clock ID. * * This API will return 3 parents with a single response. To get * other parents, master should call same API in loop with new * parent index till error is returned. E.g First call should have * index 0 which will return parents 0,1 and 2. Next call, index * should be 3 which will return parent 3,4 and 5 and so on. * * Return: 0 on success else error+reason */ static int zynqmp_pm_clock_get_parents(u32 clock_id, u32 index, struct parents_resp *response) { struct zynqmp_pm_query_data qdata = {0}; u32 ret_payload[PAYLOAD_ARG_CNT]; int ret; qdata.qid = PM_QID_CLOCK_GET_PARENTS; qdata.arg1 = clock_id; qdata.arg2 = index; ret = zynqmp_pm_query_data(qdata, ret_payload); memcpy(response, &ret_payload[1], sizeof(*response)); return ret; } /** * zynqmp_pm_clock_get_attributes() - Get the attributes of clock for given id * @clock_id: Clock ID * @response: Clock attributes response * * This function is used to get clock's attributes(e.g. valid, clock type, etc). * * Return: 0 on success else error+reason */ static int zynqmp_pm_clock_get_attributes(u32 clock_id, struct attr_resp *response) { struct zynqmp_pm_query_data qdata = {0}; u32 ret_payload[PAYLOAD_ARG_CNT]; int ret; qdata.qid = PM_QID_CLOCK_GET_ATTRIBUTES; qdata.arg1 = clock_id; ret = zynqmp_pm_query_data(qdata, ret_payload); memcpy(response, &ret_payload[1], sizeof(*response)); return ret; } /** * __zynqmp_clock_get_topology() - Get topology data of clock from firmware * response data * @topology: Clock topology * @response: Clock topology data received from firmware * @nnodes: Number of nodes * * Return: 0 on success else error+reason */ static int __zynqmp_clock_get_topology(struct clock_topology *topology, struct topology_resp *response, u32 *nnodes) { int i; u32 type; for (i = 0; i < ARRAY_SIZE(response->topology); i++) { type = FIELD_GET(CLK_TOPOLOGY_TYPE, response->topology[i]); if (type == TYPE_INVALID) return END_OF_TOPOLOGY_NODE; topology[*nnodes].type = type; topology[*nnodes].flag = FIELD_GET(CLK_TOPOLOGY_FLAGS, response->topology[i]); topology[*nnodes].type_flag = FIELD_GET(CLK_TOPOLOGY_TYPE_FLAGS, response->topology[i]); topology[*nnodes].custom_type_flag = FIELD_GET(CLK_TOPOLOGY_CUSTOM_TYPE_FLAGS, response->topology[i]); (*nnodes)++; } return 0; } /** * zynqmp_clock_get_topology() - Get topology of clock from firmware using * PM_API * @clk_id: Clock index * @topology: Clock topology * @num_nodes: Number of nodes * * Return: 0 on success else error+reason */ static int zynqmp_clock_get_topology(u32 clk_id, struct clock_topology *topology, u32 *num_nodes) { int j, ret; struct topology_resp response = { }; *num_nodes = 0; for (j = 0; j <= MAX_NODES; j += ARRAY_SIZE(response.topology)) { ret = zynqmp_pm_clock_get_topology(clock[clk_id].clk_id, j, &response); if (ret) return ret; ret = __zynqmp_clock_get_topology(topology, &response, num_nodes); if (ret == END_OF_TOPOLOGY_NODE) return 0; } return 0; } /** * __zynqmp_clock_get_parents() - Get parents info of clock from firmware * response data * @parents: Clock parents * @response: Clock parents data received from firmware * @nparent: Number of parent * * Return: 0 on success else error+reason */ static int __zynqmp_clock_get_parents(struct clock_parent *parents, struct parents_resp *response, u32 *nparent) { int i; struct clock_parent *parent; for (i = 0; i < ARRAY_SIZE(response->parents); i++) { if (response->parents[i] == NA_PARENT) return END_OF_PARENTS; parent = &parents[i]; parent->id = FIELD_GET(CLK_PARENTS_ID, response->parents[i]); if (response->parents[i] == DUMMY_PARENT) { strcpy(parent->name, "dummy_name"); parent->flag = 0; } else { parent->flag = FIELD_GET(CLK_PARENTS_FLAGS, response->parents[i]); if (zynqmp_get_clock_name(parent->id, parent->name)) continue; } *nparent += 1; } return 0; } /** * zynqmp_clock_get_parents() - Get parents info from firmware using PM_API * @clk_id: Clock index * @parents: Clock parents * @num_parents: Total number of parents * * Return: 0 on success else error+reason */ static int zynqmp_clock_get_parents(u32 clk_id, struct clock_parent *parents, u32 *num_parents) { int j = 0, ret; struct parents_resp response = { }; *num_parents = 0; do { /* Get parents from firmware */ ret = zynqmp_pm_clock_get_parents(clock[clk_id].clk_id, j, &response); if (ret) return ret; ret = __zynqmp_clock_get_parents(&parents[j], &response, num_parents); if (ret == END_OF_PARENTS) return 0; j += ARRAY_SIZE(response.parents); } while (*num_parents <= MAX_PARENT); return 0; } /** * zynqmp_get_parent_list() - Create list of parents name * @np: Device node * @clk_id: Clock index * @parent_list: List of parent's name * @num_parents: Total number of parents * * Return: 0 on success else error+reason */ static int zynqmp_get_parent_list(struct device_node *np, u32 clk_id, const char **parent_list, u32 *num_parents) { int i = 0, ret; u32 total_parents = clock[clk_id].num_parents; struct clock_topology *clk_nodes; struct clock_parent *parents; clk_nodes = clock[clk_id].node; parents = clock[clk_id].parent; for (i = 0; i < total_parents; i++) { if (!parents[i].flag) { parent_list[i] = parents[i].name; } else if (parents[i].flag == PARENT_CLK_EXTERNAL) { ret = of_property_match_string(np, "clock-names", parents[i].name); if (ret < 0) strcpy(parents[i].name, "dummy_name"); parent_list[i] = parents[i].name; } else { strcat(parents[i].name, clk_type_postfix[clk_nodes[parents[i].flag - 1]. type]); parent_list[i] = parents[i].name; } } *num_parents = total_parents; return 0; } /** * zynqmp_register_clk_topology() - Register clock topology * @clk_id: Clock index * @clk_name: Clock Name * @num_parents: Total number of parents * @parent_names: List of parents name * * Return: Returns either clock hardware or error+reason */ static struct clk_hw *zynqmp_register_clk_topology(int clk_id, char *clk_name, int num_parents, const char **parent_names) { int j; u32 num_nodes, clk_dev_id; char *clk_out[MAX_NODES]; struct clock_topology *nodes; struct clk_hw *hw = NULL; nodes = clock[clk_id].node; num_nodes = clock[clk_id].num_nodes; clk_dev_id = clock[clk_id].clk_id; for (j = 0; j < num_nodes; j++) { /* * Clock name received from firmware is output clock name. * Intermediate clock names are postfixed with type of clock. */ if (j != (num_nodes - 1)) { clk_out[j] = kasprintf(GFP_KERNEL, "%s%s", clk_name, clk_type_postfix[nodes[j].type]); } else { clk_out[j] = kasprintf(GFP_KERNEL, "%s", clk_name); } if (!clk_topology[nodes[j].type]) continue; hw = (*clk_topology[nodes[j].type])(clk_out[j], clk_dev_id, parent_names, num_parents, &nodes[j]); if (IS_ERR(hw)) pr_warn_once("%s() 0x%x: %s register fail with %ld\n", __func__, clk_dev_id, clk_name, PTR_ERR(hw)); parent_names[0] = clk_out[j]; } for (j = 0; j < num_nodes; j++) kfree(clk_out[j]); return hw; } /** * zynqmp_register_clocks() - Register clocks * @np: Device node * * Return: 0 on success else error code */ static int zynqmp_register_clocks(struct device_node *np) { int ret; u32 i, total_parents = 0, type = 0; const char *parent_names[MAX_PARENT]; for (i = 0; i < clock_max_idx; i++) { char clk_name[MAX_NAME_LEN]; /* get clock name, continue to next clock if name not found */ if (zynqmp_get_clock_name(i, clk_name)) continue; /* Check if clock is valid and output clock. * Do not register invalid or external clock. */ ret = zynqmp_get_clock_type(i, &type); if (ret || type != CLK_TYPE_OUTPUT) continue; /* Get parents of clock*/ if (zynqmp_get_parent_list(np, i, parent_names, &total_parents)) { WARN_ONCE(1, "No parents found for %s\n", clock[i].clk_name); continue; } zynqmp_data->hws[i] = zynqmp_register_clk_topology(i, clk_name, total_parents, parent_names); } for (i = 0; i < clock_max_idx; i++) { if (IS_ERR(zynqmp_data->hws[i])) { pr_err("Zynq Ultrascale+ MPSoC clk %s: register failed with %ld\n", clock[i].clk_name, PTR_ERR(zynqmp_data->hws[i])); WARN_ON(1); } } return 0; } /** * zynqmp_get_clock_info() - Get clock information from firmware using PM_API */ static void zynqmp_get_clock_info(void) { int i, ret; u32 type = 0; u32 nodetype, subclass, class; struct attr_resp attr; struct name_resp name; for (i = 0; i < clock_max_idx; i++) { ret = zynqmp_pm_clock_get_attributes(i, &attr); if (ret) continue; clock[i].valid = FIELD_GET(CLK_ATTR_VALID, attr.attr[0]); /* skip query for Invalid clock */ ret = zynqmp_is_valid_clock(i); if (ret != CLK_ATTR_VALID) continue; clock[i].type = FIELD_GET(CLK_ATTR_TYPE, attr.attr[0]) ? CLK_TYPE_EXTERNAL : CLK_TYPE_OUTPUT; nodetype = FIELD_GET(CLK_ATTR_NODE_TYPE, attr.attr[0]); subclass = FIELD_GET(CLK_ATTR_NODE_SUBCLASS, attr.attr[0]); class = FIELD_GET(CLK_ATTR_NODE_CLASS, attr.attr[0]); clock[i].clk_id = FIELD_PREP(CLK_ATTR_NODE_CLASS, class) | FIELD_PREP(CLK_ATTR_NODE_SUBCLASS, subclass) | FIELD_PREP(CLK_ATTR_NODE_TYPE, nodetype) | FIELD_PREP(CLK_ATTR_NODE_INDEX, i); zynqmp_pm_clock_get_name(clock[i].clk_id, &name); /* * Terminate with NULL character in case name provided by firmware * is longer and truncated due to size limit. */ name.name[sizeof(name.name) - 1] = '\0'; if (!strcmp(name.name, RESERVED_CLK_NAME)) continue; strscpy(clock[i].clk_name, name.name, MAX_NAME_LEN); } /* Get topology of all clock */ for (i = 0; i < clock_max_idx; i++) { ret = zynqmp_get_clock_type(i, &type); if (ret || type != CLK_TYPE_OUTPUT) continue; ret = zynqmp_clock_get_topology(i, clock[i].node, &clock[i].num_nodes); if (ret) continue; ret = zynqmp_clock_get_parents(i, clock[i].parent, &clock[i].num_parents); if (ret) continue; } } /** * zynqmp_clk_setup() - Setup the clock framework and register clocks * @np: Device node * * Return: 0 on success else error code */ static int zynqmp_clk_setup(struct device_node *np) { int ret; ret = zynqmp_pm_clock_get_num_clocks(&clock_max_idx); if (ret) return ret; zynqmp_data = kzalloc(struct_size(zynqmp_data, hws, clock_max_idx), GFP_KERNEL); if (!zynqmp_data) return -ENOMEM; clock = kcalloc(clock_max_idx, sizeof(*clock), GFP_KERNEL); if (!clock) { kfree(zynqmp_data); return -ENOMEM; } zynqmp_get_clock_info(); zynqmp_register_clocks(np); zynqmp_data->num = clock_max_idx; return of_clk_add_hw_provider(np, of_clk_hw_onecell_get, zynqmp_data); } static int zynqmp_clock_probe(struct platform_device *pdev) { int ret; struct device *dev = &pdev->dev; ret = zynqmp_clk_setup(dev->of_node); return ret; } static const struct of_device_id zynqmp_clock_of_match[] = { {.compatible = "xlnx,zynqmp-clk"}, {.compatible = "xlnx,versal-clk"}, {}, }; MODULE_DEVICE_TABLE(of, zynqmp_clock_of_match); static struct platform_driver zynqmp_clock_driver = { .driver = { .name = "zynqmp_clock", .of_match_table = zynqmp_clock_of_match, }, .probe = zynqmp_clock_probe, }; module_platform_driver(zynqmp_clock_driver);