From dc2eadece70089430f12e4ed6bb1a4421cf3d6f4 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 17 Feb 2014 11:27:49 +0100 Subject: clocksource: sh_cmt: Use request_irq() instead of setup_irq() The driver claims it needs to register an interrupt handler too early for request_irq(). This might have been true in the past, but the only meaningful difference between request_irq() and setup_irq() today is an additional kzalloc() call in request_irq(). As the driver calls kmalloc() itself we know that the slab allocator is available, we can thus switch to request_irq(). Signed-off-by: Laurent Pinchart --- drivers/clocksource/sh_cmt.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c index 0b1836a6c539..a3103b871260 100644 --- a/drivers/clocksource/sh_cmt.c +++ b/drivers/clocksource/sh_cmt.c @@ -728,12 +728,6 @@ static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev) goto err1; } - /* request irq using setup_irq() (too early for request_irq()) */ - p->irqaction.name = dev_name(&p->pdev->dev); - p->irqaction.handler = sh_cmt_interrupt; - p->irqaction.dev_id = p; - p->irqaction.flags = IRQF_TIMER | IRQF_IRQPOLL | IRQF_NOBALANCING; - /* get hold of clock */ p->clk = clk_get(&p->pdev->dev, "cmt_fck"); if (IS_ERR(p->clk)) { @@ -786,7 +780,9 @@ static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev) } p->cs_enabled = false; - ret = setup_irq(irq, &p->irqaction); + ret = request_irq(irq, sh_cmt_interrupt, + IRQF_TIMER | IRQF_IRQPOLL | IRQF_NOBALANCING, + dev_name(&p->pdev->dev), p); if (ret) { dev_err(&p->pdev->dev, "failed to request irq %d\n", irq); goto err4; -- cgit From 7269f9333292586f2378c5321b40a8d3779c4653 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 27 Jan 2014 15:29:19 +0100 Subject: clocksource: sh_cmt: Split channel fields from sh_cmt_priv Create a new sh_cmt_channel structure to hold the channel-specific field in preparation for multiple channels per device support. Signed-off-by: Laurent Pinchart --- drivers/clocksource/sh_cmt.c | 385 ++++++++++++++++++++++--------------------- 1 file changed, 199 insertions(+), 186 deletions(-) diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c index a3103b871260..351b3ca3ccc4 100644 --- a/drivers/clocksource/sh_cmt.c +++ b/drivers/clocksource/sh_cmt.c @@ -35,15 +35,10 @@ #include #include -struct sh_cmt_priv { - void __iomem *mapbase; - void __iomem *mapbase_str; - struct clk *clk; - unsigned long width; /* 16 or 32 bit version of hardware block */ - unsigned long overflow_bit; - unsigned long clear_bits; - struct irqaction irqaction; - struct platform_device *pdev; +struct sh_cmt_priv; + +struct sh_cmt_channel { + struct sh_cmt_priv *cmt; unsigned long flags; unsigned long match_value; @@ -55,6 +50,20 @@ struct sh_cmt_priv { struct clocksource cs; unsigned long total_cycles; bool cs_enabled; +}; + +struct sh_cmt_priv { + struct platform_device *pdev; + + void __iomem *mapbase; + void __iomem *mapbase_str; + struct clk *clk; + + struct sh_cmt_channel channel; + + unsigned long width; /* 16 or 32 bit version of hardware block */ + unsigned long overflow_bit; + unsigned long clear_bits; /* callbacks for CMSTR and CMCSR access */ unsigned long (*read_control)(void __iomem *base, unsigned long offs); @@ -114,60 +123,60 @@ static void sh_cmt_write32(void __iomem *base, unsigned long offs, #define CMCNT 1 /* channel register */ #define CMCOR 2 /* channel register */ -static inline unsigned long sh_cmt_read_cmstr(struct sh_cmt_priv *p) +static inline unsigned long sh_cmt_read_cmstr(struct sh_cmt_channel *ch) { - return p->read_control(p->mapbase_str, 0); + return ch->cmt->read_control(ch->cmt->mapbase_str, 0); } -static inline unsigned long sh_cmt_read_cmcsr(struct sh_cmt_priv *p) +static inline unsigned long sh_cmt_read_cmcsr(struct sh_cmt_channel *ch) { - return p->read_control(p->mapbase, CMCSR); + return ch->cmt->read_control(ch->cmt->mapbase, CMCSR); } -static inline unsigned long sh_cmt_read_cmcnt(struct sh_cmt_priv *p) +static inline unsigned long sh_cmt_read_cmcnt(struct sh_cmt_channel *ch) { - return p->read_count(p->mapbase, CMCNT); + return ch->cmt->read_count(ch->cmt->mapbase, CMCNT); } -static inline void sh_cmt_write_cmstr(struct sh_cmt_priv *p, +static inline void sh_cmt_write_cmstr(struct sh_cmt_channel *ch, unsigned long value) { - p->write_control(p->mapbase_str, 0, value); + ch->cmt->write_control(ch->cmt->mapbase_str, 0, value); } -static inline void sh_cmt_write_cmcsr(struct sh_cmt_priv *p, +static inline void sh_cmt_write_cmcsr(struct sh_cmt_channel *ch, unsigned long value) { - p->write_control(p->mapbase, CMCSR, value); + ch->cmt->write_control(ch->cmt->mapbase, CMCSR, value); } -static inline void sh_cmt_write_cmcnt(struct sh_cmt_priv *p, +static inline void sh_cmt_write_cmcnt(struct sh_cmt_channel *ch, unsigned long value) { - p->write_count(p->mapbase, CMCNT, value); + ch->cmt->write_count(ch->cmt->mapbase, CMCNT, value); } -static inline void sh_cmt_write_cmcor(struct sh_cmt_priv *p, +static inline void sh_cmt_write_cmcor(struct sh_cmt_channel *ch, unsigned long value) { - p->write_count(p->mapbase, CMCOR, value); + ch->cmt->write_count(ch->cmt->mapbase, CMCOR, value); } -static unsigned long sh_cmt_get_counter(struct sh_cmt_priv *p, +static unsigned long sh_cmt_get_counter(struct sh_cmt_channel *ch, int *has_wrapped) { unsigned long v1, v2, v3; int o1, o2; - o1 = sh_cmt_read_cmcsr(p) & p->overflow_bit; + o1 = sh_cmt_read_cmcsr(ch) & ch->cmt->overflow_bit; /* Make sure the timer value is stable. Stolen from acpi_pm.c */ do { o2 = o1; - v1 = sh_cmt_read_cmcnt(p); - v2 = sh_cmt_read_cmcnt(p); - v3 = sh_cmt_read_cmcnt(p); - o1 = sh_cmt_read_cmcsr(p) & p->overflow_bit; + v1 = sh_cmt_read_cmcnt(ch); + v2 = sh_cmt_read_cmcnt(ch); + v3 = sh_cmt_read_cmcnt(ch); + o1 = sh_cmt_read_cmcsr(ch) & ch->cmt->overflow_bit; } while (unlikely((o1 != o2) || (v1 > v2 && v1 < v3) || (v2 > v3 && v2 < v1) || (v3 > v1 && v3 < v2))); @@ -177,52 +186,52 @@ static unsigned long sh_cmt_get_counter(struct sh_cmt_priv *p, static DEFINE_RAW_SPINLOCK(sh_cmt_lock); -static void sh_cmt_start_stop_ch(struct sh_cmt_priv *p, int start) +static void sh_cmt_start_stop_ch(struct sh_cmt_channel *ch, int start) { - struct sh_timer_config *cfg = p->pdev->dev.platform_data; + struct sh_timer_config *cfg = ch->cmt->pdev->dev.platform_data; unsigned long flags, value; /* start stop register shared by multiple timer channels */ raw_spin_lock_irqsave(&sh_cmt_lock, flags); - value = sh_cmt_read_cmstr(p); + value = sh_cmt_read_cmstr(ch); if (start) value |= 1 << cfg->timer_bit; else value &= ~(1 << cfg->timer_bit); - sh_cmt_write_cmstr(p, value); + sh_cmt_write_cmstr(ch, value); raw_spin_unlock_irqrestore(&sh_cmt_lock, flags); } -static int sh_cmt_enable(struct sh_cmt_priv *p, unsigned long *rate) +static int sh_cmt_enable(struct sh_cmt_channel *ch, unsigned long *rate) { int k, ret; - pm_runtime_get_sync(&p->pdev->dev); - dev_pm_syscore_device(&p->pdev->dev, true); + pm_runtime_get_sync(&ch->cmt->pdev->dev); + dev_pm_syscore_device(&ch->cmt->pdev->dev, true); /* enable clock */ - ret = clk_enable(p->clk); + ret = clk_enable(ch->cmt->clk); if (ret) { - dev_err(&p->pdev->dev, "cannot enable clock\n"); + dev_err(&ch->cmt->pdev->dev, "cannot enable clock\n"); goto err0; } /* make sure channel is disabled */ - sh_cmt_start_stop_ch(p, 0); + sh_cmt_start_stop_ch(ch, 0); /* configure channel, periodic mode and maximum timeout */ - if (p->width == 16) { - *rate = clk_get_rate(p->clk) / 512; - sh_cmt_write_cmcsr(p, 0x43); + if (ch->cmt->width == 16) { + *rate = clk_get_rate(ch->cmt->clk) / 512; + sh_cmt_write_cmcsr(ch, 0x43); } else { - *rate = clk_get_rate(p->clk) / 8; - sh_cmt_write_cmcsr(p, 0x01a4); + *rate = clk_get_rate(ch->cmt->clk) / 8; + sh_cmt_write_cmcsr(ch, 0x01a4); } - sh_cmt_write_cmcor(p, 0xffffffff); - sh_cmt_write_cmcnt(p, 0); + sh_cmt_write_cmcor(ch, 0xffffffff); + sh_cmt_write_cmcnt(ch, 0); /* * According to the sh73a0 user's manual, as CMCNT can be operated @@ -236,41 +245,41 @@ static int sh_cmt_enable(struct sh_cmt_priv *p, unsigned long *rate) * take RCLKx2 at maximum. */ for (k = 0; k < 100; k++) { - if (!sh_cmt_read_cmcnt(p)) + if (!sh_cmt_read_cmcnt(ch)) break; udelay(1); } - if (sh_cmt_read_cmcnt(p)) { - dev_err(&p->pdev->dev, "cannot clear CMCNT\n"); + if (sh_cmt_read_cmcnt(ch)) { + dev_err(&ch->cmt->pdev->dev, "cannot clear CMCNT\n"); ret = -ETIMEDOUT; goto err1; } /* enable channel */ - sh_cmt_start_stop_ch(p, 1); + sh_cmt_start_stop_ch(ch, 1); return 0; err1: /* stop clock */ - clk_disable(p->clk); + clk_disable(ch->cmt->clk); err0: return ret; } -static void sh_cmt_disable(struct sh_cmt_priv *p) +static void sh_cmt_disable(struct sh_cmt_channel *ch) { /* disable channel */ - sh_cmt_start_stop_ch(p, 0); + sh_cmt_start_stop_ch(ch, 0); /* disable interrupts in CMT block */ - sh_cmt_write_cmcsr(p, 0); + sh_cmt_write_cmcsr(ch, 0); /* stop clock */ - clk_disable(p->clk); + clk_disable(ch->cmt->clk); - dev_pm_syscore_device(&p->pdev->dev, false); - pm_runtime_put(&p->pdev->dev); + dev_pm_syscore_device(&ch->cmt->pdev->dev, false); + pm_runtime_put(&ch->cmt->pdev->dev); } /* private flags */ @@ -280,24 +289,24 @@ static void sh_cmt_disable(struct sh_cmt_priv *p) #define FLAG_SKIPEVENT (1 << 3) #define FLAG_IRQCONTEXT (1 << 4) -static void sh_cmt_clock_event_program_verify(struct sh_cmt_priv *p, +static void sh_cmt_clock_event_program_verify(struct sh_cmt_channel *ch, int absolute) { unsigned long new_match; - unsigned long value = p->next_match_value; + unsigned long value = ch->next_match_value; unsigned long delay = 0; unsigned long now = 0; int has_wrapped; - now = sh_cmt_get_counter(p, &has_wrapped); - p->flags |= FLAG_REPROGRAM; /* force reprogram */ + now = sh_cmt_get_counter(ch, &has_wrapped); + ch->flags |= FLAG_REPROGRAM; /* force reprogram */ if (has_wrapped) { /* we're competing with the interrupt handler. * -> let the interrupt handler reprogram the timer. * -> interrupt number two handles the event. */ - p->flags |= FLAG_SKIPEVENT; + ch->flags |= FLAG_SKIPEVENT; return; } @@ -309,20 +318,20 @@ static void sh_cmt_clock_event_program_verify(struct sh_cmt_priv *p, * but don't save the new match value yet. */ new_match = now + value + delay; - if (new_match > p->max_match_value) - new_match = p->max_match_value; + if (new_match > ch->max_match_value) + new_match = ch->max_match_value; - sh_cmt_write_cmcor(p, new_match); + sh_cmt_write_cmcor(ch, new_match); - now = sh_cmt_get_counter(p, &has_wrapped); - if (has_wrapped && (new_match > p->match_value)) { + now = sh_cmt_get_counter(ch, &has_wrapped); + if (has_wrapped && (new_match > ch->match_value)) { /* we are changing to a greater match value, * so this wrap must be caused by the counter * matching the old value. * -> first interrupt reprograms the timer. * -> interrupt number two handles the event. */ - p->flags |= FLAG_SKIPEVENT; + ch->flags |= FLAG_SKIPEVENT; break; } @@ -333,7 +342,7 @@ static void sh_cmt_clock_event_program_verify(struct sh_cmt_priv *p, * -> save programmed match value. * -> let isr handle the event. */ - p->match_value = new_match; + ch->match_value = new_match; break; } @@ -344,7 +353,7 @@ static void sh_cmt_clock_event_program_verify(struct sh_cmt_priv *p, * -> save programmed match value. * -> let isr handle the event. */ - p->match_value = new_match; + ch->match_value = new_match; break; } @@ -360,138 +369,138 @@ static void sh_cmt_clock_event_program_verify(struct sh_cmt_priv *p, delay = 1; if (!delay) - dev_warn(&p->pdev->dev, "too long delay\n"); + dev_warn(&ch->cmt->pdev->dev, "too long delay\n"); } while (delay); } -static void __sh_cmt_set_next(struct sh_cmt_priv *p, unsigned long delta) +static void __sh_cmt_set_next(struct sh_cmt_channel *ch, unsigned long delta) { - if (delta > p->max_match_value) - dev_warn(&p->pdev->dev, "delta out of range\n"); + if (delta > ch->max_match_value) + dev_warn(&ch->cmt->pdev->dev, "delta out of range\n"); - p->next_match_value = delta; - sh_cmt_clock_event_program_verify(p, 0); + ch->next_match_value = delta; + sh_cmt_clock_event_program_verify(ch, 0); } -static void sh_cmt_set_next(struct sh_cmt_priv *p, unsigned long delta) +static void sh_cmt_set_next(struct sh_cmt_channel *ch, unsigned long delta) { unsigned long flags; - raw_spin_lock_irqsave(&p->lock, flags); - __sh_cmt_set_next(p, delta); - raw_spin_unlock_irqrestore(&p->lock, flags); + raw_spin_lock_irqsave(&ch->lock, flags); + __sh_cmt_set_next(ch, delta); + raw_spin_unlock_irqrestore(&ch->lock, flags); } static irqreturn_t sh_cmt_interrupt(int irq, void *dev_id) { - struct sh_cmt_priv *p = dev_id; + struct sh_cmt_channel *ch = dev_id; /* clear flags */ - sh_cmt_write_cmcsr(p, sh_cmt_read_cmcsr(p) & p->clear_bits); + sh_cmt_write_cmcsr(ch, sh_cmt_read_cmcsr(ch) & ch->cmt->clear_bits); /* update clock source counter to begin with if enabled * the wrap flag should be cleared by the timer specific * isr before we end up here. */ - if (p->flags & FLAG_CLOCKSOURCE) - p->total_cycles += p->match_value + 1; + if (ch->flags & FLAG_CLOCKSOURCE) + ch->total_cycles += ch->match_value + 1; - if (!(p->flags & FLAG_REPROGRAM)) - p->next_match_value = p->max_match_value; + if (!(ch->flags & FLAG_REPROGRAM)) + ch->next_match_value = ch->max_match_value; - p->flags |= FLAG_IRQCONTEXT; + ch->flags |= FLAG_IRQCONTEXT; - if (p->flags & FLAG_CLOCKEVENT) { - if (!(p->flags & FLAG_SKIPEVENT)) { - if (p->ced.mode == CLOCK_EVT_MODE_ONESHOT) { - p->next_match_value = p->max_match_value; - p->flags |= FLAG_REPROGRAM; + if (ch->flags & FLAG_CLOCKEVENT) { + if (!(ch->flags & FLAG_SKIPEVENT)) { + if (ch->ced.mode == CLOCK_EVT_MODE_ONESHOT) { + ch->next_match_value = ch->max_match_value; + ch->flags |= FLAG_REPROGRAM; } - p->ced.event_handler(&p->ced); + ch->ced.event_handler(&ch->ced); } } - p->flags &= ~FLAG_SKIPEVENT; + ch->flags &= ~FLAG_SKIPEVENT; - if (p->flags & FLAG_REPROGRAM) { - p->flags &= ~FLAG_REPROGRAM; - sh_cmt_clock_event_program_verify(p, 1); + if (ch->flags & FLAG_REPROGRAM) { + ch->flags &= ~FLAG_REPROGRAM; + sh_cmt_clock_event_program_verify(ch, 1); - if (p->flags & FLAG_CLOCKEVENT) - if ((p->ced.mode == CLOCK_EVT_MODE_SHUTDOWN) - || (p->match_value == p->next_match_value)) - p->flags &= ~FLAG_REPROGRAM; + if (ch->flags & FLAG_CLOCKEVENT) + if ((ch->ced.mode == CLOCK_EVT_MODE_SHUTDOWN) + || (ch->match_value == ch->next_match_value)) + ch->flags &= ~FLAG_REPROGRAM; } - p->flags &= ~FLAG_IRQCONTEXT; + ch->flags &= ~FLAG_IRQCONTEXT; return IRQ_HANDLED; } -static int sh_cmt_start(struct sh_cmt_priv *p, unsigned long flag) +static int sh_cmt_start(struct sh_cmt_channel *ch, unsigned long flag) { int ret = 0; unsigned long flags; - raw_spin_lock_irqsave(&p->lock, flags); + raw_spin_lock_irqsave(&ch->lock, flags); - if (!(p->flags & (FLAG_CLOCKEVENT | FLAG_CLOCKSOURCE))) - ret = sh_cmt_enable(p, &p->rate); + if (!(ch->flags & (FLAG_CLOCKEVENT | FLAG_CLOCKSOURCE))) + ret = sh_cmt_enable(ch, &ch->rate); if (ret) goto out; - p->flags |= flag; + ch->flags |= flag; /* setup timeout if no clockevent */ - if ((flag == FLAG_CLOCKSOURCE) && (!(p->flags & FLAG_CLOCKEVENT))) - __sh_cmt_set_next(p, p->max_match_value); + if ((flag == FLAG_CLOCKSOURCE) && (!(ch->flags & FLAG_CLOCKEVENT))) + __sh_cmt_set_next(ch, ch->max_match_value); out: - raw_spin_unlock_irqrestore(&p->lock, flags); + raw_spin_unlock_irqrestore(&ch->lock, flags); return ret; } -static void sh_cmt_stop(struct sh_cmt_priv *p, unsigned long flag) +static void sh_cmt_stop(struct sh_cmt_channel *ch, unsigned long flag) { unsigned long flags; unsigned long f; - raw_spin_lock_irqsave(&p->lock, flags); + raw_spin_lock_irqsave(&ch->lock, flags); - f = p->flags & (FLAG_CLOCKEVENT | FLAG_CLOCKSOURCE); - p->flags &= ~flag; + f = ch->flags & (FLAG_CLOCKEVENT | FLAG_CLOCKSOURCE); + ch->flags &= ~flag; - if (f && !(p->flags & (FLAG_CLOCKEVENT | FLAG_CLOCKSOURCE))) - sh_cmt_disable(p); + if (f && !(ch->flags & (FLAG_CLOCKEVENT | FLAG_CLOCKSOURCE))) + sh_cmt_disable(ch); /* adjust the timeout to maximum if only clocksource left */ - if ((flag == FLAG_CLOCKEVENT) && (p->flags & FLAG_CLOCKSOURCE)) - __sh_cmt_set_next(p, p->max_match_value); + if ((flag == FLAG_CLOCKEVENT) && (ch->flags & FLAG_CLOCKSOURCE)) + __sh_cmt_set_next(ch, ch->max_match_value); - raw_spin_unlock_irqrestore(&p->lock, flags); + raw_spin_unlock_irqrestore(&ch->lock, flags); } -static struct sh_cmt_priv *cs_to_sh_cmt(struct clocksource *cs) +static struct sh_cmt_channel *cs_to_sh_cmt(struct clocksource *cs) { - return container_of(cs, struct sh_cmt_priv, cs); + return container_of(cs, struct sh_cmt_channel, cs); } static cycle_t sh_cmt_clocksource_read(struct clocksource *cs) { - struct sh_cmt_priv *p = cs_to_sh_cmt(cs); + struct sh_cmt_channel *ch = cs_to_sh_cmt(cs); unsigned long flags, raw; unsigned long value; int has_wrapped; - raw_spin_lock_irqsave(&p->lock, flags); - value = p->total_cycles; - raw = sh_cmt_get_counter(p, &has_wrapped); + raw_spin_lock_irqsave(&ch->lock, flags); + value = ch->total_cycles; + raw = sh_cmt_get_counter(ch, &has_wrapped); if (unlikely(has_wrapped)) - raw += p->match_value + 1; - raw_spin_unlock_irqrestore(&p->lock, flags); + raw += ch->match_value + 1; + raw_spin_unlock_irqrestore(&ch->lock, flags); return value + raw; } @@ -499,50 +508,50 @@ static cycle_t sh_cmt_clocksource_read(struct clocksource *cs) static int sh_cmt_clocksource_enable(struct clocksource *cs) { int ret; - struct sh_cmt_priv *p = cs_to_sh_cmt(cs); + struct sh_cmt_channel *ch = cs_to_sh_cmt(cs); - WARN_ON(p->cs_enabled); + WARN_ON(ch->cs_enabled); - p->total_cycles = 0; + ch->total_cycles = 0; - ret = sh_cmt_start(p, FLAG_CLOCKSOURCE); + ret = sh_cmt_start(ch, FLAG_CLOCKSOURCE); if (!ret) { - __clocksource_updatefreq_hz(cs, p->rate); - p->cs_enabled = true; + __clocksource_updatefreq_hz(cs, ch->rate); + ch->cs_enabled = true; } return ret; } static void sh_cmt_clocksource_disable(struct clocksource *cs) { - struct sh_cmt_priv *p = cs_to_sh_cmt(cs); + struct sh_cmt_channel *ch = cs_to_sh_cmt(cs); - WARN_ON(!p->cs_enabled); + WARN_ON(!ch->cs_enabled); - sh_cmt_stop(p, FLAG_CLOCKSOURCE); - p->cs_enabled = false; + sh_cmt_stop(ch, FLAG_CLOCKSOURCE); + ch->cs_enabled = false; } static void sh_cmt_clocksource_suspend(struct clocksource *cs) { - struct sh_cmt_priv *p = cs_to_sh_cmt(cs); + struct sh_cmt_channel *ch = cs_to_sh_cmt(cs); - sh_cmt_stop(p, FLAG_CLOCKSOURCE); - pm_genpd_syscore_poweroff(&p->pdev->dev); + sh_cmt_stop(ch, FLAG_CLOCKSOURCE); + pm_genpd_syscore_poweroff(&ch->cmt->pdev->dev); } static void sh_cmt_clocksource_resume(struct clocksource *cs) { - struct sh_cmt_priv *p = cs_to_sh_cmt(cs); + struct sh_cmt_channel *ch = cs_to_sh_cmt(cs); - pm_genpd_syscore_poweron(&p->pdev->dev); - sh_cmt_start(p, FLAG_CLOCKSOURCE); + pm_genpd_syscore_poweron(&ch->cmt->pdev->dev); + sh_cmt_start(ch, FLAG_CLOCKSOURCE); } -static int sh_cmt_register_clocksource(struct sh_cmt_priv *p, +static int sh_cmt_register_clocksource(struct sh_cmt_channel *ch, char *name, unsigned long rating) { - struct clocksource *cs = &p->cs; + struct clocksource *cs = &ch->cs; cs->name = name; cs->rating = rating; @@ -554,47 +563,47 @@ static int sh_cmt_register_clocksource(struct sh_cmt_priv *p, cs->mask = CLOCKSOURCE_MASK(sizeof(unsigned long) * 8); cs->flags = CLOCK_SOURCE_IS_CONTINUOUS; - dev_info(&p->pdev->dev, "used as clock source\n"); + dev_info(&ch->cmt->pdev->dev, "used as clock source\n"); /* Register with dummy 1 Hz value, gets updated in ->enable() */ clocksource_register_hz(cs, 1); return 0; } -static struct sh_cmt_priv *ced_to_sh_cmt(struct clock_event_device *ced) +static struct sh_cmt_channel *ced_to_sh_cmt(struct clock_event_device *ced) { - return container_of(ced, struct sh_cmt_priv, ced); + return container_of(ced, struct sh_cmt_channel, ced); } -static void sh_cmt_clock_event_start(struct sh_cmt_priv *p, int periodic) +static void sh_cmt_clock_event_start(struct sh_cmt_channel *ch, int periodic) { - struct clock_event_device *ced = &p->ced; + struct clock_event_device *ced = &ch->ced; - sh_cmt_start(p, FLAG_CLOCKEVENT); + sh_cmt_start(ch, FLAG_CLOCKEVENT); /* TODO: calculate good shift from rate and counter bit width */ ced->shift = 32; - ced->mult = div_sc(p->rate, NSEC_PER_SEC, ced->shift); - ced->max_delta_ns = clockevent_delta2ns(p->max_match_value, ced); + ced->mult = div_sc(ch->rate, NSEC_PER_SEC, ced->shift); + ced->max_delta_ns = clockevent_delta2ns(ch->max_match_value, ced); ced->min_delta_ns = clockevent_delta2ns(0x1f, ced); if (periodic) - sh_cmt_set_next(p, ((p->rate + HZ/2) / HZ) - 1); + sh_cmt_set_next(ch, ((ch->rate + HZ/2) / HZ) - 1); else - sh_cmt_set_next(p, p->max_match_value); + sh_cmt_set_next(ch, ch->max_match_value); } static void sh_cmt_clock_event_mode(enum clock_event_mode mode, struct clock_event_device *ced) { - struct sh_cmt_priv *p = ced_to_sh_cmt(ced); + struct sh_cmt_channel *ch = ced_to_sh_cmt(ced); /* deal with old setting first */ switch (ced->mode) { case CLOCK_EVT_MODE_PERIODIC: case CLOCK_EVT_MODE_ONESHOT: - sh_cmt_stop(p, FLAG_CLOCKEVENT); + sh_cmt_stop(ch, FLAG_CLOCKEVENT); break; default: break; @@ -602,16 +611,18 @@ static void sh_cmt_clock_event_mode(enum clock_event_mode mode, switch (mode) { case CLOCK_EVT_MODE_PERIODIC: - dev_info(&p->pdev->dev, "used for periodic clock events\n"); - sh_cmt_clock_event_start(p, 1); + dev_info(&ch->cmt->pdev->dev, + "used for periodic clock events\n"); + sh_cmt_clock_event_start(ch, 1); break; case CLOCK_EVT_MODE_ONESHOT: - dev_info(&p->pdev->dev, "used for oneshot clock events\n"); - sh_cmt_clock_event_start(p, 0); + dev_info(&ch->cmt->pdev->dev, + "used for oneshot clock events\n"); + sh_cmt_clock_event_start(ch, 0); break; case CLOCK_EVT_MODE_SHUTDOWN: case CLOCK_EVT_MODE_UNUSED: - sh_cmt_stop(p, FLAG_CLOCKEVENT); + sh_cmt_stop(ch, FLAG_CLOCKEVENT); break; default: break; @@ -621,37 +632,37 @@ static void sh_cmt_clock_event_mode(enum clock_event_mode mode, static int sh_cmt_clock_event_next(unsigned long delta, struct clock_event_device *ced) { - struct sh_cmt_priv *p = ced_to_sh_cmt(ced); + struct sh_cmt_channel *ch = ced_to_sh_cmt(ced); BUG_ON(ced->mode != CLOCK_EVT_MODE_ONESHOT); - if (likely(p->flags & FLAG_IRQCONTEXT)) - p->next_match_value = delta - 1; + if (likely(ch->flags & FLAG_IRQCONTEXT)) + ch->next_match_value = delta - 1; else - sh_cmt_set_next(p, delta - 1); + sh_cmt_set_next(ch, delta - 1); return 0; } static void sh_cmt_clock_event_suspend(struct clock_event_device *ced) { - struct sh_cmt_priv *p = ced_to_sh_cmt(ced); + struct sh_cmt_channel *ch = ced_to_sh_cmt(ced); - pm_genpd_syscore_poweroff(&p->pdev->dev); - clk_unprepare(p->clk); + pm_genpd_syscore_poweroff(&ch->cmt->pdev->dev); + clk_unprepare(ch->cmt->clk); } static void sh_cmt_clock_event_resume(struct clock_event_device *ced) { - struct sh_cmt_priv *p = ced_to_sh_cmt(ced); + struct sh_cmt_channel *ch = ced_to_sh_cmt(ced); - clk_prepare(p->clk); - pm_genpd_syscore_poweron(&p->pdev->dev); + clk_prepare(ch->cmt->clk); + pm_genpd_syscore_poweron(&ch->cmt->pdev->dev); } -static void sh_cmt_register_clockevent(struct sh_cmt_priv *p, +static void sh_cmt_register_clockevent(struct sh_cmt_channel *ch, char *name, unsigned long rating) { - struct clock_event_device *ced = &p->ced; + struct clock_event_device *ced = &ch->ced; memset(ced, 0, sizeof(*ced)); @@ -665,19 +676,19 @@ static void sh_cmt_register_clockevent(struct sh_cmt_priv *p, ced->suspend = sh_cmt_clock_event_suspend; ced->resume = sh_cmt_clock_event_resume; - dev_info(&p->pdev->dev, "used for clock events\n"); + dev_info(&ch->cmt->pdev->dev, "used for clock events\n"); clockevents_register_device(ced); } -static int sh_cmt_register(struct sh_cmt_priv *p, char *name, +static int sh_cmt_register(struct sh_cmt_channel *ch, char *name, unsigned long clockevent_rating, unsigned long clocksource_rating) { if (clockevent_rating) - sh_cmt_register_clockevent(p, name, clockevent_rating); + sh_cmt_register_clockevent(ch, name, clockevent_rating); if (clocksource_rating) - sh_cmt_register_clocksource(p, name, clocksource_rating); + sh_cmt_register_clocksource(ch, name, clocksource_rating); return 0; } @@ -685,6 +696,7 @@ static int sh_cmt_register(struct sh_cmt_priv *p, char *name, static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev) { struct sh_timer_config *cfg = pdev->dev.platform_data; + struct sh_cmt_channel *ch = &p->channel; struct resource *res, *res2; int irq, ret; ret = -ENXIO; @@ -763,26 +775,27 @@ static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev) p->clear_bits = ~0xc000; } - if (p->width == (sizeof(p->max_match_value) * 8)) - p->max_match_value = ~0; + if (p->width == (sizeof(ch->max_match_value) * 8)) + ch->max_match_value = ~0; else - p->max_match_value = (1 << p->width) - 1; + ch->max_match_value = (1 << p->width) - 1; - p->match_value = p->max_match_value; - raw_spin_lock_init(&p->lock); + ch->cmt = p; + ch->match_value = ch->max_match_value; + raw_spin_lock_init(&ch->lock); - ret = sh_cmt_register(p, (char *)dev_name(&p->pdev->dev), + ret = sh_cmt_register(ch, (char *)dev_name(&p->pdev->dev), cfg->clockevent_rating, cfg->clocksource_rating); if (ret) { dev_err(&p->pdev->dev, "registration failed\n"); goto err4; } - p->cs_enabled = false; + ch->cs_enabled = false; ret = request_irq(irq, sh_cmt_interrupt, IRQF_TIMER | IRQF_IRQPOLL | IRQF_NOBALANCING, - dev_name(&p->pdev->dev), p); + dev_name(&p->pdev->dev), ch); if (ret) { dev_err(&p->pdev->dev, "failed to request irq %d\n", irq); goto err4; -- cgit From 2653caf4381f9adeec8c18dfec21ec3c855d801c Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 27 Jan 2014 22:04:17 +0100 Subject: clocksource: sh_cmt: Rename struct sh_cmt_priv to sh_cmt_device Channel data is private as well, rename priv to device to make the distrinction between the core device and the channels clearer. Signed-off-by: Laurent Pinchart --- drivers/clocksource/sh_cmt.c | 116 +++++++++++++++++++++---------------------- 1 file changed, 58 insertions(+), 58 deletions(-) diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c index 351b3ca3ccc4..604199a22265 100644 --- a/drivers/clocksource/sh_cmt.c +++ b/drivers/clocksource/sh_cmt.c @@ -35,10 +35,10 @@ #include #include -struct sh_cmt_priv; +struct sh_cmt_device; struct sh_cmt_channel { - struct sh_cmt_priv *cmt; + struct sh_cmt_device *cmt; unsigned long flags; unsigned long match_value; @@ -52,7 +52,7 @@ struct sh_cmt_channel { bool cs_enabled; }; -struct sh_cmt_priv { +struct sh_cmt_device { struct platform_device *pdev; void __iomem *mapbase; @@ -693,132 +693,132 @@ static int sh_cmt_register(struct sh_cmt_channel *ch, char *name, return 0; } -static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev) +static int sh_cmt_setup(struct sh_cmt_device *cmt, struct platform_device *pdev) { struct sh_timer_config *cfg = pdev->dev.platform_data; - struct sh_cmt_channel *ch = &p->channel; + struct sh_cmt_channel *ch = &cmt->channel; struct resource *res, *res2; int irq, ret; ret = -ENXIO; - memset(p, 0, sizeof(*p)); - p->pdev = pdev; + memset(cmt, 0, sizeof(*cmt)); + cmt->pdev = pdev; if (!cfg) { - dev_err(&p->pdev->dev, "missing platform data\n"); + dev_err(&cmt->pdev->dev, "missing platform data\n"); goto err0; } - res = platform_get_resource(p->pdev, IORESOURCE_MEM, 0); + res = platform_get_resource(cmt->pdev, IORESOURCE_MEM, 0); if (!res) { - dev_err(&p->pdev->dev, "failed to get I/O memory\n"); + dev_err(&cmt->pdev->dev, "failed to get I/O memory\n"); goto err0; } /* optional resource for the shared timer start/stop register */ - res2 = platform_get_resource(p->pdev, IORESOURCE_MEM, 1); + res2 = platform_get_resource(cmt->pdev, IORESOURCE_MEM, 1); - irq = platform_get_irq(p->pdev, 0); + irq = platform_get_irq(cmt->pdev, 0); if (irq < 0) { - dev_err(&p->pdev->dev, "failed to get irq\n"); + dev_err(&cmt->pdev->dev, "failed to get irq\n"); goto err0; } /* map memory, let mapbase point to our channel */ - p->mapbase = ioremap_nocache(res->start, resource_size(res)); - if (p->mapbase == NULL) { - dev_err(&p->pdev->dev, "failed to remap I/O memory\n"); + cmt->mapbase = ioremap_nocache(res->start, resource_size(res)); + if (cmt->mapbase == NULL) { + dev_err(&cmt->pdev->dev, "failed to remap I/O memory\n"); goto err0; } /* map second resource for CMSTR */ - p->mapbase_str = ioremap_nocache(res2 ? res2->start : - res->start - cfg->channel_offset, - res2 ? resource_size(res2) : 2); - if (p->mapbase_str == NULL) { - dev_err(&p->pdev->dev, "failed to remap I/O second memory\n"); + cmt->mapbase_str = ioremap_nocache(res2 ? res2->start : + res->start - cfg->channel_offset, + res2 ? resource_size(res2) : 2); + if (cmt->mapbase_str == NULL) { + dev_err(&cmt->pdev->dev, "failed to remap I/O second memory\n"); goto err1; } /* get hold of clock */ - p->clk = clk_get(&p->pdev->dev, "cmt_fck"); - if (IS_ERR(p->clk)) { - dev_err(&p->pdev->dev, "cannot get clock\n"); - ret = PTR_ERR(p->clk); + cmt->clk = clk_get(&cmt->pdev->dev, "cmt_fck"); + if (IS_ERR(cmt->clk)) { + dev_err(&cmt->pdev->dev, "cannot get clock\n"); + ret = PTR_ERR(cmt->clk); goto err2; } - ret = clk_prepare(p->clk); + ret = clk_prepare(cmt->clk); if (ret < 0) goto err3; if (res2 && (resource_size(res2) == 4)) { /* assume both CMSTR and CMCSR to be 32-bit */ - p->read_control = sh_cmt_read32; - p->write_control = sh_cmt_write32; + cmt->read_control = sh_cmt_read32; + cmt->write_control = sh_cmt_write32; } else { - p->read_control = sh_cmt_read16; - p->write_control = sh_cmt_write16; + cmt->read_control = sh_cmt_read16; + cmt->write_control = sh_cmt_write16; } if (resource_size(res) == 6) { - p->width = 16; - p->read_count = sh_cmt_read16; - p->write_count = sh_cmt_write16; - p->overflow_bit = 0x80; - p->clear_bits = ~0x80; + cmt->width = 16; + cmt->read_count = sh_cmt_read16; + cmt->write_count = sh_cmt_write16; + cmt->overflow_bit = 0x80; + cmt->clear_bits = ~0x80; } else { - p->width = 32; - p->read_count = sh_cmt_read32; - p->write_count = sh_cmt_write32; - p->overflow_bit = 0x8000; - p->clear_bits = ~0xc000; + cmt->width = 32; + cmt->read_count = sh_cmt_read32; + cmt->write_count = sh_cmt_write32; + cmt->overflow_bit = 0x8000; + cmt->clear_bits = ~0xc000; } - if (p->width == (sizeof(ch->max_match_value) * 8)) + if (cmt->width == (sizeof(ch->max_match_value) * 8)) ch->max_match_value = ~0; else - ch->max_match_value = (1 << p->width) - 1; + ch->max_match_value = (1 << cmt->width) - 1; - ch->cmt = p; + ch->cmt = cmt; ch->match_value = ch->max_match_value; raw_spin_lock_init(&ch->lock); - ret = sh_cmt_register(ch, (char *)dev_name(&p->pdev->dev), + ret = sh_cmt_register(ch, (char *)dev_name(&cmt->pdev->dev), cfg->clockevent_rating, cfg->clocksource_rating); if (ret) { - dev_err(&p->pdev->dev, "registration failed\n"); + dev_err(&cmt->pdev->dev, "registration failed\n"); goto err4; } ch->cs_enabled = false; ret = request_irq(irq, sh_cmt_interrupt, IRQF_TIMER | IRQF_IRQPOLL | IRQF_NOBALANCING, - dev_name(&p->pdev->dev), ch); + dev_name(&cmt->pdev->dev), ch); if (ret) { - dev_err(&p->pdev->dev, "failed to request irq %d\n", irq); + dev_err(&cmt->pdev->dev, "failed to request irq %d\n", irq); goto err4; } - platform_set_drvdata(pdev, p); + platform_set_drvdata(pdev, cmt); return 0; err4: - clk_unprepare(p->clk); + clk_unprepare(cmt->clk); err3: - clk_put(p->clk); + clk_put(cmt->clk); err2: - iounmap(p->mapbase_str); + iounmap(cmt->mapbase_str); err1: - iounmap(p->mapbase); + iounmap(cmt->mapbase); err0: return ret; } static int sh_cmt_probe(struct platform_device *pdev) { - struct sh_cmt_priv *p = platform_get_drvdata(pdev); + struct sh_cmt_device *cmt = platform_get_drvdata(pdev); struct sh_timer_config *cfg = pdev->dev.platform_data; int ret; @@ -827,20 +827,20 @@ static int sh_cmt_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); } - if (p) { + if (cmt) { dev_info(&pdev->dev, "kept as earlytimer\n"); goto out; } - p = kmalloc(sizeof(*p), GFP_KERNEL); - if (p == NULL) { + cmt = kmalloc(sizeof(*cmt), GFP_KERNEL); + if (cmt == NULL) { dev_err(&pdev->dev, "failed to allocate driver data\n"); return -ENOMEM; } - ret = sh_cmt_setup(p, pdev); + ret = sh_cmt_setup(cmt, pdev); if (ret) { - kfree(p); + kfree(cmt); pm_runtime_idle(&pdev->dev); return ret; } -- cgit From b882e7b13bc12b3d6b00e4ea2fe374413ddcdd2d Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 27 Jan 2014 22:04:17 +0100 Subject: clocksource: sh_cmt: Split channel setup to separate function Move the channel setup code from sh_cmt_setup to a new sh_cmt_setup_channel function and call it from sh_cmt_setup. Signed-off-by: Laurent Pinchart --- drivers/clocksource/sh_cmt.c | 79 ++++++++++++++++++++++++++------------------ 1 file changed, 47 insertions(+), 32 deletions(-) diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c index 604199a22265..26f73cf609ba 100644 --- a/drivers/clocksource/sh_cmt.c +++ b/drivers/clocksource/sh_cmt.c @@ -693,12 +693,55 @@ static int sh_cmt_register(struct sh_cmt_channel *ch, char *name, return 0; } +static int sh_cmt_setup_channel(struct sh_cmt_channel *ch, + struct sh_cmt_device *cmt) +{ + struct sh_timer_config *cfg = cmt->pdev->dev.platform_data; + int irq; + int ret; + + memset(ch, 0, sizeof(*ch)); + ch->cmt = cmt; + + irq = platform_get_irq(cmt->pdev, 0); + if (irq < 0) { + dev_err(&cmt->pdev->dev, "failed to get irq\n"); + return irq; + } + + if (cmt->width == (sizeof(ch->max_match_value) * 8)) + ch->max_match_value = ~0; + else + ch->max_match_value = (1 << cmt->width) - 1; + + ch->match_value = ch->max_match_value; + raw_spin_lock_init(&ch->lock); + + ret = sh_cmt_register(ch, (char *)dev_name(&cmt->pdev->dev), + cfg->clockevent_rating, + cfg->clocksource_rating); + if (ret) { + dev_err(&cmt->pdev->dev, "registration failed\n"); + return ret; + } + ch->cs_enabled = false; + + ret = request_irq(irq, sh_cmt_interrupt, + IRQF_TIMER | IRQF_IRQPOLL | IRQF_NOBALANCING, + dev_name(&cmt->pdev->dev), ch); + if (ret) { + dev_err(&cmt->pdev->dev, "failed to request irq %d\n", irq); + return ret; + } + + return 0; +} + static int sh_cmt_setup(struct sh_cmt_device *cmt, struct platform_device *pdev) { struct sh_timer_config *cfg = pdev->dev.platform_data; - struct sh_cmt_channel *ch = &cmt->channel; struct resource *res, *res2; - int irq, ret; + int ret; ret = -ENXIO; memset(cmt, 0, sizeof(*cmt)); @@ -718,12 +761,6 @@ static int sh_cmt_setup(struct sh_cmt_device *cmt, struct platform_device *pdev) /* optional resource for the shared timer start/stop register */ res2 = platform_get_resource(cmt->pdev, IORESOURCE_MEM, 1); - irq = platform_get_irq(cmt->pdev, 0); - if (irq < 0) { - dev_err(&cmt->pdev->dev, "failed to get irq\n"); - goto err0; - } - /* map memory, let mapbase point to our channel */ cmt->mapbase = ioremap_nocache(res->start, resource_size(res)); if (cmt->mapbase == NULL) { @@ -775,31 +812,9 @@ static int sh_cmt_setup(struct sh_cmt_device *cmt, struct platform_device *pdev) cmt->clear_bits = ~0xc000; } - if (cmt->width == (sizeof(ch->max_match_value) * 8)) - ch->max_match_value = ~0; - else - ch->max_match_value = (1 << cmt->width) - 1; - - ch->cmt = cmt; - ch->match_value = ch->max_match_value; - raw_spin_lock_init(&ch->lock); - - ret = sh_cmt_register(ch, (char *)dev_name(&cmt->pdev->dev), - cfg->clockevent_rating, - cfg->clocksource_rating); - if (ret) { - dev_err(&cmt->pdev->dev, "registration failed\n"); - goto err4; - } - ch->cs_enabled = false; - - ret = request_irq(irq, sh_cmt_interrupt, - IRQF_TIMER | IRQF_IRQPOLL | IRQF_NOBALANCING, - dev_name(&cmt->pdev->dev), ch); - if (ret) { - dev_err(&cmt->pdev->dev, "failed to request irq %d\n", irq); + ret = sh_cmt_setup_channel(&cmt->channel, cmt); + if (ret < 0) goto err4; - } platform_set_drvdata(pdev, cmt); -- cgit From 1d053e1d8eb28f42b7ec57d1c11ce70b8fba45ff Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 17 Feb 2014 16:04:16 +0100 Subject: clocksource: sh_cmt: Constify name argument to sh_cmt_register() The name argument is assigned to const structure fields only, constify it. Signed-off-by: Laurent Pinchart --- drivers/clocksource/sh_cmt.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c index 26f73cf609ba..febd6bf7a37d 100644 --- a/drivers/clocksource/sh_cmt.c +++ b/drivers/clocksource/sh_cmt.c @@ -549,7 +549,7 @@ static void sh_cmt_clocksource_resume(struct clocksource *cs) } static int sh_cmt_register_clocksource(struct sh_cmt_channel *ch, - char *name, unsigned long rating) + const char *name, unsigned long rating) { struct clocksource *cs = &ch->cs; @@ -660,7 +660,7 @@ static void sh_cmt_clock_event_resume(struct clock_event_device *ced) } static void sh_cmt_register_clockevent(struct sh_cmt_channel *ch, - char *name, unsigned long rating) + const char *name, unsigned long rating) { struct clock_event_device *ced = &ch->ced; @@ -680,7 +680,7 @@ static void sh_cmt_register_clockevent(struct sh_cmt_channel *ch, clockevents_register_device(ced); } -static int sh_cmt_register(struct sh_cmt_channel *ch, char *name, +static int sh_cmt_register(struct sh_cmt_channel *ch, const char *name, unsigned long clockevent_rating, unsigned long clocksource_rating) { @@ -717,7 +717,7 @@ static int sh_cmt_setup_channel(struct sh_cmt_channel *ch, ch->match_value = ch->max_match_value; raw_spin_lock_init(&ch->lock); - ret = sh_cmt_register(ch, (char *)dev_name(&cmt->pdev->dev), + ret = sh_cmt_register(ch, dev_name(&cmt->pdev->dev), cfg->clockevent_rating, cfg->clocksource_rating); if (ret) { -- cgit From 36f1ac982d94cd3cce8ae24abd0676b79dec6126 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 27 Jan 2014 22:04:17 +0100 Subject: clocksource: sh_cmt: Rename mapbase/mapbase_str to mapbase_ch/mapbase The mapbase variable points to the mapped base address of the channel, rename it to mapbase_sh. mapbase_str points to the mapped base address of the CMT device, rename it to mapbase. Signed-off-by: Laurent Pinchart --- drivers/clocksource/sh_cmt.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c index febd6bf7a37d..eb93b889e189 100644 --- a/drivers/clocksource/sh_cmt.c +++ b/drivers/clocksource/sh_cmt.c @@ -55,8 +55,8 @@ struct sh_cmt_channel { struct sh_cmt_device { struct platform_device *pdev; + void __iomem *mapbase_ch; void __iomem *mapbase; - void __iomem *mapbase_str; struct clk *clk; struct sh_cmt_channel channel; @@ -125,41 +125,41 @@ static void sh_cmt_write32(void __iomem *base, unsigned long offs, static inline unsigned long sh_cmt_read_cmstr(struct sh_cmt_channel *ch) { - return ch->cmt->read_control(ch->cmt->mapbase_str, 0); + return ch->cmt->read_control(ch->cmt->mapbase, 0); } static inline unsigned long sh_cmt_read_cmcsr(struct sh_cmt_channel *ch) { - return ch->cmt->read_control(ch->cmt->mapbase, CMCSR); + return ch->cmt->read_control(ch->cmt->mapbase_ch, CMCSR); } static inline unsigned long sh_cmt_read_cmcnt(struct sh_cmt_channel *ch) { - return ch->cmt->read_count(ch->cmt->mapbase, CMCNT); + return ch->cmt->read_count(ch->cmt->mapbase_ch, CMCNT); } static inline void sh_cmt_write_cmstr(struct sh_cmt_channel *ch, unsigned long value) { - ch->cmt->write_control(ch->cmt->mapbase_str, 0, value); + ch->cmt->write_control(ch->cmt->mapbase, 0, value); } static inline void sh_cmt_write_cmcsr(struct sh_cmt_channel *ch, unsigned long value) { - ch->cmt->write_control(ch->cmt->mapbase, CMCSR, value); + ch->cmt->write_control(ch->cmt->mapbase_ch, CMCSR, value); } static inline void sh_cmt_write_cmcnt(struct sh_cmt_channel *ch, unsigned long value) { - ch->cmt->write_count(ch->cmt->mapbase, CMCNT, value); + ch->cmt->write_count(ch->cmt->mapbase_ch, CMCNT, value); } static inline void sh_cmt_write_cmcor(struct sh_cmt_channel *ch, unsigned long value) { - ch->cmt->write_count(ch->cmt->mapbase, CMCOR, value); + ch->cmt->write_count(ch->cmt->mapbase_ch, CMCOR, value); } static unsigned long sh_cmt_get_counter(struct sh_cmt_channel *ch, @@ -761,18 +761,18 @@ static int sh_cmt_setup(struct sh_cmt_device *cmt, struct platform_device *pdev) /* optional resource for the shared timer start/stop register */ res2 = platform_get_resource(cmt->pdev, IORESOURCE_MEM, 1); - /* map memory, let mapbase point to our channel */ - cmt->mapbase = ioremap_nocache(res->start, resource_size(res)); - if (cmt->mapbase == NULL) { + /* map memory, let mapbase_ch point to our channel */ + cmt->mapbase_ch = ioremap_nocache(res->start, resource_size(res)); + if (cmt->mapbase_ch == NULL) { dev_err(&cmt->pdev->dev, "failed to remap I/O memory\n"); goto err0; } /* map second resource for CMSTR */ - cmt->mapbase_str = ioremap_nocache(res2 ? res2->start : - res->start - cfg->channel_offset, - res2 ? resource_size(res2) : 2); - if (cmt->mapbase_str == NULL) { + cmt->mapbase = ioremap_nocache(res2 ? res2->start : + res->start - cfg->channel_offset, + res2 ? resource_size(res2) : 2); + if (cmt->mapbase == NULL) { dev_err(&cmt->pdev->dev, "failed to remap I/O second memory\n"); goto err1; } @@ -824,9 +824,9 @@ err4: err3: clk_put(cmt->clk); err2: - iounmap(cmt->mapbase_str); -err1: iounmap(cmt->mapbase); +err1: + iounmap(cmt->mapbase_ch); err0: return ret; } -- cgit From c924d2d2a964715b55b6601be338b3bd05a1ced5 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 27 Jan 2014 22:04:17 +0100 Subject: clocksource: sh_cmt: Add memory base to sh_cmt_channel structure The channel memory base is channel-specific, add it to the channel structure in preparation for support of multiple channels per device. Signed-off-by: Laurent Pinchart --- drivers/clocksource/sh_cmt.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c index eb93b889e189..4fcb05dc9ea4 100644 --- a/drivers/clocksource/sh_cmt.c +++ b/drivers/clocksource/sh_cmt.c @@ -40,6 +40,8 @@ struct sh_cmt_device; struct sh_cmt_channel { struct sh_cmt_device *cmt; + void __iomem *base; + unsigned long flags; unsigned long match_value; unsigned long next_match_value; @@ -130,12 +132,12 @@ static inline unsigned long sh_cmt_read_cmstr(struct sh_cmt_channel *ch) static inline unsigned long sh_cmt_read_cmcsr(struct sh_cmt_channel *ch) { - return ch->cmt->read_control(ch->cmt->mapbase_ch, CMCSR); + return ch->cmt->read_control(ch->base, CMCSR); } static inline unsigned long sh_cmt_read_cmcnt(struct sh_cmt_channel *ch) { - return ch->cmt->read_count(ch->cmt->mapbase_ch, CMCNT); + return ch->cmt->read_count(ch->base, CMCNT); } static inline void sh_cmt_write_cmstr(struct sh_cmt_channel *ch, @@ -147,19 +149,19 @@ static inline void sh_cmt_write_cmstr(struct sh_cmt_channel *ch, static inline void sh_cmt_write_cmcsr(struct sh_cmt_channel *ch, unsigned long value) { - ch->cmt->write_control(ch->cmt->mapbase_ch, CMCSR, value); + ch->cmt->write_control(ch->base, CMCSR, value); } static inline void sh_cmt_write_cmcnt(struct sh_cmt_channel *ch, unsigned long value) { - ch->cmt->write_count(ch->cmt->mapbase_ch, CMCNT, value); + ch->cmt->write_count(ch->base, CMCNT, value); } static inline void sh_cmt_write_cmcor(struct sh_cmt_channel *ch, unsigned long value) { - ch->cmt->write_count(ch->cmt->mapbase_ch, CMCOR, value); + ch->cmt->write_count(ch->base, CMCOR, value); } static unsigned long sh_cmt_get_counter(struct sh_cmt_channel *ch, @@ -702,6 +704,7 @@ static int sh_cmt_setup_channel(struct sh_cmt_channel *ch, memset(ch, 0, sizeof(*ch)); ch->cmt = cmt; + ch->base = cmt->mapbase_ch; irq = platform_get_irq(cmt->pdev, 0); if (irq < 0) { -- cgit From 740a95184dd61eb0481f75ced05ea5e01b7ce6ac Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 27 Jan 2014 22:04:17 +0100 Subject: clocksource: sh_cmt: Add index to struct sh_cmt_channel Use the index when printing messages to identify the channel. Signed-off-by: Laurent Pinchart --- drivers/clocksource/sh_cmt.c | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c index 4fcb05dc9ea4..6b65621a9733 100644 --- a/drivers/clocksource/sh_cmt.c +++ b/drivers/clocksource/sh_cmt.c @@ -39,6 +39,7 @@ struct sh_cmt_device; struct sh_cmt_channel { struct sh_cmt_device *cmt; + unsigned int index; void __iomem *base; @@ -216,7 +217,8 @@ static int sh_cmt_enable(struct sh_cmt_channel *ch, unsigned long *rate) /* enable clock */ ret = clk_enable(ch->cmt->clk); if (ret) { - dev_err(&ch->cmt->pdev->dev, "cannot enable clock\n"); + dev_err(&ch->cmt->pdev->dev, "ch%u: cannot enable clock\n", + ch->index); goto err0; } @@ -253,7 +255,8 @@ static int sh_cmt_enable(struct sh_cmt_channel *ch, unsigned long *rate) } if (sh_cmt_read_cmcnt(ch)) { - dev_err(&ch->cmt->pdev->dev, "cannot clear CMCNT\n"); + dev_err(&ch->cmt->pdev->dev, "ch%u: cannot clear CMCNT\n", + ch->index); ret = -ETIMEDOUT; goto err1; } @@ -371,7 +374,8 @@ static void sh_cmt_clock_event_program_verify(struct sh_cmt_channel *ch, delay = 1; if (!delay) - dev_warn(&ch->cmt->pdev->dev, "too long delay\n"); + dev_warn(&ch->cmt->pdev->dev, "ch%u: too long delay\n", + ch->index); } while (delay); } @@ -379,7 +383,8 @@ static void sh_cmt_clock_event_program_verify(struct sh_cmt_channel *ch, static void __sh_cmt_set_next(struct sh_cmt_channel *ch, unsigned long delta) { if (delta > ch->max_match_value) - dev_warn(&ch->cmt->pdev->dev, "delta out of range\n"); + dev_warn(&ch->cmt->pdev->dev, "ch%u: delta out of range\n", + ch->index); ch->next_match_value = delta; sh_cmt_clock_event_program_verify(ch, 0); @@ -565,7 +570,8 @@ static int sh_cmt_register_clocksource(struct sh_cmt_channel *ch, cs->mask = CLOCKSOURCE_MASK(sizeof(unsigned long) * 8); cs->flags = CLOCK_SOURCE_IS_CONTINUOUS; - dev_info(&ch->cmt->pdev->dev, "used as clock source\n"); + dev_info(&ch->cmt->pdev->dev, "ch%u: used as clock source\n", + ch->index); /* Register with dummy 1 Hz value, gets updated in ->enable() */ clocksource_register_hz(cs, 1); @@ -614,12 +620,12 @@ static void sh_cmt_clock_event_mode(enum clock_event_mode mode, switch (mode) { case CLOCK_EVT_MODE_PERIODIC: dev_info(&ch->cmt->pdev->dev, - "used for periodic clock events\n"); + "ch%u: used for periodic clock events\n", ch->index); sh_cmt_clock_event_start(ch, 1); break; case CLOCK_EVT_MODE_ONESHOT: dev_info(&ch->cmt->pdev->dev, - "used for oneshot clock events\n"); + "ch%u: used for oneshot clock events\n", ch->index); sh_cmt_clock_event_start(ch, 0); break; case CLOCK_EVT_MODE_SHUTDOWN: @@ -678,7 +684,8 @@ static void sh_cmt_register_clockevent(struct sh_cmt_channel *ch, ced->suspend = sh_cmt_clock_event_suspend; ced->resume = sh_cmt_clock_event_resume; - dev_info(&ch->cmt->pdev->dev, "used for clock events\n"); + dev_info(&ch->cmt->pdev->dev, "ch%u: used for clock events\n", + ch->index); clockevents_register_device(ced); } @@ -695,7 +702,7 @@ static int sh_cmt_register(struct sh_cmt_channel *ch, const char *name, return 0; } -static int sh_cmt_setup_channel(struct sh_cmt_channel *ch, +static int sh_cmt_setup_channel(struct sh_cmt_channel *ch, unsigned int index, struct sh_cmt_device *cmt) { struct sh_timer_config *cfg = cmt->pdev->dev.platform_data; @@ -705,10 +712,12 @@ static int sh_cmt_setup_channel(struct sh_cmt_channel *ch, memset(ch, 0, sizeof(*ch)); ch->cmt = cmt; ch->base = cmt->mapbase_ch; + ch->index = index; irq = platform_get_irq(cmt->pdev, 0); if (irq < 0) { - dev_err(&cmt->pdev->dev, "failed to get irq\n"); + dev_err(&cmt->pdev->dev, "ch%u: failed to get irq\n", + ch->index); return irq; } @@ -724,7 +733,8 @@ static int sh_cmt_setup_channel(struct sh_cmt_channel *ch, cfg->clockevent_rating, cfg->clocksource_rating); if (ret) { - dev_err(&cmt->pdev->dev, "registration failed\n"); + dev_err(&cmt->pdev->dev, "ch%u: registration failed\n", + ch->index); return ret; } ch->cs_enabled = false; @@ -733,7 +743,8 @@ static int sh_cmt_setup_channel(struct sh_cmt_channel *ch, IRQF_TIMER | IRQF_IRQPOLL | IRQF_NOBALANCING, dev_name(&cmt->pdev->dev), ch); if (ret) { - dev_err(&cmt->pdev->dev, "failed to request irq %d\n", irq); + dev_err(&cmt->pdev->dev, "ch%u: failed to request irq %d\n", + ch->index, irq); return ret; } @@ -815,7 +826,7 @@ static int sh_cmt_setup(struct sh_cmt_device *cmt, struct platform_device *pdev) cmt->clear_bits = ~0xc000; } - ret = sh_cmt_setup_channel(&cmt->channel, cmt); + ret = sh_cmt_setup_channel(&cmt->channel, cfg->timer_bit, cmt); if (ret < 0) goto err4; -- cgit From b262bc74dcfd77355720342cbcf89cc8ec12e86b Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 27 Jan 2014 22:04:17 +0100 Subject: clocksource: sh_cmt: Replace kmalloc + memset with kzalloc One kzalloc a day keeps the bugs away. Signed-off-by: Laurent Pinchart --- drivers/clocksource/sh_cmt.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c index 6b65621a9733..0779bf194aea 100644 --- a/drivers/clocksource/sh_cmt.c +++ b/drivers/clocksource/sh_cmt.c @@ -672,8 +672,6 @@ static void sh_cmt_register_clockevent(struct sh_cmt_channel *ch, { struct clock_event_device *ced = &ch->ced; - memset(ced, 0, sizeof(*ced)); - ced->name = name; ced->features = CLOCK_EVT_FEAT_PERIODIC; ced->features |= CLOCK_EVT_FEAT_ONESHOT; @@ -709,7 +707,6 @@ static int sh_cmt_setup_channel(struct sh_cmt_channel *ch, unsigned int index, int irq; int ret; - memset(ch, 0, sizeof(*ch)); ch->cmt = cmt; ch->base = cmt->mapbase_ch; ch->index = index; @@ -758,7 +755,6 @@ static int sh_cmt_setup(struct sh_cmt_device *cmt, struct platform_device *pdev) int ret; ret = -ENXIO; - memset(cmt, 0, sizeof(*cmt)); cmt->pdev = pdev; if (!cfg) { @@ -861,7 +857,7 @@ static int sh_cmt_probe(struct platform_device *pdev) goto out; } - cmt = kmalloc(sizeof(*cmt), GFP_KERNEL); + cmt = kzalloc(sizeof(*cmt), GFP_KERNEL); if (cmt == NULL) { dev_err(&pdev->dev, "failed to allocate driver data\n"); return -ENOMEM; -- cgit From f5ec9b194a93c05e2ccdb3e90d9061cfedc806d9 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 27 Jan 2014 22:04:17 +0100 Subject: clocksource: sh_cmt: Allocate channels dynamically This prepares the driver for multi-channel support. Signed-off-by: Laurent Pinchart --- drivers/clocksource/sh_cmt.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c index 0779bf194aea..f94db327ac7c 100644 --- a/drivers/clocksource/sh_cmt.c +++ b/drivers/clocksource/sh_cmt.c @@ -62,7 +62,8 @@ struct sh_cmt_device { void __iomem *mapbase; struct clk *clk; - struct sh_cmt_channel channel; + struct sh_cmt_channel *channels; + unsigned int num_channels; unsigned long width; /* 16 or 32 bit version of hardware block */ unsigned long overflow_bit; @@ -822,7 +823,15 @@ static int sh_cmt_setup(struct sh_cmt_device *cmt, struct platform_device *pdev) cmt->clear_bits = ~0xc000; } - ret = sh_cmt_setup_channel(&cmt->channel, cfg->timer_bit, cmt); + cmt->channels = kzalloc(sizeof(*cmt->channels), GFP_KERNEL); + if (cmt->channels == NULL) { + ret = -ENOMEM; + goto err4; + } + + cmt->num_channels = 1; + + ret = sh_cmt_setup_channel(&cmt->channels[0], cfg->timer_bit, cmt); if (ret < 0) goto err4; @@ -830,6 +839,7 @@ static int sh_cmt_setup(struct sh_cmt_device *cmt, struct platform_device *pdev) return 0; err4: + kfree(cmt->channels); clk_unprepare(cmt->clk); err3: clk_put(cmt->clk); -- cgit From 2cda3ac49d5744432e9ebffb8ba47bef6eca053d Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 11 Feb 2014 23:46:48 +0100 Subject: clocksource: sh_cmt: Split static information from sh_cmt_device Create a new sh_cmt_info structure to hold static information about the device model and reference that structure from the sh_cmt_device structure. Signed-off-by: Laurent Pinchart --- drivers/clocksource/sh_cmt.c | 192 +++++++++++++++++++++++++++---------------- 1 file changed, 122 insertions(+), 70 deletions(-) diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c index f94db327ac7c..879b8c2ae556 100644 --- a/drivers/clocksource/sh_cmt.c +++ b/drivers/clocksource/sh_cmt.c @@ -37,6 +37,52 @@ struct sh_cmt_device; +/* + * The CMT comes in 5 different identified flavours, depending not only on the + * SoC but also on the particular instance. The following table lists the main + * characteristics of those flavours. + * + * 16B 32B 32B-F 48B 48B-2 + * ----------------------------------------------------------------------------- + * Channels 2 1/4 1 6 2/8 + * Control Width 16 16 16 16 32 + * Counter Width 16 32 32 32/48 32/48 + * Shared Start/Stop Y Y Y Y N + * + * The 48-bit gen2 version has a per-channel start/stop register located in the + * channel registers block. All other versions have a shared start/stop register + * located in the global space. + * + * Note that CMT0 on r8a73a4, r8a7790 and r8a7791, while implementing 32-bit + * channels only, is a 48-bit gen2 CMT with the 48-bit channels unavailable. + */ + +enum sh_cmt_model { + SH_CMT_16BIT, + SH_CMT_32BIT, + SH_CMT_32BIT_FAST, + SH_CMT_48BIT, + SH_CMT_48BIT_GEN2, +}; + +struct sh_cmt_info { + enum sh_cmt_model model; + + unsigned long width; /* 16 or 32 bit version of hardware block */ + unsigned long overflow_bit; + unsigned long clear_bits; + + /* callbacks for CMSTR and CMCSR access */ + unsigned long (*read_control)(void __iomem *base, unsigned long offs); + void (*write_control)(void __iomem *base, unsigned long offs, + unsigned long value); + + /* callbacks for CMCNT and CMCOR access */ + unsigned long (*read_count)(void __iomem *base, unsigned long offs); + void (*write_count)(void __iomem *base, unsigned long offs, + unsigned long value); +}; + struct sh_cmt_channel { struct sh_cmt_device *cmt; unsigned int index; @@ -58,49 +104,16 @@ struct sh_cmt_channel { struct sh_cmt_device { struct platform_device *pdev; + const struct sh_cmt_info *info; + void __iomem *mapbase_ch; void __iomem *mapbase; struct clk *clk; struct sh_cmt_channel *channels; unsigned int num_channels; - - unsigned long width; /* 16 or 32 bit version of hardware block */ - unsigned long overflow_bit; - unsigned long clear_bits; - - /* callbacks for CMSTR and CMCSR access */ - unsigned long (*read_control)(void __iomem *base, unsigned long offs); - void (*write_control)(void __iomem *base, unsigned long offs, - unsigned long value); - - /* callbacks for CMCNT and CMCOR access */ - unsigned long (*read_count)(void __iomem *base, unsigned long offs); - void (*write_count)(void __iomem *base, unsigned long offs, - unsigned long value); }; -/* Examples of supported CMT timer register layouts and I/O access widths: - * - * "16-bit counter and 16-bit control" as found on sh7263: - * CMSTR 0xfffec000 16-bit - * CMCSR 0xfffec002 16-bit - * CMCNT 0xfffec004 16-bit - * CMCOR 0xfffec006 16-bit - * - * "32-bit counter and 16-bit control" as found on sh7372, sh73a0, r8a7740: - * CMSTR 0xffca0000 16-bit - * CMCSR 0xffca0060 16-bit - * CMCNT 0xffca0064 32-bit - * CMCOR 0xffca0068 32-bit - * - * "32-bit counter and 32-bit control" as found on r8a73a4 and r8a7790: - * CMSTR 0xffca0500 32-bit - * CMCSR 0xffca0510 32-bit - * CMCNT 0xffca0514 32-bit - * CMCOR 0xffca0518 32-bit - */ - static unsigned long sh_cmt_read16(void __iomem *base, unsigned long offs) { return ioread16(base + (offs << 1)); @@ -123,47 +136,100 @@ static void sh_cmt_write32(void __iomem *base, unsigned long offs, iowrite32(value, base + (offs << 2)); } +static const struct sh_cmt_info sh_cmt_info[] = { + [SH_CMT_16BIT] = { + .model = SH_CMT_16BIT, + .width = 16, + .overflow_bit = 0x80, + .clear_bits = ~0x80, + .read_control = sh_cmt_read16, + .write_control = sh_cmt_write16, + .read_count = sh_cmt_read16, + .write_count = sh_cmt_write16, + }, + [SH_CMT_32BIT] = { + .model = SH_CMT_32BIT, + .width = 32, + .overflow_bit = 0x8000, + .clear_bits = ~0xc000, + .read_control = sh_cmt_read16, + .write_control = sh_cmt_write16, + .read_count = sh_cmt_read32, + .write_count = sh_cmt_write32, + }, + [SH_CMT_32BIT_FAST] = { + .model = SH_CMT_32BIT_FAST, + .width = 32, + .overflow_bit = 0x8000, + .clear_bits = ~0xc000, + .read_control = sh_cmt_read16, + .write_control = sh_cmt_write16, + .read_count = sh_cmt_read32, + .write_count = sh_cmt_write32, + }, + [SH_CMT_48BIT] = { + .model = SH_CMT_48BIT, + .width = 32, + .overflow_bit = 0x8000, + .clear_bits = ~0xc000, + .read_control = sh_cmt_read32, + .write_control = sh_cmt_write32, + .read_count = sh_cmt_read32, + .write_count = sh_cmt_write32, + }, + [SH_CMT_48BIT_GEN2] = { + .model = SH_CMT_48BIT_GEN2, + .width = 32, + .overflow_bit = 0x8000, + .clear_bits = ~0xc000, + .read_control = sh_cmt_read32, + .write_control = sh_cmt_write32, + .read_count = sh_cmt_read32, + .write_count = sh_cmt_write32, + }, +}; + #define CMCSR 0 /* channel register */ #define CMCNT 1 /* channel register */ #define CMCOR 2 /* channel register */ static inline unsigned long sh_cmt_read_cmstr(struct sh_cmt_channel *ch) { - return ch->cmt->read_control(ch->cmt->mapbase, 0); + return ch->cmt->info->read_control(ch->cmt->mapbase, 0); } static inline unsigned long sh_cmt_read_cmcsr(struct sh_cmt_channel *ch) { - return ch->cmt->read_control(ch->base, CMCSR); + return ch->cmt->info->read_control(ch->base, CMCSR); } static inline unsigned long sh_cmt_read_cmcnt(struct sh_cmt_channel *ch) { - return ch->cmt->read_count(ch->base, CMCNT); + return ch->cmt->info->read_count(ch->base, CMCNT); } static inline void sh_cmt_write_cmstr(struct sh_cmt_channel *ch, unsigned long value) { - ch->cmt->write_control(ch->cmt->mapbase, 0, value); + ch->cmt->info->write_control(ch->cmt->mapbase, 0, value); } static inline void sh_cmt_write_cmcsr(struct sh_cmt_channel *ch, unsigned long value) { - ch->cmt->write_control(ch->base, CMCSR, value); + ch->cmt->info->write_control(ch->base, CMCSR, value); } static inline void sh_cmt_write_cmcnt(struct sh_cmt_channel *ch, unsigned long value) { - ch->cmt->write_count(ch->base, CMCNT, value); + ch->cmt->info->write_count(ch->base, CMCNT, value); } static inline void sh_cmt_write_cmcor(struct sh_cmt_channel *ch, unsigned long value) { - ch->cmt->write_count(ch->base, CMCOR, value); + ch->cmt->info->write_count(ch->base, CMCOR, value); } static unsigned long sh_cmt_get_counter(struct sh_cmt_channel *ch, @@ -172,7 +238,7 @@ static unsigned long sh_cmt_get_counter(struct sh_cmt_channel *ch, unsigned long v1, v2, v3; int o1, o2; - o1 = sh_cmt_read_cmcsr(ch) & ch->cmt->overflow_bit; + o1 = sh_cmt_read_cmcsr(ch) & ch->cmt->info->overflow_bit; /* Make sure the timer value is stable. Stolen from acpi_pm.c */ do { @@ -180,7 +246,7 @@ static unsigned long sh_cmt_get_counter(struct sh_cmt_channel *ch, v1 = sh_cmt_read_cmcnt(ch); v2 = sh_cmt_read_cmcnt(ch); v3 = sh_cmt_read_cmcnt(ch); - o1 = sh_cmt_read_cmcsr(ch) & ch->cmt->overflow_bit; + o1 = sh_cmt_read_cmcsr(ch) & ch->cmt->info->overflow_bit; } while (unlikely((o1 != o2) || (v1 > v2 && v1 < v3) || (v2 > v3 && v2 < v1) || (v3 > v1 && v3 < v2))); @@ -227,7 +293,7 @@ static int sh_cmt_enable(struct sh_cmt_channel *ch, unsigned long *rate) sh_cmt_start_stop_ch(ch, 0); /* configure channel, periodic mode and maximum timeout */ - if (ch->cmt->width == 16) { + if (ch->cmt->info->width == 16) { *rate = clk_get_rate(ch->cmt->clk) / 512; sh_cmt_write_cmcsr(ch, 0x43); } else { @@ -405,7 +471,8 @@ static irqreturn_t sh_cmt_interrupt(int irq, void *dev_id) struct sh_cmt_channel *ch = dev_id; /* clear flags */ - sh_cmt_write_cmcsr(ch, sh_cmt_read_cmcsr(ch) & ch->cmt->clear_bits); + sh_cmt_write_cmcsr(ch, sh_cmt_read_cmcsr(ch) & + ch->cmt->info->clear_bits); /* update clock source counter to begin with if enabled * the wrap flag should be cleared by the timer specific @@ -719,10 +786,10 @@ static int sh_cmt_setup_channel(struct sh_cmt_channel *ch, unsigned int index, return irq; } - if (cmt->width == (sizeof(ch->max_match_value) * 8)) + if (cmt->info->width == (sizeof(ch->max_match_value) * 8)) ch->max_match_value = ~0; else - ch->max_match_value = (1 << cmt->width) - 1; + ch->max_match_value = (1 << cmt->info->width) - 1; ch->match_value = ch->max_match_value; raw_spin_lock_init(&ch->lock); @@ -800,28 +867,13 @@ static int sh_cmt_setup(struct sh_cmt_device *cmt, struct platform_device *pdev) if (ret < 0) goto err3; - if (res2 && (resource_size(res2) == 4)) { - /* assume both CMSTR and CMCSR to be 32-bit */ - cmt->read_control = sh_cmt_read32; - cmt->write_control = sh_cmt_write32; - } else { - cmt->read_control = sh_cmt_read16; - cmt->write_control = sh_cmt_write16; - } - - if (resource_size(res) == 6) { - cmt->width = 16; - cmt->read_count = sh_cmt_read16; - cmt->write_count = sh_cmt_write16; - cmt->overflow_bit = 0x80; - cmt->clear_bits = ~0x80; - } else { - cmt->width = 32; - cmt->read_count = sh_cmt_read32; - cmt->write_count = sh_cmt_write32; - cmt->overflow_bit = 0x8000; - cmt->clear_bits = ~0xc000; - } + /* identify the model based on the resources */ + if (resource_size(res) == 6) + cmt->info = &sh_cmt_info[SH_CMT_16BIT]; + else if (res2 && (resource_size(res2) == 4)) + cmt->info = &sh_cmt_info[SH_CMT_48BIT_GEN2]; + else + cmt->info = &sh_cmt_info[SH_CMT_32BIT]; cmt->channels = kzalloc(sizeof(*cmt->channels), GFP_KERNEL); if (cmt->channels == NULL) { -- cgit From d14be99b7e3fe52bc9921caa30953d49f499f121 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 29 Jan 2014 00:33:08 +0100 Subject: clocksource: sh_cmt: Replace hardcoded register values with macros Define symbolic macros for all used registers bits. Signed-off-by: Laurent Pinchart --- drivers/clocksource/sh_cmt.c | 56 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 44 insertions(+), 12 deletions(-) diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c index 879b8c2ae556..ce00baaf8bd2 100644 --- a/drivers/clocksource/sh_cmt.c +++ b/drivers/clocksource/sh_cmt.c @@ -114,6 +114,34 @@ struct sh_cmt_device { unsigned int num_channels; }; +#define SH_CMT16_CMCSR_CMF (1 << 7) +#define SH_CMT16_CMCSR_CMIE (1 << 6) +#define SH_CMT16_CMCSR_CKS8 (0 << 0) +#define SH_CMT16_CMCSR_CKS32 (1 << 0) +#define SH_CMT16_CMCSR_CKS128 (2 << 0) +#define SH_CMT16_CMCSR_CKS512 (3 << 0) +#define SH_CMT16_CMCSR_CKS_MASK (3 << 0) + +#define SH_CMT32_CMCSR_CMF (1 << 15) +#define SH_CMT32_CMCSR_OVF (1 << 14) +#define SH_CMT32_CMCSR_WRFLG (1 << 13) +#define SH_CMT32_CMCSR_STTF (1 << 12) +#define SH_CMT32_CMCSR_STPF (1 << 11) +#define SH_CMT32_CMCSR_SSIE (1 << 10) +#define SH_CMT32_CMCSR_CMS (1 << 9) +#define SH_CMT32_CMCSR_CMM (1 << 8) +#define SH_CMT32_CMCSR_CMTOUT_IE (1 << 7) +#define SH_CMT32_CMCSR_CMR_NONE (0 << 4) +#define SH_CMT32_CMCSR_CMR_DMA (1 << 4) +#define SH_CMT32_CMCSR_CMR_IRQ (2 << 4) +#define SH_CMT32_CMCSR_CMR_MASK (3 << 4) +#define SH_CMT32_CMCSR_DBGIVD (1 << 3) +#define SH_CMT32_CMCSR_CKS_RCLK8 (4 << 0) +#define SH_CMT32_CMCSR_CKS_RCLK32 (5 << 0) +#define SH_CMT32_CMCSR_CKS_RCLK128 (6 << 0) +#define SH_CMT32_CMCSR_CKS_RCLK1 (7 << 0) +#define SH_CMT32_CMCSR_CKS_MASK (7 << 0) + static unsigned long sh_cmt_read16(void __iomem *base, unsigned long offs) { return ioread16(base + (offs << 1)); @@ -140,8 +168,8 @@ static const struct sh_cmt_info sh_cmt_info[] = { [SH_CMT_16BIT] = { .model = SH_CMT_16BIT, .width = 16, - .overflow_bit = 0x80, - .clear_bits = ~0x80, + .overflow_bit = SH_CMT16_CMCSR_CMF, + .clear_bits = ~SH_CMT16_CMCSR_CMF, .read_control = sh_cmt_read16, .write_control = sh_cmt_write16, .read_count = sh_cmt_read16, @@ -150,8 +178,8 @@ static const struct sh_cmt_info sh_cmt_info[] = { [SH_CMT_32BIT] = { .model = SH_CMT_32BIT, .width = 32, - .overflow_bit = 0x8000, - .clear_bits = ~0xc000, + .overflow_bit = SH_CMT32_CMCSR_CMF, + .clear_bits = ~(SH_CMT32_CMCSR_CMF | SH_CMT32_CMCSR_OVF), .read_control = sh_cmt_read16, .write_control = sh_cmt_write16, .read_count = sh_cmt_read32, @@ -160,8 +188,8 @@ static const struct sh_cmt_info sh_cmt_info[] = { [SH_CMT_32BIT_FAST] = { .model = SH_CMT_32BIT_FAST, .width = 32, - .overflow_bit = 0x8000, - .clear_bits = ~0xc000, + .overflow_bit = SH_CMT32_CMCSR_CMF, + .clear_bits = ~(SH_CMT32_CMCSR_CMF | SH_CMT32_CMCSR_OVF), .read_control = sh_cmt_read16, .write_control = sh_cmt_write16, .read_count = sh_cmt_read32, @@ -170,8 +198,8 @@ static const struct sh_cmt_info sh_cmt_info[] = { [SH_CMT_48BIT] = { .model = SH_CMT_48BIT, .width = 32, - .overflow_bit = 0x8000, - .clear_bits = ~0xc000, + .overflow_bit = SH_CMT32_CMCSR_CMF, + .clear_bits = ~(SH_CMT32_CMCSR_CMF | SH_CMT32_CMCSR_OVF), .read_control = sh_cmt_read32, .write_control = sh_cmt_write32, .read_count = sh_cmt_read32, @@ -180,8 +208,8 @@ static const struct sh_cmt_info sh_cmt_info[] = { [SH_CMT_48BIT_GEN2] = { .model = SH_CMT_48BIT_GEN2, .width = 32, - .overflow_bit = 0x8000, - .clear_bits = ~0xc000, + .overflow_bit = SH_CMT32_CMCSR_CMF, + .clear_bits = ~(SH_CMT32_CMCSR_CMF | SH_CMT32_CMCSR_OVF), .read_control = sh_cmt_read32, .write_control = sh_cmt_write32, .read_count = sh_cmt_read32, @@ -295,10 +323,14 @@ static int sh_cmt_enable(struct sh_cmt_channel *ch, unsigned long *rate) /* configure channel, periodic mode and maximum timeout */ if (ch->cmt->info->width == 16) { *rate = clk_get_rate(ch->cmt->clk) / 512; - sh_cmt_write_cmcsr(ch, 0x43); + sh_cmt_write_cmcsr(ch, SH_CMT16_CMCSR_CMIE | + SH_CMT16_CMCSR_CKS512); } else { *rate = clk_get_rate(ch->cmt->clk) / 8; - sh_cmt_write_cmcsr(ch, 0x01a4); + sh_cmt_write_cmcsr(ch, SH_CMT32_CMCSR_CMM | + SH_CMT32_CMCSR_CMTOUT_IE | + SH_CMT32_CMCSR_CMR_IRQ | + SH_CMT32_CMCSR_CKS_RCLK8); } sh_cmt_write_cmcor(ch, 0xffffffff); -- cgit From f1ebe1e47e1979393a8492bfe751176908a830ae Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 19 Feb 2014 16:19:44 +0100 Subject: clocksource: sh_cmt: Set cpumask to cpu_possible_mask The CMT is a global timer not restricted to a single CPU. It has a lower rating than the TMU or ARM architected timer, but is still useful on systems where the other timers are stopped during CPU sleep. When multiple timers are available the timers core selects which timer to use based on timer ratings. On SMP systems where timer broadcasting is required, one dummy timer is instantiated per CPU with a rating of 100. On those systems the CMT timer has a rating of 80, which makes the dummy timer selected by default on all CPUs. The CMT is then available, and will be used as a broadcast timer. On UP systems no dummy timer is instantiated. The CMT timer has a rating of 125 on those systems and is used directly as a clock event device for CPU0 without broadcasting. The CMT rating shouldn't depend on whether we boot a UP or SMP system. We can't raise the CMT rating to 125 on SMP systems. This would select CMT as the clock event device for CPU0 as its rating is higher than the dummy timer rating, and would leave the system without a broadcast timer. We could instead lower the rating to 80 on all systems, but that wouldn't reflect reality as ratings between 1 and 99 are documented as "unfit for real use". We should raise the rating above 99 and still have the CMT selected as a broadcast timer. This can be done by changing the cpumask from cpumask_of(0) to cpu_possible_mask. In that case the timer selection logic will prefer the previously probed and already selected dummy timer for all CPUs based on the fact that already selected per-cpu timers are preferred over new global timers, regardless of their respective ratings. This also better reflects reality, as the CMT is not tied to the boot CPU. Ideally the timer selection logic should realize that the CMT needs to be used as a broadcast timer on SMP systems as no other broadcast timer is available, regardless of the cpumask and rating. Signed-off-by: Laurent Pinchart --- drivers/clocksource/sh_cmt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c index ce00baaf8bd2..926abe288126 100644 --- a/drivers/clocksource/sh_cmt.c +++ b/drivers/clocksource/sh_cmt.c @@ -776,7 +776,7 @@ static void sh_cmt_register_clockevent(struct sh_cmt_channel *ch, ced->features = CLOCK_EVT_FEAT_PERIODIC; ced->features |= CLOCK_EVT_FEAT_ONESHOT; ced->rating = rating; - ced->cpumask = cpumask_of(0); + ced->cpumask = cpu_possible_mask; ced->set_next_event = sh_cmt_clock_event_next; ced->set_mode = sh_cmt_clock_event_mode; ced->suspend = sh_cmt_clock_event_suspend; -- cgit From b7fcbb0f830e6cccc9d358c24f8463e5d8018649 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 19 Feb 2014 17:00:31 +0100 Subject: clocksource: sh_cmt: Hardcode CMT clock event rating to 125 All boards use or should use a clock event rating of 125 for the CMT, hardcode it in the driver. Signed-off-by: Laurent Pinchart --- drivers/clocksource/sh_cmt.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c index 926abe288126..75b1f83a60a8 100644 --- a/drivers/clocksource/sh_cmt.c +++ b/drivers/clocksource/sh_cmt.c @@ -768,14 +768,14 @@ static void sh_cmt_clock_event_resume(struct clock_event_device *ced) } static void sh_cmt_register_clockevent(struct sh_cmt_channel *ch, - const char *name, unsigned long rating) + const char *name) { struct clock_event_device *ced = &ch->ced; ced->name = name; ced->features = CLOCK_EVT_FEAT_PERIODIC; ced->features |= CLOCK_EVT_FEAT_ONESHOT; - ced->rating = rating; + ced->rating = 125; ced->cpumask = cpu_possible_mask; ced->set_next_event = sh_cmt_clock_event_next; ced->set_mode = sh_cmt_clock_event_mode; @@ -788,11 +788,10 @@ static void sh_cmt_register_clockevent(struct sh_cmt_channel *ch, } static int sh_cmt_register(struct sh_cmt_channel *ch, const char *name, - unsigned long clockevent_rating, - unsigned long clocksource_rating) + bool clockevent, unsigned long clocksource_rating) { - if (clockevent_rating) - sh_cmt_register_clockevent(ch, name, clockevent_rating); + if (clockevent) + sh_cmt_register_clockevent(ch, name); if (clocksource_rating) sh_cmt_register_clocksource(ch, name, clocksource_rating); @@ -827,7 +826,7 @@ static int sh_cmt_setup_channel(struct sh_cmt_channel *ch, unsigned int index, raw_spin_lock_init(&ch->lock); ret = sh_cmt_register(ch, dev_name(&cmt->pdev->dev), - cfg->clockevent_rating, + cfg->clockevent_rating != 0, cfg->clocksource_rating); if (ret) { dev_err(&cmt->pdev->dev, "ch%u: registration failed\n", -- cgit From fb28a659813084365eced5c2876c6383da52e634 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 19 Feb 2014 17:00:31 +0100 Subject: clocksource: sh_cmt: Hardcode CMT clock source rating to 125 All boards use or should use a clock source rating of 125 for the CMT, hardcode it in the driver. Signed-off-by: Laurent Pinchart --- drivers/clocksource/sh_cmt.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c index 75b1f83a60a8..c753efcfe9f5 100644 --- a/drivers/clocksource/sh_cmt.c +++ b/drivers/clocksource/sh_cmt.c @@ -656,12 +656,12 @@ static void sh_cmt_clocksource_resume(struct clocksource *cs) } static int sh_cmt_register_clocksource(struct sh_cmt_channel *ch, - const char *name, unsigned long rating) + const char *name) { struct clocksource *cs = &ch->cs; cs->name = name; - cs->rating = rating; + cs->rating = 125; cs->read = sh_cmt_clocksource_read; cs->enable = sh_cmt_clocksource_enable; cs->disable = sh_cmt_clocksource_disable; @@ -788,13 +788,13 @@ static void sh_cmt_register_clockevent(struct sh_cmt_channel *ch, } static int sh_cmt_register(struct sh_cmt_channel *ch, const char *name, - bool clockevent, unsigned long clocksource_rating) + bool clockevent, bool clocksource) { if (clockevent) sh_cmt_register_clockevent(ch, name); - if (clocksource_rating) - sh_cmt_register_clocksource(ch, name, clocksource_rating); + if (clocksource) + sh_cmt_register_clocksource(ch, name); return 0; } @@ -827,7 +827,7 @@ static int sh_cmt_setup_channel(struct sh_cmt_channel *ch, unsigned int index, ret = sh_cmt_register(ch, dev_name(&cmt->pdev->dev), cfg->clockevent_rating != 0, - cfg->clocksource_rating); + cfg->clocksource_rating != 0); if (ret) { dev_err(&cmt->pdev->dev, "ch%u: registration failed\n", ch->index); -- cgit From 81b3b2711072b6047d5f332cd8751a1c5c9a3fb2 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 28 Jan 2014 12:36:48 +0100 Subject: clocksource: sh_cmt: Add support for multiple channels per device CMT hardware devices can support multiple channels, with global registers and per-channel registers. The sh_cmt driver currently models the hardware with one Linux device per channel. This model makes it difficult to handle global registers in a clean way. Add support for a new model that uses one Linux device per timer with multiple channels per device. This requires changes to platform data, add new channel configuration fields. Support for the legacy model is kept and will be removed after all platforms switch to the new model. Signed-off-by: Laurent Pinchart --- drivers/clocksource/sh_cmt.c | 304 +++++++++++++++++++++++++++++++++---------- include/linux/sh_timer.h | 1 + 2 files changed, 237 insertions(+), 68 deletions(-) diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c index c753efcfe9f5..1efe7d64efca 100644 --- a/drivers/clocksource/sh_cmt.c +++ b/drivers/clocksource/sh_cmt.c @@ -53,7 +53,16 @@ struct sh_cmt_device; * channel registers block. All other versions have a shared start/stop register * located in the global space. * - * Note that CMT0 on r8a73a4, r8a7790 and r8a7791, while implementing 32-bit + * Channels are indexed from 0 to N-1 in the documentation. The channel index + * infers the start/stop bit position in the control register and the channel + * registers block address. Some CMT instances have a subset of channels + * available, in which case the index in the documentation doesn't match the + * "real" index as implemented in hardware. This is for instance the case with + * CMT0 on r8a7740, which is a 32-bit variant with a single channel numbered 0 + * in the documentation but using start/stop bit 5 and having its registers + * block at 0x60. + * + * Similarly CMT0 on r8a73a4, r8a7790 and r8a7791, while implementing 32-bit * channels only, is a 48-bit gen2 CMT with the 48-bit channels unavailable. */ @@ -85,10 +94,14 @@ struct sh_cmt_info { struct sh_cmt_channel { struct sh_cmt_device *cmt; - unsigned int index; - void __iomem *base; + unsigned int index; /* Index in the documentation */ + unsigned int hwidx; /* Real hardware index */ + + void __iomem *iostart; + void __iomem *ioctrl; + unsigned int timer_bit; unsigned long flags; unsigned long match_value; unsigned long next_match_value; @@ -105,6 +118,7 @@ struct sh_cmt_device { struct platform_device *pdev; const struct sh_cmt_info *info; + bool legacy; void __iomem *mapbase_ch; void __iomem *mapbase; @@ -112,6 +126,9 @@ struct sh_cmt_device { struct sh_cmt_channel *channels; unsigned int num_channels; + + bool has_clockevent; + bool has_clocksource; }; #define SH_CMT16_CMCSR_CMF (1 << 7) @@ -223,41 +240,47 @@ static const struct sh_cmt_info sh_cmt_info[] = { static inline unsigned long sh_cmt_read_cmstr(struct sh_cmt_channel *ch) { - return ch->cmt->info->read_control(ch->cmt->mapbase, 0); + if (ch->iostart) + return ch->cmt->info->read_control(ch->iostart, 0); + else + return ch->cmt->info->read_control(ch->cmt->mapbase, 0); } -static inline unsigned long sh_cmt_read_cmcsr(struct sh_cmt_channel *ch) +static inline void sh_cmt_write_cmstr(struct sh_cmt_channel *ch, + unsigned long value) { - return ch->cmt->info->read_control(ch->base, CMCSR); + if (ch->iostart) + ch->cmt->info->write_control(ch->iostart, 0, value); + else + ch->cmt->info->write_control(ch->cmt->mapbase, 0, value); } -static inline unsigned long sh_cmt_read_cmcnt(struct sh_cmt_channel *ch) +static inline unsigned long sh_cmt_read_cmcsr(struct sh_cmt_channel *ch) { - return ch->cmt->info->read_count(ch->base, CMCNT); + return ch->cmt->info->read_control(ch->ioctrl, CMCSR); } -static inline void sh_cmt_write_cmstr(struct sh_cmt_channel *ch, +static inline void sh_cmt_write_cmcsr(struct sh_cmt_channel *ch, unsigned long value) { - ch->cmt->info->write_control(ch->cmt->mapbase, 0, value); + ch->cmt->info->write_control(ch->ioctrl, CMCSR, value); } -static inline void sh_cmt_write_cmcsr(struct sh_cmt_channel *ch, - unsigned long value) +static inline unsigned long sh_cmt_read_cmcnt(struct sh_cmt_channel *ch) { - ch->cmt->info->write_control(ch->base, CMCSR, value); + return ch->cmt->info->read_count(ch->ioctrl, CMCNT); } static inline void sh_cmt_write_cmcnt(struct sh_cmt_channel *ch, unsigned long value) { - ch->cmt->info->write_count(ch->base, CMCNT, value); + ch->cmt->info->write_count(ch->ioctrl, CMCNT, value); } static inline void sh_cmt_write_cmcor(struct sh_cmt_channel *ch, unsigned long value) { - ch->cmt->info->write_count(ch->base, CMCOR, value); + ch->cmt->info->write_count(ch->ioctrl, CMCOR, value); } static unsigned long sh_cmt_get_counter(struct sh_cmt_channel *ch, @@ -286,7 +309,6 @@ static DEFINE_RAW_SPINLOCK(sh_cmt_lock); static void sh_cmt_start_stop_ch(struct sh_cmt_channel *ch, int start) { - struct sh_timer_config *cfg = ch->cmt->pdev->dev.platform_data; unsigned long flags, value; /* start stop register shared by multiple timer channels */ @@ -294,9 +316,9 @@ static void sh_cmt_start_stop_ch(struct sh_cmt_channel *ch, int start) value = sh_cmt_read_cmstr(ch); if (start) - value |= 1 << cfg->timer_bit; + value |= 1 << ch->timer_bit; else - value &= ~(1 << cfg->timer_bit); + value &= ~(1 << ch->timer_bit); sh_cmt_write_cmstr(ch, value); raw_spin_unlock_irqrestore(&sh_cmt_lock, flags); @@ -790,27 +812,72 @@ static void sh_cmt_register_clockevent(struct sh_cmt_channel *ch, static int sh_cmt_register(struct sh_cmt_channel *ch, const char *name, bool clockevent, bool clocksource) { - if (clockevent) + if (clockevent) { + ch->cmt->has_clockevent = true; sh_cmt_register_clockevent(ch, name); + } - if (clocksource) + if (clocksource) { + ch->cmt->has_clocksource = true; sh_cmt_register_clocksource(ch, name); + } return 0; } static int sh_cmt_setup_channel(struct sh_cmt_channel *ch, unsigned int index, - struct sh_cmt_device *cmt) + unsigned int hwidx, bool clockevent, + bool clocksource, struct sh_cmt_device *cmt) { - struct sh_timer_config *cfg = cmt->pdev->dev.platform_data; int irq; int ret; + /* Skip unused channels. */ + if (!clockevent && !clocksource) + return 0; + ch->cmt = cmt; - ch->base = cmt->mapbase_ch; ch->index = index; + ch->hwidx = hwidx; + + /* + * Compute the address of the channel control register block. For the + * timers with a per-channel start/stop register, compute its address + * as well. + * + * For legacy configuration the address has been mapped explicitly. + */ + if (cmt->legacy) { + ch->ioctrl = cmt->mapbase_ch; + } else { + switch (cmt->info->model) { + case SH_CMT_16BIT: + ch->ioctrl = cmt->mapbase + 2 + ch->hwidx * 6; + break; + case SH_CMT_32BIT: + case SH_CMT_48BIT: + ch->ioctrl = cmt->mapbase + 0x10 + ch->hwidx * 0x10; + break; + case SH_CMT_32BIT_FAST: + /* + * The 32-bit "fast" timer has a single channel at hwidx + * 5 but is located at offset 0x40 instead of 0x60 for + * some reason. + */ + ch->ioctrl = cmt->mapbase + 0x40; + break; + case SH_CMT_48BIT_GEN2: + ch->iostart = cmt->mapbase + ch->hwidx * 0x100; + ch->ioctrl = ch->iostart + 0x10; + break; + } + } + + if (cmt->legacy) + irq = platform_get_irq(cmt->pdev, 0); + else + irq = platform_get_irq(cmt->pdev, ch->index); - irq = platform_get_irq(cmt->pdev, 0); if (irq < 0) { dev_err(&cmt->pdev->dev, "ch%u: failed to get irq\n", ch->index); @@ -825,9 +892,15 @@ static int sh_cmt_setup_channel(struct sh_cmt_channel *ch, unsigned int index, ch->match_value = ch->max_match_value; raw_spin_lock_init(&ch->lock); + if (cmt->legacy) { + ch->timer_bit = ch->hwidx; + } else { + ch->timer_bit = cmt->info->model == SH_CMT_48BIT_GEN2 + ? 0 : ch->hwidx; + } + ret = sh_cmt_register(ch, dev_name(&cmt->pdev->dev), - cfg->clockevent_rating != 0, - cfg->clocksource_rating != 0); + clockevent, clocksource); if (ret) { dev_err(&cmt->pdev->dev, "ch%u: registration failed\n", ch->index); @@ -847,97 +920,180 @@ static int sh_cmt_setup_channel(struct sh_cmt_channel *ch, unsigned int index, return 0; } -static int sh_cmt_setup(struct sh_cmt_device *cmt, struct platform_device *pdev) +static int sh_cmt_map_memory(struct sh_cmt_device *cmt) { - struct sh_timer_config *cfg = pdev->dev.platform_data; - struct resource *res, *res2; - int ret; - ret = -ENXIO; + struct resource *mem; - cmt->pdev = pdev; + mem = platform_get_resource(cmt->pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(&cmt->pdev->dev, "failed to get I/O memory\n"); + return -ENXIO; + } - if (!cfg) { - dev_err(&cmt->pdev->dev, "missing platform data\n"); - goto err0; + cmt->mapbase = ioremap_nocache(mem->start, resource_size(mem)); + if (cmt->mapbase == NULL) { + dev_err(&cmt->pdev->dev, "failed to remap I/O memory\n"); + return -ENXIO; } + return 0; +} + +static int sh_cmt_map_memory_legacy(struct sh_cmt_device *cmt) +{ + struct sh_timer_config *cfg = cmt->pdev->dev.platform_data; + struct resource *res, *res2; + + /* map memory, let mapbase_ch point to our channel */ res = platform_get_resource(cmt->pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(&cmt->pdev->dev, "failed to get I/O memory\n"); - goto err0; + return -ENXIO; } - /* optional resource for the shared timer start/stop register */ - res2 = platform_get_resource(cmt->pdev, IORESOURCE_MEM, 1); - - /* map memory, let mapbase_ch point to our channel */ cmt->mapbase_ch = ioremap_nocache(res->start, resource_size(res)); if (cmt->mapbase_ch == NULL) { dev_err(&cmt->pdev->dev, "failed to remap I/O memory\n"); - goto err0; + return -ENXIO; } + /* optional resource for the shared timer start/stop register */ + res2 = platform_get_resource(cmt->pdev, IORESOURCE_MEM, 1); + /* map second resource for CMSTR */ cmt->mapbase = ioremap_nocache(res2 ? res2->start : res->start - cfg->channel_offset, res2 ? resource_size(res2) : 2); if (cmt->mapbase == NULL) { dev_err(&cmt->pdev->dev, "failed to remap I/O second memory\n"); - goto err1; + iounmap(cmt->mapbase_ch); + return -ENXIO; } - /* get hold of clock */ + /* identify the model based on the resources */ + if (resource_size(res) == 6) + cmt->info = &sh_cmt_info[SH_CMT_16BIT]; + else if (res2 && (resource_size(res2) == 4)) + cmt->info = &sh_cmt_info[SH_CMT_48BIT_GEN2]; + else + cmt->info = &sh_cmt_info[SH_CMT_32BIT]; + + return 0; +} + +static void sh_cmt_unmap_memory(struct sh_cmt_device *cmt) +{ + iounmap(cmt->mapbase); + if (cmt->mapbase_ch) + iounmap(cmt->mapbase_ch); +} + +static int sh_cmt_setup(struct sh_cmt_device *cmt, struct platform_device *pdev) +{ + struct sh_timer_config *cfg = pdev->dev.platform_data; + const struct platform_device_id *id = pdev->id_entry; + unsigned int hw_channels; + int ret; + + memset(cmt, 0, sizeof(*cmt)); + cmt->pdev = pdev; + + if (!cfg) { + dev_err(&cmt->pdev->dev, "missing platform data\n"); + return -ENXIO; + } + + cmt->info = (const struct sh_cmt_info *)id->driver_data; + cmt->legacy = cmt->info ? false : true; + + /* Get hold of clock. */ cmt->clk = clk_get(&cmt->pdev->dev, "cmt_fck"); if (IS_ERR(cmt->clk)) { dev_err(&cmt->pdev->dev, "cannot get clock\n"); - ret = PTR_ERR(cmt->clk); - goto err2; + return PTR_ERR(cmt->clk); } ret = clk_prepare(cmt->clk); if (ret < 0) - goto err3; + goto err_clk_put; - /* identify the model based on the resources */ - if (resource_size(res) == 6) - cmt->info = &sh_cmt_info[SH_CMT_16BIT]; - else if (res2 && (resource_size(res2) == 4)) - cmt->info = &sh_cmt_info[SH_CMT_48BIT_GEN2]; + /* + * Map the memory resource(s). We need to support both the legacy + * platform device configuration (with one device per channel) and the + * new version (with multiple channels per device). + */ + if (cmt->legacy) + ret = sh_cmt_map_memory_legacy(cmt); else - cmt->info = &sh_cmt_info[SH_CMT_32BIT]; + ret = sh_cmt_map_memory(cmt); - cmt->channels = kzalloc(sizeof(*cmt->channels), GFP_KERNEL); + if (ret < 0) + goto err_clk_unprepare; + + /* Allocate and setup the channels. */ + if (cmt->legacy) { + cmt->num_channels = 1; + hw_channels = 0; + } else { + cmt->num_channels = hweight8(cfg->channels_mask); + hw_channels = cfg->channels_mask; + } + + cmt->channels = kzalloc(cmt->num_channels * sizeof(*cmt->channels), + GFP_KERNEL); if (cmt->channels == NULL) { ret = -ENOMEM; - goto err4; + goto err_unmap; } - cmt->num_channels = 1; + if (cmt->legacy) { + ret = sh_cmt_setup_channel(&cmt->channels[0], + cfg->timer_bit, cfg->timer_bit, + cfg->clockevent_rating != 0, + cfg->clocksource_rating != 0, cmt); + if (ret < 0) + goto err_unmap; + } else { + unsigned int mask = hw_channels; + unsigned int i; - ret = sh_cmt_setup_channel(&cmt->channels[0], cfg->timer_bit, cmt); - if (ret < 0) - goto err4; + /* + * Use the first channel as a clock event device and the second + * channel as a clock source. If only one channel is available + * use it for both. + */ + for (i = 0; i < cmt->num_channels; ++i) { + unsigned int hwidx = ffs(mask) - 1; + bool clocksource = i == 1 || cmt->num_channels == 1; + bool clockevent = i == 0; + + ret = sh_cmt_setup_channel(&cmt->channels[i], i, hwidx, + clockevent, clocksource, + cmt); + if (ret < 0) + goto err_unmap; + + mask &= ~(1 << hwidx); + } + } platform_set_drvdata(pdev, cmt); return 0; -err4: + +err_unmap: kfree(cmt->channels); + sh_cmt_unmap_memory(cmt); +err_clk_unprepare: clk_unprepare(cmt->clk); -err3: +err_clk_put: clk_put(cmt->clk); -err2: - iounmap(cmt->mapbase); -err1: - iounmap(cmt->mapbase_ch); -err0: return ret; } static int sh_cmt_probe(struct platform_device *pdev) { struct sh_cmt_device *cmt = platform_get_drvdata(pdev); - struct sh_timer_config *cfg = pdev->dev.platform_data; int ret; if (!is_early_platform_device(pdev)) { @@ -966,7 +1122,7 @@ static int sh_cmt_probe(struct platform_device *pdev) return 0; out: - if (cfg->clockevent_rating || cfg->clocksource_rating) + if (cmt->has_clockevent || cmt->has_clocksource) pm_runtime_irq_safe(&pdev->dev); else pm_runtime_idle(&pdev->dev); @@ -979,12 +1135,24 @@ static int sh_cmt_remove(struct platform_device *pdev) return -EBUSY; /* cannot unregister clockevent and clocksource */ } +static const struct platform_device_id sh_cmt_id_table[] = { + { "sh_cmt", 0 }, + { "sh-cmt-16", (kernel_ulong_t)&sh_cmt_info[SH_CMT_16BIT] }, + { "sh-cmt-32", (kernel_ulong_t)&sh_cmt_info[SH_CMT_32BIT] }, + { "sh-cmt-32-fast", (kernel_ulong_t)&sh_cmt_info[SH_CMT_32BIT_FAST] }, + { "sh-cmt-48", (kernel_ulong_t)&sh_cmt_info[SH_CMT_48BIT] }, + { "sh-cmt-48-gen2", (kernel_ulong_t)&sh_cmt_info[SH_CMT_48BIT_GEN2] }, + { } +}; +MODULE_DEVICE_TABLE(platform, sh_cmt_id_table); + static struct platform_driver sh_cmt_device_driver = { .probe = sh_cmt_probe, .remove = sh_cmt_remove, .driver = { .name = "sh_cmt", - } + }, + .id_table = sh_cmt_id_table, }; static int __init sh_cmt_init(void) diff --git a/include/linux/sh_timer.h b/include/linux/sh_timer.h index 4d9dcd138315..8e1e036d6d45 100644 --- a/include/linux/sh_timer.h +++ b/include/linux/sh_timer.h @@ -7,6 +7,7 @@ struct sh_timer_config { int timer_bit; unsigned long clockevent_rating; unsigned long clocksource_rating; + unsigned int channels_mask; }; #endif /* __SH_TIMER_H__ */ -- cgit From 24b4e07df54b7bf7739fb3dd193f639a8f274ad6 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 14 Feb 2014 00:35:18 +0100 Subject: clocksource: sh_cmt: Rename clock to "fck" in the non-legacy case The sh_cmt driver gets the CMT functional clock using a connection ID of "cmt_fck". While all SH SoCs create clock lookup entries with a NULL device ID and a "cmt_fck" connection ID, the ARM SoCs use the device ID only with a NULL connection ID. This works on legacy platforms but will break on ARM with DT boot. Fix the situation by using a connection ID of "fck" in the non-legacy platform data case. Clock lookup entries will be renamed to use the device ID as well as the connection ID as platforms get moved to new platform data. The legacy code will eventually be dropped, leaving us with device ID based clock lookup, compatible with DT boot. Signed-off-by: Laurent Pinchart --- drivers/clocksource/sh_cmt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c index 1efe7d64efca..a5ea9aedbd50 100644 --- a/drivers/clocksource/sh_cmt.c +++ b/drivers/clocksource/sh_cmt.c @@ -1007,7 +1007,7 @@ static int sh_cmt_setup(struct sh_cmt_device *cmt, struct platform_device *pdev) cmt->legacy = cmt->info ? false : true; /* Get hold of clock. */ - cmt->clk = clk_get(&cmt->pdev->dev, "cmt_fck"); + cmt->clk = clk_get(&cmt->pdev->dev, cmt->legacy ? "cmt_fck" : "fck"); if (IS_ERR(cmt->clk)) { dev_err(&cmt->pdev->dev, "cannot get clock\n"); return PTR_ERR(cmt->clk); -- cgit From 1cd89c568c057a13ca11acf0eb3a78121513e2b6 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 14 Feb 2014 01:25:50 +0100 Subject: clocksource: sh_cmt: Remove FSF mail address from GPL notice Do not include the paragraph about writing to the Free Software Foundation's mailing address from the sample GPL notice. The FSF has changed addresses in the past, and may do so again. Linux already includes a copy of the GPL. Signed-off-by: Laurent Pinchart --- drivers/clocksource/sh_cmt.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c index a5ea9aedbd50..399e9525e226 100644 --- a/drivers/clocksource/sh_cmt.c +++ b/drivers/clocksource/sh_cmt.c @@ -11,10 +11,6 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include -- cgit From e7a9bcc2372b0e62443569c63a369cfd528db4f4 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 12 Feb 2014 16:56:44 +0100 Subject: clocksource: sh_cmt: Sort headers alphabetically This helps locating duplicates and inserting new headers. Signed-off-by: Laurent Pinchart --- drivers/clocksource/sh_cmt.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c index 399e9525e226..9f215e74751c 100644 --- a/drivers/clocksource/sh_cmt.c +++ b/drivers/clocksource/sh_cmt.c @@ -13,23 +13,23 @@ * GNU General Public License for more details. */ +#include +#include +#include +#include +#include #include -#include -#include #include -#include #include -#include +#include #include -#include -#include -#include -#include -#include -#include #include +#include #include #include +#include +#include +#include struct sh_cmt_device; -- cgit From bfa76bb12f23ecf0c6d07c302f4571a6fe9bc3e3 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 21 Feb 2014 01:24:47 +0100 Subject: clocksource: sh_cmt: Request IRQ for clock event device only Clock sources don't need an IRQ, request the IRQ only for channels used as clock event devices. Signed-off-by: Laurent Pinchart --- drivers/clocksource/sh_cmt.c | 51 +++++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c index 9f215e74751c..bc8d025ce861 100644 --- a/drivers/clocksource/sh_cmt.c +++ b/drivers/clocksource/sh_cmt.c @@ -785,10 +785,28 @@ static void sh_cmt_clock_event_resume(struct clock_event_device *ced) pm_genpd_syscore_poweron(&ch->cmt->pdev->dev); } -static void sh_cmt_register_clockevent(struct sh_cmt_channel *ch, - const char *name) +static int sh_cmt_register_clockevent(struct sh_cmt_channel *ch, + const char *name) { struct clock_event_device *ced = &ch->ced; + int irq; + int ret; + + irq = platform_get_irq(ch->cmt->pdev, ch->cmt->legacy ? 0 : ch->index); + if (irq < 0) { + dev_err(&ch->cmt->pdev->dev, "ch%u: failed to get irq\n", + ch->index); + return irq; + } + + ret = request_irq(irq, sh_cmt_interrupt, + IRQF_TIMER | IRQF_IRQPOLL | IRQF_NOBALANCING, + dev_name(&ch->cmt->pdev->dev), ch); + if (ret) { + dev_err(&ch->cmt->pdev->dev, "ch%u: failed to request irq %d\n", + ch->index, irq); + return ret; + } ced->name = name; ced->features = CLOCK_EVT_FEAT_PERIODIC; @@ -803,14 +821,20 @@ static void sh_cmt_register_clockevent(struct sh_cmt_channel *ch, dev_info(&ch->cmt->pdev->dev, "ch%u: used for clock events\n", ch->index); clockevents_register_device(ced); + + return 0; } static int sh_cmt_register(struct sh_cmt_channel *ch, const char *name, bool clockevent, bool clocksource) { + int ret; + if (clockevent) { ch->cmt->has_clockevent = true; - sh_cmt_register_clockevent(ch, name); + ret = sh_cmt_register_clockevent(ch, name); + if (ret < 0) + return ret; } if (clocksource) { @@ -825,7 +849,6 @@ static int sh_cmt_setup_channel(struct sh_cmt_channel *ch, unsigned int index, unsigned int hwidx, bool clockevent, bool clocksource, struct sh_cmt_device *cmt) { - int irq; int ret; /* Skip unused channels. */ @@ -869,17 +892,6 @@ static int sh_cmt_setup_channel(struct sh_cmt_channel *ch, unsigned int index, } } - if (cmt->legacy) - irq = platform_get_irq(cmt->pdev, 0); - else - irq = platform_get_irq(cmt->pdev, ch->index); - - if (irq < 0) { - dev_err(&cmt->pdev->dev, "ch%u: failed to get irq\n", - ch->index); - return irq; - } - if (cmt->info->width == (sizeof(ch->max_match_value) * 8)) ch->max_match_value = ~0; else @@ -904,15 +916,6 @@ static int sh_cmt_setup_channel(struct sh_cmt_channel *ch, unsigned int index, } ch->cs_enabled = false; - ret = request_irq(irq, sh_cmt_interrupt, - IRQF_TIMER | IRQF_IRQPOLL | IRQF_NOBALANCING, - dev_name(&cmt->pdev->dev), ch); - if (ret) { - dev_err(&cmt->pdev->dev, "ch%u: failed to request irq %d\n", - ch->index, irq); - return ret; - } - return 0; } -- cgit From 1c56cf6b048e1e1bbe08faf38b5592b373905ac5 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 17 Feb 2014 11:27:49 +0100 Subject: clocksource: sh_tmu: Use request_irq() instead of setup_irq() The driver claims it needs to register an interrupt handler too early for request_irq(). This might have been true in the past, but the only meaningful difference between request_irq() and setup_irq() today is an additional kzalloc() call in request_irq(). As the driver calls kmalloc() itself we know that the slab allocator is available, we can thus switch to request_irq(). Signed-off-by: Laurent Pinchart --- drivers/clocksource/sh_tmu.c | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/drivers/clocksource/sh_tmu.c b/drivers/clocksource/sh_tmu.c index ecd7b60bfdfa..8613cc90bb74 100644 --- a/drivers/clocksource/sh_tmu.c +++ b/drivers/clocksource/sh_tmu.c @@ -38,7 +38,7 @@ struct sh_tmu_priv { void __iomem *mapbase; struct clk *clk; - struct irqaction irqaction; + int irq; struct platform_device *pdev; unsigned long rate; unsigned long periodic; @@ -401,10 +401,11 @@ static void sh_tmu_register_clockevent(struct sh_tmu_priv *p, clockevents_config_and_register(ced, 1, 0x300, 0xffffffff); - ret = setup_irq(p->irqaction.irq, &p->irqaction); + ret = request_irq(p->irq, sh_tmu_interrupt, + IRQF_TIMER | IRQF_IRQPOLL | IRQF_NOBALANCING, + dev_name(&p->pdev->dev), p); if (ret) { - dev_err(&p->pdev->dev, "failed to request irq %d\n", - p->irqaction.irq); + dev_err(&p->pdev->dev, "failed to request irq %d\n", p->irq); return; } } @@ -425,7 +426,7 @@ static int sh_tmu_setup(struct sh_tmu_priv *p, struct platform_device *pdev) { struct sh_timer_config *cfg = pdev->dev.platform_data; struct resource *res; - int irq, ret; + int ret; ret = -ENXIO; memset(p, 0, sizeof(*p)); @@ -444,8 +445,8 @@ static int sh_tmu_setup(struct sh_tmu_priv *p, struct platform_device *pdev) goto err0; } - irq = platform_get_irq(p->pdev, 0); - if (irq < 0) { + p->irq = platform_get_irq(p->pdev, 0); + if (p->irq < 0) { dev_err(&p->pdev->dev, "failed to get irq\n"); goto err0; } @@ -457,13 +458,6 @@ static int sh_tmu_setup(struct sh_tmu_priv *p, struct platform_device *pdev) goto err0; } - /* setup data for setup_irq() (too early for request_irq()) */ - p->irqaction.name = dev_name(&p->pdev->dev); - p->irqaction.handler = sh_tmu_interrupt; - p->irqaction.dev_id = p; - p->irqaction.irq = irq; - p->irqaction.flags = IRQF_TIMER | IRQF_IRQPOLL | IRQF_NOBALANCING; - /* get hold of clock */ p->clk = clk_get(&p->pdev->dev, "tmu_fck"); if (IS_ERR(p->clk)) { -- cgit From de2d12c7e856f0fa59ea83275410a364d2b182c0 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 27 Jan 2014 15:29:19 +0100 Subject: clocksource: sh_tmu: Split channel fields from sh_tmu_priv Create a new sh_tmu_channel structure to hold the channel-specific field in preparation for multiple channels per device support. Signed-off-by: Laurent Pinchart --- drivers/clocksource/sh_tmu.c | 235 +++++++++++++++++++++++-------------------- 1 file changed, 125 insertions(+), 110 deletions(-) diff --git a/drivers/clocksource/sh_tmu.c b/drivers/clocksource/sh_tmu.c index 8613cc90bb74..26457e1fccbb 100644 --- a/drivers/clocksource/sh_tmu.c +++ b/drivers/clocksource/sh_tmu.c @@ -35,11 +35,13 @@ #include #include -struct sh_tmu_priv { - void __iomem *mapbase; - struct clk *clk; +struct sh_tmu_priv; + +struct sh_tmu_channel { + struct sh_tmu_priv *tmu; + int irq; - struct platform_device *pdev; + unsigned long rate; unsigned long periodic; struct clock_event_device ced; @@ -48,6 +50,15 @@ struct sh_tmu_priv { unsigned int enable_count; }; +struct sh_tmu_priv { + struct platform_device *pdev; + + void __iomem *mapbase; + struct clk *clk; + + struct sh_tmu_channel channel; +}; + static DEFINE_RAW_SPINLOCK(sh_tmu_lock); #define TSTR -1 /* shared register */ @@ -55,10 +66,10 @@ static DEFINE_RAW_SPINLOCK(sh_tmu_lock); #define TCNT 1 /* channel register */ #define TCR 2 /* channel register */ -static inline unsigned long sh_tmu_read(struct sh_tmu_priv *p, int reg_nr) +static inline unsigned long sh_tmu_read(struct sh_tmu_channel *ch, int reg_nr) { - struct sh_timer_config *cfg = p->pdev->dev.platform_data; - void __iomem *base = p->mapbase; + struct sh_timer_config *cfg = ch->tmu->pdev->dev.platform_data; + void __iomem *base = ch->tmu->mapbase; unsigned long offs; if (reg_nr == TSTR) @@ -72,11 +83,11 @@ static inline unsigned long sh_tmu_read(struct sh_tmu_priv *p, int reg_nr) return ioread32(base + offs); } -static inline void sh_tmu_write(struct sh_tmu_priv *p, int reg_nr, +static inline void sh_tmu_write(struct sh_tmu_channel *ch, int reg_nr, unsigned long value) { - struct sh_timer_config *cfg = p->pdev->dev.platform_data; - void __iomem *base = p->mapbase; + struct sh_timer_config *cfg = ch->tmu->pdev->dev.platform_data; + void __iomem *base = ch->tmu->mapbase; unsigned long offs; if (reg_nr == TSTR) { @@ -92,152 +103,152 @@ static inline void sh_tmu_write(struct sh_tmu_priv *p, int reg_nr, iowrite32(value, base + offs); } -static void sh_tmu_start_stop_ch(struct sh_tmu_priv *p, int start) +static void sh_tmu_start_stop_ch(struct sh_tmu_channel *ch, int start) { - struct sh_timer_config *cfg = p->pdev->dev.platform_data; + struct sh_timer_config *cfg = ch->tmu->pdev->dev.platform_data; unsigned long flags, value; /* start stop register shared by multiple timer channels */ raw_spin_lock_irqsave(&sh_tmu_lock, flags); - value = sh_tmu_read(p, TSTR); + value = sh_tmu_read(ch, TSTR); if (start) value |= 1 << cfg->timer_bit; else value &= ~(1 << cfg->timer_bit); - sh_tmu_write(p, TSTR, value); + sh_tmu_write(ch, TSTR, value); raw_spin_unlock_irqrestore(&sh_tmu_lock, flags); } -static int __sh_tmu_enable(struct sh_tmu_priv *p) +static int __sh_tmu_enable(struct sh_tmu_channel *ch) { int ret; /* enable clock */ - ret = clk_enable(p->clk); + ret = clk_enable(ch->tmu->clk); if (ret) { - dev_err(&p->pdev->dev, "cannot enable clock\n"); + dev_err(&ch->tmu->pdev->dev, "cannot enable clock\n"); return ret; } /* make sure channel is disabled */ - sh_tmu_start_stop_ch(p, 0); + sh_tmu_start_stop_ch(ch, 0); /* maximum timeout */ - sh_tmu_write(p, TCOR, 0xffffffff); - sh_tmu_write(p, TCNT, 0xffffffff); + sh_tmu_write(ch, TCOR, 0xffffffff); + sh_tmu_write(ch, TCNT, 0xffffffff); /* configure channel to parent clock / 4, irq off */ - p->rate = clk_get_rate(p->clk) / 4; - sh_tmu_write(p, TCR, 0x0000); + ch->rate = clk_get_rate(ch->tmu->clk) / 4; + sh_tmu_write(ch, TCR, 0x0000); /* enable channel */ - sh_tmu_start_stop_ch(p, 1); + sh_tmu_start_stop_ch(ch, 1); return 0; } -static int sh_tmu_enable(struct sh_tmu_priv *p) +static int sh_tmu_enable(struct sh_tmu_channel *ch) { - if (p->enable_count++ > 0) + if (ch->enable_count++ > 0) return 0; - pm_runtime_get_sync(&p->pdev->dev); - dev_pm_syscore_device(&p->pdev->dev, true); + pm_runtime_get_sync(&ch->tmu->pdev->dev); + dev_pm_syscore_device(&ch->tmu->pdev->dev, true); - return __sh_tmu_enable(p); + return __sh_tmu_enable(ch); } -static void __sh_tmu_disable(struct sh_tmu_priv *p) +static void __sh_tmu_disable(struct sh_tmu_channel *ch) { /* disable channel */ - sh_tmu_start_stop_ch(p, 0); + sh_tmu_start_stop_ch(ch, 0); /* disable interrupts in TMU block */ - sh_tmu_write(p, TCR, 0x0000); + sh_tmu_write(ch, TCR, 0x0000); /* stop clock */ - clk_disable(p->clk); + clk_disable(ch->tmu->clk); } -static void sh_tmu_disable(struct sh_tmu_priv *p) +static void sh_tmu_disable(struct sh_tmu_channel *ch) { - if (WARN_ON(p->enable_count == 0)) + if (WARN_ON(ch->enable_count == 0)) return; - if (--p->enable_count > 0) + if (--ch->enable_count > 0) return; - __sh_tmu_disable(p); + __sh_tmu_disable(ch); - dev_pm_syscore_device(&p->pdev->dev, false); - pm_runtime_put(&p->pdev->dev); + dev_pm_syscore_device(&ch->tmu->pdev->dev, false); + pm_runtime_put(&ch->tmu->pdev->dev); } -static void sh_tmu_set_next(struct sh_tmu_priv *p, unsigned long delta, +static void sh_tmu_set_next(struct sh_tmu_channel *ch, unsigned long delta, int periodic) { /* stop timer */ - sh_tmu_start_stop_ch(p, 0); + sh_tmu_start_stop_ch(ch, 0); /* acknowledge interrupt */ - sh_tmu_read(p, TCR); + sh_tmu_read(ch, TCR); /* enable interrupt */ - sh_tmu_write(p, TCR, 0x0020); + sh_tmu_write(ch, TCR, 0x0020); /* reload delta value in case of periodic timer */ if (periodic) - sh_tmu_write(p, TCOR, delta); + sh_tmu_write(ch, TCOR, delta); else - sh_tmu_write(p, TCOR, 0xffffffff); + sh_tmu_write(ch, TCOR, 0xffffffff); - sh_tmu_write(p, TCNT, delta); + sh_tmu_write(ch, TCNT, delta); /* start timer */ - sh_tmu_start_stop_ch(p, 1); + sh_tmu_start_stop_ch(ch, 1); } static irqreturn_t sh_tmu_interrupt(int irq, void *dev_id) { - struct sh_tmu_priv *p = dev_id; + struct sh_tmu_channel *ch = dev_id; /* disable or acknowledge interrupt */ - if (p->ced.mode == CLOCK_EVT_MODE_ONESHOT) - sh_tmu_write(p, TCR, 0x0000); + if (ch->ced.mode == CLOCK_EVT_MODE_ONESHOT) + sh_tmu_write(ch, TCR, 0x0000); else - sh_tmu_write(p, TCR, 0x0020); + sh_tmu_write(ch, TCR, 0x0020); /* notify clockevent layer */ - p->ced.event_handler(&p->ced); + ch->ced.event_handler(&ch->ced); return IRQ_HANDLED; } -static struct sh_tmu_priv *cs_to_sh_tmu(struct clocksource *cs) +static struct sh_tmu_channel *cs_to_sh_tmu(struct clocksource *cs) { - return container_of(cs, struct sh_tmu_priv, cs); + return container_of(cs, struct sh_tmu_channel, cs); } static cycle_t sh_tmu_clocksource_read(struct clocksource *cs) { - struct sh_tmu_priv *p = cs_to_sh_tmu(cs); + struct sh_tmu_channel *ch = cs_to_sh_tmu(cs); - return sh_tmu_read(p, TCNT) ^ 0xffffffff; + return sh_tmu_read(ch, TCNT) ^ 0xffffffff; } static int sh_tmu_clocksource_enable(struct clocksource *cs) { - struct sh_tmu_priv *p = cs_to_sh_tmu(cs); + struct sh_tmu_channel *ch = cs_to_sh_tmu(cs); int ret; - if (WARN_ON(p->cs_enabled)) + if (WARN_ON(ch->cs_enabled)) return 0; - ret = sh_tmu_enable(p); + ret = sh_tmu_enable(ch); if (!ret) { - __clocksource_updatefreq_hz(cs, p->rate); - p->cs_enabled = true; + __clocksource_updatefreq_hz(cs, ch->rate); + ch->cs_enabled = true; } return ret; @@ -245,45 +256,45 @@ static int sh_tmu_clocksource_enable(struct clocksource *cs) static void sh_tmu_clocksource_disable(struct clocksource *cs) { - struct sh_tmu_priv *p = cs_to_sh_tmu(cs); + struct sh_tmu_channel *ch = cs_to_sh_tmu(cs); - if (WARN_ON(!p->cs_enabled)) + if (WARN_ON(!ch->cs_enabled)) return; - sh_tmu_disable(p); - p->cs_enabled = false; + sh_tmu_disable(ch); + ch->cs_enabled = false; } static void sh_tmu_clocksource_suspend(struct clocksource *cs) { - struct sh_tmu_priv *p = cs_to_sh_tmu(cs); + struct sh_tmu_channel *ch = cs_to_sh_tmu(cs); - if (!p->cs_enabled) + if (!ch->cs_enabled) return; - if (--p->enable_count == 0) { - __sh_tmu_disable(p); - pm_genpd_syscore_poweroff(&p->pdev->dev); + if (--ch->enable_count == 0) { + __sh_tmu_disable(ch); + pm_genpd_syscore_poweroff(&ch->tmu->pdev->dev); } } static void sh_tmu_clocksource_resume(struct clocksource *cs) { - struct sh_tmu_priv *p = cs_to_sh_tmu(cs); + struct sh_tmu_channel *ch = cs_to_sh_tmu(cs); - if (!p->cs_enabled) + if (!ch->cs_enabled) return; - if (p->enable_count++ == 0) { - pm_genpd_syscore_poweron(&p->pdev->dev); - __sh_tmu_enable(p); + if (ch->enable_count++ == 0) { + pm_genpd_syscore_poweron(&ch->tmu->pdev->dev); + __sh_tmu_enable(ch); } } -static int sh_tmu_register_clocksource(struct sh_tmu_priv *p, +static int sh_tmu_register_clocksource(struct sh_tmu_channel *ch, char *name, unsigned long rating) { - struct clocksource *cs = &p->cs; + struct clocksource *cs = &ch->cs; cs->name = name; cs->rating = rating; @@ -295,43 +306,43 @@ static int sh_tmu_register_clocksource(struct sh_tmu_priv *p, cs->mask = CLOCKSOURCE_MASK(32); cs->flags = CLOCK_SOURCE_IS_CONTINUOUS; - dev_info(&p->pdev->dev, "used as clock source\n"); + dev_info(&ch->tmu->pdev->dev, "used as clock source\n"); /* Register with dummy 1 Hz value, gets updated in ->enable() */ clocksource_register_hz(cs, 1); return 0; } -static struct sh_tmu_priv *ced_to_sh_tmu(struct clock_event_device *ced) +static struct sh_tmu_channel *ced_to_sh_tmu(struct clock_event_device *ced) { - return container_of(ced, struct sh_tmu_priv, ced); + return container_of(ced, struct sh_tmu_channel, ced); } -static void sh_tmu_clock_event_start(struct sh_tmu_priv *p, int periodic) +static void sh_tmu_clock_event_start(struct sh_tmu_channel *ch, int periodic) { - struct clock_event_device *ced = &p->ced; + struct clock_event_device *ced = &ch->ced; - sh_tmu_enable(p); + sh_tmu_enable(ch); - clockevents_config(ced, p->rate); + clockevents_config(ced, ch->rate); if (periodic) { - p->periodic = (p->rate + HZ/2) / HZ; - sh_tmu_set_next(p, p->periodic, 1); + ch->periodic = (ch->rate + HZ/2) / HZ; + sh_tmu_set_next(ch, ch->periodic, 1); } } static void sh_tmu_clock_event_mode(enum clock_event_mode mode, struct clock_event_device *ced) { - struct sh_tmu_priv *p = ced_to_sh_tmu(ced); + struct sh_tmu_channel *ch = ced_to_sh_tmu(ced); int disabled = 0; /* deal with old setting first */ switch (ced->mode) { case CLOCK_EVT_MODE_PERIODIC: case CLOCK_EVT_MODE_ONESHOT: - sh_tmu_disable(p); + sh_tmu_disable(ch); disabled = 1; break; default: @@ -340,16 +351,18 @@ static void sh_tmu_clock_event_mode(enum clock_event_mode mode, switch (mode) { case CLOCK_EVT_MODE_PERIODIC: - dev_info(&p->pdev->dev, "used for periodic clock events\n"); - sh_tmu_clock_event_start(p, 1); + dev_info(&ch->tmu->pdev->dev, + "used for periodic clock events\n"); + sh_tmu_clock_event_start(ch, 1); break; case CLOCK_EVT_MODE_ONESHOT: - dev_info(&p->pdev->dev, "used for oneshot clock events\n"); - sh_tmu_clock_event_start(p, 0); + dev_info(&ch->tmu->pdev->dev, + "used for oneshot clock events\n"); + sh_tmu_clock_event_start(ch, 0); break; case CLOCK_EVT_MODE_UNUSED: if (!disabled) - sh_tmu_disable(p); + sh_tmu_disable(ch); break; case CLOCK_EVT_MODE_SHUTDOWN: default: @@ -360,29 +373,29 @@ static void sh_tmu_clock_event_mode(enum clock_event_mode mode, static int sh_tmu_clock_event_next(unsigned long delta, struct clock_event_device *ced) { - struct sh_tmu_priv *p = ced_to_sh_tmu(ced); + struct sh_tmu_channel *ch = ced_to_sh_tmu(ced); BUG_ON(ced->mode != CLOCK_EVT_MODE_ONESHOT); /* program new delta value */ - sh_tmu_set_next(p, delta, 0); + sh_tmu_set_next(ch, delta, 0); return 0; } static void sh_tmu_clock_event_suspend(struct clock_event_device *ced) { - pm_genpd_syscore_poweroff(&ced_to_sh_tmu(ced)->pdev->dev); + pm_genpd_syscore_poweroff(&ced_to_sh_tmu(ced)->tmu->pdev->dev); } static void sh_tmu_clock_event_resume(struct clock_event_device *ced) { - pm_genpd_syscore_poweron(&ced_to_sh_tmu(ced)->pdev->dev); + pm_genpd_syscore_poweron(&ced_to_sh_tmu(ced)->tmu->pdev->dev); } -static void sh_tmu_register_clockevent(struct sh_tmu_priv *p, +static void sh_tmu_register_clockevent(struct sh_tmu_channel *ch, char *name, unsigned long rating) { - struct clock_event_device *ced = &p->ced; + struct clock_event_device *ced = &ch->ced; int ret; memset(ced, 0, sizeof(*ced)); @@ -397,27 +410,28 @@ static void sh_tmu_register_clockevent(struct sh_tmu_priv *p, ced->suspend = sh_tmu_clock_event_suspend; ced->resume = sh_tmu_clock_event_resume; - dev_info(&p->pdev->dev, "used for clock events\n"); + dev_info(&ch->tmu->pdev->dev, "used for clock events\n"); clockevents_config_and_register(ced, 1, 0x300, 0xffffffff); - ret = request_irq(p->irq, sh_tmu_interrupt, + ret = request_irq(ch->irq, sh_tmu_interrupt, IRQF_TIMER | IRQF_IRQPOLL | IRQF_NOBALANCING, - dev_name(&p->pdev->dev), p); + dev_name(&ch->tmu->pdev->dev), ch); if (ret) { - dev_err(&p->pdev->dev, "failed to request irq %d\n", p->irq); + dev_err(&ch->tmu->pdev->dev, "failed to request irq %d\n", + ch->irq); return; } } -static int sh_tmu_register(struct sh_tmu_priv *p, char *name, +static int sh_tmu_register(struct sh_tmu_channel *ch, char *name, unsigned long clockevent_rating, unsigned long clocksource_rating) { if (clockevent_rating) - sh_tmu_register_clockevent(p, name, clockevent_rating); + sh_tmu_register_clockevent(ch, name, clockevent_rating); else if (clocksource_rating) - sh_tmu_register_clocksource(p, name, clocksource_rating); + sh_tmu_register_clocksource(ch, name, clocksource_rating); return 0; } @@ -445,8 +459,8 @@ static int sh_tmu_setup(struct sh_tmu_priv *p, struct platform_device *pdev) goto err0; } - p->irq = platform_get_irq(p->pdev, 0); - if (p->irq < 0) { + p->channel.irq = platform_get_irq(p->pdev, 0); + if (p->channel.irq < 0) { dev_err(&p->pdev->dev, "failed to get irq\n"); goto err0; } @@ -470,10 +484,11 @@ static int sh_tmu_setup(struct sh_tmu_priv *p, struct platform_device *pdev) if (ret < 0) goto err2; - p->cs_enabled = false; - p->enable_count = 0; + p->channel.cs_enabled = false; + p->channel.enable_count = 0; + p->channel.tmu = p; - ret = sh_tmu_register(p, (char *)dev_name(&p->pdev->dev), + ret = sh_tmu_register(&p->channel, (char *)dev_name(&p->pdev->dev), cfg->clockevent_rating, cfg->clocksource_rating); if (ret < 0) -- cgit From 0a72aa39cc105fbf6971feb8928a63530a4a446b Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 27 Jan 2014 22:04:17 +0100 Subject: clocksource: sh_tmu: Rename struct sh_tmu_priv to sh_tmu_device Channel data is private as well, rename priv to device to make the distrinction between the core device and the channels clearer. Signed-off-by: Laurent Pinchart --- drivers/clocksource/sh_tmu.c | 68 ++++++++++++++++++++++---------------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/drivers/clocksource/sh_tmu.c b/drivers/clocksource/sh_tmu.c index 26457e1fccbb..70137906b8c0 100644 --- a/drivers/clocksource/sh_tmu.c +++ b/drivers/clocksource/sh_tmu.c @@ -35,10 +35,10 @@ #include #include -struct sh_tmu_priv; +struct sh_tmu_device; struct sh_tmu_channel { - struct sh_tmu_priv *tmu; + struct sh_tmu_device *tmu; int irq; @@ -50,7 +50,7 @@ struct sh_tmu_channel { unsigned int enable_count; }; -struct sh_tmu_priv { +struct sh_tmu_device { struct platform_device *pdev; void __iomem *mapbase; @@ -436,59 +436,59 @@ static int sh_tmu_register(struct sh_tmu_channel *ch, char *name, return 0; } -static int sh_tmu_setup(struct sh_tmu_priv *p, struct platform_device *pdev) +static int sh_tmu_setup(struct sh_tmu_device *tmu, struct platform_device *pdev) { struct sh_timer_config *cfg = pdev->dev.platform_data; struct resource *res; int ret; ret = -ENXIO; - memset(p, 0, sizeof(*p)); - p->pdev = pdev; + memset(tmu, 0, sizeof(*tmu)); + tmu->pdev = pdev; if (!cfg) { - dev_err(&p->pdev->dev, "missing platform data\n"); + dev_err(&tmu->pdev->dev, "missing platform data\n"); goto err0; } - platform_set_drvdata(pdev, p); + platform_set_drvdata(pdev, tmu); - res = platform_get_resource(p->pdev, IORESOURCE_MEM, 0); + res = platform_get_resource(tmu->pdev, IORESOURCE_MEM, 0); if (!res) { - dev_err(&p->pdev->dev, "failed to get I/O memory\n"); + dev_err(&tmu->pdev->dev, "failed to get I/O memory\n"); goto err0; } - p->channel.irq = platform_get_irq(p->pdev, 0); - if (p->channel.irq < 0) { - dev_err(&p->pdev->dev, "failed to get irq\n"); + tmu->channel.irq = platform_get_irq(tmu->pdev, 0); + if (tmu->channel.irq < 0) { + dev_err(&tmu->pdev->dev, "failed to get irq\n"); goto err0; } /* map memory, let mapbase point to our channel */ - p->mapbase = ioremap_nocache(res->start, resource_size(res)); - if (p->mapbase == NULL) { - dev_err(&p->pdev->dev, "failed to remap I/O memory\n"); + tmu->mapbase = ioremap_nocache(res->start, resource_size(res)); + if (tmu->mapbase == NULL) { + dev_err(&tmu->pdev->dev, "failed to remap I/O memory\n"); goto err0; } /* get hold of clock */ - p->clk = clk_get(&p->pdev->dev, "tmu_fck"); - if (IS_ERR(p->clk)) { - dev_err(&p->pdev->dev, "cannot get clock\n"); - ret = PTR_ERR(p->clk); + tmu->clk = clk_get(&tmu->pdev->dev, "tmu_fck"); + if (IS_ERR(tmu->clk)) { + dev_err(&tmu->pdev->dev, "cannot get clock\n"); + ret = PTR_ERR(tmu->clk); goto err1; } - ret = clk_prepare(p->clk); + ret = clk_prepare(tmu->clk); if (ret < 0) goto err2; - p->channel.cs_enabled = false; - p->channel.enable_count = 0; - p->channel.tmu = p; + tmu->channel.cs_enabled = false; + tmu->channel.enable_count = 0; + tmu->channel.tmu = tmu; - ret = sh_tmu_register(&p->channel, (char *)dev_name(&p->pdev->dev), + ret = sh_tmu_register(&tmu->channel, (char *)dev_name(&tmu->pdev->dev), cfg->clockevent_rating, cfg->clocksource_rating); if (ret < 0) @@ -497,18 +497,18 @@ static int sh_tmu_setup(struct sh_tmu_priv *p, struct platform_device *pdev) return 0; err3: - clk_unprepare(p->clk); + clk_unprepare(tmu->clk); err2: - clk_put(p->clk); + clk_put(tmu->clk); err1: - iounmap(p->mapbase); + iounmap(tmu->mapbase); err0: return ret; } static int sh_tmu_probe(struct platform_device *pdev) { - struct sh_tmu_priv *p = platform_get_drvdata(pdev); + struct sh_tmu_device *tmu = platform_get_drvdata(pdev); struct sh_timer_config *cfg = pdev->dev.platform_data; int ret; @@ -517,20 +517,20 @@ static int sh_tmu_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); } - if (p) { + if (tmu) { dev_info(&pdev->dev, "kept as earlytimer\n"); goto out; } - p = kmalloc(sizeof(*p), GFP_KERNEL); - if (p == NULL) { + tmu = kmalloc(sizeof(*tmu), GFP_KERNEL); + if (tmu == NULL) { dev_err(&pdev->dev, "failed to allocate driver data\n"); return -ENOMEM; } - ret = sh_tmu_setup(p, pdev); + ret = sh_tmu_setup(tmu, pdev); if (ret) { - kfree(p); + kfree(tmu); pm_runtime_idle(&pdev->dev); return ret; } -- cgit From a94ddaa6fcd46e168736027535b2d81b6a18883f Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 27 Jan 2014 22:04:17 +0100 Subject: clocksource: sh_tmu: Split channel setup to separate function Move the channel setup code from sh_tmu_setup to a new sh_tmu_setup_channel function and call it from sh_tmu_setup. Signed-off-by: Laurent Pinchart --- drivers/clocksource/sh_tmu.c | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/drivers/clocksource/sh_tmu.c b/drivers/clocksource/sh_tmu.c index 70137906b8c0..4779c97bb2ee 100644 --- a/drivers/clocksource/sh_tmu.c +++ b/drivers/clocksource/sh_tmu.c @@ -436,6 +436,28 @@ static int sh_tmu_register(struct sh_tmu_channel *ch, char *name, return 0; } +static int sh_tmu_channel_setup(struct sh_tmu_channel *ch, + struct sh_tmu_device *tmu) +{ + struct sh_timer_config *cfg = tmu->pdev->dev.platform_data; + + memset(ch, 0, sizeof(*ch)); + ch->tmu = tmu; + + ch->irq = platform_get_irq(tmu->pdev, 0); + if (ch->irq < 0) { + dev_err(&tmu->pdev->dev, "failed to get irq\n"); + return ch->irq; + } + + ch->cs_enabled = false; + ch->enable_count = 0; + + return sh_tmu_register(ch, (char *)dev_name(&tmu->pdev->dev), + cfg->clockevent_rating, + cfg->clocksource_rating); +} + static int sh_tmu_setup(struct sh_tmu_device *tmu, struct platform_device *pdev) { struct sh_timer_config *cfg = pdev->dev.platform_data; @@ -459,12 +481,6 @@ static int sh_tmu_setup(struct sh_tmu_device *tmu, struct platform_device *pdev) goto err0; } - tmu->channel.irq = platform_get_irq(tmu->pdev, 0); - if (tmu->channel.irq < 0) { - dev_err(&tmu->pdev->dev, "failed to get irq\n"); - goto err0; - } - /* map memory, let mapbase point to our channel */ tmu->mapbase = ioremap_nocache(res->start, resource_size(res)); if (tmu->mapbase == NULL) { @@ -484,13 +500,7 @@ static int sh_tmu_setup(struct sh_tmu_device *tmu, struct platform_device *pdev) if (ret < 0) goto err2; - tmu->channel.cs_enabled = false; - tmu->channel.enable_count = 0; - tmu->channel.tmu = tmu; - - ret = sh_tmu_register(&tmu->channel, (char *)dev_name(&tmu->pdev->dev), - cfg->clockevent_rating, - cfg->clocksource_rating); + ret = sh_tmu_channel_setup(&tmu->channel, tmu); if (ret < 0) goto err3; -- cgit From 84876d0505b15a2907696566a80a365993feab44 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 17 Feb 2014 16:04:16 +0100 Subject: clocksource: sh_tmu: Constify name argument to sh_tmu_register() The name argument is assigned to const structure fields only, constify it. Signed-off-by: Laurent Pinchart --- drivers/clocksource/sh_tmu.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/clocksource/sh_tmu.c b/drivers/clocksource/sh_tmu.c index 4779c97bb2ee..2c64e3f93f16 100644 --- a/drivers/clocksource/sh_tmu.c +++ b/drivers/clocksource/sh_tmu.c @@ -292,7 +292,7 @@ static void sh_tmu_clocksource_resume(struct clocksource *cs) } static int sh_tmu_register_clocksource(struct sh_tmu_channel *ch, - char *name, unsigned long rating) + const char *name, unsigned long rating) { struct clocksource *cs = &ch->cs; @@ -393,7 +393,7 @@ static void sh_tmu_clock_event_resume(struct clock_event_device *ced) } static void sh_tmu_register_clockevent(struct sh_tmu_channel *ch, - char *name, unsigned long rating) + const char *name, unsigned long rating) { struct clock_event_device *ced = &ch->ced; int ret; @@ -424,7 +424,7 @@ static void sh_tmu_register_clockevent(struct sh_tmu_channel *ch, } } -static int sh_tmu_register(struct sh_tmu_channel *ch, char *name, +static int sh_tmu_register(struct sh_tmu_channel *ch, const char *name, unsigned long clockevent_rating, unsigned long clocksource_rating) { @@ -453,7 +453,7 @@ static int sh_tmu_channel_setup(struct sh_tmu_channel *ch, ch->cs_enabled = false; ch->enable_count = 0; - return sh_tmu_register(ch, (char *)dev_name(&tmu->pdev->dev), + return sh_tmu_register(ch, dev_name(&tmu->pdev->dev), cfg->clockevent_rating, cfg->clocksource_rating); } -- cgit From de693461bf9624ec12808f8c5524510364cc2a43 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 27 Jan 2014 22:04:17 +0100 Subject: clocksource: sh_tmu: Add memory base to sh_tmu_channel structure The channel memory base is channel-specific, add it to the channel structure in preparation for support of multiple channels per device. Signed-off-by: Laurent Pinchart --- drivers/clocksource/sh_tmu.c | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/drivers/clocksource/sh_tmu.c b/drivers/clocksource/sh_tmu.c index 2c64e3f93f16..a464ed868a68 100644 --- a/drivers/clocksource/sh_tmu.c +++ b/drivers/clocksource/sh_tmu.c @@ -40,6 +40,7 @@ struct sh_tmu_device; struct sh_tmu_channel { struct sh_tmu_device *tmu; + void __iomem *base; int irq; unsigned long rate; @@ -68,39 +69,35 @@ static DEFINE_RAW_SPINLOCK(sh_tmu_lock); static inline unsigned long sh_tmu_read(struct sh_tmu_channel *ch, int reg_nr) { - struct sh_timer_config *cfg = ch->tmu->pdev->dev.platform_data; - void __iomem *base = ch->tmu->mapbase; unsigned long offs; if (reg_nr == TSTR) - return ioread8(base - cfg->channel_offset); + return ioread8(ch->tmu->mapbase); offs = reg_nr << 2; if (reg_nr == TCR) - return ioread16(base + offs); + return ioread16(ch->base + offs); else - return ioread32(base + offs); + return ioread32(ch->base + offs); } static inline void sh_tmu_write(struct sh_tmu_channel *ch, int reg_nr, unsigned long value) { - struct sh_timer_config *cfg = ch->tmu->pdev->dev.platform_data; - void __iomem *base = ch->tmu->mapbase; unsigned long offs; if (reg_nr == TSTR) { - iowrite8(value, base - cfg->channel_offset); + iowrite8(value, ch->tmu->mapbase); return; } offs = reg_nr << 2; if (reg_nr == TCR) - iowrite16(value, base + offs); + iowrite16(value, ch->base + offs); else - iowrite32(value, base + offs); + iowrite32(value, ch->base + offs); } static void sh_tmu_start_stop_ch(struct sh_tmu_channel *ch, int start) @@ -481,13 +478,18 @@ static int sh_tmu_setup(struct sh_tmu_device *tmu, struct platform_device *pdev) goto err0; } - /* map memory, let mapbase point to our channel */ - tmu->mapbase = ioremap_nocache(res->start, resource_size(res)); - if (tmu->mapbase == NULL) { + /* + * Map memory, let channel.base point to our channel and mapbase to the + * start/stop shared register. + */ + tmu->channel.base = ioremap_nocache(res->start, resource_size(res)); + if (tmu->channel.base == NULL) { dev_err(&tmu->pdev->dev, "failed to remap I/O memory\n"); goto err0; } + tmu->mapbase = tmu->channel.base - cfg->channel_offset; + /* get hold of clock */ tmu->clk = clk_get(&tmu->pdev->dev, "tmu_fck"); if (IS_ERR(tmu->clk)) { @@ -511,7 +513,7 @@ static int sh_tmu_setup(struct sh_tmu_device *tmu, struct platform_device *pdev) err2: clk_put(tmu->clk); err1: - iounmap(tmu->mapbase); + iounmap(tmu->channel.base); err0: return ret; } -- cgit From fe68eb802ef8bf034735f37cb561ab814fb3b0d6 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 27 Jan 2014 22:04:17 +0100 Subject: clocksource: sh_tmu: Add index to struct sh_tmu_channel Use the index as the timer start/stop bit and when printing messages to identify the channel. Signed-off-by: Laurent Pinchart --- drivers/clocksource/sh_tmu.c | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/drivers/clocksource/sh_tmu.c b/drivers/clocksource/sh_tmu.c index a464ed868a68..e30430439bb1 100644 --- a/drivers/clocksource/sh_tmu.c +++ b/drivers/clocksource/sh_tmu.c @@ -39,6 +39,7 @@ struct sh_tmu_device; struct sh_tmu_channel { struct sh_tmu_device *tmu; + unsigned int index; void __iomem *base; int irq; @@ -102,7 +103,6 @@ static inline void sh_tmu_write(struct sh_tmu_channel *ch, int reg_nr, static void sh_tmu_start_stop_ch(struct sh_tmu_channel *ch, int start) { - struct sh_timer_config *cfg = ch->tmu->pdev->dev.platform_data; unsigned long flags, value; /* start stop register shared by multiple timer channels */ @@ -110,9 +110,9 @@ static void sh_tmu_start_stop_ch(struct sh_tmu_channel *ch, int start) value = sh_tmu_read(ch, TSTR); if (start) - value |= 1 << cfg->timer_bit; + value |= 1 << ch->index; else - value &= ~(1 << cfg->timer_bit); + value &= ~(1 << ch->index); sh_tmu_write(ch, TSTR, value); raw_spin_unlock_irqrestore(&sh_tmu_lock, flags); @@ -125,7 +125,8 @@ static int __sh_tmu_enable(struct sh_tmu_channel *ch) /* enable clock */ ret = clk_enable(ch->tmu->clk); if (ret) { - dev_err(&ch->tmu->pdev->dev, "cannot enable clock\n"); + dev_err(&ch->tmu->pdev->dev, "ch%u: cannot enable clock\n", + ch->index); return ret; } @@ -303,7 +304,8 @@ static int sh_tmu_register_clocksource(struct sh_tmu_channel *ch, cs->mask = CLOCKSOURCE_MASK(32); cs->flags = CLOCK_SOURCE_IS_CONTINUOUS; - dev_info(&ch->tmu->pdev->dev, "used as clock source\n"); + dev_info(&ch->tmu->pdev->dev, "ch%u: used as clock source\n", + ch->index); /* Register with dummy 1 Hz value, gets updated in ->enable() */ clocksource_register_hz(cs, 1); @@ -349,12 +351,12 @@ static void sh_tmu_clock_event_mode(enum clock_event_mode mode, switch (mode) { case CLOCK_EVT_MODE_PERIODIC: dev_info(&ch->tmu->pdev->dev, - "used for periodic clock events\n"); + "ch%u: used for periodic clock events\n", ch->index); sh_tmu_clock_event_start(ch, 1); break; case CLOCK_EVT_MODE_ONESHOT: dev_info(&ch->tmu->pdev->dev, - "used for oneshot clock events\n"); + "ch%u: used for oneshot clock events\n", ch->index); sh_tmu_clock_event_start(ch, 0); break; case CLOCK_EVT_MODE_UNUSED: @@ -407,7 +409,8 @@ static void sh_tmu_register_clockevent(struct sh_tmu_channel *ch, ced->suspend = sh_tmu_clock_event_suspend; ced->resume = sh_tmu_clock_event_resume; - dev_info(&ch->tmu->pdev->dev, "used for clock events\n"); + dev_info(&ch->tmu->pdev->dev, "ch%u: used for clock events\n", + ch->index); clockevents_config_and_register(ced, 1, 0x300, 0xffffffff); @@ -415,8 +418,8 @@ static void sh_tmu_register_clockevent(struct sh_tmu_channel *ch, IRQF_TIMER | IRQF_IRQPOLL | IRQF_NOBALANCING, dev_name(&ch->tmu->pdev->dev), ch); if (ret) { - dev_err(&ch->tmu->pdev->dev, "failed to request irq %d\n", - ch->irq); + dev_err(&ch->tmu->pdev->dev, "ch%u: failed to request irq %d\n", + ch->index, ch->irq); return; } } @@ -441,9 +444,19 @@ static int sh_tmu_channel_setup(struct sh_tmu_channel *ch, memset(ch, 0, sizeof(*ch)); ch->tmu = tmu; + /* + * The SH3 variant (SH770x, SH7705, SH7710 and SH7720) maps channel + * registers blocks at base + 2 + 12 * index, while all other variants + * map them at base + 4 + 12 * index. We can compute the index by just + * dividing by 12, the 2 bytes or 4 bytes offset being hidden by the + * integer division. + */ + ch->index = cfg->channel_offset / 12; + ch->irq = platform_get_irq(tmu->pdev, 0); if (ch->irq < 0) { - dev_err(&tmu->pdev->dev, "failed to get irq\n"); + dev_err(&tmu->pdev->dev, "ch%u: failed to get irq\n", + ch->index); return ch->irq; } -- cgit From 3b77a83eeabb885c5fff02756eba50f446a2d83c Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 27 Jan 2014 22:04:17 +0100 Subject: clocksource: sh_tmu: Replace kmalloc + memset with kzalloc One kzalloc a day keeps the bugs away. Signed-off-by: Laurent Pinchart --- drivers/clocksource/sh_tmu.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/clocksource/sh_tmu.c b/drivers/clocksource/sh_tmu.c index e30430439bb1..26a9f7dadfbc 100644 --- a/drivers/clocksource/sh_tmu.c +++ b/drivers/clocksource/sh_tmu.c @@ -397,8 +397,6 @@ static void sh_tmu_register_clockevent(struct sh_tmu_channel *ch, struct clock_event_device *ced = &ch->ced; int ret; - memset(ced, 0, sizeof(*ced)); - ced->name = name; ced->features = CLOCK_EVT_FEAT_PERIODIC; ced->features |= CLOCK_EVT_FEAT_ONESHOT; @@ -441,7 +439,6 @@ static int sh_tmu_channel_setup(struct sh_tmu_channel *ch, { struct sh_timer_config *cfg = tmu->pdev->dev.platform_data; - memset(ch, 0, sizeof(*ch)); ch->tmu = tmu; /* @@ -475,7 +472,6 @@ static int sh_tmu_setup(struct sh_tmu_device *tmu, struct platform_device *pdev) int ret; ret = -ENXIO; - memset(tmu, 0, sizeof(*tmu)); tmu->pdev = pdev; if (!cfg) { @@ -547,7 +543,7 @@ static int sh_tmu_probe(struct platform_device *pdev) goto out; } - tmu = kmalloc(sizeof(*tmu), GFP_KERNEL); + tmu = kzalloc(sizeof(*tmu), GFP_KERNEL); if (tmu == NULL) { dev_err(&pdev->dev, "failed to allocate driver data\n"); return -ENOMEM; -- cgit From a5de49f436e2bc498c1d13f6f8a9afaf19cb5f95 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 27 Jan 2014 22:04:17 +0100 Subject: clocksource: sh_tmu: Allocate channels dynamically This prepares the driver for multi-channel support. Signed-off-by: Laurent Pinchart --- drivers/clocksource/sh_tmu.c | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/drivers/clocksource/sh_tmu.c b/drivers/clocksource/sh_tmu.c index 26a9f7dadfbc..55b7a37f0c9b 100644 --- a/drivers/clocksource/sh_tmu.c +++ b/drivers/clocksource/sh_tmu.c @@ -58,7 +58,8 @@ struct sh_tmu_device { void __iomem *mapbase; struct clk *clk; - struct sh_tmu_channel channel; + struct sh_tmu_channel *channels; + unsigned int num_channels; }; static DEFINE_RAW_SPINLOCK(sh_tmu_lock); @@ -469,6 +470,7 @@ static int sh_tmu_setup(struct sh_tmu_device *tmu, struct platform_device *pdev) { struct sh_timer_config *cfg = pdev->dev.platform_data; struct resource *res; + void __iomem *base; int ret; ret = -ENXIO; @@ -488,16 +490,16 @@ static int sh_tmu_setup(struct sh_tmu_device *tmu, struct platform_device *pdev) } /* - * Map memory, let channel.base point to our channel and mapbase to the + * Map memory, let base point to our channel and mapbase to the * start/stop shared register. */ - tmu->channel.base = ioremap_nocache(res->start, resource_size(res)); - if (tmu->channel.base == NULL) { + base = ioremap_nocache(res->start, resource_size(res)); + if (base == NULL) { dev_err(&tmu->pdev->dev, "failed to remap I/O memory\n"); goto err0; } - tmu->mapbase = tmu->channel.base - cfg->channel_offset; + tmu->mapbase = base - cfg->channel_offset; /* get hold of clock */ tmu->clk = clk_get(&tmu->pdev->dev, "tmu_fck"); @@ -511,18 +513,29 @@ static int sh_tmu_setup(struct sh_tmu_device *tmu, struct platform_device *pdev) if (ret < 0) goto err2; - ret = sh_tmu_channel_setup(&tmu->channel, tmu); + tmu->channels = kzalloc(sizeof(*tmu->channels), GFP_KERNEL); + if (tmu->channels == NULL) { + ret = -ENOMEM; + goto err3; + } + + tmu->num_channels = 1; + + tmu->channels[0].base = base; + + ret = sh_tmu_channel_setup(&tmu->channels[0], tmu); if (ret < 0) goto err3; return 0; err3: + kfree(tmu->channels); clk_unprepare(tmu->clk); err2: clk_put(tmu->clk); err1: - iounmap(tmu->channel.base); + iounmap(base); err0: return ret; } -- cgit From 5cfe2d151f8f55052f5463e725d3d3a4aa51335d Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 29 Jan 2014 00:33:08 +0100 Subject: clocksource: sh_tmu: Replace hardcoded register values with macros Define symbolic macros for all used registers bits. Signed-off-by: Laurent Pinchart --- drivers/clocksource/sh_tmu.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/drivers/clocksource/sh_tmu.c b/drivers/clocksource/sh_tmu.c index 55b7a37f0c9b..63ed92d56c8f 100644 --- a/drivers/clocksource/sh_tmu.c +++ b/drivers/clocksource/sh_tmu.c @@ -69,6 +69,15 @@ static DEFINE_RAW_SPINLOCK(sh_tmu_lock); #define TCNT 1 /* channel register */ #define TCR 2 /* channel register */ +#define TCR_UNF (1 << 8) +#define TCR_UNIE (1 << 5) +#define TCR_TPSC_CLK4 (0 << 0) +#define TCR_TPSC_CLK16 (1 << 0) +#define TCR_TPSC_CLK64 (2 << 0) +#define TCR_TPSC_CLK256 (3 << 0) +#define TCR_TPSC_CLK1024 (4 << 0) +#define TCR_TPSC_MASK (7 << 0) + static inline unsigned long sh_tmu_read(struct sh_tmu_channel *ch, int reg_nr) { unsigned long offs; @@ -140,7 +149,7 @@ static int __sh_tmu_enable(struct sh_tmu_channel *ch) /* configure channel to parent clock / 4, irq off */ ch->rate = clk_get_rate(ch->tmu->clk) / 4; - sh_tmu_write(ch, TCR, 0x0000); + sh_tmu_write(ch, TCR, TCR_TPSC_CLK4); /* enable channel */ sh_tmu_start_stop_ch(ch, 1); @@ -165,7 +174,7 @@ static void __sh_tmu_disable(struct sh_tmu_channel *ch) sh_tmu_start_stop_ch(ch, 0); /* disable interrupts in TMU block */ - sh_tmu_write(ch, TCR, 0x0000); + sh_tmu_write(ch, TCR, TCR_TPSC_CLK4); /* stop clock */ clk_disable(ch->tmu->clk); @@ -195,7 +204,7 @@ static void sh_tmu_set_next(struct sh_tmu_channel *ch, unsigned long delta, sh_tmu_read(ch, TCR); /* enable interrupt */ - sh_tmu_write(ch, TCR, 0x0020); + sh_tmu_write(ch, TCR, TCR_UNIE | TCR_TPSC_CLK4); /* reload delta value in case of periodic timer */ if (periodic) @@ -215,9 +224,9 @@ static irqreturn_t sh_tmu_interrupt(int irq, void *dev_id) /* disable or acknowledge interrupt */ if (ch->ced.mode == CLOCK_EVT_MODE_ONESHOT) - sh_tmu_write(ch, TCR, 0x0000); + sh_tmu_write(ch, TCR, TCR_TPSC_CLK4); else - sh_tmu_write(ch, TCR, 0x0020); + sh_tmu_write(ch, TCR, TCR_UNIE | TCR_TPSC_CLK4); /* notify clockevent layer */ ch->ced.event_handler(&ch->ced); -- cgit From f1010ed1a13ea38f495ebfa2fdb1f38b7f87301f Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 19 Feb 2014 17:00:31 +0100 Subject: clocksource: sh_tmu: Hardcode TMU clock event and source ratings to 200 All boards use clock event and clock source ratings of 200 for the TMU, hardcode it in the driver. Signed-off-by: Laurent Pinchart --- drivers/clocksource/sh_tmu.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/drivers/clocksource/sh_tmu.c b/drivers/clocksource/sh_tmu.c index 63ed92d56c8f..fec9bedb8f45 100644 --- a/drivers/clocksource/sh_tmu.c +++ b/drivers/clocksource/sh_tmu.c @@ -300,12 +300,12 @@ static void sh_tmu_clocksource_resume(struct clocksource *cs) } static int sh_tmu_register_clocksource(struct sh_tmu_channel *ch, - const char *name, unsigned long rating) + const char *name) { struct clocksource *cs = &ch->cs; cs->name = name; - cs->rating = rating; + cs->rating = 200; cs->read = sh_tmu_clocksource_read; cs->enable = sh_tmu_clocksource_enable; cs->disable = sh_tmu_clocksource_disable; @@ -402,7 +402,7 @@ static void sh_tmu_clock_event_resume(struct clock_event_device *ced) } static void sh_tmu_register_clockevent(struct sh_tmu_channel *ch, - const char *name, unsigned long rating) + const char *name) { struct clock_event_device *ced = &ch->ced; int ret; @@ -410,7 +410,7 @@ static void sh_tmu_register_clockevent(struct sh_tmu_channel *ch, ced->name = name; ced->features = CLOCK_EVT_FEAT_PERIODIC; ced->features |= CLOCK_EVT_FEAT_ONESHOT; - ced->rating = rating; + ced->rating = 200; ced->cpumask = cpumask_of(0); ced->set_next_event = sh_tmu_clock_event_next; ced->set_mode = sh_tmu_clock_event_mode; @@ -433,13 +433,12 @@ static void sh_tmu_register_clockevent(struct sh_tmu_channel *ch, } static int sh_tmu_register(struct sh_tmu_channel *ch, const char *name, - unsigned long clockevent_rating, - unsigned long clocksource_rating) + bool clockevent, bool clocksource) { - if (clockevent_rating) - sh_tmu_register_clockevent(ch, name, clockevent_rating); - else if (clocksource_rating) - sh_tmu_register_clocksource(ch, name, clocksource_rating); + if (clockevent) + sh_tmu_register_clockevent(ch, name); + else if (clocksource) + sh_tmu_register_clocksource(ch, name); return 0; } @@ -471,8 +470,8 @@ static int sh_tmu_channel_setup(struct sh_tmu_channel *ch, ch->enable_count = 0; return sh_tmu_register(ch, dev_name(&tmu->pdev->dev), - cfg->clockevent_rating, - cfg->clocksource_rating); + cfg->clockevent_rating != 0, + cfg->clocksource_rating != 0); } static int sh_tmu_setup(struct sh_tmu_device *tmu, struct platform_device *pdev) -- cgit From 8c7f21e6739ad836f30561d641393a8417abdad3 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 28 Jan 2014 12:36:48 +0100 Subject: clocksource: sh_tmu: Add support for multiple channels per device TMU hardware devices can support multiple channels, with global registers and per-channel registers. The sh_tmu driver currently models the hardware with one Linux device per channel. This model makes it difficult to handle global registers in a clean way. Add support for a new model that uses one Linux device per timer with multiple channels per device. This requires changes to platform data, add new channel configuration fields. Support for the legacy model is kept and will be removed after all platforms switch to the new model. Signed-off-by: Laurent Pinchart --- drivers/clocksource/sh_tmu.c | 213 ++++++++++++++++++++++++++++++------------- 1 file changed, 152 insertions(+), 61 deletions(-) diff --git a/drivers/clocksource/sh_tmu.c b/drivers/clocksource/sh_tmu.c index fec9bedb8f45..0306d31e9f1d 100644 --- a/drivers/clocksource/sh_tmu.c +++ b/drivers/clocksource/sh_tmu.c @@ -35,6 +35,12 @@ #include #include +enum sh_tmu_model { + SH_TMU_LEGACY, + SH_TMU, + SH_TMU_SH3, +}; + struct sh_tmu_device; struct sh_tmu_channel { @@ -58,8 +64,13 @@ struct sh_tmu_device { void __iomem *mapbase; struct clk *clk; + enum sh_tmu_model model; + struct sh_tmu_channel *channels; unsigned int num_channels; + + bool has_clockevent; + bool has_clocksource; }; static DEFINE_RAW_SPINLOCK(sh_tmu_lock); @@ -82,8 +93,16 @@ static inline unsigned long sh_tmu_read(struct sh_tmu_channel *ch, int reg_nr) { unsigned long offs; - if (reg_nr == TSTR) - return ioread8(ch->tmu->mapbase); + if (reg_nr == TSTR) { + switch (ch->tmu->model) { + case SH_TMU_LEGACY: + return ioread8(ch->tmu->mapbase); + case SH_TMU_SH3: + return ioread8(ch->tmu->mapbase + 2); + case SH_TMU: + return ioread8(ch->tmu->mapbase + 4); + } + } offs = reg_nr << 2; @@ -99,8 +118,14 @@ static inline void sh_tmu_write(struct sh_tmu_channel *ch, int reg_nr, unsigned long offs; if (reg_nr == TSTR) { - iowrite8(value, ch->tmu->mapbase); - return; + switch (ch->tmu->model) { + case SH_TMU_LEGACY: + return iowrite8(value, ch->tmu->mapbase); + case SH_TMU_SH3: + return iowrite8(value, ch->tmu->mapbase + 2); + case SH_TMU: + return iowrite8(value, ch->tmu->mapbase + 4); + } } offs = reg_nr << 2; @@ -435,31 +460,49 @@ static void sh_tmu_register_clockevent(struct sh_tmu_channel *ch, static int sh_tmu_register(struct sh_tmu_channel *ch, const char *name, bool clockevent, bool clocksource) { - if (clockevent) + if (clockevent) { + ch->tmu->has_clockevent = true; sh_tmu_register_clockevent(ch, name); - else if (clocksource) + } else if (clocksource) { + ch->tmu->has_clocksource = true; sh_tmu_register_clocksource(ch, name); + } return 0; } -static int sh_tmu_channel_setup(struct sh_tmu_channel *ch, +static int sh_tmu_channel_setup(struct sh_tmu_channel *ch, unsigned int index, + bool clockevent, bool clocksource, struct sh_tmu_device *tmu) { - struct sh_timer_config *cfg = tmu->pdev->dev.platform_data; + /* Skip unused channels. */ + if (!clockevent && !clocksource) + return 0; ch->tmu = tmu; - /* - * The SH3 variant (SH770x, SH7705, SH7710 and SH7720) maps channel - * registers blocks at base + 2 + 12 * index, while all other variants - * map them at base + 4 + 12 * index. We can compute the index by just - * dividing by 12, the 2 bytes or 4 bytes offset being hidden by the - * integer division. - */ - ch->index = cfg->channel_offset / 12; + if (tmu->model == SH_TMU_LEGACY) { + struct sh_timer_config *cfg = tmu->pdev->dev.platform_data; + + /* + * The SH3 variant (SH770x, SH7705, SH7710 and SH7720) maps + * channel registers blocks at base + 2 + 12 * index, while all + * other variants map them at base + 4 + 12 * index. We can + * compute the index by just dividing by 12, the 2 bytes or 4 + * bytes offset being hidden by the integer division. + */ + ch->index = cfg->channel_offset / 12; + ch->base = tmu->mapbase + cfg->channel_offset; + } else { + ch->index = index; + + if (tmu->model == SH_TMU_SH3) + ch->base = tmu->mapbase + 4 + ch->index * 12; + else + ch->base = tmu->mapbase + 8 + ch->index * 12; + } - ch->irq = platform_get_irq(tmu->pdev, 0); + ch->irq = platform_get_irq(tmu->pdev, ch->index); if (ch->irq < 0) { dev_err(&tmu->pdev->dev, "ch%u: failed to get irq\n", ch->index); @@ -470,88 +513,127 @@ static int sh_tmu_channel_setup(struct sh_tmu_channel *ch, ch->enable_count = 0; return sh_tmu_register(ch, dev_name(&tmu->pdev->dev), - cfg->clockevent_rating != 0, - cfg->clocksource_rating != 0); + clockevent, clocksource); } -static int sh_tmu_setup(struct sh_tmu_device *tmu, struct platform_device *pdev) +static int sh_tmu_map_memory(struct sh_tmu_device *tmu) { - struct sh_timer_config *cfg = pdev->dev.platform_data; struct resource *res; - void __iomem *base; - int ret; - ret = -ENXIO; - - tmu->pdev = pdev; - - if (!cfg) { - dev_err(&tmu->pdev->dev, "missing platform data\n"); - goto err0; - } - - platform_set_drvdata(pdev, tmu); res = platform_get_resource(tmu->pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(&tmu->pdev->dev, "failed to get I/O memory\n"); - goto err0; + return -ENXIO; } + tmu->mapbase = ioremap_nocache(res->start, resource_size(res)); + if (tmu->mapbase == NULL) + return -ENXIO; + /* - * Map memory, let base point to our channel and mapbase to the - * start/stop shared register. + * In legacy platform device configuration (with one device per channel) + * the resource points to the channel base address. */ - base = ioremap_nocache(res->start, resource_size(res)); - if (base == NULL) { - dev_err(&tmu->pdev->dev, "failed to remap I/O memory\n"); - goto err0; + if (tmu->model == SH_TMU_LEGACY) { + struct sh_timer_config *cfg = tmu->pdev->dev.platform_data; + tmu->mapbase -= cfg->channel_offset; } - tmu->mapbase = base - cfg->channel_offset; + return 0; +} - /* get hold of clock */ +static void sh_tmu_unmap_memory(struct sh_tmu_device *tmu) +{ + if (tmu->model == SH_TMU_LEGACY) { + struct sh_timer_config *cfg = tmu->pdev->dev.platform_data; + tmu->mapbase += cfg->channel_offset; + } + + iounmap(tmu->mapbase); +} + +static int sh_tmu_setup(struct sh_tmu_device *tmu, struct platform_device *pdev) +{ + struct sh_timer_config *cfg = pdev->dev.platform_data; + const struct platform_device_id *id = pdev->id_entry; + unsigned int i; + int ret; + + if (!cfg) { + dev_err(&tmu->pdev->dev, "missing platform data\n"); + return -ENXIO; + } + + tmu->pdev = pdev; + tmu->model = id->driver_data; + + /* Get hold of clock. */ tmu->clk = clk_get(&tmu->pdev->dev, "tmu_fck"); if (IS_ERR(tmu->clk)) { dev_err(&tmu->pdev->dev, "cannot get clock\n"); - ret = PTR_ERR(tmu->clk); - goto err1; + return PTR_ERR(tmu->clk); } ret = clk_prepare(tmu->clk); if (ret < 0) - goto err2; + goto err_clk_put; + + /* Map the memory resource. */ + ret = sh_tmu_map_memory(tmu); + if (ret < 0) { + dev_err(&tmu->pdev->dev, "failed to remap I/O memory\n"); + goto err_clk_unprepare; + } - tmu->channels = kzalloc(sizeof(*tmu->channels), GFP_KERNEL); + /* Allocate and setup the channels. */ + if (tmu->model == SH_TMU_LEGACY) + tmu->num_channels = 1; + else + tmu->num_channels = hweight8(cfg->channels_mask); + + tmu->channels = kzalloc(sizeof(*tmu->channels) * tmu->num_channels, + GFP_KERNEL); if (tmu->channels == NULL) { ret = -ENOMEM; - goto err3; + goto err_unmap; } - tmu->num_channels = 1; - - tmu->channels[0].base = base; + if (tmu->model == SH_TMU_LEGACY) { + ret = sh_tmu_channel_setup(&tmu->channels[0], 0, + cfg->clockevent_rating != 0, + cfg->clocksource_rating != 0, tmu); + if (ret < 0) + goto err_unmap; + } else { + /* + * Use the first channel as a clock event device and the second + * channel as a clock source. + */ + for (i = 0; i < tmu->num_channels; ++i) { + ret = sh_tmu_channel_setup(&tmu->channels[i], i, + i == 0, i == 1, tmu); + if (ret < 0) + goto err_unmap; + } + } - ret = sh_tmu_channel_setup(&tmu->channels[0], tmu); - if (ret < 0) - goto err3; + platform_set_drvdata(pdev, tmu); return 0; - err3: +err_unmap: kfree(tmu->channels); + sh_tmu_unmap_memory(tmu); +err_clk_unprepare: clk_unprepare(tmu->clk); - err2: +err_clk_put: clk_put(tmu->clk); - err1: - iounmap(base); - err0: return ret; } static int sh_tmu_probe(struct platform_device *pdev) { struct sh_tmu_device *tmu = platform_get_drvdata(pdev); - struct sh_timer_config *cfg = pdev->dev.platform_data; int ret; if (!is_early_platform_device(pdev)) { @@ -580,7 +662,7 @@ static int sh_tmu_probe(struct platform_device *pdev) return 0; out: - if (cfg->clockevent_rating || cfg->clocksource_rating) + if (tmu->has_clockevent || tmu->has_clocksource) pm_runtime_irq_safe(&pdev->dev); else pm_runtime_idle(&pdev->dev); @@ -593,12 +675,21 @@ static int sh_tmu_remove(struct platform_device *pdev) return -EBUSY; /* cannot unregister clockevent and clocksource */ } +static const struct platform_device_id sh_tmu_id_table[] = { + { "sh_tmu", SH_TMU_LEGACY }, + { "sh-tmu", SH_TMU }, + { "sh-tmu-sh3", SH_TMU_SH3 }, + { } +}; +MODULE_DEVICE_TABLE(platform, sh_tmu_id_table); + static struct platform_driver sh_tmu_device_driver = { .probe = sh_tmu_probe, .remove = sh_tmu_remove, .driver = { .name = "sh_tmu", - } + }, + .id_table = sh_tmu_id_table, }; static int __init sh_tmu_init(void) -- cgit From a27d922749f3be0a88f7e0aeb507c373703c08ee Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 14 Feb 2014 00:35:18 +0100 Subject: clocksource: sh_tmu: Rename clock to "fck" in the non-legacy case The sh_tmu driver gets the TMU functional clock using a connection ID of "tmu_fck". While all SH SoCs create clock lookup entries with a NULL device ID and a "tmu_fck" connection ID, the ARM SoCs use the device ID only with a NULL connection ID. This works on legacy platforms but will break on ARM with DT boot. Fix the situation by using a connection ID of "fck" in the non-legacy platform data case. Clock lookup entries will be renamed to use the device ID as well as the connection ID as platforms get moved to new platform data. The legacy code will eventually be dropped, leaving us with device ID based clock lookup, compatible with DT boot. Signed-off-by: Laurent Pinchart --- drivers/clocksource/sh_tmu.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/clocksource/sh_tmu.c b/drivers/clocksource/sh_tmu.c index 0306d31e9f1d..cf07797dbcf3 100644 --- a/drivers/clocksource/sh_tmu.c +++ b/drivers/clocksource/sh_tmu.c @@ -568,7 +568,8 @@ static int sh_tmu_setup(struct sh_tmu_device *tmu, struct platform_device *pdev) tmu->model = id->driver_data; /* Get hold of clock. */ - tmu->clk = clk_get(&tmu->pdev->dev, "tmu_fck"); + tmu->clk = clk_get(&tmu->pdev->dev, + tmu->model == SH_TMU_LEGACY ? "tmu_fck" : "fck"); if (IS_ERR(tmu->clk)) { dev_err(&tmu->pdev->dev, "cannot get clock\n"); return PTR_ERR(tmu->clk); -- cgit From 6b96c15b034813ec0b46e5bebbf8cffae0ac72d0 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 14 Feb 2014 01:25:50 +0100 Subject: clocksource: sh_tmu: Remove FSF mail address from GPL notice Do not include the paragraph about writing to the Free Software Foundation's mailing address from the sample GPL notice. The FSF has changed addresses in the past, and may do so again. Linux already includes a copy of the GPL. Signed-off-by: Laurent Pinchart --- drivers/clocksource/sh_tmu.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/clocksource/sh_tmu.c b/drivers/clocksource/sh_tmu.c index cf07797dbcf3..981f8d302a2a 100644 --- a/drivers/clocksource/sh_tmu.c +++ b/drivers/clocksource/sh_tmu.c @@ -11,10 +11,6 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include -- cgit From 13931f8065fabff117828999e007f62a5cabea34 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 12 Feb 2014 16:56:44 +0100 Subject: clocksource: sh_tmu: Sort headers alphabetically This helps locating duplicates and inserting new headers. Signed-off-by: Laurent Pinchart --- drivers/clocksource/sh_tmu.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/clocksource/sh_tmu.c b/drivers/clocksource/sh_tmu.c index 981f8d302a2a..4ba2c0fea580 100644 --- a/drivers/clocksource/sh_tmu.c +++ b/drivers/clocksource/sh_tmu.c @@ -13,23 +13,23 @@ * GNU General Public License for more details. */ +#include +#include +#include +#include +#include #include -#include -#include #include -#include -#include #include -#include +#include #include -#include -#include -#include -#include -#include #include +#include #include #include +#include +#include +#include enum sh_tmu_model { SH_TMU_LEGACY, -- cgit From 276bee05d8b72e98d530b55161e0a2131da99f58 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 17 Feb 2014 11:27:49 +0100 Subject: clocksource: sh_mtu2: Use request_irq() instead of setup_irq() The driver claims it needs to register an interrupt handler too early for request_irq(). This might have been true in the past, but the only meaningful difference between request_irq() and setup_irq() today is an additional kzalloc() call in request_irq(). As the driver calls kmalloc() itself we know that the slab allocator is available, we can thus switch to request_irq(). Signed-off-by: Laurent Pinchart Tested-by: Wolfram Sang --- drivers/clocksource/sh_mtu2.c | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/drivers/clocksource/sh_mtu2.c b/drivers/clocksource/sh_mtu2.c index e30d76e0a6fa..77992e081205 100644 --- a/drivers/clocksource/sh_mtu2.c +++ b/drivers/clocksource/sh_mtu2.c @@ -37,7 +37,7 @@ struct sh_mtu2_priv { void __iomem *mapbase; struct clk *clk; - struct irqaction irqaction; + int irq; struct platform_device *pdev; unsigned long rate; unsigned long periodic; @@ -244,10 +244,11 @@ static void sh_mtu2_register_clockevent(struct sh_mtu2_priv *p, dev_info(&p->pdev->dev, "used for clock events\n"); clockevents_register_device(ced); - ret = setup_irq(p->irqaction.irq, &p->irqaction); + ret = request_irq(p->irq, sh_mtu2_interrupt, + IRQF_TIMER | IRQF_IRQPOLL | IRQF_NOBALANCING, + dev_name(&p->pdev->dev), p); if (ret) { - dev_err(&p->pdev->dev, "failed to request irq %d\n", - p->irqaction.irq); + dev_err(&p->pdev->dev, "failed to request irq %d\n", p->irq); return; } } @@ -265,7 +266,7 @@ static int sh_mtu2_setup(struct sh_mtu2_priv *p, struct platform_device *pdev) { struct sh_timer_config *cfg = pdev->dev.platform_data; struct resource *res; - int irq, ret; + int ret; ret = -ENXIO; memset(p, 0, sizeof(*p)); @@ -284,8 +285,8 @@ static int sh_mtu2_setup(struct sh_mtu2_priv *p, struct platform_device *pdev) goto err0; } - irq = platform_get_irq(p->pdev, 0); - if (irq < 0) { + p->irq = platform_get_irq(p->pdev, 0); + if (p->irq < 0) { dev_err(&p->pdev->dev, "failed to get irq\n"); goto err0; } @@ -297,13 +298,6 @@ static int sh_mtu2_setup(struct sh_mtu2_priv *p, struct platform_device *pdev) goto err0; } - /* setup data for setup_irq() (too early for request_irq()) */ - p->irqaction.name = dev_name(&p->pdev->dev); - p->irqaction.handler = sh_mtu2_interrupt; - p->irqaction.dev_id = p; - p->irqaction.irq = irq; - p->irqaction.flags = IRQF_TIMER | IRQF_IRQPOLL | IRQF_NOBALANCING; - /* get hold of clock */ p->clk = clk_get(&p->pdev->dev, "mtu2_fck"); if (IS_ERR(p->clk)) { -- cgit From f92d62f53973466cccb25900c2597ff6df950d74 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 4 Mar 2014 12:59:54 +0100 Subject: clocksource: sh_mtu2: Turn sh_mtu2_priv fields into local variables The rate and periodic fields are used in a single function only, as local variables. Remove them from the structure. Signed-off-by: Laurent Pinchart Tested-by: Wolfram Sang --- drivers/clocksource/sh_mtu2.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/clocksource/sh_mtu2.c b/drivers/clocksource/sh_mtu2.c index 77992e081205..66684552fcc9 100644 --- a/drivers/clocksource/sh_mtu2.c +++ b/drivers/clocksource/sh_mtu2.c @@ -39,8 +39,6 @@ struct sh_mtu2_priv { struct clk *clk; int irq; struct platform_device *pdev; - unsigned long rate; - unsigned long periodic; struct clock_event_device ced; }; @@ -122,6 +120,8 @@ static void sh_mtu2_start_stop_ch(struct sh_mtu2_priv *p, int start) static int sh_mtu2_enable(struct sh_mtu2_priv *p) { + unsigned long periodic; + unsigned long rate; int ret; pm_runtime_get_sync(&p->pdev->dev); @@ -137,13 +137,13 @@ static int sh_mtu2_enable(struct sh_mtu2_priv *p) /* make sure channel is disabled */ sh_mtu2_start_stop_ch(p, 0); - p->rate = clk_get_rate(p->clk) / 64; - p->periodic = (p->rate + HZ/2) / HZ; + rate = clk_get_rate(p->clk) / 64; + periodic = (rate + HZ/2) / HZ; /* "Periodic Counter Operation" */ sh_mtu2_write(p, TCR, 0x23); /* TGRA clear, divide clock by 64 */ sh_mtu2_write(p, TIOR, 0); - sh_mtu2_write(p, TGR, p->periodic); + sh_mtu2_write(p, TGR, periodic); sh_mtu2_write(p, TCNT, 0); sh_mtu2_write(p, TMDR, 0); sh_mtu2_write(p, TIER, 0x01); -- cgit From 42752cc619c0ee619b56f86932ce42b00adb5052 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 4 Mar 2014 12:58:30 +0100 Subject: clocksource: sh_mtu2: Split channel fields from sh_mtu2_priv Create a new sh_mtu2_channel structure to hold the channel-specific fields in preparation for multiple channels per device support. Signed-off-by: Laurent Pinchart Tested-by: Wolfram Sang --- drivers/clocksource/sh_mtu2.c | 125 +++++++++++++++++++++++------------------- 1 file changed, 69 insertions(+), 56 deletions(-) diff --git a/drivers/clocksource/sh_mtu2.c b/drivers/clocksource/sh_mtu2.c index 66684552fcc9..e509f417ef64 100644 --- a/drivers/clocksource/sh_mtu2.c +++ b/drivers/clocksource/sh_mtu2.c @@ -34,12 +34,21 @@ #include #include +struct sh_mtu2_priv; + +struct sh_mtu2_channel { + struct sh_mtu2_priv *mtu; + int irq; + struct clock_event_device ced; +}; + struct sh_mtu2_priv { + struct platform_device *pdev; + void __iomem *mapbase; struct clk *clk; - int irq; - struct platform_device *pdev; - struct clock_event_device ced; + + struct sh_mtu2_channel channel; }; static DEFINE_RAW_SPINLOCK(sh_mtu2_lock); @@ -63,10 +72,10 @@ static unsigned long mtu2_reg_offs[] = { [TGR] = 8, }; -static inline unsigned long sh_mtu2_read(struct sh_mtu2_priv *p, int reg_nr) +static inline unsigned long sh_mtu2_read(struct sh_mtu2_channel *ch, int reg_nr) { - struct sh_timer_config *cfg = p->pdev->dev.platform_data; - void __iomem *base = p->mapbase; + struct sh_timer_config *cfg = ch->mtu->pdev->dev.platform_data; + void __iomem *base = ch->mtu->mapbase; unsigned long offs; if (reg_nr == TSTR) @@ -80,11 +89,11 @@ static inline unsigned long sh_mtu2_read(struct sh_mtu2_priv *p, int reg_nr) return ioread8(base + offs); } -static inline void sh_mtu2_write(struct sh_mtu2_priv *p, int reg_nr, +static inline void sh_mtu2_write(struct sh_mtu2_channel *ch, int reg_nr, unsigned long value) { - struct sh_timer_config *cfg = p->pdev->dev.platform_data; - void __iomem *base = p->mapbase; + struct sh_timer_config *cfg = ch->mtu->pdev->dev.platform_data; + void __iomem *base = ch->mtu->mapbase; unsigned long offs; if (reg_nr == TSTR) { @@ -100,100 +109,100 @@ static inline void sh_mtu2_write(struct sh_mtu2_priv *p, int reg_nr, iowrite8(value, base + offs); } -static void sh_mtu2_start_stop_ch(struct sh_mtu2_priv *p, int start) +static void sh_mtu2_start_stop_ch(struct sh_mtu2_channel *ch, int start) { - struct sh_timer_config *cfg = p->pdev->dev.platform_data; + struct sh_timer_config *cfg = ch->mtu->pdev->dev.platform_data; unsigned long flags, value; /* start stop register shared by multiple timer channels */ raw_spin_lock_irqsave(&sh_mtu2_lock, flags); - value = sh_mtu2_read(p, TSTR); + value = sh_mtu2_read(ch, TSTR); if (start) value |= 1 << cfg->timer_bit; else value &= ~(1 << cfg->timer_bit); - sh_mtu2_write(p, TSTR, value); + sh_mtu2_write(ch, TSTR, value); raw_spin_unlock_irqrestore(&sh_mtu2_lock, flags); } -static int sh_mtu2_enable(struct sh_mtu2_priv *p) +static int sh_mtu2_enable(struct sh_mtu2_channel *ch) { unsigned long periodic; unsigned long rate; int ret; - pm_runtime_get_sync(&p->pdev->dev); - dev_pm_syscore_device(&p->pdev->dev, true); + pm_runtime_get_sync(&ch->mtu->pdev->dev); + dev_pm_syscore_device(&ch->mtu->pdev->dev, true); /* enable clock */ - ret = clk_enable(p->clk); + ret = clk_enable(ch->mtu->clk); if (ret) { - dev_err(&p->pdev->dev, "cannot enable clock\n"); + dev_err(&ch->mtu->pdev->dev, "cannot enable clock\n"); return ret; } /* make sure channel is disabled */ - sh_mtu2_start_stop_ch(p, 0); + sh_mtu2_start_stop_ch(ch, 0); - rate = clk_get_rate(p->clk) / 64; + rate = clk_get_rate(ch->mtu->clk) / 64; periodic = (rate + HZ/2) / HZ; /* "Periodic Counter Operation" */ - sh_mtu2_write(p, TCR, 0x23); /* TGRA clear, divide clock by 64 */ - sh_mtu2_write(p, TIOR, 0); - sh_mtu2_write(p, TGR, periodic); - sh_mtu2_write(p, TCNT, 0); - sh_mtu2_write(p, TMDR, 0); - sh_mtu2_write(p, TIER, 0x01); + sh_mtu2_write(ch, TCR, 0x23); /* TGRA clear, divide clock by 64 */ + sh_mtu2_write(ch, TIOR, 0); + sh_mtu2_write(ch, TGR, periodic); + sh_mtu2_write(ch, TCNT, 0); + sh_mtu2_write(ch, TMDR, 0); + sh_mtu2_write(ch, TIER, 0x01); /* enable channel */ - sh_mtu2_start_stop_ch(p, 1); + sh_mtu2_start_stop_ch(ch, 1); return 0; } -static void sh_mtu2_disable(struct sh_mtu2_priv *p) +static void sh_mtu2_disable(struct sh_mtu2_channel *ch) { /* disable channel */ - sh_mtu2_start_stop_ch(p, 0); + sh_mtu2_start_stop_ch(ch, 0); /* stop clock */ - clk_disable(p->clk); + clk_disable(ch->mtu->clk); - dev_pm_syscore_device(&p->pdev->dev, false); - pm_runtime_put(&p->pdev->dev); + dev_pm_syscore_device(&ch->mtu->pdev->dev, false); + pm_runtime_put(&ch->mtu->pdev->dev); } static irqreturn_t sh_mtu2_interrupt(int irq, void *dev_id) { - struct sh_mtu2_priv *p = dev_id; + struct sh_mtu2_channel *ch = dev_id; /* acknowledge interrupt */ - sh_mtu2_read(p, TSR); - sh_mtu2_write(p, TSR, 0xfe); + sh_mtu2_read(ch, TSR); + sh_mtu2_write(ch, TSR, 0xfe); /* notify clockevent layer */ - p->ced.event_handler(&p->ced); + ch->ced.event_handler(&ch->ced); return IRQ_HANDLED; } -static struct sh_mtu2_priv *ced_to_sh_mtu2(struct clock_event_device *ced) +static struct sh_mtu2_channel *ced_to_sh_mtu2(struct clock_event_device *ced) { - return container_of(ced, struct sh_mtu2_priv, ced); + return container_of(ced, struct sh_mtu2_channel, ced); } static void sh_mtu2_clock_event_mode(enum clock_event_mode mode, struct clock_event_device *ced) { - struct sh_mtu2_priv *p = ced_to_sh_mtu2(ced); + struct sh_mtu2_channel *ch = ced_to_sh_mtu2(ced); int disabled = 0; /* deal with old setting first */ switch (ced->mode) { case CLOCK_EVT_MODE_PERIODIC: - sh_mtu2_disable(p); + sh_mtu2_disable(ch); disabled = 1; break; default: @@ -202,12 +211,13 @@ static void sh_mtu2_clock_event_mode(enum clock_event_mode mode, switch (mode) { case CLOCK_EVT_MODE_PERIODIC: - dev_info(&p->pdev->dev, "used for periodic clock events\n"); - sh_mtu2_enable(p); + dev_info(&ch->mtu->pdev->dev, + "used for periodic clock events\n"); + sh_mtu2_enable(ch); break; case CLOCK_EVT_MODE_UNUSED: if (!disabled) - sh_mtu2_disable(p); + sh_mtu2_disable(ch); break; case CLOCK_EVT_MODE_SHUTDOWN: default: @@ -217,18 +227,18 @@ static void sh_mtu2_clock_event_mode(enum clock_event_mode mode, static void sh_mtu2_clock_event_suspend(struct clock_event_device *ced) { - pm_genpd_syscore_poweroff(&ced_to_sh_mtu2(ced)->pdev->dev); + pm_genpd_syscore_poweroff(&ced_to_sh_mtu2(ced)->mtu->pdev->dev); } static void sh_mtu2_clock_event_resume(struct clock_event_device *ced) { - pm_genpd_syscore_poweron(&ced_to_sh_mtu2(ced)->pdev->dev); + pm_genpd_syscore_poweron(&ced_to_sh_mtu2(ced)->mtu->pdev->dev); } -static void sh_mtu2_register_clockevent(struct sh_mtu2_priv *p, +static void sh_mtu2_register_clockevent(struct sh_mtu2_channel *ch, char *name, unsigned long rating) { - struct clock_event_device *ced = &p->ced; + struct clock_event_device *ced = &ch->ced; int ret; memset(ced, 0, sizeof(*ced)); @@ -241,23 +251,24 @@ static void sh_mtu2_register_clockevent(struct sh_mtu2_priv *p, ced->suspend = sh_mtu2_clock_event_suspend; ced->resume = sh_mtu2_clock_event_resume; - dev_info(&p->pdev->dev, "used for clock events\n"); + dev_info(&ch->mtu->pdev->dev, "used for clock events\n"); clockevents_register_device(ced); - ret = request_irq(p->irq, sh_mtu2_interrupt, + ret = request_irq(ch->irq, sh_mtu2_interrupt, IRQF_TIMER | IRQF_IRQPOLL | IRQF_NOBALANCING, - dev_name(&p->pdev->dev), p); + dev_name(&ch->mtu->pdev->dev), ch); if (ret) { - dev_err(&p->pdev->dev, "failed to request irq %d\n", p->irq); + dev_err(&ch->mtu->pdev->dev, "failed to request irq %d\n", + ch->irq); return; } } -static int sh_mtu2_register(struct sh_mtu2_priv *p, char *name, +static int sh_mtu2_register(struct sh_mtu2_channel *ch, char *name, unsigned long clockevent_rating) { if (clockevent_rating) - sh_mtu2_register_clockevent(p, name, clockevent_rating); + sh_mtu2_register_clockevent(ch, name, clockevent_rating); return 0; } @@ -285,8 +296,8 @@ static int sh_mtu2_setup(struct sh_mtu2_priv *p, struct platform_device *pdev) goto err0; } - p->irq = platform_get_irq(p->pdev, 0); - if (p->irq < 0) { + p->channel.irq = platform_get_irq(p->pdev, 0); + if (p->channel.irq < 0) { dev_err(&p->pdev->dev, "failed to get irq\n"); goto err0; } @@ -310,7 +321,9 @@ static int sh_mtu2_setup(struct sh_mtu2_priv *p, struct platform_device *pdev) if (ret < 0) goto err2; - ret = sh_mtu2_register(p, (char *)dev_name(&p->pdev->dev), + p->channel.mtu = p; + + ret = sh_mtu2_register(&p->channel, (char *)dev_name(&p->pdev->dev), cfg->clockevent_rating); if (ret < 0) goto err3; -- cgit From 7dad72de1b475d02935e5c79c218637b6c63108b Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 4 Mar 2014 13:04:48 +0100 Subject: clocksource: sh_mtu2: Rename struct sh_mtu2_priv to sh_mtu2_device Channel data is private as well, rename priv to device to make the distrinction between the core device and the channels clearer. Signed-off-by: Laurent Pinchart Tested-by: Wolfram Sang --- drivers/clocksource/sh_mtu2.c | 65 ++++++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 32 deletions(-) diff --git a/drivers/clocksource/sh_mtu2.c b/drivers/clocksource/sh_mtu2.c index e509f417ef64..256621c156e6 100644 --- a/drivers/clocksource/sh_mtu2.c +++ b/drivers/clocksource/sh_mtu2.c @@ -34,15 +34,15 @@ #include #include -struct sh_mtu2_priv; +struct sh_mtu2_device; struct sh_mtu2_channel { - struct sh_mtu2_priv *mtu; + struct sh_mtu2_device *mtu; int irq; struct clock_event_device ced; }; -struct sh_mtu2_priv { +struct sh_mtu2_device { struct platform_device *pdev; void __iomem *mapbase; @@ -273,75 +273,76 @@ static int sh_mtu2_register(struct sh_mtu2_channel *ch, char *name, return 0; } -static int sh_mtu2_setup(struct sh_mtu2_priv *p, struct platform_device *pdev) +static int sh_mtu2_setup(struct sh_mtu2_device *mtu, + struct platform_device *pdev) { struct sh_timer_config *cfg = pdev->dev.platform_data; struct resource *res; int ret; ret = -ENXIO; - memset(p, 0, sizeof(*p)); - p->pdev = pdev; + memset(mtu, 0, sizeof(*mtu)); + mtu->pdev = pdev; if (!cfg) { - dev_err(&p->pdev->dev, "missing platform data\n"); + dev_err(&mtu->pdev->dev, "missing platform data\n"); goto err0; } - platform_set_drvdata(pdev, p); + platform_set_drvdata(pdev, mtu); - res = platform_get_resource(p->pdev, IORESOURCE_MEM, 0); + res = platform_get_resource(mtu->pdev, IORESOURCE_MEM, 0); if (!res) { - dev_err(&p->pdev->dev, "failed to get I/O memory\n"); + dev_err(&mtu->pdev->dev, "failed to get I/O memory\n"); goto err0; } - p->channel.irq = platform_get_irq(p->pdev, 0); - if (p->channel.irq < 0) { - dev_err(&p->pdev->dev, "failed to get irq\n"); + mtu->channel.irq = platform_get_irq(mtu->pdev, 0); + if (mtu->channel.irq < 0) { + dev_err(&mtu->pdev->dev, "failed to get irq\n"); goto err0; } /* map memory, let mapbase point to our channel */ - p->mapbase = ioremap_nocache(res->start, resource_size(res)); - if (p->mapbase == NULL) { - dev_err(&p->pdev->dev, "failed to remap I/O memory\n"); + mtu->mapbase = ioremap_nocache(res->start, resource_size(res)); + if (mtu->mapbase == NULL) { + dev_err(&mtu->pdev->dev, "failed to remap I/O memory\n"); goto err0; } /* get hold of clock */ - p->clk = clk_get(&p->pdev->dev, "mtu2_fck"); - if (IS_ERR(p->clk)) { - dev_err(&p->pdev->dev, "cannot get clock\n"); - ret = PTR_ERR(p->clk); + mtu->clk = clk_get(&mtu->pdev->dev, "mtu2_fck"); + if (IS_ERR(mtu->clk)) { + dev_err(&mtu->pdev->dev, "cannot get clock\n"); + ret = PTR_ERR(mtu->clk); goto err1; } - ret = clk_prepare(p->clk); + ret = clk_prepare(mtu->clk); if (ret < 0) goto err2; - p->channel.mtu = p; + mtu->channel.mtu = mtu; - ret = sh_mtu2_register(&p->channel, (char *)dev_name(&p->pdev->dev), + ret = sh_mtu2_register(&mtu->channel, (char *)dev_name(&mtu->pdev->dev), cfg->clockevent_rating); if (ret < 0) goto err3; return 0; err3: - clk_unprepare(p->clk); + clk_unprepare(mtu->clk); err2: - clk_put(p->clk); + clk_put(mtu->clk); err1: - iounmap(p->mapbase); + iounmap(mtu->mapbase); err0: return ret; } static int sh_mtu2_probe(struct platform_device *pdev) { - struct sh_mtu2_priv *p = platform_get_drvdata(pdev); + struct sh_mtu2_device *mtu = platform_get_drvdata(pdev); struct sh_timer_config *cfg = pdev->dev.platform_data; int ret; @@ -350,20 +351,20 @@ static int sh_mtu2_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); } - if (p) { + if (mtu) { dev_info(&pdev->dev, "kept as earlytimer\n"); goto out; } - p = kmalloc(sizeof(*p), GFP_KERNEL); - if (p == NULL) { + mtu = kmalloc(sizeof(*mtu), GFP_KERNEL); + if (mtu == NULL) { dev_err(&pdev->dev, "failed to allocate driver data\n"); return -ENOMEM; } - ret = sh_mtu2_setup(p, pdev); + ret = sh_mtu2_setup(mtu, pdev); if (ret) { - kfree(p); + kfree(mtu); pm_runtime_idle(&pdev->dev); return ret; } -- cgit From 2e1a53265d550002fdd1658778854d56ae4cadc1 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 4 Mar 2014 13:11:23 +0100 Subject: clocksource: sh_mtu2: Split channel setup to separate function Move the channel setup code from sh_mtu2_setup to a new sh_mtu2_setup_channel function and call it from sh_mtu2_setup. Signed-off-by: Laurent Pinchart Tested-by: Wolfram Sang --- drivers/clocksource/sh_mtu2.c | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/drivers/clocksource/sh_mtu2.c b/drivers/clocksource/sh_mtu2.c index 256621c156e6..8fd705909f98 100644 --- a/drivers/clocksource/sh_mtu2.c +++ b/drivers/clocksource/sh_mtu2.c @@ -273,6 +273,24 @@ static int sh_mtu2_register(struct sh_mtu2_channel *ch, char *name, return 0; } +static int sh_mtu2_setup_channel(struct sh_mtu2_channel *ch, + struct sh_mtu2_device *mtu) +{ + struct sh_timer_config *cfg = mtu->pdev->dev.platform_data; + + memset(ch, 0, sizeof(*ch)); + ch->mtu = mtu; + + ch->irq = platform_get_irq(mtu->pdev, 0); + if (ch->irq < 0) { + dev_err(&mtu->pdev->dev, "failed to get irq\n"); + return ch->irq; + } + + return sh_mtu2_register(ch, (char *)dev_name(&mtu->pdev->dev), + cfg->clockevent_rating); +} + static int sh_mtu2_setup(struct sh_mtu2_device *mtu, struct platform_device *pdev) { @@ -297,12 +315,6 @@ static int sh_mtu2_setup(struct sh_mtu2_device *mtu, goto err0; } - mtu->channel.irq = platform_get_irq(mtu->pdev, 0); - if (mtu->channel.irq < 0) { - dev_err(&mtu->pdev->dev, "failed to get irq\n"); - goto err0; - } - /* map memory, let mapbase point to our channel */ mtu->mapbase = ioremap_nocache(res->start, resource_size(res)); if (mtu->mapbase == NULL) { @@ -322,10 +334,7 @@ static int sh_mtu2_setup(struct sh_mtu2_device *mtu, if (ret < 0) goto err2; - mtu->channel.mtu = mtu; - - ret = sh_mtu2_register(&mtu->channel, (char *)dev_name(&mtu->pdev->dev), - cfg->clockevent_rating); + ret = sh_mtu2_setup_channel(&mtu->channel, mtu); if (ret < 0) goto err3; -- cgit From aa83804af705731d2802b80fb4b94a79045d31a3 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 4 Mar 2014 13:57:14 +0100 Subject: clocksource: sh_mtu2: Constify name argument to sh_mtu2_register() The name argument is assigned to const structure fields only, constify it. Signed-off-by: Laurent Pinchart Tested-by: Wolfram Sang --- drivers/clocksource/sh_mtu2.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/clocksource/sh_mtu2.c b/drivers/clocksource/sh_mtu2.c index 8fd705909f98..2fe3ab4c3231 100644 --- a/drivers/clocksource/sh_mtu2.c +++ b/drivers/clocksource/sh_mtu2.c @@ -236,7 +236,7 @@ static void sh_mtu2_clock_event_resume(struct clock_event_device *ced) } static void sh_mtu2_register_clockevent(struct sh_mtu2_channel *ch, - char *name, unsigned long rating) + const char *name, unsigned long rating) { struct clock_event_device *ced = &ch->ced; int ret; @@ -264,7 +264,7 @@ static void sh_mtu2_register_clockevent(struct sh_mtu2_channel *ch, } } -static int sh_mtu2_register(struct sh_mtu2_channel *ch, char *name, +static int sh_mtu2_register(struct sh_mtu2_channel *ch, const char *name, unsigned long clockevent_rating) { if (clockevent_rating) @@ -287,7 +287,7 @@ static int sh_mtu2_setup_channel(struct sh_mtu2_channel *ch, return ch->irq; } - return sh_mtu2_register(ch, (char *)dev_name(&mtu->pdev->dev), + return sh_mtu2_register(ch, dev_name(&mtu->pdev->dev), cfg->clockevent_rating); } -- cgit From da90a1c67751a412499a9f5698c3bf0bf80f65a6 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 4 Mar 2014 14:04:24 +0100 Subject: clocksource: sh_mtu2: Add memory base to sh_mtu2_channel structure The channel memory base is channel-specific, add it to the channel structure in preparation for support of multiple channels per device. Signed-off-by: Laurent Pinchart Tested-by: Wolfram Sang --- drivers/clocksource/sh_mtu2.c | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/drivers/clocksource/sh_mtu2.c b/drivers/clocksource/sh_mtu2.c index 2fe3ab4c3231..97714ce5e851 100644 --- a/drivers/clocksource/sh_mtu2.c +++ b/drivers/clocksource/sh_mtu2.c @@ -38,7 +38,10 @@ struct sh_mtu2_device; struct sh_mtu2_channel { struct sh_mtu2_device *mtu; + + void __iomem *base; int irq; + struct clock_event_device ced; }; @@ -74,39 +77,35 @@ static unsigned long mtu2_reg_offs[] = { static inline unsigned long sh_mtu2_read(struct sh_mtu2_channel *ch, int reg_nr) { - struct sh_timer_config *cfg = ch->mtu->pdev->dev.platform_data; - void __iomem *base = ch->mtu->mapbase; unsigned long offs; if (reg_nr == TSTR) - return ioread8(base + cfg->channel_offset); + return ioread8(ch->mtu->mapbase); offs = mtu2_reg_offs[reg_nr]; if ((reg_nr == TCNT) || (reg_nr == TGR)) - return ioread16(base + offs); + return ioread16(ch->base + offs); else - return ioread8(base + offs); + return ioread8(ch->base + offs); } static inline void sh_mtu2_write(struct sh_mtu2_channel *ch, int reg_nr, unsigned long value) { - struct sh_timer_config *cfg = ch->mtu->pdev->dev.platform_data; - void __iomem *base = ch->mtu->mapbase; unsigned long offs; if (reg_nr == TSTR) { - iowrite8(value, base + cfg->channel_offset); + iowrite8(value, ch->mtu->mapbase); return; } offs = mtu2_reg_offs[reg_nr]; if ((reg_nr == TCNT) || (reg_nr == TGR)) - iowrite16(value, base + offs); + iowrite16(value, ch->base + offs); else - iowrite8(value, base + offs); + iowrite8(value, ch->base + offs); } static void sh_mtu2_start_stop_ch(struct sh_mtu2_channel *ch, int start) @@ -315,13 +314,18 @@ static int sh_mtu2_setup(struct sh_mtu2_device *mtu, goto err0; } - /* map memory, let mapbase point to our channel */ - mtu->mapbase = ioremap_nocache(res->start, resource_size(res)); - if (mtu->mapbase == NULL) { + /* + * Map memory, let channel.base point to our channel and mapbase to the + * start/stop shared register. + */ + mtu->channel.base = ioremap_nocache(res->start, resource_size(res)); + if (mtu->channel.base == NULL) { dev_err(&mtu->pdev->dev, "failed to remap I/O memory\n"); goto err0; } + mtu->mapbase = mtu->channel.base + cfg->channel_offset; + /* get hold of clock */ mtu->clk = clk_get(&mtu->pdev->dev, "mtu2_fck"); if (IS_ERR(mtu->clk)) { @@ -344,7 +348,7 @@ static int sh_mtu2_setup(struct sh_mtu2_device *mtu, err2: clk_put(mtu->clk); err1: - iounmap(mtu->mapbase); + iounmap(mtu->channel.base); err0: return ret; } -- cgit From d2b93177065fd8e1e18f4f42880326e0881ff457 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 4 Mar 2014 14:17:26 +0100 Subject: clocksource: sh_mtu2: Add index to struct sh_mtu2_channel Use the index as the timer start/stop bit and when printing messages to identify the channel. Signed-off-by: Laurent Pinchart Tested-by: Wolfram Sang --- drivers/clocksource/sh_mtu2.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/drivers/clocksource/sh_mtu2.c b/drivers/clocksource/sh_mtu2.c index 97714ce5e851..61827c66f7d3 100644 --- a/drivers/clocksource/sh_mtu2.c +++ b/drivers/clocksource/sh_mtu2.c @@ -38,6 +38,7 @@ struct sh_mtu2_device; struct sh_mtu2_channel { struct sh_mtu2_device *mtu; + unsigned int index; void __iomem *base; int irq; @@ -110,7 +111,6 @@ static inline void sh_mtu2_write(struct sh_mtu2_channel *ch, int reg_nr, static void sh_mtu2_start_stop_ch(struct sh_mtu2_channel *ch, int start) { - struct sh_timer_config *cfg = ch->mtu->pdev->dev.platform_data; unsigned long flags, value; /* start stop register shared by multiple timer channels */ @@ -118,9 +118,9 @@ static void sh_mtu2_start_stop_ch(struct sh_mtu2_channel *ch, int start) value = sh_mtu2_read(ch, TSTR); if (start) - value |= 1 << cfg->timer_bit; + value |= 1 << ch->index; else - value &= ~(1 << cfg->timer_bit); + value &= ~(1 << ch->index); sh_mtu2_write(ch, TSTR, value); raw_spin_unlock_irqrestore(&sh_mtu2_lock, flags); @@ -138,7 +138,8 @@ static int sh_mtu2_enable(struct sh_mtu2_channel *ch) /* enable clock */ ret = clk_enable(ch->mtu->clk); if (ret) { - dev_err(&ch->mtu->pdev->dev, "cannot enable clock\n"); + dev_err(&ch->mtu->pdev->dev, "ch%u: cannot enable clock\n", + ch->index); return ret; } @@ -211,7 +212,7 @@ static void sh_mtu2_clock_event_mode(enum clock_event_mode mode, switch (mode) { case CLOCK_EVT_MODE_PERIODIC: dev_info(&ch->mtu->pdev->dev, - "used for periodic clock events\n"); + "ch%u: used for periodic clock events\n", ch->index); sh_mtu2_enable(ch); break; case CLOCK_EVT_MODE_UNUSED: @@ -250,15 +251,16 @@ static void sh_mtu2_register_clockevent(struct sh_mtu2_channel *ch, ced->suspend = sh_mtu2_clock_event_suspend; ced->resume = sh_mtu2_clock_event_resume; - dev_info(&ch->mtu->pdev->dev, "used for clock events\n"); + dev_info(&ch->mtu->pdev->dev, "ch%u: used for clock events\n", + ch->index); clockevents_register_device(ced); ret = request_irq(ch->irq, sh_mtu2_interrupt, IRQF_TIMER | IRQF_IRQPOLL | IRQF_NOBALANCING, dev_name(&ch->mtu->pdev->dev), ch); if (ret) { - dev_err(&ch->mtu->pdev->dev, "failed to request irq %d\n", - ch->irq); + dev_err(&ch->mtu->pdev->dev, "ch%u: failed to request irq %d\n", + ch->index, ch->irq); return; } } @@ -279,10 +281,12 @@ static int sh_mtu2_setup_channel(struct sh_mtu2_channel *ch, memset(ch, 0, sizeof(*ch)); ch->mtu = mtu; + ch->index = cfg->timer_bit; ch->irq = platform_get_irq(mtu->pdev, 0); if (ch->irq < 0) { - dev_err(&mtu->pdev->dev, "failed to get irq\n"); + dev_err(&mtu->pdev->dev, "ch%u: failed to get irq\n", + ch->index); return ch->irq; } -- cgit From 810c651369b343618d949826e0acd0df1b8b06eb Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 4 Mar 2014 14:10:55 +0100 Subject: clocksource: sh_mtu2: Replace kmalloc + memset with kzalloc One kzalloc a day keeps the bugs away. Signed-off-by: Laurent Pinchart Tested-by: Wolfram Sang --- drivers/clocksource/sh_mtu2.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/clocksource/sh_mtu2.c b/drivers/clocksource/sh_mtu2.c index 61827c66f7d3..94a53428a556 100644 --- a/drivers/clocksource/sh_mtu2.c +++ b/drivers/clocksource/sh_mtu2.c @@ -241,8 +241,6 @@ static void sh_mtu2_register_clockevent(struct sh_mtu2_channel *ch, struct clock_event_device *ced = &ch->ced; int ret; - memset(ced, 0, sizeof(*ced)); - ced->name = name; ced->features = CLOCK_EVT_FEAT_PERIODIC; ced->rating = rating; @@ -279,7 +277,6 @@ static int sh_mtu2_setup_channel(struct sh_mtu2_channel *ch, { struct sh_timer_config *cfg = mtu->pdev->dev.platform_data; - memset(ch, 0, sizeof(*ch)); ch->mtu = mtu; ch->index = cfg->timer_bit; @@ -302,7 +299,6 @@ static int sh_mtu2_setup(struct sh_mtu2_device *mtu, int ret; ret = -ENXIO; - memset(mtu, 0, sizeof(*mtu)); mtu->pdev = pdev; if (!cfg) { @@ -373,7 +369,7 @@ static int sh_mtu2_probe(struct platform_device *pdev) goto out; } - mtu = kmalloc(sizeof(*mtu), GFP_KERNEL); + mtu = kzalloc(sizeof(*mtu), GFP_KERNEL); if (mtu == NULL) { dev_err(&pdev->dev, "failed to allocate driver data\n"); return -ENOMEM; -- cgit From c54ccb431ce6ce813bb850e8659991fc4c5bc6bc Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 4 Mar 2014 14:23:00 +0100 Subject: clocksource: sh_mtu2: Allocate channels dynamically This prepares the driver for multi-channel support. Signed-off-by: Laurent Pinchart Tested-by: Wolfram Sang --- drivers/clocksource/sh_mtu2.c | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/drivers/clocksource/sh_mtu2.c b/drivers/clocksource/sh_mtu2.c index 94a53428a556..45e1e85fcbeb 100644 --- a/drivers/clocksource/sh_mtu2.c +++ b/drivers/clocksource/sh_mtu2.c @@ -52,7 +52,8 @@ struct sh_mtu2_device { void __iomem *mapbase; struct clk *clk; - struct sh_mtu2_channel channel; + struct sh_mtu2_channel *channels; + unsigned int num_channels; }; static DEFINE_RAW_SPINLOCK(sh_mtu2_lock); @@ -296,6 +297,7 @@ static int sh_mtu2_setup(struct sh_mtu2_device *mtu, { struct sh_timer_config *cfg = pdev->dev.platform_data; struct resource *res; + void __iomem *base; int ret; ret = -ENXIO; @@ -315,16 +317,16 @@ static int sh_mtu2_setup(struct sh_mtu2_device *mtu, } /* - * Map memory, let channel.base point to our channel and mapbase to the + * Map memory, let base point to our channel and mapbase to the * start/stop shared register. */ - mtu->channel.base = ioremap_nocache(res->start, resource_size(res)); - if (mtu->channel.base == NULL) { + base = ioremap_nocache(res->start, resource_size(res)); + if (base == NULL) { dev_err(&mtu->pdev->dev, "failed to remap I/O memory\n"); goto err0; } - mtu->mapbase = mtu->channel.base + cfg->channel_offset; + mtu->mapbase = base + cfg->channel_offset; /* get hold of clock */ mtu->clk = clk_get(&mtu->pdev->dev, "mtu2_fck"); @@ -338,17 +340,28 @@ static int sh_mtu2_setup(struct sh_mtu2_device *mtu, if (ret < 0) goto err2; - ret = sh_mtu2_setup_channel(&mtu->channel, mtu); + mtu->channels = kzalloc(sizeof(*mtu->channels), GFP_KERNEL); + if (mtu->channels == NULL) { + ret = -ENOMEM; + goto err3; + } + + mtu->num_channels = 1; + + mtu->channels[0].base = base; + + ret = sh_mtu2_setup_channel(&mtu->channels[0], mtu); if (ret < 0) goto err3; return 0; err3: + kfree(mtu->channels); clk_unprepare(mtu->clk); err2: clk_put(mtu->clk); err1: - iounmap(mtu->channel.base); + iounmap(base); err0: return ret; } -- cgit From f992c2410bd31b7c80ba8cc8b989d91b9cac3c30 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 4 Mar 2014 15:16:25 +0100 Subject: clocksource: sh_mtu2: Replace hardcoded register values with macros Define symbolic macros for all used registers bits. Signed-off-by: Laurent Pinchart Tested-by: Wolfram Sang --- drivers/clocksource/sh_mtu2.c | 98 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 92 insertions(+), 6 deletions(-) diff --git a/drivers/clocksource/sh_mtu2.c b/drivers/clocksource/sh_mtu2.c index 45e1e85fcbeb..2cf004880746 100644 --- a/drivers/clocksource/sh_mtu2.c +++ b/drivers/clocksource/sh_mtu2.c @@ -67,6 +67,88 @@ static DEFINE_RAW_SPINLOCK(sh_mtu2_lock); #define TCNT 5 /* channel register */ #define TGR 6 /* channel register */ +#define TCR_CCLR_NONE (0 << 5) +#define TCR_CCLR_TGRA (1 << 5) +#define TCR_CCLR_TGRB (2 << 5) +#define TCR_CCLR_SYNC (3 << 5) +#define TCR_CCLR_TGRC (5 << 5) +#define TCR_CCLR_TGRD (6 << 5) +#define TCR_CCLR_MASK (7 << 5) +#define TCR_CKEG_RISING (0 << 3) +#define TCR_CKEG_FALLING (1 << 3) +#define TCR_CKEG_BOTH (2 << 3) +#define TCR_CKEG_MASK (3 << 3) +/* Values 4 to 7 are channel-dependent */ +#define TCR_TPSC_P1 (0 << 0) +#define TCR_TPSC_P4 (1 << 0) +#define TCR_TPSC_P16 (2 << 0) +#define TCR_TPSC_P64 (3 << 0) +#define TCR_TPSC_CH0_TCLKA (4 << 0) +#define TCR_TPSC_CH0_TCLKB (5 << 0) +#define TCR_TPSC_CH0_TCLKC (6 << 0) +#define TCR_TPSC_CH0_TCLKD (7 << 0) +#define TCR_TPSC_CH1_TCLKA (4 << 0) +#define TCR_TPSC_CH1_TCLKB (5 << 0) +#define TCR_TPSC_CH1_P256 (6 << 0) +#define TCR_TPSC_CH1_TCNT2 (7 << 0) +#define TCR_TPSC_CH2_TCLKA (4 << 0) +#define TCR_TPSC_CH2_TCLKB (5 << 0) +#define TCR_TPSC_CH2_TCLKC (6 << 0) +#define TCR_TPSC_CH2_P1024 (7 << 0) +#define TCR_TPSC_CH34_P256 (4 << 0) +#define TCR_TPSC_CH34_P1024 (5 << 0) +#define TCR_TPSC_CH34_TCLKA (6 << 0) +#define TCR_TPSC_CH34_TCLKB (7 << 0) +#define TCR_TPSC_MASK (7 << 0) + +#define TMDR_BFE (1 << 6) +#define TMDR_BFB (1 << 5) +#define TMDR_BFA (1 << 4) +#define TMDR_MD_NORMAL (0 << 0) +#define TMDR_MD_PWM_1 (2 << 0) +#define TMDR_MD_PWM_2 (3 << 0) +#define TMDR_MD_PHASE_1 (4 << 0) +#define TMDR_MD_PHASE_2 (5 << 0) +#define TMDR_MD_PHASE_3 (6 << 0) +#define TMDR_MD_PHASE_4 (7 << 0) +#define TMDR_MD_PWM_SYNC (8 << 0) +#define TMDR_MD_PWM_COMP_CREST (13 << 0) +#define TMDR_MD_PWM_COMP_TROUGH (14 << 0) +#define TMDR_MD_PWM_COMP_BOTH (15 << 0) +#define TMDR_MD_MASK (15 << 0) + +#define TIOC_IOCH(n) ((n) << 4) +#define TIOC_IOCL(n) ((n) << 0) +#define TIOR_OC_RETAIN (0 << 0) +#define TIOR_OC_0_CLEAR (1 << 0) +#define TIOR_OC_0_SET (2 << 0) +#define TIOR_OC_0_TOGGLE (3 << 0) +#define TIOR_OC_1_CLEAR (5 << 0) +#define TIOR_OC_1_SET (6 << 0) +#define TIOR_OC_1_TOGGLE (7 << 0) +#define TIOR_IC_RISING (8 << 0) +#define TIOR_IC_FALLING (9 << 0) +#define TIOR_IC_BOTH (10 << 0) +#define TIOR_IC_TCNT (12 << 0) +#define TIOR_MASK (15 << 0) + +#define TIER_TTGE (1 << 7) +#define TIER_TTGE2 (1 << 6) +#define TIER_TCIEU (1 << 5) +#define TIER_TCIEV (1 << 4) +#define TIER_TGIED (1 << 3) +#define TIER_TGIEC (1 << 2) +#define TIER_TGIEB (1 << 1) +#define TIER_TGIEA (1 << 0) + +#define TSR_TCFD (1 << 7) +#define TSR_TCFU (1 << 5) +#define TSR_TCFV (1 << 4) +#define TSR_TGFD (1 << 3) +#define TSR_TGFC (1 << 2) +#define TSR_TGFB (1 << 1) +#define TSR_TGFA (1 << 0) + static unsigned long mtu2_reg_offs[] = { [TCR] = 0, [TMDR] = 1, @@ -150,13 +232,17 @@ static int sh_mtu2_enable(struct sh_mtu2_channel *ch) rate = clk_get_rate(ch->mtu->clk) / 64; periodic = (rate + HZ/2) / HZ; - /* "Periodic Counter Operation" */ - sh_mtu2_write(ch, TCR, 0x23); /* TGRA clear, divide clock by 64 */ - sh_mtu2_write(ch, TIOR, 0); + /* + * "Periodic Counter Operation" + * Clear on TGRA compare match, divide clock by 64. + */ + sh_mtu2_write(ch, TCR, TCR_CCLR_TGRA | TCR_TPSC_P64); + sh_mtu2_write(ch, TIOR, TIOC_IOCH(TIOR_OC_0_CLEAR) | + TIOC_IOCL(TIOR_OC_0_CLEAR)); sh_mtu2_write(ch, TGR, periodic); sh_mtu2_write(ch, TCNT, 0); - sh_mtu2_write(ch, TMDR, 0); - sh_mtu2_write(ch, TIER, 0x01); + sh_mtu2_write(ch, TMDR, TMDR_MD_NORMAL); + sh_mtu2_write(ch, TIER, TIER_TGIEA); /* enable channel */ sh_mtu2_start_stop_ch(ch, 1); @@ -182,7 +268,7 @@ static irqreturn_t sh_mtu2_interrupt(int irq, void *dev_id) /* acknowledge interrupt */ sh_mtu2_read(ch, TSR); - sh_mtu2_write(ch, TSR, 0xfe); + sh_mtu2_write(ch, TSR, ~TSR_TGFA); /* notify clockevent layer */ ch->ced.event_handler(&ch->ced); -- cgit From 3cc950479891040366629247357512f1cc928da3 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 4 Mar 2014 15:22:19 +0100 Subject: clocksource: sh_mtu2: Set cpumask to cpu_possible_mask The MTU2 is not tied to CPU0, make it usable on any CPU. Signed-off-by: Laurent Pinchart Tested-by: Wolfram Sang --- drivers/clocksource/sh_mtu2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clocksource/sh_mtu2.c b/drivers/clocksource/sh_mtu2.c index 2cf004880746..702ce6044793 100644 --- a/drivers/clocksource/sh_mtu2.c +++ b/drivers/clocksource/sh_mtu2.c @@ -331,7 +331,7 @@ static void sh_mtu2_register_clockevent(struct sh_mtu2_channel *ch, ced->name = name; ced->features = CLOCK_EVT_FEAT_PERIODIC; ced->rating = rating; - ced->cpumask = cpumask_of(0); + ced->cpumask = cpu_possible_mask; ced->set_mode = sh_mtu2_clock_event_mode; ced->suspend = sh_mtu2_clock_event_suspend; ced->resume = sh_mtu2_clock_event_resume; -- cgit From 207e21a9732a27f58843ccae1c9644f3a1636b66 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 4 Mar 2014 15:19:41 +0100 Subject: clocksource: sh_mtu2: Hardcode MTU2 clock event rating to 200 All boards use clock event ratings of 200 for the MTU2, hardcode it in the driver. Signed-off-by: Laurent Pinchart Tested-by: Wolfram Sang --- drivers/clocksource/sh_mtu2.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/clocksource/sh_mtu2.c b/drivers/clocksource/sh_mtu2.c index 702ce6044793..14cc7b6f703b 100644 --- a/drivers/clocksource/sh_mtu2.c +++ b/drivers/clocksource/sh_mtu2.c @@ -323,14 +323,14 @@ static void sh_mtu2_clock_event_resume(struct clock_event_device *ced) } static void sh_mtu2_register_clockevent(struct sh_mtu2_channel *ch, - const char *name, unsigned long rating) + const char *name) { struct clock_event_device *ced = &ch->ced; int ret; ced->name = name; ced->features = CLOCK_EVT_FEAT_PERIODIC; - ced->rating = rating; + ced->rating = 200; ced->cpumask = cpu_possible_mask; ced->set_mode = sh_mtu2_clock_event_mode; ced->suspend = sh_mtu2_clock_event_suspend; @@ -351,10 +351,10 @@ static void sh_mtu2_register_clockevent(struct sh_mtu2_channel *ch, } static int sh_mtu2_register(struct sh_mtu2_channel *ch, const char *name, - unsigned long clockevent_rating) + bool clockevent) { - if (clockevent_rating) - sh_mtu2_register_clockevent(ch, name, clockevent_rating); + if (clockevent) + sh_mtu2_register_clockevent(ch, name); return 0; } @@ -375,7 +375,7 @@ static int sh_mtu2_setup_channel(struct sh_mtu2_channel *ch, } return sh_mtu2_register(ch, dev_name(&mtu->pdev->dev), - cfg->clockevent_rating); + cfg->clockevent_rating != 0); } static int sh_mtu2_setup(struct sh_mtu2_device *mtu, -- cgit From faf3f4f8c805f5f8a786ba544c94bf3e01838388 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 4 Mar 2014 18:05:45 +0100 Subject: clocksource: sh_mtu2: Add support for multiple channels per device MTU2 hardware devices can support multiple channels, with global registers and per-channel registers. The sh_mtu2 driver currently models the hardware with one Linux device per channel. This model makes it difficult to handle global registers in a clean way. Add support for a new model that uses one Linux device per timer with multiple channels per device. This requires changes to platform data, add new channel configuration fields. Support for the legacy model is kept and will be removed after all platforms switch to the new model. Signed-off-by: Laurent Pinchart Tested-by: Wolfram Sang --- drivers/clocksource/sh_mtu2.c | 184 +++++++++++++++++++++++++++++------------- 1 file changed, 130 insertions(+), 54 deletions(-) diff --git a/drivers/clocksource/sh_mtu2.c b/drivers/clocksource/sh_mtu2.c index 14cc7b6f703b..7cc6d9429f81 100644 --- a/drivers/clocksource/sh_mtu2.c +++ b/drivers/clocksource/sh_mtu2.c @@ -54,6 +54,9 @@ struct sh_mtu2_device { struct sh_mtu2_channel *channels; unsigned int num_channels; + + bool legacy; + bool has_clockevent; }; static DEFINE_RAW_SPINLOCK(sh_mtu2_lock); @@ -163,8 +166,12 @@ static inline unsigned long sh_mtu2_read(struct sh_mtu2_channel *ch, int reg_nr) { unsigned long offs; - if (reg_nr == TSTR) - return ioread8(ch->mtu->mapbase); + if (reg_nr == TSTR) { + if (ch->mtu->legacy) + return ioread8(ch->mtu->mapbase); + else + return ioread8(ch->mtu->mapbase + 0x280); + } offs = mtu2_reg_offs[reg_nr]; @@ -180,8 +187,10 @@ static inline void sh_mtu2_write(struct sh_mtu2_channel *ch, int reg_nr, unsigned long offs; if (reg_nr == TSTR) { - iowrite8(value, ch->mtu->mapbase); - return; + if (ch->mtu->legacy) + return iowrite8(value, ch->mtu->mapbase); + else + return iowrite8(value, ch->mtu->mapbase + 0x280); } offs = mtu2_reg_offs[reg_nr]; @@ -353,109 +362,168 @@ static void sh_mtu2_register_clockevent(struct sh_mtu2_channel *ch, static int sh_mtu2_register(struct sh_mtu2_channel *ch, const char *name, bool clockevent) { - if (clockevent) + if (clockevent) { + ch->mtu->has_clockevent = true; sh_mtu2_register_clockevent(ch, name); + } return 0; } -static int sh_mtu2_setup_channel(struct sh_mtu2_channel *ch, +static int sh_mtu2_setup_channel(struct sh_mtu2_channel *ch, unsigned int index, struct sh_mtu2_device *mtu) { - struct sh_timer_config *cfg = mtu->pdev->dev.platform_data; + static const unsigned int channel_offsets[] = { + 0x300, 0x380, 0x000, + }; + bool clockevent; ch->mtu = mtu; - ch->index = cfg->timer_bit; - ch->irq = platform_get_irq(mtu->pdev, 0); + if (mtu->legacy) { + struct sh_timer_config *cfg = mtu->pdev->dev.platform_data; + + clockevent = cfg->clockevent_rating != 0; + + ch->irq = platform_get_irq(mtu->pdev, 0); + ch->base = mtu->mapbase - cfg->channel_offset; + ch->index = cfg->timer_bit; + } else { + char name[6]; + + clockevent = true; + + sprintf(name, "tgi%ua", index); + ch->irq = platform_get_irq_byname(mtu->pdev, name); + ch->base = mtu->mapbase + channel_offsets[index]; + ch->index = index; + } + if (ch->irq < 0) { + /* Skip channels with no declared interrupt. */ + if (!mtu->legacy) + return 0; + dev_err(&mtu->pdev->dev, "ch%u: failed to get irq\n", ch->index); return ch->irq; } - return sh_mtu2_register(ch, dev_name(&mtu->pdev->dev), - cfg->clockevent_rating != 0); + return sh_mtu2_register(ch, dev_name(&mtu->pdev->dev), clockevent); } -static int sh_mtu2_setup(struct sh_mtu2_device *mtu, - struct platform_device *pdev) +static int sh_mtu2_map_memory(struct sh_mtu2_device *mtu) { - struct sh_timer_config *cfg = pdev->dev.platform_data; struct resource *res; - void __iomem *base; - int ret; - ret = -ENXIO; - - mtu->pdev = pdev; - - if (!cfg) { - dev_err(&mtu->pdev->dev, "missing platform data\n"); - goto err0; - } - - platform_set_drvdata(pdev, mtu); res = platform_get_resource(mtu->pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(&mtu->pdev->dev, "failed to get I/O memory\n"); - goto err0; + return -ENXIO; } + mtu->mapbase = ioremap_nocache(res->start, resource_size(res)); + if (mtu->mapbase == NULL) + return -ENXIO; + /* - * Map memory, let base point to our channel and mapbase to the - * start/stop shared register. + * In legacy platform device configuration (with one device per channel) + * the resource points to the channel base address. */ - base = ioremap_nocache(res->start, resource_size(res)); - if (base == NULL) { - dev_err(&mtu->pdev->dev, "failed to remap I/O memory\n"); - goto err0; + if (mtu->legacy) { + struct sh_timer_config *cfg = mtu->pdev->dev.platform_data; + mtu->mapbase += cfg->channel_offset; + } + + return 0; +} + +static void sh_mtu2_unmap_memory(struct sh_mtu2_device *mtu) +{ + if (mtu->legacy) { + struct sh_timer_config *cfg = mtu->pdev->dev.platform_data; + mtu->mapbase -= cfg->channel_offset; } - mtu->mapbase = base + cfg->channel_offset; + iounmap(mtu->mapbase); +} + +static int sh_mtu2_setup(struct sh_mtu2_device *mtu, + struct platform_device *pdev) +{ + struct sh_timer_config *cfg = pdev->dev.platform_data; + const struct platform_device_id *id = pdev->id_entry; + unsigned int i; + int ret; + + mtu->pdev = pdev; + mtu->legacy = id->driver_data; + + if (mtu->legacy && !cfg) { + dev_err(&mtu->pdev->dev, "missing platform data\n"); + return -ENXIO; + } - /* get hold of clock */ + /* Get hold of clock. */ mtu->clk = clk_get(&mtu->pdev->dev, "mtu2_fck"); if (IS_ERR(mtu->clk)) { dev_err(&mtu->pdev->dev, "cannot get clock\n"); - ret = PTR_ERR(mtu->clk); - goto err1; + return PTR_ERR(mtu->clk); } ret = clk_prepare(mtu->clk); if (ret < 0) - goto err2; + goto err_clk_put; - mtu->channels = kzalloc(sizeof(*mtu->channels), GFP_KERNEL); + /* Map the memory resource. */ + ret = sh_mtu2_map_memory(mtu); + if (ret < 0) { + dev_err(&mtu->pdev->dev, "failed to remap I/O memory\n"); + goto err_clk_unprepare; + } + + /* Allocate and setup the channels. */ + if (mtu->legacy) + mtu->num_channels = 1; + else + mtu->num_channels = 3; + + mtu->channels = kzalloc(sizeof(*mtu->channels) * mtu->num_channels, + GFP_KERNEL); if (mtu->channels == NULL) { ret = -ENOMEM; - goto err3; + goto err_unmap; } - mtu->num_channels = 1; - - mtu->channels[0].base = base; + if (mtu->legacy) { + ret = sh_mtu2_setup_channel(&mtu->channels[0], 0, mtu); + if (ret < 0) + goto err_unmap; + } else { + for (i = 0; i < mtu->num_channels; ++i) { + ret = sh_mtu2_setup_channel(&mtu->channels[i], i, mtu); + if (ret < 0) + goto err_unmap; + } + } - ret = sh_mtu2_setup_channel(&mtu->channels[0], mtu); - if (ret < 0) - goto err3; + platform_set_drvdata(pdev, mtu); return 0; - err3: + +err_unmap: kfree(mtu->channels); + sh_mtu2_unmap_memory(mtu); +err_clk_unprepare: clk_unprepare(mtu->clk); - err2: +err_clk_put: clk_put(mtu->clk); - err1: - iounmap(base); - err0: return ret; } static int sh_mtu2_probe(struct platform_device *pdev) { struct sh_mtu2_device *mtu = platform_get_drvdata(pdev); - struct sh_timer_config *cfg = pdev->dev.platform_data; int ret; if (!is_early_platform_device(pdev)) { @@ -484,7 +552,7 @@ static int sh_mtu2_probe(struct platform_device *pdev) return 0; out: - if (cfg->clockevent_rating) + if (mtu->has_clockevent) pm_runtime_irq_safe(&pdev->dev); else pm_runtime_idle(&pdev->dev); @@ -497,12 +565,20 @@ static int sh_mtu2_remove(struct platform_device *pdev) return -EBUSY; /* cannot unregister clockevent */ } +static const struct platform_device_id sh_mtu2_id_table[] = { + { "sh_mtu2", 1 }, + { "sh-mtu2", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(platform, sh_mtu2_id_table); + static struct platform_driver sh_mtu2_device_driver = { .probe = sh_mtu2_probe, .remove = sh_mtu2_remove, .driver = { .name = "sh_mtu2", - } + }, + .id_table = sh_mtu2_id_table, }; static int __init sh_mtu2_init(void) -- cgit From 6dc9693bb3997cb324a2ffb39deaa72081a9bd0d Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 4 Mar 2014 18:09:15 +0100 Subject: clocksource: sh_mtu2: Rename clock to "fck" in the non-legacy case The sh_mtu2 driver gets the MTU2 functional clock using a connection ID of "mtu2_fck". While all SH SoCs create clock lookup entries with a NULL device ID and a "mtu2_fck" connection ID, the ARM SoCs use the device ID only with a NULL connection ID. This works on legacy platforms but will break on ARM with DT boot. Fix the situation by using a connection ID of "fck" in the non-legacy platform data case. Clock lookup entries will be renamed to use the device ID as well as the connection ID as platforms get moved to new platform data. The legacy code will eventually be dropped, leaving us with device ID based clock lookup, compatible with DT boot. Signed-off-by: Laurent Pinchart Tested-by: Wolfram Sang --- drivers/clocksource/sh_mtu2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clocksource/sh_mtu2.c b/drivers/clocksource/sh_mtu2.c index 7cc6d9429f81..3a3785702422 100644 --- a/drivers/clocksource/sh_mtu2.c +++ b/drivers/clocksource/sh_mtu2.c @@ -465,7 +465,7 @@ static int sh_mtu2_setup(struct sh_mtu2_device *mtu, } /* Get hold of clock. */ - mtu->clk = clk_get(&mtu->pdev->dev, "mtu2_fck"); + mtu->clk = clk_get(&mtu->pdev->dev, mtu->legacy ? "mtu2_fck" : "fck"); if (IS_ERR(mtu->clk)) { dev_err(&mtu->pdev->dev, "cannot get clock\n"); return PTR_ERR(mtu->clk); -- cgit From 24c8f71707087eb177b45f4a24faedaa0d8f0287 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 4 Mar 2014 14:12:32 +0100 Subject: clocksource: sh_mtu2: Remove FSF mail address from GPL notice Do not include the paragraph about writing to the Free Software Foundation's mailing address from the sample GPL notice. The FSF has changed addresses in the past, and may do so again. Linux already includes a copy of the GPL. Signed-off-by: Laurent Pinchart Tested-by: Wolfram Sang --- drivers/clocksource/sh_mtu2.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/clocksource/sh_mtu2.c b/drivers/clocksource/sh_mtu2.c index 3a3785702422..510bd324c1a9 100644 --- a/drivers/clocksource/sh_mtu2.c +++ b/drivers/clocksource/sh_mtu2.c @@ -11,10 +11,6 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include -- cgit From 346f5e76b3822a2530a03f33b00ee89dfc463326 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 4 Mar 2014 14:11:47 +0100 Subject: clocksource: sh_mtu2: Sort headers alphabetically This helps locating duplicates and inserting new headers. Signed-off-by: Laurent Pinchart Tested-by: Wolfram Sang --- drivers/clocksource/sh_mtu2.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/clocksource/sh_mtu2.c b/drivers/clocksource/sh_mtu2.c index 510bd324c1a9..f2c1c36139e1 100644 --- a/drivers/clocksource/sh_mtu2.c +++ b/drivers/clocksource/sh_mtu2.c @@ -13,22 +13,22 @@ * GNU General Public License for more details. */ +#include +#include +#include +#include #include -#include -#include #include -#include -#include #include -#include +#include #include -#include -#include -#include -#include #include +#include #include #include +#include +#include +#include struct sh_mtu2_device; -- cgit From 2cf2ff9f1fcdca404885bc55292b2046eabc039a Mon Sep 17 00:00:00 2001 From: Matthew Leach Date: Fri, 14 Mar 2014 10:18:19 +0000 Subject: clocksource: arm_global_timer: Only check for unusable timer on A9 The check for a usable global timer in the probe code does not enquire which CPU we are currently running on. This can cause the driver to incorrectly assume we have an unusable global timer if we are running on a CPU other than A9. Before checking the CPU revision, ensure we are running on an A9 CPU. Acked-by: Will Deacon Signed-off-by: Matthew Leach Signed-off-by: Daniel Lezcano --- drivers/clocksource/arm_global_timer.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/clocksource/arm_global_timer.c b/drivers/clocksource/arm_global_timer.c index 0fc31d029e52..60e5a170c4d2 100644 --- a/drivers/clocksource/arm_global_timer.c +++ b/drivers/clocksource/arm_global_timer.c @@ -246,11 +246,12 @@ static void __init global_timer_of_register(struct device_node *np) int err = 0; /* - * In r2p0 the comparators for each processor with the global timer + * In A9 r2p0 the comparators for each processor with the global timer * fire when the timer value is greater than or equal to. In previous * revisions the comparators fired when the timer value was equal to. */ - if ((read_cpuid_id() & 0xf0000f) < 0x200000) { + if (read_cpuid_part_number() == ARM_CPU_PART_CORTEX_A9 + && (read_cpuid_id() & 0xf0000f) < 0x200000) { pr_warn("global-timer: non support for this cpu version.\n"); return; } -- cgit From 10b1f231441d8f4422c27c6e9cffc0cb2c3d871d Mon Sep 17 00:00:00 2001 From: Matthew Leach Date: Fri, 14 Mar 2014 10:18:20 +0000 Subject: documentaion: DT: allow a A5 compatible string in global timer The global timer is present on the Cortex A5. Add a compatibility string to the DT binding to allow a Cortex A5 global timer. Acked-by: Rob Herring Signed-off-by: Matthew Leach Signed-off-by: Daniel Lezcano --- Documentation/devicetree/bindings/arm/global_timer.txt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/arm/global_timer.txt b/Documentation/devicetree/bindings/arm/global_timer.txt index 1e548981eda4..bdae3a818793 100644 --- a/Documentation/devicetree/bindings/arm/global_timer.txt +++ b/Documentation/devicetree/bindings/arm/global_timer.txt @@ -4,8 +4,11 @@ ** Timer node required properties: -- compatible : Should be "arm,cortex-a9-global-timer" - Driver supports versions r2p0 and above. +- compatible : should contain + * "arm,cortex-a5-global-timer" for Cortex-A5 global timers. + * "arm,cortex-a9-global-timer" for Cortex-A9 global + timers or any compatible implementation. Note: driver + supports versions r2p0 and above. - interrupts : One interrupt to each core -- cgit From 3309a8e22d9ca4fa4dada687372b682bc6a12343 Mon Sep 17 00:00:00 2001 From: Matthew Leach Date: Fri, 14 Mar 2014 10:18:21 +0000 Subject: dts: ca5: add the global timer for the A5 The Cortex A5 contains a global timer: add the appropriate device tree node. Acked-by: Mark Rutland Signed-off-by: Matthew Leach Signed-off-by: Daniel Lezcano --- arch/arm/boot/dts/vexpress-v2p-ca5s.dts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/vexpress-v2p-ca5s.dts b/arch/arm/boot/dts/vexpress-v2p-ca5s.dts index c544a5504591..d2709b73316b 100644 --- a/arch/arm/boot/dts/vexpress-v2p-ca5s.dts +++ b/arch/arm/boot/dts/vexpress-v2p-ca5s.dts @@ -88,6 +88,14 @@ interrupts = <1 13 0x304>; }; + timer@2c000200 { + compatible = "arm,cortex-a5-global-timer", + "arm,cortex-a9-global-timer"; + reg = <0x2c000200 0x20>; + interrupts = <1 11 0x304>; + clocks = <&oscclk0>; + }; + watchdog@2c000620 { compatible = "arm,cortex-a5-twd-wdt"; reg = <0x2c000620 0x20>; @@ -120,7 +128,7 @@ compatible = "arm,vexpress,config-bus"; arm,vexpress,config-bridge = <&v2m_sysreg>; - osc@0 { + oscclk0: osc@0 { /* CPU and internal AXI reference clock */ compatible = "arm,vexpress-osc"; arm,vexpress-sysreg,func = <1 0>; -- cgit From 7e13c654f446e3c9848ac0b725ae4daf25e10846 Mon Sep 17 00:00:00 2001 From: Matthew Leach Date: Fri, 14 Mar 2014 10:18:22 +0000 Subject: KConfig: Vexpress: build the ARM_GLOBAL_TIMER with vexpress platform The Cortex A5 now contains bindings for the global timer driver. Ensure that the driver is built with a vexpress platform. Signed-off-by: Matthew Leach Signed-off-by: Daniel Lezcano --- arch/arm/mach-vexpress/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/mach-vexpress/Kconfig b/arch/arm/mach-vexpress/Kconfig index 657d52d0391f..e9811a07829a 100644 --- a/arch/arm/mach-vexpress/Kconfig +++ b/arch/arm/mach-vexpress/Kconfig @@ -4,6 +4,7 @@ config ARCH_VEXPRESS select ARCH_SUPPORTS_BIG_ENDIAN select ARM_AMBA select ARM_GIC + select ARM_GLOBAL_TIMER select ARM_TIMER_SP804 select COMMON_CLK_VERSATILE select HAVE_ARM_SCU if SMP -- cgit From 63cc122381bda123c2b1b0137cca09f66e5be660 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 25 Mar 2014 15:50:44 +0100 Subject: clocksource: efm32: use $vendor,$device scheme for compatible string MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wolfram Sang pointed out that "efm32,$device" is non-standard. So use the common scheme and prefix device with "efm32-". The old compatible string is left in place until arch/arm/boot/dts/efm32* is fixed. Acked-by: Wolfram Sang Acked-by: Rob Herring Signed-off-by: Uwe Kleine-König Signed-off-by: Daniel Lezcano --- .../devicetree/bindings/timer/efm32,timer.txt | 23 ---------------------- .../bindings/timer/energymicro,efm32-timer.txt | 23 ++++++++++++++++++++++ drivers/clocksource/time-efm32.c | 3 ++- 3 files changed, 25 insertions(+), 24 deletions(-) delete mode 100644 Documentation/devicetree/bindings/timer/efm32,timer.txt create mode 100644 Documentation/devicetree/bindings/timer/energymicro,efm32-timer.txt diff --git a/Documentation/devicetree/bindings/timer/efm32,timer.txt b/Documentation/devicetree/bindings/timer/efm32,timer.txt deleted file mode 100644 index 97a568f696c9..000000000000 --- a/Documentation/devicetree/bindings/timer/efm32,timer.txt +++ /dev/null @@ -1,23 +0,0 @@ -* EFM32 timer hardware - -The efm32 Giant Gecko SoCs come with four 16 bit timers. Two counters can be -connected to form a 32 bit counter. Each timer has three Compare/Capture -channels and can be used as PWM or Quadrature Decoder. Available clock sources -are the cpu's HFPERCLK (with a 10-bit prescaler) or an external pin. - -Required properties: -- compatible : Should be efm32,timer -- reg : Address and length of the register set -- clocks : Should contain a reference to the HFPERCLK - -Optional properties: -- interrupts : Reference to the timer interrupt - -Example: - -timer@40010c00 { - compatible = "efm32,timer"; - reg = <0x40010c00 0x400>; - interrupts = <14>; - clocks = <&cmu clk_HFPERCLKTIMER3>; -}; diff --git a/Documentation/devicetree/bindings/timer/energymicro,efm32-timer.txt b/Documentation/devicetree/bindings/timer/energymicro,efm32-timer.txt new file mode 100644 index 000000000000..e502c11b2211 --- /dev/null +++ b/Documentation/devicetree/bindings/timer/energymicro,efm32-timer.txt @@ -0,0 +1,23 @@ +* EFM32 timer hardware + +The efm32 Giant Gecko SoCs come with four 16 bit timers. Two counters can be +connected to form a 32 bit counter. Each timer has three Compare/Capture +channels and can be used as PWM or Quadrature Decoder. Available clock sources +are the cpu's HFPERCLK (with a 10-bit prescaler) or an external pin. + +Required properties: +- compatible : Should be "energymicro,efm32-timer" +- reg : Address and length of the register set +- clocks : Should contain a reference to the HFPERCLK + +Optional properties: +- interrupts : Reference to the timer interrupt + +Example: + +timer@40010c00 { + compatible = "energymicro,efm32-timer"; + reg = <0x40010c00 0x400>; + interrupts = <14>; + clocks = <&cmu clk_HFPERCLKTIMER3>; +}; diff --git a/drivers/clocksource/time-efm32.c b/drivers/clocksource/time-efm32.c index 1a6205b7bed3..bba62f9deefb 100644 --- a/drivers/clocksource/time-efm32.c +++ b/drivers/clocksource/time-efm32.c @@ -272,4 +272,5 @@ static void __init efm32_timer_init(struct device_node *np) } } } -CLOCKSOURCE_OF_DECLARE(efm32, "efm32,timer", efm32_timer_init); +CLOCKSOURCE_OF_DECLARE(efm32compat, "efm32,timer", efm32_timer_init); +CLOCKSOURCE_OF_DECLARE(efm32, "energymicro,efm32-timer", efm32_timer_init); -- cgit From e50a00be5c420b4f28836dec281cdde4bed832a2 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Thu, 17 Apr 2014 11:06:45 +0200 Subject: clocksource: sun5i: Add support for reset controller The Allwinner A31 that uses this timer has the timer IP asserted in reset. Add an optional reset property to the DT, and deassert the timer from reset if it's there. Signed-off-by: Maxime Ripard Signed-off-by: Daniel Lezcano --- .../devicetree/bindings/timer/allwinner,sun5i-a13-hstimer.txt | 4 ++++ drivers/clocksource/timer-sun5i.c | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/Documentation/devicetree/bindings/timer/allwinner,sun5i-a13-hstimer.txt b/Documentation/devicetree/bindings/timer/allwinner,sun5i-a13-hstimer.txt index 7c26154b8bbb..27cfc7d7ccd7 100644 --- a/Documentation/devicetree/bindings/timer/allwinner,sun5i-a13-hstimer.txt +++ b/Documentation/devicetree/bindings/timer/allwinner,sun5i-a13-hstimer.txt @@ -9,6 +9,9 @@ Required properties: one) - clocks: phandle to the source clock (usually the AHB clock) +Optionnal properties: +- resets: phandle to a reset controller asserting the timer + Example: timer@01c60000 { @@ -19,4 +22,5 @@ timer@01c60000 { <0 53 1>, <0 54 1>; clocks = <&ahb1_gates 19>; + resets = <&ahb1rst 19>; }; diff --git a/drivers/clocksource/timer-sun5i.c b/drivers/clocksource/timer-sun5i.c index deebcd6469fc..02268448dc85 100644 --- a/drivers/clocksource/timer-sun5i.c +++ b/drivers/clocksource/timer-sun5i.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -143,6 +144,7 @@ static u64 sun5i_timer_sched_read(void) static void __init sun5i_timer_init(struct device_node *node) { + struct reset_control *rstc; unsigned long rate; struct clk *clk; int ret, irq; @@ -162,6 +164,10 @@ static void __init sun5i_timer_init(struct device_node *node) clk_prepare_enable(clk); rate = clk_get_rate(clk); + rstc = of_reset_control_get(node, NULL); + if (!IS_ERR(rstc)) + reset_control_deassert(rstc); + writel(~0, timer_base + TIMER_INTVAL_LO_REG(1)); writel(TIMER_CTL_ENABLE | TIMER_CTL_RELOAD, timer_base + TIMER_CTL_REG(1)); -- cgit From 8cffcb0ca33381e07cee4ebe1301d0794054fc97 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Thu, 17 Apr 2014 11:06:46 +0200 Subject: ARM: sun6i: a31: Add support for the High Speed Timers The Allwinner A31 has support for four high speed timers. Apart for the number of timers (4 vs 2), it's basically the same logic than the high speed timers found in the sun5i chips. Now that we have a driver to support it, we can enable them in the device tree. Signed-off-by: Maxime Ripard Signed-off-by: Daniel Lezcano --- arch/arm/boot/dts/sun6i-a31.dtsi | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/arch/arm/boot/dts/sun6i-a31.dtsi b/arch/arm/boot/dts/sun6i-a31.dtsi index d45efa74827c..8cee8a15b90b 100644 --- a/arch/arm/boot/dts/sun6i-a31.dtsi +++ b/arch/arm/boot/dts/sun6i-a31.dtsi @@ -428,6 +428,17 @@ status = "disabled"; }; + timer@01c60000 { + compatible = "allwinner,sun6i-a31-hstimer", "allwinner,sun7i-a20-hstimer"; + reg = <0x01c60000 0x1000>; + interrupts = <0 51 4>, + <0 52 4>, + <0 53 4>, + <0 54 4>; + clocks = <&ahb1_gates 19>; + resets = <&ahb1_rst 19>; + }; + spi0: spi@01c68000 { compatible = "allwinner,sun6i-a31-spi"; reg = <0x01c68000 0x1000>; -- cgit From 013be5adf5cd6e6d58eb72f3bb0137a78be152c8 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Tue, 13 May 2014 16:01:00 -0700 Subject: clocksource: qcom: Implement read_current_timer for udelay Setup the same timer used as the clocksource to be used as the read_current_timer implementation. This allows us to support a stable udelay implementation on MSMs where it's possible for the CPUs to scale speeds independently of one another. Signed-off-by: Stephen Boyd Signed-off-by: Daniel Lezcano --- drivers/clocksource/qcom-timer.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/clocksource/qcom-timer.c b/drivers/clocksource/qcom-timer.c index e807acf4c665..8d115db1e651 100644 --- a/drivers/clocksource/qcom-timer.c +++ b/drivers/clocksource/qcom-timer.c @@ -26,6 +26,8 @@ #include #include +#include + #define TIMER_MATCH_VAL 0x0000 #define TIMER_COUNT_VAL 0x0004 #define TIMER_ENABLE 0x0008 @@ -179,6 +181,15 @@ static u64 notrace msm_sched_clock_read(void) return msm_clocksource.read(&msm_clocksource); } +static unsigned long msm_read_current_timer(void) +{ + return msm_clocksource.read(&msm_clocksource); +} + +static struct delay_timer msm_delay_timer = { + .read_current_timer = msm_read_current_timer, +}; + static void __init msm_timer_init(u32 dgt_hz, int sched_bits, int irq, bool percpu) { @@ -217,6 +228,8 @@ err: if (res) pr_err("clocksource_register failed\n"); sched_clock_register(msm_sched_clock_read, sched_bits, dgt_hz); + msm_delay_timer.freq = dgt_hz; + register_current_timer_delay(&msm_delay_timer); } #ifdef CONFIG_ARCH_QCOM -- cgit From c54697ae180e652a96db8bc71f0c7c7baee2bf16 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 16 May 2014 14:44:23 +0200 Subject: clocksource: sh_tmu: Fix channel IRQ retrieval in legacy case In the legacy platform data case each TMU platform device handles a single channel with a single IRQ for the platform device. Retrieve the IRQ using the logical channel number instead of the hardware channel number. Signed-off-by: Laurent Pinchart Signed-off-by: Daniel Lezcano --- drivers/clocksource/sh_tmu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clocksource/sh_tmu.c b/drivers/clocksource/sh_tmu.c index 4ba2c0fea580..ec340955e852 100644 --- a/drivers/clocksource/sh_tmu.c +++ b/drivers/clocksource/sh_tmu.c @@ -498,7 +498,7 @@ static int sh_tmu_channel_setup(struct sh_tmu_channel *ch, unsigned int index, ch->base = tmu->mapbase + 8 + ch->index * 12; } - ch->irq = platform_get_irq(tmu->pdev, ch->index); + ch->irq = platform_get_irq(tmu->pdev, index); if (ch->irq < 0) { dev_err(&tmu->pdev->dev, "ch%u: failed to get irq\n", ch->index); -- cgit From 4a3ae07413844733e4cb3765f67a752b846df913 Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Wed, 23 Apr 2014 10:11:59 +0800 Subject: clocksource: Fix type confusion for clocksource_mmio_readX_Y The types' definations are: o cycle_t -> u64 o readl_relaxed -> u32 o readw_relaxed -> u16 So let clocksource_mmio_readX_Ys return a cast to cycle_t, though this maybe look reduntant sometimes, it make sense and they will be more readable and less confusion... This patch clarifies the functions type and fix it. Signed-off-by: Xiubo Li Cc: Daniel Lezcano Signed-off-by: Daniel Lezcano --- drivers/clocksource/mmio.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/clocksource/mmio.c b/drivers/clocksource/mmio.c index c0e25125a55e..19a6b3f4d9cf 100644 --- a/drivers/clocksource/mmio.c +++ b/drivers/clocksource/mmio.c @@ -22,22 +22,22 @@ static inline struct clocksource_mmio *to_mmio_clksrc(struct clocksource *c) cycle_t clocksource_mmio_readl_up(struct clocksource *c) { - return readl_relaxed(to_mmio_clksrc(c)->reg); + return (cycle_t)readl_relaxed(to_mmio_clksrc(c)->reg); } cycle_t clocksource_mmio_readl_down(struct clocksource *c) { - return ~readl_relaxed(to_mmio_clksrc(c)->reg); + return ~(cycle_t)readl_relaxed(to_mmio_clksrc(c)->reg); } cycle_t clocksource_mmio_readw_up(struct clocksource *c) { - return readw_relaxed(to_mmio_clksrc(c)->reg); + return (cycle_t)readw_relaxed(to_mmio_clksrc(c)->reg); } cycle_t clocksource_mmio_readw_down(struct clocksource *c) { - return ~(unsigned)readw_relaxed(to_mmio_clksrc(c)->reg); + return ~(cycle_t)readw_relaxed(to_mmio_clksrc(c)->reg); } /** -- cgit From 95c19a06ec1cf9530ebb8f2c7eeda1d7398b43f1 Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Wed, 23 Apr 2014 10:12:00 +0800 Subject: clocksource: Fix clocksource_mmio_readX_down For some clocksource devices, for example, the registers are 32-bit, while the lower 16-bit is used for timer counting(And reading the upper 16-bit will return 0). For example, when the counter value is 0x00001111, and then the ~readl_relaxed(to_mmio_clksrc(c)->reg) will return the value of 0xFFFFEEEE, but it should be 0x0000EEEE. So just using the c->mask to mask the unused bits. Signed-off-by: Xiubo Li Signed-off-by: Daniel Lezcano --- drivers/clocksource/mmio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/clocksource/mmio.c b/drivers/clocksource/mmio.c index 19a6b3f4d9cf..1593ade2a815 100644 --- a/drivers/clocksource/mmio.c +++ b/drivers/clocksource/mmio.c @@ -27,7 +27,7 @@ cycle_t clocksource_mmio_readl_up(struct clocksource *c) cycle_t clocksource_mmio_readl_down(struct clocksource *c) { - return ~(cycle_t)readl_relaxed(to_mmio_clksrc(c)->reg); + return ~(cycle_t)readl_relaxed(to_mmio_clksrc(c)->reg) & c->mask; } cycle_t clocksource_mmio_readw_up(struct clocksource *c) @@ -37,7 +37,7 @@ cycle_t clocksource_mmio_readw_up(struct clocksource *c) cycle_t clocksource_mmio_readw_down(struct clocksource *c) { - return ~(cycle_t)readw_relaxed(to_mmio_clksrc(c)->reg); + return ~(cycle_t)readw_relaxed(to_mmio_clksrc(c)->reg) & c->mask; } /** -- cgit From 0d24d1f2495ec4d6996c70c8edec202053cf7e69 Mon Sep 17 00:00:00 2001 From: Yang Wei Date: Tue, 13 May 2014 11:10:08 +0800 Subject: clocksource: dw_apb_timer_of: Do not trace read_sched_clock We do not need to trace read_sched_clock function, so add notrace attribute for this function. Signed-off-by: Yang Wei Signed-off-by: Daniel Lezcano --- drivers/clocksource/dw_apb_timer_of.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clocksource/dw_apb_timer_of.c b/drivers/clocksource/dw_apb_timer_of.c index 2a2ea2717f3a..d305fb089767 100644 --- a/drivers/clocksource/dw_apb_timer_of.c +++ b/drivers/clocksource/dw_apb_timer_of.c @@ -106,7 +106,7 @@ static void __init add_clocksource(struct device_node *source_timer) sched_rate = rate; } -static u64 read_sched_clock(void) +static u64 notrace read_sched_clock(void) { return ~__raw_readl(sched_io_base); } -- cgit From 39dd56776e3583d08bdfaabae2171a1a70a679c9 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 22 May 2014 14:05:06 +0200 Subject: clocksource: em_sti: Remove unnecessary OOM messages The site-specific OOM messages are unnecessary, because they duplicate the MM subsystem generic OOM message. Signed-off-by: Jingoo Han Signed-off-by: Daniel Lezcano --- drivers/clocksource/em_sti.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/clocksource/em_sti.c b/drivers/clocksource/em_sti.c index 9d170834fcf3..d0a7bd66b8b9 100644 --- a/drivers/clocksource/em_sti.c +++ b/drivers/clocksource/em_sti.c @@ -318,10 +318,8 @@ static int em_sti_probe(struct platform_device *pdev) int irq; p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL); - if (p == NULL) { - dev_err(&pdev->dev, "failed to allocate driver data\n"); + if (p == NULL) return -ENOMEM; - } p->pdev = pdev; platform_set_drvdata(pdev, p); -- cgit From 0178f41d3d35b63ed25a066d90e7dda380018c06 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 22 May 2014 14:05:06 +0200 Subject: clocksource: sh_cmt: Remove unnecessary OOM messages The site-specific OOM messages are unnecessary, because they duplicate the MM subsystem generic OOM message. [dlezcano] : refreshed against latest modifications: kmalloc -> kzalloc Signed-off-by: Jingoo Han Signed-off-by: Daniel Lezcano --- drivers/clocksource/sh_cmt.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c index bc8d025ce861..dfa780396b91 100644 --- a/drivers/clocksource/sh_cmt.c +++ b/drivers/clocksource/sh_cmt.c @@ -1106,10 +1106,8 @@ static int sh_cmt_probe(struct platform_device *pdev) } cmt = kzalloc(sizeof(*cmt), GFP_KERNEL); - if (cmt == NULL) { - dev_err(&pdev->dev, "failed to allocate driver data\n"); + if (cmt == NULL) return -ENOMEM; - } ret = sh_cmt_setup(cmt, pdev); if (ret) { -- cgit From c77a565b2966567b97d589e90a6b9ce725bb15b1 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 22 May 2014 14:05:07 +0200 Subject: clocksource: sh_mtu2: Remove unnecessary OOM messages The site-specific OOM messages are unnecessary, because they duplicate the MM subsystem generic OOM message. [dlezcano] : refreshed against latest modifications: kmalloc -> kzalloc Signed-off-by: Jingoo Han Signed-off-by: Daniel Lezcano --- drivers/clocksource/sh_mtu2.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/clocksource/sh_mtu2.c b/drivers/clocksource/sh_mtu2.c index f2c1c36139e1..188d4e092efc 100644 --- a/drivers/clocksource/sh_mtu2.c +++ b/drivers/clocksource/sh_mtu2.c @@ -533,10 +533,8 @@ static int sh_mtu2_probe(struct platform_device *pdev) } mtu = kzalloc(sizeof(*mtu), GFP_KERNEL); - if (mtu == NULL) { - dev_err(&pdev->dev, "failed to allocate driver data\n"); + if (mtu == NULL) return -ENOMEM; - } ret = sh_mtu2_setup(mtu, pdev); if (ret) { -- cgit From 814876b0b00ae98a8568d1b989bc98ca5389b98a Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 22 May 2014 14:05:07 +0200 Subject: clocksource: sh_tmu: Remove unnecessary OOM messages The site-specific OOM messages are unnecessary, because they duplicate the MM subsystem generic OOM message. [dlezcano] : refreshed against latest modifications: kmalloc -> kzalloc Signed-off-by: Jingoo Han Signed-off-by: Daniel Lezcano --- drivers/clocksource/sh_tmu.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/clocksource/sh_tmu.c b/drivers/clocksource/sh_tmu.c index 4ba2c0fea580..a38022fa286f 100644 --- a/drivers/clocksource/sh_tmu.c +++ b/drivers/clocksource/sh_tmu.c @@ -644,10 +644,8 @@ static int sh_tmu_probe(struct platform_device *pdev) } tmu = kzalloc(sizeof(*tmu), GFP_KERNEL); - if (tmu == NULL) { - dev_err(&pdev->dev, "failed to allocate driver data\n"); + if (tmu == NULL) return -ENOMEM; - } ret = sh_tmu_setup(tmu, pdev); if (ret) { -- cgit From 12e499d0ed1fa09940a573e5a8cce52b556f3c38 Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Fri, 23 May 2014 10:12:04 +0200 Subject: clocksource: ftm: Add FlexTimer Module (FTM) Timer devicetree Documentation The FTM binding could be used on Vybrid and LS1+, add a binding document for it. Signed-off-by: Xiubo Li Cc: Shawn Guo Cc: Jingchang Lu Signed-off-by: Daniel Lezcano --- .../devicetree/bindings/timer/fsl,ftm-timer.txt | 31 ++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 Documentation/devicetree/bindings/timer/fsl,ftm-timer.txt diff --git a/Documentation/devicetree/bindings/timer/fsl,ftm-timer.txt b/Documentation/devicetree/bindings/timer/fsl,ftm-timer.txt new file mode 100644 index 000000000000..aa8c40230e5e --- /dev/null +++ b/Documentation/devicetree/bindings/timer/fsl,ftm-timer.txt @@ -0,0 +1,31 @@ +Freescale FlexTimer Module (FTM) Timer + +Required properties: + +- compatible : should be "fsl,ftm-timer" +- reg : Specifies base physical address and size of the register sets for the + clock event device and clock source device. +- interrupts : Should be the clock event device interrupt. +- clocks : The clocks provided by the SoC to drive the timer, must contain an + entry for each entry in clock-names. +- clock-names : Must include the following entries: + o "ftm-evt" + o "ftm-src" + o "ftm-evt-counter-en" + o "ftm-src-counter-en" +- big-endian: One boolean property, the big endian mode will be in use if it is + present, or the little endian mode will be in use for all the device registers. + +Example: +ftm: ftm@400b8000 { + compatible = "fsl,ftm-timer"; + reg = <0x400b8000 0x1000 0x400b9000 0x1000>; + interrupts = <0 44 IRQ_TYPE_LEVEL_HIGH>; + clock-names = "ftm-evt", "ftm-src", + "ftm-evt-counter-en", "ftm-src-counter-en"; + clocks = <&clks VF610_CLK_FTM2>, + <&clks VF610_CLK_FTM3>, + <&clks VF610_CLK_FTM2_EXT_FIX_EN>, + <&clks VF610_CLK_FTM3_EXT_FIX_EN>; + big-endian; +}; -- cgit From 07513e1330a90253c564e2b9258f9cd12e1eb7ba Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Fri, 23 May 2014 10:12:04 +0200 Subject: ARM: dts: vf610: Add Freescale FlexTimer Module timer node. Signed-off-by: Xiubo Li Cc: Shawn Guo Cc: Jingchang Lu Signed-off-by: Daniel Lezcano --- arch/arm/boot/dts/vf610.dtsi | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/arch/arm/boot/dts/vf610.dtsi b/arch/arm/boot/dts/vf610.dtsi index b8ce0aa7b157..3c91b84066a1 100644 --- a/arch/arm/boot/dts/vf610.dtsi +++ b/arch/arm/boot/dts/vf610.dtsi @@ -347,6 +347,19 @@ status = "disabled"; }; + ftm: ftm@400b8000 { + compatible = "fsl,ftm-timer"; + reg = <0x400b8000 0x1000 0x400b9000 0x1000>; + interrupts = <0 44 IRQ_TYPE_LEVEL_HIGH>; + clock-names = "ftm-evt", "ftm-src", + "ftm-evt-counter-en", "ftm-src-counter-en"; + clocks = <&clks VF610_CLK_FTM2>, + <&clks VF610_CLK_FTM3>, + <&clks VF610_CLK_FTM2_EXT_FIX_EN>, + <&clks VF610_CLK_FTM3_EXT_FIX_EN>; + status = "disabled"; + }; + fec0: ethernet@400d0000 { compatible = "fsl,mvf600-fec"; reg = <0x400d0000 0x1000>; -- cgit From 2529c3a330797000d699d70c9a65b8525c6652de Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Fri, 23 May 2014 10:12:04 +0200 Subject: clocksource: Add Freescale FlexTimer Module (FTM) timer support The Freescale FlexTimer Module time reference is a 16-bit counter that can be used as an unsigned or signed increase counter. CNTIN defines the starting value of the count and MOD defines the final value of the count. The value of CNTIN is loaded into the FTM counter, and the counter increments until the value of MOD is reached, at which point the counter is reloaded with the value of CNTIN. That's also when an overflow interrupt will be generated. Here using the 'evt' prefix or postfix as clock event device and the 'src' as clock source device. Signed-off-by: Xiubo Li Cc: Shawn Guo Cc: Jingchang Lu Signed-off-by: Daniel Lezcano --- drivers/clocksource/Kconfig | 5 + drivers/clocksource/Makefile | 1 + drivers/clocksource/fsl_ftm_timer.c | 367 ++++++++++++++++++++++++++++++++++++ 3 files changed, 373 insertions(+) create mode 100644 drivers/clocksource/fsl_ftm_timer.c diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 96918e1f26a3..04377675c3fa 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -136,6 +136,11 @@ config CLKSRC_SAMSUNG_PWM for all devicetree enabled platforms. This driver will be needed only on systems that do not have the Exynos MCT available. +config FSL_FTM_TIMER + bool + help + Support for Freescale FlexTimer Module (FTM) timer. + config VF_PIT_TIMER bool help diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index 98cb6c51aa87..0770916a818e 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_CADENCE_TTC_TIMER) += cadence_ttc_timer.o obj-$(CONFIG_CLKSRC_EFM32) += time-efm32.o obj-$(CONFIG_CLKSRC_EXYNOS_MCT) += exynos_mct.o obj-$(CONFIG_CLKSRC_SAMSUNG_PWM) += samsung_pwm_timer.o +obj-$(CONFIG_FSL_FTM_TIMER) += fsl_ftm_timer.o obj-$(CONFIG_VF_PIT_TIMER) += vf_pit_timer.o obj-$(CONFIG_CLKSRC_QCOM) += qcom-timer.o diff --git a/drivers/clocksource/fsl_ftm_timer.c b/drivers/clocksource/fsl_ftm_timer.c new file mode 100644 index 000000000000..454227d4f895 --- /dev/null +++ b/drivers/clocksource/fsl_ftm_timer.c @@ -0,0 +1,367 @@ +/* + * Freescale FlexTimer Module (FTM) timer driver. + * + * Copyright 2014 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define FTM_SC 0x00 +#define FTM_SC_CLK_SHIFT 3 +#define FTM_SC_CLK_MASK (0x3 << FTM_SC_CLK_SHIFT) +#define FTM_SC_CLK(c) ((c) << FTM_SC_CLK_SHIFT) +#define FTM_SC_PS_MASK 0x7 +#define FTM_SC_TOIE BIT(6) +#define FTM_SC_TOF BIT(7) + +#define FTM_CNT 0x04 +#define FTM_MOD 0x08 +#define FTM_CNTIN 0x4C + +#define FTM_PS_MAX 7 + +struct ftm_clock_device { + void __iomem *clksrc_base; + void __iomem *clkevt_base; + unsigned long periodic_cyc; + unsigned long ps; + bool big_endian; +}; + +static struct ftm_clock_device *priv; + +static inline u32 ftm_readl(void __iomem *addr) +{ + if (priv->big_endian) + return ioread32be(addr); + else + return ioread32(addr); +} + +static inline void ftm_writel(u32 val, void __iomem *addr) +{ + if (priv->big_endian) + iowrite32be(val, addr); + else + iowrite32(val, addr); +} + +static inline void ftm_counter_enable(void __iomem *base) +{ + u32 val; + + /* select and enable counter clock source */ + val = ftm_readl(base + FTM_SC); + val &= ~(FTM_SC_PS_MASK | FTM_SC_CLK_MASK); + val |= priv->ps | FTM_SC_CLK(1); + ftm_writel(val, base + FTM_SC); +} + +static inline void ftm_counter_disable(void __iomem *base) +{ + u32 val; + + /* disable counter clock source */ + val = ftm_readl(base + FTM_SC); + val &= ~(FTM_SC_PS_MASK | FTM_SC_CLK_MASK); + ftm_writel(val, base + FTM_SC); +} + +static inline void ftm_irq_acknowledge(void __iomem *base) +{ + u32 val; + + val = ftm_readl(base + FTM_SC); + val &= ~FTM_SC_TOF; + ftm_writel(val, base + FTM_SC); +} + +static inline void ftm_irq_enable(void __iomem *base) +{ + u32 val; + + val = ftm_readl(base + FTM_SC); + val |= FTM_SC_TOIE; + ftm_writel(val, base + FTM_SC); +} + +static inline void ftm_irq_disable(void __iomem *base) +{ + u32 val; + + val = ftm_readl(base + FTM_SC); + val &= ~FTM_SC_TOIE; + ftm_writel(val, base + FTM_SC); +} + +static inline void ftm_reset_counter(void __iomem *base) +{ + /* + * The CNT register contains the FTM counter value. + * Reset clears the CNT register. Writing any value to COUNT + * updates the counter with its initial value, CNTIN. + */ + ftm_writel(0x00, base + FTM_CNT); +} + +static u64 ftm_read_sched_clock(void) +{ + return ftm_readl(priv->clksrc_base + FTM_CNT); +} + +static int ftm_set_next_event(unsigned long delta, + struct clock_event_device *unused) +{ + /* + * The CNNIN and MOD are all double buffer registers, writing + * to the MOD register latches the value into a buffer. The MOD + * register is updated with the value of its write buffer with + * the following scenario: + * a, the counter source clock is diabled. + */ + ftm_counter_disable(priv->clkevt_base); + + /* Force the value of CNTIN to be loaded into the FTM counter */ + ftm_reset_counter(priv->clkevt_base); + + /* + * The counter increments until the value of MOD is reached, + * at which point the counter is reloaded with the value of CNTIN. + * The TOF (the overflow flag) bit is set when the FTM counter + * changes from MOD to CNTIN. So we should using the delta - 1. + */ + ftm_writel(delta - 1, priv->clkevt_base + FTM_MOD); + + ftm_counter_enable(priv->clkevt_base); + + ftm_irq_enable(priv->clkevt_base); + + return 0; +} + +static void ftm_set_mode(enum clock_event_mode mode, + struct clock_event_device *evt) +{ + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + ftm_set_next_event(priv->periodic_cyc, evt); + break; + case CLOCK_EVT_MODE_ONESHOT: + ftm_counter_disable(priv->clkevt_base); + break; + default: + return; + } +} + +static irqreturn_t ftm_evt_interrupt(int irq, void *dev_id) +{ + struct clock_event_device *evt = dev_id; + + ftm_irq_acknowledge(priv->clkevt_base); + + if (likely(evt->mode == CLOCK_EVT_MODE_ONESHOT)) { + ftm_irq_disable(priv->clkevt_base); + ftm_counter_disable(priv->clkevt_base); + } + + evt->event_handler(evt); + + return IRQ_HANDLED; +} + +static struct clock_event_device ftm_clockevent = { + .name = "Freescale ftm timer", + .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, + .set_mode = ftm_set_mode, + .set_next_event = ftm_set_next_event, + .rating = 300, +}; + +static struct irqaction ftm_timer_irq = { + .name = "Freescale ftm timer", + .flags = IRQF_TIMER | IRQF_IRQPOLL, + .handler = ftm_evt_interrupt, + .dev_id = &ftm_clockevent, +}; + +static int __init ftm_clockevent_init(unsigned long freq, int irq) +{ + int err; + + ftm_writel(0x00, priv->clkevt_base + FTM_CNTIN); + ftm_writel(~0UL, priv->clkevt_base + FTM_MOD); + + ftm_reset_counter(priv->clkevt_base); + + err = setup_irq(irq, &ftm_timer_irq); + if (err) { + pr_err("ftm: setup irq failed: %d\n", err); + return err; + } + + ftm_clockevent.cpumask = cpumask_of(0); + ftm_clockevent.irq = irq; + + clockevents_config_and_register(&ftm_clockevent, + freq / (1 << priv->ps), + 1, 0xffff); + + ftm_counter_enable(priv->clkevt_base); + + return 0; +} + +static int __init ftm_clocksource_init(unsigned long freq) +{ + int err; + + ftm_writel(0x00, priv->clksrc_base + FTM_CNTIN); + ftm_writel(~0UL, priv->clksrc_base + FTM_MOD); + + ftm_reset_counter(priv->clksrc_base); + + sched_clock_register(ftm_read_sched_clock, 16, freq / (1 << priv->ps)); + err = clocksource_mmio_init(priv->clksrc_base + FTM_CNT, "fsl-ftm", + freq / (1 << priv->ps), 300, 16, + clocksource_mmio_readl_up); + if (err) { + pr_err("ftm: init clock source mmio failed: %d\n", err); + return err; + } + + ftm_counter_enable(priv->clksrc_base); + + return 0; +} + +static int __init __ftm_clk_init(struct device_node *np, char *cnt_name, + char *ftm_name) +{ + struct clk *clk; + int err; + + clk = of_clk_get_by_name(np, cnt_name); + if (IS_ERR(clk)) { + pr_err("ftm: Cannot get \"%s\": %ld\n", cnt_name, PTR_ERR(clk)); + return PTR_ERR(clk); + } + err = clk_prepare_enable(clk); + if (err) { + pr_err("ftm: clock failed to prepare+enable \"%s\": %d\n", + cnt_name, err); + return err; + } + + clk = of_clk_get_by_name(np, ftm_name); + if (IS_ERR(clk)) { + pr_err("ftm: Cannot get \"%s\": %ld\n", ftm_name, PTR_ERR(clk)); + return PTR_ERR(clk); + } + err = clk_prepare_enable(clk); + if (err) + pr_err("ftm: clock failed to prepare+enable \"%s\": %d\n", + ftm_name, err); + + return clk_get_rate(clk); +} + +static unsigned long __init ftm_clk_init(struct device_node *np) +{ + unsigned long freq; + + freq = __ftm_clk_init(np, "ftm-evt-counter-en", "ftm-evt"); + if (freq <= 0) + return 0; + + freq = __ftm_clk_init(np, "ftm-src-counter-en", "ftm-src"); + if (freq <= 0) + return 0; + + return freq; +} + +static int __init ftm_calc_closest_round_cyc(unsigned long freq) +{ + priv->ps = 0; + + /* The counter register is only using the lower 16 bits, and + * if the 'freq' value is to big here, then the periodic_cyc + * may exceed 0xFFFF. + */ + do { + priv->periodic_cyc = DIV_ROUND_CLOSEST(freq, + HZ * (1 << priv->ps++)); + } while (priv->periodic_cyc > 0xFFFF); + + if (priv->ps > FTM_PS_MAX) { + pr_err("ftm: the prescaler is %lu > %d\n", + priv->ps, FTM_PS_MAX); + return -EINVAL; + } + + return 0; +} + +static void __init ftm_timer_init(struct device_node *np) +{ + unsigned long freq; + int irq; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return; + + priv->clkevt_base = of_iomap(np, 0); + if (!priv->clkevt_base) { + pr_err("ftm: unable to map event timer registers\n"); + goto err; + } + + priv->clksrc_base = of_iomap(np, 1); + if (!priv->clksrc_base) { + pr_err("ftm: unable to map source timer registers\n"); + goto err; + } + + irq = irq_of_parse_and_map(np, 0); + if (irq <= 0) { + pr_err("ftm: unable to get IRQ from DT, %d\n", irq); + goto err; + } + + priv->big_endian = of_property_read_bool(np, "big-endian"); + + freq = ftm_clk_init(np); + if (!freq) + goto err; + + if (ftm_calc_closest_round_cyc(freq)) + goto err; + + if (ftm_clocksource_init(freq)) + goto err; + + if (ftm_clockevent_init(freq, irq)) + goto err; + + return; + +err: + kfree(priv); +} +CLOCKSOURCE_OF_DECLARE(flextimer, "fsl,ftm-timer", ftm_timer_init); -- cgit