diff options
-rw-r--r-- | Documentation/devicetree/bindings/opp/opp-v2-kryo-cpu.yaml | 36 | ||||
-rw-r--r-- | drivers/base/power/common.c | 21 | ||||
-rw-r--r-- | drivers/base/power/domain.c | 33 | ||||
-rw-r--r-- | drivers/opp/core.c | 231 | ||||
-rw-r--r-- | drivers/opp/debugfs.c | 2 | ||||
-rw-r--r-- | drivers/opp/of.c | 74 | ||||
-rw-r--r-- | drivers/opp/opp.h | 4 | ||||
-rw-r--r-- | include/linux/pm.h | 2 | ||||
-rw-r--r-- | include/linux/pm_domain.h | 6 | ||||
-rw-r--r-- | include/linux/pm_opp.h | 40 |
10 files changed, 254 insertions, 195 deletions
diff --git a/Documentation/devicetree/bindings/opp/opp-v2-kryo-cpu.yaml b/Documentation/devicetree/bindings/opp/opp-v2-kryo-cpu.yaml index bbbad31ae4ca..fd04d060c1de 100644 --- a/Documentation/devicetree/bindings/opp/opp-v2-kryo-cpu.yaml +++ b/Documentation/devicetree/bindings/opp/opp-v2-kryo-cpu.yaml @@ -26,7 +26,9 @@ description: | properties: compatible: - const: operating-points-v2-kryo-cpu + enum: + - operating-points-v2-krait-cpu + - operating-points-v2-kryo-cpu nvmem-cells: description: | @@ -47,6 +49,8 @@ patternProperties: opp-microvolt: true + opp-peak-kBps: true + opp-supported-hw: description: | A single 32 bit bitmap value, representing compatible HW. @@ -63,14 +67,22 @@ patternProperties: 5: MSM8996SG, speedbin 1 6: MSM8996SG, speedbin 2 7-31: unused - enum: [0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, - 0x9, 0xd, 0xe, 0xf, - 0x10, 0x20, 0x30, 0x70] + + Bitmap for IPQ806x SoC: + 0: IPQ8062 + 1: IPQ8064/IPQ8066/IPQ8068 + 2: IPQ8065/IPQ8069 + 3-31: unused + + Other platforms use bits directly corresponding to speedbin index. clock-latency-ns: true required-opps: true + patternProperties: + '^opp-microvolt-speed[0-9]+-pvs[0-9]+$': true + required: - opp-hz @@ -256,6 +268,22 @@ examples: }; }; + /* Dummy opp table to give example for named opp-microvolt */ + opp-table-2 { + compatible = "operating-points-v2-krait-cpu"; + nvmem-cells = <&speedbin_efuse>; + + opp-384000000 { + opp-hz = /bits/ 64 <384000000>; + opp-microvolt-speed0-pvs0 = <1000000 950000 1050000>; + opp-microvolt-speed0-pvs1 = <925000 878750 971250>; + opp-microvolt-speed0-pvs2 = <875000 831250 918750>; + opp-microvolt-speed0-pvs3 = <800000 760000 840000>; + opp-supported-hw = <0x7>; + clock-latency-ns = <100000>; + }; + }; + smem { compatible = "qcom,smem"; memory-region = <&smem_mem>; diff --git a/drivers/base/power/common.c b/drivers/base/power/common.c index 72115917e0bd..44ec20918a4d 100644 --- a/drivers/base/power/common.c +++ b/drivers/base/power/common.c @@ -228,3 +228,24 @@ void dev_pm_domain_set(struct device *dev, struct dev_pm_domain *pd) device_pm_check_callbacks(dev); } EXPORT_SYMBOL_GPL(dev_pm_domain_set); + +/** + * dev_pm_domain_set_performance_state - Request a new performance state. + * @dev: The device to make the request for. + * @state: Target performance state for the device. + * + * This function should be called when a new performance state needs to be + * requested for a device that is attached to a PM domain. Note that, the + * support for performance scaling for PM domains is optional. + * + * Returns 0 on success and when performance scaling isn't supported, negative + * error code on failure. + */ +int dev_pm_domain_set_performance_state(struct device *dev, unsigned int state) +{ + if (dev->pm_domain && dev->pm_domain->set_performance_state) + return dev->pm_domain->set_performance_state(dev, state); + + return 0; +} +EXPORT_SYMBOL_GPL(dev_pm_domain_set_performance_state); diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 5cb2023581d4..e38fc140d113 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -419,6 +419,25 @@ static void genpd_restore_performance_state(struct device *dev, genpd_set_performance_state(dev, state); } +static int genpd_dev_pm_set_performance_state(struct device *dev, + unsigned int state) +{ + struct generic_pm_domain *genpd = dev_to_genpd(dev); + int ret = 0; + + genpd_lock(genpd); + if (pm_runtime_suspended(dev)) { + dev_gpd_data(dev)->rpm_pstate = state; + } else { + ret = genpd_set_performance_state(dev, state); + if (!ret) + dev_gpd_data(dev)->rpm_pstate = 0; + } + genpd_unlock(genpd); + + return ret; +} + /** * dev_pm_genpd_set_performance_state- Set performance state of device's power * domain. @@ -437,7 +456,6 @@ static void genpd_restore_performance_state(struct device *dev, int dev_pm_genpd_set_performance_state(struct device *dev, unsigned int state) { struct generic_pm_domain *genpd; - int ret = 0; genpd = dev_to_genpd_safe(dev); if (!genpd) @@ -447,17 +465,7 @@ int dev_pm_genpd_set_performance_state(struct device *dev, unsigned int state) !dev->power.subsys_data->domain_data)) return -EINVAL; - genpd_lock(genpd); - if (pm_runtime_suspended(dev)) { - dev_gpd_data(dev)->rpm_pstate = state; - } else { - ret = genpd_set_performance_state(dev, state); - if (!ret) - dev_gpd_data(dev)->rpm_pstate = 0; - } - genpd_unlock(genpd); - - return ret; + return genpd_dev_pm_set_performance_state(dev, state); } EXPORT_SYMBOL_GPL(dev_pm_genpd_set_performance_state); @@ -2079,6 +2087,7 @@ int pm_genpd_init(struct generic_pm_domain *genpd, genpd->domain.ops.restore_noirq = genpd_restore_noirq; genpd->domain.ops.complete = genpd_complete; genpd->domain.start = genpd_dev_pm_start; + genpd->domain.set_performance_state = genpd_dev_pm_set_performance_state; if (genpd->flags & GENPD_FLAG_PM_CLK) { genpd->dev_ops.stop = pm_clk_suspend; diff --git a/drivers/opp/core.c b/drivers/opp/core.c index 919cc53bc02e..84f345c69ea5 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -814,6 +814,31 @@ struct dev_pm_opp *dev_pm_opp_find_level_ceil(struct device *dev, EXPORT_SYMBOL_GPL(dev_pm_opp_find_level_ceil); /** + * dev_pm_opp_find_level_floor() - Search for a rounded floor level + * @dev: device for which we do this operation + * @level: Start level + * + * Search for the matching floor *available* OPP from a starting level + * for a device. + * + * Return: matching *opp and refreshes *level accordingly, else returns + * ERR_PTR in case of error and should be handled using IS_ERR. Error return + * values can be: + * EINVAL: for bad pointer + * ERANGE: no match found for search + * ENODEV: if device not found in list of registered devices + * + * The callers are required to call dev_pm_opp_put() for the returned OPP after + * use. + */ +struct dev_pm_opp *dev_pm_opp_find_level_floor(struct device *dev, + unsigned long *level) +{ + return _find_key_floor(dev, level, 0, true, _read_level, NULL); +} +EXPORT_SYMBOL_GPL(dev_pm_opp_find_level_floor); + +/** * dev_pm_opp_find_bw_ceil() - Search for a rounded ceil bandwidth * @dev: device for which we do this operation * @bw: start bandwidth @@ -1030,7 +1055,7 @@ static int _set_performance_state(struct device *dev, struct device *pd_dev, if (!pd_dev) return 0; - ret = dev_pm_genpd_set_performance_state(pd_dev, pstate); + ret = dev_pm_domain_set_performance_state(pd_dev, pstate); if (ret) { dev_err(dev, "Failed to set performance state of %s: %d (%d)\n", dev_name(pd_dev), pstate, ret); @@ -1051,32 +1076,28 @@ static int _opp_set_required_opps_genpd(struct device *dev, { struct device **genpd_virt_devs = opp_table->genpd_virt_devs ? opp_table->genpd_virt_devs : &dev; - int i, ret = 0; - - /* - * Acquire genpd_virt_dev_lock to make sure we don't use a genpd_dev - * after it is freed from another thread. - */ - mutex_lock(&opp_table->genpd_virt_dev_lock); + int index, target, delta, ret; /* Scaling up? Set required OPPs in normal order, else reverse */ if (!scaling_down) { - for (i = 0; i < opp_table->required_opp_count; i++) { - ret = _set_performance_state(dev, genpd_virt_devs[i], opp, i); - if (ret) - break; - } + index = 0; + target = opp_table->required_opp_count; + delta = 1; } else { - for (i = opp_table->required_opp_count - 1; i >= 0; i--) { - ret = _set_performance_state(dev, genpd_virt_devs[i], opp, i); - if (ret) - break; - } + index = opp_table->required_opp_count - 1; + target = -1; + delta = -1; } - mutex_unlock(&opp_table->genpd_virt_dev_lock); + while (index != target) { + ret = _set_performance_state(dev, genpd_virt_devs[index], opp, index); + if (ret) + return ret; - return ret; + index += delta; + } + + return 0; } /* This is only called for PM domain for now */ @@ -1107,6 +1128,28 @@ void _update_set_required_opps(struct opp_table *opp_table) opp_table->set_required_opps = _opp_set_required_opps_generic; } +static int _set_opp_level(struct device *dev, struct opp_table *opp_table, + struct dev_pm_opp *opp) +{ + unsigned int level = 0; + int ret = 0; + + if (opp) { + if (!opp->level) + return 0; + + level = opp->level; + } + + /* Request a new performance state through the device's PM domain. */ + ret = dev_pm_domain_set_performance_state(dev, level); + if (ret) + dev_err(dev, "Failed to set performance state %u (%d)\n", level, + ret); + + return ret; +} + static void _find_current_opp(struct device *dev, struct opp_table *opp_table) { struct dev_pm_opp *opp = ERR_PTR(-ENODEV); @@ -1154,8 +1197,13 @@ static int _disable_opp_table(struct device *dev, struct opp_table *opp_table) if (opp_table->regulators) regulator_disable(opp_table->regulators[0]); + ret = _set_opp_level(dev, opp_table, NULL); + if (ret) + goto out; + ret = _set_required_opps(dev, opp_table, NULL, false); +out: opp_table->enabled = false; return ret; } @@ -1198,6 +1246,10 @@ static int _set_opp(struct device *dev, struct opp_table *opp_table, return ret; } + ret = _set_opp_level(dev, opp_table, opp); + if (ret) + return ret; + ret = _set_opp_bw(opp_table, opp, dev); if (ret) { dev_err(dev, "Failed to set bw: %d\n", ret); @@ -1241,6 +1293,10 @@ static int _set_opp(struct device *dev, struct opp_table *opp_table, return ret; } + ret = _set_opp_level(dev, opp_table, opp); + if (ret) + return ret; + ret = _set_required_opps(dev, opp_table, opp, false); if (ret) { dev_err(dev, "Failed to set required opps: %d\n", ret); @@ -1410,7 +1466,6 @@ static struct opp_table *_allocate_opp_table(struct device *dev, int index) return ERR_PTR(-ENOMEM); mutex_init(&opp_table->lock); - mutex_init(&opp_table->genpd_virt_dev_lock); INIT_LIST_HEAD(&opp_table->dev_list); INIT_LIST_HEAD(&opp_table->lazy); @@ -1446,7 +1501,6 @@ static struct opp_table *_allocate_opp_table(struct device *dev, int index) remove_opp_dev: _of_clear_opp_table(opp_table); _remove_opp_dev(opp_dev, opp_table); - mutex_destroy(&opp_table->genpd_virt_dev_lock); mutex_destroy(&opp_table->lock); err: kfree(opp_table); @@ -1614,7 +1668,6 @@ static void _opp_table_kref_release(struct kref *kref) list_for_each_entry_safe(opp_dev, temp, &opp_table->dev_list, node) _remove_opp_dev(opp_dev, opp_table); - mutex_destroy(&opp_table->genpd_virt_dev_lock); mutex_destroy(&opp_table->lock); kfree(opp_table); } @@ -2002,8 +2055,7 @@ int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, * _opp_add_v1() - Allocate a OPP based on v1 bindings. * @opp_table: OPP table * @dev: device for which we do this operation - * @freq: Frequency in Hz for this OPP - * @u_volt: Voltage in uVolts for this OPP + * @data: The OPP data for the OPP to add * @dynamic: Dynamically added OPPs. * * This function adds an opp definition to the opp table and returns status. @@ -2021,10 +2073,10 @@ int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, * -ENOMEM Memory allocation failure */ int _opp_add_v1(struct opp_table *opp_table, struct device *dev, - unsigned long freq, long u_volt, bool dynamic) + struct dev_pm_opp_data *data, bool dynamic) { struct dev_pm_opp *new_opp; - unsigned long tol; + unsigned long tol, u_volt = data->u_volt; int ret; if (!assert_single_clk(opp_table)) @@ -2035,7 +2087,8 @@ int _opp_add_v1(struct opp_table *opp_table, struct device *dev, return -ENOMEM; /* populate the opp table */ - new_opp->rates[0] = freq; + new_opp->rates[0] = data->freq; + new_opp->level = data->level; tol = u_volt * opp_table->voltage_tolerance_v1 / 100; new_opp->supplies[0].u_volt = u_volt; new_opp->supplies[0].u_volt_min = u_volt - tol; @@ -2064,12 +2117,7 @@ free_opp: return ret; } -/** - * _opp_set_supported_hw() - Set supported platforms - * @dev: Device for which supported-hw has to be set. - * @versions: Array of hierarchy of versions to match. - * @count: Number of elements in the array. - * +/* * This is required only for the V2 bindings, and it enables a platform to * specify the hierarchy of versions it supports. OPP layer will then enable * OPPs, which are available for those versions, based on its 'opp-supported-hw' @@ -2092,14 +2140,6 @@ static int _opp_set_supported_hw(struct opp_table *opp_table, return 0; } -/** - * _opp_put_supported_hw() - Releases resources blocked for supported hw - * @opp_table: OPP table returned by _opp_set_supported_hw(). - * - * This is required only for the V2 bindings, and is called for a matching - * _opp_set_supported_hw(). Until this is called, the opp_table structure - * will not be freed. - */ static void _opp_put_supported_hw(struct opp_table *opp_table) { if (opp_table->supported_hw) { @@ -2109,11 +2149,7 @@ static void _opp_put_supported_hw(struct opp_table *opp_table) } } -/** - * _opp_set_prop_name() - Set prop-extn name - * @dev: Device for which the prop-name has to be set. - * @name: name to postfix to properties. - * +/* * This is required only for the V2 bindings, and it enables a platform to * specify the extn to be used for certain property names. The properties to * which the extension will apply are opp-microvolt and opp-microamp. OPP core @@ -2131,14 +2167,6 @@ static int _opp_set_prop_name(struct opp_table *opp_table, const char *name) return 0; } -/** - * _opp_put_prop_name() - Releases resources blocked for prop-name - * @opp_table: OPP table returned by _opp_set_prop_name(). - * - * This is required only for the V2 bindings, and is called for a matching - * _opp_set_prop_name(). Until this is called, the opp_table structure - * will not be freed. - */ static void _opp_put_prop_name(struct opp_table *opp_table) { if (opp_table->prop_name) { @@ -2147,12 +2175,7 @@ static void _opp_put_prop_name(struct opp_table *opp_table) } } -/** - * _opp_set_regulators() - Set regulator names for the device - * @dev: Device for which regulator name is being set. - * @names: Array of pointers to the names of the regulator. - * @count: Number of regulators. - * +/* * In order to support OPP switching, OPP layer needs to know the name of the * device's regulators, as the core would be required to switch voltages as * well. @@ -2214,10 +2237,6 @@ free_regulators: return ret; } -/** - * _opp_put_regulators() - Releases resources blocked for regulator - * @opp_table: OPP table returned from _opp_set_regulators(). - */ static void _opp_put_regulators(struct opp_table *opp_table) { int i; @@ -2249,11 +2268,7 @@ static void _put_clks(struct opp_table *opp_table, int count) opp_table->clks = NULL; } -/** - * _opp_set_clknames() - Set clk names for the device - * @dev: Device for which clk names is being set. - * @names: Clk names. - * +/* * In order to support OPP switching, OPP layer needs to get pointers to the * clocks for the device. Simple cases work fine without using this routine * (i.e. by passing connection-id as NULL), but for a device with multiple @@ -2337,10 +2352,6 @@ free_clks: return ret; } -/** - * _opp_put_clknames() - Releases resources blocked for clks. - * @opp_table: OPP table returned from _opp_set_clknames(). - */ static void _opp_put_clknames(struct opp_table *opp_table) { if (!opp_table->clks) @@ -2352,11 +2363,7 @@ static void _opp_put_clknames(struct opp_table *opp_table) _put_clks(opp_table, opp_table->clk_count); } -/** - * _opp_set_config_regulators_helper() - Register custom set regulator helper. - * @dev: Device for which the helper is getting registered. - * @config_regulators: Custom set regulator helper. - * +/* * This is useful to support platforms with multiple regulators per device. * * This must be called before any OPPs are initialized for the device. @@ -2371,20 +2378,13 @@ static int _opp_set_config_regulators_helper(struct opp_table *opp_table, return 0; } -/** - * _opp_put_config_regulators_helper() - Releases resources blocked for - * config_regulators helper. - * @opp_table: OPP table returned from _opp_set_config_regulators_helper(). - * - * Release resources blocked for platform specific config_regulators helper. - */ static void _opp_put_config_regulators_helper(struct opp_table *opp_table) { if (opp_table->config_regulators) opp_table->config_regulators = NULL; } -static void _detach_genpd(struct opp_table *opp_table) +static void _opp_detach_genpd(struct opp_table *opp_table) { int index; @@ -2403,12 +2403,7 @@ static void _detach_genpd(struct opp_table *opp_table) opp_table->genpd_virt_devs = NULL; } -/** - * _opp_attach_genpd - Attach genpd(s) for the device and save virtual device pointer - * @dev: Consumer device for which the genpd is getting attached. - * @names: Null terminated array of pointers containing names of genpd to attach. - * @virt_devs: Pointer to return the array of virtual devices. - * +/* * Multiple generic power domains for a device are supported with the help of * virtual genpd devices, which are created for each consumer device - genpd * pair. These are the device structures which are attached to the power domain @@ -2435,21 +2430,11 @@ static int _opp_attach_genpd(struct opp_table *opp_table, struct device *dev, if (opp_table->genpd_virt_devs) return 0; - /* - * If the genpd's OPP table isn't already initialized, parsing of the - * required-opps fail for dev. We should retry this after genpd's OPP - * table is added. - */ - if (!opp_table->required_opp_count) - return -EPROBE_DEFER; - - mutex_lock(&opp_table->genpd_virt_dev_lock); - opp_table->genpd_virt_devs = kcalloc(opp_table->required_opp_count, sizeof(*opp_table->genpd_virt_devs), GFP_KERNEL); if (!opp_table->genpd_virt_devs) - goto unlock; + return -ENOMEM; while (*name) { if (index >= opp_table->required_opp_count) { @@ -2472,36 +2457,15 @@ static int _opp_attach_genpd(struct opp_table *opp_table, struct device *dev, if (virt_devs) *virt_devs = opp_table->genpd_virt_devs; - mutex_unlock(&opp_table->genpd_virt_dev_lock); return 0; err: - _detach_genpd(opp_table); -unlock: - mutex_unlock(&opp_table->genpd_virt_dev_lock); + _opp_detach_genpd(opp_table); return ret; } -/** - * _opp_detach_genpd() - Detach genpd(s) from the device. - * @opp_table: OPP table returned by _opp_attach_genpd(). - * - * This detaches the genpd(s), resets the virtual device pointers, and puts the - * OPP table. - */ -static void _opp_detach_genpd(struct opp_table *opp_table) -{ - /* - * Acquire genpd_virt_dev_lock to make sure virt_dev isn't getting - * used in parallel. - */ - mutex_lock(&opp_table->genpd_virt_dev_lock); - _detach_genpd(opp_table); - mutex_unlock(&opp_table->genpd_virt_dev_lock); -} - static void _opp_clear_config(struct opp_config_data *data) { if (data->flags & OPP_CONFIG_GENPD) @@ -2642,7 +2606,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_set_config); /** * dev_pm_opp_clear_config() - Releases resources blocked for OPP configuration. - * @opp_table: OPP table returned from dev_pm_opp_set_config(). + * @token: The token returned by dev_pm_opp_set_config() previously. * * This allows all device OPP configurations to be cleared at once. This must be * called once for each call made to dev_pm_opp_set_config(), in order to free @@ -2825,10 +2789,9 @@ unlock: } /** - * dev_pm_opp_add() - Add an OPP table from a table definitions - * @dev: device for which we do this operation - * @freq: Frequency in Hz for this OPP - * @u_volt: Voltage in uVolts for this OPP + * dev_pm_opp_add_dynamic() - Add an OPP table from a table definitions + * @dev: The device for which we do this operation + * @data: The OPP data for the OPP to add * * This function adds an opp definition to the opp table and returns status. * The opp is made available by default and it can be controlled using @@ -2841,7 +2804,7 @@ unlock: * Duplicate OPPs (both freq and volt are same) and !opp->available * -ENOMEM Memory allocation failure */ -int dev_pm_opp_add(struct device *dev, unsigned long freq, unsigned long u_volt) +int dev_pm_opp_add_dynamic(struct device *dev, struct dev_pm_opp_data *data) { struct opp_table *opp_table; int ret; @@ -2853,13 +2816,13 @@ int dev_pm_opp_add(struct device *dev, unsigned long freq, unsigned long u_volt) /* Fix regulator count for dynamic OPPs */ opp_table->regulator_count = 1; - ret = _opp_add_v1(opp_table, dev, freq, u_volt, true); + ret = _opp_add_v1(opp_table, dev, data, true); if (ret) dev_pm_opp_put_opp_table(opp_table); return ret; } -EXPORT_SYMBOL_GPL(dev_pm_opp_add); +EXPORT_SYMBOL_GPL(dev_pm_opp_add_dynamic); /** * _opp_set_availability() - helper to set the availability of an opp diff --git a/drivers/opp/debugfs.c b/drivers/opp/debugfs.c index 17543c0aa5b6..ec030b19164a 100644 --- a/drivers/opp/debugfs.c +++ b/drivers/opp/debugfs.c @@ -56,7 +56,7 @@ static void opp_debug_create_bw(struct dev_pm_opp *opp, struct dentry *pdentry) { struct dentry *d; - char name[11]; + char name[20]; int i; for (i = 0; i < opp_table->path_count; i++) { diff --git a/drivers/opp/of.c b/drivers/opp/of.c index ada4963c7cfa..81fa27599d58 100644 --- a/drivers/opp/of.c +++ b/drivers/opp/of.c @@ -208,9 +208,9 @@ static void _opp_table_alloc_required_tables(struct opp_table *opp_table, mutex_lock(&opp_table_lock); list_add(&opp_table->lazy, &lazy_opp_tables); mutex_unlock(&opp_table_lock); - } - else + } else { _update_set_required_opps(opp_table); + } goto put_np; @@ -296,24 +296,41 @@ void _of_clear_opp(struct opp_table *opp_table, struct dev_pm_opp *opp) of_node_put(opp->np); } +static int _link_required_opps(struct dev_pm_opp *opp, + struct opp_table *required_table, int index) +{ + struct device_node *np; + + np = of_parse_required_opp(opp->np, index); + if (unlikely(!np)) + return -ENODEV; + + opp->required_opps[index] = _find_opp_of_np(required_table, np); + of_node_put(np); + + if (!opp->required_opps[index]) { + pr_err("%s: Unable to find required OPP node: %pOF (%d)\n", + __func__, opp->np, index); + return -ENODEV; + } + + return 0; +} + /* Populate all required OPPs which are part of "required-opps" list */ static int _of_opp_alloc_required_opps(struct opp_table *opp_table, struct dev_pm_opp *opp) { - struct dev_pm_opp **required_opps; struct opp_table *required_table; - struct device_node *np; int i, ret, count = opp_table->required_opp_count; if (!count) return 0; - required_opps = kcalloc(count, sizeof(*required_opps), GFP_KERNEL); - if (!required_opps) + opp->required_opps = kcalloc(count, sizeof(*opp->required_opps), GFP_KERNEL); + if (!opp->required_opps) return -ENOMEM; - opp->required_opps = required_opps; - for (i = 0; i < count; i++) { required_table = opp_table->required_opp_tables[i]; @@ -321,21 +338,9 @@ static int _of_opp_alloc_required_opps(struct opp_table *opp_table, if (IS_ERR_OR_NULL(required_table)) continue; - np = of_parse_required_opp(opp->np, i); - if (unlikely(!np)) { - ret = -ENODEV; - goto free_required_opps; - } - - required_opps[i] = _find_opp_of_np(required_table, np); - of_node_put(np); - - if (!required_opps[i]) { - pr_err("%s: Unable to find required OPP node: %pOF (%d)\n", - __func__, opp->np, i); - ret = -ENODEV; + ret = _link_required_opps(opp, required_table, i); + if (ret) goto free_required_opps; - } } return 0; @@ -350,22 +355,13 @@ free_required_opps: static int lazy_link_required_opps(struct opp_table *opp_table, struct opp_table *new_table, int index) { - struct device_node *required_np; struct dev_pm_opp *opp; + int ret; list_for_each_entry(opp, &opp_table->opp_list, node) { - required_np = of_parse_required_opp(opp->np, index); - if (unlikely(!required_np)) - return -ENODEV; - - opp->required_opps[index] = _find_opp_of_np(new_table, required_np); - of_node_put(required_np); - - if (!opp->required_opps[index]) { - pr_err("%s: Unable to find required OPP node: %pOF (%d)\n", - __func__, opp->np, index); - return -ENODEV; - } + ret = _link_required_opps(opp, new_table, index); + if (ret) + return ret; } return 0; @@ -1079,11 +1075,15 @@ static int _of_add_opp_table_v1(struct device *dev, struct opp_table *opp_table) while (nr) { unsigned long freq = be32_to_cpup(val++) * 1000; unsigned long volt = be32_to_cpup(val++); + struct dev_pm_opp_data data = { + .freq = freq, + .u_volt = volt, + }; - ret = _opp_add_v1(opp_table, dev, freq, volt, false); + ret = _opp_add_v1(opp_table, dev, &data, false); if (ret) { dev_err(dev, "%s: Failed to add OPP %ld (%d)\n", - __func__, freq, ret); + __func__, data.freq, ret); goto remove_static_opp; } nr -= 2; diff --git a/drivers/opp/opp.h b/drivers/opp/opp.h index 8a5ea38f3a3d..08366f90f16b 100644 --- a/drivers/opp/opp.h +++ b/drivers/opp/opp.h @@ -160,7 +160,6 @@ enum opp_table_access { * @rate_clk_single: Currently configured frequency for single clk. * @current_opp: Currently configured OPP for the table. * @suspend_opp: Pointer to OPP to be used during device suspend. - * @genpd_virt_dev_lock: Mutex protecting the genpd virtual device pointers. * @genpd_virt_devs: List of virtual devices for multiple genpd support. * @required_opp_tables: List of device OPP tables that are required by OPPs in * this table. @@ -212,7 +211,6 @@ struct opp_table { struct dev_pm_opp *current_opp; struct dev_pm_opp *suspend_opp; - struct mutex genpd_virt_dev_lock; struct device **genpd_virt_devs; struct opp_table **required_opp_tables; unsigned int required_opp_count; @@ -251,7 +249,7 @@ struct dev_pm_opp *_opp_allocate(struct opp_table *opp_table); void _opp_free(struct dev_pm_opp *opp); int _opp_compare_key(struct opp_table *opp_table, struct dev_pm_opp *opp1, struct dev_pm_opp *opp2); int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, struct opp_table *opp_table); -int _opp_add_v1(struct opp_table *opp_table, struct device *dev, unsigned long freq, long u_volt, bool dynamic); +int _opp_add_v1(struct opp_table *opp_table, struct device *dev, struct dev_pm_opp_data *data, bool dynamic); void _dev_pm_opp_cpumask_remove_table(const struct cpumask *cpumask, int last_cpu); struct opp_table *_add_opp_table_indexed(struct device *dev, int index, bool getclk); void _put_opp_list_kref(struct opp_table *opp_table); diff --git a/include/linux/pm.h b/include/linux/pm.h index 1400c37b29c7..4c9f571609c8 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -719,6 +719,7 @@ extern void dev_pm_put_subsys_data(struct device *dev); * @activate: Called before executing probe routines for bus types and drivers. * @sync: Called after successful driver probe. * @dismiss: Called after unsuccessful driver probe and after driver removal. + * @set_performance_state: Called to request a new performance state. * * Power domains provide callbacks that are executed during system suspend, * hibernation, system resume and during runtime PM transitions instead of @@ -731,6 +732,7 @@ struct dev_pm_domain { int (*activate)(struct device *dev); void (*sync)(struct device *dev); void (*dismiss)(struct device *dev); + int (*set_performance_state)(struct device *dev, unsigned int state); }; /* diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h index f776fb93eaa0..bda2964a0a56 100644 --- a/include/linux/pm_domain.h +++ b/include/linux/pm_domain.h @@ -430,6 +430,7 @@ struct device *dev_pm_domain_attach_by_name(struct device *dev, void dev_pm_domain_detach(struct device *dev, bool power_off); int dev_pm_domain_start(struct device *dev); void dev_pm_domain_set(struct device *dev, struct dev_pm_domain *pd); +int dev_pm_domain_set_performance_state(struct device *dev, unsigned int state); #else static inline int dev_pm_domain_attach(struct device *dev, bool power_on) { @@ -452,6 +453,11 @@ static inline int dev_pm_domain_start(struct device *dev) } static inline void dev_pm_domain_set(struct device *dev, struct dev_pm_domain *pd) {} +static inline int dev_pm_domain_set_performance_state(struct device *dev, + unsigned int state) +{ + return 0; +} #endif #endif /* _LINUX_PM_DOMAIN_H */ diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h index 91f87d7e807c..ccd97bcef269 100644 --- a/include/linux/pm_opp.h +++ b/include/linux/pm_opp.h @@ -92,6 +92,18 @@ struct dev_pm_opp_config { struct device ***virt_devs; }; +/** + * struct dev_pm_opp_data - The data to use to initialize an OPP. + * @level: The performance level for the OPP. + * @freq: The clock rate in Hz for the OPP. + * @u_volt: The voltage in uV for the OPP. + */ +struct dev_pm_opp_data { + unsigned int level; + unsigned long freq; + unsigned long u_volt; +}; + #if defined(CONFIG_PM_OPP) struct opp_table *dev_pm_opp_get_opp_table(struct device *dev); @@ -144,6 +156,9 @@ struct dev_pm_opp *dev_pm_opp_find_level_exact(struct device *dev, struct dev_pm_opp *dev_pm_opp_find_level_ceil(struct device *dev, unsigned int *level); +struct dev_pm_opp *dev_pm_opp_find_level_floor(struct device *dev, + unsigned long *level); + struct dev_pm_opp *dev_pm_opp_find_bw_ceil(struct device *dev, unsigned int *bw, int index); @@ -152,8 +167,8 @@ struct dev_pm_opp *dev_pm_opp_find_bw_floor(struct device *dev, void dev_pm_opp_put(struct dev_pm_opp *opp); -int dev_pm_opp_add(struct device *dev, unsigned long freq, - unsigned long u_volt); +int dev_pm_opp_add_dynamic(struct device *dev, struct dev_pm_opp_data *opp); + void dev_pm_opp_remove(struct device *dev, unsigned long freq); void dev_pm_opp_remove_all_dynamic(struct device *dev); @@ -308,6 +323,12 @@ static inline struct dev_pm_opp *dev_pm_opp_find_level_ceil(struct device *dev, return ERR_PTR(-EOPNOTSUPP); } +static inline struct dev_pm_opp *dev_pm_opp_find_level_floor(struct device *dev, + unsigned long *level) +{ + return ERR_PTR(-EOPNOTSUPP); +} + static inline struct dev_pm_opp *dev_pm_opp_find_bw_ceil(struct device *dev, unsigned int *bw, int index) { @@ -322,8 +343,8 @@ static inline struct dev_pm_opp *dev_pm_opp_find_bw_floor(struct device *dev, static inline void dev_pm_opp_put(struct dev_pm_opp *opp) {} -static inline int dev_pm_opp_add(struct device *dev, unsigned long freq, - unsigned long u_volt) +static inline int +dev_pm_opp_add_dynamic(struct device *dev, struct dev_pm_opp_data *opp) { return -EOPNOTSUPP; } @@ -519,6 +540,17 @@ static inline int dev_pm_opp_of_find_icc_paths(struct device *dev, struct opp_ta /* OPP Configuration helpers */ +static inline int dev_pm_opp_add(struct device *dev, unsigned long freq, + unsigned long u_volt) +{ + struct dev_pm_opp_data data = { + .freq = freq, + .u_volt = u_volt, + }; + + return dev_pm_opp_add_dynamic(dev, &data); +} + /* Regulators helpers */ static inline int dev_pm_opp_set_regulators(struct device *dev, const char * const names[]) |