blob: f84e8a9a921607e15f004f2e407c58a85eb92e78 [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 "thermistor.h"
#include <lib/device-protocol/pdev.h>
#include <lib/mmio/mmio.h>
#include <lib/zx/interrupt.h>
#include <string.h>
#include <zircon/types.h>
#include <ddk/debug.h>
#include <ddk/metadata.h>
#include <ddk/platform-defs.h>
#include <fbl/auto_call.h>
#include <fbl/ref_counted.h>
#include <soc/aml-common/aml-g12-saradc.h>
#include "src/devices/thermal/drivers/astro-thermistor/astro-thermistor-bind.h"
namespace thermal {
static constexpr uint32_t kMaxNtcChannels = 4;
static constexpr uint32_t kMaxAdcChannels = 4;
zx_status_t AstroThermistor::Create(void* ctx, zx_device_t* parent) {
zx_status_t status;
std::unique_ptr<AstroThermistor> device(new AstroThermistor(parent));
if ((status = device->DdkAdd("thermistor-device") != ZX_OK)) {
zxlogf(ERROR, "%s: DdkAdd failed", __func__);
return status;
}
__UNUSED auto* unused = device.release();
return ZX_OK;
}
zx_status_t AstroThermistor::InitPdev() {
zx_status_t status;
ddk::PDev pdev(parent());
if (!pdev.is_valid()) {
zxlogf(ERROR, "%s: failed to get pdev", __func__);
return ZX_ERR_NO_RESOURCES;
}
std::optional<ddk::MmioBuffer> adc_mmio;
status = pdev.MapMmio(0, &adc_mmio);
if (status != ZX_OK) {
return status;
}
std::optional<ddk::MmioBuffer> ao_mmio;
status = pdev.MapMmio(1, &ao_mmio);
if (status != ZX_OK) {
return status;
}
zx::interrupt irq;
status = pdev.GetInterrupt(0, &irq);
if (status != ZX_OK) {
zxlogf(ERROR, "dwmac: could not map dma interrupt");
return status;
}
saradc_ = fbl::MakeRefCounted<AmlSaradcDevice>(*std::move(adc_mmio), *std::move(ao_mmio),
std::move(irq));
return ZX_OK;
}
zx_status_t AstroThermistor::AddThermChannel(NtcChannel ch, NtcInfo info) {
zx_status_t status;
std::unique_ptr<ThermistorChannel> dev(
new ThermistorChannel(zxdev(), saradc_, ch.adc_channel, info, ch.pullup_ohms));
status = dev->DdkAdd(ddk::DeviceAddArgs(ch.name));
if (status != ZX_OK) {
return status;
}
__UNUSED auto ptr = dev.release();
return ZX_OK;
}
zx_status_t AstroThermistor::AddRawChannel(uint32_t adc_chan) {
std::unique_ptr<RawChannel> dev(new RawChannel(zxdev(), saradc_, adc_chan));
char name[20];
snprintf(name, sizeof(name), "adc-%d", adc_chan);
zx_status_t status = dev->DdkAdd(ddk::DeviceAddArgs(name));
if (status != ZX_OK) {
return status;
}
__UNUSED auto ptr = dev.release();
return ZX_OK;
}
void AstroThermistor::DdkInit(ddk::InitTxn txn) {
zx_status_t status = ZX_OK;
status = InitPdev();
if (status != ZX_OK) {
txn.Reply(status);
return;
}
saradc_->HwInit();
auto on_error = fbl::MakeAutoCall([this]() { saradc_->Shutdown(); });
NtcChannel ntc_channels[kMaxNtcChannels];
size_t actual;
status =
DdkGetMetadata(NTC_CHANNELS_METADATA_PRIVATE, &ntc_channels, sizeof(ntc_channels), &actual);
if (status != ZX_OK) {
txn.Reply(status);
return;
}
ZX_DEBUG_ASSERT(actual % sizeof(NtcChannel) == 0);
size_t num_channels = actual / sizeof(NtcChannel);
NtcInfo ntc_info[kMaxNtcChannels];
status = DdkGetMetadata(NTC_PROFILE_METADATA_PRIVATE, &ntc_info, sizeof(ntc_info), &actual);
if (status != ZX_OK) {
txn.Reply(status);
return;
}
ZX_DEBUG_ASSERT(actual % sizeof(NtcInfo) == 0);
size_t num_profiles = actual / sizeof(NtcInfo);
for (uint32_t i = 0; i < num_channels; i++) {
if (ntc_channels[i].profile_idx >= num_profiles) {
txn.Reply(ZX_ERR_INVALID_ARGS);
return;
}
status = AddThermChannel(ntc_channels[i], ntc_info[ntc_channels[i].profile_idx]);
if (status != ZX_OK) {
txn.Reply(status);
return;
}
}
// Expose all the adc channels via adc protocol
// this includes channels which may not have a thermistor.
for (uint32_t i = 0; i < kMaxAdcChannels; i++) {
status = AddRawChannel(i);
if (status != ZX_OK) {
txn.Reply(status);
return;
}
}
txn.Reply(ZX_OK);
on_error.cancel();
}
static constexpr zx_driver_ops_t driver_ops = []() {
zx_driver_ops_t ops = {};
ops.version = DRIVER_OPS_VERSION;
ops.bind = AstroThermistor::Create;
return ops;
}();
} // namespace thermal
// clang-format off
ZIRCON_DRIVER(astro-thermistor, thermal::driver_ops, "thermistor", "0.1");