Commit 1578968f authored by Stephen Boyd's avatar Stephen Boyd

Merge branches 'clk-imx6-mmdc', 'clk-qcom-krait', 'clk-rockchip' and...

Merge branches 'clk-imx6-mmdc', 'clk-qcom-krait', 'clk-rockchip' and 'clk-smp2s11-match' into clk-next

  - iMX6 MMDC clks
  - Qualcomm Krait CPU clk support

* clk-imx6-mmdc:
  clk: imx6q: add mmdc0 ipg clock
  clk: imx6sl: add mmdc ipg clocks
  clk: imx6sll: add mmdc1 ipg clock
  clk: imx6sx: add mmdc1 ipg clock
  clk: imx6ul: add mmdc1 ipg clock

* clk-qcom-krait:
  clk: qcom: Add safe switch hook for krait mux clocks
  dt-bindings: clock: Document qcom,krait-cc
  clk: qcom: Add Krait clock controller driver
  dt-bindings: arm: Document qcom,kpss-gcc
  clk: qcom: Add KPSS ACC/GCC driver
  clk: qcom: Add support for Krait clocks
  clk: qcom: Add IPQ806X's HFPLLs
  clk: qcom: Add MSM8960/APQ8064's HFPLLs
  dt-bindings: clock: Document qcom,hfpll
  clk: qcom: Add HFPLL driver
  clk: qcom: Add support for High-Frequency PLLs (HFPLLs)
  ARM: Add Krait L2 register accessor functions

* clk-rockchip:
  clk: rockchip: Fix static checker warning in rockchip_ddrclk_get_parent call
  clk: rockchip: use the newly added clock-id for hdmi on RK3066
  clk: rockchip: add clock-id for HCLK_HDMI on rk3066
  clk: rockchip: fix wrong mmc sample phase shift for rk3328
  clk: rockchip: improve rk3288 pll rates for better hdmi output

* clk-smp2s11-match:
  clk: s2mps11: Add used attribute to s2mps11_dt_match
  clk: s2mps11: Fix matching when built as module and DT node contains compatible
......@@ -21,10 +21,29 @@ PROPERTIES
the register region. An optional second element specifies
the base address and size of the alias register region.
- clocks:
Usage: required
Value type: <prop-encoded-array>
Definition: reference to the pll parents.
- clock-names:
Usage: required
Value type: <stringlist>
Definition: must be "pll8_vote", "pxo".
- clock-output-names:
Usage: optional
Value type: <string>
Definition: Name of the output clock. Typically acpuX_aux where X is a
CPU number starting at 0.
Example:
clock-controller@2088000 {
compatible = "qcom,kpss-acc-v2";
reg = <0x02088000 0x1000>,
<0x02008000 0x1000>;
clocks = <&gcc PLL8_VOTE>, <&gcc PXO_SRC>;
clock-names = "pll8_vote", "pxo";
clock-output-names = "acpu0_aux";
};
Krait Processor Sub-system (KPSS) Global Clock Controller (GCC)
PROPERTIES
- compatible:
Usage: required
Value type: <string>
Definition: should be one of the following. The generic compatible
"qcom,kpss-gcc" should also be included.
"qcom,kpss-gcc-ipq8064", "qcom,kpss-gcc"
"qcom,kpss-gcc-apq8064", "qcom,kpss-gcc"
"qcom,kpss-gcc-msm8974", "qcom,kpss-gcc"
"qcom,kpss-gcc-msm8960", "qcom,kpss-gcc"
- reg:
Usage: required
Value type: <prop-encoded-array>
Definition: base address and size of the register region
- clocks:
Usage: required
Value type: <prop-encoded-array>
Definition: reference to the pll parents.
- clock-names:
Usage: required
Value type: <stringlist>
Definition: must be "pll8_vote", "pxo".
- clock-output-names:
Usage: required
Value type: <string>
Definition: Name of the output clock. Typically acpu_l2_aux indicating
an L2 cache auxiliary clock.
Example:
l2cc: clock-controller@2011000 {
compatible = "qcom,kpss-gcc-ipq8064", "qcom,kpss-gcc";
reg = <0x2011000 0x1000>;
clocks = <&gcc PLL8_VOTE>, <&gcc PXO_SRC>;
clock-names = "pll8_vote", "pxo";
clock-output-names = "acpu_l2_aux";
};
High-Frequency PLL (HFPLL)
PROPERTIES
- compatible:
Usage: required
Value type: <string>:
shall contain only one of the following. The generic
compatible "qcom,hfpll" should be also included.
"qcom,hfpll-ipq8064", "qcom,hfpll"
"qcom,hfpll-apq8064", "qcom,hfpll"
"qcom,hfpll-msm8974", "qcom,hfpll"
"qcom,hfpll-msm8960", "qcom,hfpll"
- reg:
Usage: required
Value type: <prop-encoded-array>
Definition: address and size of HPLL registers. An optional second
element specifies the address and size of the alias
register region.
- clocks:
Usage: required
Value type: <prop-encoded-array>
Definition: reference to the xo clock.
- clock-names:
Usage: required
Value type: <stringlist>
Definition: must be "xo".
- clock-output-names:
Usage: required
Value type: <string>
Definition: Name of the PLL. Typically hfpllX where X is a CPU number
starting at 0. Otherwise hfpll_Y where Y is more specific
such as "l2".
Example:
1) An HFPLL for the L2 cache.
clock-controller@f9016000 {
compatible = "qcom,hfpll-ipq8064", "qcom,hfpll";
reg = <0xf9016000 0x30>;
clocks = <&xo_board>;
clock-names = "xo";
clock-output-names = "hfpll_l2";
};
2) An HFPLL for CPU0. This HFPLL has the alias register region.
clock-controller@f908a000 {
compatible = "qcom,hfpll-ipq8064", "qcom,hfpll";
reg = <0xf908a000 0x30>, <0xf900a000 0x30>;
clocks = <&xo_board>;
clock-names = "xo";
clock-output-names = "hfpll0";
};
Krait Clock Controller
PROPERTIES
- compatible:
Usage: required
Value type: <string>
Definition: must be one of:
"qcom,krait-cc-v1"
"qcom,krait-cc-v2"
- #clock-cells:
Usage: required
Value type: <u32>
Definition: must be 1
- clocks:
Usage: required
Value type: <prop-encoded-array>
Definition: reference to the clock parents of hfpll, secondary muxes.
- clock-names:
Usage: required
Value type: <stringlist>
Definition: must be "hfpll0", "hfpll1", "acpu0_aux", "acpu1_aux", "qsb".
Example:
kraitcc: clock-controller {
compatible = "qcom,krait-cc-v1";
clocks = <&hfpll0>, <&hfpll1>, <&acpu0_aux>, <&acpu1_aux>, <qsb>;
clock-names = "hfpll0", "hfpll1", "acpu0_aux", "acpu1_aux", "qsb";
#clock-cells = <1>;
};
......@@ -7,6 +7,9 @@ config DMABOUNCE
bool
select ZONE_DMA
config KRAIT_L2_ACCESSORS
bool
config SHARP_LOCOMO
bool
......
......@@ -7,6 +7,7 @@ obj-y += firmware.o
obj-$(CONFIG_SA1111) += sa1111.o
obj-$(CONFIG_DMABOUNCE) += dmabounce.o
obj-$(CONFIG_KRAIT_L2_ACCESSORS) += krait-l2-accessors.o
obj-$(CONFIG_SHARP_LOCOMO) += locomo.o
obj-$(CONFIG_SHARP_PARAM) += sharpsl_param.o
obj-$(CONFIG_SHARP_SCOOP) += scoop.o
......
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2018, The Linux Foundation. All rights reserved.
#include <linux/spinlock.h>
#include <linux/export.h>
#include <asm/barrier.h>
#include <asm/krait-l2-accessors.h>
static DEFINE_RAW_SPINLOCK(krait_l2_lock);
void krait_set_l2_indirect_reg(u32 addr, u32 val)
{
unsigned long flags;
raw_spin_lock_irqsave(&krait_l2_lock, flags);
/*
* Select the L2 window by poking l2cpselr, then write to the window
* via l2cpdr.
*/
asm volatile ("mcr p15, 3, %0, c15, c0, 6 @ l2cpselr" : : "r" (addr));
isb();
asm volatile ("mcr p15, 3, %0, c15, c0, 7 @ l2cpdr" : : "r" (val));
isb();
raw_spin_unlock_irqrestore(&krait_l2_lock, flags);
}
EXPORT_SYMBOL(krait_set_l2_indirect_reg);
u32 krait_get_l2_indirect_reg(u32 addr)
{
u32 val;
unsigned long flags;
raw_spin_lock_irqsave(&krait_l2_lock, flags);
/*
* Select the L2 window by poking l2cpselr, then read from the window
* via l2cpdr.
*/
asm volatile ("mcr p15, 3, %0, c15, c0, 6 @ l2cpselr" : : "r" (addr));
isb();
asm volatile ("mrc p15, 3, %0, c15, c0, 7 @ l2cpdr" : "=r" (val));
raw_spin_unlock_irqrestore(&krait_l2_lock, flags);
return val;
}
EXPORT_SYMBOL(krait_get_l2_indirect_reg);
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __ASMARM_KRAIT_L2_ACCESSORS_H
#define __ASMARM_KRAIT_L2_ACCESSORS_H
extern void krait_set_l2_indirect_reg(u32 addr, u32 val);
extern u32 krait_get_l2_indirect_reg(u32 addr);
#endif
......@@ -229,6 +229,36 @@ static const struct platform_device_id s2mps11_clk_id[] = {
};
MODULE_DEVICE_TABLE(platform, s2mps11_clk_id);
#ifdef CONFIG_OF
/*
* Device is instantiated through parent MFD device and device matching is done
* through platform_device_id.
*
* However if device's DT node contains proper clock compatible and driver is
* built as a module, then the *module* matching will be done trough DT aliases.
* This requires of_device_id table. In the same time this will not change the
* actual *device* matching so do not add .of_match_table.
*/
static const struct of_device_id s2mps11_dt_match[] __used = {
{
.compatible = "samsung,s2mps11-clk",
.data = (void *)S2MPS11X,
}, {
.compatible = "samsung,s2mps13-clk",
.data = (void *)S2MPS13X,
}, {
.compatible = "samsung,s2mps14-clk",
.data = (void *)S2MPS14X,
}, {
.compatible = "samsung,s5m8767-clk",
.data = (void *)S5M8767X,
}, {
/* Sentinel */
},
};
MODULE_DEVICE_TABLE(of, s2mps11_dt_match);
#endif
static struct platform_driver s2mps11_clk_driver = {
.driver = {
.name = "s2mps11-clk",
......
......@@ -789,6 +789,7 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node)
clk[IMX6QDL_CLK_MLB] = imx_clk_gate2("mlb", "axi", base + 0x74, 18);
clk[IMX6QDL_CLK_MMDC_CH0_AXI] = imx_clk_gate2_flags("mmdc_ch0_axi", "mmdc_ch0_axi_podf", base + 0x74, 20, CLK_IS_CRITICAL);
clk[IMX6QDL_CLK_MMDC_CH1_AXI] = imx_clk_gate2("mmdc_ch1_axi", "mmdc_ch1_axi_podf", base + 0x74, 22);
clk[IMX6QDL_CLK_MMDC_P0_IPG] = imx_clk_gate2_flags("mmdc_p0_ipg", "ipg", base + 0x74, 24, CLK_IS_CRITICAL);
clk[IMX6QDL_CLK_OCRAM] = imx_clk_gate2("ocram", "ahb", base + 0x74, 28);
clk[IMX6QDL_CLK_OPENVG_AXI] = imx_clk_gate2("openvg_axi", "axi", base + 0x74, 30);
clk[IMX6QDL_CLK_PCIE_AXI] = imx_clk_gate2("pcie_axi", "pcie_axi_sel", base + 0x78, 0);
......
......@@ -386,6 +386,8 @@ static void __init imx6sl_clocks_init(struct device_node *ccm_node)
clks[IMX6SL_CLK_LCDIF_AXI] = imx_clk_gate2("lcdif_axi", "lcdif_axi_podf", base + 0x74, 6);
clks[IMX6SL_CLK_LCDIF_PIX] = imx_clk_gate2("lcdif_pix", "lcdif_pix_podf", base + 0x74, 8);
clks[IMX6SL_CLK_EPDC_PIX] = imx_clk_gate2("epdc_pix", "epdc_pix_podf", base + 0x74, 10);
clks[IMX6SL_CLK_MMDC_P0_IPG] = imx_clk_gate2_flags("mmdc_p0_ipg", "ipg", base + 0x74, 24, CLK_IS_CRITICAL);
clks[IMX6SL_CLK_MMDC_P1_IPG] = imx_clk_gate2("mmdc_p1_ipg", "ipg", base + 0x74, 26);
clks[IMX6SL_CLK_OCRAM] = imx_clk_gate2("ocram", "ocram_podf", base + 0x74, 28);
clks[IMX6SL_CLK_PWM1] = imx_clk_gate2("pwm1", "perclk", base + 0x78, 16);
clks[IMX6SL_CLK_PWM2] = imx_clk_gate2("pwm2", "perclk", base + 0x78, 18);
......
......@@ -293,6 +293,7 @@ static void __init imx6sll_clocks_init(struct device_node *ccm_node)
clks[IMX6SLL_CLK_WDOG1] = imx_clk_gate2("wdog1", "ipg", base + 0x74, 16);
clks[IMX6SLL_CLK_MMDC_P0_FAST] = imx_clk_gate_flags("mmdc_p0_fast", "mmdc_podf", base + 0x74, 20, CLK_IS_CRITICAL);
clks[IMX6SLL_CLK_MMDC_P0_IPG] = imx_clk_gate2_flags("mmdc_p0_ipg", "ipg", base + 0x74, 24, CLK_IS_CRITICAL);
clks[IMX6SLL_CLK_MMDC_P1_IPG] = imx_clk_gate2("mmdc_p1_ipg", "ipg", base + 0x74, 26);
clks[IMX6SLL_CLK_OCRAM] = imx_clk_gate_flags("ocram","ahb", base + 0x74, 28, CLK_IS_CRITICAL);
/* CCGR4 */
......
......@@ -431,6 +431,7 @@ static void __init imx6sx_clocks_init(struct device_node *ccm_node)
clks[IMX6SX_CLK_MLB] = imx_clk_gate2("mlb", "ahb", base + 0x74, 18);
clks[IMX6SX_CLK_MMDC_P0_FAST] = imx_clk_gate2_flags("mmdc_p0_fast", "mmdc_podf", base + 0x74, 20, CLK_IS_CRITICAL);
clks[IMX6SX_CLK_MMDC_P0_IPG] = imx_clk_gate2_flags("mmdc_p0_ipg", "ipg", base + 0x74, 24, CLK_IS_CRITICAL);
clks[IMX6SX_CLK_MMDC_P1_IPG] = imx_clk_gate2("mmdc_p1_ipg", "ipg", base + 0x74, 26);
clks[IMX6SX_CLK_OCRAM] = imx_clk_gate2_flags("ocram", "ocram_podf", base + 0x74, 28, CLK_IS_CRITICAL);
/* CCGR4 */
......
......@@ -408,6 +408,7 @@ static void __init imx6ul_clocks_init(struct device_node *ccm_node)
clks[IMX6UL_CLK_WDOG1] = imx_clk_gate2("wdog1", "ipg", base + 0x74, 16);
clks[IMX6UL_CLK_MMDC_P0_FAST] = imx_clk_gate_flags("mmdc_p0_fast", "mmdc_podf", base + 0x74, 20, CLK_IS_CRITICAL);
clks[IMX6UL_CLK_MMDC_P0_IPG] = imx_clk_gate2_flags("mmdc_p0_ipg", "ipg", base + 0x74, 24, CLK_IS_CRITICAL);
clks[IMX6UL_CLK_MMDC_P1_IPG] = imx_clk_gate2("mmdc_p1_ipg", "ipg", base + 0x74, 26);
clks[IMX6UL_CLK_AXI] = imx_clk_gate_flags("axi", "axi_podf", base + 0x74, 28, CLK_IS_CRITICAL);
/* CCGR4 */
......
config KRAIT_CLOCKS
bool
select KRAIT_L2_ACCESSORS
config QCOM_GDSC
bool
select PM_GENERIC_DOMAINS if PM
......@@ -297,3 +301,27 @@ config SPMI_PMIC_CLKDIV
Technologies, Inc. SPMI PMIC. It configures the frequency of
clkdiv outputs of the PMIC. These clocks are typically wired
through alternate functions on GPIO pins.
config QCOM_HFPLL
tristate "High-Frequency PLL (HFPLL) Clock Controller"
depends on COMMON_CLK_QCOM
help
Support for the high-frequency PLLs present on Qualcomm devices.
Say Y if you want to support CPU frequency scaling on devices
such as MSM8974, APQ8084, etc.
config KPSS_XCC
tristate "KPSS Clock Controller"
depends on COMMON_CLK_QCOM
help
Support for the Krait ACC and GCC clock controllers. Say Y
if you want to support CPU frequency scaling on devices such
as MSM8960, APQ8064, etc.
config KRAITCC
tristate "Krait Clock Controller"
depends on COMMON_CLK_QCOM && ARM
select KRAIT_CLOCKS
help
Support for the Krait CPU clocks on Qualcomm devices.
Say Y if you want to support CPU frequency scaling.
......@@ -11,6 +11,8 @@ clk-qcom-y += clk-branch.o
clk-qcom-y += clk-regmap-divider.o
clk-qcom-y += clk-regmap-mux.o
clk-qcom-y += clk-regmap-mux-div.o
clk-qcom-$(CONFIG_KRAIT_CLOCKS) += clk-krait.o
clk-qcom-y += clk-hfpll.o
clk-qcom-y += reset.o
clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o
......@@ -46,3 +48,6 @@ obj-$(CONFIG_SDM_GCC_660) += gcc-sdm660.o
obj-$(CONFIG_SDM_GCC_845) += gcc-sdm845.o
obj-$(CONFIG_SDM_VIDEOCC_845) += videocc-sdm845.o
obj-$(CONFIG_SPMI_PMIC_CLKDIV) += clk-spmi-pmic-div.o
obj-$(CONFIG_KPSS_XCC) += kpss-xcc.o
obj-$(CONFIG_QCOM_HFPLL) += hfpll.o
obj-$(CONFIG_KRAITCC) += krait-cc.o
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2018, The Linux Foundation. All rights reserved.
#include <linux/kernel.h>
#include <linux/export.h>
#include <linux/regmap.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/clk-provider.h>
#include <linux/spinlock.h>
#include "clk-regmap.h"
#include "clk-hfpll.h"
#define PLL_OUTCTRL BIT(0)
#define PLL_BYPASSNL BIT(1)
#define PLL_RESET_N BIT(2)
/* Initialize a HFPLL at a given rate and enable it. */
static void __clk_hfpll_init_once(struct clk_hw *hw)
{
struct clk_hfpll *h = to_clk_hfpll(hw);
struct hfpll_data const *hd = h->d;
struct regmap *regmap = h->clkr.regmap;
if (likely(h->init_done))
return;
/* Configure PLL parameters for integer mode. */
if (hd->config_val)
regmap_write(regmap, hd->config_reg, hd->config_val);
regmap_write(regmap, hd->m_reg, 0);
regmap_write(regmap, hd->n_reg, 1);
if (hd->user_reg) {
u32 regval = hd->user_val;
unsigned long rate;
rate = clk_hw_get_rate(hw);
/* Pick the right VCO. */
if (hd->user_vco_mask && rate > hd->low_vco_max_rate)
regval |= hd->user_vco_mask;
regmap_write(regmap, hd->user_reg, regval);
}
if (hd->droop_reg)
regmap_write(regmap, hd->droop_reg, hd->droop_val);
h->init_done = true;
}
static void __clk_hfpll_enable(struct clk_hw *hw)
{
struct clk_hfpll *h = to_clk_hfpll(hw);
struct hfpll_data const *hd = h->d;
struct regmap *regmap = h->clkr.regmap;
u32 val;
__clk_hfpll_init_once(hw);
/* Disable PLL bypass mode. */
regmap_update_bits(regmap, hd->mode_reg, PLL_BYPASSNL, PLL_BYPASSNL);
/*
* H/W requires a 5us delay between disabling the bypass and
* de-asserting the reset. Delay 10us just to be safe.
*/
udelay(10);
/* De-assert active-low PLL reset. */
regmap_update_bits(regmap, hd->mode_reg, PLL_RESET_N, PLL_RESET_N);
/* Wait for PLL to lock. */
if (hd->status_reg) {
do {
regmap_read(regmap, hd->status_reg, &val);
} while (!(val & BIT(hd->lock_bit)));
} else {
udelay(60);
}
/* Enable PLL output. */
regmap_update_bits(regmap, hd->mode_reg, PLL_OUTCTRL, PLL_OUTCTRL);
}
/* Enable an already-configured HFPLL. */
static int clk_hfpll_enable(struct clk_hw *hw)
{
unsigned long flags;
struct clk_hfpll *h = to_clk_hfpll(hw);
struct hfpll_data const *hd = h->d;
struct regmap *regmap = h->clkr.regmap;
u32 mode;
spin_lock_irqsave(&h->lock, flags);
regmap_read(regmap, hd->mode_reg, &mode);
if (!(mode & (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL)))
__clk_hfpll_enable(hw);
spin_unlock_irqrestore(&h->lock, flags);
return 0;
}
static void __clk_hfpll_disable(struct clk_hfpll *h)
{
struct hfpll_data const *hd = h->d;
struct regmap *regmap = h->clkr.regmap;
/*
* Disable the PLL output, disable test mode, enable the bypass mode,
* and assert the reset.
*/
regmap_update_bits(regmap, hd->mode_reg,
PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL, 0);
}
static void clk_hfpll_disable(struct clk_hw *hw)
{
struct clk_hfpll *h = to_clk_hfpll(hw);
unsigned long flags;
spin_lock_irqsave(&h->lock, flags);
__clk_hfpll_disable(h);
spin_unlock_irqrestore(&h->lock, flags);
}
static long clk_hfpll_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
struct clk_hfpll *h = to_clk_hfpll(hw);
struct hfpll_data const *hd = h->d;
unsigned long rrate;
rate = clamp(rate, hd->min_rate, hd->max_rate);
rrate = DIV_ROUND_UP(rate, *parent_rate) * *parent_rate;
if (rrate > hd->max_rate)
rrate -= *parent_rate;
return rrate;
}
/*
* For optimization reasons, assumes no downstream clocks are actively using
* it.
*/
static int clk_hfpll_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct clk_hfpll *h = to_clk_hfpll(hw);
struct hfpll_data const *hd = h->d;
struct regmap *regmap = h->clkr.regmap;
unsigned long flags;
u32 l_val, val;
bool enabled;
l_val = rate / parent_rate;
spin_lock_irqsave(&h->lock, flags);
enabled = __clk_is_enabled(hw->clk);
if (enabled)
__clk_hfpll_disable(h);
/* Pick the right VCO. */
if (hd->user_reg && hd->user_vco_mask) {
regmap_read(regmap, hd->user_reg, &val);
if (rate <= hd->low_vco_max_rate)
val &= ~hd->user_vco_mask;
else
val |= hd->user_vco_mask;
regmap_write(regmap, hd->user_reg, val);
}
regmap_write(regmap, hd->l_reg, l_val);
if (enabled)
__clk_hfpll_enable(hw);
spin_unlock_irqrestore(&h->lock, flags);
return 0;
}
static unsigned long clk_hfpll_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct clk_hfpll *h = to_clk_hfpll(hw);
struct hfpll_data const *hd = h->d;
struct regmap *regmap = h->clkr.regmap;
u32 l_val;
regmap_read(regmap, hd->l_reg, &l_val);
return l_val * parent_rate;
}
static void clk_hfpll_init(struct clk_hw *hw)
{
struct clk_hfpll *h = to_clk_hfpll(hw);
struct hfpll_data const *hd = h->d;
struct regmap *regmap = h->clkr.regmap;
u32 mode, status;
regmap_read(regmap, hd->mode_reg, &mode);
if (mode != (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL)) {
__clk_hfpll_init_once(hw);
return;
}
if (hd->status_reg) {
regmap_read(regmap, hd->status_reg, &status);
if (!(status & BIT(hd->lock_bit))) {
WARN(1, "HFPLL %s is ON, but not locked!\n",
__clk_get_name(hw->clk));
clk_hfpll_disable(hw);
__clk_hfpll_init_once(hw);
}
}
}
static int hfpll_is_enabled(struct clk_hw *hw)
{
struct clk_hfpll *h = to_clk_hfpll(hw);
struct hfpll_data const *hd = h->d;
struct regmap *regmap = h->clkr.regmap;
u32 mode;
regmap_read(regmap, hd->mode_reg, &mode);
mode &= 0x7;
return mode == (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL);
}
const struct clk_ops clk_ops_hfpll = {
.enable = clk_hfpll_enable,
.disable = clk_hfpll_disable,
.is_enabled = hfpll_is_enabled,
.round_rate = clk_hfpll_round_rate,
.set_rate = clk_hfpll_set_rate,
.recalc_rate = clk_hfpll_recalc_rate,
.init = clk_hfpll_init,
};
EXPORT_SYMBOL_GPL(clk_ops_hfpll);
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __QCOM_CLK_HFPLL_H__
#define __QCOM_CLK_HFPLL_H__
#include <linux/clk-provider.h>
#include <linux/spinlock.h>
#include "clk-regmap.h"
struct hfpll_data {
u32 mode_reg;
u32 l_reg;
u32 m_reg;
u32 n_reg;
u32 user_reg;
u32 droop_reg;
u32 config_reg;
u32 status_reg;
u8 lock_bit;
u32 droop_val;
u32 config_val;
u32 user_val;
u32 user_vco_mask;
unsigned long low_vco_max_rate;
unsigned long min_rate;
unsigned long max_rate;
};
struct clk_hfpll {
struct hfpll_data const *d;
int init_done;
struct clk_regmap clkr;
spinlock_t lock;
};
#define to_clk_hfpll(_hw) \
container_of(to_clk_regmap(_hw), struct clk_hfpll, clkr)
extern const struct clk_ops clk_ops_hfpll;
#endif
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2018, The Linux Foundation. All rights reserved.
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/clk-provider.h>
#include <linux/spinlock.h>
#include <asm/krait-l2-accessors.h>
#include "clk-krait.h"
/* Secondary and primary muxes share the same cp15 register */
static DEFINE_SPINLOCK(krait_clock_reg_lock);
#define LPL_SHIFT 8
static void __krait_mux_set_sel(struct krait_mux_clk *mux, int sel)
{
unsigned long flags;
u32 regval;
spin_lock_irqsave(&krait_clock_reg_lock, flags);
regval = krait_get_l2_indirect_reg(mux->offset);
regval &= ~(mux->mask << mux->shift);
regval |= (sel & mux->mask) << mux->shift;
if (mux->lpl) {
regval &= ~(mux->mask << (mux->shift + LPL_SHIFT));
regval |= (sel & mux->mask) << (mux->shift + LPL_SHIFT);
}
krait_set_l2_indirect_reg(mux->offset, regval);
spin_unlock_irqrestore(&krait_clock_reg_lock, flags);
/* Wait for switch to complete. */
mb();
udelay(1);
}
static int krait_mux_set_parent(struct clk_hw *hw, u8 index)
{
struct krait_mux_clk *mux = to_krait_mux_clk(hw);
u32 sel;
sel = clk_mux_index_to_val(mux->parent_map, 0, index);
mux->en_mask = sel;
/* Don't touch mux if CPU is off as it won't work */
if (__clk_is_enabled(hw->clk))
__krait_mux_set_sel(mux, sel);
mux->reparent = true;
return 0;
}
static u8 krait_mux_get_parent(struct clk_hw *hw)
{
struct krait_mux_clk *mux = to_krait_mux_clk(hw);
u32 sel;
sel = krait_get_l2_indirect_reg(mux->offset);
sel >>= mux->shift;
sel &= mux->mask;
mux->en_mask = sel;
return clk_mux_val_to_index(hw, mux->parent_map, 0, sel);
}
const struct clk_ops krait_mux_clk_ops = {
.set_parent = krait_mux_set_parent,
.get_parent = krait_mux_get_parent,
.determine_rate = __clk_mux_determine_rate_closest,
};
EXPORT_SYMBOL_GPL(krait_mux_clk_ops);
/* The divider can divide by 2, 4, 6 and 8. But we only really need div-2. */
static long krait_div2_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
*parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw), rate * 2);
return DIV_ROUND_UP(*parent_rate, 2);
}
static int krait_div2_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct krait_div2_clk *d = to_krait_div2_clk(hw);
unsigned long flags;
u32 val;
u32 mask = BIT(d->width) - 1;
if (d->lpl)
mask = mask << (d->shift + LPL_SHIFT) | mask << d->shift;
spin_lock_irqsave(&krait_clock_reg_lock, flags);
val = krait_get_l2_indirect_reg(d->offset);
val &= ~mask;
krait_set_l2_indirect_reg(d->offset, val);
spin_unlock_irqrestore(&krait_clock_reg_lock, flags);
return 0;
}
static unsigned long
krait_div2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
{
struct krait_div2_clk *d = to_krait_div2_clk(hw);
u32 mask = BIT(d->width) - 1;
u32 div;
div = krait_get_l2_indirect_reg(d->offset);
div >>= d->shift;
div &= mask;
div = (div + 1) * 2;
return DIV_ROUND_UP(parent_rate, div);
}
const struct clk_ops krait_div2_clk_ops = {
.round_rate = krait_div2_round_rate,
.set_rate = krait_div2_set_rate,
.recalc_rate = krait_div2_recalc_rate,
};
EXPORT_SYMBOL_GPL(krait_div2_clk_ops);
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __QCOM_CLK_KRAIT_H
#define __QCOM_CLK_KRAIT_H
#include <linux/clk-provider.h>
struct krait_mux_clk {
unsigned int *parent_map;
u32 offset;
u32 mask;
u32 shift;
u32 en_mask;
bool lpl;
u8 safe_sel;
u8 old_index;
bool reparent;
struct clk_hw hw;
struct notifier_block clk_nb;
};
#define to_krait_mux_clk(_hw) container_of(_hw, struct krait_mux_clk, hw)
extern const struct clk_ops krait_mux_clk_ops;
struct krait_div2_clk {
u32 offset;
u8 width;
u32 shift;
bool lpl;
struct clk_hw hw;
};
#define to_krait_div2_clk(_hw) container_of(_hw, struct krait_div2_clk, hw)
extern const struct clk_ops krait_div2_clk_ops;
#endif
......@@ -30,6 +30,7 @@
#include "clk-pll.h"
#include "clk-rcg.h"
#include "clk-branch.h"
#include "clk-hfpll.h"
#include "reset.h"
static struct clk_pll pll0 = {
......@@ -113,6 +114,84 @@ static struct clk_regmap pll8_vote = {
},
};
static struct hfpll_data hfpll0_data = {
.mode_reg = 0x3200,
.l_reg = 0x3208,
.m_reg = 0x320c,
.n_reg = 0x3210,
.config_reg = 0x3204,
.status_reg = 0x321c,
.config_val = 0x7845c665,
.droop_reg = 0x3214,
.droop_val = 0x0108c000,
.min_rate = 600000000UL,
.max_rate = 1800000000UL,
};
static struct clk_hfpll hfpll0 = {
.d = &hfpll0_data,
.clkr.hw.init = &(struct clk_init_data){
.parent_names = (const char *[]){ "pxo" },
.num_parents = 1,
.name = "hfpll0",
.ops = &clk_ops_hfpll,
.flags = CLK_IGNORE_UNUSED,
},
.lock = __SPIN_LOCK_UNLOCKED(hfpll0.lock),
};
static struct hfpll_data hfpll1_data = {
.mode_reg = 0x3240,
.l_reg = 0x3248,
.m_reg = 0x324c,
.n_reg = 0x3250,
.config_reg = 0x3244,
.status_reg = 0x325c,
.config_val = 0x7845c665,
.droop_reg = 0x3314,
.droop_val = 0x0108c000,
.min_rate = 600000000UL,
.max_rate = 1800000000UL,
};
static struct clk_hfpll hfpll1 = {
.d = &hfpll1_data,
.clkr.hw.init = &(struct clk_init_data){
.parent_names = (const char *[]){ "pxo" },
.num_parents = 1,
.name = "hfpll1",
.ops = &clk_ops_hfpll,
.flags = CLK_IGNORE_UNUSED,
},
.lock = __SPIN_LOCK_UNLOCKED(hfpll1.lock),
};
static struct hfpll_data hfpll_l2_data = {
.mode_reg = 0x3300,
.l_reg = 0x3308,
.m_reg = 0x330c,
.n_reg = 0x3310,
.config_reg = 0x3304,
.status_reg = 0x331c,
.config_val = 0x7845c665,
.droop_reg = 0x3314,
.droop_val = 0x0108c000,
.min_rate = 600000000UL,
.max_rate = 1800000000UL,
};
static struct clk_hfpll hfpll_l2 = {
.d = &hfpll_l2_data,
.clkr.hw.init = &(struct clk_init_data){
.parent_names = (const char *[]){ "pxo" },
.num_parents = 1,
.name = "hfpll_l2",
.ops = &clk_ops_hfpll,
.flags = CLK_IGNORE_UNUSED,
},
.lock = __SPIN_LOCK_UNLOCKED(hfpll_l2.lock),
};
static struct clk_pll pll14 = {
.l_reg = 0x31c4,
.m_reg = 0x31c8,
......@@ -2797,6 +2876,9 @@ static struct clk_regmap *gcc_ipq806x_clks[] = {
[UBI32_CORE2_CLK_SRC] = &ubi32_core2_src_clk.clkr,
[NSSTCM_CLK_SRC] = &nss_tcm_src.clkr,
[NSSTCM_CLK] = &nss_tcm_clk.clkr,
[PLL9] = &hfpll0.clkr,
[PLL10] = &hfpll1.clkr,
[PLL12] = &hfpll_l2.clkr,
};
static const struct qcom_reset_map gcc_ipq806x_resets[] = {
......
......@@ -30,6 +30,7 @@
#include "clk-pll.h"
#include "clk-rcg.h"
#include "clk-branch.h"
#include "clk-hfpll.h"
#include "reset.h"
static struct clk_pll pll3 = {
......@@ -86,6 +87,164 @@ static struct clk_regmap pll8_vote = {
},
};
static struct hfpll_data hfpll0_data = {
.mode_reg = 0x3200,
.l_reg = 0x3208,
.m_reg = 0x320c,
.n_reg = 0x3210,
.config_reg = 0x3204,
.status_reg = 0x321c,
.config_val = 0x7845c665,
.droop_reg = 0x3214,
.droop_val = 0x0108c000,
.min_rate = 600000000UL,
.max_rate = 1800000000UL,
};
static struct clk_hfpll hfpll0 = {
.d = &hfpll0_data,
.clkr.hw.init = &(struct clk_init_data){
.parent_names = (const char *[]){ "pxo" },
.num_parents = 1,
.name = "hfpll0",
.ops = &clk_ops_hfpll,
.flags = CLK_IGNORE_UNUSED,
},
.lock = __SPIN_LOCK_UNLOCKED(hfpll0.lock),
};
static struct hfpll_data hfpll1_8064_data = {
.mode_reg = 0x3240,
.l_reg = 0x3248,
.m_reg = 0x324c,
.n_reg = 0x3250,
.config_reg = 0x3244,
.status_reg = 0x325c,
.config_val = 0x7845c665,
.droop_reg = 0x3254,
.droop_val = 0x0108c000,
.min_rate = 600000000UL,
.max_rate = 1800000000UL,
};
static struct hfpll_data hfpll1_data = {
.mode_reg = 0x3300,
.l_reg = 0x3308,
.m_reg = 0x330c,
.n_reg = 0x3310,
.config_reg = 0x3304,
.status_reg = 0x331c,
.config_val = 0x7845c665,
.droop_reg = 0x3314,
.droop_val = 0x0108c000,
.min_rate = 600000000UL,
.max_rate = 1800000000UL,
};
static struct clk_hfpll hfpll1 = {
.d = &hfpll1_data,
.clkr.hw.init = &(struct clk_init_data){
.parent_names = (const char *[]){ "pxo" },
.num_parents = 1,
.name = "hfpll1",
.ops = &clk_ops_hfpll,
.flags = CLK_IGNORE_UNUSED,
},
.lock = __SPIN_LOCK_UNLOCKED(hfpll1.lock),
};
static struct hfpll_data hfpll2_data = {
.mode_reg = 0x3280,
.l_reg = 0x3288,
.m_reg = 0x328c,
.n_reg = 0x3290,
.config_reg = 0x3284,
.status_reg = 0x329c,
.config_val = 0x7845c665,
.droop_reg = 0x3294,
.droop_val = 0x0108c000,
.min_rate = 600000000UL,
.max_rate = 1800000000UL,
};
static struct clk_hfpll hfpll2 = {
.d = &hfpll2_data,
.clkr.hw.init = &(struct clk_init_data){
.parent_names = (const char *[]){ "pxo" },
.num_parents = 1,
.name = "hfpll2",
.ops = &clk_ops_hfpll,
.flags = CLK_IGNORE_UNUSED,
},
.lock = __SPIN_LOCK_UNLOCKED(hfpll2.lock),
};
static struct hfpll_data hfpll3_data = {
.mode_reg = 0x32c0,
.l_reg = 0x32c8,
.m_reg = 0x32cc,
.n_reg = 0x32d0,
.config_reg = 0x32c4,
.status_reg = 0x32dc,
.config_val = 0x7845c665,
.droop_reg = 0x32d4,
.droop_val = 0x0108c000,
.min_rate = 600000000UL,
.max_rate = 1800000000UL,
};
static struct clk_hfpll hfpll3 = {
.d = &hfpll3_data,
.clkr.hw.init = &(struct clk_init_data){
.parent_names = (const char *[]){ "pxo" },
.num_parents = 1,
.name = "hfpll3",
.ops = &clk_ops_hfpll,
.flags = CLK_IGNORE_UNUSED,
},
.lock = __SPIN_LOCK_UNLOCKED(hfpll3.lock),
};
static struct hfpll_data hfpll_l2_8064_data = {
.mode_reg = 0x3300,
.l_reg = 0x3308,
.m_reg = 0x330c,
.n_reg = 0x3310,
.config_reg = 0x3304,
.status_reg = 0x331c,
.config_val = 0x7845c665,
.droop_reg = 0x3314,
.droop_val = 0x0108c000,
.min_rate = 600000000UL,
.max_rate = 1800000000UL,
};
static struct hfpll_data hfpll_l2_data = {
.mode_reg = 0x3400,
.l_reg = 0x3408,
.m_reg = 0x340c,
.n_reg = 0x3410,
.config_reg = 0x3404,
.status_reg = 0x341c,
.config_val = 0x7845c665,
.droop_reg = 0x3414,
.droop_val = 0x0108c000,
.min_rate = 600000000UL,
.max_rate = 1800000000UL,
};
static struct clk_hfpll hfpll_l2 = {
.d = &hfpll_l2_data,
.clkr.hw.init = &(struct clk_init_data){
.parent_names = (const char *[]){ "pxo" },
.num_parents = 1,
.name = "hfpll_l2",
.ops = &clk_ops_hfpll,
.flags = CLK_IGNORE_UNUSED,
},
.lock = __SPIN_LOCK_UNLOCKED(hfpll_l2.lock),
};
static struct clk_pll pll14 = {
.l_reg = 0x31c4,
.m_reg = 0x31c8,
......@@ -3107,6 +3266,9 @@ static struct clk_regmap *gcc_msm8960_clks[] = {
[PMIC_ARB1_H_CLK] = &pmic_arb1_h_clk.clkr,
[PMIC_SSBI2_CLK] = &pmic_ssbi2_clk.clkr,
[RPM_MSG_RAM_H_CLK] = &rpm_msg_ram_h_clk.clkr,
[PLL9] = &hfpll0.clkr,
[PLL10] = &hfpll1.clkr,
[PLL12] = &hfpll_l2.clkr,
};
static const struct qcom_reset_map gcc_msm8960_resets[] = {
......@@ -3318,6 +3480,11 @@ static struct clk_regmap *gcc_apq8064_clks[] = {
[PMIC_ARB1_H_CLK] = &pmic_arb1_h_clk.clkr,
[PMIC_SSBI2_CLK] = &pmic_ssbi2_clk.clkr,
[RPM_MSG_RAM_H_CLK] = &rpm_msg_ram_h_clk.clkr,
[PLL9] = &hfpll0.clkr,
[PLL10] = &hfpll1.clkr,
[PLL12] = &hfpll_l2.clkr,
[PLL16] = &hfpll2.clkr,
[PLL17] = &hfpll3.clkr,
};
static const struct qcom_reset_map gcc_apq8064_resets[] = {
......@@ -3477,6 +3644,11 @@ static int gcc_msm8960_probe(struct platform_device *pdev)
if (ret)
return ret;
if (match->data == &gcc_apq8064_desc) {
hfpll1.d = &hfpll1_8064_data;
hfpll_l2.d = &hfpll_l2_8064_data;
}
tsens = platform_device_register_data(&pdev->dev, "qcom-tsens", -1,
NULL, 0);
if (IS_ERR(tsens))
......
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2018, The Linux Foundation. All rights reserved.
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/regmap.h>
#include "clk-regmap.h"
#include "clk-hfpll.h"
static const struct hfpll_data hdata = {
.mode_reg = 0x00,
.l_reg = 0x04,
.m_reg = 0x08,
.n_reg = 0x0c,
.user_reg = 0x10,
.config_reg = 0x14,
.config_val = 0x430405d,
.status_reg = 0x1c,
.lock_bit = 16,
.user_val = 0x8,
.user_vco_mask = 0x100000,
.low_vco_max_rate = 1248000000,
.min_rate = 537600000UL,
.max_rate = 2900000000UL,
};
static const struct of_device_id qcom_hfpll_match_table[] = {
{ .compatible = "qcom,hfpll" },
{ }
};
MODULE_DEVICE_TABLE(of, qcom_hfpll_match_table);
static const struct regmap_config hfpll_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.max_register = 0x30,
.fast_io = true,
};
static int qcom_hfpll_probe(struct platform_device *pdev)
{
struct resource *res;
struct device *dev = &pdev->dev;
void __iomem *base;
struct regmap *regmap;
struct clk_hfpll *h;
struct clk_init_data init = {
.parent_names = (const char *[]){ "xo" },
.num_parents = 1,
.ops = &clk_ops_hfpll,
};
h = devm_kzalloc(dev, sizeof(*h), GFP_KERNEL);
if (!h)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(dev, res);
if (IS_ERR(base))
return PTR_ERR(base);
regmap = devm_regmap_init_mmio(&pdev->dev, base, &hfpll_regmap_config);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
if (of_property_read_string_index(dev->of_node, "clock-output-names",
0, &init.name))
return -ENODEV;
h->d = &hdata;
h->clkr.hw.init = &init;
spin_lock_init(&h->lock);
return devm_clk_register_regmap(&pdev->dev, &h->clkr);
}
static struct platform_driver qcom_hfpll_driver = {
.probe = qcom_hfpll_probe,
.driver = {
.name = "qcom-hfpll",
.of_match_table = qcom_hfpll_match_table,
},
};
module_platform_driver(qcom_hfpll_driver);
MODULE_DESCRIPTION("QCOM HFPLL Clock Driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:qcom-hfpll");
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2018, The Linux Foundation. All rights reserved.
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
static const char *aux_parents[] = {
"pll8_vote",
"pxo",
};
static unsigned int aux_parent_map[] = {
3,
0,
};
static const struct of_device_id kpss_xcc_match_table[] = {
{ .compatible = "qcom,kpss-acc-v1", .data = (void *)1UL },
{ .compatible = "qcom,kpss-gcc" },
{}
};
MODULE_DEVICE_TABLE(of, kpss_xcc_match_table);
static int kpss_xcc_driver_probe(struct platform_device *pdev)
{
const struct of_device_id *id;
struct clk *clk;
struct resource *res;
void __iomem *base;
const char *name;
id = of_match_device(kpss_xcc_match_table, &pdev->dev);
if (!id)
return -ENODEV;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(base))
return PTR_ERR(base);
if (id->data) {
if (of_property_read_string_index(pdev->dev.of_node,
"clock-output-names",
0, &name))
return -ENODEV;
base += 0x14;
} else {
name = "acpu_l2_aux";
base += 0x28;
}
clk = clk_register_mux_table(&pdev->dev, name, aux_parents,
ARRAY_SIZE(aux_parents), 0, base, 0, 0x3,
0, aux_parent_map, NULL);
platform_set_drvdata(pdev, clk);
return PTR_ERR_OR_ZERO(clk);
}
static int kpss_xcc_driver_remove(struct platform_device *pdev)
{
clk_unregister_mux(platform_get_drvdata(pdev));
return 0;
}
static struct platform_driver kpss_xcc_driver = {
.probe = kpss_xcc_driver_probe,
.remove = kpss_xcc_driver_remove,
.driver = {
.name = "kpss-xcc",
.of_match_table = kpss_xcc_match_table,
},
};
module_platform_driver(kpss_xcc_driver);
MODULE_DESCRIPTION("Krait Processor Sub System (KPSS) Clock Driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:kpss-xcc");
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2018, The Linux Foundation. All rights reserved.
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/slab.h>
#include "clk-krait.h"
static unsigned int sec_mux_map[] = {
2,
0,
};
static unsigned int pri_mux_map[] = {
1,
2,
0,
};
/*
* Notifier function for switching the muxes to safe parent
* while the hfpll is getting reprogrammed.
*/
static int krait_notifier_cb(struct notifier_block *nb,
unsigned long event,
void *data)
{
int ret = 0;
struct krait_mux_clk *mux = container_of(nb, struct krait_mux_clk,
clk_nb);
/* Switch to safe parent */
if (event == PRE_RATE_CHANGE) {
mux->old_index = krait_mux_clk_ops.get_parent(&mux->hw);
ret = krait_mux_clk_ops.set_parent(&mux->hw, mux->safe_sel);
mux->reparent = false;
/*
* By the time POST_RATE_CHANGE notifier is called,
* clk framework itself would have changed the parent for the new rate.
* Only otherwise, put back to the old parent.
*/
} else if (event == POST_RATE_CHANGE) {
if (!mux->reparent)
ret = krait_mux_clk_ops.set_parent(&mux->hw,
mux->old_index);
}
return notifier_from_errno(ret);
}
static int krait_notifier_register(struct device *dev, struct clk *clk,
struct krait_mux_clk *mux)
{
int ret = 0;
mux->clk_nb.notifier_call = krait_notifier_cb;
ret = clk_notifier_register(clk, &mux->clk_nb);
if (ret)
dev_err(dev, "failed to register clock notifier: %d\n", ret);
return ret;
}
static int
krait_add_div(struct device *dev, int id, const char *s, unsigned int offset)
{
struct krait_div2_clk *div;
struct clk_init_data init = {
.num_parents = 1,
.ops = &krait_div2_clk_ops,
.flags = CLK_SET_RATE_PARENT,
};
const char *p_names[1];
struct clk *clk;
div = devm_kzalloc(dev, sizeof(*div), GFP_KERNEL);
if (!div)
return -ENOMEM;
div->width = 2;
div->shift = 6;
div->lpl = id >= 0;
div->offset = offset;
div->hw.init = &init;
init.name = kasprintf(GFP_KERNEL, "hfpll%s_div", s);
if (!init.name)
return -ENOMEM;
init.parent_names = p_names;
p_names[0] = kasprintf(GFP_KERNEL, "hfpll%s", s);
if (!p_names[0]) {
kfree(init.name);
return -ENOMEM;
}
clk = devm_clk_register(dev, &div->hw);
kfree(p_names[0]);
kfree(init.name);
return PTR_ERR_OR_ZERO(clk);
}
static int
krait_add_sec_mux(struct device *dev, int id, const char *s,
unsigned int offset, bool unique_aux)
{
int ret;
struct krait_mux_clk *mux;
static const char *sec_mux_list[] = {
"acpu_aux",
"qsb",
};
struct clk_init_data init = {
.parent_names = sec_mux_list,
.num_parents = ARRAY_SIZE(sec_mux_list),
.ops = &krait_mux_clk_ops,
.flags = CLK_SET_RATE_PARENT,
};
struct clk *clk;
mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
if (!mux)
return -ENOMEM;
mux->offset = offset;
mux->lpl = id >= 0;
mux->mask = 0x3;
mux->shift = 2;
mux->parent_map = sec_mux_map;
mux->hw.init = &init;
mux->safe_sel = 0;
init.name = kasprintf(GFP_KERNEL, "krait%s_sec_mux", s);
if (!init.name)
return -ENOMEM;
if (unique_aux) {
sec_mux_list[0] = kasprintf(GFP_KERNEL, "acpu%s_aux", s);
if (!sec_mux_list[0]) {
clk = ERR_PTR(-ENOMEM);
goto err_aux;
}
}
clk = devm_clk_register(dev, &mux->hw);
ret = krait_notifier_register(dev, clk, mux);
if (ret)
goto unique_aux;
unique_aux:
if (unique_aux)
kfree(sec_mux_list[0]);
err_aux:
kfree(init.name);
return PTR_ERR_OR_ZERO(clk);
}
static struct clk *
krait_add_pri_mux(struct device *dev, int id, const char *s,
unsigned int offset)
{
int ret;
struct krait_mux_clk *mux;
const char *p_names[3];
struct clk_init_data init = {
.parent_names = p_names,
.num_parents = ARRAY_SIZE(p_names),
.ops = &krait_mux_clk_ops,
.flags = CLK_SET_RATE_PARENT,
};
struct clk *clk;
mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
if (!mux)
return ERR_PTR(-ENOMEM);
mux->mask = 0x3;
mux->shift = 0;
mux->offset = offset;
mux->lpl = id >= 0;
mux->parent_map = pri_mux_map;
mux->hw.init = &init;
mux->safe_sel = 2;
init.name = kasprintf(GFP_KERNEL, "krait%s_pri_mux", s);
if (!init.name)
return ERR_PTR(-ENOMEM);
p_names[0] = kasprintf(GFP_KERNEL, "hfpll%s", s);
if (!p_names[0]) {
clk = ERR_PTR(-ENOMEM);
goto err_p0;
}
p_names[1] = kasprintf(GFP_KERNEL, "hfpll%s_div", s);
if (!p_names[1]) {
clk = ERR_PTR(-ENOMEM);
goto err_p1;
}
p_names[2] = kasprintf(GFP_KERNEL, "krait%s_sec_mux", s);
if (!p_names[2]) {
clk = ERR_PTR(-ENOMEM);
goto err_p2;
}
clk = devm_clk_register(dev, &mux->hw);
ret = krait_notifier_register(dev, clk, mux);
if (ret)
goto err_p3;
err_p3:
kfree(p_names[2]);
err_p2:
kfree(p_names[1]);
err_p1:
kfree(p_names[0]);
err_p0:
kfree(init.name);
return clk;
}
/* id < 0 for L2, otherwise id == physical CPU number */
static struct clk *krait_add_clks(struct device *dev, int id, bool unique_aux)
{
int ret;
unsigned int offset;
void *p = NULL;
const char *s;
struct clk *clk;
if (id >= 0) {
offset = 0x4501 + (0x1000 * id);
s = p = kasprintf(GFP_KERNEL, "%d", id);
if (!s)
return ERR_PTR(-ENOMEM);
} else {
offset = 0x500;
s = "_l2";
}
ret = krait_add_div(dev, id, s, offset);
if (ret) {
clk = ERR_PTR(ret);
goto err;
}
ret = krait_add_sec_mux(dev, id, s, offset, unique_aux);
if (ret) {
clk = ERR_PTR(ret);
goto err;
}
clk = krait_add_pri_mux(dev, id, s, offset);
err:
kfree(p);
return clk;
}
static struct clk *krait_of_get(struct of_phandle_args *clkspec, void *data)
{
unsigned int idx = clkspec->args[0];
struct clk **clks = data;
if (idx >= 5) {
pr_err("%s: invalid clock index %d\n", __func__, idx);
return ERR_PTR(-EINVAL);
}
return clks[idx] ? : ERR_PTR(-ENODEV);
}
static const struct of_device_id krait_cc_match_table[] = {
{ .compatible = "qcom,krait-cc-v1", (void *)1UL },
{ .compatible = "qcom,krait-cc-v2" },
{}
};
MODULE_DEVICE_TABLE(of, krait_cc_match_table);
static int krait_cc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
const struct of_device_id *id;
unsigned long cur_rate, aux_rate;
int cpu;
struct clk *clk;
struct clk **clks;
struct clk *l2_pri_mux_clk;
id = of_match_device(krait_cc_match_table, dev);
if (!id)
return -ENODEV;
/* Rate is 1 because 0 causes problems for __clk_mux_determine_rate */
clk = clk_register_fixed_rate(dev, "qsb", NULL, 0, 1);
if (IS_ERR(clk))
return PTR_ERR(clk);
if (!id->data) {
clk = clk_register_fixed_factor(dev, "acpu_aux",
"gpll0_vote", 0, 1, 2);
if (IS_ERR(clk))
return PTR_ERR(clk);
}
/* Krait configurations have at most 4 CPUs and one L2 */
clks = devm_kcalloc(dev, 5, sizeof(*clks), GFP_KERNEL);
if (!clks)
return -ENOMEM;
for_each_possible_cpu(cpu) {
clk = krait_add_clks(dev, cpu, id->data);
if (IS_ERR(clk))
return PTR_ERR(clk);
clks[cpu] = clk;
}
l2_pri_mux_clk = krait_add_clks(dev, -1, id->data);
if (IS_ERR(l2_pri_mux_clk))
return PTR_ERR(l2_pri_mux_clk);
clks[4] = l2_pri_mux_clk;
/*
* We don't want the CPU or L2 clocks to be turned off at late init
* if CPUFREQ or HOTPLUG configs are disabled. So, bump up the
* refcount of these clocks. Any cpufreq/hotplug manager can assume
* that the clocks have already been prepared and enabled by the time
* they take over.
*/
for_each_online_cpu(cpu) {
clk_prepare_enable(l2_pri_mux_clk);
WARN(clk_prepare_enable(clks[cpu]),
"Unable to turn on CPU%d clock", cpu);
}
/*
* Force reinit of HFPLLs and muxes to overwrite any potential
* incorrect configuration of HFPLLs and muxes by the bootloader.
* While at it, also make sure the cores are running at known rates
* and print the current rate.
*
* The clocks are set to aux clock rate first to make sure the
* secondary mux is not sourcing off of QSB. The rate is then set to
* two different rates to force a HFPLL reinit under all
* circumstances.
*/
cur_rate = clk_get_rate(l2_pri_mux_clk);
aux_rate = 384000000;
if (cur_rate == 1) {
pr_info("L2 @ QSB rate. Forcing new rate.\n");
cur_rate = aux_rate;
}
clk_set_rate(l2_pri_mux_clk, aux_rate);
clk_set_rate(l2_pri_mux_clk, 2);
clk_set_rate(l2_pri_mux_clk, cur_rate);
pr_info("L2 @ %lu KHz\n", clk_get_rate(l2_pri_mux_clk) / 1000);
for_each_possible_cpu(cpu) {
clk = clks[cpu];
cur_rate = clk_get_rate(clk);
if (cur_rate == 1) {
pr_info("CPU%d @ QSB rate. Forcing new rate.\n", cpu);
cur_rate = aux_rate;
}
clk_set_rate(clk, aux_rate);
clk_set_rate(clk, 2);
clk_set_rate(clk, cur_rate);
pr_info("CPU%d @ %lu KHz\n", cpu, clk_get_rate(clk) / 1000);
}
of_clk_add_provider(dev->of_node, krait_of_get, clks);
return 0;
}
static struct platform_driver krait_cc_driver = {
.probe = krait_cc_probe,
.driver = {
.name = "krait-cc",
.of_match_table = krait_cc_match_table,
},
};
module_platform_driver(krait_cc_driver);
MODULE_DESCRIPTION("Krait CPU Clock Driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:krait-cc");
......@@ -80,16 +80,12 @@ static long rockchip_ddrclk_sip_round_rate(struct clk_hw *hw,
static u8 rockchip_ddrclk_get_parent(struct clk_hw *hw)
{
struct rockchip_ddrclk *ddrclk = to_rockchip_ddrclk_hw(hw);
int num_parents = clk_hw_get_num_parents(hw);
u32 val;
val = clk_readl(ddrclk->reg_base +
ddrclk->mux_offset) >> ddrclk->mux_shift;
val &= GENMASK(ddrclk->mux_width - 1, 0);
if (val >= num_parents)
return -EINVAL;
return val;
}
......
......@@ -645,7 +645,7 @@ static struct rockchip_clk_branch rk3066a_clk_branches[] __initdata = {
GATE(HCLK_I2S1, "hclk_i2s1", "hclk_cpu", 0, RK2928_CLKGATE_CON(7), 3, GFLAGS),
GATE(HCLK_I2S2, "hclk_i2s2", "hclk_cpu", 0, RK2928_CLKGATE_CON(7), 4, GFLAGS),
GATE(HCLK_CIF1, "hclk_cif1", "hclk_cpu", 0, RK2928_CLKGATE_CON(6), 6, GFLAGS),
GATE(0, "hclk_hdmi", "hclk_cpu", 0, RK2928_CLKGATE_CON(4), 14, GFLAGS),
GATE(HCLK_HDMI, "hclk_hdmi", "hclk_cpu", 0, RK2928_CLKGATE_CON(4), 14, GFLAGS),
GATE(HCLK_OTG1, "hclk_usbotg1", "hclk_peri", CLK_IGNORE_UNUSED,
RK2928_CLKGATE_CON(5), 14, GFLAGS),
......
......@@ -83,22 +83,43 @@ static struct rockchip_pll_rate_table rk3288_pll_rates[] = {
RK3066_PLL_RATE( 768000000, 1, 64, 2),
RK3066_PLL_RATE( 742500000, 8, 495, 2),
RK3066_PLL_RATE( 696000000, 1, 58, 2),
RK3066_PLL_RATE_NB(621000000, 1, 207, 8, 1),
RK3066_PLL_RATE( 600000000, 1, 50, 2),
RK3066_PLL_RATE_NB(594000000, 1, 198, 8, 1),
RK3066_PLL_RATE( 552000000, 1, 46, 2),
RK3066_PLL_RATE( 504000000, 1, 84, 4),
RK3066_PLL_RATE( 500000000, 3, 125, 2),
RK3066_PLL_RATE( 456000000, 1, 76, 4),
RK3066_PLL_RATE( 428000000, 1, 107, 6),
RK3066_PLL_RATE( 408000000, 1, 68, 4),
RK3066_PLL_RATE( 400000000, 3, 100, 2),
RK3066_PLL_RATE_NB( 394000000, 1, 197, 12, 1),
RK3066_PLL_RATE( 384000000, 2, 128, 4),
RK3066_PLL_RATE( 360000000, 1, 60, 4),
RK3066_PLL_RATE_NB( 356000000, 1, 178, 12, 1),
RK3066_PLL_RATE_NB( 324000000, 1, 189, 14, 1),
RK3066_PLL_RATE( 312000000, 1, 52, 4),
RK3066_PLL_RATE( 300000000, 1, 50, 4),
RK3066_PLL_RATE( 297000000, 2, 198, 8),
RK3066_PLL_RATE_NB( 308000000, 1, 154, 12, 1),
RK3066_PLL_RATE_NB( 303000000, 1, 202, 16, 1),
RK3066_PLL_RATE( 300000000, 1, 75, 6),
RK3066_PLL_RATE_NB( 297750000, 2, 397, 16, 1),
RK3066_PLL_RATE_NB( 293250000, 2, 391, 16, 1),
RK3066_PLL_RATE_NB( 292500000, 1, 195, 16, 1),
RK3066_PLL_RATE( 273600000, 1, 114, 10),
RK3066_PLL_RATE_NB( 273000000, 1, 182, 16, 1),
RK3066_PLL_RATE_NB( 270000000, 1, 180, 16, 1),
RK3066_PLL_RATE_NB( 266250000, 2, 355, 16, 1),
RK3066_PLL_RATE_NB( 256500000, 1, 171, 16, 1),
RK3066_PLL_RATE( 252000000, 1, 84, 8),
RK3066_PLL_RATE( 216000000, 1, 72, 8),
RK3066_PLL_RATE( 148500000, 2, 99, 8),
RK3066_PLL_RATE_NB( 250500000, 1, 167, 16, 1),
RK3066_PLL_RATE_NB( 243428571, 1, 142, 14, 1),
RK3066_PLL_RATE( 238000000, 1, 119, 12),
RK3066_PLL_RATE_NB( 219750000, 2, 293, 16, 1),
RK3066_PLL_RATE_NB( 216000000, 1, 144, 16, 1),
RK3066_PLL_RATE_NB( 213000000, 1, 142, 16, 1),
RK3066_PLL_RATE( 195428571, 1, 114, 14),
RK3066_PLL_RATE( 160000000, 1, 80, 12),
RK3066_PLL_RATE( 157500000, 1, 105, 16),
RK3066_PLL_RATE( 126000000, 1, 84, 16),
RK3066_PLL_RATE( 48000000, 1, 64, 32),
{ /* sentinel */ },
......
......@@ -813,22 +813,22 @@ static struct rockchip_clk_branch rk3328_clk_branches[] __initdata = {
MMC(SCLK_SDMMC_DRV, "sdmmc_drv", "clk_sdmmc",
RK3328_SDMMC_CON0, 1),
MMC(SCLK_SDMMC_SAMPLE, "sdmmc_sample", "clk_sdmmc",
RK3328_SDMMC_CON1, 1),
RK3328_SDMMC_CON1, 0),
MMC(SCLK_SDIO_DRV, "sdio_drv", "clk_sdio",
RK3328_SDIO_CON0, 1),
MMC(SCLK_SDIO_SAMPLE, "sdio_sample", "clk_sdio",
RK3328_SDIO_CON1, 1),
RK3328_SDIO_CON1, 0),
MMC(SCLK_EMMC_DRV, "emmc_drv", "clk_emmc",
RK3328_EMMC_CON0, 1),
MMC(SCLK_EMMC_SAMPLE, "emmc_sample", "clk_emmc",
RK3328_EMMC_CON1, 1),
RK3328_EMMC_CON1, 0),
MMC(SCLK_SDMMC_EXT_DRV, "sdmmc_ext_drv", "clk_sdmmc_ext",
RK3328_SDMMC_EXT_CON0, 1),
MMC(SCLK_SDMMC_EXT_SAMPLE, "sdmmc_ext_sample", "clk_sdmmc_ext",
RK3328_SDMMC_EXT_CON1, 1),
RK3328_SDMMC_EXT_CON1, 0),
};
static const char *const rk3328_critical_clocks[] __initconst = {
......
......@@ -273,6 +273,7 @@
#define IMX6QDL_CLK_MLB_PODF 260
#define IMX6QDL_CLK_EPIT1 261
#define IMX6QDL_CLK_EPIT2 262
#define IMX6QDL_CLK_END 263
#define IMX6QDL_CLK_MMDC_P0_IPG 263
#define IMX6QDL_CLK_END 264
#endif /* __DT_BINDINGS_CLOCK_IMX6QDL_H */
......@@ -175,6 +175,8 @@
#define IMX6SL_CLK_SSI2_IPG 162
#define IMX6SL_CLK_SSI3_IPG 163
#define IMX6SL_CLK_SPDIF_GCLK 164
#define IMX6SL_CLK_END 165
#define IMX6SL_CLK_MMDC_P0_IPG 165
#define IMX6SL_CLK_MMDC_P1_IPG 166
#define IMX6SL_CLK_END 167
#endif /* __DT_BINDINGS_CLOCK_IMX6SL_H */
......@@ -203,7 +203,8 @@
#define IMX6SLL_CLK_GPIO4 176
#define IMX6SLL_CLK_GPIO5 177
#define IMX6SLL_CLK_GPIO6 178
#define IMX6SLL_CLK_MMDC_P1_IPG 179
#define IMX6SLL_CLK_END 179
#define IMX6SLL_CLK_END 180
#endif /* __DT_BINDINGS_CLOCK_IMX6SLL_H */
......@@ -279,6 +279,7 @@
#define IMX6SX_CLK_LVDS2_OUT 266
#define IMX6SX_CLK_LVDS2_IN 267
#define IMX6SX_CLK_ANACLK2 268
#define IMX6SX_CLK_CLK_END 269
#define IMX6SX_CLK_MMDC_P1_IPG 269
#define IMX6SX_CLK_CLK_END 270
#endif /* __DT_BINDINGS_CLOCK_IMX6SX_H */
......@@ -259,7 +259,8 @@
#define IMX6UL_CLK_GPIO3 246
#define IMX6UL_CLK_GPIO4 247
#define IMX6UL_CLK_GPIO5 248
#define IMX6UL_CLK_MMDC_P1_IPG 249
#define IMX6UL_CLK_END 249
#define IMX6UL_CLK_END 250
#endif /* __DT_BINDINGS_CLOCK_IMX6UL_H */
......@@ -319,5 +319,7 @@
#define CE3_SRC 303
#define CE3_CORE_CLK 304
#define CE3_H_CLK 305
#define PLL16 306
#define PLL17 307
#endif
......@@ -139,8 +139,9 @@
#define HCLK_CIF1 470
#define HCLK_VEPU 471
#define HCLK_VDPU 472
#define HCLK_HDMI 473
#define CLK_NR_CLKS (HCLK_VDPU + 1)
#define CLK_NR_CLKS (HCLK_HDMI + 1)
/* soft-reset indices */
#define SRST_MCORE 2
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment