Commit 9f5af702 authored by Tomas Melin's avatar Tomas Melin Committed by Alexandre Belloni
Browse files

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: default avatarHarini T <harini.t@amd.com>
Signed-off-by: default avatarTomas Melin <tomas.melin@vaisala.com>
Reviewed-by: default avatarHarini T <harini.t@amd.com>
Acked-by: default avatarMichal Simek <michal.simek@amd.com>
Link: https://patch.msgid.link/20260122-zynqmp-rtc-updates-v4-4-d4edb966b499@vaisala.com


Signed-off-by: default avatarAlexandre Belloni <alexandre.belloni@bootlin.com>
parent 0f998944
Loading
Loading
Loading
Loading
+13 −20
Original line number Diff line number Diff line
@@ -208,13 +208,13 @@ static int xlnx_rtc_read_offset(struct device *dev, long *offset)
static int xlnx_rtc_set_offset(struct device *dev, long offset)
{
	struct xlnx_rtc_dev *xrtcdev = dev_get_drvdata(dev);
	unsigned long long rtc_ppb = RTC_PPB;
	unsigned int tick_mult = do_div(rtc_ppb, xrtcdev->freq);
	unsigned char fract_tick = 0;
	int max_tick, tick_mult, fract_offset, fract_part;
	int freq = xrtcdev->freq;
	unsigned int calibval;
	short int  max_tick;
	int fract_offset;
	int fract_data = 0;

	/* Tick to offset multiplier */
	tick_mult = DIV_ROUND_CLOSEST(RTC_PPB, freq);
	if (offset < RTC_MIN_OFFSET || offset > RTC_MAX_OFFSET)
		return -ERANGE;

@@ -223,29 +223,22 @@ static int xlnx_rtc_set_offset(struct device *dev, long offset)

	/* Number fractional ticks for given offset */
	if (fract_offset) {
		if (fract_offset < 0) {
			fract_offset = fract_offset + tick_mult;
		fract_part = DIV_ROUND_UP(tick_mult, RTC_FR_MAX_TICKS);
		fract_data = fract_offset / fract_part;
		/* Subtract one from max_tick while adding fract_offset */
		if (fract_offset < 0 && fract_data) {
			max_tick--;
		}
		if (fract_offset > (tick_mult / RTC_FR_MAX_TICKS)) {
			for (fract_tick = 1; fract_tick < 16; fract_tick++) {
				if (fract_offset <=
				    (fract_tick *
				     (tick_mult / RTC_FR_MAX_TICKS)))
					break;
			}
			fract_data += RTC_FR_MAX_TICKS;
		}
	}

	/* Zynqmp RTC uses second and fractional tick
	 * counters for compensation
	 */
	calibval = max_tick + RTC_CALIB_DEF;

	if (fract_tick)
		calibval |= RTC_FR_EN;
	calibval = max_tick + freq;

	calibval |= (fract_tick << RTC_FR_DATSHIFT);
	if (fract_data)
		calibval |= (RTC_FR_EN | (fract_data << RTC_FR_DATSHIFT));

	writel(calibval, (xrtcdev->reg_base + RTC_CALIB_WR));