root/drivers/clk/meson/clk-phase.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. meson_clk_phase_data
  2. meson_clk_degrees_from_val
  3. meson_clk_degrees_to_val
  4. meson_clk_phase_get_phase
  5. meson_clk_phase_set_phase
  6. meson_clk_triphase_data
  7. meson_clk_triphase_sync
  8. meson_clk_triphase_get_phase
  9. meson_clk_triphase_set_phase

   1 // SPDX-License-Identifier: (GPL-2.0 OR MIT)
   2 /*
   3  * Copyright (c) 2018 BayLibre, SAS.
   4  * Author: Jerome Brunet <jbrunet@baylibre.com>
   5  */
   6 
   7 #include <linux/clk-provider.h>
   8 #include <linux/module.h>
   9 
  10 #include "clk-regmap.h"
  11 #include "clk-phase.h"
  12 
  13 #define phase_step(_width) (360 / (1 << (_width)))
  14 
  15 static inline struct meson_clk_phase_data *
  16 meson_clk_phase_data(struct clk_regmap *clk)
  17 {
  18         return (struct meson_clk_phase_data *)clk->data;
  19 }
  20 
  21 static int meson_clk_degrees_from_val(unsigned int val, unsigned int width)
  22 {
  23         return phase_step(width) * val;
  24 }
  25 
  26 static unsigned int meson_clk_degrees_to_val(int degrees, unsigned int width)
  27 {
  28         unsigned int val = DIV_ROUND_CLOSEST(degrees, phase_step(width));
  29 
  30         /*
  31          * This last calculation is here for cases when degrees is rounded
  32          * to 360, in which case val == (1 << width).
  33          */
  34         return val % (1 << width);
  35 }
  36 
  37 static int meson_clk_phase_get_phase(struct clk_hw *hw)
  38 {
  39         struct clk_regmap *clk = to_clk_regmap(hw);
  40         struct meson_clk_phase_data *phase = meson_clk_phase_data(clk);
  41         unsigned int val;
  42 
  43         val = meson_parm_read(clk->map, &phase->ph);
  44 
  45         return meson_clk_degrees_from_val(val, phase->ph.width);
  46 }
  47 
  48 static int meson_clk_phase_set_phase(struct clk_hw *hw, int degrees)
  49 {
  50         struct clk_regmap *clk = to_clk_regmap(hw);
  51         struct meson_clk_phase_data *phase = meson_clk_phase_data(clk);
  52         unsigned int val;
  53 
  54         val = meson_clk_degrees_to_val(degrees, phase->ph.width);
  55         meson_parm_write(clk->map, &phase->ph, val);
  56 
  57         return 0;
  58 }
  59 
  60 const struct clk_ops meson_clk_phase_ops = {
  61         .get_phase      = meson_clk_phase_get_phase,
  62         .set_phase      = meson_clk_phase_set_phase,
  63 };
  64 EXPORT_SYMBOL_GPL(meson_clk_phase_ops);
  65 
  66 /*
  67  * This is a special clock for the audio controller.
  68  * The phase of mst_sclk clock output can be controlled independently
  69  * for the outside world (ph0), the tdmout (ph1) and tdmin (ph2).
  70  * Controlling these 3 phases as just one makes things simpler and
  71  * give the same clock view to all the element on the i2s bus.
  72  * If necessary, we can still control the phase in the tdm block
  73  * which makes these independent control redundant.
  74  */
  75 static inline struct meson_clk_triphase_data *
  76 meson_clk_triphase_data(struct clk_regmap *clk)
  77 {
  78         return (struct meson_clk_triphase_data *)clk->data;
  79 }
  80 
  81 static void meson_clk_triphase_sync(struct clk_hw *hw)
  82 {
  83         struct clk_regmap *clk = to_clk_regmap(hw);
  84         struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk);
  85         unsigned int val;
  86 
  87         /* Get phase 0 and sync it to phase 1 and 2 */
  88         val = meson_parm_read(clk->map, &tph->ph0);
  89         meson_parm_write(clk->map, &tph->ph1, val);
  90         meson_parm_write(clk->map, &tph->ph2, val);
  91 }
  92 
  93 static int meson_clk_triphase_get_phase(struct clk_hw *hw)
  94 {
  95         struct clk_regmap *clk = to_clk_regmap(hw);
  96         struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk);
  97         unsigned int val;
  98 
  99         /* Phase are in sync, reading phase 0 is enough */
 100         val = meson_parm_read(clk->map, &tph->ph0);
 101 
 102         return meson_clk_degrees_from_val(val, tph->ph0.width);
 103 }
 104 
 105 static int meson_clk_triphase_set_phase(struct clk_hw *hw, int degrees)
 106 {
 107         struct clk_regmap *clk = to_clk_regmap(hw);
 108         struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk);
 109         unsigned int val;
 110 
 111         val = meson_clk_degrees_to_val(degrees, tph->ph0.width);
 112         meson_parm_write(clk->map, &tph->ph0, val);
 113         meson_parm_write(clk->map, &tph->ph1, val);
 114         meson_parm_write(clk->map, &tph->ph2, val);
 115 
 116         return 0;
 117 }
 118 
 119 const struct clk_ops meson_clk_triphase_ops = {
 120         .init           = meson_clk_triphase_sync,
 121         .get_phase      = meson_clk_triphase_get_phase,
 122         .set_phase      = meson_clk_triphase_set_phase,
 123 };
 124 EXPORT_SYMBOL_GPL(meson_clk_triphase_ops);
 125 
 126 MODULE_DESCRIPTION("Amlogic phase driver");
 127 MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
 128 MODULE_LICENSE("GPL v2");

/* [<][>][^][v][top][bottom][index][help] */