[mt8167][usb] Dynamic endpoint number support.
Currently, the number of implemented endpoint controllers is hard-coded
into the host driver. This works for the mt8167 chip, but might be
incorrect in a different chip that uses the musbmhdrc IP. In an effort
to genericize the driver to any chip, we'll read the count of
implemented endpoint controllers from the EPINFO register.
Test: UMS iochk -c 200 /dev/class/block/xxx
Test: runtests -t mt-hci-test-test
Change-Id: I11ddab7da699ce408b840774e209fb2626942120
diff --git a/zircon/system/dev/usb/mt-musb-host/BUILD.gn b/zircon/system/dev/usb/mt-musb-host/BUILD.gn
index a99650f..e2c23bce 100644
--- a/zircon/system/dev/usb/mt-musb-host/BUILD.gn
+++ b/zircon/system/dev/usb/mt-musb-host/BUILD.gn
@@ -3,6 +3,13 @@
# found in the LICENSE file.
driver("mt-musb-host") {
+ deps = [
+ ":common",
+ ]
+}
+
+source_set("common") {
+ visibility = [ ":*" ]
sources = [
"usb-device.cpp",
"usb-hci.cpp",
@@ -11,7 +18,7 @@
"usb-spew.cpp",
"usb-transaction.cpp",
]
- deps = [
+ public_deps = [
"$zx/system/banjo/ddk.protocol.usb.bus",
"$zx/system/banjo/ddk.protocol.usb.hci",
"$zx/system/dev/lib/device-protocol-pdev",
@@ -28,6 +35,18 @@
]
}
+test("mt-hci-test") {
+ output_name = "mt-hci-test"
+ sources = [
+ "usb-hci-test.cpp",
+ ]
+ deps = [
+ ":common",
+ "$zx/system/dev/lib/fake_ddk",
+ "$zx/system/ulib/zxtest",
+ ]
+}
+
test("mt-hci-transaction-test") {
sources = [
"usb-transaction-test.cpp",
diff --git a/zircon/system/dev/usb/mt-musb-host/usb-device.cpp b/zircon/system/dev/usb/mt-musb-host/usb-device.cpp
index 60248b8..04d7021 100644
--- a/zircon/system/dev/usb/mt-musb-host/usb-device.cpp
+++ b/zircon/system/dev/usb/mt-musb-host/usb-device.cpp
@@ -62,8 +62,8 @@
}
void HardwareDevice::Disconnect() {
- for (uint8_t i=0; i<=kMaxEpNum; i++) {
- if (ep_q_[i] != nullptr) {
+ for (uint8_t i = 0; i < kMaxEndpointCount; i++) {
+ if (ep_q_[i]) {
ep_q_[i]->Halt();
}
}
@@ -170,8 +170,8 @@
}
size_t HardwareDevice::GetMaxTransferSize(uint8_t ep) {
- if (ep > kMaxEpNum || !ep_q_[ep]) {
- zxlogf(ERROR, "%s: unconfigured endpoint\n", __func__);
+ if (ep >= kMaxEndpointCount || !ep_q_[ep]) {
+ zxlogf(ERROR, "%s: unconfigured endpoint: %d\n", __func__, ep);
return 0;
}
return ep_q_[ep]->GetMaxTransferSize();
diff --git a/zircon/system/dev/usb/mt-musb-host/usb-device.h b/zircon/system/dev/usb/mt-musb-host/usb-device.h
index 46c8c94..cfd5156 100644
--- a/zircon/system/dev/usb/mt-musb-host/usb-device.h
+++ b/zircon/system/dev/usb/mt-musb-host/usb-device.h
@@ -16,8 +16,9 @@
namespace mt_usb_hci {
-// The (inclusive) maximum endpont number.
-constexpr int kMaxEpNum = 8;
+// The maximum number of endpoints any USB device could theoretically support. Endpoint addresses
+// are 4-bit values.
+constexpr int kMaxEndpointCount = 16;
// UsbDevice is a usb spec-compliant device.
class UsbDevice {
@@ -103,7 +104,7 @@
const usb_speed_t speed_;
// Array of RequestQueue unique_ptrs indexed by endpoint-number.
- std::array<std::unique_ptr<RequestQueue>, kMaxEpNum+1> ep_q_;
+ std::array<std::unique_ptr<RequestQueue>, kMaxEndpointCount> ep_q_;
};
} // namespace mt_usb_hci
diff --git a/zircon/system/dev/usb/mt-musb-host/usb-hci-test.cpp b/zircon/system/dev/usb/mt-musb-host/usb-hci-test.cpp
new file mode 100644
index 0000000..53cf56b
--- /dev/null
+++ b/zircon/system/dev/usb/mt-musb-host/usb-hci-test.cpp
@@ -0,0 +1,68 @@
+// Copyright 2019 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "usb-hci.h"
+
+#include <lib/fake_ddk/fake_ddk.h>
+#include <lib/mmio/mmio.h>
+#include <lib/zx/vmo.h>
+#include <soc/mt8167/mt8167-hw.h>
+#include <soc/mt8167/mt8167-usb.h>
+#include <zircon/types.h>
+#include <zxtest/zxtest.h>
+
+namespace mt_usb_hci {
+namespace regs = board_mt8167;
+
+// A testing instance which exposes the Init() method.
+class TUsbHci : public UsbHci {
+public:
+ using UsbHci::UsbHci;
+ using UsbHci::Init;
+};
+
+class HciTest: public zxtest::Test {
+protected:
+ void SetUp() {
+ size_t sz;
+
+ zx::vmo usb;
+ ASSERT_OK(zx::vmo::create(MT8167_USB1_LENGTH, 0, &usb));
+ ASSERT_OK(usb.get_size(&sz));
+ ASSERT_OK(ddk::MmioBuffer::Create(0, sz, std::move(usb), ZX_CACHE_POLICY_UNCACHED,
+ &usb_mmio_));
+
+ zx::vmo phy;
+ ASSERT_OK(zx::vmo::create(MT8167_USBPHY_LENGTH, 0, &phy));
+ ASSERT_OK(phy.get_size(&sz));
+ ASSERT_OK(ddk::MmioBuffer::Create(0, sz, std::move(phy), ZX_CACHE_POLICY_UNCACHED,
+ &phy_mmio_));
+
+ ASSERT_OK(zx_interrupt_create(0, 0, ZX_INTERRUPT_VIRTUAL, intr_.reset_and_get_address()));
+ }
+
+ std::optional<ddk::MmioBuffer> usb_mmio_;
+ std::optional<ddk::MmioBuffer> phy_mmio_;
+ zx::interrupt intr_;
+};
+
+TEST_F(HciTest, TestReadEndpointNumber) {
+ ddk::MmioView v = usb_mmio_->View(0);
+ regs::EPINFO::Get().FromValue(0x33).WriteTo(&v);
+
+ TUsbHci hci(fake_ddk::kFakeParent,
+ *std::move(usb_mmio_),
+ *std::move(phy_mmio_),
+ intr_.release());
+
+ EXPECT_OK(hci.Init());
+ EXPECT_EQ(3, regs::INDEX::Get().ReadFrom(&v).selected_endpoint());
+ hci.DdkUnbind();
+}
+
+} // namespace mt_usb_hci
+
+int main(int argc, char *argv[]) {
+ return RUN_ALL_TESTS(argc, argv);
+}
diff --git a/zircon/system/dev/usb/mt-musb-host/usb-hci.cpp b/zircon/system/dev/usb/mt-musb-host/usb-hci.cpp
index bae360e..335c6a7 100644
--- a/zircon/system/dev/usb/mt-musb-host/usb-hci.cpp
+++ b/zircon/system/dev/usb/mt-musb-host/usb-hci.cpp
@@ -8,12 +8,14 @@
#include "usb-root-hub.h"
#include "usb-spew.h"
+#include <algorithm>
#include <ddk/binding.h>
#include <ddk/debug.h>
#include <ddk/device.h>
#include <ddk/driver.h>
#include <ddk/platform-defs.h>
#include <fbl/auto_call.h>
+#include <lib/device-protocol/pdev.h>
#include <lib/zx/time.h>
#include <soc/mt8167/mt8167-usb.h>
#include <soc/mt8167/mt8167-usb-phy.h>
@@ -197,9 +199,9 @@
}
void UsbHci::DdkUnbind() {
- if (irq_thread_) {
+ if (irq_thread_.joinable()) {
irq_.destroy();
- thrd_join(irq_thread_, nullptr);
+ irq_thread_.join();
}
}
@@ -214,14 +216,14 @@
}
auto go = [](void* arg) { return static_cast<UsbHci*>(arg)->IrqThread(); };
- auto rc = thrd_create_with_name(&irq_thread_, go, this, "usb-hci-irq-thread");
- if (rc != thrd_success) {
+ irq_thread_ = std::thread(go, this);
+ if (!irq_thread_.joinable()) {
return ZX_ERR_INTERNAL;
}
auto cleanup = fbl::MakeAutoCall([&]() {
irq_.destroy();
- thrd_join(irq_thread_, nullptr);
+ irq_thread_.join();
});
status = InitRootHub();
@@ -229,7 +231,7 @@
return status;
}
- status = InitFifo();
+ status = InitEndpointControllers();
if (status != ZX_OK) {
return status;
}
@@ -255,7 +257,7 @@
// See: MUSBMHDRC 13.2 for the order in which IRQ events need to be serviced.
if (irqs.conn()) HandleConnect();
if (irqs.discon()) HandleDisconnect();
- for (uint8_t i=0; i <= kMaxEpNum; i++) {
+ for (uint8_t i = 0; i <= std::max(rx_ep_count_, tx_ep_count_); i++) {
auto mask = static_cast<uint16_t>(1 << i);
if ((tx_irqs.ep_tx() & mask) || (rx_irqs.ep_rx() & mask)) {
// Here, note that each endpoint can either be an IN or OUT-type endpoint, but not both.
@@ -306,7 +308,7 @@
for (;;) {
status = irq_.wait(nullptr);
if (status == ZX_ERR_CANCELED) {
- zxlogf(TRACE, "error break\n");
+ zxlogf(TRACE, "irq thread break\n");
break;
} else if (status != ZX_OK) {
zxlogf(ERROR, "irq wait error: %s\n", zx_status_get_string(status));
@@ -363,29 +365,34 @@
return ZX_OK;
}
-zx_status_t UsbHci::InitFifo() {
+zx_status_t UsbHci::InitEndpointControllers() {
+ auto epinfo = regs::EPINFO::Get().ReadFrom(usb_mmio());
+ rx_ep_count_ = epinfo.rxendpoints();
+ tx_ep_count_ = epinfo.txendpoints();
+
// Each FIFO is initialized to the largest it could possibly be (singly-buffered). As endpoints
// are subsequently intialized, each FIFO will be appropriately resized based on the needs of
// the endpoint the FIFO supports. Here, note that FIFO assumes 64-bit wordsize.
constexpr uint32_t fifo_size = kFifoMaxSize >> 3;
uint32_t fifo_addr = (64 >> 3); // The first 64 bytes are used by endpoint-0.
- for (uint8_t i = 1; i <= kMaxEpNum; i++) {
+ for (uint8_t i = 1; i <= rx_ep_count_; i++) {
regs::INDEX::Get().FromValue(0).set_selected_endpoint(i).WriteTo(usb_mmio());
-
- regs::TXFIFOADD::Get().FromValue(0)
- .set_txfifoadd(static_cast<uint16_t>(fifo_addr))
- .WriteTo(usb_mmio());
- fifo_addr += fifo_size;
-
regs::RXFIFOADD::Get().FromValue(0)
.set_rxfifoadd(static_cast<uint16_t>(fifo_addr))
.WriteTo(usb_mmio());
fifo_addr += fifo_size;
// See: MUSBMHDRC section 3.10.1.
- regs::TXFIFOSZ::Get().FromValue(0).set_txsz(0x9).WriteTo(usb_mmio());
regs::RXFIFOSZ::Get().FromValue(0).set_rxsz(0x9).WriteTo(usb_mmio());
}
+ for (uint8_t i = 1; i <= tx_ep_count_; i++) {
+ regs::INDEX::Get().FromValue(0).set_selected_endpoint(i).WriteTo(usb_mmio());
+ regs::TXFIFOADD::Get().FromValue(0)
+ .set_txfifoadd(static_cast<uint16_t>(fifo_addr))
+ .WriteTo(usb_mmio());
+ fifo_addr += fifo_size;
+ regs::TXFIFOSZ::Get().FromValue(0).set_txsz(0x9).WriteTo(usb_mmio());
+ }
return ZX_OK;
}
diff --git a/zircon/system/dev/usb/mt-musb-host/usb-hci.h b/zircon/system/dev/usb/mt-musb-host/usb-hci.h
index d6349fa..c7971ae 100644
--- a/zircon/system/dev/usb/mt-musb-host/usb-hci.h
+++ b/zircon/system/dev/usb/mt-musb-host/usb-hci.h
@@ -9,14 +9,13 @@
#include <array>
#include <ddktl/device.h>
-#include <lib/device-protocol/pdev.h>
#include <ddktl/protocol/usb/hci.h>
#include <ddktl/protocol/usb/bus.h>
#include <lib/mmio/mmio.h>
#include <lib/zx/interrupt.h>
#include <memory>
#include <optional>
-#include <threads.h>
+#include <thread>
namespace mt_usb_hci {
@@ -71,18 +70,19 @@
zx_status_t UsbHciCancelAll(uint32_t device_id, uint8_t ep_address);
size_t UsbHciGetRequestSize();
+protected:
+ // Initialize the USB HCI.
+ zx_status_t Init();
+
private:
ddk::MmioBuffer* usb_mmio() { return &usb_mmio_; }
ddk::MmioBuffer* phy_mmio() { return &phy_mmio_; }
UsbRootHub* root_hub() { return static_cast<UsbRootHub*>(device_[kRootHubId].get()); }
- // Initialize the USB HCI.
- zx_status_t Init();
-
// Initialize the given USB HCI sub-components.
zx_status_t InitPhy();
zx_status_t InitRootHub();
- zx_status_t InitFifo();
+ zx_status_t InitEndpointControllers();
// Start a USB session.
void StartSession();
@@ -104,7 +104,7 @@
zx::interrupt irq_;
// An async. thread responding to USB-common interrupt events.
- thrd_t irq_thread_;
+ std::thread irq_thread_;
// The USB-bus device, used to announce new physical devices to the upper USB stack.
ddk::UsbBusInterfaceProtocolClient bus_;
@@ -113,6 +113,10 @@
// reserved and should not be used. Additionally, device_[128] is reserved for the logical usb
// root-hub device.
std::array<std::unique_ptr<UsbDevice>, kMaxDevices> device_;
+
+ // The count of supported RX/TX endpoints in the design.
+ int rx_ep_count_;
+ int tx_ep_count_;
};
} // namespace mt_usb_hci
diff --git a/zircon/system/utest/BUILD.gn b/zircon/system/utest/BUILD.gn
index c650712..f8ec0e4 100644
--- a/zircon/system/utest/BUILD.gn
+++ b/zircon/system/utest/BUILD.gn
@@ -75,6 +75,7 @@
"$zx/system/dev/thermal/aml-thermal-s912:aml-thermal-s912-test",
"$zx/system/dev/thermal/mtk-thermal:mtk-thermal-test",
"$zx/system/dev/usb/mt-musb-host:mt-hci-request-queue-test",
+ "$zx/system/dev/usb/mt-musb-host:mt-hci-test",
"$zx/system/dev/usb/mt-musb-host:mt-hci-transaction-test",
"$zx/system/uapp/disk-pave:install-disk-image-test",
"$zx/system/uapp/nand-util:nand-util-test",