18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * SCI Clock driver for keystone based devices 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2015-2016 Texas Instruments Incorporated - https://www.ti.com/ 58c2ecf20Sopenharmony_ci * Tero Kristo <t-kristo@ti.com> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify 88c2ecf20Sopenharmony_ci * it under the terms of the GNU General Public License version 2 as 98c2ecf20Sopenharmony_ci * published by the Free Software Foundation. 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * This program is distributed "as is" WITHOUT ANY WARRANTY of any 128c2ecf20Sopenharmony_ci * kind, whether express or implied; without even the implied warranty 138c2ecf20Sopenharmony_ci * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 148c2ecf20Sopenharmony_ci * GNU General Public License for more details. 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_ci#include <linux/clk-provider.h> 178c2ecf20Sopenharmony_ci#include <linux/err.h> 188c2ecf20Sopenharmony_ci#include <linux/io.h> 198c2ecf20Sopenharmony_ci#include <linux/module.h> 208c2ecf20Sopenharmony_ci#include <linux/of_address.h> 218c2ecf20Sopenharmony_ci#include <linux/of_device.h> 228c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 238c2ecf20Sopenharmony_ci#include <linux/slab.h> 248c2ecf20Sopenharmony_ci#include <linux/soc/ti/ti_sci_protocol.h> 258c2ecf20Sopenharmony_ci#include <linux/bsearch.h> 268c2ecf20Sopenharmony_ci#include <linux/list_sort.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#define SCI_CLK_SSC_ENABLE BIT(0) 298c2ecf20Sopenharmony_ci#define SCI_CLK_ALLOW_FREQ_CHANGE BIT(1) 308c2ecf20Sopenharmony_ci#define SCI_CLK_INPUT_TERMINATION BIT(2) 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/** 338c2ecf20Sopenharmony_ci * struct sci_clk_provider - TI SCI clock provider representation 348c2ecf20Sopenharmony_ci * @sci: Handle to the System Control Interface protocol handler 358c2ecf20Sopenharmony_ci * @ops: Pointer to the SCI ops to be used by the clocks 368c2ecf20Sopenharmony_ci * @dev: Device pointer for the clock provider 378c2ecf20Sopenharmony_ci * @clocks: Clocks array for this device 388c2ecf20Sopenharmony_ci * @num_clocks: Total number of clocks for this provider 398c2ecf20Sopenharmony_ci */ 408c2ecf20Sopenharmony_cistruct sci_clk_provider { 418c2ecf20Sopenharmony_ci const struct ti_sci_handle *sci; 428c2ecf20Sopenharmony_ci const struct ti_sci_clk_ops *ops; 438c2ecf20Sopenharmony_ci struct device *dev; 448c2ecf20Sopenharmony_ci struct sci_clk **clocks; 458c2ecf20Sopenharmony_ci int num_clocks; 468c2ecf20Sopenharmony_ci}; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci/** 498c2ecf20Sopenharmony_ci * struct sci_clk - TI SCI clock representation 508c2ecf20Sopenharmony_ci * @hw: Hardware clock cookie for common clock framework 518c2ecf20Sopenharmony_ci * @dev_id: Device index 528c2ecf20Sopenharmony_ci * @clk_id: Clock index 538c2ecf20Sopenharmony_ci * @num_parents: Number of parents for this clock 548c2ecf20Sopenharmony_ci * @provider: Master clock provider 558c2ecf20Sopenharmony_ci * @flags: Flags for the clock 568c2ecf20Sopenharmony_ci * @node: Link for handling clocks probed via DT 578c2ecf20Sopenharmony_ci * @cached_req: Cached requested freq for determine rate calls 588c2ecf20Sopenharmony_ci * @cached_res: Cached result freq for determine rate calls 598c2ecf20Sopenharmony_ci */ 608c2ecf20Sopenharmony_cistruct sci_clk { 618c2ecf20Sopenharmony_ci struct clk_hw hw; 628c2ecf20Sopenharmony_ci u16 dev_id; 638c2ecf20Sopenharmony_ci u32 clk_id; 648c2ecf20Sopenharmony_ci u32 num_parents; 658c2ecf20Sopenharmony_ci struct sci_clk_provider *provider; 668c2ecf20Sopenharmony_ci u8 flags; 678c2ecf20Sopenharmony_ci struct list_head node; 688c2ecf20Sopenharmony_ci unsigned long cached_req; 698c2ecf20Sopenharmony_ci unsigned long cached_res; 708c2ecf20Sopenharmony_ci}; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci#define to_sci_clk(_hw) container_of(_hw, struct sci_clk, hw) 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci/** 758c2ecf20Sopenharmony_ci * sci_clk_prepare - Prepare (enable) a TI SCI clock 768c2ecf20Sopenharmony_ci * @hw: clock to prepare 778c2ecf20Sopenharmony_ci * 788c2ecf20Sopenharmony_ci * Prepares a clock to be actively used. Returns the SCI protocol status. 798c2ecf20Sopenharmony_ci */ 808c2ecf20Sopenharmony_cistatic int sci_clk_prepare(struct clk_hw *hw) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci struct sci_clk *clk = to_sci_clk(hw); 838c2ecf20Sopenharmony_ci bool enable_ssc = clk->flags & SCI_CLK_SSC_ENABLE; 848c2ecf20Sopenharmony_ci bool allow_freq_change = clk->flags & SCI_CLK_ALLOW_FREQ_CHANGE; 858c2ecf20Sopenharmony_ci bool input_termination = clk->flags & SCI_CLK_INPUT_TERMINATION; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci return clk->provider->ops->get_clock(clk->provider->sci, clk->dev_id, 888c2ecf20Sopenharmony_ci clk->clk_id, enable_ssc, 898c2ecf20Sopenharmony_ci allow_freq_change, 908c2ecf20Sopenharmony_ci input_termination); 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci/** 948c2ecf20Sopenharmony_ci * sci_clk_unprepare - Un-prepares (disables) a TI SCI clock 958c2ecf20Sopenharmony_ci * @hw: clock to unprepare 968c2ecf20Sopenharmony_ci * 978c2ecf20Sopenharmony_ci * Un-prepares a clock from active state. 988c2ecf20Sopenharmony_ci */ 998c2ecf20Sopenharmony_cistatic void sci_clk_unprepare(struct clk_hw *hw) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci struct sci_clk *clk = to_sci_clk(hw); 1028c2ecf20Sopenharmony_ci int ret; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci ret = clk->provider->ops->put_clock(clk->provider->sci, clk->dev_id, 1058c2ecf20Sopenharmony_ci clk->clk_id); 1068c2ecf20Sopenharmony_ci if (ret) 1078c2ecf20Sopenharmony_ci dev_err(clk->provider->dev, 1088c2ecf20Sopenharmony_ci "unprepare failed for dev=%d, clk=%d, ret=%d\n", 1098c2ecf20Sopenharmony_ci clk->dev_id, clk->clk_id, ret); 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci/** 1138c2ecf20Sopenharmony_ci * sci_clk_is_prepared - Check if a TI SCI clock is prepared or not 1148c2ecf20Sopenharmony_ci * @hw: clock to check status for 1158c2ecf20Sopenharmony_ci * 1168c2ecf20Sopenharmony_ci * Checks if a clock is prepared (enabled) in hardware. Returns non-zero 1178c2ecf20Sopenharmony_ci * value if clock is enabled, zero otherwise. 1188c2ecf20Sopenharmony_ci */ 1198c2ecf20Sopenharmony_cistatic int sci_clk_is_prepared(struct clk_hw *hw) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci struct sci_clk *clk = to_sci_clk(hw); 1228c2ecf20Sopenharmony_ci bool req_state, current_state; 1238c2ecf20Sopenharmony_ci int ret; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci ret = clk->provider->ops->is_on(clk->provider->sci, clk->dev_id, 1268c2ecf20Sopenharmony_ci clk->clk_id, &req_state, 1278c2ecf20Sopenharmony_ci ¤t_state); 1288c2ecf20Sopenharmony_ci if (ret) { 1298c2ecf20Sopenharmony_ci dev_err(clk->provider->dev, 1308c2ecf20Sopenharmony_ci "is_prepared failed for dev=%d, clk=%d, ret=%d\n", 1318c2ecf20Sopenharmony_ci clk->dev_id, clk->clk_id, ret); 1328c2ecf20Sopenharmony_ci return 0; 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci return req_state; 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci/** 1398c2ecf20Sopenharmony_ci * sci_clk_recalc_rate - Get clock rate for a TI SCI clock 1408c2ecf20Sopenharmony_ci * @hw: clock to get rate for 1418c2ecf20Sopenharmony_ci * @parent_rate: parent rate provided by common clock framework, not used 1428c2ecf20Sopenharmony_ci * 1438c2ecf20Sopenharmony_ci * Gets the current clock rate of a TI SCI clock. Returns the current 1448c2ecf20Sopenharmony_ci * clock rate, or zero in failure. 1458c2ecf20Sopenharmony_ci */ 1468c2ecf20Sopenharmony_cistatic unsigned long sci_clk_recalc_rate(struct clk_hw *hw, 1478c2ecf20Sopenharmony_ci unsigned long parent_rate) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci struct sci_clk *clk = to_sci_clk(hw); 1508c2ecf20Sopenharmony_ci u64 freq; 1518c2ecf20Sopenharmony_ci int ret; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci ret = clk->provider->ops->get_freq(clk->provider->sci, clk->dev_id, 1548c2ecf20Sopenharmony_ci clk->clk_id, &freq); 1558c2ecf20Sopenharmony_ci if (ret) { 1568c2ecf20Sopenharmony_ci dev_err(clk->provider->dev, 1578c2ecf20Sopenharmony_ci "recalc-rate failed for dev=%d, clk=%d, ret=%d\n", 1588c2ecf20Sopenharmony_ci clk->dev_id, clk->clk_id, ret); 1598c2ecf20Sopenharmony_ci return 0; 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci return freq; 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci/** 1668c2ecf20Sopenharmony_ci * sci_clk_determine_rate - Determines a clock rate a clock can be set to 1678c2ecf20Sopenharmony_ci * @hw: clock to change rate for 1688c2ecf20Sopenharmony_ci * @req: requested rate configuration for the clock 1698c2ecf20Sopenharmony_ci * 1708c2ecf20Sopenharmony_ci * Determines a suitable clock rate and parent for a TI SCI clock. 1718c2ecf20Sopenharmony_ci * The parent handling is un-used, as generally the parent clock rates 1728c2ecf20Sopenharmony_ci * are not known by the kernel; instead these are internally handled 1738c2ecf20Sopenharmony_ci * by the firmware. Returns 0 on success, negative error value on failure. 1748c2ecf20Sopenharmony_ci */ 1758c2ecf20Sopenharmony_cistatic int sci_clk_determine_rate(struct clk_hw *hw, 1768c2ecf20Sopenharmony_ci struct clk_rate_request *req) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci struct sci_clk *clk = to_sci_clk(hw); 1798c2ecf20Sopenharmony_ci int ret; 1808c2ecf20Sopenharmony_ci u64 new_rate; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci if (clk->cached_req && clk->cached_req == req->rate) { 1838c2ecf20Sopenharmony_ci req->rate = clk->cached_res; 1848c2ecf20Sopenharmony_ci return 0; 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci ret = clk->provider->ops->get_best_match_freq(clk->provider->sci, 1888c2ecf20Sopenharmony_ci clk->dev_id, 1898c2ecf20Sopenharmony_ci clk->clk_id, 1908c2ecf20Sopenharmony_ci req->min_rate, 1918c2ecf20Sopenharmony_ci req->rate, 1928c2ecf20Sopenharmony_ci req->max_rate, 1938c2ecf20Sopenharmony_ci &new_rate); 1948c2ecf20Sopenharmony_ci if (ret) { 1958c2ecf20Sopenharmony_ci dev_err(clk->provider->dev, 1968c2ecf20Sopenharmony_ci "determine-rate failed for dev=%d, clk=%d, ret=%d\n", 1978c2ecf20Sopenharmony_ci clk->dev_id, clk->clk_id, ret); 1988c2ecf20Sopenharmony_ci return ret; 1998c2ecf20Sopenharmony_ci } 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci clk->cached_req = req->rate; 2028c2ecf20Sopenharmony_ci clk->cached_res = new_rate; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci req->rate = new_rate; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci return 0; 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci/** 2108c2ecf20Sopenharmony_ci * sci_clk_set_rate - Set rate for a TI SCI clock 2118c2ecf20Sopenharmony_ci * @hw: clock to change rate for 2128c2ecf20Sopenharmony_ci * @rate: target rate for the clock 2138c2ecf20Sopenharmony_ci * @parent_rate: rate of the clock parent, not used for TI SCI clocks 2148c2ecf20Sopenharmony_ci * 2158c2ecf20Sopenharmony_ci * Sets a clock frequency for a TI SCI clock. Returns the TI SCI 2168c2ecf20Sopenharmony_ci * protocol status. 2178c2ecf20Sopenharmony_ci */ 2188c2ecf20Sopenharmony_cistatic int sci_clk_set_rate(struct clk_hw *hw, unsigned long rate, 2198c2ecf20Sopenharmony_ci unsigned long parent_rate) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci struct sci_clk *clk = to_sci_clk(hw); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci return clk->provider->ops->set_freq(clk->provider->sci, clk->dev_id, 2248c2ecf20Sopenharmony_ci clk->clk_id, rate / 10 * 9, rate, 2258c2ecf20Sopenharmony_ci rate / 10 * 11); 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci/** 2298c2ecf20Sopenharmony_ci * sci_clk_get_parent - Get the current parent of a TI SCI clock 2308c2ecf20Sopenharmony_ci * @hw: clock to get parent for 2318c2ecf20Sopenharmony_ci * 2328c2ecf20Sopenharmony_ci * Returns the index of the currently selected parent for a TI SCI clock. 2338c2ecf20Sopenharmony_ci */ 2348c2ecf20Sopenharmony_cistatic u8 sci_clk_get_parent(struct clk_hw *hw) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci struct sci_clk *clk = to_sci_clk(hw); 2378c2ecf20Sopenharmony_ci u32 parent_id = 0; 2388c2ecf20Sopenharmony_ci int ret; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci ret = clk->provider->ops->get_parent(clk->provider->sci, clk->dev_id, 2418c2ecf20Sopenharmony_ci clk->clk_id, (void *)&parent_id); 2428c2ecf20Sopenharmony_ci if (ret) { 2438c2ecf20Sopenharmony_ci dev_err(clk->provider->dev, 2448c2ecf20Sopenharmony_ci "get-parent failed for dev=%d, clk=%d, ret=%d\n", 2458c2ecf20Sopenharmony_ci clk->dev_id, clk->clk_id, ret); 2468c2ecf20Sopenharmony_ci return 0; 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci parent_id = parent_id - clk->clk_id - 1; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci return (u8)parent_id; 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci/** 2558c2ecf20Sopenharmony_ci * sci_clk_set_parent - Set the parent of a TI SCI clock 2568c2ecf20Sopenharmony_ci * @hw: clock to set parent for 2578c2ecf20Sopenharmony_ci * @index: new parent index for the clock 2588c2ecf20Sopenharmony_ci * 2598c2ecf20Sopenharmony_ci * Sets the parent of a TI SCI clock. Return TI SCI protocol status. 2608c2ecf20Sopenharmony_ci */ 2618c2ecf20Sopenharmony_cistatic int sci_clk_set_parent(struct clk_hw *hw, u8 index) 2628c2ecf20Sopenharmony_ci{ 2638c2ecf20Sopenharmony_ci struct sci_clk *clk = to_sci_clk(hw); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci clk->cached_req = 0; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci return clk->provider->ops->set_parent(clk->provider->sci, clk->dev_id, 2688c2ecf20Sopenharmony_ci clk->clk_id, 2698c2ecf20Sopenharmony_ci index + 1 + clk->clk_id); 2708c2ecf20Sopenharmony_ci} 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_cistatic const struct clk_ops sci_clk_ops = { 2738c2ecf20Sopenharmony_ci .prepare = sci_clk_prepare, 2748c2ecf20Sopenharmony_ci .unprepare = sci_clk_unprepare, 2758c2ecf20Sopenharmony_ci .is_prepared = sci_clk_is_prepared, 2768c2ecf20Sopenharmony_ci .recalc_rate = sci_clk_recalc_rate, 2778c2ecf20Sopenharmony_ci .determine_rate = sci_clk_determine_rate, 2788c2ecf20Sopenharmony_ci .set_rate = sci_clk_set_rate, 2798c2ecf20Sopenharmony_ci .get_parent = sci_clk_get_parent, 2808c2ecf20Sopenharmony_ci .set_parent = sci_clk_set_parent, 2818c2ecf20Sopenharmony_ci}; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci/** 2848c2ecf20Sopenharmony_ci * _sci_clk_get - Gets a handle for an SCI clock 2858c2ecf20Sopenharmony_ci * @provider: Handle to SCI clock provider 2868c2ecf20Sopenharmony_ci * @sci_clk: Handle to the SCI clock to populate 2878c2ecf20Sopenharmony_ci * 2888c2ecf20Sopenharmony_ci * Gets a handle to an existing TI SCI hw clock, or builds a new clock 2898c2ecf20Sopenharmony_ci * entry and registers it with the common clock framework. Called from 2908c2ecf20Sopenharmony_ci * the common clock framework, when a corresponding of_clk_get call is 2918c2ecf20Sopenharmony_ci * executed, or recursively from itself when parsing parent clocks. 2928c2ecf20Sopenharmony_ci * Returns 0 on success, negative error code on failure. 2938c2ecf20Sopenharmony_ci */ 2948c2ecf20Sopenharmony_cistatic int _sci_clk_build(struct sci_clk_provider *provider, 2958c2ecf20Sopenharmony_ci struct sci_clk *sci_clk) 2968c2ecf20Sopenharmony_ci{ 2978c2ecf20Sopenharmony_ci struct clk_init_data init = { NULL }; 2988c2ecf20Sopenharmony_ci char *name = NULL; 2998c2ecf20Sopenharmony_ci char **parent_names = NULL; 3008c2ecf20Sopenharmony_ci int i; 3018c2ecf20Sopenharmony_ci int ret = 0; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci name = kasprintf(GFP_KERNEL, "clk:%d:%d", sci_clk->dev_id, 3048c2ecf20Sopenharmony_ci sci_clk->clk_id); 3058c2ecf20Sopenharmony_ci if (!name) 3068c2ecf20Sopenharmony_ci return -ENOMEM; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci init.name = name; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci /* 3118c2ecf20Sopenharmony_ci * From kernel point of view, we only care about a clocks parents, 3128c2ecf20Sopenharmony_ci * if it has more than 1 possible parent. In this case, it is going 3138c2ecf20Sopenharmony_ci * to have mux functionality. Otherwise it is going to act as a root 3148c2ecf20Sopenharmony_ci * clock. 3158c2ecf20Sopenharmony_ci */ 3168c2ecf20Sopenharmony_ci if (sci_clk->num_parents < 2) 3178c2ecf20Sopenharmony_ci sci_clk->num_parents = 0; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci if (sci_clk->num_parents) { 3208c2ecf20Sopenharmony_ci parent_names = kcalloc(sci_clk->num_parents, sizeof(char *), 3218c2ecf20Sopenharmony_ci GFP_KERNEL); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci if (!parent_names) { 3248c2ecf20Sopenharmony_ci ret = -ENOMEM; 3258c2ecf20Sopenharmony_ci goto err; 3268c2ecf20Sopenharmony_ci } 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci for (i = 0; i < sci_clk->num_parents; i++) { 3298c2ecf20Sopenharmony_ci char *parent_name; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci parent_name = kasprintf(GFP_KERNEL, "clk:%d:%d", 3328c2ecf20Sopenharmony_ci sci_clk->dev_id, 3338c2ecf20Sopenharmony_ci sci_clk->clk_id + 1 + i); 3348c2ecf20Sopenharmony_ci if (!parent_name) { 3358c2ecf20Sopenharmony_ci ret = -ENOMEM; 3368c2ecf20Sopenharmony_ci goto err; 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci parent_names[i] = parent_name; 3398c2ecf20Sopenharmony_ci } 3408c2ecf20Sopenharmony_ci init.parent_names = (void *)parent_names; 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci init.ops = &sci_clk_ops; 3448c2ecf20Sopenharmony_ci init.num_parents = sci_clk->num_parents; 3458c2ecf20Sopenharmony_ci sci_clk->hw.init = &init; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci ret = devm_clk_hw_register(provider->dev, &sci_clk->hw); 3488c2ecf20Sopenharmony_ci if (ret) 3498c2ecf20Sopenharmony_ci dev_err(provider->dev, "failed clk register with %d\n", ret); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_cierr: 3528c2ecf20Sopenharmony_ci if (parent_names) { 3538c2ecf20Sopenharmony_ci for (i = 0; i < sci_clk->num_parents; i++) 3548c2ecf20Sopenharmony_ci kfree(parent_names[i]); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci kfree(parent_names); 3578c2ecf20Sopenharmony_ci } 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci kfree(name); 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci return ret; 3628c2ecf20Sopenharmony_ci} 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_cistatic int _cmp_sci_clk(const void *a, const void *b) 3658c2ecf20Sopenharmony_ci{ 3668c2ecf20Sopenharmony_ci const struct sci_clk *ca = a; 3678c2ecf20Sopenharmony_ci const struct sci_clk *cb = *(struct sci_clk **)b; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci if (ca->dev_id == cb->dev_id && ca->clk_id == cb->clk_id) 3708c2ecf20Sopenharmony_ci return 0; 3718c2ecf20Sopenharmony_ci if (ca->dev_id > cb->dev_id || 3728c2ecf20Sopenharmony_ci (ca->dev_id == cb->dev_id && ca->clk_id > cb->clk_id)) 3738c2ecf20Sopenharmony_ci return 1; 3748c2ecf20Sopenharmony_ci return -1; 3758c2ecf20Sopenharmony_ci} 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci/** 3788c2ecf20Sopenharmony_ci * sci_clk_get - Xlate function for getting clock handles 3798c2ecf20Sopenharmony_ci * @clkspec: device tree clock specifier 3808c2ecf20Sopenharmony_ci * @data: pointer to the clock provider 3818c2ecf20Sopenharmony_ci * 3828c2ecf20Sopenharmony_ci * Xlate function for retrieving clock TI SCI hw clock handles based on 3838c2ecf20Sopenharmony_ci * device tree clock specifier. Called from the common clock framework, 3848c2ecf20Sopenharmony_ci * when a corresponding of_clk_get call is executed. Returns a pointer 3858c2ecf20Sopenharmony_ci * to the TI SCI hw clock struct, or ERR_PTR value in failure. 3868c2ecf20Sopenharmony_ci */ 3878c2ecf20Sopenharmony_cistatic struct clk_hw *sci_clk_get(struct of_phandle_args *clkspec, void *data) 3888c2ecf20Sopenharmony_ci{ 3898c2ecf20Sopenharmony_ci struct sci_clk_provider *provider = data; 3908c2ecf20Sopenharmony_ci struct sci_clk **clk; 3918c2ecf20Sopenharmony_ci struct sci_clk key; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci if (clkspec->args_count != 2) 3948c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci key.dev_id = clkspec->args[0]; 3978c2ecf20Sopenharmony_ci key.clk_id = clkspec->args[1]; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci clk = bsearch(&key, provider->clocks, provider->num_clocks, 4008c2ecf20Sopenharmony_ci sizeof(clk), _cmp_sci_clk); 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci if (!clk) 4038c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci return &(*clk)->hw; 4068c2ecf20Sopenharmony_ci} 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_cistatic int ti_sci_init_clocks(struct sci_clk_provider *p) 4098c2ecf20Sopenharmony_ci{ 4108c2ecf20Sopenharmony_ci int i; 4118c2ecf20Sopenharmony_ci int ret; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci for (i = 0; i < p->num_clocks; i++) { 4148c2ecf20Sopenharmony_ci ret = _sci_clk_build(p, p->clocks[i]); 4158c2ecf20Sopenharmony_ci if (ret) 4168c2ecf20Sopenharmony_ci return ret; 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci return 0; 4208c2ecf20Sopenharmony_ci} 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_cistatic const struct of_device_id ti_sci_clk_of_match[] = { 4238c2ecf20Sopenharmony_ci { .compatible = "ti,k2g-sci-clk" }, 4248c2ecf20Sopenharmony_ci { /* Sentinel */ }, 4258c2ecf20Sopenharmony_ci}; 4268c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, ti_sci_clk_of_match); 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci#ifdef CONFIG_TI_SCI_CLK_PROBE_FROM_FW 4298c2ecf20Sopenharmony_cistatic int ti_sci_scan_clocks_from_fw(struct sci_clk_provider *provider) 4308c2ecf20Sopenharmony_ci{ 4318c2ecf20Sopenharmony_ci int ret; 4328c2ecf20Sopenharmony_ci int num_clks = 0; 4338c2ecf20Sopenharmony_ci struct sci_clk **clks = NULL; 4348c2ecf20Sopenharmony_ci struct sci_clk **tmp_clks; 4358c2ecf20Sopenharmony_ci struct sci_clk *sci_clk; 4368c2ecf20Sopenharmony_ci int max_clks = 0; 4378c2ecf20Sopenharmony_ci int clk_id = 0; 4388c2ecf20Sopenharmony_ci int dev_id = 0; 4398c2ecf20Sopenharmony_ci u32 num_parents = 0; 4408c2ecf20Sopenharmony_ci int gap_size = 0; 4418c2ecf20Sopenharmony_ci struct device *dev = provider->dev; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci while (1) { 4448c2ecf20Sopenharmony_ci ret = provider->ops->get_num_parents(provider->sci, dev_id, 4458c2ecf20Sopenharmony_ci clk_id, 4468c2ecf20Sopenharmony_ci (void *)&num_parents); 4478c2ecf20Sopenharmony_ci if (ret) { 4488c2ecf20Sopenharmony_ci gap_size++; 4498c2ecf20Sopenharmony_ci if (!clk_id) { 4508c2ecf20Sopenharmony_ci if (gap_size >= 5) 4518c2ecf20Sopenharmony_ci break; 4528c2ecf20Sopenharmony_ci dev_id++; 4538c2ecf20Sopenharmony_ci } else { 4548c2ecf20Sopenharmony_ci if (gap_size >= 2) { 4558c2ecf20Sopenharmony_ci dev_id++; 4568c2ecf20Sopenharmony_ci clk_id = 0; 4578c2ecf20Sopenharmony_ci gap_size = 0; 4588c2ecf20Sopenharmony_ci } else { 4598c2ecf20Sopenharmony_ci clk_id++; 4608c2ecf20Sopenharmony_ci } 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci continue; 4638c2ecf20Sopenharmony_ci } 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci gap_size = 0; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci if (num_clks == max_clks) { 4688c2ecf20Sopenharmony_ci tmp_clks = devm_kmalloc_array(dev, max_clks + 64, 4698c2ecf20Sopenharmony_ci sizeof(sci_clk), 4708c2ecf20Sopenharmony_ci GFP_KERNEL); 4718c2ecf20Sopenharmony_ci memcpy(tmp_clks, clks, max_clks * sizeof(sci_clk)); 4728c2ecf20Sopenharmony_ci if (max_clks) 4738c2ecf20Sopenharmony_ci devm_kfree(dev, clks); 4748c2ecf20Sopenharmony_ci max_clks += 64; 4758c2ecf20Sopenharmony_ci clks = tmp_clks; 4768c2ecf20Sopenharmony_ci } 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci sci_clk = devm_kzalloc(dev, sizeof(*sci_clk), GFP_KERNEL); 4798c2ecf20Sopenharmony_ci if (!sci_clk) 4808c2ecf20Sopenharmony_ci return -ENOMEM; 4818c2ecf20Sopenharmony_ci sci_clk->dev_id = dev_id; 4828c2ecf20Sopenharmony_ci sci_clk->clk_id = clk_id; 4838c2ecf20Sopenharmony_ci sci_clk->provider = provider; 4848c2ecf20Sopenharmony_ci sci_clk->num_parents = num_parents; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci clks[num_clks] = sci_clk; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci clk_id++; 4898c2ecf20Sopenharmony_ci num_clks++; 4908c2ecf20Sopenharmony_ci } 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci provider->clocks = devm_kmalloc_array(dev, num_clks, sizeof(sci_clk), 4938c2ecf20Sopenharmony_ci GFP_KERNEL); 4948c2ecf20Sopenharmony_ci if (!provider->clocks) 4958c2ecf20Sopenharmony_ci return -ENOMEM; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci memcpy(provider->clocks, clks, num_clks * sizeof(sci_clk)); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci provider->num_clocks = num_clks; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci devm_kfree(dev, clks); 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci return 0; 5048c2ecf20Sopenharmony_ci} 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci#else 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_cistatic int _cmp_sci_clk_list(void *priv, const struct list_head *a, 5098c2ecf20Sopenharmony_ci const struct list_head *b) 5108c2ecf20Sopenharmony_ci{ 5118c2ecf20Sopenharmony_ci struct sci_clk *ca = container_of(a, struct sci_clk, node); 5128c2ecf20Sopenharmony_ci struct sci_clk *cb = container_of(b, struct sci_clk, node); 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci return _cmp_sci_clk(ca, &cb); 5158c2ecf20Sopenharmony_ci} 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_cistatic int ti_sci_scan_clocks_from_dt(struct sci_clk_provider *provider) 5188c2ecf20Sopenharmony_ci{ 5198c2ecf20Sopenharmony_ci struct device *dev = provider->dev; 5208c2ecf20Sopenharmony_ci struct device_node *np = NULL; 5218c2ecf20Sopenharmony_ci int ret; 5228c2ecf20Sopenharmony_ci int index; 5238c2ecf20Sopenharmony_ci struct of_phandle_args args; 5248c2ecf20Sopenharmony_ci struct list_head clks; 5258c2ecf20Sopenharmony_ci struct sci_clk *sci_clk, *prev; 5268c2ecf20Sopenharmony_ci int num_clks = 0; 5278c2ecf20Sopenharmony_ci int num_parents; 5288c2ecf20Sopenharmony_ci int clk_id; 5298c2ecf20Sopenharmony_ci const char * const clk_names[] = { 5308c2ecf20Sopenharmony_ci "clocks", "assigned-clocks", "assigned-clock-parents", NULL 5318c2ecf20Sopenharmony_ci }; 5328c2ecf20Sopenharmony_ci const char * const *clk_name; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&clks); 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci clk_name = clk_names; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci while (*clk_name) { 5398c2ecf20Sopenharmony_ci np = of_find_node_with_property(np, *clk_name); 5408c2ecf20Sopenharmony_ci if (!np) { 5418c2ecf20Sopenharmony_ci clk_name++; 5428c2ecf20Sopenharmony_ci continue; 5438c2ecf20Sopenharmony_ci } 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci if (!of_device_is_available(np)) 5468c2ecf20Sopenharmony_ci continue; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci index = 0; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci do { 5518c2ecf20Sopenharmony_ci ret = of_parse_phandle_with_args(np, *clk_name, 5528c2ecf20Sopenharmony_ci "#clock-cells", index, 5538c2ecf20Sopenharmony_ci &args); 5548c2ecf20Sopenharmony_ci if (ret) 5558c2ecf20Sopenharmony_ci break; 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci if (args.args_count == 2 && args.np == dev->of_node) { 5588c2ecf20Sopenharmony_ci sci_clk = devm_kzalloc(dev, sizeof(*sci_clk), 5598c2ecf20Sopenharmony_ci GFP_KERNEL); 5608c2ecf20Sopenharmony_ci if (!sci_clk) 5618c2ecf20Sopenharmony_ci return -ENOMEM; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci sci_clk->dev_id = args.args[0]; 5648c2ecf20Sopenharmony_ci sci_clk->clk_id = args.args[1]; 5658c2ecf20Sopenharmony_ci sci_clk->provider = provider; 5668c2ecf20Sopenharmony_ci provider->ops->get_num_parents(provider->sci, 5678c2ecf20Sopenharmony_ci sci_clk->dev_id, 5688c2ecf20Sopenharmony_ci sci_clk->clk_id, 5698c2ecf20Sopenharmony_ci (void *)&sci_clk->num_parents); 5708c2ecf20Sopenharmony_ci list_add_tail(&sci_clk->node, &clks); 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci num_clks++; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci num_parents = sci_clk->num_parents; 5758c2ecf20Sopenharmony_ci if (num_parents == 1) 5768c2ecf20Sopenharmony_ci num_parents = 0; 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci /* 5798c2ecf20Sopenharmony_ci * Linux kernel has inherent limitation 5808c2ecf20Sopenharmony_ci * of 255 clock parents at the moment. 5818c2ecf20Sopenharmony_ci * Right now, it is not expected that 5828c2ecf20Sopenharmony_ci * any mux clock from sci-clk driver 5838c2ecf20Sopenharmony_ci * would exceed that limit either, but 5848c2ecf20Sopenharmony_ci * the ABI basically provides that 5858c2ecf20Sopenharmony_ci * possibility. Print out a warning if 5868c2ecf20Sopenharmony_ci * this happens for any clock. 5878c2ecf20Sopenharmony_ci */ 5888c2ecf20Sopenharmony_ci if (num_parents >= 255) { 5898c2ecf20Sopenharmony_ci dev_warn(dev, "too many parents for dev=%d, clk=%d (%d), cropping to 255.\n", 5908c2ecf20Sopenharmony_ci sci_clk->dev_id, 5918c2ecf20Sopenharmony_ci sci_clk->clk_id, num_parents); 5928c2ecf20Sopenharmony_ci num_parents = 255; 5938c2ecf20Sopenharmony_ci } 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci clk_id = args.args[1] + 1; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci while (num_parents--) { 5988c2ecf20Sopenharmony_ci sci_clk = devm_kzalloc(dev, 5998c2ecf20Sopenharmony_ci sizeof(*sci_clk), 6008c2ecf20Sopenharmony_ci GFP_KERNEL); 6018c2ecf20Sopenharmony_ci if (!sci_clk) 6028c2ecf20Sopenharmony_ci return -ENOMEM; 6038c2ecf20Sopenharmony_ci sci_clk->dev_id = args.args[0]; 6048c2ecf20Sopenharmony_ci sci_clk->clk_id = clk_id++; 6058c2ecf20Sopenharmony_ci sci_clk->provider = provider; 6068c2ecf20Sopenharmony_ci list_add_tail(&sci_clk->node, &clks); 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci num_clks++; 6098c2ecf20Sopenharmony_ci } 6108c2ecf20Sopenharmony_ci } 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci index++; 6138c2ecf20Sopenharmony_ci } while (args.np); 6148c2ecf20Sopenharmony_ci } 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci list_sort(NULL, &clks, _cmp_sci_clk_list); 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci provider->clocks = devm_kmalloc_array(dev, num_clks, sizeof(sci_clk), 6198c2ecf20Sopenharmony_ci GFP_KERNEL); 6208c2ecf20Sopenharmony_ci if (!provider->clocks) 6218c2ecf20Sopenharmony_ci return -ENOMEM; 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci num_clks = 0; 6248c2ecf20Sopenharmony_ci prev = NULL; 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci list_for_each_entry(sci_clk, &clks, node) { 6278c2ecf20Sopenharmony_ci if (prev && prev->dev_id == sci_clk->dev_id && 6288c2ecf20Sopenharmony_ci prev->clk_id == sci_clk->clk_id) 6298c2ecf20Sopenharmony_ci continue; 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci provider->clocks[num_clks++] = sci_clk; 6328c2ecf20Sopenharmony_ci prev = sci_clk; 6338c2ecf20Sopenharmony_ci } 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci provider->num_clocks = num_clks; 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci return 0; 6388c2ecf20Sopenharmony_ci} 6398c2ecf20Sopenharmony_ci#endif 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci/** 6428c2ecf20Sopenharmony_ci * ti_sci_clk_probe - Probe function for the TI SCI clock driver 6438c2ecf20Sopenharmony_ci * @pdev: platform device pointer to be probed 6448c2ecf20Sopenharmony_ci * 6458c2ecf20Sopenharmony_ci * Probes the TI SCI clock device. Allocates a new clock provider 6468c2ecf20Sopenharmony_ci * and registers this to the common clock framework. Also applies 6478c2ecf20Sopenharmony_ci * any required flags to the identified clocks via clock lists 6488c2ecf20Sopenharmony_ci * supplied from DT. Returns 0 for success, negative error value 6498c2ecf20Sopenharmony_ci * for failure. 6508c2ecf20Sopenharmony_ci */ 6518c2ecf20Sopenharmony_cistatic int ti_sci_clk_probe(struct platform_device *pdev) 6528c2ecf20Sopenharmony_ci{ 6538c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 6548c2ecf20Sopenharmony_ci struct device_node *np = dev->of_node; 6558c2ecf20Sopenharmony_ci struct sci_clk_provider *provider; 6568c2ecf20Sopenharmony_ci const struct ti_sci_handle *handle; 6578c2ecf20Sopenharmony_ci int ret; 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci handle = devm_ti_sci_get_handle(dev); 6608c2ecf20Sopenharmony_ci if (IS_ERR(handle)) 6618c2ecf20Sopenharmony_ci return PTR_ERR(handle); 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci provider = devm_kzalloc(dev, sizeof(*provider), GFP_KERNEL); 6648c2ecf20Sopenharmony_ci if (!provider) 6658c2ecf20Sopenharmony_ci return -ENOMEM; 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci provider->sci = handle; 6688c2ecf20Sopenharmony_ci provider->ops = &handle->ops.clk_ops; 6698c2ecf20Sopenharmony_ci provider->dev = dev; 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci#ifdef CONFIG_TI_SCI_CLK_PROBE_FROM_FW 6728c2ecf20Sopenharmony_ci ret = ti_sci_scan_clocks_from_fw(provider); 6738c2ecf20Sopenharmony_ci if (ret) { 6748c2ecf20Sopenharmony_ci dev_err(dev, "scan clocks from FW failed: %d\n", ret); 6758c2ecf20Sopenharmony_ci return ret; 6768c2ecf20Sopenharmony_ci } 6778c2ecf20Sopenharmony_ci#else 6788c2ecf20Sopenharmony_ci ret = ti_sci_scan_clocks_from_dt(provider); 6798c2ecf20Sopenharmony_ci if (ret) { 6808c2ecf20Sopenharmony_ci dev_err(dev, "scan clocks from DT failed: %d\n", ret); 6818c2ecf20Sopenharmony_ci return ret; 6828c2ecf20Sopenharmony_ci } 6838c2ecf20Sopenharmony_ci#endif 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci ret = ti_sci_init_clocks(provider); 6868c2ecf20Sopenharmony_ci if (ret) { 6878c2ecf20Sopenharmony_ci pr_err("ti-sci-init-clocks failed.\n"); 6888c2ecf20Sopenharmony_ci return ret; 6898c2ecf20Sopenharmony_ci } 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci return of_clk_add_hw_provider(np, sci_clk_get, provider); 6928c2ecf20Sopenharmony_ci} 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci/** 6958c2ecf20Sopenharmony_ci * ti_sci_clk_remove - Remove TI SCI clock device 6968c2ecf20Sopenharmony_ci * @pdev: platform device pointer for the device to be removed 6978c2ecf20Sopenharmony_ci * 6988c2ecf20Sopenharmony_ci * Removes the TI SCI device. Unregisters the clock provider registered 6998c2ecf20Sopenharmony_ci * via common clock framework. Any memory allocated for the device will 7008c2ecf20Sopenharmony_ci * be free'd silently via the devm framework. Returns 0 always. 7018c2ecf20Sopenharmony_ci */ 7028c2ecf20Sopenharmony_cistatic int ti_sci_clk_remove(struct platform_device *pdev) 7038c2ecf20Sopenharmony_ci{ 7048c2ecf20Sopenharmony_ci of_clk_del_provider(pdev->dev.of_node); 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci return 0; 7078c2ecf20Sopenharmony_ci} 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_cistatic struct platform_driver ti_sci_clk_driver = { 7108c2ecf20Sopenharmony_ci .probe = ti_sci_clk_probe, 7118c2ecf20Sopenharmony_ci .remove = ti_sci_clk_remove, 7128c2ecf20Sopenharmony_ci .driver = { 7138c2ecf20Sopenharmony_ci .name = "ti-sci-clk", 7148c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(ti_sci_clk_of_match), 7158c2ecf20Sopenharmony_ci }, 7168c2ecf20Sopenharmony_ci}; 7178c2ecf20Sopenharmony_cimodule_platform_driver(ti_sci_clk_driver); 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 7208c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("TI System Control Interface(SCI) Clock driver"); 7218c2ecf20Sopenharmony_ciMODULE_AUTHOR("Tero Kristo"); 7228c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:ti-sci-clk"); 723