@@ -20,6 +20,7 @@
#include <linux/i2c.h>
#include <linux/spi/spi.h>
#include <linux/bcd.h>
+#include <linux/bitfield.h>
#include <linux/rtc.h>
#include <linux/slab.h>
#include <linux/module.h>
@@ -48,6 +49,7 @@
#define PCF2127_BIT_CTRL3_BLF BIT(2)
#define PCF2127_BIT_CTRL3_BF BIT(3)
#define PCF2127_BIT_CTRL3_BTSE BIT(4)
+#define PCF2127_CTRL3_PWRMNG_MASK GENMASK(7, 5)
/* Time and date registers */
#define PCF2127_REG_TIME_BASE 0x03
#define PCF2127_BIT_SC_OSF BIT(7)
@@ -1080,6 +1082,57 @@ static int pcf2127_enable_ts(struct device *dev, int ts_id)
return ret;
}
+/*
+ * By default, do not reconfigure or set default power management mode,
+ * unless explicitly requested via DT properties:
+ * battery-switch-over
+ * battery-low-detect
+ */
+static int pcf2127_configure_power_management(struct device *dev)
+{
+ struct pcf2127 *pcf2127 = dev_get_drvdata(dev);
+ int ret;
+ u8 pwrmng;
+ u32 bat_sw_over, bat_low_detect;
+
+ /*
+ * The PWRMNG field is defined in a peculiar way for PCF21XX
+ * devices: there is no individual bit defined for the
+ * battery-switch-over or battery-low-detect functions.
+ * Therefore, we require that both properties must be defined
+ * to alter the PWRMNG field.
+ */
+ if (device_property_read_u32(dev, "battery-switch-over", &bat_sw_over))
+ return 0;
+
+ if (device_property_read_u32(dev, "battery-low-detect",
+ &bat_low_detect))
+ return 0;
+
+ if (!bat_sw_over) {
+ /*
+ * If battery-switch-over is disabled, then the
+ * battery-low-detect function is always disabled.
+ */
+ pwrmng = BIT(2) | BIT(1) | BIT(0);
+ } else {
+ if (bat_low_detect)
+ pwrmng = 0;
+ else
+ pwrmng = BIT(0);
+ }
+
+ ret = regmap_update_bits(pcf2127->regmap, PCF2127_REG_CTRL3,
+ PCF2127_CTRL3_PWRMNG_MASK,
+ FIELD_PREP(PCF2127_CTRL3_PWRMNG_MASK, pwrmng));
+ if (ret < 0) {
+ dev_dbg(dev, "PWRMNG config failed\n");
+ return ret;
+ }
+
+ return 0;
+}
+
/* Route all interrupt sources to INT A pin. */
static int pcf2127_configure_interrupt_pins(struct device *dev)
{
@@ -1163,6 +1216,12 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
pcf2127->irq_enabled = true;
}
+ ret = pcf2127_configure_power_management(dev);
+ if (ret) {
+ dev_err(dev, "failed to configure power management\n");
+ return ret;
+ }
+
if (alarm_irq > 0 || device_property_read_bool(dev, "wakeup-source")) {
device_init_wakeup(dev, true);
set_bit(RTC_FEATURE_ALARM, pcf2127->rtc->features);