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

drivers/rtc/rtc-da9052.c: ALARM causes interrupt storm

Setting the alarm to a time not on a minute boundary results in repeated
interrupts being generated by the DA9052/3 PMIC device until the kernel
RTC core sees that the alarm has rung. Sometimes the number and frequency
of interrupts can cause the kernel to disable the IRQ line used by the
DA9052/3 PMIC with disasterous consequences. This patch fixes the
problem.

Even though the DA9052/3 PMIC is capable generating periodic interrupts,
ie TICKS, the method used to distinguish RTC_AF from RTC_PF events was
flawed and can not work in conjunction with the regmap_irq kernel core.
Thus that flawed detection has also been removed by the DA9052/3 PMIC RTC
driver's irq handler, so that it no longer reports the wrong type of event
to the kernel RTC core.

The internal static functions within the DA9052/3 PMIC RTC driver have
been changed to pass the 'da9052_rtc' structure instead of the 'da9052'
because there is no backwards pointer from the 'da9052' structure.

This patch fixes the three issues described above. The first is serious
because usiing the RTC alarm set to a non minute boundary will eventually
cause all component drivers that depend on the interrupt line to fail.
The solution adopted is to round up to alarm time to the next highest
minute.

The second bug, reporting a RTC_PF event instead of an RTC_AF event turns
out to not matter with the current implementation of the kernel RTC core
as it seems to ignore the event type. However, should that change in the
future it is better to fix the issue now and not have 'problems waiting to
happen'

The third set of changes are to make the da9052_rtc structure available to
all the local internal functions in the driver. This was done during
testing so that diagnostic data could be stored there. Should the
solution to the first issue be found not acceptable, then the alternative
of using the TICKS interrupt at the fixed one second interval in order to
step to the exact second of the requested alarm requires an extra (alarm
time) piece of data to be stored. In devices that use the alarm function
to wake up from sleep, accuracy to the second will result in the device
being awake for up to nearly a minute longer than expected.

Signed-off-by: Anthony Olech <anthony.olech.opensource@diasemi.com>
Cc: David Dajun Chen <dchen@diasemi.com>
Cc: Alessandro Zummo <a.zummo@towertech.it>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Anthony Olech and committed by
Linus Torvalds
7c994c08 1e83d48b

+66 -56
+66 -56
drivers/rtc/rtc-da9052.c
··· 20 20 #include <linux/mfd/da9052/da9052.h> 21 21 #include <linux/mfd/da9052/reg.h> 22 22 23 - #define rtc_err(da9052, fmt, ...) \ 24 - dev_err(da9052->dev, "%s: " fmt, __func__, ##__VA_ARGS__) 23 + #define rtc_err(rtc, fmt, ...) \ 24 + dev_err(rtc->da9052->dev, "%s: " fmt, __func__, ##__VA_ARGS__) 25 25 26 26 struct da9052_rtc { 27 27 struct rtc_device *rtc; 28 28 struct da9052 *da9052; 29 29 }; 30 30 31 - static int da9052_rtc_enable_alarm(struct da9052 *da9052, bool enable) 31 + static int da9052_rtc_enable_alarm(struct da9052_rtc *rtc, bool enable) 32 32 { 33 33 int ret; 34 34 if (enable) { 35 - ret = da9052_reg_update(da9052, DA9052_ALARM_Y_REG, 36 - DA9052_ALARM_Y_ALARM_ON, 37 - DA9052_ALARM_Y_ALARM_ON); 35 + ret = da9052_reg_update(rtc->da9052, DA9052_ALARM_Y_REG, 36 + DA9052_ALARM_Y_ALARM_ON|DA9052_ALARM_Y_TICK_ON, 37 + DA9052_ALARM_Y_ALARM_ON); 38 38 if (ret != 0) 39 - rtc_err(da9052, "Failed to enable ALM: %d\n", ret); 39 + rtc_err(rtc, "Failed to enable ALM: %d\n", ret); 40 40 } else { 41 - ret = da9052_reg_update(da9052, DA9052_ALARM_Y_REG, 42 - DA9052_ALARM_Y_ALARM_ON, 0); 41 + ret = da9052_reg_update(rtc->da9052, DA9052_ALARM_Y_REG, 42 + DA9052_ALARM_Y_ALARM_ON|DA9052_ALARM_Y_TICK_ON, 0); 43 43 if (ret != 0) 44 - rtc_err(da9052, "Write error: %d\n", ret); 44 + rtc_err(rtc, "Write error: %d\n", ret); 45 45 } 46 46 return ret; 47 47 } ··· 49 49 static irqreturn_t da9052_rtc_irq(int irq, void *data) 50 50 { 51 51 struct da9052_rtc *rtc = data; 52 - int ret; 53 52 54 - ret = da9052_reg_read(rtc->da9052, DA9052_ALARM_MI_REG); 55 - if (ret < 0) { 56 - rtc_err(rtc->da9052, "Read error: %d\n", ret); 57 - return IRQ_NONE; 58 - } 59 - 60 - if (ret & DA9052_ALARMMI_ALARMTYPE) { 61 - da9052_rtc_enable_alarm(rtc->da9052, 0); 62 - rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_AF); 63 - } else 64 - rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_PF); 53 + rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_AF); 65 54 66 55 return IRQ_HANDLED; 67 56 } 68 57 69 - static int da9052_read_alarm(struct da9052 *da9052, struct rtc_time *rtc_tm) 58 + static int da9052_read_alarm(struct da9052_rtc *rtc, struct rtc_time *rtc_tm) 70 59 { 71 60 int ret; 72 61 uint8_t v[5]; 73 62 74 - ret = da9052_group_read(da9052, DA9052_ALARM_MI_REG, 5, v); 63 + ret = da9052_group_read(rtc->da9052, DA9052_ALARM_MI_REG, 5, v); 75 64 if (ret != 0) { 76 - rtc_err(da9052, "Failed to group read ALM: %d\n", ret); 65 + rtc_err(rtc, "Failed to group read ALM: %d\n", ret); 77 66 return ret; 78 67 } 79 68 ··· 73 84 rtc_tm->tm_min = v[0] & DA9052_RTC_MIN; 74 85 75 86 ret = rtc_valid_tm(rtc_tm); 76 - if (ret != 0) 77 - return ret; 78 87 return ret; 79 88 } 80 89 81 - static int da9052_set_alarm(struct da9052 *da9052, struct rtc_time *rtc_tm) 90 + static int da9052_set_alarm(struct da9052_rtc *rtc, struct rtc_time *rtc_tm) 82 91 { 92 + struct da9052 *da9052 = rtc->da9052; 93 + unsigned long alm_time; 83 94 int ret; 84 95 uint8_t v[3]; 96 + 97 + ret = rtc_tm_to_time(rtc_tm, &alm_time); 98 + if (ret != 0) 99 + return ret; 100 + 101 + if (rtc_tm->tm_sec > 0) { 102 + alm_time += 60 - rtc_tm->tm_sec; 103 + rtc_time_to_tm(alm_time, rtc_tm); 104 + } 105 + BUG_ON(rtc_tm->tm_sec); /* it will cause repeated irqs if not zero */ 85 106 86 107 rtc_tm->tm_year -= 100; 87 108 rtc_tm->tm_mon += 1; ··· 99 100 ret = da9052_reg_update(da9052, DA9052_ALARM_MI_REG, 100 101 DA9052_RTC_MIN, rtc_tm->tm_min); 101 102 if (ret != 0) { 102 - rtc_err(da9052, "Failed to write ALRM MIN: %d\n", ret); 103 + rtc_err(rtc, "Failed to write ALRM MIN: %d\n", ret); 103 104 return ret; 104 105 } 105 106 ··· 114 115 ret = da9052_reg_update(da9052, DA9052_ALARM_Y_REG, 115 116 DA9052_RTC_YEAR, rtc_tm->tm_year); 116 117 if (ret != 0) 117 - rtc_err(da9052, "Failed to write ALRM YEAR: %d\n", ret); 118 + rtc_err(rtc, "Failed to write ALRM YEAR: %d\n", ret); 118 119 119 120 return ret; 120 121 } 121 122 122 - static int da9052_rtc_get_alarm_status(struct da9052 *da9052) 123 + static int da9052_rtc_get_alarm_status(struct da9052_rtc *rtc) 123 124 { 124 125 int ret; 125 126 126 - ret = da9052_reg_read(da9052, DA9052_ALARM_Y_REG); 127 + ret = da9052_reg_read(rtc->da9052, DA9052_ALARM_Y_REG); 127 128 if (ret < 0) { 128 - rtc_err(da9052, "Failed to read ALM: %d\n", ret); 129 + rtc_err(rtc, "Failed to read ALM: %d\n", ret); 129 130 return ret; 130 131 } 131 - ret &= DA9052_ALARM_Y_ALARM_ON; 132 - return (ret > 0) ? 1 : 0; 132 + 133 + return !!(ret&DA9052_ALARM_Y_ALARM_ON); 133 134 } 134 135 135 136 static int da9052_rtc_read_time(struct device *dev, struct rtc_time *rtc_tm) ··· 140 141 141 142 ret = da9052_group_read(rtc->da9052, DA9052_COUNT_S_REG, 6, v); 142 143 if (ret < 0) { 143 - rtc_err(rtc->da9052, "Failed to read RTC time : %d\n", ret); 144 + rtc_err(rtc, "Failed to read RTC time : %d\n", ret); 144 145 return ret; 145 146 } 146 147 ··· 152 153 rtc_tm->tm_sec = v[0] & DA9052_RTC_SEC; 153 154 154 155 ret = rtc_valid_tm(rtc_tm); 155 - if (ret != 0) { 156 - rtc_err(rtc->da9052, "rtc_valid_tm failed: %d\n", ret); 157 - return ret; 158 - } 159 - 160 - return 0; 156 + return ret; 161 157 } 162 158 163 159 static int da9052_rtc_set_time(struct device *dev, struct rtc_time *tm) 164 160 { 165 161 struct da9052_rtc *rtc; 166 162 uint8_t v[6]; 163 + int ret; 167 164 168 165 rtc = dev_get_drvdata(dev); 169 166 ··· 170 175 v[4] = tm->tm_mon + 1; 171 176 v[5] = tm->tm_year - 100; 172 177 173 - return da9052_group_write(rtc->da9052, DA9052_COUNT_S_REG, 6, v); 178 + ret = da9052_group_write(rtc->da9052, DA9052_COUNT_S_REG, 6, v); 179 + if (ret < 0) 180 + rtc_err(rtc, "failed to set RTC time: %d\n", ret); 181 + return ret; 174 182 } 175 183 176 184 static int da9052_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) ··· 182 184 struct rtc_time *tm = &alrm->time; 183 185 struct da9052_rtc *rtc = dev_get_drvdata(dev); 184 186 185 - ret = da9052_read_alarm(rtc->da9052, tm); 186 - 187 - if (ret) 187 + ret = da9052_read_alarm(rtc, tm); 188 + if (ret < 0) { 189 + rtc_err(rtc, "failed to read RTC alarm: %d\n", ret); 188 190 return ret; 191 + } 189 192 190 - alrm->enabled = da9052_rtc_get_alarm_status(rtc->da9052); 191 - 193 + alrm->enabled = da9052_rtc_get_alarm_status(rtc); 192 194 return 0; 193 195 } 194 196 ··· 198 200 struct rtc_time *tm = &alrm->time; 199 201 struct da9052_rtc *rtc = dev_get_drvdata(dev); 200 202 201 - ret = da9052_rtc_enable_alarm(rtc->da9052, 0); 203 + ret = da9052_rtc_enable_alarm(rtc, 0); 202 204 if (ret < 0) 203 205 return ret; 204 206 205 - ret = da9052_set_alarm(rtc->da9052, tm); 206 - if (ret) 207 + ret = da9052_set_alarm(rtc, tm); 208 + if (ret < 0) 207 209 return ret; 208 210 209 - ret = da9052_rtc_enable_alarm(rtc->da9052, 1); 210 - 211 + ret = da9052_rtc_enable_alarm(rtc, 1); 211 212 return ret; 212 213 } 213 214 ··· 214 217 { 215 218 struct da9052_rtc *rtc = dev_get_drvdata(dev); 216 219 217 - return da9052_rtc_enable_alarm(rtc->da9052, enabled); 220 + return da9052_rtc_enable_alarm(rtc, enabled); 218 221 } 219 222 220 223 static const struct rtc_class_ops da9052_rtc_ops = { ··· 236 239 237 240 rtc->da9052 = dev_get_drvdata(pdev->dev.parent); 238 241 platform_set_drvdata(pdev, rtc); 242 + 243 + ret = da9052_reg_write(rtc->da9052, DA9052_BBAT_CONT_REG, 0xFE); 244 + if (ret < 0) { 245 + rtc_err(rtc, 246 + "Failed to setup RTC battery charging: %d\n", ret); 247 + return ret; 248 + } 249 + 250 + ret = da9052_reg_update(rtc->da9052, DA9052_ALARM_Y_REG, 251 + DA9052_ALARM_Y_TICK_ON, 0); 252 + if (ret != 0) 253 + rtc_err(rtc, "Failed to disable TICKS: %d\n", ret); 254 + 239 255 ret = da9052_request_irq(rtc->da9052, DA9052_IRQ_ALARM, "ALM", 240 256 da9052_rtc_irq, rtc); 241 257 if (ret != 0) { 242 - rtc_err(rtc->da9052, "irq registration failed: %d\n", ret); 258 + rtc_err(rtc, "irq registration failed: %d\n", ret); 243 259 return ret; 244 260 } 245 261 ··· 271 261 272 262 module_platform_driver(da9052_rtc_driver); 273 263 274 - MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>"); 264 + MODULE_AUTHOR("Anthony Olech <Anthony.Olech@diasemi.com>"); 275 265 MODULE_DESCRIPTION("RTC driver for Dialog DA9052 PMIC"); 276 266 MODULE_LICENSE("GPL"); 277 267 MODULE_ALIAS("platform:da9052-rtc");