| // 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 "imx8mevk.h" |
| #include <ddk/debug.h> |
| #include <ddk/device.h> |
| #include <ddk/metadata.h> |
| #include <ddk/mmio-buffer.h> |
| #include <ddk/platform-defs.h> |
| #include <ddk/protocol/platform/bus.h> |
| #include <ddk/protocol/usb/modeswitch.h> |
| #include <hw/reg.h> |
| #include <limits.h> |
| #include <soc/imx8m/imx8m-gpio.h> |
| #include <soc/imx8m/imx8m-hw.h> |
| #include <soc/imx8m/imx8m-iomux.h> |
| #include <soc/imx8m/imx8m-sip.h> |
| #include <zircon/syscalls/smc.h> |
| |
| static const pbus_mmio_t usb1_mmios[] = { |
| { |
| .base = IMX8M_USB1_BASE, |
| .length = IMX8M_USB1_LENGTH, |
| }, |
| }; |
| static const pbus_irq_t usb1_irqs[] = { |
| { |
| .irq = IMX8M_A53_INTR_USB1, |
| .mode = ZX_INTERRUPT_MODE_EDGE_HIGH, |
| }, |
| }; |
| |
| static const pbus_bti_t usb1_btis[] = { |
| { |
| .iommu_index = 0, |
| .bti_id = BTI_USB1, |
| }, |
| }; |
| |
| static usb_mode_t usb1_mode = USB_MODE_HOST; |
| |
| static const pbus_metadata_t usb1_metadata[] = { |
| { |
| .type = DEVICE_METADATA_USB_MODE, |
| .data_buffer = &usb1_mode, |
| .data_size = sizeof(usb1_mode), |
| }}; |
| |
| // USB1 is USB-C OTG port |
| static const pbus_dev_t usb1_dev = { |
| .name = "dwc3-1", |
| .vid = PDEV_VID_GENERIC, |
| .pid = PDEV_PID_GENERIC, |
| .did = PDEV_DID_USB_DWC3, |
| .mmio_list = usb1_mmios, |
| .mmio_count = countof(usb1_mmios), |
| .irq_list = usb1_irqs, |
| .irq_count = countof(usb1_irqs), |
| .bti_list = usb1_btis, |
| .bti_count = countof(usb1_btis), |
| .metadata_list = usb1_metadata, |
| .metadata_count = countof(usb1_metadata), |
| }; |
| |
| static const pbus_mmio_t usb2_mmios[] = { |
| { |
| .base = IMX8M_USB2_BASE, |
| .length = IMX8M_USB2_LENGTH, |
| }, |
| }; |
| static const pbus_irq_t usb2_irqs[] = { |
| { |
| .irq = IMX8M_A53_INTR_USB2, |
| .mode = ZX_INTERRUPT_MODE_EDGE_HIGH, |
| }, |
| }; |
| |
| static const pbus_bti_t usb2_btis[] = { |
| { |
| .iommu_index = 0, |
| .bti_id = BTI_USB2, |
| }, |
| }; |
| |
| static usb_mode_t usb2_mode = USB_MODE_HOST; |
| |
| static const pbus_metadata_t usb2_metadata[] = { |
| { |
| .type = DEVICE_METADATA_USB_MODE, |
| .data_buffer = &usb2_mode, |
| .data_size = sizeof(usb2_mode), |
| }}; |
| |
| // USB1 is USB-A port, host only |
| static const pbus_dev_t usb2_dev = { |
| .name = "dwc3-2", |
| .vid = PDEV_VID_GENERIC, |
| .pid = PDEV_PID_GENERIC, |
| .did = PDEV_DID_USB_DWC3, |
| .mmio_list = usb2_mmios, |
| .mmio_count = countof(usb2_mmios), |
| .irq_list = usb2_irqs, |
| .irq_count = countof(usb2_irqs), |
| .bti_list = usb2_btis, |
| .bti_count = countof(usb2_btis), |
| .metadata_list = usb2_metadata, |
| .metadata_count = countof(usb2_metadata), |
| }; |
| |
| zx_status_t imx_usb_phy_init(zx_paddr_t usb_base, size_t usb_length) { |
| uint32_t reg; |
| mmio_buffer_t usb_buf; |
| zx_status_t status = mmio_buffer_init_physical(&usb_buf, usb_base, usb_length, |
| get_root_resource(), |
| ZX_CACHE_POLICY_UNCACHED_DEVICE); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "%s mmio_buffer_init_physical failed %d\n", __FUNCTION__, status); |
| return status; |
| } |
| |
| volatile void* regs = usb_buf.vaddr; |
| //TODO: More stuff might be needed if we were to boot from our own bootloader. |
| reg = readl(regs + USB_PHY_CTRL1); |
| reg &= ~(PHY_CTRL1_VDATSRCENB0 | PHY_CTRL1_VDATDETENB0); |
| reg |= PHY_CTRL1_RESET | PHY_CTRL1_ATERESET; |
| writel(reg, regs + USB_PHY_CTRL1); |
| |
| reg = readl(regs + USB_PHY_CTRL0); |
| reg |= PHY_CTRL0_REF_SSP_EN; |
| writel(reg, regs + USB_PHY_CTRL0); |
| |
| reg = readl(regs + USB_PHY_CTRL2); |
| reg |= PHY_CTRL2_TXENABLEN0; |
| writel(reg, regs + USB_PHY_CTRL2); |
| |
| reg = readl(regs + USB_PHY_CTRL1); |
| reg &= ~(PHY_CTRL1_RESET | PHY_CTRL1_ATERESET); |
| writel(reg, regs + USB_PHY_CTRL1); |
| |
| mmio_buffer_release(&usb_buf); |
| return ZX_OK; |
| } |
| |
| zx_status_t imx_usb_init(imx8mevk_bus_t* bus) { |
| zx_status_t status; |
| |
| // turn on usb via smc calls |
| zx_smc_parameters_t otg1_en_params = {.func_id = IMX8M_SIP_GPC, |
| .arg1 = IMX8M_SIP_CONFIG_GPC_PM_DOMAIN, |
| .arg2 = IMX8M_PD_USB_OTG1, |
| .arg3 = 1}; |
| zx_smc_result_t smc_result; |
| status = zx_smc_call(get_root_resource(), &otg1_en_params, &smc_result); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "%s: SMC call to turn USB on failed %d\n", __FUNCTION__, status); |
| return status; |
| } |
| |
| zx_smc_parameters_t otg2_en_params = {.func_id = IMX8M_SIP_GPC, |
| .arg1 = IMX8M_SIP_CONFIG_GPC_PM_DOMAIN, |
| .arg2 = IMX8M_PD_USB_OTG2, |
| .arg3 = 1}; |
| status = zx_smc_call(get_root_resource(), &otg2_en_params, &smc_result); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "%s: SMC call to turn USB on failed %d\n", __FUNCTION__, status); |
| return status; |
| } |
| |
| status = imx_usb_phy_init(IMX8M_USB1_BASE, IMX8M_USB1_LENGTH); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "%s: imx_usb_phy_init failed %d\n", __FUNCTION__, status); |
| return status; |
| } |
| status = imx_usb_phy_init(IMX8M_USB2_BASE, IMX8M_USB2_LENGTH); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "%s: imx_usb_phy_init failed %d\n", __FUNCTION__, status); |
| return status; |
| } |
| |
| if ((status = pbus_device_add(&bus->pbus, &usb1_dev)) != ZX_OK) { |
| zxlogf(ERROR, "imx_usb_init could not add usb1_dev: %d\n", status); |
| return status; |
| } |
| if ((status = pbus_device_add(&bus->pbus, &usb2_dev)) != ZX_OK) { |
| zxlogf(ERROR, "imx_usb_init could not add usb2_dev: %d\n", status); |
| return status; |
| } |
| return ZX_OK; |
| } |