Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright (c) 2019 Samsung Electronics Co., Ltd.
0004  *        http://www.samsung.com/
0005  * Copyright (c) 2020 Krzysztof Kozlowski <krzk@kernel.org>
0006  * Author: Sylwester Nawrocki <s.nawrocki@samsung.com>
0007  * Author: Krzysztof Kozlowski <krzk@kernel.org>
0008  *
0009  * Samsung Exynos SoC Adaptive Supply Voltage support
0010  */
0011 
0012 #include <linux/cpu.h>
0013 #include <linux/device.h>
0014 #include <linux/errno.h>
0015 #include <linux/of.h>
0016 #include <linux/pm_opp.h>
0017 #include <linux/regmap.h>
0018 #include <linux/soc/samsung/exynos-chipid.h>
0019 
0020 #include "exynos-asv.h"
0021 #include "exynos5422-asv.h"
0022 
0023 #define MHZ 1000000U
0024 
0025 static int exynos_asv_update_cpu_opps(struct exynos_asv *asv,
0026                       struct device *cpu)
0027 {
0028     struct exynos_asv_subsys *subsys = NULL;
0029     struct dev_pm_opp *opp;
0030     unsigned int opp_freq;
0031     int i;
0032 
0033     for (i = 0; i < ARRAY_SIZE(asv->subsys); i++) {
0034         if (of_device_is_compatible(cpu->of_node,
0035                         asv->subsys[i].cpu_dt_compat)) {
0036             subsys = &asv->subsys[i];
0037             break;
0038         }
0039     }
0040     if (!subsys)
0041         return -EINVAL;
0042 
0043     for (i = 0; i < subsys->table.num_rows; i++) {
0044         unsigned int new_volt, volt;
0045         int ret;
0046 
0047         opp_freq = exynos_asv_opp_get_frequency(subsys, i);
0048 
0049         opp = dev_pm_opp_find_freq_exact(cpu, opp_freq * MHZ, true);
0050         if (IS_ERR(opp)) {
0051             dev_info(asv->dev, "cpu%d opp%d, freq: %u missing\n",
0052                  cpu->id, i, opp_freq);
0053 
0054             continue;
0055         }
0056 
0057         volt = dev_pm_opp_get_voltage(opp);
0058         new_volt = asv->opp_get_voltage(subsys, i, volt);
0059         dev_pm_opp_put(opp);
0060 
0061         if (new_volt == volt)
0062             continue;
0063 
0064         ret = dev_pm_opp_adjust_voltage(cpu, opp_freq * MHZ,
0065                         new_volt, new_volt, new_volt);
0066         if (ret < 0)
0067             dev_err(asv->dev,
0068                 "Failed to adjust OPP %u Hz/%u uV for cpu%d\n",
0069                 opp_freq, new_volt, cpu->id);
0070         else
0071             dev_dbg(asv->dev,
0072                 "Adjusted OPP %u Hz/%u -> %u uV, cpu%d\n",
0073                 opp_freq, volt, new_volt, cpu->id);
0074     }
0075 
0076     return 0;
0077 }
0078 
0079 static int exynos_asv_update_opps(struct exynos_asv *asv)
0080 {
0081     struct opp_table *last_opp_table = NULL;
0082     struct device *cpu;
0083     int ret, cpuid;
0084 
0085     for_each_possible_cpu(cpuid) {
0086         struct opp_table *opp_table;
0087 
0088         cpu = get_cpu_device(cpuid);
0089         if (!cpu)
0090             continue;
0091 
0092         opp_table = dev_pm_opp_get_opp_table(cpu);
0093         if (IS_ERR(opp_table))
0094             continue;
0095 
0096         if (!last_opp_table || opp_table != last_opp_table) {
0097             last_opp_table = opp_table;
0098 
0099             ret = exynos_asv_update_cpu_opps(asv, cpu);
0100             if (ret < 0)
0101                 dev_err(asv->dev, "Couldn't udate OPPs for cpu%d\n",
0102                     cpuid);
0103         }
0104 
0105         dev_pm_opp_put_opp_table(opp_table);
0106     }
0107 
0108     return  0;
0109 }
0110 
0111 int exynos_asv_init(struct device *dev, struct regmap *regmap)
0112 {
0113     int (*probe_func)(struct exynos_asv *asv);
0114     struct exynos_asv *asv;
0115     struct device *cpu_dev;
0116     u32 product_id = 0;
0117     int ret, i;
0118 
0119     asv = devm_kzalloc(dev, sizeof(*asv), GFP_KERNEL);
0120     if (!asv)
0121         return -ENOMEM;
0122 
0123     asv->chipid_regmap = regmap;
0124     asv->dev = dev;
0125     ret = regmap_read(asv->chipid_regmap, EXYNOS_CHIPID_REG_PRO_ID,
0126               &product_id);
0127     if (ret < 0) {
0128         dev_err(dev, "Cannot read revision from ChipID: %d\n", ret);
0129         return -ENODEV;
0130     }
0131 
0132     switch (product_id & EXYNOS_MASK) {
0133     case 0xE5422000:
0134         probe_func = exynos5422_asv_init;
0135         break;
0136     default:
0137         dev_dbg(dev, "No ASV support for this SoC\n");
0138         devm_kfree(dev, asv);
0139         return 0;
0140     }
0141 
0142     cpu_dev = get_cpu_device(0);
0143     ret = dev_pm_opp_get_opp_count(cpu_dev);
0144     if (ret < 0)
0145         return -EPROBE_DEFER;
0146 
0147     ret = of_property_read_u32(dev->of_node, "samsung,asv-bin",
0148                    &asv->of_bin);
0149     if (ret < 0)
0150         asv->of_bin = -EINVAL;
0151 
0152     for (i = 0; i < ARRAY_SIZE(asv->subsys); i++)
0153         asv->subsys[i].asv = asv;
0154 
0155     ret = probe_func(asv);
0156     if (ret < 0)
0157         return ret;
0158 
0159     return exynos_asv_update_opps(asv);
0160 }