Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

rtc: pcf8523: add BSM support

Backup Switch Mode allows to select the strategy to use to switch from the
main power supply to the backup power supply. As before, the driver will
switch from standby mode to level mode but now only when it has never been
set.

Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
Link: https://lore.kernel.org/r/20211018153651.82069-5-alexandre.belloni@bootlin.com

+97 -6
+97 -6
drivers/rtc/rtc-pcf8523.c
··· 4 4 */ 5 5 6 6 #include <linux/bcd.h> 7 + #include <linux/bitfield.h> 7 8 #include <linux/i2c.h> 8 9 #include <linux/module.h> 9 10 #include <linux/regmap.h> ··· 21 20 #define PCF8523_CONTROL2_AF BIT(3) 22 21 23 22 #define PCF8523_REG_CONTROL3 0x02 24 - #define PCF8523_CONTROL3_PM_BLD BIT(7) /* battery low detection disabled */ 25 - #define PCF8523_CONTROL3_PM_VDD BIT(6) /* switch-over disabled */ 26 - #define PCF8523_CONTROL3_PM_DSM BIT(5) /* direct switching mode */ 27 - #define PCF8523_CONTROL3_PM_MASK 0xe0 23 + #define PCF8523_CONTROL3_PM GENMASK(7,5) 24 + #define PCF8523_PM_STANDBY 0x7 28 25 #define PCF8523_CONTROL3_BLF BIT(2) /* battery low bit, read-only */ 29 26 #define PCF8523_CONTROL3_BSF BIT(3) 30 27 ··· 236 237 return 0; 237 238 } 238 239 240 + static int pcf8523_param_get(struct device *dev, struct rtc_param *param) 241 + { 242 + struct pcf8523 *pcf8523 = dev_get_drvdata(dev); 243 + int ret; 244 + 245 + switch(param->param) { 246 + u32 value; 247 + 248 + case RTC_PARAM_BACKUP_SWITCH_MODE: 249 + ret = regmap_read(pcf8523->regmap, PCF8523_REG_CONTROL3, &value); 250 + if (ret < 0) 251 + return ret; 252 + 253 + value = FIELD_GET(PCF8523_CONTROL3_PM, value); 254 + 255 + switch(value) { 256 + case 0x0: 257 + case 0x4: 258 + param->uvalue = RTC_BSM_LEVEL; 259 + break; 260 + case 0x1: 261 + case 0x5: 262 + param->uvalue = RTC_BSM_DIRECT; 263 + break; 264 + case PCF8523_PM_STANDBY: 265 + param->uvalue = RTC_BSM_STANDBY; 266 + break; 267 + default: 268 + param->uvalue = RTC_BSM_DISABLED; 269 + } 270 + 271 + break; 272 + 273 + default: 274 + return -EINVAL; 275 + } 276 + 277 + return 0; 278 + } 279 + 280 + static int pcf8523_param_set(struct device *dev, struct rtc_param *param) 281 + { 282 + struct pcf8523 *pcf8523 = dev_get_drvdata(dev); 283 + 284 + switch(param->param) { 285 + u8 mode; 286 + case RTC_PARAM_BACKUP_SWITCH_MODE: 287 + switch (param->uvalue) { 288 + case RTC_BSM_DISABLED: 289 + mode = 0x2; 290 + break; 291 + case RTC_BSM_DIRECT: 292 + mode = 0x1; 293 + break; 294 + case RTC_BSM_LEVEL: 295 + mode = 0x0; 296 + break; 297 + case RTC_BSM_STANDBY: 298 + mode = PCF8523_PM_STANDBY; 299 + break; 300 + default: 301 + return -EINVAL; 302 + } 303 + 304 + return regmap_update_bits(pcf8523->regmap, PCF8523_REG_CONTROL3, 305 + PCF8523_CONTROL3_PM, 306 + FIELD_PREP(PCF8523_CONTROL3_PM, mode)); 307 + 308 + break; 309 + 310 + default: 311 + return -EINVAL; 312 + } 313 + 314 + return 0; 315 + } 316 + 239 317 static int pcf8523_rtc_ioctl(struct device *dev, unsigned int cmd, 240 318 unsigned long arg) 241 319 { ··· 388 312 .ioctl = pcf8523_rtc_ioctl, 389 313 .read_offset = pcf8523_rtc_read_offset, 390 314 .set_offset = pcf8523_rtc_set_offset, 315 + .param_get = pcf8523_param_get, 316 + .param_set = pcf8523_param_set, 391 317 }; 392 318 393 319 static const struct regmap_config regmap_config = { ··· 404 326 struct pcf8523 *pcf8523; 405 327 struct rtc_device *rtc; 406 328 bool wakeup_source = false; 329 + u32 value; 407 330 int err; 408 331 409 332 if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) ··· 430 351 dev_warn(&client->dev, "failed to set xtal load capacitance: %d", 431 352 err); 432 353 433 - err = regmap_update_bits(pcf8523->regmap, PCF8523_REG_CONTROL3, 434 - PCF8523_CONTROL3_PM_MASK, 0); 354 + err = regmap_read(pcf8523->regmap, PCF8523_REG_SECONDS, &value); 435 355 if (err < 0) 436 356 return err; 357 + 358 + if (value & PCF8523_SECONDS_OS) { 359 + err = regmap_read(pcf8523->regmap, PCF8523_REG_CONTROL3, &value); 360 + if (err < 0) 361 + return err; 362 + 363 + if (FIELD_GET(PCF8523_CONTROL3_PM, value) == PCF8523_PM_STANDBY) { 364 + err = regmap_write(pcf8523->regmap, PCF8523_REG_CONTROL3, 365 + value & ~PCF8523_CONTROL3_PM); 366 + if (err < 0) 367 + return err; 368 + } 369 + } 437 370 438 371 rtc->ops = &pcf8523_rtc_ops; 439 372 rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;