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 <stdint.h>
7#include <stdio.h>
8#include <stdlib.h>
9#include <string.h>
10#include <threads.h>
11#include <unistd.h>
12
13#include <ddk/binding.h>
14#include <ddk/debug.h>
15#include <ddk/device.h>
16#include <ddk/driver.h>
17#include <ddk/protocol/platform-bus.h>
18#include <ddk/protocol/platform-defs.h>
19
20#include <zircon/assert.h>
21#include <zircon/process.h>
22#include <zircon/syscalls.h>
23
24#include "machina.h"
25
26typedef struct {
27    platform_bus_protocol_t pbus;
28} machina_board_t;
29
30static zx_status_t machina_pci_init(void) {
31    zx_status_t status;
32
33    zx_pci_init_arg_t* arg;
34    // Room for one addr window.
35    size_t arg_size = sizeof(*arg) + sizeof(arg->addr_windows[0]);
36    arg = calloc(1, arg_size);
37    if (!arg) {
38        return ZX_ERR_NO_MEMORY;
39    }
40
41    status = zx_pci_add_subtract_io_range(get_root_resource(), true /* mmio */,
42                                          PCIE_MMIO_BASE_PHYS,
43                                          PCIE_MMIO_SIZE, true /* add */);
44    if (status != ZX_OK) {
45        goto fail;
46    }
47
48    // Initialize our swizzle table
49    zx_pci_irq_swizzle_lut_t* lut = &arg->dev_pin_to_global_irq;
50    for (unsigned dev_id = 0; dev_id < ZX_PCI_MAX_DEVICES_PER_BUS; dev_id++) {
51        for (unsigned func_id = 0; func_id < ZX_PCI_MAX_FUNCTIONS_PER_DEVICE; func_id++) {
52            for (unsigned pin = 0; pin < ZX_PCI_MAX_LEGACY_IRQ_PINS; pin++) {
53                (*lut)[dev_id][func_id][pin] = PCIE_INT_BASE + dev_id;
54            }
55        }
56    }
57    arg->num_irqs = 0;
58    arg->addr_window_count = 1;
59    arg->addr_windows[0].is_mmio = true;
60    arg->addr_windows[0].has_ecam = true;
61    arg->addr_windows[0].base = PCIE_ECAM_BASE_PHYS;
62    arg->addr_windows[0].size = PCIE_ECAM_SIZE;
63    arg->addr_windows[0].bus_start = 0;
64    arg->addr_windows[0].bus_end = (PCIE_ECAM_SIZE / ZX_PCI_ECAM_BYTE_PER_BUS) - 1;
65
66    status = zx_pci_init(get_root_resource(), arg, arg_size);
67    if (status != ZX_OK) {
68        zxlogf(ERROR, "%s: error %d in zx_pci_init\n", __FUNCTION__, status);
69        goto fail;
70    }
71
72fail:
73    free(arg);
74    return status;
75}
76
77static void machina_board_release(void* ctx) {
78    machina_board_t* bus = ctx;
79    free(bus);
80}
81
82static zx_protocol_device_t machina_board_device_protocol = {
83    .version = DEVICE_OPS_VERSION,
84    .release = machina_board_release,
85};
86
87static const pbus_mmio_t pl031_mmios[] = {
88    {
89        .base = RTC_BASE_PHYS,
90        .length = RTC_SIZE,
91    },
92};
93
94static const pbus_dev_t pl031_dev = {
95    .name = "pl031",
96    .vid = PDEV_VID_GENERIC,
97    .pid = PDEV_PID_GENERIC,
98    .did = PDEV_DID_RTC_PL031,
99    .mmios = pl031_mmios,
100    .mmio_count = countof(pl031_mmios),
101};
102
103static zx_status_t machina_board_bind(void* ctx, zx_device_t* parent) {
104    machina_board_t* bus = calloc(1, sizeof(machina_board_t));
105    if (!bus) {
106        return ZX_ERR_NO_MEMORY;
107    }
108
109    if (device_get_protocol(parent, ZX_PROTOCOL_PLATFORM_BUS, &bus->pbus) != ZX_OK) {
110        free(bus);
111        return ZX_ERR_NOT_SUPPORTED;
112    }
113
114    zx_status_t status = machina_pci_init();
115    if (status != ZX_OK) {
116        goto fail;
117    }
118
119    device_add_args_t args = {
120        .version = DEVICE_ADD_ARGS_VERSION,
121        .name = "machina",
122        .ops = &machina_board_device_protocol,
123        .flags = DEVICE_ADD_NON_BINDABLE,
124    };
125
126    status = device_add(parent, &args, NULL);
127    if (status != ZX_OK) {
128        goto fail;
129    }
130
131    pbus_bti_t pci_btis[] = {
132        {
133            .iommu_index = 0,
134            .bti_id = 0,
135        },
136    };
137
138    pbus_dev_t pci_dev = {
139        .name = "pci",
140        .vid = PDEV_VID_GENERIC,
141        .pid = PDEV_PID_GENERIC,
142        .did = PDEV_DID_KPCI,
143        .btis = pci_btis,
144        .bti_count = countof(pci_btis),
145    };
146
147    status = pbus_device_add(&bus->pbus, &pci_dev);
148    if (status != ZX_OK) {
149        zxlogf(ERROR, "machina_board_bind could not add pci_dev: %d\n", status);
150    }
151
152    status = pbus_device_add(&bus->pbus, &pl031_dev);
153    if (status != ZX_OK) {
154        zxlogf(ERROR, "machina_board_bind could not add pl031: %d\n", status);
155    }
156
157    return ZX_OK;
158
159fail:
160    printf("machina_board_bind failed %d\n", status);
161    machina_board_release(bus);
162    return status;
163}
164
165static zx_driver_ops_t machina_board_driver_ops = {
166    .version = DRIVER_OPS_VERSION,
167    .bind = machina_board_bind,
168};
169
170ZIRCON_DRIVER_BEGIN(machina_board, machina_board_driver_ops, "zircon", "0.1", 3)
171    BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_PLATFORM_BUS),
172    BI_ABORT_IF(NE, BIND_PLATFORM_DEV_VID, PDEV_VID_GOOGLE),
173    BI_MATCH_IF(EQ, BIND_PLATFORM_DEV_PID, PDEV_PID_MACHINA),
174ZIRCON_DRIVER_END(machina_board)
175