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

platform/x86/amd/pmf: Add support for PMF core layer

PMF core layer is meant to abstract the common functionalities
across PMF features. This layer also does the plumbing work
like setting up the mailbox channel for the communication
between the PMF driver and the PMFW (Power Management Firmware)
running on the SMU.

Reviewed-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
Link: https://lore.kernel.org/r/20220802151149.2123699-2-Shyam-sundar.S-k@amd.com
Signed-off-by: Hans de Goede <hdegoede@redhat.com>

authored by

Shyam Sundar S K and committed by
Hans de Goede
da5ce22d 568035b0

+307
+2
drivers/platform/x86/amd/Kconfig
··· 3 3 # AMD x86 Platform Specific Drivers 4 4 # 5 5 6 + source "drivers/platform/x86/amd/pmf/Kconfig" 7 + 6 8 config AMD_PMC 7 9 tristate "AMD SoC PMC driver" 8 10 depends on ACPI && PCI && RTC_CLASS
+1
drivers/platform/x86/amd/Makefile
··· 8 8 obj-$(CONFIG_AMD_PMC) += amd-pmc.o 9 9 amd_hsmp-y := hsmp.o 10 10 obj-$(CONFIG_AMD_HSMP) += amd_hsmp.o 11 + obj-$(CONFIG_AMD_PMF) += pmf/
+15
drivers/platform/x86/amd/pmf/Kconfig
··· 1 + # SPDX-License-Identifier: GPL-2.0-only 2 + # 3 + # AMD PMF Driver 4 + # 5 + 6 + config AMD_PMF 7 + tristate "AMD Platform Management Framework" 8 + depends on ACPI && PCI 9 + help 10 + This driver provides support for the AMD Platform Management Framework. 11 + The goal is to enhance end user experience by making AMD PCs smarter, 12 + quiter, power efficient by adapting to user behavior and environment. 13 + 14 + To compile this driver as a module, choose M here: the module will 15 + be called amd_pmf.
+8
drivers/platform/x86/amd/pmf/Makefile
··· 1 + # SPDX-License-Identifier: GPL-2.0 2 + # 3 + # Makefile for linux/drivers/platform/x86/amd/pmf 4 + # AMD Platform Management Framework 5 + # 6 + 7 + obj-$(CONFIG_AMD_PMF) += amd-pmf.o 8 + amd-pmf-objs := core.o
+235
drivers/platform/x86/amd/pmf/core.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * AMD Platform Management Framework Driver 4 + * 5 + * Copyright (c) 2022, Advanced Micro Devices, Inc. 6 + * All Rights Reserved. 7 + * 8 + * Author: Shyam Sundar S K <Shyam-sundar.S-k@amd.com> 9 + */ 10 + 11 + #include <linux/iopoll.h> 12 + #include <linux/module.h> 13 + #include <linux/pci.h> 14 + #include <linux/platform_device.h> 15 + #include "pmf.h" 16 + 17 + /* PMF-SMU communication registers */ 18 + #define AMD_PMF_REGISTER_MESSAGE 0xA18 19 + #define AMD_PMF_REGISTER_RESPONSE 0xA78 20 + #define AMD_PMF_REGISTER_ARGUMENT 0xA58 21 + 22 + /* Base address of SMU for mapping physical address to virtual address */ 23 + #define AMD_PMF_SMU_INDEX_ADDRESS 0xB8 24 + #define AMD_PMF_SMU_INDEX_DATA 0xBC 25 + #define AMD_PMF_MAPPING_SIZE 0x01000 26 + #define AMD_PMF_BASE_ADDR_OFFSET 0x10000 27 + #define AMD_PMF_BASE_ADDR_LO 0x13B102E8 28 + #define AMD_PMF_BASE_ADDR_HI 0x13B102EC 29 + #define AMD_PMF_BASE_ADDR_LO_MASK GENMASK(15, 0) 30 + #define AMD_PMF_BASE_ADDR_HI_MASK GENMASK(31, 20) 31 + 32 + /* SMU Response Codes */ 33 + #define AMD_PMF_RESULT_OK 0x01 34 + #define AMD_PMF_RESULT_CMD_REJECT_BUSY 0xFC 35 + #define AMD_PMF_RESULT_CMD_REJECT_PREREQ 0xFD 36 + #define AMD_PMF_RESULT_CMD_UNKNOWN 0xFE 37 + #define AMD_PMF_RESULT_FAILED 0xFF 38 + 39 + /* List of supported CPU ids */ 40 + #define AMD_CPU_ID_PS 0x14e8 41 + 42 + #define PMF_MSG_DELAY_MIN_US 50 43 + #define RESPONSE_REGISTER_LOOP_MAX 20000 44 + 45 + #define DELAY_MIN_US 2000 46 + #define DELAY_MAX_US 3000 47 + 48 + static inline u32 amd_pmf_reg_read(struct amd_pmf_dev *dev, int reg_offset) 49 + { 50 + return ioread32(dev->regbase + reg_offset); 51 + } 52 + 53 + static inline void amd_pmf_reg_write(struct amd_pmf_dev *dev, int reg_offset, u32 val) 54 + { 55 + iowrite32(val, dev->regbase + reg_offset); 56 + } 57 + 58 + static void __maybe_unused amd_pmf_dump_registers(struct amd_pmf_dev *dev) 59 + { 60 + u32 value; 61 + 62 + value = amd_pmf_reg_read(dev, AMD_PMF_REGISTER_RESPONSE); 63 + dev_dbg(dev->dev, "AMD_PMF_REGISTER_RESPONSE:%x\n", value); 64 + 65 + value = amd_pmf_reg_read(dev, AMD_PMF_REGISTER_ARGUMENT); 66 + dev_dbg(dev->dev, "AMD_PMF_REGISTER_ARGUMENT:%d\n", value); 67 + 68 + value = amd_pmf_reg_read(dev, AMD_PMF_REGISTER_MESSAGE); 69 + dev_dbg(dev->dev, "AMD_PMF_REGISTER_MESSAGE:%x\n", value); 70 + } 71 + 72 + int amd_pmf_send_cmd(struct amd_pmf_dev *dev, u8 message, bool get, u32 arg, u32 *data) 73 + { 74 + int rc; 75 + u32 val; 76 + 77 + mutex_lock(&dev->lock); 78 + 79 + /* Wait until we get a valid response */ 80 + rc = readx_poll_timeout(ioread32, dev->regbase + AMD_PMF_REGISTER_RESPONSE, 81 + val, val != 0, PMF_MSG_DELAY_MIN_US, 82 + PMF_MSG_DELAY_MIN_US * RESPONSE_REGISTER_LOOP_MAX); 83 + if (rc) { 84 + dev_err(dev->dev, "failed to talk to SMU\n"); 85 + goto out_unlock; 86 + } 87 + 88 + /* Write zero to response register */ 89 + amd_pmf_reg_write(dev, AMD_PMF_REGISTER_RESPONSE, 0); 90 + 91 + /* Write argument into argument register */ 92 + amd_pmf_reg_write(dev, AMD_PMF_REGISTER_ARGUMENT, arg); 93 + 94 + /* Write message ID to message ID register */ 95 + amd_pmf_reg_write(dev, AMD_PMF_REGISTER_MESSAGE, message); 96 + 97 + /* Wait until we get a valid response */ 98 + rc = readx_poll_timeout(ioread32, dev->regbase + AMD_PMF_REGISTER_RESPONSE, 99 + val, val != 0, PMF_MSG_DELAY_MIN_US, 100 + PMF_MSG_DELAY_MIN_US * RESPONSE_REGISTER_LOOP_MAX); 101 + if (rc) { 102 + dev_err(dev->dev, "SMU response timed out\n"); 103 + goto out_unlock; 104 + } 105 + 106 + switch (val) { 107 + case AMD_PMF_RESULT_OK: 108 + if (get) { 109 + /* PMFW may take longer time to return back the data */ 110 + usleep_range(DELAY_MIN_US, 10 * DELAY_MAX_US); 111 + *data = amd_pmf_reg_read(dev, AMD_PMF_REGISTER_ARGUMENT); 112 + } 113 + break; 114 + case AMD_PMF_RESULT_CMD_REJECT_BUSY: 115 + dev_err(dev->dev, "SMU not ready. err: 0x%x\n", val); 116 + rc = -EBUSY; 117 + goto out_unlock; 118 + case AMD_PMF_RESULT_CMD_UNKNOWN: 119 + dev_err(dev->dev, "SMU cmd unknown. err: 0x%x\n", val); 120 + rc = -EINVAL; 121 + goto out_unlock; 122 + case AMD_PMF_RESULT_CMD_REJECT_PREREQ: 123 + case AMD_PMF_RESULT_FAILED: 124 + default: 125 + dev_err(dev->dev, "SMU cmd failed. err: 0x%x\n", val); 126 + rc = -EIO; 127 + goto out_unlock; 128 + } 129 + 130 + out_unlock: 131 + mutex_unlock(&dev->lock); 132 + amd_pmf_dump_registers(dev); 133 + return rc; 134 + } 135 + 136 + static const struct pci_device_id pmf_pci_ids[] = { 137 + { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_PS) }, 138 + { } 139 + }; 140 + 141 + static const struct acpi_device_id amd_pmf_acpi_ids[] = { 142 + {"AMDI0102", 0}, 143 + { } 144 + }; 145 + MODULE_DEVICE_TABLE(acpi, amd_pmf_acpi_ids); 146 + 147 + static int amd_pmf_probe(struct platform_device *pdev) 148 + { 149 + struct amd_pmf_dev *dev; 150 + struct pci_dev *rdev; 151 + u32 base_addr_lo; 152 + u32 base_addr_hi; 153 + u64 base_addr; 154 + u32 val; 155 + int err; 156 + 157 + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); 158 + if (!dev) 159 + return -ENOMEM; 160 + 161 + dev->dev = &pdev->dev; 162 + 163 + rdev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(0, 0)); 164 + if (!rdev || !pci_match_id(pmf_pci_ids, rdev)) { 165 + pci_dev_put(rdev); 166 + return -ENODEV; 167 + } 168 + 169 + dev->cpu_id = rdev->device; 170 + err = pci_write_config_dword(rdev, AMD_PMF_SMU_INDEX_ADDRESS, AMD_PMF_BASE_ADDR_LO); 171 + if (err) { 172 + dev_err(dev->dev, "error writing to 0x%x\n", AMD_PMF_SMU_INDEX_ADDRESS); 173 + pci_dev_put(rdev); 174 + return pcibios_err_to_errno(err); 175 + } 176 + 177 + err = pci_read_config_dword(rdev, AMD_PMF_SMU_INDEX_DATA, &val); 178 + if (err) { 179 + pci_dev_put(rdev); 180 + return pcibios_err_to_errno(err); 181 + } 182 + 183 + base_addr_lo = val & AMD_PMF_BASE_ADDR_HI_MASK; 184 + 185 + err = pci_write_config_dword(rdev, AMD_PMF_SMU_INDEX_ADDRESS, AMD_PMF_BASE_ADDR_HI); 186 + if (err) { 187 + dev_err(dev->dev, "error writing to 0x%x\n", AMD_PMF_SMU_INDEX_ADDRESS); 188 + pci_dev_put(rdev); 189 + return pcibios_err_to_errno(err); 190 + } 191 + 192 + err = pci_read_config_dword(rdev, AMD_PMF_SMU_INDEX_DATA, &val); 193 + if (err) { 194 + pci_dev_put(rdev); 195 + return pcibios_err_to_errno(err); 196 + } 197 + 198 + base_addr_hi = val & AMD_PMF_BASE_ADDR_LO_MASK; 199 + pci_dev_put(rdev); 200 + base_addr = ((u64)base_addr_hi << 32 | base_addr_lo); 201 + 202 + dev->regbase = devm_ioremap(dev->dev, base_addr + AMD_PMF_BASE_ADDR_OFFSET, 203 + AMD_PMF_MAPPING_SIZE); 204 + if (!dev->regbase) 205 + return -ENOMEM; 206 + 207 + platform_set_drvdata(pdev, dev); 208 + 209 + mutex_init(&dev->lock); 210 + dev_info(dev->dev, "registered PMF device successfully\n"); 211 + 212 + return 0; 213 + } 214 + 215 + static int amd_pmf_remove(struct platform_device *pdev) 216 + { 217 + struct amd_pmf_dev *dev = platform_get_drvdata(pdev); 218 + 219 + mutex_destroy(&dev->lock); 220 + kfree(dev->buf); 221 + return 0; 222 + } 223 + 224 + static struct platform_driver amd_pmf_driver = { 225 + .driver = { 226 + .name = "amd-pmf", 227 + .acpi_match_table = amd_pmf_acpi_ids, 228 + }, 229 + .probe = amd_pmf_probe, 230 + .remove = amd_pmf_remove, 231 + }; 232 + module_platform_driver(amd_pmf_driver); 233 + 234 + MODULE_LICENSE("GPL"); 235 + MODULE_DESCRIPTION("AMD Platform Management Framework Driver");
+46
drivers/platform/x86/amd/pmf/pmf.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* 3 + * AMD Platform Management Framework Driver 4 + * 5 + * Copyright (c) 2022, Advanced Micro Devices, Inc. 6 + * All Rights Reserved. 7 + * 8 + * Author: Shyam Sundar S K <Shyam-sundar.S-k@amd.com> 9 + */ 10 + 11 + #ifndef PMF_H 12 + #define PMF_H 13 + 14 + /* Message Definitions */ 15 + #define SET_SPL 0x03 /* SPL: Sustained Power Limit */ 16 + #define SET_SPPT 0x05 /* SPPT: Slow Package Power Tracking */ 17 + #define SET_FPPT 0x07 /* FPPT: Fast Package Power Tracking */ 18 + #define GET_SPL 0x0B 19 + #define GET_SPPT 0x0D 20 + #define GET_FPPT 0x0F 21 + #define SET_DRAM_ADDR_HIGH 0x14 22 + #define SET_DRAM_ADDR_LOW 0x15 23 + #define SET_TRANSFER_TABLE 0x16 24 + #define SET_STT_MIN_LIMIT 0x18 /* STT: Skin Temperature Tracking */ 25 + #define SET_STT_LIMIT_APU 0x19 26 + #define SET_STT_LIMIT_HS2 0x1A 27 + #define SET_SPPT_APU_ONLY 0x1D 28 + #define GET_SPPT_APU_ONLY 0x1E 29 + #define GET_STT_MIN_LIMIT 0x1F 30 + #define GET_STT_LIMIT_APU 0x20 31 + #define GET_STT_LIMIT_HS2 0x21 32 + 33 + struct amd_pmf_dev { 34 + void __iomem *regbase; 35 + void __iomem *smu_virt_addr; 36 + void *buf; 37 + u32 base_addr; 38 + u32 cpu_id; 39 + struct device *dev; 40 + struct mutex lock; /* protects the PMF interface */ 41 + }; 42 + 43 + /* Core Layer */ 44 + int amd_pmf_send_cmd(struct amd_pmf_dev *dev, u8 message, bool get, u32 arg, u32 *data); 45 + 46 + #endif /* PMF_H */