Skip to content

Commit

Permalink
Merge branch 'timers-core-for-linus' of git://git.kernel.org/pub/scm/…
Browse files Browse the repository at this point in the history
…linux/kernel/git/tip/tip

Pull timer updates from Thomas Gleixner:
 "A rather smalish set of updates for timers and timekeeping:

   - Two core fixes to prevent potential undefinded behaviour about
     which gcc is complaining rightfully.

   - A fix to prevent stopping the tick on an (soon) offline CPU so it
     can complete the shutdown procedure.

   - Wait for clocks to stabilize before making decisions, so a not yet
     validated clock is not rejected.

   - The usual pile of fixes to the various clocksource drivers.

   - Core code typo and include fixlets"

* 'timers-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  timekeeping: Include the correct header for errno definitions
  clocksource/drivers/ti-32k: Prevent ftrace recursion
  clocksource/mips-gic-timer: Stop checking cpu_has_counter
  clocksource/mips-gic-timer: Print an error if IRQ setup fails
  tick/nohz: Prevent stopping the tick on an offline CPU
  clocksource/drivers/oxnas: Add OX820 compatible
  clocksource/drivers/timer-atmel-pit: Simplify IRQ handler
  clocksource/drivers/timer-atmel-pit: Remove uselesss WARN_ON_ONCE
  clocksource/drivers/timer-atmel-pit: Drop at91sam926x_pit_common_init
  clocksource/drivers/moxart: Replace panic by pr_err
  clocksource/drivers/moxart: Replace setup_irq by request_irq
  clocksource/drivers/moxart: Add Aspeed support
  clocksource/drivers/moxart: Use struct to hold state
  clocksource/drivers/moxart: Refactor enable/disable
  time: Avoid undefined behaviour in ktime_add_safe()
  time: Avoid undefined behaviour in timespec64_add_safe()
  timekeeping: Prints the amounts of time spent during suspend
  clocksource: Defer override invalidation unless clock is unstable
  hrtimer: Spelling fixes
  • Loading branch information
torvalds committed Oct 4, 2016
2 parents 49deffe + b536fd5 commit 5e1b834
Show file tree
Hide file tree
Showing 15 changed files with 210 additions and 138 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ MOXA ART timer

Required properties:

- compatible : Must be "moxa,moxart-timer"
- compatible : Must be one of:
- "moxa,moxart-timer"
- "aspeed,ast2400-timer"
- reg : Should contain registers location and length
- interrupts : Should contain the timer interrupt number
- clocks : Should contain phandle for the clock that drives the counter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Oxford Semiconductor OXNAS SoCs Family RPS Timer
================================================

Required properties:
- compatible: Should be "oxsemi,ox810se-rps-timer"
- compatible: Should be "oxsemi,ox810se-rps-timer" or "oxsemi,ox820-rps-timer"
- reg : Specifies base physical address and size of the registers.
- interrupts : The interrupts of the two timers
- clocks : The phandle of the timer clock source
Expand Down
7 changes: 5 additions & 2 deletions drivers/clocksource/mips-gic-timer.c
Original file line number Diff line number Diff line change
Expand Up @@ -109,12 +109,15 @@ static int gic_clockevent_init(void)
{
int ret;

if (!cpu_has_counter || !gic_frequency)
if (!gic_frequency)
return -ENXIO;

ret = setup_percpu_irq(gic_timer_irq, &gic_compare_irqaction);
if (ret < 0)
if (ret < 0) {
pr_err("GIC timer IRQ %d setup failed: %d\n",
gic_timer_irq, ret);
return ret;
}

cpuhp_setup_state(CPUHP_AP_MIPS_GIC_TIMER_STARTING,
"AP_MIPS_GIC_TIMER_STARTING", gic_starting_cpu,
Expand Down
193 changes: 129 additions & 64 deletions drivers/clocksource/moxart_timer.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <linux/io.h>
#include <linux/clocksource.h>
#include <linux/bitops.h>
#include <linux/slab.h>

#define TIMER1_BASE 0x00
#define TIMER2_BASE 0x10
Expand All @@ -36,97 +37,129 @@
#define TIMER_INTR_MASK 0x38

/*
* TIMER_CR flags:
* Moxart TIMER_CR flags:
*
* TIMEREG_CR_*_CLOCK 0: PCLK, 1: EXT1CLK
* TIMEREG_CR_*_INT overflow interrupt enable bit
* MOXART_CR_*_CLOCK 0: PCLK, 1: EXT1CLK
* MOXART_CR_*_INT overflow interrupt enable bit
*/
#define TIMEREG_CR_1_ENABLE BIT(0)
#define TIMEREG_CR_1_CLOCK BIT(1)
#define TIMEREG_CR_1_INT BIT(2)
#define TIMEREG_CR_2_ENABLE BIT(3)
#define TIMEREG_CR_2_CLOCK BIT(4)
#define TIMEREG_CR_2_INT BIT(5)
#define TIMEREG_CR_3_ENABLE BIT(6)
#define TIMEREG_CR_3_CLOCK BIT(7)
#define TIMEREG_CR_3_INT BIT(8)
#define TIMEREG_CR_COUNT_UP BIT(9)

#define TIMER1_ENABLE (TIMEREG_CR_2_ENABLE | TIMEREG_CR_1_ENABLE)
#define TIMER1_DISABLE (TIMEREG_CR_2_ENABLE)

static void __iomem *base;
static unsigned int clock_count_per_tick;
#define MOXART_CR_1_ENABLE BIT(0)
#define MOXART_CR_1_CLOCK BIT(1)
#define MOXART_CR_1_INT BIT(2)
#define MOXART_CR_2_ENABLE BIT(3)
#define MOXART_CR_2_CLOCK BIT(4)
#define MOXART_CR_2_INT BIT(5)
#define MOXART_CR_3_ENABLE BIT(6)
#define MOXART_CR_3_CLOCK BIT(7)
#define MOXART_CR_3_INT BIT(8)
#define MOXART_CR_COUNT_UP BIT(9)

#define MOXART_TIMER1_ENABLE (MOXART_CR_2_ENABLE | MOXART_CR_1_ENABLE)
#define MOXART_TIMER1_DISABLE (MOXART_CR_2_ENABLE)

/*
* The ASpeed variant of the IP block has a different layout
* for the control register
*/
#define ASPEED_CR_1_ENABLE BIT(0)
#define ASPEED_CR_1_CLOCK BIT(1)
#define ASPEED_CR_1_INT BIT(2)
#define ASPEED_CR_2_ENABLE BIT(4)
#define ASPEED_CR_2_CLOCK BIT(5)
#define ASPEED_CR_2_INT BIT(6)
#define ASPEED_CR_3_ENABLE BIT(8)
#define ASPEED_CR_3_CLOCK BIT(9)
#define ASPEED_CR_3_INT BIT(10)

#define ASPEED_TIMER1_ENABLE (ASPEED_CR_2_ENABLE | ASPEED_CR_1_ENABLE)
#define ASPEED_TIMER1_DISABLE (ASPEED_CR_2_ENABLE)

struct moxart_timer {
void __iomem *base;
unsigned int t1_disable_val;
unsigned int t1_enable_val;
unsigned int count_per_tick;
struct clock_event_device clkevt;
};

static inline struct moxart_timer *to_moxart(struct clock_event_device *evt)
{
return container_of(evt, struct moxart_timer, clkevt);
}

static inline void moxart_disable(struct clock_event_device *evt)
{
struct moxart_timer *timer = to_moxart(evt);

writel(timer->t1_disable_val, timer->base + TIMER_CR);
}

static inline void moxart_enable(struct clock_event_device *evt)
{
struct moxart_timer *timer = to_moxart(evt);

writel(timer->t1_enable_val, timer->base + TIMER_CR);
}

static int moxart_shutdown(struct clock_event_device *evt)
{
writel(TIMER1_DISABLE, base + TIMER_CR);
moxart_disable(evt);
return 0;
}

static int moxart_set_oneshot(struct clock_event_device *evt)
{
writel(TIMER1_DISABLE, base + TIMER_CR);
writel(~0, base + TIMER1_BASE + REG_LOAD);
moxart_disable(evt);
writel(~0, to_moxart(evt)->base + TIMER1_BASE + REG_LOAD);
return 0;
}

static int moxart_set_periodic(struct clock_event_device *evt)
{
writel(clock_count_per_tick, base + TIMER1_BASE + REG_LOAD);
writel(TIMER1_ENABLE, base + TIMER_CR);
struct moxart_timer *timer = to_moxart(evt);

moxart_disable(evt);
writel(timer->count_per_tick, timer->base + TIMER1_BASE + REG_LOAD);
writel(0, timer->base + TIMER1_BASE + REG_MATCH1);
moxart_enable(evt);
return 0;
}

static int moxart_clkevt_next_event(unsigned long cycles,
struct clock_event_device *unused)
struct clock_event_device *evt)
{
struct moxart_timer *timer = to_moxart(evt);
u32 u;

writel(TIMER1_DISABLE, base + TIMER_CR);
moxart_disable(evt);

u = readl(base + TIMER1_BASE + REG_COUNT) - cycles;
writel(u, base + TIMER1_BASE + REG_MATCH1);
u = readl(timer->base + TIMER1_BASE + REG_COUNT) - cycles;
writel(u, timer->base + TIMER1_BASE + REG_MATCH1);

writel(TIMER1_ENABLE, base + TIMER_CR);
moxart_enable(evt);

return 0;
}

static struct clock_event_device moxart_clockevent = {
.name = "moxart_timer",
.rating = 200,
.features = CLOCK_EVT_FEAT_PERIODIC |
CLOCK_EVT_FEAT_ONESHOT,
.set_state_shutdown = moxart_shutdown,
.set_state_periodic = moxart_set_periodic,
.set_state_oneshot = moxart_set_oneshot,
.tick_resume = moxart_set_oneshot,
.set_next_event = moxart_clkevt_next_event,
};

static irqreturn_t moxart_timer_interrupt(int irq, void *dev_id)
{
struct clock_event_device *evt = dev_id;
evt->event_handler(evt);
return IRQ_HANDLED;
}

static struct irqaction moxart_timer_irq = {
.name = "moxart-timer",
.flags = IRQF_TIMER,
.handler = moxart_timer_interrupt,
.dev_id = &moxart_clockevent,
};

static int __init moxart_timer_init(struct device_node *node)
{
int ret, irq;
unsigned long pclk;
struct clk *clk;
struct moxart_timer *timer;

timer = kzalloc(sizeof(*timer), GFP_KERNEL);
if (!timer)
return -ENOMEM;

base = of_iomap(node, 0);
if (!base) {
timer->base = of_iomap(node, 0);
if (!timer->base) {
pr_err("%s: of_iomap failed\n", node->full_name);
return -ENXIO;
}
Expand All @@ -137,12 +170,6 @@ static int __init moxart_timer_init(struct device_node *node)
return -EINVAL;
}

ret = setup_irq(irq, &moxart_timer_irq);
if (ret) {
pr_err("%s: setup_irq failed\n", node->full_name);
return ret;
}

clk = of_clk_get(node, 0);
if (IS_ERR(clk)) {
pr_err("%s: of_clk_get failed\n", node->full_name);
Expand All @@ -151,31 +178,69 @@ static int __init moxart_timer_init(struct device_node *node)

pclk = clk_get_rate(clk);

ret = clocksource_mmio_init(base + TIMER2_BASE + REG_COUNT,
if (of_device_is_compatible(node, "moxa,moxart-timer")) {
timer->t1_enable_val = MOXART_TIMER1_ENABLE;
timer->t1_disable_val = MOXART_TIMER1_DISABLE;
} else if (of_device_is_compatible(node, "aspeed,ast2400-timer")) {
timer->t1_enable_val = ASPEED_TIMER1_ENABLE;
timer->t1_disable_val = ASPEED_TIMER1_DISABLE;
} else {
pr_err("%s: unknown platform\n", node->full_name);
return -EINVAL;
}

timer->count_per_tick = DIV_ROUND_CLOSEST(pclk, HZ);

timer->clkevt.name = node->name;
timer->clkevt.rating = 200;
timer->clkevt.features = CLOCK_EVT_FEAT_PERIODIC |
CLOCK_EVT_FEAT_ONESHOT;
timer->clkevt.set_state_shutdown = moxart_shutdown;
timer->clkevt.set_state_periodic = moxart_set_periodic;
timer->clkevt.set_state_oneshot = moxart_set_oneshot;
timer->clkevt.tick_resume = moxart_set_oneshot;
timer->clkevt.set_next_event = moxart_clkevt_next_event;
timer->clkevt.cpumask = cpumask_of(0);
timer->clkevt.irq = irq;

ret = clocksource_mmio_init(timer->base + TIMER2_BASE + REG_COUNT,
"moxart_timer", pclk, 200, 32,
clocksource_mmio_readl_down);
if (ret) {
pr_err("%s: clocksource_mmio_init failed\n", node->full_name);
return ret;
}

clock_count_per_tick = DIV_ROUND_CLOSEST(pclk, HZ);
ret = request_irq(irq, moxart_timer_interrupt, IRQF_TIMER,
node->name, &timer->clkevt);
if (ret) {
pr_err("%s: setup_irq failed\n", node->full_name);
return ret;
}

writel(~0, base + TIMER2_BASE + REG_LOAD);
writel(TIMEREG_CR_2_ENABLE, base + TIMER_CR);
/* Clear match registers */
writel(0, timer->base + TIMER1_BASE + REG_MATCH1);
writel(0, timer->base + TIMER1_BASE + REG_MATCH2);
writel(0, timer->base + TIMER2_BASE + REG_MATCH1);
writel(0, timer->base + TIMER2_BASE + REG_MATCH2);

moxart_clockevent.cpumask = cpumask_of(0);
moxart_clockevent.irq = irq;
/*
* Start timer 2 rolling as our main wall clock source, keep timer 1
* disabled
*/
writel(0, timer->base + TIMER_CR);
writel(~0, timer->base + TIMER2_BASE + REG_LOAD);
writel(timer->t1_disable_val, timer->base + TIMER_CR);

/*
* documentation is not publicly available:
* min_delta / max_delta obtained by trial-and-error,
* max_delta 0xfffffffe should be ok because count
* register size is u32
*/
clockevents_config_and_register(&moxart_clockevent, pclk,
0x4, 0xfffffffe);
clockevents_config_and_register(&timer->clkevt, pclk, 0x4, 0xfffffffe);

return 0;
}
CLOCKSOURCE_OF_DECLARE(moxart, "moxa,moxart-timer", moxart_timer_init);
CLOCKSOURCE_OF_DECLARE(aspeed, "aspeed,ast2400-timer", moxart_timer_init);
Loading

0 comments on commit 5e1b834

Please sign in to comment.