static int zl6100_probe()

in pmbus/zl6100.c [318:458]


static int zl6100_probe(struct i2c_client *client)
{
	int ret, i;
	struct zl6100_data *data;
	struct pmbus_driver_info *info;
	u8 device_id[I2C_SMBUS_BLOCK_MAX + 1];
	const struct i2c_device_id *mid;

	if (!i2c_check_functionality(client->adapter,
				     I2C_FUNC_SMBUS_READ_WORD_DATA
				     | I2C_FUNC_SMBUS_READ_BLOCK_DATA))
		return -ENODEV;

	ret = i2c_smbus_read_block_data(client, ZL6100_DEVICE_ID,
					device_id);
	if (ret < 0) {
		dev_err(&client->dev, "Failed to read device ID\n");
		return ret;
	}
	device_id[ret] = '\0';
	dev_info(&client->dev, "Device ID %s\n", device_id);

	mid = NULL;
	for (mid = zl6100_id; mid->name[0]; mid++) {
		if (!strncasecmp(mid->name, device_id, strlen(mid->name)))
			break;
	}
	if (!mid->name[0]) {
		dev_err(&client->dev, "Unsupported device\n");
		return -ENODEV;
	}
	if (strcmp(client->name, mid->name) != 0)
		dev_notice(&client->dev,
			   "Device mismatch: Configured %s, detected %s\n",
			   client->name, mid->name);

	data = devm_kzalloc(&client->dev, sizeof(struct zl6100_data),
			    GFP_KERNEL);
	if (!data)
		return -ENOMEM;

	data->id = mid->driver_data;

	/*
	 * According to information from the chip vendor, all currently
	 * supported chips are known to require a wait time between I2C
	 * accesses.
	 */
	data->delay = delay;

	/*
	 * Since there was a direct I2C device access above, wait before
	 * accessing the chip again.
	 */
	data->access = ktime_get();
	zl6100_wait(data);

	info = &data->info;

	info->pages = 1;
	info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT
	  | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
	  | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT
	  | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;

	/*
	 * ZL2004, ZL8802, ZL9101M, ZL9117M and ZLS4009 support monitoring
	 * an extra voltage (VMON for ZL2004, ZL8802 and ZLS4009,
	 * VDRV for ZL9101M and ZL9117M). Report it as vmon.
	 */
	if (data->id == zl2004 || data->id == zl8802 || data->id == zl9101 ||
	    data->id == zl9117 || data->id == zls4009)
		info->func[0] |= PMBUS_HAVE_VMON | PMBUS_HAVE_STATUS_VMON;

	/*
	 * ZL8802 has two outputs that can be used either independently or in
	 * a current sharing configuration. The driver uses the DDC_CONFIG
	 * register to check if the module is running with independent or
	 * shared outputs. If the module is in shared output mode, only one
	 * output voltage will be reported.
	 */
	if (data->id == zl8802) {
		info->pages = 2;
		info->func[0] |= PMBUS_HAVE_IIN;

		ret = i2c_smbus_read_word_data(client, ZL8802_MFR_DDC_CONFIG);
		if (ret < 0)
			return ret;

		data->access = ktime_get();
		zl6100_wait(data);

		if (ret & ZL8802_MFR_PHASES_MASK)
			info->func[1] |= PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT;
		else
			info->func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
				| PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT;

		for (i = 0; i < 2; i++) {
			ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, i);
			if (ret < 0)
				return ret;

			data->access = ktime_get();
			zl6100_wait(data);

			ret = i2c_smbus_read_word_data(client, ZL8802_MFR_USER_CONFIG);
			if (ret < 0)
				return ret;

			if (ret & ZL8802_MFR_XTEMP_ENABLE_2)
				info->func[i] |= PMBUS_HAVE_TEMP2;

			data->access = ktime_get();
			zl6100_wait(data);
		}
		ret = i2c_smbus_read_word_data(client, ZL8802_MFR_USER_GLOBAL_CONFIG);
		if (ret < 0)
			return ret;

		if (ret & ZL8802_MFR_TMON_ENABLE)
			info->func[0] |= PMBUS_HAVE_TEMP3;
	} else {
		ret = i2c_smbus_read_word_data(client, ZL6100_MFR_CONFIG);
		if (ret < 0)
			return ret;

		if (ret & ZL6100_MFR_XTEMP_ENABLE)
			info->func[0] |= PMBUS_HAVE_TEMP2;
	}

	data->access = ktime_get();
	zl6100_wait(data);

	info->read_word_data = zl6100_read_word_data;
	info->read_byte_data = zl6100_read_byte_data;
	info->write_word_data = zl6100_write_word_data;
	info->write_byte = zl6100_write_byte;

	return pmbus_do_probe(client, info);
}