blob: 0567ac9c9243911bd60a3ccb82ca5e561a39f5e5 [file] [log] [blame]
Faisal Awada5a582d32024-11-15 14:11:44 -06001/**
2 * Copyright © 2024 IBM Corporation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "config.h"
18
19#include "aei_updater.hpp"
20
21#include "pmbus.hpp"
22#include "types.hpp"
23#include "updater.hpp"
24#include "utility.hpp"
25
26#include <phosphor-logging/lg2.hpp>
27
28namespace aeiUpdater
29{
30constexpr uint8_t MAX_RETRIES = 0x02; // Constants for retry limits
31
32constexpr int ISP_STATUS_DELAY = 1200; // Delay for ISP status check (1.2s)
33constexpr int MEM_WRITE_DELAY = 5000; // Memory write delay (5s)
34constexpr int MEM_STRETCH_DELAY = 50; // Delay between writes (50ms)
35constexpr int MEM_COMPLETE_DELAY = 2000; // Delay before completion (2s)
36constexpr int REBOOT_DELAY = 8000; // Delay for reboot (8s)
37
38constexpr uint8_t I2C_SMBUS_BLOCK_MAX = 0x20; // Max Read bytes from PSU
39constexpr uint8_t FW_READ_BLOCK_SIZE = 0x20; // Read bytes from FW file
40constexpr uint8_t BLOCK_WRITE_SIZE = 0x25; // I2C block write size
41constexpr uint8_t READ_SEQ_ST_CML_SIZE = 0x6; // Read sequence and status CML
42 // size
43constexpr uint8_t START_SEQUENCE_INDEX = 0x1; // Starting sequence index
44constexpr uint8_t STATUS_CML_INDEX = 0x5; // Status CML read index
45
46// Register addresses for commands.
47constexpr uint8_t KEY_REGISTER = 0xF6; // Key register
48constexpr uint8_t STATUS_REGISTER = 0xF7; // Status register
49constexpr uint8_t ISP_MEMORY_REGISTER = 0xF9; // ISP memory register
50
51// Define AEI ISP status register commands
52constexpr uint8_t CMD_CLEAR_STATUS = 0x0; // Clear the status register
53constexpr uint8_t CMD_RESET_SEQ = 0x01; // This command will reset ISP OS for
54 // another attempt of a sequential
55 // programming operation.
56constexpr uint8_t CMD_BOOT_ISP = 0x02; // Boot the In-System Programming System.
57constexpr uint8_t CMD_BOOT_PWR = 0x03; // Attempt to boot the Power Management
58 // OS.
59
60// Define AEI ISP response status bit
61constexpr uint8_t B_CHKSUM_ERR = 0x0; // The checksum verification unsuccessful
62constexpr uint8_t B_CHKSUM_SUCCESS = 0x1; // The checksum verification
63 // successful.
64constexpr uint8_t B_MEM_ERR = 0x2; // Memory boundry error indication.
65constexpr uint8_t B_ALIGN_ERR = 0x4; // Address error indication.
66constexpr uint8_t B_KEY_ERR = 0x8; // Invalid Key
67constexpr uint8_t B_START_ERR = 0x10; // Error indicator set at startup.
68constexpr uint8_t B_IMG_MISSMATCH_ERR = 0x20; // Firmware image does not match
69 // PSU
70constexpr uint8_t B_ISP_MODE = 0x40; // ISP mode
71constexpr uint8_t B_ISP_MODE_CHKSUM_GOOD = 0x41; // ISP mode & good checksum.
72constexpr uint8_t B_PRGM_BUSY = 0x80; // Write operation in progress.
73
74using namespace phosphor::logging;
75namespace util = phosphor::power::util;
76
77int AeiUpdater::doUpdate()
78{
79 i2cInterface = Updater::getI2C();
80 if (i2cInterface == nullptr)
81 {
82 throw std::runtime_error("I2C interface error");
83 }
84 bool cleanFailedIspMode = false; // Flag to prevent download and continue to
85 // restore the PSU to it's original state.
86
87 // Set ISP mode by writing necessary keys and resetting ISP status
88 if (!writeIspKey() || !writeIspMode() || !writeIspStatusReset())
89 {
90 lg2::error("Failed to set ISP key or mode or reset ISP status");
91 cleanFailedIspMode = true;
92 }
93
94 if (cleanFailedIspMode)
95 {
96 return 1;
97 }
98 return 0; // Update successful.
99}
100
101bool AeiUpdater::writeIspKey()
102{
103 // ISP Key to unlock programming mode ( ASCII for "artY").
104 constexpr std::array<uint8_t, 4> unlockData = {0x61, 0x72, 0x74,
105 0x59}; // ISP Key "artY"
106 try
107 {
108 // Send ISP Key to unlock device for firmware update
109 i2cInterface->write(KEY_REGISTER, unlockData.size(), unlockData.data());
110 return true;
111 }
112 catch (const std::exception& e)
113 {
114 // Log failure if I2C write fails.
115 lg2::error("I2C write failed: {ERROR}", "ERROR", e.what());
116 return false;
117 }
118}
119
120bool AeiUpdater::writeIspMode()
121{
122 // Attempt to set device in ISP mode with retries.
123 uint8_t ispStatus = 0x0;
124 for (int retry = 0; retry < MAX_RETRIES; ++retry)
125 {
126 try
127 {
128 // Write command to enter ISP mode.
129 i2cInterface->write(STATUS_REGISTER, CMD_BOOT_ISP);
130 // Delay to allow status register update.
131 updater::internal::delay(ISP_STATUS_DELAY);
132 // Read back status register to confirm ISP mode is active.
133 i2cInterface->read(STATUS_REGISTER, ispStatus);
134
135 if (ispStatus & B_ISP_MODE)
136 {
137 return true;
138 }
139 }
140 catch (const std::exception& e)
141 {
142 // Log I2C error with each retry attempt.
143 lg2::error("I2C error during ISP mode write/read: {ERROR}", "ERROR",
144 e.what());
145 }
146 }
147 lg2::error("Failed to set ISP Mode");
148 return false; // Failed to set ISP Mode after retries
149}
150
151bool AeiUpdater::writeIspStatusReset()
152{
153 // Reset ISP status register before firmware download.
154 uint8_t ispStatus = 0;
155 try
156 {
157 i2cInterface->write(STATUS_REGISTER,
158 CMD_RESET_SEQ); // Start reset sequence.
159 for (int retry = 0; retry < MAX_RETRIES; ++retry)
160 {
161 i2cInterface->read(STATUS_REGISTER, ispStatus);
162 if (ispStatus == B_ISP_MODE)
163 {
164 return true; // ISP status reset successfully.
165 }
166 i2cInterface->write(STATUS_REGISTER,
167 CMD_CLEAR_STATUS); // Clear status if
168 // not reset.
169 }
170 }
171 catch (const std::exception& e)
172 {
173 // Log any errors encountered during reset sequence.
174 lg2::error("I2C Read/Write error during ISP reset: {ERROR}", "ERROR",
175 e.what());
176 }
177 lg2::error("Failed to reset ISP Status");
178 return false;
179}
180
181} // namespace aeiUpdater