blob: 675c3a58519e4884b9aa876ba85234b5b4259868 [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 "vim3-mcu.h"
#include <lib/ddk/binding_driver.h>
#include <lib/ddk/debug.h>
#include <lib/ddk/device.h>
#include <lib/ddk/driver.h>
#include <lib/ddk/platform-defs.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <zircon/compiler.h>
#include <iterator>
#include <ddktl/fidl.h>
#include <fbl/algorithm.h>
#include <fbl/alloc_checker.h>
#include <fbl/auto_lock.h>
#include "src/devices/i2c/lib/i2c-channel-legacy/i2c-channel.h"
namespace stm {
zx_status_t StmMcu::Create(void* ctx, zx_device_t* parent) {
ddk::I2cChannel i2c(parent, "i2c");
if (!i2c.is_valid()) {
zxlogf(ERROR, "Failed to get ZX_PROTOCOL_I2C");
return ZX_ERR_NO_RESOURCES;
}
fbl::AllocChecker ac;
std::unique_ptr<StmMcu> device(new (&ac) StmMcu(parent, std::move(i2c)));
if (!ac.check()) {
zxlogf(ERROR, "StmMcu alloc failed");
return ZX_ERR_NO_MEMORY;
}
device->Init();
zx::result result = device->DdkAddService<fuchsia_hardware_fan::Service>(
fuchsia_hardware_fan::Service::InstanceHandler({
.device = device->bindings_.CreateHandler(
device.get(), fdf::Dispatcher::GetCurrent()->async_dispatcher(),
fidl::kIgnoreBindingClosure),
}));
if (result.is_error()) {
zxlogf(ERROR, "DdkAddService failed");
return result.status_value();
}
if (zx_status_t status =
device->DdkAdd(ddk::DeviceAddArgs("vim3-mcu").set_inspect_vmo(device->inspect_vmo()));
status != ZX_OK) {
zxlogf(ERROR, "DdkAdd failed");
return status;
}
[[maybe_unused]] auto* dummy = device.release();
return ZX_OK;
}
void StmMcu::ShutDown() {}
void StmMcu::DdkUnbind(ddk::UnbindTxn txn) {
ShutDown();
txn.Reply();
}
void StmMcu::DdkRelease() { delete this; }
void StmMcu::Init() {
SetFanLevel(static_cast<FanLevel>(VIM3_MCU_FAN_DEFAULT_LEVEL));
const uint8_t wol_reset_enable[] = {STM_MCU_REG_BOOT_EN_WOL, STM_MCU_REG_EN_WOL_RESET_ENABLE};
zx_status_t status = i2c_.WriteSync(wol_reset_enable, std::size(wol_reset_enable));
if (status != ZX_OK) {
zxlogf(WARNING, "Failed to enable WOL: %d", status);
}
uint8_t pcie_enabled = 0;
status = i2c_.ReadSync(STM_MCU_USB_PCIE_SWITCH_REG, &pcie_enabled, 1);
if (status != ZX_OK) {
zxlogf(WARNING, "Failed to read PCIe enabled: %s", zx_status_get_string(status));
pcie_enabled = -1;
}
if (pcie_enabled != 0) {
zxlogf(INFO, "PCIe is enabled. Switching it to USB3.");
pcie_enabled = 0;
const uint8_t write_buf[] = {STM_MCU_USB_PCIE_SWITCH_REG, pcie_enabled};
status = i2c_.WriteSync(write_buf, std::size(write_buf));
if (status != ZX_OK) {
zxlogf(ERROR, "Failed to disable PCIe: %s", zx_status_get_string(status));
pcie_enabled = 1;
}
}
inspect_.GetRoot().CreateUint("PCIe enabled", pcie_enabled, &inspect_);
}
zx_status_t StmMcu::SetFanLevel(FanLevel level) {
fbl::AutoLock lock(&i2c_lock_);
uint8_t cmd[] = {STM_MCU_REG_CMD_FAN_STATUS_CTRL_REG, static_cast<uint8_t>(level)};
auto status = i2c_.WriteSync(cmd, sizeof(cmd));
if (status != ZX_OK) {
zxlogf(ERROR, "Could not set the fan level: %d", status);
return status;
}
fan_level_ = level;
return ZX_OK;
}
static constexpr zx_driver_ops_t stm_driver_ops = []() {
zx_driver_ops_t ops = {};
ops.version = DRIVER_OPS_VERSION;
ops.bind = StmMcu::Create;
return ops;
}();
} // namespace stm
ZIRCON_DRIVER(vim3_mcu, stm::stm_driver_ops, "zircon", "0.1");