Message ID | 1582191106-30431-1-git-send-email-srinivas.neeli@xilinx.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | rtc: zynqmp: Add calibration set and get support | expand |
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 <srinivas.goud@xilinx.com> > Signed-off-by: Srinivas Neeli <srinivas.neeli@xilinx.com> > --- > 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
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 <lkp@intel.com> 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/kbuild-all@lists.01.org
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 >
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)