1// Copyright 2018 The Fuchsia Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "imx8mevk.h" 6#include <ddk/debug.h> 7#include <ddk/device.h> 8#include <ddk/metadata.h> 9#include <ddk/protocol/platform-bus.h> 10#include <ddk/protocol/platform-defs.h> 11#include <ddk/protocol/usb-mode-switch.h> 12#include <hw/reg.h> 13#include <limits.h> 14#include <soc/imx8m/imx8m-gpio.h> 15#include <soc/imx8m/imx8m-hw.h> 16#include <soc/imx8m/imx8m-iomux.h> 17#include <soc/imx8m/imx8m-sip.h> 18#include <zircon/syscalls/smc.h> 19 20static const pbus_mmio_t usb1_mmios[] = { 21 { 22 .base = IMX8M_USB1_BASE, 23 .length = IMX8M_USB1_LENGTH, 24 }, 25}; 26static const pbus_irq_t usb1_irqs[] = { 27 { 28 .irq = IMX8M_A53_INTR_USB1, 29 .mode = ZX_INTERRUPT_MODE_EDGE_HIGH, 30 }, 31}; 32 33static const pbus_bti_t usb1_btis[] = { 34 { 35 .iommu_index = 0, 36 .bti_id = BTI_USB1, 37 }, 38}; 39 40static usb_mode_t usb1_mode = USB_MODE_HOST; 41 42static const pbus_metadata_t usb1_metadata[] = { 43 { 44 .type = DEVICE_METADATA_USB_MODE, 45 .data = &usb1_mode, 46 .len = sizeof(usb1_mode), 47 }}; 48 49// USB1 is USB-C OTG port 50static const pbus_dev_t usb1_dev = { 51 .name = "dwc3-1", 52 .vid = PDEV_VID_GENERIC, 53 .pid = PDEV_PID_GENERIC, 54 .did = PDEV_DID_USB_DWC3, 55 .mmios = usb1_mmios, 56 .mmio_count = countof(usb1_mmios), 57 .irqs = usb1_irqs, 58 .irq_count = countof(usb1_irqs), 59 .btis = usb1_btis, 60 .bti_count = countof(usb1_btis), 61 .metadata = usb1_metadata, 62 .metadata_count = countof(usb1_metadata), 63}; 64 65static const pbus_mmio_t usb2_mmios[] = { 66 { 67 .base = IMX8M_USB2_BASE, 68 .length = IMX8M_USB2_LENGTH, 69 }, 70}; 71static const pbus_irq_t usb2_irqs[] = { 72 { 73 .irq = IMX8M_A53_INTR_USB2, 74 .mode = ZX_INTERRUPT_MODE_EDGE_HIGH, 75 }, 76}; 77 78static const pbus_bti_t usb2_btis[] = { 79 { 80 .iommu_index = 0, 81 .bti_id = BTI_USB2, 82 }, 83}; 84 85static usb_mode_t usb2_mode = USB_MODE_HOST; 86 87static const pbus_metadata_t usb2_metadata[] = { 88 { 89 .type = DEVICE_METADATA_USB_MODE, 90 .data = &usb2_mode, 91 .len = sizeof(usb2_mode), 92 }}; 93 94// USB1 is USB-A port, host only 95static const pbus_dev_t usb2_dev = { 96 .name = "dwc3-2", 97 .vid = PDEV_VID_GENERIC, 98 .pid = PDEV_PID_GENERIC, 99 .did = PDEV_DID_USB_DWC3, 100 .mmios = usb2_mmios, 101 .mmio_count = countof(usb2_mmios), 102 .irqs = usb2_irqs, 103 .irq_count = countof(usb2_irqs), 104 .btis = usb2_btis, 105 .bti_count = countof(usb2_btis), 106 .metadata = usb2_metadata, 107 .metadata_count = countof(usb2_metadata), 108}; 109 110zx_status_t imx_usb_phy_init(zx_paddr_t usb_base, size_t usb_length, zx_handle_t bti) { 111 uint32_t reg; 112 io_buffer_t usb_buf; 113 zx_status_t status = io_buffer_init_physical(&usb_buf, bti, usb_base, usb_length, 114 get_root_resource(), ZX_CACHE_POLICY_UNCACHED_DEVICE); 115 if (status != ZX_OK) { 116 zxlogf(ERROR, "%s io_buffer_init_physical failed %d\n", __FUNCTION__, status); 117 return status; 118 } 119 120 volatile void* regs = io_buffer_virt(&usb_buf); 121 //TODO: More stuff might be needed if we were to boot from our own bootloader. 122 reg = readl(regs + USB_PHY_CTRL1); 123 reg &= ~(PHY_CTRL1_VDATSRCENB0 | PHY_CTRL1_VDATDETENB0); 124 reg |= PHY_CTRL1_RESET | PHY_CTRL1_ATERESET; 125 writel(reg, regs + USB_PHY_CTRL1); 126 127 reg = readl(regs + USB_PHY_CTRL0); 128 reg |= PHY_CTRL0_REF_SSP_EN; 129 writel(reg, regs + USB_PHY_CTRL0); 130 131 reg = readl(regs + USB_PHY_CTRL2); 132 reg |= PHY_CTRL2_TXENABLEN0; 133 writel(reg, regs + USB_PHY_CTRL2); 134 135 reg = readl(regs + USB_PHY_CTRL1); 136 reg &= ~(PHY_CTRL1_RESET | PHY_CTRL1_ATERESET); 137 writel(reg, regs + USB_PHY_CTRL1); 138 139 io_buffer_release(&usb_buf); 140 return ZX_OK; 141} 142 143zx_status_t imx_usb_init(imx8mevk_bus_t* bus) { 144 zx_status_t status; 145 zx_handle_t bti; 146 147 // turn on usb via smc calls 148 zx_smc_parameters_t otg1_en_params = {.func_id = IMX8M_SIP_GPC, 149 .arg1 = IMX8M_SIP_CONFIG_GPC_PM_DOMAIN, 150 .arg2 = IMX8M_PD_USB_OTG1, 151 .arg3 = 1}; 152 zx_smc_result_t smc_result; 153 status = zx_smc_call(get_root_resource(), &otg1_en_params, &smc_result); 154 if (status != ZX_OK) { 155 zxlogf(ERROR, "%s: SMC call to turn USB on failed %d\n", __FUNCTION__, status); 156 return status; 157 } 158 159 zx_smc_parameters_t otg2_en_params = {.func_id = IMX8M_SIP_GPC, 160 .arg1 = IMX8M_SIP_CONFIG_GPC_PM_DOMAIN, 161 .arg2 = IMX8M_PD_USB_OTG2, 162 .arg3 = 1}; 163 status = zx_smc_call(get_root_resource(), &otg2_en_params, &smc_result); 164 if (status != ZX_OK) { 165 zxlogf(ERROR, "%s: SMC call to turn USB on failed %d\n", __FUNCTION__, status); 166 return status; 167 } 168 169 status = iommu_get_bti(&bus->iommu, 0, BTI_BOARD, &bti); 170 if (status != ZX_OK) { 171 zxlogf(ERROR, "%s: iommu_get_bti failed %d\n", __FUNCTION__, status); 172 return status; 173 } 174 175 status = imx_usb_phy_init(IMX8M_USB1_BASE, IMX8M_USB1_LENGTH, bti); 176 if (status != ZX_OK) { 177 zxlogf(ERROR, "%s: imx_usb_phy_init failed %d\n", __FUNCTION__, status); 178 zx_handle_close(bti); 179 return status; 180 } 181 status = imx_usb_phy_init(IMX8M_USB2_BASE, IMX8M_USB2_LENGTH, bti); 182 if (status != ZX_OK) { 183 zxlogf(ERROR, "%s: imx_usb_phy_init failed %d\n", __FUNCTION__, status); 184 zx_handle_close(bti); 185 return status; 186 } 187 zx_handle_close(bti); 188 189 if ((status = pbus_device_add(&bus->pbus, &usb1_dev)) != ZX_OK) { 190 zxlogf(ERROR, "imx_usb_init could not add usb1_dev: %d\n", status); 191 return status; 192 } 193 if ((status = pbus_device_add(&bus->pbus, &usb2_dev)) != ZX_OK) { 194 zxlogf(ERROR, "imx_usb_init could not add usb2_dev: %d\n", status); 195 return status; 196 } 197 return ZX_OK; 198} 199