Commit 58c05c82 authored by Stephen Boyd's avatar Stephen Boyd

Merge branches 'clk-imx7ulp', 'clk-imx6-fixes', 'clk-imx-fixes', 'clk-imx8qxp'...

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
......@@ -58,19 +58,11 @@ This binding for the SCU power domain providers uses the generic power
domain binding[2].
Required properties:
- compatible: Should be "fsl,scu-pd".
- #address-cells: Should be 1.
- #size-cells: Should be 0.
Required properties for power domain sub nodes:
- #power-domain-cells: Must be 0.
Optional Properties:
- reg: Resource ID of this power domain.
No exist means uncontrollable by user.
- compatible: Should be "fsl,imx8qxp-scu-pd".
- #power-domain-cells: Must be 1. Contains the Resource ID used by
SCU commands.
See detailed Resource ID list from:
include/dt-bindings/power/imx-rsrc.h
- power-domains: phandle pointing to the parent power domain.
include/dt-bindings/firmware/imx/rsrc.h
Clock bindings based on SCU Message Protocol
------------------------------------------------------------
......@@ -152,22 +144,9 @@ firmware {
...
};
imx8qx-pm {
compatible = "fsl,scu-pd";
#address-cells = <1>;
#size-cells = <0>;
pd_dma: dma-power-domain {
#power-domain-cells = <0>;
pd_dma_lpuart0: dma-lpuart0@57 {
reg = <SC_R_UART_0>;
#power-domain-cells = <0>;
power-domains = <&pd_dma>;
};
...
};
...
pd: imx8qx-pd {
compatible = "fsl,imx8qxp-scu-pd";
#power-domain-cells = <1>;
};
};
};
......@@ -179,5 +158,5 @@ serial@5a060000 {
clocks = <&clk IMX8QXP_UART0_CLK>,
<&clk IMX8QXP_UART0_IPG_CLK>;
clock-names = "per", "ipg";
power-domains = <&pd_dma_lpuart0>;
power-domains = <&pd IMX_SC_R_UART_0>;
};
......@@ -13,6 +13,9 @@ Optional properties:
management IC (PMIC) triggered via PMIC_STBY_REQ signal.
Boards that are designed to initiate poweroff on PMIC_ON_REQ signal should
be using "syscon-poweroff" driver instead.
- clocks: list of clock specifiers, must contain an entry for each entry
in clock-names
- clock-names: valid names are "osc", "ckil", "ckih1", "anaclk1" and "anaclk2"
The clock consumer should specify the desired clock by having the clock
ID in its "clocks" phandle cell. See include/dt-bindings/clock/imx6qdl-clock.h
......
* Clock bindings for Freescale i.MX7ULP
i.MX7ULP Clock functions are under joint control of the System
Clock Generation (SCG) modules, Peripheral Clock Control (PCC)
modules, and Core Mode Controller (CMC)1 blocks
The clocking scheme provides clear separation between M4 domain
and A7 domain. Except for a few clock sources shared between two
domains, such as the System Oscillator clock, the Slow IRC (SIRC),
and and the Fast IRC clock (FIRCLK), clock sources and clock
management are separated and contained within each domain.
M4 clock management consists of SCG0, PCC0, PCC1, and CMC0 modules.
A7 clock management consists of SCG1, PCC2, PCC3, and CMC1 modules.
Note: this binding doc is only for A7 clock domain.
System Clock Generation (SCG) modules:
---------------------------------------------------------------------
The System Clock Generation (SCG) is responsible for clock generation
and distribution across this device. Functions performed by the SCG
include: clock reference selection, generation of clock used to derive
processor, system, peripheral bus and external memory interface clocks,
source selection for peripheral clocks and control of power saving
clock gating mode.
Required properties:
- compatible: Should be "fsl,imx7ulp-scg1".
- reg : Should contain registers location and length.
- #clock-cells: Should be <1>.
- clocks: Should contain the fixed input clocks.
- clock-names: Should contain the following clock names:
"rosc", "sosc", "sirc", "firc", "upll", "mpll".
Peripheral Clock Control (PCC) modules:
---------------------------------------------------------------------
The Peripheral Clock Control (PCC) is responsible for clock selection,
optional division and clock gating mode for peripherals in their
respected power domain
Required properties:
- compatible: Should be one of:
"fsl,imx7ulp-pcc2",
"fsl,imx7ulp-pcc3".
- reg : Should contain registers location and length.
- #clock-cells: Should be <1>.
- clocks: Should contain the fixed input clocks.
- clock-names: Should contain the following clock names:
"nic1_bus_clk", "nic1_clk", "ddr_clk", "apll_pfd2",
"apll_pfd1", "apll_pfd0", "upll", "sosc_bus_clk",
"mpll", "firc_bus_clk", "rosc", "spll_bus_clk";
The clock consumer should specify the desired clock by having the clock
ID in its "clocks" phandle cell.
See include/dt-bindings/clock/imx7ulp-clock.h
for the full list of i.MX7ULP clock IDs of each module.
Examples:
#include <dt-bindings/clock/imx7ulp-clock.h>
scg1: scg1@403e0000 {
compatible = "fsl,imx7ulp-scg1;
reg = <0x403e0000 0x10000>;
clocks = <&rosc>, <&sosc>, <&sirc>,
<&firc>, <&upll>, <&mpll>;
clock-names = "rosc", "sosc", "sirc",
"firc", "upll", "mpll";
#clock-cells = <1>;
};
pcc2: pcc2@403f0000 {
compatible = "fsl,imx7ulp-pcc2";
reg = <0x403f0000 0x10000>;
#clock-cells = <1>;
clocks = <&scg1 IMX7ULP_CLK_NIC1_BUS_DIV>,
<&scg1 IMX7ULP_CLK_NIC1_DIV>,
<&scg1 IMX7ULP_CLK_DDR_DIV>,
<&scg1 IMX7ULP_CLK_APLL_PFD2>,
<&scg1 IMX7ULP_CLK_APLL_PFD1>,
<&scg1 IMX7ULP_CLK_APLL_PFD0>,
<&scg1 IMX7ULP_CLK_UPLL>,
<&scg1 IMX7ULP_CLK_SOSC_BUS_CLK>,
<&scg1 IMX7ULP_CLK_MIPI_PLL>,
<&scg1 IMX7ULP_CLK_FIRC_BUS_CLK>,
<&scg1 IMX7ULP_CLK_ROSC>,
<&scg1 IMX7ULP_CLK_SPLL_BUS_CLK>;
clock-names = "nic1_bus_clk", "nic1_clk", "ddr_clk",
"apll_pfd2", "apll_pfd1", "apll_pfd0",
"upll", "sosc_bus_clk", "mpll",
"firc_bus_clk", "rosc", "spll_bus_clk";
};
usdhc1: usdhc@40380000 {
compatible = "fsl,imx7ulp-usdhc";
reg = <0x40380000 0x10000>;
interrupts = <GIC_SPI 43 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&scg1 IMX7ULP_CLK_NIC1_BUS_DIV>,
<&scg1 IMX7ULP_CLK_NIC1_DIV>,
<&pcc2 IMX7ULP_CLK_USDHC1>;
clock-names ="ipg", "ahb", "per";
bus-width = <4>;
};
* Clock bindings for NXP i.MX8M Quad
Required properties:
- compatible: Should be "fsl,imx8mq-ccm"
- reg: Address and length of the register set
- #clock-cells: Should be <1>
- clocks: list of clock specifiers, must contain an entry for each required
entry in clock-names
- clock-names: should include the following entries:
- "ckil"
- "osc_25m"
- "osc_27m"
- "clk_ext1"
- "clk_ext2"
- "clk_ext3"
- "clk_ext4"
The clock consumer should specify the desired clock by having the clock
ID in its "clocks" phandle cell. See include/dt-bindings/clock/imx8mq-clock.h
for the full list of i.MX8M Quad clock IDs.
* NXP i.MX8QXP LPCG (Low-Power Clock Gating) Clock bindings
The Low-Power Clock Gate (LPCG) modules contain a local programming
model to control the clock gates for the peripherals. An LPCG module
is used to locally gate the clocks for the associated peripheral.
Note:
This level of clock gating is provided after the clocks are generated
by the SCU resources and clock controls. Thus even if the clock is
enabled by these control bits, it might still not be running based
on the base resource.
Required properties:
- compatible: Should be one of:
"fsl,imx8qxp-lpcg-adma",
"fsl,imx8qxp-lpcg-conn",
"fsl,imx8qxp-lpcg-dc",
"fsl,imx8qxp-lpcg-dsp",
"fsl,imx8qxp-lpcg-gpu",
"fsl,imx8qxp-lpcg-hsio",
"fsl,imx8qxp-lpcg-img",
"fsl,imx8qxp-lpcg-lsio",
"fsl,imx8qxp-lpcg-vpu"
- reg: Address and length of the register set
- #clock-cells: Should be <1>
The clock consumer should specify the desired clock by having the clock
ID in its "clocks" phandle cell.
See the full list of clock IDs from:
include/dt-bindings/clock/imx8qxp-clock.h
Examples:
#include <dt-bindings/clock/imx8qxp-clock.h>
conn_lpcg: clock-controller@5b200000 {
compatible = "fsl,imx8qxp-lpcg-conn";
reg = <0x5b200000 0xb0000>;
#clock-cells = <1>;
};
usdhc1: mmc@5b010000 {
compatible = "fsl,imx8qxp-usdhc", "fsl,imx7d-usdhc";
interrupt-parent = <&gic>;
interrupts = <GIC_SPI 232 IRQ_TYPE_LEVEL_HIGH>;
reg = <0x5b010000 0x10000>;
clocks = <&conn_lpcg IMX8QXP_CONN_LPCG_SDHC0_IPG_CLK>,
<&conn_lpcg IMX8QXP_CONN_LPCG_SDHC0_PER_CLK>,
<&conn_lpcg IMX8QXP_CONN_LPCG_SDHC0_HCLK>;
clock-names = "ipg", "per", "ahb";
};
......@@ -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
......
# 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.
# 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;
......
// 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;
}
// 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);
}
// 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);