blob: 9ce09f27ad976fc2cf2b15d044dde170321a96a1 [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 <endian.h>
#include <lib/ddk/debug.h>
#include <lib/fzl/vmo-mapper.h>
#include <safemath/safe_conversions.h>
#include "src/camera/drivers/sensors/imx227/imx227.h"
#include "src/camera/drivers/sensors/imx227/imx227_otp_config.h"
namespace camera {
fpromise::result<zx::vmo, zx_status_t> Imx227Device::OtpRead() {
std::lock_guard guard(lock_);
fzl::VmoMapper mapper;
zx::vmo vmo;
zx_status_t status =
mapper.CreateAndMap(OTP_TOTAL_SIZE, ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, nullptr, &vmo);
if (status != ZX_OK) {
zxlogf(ERROR, "%s: failed to create and map VMO", __func__);
return fpromise::error(status);
}
auto* dest = static_cast<unsigned char*>(mapper.start());
// Endian-flipped OTP start address to read through I2C channel
const uint16_t kPageStartRegister = htobe16(OTP_PAGE_START);
for (uint8_t page_index = 0; page_index < OTP_PAGE_NUM; ++page_index) {
// TODO(nzo): does this check need to be in the loop?
if (!ValidateSensorID()) {
status = ZX_ERR_INTERNAL;
zxlogf(ERROR, "%s: could not read Sensor ID", __func__);
return fpromise::error(status);
}
// Select page to read from and enable OTP page reads
Write8(OTP_PAGE_SELECT, page_index);
Write8(OTP_READ_ENABLE, 1);
// Optional check
auto result = Read8(OTP_ACCESS_STATUS);
if (result.is_error()) {
return fpromise::error(result.error());
}
const auto kReadStatus = result.value();
if (kReadStatus != 1) {
status = ZX_ERR_IO;
zxlogf(ERROR, "%s: read access could not be verified, access is %x", __func__, kReadStatus);
return fpromise::error(status);
}
status = i2c_.WriteReadSync(reinterpret_cast<const uint8_t*>(&kPageStartRegister),
sizeof(kPageStartRegister), dest, OTP_PAGE_SIZE);
if (status != ZX_OK) {
zxlogf(ERROR, "%s: failed to read from I2C channel", __func__);
return fpromise::error(status);
}
dest += OTP_PAGE_SIZE;
}
mapper.Unmap();
return fpromise::ok(std::move(vmo));
}
bool Imx227Device::OtpValidate(const zx::vmo& vmo) {
std::array<uint8_t, OTP_TOTAL_SIZE> data;
vmo.read(&data, 0, OTP_TOTAL_SIZE);
const auto kId = data[0];
const auto kDay = data[1];
const auto kMonth = data[2] & 0x0F;
// Year starts from 2018 and is incremented by the stored value
const auto kYear = ((data[2] & 0xF0) >> 4) + 2018;
const auto kFactory = data[3];
uint32_t checksum = 0;
uint16_t checksum_target =
safemath::checked_cast<uint16_t>((data[OTP_CHECKSUM_HIGH_START] << CHAR_BIT)) |
(data[OTP_CHECKSUM_LOW_START]);
for (auto i = 0; i < OTP_CHECKSUM_HIGH_START; ++i) {
checksum += data[i];
}
// Only lower two bytes are counted
const uint16_t kChecksumMask = 0xFFFF;
checksum &= kChecksumMask;
if (checksum != checksum_target) {
zxlogf(ERROR, "%s: checksum validation failed. Expected 0x%x, calculated 0x%x", __func__,
checksum_target, checksum);
return false;
}
zxlogf(INFO, "%s: ID %d, built on %d-%d-%d (mm-dd-yyyy), in factory %x", __func__, kId, kMonth,
kDay, kYear, kFactory);
return true;
}
} // namespace camera