Re: [PATCH v3 3/3] clocksource: Add clockevent support to NPS400 driver

From: Thomas Gleixner
Date: Mon Oct 31 2016 - 14:15:41 EST


On Mon, 31 Oct 2016, Noam Camus wrote:
>
> -static unsigned long nps_timer_rate;
> +static unsigned long nps_timer1_freq;

This should be either in the previous patch or seperate.

> static int nps_get_timer_clk(struct device_node *node,
> unsigned long *timer_freq,
> struct clk *clk)
> @@ -87,10 +87,10 @@ static int __init nps_setup_clocksource(struct device_node *node)
> nps_host_reg((cluster << NPS_CLUSTER_OFFSET),
> NPS_MSU_BLKID, NPS_MSU_TICK_LOW);
>
> - nps_get_timer_clk(node, &nps_timer_rate, clk);
> + nps_get_timer_clk(node, &nps_timer1_freq, clk);
>
> - ret = clocksource_mmio_init(nps_msu_reg_low_addr, "EZnps-tick",
> - nps_timer_rate, 301, 32, nps_clksrc_read);
> + ret = clocksource_mmio_init(nps_msu_reg_low_addr, "nps-tick",
> + nps_timer1_freq, 301, 32, nps_clksrc_read);
> if (ret) {
> pr_err("Couldn't register clock source.\n");
> clk_disable_unprepare(clk);
> @@ -101,3 +101,215 @@ static int __init nps_setup_clocksource(struct device_node *node)
>
> CLOCKSOURCE_OF_DECLARE(ezchip_nps400_clksrc, "ezchip,nps400-timer",
> nps_setup_clocksource);
> +CLOCKSOURCE_OF_DECLARE(ezchip_nps400_clk_src, "ezchip,nps400-timer1",
> + nps_setup_clocksource);

What's the point of this change?

> +/*
> + * Arm the timer to interrupt after @cycles
> + */
> +static void nps_clkevent_timer_event_setup(unsigned int cycles)
> +{
> + write_aux_reg(NPS_REG_TIMER0_LIMIT, cycles);
> + write_aux_reg(NPS_REG_TIMER0_CNT, 0); /* start from 0 */

Please do not use tail comments. They break the reading flow.

> +
> + write_aux_reg(NPS_REG_TIMER0_CTRL, TIMER0_CTRL_IE | TIMER0_CTRL_NH);
> +}
> +
> +static void nps_clkevent_rm_thread(bool remove_thread)

I have a hard time to understand why a remove_thread function needs a
remove_thread boolean argument. Commenting things like this would be more
helpful than commenting the obvious.

> +{
> + unsigned int cflags;
> + unsigned int enabled_threads;
> + unsigned long flags;
> + int thread;
> +
> + local_irq_save(flags);

That's pointless. Both call sites have interrupts disabled.

> + hw_schd_save(&cflags);
> +
> + enabled_threads = read_aux_reg(AUX_REG_TIMER0_TSI);
> +
> + /* remove thread from TSI1 */
> + if (remove_thread) {
> + thread = read_aux_reg(CTOP_AUX_THREAD_ID);
> + enabled_threads &= ~(1 << thread);
> + write_aux_reg(AUX_REG_TIMER0_TSI, enabled_threads);
> + }
> +
> + /* Re-arm the timer if needed */
> + if (!enabled_threads)
> + write_aux_reg(NPS_REG_TIMER0_CTRL, TIMER0_CTRL_NH);
> + else
> + write_aux_reg(NPS_REG_TIMER0_CTRL,
> + TIMER0_CTRL_IE | TIMER0_CTRL_NH);
> +
> + hw_schd_restore(cflags);
> + local_irq_restore(flags);
> +}
> +
> +static void nps_clkevent_add_thread(bool set_event)
> +{
> + int thread;
> + unsigned int cflags, enabled_threads;
> + unsigned long flags;
> +
> + local_irq_save(flags);

Ditto.

> + hw_schd_save(&cflags);
> +
> + /* add thread to TSI1 */
> + thread = read_aux_reg(CTOP_AUX_THREAD_ID);
> + enabled_threads = read_aux_reg(AUX_REG_TIMER0_TSI);
> + enabled_threads |= (1 << thread);
> + write_aux_reg(AUX_REG_TIMER0_TSI, enabled_threads);
> +
> + /* set next timer event */
> + if (set_event)
> + write_aux_reg(NPS_REG_TIMER0_CTRL,
> + TIMER0_CTRL_IE | TIMER0_CTRL_NH);
> +
> + hw_schd_restore(cflags);
> + local_irq_restore(flags);
> +}
> +
> +static int nps_clkevent_set_next_event(unsigned long delta,
> + struct clock_event_device *dev)
> +{
> + struct irq_desc *desc = irq_to_desc(nps_timer0_irq);
> + struct irq_chip *chip = irq_data_get_irq_chip(&desc->irq_data);
> +
> + nps_clkevent_add_thread(true);
> + chip->irq_unmask(&desc->irq_data);

Oh no. You are not supposed to fiddle in the guts of the interrupts from a
random driver. Can you please explain what you are trying to do and why the
existing interfaces are not sufficient?

> +/*
> + * Whenever anyone tries to change modes, we just mask interrupts
> + * and wait for the next event to get set.
> + */
> +static int nps_clkevent_timer_shutdown(struct clock_event_device *dev)
> +{
> + struct irq_desc *desc = irq_to_desc(nps_timer0_irq);
> + struct irq_chip *chip = irq_data_get_irq_chip(&desc->irq_data);
> +
> + chip->irq_mask(&desc->irq_data);
> +
> + return 0;
> +}
> +

> +static int nps_clkevent_set_periodic(struct clock_event_device *dev)
> +{
> + nps_clkevent_add_thread(false);
> + if (read_aux_reg(CTOP_AUX_THREAD_ID) == 0)
> + nps_clkevent_timer_event_setup(nps_timer0_freq / HZ);

So how is that enabling interrupts again?

> +
> + return 0;
> +}
> +

> +static DEFINE_PER_CPU(struct clock_event_device, nps_clockevent_device) = {
> + .name = "NPS Timer0",
> + .features = CLOCK_EVT_FEAT_ONESHOT |
> + CLOCK_EVT_FEAT_PERIODIC,
> + .rating = 300,
> + .set_next_event = nps_clkevent_set_next_event,
> + .set_state_periodic = nps_clkevent_set_periodic,
> + .set_state_oneshot = nps_clkevent_set_oneshot,
> + .set_state_oneshot_stopped = nps_clkevent_timer_shutdown,
> + .set_state_shutdown = nps_clkevent_timer_shutdown,
> + .tick_resume = nps_clkevent_timer_shutdown,
> +};
> +
> +static irqreturn_t timer_irq_handler(int irq, void *dev_id)
> +{
> + /*
> + * Note that generic IRQ core could have passed @evt for @dev_id if
> + * irq_set_chip_and_handler() asked for handle_percpu_devid_irq()

And why are you not doing that?

> + */
> + struct clock_event_device *evt = this_cpu_ptr(&nps_clockevent_device);
> + int irq_reenable = clockevent_state_periodic(evt);
> +
> + nps_clkevent_rm_thread(!irq_reenable);
> +
> + evt->event_handler(evt);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static int nps_timer_starting_cpu(unsigned int cpu)
> +{
> + struct clock_event_device *evt = this_cpu_ptr(&nps_clockevent_device);
> +
> + evt->cpumask = cpumask_of(smp_processor_id());
> +
> + clockevents_config_and_register(evt, nps_timer0_freq, 0, ULONG_MAX);
> + enable_percpu_irq(nps_timer0_irq, 0);

And at the same time you still use the percpu infrastructure ....

> + return 0;
> +}
> +
> +static int nps_timer_dying_cpu(unsigned int cpu)
> +{
> + disable_percpu_irq(nps_timer0_irq);
> + return 0;
> +}
> +
> +static int __init nps_setup_clockevent(struct device_node *node)
> +{
> + struct clock_event_device *evt = this_cpu_ptr(&nps_clockevent_device);
> + struct clk *clk;
> + int ret;
> +
> + nps_timer0_irq = irq_of_parse_and_map(node, 0);
> + if (nps_timer0_irq <= 0) {
> + pr_err("clockevent: missing irq");
> + return -EINVAL;
> + }
> +
> + nps_get_timer_clk(node, &nps_timer0_freq, clk);
> +
> + /* Needs apriori irq_set_percpu_devid() done in intc map function */
> + ret = request_percpu_irq(nps_timer0_irq, timer_irq_handler,
> + "Timer0 (per-cpu-tick)", evt);

This is wrong. the dev_id argument wants to be a __per_cpu pointer. So it
should be &nps_clockevent_device and not a pointer to the cpu local one.

> + if (ret) {
> + pr_err("Couldn't request irq\n");
> + clk_disable_unprepare(clk);
> + return ret;
> + }
> +
> + ret = cpuhp_setup_state(CPUHP_AP_NPS_TIMER_STARTING,
> + "AP_NPS_TIMER_STARTING",

Please make this "clockevents/nps:starting"

> + nps_timer_starting_cpu,
> + nps_timer_dying_cpu);
> + if (ret) {
> + pr_err("Failed to setup hotplug state");
> + clk_disable_unprepare(clk);

You leave the irq requested....

Thanks,

tglx