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