| /* SPDX-License-Identifier: GPL-2.0-or-later */ |
| |
| /* |
| * SMM utilities used in both SMM and normal mode |
| */ |
| |
| #include <console/console.h> |
| #include <cpu/x86/smm.h> |
| #include <soc/southbridge.h> |
| #include <soc/smi.h> |
| #include <amdblocks/acpimmio.h> |
| #include <amdblocks/smi.h> |
| |
| void configure_smi(uint8_t smi_num, uint8_t mode) |
| { |
| uint8_t reg32_offset, bit_offset; |
| uint32_t reg32; |
| |
| if (smi_num >= NUMBER_SMITYPES) { |
| printk(BIOS_WARNING, "BUG: Invalid SMI: %u\n", smi_num); |
| return; |
| } |
| |
| /* 16 sources per register, 2 bits per source; registers are 4 bytes */ |
| reg32_offset = (smi_num / 16) * 4; |
| bit_offset = (smi_num % 16) * 2; |
| |
| reg32 = smi_read32(SMI_REG_CONTROL0 + reg32_offset); |
| reg32 &= ~(0x3 << (bit_offset)); |
| reg32 |= (mode & 0x3) << bit_offset; |
| smi_write32(SMI_REG_CONTROL0 + reg32_offset, reg32); |
| } |
| |
| /** |
| * Configure generation of interrupts for given GEVENT pin |
| * |
| * @param gevent The GEVENT pin number. Valid values are 0 thru 23 |
| * @param mode The type of event this pin should generate. Note that only |
| * SMI_MODE_SMI generates an SMI. SMI_MODE_DISABLE disables events. |
| * @param level SMI__SCI_LVL_LOW or SMI_SCI_LVL_HIGH |
| */ |
| void configure_gevent_smi(uint8_t gevent, uint8_t mode, uint8_t level) |
| { |
| uint32_t reg32; |
| /* GEVENT pins range from [0:23] */ |
| if (gevent >= SMI_GEVENTS) { |
| printk(BIOS_WARNING, "BUG: Invalid GEVENT: %u\n", gevent); |
| return; |
| } |
| |
| /* SMI0 source is GEVENT0 and so on */ |
| configure_smi(gevent, mode); |
| |
| /* And set set the trigger level */ |
| reg32 = smi_read32(SMI_REG_SMITRIG0); |
| reg32 &= ~(1 << gevent); |
| reg32 |= (level & 0x1) << gevent; |
| smi_write32(SMI_REG_SMITRIG0, reg32); |
| } |
| |
| /** |
| * Configure generation of SCIs. |
| */ |
| void configure_scimap(const struct sci_source *sci) |
| { |
| uint32_t reg32; |
| |
| /* GEVENT pins range */ |
| if (sci->scimap >= SCIMAPS) { |
| printk(BIOS_WARNING, "BUG: Invalid SCIMAP: %u\n", |
| sci->scimap); |
| return; |
| } |
| |
| /* GPEs range from [0:31] */ |
| if (sci->gpe >= SCI_GPES) { |
| printk(BIOS_WARNING, "BUG: Invalid SCI GPE: %u\n", sci->gpe); |
| return; |
| } |
| |
| printk(BIOS_DEBUG, "SCIMAP %u maps to GPE %u (active %s, %s trigger)\n", |
| sci->scimap, sci->gpe, |
| (!!sci->direction) ? "high" : "low", |
| (!!sci->level) ? "level" : "edge"); |
| |
| /* Map Gevent to SCI GPE# */ |
| smi_write8(SMI_SCI_MAP(sci->scimap), sci->gpe); |
| |
| /* Set the trigger direction (high/low) */ |
| reg32 = smi_read32(SMI_SCI_TRIG); |
| reg32 &= ~(1 << sci->gpe); |
| reg32 |= !!sci->direction << sci->gpe; |
| smi_write32(SMI_SCI_TRIG, reg32); |
| |
| /* Set the trigger level (edge/level) */ |
| reg32 = smi_read32(SMI_SCI_LEVEL); |
| reg32 &= ~(1 << sci->gpe); |
| reg32 |= !!sci->level << sci->gpe; |
| smi_write32(SMI_SCI_LEVEL, reg32); |
| } |
| |
| void gpe_configure_sci(const struct sci_source *scis, size_t num_gpes) |
| { |
| size_t i; |
| |
| for (i = 0; i < num_gpes; i++) |
| configure_scimap(scis + i); |
| } |
| |
| /** Disable events from given GEVENT pin */ |
| void disable_gevent_smi(uint8_t gevent) |
| { |
| /* GEVENT pins range from [0:23] */ |
| if (gevent > 23) { |
| printk(BIOS_WARNING, "BUG: Invalid GEVENT: %u\n", gevent); |
| return; |
| } |
| |
| /* SMI0 source is GEVENT0 and so on */ |
| configure_smi(gevent, SMI_MODE_DISABLE); |
| } |
| |
| uint16_t pm_acpi_smi_cmd_port(void) |
| { |
| return pm_read16(PM_ACPI_SMI_CMD); |
| } |