diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2023-10-31 18:42:56 -1000 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2023-10-31 18:42:56 -1000 |
commit | fe4ae2fab00b4751265580c5865fdf23b62d80b3 (patch) | |
tree | faeb8e5fb6d7f8165d1bfde357d22f0d96ea754e /drivers/clk/clk-twl.c | |
parent | c52894359395ea0a562b3ed556848ed66fbfff86 (diff) | |
parent | 0a6d7f8275f255eda823c0f0b61d024f6f5b483d (diff) |
Merge tag 'clk-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/clk/linux
Pull clk driver updates from Stephen Boyd:
"Herein lies a smallish collection of clk driver updates and some core
clk framework changes for the merge window. The core framework changes
are only improving the debugfs interface to allow phase adjustments
and report which consumers of a clk there are. These are most likely
only of interest to kernel developers.
On the clk driver side, it's a ghastly amount of updates with only a
handful of new clk drivers. We have a couple new clk drivers for
Qualcomm, per usual, and a driver for Renesas, Amlogic, and TI
respectively. The updates are spread throughout the clk drivers.
Some highlights are fixing kunit tests for different configurations
like lockdep and big-endian, avoiding integer overflow in rate
settable clks, moving clk_hw_onecell_data to the end of allocations so
that drivers don't corrupt their private data, and migrating clk
drivers to the regmap maple tree. Otherwise it's the usual fixes to
clk drivers that only come along with testing the drivers on real
hardware.
New Drivers:
- Add clock driver for TWL6032
- Initial support for the Qualcomm SM4450 Global Clock Controller and
SM4450 RPMh clock controllers
- Add Camera Clock Controller on Qualcomm SM8550
- Add support for the Renesas RZ/G3S (R9A08G045) SoC
- Add Amlogic s4 main clock controller support
Updates:
- Make clk kunit tests work with lockdep
- Fix clk gate kunit test for big-endian
- Convert more than a handful of clk drivers to use regmap maple tree
- Consider the CLK_FRAC_DIVIDER_ZERO_BASED in fractional divider clk
implementation
- Add consumer info to clk debugfs
- Fix various clk drivers that have clk_hw_onecell_data not at the
end of an allocation
- Drop CLK_SET_RATE_PARENT for clocks with fixed-rate GPLLs across a
variety of Qualcomm IPQ platforms
- Add missing parent of APCS PLL on Qualcomm IPQ6018
- Add I2C QUP6 clk on Qualcomm IPQ6018 but mark it critical to avoid
problems with RPM
- Implement safe source switching for a53pll and use on Qualcomm
IPQ5332
- Add support for Stromer Plus PLLs to Qualcomm clk driver
- Switch Qualcomm SM8550 Video and GPU clock controllers to use OLE
PLL configure method
- Non critical fixes to halt bit checks in Qualcomm clk drivers
- Add SMMU GDSC for Qualcomm MSM8998
- Fix possible integer overflow in Qualcomm RCG frequency calculation
code
- Remove RPM managed clks from Qualcomm MSM8996 GCC driver
- Add HFPLL configuration for the three HFPLLs in Qualcomm MSM8976
- Switch Qualcomm MSM8996 CBF clock driver's remove function to
return void
- Fix missing dependency for s4 clock controllers
- Select MXC_CLK when building in the CLK_IMX8QXP
- Fixes for error handling paths in i.MX8 ACM driver
- Move the clocks check in i.MX8 ACM driver in order to log any error
- Drop the unused return value of clk_imx_acm_detach_pm_domains
- Drop non-existant IMX8MP_CLK_AUDIOMIX_PDM_ROOT clock
- Fix error handling in i.MX8MQ clock driver
- Allow a different LCDIF1 clock parent if DT describes it for
i.MX6SX
- Keep the SCU resource table sorted in the i.MX8DXL rsrc driver
- Move the elcdif PLL clock registration above lcd_clk, as it is its
parent
- Correct some ENET specific clocks for i.MX8DXL platform
- Drop the VPU_UART and VPUCORE from i.MX8QM as latest HW revision
doesn't have them
- Remove "de-featured" MLB support from i.MX8QM/QXP/DXL platforms
- Skip registering clocks owned by Cortex-A partition SCU-based
platforms
- Add CAN_1/2 to i.MX8QM and M4_0, PI_0_PWM_0 and PI_0_I2C_0 to
i.MX8QXP resources"
* tag 'clk-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/clk/linux: (128 commits)
clk: Fix clk gate kunit test on big-endian CPUs
clk: si521xx: Increase stack based print buffer size in probe
clk: mediatek: fix double free in mtk_clk_register_pllfh()
clk: socfpga: agilex: Add bounds-checking coverage for struct stratix10_clock_data
clk: socfpga: Fix undefined behavior bug in struct stratix10_clock_data
clk: sifive: Allow building the driver as a module
clk: analogbits: Allow building the library as a module
clk: sprd: Composite driver support offset config
clk: Allow phase adjustment from debugfs
clk: Show active consumers of clocks in debugfs
clk: Use device_get_match_data()
clk: visconti: Add bounds-checking coverage for struct visconti_pll_provider
clk: visconti: Fix undefined behavior bug in struct visconti_pll_provider
clk: cdce925: Extend match support for OF tables
clk: si570: Simplify probe
clk: si5351: Simplify probe
clk: rs9: Use i2c_get_match_data() instead of device_get_match_data()
clk: clk-si544: Simplify probe() and is_valid_frequency()
clk: si521xx: Use i2c_get_match_data() instead of device_get_match_data()
clk: meson: S4: select CONFIG_COMMON_CLK_MESON_CLKC_UTILS
...
Diffstat (limited to 'drivers/clk/clk-twl.c')
-rw-r--r-- | drivers/clk/clk-twl.c | 197 |
1 files changed, 197 insertions, 0 deletions
diff --git a/drivers/clk/clk-twl.c b/drivers/clk/clk-twl.c new file mode 100644 index 000000000000..eab9d3c8ed8a --- /dev/null +++ b/drivers/clk/clk-twl.c @@ -0,0 +1,197 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Clock driver for twl device. + * + * inspired by the driver for the Palmas device + */ + +#include <linux/clk-provider.h> +#include <linux/mfd/twl.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#define VREG_STATE 2 +#define TWL6030_CFG_STATE_OFF 0x00 +#define TWL6030_CFG_STATE_ON 0x01 +#define TWL6030_CFG_STATE_MASK 0x03 + +struct twl_clock_info { + struct device *dev; + u8 base; + struct clk_hw hw; +}; + +static inline int +twlclk_read(struct twl_clock_info *info, unsigned int slave_subgp, + unsigned int offset) +{ + u8 value; + int status; + + status = twl_i2c_read_u8(slave_subgp, &value, + info->base + offset); + return (status < 0) ? status : value; +} + +static inline int +twlclk_write(struct twl_clock_info *info, unsigned int slave_subgp, + unsigned int offset, u8 value) +{ + return twl_i2c_write_u8(slave_subgp, value, + info->base + offset); +} + +static inline struct twl_clock_info *to_twl_clks_info(struct clk_hw *hw) +{ + return container_of(hw, struct twl_clock_info, hw); +} + +static unsigned long twl_clks_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + return 32768; +} + +static int twl6032_clks_prepare(struct clk_hw *hw) +{ + struct twl_clock_info *cinfo = to_twl_clks_info(hw); + int ret; + + ret = twlclk_write(cinfo, TWL_MODULE_PM_RECEIVER, VREG_STATE, + TWL6030_CFG_STATE_ON); + if (ret < 0) + dev_err(cinfo->dev, "clk prepare failed\n"); + + return ret; +} + +static void twl6032_clks_unprepare(struct clk_hw *hw) +{ + struct twl_clock_info *cinfo = to_twl_clks_info(hw); + int ret; + + ret = twlclk_write(cinfo, TWL_MODULE_PM_RECEIVER, VREG_STATE, + TWL6030_CFG_STATE_OFF); + if (ret < 0) + dev_err(cinfo->dev, "clk unprepare failed\n"); +} + +static int twl6032_clks_is_prepared(struct clk_hw *hw) +{ + struct twl_clock_info *cinfo = to_twl_clks_info(hw); + int val; + + val = twlclk_read(cinfo, TWL_MODULE_PM_RECEIVER, VREG_STATE); + if (val < 0) { + dev_err(cinfo->dev, "clk read failed\n"); + return val; + } + + val &= TWL6030_CFG_STATE_MASK; + + return val == TWL6030_CFG_STATE_ON; +} + +static const struct clk_ops twl6032_clks_ops = { + .prepare = twl6032_clks_prepare, + .unprepare = twl6032_clks_unprepare, + .is_prepared = twl6032_clks_is_prepared, + .recalc_rate = twl_clks_recalc_rate, +}; + +struct twl_clks_data { + struct clk_init_data init; + u8 base; +}; + +static const struct twl_clks_data twl6032_clks[] = { + { + .init = { + .name = "clk32kg", + .ops = &twl6032_clks_ops, + .flags = CLK_IGNORE_UNUSED, + }, + .base = 0x8C, + }, + { + .init = { + .name = "clk32kaudio", + .ops = &twl6032_clks_ops, + .flags = CLK_IGNORE_UNUSED, + }, + .base = 0x8F, + }, + { + /* sentinel */ + } +}; + +static int twl_clks_probe(struct platform_device *pdev) +{ + struct clk_hw_onecell_data *clk_data; + const struct twl_clks_data *hw_data; + + struct twl_clock_info *cinfo; + int ret; + int i; + int count; + + hw_data = twl6032_clks; + for (count = 0; hw_data[count].init.name; count++) + ; + + clk_data = devm_kzalloc(&pdev->dev, + struct_size(clk_data, hws, count), + GFP_KERNEL); + if (!clk_data) + return -ENOMEM; + + clk_data->num = count; + cinfo = devm_kcalloc(&pdev->dev, count, sizeof(*cinfo), GFP_KERNEL); + if (!cinfo) + return -ENOMEM; + + for (i = 0; i < count; i++) { + cinfo[i].base = hw_data[i].base; + cinfo[i].dev = &pdev->dev; + cinfo[i].hw.init = &hw_data[i].init; + ret = devm_clk_hw_register(&pdev->dev, &cinfo[i].hw); + if (ret) { + return dev_err_probe(&pdev->dev, ret, + "Fail to register clock %s\n", + hw_data[i].init.name); + } + clk_data->hws[i] = &cinfo[i].hw; + } + + ret = devm_of_clk_add_hw_provider(&pdev->dev, + of_clk_hw_onecell_get, clk_data); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, + "Fail to add clock driver\n"); + + return 0; +} + +static const struct platform_device_id twl_clks_id[] = { + { + .name = "twl6032-clk", + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(platform, twl_clks_id); + +static struct platform_driver twl_clks_driver = { + .driver = { + .name = "twl-clk", + }, + .probe = twl_clks_probe, + .id_table = twl_clks_id, +}; + +module_platform_driver(twl_clks_driver); + +MODULE_DESCRIPTION("Clock driver for TWL Series Devices"); +MODULE_LICENSE("GPL"); |