lkml.org 
[lkml]   [2021]   [Aug]   [9]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    /
    From
    SubjectRE: [PATCH] rtc: zynqmp: Add calibration set and get support
    Date
    Hi,

    Is this patch fine?

    Thanks
    Srinivas Neeli

    > -----Original Message-----
    > From: Srinivas Neeli <srinivas.neeli@xilinx.com>
    > Sent: Wednesday, July 14, 2021 1:38 PM
    > To: a.zummo@towertech.it; alexandre.belloni@bootlin.com; Michal Simek
    > <michals@xilinx.com>; Srinivas Goud <sgoud@xilinx.com>; Shubhrajyoti
    > Datta <shubhraj@xilinx.com>
    > Cc: linux-rtc@vger.kernel.org; linux-arm-kernel@lists.infradead.org; linux-
    > kernel@vger.kernel.org; git <git@xilinx.com>; Srinivas Neeli
    > <sneeli@xilinx.com>
    > 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 Neeli <srinivas.neeli@xilinx.com>
    > ---
    > drivers/rtc/rtc-zynqmp.c | 101 ++++++++++++++++++++++++++++++++----
    > ---
    > 1 file changed, 84 insertions(+), 17 deletions(-)
    >
    > diff --git a/drivers/rtc/rtc-zynqmp.c b/drivers/rtc/rtc-zynqmp.c index
    > f440bb52be92..718f60d42760 100644
    > --- a/drivers/rtc/rtc-zynqmp.c
    > +++ b/drivers/rtc/rtc-zynqmp.c
    > @@ -36,10 +36,16 @@
    > #define RTC_OSC_EN BIT(24)
    > #define RTC_BATT_EN BIT(31)
    >
    > -#define RTC_CALIB_DEF 0x198233
    > +#define RTC_CALIB_DEF 0x8000
    > #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;
    > @@ -61,13 +67,6 @@ static int xlnx_rtc_set_time(struct device *dev, struct
    > rtc_time *tm)
    > */
    > new_time = rtc_tm_to_time64(tm) + 1;
    >
    > - /*
    > - * Writing into calibration register will clear the Tick Counter and
    > - * force the next second to be signaled exactly in 1 second period
    > - */
    > - xrtcdev->calibval &= RTC_CALIB_MASK;
    > - writel(xrtcdev->calibval, (xrtcdev->reg_base + RTC_CALIB_WR));
    > -
    > writel(new_time, xrtcdev->reg_base + RTC_SET_TM_WR);
    >
    > /*
    > @@ -174,14 +173,76 @@ static void xlnx_init_rtc(struct xlnx_rtc_dev
    > *xrtcdev)
    > rtc_ctrl |= RTC_BATT_EN;
    > writel(rtc_ctrl, xrtcdev->reg_base + RTC_CTRL);
    >
    > - /*
    > - * Based on crystal freq of 33.330 KHz
    > - * set the seconds counter and enable, set fractions counter
    > - * to default value suggested as per design spec
    > - * to correct RTC delay in frequency over period of time.
    > + /* Update calibvalue */
    > + xrtcdev->calibval = readl(xrtcdev->reg_base + RTC_CALIB_RD); }
    > +
    > +static int xlnx_rtc_read_offset(struct device *dev, long *offset) {
    > + struct xlnx_rtc_dev *xrtcdev = dev_get_drvdata(dev);
    > + long offset_val = 0;
    > + unsigned int tick_mult = RTC_PPB / (xrtcdev->calibval &
    > +RTC_TICK_MASK);
    > +
    > + /* Offset with seconds ticks */
    > + offset_val = xrtcdev->calibval & RTC_TICK_MASK;
    > + offset_val = offset_val - RTC_CALIB_DEF;
    > + offset_val = offset_val * tick_mult;
    > +
    > + /* Offset with fractional ticks */
    > + if (xrtcdev->calibval & RTC_FR_EN)
    > + offset_val += ((xrtcdev->calibval & 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 &
    > +RTC_TICK_MASK);
    > +
    > + /* 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
    > */
    > - xrtcdev->calibval &= RTC_CALIB_MASK;
    > - writel(xrtcdev->calibval, (xrtcdev->reg_base + RTC_CALIB_WR));
    > + calibval = max_tick + RTC_CALIB_DEF;
    > +
    > + if (fract_tick)
    > + calibval |= RTC_FR_EN;
    > +
    > + calibval |= (fract_tick << RTC_FR_DATSHIFT);
    > +
    > + writel(calibval, (xrtcdev->reg_base + RTC_CALIB_WR));
    > + xrtcdev->calibval = calibval;
    > +
    > + return 0;
    > }
    >
    > static const struct rtc_class_ops xlnx_rtc_ops = { @@ -190,6 +251,8 @@
    > static const struct rtc_class_ops xlnx_rtc_ops = {
    > .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) @@ -215,6 +278,7 @@
    > static int xlnx_rtc_probe(struct platform_device *pdev) {
    > struct xlnx_rtc_dev *xrtcdev;
    > int ret;
    > + unsigned int calibval;
    >
    > xrtcdev = devm_kzalloc(&pdev->dev, sizeof(*xrtcdev),
    > GFP_KERNEL);
    > if (!xrtcdev)
    > @@ -256,9 +320,12 @@ static int xlnx_rtc_probe(struct platform_device
    > *pdev)
    > }
    >
    > ret = of_property_read_u32(pdev->dev.of_node, "calibration",
    > - &xrtcdev->calibval);
    > + &calibval);
    > if (ret)
    > - xrtcdev->calibval = RTC_CALIB_DEF;
    > + calibval = RTC_CALIB_DEF;
    > + ret = readl(xrtcdev->reg_base + RTC_CALIB_RD);
    > + if (!ret)
    > + writel(calibval, (xrtcdev->reg_base + RTC_CALIB_WR));
    >
    > xlnx_init_rtc(xrtcdev);
    >
    > --
    > 2.31.1

    \
     
     \ /
      Last update: 2021-08-09 12:06    [W:3.026 / U:0.048 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site