Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branches 'pm-cpuidle', 'pm-qos', 'pm-devfreq' and 'pm-opp'

Merge a cpuidle update, a PM QoS update, devfreq updates, and an OPP
(operating performance points) update for 6.17-rc1:

- Fix opencoded for_each_cpu() in idle_state_valid() in the DT cpuidle
driver (Yury Norov)

- Remove info about non-existing QoS interfaces from the PM QoS
documentation (Ulf Hansson)

- Use c_* types via kernel prelude in Rust for OPP (Abhinav Ananthu)

- Add HiSilicon uncore frequency scaling driver to devfreq (Jie Zhan)

- Allow devfreq drivers to add custom sysfs ABIs (Jie Zhan)

- Simplify the sun8i-a33-mbus devfreq driver by using more devm
functions (Uwe Kleine-König)

- Fix an index typo in trans_stat() in devfreq (Chanwoo Choi)

- Check devfreq governor before using governor->name (Lifeng Zheng)

- Remove a redundant devfreq_get_freq_range() call from
devfreq_add_device() (Lifeng Zheng)

- Limit max_freq with scaling_min_freq in devfreq (Lifeng Zheng)

- Replace sscanf() with kstrtoul() in set_freq_store() (Lifeng Zheng)

* pm-cpuidle:
cpuidle: dt: fix opencoded for_each_cpu() in idle_state_valid()

* pm-qos:
Documentation: power: Remove info about non-existing QoS interfaces

* pm-devfreq:
PM / devfreq: Add HiSilicon uncore frequency scaling driver
PM / devfreq: Allow devfreq driver to add custom sysfs ABIs
PM / devfreq: sun8i-a33-mbus: Simplify by using more devm functions
PM / devfreq: Fix a index typo in trans_stat
PM / devfreq: Check governor before using governor->name
PM / devfreq: Remove redundant devfreq_get_freq_range() calling in devfreq_add_device()
PM / devfreq: Limit max_freq with scaling_min_freq
PM / devfreq: governor: Replace sscanf() with kstrtoul() in set_freq_store()

* pm-opp:
rust: opp: use c_* types via kernel prelude

+713 -66
+9
Documentation/ABI/testing/sysfs-class-devfreq
··· 132 132 133 133 A list of governors that support the node: 134 134 - simple_ondemand 135 + 136 + What: /sys/class/devfreq/.../related_cpus 137 + Date: June 2025 138 + Contact: Linux power management list <linux-pm@vger.kernel.org> 139 + Description: The list of CPUs whose performance is closely related to the 140 + frequency of this devfreq domain. 141 + 142 + This file is only present if a specific devfreq device is 143 + closely associated with a subset of CPUs.
-7
Documentation/power/pm_qos_interface.rst
··· 52 52 Returns if the request is still active, i.e. it has not been removed from the 53 53 CPU latency QoS list. 54 54 55 - int cpu_latency_qos_add_notifier(notifier): 56 - Adds a notification callback function to the CPU latency QoS. The callback is 57 - called when the aggregated value for the CPU latency QoS is changed. 58 - 59 - int cpu_latency_qos_remove_notifier(notifier): 60 - Removes the notification callback function from the CPU latency QoS. 61 - 62 55 63 56 From user space: 64 57
+5 -9
drivers/cpuidle/dt_idle_states.c
··· 98 98 { 99 99 int cpu; 100 100 struct device_node *cpu_node, *curr_state_node; 101 - bool valid = true; 102 101 103 102 /* 104 103 * Compare idle state phandles for index idx on all CPUs in the ··· 106 107 * retrieved from. If a mismatch is found bail out straight 107 108 * away since we certainly hit a firmware misconfiguration. 108 109 */ 109 - for (cpu = cpumask_next(cpumask_first(cpumask), cpumask); 110 - cpu < nr_cpu_ids; cpu = cpumask_next(cpu, cpumask)) { 110 + cpu = cpumask_first(cpumask) + 1; 111 + for_each_cpu_from(cpu, cpumask) { 111 112 cpu_node = of_cpu_device_node_get(cpu); 112 113 curr_state_node = of_get_cpu_state_node(cpu_node, idx); 113 - if (state_node != curr_state_node) 114 - valid = false; 115 - 116 114 of_node_put(curr_state_node); 117 115 of_node_put(cpu_node); 118 - if (!valid) 119 - break; 116 + if (state_node != curr_state_node) 117 + return false; 120 118 } 121 119 122 - return valid; 120 + return true; 123 121 } 124 122 125 123 /**
+11
drivers/devfreq/Kconfig
··· 90 90 and adjusts the operating frequencies and voltages with OPP support. 91 91 This does not yet operate with optimal voltages. 92 92 93 + config ARM_HISI_UNCORE_DEVFREQ 94 + tristate "HiSilicon uncore DEVFREQ Driver" 95 + depends on ACPI && ACPI_PPTT && PCC 96 + select DEVFREQ_GOV_PERFORMANCE 97 + select DEVFREQ_GOV_USERSPACE 98 + help 99 + This adds a DEVFREQ driver that manages uncore frequency scaling for 100 + HiSilicon Kunpeng SoCs. This enables runtime management of uncore 101 + frequency scaling from kernel and userspace. The uncore domain 102 + contains system interconnects and L3 cache. 103 + 93 104 config ARM_IMX_BUS_DEVFREQ 94 105 tristate "i.MX Generic Bus DEVFREQ Driver" 95 106 depends on ARCH_MXC || COMPILE_TEST
+1
drivers/devfreq/Makefile
··· 9 9 10 10 # DEVFREQ Drivers 11 11 obj-$(CONFIG_ARM_EXYNOS_BUS_DEVFREQ) += exynos-bus.o 12 + obj-$(CONFIG_ARM_HISI_UNCORE_DEVFREQ) += hisi_uncore_freq.o 12 13 obj-$(CONFIG_ARM_IMX_BUS_DEVFREQ) += imx-bus.o 13 14 obj-$(CONFIG_ARM_IMX8M_DDRC_DEVFREQ) += imx8m-ddrc.o 14 15 obj-$(CONFIG_ARM_MEDIATEK_CCI_DEVFREQ) += mtk-cci-devfreq.o
+7 -16
drivers/devfreq/devfreq.c
··· 152 152 (unsigned long)HZ_PER_KHZ * qos_max_freq); 153 153 154 154 /* Apply constraints from OPP interface */ 155 - *min_freq = max(*min_freq, devfreq->scaling_min_freq); 156 - *max_freq = min(*max_freq, devfreq->scaling_max_freq); 157 - 158 - if (*min_freq > *max_freq) 159 - *min_freq = *max_freq; 155 + *max_freq = clamp(*max_freq, devfreq->scaling_min_freq, devfreq->scaling_max_freq); 156 + *min_freq = clamp(*min_freq, devfreq->scaling_min_freq, *max_freq); 160 157 } 161 158 EXPORT_SYMBOL(devfreq_get_freq_range); 162 159 ··· 804 807 { 805 808 struct devfreq *devfreq; 806 809 struct devfreq_governor *governor; 807 - unsigned long min_freq, max_freq; 808 810 int err = 0; 809 811 810 812 if (!dev || !profile || !governor_name) { ··· 831 835 mutex_lock(&devfreq->lock); 832 836 devfreq->dev.parent = dev; 833 837 devfreq->dev.class = devfreq_class; 838 + devfreq->dev.groups = profile->dev_groups; 834 839 devfreq->dev.release = devfreq_dev_release; 835 840 INIT_LIST_HEAD(&devfreq->node); 836 841 devfreq->profile = profile; ··· 871 874 err = -EINVAL; 872 875 goto err_dev; 873 876 } 874 - 875 - devfreq_get_freq_range(devfreq, &min_freq, &max_freq); 876 877 877 878 devfreq->suspend_freq = dev_pm_opp_get_suspend_opp_freq(dev); 878 879 devfreq->opp_table = dev_pm_opp_get_opp_table(dev); ··· 1377 1382 int ret; 1378 1383 struct device *dev = devfreq->dev.parent; 1379 1384 1385 + if (!devfreq->governor) 1386 + continue; 1387 + 1380 1388 if (!strncmp(devfreq->governor->name, governor->name, 1381 1389 DEVFREQ_NAME_LEN)) { 1382 - /* we should have a devfreq governor! */ 1383 - if (!devfreq->governor) { 1384 - dev_warn(dev, "%s: Governor %s NOT present\n", 1385 - __func__, governor->name); 1386 - continue; 1387 - /* Fall through */ 1388 - } 1389 1390 ret = devfreq->governor->event_handler(devfreq, 1390 1391 DEVFREQ_GOV_STOP, NULL); 1391 1392 if (ret) { ··· 1734 1743 for (i = 0; i < max_state; i++) { 1735 1744 if (len >= PAGE_SIZE - 1) 1736 1745 break; 1737 - if (df->freq_table[2] == df->previous_freq) 1746 + if (df->freq_table[i] == df->previous_freq) 1738 1747 len += sysfs_emit_at(buf, len, "*"); 1739 1748 else 1740 1749 len += sysfs_emit_at(buf, len, " ");
+5 -1
drivers/devfreq/governor_userspace.c
··· 9 9 #include <linux/slab.h> 10 10 #include <linux/device.h> 11 11 #include <linux/devfreq.h> 12 + #include <linux/kstrtox.h> 12 13 #include <linux/pm.h> 13 14 #include <linux/mutex.h> 14 15 #include <linux/module.h> ··· 40 39 unsigned long wanted; 41 40 int err = 0; 42 41 42 + err = kstrtoul(buf, 0, &wanted); 43 + if (err) 44 + return err; 45 + 43 46 mutex_lock(&devfreq->lock); 44 47 data = devfreq->governor_data; 45 48 46 - sscanf(buf, "%lu", &wanted); 47 49 data->user_frequency = wanted; 48 50 data->valid = true; 49 51 err = update_devfreq(devfreq);
+658
drivers/devfreq/hisi_uncore_freq.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * HiSilicon uncore frequency scaling driver 4 + * 5 + * Copyright (c) 2025 HiSilicon Co., Ltd 6 + */ 7 + 8 + #include <linux/acpi.h> 9 + #include <linux/bits.h> 10 + #include <linux/cleanup.h> 11 + #include <linux/devfreq.h> 12 + #include <linux/device.h> 13 + #include <linux/dev_printk.h> 14 + #include <linux/errno.h> 15 + #include <linux/iopoll.h> 16 + #include <linux/kernel.h> 17 + #include <linux/ktime.h> 18 + #include <linux/mailbox_client.h> 19 + #include <linux/module.h> 20 + #include <linux/mod_devicetable.h> 21 + #include <linux/mutex.h> 22 + #include <linux/platform_device.h> 23 + #include <linux/pm_opp.h> 24 + #include <linux/property.h> 25 + #include <linux/topology.h> 26 + #include <linux/units.h> 27 + #include <acpi/pcc.h> 28 + 29 + #include "governor.h" 30 + 31 + struct hisi_uncore_pcc_data { 32 + u16 status; 33 + u16 resv; 34 + u32 data; 35 + }; 36 + 37 + struct hisi_uncore_pcc_shmem { 38 + struct acpi_pcct_shared_memory head; 39 + struct hisi_uncore_pcc_data pcc_data; 40 + }; 41 + 42 + enum hisi_uncore_pcc_cmd_type { 43 + HUCF_PCC_CMD_GET_CAP = 0, 44 + HUCF_PCC_CMD_GET_FREQ, 45 + HUCF_PCC_CMD_SET_FREQ, 46 + HUCF_PCC_CMD_GET_MODE, 47 + HUCF_PCC_CMD_SET_MODE, 48 + HUCF_PCC_CMD_GET_PLAT_FREQ_NUM, 49 + HUCF_PCC_CMD_GET_PLAT_FREQ_BY_IDX, 50 + HUCF_PCC_CMD_MAX = 256 51 + }; 52 + 53 + static int hisi_platform_gov_usage; 54 + static DEFINE_MUTEX(hisi_platform_gov_usage_lock); 55 + 56 + enum hisi_uncore_freq_mode { 57 + HUCF_MODE_PLATFORM = 0, 58 + HUCF_MODE_OS, 59 + HUCF_MODE_MAX 60 + }; 61 + 62 + #define HUCF_CAP_PLATFORM_CTRL BIT(0) 63 + 64 + /** 65 + * struct hisi_uncore_freq - hisi uncore frequency scaling device data 66 + * @dev: device of this frequency scaling driver 67 + * @cl: mailbox client object 68 + * @pchan: PCC mailbox channel 69 + * @chan_id: PCC channel ID 70 + * @last_cmd_cmpl_time: timestamp of the last completed PCC command 71 + * @pcc_lock: PCC channel lock 72 + * @devfreq: devfreq data of this hisi_uncore_freq device 73 + * @related_cpus: CPUs whose performance is majorly affected by this 74 + * uncore frequency domain 75 + * @cap: capability flag 76 + */ 77 + struct hisi_uncore_freq { 78 + struct device *dev; 79 + struct mbox_client cl; 80 + struct pcc_mbox_chan *pchan; 81 + int chan_id; 82 + ktime_t last_cmd_cmpl_time; 83 + struct mutex pcc_lock; 84 + struct devfreq *devfreq; 85 + struct cpumask related_cpus; 86 + u32 cap; 87 + }; 88 + 89 + /* PCC channel timeout = PCC nominal latency * NUM */ 90 + #define HUCF_PCC_POLL_TIMEOUT_NUM 1000 91 + #define HUCF_PCC_POLL_INTERVAL_US 5 92 + 93 + /* Default polling interval in ms for devfreq governors*/ 94 + #define HUCF_DEFAULT_POLLING_MS 100 95 + 96 + static void hisi_uncore_free_pcc_chan(struct hisi_uncore_freq *uncore) 97 + { 98 + guard(mutex)(&uncore->pcc_lock); 99 + pcc_mbox_free_channel(uncore->pchan); 100 + uncore->pchan = NULL; 101 + } 102 + 103 + static void devm_hisi_uncore_free_pcc_chan(void *data) 104 + { 105 + hisi_uncore_free_pcc_chan(data); 106 + } 107 + 108 + static int hisi_uncore_request_pcc_chan(struct hisi_uncore_freq *uncore) 109 + { 110 + struct device *dev = uncore->dev; 111 + struct pcc_mbox_chan *pcc_chan; 112 + 113 + uncore->cl = (struct mbox_client) { 114 + .dev = dev, 115 + .tx_block = false, 116 + .knows_txdone = true, 117 + }; 118 + 119 + pcc_chan = pcc_mbox_request_channel(&uncore->cl, uncore->chan_id); 120 + if (IS_ERR(pcc_chan)) 121 + return dev_err_probe(dev, PTR_ERR(pcc_chan), 122 + "Failed to request PCC channel %u\n", uncore->chan_id); 123 + 124 + if (!pcc_chan->shmem_base_addr) { 125 + pcc_mbox_free_channel(pcc_chan); 126 + return dev_err_probe(dev, -EINVAL, 127 + "Invalid PCC shared memory address\n"); 128 + } 129 + 130 + if (pcc_chan->shmem_size < sizeof(struct hisi_uncore_pcc_shmem)) { 131 + pcc_mbox_free_channel(pcc_chan); 132 + return dev_err_probe(dev, -EINVAL, 133 + "Invalid PCC shared memory size (%lluB)\n", 134 + pcc_chan->shmem_size); 135 + } 136 + 137 + uncore->pchan = pcc_chan; 138 + 139 + return devm_add_action_or_reset(uncore->dev, 140 + devm_hisi_uncore_free_pcc_chan, uncore); 141 + } 142 + 143 + static acpi_status hisi_uncore_pcc_reg_scan(struct acpi_resource *res, 144 + void *ctx) 145 + { 146 + struct acpi_resource_generic_register *reg; 147 + struct hisi_uncore_freq *uncore; 148 + 149 + if (!res || res->type != ACPI_RESOURCE_TYPE_GENERIC_REGISTER) 150 + return AE_OK; 151 + 152 + reg = &res->data.generic_reg; 153 + if (reg->space_id != ACPI_ADR_SPACE_PLATFORM_COMM) 154 + return AE_OK; 155 + 156 + if (!ctx) 157 + return AE_ERROR; 158 + 159 + uncore = ctx; 160 + /* PCC subspace ID stored in Access Size */ 161 + uncore->chan_id = reg->access_size; 162 + 163 + return AE_CTRL_TERMINATE; 164 + } 165 + 166 + static int hisi_uncore_init_pcc_chan(struct hisi_uncore_freq *uncore) 167 + { 168 + acpi_handle handle = ACPI_HANDLE(uncore->dev); 169 + acpi_status status; 170 + int rc; 171 + 172 + uncore->chan_id = -1; 173 + status = acpi_walk_resources(handle, METHOD_NAME__CRS, 174 + hisi_uncore_pcc_reg_scan, uncore); 175 + if (ACPI_FAILURE(status) || uncore->chan_id < 0) 176 + return dev_err_probe(uncore->dev, -ENODEV, 177 + "Failed to get a PCC channel\n"); 178 + 179 + 180 + rc = devm_mutex_init(uncore->dev, &uncore->pcc_lock); 181 + if (rc) 182 + return rc; 183 + 184 + return hisi_uncore_request_pcc_chan(uncore); 185 + } 186 + 187 + static int hisi_uncore_cmd_send(struct hisi_uncore_freq *uncore, 188 + u8 cmd, u32 *data) 189 + { 190 + struct hisi_uncore_pcc_shmem __iomem *addr; 191 + struct hisi_uncore_pcc_shmem shmem; 192 + struct pcc_mbox_chan *pchan; 193 + unsigned int mrtt; 194 + s64 time_delta; 195 + u16 status; 196 + int rc; 197 + 198 + guard(mutex)(&uncore->pcc_lock); 199 + 200 + pchan = uncore->pchan; 201 + if (!pchan) 202 + return -ENODEV; 203 + 204 + addr = (struct hisi_uncore_pcc_shmem __iomem *)pchan->shmem; 205 + if (!addr) 206 + return -EINVAL; 207 + 208 + /* Handle the Minimum Request Turnaround Time (MRTT) */ 209 + mrtt = pchan->min_turnaround_time; 210 + time_delta = ktime_us_delta(ktime_get(), uncore->last_cmd_cmpl_time); 211 + if (mrtt > time_delta) 212 + udelay(mrtt - time_delta); 213 + 214 + /* Copy data */ 215 + shmem.head = (struct acpi_pcct_shared_memory) { 216 + .signature = PCC_SIGNATURE | uncore->chan_id, 217 + .command = cmd, 218 + }; 219 + shmem.pcc_data.data = *data; 220 + memcpy_toio(addr, &shmem, sizeof(shmem)); 221 + 222 + /* Ring doorbell */ 223 + rc = mbox_send_message(pchan->mchan, &cmd); 224 + if (rc < 0) { 225 + dev_err(uncore->dev, "Failed to send mbox message, %d\n", rc); 226 + return rc; 227 + } 228 + 229 + /* Wait status */ 230 + rc = readw_poll_timeout(&addr->head.status, status, 231 + status & (PCC_STATUS_CMD_COMPLETE | 232 + PCC_STATUS_ERROR), 233 + HUCF_PCC_POLL_INTERVAL_US, 234 + pchan->latency * HUCF_PCC_POLL_TIMEOUT_NUM); 235 + if (rc) { 236 + dev_err(uncore->dev, "PCC channel response timeout, cmd=%u\n", cmd); 237 + } else if (status & PCC_STATUS_ERROR) { 238 + dev_err(uncore->dev, "PCC cmd error, cmd=%u\n", cmd); 239 + rc = -EIO; 240 + } 241 + 242 + uncore->last_cmd_cmpl_time = ktime_get(); 243 + 244 + /* Copy data back */ 245 + memcpy_fromio(data, &addr->pcc_data.data, sizeof(*data)); 246 + 247 + /* Clear mailbox active req */ 248 + mbox_client_txdone(pchan->mchan, rc); 249 + 250 + return rc; 251 + } 252 + 253 + static int hisi_uncore_target(struct device *dev, unsigned long *freq, 254 + u32 flags) 255 + { 256 + struct hisi_uncore_freq *uncore = dev_get_drvdata(dev); 257 + struct dev_pm_opp *opp; 258 + u32 data; 259 + 260 + if (WARN_ON(!uncore || !uncore->pchan)) 261 + return -ENODEV; 262 + 263 + opp = devfreq_recommended_opp(dev, freq, flags); 264 + if (IS_ERR(opp)) { 265 + dev_err(dev, "Failed to get opp for freq %lu hz\n", *freq); 266 + return PTR_ERR(opp); 267 + } 268 + dev_pm_opp_put(opp); 269 + 270 + data = (u32)(dev_pm_opp_get_freq(opp) / HZ_PER_MHZ); 271 + 272 + return hisi_uncore_cmd_send(uncore, HUCF_PCC_CMD_SET_FREQ, &data); 273 + } 274 + 275 + static int hisi_uncore_get_dev_status(struct device *dev, 276 + struct devfreq_dev_status *stat) 277 + { 278 + /* Not used */ 279 + return 0; 280 + } 281 + 282 + static int hisi_uncore_get_cur_freq(struct device *dev, unsigned long *freq) 283 + { 284 + struct hisi_uncore_freq *uncore = dev_get_drvdata(dev); 285 + u32 data = 0; 286 + int rc; 287 + 288 + if (WARN_ON(!uncore || !uncore->pchan)) 289 + return -ENODEV; 290 + 291 + rc = hisi_uncore_cmd_send(uncore, HUCF_PCC_CMD_GET_FREQ, &data); 292 + 293 + /* 294 + * Upon a failure, 'data' remains 0 and 'freq' is set to 0 rather than a 295 + * random value. devfreq shouldn't use 'freq' in that case though. 296 + */ 297 + *freq = data * HZ_PER_MHZ; 298 + 299 + return rc; 300 + } 301 + 302 + static void devm_hisi_uncore_remove_opp(void *data) 303 + { 304 + struct hisi_uncore_freq *uncore = data; 305 + 306 + dev_pm_opp_remove_all_dynamic(uncore->dev); 307 + } 308 + 309 + static int hisi_uncore_init_opp(struct hisi_uncore_freq *uncore) 310 + { 311 + struct device *dev = uncore->dev; 312 + unsigned long freq_mhz; 313 + u32 num, index; 314 + u32 data = 0; 315 + int rc; 316 + 317 + rc = hisi_uncore_cmd_send(uncore, HUCF_PCC_CMD_GET_PLAT_FREQ_NUM, 318 + &data); 319 + if (rc) 320 + return dev_err_probe(dev, rc, "Failed to get plat freq num\n"); 321 + 322 + num = data; 323 + 324 + for (index = 0; index < num; index++) { 325 + data = index; 326 + rc = hisi_uncore_cmd_send(uncore, 327 + HUCF_PCC_CMD_GET_PLAT_FREQ_BY_IDX, 328 + &data); 329 + if (rc) { 330 + dev_pm_opp_remove_all_dynamic(dev); 331 + return dev_err_probe(dev, rc, 332 + "Failed to get plat freq at index %u\n", index); 333 + } 334 + freq_mhz = data; 335 + 336 + /* Don't care OPP voltage, take 1V as default */ 337 + rc = dev_pm_opp_add(dev, freq_mhz * HZ_PER_MHZ, 1000000); 338 + if (rc) { 339 + dev_pm_opp_remove_all_dynamic(dev); 340 + return dev_err_probe(dev, rc, 341 + "Add OPP %lu failed\n", freq_mhz); 342 + } 343 + } 344 + 345 + return devm_add_action_or_reset(dev, devm_hisi_uncore_remove_opp, 346 + uncore); 347 + } 348 + 349 + static int hisi_platform_gov_func(struct devfreq *df, unsigned long *freq) 350 + { 351 + /* 352 + * Platform-controlled mode doesn't care the frequency issued from 353 + * devfreq, so just pick the max freq. 354 + */ 355 + *freq = DEVFREQ_MAX_FREQ; 356 + 357 + return 0; 358 + } 359 + 360 + static int hisi_platform_gov_handler(struct devfreq *df, unsigned int event, 361 + void *val) 362 + { 363 + struct hisi_uncore_freq *uncore = dev_get_drvdata(df->dev.parent); 364 + int rc = 0; 365 + u32 data; 366 + 367 + if (WARN_ON(!uncore || !uncore->pchan)) 368 + return -ENODEV; 369 + 370 + switch (event) { 371 + case DEVFREQ_GOV_START: 372 + data = HUCF_MODE_PLATFORM; 373 + rc = hisi_uncore_cmd_send(uncore, HUCF_PCC_CMD_SET_MODE, &data); 374 + if (rc) 375 + dev_err(uncore->dev, "Failed to set platform mode (%d)\n", rc); 376 + break; 377 + case DEVFREQ_GOV_STOP: 378 + data = HUCF_MODE_OS; 379 + rc = hisi_uncore_cmd_send(uncore, HUCF_PCC_CMD_SET_MODE, &data); 380 + if (rc) 381 + dev_err(uncore->dev, "Failed to set os mode (%d)\n", rc); 382 + break; 383 + default: 384 + break; 385 + } 386 + 387 + return rc; 388 + } 389 + 390 + /* 391 + * In the platform-controlled mode, the platform decides the uncore frequency 392 + * and ignores the frequency issued from the driver. 393 + * Thus, create a pseudo 'hisi_platform' governor that stops devfreq monitor 394 + * from working so as to save meaningless overhead. 395 + */ 396 + static struct devfreq_governor hisi_platform_governor = { 397 + .name = "hisi_platform", 398 + /* 399 + * Set interrupt_driven to skip the devfreq monitor mechanism, though 400 + * this governor is not interrupt-driven. 401 + */ 402 + .flags = DEVFREQ_GOV_FLAG_IRQ_DRIVEN, 403 + .get_target_freq = hisi_platform_gov_func, 404 + .event_handler = hisi_platform_gov_handler, 405 + }; 406 + 407 + static void hisi_uncore_remove_platform_gov(struct hisi_uncore_freq *uncore) 408 + { 409 + u32 data = HUCF_MODE_PLATFORM; 410 + int rc; 411 + 412 + if (!(uncore->cap & HUCF_CAP_PLATFORM_CTRL)) 413 + return; 414 + 415 + guard(mutex)(&hisi_platform_gov_usage_lock); 416 + 417 + if (--hisi_platform_gov_usage == 0) { 418 + rc = devfreq_remove_governor(&hisi_platform_governor); 419 + if (rc) 420 + dev_err(uncore->dev, "Failed to remove hisi_platform gov (%d)\n", rc); 421 + } 422 + 423 + /* 424 + * Set to the platform-controlled mode on exit if supported, so as to 425 + * have a certain behaviour when the driver is detached. 426 + */ 427 + rc = hisi_uncore_cmd_send(uncore, HUCF_PCC_CMD_SET_MODE, &data); 428 + if (rc) 429 + dev_err(uncore->dev, "Failed to set platform mode on exit (%d)\n", rc); 430 + } 431 + 432 + static void devm_hisi_uncore_remove_platform_gov(void *data) 433 + { 434 + hisi_uncore_remove_platform_gov(data); 435 + } 436 + 437 + static int hisi_uncore_add_platform_gov(struct hisi_uncore_freq *uncore) 438 + { 439 + if (!(uncore->cap & HUCF_CAP_PLATFORM_CTRL)) 440 + return 0; 441 + 442 + guard(mutex)(&hisi_platform_gov_usage_lock); 443 + 444 + if (hisi_platform_gov_usage == 0) { 445 + int rc = devfreq_add_governor(&hisi_platform_governor); 446 + if (rc) 447 + return rc; 448 + } 449 + hisi_platform_gov_usage++; 450 + 451 + return devm_add_action_or_reset(uncore->dev, 452 + devm_hisi_uncore_remove_platform_gov, 453 + uncore); 454 + } 455 + 456 + /* 457 + * Returns: 458 + * 0 if success, uncore->related_cpus is set. 459 + * -EINVAL if property not found, or property found but without elements in it, 460 + * or invalid arguments received in any of the subroutine. 461 + * Other error codes if it goes wrong. 462 + */ 463 + static int hisi_uncore_mark_related_cpus(struct hisi_uncore_freq *uncore, 464 + char *property, int (*get_topo_id)(int cpu), 465 + const struct cpumask *(*get_cpumask)(int cpu)) 466 + { 467 + unsigned int i, cpu; 468 + size_t len; 469 + int rc; 470 + 471 + rc = device_property_count_u32(uncore->dev, property); 472 + if (rc < 0) 473 + return rc; 474 + if (rc == 0) 475 + return -EINVAL; 476 + 477 + len = rc; 478 + u32 *num __free(kfree) = kcalloc(len, sizeof(*num), GFP_KERNEL); 479 + if (!num) 480 + return -ENOMEM; 481 + 482 + rc = device_property_read_u32_array(uncore->dev, property, num, len); 483 + if (rc) 484 + return rc; 485 + 486 + for (i = 0; i < len; i++) { 487 + for_each_possible_cpu(cpu) { 488 + if (get_topo_id(cpu) != num[i]) 489 + continue; 490 + 491 + cpumask_or(&uncore->related_cpus, 492 + &uncore->related_cpus, get_cpumask(cpu)); 493 + break; 494 + } 495 + } 496 + 497 + return 0; 498 + } 499 + 500 + static int get_package_id(int cpu) 501 + { 502 + return topology_physical_package_id(cpu); 503 + } 504 + 505 + static const struct cpumask *get_package_cpumask(int cpu) 506 + { 507 + return topology_core_cpumask(cpu); 508 + } 509 + 510 + static int get_cluster_id(int cpu) 511 + { 512 + return topology_cluster_id(cpu); 513 + } 514 + 515 + static const struct cpumask *get_cluster_cpumask(int cpu) 516 + { 517 + return topology_cluster_cpumask(cpu); 518 + } 519 + 520 + static int hisi_uncore_mark_related_cpus_wrap(struct hisi_uncore_freq *uncore) 521 + { 522 + int rc; 523 + 524 + cpumask_clear(&uncore->related_cpus); 525 + 526 + rc = hisi_uncore_mark_related_cpus(uncore, "related-package", 527 + get_package_id, 528 + get_package_cpumask); 529 + /* Success, or firmware probably broken */ 530 + if (!rc || rc != -EINVAL) 531 + return rc; 532 + 533 + /* Try another property name if rc == -EINVAL */ 534 + return hisi_uncore_mark_related_cpus(uncore, "related-cluster", 535 + get_cluster_id, 536 + get_cluster_cpumask); 537 + } 538 + 539 + static ssize_t related_cpus_show(struct device *dev, 540 + struct device_attribute *attr, char *buf) 541 + { 542 + struct hisi_uncore_freq *uncore = dev_get_drvdata(dev->parent); 543 + 544 + return cpumap_print_to_pagebuf(true, buf, &uncore->related_cpus); 545 + } 546 + 547 + static DEVICE_ATTR_RO(related_cpus); 548 + 549 + static struct attribute *hisi_uncore_freq_attrs[] = { 550 + &dev_attr_related_cpus.attr, 551 + NULL 552 + }; 553 + ATTRIBUTE_GROUPS(hisi_uncore_freq); 554 + 555 + static int hisi_uncore_devfreq_register(struct hisi_uncore_freq *uncore) 556 + { 557 + struct devfreq_dev_profile *profile; 558 + struct device *dev = uncore->dev; 559 + unsigned long freq; 560 + u32 data; 561 + int rc; 562 + 563 + rc = hisi_uncore_get_cur_freq(dev, &freq); 564 + if (rc) 565 + return dev_err_probe(dev, rc, "Failed to get plat init freq\n"); 566 + 567 + profile = devm_kzalloc(dev, sizeof(*profile), GFP_KERNEL); 568 + if (!profile) 569 + return -ENOMEM; 570 + 571 + *profile = (struct devfreq_dev_profile) { 572 + .initial_freq = freq, 573 + .polling_ms = HUCF_DEFAULT_POLLING_MS, 574 + .timer = DEVFREQ_TIMER_DELAYED, 575 + .target = hisi_uncore_target, 576 + .get_dev_status = hisi_uncore_get_dev_status, 577 + .get_cur_freq = hisi_uncore_get_cur_freq, 578 + .dev_groups = hisi_uncore_freq_groups, 579 + }; 580 + 581 + rc = hisi_uncore_cmd_send(uncore, HUCF_PCC_CMD_GET_MODE, &data); 582 + if (rc) 583 + return dev_err_probe(dev, rc, "Failed to get operate mode\n"); 584 + 585 + if (data == HUCF_MODE_PLATFORM) 586 + uncore->devfreq = devm_devfreq_add_device(dev, profile, 587 + hisi_platform_governor.name, NULL); 588 + else 589 + uncore->devfreq = devm_devfreq_add_device(dev, profile, 590 + DEVFREQ_GOV_PERFORMANCE, NULL); 591 + if (IS_ERR(uncore->devfreq)) 592 + return dev_err_probe(dev, PTR_ERR(uncore->devfreq), 593 + "Failed to add devfreq device\n"); 594 + 595 + return 0; 596 + } 597 + 598 + static int hisi_uncore_freq_probe(struct platform_device *pdev) 599 + { 600 + struct hisi_uncore_freq *uncore; 601 + struct device *dev = &pdev->dev; 602 + u32 cap; 603 + int rc; 604 + 605 + uncore = devm_kzalloc(dev, sizeof(*uncore), GFP_KERNEL); 606 + if (!uncore) 607 + return -ENOMEM; 608 + 609 + uncore->dev = dev; 610 + platform_set_drvdata(pdev, uncore); 611 + 612 + rc = hisi_uncore_init_pcc_chan(uncore); 613 + if (rc) 614 + return dev_err_probe(dev, rc, "Failed to init PCC channel\n"); 615 + 616 + rc = hisi_uncore_init_opp(uncore); 617 + if (rc) 618 + return dev_err_probe(dev, rc, "Failed to init OPP\n"); 619 + 620 + rc = hisi_uncore_cmd_send(uncore, HUCF_PCC_CMD_GET_CAP, &cap); 621 + if (rc) 622 + return dev_err_probe(dev, rc, "Failed to get capability\n"); 623 + 624 + uncore->cap = cap; 625 + 626 + rc = hisi_uncore_add_platform_gov(uncore); 627 + if (rc) 628 + return dev_err_probe(dev, rc, "Failed to add hisi_platform governor\n"); 629 + 630 + rc = hisi_uncore_mark_related_cpus_wrap(uncore); 631 + if (rc) 632 + return dev_err_probe(dev, rc, "Failed to mark related cpus\n"); 633 + 634 + rc = hisi_uncore_devfreq_register(uncore); 635 + if (rc) 636 + return dev_err_probe(dev, rc, "Failed to register devfreq\n"); 637 + 638 + return 0; 639 + } 640 + 641 + static const struct acpi_device_id hisi_uncore_freq_acpi_match[] = { 642 + { "HISI04F1", }, 643 + { } 644 + }; 645 + MODULE_DEVICE_TABLE(acpi, hisi_uncore_freq_acpi_match); 646 + 647 + static struct platform_driver hisi_uncore_freq_drv = { 648 + .probe = hisi_uncore_freq_probe, 649 + .driver = { 650 + .name = "hisi_uncore_freq", 651 + .acpi_match_table = hisi_uncore_freq_acpi_match, 652 + }, 653 + }; 654 + module_platform_driver(hisi_uncore_freq_drv); 655 + 656 + MODULE_DESCRIPTION("HiSilicon uncore frequency scaling driver"); 657 + MODULE_AUTHOR("Jie Zhan <zhanjie9@hisilicon.com>"); 658 + MODULE_LICENSE("GPL");
+9 -29
drivers/devfreq/sun8i-a33-mbus.c
··· 360 360 if (IS_ERR(priv->reg_mbus)) 361 361 return PTR_ERR(priv->reg_mbus); 362 362 363 - priv->clk_bus = devm_clk_get(dev, "bus"); 363 + priv->clk_bus = devm_clk_get_enabled(dev, "bus"); 364 364 if (IS_ERR(priv->clk_bus)) 365 365 return dev_err_probe(dev, PTR_ERR(priv->clk_bus), 366 366 "failed to get bus clock\n"); ··· 375 375 return dev_err_probe(dev, PTR_ERR(priv->clk_mbus), 376 376 "failed to get mbus clock\n"); 377 377 378 - ret = clk_prepare_enable(priv->clk_bus); 379 - if (ret) 380 - return dev_err_probe(dev, ret, 381 - "failed to enable bus clock\n"); 382 - 383 378 /* Lock the DRAM clock rate to keep priv->nominal_bw in sync. */ 384 - ret = clk_rate_exclusive_get(priv->clk_dram); 385 - if (ret) { 386 - err = "failed to lock dram clock rate\n"; 387 - goto err_disable_bus; 388 - } 379 + ret = devm_clk_rate_exclusive_get(dev, priv->clk_dram); 380 + if (ret) 381 + return dev_err_probe(dev, ret, "failed to lock dram clock rate\n"); 389 382 390 383 /* Lock the MBUS clock rate to keep MBUS_TMR_PERIOD in sync. */ 391 - ret = clk_rate_exclusive_get(priv->clk_mbus); 392 - if (ret) { 393 - err = "failed to lock mbus clock rate\n"; 394 - goto err_unlock_dram; 395 - } 384 + ret = devm_clk_rate_exclusive_get(dev, priv->clk_mbus); 385 + if (ret) 386 + return dev_err_probe(dev, ret, "failed to lock mbus clock rate\n"); 396 387 397 388 priv->gov_data.upthreshold = 10; 398 389 priv->gov_data.downdifferential = 5; ··· 396 405 priv->profile.max_state = max_state; 397 406 398 407 ret = devm_pm_opp_set_clkname(dev, "dram"); 399 - if (ret) { 400 - err = "failed to add OPP table\n"; 401 - goto err_unlock_mbus; 402 - } 408 + if (ret) 409 + return dev_err_probe(dev, ret, "failed to add OPP table\n"); 403 410 404 411 base_freq = clk_get_rate(clk_get_parent(priv->clk_dram)); 405 412 for (i = 0; i < max_state; ++i) { ··· 437 448 438 449 err_remove_opps: 439 450 dev_pm_opp_remove_all_dynamic(dev); 440 - err_unlock_mbus: 441 - clk_rate_exclusive_put(priv->clk_mbus); 442 - err_unlock_dram: 443 - clk_rate_exclusive_put(priv->clk_dram); 444 - err_disable_bus: 445 - clk_disable_unprepare(priv->clk_bus); 446 451 447 452 return dev_err_probe(dev, ret, err); 448 453 } ··· 455 472 dev_warn(dev, "failed to restore DRAM frequency: %d\n", ret); 456 473 457 474 dev_pm_opp_remove_all_dynamic(dev); 458 - clk_rate_exclusive_put(priv->clk_mbus); 459 - clk_rate_exclusive_put(priv->clk_dram); 460 - clk_disable_unprepare(priv->clk_bus); 461 475 } 462 476 463 477 static const struct sun8i_a33_mbus_variant sun50i_a64_mbus = {
+4
include/linux/devfreq.h
··· 103 103 * 104 104 * @is_cooling_device: A self-explanatory boolean giving the device a 105 105 * cooling effect property. 106 + * @dev_groups: Optional device-specific sysfs attribute groups that to 107 + * be attached to the devfreq device. 106 108 */ 107 109 struct devfreq_dev_profile { 108 110 unsigned long initial_freq; ··· 121 119 unsigned int max_state; 122 120 123 121 bool is_cooling_device; 122 + 123 + const struct attribute_group **dev_groups; 124 124 }; 125 125 126 126 /**
+4 -4
rust/kernel/opp.rs
··· 514 514 dev: *mut bindings::device, 515 515 opp_table: *mut bindings::opp_table, 516 516 opp: *mut bindings::dev_pm_opp, 517 - _data: *mut kernel::ffi::c_void, 517 + _data: *mut c_void, 518 518 scaling_down: bool, 519 - ) -> kernel::ffi::c_int { 519 + ) -> c_int { 520 520 from_result(|| { 521 521 // SAFETY: 'dev' is guaranteed by the C code to be valid. 522 522 let dev = unsafe { Device::get_device(dev) }; ··· 540 540 old_opp: *mut bindings::dev_pm_opp, 541 541 new_opp: *mut bindings::dev_pm_opp, 542 542 regulators: *mut *mut bindings::regulator, 543 - count: kernel::ffi::c_uint, 544 - ) -> kernel::ffi::c_int { 543 + count: c_uint, 544 + ) -> c_int { 545 545 from_result(|| { 546 546 // SAFETY: 'dev' is guaranteed by the C code to be valid. 547 547 let dev = unsafe { Device::get_device(dev) };