Commit 6084eac3 authored by Alexandre Belloni's avatar Alexandre Belloni
Browse files

rtc: rv3032: allow setting BSM



Backup Switch Mode is currently set properly when the trickle charger is
enabled. However, in the case of a non-rechargeable battery, it is
necessary to be able to enable it, only allow that when the trickle charger
is disabled.

Signed-off-by: default avatarAlexandre Belloni <alexandre.belloni@bootlin.com>
Link: https://lore.kernel.org/r/20211018151933.76865-8-alexandre.belloni@bootlin.com
parent 018d959b
Loading
Loading
Loading
Loading
+76 −0
Original line number Diff line number Diff line
@@ -106,6 +106,7 @@
struct rv3032_data {
	struct regmap *regmap;
	struct rtc_device *rtc;
	bool trickle_charger_set;
#ifdef CONFIG_COMMON_CLK
	struct clk_hw clkout_hw;
#endif
@@ -402,6 +403,75 @@ static int rv3032_set_offset(struct device *dev, long offset)
				 FIELD_PREP(RV3032_OFFSET_MSK, offset));
}

static int rv3032_param_get(struct device *dev, struct rtc_param *param)
{
	struct rv3032_data *rv3032 = dev_get_drvdata(dev);
	int ret;

	switch(param->param) {
		u32 value;

	case RTC_PARAM_BACKUP_SWITCH_MODE:
		ret = regmap_read(rv3032->regmap, RV3032_PMU, &value);
		if (ret < 0)
			return ret;

		value = FIELD_GET(RV3032_PMU_BSM, value);

		switch(value) {
		case RV3032_PMU_BSM_DSM:
			param->uvalue = RTC_BSM_DIRECT;
			break;
		case RV3032_PMU_BSM_LSM:
			param->uvalue = RTC_BSM_LEVEL;
			break;
		default:
			param->uvalue = RTC_BSM_DISABLED;
		}

		break;

	default:
		return -EINVAL;
	}

	return 0;
}

static int rv3032_param_set(struct device *dev, struct rtc_param *param)
{
	struct rv3032_data *rv3032 = dev_get_drvdata(dev);

	switch(param->param) {
		u8 mode;
	case RTC_PARAM_BACKUP_SWITCH_MODE:
		if (rv3032->trickle_charger_set)
			return -EINVAL;

		switch (param->uvalue) {
		case RTC_BSM_DISABLED:
			mode = 0;
			break;
		case RTC_BSM_DIRECT:
			mode = RV3032_PMU_BSM_DSM;
			break;
		case RTC_BSM_LEVEL:
			mode = RV3032_PMU_BSM_LSM;
			break;
		default:
			return -EINVAL;
		}

		return rv3032_update_cfg(rv3032, RV3032_PMU, RV3032_PMU_BSM,
					 FIELD_PREP(RV3032_PMU_BSM, mode));

	default:
		return -EINVAL;
	}

	return 0;
}

static int rv3032_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
{
	struct rv3032_data *rv3032 = dev_get_drvdata(dev);
@@ -541,6 +611,8 @@ static int rv3032_trickle_charger_setup(struct device *dev, struct rv3032_data *
		return 0;
	}

	rv3032->trickle_charger_set = true;

	return rv3032_update_cfg(rv3032, RV3032_PMU,
				 RV3032_PMU_TCR | RV3032_PMU_TCM | RV3032_PMU_BSM,
				 val | FIELD_PREP(RV3032_PMU_TCR, i));
@@ -813,6 +885,8 @@ static const struct rtc_class_ops rv3032_rtc_ops = {
	.read_alarm = rv3032_get_alarm,
	.set_alarm = rv3032_set_alarm,
	.alarm_irq_enable = rv3032_alarm_irq_enable,
	.param_get = rv3032_param_get,
	.param_set = rv3032_param_set,
};

static const struct regmap_config regmap_config = {
@@ -883,6 +957,8 @@ static int rv3032_probe(struct i2c_client *client)

	rv3032_trickle_charger_setup(&client->dev, rv3032);

	set_bit(RTC_FEATURE_BACKUP_SWITCH_MODE, rv3032->rtc->features);

	rv3032->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
	rv3032->rtc->range_max = RTC_TIMESTAMP_END_2099;
	rv3032->rtc->ops = &rv3032_rtc_ops;