Re: [PATCH v7 8/9] hwmon: pmbus: adm1266: program configuration

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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;
>  	}



[Index of Archives]     [Device Tree Compilter]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux PCI Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Yosemite Backpacking]


  Powered by Linux