| // 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/binding.h> |
| #include <ddk/debug.h> |
| #include <ddk/metadata.h> |
| #include <ddk/mmio-buffer.h> |
| #include <ddk/metadata.h> |
| #include <ddk/platform-defs.h> |
| #include <ddk/usb-peripheral-config.h> |
| #include <soc/aml-s905d2/s905d2-hw.h> |
| #include <usb/dwc2/metadata.h> |
| #include <zircon/device/usb-peripheral.h> |
| #include <zircon/hw/usb.h> |
| #include <zircon/hw/usb/cdc.h> |
| |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #include "astro.h" |
| |
| static const pbus_mmio_t dwc2_mmios[] = { |
| { |
| .base = S905D2_USB1_BASE, |
| .length = S905D2_USB1_LENGTH, |
| }, |
| }; |
| |
| static const pbus_irq_t dwc2_irqs[] = { |
| { |
| .irq = S905D2_USB1_IRQ, |
| .mode = ZX_INTERRUPT_MODE_EDGE_HIGH, |
| }, |
| }; |
| |
| static const pbus_bti_t dwc2_btis[] = { |
| { |
| .iommu_index = 0, |
| .bti_id = BTI_USB, |
| }, |
| }; |
| |
| static const char kManufacturer[] = "Zircon"; |
| static const char kProduct[] = "CDC-Ethernet"; |
| static const char kSerial[] = "0123456789ABCDEF"; |
| |
| // Metadata for DWC2 driver. |
| static const dwc2_metadata_t dwc2_metadata = { |
| .dma_burst_len = DWC2_DMA_BURST_INCR8, |
| .usb_turnaround_time = 9, |
| .rx_fifo_size = 256, // for all OUT endpoints. |
| .nptx_fifo_size = 32, // for endpoint zero IN direction. |
| .tx_fifo_sizes = { |
| 128, // for CDC ethernet bulk IN. |
| 4, // for CDC ethernet interrupt IN. |
| 128, // for test function bulk IN. |
| 16, // for test function interrupt IN. |
| }, |
| }; |
| |
| static pbus_metadata_t usb_metadata[] = { |
| { |
| .type = DEVICE_METADATA_USB_CONFIG, |
| .data_buffer = NULL, |
| .data_size = 0, |
| }, |
| { |
| .type = DEVICE_METADATA_PRIVATE, |
| .data_buffer = &dwc2_metadata, |
| .data_size = sizeof(dwc2_metadata), |
| }, |
| }; |
| |
| static const pbus_boot_metadata_t usb_boot_metadata[] = { |
| { |
| // Use Bluetooth MAC address for USB ethernet as well. |
| .zbi_type = DEVICE_METADATA_MAC_ADDRESS, |
| .zbi_extra = MACADDR_BLUETOOTH, |
| }, |
| }; |
| |
| static const pbus_dev_t dwc2_dev = { |
| .name = "dwc2", |
| .vid = PDEV_VID_GENERIC, |
| .pid = PDEV_PID_GENERIC, |
| .did = PDEV_DID_USB_DWC2, |
| .mmio_list = dwc2_mmios, |
| .mmio_count = countof(dwc2_mmios), |
| .irq_list = dwc2_irqs, |
| .irq_count = countof(dwc2_irqs), |
| .bti_list = dwc2_btis, |
| .bti_count = countof(dwc2_btis), |
| .metadata_list = usb_metadata, |
| .metadata_count = countof(usb_metadata), |
| .boot_metadata_list = usb_boot_metadata, |
| .boot_metadata_count = countof(usb_boot_metadata), |
| }; |
| |
| static const pbus_mmio_t xhci_mmios[] = { |
| { |
| .base = S905D2_USB0_BASE, |
| .length = S905D2_USB0_LENGTH, |
| }, |
| }; |
| |
| static const pbus_irq_t xhci_irqs[] = { |
| { |
| .irq = S905D2_USB0_IRQ, |
| .mode = ZX_INTERRUPT_MODE_EDGE_HIGH, |
| }, |
| }; |
| |
| static const pbus_bti_t usb_btis[] = { |
| { |
| .iommu_index = 0, |
| .bti_id = BTI_USB, |
| }, |
| }; |
| |
| static const pbus_dev_t xhci_dev = { |
| .name = "xhci", |
| .vid = PDEV_VID_GENERIC, |
| .pid = PDEV_PID_GENERIC, |
| .did = PDEV_DID_USB_XHCI_COMPOSITE, |
| .mmio_list = xhci_mmios, |
| .mmio_count = countof(xhci_mmios), |
| .irq_list = xhci_irqs, |
| .irq_count = countof(xhci_irqs), |
| .bti_list = usb_btis, |
| .bti_count = countof(usb_btis), |
| }; |
| |
| static const pbus_mmio_t usb_phy_mmios[] = { |
| { |
| .base = S905D2_RESET_BASE, |
| .length = S905D2_RESET_LENGTH, |
| }, |
| { |
| .base = S905D2_USBCTRL_BASE, |
| .length = S905D2_USBCTRL_LENGTH, |
| }, |
| { |
| .base = S905D2_USBPHY20_BASE, |
| .length = S905D2_USBPHY20_LENGTH, |
| }, |
| { |
| .base = S905D2_USBPHY21_BASE, |
| .length = S905D2_USBPHY21_LENGTH, |
| }, |
| }; |
| |
| static const pbus_irq_t usb_phy_irqs[] = { |
| { |
| .irq = S905D2_USB_IDDIG_IRQ, |
| .mode = ZX_INTERRUPT_MODE_EDGE_HIGH, |
| }, |
| }; |
| |
| // values from mesong12b.dtsi usb2_phy_v2 pll-setting-# |
| static const uint32_t pll_settings[] = { |
| 0x09400414, |
| 0x927E0000, |
| 0xac5f49e5, |
| 0xfe18, |
| 0xfff, |
| 0x78000, |
| 0xe0004, |
| 0xe000c, |
| }; |
| |
| static const pbus_metadata_t usb_phy_metadata[] = { |
| { |
| .type = DEVICE_METADATA_PRIVATE, |
| .data_buffer = pll_settings, |
| .data_size = sizeof(pll_settings), |
| }, |
| }; |
| |
| static const pbus_dev_t usb_phy_dev = { |
| .name = "aml-usb-phy-v2", |
| .vid = PDEV_VID_AMLOGIC, |
| .did = PDEV_DID_AML_USB_PHY_V2, |
| .mmio_list = usb_phy_mmios, |
| .mmio_count = countof(usb_phy_mmios), |
| .irq_list = usb_phy_irqs, |
| .irq_count = countof(usb_phy_irqs), |
| .bti_list = usb_btis, |
| .bti_count = countof(usb_btis), |
| .metadata_list = usb_phy_metadata, |
| .metadata_count = countof(usb_phy_metadata), |
| }; |
| |
| static const zx_bind_inst_t root_match[] = { |
| BI_MATCH(), |
| }; |
| static const zx_bind_inst_t xhci_phy_match[] = { |
| BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_USB_PHY), |
| BI_ABORT_IF(NE, BIND_PLATFORM_DEV_VID, PDEV_VID_GENERIC), |
| BI_ABORT_IF(NE, BIND_PLATFORM_DEV_PID, PDEV_PID_GENERIC), |
| BI_MATCH_IF(EQ, BIND_PLATFORM_DEV_DID, PDEV_DID_USB_XHCI_COMPOSITE), |
| }; |
| static const device_component_part_t xhci_phy_component[] = { |
| { countof(root_match), root_match }, |
| { countof(xhci_phy_match), xhci_phy_match }, |
| }; |
| static const device_component_t xhci_components[] = { |
| { countof(xhci_phy_component), xhci_phy_component }, |
| }; |
| static const zx_bind_inst_t dwc2_phy_match[] = { |
| BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_USB_PHY), |
| BI_ABORT_IF(NE, BIND_PLATFORM_DEV_VID, PDEV_VID_GENERIC), |
| BI_ABORT_IF(NE, BIND_PLATFORM_DEV_PID, PDEV_PID_GENERIC), |
| BI_MATCH_IF(EQ, BIND_PLATFORM_DEV_DID, PDEV_DID_USB_DWC2), |
| }; |
| static const device_component_part_t dwc2_phy_component[] = { |
| { countof(root_match), root_match }, |
| { countof(dwc2_phy_match), dwc2_phy_match }, |
| }; |
| static const device_component_t dwc2_components[] = { |
| { countof(dwc2_phy_component), dwc2_phy_component }, |
| }; |
| |
| zx_status_t aml_usb_init(aml_bus_t* bus) { |
| zx_status_t status = pbus_device_add(&bus->pbus, &usb_phy_dev); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "%s: pbus_device_add failed %d\n", __func__, status); |
| return status; |
| } |
| |
| // Add XHCI and DWC2 to the same devhost as the aml-usb-phy. |
| status = pbus_composite_device_add(&bus->pbus, &xhci_dev, xhci_components, |
| countof(xhci_components), 1); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "%s: pbus_composite_device_add failed %d\n", __func__, status); |
| return status; |
| } |
| |
| const size_t config_size = sizeof(struct UsbConfig) + |
| 2 * sizeof(fuchsia_hardware_usb_peripheral_FunctionDescriptor); |
| struct UsbConfig* config = (struct UsbConfig*)calloc(1, config_size); |
| 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_subclass = USB_CDC_SUBCLASS_ETHERNET; |
| config->functions[0].interface_protocol = 0; |
| config->functions[1].interface_class = USB_CLASS_VENDOR; |
| config->functions[1].interface_subclass = 0; |
| config->functions[1].interface_protocol = 0; |
| usb_metadata[0].data_size = config_size; |
| usb_metadata[0].data_buffer = config; |
| |
| status = pbus_composite_device_add(&bus->pbus, &dwc2_dev, dwc2_components, |
| countof(dwc2_components), 1); |
| free(config); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "%s: pbus_composite_device_add failed %d\n", __func__, status); |
| return status; |
| } |
| |
| return ZX_OK; |
| } |