cregit-Linux how code gets into the kernel

Release 4.7 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 victorandrew victor3255.17%133.33%
alexandre bellonialexandre belloni1322.41%133.33%
david brownelldavid brownell1322.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 victorandrew victor4743.52%342.86%
david brownelldavid brownell4037.04%114.29%
alexandre bellonialexandre belloni1312.04%228.57%
uwe kleine-koeniguwe kleine-koenig87.41%114.29%
Total108100.00%7100.00%


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

Contributors

PersonTokensPropCommitsCommitProp
david brownelldavid brownell853.33%133.33%
magnus dammmagnus damm426.67%133.33%
andrew victorandrew victor320.00%133.33%
Total15100.00%3100.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 brownelldavid brownell2153.85%125.00%
alexandre bellonialexandre belloni1333.33%125.00%
viresh kumarviresh kumar37.69%125.00%
andrew victorandrew victor25.13%125.00%
Total39100.00%4100.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 kumarviresh kumar1963.33%133.33%
david brownelldavid brownell826.67%133.33%
alexandre bellonialexandre 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 kumarviresh kumar2460.00%125.00%
david brownelldavid brownell1230.00%125.00%
alexandre bellonialexandre belloni37.50%125.00%
andrew victorandrew victor12.50%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 kumarviresh kumar2972.50%120.00%
david brownelldavid brownell512.50%120.00%
alexandre bellonialexandre belloni410.00%240.00%
andrew victorandrew victor25.00%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 brownelldavid brownell4862.34%125.00%
alexandre bellonialexandre belloni1620.78%125.00%
andrew victorandrew victor1316.88%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 void __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)) panic(pr_fmt("Unable to get regmap\n")); /* 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) panic(pr_fmt("Unable to get IRQ from DT\n")); /* 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) panic(pr_fmt("Unable to setup IRQ\n")); sclk = of_clk_get(node, 0); if (IS_ERR(sclk)) panic(pr_fmt("Unable to get slow clock\n")); clk_prepare_enable(sclk); if (ret) panic(pr_fmt("Could not enable slow clock\n")); sclk_rate = clk_get_rate(sclk); if (!sclk_rate) panic(pr_fmt("Invalid slow clock rate\n")); 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 */ clocksource_register_hz(&clk32k, sclk_rate); }

Contributors

PersonTokensPropCommitsCommitProp
alexandre bellonialexandre belloni16366.53%330.00%
david brownelldavid brownell2610.61%110.00%
andrew victorandrew victor249.80%220.00%
joachim eastwoodjoachim eastwood239.39%110.00%
uwe kleine-koeniguwe kleine-koenig62.45%110.00%
russell kingrussell king20.82%110.00%
rusty russellrusty russell10.41%110.00%
Total245100.00%10100.00%

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

Overall Contributors

PersonTokensPropCommitsCommitProp
david brownelldavid brownell26032.38%15.00%
alexandre bellonialexandre belloni25431.63%420.00%
andrew victorandrew victor14017.43%630.00%
viresh kumarviresh kumar9211.46%15.00%
joachim eastwoodjoachim eastwood334.11%210.00%
uwe kleine-koeniguwe kleine-koenig141.74%210.00%
magnus dammmagnus damm40.50%15.00%
thomas gleixnerthomas gleixner30.37%15.00%
russell kingrussell king20.25%15.00%
rusty russellrusty russell10.12%15.00%
Total803100.00%20100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
{% endraw %}