Commit 2b3ed701 authored by Ricardo Rivera-Matos's avatar Ricardo Rivera-Matos Committed by Carles Cufi
Browse files

regulator: cp9314: Adds support for HW I2C Lock feature



Adds support for the control port write-lock feature introduced in
revision B1. The write-lock feature minimizes the risk of spurious
I2C writes flipping sensitive bits during device operation by blocking
writes to some or all of the register map.

If desired, the write-lock must be set in hardware by biasing PGPIO2
appropriately and setting the cirrus,hw-i2c-lock flag in the devicetree
entry.

Signed-off-by: default avatarRicardo Rivera-Matos <rriveram@opensource.cirrus.com>
parent a684c36e
Loading
Loading
Loading
Loading
+133 −0
Original line number Diff line number Diff line
@@ -85,6 +85,7 @@ LOG_MODULE_REGISTER(CP9314, CONFIG_REGULATOR_LOG_LEVEL);
#define CP9314_REG_LION_CFG_3        0x34
#define CP9314_LB_MIN_FREQ_SEL_0     GENMASK(7, 6)
#define CP9314_MODE_CTRL_UPDATE_BW_1 GENMASK(5, 3)
#define CP9314_ALLOW_HW_I2C_LOCK     BIT(0)

#define CP9314_REG_LB_CTRL       0x38
#define CP9314_LB1_DELTA_CFG_1   GENMASK(6, 3)
@@ -152,6 +153,8 @@ LOG_MODULE_REGISTER(CP9314, CONFIG_REGULATOR_LOG_LEVEL);
#define CP9314_PTE_2_OTP_1   0x0
#define CP9314_PTE_2_OTP_2   0x1

#define CP9314_REG_BACKDOOR_CTRL 0x8C

#define CP9314_FAULT1_STS 0x9A
#define CP9314_VIN_OV_STS BIT(4)

@@ -192,17 +195,25 @@ enum cp9314_sync_roles {
	CP9314_ROLE_STANDALONE,
};

enum cp9314_backdoor_keys {
	CP9314_BACKDOOR_LOCKED_KEY = 0x0,
	CP9314_BACKDOOR_PUBLIC_KEY = 0x0F,
};

struct regulator_cp9314_config {
	struct regulator_common_config common;
	struct i2c_dt_spec i2c;
	struct gpio_dt_spec en_pin;
	struct gpio_dt_spec pgood_pin;
	uint8_t initial_op_mode_idx;
	bool hw_i2c_lock;
};

struct regulator_cp9314_data {
	struct regulator_common_data data;
	enum cp9314_sync_roles sync_role;
	uint8_t backdoor_key;
	bool allow_hw_i2c_lock;
};

struct cp9314_reg_patch {
@@ -284,14 +295,75 @@ static int regulator_cp9314_get_error_flags(const struct device *dev,
	return 0;
}

static int regulator_cp9314_write_lock(const struct device *dev,
				       const enum cp9314_backdoor_keys key)
{
	const struct regulator_cp9314_config *config = dev->config;
	struct regulator_cp9314_data *data = dev->data;
	int ret;

	if (data->allow_hw_i2c_lock == 0U) {
		ret = i2c_reg_update_byte_dt(&config->i2c, CP9314_REG_LION_CFG_3,
					     CP9314_ALLOW_HW_I2C_LOCK, CP9314_ALLOW_HW_I2C_LOCK);
		if (ret < 0) {
			return ret;
		}

		data->allow_hw_i2c_lock = true;
	}

	if ((uint8_t)key == data->backdoor_key) {
		return 0;
	} else {
		return i2c_reg_write_byte_dt(&config->i2c, CP9314_REG_BACKDOOR_CTRL, (uint8_t)key);
	}
}

static int regulator_cp9314_write_lock_init(const struct device *dev)
{
	const struct regulator_cp9314_config *config = dev->config;
	struct regulator_cp9314_data *data = dev->data;
	uint8_t value;
	int ret;

	ret = i2c_reg_read_byte_dt(&config->i2c, CP9314_REG_LION_CFG_3, &value);
	if (ret < 0) {
		return ret;
	}

	data->allow_hw_i2c_lock = FIELD_GET(CP9314_ALLOW_HW_I2C_LOCK, value);

	ret = i2c_reg_read_byte_dt(&config->i2c, CP9314_REG_BACKDOOR_CTRL, &data->backdoor_key);
	if (ret < 0) {
		return ret;
	}

	return 0;
}

static int regulator_cp9314_disable(const struct device *dev)
{
	const struct regulator_cp9314_config *config = dev->config;
	int ret;

	if (config->en_pin.port != NULL) {
		return gpio_pin_set_dt(&config->en_pin, 0);
	}

	if (config->hw_i2c_lock != 0U) {
		ret = regulator_cp9314_write_lock(dev, CP9314_BACKDOOR_PUBLIC_KEY);
		if (ret < 0) {
			return ret;
		}

		ret = i2c_reg_update_byte_dt(&config->i2c, CP9314_REG_CTRL1, CP9314_CP_EN, 0);
		if (ret < 0) {
			return ret;
		}

		return regulator_cp9314_write_lock(dev, CP9314_BACKDOOR_LOCKED_KEY);
	}

	return i2c_reg_update_byte_dt(&config->i2c, CP9314_REG_CTRL1, CP9314_CP_EN, 0);
}

@@ -301,6 +373,13 @@ static int regulator_cp9314_enable(const struct device *dev)
	uint8_t value;
	int ret;

	if (config->hw_i2c_lock != 0U) {
		ret = regulator_cp9314_write_lock(dev, CP9314_BACKDOOR_PUBLIC_KEY);
		if (ret < 0) {
			return ret;
		}
	}

	ret = i2c_reg_read_byte_dt(&config->i2c, CP9314_REG_CONVERTER, &value);
	if (ret < 0) {
		return ret;
@@ -353,6 +432,13 @@ static int regulator_cp9314_enable(const struct device *dev)
		}
	}

	if (config->hw_i2c_lock != 0U) {
		ret = regulator_cp9314_write_lock(dev, CP9314_BACKDOOR_LOCKED_KEY);
		if (ret < 0) {
			return ret;
		}
	}

	return 0;
}

@@ -403,6 +489,19 @@ static int cp9314_do_soft_reset(const struct device *dev)
	const struct regulator_cp9314_config *config = dev->config;
	int ret;

	if (config->hw_i2c_lock != 0U) {
		ret = regulator_cp9314_write_lock(dev, CP9314_BACKDOOR_PUBLIC_KEY);
		if (ret < 0) {
			return ret;
		}

		ret = i2c_reg_update_byte_dt(&config->i2c, CP9314_REG_LION_CFG_3,
					     CP9314_ALLOW_HW_I2C_LOCK, 0);
		if (ret < 0) {
			return ret;
		}
	}

	ret = i2c_reg_write_byte_dt(&config->i2c, CP9314_REG_CRUS_CTRL, CP9314_CRUS_KEY_SOFT_RESET);
	if (ret < 0) {
		return ret;
@@ -416,6 +515,13 @@ static int cp9314_do_soft_reset(const struct device *dev)

	k_msleep(CP9314_SOFT_RESET_DELAY_MSEC);

	if (config->hw_i2c_lock != 0U) {
		ret = regulator_cp9314_write_lock_init(dev);
		if (ret < 0) {
			return ret;
		}
	}

	return 0;
}

@@ -507,11 +613,30 @@ static int regulator_cp9314_init(const struct device *dev)
		k_usleep(CP9314_EN_DEBOUNCE_USEC);
	}

	if (config->hw_i2c_lock != 0U) {
		ret = regulator_cp9314_write_lock_init(dev);
		if (ret < 0) {
			return ret;
		}
	} else {
		data->allow_hw_i2c_lock = 0;
	}

	ret = cp9314_do_soft_reset(dev);
	if (ret < 0) {
		return ret;
	}

	if (data->allow_hw_i2c_lock != 0U) {
		ret = i2c_reg_update_byte_dt(&config->i2c, CP9314_REG_LION_CFG_3,
					     CP9314_ALLOW_HW_I2C_LOCK, 0x0);
		if (ret < 0) {
			return ret;
		}

		data->allow_hw_i2c_lock = false;
	}

	ret = i2c_reg_read_byte_dt(&config->i2c, CP9314_REG_BC_STS_C, &value);
	if (ret < 0) {
		return ret;
@@ -597,6 +722,13 @@ static int regulator_cp9314_init(const struct device *dev)
		}
	}

	if (config->hw_i2c_lock != 0U) {
		ret = regulator_cp9314_write_lock(dev, CP9314_BACKDOOR_LOCKED_KEY);
		if (ret < 0) {
			return ret;
		}
	}

	regulator_common_data_init(dev);

	return regulator_common_init(dev, false);
@@ -618,6 +750,7 @@ static const struct regulator_driver_api api = {
		.pgood_pin = GPIO_DT_SPEC_INST_GET_OR(inst, cirrus_pgood_gpios, {}),               \
		.initial_op_mode_idx =                                                             \
			DT_INST_ENUM_IDX_OR(inst, cirrus_initial_switched_capacitor_mode, -1) + 1, \
		.hw_i2c_lock = DT_INST_PROP(inst, cirrus_hw_i2c_lock),                             \
	};                                                                                         \
                                                                                                   \
	DEVICE_DT_INST_DEFINE(inst, regulator_cp9314_init, NULL, &data_##inst, &config_##inst,     \