diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-08-24 08:46:03 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-08-24 08:46:03 -0700 |
| commit | cf9610f9116d55c5a66ef9f1faecacabd93a9322 (patch) | |
| tree | 757a3d280760c654767631cab5ad8af2d86d1848 /drivers/clk/ux500/clk-sysctrl.c | |
| download | linux-stable-cf9610f9116d55c5a66ef9f1faecacabd93a9322.tar.gz linux-stable-cf9610f9116d55c5a66ef9f1faecacabd93a9322.zip | |
Merge tag 'pinctrl-v7.3-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-pinctrlgrafted
Pull pin control updates from Linus Walleij:
"Core changes:
- Use the non-blocking mux_state_try_select() in the generic MUX pin
control back-end
- Free pin maps on pinctrl_generic_to_map() failure in the core
helpers
New hardware pin control:
- Qualcomm Maili TLMM SoC, PMG1110 PMIC, and Eliza LPASS LPI TLMM SoC
(this is for the low power audio, LPASS = Low Power Audio SubSystem
portions)
- Upboard support on top of the Intel pin controller
- Apple T6030 and T6031 support (just compatible strings)
- Samsung Exynos 8855 SoC
- Mediatek MT6858 SoC
- Rockchip RK3308B and RV1106 SoCs
- Airoha EN7523 and AN7563 SoCs
Improvements:
- Qualcomm TLMM GPIOs are unconditionally marked as wakeup capable
- Qualcomm improvements on top of the Qualcomm PDC (power domain
controller) irqchip improvements from tglx:s tree
- Qualcomm IPQ806x and IPQ9650 non-urgent pin grop fixups
- The NPCM8xx driver has been put into shape fixing many pin and
group definitions
- Some non-urgent Tegra 264 pin assignments are fixed up
- Some non-urgent fixups to Airoha AN7581 and AN7583 registers and
pin assignments, missing features etc"
* tag 'pinctrl-v7.3-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-pinctrl: (130 commits)
dt-bindings: pinctrl: Convert TI DA850 pupd to DT schema
pinctrl: meson: sync some modify from A4
pinctrl: meson: a4: Add input enable pin configuration
pinctrl: realtek: rtd1625: remove unused group name spdif_sel
pinctrl: realtek: rtd1625: fix base_bit for VE4 GPIO 13
dt-bindings: pinctrl: microchip,pic32mzda-pinctrl: Convert to DT schema
Revert "Merge branch 'ib-rsk7204' into devel"
pinctrl: rockchip: Add RV1106 pinctrl support
dt-bindings: pinctrl: rockchip: Add RV1106 compatible
pinctrl: rockchip: Decode drive strength in the get function
pinctrl: fix PINCTRL_GENERIC_MUX not always being selectable
pinctrl: airoha: add support of an7563 SoC
dt-bindings: pinctrl: airoha: add support of an7563 pin controller
pinctrl: airoha: try to find chip scu node by phandle first
pinctrl: airoha: add support of en7523 SoC
dt-bindings: pinctrl: airoha: add support of en7523 pin controller
pinctrl: airoha: an7583: add support for olt pinmux
pinctrl: airoha: an7583: add support for pon_alt pinmux
pinctrl: airoha: an7583: add support for npu_uart pinmux
dt-bindings: pinctrl: airoha: an7583: add missed features
...
Diffstat (limited to 'drivers/clk/ux500/clk-sysctrl.c')
| -rw-r--r-- | drivers/clk/ux500/clk-sysctrl.c | 226 |
1 files changed, 226 insertions, 0 deletions
diff --git a/drivers/clk/ux500/clk-sysctrl.c b/drivers/clk/ux500/clk-sysctrl.c new file mode 100644 index 000000000..ba3258c88 --- /dev/null +++ b/drivers/clk/ux500/clk-sysctrl.c @@ -0,0 +1,226 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Sysctrl clock implementation for ux500 platform. + * + * Copyright (C) 2013 ST-Ericsson SA + * Author: Ulf Hansson <ulf.hansson@linaro.org> + */ + +#include <linux/clk-provider.h> +#include <linux/mfd/abx500/ab8500-sysctrl.h> +#include <linux/device.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/err.h> +#include "clk.h" + +#define SYSCTRL_MAX_NUM_PARENTS 4 + +#define to_clk_sysctrl(_hw) container_of(_hw, struct clk_sysctrl, hw) + +struct clk_sysctrl { + struct clk_hw hw; + struct device *dev; + u8 parent_index; + u16 reg_sel[SYSCTRL_MAX_NUM_PARENTS]; + u8 reg_mask[SYSCTRL_MAX_NUM_PARENTS]; + u8 reg_bits[SYSCTRL_MAX_NUM_PARENTS]; + unsigned long rate; + unsigned long enable_delay_us; +}; + +/* Sysctrl clock operations. */ + +static int clk_sysctrl_prepare(struct clk_hw *hw) +{ + int ret; + struct clk_sysctrl *clk = to_clk_sysctrl(hw); + + ret = ab8500_sysctrl_write(clk->reg_sel[0], clk->reg_mask[0], + clk->reg_bits[0]); + + if (!ret && clk->enable_delay_us) + usleep_range(clk->enable_delay_us, clk->enable_delay_us + + (clk->enable_delay_us >> 2)); + + return ret; +} + +static void clk_sysctrl_unprepare(struct clk_hw *hw) +{ + struct clk_sysctrl *clk = to_clk_sysctrl(hw); + if (ab8500_sysctrl_clear(clk->reg_sel[0], clk->reg_mask[0])) + dev_err(clk->dev, "clk_sysctrl: %s fail to clear %s.\n", + __func__, clk_hw_get_name(hw)); +} + +static unsigned long clk_sysctrl_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_sysctrl *clk = to_clk_sysctrl(hw); + return clk->rate; +} + +static int clk_sysctrl_set_parent(struct clk_hw *hw, u8 index) +{ + struct clk_sysctrl *clk = to_clk_sysctrl(hw); + u8 old_index = clk->parent_index; + int ret = 0; + + if (clk->reg_sel[old_index]) { + ret = ab8500_sysctrl_clear(clk->reg_sel[old_index], + clk->reg_mask[old_index]); + if (ret) + return ret; + } + + if (clk->reg_sel[index]) { + ret = ab8500_sysctrl_write(clk->reg_sel[index], + clk->reg_mask[index], + clk->reg_bits[index]); + if (ret) { + if (clk->reg_sel[old_index]) + ab8500_sysctrl_write(clk->reg_sel[old_index], + clk->reg_mask[old_index], + clk->reg_bits[old_index]); + return ret; + } + } + clk->parent_index = index; + + return ret; +} + +static u8 clk_sysctrl_get_parent(struct clk_hw *hw) +{ + struct clk_sysctrl *clk = to_clk_sysctrl(hw); + return clk->parent_index; +} + +static const struct clk_ops clk_sysctrl_gate_ops = { + .prepare = clk_sysctrl_prepare, + .unprepare = clk_sysctrl_unprepare, +}; + +static const struct clk_ops clk_sysctrl_gate_fixed_rate_ops = { + .prepare = clk_sysctrl_prepare, + .unprepare = clk_sysctrl_unprepare, + .recalc_rate = clk_sysctrl_recalc_rate, +}; + +static const struct clk_ops clk_sysctrl_set_parent_ops = { + .determine_rate = clk_hw_determine_rate_no_reparent, + .set_parent = clk_sysctrl_set_parent, + .get_parent = clk_sysctrl_get_parent, +}; + +static struct clk *clk_reg_sysctrl(struct device *dev, + const char *name, + const char **parent_names, + u8 num_parents, + u16 *reg_sel, + u8 *reg_mask, + u8 *reg_bits, + unsigned long rate, + unsigned long enable_delay_us, + unsigned long flags, + const struct clk_ops *clk_sysctrl_ops) +{ + struct clk_sysctrl *clk; + struct clk_init_data clk_sysctrl_init; + struct clk *clk_reg; + int i; + + if (!dev) + return ERR_PTR(-EINVAL); + + if (!name || (num_parents > SYSCTRL_MAX_NUM_PARENTS)) { + dev_err(dev, "clk_sysctrl: invalid arguments passed\n"); + return ERR_PTR(-EINVAL); + } + + clk = devm_kzalloc(dev, sizeof(*clk), GFP_KERNEL); + if (!clk) + return ERR_PTR(-ENOMEM); + + /* set main clock registers */ + clk->reg_sel[0] = reg_sel[0]; + clk->reg_bits[0] = reg_bits[0]; + clk->reg_mask[0] = reg_mask[0]; + + /* handle clocks with more than one parent */ + for (i = 1; i < num_parents; i++) { + clk->reg_sel[i] = reg_sel[i]; + clk->reg_bits[i] = reg_bits[i]; + clk->reg_mask[i] = reg_mask[i]; + } + + clk->parent_index = 0; + clk->rate = rate; + clk->enable_delay_us = enable_delay_us; + clk->dev = dev; + + clk_sysctrl_init.name = name; + clk_sysctrl_init.ops = clk_sysctrl_ops; + clk_sysctrl_init.flags = flags; + clk_sysctrl_init.parent_names = parent_names; + clk_sysctrl_init.num_parents = num_parents; + clk->hw.init = &clk_sysctrl_init; + + clk_reg = devm_clk_register(clk->dev, &clk->hw); + if (IS_ERR(clk_reg)) + dev_err(dev, "clk_sysctrl: clk_register failed\n"); + + return clk_reg; +} + +struct clk *clk_reg_sysctrl_gate(struct device *dev, + const char *name, + const char *parent_name, + u16 reg_sel, + u8 reg_mask, + u8 reg_bits, + unsigned long enable_delay_us, + unsigned long flags) +{ + const char **parent_names = (parent_name ? &parent_name : NULL); + u8 num_parents = (parent_name ? 1 : 0); + + return clk_reg_sysctrl(dev, name, parent_names, num_parents, + ®_sel, ®_mask, ®_bits, 0, enable_delay_us, + flags, &clk_sysctrl_gate_ops); +} + +struct clk *clk_reg_sysctrl_gate_fixed_rate(struct device *dev, + const char *name, + const char *parent_name, + u16 reg_sel, + u8 reg_mask, + u8 reg_bits, + unsigned long rate, + unsigned long enable_delay_us, + unsigned long flags) +{ + const char **parent_names = (parent_name ? &parent_name : NULL); + u8 num_parents = (parent_name ? 1 : 0); + + return clk_reg_sysctrl(dev, name, parent_names, num_parents, + ®_sel, ®_mask, ®_bits, + rate, enable_delay_us, flags, + &clk_sysctrl_gate_fixed_rate_ops); +} + +struct clk *clk_reg_sysctrl_set_parent(struct device *dev, + const char *name, + const char **parent_names, + u8 num_parents, + u16 *reg_sel, + u8 *reg_mask, + u8 *reg_bits, + unsigned long flags) +{ + return clk_reg_sysctrl(dev, name, parent_names, num_parents, + reg_sel, reg_mask, reg_bits, 0, 0, flags, + &clk_sysctrl_set_parent_ops); +} |
