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  
17  struct quirk_entry {
18  	u32 s2idle_bug_mmio;
19  	bool spurious_8042;
20  };
21  
22  static struct quirk_entry quirk_s2idle_bug = {
23  	.s2idle_bug_mmio = 0xfed80380,
24  };
25  
26  static struct quirk_entry quirk_spurious_8042 = {
27  	.spurious_8042 = true,
28  };
29  
30  static 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   */
amd_pmc_skip_nvme_smi_handler(u32 s2idle_bug_mmio)232  static 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);
248  cleanup_resource:
249  	release_mem_region(s2idle_bug_mmio, 1);
250  }
251  
amd_pmc_process_restore_quirks(struct amd_pmc_dev * dev)252  void 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  
amd_pmc_quirks_init(struct amd_pmc_dev * dev)258  void 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