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

rtc: zynqmp: rework set_offset

set_offset was using remainder of do_div as tick_mult which resulted in
wrong offset. Calibration value also assumed builtin calibration default.
Update fract_offset to correctly calculate the value for
negative offset and replace the for loop with division.

Tested-by: Harini T <harini.t@amd.com>
Signed-off-by: Tomas Melin <tomas.melin@vaisala.com>
Reviewed-by: Harini T <harini.t@amd.com>
Acked-by: Michal Simek <michal.simek@amd.com>
Link: https://patch.msgid.link/20260122-zynqmp-rtc-updates-v4-4-d4edb966b499@vaisala.com
Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>

authored by

Tomas Melin and committed by
Alexandre Belloni
9f5af702 0f998944

+13 -20
+13 -20
drivers/rtc/rtc-zynqmp.c
··· 208 208 static int xlnx_rtc_set_offset(struct device *dev, long offset) 209 209 { 210 210 struct xlnx_rtc_dev *xrtcdev = dev_get_drvdata(dev); 211 - unsigned long long rtc_ppb = RTC_PPB; 212 - unsigned int tick_mult = do_div(rtc_ppb, xrtcdev->freq); 213 - unsigned char fract_tick = 0; 211 + int max_tick, tick_mult, fract_offset, fract_part; 212 + int freq = xrtcdev->freq; 214 213 unsigned int calibval; 215 - short int max_tick; 216 - int fract_offset; 214 + int fract_data = 0; 217 215 216 + /* Tick to offset multiplier */ 217 + tick_mult = DIV_ROUND_CLOSEST(RTC_PPB, freq); 218 218 if (offset < RTC_MIN_OFFSET || offset > RTC_MAX_OFFSET) 219 219 return -ERANGE; 220 220 ··· 223 223 224 224 /* Number fractional ticks for given offset */ 225 225 if (fract_offset) { 226 - if (fract_offset < 0) { 227 - fract_offset = fract_offset + tick_mult; 226 + fract_part = DIV_ROUND_UP(tick_mult, RTC_FR_MAX_TICKS); 227 + fract_data = fract_offset / fract_part; 228 + /* Subtract one from max_tick while adding fract_offset */ 229 + if (fract_offset < 0 && fract_data) { 228 230 max_tick--; 229 - } 230 - if (fract_offset > (tick_mult / RTC_FR_MAX_TICKS)) { 231 - for (fract_tick = 1; fract_tick < 16; fract_tick++) { 232 - if (fract_offset <= 233 - (fract_tick * 234 - (tick_mult / RTC_FR_MAX_TICKS))) 235 - break; 236 - } 231 + fract_data += RTC_FR_MAX_TICKS; 237 232 } 238 233 } 239 234 240 235 /* Zynqmp RTC uses second and fractional tick 241 236 * counters for compensation 242 237 */ 243 - calibval = max_tick + RTC_CALIB_DEF; 238 + calibval = max_tick + freq; 244 239 245 - if (fract_tick) 246 - calibval |= RTC_FR_EN; 247 - 248 - calibval |= (fract_tick << RTC_FR_DATSHIFT); 240 + if (fract_data) 241 + calibval |= (RTC_FR_EN | (fract_data << RTC_FR_DATSHIFT)); 249 242 250 243 writel(calibval, (xrtcdev->reg_base + RTC_CALIB_WR)); 251 244