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