cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

clk-phase.c (5315B)


      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
     15static inline struct meson_clk_phase_data *
     16meson_clk_phase_data(struct clk_regmap *clk)
     17{
     18	return (struct meson_clk_phase_data *)clk->data;
     19}
     20
     21static int meson_clk_degrees_from_val(unsigned int val, unsigned int width)
     22{
     23	return phase_step(width) * val;
     24}
     25
     26static 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
     37static 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
     48static 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
     60const struct clk_ops meson_clk_phase_ops = {
     61	.get_phase	= meson_clk_phase_get_phase,
     62	.set_phase	= meson_clk_phase_set_phase,
     63};
     64EXPORT_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 */
     75static inline struct meson_clk_triphase_data *
     76meson_clk_triphase_data(struct clk_regmap *clk)
     77{
     78	return (struct meson_clk_triphase_data *)clk->data;
     79}
     80
     81static int 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	return 0;
     93}
     94
     95static int meson_clk_triphase_get_phase(struct clk_hw *hw)
     96{
     97	struct clk_regmap *clk = to_clk_regmap(hw);
     98	struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk);
     99	unsigned int val;
    100
    101	/* Phase are in sync, reading phase 0 is enough */
    102	val = meson_parm_read(clk->map, &tph->ph0);
    103
    104	return meson_clk_degrees_from_val(val, tph->ph0.width);
    105}
    106
    107static int meson_clk_triphase_set_phase(struct clk_hw *hw, int degrees)
    108{
    109	struct clk_regmap *clk = to_clk_regmap(hw);
    110	struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk);
    111	unsigned int val;
    112
    113	val = meson_clk_degrees_to_val(degrees, tph->ph0.width);
    114	meson_parm_write(clk->map, &tph->ph0, val);
    115	meson_parm_write(clk->map, &tph->ph1, val);
    116	meson_parm_write(clk->map, &tph->ph2, val);
    117
    118	return 0;
    119}
    120
    121const struct clk_ops meson_clk_triphase_ops = {
    122	.init		= meson_clk_triphase_sync,
    123	.get_phase	= meson_clk_triphase_get_phase,
    124	.set_phase	= meson_clk_triphase_set_phase,
    125};
    126EXPORT_SYMBOL_GPL(meson_clk_triphase_ops);
    127
    128/*
    129 * This is a special clock for the audio controller.
    130 * This drive a bit clock inverter for which the
    131 * opposite value of the inverter bit needs to be manually
    132 * set into another bit
    133 */
    134static inline struct meson_sclk_ws_inv_data *
    135meson_sclk_ws_inv_data(struct clk_regmap *clk)
    136{
    137	return (struct meson_sclk_ws_inv_data *)clk->data;
    138}
    139
    140static int meson_sclk_ws_inv_sync(struct clk_hw *hw)
    141{
    142	struct clk_regmap *clk = to_clk_regmap(hw);
    143	struct meson_sclk_ws_inv_data *tph = meson_sclk_ws_inv_data(clk);
    144	unsigned int val;
    145
    146	/* Get phase and sync the inverted value to ws */
    147	val = meson_parm_read(clk->map, &tph->ph);
    148	meson_parm_write(clk->map, &tph->ws, val ? 0 : 1);
    149
    150	return 0;
    151}
    152
    153static int meson_sclk_ws_inv_get_phase(struct clk_hw *hw)
    154{
    155	struct clk_regmap *clk = to_clk_regmap(hw);
    156	struct meson_sclk_ws_inv_data *tph = meson_sclk_ws_inv_data(clk);
    157	unsigned int val;
    158
    159	val = meson_parm_read(clk->map, &tph->ph);
    160
    161	return meson_clk_degrees_from_val(val, tph->ph.width);
    162}
    163
    164static int meson_sclk_ws_inv_set_phase(struct clk_hw *hw, int degrees)
    165{
    166	struct clk_regmap *clk = to_clk_regmap(hw);
    167	struct meson_sclk_ws_inv_data *tph = meson_sclk_ws_inv_data(clk);
    168	unsigned int val;
    169
    170	val = meson_clk_degrees_to_val(degrees, tph->ph.width);
    171	meson_parm_write(clk->map, &tph->ph, val);
    172	meson_parm_write(clk->map, &tph->ws, val ? 0 : 1);
    173	return 0;
    174}
    175
    176const struct clk_ops meson_sclk_ws_inv_ops = {
    177	.init		= meson_sclk_ws_inv_sync,
    178	.get_phase	= meson_sclk_ws_inv_get_phase,
    179	.set_phase	= meson_sclk_ws_inv_set_phase,
    180};
    181EXPORT_SYMBOL_GPL(meson_sclk_ws_inv_ops);
    182
    183
    184MODULE_DESCRIPTION("Amlogic phase driver");
    185MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
    186MODULE_LICENSE("GPL v2");