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 }; 20 21 static struct quirk_entry quirk_s2idle_bug = { 22 .s2idle_bug_mmio = 0xfed80380, 23 }; 24 25 static const struct dmi_system_id fwbug_list[] = { 26 { 27 .ident = "L14 Gen2 AMD", 28 .driver_data = &quirk_s2idle_bug, 29 .matches = { 30 DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), 31 DMI_MATCH(DMI_PRODUCT_NAME, "20X5"), 32 } 33 }, 34 { 35 .ident = "T14s Gen2 AMD", 36 .driver_data = &quirk_s2idle_bug, 37 .matches = { 38 DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), 39 DMI_MATCH(DMI_PRODUCT_NAME, "20XF"), 40 } 41 }, 42 { 43 .ident = "X13 Gen2 AMD", 44 .driver_data = &quirk_s2idle_bug, 45 .matches = { 46 DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), 47 DMI_MATCH(DMI_PRODUCT_NAME, "20XH"), 48 } 49 }, 50 { 51 .ident = "T14 Gen2 AMD", 52 .driver_data = &quirk_s2idle_bug, 53 .matches = { 54 DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), 55 DMI_MATCH(DMI_PRODUCT_NAME, "20XK"), 56 } 57 }, 58 { 59 .ident = "T14 Gen1 AMD", 60 .driver_data = &quirk_s2idle_bug, 61 .matches = { 62 DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), 63 DMI_MATCH(DMI_PRODUCT_NAME, "20UD"), 64 } 65 }, 66 { 67 .ident = "T14 Gen1 AMD", 68 .driver_data = &quirk_s2idle_bug, 69 .matches = { 70 DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), 71 DMI_MATCH(DMI_PRODUCT_NAME, "20UE"), 72 } 73 }, 74 { 75 .ident = "T14s Gen1 AMD", 76 .driver_data = &quirk_s2idle_bug, 77 .matches = { 78 DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), 79 DMI_MATCH(DMI_PRODUCT_NAME, "20UH"), 80 } 81 }, 82 { 83 .ident = "T14s Gen1 AMD", 84 .driver_data = &quirk_s2idle_bug, 85 .matches = { 86 DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), 87 DMI_MATCH(DMI_PRODUCT_NAME, "20UJ"), 88 } 89 }, 90 { 91 .ident = "P14s Gen1 AMD", 92 .driver_data = &quirk_s2idle_bug, 93 .matches = { 94 DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), 95 DMI_MATCH(DMI_PRODUCT_NAME, "20Y1"), 96 } 97 }, 98 { 99 .ident = "P14s Gen2 AMD", 100 .driver_data = &quirk_s2idle_bug, 101 .matches = { 102 DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), 103 DMI_MATCH(DMI_PRODUCT_NAME, "21A0"), 104 } 105 }, 106 { 107 .ident = "P14s Gen2 AMD", 108 .driver_data = &quirk_s2idle_bug, 109 .matches = { 110 DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), 111 DMI_MATCH(DMI_PRODUCT_NAME, "21A1"), 112 } 113 }, 114 /* https://gitlab.freedesktop.org/drm/amd/-/issues/2684 */ 115 { 116 .ident = "HP Laptop 15s-eq2xxx", 117 .driver_data = &quirk_s2idle_bug, 118 .matches = { 119 DMI_MATCH(DMI_SYS_VENDOR, "HP"), 120 DMI_MATCH(DMI_PRODUCT_NAME, "HP Laptop 15s-eq2xxx"), 121 } 122 }, 123 {} 124 }; 125 126 /* 127 * Laptops that run a SMI handler during the D3->D0 transition that occurs 128 * specifically when exiting suspend to idle which can cause 129 * large delays during resume when the IOMMU translation layer is enabled (the default 130 * behavior) for NVME devices: 131 * 132 * To avoid this firmware problem, skip the SMI handler on these machines before the 133 * D0 transition occurs. 134 */ 135 static void amd_pmc_skip_nvme_smi_handler(u32 s2idle_bug_mmio) 136 { 137 void __iomem *addr; 138 u8 val; 139 140 if (!request_mem_region_muxed(s2idle_bug_mmio, 1, "amd_pmc_pm80")) 141 return; 142 143 addr = ioremap(s2idle_bug_mmio, 1); 144 if (!addr) 145 goto cleanup_resource; 146 147 val = ioread8(addr); 148 iowrite8(val & ~BIT(0), addr); 149 150 iounmap(addr); 151 cleanup_resource: 152 release_mem_region(s2idle_bug_mmio, 1); 153 } 154 155 void amd_pmc_process_restore_quirks(struct amd_pmc_dev *dev) 156 { 157 if (dev->quirks && dev->quirks->s2idle_bug_mmio) 158 amd_pmc_skip_nvme_smi_handler(dev->quirks->s2idle_bug_mmio); 159 } 160 161 void amd_pmc_quirks_init(struct amd_pmc_dev *dev) 162 { 163 const struct dmi_system_id *dmi_id; 164 165 dmi_id = dmi_first_match(fwbug_list); 166 if (!dmi_id) 167 return; 168 dev->quirks = dmi_id->driver_data; 169 if (dev->quirks->s2idle_bug_mmio) 170 pr_info("Using s2idle quirk to avoid %s platform firmware bug\n", 171 dmi_id->ident); 172 } 173