blob: c9ccc6ff714b4d41a9116b476929571440c2fa6c [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "vs680-clk.h"
#include <lib/ddk/platform-defs.h>
#include <lib/device-protocol/pdev.h>
#include <fbl/algorithm.h>
#include <fbl/auto_lock.h>
#include <soc/vs680/vs680-clk.h>
#include "src/devices/clock/drivers/vs680-clk/vs680-clk-bind.h"
namespace clk {
zx_status_t Vs680Clk::Create(void* ctx, zx_device_t* parent) {
ddk::PDev pdev(parent);
if (!pdev.is_valid()) {
zxlogf(ERROR, "%s: failed to get pdev", __FILE__);
return ZX_ERR_NO_RESOURCES;
}
std::optional<ddk::MmioBuffer> avio_mmio, cpu_pll_mmio, chip_ctrl_mmio;
zx_status_t status = pdev.MapMmio(vs680::kAvioMmio, &avio_mmio);
if (status != ZX_OK) {
zxlogf(ERROR, "%s: failed to map AVIO MMIO: %d", __FILE__, status);
return status;
}
if ((status = pdev.MapMmio(vs680::kCpuPllMmio, &cpu_pll_mmio)) != ZX_OK) {
zxlogf(ERROR, "%s: failed to map CPUPLL MMIO: %d", __FILE__, status);
return status;
}
if ((status = pdev.MapMmio(vs680::kChipCtrlMmio, &chip_ctrl_mmio)) != ZX_OK) {
zxlogf(ERROR, "%s: failed to map chip ctrl MMIO: %d", __FILE__, status);
return status;
}
std::unique_ptr<Vs680Clk> device(new Vs680Clk(parent, *std::move(chip_ctrl_mmio),
*std::move(cpu_pll_mmio), *std::move(avio_mmio)));
if ((status = device->DdkAdd("vs680-clk")) != ZX_OK) {
zxlogf(ERROR, "%s: DdkAdd failed %d", __FILE__, status);
return status;
}
__UNUSED auto _ = device.release();
return ZX_OK;
}
zx_status_t Vs680Clk::ClockImplEnable(uint32_t id) {
fbl::AutoLock lock(&lock_);
if (id >= std::size(clocks_) || !clocks_[id]) {
return ZX_ERR_OUT_OF_RANGE;
}
return clocks_[id]->Enable();
}
zx_status_t Vs680Clk::ClockImplDisable(uint32_t id) {
fbl::AutoLock lock(&lock_);
if (id >= std::size(clocks_) || !clocks_[id]) {
return ZX_ERR_OUT_OF_RANGE;
}
return clocks_[id]->Disable();
}
zx_status_t Vs680Clk::ClockImplIsEnabled(uint32_t id, bool* out_enabled) {
fbl::AutoLock lock(&lock_);
if (id >= std::size(clocks_) || !clocks_[id]) {
return ZX_ERR_OUT_OF_RANGE;
}
return clocks_[id]->IsEnabled(out_enabled);
}
zx_status_t Vs680Clk::ClockImplSetRate(uint32_t id, uint64_t hz) {
fbl::AutoLock lock(&lock_);
if (id >= std::size(clocks_) || !clocks_[id]) {
return ZX_ERR_OUT_OF_RANGE;
}
auto result = GetParentRate(id);
if (result.is_error()) {
return result.status_value();
}
return clocks_[id]->SetRate(result.value(), hz);
}
zx_status_t Vs680Clk::ClockImplQuerySupportedRate(uint32_t id, uint64_t hz, uint64_t* out_hz) {
fbl::AutoLock lock(&lock_);
if (id >= std::size(clocks_) || !clocks_[id]) {
return ZX_ERR_OUT_OF_RANGE;
}
auto result = GetParentRate(id);
if (result.is_error()) {
return result.status_value();
}
return clocks_[id]->QuerySupportedRate(result.value(), hz, out_hz);
}
zx_status_t Vs680Clk::ClockImplGetRate(uint32_t id, uint64_t* out_hz) {
fbl::AutoLock lock(&lock_);
if (id >= std::size(clocks_) || !clocks_[id]) {
return ZX_ERR_OUT_OF_RANGE;
}
auto result = GetRate(id);
if (result.is_ok()) {
*out_hz = result.value();
}
return result.status_value();
}
zx_status_t Vs680Clk::ClockImplSetInput(uint32_t id, uint32_t idx) {
fbl::AutoLock lock(&lock_);
if (id >= std::size(clocks_) || !clocks_[id]) {
return ZX_ERR_OUT_OF_RANGE;
}
return clocks_[id]->SetInput(idx);
}
zx_status_t Vs680Clk::ClockImplGetNumInputs(uint32_t id, uint32_t* out_n) {
fbl::AutoLock lock(&lock_);
if (id >= std::size(clocks_) || !clocks_[id]) {
return ZX_ERR_OUT_OF_RANGE;
}
return clocks_[id]->GetNumInputs(out_n);
}
zx_status_t Vs680Clk::ClockImplGetInput(uint32_t id, uint32_t* out_index) {
fbl::AutoLock lock(&lock_);
if (id >= std::size(clocks_) || !clocks_[id]) {
return ZX_ERR_OUT_OF_RANGE;
}
return clocks_[id]->GetInput(out_index);
}
zx::status<uint64_t> Vs680Clk::GetRate(uint32_t id) {
auto result = GetParentRate(id);
if (result.is_error()) {
return result;
}
uint64_t rate_hz;
zx_status_t status = clocks_[id]->GetRate(result.value(), &rate_hz);
if (status != ZX_OK) {
return zx::error_status(status);
}
return zx::success(rate_hz);
}
zx::status<uint64_t> Vs680Clk::GetParentRate(uint32_t id) {
auto result = clocks_[id]->GetInputId();
if (result.is_error()) {
return result;
}
if (result.value() >= vs680::kClockCount) {
return zx::success(0);
}
return GetRate(result.value());
}
} // namespace clk
static constexpr zx_driver_ops_t vs680_clk_driver_ops = []() {
zx_driver_ops_t ops = {};
ops.version = DRIVER_OPS_VERSION;
ops.bind = clk::Vs680Clk::Create;
return ops;
}();
ZIRCON_DRIVER(vs680_clk, vs680_clk_driver_ops, "zircon", "0.1");