blob: a73124280c47e9a84bd7711996f5faf7e738c2e8 [file] [log] [blame]
// 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 <lib/zircon-internal/align.h>
#include <string.h>
#include <zircon/device/usb-peripheral.h>
#include <zircon/hw/usb.h>
#include <zircon/hw/usb/cdc.h>
#include <ddk/debug.h>
#include <ddk/device.h>
#include <ddk/driver.h>
#include <ddk/metadata.h>
#include <ddk/platform-defs.h>
#include <ddktl/protocol/clockimpl.h>
#include <ddktl/protocol/power.h>
#include <ddktl/protocol/powerimpl.h>
#include <fbl/alloc_checker.h>
#include <hw/reg.h>
#include <soc/mt8167/mt8167-clk.h>
#include <soc/mt8167/mt8167-hw.h>
#include "mt8167.h"
namespace board_mt8167 {
namespace {
// USB peripheral device controller
constexpr pbus_mmio_t usb_dci_mmios[] = {
{
.base = MT8167_USB0_BASE,
.length = MT8167_USB0_LENGTH,
},
{
.base = MT8167_USBPHY_BASE,
.length = MT8167_USBPHY_LENGTH,
},
};
constexpr pbus_irq_t usb_dci_irqs[] = {
{
.irq = MT8167_IRQ_USB_MCU,
.mode = ZX_INTERRUPT_MODE_LEVEL_HIGH,
},
};
constexpr pbus_bti_t usb_btis[] = {
{
.iommu_index = 0,
.bti_id = BTI_USB,
},
};
constexpr char kManufacturer[] = "Zircon";
constexpr char kProduct[] = "CDC-Ethernet";
constexpr char kSerial[] = "0123456789ABCDEF";
using FunctionDescriptor = fuchsia_hardware_usb_peripheral_FunctionDescriptor;
// Statically assigned dummy MAC address.
// TODO(fxb/34469): Provide real MAC address via bootloader or some other mechanism.
static const uint8_t eth_mac_address[] = {
0x02, 0x98, 0x8f, 0x3c, 0xd2, 0xaa,
};
static pbus_metadata_t usb_metadata[] = {
{
.type = DEVICE_METADATA_USB_CONFIG,
.data_buffer = nullptr, // Filled in below.
.data_size = 0, // Filled in below.
},
{
.type = DEVICE_METADATA_MAC_ADDRESS,
.data_buffer = eth_mac_address,
.data_size = sizeof(eth_mac_address),
},
};
const pbus_dev_t usb_dci_dev = []() {
pbus_dev_t dev = {};
dev.name = "mt-usb-dci";
dev.vid = PDEV_VID_MEDIATEK;
dev.did = PDEV_DID_MUSB_PERIPHERAL;
dev.mmio_list = usb_dci_mmios;
dev.mmio_count = countof(usb_dci_mmios);
dev.irq_list = usb_dci_irqs;
dev.irq_count = countof(usb_dci_irqs);
dev.bti_list = usb_btis;
dev.bti_count = countof(usb_btis);
dev.metadata_list = usb_metadata;
dev.metadata_count = countof(usb_metadata);
return dev;
}();
// USB host controller
constexpr pbus_mmio_t usb_hci_mmios[] = {
{
.base = MT8167_USB1_BASE,
.length = MT8167_USB0_LENGTH,
},
{
.base = MT8167_USBPHY_BASE,
.length = MT8167_USBPHY_LENGTH,
},
};
constexpr pbus_irq_t usb_hci_irqs[] = {
{
.irq = MT8167_IRQ_USB_MCU_P1,
.mode = ZX_INTERRUPT_MODE_LEVEL_HIGH,
},
};
const pbus_dev_t usb_hci_dev = []() {
pbus_dev_t dev = {};
dev.name = "mt-usb-hci";
dev.vid = PDEV_VID_MEDIATEK;
dev.did = PDEV_DID_MUSB_HOST;
dev.mmio_list = usb_hci_mmios;
dev.mmio_count = countof(usb_hci_mmios);
dev.irq_list = usb_hci_irqs;
dev.irq_count = countof(usb_hci_irqs);
dev.bti_list = usb_btis;
dev.bti_count = countof(usb_btis);
return dev;
}();
} // namespace
zx_status_t Mt8167::UsbInit() {
zx_status_t status;
constexpr size_t alignment = alignof(UsbConfig) > __STDCPP_DEFAULT_NEW_ALIGNMENT__
? alignof(UsbConfig)
: __STDCPP_DEFAULT_NEW_ALIGNMENT__;
constexpr size_t config_size = sizeof(UsbConfig) + 2 * sizeof(FunctionDescriptor);
UsbConfig* config =
reinterpret_cast<UsbConfig*>(aligned_alloc(alignment, ZX_ROUNDUP(config_size, alignment)));
if (!config) {
return ZX_ERR_NO_MEMORY;
}
config->vid = GOOGLE_USB_VID;
config->pid = GOOGLE_USB_CDC_AND_FUNCTION_TEST_PID;
strcpy(config->manufacturer, kManufacturer);
strcpy(config->serial, kSerial);
strcpy(config->product, kProduct);
config->functions[0].interface_class = USB_CLASS_COMM;
config->functions[0].interface_protocol = 0;
config->functions[0].interface_subclass = USB_CDC_SUBCLASS_ETHERNET;
config->functions[1].interface_class = USB_CLASS_VENDOR;
config->functions[1].interface_protocol = 0;
config->functions[1].interface_subclass = 0;
usb_metadata[0].data_size = config_size;
usb_metadata[0].data_buffer = config;
usb_config_ = config;
// Make sure the USB3v3 LDO voltage regulator is turned on.
power_domain_status_t pwr_status;
ddk::PowerImplProtocolClient power(parent());
if (!power.is_valid()) {
zxlogf(ERROR, "%s: could not get power protocol", __func__);
return ZX_ERR_INTERNAL;
}
status = power.GetPowerDomainStatus(kVDLdoVUsb33, &pwr_status);
if (status != ZX_OK) {
zxlogf(ERROR, "%s: could not read usb power domain: %d", __func__, status);
return status;
}
if (pwr_status == POWER_DOMAIN_STATUS_DISABLED) {
zxlogf(INFO, "%s: enabling usb power domain...", __func__);
status = power.EnablePowerDomain(kVDLdoVUsb33);
if (status != ZX_OK) {
zxlogf(ERROR, "%s: could not enable usb power domain: %d", __func__, status);
return status;
}
status = power.GetPowerDomainStatus(kVDLdoVUsb33, &pwr_status);
if (status != ZX_OK) {
zxlogf(ERROR, "%s: could not read usb power domain: %d", __func__, status);
return status;
}
if (pwr_status != POWER_DOMAIN_STATUS_ENABLED) {
zxlogf(ERROR, "%s: usb power domain could not be enabled", __func__);
return ZX_ERR_INTERNAL;
}
}
ddk::ClockImplProtocolClient clk(parent());
if (!clk.is_valid()) {
zxlogf(ERROR, "%s: could not get clock protocol", __func__);
return ZX_ERR_INTERNAL;
}
status = clk.Enable(kClkUsb);
if (status != ZX_OK) {
zxlogf(ERROR, "%s: could not enable USB-P0 clock: %d", __func__, status);
return status;
}
status = clk.Enable(kClkUsb1p);
if (status != ZX_OK) {
zxlogf(ERROR, "%s: could not enable USB-P1 clock: %d", __func__, status);
return status;
}
status = pbus_.DeviceAdd(&usb_dci_dev);
if (status != ZX_OK) {
zxlogf(ERROR, "%s: (mt-usb-dci) DeviceAdd failed %d", __func__, status);
return status;
}
status = pbus_.DeviceAdd(&usb_hci_dev);
if (status != ZX_OK) {
zxlogf(ERROR, "%s: (mt-usb-hci) DeviceAdd failed %d", __func__, status);
return status;
}
return ZX_OK;
}
} // namespace board_mt8167