@@ -40,7 +40,10 @@
#define ADM1266_BLACKBOX_INFO 0xE6
#define ADM1266_PDIO_STATUS 0xE9
#define ADM1266_GPIO_STATUS 0xEA
+#define ADM1266_STATUS_MFR_2 0xED
+#define ADM1266_REFRESH_FLASH 0xF5
#define ADM1266_MEMORY_CONFIG 0xF8
+#define ADM1266_MEMORY_CRC 0xF9
#define ADM1266_SWITCH_MEMORY 0xFA
#define ADM1266_UPDATE_FW 0xFC
#define ADM1266_FW_PASSWORD 0xFD
@@ -66,6 +69,11 @@
/* ADM1266 STATUS_MFR defines */
#define ADM1266_STATUS_PART_LOCKED(x) FIELD_GET(BIT(2), x)
+#define ADM1266_RUNNING_REFRESH(x) FIELD_GET(BIT(3), x)
+#define ADM1266_ALL_CRC_FAULT(x) FIELD_GET(BIT(5), x)
+
+/* ADM1266 STATUS_MFR_2 defines */
+#define ADM1266_MAIN_CONFIG_FAULT(x) FIELD_GET(GENMASK(9, 8), x)
/* ADM1266 GO_COMMAND defines */
#define ADM1266_GO_COMMAND_STOP BIT(0)
@@ -74,6 +82,8 @@
#define ADM1266_FIRMWARE_OFFSET 0x00000
#define ADM1266_FIRMWARE_SIZE 131072
+#define ADM1266_CONFIG_OFFSET 0x30000
+#define ADM1266_CONFIG_SIZE 131072
#define ADM1266_BLACKBOX_OFFSET 0x7F700
#define ADM1266_BLACKBOX_SIZE 64
@@ -117,6 +127,11 @@ static const struct nvmem_cell_info adm1266_nvmem_cells[] = {
.offset = ADM1266_FIRMWARE_OFFSET,
.bytes = ADM1266_FIRMWARE_SIZE,
},
+ {
+ .name = "configuration",
+ .offset = ADM1266_CONFIG_OFFSET,
+ .bytes = ADM1266_CONFIG_SIZE,
+ },
};
DECLARE_CRC8_TABLE(pmbus_crc_table);
@@ -520,6 +535,9 @@ static int adm1266_read_mem_cell(struct adm1266_data *data, const struct nvmem_c
case ADM1266_FIRMWARE_OFFSET:
/* firmware is write-only */
return 0;
+ case ADM1266_CONFIG_OFFSET:
+ /* configuration is write-only */
+ return 0;
default:
return -EINVAL;
}
@@ -676,6 +694,7 @@ static int adm1266_write_hex(struct adm1266_data *data,
u8 first_writes[7];
u8 byte_count;
u8 reg_address;
+ bool to_slaves = false;
int ret;
int i;
@@ -706,7 +725,10 @@ static int adm1266_write_hex(struct adm1266_data *data,
if (ret < 0)
return ret;
- ret = adm1266_group_cmd(data, reg_address, write_buf, byte_count, true);
+ if (offset == ADM1266_FIRMWARE_OFFSET)
+ to_slaves = true;
+
+ ret = adm1266_group_cmd(data, reg_address, write_buf, byte_count, to_slaves);
if (ret < 0) {
dev_err(&data->client->dev, "Firmware write error: %d.", ret);
return ret;
@@ -731,6 +753,87 @@ static int adm1266_write_hex(struct adm1266_data *data,
return 0;
}
+static int adm1266_verify_memory(struct adm1266_data *data)
+{
+ char cmd[2];
+ int ret;
+ int reg;
+
+ cmd[0] = 0x1;
+ cmd[1] = 0x0;
+ ret = adm1266_group_cmd(data, ADM1266_MEMORY_CRC, cmd,
+ sizeof(cmd), true);
+ if (ret < 0)
+ return ret;
+
+ /* after issuing a memory recalculate crc command, wait 1000 ms */
+ msleep(1000);
+
+ reg = pmbus_read_word_data(data->client, 0, 0xFF, ADM1266_STATUS_MFR_2);
+ if (reg < 0)
+ return reg;
+
+ if (ADM1266_MAIN_CONFIG_FAULT(reg)) {
+ dev_err(&data->client->dev, "Main memory corrupted.");
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static int adm1266_refresh_memory(struct adm1266_data *data)
+{
+ unsigned int timeout = 9000;
+ int ret;
+ u8 cmd[2];
+
+ cmd[0] = 0x2;
+ ret = adm1266_group_cmd(data, ADM1266_REFRESH_FLASH, cmd, 1, true);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Could not refresh flash.");
+ return ret;
+ }
+
+ /* after issuing a refresh flash command, wait 9000 ms */
+ msleep(9000);
+
+ do {
+ msleep(1000);
+ timeout -= 1000;
+
+ ret = pmbus_read_byte_data(data->client, 0, ADM1266_STATUS_MFR);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Could not read status.");
+ return ret;
+ }
+
+ } while (ADM1266_RUNNING_REFRESH(ret) && timeout > 0);
+
+ if (timeout == 0)
+ return -ETIMEDOUT;
+
+ cmd[0] = 0x1;
+ cmd[1] = 0x0;
+ ret = adm1266_group_cmd(data, ADM1266_MEMORY_CRC, cmd,
+ sizeof(cmd), true);
+ if (ret < 0)
+ return ret;
+
+ /* after issuing a memory recalculate crc command, wait 1000 ms */
+ msleep(1000);
+
+ ret = pmbus_read_byte_data(data->client, 0, ADM1266_STATUS_MFR);
+ if (ret < 0)
+ return ret;
+
+ if (ADM1266_ALL_CRC_FAULT(ret)) {
+ dev_err(&data->client->dev, "CRC checks failed.");
+ return ret;
+ }
+
+ return 0;
+}
+
static int adm1266_program_firmware(struct adm1266_data *data)
{
u8 write_data[3];
@@ -783,6 +886,77 @@ static int adm1266_program_firmware(struct adm1266_data *data)
return ret;
}
+static int adm1266_program_config(struct adm1266_data *data)
+{
+ u8 cmd[2];
+ u8 value;
+ int ret;
+
+ value = ADM1266_GO_COMMAND_STOP | ADM1266_GO_COMMAND_SEQ_RES;
+ ret = pmbus_write_word_data(data->client, 0, ADM1266_GO_COMMAND, value);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Could not stop sequence.");
+ return ret;
+ }
+
+ /* after issuing a stop command, wait 100 ms */
+ msleep(100);
+
+ ret = adm1266_unlock_all_dev(data);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Could not unlock dev.");
+ goto lock_all_devices;
+ }
+
+ value = 0;
+ ret = i2c_smbus_write_block_data(data->client, ADM1266_SWITCH_MEMORY, 1, &value);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Could not switch to main mem.");
+ goto lock_all_devices;
+ }
+
+ /* after issuing a SWITCH_MEMORY command, wait 1000 ms */
+ msleep(1000);
+
+ ret = adm1266_write_hex(data, ADM1266_CONFIG_OFFSET, ADM1266_CONFIG_SIZE);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Could not write configuration.");
+ goto lock_all_devices;
+ }
+
+ ret = pmbus_write_byte(data->client, 0, ADM1266_STORE_USER_ALL);
+ if (ret < 0)
+ return ret;
+
+ /* after issuing a STORE_USER_ALL command, wait 300 ms */
+ msleep(300);
+
+ if (!data->master_dev)
+ goto lock_all_devices;
+
+ ret = adm1266_verify_memory(data);
+ if (ret < 0)
+ goto lock_all_devices;
+
+ cmd[0] = 0;
+ cmd[1] = 0;
+ ret = adm1266_group_cmd(data, ADM1266_GO_COMMAND, cmd, sizeof(cmd), true);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Could not restart sequence.");
+ goto lock_all_devices;
+ }
+
+ /* after issuing a restart sequence command, wait 350 ms */
+ msleep(350);
+
+ ret = adm1266_refresh_memory(data);
+
+lock_all_devices:
+ adm1266_lock_all_dev(data);
+
+ return ret;
+}
+
/* check if firmware/config write has ended */
static bool adm1266_check_ending(struct adm1266_data *data, unsigned int offset,
unsigned int size)
@@ -824,6 +998,9 @@ static int adm1266_write_mem_cell(struct adm1266_data *data,
program_func = &adm1266_program_firmware;
break;
+ case ADM1266_CONFIG_OFFSET:
+ program_func = &adm1266_program_config;
+ break;
default:
return -EINVAL;
}