| // Copyright 2018 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. |
| |
| #ifndef SRC_CONNECTIVITY_ETHERNET_DRIVERS_DWMAC_DWMAC_H_ |
| #define SRC_CONNECTIVITY_ETHERNET_DRIVERS_DWMAC_DWMAC_H_ |
| |
| #include <fidl/fuchsia.hardware.ethernet.board/cpp/wire.h> |
| #include <fuchsia/hardware/ethernet/cpp/banjo.h> |
| #include <fuchsia/hardware/ethernet/mac/cpp/banjo.h> |
| #include <fuchsia/hardware/test/c/banjo.h> |
| #include <lib/ddk/device.h> |
| #include <lib/device-protocol/pdev-fidl.h> |
| #include <lib/mmio/mmio.h> |
| #include <lib/sync/completion.h> |
| #include <lib/zx/interrupt.h> |
| #include <lib/zx/vmo.h> |
| #include <threads.h> |
| #include <zircon/compiler.h> |
| #include <zircon/types.h> |
| |
| #include <atomic> |
| #include <optional> |
| |
| #include <ddktl/device.h> |
| #include <fbl/mutex.h> |
| |
| #include "pinned-buffer.h" |
| |
| // clang-format off |
| #define DW_MAC_MAC_CONF (0x0000) |
| #define DW_MAC_MAC_FRAMEFILT (0x0004) |
| #define DW_MAC_MAC_HASHTABLEHI (0x0008) |
| #define DW_MAC_MAC_HASHTABLELO (0x000c) |
| #define DW_MAC_MAC_MIIADDR (0x0010) |
| #define DW_MAC_MAC_MIIDATA (0x0014) |
| #define DW_MAC_MAC_FLOWCONTROL (0x0018) |
| #define DW_MAC_MAC_VALANTAG (0x001c) |
| #define DW_MAC_MAC_VERSION (0x0020) |
| #define DW_MAC_MAC_DEBUG (0x0024) |
| #define DW_MAC_MAC_REMOTEWAKEFILT (0x0028) |
| #define DW_MAC_MAC_PMTCONTROL (0x002c) |
| #define DW_MAC_MAC_LPICONTROL (0x0030) |
| #define DW_MAC_MAC_LPITIMERS (0x0034) |
| #define DW_MAC_MAC_INTREG (0x0038) |
| #define DW_MAC_MAC_INTMASK (0x003c) |
| #define DW_MAC_MAC_MACADDR0HI (0x0040) |
| #define DW_MAC_MAC_MACADDR0LO (0x0044) |
| #define DW_MAC_MAC_MACADDR1HI (0x0048) |
| #define DW_MAC_MAC_MACADDR1LO (0x004c) |
| #define DW_MAC_MAC_RGMIISTATUS (0x00d8) |
| |
| // Offsets of the mac management counters |
| #define DW_MAC_MMC_CNTRL (0x0100) |
| #define DW_MAC_MMC_INTR_RX (0x0104) |
| #define DW_MAC_MMC_INTR_TX (0x0108) |
| #define DW_MAC_MMC_INTR_MASK_RX (0x010c) |
| #define DW_MAC_MMC_INTR_MASK_TX (0x0110) |
| #define DW_MAC_MMC_RXFRAMECOUNT_GB (0x0180) |
| #define DW_MAC_MMC_RXOCTETCOUNT_GB (0x0184) |
| #define DW_MAC_MMC_RXOCTETCOUNT_G (0x0188) |
| #define DW_MAC_MMC_IPC_INTR_MASK_RX (0x0200) |
| #define DW_MAC_MMC_IPC_INTR_RX (0x0208) |
| |
| // Offsets of DMA registers |
| #define DW_MAC_DMA_BUSMODE (0x1000) |
| #define DW_MAC_DMA_TXPOLLDEMAND (0x1004) |
| #define DW_MAC_DMA_RXPOLLDEMAND (0x1008) |
| #define DW_MAC_DMA_RXDESCLISTADDR (0x100c) |
| #define DW_MAC_DMA_TXDESCLISTADDR (0x1010) |
| #define DW_MAC_DMA_STATUS (0x1014) |
| #define DW_MAC_DMA_OPMODE (0x1018) |
| #define DW_MAC_DMA_INTENABLE (0x101c) |
| #define DW_MAC_DMA_MISSEDFRAMES (0x1020) |
| #define DW_MAC_DMA_RXWDT (0x1024) |
| #define DW_MAC_DMA_AXIBUSMODE (0x1028) |
| #define DW_MAC_DMA_AXISTATUS (0x102c) |
| #define DW_MAC_DMA_CURRHOSTTXDESC (0x1048) |
| #define DW_MAC_DMA_CURRHOSTRXDESC (0x104c) |
| #define DW_MAC_DMA_CURRHOSTTXBUFFADDR (0x1050) |
| #define DW_MAC_DMA_CURRHOSTRXBUFFADDR (0x1054) |
| #define DW_MAC_DMA_HWFEATURE (0x1058) |
| |
| //DMA transaction descriptors |
| typedef volatile struct dw_dmadescr { |
| uint32_t txrx_status; |
| uint32_t dmamac_cntl; |
| uint32_t dmamac_addr; |
| uint32_t dmamac_next; |
| } __ALIGNED(64) dw_dmadescr_t; |
| // clang-format on |
| |
| namespace eth { |
| |
| class EthPhyFunction; |
| class EthMacFunction; |
| |
| class DWMacDevice : public ddk::Device<DWMacDevice, ddk::Unbindable, ddk::Suspendable> { |
| public: |
| DWMacDevice(zx_device_t* device, ddk::PDevFidl pdev, |
| fidl::ClientEnd<fuchsia_hardware_ethernet_board::EthBoard> eth_board); |
| |
| static zx_status_t Create(void* ctx, zx_device_t* device); |
| |
| void DdkRelease(); |
| void DdkUnbind(ddk::UnbindTxn txn); |
| void DdkSuspend(ddk::SuspendTxn txn); |
| |
| // ZX_PROTOCOL_ETHERNET_IMPL ops. |
| zx_status_t EthernetImplQuery(uint32_t options, ethernet_info_t* info); |
| void EthernetImplStop() __TA_EXCLUDES(lock_); |
| zx_status_t EthernetImplStart(const ethernet_ifc_protocol_t* ifc) __TA_EXCLUDES(lock_); |
| void EthernetImplQueueTx(uint32_t options, ethernet_netbuf_t* netbuf, |
| ethernet_impl_queue_tx_callback completion_cb, void* cookie) |
| __TA_EXCLUDES(lock_); |
| zx_status_t EthernetImplSetParam(uint32_t param, int32_t value, const uint8_t* data, |
| size_t data_size); |
| void EthernetImplGetBti(zx::bti* bti); |
| |
| // ZX_PROTOCOL_ETH_MAC ops. |
| zx_status_t EthMacMdioWrite(uint32_t reg, uint32_t val); |
| zx_status_t EthMacMdioRead(uint32_t reg, uint32_t* val); |
| zx_status_t EthMacRegisterCallbacks(const eth_mac_callbacks_t* callbacks); |
| |
| private: |
| friend class EthMacFunction; |
| friend class EthPhyFunction; |
| |
| zx_status_t InitBuffers(); |
| zx_status_t InitDevice(); |
| zx_status_t DeInitDevice() __TA_REQUIRES(lock_); |
| zx_status_t InitPdev(); |
| zx_status_t ShutDown() __TA_EXCLUDES(lock_); |
| |
| void UpdateLinkStatus() __TA_REQUIRES(lock_); |
| void DumpRegisters(); |
| void DumpStatus(uint32_t status); |
| void ReleaseBuffers(); |
| void ProcRxBuffer(uint32_t int_status) __TA_EXCLUDES(lock_); |
| uint32_t DmaRxStatus(); |
| |
| int Thread() __TA_EXCLUDES(lock_); |
| int WorkerThread(); |
| |
| zx_status_t GetMAC(zx_device_t* dev); |
| |
| // Number each of tx/rx transaction descriptors |
| // 4096 buffers = ~48ms of packets |
| static constexpr uint32_t kNumDesc = 4096; |
| // Size of each transaction buffer |
| static constexpr uint32_t kTxnBufSize = 4096; |
| |
| dw_dmadescr_t* tx_descriptors_ = nullptr; |
| dw_dmadescr_t* rx_descriptors_ = nullptr; |
| |
| fbl::RefPtr<PinnedBuffer> txn_buffer_; |
| fbl::RefPtr<PinnedBuffer> desc_buffer_; |
| |
| uint8_t* tx_buffer_ = nullptr; |
| uint32_t curr_tx_buf_ = 0; |
| uint8_t* rx_buffer_ = nullptr; |
| uint32_t curr_rx_buf_ = 0; |
| |
| // designware mac options |
| uint32_t options_ = 0; |
| |
| // ethermac fields |
| uint32_t features_ = 0; |
| uint32_t mtu_ = 0; |
| uint8_t mac_[MAC_ARRAY_LENGTH] = {}; |
| uint16_t mii_addr_ = 0; |
| |
| zx::bti bti_; |
| zx::interrupt dma_irq_; |
| |
| ddk::PDevFidl pdev_; |
| fidl::WireSyncClient<fuchsia_hardware_ethernet_board::EthBoard> eth_board_; |
| |
| std::optional<fdf::MmioBuffer> mmio_; |
| |
| fbl::Mutex lock_; |
| ddk::EthernetIfcProtocolClient ethernet_client_ __TA_GUARDED(lock_); |
| |
| // Only accessed from Thread, so not locked. |
| bool online_ = false; |
| |
| // statistics |
| uint32_t bus_errors_; |
| uint32_t tx_counter_ = 0; |
| uint32_t rx_packet_ = 0; |
| uint32_t loop_count_ = 0; |
| |
| std::atomic<bool> running_; |
| |
| thrd_t thread_; |
| thrd_t worker_thread_; |
| |
| // PHY callbacks. |
| eth_mac_callbacks_t cbs_; |
| |
| // Callbacks registered signal. |
| sync_completion_t cb_registered_signal_; |
| |
| EthPhyFunction* phy_function_; |
| EthMacFunction* mac_function_; |
| }; |
| |
| using EthPhyFunctionType = ddk::Device<EthPhyFunction, ddk::Unbindable, ddk::Suspendable>; |
| class EthPhyFunction : public EthPhyFunctionType, |
| public ddk::EthernetImplProtocol<EthPhyFunction, ddk::base_protocol> { |
| public: |
| explicit EthPhyFunction(zx_device_t* device, DWMacDevice* peripheral) |
| : EthPhyFunctionType(device), device_(peripheral) {} |
| |
| void DdkUnbind(ddk::UnbindTxn txn) { |
| device_->phy_function_ = nullptr; |
| device_ = nullptr; |
| txn.Reply(); |
| } |
| void DdkSuspend(ddk::SuspendTxn txn) { |
| device_->phy_function_ = nullptr; |
| device_ = nullptr; |
| txn.Reply(ZX_OK, txn.requested_state()); |
| } |
| void DdkRelease() { |
| ZX_ASSERT(device_ == nullptr); |
| delete this; |
| } |
| |
| // ZX_PROTOCOL_ETHERNET_IMPL ops. |
| zx_status_t EthernetImplQuery(uint32_t options, ethernet_info_t* info) { |
| return device_->EthernetImplQuery(options, info); |
| } |
| void EthernetImplStop() { device_->EthernetImplStop(); } |
| zx_status_t EthernetImplStart(const ethernet_ifc_protocol_t* ifc) { |
| return device_->EthernetImplStart(ifc); |
| } |
| void EthernetImplQueueTx(uint32_t options, ethernet_netbuf_t* netbuf, |
| ethernet_impl_queue_tx_callback completion_cb, void* cookie) { |
| device_->EthernetImplQueueTx(options, netbuf, completion_cb, cookie); |
| } |
| zx_status_t EthernetImplSetParam(uint32_t param, int32_t value, const uint8_t* data, |
| size_t data_size) { |
| return device_->EthernetImplSetParam(param, value, data, data_size); |
| } |
| void EthernetImplGetBti(zx::bti* bti) { device_->EthernetImplGetBti(bti); } |
| |
| private: |
| DWMacDevice* device_; |
| }; |
| |
| using EthMacFunctionType = ddk::Device<EthMacFunction, ddk::Unbindable, ddk::Suspendable>; |
| class EthMacFunction : public EthMacFunctionType, |
| public ddk::EthMacProtocol<EthMacFunction, ddk::base_protocol> { |
| public: |
| explicit EthMacFunction(zx_device_t* device, DWMacDevice* peripheral) |
| : EthMacFunctionType(device), device_(peripheral) {} |
| |
| void DdkUnbind(ddk::UnbindTxn txn) { |
| device_->mac_function_ = nullptr; |
| device_ = nullptr; |
| txn.Reply(); |
| } |
| void DdkSuspend(ddk::SuspendTxn txn) { |
| device_->mac_function_ = nullptr; |
| device_ = nullptr; |
| txn.Reply(ZX_OK, txn.requested_state()); |
| } |
| void DdkRelease() { |
| ZX_ASSERT(device_ == nullptr); |
| delete this; |
| } |
| |
| // ZX_PROTOCOL_ETH_MAC ops. |
| zx_status_t EthMacMdioWrite(uint32_t reg, uint32_t val) { |
| return device_->EthMacMdioWrite(reg, val); |
| } |
| zx_status_t EthMacMdioRead(uint32_t reg, uint32_t* val) { |
| return device_->EthMacMdioRead(reg, val); |
| } |
| zx_status_t EthMacRegisterCallbacks(const eth_mac_callbacks_t* callbacks) { |
| return device_->EthMacRegisterCallbacks(callbacks); |
| } |
| |
| private: |
| DWMacDevice* device_; |
| }; |
| |
| } // namespace eth |
| |
| #endif // SRC_CONNECTIVITY_ETHERNET_DRIVERS_DWMAC_DWMAC_H_ |