Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ARM: meson6: clocksource: Add Meson6 timer support

Meson6 SoCs are equipped with 5 32-bit timers, called TIMER_A, TIMER_B,
TIMER_C, TIMER_D and TIMER_E.

The driver is providing clocksource support for the 32-bit counter using
TIMER_E. Clockevents are also supported using TIMER_A.

Acked-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Carlo Caione <carlo@caione.org>
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Reviewed-by: Matthias Brugger <matthias.bgg@gmail.com>

authored by

Carlo Caione and committed by
Daniel Lezcano
e4a6b378 66b2e373

+171
+3
drivers/clocksource/Kconfig
··· 30 30 bool 31 31 select CLKSRC_OF 32 32 33 + config MESON6_TIMER 34 + bool 35 + 33 36 config ORION_TIMER 34 37 select CLKSRC_OF 35 38 select CLKSRC_MMIO
+1
drivers/clocksource/Makefile
··· 25 25 obj-$(CONFIG_ARCH_U300) += timer-u300.o 26 26 obj-$(CONFIG_SUN4I_TIMER) += sun4i_timer.o 27 27 obj-$(CONFIG_SUN5I_HSTIMER) += timer-sun5i.o 28 + obj-$(CONFIG_MESON6_TIMER) += meson6_timer.o 28 29 obj-$(CONFIG_ARCH_TEGRA) += tegra20_timer.o 29 30 obj-$(CONFIG_VT8500_TIMER) += vt8500_timer.o 30 31 obj-$(CONFIG_ARCH_NSPIRE) += zevio-timer.o
+167
drivers/clocksource/meson6_timer.c
··· 1 + /* 2 + * Amlogic Meson6 SoCs timer handling. 3 + * 4 + * Copyright (C) 2014 Carlo Caione <carlo@caione.org> 5 + * 6 + * Based on code from Amlogic, Inc 7 + * 8 + * This file is licensed under the terms of the GNU General Public 9 + * License version 2. This program is licensed "as is" without any 10 + * warranty of any kind, whether express or implied. 11 + */ 12 + 13 + #include <linux/clk.h> 14 + #include <linux/clockchips.h> 15 + #include <linux/interrupt.h> 16 + #include <linux/irq.h> 17 + #include <linux/irqreturn.h> 18 + #include <linux/sched_clock.h> 19 + #include <linux/of.h> 20 + #include <linux/of_address.h> 21 + #include <linux/of_irq.h> 22 + 23 + #define CED_ID 0 24 + #define CSD_ID 4 25 + 26 + #define TIMER_ISA_MUX 0 27 + #define TIMER_ISA_VAL(t) (((t) + 1) << 2) 28 + 29 + #define TIMER_INPUT_BIT(t) (2 * (t)) 30 + #define TIMER_ENABLE_BIT(t) (16 + (t)) 31 + #define TIMER_PERIODIC_BIT(t) (12 + (t)) 32 + 33 + #define TIMER_CED_INPUT_MASK (3UL << TIMER_INPUT_BIT(CED_ID)) 34 + #define TIMER_CSD_INPUT_MASK (7UL << TIMER_INPUT_BIT(CSD_ID)) 35 + 36 + #define TIMER_CED_UNIT_1US 0 37 + #define TIMER_CSD_UNIT_1US 1 38 + 39 + static void __iomem *timer_base; 40 + 41 + static u64 notrace meson6_timer_sched_read(void) 42 + { 43 + return (u64)readl(timer_base + TIMER_ISA_VAL(CSD_ID)); 44 + } 45 + 46 + static void meson6_clkevt_time_stop(unsigned char timer) 47 + { 48 + u32 val = readl(timer_base + TIMER_ISA_MUX); 49 + 50 + writel(val & ~TIMER_ENABLE_BIT(timer), timer_base + TIMER_ISA_MUX); 51 + } 52 + 53 + static void meson6_clkevt_time_setup(unsigned char timer, unsigned long delay) 54 + { 55 + writel(delay, timer_base + TIMER_ISA_VAL(timer)); 56 + } 57 + 58 + static void meson6_clkevt_time_start(unsigned char timer, bool periodic) 59 + { 60 + u32 val = readl(timer_base + TIMER_ISA_MUX); 61 + 62 + if (periodic) 63 + val |= TIMER_PERIODIC_BIT(timer); 64 + else 65 + val &= ~TIMER_PERIODIC_BIT(timer); 66 + 67 + writel(val | TIMER_ENABLE_BIT(timer), timer_base + TIMER_ISA_MUX); 68 + } 69 + 70 + static void meson6_clkevt_mode(enum clock_event_mode mode, 71 + struct clock_event_device *clk) 72 + { 73 + switch (mode) { 74 + case CLOCK_EVT_MODE_PERIODIC: 75 + meson6_clkevt_time_stop(CED_ID); 76 + meson6_clkevt_time_setup(CED_ID, USEC_PER_SEC/HZ - 1); 77 + meson6_clkevt_time_start(CED_ID, true); 78 + break; 79 + case CLOCK_EVT_MODE_ONESHOT: 80 + meson6_clkevt_time_stop(CED_ID); 81 + meson6_clkevt_time_start(CED_ID, false); 82 + break; 83 + case CLOCK_EVT_MODE_UNUSED: 84 + case CLOCK_EVT_MODE_SHUTDOWN: 85 + default: 86 + meson6_clkevt_time_stop(CED_ID); 87 + break; 88 + } 89 + } 90 + 91 + static int meson6_clkevt_next_event(unsigned long evt, 92 + struct clock_event_device *unused) 93 + { 94 + meson6_clkevt_time_stop(CED_ID); 95 + meson6_clkevt_time_setup(CED_ID, evt); 96 + meson6_clkevt_time_start(CED_ID, false); 97 + 98 + return 0; 99 + } 100 + 101 + static struct clock_event_device meson6_clockevent = { 102 + .name = "meson6_tick", 103 + .rating = 400, 104 + .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, 105 + .set_mode = meson6_clkevt_mode, 106 + .set_next_event = meson6_clkevt_next_event, 107 + }; 108 + 109 + static irqreturn_t meson6_timer_interrupt(int irq, void *dev_id) 110 + { 111 + struct clock_event_device *evt = (struct clock_event_device *)dev_id; 112 + 113 + evt->event_handler(evt); 114 + 115 + return IRQ_HANDLED; 116 + } 117 + 118 + static struct irqaction meson6_timer_irq = { 119 + .name = "meson6_timer", 120 + .flags = IRQF_TIMER | IRQF_IRQPOLL, 121 + .handler = meson6_timer_interrupt, 122 + .dev_id = &meson6_clockevent, 123 + }; 124 + 125 + static void __init meson6_timer_init(struct device_node *node) 126 + { 127 + u32 val; 128 + int ret, irq; 129 + 130 + timer_base = of_io_request_and_map(node, 0, "meson6-timer"); 131 + if (IS_ERR(timer_base)) 132 + panic("Can't map registers"); 133 + 134 + irq = irq_of_parse_and_map(node, 0); 135 + if (irq <= 0) 136 + panic("Can't parse IRQ"); 137 + 138 + /* Set 1us for timer E */ 139 + val = readl(timer_base + TIMER_ISA_MUX); 140 + val &= ~TIMER_CSD_INPUT_MASK; 141 + val |= TIMER_CSD_UNIT_1US << TIMER_INPUT_BIT(CSD_ID); 142 + writel(val, timer_base + TIMER_ISA_MUX); 143 + 144 + sched_clock_register(meson6_timer_sched_read, 32, USEC_PER_SEC); 145 + clocksource_mmio_init(timer_base + TIMER_ISA_VAL(CSD_ID), node->name, 146 + 1000 * 1000, 300, 32, clocksource_mmio_readl_up); 147 + 148 + /* Timer A base 1us */ 149 + val &= ~TIMER_CED_INPUT_MASK; 150 + val |= TIMER_CED_UNIT_1US << TIMER_INPUT_BIT(CED_ID); 151 + writel(val, timer_base + TIMER_ISA_MUX); 152 + 153 + /* Stop the timer A */ 154 + meson6_clkevt_time_stop(CED_ID); 155 + 156 + ret = setup_irq(irq, &meson6_timer_irq); 157 + if (ret) 158 + pr_warn("failed to setup irq %d\n", irq); 159 + 160 + meson6_clockevent.cpumask = cpu_possible_mask; 161 + meson6_clockevent.irq = irq; 162 + 163 + clockevents_config_and_register(&meson6_clockevent, USEC_PER_SEC, 164 + 1, 0xfffe); 165 + } 166 + CLOCKSOURCE_OF_DECLARE(meson6, "amlogic,meson6-timer", 167 + meson6_timer_init);