Commit 4cf1aab4 authored by Guenter Roeck's avatar Guenter Roeck
Browse files

hwmon: (spd5118) Split into common and I2C specific code



Split spd5118 driver into common and I2C specific code to enable
adding support for I3C.

Signed-off-by: default avatarGuenter Roeck <linux@roeck-us.net>
parent e799657a
Loading
Loading
Loading
Loading
+143 −134
Original line number Diff line number Diff line
@@ -305,51 +305,6 @@ static bool spd5118_vendor_valid(u8 bank, u8 id)
	return id && id != 0x7f;
}

/* Return 0 if detection is successful, -ENODEV otherwise */
static int spd5118_detect(struct i2c_client *client, struct i2c_board_info *info)
{
	struct i2c_adapter *adapter = client->adapter;
	int regval;

	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
				     I2C_FUNC_SMBUS_WORD_DATA))
		return -ENODEV;

	regval = i2c_smbus_read_word_swapped(client, SPD5118_REG_TYPE);
	if (regval != 0x5118)
		return -ENODEV;

	regval = i2c_smbus_read_word_data(client, SPD5118_REG_VENDOR);
	if (regval < 0 || !spd5118_vendor_valid(regval & 0xff, regval >> 8))
		return -ENODEV;

	regval = i2c_smbus_read_byte_data(client, SPD5118_REG_CAPABILITY);
	if (regval < 0)
		return -ENODEV;
	if (!(regval & SPD5118_CAP_TS_SUPPORT) || (regval & 0xfc))
		return -ENODEV;

	regval = i2c_smbus_read_byte_data(client, SPD5118_REG_TEMP_CLR);
	if (regval)
		return -ENODEV;
	regval = i2c_smbus_read_byte_data(client, SPD5118_REG_ERROR_CLR);
	if (regval)
		return -ENODEV;

	regval = i2c_smbus_read_byte_data(client, SPD5118_REG_REVISION);
	if (regval < 0 || (regval & 0xc1))
		return -ENODEV;

	regval = i2c_smbus_read_byte_data(client, SPD5118_REG_TEMP_CONFIG);
	if (regval < 0)
		return -ENODEV;
	if (regval & ~SPD5118_TS_DISABLE)
		return -ENODEV;

	strscpy(info->type, "spd5118", I2C_NAME_SIZE);
	return 0;
}

static const struct hwmon_channel_info *spd5118_info[] = {
	HWMON_CHANNEL_INFO(chip,
			   HWMON_C_REGISTER_TZ),
@@ -483,7 +438,7 @@ static bool spd5118_volatile_reg(struct device *dev, unsigned int reg)
	}
}

static const struct regmap_range_cfg spd5118_regmap_range_cfg[] = {
static const struct regmap_range_cfg spd5118_i2c_regmap_range_cfg[] = {
	{
	.selector_reg   = SPD5118_REG_I2C_LEGACY_MODE,
	.selector_mask  = SPD5118_LEGACY_PAGE_MASK,
@@ -495,7 +450,7 @@ static const struct regmap_range_cfg spd5118_regmap_range_cfg[] = {
	},
};

static const struct regmap_config spd5118_regmap_config = {
static const struct regmap_config spd5118_i2c_regmap_config = {
	.reg_bits = 8,
	.val_bits = 8,
	.max_register = 0x7ff,
@@ -503,87 +458,62 @@ static const struct regmap_config spd5118_regmap_config = {
	.volatile_reg = spd5118_volatile_reg,
	.cache_type = REGCACHE_MAPLE,

	.ranges = spd5118_regmap_range_cfg,
	.num_ranges = ARRAY_SIZE(spd5118_regmap_range_cfg),
	.ranges = spd5118_i2c_regmap_range_cfg,
	.num_ranges = ARRAY_SIZE(spd5118_i2c_regmap_range_cfg),
};

static int spd5118_init(struct i2c_client *client)
static int spd5118_suspend(struct device *dev)
{
	struct i2c_adapter *adapter = client->adapter;
	int err, regval, mode;

	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
				     I2C_FUNC_SMBUS_WORD_DATA))
		return -ENODEV;

	regval = i2c_smbus_read_word_swapped(client, SPD5118_REG_TYPE);
	if (regval < 0 || (regval && regval != 0x5118))
		return -ENODEV;
	struct spd5118_data *data = dev_get_drvdata(dev);
	struct regmap *regmap = data->regmap;
	u32 regval;
	int err;

	/*
	 * If the device type registers return 0, it is possible that the chip
	 * has a non-zero page selected and takes the specification literally,
	 * i.e. disables access to volatile registers besides the page register
	 * if the page is not 0. Try to identify such chips.
	 * Make sure the configuration register in the regmap cache is current
	 * before bypassing it.
	 */
	if (!regval) {
		/* Vendor ID registers must also be 0 */
		regval = i2c_smbus_read_word_data(client, SPD5118_REG_VENDOR);
		if (regval)
			return -ENODEV;
	err = regmap_read(regmap, SPD5118_REG_TEMP_CONFIG, &regval);
	if (err < 0)
		return err;

		/* The selected page in MR11 must not be 0 */
		mode = i2c_smbus_read_byte_data(client, SPD5118_REG_I2C_LEGACY_MODE);
		if (mode < 0 || (mode & ~SPD5118_LEGACY_MODE_MASK) ||
		    !(mode & SPD5118_LEGACY_PAGE_MASK))
			return -ENODEV;
	regcache_cache_bypass(regmap, true);
	regmap_update_bits(regmap, SPD5118_REG_TEMP_CONFIG, SPD5118_TS_DISABLE,
			   SPD5118_TS_DISABLE);
	regcache_cache_bypass(regmap, false);

		err = i2c_smbus_write_byte_data(client, SPD5118_REG_I2C_LEGACY_MODE,
						mode & SPD5118_LEGACY_MODE_ADDR);
		if (err)
			return -ENODEV;
	regcache_cache_only(regmap, true);
	regcache_mark_dirty(regmap);

		/*
		 * If the device type registers are still bad after selecting
		 * page 0, this is not a SPD5118 device. Restore original
		 * legacy mode register value and abort.
		 */
		regval = i2c_smbus_read_word_swapped(client, SPD5118_REG_TYPE);
		if (regval != 0x5118) {
			i2c_smbus_write_byte_data(client, SPD5118_REG_I2C_LEGACY_MODE, mode);
			return -ENODEV;
		}
	return 0;
}

	/* We are reasonably sure that this is really a SPD5118 hub controller */
	return 0;
static int spd5118_resume(struct device *dev)
{
	struct spd5118_data *data = dev_get_drvdata(dev);
	struct regmap *regmap = data->regmap;

	regcache_cache_only(regmap, false);
	return regcache_sync(regmap);
}

static int spd5118_probe(struct i2c_client *client)
static DEFINE_SIMPLE_DEV_PM_OPS(spd5118_pm_ops, spd5118_suspend, spd5118_resume);

static int spd5118_common_probe(struct device *dev, struct regmap *regmap)
{
	struct device *dev = &client->dev;
	unsigned int regval, revision, vendor, bank;
	unsigned int capability, revision, vendor, bank;
	struct spd5118_data *data;
	struct device *hwmon_dev;
	struct regmap *regmap;
	int err;

	err = spd5118_init(client);
	if (err)
		return err;

	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
	if (!data)
		return -ENOMEM;

	regmap = devm_regmap_init_i2c(client, &spd5118_regmap_config);
	if (IS_ERR(regmap))
		return dev_err_probe(dev, PTR_ERR(regmap), "regmap init failed\n");

	err = regmap_read(regmap, SPD5118_REG_CAPABILITY, &regval);
	err = regmap_read(regmap, SPD5118_REG_CAPABILITY, &capability);
	if (err)
		return err;
	if (!(regval & SPD5118_CAP_TS_SUPPORT))
	if (!(capability & SPD5118_CAP_TS_SUPPORT))
		return -ENODEV;

	err = regmap_read(regmap, SPD5118_REG_REVISION, &revision);
@@ -627,48 +557,127 @@ static int spd5118_probe(struct i2c_client *client)
	return 0;
}

static int spd5118_suspend(struct device *dev)
/* I2C */

/* Return 0 if detection is successful, -ENODEV otherwise */
static int spd5118_detect(struct i2c_client *client, struct i2c_board_info *info)
{
	struct spd5118_data *data = dev_get_drvdata(dev);
	struct regmap *regmap = data->regmap;
	u32 regval;
	int err;
	struct i2c_adapter *adapter = client->adapter;
	int regval;

	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
				     I2C_FUNC_SMBUS_WORD_DATA))
		return -ENODEV;

	regval = i2c_smbus_read_word_swapped(client, SPD5118_REG_TYPE);
	if (regval != 0x5118)
		return -ENODEV;

	regval = i2c_smbus_read_word_data(client, SPD5118_REG_VENDOR);
	if (regval < 0 || !spd5118_vendor_valid(regval & 0xff, regval >> 8))
		return -ENODEV;

	regval = i2c_smbus_read_byte_data(client, SPD5118_REG_CAPABILITY);
	if (regval < 0)
		return -ENODEV;
	if (!(regval & SPD5118_CAP_TS_SUPPORT) || (regval & 0xfc))
		return -ENODEV;

	regval = i2c_smbus_read_byte_data(client, SPD5118_REG_TEMP_CLR);
	if (regval)
		return -ENODEV;
	regval = i2c_smbus_read_byte_data(client, SPD5118_REG_ERROR_CLR);
	if (regval)
		return -ENODEV;

	regval = i2c_smbus_read_byte_data(client, SPD5118_REG_REVISION);
	if (regval < 0 || (regval & 0xc1))
		return -ENODEV;

	regval = i2c_smbus_read_byte_data(client, SPD5118_REG_TEMP_CONFIG);
	if (regval < 0)
		return -ENODEV;
	if (regval & ~SPD5118_TS_DISABLE)
		return -ENODEV;

	strscpy(info->type, "spd5118", I2C_NAME_SIZE);
	return 0;
}

static int spd5118_i2c_init(struct i2c_client *client)
{
	struct i2c_adapter *adapter = client->adapter;
	int err, regval, mode;

	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
				     I2C_FUNC_SMBUS_WORD_DATA))
		return -ENODEV;

	regval = i2c_smbus_read_word_swapped(client, SPD5118_REG_TYPE);
	if (regval < 0 || (regval && regval != 0x5118))
		return -ENODEV;

	/*
	 * Make sure the configuration register in the regmap cache is current
	 * before bypassing it.
	 * If the device type registers return 0, it is possible that the chip
	 * has a non-zero page selected and takes the specification literally,
	 * i.e. disables access to volatile registers besides the page register
	 * if the page is not 0. Try to identify such chips.
	 */
	err = regmap_read(regmap, SPD5118_REG_TEMP_CONFIG, &regval);
	if (err < 0)
		return err;
	if (!regval) {
		/* Vendor ID registers must also be 0 */
		regval = i2c_smbus_read_word_data(client, SPD5118_REG_VENDOR);
		if (regval)
			return -ENODEV;

	regcache_cache_bypass(regmap, true);
	regmap_update_bits(regmap, SPD5118_REG_TEMP_CONFIG, SPD5118_TS_DISABLE,
			   SPD5118_TS_DISABLE);
	regcache_cache_bypass(regmap, false);
		/* The selected page in MR11 must not be 0 */
		mode = i2c_smbus_read_byte_data(client, SPD5118_REG_I2C_LEGACY_MODE);
		if (mode < 0 || (mode & ~SPD5118_LEGACY_MODE_MASK) ||
		    !(mode & SPD5118_LEGACY_PAGE_MASK))
			return -ENODEV;

	regcache_cache_only(regmap, true);
	regcache_mark_dirty(regmap);
		err = i2c_smbus_write_byte_data(client, SPD5118_REG_I2C_LEGACY_MODE,
						mode & SPD5118_LEGACY_MODE_ADDR);
		if (err)
			return -ENODEV;

		/*
		 * If the device type registers are still bad after selecting
		 * page 0, this is not a SPD5118 device. Restore original
		 * legacy mode register value and abort.
		 */
		regval = i2c_smbus_read_word_swapped(client, SPD5118_REG_TYPE);
		if (regval != 0x5118) {
			i2c_smbus_write_byte_data(client, SPD5118_REG_I2C_LEGACY_MODE, mode);
			return -ENODEV;
		}
	}

	/* We are reasonably sure that this is really a SPD5118 hub controller */
	return 0;
}

static int spd5118_resume(struct device *dev)
static int spd5118_i2c_probe(struct i2c_client *client)
{
	struct spd5118_data *data = dev_get_drvdata(dev);
	struct regmap *regmap = data->regmap;
	struct device *dev = &client->dev;
	struct regmap *regmap;
	int err;

	regcache_cache_only(regmap, false);
	return regcache_sync(regmap);
}
	err = spd5118_i2c_init(client);
	if (err)
		return err;

static DEFINE_SIMPLE_DEV_PM_OPS(spd5118_pm_ops, spd5118_suspend, spd5118_resume);
	regmap = devm_regmap_init_i2c(client, &spd5118_i2c_regmap_config);
	if (IS_ERR(regmap))
		return dev_err_probe(dev, PTR_ERR(regmap), "regmap init failed\n");

	return spd5118_common_probe(dev, regmap);
}

static const struct i2c_device_id spd5118_id[] = {
static const struct i2c_device_id spd5118_i2c_id[] = {
	{ "spd5118" },
	{ }
};
MODULE_DEVICE_TABLE(i2c, spd5118_id);
MODULE_DEVICE_TABLE(i2c, spd5118_i2c_id);

static const struct of_device_id spd5118_of_ids[] = {
	{ .compatible = "jedec,spd5118", },
@@ -676,20 +685,20 @@ static const struct of_device_id spd5118_of_ids[] = {
};
MODULE_DEVICE_TABLE(of, spd5118_of_ids);

static struct i2c_driver spd5118_driver = {
static struct i2c_driver spd5118_i2c_driver = {
	.class		= I2C_CLASS_HWMON,
	.driver = {
		.name	= "spd5118",
		.of_match_table = spd5118_of_ids,
		.pm = pm_sleep_ptr(&spd5118_pm_ops),
	},
	.probe		= spd5118_probe,
	.id_table	= spd5118_id,
	.probe		= spd5118_i2c_probe,
	.id_table	= spd5118_i2c_id,
	.detect		= IS_ENABLED(CONFIG_SENSORS_SPD5118_DETECT) ? spd5118_detect : NULL,
	.address_list	= IS_ENABLED(CONFIG_SENSORS_SPD5118_DETECT) ? normal_i2c : NULL,
};

module_i2c_driver(spd5118_driver);
module_i2c_driver(spd5118_i2c_driver);

MODULE_AUTHOR("René Rebe <rene@exactcode.de>");
MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");