Commit 889c2b7e authored by Jerome Brunet's avatar Jerome Brunet Committed by Neil Armstrong

clk: meson: rework and clean drivers dependencies

Initially, the meson clock directory only hosted 2 controllers drivers,
for meson8 and gxbb. At the time, both used the same set of clock drivers
so managing the dependencies was not a big concern.

Since this ancient time, entropy did its job, controllers with different
requirement and specific clock drivers have been added. Unfortunately, we
did not do a great job at managing the dependencies between the
controllers and the different clock drivers. Some drivers, such as
clk-phase or vid-pll-div, are compiled even if they are useless on the
target (meson8). As we are adding new controllers, we need to be able to
pick a driver w/o pulling the whole thing.

The patch aims to clean things up by:
* providing a dedicated CONFIG_ for each clock drivers
* allowing clock drivers to be compiled as a modules, if possible
* stating explicitly which drivers are required by each controller.
Signed-off-by: default avatarJerome Brunet <jbrunet@baylibre.com>
Signed-off-by: default avatarNeil Armstrong <narmstrong@baylibre.com>
Link: https://lkml.kernel.org/r/20190201125841.26785-5-jbrunet@baylibre.com
parent cb78ba76
config COMMON_CLK_AMLOGIC config COMMON_CLK_MESON_INPUT
bool tristate
depends on ARCH_MESON || COMPILE_TEST
select COMMON_CLK_REGMAP_MESON
config COMMON_CLK_AMLOGIC_AUDIO config COMMON_CLK_MESON_REGMAP
bool tristate
depends on ARCH_MESON || COMPILE_TEST select REGMAP
select COMMON_CLK_AMLOGIC
config COMMON_CLK_MESON_AO config COMMON_CLK_MESON_DUALDIV
bool tristate
depends on OF select COMMON_CLK_MESON_REGMAP
depends on ARCH_MESON || COMPILE_TEST
select COMMON_CLK_REGMAP_MESON
select RESET_CONTROLLER
config COMMON_CLK_REGMAP_MESON config COMMON_CLK_MESON_MPLL
bool tristate
select REGMAP select COMMON_CLK_MESON_REGMAP
config COMMON_CLK_MESON_PHASE
tristate
select COMMON_CLK_MESON_REGMAP
config COMMON_CLK_MESON_PLL
tristate
select COMMON_CLK_MESON_REGMAP
config COMMON_CLK_MESON_SCLK_DIV
tristate
select COMMON_CLK_MESON_REGMAP
config COMMON_CLK_MESON_VID_PLL_DIV
tristate
select COMMON_CLK_MESON_REGMAP
config COMMON_CLK_MESON_AO_CLKC
tristate
select COMMON_CLK_MESON_REGMAP
select COMMON_CLK_MESON_INPUT
select RESET_CONTROLLER
config COMMON_CLK_MESON8B config COMMON_CLK_MESON8B
bool bool
select COMMON_CLK_AMLOGIC depends on ARCH_MESON
select COMMON_CLK_MESON_REGMAP
select COMMON_CLK_MESON_MPLL
select COMMON_CLK_MESON_PLL
select MFD_SYSCON
select RESET_CONTROLLER select RESET_CONTROLLER
help help
Support for the clock controller on AmLogic S802 (Meson8), Support for the clock controller on AmLogic S802 (Meson8),
...@@ -30,8 +50,14 @@ config COMMON_CLK_MESON8B ...@@ -30,8 +50,14 @@ config COMMON_CLK_MESON8B
config COMMON_CLK_GXBB config COMMON_CLK_GXBB
bool bool
select COMMON_CLK_AMLOGIC depends on ARCH_MESON
select COMMON_CLK_MESON_AO select COMMON_CLK_MESON_INPUT
select COMMON_CLK_MESON_REGMAP
select COMMON_CLK_MESON_DUALDIV
select COMMON_CLK_MESON_VID_PLL_DIV
select COMMON_CLK_MESON_MPLL
select COMMON_CLK_MESON_PLL
select COMMON_CLK_MESON_AO_CLKC
select MFD_SYSCON select MFD_SYSCON
help help
Support for the clock controller on AmLogic S905 devices, aka gxbb. Support for the clock controller on AmLogic S905 devices, aka gxbb.
...@@ -39,8 +65,13 @@ config COMMON_CLK_GXBB ...@@ -39,8 +65,13 @@ config COMMON_CLK_GXBB
config COMMON_CLK_AXG config COMMON_CLK_AXG
bool bool
select COMMON_CLK_AMLOGIC depends on ARCH_MESON
select COMMON_CLK_MESON_AO select COMMON_CLK_MESON_INPUT
select COMMON_CLK_MESON_REGMAP
select COMMON_CLK_MESON_DUALDIV
select COMMON_CLK_MESON_MPLL
select COMMON_CLK_MESON_PLL
select COMMON_CLK_MESON_AO_CLKC
select MFD_SYSCON select MFD_SYSCON
help help
Support for the clock controller on AmLogic A113D devices, aka axg. Support for the clock controller on AmLogic A113D devices, aka axg.
...@@ -48,8 +79,11 @@ config COMMON_CLK_AXG ...@@ -48,8 +79,11 @@ config COMMON_CLK_AXG
config COMMON_CLK_AXG_AUDIO config COMMON_CLK_AXG_AUDIO
tristate "Meson AXG Audio Clock Controller Driver" tristate "Meson AXG Audio Clock Controller Driver"
depends on COMMON_CLK_AXG depends on ARCH_MESON
select COMMON_CLK_AMLOGIC_AUDIO select COMMON_CLK_MESON_INPUT
select COMMON_CLK_MESON_REGMAP
select COMMON_CLK_MESON_PHASE
select COMMON_CLK_MESON_SCLK_DIV
select REGMAP_MMIO select REGMAP_MMIO
help help
Support for the audio clock controller on AmLogic A113D devices, Support for the audio clock controller on AmLogic A113D devices,
......
# # Amlogic clock drivers
# Makefile for Meson specific clk
#
obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-pll.o clk-mpll.o clk-phase.o vid-pll-div.o obj-$(CONFIG_COMMON_CLK_MESON_AO_CLKC) += meson-aoclk.o
obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-input.o clk-dualdiv.o obj-$(CONFIG_COMMON_CLK_MESON_DUALDIV) += clk-dualdiv.o
obj-$(CONFIG_COMMON_CLK_AMLOGIC_AUDIO) += clk-triphase.o sclk-div.o obj-$(CONFIG_COMMON_CLK_MESON_INPUT) += clk-input.o
obj-$(CONFIG_COMMON_CLK_MESON_AO) += meson-aoclk.o obj-$(CONFIG_COMMON_CLK_MESON_MPLL) += clk-mpll.o
obj-$(CONFIG_COMMON_CLK_MESON_PHASE) += clk-phase.o
obj-$(CONFIG_COMMON_CLK_MESON_PLL) += clk-pll.o
obj-$(CONFIG_COMMON_CLK_MESON_REGMAP) += clk-regmap.o
obj-$(CONFIG_COMMON_CLK_MESON_SCLK_DIV) += sclk-div.o
obj-$(CONFIG_COMMON_CLK_MESON_VID_PLL_DIV) += vid-pll-div.o
# Amlogic Clock controllers
obj-$(CONFIG_COMMON_CLK_AXG) += axg.o axg-aoclk.o
obj-$(CONFIG_COMMON_CLK_AXG_AUDIO) += axg-audio.o
obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o
obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o
obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o
obj-$(CONFIG_COMMON_CLK_AXG) += axg.o axg-aoclk.o
obj-$(CONFIG_COMMON_CLK_AXG_AUDIO) += axg-audio.o
obj-$(CONFIG_COMMON_CLK_REGMAP_MESON) += clk-regmap.o
...@@ -12,10 +12,12 @@ ...@@ -12,10 +12,12 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/reset-controller.h> #include <linux/reset-controller.h>
#include <linux/mfd/syscon.h> #include <linux/mfd/syscon.h>
#include "clkc.h"
#include "meson-aoclk.h" #include "meson-aoclk.h"
#include "axg-aoclk.h" #include "axg-aoclk.h"
#include "clk-regmap.h"
#include "clk-dualdiv.h"
#define IN_PREFIX "ao-in-" #define IN_PREFIX "ao-in-"
/* /*
......
...@@ -14,8 +14,11 @@ ...@@ -14,8 +14,11 @@
#include <linux/reset.h> #include <linux/reset.h>
#include <linux/slab.h> #include <linux/slab.h>
#include "clkc-audio.h"
#include "axg-audio.h" #include "axg-audio.h"
#include "clk-input.h"
#include "clk-regmap.h"
#include "clk-phase.h"
#include "sclk-div.h"
#define AXG_MST_IN_COUNT 8 #define AXG_MST_IN_COUNT 8
#define AXG_SLV_SCLK_COUNT 10 #define AXG_SLV_SCLK_COUNT 10
......
...@@ -17,7 +17,10 @@ ...@@ -17,7 +17,10 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include "clkc.h" #include "clk-input.h"
#include "clk-regmap.h"
#include "clk-pll.h"
#include "clk-mpll.h"
#include "axg.h" #include "axg.h"
#define IN_PREFIX "ee-in-" #define IN_PREFIX "ee-in-"
......
...@@ -22,7 +22,10 @@ ...@@ -22,7 +22,10 @@
*/ */
#include <linux/clk-provider.h> #include <linux/clk-provider.h>
#include "clkc.h" #include <linux/module.h>
#include "clk-regmap.h"
#include "clk-dualdiv.h"
static inline struct meson_clk_dualdiv_data * static inline struct meson_clk_dualdiv_data *
meson_clk_dualdiv_data(struct clk_regmap *clk) meson_clk_dualdiv_data(struct clk_regmap *clk)
...@@ -128,3 +131,8 @@ const struct clk_ops meson_clk_dualdiv_ro_ops = { ...@@ -128,3 +131,8 @@ const struct clk_ops meson_clk_dualdiv_ro_ops = {
.recalc_rate = meson_clk_dualdiv_recalc_rate, .recalc_rate = meson_clk_dualdiv_recalc_rate,
}; };
EXPORT_SYMBOL_GPL(meson_clk_dualdiv_ro_ops); EXPORT_SYMBOL_GPL(meson_clk_dualdiv_ro_ops);
MODULE_DESCRIPTION("Amlogic dual divider driver");
MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
MODULE_LICENSE("GPL v2");
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 BayLibre, SAS.
* Author: Jerome Brunet <jbrunet@baylibre.com>
*/
#ifndef __MESON_CLK_DUALDIV_H
#define __MESON_CLK_DUALDIV_H
#include <linux/clk-provider.h>
#include "parm.h"
struct meson_clk_dualdiv_param {
unsigned int n1;
unsigned int n2;
unsigned int m1;
unsigned int m2;
unsigned int dual;
};
struct meson_clk_dualdiv_data {
struct parm n1;
struct parm n2;
struct parm m1;
struct parm m2;
struct parm dual;
const struct meson_clk_dualdiv_param *table;
};
extern const struct clk_ops meson_clk_dualdiv_ops;
extern const struct clk_ops meson_clk_dualdiv_ro_ops;
#endif /* __MESON_CLK_DUALDIV_H */
...@@ -7,7 +7,8 @@ ...@@ -7,7 +7,8 @@
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/clk-provider.h> #include <linux/clk-provider.h>
#include <linux/device.h> #include <linux/device.h>
#include "clkc.h" #include <linux/module.h>
#include "clk-input.h"
static const struct clk_ops meson_clk_no_ops = {}; static const struct clk_ops meson_clk_no_ops = {};
...@@ -42,3 +43,7 @@ struct clk_hw *meson_clk_hw_register_input(struct device *dev, ...@@ -42,3 +43,7 @@ struct clk_hw *meson_clk_hw_register_input(struct device *dev,
return ret ? ERR_PTR(ret) : hw; return ret ? ERR_PTR(ret) : hw;
} }
EXPORT_SYMBOL_GPL(meson_clk_hw_register_input); EXPORT_SYMBOL_GPL(meson_clk_hw_register_input);
MODULE_DESCRIPTION("Amlogic clock input helper");
MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
MODULE_LICENSE("GPL v2");
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 BayLibre, SAS.
* Author: Jerome Brunet <jbrunet@baylibre.com>
*/
#ifndef __MESON_CLK_INPUT_H
#define __MESON_CLK_INPUT_H
#include <linux/clk-provider.h>
struct device;
struct clk_hw *meson_clk_hw_register_input(struct device *dev,
const char *of_name,
const char *clk_name,
unsigned long flags);
#endif /* __MESON_CLK_INPUT_H */
...@@ -12,7 +12,11 @@ ...@@ -12,7 +12,11 @@
*/ */
#include <linux/clk-provider.h> #include <linux/clk-provider.h>
#include "clkc.h" #include <linux/module.h>
#include <linux/spinlock.h>
#include "clk-regmap.h"
#include "clk-mpll.h"
#define SDM_DEN 16384 #define SDM_DEN 16384
#define N2_MIN 4 #define N2_MIN 4
...@@ -138,9 +142,15 @@ const struct clk_ops meson_clk_mpll_ro_ops = { ...@@ -138,9 +142,15 @@ const struct clk_ops meson_clk_mpll_ro_ops = {
.recalc_rate = mpll_recalc_rate, .recalc_rate = mpll_recalc_rate,
.round_rate = mpll_round_rate, .round_rate = mpll_round_rate,
}; };
EXPORT_SYMBOL_GPL(meson_clk_mpll_ro_ops);
const struct clk_ops meson_clk_mpll_ops = { const struct clk_ops meson_clk_mpll_ops = {
.recalc_rate = mpll_recalc_rate, .recalc_rate = mpll_recalc_rate,
.round_rate = mpll_round_rate, .round_rate = mpll_round_rate,
.set_rate = mpll_set_rate, .set_rate = mpll_set_rate,
}; };
EXPORT_SYMBOL_GPL(meson_clk_mpll_ops);
MODULE_DESCRIPTION("Amlogic MPLL driver");
MODULE_AUTHOR("Michael Turquette <mturquette@baylibre.com>");
MODULE_LICENSE("GPL v2");
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 BayLibre, SAS.
* Author: Jerome Brunet <jbrunet@baylibre.com>
*/
#ifndef __MESON_CLK_MPLL_H
#define __MESON_CLK_MPLL_H
#include <linux/clk-provider.h>
#include <linux/spinlock.h>
#include "parm.h"
struct meson_clk_mpll_data {
struct parm sdm;
struct parm sdm_en;
struct parm n2;
struct parm ssen;
struct parm misc;
spinlock_t *lock;
u8 flags;
};
#define CLK_MESON_MPLL_ROUND_CLOSEST BIT(0)
extern const struct clk_ops meson_clk_mpll_ro_ops;
extern const struct clk_ops meson_clk_mpll_ops;
#endif /* __MESON_CLK_MPLL_H */
...@@ -5,7 +5,10 @@ ...@@ -5,7 +5,10 @@
*/ */
#include <linux/clk-provider.h> #include <linux/clk-provider.h>
#include "clkc.h" #include <linux/module.h>
#include "clk-regmap.h"
#include "clk-phase.h"
#define phase_step(_width) (360 / (1 << (_width))) #define phase_step(_width) (360 / (1 << (_width)))
...@@ -15,13 +18,12 @@ meson_clk_phase_data(struct clk_regmap *clk) ...@@ -15,13 +18,12 @@ meson_clk_phase_data(struct clk_regmap *clk)
return (struct meson_clk_phase_data *)clk->data; return (struct meson_clk_phase_data *)clk->data;
} }
int meson_clk_degrees_from_val(unsigned int val, unsigned int width) static int meson_clk_degrees_from_val(unsigned int val, unsigned int width)
{ {
return phase_step(width) * val; return phase_step(width) * val;
} }
EXPORT_SYMBOL_GPL(meson_clk_degrees_from_val);
unsigned int meson_clk_degrees_to_val(int degrees, unsigned int width) static unsigned int meson_clk_degrees_to_val(int degrees, unsigned int width)
{ {
unsigned int val = DIV_ROUND_CLOSEST(degrees, phase_step(width)); unsigned int val = DIV_ROUND_CLOSEST(degrees, phase_step(width));
...@@ -31,7 +33,6 @@ unsigned int meson_clk_degrees_to_val(int degrees, unsigned int width) ...@@ -31,7 +33,6 @@ unsigned int meson_clk_degrees_to_val(int degrees, unsigned int width)
*/ */
return val % (1 << width); return val % (1 << width);
} }
EXPORT_SYMBOL_GPL(meson_clk_degrees_to_val);
static int meson_clk_phase_get_phase(struct clk_hw *hw) static int meson_clk_phase_get_phase(struct clk_hw *hw)
{ {
...@@ -61,3 +62,67 @@ const struct clk_ops meson_clk_phase_ops = { ...@@ -61,3 +62,67 @@ const struct clk_ops meson_clk_phase_ops = {
.set_phase = meson_clk_phase_set_phase, .set_phase = meson_clk_phase_set_phase,
}; };
EXPORT_SYMBOL_GPL(meson_clk_phase_ops); EXPORT_SYMBOL_GPL(meson_clk_phase_ops);
/*
* This is a special clock for the audio controller.
* The phase of mst_sclk clock output can be controlled independently
* for the outside world (ph0), the tdmout (ph1) and tdmin (ph2).
* Controlling these 3 phases as just one makes things simpler and
* give the same clock view to all the element on the i2s bus.
* If necessary, we can still control the phase in the tdm block
* which makes these independent control redundant.
*/
static inline struct meson_clk_triphase_data *
meson_clk_triphase_data(struct clk_regmap *clk)
{
return (struct meson_clk_triphase_data *)clk->data;
}
static void meson_clk_triphase_sync(struct clk_hw *hw)
{
struct clk_regmap *clk = to_clk_regmap(hw);
struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk);
unsigned int val;
/* Get phase 0 and sync it to phase 1 and 2 */
val = meson_parm_read(clk->map, &tph->ph0);
meson_parm_write(clk->map, &tph->ph1, val);
meson_parm_write(clk->map, &tph->ph2, val);
}
static int meson_clk_triphase_get_phase(struct clk_hw *hw)
{
struct clk_regmap *clk = to_clk_regmap(hw);
struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk);
unsigned int val;
/* Phase are in sync, reading phase 0 is enough */
val = meson_parm_read(clk->map, &tph->ph0);
return meson_clk_degrees_from_val(val, tph->ph0.width);
}
static int meson_clk_triphase_set_phase(struct clk_hw *hw, int degrees)
{
struct clk_regmap *clk = to_clk_regmap(hw);
struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk);
unsigned int val;
val = meson_clk_degrees_to_val(degrees, tph->ph0.width);
meson_parm_write(clk->map, &tph->ph0, val);
meson_parm_write(clk->map, &tph->ph1, val);
meson_parm_write(clk->map, &tph->ph2, val);
return 0;
}
const struct clk_ops meson_clk_triphase_ops = {
.init = meson_clk_triphase_sync,
.get_phase = meson_clk_triphase_get_phase,
.set_phase = meson_clk_triphase_set_phase,
};
EXPORT_SYMBOL_GPL(meson_clk_triphase_ops);
MODULE_DESCRIPTION("Amlogic phase driver");
MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
MODULE_LICENSE("GPL v2");
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 BayLibre, SAS.
* Author: Jerome Brunet <jbrunet@baylibre.com>
*/
#ifndef __MESON_CLK_PHASE_H
#define __MESON_CLK_PHASE_H
#include <linux/clk-provider.h>
#include "parm.h"
struct meson_clk_phase_data {
struct parm ph;
};
struct meson_clk_triphase_data {
struct parm ph0;
struct parm ph1;
struct parm ph2;
};
extern const struct clk_ops meson_clk_phase_ops;
extern const struct clk_ops meson_clk_triphase_ops;
#endif /* __MESON_CLK_PHASE_H */
...@@ -32,11 +32,9 @@ ...@@ -32,11 +32,9 @@
#include <linux/io.h> #include <linux/io.h>
#include <linux/math64.h> #include <linux/math64.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of_address.h>
#include <linux/slab.h>
#include <linux/string.h>
#include "clkc.h" #include "clk-regmap.h"
#include "clk-pll.h"
static inline struct meson_clk_pll_data * static inline struct meson_clk_pll_data *
meson_clk_pll_data(struct clk_regmap *clk) meson_clk_pll_data(struct clk_regmap *clk)
...@@ -309,8 +307,15 @@ const struct clk_ops meson_clk_pll_ops = { ...@@ -309,8 +307,15 @@ const struct clk_ops meson_clk_pll_ops = {
.enable = meson_clk_pll_enable, .enable = meson_clk_pll_enable,
.disable = meson_clk_pll_disable .disable = meson_clk_pll_disable
}; };
EXPORT_SYMBOL_GPL(meson_clk_pll_ops);
const struct clk_ops meson_clk_pll_ro_ops = { const struct clk_ops meson_clk_pll_ro_ops = {
.recalc_rate = meson_clk_pll_recalc_rate, .recalc_rate = meson_clk_pll_recalc_rate,
.is_enabled = meson_clk_pll_is_enabled, .is_enabled = meson_clk_pll_is_enabled,
}; };
EXPORT_SYMBOL_GPL(meson_clk_pll_ro_ops);
MODULE_DESCRIPTION("Amlogic PLL driver");
MODULE_AUTHOR("Carlo Caione <carlo@endlessm.com>");
MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
MODULE_LICENSE("GPL v2");
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 BayLibre, SAS.
* Author: Jerome Brunet <jbrunet@baylibre.com>
*/
#ifndef __MESON_CLK_PLL_H
#define __MESON_CLK_PLL_H
#include <linux/clk-provider.h>
#include <linux/regmap.h>
#include "parm.h"
struct pll_params_table {
u16 m;
u16 n;
};
#define PLL_PARAMS(_m, _n) \
{ \
.m = (_m), \
.n = (_n), \
}
#define CLK_MESON_PLL_ROUND_CLOSEST BIT(0)
struct meson_clk_pll_data {
struct parm en;
struct parm m;
struct parm n;
struct parm frac;
struct parm l;
struct parm rst;
const struct reg_sequence *init_regs;
unsigned int init_count;
const struct pll_params_table *table;
u8 flags;
};
extern const struct clk_ops meson_clk_pll_ro_ops;
extern const struct clk_ops meson_clk_pll_ops;
#endif /* __MESON_CLK_PLL_H */
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
* Author: Jerome Brunet <jbrunet@baylibre.com> * Author: Jerome Brunet <jbrunet@baylibre.com>
*/ */
#include <linux/module.h>
#include "clk-regmap.h" #include "clk-regmap.h"
static int clk_regmap_gate_endisable(struct clk_hw *hw, int enable) static int clk_regmap_gate_endisable(struct clk_hw *hw, int enable)
...@@ -180,3 +181,7 @@ const struct clk_ops clk_regmap_mux_ro_ops = { ...@@ -180,3 +181,7 @@ const struct clk_ops clk_regmap_mux_ro_ops = {
.get_parent = clk_regmap_mux_get_parent, .get_parent = clk_regmap_mux_get_parent,
}; };
EXPORT_SYMBOL_GPL(clk_regmap_mux_ro_ops); EXPORT_SYMBOL_GPL(clk_regmap_mux_ro_ops);
MODULE_DESCRIPTION("Amlogic regmap backed clock driver");
MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
MODULE_LICENSE("GPL v2");
...@@ -111,4 +111,19 @@ clk_get_regmap_mux_data(struct clk_regmap *clk) ...@@ -111,4 +111,19 @@ clk_get_regmap_mux_data(struct clk_regmap *clk)
extern const struct clk_ops clk_regmap_mux_ops; extern const struct clk_ops clk_regmap_mux_ops;
extern const struct clk_ops clk_regmap_mux_ro_ops; extern const struct clk_ops clk_regmap_mux_ro_ops;
#define MESON_GATE(_name, _reg, _bit) \
struct clk_regmap _name = { \
.data = &(struct clk_regmap_gate_data){ \
.offset = (_reg), \
.bit_idx = (_bit), \
}, \
.hw.init = &(struct clk_init_data) { \
.name = #_name, \
.ops = &clk_regmap_gate_ops, \
.parent_names = (const char *[]){ "clk81" }, \
.num_parents = 1, \
.flags = (CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED), \
}, \
}
#endif /* __CLK_REGMAP_H */ #endif /* __CLK_REGMAP_H */
// SPDX-License-Identifier: (GPL-2.0 OR MIT)
/*
* Copyright (c) 2018 BayLibre, SAS.
* Author: Jerome Brunet <jbrunet@baylibre.com>
*/
#include <linux/clk-provider.h>
#include "clkc-audio.h"
/*
* This is a special clock for the audio controller.
* The phase of mst_sclk clock output can be controlled independently
* for the outside world (ph0), the tdmout (ph1) and tdmin (ph2).
* Controlling these 3 phases as just one makes things simpler and
* give the same clock view to all the element on the i2s bus.
* If necessary, we can still control the phase in the tdm block
* which makes these independent control redundant.
*/
static inline struct meson_clk_triphase_data *
meson_clk_triphase_data(struct clk_regmap *clk)
{
return (struct meson_clk_triphase_data *)clk->data;
}
static void meson_clk_triphase_sync(struct clk_hw *hw)
{
struct clk_regmap *clk = to_clk_regmap(hw);
struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk);
unsigned int val;
/* Get phase 0 and sync it to phase 1 and 2 */
val = meson_parm_read(clk->map, &tph->ph0);
meson_parm_write(clk->map, &tph->ph1, val);
meson_parm_write(clk->map, &tph->ph2, val);
}
static int meson_clk_triphase_get_phase(struct clk_hw *hw)
{
struct clk_regmap *clk = to_clk_regmap(hw);
struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk);
unsigned int val;
/* Phase are in sync, reading phase 0 is enough */
val = meson_parm_read(clk->map, &tph->ph0);
return meson_clk_degrees_from_val(val, tph->ph0.width);
}
static int meson_clk_triphase_set_phase(struct clk_hw *hw, int degrees)
{
struct clk_regmap *clk = to_clk_regmap(hw);
struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk);
unsigned int val;
val = meson_clk_degrees_to_val(degrees, tph->ph0.width);
meson_parm_write(clk->map, &tph->ph0, val);
meson_parm_write(clk->map, &tph->ph1, val);
meson_parm_write(clk->map, &tph->ph2, val);
return 0;
}
const struct clk_ops meson_clk_triphase_ops = {
.init = meson_clk_triphase_sync,
.get_phase = meson_clk_triphase_get_phase,
.set_phase = meson_clk_triphase_set_phase,
};
EXPORT_SYMBOL_GPL(meson_clk_triphase_ops);
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2015 Endless Mobile, Inc.
* Author: Carlo Caione <carlo@endlessm.com>
*/
#ifndef __CLKC_H
#define __CLKC_H
#include <linux/clk-provider.h>
#include "clk-regmap.h"
#define PMASK(width) GENMASK(width - 1, 0)
#define SETPMASK(width, shift) GENMASK(shift + width - 1, shift)
#define CLRPMASK(width, shift) (~SETPMASK(width, shift))
#define PARM_GET(width, shift, reg) \
(((reg) & SETPMASK(width, shift)) >> (shift))
#define PARM_SET(width, shift, reg, val) \
(((reg) & CLRPMASK(width, shift)) | ((val) << (shift)))
#define MESON_PARM_APPLICABLE(p) (!!((p)->width))
struct parm {
u16 reg_off;
u8 shift;
u8 width;
};
static inline unsigned int meson_parm_read(struct regmap *map, struct parm *p)
{
unsigned int val;
regmap_read(map, p->reg_off, &val);
return PARM_GET(p->width, p->shift, val);
}
static inline void meson_parm_write(struct regmap *map, struct parm *p,
unsigned int val)
{
regmap_update_bits(map, p->reg_off, SETPMASK(p->width, p->shift),
val << p->shift);
}
struct pll_params_table {
u16 m;
u16 n;
};
#define PLL_PARAMS(_m, _n) \
{ \
.m = (_m), \
.n = (_n), \
}
#define CLK_MESON_PLL_ROUND_CLOSEST BIT(0)
struct meson_clk_pll_data {
struct parm en;
struct parm m;
struct parm n;
struct parm frac;
struct parm l;
struct parm rst;
const struct reg_sequence *init_regs;
unsigned int init_count;
const struct pll_params_table *table;
u8 flags;
};
#define to_meson_clk_pll(_hw) container_of(_hw, struct meson_clk_pll, hw)
struct meson_clk_mpll_data {
struct parm sdm;
struct parm sdm_en;
struct parm n2;
struct parm ssen;
struct parm misc;
spinlock_t *lock;
u8 flags;
};
#define CLK_MESON_MPLL_ROUND_CLOSEST BIT(0)
struct meson_clk_phase_data {
struct parm ph;
};
int meson_clk_degrees_from_val(unsigned int val, unsigned int width);
unsigned int meson_clk_degrees_to_val(int degrees, unsigned int width);
struct meson_vid_pll_div_data {
struct parm val;
struct parm sel;
};
#define MESON_GATE(_name, _reg, _bit) \
struct clk_regmap _name = { \
.data = &(struct clk_regmap_gate_data){ \
.offset = (_reg), \
.bit_idx = (_bit), \
}, \
.hw.init = &(struct clk_init_data) { \
.name = #_name, \
.ops = &clk_regmap_gate_ops, \
.parent_names = (const char *[]){ "clk81" }, \
.num_parents = 1, \
.flags = (CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED), \
}, \
};
struct meson_clk_dualdiv_param {
unsigned int n1;
unsigned int n2;
unsigned int m1;
unsigned int m2;
unsigned int dual;
};
struct meson_clk_dualdiv_data {
struct parm n1;
struct parm n2;
struct parm m1;
struct parm m2;
struct parm dual;
const struct meson_clk_dualdiv_param *table;
};
/* clk_ops */
extern const struct clk_ops meson_clk_pll_ro_ops;
extern const struct clk_ops meson_clk_pll_ops;
extern const struct clk_ops meson_clk_cpu_ops;
extern const struct clk_ops meson_clk_mpll_ro_ops;
extern const struct clk_ops meson_clk_mpll_ops;
extern const struct clk_ops meson_clk_phase_ops;
extern const struct clk_ops meson_vid_pll_div_ro_ops;
extern const struct clk_ops meson_clk_dualdiv_ops;
extern const struct clk_ops meson_clk_dualdiv_ro_ops;
struct clk_hw *meson_clk_hw_register_input(struct device *dev,
const char *of_name,
const char *clk_name,
unsigned long flags);
#endif /* __CLKC_H */
...@@ -5,10 +5,12 @@ ...@@ -5,10 +5,12 @@
*/ */
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/mfd/syscon.h> #include <linux/mfd/syscon.h>
#include "clkc.h"
#include "meson-aoclk.h" #include "meson-aoclk.h"
#include "gxbb-aoclk.h" #include "gxbb-aoclk.h"
#include "clk-regmap.h"
#include "clk-dualdiv.h"
#define IN_PREFIX "ao-in-" #define IN_PREFIX "ao-in-"
/* AO Configuration Clock registers offsets */ /* AO Configuration Clock registers offsets */
......
...@@ -12,9 +12,12 @@ ...@@ -12,9 +12,12 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include "clkc.h"
#include "gxbb.h" #include "gxbb.h"
#include "clk-input.h"
#include "clk-regmap.h" #include "clk-regmap.h"
#include "clk-pll.h"
#include "clk-mpll.h"
#include "vid-pll-div.h"
#define IN_PREFIX "ee-in-" #define IN_PREFIX "ee-in-"
......
...@@ -17,6 +17,8 @@ ...@@ -17,6 +17,8 @@
#include <linux/slab.h> #include <linux/slab.h>
#include "meson-aoclk.h" #include "meson-aoclk.h"
#include "clk-input.h"
static int meson_aoclk_do_reset(struct reset_controller_dev *rcdev, static int meson_aoclk_do_reset(struct reset_controller_dev *rcdev,
unsigned long id) unsigned long id)
{ {
......
...@@ -11,9 +11,12 @@ ...@@ -11,9 +11,12 @@
#ifndef __MESON_AOCLK_H__ #ifndef __MESON_AOCLK_H__
#define __MESON_AOCLK_H__ #define __MESON_AOCLK_H__
#include <linux/clk-provider.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/reset-controller.h> #include <linux/reset-controller.h>
#include "clkc.h"
#include "clk-regmap.h"
struct meson_aoclk_input { struct meson_aoclk_input {
const char *name; const char *name;
......
...@@ -16,9 +16,10 @@ ...@@ -16,9 +16,10 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include "clkc.h"
#include "meson8b.h" #include "meson8b.h"
#include "clk-regmap.h" #include "clk-regmap.h"
#include "clk-pll.h"
#include "clk-mpll.h"
static DEFINE_SPINLOCK(meson_clk_lock); static DEFINE_SPINLOCK(meson_clk_lock);
......
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2015 Endless Mobile, Inc.
* Author: Carlo Caione <carlo@endlessm.com>
*/
#ifndef __MESON_PARM_H
#define __MESON_PARM_H
#include <linux/bits.h>
#include <linux/regmap.h>
#define PMASK(width) GENMASK(width - 1, 0)
#define SETPMASK(width, shift) GENMASK(shift + width - 1, shift)
#define CLRPMASK(width, shift) (~SETPMASK(width, shift))
#define PARM_GET(width, shift, reg) \
(((reg) & SETPMASK(width, shift)) >> (shift))
#define PARM_SET(width, shift, reg, val) \
(((reg) & CLRPMASK(width, shift)) | ((val) << (shift)))
#define MESON_PARM_APPLICABLE(p) (!!((p)->width))
struct parm {
u16 reg_off;
u8 shift;
u8 width;
};
static inline unsigned int meson_parm_read(struct regmap *map, struct parm *p)
{
unsigned int val;
regmap_read(map, p->reg_off, &val);
return PARM_GET(p->width, p->shift, val);
}
static inline void meson_parm_write(struct regmap *map, struct parm *p,
unsigned int val)
{
regmap_update_bits(map, p->reg_off, SETPMASK(p->width, p->shift),
val << p->shift);
}
#endif /* __MESON_PARM_H */
...@@ -16,7 +16,11 @@ ...@@ -16,7 +16,11 @@
* duty_cycle = (1 + hi) / (1 + val) * duty_cycle = (1 + hi) / (1 + val)
*/ */
#include "clkc-audio.h" #include <linux/clk-provider.h>
#include <linux/module.h>
#include "clk-regmap.h"
#include "sclk-div.h"
static inline struct meson_sclk_div_data * static inline struct meson_sclk_div_data *
meson_sclk_div_data(struct clk_regmap *clk) meson_sclk_div_data(struct clk_regmap *clk)
...@@ -241,3 +245,7 @@ const struct clk_ops meson_sclk_div_ops = { ...@@ -241,3 +245,7 @@ const struct clk_ops meson_sclk_div_ops = {
.init = sclk_div_init, .init = sclk_div_init,
}; };
EXPORT_SYMBOL_GPL(meson_sclk_div_ops); EXPORT_SYMBOL_GPL(meson_sclk_div_ops);
MODULE_DESCRIPTION("Amlogic Sample divider driver");
MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
MODULE_LICENSE("GPL v2");
...@@ -4,16 +4,11 @@ ...@@ -4,16 +4,11 @@
* Author: Jerome Brunet <jbrunet@baylibre.com> * Author: Jerome Brunet <jbrunet@baylibre.com>
*/ */
#ifndef __MESON_CLKC_AUDIO_H #ifndef __MESON_SCLK_DIV_H
#define __MESON_CLKC_AUDIO_H #define __MESON_SCLK_DIV_H
#include "clkc.h" #include <linux/clk-provider.h>
#include "parm.h"
struct meson_clk_triphase_data {
struct parm ph0;
struct parm ph1;
struct parm ph2;
};
struct meson_sclk_div_data { struct meson_sclk_div_data {
struct parm div; struct parm div;
...@@ -22,7 +17,6 @@ struct meson_sclk_div_data { ...@@ -22,7 +17,6 @@ struct meson_sclk_div_data {
struct clk_duty cached_duty; struct clk_duty cached_duty;
}; };
extern const struct clk_ops meson_clk_triphase_ops;
extern const struct clk_ops meson_sclk_div_ops; extern const struct clk_ops meson_sclk_div_ops;
#endif /* __MESON_CLKC_AUDIO_H */ #endif /* __MESON_SCLK_DIV_H */
...@@ -5,7 +5,10 @@ ...@@ -5,7 +5,10 @@
*/ */
#include <linux/clk-provider.h> #include <linux/clk-provider.h>
#include "clkc.h" #include <linux/module.h>
#include "clk-regmap.h"
#include "vid-pll-div.h"
static inline struct meson_vid_pll_div_data * static inline struct meson_vid_pll_div_data *
meson_vid_pll_div_data(struct clk_regmap *clk) meson_vid_pll_div_data(struct clk_regmap *clk)
...@@ -89,3 +92,8 @@ static unsigned long meson_vid_pll_div_recalc_rate(struct clk_hw *hw, ...@@ -89,3 +92,8 @@ static unsigned long meson_vid_pll_div_recalc_rate(struct clk_hw *hw,
const struct clk_ops meson_vid_pll_div_ro_ops = { const struct clk_ops meson_vid_pll_div_ro_ops = {
.recalc_rate = meson_vid_pll_div_recalc_rate, .recalc_rate = meson_vid_pll_div_recalc_rate,
}; };
EXPORT_SYMBOL_GPL(meson_vid_pll_div_ro_ops);
MODULE_DESCRIPTION("Amlogic video pll divider driver");
MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
MODULE_LICENSE("GPL v2");
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 BayLibre, SAS.
* Author: Jerome Brunet <jbrunet@baylibre.com>
*/
#ifndef __MESON_VID_PLL_DIV_H
#define __MESON_VID_PLL_DIV_H
#include <linux/clk-provider.h>
#include "parm.h"
struct meson_vid_pll_div_data {
struct parm val;
struct parm sel;
};
extern const struct clk_ops meson_vid_pll_div_ro_ops;
#endif /* __MESON_VID_PLL_DIV_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment