blob: fbd4544c10cd03fcf8d471064e1527735fcc5ed1 [file] [log] [blame]
// Copyright 2021 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 "ft_firmware.h"
#include "ft_device.h"
namespace {
constexpr uint8_t kFlashStatusReg = 0x6a;
constexpr uint16_t kFlashEccDone = 0xf055;
constexpr uint16_t kFlashEraseDone = 0xf0aa;
constexpr uint8_t kFirmwareEccReg = 0x66;
constexpr uint8_t kBootIdReg = 0x90;
constexpr int kGetBootIdRetries = 10;
constexpr zx::duration kBootIdWaitAfterUnlock = zx::msec(12);
constexpr uint16_t kRombootId = 0x582c;
constexpr uint8_t kChipCoreReg = 0xa3;
constexpr int kGetChipCoreRetries = 6;
constexpr uint8_t kChipCoreFirmwareValid = 0x58;
constexpr uint8_t kFirmwareVersionReg = 0xa6;
constexpr uint8_t kWorkModeReg = 0xfc;
constexpr uint8_t kWorkModeSoftwareReset1 = 0xaa;
constexpr uint8_t kWorkModeSoftwareReset2 = 0x55;
constexpr uint8_t kHidToStdReg = 0xeb;
constexpr uint16_t kHidToStdValue = 0xaa09;
// Commands and parameters
constexpr uint8_t kResetCommand = 0x07;
constexpr zx::duration kResetWait = zx::msec(400);
constexpr uint8_t kFlashEraseCommand = 0x09;
constexpr uint8_t kFlashEraseAppArea = 0x0b;
constexpr uint8_t kUnlockBootCommand = 0x55;
constexpr uint8_t kStartEraseCommand = 0x61;
constexpr zx::duration kEraseWait = zx::msec(1350);
constexpr uint8_t kEccInitializationCommand = 0x64;
constexpr uint8_t kEccCalculateCommand = 0x65;
constexpr uint8_t kFirmwarePacketCommand = 0xbf;
constexpr uint8_t kSetEraseSizeCommand = 0xb0;
// Firmware download
constexpr int kFirmwareDownloadRetries = 2;
constexpr size_t kFirmwareMinSize = 0x120;
constexpr size_t kFirmwareMaxSize = 64l * 1024;
constexpr size_t kFirmwareVersionOffset = 0x10a;
constexpr size_t kMaxPacketAddress = 0x00ff'ffff;
constexpr size_t kMaxPacketSize = 128;
constexpr size_t kMaxEraseSize = 0xfffe;
constexpr zx::duration CalculateEccSleep(const size_t check_size) {
return zx::msec(static_cast<ssize_t>(check_size) / 256);
}
constexpr uint16_t ExpectedWriteStatus(const uint32_t address, const size_t packet_size) {
return (0x1000 + (address / packet_size)) & 0xffff;
}
} // namespace
namespace ft {
uint8_t FtDevice::CalculateEcc(std::span<const uint8_t> buffer, uint8_t initial) {
for (const uint8_t byte : buffer) {
initial ^= byte;
}
return initial;
}
zx_status_t FtDevice::UpdateFirmwareIfNeeded(
const fuchsia_hardware_input_focaltech::Metadata& metadata, display::PanelType panel_type) {
if (!metadata.needs_firmware()) {
return ZX_OK;
}
cpp20::span<const uint8_t> firmware;
const cpp20::span<const FirmwareEntry> entries(kFirmwareEntries, kNumFirmwareEntries);
for (const auto& entry : entries) {
if (entry.panel_type == panel_type) {
firmware = cpp20::span(entry.firmware_data, entry.firmware_size);
break;
}
}
if (firmware.empty()) {
fdf::error("No firmware found for panel type {}", static_cast<uint32_t>(panel_type));
return ZX_OK;
}
if (firmware.size() < kFirmwareMinSize) {
fdf::error("Firmware binary is too small: {}", firmware.size());
return ZX_ERR_WRONG_TYPE;
}
if (firmware.size() > kFirmwareMaxSize) {
fdf::error("Firmware binary is too big: {}", firmware.size());
return ZX_ERR_WRONG_TYPE;
}
const uint8_t firmware_version = firmware[kFirmwareVersionOffset];
for (int i = 0; i < kFirmwareDownloadRetries; i++) {
const zx::result<bool> firmware_status = CheckFirmwareAndStartRomboot(firmware_version);
if (firmware_status.is_error()) {
fdf::warn("Failed to check firmware and start romboot: {}", firmware_status);
zx::result _ = Write8(kResetCommand);
continue;
}
if (!firmware_status.value()) {
return ZX_OK;
}
if (const zx::result result = EraseFlash(firmware.size()); result.is_error()) {
fdf::warn("Failed to erase flash: {}", result);
zx::result _ = Write8(kResetCommand);
continue;
}
if (const zx::result result = SendFirmware(firmware); result.is_error()) {
fdf::warn("Failed to send firmware: {}", result);
zx::result _ = Write8(kResetCommand);
continue;
}
if (const zx::result result = Write8(kResetCommand); result.is_error()) {
continue;
}
zx::nanosleep(zx::deadline_after(kResetWait));
fdf::info("Firmware download completed");
return ZX_OK;
}
fdf::error("Failed to update firmware");
return ZX_ERR_INTERNAL;
}
zx::result<bool> FtDevice::CheckFirmwareAndStartRomboot(const uint8_t firmware_version) {
bool firmware_valid = false;
for (int i = 0; i < kGetChipCoreRetries; i++) {
const zx::result<uint8_t> chip_core = ReadReg8(kChipCoreReg);
if (chip_core.is_ok() && chip_core.value() == kChipCoreFirmwareValid) {
firmware_valid = true;
break;
}
zx::nanosleep(zx::deadline_after(zx::msec(200)));
}
if (!firmware_valid) {
// Firmware is invalid, the chip must already be in romboot.
return zx::ok(true);
}
const zx::result<uint8_t> current_firmware_version = ReadReg8(kFirmwareVersionReg);
if (current_firmware_version.is_ok() && current_firmware_version.value() == firmware_version) {
// Firmware is valid and the version matches what the driver has, no need to update.
fdf::info("Firmware version is current, skipping download");
return zx::ok(false);
}
if (current_firmware_version.is_ok()) {
fdf::info("Chip firmware ({:#02x}) doesn't match our version ({:#02x}), starting download",
current_firmware_version.value(), firmware_version);
} else {
fdf::warn("Failed to read chip firmware version, starting download");
}
zx_status_t status;
if (zx::result result = StartRomboot(); result.is_error()) {
fdf::error("Failed to start romboot: {}", result);
return result.take_error();
}
if ((status = WaitForRomboot()) != ZX_OK) {
return zx::error_result(status);
}
return zx::ok(true);
}
zx::result<> FtDevice::StartRomboot() {
if (zx::result result = WriteReg8(kWorkModeReg, kWorkModeSoftwareReset1); result.is_error()) {
return result.take_error();
}
zx::nanosleep(zx::deadline_after(zx::msec(10)));
if (zx::result result = WriteReg8(kWorkModeReg, kWorkModeSoftwareReset2); result.is_error()) {
return result.take_error();
}
zx::nanosleep(zx::deadline_after(zx::msec(80)));
return zx::ok();
}
zx_status_t FtDevice::WaitForRomboot() {
zx::result<uint16_t> boot_id;
for (int i = 0; i < kGetBootIdRetries; i++) {
boot_id = GetBootId();
if (boot_id.is_ok() && boot_id.value() == kRombootId) {
return ZX_OK;
}
}
if (boot_id.is_error()) {
return boot_id.error_value();
}
if (boot_id.value() != kRombootId) {
fdf::error("Timed out waiting for boot ID {:#04x}, got {:#04x}", kRombootId, boot_id.value());
return ZX_ERR_TIMED_OUT;
}
return ZX_OK;
}
zx::result<uint16_t> FtDevice::GetBootId() {
zx::result _ = WriteReg16(kHidToStdReg, kHidToStdValue);
if (zx::result result = Write8(kUnlockBootCommand); result.is_error()) {
fdf::error("Failed to send unlock command: {}", result);
return result.take_error();
}
zx::nanosleep(zx::deadline_after(kBootIdWaitAfterUnlock));
return ReadReg16(kBootIdReg);
}
zx::result<bool> FtDevice::WaitForFlashStatus(const uint16_t expected_value, const int tries,
const zx::duration retry_sleep) {
zx::result<uint16_t> value;
for (int i = 0; i < tries; i++) {
value = ReadReg16(kFlashStatusReg);
if (value.is_ok() && value.value() == expected_value) {
return zx::ok(true);
}
zx::nanosleep(zx::deadline_after(retry_sleep));
}
if (value.is_error()) {
return zx::error(value.error_value());
}
return zx::ok(false);
}
zx::result<> FtDevice::SendFirmwarePacket(const uint32_t address, std::span<const uint8_t> packet) {
constexpr size_t kPacketHeaderSize = 1 + 3 + 2; // command + address + length
if (address > kMaxPacketAddress) {
fdf::error("Packet address {:#08x} is too large: Max packet address is {:#08x}", address,
kMaxPacketAddress);
return zx::error(ZX_ERR_INVALID_ARGS);
}
if (packet.size() > kMaxPacketSize) {
fdf::error("Packet size of {} bytes is too large: Max packet size is {} bytes", packet.size(),
kMaxPacketSize);
return zx::error(ZX_ERR_INVALID_ARGS);
}
std::array<uint8_t, kPacketHeaderSize + kMaxPacketSize> packet_buffer = {
kFirmwarePacketCommand,
static_cast<uint8_t>((address >> 16) & 0xff),
static_cast<uint8_t>((address >> 8) & 0xff),
static_cast<uint8_t>(address & 0xff),
static_cast<uint8_t>((packet.size() >> 8) & 0xff),
static_cast<uint8_t>(packet.size() & 0xff),
};
memcpy(packet_buffer.data() + kPacketHeaderSize, packet.data(), packet.size());
zx::result result =
i2c_.WriteSync(std::span(packet_buffer.begin(), kPacketHeaderSize + packet.size()));
if (result.is_error()) {
fdf::error("Failed to write {} bytes to {:#06x}: {}", packet.size(), address, result);
return result.take_error();
}
return zx::ok();
}
zx::result<> FtDevice::EraseFlash(const size_t size) {
if (zx::result result = WriteReg8(kFlashEraseCommand, kFlashEraseAppArea); result.is_error()) {
return result.take_error();
}
std::array<uint8_t, 4> erase_size_buffer;
erase_size_buffer[0] = kSetEraseSizeCommand;
erase_size_buffer[1] = (size >> 16) & 0xff;
erase_size_buffer[2] = (size >> 8) & 0xff;
erase_size_buffer[3] = size & 0xff;
if (zx::result result = i2c_.WriteSync(erase_size_buffer); result.is_error()) {
fdf::error("Failed to write erase size: {}", result);
return result.take_error();
}
if (zx::result result = Write8(kStartEraseCommand); result.is_error()) {
return result.take_error();
}
zx::nanosleep(zx::deadline_after(kEraseWait));
zx::result<bool> erase_done = WaitForFlashStatus(kFlashEraseDone, 50, zx::msec(400));
if (erase_done.is_error()) {
return erase_done.take_error();
}
if (!erase_done.value()) {
fdf::error("Timed out waiting for flash erase");
return zx::error(ZX_ERR_TIMED_OUT);
}
return zx::ok();
}
zx::result<> FtDevice::SendFirmware(cpp20::span<const uint8_t> firmware) {
size_t offset = 0;
uint8_t expected_ecc = 0;
while (offset < firmware.size()) {
const size_t remaining = firmware.size() - offset;
const size_t send_size = std::min(kMaxPacketSize, remaining);
const uint32_t address = static_cast<uint32_t>(offset);
zx::result result = SendFirmwarePacket(address, firmware.subspan(offset, send_size));
if (result.is_error()) {
fdf::error("Failed to send firmware packet: {}", result);
return result.take_error();
}
zx::nanosleep(zx::deadline_after(zx::msec(1)));
const uint16_t expected_status = ExpectedWriteStatus(address, send_size);
zx::result<bool> write_done = WaitForFlashStatus(expected_status, 100, zx::msec(1));
if (write_done.is_error()) {
return write_done.take_error();
}
if (!write_done.value()) {
fdf::warn("Timed out waiting for correct flash write status");
}
expected_ecc = CalculateEcc(firmware.subspan(offset, send_size), expected_ecc);
offset += send_size;
}
zx::result result = CheckFirmwareEcc(firmware.size(), expected_ecc);
if (result.is_error()) {
fdf::error("Failed to check firmware ecc: {}", result);
return result.take_error();
}
return zx::ok();
}
zx::result<> FtDevice::CheckFirmwareEcc(const size_t size, const uint8_t expected_ecc) {
if (zx::result result = Write8(kEccInitializationCommand); result.is_error()) {
return result.take_error();
}
size_t address = 0;
for (size_t bytes_remaining = size; bytes_remaining > 0;) {
const size_t check_size = std::min<size_t>(kMaxEraseSize, bytes_remaining);
const std::array<uint8_t, 6> check_buffer = {
kEccCalculateCommand,
static_cast<uint8_t>((address >> 16) & 0xff),
static_cast<uint8_t>((address >> 8) & 0xff),
static_cast<uint8_t>(address & 0xff),
static_cast<uint8_t>((check_size >> 8) & 0xff),
static_cast<uint8_t>(check_size & 0xff),
};
zx::result result = i2c_.WriteSync(check_buffer);
if (result.is_error()) {
fdf::error("Failed to send ECC calculate command: {}", result);
return result.take_error();
}
zx::nanosleep(zx::deadline_after(CalculateEccSleep(check_size)));
zx::result<bool> ecc_done = WaitForFlashStatus(kFlashEccDone, 10, zx::msec(50));
if (ecc_done.is_error()) {
return ecc_done.take_error();
}
if (!ecc_done.value()) {
fdf::error("Timed out waiting for ECC calculation");
return zx::error(ZX_ERR_TIMED_OUT);
}
bytes_remaining -= check_size;
address += check_size;
}
zx::result<uint8_t> ecc = ReadReg8(kFirmwareEccReg);
if (ecc.is_error()) {
return ecc.take_error();
}
if (ecc.value() != expected_ecc) {
fdf::error("Firmware ECC mismatch, got {:#02x}, expected {:#02x}", ecc.value(), expected_ecc);
return zx::error(ZX_ERR_IO_DATA_LOSS);
}
return zx::ok();
}
zx::result<uint8_t> FtDevice::ReadReg8(const uint8_t address) {
std::array<uint8_t, 1> value;
zx::result result = i2c_.ReadSync(address, value);
if (result.is_error()) {
fdf::error("Failed to read from {:#02x}: {}", address, result);
return result.take_error();
}
return zx::ok(value[0]);
}
zx::result<uint16_t> FtDevice::ReadReg16(const uint8_t address) {
std::array<uint8_t, 2> buffer;
zx::result result = i2c_.ReadSync(address, buffer);
if (result.is_error()) {
fdf::error("Failed to read from {:#02x}: {}", address, result);
return result.take_error();
}
return zx::ok(static_cast<uint16_t>((buffer[0] << 8) | buffer[1]));
}
zx::result<> FtDevice::Write8(const uint8_t value) {
const std::array<uint8_t, 1> write_data = {value};
zx::result result = i2c_.WriteSync(write_data);
if (result.is_error()) {
fdf::error("Failed to write {:#02x}: {}", value, result);
return result.take_error();
}
return zx::ok();
}
zx::result<> FtDevice::WriteReg8(const uint8_t address, const uint8_t value) {
const std::array<uint8_t, 2> write_data = {address, value};
zx::result result = i2c_.WriteSync(write_data);
if (result.is_error()) {
fdf::error("Failed to write {:#02x} to {:#02x}: {}", value, address, result);
return result.take_error();
}
return zx::ok();
}
zx::result<> FtDevice::WriteReg16(const uint8_t address, const uint16_t value) {
const std::array<uint8_t, 3> write_data = {
address,
static_cast<uint8_t>((value >> 8) & 0xff),
static_cast<uint8_t>(value & 0xff),
};
zx::result result = i2c_.WriteSync(write_data);
if (result.is_error()) {
fdf::error("Failed to write {:#04x} to {:#02x}: {}", value, address, result);
return result.take_error();
}
return zx::ok();
}
} // namespace ft