Skip to content

Commit da5ce22

Browse files
Shyam Sundar S Kjwrdegoede
authored andcommitted
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 <[email protected]> Signed-off-by: Shyam Sundar S K <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Hans de Goede <[email protected]>
1 parent 568035b commit da5ce22

File tree

6 files changed

+307
-0
lines changed

6 files changed

+307
-0
lines changed

drivers/platform/x86/amd/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
# AMD x86 Platform Specific Drivers
44
#
55

6+
source "drivers/platform/x86/amd/pmf/Kconfig"
7+
68
config AMD_PMC
79
tristate "AMD SoC PMC driver"
810
depends on ACPI && PCI && RTC_CLASS

drivers/platform/x86/amd/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ amd-pmc-y := pmc.o
88
obj-$(CONFIG_AMD_PMC) += amd-pmc.o
99
amd_hsmp-y := hsmp.o
1010
obj-$(CONFIG_AMD_HSMP) += amd_hsmp.o
11+
obj-$(CONFIG_AMD_PMF) += pmf/
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
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.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
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
Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
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 <[email protected]>
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");

drivers/platform/x86/amd/pmf/pmf.h

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
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 <[email protected]>
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 */

0 commit comments

Comments
 (0)