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

clk: qcom: Add SDX55 APCS clock controller support

Add a driver for the SDX55 APCS clock controller. It is part of the APCS
hardware block, which among other things implements also a combined mux
and half integer divider functionality. The APCS clock controller has 3
parent clocks:

1. Board XO
2. Fixed rate GPLL0
3. A7 PLL

This is required for enabling CPU frequency scaling on SDX55-based
platforms.

Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Link: https://lore.kernel.org/r/20210118041156.50016-6-manivannan.sadhasivam@linaro.org
[sboyd@kernel.org: Fix unused ret in probe by hardcoding it]
Signed-off-by: Stephen Boyd <sboyd@kernel.org>

authored by

Manivannan Sadhasivam and committed by
Stephen Boyd
f28dec1a 5a5223ff

+159
+9
drivers/clk/qcom/Kconfig
··· 54 54 Say Y if you want to support CPU clock scaling using CPUfreq 55 55 drivers for dynamic power management. 56 56 57 + config QCOM_CLK_APCS_SDX55 58 + tristate "SDX55 APCS Clock Controller" 59 + depends on QCOM_APCS_IPC || COMPILE_TEST 60 + help 61 + Support for the APCS Clock Controller on SDX55 platform. The 62 + APCS is managing the mux and divider which feeds the CPUs. 63 + Say Y if you want to support CPU frequency scaling on devices 64 + such as SDX55. 65 + 57 66 config QCOM_CLK_RPM 58 67 tristate "RPM based Clock Controller" 59 68 depends on MFD_QCOM_RPM
+1
drivers/clk/qcom/Makefile
··· 47 47 obj-$(CONFIG_QCOM_A7PLL) += a7-pll.o 48 48 obj-$(CONFIG_QCOM_CLK_APCS_MSM8916) += apcs-msm8916.o 49 49 obj-$(CONFIG_QCOM_CLK_APCC_MSM8996) += clk-cpu-8996.o 50 + obj-$(CONFIG_QCOM_CLK_APCS_SDX55) += apcs-sdx55.o 50 51 obj-$(CONFIG_QCOM_CLK_RPM) += clk-rpm.o 51 52 obj-$(CONFIG_QCOM_CLK_RPMH) += clk-rpmh.o 52 53 obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o
+149
drivers/clk/qcom/apcs-sdx55.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Qualcomm SDX55 APCS clock controller driver 4 + * 5 + * Copyright (c) 2020, Linaro Limited 6 + * Author: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> 7 + */ 8 + 9 + #include <linux/clk.h> 10 + #include <linux/clk-provider.h> 11 + #include <linux/cpu.h> 12 + #include <linux/kernel.h> 13 + #include <linux/module.h> 14 + #include <linux/platform_device.h> 15 + #include <linux/pm_domain.h> 16 + #include <linux/regmap.h> 17 + #include <linux/slab.h> 18 + 19 + #include "clk-regmap.h" 20 + #include "clk-regmap-mux-div.h" 21 + 22 + static const u32 apcs_mux_clk_parent_map[] = { 0, 1, 5 }; 23 + 24 + static const struct clk_parent_data pdata[] = { 25 + { .fw_name = "ref" }, 26 + { .fw_name = "aux" }, 27 + { .fw_name = "pll" }, 28 + }; 29 + 30 + /* 31 + * We use the notifier function for switching to a temporary safe configuration 32 + * (mux and divider), while the A7 PLL is reconfigured. 33 + */ 34 + static int a7cc_notifier_cb(struct notifier_block *nb, unsigned long event, 35 + void *data) 36 + { 37 + int ret = 0; 38 + struct clk_regmap_mux_div *md = container_of(nb, 39 + struct clk_regmap_mux_div, 40 + clk_nb); 41 + if (event == PRE_RATE_CHANGE) 42 + /* set the mux and divider to safe frequency (400mhz) */ 43 + ret = mux_div_set_src_div(md, 1, 2); 44 + 45 + return notifier_from_errno(ret); 46 + } 47 + 48 + static int qcom_apcs_sdx55_clk_probe(struct platform_device *pdev) 49 + { 50 + struct device *dev = &pdev->dev; 51 + struct device *parent = dev->parent; 52 + struct device *cpu_dev; 53 + struct clk_regmap_mux_div *a7cc; 54 + struct regmap *regmap; 55 + struct clk_init_data init = { }; 56 + int ret; 57 + 58 + regmap = dev_get_regmap(parent, NULL); 59 + if (!regmap) { 60 + dev_err_probe(dev, -ENODEV, "Failed to get parent regmap\n"); 61 + return -ENODEV; 62 + } 63 + 64 + a7cc = devm_kzalloc(dev, sizeof(*a7cc), GFP_KERNEL); 65 + if (!a7cc) 66 + return -ENOMEM; 67 + 68 + init.name = "a7mux"; 69 + init.parent_data = pdata; 70 + init.num_parents = ARRAY_SIZE(pdata); 71 + init.ops = &clk_regmap_mux_div_ops; 72 + 73 + a7cc->clkr.hw.init = &init; 74 + a7cc->clkr.regmap = regmap; 75 + a7cc->reg_offset = 0x8; 76 + a7cc->hid_width = 5; 77 + a7cc->hid_shift = 0; 78 + a7cc->src_width = 3; 79 + a7cc->src_shift = 8; 80 + a7cc->parent_map = apcs_mux_clk_parent_map; 81 + 82 + a7cc->pclk = devm_clk_get(parent, "pll"); 83 + if (IS_ERR(a7cc->pclk)) { 84 + ret = PTR_ERR(a7cc->pclk); 85 + if (ret != -EPROBE_DEFER) 86 + dev_err_probe(dev, ret, "Failed to get PLL clk\n"); 87 + return ret; 88 + } 89 + 90 + a7cc->clk_nb.notifier_call = a7cc_notifier_cb; 91 + ret = clk_notifier_register(a7cc->pclk, &a7cc->clk_nb); 92 + if (ret) { 93 + dev_err_probe(dev, ret, "Failed to register clock notifier\n"); 94 + return ret; 95 + } 96 + 97 + ret = devm_clk_register_regmap(dev, &a7cc->clkr); 98 + if (ret) { 99 + dev_err_probe(dev, ret, "Failed to register regmap clock\n"); 100 + goto err; 101 + } 102 + 103 + ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, 104 + &a7cc->clkr.hw); 105 + if (ret) { 106 + dev_err_probe(dev, ret, "Failed to add clock provider\n"); 107 + goto err; 108 + } 109 + 110 + platform_set_drvdata(pdev, a7cc); 111 + 112 + /* 113 + * Attach the power domain to cpudev. Since there is no dedicated driver 114 + * for CPUs and the SDX55 platform lacks hardware specific CPUFreq 115 + * driver, there seems to be no better place to do this. So do it here! 116 + */ 117 + cpu_dev = get_cpu_device(0); 118 + dev_pm_domain_attach(cpu_dev, true); 119 + 120 + return 0; 121 + 122 + err: 123 + clk_notifier_unregister(a7cc->pclk, &a7cc->clk_nb); 124 + return ret; 125 + } 126 + 127 + static int qcom_apcs_sdx55_clk_remove(struct platform_device *pdev) 128 + { 129 + struct device *cpu_dev = get_cpu_device(0); 130 + struct clk_regmap_mux_div *a7cc = platform_get_drvdata(pdev); 131 + 132 + clk_notifier_unregister(a7cc->pclk, &a7cc->clk_nb); 133 + dev_pm_domain_detach(cpu_dev, true); 134 + 135 + return 0; 136 + } 137 + 138 + static struct platform_driver qcom_apcs_sdx55_clk_driver = { 139 + .probe = qcom_apcs_sdx55_clk_probe, 140 + .remove = qcom_apcs_sdx55_clk_remove, 141 + .driver = { 142 + .name = "qcom-sdx55-acps-clk", 143 + }, 144 + }; 145 + module_platform_driver(qcom_apcs_sdx55_clk_driver); 146 + 147 + MODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>"); 148 + MODULE_LICENSE("GPL v2"); 149 + MODULE_DESCRIPTION("Qualcomm SDX55 APCS clock driver");