···11-Qualcomm Technologies, Inc. KRYO CPUFreq and OPP bindings11+Qualcomm Technologies, Inc. NVMEM CPUFreq and OPP bindings22===================================3344-In Certain Qualcomm Technologies, Inc. SoCs like apq8096 and msm899655-that have KRYO processors, the CPU ferequencies subset and voltage value66-of each OPP varies based on the silicon variant in use.44+In Certain Qualcomm Technologies, Inc. SoCs like apq8096 and msm8996,55+the CPU frequencies subset and voltage value of each OPP varies based on66+the silicon variant in use.77Qualcomm Technologies, Inc. Process Voltage Scaling Tables88defines the voltage and frequency value based on the msm-id in SMEM99and speedbin blown in the efuse combination.1010-The qcom-cpufreq-kryo driver reads the msm-id and efuse value from the SoC1010+The qcom-cpufreq-nvmem driver reads the msm-id and efuse value from the SoC1111to provide the OPP framework with required information (existing HW bitmap).1212This is used to determine the voltage and frequency value for each OPP of1313operating-points-v2 table when it is parsed by the OPP framework.14141515Required properties:1616--------------------1717-In 'cpus' nodes:1717+In 'cpu' nodes:1818- operating-points-v2: Phandle to the operating-points-v2 table to use.19192020In 'operating-points-v2' table:2121- compatible: Should be2222 - 'operating-points-v2-kryo-cpu' for apq8096 and msm8996.2323+2424+Optional properties:2525+--------------------2626+In 'cpu' nodes:2727+- power-domains: A phandle pointing to the PM domain specifier which provides2828+ the performance states available for active state management.2929+ Please refer to the power-domains bindings3030+ Documentation/devicetree/bindings/power/power_domain.txt3131+ and also examples below.3232+- power-domain-names: Should be3333+ - 'cpr' for qcs404.3434+3535+In 'operating-points-v2' table:2336- nvmem-cells: A phandle pointing to a nvmem-cells node representing the2437 efuse registers that has information about the2538 speedbin that is used to select the right frequency/voltage···689676 reg = <0x133 0x1>;690677 bits = <5 3>;691678 };679679+ };680680+};681681+682682+Example 2:683683+---------684684+685685+ cpus {686686+ #address-cells = <1>;687687+ #size-cells = <0>;688688+689689+ CPU0: cpu@100 {690690+ device_type = "cpu";691691+ compatible = "arm,cortex-a53";692692+ reg = <0x100>;693693+ ....694694+ clocks = <&apcs_glb>;695695+ operating-points-v2 = <&cpu_opp_table>;696696+ power-domains = <&cpr>;697697+ power-domain-names = "cpr";698698+ };699699+700700+ CPU1: cpu@101 {701701+ device_type = "cpu";702702+ compatible = "arm,cortex-a53";703703+ reg = <0x101>;704704+ ....705705+ clocks = <&apcs_glb>;706706+ operating-points-v2 = <&cpu_opp_table>;707707+ power-domains = <&cpr>;708708+ power-domain-names = "cpr";709709+ };710710+711711+ CPU2: cpu@102 {712712+ device_type = "cpu";713713+ compatible = "arm,cortex-a53";714714+ reg = <0x102>;715715+ ....716716+ clocks = <&apcs_glb>;717717+ operating-points-v2 = <&cpu_opp_table>;718718+ power-domains = <&cpr>;719719+ power-domain-names = "cpr";720720+ };721721+722722+ CPU3: cpu@103 {723723+ device_type = "cpu";724724+ compatible = "arm,cortex-a53";725725+ reg = <0x103>;726726+ ....727727+ clocks = <&apcs_glb>;728728+ operating-points-v2 = <&cpu_opp_table>;729729+ power-domains = <&cpr>;730730+ power-domain-names = "cpr";731731+ };732732+ };733733+734734+ cpu_opp_table: cpu-opp-table {735735+ compatible = "operating-points-v2-kryo-cpu";736736+ opp-shared;737737+738738+ opp-1094400000 {739739+ opp-hz = /bits/ 64 <1094400000>;740740+ required-opps = <&cpr_opp1>;741741+ };742742+ opp-1248000000 {743743+ opp-hz = /bits/ 64 <1248000000>;744744+ required-opps = <&cpr_opp2>;745745+ };746746+ opp-1401600000 {747747+ opp-hz = /bits/ 64 <1401600000>;748748+ required-opps = <&cpr_opp3>;749749+ };750750+ };751751+752752+ cpr_opp_table: cpr-opp-table {753753+ compatible = "operating-points-v2-qcom-level";754754+755755+ cpr_opp1: opp1 {756756+ opp-level = <1>;757757+ qcom,opp-fuse-level = <1>;758758+ };759759+ cpr_opp2: opp2 {760760+ opp-level = <2>;761761+ qcom,opp-fuse-level = <2>;762762+ };763763+ cpr_opp3: opp3 {764764+ opp-level = <3>;765765+ qcom,opp-fuse-level = <3>;766766+ };767767+ };768768+769769+....770770+771771+soc {772772+....773773+ cpr: power-controller@b018000 {774774+ compatible = "qcom,qcs404-cpr", "qcom,cpr";775775+ reg = <0x0b018000 0x1000>;776776+ ....777777+ vdd-apc-supply = <&pms405_s3>;778778+ #power-domain-cells = <0>;779779+ operating-points-v2 = <&cpr_opp_table>;780780+ ....692781 };693782};
···11+Qualcomm OPP bindings to describe OPP nodes22+33+The bindings are based on top of the operating-points-v2 bindings44+described in Documentation/devicetree/bindings/opp/opp.txt55+Additional properties are described below.66+77+* OPP Table Node88+99+Required properties:1010+- compatible: Allow OPPs to express their compatibility. It should be:1111+ "operating-points-v2-qcom-level"1212+1313+* OPP Node1414+1515+Required properties:1616+- qcom,opp-fuse-level: A positive value representing the fuse corner/level1717+ associated with this OPP node. Sometimes several corners/levels shares1818+ a certain fuse corner/level. A fuse corner/level contains e.g. ref uV,1919+ min uV, and max uV.
+2-2
MAINTAINERS
···1329213292M: Ilia Lin <ilia.lin@kernel.org>1329313293L: linux-pm@vger.kernel.org1329413294S: Maintained1329513295-F: Documentation/devicetree/bindings/opp/kryo-cpufreq.txt1329613296-F: drivers/cpufreq/qcom-cpufreq-kryo.c1329513295+F: Documentation/devicetree/bindings/opp/qcom-nvmem-cpufreq.txt1329613296+F: drivers/cpufreq/qcom-cpufreq-nvmem.c13297132971329813298QUALCOMM EMAC GIGABIT ETHERNET DRIVER1329913299M: Timur Tabi <timur@kernel.org>
+2-2
drivers/cpufreq/Kconfig.arm
···132132 depends on ARCH_OMAP2PLUS133133 default ARCH_OMAP2PLUS134134135135-config ARM_QCOM_CPUFREQ_KRYO136136- tristate "Qualcomm Kryo based CPUFreq"135135+config ARM_QCOM_CPUFREQ_NVMEM136136+ tristate "Qualcomm nvmem based CPUFreq"137137 depends on ARM64138138 depends on QCOM_QFPROM139139 depends on QCOM_SMEM
···11-// SPDX-License-Identifier: GPL-2.022-/*33- * Copyright (c) 2018, The Linux Foundation. All rights reserved.44- */55-66-/*77- * In Certain QCOM SoCs like apq8096 and msm8996 that have KRYO processors,88- * the CPU frequency subset and voltage value of each OPP varies99- * based on the silicon variant in use. Qualcomm Process Voltage Scaling Tables1010- * defines the voltage and frequency value based on the msm-id in SMEM1111- * and speedbin blown in the efuse combination.1212- * The qcom-cpufreq-kryo driver reads the msm-id and efuse value from the SoC1313- * to provide the OPP framework with required information.1414- * This is used to determine the voltage and frequency value for each OPP of1515- * operating-points-v2 table when it is parsed by the OPP framework.1616- */1717-1818-#include <linux/cpu.h>1919-#include <linux/err.h>2020-#include <linux/init.h>2121-#include <linux/kernel.h>2222-#include <linux/module.h>2323-#include <linux/nvmem-consumer.h>2424-#include <linux/of.h>2525-#include <linux/platform_device.h>2626-#include <linux/pm_opp.h>2727-#include <linux/slab.h>2828-#include <linux/soc/qcom/smem.h>2929-3030-#define MSM_ID_SMEM 1373131-3232-enum _msm_id {3333- MSM8996V3 = 0xF6ul,3434- APQ8096V3 = 0x123ul,3535- MSM8996SG = 0x131ul,3636- APQ8096SG = 0x138ul,3737-};3838-3939-enum _msm8996_version {4040- MSM8996_V3,4141- MSM8996_SG,4242- NUM_OF_MSM8996_VERSIONS,4343-};4444-4545-static struct platform_device *cpufreq_dt_pdev, *kryo_cpufreq_pdev;4646-4747-static enum _msm8996_version qcom_cpufreq_kryo_get_msm_id(void)4848-{4949- size_t len;5050- u32 *msm_id;5151- enum _msm8996_version version;5252-5353- msm_id = qcom_smem_get(QCOM_SMEM_HOST_ANY, MSM_ID_SMEM, &len);5454- if (IS_ERR(msm_id))5555- return NUM_OF_MSM8996_VERSIONS;5656-5757- /* The first 4 bytes are format, next to them is the actual msm-id */5858- msm_id++;5959-6060- switch ((enum _msm_id)*msm_id) {6161- case MSM8996V3:6262- case APQ8096V3:6363- version = MSM8996_V3;6464- break;6565- case MSM8996SG:6666- case APQ8096SG:6767- version = MSM8996_SG;6868- break;6969- default:7070- version = NUM_OF_MSM8996_VERSIONS;7171- }7272-7373- return version;7474-}7575-7676-static int qcom_cpufreq_kryo_probe(struct platform_device *pdev)7777-{7878- struct opp_table **opp_tables;7979- enum _msm8996_version msm8996_version;8080- struct nvmem_cell *speedbin_nvmem;8181- struct device_node *np;8282- struct device *cpu_dev;8383- unsigned cpu;8484- u8 *speedbin;8585- u32 versions;8686- size_t len;8787- int ret;8888-8989- cpu_dev = get_cpu_device(0);9090- if (!cpu_dev)9191- return -ENODEV;9292-9393- msm8996_version = qcom_cpufreq_kryo_get_msm_id();9494- if (NUM_OF_MSM8996_VERSIONS == msm8996_version) {9595- dev_err(cpu_dev, "Not Snapdragon 820/821!");9696- return -ENODEV;9797- }9898-9999- np = dev_pm_opp_of_get_opp_desc_node(cpu_dev);100100- if (!np)101101- return -ENOENT;102102-103103- ret = of_device_is_compatible(np, "operating-points-v2-kryo-cpu");104104- if (!ret) {105105- of_node_put(np);106106- return -ENOENT;107107- }108108-109109- speedbin_nvmem = of_nvmem_cell_get(np, NULL);110110- of_node_put(np);111111- if (IS_ERR(speedbin_nvmem)) {112112- if (PTR_ERR(speedbin_nvmem) != -EPROBE_DEFER)113113- dev_err(cpu_dev, "Could not get nvmem cell: %ld\n",114114- PTR_ERR(speedbin_nvmem));115115- return PTR_ERR(speedbin_nvmem);116116- }117117-118118- speedbin = nvmem_cell_read(speedbin_nvmem, &len);119119- nvmem_cell_put(speedbin_nvmem);120120- if (IS_ERR(speedbin))121121- return PTR_ERR(speedbin);122122-123123- switch (msm8996_version) {124124- case MSM8996_V3:125125- versions = 1 << (unsigned int)(*speedbin);126126- break;127127- case MSM8996_SG:128128- versions = 1 << ((unsigned int)(*speedbin) + 4);129129- break;130130- default:131131- BUG();132132- break;133133- }134134- kfree(speedbin);135135-136136- opp_tables = kcalloc(num_possible_cpus(), sizeof(*opp_tables), GFP_KERNEL);137137- if (!opp_tables)138138- return -ENOMEM;139139-140140- for_each_possible_cpu(cpu) {141141- cpu_dev = get_cpu_device(cpu);142142- if (NULL == cpu_dev) {143143- ret = -ENODEV;144144- goto free_opp;145145- }146146-147147- opp_tables[cpu] = dev_pm_opp_set_supported_hw(cpu_dev,148148- &versions, 1);149149- if (IS_ERR(opp_tables[cpu])) {150150- ret = PTR_ERR(opp_tables[cpu]);151151- dev_err(cpu_dev, "Failed to set supported hardware\n");152152- goto free_opp;153153- }154154- }155155-156156- cpufreq_dt_pdev = platform_device_register_simple("cpufreq-dt", -1,157157- NULL, 0);158158- if (!IS_ERR(cpufreq_dt_pdev)) {159159- platform_set_drvdata(pdev, opp_tables);160160- return 0;161161- }162162-163163- ret = PTR_ERR(cpufreq_dt_pdev);164164- dev_err(cpu_dev, "Failed to register platform device\n");165165-166166-free_opp:167167- for_each_possible_cpu(cpu) {168168- if (IS_ERR_OR_NULL(opp_tables[cpu]))169169- break;170170- dev_pm_opp_put_supported_hw(opp_tables[cpu]);171171- }172172- kfree(opp_tables);173173-174174- return ret;175175-}176176-177177-static int qcom_cpufreq_kryo_remove(struct platform_device *pdev)178178-{179179- struct opp_table **opp_tables = platform_get_drvdata(pdev);180180- unsigned int cpu;181181-182182- platform_device_unregister(cpufreq_dt_pdev);183183-184184- for_each_possible_cpu(cpu)185185- dev_pm_opp_put_supported_hw(opp_tables[cpu]);186186-187187- kfree(opp_tables);188188-189189- return 0;190190-}191191-192192-static struct platform_driver qcom_cpufreq_kryo_driver = {193193- .probe = qcom_cpufreq_kryo_probe,194194- .remove = qcom_cpufreq_kryo_remove,195195- .driver = {196196- .name = "qcom-cpufreq-kryo",197197- },198198-};199199-200200-static const struct of_device_id qcom_cpufreq_kryo_match_list[] __initconst = {201201- { .compatible = "qcom,apq8096", },202202- { .compatible = "qcom,msm8996", },203203- {}204204-};205205-206206-/*207207- * Since the driver depends on smem and nvmem drivers, which may208208- * return EPROBE_DEFER, all the real activity is done in the probe,209209- * which may be defered as well. The init here is only registering210210- * the driver and the platform device.211211- */212212-static int __init qcom_cpufreq_kryo_init(void)213213-{214214- struct device_node *np = of_find_node_by_path("/");215215- const struct of_device_id *match;216216- int ret;217217-218218- if (!np)219219- return -ENODEV;220220-221221- match = of_match_node(qcom_cpufreq_kryo_match_list, np);222222- of_node_put(np);223223- if (!match)224224- return -ENODEV;225225-226226- ret = platform_driver_register(&qcom_cpufreq_kryo_driver);227227- if (unlikely(ret < 0))228228- return ret;229229-230230- kryo_cpufreq_pdev = platform_device_register_simple(231231- "qcom-cpufreq-kryo", -1, NULL, 0);232232- ret = PTR_ERR_OR_ZERO(kryo_cpufreq_pdev);233233- if (0 == ret)234234- return 0;235235-236236- platform_driver_unregister(&qcom_cpufreq_kryo_driver);237237- return ret;238238-}239239-module_init(qcom_cpufreq_kryo_init);240240-241241-static void __exit qcom_cpufreq_kryo_exit(void)242242-{243243- platform_device_unregister(kryo_cpufreq_pdev);244244- platform_driver_unregister(&qcom_cpufreq_kryo_driver);245245-}246246-module_exit(qcom_cpufreq_kryo_exit);247247-248248-MODULE_DESCRIPTION("Qualcomm Technologies, Inc. Kryo CPUfreq driver");249249-MODULE_LICENSE("GPL v2");
+352
drivers/cpufreq/qcom-cpufreq-nvmem.c
···11+// SPDX-License-Identifier: GPL-2.022+/*33+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.44+ */55+66+/*77+ * In Certain QCOM SoCs like apq8096 and msm8996 that have KRYO processors,88+ * the CPU frequency subset and voltage value of each OPP varies99+ * based on the silicon variant in use. Qualcomm Process Voltage Scaling Tables1010+ * defines the voltage and frequency value based on the msm-id in SMEM1111+ * and speedbin blown in the efuse combination.1212+ * The qcom-cpufreq-nvmem driver reads the msm-id and efuse value from the SoC1313+ * to provide the OPP framework with required information.1414+ * This is used to determine the voltage and frequency value for each OPP of1515+ * operating-points-v2 table when it is parsed by the OPP framework.1616+ */1717+1818+#include <linux/cpu.h>1919+#include <linux/err.h>2020+#include <linux/init.h>2121+#include <linux/kernel.h>2222+#include <linux/module.h>2323+#include <linux/nvmem-consumer.h>2424+#include <linux/of.h>2525+#include <linux/of_device.h>2626+#include <linux/platform_device.h>2727+#include <linux/pm_domain.h>2828+#include <linux/pm_opp.h>2929+#include <linux/slab.h>3030+#include <linux/soc/qcom/smem.h>3131+3232+#define MSM_ID_SMEM 1373333+3434+enum _msm_id {3535+ MSM8996V3 = 0xF6ul,3636+ APQ8096V3 = 0x123ul,3737+ MSM8996SG = 0x131ul,3838+ APQ8096SG = 0x138ul,3939+};4040+4141+enum _msm8996_version {4242+ MSM8996_V3,4343+ MSM8996_SG,4444+ NUM_OF_MSM8996_VERSIONS,4545+};4646+4747+struct qcom_cpufreq_drv;4848+4949+struct qcom_cpufreq_match_data {5050+ int (*get_version)(struct device *cpu_dev,5151+ struct nvmem_cell *speedbin_nvmem,5252+ struct qcom_cpufreq_drv *drv);5353+ const char **genpd_names;5454+};5555+5656+struct qcom_cpufreq_drv {5757+ struct opp_table **opp_tables;5858+ struct opp_table **genpd_opp_tables;5959+ u32 versions;6060+ const struct qcom_cpufreq_match_data *data;6161+};6262+6363+static struct platform_device *cpufreq_dt_pdev, *cpufreq_pdev;6464+6565+static enum _msm8996_version qcom_cpufreq_get_msm_id(void)6666+{6767+ size_t len;6868+ u32 *msm_id;6969+ enum _msm8996_version version;7070+7171+ msm_id = qcom_smem_get(QCOM_SMEM_HOST_ANY, MSM_ID_SMEM, &len);7272+ if (IS_ERR(msm_id))7373+ return NUM_OF_MSM8996_VERSIONS;7474+7575+ /* The first 4 bytes are format, next to them is the actual msm-id */7676+ msm_id++;7777+7878+ switch ((enum _msm_id)*msm_id) {7979+ case MSM8996V3:8080+ case APQ8096V3:8181+ version = MSM8996_V3;8282+ break;8383+ case MSM8996SG:8484+ case APQ8096SG:8585+ version = MSM8996_SG;8686+ break;8787+ default:8888+ version = NUM_OF_MSM8996_VERSIONS;8989+ }9090+9191+ return version;9292+}9393+9494+static int qcom_cpufreq_kryo_name_version(struct device *cpu_dev,9595+ struct nvmem_cell *speedbin_nvmem,9696+ struct qcom_cpufreq_drv *drv)9797+{9898+ size_t len;9999+ u8 *speedbin;100100+ enum _msm8996_version msm8996_version;101101+102102+ msm8996_version = qcom_cpufreq_get_msm_id();103103+ if (NUM_OF_MSM8996_VERSIONS == msm8996_version) {104104+ dev_err(cpu_dev, "Not Snapdragon 820/821!");105105+ return -ENODEV;106106+ }107107+108108+ speedbin = nvmem_cell_read(speedbin_nvmem, &len);109109+ if (IS_ERR(speedbin))110110+ return PTR_ERR(speedbin);111111+112112+ switch (msm8996_version) {113113+ case MSM8996_V3:114114+ drv->versions = 1 << (unsigned int)(*speedbin);115115+ break;116116+ case MSM8996_SG:117117+ drv->versions = 1 << ((unsigned int)(*speedbin) + 4);118118+ break;119119+ default:120120+ BUG();121121+ break;122122+ }123123+124124+ kfree(speedbin);125125+ return 0;126126+}127127+128128+static const struct qcom_cpufreq_match_data match_data_kryo = {129129+ .get_version = qcom_cpufreq_kryo_name_version,130130+};131131+132132+static const char *qcs404_genpd_names[] = { "cpr", NULL };133133+134134+static const struct qcom_cpufreq_match_data match_data_qcs404 = {135135+ .genpd_names = qcs404_genpd_names,136136+};137137+138138+static int qcom_cpufreq_probe(struct platform_device *pdev)139139+{140140+ struct qcom_cpufreq_drv *drv;141141+ struct nvmem_cell *speedbin_nvmem;142142+ struct device_node *np;143143+ struct device *cpu_dev;144144+ unsigned cpu;145145+ const struct of_device_id *match;146146+ int ret;147147+148148+ cpu_dev = get_cpu_device(0);149149+ if (!cpu_dev)150150+ return -ENODEV;151151+152152+ np = dev_pm_opp_of_get_opp_desc_node(cpu_dev);153153+ if (!np)154154+ return -ENOENT;155155+156156+ ret = of_device_is_compatible(np, "operating-points-v2-kryo-cpu");157157+ if (!ret) {158158+ of_node_put(np);159159+ return -ENOENT;160160+ }161161+162162+ drv = kzalloc(sizeof(*drv), GFP_KERNEL);163163+ if (!drv)164164+ return -ENOMEM;165165+166166+ match = pdev->dev.platform_data;167167+ drv->data = match->data;168168+ if (!drv->data) {169169+ ret = -ENODEV;170170+ goto free_drv;171171+ }172172+173173+ if (drv->data->get_version) {174174+ speedbin_nvmem = of_nvmem_cell_get(np, NULL);175175+ if (IS_ERR(speedbin_nvmem)) {176176+ if (PTR_ERR(speedbin_nvmem) != -EPROBE_DEFER)177177+ dev_err(cpu_dev,178178+ "Could not get nvmem cell: %ld\n",179179+ PTR_ERR(speedbin_nvmem));180180+ ret = PTR_ERR(speedbin_nvmem);181181+ goto free_drv;182182+ }183183+184184+ ret = drv->data->get_version(cpu_dev, speedbin_nvmem, drv);185185+ if (ret) {186186+ nvmem_cell_put(speedbin_nvmem);187187+ goto free_drv;188188+ }189189+ nvmem_cell_put(speedbin_nvmem);190190+ }191191+ of_node_put(np);192192+193193+ drv->opp_tables = kcalloc(num_possible_cpus(), sizeof(*drv->opp_tables),194194+ GFP_KERNEL);195195+ if (!drv->opp_tables) {196196+ ret = -ENOMEM;197197+ goto free_drv;198198+ }199199+200200+ drv->genpd_opp_tables = kcalloc(num_possible_cpus(),201201+ sizeof(*drv->genpd_opp_tables),202202+ GFP_KERNEL);203203+ if (!drv->genpd_opp_tables) {204204+ ret = -ENOMEM;205205+ goto free_opp;206206+ }207207+208208+ for_each_possible_cpu(cpu) {209209+ cpu_dev = get_cpu_device(cpu);210210+ if (NULL == cpu_dev) {211211+ ret = -ENODEV;212212+ goto free_genpd_opp;213213+ }214214+215215+ if (drv->data->get_version) {216216+ drv->opp_tables[cpu] =217217+ dev_pm_opp_set_supported_hw(cpu_dev,218218+ &drv->versions, 1);219219+ if (IS_ERR(drv->opp_tables[cpu])) {220220+ ret = PTR_ERR(drv->opp_tables[cpu]);221221+ dev_err(cpu_dev,222222+ "Failed to set supported hardware\n");223223+ goto free_genpd_opp;224224+ }225225+ }226226+227227+ if (drv->data->genpd_names) {228228+ drv->genpd_opp_tables[cpu] =229229+ dev_pm_opp_attach_genpd(cpu_dev,230230+ drv->data->genpd_names,231231+ NULL);232232+ if (IS_ERR(drv->genpd_opp_tables[cpu])) {233233+ ret = PTR_ERR(drv->genpd_opp_tables[cpu]);234234+ if (ret != -EPROBE_DEFER)235235+ dev_err(cpu_dev,236236+ "Could not attach to pm_domain: %d\n",237237+ ret);238238+ goto free_genpd_opp;239239+ }240240+ }241241+ }242242+243243+ cpufreq_dt_pdev = platform_device_register_simple("cpufreq-dt", -1,244244+ NULL, 0);245245+ if (!IS_ERR(cpufreq_dt_pdev)) {246246+ platform_set_drvdata(pdev, drv);247247+ return 0;248248+ }249249+250250+ ret = PTR_ERR(cpufreq_dt_pdev);251251+ dev_err(cpu_dev, "Failed to register platform device\n");252252+253253+free_genpd_opp:254254+ for_each_possible_cpu(cpu) {255255+ if (IS_ERR_OR_NULL(drv->genpd_opp_tables[cpu]))256256+ break;257257+ dev_pm_opp_detach_genpd(drv->genpd_opp_tables[cpu]);258258+ }259259+ kfree(drv->genpd_opp_tables);260260+free_opp:261261+ for_each_possible_cpu(cpu) {262262+ if (IS_ERR_OR_NULL(drv->opp_tables[cpu]))263263+ break;264264+ dev_pm_opp_put_supported_hw(drv->opp_tables[cpu]);265265+ }266266+ kfree(drv->opp_tables);267267+free_drv:268268+ kfree(drv);269269+270270+ return ret;271271+}272272+273273+static int qcom_cpufreq_remove(struct platform_device *pdev)274274+{275275+ struct qcom_cpufreq_drv *drv = platform_get_drvdata(pdev);276276+ unsigned int cpu;277277+278278+ platform_device_unregister(cpufreq_dt_pdev);279279+280280+ for_each_possible_cpu(cpu) {281281+ if (drv->opp_tables[cpu])282282+ dev_pm_opp_put_supported_hw(drv->opp_tables[cpu]);283283+ if (drv->genpd_opp_tables[cpu])284284+ dev_pm_opp_detach_genpd(drv->genpd_opp_tables[cpu]);285285+ }286286+287287+ kfree(drv->opp_tables);288288+ kfree(drv->genpd_opp_tables);289289+ kfree(drv);290290+291291+ return 0;292292+}293293+294294+static struct platform_driver qcom_cpufreq_driver = {295295+ .probe = qcom_cpufreq_probe,296296+ .remove = qcom_cpufreq_remove,297297+ .driver = {298298+ .name = "qcom-cpufreq-nvmem",299299+ },300300+};301301+302302+static const struct of_device_id qcom_cpufreq_match_list[] __initconst = {303303+ { .compatible = "qcom,apq8096", .data = &match_data_kryo },304304+ { .compatible = "qcom,msm8996", .data = &match_data_kryo },305305+ { .compatible = "qcom,qcs404", .data = &match_data_qcs404 },306306+ {},307307+};308308+309309+/*310310+ * Since the driver depends on smem and nvmem drivers, which may311311+ * return EPROBE_DEFER, all the real activity is done in the probe,312312+ * which may be defered as well. The init here is only registering313313+ * the driver and the platform device.314314+ */315315+static int __init qcom_cpufreq_init(void)316316+{317317+ struct device_node *np = of_find_node_by_path("/");318318+ const struct of_device_id *match;319319+ int ret;320320+321321+ if (!np)322322+ return -ENODEV;323323+324324+ match = of_match_node(qcom_cpufreq_match_list, np);325325+ of_node_put(np);326326+ if (!match)327327+ return -ENODEV;328328+329329+ ret = platform_driver_register(&qcom_cpufreq_driver);330330+ if (unlikely(ret < 0))331331+ return ret;332332+333333+ cpufreq_pdev = platform_device_register_data(NULL, "qcom-cpufreq-nvmem",334334+ -1, match, sizeof(*match));335335+ ret = PTR_ERR_OR_ZERO(cpufreq_pdev);336336+ if (0 == ret)337337+ return 0;338338+339339+ platform_driver_unregister(&qcom_cpufreq_driver);340340+ return ret;341341+}342342+module_init(qcom_cpufreq_init);343343+344344+static void __exit qcom_cpufreq_exit(void)345345+{346346+ platform_device_unregister(cpufreq_pdev);347347+ platform_driver_unregister(&qcom_cpufreq_driver);348348+}349349+module_exit(qcom_cpufreq_exit);350350+351351+MODULE_DESCRIPTION("Qualcomm Technologies, Inc. CPUfreq driver");352352+MODULE_LICENSE("GPL v2");
+58-10
drivers/opp/core.c
···401401}402402EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_exact);403403404404+/**405405+ * dev_pm_opp_find_level_exact() - search for an exact level406406+ * @dev: device for which we do this operation407407+ * @level: level to search for408408+ *409409+ * Return: Searches for exact match in the opp table and returns pointer to the410410+ * matching opp if found, else returns ERR_PTR in case of error and should411411+ * be handled using IS_ERR. Error return values can be:412412+ * EINVAL: for bad pointer413413+ * ERANGE: no match found for search414414+ * ENODEV: if device not found in list of registered devices415415+ *416416+ * The callers are required to call dev_pm_opp_put() for the returned OPP after417417+ * use.418418+ */419419+struct dev_pm_opp *dev_pm_opp_find_level_exact(struct device *dev,420420+ unsigned int level)421421+{422422+ struct opp_table *opp_table;423423+ struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE);424424+425425+ opp_table = _find_opp_table(dev);426426+ if (IS_ERR(opp_table)) {427427+ int r = PTR_ERR(opp_table);428428+429429+ dev_err(dev, "%s: OPP table not found (%d)\n", __func__, r);430430+ return ERR_PTR(r);431431+ }432432+433433+ mutex_lock(&opp_table->lock);434434+435435+ list_for_each_entry(temp_opp, &opp_table->opp_list, node) {436436+ if (temp_opp->level == level) {437437+ opp = temp_opp;438438+439439+ /* Increment the reference count of OPP */440440+ dev_pm_opp_get(opp);441441+ break;442442+ }443443+ }444444+445445+ mutex_unlock(&opp_table->lock);446446+ dev_pm_opp_put_opp_table(opp_table);447447+448448+ return opp;449449+}450450+EXPORT_SYMBOL_GPL(dev_pm_opp_find_level_exact);451451+404452static noinline struct dev_pm_opp *_find_freq_ceil(struct opp_table *opp_table,405453 unsigned long *freq)406454{···18191771 * dev_pm_opp_attach_genpd - Attach genpd(s) for the device and save virtual device pointer18201772 * @dev: Consumer device for which the genpd is getting attached.18211773 * @names: Null terminated array of pointers containing names of genpd to attach.17741774+ * @virt_devs: Pointer to return the array of virtual devices.18221775 *18231776 * Multiple generic power domains for a device are supported with the help of18241777 * virtual genpd devices, which are created for each consumer device - genpd···18331784 *18341785 * This helper needs to be called once with a list of all genpd to attach.18351786 * Otherwise the original device structure will be used instead by the OPP core.17871787+ *17881788+ * The order of entries in the names array must match the order in which17891789+ * "required-opps" are added in DT.18361790 */18371837-struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, const char **names)17911791+struct opp_table *dev_pm_opp_attach_genpd(struct device *dev,17921792+ const char **names, struct device ***virt_devs)18381793{18391794 struct opp_table *opp_table;18401795 struct device *virt_dev;18411841- int index, ret = -EINVAL;17961796+ int index = 0, ret = -EINVAL;18421797 const char **name = names;1843179818441799 opp_table = dev_pm_opp_get_opp_table(dev);···18681815 goto unlock;1869181618701817 while (*name) {18711871- index = of_property_match_string(dev->of_node,18721872- "power-domain-names", *name);18731873- if (index < 0) {18741874- dev_err(dev, "Failed to find power domain: %s (%d)\n",18751875- *name, index);18761876- goto err;18771877- }18781878-18791818 if (index >= opp_table->required_opp_count) {18801819 dev_err(dev, "Index can't be greater than required-opp-count - 1, %s (%d : %d)\n",18811820 *name, opp_table->required_opp_count, index);···18881843 }1889184418901845 opp_table->genpd_virt_devs[index] = virt_dev;18461846+ index++;18911847 name++;18921848 }1893184918501850+ if (virt_devs)18511851+ *virt_devs = opp_table->genpd_virt_devs;18941852 mutex_unlock(&opp_table->genpd_virt_dev_lock);1895185318961854 return opp_table;