2020-02-20 09:33:34

by Srinivas Neeli

[permalink] [raw]
Subject: [PATCH] rtc: zynqmp: Add calibration set and get support

ZynqMp RTC controller has a calibration feature to compensate
time deviation due to input clock inaccuracy.
Set and get calibration API's are used for setting and getting
calibration value from the controller calibration register.

Signed-off-by: Srinivas Goud <[email protected]>
Signed-off-by: Srinivas Neeli <[email protected]>
---
drivers/rtc/rtc-zynqmp.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 78 insertions(+)

diff --git a/drivers/rtc/rtc-zynqmp.c b/drivers/rtc/rtc-zynqmp.c
index 4b1077e2f826..b4118e9e4fcc 100644
--- a/drivers/rtc/rtc-zynqmp.c
+++ b/drivers/rtc/rtc-zynqmp.c
@@ -40,6 +40,12 @@
#define RTC_CALIB_MASK 0x1FFFFF
#define RTC_ALRM_MASK BIT(1)
#define RTC_MSEC 1000
+#define RTC_FR_MASK 0xF0000
+#define RTC_SEC_MAX_VAL 0xFFFFFFFF
+#define RTC_FR_MAX_TICKS 16
+#define RTC_OFFSET_MAX 150000
+#define RTC_OFFSET_MIN -150000
+#define RTC_PPB 1000000000LL

struct xlnx_rtc_dev {
struct rtc_device *rtc;
@@ -184,12 +190,84 @@ static void xlnx_init_rtc(struct xlnx_rtc_dev *xrtcdev)
writel(xrtcdev->calibval, (xrtcdev->reg_base + RTC_CALIB_WR));
}

+static int xlnx_rtc_read_offset(struct device *dev, long *offset)
+{
+ struct xlnx_rtc_dev *xrtcdev = dev_get_drvdata(dev);
+ long offset_val;
+ unsigned int reg;
+ unsigned int tick_mult = RTC_PPB / xrtcdev->calibval;
+
+ reg = readl(xrtcdev->reg_base + RTC_CALIB_RD);
+
+ /* Offset with seconds ticks */
+ offset_val = reg & RTC_TICK_MASK;
+ offset_val = offset_val - xrtcdev->calibval;
+ offset_val = offset_val * tick_mult;
+
+ /* Offset with fractional ticks */
+ if (reg & RTC_FR_EN)
+ offset_val += ((reg & RTC_FR_MASK) >> RTC_FR_DATSHIFT)
+ * (tick_mult / RTC_FR_MAX_TICKS);
+ *offset = offset_val;
+
+ return 0;
+}
+
+static int xlnx_rtc_set_offset(struct device *dev, long offset)
+{
+ struct xlnx_rtc_dev *xrtcdev = dev_get_drvdata(dev);
+ short int max_tick;
+ unsigned char fract_tick = 0;
+ unsigned int calibval;
+ int fract_offset;
+ unsigned int tick_mult = RTC_PPB / xrtcdev->calibval;
+
+ /* Make sure offset value is within supported range */
+ if (offset < RTC_OFFSET_MIN || offset > RTC_OFFSET_MAX)
+ return -ERANGE;
+
+ /* Number ticks for given offset */
+ max_tick = div_s64_rem(offset, tick_mult, &fract_offset);
+
+ /* Number fractional ticks for given offset */
+ if (fract_offset) {
+ if (fract_offset < 0) {
+ fract_offset = fract_offset + tick_mult;
+ 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;
+ }
+ }
+ }
+
+ /* Zynqmp RTC uses second and fractional tick
+ * counters for compensation
+ */
+ calibval = max_tick + xrtcdev->calibval;
+
+ if (fract_tick)
+ calibval |= RTC_FR_EN;
+
+ calibval |= (fract_tick << RTC_FR_DATSHIFT);
+
+ writel(calibval, (xrtcdev->reg_base + RTC_CALIB_WR));
+
+ return 0;
+}
+
static const struct rtc_class_ops xlnx_rtc_ops = {
.set_time = xlnx_rtc_set_time,
.read_time = xlnx_rtc_read_time,
.read_alarm = xlnx_rtc_read_alarm,
.set_alarm = xlnx_rtc_set_alarm,
.alarm_irq_enable = xlnx_rtc_alarm_irq_enable,
+ .read_offset = xlnx_rtc_read_offset,
+ .set_offset = xlnx_rtc_set_offset,
};

static irqreturn_t xlnx_rtc_interrupt(int irq, void *id)
--
2.7.4


2020-02-24 11:17:50

by Michal Simek

[permalink] [raw]
Subject: Re: [PATCH] rtc: zynqmp: Add calibration set and get support

On 20. 02. 20 10:31, Srinivas Neeli wrote:
> ZynqMp RTC controller has a calibration feature to compensate
> time deviation due to input clock inaccuracy.
> Set and get calibration API's are used for setting and getting
> calibration value from the controller calibration register.
>
> Signed-off-by: Srinivas Goud <[email protected]>
> Signed-off-by: Srinivas Neeli <[email protected]>
> ---
> drivers/rtc/rtc-zynqmp.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 78 insertions(+)
>
> diff --git a/drivers/rtc/rtc-zynqmp.c b/drivers/rtc/rtc-zynqmp.c
> index 4b1077e2f826..b4118e9e4fcc 100644
> --- a/drivers/rtc/rtc-zynqmp.c
> +++ b/drivers/rtc/rtc-zynqmp.c
> @@ -40,6 +40,12 @@
> #define RTC_CALIB_MASK 0x1FFFFF
> #define RTC_ALRM_MASK BIT(1)
> #define RTC_MSEC 1000
> +#define RTC_FR_MASK 0xF0000
> +#define RTC_SEC_MAX_VAL 0xFFFFFFFF
> +#define RTC_FR_MAX_TICKS 16
> +#define RTC_OFFSET_MAX 150000
> +#define RTC_OFFSET_MIN -150000
> +#define RTC_PPB 1000000000LL
>

please use tabs here.

> struct xlnx_rtc_dev {
> struct rtc_device *rtc;
> @@ -184,12 +190,84 @@ static void xlnx_init_rtc(struct xlnx_rtc_dev *xrtcdev)
> writel(xrtcdev->calibval, (xrtcdev->reg_base + RTC_CALIB_WR));
> }
>
> +static int xlnx_rtc_read_offset(struct device *dev, long *offset)
> +{
> + struct xlnx_rtc_dev *xrtcdev = dev_get_drvdata(dev);
> + long offset_val;
> + unsigned int reg;
> + unsigned int tick_mult = RTC_PPB / xrtcdev->calibval;
> +
> + reg = readl(xrtcdev->reg_base + RTC_CALIB_RD);
> +
> + /* Offset with seconds ticks */
> + offset_val = reg & RTC_TICK_MASK;
> + offset_val = offset_val - xrtcdev->calibval;
> + offset_val = offset_val * tick_mult;
> +
> + /* Offset with fractional ticks */
> + if (reg & RTC_FR_EN)
> + offset_val += ((reg & RTC_FR_MASK) >> RTC_FR_DATSHIFT)
> + * (tick_mult / RTC_FR_MAX_TICKS);
> + *offset = offset_val;
> +
> + return 0;
> +}
> +
> +static int xlnx_rtc_set_offset(struct device *dev, long offset)
> +{
> + struct xlnx_rtc_dev *xrtcdev = dev_get_drvdata(dev);
> + short int max_tick;
> + unsigned char fract_tick = 0;
> + unsigned int calibval;

double space.

> + int fract_offset;
> + unsigned int tick_mult = RTC_PPB / xrtcdev->calibval;
> +
> + /* Make sure offset value is within supported range */
> + if (offset < RTC_OFFSET_MIN || offset > RTC_OFFSET_MAX)
> + return -ERANGE;
> +
> + /* Number ticks for given offset */
> + max_tick = div_s64_rem(offset, tick_mult, &fract_offset);
> +
> + /* Number fractional ticks for given offset */
> + if (fract_offset) {
> + if (fract_offset < 0) {
> + fract_offset = fract_offset + tick_mult;
> + 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;
> + }
> + }
> + }
> +
> + /* Zynqmp RTC uses second and fractional tick
> + * counters for compensation
> + */
> + calibval = max_tick + xrtcdev->calibval;
> +
> + if (fract_tick)
> + calibval |= RTC_FR_EN;
> +
> + calibval |= (fract_tick << RTC_FR_DATSHIFT);

here is double space.

> +
> + writel(calibval, (xrtcdev->reg_base + RTC_CALIB_WR));
> +
> + return 0;
> +}
> +
> static const struct rtc_class_ops xlnx_rtc_ops = {
> .set_time = xlnx_rtc_set_time,
> .read_time = xlnx_rtc_read_time,
> .read_alarm = xlnx_rtc_read_alarm,
> .set_alarm = xlnx_rtc_set_alarm,
> .alarm_irq_enable = xlnx_rtc_alarm_irq_enable,
> + .read_offset = xlnx_rtc_read_offset,
> + .set_offset = xlnx_rtc_set_offset,

use tabs as is done above.

> };
>
> static irqreturn_t xlnx_rtc_interrupt(int irq, void *id)
>

The rest looks good.

Thanks,
Michal

2020-02-25 01:20:55

by kernel test robot

[permalink] [raw]
Subject: Re: [PATCH] rtc: zynqmp: Add calibration set and get support

Hi Srinivas,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on abelloni/rtc-next]
[also build test ERROR on next-20200224]
[cannot apply to v5.6-rc3]
[if your patch is applied to the wrong git tree, please drop us a note to help
improve the system. BTW, we also suggest to use '--base' option to specify the
base tree in git format-patch, please see https://stackoverflow.com/a/37406982]

url: https://github.com/0day-ci/linux/commits/Srinivas-Neeli/rtc-zynqmp-Add-calibration-set-and-get-support/20200222-053755
base: https://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux.git rtc-next
config: c6x-randconfig-a001-20200225 (attached as .config)
compiler: c6x-elf-gcc (GCC) 7.5.0
reproduce:
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# save the attached .config to linux build tree
GCC_VERSION=7.5.0 make.cross ARCH=c6x

If you fix the issue, kindly add following tag
Reported-by: kbuild test robot <[email protected]>

All errors (new ones prefixed by >>):

drivers/rtc/rtc-zynqmp.o: In function `xlnx_rtc_read_offset':
>> drivers/rtc/rtc-zynqmp.c:198: undefined reference to `__c6xabi_divlli'
>> drivers/rtc/rtc-zynqmp.c:198: undefined reference to `__c6xabi_divlli'
drivers/rtc/rtc-zynqmp.o: In function `xlnx_rtc_set_offset':
drivers/rtc/rtc-zynqmp.c:223: undefined reference to `__c6xabi_divlli'
drivers/rtc/rtc-zynqmp.c:223: undefined reference to `__c6xabi_divlli'

vim +198 drivers/rtc/rtc-zynqmp.c

192
193 static int xlnx_rtc_read_offset(struct device *dev, long *offset)
194 {
195 struct xlnx_rtc_dev *xrtcdev = dev_get_drvdata(dev);
196 long offset_val;
197 unsigned int reg;
> 198 unsigned int tick_mult = RTC_PPB / xrtcdev->calibval;
199
200 reg = readl(xrtcdev->reg_base + RTC_CALIB_RD);
201
202 /* Offset with seconds ticks */
203 offset_val = reg & RTC_TICK_MASK;
204 offset_val = offset_val - xrtcdev->calibval;
205 offset_val = offset_val * tick_mult;
206
207 /* Offset with fractional ticks */
208 if (reg & RTC_FR_EN)
209 offset_val += ((reg & RTC_FR_MASK) >> RTC_FR_DATSHIFT)
210 * (tick_mult / RTC_FR_MAX_TICKS);
211 *offset = offset_val;
212
213 return 0;
214 }
215

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/[email protected]


Attachments:
(No filename) (2.47 kB)
.config.gz (25.34 kB)
Download all attachments

2020-02-27 11:46:46

by Alexandre Belloni

[permalink] [raw]
Subject: Re: [PATCH] rtc: zynqmp: Add calibration set and get support

Hi,

On 20/02/2020 15:01:46+0530, Srinivas Neeli wrote:
> diff --git a/drivers/rtc/rtc-zynqmp.c b/drivers/rtc/rtc-zynqmp.c
> index 4b1077e2f826..b4118e9e4fcc 100644
> --- a/drivers/rtc/rtc-zynqmp.c
> +++ b/drivers/rtc/rtc-zynqmp.c
> @@ -40,6 +40,12 @@
> #define RTC_CALIB_MASK 0x1FFFFF
> #define RTC_ALRM_MASK BIT(1)
> #define RTC_MSEC 1000
> +#define RTC_FR_MASK 0xF0000
> +#define RTC_SEC_MAX_VAL 0xFFFFFFFF

This value is not used

> +#define RTC_FR_MAX_TICKS 16
> +#define RTC_OFFSET_MAX 150000
> +#define RTC_OFFSET_MIN -150000
> +#define RTC_PPB 1000000000LL
>
> struct xlnx_rtc_dev {
> struct rtc_device *rtc;
> @@ -184,12 +190,84 @@ static void xlnx_init_rtc(struct xlnx_rtc_dev *xrtcdev)
> writel(xrtcdev->calibval, (xrtcdev->reg_base + RTC_CALIB_WR));
> }
>
> +static int xlnx_rtc_read_offset(struct device *dev, long *offset)
> +{
> + struct xlnx_rtc_dev *xrtcdev = dev_get_drvdata(dev);
> + long offset_val;
> + unsigned int reg;
> + unsigned int tick_mult = RTC_PPB / xrtcdev->calibval;
> +

I don't get why you are not simply reusing xrtcdev->calibval. Using
.set_offset has to take precedence on any value that would have been set
using DT. Ideally, the DT binding should be removed too.

Currently, the calibration value is overwritten using the DT value
every time .set_time is called because xrtcdev->calibval is never
updated.

> + reg = readl(xrtcdev->reg_base + RTC_CALIB_RD);
> +
> + /* Offset with seconds ticks */
> + offset_val = reg & RTC_TICK_MASK;
> + offset_val = offset_val - xrtcdev->calibval;
> + offset_val = offset_val * tick_mult;
> +
> + /* Offset with fractional ticks */
> + if (reg & RTC_FR_EN)
> + offset_val += ((reg & RTC_FR_MASK) >> RTC_FR_DATSHIFT)
> + * (tick_mult / RTC_FR_MAX_TICKS);
> + *offset = offset_val;
> +
> + return 0;
> +}
> +
> +static int xlnx_rtc_set_offset(struct device *dev, long offset)
> +{
> + struct xlnx_rtc_dev *xrtcdev = dev_get_drvdata(dev);
> + short int max_tick;
> + unsigned char fract_tick = 0;
> + unsigned int calibval;
> + int fract_offset;
> + unsigned int tick_mult = RTC_PPB / xrtcdev->calibval;
> +
> + /* Make sure offset value is within supported range */
> + if (offset < RTC_OFFSET_MIN || offset > RTC_OFFSET_MAX)
> + return -ERANGE;
> +
> + /* Number ticks for given offset */
> + max_tick = div_s64_rem(offset, tick_mult, &fract_offset);
> +
> + /* Number fractional ticks for given offset */
> + if (fract_offset) {
> + if (fract_offset < 0) {
> + fract_offset = fract_offset + tick_mult;
> + 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;
> + }
> + }
> + }
> +
> + /* Zynqmp RTC uses second and fractional tick
> + * counters for compensation
> + */
> + calibval = max_tick + xrtcdev->calibval;
> +
> + if (fract_tick)
> + calibval |= RTC_FR_EN;
> +
> + calibval |= (fract_tick << RTC_FR_DATSHIFT);
> +
> + writel(calibval, (xrtcdev->reg_base + RTC_CALIB_WR));
> +
> + return 0;
> +}
> +
> static const struct rtc_class_ops xlnx_rtc_ops = {
> .set_time = xlnx_rtc_set_time,
> .read_time = xlnx_rtc_read_time,
> .read_alarm = xlnx_rtc_read_alarm,
> .set_alarm = xlnx_rtc_set_alarm,
> .alarm_irq_enable = xlnx_rtc_alarm_irq_enable,
> + .read_offset = xlnx_rtc_read_offset,
> + .set_offset = xlnx_rtc_set_offset,
> };
>
> static irqreturn_t xlnx_rtc_interrupt(int irq, void *id)
> --
> 2.7.4
>

--
Alexandre Belloni, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com