Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge tag 'samsung-drivers-firmware-clk-6.19' of https://git.kernel.org/pub/scm/linux/kernel/git/krzk/linux into clk-samsung

Pull Samsung clk driver updates from Krzysztof Kozlowski:

Extend Samsung ACPM (Alive Clock and Power Manager) firmware drivers:

- Add support for passing DVFS (Dynamic Voltage and Frequency) messages
to configure the clocks in ACPM device.
- Add Exynos ACPM clock driver, which exposes to Linux several clocks
handled by the ACPM (firmware)

* tag 'samsung-drivers-firmware-clk-6.19' of https://git.kernel.org/pub/scm/linux/kernel/git/krzk/linux:
firmware: exynos-acpm: add empty method to allow compile test
MAINTAINERS: add ACPM clock bindings and driver
clk: samsung: add Exynos ACPM clock driver
firmware: exynos-acpm: register ACPM clocks pdev
firmware: exynos-acpm: add DVFS protocol
dt-bindings: firmware: google,gs101-acpm-ipc: add ACPM clocks

+384 -2
+11
Documentation/devicetree/bindings/firmware/google,gs101-acpm-ipc.yaml
··· 24 24 compatible: 25 25 const: google,gs101-acpm-ipc 26 26 27 + "#clock-cells": 28 + const: 1 29 + description: 30 + Clocks that are variable and index based. These clocks don't provide 31 + an entire range of values between the limits but only discrete points 32 + within the range. The firmware also manages the voltage scaling 33 + appropriately with the clock scaling. The argument is the ID of the 34 + clock contained by the firmware messages. 35 + 27 36 mboxes: 28 37 maxItems: 1 29 38 ··· 54 45 55 46 required: 56 47 - compatible 48 + - "#clock-cells" 57 49 - mboxes 58 50 - shmem 59 51 ··· 66 56 67 57 power-management { 68 58 compatible = "google,gs101-acpm-ipc"; 59 + #clock-cells = <1>; 69 60 mboxes = <&ap2apm_mailbox>; 70 61 shmem = <&apm_sram>; 71 62
+2 -1
MAINTAINERS
··· 10601 10601 F: arch/arm64/boot/dts/exynos/google/ 10602 10602 F: drivers/clk/samsung/clk-gs101.c 10603 10603 F: drivers/phy/samsung/phy-gs101-ufs.c 10604 - F: include/dt-bindings/clock/google,gs101.h 10604 + F: include/dt-bindings/clock/google,gs101* 10605 10605 K: [gG]oogle.?[tT]ensor 10606 10606 10607 10607 GPD FAN DRIVER ··· 22761 22761 L: linux-samsung-soc@vger.kernel.org 22762 22762 S: Supported 22763 22763 F: Documentation/devicetree/bindings/firmware/google,gs101-acpm-ipc.yaml 22764 + F: drivers/clk/samsung/clk-acpm.c 22764 22765 F: drivers/firmware/samsung/exynos-acpm* 22765 22766 F: include/linux/firmware/samsung/exynos-acpm-protocol.h 22766 22767
+10
drivers/clk/samsung/Kconfig
··· 95 95 status of the certains clocks from SoC, but it could also be tied to 96 96 other devices as an input clock. 97 97 98 + config EXYNOS_ACPM_CLK 99 + tristate "Clock driver controlled via ACPM interface" 100 + depends on EXYNOS_ACPM_PROTOCOL || (COMPILE_TEST && !EXYNOS_ACPM_PROTOCOL) 101 + help 102 + This driver provides support for clocks that are controlled by 103 + firmware that implements the ACPM interface. 104 + 105 + This driver uses the ACPM interface to interact with the firmware 106 + providing all the clock controlls. 107 + 98 108 config TESLA_FSD_COMMON_CLK 99 109 bool "Tesla FSD clock controller support" if COMPILE_TEST 100 110 depends on COMMON_CLK_SAMSUNG
+1
drivers/clk/samsung/Makefile
··· 28 28 obj-$(CONFIG_EXYNOS_ARM64_COMMON_CLK) += clk-exynosautov9.o 29 29 obj-$(CONFIG_EXYNOS_ARM64_COMMON_CLK) += clk-exynosautov920.o 30 30 obj-$(CONFIG_EXYNOS_ARM64_COMMON_CLK) += clk-gs101.o 31 + obj-$(CONFIG_EXYNOS_ACPM_CLK) += clk-acpm.o 31 32 obj-$(CONFIG_S3C64XX_COMMON_CLK) += clk-s3c64xx.o 32 33 obj-$(CONFIG_S5PV210_COMMON_CLK) += clk-s5pv210.o clk-s5pv210-audss.o 33 34 obj-$(CONFIG_TESLA_FSD_COMMON_CLK) += clk-fsd.o
+185
drivers/clk/samsung/clk-acpm.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Samsung Exynos ACPM protocol based clock driver. 4 + * 5 + * Copyright 2025 Linaro Ltd. 6 + */ 7 + 8 + #include <linux/array_size.h> 9 + #include <linux/clk-provider.h> 10 + #include <linux/container_of.h> 11 + #include <linux/device/devres.h> 12 + #include <linux/device.h> 13 + #include <linux/err.h> 14 + #include <linux/firmware/samsung/exynos-acpm-protocol.h> 15 + #include <linux/module.h> 16 + #include <linux/platform_device.h> 17 + #include <linux/types.h> 18 + 19 + struct acpm_clk { 20 + u32 id; 21 + struct clk_hw hw; 22 + unsigned int mbox_chan_id; 23 + const struct acpm_handle *handle; 24 + }; 25 + 26 + struct acpm_clk_variant { 27 + const char *name; 28 + }; 29 + 30 + struct acpm_clk_driver_data { 31 + const struct acpm_clk_variant *clks; 32 + unsigned int nr_clks; 33 + unsigned int mbox_chan_id; 34 + }; 35 + 36 + #define to_acpm_clk(clk) container_of(clk, struct acpm_clk, hw) 37 + 38 + #define ACPM_CLK(cname) \ 39 + { \ 40 + .name = cname, \ 41 + } 42 + 43 + static const struct acpm_clk_variant gs101_acpm_clks[] = { 44 + ACPM_CLK("mif"), 45 + ACPM_CLK("int"), 46 + ACPM_CLK("cpucl0"), 47 + ACPM_CLK("cpucl1"), 48 + ACPM_CLK("cpucl2"), 49 + ACPM_CLK("g3d"), 50 + ACPM_CLK("g3dl2"), 51 + ACPM_CLK("tpu"), 52 + ACPM_CLK("intcam"), 53 + ACPM_CLK("tnr"), 54 + ACPM_CLK("cam"), 55 + ACPM_CLK("mfc"), 56 + ACPM_CLK("disp"), 57 + ACPM_CLK("bo"), 58 + }; 59 + 60 + static const struct acpm_clk_driver_data acpm_clk_gs101 = { 61 + .clks = gs101_acpm_clks, 62 + .nr_clks = ARRAY_SIZE(gs101_acpm_clks), 63 + .mbox_chan_id = 0, 64 + }; 65 + 66 + static unsigned long acpm_clk_recalc_rate(struct clk_hw *hw, 67 + unsigned long parent_rate) 68 + { 69 + struct acpm_clk *clk = to_acpm_clk(hw); 70 + 71 + return clk->handle->ops.dvfs_ops.get_rate(clk->handle, 72 + clk->mbox_chan_id, clk->id); 73 + } 74 + 75 + static int acpm_clk_determine_rate(struct clk_hw *hw, 76 + struct clk_rate_request *req) 77 + { 78 + /* 79 + * We can't figure out what rate it will be, so just return the 80 + * rate back to the caller. acpm_clk_recalc_rate() will be called 81 + * after the rate is set and we'll know what rate the clock is 82 + * running at then. 83 + */ 84 + return 0; 85 + } 86 + 87 + static int acpm_clk_set_rate(struct clk_hw *hw, unsigned long rate, 88 + unsigned long parent_rate) 89 + { 90 + struct acpm_clk *clk = to_acpm_clk(hw); 91 + 92 + return clk->handle->ops.dvfs_ops.set_rate(clk->handle, 93 + clk->mbox_chan_id, clk->id, rate); 94 + } 95 + 96 + static const struct clk_ops acpm_clk_ops = { 97 + .recalc_rate = acpm_clk_recalc_rate, 98 + .determine_rate = acpm_clk_determine_rate, 99 + .set_rate = acpm_clk_set_rate, 100 + }; 101 + 102 + static int acpm_clk_register(struct device *dev, struct acpm_clk *aclk, 103 + const char *name) 104 + { 105 + struct clk_init_data init = {}; 106 + 107 + init.name = name; 108 + init.ops = &acpm_clk_ops; 109 + aclk->hw.init = &init; 110 + 111 + return devm_clk_hw_register(dev, &aclk->hw); 112 + } 113 + 114 + static int acpm_clk_probe(struct platform_device *pdev) 115 + { 116 + const struct acpm_handle *acpm_handle; 117 + struct clk_hw_onecell_data *clk_data; 118 + struct clk_hw **hws; 119 + struct device *dev = &pdev->dev; 120 + struct acpm_clk *aclks; 121 + unsigned int mbox_chan_id; 122 + int i, err, count; 123 + 124 + acpm_handle = devm_acpm_get_by_node(dev, dev->parent->of_node); 125 + if (IS_ERR(acpm_handle)) 126 + return dev_err_probe(dev, PTR_ERR(acpm_handle), 127 + "Failed to get acpm handle\n"); 128 + 129 + count = acpm_clk_gs101.nr_clks; 130 + mbox_chan_id = acpm_clk_gs101.mbox_chan_id; 131 + 132 + clk_data = devm_kzalloc(dev, struct_size(clk_data, hws, count), 133 + GFP_KERNEL); 134 + if (!clk_data) 135 + return -ENOMEM; 136 + 137 + clk_data->num = count; 138 + hws = clk_data->hws; 139 + 140 + aclks = devm_kcalloc(dev, count, sizeof(*aclks), GFP_KERNEL); 141 + if (!aclks) 142 + return -ENOMEM; 143 + 144 + for (i = 0; i < count; i++) { 145 + struct acpm_clk *aclk = &aclks[i]; 146 + 147 + /* 148 + * The code assumes the clock IDs start from zero, 149 + * are sequential and do not have gaps. 150 + */ 151 + aclk->id = i; 152 + aclk->handle = acpm_handle; 153 + aclk->mbox_chan_id = mbox_chan_id; 154 + 155 + hws[i] = &aclk->hw; 156 + 157 + err = acpm_clk_register(dev, aclk, 158 + acpm_clk_gs101.clks[i].name); 159 + if (err) 160 + return dev_err_probe(dev, err, 161 + "Failed to register clock\n"); 162 + } 163 + 164 + return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, 165 + clk_data); 166 + } 167 + 168 + static const struct platform_device_id acpm_clk_id[] = { 169 + { "gs101-acpm-clk" }, 170 + {} 171 + }; 172 + MODULE_DEVICE_TABLE(platform, acpm_clk_id); 173 + 174 + static struct platform_driver acpm_clk_driver = { 175 + .driver = { 176 + .name = "acpm-clocks", 177 + }, 178 + .probe = acpm_clk_probe, 179 + .id_table = acpm_clk_id, 180 + }; 181 + module_platform_driver(acpm_clk_driver); 182 + 183 + MODULE_AUTHOR("Tudor Ambarus <tudor.ambarus@linaro.org>"); 184 + MODULE_DESCRIPTION("Samsung Exynos ACPM clock driver"); 185 + MODULE_LICENSE("GPL");
+3 -1
drivers/firmware/samsung/Makefile
··· 1 1 # SPDX-License-Identifier: GPL-2.0-only 2 2 3 - acpm-protocol-objs := exynos-acpm.o exynos-acpm-pmic.o 3 + acpm-protocol-objs := exynos-acpm.o 4 + acpm-protocol-objs += exynos-acpm-pmic.o 5 + acpm-protocol-objs += exynos-acpm-dvfs.o 4 6 obj-$(CONFIG_EXYNOS_ACPM_PROTOCOL) += acpm-protocol.o
+80
drivers/firmware/samsung/exynos-acpm-dvfs.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Copyright 2020 Samsung Electronics Co., Ltd. 4 + * Copyright 2020 Google LLC. 5 + * Copyright 2025 Linaro Ltd. 6 + */ 7 + 8 + #include <linux/bitfield.h> 9 + #include <linux/firmware/samsung/exynos-acpm-protocol.h> 10 + #include <linux/ktime.h> 11 + #include <linux/types.h> 12 + #include <linux/units.h> 13 + 14 + #include "exynos-acpm.h" 15 + #include "exynos-acpm-dvfs.h" 16 + 17 + #define ACPM_DVFS_ID GENMASK(11, 0) 18 + #define ACPM_DVFS_REQ_TYPE GENMASK(15, 0) 19 + 20 + #define ACPM_DVFS_FREQ_REQ 0 21 + #define ACPM_DVFS_FREQ_GET 1 22 + 23 + static void acpm_dvfs_set_xfer(struct acpm_xfer *xfer, u32 *cmd, size_t cmdlen, 24 + unsigned int acpm_chan_id, bool response) 25 + { 26 + xfer->acpm_chan_id = acpm_chan_id; 27 + xfer->txd = cmd; 28 + xfer->txlen = cmdlen; 29 + 30 + if (response) { 31 + xfer->rxd = cmd; 32 + xfer->rxlen = cmdlen; 33 + } 34 + } 35 + 36 + static void acpm_dvfs_init_set_rate_cmd(u32 cmd[4], unsigned int clk_id, 37 + unsigned long rate) 38 + { 39 + cmd[0] = FIELD_PREP(ACPM_DVFS_ID, clk_id); 40 + cmd[1] = rate / HZ_PER_KHZ; 41 + cmd[2] = FIELD_PREP(ACPM_DVFS_REQ_TYPE, ACPM_DVFS_FREQ_REQ); 42 + cmd[3] = ktime_to_ms(ktime_get()); 43 + } 44 + 45 + int acpm_dvfs_set_rate(const struct acpm_handle *handle, 46 + unsigned int acpm_chan_id, unsigned int clk_id, 47 + unsigned long rate) 48 + { 49 + struct acpm_xfer xfer = {0}; 50 + u32 cmd[4]; 51 + 52 + acpm_dvfs_init_set_rate_cmd(cmd, clk_id, rate); 53 + acpm_dvfs_set_xfer(&xfer, cmd, sizeof(cmd), acpm_chan_id, false); 54 + 55 + return acpm_do_xfer(handle, &xfer); 56 + } 57 + 58 + static void acpm_dvfs_init_get_rate_cmd(u32 cmd[4], unsigned int clk_id) 59 + { 60 + cmd[0] = FIELD_PREP(ACPM_DVFS_ID, clk_id); 61 + cmd[2] = FIELD_PREP(ACPM_DVFS_REQ_TYPE, ACPM_DVFS_FREQ_GET); 62 + cmd[3] = ktime_to_ms(ktime_get()); 63 + } 64 + 65 + unsigned long acpm_dvfs_get_rate(const struct acpm_handle *handle, 66 + unsigned int acpm_chan_id, unsigned int clk_id) 67 + { 68 + struct acpm_xfer xfer; 69 + unsigned int cmd[4] = {0}; 70 + int ret; 71 + 72 + acpm_dvfs_init_get_rate_cmd(cmd, clk_id); 73 + acpm_dvfs_set_xfer(&xfer, cmd, sizeof(cmd), acpm_chan_id, true); 74 + 75 + ret = acpm_do_xfer(handle, &xfer); 76 + if (ret) 77 + return 0; 78 + 79 + return xfer.rxd[1] * HZ_PER_KHZ; 80 + }
+21
drivers/firmware/samsung/exynos-acpm-dvfs.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only */ 2 + /* 3 + * Copyright 2020 Samsung Electronics Co., Ltd. 4 + * Copyright 2020 Google LLC. 5 + * Copyright 2025 Linaro Ltd. 6 + */ 7 + #ifndef __EXYNOS_ACPM_DVFS_H__ 8 + #define __EXYNOS_ACPM_DVFS_H__ 9 + 10 + #include <linux/types.h> 11 + 12 + struct acpm_handle; 13 + 14 + int acpm_dvfs_set_rate(const struct acpm_handle *handle, 15 + unsigned int acpm_chan_id, unsigned int id, 16 + unsigned long rate); 17 + unsigned long acpm_dvfs_get_rate(const struct acpm_handle *handle, 18 + unsigned int acpm_chan_id, 19 + unsigned int clk_id); 20 + 21 + #endif /* __EXYNOS_ACPM_DVFS_H__ */
+26
drivers/firmware/samsung/exynos-acpm.c
··· 29 29 #include <linux/types.h> 30 30 31 31 #include "exynos-acpm.h" 32 + #include "exynos-acpm-dvfs.h" 32 33 #include "exynos-acpm-pmic.h" 33 34 34 35 #define ACPM_PROTOCOL_SEQNUM GENMASK(21, 16) ··· 177 176 /** 178 177 * struct acpm_match_data - of_device_id data. 179 178 * @initdata_base: offset in SRAM where the channels configuration resides. 179 + * @acpm_clk_dev_name: base name for the ACPM clocks device that we're registering. 180 180 */ 181 181 struct acpm_match_data { 182 182 loff_t initdata_base; 183 + const char *acpm_clk_dev_name; 183 184 }; 184 185 185 186 #define client_to_acpm_chan(c) container_of(c, struct acpm_chan, cl) ··· 593 590 */ 594 591 static void acpm_setup_ops(struct acpm_info *acpm) 595 592 { 593 + struct acpm_dvfs_ops *dvfs_ops = &acpm->handle.ops.dvfs_ops; 596 594 struct acpm_pmic_ops *pmic_ops = &acpm->handle.ops.pmic_ops; 595 + 596 + dvfs_ops->set_rate = acpm_dvfs_set_rate; 597 + dvfs_ops->get_rate = acpm_dvfs_get_rate; 597 598 598 599 pmic_ops->read_reg = acpm_pmic_read_reg; 599 600 pmic_ops->bulk_read = acpm_pmic_bulk_read; ··· 606 599 pmic_ops->update_reg = acpm_pmic_update_reg; 607 600 } 608 601 602 + static void acpm_clk_pdev_unregister(void *data) 603 + { 604 + platform_device_unregister(data); 605 + } 606 + 609 607 static int acpm_probe(struct platform_device *pdev) 610 608 { 611 609 const struct acpm_match_data *match_data; 610 + struct platform_device *acpm_clk_pdev; 612 611 struct device *dev = &pdev->dev; 613 612 struct device_node *shmem; 614 613 struct acpm_info *acpm; ··· 654 641 acpm_setup_ops(acpm); 655 642 656 643 platform_set_drvdata(pdev, acpm); 644 + 645 + acpm_clk_pdev = platform_device_register_data(dev, 646 + match_data->acpm_clk_dev_name, 647 + PLATFORM_DEVID_NONE, NULL, 0); 648 + if (IS_ERR(acpm_clk_pdev)) 649 + return dev_err_probe(dev, PTR_ERR(acpm_clk_pdev), 650 + "Failed to register ACPM clocks device.\n"); 651 + 652 + ret = devm_add_action_or_reset(dev, acpm_clk_pdev_unregister, 653 + acpm_clk_pdev); 654 + if (ret) 655 + return dev_err_probe(dev, ret, "Failed to add devm action.\n"); 657 656 658 657 return devm_of_platform_populate(dev); 659 658 } ··· 766 741 767 742 static const struct acpm_match_data acpm_gs101 = { 768 743 .initdata_base = ACPM_GS101_INITDATA_BASE, 744 + .acpm_clk_dev_name = "gs101-acpm-clk", 769 745 }; 770 746 771 747 static const struct of_device_id acpm_match[] = {
+26
include/dt-bindings/clock/google,gs101-acpm.h
··· 1 + /* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ 2 + /* 3 + * Copyright 2025 Linaro Ltd. 4 + * 5 + * Device Tree binding constants for Google gs101 ACPM clock controller. 6 + */ 7 + 8 + #ifndef _DT_BINDINGS_CLOCK_GOOGLE_GS101_ACPM_H 9 + #define _DT_BINDINGS_CLOCK_GOOGLE_GS101_ACPM_H 10 + 11 + #define GS101_CLK_ACPM_DVFS_MIF 0 12 + #define GS101_CLK_ACPM_DVFS_INT 1 13 + #define GS101_CLK_ACPM_DVFS_CPUCL0 2 14 + #define GS101_CLK_ACPM_DVFS_CPUCL1 3 15 + #define GS101_CLK_ACPM_DVFS_CPUCL2 4 16 + #define GS101_CLK_ACPM_DVFS_G3D 5 17 + #define GS101_CLK_ACPM_DVFS_G3DL2 6 18 + #define GS101_CLK_ACPM_DVFS_TPU 7 19 + #define GS101_CLK_ACPM_DVFS_INTCAM 8 20 + #define GS101_CLK_ACPM_DVFS_TNR 9 21 + #define GS101_CLK_ACPM_DVFS_CAM 10 22 + #define GS101_CLK_ACPM_DVFS_MFC 11 23 + #define GS101_CLK_ACPM_DVFS_DISP 12 24 + #define GS101_CLK_ACPM_DVFS_BO 13 25 + 26 + #endif /* _DT_BINDINGS_CLOCK_GOOGLE_GS101_ACPM_H */
+19
include/linux/firmware/samsung/exynos-acpm-protocol.h
··· 13 13 struct acpm_handle; 14 14 struct device_node; 15 15 16 + struct acpm_dvfs_ops { 17 + int (*set_rate)(const struct acpm_handle *handle, 18 + unsigned int acpm_chan_id, unsigned int clk_id, 19 + unsigned long rate); 20 + unsigned long (*get_rate)(const struct acpm_handle *handle, 21 + unsigned int acpm_chan_id, 22 + unsigned int clk_id); 23 + }; 24 + 16 25 struct acpm_pmic_ops { 17 26 int (*read_reg)(const struct acpm_handle *handle, 18 27 unsigned int acpm_chan_id, u8 type, u8 reg, u8 chan, ··· 41 32 }; 42 33 43 34 struct acpm_ops { 35 + struct acpm_dvfs_ops dvfs_ops; 44 36 struct acpm_pmic_ops pmic_ops; 45 37 }; 46 38 ··· 55 45 56 46 struct device; 57 47 48 + #if IS_ENABLED(CONFIG_EXYNOS_ACPM_PROTOCOL) 58 49 const struct acpm_handle *devm_acpm_get_by_node(struct device *dev, 59 50 struct device_node *np); 51 + #else 52 + 53 + static inline const struct acpm_handle *devm_acpm_get_by_node(struct device *dev, 54 + struct device_node *np) 55 + { 56 + return NULL; 57 + } 58 + #endif 60 59 61 60 #endif /* __EXYNOS_ACPM_PROTOCOL_H */