clk: meson: g12a: add notifiers to handle cpu clock change

In order to implement clock switching for the CLKID_CPU_CLK and
CLKID_CPUB_CLK, notifiers are added on specific points of the
clock tree :

cpu_clk / cpub_clk
|   \- cpu_clk_dyn
|      |  \- cpu_clk_premux0
|      |        |- cpu_clk_postmux0
|      |        |    |- cpu_clk_dyn0_div
|      |        |    \- xtal/fclk_div2/fclk_div3
|      |        \- xtal/fclk_div2/fclk_div3
|      \- cpu_clk_premux1
|            |- cpu_clk_postmux1
|            |    |- cpu_clk_dyn1_div
|            |    \- xtal/fclk_div2/fclk_div3
|            \- xtal/fclk_div2/fclk_div3
\ sys_pll / sys1_pll

This for each cluster, a single one for G12A, two for G12B.

Each cpu_clk_premux1 tree is marked as read-only and CLK_SET_RATE_NO_REPARENT,
to be used as "parking" clock in a safe clock frequency.

A notifier is added on each cpu_clk_premux0 to detech when CCF want to
change the frequency of the cpu_clk_dyn tree.
In this notifier, the cpu_clk_premux1 tree is configured to use the xtal
clock and then the cpu_clk_dyn is switch to cpu_clk_premux1 while CCF
updates the cpu_clk_premux0 tree.

A notifier is added on each sys_pll/sys1_pll to detect when CCF wants to
change the PLL clock source of the cpu_clk.
In this notifier, the cpu_clk is switched to cpu_clk_dyn while CCF
updates the sys_pll/sys1_pll frequency.

A third small notifier is added on each cpu_clk / cpub_clk and cpu_clk_dyn,
add a small delay at PRE_RATE_CHANGE/POST_RATE_CHANGE to let the other
notofiers change propagate before changing the cpu_clk_premux0 and sys_pll
clock trees.

This notifier set permits switching the cpu_clk / cpub_clk without any
glitches and using a safe parking clock while switching between sub-GHz
clocks using the cpu_clk_dyn tree.

This setup has been tested and validated on the Amlogic G12A and G12B
SoCs running the arm64 cpuburn at [1] and cycling between all the possible
cpufreq translations of each cluster and checking the final frequency using
the clock-measurer, script at [2].

[1] https://github.com/ssvb/cpuburn-arm/blob/master/cpuburn-a53.S
[2] https://gist.github.com/superna9999/d4de964dbc0f84b7d527e1df2ddea25f

Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
This commit is contained in:
Neil Armstrong 2019-07-31 10:40:18 +02:00 committed by Jerome Brunet
parent 26d34431ad
commit ffae8475b9
1 changed files with 479 additions and 52 deletions

View File

@ -14,10 +14,12 @@
#include <linux/init.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include "clk-mpll.h"
#include "clk-pll.h"
#include "clk-regmap.h"
#include "clk-cpu-dyndiv.h"
#include "vid-pll-div.h"
#include "meson-eeclk.h"
#include "g12a.h"
@ -88,16 +90,9 @@ static struct clk_regmap g12a_fixed_pll = {
},
};
/*
* Internal sys pll emulation configuration parameters
*/
static const struct reg_sequence g12a_sys_init_regs[] = {
{ .reg = HHI_SYS_PLL_CNTL1, .def = 0x00000000 },
{ .reg = HHI_SYS_PLL_CNTL2, .def = 0x00000000 },
{ .reg = HHI_SYS_PLL_CNTL3, .def = 0x48681c00 },
{ .reg = HHI_SYS_PLL_CNTL4, .def = 0x88770290 },
{ .reg = HHI_SYS_PLL_CNTL5, .def = 0x39272000 },
{ .reg = HHI_SYS_PLL_CNTL6, .def = 0x56540000 },
static const struct pll_mult_range g12a_sys_pll_mult_range = {
.min = 128,
.max = 250,
};
static struct clk_regmap g12a_sys_pll_dco = {
@ -127,16 +122,17 @@ static struct clk_regmap g12a_sys_pll_dco = {
.shift = 29,
.width = 1,
},
.init_regs = g12a_sys_init_regs,
.init_count = ARRAY_SIZE(g12a_sys_init_regs),
.range = &g12a_sys_pll_mult_range,
},
.hw.init = &(struct clk_init_data){
.name = "sys_pll_dco",
.ops = &meson_clk_pll_ro_ops,
.ops = &meson_clk_pll_ops,
.parent_data = &(const struct clk_parent_data) {
.fw_name = "xtal",
},
.num_parents = 1,
/* This clock feeds the CPU, avoid disabling it */
.flags = CLK_IS_CRITICAL,
},
};
@ -149,11 +145,12 @@ static struct clk_regmap g12a_sys_pll = {
},
.hw.init = &(struct clk_init_data){
.name = "sys_pll",
.ops = &clk_regmap_divider_ro_ops,
.ops = &clk_regmap_divider_ops,
.parent_hws = (const struct clk_hw *[]) {
&g12a_sys_pll_dco.hw
},
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
},
};
@ -184,14 +181,17 @@ static struct clk_regmap g12b_sys1_pll_dco = {
.shift = 29,
.width = 1,
},
.range = &g12a_sys_pll_mult_range,
},
.hw.init = &(struct clk_init_data){
.name = "sys1_pll_dco",
.ops = &meson_clk_pll_ro_ops,
.ops = &meson_clk_pll_ops,
.parent_data = &(const struct clk_parent_data) {
.fw_name = "xtal",
},
.num_parents = 1,
/* This clock feeds the CPU, avoid disabling it */
.flags = CLK_IS_CRITICAL,
},
};
@ -204,11 +204,12 @@ static struct clk_regmap g12b_sys1_pll = {
},
.hw.init = &(struct clk_init_data){
.name = "sys1_pll",
.ops = &clk_regmap_divider_ro_ops,
.ops = &clk_regmap_divider_ops,
.parent_hws = (const struct clk_hw *[]) {
&g12b_sys1_pll_dco.hw
},
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
},
};
@ -345,13 +346,15 @@ static struct clk_regmap g12a_cpu_clk_premux0 = {
},
.hw.init = &(struct clk_init_data){
.name = "cpu_clk_dyn0_sel",
.ops = &clk_regmap_mux_ro_ops,
.ops = &clk_regmap_mux_ops,
.parent_data = (const struct clk_parent_data []) {
{ .fw_name = "xtal", },
{ .hw = &g12a_fclk_div2.hw },
{ .hw = &g12a_fclk_div3.hw },
},
.num_parents = 3,
/* This sub-tree is used a parking clock */
.flags = CLK_SET_RATE_NO_REPARENT,
},
};
@ -364,30 +367,40 @@ static struct clk_regmap g12a_cpu_clk_premux1 = {
},
.hw.init = &(struct clk_init_data){
.name = "cpu_clk_dyn1_sel",
.ops = &clk_regmap_mux_ro_ops,
.ops = &clk_regmap_mux_ops,
.parent_data = (const struct clk_parent_data []) {
{ .fw_name = "xtal", },
{ .hw = &g12a_fclk_div2.hw },
{ .hw = &g12a_fclk_div3.hw },
},
.num_parents = 3,
/* This sub-tree is used a parking clock */
.flags = CLK_SET_RATE_NO_REPARENT
},
};
/* Datasheet names this field as "mux0_divn_tcnt" */
static struct clk_regmap g12a_cpu_clk_mux0_div = {
.data = &(struct clk_regmap_div_data){
.offset = HHI_SYS_CPU_CLK_CNTL0,
.shift = 4,
.width = 6,
.data = &(struct meson_clk_cpu_dyndiv_data){
.div = {
.reg_off = HHI_SYS_CPU_CLK_CNTL0,
.shift = 4,
.width = 6,
},
.dyn = {
.reg_off = HHI_SYS_CPU_CLK_CNTL0,
.shift = 26,
.width = 1,
},
},
.hw.init = &(struct clk_init_data){
.name = "cpu_clk_dyn0_div",
.ops = &clk_regmap_divider_ro_ops,
.ops = &meson_clk_cpu_dyndiv_ops,
.parent_hws = (const struct clk_hw *[]) {
&g12a_cpu_clk_premux0.hw
},
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
},
};
@ -400,12 +413,13 @@ static struct clk_regmap g12a_cpu_clk_postmux0 = {
},
.hw.init = &(struct clk_init_data){
.name = "cpu_clk_dyn0",
.ops = &clk_regmap_mux_ro_ops,
.ops = &clk_regmap_mux_ops,
.parent_hws = (const struct clk_hw *[]) {
&g12a_cpu_clk_premux0.hw,
&g12a_cpu_clk_mux0_div.hw,
},
.num_parents = 2,
.flags = CLK_SET_RATE_PARENT,
},
};
@ -435,12 +449,14 @@ static struct clk_regmap g12a_cpu_clk_postmux1 = {
},
.hw.init = &(struct clk_init_data){
.name = "cpu_clk_dyn1",
.ops = &clk_regmap_mux_ro_ops,
.ops = &clk_regmap_mux_ops,
.parent_hws = (const struct clk_hw *[]) {
&g12a_cpu_clk_premux1.hw,
&g12a_cpu_clk_mux1_div.hw,
},
.num_parents = 2,
/* This sub-tree is used a parking clock */
.flags = CLK_SET_RATE_NO_REPARENT,
},
};
@ -453,12 +469,13 @@ static struct clk_regmap g12a_cpu_clk_dyn = {
},
.hw.init = &(struct clk_init_data){
.name = "cpu_clk_dyn",
.ops = &clk_regmap_mux_ro_ops,
.ops = &clk_regmap_mux_ops,
.parent_hws = (const struct clk_hw *[]) {
&g12a_cpu_clk_postmux0.hw,
&g12a_cpu_clk_postmux1.hw,
},
.num_parents = 2,
.flags = CLK_SET_RATE_PARENT,
},
};
@ -471,12 +488,13 @@ static struct clk_regmap g12a_cpu_clk = {
},
.hw.init = &(struct clk_init_data){
.name = "cpu_clk",
.ops = &clk_regmap_mux_ro_ops,
.ops = &clk_regmap_mux_ops,
.parent_hws = (const struct clk_hw *[]) {
&g12a_cpu_clk_dyn.hw,
&g12a_sys_pll.hw,
},
.num_parents = 2,
.flags = CLK_SET_RATE_PARENT,
},
};
@ -489,12 +507,13 @@ static struct clk_regmap g12b_cpu_clk = {
},
.hw.init = &(struct clk_init_data){
.name = "cpu_clk",
.ops = &clk_regmap_mux_ro_ops,
.ops = &clk_regmap_mux_ops,
.parent_hws = (const struct clk_hw *[]) {
&g12a_cpu_clk_dyn.hw,
&g12b_sys1_pll.hw
},
.num_parents = 2,
.flags = CLK_SET_RATE_PARENT,
},
};
@ -507,7 +526,7 @@ static struct clk_regmap g12b_cpub_clk_premux0 = {
},
.hw.init = &(struct clk_init_data){
.name = "cpub_clk_dyn0_sel",
.ops = &clk_regmap_mux_ro_ops,
.ops = &clk_regmap_mux_ops,
.parent_data = (const struct clk_parent_data []) {
{ .fw_name = "xtal", },
{ .hw = &g12a_fclk_div2.hw },
@ -519,18 +538,26 @@ static struct clk_regmap g12b_cpub_clk_premux0 = {
/* Datasheet names this field as "mux0_divn_tcnt" */
static struct clk_regmap g12b_cpub_clk_mux0_div = {
.data = &(struct clk_regmap_div_data){
.offset = HHI_SYS_CPUB_CLK_CNTL,
.shift = 4,
.width = 6,
.data = &(struct meson_clk_cpu_dyndiv_data){
.div = {
.reg_off = HHI_SYS_CPUB_CLK_CNTL,
.shift = 4,
.width = 6,
},
.dyn = {
.reg_off = HHI_SYS_CPUB_CLK_CNTL,
.shift = 26,
.width = 1,
},
},
.hw.init = &(struct clk_init_data){
.name = "cpub_clk_dyn0_div",
.ops = &clk_regmap_divider_ro_ops,
.ops = &meson_clk_cpu_dyndiv_ops,
.parent_hws = (const struct clk_hw *[]) {
&g12b_cpub_clk_premux0.hw
},
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
},
};
@ -543,12 +570,13 @@ static struct clk_regmap g12b_cpub_clk_postmux0 = {
},
.hw.init = &(struct clk_init_data){
.name = "cpub_clk_dyn0",
.ops = &clk_regmap_mux_ro_ops,
.ops = &clk_regmap_mux_ops,
.parent_hws = (const struct clk_hw *[]) {
&g12b_cpub_clk_premux0.hw,
&g12b_cpub_clk_mux0_div.hw
},
.num_parents = 2,
.flags = CLK_SET_RATE_PARENT,
},
};
@ -561,13 +589,15 @@ static struct clk_regmap g12b_cpub_clk_premux1 = {
},
.hw.init = &(struct clk_init_data){
.name = "cpub_clk_dyn1_sel",
.ops = &clk_regmap_mux_ro_ops,
.ops = &clk_regmap_mux_ops,
.parent_data = (const struct clk_parent_data []) {
{ .fw_name = "xtal", },
{ .hw = &g12a_fclk_div2.hw },
{ .hw = &g12a_fclk_div3.hw },
},
.num_parents = 3,
/* This sub-tree is used a parking clock */
.flags = CLK_SET_RATE_NO_REPARENT,
},
};
@ -597,12 +627,14 @@ static struct clk_regmap g12b_cpub_clk_postmux1 = {
},
.hw.init = &(struct clk_init_data){
.name = "cpub_clk_dyn1",
.ops = &clk_regmap_mux_ro_ops,
.ops = &clk_regmap_mux_ops,
.parent_hws = (const struct clk_hw *[]) {
&g12b_cpub_clk_premux1.hw,
&g12b_cpub_clk_mux1_div.hw
},
.num_parents = 2,
/* This sub-tree is used a parking clock */
.flags = CLK_SET_RATE_NO_REPARENT,
},
};
@ -615,12 +647,13 @@ static struct clk_regmap g12b_cpub_clk_dyn = {
},
.hw.init = &(struct clk_init_data){
.name = "cpub_clk_dyn",
.ops = &clk_regmap_mux_ro_ops,
.ops = &clk_regmap_mux_ops,
.parent_hws = (const struct clk_hw *[]) {
&g12b_cpub_clk_postmux0.hw,
&g12b_cpub_clk_postmux1.hw
},
.num_parents = 2,
.flags = CLK_SET_RATE_PARENT,
},
};
@ -633,15 +666,227 @@ static struct clk_regmap g12b_cpub_clk = {
},
.hw.init = &(struct clk_init_data){
.name = "cpub_clk",
.ops = &clk_regmap_mux_ro_ops,
.ops = &clk_regmap_mux_ops,
.parent_hws = (const struct clk_hw *[]) {
&g12b_cpub_clk_dyn.hw,
&g12a_sys_pll.hw
},
.num_parents = 2,
.flags = CLK_SET_RATE_PARENT,
},
};
static int g12a_cpu_clk_mux_notifier_cb(struct notifier_block *nb,
unsigned long event, void *data)
{
if (event == POST_RATE_CHANGE || event == PRE_RATE_CHANGE) {
/* Wait for clock propagation before/after changing the mux */
udelay(100);
return NOTIFY_OK;
}
return NOTIFY_DONE;
}
static struct notifier_block g12a_cpu_clk_mux_nb = {
.notifier_call = g12a_cpu_clk_mux_notifier_cb,
};
struct g12a_cpu_clk_postmux_nb_data {
struct notifier_block nb;
struct clk_hw *xtal;
struct clk_hw *cpu_clk_dyn;
struct clk_hw *cpu_clk_postmux0;
struct clk_hw *cpu_clk_postmux1;
struct clk_hw *cpu_clk_premux1;
};
static int g12a_cpu_clk_postmux_notifier_cb(struct notifier_block *nb,
unsigned long event, void *data)
{
struct g12a_cpu_clk_postmux_nb_data *nb_data =
container_of(nb, struct g12a_cpu_clk_postmux_nb_data, nb);
switch (event) {
case PRE_RATE_CHANGE:
/*
* This notifier means cpu_clk_postmux0 clock will be changed
* to feed cpu_clk, this is the current path :
* cpu_clk
* \- cpu_clk_dyn
* \- cpu_clk_postmux0
* \- cpu_clk_muxX_div
* \- cpu_clk_premux0
* \- fclk_div3 or fclk_div2
* OR
* \- cpu_clk_premux0
* \- fclk_div3 or fclk_div2
*/
/* Setup cpu_clk_premux1 to xtal */
clk_hw_set_parent(nb_data->cpu_clk_premux1,
nb_data->xtal);
/* Setup cpu_clk_postmux1 to bypass divider */
clk_hw_set_parent(nb_data->cpu_clk_postmux1,
nb_data->cpu_clk_premux1);
/* Switch to parking clk on cpu_clk_postmux1 */
clk_hw_set_parent(nb_data->cpu_clk_dyn,
nb_data->cpu_clk_postmux1);
/*
* Now, cpu_clk is 24MHz in the current path :
* cpu_clk
* \- cpu_clk_dyn
* \- cpu_clk_postmux1
* \- cpu_clk_premux1
* \- xtal
*/
udelay(100);
return NOTIFY_OK;
case POST_RATE_CHANGE:
/*
* The cpu_clk_postmux0 has ben updated, now switch back
* cpu_clk_dyn to cpu_clk_postmux0 and take the changes
* in account.
*/
/* Configure cpu_clk_dyn back to cpu_clk_postmux0 */
clk_hw_set_parent(nb_data->cpu_clk_dyn,
nb_data->cpu_clk_postmux0);
/*
* new path :
* cpu_clk
* \- cpu_clk_dyn
* \- cpu_clk_postmux0
* \- cpu_clk_muxX_div
* \- cpu_clk_premux0
* \- fclk_div3 or fclk_div2
* OR
* \- cpu_clk_premux0
* \- fclk_div3 or fclk_div2
*/
udelay(100);
return NOTIFY_OK;
default:
return NOTIFY_DONE;
}
}
static struct g12a_cpu_clk_postmux_nb_data g12a_cpu_clk_postmux0_nb_data = {
.cpu_clk_dyn = &g12a_cpu_clk_dyn.hw,
.cpu_clk_postmux0 = &g12a_cpu_clk_postmux0.hw,
.cpu_clk_postmux1 = &g12a_cpu_clk_postmux1.hw,
.cpu_clk_premux1 = &g12a_cpu_clk_premux1.hw,
.nb.notifier_call = g12a_cpu_clk_postmux_notifier_cb,
};
static struct g12a_cpu_clk_postmux_nb_data g12b_cpub_clk_postmux0_nb_data = {
.cpu_clk_dyn = &g12b_cpub_clk_dyn.hw,
.cpu_clk_postmux0 = &g12b_cpub_clk_postmux0.hw,
.cpu_clk_postmux1 = &g12b_cpub_clk_postmux1.hw,
.cpu_clk_premux1 = &g12b_cpub_clk_premux1.hw,
.nb.notifier_call = g12a_cpu_clk_postmux_notifier_cb,
};
struct g12a_sys_pll_nb_data {
struct notifier_block nb;
struct clk_hw *sys_pll;
struct clk_hw *cpu_clk;
struct clk_hw *cpu_clk_dyn;
};
static int g12a_sys_pll_notifier_cb(struct notifier_block *nb,
unsigned long event, void *data)
{
struct g12a_sys_pll_nb_data *nb_data =
container_of(nb, struct g12a_sys_pll_nb_data, nb);
switch (event) {
case PRE_RATE_CHANGE:
/*
* This notifier means sys_pll clock will be changed
* to feed cpu_clk, this the current path :
* cpu_clk
* \- sys_pll
* \- sys_pll_dco
*/
/* Configure cpu_clk to use cpu_clk_dyn */
clk_hw_set_parent(nb_data->cpu_clk,
nb_data->cpu_clk_dyn);
/*
* Now, cpu_clk uses the dyn path
* cpu_clk
* \- cpu_clk_dyn
* \- cpu_clk_dynX
* \- cpu_clk_dynX_sel
* \- cpu_clk_dynX_div
* \- xtal/fclk_div2/fclk_div3
* \- xtal/fclk_div2/fclk_div3
*/
udelay(100);
return NOTIFY_OK;
case POST_RATE_CHANGE:
/*
* The sys_pll has ben updated, now switch back cpu_clk to
* sys_pll
*/
/* Configure cpu_clk to use sys_pll */
clk_hw_set_parent(nb_data->cpu_clk,
nb_data->sys_pll);
udelay(100);
/* new path :
* cpu_clk
* \- sys_pll
* \- sys_pll_dco
*/
return NOTIFY_OK;
default:
return NOTIFY_DONE;
}
}
static struct g12a_sys_pll_nb_data g12a_sys_pll_nb_data = {
.sys_pll = &g12a_sys_pll.hw,
.cpu_clk = &g12a_cpu_clk.hw,
.cpu_clk_dyn = &g12a_cpu_clk_dyn.hw,
.nb.notifier_call = g12a_sys_pll_notifier_cb,
};
/* G12B first CPU cluster uses sys1_pll */
static struct g12a_sys_pll_nb_data g12b_cpu_clk_sys1_pll_nb_data = {
.sys_pll = &g12b_sys1_pll.hw,
.cpu_clk = &g12b_cpu_clk.hw,
.cpu_clk_dyn = &g12a_cpu_clk_dyn.hw,
.nb.notifier_call = g12a_sys_pll_notifier_cb,
};
/* G12B second CPU cluster uses sys_pll */
static struct g12a_sys_pll_nb_data g12b_cpub_clk_sys_pll_nb_data = {
.sys_pll = &g12a_sys_pll.hw,
.cpu_clk = &g12b_cpub_clk.hw,
.cpu_clk_dyn = &g12b_cpub_clk_dyn.hw,
.nb.notifier_call = g12a_sys_pll_notifier_cb,
};
static struct clk_regmap g12a_cpu_clk_div16_en = {
.data = &(struct clk_regmap_gate_data){
.offset = HHI_SYS_CPU_CLK_CNTL1,
@ -4097,28 +4342,210 @@ static const struct reg_sequence g12a_init_regs[] = {
{ .reg = HHI_MPLL_CNTL0, .def = 0x00000543 },
};
static const struct meson_eeclkc_data g12a_clkc_data = {
.regmap_clks = g12a_clk_regmaps,
.regmap_clk_num = ARRAY_SIZE(g12a_clk_regmaps),
.hw_onecell_data = &g12a_hw_onecell_data,
.init_regs = g12a_init_regs,
.init_count = ARRAY_SIZE(g12a_init_regs),
static int meson_g12a_dvfs_setup_common(struct platform_device *pdev,
struct clk_hw **hws)
{
const char *notifier_clk_name;
struct clk *notifier_clk;
struct clk_hw *xtal;
int ret;
xtal = clk_hw_get_parent_by_index(hws[CLKID_CPU_CLK_DYN1_SEL], 0);
/* Setup clock notifier for cpu_clk_postmux0 */
g12a_cpu_clk_postmux0_nb_data.xtal = xtal;
notifier_clk_name = clk_hw_get_name(&g12a_cpu_clk_postmux0.hw);
notifier_clk = __clk_lookup(notifier_clk_name);
ret = clk_notifier_register(notifier_clk,
&g12a_cpu_clk_postmux0_nb_data.nb);
if (ret) {
dev_err(&pdev->dev, "failed to register the cpu_clk_postmux0 notifier\n");
return ret;
}
/* Setup clock notifier for cpu_clk_dyn mux */
notifier_clk_name = clk_hw_get_name(&g12a_cpu_clk_dyn.hw);
notifier_clk = __clk_lookup(notifier_clk_name);
ret = clk_notifier_register(notifier_clk, &g12a_cpu_clk_mux_nb);
if (ret) {
dev_err(&pdev->dev, "failed to register the cpu_clk_dyn notifier\n");
return ret;
}
return 0;
}
static int meson_g12b_dvfs_setup(struct platform_device *pdev)
{
struct clk_hw **hws = g12b_hw_onecell_data.hws;
const char *notifier_clk_name;
struct clk *notifier_clk;
struct clk_hw *xtal;
int ret;
ret = meson_g12a_dvfs_setup_common(pdev, hws);
if (ret)
return ret;
xtal = clk_hw_get_parent_by_index(hws[CLKID_CPU_CLK_DYN1_SEL], 0);
/* Setup clock notifier for cpu_clk mux */
notifier_clk_name = clk_hw_get_name(&g12b_cpu_clk.hw);
notifier_clk = __clk_lookup(notifier_clk_name);
ret = clk_notifier_register(notifier_clk, &g12a_cpu_clk_mux_nb);
if (ret) {
dev_err(&pdev->dev, "failed to register the cpu_clk notifier\n");
return ret;
}
/* Setup clock notifier for sys1_pll */
notifier_clk_name = clk_hw_get_name(&g12b_sys1_pll.hw);
notifier_clk = __clk_lookup(notifier_clk_name);
ret = clk_notifier_register(notifier_clk,
&g12b_cpu_clk_sys1_pll_nb_data.nb);
if (ret) {
dev_err(&pdev->dev, "failed to register the sys1_pll notifier\n");
return ret;
}
/* Add notifiers for the second CPU cluster */
/* Setup clock notifier for cpub_clk_postmux0 */
g12b_cpub_clk_postmux0_nb_data.xtal = xtal;
notifier_clk_name = clk_hw_get_name(&g12b_cpub_clk_postmux0.hw);
notifier_clk = __clk_lookup(notifier_clk_name);
ret = clk_notifier_register(notifier_clk,
&g12b_cpub_clk_postmux0_nb_data.nb);
if (ret) {
dev_err(&pdev->dev, "failed to register the cpub_clk_postmux0 notifier\n");
return ret;
}
/* Setup clock notifier for cpub_clk_dyn mux */
notifier_clk_name = clk_hw_get_name(&g12b_cpub_clk_dyn.hw);
notifier_clk = __clk_lookup(notifier_clk_name);
ret = clk_notifier_register(notifier_clk, &g12a_cpu_clk_mux_nb);
if (ret) {
dev_err(&pdev->dev, "failed to register the cpub_clk_dyn notifier\n");
return ret;
}
/* Setup clock notifier for cpub_clk mux */
notifier_clk_name = clk_hw_get_name(&g12b_cpub_clk.hw);
notifier_clk = __clk_lookup(notifier_clk_name);
ret = clk_notifier_register(notifier_clk, &g12a_cpu_clk_mux_nb);
if (ret) {
dev_err(&pdev->dev, "failed to register the cpub_clk notifier\n");
return ret;
}
/* Setup clock notifier for sys_pll */
notifier_clk_name = clk_hw_get_name(&g12a_sys_pll.hw);
notifier_clk = __clk_lookup(notifier_clk_name);
ret = clk_notifier_register(notifier_clk,
&g12b_cpub_clk_sys_pll_nb_data.nb);
if (ret) {
dev_err(&pdev->dev, "failed to register the sys_pll notifier\n");
return ret;
}
return 0;
}
static int meson_g12a_dvfs_setup(struct platform_device *pdev)
{
struct clk_hw **hws = g12a_hw_onecell_data.hws;
const char *notifier_clk_name;
struct clk *notifier_clk;
int ret;
ret = meson_g12a_dvfs_setup_common(pdev, hws);
if (ret)
return ret;
/* Setup clock notifier for cpu_clk mux */
notifier_clk_name = clk_hw_get_name(&g12a_cpu_clk.hw);
notifier_clk = __clk_lookup(notifier_clk_name);
ret = clk_notifier_register(notifier_clk, &g12a_cpu_clk_mux_nb);
if (ret) {
dev_err(&pdev->dev, "failed to register the cpu_clk notifier\n");
return ret;
}
/* Setup clock notifier for sys_pll */
notifier_clk_name = clk_hw_get_name(&g12a_sys_pll.hw);
notifier_clk = __clk_lookup(notifier_clk_name);
ret = clk_notifier_register(notifier_clk, &g12a_sys_pll_nb_data.nb);
if (ret) {
dev_err(&pdev->dev, "failed to register the sys_pll notifier\n");
return ret;
}
return 0;
}
struct meson_g12a_data {
const struct meson_eeclkc_data eeclkc_data;
int (*dvfs_setup)(struct platform_device *pdev);
};
static const struct meson_eeclkc_data g12b_clkc_data = {
.regmap_clks = g12a_clk_regmaps,
.regmap_clk_num = ARRAY_SIZE(g12a_clk_regmaps),
.hw_onecell_data = &g12b_hw_onecell_data
static int meson_g12a_probe(struct platform_device *pdev)
{
const struct meson_eeclkc_data *eeclkc_data;
const struct meson_g12a_data *g12a_data;
int ret;
eeclkc_data = of_device_get_match_data(&pdev->dev);
if (!eeclkc_data)
return -EINVAL;
ret = meson_eeclkc_probe(pdev);
if (ret)
return ret;
g12a_data = container_of(eeclkc_data, struct meson_g12a_data,
eeclkc_data);
if (g12a_data->dvfs_setup)
return g12a_data->dvfs_setup(pdev);
return 0;
}
static const struct meson_g12a_data g12a_clkc_data = {
.eeclkc_data = {
.regmap_clks = g12a_clk_regmaps,
.regmap_clk_num = ARRAY_SIZE(g12a_clk_regmaps),
.hw_onecell_data = &g12a_hw_onecell_data,
.init_regs = g12a_init_regs,
.init_count = ARRAY_SIZE(g12a_init_regs),
},
.dvfs_setup = meson_g12a_dvfs_setup,
};
static const struct meson_g12a_data g12b_clkc_data = {
.eeclkc_data = {
.regmap_clks = g12a_clk_regmaps,
.regmap_clk_num = ARRAY_SIZE(g12a_clk_regmaps),
.hw_onecell_data = &g12b_hw_onecell_data,
},
.dvfs_setup = meson_g12b_dvfs_setup,
};
static const struct of_device_id clkc_match_table[] = {
{ .compatible = "amlogic,g12a-clkc", .data = &g12a_clkc_data },
{ .compatible = "amlogic,g12b-clkc", .data = &g12b_clkc_data },
{
.compatible = "amlogic,g12a-clkc",
.data = &g12a_clkc_data.eeclkc_data
},
{
.compatible = "amlogic,g12b-clkc",
.data = &g12b_clkc_data.eeclkc_data
},
{}
};
static struct platform_driver g12a_driver = {
.probe = meson_eeclkc_probe,
.probe = meson_g12a_probe,
.driver = {
.name = "g12a-clkc",
.of_match_table = clkc_match_table,