Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: (GPL-2.0 OR MIT)
0002 /*
0003  * Copyright (c) 2019 BayLibre, SAS.
0004  * Author: Neil Armstrong <narmstrong@baylibre.com>
0005  */
0006 
0007 #include <linux/clk-provider.h>
0008 #include <linux/module.h>
0009 
0010 #include "clk-regmap.h"
0011 #include "clk-cpu-dyndiv.h"
0012 
0013 static inline struct meson_clk_cpu_dyndiv_data *
0014 meson_clk_cpu_dyndiv_data(struct clk_regmap *clk)
0015 {
0016     return (struct meson_clk_cpu_dyndiv_data *)clk->data;
0017 }
0018 
0019 static unsigned long meson_clk_cpu_dyndiv_recalc_rate(struct clk_hw *hw,
0020                               unsigned long prate)
0021 {
0022     struct clk_regmap *clk = to_clk_regmap(hw);
0023     struct meson_clk_cpu_dyndiv_data *data = meson_clk_cpu_dyndiv_data(clk);
0024 
0025     return divider_recalc_rate(hw, prate,
0026                    meson_parm_read(clk->map, &data->div),
0027                    NULL, 0, data->div.width);
0028 }
0029 
0030 static long meson_clk_cpu_dyndiv_round_rate(struct clk_hw *hw,
0031                         unsigned long rate,
0032                         unsigned long *prate)
0033 {
0034     struct clk_regmap *clk = to_clk_regmap(hw);
0035     struct meson_clk_cpu_dyndiv_data *data = meson_clk_cpu_dyndiv_data(clk);
0036 
0037     return divider_round_rate(hw, rate, prate, NULL, data->div.width, 0);
0038 }
0039 
0040 static int meson_clk_cpu_dyndiv_set_rate(struct clk_hw *hw, unsigned long rate,
0041                       unsigned long parent_rate)
0042 {
0043     struct clk_regmap *clk = to_clk_regmap(hw);
0044     struct meson_clk_cpu_dyndiv_data *data = meson_clk_cpu_dyndiv_data(clk);
0045     unsigned int val;
0046     int ret;
0047 
0048     ret = divider_get_val(rate, parent_rate, NULL, data->div.width, 0);
0049     if (ret < 0)
0050         return ret;
0051 
0052     val = (unsigned int)ret << data->div.shift;
0053 
0054     /* Write the SYS_CPU_DYN_ENABLE bit before changing the divider */
0055     meson_parm_write(clk->map, &data->dyn, 1);
0056 
0057     /* Update the divider while removing the SYS_CPU_DYN_ENABLE bit */
0058     return regmap_update_bits(clk->map, data->div.reg_off,
0059                   SETPMASK(data->div.width, data->div.shift) |
0060                   SETPMASK(data->dyn.width, data->dyn.shift),
0061                   val);
0062 };
0063 
0064 const struct clk_ops meson_clk_cpu_dyndiv_ops = {
0065     .recalc_rate = meson_clk_cpu_dyndiv_recalc_rate,
0066     .round_rate = meson_clk_cpu_dyndiv_round_rate,
0067     .set_rate = meson_clk_cpu_dyndiv_set_rate,
0068 };
0069 EXPORT_SYMBOL_GPL(meson_clk_cpu_dyndiv_ops);
0070 
0071 MODULE_DESCRIPTION("Amlogic CPU Dynamic Clock divider");
0072 MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
0073 MODULE_LICENSE("GPL v2");