a4f9285520
clk driver support. We have the usual Qualcomm clk drivers, along with clk drivers for the Sophgo and T-Head vendors, all to support some new SoCs. Nothing in particular stands out to me in the updates. There's the interconnect clk driver which exposes clks as interconnects, crossing subsystems. There's a bunch of janitorial things that are improving drivers in general like kmemdup_array() or fixing error paths. But overall the updates look normal to fix the description data which is usually the stuff that's wrong and/or untested. I really wanted to land a bunch of KUnit clk code that I've been working on whenever I get some free time but it turned into a pumpkin at the last minute so I dropped those patches. I'll let it soak in linux-next after the merge window closes. I have a suspicion that we're going to need to totally rework the clk framework to fix structural issues like locking, clk rate setting, and runtime PM usage. Having a bunch of unit tests for that will help make sure that all keeps working. Core: - Skip gate basic type KUnit tests on s390 due to lack of MMIO emulation New Drivers: - AP sub-system clock controller in the T-Head TH1520 - Sophgo Sophon sg2042 clk driver - Qualcomm SM7150 camera, display and video clk drivers - Qualcomm QCM2290 GPU clk driver - Qualcomm QCS8386/QCS8084 NSS clk driver - Qualcomm SM8650 camera and video drivers Updates: - Add reset support to Airoha EN7581 clk driver - Add MODULE_DESCRIPTIONs to various clk drivers - Introduce helper logic to expose clock controllers as simple interconnect providers - Use the interconnect helper above on Qualcomm ipq9574 - Add CLK_SET_RATE_PARENT to the remaining USB pipe clocks on Qualcomm X1Elite - Improve error handling in Qualcomm kpss-xcc driver - Mark Qualcomm SC8280XP LPASS clock controller regmap_config const - Export more clocks for Rockchip rk3128 peripherals - Convert Rockchip clk drivers to use kmemdup_array() - Drop CLK_NR_CLKS from Rockchip rk3128 and rk3188 binding headers - Make qcom_cc_really_probe() take a struct device to allow reuse in non-platform-drivers - Introduce prepare-only branch clock ops in the qcom clk driver to support clocks on buses that take locks - Describe parent/child relationship for Qualcomm SC7280 camera GDSCs - Support Qualcomm Huayra 2290 alpha PLL - Adjust the highest SDCC clock frequency on Qualcomm IPQ6018 to match HS200 support - Add missing PCIe PIPE clocks on Qualcomm IPQ9574 - Fix various configurations and properties in the Qualcomm SA8775P, X1E80100 and SM7280 drivers - Park Qualcomm SM8350 GPU RCGs on XO while disabled - Remove unused CONFIG_QCOM_RPMCC Kconfig symbol - exynos-clkout: Remove usage of of_device_id table as .of_match_table, because the driver is instantiated as MFD cell, not as standalone platform driver. Populated .of_match_table confused people few times to convert the code to device_get_match_data(), which broke the driver - Mark one Samsung UFS clock as critical, because having it off stops the system from shutdown - Use kmemdup_array() when applicable - Remove unused 'struct gates_data' from old sunxi driver library - Add GPADC clock and reset for Allwinner H616 - Minor Amlogic S4 clock fixes - DT bindings Yaml conversion of the Amlogic AXG audio controller - Amlogic C3 clock controllers support - Amlogic clk flag added to skip init of already enabled PLLs and avoid relocking - Amlogic A1 DT bindings updates for system pll support - Add missing MODULE_DESCRIPTION where necessary - Remove obsolete clock DT binding header files - Add Battery Backup (VBATTB) and I2C clocks, resets, and power domains on Renesas RZ/G3S - Add audio clocks on Renesas R-Car V4M - Add video capture (ISPCS, CSI-2, VIN) clocks on Renesas R-Car V4M -----BEGIN PGP SIGNATURE----- iQJFBAABCAAvFiEE9L57QeeUxqYDyoaDrQKIl8bklSUFAmaZd3wRHHNib3lkQGtl cm5lbC5vcmcACgkQrQKIl8bklSVwCRAAz6leVJuGDmnyNvyq+BrXOHBI89/vAI1c ZejQLTKsXveI3fIQUTPAQ15XW1lRIPpPWRG09yZbVJ0P7WsNlfHA6KUjYiAaS9RN zkwLSI52ZulAhdRxBycIVMnfVOnoaJs4Vvp2jLdW+cRLj9BVwC1vXSDmWENvMrh2 Om7W1r3+Utg/nO3eRVdM3+LZTfveUd6PWZnz/zp20sZLZRUeDA5DKj8fqg0dHuvZ auZ8byeELp39rFJqE9YO5fDH+kmzXL3CAHz8s8NEDA+BBD9S4w+mvEMjHSQfQdnB LetpZ9DPoYscnWgYS/KWCiodCIAq6ThVkkcX1lAmndPQDwPCKVOoBomNuRaNZvI8 qJnP2ZhfWMbnrc291ECbPg82RjSOtp3ZzFij2T6jwDSsBc6pmJlwSwtvjtYC7fm+ N1Ldrl2qz6BYdbqJWXBRApFdqcI8Z3aENrqpy98LJiPdGdwmcbPA2cAnEPzJENdo ggTYXC//oVoyA6xnA1vwJQDVR0TAAu8mm3brW3uYww0T46R7HOMhtaNqIcEc1fQU 0k8mU4iW2xGQkeyR62afxDETKIe8/DMQUwoIwIZ1ogohHF+a8LmY+KnjAAmJK9LB yHhsQUOggmRe10vVfWxDSBZRyFuPblhCYTzytoAlEUs71jLJw7PF+CrF2ZZw8fP5 OEE2/O5+XXU= =jEjn -----END PGP SIGNATURE----- Merge tag 'clk-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/clk/linux Pull clk updates from Stephen Boyd: "This a large collection of clk driver updates and a handful of new SoC clk driver support. We have the usual Qualcomm clk drivers, along with clk drivers for the Sophgo and T-Head vendors, all to support some new SoCs. Nothing in particular stands out to me in the updates. There's the interconnect clk driver which exposes clks as interconnects, crossing subsystems. There's a bunch of janitorial things that are improving drivers in general like kmemdup_array() or fixing error paths. But overall the updates look normal to fix the description data which is usually the stuff that's wrong and/or untested. Core: - Skip gate basic type KUnit tests on s390 due to lack of MMIO emulation New Drivers: - AP sub-system clock controller in the T-Head TH1520 - Sophgo Sophon sg2042 clk driver - Qualcomm SM7150 camera, display and video clk drivers - Qualcomm QCM2290 GPU clk driver - Qualcomm QCS8386/QCS8084 NSS clk driver - Qualcomm SM8650 camera and video drivers Updates: - Add reset support to Airoha EN7581 clk driver - Add MODULE_DESCRIPTIONs to various clk drivers - Introduce helper logic to expose clock controllers as simple interconnect providers - Use the interconnect helper above on Qualcomm ipq9574 - Add CLK_SET_RATE_PARENT to the remaining USB pipe clocks on Qualcomm X1Elite - Improve error handling in Qualcomm kpss-xcc driver - Mark Qualcomm SC8280XP LPASS clock controller regmap_config const - Export more clocks for Rockchip rk3128 peripherals - Convert Rockchip clk drivers to use kmemdup_array() - Drop CLK_NR_CLKS from Rockchip rk3128 and rk3188 binding headers - Make qcom_cc_really_probe() take a struct device to allow reuse in non-platform-drivers - Introduce prepare-only branch clock ops in the qcom clk driver to support clocks on buses that take locks - Describe parent/child relationship for Qualcomm SC7280 camera GDSCs - Support Qualcomm Huayra 2290 alpha PLL - Adjust the highest SDCC clock frequency on Qualcomm IPQ6018 to match HS200 support - Add missing PCIe PIPE clocks on Qualcomm IPQ9574 - Fix various configurations and properties in the Qualcomm SA8775P, X1E80100 and SM7280 drivers - Park Qualcomm SM8350 GPU RCGs on XO while disabled - Remove unused CONFIG_QCOM_RPMCC Kconfig symbol - exynos-clkout: Remove usage of of_device_id table as .of_match_table, because the driver is instantiated as MFD cell, not as standalone platform driver. Populated .of_match_table confused people few times to convert the code to device_get_match_data(), which broke the driver - Mark one Samsung UFS clock as critical, because having it off stops the system from shutdown - Use kmemdup_array() when applicable - Remove unused 'struct gates_data' from old sunxi driver library - Add GPADC clock and reset for Allwinner H616 - Minor Amlogic S4 clock fixes - DT bindings Yaml conversion of the Amlogic AXG audio controller - Amlogic C3 clock controllers support - Amlogic clk flag added to skip init of already enabled PLLs and avoid relocking - Amlogic A1 DT bindings updates for system pll support - Add missing MODULE_DESCRIPTION where necessary - Remove obsolete clock DT binding header files - Add Battery Backup (VBATTB) and I2C clocks, resets, and power domains on Renesas RZ/G3S - Add audio clocks on Renesas R-Car V4M - Add video capture (ISPCS, CSI-2, VIN) clocks on Renesas R-Car V4M" * tag 'clk-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/clk/linux: (135 commits) clk: thead: Add support for T-Head TH1520 AP_SUBSYS clocks dt-bindings: clock: Document T-Head TH1520 AP_SUBSYS controller clk: sophgo: Avoid -Wsometimes-uninitialized in sg2042_clk_pll_set_rate() clk/sophgo: Using BUG() instead of unreachable() in mmux_get_parent_id() clk: mxs: Use clamp() in clk_ref_round_rate() and clk_ref_set_rate() clk: sunxi-ng r40: Constify struct regmap_config clk: en7523: fix rate divider for slic and spi clocks clk: lpc32xx: Constify struct regmap_config clk: xilinx: Constify struct regmap_config clk: en7523: Remove PCIe reset open drain configuration for EN7581 clk: en7523: Remove pcie prepare/unpreare callbacks for EN7581 SoC clk: en7523: Add reset-controller support for EN7581 SoC dt-bindings: clock: airoha: Add reset support to EN7581 clock binding dt-bindings: clock: mediatek: Document reset cells for MT8188 sys clk: mediatek: mt8173-infracfg: Handle unallocated infracfg when module dt-bindings: clock: mediatek: add syscon compatible for mt7622 pciesys dt-bindings: clock: sprd,sc9860-clk: convert to YAML dt-bindings: clock: qoriq-clock: convert to yaml format clk: qcom: Park shared RCGs upon registration clk: qcom: ipq9574: Use icc-clk for enabling NoC related clocks ...
258 lines
5.9 KiB
C
258 lines
5.9 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* Copyright 2016 Maxime Ripard
|
|
*
|
|
* Maxime Ripard <maxime.ripard@free-electrons.com>
|
|
*/
|
|
|
|
#include <linux/clk.h>
|
|
#include <linux/clk-provider.h>
|
|
#include <linux/device.h>
|
|
#include <linux/iopoll.h>
|
|
#include <linux/module.h>
|
|
#include <linux/slab.h>
|
|
|
|
#include "ccu_common.h"
|
|
#include "ccu_gate.h"
|
|
#include "ccu_reset.h"
|
|
|
|
struct sunxi_ccu {
|
|
const struct sunxi_ccu_desc *desc;
|
|
spinlock_t lock;
|
|
struct ccu_reset reset;
|
|
};
|
|
|
|
void ccu_helper_wait_for_lock(struct ccu_common *common, u32 lock)
|
|
{
|
|
void __iomem *addr;
|
|
u32 reg;
|
|
|
|
if (!lock)
|
|
return;
|
|
|
|
if (common->features & CCU_FEATURE_LOCK_REG)
|
|
addr = common->base + common->lock_reg;
|
|
else
|
|
addr = common->base + common->reg;
|
|
|
|
WARN_ON(readl_relaxed_poll_timeout(addr, reg, reg & lock, 100, 70000));
|
|
}
|
|
EXPORT_SYMBOL_NS_GPL(ccu_helper_wait_for_lock, SUNXI_CCU);
|
|
|
|
bool ccu_is_better_rate(struct ccu_common *common,
|
|
unsigned long target_rate,
|
|
unsigned long current_rate,
|
|
unsigned long best_rate)
|
|
{
|
|
unsigned long min_rate, max_rate;
|
|
|
|
clk_hw_get_rate_range(&common->hw, &min_rate, &max_rate);
|
|
|
|
if (current_rate > max_rate)
|
|
return false;
|
|
|
|
if (current_rate < min_rate)
|
|
return false;
|
|
|
|
if (common->features & CCU_FEATURE_CLOSEST_RATE)
|
|
return abs(current_rate - target_rate) < abs(best_rate - target_rate);
|
|
|
|
return current_rate <= target_rate && current_rate > best_rate;
|
|
}
|
|
EXPORT_SYMBOL_NS_GPL(ccu_is_better_rate, SUNXI_CCU);
|
|
|
|
/*
|
|
* This clock notifier is called when the frequency of a PLL clock is
|
|
* changed. In common PLL designs, changes to the dividers take effect
|
|
* almost immediately, while changes to the multipliers (implemented
|
|
* as dividers in the feedback loop) take a few cycles to work into
|
|
* the feedback loop for the PLL to stablize.
|
|
*
|
|
* Sometimes when the PLL clock rate is changed, the decrease in the
|
|
* divider is too much for the decrease in the multiplier to catch up.
|
|
* The PLL clock rate will spike, and in some cases, might lock up
|
|
* completely.
|
|
*
|
|
* This notifier callback will gate and then ungate the clock,
|
|
* effectively resetting it, so it proceeds to work. Care must be
|
|
* taken to reparent consumers to other temporary clocks during the
|
|
* rate change, and that this notifier callback must be the first
|
|
* to be registered.
|
|
*/
|
|
static int ccu_pll_notifier_cb(struct notifier_block *nb,
|
|
unsigned long event, void *data)
|
|
{
|
|
struct ccu_pll_nb *pll = to_ccu_pll_nb(nb);
|
|
int ret = 0;
|
|
|
|
if (event != POST_RATE_CHANGE)
|
|
goto out;
|
|
|
|
ccu_gate_helper_disable(pll->common, pll->enable);
|
|
|
|
ret = ccu_gate_helper_enable(pll->common, pll->enable);
|
|
if (ret)
|
|
goto out;
|
|
|
|
ccu_helper_wait_for_lock(pll->common, pll->lock);
|
|
|
|
out:
|
|
return notifier_from_errno(ret);
|
|
}
|
|
|
|
int ccu_pll_notifier_register(struct ccu_pll_nb *pll_nb)
|
|
{
|
|
pll_nb->clk_nb.notifier_call = ccu_pll_notifier_cb;
|
|
|
|
return clk_notifier_register(pll_nb->common->hw.clk,
|
|
&pll_nb->clk_nb);
|
|
}
|
|
EXPORT_SYMBOL_NS_GPL(ccu_pll_notifier_register, SUNXI_CCU);
|
|
|
|
static int sunxi_ccu_probe(struct sunxi_ccu *ccu, struct device *dev,
|
|
struct device_node *node, void __iomem *reg,
|
|
const struct sunxi_ccu_desc *desc)
|
|
{
|
|
struct ccu_reset *reset;
|
|
int i, ret;
|
|
|
|
ccu->desc = desc;
|
|
|
|
spin_lock_init(&ccu->lock);
|
|
|
|
for (i = 0; i < desc->num_ccu_clks; i++) {
|
|
struct ccu_common *cclk = desc->ccu_clks[i];
|
|
|
|
if (!cclk)
|
|
continue;
|
|
|
|
cclk->base = reg;
|
|
cclk->lock = &ccu->lock;
|
|
}
|
|
|
|
for (i = 0; i < desc->hw_clks->num ; i++) {
|
|
struct clk_hw *hw = desc->hw_clks->hws[i];
|
|
const char *name;
|
|
|
|
if (!hw)
|
|
continue;
|
|
|
|
name = hw->init->name;
|
|
if (dev)
|
|
ret = clk_hw_register(dev, hw);
|
|
else
|
|
ret = of_clk_hw_register(node, hw);
|
|
if (ret) {
|
|
pr_err("Couldn't register clock %d - %s\n", i, name);
|
|
goto err_clk_unreg;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < desc->num_ccu_clks; i++) {
|
|
struct ccu_common *cclk = desc->ccu_clks[i];
|
|
|
|
if (!cclk)
|
|
continue;
|
|
|
|
if (cclk->max_rate)
|
|
clk_hw_set_rate_range(&cclk->hw, cclk->min_rate,
|
|
cclk->max_rate);
|
|
else
|
|
WARN(cclk->min_rate,
|
|
"No max_rate, ignoring min_rate of clock %d - %s\n",
|
|
i, clk_hw_get_name(&cclk->hw));
|
|
}
|
|
|
|
ret = of_clk_add_hw_provider(node, of_clk_hw_onecell_get,
|
|
desc->hw_clks);
|
|
if (ret)
|
|
goto err_clk_unreg;
|
|
|
|
reset = &ccu->reset;
|
|
reset->rcdev.of_node = node;
|
|
reset->rcdev.ops = &ccu_reset_ops;
|
|
reset->rcdev.owner = dev ? dev->driver->owner : THIS_MODULE;
|
|
reset->rcdev.nr_resets = desc->num_resets;
|
|
reset->base = reg;
|
|
reset->lock = &ccu->lock;
|
|
reset->reset_map = desc->resets;
|
|
|
|
ret = reset_controller_register(&reset->rcdev);
|
|
if (ret)
|
|
goto err_del_provider;
|
|
|
|
return 0;
|
|
|
|
err_del_provider:
|
|
of_clk_del_provider(node);
|
|
err_clk_unreg:
|
|
while (--i >= 0) {
|
|
struct clk_hw *hw = desc->hw_clks->hws[i];
|
|
|
|
if (!hw)
|
|
continue;
|
|
clk_hw_unregister(hw);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static void devm_sunxi_ccu_release(struct device *dev, void *res)
|
|
{
|
|
struct sunxi_ccu *ccu = res;
|
|
const struct sunxi_ccu_desc *desc = ccu->desc;
|
|
int i;
|
|
|
|
reset_controller_unregister(&ccu->reset.rcdev);
|
|
of_clk_del_provider(dev->of_node);
|
|
|
|
for (i = 0; i < desc->hw_clks->num; i++) {
|
|
struct clk_hw *hw = desc->hw_clks->hws[i];
|
|
|
|
if (!hw)
|
|
continue;
|
|
clk_hw_unregister(hw);
|
|
}
|
|
}
|
|
|
|
int devm_sunxi_ccu_probe(struct device *dev, void __iomem *reg,
|
|
const struct sunxi_ccu_desc *desc)
|
|
{
|
|
struct sunxi_ccu *ccu;
|
|
int ret;
|
|
|
|
ccu = devres_alloc(devm_sunxi_ccu_release, sizeof(*ccu), GFP_KERNEL);
|
|
if (!ccu)
|
|
return -ENOMEM;
|
|
|
|
ret = sunxi_ccu_probe(ccu, dev, dev->of_node, reg, desc);
|
|
if (ret) {
|
|
devres_free(ccu);
|
|
return ret;
|
|
}
|
|
|
|
devres_add(dev, ccu);
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_NS_GPL(devm_sunxi_ccu_probe, SUNXI_CCU);
|
|
|
|
void of_sunxi_ccu_probe(struct device_node *node, void __iomem *reg,
|
|
const struct sunxi_ccu_desc *desc)
|
|
{
|
|
struct sunxi_ccu *ccu;
|
|
int ret;
|
|
|
|
ccu = kzalloc(sizeof(*ccu), GFP_KERNEL);
|
|
if (!ccu)
|
|
return;
|
|
|
|
ret = sunxi_ccu_probe(ccu, NULL, node, reg, desc);
|
|
if (ret) {
|
|
pr_err("%pOF: probing clocks failed: %d\n", node, ret);
|
|
kfree(ccu);
|
|
}
|
|
}
|
|
|
|
MODULE_DESCRIPTION("Common clock support for Allwinner SoCs");
|
|
MODULE_LICENSE("GPL");
|