blob: 4a15cbaec0798a9033e9b65026bf626bf9c67e44 [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 "src/devices/adc/drivers/aml-saradc/aml-saradc.h"
#include <lib/device-protocol/pdev-fidl.h>
#include <lib/driver/component/cpp/driver_export.h>
#include <lib/driver/component/cpp/node_add_args.h>
#include <fbl/auto_lock.h>
#include "src/devices/adc/drivers/aml-saradc/registers.h"
namespace aml_saradc {
void AmlSaradcDevice::SetClock(uint32_t src, uint32_t div) {
ao_mmio_.ModifyBits32(src << AO_SAR_CLK_SRC_POS, AO_SAR_CLK_SRC_MASK, AO_SAR_CLK_OFFS);
ao_mmio_.ModifyBits32(div << AO_SAR_CLK_DIV_POS, AO_SAR_CLK_DIV_MASK, AO_SAR_CLK_OFFS);
}
void AmlSaradcDevice::Shutdown() {
Stop();
Enable(false);
}
void AmlSaradcDevice::Stop() {
// Stop Conversion
adc_mmio_.SetBits32(REG0_SAMPLING_STOP_MASK, AO_SAR_ADC_REG0_OFFS);
// Disable Sampling
adc_mmio_.ClearBits32(REG0_SAMPLING_ENABLE_MASK, AO_SAR_ADC_REG0_OFFS);
}
void AmlSaradcDevice::ClkEna(bool ena) {
if (ena) {
ao_mmio_.SetBits32(AO_SAR_CLK_ENA_MASK, AO_SAR_CLK_OFFS);
} else {
ao_mmio_.ClearBits32(AO_SAR_CLK_ENA_MASK, AO_SAR_CLK_OFFS);
}
}
void AmlSaradcDevice::Enable(bool ena) {
if (ena) {
// Enable bandgap reference
adc_mmio_.SetBits32(REG11_TS_VBG_EN_MASK, AO_SAR_ADC_REG11_OFFS);
// Set common mode vref
adc_mmio_.ClearBits32(REG11_RSV6_MASK, AO_SAR_ADC_REG11_OFFS);
// Select bandgap as reference
adc_mmio_.ClearBits32(REG11_RSV5_MASK, AO_SAR_ADC_REG11_OFFS);
// Enable IRQ
adc_mmio_.SetBits32(REG0_FIFO_IRQ_EN_MASK, AO_SAR_ADC_REG0_OFFS);
// Enable the ADC
adc_mmio_.SetBits32(REG3_ADC_EN_MASK, AO_SAR_ADC_REG3_OFFS);
zx_nanosleep(zx_deadline_after(ZX_USEC(5)));
// Enable clock source
ClkEna(true);
} else {
// Disable IRQ
adc_mmio_.ClearBits32(REG0_FIFO_IRQ_EN_MASK, AO_SAR_ADC_REG0_OFFS);
// Disable clock source
ClkEna(false);
// Disable the ADC
adc_mmio_.ClearBits32(REG3_ADC_EN_MASK, AO_SAR_ADC_REG3_OFFS);
}
zx_nanosleep(zx_deadline_after(ZX_USEC(10)));
}
void AmlSaradcDevice::GetSample(GetSampleRequest& request, GetSampleCompleter::Sync& completer) {
auto channel = request.channel_id();
if (channel >= kMaxChannels) {
completer.Reply(fit::error(ZX_ERR_INVALID_ARGS));
return;
}
// Slow clock for conversion
ClkEna(false);
SetClock(CLK_SRC_OSCIN, 160);
ClkEna(true);
// Select channel
adc_mmio_.Write32(channel, AO_SAR_ADC_CHAN_LIST_OFFS);
// Set analog mux (active and idle) to requested channel
adc_mmio_.Write32(0x000c000c | (channel << 23) | (channel << 7), AO_SAR_ADC_DETECT_IDLE_SW_OFFS);
// Enable sampling
adc_mmio_.SetBits32(REG0_SAMPLING_ENABLE_MASK, AO_SAR_ADC_REG0_OFFS);
// Start sampling
adc_mmio_.SetBits32(REG0_SAMPLING_START_MASK, AO_SAR_ADC_REG0_OFFS);
fit::result<uint32_t, zx_status_t> result = fit::error(ZX_ERR_UNAVAILABLE);
auto status = irq_.wait(nullptr);
if (status == ZX_OK) {
uint32_t value = adc_mmio_.Read32(AO_SAR_ADC_FIFO_RD_OFFS);
result = fit::ok((value >> 2) & 0x3ff);
} else {
result = fit::error(status);
}
Stop();
ClkEna(false);
SetClock(CLK_SRC_OSCIN, 20);
ClkEna(true);
completer.Reply(result);
}
void AmlSaradcDevice::HwInit() {
adc_mmio_.Write32(0x84004040, AO_SAR_ADC_REG0_OFFS);
// Set IRQ trigger to one sample.
adc_mmio_.ModifyBits32(1 << REG0_FIFO_CNT_IRQ_POS, REG0_FIFO_CNT_IRQ_MASK, AO_SAR_ADC_REG0_OFFS);
// Set channel list to only channel zero
adc_mmio_.Write32(0x00000000, AO_SAR_ADC_CHAN_LIST_OFFS);
// Disable averaging modes
adc_mmio_.Write32(0x00000000, AO_SAR_ADC_AVG_CNTL_OFFS);
adc_mmio_.Write32(0x9388000a, AO_SAR_ADC_REG3_OFFS);
adc_mmio_.Write32(0x010a000a, AO_SAR_ADC_DELAY_OFFS);
adc_mmio_.Write32(0x03eb1a0c, AO_SAR_ADC_AUX_SW_OFFS);
adc_mmio_.Write32(0x008c000c, AO_SAR_ADC_CHAN_10_SW_OFFS);
adc_mmio_.Write32(0x000c000c, AO_SAR_ADC_DETECT_IDLE_SW_OFFS);
// Disable ring counter (not used on g12)
adc_mmio_.SetBits32((1 << 27), AO_SAR_ADC_REG3_OFFS);
adc_mmio_.SetBits32(REG11_RSV1_MASK, AO_SAR_ADC_REG11_OFFS);
adc_mmio_.Write32(0x00002000, AO_SAR_ADC_REG13_OFFS);
// Select 24MHz oscillator / 20 = 1.2MHz
SetClock(CLK_SRC_OSCIN, 20);
Enable(true);
zx_nanosleep(zx_deadline_after(ZX_USEC(10)));
}
zx::result<> AmlSaradc::CreateNode() {
fidl::Arena arena;
auto offers = compat_server_.CreateOffers2(arena);
offers.push_back(
fdf::MakeOffer2<fuchsia_hardware_adcimpl::Service>(arena, component::kDefaultInstance));
auto args = fuchsia_driver_framework::wire::NodeAddArgs::Builder(arena)
.name(arena, kDeviceName)
.offers2(arena, std::move(offers))
.Build();
auto controller_endpoints = fidl::Endpoints<fuchsia_driver_framework::NodeController>::Create();
fidl::WireResult result =
fidl::WireCall(node())->AddChild(args, std::move(controller_endpoints.server), {});
if (!result.ok()) {
FDF_LOG(ERROR, "Failed to add child %s", result.status_string());
return zx::error(result.status());
}
controller_.Bind(std::move(controller_endpoints.client));
return zx::ok();
}
zx::result<> AmlSaradc::Start() {
// Initialize our compat server.
{
zx::result result =
compat_server_.Initialize(incoming(), outgoing(), node_name(), kDeviceName,
compat::ForwardMetadata::Some({DEVICE_METADATA_ADC}));
if (result.is_error()) {
return result.take_error();
}
}
// Map hardware resources from pdev.
std::optional<fdf::MmioBuffer> adc_mmio, ao_mmio;
zx::interrupt irq;
{
zx::result result = incoming()->Connect<fuchsia_hardware_platform_device::Service::Device>();
if (result.is_error()) {
FDF_LOG(ERROR, "Failed to open pdev service: %s", result.status_string());
return result.take_error();
}
auto pdev = ddk::PDevFidl(std::move(result.value()));
if (!pdev.is_valid()) {
FDF_LOG(ERROR, "Failed to get pdev");
return zx::error(ZX_ERR_NO_RESOURCES);
}
auto status = pdev.MapMmio(0, &adc_mmio);
if (status != ZX_OK) {
FDF_LOG(ERROR, "Failed to map mmio 0");
return zx::error(status);
}
status = pdev.MapMmio(1, &ao_mmio);
if (status != ZX_OK) {
FDF_LOG(ERROR, "Failed to map mmio 1");
return zx::error(status);
}
status = pdev.GetInterrupt(0, &irq);
if (status != ZX_OK) {
FDF_LOG(ERROR, "Failed to get interrupt");
return zx::error(status);
}
}
device_ =
std::make_unique<AmlSaradcDevice>(std::move(*adc_mmio), std::move(*ao_mmio), std::move(irq));
device_->HwInit();
auto result = outgoing()->AddService<fuchsia_hardware_adcimpl::Service>(
fuchsia_hardware_adcimpl::Service::InstanceHandler({
.device = bindings_.CreateHandler(device_.get(), fdf::Dispatcher::GetCurrent()->get(),
fidl::kIgnoreBindingClosure),
}));
if (result.is_error()) {
FDF_LOG(ERROR, "Failed to add Device service %s", result.status_string());
return result.take_error();
}
if (zx::result result = CreateNode(); result.is_error()) {
FDF_LOG(ERROR, "Failed to create node %s", result.status_string());
return result.take_error();
}
return zx::ok();
}
void AmlSaradc::PrepareStop(fdf::PrepareStopCompleter completer) {
device_->Shutdown();
completer(zx::ok());
}
} // namespace aml_saradc
FUCHSIA_DRIVER_EXPORT(aml_saradc::AmlSaradc);