Tarun Tuli | 166387f7 | 2023-03-29 13:36:26 +0000 | [diff] [blame] | 1 | /* SPDX-License-Identifier: GPL-2.0-only */ |
| 2 | |
| 3 | #include <acpi/acpi.h> |
| 4 | #include <acpi/acpigen.h> |
| 5 | #include <baseboard/variants.h> |
| 6 | #include <boardid.h> |
| 7 | #include <delay.h> |
| 8 | #include <device/pci.h> |
| 9 | #include <gpio.h> |
| 10 | #include <timer.h> |
| 11 | #include <types.h> |
| 12 | |
| 13 | #define GPU_1V2_PWR_EN GPP_D0 |
| 14 | #define GPU_1V2_PG GPP_D1 |
| 15 | #define GPU_1V8_PWR_EN GPP_E11 |
| 16 | #define GPU_1V8_PG GPP_E20 |
| 17 | #define GPU_3V3_PWR_EN GPP_E1 |
| 18 | #define GPU_3V3_PG GPP_E2 |
| 19 | #define NVVDD_PWR_EN GPP_E0 |
Tarun Tuli | 15dd44e | 2023-04-14 19:32:24 +0000 | [diff] [blame] | 20 | #define NVVDD_PG GPP_E8 |
Tarun Tuli | 166387f7 | 2023-03-29 13:36:26 +0000 | [diff] [blame] | 21 | #define PEXVDD_PWR_EN GPP_E10 |
| 22 | #define PEXVDD_PG GPP_E17 |
| 23 | #define FBVDD_PWR_EN GPP_A19 |
| 24 | #define FBVDD_PG GPP_E4 |
| 25 | #define GPU_PERST_L GPP_B3 |
| 26 | #define GPU_ALLRAILS_PG GPP_E5 |
| 27 | |
| 28 | #define DEFAULT_PG_TIMEOUT_US 20000 |
| 29 | |
| 30 | #define VGAR_BYTE_OFFSET 5 |
| 31 | |
| 32 | /* Maximum size of PCI config space to save. */ |
| 33 | #define GPU_CONFIG_SAVE_SPACE_BYTES 0x100 |
| 34 | |
| 35 | static bool gpu_powered_on; |
| 36 | |
| 37 | struct power_rail_sequence { |
| 38 | const char *name; |
| 39 | |
| 40 | /* This is the GPIO (output) connected to the VR's enable pin. */ |
| 41 | gpio_t pwr_en_gpio; |
| 42 | bool pwr_en_active_low; |
| 43 | |
| 44 | /* This is the GPIO (input) connected to the VR's power-good pin. */ |
| 45 | gpio_t pg_gpio; |
| 46 | |
| 47 | /* Delay after sequencing this rail. */ |
| 48 | unsigned int delay_ms; |
| 49 | }; |
| 50 | |
| 51 | /* In GCOFF exit order (i.e., power-on order) */ |
| 52 | static struct power_rail_sequence gpu_on_seq[] = { |
| 53 | { "GPU 1.2V", GPU_1V2_PWR_EN, false, GPU_1V2_PG, }, |
| 54 | { "GPU 1.8V", GPU_1V8_PWR_EN, false, GPU_1V8_PG, }, |
| 55 | { "GPU 3.3V", GPU_3V3_PWR_EN, false, GPU_3V3_PG, }, |
| 56 | { "NVVDD+MSVDD", NVVDD_PWR_EN, false, NVVDD_PG, }, |
| 57 | { "PEXVDD", PEXVDD_PWR_EN, false, PEXVDD_PG, }, |
| 58 | { "FBVDD", FBVDD_PWR_EN, false, FBVDD_PG, }, |
| 59 | }; |
| 60 | |
| 61 | /* In GCOFF entry order (i.e., power-off order) */ |
| 62 | static struct power_rail_sequence gpu_off_seq[] = { |
| 63 | { "FBVDD", FBVDD_PWR_EN, false, FBVDD_PG, 0,}, |
Tarun Tuli | 3e304e5 | 2023-05-09 13:00:26 +0000 | [diff] [blame] | 64 | { "PEXVDD", PEXVDD_PWR_EN, false, PEXVDD_PG, 3,}, |
Tarun Tuli | 166387f7 | 2023-03-29 13:36:26 +0000 | [diff] [blame] | 65 | { "NVVDD+MSVDD", NVVDD_PWR_EN, false, NVVDD_PG, 2,}, |
| 66 | { "GPU 3.3V", GPU_3V3_PWR_EN, false, GPU_3V3_PG, 4,}, |
| 67 | { "GPU 1.8V", GPU_1V8_PWR_EN, false, GPU_1V8_PG, 0,}, |
| 68 | { "GPU 1.2V", GPU_1V2_PWR_EN, false, GPU_1V2_PG, 0,}, |
| 69 | }; |
| 70 | |
| 71 | enum rail_state { |
| 72 | RAIL_OFF = 0, |
| 73 | RAIL_ON = 1, |
| 74 | }; |
| 75 | |
| 76 | /* Assert the VR's enable pin, and wait until the VR's power-good is asserted. */ |
| 77 | static bool sequence_rail(const struct power_rail_sequence *seq, enum rail_state state) |
| 78 | { |
| 79 | enum rail_state pwr_en_state = state; |
| 80 | bool result; |
| 81 | |
| 82 | if (seq->pwr_en_active_low) |
| 83 | pwr_en_state = !pwr_en_state; |
| 84 | |
| 85 | gpio_output(seq->pwr_en_gpio, pwr_en_state); |
Tarun Tuli | 11ef816 | 2023-06-07 21:23:13 +0000 | [diff] [blame^] | 86 | result = wait_us(DEFAULT_PG_TIMEOUT_US, gpio_get(seq->pg_gpio) == state) > 0; |
Tarun Tuli | 166387f7 | 2023-03-29 13:36:26 +0000 | [diff] [blame] | 87 | if (seq->delay_ms) |
| 88 | mdelay(seq->delay_ms); |
| 89 | |
| 90 | return result; |
| 91 | } |
| 92 | |
| 93 | static void dgpu_power_sequence_off(void) |
| 94 | { |
| 95 | /* Assert reset and clear power-good */ |
| 96 | gpio_output(GPU_PERST_L, 0); |
| 97 | |
| 98 | /* Inform the GPU that the power is no longer good. */ |
| 99 | gpio_output(GPU_ALLRAILS_PG, 0); |
| 100 | |
| 101 | for (size_t i = 0; i < ARRAY_SIZE(gpu_off_seq); i++) { |
| 102 | if (!sequence_rail(&gpu_off_seq[i], RAIL_OFF)) { |
| 103 | printk(BIOS_ERR, "Failed to disable %s rail, continuing!\n", |
| 104 | gpu_off_seq[i].name); |
| 105 | } |
| 106 | } |
| 107 | } |
| 108 | |
| 109 | static void dgpu_power_sequence_on(void) |
| 110 | { |
| 111 | /* Assert PERST# */ |
| 112 | gpio_output(GPU_PERST_L, 0); |
| 113 | |
| 114 | for (size_t i = 0; i < ARRAY_SIZE(gpu_on_seq); i++) { |
| 115 | if (!sequence_rail(&gpu_on_seq[i], RAIL_ON)) { |
| 116 | printk(BIOS_ERR, "Failed to enable %s rail, sequencing back down!\n", |
| 117 | gpu_on_seq[i].name); |
| 118 | |
| 119 | /* If an error occurred, then perform the power-off sequence and |
| 120 | return early to avoid setting GPU_ALLRAILS_PG and PERST_L. */ |
| 121 | dgpu_power_sequence_off(); |
| 122 | return; |
| 123 | } |
| 124 | } |
| 125 | |
| 126 | /* Set power-good and release PERST# */ |
| 127 | gpio_output(GPU_ALLRAILS_PG, 1); |
| 128 | mdelay(1); |
| 129 | gpio_output(GPU_PERST_L, 1); |
| 130 | |
| 131 | printk(BIOS_INFO, "Sequenced GPU successfully\n"); |
| 132 | mdelay(1); |
| 133 | |
| 134 | gpu_powered_on = true; |
| 135 | } |
| 136 | |
| 137 | void variant_init(void) |
| 138 | { |
| 139 | if (acpi_is_wakeup_s3()) |
| 140 | return; |
| 141 | |
| 142 | dgpu_power_sequence_on(); |
| 143 | } |
| 144 | |
| 145 | /* |
| 146 | * Pass the specific GPIO names |
| 147 | */ |
| 148 | void variant_fill_ssdt(const struct device *dev) |
| 149 | { |
| 150 | const int nvvdd_pg_gpio = NVVDD_PG; |
| 151 | const int gpu_1v8_en_gpio = GPU_1V8_PWR_EN; |
| 152 | acpigen_write_scope("\\_SB.PCI0.PEG0.PEGP"); |
| 153 | acpigen_write_method("_INI", 0); |
| 154 | acpigen_write_store_int_to_namestr(nvvdd_pg_gpio, "NVPG"); |
| 155 | acpigen_write_store_int_to_namestr(gpu_1v8_en_gpio, "GPEN"); |
| 156 | acpigen_write_method_end(); |
| 157 | acpigen_write_scope_end(); |
| 158 | } |
| 159 | |
| 160 | void variant_finalize(void) |
| 161 | { |
| 162 | /* |
| 163 | * Currently the `pch_pirq_init()` function in lpc_lib.c will program |
| 164 | * PIRQ IRQs for all PCI devices discovered during enumeration. This may |
| 165 | * not be correct for all devices, and causes strange behavior with the |
| 166 | * Nvidia dGPU; it will start out with IRQ 11 and then after a |
| 167 | * suspend/resume cycle, it will get programmed back to 16, so the Linux |
| 168 | * kernel must be doing some IRQ sanitization at some point. To fix |
| 169 | * this anomaly, explicitly program the IRQ to 16 (which we know is what |
| 170 | * IRQ it will eventually take). |
| 171 | */ |
| 172 | const struct device *dgpu = DEV_PTR(dgpu); |
| 173 | pci_write_config8(dgpu, PCI_INTERRUPT_LINE, 16); |
| 174 | } |