blob: 9b8e4f079fabe01d68c2aad7362c563dd643aee3 [file] [log] [blame]
// 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 "as370-usb-phy.h"
#include <assert.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ddk/binding.h>
#include <ddk/debug.h>
#include <ddk/device.h>
#include <ddk/driver.h>
#include <ddk/platform-defs.h>
#include <soc/as370/as370-reset.h>
#include <soc/as370/as370-usb.h>
#include <fbl/algorithm.h>
#include <fbl/unique_ptr.h>
#include <hw/reg.h>
namespace as370_usb_phy {
void UsbPhy::ResetPhy() {
auto* mmio = &*reset_mmio_;
auto reset = as370::GblPerifStickyResetN::Get().ReadFrom(mmio);
reset.set_usbOtgPhyreset(0).WriteTo(mmio);
reset.set_usbOtgPrstn(1).WriteTo(mmio);
usleep(10);
reset.set_usbOtgHresetn(1).WriteTo(mmio);
usleep(100);
}
zx_status_t UsbPhy::InitPhy() {
auto* mmio = &*usbphy_mmio_;
as370::USB_PHY_CTRL0::Get().FromValue(0).set_value(0x0EB35E84).WriteTo(mmio);
as370::USB_PHY_CTRL1::Get().FromValue(0).set_value(0x80E9F004).WriteTo(mmio);
ResetPhy();
uint32_t count = 10000;
while (count) {
if (as370::USB_PHY_RB::Get().ReadFrom(mmio).clk_rdy()) {
break;
}
usleep(1);
count--;
}
return count ? 0 : ZX_ERR_TIMED_OUT;
return ZX_OK;
}
zx_status_t UsbPhy::Create(void* ctx, zx_device_t* parent) {
auto dev = std::make_unique<UsbPhy>(parent);
auto status = dev->Init();
if (status != ZX_OK) {
return status;
}
// devmgr is now in charge of the device.
__UNUSED auto* dummy = dev.release();
return ZX_OK;
}
zx_status_t UsbPhy::AddDwc2Device() {
if (dwc2_device_) {
zxlogf(ERROR, "UsbPhy::AddDwc2Device: device already exists!\n");
return ZX_ERR_BAD_STATE;
}
fbl::AllocChecker ac;
dwc2_device_ = fbl::make_unique_checked<Dwc2Device>(&ac, zxdev());
if (!ac.check()) {
return ZX_ERR_NO_MEMORY;
}
zx_device_prop_t props[] = {
{BIND_PLATFORM_DEV_VID, 0, PDEV_VID_GENERIC},
{BIND_PLATFORM_DEV_PID, 0, PDEV_PID_GENERIC},
{BIND_PLATFORM_DEV_DID, 0, PDEV_DID_USB_DWC2},
};
return dwc2_device_->DdkAdd("dwc2", 0, props, countof(props), ZX_PROTOCOL_USB_PHY);
}
zx_status_t UsbPhy::RemoveDwc2Device() {
if (dwc2_device_ == nullptr) {
zxlogf(ERROR, "UsbPhy::RemoveDwc2Device: device does not exist!\n");
return ZX_ERR_BAD_STATE;
}
// devmgr will own the device until it is destroyed.
__UNUSED auto* dev = dwc2_device_.release();
dev->DdkRemove();
return ZX_OK;
}
zx_status_t UsbPhy::Init() {
if (!pdev_.is_valid()) {
zxlogf(ERROR, "UsbPhy::Init: could not get platform device protocol\n");
return ZX_ERR_NOT_SUPPORTED;
}
auto status = pdev_.MapMmio(0, &usbphy_mmio_);
if (status != ZX_OK) {
zxlogf(ERROR, "UsbPhy::Init: MapMmio failed for usbphy_mmio_\n");
return status;
}
status = pdev_.MapMmio(1, &reset_mmio_);
if (status != ZX_OK) {
zxlogf(ERROR, "UsbPhy::Init: MapMmio failed for reset_mmio_\n");
return status;
}
status = InitPhy();
if (status != ZX_OK) {
zxlogf(ERROR, "UsbPhy::Init: InitPhy() failed\n");
return status;
}
status = DdkAdd("as370-usb-phy", DEVICE_ADD_NON_BINDABLE);
if (status != ZX_OK) {
zxlogf(ERROR, "UsbPhy::Init: DdkAdd() failed\n");
return status;
}
AddDwc2Device();
return ZX_OK;
}
void UsbPhy::DdkUnbind() {
RemoveDwc2Device();
DdkRemove();
}
void UsbPhy::DdkRelease() { delete this; }
static constexpr zx_driver_ops_t driver_ops = []() {
zx_driver_ops_t ops = {};
ops.version = DRIVER_OPS_VERSION;
ops.bind = UsbPhy::Create;
return ops;
}();
} // namespace as370_usb_phy
ZIRCON_DRIVER_BEGIN(as370_usb_phy, as370_usb_phy::driver_ops, "zircon", "0.1", 4)
BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_PDEV),
BI_ABORT_IF(NE, BIND_PLATFORM_DEV_VID, PDEV_VID_SYNAPTICS),
BI_ABORT_IF(NE, BIND_PLATFORM_DEV_PID, PDEV_PID_SYNAPTICS_AS370),
BI_MATCH_IF(EQ, BIND_PLATFORM_DEV_DID, PDEV_DID_AS370_USB_PHY), ZIRCON_DRIVER_END(as370_usb_phy)