blob: c9f4fdee5a7934b948a707531c194d5259ccccbe [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 "sy-buck.h"
#include <memory>
#include <ddk/debug.h>
#include <ddk/metadata.h>
#include <ddk/metadata/i2c.h>
#include <ddk/platform-defs.h>
#include <ddktl/protocol/composite.h>
#include <hwreg/i2c.h>
#include "src/devices/power/drivers/silergy/sy-buck-bind.h"
#include "sy-buck-regs.h"
namespace silergy {
zx_status_t SyBuck::VregSetVoltageStep(uint32_t step) {
if (step >= kNumSteps) {
zxlogf(ERROR, "%s: Requested step out of range step = %u, max = %u", __func__, step, kNumSteps);
return ZX_ERR_OUT_OF_RANGE;
}
auto vsel = VselReg::Get(vsel_).FromValue(0);
zx_status_t st = vsel.ReadFrom(i2c_);
if (st != ZX_OK) {
zxlogf(ERROR, "%s: failed to read vsel reg, st = %d", __func__, st);
return st;
}
st = vsel.set_n_sel(static_cast<uint8_t>(step)).WriteTo(i2c_);
if (st != ZX_OK) {
zxlogf(ERROR, "%s: failed to write vsel reg, st = %d", __func__, st);
return st;
}
return ZX_OK;
}
uint32_t SyBuck::VregGetVoltageStep() { return current_step_; }
void SyBuck::VregGetRegulatorParams(vreg_params_t* out_params) {
if (!out_params) {
return;
}
out_params->min_uv = kMinVoltageUv;
out_params->num_steps = kNumSteps;
out_params->step_size_uv = kVoltageStepUv;
}
zx_status_t SyBuck::Init() {
zx_status_t st;
auto id1 = Id1Reg::Get().FromValue(0);
auto id2 = Id2Reg::Get().FromValue(0);
auto vsel = VselReg::Get(vsel_).FromValue(0);
st = id1.ReadFrom(i2c_);
if (st != ZX_OK) {
zxlogf(WARNING, "failed to read id1 from i2c, st = %u", st);
return st;
}
st = id2.ReadFrom(i2c_);
if (st != ZX_OK) {
zxlogf(WARNING, "failed to read id2 from i2c, st = %u", st);
return st;
}
st = vsel.ReadFrom(i2c_);
if (st != ZX_OK) {
zxlogf(WARNING, "failed to read vsel from i2c, st = %u", st);
return st;
}
zxlogf(INFO, "sybuck init vendor = %u, die_id = %u, die_rev = %u, vsel = %u", id1.vendor(),
id1.die_id(), id2.die_rev(), static_cast<uint32_t>(vsel_));
current_step_ = vsel.n_sel();
return ZX_OK;
}
zx_status_t SyBuck::Create(void* ctx, zx_device_t* parent) {
zx_status_t st;
zxlogf(DEBUG, "%s: Binding SyBuck", __func__);
// Determine which i2c Bus/Address this device is attached to.
size_t metadata_size = 0;
st = device_get_metadata_size(parent, DEVICE_METADATA_I2C_CHANNELS, &metadata_size);
if (metadata_size != sizeof(i2c_channel_t)) {
zxlogf(ERROR,
"%s: sybuck expects exactly one i2c channel passed as metadata. "
"expected = %lu, got = %lu",
__func__, sizeof(i2c_channel_t), metadata_size);
return ZX_ERR_INTERNAL;
}
i2c_channel_t channel;
st = device_get_metadata(parent, DEVICE_METADATA_I2C_CHANNELS, &channel, sizeof(channel),
&metadata_size);
if (st != ZX_OK || metadata_size != sizeof(i2c_channel_t)) {
zxlogf(ERROR, "%s: Error while getting i2c channel address. st = %d", __func__, st);
return st;
}
ddk::CompositeProtocolClient composite(parent);
if (!composite.is_valid()) {
zxlogf(ERROR, "%s: Failed to get composite protocol.", __func__);
return ZX_ERR_INTERNAL;
}
ddk::I2cProtocolClient i2c(composite, "i2c");
if (!i2c.is_valid()) {
zxlogf(ERROR, "%s: SyBuck failed to get i2c channel", __func__);
return ZX_ERR_INTERNAL;
}
auto device = std::make_unique<SyBuck>(parent, std::move(i2c));
st = device->Init();
if (st != ZX_OK) {
zxlogf(ERROR, "%s: Failed to init device, st = %u", __func__, st);
return st;
}
zx_device_prop_t props[] = {
{BIND_I2C_BUS_ID, 0, channel.bus_id},
{BIND_I2C_ADDRESS, 0, channel.address},
};
st = device->DdkAdd(ddk::DeviceAddArgs("silergy-sy-buck").set_props(props));
if (st != ZX_OK) {
zxlogf(ERROR, "%s: DdkAdd failed, st = %d", __func__, st);
return st;
}
__UNUSED auto ptr = device.release();
return st;
}
} // namespace silergy
static constexpr zx_driver_ops_t sy_buck_driver_ops = []() {
zx_driver_ops_t ops = {};
ops.version = DRIVER_OPS_VERSION;
ops.bind = silergy::SyBuck::Create;
return ops;
}();
ZIRCON_DRIVER(sybuck, sy_buck_driver_ops, "zircon", "0.1");