static int sh_tmu_enable(struct sh_tmu_priv *p) { int ret; /* enable clock */ ret = clk_enable(p->clk); if (ret) { dev_err(&p->pdev->dev, "cannot enable clock\n"); return ret; } /* make sure channel is disabled */ sh_tmu_start_stop_ch(p, 0); /* maximum timeout */ sh_tmu_write(p, TCOR, 0xffffffff); sh_tmu_write(p, TCNT, 0xffffffff); /* configure channel to parent clock / 4, irq off */ p->rate = clk_get_rate(p->clk) / 4; sh_tmu_write(p, TCR, 0x0000); /* enable channel */ sh_tmu_start_stop_ch(p, 1); return 0; }
static int __sh_tmu_enable(struct sh_tmu_channel *ch) { int ret; /* enable clock */ ret = clk_enable(ch->tmu->clk); if (ret) { dev_err(&ch->tmu->pdev->dev, "ch%u: cannot enable clock\n", ch->index); return ret; } /* make sure channel is disabled */ sh_tmu_start_stop_ch(ch, 0); /* maximum timeout */ sh_tmu_write(ch, TCOR, 0xffffffff); sh_tmu_write(ch, TCNT, 0xffffffff); /* configure channel to parent clock / 4, irq off */ ch->rate = clk_get_rate(ch->tmu->clk) / 4; sh_tmu_write(ch, TCR, TCR_TPSC_CLK4); /* enable channel */ sh_tmu_start_stop_ch(ch, 1); return 0; }
static int sh_tmu_enable(struct sh_tmu_priv *p) { struct sh_timer_config *cfg = p->pdev->dev.platform_data; int ret; /* enable clock */ ret = clk_enable(p->clk); if (ret) { pr_err("sh_tmu: cannot enable clock \"%s\"\n", cfg->clk); return ret; } /* make sure channel is disabled */ sh_tmu_start_stop_ch(p, 0); /* maximum timeout */ sh_tmu_write(p, TCOR, 0xffffffff); sh_tmu_write(p, TCNT, 0xffffffff); /* configure channel to parent clock / 4, irq off */ p->rate = clk_get_rate(p->clk) / 4; sh_tmu_write(p, TCR, 0x0000); /* enable channel */ sh_tmu_start_stop_ch(p, 1); return 0; }
/* A Channel not used as clock source or for clock event (e.g. TMU2) * can be used as generic timer and registered by another device. */ struct sh_timer_callb *sh_timer_register(void *handler, void *data) { struct sh_tmu_priv *p = NULL; int ret, i; /* As first element, look at the channel 2 because usually not * used. */ char *c[] = { "sh_tmu.2", "sh_tmu.1", "sh_tmu.0" }; for (i = 0; i < ARRAY_SIZE(c); i++) { struct device *dev = bus_find_device_by_name(&platform_bus_type, NULL, c[i]); if (dev) { struct sh_timer_config *cfg = dev->platform_data; if (!cfg->clockevent_rating && !cfg->clocksource_rating) { p = dev_get_drvdata(dev); break; } } } /* Check if the channel has been not already registered as timer. */ if ((p == NULL) || (p->tm.fnc != NULL)) return NULL; if (handler == NULL) { pr_err("sh_tmu: invalid handler\n"); return NULL; } p->tm.fnc = kmalloc(sizeof(struct sh_timer_callb *), GFP_KERNEL); if (p->tm.fnc == NULL) return NULL; /* Prepare the new handler */ p->irqaction.handler = sh_timer_interrupt; p->tm.priv_handler = handler; p->tm.data = data; ret = setup_irq(p->irqaction.irq, &p->irqaction); if (ret) { pr_err("sh_tmu: failed to request irq %d\n", p->irqaction.irq); return NULL; } /* Channel is stopped and irq off */ sh_timer_stop(p); sh_tmu_write(p, TCOR, 0xffffffff); sh_tmu_write(p, TCNT, 0xffffffff); p->rate = clk_get_rate(p->clk) / 4; p->tm.fnc->tmu_priv = p; /* hook to invoke the timer callbacks */ p->tm.fnc->timer_start = sh_timer_start; p->tm.fnc->timer_stop = sh_timer_stop; p->tm.fnc->set_rate = sh_timer_set_rate; return p->tm.fnc; }
static irqreturn_t sh_tmu_interrupt(int irq, void *dev_id) { struct sh_tmu_channel *ch = dev_id; /* disable or acknowledge interrupt */ if (ch->ced.mode == CLOCK_EVT_MODE_ONESHOT) sh_tmu_write(ch, TCR, TCR_TPSC_CLK4); else sh_tmu_write(ch, TCR, TCR_UNIE | TCR_TPSC_CLK4); /* notify clockevent layer */ ch->ced.event_handler(&ch->ced); return IRQ_HANDLED; }
static irqreturn_t sh_tmu_interrupt(int irq, void *dev_id) { struct sh_tmu_priv *p = dev_id; /* disable or acknowledge interrupt */ if (p->ced.mode == CLOCK_EVT_MODE_ONESHOT) sh_tmu_write(p, TCR, 0x0000); else sh_tmu_write(p, TCR, 0x0020); /* notify clockevent layer */ p->ced.event_handler(&p->ced); return IRQ_HANDLED; }
static void sh_timer_stop(void *priv) { struct sh_tmu_priv *p = priv; sh_tmu_start_stop_ch(p, 0); sh_tmu_write(p, TCR, 0x0000); }
static irqreturn_t sh_timer_interrupt(int irq, void *dev_id) { struct sh_tmu_priv *p = dev_id; sh_tmu_write(p, TCR, 0x0020); p->tm.priv_handler(p->tm.data); return IRQ_HANDLED; }
static void __sh_tmu_disable(struct sh_tmu_channel *ch) { /* disable channel */ sh_tmu_start_stop_ch(ch, 0); /* disable interrupts in TMU block */ sh_tmu_write(ch, TCR, TCR_TPSC_CLK4); /* stop clock */ clk_disable(ch->tmu->clk); }
static void sh_tmu_disable(struct sh_tmu_priv *p) { /* disable channel */ sh_tmu_start_stop_ch(p, 0); /* disable interrupts in TMU block */ sh_tmu_write(p, TCR, 0x0000); /* stop clock */ clk_disable(p->clk); }
static void sh_tmu_set_next(struct sh_tmu_channel *ch, unsigned long delta, int periodic) { /* stop timer */ sh_tmu_start_stop_ch(ch, 0); /* acknowledge interrupt */ sh_tmu_read(ch, TCR); /* enable interrupt */ sh_tmu_write(ch, TCR, TCR_UNIE | TCR_TPSC_CLK4); /* reload delta value in case of periodic timer */ if (periodic) sh_tmu_write(ch, TCOR, delta); else sh_tmu_write(ch, TCOR, 0xffffffff); sh_tmu_write(ch, TCNT, delta); /* start timer */ sh_tmu_start_stop_ch(ch, 1); }
static void sh_tmu_set_next(struct sh_tmu_priv *p, unsigned long delta, int periodic) { /* stop timer */ sh_tmu_start_stop_ch(p, 0); /* acknowledge interrupt */ sh_tmu_read(p, TCR); /* enable interrupt */ sh_tmu_write(p, TCR, 0x0020); /* reload delta value in case of periodic timer */ if (periodic) sh_tmu_write(p, TCOR, delta); else sh_tmu_write(p, TCOR, 0xffffffff); sh_tmu_write(p, TCNT, delta); /* start timer */ sh_tmu_start_stop_ch(p, 1); }
static void sh_tmu_start_stop_ch(struct sh_tmu_channel *ch, int start) { unsigned long flags, value; /* start stop register shared by multiple timer channels */ raw_spin_lock_irqsave(&ch->tmu->lock, flags); value = sh_tmu_read(ch, TSTR); if (start) value |= 1 << ch->index; else value &= ~(1 << ch->index); sh_tmu_write(ch, TSTR, value); raw_spin_unlock_irqrestore(&ch->tmu->lock, flags); }
static void sh_tmu_start_stop_ch(struct sh_tmu_priv *p, int start) { struct sh_timer_config *cfg = p->pdev->dev.platform_data; unsigned long flags, value; /* start stop register shared by multiple timer channels */ spin_lock_irqsave(&sh_tmu_lock, flags); value = sh_tmu_read(p, TSTR); if (start) value |= 1 << cfg->timer_bit; else value &= ~(1 << cfg->timer_bit); sh_tmu_write(p, TSTR, value); spin_unlock_irqrestore(&sh_tmu_lock, flags); }