| // 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. |
| |
| #include <ddk/debug.h> |
| #include <ddk/metadata.h> |
| #include <ddk/protocol/platform-device.h> |
| #include <fbl/auto_call.h> |
| #include <fbl/auto_lock.h> |
| #include <fbl/ref_counted.h> |
| #include <fbl/ref_ptr.h> |
| #include <fbl/type_support.h> |
| #include <hw/arch_ops.h> |
| #include <hw/reg.h> |
| #include <lib/fzl/vmar-manager.h> |
| #include <soc/aml-s912/s912-hw.h> |
| #include <zircon/compiler.h> |
| #include <zircon/device/ethernet.h> |
| |
| #include <stdio.h> |
| #include <string.h> |
| |
| #include "aml-dwmac.h" |
| #include "dw-gmac-dma.h" |
| |
| namespace eth { |
| |
| enum { |
| PHY_RESET, |
| PHY_INTR, |
| }; |
| |
| #define MCU_I2C_REG_BOOT_EN_WOL 0x21 |
| #define MCU_I2C_REG_BOOT_EN_WOL_RESET_ENABLE 0x03 |
| |
| template <typename T, typename U> |
| static inline T* offset_ptr(U* ptr, size_t offset) { |
| return reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(ptr) + offset); |
| } |
| |
| int AmlDWMacDevice::Thread() { |
| zxlogf(INFO, "AmLogic ethmac started\n"); |
| |
| zx_status_t status; |
| while (true) { |
| status = dma_irq_.wait(nullptr); |
| if (!running_.load()) { |
| status = ZX_OK; |
| break; |
| } |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "aml-dwmac: Interrupt error\n"); |
| break; |
| } |
| uint32_t stat = dwdma_regs_->status; |
| dwdma_regs_->status = stat; |
| |
| if (stat & DMA_STATUS_GLI) { |
| fbl::AutoLock lock(&lock_); //Note: limited scope of autolock |
| UpdateLinkStatus(); |
| } |
| if (stat & DMA_STATUS_RI) { |
| ProcRxBuffer(stat); |
| } |
| if (stat & DMA_STATUS_AIS) { |
| bus_errors_++; |
| zxlogf(ERROR, "aml-dwmac: abnormal interrupt %08x\n", stat); |
| } |
| } |
| return status; |
| } |
| |
| void AmlDWMacDevice::UpdateLinkStatus() { |
| bool temp = dwmac_regs_->rgmiistatus & GMAC_RGMII_STATUS_LNKSTS; |
| if (temp != online_) { |
| online_ = temp; |
| if (ethmac_proxy_ != nullptr) { |
| ethmac_proxy_->Status(online_ ? ETH_STATUS_ONLINE : 0u); |
| } else { |
| zxlogf(ERROR, "aml-dwmac: System not ready\n"); |
| } |
| } |
| if (online_) { |
| dwmac_regs_->conf |= GMAC_CONF_TE | GMAC_CONF_RE; |
| } else { |
| dwmac_regs_->conf &= ~(GMAC_CONF_TE | GMAC_CONF_RE); |
| } |
| zxlogf(INFO, "aml-dwmac: Link is now %s\n", online_ ? "up" : "down"); |
| } |
| |
| zx_status_t AmlDWMacDevice::InitPdev() { |
| |
| zx_status_t status = device_get_protocol(parent_, |
| ZX_PROTOCOL_PLATFORM_DEV, |
| &pdev_); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| status = device_get_protocol(parent_, ZX_PROTOCOL_GPIO, &gpio_); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| gpio_config_out(&gpio_, PHY_RESET, 0); |
| ResetPhy(); |
| |
| // Map amlogic peripheral control registers |
| status = pdev_map_mmio_buffer(&pdev_, 0, ZX_CACHE_POLICY_UNCACHED_DEVICE, |
| &periph_regs_iobuff_); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "aml-dwmac: could not map periph mmio: %d\n", status); |
| return status; |
| } |
| |
| // Map mac control registers and dma control registers |
| status = pdev_map_mmio_buffer(&pdev_, 1, ZX_CACHE_POLICY_UNCACHED_DEVICE, |
| &dwmac_regs_iobuff_); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "aml-dwmac: could not map dwmac mmio: %d\n", status); |
| return status; |
| } |
| dwmac_regs_ = static_cast<dw_mac_regs_t*>(io_buffer_virt(&dwmac_regs_iobuff_)); |
| dwdma_regs_ = offset_ptr<dw_dma_regs_t>(dwmac_regs_, DW_DMA_BASE_OFFSET); |
| |
| // Map HHI regs (clocks and power domains) |
| status = pdev_map_mmio_buffer(&pdev_, 2, ZX_CACHE_POLICY_UNCACHED_DEVICE, |
| &hhi_regs_iobuff_); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "aml-dwmac: could not map hiu mmio: %d\n", status); |
| return status; |
| } |
| |
| // Map dma interrupt |
| status = pdev_map_interrupt(&pdev_, 0, dma_irq_.reset_and_get_address()); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "aml-dwmac: could not map dma interrupt\n"); |
| return status; |
| } |
| |
| // Get our bti |
| status = pdev_get_bti(&pdev_, 0, bti_.reset_and_get_address()); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "aml-dwmac: could not obtain bti: %d\n", status); |
| return status; |
| } |
| |
| // i2c for MCU messages |
| status = device_get_protocol(parent_, ZX_PROTOCOL_I2C, &i2c_); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| return status; |
| } |
| |
| void AmlDWMacDevice::ResetPhy() { |
| gpio_write(&gpio_, PHY_RESET, 0); |
| zx_nanosleep(zx_deadline_after(ZX_MSEC(100))); |
| gpio_write(&gpio_, PHY_RESET, 1); |
| zx_nanosleep(zx_deadline_after(ZX_MSEC(100))); |
| } |
| |
| void AmlDWMacDevice::ConfigPhy() { |
| uint32_t val; |
| |
| // WOL reset |
| MDIOWrite(MII_EPAGSR, 0xd40); |
| MDIOWrite(22, 0x20); |
| MDIOWrite(MII_EPAGSR, 0); |
| |
| MDIOWrite(MII_EPAGSR, 0xd8c); |
| MDIOWrite(16, (mac_[1] << 8) | mac_[0]); |
| MDIOWrite(17, (mac_[3] << 8) | mac_[2]); |
| MDIOWrite(18, (mac_[5] << 8) | mac_[4]); |
| MDIOWrite(MII_EPAGSR, 0); |
| |
| MDIOWrite(MII_EPAGSR, 0xd8a); |
| MDIOWrite(17, 0x9fff); |
| MDIOWrite(MII_EPAGSR, 0); |
| |
| MDIOWrite(MII_EPAGSR, 0xd8a); |
| MDIOWrite(16, 0x1000); |
| MDIOWrite(MII_EPAGSR, 0); |
| |
| MDIOWrite(MII_EPAGSR, 0xd80); |
| MDIOWrite(16, 0x3000); |
| MDIOWrite(17, 0x0020); |
| MDIOWrite(18, 0x03c0); |
| MDIOWrite(19, 0x0000); |
| MDIOWrite(20, 0x0000); |
| MDIOWrite(21, 0x0000); |
| MDIOWrite(22, 0x0000); |
| MDIOWrite(23, 0x0000); |
| MDIOWrite(MII_EPAGSR, 0); |
| |
| MDIOWrite(MII_EPAGSR, 0xd8a); |
| MDIOWrite(19, 0x1002); |
| MDIOWrite(MII_EPAGSR, 0); |
| |
| // Fix txdelay issuee for rtl8211. When a hw reset is performed |
| // on the phy, it defaults to having an extra delay in the TXD path. |
| // Since we reset the phy, this needs to be corrected. |
| MDIOWrite(MII_EPAGSR, 0xd08); |
| MDIORead(0x11, &val); |
| val &= ~0x100; |
| MDIOWrite(0x11, val); |
| MDIOWrite(MII_EPAGSR, 0x00); |
| |
| //Enable GigE advertisement |
| MDIOWrite(MII_GBCR, 1 << 9); |
| |
| //Restart advertisements |
| MDIORead(MII_BMCR, &val); |
| val |= BMCR_ANENABLE | BMCR_ANRESTART; |
| val &= ~BMCR_ISOLATE; |
| MDIOWrite(MII_BMCR, val); |
| } |
| |
| zx_status_t AmlDWMacDevice::Create(zx_device_t* device) { |
| |
| auto mac_device = fbl::make_unique<AmlDWMacDevice>(device); |
| |
| zx_status_t status = mac_device->InitPdev(); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| // Initialize AMLogic peripheral registers associated with dwmac |
| void* pregs = io_buffer_virt(&mac_device->periph_regs_iobuff_); |
| //Sorry about the magic...rtfm |
| writel(0x1621, offset_ptr<uint32_t>(pregs, PER_ETH_REG0)); |
| writel(0x20000, offset_ptr<uint32_t>(pregs, PER_ETH_REG1)); |
| |
| writel(REG2_ETH_REG2_REVERSED | REG2_INTERNAL_PHY_ID, |
| offset_ptr<uint32_t>(pregs, PER_ETH_REG2)); |
| |
| writel(REG3_CLK_IN_EN | REG3_ETH_REG3_19_RESVERD | |
| REG3_CFG_PHY_ADDR | REG3_CFG_MODE | |
| REG3_CFG_EN_HIGH | REG3_ETH_REG3_2_RESERVED, |
| offset_ptr<uint32_t>(pregs, PER_ETH_REG3)); |
| |
| // Enable clocks and power domain for dwmac |
| void* hregs = io_buffer_virt(&mac_device->hhi_regs_iobuff_); |
| set_bitsl(1 << 3, offset_ptr<uint32_t>(hregs, HHI_GCLK_MPEG1)); |
| clr_bitsl((1 << 3) | (1 << 2), offset_ptr<uint32_t>(hregs, HHI_MEM_PD_REG0)); |
| |
| // get and cache the mac address |
| mac_device->GetMAC(device); |
| |
| //Reset the dma peripheral |
| mac_device->dwdma_regs_->busmode |= DMAMAC_SRST; |
| uint32_t loop_count = 10; |
| do { |
| zx_nanosleep(zx_deadline_after(ZX_MSEC(10))); |
| loop_count--; |
| } while ((mac_device->dwdma_regs_->busmode & DMAMAC_SRST) && loop_count); |
| if (!loop_count) { |
| return ZX_ERR_TIMED_OUT; |
| } |
| |
| // mac address register was erased by the reset; set it! |
| mac_device->dwmac_regs_->macaddr0hi = (mac_device->mac_[5] << 8) | (mac_device->mac_[4] << 0); |
| mac_device->dwmac_regs_->macaddr0lo = (mac_device->mac_[3] << 24) | (mac_device->mac_[2] << 16) | (mac_device->mac_[1] << 8) | (mac_device->mac_[0] << 0); |
| |
| auto cleanup = fbl::MakeAutoCall([&]() { mac_device->ShutDown(); }); |
| |
| status = mac_device->InitBuffers(); |
| if (status != ZX_OK) |
| return status; |
| |
| //reset the phy |
| mac_device->ResetPhy(); |
| //configure phy |
| mac_device->ConfigPhy(); |
| // WOL reset enable to MCU |
| uint8_t write_buf[2] = {MCU_I2C_REG_BOOT_EN_WOL, MCU_I2C_REG_BOOT_EN_WOL_RESET_ENABLE}; |
| status = i2c_write_sync(&mac_device->i2c_, 0, write_buf, sizeof write_buf); |
| if (status) { |
| zxlogf(ERROR, "aml-dwmac: WOL reset enable to MCU failed: %d\n", status); |
| } |
| |
| mac_device->InitDevice(); |
| |
| auto thunk = [](void* arg) -> int { return reinterpret_cast<AmlDWMacDevice*>(arg)->Thread(); }; |
| |
| mac_device->running_.store(true); |
| int ret = thrd_create_with_name(&mac_device->thread_, thunk, |
| reinterpret_cast<void*>(mac_device.get()), |
| "amlmac-thread"); |
| ZX_DEBUG_ASSERT(ret == thrd_success); |
| |
| status = mac_device->DdkAdd("AmLogic dwMac"); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "aml-dwmac: Could not create eth device: %d\n", status); |
| return status; |
| } else { |
| zxlogf(INFO, "aml-dwmac: Added AmLogic dwMac device\n"); |
| } |
| |
| cleanup.cancel(); |
| |
| // mac_device intentionally leaked as it is now held by DevMgr |
| __UNUSED auto ptr = mac_device.release(); |
| return ZX_OK; |
| } |
| |
| zx_status_t AmlDWMacDevice::InitBuffers() { |
| |
| fbl::RefPtr<fzl::VmarManager> vmar_mgr; |
| |
| constexpr size_t kDescSize = ROUNDUP(2 * kNumDesc * sizeof(dw_dmadescr_t), PAGE_SIZE); |
| |
| constexpr size_t kBufSize = 2 * kNumDesc * kTxnBufSize; |
| |
| txn_buffer_ = PinnedBuffer::Create(kBufSize, bti_, ZX_CACHE_POLICY_CACHED); |
| desc_buffer_ = PinnedBuffer::Create(kDescSize, bti_, ZX_CACHE_POLICY_UNCACHED); |
| |
| tx_buffer_ = static_cast<uint8_t*>(txn_buffer_->GetBaseAddress()); |
| zx_cache_flush(tx_buffer_, kBufSize, ZX_CACHE_FLUSH_DATA | ZX_CACHE_FLUSH_INVALIDATE); |
| //rx buffer right after tx |
| rx_buffer_ = &tx_buffer_[kBufSize / 2]; |
| |
| tx_descriptors_ = static_cast<dw_dmadescr_t*>(desc_buffer_->GetBaseAddress()); |
| //rx descriptors right after tx |
| rx_descriptors_ = &tx_descriptors_[kNumDesc]; |
| |
| zx_paddr_t tmpaddr; |
| |
| // Initialize descriptors. Doing tx and rx all at once |
| for (uint i = 0; i < kNumDesc; i++) { |
| |
| desc_buffer_->LookupPhys(((i + 1) % kNumDesc) * sizeof(dw_dmadescr_t), &tmpaddr); |
| tx_descriptors_[i].dmamac_next = static_cast<uint32_t>(tmpaddr); |
| |
| txn_buffer_->LookupPhys(i * kTxnBufSize, &tmpaddr); |
| tx_descriptors_[i].dmamac_addr = static_cast<uint32_t>(tmpaddr); |
| tx_descriptors_[i].txrx_status = 0; |
| tx_descriptors_[i].dmamac_cntl = DESC_TXCTRL_TXCHAIN; |
| |
| desc_buffer_->LookupPhys((((i + 1) % kNumDesc) + kNumDesc) * sizeof(dw_dmadescr_t), |
| &tmpaddr); |
| rx_descriptors_[i].dmamac_next = static_cast<uint32_t>(tmpaddr); |
| |
| txn_buffer_->LookupPhys((i + kNumDesc) * kTxnBufSize, &tmpaddr); |
| rx_descriptors_[i].dmamac_addr = static_cast<uint32_t>(tmpaddr); |
| rx_descriptors_[i].dmamac_cntl = |
| (MAC_MAX_FRAME_SZ & DESC_RXCTRL_SIZE1MASK) | |
| DESC_RXCTRL_RXCHAIN; |
| |
| rx_descriptors_[i].txrx_status = DESC_RXSTS_OWNBYDMA; |
| } |
| |
| desc_buffer_->LookupPhys(0, &tmpaddr); |
| dwdma_regs_->txdesclistaddr = static_cast<uint32_t>(tmpaddr); |
| |
| desc_buffer_->LookupPhys(kNumDesc * sizeof(dw_dmadescr_t), &tmpaddr); |
| dwdma_regs_->rxdesclistaddr = static_cast<uint32_t>(tmpaddr); |
| return ZX_OK; |
| } |
| |
| zx_handle_t AmlDWMacDevice::EthmacGetBti() { |
| |
| return bti_.get(); |
| } |
| |
| zx_status_t AmlDWMacDevice::MDIOWrite(uint32_t reg, uint32_t val) { |
| |
| dwmac_regs_->miidata = val; |
| |
| uint32_t miiaddr = (mii_addr_ << MIIADDRSHIFT) | |
| (reg << MIIREGSHIFT) | |
| MII_WRITE; |
| |
| dwmac_regs_->miiaddr = miiaddr | MII_CLKRANGE_150_250M | MII_BUSY; |
| |
| zx_time_t deadline = zx_deadline_after(ZX_MSEC(3)); |
| do { |
| if (!(dwmac_regs_->miiaddr & MII_BUSY)) { |
| return ZX_OK; |
| } |
| zx_nanosleep(zx_deadline_after(ZX_USEC(10))); |
| } while (zx_clock_get_monotonic() < deadline); |
| return ZX_ERR_TIMED_OUT; |
| } |
| |
| zx_status_t AmlDWMacDevice::MDIORead(uint32_t reg, uint32_t* val) { |
| |
| uint32_t miiaddr = (mii_addr_ << MIIADDRSHIFT) | |
| (reg << MIIREGSHIFT); |
| |
| dwmac_regs_->miiaddr = miiaddr | MII_CLKRANGE_150_250M | MII_BUSY; |
| |
| zx_time_t deadline = zx_deadline_after(ZX_MSEC(3)); |
| do { |
| if (!(dwmac_regs_->miiaddr & MII_BUSY)) { |
| *val = dwmac_regs_->miidata; |
| return ZX_OK; |
| } |
| zx_nanosleep(zx_deadline_after(ZX_USEC(10))); |
| } while (zx_clock_get_monotonic() < deadline); |
| return ZX_ERR_TIMED_OUT; |
| } |
| |
| AmlDWMacDevice::AmlDWMacDevice(zx_device_t* device) |
| : ddk::Device<AmlDWMacDevice, ddk::Unbindable>(device) { |
| } |
| |
| void AmlDWMacDevice::ReleaseBuffers() { |
| io_buffer_release(&periph_regs_iobuff_); |
| io_buffer_release(&hhi_regs_iobuff_); |
| io_buffer_release(&dwmac_regs_iobuff_); |
| //Unpin the memory used for the dma buffers |
| if (txn_buffer_->UnPin() != ZX_OK) { |
| zxlogf(ERROR, "aml_dwmac: Error unpinning transaction buffers\n"); |
| } |
| if (desc_buffer_->UnPin() != ZX_OK) { |
| zxlogf(ERROR, "aml-dwmac: Error unpinning description buffers\n"); |
| } |
| } |
| |
| void AmlDWMacDevice::DdkRelease() { |
| zxlogf(INFO, "AmLogic Ethmac release...\n"); |
| delete this; |
| } |
| |
| void AmlDWMacDevice::DdkUnbind() { |
| zxlogf(INFO, "AmLogic Ethmac DdkUnbind\n"); |
| ShutDown(); |
| DdkRemove(); |
| } |
| |
| zx_status_t AmlDWMacDevice::ShutDown() { |
| running_.store(false); |
| dma_irq_.destroy(); |
| thrd_join(thread_, NULL); |
| fbl::AutoLock lock(&lock_); |
| online_ = false; |
| ethmac_proxy_.reset(); |
| DeInitDevice(); |
| ReleaseBuffers(); |
| |
| return ZX_OK; |
| } |
| |
| zx_status_t AmlDWMacDevice::GetMAC(zx_device_t* dev) { |
| // look for MAC address device metadata |
| // metadata is padded so we need buffer size > 6 bytes |
| uint8_t buffer[16]; |
| size_t actual; |
| zx_status_t status = device_get_metadata(dev, DEVICE_METADATA_MAC_ADDRESS, buffer, |
| sizeof(buffer), &actual); |
| if (status != ZX_OK || actual < 6) { |
| zxlogf(ERROR, "aml_dwmac: MAC address metadata load failed. Falling back on HW setting."); |
| // read MAC address from hardware register |
| uint32_t hi = dwmac_regs_->macaddr0hi; |
| uint32_t lo = dwmac_regs_->macaddr0lo; |
| |
| /* Extract the MAC address from the high and low words */ |
| buffer[0] = static_cast<uint8_t>(lo & 0xff); |
| buffer[1] = static_cast<uint8_t>((lo >> 8) & 0xff); |
| buffer[2] = static_cast<uint8_t>((lo >> 16) & 0xff); |
| buffer[3] = static_cast<uint8_t>((lo >> 24) & 0xff); |
| buffer[4] = static_cast<uint8_t>(hi & 0xff); |
| buffer[5] = static_cast<uint8_t>((hi >> 8) & 0xff); |
| } |
| |
| zxlogf(INFO, "aml_dwmac: MAC address %02x:%02x:%02x:%02x:%02x:%02x\n", |
| buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5]); |
| memcpy(mac_, buffer, sizeof mac_); |
| return ZX_OK; |
| } |
| |
| zx_status_t AmlDWMacDevice::EthmacQuery(uint32_t options, ethmac_info_t* info) { |
| memset(info, 0, sizeof(*info)); |
| info->features = ETHMAC_FEATURE_DMA; |
| info->mtu = 1500; |
| memcpy(info->mac, mac_, sizeof info->mac); |
| return ZX_OK; |
| } |
| |
| void AmlDWMacDevice::EthmacStop() { |
| zxlogf(INFO, "Stopping AmLogic Ethermac\n"); |
| fbl::AutoLock lock(&lock_); |
| ethmac_proxy_.reset(); |
| } |
| |
| zx_status_t AmlDWMacDevice::EthmacStart(const ethmac_ifc_t* ifc) { |
| fbl::AutoLock lock(&lock_); |
| |
| if (ethmac_proxy_ != nullptr) { |
| zxlogf(ERROR, "aml_dwmac: Already bound!!!"); |
| return ZX_ERR_ALREADY_BOUND; |
| } else { |
| ethmac_proxy_ = fbl::make_unique<ddk::EthmacIfcProxy>(ifc); |
| UpdateLinkStatus(); |
| zxlogf(INFO, "aml_dwmac: Started\n"); |
| } |
| return ZX_OK; |
| } |
| |
| zx_status_t AmlDWMacDevice::InitDevice() { |
| |
| dwdma_regs_->intenable = 0; |
| dwdma_regs_->busmode = X8PBL | DMA_PBL; |
| |
| dwdma_regs_->opmode = DMA_OPMODE_TSF | DMA_OPMODE_RSF; |
| |
| dwdma_regs_->opmode |= DMA_OPMODE_SR | DMA_OPMODE_ST; //start tx and rx |
| |
| //Clear all the interrupt flags |
| dwdma_regs_->status = ~0; |
| |
| //Enable Interrupts |
| dwdma_regs_->intenable = DMA_INT_NIE | DMA_INT_AIE | DMA_INT_FBE | |
| DMA_INT_RIE | DMA_INT_RUE | DMA_INT_OVE | |
| DMA_INT_UNE | DMA_INT_TSE | DMA_INT_RSE; |
| |
| dwmac_regs_->macaddr1lo = 0; |
| dwmac_regs_->macaddr1hi = 0; |
| dwmac_regs_->hashtablehigh = 0xffffffff; |
| dwmac_regs_->hashtablelow = 0xffffffff; |
| |
| //TODO - configure filters |
| zxlogf(INFO, "macaddr0hi = %08x\n", dwmac_regs_->macaddr0hi); |
| zxlogf(INFO, "macaddr0lo = %08x\n", dwmac_regs_->macaddr0lo); |
| |
| dwmac_regs_->framefilt |= (1 << 10) | (1 << 4) | (1 << 0); //promiscuous |
| |
| dwmac_regs_->conf = GMAC_CORE_INIT; |
| |
| return ZX_OK; |
| } |
| |
| zx_status_t AmlDWMacDevice::DeInitDevice() { |
| //Disable Interrupts |
| dwdma_regs_->intenable = 0; |
| //Disable Transmit and Receive |
| dwmac_regs_->conf &= ~(GMAC_CONF_TE | GMAC_CONF_RE); |
| |
| //reset the phy (hold in reset) |
| gpio_write(&gpio_, PHY_RESET, 0); |
| |
| //transmit and receive are not disables, safe to null descriptor list ptrs |
| dwdma_regs_->txdesclistaddr = 0; |
| dwdma_regs_->rxdesclistaddr = 0; |
| |
| return ZX_OK; |
| } |
| |
| uint32_t AmlDWMacDevice::DmaRxStatus() { |
| return (dwdma_regs_->status & DMA_STATUS_RS_MASK) >> DMA_STATUS_RS_POS; |
| } |
| |
| void AmlDWMacDevice::ProcRxBuffer(uint32_t int_status) { |
| while (true) { |
| uint32_t pkt_stat = rx_descriptors_[curr_rx_buf_].txrx_status; |
| |
| if (pkt_stat & DESC_RXSTS_OWNBYDMA) { |
| return; |
| } |
| size_t fr_len = (pkt_stat & DESC_RXSTS_FRMLENMSK) >> DESC_RXSTS_FRMLENSHFT; |
| if (fr_len > kTxnBufSize) { |
| zxlogf(ERROR, "aml-dwmac: unsupported packet size received\n"); |
| return; |
| } |
| |
| uint8_t* temptr = &rx_buffer_[curr_rx_buf_ * kTxnBufSize]; |
| |
| zx_cache_flush(temptr, kTxnBufSize, ZX_CACHE_FLUSH_DATA | ZX_CACHE_FLUSH_INVALIDATE); |
| |
| { // limit scope of autolock |
| fbl::AutoLock lock(&lock_); |
| if ((ethmac_proxy_ != nullptr)) { |
| |
| ethmac_proxy_->Recv(temptr, fr_len, 0); |
| |
| } else { |
| zxlogf(ERROR, "Dropping bad packet\n"); |
| } |
| }; |
| |
| rx_descriptors_[curr_rx_buf_].txrx_status = DESC_RXSTS_OWNBYDMA; |
| rx_packet_++; |
| |
| curr_rx_buf_ = (curr_rx_buf_ + 1) % kNumDesc; |
| if (curr_rx_buf_ == 0) { |
| loop_count_++; |
| } |
| dwdma_regs_->rxpolldemand = ~0; |
| } |
| } |
| |
| zx_status_t AmlDWMacDevice::EthmacQueueTx(uint32_t options, ethmac_netbuf_t* netbuf) { |
| |
| { //Check to make sure we are ready to accept packets |
| fbl::AutoLock lock(&lock_); |
| if (!online_) { |
| return ZX_ERR_UNAVAILABLE; |
| } |
| } |
| |
| if (netbuf->data_size > kTxnBufSize) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| if (tx_descriptors_[curr_tx_buf_].txrx_status & DESC_TXSTS_OWNBYDMA) { |
| zxlogf(ERROR, "TX buffer overrun@ %u\n", curr_tx_buf_); |
| return ZX_ERR_UNAVAILABLE; |
| } |
| uint8_t* temptr = &tx_buffer_[curr_tx_buf_ * kTxnBufSize]; |
| |
| memcpy(temptr, netbuf->data_buffer, netbuf->data_size); |
| hw_mb(); |
| |
| zx_cache_flush(temptr, netbuf->data_size, ZX_CACHE_FLUSH_DATA); |
| |
| //Descriptors are pre-iniitialized with the paddr of their corresponding |
| // buffers, only need to setup the control and status fields. |
| tx_descriptors_[curr_tx_buf_].dmamac_cntl = |
| DESC_TXCTRL_TXINT | |
| DESC_TXCTRL_TXLAST | |
| DESC_TXCTRL_TXFIRST | |
| DESC_TXCTRL_TXCHAIN | |
| ((uint32_t)netbuf->data_size & DESC_TXCTRL_SIZE1MASK); |
| |
| tx_descriptors_[curr_tx_buf_].txrx_status = DESC_TXSTS_OWNBYDMA; |
| curr_tx_buf_ = (curr_tx_buf_ + 1) % kNumDesc; |
| |
| hw_mb(); |
| dwdma_regs_->txpolldemand = ~0; |
| tx_counter_++; |
| return ZX_OK; |
| } |
| |
| zx_status_t AmlDWMacDevice::EthmacSetParam(uint32_t param, int32_t value, const void* data, |
| size_t data_size) { |
| zxlogf(INFO, "SetParam called %x %x\n", param, value); |
| return ZX_OK; |
| } |
| |
| } // namespace eth |
| |
| extern "C" zx_status_t aml_eth_bind(void* ctx, zx_device_t* device, void** cookie) { |
| return eth::AmlDWMacDevice::Create(device); |
| } |