cregit-Linux how code gets into the kernel

Release 4.14 drivers/clocksource/timer-atmel-st.c

/*
 * linux/arch/arm/mach-at91/at91rm9200_time.c
 *
 *  Copyright (C) 2003 SAN People
 *  Copyright (C) 2003 ATMEL
 *
 * 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.
 *
 * This program is distributed in the hope that it will be useful,
 * 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 <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/clk.h>
#include <linux/clockchips.h>
#include <linux/export.h>
#include <linux/mfd/syscon.h>
#include <linux/mfd/syscon/atmel-st.h>
#include <linux/of_irq.h>
#include <linux/regmap.h>


static unsigned long last_crtr;

static u32 irqmask;

static struct clock_event_device clkevt;

static struct regmap *regmap_st;

static int timer_latch;

/*
 * The ST_CRTR is updated asynchronously to the master clock ... but
 * the updates as seen by the CPU don't seem to be strictly monotonic.
 * Waiting until we read the same value twice avoids glitching.
 */

static inline unsigned long read_CRTR(void) { unsigned int x1, x2; regmap_read(regmap_st, AT91_ST_CRTR, &x1); do { regmap_read(regmap_st, AT91_ST_CRTR, &x2); if (x1 == x2) break; x1 = x2; } while (1); return x1; }

Contributors

PersonTokensPropCommitsCommitProp
Andrew Victor3255.17%133.33%
David Brownell1322.41%133.33%
Alexandre Belloni1322.41%133.33%
Total58100.00%3100.00%

/* * IRQ handler for the timer. */
static irqreturn_t at91rm9200_timer_interrupt(int irq, void *dev_id) { u32 sr; regmap_read(regmap_st, AT91_ST_SR, &sr); sr &= irqmask; /* * irqs should be disabled here, but as the irq is shared they are only * guaranteed to be off if the timer irq is registered first. */ WARN_ON_ONCE(!irqs_disabled()); /* simulate "oneshot" timer with alarm */ if (sr & AT91_ST_ALMS) { clkevt.event_handler(&clkevt); return IRQ_HANDLED; } /* periodic mode should handle delayed ticks */ if (sr & AT91_ST_PITS) { u32 crtr = read_CRTR(); while (((crtr - last_crtr) & AT91_ST_CRTV) >= timer_latch) { last_crtr += timer_latch; clkevt.event_handler(&clkevt); } return IRQ_HANDLED; } /* this irq is shared ... */ return IRQ_NONE; }

Contributors

PersonTokensPropCommitsCommitProp
Andrew Victor4743.52%342.86%
David Brownell4037.04%114.29%
Alexandre Belloni1312.04%228.57%
Uwe Kleine-König87.41%114.29%
Total108100.00%7100.00%


static u64 read_clk32k(struct clocksource *cs) { return read_CRTR(); }

Contributors

PersonTokensPropCommitsCommitProp
David Brownell746.67%125.00%
Magnus Damm426.67%125.00%
Andrew Victor320.00%125.00%
Thomas Gleixner16.67%125.00%
Total15100.00%4100.00%

static struct clocksource clk32k = { .name = "32k_counter", .rating = 150, .read = read_clk32k, .mask = CLOCKSOURCE_MASK(20), .flags = CLOCK_SOURCE_IS_CONTINUOUS, };
static void clkdev32k_disable_and_flush_irq(void) { unsigned int val; /* Disable and flush pending timer interrupts */ regmap_write(regmap_st, AT91_ST_IDR, AT91_ST_PITS | AT91_ST_ALMS); regmap_read(regmap_st, AT91_ST_SR, &val); last_crtr = read_CRTR(); }

Contributors

PersonTokensPropCommitsCommitProp
David Brownell2358.97%133.33%
Alexandre Belloni1333.33%133.33%
Viresh Kumar37.69%133.33%
Total39100.00%3100.00%


static int clkevt32k_shutdown(struct clock_event_device *evt) { clkdev32k_disable_and_flush_irq(); irqmask = 0; regmap_write(regmap_st, AT91_ST_IER, irqmask); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Viresh Kumar1963.33%133.33%
David Brownell826.67%133.33%
Alexandre Belloni310.00%133.33%
Total30100.00%3100.00%


static int clkevt32k_set_oneshot(struct clock_event_device *dev) { clkdev32k_disable_and_flush_irq(); /* * ALM for oneshot irqs, set by next_event() * before 32 seconds have passed. */ irqmask = AT91_ST_ALMS; regmap_write(regmap_st, AT91_ST_RTAR, last_crtr); regmap_write(regmap_st, AT91_ST_IER, irqmask); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Viresh Kumar2460.00%125.00%
David Brownell1127.50%125.00%
Alexandre Belloni37.50%125.00%
Andrew Victor25.00%125.00%
Total40100.00%4100.00%


static int clkevt32k_set_periodic(struct clock_event_device *dev) { clkdev32k_disable_and_flush_irq(); /* PIT for periodic irqs; fixed rate of 1/HZ */ irqmask = AT91_ST_PITS; regmap_write(regmap_st, AT91_ST_PIMR, timer_latch); regmap_write(regmap_st, AT91_ST_IER, irqmask); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Viresh Kumar2972.50%120.00%
Alexandre Belloni410.00%240.00%
Andrew Victor410.00%120.00%
David Brownell37.50%120.00%
Total40100.00%5100.00%


static int clkevt32k_next_event(unsigned long delta, struct clock_event_device *dev) { u32 alm; int status = 0; unsigned int val; BUG_ON(delta < 2); /* The alarm IRQ uses absolute time (now+delta), not the relative * time (delta) in our calling convention. Like all clockevents * using such "match" hardware, we have a race to defend against. * * Our defense here is to have set up the clockevent device so the * delta is at least two. That way we never end up writing RTAR * with the value then held in CRTR ... which would mean the match * wouldn't trigger until 32 seconds later, after CRTR wraps. */ alm = read_CRTR(); /* Cancel any pending alarm; flush any pending IRQ */ regmap_write(regmap_st, AT91_ST_RTAR, alm); regmap_read(regmap_st, AT91_ST_SR, &val); /* Schedule alarm by writing RTAR. */ alm += delta; regmap_write(regmap_st, AT91_ST_RTAR, alm); return status; }

Contributors

PersonTokensPropCommitsCommitProp
David Brownell5064.94%125.00%
Alexandre Belloni1620.78%125.00%
Andrew Victor1114.29%250.00%
Total77100.00%4100.00%

static struct clock_event_device clkevt = { .name = "at91_tick", .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, .rating = 150, .set_next_event = clkevt32k_next_event, .set_state_shutdown = clkevt32k_shutdown, .set_state_periodic = clkevt32k_set_periodic, .set_state_oneshot = clkevt32k_set_oneshot, .tick_resume = clkevt32k_shutdown, }; /* * ST (system timer) module supports both clockevents and clocksource. */
static int __init atmel_st_timer_init(struct device_node *node) { struct clk *sclk; unsigned int sclk_rate, val; int irq, ret; regmap_st = syscon_node_to_regmap(node); if (IS_ERR(regmap_st)) { pr_err("Unable to get regmap\n"); return PTR_ERR(regmap_st); } /* Disable all timer interrupts, and clear any pending ones */ regmap_write(regmap_st, AT91_ST_IDR, AT91_ST_PITS | AT91_ST_WDOVF | AT91_ST_RTTINC | AT91_ST_ALMS); regmap_read(regmap_st, AT91_ST_SR, &val); /* Get the interrupts property */ irq = irq_of_parse_and_map(node, 0); if (!irq) { pr_err("Unable to get IRQ from DT\n"); return -EINVAL; } /* Make IRQs happen for the system timer */ ret = request_irq(irq, at91rm9200_timer_interrupt, IRQF_SHARED | IRQF_TIMER | IRQF_IRQPOLL, "at91_tick", regmap_st); if (ret) { pr_err("Unable to setup IRQ\n"); return ret; } sclk = of_clk_get(node, 0); if (IS_ERR(sclk)) { pr_err("Unable to get slow clock\n"); return PTR_ERR(sclk); } ret = clk_prepare_enable(sclk); if (ret) { pr_err("Could not enable slow clock\n"); return ret; } sclk_rate = clk_get_rate(sclk); if (!sclk_rate) { pr_err("Invalid slow clock rate\n"); return -EINVAL; } timer_latch = (sclk_rate + HZ / 2) / HZ; /* The 32KiHz "Slow Clock" (tick every 30517.58 nanoseconds) is used * directly for the clocksource and all clockevents, after adjusting * its prescaler from the 1 Hz default. */ regmap_write(regmap_st, AT91_ST_RTMR, 1); /* Setup timer clockevent, with minimum of two ticks (important!!) */ clkevt.cpumask = cpumask_of(0); clockevents_config_and_register(&clkevt, sclk_rate, 2, AT91_ST_ALMV); /* register clocksource */ return clocksource_register_hz(&clk32k, sclk_rate); }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Belloni14252.99%327.27%
Daniel Lezcano4918.28%19.09%
David Brownell269.70%19.09%
Joachim Eastwood217.84%19.09%
Andrew Victor217.84%218.18%
Uwe Kleine-König62.24%19.09%
Russell King20.75%19.09%
Rusty Russell10.37%19.09%
Total268100.00%11100.00%

TIMER_OF_DECLARE(atmel_st_timer, "atmel,at91rm9200-st", atmel_st_timer_init);

Overall Contributors

PersonTokensPropCommitsCommitProp
David Brownell26031.48%14.35%
Alexandre Belloni23228.09%417.39%
Andrew Victor13816.71%626.09%
Viresh Kumar9211.14%14.35%
Daniel Lezcano506.05%28.70%
Joachim Eastwood313.75%28.70%
Uwe Kleine-König141.69%28.70%
Magnus Damm40.48%14.35%
Russell King20.24%14.35%
Thomas Gleixner20.24%28.70%
Rusty Russell10.12%14.35%
Total826100.00%23100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.