1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * AMD SoC Power Management Controller Driver Quirks
4 *
5 * Copyright (c) 2023, Advanced Micro Devices, Inc.
6 * All Rights Reserved.
7 *
8 * Author: Mario Limonciello <mario.limonciello@amd.com>
9 */
10
11#include <linux/dmi.h>
12#include <linux/io.h>
13#include <linux/ioport.h>
14
15#include "pmc.h"
16
17struct quirk_entry {
18	u32 s2idle_bug_mmio;
19	bool spurious_8042;
20};
21
22static struct quirk_entry quirk_s2idle_bug = {
23	.s2idle_bug_mmio = 0xfed80380,
24};
25
26static struct quirk_entry quirk_spurious_8042 = {
27	.spurious_8042 = true,
28};
29
30static const struct dmi_system_id fwbug_list[] = {
31	{
32		.ident = "L14 Gen2 AMD",
33		.driver_data = &quirk_s2idle_bug,
34		.matches = {
35			DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
36			DMI_MATCH(DMI_PRODUCT_NAME, "20X5"),
37		}
38	},
39	{
40		.ident = "T14s Gen2 AMD",
41		.driver_data = &quirk_s2idle_bug,
42		.matches = {
43			DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
44			DMI_MATCH(DMI_PRODUCT_NAME, "20XF"),
45		}
46	},
47	{
48		.ident = "X13 Gen2 AMD",
49		.driver_data = &quirk_s2idle_bug,
50		.matches = {
51			DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
52			DMI_MATCH(DMI_PRODUCT_NAME, "20XH"),
53		}
54	},
55	{
56		.ident = "T14 Gen2 AMD",
57		.driver_data = &quirk_s2idle_bug,
58		.matches = {
59			DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
60			DMI_MATCH(DMI_PRODUCT_NAME, "20XK"),
61		}
62	},
63	{
64		.ident = "T14 Gen1 AMD",
65		.driver_data = &quirk_s2idle_bug,
66		.matches = {
67			DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
68			DMI_MATCH(DMI_PRODUCT_NAME, "20UD"),
69		}
70	},
71	{
72		.ident = "T14 Gen1 AMD",
73		.driver_data = &quirk_s2idle_bug,
74		.matches = {
75			DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
76			DMI_MATCH(DMI_PRODUCT_NAME, "20UE"),
77		}
78	},
79	{
80		.ident = "T14s Gen1 AMD",
81		.driver_data = &quirk_s2idle_bug,
82		.matches = {
83			DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
84			DMI_MATCH(DMI_PRODUCT_NAME, "20UH"),
85		}
86	},
87	{
88		.ident = "T14s Gen1 AMD",
89		.driver_data = &quirk_s2idle_bug,
90		.matches = {
91			DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
92			DMI_MATCH(DMI_PRODUCT_NAME, "20UJ"),
93		}
94	},
95	{
96		.ident = "P14s Gen1 AMD",
97		.driver_data = &quirk_s2idle_bug,
98		.matches = {
99			DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
100			DMI_MATCH(DMI_PRODUCT_NAME, "20Y1"),
101		}
102	},
103	{
104		.ident = "P14s Gen2 AMD",
105		.driver_data = &quirk_s2idle_bug,
106		.matches = {
107			DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
108			DMI_MATCH(DMI_PRODUCT_NAME, "21A0"),
109		}
110	},
111	{
112		.ident = "P14s Gen2 AMD",
113		.driver_data = &quirk_s2idle_bug,
114		.matches = {
115			DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
116			DMI_MATCH(DMI_PRODUCT_NAME, "21A1"),
117		}
118	},
119	/* https://bugzilla.kernel.org/show_bug.cgi?id=218024 */
120	{
121		.ident = "V14 G4 AMN",
122		.driver_data = &quirk_s2idle_bug,
123		.matches = {
124			DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
125			DMI_MATCH(DMI_PRODUCT_NAME, "82YT"),
126		}
127	},
128	{
129		.ident = "V14 G4 AMN",
130		.driver_data = &quirk_s2idle_bug,
131		.matches = {
132			DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
133			DMI_MATCH(DMI_PRODUCT_NAME, "83GE"),
134		}
135	},
136	{
137		.ident = "V15 G4 AMN",
138		.driver_data = &quirk_s2idle_bug,
139		.matches = {
140			DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
141			DMI_MATCH(DMI_PRODUCT_NAME, "82YU"),
142		}
143	},
144	{
145		.ident = "V15 G4 AMN",
146		.driver_data = &quirk_s2idle_bug,
147		.matches = {
148			DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
149			DMI_MATCH(DMI_PRODUCT_NAME, "83CQ"),
150		}
151	},
152	{
153		.ident = "IdeaPad 1 14AMN7",
154		.driver_data = &quirk_s2idle_bug,
155		.matches = {
156			DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
157			DMI_MATCH(DMI_PRODUCT_NAME, "82VF"),
158		}
159	},
160	{
161		.ident = "IdeaPad 1 15AMN7",
162		.driver_data = &quirk_s2idle_bug,
163		.matches = {
164			DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
165			DMI_MATCH(DMI_PRODUCT_NAME, "82VG"),
166		}
167	},
168	{
169		.ident = "IdeaPad 1 15AMN7",
170		.driver_data = &quirk_s2idle_bug,
171		.matches = {
172			DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
173			DMI_MATCH(DMI_PRODUCT_NAME, "82X5"),
174		}
175	},
176	{
177		.ident = "IdeaPad Slim 3 14AMN8",
178		.driver_data = &quirk_s2idle_bug,
179		.matches = {
180			DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
181			DMI_MATCH(DMI_PRODUCT_NAME, "82XN"),
182		}
183	},
184	{
185		.ident = "IdeaPad Slim 3 15AMN8",
186		.driver_data = &quirk_s2idle_bug,
187		.matches = {
188			DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
189			DMI_MATCH(DMI_PRODUCT_NAME, "82XQ"),
190		}
191	},
192	/* https://gitlab.freedesktop.org/drm/amd/-/issues/2684 */
193	{
194		.ident = "HP Laptop 15s-eq2xxx",
195		.driver_data = &quirk_s2idle_bug,
196		.matches = {
197			DMI_MATCH(DMI_SYS_VENDOR, "HP"),
198			DMI_MATCH(DMI_PRODUCT_NAME, "HP Laptop 15s-eq2xxx"),
199		}
200	},
201	/* https://community.frame.work/t/tracking-framework-amd-ryzen-7040-series-lid-wakeup-behavior-feedback/39128 */
202	{
203		.ident = "Framework Laptop 13 (Phoenix)",
204		.driver_data = &quirk_spurious_8042,
205		.matches = {
206			DMI_MATCH(DMI_SYS_VENDOR, "Framework"),
207			DMI_MATCH(DMI_PRODUCT_NAME, "Laptop 13 (AMD Ryzen 7040Series)"),
208			DMI_MATCH(DMI_BIOS_VERSION, "03.03"),
209		}
210	},
211	{
212		.ident = "Framework Laptop 13 (Phoenix)",
213		.driver_data = &quirk_spurious_8042,
214		.matches = {
215			DMI_MATCH(DMI_SYS_VENDOR, "Framework"),
216			DMI_MATCH(DMI_PRODUCT_NAME, "Laptop 13 (AMD Ryzen 7040Series)"),
217			DMI_MATCH(DMI_BIOS_VERSION, "03.05"),
218		}
219	},
220	{}
221};
222
223/*
224 * Laptops that run a SMI handler during the D3->D0 transition that occurs
225 * specifically when exiting suspend to idle which can cause
226 * large delays during resume when the IOMMU translation layer is enabled (the default
227 * behavior) for NVME devices:
228 *
229 * To avoid this firmware problem, skip the SMI handler on these machines before the
230 * D0 transition occurs.
231 */
232static void amd_pmc_skip_nvme_smi_handler(u32 s2idle_bug_mmio)
233{
234	void __iomem *addr;
235	u8 val;
236
237	if (!request_mem_region_muxed(s2idle_bug_mmio, 1, "amd_pmc_pm80"))
238		return;
239
240	addr = ioremap(s2idle_bug_mmio, 1);
241	if (!addr)
242		goto cleanup_resource;
243
244	val = ioread8(addr);
245	iowrite8(val & ~BIT(0), addr);
246
247	iounmap(addr);
248cleanup_resource:
249	release_mem_region(s2idle_bug_mmio, 1);
250}
251
252void amd_pmc_process_restore_quirks(struct amd_pmc_dev *dev)
253{
254	if (dev->quirks && dev->quirks->s2idle_bug_mmio)
255		amd_pmc_skip_nvme_smi_handler(dev->quirks->s2idle_bug_mmio);
256}
257
258void amd_pmc_quirks_init(struct amd_pmc_dev *dev)
259{
260	const struct dmi_system_id *dmi_id;
261
262	if (dev->cpu_id == AMD_CPU_ID_CZN)
263		dev->disable_8042_wakeup = true;
264
265	dmi_id = dmi_first_match(fwbug_list);
266	if (!dmi_id)
267		return;
268	dev->quirks = dmi_id->driver_data;
269	if (dev->quirks->s2idle_bug_mmio)
270		pr_info("Using s2idle quirk to avoid %s platform firmware bug\n",
271			dmi_id->ident);
272	if (dev->quirks->spurious_8042)
273		dev->disable_8042_wakeup = true;
274}
275