mirror of
https://github.com/u-boot/u-boot.git
synced 2024-11-25 05:04:23 +08:00
- Port more CCF code to work with i.MX8 devices.
This commit is contained in:
commit
898c40c0d6
@ -93,6 +93,7 @@ CONFIG_BOOTCOUNT_LIMIT=y
|
||||
CONFIG_DM_BOOTCOUNT=y
|
||||
CONFIG_DM_BOOTCOUNT_RTC=y
|
||||
CONFIG_CLK=y
|
||||
CONFIG_CLK_COMPOSITE_CCF=y
|
||||
CONFIG_SANDBOX_CLK_CCF=y
|
||||
CONFIG_CPU=y
|
||||
CONFIG_DM_DEMO=y
|
||||
|
@ -66,6 +66,7 @@ CONFIG_DEBUG_DEVRES=y
|
||||
CONFIG_ADC=y
|
||||
CONFIG_ADC_SANDBOX=y
|
||||
CONFIG_CLK=y
|
||||
CONFIG_CLK_COMPOSITE_CCF=y
|
||||
CONFIG_SANDBOX_CLK_CCF=y
|
||||
CONFIG_CPU=y
|
||||
CONFIG_DM_DEMO=y
|
||||
|
@ -53,6 +53,13 @@ config SPL_CLK_CCF
|
||||
Enable this option if you want to (re-)use the Linux kernel's Common
|
||||
Clock Framework [CCF] code in U-Boot's SPL.
|
||||
|
||||
config SPL_CLK_COMPOSITE_CCF
|
||||
bool "SPL Common Clock Framework [CCF] composite clk support "
|
||||
depends on SPL_CLK_CCF
|
||||
help
|
||||
Enable this option if you want to (re-)use the Linux kernel's Common
|
||||
Clock Framework [CCF] composite code in U-Boot's SPL.
|
||||
|
||||
config CLK_CCF
|
||||
bool "Common Clock Framework [CCF] support "
|
||||
depends on CLK_IMX6Q || SANDBOX_CLK_CCF
|
||||
@ -60,6 +67,13 @@ config CLK_CCF
|
||||
Enable this option if you want to (re-)use the Linux kernel's Common
|
||||
Clock Framework [CCF] code in U-Boot's clock driver.
|
||||
|
||||
config CLK_COMPOSITE_CCF
|
||||
bool "Common Clock Framework [CCF] composite clk support "
|
||||
depends on CLK_CCF
|
||||
help
|
||||
Enable this option if you want to (re-)use the Linux kernel's Common
|
||||
Clock Framework [CCF] composite code in U-Boot's clock driver.
|
||||
|
||||
config CLK_STM32F
|
||||
bool "Enable clock driver support for STM32F family"
|
||||
depends on CLK && (STM32F7 || STM32F4)
|
||||
|
@ -7,8 +7,9 @@
|
||||
obj-$(CONFIG_$(SPL_TPL_)CLK) += clk-uclass.o
|
||||
obj-$(CONFIG_$(SPL_TPL_)CLK) += clk_fixed_rate.o
|
||||
obj-$(CONFIG_$(SPL_TPL_)CLK) += clk_fixed_factor.o
|
||||
obj-$(CONFIG_$(SPL_TPL_)CLK_CCF) += clk.o clk-divider.o clk-mux.o
|
||||
obj-$(CONFIG_$(SPL_TPL_)CLK_CCF) += clk.o clk-divider.o clk-mux.o clk-gate.o
|
||||
obj-$(CONFIG_$(SPL_TPL_)CLK_CCF) += clk-fixed-factor.o
|
||||
obj-$(CONFIG_$(SPL_TPL_)CLK_COMPOSITE_CCF) += clk-composite.o
|
||||
|
||||
obj-y += analogbits/
|
||||
obj-y += imx/
|
||||
|
160
drivers/clk/clk-composite.c
Normal file
160
drivers/clk/clk-composite.c
Normal file
@ -0,0 +1,160 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2013 NVIDIA CORPORATION. All rights reserved.
|
||||
* Copyright 2019 NXP
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <malloc.h>
|
||||
#include <clk-uclass.h>
|
||||
#include <dm/device.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <clk.h>
|
||||
|
||||
#include "clk.h"
|
||||
|
||||
#define UBOOT_DM_CLK_COMPOSITE "clk_composite"
|
||||
|
||||
static u8 clk_composite_get_parent(struct clk *clk)
|
||||
{
|
||||
struct clk_composite *composite = to_clk_composite(clk_dev_binded(clk) ?
|
||||
(struct clk *)dev_get_clk_ptr(clk->dev) : clk);
|
||||
struct clk *mux = composite->mux;
|
||||
|
||||
return clk_mux_get_parent(mux);
|
||||
}
|
||||
|
||||
static int clk_composite_set_parent(struct clk *clk, struct clk *parent)
|
||||
{
|
||||
struct clk_composite *composite = to_clk_composite(clk_dev_binded(clk) ?
|
||||
(struct clk *)dev_get_clk_ptr(clk->dev) : clk);
|
||||
const struct clk_ops *mux_ops = composite->mux_ops;
|
||||
struct clk *mux = composite->mux;
|
||||
|
||||
return mux_ops->set_parent(mux, parent);
|
||||
}
|
||||
|
||||
static unsigned long clk_composite_recalc_rate(struct clk *clk)
|
||||
{
|
||||
struct clk_composite *composite = to_clk_composite(clk_dev_binded(clk) ?
|
||||
(struct clk *)dev_get_clk_ptr(clk->dev) : clk);
|
||||
const struct clk_ops *rate_ops = composite->rate_ops;
|
||||
struct clk *rate = composite->rate;
|
||||
|
||||
return rate_ops->get_rate(rate);
|
||||
}
|
||||
|
||||
static ulong clk_composite_set_rate(struct clk *clk, unsigned long rate)
|
||||
{
|
||||
struct clk_composite *composite = to_clk_composite(clk_dev_binded(clk) ?
|
||||
(struct clk *)dev_get_clk_ptr(clk->dev) : clk);
|
||||
const struct clk_ops *rate_ops = composite->rate_ops;
|
||||
struct clk *clk_rate = composite->rate;
|
||||
|
||||
return rate_ops->set_rate(clk_rate, rate);
|
||||
}
|
||||
|
||||
static int clk_composite_enable(struct clk *clk)
|
||||
{
|
||||
struct clk_composite *composite = to_clk_composite(clk_dev_binded(clk) ?
|
||||
(struct clk *)dev_get_clk_ptr(clk->dev) : clk);
|
||||
const struct clk_ops *gate_ops = composite->gate_ops;
|
||||
struct clk *gate = composite->gate;
|
||||
|
||||
return gate_ops->enable(gate);
|
||||
}
|
||||
|
||||
static int clk_composite_disable(struct clk *clk)
|
||||
{
|
||||
struct clk_composite *composite = to_clk_composite(clk_dev_binded(clk) ?
|
||||
(struct clk *)dev_get_clk_ptr(clk->dev) : clk);
|
||||
const struct clk_ops *gate_ops = composite->gate_ops;
|
||||
struct clk *gate = composite->gate;
|
||||
|
||||
gate_ops->disable(gate);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct clk_ops clk_composite_ops = {
|
||||
/* This will be set according to clk_register_composite */
|
||||
};
|
||||
|
||||
struct clk *clk_register_composite(struct device *dev, const char *name,
|
||||
const char * const *parent_names,
|
||||
int num_parents, struct clk *mux,
|
||||
const struct clk_ops *mux_ops,
|
||||
struct clk *rate,
|
||||
const struct clk_ops *rate_ops,
|
||||
struct clk *gate,
|
||||
const struct clk_ops *gate_ops,
|
||||
unsigned long flags)
|
||||
{
|
||||
struct clk *clk;
|
||||
struct clk_composite *composite;
|
||||
int ret;
|
||||
struct clk_ops *composite_ops = &clk_composite_ops;
|
||||
|
||||
composite = kzalloc(sizeof(*composite), GFP_KERNEL);
|
||||
if (!composite)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
if (mux && mux_ops) {
|
||||
composite->mux = mux;
|
||||
composite->mux_ops = mux_ops;
|
||||
if (mux_ops->set_parent)
|
||||
composite_ops->set_parent = clk_composite_set_parent;
|
||||
mux->data = (ulong)composite;
|
||||
}
|
||||
|
||||
if (rate && rate_ops) {
|
||||
if (!rate_ops->get_rate) {
|
||||
clk = ERR_PTR(-EINVAL);
|
||||
goto err;
|
||||
}
|
||||
composite_ops->get_rate = clk_composite_recalc_rate;
|
||||
|
||||
/* .set_rate requires either .round_rate or .determine_rate */
|
||||
if (rate_ops->set_rate)
|
||||
composite_ops->set_rate = clk_composite_set_rate;
|
||||
|
||||
composite->rate = rate;
|
||||
composite->rate_ops = rate_ops;
|
||||
rate->data = (ulong)composite;
|
||||
}
|
||||
|
||||
if (gate && gate_ops) {
|
||||
if (!gate_ops->enable || !gate_ops->disable) {
|
||||
clk = ERR_PTR(-EINVAL);
|
||||
goto err;
|
||||
}
|
||||
|
||||
composite->gate = gate;
|
||||
composite->gate_ops = gate_ops;
|
||||
composite_ops->enable = clk_composite_enable;
|
||||
composite_ops->disable = clk_composite_disable;
|
||||
gate->data = (ulong)composite;
|
||||
}
|
||||
|
||||
clk = &composite->clk;
|
||||
ret = clk_register(clk, UBOOT_DM_CLK_COMPOSITE, name,
|
||||
parent_names[clk_composite_get_parent(clk)]);
|
||||
if (ret) {
|
||||
clk = ERR_PTR(ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
return clk;
|
||||
|
||||
err:
|
||||
kfree(composite);
|
||||
return clk;
|
||||
}
|
||||
|
||||
U_BOOT_DRIVER(clk_composite) = {
|
||||
.name = UBOOT_DM_CLK_COMPOSITE,
|
||||
.id = UCLASS_CLK,
|
||||
.ops = &clk_composite_ops,
|
||||
.flags = DM_FLAG_PRE_RELOC,
|
||||
};
|
@ -18,6 +18,7 @@
|
||||
#include <dm/lists.h>
|
||||
#include <dm/device-internal.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/log2.h>
|
||||
#include <div64.h>
|
||||
#include <clk.h>
|
||||
#include "clk.h"
|
||||
@ -69,8 +70,8 @@ unsigned long divider_recalc_rate(struct clk *hw, unsigned long parent_rate,
|
||||
|
||||
static ulong clk_divider_recalc_rate(struct clk *clk)
|
||||
{
|
||||
struct clk_divider *divider =
|
||||
to_clk_divider(dev_get_clk_ptr(clk->dev));
|
||||
struct clk_divider *divider = to_clk_divider(clk_dev_binded(clk) ?
|
||||
dev_get_clk_ptr(clk->dev) : clk);
|
||||
unsigned long parent_rate = clk_get_parent_rate(clk);
|
||||
unsigned int val;
|
||||
|
||||
@ -86,8 +87,95 @@ static ulong clk_divider_recalc_rate(struct clk *clk)
|
||||
divider->flags, divider->width);
|
||||
}
|
||||
|
||||
static bool _is_valid_table_div(const struct clk_div_table *table,
|
||||
unsigned int div)
|
||||
{
|
||||
const struct clk_div_table *clkt;
|
||||
|
||||
for (clkt = table; clkt->div; clkt++)
|
||||
if (clkt->div == div)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool _is_valid_div(const struct clk_div_table *table, unsigned int div,
|
||||
unsigned long flags)
|
||||
{
|
||||
if (flags & CLK_DIVIDER_POWER_OF_TWO)
|
||||
return is_power_of_2(div);
|
||||
if (table)
|
||||
return _is_valid_table_div(table, div);
|
||||
return true;
|
||||
}
|
||||
|
||||
static unsigned int _get_table_val(const struct clk_div_table *table,
|
||||
unsigned int div)
|
||||
{
|
||||
const struct clk_div_table *clkt;
|
||||
|
||||
for (clkt = table; clkt->div; clkt++)
|
||||
if (clkt->div == div)
|
||||
return clkt->val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int _get_val(const struct clk_div_table *table,
|
||||
unsigned int div, unsigned long flags, u8 width)
|
||||
{
|
||||
if (flags & CLK_DIVIDER_ONE_BASED)
|
||||
return div;
|
||||
if (flags & CLK_DIVIDER_POWER_OF_TWO)
|
||||
return __ffs(div);
|
||||
if (flags & CLK_DIVIDER_MAX_AT_ZERO)
|
||||
return (div == clk_div_mask(width) + 1) ? 0 : div;
|
||||
if (table)
|
||||
return _get_table_val(table, div);
|
||||
return div - 1;
|
||||
}
|
||||
int divider_get_val(unsigned long rate, unsigned long parent_rate,
|
||||
const struct clk_div_table *table, u8 width,
|
||||
unsigned long flags)
|
||||
{
|
||||
unsigned int div, value;
|
||||
|
||||
div = DIV_ROUND_UP_ULL((u64)parent_rate, rate);
|
||||
|
||||
if (!_is_valid_div(table, div, flags))
|
||||
return -EINVAL;
|
||||
|
||||
value = _get_val(table, div, flags, width);
|
||||
|
||||
return min_t(unsigned int, value, clk_div_mask(width));
|
||||
}
|
||||
|
||||
static ulong clk_divider_set_rate(struct clk *clk, unsigned long rate)
|
||||
{
|
||||
struct clk_divider *divider = to_clk_divider(clk_dev_binded(clk) ?
|
||||
dev_get_clk_ptr(clk->dev) : clk);
|
||||
unsigned long parent_rate = clk_get_parent_rate(clk);
|
||||
int value;
|
||||
u32 val;
|
||||
|
||||
value = divider_get_val(rate, parent_rate, divider->table,
|
||||
divider->width, divider->flags);
|
||||
if (value < 0)
|
||||
return value;
|
||||
|
||||
if (divider->flags & CLK_DIVIDER_HIWORD_MASK) {
|
||||
val = clk_div_mask(divider->width) << (divider->shift + 16);
|
||||
} else {
|
||||
val = readl(divider->reg);
|
||||
val &= ~(clk_div_mask(divider->width) << divider->shift);
|
||||
}
|
||||
val |= (u32)value << divider->shift;
|
||||
writel(val, divider->reg);
|
||||
|
||||
return clk_get_rate(clk);
|
||||
}
|
||||
|
||||
const struct clk_ops clk_divider_ops = {
|
||||
.get_rate = clk_divider_recalc_rate,
|
||||
.set_rate = clk_divider_set_rate,
|
||||
};
|
||||
|
||||
static struct clk *_register_divider(struct device *dev, const char *name,
|
||||
|
159
drivers/clk/clk-gate.c
Normal file
159
drivers/clk/clk-gate.c
Normal file
@ -0,0 +1,159 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2010-2011 Canonical Ltd <jeremy.kerr@canonical.com>
|
||||
* Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org>
|
||||
* Copyright 2019 NXP
|
||||
*
|
||||
* Gated clock implementation
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <malloc.h>
|
||||
#include <clk-uclass.h>
|
||||
#include <dm/device.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <clk.h>
|
||||
#include "clk.h"
|
||||
|
||||
#define UBOOT_DM_CLK_GATE "clk_gate"
|
||||
|
||||
/**
|
||||
* DOC: basic gatable clock which can gate and ungate it's output
|
||||
*
|
||||
* Traits of this clock:
|
||||
* prepare - clk_(un)prepare only ensures parent is (un)prepared
|
||||
* enable - clk_enable and clk_disable are functional & control gating
|
||||
* rate - inherits rate from parent. No clk_set_rate support
|
||||
* parent - fixed parent. No clk_set_parent support
|
||||
*/
|
||||
|
||||
/*
|
||||
* It works on following logic:
|
||||
*
|
||||
* For enabling clock, enable = 1
|
||||
* set2dis = 1 -> clear bit -> set = 0
|
||||
* set2dis = 0 -> set bit -> set = 1
|
||||
*
|
||||
* For disabling clock, enable = 0
|
||||
* set2dis = 1 -> set bit -> set = 1
|
||||
* set2dis = 0 -> clear bit -> set = 0
|
||||
*
|
||||
* So, result is always: enable xor set2dis.
|
||||
*/
|
||||
static void clk_gate_endisable(struct clk *clk, int enable)
|
||||
{
|
||||
struct clk_gate *gate = to_clk_gate(clk_dev_binded(clk) ?
|
||||
dev_get_clk_ptr(clk->dev) : clk);
|
||||
int set = gate->flags & CLK_GATE_SET_TO_DISABLE ? 1 : 0;
|
||||
u32 reg;
|
||||
|
||||
set ^= enable;
|
||||
|
||||
if (gate->flags & CLK_GATE_HIWORD_MASK) {
|
||||
reg = BIT(gate->bit_idx + 16);
|
||||
if (set)
|
||||
reg |= BIT(gate->bit_idx);
|
||||
} else {
|
||||
#if CONFIG_IS_ENABLED(SANDBOX_CLK_CCF)
|
||||
reg = gate->io_gate_val;
|
||||
#else
|
||||
reg = readl(gate->reg);
|
||||
#endif
|
||||
|
||||
if (set)
|
||||
reg |= BIT(gate->bit_idx);
|
||||
else
|
||||
reg &= ~BIT(gate->bit_idx);
|
||||
}
|
||||
|
||||
writel(reg, gate->reg);
|
||||
}
|
||||
|
||||
static int clk_gate_enable(struct clk *clk)
|
||||
{
|
||||
clk_gate_endisable(clk, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int clk_gate_disable(struct clk *clk)
|
||||
{
|
||||
clk_gate_endisable(clk, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int clk_gate_is_enabled(struct clk *clk)
|
||||
{
|
||||
struct clk_gate *gate = to_clk_gate(clk_dev_binded(clk) ?
|
||||
dev_get_clk_ptr(clk->dev) : clk);
|
||||
u32 reg;
|
||||
|
||||
#if CONFIG_IS_ENABLED(SANDBOX_CLK_CCF)
|
||||
reg = gate->io_gate_val;
|
||||
#else
|
||||
reg = readl(gate->reg);
|
||||
#endif
|
||||
|
||||
/* if a set bit disables this clk, flip it before masking */
|
||||
if (gate->flags & CLK_GATE_SET_TO_DISABLE)
|
||||
reg ^= BIT(gate->bit_idx);
|
||||
|
||||
reg &= BIT(gate->bit_idx);
|
||||
|
||||
return reg ? 1 : 0;
|
||||
}
|
||||
|
||||
const struct clk_ops clk_gate_ops = {
|
||||
.enable = clk_gate_enable,
|
||||
.disable = clk_gate_disable,
|
||||
.get_rate = clk_generic_get_rate,
|
||||
};
|
||||
|
||||
struct clk *clk_register_gate(struct device *dev, const char *name,
|
||||
const char *parent_name, unsigned long flags,
|
||||
void __iomem *reg, u8 bit_idx,
|
||||
u8 clk_gate_flags, spinlock_t *lock)
|
||||
{
|
||||
struct clk_gate *gate;
|
||||
struct clk *clk;
|
||||
int ret;
|
||||
|
||||
if (clk_gate_flags & CLK_GATE_HIWORD_MASK) {
|
||||
if (bit_idx > 15) {
|
||||
pr_err("gate bit exceeds LOWORD field\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
}
|
||||
|
||||
/* allocate the gate */
|
||||
gate = kzalloc(sizeof(*gate), GFP_KERNEL);
|
||||
if (!gate)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
/* struct clk_gate assignments */
|
||||
gate->reg = reg;
|
||||
gate->bit_idx = bit_idx;
|
||||
gate->flags = clk_gate_flags;
|
||||
#if CONFIG_IS_ENABLED(SANDBOX_CLK_CCF)
|
||||
gate->io_gate_val = *(u32 *)reg;
|
||||
#endif
|
||||
|
||||
clk = &gate->clk;
|
||||
|
||||
ret = clk_register(clk, UBOOT_DM_CLK_GATE, name, parent_name);
|
||||
if (ret) {
|
||||
kfree(gate);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
return clk;
|
||||
}
|
||||
|
||||
U_BOOT_DRIVER(clk_gate) = {
|
||||
.name = UBOOT_DM_CLK_GATE,
|
||||
.id = UCLASS_CLK,
|
||||
.ops = &clk_gate_ops,
|
||||
.flags = DM_FLAG_PRE_RELOC,
|
||||
};
|
@ -35,7 +35,8 @@
|
||||
int clk_mux_val_to_index(struct clk *clk, u32 *table, unsigned int flags,
|
||||
unsigned int val)
|
||||
{
|
||||
struct clk_mux *mux = to_clk_mux(clk);
|
||||
struct clk_mux *mux = to_clk_mux(clk_dev_binded(clk) ?
|
||||
dev_get_clk_ptr(clk->dev) : clk);
|
||||
int num_parents = mux->num_parents;
|
||||
|
||||
if (table) {
|
||||
@ -59,9 +60,27 @@ int clk_mux_val_to_index(struct clk *clk, u32 *table, unsigned int flags,
|
||||
return val;
|
||||
}
|
||||
|
||||
static u8 clk_mux_get_parent(struct clk *clk)
|
||||
unsigned int clk_mux_index_to_val(u32 *table, unsigned int flags, u8 index)
|
||||
{
|
||||
struct clk_mux *mux = to_clk_mux(clk);
|
||||
unsigned int val = index;
|
||||
|
||||
if (table) {
|
||||
val = table[index];
|
||||
} else {
|
||||
if (flags & CLK_MUX_INDEX_BIT)
|
||||
val = 1 << index;
|
||||
|
||||
if (flags & CLK_MUX_INDEX_ONE)
|
||||
val++;
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
u8 clk_mux_get_parent(struct clk *clk)
|
||||
{
|
||||
struct clk_mux *mux = to_clk_mux(clk_dev_binded(clk) ?
|
||||
dev_get_clk_ptr(clk->dev) : clk);
|
||||
u32 val;
|
||||
|
||||
#if CONFIG_IS_ENABLED(SANDBOX_CLK_CCF)
|
||||
@ -75,8 +94,57 @@ static u8 clk_mux_get_parent(struct clk *clk)
|
||||
return clk_mux_val_to_index(clk, mux->table, mux->flags, val);
|
||||
}
|
||||
|
||||
static int clk_fetch_parent_index(struct clk *clk,
|
||||
struct clk *parent)
|
||||
{
|
||||
struct clk_mux *mux = to_clk_mux(clk_dev_binded(clk) ?
|
||||
dev_get_clk_ptr(clk->dev) : clk);
|
||||
|
||||
int i;
|
||||
|
||||
if (!parent)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < mux->num_parents; i++) {
|
||||
if (!strcmp(parent->dev->name, mux->parent_names[i]))
|
||||
return i;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int clk_mux_set_parent(struct clk *clk, struct clk *parent)
|
||||
{
|
||||
struct clk_mux *mux = to_clk_mux(clk_dev_binded(clk) ?
|
||||
dev_get_clk_ptr(clk->dev) : clk);
|
||||
int index;
|
||||
u32 val;
|
||||
u32 reg;
|
||||
|
||||
index = clk_fetch_parent_index(clk, parent);
|
||||
if (index < 0) {
|
||||
printf("Could not fetch index\n");
|
||||
return index;
|
||||
}
|
||||
|
||||
val = clk_mux_index_to_val(mux->table, mux->flags, index);
|
||||
|
||||
if (mux->flags & CLK_MUX_HIWORD_MASK) {
|
||||
reg = mux->mask << (mux->shift + 16);
|
||||
} else {
|
||||
reg = readl(mux->reg);
|
||||
reg &= ~(mux->mask << mux->shift);
|
||||
}
|
||||
val = val << mux->shift;
|
||||
reg |= val;
|
||||
writel(reg, mux->reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct clk_ops clk_mux_ops = {
|
||||
.get_rate = clk_generic_get_rate,
|
||||
.get_rate = clk_generic_get_rate,
|
||||
.set_parent = clk_mux_set_parent,
|
||||
};
|
||||
|
||||
struct clk *clk_hw_register_mux_table(struct device *dev, const char *name,
|
||||
|
@ -285,6 +285,9 @@ int clk_set_defaults(struct udevice *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!dev_of_valid(dev))
|
||||
return 0;
|
||||
|
||||
/* If this not in SPL and pre-reloc state, don't take any action. */
|
||||
if (!(IS_ENABLED(CONFIG_SPL_BUILD) || (gd->flags & GD_FLG_RELOC)))
|
||||
return 0;
|
||||
|
@ -55,3 +55,11 @@ const char *clk_hw_get_name(const struct clk *hw)
|
||||
{
|
||||
return hw->dev->name;
|
||||
}
|
||||
|
||||
bool clk_dev_binded(struct clk *clk)
|
||||
{
|
||||
if (clk->dev && (clk->dev->flags & DM_FLAG_BOUND))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -6,13 +6,7 @@
|
||||
#include <common.h>
|
||||
#include <clk-uclass.h>
|
||||
#include <dm.h>
|
||||
|
||||
struct clk_fixed_rate {
|
||||
struct clk clk;
|
||||
unsigned long fixed_rate;
|
||||
};
|
||||
|
||||
#define to_clk_fixed_rate(dev) ((struct clk_fixed_rate *)dev_get_platdata(dev))
|
||||
#include <linux/clk-provider.h>
|
||||
|
||||
static ulong clk_fixed_rate_get_rate(struct clk *clk)
|
||||
{
|
||||
|
@ -130,6 +130,80 @@ U_BOOT_DRIVER(sandbox_clk_gate2) = {
|
||||
.ops = &clk_gate2_ops,
|
||||
};
|
||||
|
||||
static unsigned long sandbox_clk_composite_divider_recalc_rate(struct clk *clk)
|
||||
{
|
||||
struct clk_divider *divider = (struct clk_divider *)to_clk_divider(clk);
|
||||
struct clk_composite *composite = (struct clk_composite *)clk->data;
|
||||
ulong parent_rate = clk_get_parent_rate(&composite->clk);
|
||||
unsigned int val;
|
||||
|
||||
val = divider->io_divider_val;
|
||||
val >>= divider->shift;
|
||||
val &= clk_div_mask(divider->width);
|
||||
|
||||
return divider_recalc_rate(clk, parent_rate, val, divider->table,
|
||||
divider->flags, divider->width);
|
||||
}
|
||||
|
||||
static const struct clk_ops sandbox_clk_composite_divider_ops = {
|
||||
.get_rate = sandbox_clk_composite_divider_recalc_rate,
|
||||
};
|
||||
|
||||
struct clk *sandbox_clk_composite(const char *name,
|
||||
const char * const *parent_names,
|
||||
int num_parents, void __iomem *reg,
|
||||
unsigned long flags)
|
||||
{
|
||||
struct clk *clk = ERR_PTR(-ENOMEM);
|
||||
struct clk_divider *div = NULL;
|
||||
struct clk_gate *gate = NULL;
|
||||
struct clk_mux *mux = NULL;
|
||||
|
||||
mux = kzalloc(sizeof(*mux), GFP_KERNEL);
|
||||
if (!mux)
|
||||
goto fail;
|
||||
|
||||
mux->reg = reg;
|
||||
mux->shift = 24;
|
||||
mux->mask = 0x7;
|
||||
mux->num_parents = num_parents;
|
||||
mux->flags = flags;
|
||||
mux->parent_names = parent_names;
|
||||
|
||||
div = kzalloc(sizeof(*div), GFP_KERNEL);
|
||||
if (!div)
|
||||
goto fail;
|
||||
|
||||
div->reg = reg;
|
||||
div->shift = 16;
|
||||
div->width = 3;
|
||||
div->flags = CLK_DIVIDER_ROUND_CLOSEST | flags;
|
||||
|
||||
gate = kzalloc(sizeof(*gate), GFP_KERNEL);
|
||||
if (!gate)
|
||||
goto fail;
|
||||
|
||||
gate->reg = reg;
|
||||
gate->bit_idx = 28;
|
||||
gate->flags = flags;
|
||||
|
||||
clk = clk_register_composite(NULL, name,
|
||||
parent_names, num_parents,
|
||||
&mux->clk, &clk_mux_ops, &div->clk,
|
||||
&sandbox_clk_composite_divider_ops,
|
||||
&gate->clk, &clk_gate_ops, flags);
|
||||
if (IS_ERR(clk))
|
||||
goto fail;
|
||||
|
||||
return clk;
|
||||
|
||||
fail:
|
||||
kfree(gate);
|
||||
kfree(div);
|
||||
kfree(mux);
|
||||
return ERR_CAST(clk);
|
||||
}
|
||||
|
||||
/* --- Sandbox Gate --- */
|
||||
/* The CCF core driver itself */
|
||||
static const struct udevice_id sandbox_clk_ccf_test_ids[] = {
|
||||
@ -138,6 +212,7 @@ static const struct udevice_id sandbox_clk_ccf_test_ids[] = {
|
||||
};
|
||||
|
||||
static const char *const usdhc_sels[] = { "pll3_60m", "pll3_80m", };
|
||||
static const char *const i2c_sels[] = { "pll3_60m", "pll3_80m", };
|
||||
|
||||
static int sandbox_clk_ccf_probe(struct udevice *dev)
|
||||
{
|
||||
@ -174,6 +249,11 @@ static int sandbox_clk_ccf_probe(struct udevice *dev)
|
||||
sandbox_clk_mux("usdhc2_sel", ®, 17, 1, usdhc_sels,
|
||||
ARRAY_SIZE(usdhc_sels)));
|
||||
|
||||
reg = BIT(28) | BIT(24) | BIT(16);
|
||||
clk_dm(SANDBOX_CLK_I2C,
|
||||
sandbox_clk_composite("i2c", i2c_sels, ARRAY_SIZE(i2c_sels),
|
||||
®, 0));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -60,7 +60,18 @@ static int clk_gate2_disable(struct clk *clk)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ulong clk_gate2_set_rate(struct clk *clk, ulong rate)
|
||||
{
|
||||
struct clk *parent = clk_get_parent(clk);
|
||||
|
||||
if (parent)
|
||||
return clk_set_rate(parent, rate);
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static const struct clk_ops clk_gate2_ops = {
|
||||
.set_rate = clk_gate2_set_rate,
|
||||
.enable = clk_gate2_enable,
|
||||
.disable = clk_gate2_disable,
|
||||
.get_rate = clk_generic_get_rate,
|
||||
|
@ -36,6 +36,23 @@ static inline struct clk *imx_clk_gate2(const char *name, const char *parent,
|
||||
shift, 0x3, 0);
|
||||
}
|
||||
|
||||
static inline struct clk *imx_clk_gate4(const char *name, const char *parent,
|
||||
void __iomem *reg, u8 shift)
|
||||
{
|
||||
return clk_register_gate2(NULL, name, parent,
|
||||
CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE,
|
||||
reg, shift, 0x3, 0);
|
||||
}
|
||||
|
||||
static inline struct clk *imx_clk_gate4_flags(const char *name,
|
||||
const char *parent, void __iomem *reg, u8 shift,
|
||||
unsigned long flags)
|
||||
{
|
||||
return clk_register_gate2(NULL, name, parent,
|
||||
flags | CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE,
|
||||
reg, shift, 0x3, 0);
|
||||
}
|
||||
|
||||
static inline struct clk *imx_clk_fixed_factor(const char *name,
|
||||
const char *parent, unsigned int mult, unsigned int div)
|
||||
{
|
||||
@ -50,6 +67,14 @@ static inline struct clk *imx_clk_divider(const char *name, const char *parent,
|
||||
reg, shift, width, 0);
|
||||
}
|
||||
|
||||
static inline struct clk *imx_clk_divider2(const char *name, const char *parent,
|
||||
void __iomem *reg, u8 shift, u8 width)
|
||||
{
|
||||
return clk_register_divider(NULL, name, parent,
|
||||
CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE,
|
||||
reg, shift, width, 0);
|
||||
}
|
||||
|
||||
struct clk *imx_clk_pfd(const char *name, const char *parent_name,
|
||||
void __iomem *reg, u8 idx);
|
||||
|
||||
@ -57,6 +82,16 @@ struct clk *imx_clk_fixup_mux(const char *name, void __iomem *reg,
|
||||
u8 shift, u8 width, const char * const *parents,
|
||||
int num_parents, void (*fixup)(u32 *val));
|
||||
|
||||
static inline struct clk *imx_clk_mux_flags(const char *name,
|
||||
void __iomem *reg, u8 shift, u8 width,
|
||||
const char * const *parents, int num_parents,
|
||||
unsigned long flags)
|
||||
{
|
||||
return clk_register_mux(NULL, name, parents, num_parents,
|
||||
flags | CLK_SET_RATE_NO_REPARENT, reg, shift,
|
||||
width, 0);
|
||||
}
|
||||
|
||||
static inline struct clk *imx_clk_mux(const char *name, void __iomem *reg,
|
||||
u8 shift, u8 width, const char * const *parents,
|
||||
int num_parents)
|
||||
@ -66,4 +101,50 @@ static inline struct clk *imx_clk_mux(const char *name, void __iomem *reg,
|
||||
width, 0);
|
||||
}
|
||||
|
||||
static inline struct clk *imx_clk_mux2(const char *name, void __iomem *reg,
|
||||
u8 shift, u8 width, const char * const *parents,
|
||||
int num_parents)
|
||||
{
|
||||
return clk_register_mux(NULL, name, parents, num_parents,
|
||||
CLK_SET_RATE_NO_REPARENT | CLK_OPS_PARENT_ENABLE,
|
||||
reg, shift, width, 0);
|
||||
}
|
||||
|
||||
static inline struct clk *imx_clk_gate(const char *name, const char *parent,
|
||||
void __iomem *reg, u8 shift)
|
||||
{
|
||||
return clk_register_gate(NULL, name, parent, CLK_SET_RATE_PARENT, reg,
|
||||
shift, 0, NULL);
|
||||
}
|
||||
|
||||
static inline struct clk *imx_clk_gate_flags(const char *name, const char *parent,
|
||||
void __iomem *reg, u8 shift, unsigned long flags)
|
||||
{
|
||||
return clk_register_gate(NULL, name, parent, flags | CLK_SET_RATE_PARENT, reg,
|
||||
shift, 0, NULL);
|
||||
}
|
||||
|
||||
static inline struct clk *imx_clk_gate3(const char *name, const char *parent,
|
||||
void __iomem *reg, u8 shift)
|
||||
{
|
||||
return clk_register_gate(NULL, name, parent,
|
||||
CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE,
|
||||
reg, shift, 0, NULL);
|
||||
}
|
||||
|
||||
struct clk *imx8m_clk_composite_flags(const char *name,
|
||||
const char * const *parent_names,
|
||||
int num_parents, void __iomem *reg, unsigned long flags);
|
||||
|
||||
#define __imx8m_clk_composite(name, parent_names, reg, flags) \
|
||||
imx8m_clk_composite_flags(name, parent_names, \
|
||||
ARRAY_SIZE(parent_names), reg, \
|
||||
flags | CLK_SET_RATE_NO_REPARENT | CLK_OPS_PARENT_ENABLE)
|
||||
|
||||
#define imx8m_clk_composite(name, parent_names, reg) \
|
||||
__imx8m_clk_composite(name, parent_names, reg, 0)
|
||||
|
||||
#define imx8m_clk_composite_critical(name, parent_names, reg) \
|
||||
__imx8m_clk_composite(name, parent_names, reg, CLK_IS_CRITICAL)
|
||||
|
||||
#endif /* __MACH_IMX_CLK_H */
|
||||
|
@ -356,4 +356,13 @@ static inline bool clk_valid(struct clk *clk)
|
||||
* @return zero on success, or -ENOENT on error
|
||||
*/
|
||||
int clk_get_by_id(ulong id, struct clk **clkp);
|
||||
|
||||
/**
|
||||
* clk_dev_binded() - Check whether the clk has a device binded
|
||||
*
|
||||
* @clk A pointer to the clk
|
||||
*
|
||||
* @return true on binded, or false on no
|
||||
*/
|
||||
bool clk_dev_binded(struct clk *clk);
|
||||
#endif
|
||||
|
@ -8,6 +8,7 @@
|
||||
*/
|
||||
#ifndef __LINUX_CLK_PROVIDER_H
|
||||
#define __LINUX_CLK_PROVIDER_H
|
||||
#include <clk-uclass.h>
|
||||
|
||||
static inline void clk_dm(ulong id, struct clk *clk)
|
||||
{
|
||||
@ -66,6 +67,29 @@ struct clk_mux {
|
||||
};
|
||||
|
||||
#define to_clk_mux(_clk) container_of(_clk, struct clk_mux, clk)
|
||||
extern const struct clk_ops clk_mux_ops;
|
||||
u8 clk_mux_get_parent(struct clk *clk);
|
||||
|
||||
struct clk_gate {
|
||||
struct clk clk;
|
||||
void __iomem *reg;
|
||||
u8 bit_idx;
|
||||
u8 flags;
|
||||
#if CONFIG_IS_ENABLED(SANDBOX_CLK_CCF)
|
||||
u32 io_gate_val;
|
||||
#endif
|
||||
};
|
||||
|
||||
#define to_clk_gate(_clk) container_of(_clk, struct clk_gate, clk)
|
||||
|
||||
#define CLK_GATE_SET_TO_DISABLE BIT(0)
|
||||
#define CLK_GATE_HIWORD_MASK BIT(1)
|
||||
|
||||
extern const struct clk_ops clk_gate_ops;
|
||||
struct clk *clk_register_gate(struct device *dev, const char *name,
|
||||
const char *parent_name, unsigned long flags,
|
||||
void __iomem *reg, u8 bit_idx,
|
||||
u8 clk_gate_flags, spinlock_t *lock);
|
||||
|
||||
struct clk_div_table {
|
||||
unsigned int val;
|
||||
@ -94,6 +118,11 @@ struct clk_divider {
|
||||
#define CLK_DIVIDER_ROUND_CLOSEST BIT(4)
|
||||
#define CLK_DIVIDER_READ_ONLY BIT(5)
|
||||
#define CLK_DIVIDER_MAX_AT_ZERO BIT(6)
|
||||
extern const struct clk_ops clk_divider_ops;
|
||||
unsigned long divider_recalc_rate(struct clk *hw, unsigned long parent_rate,
|
||||
unsigned int val,
|
||||
const struct clk_div_table *table,
|
||||
unsigned long flags, unsigned long width);
|
||||
|
||||
struct clk_fixed_factor {
|
||||
struct clk clk;
|
||||
@ -104,6 +133,35 @@ struct clk_fixed_factor {
|
||||
#define to_clk_fixed_factor(_clk) container_of(_clk, struct clk_fixed_factor,\
|
||||
clk)
|
||||
|
||||
struct clk_fixed_rate {
|
||||
struct clk clk;
|
||||
unsigned long fixed_rate;
|
||||
};
|
||||
|
||||
#define to_clk_fixed_rate(dev) ((struct clk_fixed_rate *)dev_get_platdata(dev))
|
||||
|
||||
struct clk_composite {
|
||||
struct clk clk;
|
||||
struct clk_ops ops;
|
||||
|
||||
struct clk *mux;
|
||||
struct clk *rate;
|
||||
struct clk *gate;
|
||||
|
||||
const struct clk_ops *mux_ops;
|
||||
const struct clk_ops *rate_ops;
|
||||
const struct clk_ops *gate_ops;
|
||||
};
|
||||
|
||||
#define to_clk_composite(_clk) container_of(_clk, struct clk_composite, clk)
|
||||
|
||||
struct clk *clk_register_composite(struct device *dev, const char *name,
|
||||
const char * const *parent_names, int num_parents,
|
||||
struct clk *mux_clk, const struct clk_ops *mux_ops,
|
||||
struct clk *rate_clk, const struct clk_ops *rate_ops,
|
||||
struct clk *gate_clk, const struct clk_ops *gate_ops,
|
||||
unsigned long flags);
|
||||
|
||||
int clk_register(struct clk *clk, const char *drv_name, const char *name,
|
||||
const char *parent_name);
|
||||
|
||||
|
@ -19,6 +19,7 @@ enum {
|
||||
SANDBOX_CLK_ECSPI1,
|
||||
SANDBOX_CLK_USDHC1_SEL,
|
||||
SANDBOX_CLK_USDHC2_SEL,
|
||||
SANDBOX_CLK_I2C,
|
||||
};
|
||||
|
||||
enum sandbox_pllv3_type {
|
||||
|
@ -56,6 +56,14 @@ static int dm_test_clk_ccf(struct unit_test_state *uts)
|
||||
pclk = clk_get_parent(clk);
|
||||
ut_asserteq_str("pll3_80m", pclk->dev->name);
|
||||
|
||||
/* Test the composite of CCF */
|
||||
ret = clk_get_by_id(SANDBOX_CLK_I2C, &clk);
|
||||
ut_assertok(ret);
|
||||
ut_asserteq_str("i2c", clk->dev->name);
|
||||
|
||||
rate = clk_get_rate(clk);
|
||||
ut_asserteq(rate, 60000000);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user