timer-stm32.c 4.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 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 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 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
/*
 * Copyright (C) Maxime Coquelin 2015
 * Author:  Maxime Coquelin <mcoquelin.stm32@gmail.com>
 * License terms:  GNU General Public License (GPL), version 2
 *
 * Inspired by time-efm32.c from Uwe Kleine-Koenig
 */

#include <linux/kernel.h>
#include <linux/clocksource.h>
#include <linux/clockchips.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/clk.h>
#include <linux/reset.h>

#define TIM_CR1		0x00
#define TIM_DIER	0x0c
#define TIM_SR		0x10
#define TIM_EGR		0x14
#define TIM_PSC		0x28
#define TIM_ARR		0x2c

#define TIM_CR1_CEN	BIT(0)
#define TIM_CR1_OPM	BIT(3)
#define TIM_CR1_ARPE	BIT(7)

#define TIM_DIER_UIE	BIT(0)

#define TIM_SR_UIF	BIT(0)

#define TIM_EGR_UG	BIT(0)

struct stm32_clock_event_ddata {
	struct clock_event_device evtdev;
	unsigned periodic_top;
	void __iomem *base;
};

static void stm32_clock_event_set_mode(enum clock_event_mode mode,
				       struct clock_event_device *evtdev)
{
	struct stm32_clock_event_ddata *data =
		container_of(evtdev, struct stm32_clock_event_ddata, evtdev);
	void *base = data->base;

	switch (mode) {
	case CLOCK_EVT_MODE_PERIODIC:
		writel_relaxed(data->periodic_top, base + TIM_ARR);
		writel_relaxed(TIM_CR1_ARPE | TIM_CR1_CEN, base + TIM_CR1);
		break;

	case CLOCK_EVT_MODE_ONESHOT:
	default:
		writel_relaxed(0, base + TIM_CR1);
		break;
	}
}

static int stm32_clock_event_set_next_event(unsigned long evt,
					    struct clock_event_device *evtdev)
{
	struct stm32_clock_event_ddata *data =
		container_of(evtdev, struct stm32_clock_event_ddata, evtdev);

	writel_relaxed(evt, data->base + TIM_ARR);
	writel_relaxed(TIM_CR1_ARPE | TIM_CR1_OPM | TIM_CR1_CEN,
		       data->base + TIM_CR1);

	return 0;
}

static irqreturn_t stm32_clock_event_handler(int irq, void *dev_id)
{
	struct stm32_clock_event_ddata *data = dev_id;

	writel_relaxed(0, data->base + TIM_SR);

	data->evtdev.event_handler(&data->evtdev);

	return IRQ_HANDLED;
}

static struct stm32_clock_event_ddata clock_event_ddata = {
	.evtdev = {
		.name = "stm32 clockevent",
		.features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC,
		.set_mode = stm32_clock_event_set_mode,
		.set_next_event = stm32_clock_event_set_next_event,
		.rating = 200,
	},
};

static void __init stm32_clockevent_init(struct device_node *np)
{
	struct stm32_clock_event_ddata *data = &clock_event_ddata;
	struct clk *clk;
	struct reset_control *rstc;
	unsigned long rate, max_delta;
	int irq, ret, bits, prescaler = 1;

	clk = of_clk_get(np, 0);
	if (IS_ERR(clk)) {
		ret = PTR_ERR(clk);
		pr_err("failed to get clock for clockevent (%d)\n", ret);
		goto err_clk_get;
	}

	ret = clk_prepare_enable(clk);
	if (ret) {
		pr_err("failed to enable timer clock for clockevent (%d)\n",
		       ret);
		goto err_clk_enable;
	}

	rate = clk_get_rate(clk);

	rstc = of_reset_control_get(np, NULL);
	if (!IS_ERR(rstc)) {
		reset_control_assert(rstc);
		reset_control_deassert(rstc);
	}

	data->base = of_iomap(np, 0);
	if (!data->base) {
		pr_err("failed to map registers for clockevent\n");
		goto err_iomap;
	}

	irq = irq_of_parse_and_map(np, 0);
	if (!irq) {
		pr_err("%s: failed to get irq.\n", np->full_name);
		goto err_get_irq;
	}

	/* Detect whether the timer is 16 or 32 bits */
	writel_relaxed(~0UL, data->base + TIM_ARR);
	max_delta = readl_relaxed(data->base + TIM_ARR);
	if (max_delta == ~0UL) {
		prescaler = 1;
		bits = 32;
	} else {
		prescaler = 1024;
		bits = 16;
	}
	writel_relaxed(0, data->base + TIM_ARR);

	writel_relaxed(prescaler - 1, data->base + TIM_PSC);
	writel_relaxed(TIM_EGR_UG, data->base + TIM_EGR);
	writel_relaxed(TIM_DIER_UIE, data->base + TIM_DIER);
	writel_relaxed(0, data->base + TIM_SR);

	data->periodic_top = DIV_ROUND_CLOSEST(rate, prescaler * HZ);

	clockevents_config_and_register(&data->evtdev,
					DIV_ROUND_CLOSEST(rate, prescaler),
					0x1, max_delta);

	ret = request_irq(irq, stm32_clock_event_handler, IRQF_TIMER,
			"stm32 clockevent", data);
	if (ret) {
		pr_err("%s: failed to request irq.\n", np->full_name);
		goto err_get_irq;
	}

	pr_info("%s: STM32 clockevent driver initialized (%d bits)\n",
			np->full_name, bits);

	return;

err_get_irq:
	iounmap(data->base);
err_iomap:
	clk_disable_unprepare(clk);
err_clk_enable:
	clk_put(clk);
err_clk_get:
	return;
}

CLOCKSOURCE_OF_DECLARE(stm32, "st,stm32-timer", stm32_clockevent_init);