18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2018, The Linux Foundation. All rights reserved.
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci/*
78c2ecf20Sopenharmony_ci * In Certain QCOM SoCs like apq8096 and msm8996 that have KRYO processors,
88c2ecf20Sopenharmony_ci * the CPU frequency subset and voltage value of each OPP varies
98c2ecf20Sopenharmony_ci * based on the silicon variant in use. Qualcomm Process Voltage Scaling Tables
108c2ecf20Sopenharmony_ci * defines the voltage and frequency value based on the msm-id in SMEM
118c2ecf20Sopenharmony_ci * and speedbin blown in the efuse combination.
128c2ecf20Sopenharmony_ci * The qcom-cpufreq-nvmem driver reads the msm-id and efuse value from the SoC
138c2ecf20Sopenharmony_ci * to provide the OPP framework with required information.
148c2ecf20Sopenharmony_ci * This is used to determine the voltage and frequency value for each OPP of
158c2ecf20Sopenharmony_ci * operating-points-v2 table when it is parsed by the OPP framework.
168c2ecf20Sopenharmony_ci */
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include <linux/cpu.h>
198c2ecf20Sopenharmony_ci#include <linux/err.h>
208c2ecf20Sopenharmony_ci#include <linux/init.h>
218c2ecf20Sopenharmony_ci#include <linux/kernel.h>
228c2ecf20Sopenharmony_ci#include <linux/module.h>
238c2ecf20Sopenharmony_ci#include <linux/nvmem-consumer.h>
248c2ecf20Sopenharmony_ci#include <linux/of.h>
258c2ecf20Sopenharmony_ci#include <linux/of_device.h>
268c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
278c2ecf20Sopenharmony_ci#include <linux/pm_domain.h>
288c2ecf20Sopenharmony_ci#include <linux/pm_opp.h>
298c2ecf20Sopenharmony_ci#include <linux/slab.h>
308c2ecf20Sopenharmony_ci#include <linux/soc/qcom/smem.h>
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#define MSM_ID_SMEM	137
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_cienum _msm_id {
358c2ecf20Sopenharmony_ci	MSM8996V3 = 0xF6ul,
368c2ecf20Sopenharmony_ci	APQ8096V3 = 0x123ul,
378c2ecf20Sopenharmony_ci	MSM8996SG = 0x131ul,
388c2ecf20Sopenharmony_ci	APQ8096SG = 0x138ul,
398c2ecf20Sopenharmony_ci};
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cienum _msm8996_version {
428c2ecf20Sopenharmony_ci	MSM8996_V3,
438c2ecf20Sopenharmony_ci	MSM8996_SG,
448c2ecf20Sopenharmony_ci	NUM_OF_MSM8996_VERSIONS,
458c2ecf20Sopenharmony_ci};
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistruct qcom_cpufreq_drv;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_cistruct qcom_cpufreq_match_data {
508c2ecf20Sopenharmony_ci	int (*get_version)(struct device *cpu_dev,
518c2ecf20Sopenharmony_ci			   struct nvmem_cell *speedbin_nvmem,
528c2ecf20Sopenharmony_ci			   char **pvs_name,
538c2ecf20Sopenharmony_ci			   struct qcom_cpufreq_drv *drv);
548c2ecf20Sopenharmony_ci	const char **genpd_names;
558c2ecf20Sopenharmony_ci};
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistruct qcom_cpufreq_drv {
588c2ecf20Sopenharmony_ci	struct opp_table **names_opp_tables;
598c2ecf20Sopenharmony_ci	struct opp_table **hw_opp_tables;
608c2ecf20Sopenharmony_ci	struct opp_table **genpd_opp_tables;
618c2ecf20Sopenharmony_ci	u32 versions;
628c2ecf20Sopenharmony_ci	const struct qcom_cpufreq_match_data *data;
638c2ecf20Sopenharmony_ci};
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic struct platform_device *cpufreq_dt_pdev, *cpufreq_pdev;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic void get_krait_bin_format_a(struct device *cpu_dev,
688c2ecf20Sopenharmony_ci					  int *speed, int *pvs, int *pvs_ver,
698c2ecf20Sopenharmony_ci					  struct nvmem_cell *pvs_nvmem, u8 *buf)
708c2ecf20Sopenharmony_ci{
718c2ecf20Sopenharmony_ci	u32 pte_efuse;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	pte_efuse = *((u32 *)buf);
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	*speed = pte_efuse & 0xf;
768c2ecf20Sopenharmony_ci	if (*speed == 0xf)
778c2ecf20Sopenharmony_ci		*speed = (pte_efuse >> 4) & 0xf;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	if (*speed == 0xf) {
808c2ecf20Sopenharmony_ci		*speed = 0;
818c2ecf20Sopenharmony_ci		dev_warn(cpu_dev, "Speed bin: Defaulting to %d\n", *speed);
828c2ecf20Sopenharmony_ci	} else {
838c2ecf20Sopenharmony_ci		dev_dbg(cpu_dev, "Speed bin: %d\n", *speed);
848c2ecf20Sopenharmony_ci	}
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	*pvs = (pte_efuse >> 10) & 0x7;
878c2ecf20Sopenharmony_ci	if (*pvs == 0x7)
888c2ecf20Sopenharmony_ci		*pvs = (pte_efuse >> 13) & 0x7;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	if (*pvs == 0x7) {
918c2ecf20Sopenharmony_ci		*pvs = 0;
928c2ecf20Sopenharmony_ci		dev_warn(cpu_dev, "PVS bin: Defaulting to %d\n", *pvs);
938c2ecf20Sopenharmony_ci	} else {
948c2ecf20Sopenharmony_ci		dev_dbg(cpu_dev, "PVS bin: %d\n", *pvs);
958c2ecf20Sopenharmony_ci	}
968c2ecf20Sopenharmony_ci}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_cistatic void get_krait_bin_format_b(struct device *cpu_dev,
998c2ecf20Sopenharmony_ci					  int *speed, int *pvs, int *pvs_ver,
1008c2ecf20Sopenharmony_ci					  struct nvmem_cell *pvs_nvmem, u8 *buf)
1018c2ecf20Sopenharmony_ci{
1028c2ecf20Sopenharmony_ci	u32 pte_efuse, redundant_sel;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	pte_efuse = *((u32 *)buf);
1058c2ecf20Sopenharmony_ci	redundant_sel = (pte_efuse >> 24) & 0x7;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	*pvs_ver = (pte_efuse >> 4) & 0x3;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	switch (redundant_sel) {
1108c2ecf20Sopenharmony_ci	case 1:
1118c2ecf20Sopenharmony_ci		*pvs = ((pte_efuse >> 28) & 0x8) | ((pte_efuse >> 6) & 0x7);
1128c2ecf20Sopenharmony_ci		*speed = (pte_efuse >> 27) & 0xf;
1138c2ecf20Sopenharmony_ci		break;
1148c2ecf20Sopenharmony_ci	case 2:
1158c2ecf20Sopenharmony_ci		*pvs = (pte_efuse >> 27) & 0xf;
1168c2ecf20Sopenharmony_ci		*speed = pte_efuse & 0x7;
1178c2ecf20Sopenharmony_ci		break;
1188c2ecf20Sopenharmony_ci	default:
1198c2ecf20Sopenharmony_ci		/* 4 bits of PVS are in efuse register bits 31, 8-6. */
1208c2ecf20Sopenharmony_ci		*pvs = ((pte_efuse >> 28) & 0x8) | ((pte_efuse >> 6) & 0x7);
1218c2ecf20Sopenharmony_ci		*speed = pte_efuse & 0x7;
1228c2ecf20Sopenharmony_ci	}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	/* Check SPEED_BIN_BLOW_STATUS */
1258c2ecf20Sopenharmony_ci	if (pte_efuse & BIT(3)) {
1268c2ecf20Sopenharmony_ci		dev_dbg(cpu_dev, "Speed bin: %d\n", *speed);
1278c2ecf20Sopenharmony_ci	} else {
1288c2ecf20Sopenharmony_ci		dev_warn(cpu_dev, "Speed bin not set. Defaulting to 0!\n");
1298c2ecf20Sopenharmony_ci		*speed = 0;
1308c2ecf20Sopenharmony_ci	}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	/* Check PVS_BLOW_STATUS */
1338c2ecf20Sopenharmony_ci	pte_efuse = *(((u32 *)buf) + 1);
1348c2ecf20Sopenharmony_ci	pte_efuse &= BIT(21);
1358c2ecf20Sopenharmony_ci	if (pte_efuse) {
1368c2ecf20Sopenharmony_ci		dev_dbg(cpu_dev, "PVS bin: %d\n", *pvs);
1378c2ecf20Sopenharmony_ci	} else {
1388c2ecf20Sopenharmony_ci		dev_warn(cpu_dev, "PVS bin not set. Defaulting to 0!\n");
1398c2ecf20Sopenharmony_ci		*pvs = 0;
1408c2ecf20Sopenharmony_ci	}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	dev_dbg(cpu_dev, "PVS version: %d\n", *pvs_ver);
1438c2ecf20Sopenharmony_ci}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_cistatic enum _msm8996_version qcom_cpufreq_get_msm_id(void)
1468c2ecf20Sopenharmony_ci{
1478c2ecf20Sopenharmony_ci	size_t len;
1488c2ecf20Sopenharmony_ci	u32 *msm_id;
1498c2ecf20Sopenharmony_ci	enum _msm8996_version version;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	msm_id = qcom_smem_get(QCOM_SMEM_HOST_ANY, MSM_ID_SMEM, &len);
1528c2ecf20Sopenharmony_ci	if (IS_ERR(msm_id))
1538c2ecf20Sopenharmony_ci		return NUM_OF_MSM8996_VERSIONS;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	/* The first 4 bytes are format, next to them is the actual msm-id */
1568c2ecf20Sopenharmony_ci	msm_id++;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	switch ((enum _msm_id)*msm_id) {
1598c2ecf20Sopenharmony_ci	case MSM8996V3:
1608c2ecf20Sopenharmony_ci	case APQ8096V3:
1618c2ecf20Sopenharmony_ci		version = MSM8996_V3;
1628c2ecf20Sopenharmony_ci		break;
1638c2ecf20Sopenharmony_ci	case MSM8996SG:
1648c2ecf20Sopenharmony_ci	case APQ8096SG:
1658c2ecf20Sopenharmony_ci		version = MSM8996_SG;
1668c2ecf20Sopenharmony_ci		break;
1678c2ecf20Sopenharmony_ci	default:
1688c2ecf20Sopenharmony_ci		version = NUM_OF_MSM8996_VERSIONS;
1698c2ecf20Sopenharmony_ci	}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	return version;
1728c2ecf20Sopenharmony_ci}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_cistatic int qcom_cpufreq_kryo_name_version(struct device *cpu_dev,
1758c2ecf20Sopenharmony_ci					  struct nvmem_cell *speedbin_nvmem,
1768c2ecf20Sopenharmony_ci					  char **pvs_name,
1778c2ecf20Sopenharmony_ci					  struct qcom_cpufreq_drv *drv)
1788c2ecf20Sopenharmony_ci{
1798c2ecf20Sopenharmony_ci	size_t len;
1808c2ecf20Sopenharmony_ci	u8 *speedbin;
1818c2ecf20Sopenharmony_ci	enum _msm8996_version msm8996_version;
1828c2ecf20Sopenharmony_ci	*pvs_name = NULL;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	msm8996_version = qcom_cpufreq_get_msm_id();
1858c2ecf20Sopenharmony_ci	if (NUM_OF_MSM8996_VERSIONS == msm8996_version) {
1868c2ecf20Sopenharmony_ci		dev_err(cpu_dev, "Not Snapdragon 820/821!");
1878c2ecf20Sopenharmony_ci		return -ENODEV;
1888c2ecf20Sopenharmony_ci	}
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	speedbin = nvmem_cell_read(speedbin_nvmem, &len);
1918c2ecf20Sopenharmony_ci	if (IS_ERR(speedbin))
1928c2ecf20Sopenharmony_ci		return PTR_ERR(speedbin);
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	switch (msm8996_version) {
1958c2ecf20Sopenharmony_ci	case MSM8996_V3:
1968c2ecf20Sopenharmony_ci		drv->versions = 1 << (unsigned int)(*speedbin);
1978c2ecf20Sopenharmony_ci		break;
1988c2ecf20Sopenharmony_ci	case MSM8996_SG:
1998c2ecf20Sopenharmony_ci		drv->versions = 1 << ((unsigned int)(*speedbin) + 4);
2008c2ecf20Sopenharmony_ci		break;
2018c2ecf20Sopenharmony_ci	default:
2028c2ecf20Sopenharmony_ci		BUG();
2038c2ecf20Sopenharmony_ci		break;
2048c2ecf20Sopenharmony_ci	}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	kfree(speedbin);
2078c2ecf20Sopenharmony_ci	return 0;
2088c2ecf20Sopenharmony_ci}
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_cistatic int qcom_cpufreq_krait_name_version(struct device *cpu_dev,
2118c2ecf20Sopenharmony_ci					   struct nvmem_cell *speedbin_nvmem,
2128c2ecf20Sopenharmony_ci					   char **pvs_name,
2138c2ecf20Sopenharmony_ci					   struct qcom_cpufreq_drv *drv)
2148c2ecf20Sopenharmony_ci{
2158c2ecf20Sopenharmony_ci	int speed = 0, pvs = 0, pvs_ver = 0;
2168c2ecf20Sopenharmony_ci	u8 *speedbin;
2178c2ecf20Sopenharmony_ci	size_t len;
2188c2ecf20Sopenharmony_ci	int ret = 0;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	speedbin = nvmem_cell_read(speedbin_nvmem, &len);
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	if (IS_ERR(speedbin))
2238c2ecf20Sopenharmony_ci		return PTR_ERR(speedbin);
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	switch (len) {
2268c2ecf20Sopenharmony_ci	case 4:
2278c2ecf20Sopenharmony_ci		get_krait_bin_format_a(cpu_dev, &speed, &pvs, &pvs_ver,
2288c2ecf20Sopenharmony_ci				       speedbin_nvmem, speedbin);
2298c2ecf20Sopenharmony_ci		break;
2308c2ecf20Sopenharmony_ci	case 8:
2318c2ecf20Sopenharmony_ci		get_krait_bin_format_b(cpu_dev, &speed, &pvs, &pvs_ver,
2328c2ecf20Sopenharmony_ci				       speedbin_nvmem, speedbin);
2338c2ecf20Sopenharmony_ci		break;
2348c2ecf20Sopenharmony_ci	default:
2358c2ecf20Sopenharmony_ci		dev_err(cpu_dev, "Unable to read nvmem data. Defaulting to 0!\n");
2368c2ecf20Sopenharmony_ci		ret = -ENODEV;
2378c2ecf20Sopenharmony_ci		goto len_error;
2388c2ecf20Sopenharmony_ci	}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	snprintf(*pvs_name, sizeof("speedXX-pvsXX-vXX"), "speed%d-pvs%d-v%d",
2418c2ecf20Sopenharmony_ci		 speed, pvs, pvs_ver);
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	drv->versions = (1 << speed);
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_cilen_error:
2468c2ecf20Sopenharmony_ci	kfree(speedbin);
2478c2ecf20Sopenharmony_ci	return ret;
2488c2ecf20Sopenharmony_ci}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_cistatic const struct qcom_cpufreq_match_data match_data_kryo = {
2518c2ecf20Sopenharmony_ci	.get_version = qcom_cpufreq_kryo_name_version,
2528c2ecf20Sopenharmony_ci};
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_cistatic const struct qcom_cpufreq_match_data match_data_krait = {
2558c2ecf20Sopenharmony_ci	.get_version = qcom_cpufreq_krait_name_version,
2568c2ecf20Sopenharmony_ci};
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_cistatic const char *qcs404_genpd_names[] = { "cpr", NULL };
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_cistatic const struct qcom_cpufreq_match_data match_data_qcs404 = {
2618c2ecf20Sopenharmony_ci	.genpd_names = qcs404_genpd_names,
2628c2ecf20Sopenharmony_ci};
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_cistatic int qcom_cpufreq_probe(struct platform_device *pdev)
2658c2ecf20Sopenharmony_ci{
2668c2ecf20Sopenharmony_ci	struct qcom_cpufreq_drv *drv;
2678c2ecf20Sopenharmony_ci	struct nvmem_cell *speedbin_nvmem;
2688c2ecf20Sopenharmony_ci	struct device_node *np;
2698c2ecf20Sopenharmony_ci	struct device *cpu_dev;
2708c2ecf20Sopenharmony_ci	char pvs_name_buffer[] = "speedXX-pvsXX-vXX";
2718c2ecf20Sopenharmony_ci	char *pvs_name = pvs_name_buffer;
2728c2ecf20Sopenharmony_ci	unsigned cpu;
2738c2ecf20Sopenharmony_ci	const struct of_device_id *match;
2748c2ecf20Sopenharmony_ci	int ret;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	cpu_dev = get_cpu_device(0);
2778c2ecf20Sopenharmony_ci	if (!cpu_dev)
2788c2ecf20Sopenharmony_ci		return -ENODEV;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	np = dev_pm_opp_of_get_opp_desc_node(cpu_dev);
2818c2ecf20Sopenharmony_ci	if (!np)
2828c2ecf20Sopenharmony_ci		return -ENOENT;
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	ret = of_device_is_compatible(np, "operating-points-v2-kryo-cpu");
2858c2ecf20Sopenharmony_ci	if (!ret) {
2868c2ecf20Sopenharmony_ci		of_node_put(np);
2878c2ecf20Sopenharmony_ci		return -ENOENT;
2888c2ecf20Sopenharmony_ci	}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	drv = kzalloc(sizeof(*drv), GFP_KERNEL);
2918c2ecf20Sopenharmony_ci	if (!drv)
2928c2ecf20Sopenharmony_ci		return -ENOMEM;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	match = pdev->dev.platform_data;
2958c2ecf20Sopenharmony_ci	drv->data = match->data;
2968c2ecf20Sopenharmony_ci	if (!drv->data) {
2978c2ecf20Sopenharmony_ci		ret = -ENODEV;
2988c2ecf20Sopenharmony_ci		goto free_drv;
2998c2ecf20Sopenharmony_ci	}
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	if (drv->data->get_version) {
3028c2ecf20Sopenharmony_ci		speedbin_nvmem = of_nvmem_cell_get(np, NULL);
3038c2ecf20Sopenharmony_ci		if (IS_ERR(speedbin_nvmem)) {
3048c2ecf20Sopenharmony_ci			if (PTR_ERR(speedbin_nvmem) != -EPROBE_DEFER)
3058c2ecf20Sopenharmony_ci				dev_err(cpu_dev,
3068c2ecf20Sopenharmony_ci					"Could not get nvmem cell: %ld\n",
3078c2ecf20Sopenharmony_ci					PTR_ERR(speedbin_nvmem));
3088c2ecf20Sopenharmony_ci			ret = PTR_ERR(speedbin_nvmem);
3098c2ecf20Sopenharmony_ci			goto free_drv;
3108c2ecf20Sopenharmony_ci		}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci		ret = drv->data->get_version(cpu_dev,
3138c2ecf20Sopenharmony_ci							speedbin_nvmem, &pvs_name, drv);
3148c2ecf20Sopenharmony_ci		if (ret) {
3158c2ecf20Sopenharmony_ci			nvmem_cell_put(speedbin_nvmem);
3168c2ecf20Sopenharmony_ci			goto free_drv;
3178c2ecf20Sopenharmony_ci		}
3188c2ecf20Sopenharmony_ci		nvmem_cell_put(speedbin_nvmem);
3198c2ecf20Sopenharmony_ci	}
3208c2ecf20Sopenharmony_ci	of_node_put(np);
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	drv->names_opp_tables = kcalloc(num_possible_cpus(),
3238c2ecf20Sopenharmony_ci				  sizeof(*drv->names_opp_tables),
3248c2ecf20Sopenharmony_ci				  GFP_KERNEL);
3258c2ecf20Sopenharmony_ci	if (!drv->names_opp_tables) {
3268c2ecf20Sopenharmony_ci		ret = -ENOMEM;
3278c2ecf20Sopenharmony_ci		goto free_drv;
3288c2ecf20Sopenharmony_ci	}
3298c2ecf20Sopenharmony_ci	drv->hw_opp_tables = kcalloc(num_possible_cpus(),
3308c2ecf20Sopenharmony_ci				  sizeof(*drv->hw_opp_tables),
3318c2ecf20Sopenharmony_ci				  GFP_KERNEL);
3328c2ecf20Sopenharmony_ci	if (!drv->hw_opp_tables) {
3338c2ecf20Sopenharmony_ci		ret = -ENOMEM;
3348c2ecf20Sopenharmony_ci		goto free_opp_names;
3358c2ecf20Sopenharmony_ci	}
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	drv->genpd_opp_tables = kcalloc(num_possible_cpus(),
3388c2ecf20Sopenharmony_ci					sizeof(*drv->genpd_opp_tables),
3398c2ecf20Sopenharmony_ci					GFP_KERNEL);
3408c2ecf20Sopenharmony_ci	if (!drv->genpd_opp_tables) {
3418c2ecf20Sopenharmony_ci		ret = -ENOMEM;
3428c2ecf20Sopenharmony_ci		goto free_opp;
3438c2ecf20Sopenharmony_ci	}
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	for_each_possible_cpu(cpu) {
3468c2ecf20Sopenharmony_ci		cpu_dev = get_cpu_device(cpu);
3478c2ecf20Sopenharmony_ci		if (NULL == cpu_dev) {
3488c2ecf20Sopenharmony_ci			ret = -ENODEV;
3498c2ecf20Sopenharmony_ci			goto free_genpd_opp;
3508c2ecf20Sopenharmony_ci		}
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci		if (drv->data->get_version) {
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci			if (pvs_name) {
3558c2ecf20Sopenharmony_ci				drv->names_opp_tables[cpu] = dev_pm_opp_set_prop_name(
3568c2ecf20Sopenharmony_ci								     cpu_dev,
3578c2ecf20Sopenharmony_ci								     pvs_name);
3588c2ecf20Sopenharmony_ci				if (IS_ERR(drv->names_opp_tables[cpu])) {
3598c2ecf20Sopenharmony_ci					ret = PTR_ERR(drv->names_opp_tables[cpu]);
3608c2ecf20Sopenharmony_ci					dev_err(cpu_dev, "Failed to add OPP name %s\n",
3618c2ecf20Sopenharmony_ci						pvs_name);
3628c2ecf20Sopenharmony_ci					goto free_opp;
3638c2ecf20Sopenharmony_ci				}
3648c2ecf20Sopenharmony_ci			}
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci			drv->hw_opp_tables[cpu] = dev_pm_opp_set_supported_hw(
3678c2ecf20Sopenharmony_ci									 cpu_dev, &drv->versions, 1);
3688c2ecf20Sopenharmony_ci			if (IS_ERR(drv->hw_opp_tables[cpu])) {
3698c2ecf20Sopenharmony_ci				ret = PTR_ERR(drv->hw_opp_tables[cpu]);
3708c2ecf20Sopenharmony_ci				dev_err(cpu_dev,
3718c2ecf20Sopenharmony_ci					"Failed to set supported hardware\n");
3728c2ecf20Sopenharmony_ci				goto free_genpd_opp;
3738c2ecf20Sopenharmony_ci			}
3748c2ecf20Sopenharmony_ci		}
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci		if (drv->data->genpd_names) {
3778c2ecf20Sopenharmony_ci			drv->genpd_opp_tables[cpu] =
3788c2ecf20Sopenharmony_ci				dev_pm_opp_attach_genpd(cpu_dev,
3798c2ecf20Sopenharmony_ci							drv->data->genpd_names,
3808c2ecf20Sopenharmony_ci							NULL);
3818c2ecf20Sopenharmony_ci			if (IS_ERR(drv->genpd_opp_tables[cpu])) {
3828c2ecf20Sopenharmony_ci				ret = PTR_ERR(drv->genpd_opp_tables[cpu]);
3838c2ecf20Sopenharmony_ci				if (ret != -EPROBE_DEFER)
3848c2ecf20Sopenharmony_ci					dev_err(cpu_dev,
3858c2ecf20Sopenharmony_ci						"Could not attach to pm_domain: %d\n",
3868c2ecf20Sopenharmony_ci						ret);
3878c2ecf20Sopenharmony_ci				goto free_genpd_opp;
3888c2ecf20Sopenharmony_ci			}
3898c2ecf20Sopenharmony_ci		}
3908c2ecf20Sopenharmony_ci	}
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	cpufreq_dt_pdev = platform_device_register_simple("cpufreq-dt", -1,
3938c2ecf20Sopenharmony_ci							  NULL, 0);
3948c2ecf20Sopenharmony_ci	if (!IS_ERR(cpufreq_dt_pdev)) {
3958c2ecf20Sopenharmony_ci		platform_set_drvdata(pdev, drv);
3968c2ecf20Sopenharmony_ci		return 0;
3978c2ecf20Sopenharmony_ci	}
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	ret = PTR_ERR(cpufreq_dt_pdev);
4008c2ecf20Sopenharmony_ci	dev_err(cpu_dev, "Failed to register platform device\n");
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_cifree_genpd_opp:
4038c2ecf20Sopenharmony_ci	for_each_possible_cpu(cpu) {
4048c2ecf20Sopenharmony_ci		if (IS_ERR_OR_NULL(drv->genpd_opp_tables[cpu]))
4058c2ecf20Sopenharmony_ci			break;
4068c2ecf20Sopenharmony_ci		dev_pm_opp_detach_genpd(drv->genpd_opp_tables[cpu]);
4078c2ecf20Sopenharmony_ci	}
4088c2ecf20Sopenharmony_ci	kfree(drv->genpd_opp_tables);
4098c2ecf20Sopenharmony_cifree_opp:
4108c2ecf20Sopenharmony_ci	for_each_possible_cpu(cpu) {
4118c2ecf20Sopenharmony_ci		if (IS_ERR_OR_NULL(drv->names_opp_tables[cpu]))
4128c2ecf20Sopenharmony_ci			break;
4138c2ecf20Sopenharmony_ci		dev_pm_opp_put_prop_name(drv->names_opp_tables[cpu]);
4148c2ecf20Sopenharmony_ci	}
4158c2ecf20Sopenharmony_ci	for_each_possible_cpu(cpu) {
4168c2ecf20Sopenharmony_ci		if (IS_ERR_OR_NULL(drv->hw_opp_tables[cpu]))
4178c2ecf20Sopenharmony_ci			break;
4188c2ecf20Sopenharmony_ci		dev_pm_opp_put_supported_hw(drv->hw_opp_tables[cpu]);
4198c2ecf20Sopenharmony_ci	}
4208c2ecf20Sopenharmony_ci	kfree(drv->hw_opp_tables);
4218c2ecf20Sopenharmony_cifree_opp_names:
4228c2ecf20Sopenharmony_ci	kfree(drv->names_opp_tables);
4238c2ecf20Sopenharmony_cifree_drv:
4248c2ecf20Sopenharmony_ci	kfree(drv);
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	return ret;
4278c2ecf20Sopenharmony_ci}
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_cistatic int qcom_cpufreq_remove(struct platform_device *pdev)
4308c2ecf20Sopenharmony_ci{
4318c2ecf20Sopenharmony_ci	struct qcom_cpufreq_drv *drv = platform_get_drvdata(pdev);
4328c2ecf20Sopenharmony_ci	unsigned int cpu;
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	platform_device_unregister(cpufreq_dt_pdev);
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	for_each_possible_cpu(cpu) {
4378c2ecf20Sopenharmony_ci		if (drv->names_opp_tables[cpu])
4388c2ecf20Sopenharmony_ci			dev_pm_opp_put_supported_hw(drv->names_opp_tables[cpu]);
4398c2ecf20Sopenharmony_ci		if (drv->hw_opp_tables[cpu])
4408c2ecf20Sopenharmony_ci			dev_pm_opp_put_supported_hw(drv->hw_opp_tables[cpu]);
4418c2ecf20Sopenharmony_ci		if (drv->genpd_opp_tables[cpu])
4428c2ecf20Sopenharmony_ci			dev_pm_opp_detach_genpd(drv->genpd_opp_tables[cpu]);
4438c2ecf20Sopenharmony_ci	}
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	kfree(drv->names_opp_tables);
4468c2ecf20Sopenharmony_ci	kfree(drv->hw_opp_tables);
4478c2ecf20Sopenharmony_ci	kfree(drv->genpd_opp_tables);
4488c2ecf20Sopenharmony_ci	kfree(drv);
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	return 0;
4518c2ecf20Sopenharmony_ci}
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_cistatic struct platform_driver qcom_cpufreq_driver = {
4548c2ecf20Sopenharmony_ci	.probe = qcom_cpufreq_probe,
4558c2ecf20Sopenharmony_ci	.remove = qcom_cpufreq_remove,
4568c2ecf20Sopenharmony_ci	.driver = {
4578c2ecf20Sopenharmony_ci		.name = "qcom-cpufreq-nvmem",
4588c2ecf20Sopenharmony_ci	},
4598c2ecf20Sopenharmony_ci};
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_cistatic const struct of_device_id qcom_cpufreq_match_list[] __initconst = {
4628c2ecf20Sopenharmony_ci	{ .compatible = "qcom,apq8096", .data = &match_data_kryo },
4638c2ecf20Sopenharmony_ci	{ .compatible = "qcom,msm8996", .data = &match_data_kryo },
4648c2ecf20Sopenharmony_ci	{ .compatible = "qcom,qcs404", .data = &match_data_qcs404 },
4658c2ecf20Sopenharmony_ci	{ .compatible = "qcom,ipq8064", .data = &match_data_krait },
4668c2ecf20Sopenharmony_ci	{ .compatible = "qcom,apq8064", .data = &match_data_krait },
4678c2ecf20Sopenharmony_ci	{ .compatible = "qcom,msm8974", .data = &match_data_krait },
4688c2ecf20Sopenharmony_ci	{ .compatible = "qcom,msm8960", .data = &match_data_krait },
4698c2ecf20Sopenharmony_ci	{},
4708c2ecf20Sopenharmony_ci};
4718c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, qcom_cpufreq_match_list);
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci/*
4748c2ecf20Sopenharmony_ci * Since the driver depends on smem and nvmem drivers, which may
4758c2ecf20Sopenharmony_ci * return EPROBE_DEFER, all the real activity is done in the probe,
4768c2ecf20Sopenharmony_ci * which may be defered as well. The init here is only registering
4778c2ecf20Sopenharmony_ci * the driver and the platform device.
4788c2ecf20Sopenharmony_ci */
4798c2ecf20Sopenharmony_cistatic int __init qcom_cpufreq_init(void)
4808c2ecf20Sopenharmony_ci{
4818c2ecf20Sopenharmony_ci	struct device_node *np = of_find_node_by_path("/");
4828c2ecf20Sopenharmony_ci	const struct of_device_id *match;
4838c2ecf20Sopenharmony_ci	int ret;
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	if (!np)
4868c2ecf20Sopenharmony_ci		return -ENODEV;
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	match = of_match_node(qcom_cpufreq_match_list, np);
4898c2ecf20Sopenharmony_ci	of_node_put(np);
4908c2ecf20Sopenharmony_ci	if (!match)
4918c2ecf20Sopenharmony_ci		return -ENODEV;
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	ret = platform_driver_register(&qcom_cpufreq_driver);
4948c2ecf20Sopenharmony_ci	if (unlikely(ret < 0))
4958c2ecf20Sopenharmony_ci		return ret;
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	cpufreq_pdev = platform_device_register_data(NULL, "qcom-cpufreq-nvmem",
4988c2ecf20Sopenharmony_ci						     -1, match, sizeof(*match));
4998c2ecf20Sopenharmony_ci	ret = PTR_ERR_OR_ZERO(cpufreq_pdev);
5008c2ecf20Sopenharmony_ci	if (0 == ret)
5018c2ecf20Sopenharmony_ci		return 0;
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	platform_driver_unregister(&qcom_cpufreq_driver);
5048c2ecf20Sopenharmony_ci	return ret;
5058c2ecf20Sopenharmony_ci}
5068c2ecf20Sopenharmony_cimodule_init(qcom_cpufreq_init);
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_cistatic void __exit qcom_cpufreq_exit(void)
5098c2ecf20Sopenharmony_ci{
5108c2ecf20Sopenharmony_ci	platform_device_unregister(cpufreq_pdev);
5118c2ecf20Sopenharmony_ci	platform_driver_unregister(&qcom_cpufreq_driver);
5128c2ecf20Sopenharmony_ci}
5138c2ecf20Sopenharmony_cimodule_exit(qcom_cpufreq_exit);
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Qualcomm Technologies, Inc. CPUfreq driver");
5168c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
517