Re: [PATCH v7 8/9] hwmon: pmbus: adm1266: program configuration
From: Guenter Roeck
Date: Fri Aug 07 2020 - 11:44:36 EST
On Mon, Jul 27, 2020 at 07:19:27PM +0300, alexandru.tachici@xxxxxxxxxx wrote:
> From: Alexandru Tachici <alexandru.tachici@xxxxxxxxxx>
>
> Writing the configuration Intel hex file to the nvmem,
> of an adm1266, with offset 0x30000, will now
> trigger the configuration programming.
>
> During this process the adm1266 sequencer will be
> stopped and at the end will be issued a seq reset
> (see AN-1453 Programming the configuration).
>
Same as writing the firmware: This should be done from userspace,
using i2c-dev, in a controlled environment (eg manufacturing).
It can easily brick the hardware, and should not be done in the driver.
Thanks,
Guenter
> Signed-off-by: Alexandru Tachici <alexandru.tachici@xxxxxxxxxx>
> ---
> drivers/hwmon/pmbus/adm1266.c | 179 +++++++++++++++++++++++++++++++++-
> 1 file changed, 178 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c
> index f851c6617870..50386c98d714 100644
> --- a/drivers/hwmon/pmbus/adm1266.c
> +++ b/drivers/hwmon/pmbus/adm1266.c
> @@ -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;
> }