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

rtc: rs5c372: Fix reading from rtc when the oscillator got interrupted.

When the oscillator of the rtc gets interrupted,
e.g. due to an empty battery, reading from the rtc will now return an error
and the oscillator bit will be cleared, once the rtc is successfully reset.

Signed-off-by: Oliver Rohe <oliver.rohe@wago.com>
Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>

authored by

Oliver.Rohe@wago.com and committed by
Alexandre Belloni
09832dc0 7dd63ec0

+47 -7
+47 -7
drivers/rtc/rtc-rs5c372.c
··· 52 52 # define RS5C_CTRL1_CT4 (4 << 0) /* 1 Hz level irq */ 53 53 #define RS5C_REG_CTRL2 15 54 54 # define RS5C372_CTRL2_24 (1 << 5) 55 - # define R2x2x_CTRL2_XSTP (1 << 5) /* only if R2x2x */ 56 55 # define RS5C_CTRL2_XSTP (1 << 4) /* only if !R2x2x */ 56 + # define R2x2x_CTRL2_VDET (1 << 6) /* only if R2x2x */ 57 + # define R2x2x_CTRL2_XSTP (1 << 5) /* only if R2x2x */ 58 + # define R2x2x_CTRL2_PON (1 << 4) /* only if R2x2x */ 57 59 # define RS5C_CTRL2_CTFG (1 << 2) 58 60 # define RS5C_CTRL2_AAFG (1 << 1) /* or WAFG */ 59 61 # define RS5C_CTRL2_BAFG (1 << 0) /* or DAFG */ ··· 214 212 struct i2c_client *client = to_i2c_client(dev); 215 213 struct rs5c372 *rs5c = i2c_get_clientdata(client); 216 214 int status = rs5c_get_regs(rs5c); 215 + unsigned char ctrl2 = rs5c->regs[RS5C_REG_CTRL2]; 217 216 218 217 if (status < 0) 219 218 return status; 219 + 220 + switch (rs5c->type) { 221 + case rtc_r2025sd: 222 + case rtc_r2221tl: 223 + if ((rs5c->type == rtc_r2025sd && !(ctrl2 & R2x2x_CTRL2_XSTP)) || 224 + (rs5c->type == rtc_r2221tl && (ctrl2 & R2x2x_CTRL2_XSTP))) { 225 + dev_warn(&client->dev, "rtc oscillator interruption detected. Please reset the rtc clock.\n"); 226 + return -EINVAL; 227 + } 228 + break; 229 + default: 230 + if (ctrl2 & RS5C_CTRL2_XSTP) { 231 + dev_warn(&client->dev, "rtc oscillator interruption detected. Please reset the rtc clock.\n"); 232 + return -EINVAL; 233 + } 234 + } 220 235 221 236 tm->tm_sec = bcd2bin(rs5c->regs[RS5C372_REG_SECS] & 0x7f); 222 237 tm->tm_min = bcd2bin(rs5c->regs[RS5C372_REG_MINS] & 0x7f); ··· 262 243 struct i2c_client *client = to_i2c_client(dev); 263 244 struct rs5c372 *rs5c = i2c_get_clientdata(client); 264 245 unsigned char buf[7]; 246 + unsigned char ctrl2; 265 247 int addr; 266 248 267 249 dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d " ··· 281 261 buf[6] = bin2bcd(tm->tm_year - 100); 282 262 283 263 if (i2c_smbus_write_i2c_block_data(client, addr, sizeof(buf), buf) < 0) { 284 - dev_err(&client->dev, "%s: write error\n", __func__); 264 + dev_dbg(&client->dev, "%s: write error in line %i\n", 265 + __func__, __LINE__); 266 + return -EIO; 267 + } 268 + 269 + addr = RS5C_ADDR(RS5C_REG_CTRL2); 270 + ctrl2 = i2c_smbus_read_byte_data(client, addr); 271 + 272 + /* clear rtc warning bits */ 273 + switch (rs5c->type) { 274 + case rtc_r2025sd: 275 + case rtc_r2221tl: 276 + ctrl2 &= ~(R2x2x_CTRL2_VDET | R2x2x_CTRL2_PON); 277 + if (rs5c->type == rtc_r2025sd) 278 + ctrl2 |= R2x2x_CTRL2_XSTP; 279 + else 280 + ctrl2 &= ~R2x2x_CTRL2_XSTP; 281 + break; 282 + default: 283 + ctrl2 &= ~RS5C_CTRL2_XSTP; 284 + break; 285 + } 286 + 287 + if (i2c_smbus_write_byte_data(client, addr, ctrl2) < 0) { 288 + dev_dbg(&client->dev, "%s: write error in line %i\n", 289 + __func__, __LINE__); 285 290 return -EIO; 286 291 } 287 292 ··· 568 523 buf[0] = rs5c372->regs[RS5C_REG_CTRL1]; 569 524 buf[1] = rs5c372->regs[RS5C_REG_CTRL2]; 570 525 571 - /* handle xstp bit */ 572 526 switch (rs5c372->type) { 573 527 case rtc_r2025sd: 574 528 if (buf[1] & R2x2x_CTRL2_XSTP) 575 529 return ret; 576 - rs5c372->regs[RS5C_REG_CTRL2] |= R2x2x_CTRL2_XSTP; 577 530 break; 578 531 case rtc_r2221tl: 579 532 if (!(buf[1] & R2x2x_CTRL2_XSTP)) 580 533 return ret; 581 - rs5c372->regs[RS5C_REG_CTRL2] &= ~R2x2x_CTRL2_XSTP; 582 534 break; 583 - 584 535 default: 585 536 if (!(buf[1] & RS5C_CTRL2_XSTP)) 586 537 return ret; 587 - rs5c372->regs[RS5C_REG_CTRL2] &= ~RS5C_CTRL2_XSTP; 588 538 break; 589 539 } 590 540