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-em', 'pm-opp' and 'pm-devfreq'

Merge energy model management, OPP (operating performance points) and
devfreq updates for 6.18-rc1:

- Prevent CPU capacity updates after registering a perf domain from
failing on a first CPU that is not present (Christian Loehle)

- Add support for the cases in which frequency alone is not sufficient
to uniquely identify an OPP (Krishna Chaitanya Chundru)

- Use to_result() for OPP error handling in Rust (Onur Özkan)

- Add support for LPDDR5 on Rockhip RK3588 SoC to rockchip-dfi devfreq
driver (Nicolas Frattaroli)

- Fix an issue where DDR cycle counts on RK3588/RK3528 with LPDDR4(X)
are reported as half by adding a cycle multiplier to the DFI driver
in rockchip-dfi devfreq-event driver (Nicolas Frattaroli)

- Fix missing error pointer dereference check of regulator instance in
the mtk-cci devfreq driver probe and remove a redundant condition from
an if () statement in that driver (Dan Carpenter, Liao Yuanhong)

* pm-em:
PM: EM: Fix late boot with holes in CPU topology

* pm-opp:
OPP: Add support to find OPP for a set of keys
rust: opp: use to_result for error handling

* pm-devfreq:
PM / devfreq: rockchip-dfi: add support for LPDDR5
PM / devfreq: rockchip-dfi: double count on RK3588
PM / devfreq: mtk-cci: avoid redundant conditions
PM / devfreq: mtk-cci: Fix potential error pointer dereference in probe()

+222 -39
+72 -19
drivers/devfreq/event/rockchip-dfi.c
··· 34 34 35 35 /* DDRMON_CTRL */ 36 36 #define DDRMON_CTRL 0x04 37 + #define DDRMON_CTRL_LPDDR5 BIT(6) 37 38 #define DDRMON_CTRL_DDR4 BIT(5) 38 39 #define DDRMON_CTRL_LPDDR4 BIT(4) 39 40 #define DDRMON_CTRL_HARDWARE_EN BIT(3) 40 41 #define DDRMON_CTRL_LPDDR23 BIT(2) 41 42 #define DDRMON_CTRL_SOFTWARE_EN BIT(1) 42 43 #define DDRMON_CTRL_TIMER_CNT_EN BIT(0) 43 - #define DDRMON_CTRL_DDR_TYPE_MASK (DDRMON_CTRL_DDR4 | \ 44 + #define DDRMON_CTRL_DDR_TYPE_MASK (DDRMON_CTRL_LPDDR5 | \ 45 + DDRMON_CTRL_DDR4 | \ 44 46 DDRMON_CTRL_LPDDR4 | \ 45 47 DDRMON_CTRL_LPDDR23) 48 + #define DDRMON_CTRL_LP5_BANK_MODE_MASK GENMASK(8, 7) 46 49 47 50 #define DDRMON_CH0_WR_NUM 0x20 48 51 #define DDRMON_CH0_RD_NUM 0x24 ··· 119 116 int buswidth[DMC_MAX_CHANNELS]; 120 117 int ddrmon_stride; 121 118 bool ddrmon_ctrl_single; 119 + u32 lp5_bank_mode; 120 + bool lp5_ckr; /* true if in 4:1 command-to-data clock ratio mode */ 121 + unsigned int count_multiplier; /* number of data clocks per count */ 122 122 }; 123 + 124 + static int rockchip_dfi_ddrtype_to_ctrl(struct rockchip_dfi *dfi, u32 *ctrl, 125 + u32 *mask) 126 + { 127 + u32 ddrmon_ver; 128 + 129 + *mask = DDRMON_CTRL_DDR_TYPE_MASK; 130 + 131 + switch (dfi->ddr_type) { 132 + case ROCKCHIP_DDRTYPE_LPDDR2: 133 + case ROCKCHIP_DDRTYPE_LPDDR3: 134 + *ctrl = DDRMON_CTRL_LPDDR23; 135 + break; 136 + case ROCKCHIP_DDRTYPE_LPDDR4: 137 + case ROCKCHIP_DDRTYPE_LPDDR4X: 138 + *ctrl = DDRMON_CTRL_LPDDR4; 139 + break; 140 + case ROCKCHIP_DDRTYPE_LPDDR5: 141 + ddrmon_ver = readl_relaxed(dfi->regs); 142 + if (ddrmon_ver < 0x40) { 143 + *ctrl = DDRMON_CTRL_LPDDR5 | dfi->lp5_bank_mode; 144 + *mask |= DDRMON_CTRL_LP5_BANK_MODE_MASK; 145 + break; 146 + } 147 + 148 + /* 149 + * As it is unknown whether the unpleasant special case 150 + * behaviour used by the vendor kernel is needed for any 151 + * shipping hardware, ask users to report if they have 152 + * some of that hardware. 153 + */ 154 + dev_err(&dfi->edev->dev, 155 + "unsupported DDRMON version 0x%04X, please let linux-rockchip know!\n", 156 + ddrmon_ver); 157 + return -EOPNOTSUPP; 158 + default: 159 + dev_err(&dfi->edev->dev, "unsupported memory type 0x%X\n", 160 + dfi->ddr_type); 161 + return -EOPNOTSUPP; 162 + } 163 + 164 + return 0; 165 + } 123 166 124 167 static int rockchip_dfi_enable(struct rockchip_dfi *dfi) 125 168 { 126 169 void __iomem *dfi_regs = dfi->regs; 127 170 int i, ret = 0; 171 + u32 ctrl; 172 + u32 ctrl_mask; 128 173 129 174 mutex_lock(&dfi->mutex); 130 175 ··· 186 135 goto out; 187 136 } 188 137 138 + ret = rockchip_dfi_ddrtype_to_ctrl(dfi, &ctrl, &ctrl_mask); 139 + if (ret) 140 + goto out; 141 + 189 142 for (i = 0; i < dfi->max_channels; i++) { 190 - u32 ctrl = 0; 191 143 192 144 if (!(dfi->channel_mask & BIT(i))) 193 145 continue; ··· 200 146 DDRMON_CTRL_SOFTWARE_EN | DDRMON_CTRL_HARDWARE_EN), 201 147 dfi_regs + i * dfi->ddrmon_stride + DDRMON_CTRL); 202 148 203 - /* set ddr type to dfi */ 204 - switch (dfi->ddr_type) { 205 - case ROCKCHIP_DDRTYPE_LPDDR2: 206 - case ROCKCHIP_DDRTYPE_LPDDR3: 207 - ctrl = DDRMON_CTRL_LPDDR23; 208 - break; 209 - case ROCKCHIP_DDRTYPE_LPDDR4: 210 - case ROCKCHIP_DDRTYPE_LPDDR4X: 211 - ctrl = DDRMON_CTRL_LPDDR4; 212 - break; 213 - default: 214 - break; 215 - } 216 - 217 - writel_relaxed(HIWORD_UPDATE(ctrl, DDRMON_CTRL_DDR_TYPE_MASK), 149 + writel_relaxed(HIWORD_UPDATE(ctrl, ctrl_mask), 218 150 dfi_regs + i * dfi->ddrmon_stride + DDRMON_CTRL); 219 151 220 152 /* enable count, use software mode */ ··· 475 435 476 436 switch (event->attr.config) { 477 437 case PERF_EVENT_CYCLES: 478 - count = total.c[0].clock_cycles; 438 + count = total.c[0].clock_cycles * dfi->count_multiplier; 479 439 break; 480 440 case PERF_EVENT_READ_BYTES: 481 441 for (i = 0; i < dfi->max_channels; i++) ··· 691 651 break; 692 652 case ROCKCHIP_DDRTYPE_LPDDR4: 693 653 case ROCKCHIP_DDRTYPE_LPDDR4X: 654 + case ROCKCHIP_DDRTYPE_LPDDR5: 694 655 dfi->burst_len = 16; 695 656 break; 696 657 } 658 + 659 + if (!dfi->count_multiplier) 660 + dfi->count_multiplier = 1; 697 661 698 662 ret = perf_pmu_register(pmu, "rockchip_ddr", -1); 699 663 if (ret) ··· 770 726 static int rk3588_dfi_init(struct rockchip_dfi *dfi) 771 727 { 772 728 struct regmap *regmap_pmu = dfi->regmap_pmu; 773 - u32 reg2, reg3, reg4; 729 + u32 reg2, reg3, reg4, reg6; 774 730 775 731 regmap_read(regmap_pmu, RK3588_PMUGRF_OS_REG2, &reg2); 776 732 regmap_read(regmap_pmu, RK3588_PMUGRF_OS_REG3, &reg3); ··· 795 751 dfi->max_channels = 4; 796 752 797 753 dfi->ddrmon_stride = 0x4000; 754 + dfi->count_multiplier = 2; 755 + 756 + if (dfi->ddr_type == ROCKCHIP_DDRTYPE_LPDDR5) { 757 + regmap_read(regmap_pmu, RK3588_PMUGRF_OS_REG6, &reg6); 758 + dfi->lp5_bank_mode = FIELD_GET(RK3588_PMUGRF_OS_REG6_LP5_BANK_MODE, reg6) << 7; 759 + dfi->lp5_ckr = FIELD_GET(RK3588_PMUGRF_OS_REG6_LP5_CKR, reg6); 760 + if (dfi->lp5_ckr) 761 + dfi->count_multiplier *= 2; 762 + } 798 763 799 764 return 0; 800 765 };
+3 -2
drivers/devfreq/mtk-cci-devfreq.c
··· 86 86 soc_data->sram_max_volt); 87 87 return ret; 88 88 } 89 - } else if (pre_voltage > new_voltage) { 89 + } else { 90 90 voltage = max(new_voltage, 91 91 pre_vsram - soc_data->max_volt_shift); 92 92 ret = regulator_set_voltage(drv->proc_reg, voltage, ··· 386 386 out_free_resources: 387 387 if (regulator_is_enabled(drv->proc_reg)) 388 388 regulator_disable(drv->proc_reg); 389 - if (drv->sram_reg && regulator_is_enabled(drv->sram_reg)) 389 + if (!IS_ERR_OR_NULL(drv->sram_reg) && 390 + regulator_is_enabled(drv->sram_reg)) 390 391 regulator_disable(drv->sram_reg); 391 392 392 393 return ret;
+99
drivers/opp/core.c
··· 476 476 return opp->bandwidth[index].peak; 477 477 } 478 478 479 + static unsigned long _read_opp_key(struct dev_pm_opp *opp, int index, 480 + struct dev_pm_opp_key *key) 481 + { 482 + key->bw = opp->bandwidth ? opp->bandwidth[index].peak : 0; 483 + key->freq = opp->rates[index]; 484 + key->level = opp->level; 485 + 486 + return true; 487 + } 488 + 479 489 /* Generic comparison helpers */ 480 490 static bool _compare_exact(struct dev_pm_opp **opp, struct dev_pm_opp *temp_opp, 481 491 unsigned long opp_key, unsigned long key) ··· 519 509 return false; 520 510 } 521 511 512 + static bool _compare_opp_key_exact(struct dev_pm_opp **opp, 513 + struct dev_pm_opp *temp_opp, struct dev_pm_opp_key *opp_key, 514 + struct dev_pm_opp_key *key) 515 + { 516 + bool level_match = (key->level == OPP_LEVEL_UNSET || opp_key->level == key->level); 517 + bool freq_match = (key->freq == 0 || opp_key->freq == key->freq); 518 + bool bw_match = (key->bw == 0 || opp_key->bw == key->bw); 519 + 520 + if (freq_match && level_match && bw_match) { 521 + *opp = temp_opp; 522 + return true; 523 + } 524 + 525 + return false; 526 + } 527 + 522 528 /* Generic key finding helpers */ 523 529 static struct dev_pm_opp *_opp_table_find_key(struct opp_table *opp_table, 524 530 unsigned long *key, int index, bool available, ··· 562 536 if (!IS_ERR(opp)) { 563 537 *key = read(opp, index); 564 538 dev_pm_opp_get(opp); 539 + } 540 + 541 + return opp; 542 + } 543 + 544 + static struct dev_pm_opp *_opp_table_find_opp_key(struct opp_table *opp_table, 545 + struct dev_pm_opp_key *key, bool available, 546 + unsigned long (*read)(struct dev_pm_opp *opp, int index, 547 + struct dev_pm_opp_key *key), 548 + bool (*compare)(struct dev_pm_opp **opp, struct dev_pm_opp *temp_opp, 549 + struct dev_pm_opp_key *opp_key, struct dev_pm_opp_key *key), 550 + bool (*assert)(struct opp_table *opp_table, unsigned int index)) 551 + { 552 + struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE); 553 + struct dev_pm_opp_key temp_key; 554 + 555 + /* Assert that the requirement is met */ 556 + if (!assert(opp_table, 0)) 557 + return ERR_PTR(-EINVAL); 558 + 559 + guard(mutex)(&opp_table->lock); 560 + 561 + list_for_each_entry(temp_opp, &opp_table->opp_list, node) { 562 + if (temp_opp->available == available) { 563 + read(temp_opp, 0, &temp_key); 564 + if (compare(&opp, temp_opp, &temp_key, key)) { 565 + /* Increment the reference count of OPP */ 566 + dev_pm_opp_get(opp); 567 + break; 568 + } 569 + } 565 570 } 566 571 567 572 return opp; ··· 688 631 assert_single_clk); 689 632 } 690 633 EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_exact); 634 + 635 + /** 636 + * dev_pm_opp_find_key_exact() - Search for an OPP with exact key set 637 + * @dev: Device for which the OPP is being searched 638 + * @key: OPP key set to match 639 + * @available: true/false - match for available OPP 640 + * 641 + * Search for an exact match of the key set in the OPP table. 642 + * 643 + * Return: A matching opp on success, else ERR_PTR in case of error. 644 + * Possible error values: 645 + * EINVAL: for bad pointers 646 + * ERANGE: no match found for search 647 + * ENODEV: if device not found in list of registered devices 648 + * 649 + * Note: 'available' is a modifier for the search. If 'available' == true, 650 + * then the match is for exact matching key and is available in the stored 651 + * OPP table. If false, the match is for exact key which is not available. 652 + * 653 + * This provides a mechanism to enable an OPP which is not available currently 654 + * or the opposite as well. 655 + * 656 + * The callers are required to call dev_pm_opp_put() for the returned OPP after 657 + * use. 658 + */ 659 + struct dev_pm_opp *dev_pm_opp_find_key_exact(struct device *dev, 660 + struct dev_pm_opp_key *key, 661 + bool available) 662 + { 663 + struct opp_table *opp_table __free(put_opp_table) = _find_opp_table(dev); 664 + 665 + if (IS_ERR(opp_table)) { 666 + dev_err(dev, "%s: OPP table not found (%ld)\n", __func__, 667 + PTR_ERR(opp_table)); 668 + return ERR_CAST(opp_table); 669 + } 670 + 671 + return _opp_table_find_opp_key(opp_table, key, available, 672 + _read_opp_key, _compare_opp_key_exact, 673 + assert_single_clk); 674 + } 675 + EXPORT_SYMBOL_GPL(dev_pm_opp_find_key_exact); 691 676 692 677 /** 693 678 * dev_pm_opp_find_freq_exact_indexed() - Search for an exact freq for the
+30
include/linux/pm_opp.h
··· 98 98 unsigned long u_volt; 99 99 }; 100 100 101 + /** 102 + * struct dev_pm_opp_key - Key used to identify OPP entries 103 + * @freq: Frequency in Hz. Use 0 if frequency is not to be matched. 104 + * @level: Performance level associated with the OPP entry. 105 + * Use OPP_LEVEL_UNSET if level is not to be matched. 106 + * @bw: Bandwidth associated with the OPP entry. 107 + * Use 0 if bandwidth is not to be matched. 108 + * 109 + * This structure is used to uniquely identify an OPP entry based on 110 + * frequency, performance level, and bandwidth. Each field can be 111 + * selectively ignored during matching by setting it to its respective 112 + * NOP value. 113 + */ 114 + struct dev_pm_opp_key { 115 + unsigned long freq; 116 + unsigned int level; 117 + u32 bw; 118 + }; 119 + 101 120 #if defined(CONFIG_PM_OPP) 102 121 103 122 struct opp_table *dev_pm_opp_get_opp_table(struct device *dev); ··· 149 130 struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev, 150 131 unsigned long freq, 151 132 bool available); 133 + 134 + struct dev_pm_opp *dev_pm_opp_find_key_exact(struct device *dev, 135 + struct dev_pm_opp_key *key, 136 + bool available); 152 137 153 138 struct dev_pm_opp * 154 139 dev_pm_opp_find_freq_exact_indexed(struct device *dev, unsigned long freq, ··· 308 285 309 286 static inline struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev, 310 287 unsigned long freq, bool available) 288 + { 289 + return ERR_PTR(-EOPNOTSUPP); 290 + } 291 + 292 + static inline struct dev_pm_opp *dev_pm_opp_find_key_exact(struct device *dev, 293 + struct dev_pm_opp_key *key, 294 + bool available) 311 295 { 312 296 return ERR_PTR(-EOPNOTSUPP); 313 297 }
+6 -2
include/soc/rockchip/rk3588_grf.h
··· 12 12 #define RK3588_PMUGRF_OS_REG3_DRAMTYPE_INFO_V3 GENMASK(13, 12) 13 13 #define RK3588_PMUGRF_OS_REG3_SYSREG_VERSION GENMASK(31, 28) 14 14 15 - #define RK3588_PMUGRF_OS_REG4 0x210 16 - #define RK3588_PMUGRF_OS_REG5 0x214 15 + #define RK3588_PMUGRF_OS_REG4 0x210 16 + #define RK3588_PMUGRF_OS_REG5 0x214 17 + #define RK3588_PMUGRF_OS_REG6 0x218 18 + #define RK3588_PMUGRF_OS_REG6_LP5_BANK_MODE GENMASK(2, 1) 19 + /* Whether the LPDDR5 is in 2:1 (= 0) or 4:1 (= 1) CKR a.k.a. DQS mode */ 20 + #define RK3588_PMUGRF_OS_REG6_LP5_CKR BIT(0) 17 21 18 22 #endif /* __SOC_RK3588_GRF_H */
+1
include/soc/rockchip/rockchip_grf.h
··· 13 13 ROCKCHIP_DDRTYPE_LPDDR3 = 6, 14 14 ROCKCHIP_DDRTYPE_LPDDR4 = 7, 15 15 ROCKCHIP_DDRTYPE_LPDDR4X = 8, 16 + ROCKCHIP_DDRTYPE_LPDDR5 = 9, 16 17 }; 17 18 18 19 #endif /* __SOC_ROCKCHIP_GRF_H */
+6 -5
kernel/power/energy_model.c
··· 799 799 static void em_check_capacity_update(void) 800 800 { 801 801 cpumask_var_t cpu_done_mask; 802 - int cpu; 802 + int cpu, failed_cpus = 0; 803 803 804 804 if (!zalloc_cpumask_var(&cpu_done_mask, GFP_KERNEL)) { 805 805 pr_warn("no free memory\n"); ··· 817 817 818 818 policy = cpufreq_cpu_get(cpu); 819 819 if (!policy) { 820 - pr_debug("Accessing cpu%d policy failed\n", cpu); 821 - schedule_delayed_work(&em_update_work, 822 - msecs_to_jiffies(1000)); 823 - break; 820 + failed_cpus++; 821 + continue; 824 822 } 825 823 cpufreq_cpu_put(policy); 826 824 ··· 832 834 833 835 em_adjust_new_capacity(cpu, dev, pd); 834 836 } 837 + 838 + if (failed_cpus) 839 + schedule_delayed_work(&em_update_work, msecs_to_jiffies(1000)); 835 840 836 841 free_cpumask_var(cpu_done_mask); 837 842 }
+5 -11
rust/kernel/opp.rs
··· 12 12 clk::Hertz, 13 13 cpumask::{Cpumask, CpumaskVar}, 14 14 device::Device, 15 - error::{code::*, from_err_ptr, from_result, to_result, Error, Result, VTABLE_DEFAULT_ERROR}, 15 + error::{code::*, from_err_ptr, from_result, to_result, Result, VTABLE_DEFAULT_ERROR}, 16 16 ffi::c_ulong, 17 17 prelude::*, 18 18 str::CString, ··· 501 501 // requirements. The OPP core guarantees not to access fields of [`Config`] after this call 502 502 // and so we don't need to save a copy of them for future use. 503 503 let ret = unsafe { bindings::dev_pm_opp_set_config(dev.as_raw(), &mut config) }; 504 - if ret < 0 { 505 - Err(Error::from_errno(ret)) 506 - } else { 507 - Ok(ConfigToken(ret)) 508 - } 504 + 505 + to_result(ret).map(|()| ConfigToken(ret)) 509 506 } 510 507 511 508 /// Config's clk callback. ··· 711 714 // SAFETY: The requirements are satisfied by the existence of [`Device`] and its safety 712 715 // requirements. 713 716 let ret = unsafe { bindings::dev_pm_opp_get_opp_count(self.dev.as_raw()) }; 714 - if ret < 0 { 715 - Err(Error::from_errno(ret)) 716 - } else { 717 - Ok(ret as u32) 718 - } 717 + 718 + to_result(ret).map(|()| ret as u32) 719 719 } 720 720 721 721 /// Returns max clock latency (in nanoseconds) of the [`OPP`]s in the [`Table`].