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