blob: 38ce3ed66d3b7ac08c013cc7002057717d445721 [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 <fuchsia/hardware/usb/peripheral/c/fidl.h>
#include <stdio.h>
#include <string.h>
#include <zircon/device/usb-peripheral.h>
#include <zircon/hw/usb.h>
#include <zircon/hw/usb/cdc.h>
#include <ddk/binding.h>
#include <ddk/debug.h>
#include <ddk/metadata.h>
#include <ddk/platform-defs.h>
#include <ddk/protocol/usb/modeswitch.h>
#include <ddk/usb-peripheral-config.h>
#include <hw/reg.h>
#include <soc/hi3660/hi3660-hw.h>
#include <soc/hi3660/hi3660-regs.h>
#include "hikey960-hw.h"
#include "hikey960.h"
static const char* kManufacturer = "Zircon";
static const char* kProduct = "CDC-Ethernet";
static const char* kSerial = "0123456789ABCDEF";
typedef fuchsia_hardware_usb_peripheral_FunctionDescriptor FunctionDescriptor;
zx_status_t hikey960_usb_phy_init(hikey960_t* hikey) {
volatile void* usb3otg_bc = hikey->usb3otg_bc.vaddr;
volatile void* peri_crg = hikey->peri_crg.vaddr;
volatile void* pctrl = hikey->pctrl.vaddr;
uint32_t temp;
writel(PERI_CRG_ISODIS_REFCLK_ISO_EN, peri_crg + PERI_CRG_ISODIS);
writel(PCTRL_CTRL3_USB_TCXO_EN | (PCTRL_CTRL3_USB_TCXO_EN << PCTRL_CTRL3_MSK_START),
pctrl + PCTRL_CTRL3);
temp = readl(pctrl + PCTRL_CTRL24);
temp &= ~PCTRL_CTRL24_SC_CLK_USB3PHY_3MUX1_SEL;
writel(temp, pctrl + PCTRL_CTRL24);
writel(PERI_CRG_GT_CLK_USB3OTG_REF | PERI_CRG_GT_ACLK_USB3OTG, peri_crg + PERI_CRG_CLK_EN4);
writel(PERI_CRG_IP_RST_USB3OTG_MUX | PERI_CRG_IP_RST_USB3OTG_AHBIF | PERI_CRG_IP_RST_USB3OTG_32K,
peri_crg + PERI_CRG_RSTDIS4);
writel(PERI_CRG_IP_RST_USB3OTGPHY_POR | PERI_CRG_IP_RST_USB3OTG, peri_crg + PERI_CRG_RSTEN4);
// enable PHY REF CLK
temp = readl(usb3otg_bc + USB3OTG_CTRL0);
temp |= USB3OTG_CTRL0_ABB_GT_EN;
writel(temp, usb3otg_bc + USB3OTG_CTRL0);
temp = readl(usb3otg_bc + USB3OTG_CTRL7);
temp |= USB3OTG_CTRL7_REF_SSP_EN;
writel(temp, usb3otg_bc + USB3OTG_CTRL7);
// exit from IDDQ mode
temp = readl(usb3otg_bc + USB3OTG_CTRL2);
temp &= ~(USB3OTG_CTRL2_POWERDOWN_HSP | USB3OTG_CTRL2_POWERDOWN_SSP);
writel(temp, usb3otg_bc + USB3OTG_CTRL2);
zx_nanosleep(zx_deadline_after(ZX_USEC(100)));
writel(PERI_CRG_IP_RST_USB3OTGPHY_POR, peri_crg + PERI_CRG_RSTDIS4);
writel(PERI_CRG_IP_RST_USB3OTG, peri_crg + PERI_CRG_RSTDIS4);
zx_nanosleep(zx_deadline_after(ZX_MSEC(20)));
temp = readl(usb3otg_bc + USB3OTG_CTRL3);
temp |= (USB3OTG_CTRL3_VBUSVLDEXT | USB3OTG_CTRL3_VBUSVLDEXTSEL);
writel(temp, usb3otg_bc + USB3OTG_CTRL3);
zx_nanosleep(zx_deadline_after(ZX_USEC(100)));
return ZX_OK;
}
static const pbus_mmio_t dwc3_mmios[] = {
{
.base = MMIO_USB3OTG_BASE,
.length = MMIO_USB3OTG_LENGTH,
},
};
static const pbus_irq_t dwc3_irqs[] = {
{
.irq = IRQ_USB3,
.mode = ZX_INTERRUPT_MODE_EDGE_HIGH,
},
};
static const pbus_bti_t dwc3_btis[] = {
{
.iommu_index = 0,
.bti_id = BTI_USB_DWC3,
},
};
static pbus_metadata_t dwc3_metadata[] = {
{
.type = DEVICE_METADATA_USB_CONFIG,
// data_buffer and data_size filled in below
},
};
static const pbus_dev_t dwc3_dev = {
.name = "dwc3",
.vid = PDEV_VID_GENERIC,
.pid = PDEV_PID_GENERIC,
.did = PDEV_DID_USB_DWC3,
.mmio_list = dwc3_mmios,
.mmio_count = countof(dwc3_mmios),
.irq_list = dwc3_irqs,
.irq_count = countof(dwc3_irqs),
.bti_list = dwc3_btis,
.bti_count = countof(dwc3_btis),
.metadata_list = dwc3_metadata,
.metadata_count = countof(dwc3_metadata),
};
static usb_mode_t hikey_usb_mode = USB_MODE_HOST;
static pbus_metadata_t hikey_usb_metadata[] = {
{
.type = DEVICE_METADATA_USB_MODE,
.data_buffer = &hikey_usb_mode,
.data_size = sizeof(hikey_usb_mode),
},
};
const pbus_dev_t hikey_usb_dev = {
.name = "hikey-usb",
.vid = PDEV_VID_96BOARDS,
.pid = PDEV_PID_HIKEY960,
.did = PDEV_DID_HIKEY960_USB,
.metadata_list = hikey_usb_metadata,
.metadata_count = countof(hikey_usb_metadata),
};
// Composite binding rules for the USB drivers.
static const zx_bind_inst_t root_match[] = {
BI_MATCH(),
};
static const zx_bind_inst_t gpio1_match[] = {
BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_GPIO),
BI_MATCH_IF(EQ, BIND_GPIO_PIN, GPIO_HUB_VDD33_EN),
};
static const device_fragment_part_t gpio1_fragment[] = {
{countof(root_match), root_match},
{countof(gpio1_match), gpio1_match},
};
static const zx_bind_inst_t gpio2_match[] = {
BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_GPIO),
BI_MATCH_IF(EQ, BIND_GPIO_PIN, GPIO_VBUS_TYPEC),
};
static const device_fragment_part_t gpio2_fragment[] = {
{countof(root_match), root_match},
{countof(gpio2_match), gpio2_match},
};
static const zx_bind_inst_t gpio3_match[] = {
BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_GPIO),
BI_MATCH_IF(EQ, BIND_GPIO_PIN, GPIO_USBSW_SW_SEL),
};
static const device_fragment_part_t gpio3_fragment[] = {
{countof(root_match), root_match},
{countof(gpio3_match), gpio3_match},
};
static const device_fragment_t hikey_usb_fragments[] = {
{countof(gpio1_fragment), gpio1_fragment},
{countof(gpio2_fragment), gpio2_fragment},
{countof(gpio3_fragment), gpio3_fragment},
};
static const zx_bind_inst_t ums_match[] = {
BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_USB_MODE_SWITCH),
BI_ABORT_IF(NE, BIND_PLATFORM_DEV_VID, PDEV_VID_GENERIC),
BI_MATCH_IF(EQ, BIND_PLATFORM_DEV_PID, PDEV_PID_GENERIC),
BI_MATCH_IF(EQ, BIND_PLATFORM_DEV_DID, PDEV_DID_USB_DWC3),
};
static const device_fragment_part_t ums_fragment[] = {
{countof(root_match), root_match},
{countof(ums_match), ums_match},
};
static const device_fragment_t dwc3_fragments[] = {
{countof(ums_fragment), ums_fragment},
};
zx_status_t hikey960_usb_init(hikey960_t* hikey) {
zx_status_t status = hikey960_usb_phy_init(hikey);
if (status != ZX_OK) {
return status;
}
status = pbus_composite_device_add(&hikey->pbus, &hikey_usb_dev, hikey_usb_fragments,
countof(hikey_usb_fragments), UINT32_MAX);
if (status != ZX_OK) {
zxlogf(ERROR, "hikey960_add_devices could not add hikey_usb_dev: %d", status);
return status;
}
// construct USB config metadata
uint8_t buffer[sizeof(struct UsbConfig) + sizeof(FunctionDescriptor)];
struct UsbConfig* config = (struct UsbConfig*)buffer;
config->vid = GOOGLE_USB_VID;
config->pid = GOOGLE_USB_CDC_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;
dwc3_metadata[0].data_size = sizeof(struct UsbConfig) + sizeof(FunctionDescriptor);
dwc3_metadata[0].data_buffer = config;
status = pbus_composite_device_add(&hikey->pbus, &dwc3_dev, dwc3_fragments,
countof(dwc3_fragments), 1);
if (status != ZX_OK) {
zxlogf(ERROR, "%s: pbus_composite_device_add failed: %d", __FUNCTION__, status);
return status;
}
return ZX_OK;
}