Linux Audio

Check our new training course

Yocto / OpenEmbedded training

Mar 24-27, 2025, special US time zones
Register
Loading...
Note: File does not exist in v6.13.7.
  1/*
  2 * TCC8000 system timer setup
  3 *
  4 * (C) 2009 Hans J. Koch <hjk@linutronix.de>
  5 *
  6 * Licensed under the terms of the GPL version 2.
  7 *
  8 */
  9
 10#include <linux/clk.h>
 11#include <linux/clockchips.h>
 12#include <linux/init.h>
 13#include <linux/interrupt.h>
 14#include <linux/io.h>
 15#include <linux/irq.h>
 16#include <linux/kernel.h>
 17#include <linux/spinlock.h>
 18
 19#include <asm/mach/time.h>
 20
 21#include <mach/tcc8k-regs.h>
 22#include <mach/irqs.h>
 23
 24#include "common.h"
 25
 26static void __iomem *timer_base;
 27
 28static int tcc_set_next_event(unsigned long evt,
 29			      struct clock_event_device *unused)
 30{
 31	unsigned long reg = __raw_readl(timer_base + TC32MCNT_OFFS);
 32
 33	__raw_writel(reg + evt, timer_base + TC32CMP0_OFFS);
 34	return 0;
 35}
 36
 37static void tcc_set_mode(enum clock_event_mode mode,
 38				struct clock_event_device *evt)
 39{
 40	unsigned long tc32irq;
 41
 42	switch (mode) {
 43	case CLOCK_EVT_MODE_ONESHOT:
 44		tc32irq = __raw_readl(timer_base + TC32IRQ_OFFS);
 45		tc32irq |= TC32IRQ_IRQEN0;
 46		__raw_writel(tc32irq, timer_base + TC32IRQ_OFFS);
 47		break;
 48	case CLOCK_EVT_MODE_SHUTDOWN:
 49	case CLOCK_EVT_MODE_UNUSED:
 50		tc32irq = __raw_readl(timer_base + TC32IRQ_OFFS);
 51		tc32irq &= ~TC32IRQ_IRQEN0;
 52		__raw_writel(tc32irq, timer_base + TC32IRQ_OFFS);
 53		break;
 54	case CLOCK_EVT_MODE_PERIODIC:
 55	case CLOCK_EVT_MODE_RESUME:
 56		break;
 57	}
 58}
 59
 60static irqreturn_t tcc8k_timer_interrupt(int irq, void *dev_id)
 61{
 62	struct clock_event_device *evt = dev_id;
 63
 64	/* Acknowledge TC32 interrupt by reading TC32IRQ */
 65	__raw_readl(timer_base + TC32IRQ_OFFS);
 66
 67	evt->event_handler(evt);
 68
 69	return IRQ_HANDLED;
 70}
 71
 72static struct clock_event_device clockevent_tcc = {
 73	.name		= "tcc_timer1",
 74	.features	= CLOCK_EVT_FEAT_ONESHOT,
 75	.shift		= 32,
 76	.set_mode	= tcc_set_mode,
 77	.set_next_event	= tcc_set_next_event,
 78	.rating		= 200,
 79};
 80
 81static struct irqaction tcc8k_timer_irq = {
 82	.name		= "TC32_timer",
 83	.flags		= IRQF_DISABLED | IRQF_TIMER,
 84	.handler	= tcc8k_timer_interrupt,
 85	.dev_id		= &clockevent_tcc,
 86};
 87
 88static int __init tcc_clockevent_init(struct clk *clock)
 89{
 90	unsigned int c = clk_get_rate(clock);
 91
 92	clocksource_mmio_init(timer_base + TC32MCNT_OFFS, "tcc_tc32", c,
 93		200, 32, clocksource_mmio_readl_up);
 94
 95	clockevent_tcc.mult = div_sc(c, NSEC_PER_SEC,
 96					clockevent_tcc.shift);
 97	clockevent_tcc.max_delta_ns =
 98			clockevent_delta2ns(0xfffffffe, &clockevent_tcc);
 99	clockevent_tcc.min_delta_ns =
100			clockevent_delta2ns(0xff, &clockevent_tcc);
101
102	clockevent_tcc.cpumask = cpumask_of(0);
103
104	clockevents_register_device(&clockevent_tcc);
105
106	return 0;
107}
108
109void __init tcc8k_timer_init(struct clk *clock, void __iomem *base, int irq)
110{
111	u32 reg;
112
113	timer_base = base;
114	tcc8k_timer_irq.irq = irq;
115
116	/* Enable clocks */
117	clk_enable(clock);
118
119	/* Initialize 32-bit timer */
120	reg = __raw_readl(timer_base + TC32EN_OFFS);
121	reg &= ~TC32EN_ENABLE; /* Disable timer */
122	__raw_writel(reg, timer_base + TC32EN_OFFS);
123	/* Free running timer, counting from 0 to 0xffffffff */
124	__raw_writel(0, timer_base + TC32EN_OFFS);
125	__raw_writel(0, timer_base + TC32LDV_OFFS);
126	reg = __raw_readl(timer_base + TC32IRQ_OFFS);
127	reg |= TC32IRQ_IRQEN0; /* irq at match with CMP0 */
128	__raw_writel(reg, timer_base + TC32IRQ_OFFS);
129
130	__raw_writel(TC32EN_ENABLE, timer_base + TC32EN_OFFS);
131
132	tcc_clockevent_init(clock);
133	setup_irq(irq, &tcc8k_timer_irq);
134}