On Mon, Jul 27, 2020 at 07:19:27PM +0300, alexandru.tach...@analog.com wrote:
> From: Alexandru Tachici <alexandru.tach...@analog.com>
> 
> 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.tach...@analog.com>
> ---
>  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;
>       }

Reply via email to