mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-30 11:41:38 -04:00
Merge branches 'clk-imx7ulp', 'clk-imx6-fixes', 'clk-imx-fixes', 'clk-imx8qxp' and 'clk-imx8mq' into clk-next
- NXP i.MX7ULP SoC clock support - Support for i.MX8QXP SoC clocks - Support for NXP i.MX8MQ clock controllers * clk-imx7ulp: clk: imx: add imx7ulp clk driver clk: imx: implement new clk_hw based APIs clk: imx: make mux parent strings const dt-bindings: clock: add imx7ulp clock binding doc clk: imx: add imx7ulp composite clk support clk: imx: add pfdv2 support clk: imx: add pllv4 support clk: fractional-divider: add CLK_FRAC_DIVIDER_ZERO_BASED flag support clk: imx: add gatable clock divider support * clk-imx6-fixes: clk: imx6q: handle ENET PLL bypass clk: imx6q: optionally get CCM inputs via standard clock handles clk: imx6q: reset exclusive gates on init * clk-imx-fixes: clk: imx6q: add DCICx clocks gate clk: imx6sl: ensure MMDC CH0 handshake is bypassed clk: imx7d: remove UART1 clock setting * clk-imx8qxp: clk: imx: add imx8qxp lpcg driver clk: imx: add lpcg clock support clk: imx: add imx8qxp clk driver clk: imx: add scu clock common part clk: imx: add configuration option for mmio clks dt-bindings: clock: add imx8qxp lpcg clock binding dt-bindings: clock: imx8qxp: add SCU clock IDs firmware: imx: add pm svc headfile dt-bindings: fsl: scu: update power domain binding firmware: imx: remove resource id enums dt-bindings: imx: add scu resource id headfile * clk-imx8mq: clk: imx: Make the i.MX8MQ CCM clock driver CLK_IMX8MQ dependant clk: imx: remove redundant initialization of ret to zero clk: imx: Add SCCG PLL type clk: imx: Add fractional PLL output clock clk: imx: Add clock driver for i.MX8MQ CCM clk: imx: Add imx composite clock dt-bindings: Add binding for i.MX8MQ CCM
This commit is contained in:
commit
58c05c823b
41 changed files with 4996 additions and 609 deletions
|
|
@ -293,7 +293,9 @@ config COMMON_CLK_BD718XX
|
|||
source "drivers/clk/actions/Kconfig"
|
||||
source "drivers/clk/bcm/Kconfig"
|
||||
source "drivers/clk/hisilicon/Kconfig"
|
||||
source "drivers/clk/imx/Kconfig"
|
||||
source "drivers/clk/imgtec/Kconfig"
|
||||
source "drivers/clk/imx/Kconfig"
|
||||
source "drivers/clk/ingenic/Kconfig"
|
||||
source "drivers/clk/keystone/Kconfig"
|
||||
source "drivers/clk/mediatek/Kconfig"
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ obj-$(CONFIG_ARCH_DAVINCI) += davinci/
|
|||
obj-$(CONFIG_H8300) += h8300/
|
||||
obj-$(CONFIG_ARCH_HISI) += hisilicon/
|
||||
obj-y += imgtec/
|
||||
obj-$(CONFIG_ARCH_MXC) += imx/
|
||||
obj-y += imx/
|
||||
obj-y += ingenic/
|
||||
obj-$(CONFIG_ARCH_K3) += keystone/
|
||||
obj-$(CONFIG_ARCH_KEYSTONE) += keystone/
|
||||
|
|
|
|||
|
|
@ -37,6 +37,11 @@ static unsigned long clk_fd_recalc_rate(struct clk_hw *hw,
|
|||
m = (val & fd->mmask) >> fd->mshift;
|
||||
n = (val & fd->nmask) >> fd->nshift;
|
||||
|
||||
if (fd->flags & CLK_FRAC_DIVIDER_ZERO_BASED) {
|
||||
m++;
|
||||
n++;
|
||||
}
|
||||
|
||||
if (!n || !m)
|
||||
return parent_rate;
|
||||
|
||||
|
|
@ -100,6 +105,11 @@ static int clk_fd_set_rate(struct clk_hw *hw, unsigned long rate,
|
|||
GENMASK(fd->mwidth - 1, 0), GENMASK(fd->nwidth - 1, 0),
|
||||
&m, &n);
|
||||
|
||||
if (fd->flags & CLK_FRAC_DIVIDER_ZERO_BASED) {
|
||||
m--;
|
||||
n--;
|
||||
}
|
||||
|
||||
if (fd->lock)
|
||||
spin_lock_irqsave(fd->lock, flags);
|
||||
else
|
||||
|
|
|
|||
22
drivers/clk/imx/Kconfig
Normal file
22
drivers/clk/imx/Kconfig
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
# common clock support for NXP i.MX SoC family.
|
||||
config MXC_CLK
|
||||
bool
|
||||
def_bool ARCH_MXC
|
||||
|
||||
config MXC_CLK_SCU
|
||||
bool
|
||||
depends on IMX_SCU
|
||||
|
||||
config CLK_IMX8MQ
|
||||
bool "IMX8MQ CCM Clock Driver"
|
||||
depends on ARCH_MXC && ARM64
|
||||
help
|
||||
Build the driver for i.MX8MQ CCM Clock Driver
|
||||
|
||||
config CLK_IMX8QXP
|
||||
bool "IMX8QXP SCU Clock"
|
||||
depends on ARCH_MXC && IMX_SCU && ARM64
|
||||
select MXC_CLK_SCU
|
||||
help
|
||||
Build the driver for IMX8QXP SCU based clocks.
|
||||
|
|
@ -1,17 +1,31 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
obj-y += \
|
||||
obj-$(CONFIG_MXC_CLK) += \
|
||||
clk.o \
|
||||
clk-busy.o \
|
||||
clk-composite-8m.o \
|
||||
clk-cpu.o \
|
||||
clk-composite-7ulp.o \
|
||||
clk-divider-gate.o \
|
||||
clk-fixup-div.o \
|
||||
clk-fixup-mux.o \
|
||||
clk-frac-pll.o \
|
||||
clk-gate-exclusive.o \
|
||||
clk-gate2.o \
|
||||
clk-pfd.o \
|
||||
clk-pfdv2.o \
|
||||
clk-pllv1.o \
|
||||
clk-pllv2.o \
|
||||
clk-pllv3.o \
|
||||
clk-pfd.o
|
||||
clk-pllv4.o \
|
||||
clk-sccg-pll.o
|
||||
|
||||
obj-$(CONFIG_MXC_CLK_SCU) += \
|
||||
clk-scu.o \
|
||||
clk-lpcg-scu.o
|
||||
|
||||
obj-$(CONFIG_CLK_IMX8MQ) += clk-imx8mq.o
|
||||
obj-$(CONFIG_CLK_IMX8QXP) += clk-imx8qxp.o clk-imx8qxp-lpcg.o
|
||||
|
||||
obj-$(CONFIG_SOC_IMX1) += clk-imx1.o
|
||||
obj-$(CONFIG_SOC_IMX21) += clk-imx21.o
|
||||
|
|
@ -26,4 +40,5 @@ obj-$(CONFIG_SOC_IMX6SLL) += clk-imx6sll.o
|
|||
obj-$(CONFIG_SOC_IMX6SX) += clk-imx6sx.o
|
||||
obj-$(CONFIG_SOC_IMX6UL) += clk-imx6ul.o
|
||||
obj-$(CONFIG_SOC_IMX7D) += clk-imx7d.o
|
||||
obj-$(CONFIG_SOC_IMX7ULP) += clk-imx7ulp.o
|
||||
obj-$(CONFIG_SOC_VF610) += clk-vf610.o
|
||||
|
|
|
|||
|
|
@ -154,7 +154,7 @@ static const struct clk_ops clk_busy_mux_ops = {
|
|||
|
||||
struct clk *imx_clk_busy_mux(const char *name, void __iomem *reg, u8 shift,
|
||||
u8 width, void __iomem *busy_reg, u8 busy_shift,
|
||||
const char **parent_names, int num_parents)
|
||||
const char * const *parent_names, int num_parents)
|
||||
{
|
||||
struct clk_busy_mux *busy;
|
||||
struct clk *clk;
|
||||
|
|
|
|||
87
drivers/clk/imx/clk-composite-7ulp.c
Normal file
87
drivers/clk/imx/clk-composite-7ulp.c
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2016 Freescale Semiconductor, Inc.
|
||||
* Copyright 2017~2018 NXP
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "clk.h"
|
||||
|
||||
#define PCG_PCS_SHIFT 24
|
||||
#define PCG_PCS_MASK 0x7
|
||||
#define PCG_CGC_SHIFT 30
|
||||
#define PCG_FRAC_SHIFT 3
|
||||
#define PCG_FRAC_WIDTH 1
|
||||
#define PCG_FRAC_MASK BIT(3)
|
||||
#define PCG_PCD_SHIFT 0
|
||||
#define PCG_PCD_WIDTH 3
|
||||
#define PCG_PCD_MASK 0x7
|
||||
|
||||
struct clk_hw *imx7ulp_clk_composite(const char *name,
|
||||
const char * const *parent_names,
|
||||
int num_parents, bool mux_present,
|
||||
bool rate_present, bool gate_present,
|
||||
void __iomem *reg)
|
||||
{
|
||||
struct clk_hw *mux_hw = NULL, *fd_hw = NULL, *gate_hw = NULL;
|
||||
struct clk_fractional_divider *fd = NULL;
|
||||
struct clk_gate *gate = NULL;
|
||||
struct clk_mux *mux = NULL;
|
||||
struct clk_hw *hw;
|
||||
|
||||
if (mux_present) {
|
||||
mux = kzalloc(sizeof(*mux), GFP_KERNEL);
|
||||
if (!mux)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
mux_hw = &mux->hw;
|
||||
mux->reg = reg;
|
||||
mux->shift = PCG_PCS_SHIFT;
|
||||
mux->mask = PCG_PCS_MASK;
|
||||
}
|
||||
|
||||
if (rate_present) {
|
||||
fd = kzalloc(sizeof(*fd), GFP_KERNEL);
|
||||
if (!fd) {
|
||||
kfree(mux);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
fd_hw = &fd->hw;
|
||||
fd->reg = reg;
|
||||
fd->mshift = PCG_FRAC_SHIFT;
|
||||
fd->mwidth = PCG_FRAC_WIDTH;
|
||||
fd->mmask = PCG_FRAC_MASK;
|
||||
fd->nshift = PCG_PCD_SHIFT;
|
||||
fd->nwidth = PCG_PCD_WIDTH;
|
||||
fd->nmask = PCG_PCD_MASK;
|
||||
fd->flags = CLK_FRAC_DIVIDER_ZERO_BASED;
|
||||
}
|
||||
|
||||
if (gate_present) {
|
||||
gate = kzalloc(sizeof(*gate), GFP_KERNEL);
|
||||
if (!gate) {
|
||||
kfree(mux);
|
||||
kfree(fd);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
gate_hw = &gate->hw;
|
||||
gate->reg = reg;
|
||||
gate->bit_idx = PCG_CGC_SHIFT;
|
||||
}
|
||||
|
||||
hw = clk_hw_register_composite(NULL, name, parent_names, num_parents,
|
||||
mux_hw, &clk_mux_ops, fd_hw,
|
||||
&clk_fractional_divider_ops, gate_hw,
|
||||
&clk_gate_ops, CLK_SET_RATE_GATE |
|
||||
CLK_SET_PARENT_GATE);
|
||||
if (IS_ERR(hw)) {
|
||||
kfree(mux);
|
||||
kfree(fd);
|
||||
kfree(gate);
|
||||
}
|
||||
|
||||
return hw;
|
||||
}
|
||||
178
drivers/clk/imx/clk-composite-8m.c
Normal file
178
drivers/clk/imx/clk-composite-8m.c
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright 2018 NXP
|
||||
*/
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/clk-provider.h>
|
||||
|
||||
#include "clk.h"
|
||||
|
||||
#define PCG_PREDIV_SHIFT 16
|
||||
#define PCG_PREDIV_WIDTH 3
|
||||
#define PCG_PREDIV_MAX 8
|
||||
|
||||
#define PCG_DIV_SHIFT 0
|
||||
#define PCG_DIV_WIDTH 6
|
||||
#define PCG_DIV_MAX 64
|
||||
|
||||
#define PCG_PCS_SHIFT 24
|
||||
#define PCG_PCS_MASK 0x7
|
||||
|
||||
#define PCG_CGC_SHIFT 28
|
||||
|
||||
static unsigned long imx8m_clk_composite_divider_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct clk_divider *divider = to_clk_divider(hw);
|
||||
unsigned long prediv_rate;
|
||||
unsigned int prediv_value;
|
||||
unsigned int div_value;
|
||||
|
||||
prediv_value = readl(divider->reg) >> divider->shift;
|
||||
prediv_value &= clk_div_mask(divider->width);
|
||||
|
||||
prediv_rate = divider_recalc_rate(hw, parent_rate, prediv_value,
|
||||
NULL, divider->flags,
|
||||
divider->width);
|
||||
|
||||
div_value = readl(divider->reg) >> PCG_DIV_SHIFT;
|
||||
div_value &= clk_div_mask(PCG_DIV_WIDTH);
|
||||
|
||||
return divider_recalc_rate(hw, prediv_rate, div_value, NULL,
|
||||
divider->flags, PCG_DIV_WIDTH);
|
||||
}
|
||||
|
||||
static int imx8m_clk_composite_compute_dividers(unsigned long rate,
|
||||
unsigned long parent_rate,
|
||||
int *prediv, int *postdiv)
|
||||
{
|
||||
int div1, div2;
|
||||
int error = INT_MAX;
|
||||
int ret = -EINVAL;
|
||||
|
||||
*prediv = 1;
|
||||
*postdiv = 1;
|
||||
|
||||
for (div1 = 1; div1 <= PCG_PREDIV_MAX; div1++) {
|
||||
for (div2 = 1; div2 <= PCG_DIV_MAX; div2++) {
|
||||
int new_error = ((parent_rate / div1) / div2) - rate;
|
||||
|
||||
if (abs(new_error) < abs(error)) {
|
||||
*prediv = div1;
|
||||
*postdiv = div2;
|
||||
error = new_error;
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static long imx8m_clk_composite_divider_round_rate(struct clk_hw *hw,
|
||||
unsigned long rate,
|
||||
unsigned long *prate)
|
||||
{
|
||||
int prediv_value;
|
||||
int div_value;
|
||||
|
||||
imx8m_clk_composite_compute_dividers(rate, *prate,
|
||||
&prediv_value, &div_value);
|
||||
rate = DIV_ROUND_UP(*prate, prediv_value);
|
||||
|
||||
return DIV_ROUND_UP(rate, div_value);
|
||||
|
||||
}
|
||||
|
||||
static int imx8m_clk_composite_divider_set_rate(struct clk_hw *hw,
|
||||
unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct clk_divider *divider = to_clk_divider(hw);
|
||||
unsigned long flags = 0;
|
||||
int prediv_value;
|
||||
int div_value;
|
||||
int ret;
|
||||
u32 val;
|
||||
|
||||
ret = imx8m_clk_composite_compute_dividers(rate, parent_rate,
|
||||
&prediv_value, &div_value);
|
||||
if (ret)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(divider->lock, flags);
|
||||
|
||||
val = readl(divider->reg);
|
||||
val &= ~((clk_div_mask(divider->width) << divider->shift) |
|
||||
(clk_div_mask(PCG_DIV_WIDTH) << PCG_DIV_SHIFT));
|
||||
|
||||
val |= (u32)(prediv_value - 1) << divider->shift;
|
||||
val |= (u32)(div_value - 1) << PCG_DIV_SHIFT;
|
||||
writel(val, divider->reg);
|
||||
|
||||
spin_unlock_irqrestore(divider->lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct clk_ops imx8m_clk_composite_divider_ops = {
|
||||
.recalc_rate = imx8m_clk_composite_divider_recalc_rate,
|
||||
.round_rate = imx8m_clk_composite_divider_round_rate,
|
||||
.set_rate = imx8m_clk_composite_divider_set_rate,
|
||||
};
|
||||
|
||||
struct clk *imx8m_clk_composite_flags(const char *name,
|
||||
const char **parent_names,
|
||||
int num_parents, void __iomem *reg,
|
||||
unsigned long flags)
|
||||
{
|
||||
struct clk_hw *hw = ERR_PTR(-ENOMEM), *mux_hw;
|
||||
struct clk_hw *div_hw, *gate_hw;
|
||||
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_hw = &mux->hw;
|
||||
mux->reg = reg;
|
||||
mux->shift = PCG_PCS_SHIFT;
|
||||
mux->mask = PCG_PCS_MASK;
|
||||
|
||||
div = kzalloc(sizeof(*div), GFP_KERNEL);
|
||||
if (!div)
|
||||
goto fail;
|
||||
|
||||
div_hw = &div->hw;
|
||||
div->reg = reg;
|
||||
div->shift = PCG_PREDIV_SHIFT;
|
||||
div->width = PCG_PREDIV_WIDTH;
|
||||
div->lock = &imx_ccm_lock;
|
||||
div->flags = CLK_DIVIDER_ROUND_CLOSEST;
|
||||
|
||||
gate = kzalloc(sizeof(*gate), GFP_KERNEL);
|
||||
if (!gate)
|
||||
goto fail;
|
||||
|
||||
gate_hw = &gate->hw;
|
||||
gate->reg = reg;
|
||||
gate->bit_idx = PCG_CGC_SHIFT;
|
||||
|
||||
hw = clk_hw_register_composite(NULL, name, parent_names, num_parents,
|
||||
mux_hw, &clk_mux_ops, div_hw,
|
||||
&imx8m_clk_composite_divider_ops,
|
||||
gate_hw, &clk_gate_ops, flags);
|
||||
if (IS_ERR(hw))
|
||||
goto fail;
|
||||
|
||||
return hw->clk;
|
||||
|
||||
fail:
|
||||
kfree(gate);
|
||||
kfree(div);
|
||||
kfree(mux);
|
||||
return ERR_CAST(hw);
|
||||
}
|
||||
221
drivers/clk/imx/clk-divider-gate.c
Normal file
221
drivers/clk/imx/clk-divider-gate.c
Normal file
|
|
@ -0,0 +1,221 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2018 NXP.
|
||||
* Dong Aisheng <aisheng.dong@nxp.com>
|
||||
*/
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "clk.h"
|
||||
|
||||
struct clk_divider_gate {
|
||||
struct clk_divider divider;
|
||||
u32 cached_val;
|
||||
};
|
||||
|
||||
static inline struct clk_divider_gate *to_clk_divider_gate(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_divider *div = to_clk_divider(hw);
|
||||
|
||||
return container_of(div, struct clk_divider_gate, divider);
|
||||
}
|
||||
|
||||
static unsigned long clk_divider_gate_recalc_rate_ro(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct clk_divider *div = to_clk_divider(hw);
|
||||
unsigned int val;
|
||||
|
||||
val = clk_readl(div->reg) >> div->shift;
|
||||
val &= clk_div_mask(div->width);
|
||||
if (!val)
|
||||
return 0;
|
||||
|
||||
return divider_recalc_rate(hw, parent_rate, val, div->table,
|
||||
div->flags, div->width);
|
||||
}
|
||||
|
||||
static unsigned long clk_divider_gate_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct clk_divider_gate *div_gate = to_clk_divider_gate(hw);
|
||||
struct clk_divider *div = to_clk_divider(hw);
|
||||
unsigned long flags = 0;
|
||||
unsigned int val;
|
||||
|
||||
spin_lock_irqsave(div->lock, flags);
|
||||
|
||||
if (!clk_hw_is_enabled(hw)) {
|
||||
val = div_gate->cached_val;
|
||||
} else {
|
||||
val = clk_readl(div->reg) >> div->shift;
|
||||
val &= clk_div_mask(div->width);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(div->lock, flags);
|
||||
|
||||
if (!val)
|
||||
return 0;
|
||||
|
||||
return divider_recalc_rate(hw, parent_rate, val, div->table,
|
||||
div->flags, div->width);
|
||||
}
|
||||
|
||||
static long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *prate)
|
||||
{
|
||||
return clk_divider_ops.round_rate(hw, rate, prate);
|
||||
}
|
||||
|
||||
static int clk_divider_gate_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct clk_divider_gate *div_gate = to_clk_divider_gate(hw);
|
||||
struct clk_divider *div = to_clk_divider(hw);
|
||||
unsigned long flags = 0;
|
||||
int value;
|
||||
u32 val;
|
||||
|
||||
value = divider_get_val(rate, parent_rate, div->table,
|
||||
div->width, div->flags);
|
||||
if (value < 0)
|
||||
return value;
|
||||
|
||||
spin_lock_irqsave(div->lock, flags);
|
||||
|
||||
if (clk_hw_is_enabled(hw)) {
|
||||
val = clk_readl(div->reg);
|
||||
val &= ~(clk_div_mask(div->width) << div->shift);
|
||||
val |= (u32)value << div->shift;
|
||||
clk_writel(val, div->reg);
|
||||
} else {
|
||||
div_gate->cached_val = value;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(div->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int clk_divider_enable(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_divider_gate *div_gate = to_clk_divider_gate(hw);
|
||||
struct clk_divider *div = to_clk_divider(hw);
|
||||
unsigned long flags = 0;
|
||||
u32 val;
|
||||
|
||||
if (!div_gate->cached_val) {
|
||||
pr_err("%s: no valid preset rate\n", clk_hw_get_name(hw));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(div->lock, flags);
|
||||
/* restore div val */
|
||||
val = clk_readl(div->reg);
|
||||
val |= div_gate->cached_val << div->shift;
|
||||
clk_writel(val, div->reg);
|
||||
|
||||
spin_unlock_irqrestore(div->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void clk_divider_disable(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_divider_gate *div_gate = to_clk_divider_gate(hw);
|
||||
struct clk_divider *div = to_clk_divider(hw);
|
||||
unsigned long flags = 0;
|
||||
u32 val;
|
||||
|
||||
spin_lock_irqsave(div->lock, flags);
|
||||
|
||||
/* store the current div val */
|
||||
val = clk_readl(div->reg) >> div->shift;
|
||||
val &= clk_div_mask(div->width);
|
||||
div_gate->cached_val = val;
|
||||
clk_writel(0, div->reg);
|
||||
|
||||
spin_unlock_irqrestore(div->lock, flags);
|
||||
}
|
||||
|
||||
static int clk_divider_is_enabled(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_divider *div = to_clk_divider(hw);
|
||||
u32 val;
|
||||
|
||||
val = clk_readl(div->reg) >> div->shift;
|
||||
val &= clk_div_mask(div->width);
|
||||
|
||||
return val ? 1 : 0;
|
||||
}
|
||||
|
||||
static const struct clk_ops clk_divider_gate_ro_ops = {
|
||||
.recalc_rate = clk_divider_gate_recalc_rate_ro,
|
||||
.round_rate = clk_divider_round_rate,
|
||||
};
|
||||
|
||||
static const struct clk_ops clk_divider_gate_ops = {
|
||||
.recalc_rate = clk_divider_gate_recalc_rate,
|
||||
.round_rate = clk_divider_round_rate,
|
||||
.set_rate = clk_divider_gate_set_rate,
|
||||
.enable = clk_divider_enable,
|
||||
.disable = clk_divider_disable,
|
||||
.is_enabled = clk_divider_is_enabled,
|
||||
};
|
||||
|
||||
/*
|
||||
* NOTE: In order to resue the most code from the common divider,
|
||||
* we also design our divider following the way that provids an extra
|
||||
* clk_divider_flags, however it's fixed to CLK_DIVIDER_ONE_BASED by
|
||||
* default as our HW is. Besides that it supports only CLK_DIVIDER_READ_ONLY
|
||||
* flag which can be specified by user flexibly.
|
||||
*/
|
||||
struct clk_hw *imx_clk_divider_gate(const char *name, const char *parent_name,
|
||||
unsigned long flags, void __iomem *reg,
|
||||
u8 shift, u8 width, u8 clk_divider_flags,
|
||||
const struct clk_div_table *table,
|
||||
spinlock_t *lock)
|
||||
{
|
||||
struct clk_init_data init;
|
||||
struct clk_divider_gate *div_gate;
|
||||
struct clk_hw *hw;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
div_gate = kzalloc(sizeof(*div_gate), GFP_KERNEL);
|
||||
if (!div_gate)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
init.name = name;
|
||||
if (clk_divider_flags & CLK_DIVIDER_READ_ONLY)
|
||||
init.ops = &clk_divider_gate_ro_ops;
|
||||
else
|
||||
init.ops = &clk_divider_gate_ops;
|
||||
init.flags = flags;
|
||||
init.parent_names = parent_name ? &parent_name : NULL;
|
||||
init.num_parents = parent_name ? 1 : 0;
|
||||
|
||||
div_gate->divider.reg = reg;
|
||||
div_gate->divider.shift = shift;
|
||||
div_gate->divider.width = width;
|
||||
div_gate->divider.lock = lock;
|
||||
div_gate->divider.table = table;
|
||||
div_gate->divider.hw.init = &init;
|
||||
div_gate->divider.flags = CLK_DIVIDER_ONE_BASED | clk_divider_flags;
|
||||
/* cache gate status */
|
||||
val = clk_readl(reg) >> shift;
|
||||
val &= clk_div_mask(width);
|
||||
div_gate->cached_val = val;
|
||||
|
||||
hw = &div_gate->divider.hw;
|
||||
ret = clk_hw_register(NULL, hw);
|
||||
if (ret) {
|
||||
kfree(div_gate);
|
||||
hw = ERR_PTR(ret);
|
||||
}
|
||||
|
||||
return hw;
|
||||
}
|
||||
|
|
@ -70,7 +70,7 @@ static const struct clk_ops clk_fixup_mux_ops = {
|
|||
};
|
||||
|
||||
struct clk *imx_clk_fixup_mux(const char *name, void __iomem *reg,
|
||||
u8 shift, u8 width, const char **parents,
|
||||
u8 shift, u8 width, const char * const *parents,
|
||||
int num_parents, void (*fixup)(u32 *val))
|
||||
{
|
||||
struct clk_fixup_mux *fixup_mux;
|
||||
|
|
|
|||
232
drivers/clk/imx/clk-frac-pll.c
Normal file
232
drivers/clk/imx/clk-frac-pll.c
Normal file
|
|
@ -0,0 +1,232 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright 2018 NXP.
|
||||
*
|
||||
* This driver supports the fractional plls found in the imx8m SOCs
|
||||
*
|
||||
* Documentation for this fractional pll can be found at:
|
||||
* https://www.nxp.com/docs/en/reference-manual/IMX8MDQLQRM.pdf#page=834
|
||||
*/
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/bitfield.h>
|
||||
|
||||
#include "clk.h"
|
||||
|
||||
#define PLL_CFG0 0x0
|
||||
#define PLL_CFG1 0x4
|
||||
|
||||
#define PLL_LOCK_STATUS BIT(31)
|
||||
#define PLL_PD_MASK BIT(19)
|
||||
#define PLL_BYPASS_MASK BIT(14)
|
||||
#define PLL_NEWDIV_VAL BIT(12)
|
||||
#define PLL_NEWDIV_ACK BIT(11)
|
||||
#define PLL_FRAC_DIV_MASK GENMASK(30, 7)
|
||||
#define PLL_INT_DIV_MASK GENMASK(6, 0)
|
||||
#define PLL_OUTPUT_DIV_MASK GENMASK(4, 0)
|
||||
#define PLL_FRAC_DENOM 0x1000000
|
||||
|
||||
#define PLL_FRAC_LOCK_TIMEOUT 10000
|
||||
#define PLL_FRAC_ACK_TIMEOUT 500000
|
||||
|
||||
struct clk_frac_pll {
|
||||
struct clk_hw hw;
|
||||
void __iomem *base;
|
||||
};
|
||||
|
||||
#define to_clk_frac_pll(_hw) container_of(_hw, struct clk_frac_pll, hw)
|
||||
|
||||
static int clk_wait_lock(struct clk_frac_pll *pll)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
return readl_poll_timeout(pll->base, val, val & PLL_LOCK_STATUS, 0,
|
||||
PLL_FRAC_LOCK_TIMEOUT);
|
||||
}
|
||||
|
||||
static int clk_wait_ack(struct clk_frac_pll *pll)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
/* return directly if the pll is in powerdown or in bypass */
|
||||
if (readl_relaxed(pll->base) & (PLL_PD_MASK | PLL_BYPASS_MASK))
|
||||
return 0;
|
||||
|
||||
/* Wait for the pll's divfi and divff to be reloaded */
|
||||
return readl_poll_timeout(pll->base, val, val & PLL_NEWDIV_ACK, 0,
|
||||
PLL_FRAC_ACK_TIMEOUT);
|
||||
}
|
||||
|
||||
static int clk_pll_prepare(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_frac_pll *pll = to_clk_frac_pll(hw);
|
||||
u32 val;
|
||||
|
||||
val = readl_relaxed(pll->base + PLL_CFG0);
|
||||
val &= ~PLL_PD_MASK;
|
||||
writel_relaxed(val, pll->base + PLL_CFG0);
|
||||
|
||||
return clk_wait_lock(pll);
|
||||
}
|
||||
|
||||
static void clk_pll_unprepare(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_frac_pll *pll = to_clk_frac_pll(hw);
|
||||
u32 val;
|
||||
|
||||
val = readl_relaxed(pll->base + PLL_CFG0);
|
||||
val |= PLL_PD_MASK;
|
||||
writel_relaxed(val, pll->base + PLL_CFG0);
|
||||
}
|
||||
|
||||
static int clk_pll_is_prepared(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_frac_pll *pll = to_clk_frac_pll(hw);
|
||||
u32 val;
|
||||
|
||||
val = readl_relaxed(pll->base + PLL_CFG0);
|
||||
return (val & PLL_PD_MASK) ? 0 : 1;
|
||||
}
|
||||
|
||||
static unsigned long clk_pll_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct clk_frac_pll *pll = to_clk_frac_pll(hw);
|
||||
u32 val, divff, divfi, divq;
|
||||
u64 temp64 = parent_rate;
|
||||
u64 rate;
|
||||
|
||||
val = readl_relaxed(pll->base + PLL_CFG0);
|
||||
divq = (FIELD_GET(PLL_OUTPUT_DIV_MASK, val) + 1) * 2;
|
||||
val = readl_relaxed(pll->base + PLL_CFG1);
|
||||
divff = FIELD_GET(PLL_FRAC_DIV_MASK, val);
|
||||
divfi = FIELD_GET(PLL_INT_DIV_MASK, val);
|
||||
|
||||
temp64 *= 8;
|
||||
temp64 *= divff;
|
||||
do_div(temp64, PLL_FRAC_DENOM);
|
||||
do_div(temp64, divq);
|
||||
|
||||
rate = parent_rate * 8 * (divfi + 1);
|
||||
do_div(rate, divq);
|
||||
rate += temp64;
|
||||
|
||||
return rate;
|
||||
}
|
||||
|
||||
static long clk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *prate)
|
||||
{
|
||||
u64 parent_rate = *prate;
|
||||
u32 divff, divfi;
|
||||
u64 temp64;
|
||||
|
||||
parent_rate *= 8;
|
||||
rate *= 2;
|
||||
temp64 = rate;
|
||||
do_div(temp64, parent_rate);
|
||||
divfi = temp64;
|
||||
temp64 = rate - divfi * parent_rate;
|
||||
temp64 *= PLL_FRAC_DENOM;
|
||||
do_div(temp64, parent_rate);
|
||||
divff = temp64;
|
||||
|
||||
temp64 = parent_rate;
|
||||
temp64 *= divff;
|
||||
do_div(temp64, PLL_FRAC_DENOM);
|
||||
|
||||
rate = parent_rate * divfi + temp64;
|
||||
|
||||
return rate / 2;
|
||||
}
|
||||
|
||||
/*
|
||||
* To simplify the clock calculation, we can keep the 'PLL_OUTPUT_VAL' at zero
|
||||
* (means the PLL output will be divided by 2). So the PLL output can use
|
||||
* the below formula:
|
||||
* pllout = parent_rate * 8 / 2 * DIVF_VAL;
|
||||
* where DIVF_VAL = 1 + DIVFI + DIVFF / 2^24.
|
||||
*/
|
||||
static int clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct clk_frac_pll *pll = to_clk_frac_pll(hw);
|
||||
u32 val, divfi, divff;
|
||||
u64 temp64 = parent_rate;
|
||||
int ret;
|
||||
|
||||
parent_rate *= 8;
|
||||
rate *= 2;
|
||||
divfi = rate / parent_rate;
|
||||
temp64 *= rate - divfi;
|
||||
temp64 *= PLL_FRAC_DENOM;
|
||||
do_div(temp64, parent_rate);
|
||||
divff = temp64;
|
||||
|
||||
val = readl_relaxed(pll->base + PLL_CFG1);
|
||||
val &= ~(PLL_FRAC_DIV_MASK | PLL_INT_DIV_MASK);
|
||||
val |= (divff << 7) | (divfi - 1);
|
||||
writel_relaxed(val, pll->base + PLL_CFG1);
|
||||
|
||||
val = readl_relaxed(pll->base + PLL_CFG0);
|
||||
val &= ~0x1f;
|
||||
writel_relaxed(val, pll->base + PLL_CFG0);
|
||||
|
||||
/* Set the NEV_DIV_VAL to reload the DIVFI and DIVFF */
|
||||
val = readl_relaxed(pll->base + PLL_CFG0);
|
||||
val |= PLL_NEWDIV_VAL;
|
||||
writel_relaxed(val, pll->base + PLL_CFG0);
|
||||
|
||||
ret = clk_wait_ack(pll);
|
||||
|
||||
/* clear the NEV_DIV_VAL */
|
||||
val = readl_relaxed(pll->base + PLL_CFG0);
|
||||
val &= ~PLL_NEWDIV_VAL;
|
||||
writel_relaxed(val, pll->base + PLL_CFG0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct clk_ops clk_frac_pll_ops = {
|
||||
.prepare = clk_pll_prepare,
|
||||
.unprepare = clk_pll_unprepare,
|
||||
.is_prepared = clk_pll_is_prepared,
|
||||
.recalc_rate = clk_pll_recalc_rate,
|
||||
.round_rate = clk_pll_round_rate,
|
||||
.set_rate = clk_pll_set_rate,
|
||||
};
|
||||
|
||||
struct clk *imx_clk_frac_pll(const char *name, const char *parent_name,
|
||||
void __iomem *base)
|
||||
{
|
||||
struct clk_init_data init;
|
||||
struct clk_frac_pll *pll;
|
||||
struct clk_hw *hw;
|
||||
int ret;
|
||||
|
||||
pll = kzalloc(sizeof(*pll), GFP_KERNEL);
|
||||
if (!pll)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
init.name = name;
|
||||
init.ops = &clk_frac_pll_ops;
|
||||
init.flags = 0;
|
||||
init.parent_names = &parent_name;
|
||||
init.num_parents = 1;
|
||||
|
||||
pll->base = base;
|
||||
pll->hw.init = &init;
|
||||
|
||||
hw = &pll->hw;
|
||||
|
||||
ret = clk_hw_register(NULL, hw);
|
||||
if (ret) {
|
||||
kfree(pll);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
return hw->clk;
|
||||
}
|
||||
|
|
@ -225,6 +225,41 @@ static void of_assigned_ldb_sels(struct device_node *node,
|
|||
}
|
||||
}
|
||||
|
||||
static bool pll6_bypassed(struct device_node *node)
|
||||
{
|
||||
int index, ret, num_clocks;
|
||||
struct of_phandle_args clkspec;
|
||||
|
||||
num_clocks = of_count_phandle_with_args(node, "assigned-clocks",
|
||||
"#clock-cells");
|
||||
if (num_clocks < 0)
|
||||
return false;
|
||||
|
||||
for (index = 0; index < num_clocks; index++) {
|
||||
ret = of_parse_phandle_with_args(node, "assigned-clocks",
|
||||
"#clock-cells", index,
|
||||
&clkspec);
|
||||
if (ret < 0)
|
||||
return false;
|
||||
|
||||
if (clkspec.np == node &&
|
||||
clkspec.args[0] == IMX6QDL_PLL6_BYPASS)
|
||||
break;
|
||||
}
|
||||
|
||||
/* PLL6 bypass is not part of the assigned clock list */
|
||||
if (index == num_clocks)
|
||||
return false;
|
||||
|
||||
ret = of_parse_phandle_with_args(node, "assigned-clock-parents",
|
||||
"#clock-cells", index, &clkspec);
|
||||
|
||||
if (clkspec.args[0] != IMX6QDL_CLK_PLL6)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#define CCM_CCDR 0x04
|
||||
#define CCM_CCSR 0x0c
|
||||
#define CCM_CS2CDR 0x2c
|
||||
|
|
@ -414,12 +449,24 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node)
|
|||
int ret;
|
||||
|
||||
clk[IMX6QDL_CLK_DUMMY] = imx_clk_fixed("dummy", 0);
|
||||
clk[IMX6QDL_CLK_CKIL] = imx_obtain_fixed_clock("ckil", 0);
|
||||
clk[IMX6QDL_CLK_CKIH] = imx_obtain_fixed_clock("ckih1", 0);
|
||||
clk[IMX6QDL_CLK_OSC] = imx_obtain_fixed_clock("osc", 0);
|
||||
clk[IMX6QDL_CLK_CKIL] = of_clk_get_by_name(ccm_node, "ckil");
|
||||
if (IS_ERR(clk[IMX6QDL_CLK_CKIL]))
|
||||
clk[IMX6QDL_CLK_CKIL] = imx_obtain_fixed_clock("ckil", 0);
|
||||
clk[IMX6QDL_CLK_CKIH] = of_clk_get_by_name(ccm_node, "ckih1");
|
||||
if (IS_ERR(clk[IMX6QDL_CLK_CKIH]))
|
||||
clk[IMX6QDL_CLK_CKIH] = imx_obtain_fixed_clock("ckih1", 0);
|
||||
clk[IMX6QDL_CLK_OSC] = of_clk_get_by_name(ccm_node, "osc");
|
||||
if (IS_ERR(clk[IMX6QDL_CLK_OSC]))
|
||||
clk[IMX6QDL_CLK_OSC] = imx_obtain_fixed_clock("osc", 0);
|
||||
|
||||
/* Clock source from external clock via CLK1/2 PADs */
|
||||
clk[IMX6QDL_CLK_ANACLK1] = imx_obtain_fixed_clock("anaclk1", 0);
|
||||
clk[IMX6QDL_CLK_ANACLK2] = imx_obtain_fixed_clock("anaclk2", 0);
|
||||
clk[IMX6QDL_CLK_ANACLK1] = of_clk_get_by_name(ccm_node, "anaclk1");
|
||||
if (IS_ERR(clk[IMX6QDL_CLK_ANACLK1]))
|
||||
clk[IMX6QDL_CLK_ANACLK1] = imx_obtain_fixed_clock("anaclk1", 0);
|
||||
|
||||
clk[IMX6QDL_CLK_ANACLK2] = of_clk_get_by_name(ccm_node, "anaclk2");
|
||||
if (IS_ERR(clk[IMX6QDL_CLK_ANACLK2]))
|
||||
clk[IMX6QDL_CLK_ANACLK2] = imx_obtain_fixed_clock("anaclk2", 0);
|
||||
|
||||
np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-anatop");
|
||||
anatop_base = base = of_iomap(np, 0);
|
||||
|
|
@ -491,16 +538,32 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node)
|
|||
clk[IMX6QDL_CLK_USBPHY1_GATE] = imx_clk_gate("usbphy1_gate", "dummy", base + 0x10, 6);
|
||||
clk[IMX6QDL_CLK_USBPHY2_GATE] = imx_clk_gate("usbphy2_gate", "dummy", base + 0x20, 6);
|
||||
|
||||
clk[IMX6QDL_CLK_SATA_REF] = imx_clk_fixed_factor("sata_ref", "pll6_enet", 1, 5);
|
||||
clk[IMX6QDL_CLK_PCIE_REF] = imx_clk_fixed_factor("pcie_ref", "pll6_enet", 1, 4);
|
||||
/*
|
||||
* The ENET PLL is special in that is has multiple outputs with
|
||||
* different post-dividers that are all affected by the single bypass
|
||||
* bit, so a single mux bit affects 3 independent branches of the clock
|
||||
* tree. There is no good way to model this in the clock framework and
|
||||
* dynamically changing the bypass bit, will yield unexpected results.
|
||||
* So we treat any configuration that bypasses the ENET PLL as
|
||||
* essentially static with the divider ratios reflecting the bypass
|
||||
* status.
|
||||
*
|
||||
*/
|
||||
if (!pll6_bypassed(ccm_node)) {
|
||||
clk[IMX6QDL_CLK_SATA_REF] = imx_clk_fixed_factor("sata_ref", "pll6_enet", 1, 5);
|
||||
clk[IMX6QDL_CLK_PCIE_REF] = imx_clk_fixed_factor("pcie_ref", "pll6_enet", 1, 4);
|
||||
clk[IMX6QDL_CLK_ENET_REF] = clk_register_divider_table(NULL, "enet_ref", "pll6_enet", 0,
|
||||
base + 0xe0, 0, 2, 0, clk_enet_ref_table,
|
||||
&imx_ccm_lock);
|
||||
} else {
|
||||
clk[IMX6QDL_CLK_SATA_REF] = imx_clk_fixed_factor("sata_ref", "pll6_enet", 1, 1);
|
||||
clk[IMX6QDL_CLK_PCIE_REF] = imx_clk_fixed_factor("pcie_ref", "pll6_enet", 1, 1);
|
||||
clk[IMX6QDL_CLK_ENET_REF] = imx_clk_fixed_factor("enet_ref", "pll6_enet", 1, 1);
|
||||
}
|
||||
|
||||
clk[IMX6QDL_CLK_SATA_REF_100M] = imx_clk_gate("sata_ref_100m", "sata_ref", base + 0xe0, 20);
|
||||
clk[IMX6QDL_CLK_PCIE_REF_125M] = imx_clk_gate("pcie_ref_125m", "pcie_ref", base + 0xe0, 19);
|
||||
|
||||
clk[IMX6QDL_CLK_ENET_REF] = clk_register_divider_table(NULL, "enet_ref", "pll6_enet", 0,
|
||||
base + 0xe0, 0, 2, 0, clk_enet_ref_table,
|
||||
&imx_ccm_lock);
|
||||
|
||||
clk[IMX6QDL_CLK_LVDS1_SEL] = imx_clk_mux("lvds1_sel", base + 0x160, 0, 5, lvds_sels, ARRAY_SIZE(lvds_sels));
|
||||
clk[IMX6QDL_CLK_LVDS2_SEL] = imx_clk_mux("lvds2_sel", base + 0x160, 5, 5, lvds_sels, ARRAY_SIZE(lvds_sels));
|
||||
|
||||
|
|
@ -508,8 +571,12 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node)
|
|||
* lvds1_gate and lvds2_gate are pseudo-gates. Both can be
|
||||
* independently configured as clock inputs or outputs. We treat
|
||||
* the "output_enable" bit as a gate, even though it's really just
|
||||
* enabling clock output.
|
||||
* enabling clock output. Initially the gate bits are cleared, as
|
||||
* otherwise the exclusive configuration gets locked in the setup done
|
||||
* by software running before the clock driver, with no way to change
|
||||
* it.
|
||||
*/
|
||||
writel(readl(base + 0x160) & ~0x3c00, base + 0x160);
|
||||
clk[IMX6QDL_CLK_LVDS1_GATE] = imx_clk_gate_exclusive("lvds1_gate", "lvds1_sel", base + 0x160, 10, BIT(12));
|
||||
clk[IMX6QDL_CLK_LVDS2_GATE] = imx_clk_gate_exclusive("lvds2_gate", "lvds2_sel", base + 0x160, 11, BIT(13));
|
||||
|
||||
|
|
@ -737,6 +804,8 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node)
|
|||
clk[IMX6QDL_CLK_CAN1_SERIAL] = imx_clk_gate2("can1_serial", "can_root", base + 0x68, 16);
|
||||
clk[IMX6QDL_CLK_CAN2_IPG] = imx_clk_gate2("can2_ipg", "ipg", base + 0x68, 18);
|
||||
clk[IMX6QDL_CLK_CAN2_SERIAL] = imx_clk_gate2("can2_serial", "can_root", base + 0x68, 20);
|
||||
clk[IMX6QDL_CLK_DCIC1] = imx_clk_gate2("dcic1", "ipu1_podf", base + 0x68, 24);
|
||||
clk[IMX6QDL_CLK_DCIC2] = imx_clk_gate2("dcic2", "ipu2_podf", base + 0x68, 26);
|
||||
clk[IMX6QDL_CLK_ECSPI1] = imx_clk_gate2("ecspi1", "ecspi_root", base + 0x6c, 0);
|
||||
clk[IMX6QDL_CLK_ECSPI2] = imx_clk_gate2("ecspi2", "ecspi_root", base + 0x6c, 2);
|
||||
clk[IMX6QDL_CLK_ECSPI3] = imx_clk_gate2("ecspi3", "ecspi_root", base + 0x6c, 4);
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@
|
|||
|
||||
#include "clk.h"
|
||||
|
||||
#define CCDR 0x4
|
||||
#define BM_CCM_CCDR_MMDC_CH0_MASK (1 << 17)
|
||||
#define CCSR 0xc
|
||||
#define BM_CCSR_PLL1_SW_CLK_SEL (1 << 2)
|
||||
#define CACRR 0x10
|
||||
|
|
@ -411,6 +413,10 @@ static void __init imx6sl_clocks_init(struct device_node *ccm_node)
|
|||
clks[IMX6SL_CLK_USDHC3] = imx_clk_gate2("usdhc3", "usdhc3_podf", base + 0x80, 6);
|
||||
clks[IMX6SL_CLK_USDHC4] = imx_clk_gate2("usdhc4", "usdhc4_podf", base + 0x80, 8);
|
||||
|
||||
/* Ensure the MMDC CH0 handshake is bypassed */
|
||||
writel_relaxed(readl_relaxed(base + CCDR) |
|
||||
BM_CCM_CCDR_MMDC_CH0_MASK, base + CCDR);
|
||||
|
||||
imx_check_clocks(clks, ARRAY_SIZE(clks));
|
||||
|
||||
clk_data.clks = clks;
|
||||
|
|
|
|||
|
|
@ -886,9 +886,6 @@ static void __init imx7d_clocks_init(struct device_node *ccm_node)
|
|||
/* use old gpt clk setting, gpt1 root clk must be twice as gpt counter freq */
|
||||
clk_set_parent(clks[IMX7D_GPT1_ROOT_SRC], clks[IMX7D_OSC_24M_CLK]);
|
||||
|
||||
/* set uart module clock's parent clock source that must be great then 80MHz */
|
||||
clk_set_parent(clks[IMX7D_UART1_ROOT_SRC], clks[IMX7D_OSC_24M_CLK]);
|
||||
|
||||
/* Set clock rate for USBPHY, the USB_PLL at CCM is from USBOTG2 */
|
||||
clks[IMX7D_USB1_MAIN_480M_CLK] = imx_clk_fixed_factor("pll_usb1_main_clk", "osc", 20, 1);
|
||||
clks[IMX7D_USB_MAIN_480M_CLK] = imx_clk_fixed_factor("pll_usb_main_clk", "osc", 20, 1);
|
||||
|
|
|
|||
220
drivers/clk/imx/clk-imx7ulp.c
Normal file
220
drivers/clk/imx/clk-imx7ulp.c
Normal file
|
|
@ -0,0 +1,220 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2016 Freescale Semiconductor, Inc.
|
||||
* Copyright 2017~2018 NXP
|
||||
*
|
||||
* Author: Dong Aisheng <aisheng.dong@nxp.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <dt-bindings/clock/imx7ulp-clock.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "clk.h"
|
||||
|
||||
static const char * const pll_pre_sels[] = { "sosc", "firc", };
|
||||
static const char * const spll_pfd_sels[] = { "spll_pfd0", "spll_pfd1", "spll_pfd2", "spll_pfd3", };
|
||||
static const char * const spll_sels[] = { "spll", "spll_pfd_sel", };
|
||||
static const char * const apll_pfd_sels[] = { "apll_pfd0", "apll_pfd1", "apll_pfd2", "apll_pfd3", };
|
||||
static const char * const apll_sels[] = { "apll", "apll_pfd_sel", };
|
||||
static const char * const scs_sels[] = { "dummy", "sosc", "sirc", "firc", "dummy", "apll_sel", "spll_sel", "upll", };
|
||||
static const char * const ddr_sels[] = { "apll_pfd_sel", "upll", };
|
||||
static const char * const nic_sels[] = { "firc", "ddr_clk", };
|
||||
static const char * const periph_plat_sels[] = { "dummy", "nic1_bus_clk", "nic1_clk", "ddr_clk", "apll_pfd2", "apll_pfd1", "apll_pfd0", "upll", };
|
||||
static const char * const periph_bus_sels[] = { "dummy", "sosc_bus_clk", "mpll", "firc_bus_clk", "rosc", "nic1_bus_clk", "nic1_clk", "spll_bus_clk", };
|
||||
|
||||
/* used by sosc/sirc/firc/ddr/spll/apll dividers */
|
||||
static const struct clk_div_table ulp_div_table[] = {
|
||||
{ .val = 1, .div = 1, },
|
||||
{ .val = 2, .div = 2, },
|
||||
{ .val = 3, .div = 4, },
|
||||
{ .val = 4, .div = 8, },
|
||||
{ .val = 5, .div = 16, },
|
||||
{ .val = 6, .div = 32, },
|
||||
{ .val = 7, .div = 64, },
|
||||
};
|
||||
|
||||
static void __init imx7ulp_clk_scg1_init(struct device_node *np)
|
||||
{
|
||||
struct clk_hw_onecell_data *clk_data;
|
||||
struct clk_hw **clks;
|
||||
void __iomem *base;
|
||||
|
||||
clk_data = kzalloc(sizeof(*clk_data) + sizeof(*clk_data->hws) *
|
||||
IMX7ULP_CLK_SCG1_END, GFP_KERNEL);
|
||||
if (!clk_data)
|
||||
return;
|
||||
|
||||
clk_data->num = IMX7ULP_CLK_SCG1_END;
|
||||
clks = clk_data->hws;
|
||||
|
||||
clks[IMX7ULP_CLK_DUMMY] = imx_clk_hw_fixed("dummy", 0);
|
||||
|
||||
clks[IMX7ULP_CLK_ROSC] = imx_obtain_fixed_clk_hw(np, "rosc");
|
||||
clks[IMX7ULP_CLK_SOSC] = imx_obtain_fixed_clk_hw(np, "sosc");
|
||||
clks[IMX7ULP_CLK_SIRC] = imx_obtain_fixed_clk_hw(np, "sirc");
|
||||
clks[IMX7ULP_CLK_FIRC] = imx_obtain_fixed_clk_hw(np, "firc");
|
||||
clks[IMX7ULP_CLK_MIPI_PLL] = imx_obtain_fixed_clk_hw(np, "mpll");
|
||||
clks[IMX7ULP_CLK_UPLL] = imx_obtain_fixed_clk_hw(np, "upll");
|
||||
|
||||
/* SCG1 */
|
||||
base = of_iomap(np, 0);
|
||||
WARN_ON(!base);
|
||||
|
||||
/* NOTE: xPLL config can't be changed when xPLL is enabled */
|
||||
clks[IMX7ULP_CLK_APLL_PRE_SEL] = imx_clk_hw_mux_flags("apll_pre_sel", base + 0x508, 0, 1, pll_pre_sels, ARRAY_SIZE(pll_pre_sels), CLK_SET_PARENT_GATE);
|
||||
clks[IMX7ULP_CLK_SPLL_PRE_SEL] = imx_clk_hw_mux_flags("spll_pre_sel", base + 0x608, 0, 1, pll_pre_sels, ARRAY_SIZE(pll_pre_sels), CLK_SET_PARENT_GATE);
|
||||
|
||||
/* name parent_name reg shift width flags */
|
||||
clks[IMX7ULP_CLK_APLL_PRE_DIV] = imx_clk_hw_divider_flags("apll_pre_div", "apll_pre_sel", base + 0x508, 8, 3, CLK_SET_RATE_GATE);
|
||||
clks[IMX7ULP_CLK_SPLL_PRE_DIV] = imx_clk_hw_divider_flags("spll_pre_div", "spll_pre_sel", base + 0x608, 8, 3, CLK_SET_RATE_GATE);
|
||||
|
||||
/* name parent_name base */
|
||||
clks[IMX7ULP_CLK_APLL] = imx_clk_pllv4("apll", "apll_pre_div", base + 0x500);
|
||||
clks[IMX7ULP_CLK_SPLL] = imx_clk_pllv4("spll", "spll_pre_div", base + 0x600);
|
||||
|
||||
/* APLL PFDs */
|
||||
clks[IMX7ULP_CLK_APLL_PFD0] = imx_clk_pfdv2("apll_pfd0", "apll", base + 0x50c, 0);
|
||||
clks[IMX7ULP_CLK_APLL_PFD1] = imx_clk_pfdv2("apll_pfd1", "apll", base + 0x50c, 1);
|
||||
clks[IMX7ULP_CLK_APLL_PFD2] = imx_clk_pfdv2("apll_pfd2", "apll", base + 0x50c, 2);
|
||||
clks[IMX7ULP_CLK_APLL_PFD3] = imx_clk_pfdv2("apll_pfd3", "apll", base + 0x50c, 3);
|
||||
|
||||
/* SPLL PFDs */
|
||||
clks[IMX7ULP_CLK_SPLL_PFD0] = imx_clk_pfdv2("spll_pfd0", "spll", base + 0x60C, 0);
|
||||
clks[IMX7ULP_CLK_SPLL_PFD1] = imx_clk_pfdv2("spll_pfd1", "spll", base + 0x60C, 1);
|
||||
clks[IMX7ULP_CLK_SPLL_PFD2] = imx_clk_pfdv2("spll_pfd2", "spll", base + 0x60C, 2);
|
||||
clks[IMX7ULP_CLK_SPLL_PFD3] = imx_clk_pfdv2("spll_pfd3", "spll", base + 0x60C, 3);
|
||||
|
||||
/* PLL Mux */
|
||||
clks[IMX7ULP_CLK_APLL_PFD_SEL] = imx_clk_hw_mux_flags("apll_pfd_sel", base + 0x508, 14, 2, apll_pfd_sels, ARRAY_SIZE(apll_pfd_sels), CLK_SET_RATE_PARENT | CLK_SET_PARENT_GATE);
|
||||
clks[IMX7ULP_CLK_SPLL_PFD_SEL] = imx_clk_hw_mux_flags("spll_pfd_sel", base + 0x608, 14, 2, spll_pfd_sels, ARRAY_SIZE(spll_pfd_sels), CLK_SET_RATE_PARENT | CLK_SET_PARENT_GATE);
|
||||
clks[IMX7ULP_CLK_APLL_SEL] = imx_clk_hw_mux_flags("apll_sel", base + 0x508, 1, 1, apll_sels, ARRAY_SIZE(apll_sels), CLK_SET_RATE_PARENT | CLK_SET_PARENT_GATE);
|
||||
clks[IMX7ULP_CLK_SPLL_SEL] = imx_clk_hw_mux_flags("spll_sel", base + 0x608, 1, 1, spll_sels, ARRAY_SIZE(spll_sels), CLK_SET_RATE_PARENT | CLK_SET_PARENT_GATE);
|
||||
|
||||
clks[IMX7ULP_CLK_SPLL_BUS_CLK] = imx_clk_divider_gate("spll_bus_clk", "spll_sel", CLK_SET_RATE_GATE, base + 0x604, 8, 3, 0, ulp_div_table, &imx_ccm_lock);
|
||||
|
||||
/* scs/ddr/nic select different clock source requires that clock to be enabled first */
|
||||
clks[IMX7ULP_CLK_SYS_SEL] = imx_clk_hw_mux2("scs_sel", base + 0x14, 24, 4, scs_sels, ARRAY_SIZE(scs_sels));
|
||||
clks[IMX7ULP_CLK_NIC_SEL] = imx_clk_hw_mux2("nic_sel", base + 0x40, 28, 1, nic_sels, ARRAY_SIZE(nic_sels));
|
||||
clks[IMX7ULP_CLK_DDR_SEL] = imx_clk_hw_mux_flags("ddr_sel", base + 0x30, 24, 1, ddr_sels, ARRAY_SIZE(ddr_sels), CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE);
|
||||
|
||||
clks[IMX7ULP_CLK_CORE_DIV] = imx_clk_hw_divider_flags("divcore", "scs_sel", base + 0x14, 16, 4, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL);
|
||||
|
||||
clks[IMX7ULP_CLK_DDR_DIV] = imx_clk_divider_gate("ddr_clk", "ddr_sel", CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, base + 0x30, 0, 3,
|
||||
0, ulp_div_table, &imx_ccm_lock);
|
||||
|
||||
clks[IMX7ULP_CLK_NIC0_DIV] = imx_clk_hw_divider_flags("nic0_clk", "nic_sel", base + 0x40, 24, 4, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL);
|
||||
clks[IMX7ULP_CLK_NIC1_DIV] = imx_clk_hw_divider_flags("nic1_clk", "nic0_clk", base + 0x40, 16, 4, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL);
|
||||
clks[IMX7ULP_CLK_NIC1_BUS_DIV] = imx_clk_hw_divider_flags("nic1_bus_clk", "nic1_clk", base + 0x40, 4, 4, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL);
|
||||
|
||||
clks[IMX7ULP_CLK_GPU_DIV] = imx_clk_hw_divider("gpu_clk", "nic0_clk", base + 0x40, 20, 4);
|
||||
|
||||
clks[IMX7ULP_CLK_SOSC_BUS_CLK] = imx_clk_divider_gate("sosc_bus_clk", "sosc", 0, base + 0x104, 8, 3,
|
||||
CLK_DIVIDER_READ_ONLY, ulp_div_table, &imx_ccm_lock);
|
||||
clks[IMX7ULP_CLK_FIRC_BUS_CLK] = imx_clk_divider_gate("firc_bus_clk", "firc", 0, base + 0x304, 8, 3,
|
||||
CLK_DIVIDER_READ_ONLY, ulp_div_table, &imx_ccm_lock);
|
||||
|
||||
imx_check_clk_hws(clks, clk_data->num);
|
||||
|
||||
of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_data);
|
||||
}
|
||||
CLK_OF_DECLARE(imx7ulp_clk_scg1, "fsl,imx7ulp-scg1", imx7ulp_clk_scg1_init);
|
||||
|
||||
static void __init imx7ulp_clk_pcc2_init(struct device_node *np)
|
||||
{
|
||||
struct clk_hw_onecell_data *clk_data;
|
||||
struct clk_hw **clks;
|
||||
void __iomem *base;
|
||||
|
||||
clk_data = kzalloc(sizeof(*clk_data) + sizeof(*clk_data->hws) *
|
||||
IMX7ULP_CLK_PCC2_END, GFP_KERNEL);
|
||||
if (!clk_data)
|
||||
return;
|
||||
|
||||
clk_data->num = IMX7ULP_CLK_PCC2_END;
|
||||
clks = clk_data->hws;
|
||||
|
||||
/* PCC2 */
|
||||
base = of_iomap(np, 0);
|
||||
WARN_ON(!base);
|
||||
|
||||
clks[IMX7ULP_CLK_DMA1] = imx_clk_hw_gate("dma1", "nic1_clk", base + 0x20, 30);
|
||||
clks[IMX7ULP_CLK_RGPIO2P1] = imx_clk_hw_gate("rgpio2p1", "nic1_bus_clk", base + 0x3c, 30);
|
||||
clks[IMX7ULP_CLK_DMA_MUX1] = imx_clk_hw_gate("dma_mux1", "nic1_bus_clk", base + 0x84, 30);
|
||||
clks[IMX7ULP_CLK_SNVS] = imx_clk_hw_gate("snvs", "nic1_bus_clk", base + 0x8c, 30);
|
||||
clks[IMX7ULP_CLK_CAAM] = imx_clk_hw_gate("caam", "nic1_clk", base + 0x90, 30);
|
||||
clks[IMX7ULP_CLK_LPTPM4] = imx7ulp_clk_composite("lptpm4", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0x94);
|
||||
clks[IMX7ULP_CLK_LPTPM5] = imx7ulp_clk_composite("lptpm5", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0x98);
|
||||
clks[IMX7ULP_CLK_LPIT1] = imx7ulp_clk_composite("lpit1", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0x9c);
|
||||
clks[IMX7ULP_CLK_LPSPI2] = imx7ulp_clk_composite("lpspi2", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0xa4);
|
||||
clks[IMX7ULP_CLK_LPSPI3] = imx7ulp_clk_composite("lpspi3", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0xa8);
|
||||
clks[IMX7ULP_CLK_LPI2C4] = imx7ulp_clk_composite("lpi2c4", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0xac);
|
||||
clks[IMX7ULP_CLK_LPI2C5] = imx7ulp_clk_composite("lpi2c5", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0xb0);
|
||||
clks[IMX7ULP_CLK_LPUART4] = imx7ulp_clk_composite("lpuart4", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0xb4);
|
||||
clks[IMX7ULP_CLK_LPUART5] = imx7ulp_clk_composite("lpuart5", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0xb8);
|
||||
clks[IMX7ULP_CLK_FLEXIO1] = imx7ulp_clk_composite("flexio1", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0xc4);
|
||||
clks[IMX7ULP_CLK_USB0] = imx7ulp_clk_composite("usb0", periph_plat_sels, ARRAY_SIZE(periph_plat_sels), true, true, true, base + 0xcc);
|
||||
clks[IMX7ULP_CLK_USB1] = imx7ulp_clk_composite("usb1", periph_plat_sels, ARRAY_SIZE(periph_plat_sels), true, true, true, base + 0xd0);
|
||||
clks[IMX7ULP_CLK_USB_PHY] = imx_clk_hw_gate("usb_phy", "nic1_bus_clk", base + 0xd4, 30);
|
||||
clks[IMX7ULP_CLK_USDHC0] = imx7ulp_clk_composite("usdhc0", periph_plat_sels, ARRAY_SIZE(periph_plat_sels), true, true, true, base + 0xdc);
|
||||
clks[IMX7ULP_CLK_USDHC1] = imx7ulp_clk_composite("usdhc1", periph_plat_sels, ARRAY_SIZE(periph_plat_sels), true, true, true, base + 0xe0);
|
||||
clks[IMX7ULP_CLK_WDG1] = imx7ulp_clk_composite("wdg1", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, true, true, base + 0xf4);
|
||||
clks[IMX7ULP_CLK_WDG2] = imx7ulp_clk_composite("sdg2", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, true, true, base + 0x10c);
|
||||
|
||||
imx_check_clk_hws(clks, clk_data->num);
|
||||
|
||||
of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_data);
|
||||
}
|
||||
CLK_OF_DECLARE(imx7ulp_clk_pcc2, "fsl,imx7ulp-pcc2", imx7ulp_clk_pcc2_init);
|
||||
|
||||
static void __init imx7ulp_clk_pcc3_init(struct device_node *np)
|
||||
{
|
||||
struct clk_hw_onecell_data *clk_data;
|
||||
struct clk_hw **clks;
|
||||
void __iomem *base;
|
||||
|
||||
clk_data = kzalloc(sizeof(*clk_data) + sizeof(*clk_data->hws) *
|
||||
IMX7ULP_CLK_PCC3_END, GFP_KERNEL);
|
||||
if (!clk_data)
|
||||
return;
|
||||
|
||||
clk_data->num = IMX7ULP_CLK_PCC3_END;
|
||||
clks = clk_data->hws;
|
||||
|
||||
/* PCC3 */
|
||||
base = of_iomap(np, 0);
|
||||
WARN_ON(!base);
|
||||
|
||||
clks[IMX7ULP_CLK_LPTPM6] = imx7ulp_clk_composite("lptpm6", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0x84);
|
||||
clks[IMX7ULP_CLK_LPTPM7] = imx7ulp_clk_composite("lptpm7", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0x88);
|
||||
|
||||
clks[IMX7ULP_CLK_MMDC] = clk_hw_register_gate(NULL, "mmdc", "nic1_clk", CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
|
||||
base + 0xac, 30, 0, &imx_ccm_lock);
|
||||
clks[IMX7ULP_CLK_LPI2C6] = imx7ulp_clk_composite("lpi2c6", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0x90);
|
||||
clks[IMX7ULP_CLK_LPI2C7] = imx7ulp_clk_composite("lpi2c7", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0x94);
|
||||
clks[IMX7ULP_CLK_LPUART6] = imx7ulp_clk_composite("lpuart6", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0x98);
|
||||
clks[IMX7ULP_CLK_LPUART7] = imx7ulp_clk_composite("lpuart7", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0x9c);
|
||||
clks[IMX7ULP_CLK_DSI] = imx7ulp_clk_composite("dsi", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, true, true, base + 0xa4);
|
||||
clks[IMX7ULP_CLK_LCDIF] = imx7ulp_clk_composite("lcdif", periph_plat_sels, ARRAY_SIZE(periph_plat_sels), true, true, true, base + 0xa8);
|
||||
|
||||
clks[IMX7ULP_CLK_VIU] = imx_clk_hw_gate("viu", "nic1_clk", base + 0xa0, 30);
|
||||
clks[IMX7ULP_CLK_PCTLC] = imx_clk_hw_gate("pctlc", "nic1_bus_clk", base + 0xb8, 30);
|
||||
clks[IMX7ULP_CLK_PCTLD] = imx_clk_hw_gate("pctld", "nic1_bus_clk", base + 0xbc, 30);
|
||||
clks[IMX7ULP_CLK_PCTLE] = imx_clk_hw_gate("pctle", "nic1_bus_clk", base + 0xc0, 30);
|
||||
clks[IMX7ULP_CLK_PCTLF] = imx_clk_hw_gate("pctlf", "nic1_bus_clk", base + 0xc4, 30);
|
||||
|
||||
clks[IMX7ULP_CLK_GPU3D] = imx7ulp_clk_composite("gpu3d", periph_plat_sels, ARRAY_SIZE(periph_plat_sels), true, false, true, base + 0x140);
|
||||
clks[IMX7ULP_CLK_GPU2D] = imx7ulp_clk_composite("gpu2d", periph_plat_sels, ARRAY_SIZE(periph_plat_sels), true, false, true, base + 0x144);
|
||||
|
||||
imx_check_clk_hws(clks, clk_data->num);
|
||||
|
||||
of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_data);
|
||||
}
|
||||
CLK_OF_DECLARE(imx7ulp_clk_pcc3, "fsl,imx7ulp-pcc3", imx7ulp_clk_pcc3_init);
|
||||
589
drivers/clk/imx/clk-imx8mq.c
Normal file
589
drivers/clk/imx/clk-imx8mq.c
Normal file
|
|
@ -0,0 +1,589 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright 2018 NXP.
|
||||
* Copyright (C) 2017 Pengutronix, Lucas Stach <kernel@pengutronix.de>
|
||||
*/
|
||||
|
||||
#include <dt-bindings/clock/imx8mq-clock.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include "clk.h"
|
||||
|
||||
static u32 share_count_sai1;
|
||||
static u32 share_count_sai2;
|
||||
static u32 share_count_sai3;
|
||||
static u32 share_count_sai4;
|
||||
static u32 share_count_sai5;
|
||||
static u32 share_count_sai6;
|
||||
static u32 share_count_dcss;
|
||||
static u32 share_count_nand;
|
||||
|
||||
static struct clk *clks[IMX8MQ_CLK_END];
|
||||
|
||||
static const char *pll_ref_sels[] = { "osc_25m", "osc_27m", "dummy", "dummy", };
|
||||
static const char *arm_pll_bypass_sels[] = {"arm_pll", "arm_pll_ref_sel", };
|
||||
static const char *gpu_pll_bypass_sels[] = {"gpu_pll", "gpu_pll_ref_sel", };
|
||||
static const char *vpu_pll_bypass_sels[] = {"vpu_pll", "vpu_pll_ref_sel", };
|
||||
static const char *audio_pll1_bypass_sels[] = {"audio_pll1", "audio_pll1_ref_sel", };
|
||||
static const char *audio_pll2_bypass_sels[] = {"audio_pll2", "audio_pll2_ref_sel", };
|
||||
static const char *video_pll1_bypass_sels[] = {"video_pll1", "video_pll1_ref_sel", };
|
||||
|
||||
static const char *sys1_pll1_out_sels[] = {"sys1_pll1", "sys1_pll1_ref_sel", };
|
||||
static const char *sys2_pll1_out_sels[] = {"sys2_pll1", "sys1_pll1_ref_sel", };
|
||||
static const char *sys3_pll1_out_sels[] = {"sys3_pll1", "sys3_pll1_ref_sel", };
|
||||
static const char *dram_pll1_out_sels[] = {"dram_pll1", "dram_pll1_ref_sel", };
|
||||
|
||||
static const char *sys1_pll2_out_sels[] = {"sys1_pll2_div", "sys1_pll1_ref_sel", };
|
||||
static const char *sys2_pll2_out_sels[] = {"sys2_pll2_div", "sys2_pll1_ref_sel", };
|
||||
static const char *sys3_pll2_out_sels[] = {"sys3_pll2_div", "sys2_pll1_ref_sel", };
|
||||
static const char *dram_pll2_out_sels[] = {"dram_pll2_div", "dram_pll1_ref_sel", };
|
||||
|
||||
/* CCM ROOT */
|
||||
static const char *imx8mq_a53_sels[] = {"osc_25m", "arm_pll_out", "sys2_pll_500m", "sys2_pll_1000m",
|
||||
"sys1_pll_800m", "sys1_pll_400m", "audio_pll1_out", "sys3_pll2_out", };
|
||||
|
||||
static const char *imx8mq_vpu_sels[] = {"osc_25m", "arm_pll_out", "sys2_pll_500m", "sys2_pll_1000m",
|
||||
"sys1_pll_800m", "sys1_pll_400m", "audio_pll1_out", "vpu_pll_out", };
|
||||
|
||||
static const char *imx8mq_gpu_core_sels[] = {"osc_25m", "gpu_pll_out", "sys1_pll_800m", "sys3_pll2_out",
|
||||
"sys2_pll_1000m", "audio_pll1_out", "video_pll1_out", "audio_pll2_out", };
|
||||
|
||||
static const char *imx8mq_gpu_shader_sels[] = {"osc_25m", "gpu_pll_out", "sys1_pll_800m", "sys3_pll2_out",
|
||||
"sys2_pll_1000m", "audio_pll1_out", "video_pll1_out", "audio_pll2_out", };
|
||||
|
||||
static const char *imx8mq_main_axi_sels[] = {"osc_25m", "sys2_pll_333m", "sys1_pll_800m", "sys2_pll_250m",
|
||||
"sys2_pll_1000m", "audio_pll1_out", "video_pll1_out", "sys1_pll_100m",};
|
||||
|
||||
static const char *imx8mq_enet_axi_sels[] = {"osc_25m", "sys1_pll_266m", "sys1_pll_800m", "sys2_pll_250m",
|
||||
"sys2_pll_200m", "audio_pll1_out", "video_pll1_out", "sys3_pll2_out", };
|
||||
|
||||
static const char *imx8mq_nand_usdhc_sels[] = {"osc_25m", "sys1_pll_266m", "sys1_pll_800m", "sys2_pll_200m",
|
||||
"sys1_pll_133m", "sys3_pll2_out", "sys2_pll_250m", "audio_pll1_out", };
|
||||
|
||||
static const char *imx8mq_vpu_bus_sels[] = {"osc_25m", "sys1_pll_800m", "vpu_pll_out", "audio_pll2_out", "sys3_pll2_out", "sys2_pll_1000m", "sys2_pll_200m", "sys1_pll_100m", };
|
||||
|
||||
static const char *imx8mq_disp_axi_sels[] = {"osc_25m", "sys2_pll_125m", "sys1_pll_800m", "sys3_pll2_out", "sys1_pll_400m", "audio_pll2_out", "clk_ext1", "clk_ext4", };
|
||||
|
||||
static const char *imx8mq_disp_apb_sels[] = {"osc_25m", "sys2_pll_125m", "sys1_pll_800m", "sys3_pll2_out",
|
||||
"sys1_pll_40m", "audio_pll2_out", "clk_ext1", "clk_ext3", };
|
||||
|
||||
static const char *imx8mq_disp_rtrm_sels[] = {"osc_25m", "sys1_pll_800m", "sys2_pll_200m", "sys1_pll_400m",
|
||||
"audio_pll1_out", "video_pll1_out", "clk_ext2", "clk_ext3", };
|
||||
|
||||
static const char *imx8mq_usb_bus_sels[] = {"osc_25m", "sys2_pll_500m", "sys1_pll_800m", "sys2_pll_100m",
|
||||
"sys2_pll_200m", "clk_ext2", "clk_ext4", "audio_pll2_out", };
|
||||
|
||||
static const char *imx8mq_gpu_axi_sels[] = {"osc_25m", "sys1_pll_800m", "gpu_pll_out", "sys3_pll2_out", "sys2_pll_1000m",
|
||||
"audio_pll1_out", "video_pll1_out", "audio_pll2_out", };
|
||||
|
||||
static const char *imx8mq_gpu_ahb_sels[] = {"osc_25m", "sys1_pll_800m", "gpu_pll_out", "sys3_pll2_out", "sys2_pll_1000m",
|
||||
"audio_pll1_out", "video_pll1_out", "audio_pll2_out", };
|
||||
|
||||
static const char *imx8mq_noc_sels[] = {"osc_25m", "sys1_pll_800m", "sys3_pll2_out", "sys2_pll_1000m", "sys2_pll_500m",
|
||||
"audio_pll1_out", "video_pll1_out", "audio_pll2_out", };
|
||||
|
||||
static const char *imx8mq_noc_apb_sels[] = {"osc_25m", "sys1_pll_400m", "sys3_pll2_out", "sys2_pll_333m", "sys2_pll_200m",
|
||||
"sys1_pll_800m", "audio_pll1_out", "video_pll1_out", };
|
||||
|
||||
static const char *imx8mq_ahb_sels[] = {"osc_25m", "sys1_pll_133m", "sys1_pll_800m", "sys1_pll_400m",
|
||||
"sys2_pll_125m", "sys3_pll2_out", "audio_pll1_out", "video_pll1_out", };
|
||||
|
||||
static const char *imx8mq_audio_ahb_sels[] = {"osc_25m", "sys2_pll_500m", "sys1_pll_800m", "sys2_pll_1000m",
|
||||
"sys2_pll_166m", "sys3_pll2_out", "audio_pll1_out", "video_pll1_out", };
|
||||
|
||||
static const char *imx8mq_dsi_ahb_sels[] = {"osc_25m", "sys2_pll_100m", "sys1_pll_80m", "sys1_pll_800m",
|
||||
"sys2_pll_1000m", "sys3_pll2_out", "clk_ext3", "audio_pll2_out"};
|
||||
|
||||
static const char *imx8mq_dram_alt_sels[] = {"osc_25m", "sys1_pll_800m", "sys1_pll_100m", "sys2_pll_500m",
|
||||
"sys2_pll_250m", "sys1_pll_400m", "audio_pll1_out", "sys1_pll_266m", };
|
||||
|
||||
static const char *imx8mq_dram_apb_sels[] = {"osc_25m", "sys2_pll_200m", "sys1_pll_40m", "sys1_pll_160m",
|
||||
"sys1_pll_800m", "sys3_pll2_out", "sys2_pll_250m", "audio_pll2_out", };
|
||||
|
||||
static const char *imx8mq_vpu_g1_sels[] = {"osc_25m", "vpu_pll_out", "sys1_pll_800m", "sys2_pll_1000m", "sys1_pll_100m", "sys2_pll_125m", "sys3_pll2_out", "audio_pll1_out", };
|
||||
|
||||
static const char *imx8mq_vpu_g2_sels[] = {"osc_25m", "vpu_pll_out", "sys1_pll_800m", "sys2_pll_1000m", "sys1_pll_100m", "sys2_pll_125m", "sys3_pll2_out", "audio_pll1_out", };
|
||||
|
||||
static const char *imx8mq_disp_dtrc_sels[] = {"osc_25m", "vpu_pll_out", "sys1_pll_800m", "sys2_pll_1000m", "sys1_pll_160m", "sys2_pll_100m", "sys3_pll2_out", "audio_pll2_out", };
|
||||
|
||||
static const char *imx8mq_disp_dc8000_sels[] = {"osc_25m", "vpu_pll_out", "sys1_pll_800m", "sys2_pll_1000m", "sys1_pll_160m", "sys2_pll_100m", "sys3_pll2_out", "audio_pll2_out", };
|
||||
|
||||
static const char *imx8mq_pcie1_ctrl_sels[] = {"osc_25m", "sys2_pll_250m", "sys2_pll_200m", "sys1_pll_266m",
|
||||
"sys1_pll_800m", "sys2_pll_500m", "sys2_pll_250m", "sys3_pll2_out", };
|
||||
|
||||
static const char *imx8mq_pcie1_phy_sels[] = {"osc_25m", "sys2_pll_100m", "sys2_pll_500m", "clk_ext1", "clk_ext2",
|
||||
"clk_ext3", "clk_ext4", };
|
||||
|
||||
static const char *imx8mq_pcie1_aux_sels[] = {"osc_25m", "sys2_pll_200m", "sys2_pll_500m", "sys3_pll2_out",
|
||||
"sys2_pll_100m", "sys1_pll_80m", "sys1_pll_160m", "sys1_pll_200m", };
|
||||
|
||||
static const char *imx8mq_dc_pixel_sels[] = {"osc_25m", "video_pll1_out", "audio_pll2_out", "audio_pll1_out", "sys1_pll_800m", "sys2_pll_1000m", "sys3_pll2_out", "clk_ext4", };
|
||||
|
||||
static const char *imx8mq_lcdif_pixel_sels[] = {"osc_25m", "video_pll1_out", "audio_pll2_out", "audio_pll1_out", "sys1_pll_800m", "sys2_pll_1000m", "sys3_pll2_out", "clk_ext4", };
|
||||
|
||||
static const char *imx8mq_sai1_sels[] = {"osc_25m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", "sys1_pll_133m", "osc_27m", "clk_ext1", "clk_ext2", };
|
||||
|
||||
static const char *imx8mq_sai2_sels[] = {"osc_25m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", "sys1_pll_133m", "osc_27m", "clk_ext2", "clk_ext3", };
|
||||
|
||||
static const char *imx8mq_sai3_sels[] = {"osc_25m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", "sys1_pll_133m", "osc_27m", "clk_ext3", "clk_ext4", };
|
||||
|
||||
static const char *imx8mq_sai4_sels[] = {"osc_25m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", "sys1_pll_133m", "osc_27m", "clk_ext1", "clk_ext2", };
|
||||
|
||||
static const char *imx8mq_sai5_sels[] = {"osc_25m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", "sys1_pll_133m", "osc_27m", "clk_ext2", "clk_ext3", };
|
||||
|
||||
static const char *imx8mq_sai6_sels[] = {"osc_25m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", "sys1_pll_133m", "osc_27m", "clk_ext3", "clk_ext4", };
|
||||
|
||||
static const char *imx8mq_spdif1_sels[] = {"osc_25m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", "sys1_pll_133m", "osc_27m", "clk_ext2", "clk_ext3", };
|
||||
|
||||
static const char *imx8mq_spdif2_sels[] = {"osc_25m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", "sys1_pll_133m", "osc_27m", "clk_ext3", "clk_ext4", };
|
||||
|
||||
static const char *imx8mq_enet_ref_sels[] = {"osc_25m", "sys2_pll_125m", "sys2_pll_500m", "sys2_pll_100m",
|
||||
"sys1_pll_160m", "audio_pll1_out", "video_pll1_out", "clk_ext4", };
|
||||
|
||||
static const char *imx8mq_enet_timer_sels[] = {"osc_25m", "sys2_pll_100m", "audio_pll1_out", "clk_ext1", "clk_ext2",
|
||||
"clk_ext3", "clk_ext4", "video_pll1_out", };
|
||||
|
||||
static const char *imx8mq_enet_phy_sels[] = {"osc_25m", "sys2_pll_50m", "sys2_pll_125m", "sys2_pll_500m",
|
||||
"audio_pll1_out", "video_pll1_out", "audio_pll2_out", };
|
||||
|
||||
static const char *imx8mq_nand_sels[] = {"osc_25m", "sys2_pll_500m", "audio_pll1_out", "sys1_pll_400m",
|
||||
"audio_pll2_out", "sys3_pll2_out", "sys2_pll_250m", "video_pll1_out", };
|
||||
|
||||
static const char *imx8mq_qspi_sels[] = {"osc_25m", "sys1_pll_400m", "sys1_pll_800m", "sys2_pll_500m",
|
||||
"audio_pll2_out", "sys1_pll_266m", "sys3_pll2_out", "sys1_pll_100m", };
|
||||
|
||||
static const char *imx8mq_usdhc1_sels[] = {"osc_25m", "sys1_pll_400m", "sys1_pll_800m", "sys2_pll_500m",
|
||||
"audio_pll2_out", "sys1_pll_266m", "sys3_pll2_out", "sys1_pll_100m", };
|
||||
|
||||
static const char *imx8mq_usdhc2_sels[] = {"osc_25m", "sys1_pll_400m", "sys1_pll_800m", "sys2_pll_500m",
|
||||
"audio_pll2_out", "sys1_pll_266m", "sys3_pll2_out", "sys1_pll_100m", };
|
||||
|
||||
static const char *imx8mq_i2c1_sels[] = {"osc_25m", "sys1_pll_160m", "sys2_pll_50m", "sys3_pll2_out", "audio_pll1_out",
|
||||
"video_pll1_out", "audio_pll2_out", "sys1_pll_133m", };
|
||||
|
||||
static const char *imx8mq_i2c2_sels[] = {"osc_25m", "sys1_pll_160m", "sys2_pll_50m", "sys3_pll2_out", "audio_pll1_out",
|
||||
"video_pll1_out", "audio_pll2_out", "sys1_pll_133m", };
|
||||
|
||||
static const char *imx8mq_i2c3_sels[] = {"osc_25m", "sys1_pll_160m", "sys2_pll_50m", "sys3_pll2_out", "audio_pll1_out",
|
||||
"video_pll1_out", "audio_pll2_out", "sys1_pll_133m", };
|
||||
|
||||
static const char *imx8mq_i2c4_sels[] = {"osc_25m", "sys1_pll_160m", "sys2_pll_50m", "sys3_pll2_out", "audio_pll1_out",
|
||||
"video_pll1_out", "audio_pll2_out", "sys1_pll_133m", };
|
||||
|
||||
static const char *imx8mq_uart1_sels[] = {"osc_25m", "sys1_pll_80m", "sys2_pll_200m", "sys2_pll_100m",
|
||||
"sys3_pll2_out", "clk_ext2", "clk_ext4", "audio_pll2_out", };
|
||||
|
||||
static const char *imx8mq_uart2_sels[] = {"osc_25m", "sys1_pll_80m", "sys2_pll_200m", "sys2_pll_100m",
|
||||
"sys3_pll2_out", "clk_ext2", "clk_ext3", "audio_pll2_out", };
|
||||
|
||||
static const char *imx8mq_uart3_sels[] = {"osc_25m", "sys1_pll_80m", "sys2_pll_200m", "sys2_pll_100m",
|
||||
"sys3_pll2_out", "clk_ext2", "clk_ext4", "audio_pll2_out", };
|
||||
|
||||
static const char *imx8mq_uart4_sels[] = {"osc_25m", "sys1_pll_80m", "sys2_pll_200m", "sys2_pll_100m",
|
||||
"sys3_pll2_out", "clk_ext2", "clk_ext3", "audio_pll2_out", };
|
||||
|
||||
static const char *imx8mq_usb_core_sels[] = {"osc_25m", "sys1_pll_100m", "sys1_pll_40m", "sys2_pll_100m",
|
||||
"sys2_pll_200m", "clk_ext2", "clk_ext3", "audio_pll2_out", };
|
||||
|
||||
static const char *imx8mq_usb_phy_sels[] = {"osc_25m", "sys1_pll_100m", "sys1_pll_40m", "sys2_pll_100m",
|
||||
"sys2_pll_200m", "clk_ext2", "clk_ext3", "audio_pll2_out", };
|
||||
|
||||
static const char *imx8mq_ecspi1_sels[] = {"osc_25m", "sys2_pll_200m", "sys1_pll_40m", "sys1_pll_160m",
|
||||
"sys1_pll_800m", "sys3_pll2_out", "sys2_pll_250m", "audio_pll2_out", };
|
||||
|
||||
static const char *imx8mq_ecspi2_sels[] = {"osc_25m", "sys2_pll_200m", "sys1_pll_40m", "sys1_pll_160m",
|
||||
"sys1_pll_800m", "sys3_pll2_out", "sys2_pll_250m", "audio_pll2_out", };
|
||||
|
||||
static const char *imx8mq_pwm1_sels[] = {"osc_25m", "sys2_pll_100m", "sys1_pll_160m", "sys1_pll_40m",
|
||||
"sys3_pll2_out", "clk_ext1", "sys1_pll_80m", "video_pll1_out", };
|
||||
|
||||
static const char *imx8mq_pwm2_sels[] = {"osc_25m", "sys2_pll_100m", "sys1_pll_160m", "sys1_pll_40m",
|
||||
"sys3_pll2_out", "clk_ext1", "sys1_pll_80m", "video_pll1_out", };
|
||||
|
||||
static const char *imx8mq_pwm3_sels[] = {"osc_25m", "sys2_pll_100m", "sys1_pll_160m", "sys1_pll_40m",
|
||||
"sys3_pll2_out", "clk_ext2", "sys1_pll_80m", "video_pll1_out", };
|
||||
|
||||
static const char *imx8mq_pwm4_sels[] = {"osc_25m", "sys2_pll_100m", "sys1_pll_160m", "sys1_pll_40m",
|
||||
"sys3_pll2_out", "clk_ext2", "sys1_pll_80m", "video_pll1_out", };
|
||||
|
||||
static const char *imx8mq_gpt1_sels[] = {"osc_25m", "sys2_pll_100m", "sys1_pll_400m", "sys1_pll_40m",
|
||||
"sys1_pll_80m", "audio_pll1_out", "clk_ext1", };
|
||||
|
||||
static const char *imx8mq_wdog_sels[] = {"osc_25m", "sys1_pll_133m", "sys1_pll_160m", "vpu_pll_out",
|
||||
"sys2_pll_125m", "sys3_pll2_out", "sys1_pll_80m", "sys2_pll_166m", };
|
||||
|
||||
static const char *imx8mq_wrclk_sels[] = {"osc_25m", "sys1_pll_40m", "vpu_pll_out", "sys3_pll2_out", "sys2_pll_200m",
|
||||
"sys1_pll_266m", "sys2_pll_500m", "sys1_pll_100m", };
|
||||
|
||||
static const char *imx8mq_dsi_core_sels[] = {"osc_25m", "sys1_pll_266m", "sys2_pll_250m", "sys1_pll_800m",
|
||||
"sys2_pll_1000m", "sys3_pll2_out", "audio_pll2_out", "video_pll1_out", };
|
||||
|
||||
static const char *imx8mq_dsi_phy_sels[] = {"osc_25m", "sys2_pll_125m", "sys2_pll_100m", "sys1_pll_800m",
|
||||
"sys2_pll_1000m", "clk_ext2", "audio_pll2_out", "video_pll1_out", };
|
||||
|
||||
static const char *imx8mq_dsi_dbi_sels[] = {"osc_25m", "sys1_pll_266m", "sys2_pll_100m", "sys1_pll_800m",
|
||||
"sys2_pll_1000m", "sys3_pll2_out", "audio_pll2_out", "video_pll1_out", };
|
||||
|
||||
static const char *imx8mq_dsi_esc_sels[] = {"osc_25m", "sys2_pll_100m", "sys1_pll_80m", "sys1_pll_800m",
|
||||
"sys2_pll_1000m", "sys3_pll2_out", "clk_ext3", "audio_pll2_out", };
|
||||
|
||||
static const char *imx8mq_csi1_core_sels[] = {"osc_25m", "sys1_pll_266m", "sys2_pll_250m", "sys1_pll_800m",
|
||||
"sys2_pll_1000m", "sys3_pll2_out", "audio_pll2_out", "video_pll1_out", };
|
||||
|
||||
static const char *imx8mq_csi1_phy_sels[] = {"osc_25m", "sys2_pll_125m", "sys2_pll_100m", "sys1_pll_800m",
|
||||
"sys2_pll_1000m", "clk_ext2", "audio_pll2_out", "video_pll1_out", };
|
||||
|
||||
static const char *imx8mq_csi1_esc_sels[] = {"osc_25m", "sys2_pll_100m", "sys1_pll_80m", "sys1_pll_800m",
|
||||
"sys2_pll_1000m", "sys3_pll2_out", "clk_ext3", "audio_pll2_out", };
|
||||
|
||||
static const char *imx8mq_csi2_core_sels[] = {"osc_25m", "sys1_pll_266m", "sys2_pll_250m", "sys1_pll_800m",
|
||||
"sys2_pll_1000m", "sys3_pll2_out", "audio_pll2_out", "video_pll1_out", };
|
||||
|
||||
static const char *imx8mq_csi2_phy_sels[] = {"osc_25m", "sys2_pll_125m", "sys2_pll_100m", "sys1_pll_800m",
|
||||
"sys2_pll_1000m", "clk_ext2", "audio_pll2_out", "video_pll1_out", };
|
||||
|
||||
static const char *imx8mq_csi2_esc_sels[] = {"osc_25m", "sys2_pll_100m", "sys1_pll_80m", "sys1_pll_800m",
|
||||
"sys2_pll_1000m", "sys3_pll2_out", "clk_ext3", "audio_pll2_out", };
|
||||
|
||||
static const char *imx8mq_pcie2_ctrl_sels[] = {"osc_25m", "sys2_pll_250m", "sys2_pll_200m", "sys1_pll_266m",
|
||||
"sys1_pll_800m", "sys2_pll_500m", "sys2_pll_333m", "sys3_pll2_out", };
|
||||
|
||||
static const char *imx8mq_pcie2_phy_sels[] = {"osc_25m", "sys2_pll_100m", "sys2_pll_500m", "clk_ext1",
|
||||
"clk_ext2", "clk_ext3", "clk_ext4", "sys1_pll_400m", };
|
||||
|
||||
static const char *imx8mq_pcie2_aux_sels[] = {"osc_25m", "sys2_pll_200m", "sys2_pll_50m", "sys3_pll2_out",
|
||||
"sys2_pll_100m", "sys1_pll_80m", "sys1_pll_160m", "sys1_pll_200m", };
|
||||
|
||||
static const char *imx8mq_ecspi3_sels[] = {"osc_25m", "sys2_pll_200m", "sys1_pll_40m", "sys1_pll_160m",
|
||||
"sys1_pll_800m", "sys3_pll2_out", "sys2_pll_250m", "audio_pll2_out", };
|
||||
static const char *imx8mq_dram_core_sels[] = {"dram_pll_out", "dram_alt_root", };
|
||||
|
||||
static const char *imx8mq_clko2_sels[] = {"osc_25m", "sys2_pll_200m", "sys1_pll_400m", "sys2_pll_166m", "audio_pll1_out",
|
||||
"video_pll1_out", "ckil", };
|
||||
|
||||
static struct clk_onecell_data clk_data;
|
||||
|
||||
static int imx8mq_clocks_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
void __iomem *base;
|
||||
int err;
|
||||
int i;
|
||||
|
||||
clks[IMX8MQ_CLK_DUMMY] = imx_clk_fixed("dummy", 0);
|
||||
clks[IMX8MQ_CLK_32K] = of_clk_get_by_name(np, "ckil");
|
||||
clks[IMX8MQ_CLK_25M] = of_clk_get_by_name(np, "osc_25m");
|
||||
clks[IMX8MQ_CLK_27M] = of_clk_get_by_name(np, "osc_27m");
|
||||
clks[IMX8MQ_CLK_EXT1] = of_clk_get_by_name(np, "clk_ext1");
|
||||
clks[IMX8MQ_CLK_EXT2] = of_clk_get_by_name(np, "clk_ext2");
|
||||
clks[IMX8MQ_CLK_EXT3] = of_clk_get_by_name(np, "clk_ext3");
|
||||
clks[IMX8MQ_CLK_EXT4] = of_clk_get_by_name(np, "clk_ext4");
|
||||
|
||||
np = of_find_compatible_node(NULL, NULL, "fsl,imx8mq-anatop");
|
||||
base = of_iomap(np, 0);
|
||||
if (WARN_ON(!base))
|
||||
return -ENOMEM;
|
||||
|
||||
clks[IMX8MQ_ARM_PLL_REF_SEL] = imx_clk_mux("arm_pll_ref_sel", base + 0x28, 16, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels));
|
||||
clks[IMX8MQ_GPU_PLL_REF_SEL] = imx_clk_mux("gpu_pll_ref_sel", base + 0x18, 16, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels));
|
||||
clks[IMX8MQ_VPU_PLL_REF_SEL] = imx_clk_mux("vpu_pll_ref_sel", base + 0x20, 16, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels));
|
||||
clks[IMX8MQ_AUDIO_PLL1_REF_SEL] = imx_clk_mux("audio_pll1_ref_sel", base + 0x0, 16, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels));
|
||||
clks[IMX8MQ_AUDIO_PLL2_REF_SEL] = imx_clk_mux("audio_pll2_ref_sel", base + 0x8, 16, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels));
|
||||
clks[IMX8MQ_VIDEO_PLL1_REF_SEL] = imx_clk_mux("video_pll1_ref_sel", base + 0x10, 16, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels));
|
||||
clks[IMX8MQ_SYS1_PLL1_REF_SEL] = imx_clk_mux("sys1_pll1_ref_sel", base + 0x30, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels));
|
||||
clks[IMX8MQ_SYS2_PLL1_REF_SEL] = imx_clk_mux("sys2_pll1_ref_sel", base + 0x3c, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels));
|
||||
clks[IMX8MQ_SYS3_PLL1_REF_SEL] = imx_clk_mux("sys3_pll1_ref_sel", base + 0x48, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels));
|
||||
clks[IMX8MQ_DRAM_PLL1_REF_SEL] = imx_clk_mux("dram_pll1_ref_sel", base + 0x60, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels));
|
||||
|
||||
clks[IMX8MQ_ARM_PLL_REF_DIV] = imx_clk_divider("arm_pll_ref_div", "arm_pll_ref_sel", base + 0x28, 5, 6);
|
||||
clks[IMX8MQ_GPU_PLL_REF_DIV] = imx_clk_divider("gpu_pll_ref_div", "gpu_pll_ref_sel", base + 0x18, 5, 6);
|
||||
clks[IMX8MQ_VPU_PLL_REF_DIV] = imx_clk_divider("vpu_pll_ref_div", "vpu_pll_ref_sel", base + 0x20, 5, 6);
|
||||
clks[IMX8MQ_AUDIO_PLL1_REF_DIV] = imx_clk_divider("audio_pll1_ref_div", "audio_pll1_ref_sel", base + 0x0, 5, 6);
|
||||
clks[IMX8MQ_AUDIO_PLL2_REF_DIV] = imx_clk_divider("audio_pll2_ref_div", "audio_pll2_ref_sel", base + 0x8, 5, 6);
|
||||
clks[IMX8MQ_VIDEO_PLL1_REF_DIV] = imx_clk_divider("video_pll1_ref_div", "video_pll1_ref_sel", base + 0x10, 5, 6);
|
||||
clks[IMX8MQ_SYS1_PLL1_REF_DIV] = imx_clk_divider("sys1_pll1_ref_div", "sys1_pll1_ref_sel", base + 0x38, 25, 3);
|
||||
clks[IMX8MQ_SYS2_PLL1_REF_DIV] = imx_clk_divider("sys2_pll1_ref_div", "sys2_pll1_ref_sel", base + 0x44, 25, 3);
|
||||
clks[IMX8MQ_SYS3_PLL1_REF_DIV] = imx_clk_divider("sys3_pll1_ref_div", "sys3_pll1_ref_sel", base + 0x50, 25, 3);
|
||||
clks[IMX8MQ_DRAM_PLL1_REF_DIV] = imx_clk_divider("dram_pll1_ref_div", "dram_pll1_ref_sel", base + 0x68, 25, 3);
|
||||
|
||||
clks[IMX8MQ_ARM_PLL] = imx_clk_frac_pll("arm_pll", "arm_pll_ref_div", base + 0x28);
|
||||
clks[IMX8MQ_GPU_PLL] = imx_clk_frac_pll("gpu_pll", "gpu_pll_ref_div", base + 0x18);
|
||||
clks[IMX8MQ_VPU_PLL] = imx_clk_frac_pll("vpu_pll", "vpu_pll_ref_div", base + 0x20);
|
||||
clks[IMX8MQ_AUDIO_PLL1] = imx_clk_frac_pll("audio_pll1", "audio_pll1_ref_div", base + 0x0);
|
||||
clks[IMX8MQ_AUDIO_PLL2] = imx_clk_frac_pll("audio_pll2", "audio_pll2_ref_div", base + 0x8);
|
||||
clks[IMX8MQ_VIDEO_PLL1] = imx_clk_frac_pll("video_pll1", "video_pll1_ref_div", base + 0x10);
|
||||
clks[IMX8MQ_SYS1_PLL1] = imx_clk_sccg_pll("sys1_pll1", "sys1_pll1_ref_div", base + 0x30, SCCG_PLL1);
|
||||
clks[IMX8MQ_SYS2_PLL1] = imx_clk_sccg_pll("sys2_pll1", "sys2_pll1_ref_div", base + 0x3c, SCCG_PLL1);
|
||||
clks[IMX8MQ_SYS3_PLL1] = imx_clk_sccg_pll("sys3_pll1", "sys3_pll1_ref_div", base + 0x48, SCCG_PLL1);
|
||||
clks[IMX8MQ_DRAM_PLL1] = imx_clk_sccg_pll("dram_pll1", "dram_pll1_ref_div", base + 0x60, SCCG_PLL1);
|
||||
|
||||
clks[IMX8MQ_SYS1_PLL2] = imx_clk_sccg_pll("sys1_pll2", "sys1_pll1_out_div", base + 0x30, SCCG_PLL2);
|
||||
clks[IMX8MQ_SYS2_PLL2] = imx_clk_sccg_pll("sys2_pll2", "sys2_pll1_out_div", base + 0x3c, SCCG_PLL2);
|
||||
clks[IMX8MQ_SYS3_PLL2] = imx_clk_sccg_pll("sys3_pll2", "sys3_pll1_out_div", base + 0x48, SCCG_PLL2);
|
||||
clks[IMX8MQ_DRAM_PLL2] = imx_clk_sccg_pll("dram_pll2", "dram_pll1_out_div", base + 0x60, SCCG_PLL2);
|
||||
|
||||
/* PLL divs */
|
||||
clks[IMX8MQ_SYS1_PLL1_OUT_DIV] = imx_clk_divider("sys1_pll1_out_div", "sys1_pll1_out", base + 0x38, 19, 6);
|
||||
clks[IMX8MQ_SYS2_PLL1_OUT_DIV] = imx_clk_divider("sys2_pll1_out_div", "sys2_pll1_out", base + 0x44, 19, 6);
|
||||
clks[IMX8MQ_SYS3_PLL1_OUT_DIV] = imx_clk_divider("sys3_pll1_out_div", "sys3_pll1_out", base + 0x50, 19, 6);
|
||||
clks[IMX8MQ_DRAM_PLL1_OUT_DIV] = imx_clk_divider("dram_pll1_out_div", "dram_pll1_out", base + 0x68, 19, 6);
|
||||
clks[IMX8MQ_SYS1_PLL2_DIV] = imx_clk_divider("sys1_pll2_div", "sys1_pll2", base + 0x38, 1, 6);
|
||||
clks[IMX8MQ_SYS2_PLL2_DIV] = imx_clk_divider("sys2_pll2_div", "sys2_pll2", base + 0x44, 1, 6);
|
||||
clks[IMX8MQ_SYS3_PLL2_DIV] = imx_clk_divider("sys3_pll2_div", "sys3_pll2", base + 0x50, 1, 6);
|
||||
clks[IMX8MQ_DRAM_PLL2_DIV] = imx_clk_divider("dram_pll2_div", "dram_pll2", base + 0x68, 1, 6);
|
||||
|
||||
/* PLL bypass out */
|
||||
clks[IMX8MQ_ARM_PLL_BYPASS] = imx_clk_mux("arm_pll_bypass", base + 0x28, 14, 1, arm_pll_bypass_sels, ARRAY_SIZE(arm_pll_bypass_sels));
|
||||
clks[IMX8MQ_GPU_PLL_BYPASS] = imx_clk_mux("gpu_pll_bypass", base + 0x18, 14, 1, gpu_pll_bypass_sels, ARRAY_SIZE(gpu_pll_bypass_sels));
|
||||
clks[IMX8MQ_VPU_PLL_BYPASS] = imx_clk_mux("vpu_pll_bypass", base + 0x20, 14, 1, vpu_pll_bypass_sels, ARRAY_SIZE(vpu_pll_bypass_sels));
|
||||
clks[IMX8MQ_AUDIO_PLL1_BYPASS] = imx_clk_mux("audio_pll1_bypass", base + 0x0, 14, 1, audio_pll1_bypass_sels, ARRAY_SIZE(audio_pll1_bypass_sels));
|
||||
clks[IMX8MQ_AUDIO_PLL2_BYPASS] = imx_clk_mux("audio_pll2_bypass", base + 0x8, 14, 1, audio_pll2_bypass_sels, ARRAY_SIZE(audio_pll2_bypass_sels));
|
||||
clks[IMX8MQ_VIDEO_PLL1_BYPASS] = imx_clk_mux("video_pll1_bypass", base + 0x10, 14, 1, video_pll1_bypass_sels, ARRAY_SIZE(video_pll1_bypass_sels));
|
||||
|
||||
clks[IMX8MQ_SYS1_PLL1_OUT] = imx_clk_mux("sys1_pll1_out", base + 0x30, 5, 1, sys1_pll1_out_sels, ARRAY_SIZE(sys1_pll1_out_sels));
|
||||
clks[IMX8MQ_SYS2_PLL1_OUT] = imx_clk_mux("sys2_pll1_out", base + 0x3c, 5, 1, sys2_pll1_out_sels, ARRAY_SIZE(sys2_pll1_out_sels));
|
||||
clks[IMX8MQ_SYS3_PLL1_OUT] = imx_clk_mux("sys3_pll1_out", base + 0x48, 5, 1, sys3_pll1_out_sels, ARRAY_SIZE(sys3_pll1_out_sels));
|
||||
clks[IMX8MQ_DRAM_PLL1_OUT] = imx_clk_mux("dram_pll1_out", base + 0x60, 5, 1, dram_pll1_out_sels, ARRAY_SIZE(dram_pll1_out_sels));
|
||||
clks[IMX8MQ_SYS1_PLL2_OUT] = imx_clk_mux("sys1_pll2_out", base + 0x30, 4, 1, sys1_pll2_out_sels, ARRAY_SIZE(sys1_pll2_out_sels));
|
||||
clks[IMX8MQ_SYS2_PLL2_OUT] = imx_clk_mux("sys2_pll2_out", base + 0x3c, 4, 1, sys2_pll2_out_sels, ARRAY_SIZE(sys2_pll2_out_sels));
|
||||
clks[IMX8MQ_SYS3_PLL2_OUT] = imx_clk_mux("sys3_pll2_out", base + 0x48, 4, 1, sys3_pll2_out_sels, ARRAY_SIZE(sys3_pll2_out_sels));
|
||||
clks[IMX8MQ_DRAM_PLL2_OUT] = imx_clk_mux("dram_pll2_out", base + 0x60, 4, 1, dram_pll2_out_sels, ARRAY_SIZE(dram_pll2_out_sels));
|
||||
|
||||
/* PLL OUT GATE */
|
||||
clks[IMX8MQ_ARM_PLL_OUT] = imx_clk_gate("arm_pll_out", "arm_pll_bypass", base + 0x28, 21);
|
||||
clks[IMX8MQ_GPU_PLL_OUT] = imx_clk_gate("gpu_pll_out", "gpu_pll_bypass", base + 0x18, 21);
|
||||
clks[IMX8MQ_VPU_PLL_OUT] = imx_clk_gate("vpu_pll_out", "vpu_pll_bypass", base + 0x20, 21);
|
||||
clks[IMX8MQ_AUDIO_PLL1_OUT] = imx_clk_gate("audio_pll1_out", "audio_pll1_bypass", base + 0x0, 21);
|
||||
clks[IMX8MQ_AUDIO_PLL2_OUT] = imx_clk_gate("audio_pll2_out", "audio_pll2_bypass", base + 0x8, 21);
|
||||
clks[IMX8MQ_VIDEO_PLL1_OUT] = imx_clk_gate("video_pll1_out", "video_pll1_bypass", base + 0x10, 21);
|
||||
clks[IMX8MQ_SYS1_PLL_OUT] = imx_clk_gate("sys1_pll_out", "sys1_pll2_out", base + 0x30, 9);
|
||||
clks[IMX8MQ_SYS2_PLL_OUT] = imx_clk_gate("sys2_pll_out", "sys2_pll2_out", base + 0x3c, 9);
|
||||
clks[IMX8MQ_SYS3_PLL_OUT] = imx_clk_gate("sys3_pll_out", "sys3_pll2_out", base + 0x48, 9);
|
||||
clks[IMX8MQ_DRAM_PLL_OUT] = imx_clk_gate("dram_pll_out", "dram_pll2_out", base + 0x60, 9);
|
||||
|
||||
/* SYS PLL fixed output */
|
||||
clks[IMX8MQ_SYS1_PLL_40M] = imx_clk_fixed_factor("sys1_pll_40m", "sys1_pll_out", 1, 20);
|
||||
clks[IMX8MQ_SYS1_PLL_80M] = imx_clk_fixed_factor("sys1_pll_80m", "sys1_pll_out", 1, 10);
|
||||
clks[IMX8MQ_SYS1_PLL_100M] = imx_clk_fixed_factor("sys1_pll_100m", "sys1_pll_out", 1, 8);
|
||||
clks[IMX8MQ_SYS1_PLL_133M] = imx_clk_fixed_factor("sys1_pll_133m", "sys1_pll_out", 1, 6);
|
||||
clks[IMX8MQ_SYS1_PLL_160M] = imx_clk_fixed_factor("sys1_pll_160m", "sys1_pll_out", 1, 5);
|
||||
clks[IMX8MQ_SYS1_PLL_200M] = imx_clk_fixed_factor("sys1_pll_200m", "sys1_pll_out", 1, 4);
|
||||
clks[IMX8MQ_SYS1_PLL_266M] = imx_clk_fixed_factor("sys1_pll_266m", "sys1_pll_out", 1, 3);
|
||||
clks[IMX8MQ_SYS1_PLL_400M] = imx_clk_fixed_factor("sys1_pll_400m", "sys1_pll_out", 1, 2);
|
||||
clks[IMX8MQ_SYS1_PLL_800M] = imx_clk_fixed_factor("sys1_pll_800m", "sys1_pll_out", 1, 1);
|
||||
|
||||
clks[IMX8MQ_SYS2_PLL_50M] = imx_clk_fixed_factor("sys2_pll_50m", "sys2_pll_out", 1, 20);
|
||||
clks[IMX8MQ_SYS2_PLL_100M] = imx_clk_fixed_factor("sys2_pll_100m", "sys2_pll_out", 1, 10);
|
||||
clks[IMX8MQ_SYS2_PLL_125M] = imx_clk_fixed_factor("sys2_pll_125m", "sys2_pll_out", 1, 8);
|
||||
clks[IMX8MQ_SYS2_PLL_166M] = imx_clk_fixed_factor("sys2_pll_166m", "sys2_pll_out", 1, 6);
|
||||
clks[IMX8MQ_SYS2_PLL_200M] = imx_clk_fixed_factor("sys2_pll_200m", "sys2_pll_out", 1, 5);
|
||||
clks[IMX8MQ_SYS2_PLL_250M] = imx_clk_fixed_factor("sys2_pll_250m", "sys2_pll_out", 1, 4);
|
||||
clks[IMX8MQ_SYS2_PLL_333M] = imx_clk_fixed_factor("sys2_pll_333m", "sys2_pll_out", 1, 3);
|
||||
clks[IMX8MQ_SYS2_PLL_500M] = imx_clk_fixed_factor("sys2_pll_500m", "sys2_pll_out", 1, 2);
|
||||
clks[IMX8MQ_SYS2_PLL_1000M] = imx_clk_fixed_factor("sys2_pll_1000m", "sys2_pll_out", 1, 1);
|
||||
|
||||
np = dev->of_node;
|
||||
base = of_iomap(np, 0);
|
||||
if (WARN_ON(!base))
|
||||
return -ENOMEM;
|
||||
|
||||
/* CORE */
|
||||
clks[IMX8MQ_CLK_A53_SRC] = imx_clk_mux2("arm_a53_src", base + 0x8000, 24, 3, imx8mq_a53_sels, ARRAY_SIZE(imx8mq_a53_sels));
|
||||
clks[IMX8MQ_CLK_VPU_SRC] = imx_clk_mux2("vpu_src", base + 0x8100, 24, 3, imx8mq_vpu_sels, ARRAY_SIZE(imx8mq_vpu_sels));
|
||||
clks[IMX8MQ_CLK_GPU_CORE_SRC] = imx_clk_mux2("gpu_core_src", base + 0x8180, 24, 3, imx8mq_gpu_core_sels, ARRAY_SIZE(imx8mq_gpu_core_sels));
|
||||
clks[IMX8MQ_CLK_GPU_SHADER_SRC] = imx_clk_mux2("gpu_shader_src", base + 0x8200, 24, 3, imx8mq_gpu_shader_sels, ARRAY_SIZE(imx8mq_gpu_shader_sels));
|
||||
clks[IMX8MQ_CLK_A53_CG] = imx_clk_gate3_flags("arm_a53_cg", "arm_a53_src", base + 0x8000, 28, CLK_IS_CRITICAL);
|
||||
clks[IMX8MQ_CLK_VPU_CG] = imx_clk_gate3("vpu_cg", "vpu_src", base + 0x8100, 28);
|
||||
clks[IMX8MQ_CLK_GPU_CORE_CG] = imx_clk_gate3("gpu_core_cg", "gpu_core_src", base + 0x8180, 28);
|
||||
clks[IMX8MQ_CLK_GPU_SHADER_CG] = imx_clk_gate3("gpu_shader_cg", "gpu_shader_src", base + 0x8200, 28);
|
||||
|
||||
clks[IMX8MQ_CLK_A53_DIV] = imx_clk_divider2("arm_a53_div", "arm_a53_cg", base + 0x8000, 0, 3);
|
||||
clks[IMX8MQ_CLK_VPU_DIV] = imx_clk_divider2("vpu_div", "vpu_cg", base + 0x8100, 0, 3);
|
||||
clks[IMX8MQ_CLK_GPU_CORE_DIV] = imx_clk_divider2("gpu_core_div", "gpu_core_cg", base + 0x8180, 0, 3);
|
||||
clks[IMX8MQ_CLK_GPU_SHADER_DIV] = imx_clk_divider2("gpu_shader_div", "gpu_shader_cg", base + 0x8200, 0, 3);
|
||||
|
||||
/* BUS */
|
||||
clks[IMX8MQ_CLK_MAIN_AXI] = imx8m_clk_composite_critical("main_axi", imx8mq_main_axi_sels, base + 0x8800);
|
||||
clks[IMX8MQ_CLK_ENET_AXI] = imx8m_clk_composite("enet_axi", imx8mq_enet_axi_sels, base + 0x8880);
|
||||
clks[IMX8MQ_CLK_NAND_USDHC_BUS] = imx8m_clk_composite("nand_usdhc_bus", imx8mq_nand_usdhc_sels, base + 0x8900);
|
||||
clks[IMX8MQ_CLK_VPU_BUS] = imx8m_clk_composite("vpu_bus", imx8mq_vpu_bus_sels, base + 0x8980);
|
||||
clks[IMX8MQ_CLK_DISP_AXI] = imx8m_clk_composite("disp_axi", imx8mq_disp_axi_sels, base + 0x8a00);
|
||||
clks[IMX8MQ_CLK_DISP_APB] = imx8m_clk_composite("disp_apb", imx8mq_disp_apb_sels, base + 0x8a80);
|
||||
clks[IMX8MQ_CLK_DISP_RTRM] = imx8m_clk_composite("disp_rtrm", imx8mq_disp_rtrm_sels, base + 0x8b00);
|
||||
clks[IMX8MQ_CLK_USB_BUS] = imx8m_clk_composite("usb_bus", imx8mq_usb_bus_sels, base + 0x8b80);
|
||||
clks[IMX8MQ_CLK_GPU_AXI] = imx8m_clk_composite("gpu_axi", imx8mq_gpu_axi_sels, base + 0x8c00);
|
||||
clks[IMX8MQ_CLK_GPU_AHB] = imx8m_clk_composite("gpu_ahb", imx8mq_gpu_ahb_sels, base + 0x8c80);
|
||||
clks[IMX8MQ_CLK_NOC] = imx8m_clk_composite_critical("noc", imx8mq_noc_sels, base + 0x8d00);
|
||||
clks[IMX8MQ_CLK_NOC_APB] = imx8m_clk_composite_critical("noc_apb", imx8mq_noc_apb_sels, base + 0x8d80);
|
||||
|
||||
/* AHB */
|
||||
clks[IMX8MQ_CLK_AHB] = imx8m_clk_composite("ahb", imx8mq_ahb_sels, base + 0x9000);
|
||||
clks[IMX8MQ_CLK_AUDIO_AHB] = imx8m_clk_composite("audio_ahb", imx8mq_audio_ahb_sels, base + 0x9100);
|
||||
|
||||
/* IPG */
|
||||
clks[IMX8MQ_CLK_IPG_ROOT] = imx_clk_divider2("ipg_root", "ahb", base + 0x9080, 0, 1);
|
||||
clks[IMX8MQ_CLK_IPG_AUDIO_ROOT] = imx_clk_divider2("ipg_audio_root", "audio_ahb", base + 0x9180, 0, 1);
|
||||
|
||||
/* IP */
|
||||
clks[IMX8MQ_CLK_DRAM_CORE] = imx_clk_mux2_flags("dram_core_clk", base + 0x9800, 24, 1, imx8mq_dram_core_sels, ARRAY_SIZE(imx8mq_dram_core_sels), CLK_IS_CRITICAL);
|
||||
|
||||
clks[IMX8MQ_CLK_DRAM_ALT] = imx8m_clk_composite("dram_alt", imx8mq_dram_alt_sels, base + 0xa000);
|
||||
clks[IMX8MQ_CLK_DRAM_APB] = imx8m_clk_composite_critical("dram_apb", imx8mq_dram_apb_sels, base + 0xa080);
|
||||
clks[IMX8MQ_CLK_VPU_G1] = imx8m_clk_composite("vpu_g1", imx8mq_vpu_g1_sels, base + 0xa100);
|
||||
clks[IMX8MQ_CLK_VPU_G2] = imx8m_clk_composite("vpu_g2", imx8mq_vpu_g2_sels, base + 0xa180);
|
||||
clks[IMX8MQ_CLK_DISP_DTRC] = imx8m_clk_composite("disp_dtrc", imx8mq_disp_dtrc_sels, base + 0xa200);
|
||||
clks[IMX8MQ_CLK_DISP_DC8000] = imx8m_clk_composite("disp_dc8000", imx8mq_disp_dc8000_sels, base + 0xa280);
|
||||
clks[IMX8MQ_CLK_PCIE1_CTRL] = imx8m_clk_composite("pcie1_ctrl", imx8mq_pcie1_ctrl_sels, base + 0xa300);
|
||||
clks[IMX8MQ_CLK_PCIE1_PHY] = imx8m_clk_composite("pcie1_phy", imx8mq_pcie1_phy_sels, base + 0xa380);
|
||||
clks[IMX8MQ_CLK_PCIE1_AUX] = imx8m_clk_composite("pcie1_aux", imx8mq_pcie1_aux_sels, base + 0xa400);
|
||||
clks[IMX8MQ_CLK_DC_PIXEL] = imx8m_clk_composite("dc_pixel", imx8mq_dc_pixel_sels, base + 0xa480);
|
||||
clks[IMX8MQ_CLK_LCDIF_PIXEL] = imx8m_clk_composite("lcdif_pixel", imx8mq_lcdif_pixel_sels, base + 0xa500);
|
||||
clks[IMX8MQ_CLK_SAI1] = imx8m_clk_composite("sai1", imx8mq_sai1_sels, base + 0xa580);
|
||||
clks[IMX8MQ_CLK_SAI2] = imx8m_clk_composite("sai2", imx8mq_sai2_sels, base + 0xa600);
|
||||
clks[IMX8MQ_CLK_SAI3] = imx8m_clk_composite("sai3", imx8mq_sai3_sels, base + 0xa680);
|
||||
clks[IMX8MQ_CLK_SAI4] = imx8m_clk_composite("sai4", imx8mq_sai4_sels, base + 0xa700);
|
||||
clks[IMX8MQ_CLK_SAI5] = imx8m_clk_composite("sai5", imx8mq_sai5_sels, base + 0xa780);
|
||||
clks[IMX8MQ_CLK_SAI6] = imx8m_clk_composite("sai6", imx8mq_sai6_sels, base + 0xa800);
|
||||
clks[IMX8MQ_CLK_SPDIF1] = imx8m_clk_composite("spdif1", imx8mq_spdif1_sels, base + 0xa880);
|
||||
clks[IMX8MQ_CLK_SPDIF2] = imx8m_clk_composite("spdif2", imx8mq_spdif2_sels, base + 0xa900);
|
||||
clks[IMX8MQ_CLK_ENET_REF] = imx8m_clk_composite("enet_ref", imx8mq_enet_ref_sels, base + 0xa980);
|
||||
clks[IMX8MQ_CLK_ENET_TIMER] = imx8m_clk_composite("enet_timer", imx8mq_enet_timer_sels, base + 0xaa00);
|
||||
clks[IMX8MQ_CLK_ENET_PHY_REF] = imx8m_clk_composite("enet_phy", imx8mq_enet_phy_sels, base + 0xaa80);
|
||||
clks[IMX8MQ_CLK_NAND] = imx8m_clk_composite("nand", imx8mq_nand_sels, base + 0xab00);
|
||||
clks[IMX8MQ_CLK_QSPI] = imx8m_clk_composite("qspi", imx8mq_qspi_sels, base + 0xab80);
|
||||
clks[IMX8MQ_CLK_USDHC1] = imx8m_clk_composite("usdhc1", imx8mq_usdhc1_sels, base + 0xac00);
|
||||
clks[IMX8MQ_CLK_USDHC2] = imx8m_clk_composite("usdhc2", imx8mq_usdhc2_sels, base + 0xac80);
|
||||
clks[IMX8MQ_CLK_I2C1] = imx8m_clk_composite("i2c1", imx8mq_i2c1_sels, base + 0xad00);
|
||||
clks[IMX8MQ_CLK_I2C2] = imx8m_clk_composite("i2c2", imx8mq_i2c2_sels, base + 0xad80);
|
||||
clks[IMX8MQ_CLK_I2C3] = imx8m_clk_composite("i2c3", imx8mq_i2c3_sels, base + 0xae00);
|
||||
clks[IMX8MQ_CLK_I2C4] = imx8m_clk_composite("i2c4", imx8mq_i2c4_sels, base + 0xae80);
|
||||
clks[IMX8MQ_CLK_UART1] = imx8m_clk_composite("uart1", imx8mq_uart1_sels, base + 0xaf00);
|
||||
clks[IMX8MQ_CLK_UART2] = imx8m_clk_composite("uart2", imx8mq_uart2_sels, base + 0xaf80);
|
||||
clks[IMX8MQ_CLK_UART3] = imx8m_clk_composite("uart3", imx8mq_uart3_sels, base + 0xb000);
|
||||
clks[IMX8MQ_CLK_UART4] = imx8m_clk_composite("uart4", imx8mq_uart4_sels, base + 0xb080);
|
||||
clks[IMX8MQ_CLK_USB_CORE_REF] = imx8m_clk_composite("usb_core_ref", imx8mq_usb_core_sels, base + 0xb100);
|
||||
clks[IMX8MQ_CLK_USB_PHY_REF] = imx8m_clk_composite("usb_phy_ref", imx8mq_usb_phy_sels, base + 0xb180);
|
||||
clks[IMX8MQ_CLK_ECSPI1] = imx8m_clk_composite("ecspi1", imx8mq_ecspi1_sels, base + 0xb280);
|
||||
clks[IMX8MQ_CLK_ECSPI2] = imx8m_clk_composite("ecspi2", imx8mq_ecspi2_sels, base + 0xb300);
|
||||
clks[IMX8MQ_CLK_PWM1] = imx8m_clk_composite("pwm1", imx8mq_pwm1_sels, base + 0xb380);
|
||||
clks[IMX8MQ_CLK_PWM2] = imx8m_clk_composite("pwm2", imx8mq_pwm2_sels, base + 0xb400);
|
||||
clks[IMX8MQ_CLK_PWM3] = imx8m_clk_composite("pwm3", imx8mq_pwm3_sels, base + 0xb480);
|
||||
clks[IMX8MQ_CLK_PWM4] = imx8m_clk_composite("pwm4", imx8mq_pwm4_sels, base + 0xb500);
|
||||
clks[IMX8MQ_CLK_GPT1] = imx8m_clk_composite("gpt1", imx8mq_gpt1_sels, base + 0xb580);
|
||||
clks[IMX8MQ_CLK_WDOG] = imx8m_clk_composite("wdog", imx8mq_wdog_sels, base + 0xb900);
|
||||
clks[IMX8MQ_CLK_WRCLK] = imx8m_clk_composite("wrclk", imx8mq_wrclk_sels, base + 0xb980);
|
||||
clks[IMX8MQ_CLK_CLKO2] = imx8m_clk_composite("clko2", imx8mq_clko2_sels, base + 0xba80);
|
||||
clks[IMX8MQ_CLK_DSI_CORE] = imx8m_clk_composite("dsi_core", imx8mq_dsi_core_sels, base + 0xbb00);
|
||||
clks[IMX8MQ_CLK_DSI_PHY_REF] = imx8m_clk_composite("dsi_phy_ref", imx8mq_dsi_phy_sels, base + 0xbb80);
|
||||
clks[IMX8MQ_CLK_DSI_DBI] = imx8m_clk_composite("dsi_dbi", imx8mq_dsi_dbi_sels, base + 0xbc00);
|
||||
clks[IMX8MQ_CLK_DSI_ESC] = imx8m_clk_composite("dsi_esc", imx8mq_dsi_esc_sels, base + 0xbc80);
|
||||
clks[IMX8MQ_CLK_DSI_AHB] = imx8m_clk_composite("dsi_ahb", imx8mq_dsi_ahb_sels, base + 0x9200);
|
||||
clks[IMX8MQ_CLK_CSI1_CORE] = imx8m_clk_composite("csi1_core", imx8mq_csi1_core_sels, base + 0xbd00);
|
||||
clks[IMX8MQ_CLK_CSI1_PHY_REF] = imx8m_clk_composite("csi1_phy_ref", imx8mq_csi1_phy_sels, base + 0xbd80);
|
||||
clks[IMX8MQ_CLK_CSI1_ESC] = imx8m_clk_composite("csi1_esc", imx8mq_csi1_esc_sels, base + 0xbe00);
|
||||
clks[IMX8MQ_CLK_CSI2_CORE] = imx8m_clk_composite("csi2_core", imx8mq_csi2_core_sels, base + 0xbe80);
|
||||
clks[IMX8MQ_CLK_CSI2_PHY_REF] = imx8m_clk_composite("csi2_phy_ref", imx8mq_csi2_phy_sels, base + 0xbf00);
|
||||
clks[IMX8MQ_CLK_CSI2_ESC] = imx8m_clk_composite("csi2_esc", imx8mq_csi2_esc_sels, base + 0xbf80);
|
||||
clks[IMX8MQ_CLK_PCIE2_CTRL] = imx8m_clk_composite("pcie2_ctrl", imx8mq_pcie2_ctrl_sels, base + 0xc000);
|
||||
clks[IMX8MQ_CLK_PCIE2_PHY] = imx8m_clk_composite("pcie2_phy", imx8mq_pcie2_phy_sels, base + 0xc080);
|
||||
clks[IMX8MQ_CLK_PCIE2_AUX] = imx8m_clk_composite("pcie2_aux", imx8mq_pcie2_aux_sels, base + 0xc100);
|
||||
clks[IMX8MQ_CLK_ECSPI3] = imx8m_clk_composite("ecspi3", imx8mq_ecspi3_sels, base + 0xc180);
|
||||
|
||||
clks[IMX8MQ_CLK_ECSPI1_ROOT] = imx_clk_gate4("ecspi1_root_clk", "ecspi1", base + 0x4070, 0);
|
||||
clks[IMX8MQ_CLK_ECSPI2_ROOT] = imx_clk_gate4("ecspi2_root_clk", "ecspi2", base + 0x4080, 0);
|
||||
clks[IMX8MQ_CLK_ECSPI3_ROOT] = imx_clk_gate4("ecspi3_root_clk", "ecspi3", base + 0x4090, 0);
|
||||
clks[IMX8MQ_CLK_ENET1_ROOT] = imx_clk_gate4("enet1_root_clk", "enet_axi", base + 0x40a0, 0);
|
||||
clks[IMX8MQ_CLK_GPT1_ROOT] = imx_clk_gate4("gpt1_root_clk", "gpt1", base + 0x4100, 0);
|
||||
clks[IMX8MQ_CLK_I2C1_ROOT] = imx_clk_gate4("i2c1_root_clk", "i2c1", base + 0x4170, 0);
|
||||
clks[IMX8MQ_CLK_I2C2_ROOT] = imx_clk_gate4("i2c2_root_clk", "i2c2", base + 0x4180, 0);
|
||||
clks[IMX8MQ_CLK_I2C3_ROOT] = imx_clk_gate4("i2c3_root_clk", "i2c3", base + 0x4190, 0);
|
||||
clks[IMX8MQ_CLK_I2C4_ROOT] = imx_clk_gate4("i2c4_root_clk", "i2c4", base + 0x41a0, 0);
|
||||
clks[IMX8MQ_CLK_MU_ROOT] = imx_clk_gate4("mu_root_clk", "ipg_root", base + 0x4210, 0);
|
||||
clks[IMX8MQ_CLK_OCOTP_ROOT] = imx_clk_gate4("ocotp_root_clk", "ipg_root", base + 0x4220, 0);
|
||||
clks[IMX8MQ_CLK_PCIE1_ROOT] = imx_clk_gate4("pcie1_root_clk", "pcie1_ctrl", base + 0x4250, 0);
|
||||
clks[IMX8MQ_CLK_PCIE2_ROOT] = imx_clk_gate4("pcie2_root_clk", "pcie2_ctrl", base + 0x4640, 0);
|
||||
clks[IMX8MQ_CLK_PWM1_ROOT] = imx_clk_gate4("pwm1_root_clk", "pwm1", base + 0x4280, 0);
|
||||
clks[IMX8MQ_CLK_PWM2_ROOT] = imx_clk_gate4("pwm2_root_clk", "pwm2", base + 0x4290, 0);
|
||||
clks[IMX8MQ_CLK_PWM3_ROOT] = imx_clk_gate4("pwm3_root_clk", "pwm3", base + 0x42a0, 0);
|
||||
clks[IMX8MQ_CLK_PWM4_ROOT] = imx_clk_gate4("pwm4_root_clk", "pwm4", base + 0x42b0, 0);
|
||||
clks[IMX8MQ_CLK_QSPI_ROOT] = imx_clk_gate4("qspi_root_clk", "qspi", base + 0x42f0, 0);
|
||||
clks[IMX8MQ_CLK_RAWNAND_ROOT] = imx_clk_gate2_shared2("nand_root_clk", "nand", base + 0x4300, 0, &share_count_nand);
|
||||
clks[IMX8MQ_CLK_NAND_USDHC_BUS_RAWNAND_CLK] = imx_clk_gate2_shared2("nand_usdhc_rawnand_clk", "nand_usdhc_bus", base + 0x4300, 0, &share_count_nand);
|
||||
clks[IMX8MQ_CLK_SAI1_ROOT] = imx_clk_gate2_shared2("sai1_root_clk", "sai1", base + 0x4330, 0, &share_count_sai1);
|
||||
clks[IMX8MQ_CLK_SAI1_IPG] = imx_clk_gate2_shared2("sai1_ipg_clk", "ipg_audio_root", base + 0x4330, 0, &share_count_sai1);
|
||||
clks[IMX8MQ_CLK_SAI2_ROOT] = imx_clk_gate2_shared2("sai2_root_clk", "sai2", base + 0x4340, 0, &share_count_sai2);
|
||||
clks[IMX8MQ_CLK_SAI2_IPG] = imx_clk_gate2_shared2("sai2_ipg_clk", "ipg_root", base + 0x4340, 0, &share_count_sai2);
|
||||
clks[IMX8MQ_CLK_SAI3_ROOT] = imx_clk_gate2_shared2("sai3_root_clk", "sai3", base + 0x4350, 0, &share_count_sai3);
|
||||
clks[IMX8MQ_CLK_SAI3_IPG] = imx_clk_gate2_shared2("sai3_ipg_clk", "ipg_root", base + 0x4350, 0, &share_count_sai3);
|
||||
clks[IMX8MQ_CLK_SAI4_ROOT] = imx_clk_gate2_shared2("sai4_root_clk", "sai4", base + 0x4360, 0, &share_count_sai4);
|
||||
clks[IMX8MQ_CLK_SAI4_IPG] = imx_clk_gate2_shared2("sai4_ipg_clk", "ipg_audio_root", base + 0x4360, 0, &share_count_sai4);
|
||||
clks[IMX8MQ_CLK_SAI5_ROOT] = imx_clk_gate2_shared2("sai5_root_clk", "sai5", base + 0x4370, 0, &share_count_sai5);
|
||||
clks[IMX8MQ_CLK_SAI5_IPG] = imx_clk_gate2_shared2("sai5_ipg_clk", "ipg_audio_root", base + 0x4370, 0, &share_count_sai5);
|
||||
clks[IMX8MQ_CLK_SAI6_ROOT] = imx_clk_gate2_shared2("sai6_root_clk", "sai6", base + 0x4380, 0, &share_count_sai6);
|
||||
clks[IMX8MQ_CLK_SAI6_IPG] = imx_clk_gate2_shared2("sai6_ipg_clk", "ipg_audio_root", base + 0x4380, 0, &share_count_sai6);
|
||||
clks[IMX8MQ_CLK_UART1_ROOT] = imx_clk_gate4("uart1_root_clk", "uart1", base + 0x4490, 0);
|
||||
clks[IMX8MQ_CLK_UART2_ROOT] = imx_clk_gate4("uart2_root_clk", "uart2", base + 0x44a0, 0);
|
||||
clks[IMX8MQ_CLK_UART3_ROOT] = imx_clk_gate4("uart3_root_clk", "uart3", base + 0x44b0, 0);
|
||||
clks[IMX8MQ_CLK_UART4_ROOT] = imx_clk_gate4("uart4_root_clk", "uart4", base + 0x44c0, 0);
|
||||
clks[IMX8MQ_CLK_USB1_CTRL_ROOT] = imx_clk_gate4("usb1_ctrl_root_clk", "usb_core_ref", base + 0x44d0, 0);
|
||||
clks[IMX8MQ_CLK_USB2_CTRL_ROOT] = imx_clk_gate4("usb2_ctrl_root_clk", "usb_core_ref", base + 0x44e0, 0);
|
||||
clks[IMX8MQ_CLK_USB1_PHY_ROOT] = imx_clk_gate4("usb1_phy_root_clk", "usb_phy_ref", base + 0x44f0, 0);
|
||||
clks[IMX8MQ_CLK_USB2_PHY_ROOT] = imx_clk_gate4("usb2_phy_root_clk", "usb_phy_ref", base + 0x4500, 0);
|
||||
clks[IMX8MQ_CLK_USDHC1_ROOT] = imx_clk_gate4("usdhc1_root_clk", "usdhc1", base + 0x4510, 0);
|
||||
clks[IMX8MQ_CLK_USDHC2_ROOT] = imx_clk_gate4("usdhc2_root_clk", "usdhc2", base + 0x4520, 0);
|
||||
clks[IMX8MQ_CLK_WDOG1_ROOT] = imx_clk_gate4("wdog1_root_clk", "wdog", base + 0x4530, 0);
|
||||
clks[IMX8MQ_CLK_WDOG2_ROOT] = imx_clk_gate4("wdog2_root_clk", "wdog", base + 0x4540, 0);
|
||||
clks[IMX8MQ_CLK_WDOG3_ROOT] = imx_clk_gate4("wdog3_root_clk", "wdog", base + 0x4550, 0);
|
||||
clks[IMX8MQ_CLK_VPU_G1_ROOT] = imx_clk_gate2_flags("vpu_g1_root_clk", "vpu_g1", base + 0x4560, 0, CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE);
|
||||
clks[IMX8MQ_CLK_GPU_ROOT] = imx_clk_gate4("gpu_root_clk", "gpu_core_div", base + 0x4570, 0);
|
||||
clks[IMX8MQ_CLK_VPU_G2_ROOT] = imx_clk_gate2_flags("vpu_g2_root_clk", "vpu_g2", base + 0x45a0, 0, CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE);
|
||||
clks[IMX8MQ_CLK_DISP_ROOT] = imx_clk_gate2_shared2("disp_root_clk", "disp_dc8000", base + 0x45d0, 0, &share_count_dcss);
|
||||
clks[IMX8MQ_CLK_DISP_AXI_ROOT] = imx_clk_gate2_shared2("disp_axi_root_clk", "disp_axi", base + 0x45d0, 0, &share_count_dcss);
|
||||
clks[IMX8MQ_CLK_DISP_APB_ROOT] = imx_clk_gate2_shared2("disp_apb_root_clk", "disp_apb", base + 0x45d0, 0, &share_count_dcss);
|
||||
clks[IMX8MQ_CLK_DISP_RTRM_ROOT] = imx_clk_gate2_shared2("disp_rtrm_root_clk", "disp_rtrm", base + 0x45d0, 0, &share_count_dcss);
|
||||
clks[IMX8MQ_CLK_TMU_ROOT] = imx_clk_gate4_flags("tmu_root_clk", "ipg_root", base + 0x4620, 0, CLK_IS_CRITICAL);
|
||||
clks[IMX8MQ_CLK_VPU_DEC_ROOT] = imx_clk_gate2_flags("vpu_dec_root_clk", "vpu_bus", base + 0x4630, 0, CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE);
|
||||
clks[IMX8MQ_CLK_CSI1_ROOT] = imx_clk_gate4("csi1_root_clk", "csi1_core", base + 0x4650, 0);
|
||||
clks[IMX8MQ_CLK_CSI2_ROOT] = imx_clk_gate4("csi2_root_clk", "csi2_core", base + 0x4660, 0);
|
||||
clks[IMX8MQ_CLK_SDMA1_ROOT] = imx_clk_gate4("sdma1_clk", "ipg_root", base + 0x43a0, 0);
|
||||
clks[IMX8MQ_CLK_SDMA2_ROOT] = imx_clk_gate4("sdma2_clk", "ipg_audio_root", base + 0x43b0, 0);
|
||||
|
||||
clks[IMX8MQ_GPT_3M_CLK] = imx_clk_fixed_factor("gpt_3m", "osc_25m", 1, 8);
|
||||
clks[IMX8MQ_CLK_DRAM_ALT_ROOT] = imx_clk_fixed_factor("dram_alt_root", "dram_alt", 1, 4);
|
||||
|
||||
for (i = 0; i < IMX8MQ_CLK_END; i++)
|
||||
if (IS_ERR(clks[i]))
|
||||
pr_err("i.MX8mq clk %u register failed with %ld\n",
|
||||
i, PTR_ERR(clks[i]));
|
||||
|
||||
clk_data.clks = clks;
|
||||
clk_data.clk_num = ARRAY_SIZE(clks);
|
||||
|
||||
err = of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
|
||||
WARN_ON(err);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct of_device_id imx8mq_clk_of_match[] = {
|
||||
{ .compatible = "fsl,imx8mq-ccm" },
|
||||
{ /* Sentinel */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, imx8mq_clk_of_match);
|
||||
|
||||
|
||||
static struct platform_driver imx8mq_clk_driver = {
|
||||
.probe = imx8mq_clocks_probe,
|
||||
.driver = {
|
||||
.name = "imx8mq-ccm",
|
||||
.of_match_table = of_match_ptr(imx8mq_clk_of_match),
|
||||
},
|
||||
};
|
||||
module_platform_driver(imx8mq_clk_driver);
|
||||
216
drivers/clk/imx/clk-imx8qxp-lpcg.c
Normal file
216
drivers/clk/imx/clk-imx8qxp-lpcg.c
Normal file
|
|
@ -0,0 +1,216 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2018 NXP
|
||||
* Dong Aisheng <aisheng.dong@nxp.com>
|
||||
*/
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "clk-scu.h"
|
||||
#include "clk-imx8qxp-lpcg.h"
|
||||
|
||||
#include <dt-bindings/clock/imx8qxp-clock.h>
|
||||
|
||||
/*
|
||||
* struct imx8qxp_lpcg_data - Description of one LPCG clock
|
||||
* @id: clock ID
|
||||
* @name: clock name
|
||||
* @parent: parent clock name
|
||||
* @flags: common clock flags
|
||||
* @offset: offset of this LPCG clock
|
||||
* @bit_idx: bit index of this LPCG clock
|
||||
* @hw_gate: whether supports HW autogate
|
||||
*
|
||||
* This structure describes one LPCG clock
|
||||
*/
|
||||
struct imx8qxp_lpcg_data {
|
||||
int id;
|
||||
char *name;
|
||||
char *parent;
|
||||
unsigned long flags;
|
||||
u32 offset;
|
||||
u8 bit_idx;
|
||||
bool hw_gate;
|
||||
};
|
||||
|
||||
/*
|
||||
* struct imx8qxp_ss_lpcg - Description of one subsystem LPCG clocks
|
||||
* @lpcg: LPCG clocks array of one subsystem
|
||||
* @num_lpcg: the number of LPCG clocks
|
||||
* @num_max: the maximum number of LPCG clocks
|
||||
*
|
||||
* This structure describes each subsystem LPCG clocks information
|
||||
* which then will be used to create respective LPCGs clocks
|
||||
*/
|
||||
struct imx8qxp_ss_lpcg {
|
||||
const struct imx8qxp_lpcg_data *lpcg;
|
||||
u8 num_lpcg;
|
||||
u8 num_max;
|
||||
};
|
||||
|
||||
static const struct imx8qxp_lpcg_data imx8qxp_lpcg_adma[] = {
|
||||
{ IMX8QXP_ADMA_LPCG_UART0_IPG_CLK, "uart0_lpcg_ipg_clk", "dma_ipg_clk_root", 0, ADMA_LPUART_0_LPCG, 16, 0, },
|
||||
{ IMX8QXP_ADMA_LPCG_UART0_BAUD_CLK, "uart0_lpcg_baud_clk", "uart0_clk", 0, ADMA_LPUART_0_LPCG, 0, 0, },
|
||||
{ IMX8QXP_ADMA_LPCG_UART1_IPG_CLK, "uart1_lpcg_ipg_clk", "dma_ipg_clk_root", 0, ADMA_LPUART_1_LPCG, 16, 0, },
|
||||
{ IMX8QXP_ADMA_LPCG_UART1_BAUD_CLK, "uart1_lpcg_baud_clk", "uart1_clk", 0, ADMA_LPUART_1_LPCG, 0, 0, },
|
||||
{ IMX8QXP_ADMA_LPCG_UART2_IPG_CLK, "uart2_lpcg_ipg_clk", "dma_ipg_clk_root", 0, ADMA_LPUART_2_LPCG, 16, 0, },
|
||||
{ IMX8QXP_ADMA_LPCG_UART2_BAUD_CLK, "uart2_lpcg_baud_clk", "uart2_clk", 0, ADMA_LPUART_2_LPCG, 0, 0, },
|
||||
{ IMX8QXP_ADMA_LPCG_UART3_IPG_CLK, "uart3_lpcg_ipg_clk", "dma_ipg_clk_root", 0, ADMA_LPUART_3_LPCG, 16, 0, },
|
||||
{ IMX8QXP_ADMA_LPCG_UART3_BAUD_CLK, "uart3_lpcg_baud_clk", "uart3_clk", 0, ADMA_LPUART_3_LPCG, 0, 0, },
|
||||
{ IMX8QXP_ADMA_LPCG_I2C0_IPG_CLK, "i2c0_lpcg_ipg_clk", "dma_ipg_clk_root", 0, ADMA_LPI2C_0_LPCG, 16, 0, },
|
||||
{ IMX8QXP_ADMA_LPCG_I2C0_CLK, "i2c0_lpcg_clk", "i2c0_clk", 0, ADMA_LPI2C_0_LPCG, 0, 0, },
|
||||
{ IMX8QXP_ADMA_LPCG_I2C1_IPG_CLK, "i2c1_lpcg_ipg_clk", "dma_ipg_clk_root", 0, ADMA_LPI2C_1_LPCG, 16, 0, },
|
||||
{ IMX8QXP_ADMA_LPCG_I2C1_CLK, "i2c1_lpcg_clk", "i2c1_clk", 0, ADMA_LPI2C_1_LPCG, 0, 0, },
|
||||
{ IMX8QXP_ADMA_LPCG_I2C2_IPG_CLK, "i2c2_lpcg_ipg_clk", "dma_ipg_clk_root", 0, ADMA_LPI2C_2_LPCG, 16, 0, },
|
||||
{ IMX8QXP_ADMA_LPCG_I2C2_CLK, "i2c2_lpcg_clk", "i2c2_clk", 0, ADMA_LPI2C_2_LPCG, 0, 0, },
|
||||
{ IMX8QXP_ADMA_LPCG_I2C3_IPG_CLK, "i2c3_lpcg_ipg_clk", "dma_ipg_clk_root", 0, ADMA_LPI2C_3_LPCG, 16, 0, },
|
||||
{ IMX8QXP_ADMA_LPCG_I2C3_CLK, "i2c3_lpcg_clk", "i2c3_clk", 0, ADMA_LPI2C_3_LPCG, 0, 0, },
|
||||
};
|
||||
|
||||
static const struct imx8qxp_ss_lpcg imx8qxp_ss_adma = {
|
||||
.lpcg = imx8qxp_lpcg_adma,
|
||||
.num_lpcg = ARRAY_SIZE(imx8qxp_lpcg_adma),
|
||||
.num_max = IMX8QXP_ADMA_LPCG_CLK_END,
|
||||
};
|
||||
|
||||
static const struct imx8qxp_lpcg_data imx8qxp_lpcg_conn[] = {
|
||||
{ IMX8QXP_CONN_LPCG_SDHC0_PER_CLK, "sdhc0_lpcg_per_clk", "sdhc0_clk", 0, CONN_USDHC_0_LPCG, 0, 0, },
|
||||
{ IMX8QXP_CONN_LPCG_SDHC0_IPG_CLK, "sdhc0_lpcg_ipg_clk", "conn_ipg_clk_root", 0, CONN_USDHC_0_LPCG, 16, 0, },
|
||||
{ IMX8QXP_CONN_LPCG_SDHC0_HCLK, "sdhc0_lpcg_ahb_clk", "conn_axi_clk_root", 0, CONN_USDHC_0_LPCG, 20, 0, },
|
||||
{ IMX8QXP_CONN_LPCG_SDHC1_PER_CLK, "sdhc1_lpcg_per_clk", "sdhc1_clk", 0, CONN_USDHC_1_LPCG, 0, 0, },
|
||||
{ IMX8QXP_CONN_LPCG_SDHC1_IPG_CLK, "sdhc1_lpcg_ipg_clk", "conn_ipg_clk_root", 0, CONN_USDHC_1_LPCG, 16, 0, },
|
||||
{ IMX8QXP_CONN_LPCG_SDHC1_HCLK, "sdhc1_lpcg_ahb_clk", "conn_axi_clk_root", 0, CONN_USDHC_1_LPCG, 20, 0, },
|
||||
{ IMX8QXP_CONN_LPCG_SDHC2_PER_CLK, "sdhc2_lpcg_per_clk", "sdhc2_clk", 0, CONN_USDHC_2_LPCG, 0, 0, },
|
||||
{ IMX8QXP_CONN_LPCG_SDHC2_IPG_CLK, "sdhc2_lpcg_ipg_clk", "conn_ipg_clk_root", 0, CONN_USDHC_2_LPCG, 16, 0, },
|
||||
{ IMX8QXP_CONN_LPCG_SDHC2_HCLK, "sdhc2_lpcg_ahb_clk", "conn_axi_clk_root", 0, CONN_USDHC_2_LPCG, 20, 0, },
|
||||
{ IMX8QXP_CONN_LPCG_ENET0_ROOT_CLK, "enet0_ipg_root_clk", "enet0_clk", 0, CONN_ENET_0_LPCG, 0, 0, },
|
||||
{ IMX8QXP_CONN_LPCG_ENET0_TX_CLK, "enet0_tx_clk", "enet0_clk", 0, CONN_ENET_0_LPCG, 4, 0, },
|
||||
{ IMX8QXP_CONN_LPCG_ENET0_AHB_CLK, "enet0_ahb_clk", "conn_axi_clk_root", 0, CONN_ENET_0_LPCG, 8, 0, },
|
||||
{ IMX8QXP_CONN_LPCG_ENET0_IPG_S_CLK, "enet0_ipg_s_clk", "conn_ipg_clk_root", 0, CONN_ENET_0_LPCG, 20, 0, },
|
||||
{ IMX8QXP_CONN_LPCG_ENET0_IPG_CLK, "enet0_ipg_clk", "enet0_ipg_s_clk", 0, CONN_ENET_0_LPCG, 16, 0, },
|
||||
{ IMX8QXP_CONN_LPCG_ENET1_ROOT_CLK, "enet1_ipg_root_clk", "enet1_clk", 0, CONN_ENET_1_LPCG, 0, 0, },
|
||||
{ IMX8QXP_CONN_LPCG_ENET1_TX_CLK, "enet1_tx_clk", "enet1_clk", 0, CONN_ENET_1_LPCG, 4, 0, },
|
||||
{ IMX8QXP_CONN_LPCG_ENET1_AHB_CLK, "enet1_ahb_clk", "conn_axi_clk_root", 0, CONN_ENET_1_LPCG, 8, 0, },
|
||||
{ IMX8QXP_CONN_LPCG_ENET1_IPG_S_CLK, "enet1_ipg_s_clk", "conn_ipg_clk_root", 0, CONN_ENET_1_LPCG, 20, 0, },
|
||||
{ IMX8QXP_CONN_LPCG_ENET1_IPG_CLK, "enet1_ipg_clk", "enet0_ipg_s_clk", 0, CONN_ENET_1_LPCG, 16, 0, },
|
||||
};
|
||||
|
||||
static const struct imx8qxp_ss_lpcg imx8qxp_ss_conn = {
|
||||
.lpcg = imx8qxp_lpcg_conn,
|
||||
.num_lpcg = ARRAY_SIZE(imx8qxp_lpcg_conn),
|
||||
.num_max = IMX8QXP_CONN_LPCG_CLK_END,
|
||||
};
|
||||
|
||||
static const struct imx8qxp_lpcg_data imx8qxp_lpcg_lsio[] = {
|
||||
{ IMX8QXP_LSIO_LPCG_PWM0_IPG_CLK, "pwm0_lpcg_ipg_clk", "pwm0_clk", 0, LSIO_PWM_0_LPCG, 0, 0, },
|
||||
{ IMX8QXP_LSIO_LPCG_PWM0_IPG_HF_CLK, "pwm0_lpcg_ipg_hf_clk", "pwm0_clk", 0, LSIO_PWM_0_LPCG, 4, 0, },
|
||||
{ IMX8QXP_LSIO_LPCG_PWM0_IPG_S_CLK, "pwm0_lpcg_ipg_s_clk", "pwm0_clk", 0, LSIO_PWM_0_LPCG, 16, 0, },
|
||||
{ IMX8QXP_LSIO_LPCG_PWM0_IPG_SLV_CLK, "pwm0_lpcg_ipg_slv_clk", "lsio_bus_clk_root", 0, LSIO_PWM_0_LPCG, 20, 0, },
|
||||
{ IMX8QXP_LSIO_LPCG_PWM0_IPG_MSTR_CLK, "pwm0_lpcg_ipg_mstr_clk", "pwm0_clk", 0, LSIO_PWM_0_LPCG, 24, 0, },
|
||||
{ IMX8QXP_LSIO_LPCG_PWM1_IPG_CLK, "pwm1_lpcg_ipg_clk", "pwm1_clk", 0, LSIO_PWM_1_LPCG, 0, 0, },
|
||||
{ IMX8QXP_LSIO_LPCG_PWM1_IPG_HF_CLK, "pwm1_lpcg_ipg_hf_clk", "pwm1_clk", 0, LSIO_PWM_1_LPCG, 4, 0, },
|
||||
{ IMX8QXP_LSIO_LPCG_PWM1_IPG_S_CLK, "pwm1_lpcg_ipg_s_clk", "pwm1_clk", 0, LSIO_PWM_1_LPCG, 16, 0, },
|
||||
{ IMX8QXP_LSIO_LPCG_PWM1_IPG_SLV_CLK, "pwm1_lpcg_ipg_slv_clk", "lsio_bus_clk_root", 0, LSIO_PWM_1_LPCG, 20, 0, },
|
||||
{ IMX8QXP_LSIO_LPCG_PWM1_IPG_MSTR_CLK, "pwm1_lpcg_ipg_mstr_clk", "pwm1_clk", 0, LSIO_PWM_1_LPCG, 24, 0, },
|
||||
{ IMX8QXP_LSIO_LPCG_PWM2_IPG_CLK, "pwm2_lpcg_ipg_clk", "pwm2_clk", 0, LSIO_PWM_2_LPCG, 0, 0, },
|
||||
{ IMX8QXP_LSIO_LPCG_PWM2_IPG_HF_CLK, "pwm2_lpcg_ipg_hf_clk", "pwm2_clk", 0, LSIO_PWM_2_LPCG, 4, 0, },
|
||||
{ IMX8QXP_LSIO_LPCG_PWM2_IPG_S_CLK, "pwm2_lpcg_ipg_s_clk", "pwm2_clk", 0, LSIO_PWM_2_LPCG, 16, 0, },
|
||||
{ IMX8QXP_LSIO_LPCG_PWM2_IPG_SLV_CLK, "pwm2_lpcg_ipg_slv_clk", "lsio_bus_clk_root", 0, LSIO_PWM_2_LPCG, 20, 0, },
|
||||
{ IMX8QXP_LSIO_LPCG_PWM2_IPG_MSTR_CLK, "pwm2_lpcg_ipg_mstr_clk", "pwm2_clk", 0, LSIO_PWM_2_LPCG, 24, 0, },
|
||||
{ IMX8QXP_LSIO_LPCG_PWM3_IPG_CLK, "pwm3_lpcg_ipg_clk", "pwm3_clk", 0, LSIO_PWM_3_LPCG, 0, 0, },
|
||||
{ IMX8QXP_LSIO_LPCG_PWM3_IPG_HF_CLK, "pwm3_lpcg_ipg_hf_clk", "pwm3_clk", 0, LSIO_PWM_3_LPCG, 4, 0, },
|
||||
{ IMX8QXP_LSIO_LPCG_PWM3_IPG_S_CLK, "pwm3_lpcg_ipg_s_clk", "pwm3_clk", 0, LSIO_PWM_3_LPCG, 16, 0, },
|
||||
{ IMX8QXP_LSIO_LPCG_PWM3_IPG_SLV_CLK, "pwm3_lpcg_ipg_slv_clk", "lsio_bus_clk_root", 0, LSIO_PWM_3_LPCG, 20, 0, },
|
||||
{ IMX8QXP_LSIO_LPCG_PWM3_IPG_MSTR_CLK, "pwm3_lpcg_ipg_mstr_clk", "pwm3_clk", 0, LSIO_PWM_3_LPCG, 24, 0, },
|
||||
{ IMX8QXP_LSIO_LPCG_PWM4_IPG_CLK, "pwm4_lpcg_ipg_clk", "pwm4_clk", 0, LSIO_PWM_4_LPCG, 0, 0, },
|
||||
{ IMX8QXP_LSIO_LPCG_PWM4_IPG_HF_CLK, "pwm4_lpcg_ipg_hf_clk", "pwm4_clk", 0, LSIO_PWM_4_LPCG, 4, 0, },
|
||||
{ IMX8QXP_LSIO_LPCG_PWM4_IPG_S_CLK, "pwm4_lpcg_ipg_s_clk", "pwm4_clk", 0, LSIO_PWM_4_LPCG, 16, 0, },
|
||||
{ IMX8QXP_LSIO_LPCG_PWM4_IPG_SLV_CLK, "pwm4_lpcg_ipg_slv_clk", "lsio_bus_clk_root", 0, LSIO_PWM_4_LPCG, 20, 0, },
|
||||
{ IMX8QXP_LSIO_LPCG_PWM4_IPG_MSTR_CLK, "pwm4_lpcg_ipg_mstr_clk", "pwm4_clk", 0, LSIO_PWM_4_LPCG, 24, 0, },
|
||||
{ IMX8QXP_LSIO_LPCG_PWM5_IPG_CLK, "pwm5_lpcg_ipg_clk", "pwm5_clk", 0, LSIO_PWM_5_LPCG, 0, 0, },
|
||||
{ IMX8QXP_LSIO_LPCG_PWM5_IPG_HF_CLK, "pwm5_lpcg_ipg_hf_clk", "pwm5_clk", 0, LSIO_PWM_5_LPCG, 4, 0, },
|
||||
{ IMX8QXP_LSIO_LPCG_PWM5_IPG_S_CLK, "pwm5_lpcg_ipg_s_clk", "pwm5_clk", 0, LSIO_PWM_5_LPCG, 16, 0, },
|
||||
{ IMX8QXP_LSIO_LPCG_PWM5_IPG_SLV_CLK, "pwm5_lpcg_ipg_slv_clk", "lsio_bus_clk_root", 0, LSIO_PWM_5_LPCG, 20, 0, },
|
||||
{ IMX8QXP_LSIO_LPCG_PWM5_IPG_MSTR_CLK, "pwm5_lpcg_ipg_mstr_clk", "pwm5_clk", 0, LSIO_PWM_5_LPCG, 24, 0, },
|
||||
{ IMX8QXP_LSIO_LPCG_PWM6_IPG_CLK, "pwm6_lpcg_ipg_clk", "pwm6_clk", 0, LSIO_PWM_6_LPCG, 0, 0, },
|
||||
{ IMX8QXP_LSIO_LPCG_PWM6_IPG_HF_CLK, "pwm6_lpcg_ipg_hf_clk", "pwm6_clk", 0, LSIO_PWM_6_LPCG, 4, 0, },
|
||||
{ IMX8QXP_LSIO_LPCG_PWM6_IPG_S_CLK, "pwm6_lpcg_ipg_s_clk", "pwm6_clk", 0, LSIO_PWM_6_LPCG, 16, 0, },
|
||||
{ IMX8QXP_LSIO_LPCG_PWM6_IPG_SLV_CLK, "pwm6_lpcg_ipg_slv_clk", "lsio_bus_clk_root", 0, LSIO_PWM_6_LPCG, 20, 0, },
|
||||
{ IMX8QXP_LSIO_LPCG_PWM6_IPG_MSTR_CLK, "pwm6_lpcg_ipg_mstr_clk", "pwm6_clk", 0, LSIO_PWM_6_LPCG, 24, 0, },
|
||||
};
|
||||
|
||||
static const struct imx8qxp_ss_lpcg imx8qxp_ss_lsio = {
|
||||
.lpcg = imx8qxp_lpcg_lsio,
|
||||
.num_lpcg = ARRAY_SIZE(imx8qxp_lpcg_lsio),
|
||||
.num_max = IMX8QXP_LSIO_LPCG_CLK_END,
|
||||
};
|
||||
|
||||
static int imx8qxp_lpcg_clk_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct clk_hw_onecell_data *clk_data;
|
||||
const struct imx8qxp_ss_lpcg *ss_lpcg;
|
||||
const struct imx8qxp_lpcg_data *lpcg;
|
||||
struct resource *res;
|
||||
struct clk_hw **clks;
|
||||
void __iomem *base;
|
||||
int i;
|
||||
|
||||
ss_lpcg = of_device_get_match_data(dev);
|
||||
if (!ss_lpcg)
|
||||
return -ENODEV;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
base = devm_ioremap(dev, res->start, resource_size(res));
|
||||
if (!base)
|
||||
return -ENOMEM;
|
||||
|
||||
clk_data = devm_kzalloc(&pdev->dev, struct_size(clk_data, hws,
|
||||
ss_lpcg->num_max), GFP_KERNEL);
|
||||
if (!clk_data)
|
||||
return -ENOMEM;
|
||||
|
||||
clk_data->num = ss_lpcg->num_max;
|
||||
clks = clk_data->hws;
|
||||
|
||||
for (i = 0; i < ss_lpcg->num_lpcg; i++) {
|
||||
lpcg = ss_lpcg->lpcg + i;
|
||||
clks[lpcg->id] = imx_clk_lpcg_scu(lpcg->name, lpcg->parent,
|
||||
lpcg->flags, base + lpcg->offset,
|
||||
lpcg->bit_idx, lpcg->hw_gate);
|
||||
}
|
||||
|
||||
for (i = 0; i < clk_data->num; i++) {
|
||||
if (IS_ERR(clks[i]))
|
||||
pr_warn("i.MX clk %u: register failed with %ld\n",
|
||||
i, PTR_ERR(clks[i]));
|
||||
}
|
||||
|
||||
return of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_data);
|
||||
}
|
||||
|
||||
static const struct of_device_id imx8qxp_lpcg_match[] = {
|
||||
{ .compatible = "fsl,imx8qxp-lpcg-adma", &imx8qxp_ss_adma, },
|
||||
{ .compatible = "fsl,imx8qxp-lpcg-conn", &imx8qxp_ss_conn, },
|
||||
{ .compatible = "fsl,imx8qxp-lpcg-lsio", &imx8qxp_ss_lsio, },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
static struct platform_driver imx8qxp_lpcg_clk_driver = {
|
||||
.driver = {
|
||||
.name = "imx8qxp-lpcg-clk",
|
||||
.of_match_table = imx8qxp_lpcg_match,
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
.probe = imx8qxp_lpcg_clk_probe,
|
||||
};
|
||||
|
||||
builtin_platform_driver(imx8qxp_lpcg_clk_driver);
|
||||
102
drivers/clk/imx/clk-imx8qxp-lpcg.h
Normal file
102
drivers/clk/imx/clk-imx8qxp-lpcg.h
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Copyright 2018 NXP
|
||||
* Dong Aisheng <aisheng.dong@nxp.com>
|
||||
*/
|
||||
|
||||
#ifndef _IMX8QXP_LPCG_H
|
||||
#define _IMX8QXP_LPCG_H
|
||||
|
||||
/*LSIO SS */
|
||||
#define LSIO_PWM_0_LPCG 0x00000
|
||||
#define LSIO_PWM_1_LPCG 0x10000
|
||||
#define LSIO_PWM_2_LPCG 0x20000
|
||||
#define LSIO_PWM_3_LPCG 0x30000
|
||||
#define LSIO_PWM_4_LPCG 0x40000
|
||||
#define LSIO_PWM_5_LPCG 0x50000
|
||||
#define LSIO_PWM_6_LPCG 0x60000
|
||||
#define LSIO_PWM_7_LPCG 0x70000
|
||||
#define LSIO_GPIO_0_LPCG 0x80000
|
||||
#define LSIO_GPIO_1_LPCG 0x90000
|
||||
#define LSIO_GPIO_2_LPCG 0xa0000
|
||||
#define LSIO_GPIO_3_LPCG 0xb0000
|
||||
#define LSIO_GPIO_4_LPCG 0xc0000
|
||||
#define LSIO_GPIO_5_LPCG 0xd0000
|
||||
#define LSIO_GPIO_6_LPCG 0xe0000
|
||||
#define LSIO_GPIO_7_LPCG 0xf0000
|
||||
#define LSIO_FSPI_0_LPCG 0x120000
|
||||
#define LSIO_FSPI_1_LPCG 0x130000
|
||||
#define LSIO_GPT_0_LPCG 0x140000
|
||||
#define LSIO_GPT_1_LPCG 0x150000
|
||||
#define LSIO_GPT_2_LPCG 0x160000
|
||||
#define LSIO_GPT_3_LPCG 0x170000
|
||||
#define LSIO_GPT_4_LPCG 0x180000
|
||||
#define LSIO_OCRAM_LPCG 0x190000
|
||||
#define LSIO_KPP_LPCG 0x1a0000
|
||||
#define LSIO_ROMCP_LPCG 0x100000
|
||||
|
||||
/* Connectivity SS */
|
||||
#define CONN_USDHC_0_LPCG 0x00000
|
||||
#define CONN_USDHC_1_LPCG 0x10000
|
||||
#define CONN_USDHC_2_LPCG 0x20000
|
||||
#define CONN_ENET_0_LPCG 0x30000
|
||||
#define CONN_ENET_1_LPCG 0x40000
|
||||
#define CONN_DTCP_LPCG 0x50000
|
||||
#define CONN_MLB_LPCG 0x60000
|
||||
#define CONN_USB_2_LPCG 0x70000
|
||||
#define CONN_USB_3_LPCG 0x80000
|
||||
#define CONN_NAND_LPCG 0x90000
|
||||
#define CONN_EDMA_LPCG 0xa0000
|
||||
|
||||
/* ADMA SS */
|
||||
#define ADMA_ASRC_0_LPCG 0x400000
|
||||
#define ADMA_ESAI_0_LPCG 0x410000
|
||||
#define ADMA_SPDIF_0_LPCG 0x420000
|
||||
#define ADMA_SAI_0_LPCG 0x440000
|
||||
#define ADMA_SAI_1_LPCG 0x450000
|
||||
#define ADMA_SAI_2_LPCG 0x460000
|
||||
#define ADMA_SAI_3_LPCG 0x470000
|
||||
#define ADMA_GPT_5_LPCG 0x4b0000
|
||||
#define ADMA_GPT_6_LPCG 0x4c0000
|
||||
#define ADMA_GPT_7_LPCG 0x4d0000
|
||||
#define ADMA_GPT_8_LPCG 0x4e0000
|
||||
#define ADMA_GPT_9_LPCG 0x4f0000
|
||||
#define ADMA_GPT_10_LPCG 0x500000
|
||||
#define ADMA_HIFI_LPCG 0x580000
|
||||
#define ADMA_OCRAM_LPCG 0x590000
|
||||
#define ADMA_EDMA_0_LPCG 0x5f0000
|
||||
#define ADMA_ASRC_1_LPCG 0xc00000
|
||||
#define ADMA_SAI_4_LPCG 0xc20000
|
||||
#define ADMA_SAI_5_LPCG 0xc30000
|
||||
#define ADMA_AMIX_LPCG 0xc40000
|
||||
#define ADMA_MQS_LPCG 0xc50000
|
||||
#define ADMA_ACM_LPCG 0xc60000
|
||||
#define ADMA_REC_CLK0_LPCG 0xd00000
|
||||
#define ADMA_REC_CLK1_LPCG 0xd10000
|
||||
#define ADMA_PLL_CLK0_LPCG 0xd20000
|
||||
#define ADMA_PLL_CLK1_LPCG 0xd30000
|
||||
#define ADMA_MCLKOUT0_LPCG 0xd50000
|
||||
#define ADMA_MCLKOUT1_LPCG 0xd60000
|
||||
#define ADMA_EDMA_1_LPCG 0xdf0000
|
||||
#define ADMA_LPSPI_0_LPCG 0x1400000
|
||||
#define ADMA_LPSPI_1_LPCG 0x1410000
|
||||
#define ADMA_LPSPI_2_LPCG 0x1420000
|
||||
#define ADMA_LPSPI_3_LPCG 0x1430000
|
||||
#define ADMA_LPUART_0_LPCG 0x1460000
|
||||
#define ADMA_LPUART_1_LPCG 0x1470000
|
||||
#define ADMA_LPUART_2_LPCG 0x1480000
|
||||
#define ADMA_LPUART_3_LPCG 0x1490000
|
||||
#define ADMA_LCD_LPCG 0x1580000
|
||||
#define ADMA_PWM_LPCG 0x1590000
|
||||
#define ADMA_LPI2C_0_LPCG 0x1c00000
|
||||
#define ADMA_LPI2C_1_LPCG 0x1c10000
|
||||
#define ADMA_LPI2C_2_LPCG 0x1c20000
|
||||
#define ADMA_LPI2C_3_LPCG 0x1c30000
|
||||
#define ADMA_ADC_0_LPCG 0x1c80000
|
||||
#define ADMA_FTM_0_LPCG 0x1ca0000
|
||||
#define ADMA_FTM_1_LPCG 0x1cb0000
|
||||
#define ADMA_FLEXCAN_0_LPCG 0x1cd0000
|
||||
#define ADMA_FLEXCAN_1_LPCG 0x1ce0000
|
||||
#define ADMA_FLEXCAN_2_LPCG 0x1cf0000
|
||||
|
||||
#endif /* _IMX8QXP_LPCG_H */
|
||||
153
drivers/clk/imx/clk-imx8qxp.c
Normal file
153
drivers/clk/imx/clk-imx8qxp.c
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2018 NXP
|
||||
* Dong Aisheng <aisheng.dong@nxp.com>
|
||||
*/
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "clk-scu.h"
|
||||
|
||||
#include <dt-bindings/clock/imx8qxp-clock.h>
|
||||
#include <dt-bindings/firmware/imx/rsrc.h>
|
||||
|
||||
static int imx8qxp_clk_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *ccm_node = pdev->dev.of_node;
|
||||
struct clk_hw_onecell_data *clk_data;
|
||||
struct clk_hw **clks;
|
||||
int ret, i;
|
||||
|
||||
ret = imx_clk_scu_init();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
clk_data = devm_kzalloc(&pdev->dev, struct_size(clk_data, hws,
|
||||
IMX8QXP_SCU_CLK_END), GFP_KERNEL);
|
||||
if (!clk_data)
|
||||
return -ENOMEM;
|
||||
|
||||
clk_data->num = IMX8QXP_SCU_CLK_END;
|
||||
clks = clk_data->hws;
|
||||
|
||||
/* Fixed clocks */
|
||||
clks[IMX8QXP_CLK_DUMMY] = clk_hw_register_fixed_rate(NULL, "dummy", NULL, 0, 0);
|
||||
clks[IMX8QXP_ADMA_IPG_CLK_ROOT] = clk_hw_register_fixed_rate(NULL, "dma_ipg_clk_root", NULL, 0, 120000000);
|
||||
clks[IMX8QXP_CONN_AXI_CLK_ROOT] = clk_hw_register_fixed_rate(NULL, "conn_axi_clk_root", NULL, 0, 333333333);
|
||||
clks[IMX8QXP_CONN_AHB_CLK_ROOT] = clk_hw_register_fixed_rate(NULL, "conn_ahb_clk_root", NULL, 0, 166666666);
|
||||
clks[IMX8QXP_CONN_IPG_CLK_ROOT] = clk_hw_register_fixed_rate(NULL, "conn_ipg_clk_root", NULL, 0, 83333333);
|
||||
clks[IMX8QXP_DC_AXI_EXT_CLK] = clk_hw_register_fixed_rate(NULL, "dc_axi_ext_clk_root", NULL, 0, 800000000);
|
||||
clks[IMX8QXP_DC_AXI_INT_CLK] = clk_hw_register_fixed_rate(NULL, "dc_axi_int_clk_root", NULL, 0, 400000000);
|
||||
clks[IMX8QXP_DC_CFG_CLK] = clk_hw_register_fixed_rate(NULL, "dc_cfg_clk_root", NULL, 0, 100000000);
|
||||
clks[IMX8QXP_MIPI_IPG_CLK] = clk_hw_register_fixed_rate(NULL, "mipi_ipg_clk_root", NULL, 0, 120000000);
|
||||
clks[IMX8QXP_IMG_AXI_CLK] = clk_hw_register_fixed_rate(NULL, "img_axi_clk_root", NULL, 0, 400000000);
|
||||
clks[IMX8QXP_IMG_IPG_CLK] = clk_hw_register_fixed_rate(NULL, "img_ipg_clk_root", NULL, 0, 200000000);
|
||||
clks[IMX8QXP_IMG_PXL_CLK] = clk_hw_register_fixed_rate(NULL, "img_pxl_clk_root", NULL, 0, 600000000);
|
||||
clks[IMX8QXP_HSIO_AXI_CLK] = clk_hw_register_fixed_rate(NULL, "hsio_axi_clk_root", NULL, 0, 400000000);
|
||||
clks[IMX8QXP_HSIO_PER_CLK] = clk_hw_register_fixed_rate(NULL, "hsio_per_clk_root", NULL, 0, 133333333);
|
||||
clks[IMX8QXP_LSIO_MEM_CLK] = clk_hw_register_fixed_rate(NULL, "lsio_mem_clk_root", NULL, 0, 200000000);
|
||||
clks[IMX8QXP_LSIO_BUS_CLK] = clk_hw_register_fixed_rate(NULL, "lsio_bus_clk_root", NULL, 0, 100000000);
|
||||
|
||||
/* ARM core */
|
||||
clks[IMX8QXP_A35_CLK] = imx_clk_scu("a35_clk", IMX_SC_R_A35, IMX_SC_PM_CLK_CPU);
|
||||
|
||||
/* LSIO SS */
|
||||
clks[IMX8QXP_LSIO_PWM0_CLK] = imx_clk_scu("pwm0_clk", IMX_SC_R_PWM_0, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX8QXP_LSIO_PWM1_CLK] = imx_clk_scu("pwm1_clk", IMX_SC_R_PWM_1, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX8QXP_LSIO_PWM2_CLK] = imx_clk_scu("pwm2_clk", IMX_SC_R_PWM_2, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX8QXP_LSIO_PWM3_CLK] = imx_clk_scu("pwm3_clk", IMX_SC_R_PWM_3, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX8QXP_LSIO_PWM4_CLK] = imx_clk_scu("pwm4_clk", IMX_SC_R_PWM_4, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX8QXP_LSIO_PWM5_CLK] = imx_clk_scu("pwm5_clk", IMX_SC_R_PWM_5, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX8QXP_LSIO_PWM6_CLK] = imx_clk_scu("pwm6_clk", IMX_SC_R_PWM_6, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX8QXP_LSIO_PWM7_CLK] = imx_clk_scu("pwm7_clk", IMX_SC_R_PWM_7, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX8QXP_LSIO_GPT0_CLK] = imx_clk_scu("gpt0_clk", IMX_SC_R_GPT_0, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX8QXP_LSIO_GPT1_CLK] = imx_clk_scu("gpt1_clk", IMX_SC_R_GPT_1, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX8QXP_LSIO_GPT2_CLK] = imx_clk_scu("gpt2_clk", IMX_SC_R_GPT_2, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX8QXP_LSIO_GPT3_CLK] = imx_clk_scu("gpt3_clk", IMX_SC_R_GPT_3, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX8QXP_LSIO_GPT4_CLK] = imx_clk_scu("gpt4_clk", IMX_SC_R_GPT_4, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX8QXP_LSIO_FSPI0_CLK] = imx_clk_scu("fspi0_clk", IMX_SC_R_FSPI_0, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX8QXP_LSIO_FSPI1_CLK] = imx_clk_scu("fspi1_clk", IMX_SC_R_FSPI_1, IMX_SC_PM_CLK_PER);
|
||||
|
||||
/* ADMA SS */
|
||||
clks[IMX8QXP_ADMA_UART0_CLK] = imx_clk_scu("uart0_clk", IMX_SC_R_UART_0, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX8QXP_ADMA_UART1_CLK] = imx_clk_scu("uart1_clk", IMX_SC_R_UART_1, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX8QXP_ADMA_UART2_CLK] = imx_clk_scu("uart2_clk", IMX_SC_R_UART_2, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX8QXP_ADMA_UART3_CLK] = imx_clk_scu("uart3_clk", IMX_SC_R_UART_3, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX8QXP_ADMA_SPI0_CLK] = imx_clk_scu("spi0_clk", IMX_SC_R_SPI_0, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX8QXP_ADMA_SPI1_CLK] = imx_clk_scu("spi1_clk", IMX_SC_R_SPI_1, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX8QXP_ADMA_SPI2_CLK] = imx_clk_scu("spi2_clk", IMX_SC_R_SPI_2, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX8QXP_ADMA_SPI3_CLK] = imx_clk_scu("spi3_clk", IMX_SC_R_SPI_3, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX8QXP_ADMA_CAN0_CLK] = imx_clk_scu("can0_clk", IMX_SC_R_CAN_0, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX8QXP_ADMA_I2C0_CLK] = imx_clk_scu("i2c0_clk", IMX_SC_R_I2C_0, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX8QXP_ADMA_I2C1_CLK] = imx_clk_scu("i2c1_clk", IMX_SC_R_I2C_1, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX8QXP_ADMA_I2C2_CLK] = imx_clk_scu("i2c2_clk", IMX_SC_R_I2C_2, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX8QXP_ADMA_I2C3_CLK] = imx_clk_scu("i2c3_clk", IMX_SC_R_I2C_3, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX8QXP_ADMA_FTM0_CLK] = imx_clk_scu("ftm0_clk", IMX_SC_R_FTM_0, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX8QXP_ADMA_FTM1_CLK] = imx_clk_scu("ftm1_clk", IMX_SC_R_FTM_1, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX8QXP_ADMA_ADC0_CLK] = imx_clk_scu("adc0_clk", IMX_SC_R_ADC_0, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX8QXP_ADMA_PWM_CLK] = imx_clk_scu("pwm_clk", IMX_SC_R_LCD_0_PWM_0, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX8QXP_ADMA_LCD_CLK] = imx_clk_scu("lcd_clk", IMX_SC_R_LCD_0, IMX_SC_PM_CLK_PER);
|
||||
|
||||
/* Connectivity */
|
||||
clks[IMX8QXP_CONN_SDHC0_CLK] = imx_clk_scu("sdhc0_clk", IMX_SC_R_SDHC_0, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX8QXP_CONN_SDHC1_CLK] = imx_clk_scu("sdhc1_clk", IMX_SC_R_SDHC_1, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX8QXP_CONN_SDHC2_CLK] = imx_clk_scu("sdhc2_clk", IMX_SC_R_SDHC_2, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX8QXP_CONN_ENET0_ROOT_CLK] = imx_clk_scu("enet0_clk", IMX_SC_R_ENET_0, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX8QXP_CONN_ENET0_BYPASS_CLK] = imx_clk_scu("enet0_bypass_clk", IMX_SC_R_ENET_0, IMX_SC_PM_CLK_BYPASS);
|
||||
clks[IMX8QXP_CONN_ENET0_RGMII_CLK] = imx_clk_scu("enet0_rgmii_clk", IMX_SC_R_ENET_0, IMX_SC_PM_CLK_MISC0);
|
||||
clks[IMX8QXP_CONN_ENET1_ROOT_CLK] = imx_clk_scu("enet1_clk", IMX_SC_R_ENET_1, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX8QXP_CONN_ENET1_BYPASS_CLK] = imx_clk_scu("enet1_bypass_clk", IMX_SC_R_ENET_1, IMX_SC_PM_CLK_BYPASS);
|
||||
clks[IMX8QXP_CONN_ENET1_RGMII_CLK] = imx_clk_scu("enet1_rgmii_clk", IMX_SC_R_ENET_1, IMX_SC_PM_CLK_MISC0);
|
||||
clks[IMX8QXP_CONN_GPMI_BCH_IO_CLK] = imx_clk_scu("gpmi_io_clk", IMX_SC_R_NAND, IMX_SC_PM_CLK_MST_BUS);
|
||||
clks[IMX8QXP_CONN_GPMI_BCH_CLK] = imx_clk_scu("gpmi_bch_clk", IMX_SC_R_NAND, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX8QXP_CONN_USB2_ACLK] = imx_clk_scu("usb3_aclk_div", IMX_SC_R_USB_2, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX8QXP_CONN_USB2_BUS_CLK] = imx_clk_scu("usb3_bus_div", IMX_SC_R_USB_2, IMX_SC_PM_CLK_MST_BUS);
|
||||
clks[IMX8QXP_CONN_USB2_LPM_CLK] = imx_clk_scu("usb3_lpm_div", IMX_SC_R_USB_2, IMX_SC_PM_CLK_MISC);
|
||||
|
||||
/* Display controller SS */
|
||||
clks[IMX8QXP_DC0_DISP0_CLK] = imx_clk_scu("dc0_disp0_clk", IMX_SC_R_DC_0, IMX_SC_PM_CLK_MISC0);
|
||||
clks[IMX8QXP_DC0_DISP1_CLK] = imx_clk_scu("dc0_disp1_clk", IMX_SC_R_DC_0, IMX_SC_PM_CLK_MISC1);
|
||||
|
||||
/* MIPI-LVDS SS */
|
||||
clks[IMX8QXP_MIPI0_I2C0_CLK] = imx_clk_scu("mipi0_i2c0_clk", IMX_SC_R_MIPI_0_I2C_0, IMX_SC_PM_CLK_MISC2);
|
||||
clks[IMX8QXP_MIPI0_I2C1_CLK] = imx_clk_scu("mipi0_i2c1_clk", IMX_SC_R_MIPI_0_I2C_1, IMX_SC_PM_CLK_MISC2);
|
||||
|
||||
/* MIPI CSI SS */
|
||||
clks[IMX8QXP_CSI0_CORE_CLK] = imx_clk_scu("mipi_csi0_core_clk", IMX_SC_R_CSI_0, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX8QXP_CSI0_ESC_CLK] = imx_clk_scu("mipi_csi0_esc_clk", IMX_SC_R_CSI_0, IMX_SC_PM_CLK_MISC);
|
||||
clks[IMX8QXP_CSI0_I2C0_CLK] = imx_clk_scu("mipi_csi0_i2c0_clk", IMX_SC_R_CSI_0_I2C_0, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX8QXP_CSI0_PWM0_CLK] = imx_clk_scu("mipi_csi0_pwm0_clk", IMX_SC_R_CSI_0_PWM_0, IMX_SC_PM_CLK_PER);
|
||||
|
||||
/* GPU SS */
|
||||
clks[IMX8QXP_GPU0_CORE_CLK] = imx_clk_scu("gpu_core0_clk", IMX_SC_R_GPU_0_PID0, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX8QXP_GPU0_SHADER_CLK] = imx_clk_scu("gpu_shader0_clk", IMX_SC_R_GPU_0_PID0, IMX_SC_PM_CLK_MISC);
|
||||
|
||||
for (i = 0; i < clk_data->num; i++) {
|
||||
if (IS_ERR(clks[i]))
|
||||
pr_warn("i.MX clk %u: register failed with %ld\n",
|
||||
i, PTR_ERR(clks[i]));
|
||||
}
|
||||
|
||||
return of_clk_add_hw_provider(ccm_node, of_clk_hw_onecell_get, clk_data);
|
||||
}
|
||||
|
||||
static const struct of_device_id imx8qxp_match[] = {
|
||||
{ .compatible = "fsl,imx8qxp-clk", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
static struct platform_driver imx8qxp_clk_driver = {
|
||||
.driver = {
|
||||
.name = "imx8qxp-clk",
|
||||
.of_match_table = imx8qxp_match,
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
.probe = imx8qxp_clk_probe,
|
||||
};
|
||||
builtin_platform_driver(imx8qxp_clk_driver);
|
||||
116
drivers/clk/imx/clk-lpcg-scu.c
Normal file
116
drivers/clk/imx/clk-lpcg-scu.c
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2018 NXP
|
||||
* Dong Aisheng <aisheng.dong@nxp.com>
|
||||
*/
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#include "clk-scu.h"
|
||||
|
||||
static DEFINE_SPINLOCK(imx_lpcg_scu_lock);
|
||||
|
||||
#define CLK_GATE_SCU_LPCG_MASK 0x3
|
||||
#define CLK_GATE_SCU_LPCG_HW_SEL BIT(0)
|
||||
#define CLK_GATE_SCU_LPCG_SW_SEL BIT(1)
|
||||
|
||||
/*
|
||||
* struct clk_lpcg_scu - Description of LPCG clock
|
||||
*
|
||||
* @hw: clk_hw of this LPCG
|
||||
* @reg: register of this LPCG clock
|
||||
* @bit_idx: bit index of this LPCG clock
|
||||
* @hw_gate: HW auto gate enable
|
||||
*
|
||||
* This structure describes one LPCG clock
|
||||
*/
|
||||
struct clk_lpcg_scu {
|
||||
struct clk_hw hw;
|
||||
void __iomem *reg;
|
||||
u8 bit_idx;
|
||||
bool hw_gate;
|
||||
};
|
||||
|
||||
#define to_clk_lpcg_scu(_hw) container_of(_hw, struct clk_lpcg_scu, hw)
|
||||
|
||||
static int clk_lpcg_scu_enable(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_lpcg_scu *clk = to_clk_lpcg_scu(hw);
|
||||
unsigned long flags;
|
||||
u32 reg, val;
|
||||
|
||||
spin_lock_irqsave(&imx_lpcg_scu_lock, flags);
|
||||
|
||||
reg = readl_relaxed(clk->reg);
|
||||
reg &= ~(CLK_GATE_SCU_LPCG_MASK << clk->bit_idx);
|
||||
|
||||
val = CLK_GATE_SCU_LPCG_SW_SEL;
|
||||
if (clk->hw_gate)
|
||||
val |= CLK_GATE_SCU_LPCG_HW_SEL;
|
||||
|
||||
reg |= val << clk->bit_idx;
|
||||
writel(reg, clk->reg);
|
||||
|
||||
spin_unlock_irqrestore(&imx_lpcg_scu_lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void clk_lpcg_scu_disable(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_lpcg_scu *clk = to_clk_lpcg_scu(hw);
|
||||
unsigned long flags;
|
||||
u32 reg;
|
||||
|
||||
spin_lock_irqsave(&imx_lpcg_scu_lock, flags);
|
||||
|
||||
reg = readl_relaxed(clk->reg);
|
||||
reg &= ~(CLK_GATE_SCU_LPCG_MASK << clk->bit_idx);
|
||||
writel(reg, clk->reg);
|
||||
|
||||
spin_unlock_irqrestore(&imx_lpcg_scu_lock, flags);
|
||||
}
|
||||
|
||||
static const struct clk_ops clk_lpcg_scu_ops = {
|
||||
.enable = clk_lpcg_scu_enable,
|
||||
.disable = clk_lpcg_scu_disable,
|
||||
};
|
||||
|
||||
struct clk_hw *imx_clk_lpcg_scu(const char *name, const char *parent_name,
|
||||
unsigned long flags, void __iomem *reg,
|
||||
u8 bit_idx, bool hw_gate)
|
||||
{
|
||||
struct clk_lpcg_scu *clk;
|
||||
struct clk_init_data init;
|
||||
struct clk_hw *hw;
|
||||
int ret;
|
||||
|
||||
clk = kzalloc(sizeof(*clk), GFP_KERNEL);
|
||||
if (!clk)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
clk->reg = reg;
|
||||
clk->bit_idx = bit_idx;
|
||||
clk->hw_gate = hw_gate;
|
||||
|
||||
init.name = name;
|
||||
init.ops = &clk_lpcg_scu_ops;
|
||||
init.flags = CLK_SET_RATE_PARENT | flags;
|
||||
init.parent_names = parent_name ? &parent_name : NULL;
|
||||
init.num_parents = parent_name ? 1 : 0;
|
||||
|
||||
clk->hw.init = &init;
|
||||
|
||||
hw = &clk->hw;
|
||||
ret = clk_hw_register(NULL, hw);
|
||||
if (ret) {
|
||||
kfree(clk);
|
||||
hw = ERR_PTR(ret);
|
||||
}
|
||||
|
||||
return hw;
|
||||
}
|
||||
203
drivers/clk/imx/clk-pfdv2.c
Normal file
203
drivers/clk/imx/clk-pfdv2.c
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2016 Freescale Semiconductor, Inc.
|
||||
* Copyright 2017~2018 NXP
|
||||
*
|
||||
* Author: Dong Aisheng <aisheng.dong@nxp.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "clk.h"
|
||||
|
||||
/**
|
||||
* struct clk_pfdv2 - IMX PFD clock
|
||||
* @clk_hw: clock source
|
||||
* @reg: PFD register address
|
||||
* @gate_bit: Gate bit offset
|
||||
* @vld_bit: Valid bit offset
|
||||
* @frac_off: PLL Fractional Divider offset
|
||||
*/
|
||||
|
||||
struct clk_pfdv2 {
|
||||
struct clk_hw hw;
|
||||
void __iomem *reg;
|
||||
u8 gate_bit;
|
||||
u8 vld_bit;
|
||||
u8 frac_off;
|
||||
};
|
||||
|
||||
#define to_clk_pfdv2(_hw) container_of(_hw, struct clk_pfdv2, hw)
|
||||
|
||||
#define CLK_PFDV2_FRAC_MASK 0x3f
|
||||
|
||||
#define LOCK_TIMEOUT_US USEC_PER_MSEC
|
||||
|
||||
static DEFINE_SPINLOCK(pfd_lock);
|
||||
|
||||
static int clk_pfdv2_wait(struct clk_pfdv2 *pfd)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
return readl_poll_timeout(pfd->reg, val, val & pfd->vld_bit,
|
||||
0, LOCK_TIMEOUT_US);
|
||||
}
|
||||
|
||||
static int clk_pfdv2_enable(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_pfdv2 *pfd = to_clk_pfdv2(hw);
|
||||
unsigned long flags;
|
||||
u32 val;
|
||||
|
||||
spin_lock_irqsave(&pfd_lock, flags);
|
||||
val = readl_relaxed(pfd->reg);
|
||||
val &= ~pfd->gate_bit;
|
||||
writel_relaxed(val, pfd->reg);
|
||||
spin_unlock_irqrestore(&pfd_lock, flags);
|
||||
|
||||
return clk_pfdv2_wait(pfd);
|
||||
}
|
||||
|
||||
static void clk_pfdv2_disable(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_pfdv2 *pfd = to_clk_pfdv2(hw);
|
||||
unsigned long flags;
|
||||
u32 val;
|
||||
|
||||
spin_lock_irqsave(&pfd_lock, flags);
|
||||
val = readl_relaxed(pfd->reg);
|
||||
val |= pfd->gate_bit;
|
||||
writel_relaxed(val, pfd->reg);
|
||||
spin_unlock_irqrestore(&pfd_lock, flags);
|
||||
}
|
||||
|
||||
static unsigned long clk_pfdv2_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct clk_pfdv2 *pfd = to_clk_pfdv2(hw);
|
||||
u64 tmp = parent_rate;
|
||||
u8 frac;
|
||||
|
||||
frac = (readl_relaxed(pfd->reg) >> pfd->frac_off)
|
||||
& CLK_PFDV2_FRAC_MASK;
|
||||
|
||||
if (!frac) {
|
||||
pr_debug("clk_pfdv2: %s invalid pfd frac value 0\n",
|
||||
clk_hw_get_name(hw));
|
||||
return 0;
|
||||
}
|
||||
|
||||
tmp *= 18;
|
||||
do_div(tmp, frac);
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static long clk_pfdv2_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *prate)
|
||||
{
|
||||
u64 tmp = *prate;
|
||||
u8 frac;
|
||||
|
||||
tmp = tmp * 18 + rate / 2;
|
||||
do_div(tmp, rate);
|
||||
frac = tmp;
|
||||
|
||||
if (frac < 12)
|
||||
frac = 12;
|
||||
else if (frac > 35)
|
||||
frac = 35;
|
||||
|
||||
tmp = *prate;
|
||||
tmp *= 18;
|
||||
do_div(tmp, frac);
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static int clk_pfdv2_is_enabled(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_pfdv2 *pfd = to_clk_pfdv2(hw);
|
||||
|
||||
if (readl_relaxed(pfd->reg) & pfd->gate_bit)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int clk_pfdv2_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct clk_pfdv2 *pfd = to_clk_pfdv2(hw);
|
||||
unsigned long flags;
|
||||
u64 tmp = parent_rate;
|
||||
u32 val;
|
||||
u8 frac;
|
||||
|
||||
tmp = tmp * 18 + rate / 2;
|
||||
do_div(tmp, rate);
|
||||
frac = tmp;
|
||||
if (frac < 12)
|
||||
frac = 12;
|
||||
else if (frac > 35)
|
||||
frac = 35;
|
||||
|
||||
spin_lock_irqsave(&pfd_lock, flags);
|
||||
val = readl_relaxed(pfd->reg);
|
||||
val &= ~(CLK_PFDV2_FRAC_MASK << pfd->frac_off);
|
||||
val |= frac << pfd->frac_off;
|
||||
writel_relaxed(val, pfd->reg);
|
||||
spin_unlock_irqrestore(&pfd_lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct clk_ops clk_pfdv2_ops = {
|
||||
.enable = clk_pfdv2_enable,
|
||||
.disable = clk_pfdv2_disable,
|
||||
.recalc_rate = clk_pfdv2_recalc_rate,
|
||||
.round_rate = clk_pfdv2_round_rate,
|
||||
.set_rate = clk_pfdv2_set_rate,
|
||||
.is_enabled = clk_pfdv2_is_enabled,
|
||||
};
|
||||
|
||||
struct clk_hw *imx_clk_pfdv2(const char *name, const char *parent_name,
|
||||
void __iomem *reg, u8 idx)
|
||||
{
|
||||
struct clk_init_data init;
|
||||
struct clk_pfdv2 *pfd;
|
||||
struct clk_hw *hw;
|
||||
int ret;
|
||||
|
||||
WARN_ON(idx > 3);
|
||||
|
||||
pfd = kzalloc(sizeof(*pfd), GFP_KERNEL);
|
||||
if (!pfd)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
pfd->reg = reg;
|
||||
pfd->gate_bit = 1 << ((idx + 1) * 8 - 1);
|
||||
pfd->vld_bit = pfd->gate_bit - 1;
|
||||
pfd->frac_off = idx * 8;
|
||||
|
||||
init.name = name;
|
||||
init.ops = &clk_pfdv2_ops;
|
||||
init.parent_names = &parent_name;
|
||||
init.num_parents = 1;
|
||||
init.flags = CLK_SET_RATE_GATE;
|
||||
|
||||
pfd->hw.init = &init;
|
||||
|
||||
hw = &pfd->hw;
|
||||
ret = clk_hw_register(NULL, hw);
|
||||
if (ret) {
|
||||
kfree(pfd);
|
||||
hw = ERR_PTR(ret);
|
||||
}
|
||||
|
||||
return hw;
|
||||
}
|
||||
184
drivers/clk/imx/clk-pllv4.c
Normal file
184
drivers/clk/imx/clk-pllv4.c
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2016 Freescale Semiconductor, Inc.
|
||||
* Copyright 2017~2018 NXP
|
||||
*
|
||||
* Author: Dong Aisheng <aisheng.dong@nxp.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "clk.h"
|
||||
|
||||
/* PLL Control Status Register (xPLLCSR) */
|
||||
#define PLL_CSR_OFFSET 0x0
|
||||
#define PLL_VLD BIT(24)
|
||||
#define PLL_EN BIT(0)
|
||||
|
||||
/* PLL Configuration Register (xPLLCFG) */
|
||||
#define PLL_CFG_OFFSET 0x08
|
||||
#define BP_PLL_MULT 16
|
||||
#define BM_PLL_MULT (0x7f << 16)
|
||||
|
||||
/* PLL Numerator Register (xPLLNUM) */
|
||||
#define PLL_NUM_OFFSET 0x10
|
||||
|
||||
/* PLL Denominator Register (xPLLDENOM) */
|
||||
#define PLL_DENOM_OFFSET 0x14
|
||||
|
||||
struct clk_pllv4 {
|
||||
struct clk_hw hw;
|
||||
void __iomem *base;
|
||||
};
|
||||
|
||||
/* Valid PLL MULT Table */
|
||||
static const int pllv4_mult_table[] = {33, 27, 22, 20, 17, 16};
|
||||
|
||||
#define to_clk_pllv4(__hw) container_of(__hw, struct clk_pllv4, hw)
|
||||
|
||||
#define LOCK_TIMEOUT_US USEC_PER_MSEC
|
||||
|
||||
static inline int clk_pllv4_wait_lock(struct clk_pllv4 *pll)
|
||||
{
|
||||
u32 csr;
|
||||
|
||||
return readl_poll_timeout(pll->base + PLL_CSR_OFFSET,
|
||||
csr, csr & PLL_VLD, 0, LOCK_TIMEOUT_US);
|
||||
}
|
||||
|
||||
static int clk_pllv4_is_enabled(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_pllv4 *pll = to_clk_pllv4(hw);
|
||||
|
||||
if (readl_relaxed(pll->base) & PLL_EN)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long clk_pllv4_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct clk_pllv4 *pll = to_clk_pllv4(hw);
|
||||
u32 div;
|
||||
|
||||
div = readl_relaxed(pll->base + PLL_CFG_OFFSET);
|
||||
div &= BM_PLL_MULT;
|
||||
div >>= BP_PLL_MULT;
|
||||
|
||||
return parent_rate * div;
|
||||
}
|
||||
|
||||
static long clk_pllv4_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *prate)
|
||||
{
|
||||
unsigned long parent_rate = *prate;
|
||||
unsigned long round_rate, i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(pllv4_mult_table); i++) {
|
||||
round_rate = parent_rate * pllv4_mult_table[i];
|
||||
if (rate >= round_rate)
|
||||
return round_rate;
|
||||
}
|
||||
|
||||
return round_rate;
|
||||
}
|
||||
|
||||
static bool clk_pllv4_is_valid_mult(unsigned int mult)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* check if mult is in valid MULT table */
|
||||
for (i = 0; i < ARRAY_SIZE(pllv4_mult_table); i++) {
|
||||
if (pllv4_mult_table[i] == mult)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int clk_pllv4_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct clk_pllv4 *pll = to_clk_pllv4(hw);
|
||||
u32 val, mult;
|
||||
|
||||
mult = rate / parent_rate;
|
||||
|
||||
if (!clk_pllv4_is_valid_mult(mult))
|
||||
return -EINVAL;
|
||||
|
||||
val = readl_relaxed(pll->base + PLL_CFG_OFFSET);
|
||||
val &= ~BM_PLL_MULT;
|
||||
val |= mult << BP_PLL_MULT;
|
||||
writel_relaxed(val, pll->base + PLL_CFG_OFFSET);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int clk_pllv4_enable(struct clk_hw *hw)
|
||||
{
|
||||
u32 val;
|
||||
struct clk_pllv4 *pll = to_clk_pllv4(hw);
|
||||
|
||||
val = readl_relaxed(pll->base);
|
||||
val |= PLL_EN;
|
||||
writel_relaxed(val, pll->base);
|
||||
|
||||
return clk_pllv4_wait_lock(pll);
|
||||
}
|
||||
|
||||
static void clk_pllv4_disable(struct clk_hw *hw)
|
||||
{
|
||||
u32 val;
|
||||
struct clk_pllv4 *pll = to_clk_pllv4(hw);
|
||||
|
||||
val = readl_relaxed(pll->base);
|
||||
val &= ~PLL_EN;
|
||||
writel_relaxed(val, pll->base);
|
||||
}
|
||||
|
||||
static const struct clk_ops clk_pllv4_ops = {
|
||||
.recalc_rate = clk_pllv4_recalc_rate,
|
||||
.round_rate = clk_pllv4_round_rate,
|
||||
.set_rate = clk_pllv4_set_rate,
|
||||
.enable = clk_pllv4_enable,
|
||||
.disable = clk_pllv4_disable,
|
||||
.is_enabled = clk_pllv4_is_enabled,
|
||||
};
|
||||
|
||||
struct clk_hw *imx_clk_pllv4(const char *name, const char *parent_name,
|
||||
void __iomem *base)
|
||||
{
|
||||
struct clk_pllv4 *pll;
|
||||
struct clk_hw *hw;
|
||||
struct clk_init_data init;
|
||||
int ret;
|
||||
|
||||
pll = kzalloc(sizeof(*pll), GFP_KERNEL);
|
||||
if (!pll)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
pll->base = base;
|
||||
|
||||
init.name = name;
|
||||
init.ops = &clk_pllv4_ops;
|
||||
init.parent_names = &parent_name;
|
||||
init.num_parents = 1;
|
||||
init.flags = CLK_SET_RATE_GATE;
|
||||
|
||||
pll->hw.init = &init;
|
||||
|
||||
hw = &pll->hw;
|
||||
ret = clk_hw_register(NULL, hw);
|
||||
if (ret) {
|
||||
kfree(pll);
|
||||
hw = ERR_PTR(ret);
|
||||
}
|
||||
|
||||
return hw;
|
||||
}
|
||||
256
drivers/clk/imx/clk-sccg-pll.c
Normal file
256
drivers/clk/imx/clk-sccg-pll.c
Normal file
|
|
@ -0,0 +1,256 @@
|
|||
// SPDX-License-Identifier: (GPL-2.0 OR MIT)
|
||||
/*
|
||||
* Copyright 2018 NXP.
|
||||
*
|
||||
* This driver supports the SCCG plls found in the imx8m SOCs
|
||||
*
|
||||
* Documentation for this SCCG pll can be found at:
|
||||
* https://www.nxp.com/docs/en/reference-manual/IMX8MDQLQRM.pdf#page=834
|
||||
*/
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/bitfield.h>
|
||||
|
||||
#include "clk.h"
|
||||
|
||||
/* PLL CFGs */
|
||||
#define PLL_CFG0 0x0
|
||||
#define PLL_CFG1 0x4
|
||||
#define PLL_CFG2 0x8
|
||||
|
||||
#define PLL_DIVF1_MASK GENMASK(18, 13)
|
||||
#define PLL_DIVF2_MASK GENMASK(12, 7)
|
||||
#define PLL_DIVR1_MASK GENMASK(27, 25)
|
||||
#define PLL_DIVR2_MASK GENMASK(24, 19)
|
||||
#define PLL_REF_MASK GENMASK(2, 0)
|
||||
|
||||
#define PLL_LOCK_MASK BIT(31)
|
||||
#define PLL_PD_MASK BIT(7)
|
||||
|
||||
#define OSC_25M 25000000
|
||||
#define OSC_27M 27000000
|
||||
|
||||
#define PLL_SCCG_LOCK_TIMEOUT 70
|
||||
|
||||
struct clk_sccg_pll {
|
||||
struct clk_hw hw;
|
||||
void __iomem *base;
|
||||
};
|
||||
|
||||
#define to_clk_sccg_pll(_hw) container_of(_hw, struct clk_sccg_pll, hw)
|
||||
|
||||
static int clk_pll_wait_lock(struct clk_sccg_pll *pll)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
return readl_poll_timeout(pll->base, val, val & PLL_LOCK_MASK, 0,
|
||||
PLL_SCCG_LOCK_TIMEOUT);
|
||||
}
|
||||
|
||||
static int clk_pll1_is_prepared(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_sccg_pll *pll = to_clk_sccg_pll(hw);
|
||||
u32 val;
|
||||
|
||||
val = readl_relaxed(pll->base + PLL_CFG0);
|
||||
return (val & PLL_PD_MASK) ? 0 : 1;
|
||||
}
|
||||
|
||||
static unsigned long clk_pll1_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct clk_sccg_pll *pll = to_clk_sccg_pll(hw);
|
||||
u32 val, divf;
|
||||
|
||||
val = readl_relaxed(pll->base + PLL_CFG2);
|
||||
divf = FIELD_GET(PLL_DIVF1_MASK, val);
|
||||
|
||||
return parent_rate * 2 * (divf + 1);
|
||||
}
|
||||
|
||||
static long clk_pll1_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *prate)
|
||||
{
|
||||
unsigned long parent_rate = *prate;
|
||||
u32 div;
|
||||
|
||||
if (!parent_rate)
|
||||
return 0;
|
||||
|
||||
div = rate / (parent_rate * 2);
|
||||
|
||||
return parent_rate * div * 2;
|
||||
}
|
||||
|
||||
static int clk_pll1_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct clk_sccg_pll *pll = to_clk_sccg_pll(hw);
|
||||
u32 val;
|
||||
u32 divf;
|
||||
|
||||
if (!parent_rate)
|
||||
return -EINVAL;
|
||||
|
||||
divf = rate / (parent_rate * 2);
|
||||
|
||||
val = readl_relaxed(pll->base + PLL_CFG2);
|
||||
val &= ~PLL_DIVF1_MASK;
|
||||
val |= FIELD_PREP(PLL_DIVF1_MASK, divf - 1);
|
||||
writel_relaxed(val, pll->base + PLL_CFG2);
|
||||
|
||||
return clk_pll_wait_lock(pll);
|
||||
}
|
||||
|
||||
static int clk_pll1_prepare(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_sccg_pll *pll = to_clk_sccg_pll(hw);
|
||||
u32 val;
|
||||
|
||||
val = readl_relaxed(pll->base + PLL_CFG0);
|
||||
val &= ~PLL_PD_MASK;
|
||||
writel_relaxed(val, pll->base + PLL_CFG0);
|
||||
|
||||
return clk_pll_wait_lock(pll);
|
||||
}
|
||||
|
||||
static void clk_pll1_unprepare(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_sccg_pll *pll = to_clk_sccg_pll(hw);
|
||||
u32 val;
|
||||
|
||||
val = readl_relaxed(pll->base + PLL_CFG0);
|
||||
val |= PLL_PD_MASK;
|
||||
writel_relaxed(val, pll->base + PLL_CFG0);
|
||||
|
||||
}
|
||||
|
||||
static unsigned long clk_pll2_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct clk_sccg_pll *pll = to_clk_sccg_pll(hw);
|
||||
u32 val, ref, divr1, divf1, divr2, divf2;
|
||||
u64 temp64;
|
||||
|
||||
val = readl_relaxed(pll->base + PLL_CFG0);
|
||||
switch (FIELD_GET(PLL_REF_MASK, val)) {
|
||||
case 0:
|
||||
ref = OSC_25M;
|
||||
break;
|
||||
case 1:
|
||||
ref = OSC_27M;
|
||||
break;
|
||||
default:
|
||||
ref = OSC_25M;
|
||||
break;
|
||||
}
|
||||
|
||||
val = readl_relaxed(pll->base + PLL_CFG2);
|
||||
divr1 = FIELD_GET(PLL_DIVR1_MASK, val);
|
||||
divr2 = FIELD_GET(PLL_DIVR2_MASK, val);
|
||||
divf1 = FIELD_GET(PLL_DIVF1_MASK, val);
|
||||
divf2 = FIELD_GET(PLL_DIVF2_MASK, val);
|
||||
|
||||
temp64 = ref * 2;
|
||||
temp64 *= (divf1 + 1) * (divf2 + 1);
|
||||
|
||||
do_div(temp64, (divr1 + 1) * (divr2 + 1));
|
||||
|
||||
return temp64;
|
||||
}
|
||||
|
||||
static long clk_pll2_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *prate)
|
||||
{
|
||||
u32 div;
|
||||
unsigned long parent_rate = *prate;
|
||||
|
||||
if (!parent_rate)
|
||||
return 0;
|
||||
|
||||
div = rate / parent_rate;
|
||||
|
||||
return parent_rate * div;
|
||||
}
|
||||
|
||||
static int clk_pll2_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
u32 val;
|
||||
u32 divf;
|
||||
struct clk_sccg_pll *pll = to_clk_sccg_pll(hw);
|
||||
|
||||
if (!parent_rate)
|
||||
return -EINVAL;
|
||||
|
||||
divf = rate / parent_rate;
|
||||
|
||||
val = readl_relaxed(pll->base + PLL_CFG2);
|
||||
val &= ~PLL_DIVF2_MASK;
|
||||
val |= FIELD_PREP(PLL_DIVF2_MASK, divf - 1);
|
||||
writel_relaxed(val, pll->base + PLL_CFG2);
|
||||
|
||||
return clk_pll_wait_lock(pll);
|
||||
}
|
||||
|
||||
static const struct clk_ops clk_sccg_pll1_ops = {
|
||||
.is_prepared = clk_pll1_is_prepared,
|
||||
.recalc_rate = clk_pll1_recalc_rate,
|
||||
.round_rate = clk_pll1_round_rate,
|
||||
.set_rate = clk_pll1_set_rate,
|
||||
};
|
||||
|
||||
static const struct clk_ops clk_sccg_pll2_ops = {
|
||||
.prepare = clk_pll1_prepare,
|
||||
.unprepare = clk_pll1_unprepare,
|
||||
.recalc_rate = clk_pll2_recalc_rate,
|
||||
.round_rate = clk_pll2_round_rate,
|
||||
.set_rate = clk_pll2_set_rate,
|
||||
};
|
||||
|
||||
struct clk *imx_clk_sccg_pll(const char *name,
|
||||
const char *parent_name,
|
||||
void __iomem *base,
|
||||
enum imx_sccg_pll_type pll_type)
|
||||
{
|
||||
struct clk_sccg_pll *pll;
|
||||
struct clk_init_data init;
|
||||
struct clk_hw *hw;
|
||||
int ret;
|
||||
|
||||
switch (pll_type) {
|
||||
case SCCG_PLL1:
|
||||
init.ops = &clk_sccg_pll1_ops;
|
||||
break;
|
||||
case SCCG_PLL2:
|
||||
init.ops = &clk_sccg_pll2_ops;
|
||||
break;
|
||||
default:
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
pll = kzalloc(sizeof(*pll), GFP_KERNEL);
|
||||
if (!pll)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
init.name = name;
|
||||
init.flags = 0;
|
||||
init.parent_names = &parent_name;
|
||||
init.num_parents = 1;
|
||||
|
||||
pll->base = base;
|
||||
pll->hw.init = &init;
|
||||
|
||||
hw = &pll->hw;
|
||||
|
||||
ret = clk_hw_register(NULL, hw);
|
||||
if (ret) {
|
||||
kfree(pll);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
return hw->clk;
|
||||
}
|
||||
270
drivers/clk/imx/clk-scu.c
Normal file
270
drivers/clk/imx/clk-scu.c
Normal file
|
|
@ -0,0 +1,270 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2018 NXP
|
||||
* Dong Aisheng <aisheng.dong@nxp.com>
|
||||
*/
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "clk-scu.h"
|
||||
|
||||
static struct imx_sc_ipc *ccm_ipc_handle;
|
||||
|
||||
/*
|
||||
* struct clk_scu - Description of one SCU clock
|
||||
* @hw: the common clk_hw
|
||||
* @rsrc_id: resource ID of this SCU clock
|
||||
* @clk_type: type of this clock resource
|
||||
*/
|
||||
struct clk_scu {
|
||||
struct clk_hw hw;
|
||||
u16 rsrc_id;
|
||||
u8 clk_type;
|
||||
};
|
||||
|
||||
/*
|
||||
* struct imx_sc_msg_req_set_clock_rate - clock set rate protocol
|
||||
* @hdr: SCU protocol header
|
||||
* @rate: rate to set
|
||||
* @resource: clock resource to set rate
|
||||
* @clk: clk type of this resource
|
||||
*
|
||||
* This structure describes the SCU protocol of clock rate set
|
||||
*/
|
||||
struct imx_sc_msg_req_set_clock_rate {
|
||||
struct imx_sc_rpc_msg hdr;
|
||||
__le32 rate;
|
||||
__le16 resource;
|
||||
u8 clk;
|
||||
} __packed;
|
||||
|
||||
struct req_get_clock_rate {
|
||||
__le16 resource;
|
||||
u8 clk;
|
||||
} __packed;
|
||||
|
||||
struct resp_get_clock_rate {
|
||||
__le32 rate;
|
||||
};
|
||||
|
||||
/*
|
||||
* struct imx_sc_msg_get_clock_rate - clock get rate protocol
|
||||
* @hdr: SCU protocol header
|
||||
* @req: get rate request protocol
|
||||
* @resp: get rate response protocol
|
||||
*
|
||||
* This structure describes the SCU protocol of clock rate get
|
||||
*/
|
||||
struct imx_sc_msg_get_clock_rate {
|
||||
struct imx_sc_rpc_msg hdr;
|
||||
union {
|
||||
struct req_get_clock_rate req;
|
||||
struct resp_get_clock_rate resp;
|
||||
} data;
|
||||
};
|
||||
|
||||
/*
|
||||
* struct imx_sc_msg_req_clock_enable - clock gate protocol
|
||||
* @hdr: SCU protocol header
|
||||
* @resource: clock resource to gate
|
||||
* @clk: clk type of this resource
|
||||
* @enable: whether gate off the clock
|
||||
* @autog: HW auto gate enable
|
||||
*
|
||||
* This structure describes the SCU protocol of clock gate
|
||||
*/
|
||||
struct imx_sc_msg_req_clock_enable {
|
||||
struct imx_sc_rpc_msg hdr;
|
||||
__le16 resource;
|
||||
u8 clk;
|
||||
u8 enable;
|
||||
u8 autog;
|
||||
} __packed;
|
||||
|
||||
static inline struct clk_scu *to_clk_scu(struct clk_hw *hw)
|
||||
{
|
||||
return container_of(hw, struct clk_scu, hw);
|
||||
}
|
||||
|
||||
int imx_clk_scu_init(void)
|
||||
{
|
||||
return imx_scu_get_handle(&ccm_ipc_handle);
|
||||
}
|
||||
|
||||
/*
|
||||
* clk_scu_recalc_rate - Get clock rate for a SCU clock
|
||||
* @hw: clock to get rate for
|
||||
* @parent_rate: parent rate provided by common clock framework, not used
|
||||
*
|
||||
* Gets the current clock rate of a SCU clock. Returns the current
|
||||
* clock rate, or zero in failure.
|
||||
*/
|
||||
static unsigned long clk_scu_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct clk_scu *clk = to_clk_scu(hw);
|
||||
struct imx_sc_msg_get_clock_rate msg;
|
||||
struct imx_sc_rpc_msg *hdr = &msg.hdr;
|
||||
int ret;
|
||||
|
||||
hdr->ver = IMX_SC_RPC_VERSION;
|
||||
hdr->svc = IMX_SC_RPC_SVC_PM;
|
||||
hdr->func = IMX_SC_PM_FUNC_GET_CLOCK_RATE;
|
||||
hdr->size = 2;
|
||||
|
||||
msg.data.req.resource = cpu_to_le16(clk->rsrc_id);
|
||||
msg.data.req.clk = clk->clk_type;
|
||||
|
||||
ret = imx_scu_call_rpc(ccm_ipc_handle, &msg, true);
|
||||
if (ret) {
|
||||
pr_err("%s: failed to get clock rate %d\n",
|
||||
clk_hw_get_name(hw), ret);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return le32_to_cpu(msg.data.resp.rate);
|
||||
}
|
||||
|
||||
/*
|
||||
* clk_scu_round_rate - Round clock rate for a SCU clock
|
||||
* @hw: clock to round rate for
|
||||
* @rate: rate to round
|
||||
* @parent_rate: parent rate provided by common clock framework, not used
|
||||
*
|
||||
* Returns the current clock rate, or zero in failure.
|
||||
*/
|
||||
static long clk_scu_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *parent_rate)
|
||||
{
|
||||
/*
|
||||
* Assume we support all the requested rate and let the SCU firmware
|
||||
* to handle the left work
|
||||
*/
|
||||
return rate;
|
||||
}
|
||||
|
||||
/*
|
||||
* clk_scu_set_rate - Set rate for a SCU clock
|
||||
* @hw: clock to change rate for
|
||||
* @rate: target rate for the clock
|
||||
* @parent_rate: rate of the clock parent, not used for SCU clocks
|
||||
*
|
||||
* Sets a clock frequency for a SCU clock. Returns the SCU
|
||||
* protocol status.
|
||||
*/
|
||||
static int clk_scu_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct clk_scu *clk = to_clk_scu(hw);
|
||||
struct imx_sc_msg_req_set_clock_rate msg;
|
||||
struct imx_sc_rpc_msg *hdr = &msg.hdr;
|
||||
|
||||
hdr->ver = IMX_SC_RPC_VERSION;
|
||||
hdr->svc = IMX_SC_RPC_SVC_PM;
|
||||
hdr->func = IMX_SC_PM_FUNC_SET_CLOCK_RATE;
|
||||
hdr->size = 3;
|
||||
|
||||
msg.rate = cpu_to_le32(rate);
|
||||
msg.resource = cpu_to_le16(clk->rsrc_id);
|
||||
msg.clk = clk->clk_type;
|
||||
|
||||
return imx_scu_call_rpc(ccm_ipc_handle, &msg, true);
|
||||
}
|
||||
|
||||
static int sc_pm_clock_enable(struct imx_sc_ipc *ipc, u16 resource,
|
||||
u8 clk, bool enable, bool autog)
|
||||
{
|
||||
struct imx_sc_msg_req_clock_enable msg;
|
||||
struct imx_sc_rpc_msg *hdr = &msg.hdr;
|
||||
|
||||
hdr->ver = IMX_SC_RPC_VERSION;
|
||||
hdr->svc = IMX_SC_RPC_SVC_PM;
|
||||
hdr->func = IMX_SC_PM_FUNC_CLOCK_ENABLE;
|
||||
hdr->size = 3;
|
||||
|
||||
msg.resource = cpu_to_le16(resource);
|
||||
msg.clk = clk;
|
||||
msg.enable = enable;
|
||||
msg.autog = autog;
|
||||
|
||||
return imx_scu_call_rpc(ccm_ipc_handle, &msg, true);
|
||||
}
|
||||
|
||||
/*
|
||||
* clk_scu_prepare - Enable a SCU clock
|
||||
* @hw: clock to enable
|
||||
*
|
||||
* Enable the clock at the DSC slice level
|
||||
*/
|
||||
static int clk_scu_prepare(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_scu *clk = to_clk_scu(hw);
|
||||
|
||||
return sc_pm_clock_enable(ccm_ipc_handle, clk->rsrc_id,
|
||||
clk->clk_type, true, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* clk_scu_unprepare - Disable a SCU clock
|
||||
* @hw: clock to enable
|
||||
*
|
||||
* Disable the clock at the DSC slice level
|
||||
*/
|
||||
static void clk_scu_unprepare(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_scu *clk = to_clk_scu(hw);
|
||||
int ret;
|
||||
|
||||
ret = sc_pm_clock_enable(ccm_ipc_handle, clk->rsrc_id,
|
||||
clk->clk_type, false, false);
|
||||
if (ret)
|
||||
pr_warn("%s: clk unprepare failed %d\n", clk_hw_get_name(hw),
|
||||
ret);
|
||||
}
|
||||
|
||||
static const struct clk_ops clk_scu_ops = {
|
||||
.recalc_rate = clk_scu_recalc_rate,
|
||||
.round_rate = clk_scu_round_rate,
|
||||
.set_rate = clk_scu_set_rate,
|
||||
.prepare = clk_scu_prepare,
|
||||
.unprepare = clk_scu_unprepare,
|
||||
};
|
||||
|
||||
struct clk_hw *imx_clk_scu(const char *name, u32 rsrc_id, u8 clk_type)
|
||||
{
|
||||
struct clk_init_data init;
|
||||
struct clk_scu *clk;
|
||||
struct clk_hw *hw;
|
||||
int ret;
|
||||
|
||||
clk = kzalloc(sizeof(*clk), GFP_KERNEL);
|
||||
if (!clk)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
clk->rsrc_id = rsrc_id;
|
||||
clk->clk_type = clk_type;
|
||||
|
||||
init.name = name;
|
||||
init.ops = &clk_scu_ops;
|
||||
init.num_parents = 0;
|
||||
/*
|
||||
* Note on MX8, the clocks are tightly coupled with power domain
|
||||
* that once the power domain is off, the clock status may be
|
||||
* lost. So we make it NOCACHE to let user to retrieve the real
|
||||
* clock status from HW instead of using the possible invalid
|
||||
* cached rate.
|
||||
*/
|
||||
init.flags = CLK_GET_RATE_NOCACHE;
|
||||
clk->hw.init = &init;
|
||||
|
||||
hw = &clk->hw;
|
||||
ret = clk_hw_register(NULL, hw);
|
||||
if (ret) {
|
||||
kfree(clk);
|
||||
hw = ERR_PTR(ret);
|
||||
}
|
||||
|
||||
return hw;
|
||||
}
|
||||
18
drivers/clk/imx/clk-scu.h
Normal file
18
drivers/clk/imx/clk-scu.h
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Copyright 2018 NXP
|
||||
* Dong Aisheng <aisheng.dong@nxp.com>
|
||||
*/
|
||||
|
||||
#ifndef __IMX_CLK_SCU_H
|
||||
#define __IMX_CLK_SCU_H
|
||||
|
||||
#include <linux/firmware/imx/sci.h>
|
||||
|
||||
int imx_clk_scu_init(void);
|
||||
struct clk_hw *imx_clk_scu(const char *name, u32 rsrc_id, u8 clk_type);
|
||||
|
||||
struct clk_hw *imx_clk_lpcg_scu(const char *name, const char *parent_name,
|
||||
unsigned long flags, void __iomem *reg,
|
||||
u8 bit_idx, bool hw_gate);
|
||||
#endif
|
||||
|
|
@ -18,6 +18,16 @@ void __init imx_check_clocks(struct clk *clks[], unsigned int count)
|
|||
i, PTR_ERR(clks[i]));
|
||||
}
|
||||
|
||||
void imx_check_clk_hws(struct clk_hw *clks[], unsigned int count)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
if (IS_ERR(clks[i]))
|
||||
pr_err("i.MX clk %u: register failed with %ld\n",
|
||||
i, PTR_ERR(clks[i]));
|
||||
}
|
||||
|
||||
static struct clk * __init imx_obtain_fixed_clock_from_dt(const char *name)
|
||||
{
|
||||
struct of_phandle_args phandle;
|
||||
|
|
@ -49,6 +59,18 @@ struct clk * __init imx_obtain_fixed_clock(
|
|||
return clk;
|
||||
}
|
||||
|
||||
struct clk_hw * __init imx_obtain_fixed_clk_hw(struct device_node *np,
|
||||
const char *name)
|
||||
{
|
||||
struct clk *clk;
|
||||
|
||||
clk = of_clk_get_by_name(np, name);
|
||||
if (IS_ERR(clk))
|
||||
return ERR_PTR(-ENOENT);
|
||||
|
||||
return __clk_get_hw(clk);
|
||||
}
|
||||
|
||||
/*
|
||||
* This fixups the register CCM_CSCMR1 write value.
|
||||
* The write/read/divider values of the aclk_podf field
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
extern spinlock_t imx_ccm_lock;
|
||||
|
||||
void imx_check_clocks(struct clk *clks[], unsigned int count);
|
||||
void imx_check_clk_hws(struct clk_hw *clks[], unsigned int count);
|
||||
void imx_register_uart_clocks(struct clk ** const clks[]);
|
||||
|
||||
extern void imx_cscmr1_fixup(u32 *val);
|
||||
|
|
@ -21,12 +22,24 @@ enum imx_pllv1_type {
|
|||
IMX_PLLV1_IMX35,
|
||||
};
|
||||
|
||||
enum imx_sccg_pll_type {
|
||||
SCCG_PLL1,
|
||||
SCCG_PLL2,
|
||||
};
|
||||
|
||||
struct clk *imx_clk_pllv1(enum imx_pllv1_type type, const char *name,
|
||||
const char *parent, void __iomem *base);
|
||||
|
||||
struct clk *imx_clk_pllv2(const char *name, const char *parent,
|
||||
void __iomem *base);
|
||||
|
||||
struct clk *imx_clk_frac_pll(const char *name, const char *parent_name,
|
||||
void __iomem *base);
|
||||
|
||||
struct clk *imx_clk_sccg_pll(const char *name, const char *parent_name,
|
||||
void __iomem *base,
|
||||
enum imx_sccg_pll_type pll_type);
|
||||
|
||||
enum imx_pllv3_type {
|
||||
IMX_PLLV3_GENERIC,
|
||||
IMX_PLLV3_SYS,
|
||||
|
|
@ -42,6 +55,9 @@ enum imx_pllv3_type {
|
|||
struct clk *imx_clk_pllv3(enum imx_pllv3_type type, const char *name,
|
||||
const char *parent_name, void __iomem *base, u32 div_mask);
|
||||
|
||||
struct clk_hw *imx_clk_pllv4(const char *name, const char *parent_name,
|
||||
void __iomem *base);
|
||||
|
||||
struct clk *clk_register_gate2(struct device *dev, const char *name,
|
||||
const char *parent_name, unsigned long flags,
|
||||
void __iomem *reg, u8 bit_idx, u8 cgr_val,
|
||||
|
|
@ -51,26 +67,38 @@ struct clk *clk_register_gate2(struct device *dev, const char *name,
|
|||
struct clk * imx_obtain_fixed_clock(
|
||||
const char *name, unsigned long rate);
|
||||
|
||||
struct clk_hw *imx_obtain_fixed_clk_hw(struct device_node *np,
|
||||
const char *name);
|
||||
|
||||
struct clk *imx_clk_gate_exclusive(const char *name, const char *parent,
|
||||
void __iomem *reg, u8 shift, u32 exclusive_mask);
|
||||
|
||||
struct clk *imx_clk_pfd(const char *name, const char *parent_name,
|
||||
void __iomem *reg, u8 idx);
|
||||
|
||||
struct clk_hw *imx_clk_pfdv2(const char *name, const char *parent_name,
|
||||
void __iomem *reg, u8 idx);
|
||||
|
||||
struct clk *imx_clk_busy_divider(const char *name, const char *parent_name,
|
||||
void __iomem *reg, u8 shift, u8 width,
|
||||
void __iomem *busy_reg, u8 busy_shift);
|
||||
|
||||
struct clk *imx_clk_busy_mux(const char *name, void __iomem *reg, u8 shift,
|
||||
u8 width, void __iomem *busy_reg, u8 busy_shift,
|
||||
const char **parent_names, int num_parents);
|
||||
const char * const *parent_names, int num_parents);
|
||||
|
||||
struct clk_hw *imx7ulp_clk_composite(const char *name,
|
||||
const char * const *parent_names,
|
||||
int num_parents, bool mux_present,
|
||||
bool rate_present, bool gate_present,
|
||||
void __iomem *reg);
|
||||
|
||||
struct clk *imx_clk_fixup_divider(const char *name, const char *parent,
|
||||
void __iomem *reg, u8 shift, u8 width,
|
||||
void (*fixup)(u32 *val));
|
||||
|
||||
struct clk *imx_clk_fixup_mux(const char *name, void __iomem *reg,
|
||||
u8 shift, u8 width, const char **parents,
|
||||
u8 shift, u8 width, const char * const *parents,
|
||||
int num_parents, void (*fixup)(u32 *val));
|
||||
|
||||
static inline struct clk *imx_clk_fixed(const char *name, int rate)
|
||||
|
|
@ -78,8 +106,19 @@ static inline struct clk *imx_clk_fixed(const char *name, int rate)
|
|||
return clk_register_fixed_rate(NULL, name, NULL, 0, rate);
|
||||
}
|
||||
|
||||
static inline struct clk_hw *imx_clk_hw_fixed(const char *name, int rate)
|
||||
{
|
||||
return clk_hw_register_fixed_rate(NULL, name, NULL, 0, rate);
|
||||
}
|
||||
|
||||
static inline struct clk_hw *imx_get_clk_hw_fixed(const char *name, int rate)
|
||||
{
|
||||
return clk_hw_register_fixed_rate(NULL, name, NULL, 0, rate);
|
||||
}
|
||||
|
||||
static inline struct clk *imx_clk_mux_ldb(const char *name, void __iomem *reg,
|
||||
u8 shift, u8 width, const char **parents, int num_parents)
|
||||
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_SET_RATE_PARENT, reg,
|
||||
|
|
@ -100,6 +139,15 @@ static inline struct clk *imx_clk_divider(const char *name, const char *parent,
|
|||
reg, shift, width, 0, &imx_ccm_lock);
|
||||
}
|
||||
|
||||
static inline struct clk_hw *imx_clk_hw_divider(const char *name,
|
||||
const char *parent,
|
||||
void __iomem *reg, u8 shift,
|
||||
u8 width)
|
||||
{
|
||||
return clk_hw_register_divider(NULL, name, parent, CLK_SET_RATE_PARENT,
|
||||
reg, shift, width, 0, &imx_ccm_lock);
|
||||
}
|
||||
|
||||
static inline struct clk *imx_clk_divider_flags(const char *name,
|
||||
const char *parent, void __iomem *reg, u8 shift, u8 width,
|
||||
unsigned long flags)
|
||||
|
|
@ -108,6 +156,15 @@ static inline struct clk *imx_clk_divider_flags(const char *name,
|
|||
reg, shift, width, 0, &imx_ccm_lock);
|
||||
}
|
||||
|
||||
static inline struct clk_hw *imx_clk_hw_divider_flags(const char *name,
|
||||
const char *parent,
|
||||
void __iomem *reg, u8 shift,
|
||||
u8 width, unsigned long flags)
|
||||
{
|
||||
return clk_hw_register_divider(NULL, name, parent, flags,
|
||||
reg, shift, width, 0, &imx_ccm_lock);
|
||||
}
|
||||
|
||||
static inline struct clk *imx_clk_divider2(const char *name, const char *parent,
|
||||
void __iomem *reg, u8 shift, u8 width)
|
||||
{
|
||||
|
|
@ -116,6 +173,15 @@ static inline struct clk *imx_clk_divider2(const char *name, const char *parent,
|
|||
reg, shift, width, 0, &imx_ccm_lock);
|
||||
}
|
||||
|
||||
static inline struct clk *imx_clk_divider2_flags(const char *name,
|
||||
const char *parent, void __iomem *reg, u8 shift, u8 width,
|
||||
unsigned long flags)
|
||||
{
|
||||
return clk_register_divider(NULL, name, parent,
|
||||
flags | CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE,
|
||||
reg, shift, width, 0, &imx_ccm_lock);
|
||||
}
|
||||
|
||||
static inline struct clk *imx_clk_gate(const char *name, const char *parent,
|
||||
void __iomem *reg, u8 shift)
|
||||
{
|
||||
|
|
@ -130,6 +196,13 @@ static inline struct clk *imx_clk_gate_flags(const char *name, const char *paren
|
|||
shift, 0, &imx_ccm_lock);
|
||||
}
|
||||
|
||||
static inline struct clk_hw *imx_clk_hw_gate(const char *name, const char *parent,
|
||||
void __iomem *reg, u8 shift)
|
||||
{
|
||||
return clk_hw_register_gate(NULL, name, parent, CLK_SET_RATE_PARENT, reg,
|
||||
shift, 0, &imx_ccm_lock);
|
||||
}
|
||||
|
||||
static inline struct clk *imx_clk_gate_dis(const char *name, const char *parent,
|
||||
void __iomem *reg, u8 shift)
|
||||
{
|
||||
|
|
@ -190,6 +263,15 @@ static inline struct clk *imx_clk_gate3(const char *name, const char *parent,
|
|||
reg, shift, 0, &imx_ccm_lock);
|
||||
}
|
||||
|
||||
static inline struct clk *imx_clk_gate3_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 | CLK_OPS_PARENT_ENABLE,
|
||||
reg, shift, 0, &imx_ccm_lock);
|
||||
}
|
||||
|
||||
static inline struct clk *imx_clk_gate4(const char *name, const char *parent,
|
||||
void __iomem *reg, u8 shift)
|
||||
{
|
||||
|
|
@ -198,8 +280,18 @@ static inline struct clk *imx_clk_gate4(const char *name, const char *parent,
|
|||
reg, shift, 0x3, 0, &imx_ccm_lock, NULL);
|
||||
}
|
||||
|
||||
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, &imx_ccm_lock, NULL);
|
||||
}
|
||||
|
||||
static inline struct clk *imx_clk_mux(const char *name, void __iomem *reg,
|
||||
u8 shift, u8 width, const char **parents, int num_parents)
|
||||
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, reg, shift,
|
||||
|
|
@ -207,24 +299,78 @@ static inline struct clk *imx_clk_mux(const char *name, void __iomem *reg,
|
|||
}
|
||||
|
||||
static inline struct clk *imx_clk_mux2(const char *name, void __iomem *reg,
|
||||
u8 shift, u8 width, const char **parents, int num_parents)
|
||||
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, &imx_ccm_lock);
|
||||
}
|
||||
|
||||
static inline struct clk_hw *imx_clk_hw_mux2(const char *name, void __iomem *reg,
|
||||
u8 shift, u8 width,
|
||||
const char * const *parents,
|
||||
int num_parents)
|
||||
{
|
||||
return clk_hw_register_mux(NULL, name, parents, num_parents,
|
||||
CLK_SET_RATE_NO_REPARENT |
|
||||
CLK_OPS_PARENT_ENABLE,
|
||||
reg, shift, width, 0, &imx_ccm_lock);
|
||||
}
|
||||
|
||||
static inline struct clk *imx_clk_mux_flags(const char *name,
|
||||
void __iomem *reg, u8 shift, u8 width, const char **parents,
|
||||
int num_parents, unsigned long flags)
|
||||
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,
|
||||
&imx_ccm_lock);
|
||||
}
|
||||
|
||||
static inline struct clk *imx_clk_mux2_flags(const char *name,
|
||||
void __iomem *reg, u8 shift, u8 width, const char **parents,
|
||||
int num_parents, unsigned long flags)
|
||||
{
|
||||
return clk_register_mux(NULL, name, parents, num_parents,
|
||||
flags | CLK_SET_RATE_NO_REPARENT | CLK_OPS_PARENT_ENABLE,
|
||||
reg, shift, width, 0, &imx_ccm_lock);
|
||||
}
|
||||
|
||||
static inline struct clk_hw *imx_clk_hw_mux_flags(const char *name,
|
||||
void __iomem *reg, u8 shift,
|
||||
u8 width,
|
||||
const char * const *parents,
|
||||
int num_parents,
|
||||
unsigned long flags)
|
||||
{
|
||||
return clk_hw_register_mux(NULL, name, parents, num_parents,
|
||||
flags | CLK_SET_RATE_NO_REPARENT,
|
||||
reg, shift, width, 0, &imx_ccm_lock);
|
||||
}
|
||||
|
||||
struct clk *imx_clk_cpu(const char *name, const char *parent_name,
|
||||
struct clk *div, struct clk *mux, struct clk *pll,
|
||||
struct clk *step);
|
||||
|
||||
struct clk *imx8m_clk_composite_flags(const char *name,
|
||||
const char **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)
|
||||
|
||||
struct clk_hw *imx_clk_divider_gate(const char *name, const char *parent_name,
|
||||
unsigned long flags, void __iomem *reg, u8 shift, u8 width,
|
||||
u8 clk_divider_flags, const struct clk_div_table *table,
|
||||
spinlock_t *lock);
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Reference in a new issue