/** * 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
*/ staticinlineint 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
*/ staticint 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
*/ staticint 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.
*/ staticint 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
*/ staticint 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;
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
*/ staticint 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;
/** * 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
*/ staticint 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;
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
*/ staticint 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;
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
*/ staticint __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
*/ staticint 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
*/ staticint __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;
/** * 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
*/ staticint 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
*/ staticint zynqmp_get_parent_list(struct device_node *np, u32 clk_id, constchar **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;
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
*/ staticint zynqmp_register_clocks(struct device_node *np)
{ int ret;
u32 i, total_parents = 0, type = 0; constchar *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;
}
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
*/ staticvoid 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;
/* * 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
*/ staticint 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;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.