rtc: rv3028: add BSM support
authorAlexandre Belloni <alexandre.belloni@bootlin.com>
Mon, 18 Oct 2021 15:19:32 +0000 (17:19 +0200)
committerAlexandre Belloni <alexandre.belloni@bootlin.com>
Mon, 18 Oct 2021 15:20:50 +0000 (17:20 +0200)
Backup Switch Mode controls how the RTC decides when to switch to the
backup power supply. As it is disabled by default, provide a way to enable
and configure it.

Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
Link: https://lore.kernel.org/r/20211018151933.76865-7-alexandre.belloni@bootlin.com
drivers/rtc/rtc-rv3028.c

index 12c8073..cdc623b 100644 (file)
@@ -10,6 +10,7 @@
 
 #include <linux/clk-provider.h>
 #include <linux/bcd.h>
+#include <linux/bitfield.h>
 #include <linux/bitops.h>
 #include <linux/i2c.h>
 #include <linux/interrupt.h>
 
 #define RV3028_BACKUP_TCE              BIT(5)
 #define RV3028_BACKUP_TCR_MASK         GENMASK(1,0)
+#define RV3028_BACKUP_BSM              GENMASK(3,2)
+
+#define RV3028_BACKUP_BSM_DSM          0x1
+#define RV3028_BACKUP_BSM_LSM          0x3
 
 #define OFFSET_STEP_PPT                        953674
 
@@ -512,6 +517,71 @@ exit_eerd:
 
 }
 
+static int rv3028_param_get(struct device *dev, struct rtc_param *param)
+{
+       struct rv3028_data *rv3028 = dev_get_drvdata(dev);
+       int ret;
+
+       switch(param->param) {
+               u32 value;
+
+       case RTC_PARAM_BACKUP_SWITCH_MODE:
+               ret = regmap_read(rv3028->regmap, RV3028_BACKUP, &value);
+               if (ret < 0)
+                       return ret;
+
+               value = FIELD_GET(RV3028_BACKUP_BSM, value);
+
+               switch(value) {
+               case RV3028_BACKUP_BSM_DSM:
+                       param->uvalue = RTC_BSM_DIRECT;
+                       break;
+               case RV3028_BACKUP_BSM_LSM:
+                       param->uvalue = RTC_BSM_LEVEL;
+                       break;
+               default:
+                       param->uvalue = RTC_BSM_DISABLED;
+               }
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int rv3028_param_set(struct device *dev, struct rtc_param *param)
+{
+       struct rv3028_data *rv3028 = dev_get_drvdata(dev);
+
+       switch(param->param) {
+               u8 mode;
+       case RTC_PARAM_BACKUP_SWITCH_MODE:
+               switch (param->uvalue) {
+               case RTC_BSM_DISABLED:
+                       mode = 0;
+                       break;
+               case RTC_BSM_DIRECT:
+                       mode = RV3028_BACKUP_BSM_DSM;
+                       break;
+               case RTC_BSM_LEVEL:
+                       mode = RV3028_BACKUP_BSM_LSM;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+
+               return rv3028_update_cfg(rv3028, RV3028_BACKUP, RV3028_BACKUP_BSM,
+                                        FIELD_PREP(RV3028_BACKUP_BSM, mode));
+
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
 static int rv3028_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
 {
        struct rv3028_data *rv3028 = dev_get_drvdata(dev);
@@ -776,6 +846,8 @@ static const struct rtc_class_ops rv3028_rtc_ops = {
        .read_offset = rv3028_read_offset,
        .set_offset = rv3028_set_offset,
        .ioctl = rv3028_ioctl,
+       .param_get = rv3028_param_get,
+       .param_set = rv3028_param_set,
 };
 
 static const struct regmap_config regmap_config = {
@@ -878,6 +950,8 @@ static int rv3028_probe(struct i2c_client *client)
        if (ret)
                return ret;
 
+       set_bit(RTC_FEATURE_BACKUP_SWITCH_MODE, rv3028->rtc->features);
+
        rv3028->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
        rv3028->rtc->range_max = RTC_TIMESTAMP_END_2099;
        rv3028->rtc->ops = &rv3028_rtc_ops;