tools/pci: add Nuvoton bridge configuration
The Nuvoton PCI device requires the Memory Space bit to be set in its
command register in order to access the mailbox over MMIO.
Signed-off-by: Benjamin Fair <benjaminfair@google.com>
Change-Id: Ic262b907ae55c622999aa68891b618650ccad3f2
diff --git a/tools/test/tools_pci_unittest.cpp b/tools/test/tools_pci_unittest.cpp
index 00b3eb1..4c4f76a 100644
--- a/tools/test/tools_pci_unittest.cpp
+++ b/tools/test/tools_pci_unittest.cpp
@@ -68,6 +68,8 @@
public:
virtual const struct pci_id_match* getMatch() const = 0;
virtual struct pci_device getDevice() const = 0;
+ virtual void expectSetup(PciAccessMock& pciMock,
+ const struct pci_device& dev) const {};
virtual std::unique_ptr<PciBridgeIntf> getBridge(PciAccess* pci) const = 0;
virtual std::string getName() const = 0;
};
@@ -94,6 +96,29 @@
return dev;
}
+ void expectSetup(PciAccessMock& pciMock,
+ const struct pci_device& dev) const override
+ {
+ static constexpr std::uint8_t defaultVal = 0x40;
+
+ InSequence in;
+
+ EXPECT_CALL(pciMock,
+ pci_device_cfg_read_u8(Eq(&dev), NotNull(), config))
+ .WillOnce(DoAll(SetArgPointee<1>(defaultVal), Return(0)));
+ EXPECT_CALL(pciMock, pci_device_cfg_write_u8(
+ Eq(&dev), defaultVal | bridgeEnabled, config))
+ .WillOnce(Return(0));
+
+ EXPECT_CALL(pciMock,
+ pci_device_cfg_read_u8(Eq(&dev), NotNull(), config))
+ .WillOnce(
+ DoAll(SetArgPointee<1>(defaultVal | bridgeEnabled), Return(0)));
+ EXPECT_CALL(pciMock,
+ pci_device_cfg_write_u8(Eq(&dev), defaultVal, config))
+ .WillOnce(Return(0));
+ }
+
std::unique_ptr<PciBridgeIntf> getBridge(PciAccess* pci) const override
{
return std::make_unique<NuvotonPciBridge>(pci);
@@ -104,6 +129,11 @@
return "Nuvoton"s;
}
+ /* Offset to the config register */
+ static constexpr int config = 0x04;
+ /* Second bit determines whether bridge is enabled */
+ static constexpr std::uint8_t bridgeEnabled = 0x02;
+
private:
static constexpr struct pci_id_match match
{
@@ -253,6 +283,7 @@
mockRegionSize))
.WillOnce(Return(EFAULT));
+ GetParam()->expectSetup(pciMock, dev);
// This will print an error but not throw
GetParam()->getBridge(&pciMock);
}
@@ -260,7 +291,7 @@
/* Create expectations on pciMock for finding device and mapping memory region
*/
void expectSetup(PciAccessMock& pciMock, struct pci_device& dev, Device* param,
- std::uint8_t* region)
+ std::uint8_t* region, bool deviceExpectations = true)
{
EXPECT_CALL(pciMock,
pci_id_match_iterator_create(PciIdMatch(param->getMatch())))
@@ -281,6 +312,9 @@
EXPECT_CALL(pciMock,
pci_device_unmap_range(Eq(&dev), Eq(region), mockRegionSize))
.WillOnce(Return(0));
+
+ if (deviceExpectations)
+ param->expectSetup(pciMock, dev);
}
/* Test finding device and mapping memory region */
@@ -361,6 +395,161 @@
EXPECT_EQ(bridge->getDataLength(), 0x4000);
}
+/* Make sure config register is left alone if the bridge is already enabled */
+TEST(NuvotonBridgeTest, AlreadyEnabledSuccess)
+{
+ PciAccessMock pciMock;
+ struct pci_device dev;
+ std::vector<std::uint8_t> region(mockRegionSize);
+
+ constexpr std::uint8_t defaultVal = 0x40;
+
+ /* Only set standard expectations; not those from nuvotonDevice */
+ expectSetup(pciMock, dev, &nuvotonDevice, region.data(), false);
+
+ {
+ InSequence in;
+
+ EXPECT_CALL(pciMock, pci_device_cfg_read_u8(Eq(&dev), NotNull(),
+ NuvotonDevice::config))
+ .WillOnce(DoAll(
+ SetArgPointee<1>(defaultVal | NuvotonDevice::bridgeEnabled),
+ Return(0)));
+
+ EXPECT_CALL(pciMock, pci_device_cfg_read_u8(Eq(&dev), NotNull(),
+ NuvotonDevice::config))
+ .WillOnce(DoAll(
+ SetArgPointee<1>(defaultVal | NuvotonDevice::bridgeEnabled),
+ Return(0)));
+ EXPECT_CALL(pciMock, pci_device_cfg_write_u8(Eq(&dev), defaultVal,
+ NuvotonDevice::config))
+ .WillOnce(Return(0));
+ }
+
+ nuvotonDevice.getBridge(&pciMock);
+}
+
+/* Read fails when attempting to setup the bridge */
+TEST(NuvotonBridgeTest, ReadSetupFail)
+{
+ PciAccessMock pciMock;
+ struct pci_device dev;
+ std::vector<std::uint8_t> region(mockRegionSize);
+
+ /* Only set standard expectations; not those from nuvotonDevice */
+ expectSetup(pciMock, dev, &nuvotonDevice, region.data(), false);
+
+ EXPECT_CALL(pciMock, pci_device_cfg_read_u8(Eq(&dev), NotNull(),
+ NuvotonDevice::config))
+ .WillOnce(Return(EFAULT));
+
+ EXPECT_THROW(nuvotonDevice.getBridge(&pciMock), std::system_error);
+}
+
+/* Write fails when attempting to setup the bridge */
+TEST(NuvotonBridgeTest, WriteSetupFail)
+{
+ PciAccessMock pciMock;
+ struct pci_device dev;
+ std::vector<std::uint8_t> region(mockRegionSize);
+
+ constexpr std::uint8_t defaultVal = 0x40;
+
+ /* Only set standard expectations; not those from nuvotonDevice */
+ expectSetup(pciMock, dev, &nuvotonDevice, region.data(), false);
+
+ EXPECT_CALL(pciMock, pci_device_cfg_read_u8(Eq(&dev), NotNull(),
+ NuvotonDevice::config))
+ .WillOnce(DoAll(SetArgPointee<1>(defaultVal), Return(0)));
+ EXPECT_CALL(pciMock,
+ pci_device_cfg_write_u8(
+ Eq(&dev), defaultVal | NuvotonDevice::bridgeEnabled,
+ NuvotonDevice::config))
+ .WillOnce(Return(EFAULT));
+
+ EXPECT_THROW(nuvotonDevice.getBridge(&pciMock), std::system_error);
+}
+
+/* Read fails when attempting to disable the bridge */
+TEST(NuvotonBridgeTest, ReadDisableFail)
+{
+ PciAccessMock pciMock;
+ struct pci_device dev;
+ std::vector<std::uint8_t> region(mockRegionSize);
+
+ constexpr std::uint8_t defaultVal = 0x40;
+
+ /* Only set standard expectations; not those from nuvotonDevice */
+ expectSetup(pciMock, dev, &nuvotonDevice, region.data(), false);
+
+ {
+ InSequence in;
+
+ EXPECT_CALL(pciMock, pci_device_cfg_read_u8(Eq(&dev), NotNull(),
+ NuvotonDevice::config))
+ .WillOnce(DoAll(SetArgPointee<1>(defaultVal), Return(0)));
+ EXPECT_CALL(pciMock,
+ pci_device_cfg_write_u8(
+ Eq(&dev), defaultVal | NuvotonDevice::bridgeEnabled,
+ NuvotonDevice::config))
+ .WillOnce(Return(0));
+
+ EXPECT_CALL(pciMock, pci_device_cfg_read_u8(Eq(&dev), NotNull(),
+ NuvotonDevice::config))
+ .WillOnce(Return(EFAULT));
+ }
+
+ nuvotonDevice.getBridge(&pciMock);
+}
+
+/* Write fails when attempting to disable the bridge */
+TEST(NuvotonBridgeTest, WriteDisableFail)
+{
+ PciAccessMock pciMock;
+ struct pci_device dev;
+ std::vector<std::uint8_t> region(mockRegionSize);
+
+ constexpr std::uint8_t defaultVal = 0x40;
+
+ /* Only set standard expectations; not those from nuvotonDevice */
+ expectSetup(pciMock, dev, &nuvotonDevice, region.data(), false);
+
+ {
+ InSequence in;
+
+ EXPECT_CALL(pciMock, pci_device_cfg_read_u8(Eq(&dev), NotNull(),
+ NuvotonDevice::config))
+ .WillOnce(DoAll(SetArgPointee<1>(defaultVal), Return(0)));
+ EXPECT_CALL(pciMock,
+ pci_device_cfg_write_u8(
+ Eq(&dev), defaultVal | NuvotonDevice::bridgeEnabled,
+ NuvotonDevice::config))
+ .WillOnce(Return(0));
+
+ EXPECT_CALL(pciMock, pci_device_cfg_read_u8(Eq(&dev), NotNull(),
+ NuvotonDevice::config))
+ .WillOnce(DoAll(
+ SetArgPointee<1>(defaultVal | NuvotonDevice::bridgeEnabled),
+ Return(0)));
+ EXPECT_CALL(pciMock, pci_device_cfg_write_u8(Eq(&dev), defaultVal,
+ NuvotonDevice::config))
+ .WillOnce(Return(EFAULT));
+ }
+
+ nuvotonDevice.getBridge(&pciMock);
+}
+
+/* Make sure the bridge gets enabled when needed */
+TEST(NuvotonBridgeTest, NotEnabledSuccess)
+{
+ PciAccessMock pciMock;
+ struct pci_device dev;
+ std::vector<std::uint8_t> region(mockRegionSize);
+
+ expectSetup(pciMock, dev, &nuvotonDevice, region.data());
+ nuvotonDevice.getBridge(&pciMock);
+}
+
TEST(AspeedWriteTest, TooLarge)
{
PciAccessMock pciMock;