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 <assert.h>
6#include <limits.h>
7#include <stdint.h>
8#include <stdio.h>
9#include <stdlib.h>
10#include <string.h>
11#include <threads.h>
12#include <unistd.h>
13
14#include <ddk/binding.h>
15#include <ddk/debug.h>
16#include <ddk/device.h>
17#include <ddk/driver.h>
18#include <ddk/protocol/platform-defs.h>
19#include <hw/reg.h>
20
21#include <soc/imx8m/imx8m-hw.h>
22#include <soc/imx8m/imx8m-iomux.h>
23
24#include <zircon/assert.h>
25#include <zircon/process.h>
26#include <zircon/syscalls.h>
27#include <zircon/threads.h>
28
29#include "imx8mevk.h"
30
31static pbus_mmio_t imx8mevk_display_mmios[] = {
32    {
33        .base = IMX8M_AIPS_DC_MST1_BASE,
34        .length = IMX8M_AIPS_LENGTH,
35    },
36};
37
38static const pbus_bti_t imx8mevk_display_btis[] = {
39    {
40        .iommu_index = 0,
41        .bti_id = BTI_DISPLAY,
42    },
43};
44
45static const pbus_dev_t display_dev = {
46    .name = "display",
47    .vid = PDEV_VID_NXP,
48    .pid = PDEV_PID_IMX8MEVK,
49    .did = PDEV_DID_IMX_DISPLAY,
50    .mmios = imx8mevk_display_mmios,
51    .mmio_count = countof(imx8mevk_display_mmios),
52    .btis = imx8mevk_display_btis,
53    .bti_count = countof(imx8mevk_display_btis),
54};
55
56/* iMX8M EVK Pin Mux Table TODO: Add all supported peripherals on EVK board */
57iomux_cfg_struct imx8mevk_pinmux[] = {
58    // UART1 RX
59    MAKE_PIN_CFG_UART(0, SW_MUX_CTL_PAD_UART1_RXD,
60                      SW_PAD_CTL_PAD_UART1_RXD,
61                      UART1_RXD_SELECT_INPUT),
62    // UART1 TX
63    MAKE_PIN_CFG_UART(0, SW_MUX_CTL_PAD_UART1_TXD,
64                      SW_PAD_CTL_PAD_UART1_TXD,
65                      0x000ULL),
66
67    // PWR_LED (used for GPIO Driver)
68    MAKE_PIN_CFG_DEFAULT(0, SW_MUX_CTL_PAD_GPIO1_IO13),
69
70    // eMMC (USDHC1) Pinmux
71    MAKE_PIN_CFG_USDHC_CLK(0, SW_MUX_CTL_PAD_SD1_CLK,
72                           SW_PAD_CTL_PAD_SD1_CLK),
73    MAKE_PIN_CFG_USDHC(0, SW_MUX_CTL_PAD_SD1_CMD,
74                       SW_PAD_CTL_PAD_SD1_CMD),
75    MAKE_PIN_CFG_USDHC(0, SW_MUX_CTL_PAD_SD1_DATA0,
76                       SW_PAD_CTL_PAD_SD1_DATA0),
77    MAKE_PIN_CFG_USDHC(0, SW_MUX_CTL_PAD_SD1_DATA1,
78                       SW_PAD_CTL_PAD_SD1_DATA1),
79    MAKE_PIN_CFG_USDHC(0, SW_MUX_CTL_PAD_SD1_DATA2,
80                       SW_PAD_CTL_PAD_SD1_DATA2),
81    MAKE_PIN_CFG_USDHC(0, SW_MUX_CTL_PAD_SD1_DATA3,
82                       SW_PAD_CTL_PAD_SD1_DATA3),
83    MAKE_PIN_CFG_USDHC(0, SW_MUX_CTL_PAD_SD1_DATA4,
84                       SW_PAD_CTL_PAD_SD1_DATA4),
85    MAKE_PIN_CFG_USDHC(0, SW_MUX_CTL_PAD_SD1_DATA5,
86                       SW_PAD_CTL_PAD_SD1_DATA5),
87    MAKE_PIN_CFG_USDHC(0, SW_MUX_CTL_PAD_SD1_DATA6,
88                       SW_PAD_CTL_PAD_SD1_DATA6),
89    MAKE_PIN_CFG_USDHC(0, SW_MUX_CTL_PAD_SD1_DATA7,
90                       SW_PAD_CTL_PAD_SD1_DATA7),
91    MAKE_PIN_CFG_USDHC_CLK(0, SW_MUX_CTL_PAD_SD1_STROBE,
92                           SW_PAD_CTL_PAD_SD1_STROBE),
93
94    MAKE_PIN_CFG_DEFAULT(5, SW_MUX_CTL_PAD_SD1_RESET_B),
95};
96
97static void imx8mevk_bus_release(void* ctx) {
98    imx8mevk_bus_t* bus = ctx;
99    free(bus);
100}
101
102static zx_protocol_device_t imx8mevk_bus_device_protocol = {
103    .version = DEVICE_OPS_VERSION,
104    .release = imx8mevk_bus_release,
105};
106
107static int imx8mevk_start_thread(void* arg) {
108    zx_status_t status;
109    imx8mevk_bus_t* bus = arg;
110
111    // TODO: Power and Clocks
112
113    // start the gpio driver first so we can do our initial pinmux
114    if ((status = imx8m_gpio_init(bus)) != ZX_OK) {
115        zxlogf(ERROR, "%s: failed %d\n", __FUNCTION__, status);
116        goto fail;
117    }
118
119    // Pinmux
120    for (unsigned i = 0; i < sizeof(imx8mevk_pinmux) / sizeof(imx8mevk_pinmux[0]); i++) {
121        gpio_impl_set_alt_function(&bus->gpio, 0, imx8mevk_pinmux[i]);
122    }
123
124    if ((status = imx_i2c_init(bus)) != ZX_OK) {
125        zxlogf(ERROR, "%s: failed %d\n", __FUNCTION__, status);
126        goto fail;
127    }
128
129    if ((status = imx_usb_init(bus)) != ZX_OK) {
130        zxlogf(ERROR, "%s: failed %d\n", __FUNCTION__, status);
131        goto fail;
132    }
133
134    if ((status = imx_gpu_init(bus)) != ZX_OK) {
135        zxlogf(ERROR, "%s: imx_gpu_init failed %d\n", __FUNCTION__, status);
136        goto fail;
137    }
138
139    if ((status = imx8m_sdhci_init(bus)) != ZX_OK) {
140        zxlogf(ERROR, "%s: failed %d\n", __FUNCTION__, status);
141        goto fail;
142    }
143
144    if ((status = pbus_device_add(&bus->pbus, &display_dev)) != ZX_OK) {
145        zxlogf(ERROR, "%s could not add display_dev: %d\n", __FUNCTION__, status);
146        goto fail;
147    }
148
149    return ZX_OK;
150
151fail:
152    zxlogf(ERROR, "aml_start_thread failed, not all devices have been initialized\n");
153    return status;
154}
155
156static zx_status_t imx8mevk_bus_bind(void* ctx, zx_device_t* parent) {
157    imx8mevk_bus_t* bus = calloc(1, sizeof(imx8mevk_bus_t));
158    if (!bus) {
159        return ZX_ERR_NO_MEMORY;
160    }
161    bus->parent = parent;
162
163    zx_status_t status = device_get_protocol(parent, ZX_PROTOCOL_PLATFORM_BUS, &bus->pbus);
164    if (status != ZX_OK) {
165        goto fail;
166    }
167
168    // get default BTI from the dummy IOMMU implementation in the platform bus
169    status = device_get_protocol(parent, ZX_PROTOCOL_IOMMU, &bus->iommu);
170    if (status != ZX_OK) {
171        zxlogf(ERROR, "%s: Could not get ZX_PROTOCOL_IOMMU\n", __FUNCTION__);
172        goto fail;
173    }
174
175    status = iommu_get_bti(&bus->iommu, 0, BTI_BOARD, &bus->bti_handle);
176    if (status != ZX_OK) {
177        zxlogf(ERROR, "%s: iommu_get_bti failed %d\n", __FUNCTION__, status);
178        goto fail;
179    }
180
181    device_add_args_t args = {
182        .version = DEVICE_ADD_ARGS_VERSION,
183        .name = "imx8mevk",
184        .ctx = bus,
185        .ops = &imx8mevk_bus_device_protocol,
186        .flags = DEVICE_ADD_NON_BINDABLE,
187    };
188
189    status = device_add(parent, &args, NULL);
190    if (status != ZX_OK) {
191        goto fail;
192    }
193
194    thrd_t t;
195    int thrd_rc = thrd_create_with_name(&t, imx8mevk_start_thread, bus, "imx8mevk_start_thread");
196    if (thrd_rc != thrd_success) {
197        status = thrd_status_to_zx_status(thrd_rc);
198        goto fail;
199    }
200
201    return ZX_OK;
202
203fail:
204    zxlogf(ERROR, "%s failed. %d\n", __FUNCTION__, status);
205    imx8mevk_bus_release(bus);
206    return status;
207}
208
209static zx_driver_ops_t imx8mevk_bus_driver_ops = {
210    .version = DRIVER_OPS_VERSION,
211    .bind = imx8mevk_bus_bind,
212};
213
214ZIRCON_DRIVER_BEGIN(imx8mevk_bus, imx8mevk_bus_driver_ops, "zircon", "0.1", 6)
215    BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_PLATFORM_BUS),
216    BI_GOTO_IF(NE, BIND_PLATFORM_DEV_VID, PDEV_VID_NXP, 0),
217    BI_MATCH_IF(EQ, BIND_PLATFORM_DEV_PID, PDEV_PID_IMX8MEVK),
218    BI_LABEL(0),
219    BI_ABORT_IF(NE, BIND_PLATFORM_DEV_VID, PDEV_VID_GOOGLE),
220    BI_MATCH_IF(EQ, BIND_PLATFORM_DEV_PID, PDEV_PID_MADRONE),
221ZIRCON_DRIVER_END(imx8mevk_bus)
222