xref: /openbmc/linux/drivers/cpufreq/qcom-cpufreq-nvmem.c (revision 7d12709544b8b3fb9727a34a664b8380e1e3493a)
1*7d127095SSricharan R // SPDX-License-Identifier: GPL-2.0
2*7d127095SSricharan R /*
3*7d127095SSricharan R  * Copyright (c) 2018, The Linux Foundation. All rights reserved.
4*7d127095SSricharan R  */
5*7d127095SSricharan R 
6*7d127095SSricharan R /*
7*7d127095SSricharan R  * In Certain QCOM SoCs like apq8096 and msm8996 that have KRYO processors,
8*7d127095SSricharan R  * the CPU frequency subset and voltage value of each OPP varies
9*7d127095SSricharan R  * based on the silicon variant in use. Qualcomm Process Voltage Scaling Tables
10*7d127095SSricharan R  * defines the voltage and frequency value based on the msm-id in SMEM
11*7d127095SSricharan R  * and speedbin blown in the efuse combination.
12*7d127095SSricharan R  * The qcom-cpufreq-nvmem driver reads the msm-id and efuse value from the SoC
13*7d127095SSricharan R  * to provide the OPP framework with required information.
14*7d127095SSricharan R  * This is used to determine the voltage and frequency value for each OPP of
15*7d127095SSricharan R  * operating-points-v2 table when it is parsed by the OPP framework.
16*7d127095SSricharan R  */
17*7d127095SSricharan R 
18*7d127095SSricharan R #include <linux/cpu.h>
19*7d127095SSricharan R #include <linux/err.h>
20*7d127095SSricharan R #include <linux/init.h>
21*7d127095SSricharan R #include <linux/kernel.h>
22*7d127095SSricharan R #include <linux/module.h>
23*7d127095SSricharan R #include <linux/nvmem-consumer.h>
24*7d127095SSricharan R #include <linux/of.h>
25*7d127095SSricharan R #include <linux/of_device.h>
26*7d127095SSricharan R #include <linux/platform_device.h>
27*7d127095SSricharan R #include <linux/pm_opp.h>
28*7d127095SSricharan R #include <linux/slab.h>
29*7d127095SSricharan R #include <linux/soc/qcom/smem.h>
30*7d127095SSricharan R 
31*7d127095SSricharan R #define MSM_ID_SMEM	137
32*7d127095SSricharan R 
33*7d127095SSricharan R enum _msm_id {
34*7d127095SSricharan R 	MSM8996V3 = 0xF6ul,
35*7d127095SSricharan R 	APQ8096V3 = 0x123ul,
36*7d127095SSricharan R 	MSM8996SG = 0x131ul,
37*7d127095SSricharan R 	APQ8096SG = 0x138ul,
38*7d127095SSricharan R };
39*7d127095SSricharan R 
40*7d127095SSricharan R enum _msm8996_version {
41*7d127095SSricharan R 	MSM8996_V3,
42*7d127095SSricharan R 	MSM8996_SG,
43*7d127095SSricharan R 	NUM_OF_MSM8996_VERSIONS,
44*7d127095SSricharan R };
45*7d127095SSricharan R 
46*7d127095SSricharan R static struct platform_device *cpufreq_dt_pdev, *cpufreq_pdev;
47*7d127095SSricharan R 
48*7d127095SSricharan R static enum _msm8996_version qcom_cpufreq_get_msm_id(void)
49*7d127095SSricharan R {
50*7d127095SSricharan R 	size_t len;
51*7d127095SSricharan R 	u32 *msm_id;
52*7d127095SSricharan R 	enum _msm8996_version version;
53*7d127095SSricharan R 
54*7d127095SSricharan R 	msm_id = qcom_smem_get(QCOM_SMEM_HOST_ANY, MSM_ID_SMEM, &len);
55*7d127095SSricharan R 	if (IS_ERR(msm_id))
56*7d127095SSricharan R 		return NUM_OF_MSM8996_VERSIONS;
57*7d127095SSricharan R 
58*7d127095SSricharan R 	/* The first 4 bytes are format, next to them is the actual msm-id */
59*7d127095SSricharan R 	msm_id++;
60*7d127095SSricharan R 
61*7d127095SSricharan R 	switch ((enum _msm_id)*msm_id) {
62*7d127095SSricharan R 	case MSM8996V3:
63*7d127095SSricharan R 	case APQ8096V3:
64*7d127095SSricharan R 		version = MSM8996_V3;
65*7d127095SSricharan R 		break;
66*7d127095SSricharan R 	case MSM8996SG:
67*7d127095SSricharan R 	case APQ8096SG:
68*7d127095SSricharan R 		version = MSM8996_SG;
69*7d127095SSricharan R 		break;
70*7d127095SSricharan R 	default:
71*7d127095SSricharan R 		version = NUM_OF_MSM8996_VERSIONS;
72*7d127095SSricharan R 	}
73*7d127095SSricharan R 
74*7d127095SSricharan R 	return version;
75*7d127095SSricharan R }
76*7d127095SSricharan R 
77*7d127095SSricharan R static int qcom_cpufreq_kryo_name_version(struct device *cpu_dev,
78*7d127095SSricharan R 					  struct nvmem_cell *speedbin_nvmem,
79*7d127095SSricharan R 					  u32 *versions)
80*7d127095SSricharan R {
81*7d127095SSricharan R 	size_t len;
82*7d127095SSricharan R 	u8 *speedbin;
83*7d127095SSricharan R 	enum _msm8996_version msm8996_version;
84*7d127095SSricharan R 
85*7d127095SSricharan R 	msm8996_version = qcom_cpufreq_get_msm_id();
86*7d127095SSricharan R 	if (NUM_OF_MSM8996_VERSIONS == msm8996_version) {
87*7d127095SSricharan R 		dev_err(cpu_dev, "Not Snapdragon 820/821!");
88*7d127095SSricharan R 		return -ENODEV;
89*7d127095SSricharan R 	}
90*7d127095SSricharan R 
91*7d127095SSricharan R 	speedbin = nvmem_cell_read(speedbin_nvmem, &len);
92*7d127095SSricharan R 	if (IS_ERR(speedbin))
93*7d127095SSricharan R 		return PTR_ERR(speedbin);
94*7d127095SSricharan R 
95*7d127095SSricharan R 	switch (msm8996_version) {
96*7d127095SSricharan R 	case MSM8996_V3:
97*7d127095SSricharan R 		*versions = 1 << (unsigned int)(*speedbin);
98*7d127095SSricharan R 		break;
99*7d127095SSricharan R 	case MSM8996_SG:
100*7d127095SSricharan R 		*versions = 1 << ((unsigned int)(*speedbin) + 4);
101*7d127095SSricharan R 		break;
102*7d127095SSricharan R 	default:
103*7d127095SSricharan R 		BUG();
104*7d127095SSricharan R 		break;
105*7d127095SSricharan R 	}
106*7d127095SSricharan R 
107*7d127095SSricharan R 	kfree(speedbin);
108*7d127095SSricharan R 	return 0;
109*7d127095SSricharan R }
110*7d127095SSricharan R 
111*7d127095SSricharan R static int qcom_cpufreq_probe(struct platform_device *pdev)
112*7d127095SSricharan R {
113*7d127095SSricharan R 	struct opp_table **opp_tables;
114*7d127095SSricharan R 	int (*get_version)(struct device *cpu_dev,
115*7d127095SSricharan R 			   struct nvmem_cell *speedbin_nvmem,
116*7d127095SSricharan R 			   u32 *versions);
117*7d127095SSricharan R 	struct nvmem_cell *speedbin_nvmem;
118*7d127095SSricharan R 	struct device_node *np;
119*7d127095SSricharan R 	struct device *cpu_dev;
120*7d127095SSricharan R 	unsigned cpu;
121*7d127095SSricharan R 	u32 versions;
122*7d127095SSricharan R 	const struct of_device_id *match;
123*7d127095SSricharan R 	int ret;
124*7d127095SSricharan R 
125*7d127095SSricharan R 	cpu_dev = get_cpu_device(0);
126*7d127095SSricharan R 	if (!cpu_dev)
127*7d127095SSricharan R 		return -ENODEV;
128*7d127095SSricharan R 
129*7d127095SSricharan R 	match = pdev->dev.platform_data;
130*7d127095SSricharan R 	get_version = match->data;
131*7d127095SSricharan R 	if (!get_version)
132*7d127095SSricharan R 		return -ENODEV;
133*7d127095SSricharan R 
134*7d127095SSricharan R 	np = dev_pm_opp_of_get_opp_desc_node(cpu_dev);
135*7d127095SSricharan R 	if (!np)
136*7d127095SSricharan R 		return -ENOENT;
137*7d127095SSricharan R 
138*7d127095SSricharan R 	ret = of_device_is_compatible(np, "operating-points-v2-kryo-cpu");
139*7d127095SSricharan R 	if (!ret) {
140*7d127095SSricharan R 		of_node_put(np);
141*7d127095SSricharan R 		return -ENOENT;
142*7d127095SSricharan R 	}
143*7d127095SSricharan R 
144*7d127095SSricharan R 	speedbin_nvmem = of_nvmem_cell_get(np, NULL);
145*7d127095SSricharan R 	of_node_put(np);
146*7d127095SSricharan R 	if (IS_ERR(speedbin_nvmem)) {
147*7d127095SSricharan R 		if (PTR_ERR(speedbin_nvmem) != -EPROBE_DEFER)
148*7d127095SSricharan R 			dev_err(cpu_dev, "Could not get nvmem cell: %ld\n",
149*7d127095SSricharan R 				PTR_ERR(speedbin_nvmem));
150*7d127095SSricharan R 		return PTR_ERR(speedbin_nvmem);
151*7d127095SSricharan R 	}
152*7d127095SSricharan R 
153*7d127095SSricharan R 	ret = get_version(cpu_dev, speedbin_nvmem, &versions);
154*7d127095SSricharan R 	nvmem_cell_put(speedbin_nvmem);
155*7d127095SSricharan R 	if (ret)
156*7d127095SSricharan R 		return ret;
157*7d127095SSricharan R 
158*7d127095SSricharan R 	opp_tables = kcalloc(num_possible_cpus(), sizeof(*opp_tables), GFP_KERNEL);
159*7d127095SSricharan R 	if (!opp_tables)
160*7d127095SSricharan R 		return -ENOMEM;
161*7d127095SSricharan R 
162*7d127095SSricharan R 	for_each_possible_cpu(cpu) {
163*7d127095SSricharan R 		cpu_dev = get_cpu_device(cpu);
164*7d127095SSricharan R 		if (NULL == cpu_dev) {
165*7d127095SSricharan R 			ret = -ENODEV;
166*7d127095SSricharan R 			goto free_opp;
167*7d127095SSricharan R 		}
168*7d127095SSricharan R 
169*7d127095SSricharan R 		opp_tables[cpu] = dev_pm_opp_set_supported_hw(cpu_dev,
170*7d127095SSricharan R 							      &versions, 1);
171*7d127095SSricharan R 		if (IS_ERR(opp_tables[cpu])) {
172*7d127095SSricharan R 			ret = PTR_ERR(opp_tables[cpu]);
173*7d127095SSricharan R 			dev_err(cpu_dev, "Failed to set supported hardware\n");
174*7d127095SSricharan R 			goto free_opp;
175*7d127095SSricharan R 		}
176*7d127095SSricharan R 	}
177*7d127095SSricharan R 
178*7d127095SSricharan R 	cpufreq_dt_pdev = platform_device_register_simple("cpufreq-dt", -1,
179*7d127095SSricharan R 							  NULL, 0);
180*7d127095SSricharan R 	if (!IS_ERR(cpufreq_dt_pdev)) {
181*7d127095SSricharan R 		platform_set_drvdata(pdev, opp_tables);
182*7d127095SSricharan R 		return 0;
183*7d127095SSricharan R 	}
184*7d127095SSricharan R 
185*7d127095SSricharan R 	ret = PTR_ERR(cpufreq_dt_pdev);
186*7d127095SSricharan R 	dev_err(cpu_dev, "Failed to register platform device\n");
187*7d127095SSricharan R 
188*7d127095SSricharan R free_opp:
189*7d127095SSricharan R 	for_each_possible_cpu(cpu) {
190*7d127095SSricharan R 		if (IS_ERR_OR_NULL(opp_tables[cpu]))
191*7d127095SSricharan R 			break;
192*7d127095SSricharan R 		dev_pm_opp_put_supported_hw(opp_tables[cpu]);
193*7d127095SSricharan R 	}
194*7d127095SSricharan R 	kfree(opp_tables);
195*7d127095SSricharan R 
196*7d127095SSricharan R 	return ret;
197*7d127095SSricharan R }
198*7d127095SSricharan R 
199*7d127095SSricharan R static int qcom_cpufreq_remove(struct platform_device *pdev)
200*7d127095SSricharan R {
201*7d127095SSricharan R 	struct opp_table **opp_tables = platform_get_drvdata(pdev);
202*7d127095SSricharan R 	unsigned int cpu;
203*7d127095SSricharan R 
204*7d127095SSricharan R 	platform_device_unregister(cpufreq_dt_pdev);
205*7d127095SSricharan R 
206*7d127095SSricharan R 	for_each_possible_cpu(cpu)
207*7d127095SSricharan R 		dev_pm_opp_put_supported_hw(opp_tables[cpu]);
208*7d127095SSricharan R 
209*7d127095SSricharan R 	kfree(opp_tables);
210*7d127095SSricharan R 
211*7d127095SSricharan R 	return 0;
212*7d127095SSricharan R }
213*7d127095SSricharan R 
214*7d127095SSricharan R static struct platform_driver qcom_cpufreq_driver = {
215*7d127095SSricharan R 	.probe = qcom_cpufreq_probe,
216*7d127095SSricharan R 	.remove = qcom_cpufreq_remove,
217*7d127095SSricharan R 	.driver = {
218*7d127095SSricharan R 		.name = "qcom-cpufreq-nvmem",
219*7d127095SSricharan R 	},
220*7d127095SSricharan R };
221*7d127095SSricharan R 
222*7d127095SSricharan R static const struct of_device_id qcom_cpufreq_match_list[] __initconst = {
223*7d127095SSricharan R 	{ .compatible = "qcom,apq8096",
224*7d127095SSricharan R 	  .data = qcom_cpufreq_kryo_name_version },
225*7d127095SSricharan R 	{ .compatible = "qcom,msm8996",
226*7d127095SSricharan R 	  .data = qcom_cpufreq_kryo_name_version },
227*7d127095SSricharan R 	{},
228*7d127095SSricharan R };
229*7d127095SSricharan R 
230*7d127095SSricharan R /*
231*7d127095SSricharan R  * Since the driver depends on smem and nvmem drivers, which may
232*7d127095SSricharan R  * return EPROBE_DEFER, all the real activity is done in the probe,
233*7d127095SSricharan R  * which may be defered as well. The init here is only registering
234*7d127095SSricharan R  * the driver and the platform device.
235*7d127095SSricharan R  */
236*7d127095SSricharan R static int __init qcom_cpufreq_init(void)
237*7d127095SSricharan R {
238*7d127095SSricharan R 	struct device_node *np = of_find_node_by_path("/");
239*7d127095SSricharan R 	const struct of_device_id *match;
240*7d127095SSricharan R 	int ret;
241*7d127095SSricharan R 
242*7d127095SSricharan R 	if (!np)
243*7d127095SSricharan R 		return -ENODEV;
244*7d127095SSricharan R 
245*7d127095SSricharan R 	match = of_match_node(qcom_cpufreq_match_list, np);
246*7d127095SSricharan R 	of_node_put(np);
247*7d127095SSricharan R 	if (!match)
248*7d127095SSricharan R 		return -ENODEV;
249*7d127095SSricharan R 
250*7d127095SSricharan R 	ret = platform_driver_register(&qcom_cpufreq_driver);
251*7d127095SSricharan R 	if (unlikely(ret < 0))
252*7d127095SSricharan R 		return ret;
253*7d127095SSricharan R 
254*7d127095SSricharan R 	cpufreq_pdev = platform_device_register_data(NULL, "qcom-cpufreq-nvmem",
255*7d127095SSricharan R 						     -1, match, sizeof(*match));
256*7d127095SSricharan R 	ret = PTR_ERR_OR_ZERO(cpufreq_pdev);
257*7d127095SSricharan R 	if (0 == ret)
258*7d127095SSricharan R 		return 0;
259*7d127095SSricharan R 
260*7d127095SSricharan R 	platform_driver_unregister(&qcom_cpufreq_driver);
261*7d127095SSricharan R 	return ret;
262*7d127095SSricharan R }
263*7d127095SSricharan R module_init(qcom_cpufreq_init);
264*7d127095SSricharan R 
265*7d127095SSricharan R static void __exit qcom_cpufreq_exit(void)
266*7d127095SSricharan R {
267*7d127095SSricharan R 	platform_device_unregister(cpufreq_pdev);
268*7d127095SSricharan R 	platform_driver_unregister(&qcom_cpufreq_driver);
269*7d127095SSricharan R }
270*7d127095SSricharan R module_exit(qcom_cpufreq_exit);
271*7d127095SSricharan R 
272*7d127095SSricharan R MODULE_DESCRIPTION("Qualcomm Technologies, Inc. CPUfreq driver");
273*7d127095SSricharan R MODULE_LICENSE("GPL v2");
274