Patrick Georgi | ac95903 | 2020-05-05 22:49:26 +0200 | [diff] [blame] | 1 | /* SPDX-License-Identifier: GPL-2.0-or-later */ |
Elyes HAOUAS | 20eaef0 | 2019-03-29 17:45:28 +0100 | [diff] [blame] | 2 | |
John Zhao | 98ce39d | 2022-01-10 10:51:24 -0800 | [diff] [blame] | 3 | #define __SIMPLE_DEVICE__ |
| 4 | |
Hannah Williams | d28bd0c | 2017-05-17 23:24:22 -0700 | [diff] [blame] | 5 | #include <assert.h> |
Furquan Shaikh | d9ce285 | 2021-04-10 14:27:16 -0700 | [diff] [blame] | 6 | #include <bootstate.h> |
Elyes HAOUAS | 20eaef0 | 2019-03-29 17:45:28 +0100 | [diff] [blame] | 7 | #include <console/console.h> |
Michael Niewöhner | 8913b78 | 2020-12-11 22:13:44 +0100 | [diff] [blame] | 8 | #include <device/device.h> |
Furquan Shaikh | d9ce285 | 2021-04-10 14:27:16 -0700 | [diff] [blame] | 9 | #include <fsp/debug.h> |
Subrata Banik | fe678cb | 2022-01-05 18:46:02 +0000 | [diff] [blame] | 10 | #include <intelblocks/cpulib.h> |
Hannah Williams | d28bd0c | 2017-05-17 23:24:22 -0700 | [diff] [blame] | 11 | #include <intelblocks/gpio.h> |
| 12 | #include <gpio.h> |
| 13 | #include <intelblocks/itss.h> |
Aseda Aboagye | e58e6f2 | 2021-06-15 23:11:41 -0700 | [diff] [blame] | 14 | #include <intelblocks/p2sb.h> |
Hannah Williams | d28bd0c | 2017-05-17 23:24:22 -0700 | [diff] [blame] | 15 | #include <intelblocks/pcr.h> |
Subrata Banik | 5e84a42 | 2022-01-27 19:55:10 +0530 | [diff] [blame] | 16 | #include <security/vboot/vboot_common.h> |
John Zhao | 98ce39d | 2022-01-10 10:51:24 -0800 | [diff] [blame] | 17 | #include <soc/pci_devs.h> |
Hannah Williams | d28bd0c | 2017-05-17 23:24:22 -0700 | [diff] [blame] | 18 | #include <soc/pm.h> |
Furquan Shaikh | d9ce285 | 2021-04-10 14:27:16 -0700 | [diff] [blame] | 19 | #include <stdlib.h> |
Hannah Williams | d28bd0c | 2017-05-17 23:24:22 -0700 | [diff] [blame] | 20 | #include <types.h> |
| 21 | |
| 22 | #define GPIO_DWx_SIZE(x) (sizeof(uint32_t) * (x)) |
| 23 | #define PAD_CFG_OFFSET(x, dw_num) ((x) + GPIO_DWx_SIZE(dw_num)) |
| 24 | #define PAD_CFG0_OFFSET(x) PAD_CFG_OFFSET(x, 0) |
| 25 | #define PAD_CFG1_OFFSET(x) PAD_CFG_OFFSET(x, 1) |
| 26 | #define PAD_CFG2_OFFSET(x) PAD_CFG_OFFSET(x, 2) |
| 27 | #define PAD_CFG3_OFFSET(x) PAD_CFG_OFFSET(x, 3) |
| 28 | |
| 29 | #define PAD_DW0_MASK (PAD_CFG0_TX_STATE | \ |
| 30 | PAD_CFG0_TX_DISABLE | PAD_CFG0_RX_DISABLE | PAD_CFG0_MODE_MASK |\ |
| 31 | PAD_CFG0_ROUTE_MASK | PAD_CFG0_RXTENCFG_MASK | \ |
| 32 | PAD_CFG0_RXINV_MASK | PAD_CFG0_PREGFRXSEL | \ |
Maulik V Vaghela | f600411 | 2021-05-17 12:25:40 +0530 | [diff] [blame] | 33 | PAD_CFG0_TRIG_MASK | PAD_CFG0_RXRAW1_MASK | PAD_CFG0_NAFVWE_ENABLE |\ |
Hannah Williams | d28bd0c | 2017-05-17 23:24:22 -0700 | [diff] [blame] | 34 | PAD_CFG0_RXPADSTSEL_MASK | PAD_CFG0_RESET_MASK) |
| 35 | |
Julius Werner | cd49cce | 2019-03-05 16:53:33 -0800 | [diff] [blame] | 36 | #if CONFIG(SOC_INTEL_COMMON_BLOCK_GPIO_PADCFG_PADTOL) |
Hannah Williams | 0805a7e | 2017-08-29 16:46:35 -0700 | [diff] [blame] | 37 | #define PAD_DW1_MASK (PAD_CFG1_IOSTERM_MASK | \ |
| 38 | PAD_CFG1_PULL_MASK | \ |
| 39 | PAD_CFG1_TOL_MASK | \ |
| 40 | PAD_CFG1_IOSSTATE_MASK) |
| 41 | #else |
Hannah Williams | d28bd0c | 2017-05-17 23:24:22 -0700 | [diff] [blame] | 42 | #define PAD_DW1_MASK (PAD_CFG1_IOSTERM_MASK | \ |
| 43 | PAD_CFG1_PULL_MASK | \ |
| 44 | PAD_CFG1_IOSSTATE_MASK) |
Hannah Williams | 0805a7e | 2017-08-29 16:46:35 -0700 | [diff] [blame] | 45 | #endif |
Hannah Williams | d28bd0c | 2017-05-17 23:24:22 -0700 | [diff] [blame] | 46 | |
Karthikeyan Ramasubramanian | cff4507 | 2018-12-26 22:01:26 -0700 | [diff] [blame] | 47 | #define PAD_DW2_MASK (PAD_CFG2_DEBOUNCE_MASK) |
Hannah Williams | d28bd0c | 2017-05-17 23:24:22 -0700 | [diff] [blame] | 48 | #define PAD_DW3_MASK (0) |
| 49 | |
| 50 | #define MISCCFG_GPE0_DW0_SHIFT 8 |
| 51 | #define MISCCFG_GPE0_DW0_MASK (0xf << MISCCFG_GPE0_DW0_SHIFT) |
| 52 | #define MISCCFG_GPE0_DW1_SHIFT 12 |
| 53 | #define MISCCFG_GPE0_DW1_MASK (0xf << MISCCFG_GPE0_DW1_SHIFT) |
| 54 | #define MISCCFG_GPE0_DW2_SHIFT 16 |
| 55 | #define MISCCFG_GPE0_DW2_MASK (0xf << MISCCFG_GPE0_DW2_SHIFT) |
| 56 | |
| 57 | #define GPI_SMI_STS_OFFSET(comm, group) ((comm)->gpi_smi_sts_reg_0 + \ |
| 58 | ((group) * sizeof(uint32_t))) |
| 59 | #define GPI_SMI_EN_OFFSET(comm, group) ((comm)->gpi_smi_en_reg_0 + \ |
| 60 | ((group) * sizeof(uint32_t))) |
Michael Niewöhner | 14512f9 | 2020-11-23 15:53:28 +0100 | [diff] [blame] | 61 | #define GPI_NMI_STS_OFFSET(comm, group) ((comm)->gpi_nmi_sts_reg_0 + \ |
| 62 | ((group) * sizeof(uint32_t))) |
| 63 | #define GPI_NMI_EN_OFFSET(comm, group) ((comm)->gpi_nmi_en_reg_0 + \ |
| 64 | ((group) * sizeof(uint32_t))) |
Karthikeyan Ramasubramanian | 3391a31 | 2019-04-24 10:12:38 -0600 | [diff] [blame] | 65 | #define GPI_IS_OFFSET(comm, group) ((comm)->gpi_int_sts_reg_0 + \ |
| 66 | ((group) * sizeof(uint32_t))) |
| 67 | #define GPI_IE_OFFSET(comm, group) ((comm)->gpi_int_en_reg_0 + \ |
| 68 | ((group) * sizeof(uint32_t))) |
Maulik V Vaghela | 38b8bf0 | 2022-05-06 11:05:21 +0530 | [diff] [blame] | 69 | #define GPI_GPE_STS_OFFSET(comm, group) ((comm)->gpi_gpe_sts_reg_0 + \ |
| 70 | ((group) * sizeof(uint32_t))) |
| 71 | #define GPI_GPE_EN_OFFSET(comm, group) ((comm)->gpi_gpe_en_reg_0 + \ |
| 72 | ((group) * sizeof(uint32_t))) |
Hannah Williams | d28bd0c | 2017-05-17 23:24:22 -0700 | [diff] [blame] | 73 | |
Aaron Durbin | 65943e1 | 2017-07-19 13:16:51 -0600 | [diff] [blame] | 74 | static inline size_t relative_pad_in_comm(const struct pad_community *comm, |
| 75 | gpio_t gpio) |
| 76 | { |
| 77 | return gpio - comm->first_pad; |
| 78 | } |
| 79 | |
Bora Guvendik | 3f67232 | 2017-11-22 13:48:12 -0800 | [diff] [blame] | 80 | /* find the group within the community that the pad is a part of */ |
Aaron Durbin | 65943e1 | 2017-07-19 13:16:51 -0600 | [diff] [blame] | 81 | static inline size_t gpio_group_index(const struct pad_community *comm, |
| 82 | unsigned int relative_pad) |
| 83 | { |
Bora Guvendik | 3f67232 | 2017-11-22 13:48:12 -0800 | [diff] [blame] | 84 | size_t i; |
| 85 | |
Bora Guvendik | a08475e | 2018-10-05 12:33:53 -0700 | [diff] [blame] | 86 | if (!comm->groups) |
| 87 | die("Failed to get comm->groups."); |
Bora Guvendik | 3f67232 | 2017-11-22 13:48:12 -0800 | [diff] [blame] | 88 | |
| 89 | /* find the base pad number for this pad's group */ |
| 90 | for (i = 0; i < comm->num_groups; i++) { |
| 91 | if (relative_pad >= comm->groups[i].first_pad && |
| 92 | relative_pad < comm->groups[i].first_pad + |
| 93 | comm->groups[i].size) { |
| 94 | return i; |
| 95 | } |
| 96 | } |
Maxim Polyakov | ce2399a | 2020-04-01 20:23:38 +0300 | [diff] [blame] | 97 | printk(BIOS_ERR, "%s: pad %d is not found in community %s!\n", |
| 98 | __func__, relative_pad, comm->name); |
Julius Werner | 3e034b6 | 2020-07-29 17:39:21 -0700 | [diff] [blame] | 99 | BUG(); |
Bora Guvendik | 3f67232 | 2017-11-22 13:48:12 -0800 | [diff] [blame] | 100 | |
| 101 | return i; |
| 102 | } |
| 103 | |
| 104 | static inline size_t gpio_group_index_scaled(const struct pad_community *comm, |
| 105 | unsigned int relative_pad, size_t scale) |
| 106 | { |
| 107 | return gpio_group_index(comm, relative_pad) * scale; |
Aaron Durbin | 65943e1 | 2017-07-19 13:16:51 -0600 | [diff] [blame] | 108 | } |
| 109 | |
| 110 | static inline size_t gpio_within_group(const struct pad_community *comm, |
| 111 | unsigned int relative_pad) |
| 112 | { |
Bora Guvendik | 3f67232 | 2017-11-22 13:48:12 -0800 | [diff] [blame] | 113 | size_t i; |
| 114 | |
| 115 | i = gpio_group_index(comm, relative_pad); |
| 116 | |
| 117 | return relative_pad - comm->groups[i].first_pad; |
Aaron Durbin | 65943e1 | 2017-07-19 13:16:51 -0600 | [diff] [blame] | 118 | } |
| 119 | |
| 120 | static inline uint32_t gpio_bitmask_within_group( |
| 121 | const struct pad_community *comm, |
| 122 | unsigned int relative_pad) |
| 123 | { |
| 124 | return 1U << gpio_within_group(comm, relative_pad); |
| 125 | } |
| 126 | |
Hannah Williams | d28bd0c | 2017-05-17 23:24:22 -0700 | [diff] [blame] | 127 | static const struct pad_community *gpio_get_community(gpio_t pad) |
| 128 | { |
| 129 | size_t gpio_communities; |
| 130 | size_t i; |
| 131 | const struct pad_community *comm; |
| 132 | comm = soc_gpio_get_community(&gpio_communities); |
| 133 | for (i = 0; i < gpio_communities; i++, comm++) { |
| 134 | if (pad >= comm->first_pad && pad <= comm->last_pad) |
| 135 | return comm; |
| 136 | } |
| 137 | printk(BIOS_ERR, "%s pad %d not found\n", __func__, pad); |
| 138 | die("Invalid GPIO pad number\n"); |
| 139 | return NULL; |
| 140 | } |
| 141 | |
| 142 | static void gpio_configure_owner(const struct pad_config *cfg, |
| 143 | const struct pad_community *comm) |
| 144 | { |
Keith Short | cc68c01 | 2019-02-01 16:26:30 -0700 | [diff] [blame] | 145 | uint32_t hostsw_own; |
| 146 | uint16_t hostsw_own_offset; |
Hannah Williams | d28bd0c | 2017-05-17 23:24:22 -0700 | [diff] [blame] | 147 | int pin; |
| 148 | |
Aaron Durbin | 65943e1 | 2017-07-19 13:16:51 -0600 | [diff] [blame] | 149 | pin = relative_pad_in_comm(comm, cfg->pad); |
Hannah Williams | d28bd0c | 2017-05-17 23:24:22 -0700 | [diff] [blame] | 150 | |
Hannah Williams | d28bd0c | 2017-05-17 23:24:22 -0700 | [diff] [blame] | 151 | /* Based on the gpio pin number configure the corresponding bit in |
| 152 | * HOSTSW_OWN register. Value of 0x1 indicates GPIO Driver onwership. |
| 153 | */ |
Keith Short | cc68c01 | 2019-02-01 16:26:30 -0700 | [diff] [blame] | 154 | hostsw_own_offset = comm->host_own_reg_0; |
| 155 | hostsw_own_offset += gpio_group_index_scaled(comm, pin, |
| 156 | sizeof(uint32_t)); |
| 157 | |
| 158 | hostsw_own = pcr_read32(comm->port, hostsw_own_offset); |
| 159 | |
| 160 | /* The 4th bit in pad_config 1 (RO) is used to indicate if the pad |
| 161 | * needs GPIO driver ownership. Set the bit if GPIO driver ownership |
| 162 | * requested, otherwise clear the bit. |
| 163 | */ |
Maxim Polyakov | b2634c1 | 2020-04-10 19:36:42 +0300 | [diff] [blame] | 164 | if (cfg->pad_config[1] & PAD_CFG_OWN_GPIO_DRIVER) |
Keith Short | cc68c01 | 2019-02-01 16:26:30 -0700 | [diff] [blame] | 165 | hostsw_own |= gpio_bitmask_within_group(comm, pin); |
| 166 | else |
| 167 | hostsw_own &= ~gpio_bitmask_within_group(comm, pin); |
| 168 | |
| 169 | pcr_write32(comm->port, hostsw_own_offset, hostsw_own); |
Hannah Williams | d28bd0c | 2017-05-17 23:24:22 -0700 | [diff] [blame] | 170 | } |
| 171 | |
Maulik V Vaghela | 38b8bf0 | 2022-05-06 11:05:21 +0530 | [diff] [blame] | 172 | static void gpi_enable_gpe(const struct pad_config *cfg, |
Maulik V Vaghela | 0485ab6 | 2022-05-10 10:33:04 +0530 | [diff] [blame] | 173 | const struct pad_community *comm, int group, int pin) |
Maulik V Vaghela | 38b8bf0 | 2022-05-06 11:05:21 +0530 | [diff] [blame] | 174 | { |
| 175 | uint16_t en_reg; |
| 176 | uint32_t en_value; |
Maulik V Vaghela | 38b8bf0 | 2022-05-06 11:05:21 +0530 | [diff] [blame] | 177 | |
| 178 | /* Do not configure GPE_EN if PAD is not configured for SCI/wake */ |
| 179 | if (((cfg->pad_config[0]) & PAD_CFG0_ROUTE_SCI) != PAD_CFG0_ROUTE_SCI) |
| 180 | return; |
| 181 | |
| 182 | /* Get comm offset and bit mask to be set as per pin */ |
Maulik V Vaghela | 38b8bf0 | 2022-05-06 11:05:21 +0530 | [diff] [blame] | 183 | en_reg = GPI_GPE_EN_OFFSET(comm, group); |
| 184 | en_value = gpio_bitmask_within_group(comm, pin); |
| 185 | |
| 186 | /* Set enable bits */ |
| 187 | pcr_or32(comm->port, en_reg, en_value); |
| 188 | |
| 189 | if (CONFIG(DEBUG_GPIO)) { |
Elyes Haouas | 9bd9741 | 2023-02-08 11:00:51 +0100 | [diff] [blame] | 190 | printk(BIOS_DEBUG, "GPE_EN[0x%02x, %02zd]: Reg: 0x%x, Value = 0x%x\n", |
| 191 | comm->port, relative_pad_in_comm(comm, cfg->pad), en_reg, |
Maulik V Vaghela | 38b8bf0 | 2022-05-06 11:05:21 +0530 | [diff] [blame] | 192 | pcr_read32(comm->port, en_reg)); |
| 193 | } |
| 194 | } |
| 195 | |
Hannah Williams | d28bd0c | 2017-05-17 23:24:22 -0700 | [diff] [blame] | 196 | static void gpi_enable_smi(const struct pad_config *cfg, |
Maulik V Vaghela | 0485ab6 | 2022-05-10 10:33:04 +0530 | [diff] [blame] | 197 | const struct pad_community *comm, int group, int pin) |
Hannah Williams | d28bd0c | 2017-05-17 23:24:22 -0700 | [diff] [blame] | 198 | { |
Hannah Williams | d28bd0c | 2017-05-17 23:24:22 -0700 | [diff] [blame] | 199 | uint16_t sts_reg; |
| 200 | uint16_t en_reg; |
Michael Niewöhner | c3ab442 | 2020-11-23 15:42:49 +0100 | [diff] [blame] | 201 | uint32_t en_value; |
Hannah Williams | d28bd0c | 2017-05-17 23:24:22 -0700 | [diff] [blame] | 202 | |
| 203 | if (((cfg->pad_config[0]) & PAD_CFG0_ROUTE_SMI) != PAD_CFG0_ROUTE_SMI) |
| 204 | return; |
| 205 | |
Hannah Williams | d28bd0c | 2017-05-17 23:24:22 -0700 | [diff] [blame] | 206 | sts_reg = GPI_SMI_STS_OFFSET(comm, group); |
Michael Niewöhner | c3ab442 | 2020-11-23 15:42:49 +0100 | [diff] [blame] | 207 | en_reg = GPI_SMI_EN_OFFSET(comm, group); |
| 208 | en_value = gpio_bitmask_within_group(comm, pin); |
| 209 | |
| 210 | /* Write back 1 to reset the sts bit */ |
| 211 | pcr_rmw32(comm->port, sts_reg, en_value, 0); |
Hannah Williams | d28bd0c | 2017-05-17 23:24:22 -0700 | [diff] [blame] | 212 | |
| 213 | /* Set enable bits */ |
Michael Niewöhner | c3ab442 | 2020-11-23 15:42:49 +0100 | [diff] [blame] | 214 | pcr_or32(comm->port, en_reg, en_value); |
Hannah Williams | d28bd0c | 2017-05-17 23:24:22 -0700 | [diff] [blame] | 215 | } |
| 216 | |
Michael Niewöhner | 14512f9 | 2020-11-23 15:53:28 +0100 | [diff] [blame] | 217 | static void gpi_enable_nmi(const struct pad_config *cfg, |
Maulik V Vaghela | 0485ab6 | 2022-05-10 10:33:04 +0530 | [diff] [blame] | 218 | const struct pad_community *comm, int group, int pin) |
Michael Niewöhner | 14512f9 | 2020-11-23 15:53:28 +0100 | [diff] [blame] | 219 | { |
| 220 | uint16_t sts_reg; |
| 221 | uint16_t en_reg; |
| 222 | uint32_t en_value; |
Michael Niewöhner | 14512f9 | 2020-11-23 15:53:28 +0100 | [diff] [blame] | 223 | |
| 224 | if (((cfg->pad_config[0]) & PAD_CFG0_ROUTE_NMI) != PAD_CFG0_ROUTE_NMI) |
| 225 | return; |
| 226 | |
| 227 | /* Do not configure NMI if the platform doesn't support it */ |
| 228 | if (!comm->gpi_nmi_sts_reg_0 || !comm->gpi_nmi_en_reg_0) |
| 229 | return; |
| 230 | |
Michael Niewöhner | 14512f9 | 2020-11-23 15:53:28 +0100 | [diff] [blame] | 231 | sts_reg = GPI_NMI_STS_OFFSET(comm, group); |
| 232 | en_reg = GPI_NMI_EN_OFFSET(comm, group); |
| 233 | en_value = gpio_bitmask_within_group(comm, pin); |
| 234 | |
| 235 | /* Write back 1 to reset the sts bit */ |
| 236 | pcr_rmw32(comm->port, sts_reg, en_value, 0); |
| 237 | |
| 238 | /* Set enable bits */ |
| 239 | pcr_or32(comm->port, en_reg, en_value); |
| 240 | } |
| 241 | |
Tim Wawrzynczak | 740cd31 | 2021-02-04 16:33:04 -0700 | [diff] [blame] | 242 | /* 120 GSIs is the default for IOxAPIC */ |
| 243 | static uint32_t gpio_ioapic_irqs_used[120 / (sizeof(uint32_t) * BITS_PER_BYTE) + 1]; |
| 244 | static void set_ioapic_used(uint32_t irq) |
| 245 | { |
| 246 | size_t word_offset = irq / 32; |
| 247 | size_t bit_offset = irq % 32; |
Elyes Haouas | c54a967 | 2023-09-10 10:38:22 +0200 | [diff] [blame] | 248 | assert(word_offset < ARRAY_SIZE(gpio_ioapic_irqs_used)); |
Tim Wawrzynczak | 740cd31 | 2021-02-04 16:33:04 -0700 | [diff] [blame] | 249 | gpio_ioapic_irqs_used[word_offset] |= BIT(bit_offset); |
| 250 | } |
| 251 | |
| 252 | bool gpio_routes_ioapic_irq(uint32_t irq) |
| 253 | { |
| 254 | size_t word_offset = irq / 32; |
| 255 | size_t bit_offset = irq % 32; |
Elyes Haouas | c54a967 | 2023-09-10 10:38:22 +0200 | [diff] [blame] | 256 | assert(word_offset < ARRAY_SIZE(gpio_ioapic_irqs_used)); |
Tim Wawrzynczak | 740cd31 | 2021-02-04 16:33:04 -0700 | [diff] [blame] | 257 | return (gpio_ioapic_irqs_used[word_offset] & BIT(bit_offset)) != 0; |
| 258 | } |
| 259 | |
Hannah Williams | d28bd0c | 2017-05-17 23:24:22 -0700 | [diff] [blame] | 260 | static void gpio_configure_itss(const struct pad_config *cfg, uint16_t port, |
| 261 | uint16_t pad_cfg_offset) |
| 262 | { |
| 263 | /* No ITSS configuration in SMM. */ |
| 264 | if (ENV_SMM) |
| 265 | return; |
| 266 | |
Hannah Williams | d28bd0c | 2017-05-17 23:24:22 -0700 | [diff] [blame] | 267 | int irq; |
| 268 | |
| 269 | /* Set up ITSS polarity if pad is routed to APIC. |
| 270 | * |
| 271 | * The ITSS takes only active high interrupt signals. Therefore, |
| 272 | * if the pad configuration indicates an inversion assume the |
| 273 | * intent is for the ITSS polarity. Before forwarding on the |
| 274 | * request to the APIC there's an inversion setting for how the |
| 275 | * signal is forwarded to the APIC. Honor the inversion setting |
| 276 | * in the GPIO pad configuration so that a hardware active low |
| 277 | * signal looks that way to the APIC (double inversion). |
| 278 | */ |
Ashish Kumar Mishra | 32ebaef | 2024-02-02 20:18:56 +0530 | [diff] [blame] | 279 | if (!(cfg->pad_config[0] & PAD_CFG0_ROUTE_SWAPPED) && |
| 280 | !(cfg->pad_config[0] & PAD_CFG0_ROUTE_IOAPIC)) |
Hannah Williams | d28bd0c | 2017-05-17 23:24:22 -0700 | [diff] [blame] | 281 | return; |
| 282 | |
| 283 | irq = pcr_read32(port, PAD_CFG1_OFFSET(pad_cfg_offset)); |
| 284 | irq &= PAD_CFG1_IRQ_MASK; |
| 285 | if (!irq) { |
| 286 | printk(BIOS_ERR, "GPIO %u doesn't support APIC routing,\n", |
| 287 | cfg->pad); |
| 288 | return; |
| 289 | } |
Tim Wawrzynczak | 740cd31 | 2021-02-04 16:33:04 -0700 | [diff] [blame] | 290 | |
Ashish Kumar Mishra | 32ebaef | 2024-02-02 20:18:56 +0530 | [diff] [blame] | 291 | if (CONFIG(SOC_INTEL_COMMON_BLOCK_GPIO_ITSS_POL_CFG) && |
| 292 | !(cfg->pad_config[0] & PAD_CFG0_ROUTE_SWAPPED)) |
Tim Wawrzynczak | 740cd31 | 2021-02-04 16:33:04 -0700 | [diff] [blame] | 293 | itss_set_irq_polarity(irq, !!(cfg->pad_config[0] & |
| 294 | PAD_CFG0_RX_POL_INVERT)); |
| 295 | |
| 296 | set_ioapic_used(irq); |
Hannah Williams | d28bd0c | 2017-05-17 23:24:22 -0700 | [diff] [blame] | 297 | } |
| 298 | |
| 299 | /* Number of DWx config registers can be different for different SOCs */ |
| 300 | static uint16_t pad_config_offset(const struct pad_community *comm, gpio_t pad) |
| 301 | { |
| 302 | size_t offset; |
| 303 | |
Aaron Durbin | 65943e1 | 2017-07-19 13:16:51 -0600 | [diff] [blame] | 304 | offset = relative_pad_in_comm(comm, pad); |
Hannah Williams | d28bd0c | 2017-05-17 23:24:22 -0700 | [diff] [blame] | 305 | offset *= GPIO_DWx_SIZE(GPIO_NUM_PAD_CFG_REGS); |
| 306 | return offset + comm->pad_cfg_base; |
| 307 | } |
| 308 | |
| 309 | static uint32_t gpio_pad_reset_config_override(const struct pad_community *comm, |
| 310 | uint32_t config_value) |
| 311 | { |
| 312 | const struct reset_mapping *rst_map = comm->reset_map; |
| 313 | int i; |
| 314 | |
| 315 | if (rst_map == NULL || comm->num_reset_vals == 0) |
| 316 | return config_value;/* Logical reset values equal chipset |
| 317 | values */ |
| 318 | for (i = 0; i < comm->num_reset_vals; i++, rst_map++) { |
Aaron Durbin | b5a5aa6 | 2017-07-14 17:29:29 -0600 | [diff] [blame] | 319 | if ((config_value & PAD_CFG0_RESET_MASK) == rst_map->logical) { |
Hannah Williams | d28bd0c | 2017-05-17 23:24:22 -0700 | [diff] [blame] | 320 | config_value &= ~PAD_CFG0_RESET_MASK; |
| 321 | config_value |= rst_map->chipset; |
| 322 | return config_value; |
| 323 | } |
| 324 | } |
| 325 | printk(BIOS_ERR, "%s: Logical to Chipset mapping not found\n", |
| 326 | __func__); |
| 327 | return config_value; |
| 328 | } |
| 329 | |
| 330 | static const int mask[4] = { |
| 331 | PAD_DW0_MASK, PAD_DW1_MASK, PAD_DW2_MASK, PAD_DW3_MASK |
| 332 | }; |
| 333 | |
| 334 | static void gpio_configure_pad(const struct pad_config *cfg) |
| 335 | { |
Maulik V Vaghela | cdc1de7 | 2022-05-31 20:24:42 +0530 | [diff] [blame] | 336 | const struct pad_community *comm; |
Hannah Williams | d28bd0c | 2017-05-17 23:24:22 -0700 | [diff] [blame] | 337 | uint16_t config_offset; |
| 338 | uint32_t pad_conf, soc_pad_conf; |
Maulik V Vaghela | 0485ab6 | 2022-05-10 10:33:04 +0530 | [diff] [blame] | 339 | int i, pin, group; |
Hannah Williams | d28bd0c | 2017-05-17 23:24:22 -0700 | [diff] [blame] | 340 | |
Maulik V Vaghela | cdc1de7 | 2022-05-31 20:24:42 +0530 | [diff] [blame] | 341 | if (!cfg) { |
| 342 | printk(BIOS_ERR, "%s: cfg value is NULL\n", __func__); |
| 343 | return; |
| 344 | } |
| 345 | |
| 346 | comm = gpio_get_community(cfg->pad); |
| 347 | if (!comm) { |
| 348 | printk(BIOS_ERR, "%s: Could not find community for pad: 0x%x\n", |
| 349 | __func__, cfg->pad); |
| 350 | return; |
| 351 | } |
| 352 | |
Hannah Williams | d28bd0c | 2017-05-17 23:24:22 -0700 | [diff] [blame] | 353 | config_offset = pad_config_offset(comm, cfg->pad); |
Maulik V Vaghela | 0485ab6 | 2022-05-10 10:33:04 +0530 | [diff] [blame] | 354 | pin = relative_pad_in_comm(comm, cfg->pad); |
| 355 | group = gpio_group_index(comm, pin); |
| 356 | |
Hannah Williams | d28bd0c | 2017-05-17 23:24:22 -0700 | [diff] [blame] | 357 | for (i = 0; i < GPIO_NUM_PAD_CFG_REGS; i++) { |
| 358 | pad_conf = pcr_read32(comm->port, |
| 359 | PAD_CFG_OFFSET(config_offset, i)); |
| 360 | |
| 361 | soc_pad_conf = cfg->pad_config[i]; |
| 362 | if (i == 0) |
| 363 | soc_pad_conf = gpio_pad_reset_config_override(comm, |
| 364 | soc_pad_conf); |
| 365 | soc_pad_conf &= mask[i]; |
| 366 | soc_pad_conf |= pad_conf & ~mask[i]; |
| 367 | |
Kane Chen | 223ddc2 | 2019-02-12 21:14:13 +0800 | [diff] [blame] | 368 | /* Patch GPIO settings for SoC specifically */ |
| 369 | soc_pad_conf = soc_gpio_pad_config_fixup(cfg, i, soc_pad_conf); |
| 370 | |
Julius Werner | cd49cce | 2019-03-05 16:53:33 -0800 | [diff] [blame] | 371 | if (CONFIG(DEBUG_GPIO)) |
Hannah Williams | d28bd0c | 2017-05-17 23:24:22 -0700 | [diff] [blame] | 372 | printk(BIOS_DEBUG, |
Maulik V Vaghela | 0485ab6 | 2022-05-10 10:33:04 +0530 | [diff] [blame] | 373 | "gpio_padcfg [0x%02x, %02d] DW%d [0x%08x : 0x%08x" |
Hannah Williams | d28bd0c | 2017-05-17 23:24:22 -0700 | [diff] [blame] | 374 | " : 0x%08x]\n", |
Maulik V Vaghela | 0485ab6 | 2022-05-10 10:33:04 +0530 | [diff] [blame] | 375 | comm->port, pin, i, |
Hannah Williams | d28bd0c | 2017-05-17 23:24:22 -0700 | [diff] [blame] | 376 | pad_conf,/* old value */ |
| 377 | cfg->pad_config[i],/* value passed from gpio table */ |
| 378 | soc_pad_conf);/*new value*/ |
| 379 | pcr_write32(comm->port, PAD_CFG_OFFSET(config_offset, i), |
| 380 | soc_pad_conf); |
| 381 | } |
Maulik V Vaghela | 0485ab6 | 2022-05-10 10:33:04 +0530 | [diff] [blame] | 382 | |
Hannah Williams | d28bd0c | 2017-05-17 23:24:22 -0700 | [diff] [blame] | 383 | gpio_configure_itss(cfg, comm->port, config_offset); |
| 384 | gpio_configure_owner(cfg, comm); |
Maulik V Vaghela | 0485ab6 | 2022-05-10 10:33:04 +0530 | [diff] [blame] | 385 | gpi_enable_smi(cfg, comm, group, pin); |
| 386 | gpi_enable_nmi(cfg, comm, group, pin); |
| 387 | gpi_enable_gpe(cfg, comm, group, pin); |
Subrata Banik | 7e8a0e6 | 2022-01-04 20:29:27 +0000 | [diff] [blame] | 388 | if (cfg->lock_action) |
| 389 | gpio_lock_pad(cfg->pad, cfg->lock_action); |
Hannah Williams | d28bd0c | 2017-05-17 23:24:22 -0700 | [diff] [blame] | 390 | } |
| 391 | |
| 392 | void gpio_configure_pads(const struct pad_config *cfg, size_t num_pads) |
| 393 | { |
| 394 | size_t i; |
| 395 | |
| 396 | for (i = 0; i < num_pads; i++) |
| 397 | gpio_configure_pad(cfg + i); |
| 398 | } |
| 399 | |
Furquan Shaikh | 4a12a56 | 2018-07-25 14:12:58 -0700 | [diff] [blame] | 400 | /* |
| 401 | * This functions checks to see if there is an override config present for the |
| 402 | * provided pad_config. If no override config is present, then the input config |
| 403 | * is returned. Else, it returns the override config. |
| 404 | */ |
| 405 | static const struct pad_config *gpio_get_config(const struct pad_config *c, |
| 406 | const struct pad_config *override_cfg_table, |
| 407 | size_t num) |
| 408 | { |
| 409 | size_t i; |
| 410 | |
| 411 | if (override_cfg_table == NULL) |
| 412 | return c; |
| 413 | |
| 414 | for (i = 0; i < num; i++) { |
| 415 | if (c->pad == override_cfg_table[i].pad) |
| 416 | return override_cfg_table + i; |
| 417 | } |
| 418 | |
| 419 | return c; |
| 420 | } |
| 421 | |
| 422 | void gpio_configure_pads_with_override(const struct pad_config *base_cfg, |
| 423 | size_t base_num_pads, |
| 424 | const struct pad_config *override_cfg, |
| 425 | size_t override_num_pads) |
| 426 | { |
| 427 | size_t i; |
| 428 | const struct pad_config *c; |
| 429 | |
| 430 | for (i = 0; i < base_num_pads; i++) { |
| 431 | c = gpio_get_config(base_cfg + i, override_cfg, |
| 432 | override_num_pads); |
| 433 | gpio_configure_pad(c); |
| 434 | } |
| 435 | } |
| 436 | |
Eric Lai | ce026c9 | 2022-05-27 09:11:27 +0800 | [diff] [blame] | 437 | struct pad_config *new_padbased_table(void) |
| 438 | { |
| 439 | struct pad_config *padbased_table; |
| 440 | padbased_table = malloc(sizeof(struct pad_config) * TOTAL_PADS); |
| 441 | memset(padbased_table, 0, sizeof(struct pad_config) * TOTAL_PADS); |
| 442 | |
| 443 | return padbased_table; |
| 444 | } |
| 445 | |
| 446 | void gpio_padbased_override(struct pad_config *padbased_table, |
| 447 | const struct pad_config *override_cfg, |
| 448 | size_t override_num_pads) |
| 449 | { |
| 450 | for (size_t i = 0; i < override_num_pads; i++) { |
| 451 | /* Prevent overflow hack */ |
| 452 | ASSERT(override_cfg[i].pad < TOTAL_PADS); |
| 453 | padbased_table[override_cfg[i].pad] = override_cfg[i]; |
| 454 | } |
| 455 | } |
| 456 | |
| 457 | void gpio_configure_pads_with_padbased(struct pad_config *padbased_table) |
| 458 | { |
| 459 | size_t i; |
| 460 | const struct pad_config *cfg = padbased_table; |
| 461 | for (i = 0; i < TOTAL_PADS; i++) { |
| 462 | /* Consider unmapped pin as default setting, skip */ |
| 463 | if (cfg[i].pad == 0 && cfg[i].pad_config[0] == 0) |
| 464 | continue; |
| 465 | gpio_configure_pad(&cfg[i]); |
| 466 | } |
| 467 | } |
| 468 | |
Hannah Williams | d28bd0c | 2017-05-17 23:24:22 -0700 | [diff] [blame] | 469 | void *gpio_dwx_address(const gpio_t pad) |
| 470 | { |
| 471 | /* Calculate Address of DW0 register for given GPIO |
| 472 | * pad - GPIO number |
| 473 | * returns - address of GPIO |
| 474 | */ |
| 475 | const struct pad_community *comm = gpio_get_community(pad); |
| 476 | uint16_t config_offset; |
| 477 | |
| 478 | config_offset = pad_config_offset(comm, pad); |
| 479 | return pcr_reg_address(comm->port, config_offset); |
| 480 | } |
| 481 | |
| 482 | uint8_t gpio_get_pad_portid(const gpio_t pad) |
| 483 | { |
| 484 | /* Get the port id of given pad |
| 485 | * pad - GPIO number |
| 486 | * returns - given pad port id |
| 487 | */ |
| 488 | const struct pad_community *comm = gpio_get_community(pad); |
| 489 | return comm->port; |
| 490 | } |
| 491 | |
| 492 | void gpio_input_pulldown(gpio_t gpio) |
| 493 | { |
| 494 | struct pad_config cfg = PAD_CFG_GPI(gpio, DN_20K, DEEP); |
| 495 | gpio_configure_pad(&cfg); |
| 496 | } |
| 497 | |
| 498 | void gpio_input_pullup(gpio_t gpio) |
| 499 | { |
| 500 | struct pad_config cfg = PAD_CFG_GPI(gpio, UP_20K, DEEP); |
| 501 | gpio_configure_pad(&cfg); |
| 502 | } |
| 503 | |
| 504 | void gpio_input(gpio_t gpio) |
| 505 | { |
| 506 | struct pad_config cfg = PAD_CFG_GPI(gpio, NONE, DEEP); |
| 507 | gpio_configure_pad(&cfg); |
| 508 | } |
| 509 | |
| 510 | void gpio_output(gpio_t gpio, int value) |
| 511 | { |
| 512 | struct pad_config cfg = PAD_CFG_GPO(gpio, value, DEEP); |
| 513 | gpio_configure_pad(&cfg); |
| 514 | } |
| 515 | |
| 516 | int gpio_get(gpio_t gpio_num) |
| 517 | { |
| 518 | const struct pad_community *comm = gpio_get_community(gpio_num); |
| 519 | uint16_t config_offset; |
| 520 | uint32_t reg; |
| 521 | |
| 522 | config_offset = pad_config_offset(comm, gpio_num); |
| 523 | reg = pcr_read32(comm->port, config_offset); |
| 524 | |
| 525 | return !!(reg & PAD_CFG0_RX_STATE); |
| 526 | } |
| 527 | |
Cliff Huang | 546e093 | 2023-01-19 21:38:56 -0800 | [diff] [blame] | 528 | int gpio_tx_get(gpio_t gpio_num) |
| 529 | { |
| 530 | const struct pad_community *comm = gpio_get_community(gpio_num); |
| 531 | uint16_t config_offset; |
| 532 | uint32_t reg; |
| 533 | |
| 534 | config_offset = pad_config_offset(comm, gpio_num); |
| 535 | reg = pcr_read32(comm->port, config_offset); |
| 536 | |
| 537 | return !!(reg & PAD_CFG0_TX_STATE); |
| 538 | } |
| 539 | |
Subrata Banik | fe678cb | 2022-01-05 18:46:02 +0000 | [diff] [blame] | 540 | static void |
| 541 | gpio_pad_config_lock_using_sbi(const struct gpio_lock_config *pad_info, |
| 542 | uint8_t pid, uint16_t offset, const uint32_t bit_mask) |
Aseda Aboagye | e58e6f2 | 2021-06-15 23:11:41 -0700 | [diff] [blame] | 543 | { |
Subrata Banik | fe678cb | 2022-01-05 18:46:02 +0000 | [diff] [blame] | 544 | int status; |
| 545 | uint8_t response; |
| 546 | uint32_t data; |
| 547 | struct pcr_sbi_msg msg = { |
| 548 | .pid = pid, |
| 549 | .offset = offset, |
| 550 | .opcode = GPIO_LOCK_UNLOCK, |
| 551 | .is_posted = false, |
| 552 | .fast_byte_enable = 0xf, |
| 553 | .bar = 0, |
| 554 | .fid = 0, |
| 555 | }; |
| 556 | |
Subrata Banik | 7e8a0e6 | 2022-01-04 20:29:27 +0000 | [diff] [blame] | 557 | if (!(pad_info->lock_action & GPIO_LOCK_FULL)) { |
| 558 | printk(BIOS_ERR, "%s: Error: no lock_action specified for pad %d!\n", |
Subrata Banik | fe678cb | 2022-01-05 18:46:02 +0000 | [diff] [blame] | 559 | __func__, pad_info->pad); |
| 560 | return; |
Nick Vaccaro | b6f29c9 | 2021-10-12 17:26:52 -0700 | [diff] [blame] | 561 | } |
| 562 | |
Subrata Banik | 7e8a0e6 | 2022-01-04 20:29:27 +0000 | [diff] [blame] | 563 | if ((pad_info->lock_action & GPIO_LOCK_CONFIG) == GPIO_LOCK_CONFIG) { |
Subrata Banik | fe678cb | 2022-01-05 18:46:02 +0000 | [diff] [blame] | 564 | if (CONFIG(DEBUG_GPIO)) |
| 565 | printk(BIOS_INFO, "%s: Locking pad %d configuration\n", |
| 566 | __func__, pad_info->pad); |
| 567 | data = pcr_read32(pid, offset) | bit_mask; |
| 568 | status = pcr_execute_sideband_msg(PCH_DEV_P2SB, &msg, &data, &response); |
| 569 | if (status || response) |
| 570 | printk(BIOS_ERR, "Failed to lock GPIO PAD, response = %d\n", response); |
| 571 | } |
| 572 | |
Subrata Banik | 7e8a0e6 | 2022-01-04 20:29:27 +0000 | [diff] [blame] | 573 | if ((pad_info->lock_action & GPIO_LOCK_TX) == GPIO_LOCK_TX) { |
Subrata Banik | fe678cb | 2022-01-05 18:46:02 +0000 | [diff] [blame] | 574 | if (CONFIG(DEBUG_GPIO)) |
| 575 | printk(BIOS_INFO, "%s: Locking pad %d Tx state\n", |
| 576 | __func__, pad_info->pad); |
| 577 | offset += sizeof(uint32_t); |
| 578 | data = pcr_read32(pid, offset) | bit_mask; |
| 579 | msg.offset = offset; |
| 580 | status = pcr_execute_sideband_msg(PCH_DEV_P2SB, &msg, &data, &response); |
| 581 | if (status || response) |
| 582 | printk(BIOS_ERR, "Failed to lock GPIO PAD Tx state, response = %d\n", |
| 583 | response); |
| 584 | } |
Nick Vaccaro | b6f29c9 | 2021-10-12 17:26:52 -0700 | [diff] [blame] | 585 | } |
| 586 | |
| 587 | int gpio_lock_pads(const struct gpio_lock_config *pad_list, const size_t count) |
| 588 | { |
| 589 | const struct pad_community *comm; |
Nick Vaccaro | b6f29c9 | 2021-10-12 17:26:52 -0700 | [diff] [blame] | 590 | uint16_t offset; |
| 591 | size_t rel_pad; |
Nick Vaccaro | b6f29c9 | 2021-10-12 17:26:52 -0700 | [diff] [blame] | 592 | gpio_t pad; |
Aseda Aboagye | e58e6f2 | 2021-06-15 23:11:41 -0700 | [diff] [blame] | 593 | |
Subrata Banik | 724fc89 | 2022-01-04 21:16:33 +0000 | [diff] [blame] | 594 | if (!CONFIG(SOC_INTEL_COMMON_BLOCK_SMM_LOCK_GPIO_PADS)) |
| 595 | return -1; |
| 596 | |
Aseda Aboagye | e58e6f2 | 2021-06-15 23:11:41 -0700 | [diff] [blame] | 597 | /* |
| 598 | * FSP-S will unlock all the GPIO pads and hide the P2SB device. With |
| 599 | * the device hidden, we will not be able to send the sideband interface |
| 600 | * message to lock the GPIO configuration. Therefore, we need to unhide |
| 601 | * the P2SB device which can only be done in SMM requiring that this |
| 602 | * function is called from SMM. |
| 603 | */ |
| 604 | if (!ENV_SMM) { |
| 605 | printk(BIOS_ERR, "%s: Error: must be called from SMM!\n", __func__); |
| 606 | return -1; |
| 607 | } |
| 608 | |
Nick Vaccaro | b6f29c9 | 2021-10-12 17:26:52 -0700 | [diff] [blame] | 609 | if ((pad_list == NULL) || (count == 0)) { |
| 610 | printk(BIOS_ERR, "%s: Error: pad_list null or count = 0!\n", __func__); |
Aseda Aboagye | e58e6f2 | 2021-06-15 23:11:41 -0700 | [diff] [blame] | 611 | return -1; |
| 612 | } |
| 613 | |
Aseda Aboagye | e58e6f2 | 2021-06-15 23:11:41 -0700 | [diff] [blame] | 614 | p2sb_unhide(); |
| 615 | |
Nick Vaccaro | b6f29c9 | 2021-10-12 17:26:52 -0700 | [diff] [blame] | 616 | for (int x = 0; x < count; x++) { |
Subrata Banik | 0dc0772 | 2022-01-04 19:49:24 +0000 | [diff] [blame] | 617 | pad = pad_list[x].pad; |
Nick Vaccaro | b6f29c9 | 2021-10-12 17:26:52 -0700 | [diff] [blame] | 618 | comm = gpio_get_community(pad); |
| 619 | rel_pad = relative_pad_in_comm(comm, pad); |
| 620 | offset = comm->pad_cfg_lock_offset; |
| 621 | if (!offset) { |
| 622 | printk(BIOS_ERR, "%s: Error: offset not defined for pad %d!\n", |
| 623 | __func__, pad); |
| 624 | continue; |
| 625 | } |
Subrata Banik | 77885136 | 2022-01-11 12:20:24 +0530 | [diff] [blame] | 626 | /* PADCFGLOCK and PADCFGLOCKTX registers for each community are contiguous */ |
Nick Vaccaro | b6f29c9 | 2021-10-12 17:26:52 -0700 | [diff] [blame] | 627 | offset += gpio_group_index_scaled(comm, rel_pad, 2 * sizeof(uint32_t)); |
| 628 | |
Subrata Banik | 6a4f573 | 2022-01-11 10:14:26 +0000 | [diff] [blame] | 629 | const uint32_t bit_mask = gpio_bitmask_within_group(comm, rel_pad); |
Nick Vaccaro | b6f29c9 | 2021-10-12 17:26:52 -0700 | [diff] [blame] | 630 | |
Subrata Banik | fe678cb | 2022-01-05 18:46:02 +0000 | [diff] [blame] | 631 | gpio_pad_config_lock_using_sbi(&pad_list[x], comm->port, offset, bit_mask); |
Aseda Aboagye | e58e6f2 | 2021-06-15 23:11:41 -0700 | [diff] [blame] | 632 | } |
| 633 | |
| 634 | p2sb_hide(); |
Subrata Banik | fe678cb | 2022-01-05 18:46:02 +0000 | [diff] [blame] | 635 | } |
Nick Vaccaro | b6f29c9 | 2021-10-12 17:26:52 -0700 | [diff] [blame] | 636 | |
Subrata Banik | fe678cb | 2022-01-05 18:46:02 +0000 | [diff] [blame] | 637 | static void |
| 638 | gpio_pad_config_lock_using_pcr(const struct gpio_lock_config *pad_info, |
| 639 | uint8_t pid, uint16_t offset, const uint32_t bit_mask) |
| 640 | { |
Subrata Banik | 7e8a0e6 | 2022-01-04 20:29:27 +0000 | [diff] [blame] | 641 | if ((pad_info->lock_action & GPIO_LOCK_CONFIG) == GPIO_LOCK_CONFIG) { |
Subrata Banik | fe678cb | 2022-01-05 18:46:02 +0000 | [diff] [blame] | 642 | if (CONFIG(DEBUG_GPIO)) |
| 643 | printk(BIOS_INFO, "%s: Locking pad %d configuration\n", |
| 644 | __func__, pad_info->pad); |
| 645 | pcr_or32(pid, offset, bit_mask); |
| 646 | } |
| 647 | |
Subrata Banik | 7e8a0e6 | 2022-01-04 20:29:27 +0000 | [diff] [blame] | 648 | if ((pad_info->lock_action & GPIO_LOCK_TX) == GPIO_LOCK_TX) { |
Subrata Banik | fe678cb | 2022-01-05 18:46:02 +0000 | [diff] [blame] | 649 | if (CONFIG(DEBUG_GPIO)) |
| 650 | printk(BIOS_INFO, "%s: Locking pad %d TX state\n", |
| 651 | __func__, pad_info->pad); |
| 652 | pcr_or32(pid, offset + sizeof(uint32_t), bit_mask); |
| 653 | } |
| 654 | } |
| 655 | |
| 656 | static int gpio_non_smm_lock_pad(const struct gpio_lock_config *pad_info) |
| 657 | { |
| 658 | const struct pad_community *comm = gpio_get_community(pad_info->pad); |
| 659 | uint16_t offset; |
| 660 | size_t rel_pad; |
| 661 | |
| 662 | if (!pad_info) { |
| 663 | printk(BIOS_ERR, "%s: Error: pad_info is null!\n", __func__); |
| 664 | return -1; |
| 665 | } |
| 666 | |
| 667 | if (cpu_soc_is_in_untrusted_mode()) { |
| 668 | printk(BIOS_ERR, "%s: Error: IA Untrusted Mode enabled, can't lock pad!\n", |
| 669 | __func__); |
| 670 | return -1; |
| 671 | } |
| 672 | |
| 673 | rel_pad = relative_pad_in_comm(comm, pad_info->pad); |
| 674 | offset = comm->pad_cfg_lock_offset; |
| 675 | if (!offset) { |
| 676 | printk(BIOS_ERR, "%s: Error: offset not defined for pad %d!\n", |
| 677 | __func__, pad_info->pad); |
| 678 | return -1; |
| 679 | } |
| 680 | |
| 681 | /* PADCFGLOCK and PADCFGLOCKTX registers for each community are contiguous */ |
| 682 | offset += gpio_group_index_scaled(comm, rel_pad, 2 * sizeof(uint32_t)); |
| 683 | const uint32_t bit_mask = gpio_bitmask_within_group(comm, rel_pad); |
| 684 | |
| 685 | if (CONFIG(SOC_INTEL_COMMON_BLOCK_GPIO_LOCK_USING_PCR)) { |
| 686 | if (CONFIG(DEBUG_GPIO)) |
| 687 | printk(BIOS_INFO, "Locking pad configuration using PCR\n"); |
| 688 | gpio_pad_config_lock_using_pcr(pad_info, comm->port, offset, bit_mask); |
| 689 | } else if (CONFIG(SOC_INTEL_COMMON_BLOCK_GPIO_LOCK_USING_SBI)) { |
| 690 | if (CONFIG(DEBUG_GPIO)) |
| 691 | printk(BIOS_INFO, "Locking pad configuration using SBI\n"); |
| 692 | gpio_pad_config_lock_using_sbi(pad_info, comm->port, offset, bit_mask); |
| 693 | } else { |
| 694 | printk(BIOS_ERR, "%s: Error: No pad configuration lock method is selected!\n", |
| 695 | __func__); |
| 696 | } |
| 697 | |
| 698 | return 0; |
Nick Vaccaro | b6f29c9 | 2021-10-12 17:26:52 -0700 | [diff] [blame] | 699 | } |
| 700 | |
Subrata Banik | 7e8a0e6 | 2022-01-04 20:29:27 +0000 | [diff] [blame] | 701 | int gpio_lock_pad(const gpio_t pad, enum gpio_lock_action lock_action) |
Nick Vaccaro | b6f29c9 | 2021-10-12 17:26:52 -0700 | [diff] [blame] | 702 | { |
Subrata Banik | 5e84a42 | 2022-01-27 19:55:10 +0530 | [diff] [blame] | 703 | /* Skip locking GPIO PAD in early stages or in recovery mode */ |
| 704 | if (ENV_ROMSTAGE_OR_BEFORE || vboot_recovery_mode_enabled()) |
Subrata Banik | fe678cb | 2022-01-05 18:46:02 +0000 | [diff] [blame] | 705 | return -1; |
| 706 | |
Nick Vaccaro | b6f29c9 | 2021-10-12 17:26:52 -0700 | [diff] [blame] | 707 | const struct gpio_lock_config pads = { |
Subrata Banik | 0dc0772 | 2022-01-04 19:49:24 +0000 | [diff] [blame] | 708 | .pad = pad, |
Subrata Banik | 7e8a0e6 | 2022-01-04 20:29:27 +0000 | [diff] [blame] | 709 | .lock_action = lock_action |
Nick Vaccaro | b6f29c9 | 2021-10-12 17:26:52 -0700 | [diff] [blame] | 710 | }; |
| 711 | |
Subrata Banik | fe678cb | 2022-01-05 18:46:02 +0000 | [diff] [blame] | 712 | if (!ENV_SMM && !CONFIG(SOC_INTEL_COMMON_BLOCK_SMM_LOCK_GPIO_PADS)) |
| 713 | return gpio_non_smm_lock_pad(&pads); |
| 714 | |
Nick Vaccaro | b6f29c9 | 2021-10-12 17:26:52 -0700 | [diff] [blame] | 715 | return gpio_lock_pads(&pads, 1); |
Aseda Aboagye | e58e6f2 | 2021-06-15 23:11:41 -0700 | [diff] [blame] | 716 | } |
| 717 | |
Hannah Williams | d28bd0c | 2017-05-17 23:24:22 -0700 | [diff] [blame] | 718 | void gpio_set(gpio_t gpio_num, int value) |
| 719 | { |
| 720 | const struct pad_community *comm = gpio_get_community(gpio_num); |
| 721 | uint16_t config_offset; |
| 722 | |
| 723 | config_offset = pad_config_offset(comm, gpio_num); |
| 724 | pcr_rmw32(comm->port, config_offset, |
| 725 | ~PAD_CFG0_TX_STATE, (!!value & PAD_CFG0_TX_STATE)); |
| 726 | } |
| 727 | |
| 728 | uint16_t gpio_acpi_pin(gpio_t gpio_num) |
| 729 | { |
Duncan Laurie | 28e8ae5 | 2018-12-10 11:19:36 -0800 | [diff] [blame] | 730 | const struct pad_community *comm; |
| 731 | size_t group, pin; |
| 732 | |
Julius Werner | cd49cce | 2019-03-05 16:53:33 -0800 | [diff] [blame] | 733 | if (CONFIG(SOC_INTEL_COMMON_BLOCK_GPIO_MULTI_ACPI_DEVICES)) |
Duncan Laurie | 28e8ae5 | 2018-12-10 11:19:36 -0800 | [diff] [blame] | 734 | return relative_pad_in_comm(gpio_get_community(gpio_num), |
| 735 | gpio_num); |
| 736 | |
| 737 | comm = gpio_get_community(gpio_num); |
| 738 | pin = relative_pad_in_comm(comm, gpio_num); |
| 739 | group = gpio_group_index(comm, pin); |
| 740 | |
| 741 | /* If pad base is not set then use GPIO number as ACPI pin number. */ |
| 742 | if (comm->groups[group].acpi_pad_base == PAD_BASE_NONE) |
Aaron Durbin | aa2504a | 2017-07-14 16:53:49 -0600 | [diff] [blame] | 743 | return gpio_num; |
| 744 | |
Duncan Laurie | 28e8ae5 | 2018-12-10 11:19:36 -0800 | [diff] [blame] | 745 | /* |
| 746 | * If this group has a non-zero pad base then compute the ACPI pin |
| 747 | * number from the pad base and the relative pad in the group. |
| 748 | */ |
| 749 | return comm->groups[group].acpi_pad_base + gpio_within_group(comm, pin); |
Hannah Williams | d28bd0c | 2017-05-17 23:24:22 -0700 | [diff] [blame] | 750 | } |
| 751 | |
| 752 | static void print_gpi_status(const struct gpi_status *sts) |
| 753 | { |
| 754 | int i; |
| 755 | int group; |
| 756 | int index; |
| 757 | int bit_set; |
| 758 | int num_groups; |
| 759 | int abs_bit; |
| 760 | size_t gpio_communities; |
| 761 | const struct pad_community *comm; |
| 762 | |
| 763 | comm = soc_gpio_get_community(&gpio_communities); |
| 764 | for (i = 0; i < gpio_communities; i++) { |
| 765 | num_groups = comm->num_gpi_regs; |
| 766 | index = comm->gpi_status_offset; |
| 767 | for (group = 0; group < num_groups; group++, index++) { |
| 768 | for (bit_set = comm->max_pads_per_group - 1; |
| 769 | bit_set >= 0; bit_set--) { |
| 770 | if (!(sts->grp[index] & (1 << bit_set))) |
| 771 | continue; |
| 772 | |
| 773 | abs_bit = bit_set; |
| 774 | abs_bit += group * comm->max_pads_per_group; |
| 775 | printk(BIOS_DEBUG, "%s %d\n", comm->name, |
| 776 | abs_bit); |
| 777 | } |
| 778 | } |
| 779 | comm++; |
| 780 | } |
| 781 | } |
| 782 | |
| 783 | void gpi_clear_get_smi_status(struct gpi_status *sts) |
| 784 | { |
| 785 | int i; |
| 786 | int group; |
| 787 | int index; |
| 788 | uint32_t sts_value; |
| 789 | uint32_t en_value; |
| 790 | size_t gpio_communities; |
| 791 | int num_groups; |
| 792 | const struct pad_community *comm; |
| 793 | |
| 794 | comm = soc_gpio_get_community(&gpio_communities); |
| 795 | for (i = 0; i < gpio_communities; i++) { |
| 796 | num_groups = comm->num_gpi_regs; |
| 797 | index = comm->gpi_status_offset; |
| 798 | for (group = 0; group < num_groups; group++, index++) { |
| 799 | sts_value = pcr_read32(comm->port, |
| 800 | GPI_SMI_STS_OFFSET(comm, group)); |
| 801 | en_value = pcr_read32(comm->port, |
| 802 | GPI_SMI_EN_OFFSET(comm, group)); |
| 803 | sts->grp[index] = sts_value & en_value; |
| 804 | /* Clear the set status bits. */ |
| 805 | pcr_write32(comm->port, GPI_SMI_STS_OFFSET(comm, |
| 806 | group), sts->grp[index]); |
| 807 | } |
| 808 | comm++; |
| 809 | } |
| 810 | |
Julius Werner | cd49cce | 2019-03-05 16:53:33 -0800 | [diff] [blame] | 811 | if (CONFIG(DEBUG_SMI)) |
Hannah Williams | d28bd0c | 2017-05-17 23:24:22 -0700 | [diff] [blame] | 812 | print_gpi_status(sts); |
Hannah Williams | d28bd0c | 2017-05-17 23:24:22 -0700 | [diff] [blame] | 813 | } |
| 814 | |
| 815 | int gpi_status_get(const struct gpi_status *sts, gpio_t pad) |
| 816 | { |
| 817 | uint8_t sts_index; |
| 818 | const struct pad_community *comm = gpio_get_community(pad); |
| 819 | |
Aaron Durbin | 65943e1 | 2017-07-19 13:16:51 -0600 | [diff] [blame] | 820 | pad = relative_pad_in_comm(comm, pad); |
Aaron Durbin | ac8e4db | 2017-07-19 10:46:46 -0600 | [diff] [blame] | 821 | sts_index = comm->gpi_status_offset; |
Aaron Durbin | 65943e1 | 2017-07-19 13:16:51 -0600 | [diff] [blame] | 822 | sts_index += gpio_group_index(comm, pad); |
Hannah Williams | d28bd0c | 2017-05-17 23:24:22 -0700 | [diff] [blame] | 823 | |
Aaron Durbin | 65943e1 | 2017-07-19 13:16:51 -0600 | [diff] [blame] | 824 | return !!(sts->grp[sts_index] & gpio_bitmask_within_group(comm, pad)); |
Hannah Williams | d28bd0c | 2017-05-17 23:24:22 -0700 | [diff] [blame] | 825 | } |
| 826 | |
| 827 | static int gpio_route_pmc_gpio_gpe(int pmc_gpe_num) |
| 828 | { |
| 829 | size_t num_routes; |
| 830 | const struct pmc_to_gpio_route *routes; |
| 831 | int i; |
| 832 | |
| 833 | routes = soc_pmc_gpio_routes(&num_routes); |
Elyes Haouas | c54a967 | 2023-09-10 10:38:22 +0200 | [diff] [blame] | 834 | assert(routes != NULL); |
Hannah Williams | d28bd0c | 2017-05-17 23:24:22 -0700 | [diff] [blame] | 835 | for (i = 0; i < num_routes; i++, routes++) { |
| 836 | if (pmc_gpe_num == routes->pmc) |
| 837 | return routes->gpio; |
| 838 | } |
| 839 | return -1; |
| 840 | } |
| 841 | |
| 842 | void gpio_route_gpe(uint8_t gpe0b, uint8_t gpe0c, uint8_t gpe0d) |
| 843 | { |
| 844 | int i; |
| 845 | uint32_t misccfg_mask; |
| 846 | uint32_t misccfg_value; |
| 847 | int ret; |
| 848 | size_t gpio_communities; |
| 849 | const struct pad_community *comm; |
| 850 | |
| 851 | /* Get the group here for community specific MISCCFG register. |
| 852 | * If any of these returns -1 then there is some error in devicetree |
| 853 | * where the group is probably hardcoded and does not comply with the |
| 854 | * PMC group defines. So we return from here and MISCFG is set to |
| 855 | * default. |
| 856 | */ |
| 857 | ret = gpio_route_pmc_gpio_gpe(gpe0b); |
| 858 | if (ret == -1) |
| 859 | return; |
| 860 | gpe0b = ret; |
| 861 | |
| 862 | ret = gpio_route_pmc_gpio_gpe(gpe0c); |
| 863 | if (ret == -1) |
| 864 | return; |
| 865 | gpe0c = ret; |
| 866 | |
| 867 | ret = gpio_route_pmc_gpio_gpe(gpe0d); |
| 868 | if (ret == -1) |
| 869 | return; |
| 870 | gpe0d = ret; |
| 871 | |
| 872 | misccfg_value = gpe0b << MISCCFG_GPE0_DW0_SHIFT; |
| 873 | misccfg_value |= gpe0c << MISCCFG_GPE0_DW1_SHIFT; |
| 874 | misccfg_value |= gpe0d << MISCCFG_GPE0_DW2_SHIFT; |
| 875 | |
| 876 | /* Program GPIO_MISCCFG */ |
| 877 | misccfg_mask = ~(MISCCFG_GPE0_DW2_MASK | |
| 878 | MISCCFG_GPE0_DW1_MASK | |
| 879 | MISCCFG_GPE0_DW0_MASK); |
| 880 | |
Julius Werner | cd49cce | 2019-03-05 16:53:33 -0800 | [diff] [blame] | 881 | if (CONFIG(DEBUG_GPIO)) |
Hannah Williams | d28bd0c | 2017-05-17 23:24:22 -0700 | [diff] [blame] | 882 | printk(BIOS_DEBUG, "misccfg_mask:%x misccfg_value:%x\n", |
| 883 | misccfg_mask, misccfg_value); |
| 884 | comm = soc_gpio_get_community(&gpio_communities); |
| 885 | for (i = 0; i < gpio_communities; i++, comm++) |
| 886 | pcr_rmw32(comm->port, GPIO_MISCCFG, |
| 887 | misccfg_mask, misccfg_value); |
| 888 | } |
| 889 | |
| 890 | const char *gpio_acpi_path(gpio_t gpio_num) |
| 891 | { |
| 892 | const struct pad_community *comm = gpio_get_community(gpio_num); |
| 893 | return comm->acpi_path; |
| 894 | } |
Kane Chen | 223ddc2 | 2019-02-12 21:14:13 +0800 | [diff] [blame] | 895 | |
| 896 | uint32_t __weak soc_gpio_pad_config_fixup(const struct pad_config *cfg, |
| 897 | int dw_reg, uint32_t reg_val) |
| 898 | { |
| 899 | return reg_val; |
| 900 | } |
Karthikeyan Ramasubramanian | 3391a31 | 2019-04-24 10:12:38 -0600 | [diff] [blame] | 901 | |
| 902 | void gpi_clear_int_cfg(void) |
| 903 | { |
| 904 | int i, group, num_groups; |
| 905 | uint32_t sts_value; |
| 906 | size_t gpio_communities; |
| 907 | const struct pad_community *comm; |
| 908 | |
| 909 | comm = soc_gpio_get_community(&gpio_communities); |
| 910 | for (i = 0; i < gpio_communities; i++, comm++) { |
| 911 | num_groups = comm->num_gpi_regs; |
| 912 | for (group = 0; group < num_groups; group++) { |
| 913 | /* Clear the enable register */ |
| 914 | pcr_write32(comm->port, GPI_IE_OFFSET(comm, group), 0); |
| 915 | |
| 916 | /* Read and clear the set status register bits*/ |
| 917 | sts_value = pcr_read32(comm->port, |
| 918 | GPI_IS_OFFSET(comm, group)); |
| 919 | pcr_write32(comm->port, |
| 920 | GPI_IS_OFFSET(comm, group), sts_value); |
| 921 | } |
| 922 | } |
| 923 | } |
Subrata Banik | abdc9bc | 2019-05-14 17:31:19 +0530 | [diff] [blame] | 924 | |
| 925 | /* The function performs GPIO Power Management programming. */ |
| 926 | void gpio_pm_configure(const uint8_t *misccfg_pm_values, size_t num) |
| 927 | { |
| 928 | int i; |
| 929 | size_t gpio_communities; |
Subrata Banik | 2ccc0a4 | 2021-03-25 20:01:47 +0530 | [diff] [blame] | 930 | const uint8_t misccfg_pm_mask = (uint8_t)~MISCCFG_GPIO_PM_CONFIG_BITS; |
Subrata Banik | abdc9bc | 2019-05-14 17:31:19 +0530 | [diff] [blame] | 931 | const struct pad_community *comm; |
| 932 | |
| 933 | comm = soc_gpio_get_community(&gpio_communities); |
| 934 | if (gpio_communities != num) |
| 935 | die("Incorrect GPIO community count!\n"); |
| 936 | |
| 937 | /* Program GPIO_MISCCFG */ |
| 938 | for (i = 0; i < num; i++, comm++) |
| 939 | pcr_rmw8(comm->port, GPIO_MISCCFG, |
| 940 | misccfg_pm_mask, misccfg_pm_values[i]); |
| 941 | } |
Tim Wawrzynczak | a603e44 | 2021-03-22 10:40:51 -0600 | [diff] [blame] | 942 | |
| 943 | size_t gpio_get_index_in_group(gpio_t pad) |
| 944 | { |
| 945 | const struct pad_community *comm; |
| 946 | size_t pin; |
| 947 | |
| 948 | comm = gpio_get_community(pad); |
| 949 | pin = relative_pad_in_comm(comm, pad); |
| 950 | return gpio_within_group(comm, pin); |
| 951 | } |
Furquan Shaikh | d9ce285 | 2021-04-10 14:27:16 -0700 | [diff] [blame] | 952 | |
| 953 | static uint32_t *snapshot; |
| 954 | |
| 955 | static void *allocate_snapshot_space(void) |
| 956 | { |
| 957 | size_t gpio_communities, total = 0, i; |
| 958 | const struct pad_community *comm; |
| 959 | |
| 960 | comm = soc_gpio_get_community(&gpio_communities); |
| 961 | for (i = 0; i < gpio_communities; i++, comm++) |
| 962 | total += comm->last_pad - comm->first_pad + 1; |
| 963 | |
| 964 | if (total == 0) |
| 965 | return NULL; |
| 966 | |
| 967 | return malloc(total * GPIO_NUM_PAD_CFG_REGS * sizeof(uint32_t)); |
| 968 | } |
| 969 | |
| 970 | void gpio_snapshot(void) |
| 971 | { |
| 972 | size_t gpio_communities, index, i, pad, reg; |
| 973 | const struct pad_community *comm; |
| 974 | uint16_t config_offset; |
| 975 | |
| 976 | if (snapshot == NULL) { |
| 977 | snapshot = allocate_snapshot_space(); |
| 978 | if (snapshot == NULL) |
| 979 | return; |
| 980 | } |
| 981 | |
| 982 | comm = soc_gpio_get_community(&gpio_communities); |
| 983 | for (i = 0, index = 0; i < gpio_communities; i++, comm++) { |
| 984 | for (pad = comm->first_pad; pad <= comm->last_pad; pad++) { |
| 985 | config_offset = pad_config_offset(comm, pad); |
| 986 | for (reg = 0; reg < GPIO_NUM_PAD_CFG_REGS; reg++) { |
| 987 | snapshot[index] = pcr_read32(comm->port, |
| 988 | PAD_CFG_OFFSET(config_offset, reg)); |
| 989 | index++; |
| 990 | } |
| 991 | } |
| 992 | } |
| 993 | } |
| 994 | |
| 995 | size_t gpio_verify_snapshot(void) |
| 996 | { |
| 997 | size_t gpio_communities, index, i, pad, reg; |
| 998 | const struct pad_community *comm; |
| 999 | uint32_t curr_val; |
| 1000 | uint16_t config_offset; |
| 1001 | size_t changes = 0; |
| 1002 | |
| 1003 | if (snapshot == NULL) |
| 1004 | return 0; |
| 1005 | |
| 1006 | comm = soc_gpio_get_community(&gpio_communities); |
| 1007 | for (i = 0, index = 0; i < gpio_communities; i++, comm++) { |
| 1008 | for (pad = comm->first_pad; pad <= comm->last_pad; pad++) { |
| 1009 | config_offset = pad_config_offset(comm, pad); |
| 1010 | for (reg = 0; reg < GPIO_NUM_PAD_CFG_REGS; reg++) { |
| 1011 | curr_val = pcr_read32(comm->port, |
| 1012 | PAD_CFG_OFFSET(config_offset, reg)); |
| 1013 | if (curr_val != snapshot[index]) { |
| 1014 | printk(BIOS_SPEW, |
| 1015 | "%zd(DW%zd): Changed from 0x%x to 0x%x\n", |
| 1016 | pad, reg, snapshot[index], curr_val); |
| 1017 | changes++; |
| 1018 | } |
| 1019 | index++; |
| 1020 | } |
| 1021 | } |
| 1022 | } |
| 1023 | |
| 1024 | return changes; |
| 1025 | } |
| 1026 | |
| 1027 | static void snapshot_cleanup(void *unused) |
| 1028 | { |
| 1029 | free(snapshot); |
| 1030 | } |
| 1031 | |
Arthur Heymans | 897d63a | 2023-02-01 11:10:00 +0100 | [diff] [blame] | 1032 | BOOT_STATE_INIT_ENTRY(BS_OS_RESUME, BS_ON_ENTRY, snapshot_cleanup, NULL); |
Furquan Shaikh | d9ce285 | 2021-04-10 14:27:16 -0700 | [diff] [blame] | 1033 | BOOT_STATE_INIT_ENTRY(BS_PAYLOAD_LOAD, BS_ON_EXIT, snapshot_cleanup, NULL); |
Tim Wawrzynczak | 629ddfd | 2021-05-03 12:14:01 -0600 | [diff] [blame] | 1034 | |
| 1035 | bool gpio_get_vw_info(gpio_t pad, unsigned int *vw_index, unsigned int *vw_bit) |
| 1036 | { |
| 1037 | const struct pad_community *comm; |
| 1038 | unsigned int offset = 0; |
| 1039 | size_t i; |
| 1040 | |
| 1041 | comm = gpio_get_community(pad); |
| 1042 | for (i = 0; i < comm->num_vw_entries; i++) { |
| 1043 | if (pad >= comm->vw_entries[i].first_pad && pad <= comm->vw_entries[i].last_pad) |
| 1044 | break; |
| 1045 | |
| 1046 | offset += 1 + comm->vw_entries[i].last_pad - comm->vw_entries[i].first_pad; |
| 1047 | } |
| 1048 | |
| 1049 | if (i == comm->num_vw_entries) |
| 1050 | return false; |
| 1051 | |
| 1052 | offset += pad - comm->vw_entries[i].first_pad; |
| 1053 | *vw_index = comm->vw_base + offset / 8; |
| 1054 | *vw_bit = offset % 8; |
| 1055 | |
| 1056 | return true; |
| 1057 | } |
Tim Wawrzynczak | 87b7ec2 | 2021-04-21 14:08:53 -0600 | [diff] [blame] | 1058 | |
| 1059 | unsigned int gpio_get_pad_cpu_portid(gpio_t pad) |
| 1060 | { |
| 1061 | const struct pad_community *comm = gpio_get_community(pad); |
| 1062 | return comm->cpu_port; |
| 1063 | } |