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

ARM: clocksource: Add asm9260_timer driver

In some cases asm9260 looks similar to iMX2x. One of exceptions is
timer controller. So this patch introduces new driver for this special case.

Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
Acked-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>

authored by

Oleksij Rempel and committed by
Daniel Lezcano
8d8bd7be 5833ac98

+231
+10
drivers/clocksource/Kconfig
··· 229 229 depends on MIPS_GIC 230 230 select CLKSRC_OF 231 231 232 + config ASM9260_TIMER 233 + bool "Alphascale ASM9260 timer driver" 234 + depends on GENERIC_CLOCKEVENTS 235 + select CLKSRC_MMIO 236 + select CLKSRC_OF 237 + default y if MACH_ASM9260 238 + help 239 + This enables build of a clocksource and clockevent driver for 240 + the 32-bit System Timer hardware available on a Alphascale ASM9260. 241 + 232 242 endmenu
+1
drivers/clocksource/Makefile
··· 48 48 obj-$(CONFIG_ARCH_INTEGRATOR_AP) += timer-integrator-ap.o 49 49 obj-$(CONFIG_CLKSRC_VERSATILE) += versatile.o 50 50 obj-$(CONFIG_CLKSRC_MIPS_GIC) += mips-gic-timer.o 51 + obj-$(CONFIG_ASM9260_TIMER) += asm9260_timer.o
+220
drivers/clocksource/asm9260_timer.c
··· 1 + /* 2 + * Copyright (C) 2014 Oleksij Rempel <linux@rempel-privat.de> 3 + * 4 + * This program is free software; you can redistribute it and/or 5 + * modify it under the terms of the GNU General Public License 6 + * as published by the Free Software Foundation; either version 2 7 + * of the License, or (at your option) any later version. 8 + */ 9 + 10 + #include <linux/kernel.h> 11 + #include <linux/init.h> 12 + #include <linux/interrupt.h> 13 + #include <linux/sched.h> 14 + #include <linux/clk.h> 15 + #include <linux/clocksource.h> 16 + #include <linux/clockchips.h> 17 + #include <linux/io.h> 18 + #include <linux/of.h> 19 + #include <linux/of_address.h> 20 + #include <linux/of_irq.h> 21 + #include <linux/bitops.h> 22 + 23 + #define DRIVER_NAME "asm9260-timer" 24 + 25 + /* 26 + * this device provide 4 offsets for each register: 27 + * 0x0 - plain read write mode 28 + * 0x4 - set mode, OR logic. 29 + * 0x8 - clr mode, XOR logic. 30 + * 0xc - togle mode. 31 + */ 32 + #define SET_REG 4 33 + #define CLR_REG 8 34 + 35 + #define HW_IR 0x0000 /* RW. Interrupt */ 36 + #define BM_IR_CR0 BIT(4) 37 + #define BM_IR_MR3 BIT(3) 38 + #define BM_IR_MR2 BIT(2) 39 + #define BM_IR_MR1 BIT(1) 40 + #define BM_IR_MR0 BIT(0) 41 + 42 + #define HW_TCR 0x0010 /* RW. Timer controller */ 43 + /* BM_C*_RST 44 + * Timer Counter and the Prescale Counter are synchronously reset on the 45 + * next positive edge of PCLK. The counters remain reset until TCR[1] is 46 + * returned to zero. */ 47 + #define BM_C3_RST BIT(7) 48 + #define BM_C2_RST BIT(6) 49 + #define BM_C1_RST BIT(5) 50 + #define BM_C0_RST BIT(4) 51 + /* BM_C*_EN 52 + * 1 - Timer Counter and Prescale Counter are enabled for counting 53 + * 0 - counters are disabled */ 54 + #define BM_C3_EN BIT(3) 55 + #define BM_C2_EN BIT(2) 56 + #define BM_C1_EN BIT(1) 57 + #define BM_C0_EN BIT(0) 58 + 59 + #define HW_DIR 0x0020 /* RW. Direction? */ 60 + /* 00 - count up 61 + * 01 - count down 62 + * 10 - ?? 2^n/2 */ 63 + #define BM_DIR_COUNT_UP 0 64 + #define BM_DIR_COUNT_DOWN 1 65 + #define BM_DIR0_SHIFT 0 66 + #define BM_DIR1_SHIFT 4 67 + #define BM_DIR2_SHIFT 8 68 + #define BM_DIR3_SHIFT 12 69 + #define BM_DIR_DEFAULT (BM_DIR_COUNT_UP << BM_DIR0_SHIFT | \ 70 + BM_DIR_COUNT_UP << BM_DIR1_SHIFT | \ 71 + BM_DIR_COUNT_UP << BM_DIR2_SHIFT | \ 72 + BM_DIR_COUNT_UP << BM_DIR3_SHIFT) 73 + 74 + #define HW_TC0 0x0030 /* RO. Timer counter 0 */ 75 + /* HW_TC*. Timer counter owerflow (0xffff.ffff to 0x0000.0000) do not generate 76 + * interrupt. This registers can be used to detect overflow */ 77 + #define HW_TC1 0x0040 78 + #define HW_TC2 0x0050 79 + #define HW_TC3 0x0060 80 + 81 + #define HW_PR 0x0070 /* RW. prescaler */ 82 + #define BM_PR_DISABLE 0 83 + #define HW_PC 0x0080 /* RO. Prescaler counter */ 84 + #define HW_MCR 0x0090 /* RW. Match control */ 85 + /* enable interrupt on match */ 86 + #define BM_MCR_INT_EN(n) (1 << (n * 3 + 0)) 87 + /* enable TC reset on match */ 88 + #define BM_MCR_RES_EN(n) (1 << (n * 3 + 1)) 89 + /* enable stop TC on match */ 90 + #define BM_MCR_STOP_EN(n) (1 << (n * 3 + 2)) 91 + 92 + #define HW_MR0 0x00a0 /* RW. Match reg */ 93 + #define HW_MR1 0x00b0 94 + #define HW_MR2 0x00C0 95 + #define HW_MR3 0x00D0 96 + 97 + #define HW_CTCR 0x0180 /* Counter control */ 98 + #define BM_CTCR0_SHIFT 0 99 + #define BM_CTCR1_SHIFT 2 100 + #define BM_CTCR2_SHIFT 4 101 + #define BM_CTCR3_SHIFT 6 102 + #define BM_CTCR_TM 0 /* Timer mode. Every rising PCLK edge. */ 103 + #define BM_CTCR_DEFAULT (BM_CTCR_TM << BM_CTCR0_SHIFT | \ 104 + BM_CTCR_TM << BM_CTCR1_SHIFT | \ 105 + BM_CTCR_TM << BM_CTCR2_SHIFT | \ 106 + BM_CTCR_TM << BM_CTCR3_SHIFT) 107 + 108 + static struct asm9260_timer_priv { 109 + void __iomem *base; 110 + unsigned long ticks_per_jiffy; 111 + } priv; 112 + 113 + static int asm9260_timer_set_next_event(unsigned long delta, 114 + struct clock_event_device *evt) 115 + { 116 + /* configure match count for TC0 */ 117 + writel_relaxed(delta, priv.base + HW_MR0); 118 + /* enable TC0 */ 119 + writel_relaxed(BM_C0_EN, priv.base + HW_TCR + SET_REG); 120 + return 0; 121 + } 122 + 123 + static void asm9260_timer_set_mode(enum clock_event_mode mode, 124 + struct clock_event_device *evt) 125 + { 126 + /* stop timer0 */ 127 + writel_relaxed(BM_C0_EN, priv.base + HW_TCR + CLR_REG); 128 + 129 + switch (mode) { 130 + case CLOCK_EVT_MODE_PERIODIC: 131 + /* disable reset and stop on match */ 132 + writel_relaxed(BM_MCR_RES_EN(0) | BM_MCR_STOP_EN(0), 133 + priv.base + HW_MCR + CLR_REG); 134 + /* configure match count for TC0 */ 135 + writel_relaxed(priv.ticks_per_jiffy, priv.base + HW_MR0); 136 + /* enable TC0 */ 137 + writel_relaxed(BM_C0_EN, priv.base + HW_TCR + SET_REG); 138 + break; 139 + case CLOCK_EVT_MODE_ONESHOT: 140 + /* enable reset and stop on match */ 141 + writel_relaxed(BM_MCR_RES_EN(0) | BM_MCR_STOP_EN(0), 142 + priv.base + HW_MCR + SET_REG); 143 + break; 144 + default: 145 + break; 146 + } 147 + } 148 + 149 + static struct clock_event_device event_dev = { 150 + .name = DRIVER_NAME, 151 + .rating = 200, 152 + .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, 153 + .set_next_event = asm9260_timer_set_next_event, 154 + .set_mode = asm9260_timer_set_mode, 155 + }; 156 + 157 + static irqreturn_t asm9260_timer_interrupt(int irq, void *dev_id) 158 + { 159 + struct clock_event_device *evt = dev_id; 160 + 161 + evt->event_handler(evt); 162 + 163 + writel_relaxed(BM_IR_MR0, priv.base + HW_IR); 164 + 165 + return IRQ_HANDLED; 166 + } 167 + 168 + /* 169 + * --------------------------------------------------------------------------- 170 + * Timer initialization 171 + * --------------------------------------------------------------------------- 172 + */ 173 + static void __init asm9260_timer_init(struct device_node *np) 174 + { 175 + int irq; 176 + struct clk *clk; 177 + int ret; 178 + unsigned long rate; 179 + 180 + priv.base = of_io_request_and_map(np, 0, np->name); 181 + if (!priv.base) 182 + panic("%s: unable to map resource", np->name); 183 + 184 + clk = of_clk_get(np, 0); 185 + 186 + ret = clk_prepare_enable(clk); 187 + if (ret) 188 + panic("Failed to enable clk!\n"); 189 + 190 + irq = irq_of_parse_and_map(np, 0); 191 + ret = request_irq(irq, asm9260_timer_interrupt, IRQF_TIMER, 192 + DRIVER_NAME, &event_dev); 193 + if (ret) 194 + panic("Failed to setup irq!\n"); 195 + 196 + /* set all timers for count-up */ 197 + writel_relaxed(BM_DIR_DEFAULT, priv.base + HW_DIR); 198 + /* disable divider */ 199 + writel_relaxed(BM_PR_DISABLE, priv.base + HW_PR); 200 + /* make sure all timers use every rising PCLK edge. */ 201 + writel_relaxed(BM_CTCR_DEFAULT, priv.base + HW_CTCR); 202 + /* enable interrupt for TC0 and clean setting for all other lines */ 203 + writel_relaxed(BM_MCR_INT_EN(0) , priv.base + HW_MCR); 204 + 205 + rate = clk_get_rate(clk); 206 + clocksource_mmio_init(priv.base + HW_TC1, DRIVER_NAME, rate, 207 + 200, 32, clocksource_mmio_readl_up); 208 + 209 + /* Seems like we can't use counter without match register even if 210 + * actions for MR are disabled. So, set MR to max value. */ 211 + writel_relaxed(0xffffffff, priv.base + HW_MR1); 212 + /* enable TC1 */ 213 + writel_relaxed(BM_C1_EN, priv.base + HW_TCR + SET_REG); 214 + 215 + priv.ticks_per_jiffy = DIV_ROUND_CLOSEST(rate, HZ); 216 + event_dev.cpumask = cpumask_of(0); 217 + clockevents_config_and_register(&event_dev, rate, 0x2c00, 0xfffffffe); 218 + } 219 + CLOCKSOURCE_OF_DECLARE(asm9260_timer, "alphascale,asm9260-timer", 220 + asm9260_timer_init);