1 // Copyright 2017 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/platform-defs.h>
19 #include <ddk/protocol/scpi.h>
20 #include <hw/reg.h>
21
22 #include <soc/aml-s912/s912-hw.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 "vim.h"
30
vim_bus_release(void * ctx)31 static void vim_bus_release(void* ctx) {
32 vim_bus_t* bus = ctx;
33 free(bus);
34 }
35
36 static zx_protocol_device_t vim_bus_device_protocol = {
37 .version = DEVICE_OPS_VERSION,
38 .release = vim_bus_release,
39 };
40
41 // TODO(rjascani): Remove this when not needed for testing any longer
42 static const pbus_dev_t tee_dev = {
43 .name = "tee",
44 .vid = PDEV_VID_GENERIC,
45 .pid = PDEV_PID_GENERIC,
46 .did = PDEV_DID_OPTEE,
47 };
48
vim_start_thread(void * arg)49 static int vim_start_thread(void* arg) {
50 vim_bus_t* bus = arg;
51 zx_status_t status;
52 pdev_board_info_t info;
53
54 // Fetch the board info so that we can distinguish between the "vim2" and
55 // "vim2-machina" boards. The latter of which has fewer devices available
56 // to Zircon.
57 if ((status = pbus_get_board_info(&bus->pbus, &info)) != ZX_OK) {
58 zxlogf(ERROR, "pbus_get_board_info failed: %d\n", status);
59 goto fail;
60 }
61
62 // Start protocol drivers before adding platform devices.
63 if ((status = vim_gpio_init(bus, info.pid == PDEV_PID_VIM2)) != ZX_OK) {
64 zxlogf(ERROR, "vim_gpio_init failed: %d\n", status);
65 goto fail;
66 }
67 if ((status = vim_i2c_init(bus)) != ZX_OK) {
68 zxlogf(ERROR, "vim_i2c_init failed: %d\n", status);
69 goto fail;
70 }
71 if ((status = vim_clk_init(bus)) != ZX_OK) {
72 zxlogf(ERROR, "vim_clk_init failed: %d\n", status);
73 goto fail;
74 }
75 if (info.pid == PDEV_PID_VIM2) {
76 if ((status = vim2_canvas_init(bus)) != ZX_OK) {
77 zxlogf(ERROR, "vim2_canvas_init failed: %d\n", status);
78 }
79 }
80
81 // Start platform devices.
82 if ((status = vim_uart_init(bus)) != ZX_OK) {
83 zxlogf(ERROR, "vim_uart_init failed: %d\n", status);
84 }
85 if ((status = vim_sd_emmc_init(bus)) != ZX_OK) {
86 zxlogf(ERROR, "vim_sd_emmc_init failed: %d\n", status);
87 }
88 if ((status = vim_sdio_init(bus)) != ZX_OK) {
89 zxlogf(ERROR, "vim_sdio_init failed: %d\n", status);
90 }
91 if ((status = vim_eth_init(bus)) != ZX_OK) {
92 zxlogf(ERROR, "vim_eth_init failed: %d\n", status);
93 }
94 if (info.pid == PDEV_PID_VIM2) {
95 if ((status = vim_usb_init(bus)) != ZX_OK) {
96 zxlogf(ERROR, "vim_usb_init failed: %d\n", status);
97 }
98 if ((status = vim_mali_init(bus, BTI_MALI)) != ZX_OK) {
99 zxlogf(ERROR, "vim_mali_init failed: %d\n", status);
100 }
101 if ((status = vim2_thermal_init(bus)) != ZX_OK) {
102 zxlogf(ERROR, "vim2_thermal_init failed: %d\n", status);
103 }
104 if ((status = vim_display_init(bus)) != ZX_OK) {
105 zxlogf(ERROR, "vim_display_init failed: %d\n", status);
106 }
107 if ((status = vim_video_init(bus)) != ZX_OK) {
108 zxlogf(ERROR, "vim_video_init failed: %d\n", status);
109 }
110 if ((status = vim_led2472g_init(bus)) != ZX_OK) {
111 zxlogf(ERROR, "vim_led2472g_init failed: %d\n", status);
112 }
113 // TODO(rjascani): Remove this when not needed for testing any longer
114 if ((status = pbus_device_add(&bus->pbus, &tee_dev)) != ZX_OK) {
115 zxlogf(ERROR, "vim_start_thread, could not add tee_dev: %d\n", status);
116 }
117 if ((status = vim_rtc_init(bus)) != ZX_OK) {
118 zxlogf(ERROR, "vim_rtc_init failed: %d\n", status);
119 }
120 }
121
122 return ZX_OK;
123 fail:
124 zxlogf(ERROR, "vim_start_thread failed, not all devices have been initialized\n");
125 return status;
126 }
127
vim_bus_bind(void * ctx,zx_device_t * parent)128 static zx_status_t vim_bus_bind(void* ctx, zx_device_t* parent) {
129 vim_bus_t* bus = calloc(1, sizeof(vim_bus_t));
130 if (!bus) {
131 return ZX_ERR_NO_MEMORY;
132 }
133 bus->parent = parent;
134
135 zx_status_t status = device_get_protocol(parent, ZX_PROTOCOL_PBUS, &bus->pbus);
136 if (status != ZX_OK) {
137 goto fail;
138 }
139
140 // Set dummy board revision to facilitate testing of platform device get_board_info support.
141 pbus_board_info_t info;
142 info.board_revision = 1234;
143 pbus_set_board_info(&bus->pbus, &info);
144
145 // Get default BTI from the dummy IOMMU implementation in the platform bus.
146 status = device_get_protocol(parent, ZX_PROTOCOL_IOMMU, &bus->iommu);
147 if (status != ZX_OK) {
148 zxlogf(ERROR, "vim_bus_bind: could not get ZX_PROTOCOL_IOMMU\n");
149 goto fail;
150 }
151
152 device_add_args_t args = {
153 .version = DEVICE_ADD_ARGS_VERSION,
154 .name = "vim-bus",
155 .ctx = bus,
156 .ops = &vim_bus_device_protocol,
157 .flags = DEVICE_ADD_NON_BINDABLE,
158 };
159
160 status = device_add(parent, &args, NULL);
161 if (status != ZX_OK) {
162 goto fail;
163 }
164
165 thrd_t t;
166 int thrd_rc = thrd_create_with_name(&t, vim_start_thread, bus, "vim_start_thread");
167 if (thrd_rc != thrd_success) {
168 status = thrd_status_to_zx_status(thrd_rc);
169 goto fail;
170 }
171
172 return ZX_OK;
173
174 fail:
175 zxlogf(ERROR, "vim_bus_bind failed %d\n", status);
176 vim_bus_release(bus);
177 return status;
178 }
179
180 static zx_driver_ops_t vim_bus_driver_ops = {
181 .version = DRIVER_OPS_VERSION,
182 .bind = vim_bus_bind,
183 };
184
185 ZIRCON_DRIVER_BEGIN(vim_bus, vim_bus_driver_ops, "zircon", "0.1", 4)
186 BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_PBUS),
187 BI_ABORT_IF(NE, BIND_PLATFORM_DEV_VID, PDEV_VID_KHADAS),
188 BI_MATCH_IF(EQ, BIND_PLATFORM_DEV_PID, PDEV_PID_VIM2),
189 BI_MATCH_IF(EQ, BIND_PLATFORM_DEV_PID, PDEV_PID_VIM2_MACHINA),
190 ZIRCON_DRIVER_END(vim_bus)
191