diff mbox series

[v5,4/5] rtc: rzn1: Add oscillator offset support

Message ID 20220512143920.238987-5-miquel.raynal@bootlin.com (mailing list archive)
State Superseded
Delegated to: Geert Uytterhoeven
Headers show
Series RZ/N1 RTC support | expand

Commit Message

Miquel Raynal May 12, 2022, 2:39 p.m. UTC
The RZN1 RTC can compensate the imprecision of the oscillator up to
approximately 190ppm.

Seconds can last slightly shorter or longer depending on the
configuration.

Below ~65ppm of correction, we can change the time spent in a second
every minute, which is the most accurate compensation that the RTC can
offer. Above, the compensation will be active every 20s.

Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
 drivers/rtc/rtc-rzn1.c | 73 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 73 insertions(+)

Comments

kernel test robot May 12, 2022, 8:50 p.m. UTC | #1
Hi Miquel,

I love your patch! Perhaps something to improve:

[auto build test WARNING on abelloni/rtc-next]
[also build test WARNING on robh/for-next linus/master v5.18-rc6]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/intel-lab-lkp/linux/commits/Miquel-Raynal/RZ-N1-RTC-support/20220512-224022
base:   https://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux.git rtc-next
config: mips-allyesconfig (https://download.01.org/0day-ci/archive/20220513/202205130445.mjHjgcBv-lkp@intel.com/config)
compiler: mips-linux-gcc (GCC) 11.3.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/intel-lab-lkp/linux/commit/1b2c010278819d2c7aab406d9718a659ead047cf
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Miquel-Raynal/RZ-N1-RTC-support/20220512-224022
        git checkout 1b2c010278819d2c7aab406d9718a659ead047cf
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.3.0 make.cross W=1 O=build_dir ARCH=mips SHELL=/bin/bash drivers/rtc/

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

   drivers/rtc/rtc-rzn1.c: In function 'rzn1_rtc_set_offset':
>> drivers/rtc/rtc-rzn1.c:275:29: warning: variable 'ppb_per_step' set but not used [-Wunused-but-set-variable]
     275 |         unsigned int steps, ppb_per_step;
         |                             ^~~~~~~~~~~~


vim +/ppb_per_step +275 drivers/rtc/rtc-rzn1.c

   271	
   272	static int rzn1_rtc_set_offset(struct device *dev, long offset)
   273	{
   274		struct rzn1_rtc *rtc = dev_get_drvdata(dev);
 > 275		unsigned int steps, ppb_per_step;
   276		int stepsh, stepsl;
   277		u32 val;
   278		int ret;
   279	
   280		/*
   281		 * Check which resolution mode (every 20 or 60s) can be used.
   282		 * Between 2 and 124 clock pulses can be added or substracted.
   283		 *
   284		 * In 20s mode, the minimum resolution is 2 / (32768 * 20) which is
   285		 * close to 3051 ppb. In 60s mode, the resolution is closer to 1017.
   286		 */
   287		stepsh = DIV_ROUND_CLOSEST(offset, 1017);
   288		stepsl = DIV_ROUND_CLOSEST(offset, 3051);
   289	
   290		if (stepsh >= -0x3E && stepsh <= 0x3E) {
   291			ppb_per_step = 1017;
   292			steps = stepsh;
   293			val |= RZN1_RTC_SUBU_DEV;
   294		} else if (stepsl >= -0x3E && stepsl <= 0x3E) {
   295			ppb_per_step = 3051;
   296			steps = stepsl;
   297		} else {
   298			return -ERANGE;
   299		}
   300	
   301		if (!steps)
   302			return 0;
   303	
   304		if (steps > 0) {
   305			val |= steps + 1;
   306		} else {
   307			val |= RZN1_RTC_SUBU_DECR;
   308			val |= (~(-steps - 1)) & 0x3F;
   309		}
   310	
   311		ret = readl_poll_timeout(rtc->base + RZN1_RTC_CTL2, val,
   312					 !(val & RZN1_RTC_CTL2_WUST), 100, 2000000);
   313		if (ret)
   314			return ret;
   315	
   316		writel(val, rtc->base + RZN1_RTC_SUBU);
   317	
   318		return 0;
   319	}
   320
diff mbox series

Patch

diff --git a/drivers/rtc/rtc-rzn1.c b/drivers/rtc/rtc-rzn1.c
index 7ee190daa651..79b72488231c 100644
--- a/drivers/rtc/rtc-rzn1.c
+++ b/drivers/rtc/rtc-rzn1.c
@@ -247,12 +247,85 @@  static int rzn1_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
 	return 0;
 }
 
+static int rzn1_rtc_read_offset(struct device *dev, long *offset)
+{
+	struct rzn1_rtc *rtc = dev_get_drvdata(dev);
+	unsigned int ppb_per_step;
+	bool subtract;
+	u32 val;
+
+	val = readl(rtc->base + RZN1_RTC_SUBU);
+	ppb_per_step = val & RZN1_RTC_SUBU_DEV ? 1017 : 3051;
+	subtract = val & RZN1_RTC_SUBU_DECR;
+	val &= 0x3F;
+
+	if (!val)
+		*offset = 0;
+	else if (subtract)
+		*offset = -(((~val) & 0x3F) + 1) * ppb_per_step;
+	else
+		*offset = (val - 1) * ppb_per_step;
+
+	return 0;
+}
+
+static int rzn1_rtc_set_offset(struct device *dev, long offset)
+{
+	struct rzn1_rtc *rtc = dev_get_drvdata(dev);
+	unsigned int steps, ppb_per_step;
+	int stepsh, stepsl;
+	u32 val;
+	int ret;
+
+	/*
+	 * Check which resolution mode (every 20 or 60s) can be used.
+	 * Between 2 and 124 clock pulses can be added or substracted.
+	 *
+	 * In 20s mode, the minimum resolution is 2 / (32768 * 20) which is
+	 * close to 3051 ppb. In 60s mode, the resolution is closer to 1017.
+	 */
+	stepsh = DIV_ROUND_CLOSEST(offset, 1017);
+	stepsl = DIV_ROUND_CLOSEST(offset, 3051);
+
+	if (stepsh >= -0x3E && stepsh <= 0x3E) {
+		ppb_per_step = 1017;
+		steps = stepsh;
+		val |= RZN1_RTC_SUBU_DEV;
+	} else if (stepsl >= -0x3E && stepsl <= 0x3E) {
+		ppb_per_step = 3051;
+		steps = stepsl;
+	} else {
+		return -ERANGE;
+	}
+
+	if (!steps)
+		return 0;
+
+	if (steps > 0) {
+		val |= steps + 1;
+	} else {
+		val |= RZN1_RTC_SUBU_DECR;
+		val |= (~(-steps - 1)) & 0x3F;
+	}
+
+	ret = readl_poll_timeout(rtc->base + RZN1_RTC_CTL2, val,
+				 !(val & RZN1_RTC_CTL2_WUST), 100, 2000000);
+	if (ret)
+		return ret;
+
+	writel(val, rtc->base + RZN1_RTC_SUBU);
+
+	return 0;
+}
+
 static const struct rtc_class_ops rzn1_rtc_ops = {
 	.read_time = rzn1_rtc_read_time,
 	.set_time = rzn1_rtc_set_time,
 	.read_alarm = rzn1_rtc_read_alarm,
 	.set_alarm = rzn1_rtc_set_alarm,
 	.alarm_irq_enable = rzn1_rtc_alarm_irq_enable,
+	.read_offset = rzn1_rtc_read_offset,
+	.set_offset = rzn1_rtc_set_offset,
 };
 
 static int rzn1_rtc_probe(struct platform_device *pdev)