"qcom,gcc-msm8974pro-ac"
"qcom,gcc-msm8994"
"qcom,gcc-msm8996"
+ "qcom,gcc-msm8998"
"qcom,gcc-mdm9615"
+ "qcom,gcc-sdm845"
- reg : shall contain base register location and length
- #clock-cells : shall contain 1
Say Y if you want to support multimedia devices such as display,
graphics, video encode/decode, camera, etc.
- Support for the global clock controller on msm8998 devices.
+config MSM_GCC_8998
+ tristate "MSM8998 Global Clock Controller"
+ depends on COMMON_CLK_QCOM
+ help
+ Support for the global clock controller on msm8998 devices.
+ Say Y if you want to use peripheral devices such as UART, SPI,
+ i2c, USB, UFS, SD/eMMC, PCIe, etc.
+
+ config SDM_GCC_845
+ tristate "SDM845 Global Clock Controller"
+ select QCOM_GDSC
+ depends on COMMON_CLK_QCOM
+ help
- I2C, USB, UFS, SDDC, PCIe, etc.
++ Support for the global clock controller on SDM845 devices.
+ Say Y if you want to use peripheral devices such as UART, SPI,
- Support for the video clock controller on Qualcomm Technologies, Inc
- SDM845 devices.
++ i2C, USB, UFS, SDDC, PCIe, etc.
+
+ config SDM_VIDEOCC_845
+ tristate "SDM845 Video Clock Controller"
+ depends on COMMON_CLK_QCOM
+ select SDM_GCC_845
+ select QCOM_GDSC
+ help
++ Support for the video clock controller on SDM845 devices.
+ Say Y if you want to support video devices and functionality such as
+ video encode and decode.
+
config SPMI_PMIC_CLKDIV
tristate "SPMI PMIC clkdiv Support"
depends on (COMMON_CLK_QCOM && SPMI) || COMPILE_TEST
obj-$(CONFIG_MSM_GCC_8994) += gcc-msm8994.o
obj-$(CONFIG_MSM_GCC_8996) += gcc-msm8996.o
obj-$(CONFIG_MSM_LCC_8960) += lcc-msm8960.o
+obj-$(CONFIG_MSM_GCC_8998) += gcc-msm8998.o
obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8960.o
obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.o
obj-$(CONFIG_MSM_MMCC_8996) += mmcc-msm8996.o
obj-$(CONFIG_QCOM_CLK_APCS_MSM8916) += apcs-msm8916.o
obj-$(CONFIG_QCOM_CLK_RPM) += clk-rpm.o
obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.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
+ // SPDX-License-Identifier: GPL-2.0
/*
- * Copyright (c) 2013, The Linux Foundation. All rights reserved.
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * Copyright (c) 2013, 2018, The Linux Foundation. All rights reserved.
*/
#include <linux/kernel.h>
#define CFG_MODE_SHIFT 12
#define CFG_MODE_MASK (0x3 << CFG_MODE_SHIFT)
#define CFG_MODE_DUAL_EDGE (0x2 << CFG_MODE_SHIFT)
+ #define CFG_HW_CLK_CTRL_MASK BIT(20)
#define M_REG 0x8
#define N_REG 0xc
clk_flags = clk_hw_get_flags(hw);
p = clk_hw_get_parent_by_index(hw, index);
if (clk_flags & CLK_SET_RATE_PARENT) {
+ rate = f->freq;
if (f->pre_div) {
rate /= 2;
rate *= f->pre_div + 1;
return _freq_tbl_determine_rate(hw, rcg->freq_tbl, req, FLOOR);
}
- static int clk_rcg2_configure(struct clk_rcg2 *rcg, const struct freq_tbl *f)
+ static int __clk_rcg2_configure(struct clk_rcg2 *rcg, const struct freq_tbl *f)
{
u32 cfg, mask;
struct clk_hw *hw = &rcg->clkr.hw;
}
mask = BIT(rcg->hid_width) - 1;
- mask |= CFG_SRC_SEL_MASK | CFG_MODE_MASK;
+ mask |= CFG_SRC_SEL_MASK | CFG_MODE_MASK | CFG_HW_CLK_CTRL_MASK;
cfg = f->pre_div << CFG_SRC_DIV_SHIFT;
cfg |= rcg->parent_map[index].cfg << CFG_SRC_SEL_SHIFT;
if (rcg->mnd_width && f->n && (f->m != f->n))
cfg |= CFG_MODE_DUAL_EDGE;
- ret = regmap_update_bits(rcg->clkr.regmap,
- rcg->cmd_rcgr + CFG_REG, mask, cfg);
+
+ return regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG,
+ mask, cfg);
+ }
+
+ static int clk_rcg2_configure(struct clk_rcg2 *rcg, const struct freq_tbl *f)
+ {
+ int ret;
+
+ ret = __clk_rcg2_configure(rcg, f);
if (ret)
return ret;
.determine_rate = clk_gfx3d_determine_rate,
};
EXPORT_SYMBOL_GPL(clk_gfx3d_ops);
+
+ static int clk_rcg2_set_force_enable(struct clk_hw *hw)
+ {
+ struct clk_rcg2 *rcg = to_clk_rcg2(hw);
+ const char *name = clk_hw_get_name(hw);
+ int ret, count;
+
+ ret = regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CMD_REG,
+ CMD_ROOT_EN, CMD_ROOT_EN);
+ if (ret)
+ return ret;
+
+ /* wait for RCG to turn ON */
+ for (count = 500; count > 0; count--) {
+ if (clk_rcg2_is_enabled(hw))
+ return 0;
+
+ udelay(1);
+ }
+
+ pr_err("%s: RCG did not turn on\n", name);
+ return -ETIMEDOUT;
+ }
+
+ static int clk_rcg2_clear_force_enable(struct clk_hw *hw)
+ {
+ struct clk_rcg2 *rcg = to_clk_rcg2(hw);
+
+ return regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CMD_REG,
+ CMD_ROOT_EN, 0);
+ }
+
+ static int
+ clk_rcg2_shared_force_enable_clear(struct clk_hw *hw, const struct freq_tbl *f)
+ {
+ struct clk_rcg2 *rcg = to_clk_rcg2(hw);
+ int ret;
+
+ ret = clk_rcg2_set_force_enable(hw);
+ if (ret)
+ return ret;
+
+ ret = clk_rcg2_configure(rcg, f);
+ if (ret)
+ return ret;
+
+ return clk_rcg2_clear_force_enable(hw);
+ }
+
+ static int clk_rcg2_shared_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+ {
+ struct clk_rcg2 *rcg = to_clk_rcg2(hw);
+ const struct freq_tbl *f;
+
+ f = qcom_find_freq(rcg->freq_tbl, rate);
+ if (!f)
+ return -EINVAL;
+
+ /*
+ * In case clock is disabled, update the CFG, M, N and D registers
+ * and don't hit the update bit of CMD register.
+ */
+ if (!__clk_is_enabled(hw->clk))
+ return __clk_rcg2_configure(rcg, f);
+
+ return clk_rcg2_shared_force_enable_clear(hw, f);
+ }
+
+ static int clk_rcg2_shared_set_rate_and_parent(struct clk_hw *hw,
+ unsigned long rate, unsigned long parent_rate, u8 index)
+ {
+ return clk_rcg2_shared_set_rate(hw, rate, parent_rate);
+ }
+
+ static int clk_rcg2_shared_enable(struct clk_hw *hw)
+ {
+ struct clk_rcg2 *rcg = to_clk_rcg2(hw);
+ int ret;
+
+ /*
+ * Set the update bit because required configuration has already
+ * been written in clk_rcg2_shared_set_rate()
+ */
+ ret = clk_rcg2_set_force_enable(hw);
+ if (ret)
+ return ret;
+
+ ret = update_config(rcg);
+ if (ret)
+ return ret;
+
+ return clk_rcg2_clear_force_enable(hw);
+ }
+
+ static void clk_rcg2_shared_disable(struct clk_hw *hw)
+ {
+ struct clk_rcg2 *rcg = to_clk_rcg2(hw);
+ u32 cfg;
+
+ /*
+ * Store current configuration as switching to safe source would clear
+ * the SRC and DIV of CFG register
+ */
+ regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, &cfg);
+
+ /*
+ * Park the RCG at a safe configuration - sourced off of safe source.
+ * Force enable and disable the RCG while configuring it to safeguard
+ * against any update signal coming from the downstream clock.
+ * The current parent is still prepared and enabled at this point, and
+ * the safe source is always on while application processor subsystem
+ * is online. Therefore, the RCG can safely switch its parent.
+ */
+ clk_rcg2_set_force_enable(hw);
+
+ regmap_write(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG,
+ rcg->safe_src_index << CFG_SRC_SEL_SHIFT);
+
+ update_config(rcg);
+
+ clk_rcg2_clear_force_enable(hw);
+
+ /* Write back the stored configuration corresponding to current rate */
+ regmap_write(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, cfg);
+ }
+
+ const struct clk_ops clk_rcg2_shared_ops = {
+ .enable = clk_rcg2_shared_enable,
+ .disable = clk_rcg2_shared_disable,
+ .get_parent = clk_rcg2_get_parent,
+ .set_parent = clk_rcg2_set_parent,
+ .recalc_rate = clk_rcg2_recalc_rate,
+ .determine_rate = clk_rcg2_determine_rate,
+ .set_rate = clk_rcg2_shared_set_rate,
+ .set_rate_and_parent = clk_rcg2_shared_set_rate_and_parent,
+ };
+ EXPORT_SYMBOL_GPL(clk_rcg2_shared_ops);
/*
- * Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015, 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
#define HW_CONTROL_MASK BIT(1)
#define SW_COLLAPSE_MASK BIT(0)
#define GMEM_CLAMP_IO_MASK BIT(0)
+ #define GMEM_RESET_MASK BIT(4)
+
+ /* CFG_GDSCR */
+ #define GDSC_POWER_UP_COMPLETE BIT(16)
+ #define GDSC_POWER_DOWN_COMPLETE BIT(15)
+ #define CFG_GDSCR_OFFSET 0x4
/* Wait 2^n CXO cycles between all states. Here, n=2 (4 cycles). */
#define EN_REST_WAIT_VAL (0x2 << 20)
#define RETAIN_MEM BIT(14)
#define RETAIN_PERIPH BIT(13)
- #define TIMEOUT_US 100
+ #define TIMEOUT_US 500
#define domain_to_gdsc(domain) container_of(domain, struct gdsc, pd)
- static int gdsc_is_enabled(struct gdsc *sc, unsigned int reg)
+ enum gdsc_status {
+ GDSC_OFF,
+ GDSC_ON
+ };
+
+ /* Returns 1 if GDSC status is status, 0 if not, and < 0 on error */
+ static int gdsc_check_status(struct gdsc *sc, enum gdsc_status status)
{
+ unsigned int reg;
u32 val;
int ret;
+ if (sc->flags & POLL_CFG_GDSCR)
+ reg = sc->gdscr + CFG_GDSCR_OFFSET;
+ else if (sc->gds_hw_ctrl)
+ reg = sc->gds_hw_ctrl;
+ else
+ reg = sc->gdscr;
+
ret = regmap_read(sc->regmap, reg, &val);
if (ret)
return ret;
- return !!(val & PWR_ON_MASK);
+ if (sc->flags & POLL_CFG_GDSCR) {
+ switch (status) {
+ case GDSC_ON:
+ return !!(val & GDSC_POWER_UP_COMPLETE);
+ case GDSC_OFF:
+ return !!(val & GDSC_POWER_DOWN_COMPLETE);
+ }
+ }
+
+ switch (status) {
+ case GDSC_ON:
+ return !!(val & PWR_ON_MASK);
+ case GDSC_OFF:
+ return !(val & PWR_ON_MASK);
+ }
+
+ return -EINVAL;
}
static int gdsc_hwctrl(struct gdsc *sc, bool en)
return regmap_update_bits(sc->regmap, sc->gdscr, HW_CONTROL_MASK, val);
}
- static int gdsc_poll_status(struct gdsc *sc, unsigned int reg, bool en)
+ static int gdsc_poll_status(struct gdsc *sc, enum gdsc_status status)
{
ktime_t start;
start = ktime_get();
do {
- if (gdsc_is_enabled(sc, reg) == en)
+ if (gdsc_check_status(sc, status))
return 0;
} while (ktime_us_delta(ktime_get(), start) < TIMEOUT_US);
- if (gdsc_is_enabled(sc, reg) == en)
+ if (gdsc_check_status(sc, status))
return 0;
return -ETIMEDOUT;
}
- static int gdsc_toggle_logic(struct gdsc *sc, bool en)
+ static int gdsc_toggle_logic(struct gdsc *sc, enum gdsc_status status)
{
int ret;
- u32 val = en ? 0 : SW_COLLAPSE_MASK;
- unsigned int status_reg = sc->gdscr;
+ u32 val = (status == GDSC_ON) ? 0 : SW_COLLAPSE_MASK;
ret = regmap_update_bits(sc->regmap, sc->gdscr, SW_COLLAPSE_MASK, val);
if (ret)
return ret;
/* If disabling votable gdscs, don't poll on status */
- if ((sc->flags & VOTABLE) && !en) {
+ if ((sc->flags & VOTABLE) && status == GDSC_OFF) {
/*
* Add a short delay here to ensure that an enable
* right after it was disabled does not put it in an
}
if (sc->gds_hw_ctrl) {
- status_reg = sc->gds_hw_ctrl;
/*
* The gds hw controller asserts/de-asserts the status bit soon
* after it receives a power on/off request from a master.
udelay(1);
}
- return gdsc_poll_status(sc, status_reg, en);
+ return gdsc_poll_status(sc, status);
}
static inline int gdsc_deassert_reset(struct gdsc *sc)
GMEM_CLAMP_IO_MASK, 1);
}
+ static inline void gdsc_assert_reset_aon(struct gdsc *sc)
+ {
+ regmap_update_bits(sc->regmap, sc->clamp_io_ctrl,
+ GMEM_RESET_MASK, 1);
+ udelay(1);
+ regmap_update_bits(sc->regmap, sc->clamp_io_ctrl,
+ GMEM_RESET_MASK, 0);
+ }
static int gdsc_enable(struct generic_pm_domain *domain)
{
struct gdsc *sc = domain_to_gdsc(domain);
if (sc->pwrsts == PWRSTS_ON)
return gdsc_deassert_reset(sc);
- if (sc->flags & CLAMP_IO)
+ if (sc->flags & SW_RESET) {
+ gdsc_assert_reset(sc);
+ udelay(1);
+ gdsc_deassert_reset(sc);
+ }
+
+ if (sc->flags & CLAMP_IO) {
+ if (sc->flags & AON_RESET)
+ gdsc_assert_reset_aon(sc);
gdsc_deassert_clamp_io(sc);
+ }
- ret = gdsc_toggle_logic(sc, true);
+ ret = gdsc_toggle_logic(sc, GDSC_ON);
if (ret)
return ret;
/* Turn off HW trigger mode if supported */
if (sc->flags & HW_CTRL) {
- unsigned int reg;
-
ret = gdsc_hwctrl(sc, false);
if (ret < 0)
return ret;
*/
udelay(1);
- reg = sc->gds_hw_ctrl ? sc->gds_hw_ctrl : sc->gdscr;
- ret = gdsc_poll_status(sc, reg, true);
+ ret = gdsc_poll_status(sc, GDSC_ON);
if (ret)
return ret;
}
if (sc->pwrsts & PWRSTS_OFF)
gdsc_clear_mem_on(sc);
- ret = gdsc_toggle_logic(sc, false);
+ ret = gdsc_toggle_logic(sc, GDSC_OFF);
if (ret)
return ret;
{
u32 mask, val;
int on, ret;
- unsigned int reg;
/*
* Disable HW trigger: collapse/restore occur based on registers writes.
/* Force gdsc ON if only ON state is supported */
if (sc->pwrsts == PWRSTS_ON) {
- ret = gdsc_toggle_logic(sc, true);
+ ret = gdsc_toggle_logic(sc, GDSC_ON);
if (ret)
return ret;
}
- reg = sc->gds_hw_ctrl ? sc->gds_hw_ctrl : sc->gdscr;
- on = gdsc_is_enabled(sc, reg);
+ on = gdsc_check_status(sc, GDSC_ON);
if (on < 0)
return on;
if ((sc->flags & VOTABLE) && on)
gdsc_enable(&sc->pd);
+ /* If ALWAYS_ON GDSCs are not ON, turn them ON */
+ if (sc->flags & ALWAYS_ON) {
+ if (!on)
+ gdsc_enable(&sc->pd);
+ on = true;
+ sc->pd.flags |= GENPD_FLAG_ALWAYS_ON;
+ }
+
if (on || (sc->pwrsts & PWRSTS_RET))
gdsc_force_mem_on(sc);
else
/*
- * Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015, 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
#define VOTABLE BIT(0)
#define CLAMP_IO BIT(1)
#define HW_CTRL BIT(2)
- #define ALWAYS_ON BIT(3)
+ #define SW_RESET BIT(3)
+ #define AON_RESET BIT(4)
+ #define POLL_CFG_GDSCR BIT(5)
++#define ALWAYS_ON BIT(6)
struct reset_controller_dev *rcdev;
unsigned int *resets;
unsigned int reset_count;