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 tag 'samsung-drivers-6.18' of https://git.kernel.org/pub/scm/linux/kernel/git/krzk/linux into soc/drivers

Samsung SoC drivers for v6.18

1. Google GS101:
Enable CPU Idle, which needs programming C2 idle hints
via ACPM firmware (Alive Clock and Power Manager). The patch
introducing this depends on 'local-timer-stop' Devicetree property,
which was merged in v6.17.

Fix handling error codes in ACPM firmware driver when talking to
PMIC.

2. Exynos2200: Add dedicated compatible for serial engines (USI).

* tag 'samsung-drivers-6.18' of https://git.kernel.org/pub/scm/linux/kernel/git/krzk/linux:
firmware: exynos-acpm: fix PMIC returned errno
dt-bindings: soc: samsung: usi: add samsung,exynos2200-usi compatible
soc: samsung: exynos-pmu: Enable CPU Idle for gs101

Link: https://lore.kernel.org/r/20250912135448.203678-2-krzysztof.kozlowski@linaro.org
Signed-off-by: Arnd Bergmann <arnd@arndb.de>

+275 -27
+1
Documentation/devicetree/bindings/soc/samsung/exynos-usi.yaml
··· 36 36 - items: 37 37 - enum: 38 38 - google,gs101-usi 39 + - samsung,exynos2200-usi 39 40 - samsung,exynosautov9-usi 40 41 - samsung,exynosautov920-usi 41 42 - const: samsung,exynos850-usi
+20 -5
drivers/firmware/samsung/exynos-acpm-pmic.c
··· 4 4 * Copyright 2020 Google LLC. 5 5 * Copyright 2024 Linaro Ltd. 6 6 */ 7 + #include <linux/array_size.h> 7 8 #include <linux/bitfield.h> 9 + #include <linux/errno.h> 8 10 #include <linux/firmware/samsung/exynos-acpm-protocol.h> 9 11 #include <linux/ktime.h> 10 12 #include <linux/types.h> ··· 34 32 ACPM_PMIC_BULK_READ, 35 33 ACPM_PMIC_BULK_WRITE, 36 34 }; 35 + 36 + static const int acpm_pmic_linux_errmap[] = { 37 + [0] = 0, /* ACPM_PMIC_SUCCESS */ 38 + [1] = -EACCES, /* Read register can't be accessed or issues to access it. */ 39 + [2] = -EACCES, /* Write register can't be accessed or issues to access it. */ 40 + }; 41 + 42 + static int acpm_pmic_to_linux_err(int err) 43 + { 44 + if (err >= 0 && err < ARRAY_SIZE(acpm_pmic_linux_errmap)) 45 + return acpm_pmic_linux_errmap[err]; 46 + return -EIO; 47 + } 37 48 38 49 static inline u32 acpm_pmic_set_bulk(u32 data, unsigned int i) 39 50 { ··· 94 79 95 80 *buf = FIELD_GET(ACPM_PMIC_VALUE, xfer.rxd[1]); 96 81 97 - return FIELD_GET(ACPM_PMIC_RETURN, xfer.rxd[1]); 82 + return acpm_pmic_to_linux_err(FIELD_GET(ACPM_PMIC_RETURN, xfer.rxd[1])); 98 83 } 99 84 100 85 static void acpm_pmic_init_bulk_read_cmd(u32 cmd[4], u8 type, u8 reg, u8 chan, ··· 125 110 if (ret) 126 111 return ret; 127 112 128 - ret = FIELD_GET(ACPM_PMIC_RETURN, xfer.rxd[1]); 113 + ret = acpm_pmic_to_linux_err(FIELD_GET(ACPM_PMIC_RETURN, xfer.rxd[1])); 129 114 if (ret) 130 115 return ret; 131 116 ··· 165 150 if (ret) 166 151 return ret; 167 152 168 - return FIELD_GET(ACPM_PMIC_RETURN, xfer.rxd[1]); 153 + return acpm_pmic_to_linux_err(FIELD_GET(ACPM_PMIC_RETURN, xfer.rxd[1])); 169 154 } 170 155 171 156 static void acpm_pmic_init_bulk_write_cmd(u32 cmd[4], u8 type, u8 reg, u8 chan, ··· 205 190 if (ret) 206 191 return ret; 207 192 208 - return FIELD_GET(ACPM_PMIC_RETURN, xfer.rxd[1]); 193 + return acpm_pmic_to_linux_err(FIELD_GET(ACPM_PMIC_RETURN, xfer.rxd[1])); 209 194 } 210 195 211 196 static void acpm_pmic_init_update_cmd(u32 cmd[4], u8 type, u8 reg, u8 chan, ··· 235 220 if (ret) 236 221 return ret; 237 222 238 - return FIELD_GET(ACPM_PMIC_RETURN, xfer.rxd[1]); 223 + return acpm_pmic_to_linux_err(FIELD_GET(ACPM_PMIC_RETURN, xfer.rxd[1])); 239 224 }
+254 -22
drivers/soc/samsung/exynos-pmu.c
··· 7 7 8 8 #include <linux/array_size.h> 9 9 #include <linux/arm-smccc.h> 10 + #include <linux/bitmap.h> 10 11 #include <linux/cpuhotplug.h> 12 + #include <linux/cpu_pm.h> 11 13 #include <linux/of.h> 12 14 #include <linux/of_address.h> 13 15 #include <linux/mfd/core.h> ··· 17 15 #include <linux/of_platform.h> 18 16 #include <linux/platform_device.h> 19 17 #include <linux/delay.h> 18 + #include <linux/reboot.h> 20 19 #include <linux/regmap.h> 21 20 22 21 #include <linux/soc/samsung/exynos-regs-pmu.h> ··· 38 35 const struct exynos_pmu_data *pmu_data; 39 36 struct regmap *pmureg; 40 37 struct regmap *pmuintrgen; 38 + /* 39 + * Serialization lock for CPU hot plug and cpuidle ACPM hint 40 + * programming. Also protects in_cpuhp, sys_insuspend & sys_inreboot 41 + * flags. 42 + */ 43 + raw_spinlock_t cpupm_lock; 44 + unsigned long *in_cpuhp; 45 + bool sys_insuspend; 46 + bool sys_inreboot; 41 47 }; 42 48 43 49 void __iomem *pmu_base_addr; ··· 233 221 .reg_read = tensor_sec_reg_read, 234 222 .reg_write = tensor_sec_reg_write, 235 223 .reg_update_bits = tensor_sec_update_bits, 224 + .use_raw_spinlock = true, 225 + }; 226 + 227 + static const struct regmap_config regmap_pmu_intr = { 228 + .name = "pmu_intr_gen", 229 + .reg_bits = 32, 230 + .reg_stride = 4, 231 + .val_bits = 32, 232 + .use_raw_spinlock = true, 236 233 }; 237 234 238 235 static const struct exynos_pmu_data gs101_pmu_data = { ··· 351 330 EXPORT_SYMBOL_GPL(exynos_get_pmu_regmap_by_phandle); 352 331 353 332 /* 354 - * CPU_INFORM register hint values which are used by 355 - * EL3 firmware (el3mon). 333 + * CPU_INFORM register "hint" values are required to be programmed in addition to 334 + * the standard PSCI calls to have functional CPU hotplug and CPU idle states. 335 + * This is required to workaround limitations in the el3mon/ACPM firmware. 356 336 */ 357 337 #define CPU_INFORM_CLEAR 0 358 338 #define CPU_INFORM_C2 1 359 339 360 - static int gs101_cpuhp_pmu_online(unsigned int cpu) 340 + /* 341 + * __gs101_cpu_pmu_ prefix functions are common code shared by CPU PM notifiers 342 + * (CPUIdle) and CPU hotplug callbacks. Functions should be called with IRQs 343 + * disabled and cpupm_lock held. 344 + */ 345 + static int __gs101_cpu_pmu_online(unsigned int cpu) 361 346 { 362 347 unsigned int cpuhint = smp_processor_id(); 363 348 u32 reg, mask; ··· 385 358 return 0; 386 359 } 387 360 388 - static int gs101_cpuhp_pmu_offline(unsigned int cpu) 361 + /* Called from CPU PM notifier (CPUIdle code path) with IRQs disabled */ 362 + static int gs101_cpu_pmu_online(void) 389 363 { 390 - u32 reg, mask; 364 + int cpu; 365 + 366 + raw_spin_lock(&pmu_context->cpupm_lock); 367 + 368 + if (pmu_context->sys_inreboot) { 369 + raw_spin_unlock(&pmu_context->cpupm_lock); 370 + return NOTIFY_OK; 371 + } 372 + 373 + cpu = smp_processor_id(); 374 + __gs101_cpu_pmu_online(cpu); 375 + raw_spin_unlock(&pmu_context->cpupm_lock); 376 + 377 + return NOTIFY_OK; 378 + } 379 + 380 + /* Called from CPU hot plug callback with IRQs enabled */ 381 + static int gs101_cpuhp_pmu_online(unsigned int cpu) 382 + { 383 + unsigned long flags; 384 + 385 + raw_spin_lock_irqsave(&pmu_context->cpupm_lock, flags); 386 + 387 + __gs101_cpu_pmu_online(cpu); 388 + /* 389 + * Mark this CPU as having finished the hotplug. 390 + * This means this CPU can now enter C2 idle state. 391 + */ 392 + clear_bit(cpu, pmu_context->in_cpuhp); 393 + raw_spin_unlock_irqrestore(&pmu_context->cpupm_lock, flags); 394 + 395 + return 0; 396 + } 397 + 398 + /* Common function shared by both CPU hot plug and CPUIdle */ 399 + static int __gs101_cpu_pmu_offline(unsigned int cpu) 400 + { 391 401 unsigned int cpuhint = smp_processor_id(); 402 + u32 reg, mask; 392 403 393 404 /* set cpu inform hint */ 394 405 regmap_write(pmu_context->pmureg, GS101_CPU_INFORM(cpuhint), ··· 444 379 regmap_read(pmu_context->pmuintrgen, GS101_GRP1_INTR_BID_UPEND, &reg); 445 380 regmap_write(pmu_context->pmuintrgen, GS101_GRP1_INTR_BID_CLEAR, 446 381 reg & mask); 382 + 383 + return 0; 384 + } 385 + 386 + /* Called from CPU PM notifier (CPUIdle code path) with IRQs disabled */ 387 + static int gs101_cpu_pmu_offline(void) 388 + { 389 + int cpu; 390 + 391 + raw_spin_lock(&pmu_context->cpupm_lock); 392 + cpu = smp_processor_id(); 393 + 394 + if (test_bit(cpu, pmu_context->in_cpuhp)) { 395 + raw_spin_unlock(&pmu_context->cpupm_lock); 396 + return NOTIFY_BAD; 397 + } 398 + 399 + /* Ignore CPU_PM_ENTER event in reboot or suspend sequence. */ 400 + if (pmu_context->sys_insuspend || pmu_context->sys_inreboot) { 401 + raw_spin_unlock(&pmu_context->cpupm_lock); 402 + return NOTIFY_OK; 403 + } 404 + 405 + __gs101_cpu_pmu_offline(cpu); 406 + raw_spin_unlock(&pmu_context->cpupm_lock); 407 + 408 + return NOTIFY_OK; 409 + } 410 + 411 + /* Called from CPU hot plug callback with IRQs enabled */ 412 + static int gs101_cpuhp_pmu_offline(unsigned int cpu) 413 + { 414 + unsigned long flags; 415 + 416 + raw_spin_lock_irqsave(&pmu_context->cpupm_lock, flags); 417 + /* 418 + * Mark this CPU as entering hotplug. So as not to confuse 419 + * ACPM the CPU entering hotplug should not enter C2 idle state. 420 + */ 421 + set_bit(cpu, pmu_context->in_cpuhp); 422 + __gs101_cpu_pmu_offline(cpu); 423 + 424 + raw_spin_unlock_irqrestore(&pmu_context->cpupm_lock, flags); 425 + 426 + return 0; 427 + } 428 + 429 + static int gs101_cpu_pm_notify_callback(struct notifier_block *self, 430 + unsigned long action, void *v) 431 + { 432 + switch (action) { 433 + case CPU_PM_ENTER: 434 + return gs101_cpu_pmu_offline(); 435 + 436 + case CPU_PM_EXIT: 437 + return gs101_cpu_pmu_online(); 438 + } 439 + 440 + return NOTIFY_OK; 441 + } 442 + 443 + static struct notifier_block gs101_cpu_pm_notifier = { 444 + .notifier_call = gs101_cpu_pm_notify_callback, 445 + /* 446 + * We want to be called first, as the ACPM hint and handshake is what 447 + * puts the CPU into C2. 448 + */ 449 + .priority = INT_MAX 450 + }; 451 + 452 + static int exynos_cpupm_reboot_notifier(struct notifier_block *nb, 453 + unsigned long event, void *v) 454 + { 455 + unsigned long flags; 456 + 457 + switch (event) { 458 + case SYS_POWER_OFF: 459 + case SYS_RESTART: 460 + raw_spin_lock_irqsave(&pmu_context->cpupm_lock, flags); 461 + pmu_context->sys_inreboot = true; 462 + raw_spin_unlock_irqrestore(&pmu_context->cpupm_lock, flags); 463 + break; 464 + } 465 + 466 + return NOTIFY_OK; 467 + } 468 + 469 + static struct notifier_block exynos_cpupm_reboot_nb = { 470 + .priority = INT_MAX, 471 + .notifier_call = exynos_cpupm_reboot_notifier, 472 + }; 473 + 474 + static int setup_cpuhp_and_cpuidle(struct device *dev) 475 + { 476 + struct device_node *intr_gen_node; 477 + struct resource intrgen_res; 478 + void __iomem *virt_addr; 479 + int ret, cpu; 480 + 481 + intr_gen_node = of_parse_phandle(dev->of_node, 482 + "google,pmu-intr-gen-syscon", 0); 483 + if (!intr_gen_node) { 484 + /* 485 + * To maintain support for older DTs that didn't specify syscon 486 + * phandle just issue a warning rather than fail to probe. 487 + */ 488 + dev_warn(dev, "pmu-intr-gen syscon unavailable\n"); 489 + return 0; 490 + } 491 + 492 + /* 493 + * To avoid lockdep issues (CPU PM notifiers use raw spinlocks) create 494 + * a mmio regmap for pmu-intr-gen that uses raw spinlocks instead of 495 + * syscon provided regmap. 496 + */ 497 + ret = of_address_to_resource(intr_gen_node, 0, &intrgen_res); 498 + of_node_put(intr_gen_node); 499 + 500 + virt_addr = devm_ioremap(dev, intrgen_res.start, 501 + resource_size(&intrgen_res)); 502 + if (!virt_addr) 503 + return -ENOMEM; 504 + 505 + pmu_context->pmuintrgen = devm_regmap_init_mmio(dev, virt_addr, 506 + &regmap_pmu_intr); 507 + if (IS_ERR(pmu_context->pmuintrgen)) { 508 + dev_err(dev, "failed to initialize pmu-intr-gen regmap\n"); 509 + return PTR_ERR(pmu_context->pmuintrgen); 510 + } 511 + 512 + /* register custom mmio regmap with syscon */ 513 + ret = of_syscon_register_regmap(intr_gen_node, 514 + pmu_context->pmuintrgen); 515 + if (ret) 516 + return ret; 517 + 518 + pmu_context->in_cpuhp = devm_bitmap_zalloc(dev, num_possible_cpus(), 519 + GFP_KERNEL); 520 + if (!pmu_context->in_cpuhp) 521 + return -ENOMEM; 522 + 523 + raw_spin_lock_init(&pmu_context->cpupm_lock); 524 + pmu_context->sys_inreboot = false; 525 + pmu_context->sys_insuspend = false; 526 + 527 + /* set PMU to power on */ 528 + for_each_online_cpu(cpu) 529 + gs101_cpuhp_pmu_online(cpu); 530 + 531 + /* register CPU hotplug callbacks */ 532 + cpuhp_setup_state(CPUHP_BP_PREPARE_DYN, "soc/exynos-pmu:prepare", 533 + gs101_cpuhp_pmu_online, NULL); 534 + 535 + cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "soc/exynos-pmu:online", 536 + NULL, gs101_cpuhp_pmu_offline); 537 + 538 + /* register CPU PM notifiers for cpuidle */ 539 + cpu_pm_register_notifier(&gs101_cpu_pm_notifier); 540 + register_reboot_notifier(&exynos_cpupm_reboot_nb); 447 541 return 0; 448 542 } 449 543 ··· 659 435 pmu_context->dev = dev; 660 436 661 437 if (pmu_context->pmu_data && pmu_context->pmu_data->pmu_cpuhp) { 662 - pmu_context->pmuintrgen = syscon_regmap_lookup_by_phandle(dev->of_node, 663 - "google,pmu-intr-gen-syscon"); 664 - if (IS_ERR(pmu_context->pmuintrgen)) { 665 - /* 666 - * To maintain support for older DTs that didn't specify syscon phandle 667 - * just issue a warning rather than fail to probe. 668 - */ 669 - dev_warn(&pdev->dev, "pmu-intr-gen syscon unavailable\n"); 670 - } else { 671 - cpuhp_setup_state(CPUHP_BP_PREPARE_DYN, 672 - "soc/exynos-pmu:prepare", 673 - gs101_cpuhp_pmu_online, NULL); 674 - 675 - cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, 676 - "soc/exynos-pmu:online", 677 - NULL, gs101_cpuhp_pmu_offline); 678 - } 438 + ret = setup_cpuhp_and_cpuidle(dev); 439 + if (ret) 440 + return ret; 679 441 } 680 442 681 443 if (pmu_context->pmu_data && pmu_context->pmu_data->pmu_init) ··· 681 471 return 0; 682 472 } 683 473 474 + static int exynos_cpupm_suspend_noirq(struct device *dev) 475 + { 476 + raw_spin_lock(&pmu_context->cpupm_lock); 477 + pmu_context->sys_insuspend = true; 478 + raw_spin_unlock(&pmu_context->cpupm_lock); 479 + return 0; 480 + } 481 + 482 + static int exynos_cpupm_resume_noirq(struct device *dev) 483 + { 484 + raw_spin_lock(&pmu_context->cpupm_lock); 485 + pmu_context->sys_insuspend = false; 486 + raw_spin_unlock(&pmu_context->cpupm_lock); 487 + return 0; 488 + } 489 + 490 + static const struct dev_pm_ops cpupm_pm_ops = { 491 + NOIRQ_SYSTEM_SLEEP_PM_OPS(exynos_cpupm_suspend_noirq, 492 + exynos_cpupm_resume_noirq) 493 + }; 494 + 684 495 static struct platform_driver exynos_pmu_driver = { 685 496 .driver = { 686 497 .name = "exynos-pmu", 687 498 .of_match_table = exynos_pmu_of_device_ids, 499 + .pm = pm_sleep_ptr(&cpupm_pm_ops), 688 500 }, 689 501 .probe = exynos_pmu_probe, 690 502 };