Commit 3f8e7e72 authored by Stephen Boyd's avatar Stephen Boyd

Merge branches 'clk-optional', 'clk-devm-clkdev-register', 'clk-allwinner',...

Merge branches 'clk-optional', 'clk-devm-clkdev-register', 'clk-allwinner', 'clk-meson' and 'clk-renesas' into clk-next

 - Add a {devm_}clk_get_optional() API
 - Add devm_clk_hw_register_clkdev() API to manage clkdev lookups

* clk-optional:
  clk: Add (devm_)clk_get_optional() functions
  clk: Add comment about __of_clk_get_by_name() error values

* clk-devm-clkdev-register:
  clk: clk-st: avoid clkdev lookup leak at remove
  clk: clk-max77686: Clean clkdev lookup leak and use devm
  clkdev: add managed clkdev lookup registration

* clk-allwinner:
  clk: sunxi-ng: sun8i-a23: Enable PLL-MIPI LDOs when ungating it

* clk-meson: (22 commits)
  clk: meson: meson8b: fix the naming of the APB clocks
  dt-bindings: clock: meson8b: add APB clock definition
  clk: meson: Add G12A AO Clock + Reset Controller
  dt-bindings: clk: add G12A AO Clock and Reset Bindings
  clk: meson: factorise meson64 peripheral clock controller drivers
  clk: meson: g12a: add peripheral clock controller
  dt-bindings: clk: meson: add g12a periph clock controller bindings
  clk: meson: pll: update driver for the g12a
  clk: meson: rework and clean drivers dependencies
  clk: meson: axg-audio does not require syscon
  clk: meson: use CONFIG_ARCH_MESON to enter meson clk directory
  clk: export some clk_hw function symbols for module drivers
  clk: meson: ao-clkc: claim clock controller input clocks from DT
  clk: meson: axg: claim clock controller input clock from DT
  clk: meson: gxbb: claim clock controller input clock from DT
  clk: meson: meson8b: add the GPU clock tree
  clk: meson: meson8b: use a separate clock table for Meson8
  clk: meson: axg-ao: add 32k generation subtree
  clk: meson: gxbb-ao: replace cec-32k with the dual divider
  clk: meson: add dual divider clock driver
  ...

* clk-renesas:
  clk: renesas: r8a774a1: Fix LAST_DT_CORE_CLK
  clk: renesas: r8a774c0: Fix LAST_DT_CORE_CLK
  clk: renesas: r8a774c0: Add TMU clock
  clk: renesas: r8a77980: Add RPC clocks
  clk: renesas: rcar-gen3: Add RPC clocks
  clk: renesas: rcar-gen3: Add spinlock
  clk: renesas: rcar-gen3: Factor out cpg_reg_modify()
  clk: renesas: r8a774c0: Correct parent clock of DU
  clk: renesas: r8a774a1: Add missing CANFD clock
  clk: renesas: r8a774c0: Add missing CANFD clock
......@@ -10,6 +10,7 @@ Required Properties:
- GXL (S905X, S905D) : "amlogic,meson-gxl-aoclkc"
- GXM (S912) : "amlogic,meson-gxm-aoclkc"
- AXG (A113D, A113X) : "amlogic,meson-axg-aoclkc"
- G12A (S905X2, S905D2, S905Y2) : "amlogic,meson-g12a-aoclkc"
followed by the common "amlogic,meson-gx-aoclkc"
- clocks: list of clock phandle, one for each entry clock-names.
- clock-names: should contain the following:
......
......@@ -9,6 +9,7 @@ Required Properties:
"amlogic,gxbb-clkc" for GXBB SoC,
"amlogic,gxl-clkc" for GXL and GXM SoC,
"amlogic,axg-clkc" for AXG SoC.
"amlogic,g12a-clkc" for G12A SoC.
- clocks : list of clock phandle, one for each entry clock-names.
- clock-names : should contain the following:
* "xtal": the platform xtal
......
......@@ -242,9 +242,11 @@ certainly invest a bit more effort into libata core layer).
CLOCK
devm_clk_get()
devm_clk_get_optional()
devm_clk_put()
devm_clk_hw_register()
devm_of_clk_add_hw_provider()
devm_clk_hw_register_clkdev()
DMA
dmaenginem_async_device_register()
......
......@@ -79,7 +79,7 @@ obj-$(CONFIG_ARCH_K3) += keystone/
obj-$(CONFIG_ARCH_KEYSTONE) += keystone/
obj-$(CONFIG_MACH_LOONGSON32) += loongson1/
obj-y += mediatek/
obj-$(CONFIG_COMMON_CLK_AMLOGIC) += meson/
obj-$(CONFIG_ARCH_MESON) += meson/
obj-$(CONFIG_MACH_PIC32) += microchip/
ifeq ($(CONFIG_COMMON_CLK), y)
obj-$(CONFIG_ARCH_MMP) += mmp/
......
......@@ -29,6 +29,17 @@ struct clk *devm_clk_get(struct device *dev, const char *id)
}
EXPORT_SYMBOL(devm_clk_get);
struct clk *devm_clk_get_optional(struct device *dev, const char *id)
{
struct clk *clk = devm_clk_get(dev, id);
if (clk == ERR_PTR(-ENOENT))
return NULL;
return clk;
}
EXPORT_SYMBOL(devm_clk_get_optional);
struct clk_bulk_devres {
struct clk_bulk_data *clks;
int num_clks;
......
......@@ -235,8 +235,9 @@ static int max77686_clk_probe(struct platform_device *pdev)
return ret;
}
ret = clk_hw_register_clkdev(&max_clk_data->hw,
max_clk_data->clk_idata.name, NULL);
ret = devm_clk_hw_register_clkdev(dev, &max_clk_data->hw,
max_clk_data->clk_idata.name,
NULL);
if (ret < 0) {
dev_err(dev, "Failed to clkdev register: %d\n", ret);
return ret;
......@@ -244,8 +245,8 @@ static int max77686_clk_probe(struct platform_device *pdev)
}
if (parent->of_node) {
ret = of_clk_add_hw_provider(parent->of_node, of_clk_max77686_get,
drv_data);
ret = devm_of_clk_add_hw_provider(dev, of_clk_max77686_get,
drv_data);
if (ret < 0) {
dev_err(dev, "Failed to register OF clock provider: %d\n",
......@@ -261,27 +262,11 @@ static int max77686_clk_probe(struct platform_device *pdev)
1 << MAX77802_CLOCK_LOW_JITTER_SHIFT);
if (ret < 0) {
dev_err(dev, "Failed to config low-jitter: %d\n", ret);
goto remove_of_clk_provider;
return ret;
}
}
return 0;
remove_of_clk_provider:
if (parent->of_node)
of_clk_del_provider(parent->of_node);
return ret;
}
static int max77686_clk_remove(struct platform_device *pdev)
{
struct device *parent = pdev->dev.parent;
if (parent->of_node)
of_clk_del_provider(parent->of_node);
return 0;
}
static const struct platform_device_id max77686_clk_id[] = {
......@@ -297,7 +282,6 @@ static struct platform_driver max77686_clk_driver = {
.name = "max77686-clk",
},
.probe = max77686_clk_probe,
.remove = max77686_clk_remove,
.id_table = max77686_clk_id,
};
......
......@@ -394,16 +394,19 @@ bool clk_hw_is_prepared(const struct clk_hw *hw)
{
return clk_core_is_prepared(hw->core);
}
EXPORT_SYMBOL_GPL(clk_hw_is_prepared);
bool clk_hw_rate_is_protected(const struct clk_hw *hw)
{
return clk_core_rate_is_protected(hw->core);
}
EXPORT_SYMBOL_GPL(clk_hw_rate_is_protected);
bool clk_hw_is_enabled(const struct clk_hw *hw)
{
return clk_core_is_enabled(hw->core);
}
EXPORT_SYMBOL_GPL(clk_hw_is_enabled);
bool __clk_is_enabled(struct clk *clk)
{
......
......@@ -52,6 +52,12 @@ struct clk *of_clk_get(struct device_node *np, int index)
}
EXPORT_SYMBOL(of_clk_get);
/*
* Beware the return values when np is valid, but no clock provider is found.
* If name == NULL, the function returns -ENOENT.
* If name != NULL, the function returns -EINVAL. This is because __of_clk_get()
* is called even if of_property_match_string() returns an error.
*/
static struct clk *__of_clk_get_by_name(struct device_node *np,
const char *dev_id,
const char *name)
......@@ -401,6 +407,23 @@ static struct clk_lookup *__clk_register_clkdev(struct clk_hw *hw,
return cl;
}
static int do_clk_register_clkdev(struct clk_hw *hw,
struct clk_lookup **cl, const char *con_id, const char *dev_id)
{
if (IS_ERR(hw))
return PTR_ERR(hw);
/*
* Since dev_id can be NULL, and NULL is handled specially, we must
* pass it as either a NULL format string, or with "%s".
*/
if (dev_id)
*cl = __clk_register_clkdev(hw, con_id, "%s", dev_id);
else
*cl = __clk_register_clkdev(hw, con_id, NULL);
return *cl ? 0 : -ENOMEM;
}
/**
* clk_register_clkdev - register one clock lookup for a struct clk
* @clk: struct clk to associate with all clk_lookups
......@@ -423,17 +446,8 @@ int clk_register_clkdev(struct clk *clk, const char *con_id,
if (IS_ERR(clk))
return PTR_ERR(clk);
/*
* Since dev_id can be NULL, and NULL is handled specially, we must
* pass it as either a NULL format string, or with "%s".
*/
if (dev_id)
cl = __clk_register_clkdev(__clk_get_hw(clk), con_id, "%s",
dev_id);
else
cl = __clk_register_clkdev(__clk_get_hw(clk), con_id, NULL);
return cl ? 0 : -ENOMEM;
return do_clk_register_clkdev(__clk_get_hw(clk), &cl, con_id,
dev_id);
}
EXPORT_SYMBOL(clk_register_clkdev);
......@@ -456,18 +470,75 @@ int clk_hw_register_clkdev(struct clk_hw *hw, const char *con_id,
{
struct clk_lookup *cl;
if (IS_ERR(hw))
return PTR_ERR(hw);
return do_clk_register_clkdev(hw, &cl, con_id, dev_id);
}
EXPORT_SYMBOL(clk_hw_register_clkdev);
/*
* Since dev_id can be NULL, and NULL is handled specially, we must
* pass it as either a NULL format string, or with "%s".
*/
if (dev_id)
cl = __clk_register_clkdev(hw, con_id, "%s", dev_id);
else
cl = __clk_register_clkdev(hw, con_id, NULL);
static void devm_clkdev_release(struct device *dev, void *res)
{
clkdev_drop(*(struct clk_lookup **)res);
}
static int devm_clk_match_clkdev(struct device *dev, void *res, void *data)
{
struct clk_lookup **l = res;
return cl ? 0 : -ENOMEM;
return *l == data;
}
EXPORT_SYMBOL(clk_hw_register_clkdev);
/**
* devm_clk_release_clkdev - Resource managed clkdev lookup release
* @dev: device this lookup is bound
* @con_id: connection ID string on device
* @dev_id: format string describing device name
*
* Drop the clkdev lookup created with devm_clk_hw_register_clkdev.
* Normally this function will not need to be called and the resource
* management code will ensure that the resource is freed.
*/
void devm_clk_release_clkdev(struct device *dev, const char *con_id,
const char *dev_id)
{
struct clk_lookup *cl;
int rval;
cl = clk_find(dev_id, con_id);
WARN_ON(!cl);
rval = devres_release(dev, devm_clkdev_release,
devm_clk_match_clkdev, cl);
WARN_ON(rval);
}
EXPORT_SYMBOL(devm_clk_release_clkdev);
/**
* devm_clk_hw_register_clkdev - managed clk lookup registration for clk_hw
* @dev: device this lookup is bound
* @hw: struct clk_hw to associate with all clk_lookups
* @con_id: connection ID string on device
* @dev_id: format string describing device name
*
* con_id or dev_id may be NULL as a wildcard, just as in the rest of
* clkdev.
*
* To make things easier for mass registration, we detect error clk_hws
* from a previous clk_hw_register_*() call, and return the error code for
* those. This is to permit this function to be called immediately
* after clk_hw_register_*().
*/
int devm_clk_hw_register_clkdev(struct device *dev, struct clk_hw *hw,
const char *con_id, const char *dev_id)
{
int rval = -ENOMEM;
struct clk_lookup **cl;
cl = devres_alloc(devm_clkdev_release, sizeof(*cl), GFP_KERNEL);
if (cl) {
rval = do_clk_register_clkdev(hw, cl, con_id, dev_id);
if (!rval)
devres_add(dev, cl);
else
devres_free(cl);
}
return rval;
}
EXPORT_SYMBOL(devm_clk_hw_register_clkdev);
config COMMON_CLK_AMLOGIC
bool
depends on ARCH_MESON || COMPILE_TEST
select COMMON_CLK_REGMAP_MESON
config COMMON_CLK_MESON_INPUT
tristate
config COMMON_CLK_AMLOGIC_AUDIO
bool
depends on ARCH_MESON || COMPILE_TEST
select COMMON_CLK_AMLOGIC
config COMMON_CLK_MESON_REGMAP
tristate
select REGMAP
config COMMON_CLK_MESON_AO
bool
depends on OF
depends on ARCH_MESON || COMPILE_TEST
select COMMON_CLK_REGMAP_MESON
config COMMON_CLK_MESON_DUALDIV
tristate
select COMMON_CLK_MESON_REGMAP
config COMMON_CLK_MESON_MPLL
tristate
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_REGMAP_MESON
bool
select REGMAP
config COMMON_CLK_MESON_EE_CLKC
tristate
select COMMON_CLK_MESON_REGMAP
select COMMON_CLK_MESON_INPUT
config COMMON_CLK_MESON8B
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
help
Support for the clock controller on AmLogic S802 (Meson8),
......@@ -30,8 +55,14 @@ config COMMON_CLK_MESON8B
config COMMON_CLK_GXBB
bool
select COMMON_CLK_AMLOGIC
select COMMON_CLK_MESON_AO
depends on ARCH_MESON
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 COMMON_CLK_MESON_EE_CLKC
select MFD_SYSCON
help
Support for the clock controller on AmLogic S905 devices, aka gxbb.
......@@ -39,8 +70,13 @@ config COMMON_CLK_GXBB
config COMMON_CLK_AXG
bool
select COMMON_CLK_AMLOGIC
select COMMON_CLK_MESON_AO
depends on ARCH_MESON
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 COMMON_CLK_MESON_EE_CLKC
select MFD_SYSCON
help
Support for the clock controller on AmLogic A113D devices, aka axg.
......@@ -48,9 +84,26 @@ config COMMON_CLK_AXG
config COMMON_CLK_AXG_AUDIO
tristate "Meson AXG Audio Clock Controller Driver"
depends on COMMON_CLK_AXG
select COMMON_CLK_AMLOGIC_AUDIO
select MFD_SYSCON
depends on ARCH_MESON
select COMMON_CLK_MESON_INPUT
select COMMON_CLK_MESON_REGMAP
select COMMON_CLK_MESON_PHASE
select COMMON_CLK_MESON_SCLK_DIV
select REGMAP_MMIO
help
Support for the audio clock controller on AmLogic A113D devices,
aka axg, Say Y if you want audio subsystem to work.
config COMMON_CLK_G12A
bool
depends on ARCH_MESON
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 COMMON_CLK_MESON_EE_CLKC
select MFD_SYSCON
help
Support for the clock controller on Amlogic S905D2, S905X2 and S905Y2
devices, aka g12a. Say Y if you want peripherals to work.
#
# Makefile for Meson specific clk
#
# Amlogic clock drivers
obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-pll.o clk-mpll.o clk-phase.o vid-pll-div.o
obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-input.o
obj-$(CONFIG_COMMON_CLK_AMLOGIC_AUDIO) += clk-triphase.o sclk-div.o
obj-$(CONFIG_COMMON_CLK_MESON_AO) += meson-aoclk.o
obj-$(CONFIG_COMMON_CLK_MESON_AO_CLKC) += meson-aoclk.o
obj-$(CONFIG_COMMON_CLK_MESON_DUALDIV) += clk-dualdiv.o
obj-$(CONFIG_COMMON_CLK_MESON_EE_CLKC) += meson-eeclk.o
obj-$(CONFIG_COMMON_CLK_MESON_INPUT) += clk-input.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_G12A) += g12a.o g12a-aoclk.o
obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o
obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o gxbb-aoclk-32k.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,27 @@
#include <linux/platform_device.h>
#include <linux/reset-controller.h>
#include <linux/mfd/syscon.h>
#include "clk-regmap.h"
#include "meson-aoclk.h"
#include "axg-aoclk.h"
#include "clk-regmap.h"
#include "clk-dualdiv.h"
#define IN_PREFIX "ao-in-"
/*
* AO Configuration Clock registers offsets
* Register offsets from the data sheet must be multiplied by 4.
*/
#define AO_RTI_PWR_CNTL_REG1 0x0C
#define AO_RTI_PWR_CNTL_REG0 0x10
#define AO_RTI_GEN_CNTL_REG0 0x40
#define AO_OSCIN_CNTL 0x58
#define AO_CRT_CLK_CNTL1 0x68
#define AO_SAR_CLK 0x90
#define AO_RTC_ALT_CLK_CNTL0 0x94
#define AO_RTC_ALT_CLK_CNTL1 0x98
#define AXG_AO_GATE(_name, _bit) \
static struct clk_regmap axg_aoclk_##_name = { \
.data = &(struct clk_regmap_gate_data) { \
......@@ -25,7 +42,7 @@ static struct clk_regmap axg_aoclk_##_name = { \
.hw.init = &(struct clk_init_data) { \
.name = "axg_ao_" #_name, \
.ops = &clk_regmap_gate_ops, \
.parent_names = (const char *[]){ "clk81" }, \
.parent_names = (const char *[]){ IN_PREFIX "mpeg-clk" }, \
.num_parents = 1, \
.flags = CLK_IGNORE_UNUSED, \
}, \
......@@ -39,17 +56,141 @@ AXG_AO_GATE(uart2, 5);
AXG_AO_GATE(ir_blaster, 6);
AXG_AO_GATE(saradc, 7);
static struct clk_regmap axg_aoclk_cts_oscin = {
.data = &(struct clk_regmap_gate_data){
.offset = AO_RTI_PWR_CNTL_REG0,
.bit_idx = 14,
},
.hw.init = &(struct clk_init_data){
.name = "cts_oscin",
.ops = &clk_regmap_gate_ro_ops,
.parent_names = (const char *[]){ IN_PREFIX "xtal" },
.num_parents = 1,
},
};
static struct clk_regmap axg_aoclk_32k_pre = {
.data = &(struct clk_regmap_gate_data){
.offset = AO_RTC_ALT_CLK_CNTL0,
.bit_idx = 31,
},
.hw.init = &(struct clk_init_data){
.name = "axg_ao_32k_pre",
.ops = &clk_regmap_gate_ops,
.parent_names = (const char *[]){ "cts_oscin" },
.num_parents = 1,
},
};
static const struct meson_clk_dualdiv_param axg_32k_div_table[] = {
{
.dual = 1,
.n1 = 733,
.m1 = 8,
.n2 = 732,
.m2 = 11,
}, {}
};
static struct clk_regmap axg_aoclk_32k_div = {
.data = &(struct meson_clk_dualdiv_data){
.n1 = {
.reg_off = AO_RTC_ALT_CLK_CNTL0,
.shift = 0,
.width = 12,
},
.n2 = {
.reg_off = AO_RTC_ALT_CLK_CNTL0,
.shift = 12,
.width = 12,
},
.m1 = {
.reg_off = AO_RTC_ALT_CLK_CNTL1,
.shift = 0,
.width = 12,
},
.m2 = {
.reg_off = AO_RTC_ALT_CLK_CNTL1,
.shift = 12,
.width = 12,
},
.dual = {
.reg_off = AO_RTC_ALT_CLK_CNTL0,
.shift = 28,
.width = 1,
},
.table = axg_32k_div_table,
},
.hw.init = &(struct clk_init_data){
.name = "axg_ao_32k_div",
.ops = &meson_clk_dualdiv_ops,
.parent_names = (const char *[]){ "axg_ao_32k_pre" },
.num_parents = 1,
},
};
static struct clk_regmap axg_aoclk_32k_sel = {
.data = &(struct clk_regmap_mux_data) {
.offset = AO_RTC_ALT_CLK_CNTL1,
.mask = 0x1,
.shift = 24,
.flags = CLK_MUX_ROUND_CLOSEST,
},
.hw.init = &(struct clk_init_data){
.name = "axg_ao_32k_sel",
.ops = &clk_regmap_mux_ops,
.parent_names = (const char *[]){ "axg_ao_32k_div",
"axg_ao_32k_pre" },
.num_parents = 2,
.flags = CLK_SET_RATE_PARENT,
},
};
static struct clk_regmap axg_aoclk_32k = {
.data = &(struct clk_regmap_gate_data){
.offset = AO_RTC_ALT_CLK_CNTL0,
.bit_idx = 30,
},
.hw.init = &(struct clk_init_data){
.name = "axg_ao_32k",
.ops = &clk_regmap_gate_ops,
.parent_names = (const char *[]){ "axg_ao_32k_sel" },
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
},
};
static struct clk_regmap axg_aoclk_cts_rtc_oscin = {
.data = &(struct clk_regmap_mux_data) {
.offset = AO_RTI_PWR_CNTL_REG0,
.mask = 0x1,
.shift = 10,
.flags = CLK_MUX_ROUND_CLOSEST,
},
.hw.init = &(struct clk_init_data){
.name = "axg_ao_cts_rtc_oscin",
.ops = &clk_regmap_mux_ops,
.parent_names = (const char *[]){ "axg_ao_32k",
IN_PREFIX "ext_32k-0" },
.num_parents = 2,
.flags = CLK_SET_RATE_PARENT,
},
};
static struct clk_regmap axg_aoclk_clk81 = {
.data = &(struct clk_regmap_mux_data) {
.offset = AO_RTI_PWR_CNTL_REG0,
.mask = 0x1,
.shift = 8,
.flags = CLK_MUX_ROUND_CLOSEST,
},
.hw.init = &(struct clk_init_data){
.name = "axg_ao_clk81",
.ops = &clk_regmap_mux_ro_ops,
.parent_names = (const char *[]){ "clk81", "ao_alt_xtal"},
.parent_names = (const char *[]){ IN_PREFIX "mpeg-clk",
"axg_ao_cts_rtc_oscin"},
.num_parents = 2,
.flags = CLK_SET_RATE_PARENT,
},
};
......@@ -62,7 +203,8 @@ static struct clk_regmap axg_aoclk_saradc_mux = {
.hw.init = &(struct clk_init_data){
.name = "axg_ao_saradc_mux",
.ops = &clk_regmap_mux_ops,
.parent_names = (const char *[]){ "xtal", "axg_ao_clk81" },
.parent_names = (const char *[]){ IN_PREFIX "xtal",
"axg_ao_clk81" },
.num_parents = 2,
},
};
......@@ -106,17 +248,23 @@ static const unsigned int axg_aoclk_reset[] = {
};
static struct clk_regmap *axg_aoclk_regmap[] = {
[CLKID_AO_REMOTE] = &axg_aoclk_remote,
[CLKID_AO_I2C_MASTER] = &axg_aoclk_i2c_master,
[CLKID_AO_I2C_SLAVE] = &axg_aoclk_i2c_slave,
[CLKID_AO_UART1] = &axg_aoclk_uart1,
[CLKID_AO_UART2] = &axg_aoclk_uart2,
[CLKID_AO_IR_BLASTER] = &axg_aoclk_ir_blaster,
[CLKID_AO_SAR_ADC] = &axg_aoclk_saradc,
[CLKID_AO_CLK81] = &axg_aoclk_clk81,
[CLKID_AO_SAR_ADC_SEL] = &axg_aoclk_saradc_mux,
[CLKID_AO_SAR_ADC_DIV] = &axg_aoclk_saradc_div,
[CLKID_AO_SAR_ADC_CLK] = &axg_aoclk_saradc_gate,
&axg_aoclk_remote,
&axg_aoclk_i2c_master,
&axg_aoclk_i2c_slave,
&axg_aoclk_uart1,
&axg_aoclk_uart2,
&axg_aoclk_ir_blaster,
&axg_aoclk_saradc,
&axg_aoclk_cts_oscin,
&axg_aoclk_32k_pre,
&axg_aoclk_32k_div,
&axg_aoclk_32k_sel,
&axg_aoclk_32k,
&axg_aoclk_cts_rtc_oscin,
&axg_aoclk_clk81,
&axg_aoclk_saradc_mux,
&axg_aoclk_saradc_div,
&axg_aoclk_saradc_gate,
};
static const struct clk_hw_onecell_data axg_aoclk_onecell_data = {
......@@ -132,10 +280,22 @@ static const struct clk_hw_onecell_data axg_aoclk_onecell_data = {
[CLKID_AO_SAR_ADC_SEL] = &axg_aoclk_saradc_mux.hw,
[CLKID_AO_SAR_ADC_DIV] = &axg_aoclk_saradc_div.hw,
[CLKID_AO_SAR_ADC_CLK] = &axg_aoclk_saradc_gate.hw,
[CLKID_AO_CTS_OSCIN] = &axg_aoclk_cts_oscin.hw,
[CLKID_AO_32K_PRE] = &axg_aoclk_32k_pre.hw,
[CLKID_AO_32K_DIV] = &axg_aoclk_32k_div.hw,
[CLKID_AO_32K_SEL] = &axg_aoclk_32k_sel.hw,
[CLKID_AO_32K] = &axg_aoclk_32k.hw,
[CLKID_AO_CTS_RTC_OSCIN] = &axg_aoclk_cts_rtc_oscin.hw,
},
.num = NR_CLKS,
};
static const struct meson_aoclk_input axg_aoclk_inputs[] = {
{ .name = "xtal", .required = true },
{ .name = "mpeg-clk", .required = true },
{ .name = "ext-32k-0", .required = false },
};
static const struct meson_aoclk_data axg_aoclkc_data = {
.reset_reg = AO_RTI_GEN_CNTL_REG0,
.num_reset = ARRAY_SIZE(axg_aoclk_reset),
......@@ -143,6 +303,9 @@ static const struct meson_aoclk_data axg_aoclkc_data = {
.num_clks = ARRAY_SIZE(axg_aoclk_regmap),
.clks = axg_aoclk_regmap,
.hw_data = &axg_aoclk_onecell_data,
.inputs = axg_aoclk_inputs,
.num_inputs = ARRAY_SIZE(axg_aoclk_inputs),
.input_prefix = IN_PREFIX,
};
static const struct of_device_id axg_aoclkc_match_table[] = {
......
......@@ -10,18 +10,7 @@
#ifndef __AXG_AOCLKC_H
#define __AXG_AOCLKC_H
#define NR_CLKS 11
/* AO Configuration Clock registers offsets
* Register offsets from the data sheet must be multiplied by 4.
*/
#define AO_RTI_PWR_CNTL_REG1 0x0C
#define AO_RTI_PWR_CNTL_REG0 0x10
#define AO_RTI_GEN_CNTL_REG0 0x40
#define AO_OSCIN_CNTL 0x58
#define AO_CRT_CLK_CNTL1 0x68
#define AO_SAR_CLK 0x90
#define AO_RTC_ALT_CLK_CNTL0 0x94
#define AO_RTC_ALT_CLK_CNTL1 0x98
#define NR_CLKS 17
#include <dt-bindings/clock/axg-aoclkc.h>
#include <dt-bindings/reset/axg-aoclkc.h>
......
......@@ -14,8 +14,11 @@
#include <linux/reset.h>
#include <linux/slab.h>
#include "clkc-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_SLV_SCLK_COUNT 10
......
......@@ -9,16 +9,17 @@
* Author: Qiufang Dai <qiufang.dai@amlogic.com>
*/
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/init.h>
#include <linux/of_device.h>
#include <linux/mfd/syscon.h>
#include <linux/platform_device.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 "meson-eeclk.h"
static DEFINE_SPINLOCK(meson_clk_lock);
......@@ -58,7 +59,7 @@ static struct clk_regmap axg_fixed_pll_dco = {
.hw.init = &(struct clk_init_data){
.name = "fixed_pll_dco",
.ops = &meson_clk_pll_ro_ops,
.parent_names = (const char *[]){ "xtal" },
.parent_names = (const char *[]){ IN_PREFIX "xtal" },
.num_parents = 1,
},
};
......@@ -113,7 +114,7 @@ static struct clk_regmap axg_sys_pll_dco = {
.hw.init = &(struct clk_init_data){
.name = "sys_pll_dco",
.ops = &meson_clk_pll_ro_ops,
.parent_names = (const char *[]){ "xtal" },
.parent_names = (const char *[]){ IN_PREFIX "xtal" },
.num_parents = 1,
},
};
......@@ -214,7 +215,7 @@ static struct clk_regmap axg_gp0_pll_dco = {
.hw.init = &(struct clk_init_data){
.name = "gp0_pll_dco",
.ops = &meson_clk_pll_ops,
.parent_names = (const char *[]){ "xtal" },
.parent_names = (const char *[]){ IN_PREFIX "xtal" },
.num_parents = 1,
},
};
......@@ -283,7 +284,7 @@ static struct clk_regmap axg_hifi_pll_dco = {
.hw.init = &(struct clk_init_data){
.name = "hifi_pll_dco",
.ops = &meson_clk_pll_ops,
.parent_names = (const char *[]){ "xtal" },
.parent_names = (const char *[]){ IN_PREFIX "xtal" },
.num_parents = 1,
},
};
......@@ -701,7 +702,7 @@ static struct clk_regmap axg_pcie_pll_dco = {
.hw.init = &(struct clk_init_data){
.name = "pcie_pll_dco",
.ops = &meson_clk_pll_ops,
.parent_names = (const char *[]){ "xtal" },
.parent_names = (const char *[]){ IN_PREFIX "xtal" },
.num_parents = 1,
},
};
......@@ -803,7 +804,7 @@ static struct clk_regmap axg_pcie_cml_en1 = {
static u32 mux_table_clk81[] = { 0, 2, 3, 4, 5, 6, 7 };
static const char * const clk81_parent_names[] = {
"xtal", "fclk_div7", "mpll1", "mpll2", "fclk_div4",
IN_PREFIX "xtal", "fclk_div7", "mpll1", "mpll2", "fclk_div4",
"fclk_div3", "fclk_div5"
};
......@@ -852,7 +853,7 @@ static struct clk_regmap axg_clk81 = {
};
static const char * const axg_sd_emmc_clk0_parent_names[] = {
"xtal", "fclk_div2", "fclk_div3", "fclk_div5", "fclk_div7",
IN_PREFIX "xtal", "fclk_div2", "fclk_div3", "fclk_div5", "fclk_div7",
/*
* Following these parent clocks, we should also have had mpll2, mpll3
......@@ -957,7 +958,7 @@ static struct clk_regmap axg_sd_emmc_c_clk0 = {
static u32 mux_table_gen_clk[] = { 0, 4, 5, 6, 7, 8,
9, 10, 11, 13, 14, };
static const char * const gen_clk_parent_names[] = {
"xtal", "hifi_pll", "mpll0", "mpll1", "mpll2", "mpll3",
IN_PREFIX "xtal", "hifi_pll", "mpll0", "mpll1", "mpll2", "mpll3",
"fclk_div4", "fclk_div3", "fclk_div5", "fclk_div7", "gp0_pll",
};
......@@ -1255,46 +1256,20 @@ static struct clk_regmap *const axg_clk_regmaps[] = {
&axg_pcie_pll_od,
};
static const struct meson_eeclkc_data axg_clkc_data = {
.regmap_clks = axg_clk_regmaps,
.regmap_clk_num = ARRAY_SIZE(axg_clk_regmaps),
.hw_onecell_data = &axg_hw_onecell_data,
};
static const struct of_device_id clkc_match_table[] = {
{ .compatible = "amlogic,axg-clkc" },
{ .compatible = "amlogic,axg-clkc", .data = &axg_clkc_data },
{}
};
static int axg_clkc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct regmap *map;
int ret, i;
/* Get the hhi system controller node if available */
map = syscon_node_to_regmap(of_get_parent(dev->of_node));
if (IS_ERR(map)) {
dev_err(dev, "failed to get HHI regmap\n");
return PTR_ERR(map);
}
/* Populate regmap for the regmap backed clocks */
for (i = 0; i < ARRAY_SIZE(axg_clk_regmaps); i++)
axg_clk_regmaps[i]->map = map;
for (i = 0; i < axg_hw_onecell_data.num; i++) {
/* array might be sparse */
if (!axg_hw_onecell_data.hws[i])
continue;
ret = devm_clk_hw_register(dev, axg_hw_onecell_data.hws[i]);
if (ret) {
dev_err(dev, "Clock registration failed\n");
return ret;
}
}
return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,
&axg_hw_onecell_data);
}
static struct platform_driver axg_driver = {
.probe = axg_clkc_probe,
.probe = meson_eeclkc_probe,
.driver = {
.name = "axg-clkc",
.of_match_table = clkc_match_table,
......
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2017 BayLibre, SAS
* Author: Neil Armstrong <narmstrong@baylibre.com>
* Author: Jerome Brunet <jbrunet@baylibre.com>
*/
/*
* The AO Domain embeds a dual/divider to generate a more precise
* 32,768KHz clock for low-power suspend mode and CEC.
* ______ ______
* | | | |
* | Div1 |-| Cnt1 |
* /|______| |______|\
* -| ______ ______ X--> Out
* \| | | |/
* | Div2 |-| Cnt2 |
* |______| |______|
*
* The dividing can be switched to single or dual, with a counter
* for each divider to set when the switching is done.
*/
#include <linux/clk-provider.h>
#include <linux/module.h>
#include "clk-regmap.h"
#include "clk-dualdiv.h"
static inline struct meson_clk_dualdiv_data *
meson_clk_dualdiv_data(struct clk_regmap *clk)
{
return (struct meson_clk_dualdiv_data *)clk->data;
}
static unsigned long
__dualdiv_param_to_rate(unsigned long parent_rate,
const struct meson_clk_dualdiv_param *p)
{
if (!p->dual)
return DIV_ROUND_CLOSEST(parent_rate, p->n1);
return DIV_ROUND_CLOSEST(parent_rate * (p->m1 + p->m2),
p->n1 * p->m1 + p->n2 * p->m2);
}
static unsigned long meson_clk_dualdiv_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct clk_regmap *clk = to_clk_regmap(hw);
struct meson_clk_dualdiv_data *dualdiv = meson_clk_dualdiv_data(clk);
struct meson_clk_dualdiv_param setting;
setting.dual = meson_parm_read(clk->map, &dualdiv->dual);
setting.n1 = meson_parm_read(clk->map, &dualdiv->n1) + 1;
setting.m1 = meson_parm_read(clk->map, &dualdiv->m1) + 1;
setting.n2 = meson_parm_read(clk->map, &dualdiv->n2) + 1;
setting.m2 = meson_parm_read(clk->map, &dualdiv->m2) + 1;
return __dualdiv_param_to_rate(parent_rate, &setting);
}
static const struct meson_clk_dualdiv_param *
__dualdiv_get_setting(unsigned long rate, unsigned long parent_rate,
struct meson_clk_dualdiv_data *dualdiv)
{
const struct meson_clk_dualdiv_param *table = dualdiv->table;
unsigned long best = 0, now = 0;
unsigned int i, best_i = 0;
if (!table)
return NULL;
for (i = 0; table[i].n1; i++) {
now = __dualdiv_param_to_rate(parent_rate, &table[i]);
/* If we get an exact match, don't bother any further */
if (now == rate) {
return &table[i];
} else if (abs(now - rate) < abs(best - rate)) {
best = now;
best_i = i;
}
}
return (struct meson_clk_dualdiv_param *)&table[best_i];
}
static long meson_clk_dualdiv_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
struct clk_regmap *clk = to_clk_regmap(hw);
struct meson_clk_dualdiv_data *dualdiv = meson_clk_dualdiv_data(clk);
const struct meson_clk_dualdiv_param *setting =
__dualdiv_get_setting(rate, *parent_rate, dualdiv);
if (!setting)
return meson_clk_dualdiv_recalc_rate(hw, *parent_rate);
return __dualdiv_param_to_rate(*parent_rate, setting);
}
static int meson_clk_dualdiv_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct clk_regmap *clk = to_clk_regmap(hw);
struct meson_clk_dualdiv_data *dualdiv = meson_clk_dualdiv_data(clk);
const struct meson_clk_dualdiv_param *setting =
__dualdiv_get_setting(rate, parent_rate, dualdiv);
if (!setting)
return -EINVAL;
meson_parm_write(clk->map, &dualdiv->dual, setting->dual);
meson_parm_write(clk->map, &dualdiv->n1, setting->n1 - 1);
meson_parm_write(clk->map, &dualdiv->m1, setting->m1 - 1);
meson_parm_write(clk->map, &dualdiv->n2, setting->n2 - 1);
meson_parm_write(clk->map, &dualdiv->m2, setting->m2 - 1);
return 0;
}
const struct clk_ops meson_clk_dualdiv_ops = {
.recalc_rate = meson_clk_dualdiv_recalc_rate,
.round_rate = meson_clk_dualdiv_round_rate,
.set_rate = meson_clk_dualdiv_set_rate,
};
EXPORT_SYMBOL_GPL(meson_clk_dualdiv_ops);
const struct clk_ops meson_clk_dualdiv_ro_ops = {
.recalc_rate = meson_clk_dualdiv_recalc_rate,
};
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 @@
#include <linux/clk.h>
#include <linux/clk-provider.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 = {};
......@@ -42,3 +43,7 @@ struct clk_hw *meson_clk_hw_register_input(struct device *dev,
return ret ? ERR_PTR(ret) : hw;
}
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 @@
*/
#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 N2_MIN 4
......@@ -138,9 +142,15 @@ const struct clk_ops meson_clk_mpll_ro_ops = {
.recalc_rate = mpll_recalc_rate,
.round_rate = mpll_round_rate,
};
EXPORT_SYMBOL_GPL(meson_clk_mpll_ro_ops);
const struct clk_ops meson_clk_mpll_ops = {
.recalc_rate = mpll_recalc_rate,
.round_rate = mpll_round_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 @@
*/
#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)))
......@@ -15,13 +18,12 @@ meson_clk_phase_data(struct clk_regmap *clk)
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;
}
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));
......@@ -31,7 +33,6 @@ unsigned int meson_clk_degrees_to_val(int degrees, unsigned int width)
*/
return val % (1 << width);
}
EXPORT_SYMBOL_GPL(meson_clk_degrees_to_val);
static int meson_clk_phase_get_phase(struct clk_hw *hw)
{
......@@ -61,3 +62,67 @@ const struct clk_ops meson_clk_phase_ops = {
.set_phase = meson_clk_phase_set_phase,
};
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,10 @@
#include <linux/io.h>
#include <linux/math64.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/rational.h>
#include "clkc.h"
#include "clk-regmap.h"
#include "clk-pll.h"
static inline struct meson_clk_pll_data *
meson_clk_pll_data(struct clk_regmap *clk)
......@@ -44,12 +43,21 @@ meson_clk_pll_data(struct clk_regmap *clk)
return (struct meson_clk_pll_data *)clk->data;
}
static int __pll_round_closest_mult(struct meson_clk_pll_data *pll)
{
if ((pll->flags & CLK_MESON_PLL_ROUND_CLOSEST) &&
!MESON_PARM_APPLICABLE(&pll->frac))
return 1;
return 0;
}
static unsigned long __pll_params_to_rate(unsigned long parent_rate,
const struct pll_params_table *pllt,
u16 frac,
unsigned int m, unsigned int n,
unsigned int frac,
struct meson_clk_pll_data *pll)
{
u64 rate = (u64)parent_rate * pllt->m;
u64 rate = (u64)parent_rate * m;
if (frac && MESON_PARM_APPLICABLE(&pll->frac)) {
u64 frac_rate = (u64)parent_rate * frac;
......@@ -58,7 +66,7 @@ static unsigned long __pll_params_to_rate(unsigned long parent_rate,
(1 << pll->frac.width));
}
return DIV_ROUND_UP_ULL(rate, pllt->n);
return DIV_ROUND_UP_ULL(rate, n);
}
static unsigned long meson_clk_pll_recalc_rate(struct clk_hw *hw,
......@@ -66,35 +74,39 @@ static unsigned long meson_clk_pll_recalc_rate(struct clk_hw *hw,
{
struct clk_regmap *clk = to_clk_regmap(hw);
struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
struct pll_params_table pllt;
u16 frac;
unsigned int m, n, frac;
pllt.n = meson_parm_read(clk->map, &pll->n);
pllt.m = meson_parm_read(clk->map, &pll->m);
n = meson_parm_read(clk->map, &pll->n);
m = meson_parm_read(clk->map, &pll->m);
frac = MESON_PARM_APPLICABLE(&pll->frac) ?
meson_parm_read(clk->map, &pll->frac) :
0;
return __pll_params_to_rate(parent_rate, &pllt, frac, pll);
return __pll_params_to_rate(parent_rate, m, n, frac, pll);
}
static u16 __pll_params_with_frac(unsigned long rate,
unsigned long parent_rate,
const struct pll_params_table *pllt,
struct meson_clk_pll_data *pll)
static unsigned int __pll_params_with_frac(unsigned long rate,
unsigned long parent_rate,
unsigned int m,
unsigned int n,
struct meson_clk_pll_data *pll)
{
u16 frac_max = (1 << pll->frac.width);
u64 val = (u64)rate * pllt->n;
unsigned int frac_max = (1 << pll->frac.width);
u64 val = (u64)rate * n;
/* Bail out if we are already over the requested rate */
if (rate < parent_rate * m / n)
return 0;
if (pll->flags & CLK_MESON_PLL_ROUND_CLOSEST)
val = DIV_ROUND_CLOSEST_ULL(val * frac_max, parent_rate);
else
val = div_u64(val * frac_max, parent_rate);
val -= pllt->m * frac_max;
val -= m * frac_max;
return min((u16)val, (u16)(frac_max - 1));
return min((unsigned int)val, (frac_max - 1));
}
static bool meson_clk_pll_is_better(unsigned long rate,
......@@ -102,45 +114,123 @@ static bool meson_clk_pll_is_better(unsigned long rate,
unsigned long now,
struct meson_clk_pll_data *pll)
{
if (!(pll->flags & CLK_MESON_PLL_ROUND_CLOSEST) ||
MESON_PARM_APPLICABLE(&pll->frac)) {
/* Round down */
if (now < rate && best < now)
return true;
} else {
if (__pll_round_closest_mult(pll)) {
/* Round Closest */
if (abs(now - rate) < abs(best - rate))
return true;
} else {
/* Round down */
if (now < rate && best < now)
return true;
}
return false;
}
static const struct pll_params_table *
meson_clk_get_pll_settings(unsigned long rate,
unsigned long parent_rate,
struct meson_clk_pll_data *pll)
static int meson_clk_get_pll_table_index(unsigned int index,
unsigned int *m,
unsigned int *n,
struct meson_clk_pll_data *pll)
{
const struct pll_params_table *table = pll->table;
unsigned long best = 0, now = 0;
unsigned int i, best_i = 0;
if (!pll->table[index].n)
return -EINVAL;
*m = pll->table[index].m;
*n = pll->table[index].n;
return 0;
}
static unsigned int meson_clk_get_pll_range_m(unsigned long rate,
unsigned long parent_rate,
unsigned int n,
struct meson_clk_pll_data *pll)
{
u64 val = (u64)rate * n;
if (!table)
return NULL;
if (__pll_round_closest_mult(pll))
return DIV_ROUND_CLOSEST_ULL(val, parent_rate);
for (i = 0; table[i].n; i++) {
now = __pll_params_to_rate(parent_rate, &table[i], 0, pll);
return div_u64(val, parent_rate);
}
/* If we get an exact match, don't bother any further */
if (now == rate) {
return &table[i];
} else if (meson_clk_pll_is_better(rate, best, now, pll)) {
static int meson_clk_get_pll_range_index(unsigned long rate,
unsigned long parent_rate,
unsigned int index,
unsigned int *m,
unsigned int *n,
struct meson_clk_pll_data *pll)
{
*n = index + 1;
/* Check the predivider range */
if (*n >= (1 << pll->n.width))
return -EINVAL;
if (*n == 1) {
/* Get the boundaries out the way */
if (rate <= pll->range->min * parent_rate) {
*m = pll->range->min;
return -ENODATA;
} else if (rate >= pll->range->max * parent_rate) {
*m = pll->range->max;
return -ENODATA;
}
}
*m = meson_clk_get_pll_range_m(rate, parent_rate, *n, pll);
/* the pre-divider gives a multiplier too big - stop */
if (*m >= (1 << pll->m.width))
return -EINVAL;
return 0;
}
static int meson_clk_get_pll_get_index(unsigned long rate,
unsigned long parent_rate,
unsigned int index,
unsigned int *m,
unsigned int *n,
struct meson_clk_pll_data *pll)
{
if (pll->range)
return meson_clk_get_pll_range_index(rate, parent_rate,
index, m, n, pll);
else if (pll->table)
return meson_clk_get_pll_table_index(index, m, n, pll);
return -EINVAL;
}
static int meson_clk_get_pll_settings(unsigned long rate,
unsigned long parent_rate,
unsigned int *best_m,
unsigned int *best_n,
struct meson_clk_pll_data *pll)
{
unsigned long best = 0, now = 0;
unsigned int i, m, n;
int ret;
for (i = 0, ret = 0; !ret; i++) {
ret = meson_clk_get_pll_get_index(rate, parent_rate,
i, &m, &n, pll);
if (ret == -EINVAL)
break;
now = __pll_params_to_rate(parent_rate, m, n, 0, pll);
if (meson_clk_pll_is_better(rate, best, now, pll)) {
best = now;
best_i = i;
*best_m = m;
*best_n = n;
if (now == rate)
break;
}
}
return (struct pll_params_table *)&table[best_i];
return best ? 0 : -EINVAL;
}
static long meson_clk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
......@@ -148,15 +238,15 @@ static long meson_clk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
{
struct clk_regmap *clk = to_clk_regmap(hw);
struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
const struct pll_params_table *pllt =
meson_clk_get_pll_settings(rate, *parent_rate, pll);
unsigned int m, n, frac;
unsigned long round;
u16 frac;
int ret;
if (!pllt)
ret = meson_clk_get_pll_settings(rate, *parent_rate, &m, &n, pll);
if (ret)
return meson_clk_pll_recalc_rate(hw, *parent_rate);
round = __pll_params_to_rate(*parent_rate, pllt, 0, pll);
round = __pll_params_to_rate(*parent_rate, m, n, 0, pll);
if (!MESON_PARM_APPLICABLE(&pll->frac) || rate == round)
return round;
......@@ -165,9 +255,9 @@ static long meson_clk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
* The rate provided by the setting is not an exact match, let's
* try to improve the result using the fractional parameter
*/
frac = __pll_params_with_frac(rate, *parent_rate, pllt, pll);
frac = __pll_params_with_frac(rate, *parent_rate, m, n, pll);
return __pll_params_to_rate(*parent_rate, pllt, frac, pll);
return __pll_params_to_rate(*parent_rate, m, n, frac, pll);
}
static int meson_clk_pll_wait_lock(struct clk_hw *hw)
......@@ -254,30 +344,27 @@ static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
{
struct clk_regmap *clk = to_clk_regmap(hw);
struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
const struct pll_params_table *pllt;
unsigned int enabled;
unsigned int enabled, m, n, frac = 0, ret;
unsigned long old_rate;
u16 frac = 0;
if (parent_rate == 0 || rate == 0)
return -EINVAL;
old_rate = rate;
pllt = meson_clk_get_pll_settings(rate, parent_rate, pll);
if (!pllt)
return -EINVAL;
ret = meson_clk_get_pll_settings(rate, parent_rate, &m, &n, pll);
if (ret)
return ret;
enabled = meson_parm_read(clk->map, &pll->en);
if (enabled)
meson_clk_pll_disable(hw);
meson_parm_write(clk->map, &pll->n, pllt->n);
meson_parm_write(clk->map, &pll->m, pllt->m);
meson_parm_write(clk->map, &pll->n, n);
meson_parm_write(clk->map, &pll->m, m);
if (MESON_PARM_APPLICABLE(&pll->frac)) {
frac = __pll_params_with_frac(rate, parent_rate, pllt, pll);
frac = __pll_params_with_frac(rate, parent_rate, m, n, pll);
meson_parm_write(clk->map, &pll->frac, frac);
}
......@@ -309,8 +396,15 @@ const struct clk_ops meson_clk_pll_ops = {
.enable = meson_clk_pll_enable,
.disable = meson_clk_pll_disable
};
EXPORT_SYMBOL_GPL(meson_clk_pll_ops);
const struct clk_ops meson_clk_pll_ro_ops = {
.recalc_rate = meson_clk_pll_recalc_rate,
.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 {
unsigned int m;
unsigned int n;
};
struct pll_mult_range {
unsigned int min;
unsigned int max;
};
#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;
const struct pll_mult_range *range;
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 @@
* Author: Jerome Brunet <jbrunet@baylibre.com>
*/
#include <linux/module.h>
#include "clk-regmap.h"
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 = {
.get_parent = clk_regmap_mux_get_parent,
};
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,24 @@ 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_ro_ops;
#define __MESON_GATE(_name, _reg, _bit, _ops) \
struct clk_regmap _name = { \
.data = &(struct clk_regmap_gate_data){ \
.offset = (_reg), \
.bit_idx = (_bit), \
}, \
.hw.init = &(struct clk_init_data) { \
.name = #_name, \
.ops = _ops, \
.parent_names = (const char *[]){ "clk81" }, \
.num_parents = 1, \
.flags = (CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED), \
}, \
}
#define MESON_GATE(_name, _reg, _bit) \
__MESON_GATE(_name, _reg, _bit, &clk_regmap_gate_ops)
#define MESON_GATE_RO(_name, _reg, _bit) \
__MESON_GATE(_name, _reg, _bit, &clk_regmap_gate_ro_ops)
#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);
This diff is collapsed.
/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */
/*
* Copyright (c) 2019 BayLibre, SAS
* Author: Neil Armstrong <narmstrong@baylibre.com>
*/
#ifndef __G12A_AOCLKC_H
#define __G12A_AOCLKC_H
/*
* CLKID index values
*
* These indices are entirely contrived and do not map onto the hardware.
* It has now been decided to expose everything by default in the DT header:
* include/dt-bindings/clock/g12a-aoclkc.h. Only the clocks ids we don't want
* to expose, such as the internal muxes and dividers of composite clocks,
* will remain defined here.
*/
#define CLKID_AO_SAR_ADC_SEL 16
#define CLKID_AO_SAR_ADC_DIV 17
#define CLKID_AO_CTS_OSCIN 19
#define CLKID_AO_32K_PRE 20
#define CLKID_AO_32K_DIV 21
#define CLKID_AO_32K_SEL 22
#define CLKID_AO_CEC_PRE 24
#define CLKID_AO_CEC_DIV 25
#define CLKID_AO_CEC_SEL 26
#define NR_CLKS 29
#include <dt-bindings/clock/g12a-aoclkc.h>
#include <dt-bindings/reset/g12a-aoclkc.h>
#endif /* __G12A_AOCLKC_H */
This diff is collapsed.
/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */
/*
* Copyright (c) 2016 Amlogic, Inc.
* Author: Michael Turquette <mturquette@baylibre.com>
*
* Copyright (c) 2018 Amlogic, inc.
* Author: Qiufang Dai <qiufang.dai@amlogic.com>
* Author: Jian Hu <jian.hu@amlogic.com>
*
*/
#ifndef __G12A_H
#define __G12A_H
/*
* Clock controller register offsets
*
* Register offsets from the data sheet must be multiplied by 4 before
* adding them to the base address to get the right value.
*/
#define HHI_MIPI_CNTL0 0x000
#define HHI_MIPI_CNTL1 0x004
#define HHI_MIPI_CNTL2 0x008
#define HHI_MIPI_STS 0x00C
#define HHI_GP0_PLL_CNTL0 0x040
#define HHI_GP0_PLL_CNTL1 0x044
#define HHI_GP0_PLL_CNTL2 0x048
#define HHI_GP0_PLL_CNTL3 0x04C
#define HHI_GP0_PLL_CNTL4 0x050
#define HHI_GP0_PLL_CNTL5 0x054
#define HHI_GP0_PLL_CNTL6 0x058
#define HHI_GP0_PLL_STS 0x05C
#define HHI_PCIE_PLL_CNTL0 0x098
#define HHI_PCIE_PLL_CNTL1 0x09C
#define HHI_PCIE_PLL_CNTL2 0x0A0
#define HHI_PCIE_PLL_CNTL3 0x0A4
#define HHI_PCIE_PLL_CNTL4 0x0A8
#define HHI_PCIE_PLL_CNTL5 0x0AC
#define HHI_PCIE_PLL_STS 0x0B8
#define HHI_HIFI_PLL_CNTL0 0x0D8
#define HHI_HIFI_PLL_CNTL1 0x0DC
#define HHI_HIFI_PLL_CNTL2 0x0E0
#define HHI_HIFI_PLL_CNTL3 0x0E4
#define HHI_HIFI_PLL_CNTL4 0x0E8
#define HHI_HIFI_PLL_CNTL5 0x0EC
#define HHI_HIFI_PLL_CNTL6 0x0F0
#define HHI_VIID_CLK_DIV 0x128
#define HHI_VIID_CLK_CNTL 0x12C
#define HHI_GCLK_MPEG0 0x140
#define HHI_GCLK_MPEG1 0x144
#define HHI_GCLK_MPEG2 0x148
#define HHI_GCLK_OTHER 0x150
#define HHI_GCLK_OTHER2 0x154
#define HHI_VID_CLK_DIV 0x164
#define HHI_MPEG_CLK_CNTL 0x174
#define HHI_AUD_CLK_CNTL 0x178
#define HHI_VID_CLK_CNTL 0x17c
#define HHI_TS_CLK_CNTL 0x190
#define HHI_VID_CLK_CNTL2 0x194
#define HHI_SYS_CPU_CLK_CNTL0 0x19c
#define HHI_VID_PLL_CLK_DIV 0x1A0
#define HHI_MALI_CLK_CNTL 0x1b0
#define HHI_VPU_CLKC_CNTL 0x1b4
#define HHI_VPU_CLK_CNTL 0x1bC
#define HHI_HDMI_CLK_CNTL 0x1CC
#define HHI_VDEC_CLK_CNTL 0x1E0
#define HHI_VDEC2_CLK_CNTL 0x1E4
#define HHI_VDEC3_CLK_CNTL 0x1E8
#define HHI_VDEC4_CLK_CNTL 0x1EC
#define HHI_HDCP22_CLK_CNTL 0x1F0
#define HHI_VAPBCLK_CNTL 0x1F4
#define HHI_VPU_CLKB_CNTL 0x20C
#define HHI_GEN_CLK_CNTL 0x228
#define HHI_VDIN_MEAS_CLK_CNTL 0x250
#define HHI_MIPIDSI_PHY_CLK_CNTL 0x254
#define HHI_NAND_CLK_CNTL 0x25C
#define HHI_SD_EMMC_CLK_CNTL 0x264
#define HHI_MPLL_CNTL0 0x278
#define HHI_MPLL_CNTL1 0x27C
#define HHI_MPLL_CNTL2 0x280
#define HHI_MPLL_CNTL3 0x284
#define HHI_MPLL_CNTL4 0x288
#define HHI_MPLL_CNTL5 0x28c
#define HHI_MPLL_CNTL6 0x290
#define HHI_MPLL_CNTL7 0x294
#define HHI_MPLL_CNTL8 0x298
#define HHI_FIX_PLL_CNTL0 0x2A0
#define HHI_FIX_PLL_CNTL1 0x2A4
#define HHI_FIX_PLL_CNTL3 0x2AC
#define HHI_SYS_PLL_CNTL0 0x2f4
#define HHI_SYS_PLL_CNTL1 0x2f8
#define HHI_SYS_PLL_CNTL2 0x2fc
#define HHI_SYS_PLL_CNTL3 0x300
#define HHI_SYS_PLL_CNTL4 0x304
#define HHI_SYS_PLL_CNTL5 0x308
#define HHI_SYS_PLL_CNTL6 0x30c
#define HHI_HDMI_PLL_CNTL0 0x320
#define HHI_HDMI_PLL_CNTL1 0x324
#define HHI_HDMI_PLL_CNTL2 0x328
#define HHI_HDMI_PLL_CNTL3 0x32c
#define HHI_HDMI_PLL_CNTL4 0x330
#define HHI_HDMI_PLL_CNTL5 0x334
#define HHI_HDMI_PLL_CNTL6 0x338
#define HHI_SPICC_CLK_CNTL 0x3dc
/*
* CLKID index values
*
* These indices are entirely contrived and do not map onto the hardware.
* It has now been decided to expose everything by default in the DT header:
* include/dt-bindings/clock/g12a-clkc.h. Only the clocks ids we don't want
* to expose, such as the internal muxes and dividers of composite clocks,
* will remain defined here.
*/
#define CLKID_MPEG_SEL 8
#define CLKID_MPEG_DIV 9
#define CLKID_SD_EMMC_A_CLK0_SEL 63
#define CLKID_SD_EMMC_A_CLK0_DIV 64
#define CLKID_SD_EMMC_B_CLK0_SEL 65
#define CLKID_SD_EMMC_B_CLK0_DIV 66
#define CLKID_SD_EMMC_C_CLK0_SEL 67
#define CLKID_SD_EMMC_C_CLK0_DIV 68
#define CLKID_MPLL0_DIV 69
#define CLKID_MPLL1_DIV 70
#define CLKID_MPLL2_DIV 71
#define CLKID_MPLL3_DIV 72
#define CLKID_MPLL_PREDIV 73
#define CLKID_FCLK_DIV2_DIV 75
#define CLKID_FCLK_DIV3_DIV 76
#define CLKID_FCLK_DIV4_DIV 77
#define CLKID_FCLK_DIV5_DIV 78
#define CLKID_FCLK_DIV7_DIV 79
#define CLKID_FCLK_DIV2P5_DIV 100
#define CLKID_FIXED_PLL_DCO 101
#define CLKID_SYS_PLL_DCO 102
#define CLKID_GP0_PLL_DCO 103
#define CLKID_HIFI_PLL_DCO 104
#define CLKID_VPU_0_DIV 111
#define CLKID_VPU_1_DIV 114
#define CLKID_VAPB_0_DIV 118
#define CLKID_VAPB_1_DIV 121
#define CLKID_HDMI_PLL_DCO 125
#define CLKID_HDMI_PLL_OD 126
#define CLKID_HDMI_PLL_OD2 127
#define CLKID_VID_PLL_SEL 130
#define CLKID_VID_PLL_DIV 131
#define CLKID_VCLK_SEL 132
#define CLKID_VCLK2_SEL 133
#define CLKID_VCLK_INPUT 134
#define CLKID_VCLK2_INPUT 135
#define CLKID_VCLK_DIV 136
#define CLKID_VCLK2_DIV 137
#define CLKID_VCLK_DIV2_EN 140
#define CLKID_VCLK_DIV4_EN 141
#define CLKID_VCLK_DIV6_EN 142
#define CLKID_VCLK_DIV12_EN 143
#define CLKID_VCLK2_DIV2_EN 144
#define CLKID_VCLK2_DIV4_EN 145
#define CLKID_VCLK2_DIV6_EN 146
#define CLKID_VCLK2_DIV12_EN 147
#define CLKID_CTS_ENCI_SEL 158
#define CLKID_CTS_ENCP_SEL 159
#define CLKID_CTS_VDAC_SEL 160
#define CLKID_HDMI_TX_SEL 161
#define CLKID_HDMI_SEL 166
#define CLKID_HDMI_DIV 167
#define CLKID_MALI_0_DIV 170
#define CLKID_MALI_1_DIV 173
#define CLKID_MPLL_5OM_DIV 176
#define NR_CLKS 178
/* include the CLKIDs that have been made part of the DT binding */
#include <dt-bindings/clock/g12a-clkc.h>
#endif /* __G12A_H */
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2017 BayLibre, SAS.
* Author: Neil Armstrong <narmstrong@baylibre.com>
*/
#include <linux/clk-provider.h>
#include <linux/bitfield.h>
#include <linux/regmap.h>
#include "gxbb-aoclk.h"
/*
* The AO Domain embeds a dual/divider to generate a more precise
* 32,768KHz clock for low-power suspend mode and CEC.
* ______ ______
* | | | |
* ______ | Div1 |-| Cnt1 | ______
* | | /|______| |______|\ | |
* Xtal-->| Gate |---| ______ ______ X-X--| Gate |-->
* |______| | \| | | |/ | |______|
* | | Div2 |-| Cnt2 | |
* | |______| |______| |
* |_______________________|
*
* The dividing can be switched to single or dual, with a counter
* for each divider to set when the switching is done.
* The entire dividing mechanism can be also bypassed.
*/
#define CLK_CNTL0_N1_MASK GENMASK(11, 0)
#define CLK_CNTL0_N2_MASK GENMASK(23, 12)
#define CLK_CNTL0_DUALDIV_EN BIT(28)
#define CLK_CNTL0_OUT_GATE_EN BIT(30)
#define CLK_CNTL0_IN_GATE_EN BIT(31)
#define CLK_CNTL1_M1_MASK GENMASK(11, 0)
#define CLK_CNTL1_M2_MASK GENMASK(23, 12)
#define CLK_CNTL1_BYPASS_EN BIT(24)
#define CLK_CNTL1_SELECT_OSC BIT(27)
#define PWR_CNTL_ALT_32K_SEL GENMASK(13, 10)
struct cec_32k_freq_table {
unsigned long parent_rate;
unsigned long target_rate;
bool dualdiv;
unsigned int n1;
unsigned int n2;
unsigned int m1;
unsigned int m2;
};
static const struct cec_32k_freq_table aoclk_cec_32k_table[] = {
[0] = {
.parent_rate = 24000000,
.target_rate = 32768,
.dualdiv = true,
.n1 = 733,
.n2 = 732,
.m1 = 8,
.m2 = 11,
},
};
/*
* If CLK_CNTL0_DUALDIV_EN == 0
* - will use N1 divider only
* If CLK_CNTL0_DUALDIV_EN == 1
* - hold M1 cycles of N1 divider then changes to N2
* - hold M2 cycles of N2 divider then changes to N1
* Then we can get more accurate division.
*/
static unsigned long aoclk_cec_32k_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct aoclk_cec_32k *cec_32k = to_aoclk_cec_32k(hw);
unsigned long n1;
u32 reg0, reg1;
regmap_read(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL0, &reg0);
regmap_read(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL1, &reg1);
if (reg1 & CLK_CNTL1_BYPASS_EN)
return parent_rate;
if (reg0 & CLK_CNTL0_DUALDIV_EN) {
unsigned long n2, m1, m2, f1, f2, p1, p2;
n1 = FIELD_GET(CLK_CNTL0_N1_MASK, reg0) + 1;
n2 = FIELD_GET(CLK_CNTL0_N2_MASK, reg0) + 1;
m1 = FIELD_GET(CLK_CNTL1_M1_MASK, reg1) + 1;
m2 = FIELD_GET(CLK_CNTL1_M2_MASK, reg1) + 1;
f1 = DIV_ROUND_CLOSEST(parent_rate, n1);
f2 = DIV_ROUND_CLOSEST(parent_rate, n2);
p1 = DIV_ROUND_CLOSEST(100000000 * m1, f1 * (m1 + m2));
p2 = DIV_ROUND_CLOSEST(100000000 * m2, f2 * (m1 + m2));
return DIV_ROUND_UP(100000000, p1 + p2);
}
n1 = FIELD_GET(CLK_CNTL0_N1_MASK, reg0) + 1;
return DIV_ROUND_CLOSEST(parent_rate, n1);
}
static const struct cec_32k_freq_table *find_cec_32k_freq(unsigned long rate,
unsigned long prate)
{
int i;
for (i = 0 ; i < ARRAY_SIZE(aoclk_cec_32k_table) ; ++i)
if (aoclk_cec_32k_table[i].parent_rate == prate &&
aoclk_cec_32k_table[i].target_rate == rate)
return &aoclk_cec_32k_table[i];
return NULL;
}
static long aoclk_cec_32k_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *prate)
{
const struct cec_32k_freq_table *freq = find_cec_32k_freq(rate,
*prate);
/* If invalid return first one */
if (!freq)
return aoclk_cec_32k_table[0].target_rate;
return freq->target_rate;
}
/*
* From the Amlogic init procedure, the IN and OUT gates needs to be handled
* in the init procedure to avoid any glitches.
*/
static int aoclk_cec_32k_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
const struct cec_32k_freq_table *freq = find_cec_32k_freq(rate,
parent_rate);
struct aoclk_cec_32k *cec_32k = to_aoclk_cec_32k(hw);
u32 reg = 0;
if (!freq)
return -EINVAL;
/* Disable clock */
regmap_update_bits(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL0,
CLK_CNTL0_IN_GATE_EN | CLK_CNTL0_OUT_GATE_EN, 0);
reg = FIELD_PREP(CLK_CNTL0_N1_MASK, freq->n1 - 1);
if (freq->dualdiv)
reg |= CLK_CNTL0_DUALDIV_EN |
FIELD_PREP(CLK_CNTL0_N2_MASK, freq->n2 - 1);
regmap_write(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL0, reg);
reg = FIELD_PREP(CLK_CNTL1_M1_MASK, freq->m1 - 1);
if (freq->dualdiv)
reg |= FIELD_PREP(CLK_CNTL1_M2_MASK, freq->m2 - 1);
regmap_write(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL1, reg);
/* Enable clock */
regmap_update_bits(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL0,
CLK_CNTL0_IN_GATE_EN, CLK_CNTL0_IN_GATE_EN);
udelay(200);
regmap_update_bits(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL0,
CLK_CNTL0_OUT_GATE_EN, CLK_CNTL0_OUT_GATE_EN);
regmap_update_bits(cec_32k->regmap, AO_CRT_CLK_CNTL1,
CLK_CNTL1_SELECT_OSC, CLK_CNTL1_SELECT_OSC);
/* Select 32k from XTAL */
regmap_update_bits(cec_32k->regmap,
AO_RTI_PWR_CNTL_REG0,
PWR_CNTL_ALT_32K_SEL,
FIELD_PREP(PWR_CNTL_ALT_32K_SEL, 4));
return 0;
}
const struct clk_ops meson_aoclk_cec_32k_ops = {
.recalc_rate = aoclk_cec_32k_recalc_rate,
.round_rate = aoclk_cec_32k_round_rate,
.set_rate = aoclk_cec_32k_set_rate,
};
This diff is collapsed.
......@@ -7,25 +7,7 @@
#ifndef __GXBB_AOCLKC_H
#define __GXBB_AOCLKC_H
#define NR_CLKS 7
/* AO Configuration Clock registers offsets */
#define AO_RTI_PWR_CNTL_REG1 0x0c
#define AO_RTI_PWR_CNTL_REG0 0x10
#define AO_RTI_GEN_CNTL_REG0 0x40
#define AO_OSCIN_CNTL 0x58
#define AO_CRT_CLK_CNTL1 0x68
#define AO_RTC_ALT_CLK_CNTL0 0x94
#define AO_RTC_ALT_CLK_CNTL1 0x98
struct aoclk_cec_32k {
struct clk_hw hw;
struct regmap *regmap;
};
#define to_aoclk_cec_32k(_hw) container_of(_hw, struct aoclk_cec_32k, hw)
extern const struct clk_ops meson_aoclk_cec_32k_ops;
#define NR_CLKS 14
#include <dt-bindings/clock/gxbb-aoclkc.h>
#include <dt-bindings/reset/gxbb-aoclkc.h>
......
This diff is collapsed.
This diff is collapsed.
......@@ -11,16 +11,27 @@
#ifndef __MESON_AOCLK_H__
#define __MESON_AOCLK_H__
#include <linux/clk-provider.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/reset-controller.h>
#include "clk-regmap.h"
struct meson_aoclk_input {
const char *name;
bool required;
};
struct meson_aoclk_data {
const unsigned int reset_reg;
const int num_reset;
const unsigned int *reset;
int num_clks;
const int num_clks;
struct clk_regmap **clks;
const int num_inputs;
const struct meson_aoclk_input *inputs;
const char *input_prefix;
const struct clk_hw_onecell_data *hw_data;
};
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -144,7 +144,7 @@ static SUNXI_CCU_NKM_WITH_GATE_LOCK(pll_mipi_clk, "pll-mipi",
8, 4, /* N */
4, 2, /* K */
0, 4, /* M */
BIT(31), /* gate */
BIT(31) | BIT(23) | BIT(22), /* gate */
BIT(28), /* lock */
CLK_SET_RATE_UNGATE);
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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