Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Linus Walleij | 1014 | 100.00% | 2 | 100.00% |
Total | 1014 | 2 |
// SPDX-License-Identifier: GPL-2.0 /* * IXP4 timer driver * Copyright (C) 2019 Linus Walleij <linus.walleij@linaro.org> * * Based on arch/arm/mach-ixp4xx/common.c * Copyright 2002 (C) Intel Corporation * Copyright 2003-2004 (C) MontaVista, Software, Inc. * Copyright (C) Deepak Saxena <dsaxena@plexity.net> */ #include <linux/interrupt.h> #include <linux/io.h> #include <linux/clockchips.h> #include <linux/clocksource.h> #include <linux/sched_clock.h> #include <linux/slab.h> #include <linux/bitops.h> #include <linux/delay.h> #include <linux/of_address.h> #include <linux/of_irq.h> /* Goes away with OF conversion */ #include <linux/platform_data/timer-ixp4xx.h> /* * Constants to make it easy to access Timer Control/Status registers */ #define IXP4XX_OSTS_OFFSET 0x00 /* Continuous Timestamp */ #define IXP4XX_OST1_OFFSET 0x04 /* Timer 1 Timestamp */ #define IXP4XX_OSRT1_OFFSET 0x08 /* Timer 1 Reload */ #define IXP4XX_OST2_OFFSET 0x0C /* Timer 2 Timestamp */ #define IXP4XX_OSRT2_OFFSET 0x10 /* Timer 2 Reload */ #define IXP4XX_OSWT_OFFSET 0x14 /* Watchdog Timer */ #define IXP4XX_OSWE_OFFSET 0x18 /* Watchdog Enable */ #define IXP4XX_OSWK_OFFSET 0x1C /* Watchdog Key */ #define IXP4XX_OSST_OFFSET 0x20 /* Timer Status */ /* * Timer register values and bit definitions */ #define IXP4XX_OST_ENABLE 0x00000001 #define IXP4XX_OST_ONE_SHOT 0x00000002 /* Low order bits of reload value ignored */ #define IXP4XX_OST_RELOAD_MASK 0x00000003 #define IXP4XX_OST_DISABLED 0x00000000 #define IXP4XX_OSST_TIMER_1_PEND 0x00000001 #define IXP4XX_OSST_TIMER_2_PEND 0x00000002 #define IXP4XX_OSST_TIMER_TS_PEND 0x00000004 #define IXP4XX_OSST_TIMER_WDOG_PEND 0x00000008 #define IXP4XX_OSST_TIMER_WARM_RESET 0x00000010 #define IXP4XX_WDT_KEY 0x0000482E #define IXP4XX_WDT_RESET_ENABLE 0x00000001 #define IXP4XX_WDT_IRQ_ENABLE 0x00000002 #define IXP4XX_WDT_COUNT_ENABLE 0x00000004 struct ixp4xx_timer { void __iomem *base; unsigned int tick_rate; u32 latch; struct clock_event_device clkevt; #ifdef CONFIG_ARM struct delay_timer delay_timer; #endif }; /* * A local singleton used by sched_clock and delay timer reads, which are * fast and stateless */ static struct ixp4xx_timer *local_ixp4xx_timer; static inline struct ixp4xx_timer * to_ixp4xx_timer(struct clock_event_device *evt) { return container_of(evt, struct ixp4xx_timer, clkevt); } static u64 notrace ixp4xx_read_sched_clock(void) { return __raw_readl(local_ixp4xx_timer->base + IXP4XX_OSTS_OFFSET); } static u64 ixp4xx_clocksource_read(struct clocksource *c) { return __raw_readl(local_ixp4xx_timer->base + IXP4XX_OSTS_OFFSET); } static irqreturn_t ixp4xx_timer_interrupt(int irq, void *dev_id) { struct ixp4xx_timer *tmr = dev_id; struct clock_event_device *evt = &tmr->clkevt; /* Clear Pending Interrupt */ __raw_writel(IXP4XX_OSST_TIMER_1_PEND, tmr->base + IXP4XX_OSST_OFFSET); evt->event_handler(evt); return IRQ_HANDLED; } static int ixp4xx_set_next_event(unsigned long cycles, struct clock_event_device *evt) { struct ixp4xx_timer *tmr = to_ixp4xx_timer(evt); u32 val; val = __raw_readl(tmr->base + IXP4XX_OSRT1_OFFSET); /* Keep enable/oneshot bits */ val &= IXP4XX_OST_RELOAD_MASK; __raw_writel((cycles & ~IXP4XX_OST_RELOAD_MASK) | val, tmr->base + IXP4XX_OSRT1_OFFSET); return 0; } static int ixp4xx_shutdown(struct clock_event_device *evt) { struct ixp4xx_timer *tmr = to_ixp4xx_timer(evt); u32 val; val = __raw_readl(tmr->base + IXP4XX_OSRT1_OFFSET); val &= ~IXP4XX_OST_ENABLE; __raw_writel(val, tmr->base + IXP4XX_OSRT1_OFFSET); return 0; } static int ixp4xx_set_oneshot(struct clock_event_device *evt) { struct ixp4xx_timer *tmr = to_ixp4xx_timer(evt); __raw_writel(IXP4XX_OST_ENABLE | IXP4XX_OST_ONE_SHOT, tmr->base + IXP4XX_OSRT1_OFFSET); return 0; } static int ixp4xx_set_periodic(struct clock_event_device *evt) { struct ixp4xx_timer *tmr = to_ixp4xx_timer(evt); u32 val; val = tmr->latch & ~IXP4XX_OST_RELOAD_MASK; val |= IXP4XX_OST_ENABLE; __raw_writel(val, tmr->base + IXP4XX_OSRT1_OFFSET); return 0; } static int ixp4xx_resume(struct clock_event_device *evt) { struct ixp4xx_timer *tmr = to_ixp4xx_timer(evt); u32 val; val = __raw_readl(tmr->base + IXP4XX_OSRT1_OFFSET); val |= IXP4XX_OST_ENABLE; __raw_writel(val, tmr->base + IXP4XX_OSRT1_OFFSET); return 0; } /* * IXP4xx timer tick * We use OS timer1 on the CPU for the timer tick and the timestamp * counter as a source of real clock ticks to account for missed jiffies. */ static __init int ixp4xx_timer_register(void __iomem *base, int timer_irq, unsigned int timer_freq) { struct ixp4xx_timer *tmr; int ret; tmr = kzalloc(sizeof(*tmr), GFP_KERNEL); if (!tmr) return -ENOMEM; tmr->base = base; tmr->tick_rate = timer_freq; /* * The timer register doesn't allow to specify the two least * significant bits of the timeout value and assumes them being zero. * So make sure the latch is the best value with the two least * significant bits unset. */ tmr->latch = DIV_ROUND_CLOSEST(timer_freq, (IXP4XX_OST_RELOAD_MASK + 1) * HZ) * (IXP4XX_OST_RELOAD_MASK + 1); local_ixp4xx_timer = tmr; /* Reset/disable counter */ __raw_writel(0, tmr->base + IXP4XX_OSRT1_OFFSET); /* Clear any pending interrupt on timer 1 */ __raw_writel(IXP4XX_OSST_TIMER_1_PEND, tmr->base + IXP4XX_OSST_OFFSET); /* Reset time-stamp counter */ __raw_writel(0, tmr->base + IXP4XX_OSTS_OFFSET); clocksource_mmio_init(NULL, "OSTS", timer_freq, 200, 32, ixp4xx_clocksource_read); tmr->clkevt.name = "ixp4xx timer1"; tmr->clkevt.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT; tmr->clkevt.rating = 200; tmr->clkevt.set_state_shutdown = ixp4xx_shutdown; tmr->clkevt.set_state_periodic = ixp4xx_set_periodic; tmr->clkevt.set_state_oneshot = ixp4xx_set_oneshot; tmr->clkevt.tick_resume = ixp4xx_resume; tmr->clkevt.set_next_event = ixp4xx_set_next_event; tmr->clkevt.cpumask = cpumask_of(0); tmr->clkevt.irq = timer_irq; ret = request_irq(timer_irq, ixp4xx_timer_interrupt, IRQF_TIMER, "IXP4XX-TIMER1", tmr); if (ret) { pr_crit("no timer IRQ\n"); return -ENODEV; } clockevents_config_and_register(&tmr->clkevt, timer_freq, 0xf, 0xfffffffe); sched_clock_register(ixp4xx_read_sched_clock, 32, timer_freq); return 0; } /** * ixp4xx_timer_setup() - Timer setup function to be called from boardfiles * @timerbase: physical base of timer block * @timer_irq: Linux IRQ number for the timer * @timer_freq: Fixed frequency of the timer */ void __init ixp4xx_timer_setup(resource_size_t timerbase, int timer_irq, unsigned int timer_freq) { void __iomem *base; base = ioremap(timerbase, 0x100); if (!base) { pr_crit("IXP4xx: can't remap timer\n"); return; } ixp4xx_timer_register(base, timer_irq, timer_freq); } EXPORT_SYMBOL_GPL(ixp4xx_timer_setup); #ifdef CONFIG_OF static __init int ixp4xx_of_timer_init(struct device_node *np) { void __iomem *base; int irq; int ret; base = of_iomap(np, 0); if (!base) { pr_crit("IXP4xx: can't remap timer\n"); return -ENODEV; } irq = irq_of_parse_and_map(np, 0); if (irq <= 0) { pr_err("Can't parse IRQ\n"); ret = -EINVAL; goto out_unmap; } /* TODO: get some fixed clocks into the device tree */ ret = ixp4xx_timer_register(base, irq, 66666000); if (ret) goto out_unmap; return 0; out_unmap: iounmap(base); return ret; } TIMER_OF_DECLARE(ixp4xx, "intel,ixp4xx-timer", ixp4xx_of_timer_init); #endif
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1