|  | // SPDX-License-Identifier: GPL-2.0 | 
|  | /* | 
|  | * Zynq UltraScale+ MPSoC clock controller | 
|  | * | 
|  | *  Copyright (C) 2016-2018 Xilinx | 
|  | * | 
|  | * Based on drivers/clk/zynq/clkc.c | 
|  | */ | 
|  |  | 
|  | #include <linux/bitfield.h> | 
|  | #include <linux/clk.h> | 
|  | #include <linux/clk-provider.h> | 
|  | #include <linux/module.h> | 
|  | #include <linux/of_platform.h> | 
|  | #include <linux/slab.h> | 
|  | #include <linux/string.h> | 
|  |  | 
|  | #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_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; | 
|  | static const struct zynqmp_eemi_ops *eemi_ops; | 
|  |  | 
|  | /** | 
|  | * 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) { | 
|  | strncpy(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 = eemi_ops->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: Returns 0 | 
|  | */ | 
|  | 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]; | 
|  |  | 
|  | qdata.qid = PM_QID_CLOCK_GET_NAME; | 
|  | qdata.arg1 = clock_id; | 
|  |  | 
|  | eemi_ops->query_data(qdata, ret_payload); | 
|  | 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 = eemi_ops->query_data(qdata, ret_payload); | 
|  | memcpy(response, &ret_payload[1], sizeof(*response)); | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * 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; | 
|  |  | 
|  | qdata.qid = PM_QID_CLOCK_GET_FIXEDFACTOR_PARAMS; | 
|  | qdata.arg1 = clk_id; | 
|  |  | 
|  | ret = eemi_ops->query_data(qdata, ret_payload); | 
|  | if (ret) | 
|  | return ERR_PTR(ret); | 
|  |  | 
|  | mult = ret_payload[1]; | 
|  | div = ret_payload[2]; | 
|  |  | 
|  | hw = clk_hw_register_fixed_factor(NULL, name, | 
|  | parents[0], | 
|  | nodes->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 = eemi_ops->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 = eemi_ops->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]); | 
|  | (*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 = NULL; | 
|  | 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 = kasprintf(GFP_KERNEL, "%s%s", clk_name, | 
|  | clk_type_postfix[nodes[j].type]); | 
|  | } else { | 
|  | clk_out = kasprintf(GFP_KERNEL, "%s", clk_name); | 
|  | } | 
|  |  | 
|  | if (!clk_topology[nodes[j].type]) | 
|  | continue; | 
|  |  | 
|  | hw = (*clk_topology[nodes[j].type])(clk_out, 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; | 
|  | } | 
|  | kfree(clk_out); | 
|  | 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]); | 
|  | 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); | 
|  | if (!strcmp(name.name, RESERVED_CLK_NAME)) | 
|  | continue; | 
|  | strncpy(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; | 
|  | of_clk_add_hw_provider(np, of_clk_hw_onecell_get, zynqmp_data); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int zynqmp_clock_probe(struct platform_device *pdev) | 
|  | { | 
|  | int ret; | 
|  | struct device *dev = &pdev->dev; | 
|  |  | 
|  | eemi_ops = zynqmp_pm_get_eemi_ops(); | 
|  | if (IS_ERR(eemi_ops)) | 
|  | return PTR_ERR(eemi_ops); | 
|  |  | 
|  | ret = zynqmp_clk_setup(dev->of_node); | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static const struct of_device_id zynqmp_clock_of_match[] = { | 
|  | {.compatible = "xlnx,zynqmp-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); |