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
6#include "aml-pcie-device.h"
7
8#include <ddk/debug.h>
9#include <ddk/protocol/platform-defs.h>
10#include <fbl/algorithm.h>
11#include <zircon/driver/binding.h>
12
13#include "aml-pcie-clk.h"
14#include "aml-pcie.h"
15
16namespace pcie {
17namespace aml {
18
19const size_t kElbMmio = 0;
20const size_t kCfgMmio = 1;
21const size_t kRstMmio = 2;
22const size_t kPllMmio = 3;
23
24const size_t kClk81 = 0;
25const size_t kClkPcieA = 1;
26const size_t kClkPort = 2;
27
28zx_status_t AmlPcieDevice::InitProtocols() {
29    zx_status_t st;
30
31    st = device_get_protocol(parent_, ZX_PROTOCOL_PLATFORM_DEV, &pdev_);
32    if (st != ZX_OK) {
33        zxlogf(ERROR, "aml_pcie: failed to get pdev protocol, st = %d", st);
34        return st;
35    }
36
37    st = device_get_protocol(parent_, ZX_PROTOCOL_GPIO, &gpio_);
38    if (st != ZX_OK) {
39        zxlogf(ERROR, "aml_pcie: failed to get gpio protocol, st = %d", st);
40        return st;
41    }
42
43    st = gpio_config_out(&gpio_, 0);
44    if (st != ZX_OK) {
45        zxlogf(ERROR, "aml_pcie: failed to configure rst gpio, st = %d", st);
46        return st;
47    }
48
49    st = device_get_protocol(parent_, ZX_PROTOCOL_CLK, &clk_);
50    if (st != ZX_OK) {
51        zxlogf(ERROR, "aml_pcie: failed to get clk protocol, st = %d", st);
52        return st;
53    }
54
55    return st;
56}
57
58zx_status_t AmlPcieDevice::InitMmios() {
59    zx_status_t st;
60
61    st = pdev_map_mmio_buffer(&pdev_, kElbMmio,
62                              ZX_CACHE_POLICY_UNCACHED_DEVICE, &dbi_);
63    if (st != ZX_OK) {
64        zxlogf(ERROR, "aml_pcie: failed to map elbi mmio, st = %d\n", st);
65        return st;
66    }
67
68    st = pdev_map_mmio_buffer(&pdev_, kCfgMmio,
69                              ZX_CACHE_POLICY_UNCACHED_DEVICE, &cfg_);
70    if (st != ZX_OK) {
71        zxlogf(ERROR, "aml_pcie: failed to map cfg mmio, st = %d\n", st);
72        return st;
73    }
74
75    st = pdev_map_mmio_buffer(&pdev_, kRstMmio,
76                              ZX_CACHE_POLICY_UNCACHED_DEVICE, &rst_);
77    if (st != ZX_OK) {
78        zxlogf(ERROR, "aml_pcie: failed to map rst mmio, st = %d\n", st);
79        return st;
80    }
81
82    st = pdev_map_mmio_buffer(&pdev_, kPllMmio,
83                              ZX_CACHE_POLICY_UNCACHED_DEVICE, &pll_);
84    if (st != ZX_OK) {
85        zxlogf(ERROR, "aml_pcie: failed to map pll mmio, st = %d\n", st);
86        return st;
87    }
88
89    return st;
90}
91
92zx_status_t AmlPcieDevice::InitMetadata() {
93    zx_status_t st;
94    size_t actual;
95
96    st = device_get_metadata(parent_, IATU_CFG_APERTURE_METADATA, &atu_cfg_,
97                             sizeof(atu_cfg_), &actual);
98    if (st != ZX_OK || actual != sizeof(atu_cfg_)) {
99        zxlogf(ERROR, "aml_pcie: could not get cfg atu metadata\n");
100        return st;
101    }
102
103    st = device_get_metadata(parent_, IATU_IO_APERTURE_METADATA, &atu_io_,
104                             sizeof(atu_io_), &actual);
105    if (st != ZX_OK || actual != sizeof(atu_io_)) {
106        zxlogf(ERROR, "aml_pcie: could not get io atu metadata\n");
107        return st;
108    }
109
110    st = device_get_metadata(parent_, IATU_MMIO_APERTURE_METADATA, &atu_mem_,
111                             sizeof(atu_mem_), &actual);
112    if (st != ZX_OK || actual != sizeof(atu_mem_)) {
113        zxlogf(ERROR, "aml_pcie: could not get mem atu metadata\n");
114        return st;
115    }
116
117    return st;
118}
119
120static void aml_pcie_release(void* ctx) {
121    AmlPcieDevice* self = reinterpret_cast<AmlPcieDevice*>(ctx);
122
123    delete self;
124}
125
126static zx_protocol_device_t aml_pcie_device_proto = []() {
127    zx_protocol_device_t result;
128    result.version = DEVICE_OPS_VERSION;
129    result.release = aml_pcie_release;
130    return result;
131}();
132
133zx_device_prop_t props[] = {
134    { BIND_PLATFORM_DEV_VID, 0, PDEV_VID_GENERIC },
135    { BIND_PLATFORM_DEV_PID, 0, PDEV_PID_GENERIC },
136    { BIND_PLATFORM_DEV_DID, 0, PDEV_DID_KPCI },
137};
138
139device_add_args_t pci_dev_args = []() {
140    device_add_args_t result;
141
142    result.version = DEVICE_ADD_ARGS_VERSION;
143    result.name = "aml-dw-pcie";
144    result.ops = &aml_pcie_device_proto,
145    result.props = props;
146    result.prop_count = fbl::count_of(props);
147
148    return result;
149}();
150
151zx_status_t AmlPcieDevice::Init() {
152    zx_status_t st;
153
154    st = InitProtocols();
155    if (st != ZX_OK) return st;
156
157    st = InitMmios();
158    if (st != ZX_OK) return st;
159
160    st = InitMetadata();
161    if (st != ZX_OK) return st;
162
163    pcie_ = fbl::make_unique<AmlPcie>(
164        io_buffer_virt(&dbi_),
165        io_buffer_virt(&cfg_),
166        io_buffer_virt(&rst_),
167        1   // Single Lane PCIe
168    );
169
170    pcie_->AssertReset(kRstPcieA | kRstPcieB | kRstPcieApb | kRstPciePhy);
171
172    PllSetRate((zx_vaddr_t)io_buffer_virt(&pll_));
173
174    pcie_->ClearReset(kRstPcieApb | kRstPciePhy);
175
176    st = clk_enable(&clk_, kClk81);
177    if (st != ZX_OK) {
178        zxlogf(ERROR, "aml_pcie: failed to init root clock, st = %d\n", st);
179        return st;
180    }
181
182    st = clk_enable(&clk_, kClkPcieA);
183    if (st != ZX_OK) {
184        zxlogf(ERROR, "aml_pcie: failed to init pciea clock, st = %d\n", st);
185        return st;
186    }
187
188    pcie_->ClearReset(kRstPcieA);
189
190    st = clk_enable(&clk_, kClkPort);
191    if (st != ZX_OK) {
192        zxlogf(ERROR, "aml_pcie: failed to init port clock, st = %d\n", st);
193        return st;
194    }
195
196    // Whack the reset gpio.
197    gpio_write(&gpio_, 0);
198    zx_nanosleep(zx_deadline_after(ZX_MSEC(10)));
199    gpio_write(&gpio_, 1);
200
201    st = pcie_->EstablishLink(&atu_cfg_, &atu_io_, &atu_mem_);
202    if (st != ZX_OK) {
203        zxlogf(ERROR, "aml_pcie: failed to establish link, st = %d\n", st);
204        return st;
205    }
206
207    st = zx_pci_add_subtract_io_range(get_root_resource(), false,
208                                      atu_io_.cpu_addr, atu_io_.length, true);
209    if (st != ZX_OK) {
210        zxlogf(ERROR, "aml_pcie: failed to add pcie io range, st = %d\n", st);
211        return st;
212    }
213
214    st = zx_pci_add_subtract_io_range(get_root_resource(), true,
215                                      atu_mem_.cpu_addr, atu_mem_.length, true);
216    if (st != ZX_OK) {
217        zxlogf(ERROR, "aml_pcie: failed to add pcie mmio range, st = %d\n", st);
218        return st;
219    }
220
221    // Fire up the kernel PCI driver!
222    zx_pci_init_arg_t* arg;
223    const size_t arg_size = sizeof(*arg) + sizeof(arg->addr_windows[0]);
224    arg = (zx_pci_init_arg_t*)calloc(1, arg_size);
225    if (!arg) {
226        zxlogf(ERROR, "aml_pcie: failed to allocate pci init arg\n");
227        return ZX_ERR_NO_MEMORY;
228    }
229
230    // Automatically release this object when it goes out of scope.
231    fbl::unique_ptr<zx_pci_init_arg_t> deleter;
232    deleter.reset(arg);
233
234    arg->num_irqs = 0;
235    arg->addr_window_count = 1;
236    arg->addr_windows[0].is_mmio = true;
237    arg->addr_windows[0].has_ecam = true;
238    arg->addr_windows[0].base = atu_cfg_.cpu_addr;
239    arg->addr_windows[0].size = 1 * 1024 * 1024;
240    arg->addr_windows[0].bus_start = 0;
241    arg->addr_windows[0].bus_end = 0xff;
242
243    st = zx_pci_init(get_root_resource(), arg, arg_size);
244    if (st != ZX_OK) {
245        zxlogf(ERROR, "aml_pcie: failed to init pci bus driver, st = %d\n", st);
246        return st;
247    }
248
249    pci_dev_args.ctx = (void*)this;
250
251    st = pdev_device_add(&pdev_, 0, &pci_dev_args, &dev_);
252    if (st != ZX_OK) {
253        zxlogf(ERROR, "aml_pcie: pdev_device_add failed, st = %d\n", st);
254        return st;
255    }
256
257    return st;
258}
259
260AmlPcieDevice::~AmlPcieDevice() {
261    io_buffer_release(&dbi_);
262    io_buffer_release(&cfg_);
263    io_buffer_release(&rst_);
264    io_buffer_release(&pll_);
265}
266
267}  // namespace aml
268}  // namespace pcie
269
270extern "C" zx_status_t aml_pcie_bind(void* ctx, zx_device_t* device, void** cookie) {
271    fbl::AllocChecker ac;
272    pcie::aml::AmlPcieDevice* dev = new (&ac) pcie::aml::AmlPcieDevice(device);
273
274    if (!ac.check()) {
275        zxlogf(ERROR, "aml_pcie: failed to allocate aml pcie device\n");
276        return ZX_ERR_NO_MEMORY;
277    }
278
279    // Note: dev is leaked if the driver successfully binds since devmgr now
280    // owns the memory.
281    zx_status_t st = dev->Init();
282    if (st != ZX_OK) {
283        zxlogf(ERROR, "aml_pcie: failed to start, st = %d\n", st);
284        delete dev;
285    }
286
287    return st;
288}
289