|
| 1 | +// SPDX-License-Identifier: GPL-2.0 |
1 | 2 | /*
|
2 | 3 | * Copyright (c) 2013, 2018, The Linux Foundation. All rights reserved.
|
3 |
| - * |
4 |
| - * This software is licensed under the terms of the GNU General Public |
5 |
| - * License version 2, as published by the Free Software Foundation, and |
6 |
| - * may be copied, distributed, and modified under those terms. |
7 |
| - * |
8 |
| - * This program is distributed in the hope that it will be useful, |
9 |
| - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
10 |
| - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
11 |
| - * GNU General Public License for more details. |
12 | 4 | */
|
13 | 5 |
|
14 | 6 | #include <linux/kernel.h>
|
@@ -249,7 +241,7 @@ static int clk_rcg2_determine_floor_rate(struct clk_hw *hw,
|
249 | 241 | return _freq_tbl_determine_rate(hw, rcg->freq_tbl, req, FLOOR);
|
250 | 242 | }
|
251 | 243 |
|
252 |
| -static int clk_rcg2_configure(struct clk_rcg2 *rcg, const struct freq_tbl *f) |
| 244 | +static int __clk_rcg2_configure(struct clk_rcg2 *rcg, const struct freq_tbl *f) |
253 | 245 | {
|
254 | 246 | u32 cfg, mask;
|
255 | 247 | struct clk_hw *hw = &rcg->clkr.hw;
|
@@ -282,8 +274,16 @@ static int clk_rcg2_configure(struct clk_rcg2 *rcg, const struct freq_tbl *f)
|
282 | 274 | cfg |= rcg->parent_map[index].cfg << CFG_SRC_SEL_SHIFT;
|
283 | 275 | if (rcg->mnd_width && f->n && (f->m != f->n))
|
284 | 276 | cfg |= CFG_MODE_DUAL_EDGE;
|
285 |
| - ret = regmap_update_bits(rcg->clkr.regmap, |
286 |
| - rcg->cmd_rcgr + CFG_REG, mask, cfg); |
| 277 | + |
| 278 | + return regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, |
| 279 | + mask, cfg); |
| 280 | +} |
| 281 | + |
| 282 | +static int clk_rcg2_configure(struct clk_rcg2 *rcg, const struct freq_tbl *f) |
| 283 | +{ |
| 284 | + int ret; |
| 285 | + |
| 286 | + ret = __clk_rcg2_configure(rcg, f); |
287 | 287 | if (ret)
|
288 | 288 | return ret;
|
289 | 289 |
|
@@ -790,3 +790,141 @@ const struct clk_ops clk_gfx3d_ops = {
|
790 | 790 | .determine_rate = clk_gfx3d_determine_rate,
|
791 | 791 | };
|
792 | 792 | EXPORT_SYMBOL_GPL(clk_gfx3d_ops);
|
| 793 | + |
| 794 | +static int clk_rcg2_set_force_enable(struct clk_hw *hw) |
| 795 | +{ |
| 796 | + struct clk_rcg2 *rcg = to_clk_rcg2(hw); |
| 797 | + const char *name = clk_hw_get_name(hw); |
| 798 | + int ret, count; |
| 799 | + |
| 800 | + ret = regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CMD_REG, |
| 801 | + CMD_ROOT_EN, CMD_ROOT_EN); |
| 802 | + if (ret) |
| 803 | + return ret; |
| 804 | + |
| 805 | + /* wait for RCG to turn ON */ |
| 806 | + for (count = 500; count > 0; count--) { |
| 807 | + if (clk_rcg2_is_enabled(hw)) |
| 808 | + return 0; |
| 809 | + |
| 810 | + udelay(1); |
| 811 | + } |
| 812 | + |
| 813 | + pr_err("%s: RCG did not turn on\n", name); |
| 814 | + return -ETIMEDOUT; |
| 815 | +} |
| 816 | + |
| 817 | +static int clk_rcg2_clear_force_enable(struct clk_hw *hw) |
| 818 | +{ |
| 819 | + struct clk_rcg2 *rcg = to_clk_rcg2(hw); |
| 820 | + |
| 821 | + return regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CMD_REG, |
| 822 | + CMD_ROOT_EN, 0); |
| 823 | +} |
| 824 | + |
| 825 | +static int |
| 826 | +clk_rcg2_shared_force_enable_clear(struct clk_hw *hw, const struct freq_tbl *f) |
| 827 | +{ |
| 828 | + struct clk_rcg2 *rcg = to_clk_rcg2(hw); |
| 829 | + int ret; |
| 830 | + |
| 831 | + ret = clk_rcg2_set_force_enable(hw); |
| 832 | + if (ret) |
| 833 | + return ret; |
| 834 | + |
| 835 | + ret = clk_rcg2_configure(rcg, f); |
| 836 | + if (ret) |
| 837 | + return ret; |
| 838 | + |
| 839 | + return clk_rcg2_clear_force_enable(hw); |
| 840 | +} |
| 841 | + |
| 842 | +static int clk_rcg2_shared_set_rate(struct clk_hw *hw, unsigned long rate, |
| 843 | + unsigned long parent_rate) |
| 844 | +{ |
| 845 | + struct clk_rcg2 *rcg = to_clk_rcg2(hw); |
| 846 | + const struct freq_tbl *f; |
| 847 | + |
| 848 | + f = qcom_find_freq(rcg->freq_tbl, rate); |
| 849 | + if (!f) |
| 850 | + return -EINVAL; |
| 851 | + |
| 852 | + /* |
| 853 | + * In case clock is disabled, update the CFG, M, N and D registers |
| 854 | + * and don't hit the update bit of CMD register. |
| 855 | + */ |
| 856 | + if (!__clk_is_enabled(hw->clk)) |
| 857 | + return __clk_rcg2_configure(rcg, f); |
| 858 | + |
| 859 | + return clk_rcg2_shared_force_enable_clear(hw, f); |
| 860 | +} |
| 861 | + |
| 862 | +static int clk_rcg2_shared_set_rate_and_parent(struct clk_hw *hw, |
| 863 | + unsigned long rate, unsigned long parent_rate, u8 index) |
| 864 | +{ |
| 865 | + return clk_rcg2_shared_set_rate(hw, rate, parent_rate); |
| 866 | +} |
| 867 | + |
| 868 | +static int clk_rcg2_shared_enable(struct clk_hw *hw) |
| 869 | +{ |
| 870 | + struct clk_rcg2 *rcg = to_clk_rcg2(hw); |
| 871 | + int ret; |
| 872 | + |
| 873 | + /* |
| 874 | + * Set the update bit because required configuration has already |
| 875 | + * been written in clk_rcg2_shared_set_rate() |
| 876 | + */ |
| 877 | + ret = clk_rcg2_set_force_enable(hw); |
| 878 | + if (ret) |
| 879 | + return ret; |
| 880 | + |
| 881 | + ret = update_config(rcg); |
| 882 | + if (ret) |
| 883 | + return ret; |
| 884 | + |
| 885 | + return clk_rcg2_clear_force_enable(hw); |
| 886 | +} |
| 887 | + |
| 888 | +static void clk_rcg2_shared_disable(struct clk_hw *hw) |
| 889 | +{ |
| 890 | + struct clk_rcg2 *rcg = to_clk_rcg2(hw); |
| 891 | + u32 cfg; |
| 892 | + |
| 893 | + /* |
| 894 | + * Store current configuration as switching to safe source would clear |
| 895 | + * the SRC and DIV of CFG register |
| 896 | + */ |
| 897 | + regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, &cfg); |
| 898 | + |
| 899 | + /* |
| 900 | + * Park the RCG at a safe configuration - sourced off of safe source. |
| 901 | + * Force enable and disable the RCG while configuring it to safeguard |
| 902 | + * against any update signal coming from the downstream clock. |
| 903 | + * The current parent is still prepared and enabled at this point, and |
| 904 | + * the safe source is always on while application processor subsystem |
| 905 | + * is online. Therefore, the RCG can safely switch its parent. |
| 906 | + */ |
| 907 | + clk_rcg2_set_force_enable(hw); |
| 908 | + |
| 909 | + regmap_write(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, |
| 910 | + rcg->safe_src_index << CFG_SRC_SEL_SHIFT); |
| 911 | + |
| 912 | + update_config(rcg); |
| 913 | + |
| 914 | + clk_rcg2_clear_force_enable(hw); |
| 915 | + |
| 916 | + /* Write back the stored configuration corresponding to current rate */ |
| 917 | + regmap_write(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, cfg); |
| 918 | +} |
| 919 | + |
| 920 | +const struct clk_ops clk_rcg2_shared_ops = { |
| 921 | + .enable = clk_rcg2_shared_enable, |
| 922 | + .disable = clk_rcg2_shared_disable, |
| 923 | + .get_parent = clk_rcg2_get_parent, |
| 924 | + .set_parent = clk_rcg2_set_parent, |
| 925 | + .recalc_rate = clk_rcg2_recalc_rate, |
| 926 | + .determine_rate = clk_rcg2_determine_rate, |
| 927 | + .set_rate = clk_rcg2_shared_set_rate, |
| 928 | + .set_rate_and_parent = clk_rcg2_shared_set_rate_and_parent, |
| 929 | +}; |
| 930 | +EXPORT_SYMBOL_GPL(clk_rcg2_shared_ops); |
0 commit comments