First round of updates for meson clocks targeted at v4.19
* Remove legacy register access (finish moving to syscon) * Clean up configuration flags * Add axg PCIe clocks * Add GEN CLK on gxbb, gxl and axg * Remove clk_audio_divider driver * Add axg audio clock controller -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEE9OFZrhjz9W1fG7cb5vwPHDfy2oUFAltDV88ACgkQ5vwPHDfy 2oWk1w//bKOHNvjBEqkA5kZcFd6hQ81KP0poG3QoZqd9Mkvj7sT1GOUVm8QS9oll neVXUT6JIavwOobwnD6f7WCNSHJ6buWTlnxJc37JJU81PBhCV2t2owhLkItSf/ni GDK/BhGf/vGCIAvjlfU2Bh2crxG7hXGVMeg8OFG99my9WryXPL1HaGJfO6ge1T26 N7Gx1bFAdf2wAK3B/EpCPNBYsnS+ohJz0Cq1S8BbJXOpMLjgAsR7UB25F+DCCjp2 pPSHq7gNKUVcgZKveiy2xRqj8F/j3eQyF9tr6K3mpx0obFzXXxFT8meMR54a6y61 swJ6E0PI9+u9kOOo7KOAEuLBIpamgIP19BJNLmXxb+ibAjKjp/mUc7i5qBoqvCOW 8Rab8Yc9/PP03VPY+Pm66OrryrsLciVX9vcSCA5TPXTzp5chyM9Yi9ZsP0QQEC2f NY7A55aL7WfHJsG28NxGEsbAf4F9ozvIFgg4FFPBq/kFlf/wZeWGtz3JPyhK84I6 yxsQbCFDqyFCNJvwPnMVKmGuui26E7w2Ctf8pdWt+/Amnd2kHekJRDi5dx/d5RK6 UhRrrckBBqV3C8UweQZZPPz7aBEA4SYnxwUc9w1jU9l2osbn0WAA/wuOuVEUVRAg 5675tDnmZwVpVmxnMH8aZH0ykaNPX7jk/XLHTdDHqk8NRDUKHRk= =w4lD -----END PGP SIGNATURE----- Merge tag 'meson-clk-4.19-1' of https://github.com/BayLibre/clk-meson into clk-meson Pull first round of updates for meson clocks from Jerome Brunet: - Remove legacy register access (finish moving to syscon) - Clean up configuration flags - Add axg PCIe clocks - Add GEN CLK on gxbb, gxl and axg - Remove clk_audio_divider driver - Add axg audio clock controller * tag 'meson-clk-4.19-1' of https://github.com/BayLibre/clk-meson: clk: meson: add gen_clk clk: meson: gxbb: remove HHI_GEN_CLK_CTNL duplicate definition clk: meson-axg: add clocks required by pcie driver clk: meson: remove unused clk-audio-divider driver clk: meson: stop rate propagation for audio clocks clk: meson: axg: add the audio clock controller driver clk: meson: add axg audio sclk divider driver clk: meson: add triple phase clock driver clk: meson: add clk-phase clock driver clk: meson: clean-up meson clock configuration clk: meson: remove obsolete register access clk: meson: expose GEN_CLK clkid clk: meson-axg: add pcie and mipi clock bindings dt-bindings: clock: add meson axg audio clock controller bindings clk: meson: audio-divider is one based clk: add duty cycle support clk: meson-gxbb: set fclk_div2 as CLK_IS_CRITICALhifive-unleashed-5.1
commit
166f3a8ad6
|
@ -0,0 +1,56 @@
|
|||
* Amlogic AXG Audio Clock Controllers
|
||||
|
||||
The Amlogic AXG audio clock controller generates and supplies clock to the
|
||||
other elements of the audio subsystem, such as fifos, i2s, spdif and pdm
|
||||
devices.
|
||||
|
||||
Required Properties:
|
||||
|
||||
- compatible : should be "amlogic,axg-audio-clkc" for the A113X and A113D
|
||||
- reg : physical base address of the clock controller and length of
|
||||
memory mapped region.
|
||||
- clocks : a list of phandle + clock-specifier pairs for the clocks listed
|
||||
in clock-names.
|
||||
- clock-names : must contain the following:
|
||||
* "pclk" - Main peripheral bus clock
|
||||
may contain the following:
|
||||
* "mst_in[0-7]" - 8 input plls to generate clock signals
|
||||
* "slv_sclk[0-9]" - 10 slave bit clocks provided by external
|
||||
components.
|
||||
* "slv_lrclk[0-9]" - 10 slave sample clocks provided by external
|
||||
components.
|
||||
- resets : phandle of the internal reset line
|
||||
- #clock-cells : should be 1.
|
||||
|
||||
Each clock is assigned an identifier and client nodes can use this identifier
|
||||
to specify the clock which they consume. All available clocks are defined as
|
||||
preprocessor macros in the dt-bindings/clock/axg-audio-clkc.h header and can be
|
||||
used in device tree sources.
|
||||
|
||||
Example:
|
||||
|
||||
clkc_audio: clock-controller@0 {
|
||||
compatible = "amlogic,axg-audio-clkc";
|
||||
reg = <0x0 0x0 0x0 0xb4>;
|
||||
#clock-cells = <1>;
|
||||
|
||||
clocks = <&clkc CLKID_AUDIO>,
|
||||
<&clkc CLKID_MPLL0>,
|
||||
<&clkc CLKID_MPLL1>,
|
||||
<&clkc CLKID_MPLL2>,
|
||||
<&clkc CLKID_MPLL3>,
|
||||
<&clkc CLKID_HIFI_PLL>,
|
||||
<&clkc CLKID_FCLK_DIV3>,
|
||||
<&clkc CLKID_FCLK_DIV4>,
|
||||
<&clkc CLKID_GP0_PLL>;
|
||||
clock-names = "pclk",
|
||||
"mst_in0",
|
||||
"mst_in1",
|
||||
"mst_in2",
|
||||
"mst_in3",
|
||||
"mst_in4",
|
||||
"mst_in5",
|
||||
"mst_in6",
|
||||
"mst_in7";
|
||||
resets = <&reset RESET_AUDIO>;
|
||||
};
|
|
@ -68,6 +68,7 @@ struct clk_core {
|
|||
unsigned long max_rate;
|
||||
unsigned long accuracy;
|
||||
int phase;
|
||||
struct clk_duty duty;
|
||||
struct hlist_head children;
|
||||
struct hlist_node child_node;
|
||||
struct hlist_head clks;
|
||||
|
@ -2402,6 +2403,172 @@ int clk_get_phase(struct clk *clk)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(clk_get_phase);
|
||||
|
||||
static void clk_core_reset_duty_cycle_nolock(struct clk_core *core)
|
||||
{
|
||||
/* Assume a default value of 50% */
|
||||
core->duty.num = 1;
|
||||
core->duty.den = 2;
|
||||
}
|
||||
|
||||
static int clk_core_update_duty_cycle_parent_nolock(struct clk_core *core);
|
||||
|
||||
static int clk_core_update_duty_cycle_nolock(struct clk_core *core)
|
||||
{
|
||||
struct clk_duty *duty = &core->duty;
|
||||
int ret = 0;
|
||||
|
||||
if (!core->ops->get_duty_cycle)
|
||||
return clk_core_update_duty_cycle_parent_nolock(core);
|
||||
|
||||
ret = core->ops->get_duty_cycle(core->hw, duty);
|
||||
if (ret)
|
||||
goto reset;
|
||||
|
||||
/* Don't trust the clock provider too much */
|
||||
if (duty->den == 0 || duty->num > duty->den) {
|
||||
ret = -EINVAL;
|
||||
goto reset;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
reset:
|
||||
clk_core_reset_duty_cycle_nolock(core);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int clk_core_update_duty_cycle_parent_nolock(struct clk_core *core)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (core->parent &&
|
||||
core->flags & CLK_DUTY_CYCLE_PARENT) {
|
||||
ret = clk_core_update_duty_cycle_nolock(core->parent);
|
||||
memcpy(&core->duty, &core->parent->duty, sizeof(core->duty));
|
||||
} else {
|
||||
clk_core_reset_duty_cycle_nolock(core);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int clk_core_set_duty_cycle_parent_nolock(struct clk_core *core,
|
||||
struct clk_duty *duty);
|
||||
|
||||
static int clk_core_set_duty_cycle_nolock(struct clk_core *core,
|
||||
struct clk_duty *duty)
|
||||
{
|
||||
int ret;
|
||||
|
||||
lockdep_assert_held(&prepare_lock);
|
||||
|
||||
if (clk_core_rate_is_protected(core))
|
||||
return -EBUSY;
|
||||
|
||||
trace_clk_set_duty_cycle(core, duty);
|
||||
|
||||
if (!core->ops->set_duty_cycle)
|
||||
return clk_core_set_duty_cycle_parent_nolock(core, duty);
|
||||
|
||||
ret = core->ops->set_duty_cycle(core->hw, duty);
|
||||
if (!ret)
|
||||
memcpy(&core->duty, duty, sizeof(*duty));
|
||||
|
||||
trace_clk_set_duty_cycle_complete(core, duty);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int clk_core_set_duty_cycle_parent_nolock(struct clk_core *core,
|
||||
struct clk_duty *duty)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (core->parent &&
|
||||
core->flags & (CLK_DUTY_CYCLE_PARENT | CLK_SET_RATE_PARENT)) {
|
||||
ret = clk_core_set_duty_cycle_nolock(core->parent, duty);
|
||||
memcpy(&core->duty, &core->parent->duty, sizeof(core->duty));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* clk_set_duty_cycle - adjust the duty cycle ratio of a clock signal
|
||||
* @clk: clock signal source
|
||||
* @num: numerator of the duty cycle ratio to be applied
|
||||
* @den: denominator of the duty cycle ratio to be applied
|
||||
*
|
||||
* Apply the duty cycle ratio if the ratio is valid and the clock can
|
||||
* perform this operation
|
||||
*
|
||||
* Returns (0) on success, a negative errno otherwise.
|
||||
*/
|
||||
int clk_set_duty_cycle(struct clk *clk, unsigned int num, unsigned int den)
|
||||
{
|
||||
int ret;
|
||||
struct clk_duty duty;
|
||||
|
||||
if (!clk)
|
||||
return 0;
|
||||
|
||||
/* sanity check the ratio */
|
||||
if (den == 0 || num > den)
|
||||
return -EINVAL;
|
||||
|
||||
duty.num = num;
|
||||
duty.den = den;
|
||||
|
||||
clk_prepare_lock();
|
||||
|
||||
if (clk->exclusive_count)
|
||||
clk_core_rate_unprotect(clk->core);
|
||||
|
||||
ret = clk_core_set_duty_cycle_nolock(clk->core, &duty);
|
||||
|
||||
if (clk->exclusive_count)
|
||||
clk_core_rate_protect(clk->core);
|
||||
|
||||
clk_prepare_unlock();
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(clk_set_duty_cycle);
|
||||
|
||||
static int clk_core_get_scaled_duty_cycle(struct clk_core *core,
|
||||
unsigned int scale)
|
||||
{
|
||||
struct clk_duty *duty = &core->duty;
|
||||
int ret;
|
||||
|
||||
clk_prepare_lock();
|
||||
|
||||
ret = clk_core_update_duty_cycle_nolock(core);
|
||||
if (!ret)
|
||||
ret = mult_frac(scale, duty->num, duty->den);
|
||||
|
||||
clk_prepare_unlock();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* clk_get_scaled_duty_cycle - return the duty cycle ratio of a clock signal
|
||||
* @clk: clock signal source
|
||||
* @scale: scaling factor to be applied to represent the ratio as an integer
|
||||
*
|
||||
* Returns the duty cycle ratio of a clock node multiplied by the provided
|
||||
* scaling factor, or negative errno on error.
|
||||
*/
|
||||
int clk_get_scaled_duty_cycle(struct clk *clk, unsigned int scale)
|
||||
{
|
||||
if (!clk)
|
||||
return 0;
|
||||
|
||||
return clk_core_get_scaled_duty_cycle(clk->core, scale);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(clk_get_scaled_duty_cycle);
|
||||
|
||||
/**
|
||||
* clk_is_match - check if two clk's point to the same hardware clock
|
||||
* @p: clk compared against q
|
||||
|
@ -2455,12 +2622,13 @@ static void clk_summary_show_one(struct seq_file *s, struct clk_core *c,
|
|||
if (!c)
|
||||
return;
|
||||
|
||||
seq_printf(s, "%*s%-*s %7d %8d %8d %11lu %10lu %-3d\n",
|
||||
seq_printf(s, "%*s%-*s %7d %8d %8d %11lu %10lu %5d %6d\n",
|
||||
level * 3 + 1, "",
|
||||
30 - level * 3, c->name,
|
||||
c->enable_count, c->prepare_count, c->protect_count,
|
||||
clk_core_get_rate(c), clk_core_get_accuracy(c),
|
||||
clk_core_get_phase(c));
|
||||
clk_core_get_phase(c),
|
||||
clk_core_get_scaled_duty_cycle(c, 100000));
|
||||
}
|
||||
|
||||
static void clk_summary_show_subtree(struct seq_file *s, struct clk_core *c,
|
||||
|
@ -2482,9 +2650,9 @@ static int clk_summary_show(struct seq_file *s, void *data)
|
|||
struct clk_core *c;
|
||||
struct hlist_head **lists = (struct hlist_head **)s->private;
|
||||
|
||||
seq_puts(s, " enable prepare protect \n");
|
||||
seq_puts(s, " clock count count count rate accuracy phase\n");
|
||||
seq_puts(s, "----------------------------------------------------------------------------------------\n");
|
||||
seq_puts(s, " enable prepare protect duty\n");
|
||||
seq_puts(s, " clock count count count rate accuracy phase cycle\n");
|
||||
seq_puts(s, "---------------------------------------------------------------------------------------------\n");
|
||||
|
||||
clk_prepare_lock();
|
||||
|
||||
|
@ -2511,6 +2679,8 @@ static void clk_dump_one(struct seq_file *s, struct clk_core *c, int level)
|
|||
seq_printf(s, "\"rate\": %lu,", clk_core_get_rate(c));
|
||||
seq_printf(s, "\"accuracy\": %lu,", clk_core_get_accuracy(c));
|
||||
seq_printf(s, "\"phase\": %d", clk_core_get_phase(c));
|
||||
seq_printf(s, "\"duty_cycle\": %u",
|
||||
clk_core_get_scaled_duty_cycle(c, 100000));
|
||||
}
|
||||
|
||||
static void clk_dump_subtree(struct seq_file *s, struct clk_core *c, int level)
|
||||
|
@ -2572,6 +2742,7 @@ static const struct {
|
|||
ENTRY(CLK_SET_RATE_UNGATE),
|
||||
ENTRY(CLK_IS_CRITICAL),
|
||||
ENTRY(CLK_OPS_PARENT_ENABLE),
|
||||
ENTRY(CLK_DUTY_CYCLE_PARENT),
|
||||
#undef ENTRY
|
||||
};
|
||||
|
||||
|
@ -2610,6 +2781,17 @@ static int possible_parents_show(struct seq_file *s, void *data)
|
|||
}
|
||||
DEFINE_SHOW_ATTRIBUTE(possible_parents);
|
||||
|
||||
static int clk_duty_cycle_show(struct seq_file *s, void *data)
|
||||
{
|
||||
struct clk_core *core = s->private;
|
||||
struct clk_duty *duty = &core->duty;
|
||||
|
||||
seq_printf(s, "%u/%u\n", duty->num, duty->den);
|
||||
|
||||
return 0;
|
||||
}
|
||||
DEFINE_SHOW_ATTRIBUTE(clk_duty_cycle);
|
||||
|
||||
static void clk_debug_create_one(struct clk_core *core, struct dentry *pdentry)
|
||||
{
|
||||
struct dentry *root;
|
||||
|
@ -2628,6 +2810,8 @@ static void clk_debug_create_one(struct clk_core *core, struct dentry *pdentry)
|
|||
debugfs_create_u32("clk_enable_count", 0444, root, &core->enable_count);
|
||||
debugfs_create_u32("clk_protect_count", 0444, root, &core->protect_count);
|
||||
debugfs_create_u32("clk_notifier_count", 0444, root, &core->notifier_count);
|
||||
debugfs_create_file("clk_duty_cycle", 0444, root, core,
|
||||
&clk_duty_cycle_fops);
|
||||
|
||||
if (core->num_parents > 1)
|
||||
debugfs_create_file("clk_possible_parents", 0444, root, core,
|
||||
|
@ -2845,6 +3029,11 @@ static int __clk_core_init(struct clk_core *core)
|
|||
else
|
||||
core->phase = 0;
|
||||
|
||||
/*
|
||||
* Set clk's duty cycle.
|
||||
*/
|
||||
clk_core_update_duty_cycle_nolock(core);
|
||||
|
||||
/*
|
||||
* Set clk's rate. The preferred method is to use .recalc_rate. For
|
||||
* simple clocks and lazy developers the default fallback is to use the
|
||||
|
|
|
@ -1,13 +1,19 @@
|
|||
config COMMON_CLK_AMLOGIC
|
||||
bool
|
||||
depends on OF
|
||||
depends on ARCH_MESON || COMPILE_TEST
|
||||
select COMMON_CLK_REGMAP_MESON
|
||||
|
||||
config COMMON_CLK_AMLOGIC_AUDIO
|
||||
bool
|
||||
depends on ARCH_MESON || COMPILE_TEST
|
||||
select COMMON_CLK_AMLOGIC
|
||||
|
||||
config COMMON_CLK_MESON_AO
|
||||
bool
|
||||
depends on OF
|
||||
depends on ARCH_MESON || COMPILE_TEST
|
||||
select COMMON_CLK_REGMAP_MESON
|
||||
select RESET_CONTROLLER
|
||||
|
||||
config COMMON_CLK_REGMAP_MESON
|
||||
bool
|
||||
|
@ -15,9 +21,8 @@ config COMMON_CLK_REGMAP_MESON
|
|||
|
||||
config COMMON_CLK_MESON8B
|
||||
bool
|
||||
depends on COMMON_CLK_AMLOGIC
|
||||
select COMMON_CLK_AMLOGIC
|
||||
select RESET_CONTROLLER
|
||||
select COMMON_CLK_REGMAP_MESON
|
||||
help
|
||||
Support for the clock controller on AmLogic S802 (Meson8),
|
||||
S805 (Meson8b) and S812 (Meson8m2) devices. Say Y if you
|
||||
|
@ -25,10 +30,8 @@ config COMMON_CLK_MESON8B
|
|||
|
||||
config COMMON_CLK_GXBB
|
||||
bool
|
||||
depends on COMMON_CLK_AMLOGIC
|
||||
select RESET_CONTROLLER
|
||||
select COMMON_CLK_AMLOGIC
|
||||
select COMMON_CLK_MESON_AO
|
||||
select COMMON_CLK_REGMAP_MESON
|
||||
select MFD_SYSCON
|
||||
help
|
||||
Support for the clock controller on AmLogic S905 devices, aka gxbb.
|
||||
|
@ -36,11 +39,18 @@ config COMMON_CLK_GXBB
|
|||
|
||||
config COMMON_CLK_AXG
|
||||
bool
|
||||
depends on COMMON_CLK_AMLOGIC
|
||||
select RESET_CONTROLLER
|
||||
select COMMON_CLK_AMLOGIC
|
||||
select COMMON_CLK_MESON_AO
|
||||
select COMMON_CLK_REGMAP_MESON
|
||||
select MFD_SYSCON
|
||||
help
|
||||
Support for the clock controller on AmLogic A113D devices, aka axg.
|
||||
Say Y if you want peripherals and CPU frequency scaling to work.
|
||||
|
||||
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
|
||||
help
|
||||
Support for the audio clock controller on AmLogic A113D devices,
|
||||
aka axg, Say Y if you want audio subsystem to work.
|
||||
|
|
|
@ -2,9 +2,11 @@
|
|||
# Makefile for Meson specific clk
|
||||
#
|
||||
|
||||
obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-pll.o clk-mpll.o clk-audio-divider.o
|
||||
obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-pll.o clk-mpll.o clk-phase.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_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
|
||||
|
|
|
@ -0,0 +1,845 @@
|
|||
// SPDX-License-Identifier: (GPL-2.0 OR MIT)
|
||||
/*
|
||||
* Copyright (c) 2018 BayLibre, SAS.
|
||||
* Author: Jerome Brunet <jbrunet@baylibre.com>
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "clkc-audio.h"
|
||||
#include "axg-audio.h"
|
||||
|
||||
#define AXG_MST_IN_COUNT 8
|
||||
#define AXG_SLV_SCLK_COUNT 10
|
||||
#define AXG_SLV_LRCLK_COUNT 10
|
||||
|
||||
#define AXG_AUD_GATE(_name, _reg, _bit, _pname, _iflags) \
|
||||
struct clk_regmap axg_##_name = { \
|
||||
.data = &(struct clk_regmap_gate_data){ \
|
||||
.offset = (_reg), \
|
||||
.bit_idx = (_bit), \
|
||||
}, \
|
||||
.hw.init = &(struct clk_init_data) { \
|
||||
.name = "axg_"#_name, \
|
||||
.ops = &clk_regmap_gate_ops, \
|
||||
.parent_names = (const char *[]){ _pname }, \
|
||||
.num_parents = 1, \
|
||||
.flags = CLK_DUTY_CYCLE_PARENT | (_iflags), \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define AXG_AUD_MUX(_name, _reg, _mask, _shift, _dflags, _pnames, _iflags) \
|
||||
struct clk_regmap axg_##_name = { \
|
||||
.data = &(struct clk_regmap_mux_data){ \
|
||||
.offset = (_reg), \
|
||||
.mask = (_mask), \
|
||||
.shift = (_shift), \
|
||||
.flags = (_dflags), \
|
||||
}, \
|
||||
.hw.init = &(struct clk_init_data){ \
|
||||
.name = "axg_"#_name, \
|
||||
.ops = &clk_regmap_mux_ops, \
|
||||
.parent_names = (_pnames), \
|
||||
.num_parents = ARRAY_SIZE(_pnames), \
|
||||
.flags = CLK_DUTY_CYCLE_PARENT | (_iflags), \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define AXG_AUD_DIV(_name, _reg, _shift, _width, _dflags, _pname, _iflags) \
|
||||
struct clk_regmap axg_##_name = { \
|
||||
.data = &(struct clk_regmap_div_data){ \
|
||||
.offset = (_reg), \
|
||||
.shift = (_shift), \
|
||||
.width = (_width), \
|
||||
.flags = (_dflags), \
|
||||
}, \
|
||||
.hw.init = &(struct clk_init_data){ \
|
||||
.name = "axg_"#_name, \
|
||||
.ops = &clk_regmap_divider_ops, \
|
||||
.parent_names = (const char *[]) { _pname }, \
|
||||
.num_parents = 1, \
|
||||
.flags = (_iflags), \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define AXG_PCLK_GATE(_name, _bit) \
|
||||
AXG_AUD_GATE(_name, AUDIO_CLK_GATE_EN, _bit, "axg_audio_pclk", 0)
|
||||
|
||||
/* Audio peripheral clocks */
|
||||
static AXG_PCLK_GATE(ddr_arb, 0);
|
||||
static AXG_PCLK_GATE(pdm, 1);
|
||||
static AXG_PCLK_GATE(tdmin_a, 2);
|
||||
static AXG_PCLK_GATE(tdmin_b, 3);
|
||||
static AXG_PCLK_GATE(tdmin_c, 4);
|
||||
static AXG_PCLK_GATE(tdmin_lb, 5);
|
||||
static AXG_PCLK_GATE(tdmout_a, 6);
|
||||
static AXG_PCLK_GATE(tdmout_b, 7);
|
||||
static AXG_PCLK_GATE(tdmout_c, 8);
|
||||
static AXG_PCLK_GATE(frddr_a, 9);
|
||||
static AXG_PCLK_GATE(frddr_b, 10);
|
||||
static AXG_PCLK_GATE(frddr_c, 11);
|
||||
static AXG_PCLK_GATE(toddr_a, 12);
|
||||
static AXG_PCLK_GATE(toddr_b, 13);
|
||||
static AXG_PCLK_GATE(toddr_c, 14);
|
||||
static AXG_PCLK_GATE(loopback, 15);
|
||||
static AXG_PCLK_GATE(spdifin, 16);
|
||||
static AXG_PCLK_GATE(spdifout, 17);
|
||||
static AXG_PCLK_GATE(resample, 18);
|
||||
static AXG_PCLK_GATE(power_detect, 19);
|
||||
|
||||
/* Audio Master Clocks */
|
||||
static const char * const mst_mux_parent_names[] = {
|
||||
"axg_mst_in0", "axg_mst_in1", "axg_mst_in2", "axg_mst_in3",
|
||||
"axg_mst_in4", "axg_mst_in5", "axg_mst_in6", "axg_mst_in7",
|
||||
};
|
||||
|
||||
#define AXG_MST_MCLK_MUX(_name, _reg) \
|
||||
AXG_AUD_MUX(_name##_sel, _reg, 0x7, 24, CLK_MUX_ROUND_CLOSEST, \
|
||||
mst_mux_parent_names, CLK_SET_RATE_PARENT)
|
||||
|
||||
static AXG_MST_MCLK_MUX(mst_a_mclk, AUDIO_MCLK_A_CTRL);
|
||||
static AXG_MST_MCLK_MUX(mst_b_mclk, AUDIO_MCLK_B_CTRL);
|
||||
static AXG_MST_MCLK_MUX(mst_c_mclk, AUDIO_MCLK_C_CTRL);
|
||||
static AXG_MST_MCLK_MUX(mst_d_mclk, AUDIO_MCLK_D_CTRL);
|
||||
static AXG_MST_MCLK_MUX(mst_e_mclk, AUDIO_MCLK_E_CTRL);
|
||||
static AXG_MST_MCLK_MUX(mst_f_mclk, AUDIO_MCLK_F_CTRL);
|
||||
static AXG_MST_MCLK_MUX(spdifout_clk, AUDIO_CLK_SPDIFOUT_CTRL);
|
||||
static AXG_MST_MCLK_MUX(spdifin_clk, AUDIO_CLK_SPDIFIN_CTRL);
|
||||
static AXG_MST_MCLK_MUX(pdm_dclk, AUDIO_CLK_PDMIN_CTRL0);
|
||||
static AXG_MST_MCLK_MUX(pdm_sysclk, AUDIO_CLK_PDMIN_CTRL1);
|
||||
|
||||
#define AXG_MST_MCLK_DIV(_name, _reg) \
|
||||
AXG_AUD_DIV(_name##_div, _reg, 0, 16, CLK_DIVIDER_ROUND_CLOSEST, \
|
||||
"axg_"#_name"_sel", CLK_SET_RATE_PARENT) \
|
||||
|
||||
static AXG_MST_MCLK_DIV(mst_a_mclk, AUDIO_MCLK_A_CTRL);
|
||||
static AXG_MST_MCLK_DIV(mst_b_mclk, AUDIO_MCLK_B_CTRL);
|
||||
static AXG_MST_MCLK_DIV(mst_c_mclk, AUDIO_MCLK_C_CTRL);
|
||||
static AXG_MST_MCLK_DIV(mst_d_mclk, AUDIO_MCLK_D_CTRL);
|
||||
static AXG_MST_MCLK_DIV(mst_e_mclk, AUDIO_MCLK_E_CTRL);
|
||||
static AXG_MST_MCLK_DIV(mst_f_mclk, AUDIO_MCLK_F_CTRL);
|
||||
static AXG_MST_MCLK_DIV(spdifout_clk, AUDIO_CLK_SPDIFOUT_CTRL);
|
||||
static AXG_MST_MCLK_DIV(spdifin_clk, AUDIO_CLK_SPDIFIN_CTRL);
|
||||
static AXG_MST_MCLK_DIV(pdm_dclk, AUDIO_CLK_PDMIN_CTRL0);
|
||||
static AXG_MST_MCLK_DIV(pdm_sysclk, AUDIO_CLK_PDMIN_CTRL1);
|
||||
|
||||
#define AXG_MST_MCLK_GATE(_name, _reg) \
|
||||
AXG_AUD_GATE(_name, _reg, 31, "axg_"#_name"_div", \
|
||||
CLK_SET_RATE_PARENT)
|
||||
|
||||
static AXG_MST_MCLK_GATE(mst_a_mclk, AUDIO_MCLK_A_CTRL);
|
||||
static AXG_MST_MCLK_GATE(mst_b_mclk, AUDIO_MCLK_B_CTRL);
|
||||
static AXG_MST_MCLK_GATE(mst_c_mclk, AUDIO_MCLK_C_CTRL);
|
||||
static AXG_MST_MCLK_GATE(mst_d_mclk, AUDIO_MCLK_D_CTRL);
|
||||
static AXG_MST_MCLK_GATE(mst_e_mclk, AUDIO_MCLK_E_CTRL);
|
||||
static AXG_MST_MCLK_GATE(mst_f_mclk, AUDIO_MCLK_F_CTRL);
|
||||
static AXG_MST_MCLK_GATE(spdifout_clk, AUDIO_CLK_SPDIFOUT_CTRL);
|
||||
static AXG_MST_MCLK_GATE(spdifin_clk, AUDIO_CLK_SPDIFIN_CTRL);
|
||||
static AXG_MST_MCLK_GATE(pdm_dclk, AUDIO_CLK_PDMIN_CTRL0);
|
||||
static AXG_MST_MCLK_GATE(pdm_sysclk, AUDIO_CLK_PDMIN_CTRL1);
|
||||
|
||||
/* Sample Clocks */
|
||||
#define AXG_MST_SCLK_PRE_EN(_name, _reg) \
|
||||
AXG_AUD_GATE(mst_##_name##_sclk_pre_en, _reg, 31, \
|
||||
"axg_mst_"#_name"_mclk", 0)
|
||||
|
||||
static AXG_MST_SCLK_PRE_EN(a, AUDIO_MST_A_SCLK_CTRL0);
|
||||
static AXG_MST_SCLK_PRE_EN(b, AUDIO_MST_B_SCLK_CTRL0);
|
||||
static AXG_MST_SCLK_PRE_EN(c, AUDIO_MST_C_SCLK_CTRL0);
|
||||
static AXG_MST_SCLK_PRE_EN(d, AUDIO_MST_D_SCLK_CTRL0);
|
||||
static AXG_MST_SCLK_PRE_EN(e, AUDIO_MST_E_SCLK_CTRL0);
|
||||
static AXG_MST_SCLK_PRE_EN(f, AUDIO_MST_F_SCLK_CTRL0);
|
||||
|
||||
#define AXG_AUD_SCLK_DIV(_name, _reg, _div_shift, _div_width, \
|
||||
_hi_shift, _hi_width, _pname, _iflags) \
|
||||
struct clk_regmap axg_##_name = { \
|
||||
.data = &(struct meson_sclk_div_data) { \
|
||||
.div = { \
|
||||
.reg_off = (_reg), \
|
||||
.shift = (_div_shift), \
|
||||
.width = (_div_width), \
|
||||
}, \
|
||||
.hi = { \
|
||||
.reg_off = (_reg), \
|
||||
.shift = (_hi_shift), \
|
||||
.width = (_hi_width), \
|
||||
}, \
|
||||
}, \
|
||||
.hw.init = &(struct clk_init_data) { \
|
||||
.name = "axg_"#_name, \
|
||||
.ops = &meson_sclk_div_ops, \
|
||||
.parent_names = (const char *[]) { _pname }, \
|
||||
.num_parents = 1, \
|
||||
.flags = (_iflags), \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define AXG_MST_SCLK_DIV(_name, _reg) \
|
||||
AXG_AUD_SCLK_DIV(mst_##_name##_sclk_div, _reg, 20, 10, 0, 0, \
|
||||
"axg_mst_"#_name"_sclk_pre_en", \
|
||||
CLK_SET_RATE_PARENT)
|
||||
|
||||
static AXG_MST_SCLK_DIV(a, AUDIO_MST_A_SCLK_CTRL0);
|
||||
static AXG_MST_SCLK_DIV(b, AUDIO_MST_B_SCLK_CTRL0);
|
||||
static AXG_MST_SCLK_DIV(c, AUDIO_MST_C_SCLK_CTRL0);
|
||||
static AXG_MST_SCLK_DIV(d, AUDIO_MST_D_SCLK_CTRL0);
|
||||
static AXG_MST_SCLK_DIV(e, AUDIO_MST_E_SCLK_CTRL0);
|
||||
static AXG_MST_SCLK_DIV(f, AUDIO_MST_F_SCLK_CTRL0);
|
||||
|
||||
#define AXG_MST_SCLK_POST_EN(_name, _reg) \
|
||||
AXG_AUD_GATE(mst_##_name##_sclk_post_en, _reg, 30, \
|
||||
"axg_mst_"#_name"_sclk_div", CLK_SET_RATE_PARENT)
|
||||
|
||||
static AXG_MST_SCLK_POST_EN(a, AUDIO_MST_A_SCLK_CTRL0);
|
||||
static AXG_MST_SCLK_POST_EN(b, AUDIO_MST_B_SCLK_CTRL0);
|
||||
static AXG_MST_SCLK_POST_EN(c, AUDIO_MST_C_SCLK_CTRL0);
|
||||
static AXG_MST_SCLK_POST_EN(d, AUDIO_MST_D_SCLK_CTRL0);
|
||||
static AXG_MST_SCLK_POST_EN(e, AUDIO_MST_E_SCLK_CTRL0);
|
||||
static AXG_MST_SCLK_POST_EN(f, AUDIO_MST_F_SCLK_CTRL0);
|
||||
|
||||
#define AXG_AUD_TRIPHASE(_name, _reg, _width, _shift0, _shift1, _shift2, \
|
||||
_pname, _iflags) \
|
||||
struct clk_regmap axg_##_name = { \
|
||||
.data = &(struct meson_clk_triphase_data) { \
|
||||
.ph0 = { \
|
||||
.reg_off = (_reg), \
|
||||
.shift = (_shift0), \
|
||||
.width = (_width), \
|
||||
}, \
|
||||
.ph1 = { \
|
||||
.reg_off = (_reg), \
|
||||
.shift = (_shift1), \
|
||||
.width = (_width), \
|
||||
}, \
|
||||
.ph2 = { \
|
||||
.reg_off = (_reg), \
|
||||
.shift = (_shift2), \
|
||||
.width = (_width), \
|
||||
}, \
|
||||
}, \
|
||||
.hw.init = &(struct clk_init_data) { \
|
||||
.name = "axg_"#_name, \
|
||||
.ops = &meson_clk_triphase_ops, \
|
||||
.parent_names = (const char *[]) { _pname }, \
|
||||
.num_parents = 1, \
|
||||
.flags = CLK_DUTY_CYCLE_PARENT | (_iflags), \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define AXG_MST_SCLK(_name, _reg) \
|
||||
AXG_AUD_TRIPHASE(mst_##_name##_sclk, _reg, 1, 0, 2, 4, \
|
||||
"axg_mst_"#_name"_sclk_post_en", CLK_SET_RATE_PARENT)
|
||||
|
||||
static AXG_MST_SCLK(a, AUDIO_MST_A_SCLK_CTRL1);
|
||||
static AXG_MST_SCLK(b, AUDIO_MST_B_SCLK_CTRL1);
|
||||
static AXG_MST_SCLK(c, AUDIO_MST_C_SCLK_CTRL1);
|
||||
static AXG_MST_SCLK(d, AUDIO_MST_D_SCLK_CTRL1);
|
||||
static AXG_MST_SCLK(e, AUDIO_MST_E_SCLK_CTRL1);
|
||||
static AXG_MST_SCLK(f, AUDIO_MST_F_SCLK_CTRL1);
|
||||
|
||||
#define AXG_MST_LRCLK_DIV(_name, _reg) \
|
||||
AXG_AUD_SCLK_DIV(mst_##_name##_lrclk_div, _reg, 0, 10, 10, 10, \
|
||||
"axg_mst_"#_name"_sclk_post_en", 0) \
|
||||
|
||||
static AXG_MST_LRCLK_DIV(a, AUDIO_MST_A_SCLK_CTRL0);
|
||||
static AXG_MST_LRCLK_DIV(b, AUDIO_MST_B_SCLK_CTRL0);
|
||||
static AXG_MST_LRCLK_DIV(c, AUDIO_MST_C_SCLK_CTRL0);
|
||||
static AXG_MST_LRCLK_DIV(d, AUDIO_MST_D_SCLK_CTRL0);
|
||||
static AXG_MST_LRCLK_DIV(e, AUDIO_MST_E_SCLK_CTRL0);
|
||||
static AXG_MST_LRCLK_DIV(f, AUDIO_MST_F_SCLK_CTRL0);
|
||||
|
||||
#define AXG_MST_LRCLK(_name, _reg) \
|
||||
AXG_AUD_TRIPHASE(mst_##_name##_lrclk, _reg, 1, 1, 3, 5, \
|
||||
"axg_mst_"#_name"_lrclk_div", CLK_SET_RATE_PARENT)
|
||||
|
||||
static AXG_MST_LRCLK(a, AUDIO_MST_A_SCLK_CTRL1);
|
||||
static AXG_MST_LRCLK(b, AUDIO_MST_B_SCLK_CTRL1);
|
||||
static AXG_MST_LRCLK(c, AUDIO_MST_C_SCLK_CTRL1);
|
||||
static AXG_MST_LRCLK(d, AUDIO_MST_D_SCLK_CTRL1);
|
||||
static AXG_MST_LRCLK(e, AUDIO_MST_E_SCLK_CTRL1);
|
||||
static AXG_MST_LRCLK(f, AUDIO_MST_F_SCLK_CTRL1);
|
||||
|
||||
static const char * const tdm_sclk_parent_names[] = {
|
||||
"axg_mst_a_sclk", "axg_mst_b_sclk", "axg_mst_c_sclk",
|
||||
"axg_mst_d_sclk", "axg_mst_e_sclk", "axg_mst_f_sclk",
|
||||
"axg_slv_sclk0", "axg_slv_sclk1", "axg_slv_sclk2",
|
||||
"axg_slv_sclk3", "axg_slv_sclk4", "axg_slv_sclk5",
|
||||
"axg_slv_sclk6", "axg_slv_sclk7", "axg_slv_sclk8",
|
||||
"axg_slv_sclk9"
|
||||
};
|
||||
|
||||
#define AXG_TDM_SCLK_MUX(_name, _reg) \
|
||||
AXG_AUD_MUX(tdm##_name##_sclk_sel, _reg, 0xf, 24, \
|
||||
CLK_MUX_ROUND_CLOSEST, \
|
||||
tdm_sclk_parent_names, 0)
|
||||
|
||||
static AXG_TDM_SCLK_MUX(in_a, AUDIO_CLK_TDMIN_A_CTRL);
|
||||
static AXG_TDM_SCLK_MUX(in_b, AUDIO_CLK_TDMIN_B_CTRL);
|
||||
static AXG_TDM_SCLK_MUX(in_c, AUDIO_CLK_TDMIN_C_CTRL);
|
||||
static AXG_TDM_SCLK_MUX(in_lb, AUDIO_CLK_TDMIN_LB_CTRL);
|
||||
static AXG_TDM_SCLK_MUX(out_a, AUDIO_CLK_TDMOUT_A_CTRL);
|
||||
static AXG_TDM_SCLK_MUX(out_b, AUDIO_CLK_TDMOUT_B_CTRL);
|
||||
static AXG_TDM_SCLK_MUX(out_c, AUDIO_CLK_TDMOUT_C_CTRL);
|
||||
|
||||
#define AXG_TDM_SCLK_PRE_EN(_name, _reg) \
|
||||
AXG_AUD_GATE(tdm##_name##_sclk_pre_en, _reg, 31, \
|
||||
"axg_tdm"#_name"_sclk_sel", CLK_SET_RATE_PARENT)
|
||||
|
||||
static AXG_TDM_SCLK_PRE_EN(in_a, AUDIO_CLK_TDMIN_A_CTRL);
|
||||
static AXG_TDM_SCLK_PRE_EN(in_b, AUDIO_CLK_TDMIN_B_CTRL);
|
||||
static AXG_TDM_SCLK_PRE_EN(in_c, AUDIO_CLK_TDMIN_C_CTRL);
|
||||
static AXG_TDM_SCLK_PRE_EN(in_lb, AUDIO_CLK_TDMIN_LB_CTRL);
|
||||
static AXG_TDM_SCLK_PRE_EN(out_a, AUDIO_CLK_TDMOUT_A_CTRL);
|
||||
static AXG_TDM_SCLK_PRE_EN(out_b, AUDIO_CLK_TDMOUT_B_CTRL);
|
||||
static AXG_TDM_SCLK_PRE_EN(out_c, AUDIO_CLK_TDMOUT_C_CTRL);
|
||||
|
||||
#define AXG_TDM_SCLK_POST_EN(_name, _reg) \
|
||||
AXG_AUD_GATE(tdm##_name##_sclk_post_en, _reg, 30, \
|
||||
"axg_tdm"#_name"_sclk_pre_en", CLK_SET_RATE_PARENT)
|
||||
|
||||
static AXG_TDM_SCLK_POST_EN(in_a, AUDIO_CLK_TDMIN_A_CTRL);
|
||||
static AXG_TDM_SCLK_POST_EN(in_b, AUDIO_CLK_TDMIN_B_CTRL);
|
||||
static AXG_TDM_SCLK_POST_EN(in_c, AUDIO_CLK_TDMIN_C_CTRL);
|
||||
static AXG_TDM_SCLK_POST_EN(in_lb, AUDIO_CLK_TDMIN_LB_CTRL);
|
||||
static AXG_TDM_SCLK_POST_EN(out_a, AUDIO_CLK_TDMOUT_A_CTRL);
|
||||
static AXG_TDM_SCLK_POST_EN(out_b, AUDIO_CLK_TDMOUT_B_CTRL);
|
||||
static AXG_TDM_SCLK_POST_EN(out_c, AUDIO_CLK_TDMOUT_C_CTRL);
|
||||
|
||||
#define AXG_TDM_SCLK(_name, _reg) \
|
||||
struct clk_regmap axg_tdm##_name##_sclk = { \
|
||||
.data = &(struct meson_clk_phase_data) { \
|
||||
.ph = { \
|
||||
.reg_off = (_reg), \
|
||||
.shift = 29, \
|
||||
.width = 1, \
|
||||
}, \
|
||||
}, \
|
||||
.hw.init = &(struct clk_init_data) { \
|
||||
.name = "axg_tdm"#_name"_sclk", \
|
||||
.ops = &meson_clk_phase_ops, \
|
||||
.parent_names = (const char *[]) \
|
||||
{ "axg_tdm"#_name"_sclk_post_en" }, \
|
||||
.num_parents = 1, \
|
||||
.flags = CLK_DUTY_CYCLE_PARENT | CLK_SET_RATE_PARENT, \
|
||||
}, \
|
||||
}
|
||||
|
||||
static AXG_TDM_SCLK(in_a, AUDIO_CLK_TDMIN_A_CTRL);
|
||||
static AXG_TDM_SCLK(in_b, AUDIO_CLK_TDMIN_B_CTRL);
|
||||
static AXG_TDM_SCLK(in_c, AUDIO_CLK_TDMIN_C_CTRL);
|
||||
static AXG_TDM_SCLK(in_lb, AUDIO_CLK_TDMIN_LB_CTRL);
|
||||
static AXG_TDM_SCLK(out_a, AUDIO_CLK_TDMOUT_A_CTRL);
|
||||
static AXG_TDM_SCLK(out_b, AUDIO_CLK_TDMOUT_B_CTRL);
|
||||
static AXG_TDM_SCLK(out_c, AUDIO_CLK_TDMOUT_C_CTRL);
|
||||
|
||||
static const char * const tdm_lrclk_parent_names[] = {
|
||||
"axg_mst_a_lrclk", "axg_mst_b_lrclk", "axg_mst_c_lrclk",
|
||||
"axg_mst_d_lrclk", "axg_mst_e_lrclk", "axg_mst_f_lrclk",
|
||||
"axg_slv_lrclk0", "axg_slv_lrclk1", "axg_slv_lrclk2",
|
||||
"axg_slv_lrclk3", "axg_slv_lrclk4", "axg_slv_lrclk5",
|
||||
"axg_slv_lrclk6", "axg_slv_lrclk7", "axg_slv_lrclk8",
|
||||
"axg_slv_lrclk9"
|
||||
};
|
||||
|
||||
#define AXG_TDM_LRLCK(_name, _reg) \
|
||||
AXG_AUD_MUX(tdm##_name##_lrclk, _reg, 0xf, 20, \
|
||||
CLK_MUX_ROUND_CLOSEST, \
|
||||
tdm_lrclk_parent_names, 0)
|
||||
|
||||
static AXG_TDM_LRLCK(in_a, AUDIO_CLK_TDMIN_A_CTRL);
|
||||
static AXG_TDM_LRLCK(in_b, AUDIO_CLK_TDMIN_B_CTRL);
|
||||
static AXG_TDM_LRLCK(in_c, AUDIO_CLK_TDMIN_C_CTRL);
|
||||
static AXG_TDM_LRLCK(in_lb, AUDIO_CLK_TDMIN_LB_CTRL);
|
||||
static AXG_TDM_LRLCK(out_a, AUDIO_CLK_TDMOUT_A_CTRL);
|
||||
static AXG_TDM_LRLCK(out_b, AUDIO_CLK_TDMOUT_B_CTRL);
|
||||
static AXG_TDM_LRLCK(out_c, AUDIO_CLK_TDMOUT_C_CTRL);
|
||||
|
||||
/*
|
||||
* Array of all clocks provided by this provider
|
||||
* The input clocks of the controller will be populated at runtime
|
||||
*/
|
||||
static struct clk_hw_onecell_data axg_audio_hw_onecell_data = {
|
||||
.hws = {
|
||||
[AUD_CLKID_DDR_ARB] = &axg_ddr_arb.hw,
|
||||
[AUD_CLKID_PDM] = &axg_pdm.hw,
|
||||
[AUD_CLKID_TDMIN_A] = &axg_tdmin_a.hw,
|
||||
[AUD_CLKID_TDMIN_B] = &axg_tdmin_b.hw,
|
||||
[AUD_CLKID_TDMIN_C] = &axg_tdmin_c.hw,
|
||||
[AUD_CLKID_TDMIN_LB] = &axg_tdmin_lb.hw,
|
||||
[AUD_CLKID_TDMOUT_A] = &axg_tdmout_a.hw,
|
||||
[AUD_CLKID_TDMOUT_B] = &axg_tdmout_b.hw,
|
||||
[AUD_CLKID_TDMOUT_C] = &axg_tdmout_c.hw,
|
||||
[AUD_CLKID_FRDDR_A] = &axg_frddr_a.hw,
|
||||
[AUD_CLKID_FRDDR_B] = &axg_frddr_b.hw,
|
||||
[AUD_CLKID_FRDDR_C] = &axg_frddr_c.hw,
|
||||
[AUD_CLKID_TODDR_A] = &axg_toddr_a.hw,
|
||||
[AUD_CLKID_TODDR_B] = &axg_toddr_b.hw,
|
||||
[AUD_CLKID_TODDR_C] = &axg_toddr_c.hw,
|
||||
[AUD_CLKID_LOOPBACK] = &axg_loopback.hw,
|
||||
[AUD_CLKID_SPDIFIN] = &axg_spdifin.hw,
|
||||
[AUD_CLKID_SPDIFOUT] = &axg_spdifout.hw,
|
||||
[AUD_CLKID_RESAMPLE] = &axg_resample.hw,
|
||||
[AUD_CLKID_POWER_DETECT] = &axg_power_detect.hw,
|
||||
[AUD_CLKID_MST_A_MCLK_SEL] = &axg_mst_a_mclk_sel.hw,
|
||||
[AUD_CLKID_MST_B_MCLK_SEL] = &axg_mst_b_mclk_sel.hw,
|
||||
[AUD_CLKID_MST_C_MCLK_SEL] = &axg_mst_c_mclk_sel.hw,
|
||||
[AUD_CLKID_MST_D_MCLK_SEL] = &axg_mst_d_mclk_sel.hw,
|
||||
[AUD_CLKID_MST_E_MCLK_SEL] = &axg_mst_e_mclk_sel.hw,
|
||||
[AUD_CLKID_MST_F_MCLK_SEL] = &axg_mst_f_mclk_sel.hw,
|
||||
[AUD_CLKID_MST_A_MCLK_DIV] = &axg_mst_a_mclk_div.hw,
|
||||
[AUD_CLKID_MST_B_MCLK_DIV] = &axg_mst_b_mclk_div.hw,
|
||||
[AUD_CLKID_MST_C_MCLK_DIV] = &axg_mst_c_mclk_div.hw,
|
||||
[AUD_CLKID_MST_D_MCLK_DIV] = &axg_mst_d_mclk_div.hw,
|
||||
[AUD_CLKID_MST_E_MCLK_DIV] = &axg_mst_e_mclk_div.hw,
|
||||
[AUD_CLKID_MST_F_MCLK_DIV] = &axg_mst_f_mclk_div.hw,
|
||||
[AUD_CLKID_MST_A_MCLK] = &axg_mst_a_mclk.hw,
|
||||
[AUD_CLKID_MST_B_MCLK] = &axg_mst_b_mclk.hw,
|
||||
[AUD_CLKID_MST_C_MCLK] = &axg_mst_c_mclk.hw,
|
||||
[AUD_CLKID_MST_D_MCLK] = &axg_mst_d_mclk.hw,
|
||||
[AUD_CLKID_MST_E_MCLK] = &axg_mst_e_mclk.hw,
|
||||
[AUD_CLKID_MST_F_MCLK] = &axg_mst_f_mclk.hw,
|
||||
[AUD_CLKID_SPDIFOUT_CLK_SEL] = &axg_spdifout_clk_sel.hw,
|
||||
[AUD_CLKID_SPDIFOUT_CLK_DIV] = &axg_spdifout_clk_div.hw,
|
||||
[AUD_CLKID_SPDIFOUT_CLK] = &axg_spdifout_clk.hw,
|
||||
[AUD_CLKID_SPDIFIN_CLK_SEL] = &axg_spdifin_clk_sel.hw,
|
||||
[AUD_CLKID_SPDIFIN_CLK_DIV] = &axg_spdifin_clk_div.hw,
|
||||
[AUD_CLKID_SPDIFIN_CLK] = &axg_spdifin_clk.hw,
|
||||
[AUD_CLKID_PDM_DCLK_SEL] = &axg_pdm_dclk_sel.hw,
|
||||
[AUD_CLKID_PDM_DCLK_DIV] = &axg_pdm_dclk_div.hw,
|
||||
[AUD_CLKID_PDM_DCLK] = &axg_pdm_dclk.hw,
|
||||
[AUD_CLKID_PDM_SYSCLK_SEL] = &axg_pdm_sysclk_sel.hw,
|
||||
[AUD_CLKID_PDM_SYSCLK_DIV] = &axg_pdm_sysclk_div.hw,
|
||||
[AUD_CLKID_PDM_SYSCLK] = &axg_pdm_sysclk.hw,
|
||||
[AUD_CLKID_MST_A_SCLK_PRE_EN] = &axg_mst_a_sclk_pre_en.hw,
|
||||
[AUD_CLKID_MST_B_SCLK_PRE_EN] = &axg_mst_b_sclk_pre_en.hw,
|
||||
[AUD_CLKID_MST_C_SCLK_PRE_EN] = &axg_mst_c_sclk_pre_en.hw,
|
||||
[AUD_CLKID_MST_D_SCLK_PRE_EN] = &axg_mst_d_sclk_pre_en.hw,
|
||||
[AUD_CLKID_MST_E_SCLK_PRE_EN] = &axg_mst_e_sclk_pre_en.hw,
|
||||
[AUD_CLKID_MST_F_SCLK_PRE_EN] = &axg_mst_f_sclk_pre_en.hw,
|
||||
[AUD_CLKID_MST_A_SCLK_DIV] = &axg_mst_a_sclk_div.hw,
|
||||
[AUD_CLKID_MST_B_SCLK_DIV] = &axg_mst_b_sclk_div.hw,
|
||||
[AUD_CLKID_MST_C_SCLK_DIV] = &axg_mst_c_sclk_div.hw,
|
||||
[AUD_CLKID_MST_D_SCLK_DIV] = &axg_mst_d_sclk_div.hw,
|
||||
[AUD_CLKID_MST_E_SCLK_DIV] = &axg_mst_e_sclk_div.hw,
|
||||
[AUD_CLKID_MST_F_SCLK_DIV] = &axg_mst_f_sclk_div.hw,
|
||||
[AUD_CLKID_MST_A_SCLK_POST_EN] = &axg_mst_a_sclk_post_en.hw,
|
||||
[AUD_CLKID_MST_B_SCLK_POST_EN] = &axg_mst_b_sclk_post_en.hw,
|
||||
[AUD_CLKID_MST_C_SCLK_POST_EN] = &axg_mst_c_sclk_post_en.hw,
|
||||
[AUD_CLKID_MST_D_SCLK_POST_EN] = &axg_mst_d_sclk_post_en.hw,
|
||||
[AUD_CLKID_MST_E_SCLK_POST_EN] = &axg_mst_e_sclk_post_en.hw,
|
||||
[AUD_CLKID_MST_F_SCLK_POST_EN] = &axg_mst_f_sclk_post_en.hw,
|
||||
[AUD_CLKID_MST_A_SCLK] = &axg_mst_a_sclk.hw,
|
||||
[AUD_CLKID_MST_B_SCLK] = &axg_mst_b_sclk.hw,
|
||||
[AUD_CLKID_MST_C_SCLK] = &axg_mst_c_sclk.hw,
|
||||
[AUD_CLKID_MST_D_SCLK] = &axg_mst_d_sclk.hw,
|
||||
[AUD_CLKID_MST_E_SCLK] = &axg_mst_e_sclk.hw,
|
||||
[AUD_CLKID_MST_F_SCLK] = &axg_mst_f_sclk.hw,
|
||||
[AUD_CLKID_MST_A_LRCLK_DIV] = &axg_mst_a_lrclk_div.hw,
|
||||
[AUD_CLKID_MST_B_LRCLK_DIV] = &axg_mst_b_lrclk_div.hw,
|
||||
[AUD_CLKID_MST_C_LRCLK_DIV] = &axg_mst_c_lrclk_div.hw,
|
||||
[AUD_CLKID_MST_D_LRCLK_DIV] = &axg_mst_d_lrclk_div.hw,
|
||||
[AUD_CLKID_MST_E_LRCLK_DIV] = &axg_mst_e_lrclk_div.hw,
|
||||
[AUD_CLKID_MST_F_LRCLK_DIV] = &axg_mst_f_lrclk_div.hw,
|
||||
[AUD_CLKID_MST_A_LRCLK] = &axg_mst_a_lrclk.hw,
|
||||
[AUD_CLKID_MST_B_LRCLK] = &axg_mst_b_lrclk.hw,
|
||||
[AUD_CLKID_MST_C_LRCLK] = &axg_mst_c_lrclk.hw,
|
||||
[AUD_CLKID_MST_D_LRCLK] = &axg_mst_d_lrclk.hw,
|
||||
[AUD_CLKID_MST_E_LRCLK] = &axg_mst_e_lrclk.hw,
|
||||
[AUD_CLKID_MST_F_LRCLK] = &axg_mst_f_lrclk.hw,
|
||||
[AUD_CLKID_TDMIN_A_SCLK_SEL] = &axg_tdmin_a_sclk_sel.hw,
|
||||
[AUD_CLKID_TDMIN_B_SCLK_SEL] = &axg_tdmin_b_sclk_sel.hw,
|
||||
[AUD_CLKID_TDMIN_C_SCLK_SEL] = &axg_tdmin_c_sclk_sel.hw,
|
||||
[AUD_CLKID_TDMIN_LB_SCLK_SEL] = &axg_tdmin_lb_sclk_sel.hw,
|
||||
[AUD_CLKID_TDMOUT_A_SCLK_SEL] = &axg_tdmout_a_sclk_sel.hw,
|
||||
[AUD_CLKID_TDMOUT_B_SCLK_SEL] = &axg_tdmout_b_sclk_sel.hw,
|
||||
[AUD_CLKID_TDMOUT_C_SCLK_SEL] = &axg_tdmout_c_sclk_sel.hw,
|
||||
[AUD_CLKID_TDMIN_A_SCLK_PRE_EN] = &axg_tdmin_a_sclk_pre_en.hw,
|
||||
[AUD_CLKID_TDMIN_B_SCLK_PRE_EN] = &axg_tdmin_b_sclk_pre_en.hw,
|
||||
[AUD_CLKID_TDMIN_C_SCLK_PRE_EN] = &axg_tdmin_c_sclk_pre_en.hw,
|
||||
[AUD_CLKID_TDMIN_LB_SCLK_PRE_EN] = &axg_tdmin_lb_sclk_pre_en.hw,
|
||||
[AUD_CLKID_TDMOUT_A_SCLK_PRE_EN] = &axg_tdmout_a_sclk_pre_en.hw,
|
||||
[AUD_CLKID_TDMOUT_B_SCLK_PRE_EN] = &axg_tdmout_b_sclk_pre_en.hw,
|
||||
[AUD_CLKID_TDMOUT_C_SCLK_PRE_EN] = &axg_tdmout_c_sclk_pre_en.hw,
|
||||
[AUD_CLKID_TDMIN_A_SCLK_POST_EN] = &axg_tdmin_a_sclk_post_en.hw,
|
||||
[AUD_CLKID_TDMIN_B_SCLK_POST_EN] = &axg_tdmin_b_sclk_post_en.hw,
|
||||
[AUD_CLKID_TDMIN_C_SCLK_POST_EN] = &axg_tdmin_c_sclk_post_en.hw,
|
||||
[AUD_CLKID_TDMIN_LB_SCLK_POST_EN] = &axg_tdmin_lb_sclk_post_en.hw,
|
||||
[AUD_CLKID_TDMOUT_A_SCLK_POST_EN] = &axg_tdmout_a_sclk_post_en.hw,
|
||||
[AUD_CLKID_TDMOUT_B_SCLK_POST_EN] = &axg_tdmout_b_sclk_post_en.hw,
|
||||
[AUD_CLKID_TDMOUT_C_SCLK_POST_EN] = &axg_tdmout_c_sclk_post_en.hw,
|
||||
[AUD_CLKID_TDMIN_A_SCLK] = &axg_tdmin_a_sclk.hw,
|
||||
[AUD_CLKID_TDMIN_B_SCLK] = &axg_tdmin_b_sclk.hw,
|
||||
[AUD_CLKID_TDMIN_C_SCLK] = &axg_tdmin_c_sclk.hw,
|
||||
[AUD_CLKID_TDMIN_LB_SCLK] = &axg_tdmin_lb_sclk.hw,
|
||||
[AUD_CLKID_TDMOUT_A_SCLK] = &axg_tdmout_a_sclk.hw,
|
||||
[AUD_CLKID_TDMOUT_B_SCLK] = &axg_tdmout_b_sclk.hw,
|
||||
[AUD_CLKID_TDMOUT_C_SCLK] = &axg_tdmout_c_sclk.hw,
|
||||
[AUD_CLKID_TDMIN_A_LRCLK] = &axg_tdmin_a_lrclk.hw,
|
||||
[AUD_CLKID_TDMIN_B_LRCLK] = &axg_tdmin_b_lrclk.hw,
|
||||
[AUD_CLKID_TDMIN_C_LRCLK] = &axg_tdmin_c_lrclk.hw,
|
||||
[AUD_CLKID_TDMIN_LB_LRCLK] = &axg_tdmin_lb_lrclk.hw,
|
||||
[AUD_CLKID_TDMOUT_A_LRCLK] = &axg_tdmout_a_lrclk.hw,
|
||||
[AUD_CLKID_TDMOUT_B_LRCLK] = &axg_tdmout_b_lrclk.hw,
|
||||
[AUD_CLKID_TDMOUT_C_LRCLK] = &axg_tdmout_c_lrclk.hw,
|
||||
[NR_CLKS] = NULL,
|
||||
},
|
||||
.num = NR_CLKS,
|
||||
};
|
||||
|
||||
/* Convenience table to populate regmap in .probe() */
|
||||
static struct clk_regmap *const axg_audio_clk_regmaps[] = {
|
||||
&axg_ddr_arb,
|
||||
&axg_pdm,
|
||||
&axg_tdmin_a,
|
||||
&axg_tdmin_b,
|
||||
&axg_tdmin_c,
|
||||
&axg_tdmin_lb,
|
||||
&axg_tdmout_a,
|
||||
&axg_tdmout_b,
|
||||
&axg_tdmout_c,
|
||||
&axg_frddr_a,
|
||||
&axg_frddr_b,
|
||||
&axg_frddr_c,
|
||||
&axg_toddr_a,
|
||||
&axg_toddr_b,
|
||||
&axg_toddr_c,
|
||||
&axg_loopback,
|
||||
&axg_spdifin,
|
||||
&axg_spdifout,
|
||||
&axg_resample,
|
||||
&axg_power_detect,
|
||||
&axg_mst_a_mclk_sel,
|
||||
&axg_mst_b_mclk_sel,
|
||||
&axg_mst_c_mclk_sel,
|
||||
&axg_mst_d_mclk_sel,
|
||||
&axg_mst_e_mclk_sel,
|
||||
&axg_mst_f_mclk_sel,
|
||||
&axg_mst_a_mclk_div,
|
||||
&axg_mst_b_mclk_div,
|
||||
&axg_mst_c_mclk_div,
|
||||
&axg_mst_d_mclk_div,
|
||||
&axg_mst_e_mclk_div,
|
||||
&axg_mst_f_mclk_div,
|
||||
&axg_mst_a_mclk,
|
||||
&axg_mst_b_mclk,
|
||||
&axg_mst_c_mclk,
|
||||
&axg_mst_d_mclk,
|
||||
&axg_mst_e_mclk,
|
||||
&axg_mst_f_mclk,
|
||||
&axg_spdifout_clk_sel,
|
||||
&axg_spdifout_clk_div,
|
||||
&axg_spdifout_clk,
|
||||
&axg_spdifin_clk_sel,
|
||||
&axg_spdifin_clk_div,
|
||||
&axg_spdifin_clk,
|
||||
&axg_pdm_dclk_sel,
|
||||
&axg_pdm_dclk_div,
|
||||
&axg_pdm_dclk,
|
||||
&axg_pdm_sysclk_sel,
|
||||
&axg_pdm_sysclk_div,
|
||||
&axg_pdm_sysclk,
|
||||
&axg_mst_a_sclk_pre_en,
|
||||
&axg_mst_b_sclk_pre_en,
|
||||
&axg_mst_c_sclk_pre_en,
|
||||
&axg_mst_d_sclk_pre_en,
|
||||
&axg_mst_e_sclk_pre_en,
|
||||
&axg_mst_f_sclk_pre_en,
|
||||
&axg_mst_a_sclk_div,
|
||||
&axg_mst_b_sclk_div,
|
||||
&axg_mst_c_sclk_div,
|
||||
&axg_mst_d_sclk_div,
|
||||
&axg_mst_e_sclk_div,
|
||||
&axg_mst_f_sclk_div,
|
||||
&axg_mst_a_sclk_post_en,
|
||||
&axg_mst_b_sclk_post_en,
|
||||
&axg_mst_c_sclk_post_en,
|
||||
&axg_mst_d_sclk_post_en,
|
||||
&axg_mst_e_sclk_post_en,
|
||||
&axg_mst_f_sclk_post_en,
|
||||
&axg_mst_a_sclk,
|
||||
&axg_mst_b_sclk,
|
||||
&axg_mst_c_sclk,
|
||||
&axg_mst_d_sclk,
|
||||
&axg_mst_e_sclk,
|
||||
&axg_mst_f_sclk,
|
||||
&axg_mst_a_lrclk_div,
|
||||
&axg_mst_b_lrclk_div,
|
||||
&axg_mst_c_lrclk_div,
|
||||
&axg_mst_d_lrclk_div,
|
||||
&axg_mst_e_lrclk_div,
|
||||
&axg_mst_f_lrclk_div,
|
||||
&axg_mst_a_lrclk,
|
||||
&axg_mst_b_lrclk,
|
||||
&axg_mst_c_lrclk,
|
||||
&axg_mst_d_lrclk,
|
||||
&axg_mst_e_lrclk,
|
||||
&axg_mst_f_lrclk,
|
||||
&axg_tdmin_a_sclk_sel,
|
||||
&axg_tdmin_b_sclk_sel,
|
||||
&axg_tdmin_c_sclk_sel,
|
||||
&axg_tdmin_lb_sclk_sel,
|
||||
&axg_tdmout_a_sclk_sel,
|
||||
&axg_tdmout_b_sclk_sel,
|
||||
&axg_tdmout_c_sclk_sel,
|
||||
&axg_tdmin_a_sclk_pre_en,
|
||||
&axg_tdmin_b_sclk_pre_en,
|
||||
&axg_tdmin_c_sclk_pre_en,
|
||||
&axg_tdmin_lb_sclk_pre_en,
|
||||
&axg_tdmout_a_sclk_pre_en,
|
||||
&axg_tdmout_b_sclk_pre_en,
|
||||
&axg_tdmout_c_sclk_pre_en,
|
||||
&axg_tdmin_a_sclk_post_en,
|
||||
&axg_tdmin_b_sclk_post_en,
|
||||
&axg_tdmin_c_sclk_post_en,
|
||||
&axg_tdmin_lb_sclk_post_en,
|
||||
&axg_tdmout_a_sclk_post_en,
|
||||
&axg_tdmout_b_sclk_post_en,
|
||||
&axg_tdmout_c_sclk_post_en,
|
||||
&axg_tdmin_a_sclk,
|
||||
&axg_tdmin_b_sclk,
|
||||
&axg_tdmin_c_sclk,
|
||||
&axg_tdmin_lb_sclk,
|
||||
&axg_tdmout_a_sclk,
|
||||
&axg_tdmout_b_sclk,
|
||||
&axg_tdmout_c_sclk,
|
||||
&axg_tdmin_a_lrclk,
|
||||
&axg_tdmin_b_lrclk,
|
||||
&axg_tdmin_c_lrclk,
|
||||
&axg_tdmin_lb_lrclk,
|
||||
&axg_tdmout_a_lrclk,
|
||||
&axg_tdmout_b_lrclk,
|
||||
&axg_tdmout_c_lrclk,
|
||||
};
|
||||
|
||||
static struct clk *devm_clk_get_enable(struct device *dev, char *id)
|
||||
{
|
||||
struct clk *clk;
|
||||
int ret;
|
||||
|
||||
clk = devm_clk_get(dev, id);
|
||||
if (IS_ERR(clk)) {
|
||||
if (PTR_ERR(clk) != -EPROBE_DEFER)
|
||||
dev_err(dev, "failed to get %s", id);
|
||||
return clk;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable %s", id);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
ret = devm_add_action_or_reset(dev,
|
||||
(void(*)(void *))clk_disable_unprepare,
|
||||
clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to add reset action on %s", id);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
return clk;
|
||||
}
|
||||
|
||||
static const struct clk_ops axg_clk_no_ops = {};
|
||||
|
||||
static struct clk_hw *axg_clk_hw_register_bypass(struct device *dev,
|
||||
const char *name,
|
||||
const char *parent_name)
|
||||
{
|
||||
struct clk_hw *hw;
|
||||
struct clk_init_data init;
|
||||
char *clk_name;
|
||||
int ret;
|
||||
|
||||
hw = devm_kzalloc(dev, sizeof(*hw), GFP_KERNEL);
|
||||
if (!hw)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
clk_name = kasprintf(GFP_KERNEL, "axg_%s", name);
|
||||
if (!clk_name)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
init.name = clk_name;
|
||||
init.ops = &axg_clk_no_ops;
|
||||
init.flags = 0;
|
||||
init.parent_names = parent_name ? &parent_name : NULL;
|
||||
init.num_parents = parent_name ? 1 : 0;
|
||||
hw->init = &init;
|
||||
|
||||
ret = devm_clk_hw_register(dev, hw);
|
||||
kfree(clk_name);
|
||||
|
||||
return ret ? ERR_PTR(ret) : hw;
|
||||
}
|
||||
|
||||
static int axg_register_clk_hw_input(struct device *dev,
|
||||
const char *name,
|
||||
unsigned int clkid)
|
||||
{
|
||||
struct clk *parent_clk = devm_clk_get(dev, name);
|
||||
struct clk_hw *hw = NULL;
|
||||
|
||||
if (IS_ERR(parent_clk)) {
|
||||
int err = PTR_ERR(parent_clk);
|
||||
|
||||
/* It is ok if an input clock is missing */
|
||||
if (err == -ENOENT) {
|
||||
dev_dbg(dev, "%s not provided", name);
|
||||
} else {
|
||||
if (err != -EPROBE_DEFER)
|
||||
dev_err(dev, "failed to get %s clock", name);
|
||||
return err;
|
||||
}
|
||||
} else {
|
||||
hw = axg_clk_hw_register_bypass(dev, name,
|
||||
__clk_get_name(parent_clk));
|
||||
}
|
||||
|
||||
if (IS_ERR(hw)) {
|
||||
dev_err(dev, "failed to register %s clock", name);
|
||||
return PTR_ERR(hw);
|
||||
}
|
||||
|
||||
axg_audio_hw_onecell_data.hws[clkid] = hw;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int axg_register_clk_hw_inputs(struct device *dev,
|
||||
const char *basename,
|
||||
unsigned int count,
|
||||
unsigned int clkid)
|
||||
{
|
||||
char *name;
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
name = kasprintf(GFP_KERNEL, "%s%d", basename, i);
|
||||
if (!name)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = axg_register_clk_hw_input(dev, name, clkid + i);
|
||||
kfree(name);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct regmap_config axg_audio_regmap_cfg = {
|
||||
.reg_bits = 32,
|
||||
.val_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.max_register = AUDIO_CLK_PDMIN_CTRL1,
|
||||
};
|
||||
|
||||
static int axg_audio_clkc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct regmap *map;
|
||||
struct resource *res;
|
||||
void __iomem *regs;
|
||||
struct clk *clk;
|
||||
struct clk_hw *hw;
|
||||
int ret, i;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
regs = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(regs))
|
||||
return PTR_ERR(regs);
|
||||
|
||||
map = devm_regmap_init_mmio(dev, regs, &axg_audio_regmap_cfg);
|
||||
if (IS_ERR(map)) {
|
||||
dev_err(dev, "failed to init regmap: %ld\n", PTR_ERR(map));
|
||||
return PTR_ERR(map);
|
||||
}
|
||||
|
||||
/* Get the mandatory peripheral clock */
|
||||
clk = devm_clk_get_enable(dev, "pclk");
|
||||
if (IS_ERR(clk))
|
||||
return PTR_ERR(clk);
|
||||
|
||||
ret = device_reset(dev);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to reset device\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Register the peripheral input clock */
|
||||
hw = axg_clk_hw_register_bypass(dev, "audio_pclk",
|
||||
__clk_get_name(clk));
|
||||
if (IS_ERR(hw))
|
||||
return PTR_ERR(hw);
|
||||
|
||||
axg_audio_hw_onecell_data.hws[AUD_CLKID_PCLK] = hw;
|
||||
|
||||
/* Register optional input master clocks */
|
||||
ret = axg_register_clk_hw_inputs(dev, "mst_in",
|
||||
AXG_MST_IN_COUNT,
|
||||
AUD_CLKID_MST0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Register optional input slave sclks */
|
||||
ret = axg_register_clk_hw_inputs(dev, "slv_sclk",
|
||||
AXG_SLV_SCLK_COUNT,
|
||||
AUD_CLKID_SLV_SCLK0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Register optional input slave lrclks */
|
||||
ret = axg_register_clk_hw_inputs(dev, "slv_lrclk",
|
||||
AXG_SLV_LRCLK_COUNT,
|
||||
AUD_CLKID_SLV_LRCLK0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Populate regmap for the regmap backed clocks */
|
||||
for (i = 0; i < ARRAY_SIZE(axg_audio_clk_regmaps); i++)
|
||||
axg_audio_clk_regmaps[i]->map = map;
|
||||
|
||||
/* Take care to skip the registered input clocks */
|
||||
for (i = AUD_CLKID_DDR_ARB; i < axg_audio_hw_onecell_data.num; i++) {
|
||||
hw = axg_audio_hw_onecell_data.hws[i];
|
||||
/* array might be sparse */
|
||||
if (!hw)
|
||||
continue;
|
||||
|
||||
ret = devm_clk_hw_register(dev, hw);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to register clock %s\n",
|
||||
hw->init->name);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,
|
||||
&axg_audio_hw_onecell_data);
|
||||
}
|
||||
|
||||
static const struct of_device_id clkc_match_table[] = {
|
||||
{ .compatible = "amlogic,axg-audio-clkc" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, clkc_match_table);
|
||||
|
||||
static struct platform_driver axg_audio_driver = {
|
||||
.probe = axg_audio_clkc_probe,
|
||||
.driver = {
|
||||
.name = "axg-audio-clkc",
|
||||
.of_match_table = clkc_match_table,
|
||||
},
|
||||
};
|
||||
module_platform_driver(axg_audio_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Amlogic A113x Audio Clock driver");
|
||||
MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -0,0 +1,127 @@
|
|||
/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
|
||||
/*
|
||||
* Copyright (c) 2018 BayLibre, SAS.
|
||||
* Author: Jerome Brunet <jbrunet@baylibre.com>
|
||||
*/
|
||||
|
||||
#ifndef __AXG_AUDIO_CLKC_H
|
||||
#define __AXG_AUDIO_CLKC_H
|
||||
|
||||
/*
|
||||
* Audio Clock register offsets
|
||||
*
|
||||
* Register offsets from the datasheet must be multiplied by 4 before
|
||||
* to get the right offset
|
||||
*/
|
||||
#define AUDIO_CLK_GATE_EN 0x000
|
||||
#define AUDIO_MCLK_A_CTRL 0x004
|
||||
#define AUDIO_MCLK_B_CTRL 0x008
|
||||
#define AUDIO_MCLK_C_CTRL 0x00C
|
||||
#define AUDIO_MCLK_D_CTRL 0x010
|
||||
#define AUDIO_MCLK_E_CTRL 0x014
|
||||
#define AUDIO_MCLK_F_CTRL 0x018
|
||||
#define AUDIO_MST_A_SCLK_CTRL0 0x040
|
||||
#define AUDIO_MST_A_SCLK_CTRL1 0x044
|
||||
#define AUDIO_MST_B_SCLK_CTRL0 0x048
|
||||
#define AUDIO_MST_B_SCLK_CTRL1 0x04C
|
||||
#define AUDIO_MST_C_SCLK_CTRL0 0x050
|
||||
#define AUDIO_MST_C_SCLK_CTRL1 0x054
|
||||
#define AUDIO_MST_D_SCLK_CTRL0 0x058
|
||||
#define AUDIO_MST_D_SCLK_CTRL1 0x05C
|
||||
#define AUDIO_MST_E_SCLK_CTRL0 0x060
|
||||
#define AUDIO_MST_E_SCLK_CTRL1 0x064
|
||||
#define AUDIO_MST_F_SCLK_CTRL0 0x068
|
||||
#define AUDIO_MST_F_SCLK_CTRL1 0x06C
|
||||
#define AUDIO_CLK_TDMIN_A_CTRL 0x080
|
||||
#define AUDIO_CLK_TDMIN_B_CTRL 0x084
|
||||
#define AUDIO_CLK_TDMIN_C_CTRL 0x088
|
||||
#define AUDIO_CLK_TDMIN_LB_CTRL 0x08C
|
||||
#define AUDIO_CLK_TDMOUT_A_CTRL 0x090
|
||||
#define AUDIO_CLK_TDMOUT_B_CTRL 0x094
|
||||
#define AUDIO_CLK_TDMOUT_C_CTRL 0x098
|
||||
#define AUDIO_CLK_SPDIFIN_CTRL 0x09C
|
||||
#define AUDIO_CLK_SPDIFOUT_CTRL 0x0A0
|
||||
#define AUDIO_CLK_RESAMPLE_CTRL 0x0A4
|
||||
#define AUDIO_CLK_LOCKER_CTRL 0x0A8
|
||||
#define AUDIO_CLK_PDMIN_CTRL0 0x0AC
|
||||
#define AUDIO_CLK_PDMIN_CTRL1 0x0B0
|
||||
|
||||
/*
|
||||
* CLKID index values
|
||||
* These indices are entirely contrived and do not map onto the hardware.
|
||||
*/
|
||||
|
||||
#define AUD_CLKID_PCLK 0
|
||||
#define AUD_CLKID_MST0 1
|
||||
#define AUD_CLKID_MST1 2
|
||||
#define AUD_CLKID_MST2 3
|
||||
#define AUD_CLKID_MST3 4
|
||||
#define AUD_CLKID_MST4 5
|
||||
#define AUD_CLKID_MST5 6
|
||||
#define AUD_CLKID_MST6 7
|
||||
#define AUD_CLKID_MST7 8
|
||||
#define AUD_CLKID_MST_A_MCLK_SEL 59
|
||||
#define AUD_CLKID_MST_B_MCLK_SEL 60
|
||||
#define AUD_CLKID_MST_C_MCLK_SEL 61
|
||||
#define AUD_CLKID_MST_D_MCLK_SEL 62
|
||||
#define AUD_CLKID_MST_E_MCLK_SEL 63
|
||||
#define AUD_CLKID_MST_F_MCLK_SEL 64
|
||||
#define AUD_CLKID_MST_A_MCLK_DIV 65
|
||||
#define AUD_CLKID_MST_B_MCLK_DIV 66
|
||||
#define AUD_CLKID_MST_C_MCLK_DIV 67
|
||||
#define AUD_CLKID_MST_D_MCLK_DIV 68
|
||||
#define AUD_CLKID_MST_E_MCLK_DIV 69
|
||||
#define AUD_CLKID_MST_F_MCLK_DIV 70
|
||||
#define AUD_CLKID_SPDIFOUT_CLK_SEL 71
|
||||
#define AUD_CLKID_SPDIFOUT_CLK_DIV 72
|
||||
#define AUD_CLKID_SPDIFIN_CLK_SEL 73
|
||||
#define AUD_CLKID_SPDIFIN_CLK_DIV 74
|
||||
#define AUD_CLKID_PDM_DCLK_SEL 75
|
||||
#define AUD_CLKID_PDM_DCLK_DIV 76
|
||||
#define AUD_CLKID_PDM_SYSCLK_SEL 77
|
||||
#define AUD_CLKID_PDM_SYSCLK_DIV 78
|
||||
#define AUD_CLKID_MST_A_SCLK_PRE_EN 92
|
||||
#define AUD_CLKID_MST_B_SCLK_PRE_EN 93
|
||||
#define AUD_CLKID_MST_C_SCLK_PRE_EN 94
|
||||
#define AUD_CLKID_MST_D_SCLK_PRE_EN 95
|
||||
#define AUD_CLKID_MST_E_SCLK_PRE_EN 96
|
||||
#define AUD_CLKID_MST_F_SCLK_PRE_EN 97
|
||||
#define AUD_CLKID_MST_A_SCLK_DIV 98
|
||||
#define AUD_CLKID_MST_B_SCLK_DIV 99
|
||||
#define AUD_CLKID_MST_C_SCLK_DIV 100
|
||||
#define AUD_CLKID_MST_D_SCLK_DIV 101
|
||||
#define AUD_CLKID_MST_E_SCLK_DIV 102
|
||||
#define AUD_CLKID_MST_F_SCLK_DIV 103
|
||||
#define AUD_CLKID_MST_A_SCLK_POST_EN 104
|
||||
#define AUD_CLKID_MST_B_SCLK_POST_EN 105
|
||||
#define AUD_CLKID_MST_C_SCLK_POST_EN 106
|
||||
#define AUD_CLKID_MST_D_SCLK_POST_EN 107
|
||||
#define AUD_CLKID_MST_E_SCLK_POST_EN 108
|
||||
#define AUD_CLKID_MST_F_SCLK_POST_EN 109
|
||||
#define AUD_CLKID_MST_A_LRCLK_DIV 110
|
||||
#define AUD_CLKID_MST_B_LRCLK_DIV 111
|
||||
#define AUD_CLKID_MST_C_LRCLK_DIV 112
|
||||
#define AUD_CLKID_MST_D_LRCLK_DIV 113
|
||||
#define AUD_CLKID_MST_E_LRCLK_DIV 114
|
||||
#define AUD_CLKID_MST_F_LRCLK_DIV 115
|
||||
#define AUD_CLKID_TDMIN_A_SCLK_PRE_EN 137
|
||||
#define AUD_CLKID_TDMIN_B_SCLK_PRE_EN 138
|
||||
#define AUD_CLKID_TDMIN_C_SCLK_PRE_EN 139
|
||||
#define AUD_CLKID_TDMIN_LB_SCLK_PRE_EN 140
|
||||
#define AUD_CLKID_TDMOUT_A_SCLK_PRE_EN 141
|
||||
#define AUD_CLKID_TDMOUT_B_SCLK_PRE_EN 142
|
||||
#define AUD_CLKID_TDMOUT_C_SCLK_PRE_EN 143
|
||||
#define AUD_CLKID_TDMIN_A_SCLK_POST_EN 144
|
||||
#define AUD_CLKID_TDMIN_B_SCLK_POST_EN 145
|
||||
#define AUD_CLKID_TDMIN_C_SCLK_POST_EN 146
|
||||
#define AUD_CLKID_TDMIN_LB_SCLK_POST_EN 147
|
||||
#define AUD_CLKID_TDMOUT_A_SCLK_POST_EN 148
|
||||
#define AUD_CLKID_TDMOUT_B_SCLK_POST_EN 149
|
||||
#define AUD_CLKID_TDMOUT_C_SCLK_POST_EN 150
|
||||
|
||||
/* include the CLKIDs which are part of the DT bindings */
|
||||
#include <dt-bindings/clock/axg-audio-clkc.h>
|
||||
|
||||
#define NR_CLKS 151
|
||||
|
||||
#endif /*__AXG_AUDIO_CLKC_H */
|
|
@ -12,7 +12,6 @@
|
|||
#include <linux/clk.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
@ -626,6 +625,137 @@ static struct clk_regmap axg_mpll3 = {
|
|||
},
|
||||
};
|
||||
|
||||
static const struct pll_rate_table axg_pcie_pll_rate_table[] = {
|
||||
{
|
||||
.rate = 100000000,
|
||||
.m = 200,
|
||||
.n = 3,
|
||||
.od = 1,
|
||||
.od2 = 3,
|
||||
},
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
|
||||
static const struct reg_sequence axg_pcie_init_regs[] = {
|
||||
{ .reg = HHI_PCIE_PLL_CNTL, .def = 0x400106c8 },
|
||||
{ .reg = HHI_PCIE_PLL_CNTL1, .def = 0x0084a2aa },
|
||||
{ .reg = HHI_PCIE_PLL_CNTL2, .def = 0xb75020be },
|
||||
{ .reg = HHI_PCIE_PLL_CNTL3, .def = 0x0a47488e },
|
||||
{ .reg = HHI_PCIE_PLL_CNTL4, .def = 0xc000004d },
|
||||
{ .reg = HHI_PCIE_PLL_CNTL5, .def = 0x00078000 },
|
||||
{ .reg = HHI_PCIE_PLL_CNTL6, .def = 0x002323c6 },
|
||||
};
|
||||
|
||||
static struct clk_regmap axg_pcie_pll = {
|
||||
.data = &(struct meson_clk_pll_data){
|
||||
.m = {
|
||||
.reg_off = HHI_PCIE_PLL_CNTL,
|
||||
.shift = 0,
|
||||
.width = 9,
|
||||
},
|
||||
.n = {
|
||||
.reg_off = HHI_PCIE_PLL_CNTL,
|
||||
.shift = 9,
|
||||
.width = 5,
|
||||
},
|
||||
.od = {
|
||||
.reg_off = HHI_PCIE_PLL_CNTL,
|
||||
.shift = 16,
|
||||
.width = 2,
|
||||
},
|
||||
.od2 = {
|
||||
.reg_off = HHI_PCIE_PLL_CNTL6,
|
||||
.shift = 6,
|
||||
.width = 2,
|
||||
},
|
||||
.frac = {
|
||||
.reg_off = HHI_PCIE_PLL_CNTL1,
|
||||
.shift = 0,
|
||||
.width = 12,
|
||||
},
|
||||
.l = {
|
||||
.reg_off = HHI_PCIE_PLL_CNTL,
|
||||
.shift = 31,
|
||||
.width = 1,
|
||||
},
|
||||
.rst = {
|
||||
.reg_off = HHI_PCIE_PLL_CNTL,
|
||||
.shift = 29,
|
||||
.width = 1,
|
||||
},
|
||||
.table = axg_pcie_pll_rate_table,
|
||||
.init_regs = axg_pcie_init_regs,
|
||||
.init_count = ARRAY_SIZE(axg_pcie_init_regs),
|
||||
},
|
||||
.hw.init = &(struct clk_init_data){
|
||||
.name = "pcie_pll",
|
||||
.ops = &meson_clk_pll_ops,
|
||||
.parent_names = (const char *[]){ "xtal" },
|
||||
.num_parents = 1,
|
||||
},
|
||||
};
|
||||
|
||||
static struct clk_regmap axg_pcie_mux = {
|
||||
.data = &(struct clk_regmap_mux_data){
|
||||
.offset = HHI_PCIE_PLL_CNTL6,
|
||||
.mask = 0x1,
|
||||
.shift = 2,
|
||||
},
|
||||
.hw.init = &(struct clk_init_data){
|
||||
.name = "pcie_mux",
|
||||
.ops = &clk_regmap_mux_ops,
|
||||
.parent_names = (const char *[]){ "mpll3", "pcie_pll" },
|
||||
.num_parents = 2,
|
||||
.flags = CLK_SET_RATE_PARENT,
|
||||
},
|
||||
};
|
||||
|
||||
static struct clk_regmap axg_pcie_ref = {
|
||||
.data = &(struct clk_regmap_mux_data){
|
||||
.offset = HHI_PCIE_PLL_CNTL6,
|
||||
.mask = 0x1,
|
||||
.shift = 1,
|
||||
/* skip the parent 0, reserved for debug */
|
||||
.table = (u32[]){ 1 },
|
||||
},
|
||||
.hw.init = &(struct clk_init_data){
|
||||
.name = "pcie_ref",
|
||||
.ops = &clk_regmap_mux_ops,
|
||||
.parent_names = (const char *[]){ "pcie_mux" },
|
||||
.num_parents = 1,
|
||||
.flags = CLK_SET_RATE_PARENT,
|
||||
},
|
||||
};
|
||||
|
||||
static struct clk_regmap axg_pcie_cml_en0 = {
|
||||
.data = &(struct clk_regmap_gate_data){
|
||||
.offset = HHI_PCIE_PLL_CNTL6,
|
||||
.bit_idx = 4,
|
||||
},
|
||||
.hw.init = &(struct clk_init_data) {
|
||||
.name = "pcie_cml_en0",
|
||||
.ops = &clk_regmap_gate_ops,
|
||||
.parent_names = (const char *[]){ "pcie_ref" },
|
||||
.num_parents = 1,
|
||||
.flags = CLK_SET_RATE_PARENT,
|
||||
|
||||
},
|
||||
};
|
||||
|
||||
static struct clk_regmap axg_pcie_cml_en1 = {
|
||||
.data = &(struct clk_regmap_gate_data){
|
||||
.offset = HHI_PCIE_PLL_CNTL6,
|
||||
.bit_idx = 3,
|
||||
},
|
||||
.hw.init = &(struct clk_init_data) {
|
||||
.name = "pcie_cml_en1",
|
||||
.ops = &clk_regmap_gate_ops,
|
||||
.parent_names = (const char *[]){ "pcie_ref" },
|
||||
.num_parents = 1,
|
||||
.flags = CLK_SET_RATE_PARENT,
|
||||
},
|
||||
};
|
||||
|
||||
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",
|
||||
|
@ -779,6 +909,63 @@ 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",
|
||||
"fclk_div4", "fclk_div3", "fclk_div5", "fclk_div7", "gp0_pll",
|
||||
};
|
||||
|
||||
static struct clk_regmap axg_gen_clk_sel = {
|
||||
.data = &(struct clk_regmap_mux_data){
|
||||
.offset = HHI_GEN_CLK_CNTL,
|
||||
.mask = 0xf,
|
||||
.shift = 12,
|
||||
.table = mux_table_gen_clk,
|
||||
},
|
||||
.hw.init = &(struct clk_init_data){
|
||||
.name = "gen_clk_sel",
|
||||
.ops = &clk_regmap_mux_ops,
|
||||
/*
|
||||
* bits 15:12 selects from 14 possible parents:
|
||||
* xtal, [rtc_oscin_i], [sys_cpu_div16], [ddr_dpll_pt],
|
||||
* hifi_pll, mpll0, mpll1, mpll2, mpll3, fdiv4,
|
||||
* fdiv3, fdiv5, [cts_msr_clk], fdiv7, gp0_pll
|
||||
*/
|
||||
.parent_names = gen_clk_parent_names,
|
||||
.num_parents = ARRAY_SIZE(gen_clk_parent_names),
|
||||
},
|
||||
};
|
||||
|
||||
static struct clk_regmap axg_gen_clk_div = {
|
||||
.data = &(struct clk_regmap_div_data){
|
||||
.offset = HHI_GEN_CLK_CNTL,
|
||||
.shift = 0,
|
||||
.width = 11,
|
||||
},
|
||||
.hw.init = &(struct clk_init_data){
|
||||
.name = "gen_clk_div",
|
||||
.ops = &clk_regmap_divider_ops,
|
||||
.parent_names = (const char *[]){ "gen_clk_sel" },
|
||||
.num_parents = 1,
|
||||
.flags = CLK_SET_RATE_PARENT,
|
||||
},
|
||||
};
|
||||
|
||||
static struct clk_regmap axg_gen_clk = {
|
||||
.data = &(struct clk_regmap_gate_data){
|
||||
.offset = HHI_GEN_CLK_CNTL,
|
||||
.bit_idx = 7,
|
||||
},
|
||||
.hw.init = &(struct clk_init_data){
|
||||
.name = "gen_clk",
|
||||
.ops = &clk_regmap_gate_ops,
|
||||
.parent_names = (const char *[]){ "gen_clk_div" },
|
||||
.num_parents = 1,
|
||||
.flags = CLK_SET_RATE_PARENT,
|
||||
},
|
||||
};
|
||||
|
||||
/* Everything Else (EE) domain gates */
|
||||
static MESON_GATE(axg_ddr, HHI_GCLK_MPEG0, 0);
|
||||
static MESON_GATE(axg_audio_locker, HHI_GCLK_MPEG0, 2);
|
||||
|
@ -821,6 +1008,7 @@ static MESON_GATE(axg_mmc_pclk, HHI_GCLK_MPEG2, 11);
|
|||
static MESON_GATE(axg_vpu_intr, HHI_GCLK_MPEG2, 25);
|
||||
static MESON_GATE(axg_sec_ahb_ahb3_bridge, HHI_GCLK_MPEG2, 26);
|
||||
static MESON_GATE(axg_gic, HHI_GCLK_MPEG2, 30);
|
||||
static MESON_GATE(axg_mipi_enable, HHI_MIPI_CNTL0, 29);
|
||||
|
||||
/* Always On (AO) domain gates */
|
||||
|
||||
|
@ -910,6 +1098,15 @@ static struct clk_hw_onecell_data axg_hw_onecell_data = {
|
|||
[CLKID_FCLK_DIV4_DIV] = &axg_fclk_div4_div.hw,
|
||||
[CLKID_FCLK_DIV5_DIV] = &axg_fclk_div5_div.hw,
|
||||
[CLKID_FCLK_DIV7_DIV] = &axg_fclk_div7_div.hw,
|
||||
[CLKID_PCIE_PLL] = &axg_pcie_pll.hw,
|
||||
[CLKID_PCIE_MUX] = &axg_pcie_mux.hw,
|
||||
[CLKID_PCIE_REF] = &axg_pcie_ref.hw,
|
||||
[CLKID_PCIE_CML_EN0] = &axg_pcie_cml_en0.hw,
|
||||
[CLKID_PCIE_CML_EN1] = &axg_pcie_cml_en1.hw,
|
||||
[CLKID_MIPI_ENABLE] = &axg_mipi_enable.hw,
|
||||
[CLKID_GEN_CLK_SEL] = &axg_gen_clk_sel.hw,
|
||||
[CLKID_GEN_CLK_DIV] = &axg_gen_clk_div.hw,
|
||||
[CLKID_GEN_CLK] = &axg_gen_clk.hw,
|
||||
[NR_CLKS] = NULL,
|
||||
},
|
||||
.num = NR_CLKS,
|
||||
|
@ -988,6 +1185,15 @@ static struct clk_regmap *const axg_clk_regmaps[] = {
|
|||
&axg_fclk_div4,
|
||||
&axg_fclk_div5,
|
||||
&axg_fclk_div7,
|
||||
&axg_pcie_pll,
|
||||
&axg_pcie_mux,
|
||||
&axg_pcie_ref,
|
||||
&axg_pcie_cml_en0,
|
||||
&axg_pcie_cml_en1,
|
||||
&axg_mipi_enable,
|
||||
&axg_gen_clk_sel,
|
||||
&axg_gen_clk_div,
|
||||
&axg_gen_clk,
|
||||
};
|
||||
|
||||
static const struct of_device_id clkc_match_table[] = {
|
||||
|
@ -995,49 +1201,17 @@ static const struct of_device_id clkc_match_table[] = {
|
|||
{}
|
||||
};
|
||||
|
||||
static const struct regmap_config clkc_regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.val_bits = 32,
|
||||
.reg_stride = 4,
|
||||
};
|
||||
|
||||
static int axg_clkc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct resource *res;
|
||||
void __iomem *clk_base = NULL;
|
||||
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 - Trying obsolete regs\n");
|
||||
|
||||
/*
|
||||
* FIXME: HHI registers should be accessed through
|
||||
* the appropriate system controller. This is required because
|
||||
* there is more than just clocks in this register space
|
||||
*
|
||||
* This fallback method is only provided temporarily until
|
||||
* all the platform DTs are properly using the syscon node
|
||||
*/
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res)
|
||||
return -EINVAL;
|
||||
|
||||
|
||||
clk_base = devm_ioremap(dev, res->start, resource_size(res));
|
||||
if (!clk_base) {
|
||||
dev_err(dev, "Unable to map clk base\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
map = devm_regmap_init_mmio(dev, clk_base,
|
||||
&clkc_regmap_config);
|
||||
if (IS_ERR(map))
|
||||
return PTR_ERR(map);
|
||||
dev_err(dev, "failed to get HHI regmap\n");
|
||||
return PTR_ERR(map);
|
||||
}
|
||||
|
||||
/* Populate regmap for the regmap backed clocks */
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
* 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 0x00
|
||||
#define HHI_GP0_PLL_CNTL 0x40
|
||||
#define HHI_GP0_PLL_CNTL2 0x44
|
||||
#define HHI_GP0_PLL_CNTL3 0x48
|
||||
|
@ -127,8 +128,13 @@
|
|||
#define CLKID_FCLK_DIV4_DIV 73
|
||||
#define CLKID_FCLK_DIV5_DIV 74
|
||||
#define CLKID_FCLK_DIV7_DIV 75
|
||||
#define CLKID_PCIE_PLL 76
|
||||
#define CLKID_PCIE_MUX 77
|
||||
#define CLKID_PCIE_REF 78
|
||||
#define CLKID_GEN_CLK_SEL 82
|
||||
#define CLKID_GEN_CLK_DIV 83
|
||||
|
||||
#define NR_CLKS 76
|
||||
#define NR_CLKS 85
|
||||
|
||||
/* include the CLKIDs that have been made part of the DT binding */
|
||||
#include <dt-bindings/clock/axg-clkc.h>
|
||||
|
|
|
@ -1,110 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2017 AmLogic, Inc.
|
||||
* Author: Jerome Brunet <jbrunet@baylibre.com>
|
||||
*/
|
||||
|
||||
/*
|
||||
* i2s master clock divider: The algorithm of the generic clk-divider used with
|
||||
* a very precise clock parent such as the mpll tends to select a low divider
|
||||
* factor. This gives poor results with this particular divider, especially with
|
||||
* high frequencies (> 100 MHz)
|
||||
*
|
||||
* This driver try to select the maximum possible divider with the rate the
|
||||
* upstream clock can provide.
|
||||
*/
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include "clkc.h"
|
||||
|
||||
static inline struct meson_clk_audio_div_data *
|
||||
meson_clk_audio_div_data(struct clk_regmap *clk)
|
||||
{
|
||||
return (struct meson_clk_audio_div_data *)clk->data;
|
||||
}
|
||||
|
||||
static int _div_round(unsigned long parent_rate, unsigned long rate,
|
||||
unsigned long flags)
|
||||
{
|
||||
if (flags & CLK_DIVIDER_ROUND_CLOSEST)
|
||||
return DIV_ROUND_CLOSEST_ULL((u64)parent_rate, rate);
|
||||
|
||||
return DIV_ROUND_UP_ULL((u64)parent_rate, rate);
|
||||
}
|
||||
|
||||
static int _get_val(unsigned long parent_rate, unsigned long rate)
|
||||
{
|
||||
return DIV_ROUND_UP_ULL((u64)parent_rate, rate) - 1;
|
||||
}
|
||||
|
||||
static int _valid_divider(unsigned int width, int divider)
|
||||
{
|
||||
int max_divider = 1 << width;
|
||||
|
||||
return clamp(divider, 1, max_divider);
|
||||
}
|
||||
|
||||
static unsigned long audio_divider_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct clk_regmap *clk = to_clk_regmap(hw);
|
||||
struct meson_clk_audio_div_data *adiv = meson_clk_audio_div_data(clk);
|
||||
unsigned long divider;
|
||||
|
||||
divider = meson_parm_read(clk->map, &adiv->div);
|
||||
|
||||
return DIV_ROUND_UP_ULL((u64)parent_rate, divider);
|
||||
}
|
||||
|
||||
static long audio_divider_round_rate(struct clk_hw *hw,
|
||||
unsigned long rate,
|
||||
unsigned long *parent_rate)
|
||||
{
|
||||
struct clk_regmap *clk = to_clk_regmap(hw);
|
||||
struct meson_clk_audio_div_data *adiv = meson_clk_audio_div_data(clk);
|
||||
unsigned long max_prate;
|
||||
int divider;
|
||||
|
||||
if (!(clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT)) {
|
||||
divider = _div_round(*parent_rate, rate, adiv->flags);
|
||||
divider = _valid_divider(adiv->div.width, divider);
|
||||
return DIV_ROUND_UP_ULL((u64)*parent_rate, divider);
|
||||
}
|
||||
|
||||
/* Get the maximum parent rate */
|
||||
max_prate = clk_hw_round_rate(clk_hw_get_parent(hw), ULONG_MAX);
|
||||
|
||||
/* Get the corresponding rounded down divider */
|
||||
divider = max_prate / rate;
|
||||
divider = _valid_divider(adiv->div.width, divider);
|
||||
|
||||
/* Get actual rate of the parent */
|
||||
*parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw),
|
||||
divider * rate);
|
||||
|
||||
return DIV_ROUND_UP_ULL((u64)*parent_rate, divider);
|
||||
}
|
||||
|
||||
static int audio_divider_set_rate(struct clk_hw *hw,
|
||||
unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct clk_regmap *clk = to_clk_regmap(hw);
|
||||
struct meson_clk_audio_div_data *adiv = meson_clk_audio_div_data(clk);
|
||||
int val = _get_val(parent_rate, rate);
|
||||
|
||||
meson_parm_write(clk->map, &adiv->div, val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct clk_ops meson_clk_audio_divider_ro_ops = {
|
||||
.recalc_rate = audio_divider_recalc_rate,
|
||||
.round_rate = audio_divider_round_rate,
|
||||
};
|
||||
|
||||
const struct clk_ops meson_clk_audio_divider_ops = {
|
||||
.recalc_rate = audio_divider_recalc_rate,
|
||||
.round_rate = audio_divider_round_rate,
|
||||
.set_rate = audio_divider_set_rate,
|
||||
};
|
|
@ -0,0 +1,63 @@
|
|||
// 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.h"
|
||||
|
||||
#define phase_step(_width) (360 / (1 << (_width)))
|
||||
|
||||
static inline struct meson_clk_phase_data *
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
unsigned int val = DIV_ROUND_CLOSEST(degrees, phase_step(width));
|
||||
|
||||
/*
|
||||
* This last calculation is here for cases when degrees is rounded
|
||||
* to 360, in which case val == (1 << width).
|
||||
*/
|
||||
return val % (1 << width);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(meson_clk_degrees_to_val);
|
||||
|
||||
static int meson_clk_phase_get_phase(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_regmap *clk = to_clk_regmap(hw);
|
||||
struct meson_clk_phase_data *phase = meson_clk_phase_data(clk);
|
||||
unsigned int val;
|
||||
|
||||
val = meson_parm_read(clk->map, &phase->ph);
|
||||
|
||||
return meson_clk_degrees_from_val(val, phase->ph.width);
|
||||
}
|
||||
|
||||
static int meson_clk_phase_set_phase(struct clk_hw *hw, int degrees)
|
||||
{
|
||||
struct clk_regmap *clk = to_clk_regmap(hw);
|
||||
struct meson_clk_phase_data *phase = meson_clk_phase_data(clk);
|
||||
unsigned int val;
|
||||
|
||||
val = meson_clk_degrees_to_val(degrees, phase->ph.width);
|
||||
meson_parm_write(clk->map, &phase->ph, val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct clk_ops meson_clk_phase_ops = {
|
||||
.get_phase = meson_clk_phase_get_phase,
|
||||
.set_phase = meson_clk_phase_set_phase,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(meson_clk_phase_ops);
|
|
@ -0,0 +1,68 @@
|
|||
// 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);
|
|
@ -0,0 +1,28 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2018 BayLibre, SAS.
|
||||
* Author: Jerome Brunet <jbrunet@baylibre.com>
|
||||
*/
|
||||
|
||||
#ifndef __MESON_CLKC_AUDIO_H
|
||||
#define __MESON_CLKC_AUDIO_H
|
||||
|
||||
#include "clkc.h"
|
||||
|
||||
struct meson_clk_triphase_data {
|
||||
struct parm ph0;
|
||||
struct parm ph1;
|
||||
struct parm ph2;
|
||||
};
|
||||
|
||||
struct meson_sclk_div_data {
|
||||
struct parm div;
|
||||
struct parm hi;
|
||||
unsigned int cached_div;
|
||||
struct clk_duty cached_duty;
|
||||
};
|
||||
|
||||
extern const struct clk_ops meson_clk_triphase_ops;
|
||||
extern const struct clk_ops meson_sclk_div_ops;
|
||||
|
||||
#endif /* __MESON_CLKC_AUDIO_H */
|
|
@ -91,11 +91,13 @@ struct meson_clk_mpll_data {
|
|||
|
||||
#define CLK_MESON_MPLL_ROUND_CLOSEST BIT(0)
|
||||
|
||||
struct meson_clk_audio_div_data {
|
||||
struct parm div;
|
||||
u8 flags;
|
||||
struct meson_clk_phase_data {
|
||||
struct parm ph;
|
||||
};
|
||||
|
||||
int meson_clk_degrees_from_val(unsigned int val, unsigned int width);
|
||||
unsigned int meson_clk_degrees_to_val(int degrees, unsigned int width);
|
||||
|
||||
#define MESON_GATE(_name, _reg, _bit) \
|
||||
struct clk_regmap _name = { \
|
||||
.data = &(struct clk_regmap_gate_data){ \
|
||||
|
@ -117,7 +119,6 @@ extern const struct clk_ops meson_clk_pll_ops;
|
|||
extern const struct clk_ops meson_clk_cpu_ops;
|
||||
extern const struct clk_ops meson_clk_mpll_ro_ops;
|
||||
extern const struct clk_ops meson_clk_mpll_ops;
|
||||
extern const struct clk_ops meson_clk_audio_divider_ro_ops;
|
||||
extern const struct clk_ops meson_clk_audio_divider_ops;
|
||||
extern const struct clk_ops meson_clk_phase_ops;
|
||||
|
||||
#endif /* __CLKC_H */
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
#include <linux/clk.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
@ -498,6 +497,7 @@ static struct clk_regmap gxbb_fclk_div2 = {
|
|||
.ops = &clk_regmap_gate_ops,
|
||||
.parent_names = (const char *[]){ "fclk_div2_div" },
|
||||
.num_parents = 1,
|
||||
.flags = CLK_IS_CRITICAL,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -970,28 +970,26 @@ static struct clk_regmap gxbb_cts_amclk_sel = {
|
|||
.mask = 0x3,
|
||||
.shift = 9,
|
||||
.table = (u32[]){ 1, 2, 3 },
|
||||
.flags = CLK_MUX_ROUND_CLOSEST,
|
||||
},
|
||||
.hw.init = &(struct clk_init_data){
|
||||
.name = "cts_amclk_sel",
|
||||
.ops = &clk_regmap_mux_ops,
|
||||
.parent_names = (const char *[]){ "mpll0", "mpll1", "mpll2" },
|
||||
.num_parents = 3,
|
||||
.flags = CLK_SET_RATE_PARENT,
|
||||
},
|
||||
};
|
||||
|
||||
static struct clk_regmap gxbb_cts_amclk_div = {
|
||||
.data = &(struct meson_clk_audio_div_data){
|
||||
.div = {
|
||||
.reg_off = HHI_AUD_CLK_CNTL,
|
||||
.shift = 0,
|
||||
.width = 8,
|
||||
},
|
||||
.data = &(struct clk_regmap_div_data) {
|
||||
.offset = HHI_AUD_CLK_CNTL,
|
||||
.shift = 0,
|
||||
.width = 8,
|
||||
.flags = CLK_DIVIDER_ROUND_CLOSEST,
|
||||
},
|
||||
.hw.init = &(struct clk_init_data){
|
||||
.name = "cts_amclk_div",
|
||||
.ops = &meson_clk_audio_divider_ops,
|
||||
.ops = &clk_regmap_divider_ops,
|
||||
.parent_names = (const char *[]){ "cts_amclk_sel" },
|
||||
.num_parents = 1,
|
||||
.flags = CLK_SET_RATE_PARENT,
|
||||
|
@ -1018,13 +1016,13 @@ static struct clk_regmap gxbb_cts_mclk_i958_sel = {
|
|||
.mask = 0x3,
|
||||
.shift = 25,
|
||||
.table = (u32[]){ 1, 2, 3 },
|
||||
.flags = CLK_MUX_ROUND_CLOSEST,
|
||||
},
|
||||
.hw.init = &(struct clk_init_data) {
|
||||
.name = "cts_mclk_i958_sel",
|
||||
.ops = &clk_regmap_mux_ops,
|
||||
.parent_names = (const char *[]){ "mpll0", "mpll1", "mpll2" },
|
||||
.num_parents = 3,
|
||||
.flags = CLK_SET_RATE_PARENT,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -1626,6 +1624,63 @@ static struct clk_regmap gxbb_vdec_hevc = {
|
|||
},
|
||||
};
|
||||
|
||||
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", "vdec_1", "vdec_hevc", "mpll0", "mpll1", "mpll2",
|
||||
"fclk_div4", "fclk_div3", "fclk_div5", "fclk_div7", "gp0_pll",
|
||||
};
|
||||
|
||||
static struct clk_regmap gxbb_gen_clk_sel = {
|
||||
.data = &(struct clk_regmap_mux_data){
|
||||
.offset = HHI_GEN_CLK_CNTL,
|
||||
.mask = 0xf,
|
||||
.shift = 12,
|
||||
.table = mux_table_gen_clk,
|
||||
},
|
||||
.hw.init = &(struct clk_init_data){
|
||||
.name = "gen_clk_sel",
|
||||
.ops = &clk_regmap_mux_ops,
|
||||
/*
|
||||
* bits 15:12 selects from 14 possible parents:
|
||||
* xtal, [rtc_oscin_i], [sys_cpu_div16], [ddr_dpll_pt],
|
||||
* vid_pll, vid2_pll (hevc), mpll0, mpll1, mpll2, fdiv4,
|
||||
* fdiv3, fdiv5, [cts_msr_clk], fdiv7, gp0_pll
|
||||
*/
|
||||
.parent_names = gen_clk_parent_names,
|
||||
.num_parents = ARRAY_SIZE(gen_clk_parent_names),
|
||||
},
|
||||
};
|
||||
|
||||
static struct clk_regmap gxbb_gen_clk_div = {
|
||||
.data = &(struct clk_regmap_div_data){
|
||||
.offset = HHI_GEN_CLK_CNTL,
|
||||
.shift = 0,
|
||||
.width = 11,
|
||||
},
|
||||
.hw.init = &(struct clk_init_data){
|
||||
.name = "gen_clk_div",
|
||||
.ops = &clk_regmap_divider_ops,
|
||||
.parent_names = (const char *[]){ "gen_clk_sel" },
|
||||
.num_parents = 1,
|
||||
.flags = CLK_SET_RATE_PARENT,
|
||||
},
|
||||
};
|
||||
|
||||
static struct clk_regmap gxbb_gen_clk = {
|
||||
.data = &(struct clk_regmap_gate_data){
|
||||
.offset = HHI_GEN_CLK_CNTL,
|
||||
.bit_idx = 7,
|
||||
},
|
||||
.hw.init = &(struct clk_init_data){
|
||||
.name = "gen_clk",
|
||||
.ops = &clk_regmap_gate_ops,
|
||||
.parent_names = (const char *[]){ "gen_clk_div" },
|
||||
.num_parents = 1,
|
||||
.flags = CLK_SET_RATE_PARENT,
|
||||
},
|
||||
};
|
||||
|
||||
/* Everything Else (EE) domain gates */
|
||||
static MESON_GATE(gxbb_ddr, HHI_GCLK_MPEG0, 0);
|
||||
static MESON_GATE(gxbb_dos, HHI_GCLK_MPEG0, 1);
|
||||
|
@ -1875,6 +1930,9 @@ static struct clk_hw_onecell_data gxbb_hw_onecell_data = {
|
|||
[CLKID_VDEC_HEVC_SEL] = &gxbb_vdec_hevc_sel.hw,
|
||||
[CLKID_VDEC_HEVC_DIV] = &gxbb_vdec_hevc_div.hw,
|
||||
[CLKID_VDEC_HEVC] = &gxbb_vdec_hevc.hw,
|
||||
[CLKID_GEN_CLK_SEL] = &gxbb_gen_clk_sel.hw,
|
||||
[CLKID_GEN_CLK_DIV] = &gxbb_gen_clk_div.hw,
|
||||
[CLKID_GEN_CLK] = &gxbb_gen_clk.hw,
|
||||
[NR_CLKS] = NULL,
|
||||
},
|
||||
.num = NR_CLKS,
|
||||
|
@ -2037,6 +2095,9 @@ static struct clk_hw_onecell_data gxl_hw_onecell_data = {
|
|||
[CLKID_VDEC_HEVC_SEL] = &gxbb_vdec_hevc_sel.hw,
|
||||
[CLKID_VDEC_HEVC_DIV] = &gxbb_vdec_hevc_div.hw,
|
||||
[CLKID_VDEC_HEVC] = &gxbb_vdec_hevc.hw,
|
||||
[CLKID_GEN_CLK_SEL] = &gxbb_gen_clk_sel.hw,
|
||||
[CLKID_GEN_CLK_DIV] = &gxbb_gen_clk_div.hw,
|
||||
[CLKID_GEN_CLK] = &gxbb_gen_clk.hw,
|
||||
[NR_CLKS] = NULL,
|
||||
},
|
||||
.num = NR_CLKS,
|
||||
|
@ -2201,6 +2262,9 @@ static struct clk_regmap *const gx_clk_regmaps[] = {
|
|||
&gxbb_vdec_hevc_sel,
|
||||
&gxbb_vdec_hevc_div,
|
||||
&gxbb_vdec_hevc,
|
||||
&gxbb_gen_clk_sel,
|
||||
&gxbb_gen_clk_div,
|
||||
&gxbb_gen_clk,
|
||||
};
|
||||
|
||||
struct clkc_data {
|
||||
|
@ -2227,17 +2291,9 @@ static const struct of_device_id clkc_match_table[] = {
|
|||
{},
|
||||
};
|
||||
|
||||
static const struct regmap_config clkc_regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.val_bits = 32,
|
||||
.reg_stride = 4,
|
||||
};
|
||||
|
||||
static int gxbb_clkc_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct clkc_data *clkc_data;
|
||||
struct resource *res;
|
||||
void __iomem *clk_base;
|
||||
struct regmap *map;
|
||||
int ret, i;
|
||||
struct device *dev = &pdev->dev;
|
||||
|
@ -2249,31 +2305,8 @@ static int gxbb_clkc_probe(struct platform_device *pdev)
|
|||
/* 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 - Trying obsolete regs\n");
|
||||
|
||||
/*
|
||||
* FIXME: HHI registers should be accessed through
|
||||
* the appropriate system controller. This is required because
|
||||
* there is more than just clocks in this register space
|
||||
*
|
||||
* This fallback method is only provided temporarily until
|
||||
* all the platform DTs are properly using the syscon node
|
||||
*/
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res)
|
||||
return -EINVAL;
|
||||
|
||||
clk_base = devm_ioremap(dev, res->start, resource_size(res));
|
||||
if (!clk_base) {
|
||||
dev_err(dev, "Unable to map clk base\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
map = devm_regmap_init_mmio(dev, clk_base,
|
||||
&clkc_regmap_config);
|
||||
if (IS_ERR(map))
|
||||
return PTR_ERR(map);
|
||||
dev_err(dev, "failed to get HHI regmap\n");
|
||||
return PTR_ERR(map);
|
||||
}
|
||||
|
||||
/* Populate regmap for the common regmap backed clocks */
|
||||
|
|
|
@ -66,7 +66,6 @@
|
|||
#define HHI_USB_CLK_CNTL 0x220 /* 0x88 offset in data sheet */
|
||||
#define HHI_32K_CLK_CNTL 0x224 /* 0x89 offset in data sheet */
|
||||
#define HHI_GEN_CLK_CNTL 0x228 /* 0x8a offset in data sheet */
|
||||
#define HHI_GEN_CLK_CNTL 0x228 /* 0x8a offset in data sheet */
|
||||
|
||||
#define HHI_PCM_CLK_CNTL 0x258 /* 0x96 offset in data sheet */
|
||||
#define HHI_NAND_CLK_CNTL 0x25C /* 0x97 offset in data sheet */
|
||||
|
@ -158,8 +157,10 @@
|
|||
#define CLKID_VDEC_1_DIV 152
|
||||
#define CLKID_VDEC_HEVC_SEL 154
|
||||
#define CLKID_VDEC_HEVC_DIV 155
|
||||
#define CLKID_GEN_CLK_SEL 157
|
||||
#define CLKID_GEN_CLK_DIV 158
|
||||
|
||||
#define NR_CLKS 157
|
||||
#define NR_CLKS 160
|
||||
|
||||
/* include the CLKIDs that have been made part of the DT binding */
|
||||
#include <dt-bindings/clock/gxbb-clkc.h>
|
||||
|
|
|
@ -0,0 +1,243 @@
|
|||
// SPDX-License-Identifier: (GPL-2.0 OR MIT)
|
||||
/*
|
||||
* Copyright (c) 2018 BayLibre, SAS.
|
||||
* Author: Jerome Brunet <jbrunet@baylibre.com>
|
||||
*
|
||||
* Sample clock generator divider:
|
||||
* This HW divider gates with value 0 but is otherwise a zero based divider:
|
||||
*
|
||||
* val >= 1
|
||||
* divider = val + 1
|
||||
*
|
||||
* The duty cycle may also be set for the LR clock variant. The duty cycle
|
||||
* ratio is:
|
||||
*
|
||||
* hi = [0 - val]
|
||||
* duty_cycle = (1 + hi) / (1 + val)
|
||||
*/
|
||||
|
||||
#include "clkc-audio.h"
|
||||
|
||||
static inline struct meson_sclk_div_data *
|
||||
meson_sclk_div_data(struct clk_regmap *clk)
|
||||
{
|
||||
return (struct meson_sclk_div_data *)clk->data;
|
||||
}
|
||||
|
||||
static int sclk_div_maxval(struct meson_sclk_div_data *sclk)
|
||||
{
|
||||
return (1 << sclk->div.width) - 1;
|
||||
}
|
||||
|
||||
static int sclk_div_maxdiv(struct meson_sclk_div_data *sclk)
|
||||
{
|
||||
return sclk_div_maxval(sclk) + 1;
|
||||
}
|
||||
|
||||
static int sclk_div_getdiv(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long prate, int maxdiv)
|
||||
{
|
||||
int div = DIV_ROUND_CLOSEST_ULL((u64)prate, rate);
|
||||
|
||||
return clamp(div, 2, maxdiv);
|
||||
}
|
||||
|
||||
static int sclk_div_bestdiv(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *prate,
|
||||
struct meson_sclk_div_data *sclk)
|
||||
{
|
||||
struct clk_hw *parent = clk_hw_get_parent(hw);
|
||||
int bestdiv = 0, i;
|
||||
unsigned long maxdiv, now, parent_now;
|
||||
unsigned long best = 0, best_parent = 0;
|
||||
|
||||
if (!rate)
|
||||
rate = 1;
|
||||
|
||||
maxdiv = sclk_div_maxdiv(sclk);
|
||||
|
||||
if (!(clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT))
|
||||
return sclk_div_getdiv(hw, rate, *prate, maxdiv);
|
||||
|
||||
/*
|
||||
* The maximum divider we can use without overflowing
|
||||
* unsigned long in rate * i below
|
||||
*/
|
||||
maxdiv = min(ULONG_MAX / rate, maxdiv);
|
||||
|
||||
for (i = 2; i <= maxdiv; i++) {
|
||||
/*
|
||||
* It's the most ideal case if the requested rate can be
|
||||
* divided from parent clock without needing to change
|
||||
* parent rate, so return the divider immediately.
|
||||
*/
|
||||
if (rate * i == *prate)
|
||||
return i;
|
||||
|
||||
parent_now = clk_hw_round_rate(parent, rate * i);
|
||||
now = DIV_ROUND_UP_ULL((u64)parent_now, i);
|
||||
|
||||
if (abs(rate - now) < abs(rate - best)) {
|
||||
bestdiv = i;
|
||||
best = now;
|
||||
best_parent = parent_now;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bestdiv)
|
||||
bestdiv = sclk_div_maxdiv(sclk);
|
||||
else
|
||||
*prate = best_parent;
|
||||
|
||||
return bestdiv;
|
||||
}
|
||||
|
||||
static long sclk_div_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *prate)
|
||||
{
|
||||
struct clk_regmap *clk = to_clk_regmap(hw);
|
||||
struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
|
||||
int div;
|
||||
|
||||
div = sclk_div_bestdiv(hw, rate, prate, sclk);
|
||||
|
||||
return DIV_ROUND_UP_ULL((u64)*prate, div);
|
||||
}
|
||||
|
||||
static void sclk_apply_ratio(struct clk_regmap *clk,
|
||||
struct meson_sclk_div_data *sclk)
|
||||
{
|
||||
unsigned int hi = DIV_ROUND_CLOSEST(sclk->cached_div *
|
||||
sclk->cached_duty.num,
|
||||
sclk->cached_duty.den);
|
||||
|
||||
if (hi)
|
||||
hi -= 1;
|
||||
|
||||
meson_parm_write(clk->map, &sclk->hi, hi);
|
||||
}
|
||||
|
||||
static int sclk_div_set_duty_cycle(struct clk_hw *hw,
|
||||
struct clk_duty *duty)
|
||||
{
|
||||
struct clk_regmap *clk = to_clk_regmap(hw);
|
||||
struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
|
||||
|
||||
if (MESON_PARM_APPLICABLE(&sclk->hi)) {
|
||||
memcpy(&sclk->cached_duty, duty, sizeof(*duty));
|
||||
sclk_apply_ratio(clk, sclk);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sclk_div_get_duty_cycle(struct clk_hw *hw,
|
||||
struct clk_duty *duty)
|
||||
{
|
||||
struct clk_regmap *clk = to_clk_regmap(hw);
|
||||
struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
|
||||
int hi;
|
||||
|
||||
if (!MESON_PARM_APPLICABLE(&sclk->hi)) {
|
||||
duty->num = 1;
|
||||
duty->den = 2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
hi = meson_parm_read(clk->map, &sclk->hi);
|
||||
duty->num = hi + 1;
|
||||
duty->den = sclk->cached_div;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sclk_apply_divider(struct clk_regmap *clk,
|
||||
struct meson_sclk_div_data *sclk)
|
||||
{
|
||||
if (MESON_PARM_APPLICABLE(&sclk->hi))
|
||||
sclk_apply_ratio(clk, sclk);
|
||||
|
||||
meson_parm_write(clk->map, &sclk->div, sclk->cached_div - 1);
|
||||
}
|
||||
|
||||
static int sclk_div_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long prate)
|
||||
{
|
||||
struct clk_regmap *clk = to_clk_regmap(hw);
|
||||
struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
|
||||
unsigned long maxdiv = sclk_div_maxdiv(sclk);
|
||||
|
||||
sclk->cached_div = sclk_div_getdiv(hw, rate, prate, maxdiv);
|
||||
|
||||
if (clk_hw_is_enabled(hw))
|
||||
sclk_apply_divider(clk, sclk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long sclk_div_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long prate)
|
||||
{
|
||||
struct clk_regmap *clk = to_clk_regmap(hw);
|
||||
struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
|
||||
|
||||
return DIV_ROUND_UP_ULL((u64)prate, sclk->cached_div);
|
||||
}
|
||||
|
||||
static int sclk_div_enable(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_regmap *clk = to_clk_regmap(hw);
|
||||
struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
|
||||
|
||||
sclk_apply_divider(clk, sclk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sclk_div_disable(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_regmap *clk = to_clk_regmap(hw);
|
||||
struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
|
||||
|
||||
meson_parm_write(clk->map, &sclk->div, 0);
|
||||
}
|
||||
|
||||
static int sclk_div_is_enabled(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_regmap *clk = to_clk_regmap(hw);
|
||||
struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
|
||||
|
||||
if (meson_parm_read(clk->map, &sclk->div))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sclk_div_init(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_regmap *clk = to_clk_regmap(hw);
|
||||
struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
|
||||
unsigned int val;
|
||||
|
||||
val = meson_parm_read(clk->map, &sclk->div);
|
||||
|
||||
/* if the divider is initially disabled, assume max */
|
||||
if (!val)
|
||||
sclk->cached_div = sclk_div_maxdiv(sclk);
|
||||
else
|
||||
sclk->cached_div = val + 1;
|
||||
|
||||
sclk_div_get_duty_cycle(hw, &sclk->cached_duty);
|
||||
}
|
||||
|
||||
const struct clk_ops meson_sclk_div_ops = {
|
||||
.recalc_rate = sclk_div_recalc_rate,
|
||||
.round_rate = sclk_div_round_rate,
|
||||
.set_rate = sclk_div_set_rate,
|
||||
.enable = sclk_div_enable,
|
||||
.disable = sclk_div_disable,
|
||||
.is_enabled = sclk_div_is_enabled,
|
||||
.get_duty_cycle = sclk_div_get_duty_cycle,
|
||||
.set_duty_cycle = sclk_div_set_duty_cycle,
|
||||
.init = sclk_div_init,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(meson_sclk_div_ops);
|
|
@ -0,0 +1,94 @@
|
|||
/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
|
||||
/*
|
||||
* Copyright (c) 2018 Baylibre SAS.
|
||||
* Author: Jerome Brunet <jbrunet@baylibre.com>
|
||||
*/
|
||||
|
||||
#ifndef __AXG_AUDIO_CLKC_BINDINGS_H
|
||||
#define __AXG_AUDIO_CLKC_BINDINGS_H
|
||||
|
||||
#define AUD_CLKID_SLV_SCLK0 9
|
||||
#define AUD_CLKID_SLV_SCLK1 10
|
||||
#define AUD_CLKID_SLV_SCLK2 11
|
||||
#define AUD_CLKID_SLV_SCLK3 12
|
||||
#define AUD_CLKID_SLV_SCLK4 13
|
||||
#define AUD_CLKID_SLV_SCLK5 14
|
||||
#define AUD_CLKID_SLV_SCLK6 15
|
||||
#define AUD_CLKID_SLV_SCLK7 16
|
||||
#define AUD_CLKID_SLV_SCLK8 17
|
||||
#define AUD_CLKID_SLV_SCLK9 18
|
||||
#define AUD_CLKID_SLV_LRCLK0 19
|
||||
#define AUD_CLKID_SLV_LRCLK1 20
|
||||
#define AUD_CLKID_SLV_LRCLK2 21
|
||||
#define AUD_CLKID_SLV_LRCLK3 22
|
||||
#define AUD_CLKID_SLV_LRCLK4 23
|
||||
#define AUD_CLKID_SLV_LRCLK5 24
|
||||
#define AUD_CLKID_SLV_LRCLK6 25
|
||||
#define AUD_CLKID_SLV_LRCLK7 26
|
||||
#define AUD_CLKID_SLV_LRCLK8 27
|
||||
#define AUD_CLKID_SLV_LRCLK9 28
|
||||
#define AUD_CLKID_DDR_ARB 29
|
||||
#define AUD_CLKID_PDM 30
|
||||
#define AUD_CLKID_TDMIN_A 31
|
||||
#define AUD_CLKID_TDMIN_B 32
|
||||
#define AUD_CLKID_TDMIN_C 33
|
||||
#define AUD_CLKID_TDMIN_LB 34
|
||||
#define AUD_CLKID_TDMOUT_A 35
|
||||
#define AUD_CLKID_TDMOUT_B 36
|
||||
#define AUD_CLKID_TDMOUT_C 37
|
||||
#define AUD_CLKID_FRDDR_A 38
|
||||
#define AUD_CLKID_FRDDR_B 39
|
||||
#define AUD_CLKID_FRDDR_C 40
|
||||
#define AUD_CLKID_TODDR_A 41
|
||||
#define AUD_CLKID_TODDR_B 42
|
||||
#define AUD_CLKID_TODDR_C 43
|
||||
#define AUD_CLKID_LOOPBACK 44
|
||||
#define AUD_CLKID_SPDIFIN 45
|
||||
#define AUD_CLKID_SPDIFOUT 46
|
||||
#define AUD_CLKID_RESAMPLE 47
|
||||
#define AUD_CLKID_POWER_DETECT 48
|
||||
#define AUD_CLKID_MST_A_MCLK 49
|
||||
#define AUD_CLKID_MST_B_MCLK 50
|
||||
#define AUD_CLKID_MST_C_MCLK 51
|
||||
#define AUD_CLKID_MST_D_MCLK 52
|
||||
#define AUD_CLKID_MST_E_MCLK 53
|
||||
#define AUD_CLKID_MST_F_MCLK 54
|
||||
#define AUD_CLKID_SPDIFOUT_CLK 55
|
||||
#define AUD_CLKID_SPDIFIN_CLK 56
|
||||
#define AUD_CLKID_PDM_DCLK 57
|
||||
#define AUD_CLKID_PDM_SYSCLK 58
|
||||
#define AUD_CLKID_MST_A_SCLK 79
|
||||
#define AUD_CLKID_MST_B_SCLK 80
|
||||
#define AUD_CLKID_MST_C_SCLK 81
|
||||
#define AUD_CLKID_MST_D_SCLK 82
|
||||
#define AUD_CLKID_MST_E_SCLK 83
|
||||
#define AUD_CLKID_MST_F_SCLK 84
|
||||
#define AUD_CLKID_MST_A_LRCLK 86
|
||||
#define AUD_CLKID_MST_B_LRCLK 87
|
||||
#define AUD_CLKID_MST_C_LRCLK 88
|
||||
#define AUD_CLKID_MST_D_LRCLK 89
|
||||
#define AUD_CLKID_MST_E_LRCLK 90
|
||||
#define AUD_CLKID_MST_F_LRCLK 91
|
||||
#define AUD_CLKID_TDMIN_A_SCLK_SEL 116
|
||||
#define AUD_CLKID_TDMIN_B_SCLK_SEL 117
|
||||
#define AUD_CLKID_TDMIN_C_SCLK_SEL 118
|
||||
#define AUD_CLKID_TDMIN_LB_SCLK_SEL 119
|
||||
#define AUD_CLKID_TDMOUT_A_SCLK_SEL 120
|
||||
#define AUD_CLKID_TDMOUT_B_SCLK_SEL 121
|
||||
#define AUD_CLKID_TDMOUT_C_SCLK_SEL 122
|
||||
#define AUD_CLKID_TDMIN_A_SCLK 123
|
||||
#define AUD_CLKID_TDMIN_B_SCLK 124
|
||||
#define AUD_CLKID_TDMIN_C_SCLK 125
|
||||
#define AUD_CLKID_TDMIN_LB_SCLK 126
|
||||
#define AUD_CLKID_TDMOUT_A_SCLK 127
|
||||
#define AUD_CLKID_TDMOUT_B_SCLK 128
|
||||
#define AUD_CLKID_TDMOUT_C_SCLK 129
|
||||
#define AUD_CLKID_TDMIN_A_LRCLK 130
|
||||
#define AUD_CLKID_TDMIN_B_LRCLK 131
|
||||
#define AUD_CLKID_TDMIN_C_LRCLK 132
|
||||
#define AUD_CLKID_TDMIN_LB_LRCLK 133
|
||||
#define AUD_CLKID_TDMOUT_A_LRCLK 134
|
||||
#define AUD_CLKID_TDMOUT_B_LRCLK 135
|
||||
#define AUD_CLKID_TDMOUT_C_LRCLK 136
|
||||
|
||||
#endif /* __AXG_AUDIO_CLKC_BINDINGS_H */
|
|
@ -68,5 +68,9 @@
|
|||
#define CLKID_SD_EMMC_B_CLK0 59
|
||||
#define CLKID_SD_EMMC_C_CLK0 60
|
||||
#define CLKID_HIFI_PLL 69
|
||||
#define CLKID_PCIE_CML_EN0 79
|
||||
#define CLKID_PCIE_CML_EN1 80
|
||||
#define CLKID_MIPI_ENABLE 81
|
||||
#define CLKID_GEN_CLK 84
|
||||
|
||||
#endif /* __AXG_CLKC_H */
|
||||
|
|
|
@ -127,5 +127,6 @@
|
|||
#define CLKID_VAPB 140
|
||||
#define CLKID_VDEC_1 153
|
||||
#define CLKID_VDEC_HEVC 156
|
||||
#define CLKID_GEN_CLK 159
|
||||
|
||||
#endif /* __GXBB_CLKC_H */
|
||||
|
|
|
@ -38,6 +38,8 @@
|
|||
#define CLK_IS_CRITICAL BIT(11) /* do not gate, ever */
|
||||
/* parents need enable during gate/ungate, set rate and re-parent */
|
||||
#define CLK_OPS_PARENT_ENABLE BIT(12)
|
||||
/* duty cycle call may be forwarded to the parent clock */
|
||||
#define CLK_DUTY_CYCLE_PARENT BIT(13)
|
||||
|
||||
struct clk;
|
||||
struct clk_hw;
|
||||
|
@ -66,6 +68,17 @@ struct clk_rate_request {
|
|||
struct clk_hw *best_parent_hw;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct clk_duty - Struture encoding the duty cycle ratio of a clock
|
||||
*
|
||||
* @num: Numerator of the duty cycle ratio
|
||||
* @den: Denominator of the duty cycle ratio
|
||||
*/
|
||||
struct clk_duty {
|
||||
unsigned int num;
|
||||
unsigned int den;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct clk_ops - Callback operations for hardware clocks; these are to
|
||||
* be provided by the clock implementation, and will be called by drivers
|
||||
|
@ -169,6 +182,15 @@ struct clk_rate_request {
|
|||
* by the second argument. Valid values for degrees are
|
||||
* 0-359. Return 0 on success, otherwise -EERROR.
|
||||
*
|
||||
* @get_duty_cycle: Queries the hardware to get the current duty cycle ratio
|
||||
* of a clock. Returned values denominator cannot be 0 and must be
|
||||
* superior or equal to the numerator.
|
||||
*
|
||||
* @set_duty_cycle: Apply the duty cycle ratio to this clock signal specified by
|
||||
* the numerator (2nd argurment) and denominator (3rd argument).
|
||||
* Argument must be a valid ratio (denominator > 0
|
||||
* and >= numerator) Return 0 on success, otherwise -EERROR.
|
||||
*
|
||||
* @init: Perform platform-specific initialization magic.
|
||||
* This is not not used by any of the basic clock types.
|
||||
* Please consider other ways of solving initialization problems
|
||||
|
@ -218,6 +240,10 @@ struct clk_ops {
|
|||
unsigned long parent_accuracy);
|
||||
int (*get_phase)(struct clk_hw *hw);
|
||||
int (*set_phase)(struct clk_hw *hw, int degrees);
|
||||
int (*get_duty_cycle)(struct clk_hw *hw,
|
||||
struct clk_duty *duty);
|
||||
int (*set_duty_cycle)(struct clk_hw *hw,
|
||||
struct clk_duty *duty);
|
||||
void (*init)(struct clk_hw *hw);
|
||||
void (*debug_init)(struct clk_hw *hw, struct dentry *dentry);
|
||||
};
|
||||
|
|
|
@ -141,6 +141,27 @@ int clk_set_phase(struct clk *clk, int degrees);
|
|||
*/
|
||||
int clk_get_phase(struct clk *clk);
|
||||
|
||||
/**
|
||||
* clk_set_duty_cycle - adjust the duty cycle ratio of a clock signal
|
||||
* @clk: clock signal source
|
||||
* @num: numerator of the duty cycle ratio to be applied
|
||||
* @den: denominator of the duty cycle ratio to be applied
|
||||
*
|
||||
* Adjust the duty cycle of a clock signal by the specified ratio. Returns 0 on
|
||||
* success, -EERROR otherwise.
|
||||
*/
|
||||
int clk_set_duty_cycle(struct clk *clk, unsigned int num, unsigned int den);
|
||||
|
||||
/**
|
||||
* clk_get_duty_cycle - return the duty cycle ratio of a clock signal
|
||||
* @clk: clock signal source
|
||||
* @scale: scaling factor to be applied to represent the ratio as an integer
|
||||
*
|
||||
* Returns the duty cycle ratio multiplied by the scale provided, otherwise
|
||||
* returns -EERROR.
|
||||
*/
|
||||
int clk_get_scaled_duty_cycle(struct clk *clk, unsigned int scale);
|
||||
|
||||
/**
|
||||
* clk_is_match - check if two clk's point to the same hardware clock
|
||||
* @p: clk compared against q
|
||||
|
@ -183,6 +204,18 @@ static inline long clk_get_phase(struct clk *clk)
|
|||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
static inline int clk_set_duty_cycle(struct clk *clk, unsigned int num,
|
||||
unsigned int den)
|
||||
{
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
static inline unsigned int clk_get_scaled_duty_cycle(struct clk *clk,
|
||||
unsigned int scale)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline bool clk_is_match(const struct clk *p, const struct clk *q)
|
||||
{
|
||||
return p == q;
|
||||
|
|
|
@ -192,6 +192,42 @@ DEFINE_EVENT(clk_phase, clk_set_phase_complete,
|
|||
TP_ARGS(core, phase)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(clk_duty_cycle,
|
||||
|
||||
TP_PROTO(struct clk_core *core, struct clk_duty *duty),
|
||||
|
||||
TP_ARGS(core, duty),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__string( name, core->name )
|
||||
__field( unsigned int, num )
|
||||
__field( unsigned int, den )
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__assign_str(name, core->name);
|
||||
__entry->num = duty->num;
|
||||
__entry->den = duty->den;
|
||||
),
|
||||
|
||||
TP_printk("%s %u/%u", __get_str(name), (unsigned int)__entry->num,
|
||||
(unsigned int)__entry->den)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(clk_duty_cycle, clk_set_duty_cycle,
|
||||
|
||||
TP_PROTO(struct clk_core *core, struct clk_duty *duty),
|
||||
|
||||
TP_ARGS(core, duty)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(clk_duty_cycle, clk_set_duty_cycle_complete,
|
||||
|
||||
TP_PROTO(struct clk_core *core, struct clk_duty *duty),
|
||||
|
||||
TP_ARGS(core, duty)
|
||||
);
|
||||
|
||||
#endif /* _TRACE_CLK_H */
|
||||
|
||||
/* This part must be outside protection */
|
||||
|
|
Loading…
Reference in New Issue