clk/rockchip: add cru support for rk618
Change-Id: I223c85194f62fec2c22c2a013466b767a1128f9c Signed-off-by: Wyon Bi <bivvy.bi@rock-chips.com>
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
Rockchip RK618 Clock and Reset Unit
|
||||
|
||||
This binding uses the common clock binding:
|
||||
Documentation/devicetree/bindings/clock/clock-bindings.txt
|
||||
|
||||
Required properties :
|
||||
- compatible : Should be "rockchip,rk618-cru"
|
||||
- clocks : Should contain phandle and clock specifiers for the input clock:
|
||||
the AP I2S master clock output(mclk) "clkin", and the AP LCDC master
|
||||
dclk output(dclk) "lcdc0_dclkp".
|
||||
- #clock-cells : Should be 1.
|
||||
|
||||
Example:
|
||||
|
||||
&rk618 {
|
||||
CRU: cru {
|
||||
compatible = "rockchip,rk618-cru";
|
||||
clocks = <&cru SCLK_I2S_8CH_OUT>, <&cru DCLK_VOP>;
|
||||
clock-names = "clkin", "lcdc0_dclkp";
|
||||
assigned-clocks = <&CRU SCALER_PLLIN_CLK>, <&CRU VIF_PLLIN_CLK>,
|
||||
<&CRU HDMI_CLK>, <&CRU SCALER_CLK>,
|
||||
<&CRU CODEC_CLK>;
|
||||
assigned-clock-parents = <&CRU LCDC0_CLK>, <&CRU LCDC0_CLK>,
|
||||
<&CRU VIF0_CLK>, <&CRU SCALER_PLL_CLK>,
|
||||
<&cru SCLK_I2S_8CH_OUT>;
|
||||
#clock-cells = <1>;
|
||||
status = "okay";
|
||||
};
|
||||
};
|
||||
@@ -22,3 +22,4 @@ obj-y += clk-rk3328.o
|
||||
obj-y += clk-rk3366.o
|
||||
obj-y += clk-rk3368.o
|
||||
obj-y += clk-rk3399.o
|
||||
obj-$(CONFIG_MFD_RK618) += rk618/
|
||||
|
||||
11
drivers/clk/rockchip/rk618/Makefile
Normal file
11
drivers/clk/rockchip/rk618/Makefile
Normal file
@@ -0,0 +1,11 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# Makefile for the Rockchip RK618 CRU driver.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_MFD_RK618) += clk-regmap-mux.o \
|
||||
clk-regmap-divider.o \
|
||||
clk-regmap-gate.o \
|
||||
clk-regmap-composite.o \
|
||||
clk-regmap-pll.o \
|
||||
clk-rk618.o
|
||||
351
drivers/clk/rockchip/rk618/clk-regmap-composite.c
Normal file
351
drivers/clk/rockchip/rk618/clk-regmap-composite.c
Normal file
@@ -0,0 +1,351 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Rockchip Electronics Co. Ltd.
|
||||
*
|
||||
* Base on code in drivers/clk/clk-composite.c.
|
||||
* See clk-composite.c for further copyright information.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "clk-regmap.h"
|
||||
|
||||
struct clk_regmap_composite {
|
||||
struct device *dev;
|
||||
struct clk_hw hw;
|
||||
struct clk_ops ops;
|
||||
|
||||
struct clk_hw *mux_hw;
|
||||
struct clk_hw *rate_hw;
|
||||
struct clk_hw *gate_hw;
|
||||
|
||||
const struct clk_ops *mux_ops;
|
||||
const struct clk_ops *rate_ops;
|
||||
const struct clk_ops *gate_ops;
|
||||
};
|
||||
|
||||
#define to_clk_regmap_composite(_hw) \
|
||||
container_of(_hw, struct clk_regmap_composite, hw)
|
||||
|
||||
static u8 clk_regmap_composite_get_parent(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_regmap_composite *composite = to_clk_regmap_composite(hw);
|
||||
const struct clk_ops *mux_ops = composite->mux_ops;
|
||||
struct clk_hw *mux_hw = composite->mux_hw;
|
||||
|
||||
__clk_hw_set_clk(mux_hw, hw);
|
||||
|
||||
return mux_ops->get_parent(mux_hw);
|
||||
}
|
||||
|
||||
static int clk_regmap_composite_set_parent(struct clk_hw *hw, u8 index)
|
||||
{
|
||||
struct clk_regmap_composite *composite = to_clk_regmap_composite(hw);
|
||||
const struct clk_ops *mux_ops = composite->mux_ops;
|
||||
struct clk_hw *mux_hw = composite->mux_hw;
|
||||
|
||||
__clk_hw_set_clk(mux_hw, hw);
|
||||
|
||||
return mux_ops->set_parent(mux_hw, index);
|
||||
}
|
||||
|
||||
static unsigned long clk_regmap_composite_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct clk_regmap_composite *composite = to_clk_regmap_composite(hw);
|
||||
const struct clk_ops *rate_ops = composite->rate_ops;
|
||||
struct clk_hw *rate_hw = composite->rate_hw;
|
||||
|
||||
__clk_hw_set_clk(rate_hw, hw);
|
||||
|
||||
return rate_ops->recalc_rate(rate_hw, parent_rate);
|
||||
}
|
||||
|
||||
static int clk_regmap_composite_determine_rate(struct clk_hw *hw,
|
||||
struct clk_rate_request *req)
|
||||
{
|
||||
struct clk_regmap_composite *composite = to_clk_regmap_composite(hw);
|
||||
const struct clk_ops *rate_ops = composite->rate_ops;
|
||||
const struct clk_ops *mux_ops = composite->mux_ops;
|
||||
struct clk_hw *rate_hw = composite->rate_hw;
|
||||
struct clk_hw *mux_hw = composite->mux_hw;
|
||||
struct clk_hw *parent;
|
||||
unsigned long parent_rate;
|
||||
long tmp_rate, best_rate = 0;
|
||||
unsigned long rate_diff;
|
||||
unsigned long best_rate_diff = ULONG_MAX;
|
||||
long rate;
|
||||
int i;
|
||||
|
||||
if (rate_hw && rate_ops && rate_ops->determine_rate) {
|
||||
__clk_hw_set_clk(rate_hw, hw);
|
||||
return rate_ops->determine_rate(rate_hw, req);
|
||||
} else if (rate_hw && rate_ops && rate_ops->round_rate &&
|
||||
mux_hw && mux_ops && mux_ops->set_parent) {
|
||||
req->best_parent_hw = NULL;
|
||||
|
||||
if (clk_hw_get_flags(hw) & CLK_SET_RATE_NO_REPARENT) {
|
||||
parent = clk_hw_get_parent(mux_hw);
|
||||
req->best_parent_hw = parent;
|
||||
req->best_parent_rate = clk_hw_get_rate(parent);
|
||||
|
||||
rate = rate_ops->round_rate(rate_hw, req->rate,
|
||||
&req->best_parent_rate);
|
||||
if (rate < 0)
|
||||
return rate;
|
||||
|
||||
req->rate = rate;
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < clk_hw_get_num_parents(mux_hw); i++) {
|
||||
parent = clk_hw_get_parent_by_index(mux_hw, i);
|
||||
if (!parent)
|
||||
continue;
|
||||
|
||||
parent_rate = clk_hw_get_rate(parent);
|
||||
|
||||
tmp_rate = rate_ops->round_rate(rate_hw, req->rate,
|
||||
&parent_rate);
|
||||
if (tmp_rate < 0)
|
||||
continue;
|
||||
|
||||
rate_diff = abs(req->rate - tmp_rate);
|
||||
|
||||
if (!rate_diff || !req->best_parent_hw ||
|
||||
best_rate_diff > rate_diff) {
|
||||
req->best_parent_hw = parent;
|
||||
req->best_parent_rate = parent_rate;
|
||||
best_rate_diff = rate_diff;
|
||||
best_rate = tmp_rate;
|
||||
}
|
||||
|
||||
if (!rate_diff)
|
||||
return 0;
|
||||
}
|
||||
|
||||
req->rate = best_rate;
|
||||
return 0;
|
||||
} else if (mux_hw && mux_ops && mux_ops->determine_rate) {
|
||||
__clk_hw_set_clk(mux_hw, hw);
|
||||
return mux_ops->determine_rate(mux_hw, req);
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long clk_regmap_composite_round_rate(struct clk_hw *hw,
|
||||
unsigned long rate,
|
||||
unsigned long *prate)
|
||||
{
|
||||
struct clk_regmap_composite *composite = to_clk_regmap_composite(hw);
|
||||
const struct clk_ops *rate_ops = composite->rate_ops;
|
||||
struct clk_hw *rate_hw = composite->rate_hw;
|
||||
|
||||
__clk_hw_set_clk(rate_hw, hw);
|
||||
|
||||
return rate_ops->round_rate(rate_hw, rate, prate);
|
||||
}
|
||||
|
||||
static int clk_regmap_composite_set_rate(struct clk_hw *hw,
|
||||
unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct clk_regmap_composite *composite = to_clk_regmap_composite(hw);
|
||||
const struct clk_ops *rate_ops = composite->rate_ops;
|
||||
struct clk_hw *rate_hw = composite->rate_hw;
|
||||
|
||||
__clk_hw_set_clk(rate_hw, hw);
|
||||
|
||||
return rate_ops->set_rate(rate_hw, rate, parent_rate);
|
||||
}
|
||||
|
||||
static int clk_regmap_composite_is_prepared(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_regmap_composite *composite = to_clk_regmap_composite(hw);
|
||||
const struct clk_ops *gate_ops = composite->gate_ops;
|
||||
struct clk_hw *gate_hw = composite->gate_hw;
|
||||
|
||||
__clk_hw_set_clk(gate_hw, hw);
|
||||
|
||||
return gate_ops->is_prepared(gate_hw);
|
||||
}
|
||||
|
||||
static int clk_regmap_composite_prepare(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_regmap_composite *composite = to_clk_regmap_composite(hw);
|
||||
const struct clk_ops *gate_ops = composite->gate_ops;
|
||||
struct clk_hw *gate_hw = composite->gate_hw;
|
||||
|
||||
__clk_hw_set_clk(gate_hw, hw);
|
||||
|
||||
return gate_ops->prepare(gate_hw);
|
||||
}
|
||||
|
||||
static void clk_regmap_composite_unprepare(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_regmap_composite *composite = to_clk_regmap_composite(hw);
|
||||
const struct clk_ops *gate_ops = composite->gate_ops;
|
||||
struct clk_hw *gate_hw = composite->gate_hw;
|
||||
|
||||
__clk_hw_set_clk(gate_hw, hw);
|
||||
|
||||
gate_ops->unprepare(gate_hw);
|
||||
}
|
||||
|
||||
struct clk *
|
||||
devm_clk_regmap_register_composite(struct device *dev, const char *name,
|
||||
const char *const *parent_names,
|
||||
u8 num_parents, struct regmap *regmap,
|
||||
u32 mux_reg, u8 mux_shift, u8 mux_width,
|
||||
u32 div_reg, u8 div_shift, u8 div_width,
|
||||
u32 gate_reg, u8 gate_shift,
|
||||
unsigned long flags)
|
||||
{
|
||||
struct clk_regmap_gate *gate = NULL;
|
||||
struct clk_regmap_mux *mux = NULL;
|
||||
struct clk_regmap_divider *div = NULL;
|
||||
const struct clk_ops *mux_ops = NULL, *div_ops = NULL, *gate_ops = NULL;
|
||||
struct clk_hw *mux_hw = NULL, *div_hw = NULL, *gate_hw = NULL;
|
||||
struct clk *clk;
|
||||
struct clk_init_data init;
|
||||
struct clk_regmap_composite *composite;
|
||||
struct clk_ops *clk_composite_ops;
|
||||
|
||||
if (num_parents > 1) {
|
||||
mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
|
||||
if (!mux)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
mux->dev = dev;
|
||||
mux->regmap = regmap;
|
||||
mux->reg = mux_reg;
|
||||
mux->shift = mux_shift;
|
||||
mux->mask = BIT(mux_width) - 1;
|
||||
mux_ops = &clk_regmap_mux_ops;
|
||||
mux_hw = &mux->hw;
|
||||
}
|
||||
|
||||
if (gate_reg > 0) {
|
||||
gate = devm_kzalloc(dev, sizeof(*gate), GFP_KERNEL);
|
||||
if (!gate)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
gate->dev = dev;
|
||||
gate->regmap = regmap;
|
||||
gate->reg = gate_reg;
|
||||
gate->shift = gate_shift;
|
||||
gate_ops = &clk_regmap_gate_ops;
|
||||
gate_hw = &gate->hw;
|
||||
}
|
||||
|
||||
if (div_width > 0) {
|
||||
div = devm_kzalloc(dev, sizeof(*div), GFP_KERNEL);
|
||||
if (!div)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
div->dev = dev;
|
||||
div->regmap = regmap;
|
||||
div->reg = div_reg;
|
||||
div->shift = div_shift;
|
||||
div->width = div_width;
|
||||
div_ops = &clk_regmap_divider_ops;
|
||||
div_hw = &div->hw;
|
||||
}
|
||||
|
||||
composite = devm_kzalloc(dev, sizeof(*composite), GFP_KERNEL);
|
||||
if (!composite)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
init.name = name;
|
||||
init.flags = flags;
|
||||
init.parent_names = parent_names;
|
||||
init.num_parents = num_parents;
|
||||
|
||||
clk_composite_ops = &composite->ops;
|
||||
|
||||
if (mux_hw && mux_ops) {
|
||||
if (!mux_ops->get_parent)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
composite->mux_hw = mux_hw;
|
||||
composite->mux_ops = mux_ops;
|
||||
clk_composite_ops->get_parent =
|
||||
clk_regmap_composite_get_parent;
|
||||
if (mux_ops->set_parent)
|
||||
clk_composite_ops->set_parent =
|
||||
clk_regmap_composite_set_parent;
|
||||
if (mux_ops->determine_rate)
|
||||
clk_composite_ops->determine_rate =
|
||||
clk_regmap_composite_determine_rate;
|
||||
}
|
||||
|
||||
if (div_hw && div_ops) {
|
||||
if (!div_ops->recalc_rate)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
clk_composite_ops->recalc_rate =
|
||||
clk_regmap_composite_recalc_rate;
|
||||
|
||||
if (div_ops->determine_rate)
|
||||
clk_composite_ops->determine_rate =
|
||||
clk_regmap_composite_determine_rate;
|
||||
else if (div_ops->round_rate)
|
||||
clk_composite_ops->round_rate =
|
||||
clk_regmap_composite_round_rate;
|
||||
|
||||
/* .set_rate requires either .round_rate or .determine_rate */
|
||||
if (div_ops->set_rate) {
|
||||
if (div_ops->determine_rate || div_ops->round_rate)
|
||||
clk_composite_ops->set_rate =
|
||||
clk_regmap_composite_set_rate;
|
||||
else
|
||||
WARN(1, "missing round_rate op\n");
|
||||
}
|
||||
|
||||
composite->rate_hw = div_hw;
|
||||
composite->rate_ops = div_ops;
|
||||
}
|
||||
|
||||
if (gate_hw && gate_ops) {
|
||||
if (!gate_ops->is_prepared || !gate_ops->prepare ||
|
||||
!gate_ops->unprepare)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
composite->gate_hw = gate_hw;
|
||||
composite->gate_ops = gate_ops;
|
||||
clk_composite_ops->is_prepared =
|
||||
clk_regmap_composite_is_prepared;
|
||||
clk_composite_ops->prepare = clk_regmap_composite_prepare;
|
||||
clk_composite_ops->unprepare = clk_regmap_composite_unprepare;
|
||||
}
|
||||
|
||||
init.ops = clk_composite_ops;
|
||||
composite->dev = dev;
|
||||
composite->hw.init = &init;
|
||||
|
||||
clk = devm_clk_register(dev, &composite->hw);
|
||||
if (IS_ERR(clk))
|
||||
return clk;
|
||||
|
||||
if (composite->mux_hw)
|
||||
composite->mux_hw->clk = clk;
|
||||
|
||||
if (composite->rate_hw)
|
||||
composite->rate_hw->clk = clk;
|
||||
|
||||
if (composite->gate_hw)
|
||||
composite->gate_hw->clk = clk;
|
||||
|
||||
return clk;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_clk_regmap_register_composite);
|
||||
104
drivers/clk/rockchip/rk618/clk-regmap-divider.c
Normal file
104
drivers/clk/rockchip/rk618/clk-regmap-divider.c
Normal file
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Rockchip Electronics Co. Ltd.
|
||||
*
|
||||
* Base on code in drivers/clk/clk-divider.c.
|
||||
* See clk-divider.c for further copyright information.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "clk-regmap.h"
|
||||
|
||||
#define div_mask(width) ((1 << (width)) - 1)
|
||||
|
||||
#define to_clk_regmap_divider(_hw) \
|
||||
container_of(_hw, struct clk_regmap_divider, hw)
|
||||
|
||||
static unsigned long
|
||||
clk_regmap_divider_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
|
||||
{
|
||||
struct clk_regmap_divider *divider = to_clk_regmap_divider(hw);
|
||||
unsigned int val, div;
|
||||
|
||||
regmap_read(divider->regmap, divider->reg, &val);
|
||||
|
||||
div = val >> divider->shift;
|
||||
div &= div_mask(divider->width);
|
||||
|
||||
return divider_recalc_rate(hw, parent_rate, div, NULL,
|
||||
CLK_DIVIDER_ROUND_CLOSEST);
|
||||
}
|
||||
|
||||
static long
|
||||
clk_regmap_divider_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *prate)
|
||||
{
|
||||
struct clk_regmap_divider *divider = to_clk_regmap_divider(hw);
|
||||
|
||||
return divider_round_rate(hw, rate, prate, NULL, divider->width,
|
||||
CLK_DIVIDER_ROUND_CLOSEST);
|
||||
}
|
||||
|
||||
static int
|
||||
clk_regmap_divider_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct clk_regmap_divider *divider = to_clk_regmap_divider(hw);
|
||||
u32 val, div;
|
||||
|
||||
div = divider_get_val(rate, parent_rate, NULL, divider->width,
|
||||
CLK_DIVIDER_ROUND_CLOSEST);
|
||||
|
||||
dev_dbg(divider->dev, "%s: parent_rate=%ld, div=%d, rate=%ld\n",
|
||||
clk_hw_get_name(hw), parent_rate, div, rate);
|
||||
|
||||
val = div_mask(divider->width) << (divider->shift + 16);
|
||||
val |= div << divider->shift;
|
||||
|
||||
return regmap_write(divider->regmap, divider->reg, val);
|
||||
}
|
||||
|
||||
const struct clk_ops clk_regmap_divider_ops = {
|
||||
.recalc_rate = clk_regmap_divider_recalc_rate,
|
||||
.round_rate = clk_regmap_divider_round_rate,
|
||||
.set_rate = clk_regmap_divider_set_rate,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(clk_regmap_divider_ops);
|
||||
|
||||
struct clk *
|
||||
devm_clk_regmap_register_divider(struct device *dev, const char *name,
|
||||
const char *parent_name, struct regmap *regmap,
|
||||
u32 reg, u8 shift, u8 width,
|
||||
unsigned long flags)
|
||||
{
|
||||
struct clk_regmap_divider *divider;
|
||||
struct clk_init_data init;
|
||||
|
||||
divider = devm_kzalloc(dev, sizeof(*divider), GFP_KERNEL);
|
||||
if (!divider)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
init.name = name;
|
||||
init.ops = &clk_regmap_divider_ops;
|
||||
init.flags = flags;
|
||||
init.parent_names = (parent_name ? &parent_name : NULL);
|
||||
init.num_parents = (parent_name ? 1 : 0);
|
||||
|
||||
divider->dev = dev;
|
||||
divider->regmap = regmap;
|
||||
divider->reg = reg;
|
||||
divider->shift = shift;
|
||||
divider->width = width;
|
||||
divider->hw.init = &init;
|
||||
|
||||
return devm_clk_register(dev, ÷r->hw);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_clk_regmap_register_divider);
|
||||
82
drivers/clk/rockchip/rk618/clk-regmap-gate.c
Normal file
82
drivers/clk/rockchip/rk618/clk-regmap-gate.c
Normal file
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Rockchip Electronics Co. Ltd.
|
||||
*
|
||||
* Base on code in drivers/clk/clk-gate.c.
|
||||
* See clk-gate.c for further copyright information.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "clk-regmap.h"
|
||||
|
||||
#define to_clk_regmap_gate(_hw) container_of(_hw, struct clk_regmap_gate, hw)
|
||||
|
||||
static int clk_regmap_gate_prepare(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_regmap_gate *gate = to_clk_regmap_gate(hw);
|
||||
|
||||
return regmap_write(gate->regmap, gate->reg,
|
||||
0 | BIT(gate->shift + 16));
|
||||
}
|
||||
|
||||
static void clk_regmap_gate_unprepare(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_regmap_gate *gate = to_clk_regmap_gate(hw);
|
||||
|
||||
regmap_write(gate->regmap, gate->reg,
|
||||
BIT(gate->shift) | BIT(gate->shift + 16));
|
||||
}
|
||||
|
||||
static int clk_regmap_gate_is_prepared(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_regmap_gate *gate = to_clk_regmap_gate(hw);
|
||||
u32 val;
|
||||
|
||||
regmap_read(gate->regmap, gate->reg, &val);
|
||||
|
||||
return !(val & BIT(gate->shift));
|
||||
}
|
||||
|
||||
const struct clk_ops clk_regmap_gate_ops = {
|
||||
.prepare = clk_regmap_gate_prepare,
|
||||
.unprepare = clk_regmap_gate_unprepare,
|
||||
.is_prepared = clk_regmap_gate_is_prepared,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(clk_regmap_gate_ops);
|
||||
|
||||
struct clk *
|
||||
devm_clk_regmap_register_gate(struct device *dev, const char *name,
|
||||
const char *parent_name,
|
||||
struct regmap *regmap, u32 reg, u8 shift,
|
||||
unsigned long flags)
|
||||
{
|
||||
struct clk_regmap_gate *gate;
|
||||
struct clk_init_data init;
|
||||
|
||||
gate = devm_kzalloc(dev, sizeof(*gate), GFP_KERNEL);
|
||||
if (!gate)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
init.name = name;
|
||||
init.ops = &clk_regmap_gate_ops;
|
||||
init.flags = flags;
|
||||
init.parent_names = (parent_name ? &parent_name : NULL);
|
||||
init.num_parents = (parent_name ? 1 : 0);
|
||||
|
||||
gate->dev = dev;
|
||||
gate->regmap = regmap;
|
||||
gate->reg = reg;
|
||||
gate->shift = shift;
|
||||
gate->hw.init = &init;
|
||||
|
||||
return devm_clk_register(dev, &gate->hw);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_clk_regmap_register_gate);
|
||||
79
drivers/clk/rockchip/rk618/clk-regmap-mux.c
Normal file
79
drivers/clk/rockchip/rk618/clk-regmap-mux.c
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Rockchip Electronics Co. Ltd.
|
||||
*
|
||||
* Base on code in drivers/clk/clk-mux.c.
|
||||
* See clk-mux.c for further copyright information.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "clk-regmap.h"
|
||||
|
||||
#define to_clk_regmap_mux(_hw) container_of(_hw, struct clk_regmap_mux, hw)
|
||||
|
||||
static u8 clk_regmap_mux_get_parent(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_regmap_mux *mux = to_clk_regmap_mux(hw);
|
||||
u8 index;
|
||||
u32 val;
|
||||
|
||||
regmap_read(mux->regmap, mux->reg, &val);
|
||||
|
||||
index = val >> mux->shift;
|
||||
index &= mux->mask;
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
static int clk_regmap_mux_set_parent(struct clk_hw *hw, u8 index)
|
||||
{
|
||||
struct clk_regmap_mux *mux = to_clk_regmap_mux(hw);
|
||||
|
||||
return regmap_write(mux->regmap, mux->reg, (index << mux->shift) |
|
||||
(mux->mask << (mux->shift + 16)));
|
||||
}
|
||||
|
||||
const struct clk_ops clk_regmap_mux_ops = {
|
||||
.set_parent = clk_regmap_mux_set_parent,
|
||||
.get_parent = clk_regmap_mux_get_parent,
|
||||
.determine_rate = __clk_mux_determine_rate,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(clk_regmap_mux_ops);
|
||||
|
||||
struct clk *
|
||||
devm_clk_regmap_register_mux(struct device *dev, const char *name,
|
||||
const char * const *parent_names, u8 num_parents,
|
||||
struct regmap *regmap, u32 reg, u8 shift, u8 width,
|
||||
unsigned long flags)
|
||||
{
|
||||
struct clk_regmap_mux *mux;
|
||||
struct clk_init_data init;
|
||||
|
||||
mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
|
||||
if (!mux)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
init.name = name;
|
||||
init.ops = &clk_regmap_mux_ops;
|
||||
init.flags = flags;
|
||||
init.parent_names = parent_names;
|
||||
init.num_parents = num_parents;
|
||||
|
||||
mux->dev = dev;
|
||||
mux->regmap = regmap;
|
||||
mux->reg = reg;
|
||||
mux->shift = shift;
|
||||
mux->mask = BIT(width) - 1;
|
||||
mux->hw.init = &init;
|
||||
|
||||
return devm_clk_register(dev, &mux->hw);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_clk_regmap_register_mux);
|
||||
341
drivers/clk/rockchip/rk618/clk-regmap-pll.c
Normal file
341
drivers/clk/rockchip/rk618/clk-regmap-pll.c
Normal file
@@ -0,0 +1,341 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Rockchip Electronics Co. Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "clk-regmap.h"
|
||||
|
||||
#define PLLCON_OFFSET(x) (x * 4)
|
||||
|
||||
#define PLL_BYPASS BIT(15)
|
||||
#define PLL_POSTDIV1(x) HIWORD_UPDATE(x, 14, 12)
|
||||
#define PLL_POSTDIV1_MASK GENMASK(14, 12)
|
||||
#define PLL_POSTDIV1_SHIFT 12
|
||||
#define PLL_FBDIV(x) HIWORD_UPDATE(x, 11, 0)
|
||||
#define PLL_FBDIV_MASK GENMASK(11, 0)
|
||||
#define PLL_FBDIV_SHIFT 0
|
||||
|
||||
#define PLL_LOCK BIT(15)
|
||||
#define PLL_POWER_DOWN HIWORD_UPDATE(1, 10, 10)
|
||||
#define PLL_POWER_UP HIWORD_UPDATE(0, 10, 10)
|
||||
#define PLL_DSMPD_MASK BIT(9)
|
||||
#define PLL_DSMPD_SHIFT 9
|
||||
#define PLL_DSMPD(x) HIWORD_UPDATE(x, 9, 9)
|
||||
#define PLL_POSTDIV2(x) HIWORD_UPDATE(x, 8, 6)
|
||||
#define PLL_POSTDIV2_MASK GENMASK(8, 6)
|
||||
#define PLL_POSTDIV2_SHIFT 6
|
||||
#define PLL_REFDIV(x) HIWORD_UPDATE(x, 5, 0)
|
||||
#define PLL_REFDIV_MASK GENMASK(5, 0)
|
||||
#define PLL_REFDIV_SHIFT 0
|
||||
|
||||
#define PLL_FOUT_4PHASE_CLK_POWER_DOWN BIT(27)
|
||||
#define PLL_FOUT_VCO_CLK_POWER_DOWN BIT(26)
|
||||
#define PLL_FOUT_POST_DIV_POWER_DOWN BIT(25)
|
||||
#define PLL_DAC_POWER_DOWN BIT(24)
|
||||
#define PLL_FRAC(x) UPDATE(x, 23, 0)
|
||||
#define PLL_FRAC_MASK GENMASK(23, 0)
|
||||
#define PLL_FRAC_SHIFT 0
|
||||
|
||||
#define MIN_FREF_RATE 10000000UL
|
||||
#define MAX_FREF_RATE 800000000UL
|
||||
#define MIN_FREFDIV_RATE 1000000UL
|
||||
#define MAX_FREFDIV_RATE 40000000UL
|
||||
#define MIN_FVCO_RATE 400000000UL
|
||||
#define MAX_FVCO_RATE 1600000000UL
|
||||
#define MIN_FOUTPOSTDIV_RATE 8000000UL
|
||||
#define MAX_FOUTPOSTDIV_RATE 1600000000UL
|
||||
|
||||
struct clk_regmap_pll {
|
||||
struct clk_hw hw;
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
unsigned int reg;
|
||||
};
|
||||
|
||||
#define to_clk_regmap_pll(_hw) container_of(_hw, struct clk_regmap_pll, hw)
|
||||
|
||||
static unsigned long
|
||||
clk_regmap_pll_recalc_rate(struct clk_hw *hw, unsigned long prate)
|
||||
{
|
||||
struct clk_regmap_pll *pll = to_clk_regmap_pll(hw);
|
||||
unsigned int postdiv1, fbdiv, dsmpd, postdiv2, refdiv, frac;
|
||||
unsigned int con0, con1, con2;
|
||||
u64 foutvco, foutpostdiv;
|
||||
|
||||
regmap_read(pll->regmap, pll->reg + PLLCON_OFFSET(0), &con0);
|
||||
regmap_read(pll->regmap, pll->reg + PLLCON_OFFSET(1), &con1);
|
||||
regmap_read(pll->regmap, pll->reg + PLLCON_OFFSET(2), &con2);
|
||||
|
||||
postdiv1 = (con0 & PLL_POSTDIV1_MASK) >> PLL_POSTDIV1_SHIFT;
|
||||
fbdiv = (con0 & PLL_FBDIV_MASK) >> PLL_FBDIV_SHIFT;
|
||||
dsmpd = (con1 & PLL_DSMPD_MASK) >> PLL_DSMPD_SHIFT;
|
||||
postdiv2 = (con1 & PLL_POSTDIV2_MASK) >> PLL_POSTDIV2_SHIFT;
|
||||
refdiv = (con1 & PLL_REFDIV_MASK) >> PLL_REFDIV_SHIFT;
|
||||
frac = (con2 & PLL_FRAC_MASK) >> PLL_FRAC_SHIFT;
|
||||
|
||||
foutvco = prate * fbdiv;
|
||||
do_div(foutvco, refdiv);
|
||||
|
||||
if (!dsmpd) {
|
||||
u64 frac_rate = prate * frac;
|
||||
|
||||
do_div(frac_rate, refdiv);
|
||||
foutvco += frac_rate >> 24;
|
||||
}
|
||||
|
||||
foutpostdiv = foutvco;
|
||||
do_div(foutpostdiv, postdiv1);
|
||||
do_div(foutpostdiv, postdiv2);
|
||||
|
||||
return foutpostdiv;
|
||||
}
|
||||
|
||||
static unsigned long clk_pll_round_rate(unsigned long fin,
|
||||
unsigned long fout,
|
||||
u8 *refdiv, u16 *fbdiv,
|
||||
u8 *postdiv1, u8 *postdiv2,
|
||||
u32 *frac, u8 *dsmpd)
|
||||
{
|
||||
u8 min_refdiv, max_refdiv, postdiv;
|
||||
u8 _dsmpd = 1, _postdiv1 = 0, _postdiv2 = 0, _refdiv = 0;
|
||||
u16 _fbdiv = 0;
|
||||
u32 _frac = 0;
|
||||
u64 foutvco, foutpostdiv;
|
||||
|
||||
/*
|
||||
* FREF : 10MHz ~ 800MHz
|
||||
* FREFDIV : 1MHz ~ 40MHz
|
||||
* FOUTVCO : 400MHz ~ 1.6GHz
|
||||
* FOUTPOSTDIV : 8MHz ~ 1.6GHz
|
||||
*/
|
||||
if (fin < MIN_FREF_RATE || fin > MAX_FREF_RATE)
|
||||
return -EINVAL;
|
||||
|
||||
if (fout < MIN_FOUTPOSTDIV_RATE || fout > MAX_FOUTPOSTDIV_RATE)
|
||||
return -EINVAL;
|
||||
|
||||
min_refdiv = DIV_ROUND_UP(fin, MAX_FREFDIV_RATE);
|
||||
max_refdiv = fin / MIN_FREFDIV_RATE;
|
||||
if (max_refdiv > 64)
|
||||
max_refdiv = 64;
|
||||
|
||||
if (fout < MIN_FVCO_RATE) {
|
||||
postdiv = DIV_ROUND_UP_ULL(MIN_FVCO_RATE, fout);
|
||||
|
||||
for (_postdiv2 = 1; _postdiv2 < 8; _postdiv2++) {
|
||||
if (postdiv % _postdiv2)
|
||||
continue;
|
||||
|
||||
_postdiv1 = postdiv / _postdiv2;
|
||||
|
||||
if (_postdiv1 > 0 && _postdiv1 < 8)
|
||||
break;
|
||||
}
|
||||
|
||||
fout *= _postdiv1 * _postdiv2;
|
||||
} else {
|
||||
_postdiv1 = 1;
|
||||
_postdiv2 = 1;
|
||||
}
|
||||
|
||||
for (_refdiv = min_refdiv; _refdiv <= max_refdiv; _refdiv++) {
|
||||
u64 tmp, frac_rate;
|
||||
|
||||
if (fin % _refdiv)
|
||||
continue;
|
||||
|
||||
tmp = (u64)fout * _refdiv;
|
||||
do_div(tmp, fin);
|
||||
_fbdiv = tmp;
|
||||
if (_fbdiv < 10 || _fbdiv > 1600)
|
||||
continue;
|
||||
|
||||
tmp = (u64)_fbdiv * fin;
|
||||
do_div(tmp, _refdiv);
|
||||
if (fout < MIN_FVCO_RATE || fout > MAX_FVCO_RATE)
|
||||
continue;
|
||||
|
||||
frac_rate = fout - tmp;
|
||||
|
||||
if (frac_rate) {
|
||||
tmp = (u64)frac_rate * _refdiv;
|
||||
tmp <<= 24;
|
||||
do_div(tmp, fin);
|
||||
_frac = tmp;
|
||||
_dsmpd = 0;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* If DSMPD = 1 (DSM is disabled, "integer mode")
|
||||
* FOUTVCO = FREF / REFDIV * FBDIV
|
||||
* FOUTPOSTDIV = FOUTVCO / POSTDIV1 / POSTDIV2
|
||||
*
|
||||
* If DSMPD = 0 (DSM is enabled, "fractional mode")
|
||||
* FOUTVCO = FREF / REFDIV * (FBDIV + FRAC / 2^24)
|
||||
* FOUTPOSTDIV = FOUTVCO / POSTDIV1 / POSTDIV2
|
||||
*/
|
||||
foutvco = fin * _fbdiv;
|
||||
do_div(foutvco, _refdiv);
|
||||
|
||||
if (!_dsmpd) {
|
||||
u64 frac_rate = fin * _frac;
|
||||
|
||||
do_div(frac_rate, _refdiv);
|
||||
foutvco += frac_rate >> 24;
|
||||
}
|
||||
|
||||
foutpostdiv = foutvco;
|
||||
do_div(foutpostdiv, _postdiv1);
|
||||
do_div(foutpostdiv, _postdiv2);
|
||||
|
||||
if (refdiv)
|
||||
*refdiv = _refdiv;
|
||||
if (fbdiv)
|
||||
*fbdiv = _fbdiv;
|
||||
if (postdiv1)
|
||||
*postdiv1 = _postdiv1;
|
||||
if (postdiv2)
|
||||
*postdiv2 = _postdiv2;
|
||||
if (frac)
|
||||
*frac = _frac;
|
||||
if (dsmpd)
|
||||
*dsmpd = _dsmpd;
|
||||
|
||||
return (unsigned long)foutpostdiv;
|
||||
}
|
||||
|
||||
static long
|
||||
clk_regmap_pll_round_rate(struct clk_hw *hw, unsigned long drate,
|
||||
unsigned long *prate)
|
||||
{
|
||||
struct clk_regmap_pll *pll = to_clk_regmap_pll(hw);
|
||||
long rate;
|
||||
|
||||
rate = clk_pll_round_rate(*prate, drate, NULL, NULL, NULL, NULL,
|
||||
NULL, NULL);
|
||||
|
||||
dev_dbg(pll->dev, "%s: prate=%ld, drate=%ld, rate=%ld\n",
|
||||
clk_hw_get_name(hw), *prate, drate, rate);
|
||||
|
||||
return rate;
|
||||
}
|
||||
|
||||
static int
|
||||
clk_regmap_pll_set_rate(struct clk_hw *hw, unsigned long drate,
|
||||
unsigned long prate)
|
||||
{
|
||||
struct clk_regmap_pll *pll = to_clk_regmap_pll(hw);
|
||||
u8 refdiv, postdiv1, postdiv2, dsmpd;
|
||||
u16 fbdiv;
|
||||
u32 frac;
|
||||
u32 v;
|
||||
int ret;
|
||||
|
||||
ret = clk_pll_round_rate(prate, drate, &refdiv, &fbdiv, &postdiv1,
|
||||
&postdiv2, &frac, &dsmpd);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* When changing PLL setting, we must force PLL into power down mode. */
|
||||
regmap_write(pll->regmap, pll->reg + PLLCON_OFFSET(1), PLL_POWER_DOWN);
|
||||
|
||||
regmap_write(pll->regmap, pll->reg + PLLCON_OFFSET(0),
|
||||
PLL_POSTDIV1(postdiv1) | PLL_FBDIV(fbdiv));
|
||||
regmap_write(pll->regmap, pll->reg + PLLCON_OFFSET(1),
|
||||
PLL_DSMPD(dsmpd) | PLL_POSTDIV2(postdiv2) |
|
||||
PLL_REFDIV(refdiv));
|
||||
regmap_write(pll->regmap, pll->reg + PLLCON_OFFSET(2), PLL_FRAC(frac));
|
||||
|
||||
regmap_write(pll->regmap, pll->reg + PLLCON_OFFSET(1), PLL_POWER_UP);
|
||||
|
||||
ret = regmap_read_poll_timeout(pll->regmap,
|
||||
pll->reg + PLLCON_OFFSET(1),
|
||||
v, v & PLL_LOCK, 50, 50000);
|
||||
if (ret)
|
||||
dev_err(pll->dev, "PLL is not lock\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int clk_regmap_pll_prepare(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_regmap_pll *pll = to_clk_regmap_pll(hw);
|
||||
u32 v;
|
||||
int ret;
|
||||
|
||||
regmap_write(pll->regmap, pll->reg + PLLCON_OFFSET(1), PLL_POWER_UP);
|
||||
|
||||
ret = regmap_read_poll_timeout(pll->regmap,
|
||||
pll->reg + PLLCON_OFFSET(1),
|
||||
v, v & PLL_LOCK, 50, 50000);
|
||||
if (ret)
|
||||
dev_err(pll->dev, "PLL is not lock\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void clk_regmap_pll_unprepare(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_regmap_pll *pll = to_clk_regmap_pll(hw);
|
||||
|
||||
regmap_write(pll->regmap, pll->reg + PLLCON_OFFSET(1), PLL_POWER_DOWN);
|
||||
}
|
||||
|
||||
static int clk_regmap_pll_is_prepared(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_regmap_pll *pll = to_clk_regmap_pll(hw);
|
||||
unsigned int con1;
|
||||
|
||||
regmap_read(pll->regmap, pll->reg + PLLCON_OFFSET(1), &con1);
|
||||
|
||||
return !(con1 & PLL_POWER_DOWN);
|
||||
}
|
||||
|
||||
static const struct clk_ops clk_regmap_pll_ops = {
|
||||
.recalc_rate = clk_regmap_pll_recalc_rate,
|
||||
.round_rate = clk_regmap_pll_round_rate,
|
||||
.set_rate = clk_regmap_pll_set_rate,
|
||||
.prepare = clk_regmap_pll_prepare,
|
||||
.unprepare = clk_regmap_pll_unprepare,
|
||||
.is_prepared = clk_regmap_pll_is_prepared,
|
||||
};
|
||||
|
||||
struct clk *
|
||||
devm_clk_regmap_register_pll(struct device *dev, const char *name,
|
||||
const char *parent_name,
|
||||
struct regmap *regmap, u32 reg,
|
||||
unsigned long flags)
|
||||
{
|
||||
struct clk_regmap_pll *pll;
|
||||
struct clk_init_data init;
|
||||
|
||||
pll = devm_kzalloc(dev, sizeof(*pll), GFP_KERNEL);
|
||||
if (!pll)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
init.name = name;
|
||||
init.ops = &clk_regmap_pll_ops;
|
||||
init.flags = flags;
|
||||
init.parent_names = (parent_name ? &parent_name : NULL);
|
||||
init.num_parents = (parent_name ? 1 : 0);
|
||||
|
||||
pll->dev = dev;
|
||||
pll->regmap = regmap;
|
||||
pll->reg = reg;
|
||||
pll->hw.init = &init;
|
||||
|
||||
return devm_clk_register(dev, &pll->hw);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_clk_regmap_register_pll);
|
||||
92
drivers/clk/rockchip/rk618/clk-regmap.h
Normal file
92
drivers/clk/rockchip/rk618/clk-regmap.h
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Rockchip Electronics Co. Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef __CLK_REGMAP_H__
|
||||
#define __CLK_REGMAP_H__
|
||||
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#define UPDATE(x, h, l) (((x) << (l)) & GENMASK((h), (l)))
|
||||
#define HIWORD_UPDATE(v, h, l) (((v) << (l)) | (GENMASK((h), (l)) << 16))
|
||||
|
||||
struct clk_regmap_divider {
|
||||
struct clk_hw hw;
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
u32 reg;
|
||||
u8 shift;
|
||||
u8 width;
|
||||
};
|
||||
|
||||
struct clk_regmap_gate {
|
||||
struct clk_hw hw;
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
u32 reg;
|
||||
u8 shift;
|
||||
};
|
||||
|
||||
struct clk_regmap_mux {
|
||||
struct clk_hw hw;
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
u32 reg;
|
||||
u32 mask;
|
||||
u8 shift;
|
||||
};
|
||||
|
||||
extern const struct clk_ops clk_regmap_mux_ops;
|
||||
extern const struct clk_ops clk_regmap_divider_ops;
|
||||
extern const struct clk_ops clk_regmap_gate_ops;
|
||||
|
||||
struct clk *
|
||||
devm_clk_regmap_register_pll(struct device *dev, const char *name,
|
||||
const char *parent_name,
|
||||
struct regmap *regmap, u32 reg,
|
||||
unsigned long flags);
|
||||
|
||||
struct clk *
|
||||
devm_clk_regmap_register_mux(struct device *dev, const char *name,
|
||||
const char * const *parent_names, u8 num_parents,
|
||||
struct regmap *regmap, u32 reg, u8 shift, u8 width,
|
||||
unsigned long flags);
|
||||
|
||||
struct clk *
|
||||
devm_clk_regmap_register_divider(struct device *dev, const char *name,
|
||||
const char *parent_name, struct regmap *regmap,
|
||||
u32 reg, u8 shift, u8 width,
|
||||
unsigned long flags);
|
||||
|
||||
struct clk *
|
||||
devm_clk_regmap_register_gate(struct device *dev, const char *name,
|
||||
const char *parent_name,
|
||||
struct regmap *regmap, u32 reg, u8 shift,
|
||||
unsigned long flags);
|
||||
|
||||
struct clk *
|
||||
devm_clk_regmap_register_composite(struct device *dev, const char *name,
|
||||
const char *const *parent_names,
|
||||
u8 num_parents, struct regmap *regmap,
|
||||
u32 mux_reg, u8 mux_shift, u8 mux_width,
|
||||
u32 div_reg, u8 div_shift, u8 div_width,
|
||||
u32 gate_reg, u8 gate_shift,
|
||||
unsigned long flags);
|
||||
|
||||
#endif
|
||||
532
drivers/clk/rockchip/rk618/clk-rk618.c
Normal file
532
drivers/clk/rockchip/rk618/clk-rk618.c
Normal file
@@ -0,0 +1,532 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Rockchip Electronics Co. Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/mfd/rk618.h>
|
||||
|
||||
#include "clk-regmap.h"
|
||||
|
||||
#define RK618_CRU_CLKSEL0 0x0058
|
||||
#define RK618_CRU_CLKSEL1 0x005c
|
||||
#define RK618_CRU_CLKSEL2 0x0060
|
||||
#define RK618_CRU_CLKSEL3 0x0064
|
||||
#define RK618_CRU_PLL0_CON0 0x0068
|
||||
#define RK618_CRU_PLL0_CON1 0x006c
|
||||
#define RK618_CRU_PLL0_CON2 0x0070
|
||||
#define RK618_CRU_PLL1_CON0 0x0074
|
||||
#define RK618_CRU_PLL1_CON1 0x0078
|
||||
#define RK618_CRU_PLL1_CON2 0x007c
|
||||
|
||||
struct clk_pll_data {
|
||||
unsigned int id;
|
||||
const char *name;
|
||||
const char *parent_name;
|
||||
u32 reg;
|
||||
unsigned long flags;
|
||||
};
|
||||
|
||||
#define PLL(_id, _name, _parent_name, _reg, _flags) \
|
||||
{ \
|
||||
.id = _id, \
|
||||
.name = _name, \
|
||||
.parent_name = _parent_name, \
|
||||
.reg = _reg, \
|
||||
.flags = _flags, \
|
||||
}
|
||||
|
||||
struct clk_mux_data {
|
||||
unsigned int id;
|
||||
const char *name;
|
||||
const char *const *parent_names;
|
||||
u8 num_parents;
|
||||
u32 reg;
|
||||
u8 shift;
|
||||
u8 width;
|
||||
unsigned long flags;
|
||||
};
|
||||
|
||||
#define MUX(_id, _name, _parent_names, _reg, _shift, _width, _flags) \
|
||||
{ \
|
||||
.id = _id, \
|
||||
.name = _name, \
|
||||
.parent_names = _parent_names, \
|
||||
.num_parents = ARRAY_SIZE(_parent_names), \
|
||||
.reg = _reg, \
|
||||
.shift = _shift, \
|
||||
.width = _width, \
|
||||
.flags = _flags, \
|
||||
}
|
||||
|
||||
struct clk_gate_data {
|
||||
unsigned int id;
|
||||
const char *name;
|
||||
const char *parent_name;
|
||||
u32 reg;
|
||||
u8 shift;
|
||||
unsigned long flags;
|
||||
};
|
||||
|
||||
#define GATE(_id, _name, _parent_name, _reg, _shift, _flags) \
|
||||
{ \
|
||||
.id = _id, \
|
||||
.name = _name, \
|
||||
.parent_name = _parent_name, \
|
||||
.reg = _reg, \
|
||||
.shift = _shift, \
|
||||
.flags = _flags, \
|
||||
}
|
||||
|
||||
struct clk_divider_data {
|
||||
unsigned int id;
|
||||
const char *name;
|
||||
const char *parent_name;
|
||||
u32 reg;
|
||||
u8 shift;
|
||||
u8 width;
|
||||
unsigned long flags;
|
||||
};
|
||||
|
||||
#define DIV(_id, _name, _parent_name, _reg, _shift, _width, _flags) \
|
||||
{ \
|
||||
.id = _id, \
|
||||
.name = _name, \
|
||||
.parent_name = _parent_name, \
|
||||
.reg = _reg, \
|
||||
.shift = _shift, \
|
||||
.width = _width, \
|
||||
.flags = _flags, \
|
||||
}
|
||||
|
||||
struct clk_composite_data {
|
||||
unsigned int id;
|
||||
const char *name;
|
||||
const char *const *parent_names;
|
||||
u8 num_parents;
|
||||
u32 mux_reg;
|
||||
u8 mux_shift;
|
||||
u8 mux_width;
|
||||
u32 div_reg;
|
||||
u8 div_shift;
|
||||
u8 div_width;
|
||||
u32 gate_reg;
|
||||
u8 gate_shift;
|
||||
unsigned long flags;
|
||||
};
|
||||
|
||||
#define COMPOSITE(_id, _name, _parent_names, \
|
||||
_mux_reg, _mux_shift, _mux_width, \
|
||||
_div_reg, _div_shift, _div_width, \
|
||||
_gate_reg, _gate_shift, _flags) \
|
||||
{ \
|
||||
.id = _id, \
|
||||
.name = _name, \
|
||||
.parent_names = _parent_names, \
|
||||
.num_parents = ARRAY_SIZE(_parent_names), \
|
||||
.mux_reg = _mux_reg, \
|
||||
.mux_shift = _mux_shift, \
|
||||
.mux_width = _mux_width, \
|
||||
.div_reg = _div_reg, \
|
||||
.div_shift = _div_shift, \
|
||||
.div_width = _div_width, \
|
||||
.gate_reg = _gate_reg, \
|
||||
.gate_shift = _gate_shift, \
|
||||
.flags = _flags, \
|
||||
}
|
||||
|
||||
#define COMPOSITE_NODIV(_id, _name, _parent_names, \
|
||||
_mux_reg, _mux_shift, _mux_width, \
|
||||
_gate_reg, _gate_shift, _flags) \
|
||||
{ \
|
||||
.id = _id, \
|
||||
.name = _name, \
|
||||
.parent_names = _parent_names, \
|
||||
.num_parents = ARRAY_SIZE(_parent_names), \
|
||||
.mux_reg = _mux_reg, \
|
||||
.mux_shift = _mux_shift, \
|
||||
.mux_width = _mux_width, \
|
||||
.gate_reg = _gate_reg, \
|
||||
.gate_shift = _gate_shift, \
|
||||
.flags = _flags, \
|
||||
}
|
||||
|
||||
enum {
|
||||
LCDC0_CLK = 1,
|
||||
LCDC1_CLK,
|
||||
VIF_PLLIN_CLK,
|
||||
SCALER_PLLIN_CLK,
|
||||
VIF_PLL_CLK,
|
||||
SCALER_PLL_CLK,
|
||||
VIF0_CLK,
|
||||
VIF1_CLK,
|
||||
SCALER_IN_CLK,
|
||||
SCALER_CLK,
|
||||
DITHER_CLK,
|
||||
HDMI_CLK,
|
||||
MIPI_CLK,
|
||||
LVDS_CLK,
|
||||
LVTTL_CLK,
|
||||
RGB_CLK,
|
||||
VIF0_PRE_CLK,
|
||||
VIF1_PRE_CLK,
|
||||
CODEC_CLK,
|
||||
NR_CLKS,
|
||||
};
|
||||
|
||||
struct rk618_cru {
|
||||
struct device *dev;
|
||||
struct rk618 *parent;
|
||||
struct regmap *regmap;
|
||||
|
||||
struct clk_onecell_data clk_data;
|
||||
};
|
||||
|
||||
static char clkin_name[16] = "dummy";
|
||||
static char lcdc0_dclkp_name[16] = "dummy";
|
||||
static char lcdc1_dclkp_name[16] = "dummy";
|
||||
|
||||
#define PNAME(x) static const char *const x[]
|
||||
|
||||
PNAME(mux_pll_in_p) = { "lcdc0_clk", "lcdc1_clk", clkin_name };
|
||||
PNAME(mux_pll_src_p) = { "vif_pll_clk", "scaler_pll_clk", };
|
||||
PNAME(mux_scaler_in_src_p) = { "vif0_clk", "vif1_clk" };
|
||||
PNAME(mux_hdmi_src_p) = { "vif1_clk", "scaler_clk", "vif0_clk" };
|
||||
PNAME(mux_dither_src_p) = { "vif0_clk", "scaler_clk" };
|
||||
PNAME(mux_vif0_src_p) = { "vif0_pre_clk", lcdc0_dclkp_name };
|
||||
PNAME(mux_vif1_src_p) = { "vif1_pre_clk", lcdc1_dclkp_name };
|
||||
PNAME(mux_codec_src_p) = { "vif_pll_clk", "scaler_pll_clk", clkin_name };
|
||||
|
||||
/* Two PLL, one for dual datarate input logic, the other for scaler */
|
||||
static const struct clk_pll_data rk618_clk_plls[] = {
|
||||
PLL(VIF_PLL_CLK, "vif_pll_clk", "vif_pllin_clk",
|
||||
RK618_CRU_PLL0_CON0,
|
||||
0),
|
||||
PLL(SCALER_PLL_CLK, "scaler_pll_clk", "scaler_pllin_clk",
|
||||
RK618_CRU_PLL1_CON0,
|
||||
0),
|
||||
};
|
||||
|
||||
static const struct clk_mux_data rk618_clk_muxes[] = {
|
||||
MUX(VIF_PLLIN_CLK, "vif_pllin_clk", mux_pll_in_p,
|
||||
RK618_CRU_CLKSEL0, 6, 2,
|
||||
0),
|
||||
MUX(SCALER_PLLIN_CLK, "scaler_pllin_clk", mux_pll_in_p,
|
||||
RK618_CRU_CLKSEL0, 8, 2,
|
||||
0),
|
||||
MUX(SCALER_IN_CLK, "scaler_in_clk", mux_scaler_in_src_p,
|
||||
RK618_CRU_CLKSEL3, 15, 1,
|
||||
0),
|
||||
MUX(DITHER_CLK, "dither_clk", mux_dither_src_p,
|
||||
RK618_CRU_CLKSEL3, 14, 1,
|
||||
0),
|
||||
MUX(VIF0_CLK, "vif0_clk", mux_vif0_src_p,
|
||||
RK618_CRU_CLKSEL3, 1, 1,
|
||||
CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT),
|
||||
MUX(VIF1_CLK, "vif1_clk", mux_vif1_src_p,
|
||||
RK618_CRU_CLKSEL3, 7, 1,
|
||||
CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT),
|
||||
};
|
||||
|
||||
static const struct clk_divider_data rk618_clk_dividers[] = {
|
||||
DIV(LCDC0_CLK, "lcdc0_clk", lcdc0_dclkp_name,
|
||||
RK618_CRU_CLKSEL0, 0, 3,
|
||||
0),
|
||||
DIV(LCDC1_CLK, "lcdc1_clk", lcdc1_dclkp_name,
|
||||
RK618_CRU_CLKSEL0, 3, 3,
|
||||
0),
|
||||
};
|
||||
|
||||
static const struct clk_gate_data rk618_clk_gates[] = {
|
||||
GATE(MIPI_CLK, "mipi_clk", "dither_clk",
|
||||
RK618_CRU_CLKSEL1, 10,
|
||||
0),
|
||||
GATE(LVDS_CLK, "lvds_clk", "dither_clk",
|
||||
RK618_CRU_CLKSEL1, 9,
|
||||
0),
|
||||
GATE(LVTTL_CLK, "lvttl_clk", "dither_clk",
|
||||
RK618_CRU_CLKSEL1, 12,
|
||||
0),
|
||||
GATE(RGB_CLK, "rgb_clk", "dither_clk",
|
||||
RK618_CRU_CLKSEL1, 11,
|
||||
0),
|
||||
};
|
||||
|
||||
static const struct clk_composite_data rk618_clk_composites[] = {
|
||||
COMPOSITE(SCALER_CLK, "scaler_clk", mux_pll_src_p,
|
||||
RK618_CRU_CLKSEL1, 3, 1,
|
||||
RK618_CRU_CLKSEL1, 5, 3,
|
||||
RK618_CRU_CLKSEL1, 4,
|
||||
CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT),
|
||||
COMPOSITE_NODIV(HDMI_CLK, "hdmi_clk", mux_hdmi_src_p,
|
||||
RK618_CRU_CLKSEL3, 12, 2,
|
||||
RK618_CRU_CLKSEL1, 8,
|
||||
0),
|
||||
COMPOSITE(VIF0_PRE_CLK, "vif0_pre_clk", mux_pll_src_p,
|
||||
RK618_CRU_CLKSEL3, 0, 1,
|
||||
RK618_CRU_CLKSEL3, 3, 3,
|
||||
RK618_CRU_CLKSEL3, 2,
|
||||
CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT),
|
||||
COMPOSITE(VIF1_PRE_CLK, "vif1_pre_clk", mux_pll_src_p,
|
||||
RK618_CRU_CLKSEL3, 6, 1,
|
||||
RK618_CRU_CLKSEL3, 9, 3,
|
||||
RK618_CRU_CLKSEL3, 8,
|
||||
CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT),
|
||||
COMPOSITE_NODIV(CODEC_CLK, "codec_clk", mux_codec_src_p,
|
||||
RK618_CRU_CLKSEL1, 0, 2,
|
||||
RK618_CRU_CLKSEL1, 2,
|
||||
0),
|
||||
};
|
||||
|
||||
static void rk618_clk_add_lookup(struct rk618_cru *cru, struct clk *clk,
|
||||
unsigned int id)
|
||||
{
|
||||
if (cru->clk_data.clks && id)
|
||||
cru->clk_data.clks[id] = clk;
|
||||
}
|
||||
|
||||
static void rk618_clk_register_muxes(struct rk618_cru *cru)
|
||||
{
|
||||
struct clk *clk;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(rk618_clk_muxes); i++) {
|
||||
const struct clk_mux_data *data = &rk618_clk_muxes[i];
|
||||
|
||||
clk = devm_clk_regmap_register_mux(cru->dev, data->name,
|
||||
data->parent_names,
|
||||
data->num_parents,
|
||||
cru->regmap, data->reg,
|
||||
data->shift, data->width,
|
||||
data->flags);
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(cru->dev, "failed to register clock %s\n",
|
||||
data->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
rk618_clk_add_lookup(cru, clk, data->id);
|
||||
}
|
||||
}
|
||||
|
||||
static void rk618_clk_register_dividers(struct rk618_cru *cru)
|
||||
{
|
||||
struct clk *clk;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(rk618_clk_dividers); i++) {
|
||||
const struct clk_divider_data *data = &rk618_clk_dividers[i];
|
||||
|
||||
clk = devm_clk_regmap_register_divider(cru->dev, data->name,
|
||||
data->parent_name,
|
||||
cru->regmap, data->reg,
|
||||
data->shift, data->width,
|
||||
data->flags);
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(cru->dev, "failed to register clock %s\n",
|
||||
data->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
rk618_clk_add_lookup(cru, clk, data->id);
|
||||
}
|
||||
}
|
||||
|
||||
static void rk618_clk_register_gates(struct rk618_cru *cru)
|
||||
{
|
||||
struct clk *clk;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(rk618_clk_gates); i++) {
|
||||
const struct clk_gate_data *data = &rk618_clk_gates[i];
|
||||
|
||||
clk = devm_clk_regmap_register_gate(cru->dev, data->name,
|
||||
data->parent_name,
|
||||
cru->regmap,
|
||||
data->reg, data->shift,
|
||||
data->flags);
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(cru->dev, "failed to register clock %s\n",
|
||||
data->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
rk618_clk_add_lookup(cru, clk, data->id);
|
||||
}
|
||||
}
|
||||
|
||||
static void rk618_clk_register_composites(struct rk618_cru *cru)
|
||||
{
|
||||
struct clk *clk;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(rk618_clk_composites); i++) {
|
||||
const struct clk_composite_data *data =
|
||||
&rk618_clk_composites[i];
|
||||
|
||||
clk = devm_clk_regmap_register_composite(cru->dev, data->name,
|
||||
data->parent_names,
|
||||
data->num_parents,
|
||||
cru->regmap,
|
||||
data->mux_reg,
|
||||
data->mux_shift,
|
||||
data->mux_width,
|
||||
data->div_reg,
|
||||
data->div_shift,
|
||||
data->div_width,
|
||||
data->gate_reg,
|
||||
data->gate_shift,
|
||||
data->flags);
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(cru->dev, "failed to register clock %s\n",
|
||||
data->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
rk618_clk_add_lookup(cru, clk, data->id);
|
||||
}
|
||||
}
|
||||
|
||||
static void rk618_clk_register_plls(struct rk618_cru *cru)
|
||||
{
|
||||
struct clk *clk;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(rk618_clk_plls); i++) {
|
||||
const struct clk_pll_data *data = &rk618_clk_plls[i];
|
||||
|
||||
clk = devm_clk_regmap_register_pll(cru->dev, data->name,
|
||||
data->parent_name,
|
||||
cru->regmap, data->reg,
|
||||
data->flags);
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(cru->dev, "failed to register clock %s\n",
|
||||
data->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
rk618_clk_add_lookup(cru, clk, data->id);
|
||||
}
|
||||
}
|
||||
|
||||
static int rk618_cru_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct rk618 *rk618 = dev_get_drvdata(pdev->dev.parent);
|
||||
struct device *dev = &pdev->dev;
|
||||
struct rk618_cru *cru;
|
||||
struct clk **clk_table;
|
||||
const char *parent_name;
|
||||
struct clk *clk;
|
||||
int ret, i;
|
||||
|
||||
if (!of_device_is_available(dev->of_node))
|
||||
return -ENODEV;
|
||||
|
||||
cru = devm_kzalloc(dev, sizeof(*cru), GFP_KERNEL);
|
||||
if (!cru)
|
||||
return -ENOMEM;
|
||||
|
||||
clk_table = devm_kcalloc(dev, NR_CLKS, sizeof(struct clk *),
|
||||
GFP_KERNEL);
|
||||
if (!clk_table)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < NR_CLKS; i++)
|
||||
clk_table[i] = ERR_PTR(-ENOENT);
|
||||
|
||||
cru->dev = dev;
|
||||
cru->parent = rk618;
|
||||
cru->regmap = rk618->regmap;
|
||||
cru->clk_data.clks = clk_table;
|
||||
cru->clk_data.clk_num = NR_CLKS;
|
||||
platform_set_drvdata(pdev, cru);
|
||||
|
||||
clk = devm_clk_get(dev, "clkin");
|
||||
if (IS_ERR(clk)) {
|
||||
ret = PTR_ERR(clk);
|
||||
dev_err(dev, "failed to get clkin: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
strlcpy(clkin_name, __clk_get_name(clk), sizeof(clkin_name));
|
||||
|
||||
clk = devm_clk_get(dev, "lcdc0_dclkp");
|
||||
if (IS_ERR(clk)) {
|
||||
if (PTR_ERR(clk) != -ENOENT) {
|
||||
ret = PTR_ERR(clk);
|
||||
dev_err(dev, "failed to get lcdc0_dclkp: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
clk = NULL;
|
||||
}
|
||||
|
||||
parent_name = __clk_get_name(clk);
|
||||
if (parent_name)
|
||||
strlcpy(lcdc0_dclkp_name, parent_name,
|
||||
sizeof(lcdc0_dclkp_name));
|
||||
|
||||
clk = devm_clk_get(dev, "lcdc1_dclkp");
|
||||
if (IS_ERR(clk)) {
|
||||
if (PTR_ERR(clk) != -ENOENT) {
|
||||
ret = PTR_ERR(clk);
|
||||
dev_err(dev, "failed to get lcdc1_dclkp: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
clk = NULL;
|
||||
}
|
||||
|
||||
parent_name = __clk_get_name(clk);
|
||||
if (parent_name)
|
||||
strlcpy(lcdc1_dclkp_name, parent_name,
|
||||
sizeof(lcdc1_dclkp_name));
|
||||
|
||||
rk618_clk_register_plls(cru);
|
||||
rk618_clk_register_muxes(cru);
|
||||
rk618_clk_register_dividers(cru);
|
||||
rk618_clk_register_gates(cru);
|
||||
rk618_clk_register_composites(cru);
|
||||
|
||||
return of_clk_add_provider(dev->of_node, of_clk_src_onecell_get,
|
||||
&cru->clk_data);
|
||||
}
|
||||
|
||||
static int rk618_cru_remove(struct platform_device *pdev)
|
||||
{
|
||||
of_clk_del_provider(pdev->dev.of_node);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id rk618_cru_of_match[] = {
|
||||
{ .compatible = "rockchip,rk618-cru", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rk618_cru_of_match);
|
||||
|
||||
static struct platform_driver rk618_cru_driver = {
|
||||
.driver = {
|
||||
.name = "rk618-cru",
|
||||
.of_match_table = of_match_ptr(rk618_cru_of_match),
|
||||
},
|
||||
.probe = rk618_cru_probe,
|
||||
.remove = rk618_cru_remove,
|
||||
};
|
||||
module_platform_driver(rk618_cru_driver);
|
||||
|
||||
MODULE_AUTHOR("Wyon Bi <bivvy.bi@rock-chips.com>");
|
||||
MODULE_DESCRIPTION("Rockchip rk618 CRU driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
38
include/dt-bindings/clock/rk618-cru.h
Normal file
38
include/dt-bindings/clock/rk618-cru.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Rockchip Electronics Co. Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef _DT_BINDINGS_CLK_RK618_CRU_H
|
||||
#define _DT_BINDINGS_CLK_RK618_CRU_H
|
||||
|
||||
#define LCDC0_CLK 1
|
||||
#define LCDC1_CLK 2
|
||||
#define VIF_PLLIN_CLK 3
|
||||
#define SCALER_PLLIN_CLK 4
|
||||
#define VIF_PLL_CLK 5
|
||||
#define SCALER_PLL_CLK 6
|
||||
#define VIF0_CLK 7
|
||||
#define VIF1_CLK 8
|
||||
#define SCALER_IN_CLK 9
|
||||
#define SCALER_CLK 10
|
||||
#define DITHER_CLK 11
|
||||
#define HDMI_CLK 12
|
||||
#define MIPI_CLK 13
|
||||
#define LVDS_CLK 14
|
||||
#define LVTTL_CLK 15
|
||||
#define RGB_CLK 16
|
||||
#define VIF0_PRE_CLK 17
|
||||
#define VIF1_PRE_CLK 18
|
||||
#define CODEC_CLK 19
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user