timer64.c 6.0 KB
Newer Older
A
Aurelien Jacquiot 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*
 *  Copyright (C) 2010, 2011 Texas Instruments Incorporated
 *  Contributed by: Mark Salter (msalter@redhat.com)
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2 as
 *  published by the Free Software Foundation.
 */

#include <linux/clockchips.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_address.h>
#include <asm/soc.h>
#include <asm/dscr.h>
18
#include <asm/special_insns.h>
A
Aurelien Jacquiot 已提交
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
#include <asm/timer64.h>

struct timer_regs {
	u32	reserved0;
	u32	emumgt;
	u32	reserved1;
	u32	reserved2;
	u32	cntlo;
	u32	cnthi;
	u32	prdlo;
	u32	prdhi;
	u32	tcr;
	u32	tgcr;
	u32	wdtcr;
};

static struct timer_regs __iomem *timer;

#define TCR_TSTATLO	     0x001
#define TCR_INVOUTPLO	     0x002
#define TCR_INVINPLO	     0x004
#define TCR_CPLO	     0x008
#define TCR_ENAMODELO_ONCE   0x040
#define TCR_ENAMODELO_CONT   0x080
#define TCR_ENAMODELO_MASK   0x0c0
#define TCR_PWIDLO_MASK      0x030
#define TCR_CLKSRCLO	     0x100
#define TCR_TIENLO	     0x200
#define TCR_TSTATHI	     (0x001 << 16)
#define TCR_INVOUTPHI	     (0x002 << 16)
#define TCR_CPHI	     (0x008 << 16)
#define TCR_PWIDHI_MASK      (0x030 << 16)
#define TCR_ENAMODEHI_ONCE   (0x040 << 16)
#define TCR_ENAMODEHI_CONT   (0x080 << 16)
#define TCR_ENAMODEHI_MASK   (0x0c0 << 16)

#define TGCR_TIMLORS	     0x001
#define TGCR_TIMHIRS	     0x002
#define TGCR_TIMMODE_UD32    0x004
#define TGCR_TIMMODE_WDT64   0x008
#define TGCR_TIMMODE_CD32    0x00c
#define TGCR_TIMMODE_MASK    0x00c
#define TGCR_PSCHI_MASK      (0x00f << 8)
#define TGCR_TDDRHI_MASK     (0x00f << 12)

/*
 * Timer clocks are divided down from the CPU clock
 * The divisor is in the EMUMGTCLKSPD register
 */
#define TIMER_DIVISOR \
	((soc_readl(&timer->emumgt) & (0xf << 16)) >> 16)

#define TIMER64_RATE (c6x_core_freq / TIMER_DIVISOR)

#define TIMER64_MODE_DISABLED 0
#define TIMER64_MODE_ONE_SHOT TCR_ENAMODELO_ONCE
#define TIMER64_MODE_PERIODIC TCR_ENAMODELO_CONT

static int timer64_mode;
static int timer64_devstate_id = -1;

static void timer64_config(unsigned long period)
{
	u32 tcr = soc_readl(&timer->tcr) & ~TCR_ENAMODELO_MASK;

	soc_writel(tcr, &timer->tcr);
	soc_writel(period - 1, &timer->prdlo);
	soc_writel(0, &timer->cntlo);
	tcr |= timer64_mode;
	soc_writel(tcr, &timer->tcr);
}

static void timer64_enable(void)
{
	u32 val;

	if (timer64_devstate_id >= 0)
		dscr_set_devstate(timer64_devstate_id, DSCR_DEVSTATE_ENABLED);

	/* disable timer, reset count */
	soc_writel(soc_readl(&timer->tcr) & ~TCR_ENAMODELO_MASK, &timer->tcr);
	soc_writel(0, &timer->prdlo);

	/* use internal clock and 1 cycle pulse width */
	val = soc_readl(&timer->tcr);
	soc_writel(val & ~(TCR_CLKSRCLO | TCR_PWIDLO_MASK), &timer->tcr);

	/* dual 32-bit unchained mode */
	val = soc_readl(&timer->tgcr) & ~TGCR_TIMMODE_MASK;
	soc_writel(val, &timer->tgcr);
	soc_writel(val | (TGCR_TIMLORS | TGCR_TIMMODE_UD32), &timer->tgcr);
}

static void timer64_disable(void)
{
	/* disable timer, reset count */
	soc_writel(soc_readl(&timer->tcr) & ~TCR_ENAMODELO_MASK, &timer->tcr);
	soc_writel(0, &timer->prdlo);

	if (timer64_devstate_id >= 0)
		dscr_set_devstate(timer64_devstate_id, DSCR_DEVSTATE_DISABLED);
}

static int next_event(unsigned long delta,
		      struct clock_event_device *evt)
{
	timer64_config(delta);
	return 0;
}

129
static int set_periodic(struct clock_event_device *evt)
A
Aurelien Jacquiot 已提交
130
{
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
	timer64_enable();
	timer64_mode = TIMER64_MODE_PERIODIC;
	timer64_config(TIMER64_RATE / HZ);
	return 0;
}

static int set_oneshot(struct clock_event_device *evt)
{
	timer64_enable();
	timer64_mode = TIMER64_MODE_ONE_SHOT;
	return 0;
}

static int shutdown(struct clock_event_device *evt)
{
	timer64_mode = TIMER64_MODE_DISABLED;
	timer64_disable();
	return 0;
A
Aurelien Jacquiot 已提交
149 150 151
}

static struct clock_event_device t64_clockevent_device = {
152 153 154 155 156 157 158 159
	.name			= "TIMER64_EVT32_TIMER",
	.features		= CLOCK_EVT_FEAT_ONESHOT |
				  CLOCK_EVT_FEAT_PERIODIC,
	.rating			= 200,
	.set_state_shutdown	= shutdown,
	.set_state_periodic	= set_periodic,
	.set_state_oneshot	= set_oneshot,
	.set_next_event		= next_event,
A
Aurelien Jacquiot 已提交
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
};

static irqreturn_t timer_interrupt(int irq, void *dev_id)
{
	struct clock_event_device *cd = &t64_clockevent_device;

	cd->event_handler(cd);

	return IRQ_HANDLED;
}

static struct irqaction timer_iact = {
	.name		= "timer",
	.flags		= IRQF_TIMER,
	.handler	= timer_interrupt,
	.dev_id		= &t64_clockevent_device,
};

void __init timer64_init(void)
{
	struct clock_event_device *cd = &t64_clockevent_device;
	struct device_node *np, *first = NULL;
	u32 val;
	int err, found = 0;

	for_each_compatible_node(np, NULL, "ti,c64x+timer64") {
		err = of_property_read_u32(np, "ti,core-mask", &val);
		if (!err) {
			if (val & (1 << get_coreid())) {
				found = 1;
				break;
			}
		} else if (!first)
			first = np;
	}
	if (!found) {
		/* try first one with no core-mask */
		if (first)
			np = of_node_get(first);
		else {
			pr_debug("Cannot find ti,c64x+timer64 timer.\n");
			return;
		}
	}

	timer = of_iomap(np, 0);
	if (!timer) {
207
		pr_debug("%pOF: Cannot map timer registers.\n", np);
A
Aurelien Jacquiot 已提交
208 209
		goto out;
	}
210
	pr_debug("%pOF: Timer registers=%p.\n", np, timer);
A
Aurelien Jacquiot 已提交
211 212 213

	cd->irq	= irq_of_parse_and_map(np, 0);
	if (cd->irq == NO_IRQ) {
214
		pr_debug("%pOF: Cannot find interrupt.\n", np);
A
Aurelien Jacquiot 已提交
215 216 217 218 219 220
		iounmap(timer);
		goto out;
	}

	/* If there is a device state control, save the ID. */
	err = of_property_read_u32(np, "ti,dscr-dev-enable", &val);
M
Mark Salter 已提交
221
	if (!err) {
A
Aurelien Jacquiot 已提交
222 223
		timer64_devstate_id = val;

M
Mark Salter 已提交
224 225 226 227 228 229 230 231
		/*
		 * It is necessary to enable the timer block here because
		 * the TIMER_DIVISOR macro needs to read a timer register
		 * to get the divisor.
		 */
		dscr_set_devstate(timer64_devstate_id, DSCR_DEVSTATE_ENABLED);
	}

232
	pr_debug("%pOF: Timer irq=%d.\n", np, cd->irq);
A
Aurelien Jacquiot 已提交
233 234 235 236

	clockevents_calc_mult_shift(cd, c6x_core_freq / TIMER_DIVISOR, 5);

	cd->max_delta_ns	= clockevent_delta2ns(0x7fffffff, cd);
237
	cd->max_delta_ticks	= 0x7fffffff;
A
Aurelien Jacquiot 已提交
238
	cd->min_delta_ns	= clockevent_delta2ns(250, cd);
239
	cd->min_delta_ticks	= 250;
A
Aurelien Jacquiot 已提交
240 241 242 243 244 245 246 247 248 249

	cd->cpumask		= cpumask_of(smp_processor_id());

	clockevents_register_device(cd);
	setup_irq(cd->irq, &timer_iact);

out:
	of_node_put(np);
	return;
}