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
usage(char * name)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 */
getReg(stdplus::fd::MMap & map,size_t regOffset)86cc45be7cSJohn Wedig static inline volatile uint32_t* getReg(stdplus::fd::MMap& map,
87cc45be7cSJohn Wedig size_t regOffset)
88cc45be7cSJohn Wedig {
89*c66ebc35SPatrick Williams uintptr_t regPtr =
90*c66ebc35SPatrick 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
modifyESPIRegisters(bool disable)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
115*c66ebc35SPatrick Williams volatile uint32_t* const pdidReg =
116*c66ebc35SPatrick 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(
128cc45be7cSJohn Wedig std::format("Unexpected product ID {:#x} != {:#x}", *pdidReg,
129cc45be7cSJohn Wedig 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. */
147*c66ebc35SPatrick Williams volatile uint32_t* const espicfgReg =
148*c66ebc35SPatrick 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 */
170*c66ebc35SPatrick Williams uint32_t hostChannelEnableBits =
171*c66ebc35SPatrick 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
main(int argc,char ** argv)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