These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / clocksource / tcb_clksrc.c
index 43f1c6b..5b6f57f 100644 (file)
@@ -73,6 +73,7 @@ static struct clocksource clksrc = {
 struct tc_clkevt_device {
        struct clock_event_device       clkevt;
        struct clk                      *clk;
+       bool                            clk_enabled;
        u32                             freq;
        void __iomem                    *regs;
 };
@@ -84,56 +85,87 @@ static struct tc_clkevt_device *to_tc_clkevt(struct clock_event_device *clkevt)
 
 static u32 timer_clock;
 
-static void tc_mode(enum clock_event_mode m, struct clock_event_device *d)
+static void tc_clk_disable(struct clock_event_device *d)
+{
+       struct tc_clkevt_device *tcd = to_tc_clkevt(d);
+
+       clk_disable(tcd->clk);
+       tcd->clk_enabled = false;
+}
+
+static void tc_clk_enable(struct clock_event_device *d)
+{
+       struct tc_clkevt_device *tcd = to_tc_clkevt(d);
+
+       if (tcd->clk_enabled)
+               return;
+       clk_enable(tcd->clk);
+       tcd->clk_enabled = true;
+}
+
+static int tc_shutdown(struct clock_event_device *d)
 {
        struct tc_clkevt_device *tcd = to_tc_clkevt(d);
        void __iomem            *regs = tcd->regs;
 
-       if (tcd->clkevt.mode == CLOCK_EVT_MODE_PERIODIC
-                       || tcd->clkevt.mode == CLOCK_EVT_MODE_ONESHOT) {
-               __raw_writel(0xff, regs + ATMEL_TC_REG(2, IDR));
-               __raw_writel(ATMEL_TC_CLKDIS, regs + ATMEL_TC_REG(2, CCR));
-               clk_disable(tcd->clk);
-       }
+       __raw_writel(0xff, regs + ATMEL_TC_REG(2, IDR));
+       __raw_writel(ATMEL_TC_CLKDIS, regs + ATMEL_TC_REG(2, CCR));
+       return 0;
+}
+
+static int tc_shutdown_clk_off(struct clock_event_device *d)
+{
+       tc_shutdown(d);
+       if (!clockevent_state_detached(d))
+               tc_clk_disable(d);
+
+       return 0;
+}
+
+static int tc_set_oneshot(struct clock_event_device *d)
+{
+       struct tc_clkevt_device *tcd = to_tc_clkevt(d);
+       void __iomem            *regs = tcd->regs;
+
+       if (clockevent_state_oneshot(d) || clockevent_state_periodic(d))
+               tc_shutdown(d);
+
+       tc_clk_enable(d);
 
-       switch (m) {
+       /* count up to RC, then irq and stop */
+       __raw_writel(timer_clock | ATMEL_TC_CPCSTOP | ATMEL_TC_WAVE |
+                    ATMEL_TC_WAVESEL_UP_AUTO, regs + ATMEL_TC_REG(2, CMR));
+       __raw_writel(ATMEL_TC_CPCS, regs + ATMEL_TC_REG(2, IER));
+
+       /* set_next_event() configures and starts the timer */
+       return 0;
+}
+
+static int tc_set_periodic(struct clock_event_device *d)
+{
+       struct tc_clkevt_device *tcd = to_tc_clkevt(d);
+       void __iomem            *regs = tcd->regs;
+
+       if (clockevent_state_oneshot(d) || clockevent_state_periodic(d))
+               tc_shutdown(d);
 
        /* By not making the gentime core emulate periodic mode on top
         * of oneshot, we get lower overhead and improved accuracy.
         */
-       case CLOCK_EVT_MODE_PERIODIC:
-               clk_enable(tcd->clk);
-
-               /* count up to RC, then irq and restart */
-               __raw_writel(timer_clock
-                               | ATMEL_TC_WAVE | ATMEL_TC_WAVESEL_UP_AUTO,
-                               regs + ATMEL_TC_REG(2, CMR));
-               __raw_writel((tcd->freq + HZ / 2) / HZ,
-                            tcaddr + ATMEL_TC_REG(2, RC));
-
-               /* Enable clock and interrupts on RC compare */
-               __raw_writel(ATMEL_TC_CPCS, regs + ATMEL_TC_REG(2, IER));
-
-               /* go go gadget! */
-               __raw_writel(ATMEL_TC_CLKEN | ATMEL_TC_SWTRG,
-                               regs + ATMEL_TC_REG(2, CCR));
-               break;
-
-       case CLOCK_EVT_MODE_ONESHOT:
-               clk_enable(tcd->clk);
-
-               /* count up to RC, then irq and stop */
-               __raw_writel(timer_clock | ATMEL_TC_CPCSTOP
-                               | ATMEL_TC_WAVE | ATMEL_TC_WAVESEL_UP_AUTO,
-                               regs + ATMEL_TC_REG(2, CMR));
-               __raw_writel(ATMEL_TC_CPCS, regs + ATMEL_TC_REG(2, IER));
-
-               /* set_next_event() configures and starts the timer */
-               break;
-
-       default:
-               break;
-       }
+       tc_clk_enable(d);
+
+       /* count up to RC, then irq and restart */
+       __raw_writel(timer_clock | ATMEL_TC_WAVE | ATMEL_TC_WAVESEL_UP_AUTO,
+                    regs + ATMEL_TC_REG(2, CMR));
+       __raw_writel((tcd->freq + HZ / 2) / HZ, tcaddr + ATMEL_TC_REG(2, RC));
+
+       /* Enable clock and interrupts on RC compare */
+       __raw_writel(ATMEL_TC_CPCS, regs + ATMEL_TC_REG(2, IER));
+
+       /* go go gadget! */
+       __raw_writel(ATMEL_TC_CLKEN | ATMEL_TC_SWTRG, regs +
+                    ATMEL_TC_REG(2, CCR));
+       return 0;
 }
 
 static int tc_next_event(unsigned long delta, struct clock_event_device *d)
@@ -148,17 +180,19 @@ static int tc_next_event(unsigned long delta, struct clock_event_device *d)
 
 static struct tc_clkevt_device clkevt = {
        .clkevt = {
-               .name           = "tc_clkevt",
-               .features       = CLOCK_EVT_FEAT_PERIODIC
-                                       | CLOCK_EVT_FEAT_ONESHOT,
-#ifdef CONFIG_ATMEL_TCB_CLKSRC_USE_SLOW_CLOCK
+               .name                   = "tc_clkevt",
+               .features               = CLOCK_EVT_FEAT_PERIODIC |
+                                         CLOCK_EVT_FEAT_ONESHOT,
                /* Should be lower than at91rm9200's system timer */
-               .rating         = 125,
+#ifdef CONFIG_ATMEL_TCB_CLKSRC_USE_SLOW_CLOCK
+               .rating                 = 125,
 #else
-               .rating         = 200,
+               .rating                 = 200,
 #endif
-               .set_next_event = tc_next_event,
-               .set_mode       = tc_mode,
+               .set_next_event         = tc_next_event,
+               .set_state_shutdown     = tc_shutdown_clk_off,
+               .set_state_periodic     = tc_set_periodic,
+               .set_state_oneshot      = tc_set_oneshot,
        },
 };
 
@@ -183,10 +217,17 @@ static int __init setup_clkevents(struct atmel_tc *tc, int divisor_idx)
        struct clk *t2_clk = tc->clk[2];
        int irq = tc->irq[2];
 
+       ret = clk_prepare_enable(tc->slow_clk);
+       if (ret)
+               return ret;
+
        /* try to enable t2 clk to avoid future errors in mode change */
        ret = clk_prepare_enable(t2_clk);
-       if (ret)
+       if (ret) {
+               clk_disable_unprepare(tc->slow_clk);
                return ret;
+       }
+
        clk_disable(t2_clk);
 
        clkevt.regs = tc->regs;
@@ -202,7 +243,8 @@ static int __init setup_clkevents(struct atmel_tc *tc, int divisor_idx)
 
        ret = request_irq(irq, ch2_irq, IRQF_TIMER, "tc_clkevt", &clkevt);
        if (ret) {
-               clk_disable_unprepare(t2_clk);
+               clk_unprepare(t2_clk);
+               clk_disable_unprepare(tc->slow_clk);
                return ret;
        }