023cd1cf3f
-----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEEZH8oZUiU471FcZm+ONu9yGCSaT4FAmJZYqcACgkQONu9yGCS aT51cA/+PXr+24MJMwa0PyLuQO9YScRAu/4E8PtEkumpj5dA9FuWJCcuNwO9cmNp YM6IdGGbHfW+JRsX3wLAothut1ID9hfl+Y2tpBFJacS7E5ezgzoiAF1ke8RsBxd/ s+sRwZqRfSVoVmwYGj9/SwXLzJJTdPwY/FwXUdsyxxkn8u99YmAURlNUZdv0+KWs vmAvS6mj4M4GfazS9FfBhnUVMcxbDgY0/rNlek2rMQi1ValvrYeNBATjKMI/NrkR /bRTplCezuDFDw82IqQfiqGQ71mMbpYFXxkbdXsJj3nhIJ1AimWRQhLRg/TqJOi4 0Hhx3cEk/5hs/22VBN9sIYIAbJr+z7Kr9gnhltAETPOrv0s9w9fnJARve5GlwSHV yKBm3Pfq0+abAQ2urnsmiHFvMMzFaiNuWe98TOF0BHkJbwMSFQpgFtp0yWx2bgMf Svx/rEXzd2Cx0h5X4dHAMykPqsJAek0qIb4MgOPAEpuZWLZ09xfXOeVc8lTbHG22 y/HfKE+4FMTw8tsAe/7E7xP+yjosPrAq8De2ymMo9NGDFxT8I9ro+gkqwMWwC+yi trYDVFEX3NNIEG9D6Oh+eP2nY97U898wCI1GFU18J9zOPQsw4peHSS8xPW7vLbqy zrzOxMKW+2khSwj/wFlSXRaj3pogP5/y4jaAXpMSse0Zb3Neu2U= =p4tc -----END PGP SIGNATURE----- Merge 5.4.189 into android11-5.4-lts Changes in 5.4.189 swiotlb: fix info leak with DMA_FROM_DEVICE USB: serial: pl2303: add IBM device IDs USB: serial: simple: add Nokia phone driver netdevice: add the case if dev is NULL HID: logitech-dj: add new lightspeed receiver id xfrm: fix tunnel model fragmentation behavior virtio_console: break out of buf poll on remove ethernet: sun: Free the coherent when failing in probing spi: Fix invalid sgs value net:mcf8390: Use platform_get_irq() to get the interrupt spi: Fix erroneous sgs value with min_t() af_key: add __GFP_ZERO flag for compose_sadb_supported in function pfkey_register net: dsa: microchip: add spi_device_id tables iommu/iova: Improve 32-bit free space estimate tpm: fix reference counting for struct tpm_chip block: Add a helper to validate the block size virtio-blk: Use blk_validate_block_size() to validate block size USB: usb-storage: Fix use of bitfields for hardware data in ene_ub6250.c xhci: fix runtime PM imbalance in USB2 resume xhci: make xhci_handshake timeout for xhci_reset() adjustable xhci: fix uninitialized string returned by xhci_decode_ctrl_ctx() coresight: Fix TRCCONFIGR.QE sysfs interface iio: afe: rescale: use s64 for temporary scale calculations iio: inkern: apply consumer scale on IIO_VAL_INT cases iio: inkern: apply consumer scale when no channel scale is available iio: inkern: make a best effort on offset calculation greybus: svc: fix an error handling bug in gb_svc_hello() clk: uniphier: Fix fixed-rate initialization ptrace: Check PTRACE_O_SUSPEND_SECCOMP permission on PTRACE_SEIZE KEYS: fix length validation in keyctl_pkey_params_get_2() Documentation: add link to stable release candidate tree Documentation: update stable tree link HID: intel-ish-hid: Use dma_alloc_coherent for firmware update SUNRPC: avoid race between mod_timer() and del_timer_sync() NFSD: prevent underflow in nfssvc_decode_writeargs() NFSD: prevent integer overflow on 32 bit systems f2fs: fix to unlock page correctly in error path of is_alive() f2fs: quota: fix loop condition at f2fs_quota_sync() f2fs: fix to do sanity check on .cp_pack_total_block_count pinctrl: samsung: drop pin banks references on error paths spi: mxic: Fix the transmit path can: ems_usb: ems_usb_start_xmit(): fix double dev_kfree_skb() in error path jffs2: fix use-after-free in jffs2_clear_xattr_subsystem jffs2: fix memory leak in jffs2_do_mount_fs jffs2: fix memory leak in jffs2_scan_medium mm/pages_alloc.c: don't create ZONE_MOVABLE beyond the end of a node mm: invalidate hwpoison page cache page in fault path mempolicy: mbind_range() set_policy() after vma_merge() scsi: libsas: Fix sas_ata_qc_issue() handling of NCQ NON DATA commands qed: display VF trust config qed: validate and restrict untrusted VFs vlan promisc mode riscv: Fix fill_callchain return value Revert "Input: clear BTN_RIGHT/MIDDLE on buttonpads" ALSA: cs4236: fix an incorrect NULL check on list iterator ALSA: hda/realtek: Fix audio regression on Mi Notebook Pro 2020 mm,hwpoison: unmap poisoned page before invalidation mm/kmemleak: reset tag when compare object pointer drbd: fix potential silent data corruption powerpc/kvm: Fix kvm_use_magic_page udp: call udp_encap_enable for v6 sockets when enabling encap ACPI: properties: Consistently return -ENOENT if there are no more references drivers: hamradio: 6pack: fix UAF bug caused by mod_timer() mailbox: tegra-hsp: Flush whole channel block: don't merge across cgroup boundaries if blkcg is enabled drm/edid: check basic audio support on CEA extension block video: fbdev: sm712fb: Fix crash in smtcfb_read() video: fbdev: atari: Atari 2 bpp (STe) palette bugfix ARM: dts: at91: sama5d2: Fix PMERRLOC resource size ARM: dts: exynos: fix UART3 pins configuration in Exynos5250 ARM: dts: exynos: add missing HDMI supplies on SMDK5250 ARM: dts: exynos: add missing HDMI supplies on SMDK5420 carl9170: fix missing bit-wise or operator for tx_params thermal: int340x: Increase bitmap size lib/raid6/test: fix multiple definition linking error crypto: rsa-pkcs1pad - correctly get hash from source scatterlist crypto: rsa-pkcs1pad - restore signature length check crypto: rsa-pkcs1pad - fix buffer overread in pkcs1pad_verify_complete() DEC: Limit PMAX memory probing to R3k systems media: davinci: vpif: fix unbalanced runtime PM get xtensa: fix stop_machine_cpuslocked call in patch_text xtensa: fix xtensa_wsr always writing 0 brcmfmac: firmware: Allocate space for default boardrev in nvram brcmfmac: pcie: Release firmwares in the brcmf_pcie_setup error path brcmfmac: pcie: Replace brcmf_pcie_copy_mem_todev with memcpy_toio brcmfmac: pcie: Fix crashes due to early IRQs PCI: pciehp: Clear cmd_busy bit in polling mode regulator: qcom_smd: fix for_each_child.cocci warnings crypto: authenc - Fix sleep in atomic context in decrypt_tail crypto: mxs-dcp - Fix scatterlist processing spi: tegra114: Add missing IRQ check in tegra_spi_probe selftests/x86: Add validity check and allow field splitting audit: log AUDIT_TIME_* records only from rules crypto: ccree - don't attempt 0 len DMA mappings spi: pxa2xx-pci: Balance reference count for PCI DMA device hwmon: (pmbus) Add mutex to regulator ops hwmon: (sch56xx-common) Replace WDOG_ACTIVE with WDOG_HW_RUNNING block: don't delete queue kobject before its children PM: hibernate: fix __setup handler error handling PM: suspend: fix return value of __setup handler hwrng: atmel - disable trng on failure path crypto: vmx - add missing dependencies clocksource/drivers/timer-of: Check return value of of_iomap in timer_of_base_init() ACPI: APEI: fix return value of __setup handlers crypto: ccp - ccp_dmaengine_unregister release dma channels hwmon: (pmbus) Add Vin unit off handling clocksource: acpi_pm: fix return value of __setup handler sched/debug: Remove mpol_get/put and task_lock/unlock from sched_show_numa perf/core: Fix address filter parser for multiple filters perf/x86/intel/pt: Fix address filter config for 32-bit kernel f2fs: fix missing free nid in f2fs_handle_failed_inode f2fs: fix to avoid potential deadlock media: bttv: fix WARNING regression on tunerless devices media: coda: Fix missing put_device() call in coda_get_vdoa_data media: hantro: Fix overfill bottom register field name media: aspeed: Correct value for h-total-pixels video: fbdev: smscufx: Fix null-ptr-deref in ufx_usb_probe() video: fbdev: atmel_lcdfb: fix an error code in atmel_lcdfb_probe() video: fbdev: fbcvt.c: fix printing in fb_cvt_print_name() ARM: dts: qcom: ipq4019: fix sleep clock soc: qcom: rpmpd: Check for null return of devm_kcalloc soc: qcom: aoss: remove spurious IRQF_ONESHOT flags arm64: dts: qcom: sm8150: Correct TCS configuration for apps rsc soc: ti: wkup_m3_ipc: Fix IRQ check in wkup_m3_ipc_probe ARM: dts: imx: Add missing LVDS decoder on M53Menlo media: video/hdmi: handle short reads of hdmi info frame. media: em28xx: initialize refcount before kref_get media: usb: go7007: s2250-board: fix leak in probe() uaccess: fix nios2 and microblaze get_user_8() ASoC: rt5663: check the return value of devm_kzalloc() in rt5663_parse_dp() ASoC: ti: davinci-i2s: Add check for clk_enable() ALSA: spi: Add check for clk_enable() arm64: dts: ns2: Fix spi-cpol and spi-cpha property arm64: dts: broadcom: Fix sata nodename printk: fix return value of printk.devkmsg __setup handler ASoC: mxs-saif: Handle errors for clk_enable ASoC: atmel_ssc_dai: Handle errors for clk_enable ASoC: soc-compress: prevent the potentially use of null pointer memory: emif: Add check for setup_interrupts memory: emif: check the pointer temp in get_device_details() ALSA: firewire-lib: fix uninitialized flag for AV/C deferred transaction arm64: dts: rockchip: Fix SDIO regulator supply properties on rk3399-firefly media: stk1160: If start stream fails, return buffers with VB2_BUF_STATE_QUEUED ASoC: atmel: Add missing of_node_put() in at91sam9g20ek_audio_probe ASoC: wm8350: Handle error for wm8350_register_irq ASoC: fsi: Add check for clk_enable video: fbdev: omapfb: Add missing of_node_put() in dvic_probe_of ivtv: fix incorrect device_caps for ivtvfb ASoC: dmaengine: do not use a NULL prepare_slave_config() callback ASoC: mxs: Fix error handling in mxs_sgtl5000_probe ASoC: imx-es8328: Fix error return code in imx_es8328_probe() ASoC: msm8916-wcd-digital: Fix missing clk_disable_unprepare() in msm8916_wcd_digital_probe mmc: davinci_mmc: Handle error for clk_enable ASoC: msm8916-wcd-analog: Fix error handling in pm8916_wcd_analog_spmi_probe drm/bridge: Fix free wrong object in sii8620_init_rcp_input_dev drm/bridge: Add missing pm_runtime_disable() in __dw_mipi_dsi_probe ath10k: fix memory overwrite of the WoWLAN wakeup packet pattern udmabuf: validate ubuf->pagecount Bluetooth: hci_serdev: call init_rwsem() before p->open() mtd: onenand: Check for error irq mtd: rawnand: gpmi: fix controller timings setting drm/edid: Don't clear formats if using deep color drm/amd/display: Fix a NULL pointer dereference in amdgpu_dm_connector_add_common_modes() ath9k_htc: fix uninit value bugs KVM: PPC: Fix vmx/vsx mixup in mmio emulation i40e: don't reserve excessive XDP_PACKET_HEADROOM on XSK Rx to skb power: reset: gemini-poweroff: Fix IRQ check in gemini_poweroff_probe ray_cs: Check ioremap return value powerpc/perf: Don't use perf_hw_context for trace IMC PMU mt76: mt7603: check sta_rates pointer in mt7603_sta_rate_tbl_update mt76: mt7615: check sta_rates pointer in mt7615_sta_rate_tbl_update net: dsa: mv88e6xxx: Enable port policy support on 6097 PCI: aardvark: Fix reading PCI_EXP_RTSTA_PME bit on emulated bridge power: supply: ab8500: Fix memory leak in ab8500_fg_sysfs_init HID: i2c-hid: fix GET/SET_REPORT for unnumbered reports iommu/ipmmu-vmsa: Check for error num after setting mask drm/amd/display: Add affected crtcs to atomic state for dsc mst unplug IB/cma: Allow XRC INI QPs to set their local ACK timeout dax: make sure inodes are flushed before destroy cache iwlwifi: Fix -EIO error code that is never returned iwlwifi: mvm: Fix an error code in iwl_mvm_up() dm crypt: fix get_key_size compiler warning if !CONFIG_KEYS scsi: pm8001: Fix command initialization in pm80XX_send_read_log() scsi: pm8001: Fix command initialization in pm8001_chip_ssp_tm_req() scsi: pm8001: Fix payload initialization in pm80xx_set_thermal_config() scsi: pm8001: Fix abort all task initialization drm/amd/display: Remove vupdate_int_entry definition TOMOYO: fix __setup handlers return values ext2: correct max file size computing drm/tegra: Fix reference leak in tegra_dsi_ganged_probe power: supply: bq24190_charger: Fix bq24190_vbus_is_enabled() wrong false return scsi: hisi_sas: Change permission of parameter prot_mask drm/bridge: cdns-dsi: Make sure to to create proper aliases for dt bpf, arm64: Call build_prologue() first in first JIT pass bpf, arm64: Feed byte-offset into bpf line info libbpf: Skip forward declaration when counting duplicated type names powerpc/Makefile: Don't pass -mcpu=powerpc64 when building 32-bit KVM: x86: Fix emulation in writing cr8 KVM: x86/emulator: Defer not-present segment check in __load_segment_descriptor() hv_balloon: rate-limit "Unhandled message" warning i2c: xiic: Make bus names unique power: supply: wm8350-power: Handle error for wm8350_register_irq power: supply: wm8350-power: Add missing free in free_charger_irq PCI: Reduce warnings on possible RW1C corruption mips: DEC: honor CONFIG_MIPS_FP_SUPPORT=n powerpc/sysdev: fix incorrect use to determine if list is empty mfd: mc13xxx: Add check for mc13xxx_irq_request selftests/bpf: Make test_lwt_ip_encap more stable and faster powerpc: 8xx: fix a return value error in mpc8xx_pic_init vxcan: enable local echo for sent CAN frames MIPS: RB532: fix return value of __setup handler mtd: rawnand: atmel: fix refcount issue in atmel_nand_controller_init RDMA/mlx5: Fix memory leak in error flow for subscribe event routine bpf, sockmap: Fix memleak in tcp_bpf_sendmsg while sk msg is full bpf, sockmap: Fix more uncharged while msg has more_data bpf, sockmap: Fix double uncharge the mem of sk_msg USB: storage: ums-realtek: fix error code in rts51x_read_mem() Bluetooth: btmtksdio: Fix kernel oops in btmtksdio_interrupt af_netlink: Fix shift out of bounds in group mask calculation i2c: mux: demux-pinctrl: do not deactivate a master that is not active selftests/bpf/test_lirc_mode2.sh: Exit with proper code tcp: ensure PMTU updates are processed during fastopen openvswitch: always update flow key after nat tipc: fix the timer expires after interval 100ms mfd: asic3: Add missing iounmap() on error asic3_mfd_probe mxser: fix xmit_buf leak in activate when LSR == 0xff pwm: lpc18xx-sct: Initialize driver data and hardware before pwmchip_add() misc: alcor_pci: Fix an error handling path staging:iio:adc:ad7280a: Fix handing of device address bit reversing. pinctrl: renesas: r8a77470: Reduce size for narrow VIN1 channel clk: qcom: ipq8074: Use floor ops for SDCC1 clock phy: dphy: Correct lpx parameter and its derivatives(ta_{get,go,sure}) serial: 8250_mid: Balance reference count for PCI DMA device serial: 8250: Fix race condition in RTS-after-send handling iio: adc: Add check for devm_request_threaded_irq NFS: Return valid errors from nfs2/3_decode_dirent() dma-debug: fix return value of __setup handlers clk: imx7d: Remove audio_mclk_root_clk clk: qcom: clk-rcg2: Update logic to calculate D value for RCG clk: qcom: clk-rcg2: Update the frac table for pixel clock remoteproc: qcom: Fix missing of_node_put in adsp_alloc_memory_region remoteproc: qcom_wcnss: Add missing of_node_put() in wcnss_alloc_memory_region clk: actions: Terminate clk_div_table with sentinel element clk: loongson1: Terminate clk_div_table with sentinel element clk: clps711x: Terminate clk_div_table with sentinel element clk: tegra: tegra124-emc: Fix missing put_device() call in emc_ensure_emc_driver NFS: remove unneeded check in decode_devicenotify_args() staging: mt7621-dts: fix LEDs and pinctrl on GB-PC1 devicetree pinctrl: mediatek: Fix missing of_node_put() in mtk_pctrl_init pinctrl: mediatek: paris: Fix "argument" argument type for mtk_pinconf_get() pinctrl: mediatek: paris: Fix pingroup pin config state readback pinctrl: nomadik: Add missing of_node_put() in nmk_pinctrl_probe pinctrl/rockchip: Add missing of_node_put() in rockchip_pinctrl_probe tty: hvc: fix return value of __setup handler kgdboc: fix return value of __setup handler kgdbts: fix return value of __setup handler firmware: google: Properly state IOMEM dependency driver core: dd: fix return value of __setup handler jfs: fix divide error in dbNextAG netfilter: nf_conntrack_tcp: preserve liberal flag in tcp options NFSv4.1: don't retry BIND_CONN_TO_SESSION on session error clk: qcom: gcc-msm8994: Fix gpll4 width clk: Initialize orphan req_rate xen: fix is_xen_pmu() net: phy: broadcom: Fix brcm_fet_config_init() selftests: test_vxlan_under_vrf: Fix broken test case qlcnic: dcb: default to returning -EOPNOTSUPP net/x25: Fix null-ptr-deref caused by x25_disconnect NFSv4/pNFS: Fix another issue with a list iterator pointing to the head net: dsa: bcm_sf2_cfp: fix an incorrect NULL check on list iterator lib/test: use after free in register_test_dev_kmod() LSM: general protection fault in legacy_parse_param gcc-plugins/stackleak: Exactly match strings instead of prefixes pinctrl: npcm: Fix broken references to chip->parent_device block, bfq: don't move oom_bfqq selinux: use correct type for context length loop: use sysfs_emit() in the sysfs xxx show() Fix incorrect type in assignment of ipv6 port for audit irqchip/qcom-pdc: Fix broken locking irqchip/nvic: Release nvic_base upon failure bfq: fix use-after-free in bfq_dispatch_request ACPICA: Avoid walking the ACPI Namespace if it is not there lib/raid6/test/Makefile: Use $(pound) instead of \# for Make 4.3 Revert "Revert "block, bfq: honor already-setup queue merges"" ACPI/APEI: Limit printable size of BERT table data PM: core: keep irq flags in device_pm_check_callbacks() spi: tegra20: Use of_device_get_match_data() ext4: don't BUG if someone dirty pages without asking ext4 first ntfs: add sanity check on allocation size video: fbdev: nvidiafb: Use strscpy() to prevent buffer overflow video: fbdev: w100fb: Reset global state video: fbdev: cirrusfb: check pixclock to avoid divide by zero video: fbdev: omapfb: acx565akm: replace snprintf with sysfs_emit ARM: dts: qcom: fix gic_irq_domain_translate warnings for msm8960 ARM: dts: bcm2837: Add the missing L1/L2 cache information ASoC: madera: Add dependencies on MFD video: fbdev: omapfb: panel-dsi-cm: Use sysfs_emit() instead of snprintf() video: fbdev: omapfb: panel-tpo-td043mtea1: Use sysfs_emit() instead of snprintf() video: fbdev: udlfb: replace snprintf in show functions with sysfs_emit ASoC: soc-core: skip zero num_dai component in searching dai name media: cx88-mpeg: clear interrupt status register before streaming video ARM: tegra: tamonten: Fix I2C3 pad setting ARM: mmp: Fix failure to remove sram device video: fbdev: sm712fb: Fix crash in smtcfb_write() media: Revert "media: em28xx: add missing em28xx_close_extension" media: hdpvr: initialize dev->worker at hdpvr_register_videodev mmc: host: Return an error when ->enable_sdio_irq() ops is missing ALSA: hda/realtek: Add alc256-samsung-headphone fixup powerpc/lib/sstep: Fix 'sthcx' instruction powerpc/lib/sstep: Fix build errors with newer binutils powerpc: Fix build errors with newer binutils scsi: qla2xxx: Fix stuck session in gpdb scsi: qla2xxx: Fix wrong FDMI data for 64G adapter scsi: qla2xxx: Fix warning for missing error code scsi: qla2xxx: Fix device reconnect in loop topology scsi: qla2xxx: Add devids and conditionals for 28xx scsi: qla2xxx: Check for firmware dump already collected scsi: qla2xxx: Suppress a kernel complaint in qla_create_qpair() scsi: qla2xxx: Fix disk failure to rediscover scsi: qla2xxx: Fix incorrect reporting of task management failure scsi: qla2xxx: Fix hang due to session stuck scsi: qla2xxx: Fix missed DMA unmap for NVMe ls requests scsi: qla2xxx: Fix N2N inconsistent PLOGI scsi: qla2xxx: Reduce false trigger to login scsi: qla2xxx: Use correct feature type field during RFF_ID processing KVM: Prevent module exit until all VMs are freed KVM: x86: fix sending PV IPI ASoC: SOF: Intel: Fix NULL ptr dereference when ENOMEM ubifs: rename_whiteout: Fix double free for whiteout_ui->data ubifs: Fix deadlock in concurrent rename whiteout and inode writeback ubifs: Add missing iput if do_tmpfile() failed in rename whiteout ubifs: setflags: Make dirtied_ino_d 8 bytes aligned ubifs: Fix read out-of-bounds in ubifs_wbuf_write_nolock() ubifs: rename_whiteout: correct old_dir size computing XArray: Fix xas_create_range() when multi-order entry present can: mcba_usb: mcba_usb_start_xmit(): fix double dev_kfree_skb in error path can: mcba_usb: properly check endpoint type XArray: Update the LRU list in xas_split() rtc: check if __rtc_read_time was successful gfs2: Make sure FITRIM minlen is rounded up to fs block size net: hns3: fix software vlan talbe of vlan 0 inconsistent with hardware pinctrl: pinconf-generic: Print arguments for bias-pull-* pinctrl: nuvoton: npcm7xx: Rename DS() macro to DSTR() pinctrl: nuvoton: npcm7xx: Use %zu printk format for ARRAY_SIZE() ASoC: mediatek: mt6358: add missing EXPORT_SYMBOLs ubi: Fix race condition between ctrl_cdev_ioctl and ubi_cdev_ioctl ARM: iop32x: offset IRQ numbers by 1 ACPI: CPPC: Avoid out of bounds access when parsing _CPC data powerpc/kasan: Fix early region not updated correctly ASoC: soc-compress: Change the check for codec_dai mm/mmap: return 1 from stack_guard_gap __setup() handler mm/memcontrol: return 1 from cgroup.memory __setup() handler mm/usercopy: return 1 from hardened_usercopy __setup() handler bpf: Fix comment for helper bpf_current_task_under_cgroup() dt-bindings: mtd: nand-controller: Fix the reg property description dt-bindings: mtd: nand-controller: Fix a comment in the examples dt-bindings: spi: mxic: The interrupt property is not mandatory ubi: fastmap: Return error code if memory allocation fails in add_aeb() ASoC: topology: Allow TLV control to be either read or write ARM: dts: spear1340: Update serial node properties ARM: dts: spear13xx: Update SPI dma properties um: Fix uml_mconsole stop/go openvswitch: Fixed nd target mask field in the flow dump. KVM: x86/mmu: do compare-and-exchange of gPTE via the user address KVM: x86: Forbid VMM to set SYNIC/STIMER MSRs when SynIC wasn't activated ubifs: Rectify space amount budget for mkdir/tmpfile operations rtc: wm8350: Handle error for wm8350_register_irq riscv module: remove (NOLOAD) ARM: 9187/1: JIVE: fix return value of __setup handler KVM: x86/svm: Clear reserved bits written to PerfEvtSeln MSRs drm: Add orientation quirk for GPD Win Max ath5k: fix OOB in ath5k_eeprom_read_pcal_info_5111 drm/amd/amdgpu/amdgpu_cs: fix refcount leak of a dma_fence obj ptp: replace snprintf with sysfs_emit powerpc: dts: t104xrdb: fix phy type for FMAN 4/5 bpf: Make dst_port field in struct bpf_sock 16-bit wide scsi: mvsas: Replace snprintf() with sysfs_emit() scsi: bfa: Replace snprintf() with sysfs_emit() power: supply: axp20x_battery: properly report current when discharging ipv6: make mc_forwarding atomic powerpc: Set crashkernel offset to mid of RMA region drm/amdgpu: Fix recursive locking warning PCI: aardvark: Fix support for MSI interrupts iommu/arm-smmu-v3: fix event handling soft lockup usb: ehci: add pci device support for Aspeed platforms PCI: pciehp: Add Qualcomm quirk for Command Completed erratum power: supply: axp288-charger: Set Vhold to 4.4V ipv4: Invalidate neighbour for broadcast address upon address addition dm ioctl: prevent potential spectre v1 gadget drm/amdkfd: make CRAT table missing message informational only scsi: pm8001: Fix pm8001_mpi_task_abort_resp() scsi: aha152x: Fix aha152x_setup() __setup handler return value net/smc: correct settings of RMB window update limit mips: ralink: fix a refcount leak in ill_acc_of_setup() macvtap: advertise link netns via netlink tuntap: add sanity checks about msg_controllen in sendmsg bnxt_en: Eliminate unintended link toggle during FW reset MIPS: fix fortify panic when copying asm exception handlers scsi: libfc: Fix use after free in fc_exch_abts_resp() usb: dwc3: omap: fix "unbalanced disables for smps10_out1" on omap5evm xtensa: fix DTC warning unit_address_format Bluetooth: Fix use after free in hci_send_acl netlabel: fix out-of-bounds memory accesses init/main.c: return 1 from handled __setup() functions minix: fix bug when opening a file with O_DIRECT clk: si5341: fix reported clk_rate when output divider is 2 w1: w1_therm: fixes w1_seq for ds28ea00 sensors NFSv4: Protect the state recovery thread against direct reclaim xen: delay xen_hvm_init_time_ops() if kdump is boot on vcpu>=32 clk: Enforce that disjoints limits are invalid SUNRPC/call_alloc: async tasks mustn't block waiting for memory NFS: swap IO handling is slightly different for O_DIRECT IO NFS: swap-out must always use STABLE writes. serial: samsung_tty: do not unlock port->lock for uart_write_wakeup() virtio_console: eliminate anonymous module_init & module_exit jfs: prevent NULL deref in diFree SUNRPC: Fix socket waits for write buffer space parisc: Fix CPU affinity for Lasi, WAX and Dino chips parisc: Fix patch code locking and flushing mm: fix race between MADV_FREE reclaim and blkdev direct IO read KVM: arm64: Check arm64_get_bp_hardening_data() didn't return NULL drm/amdgpu: fix off by one in amdgpu_gfx_kiq_acquire() Drivers: hv: vmbus: Fix potential crash on module unload scsi: zorro7xx: Fix a resource leak in zorro7xx_remove_one() net/tls: fix slab-out-of-bounds bug in decrypt_internal net: ipv4: fix route with nexthop object delete warning net: stmmac: Fix unset max_speed difference between DT and non-DT platforms drm/imx: Fix memory leak in imx_pd_connector_get_modes bnxt_en: reserve space inside receive page for skb_shared_info IB/rdmavt: add lock to call to rvt_error_qp to prevent a race condition dpaa2-ptp: Fix refcount leak in dpaa2_ptp_probe ipv6: Fix stats accounting in ip6_pkt_drop net: openvswitch: don't send internal clone attribute to the userspace. rxrpc: fix a race in rxrpc_exit_net() qede: confirm skb is allocated before using spi: bcm-qspi: fix MSPI only access with bcm_qspi_exec_mem_op() bpf: Support dual-stack sockets in bpf_tcp_check_syncookie drbd: Fix five use after free bugs in get_initial_state SUNRPC: Handle ENOMEM in call_transmit_status() SUNRPC: Handle low memory situations in call_status() perf tools: Fix perf's libperf_print callback perf session: Remap buf if there is no space for event Revert "mmc: sdhci-xenon: fix annoying 1.8V regulator warning" mmc: renesas_sdhi: don't overwrite TAP settings when HS400 tuning is complete lz4: fix LZ4_decompress_safe_partial read out of bound mmmremap.c: avoid pointless invalidate_range_start/end on mremap(old_size=0) mm/mempolicy: fix mpol_new leak in shared_policy_replace x86/pm: Save the MSR validity status at context setup x86/speculation: Restore speculation related MSRs during S3 resume btrfs: fix qgroup reserve overflow the qgroup limit arm64: patch_text: Fixup last cpu should be master ata: sata_dwc_460ex: Fix crash due to OOB write perf: qcom_l2_pmu: fix an incorrect NULL check on list iterator irqchip/gic-v3: Fix GICR_CTLR.RWP polling tools build: Filter out options and warnings not supported by clang tools build: Use $(shell ) instead of `` to get embedded libperl's ccopts dmaengine: Revert "dmaengine: shdma: Fix runtime PM imbalance on error" mmc: mmci_sdmmc: Replace sg_dma_xxx macros mmc: mmci: stm32: correctly check all elements of sg list mm: don't skip swap entry even if zap_details specified arm64: module: remove (NOLOAD) from linker script mm/sparsemem: fix 'mem_section' will never be NULL gcc 12 warning drm/amdkfd: add missing void argument to function kgd2kfd_init drm/amdkfd: Fix -Wstrict-prototypes from amdgpu_amdkfd_gfx_10_0_get_functions() io_uring: fix fs->users overflow cgroup: Use open-time credentials for process migraton perm checks cgroup: Allocate cgroup_file_ctx for kernfs_open_file->priv cgroup: Use open-time cgroup namespace for process migration perm checks selftests: cgroup: Make cg_create() use 0755 for permission instead of 0644 selftests: cgroup: Test open-time credential usage for migration checks selftests: cgroup: Test open-time cgroup namespace usage for migration checks cpuidle: PSCI: Move the `has_lpi` check to the beginning of the function ACPI: processor idle: Check for architectural support for LPI Linux 5.4.189 Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> Change-Id: If3564fc9b0854c215e077cf29dabd4d88de266eb
1541 lines
39 KiB
C
1541 lines
39 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* processor_idle - idle state submodule to the ACPI processor driver
|
|
*
|
|
* Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
|
|
* Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
|
|
* Copyright (C) 2004, 2005 Dominik Brodowski <linux@brodo.de>
|
|
* Copyright (C) 2004 Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com>
|
|
* - Added processor hotplug support
|
|
* Copyright (C) 2005 Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>
|
|
* - Added support for C3 on SMP
|
|
*/
|
|
#define pr_fmt(fmt) "ACPI: " fmt
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/acpi.h>
|
|
#include <linux/dmi.h>
|
|
#include <linux/sched.h> /* need_resched() */
|
|
#include <linux/sort.h>
|
|
#include <linux/tick.h>
|
|
#include <linux/cpuidle.h>
|
|
#include <linux/cpu.h>
|
|
#include <acpi/processor.h>
|
|
|
|
/*
|
|
* Include the apic definitions for x86 to have the APIC timer related defines
|
|
* available also for UP (on SMP it gets magically included via linux/smp.h).
|
|
* asm/acpi.h is not an option, as it would require more include magic. Also
|
|
* creating an empty asm-ia64/apic.h would just trade pest vs. cholera.
|
|
*/
|
|
#ifdef CONFIG_X86
|
|
#include <asm/apic.h>
|
|
#endif
|
|
|
|
#define ACPI_PROCESSOR_CLASS "processor"
|
|
#define _COMPONENT ACPI_PROCESSOR_COMPONENT
|
|
ACPI_MODULE_NAME("processor_idle");
|
|
|
|
#define ACPI_IDLE_STATE_START (IS_ENABLED(CONFIG_ARCH_HAS_CPU_RELAX) ? 1 : 0)
|
|
|
|
static unsigned int max_cstate __read_mostly = ACPI_PROCESSOR_MAX_POWER;
|
|
module_param(max_cstate, uint, 0000);
|
|
static unsigned int nocst __read_mostly;
|
|
module_param(nocst, uint, 0000);
|
|
static int bm_check_disable __read_mostly;
|
|
module_param(bm_check_disable, uint, 0000);
|
|
|
|
static unsigned int latency_factor __read_mostly = 2;
|
|
module_param(latency_factor, uint, 0644);
|
|
|
|
static DEFINE_PER_CPU(struct cpuidle_device *, acpi_cpuidle_device);
|
|
|
|
struct cpuidle_driver acpi_idle_driver = {
|
|
.name = "acpi_idle",
|
|
.owner = THIS_MODULE,
|
|
};
|
|
|
|
#ifdef CONFIG_ACPI_PROCESSOR_CSTATE
|
|
static
|
|
DEFINE_PER_CPU(struct acpi_processor_cx * [CPUIDLE_STATE_MAX], acpi_cstate);
|
|
|
|
static int disabled_by_idle_boot_param(void)
|
|
{
|
|
return boot_option_idle_override == IDLE_POLL ||
|
|
boot_option_idle_override == IDLE_HALT;
|
|
}
|
|
|
|
/*
|
|
* IBM ThinkPad R40e crashes mysteriously when going into C2 or C3.
|
|
* For now disable this. Probably a bug somewhere else.
|
|
*
|
|
* To skip this limit, boot/load with a large max_cstate limit.
|
|
*/
|
|
static int set_max_cstate(const struct dmi_system_id *id)
|
|
{
|
|
if (max_cstate > ACPI_PROCESSOR_MAX_POWER)
|
|
return 0;
|
|
|
|
pr_notice("%s detected - limiting to C%ld max_cstate."
|
|
" Override with \"processor.max_cstate=%d\"\n", id->ident,
|
|
(long)id->driver_data, ACPI_PROCESSOR_MAX_POWER + 1);
|
|
|
|
max_cstate = (long)id->driver_data;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct dmi_system_id processor_power_dmi_table[] = {
|
|
{ set_max_cstate, "Clevo 5600D", {
|
|
DMI_MATCH(DMI_BIOS_VENDOR,"Phoenix Technologies LTD"),
|
|
DMI_MATCH(DMI_BIOS_VERSION,"SHE845M0.86C.0013.D.0302131307")},
|
|
(void *)2},
|
|
{ set_max_cstate, "Pavilion zv5000", {
|
|
DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
|
|
DMI_MATCH(DMI_PRODUCT_NAME,"Pavilion zv5000 (DS502A#ABA)")},
|
|
(void *)1},
|
|
{ set_max_cstate, "Asus L8400B", {
|
|
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
|
|
DMI_MATCH(DMI_PRODUCT_NAME,"L8400B series Notebook PC")},
|
|
(void *)1},
|
|
{},
|
|
};
|
|
|
|
|
|
/*
|
|
* Callers should disable interrupts before the call and enable
|
|
* interrupts after return.
|
|
*/
|
|
static void __cpuidle acpi_safe_halt(void)
|
|
{
|
|
if (!tif_need_resched()) {
|
|
safe_halt();
|
|
local_irq_disable();
|
|
}
|
|
}
|
|
|
|
#ifdef ARCH_APICTIMER_STOPS_ON_C3
|
|
|
|
/*
|
|
* Some BIOS implementations switch to C3 in the published C2 state.
|
|
* This seems to be a common problem on AMD boxen, but other vendors
|
|
* are affected too. We pick the most conservative approach: we assume
|
|
* that the local APIC stops in both C2 and C3.
|
|
*/
|
|
static void lapic_timer_check_state(int state, struct acpi_processor *pr,
|
|
struct acpi_processor_cx *cx)
|
|
{
|
|
struct acpi_processor_power *pwr = &pr->power;
|
|
u8 type = local_apic_timer_c2_ok ? ACPI_STATE_C3 : ACPI_STATE_C2;
|
|
|
|
if (cpu_has(&cpu_data(pr->id), X86_FEATURE_ARAT))
|
|
return;
|
|
|
|
if (boot_cpu_has_bug(X86_BUG_AMD_APIC_C1E))
|
|
type = ACPI_STATE_C1;
|
|
|
|
/*
|
|
* Check, if one of the previous states already marked the lapic
|
|
* unstable
|
|
*/
|
|
if (pwr->timer_broadcast_on_state < state)
|
|
return;
|
|
|
|
if (cx->type >= type)
|
|
pr->power.timer_broadcast_on_state = state;
|
|
}
|
|
|
|
static void __lapic_timer_propagate_broadcast(void *arg)
|
|
{
|
|
struct acpi_processor *pr = (struct acpi_processor *) arg;
|
|
|
|
if (pr->power.timer_broadcast_on_state < INT_MAX)
|
|
tick_broadcast_enable();
|
|
else
|
|
tick_broadcast_disable();
|
|
}
|
|
|
|
static void lapic_timer_propagate_broadcast(struct acpi_processor *pr)
|
|
{
|
|
smp_call_function_single(pr->id, __lapic_timer_propagate_broadcast,
|
|
(void *)pr, 1);
|
|
}
|
|
|
|
/* Power(C) State timer broadcast control */
|
|
static void lapic_timer_state_broadcast(struct acpi_processor *pr,
|
|
struct acpi_processor_cx *cx,
|
|
int broadcast)
|
|
{
|
|
int state = cx - pr->power.states;
|
|
|
|
if (state >= pr->power.timer_broadcast_on_state) {
|
|
if (broadcast)
|
|
tick_broadcast_enter();
|
|
else
|
|
tick_broadcast_exit();
|
|
}
|
|
}
|
|
|
|
#else
|
|
|
|
static void lapic_timer_check_state(int state, struct acpi_processor *pr,
|
|
struct acpi_processor_cx *cstate) { }
|
|
static void lapic_timer_propagate_broadcast(struct acpi_processor *pr) { }
|
|
static void lapic_timer_state_broadcast(struct acpi_processor *pr,
|
|
struct acpi_processor_cx *cx,
|
|
int broadcast)
|
|
{
|
|
}
|
|
|
|
#endif
|
|
|
|
#if defined(CONFIG_X86)
|
|
static void tsc_check_state(int state)
|
|
{
|
|
switch (boot_cpu_data.x86_vendor) {
|
|
case X86_VENDOR_HYGON:
|
|
case X86_VENDOR_AMD:
|
|
case X86_VENDOR_INTEL:
|
|
case X86_VENDOR_CENTAUR:
|
|
case X86_VENDOR_ZHAOXIN:
|
|
/*
|
|
* AMD Fam10h TSC will tick in all
|
|
* C/P/S0/S1 states when this bit is set.
|
|
*/
|
|
if (boot_cpu_has(X86_FEATURE_NONSTOP_TSC))
|
|
return;
|
|
|
|
/*FALL THROUGH*/
|
|
default:
|
|
/* TSC could halt in idle, so notify users */
|
|
if (state > ACPI_STATE_C1)
|
|
mark_tsc_unstable("TSC halts in idle");
|
|
}
|
|
}
|
|
#else
|
|
static void tsc_check_state(int state) { return; }
|
|
#endif
|
|
|
|
static int acpi_processor_get_power_info_fadt(struct acpi_processor *pr)
|
|
{
|
|
|
|
if (!pr->pblk)
|
|
return -ENODEV;
|
|
|
|
/* if info is obtained from pblk/fadt, type equals state */
|
|
pr->power.states[ACPI_STATE_C2].type = ACPI_STATE_C2;
|
|
pr->power.states[ACPI_STATE_C3].type = ACPI_STATE_C3;
|
|
|
|
#ifndef CONFIG_HOTPLUG_CPU
|
|
/*
|
|
* Check for P_LVL2_UP flag before entering C2 and above on
|
|
* an SMP system.
|
|
*/
|
|
if ((num_online_cpus() > 1) &&
|
|
!(acpi_gbl_FADT.flags & ACPI_FADT_C2_MP_SUPPORTED))
|
|
return -ENODEV;
|
|
#endif
|
|
|
|
/* determine C2 and C3 address from pblk */
|
|
pr->power.states[ACPI_STATE_C2].address = pr->pblk + 4;
|
|
pr->power.states[ACPI_STATE_C3].address = pr->pblk + 5;
|
|
|
|
/* determine latencies from FADT */
|
|
pr->power.states[ACPI_STATE_C2].latency = acpi_gbl_FADT.c2_latency;
|
|
pr->power.states[ACPI_STATE_C3].latency = acpi_gbl_FADT.c3_latency;
|
|
|
|
/*
|
|
* FADT specified C2 latency must be less than or equal to
|
|
* 100 microseconds.
|
|
*/
|
|
if (acpi_gbl_FADT.c2_latency > ACPI_PROCESSOR_MAX_C2_LATENCY) {
|
|
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
|
|
"C2 latency too large [%d]\n", acpi_gbl_FADT.c2_latency));
|
|
/* invalidate C2 */
|
|
pr->power.states[ACPI_STATE_C2].address = 0;
|
|
}
|
|
|
|
/*
|
|
* FADT supplied C3 latency must be less than or equal to
|
|
* 1000 microseconds.
|
|
*/
|
|
if (acpi_gbl_FADT.c3_latency > ACPI_PROCESSOR_MAX_C3_LATENCY) {
|
|
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
|
|
"C3 latency too large [%d]\n", acpi_gbl_FADT.c3_latency));
|
|
/* invalidate C3 */
|
|
pr->power.states[ACPI_STATE_C3].address = 0;
|
|
}
|
|
|
|
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
|
|
"lvl2[0x%08x] lvl3[0x%08x]\n",
|
|
pr->power.states[ACPI_STATE_C2].address,
|
|
pr->power.states[ACPI_STATE_C3].address));
|
|
|
|
snprintf(pr->power.states[ACPI_STATE_C2].desc,
|
|
ACPI_CX_DESC_LEN, "ACPI P_LVL2 IOPORT 0x%x",
|
|
pr->power.states[ACPI_STATE_C2].address);
|
|
snprintf(pr->power.states[ACPI_STATE_C3].desc,
|
|
ACPI_CX_DESC_LEN, "ACPI P_LVL3 IOPORT 0x%x",
|
|
pr->power.states[ACPI_STATE_C3].address);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int acpi_processor_get_power_info_default(struct acpi_processor *pr)
|
|
{
|
|
if (!pr->power.states[ACPI_STATE_C1].valid) {
|
|
/* set the first C-State to C1 */
|
|
/* all processors need to support C1 */
|
|
pr->power.states[ACPI_STATE_C1].type = ACPI_STATE_C1;
|
|
pr->power.states[ACPI_STATE_C1].valid = 1;
|
|
pr->power.states[ACPI_STATE_C1].entry_method = ACPI_CSTATE_HALT;
|
|
|
|
snprintf(pr->power.states[ACPI_STATE_C1].desc,
|
|
ACPI_CX_DESC_LEN, "ACPI HLT");
|
|
}
|
|
/* the C0 state only exists as a filler in our array */
|
|
pr->power.states[ACPI_STATE_C0].valid = 1;
|
|
return 0;
|
|
}
|
|
|
|
static int acpi_processor_get_power_info_cst(struct acpi_processor *pr)
|
|
{
|
|
acpi_status status;
|
|
u64 count;
|
|
int current_count;
|
|
int i, ret = 0;
|
|
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
|
|
union acpi_object *cst;
|
|
|
|
if (nocst)
|
|
return -ENODEV;
|
|
|
|
current_count = 0;
|
|
|
|
status = acpi_evaluate_object(pr->handle, "_CST", NULL, &buffer);
|
|
if (ACPI_FAILURE(status)) {
|
|
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No _CST, giving up\n"));
|
|
return -ENODEV;
|
|
}
|
|
|
|
cst = buffer.pointer;
|
|
|
|
/* There must be at least 2 elements */
|
|
if (!cst || (cst->type != ACPI_TYPE_PACKAGE) || cst->package.count < 2) {
|
|
pr_err("not enough elements in _CST\n");
|
|
ret = -EFAULT;
|
|
goto end;
|
|
}
|
|
|
|
count = cst->package.elements[0].integer.value;
|
|
|
|
/* Validate number of power states. */
|
|
if (count < 1 || count != cst->package.count - 1) {
|
|
pr_err("count given by _CST is not valid\n");
|
|
ret = -EFAULT;
|
|
goto end;
|
|
}
|
|
|
|
/* Tell driver that at least _CST is supported. */
|
|
pr->flags.has_cst = 1;
|
|
|
|
for (i = 1; i <= count; i++) {
|
|
union acpi_object *element;
|
|
union acpi_object *obj;
|
|
struct acpi_power_register *reg;
|
|
struct acpi_processor_cx cx;
|
|
|
|
memset(&cx, 0, sizeof(cx));
|
|
|
|
element = &(cst->package.elements[i]);
|
|
if (element->type != ACPI_TYPE_PACKAGE)
|
|
continue;
|
|
|
|
if (element->package.count != 4)
|
|
continue;
|
|
|
|
obj = &(element->package.elements[0]);
|
|
|
|
if (obj->type != ACPI_TYPE_BUFFER)
|
|
continue;
|
|
|
|
reg = (struct acpi_power_register *)obj->buffer.pointer;
|
|
|
|
if (reg->space_id != ACPI_ADR_SPACE_SYSTEM_IO &&
|
|
(reg->space_id != ACPI_ADR_SPACE_FIXED_HARDWARE))
|
|
continue;
|
|
|
|
/* There should be an easy way to extract an integer... */
|
|
obj = &(element->package.elements[1]);
|
|
if (obj->type != ACPI_TYPE_INTEGER)
|
|
continue;
|
|
|
|
cx.type = obj->integer.value;
|
|
/*
|
|
* Some buggy BIOSes won't list C1 in _CST -
|
|
* Let acpi_processor_get_power_info_default() handle them later
|
|
*/
|
|
if (i == 1 && cx.type != ACPI_STATE_C1)
|
|
current_count++;
|
|
|
|
cx.address = reg->address;
|
|
cx.index = current_count + 1;
|
|
|
|
cx.entry_method = ACPI_CSTATE_SYSTEMIO;
|
|
if (reg->space_id == ACPI_ADR_SPACE_FIXED_HARDWARE) {
|
|
if (acpi_processor_ffh_cstate_probe
|
|
(pr->id, &cx, reg) == 0) {
|
|
cx.entry_method = ACPI_CSTATE_FFH;
|
|
} else if (cx.type == ACPI_STATE_C1) {
|
|
/*
|
|
* C1 is a special case where FIXED_HARDWARE
|
|
* can be handled in non-MWAIT way as well.
|
|
* In that case, save this _CST entry info.
|
|
* Otherwise, ignore this info and continue.
|
|
*/
|
|
cx.entry_method = ACPI_CSTATE_HALT;
|
|
snprintf(cx.desc, ACPI_CX_DESC_LEN, "ACPI HLT");
|
|
} else {
|
|
continue;
|
|
}
|
|
if (cx.type == ACPI_STATE_C1 &&
|
|
(boot_option_idle_override == IDLE_NOMWAIT)) {
|
|
/*
|
|
* In most cases the C1 space_id obtained from
|
|
* _CST object is FIXED_HARDWARE access mode.
|
|
* But when the option of idle=halt is added,
|
|
* the entry_method type should be changed from
|
|
* CSTATE_FFH to CSTATE_HALT.
|
|
* When the option of idle=nomwait is added,
|
|
* the C1 entry_method type should be
|
|
* CSTATE_HALT.
|
|
*/
|
|
cx.entry_method = ACPI_CSTATE_HALT;
|
|
snprintf(cx.desc, ACPI_CX_DESC_LEN, "ACPI HLT");
|
|
}
|
|
} else {
|
|
snprintf(cx.desc, ACPI_CX_DESC_LEN, "ACPI IOPORT 0x%x",
|
|
cx.address);
|
|
}
|
|
|
|
if (cx.type == ACPI_STATE_C1) {
|
|
cx.valid = 1;
|
|
}
|
|
|
|
obj = &(element->package.elements[2]);
|
|
if (obj->type != ACPI_TYPE_INTEGER)
|
|
continue;
|
|
|
|
cx.latency = obj->integer.value;
|
|
|
|
obj = &(element->package.elements[3]);
|
|
if (obj->type != ACPI_TYPE_INTEGER)
|
|
continue;
|
|
|
|
current_count++;
|
|
memcpy(&(pr->power.states[current_count]), &cx, sizeof(cx));
|
|
|
|
/*
|
|
* We support total ACPI_PROCESSOR_MAX_POWER - 1
|
|
* (From 1 through ACPI_PROCESSOR_MAX_POWER - 1)
|
|
*/
|
|
if (current_count >= (ACPI_PROCESSOR_MAX_POWER - 1)) {
|
|
pr_warn("Limiting number of power states to max (%d)\n",
|
|
ACPI_PROCESSOR_MAX_POWER);
|
|
pr_warn("Please increase ACPI_PROCESSOR_MAX_POWER if needed.\n");
|
|
break;
|
|
}
|
|
}
|
|
|
|
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found %d power states\n",
|
|
current_count));
|
|
|
|
/* Validate number of power states discovered */
|
|
if (current_count < 2)
|
|
ret = -EFAULT;
|
|
|
|
end:
|
|
kfree(buffer.pointer);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void acpi_processor_power_verify_c3(struct acpi_processor *pr,
|
|
struct acpi_processor_cx *cx)
|
|
{
|
|
static int bm_check_flag = -1;
|
|
static int bm_control_flag = -1;
|
|
|
|
|
|
if (!cx->address)
|
|
return;
|
|
|
|
/*
|
|
* PIIX4 Erratum #18: We don't support C3 when Type-F (fast)
|
|
* DMA transfers are used by any ISA device to avoid livelock.
|
|
* Note that we could disable Type-F DMA (as recommended by
|
|
* the erratum), but this is known to disrupt certain ISA
|
|
* devices thus we take the conservative approach.
|
|
*/
|
|
else if (errata.piix4.fdma) {
|
|
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
|
|
"C3 not supported on PIIX4 with Type-F DMA\n"));
|
|
return;
|
|
}
|
|
|
|
/* All the logic here assumes flags.bm_check is same across all CPUs */
|
|
if (bm_check_flag == -1) {
|
|
/* Determine whether bm_check is needed based on CPU */
|
|
acpi_processor_power_init_bm_check(&(pr->flags), pr->id);
|
|
bm_check_flag = pr->flags.bm_check;
|
|
bm_control_flag = pr->flags.bm_control;
|
|
} else {
|
|
pr->flags.bm_check = bm_check_flag;
|
|
pr->flags.bm_control = bm_control_flag;
|
|
}
|
|
|
|
if (pr->flags.bm_check) {
|
|
if (!pr->flags.bm_control) {
|
|
if (pr->flags.has_cst != 1) {
|
|
/* bus mastering control is necessary */
|
|
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
|
|
"C3 support requires BM control\n"));
|
|
return;
|
|
} else {
|
|
/* Here we enter C3 without bus mastering */
|
|
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
|
|
"C3 support without BM control\n"));
|
|
}
|
|
}
|
|
} else {
|
|
/*
|
|
* WBINVD should be set in fadt, for C3 state to be
|
|
* supported on when bm_check is not required.
|
|
*/
|
|
if (!(acpi_gbl_FADT.flags & ACPI_FADT_WBINVD)) {
|
|
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
|
|
"Cache invalidation should work properly"
|
|
" for C3 to be enabled on SMP systems\n"));
|
|
return;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Otherwise we've met all of our C3 requirements.
|
|
* Normalize the C3 latency to expidite policy. Enable
|
|
* checking of bus mastering status (bm_check) so we can
|
|
* use this in our C3 policy
|
|
*/
|
|
cx->valid = 1;
|
|
|
|
/*
|
|
* On older chipsets, BM_RLD needs to be set
|
|
* in order for Bus Master activity to wake the
|
|
* system from C3. Newer chipsets handle DMA
|
|
* during C3 automatically and BM_RLD is a NOP.
|
|
* In either case, the proper way to
|
|
* handle BM_RLD is to set it and leave it set.
|
|
*/
|
|
acpi_write_bit_register(ACPI_BITREG_BUS_MASTER_RLD, 1);
|
|
|
|
return;
|
|
}
|
|
|
|
static int acpi_cst_latency_cmp(const void *a, const void *b)
|
|
{
|
|
const struct acpi_processor_cx *x = a, *y = b;
|
|
|
|
if (!(x->valid && y->valid))
|
|
return 0;
|
|
if (x->latency > y->latency)
|
|
return 1;
|
|
if (x->latency < y->latency)
|
|
return -1;
|
|
return 0;
|
|
}
|
|
static void acpi_cst_latency_swap(void *a, void *b, int n)
|
|
{
|
|
struct acpi_processor_cx *x = a, *y = b;
|
|
u32 tmp;
|
|
|
|
if (!(x->valid && y->valid))
|
|
return;
|
|
tmp = x->latency;
|
|
x->latency = y->latency;
|
|
y->latency = tmp;
|
|
}
|
|
|
|
static int acpi_processor_power_verify(struct acpi_processor *pr)
|
|
{
|
|
unsigned int i;
|
|
unsigned int working = 0;
|
|
unsigned int last_latency = 0;
|
|
unsigned int last_type = 0;
|
|
bool buggy_latency = false;
|
|
|
|
pr->power.timer_broadcast_on_state = INT_MAX;
|
|
|
|
for (i = 1; i < ACPI_PROCESSOR_MAX_POWER && i <= max_cstate; i++) {
|
|
struct acpi_processor_cx *cx = &pr->power.states[i];
|
|
|
|
switch (cx->type) {
|
|
case ACPI_STATE_C1:
|
|
cx->valid = 1;
|
|
break;
|
|
|
|
case ACPI_STATE_C2:
|
|
if (!cx->address)
|
|
break;
|
|
cx->valid = 1;
|
|
break;
|
|
|
|
case ACPI_STATE_C3:
|
|
acpi_processor_power_verify_c3(pr, cx);
|
|
break;
|
|
}
|
|
if (!cx->valid)
|
|
continue;
|
|
if (cx->type >= last_type && cx->latency < last_latency)
|
|
buggy_latency = true;
|
|
last_latency = cx->latency;
|
|
last_type = cx->type;
|
|
|
|
lapic_timer_check_state(i, pr, cx);
|
|
tsc_check_state(cx->type);
|
|
working++;
|
|
}
|
|
|
|
if (buggy_latency) {
|
|
pr_notice("FW issue: working around C-state latencies out of order\n");
|
|
sort(&pr->power.states[1], max_cstate,
|
|
sizeof(struct acpi_processor_cx),
|
|
acpi_cst_latency_cmp,
|
|
acpi_cst_latency_swap);
|
|
}
|
|
|
|
lapic_timer_propagate_broadcast(pr);
|
|
|
|
return (working);
|
|
}
|
|
|
|
static int acpi_processor_get_cstate_info(struct acpi_processor *pr)
|
|
{
|
|
unsigned int i;
|
|
int result;
|
|
|
|
|
|
/* NOTE: the idle thread may not be running while calling
|
|
* this function */
|
|
|
|
/* Zero initialize all the C-states info. */
|
|
memset(pr->power.states, 0, sizeof(pr->power.states));
|
|
|
|
result = acpi_processor_get_power_info_cst(pr);
|
|
if (result == -ENODEV)
|
|
result = acpi_processor_get_power_info_fadt(pr);
|
|
|
|
if (result)
|
|
return result;
|
|
|
|
acpi_processor_get_power_info_default(pr);
|
|
|
|
pr->power.count = acpi_processor_power_verify(pr);
|
|
|
|
/*
|
|
* if one state of type C2 or C3 is available, mark this
|
|
* CPU as being "idle manageable"
|
|
*/
|
|
for (i = 1; i < ACPI_PROCESSOR_MAX_POWER; i++) {
|
|
if (pr->power.states[i].valid) {
|
|
pr->power.count = i;
|
|
if (pr->power.states[i].type >= ACPI_STATE_C2)
|
|
pr->flags.power = 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* acpi_idle_bm_check - checks if bus master activity was detected
|
|
*/
|
|
static int acpi_idle_bm_check(void)
|
|
{
|
|
u32 bm_status = 0;
|
|
|
|
if (bm_check_disable)
|
|
return 0;
|
|
|
|
acpi_read_bit_register(ACPI_BITREG_BUS_MASTER_STATUS, &bm_status);
|
|
if (bm_status)
|
|
acpi_write_bit_register(ACPI_BITREG_BUS_MASTER_STATUS, 1);
|
|
/*
|
|
* PIIX4 Erratum #18: Note that BM_STS doesn't always reflect
|
|
* the true state of bus mastering activity; forcing us to
|
|
* manually check the BMIDEA bit of each IDE channel.
|
|
*/
|
|
else if (errata.piix4.bmisx) {
|
|
if ((inb_p(errata.piix4.bmisx + 0x02) & 0x01)
|
|
|| (inb_p(errata.piix4.bmisx + 0x0A) & 0x01))
|
|
bm_status = 1;
|
|
}
|
|
return bm_status;
|
|
}
|
|
|
|
/**
|
|
* acpi_idle_do_entry - enter idle state using the appropriate method
|
|
* @cx: cstate data
|
|
*
|
|
* Caller disables interrupt before call and enables interrupt after return.
|
|
*/
|
|
static void __cpuidle acpi_idle_do_entry(struct acpi_processor_cx *cx)
|
|
{
|
|
if (cx->entry_method == ACPI_CSTATE_FFH) {
|
|
/* Call into architectural FFH based C-state */
|
|
acpi_processor_ffh_cstate_enter(cx);
|
|
} else if (cx->entry_method == ACPI_CSTATE_HALT) {
|
|
acpi_safe_halt();
|
|
} else {
|
|
/* IO port based C-state */
|
|
inb(cx->address);
|
|
/* Dummy wait op - must do something useless after P_LVL2 read
|
|
because chipsets cannot guarantee that STPCLK# signal
|
|
gets asserted in time to freeze execution properly. */
|
|
inl(acpi_gbl_FADT.xpm_timer_block.address);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* acpi_idle_play_dead - enters an ACPI state for long-term idle (i.e. off-lining)
|
|
* @dev: the target CPU
|
|
* @index: the index of suggested state
|
|
*/
|
|
static int acpi_idle_play_dead(struct cpuidle_device *dev, int index)
|
|
{
|
|
struct acpi_processor_cx *cx = per_cpu(acpi_cstate[index], dev->cpu);
|
|
|
|
ACPI_FLUSH_CPU_CACHE();
|
|
|
|
while (1) {
|
|
|
|
if (cx->entry_method == ACPI_CSTATE_HALT)
|
|
safe_halt();
|
|
else if (cx->entry_method == ACPI_CSTATE_SYSTEMIO) {
|
|
inb(cx->address);
|
|
/* See comment in acpi_idle_do_entry() */
|
|
inl(acpi_gbl_FADT.xpm_timer_block.address);
|
|
} else
|
|
return -ENODEV;
|
|
}
|
|
|
|
/* Never reached */
|
|
return 0;
|
|
}
|
|
|
|
static bool acpi_idle_fallback_to_c1(struct acpi_processor *pr)
|
|
{
|
|
return IS_ENABLED(CONFIG_HOTPLUG_CPU) && !pr->flags.has_cst &&
|
|
!(acpi_gbl_FADT.flags & ACPI_FADT_C2_MP_SUPPORTED);
|
|
}
|
|
|
|
static int c3_cpu_count;
|
|
static DEFINE_RAW_SPINLOCK(c3_lock);
|
|
|
|
/**
|
|
* acpi_idle_enter_bm - enters C3 with proper BM handling
|
|
* @pr: Target processor
|
|
* @cx: Target state context
|
|
* @timer_bc: Whether or not to change timer mode to broadcast
|
|
*/
|
|
static void acpi_idle_enter_bm(struct acpi_processor *pr,
|
|
struct acpi_processor_cx *cx, bool timer_bc)
|
|
{
|
|
acpi_unlazy_tlb(smp_processor_id());
|
|
|
|
/*
|
|
* Must be done before busmaster disable as we might need to
|
|
* access HPET !
|
|
*/
|
|
if (timer_bc)
|
|
lapic_timer_state_broadcast(pr, cx, 1);
|
|
|
|
/*
|
|
* disable bus master
|
|
* bm_check implies we need ARB_DIS
|
|
* bm_control implies whether we can do ARB_DIS
|
|
*
|
|
* That leaves a case where bm_check is set and bm_control is
|
|
* not set. In that case we cannot do much, we enter C3
|
|
* without doing anything.
|
|
*/
|
|
if (pr->flags.bm_control) {
|
|
raw_spin_lock(&c3_lock);
|
|
c3_cpu_count++;
|
|
/* Disable bus master arbitration when all CPUs are in C3 */
|
|
if (c3_cpu_count == num_online_cpus())
|
|
acpi_write_bit_register(ACPI_BITREG_ARB_DISABLE, 1);
|
|
raw_spin_unlock(&c3_lock);
|
|
}
|
|
|
|
acpi_idle_do_entry(cx);
|
|
|
|
/* Re-enable bus master arbitration */
|
|
if (pr->flags.bm_control) {
|
|
raw_spin_lock(&c3_lock);
|
|
acpi_write_bit_register(ACPI_BITREG_ARB_DISABLE, 0);
|
|
c3_cpu_count--;
|
|
raw_spin_unlock(&c3_lock);
|
|
}
|
|
|
|
if (timer_bc)
|
|
lapic_timer_state_broadcast(pr, cx, 0);
|
|
}
|
|
|
|
static int acpi_idle_enter(struct cpuidle_device *dev,
|
|
struct cpuidle_driver *drv, int index)
|
|
{
|
|
struct acpi_processor_cx *cx = per_cpu(acpi_cstate[index], dev->cpu);
|
|
struct acpi_processor *pr;
|
|
|
|
pr = __this_cpu_read(processors);
|
|
if (unlikely(!pr))
|
|
return -EINVAL;
|
|
|
|
if (cx->type != ACPI_STATE_C1) {
|
|
if (acpi_idle_fallback_to_c1(pr) && num_online_cpus() > 1) {
|
|
index = ACPI_IDLE_STATE_START;
|
|
cx = per_cpu(acpi_cstate[index], dev->cpu);
|
|
} else if (cx->type == ACPI_STATE_C3 && pr->flags.bm_check) {
|
|
if (cx->bm_sts_skip || !acpi_idle_bm_check()) {
|
|
acpi_idle_enter_bm(pr, cx, true);
|
|
return index;
|
|
} else if (drv->safe_state_index >= 0) {
|
|
index = drv->safe_state_index;
|
|
cx = per_cpu(acpi_cstate[index], dev->cpu);
|
|
} else {
|
|
acpi_safe_halt();
|
|
return -EBUSY;
|
|
}
|
|
}
|
|
}
|
|
|
|
lapic_timer_state_broadcast(pr, cx, 1);
|
|
|
|
if (cx->type == ACPI_STATE_C3)
|
|
ACPI_FLUSH_CPU_CACHE();
|
|
|
|
acpi_idle_do_entry(cx);
|
|
|
|
lapic_timer_state_broadcast(pr, cx, 0);
|
|
|
|
return index;
|
|
}
|
|
|
|
static int acpi_idle_enter_s2idle(struct cpuidle_device *dev,
|
|
struct cpuidle_driver *drv, int index)
|
|
{
|
|
struct acpi_processor_cx *cx = per_cpu(acpi_cstate[index], dev->cpu);
|
|
|
|
if (cx->type == ACPI_STATE_C3) {
|
|
struct acpi_processor *pr = __this_cpu_read(processors);
|
|
|
|
if (unlikely(!pr))
|
|
return 0;
|
|
|
|
if (pr->flags.bm_check) {
|
|
acpi_idle_enter_bm(pr, cx, false);
|
|
return 0;
|
|
} else {
|
|
ACPI_FLUSH_CPU_CACHE();
|
|
}
|
|
}
|
|
acpi_idle_do_entry(cx);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int acpi_processor_setup_cpuidle_cx(struct acpi_processor *pr,
|
|
struct cpuidle_device *dev)
|
|
{
|
|
int i, count = ACPI_IDLE_STATE_START;
|
|
struct acpi_processor_cx *cx;
|
|
|
|
if (max_cstate == 0)
|
|
max_cstate = 1;
|
|
|
|
for (i = 1; i < ACPI_PROCESSOR_MAX_POWER && i <= max_cstate; i++) {
|
|
cx = &pr->power.states[i];
|
|
|
|
if (!cx->valid)
|
|
continue;
|
|
|
|
per_cpu(acpi_cstate[count], dev->cpu) = cx;
|
|
|
|
count++;
|
|
if (count == CPUIDLE_STATE_MAX)
|
|
break;
|
|
}
|
|
|
|
if (!count)
|
|
return -EINVAL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int acpi_processor_setup_cstates(struct acpi_processor *pr)
|
|
{
|
|
int i, count;
|
|
struct acpi_processor_cx *cx;
|
|
struct cpuidle_state *state;
|
|
struct cpuidle_driver *drv = &acpi_idle_driver;
|
|
|
|
if (max_cstate == 0)
|
|
max_cstate = 1;
|
|
|
|
if (IS_ENABLED(CONFIG_ARCH_HAS_CPU_RELAX)) {
|
|
cpuidle_poll_state_init(drv);
|
|
count = 1;
|
|
} else {
|
|
count = 0;
|
|
}
|
|
|
|
for (i = 1; i < ACPI_PROCESSOR_MAX_POWER && i <= max_cstate; i++) {
|
|
cx = &pr->power.states[i];
|
|
|
|
if (!cx->valid)
|
|
continue;
|
|
|
|
state = &drv->states[count];
|
|
snprintf(state->name, CPUIDLE_NAME_LEN, "C%d", i);
|
|
strlcpy(state->desc, cx->desc, CPUIDLE_DESC_LEN);
|
|
state->exit_latency = cx->latency;
|
|
state->target_residency = cx->latency * latency_factor;
|
|
state->enter = acpi_idle_enter;
|
|
|
|
state->flags = 0;
|
|
if (cx->type == ACPI_STATE_C1 || cx->type == ACPI_STATE_C2) {
|
|
state->enter_dead = acpi_idle_play_dead;
|
|
drv->safe_state_index = count;
|
|
}
|
|
/*
|
|
* Halt-induced C1 is not good for ->enter_s2idle, because it
|
|
* re-enables interrupts on exit. Moreover, C1 is generally not
|
|
* particularly interesting from the suspend-to-idle angle, so
|
|
* avoid C1 and the situations in which we may need to fall back
|
|
* to it altogether.
|
|
*/
|
|
if (cx->type != ACPI_STATE_C1 && !acpi_idle_fallback_to_c1(pr))
|
|
state->enter_s2idle = acpi_idle_enter_s2idle;
|
|
|
|
count++;
|
|
if (count == CPUIDLE_STATE_MAX)
|
|
break;
|
|
}
|
|
|
|
drv->state_count = count;
|
|
|
|
if (!count)
|
|
return -EINVAL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline void acpi_processor_cstate_first_run_checks(void)
|
|
{
|
|
acpi_status status;
|
|
static int first_run;
|
|
|
|
if (first_run)
|
|
return;
|
|
dmi_check_system(processor_power_dmi_table);
|
|
max_cstate = acpi_processor_cstate_check(max_cstate);
|
|
if (max_cstate < ACPI_C_STATES_MAX)
|
|
pr_notice("ACPI: processor limited to max C-state %d\n",
|
|
max_cstate);
|
|
first_run++;
|
|
|
|
if (acpi_gbl_FADT.cst_control && !nocst) {
|
|
status = acpi_os_write_port(acpi_gbl_FADT.smi_command,
|
|
acpi_gbl_FADT.cst_control, 8);
|
|
if (ACPI_FAILURE(status))
|
|
ACPI_EXCEPTION((AE_INFO, status,
|
|
"Notifying BIOS of _CST ability failed"));
|
|
}
|
|
}
|
|
#else
|
|
|
|
static inline int disabled_by_idle_boot_param(void) { return 0; }
|
|
static inline void acpi_processor_cstate_first_run_checks(void) { }
|
|
static int acpi_processor_get_cstate_info(struct acpi_processor *pr)
|
|
{
|
|
return -ENODEV;
|
|
}
|
|
|
|
static int acpi_processor_setup_cpuidle_cx(struct acpi_processor *pr,
|
|
struct cpuidle_device *dev)
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int acpi_processor_setup_cstates(struct acpi_processor *pr)
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
|
|
#endif /* CONFIG_ACPI_PROCESSOR_CSTATE */
|
|
|
|
struct acpi_lpi_states_array {
|
|
unsigned int size;
|
|
unsigned int composite_states_size;
|
|
struct acpi_lpi_state *entries;
|
|
struct acpi_lpi_state *composite_states[ACPI_PROCESSOR_MAX_POWER];
|
|
};
|
|
|
|
static int obj_get_integer(union acpi_object *obj, u32 *value)
|
|
{
|
|
if (obj->type != ACPI_TYPE_INTEGER)
|
|
return -EINVAL;
|
|
|
|
*value = obj->integer.value;
|
|
return 0;
|
|
}
|
|
|
|
static int acpi_processor_evaluate_lpi(acpi_handle handle,
|
|
struct acpi_lpi_states_array *info)
|
|
{
|
|
acpi_status status;
|
|
int ret = 0;
|
|
int pkg_count, state_idx = 1, loop;
|
|
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
|
|
union acpi_object *lpi_data;
|
|
struct acpi_lpi_state *lpi_state;
|
|
|
|
status = acpi_evaluate_object(handle, "_LPI", NULL, &buffer);
|
|
if (ACPI_FAILURE(status)) {
|
|
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No _LPI, giving up\n"));
|
|
return -ENODEV;
|
|
}
|
|
|
|
lpi_data = buffer.pointer;
|
|
|
|
/* There must be at least 4 elements = 3 elements + 1 package */
|
|
if (!lpi_data || lpi_data->type != ACPI_TYPE_PACKAGE ||
|
|
lpi_data->package.count < 4) {
|
|
pr_debug("not enough elements in _LPI\n");
|
|
ret = -ENODATA;
|
|
goto end;
|
|
}
|
|
|
|
pkg_count = lpi_data->package.elements[2].integer.value;
|
|
|
|
/* Validate number of power states. */
|
|
if (pkg_count < 1 || pkg_count != lpi_data->package.count - 3) {
|
|
pr_debug("count given by _LPI is not valid\n");
|
|
ret = -ENODATA;
|
|
goto end;
|
|
}
|
|
|
|
lpi_state = kcalloc(pkg_count, sizeof(*lpi_state), GFP_KERNEL);
|
|
if (!lpi_state) {
|
|
ret = -ENOMEM;
|
|
goto end;
|
|
}
|
|
|
|
info->size = pkg_count;
|
|
info->entries = lpi_state;
|
|
|
|
/* LPI States start at index 3 */
|
|
for (loop = 3; state_idx <= pkg_count; loop++, state_idx++, lpi_state++) {
|
|
union acpi_object *element, *pkg_elem, *obj;
|
|
|
|
element = &lpi_data->package.elements[loop];
|
|
if (element->type != ACPI_TYPE_PACKAGE || element->package.count < 7)
|
|
continue;
|
|
|
|
pkg_elem = element->package.elements;
|
|
|
|
obj = pkg_elem + 6;
|
|
if (obj->type == ACPI_TYPE_BUFFER) {
|
|
struct acpi_power_register *reg;
|
|
|
|
reg = (struct acpi_power_register *)obj->buffer.pointer;
|
|
if (reg->space_id != ACPI_ADR_SPACE_SYSTEM_IO &&
|
|
reg->space_id != ACPI_ADR_SPACE_FIXED_HARDWARE)
|
|
continue;
|
|
|
|
lpi_state->address = reg->address;
|
|
lpi_state->entry_method =
|
|
reg->space_id == ACPI_ADR_SPACE_FIXED_HARDWARE ?
|
|
ACPI_CSTATE_FFH : ACPI_CSTATE_SYSTEMIO;
|
|
} else if (obj->type == ACPI_TYPE_INTEGER) {
|
|
lpi_state->entry_method = ACPI_CSTATE_INTEGER;
|
|
lpi_state->address = obj->integer.value;
|
|
} else {
|
|
continue;
|
|
}
|
|
|
|
/* elements[7,8] skipped for now i.e. Residency/Usage counter*/
|
|
|
|
obj = pkg_elem + 9;
|
|
if (obj->type == ACPI_TYPE_STRING)
|
|
strlcpy(lpi_state->desc, obj->string.pointer,
|
|
ACPI_CX_DESC_LEN);
|
|
|
|
lpi_state->index = state_idx;
|
|
if (obj_get_integer(pkg_elem + 0, &lpi_state->min_residency)) {
|
|
pr_debug("No min. residency found, assuming 10 us\n");
|
|
lpi_state->min_residency = 10;
|
|
}
|
|
|
|
if (obj_get_integer(pkg_elem + 1, &lpi_state->wake_latency)) {
|
|
pr_debug("No wakeup residency found, assuming 10 us\n");
|
|
lpi_state->wake_latency = 10;
|
|
}
|
|
|
|
if (obj_get_integer(pkg_elem + 2, &lpi_state->flags))
|
|
lpi_state->flags = 0;
|
|
|
|
if (obj_get_integer(pkg_elem + 3, &lpi_state->arch_flags))
|
|
lpi_state->arch_flags = 0;
|
|
|
|
if (obj_get_integer(pkg_elem + 4, &lpi_state->res_cnt_freq))
|
|
lpi_state->res_cnt_freq = 1;
|
|
|
|
if (obj_get_integer(pkg_elem + 5, &lpi_state->enable_parent_state))
|
|
lpi_state->enable_parent_state = 0;
|
|
}
|
|
|
|
acpi_handle_debug(handle, "Found %d power states\n", state_idx);
|
|
end:
|
|
kfree(buffer.pointer);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* flat_state_cnt - the number of composite LPI states after the process of flattening
|
|
*/
|
|
static int flat_state_cnt;
|
|
|
|
/**
|
|
* combine_lpi_states - combine local and parent LPI states to form a composite LPI state
|
|
*
|
|
* @local: local LPI state
|
|
* @parent: parent LPI state
|
|
* @result: composite LPI state
|
|
*/
|
|
static bool combine_lpi_states(struct acpi_lpi_state *local,
|
|
struct acpi_lpi_state *parent,
|
|
struct acpi_lpi_state *result)
|
|
{
|
|
if (parent->entry_method == ACPI_CSTATE_INTEGER) {
|
|
if (!parent->address) /* 0 means autopromotable */
|
|
return false;
|
|
result->address = local->address + parent->address;
|
|
} else {
|
|
result->address = parent->address;
|
|
}
|
|
|
|
result->min_residency = max(local->min_residency, parent->min_residency);
|
|
result->wake_latency = local->wake_latency + parent->wake_latency;
|
|
result->enable_parent_state = parent->enable_parent_state;
|
|
result->entry_method = local->entry_method;
|
|
|
|
result->flags = parent->flags;
|
|
result->arch_flags = parent->arch_flags;
|
|
result->index = parent->index;
|
|
|
|
strlcpy(result->desc, local->desc, ACPI_CX_DESC_LEN);
|
|
strlcat(result->desc, "+", ACPI_CX_DESC_LEN);
|
|
strlcat(result->desc, parent->desc, ACPI_CX_DESC_LEN);
|
|
return true;
|
|
}
|
|
|
|
#define ACPI_LPI_STATE_FLAGS_ENABLED BIT(0)
|
|
|
|
static void stash_composite_state(struct acpi_lpi_states_array *curr_level,
|
|
struct acpi_lpi_state *t)
|
|
{
|
|
curr_level->composite_states[curr_level->composite_states_size++] = t;
|
|
}
|
|
|
|
static int flatten_lpi_states(struct acpi_processor *pr,
|
|
struct acpi_lpi_states_array *curr_level,
|
|
struct acpi_lpi_states_array *prev_level)
|
|
{
|
|
int i, j, state_count = curr_level->size;
|
|
struct acpi_lpi_state *p, *t = curr_level->entries;
|
|
|
|
curr_level->composite_states_size = 0;
|
|
for (j = 0; j < state_count; j++, t++) {
|
|
struct acpi_lpi_state *flpi;
|
|
|
|
if (!(t->flags & ACPI_LPI_STATE_FLAGS_ENABLED))
|
|
continue;
|
|
|
|
if (flat_state_cnt >= ACPI_PROCESSOR_MAX_POWER) {
|
|
pr_warn("Limiting number of LPI states to max (%d)\n",
|
|
ACPI_PROCESSOR_MAX_POWER);
|
|
pr_warn("Please increase ACPI_PROCESSOR_MAX_POWER if needed.\n");
|
|
break;
|
|
}
|
|
|
|
flpi = &pr->power.lpi_states[flat_state_cnt];
|
|
|
|
if (!prev_level) { /* leaf/processor node */
|
|
memcpy(flpi, t, sizeof(*t));
|
|
stash_composite_state(curr_level, flpi);
|
|
flat_state_cnt++;
|
|
continue;
|
|
}
|
|
|
|
for (i = 0; i < prev_level->composite_states_size; i++) {
|
|
p = prev_level->composite_states[i];
|
|
if (t->index <= p->enable_parent_state &&
|
|
combine_lpi_states(p, t, flpi)) {
|
|
stash_composite_state(curr_level, flpi);
|
|
flat_state_cnt++;
|
|
flpi++;
|
|
}
|
|
}
|
|
}
|
|
|
|
kfree(curr_level->entries);
|
|
return 0;
|
|
}
|
|
|
|
int __weak acpi_processor_ffh_lpi_probe(unsigned int cpu)
|
|
{
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
static int acpi_processor_get_lpi_info(struct acpi_processor *pr)
|
|
{
|
|
int ret, i;
|
|
acpi_status status;
|
|
acpi_handle handle = pr->handle, pr_ahandle;
|
|
struct acpi_device *d = NULL;
|
|
struct acpi_lpi_states_array info[2], *tmp, *prev, *curr;
|
|
|
|
/* make sure our architecture has support */
|
|
ret = acpi_processor_ffh_lpi_probe(pr->id);
|
|
if (ret == -EOPNOTSUPP)
|
|
return ret;
|
|
|
|
if (!osc_pc_lpi_support_confirmed)
|
|
return -EOPNOTSUPP;
|
|
|
|
if (!acpi_has_method(handle, "_LPI"))
|
|
return -EINVAL;
|
|
|
|
flat_state_cnt = 0;
|
|
prev = &info[0];
|
|
curr = &info[1];
|
|
handle = pr->handle;
|
|
ret = acpi_processor_evaluate_lpi(handle, prev);
|
|
if (ret)
|
|
return ret;
|
|
flatten_lpi_states(pr, prev, NULL);
|
|
|
|
status = acpi_get_parent(handle, &pr_ahandle);
|
|
while (ACPI_SUCCESS(status)) {
|
|
acpi_bus_get_device(pr_ahandle, &d);
|
|
handle = pr_ahandle;
|
|
|
|
if (strcmp(acpi_device_hid(d), ACPI_PROCESSOR_CONTAINER_HID))
|
|
break;
|
|
|
|
/* can be optional ? */
|
|
if (!acpi_has_method(handle, "_LPI"))
|
|
break;
|
|
|
|
ret = acpi_processor_evaluate_lpi(handle, curr);
|
|
if (ret)
|
|
break;
|
|
|
|
/* flatten all the LPI states in this level of hierarchy */
|
|
flatten_lpi_states(pr, curr, prev);
|
|
|
|
tmp = prev, prev = curr, curr = tmp;
|
|
|
|
status = acpi_get_parent(handle, &pr_ahandle);
|
|
}
|
|
|
|
pr->power.count = flat_state_cnt;
|
|
/* reset the index after flattening */
|
|
for (i = 0; i < pr->power.count; i++)
|
|
pr->power.lpi_states[i].index = i;
|
|
|
|
/* Tell driver that _LPI is supported. */
|
|
pr->flags.has_lpi = 1;
|
|
pr->flags.power = 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int __weak acpi_processor_ffh_lpi_enter(struct acpi_lpi_state *lpi)
|
|
{
|
|
return -ENODEV;
|
|
}
|
|
|
|
/**
|
|
* acpi_idle_lpi_enter - enters an ACPI any LPI state
|
|
* @dev: the target CPU
|
|
* @drv: cpuidle driver containing cpuidle state info
|
|
* @index: index of target state
|
|
*
|
|
* Return: 0 for success or negative value for error
|
|
*/
|
|
static int acpi_idle_lpi_enter(struct cpuidle_device *dev,
|
|
struct cpuidle_driver *drv, int index)
|
|
{
|
|
struct acpi_processor *pr;
|
|
struct acpi_lpi_state *lpi;
|
|
|
|
pr = __this_cpu_read(processors);
|
|
|
|
if (unlikely(!pr))
|
|
return -EINVAL;
|
|
|
|
lpi = &pr->power.lpi_states[index];
|
|
if (lpi->entry_method == ACPI_CSTATE_FFH)
|
|
return acpi_processor_ffh_lpi_enter(lpi);
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int acpi_processor_setup_lpi_states(struct acpi_processor *pr)
|
|
{
|
|
int i;
|
|
struct acpi_lpi_state *lpi;
|
|
struct cpuidle_state *state;
|
|
struct cpuidle_driver *drv = &acpi_idle_driver;
|
|
|
|
if (!pr->flags.has_lpi)
|
|
return -EOPNOTSUPP;
|
|
|
|
for (i = 0; i < pr->power.count && i < CPUIDLE_STATE_MAX; i++) {
|
|
lpi = &pr->power.lpi_states[i];
|
|
|
|
state = &drv->states[i];
|
|
snprintf(state->name, CPUIDLE_NAME_LEN, "LPI-%d", i);
|
|
strlcpy(state->desc, lpi->desc, CPUIDLE_DESC_LEN);
|
|
state->exit_latency = lpi->wake_latency;
|
|
state->target_residency = lpi->min_residency;
|
|
if (lpi->arch_flags)
|
|
state->flags |= CPUIDLE_FLAG_TIMER_STOP;
|
|
state->enter = acpi_idle_lpi_enter;
|
|
drv->safe_state_index = i;
|
|
}
|
|
|
|
drv->state_count = i;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* acpi_processor_setup_cpuidle_states- prepares and configures cpuidle
|
|
* global state data i.e. idle routines
|
|
*
|
|
* @pr: the ACPI processor
|
|
*/
|
|
static int acpi_processor_setup_cpuidle_states(struct acpi_processor *pr)
|
|
{
|
|
int i;
|
|
struct cpuidle_driver *drv = &acpi_idle_driver;
|
|
|
|
if (!pr->flags.power_setup_done || !pr->flags.power)
|
|
return -EINVAL;
|
|
|
|
drv->safe_state_index = -1;
|
|
for (i = ACPI_IDLE_STATE_START; i < CPUIDLE_STATE_MAX; i++) {
|
|
drv->states[i].name[0] = '\0';
|
|
drv->states[i].desc[0] = '\0';
|
|
}
|
|
|
|
if (pr->flags.has_lpi)
|
|
return acpi_processor_setup_lpi_states(pr);
|
|
|
|
return acpi_processor_setup_cstates(pr);
|
|
}
|
|
|
|
/**
|
|
* acpi_processor_setup_cpuidle_dev - prepares and configures CPUIDLE
|
|
* device i.e. per-cpu data
|
|
*
|
|
* @pr: the ACPI processor
|
|
* @dev : the cpuidle device
|
|
*/
|
|
static int acpi_processor_setup_cpuidle_dev(struct acpi_processor *pr,
|
|
struct cpuidle_device *dev)
|
|
{
|
|
if (!pr->flags.power_setup_done || !pr->flags.power || !dev)
|
|
return -EINVAL;
|
|
|
|
dev->cpu = pr->id;
|
|
if (pr->flags.has_lpi)
|
|
return acpi_processor_ffh_lpi_probe(pr->id);
|
|
|
|
return acpi_processor_setup_cpuidle_cx(pr, dev);
|
|
}
|
|
|
|
static int acpi_processor_get_power_info(struct acpi_processor *pr)
|
|
{
|
|
int ret;
|
|
|
|
ret = acpi_processor_get_lpi_info(pr);
|
|
if (ret)
|
|
ret = acpi_processor_get_cstate_info(pr);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int acpi_processor_hotplug(struct acpi_processor *pr)
|
|
{
|
|
int ret = 0;
|
|
struct cpuidle_device *dev;
|
|
|
|
if (disabled_by_idle_boot_param())
|
|
return 0;
|
|
|
|
if (!pr->flags.power_setup_done)
|
|
return -ENODEV;
|
|
|
|
dev = per_cpu(acpi_cpuidle_device, pr->id);
|
|
cpuidle_pause_and_lock();
|
|
cpuidle_disable_device(dev);
|
|
ret = acpi_processor_get_power_info(pr);
|
|
if (!ret && pr->flags.power) {
|
|
acpi_processor_setup_cpuidle_dev(pr, dev);
|
|
ret = cpuidle_enable_device(dev);
|
|
}
|
|
cpuidle_resume_and_unlock();
|
|
|
|
return ret;
|
|
}
|
|
|
|
int acpi_processor_power_state_has_changed(struct acpi_processor *pr)
|
|
{
|
|
int cpu;
|
|
struct acpi_processor *_pr;
|
|
struct cpuidle_device *dev;
|
|
|
|
if (disabled_by_idle_boot_param())
|
|
return 0;
|
|
|
|
if (!pr->flags.power_setup_done)
|
|
return -ENODEV;
|
|
|
|
/*
|
|
* FIXME: Design the ACPI notification to make it once per
|
|
* system instead of once per-cpu. This condition is a hack
|
|
* to make the code that updates C-States be called once.
|
|
*/
|
|
|
|
if (pr->id == 0 && cpuidle_get_driver() == &acpi_idle_driver) {
|
|
|
|
/* Protect against cpu-hotplug */
|
|
get_online_cpus();
|
|
cpuidle_pause_and_lock();
|
|
|
|
/* Disable all cpuidle devices */
|
|
for_each_online_cpu(cpu) {
|
|
_pr = per_cpu(processors, cpu);
|
|
if (!_pr || !_pr->flags.power_setup_done)
|
|
continue;
|
|
dev = per_cpu(acpi_cpuidle_device, cpu);
|
|
cpuidle_disable_device(dev);
|
|
}
|
|
|
|
/* Populate Updated C-state information */
|
|
acpi_processor_get_power_info(pr);
|
|
acpi_processor_setup_cpuidle_states(pr);
|
|
|
|
/* Enable all cpuidle devices */
|
|
for_each_online_cpu(cpu) {
|
|
_pr = per_cpu(processors, cpu);
|
|
if (!_pr || !_pr->flags.power_setup_done)
|
|
continue;
|
|
acpi_processor_get_power_info(_pr);
|
|
if (_pr->flags.power) {
|
|
dev = per_cpu(acpi_cpuidle_device, cpu);
|
|
acpi_processor_setup_cpuidle_dev(_pr, dev);
|
|
cpuidle_enable_device(dev);
|
|
}
|
|
}
|
|
cpuidle_resume_and_unlock();
|
|
put_online_cpus();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int acpi_processor_registered;
|
|
|
|
int acpi_processor_power_init(struct acpi_processor *pr)
|
|
{
|
|
int retval;
|
|
struct cpuidle_device *dev;
|
|
|
|
if (disabled_by_idle_boot_param())
|
|
return 0;
|
|
|
|
acpi_processor_cstate_first_run_checks();
|
|
|
|
if (!acpi_processor_get_power_info(pr))
|
|
pr->flags.power_setup_done = 1;
|
|
|
|
/*
|
|
* Install the idle handler if processor power management is supported.
|
|
* Note that we use previously set idle handler will be used on
|
|
* platforms that only support C1.
|
|
*/
|
|
if (pr->flags.power) {
|
|
/* Register acpi_idle_driver if not already registered */
|
|
if (!acpi_processor_registered) {
|
|
acpi_processor_setup_cpuidle_states(pr);
|
|
retval = cpuidle_register_driver(&acpi_idle_driver);
|
|
if (retval)
|
|
return retval;
|
|
pr_debug("%s registered with cpuidle\n",
|
|
acpi_idle_driver.name);
|
|
}
|
|
|
|
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
|
|
if (!dev)
|
|
return -ENOMEM;
|
|
per_cpu(acpi_cpuidle_device, pr->id) = dev;
|
|
|
|
acpi_processor_setup_cpuidle_dev(pr, dev);
|
|
|
|
/* Register per-cpu cpuidle_device. Cpuidle driver
|
|
* must already be registered before registering device
|
|
*/
|
|
retval = cpuidle_register_device(dev);
|
|
if (retval) {
|
|
if (acpi_processor_registered == 0)
|
|
cpuidle_unregister_driver(&acpi_idle_driver);
|
|
return retval;
|
|
}
|
|
acpi_processor_registered++;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int acpi_processor_power_exit(struct acpi_processor *pr)
|
|
{
|
|
struct cpuidle_device *dev = per_cpu(acpi_cpuidle_device, pr->id);
|
|
|
|
if (disabled_by_idle_boot_param())
|
|
return 0;
|
|
|
|
if (pr->flags.power) {
|
|
cpuidle_unregister_device(dev);
|
|
acpi_processor_registered--;
|
|
if (acpi_processor_registered == 0)
|
|
cpuidle_unregister_driver(&acpi_idle_driver);
|
|
}
|
|
|
|
pr->flags.power_setup_done = 0;
|
|
return 0;
|
|
}
|