1cc45be7cSJohn Wedig // Copyright 2023 Google LLC 2cc45be7cSJohn Wedig // 3cc45be7cSJohn Wedig // Licensed under the Apache License, Version 2.0 (the "License"); 4cc45be7cSJohn Wedig // you may not use this file except in compliance with the License. 5cc45be7cSJohn Wedig // You may obtain a copy of the License at 6cc45be7cSJohn Wedig // 7cc45be7cSJohn Wedig // http://www.apache.org/licenses/LICENSE-2.0 8cc45be7cSJohn Wedig // 9cc45be7cSJohn Wedig // Unless required by applicable law or agreed to in writing, software 10cc45be7cSJohn Wedig // distributed under the License is distributed on an "AS IS" BASIS, 11cc45be7cSJohn Wedig // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12cc45be7cSJohn Wedig // See the License for the specific language governing permissions and 13cc45be7cSJohn Wedig // limitations under the License. 14cc45be7cSJohn Wedig 15cc45be7cSJohn Wedig /* 16cc45be7cSJohn Wedig * Disclaimer: This binary is only intended to be used on Nuvoton NPCM7xx BMCs. 17cc45be7cSJohn Wedig * 18cc45be7cSJohn Wedig * It could also be extended to support NPCM8xx, but it hasn't been tested with 19cc45be7cSJohn Wedig * that model BMC. 20cc45be7cSJohn Wedig * 21cc45be7cSJohn Wedig * This binary is NOT intended to support Aspeed BMCs. 22cc45be7cSJohn Wedig */ 23cc45be7cSJohn Wedig 24cc45be7cSJohn Wedig #include <unistd.h> 25cc45be7cSJohn Wedig 26cc45be7cSJohn Wedig #include <stdplus/fd/create.hpp> 27cc45be7cSJohn Wedig #include <stdplus/fd/intf.hpp> 28cc45be7cSJohn Wedig #include <stdplus/fd/managed.hpp> 29cc45be7cSJohn Wedig #include <stdplus/fd/mmap.hpp> 30cc45be7cSJohn Wedig 31cc45be7cSJohn Wedig #include <cassert> 32cc45be7cSJohn Wedig #include <cstdint> 33cc45be7cSJohn Wedig #include <cstdio> 34cc45be7cSJohn Wedig #include <format> 35cc45be7cSJohn Wedig #include <stdexcept> 36cc45be7cSJohn Wedig 37cc45be7cSJohn Wedig /* Base address for Nuvoton's global control register space. */ 38cc45be7cSJohn Wedig #define NPCM7XX_GLOBAL_CTRL_BASE_ADDR 0xF0800000 39cc45be7cSJohn Wedig /* Offset of the PDID register and expected PDID value. */ 40cc45be7cSJohn Wedig #define PDID_OFFSET 0x00 41cc45be7cSJohn Wedig #define NPCM7XX_PDID 0x04A92750 42cc45be7cSJohn Wedig 43cc45be7cSJohn Wedig /* Register width in bytes. */ 44cc45be7cSJohn Wedig #define REGISTER_WIDTH 4 45cc45be7cSJohn Wedig 46cc45be7cSJohn Wedig /* Base address for Nuvoton's eSPI register space. */ 47cc45be7cSJohn Wedig #define NPCM7XX_ESPI_BASE_ADDR 0xF009F000 48cc45be7cSJohn Wedig /* 49cc45be7cSJohn Wedig * Offset of the eSPI config (ESPICFG) register, along with host channel enable 50cc45be7cSJohn Wedig * mask and core channel enable mask. 51cc45be7cSJohn Wedig */ 52cc45be7cSJohn Wedig #define ESPICFG_OFFSET 0x4 53cc45be7cSJohn Wedig #define ESPICFG_HOST_CHANNEL_ENABLE_MASK 0xF0 54cc45be7cSJohn Wedig #define ESPICFG_CORE_CHANNEL_ENABLE_MASK 0x0F 55cc45be7cSJohn Wedig /* 56cc45be7cSJohn Wedig * Offset of the host independence (ESPIHINDP) register and automatic ready bit 57cc45be7cSJohn Wedig * mask. 58cc45be7cSJohn Wedig */ 59cc45be7cSJohn Wedig #define ESPIHINDP_OFFSET 0x80 60cc45be7cSJohn Wedig #define ESPI_AUTO_READY_MASK 0xF 61cc45be7cSJohn Wedig 62cc45be7cSJohn Wedig namespace 63cc45be7cSJohn Wedig { 64cc45be7cSJohn Wedig 65cc45be7cSJohn Wedig using stdplus::fd::MMapAccess; 66cc45be7cSJohn Wedig using stdplus::fd::MMapFlags; 67cc45be7cSJohn Wedig using stdplus::fd::OpenAccess; 68cc45be7cSJohn Wedig using stdplus::fd::OpenFlag; 69cc45be7cSJohn Wedig using stdplus::fd::OpenFlags; 70cc45be7cSJohn Wedig using stdplus::fd::ProtFlag; 71cc45be7cSJohn Wedig using stdplus::fd::ProtFlags; 72cc45be7cSJohn Wedig 73cc45be7cSJohn Wedig static void usage(char* name) 74cc45be7cSJohn Wedig { 75cc45be7cSJohn Wedig std::fprintf(stderr, "Usage: %s [-d]\n", name); 76cc45be7cSJohn Wedig std::fprintf(stderr, "Enable or disable eSPI bus on NPCM7XX BMC\n"); 77cc45be7cSJohn Wedig std::fprintf(stderr, "This program will enable eSPI by default, unless " 78cc45be7cSJohn Wedig "the -d option is used.\n"); 79cc45be7cSJohn Wedig std::fprintf(stderr, " -d Disable eSPI\n"); 80cc45be7cSJohn Wedig } 81cc45be7cSJohn Wedig 82cc45be7cSJohn Wedig /* 83cc45be7cSJohn Wedig * Get a pointer to the register at the given offset, within the provided 84cc45be7cSJohn Wedig * memory-mapped I/O space. 85cc45be7cSJohn Wedig */ 86cc45be7cSJohn Wedig static inline volatile uint32_t* getReg(stdplus::fd::MMap& map, 87cc45be7cSJohn Wedig size_t regOffset) 88cc45be7cSJohn Wedig { 89c66ebc35SPatrick Williams uintptr_t regPtr = 90c66ebc35SPatrick Williams reinterpret_cast<uintptr_t>(map.get().data()) + regOffset; 91cc45be7cSJohn Wedig /* Make sure the register pointer is properly aligned. */ 92cc45be7cSJohn Wedig assert((regPtr & ~(REGISTER_WIDTH - 1)) == regPtr); 93cc45be7cSJohn Wedig 94cc45be7cSJohn Wedig return reinterpret_cast<volatile uint32_t*>(regPtr); 95cc45be7cSJohn Wedig } 96cc45be7cSJohn Wedig 97cc45be7cSJohn Wedig static void modifyESPIRegisters(bool disable) 98cc45be7cSJohn Wedig { 99cc45be7cSJohn Wedig /* 100cc45be7cSJohn Wedig * We need to make sure this is running on a Nuvoton BMC. To do that, we'll 101cc45be7cSJohn Wedig * read the product identification (PDID) register. 102cc45be7cSJohn Wedig */ 103cc45be7cSJohn Wedig 104cc45be7cSJohn Wedig /* Find the page that includes the Product ID register. */ 105cc45be7cSJohn Wedig size_t pageSize = sysconf(_SC_PAGE_SIZE); 106cc45be7cSJohn Wedig size_t pageBase = NPCM7XX_GLOBAL_CTRL_BASE_ADDR / pageSize * pageSize; 107cc45be7cSJohn Wedig size_t pageOffset = NPCM7XX_GLOBAL_CTRL_BASE_ADDR - pageBase; 108cc45be7cSJohn Wedig size_t mapLength = pageOffset + PDID_OFFSET + REGISTER_WIDTH; 109cc45be7cSJohn Wedig 110cc45be7cSJohn Wedig auto fd = stdplus::fd::open( 111cc45be7cSJohn Wedig "/dev/mem", OpenFlags(OpenAccess::ReadWrite).set(OpenFlag::Sync)); 112cc45be7cSJohn Wedig stdplus::fd::MMap pdidMap(fd, mapLength, ProtFlags().set(ProtFlag::Read), 113cc45be7cSJohn Wedig MMapFlags(MMapAccess::Shared), pageBase); 114cc45be7cSJohn Wedig 115c66ebc35SPatrick Williams volatile uint32_t* const pdidReg = 116c66ebc35SPatrick Williams getReg(pdidMap, pageOffset + PDID_OFFSET); 117cc45be7cSJohn Wedig 118cc45be7cSJohn Wedig /* 119cc45be7cSJohn Wedig * Read the PDID register to make sure we're running on a Nuvoton NPCM7xx 120cc45be7cSJohn Wedig * BMC. 121cc45be7cSJohn Wedig * Note: This binary would probably work on NPCM8xx, as well, if we also 122cc45be7cSJohn Wedig * allowed the NPCM8xx PDID, since the register addresses are the same. But 123cc45be7cSJohn Wedig * that hasn't been tested. 124cc45be7cSJohn Wedig */ 125cc45be7cSJohn Wedig if (*pdidReg != NPCM7XX_PDID) 126cc45be7cSJohn Wedig { 127cc45be7cSJohn Wedig throw std::runtime_error( 128*d89f889bSYuxiao Zhang std::format("Unexpected product ID {:#x} != {:#x}", 129*d89f889bSYuxiao Zhang static_cast<unsigned int>(*pdidReg), NPCM7XX_PDID) 130cc45be7cSJohn Wedig .data()); 131cc45be7cSJohn Wedig } 132cc45be7cSJohn Wedig 133cc45be7cSJohn Wedig /* 134cc45be7cSJohn Wedig * Find the start of the page that includes the start of the eSPI register 135cc45be7cSJohn Wedig * space. 136cc45be7cSJohn Wedig */ 137cc45be7cSJohn Wedig pageBase = NPCM7XX_ESPI_BASE_ADDR / pageSize * pageSize; 138cc45be7cSJohn Wedig pageOffset = NPCM7XX_ESPI_BASE_ADDR - pageBase; 139cc45be7cSJohn Wedig 140cc45be7cSJohn Wedig mapLength = pageOffset + ESPIHINDP_OFFSET + REGISTER_WIDTH; 141cc45be7cSJohn Wedig 142cc45be7cSJohn Wedig stdplus::fd::MMap espiMap( 143cc45be7cSJohn Wedig fd, mapLength, ProtFlags().set(ProtFlag::Read).set(ProtFlag::Write), 144cc45be7cSJohn Wedig MMapFlags(MMapAccess::Shared), pageBase); 145cc45be7cSJohn Wedig 146cc45be7cSJohn Wedig /* Read the ESPICFG register. */ 147c66ebc35SPatrick Williams volatile uint32_t* const espicfgReg = 148c66ebc35SPatrick Williams getReg(espiMap, pageOffset + ESPICFG_OFFSET); 149cc45be7cSJohn Wedig uint32_t espicfgValue = *espicfgReg; 150cc45be7cSJohn Wedig 151cc45be7cSJohn Wedig if (disable) 152cc45be7cSJohn Wedig { 153cc45be7cSJohn Wedig /* 154cc45be7cSJohn Wedig * Check if the automatic ready bits are set in the eSPI host 155cc45be7cSJohn Wedig * independence register (ESPIHINDP). 156cc45be7cSJohn Wedig */ 157cc45be7cSJohn Wedig volatile uint32_t* const espihindpReg = 158cc45be7cSJohn Wedig getReg(espiMap, pageOffset + ESPIHINDP_OFFSET); 159cc45be7cSJohn Wedig uint32_t espihindpValue = *espihindpReg; 160cc45be7cSJohn Wedig if (espihindpValue & ESPI_AUTO_READY_MASK) 161cc45be7cSJohn Wedig { 162cc45be7cSJohn Wedig /* 163cc45be7cSJohn Wedig * If any of the automatic ready bits are set, we need to disable 164cc45be7cSJohn Wedig * them, using several steps: 165cc45be7cSJohn Wedig * - Make sure the host channel enable and core channel bits are 166cc45be7cSJohn Wedig * consistent, in the ESPICFG register, i.e. copy the host 167cc45be7cSJohn Wedig * channel enable bits to the core channel enable bits. 168cc45be7cSJohn Wedig * - Clear the automatic ready bits in ESPIHINDP. 169cc45be7cSJohn Wedig */ 170c66ebc35SPatrick Williams uint32_t hostChannelEnableBits = 171c66ebc35SPatrick Williams espicfgValue & ESPICFG_HOST_CHANNEL_ENABLE_MASK; 172cc45be7cSJohn Wedig espicfgValue |= (hostChannelEnableBits >> 4); 173cc45be7cSJohn Wedig *espicfgReg = espicfgValue; 174cc45be7cSJohn Wedig 175cc45be7cSJohn Wedig espihindpValue &= ~ESPI_AUTO_READY_MASK; 176cc45be7cSJohn Wedig *espihindpReg = espihindpValue; 177cc45be7cSJohn Wedig } 178cc45be7cSJohn Wedig 179cc45be7cSJohn Wedig /* Now disable the core channel enable bits in ESPICFG. */ 180cc45be7cSJohn Wedig espicfgValue &= ~ESPICFG_CORE_CHANNEL_ENABLE_MASK; 181cc45be7cSJohn Wedig *espicfgReg = espicfgValue; 182cc45be7cSJohn Wedig 183cc45be7cSJohn Wedig fprintf(stderr, "Disabled eSPI bus\n"); 184cc45be7cSJohn Wedig } 185cc45be7cSJohn Wedig else 186cc45be7cSJohn Wedig { 187cc45be7cSJohn Wedig /* Enable eSPI by setting the core channel enable bits in ESPICFG. */ 188cc45be7cSJohn Wedig espicfgValue |= ESPICFG_CORE_CHANNEL_ENABLE_MASK; 189cc45be7cSJohn Wedig *espicfgReg = espicfgValue; 190cc45be7cSJohn Wedig 191cc45be7cSJohn Wedig fprintf(stderr, "Enabled eSPI bus\n"); 192cc45be7cSJohn Wedig } 193cc45be7cSJohn Wedig } 194cc45be7cSJohn Wedig 195cc45be7cSJohn Wedig } // namespace 196cc45be7cSJohn Wedig 197cc45be7cSJohn Wedig int main(int argc, char** argv) 198cc45be7cSJohn Wedig { 199cc45be7cSJohn Wedig int opt; 200cc45be7cSJohn Wedig bool disable = false; 201cc45be7cSJohn Wedig while ((opt = getopt(argc, argv, "d")) != -1) 202cc45be7cSJohn Wedig { 203cc45be7cSJohn Wedig switch (opt) 204cc45be7cSJohn Wedig { 205cc45be7cSJohn Wedig case 'd': 206cc45be7cSJohn Wedig disable = true; 207cc45be7cSJohn Wedig break; 208cc45be7cSJohn Wedig default: 209cc45be7cSJohn Wedig usage(argv[0]); 210cc45be7cSJohn Wedig return -1; 211cc45be7cSJohn Wedig } 212cc45be7cSJohn Wedig } 213cc45be7cSJohn Wedig 214cc45be7cSJohn Wedig try 215cc45be7cSJohn Wedig { 216cc45be7cSJohn Wedig /* Update registers to enable or disable eSPI. */ 217cc45be7cSJohn Wedig modifyESPIRegisters(disable); 218cc45be7cSJohn Wedig } 219cc45be7cSJohn Wedig catch (const std::exception& e) 220cc45be7cSJohn Wedig { 221cc45be7cSJohn Wedig fprintf(stderr, "Failed to %s eSPI bus: %s\n", 222cc45be7cSJohn Wedig disable ? "disable" : "enable", e.what()); 223cc45be7cSJohn Wedig return -1; 224cc45be7cSJohn Wedig } 225cc45be7cSJohn Wedig 226cc45be7cSJohn Wedig return 0; 227cc45be7cSJohn Wedig } 228