From 5945b2880363ed7648e62aabba770ec57ff2a316 Mon Sep 17 00:00:00 2001 From: Arnaud Ebalard Date: Wed, 10 Dec 2014 15:54:02 -0800 Subject: drivers/rtc/rtc-isl12057.c: fix masking of register values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When Intersil ISL12057 support was added by commit 70e123373c05 ("rtc: Add support for Intersil ISL12057 I2C RTC chip"), two masks for time registers values imported from the device were either wrong or omitted, leading to additional bits from those registers to impact read values: - mask for hour register value when reading it in AM/PM mode. As AM/PM mode is not the usual mode used by the driver, this error would only have an impact on an externally configured RTC hour later read by the driver. - mask for month value. The lack of masking would provide an erroneous value if century bit is set. This patch fixes those two masks. Fixes: 70e123373c05 ("rtc: Add support for Intersil ISL12057 I2C RTC chip") Signed-off-by: Arnaud Ebalard Cc: Mark Rutland Cc: Alessandro Zummo Cc: Peter Huewe Cc: Linus Walleij Cc: Thierry Reding Cc: Mark Brown Cc: Grant Likely Acked-by: Uwe Kleine-König Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-isl12057.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/rtc/rtc-isl12057.c') diff --git a/drivers/rtc/rtc-isl12057.c b/drivers/rtc/rtc-isl12057.c index 455b601d731d..8c3f60737df8 100644 --- a/drivers/rtc/rtc-isl12057.c +++ b/drivers/rtc/rtc-isl12057.c @@ -88,7 +88,7 @@ static void isl12057_rtc_regs_to_tm(struct rtc_time *tm, u8 *regs) tm->tm_min = bcd2bin(regs[ISL12057_REG_RTC_MN]); if (regs[ISL12057_REG_RTC_HR] & ISL12057_REG_RTC_HR_MIL) { /* AM/PM */ - tm->tm_hour = bcd2bin(regs[ISL12057_REG_RTC_HR] & 0x0f); + tm->tm_hour = bcd2bin(regs[ISL12057_REG_RTC_HR] & 0x1f); if (regs[ISL12057_REG_RTC_HR] & ISL12057_REG_RTC_HR_PM) tm->tm_hour += 12; } else { /* 24 hour mode */ @@ -97,7 +97,7 @@ static void isl12057_rtc_regs_to_tm(struct rtc_time *tm, u8 *regs) tm->tm_mday = bcd2bin(regs[ISL12057_REG_RTC_DT]); tm->tm_wday = bcd2bin(regs[ISL12057_REG_RTC_DW]) - 1; /* starts at 1 */ - tm->tm_mon = bcd2bin(regs[ISL12057_REG_RTC_MO]) - 1; /* starts at 1 */ + tm->tm_mon = bcd2bin(regs[ISL12057_REG_RTC_MO] & 0x1f) - 1; /* ditto */ tm->tm_year = bcd2bin(regs[ISL12057_REG_RTC_YR]) + 100; } -- cgit From b5f4184d1439b6c5b36f54804b194f0b93a5b232 Mon Sep 17 00:00:00 2001 From: Arnaud Ebalard Date: Wed, 10 Dec 2014 15:54:05 -0800 Subject: drivers/rtc/rtc-isl12057.c: add support for century bit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The month register of ISL12057 RTC chip includes a century bit which reports overflow of year register from 99 to 0. This bit can also be written, which allows using it to extend the time interval the chip can support from 99 to 199 years. This patch adds support for century overflow bit in tm to regs and regs to tm helpers in ISL12057 driver. This was tested by putting a device 100 years in the future (using a specific kernel due to the inability of userland tools such as date or hwclock to pass year 2038), rebooting on a kernel w/ this patch applied and verifying the device was still 100 years in the future. Signed-off-by: Arnaud Ebalard Suggested-by: Uwe Kleine-König Acked-by: Uwe Kleine-König Cc: Mark Rutland Cc: Alessandro Zummo Cc: Peter Huewe Cc: Linus Walleij Cc: Thierry Reding Cc: Mark Brown Cc: Grant Likely Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-isl12057.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) (limited to 'drivers/rtc/rtc-isl12057.c') diff --git a/drivers/rtc/rtc-isl12057.c b/drivers/rtc/rtc-isl12057.c index 8c3f60737df8..b538fabfcfd3 100644 --- a/drivers/rtc/rtc-isl12057.c +++ b/drivers/rtc/rtc-isl12057.c @@ -41,6 +41,7 @@ #define ISL12057_REG_RTC_DW 0x03 /* Day of the Week */ #define ISL12057_REG_RTC_DT 0x04 /* Date */ #define ISL12057_REG_RTC_MO 0x05 /* Month */ +#define ISL12057_REG_RTC_MO_CEN BIT(7) /* Century bit */ #define ISL12057_REG_RTC_YR 0x06 /* Year */ #define ISL12057_RTC_SEC_LEN 7 @@ -99,24 +100,35 @@ static void isl12057_rtc_regs_to_tm(struct rtc_time *tm, u8 *regs) tm->tm_wday = bcd2bin(regs[ISL12057_REG_RTC_DW]) - 1; /* starts at 1 */ tm->tm_mon = bcd2bin(regs[ISL12057_REG_RTC_MO] & 0x1f) - 1; /* ditto */ tm->tm_year = bcd2bin(regs[ISL12057_REG_RTC_YR]) + 100; + + /* Check if years register has overflown from 99 to 00 */ + if (regs[ISL12057_REG_RTC_MO] & ISL12057_REG_RTC_MO_CEN) + tm->tm_year += 100; } static int isl12057_rtc_tm_to_regs(u8 *regs, struct rtc_time *tm) { + u8 century_bit; + /* * The clock has an 8 bit wide bcd-coded register for the year. + * It also has a century bit encoded in MO flag which provides + * information about overflow of year register from 99 to 00. * tm_year is an offset from 1900 and we are interested in the - * 2000-2099 range, so any value less than 100 is invalid. + * 2000-2199 range, so any value less than 100 or larger than + * 299 is invalid. */ - if (tm->tm_year < 100) + if (tm->tm_year < 100 || tm->tm_year > 299) return -EINVAL; + century_bit = (tm->tm_year > 199) ? ISL12057_REG_RTC_MO_CEN : 0; + regs[ISL12057_REG_RTC_SC] = bin2bcd(tm->tm_sec); regs[ISL12057_REG_RTC_MN] = bin2bcd(tm->tm_min); regs[ISL12057_REG_RTC_HR] = bin2bcd(tm->tm_hour); /* 24-hour format */ regs[ISL12057_REG_RTC_DT] = bin2bcd(tm->tm_mday); - regs[ISL12057_REG_RTC_MO] = bin2bcd(tm->tm_mon + 1); - regs[ISL12057_REG_RTC_YR] = bin2bcd(tm->tm_year - 100); + regs[ISL12057_REG_RTC_MO] = bin2bcd(tm->tm_mon + 1) | century_bit; + regs[ISL12057_REG_RTC_YR] = bin2bcd(tm->tm_year % 100); regs[ISL12057_REG_RTC_DW] = bin2bcd(tm->tm_wday + 1); return 0; -- cgit From 10df1e6787647f381a90689a5285555275f1a3a3 Mon Sep 17 00:00:00 2001 From: Arnaud Ebalard Date: Wed, 10 Dec 2014 15:54:08 -0800 Subject: drivers/rtc/rtc-isl12057.c: add proper handling of oscillator failure bit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As suggested by Uwe, instead of clearing oscillator failure bit unconditionally at driver load, this patch adds proper handling of the flag. The driver now returns -ENODATA when reading time from the device and oscillator failure bit is set. The flag is now cleared only when the a new time value is pushed to the device. Signed-off-by: Arnaud Ebalard Reported-by: Uwe Kleine-König Acked-by: Uwe Kleine-König Cc: Mark Rutland Cc: Alessandro Zummo Cc: Peter Huewe Cc: Linus Walleij Cc: Thierry Reding Cc: Mark Brown Cc: Grant Likely Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-isl12057.c | 47 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 14 deletions(-) (limited to 'drivers/rtc/rtc-isl12057.c') diff --git a/drivers/rtc/rtc-isl12057.c b/drivers/rtc/rtc-isl12057.c index b538fabfcfd3..fe562820a54a 100644 --- a/drivers/rtc/rtc-isl12057.c +++ b/drivers/rtc/rtc-isl12057.c @@ -164,17 +164,32 @@ static int isl12057_rtc_read_time(struct device *dev, struct rtc_time *tm) { struct isl12057_rtc_data *data = dev_get_drvdata(dev); u8 regs[ISL12057_RTC_SEC_LEN]; + unsigned int sr; int ret; mutex_lock(&data->lock); + ret = regmap_read(data->regmap, ISL12057_REG_SR, &sr); + if (ret) { + dev_err(dev, "%s: unable to read oscillator status flag\n", + __func__); + goto out; + } else { + if (sr & ISL12057_REG_SR_OSF) { + ret = -ENODATA; + goto out; + } + } + ret = regmap_bulk_read(data->regmap, ISL12057_REG_RTC_SC, regs, ISL12057_RTC_SEC_LEN); + if (ret) + dev_err(dev, "%s: unable to read RTC time\n", __func__); + +out: mutex_unlock(&data->lock); - if (ret) { - dev_err(dev, "%s: RTC read failed\n", __func__); + if (ret) return ret; - } isl12057_rtc_regs_to_tm(tm, regs); @@ -194,10 +209,22 @@ static int isl12057_rtc_set_time(struct device *dev, struct rtc_time *tm) mutex_lock(&data->lock); ret = regmap_bulk_write(data->regmap, ISL12057_REG_RTC_SC, regs, ISL12057_RTC_SEC_LEN); - mutex_unlock(&data->lock); + if (ret) { + dev_err(dev, "%s: writing RTC time failed\n", __func__); + goto out; + } - if (ret) - dev_err(dev, "%s: RTC write failed\n", __func__); + /* + * Now that RTC time has been updated, let's clear oscillator + * failure flag, if needed. + */ + ret = regmap_update_bits(data->regmap, ISL12057_REG_SR, + ISL12057_REG_SR_OSF, 0); + if (ret < 0) + dev_err(dev, "Unable to clear oscillator failure bit\n"); + +out: + mutex_unlock(&data->lock); return ret; } @@ -219,14 +246,6 @@ static int isl12057_check_rtc_status(struct device *dev, struct regmap *regmap) return ret; } - /* Clear oscillator failure bit if needed */ - ret = regmap_update_bits(regmap, ISL12057_REG_SR, - ISL12057_REG_SR_OSF, 0); - if (ret < 0) { - dev_err(dev, "Unable to clear oscillator failure bit\n"); - return ret; - } - /* Clear alarm bit if needed */ ret = regmap_update_bits(regmap, ISL12057_REG_SR, ISL12057_REG_SR_A1F, 0); -- cgit From cf67d0b6410182eefd4db665279fce76c9a19cf9 Mon Sep 17 00:00:00 2001 From: Arnaud Ebalard Date: Wed, 10 Dec 2014 15:54:11 -0800 Subject: drivers/rtc/rtc-isl12057.c: report error code upon failure in dev_err() calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As pointed out by Mark, it is generally useful to log the error code when reporting a failure. This patch improves existing calls to dev_err() in ISL12057 driver to also report error code. Signed-off-by: Arnaud Ebalard Suggested-by: Mark Brown Cc: Mark Rutland Cc: Alessandro Zummo Cc: Peter Huewe Cc: Linus Walleij Cc: Thierry Reding Cc: Grant Likely Acked-by: Uwe Kleine-König Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-isl12057.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) (limited to 'drivers/rtc/rtc-isl12057.c') diff --git a/drivers/rtc/rtc-isl12057.c b/drivers/rtc/rtc-isl12057.c index fe562820a54a..6e1fcfb5d7e6 100644 --- a/drivers/rtc/rtc-isl12057.c +++ b/drivers/rtc/rtc-isl12057.c @@ -170,8 +170,8 @@ static int isl12057_rtc_read_time(struct device *dev, struct rtc_time *tm) mutex_lock(&data->lock); ret = regmap_read(data->regmap, ISL12057_REG_SR, &sr); if (ret) { - dev_err(dev, "%s: unable to read oscillator status flag\n", - __func__); + dev_err(dev, "%s: unable to read oscillator status flag (%d)\n", + __func__, ret); goto out; } else { if (sr & ISL12057_REG_SR_OSF) { @@ -183,7 +183,8 @@ static int isl12057_rtc_read_time(struct device *dev, struct rtc_time *tm) ret = regmap_bulk_read(data->regmap, ISL12057_REG_RTC_SC, regs, ISL12057_RTC_SEC_LEN); if (ret) - dev_err(dev, "%s: unable to read RTC time\n", __func__); + dev_err(dev, "%s: unable to read RTC time section (%d)\n", + __func__, ret); out: mutex_unlock(&data->lock); @@ -210,7 +211,8 @@ static int isl12057_rtc_set_time(struct device *dev, struct rtc_time *tm) ret = regmap_bulk_write(data->regmap, ISL12057_REG_RTC_SC, regs, ISL12057_RTC_SEC_LEN); if (ret) { - dev_err(dev, "%s: writing RTC time failed\n", __func__); + dev_err(dev, "%s: unable to write RTC time section (%d)\n", + __func__, ret); goto out; } @@ -221,7 +223,8 @@ static int isl12057_rtc_set_time(struct device *dev, struct rtc_time *tm) ret = regmap_update_bits(data->regmap, ISL12057_REG_SR, ISL12057_REG_SR_OSF, 0); if (ret < 0) - dev_err(dev, "Unable to clear oscillator failure bit\n"); + dev_err(dev, "%s: unable to clear osc. failure bit (%d)\n", + __func__, ret); out: mutex_unlock(&data->lock); @@ -242,7 +245,8 @@ static int isl12057_check_rtc_status(struct device *dev, struct regmap *regmap) ret = regmap_update_bits(regmap, ISL12057_REG_INT, ISL12057_REG_INT_EOSC, 0); if (ret < 0) { - dev_err(dev, "Unable to enable oscillator\n"); + dev_err(dev, "%s: unable to enable oscillator (%d)\n", + __func__, ret); return ret; } @@ -250,7 +254,8 @@ static int isl12057_check_rtc_status(struct device *dev, struct regmap *regmap) ret = regmap_update_bits(regmap, ISL12057_REG_SR, ISL12057_REG_SR_A1F, 0); if (ret < 0) { - dev_err(dev, "Unable to clear alarm bit\n"); + dev_err(dev, "%s: unable to clear alarm bit (%d)\n", + __func__, ret); return ret; } @@ -284,7 +289,8 @@ static int isl12057_probe(struct i2c_client *client, regmap = devm_regmap_init_i2c(client, &isl12057_rtc_regmap_config); if (IS_ERR(regmap)) { ret = PTR_ERR(regmap); - dev_err(dev, "regmap allocation failed: %d\n", ret); + dev_err(dev, "%s: regmap allocation failed (%d)\n", + __func__, ret); return ret; } -- cgit