diff options
| author | Ingo Molnar <mingo@kernel.org> | 2015-02-09 05:20:36 -0500 |
|---|---|---|
| committer | Ingo Molnar <mingo@kernel.org> | 2015-02-09 05:20:36 -0500 |
| commit | f40d149b58f5dea148ceaee5f9249da133e5004c (patch) | |
| tree | 413b03df98cb9b2d8e877481dfd0795a89dedc7d /drivers | |
| parent | 4ebbda5251374d532ba8939de4241d769d1420b6 (diff) | |
| parent | 35a8578e8b83eb13f8d57ab40b98bcfd5199d3d4 (diff) | |
Merge branch 'clockevents/3.20' of http://git.linaro.org/people/daniel.lezcano/linux into timers/core
Pull clockevents updates from Daniel Lezcano:
- Add new driver for the Conexant Digicolor SoCs (Baruch Siach)
- Add new driver for the rockchip rk3288 board (Daniel Lezcano)
- Add new asm9260 driver for MIPS (Oleksij Rempel)
- Add DT definitions for the versatile AB/PB boards (Rob Herring)
- Rename the 'marco' timer to 'atlas7' (Barry Song)
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/clocksource/Kconfig | 17 | ||||
| -rw-r--r-- | drivers/clocksource/Makefile | 5 | ||||
| -rw-r--r-- | drivers/clocksource/asm9260_timer.c | 220 | ||||
| -rw-r--r-- | drivers/clocksource/rockchip_timer.c | 180 | ||||
| -rw-r--r-- | drivers/clocksource/timer-atlas7.c (renamed from drivers/clocksource/timer-marco.c) | 15 | ||||
| -rw-r--r-- | drivers/clocksource/timer-digicolor.c | 199 | ||||
| -rw-r--r-- | drivers/clocksource/versatile.c | 4 |
7 files changed, 630 insertions, 10 deletions
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index fc01ec27d3c8..72a38273f7f5 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig | |||
| @@ -18,6 +18,9 @@ config CLKBLD_I8253 | |||
| 18 | config CLKSRC_MMIO | 18 | config CLKSRC_MMIO |
| 19 | bool | 19 | bool |
| 20 | 20 | ||
| 21 | config DIGICOLOR_TIMER | ||
| 22 | bool | ||
| 23 | |||
| 21 | config DW_APB_TIMER | 24 | config DW_APB_TIMER |
| 22 | bool | 25 | bool |
| 23 | 26 | ||
| @@ -26,6 +29,10 @@ config DW_APB_TIMER_OF | |||
| 26 | select DW_APB_TIMER | 29 | select DW_APB_TIMER |
| 27 | select CLKSRC_OF | 30 | select CLKSRC_OF |
| 28 | 31 | ||
| 32 | config ROCKCHIP_TIMER | ||
| 33 | bool | ||
| 34 | select CLKSRC_OF | ||
| 35 | |||
| 29 | config ARMADA_370_XP_TIMER | 36 | config ARMADA_370_XP_TIMER |
| 30 | bool | 37 | bool |
| 31 | select CLKSRC_OF | 38 | select CLKSRC_OF |
| @@ -229,4 +236,14 @@ config CLKSRC_MIPS_GIC | |||
| 229 | depends on MIPS_GIC | 236 | depends on MIPS_GIC |
| 230 | select CLKSRC_OF | 237 | select CLKSRC_OF |
| 231 | 238 | ||
| 239 | config ASM9260_TIMER | ||
| 240 | bool "Alphascale ASM9260 timer driver" | ||
| 241 | depends on GENERIC_CLOCKEVENTS | ||
| 242 | select CLKSRC_MMIO | ||
| 243 | select CLKSRC_OF | ||
| 244 | default y if MACH_ASM9260 | ||
| 245 | help | ||
| 246 | This enables build of a clocksource and clockevent driver for | ||
| 247 | the 32-bit System Timer hardware available on a Alphascale ASM9260. | ||
| 248 | |||
| 232 | endmenu | 249 | endmenu |
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index 94d90b24b56b..0f8003d76c32 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile | |||
| @@ -10,15 +10,17 @@ obj-$(CONFIG_SH_TIMER_TMU) += sh_tmu.o | |||
| 10 | obj-$(CONFIG_EM_TIMER_STI) += em_sti.o | 10 | obj-$(CONFIG_EM_TIMER_STI) += em_sti.o |
| 11 | obj-$(CONFIG_CLKBLD_I8253) += i8253.o | 11 | obj-$(CONFIG_CLKBLD_I8253) += i8253.o |
| 12 | obj-$(CONFIG_CLKSRC_MMIO) += mmio.o | 12 | obj-$(CONFIG_CLKSRC_MMIO) += mmio.o |
| 13 | obj-$(CONFIG_DIGICOLOR_TIMER) += timer-digicolor.o | ||
| 13 | obj-$(CONFIG_DW_APB_TIMER) += dw_apb_timer.o | 14 | obj-$(CONFIG_DW_APB_TIMER) += dw_apb_timer.o |
| 14 | obj-$(CONFIG_DW_APB_TIMER_OF) += dw_apb_timer_of.o | 15 | obj-$(CONFIG_DW_APB_TIMER_OF) += dw_apb_timer_of.o |
| 16 | obj-$(CONFIG_ROCKCHIP_TIMER) += rockchip_timer.o | ||
| 15 | obj-$(CONFIG_CLKSRC_NOMADIK_MTU) += nomadik-mtu.o | 17 | obj-$(CONFIG_CLKSRC_NOMADIK_MTU) += nomadik-mtu.o |
| 16 | obj-$(CONFIG_CLKSRC_DBX500_PRCMU) += clksrc-dbx500-prcmu.o | 18 | obj-$(CONFIG_CLKSRC_DBX500_PRCMU) += clksrc-dbx500-prcmu.o |
| 17 | obj-$(CONFIG_ARMADA_370_XP_TIMER) += time-armada-370-xp.o | 19 | obj-$(CONFIG_ARMADA_370_XP_TIMER) += time-armada-370-xp.o |
| 18 | obj-$(CONFIG_ORION_TIMER) += time-orion.o | 20 | obj-$(CONFIG_ORION_TIMER) += time-orion.o |
| 19 | obj-$(CONFIG_ARCH_BCM2835) += bcm2835_timer.o | 21 | obj-$(CONFIG_ARCH_BCM2835) += bcm2835_timer.o |
| 20 | obj-$(CONFIG_ARCH_CLPS711X) += clps711x-timer.o | 22 | obj-$(CONFIG_ARCH_CLPS711X) += clps711x-timer.o |
| 21 | obj-$(CONFIG_ARCH_MARCO) += timer-marco.o | 23 | obj-$(CONFIG_ARCH_ATLAS7) += timer-atlas7.o |
| 22 | obj-$(CONFIG_ARCH_MOXART) += moxart_timer.o | 24 | obj-$(CONFIG_ARCH_MOXART) += moxart_timer.o |
| 23 | obj-$(CONFIG_ARCH_MXS) += mxs_timer.o | 25 | obj-$(CONFIG_ARCH_MXS) += mxs_timer.o |
| 24 | obj-$(CONFIG_ARCH_PXA) += pxa_timer.o | 26 | obj-$(CONFIG_ARCH_PXA) += pxa_timer.o |
| @@ -48,3 +50,4 @@ obj-$(CONFIG_ARCH_KEYSTONE) += timer-keystone.o | |||
| 48 | obj-$(CONFIG_ARCH_INTEGRATOR_AP) += timer-integrator-ap.o | 50 | obj-$(CONFIG_ARCH_INTEGRATOR_AP) += timer-integrator-ap.o |
| 49 | obj-$(CONFIG_CLKSRC_VERSATILE) += versatile.o | 51 | obj-$(CONFIG_CLKSRC_VERSATILE) += versatile.o |
| 50 | obj-$(CONFIG_CLKSRC_MIPS_GIC) += mips-gic-timer.o | 52 | obj-$(CONFIG_CLKSRC_MIPS_GIC) += mips-gic-timer.o |
| 53 | obj-$(CONFIG_ASM9260_TIMER) += asm9260_timer.o | ||
diff --git a/drivers/clocksource/asm9260_timer.c b/drivers/clocksource/asm9260_timer.c new file mode 100644 index 000000000000..2c9c993727c8 --- /dev/null +++ b/drivers/clocksource/asm9260_timer.c | |||
| @@ -0,0 +1,220 @@ | |||
| 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); | ||
diff --git a/drivers/clocksource/rockchip_timer.c b/drivers/clocksource/rockchip_timer.c new file mode 100644 index 000000000000..a35993bafb20 --- /dev/null +++ b/drivers/clocksource/rockchip_timer.c | |||
| @@ -0,0 +1,180 @@ | |||
| 1 | /* | ||
| 2 | * Rockchip timer support | ||
| 3 | * | ||
| 4 | * Copyright (C) Daniel Lezcano <daniel.lezcano@linaro.org> | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License version 2 as | ||
| 8 | * published by the Free Software Foundation. | ||
| 9 | */ | ||
| 10 | #include <linux/clk.h> | ||
| 11 | #include <linux/clockchips.h> | ||
| 12 | #include <linux/init.h> | ||
| 13 | #include <linux/interrupt.h> | ||
| 14 | #include <linux/of.h> | ||
| 15 | #include <linux/of_address.h> | ||
| 16 | #include <linux/of_irq.h> | ||
| 17 | |||
| 18 | #define TIMER_NAME "rk_timer" | ||
| 19 | |||
| 20 | #define TIMER_LOAD_COUNT0 0x00 | ||
| 21 | #define TIMER_LOAD_COUNT1 0x04 | ||
| 22 | #define TIMER_CONTROL_REG 0x10 | ||
| 23 | #define TIMER_INT_STATUS 0x18 | ||
| 24 | |||
| 25 | #define TIMER_DISABLE 0x0 | ||
| 26 | #define TIMER_ENABLE 0x1 | ||
| 27 | #define TIMER_MODE_FREE_RUNNING (0 << 1) | ||
| 28 | #define TIMER_MODE_USER_DEFINED_COUNT (1 << 1) | ||
| 29 | #define TIMER_INT_UNMASK (1 << 2) | ||
| 30 | |||
| 31 | struct bc_timer { | ||
| 32 | struct clock_event_device ce; | ||
| 33 | void __iomem *base; | ||
| 34 | u32 freq; | ||
| 35 | }; | ||
| 36 | |||
| 37 | static struct bc_timer bc_timer; | ||
| 38 | |||
| 39 | static inline struct bc_timer *rk_timer(struct clock_event_device *ce) | ||
| 40 | { | ||
| 41 | return container_of(ce, struct bc_timer, ce); | ||
| 42 | } | ||
| 43 | |||
| 44 | static inline void __iomem *rk_base(struct clock_event_device *ce) | ||
| 45 | { | ||
| 46 | return rk_timer(ce)->base; | ||
| 47 | } | ||
| 48 | |||
| 49 | static inline void rk_timer_disable(struct clock_event_device *ce) | ||
| 50 | { | ||
| 51 | writel_relaxed(TIMER_DISABLE, rk_base(ce) + TIMER_CONTROL_REG); | ||
| 52 | dsb(); | ||
| 53 | } | ||
| 54 | |||
| 55 | static inline void rk_timer_enable(struct clock_event_device *ce, u32 flags) | ||
| 56 | { | ||
| 57 | writel_relaxed(TIMER_ENABLE | TIMER_INT_UNMASK | flags, | ||
| 58 | rk_base(ce) + TIMER_CONTROL_REG); | ||
| 59 | dsb(); | ||
| 60 | } | ||
| 61 | |||
| 62 | static void rk_timer_update_counter(unsigned long cycles, | ||
| 63 | struct clock_event_device *ce) | ||
| 64 | { | ||
| 65 | writel_relaxed(cycles, rk_base(ce) + TIMER_LOAD_COUNT0); | ||
| 66 | writel_relaxed(0, rk_base(ce) + TIMER_LOAD_COUNT1); | ||
| 67 | dsb(); | ||
| 68 | } | ||
| 69 | |||
| 70 | static void rk_timer_interrupt_clear(struct clock_event_device *ce) | ||
| 71 | { | ||
| 72 | writel_relaxed(1, rk_base(ce) + TIMER_INT_STATUS); | ||
| 73 | dsb(); | ||
| 74 | } | ||
| 75 | |||
| 76 | static inline int rk_timer_set_next_event(unsigned long cycles, | ||
| 77 | struct clock_event_device *ce) | ||
| 78 | { | ||
| 79 | rk_timer_disable(ce); | ||
| 80 | rk_timer_update_counter(cycles, ce); | ||
| 81 | rk_timer_enable(ce, TIMER_MODE_USER_DEFINED_COUNT); | ||
| 82 | return 0; | ||
| 83 | } | ||
| 84 | |||
| 85 | static inline void rk_timer_set_mode(enum clock_event_mode mode, | ||
| 86 | struct clock_event_device *ce) | ||
| 87 | { | ||
| 88 | switch (mode) { | ||
| 89 | case CLOCK_EVT_MODE_PERIODIC: | ||
| 90 | rk_timer_disable(ce); | ||
| 91 | rk_timer_update_counter(rk_timer(ce)->freq / HZ - 1, ce); | ||
| 92 | rk_timer_enable(ce, TIMER_MODE_FREE_RUNNING); | ||
| 93 | break; | ||
| 94 | case CLOCK_EVT_MODE_ONESHOT: | ||
| 95 | case CLOCK_EVT_MODE_RESUME: | ||
| 96 | break; | ||
| 97 | case CLOCK_EVT_MODE_UNUSED: | ||
| 98 | case CLOCK_EVT_MODE_SHUTDOWN: | ||
| 99 | rk_timer_disable(ce); | ||
| 100 | break; | ||
| 101 | } | ||
| 102 | } | ||
| 103 | |||
| 104 | static irqreturn_t rk_timer_interrupt(int irq, void *dev_id) | ||
| 105 | { | ||
| 106 | struct clock_event_device *ce = dev_id; | ||
| 107 | |||
| 108 | rk_timer_interrupt_clear(ce); | ||
| 109 | |||
| 110 | if (ce->mode == CLOCK_EVT_MODE_ONESHOT) | ||
| 111 | rk_timer_disable(ce); | ||
| 112 | |||
| 113 | ce->event_handler(ce); | ||
| 114 | |||
| 115 | return IRQ_HANDLED; | ||
| 116 | } | ||
| 117 | |||
| 118 | static void __init rk_timer_init(struct device_node *np) | ||
| 119 | { | ||
| 120 | struct clock_event_device *ce = &bc_timer.ce; | ||
| 121 | struct clk *timer_clk; | ||
| 122 | struct clk *pclk; | ||
| 123 | int ret, irq; | ||
| 124 | |||
| 125 | bc_timer.base = of_iomap(np, 0); | ||
| 126 | if (!bc_timer.base) { | ||
| 127 | pr_err("Failed to get base address for '%s'\n", TIMER_NAME); | ||
| 128 | return; | ||
| 129 | } | ||
| 130 | |||
| 131 | pclk = of_clk_get_by_name(np, "pclk"); | ||
| 132 | if (IS_ERR(pclk)) { | ||
| 133 | pr_err("Failed to get pclk for '%s'\n", TIMER_NAME); | ||
| 134 | return; | ||
| 135 | } | ||
| 136 | |||
| 137 | if (clk_prepare_enable(pclk)) { | ||
| 138 | pr_err("Failed to enable pclk for '%s'\n", TIMER_NAME); | ||
| 139 | return; | ||
| 140 | } | ||
| 141 | |||
| 142 | timer_clk = of_clk_get_by_name(np, "timer"); | ||
| 143 | if (IS_ERR(timer_clk)) { | ||
| 144 | pr_err("Failed to get timer clock for '%s'\n", TIMER_NAME); | ||
| 145 | return; | ||
| 146 | } | ||
| 147 | |||
| 148 | if (clk_prepare_enable(timer_clk)) { | ||
| 149 | pr_err("Failed to enable timer clock\n"); | ||
| 150 | return; | ||
| 151 | } | ||
| 152 | |||
| 153 | bc_timer.freq = clk_get_rate(timer_clk); | ||
| 154 | |||
| 155 | irq = irq_of_parse_and_map(np, 0); | ||
| 156 | if (irq == NO_IRQ) { | ||
| 157 | pr_err("Failed to map interrupts for '%s'\n", TIMER_NAME); | ||
| 158 | return; | ||
| 159 | } | ||
| 160 | |||
| 161 | ce->name = TIMER_NAME; | ||
| 162 | ce->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT; | ||
| 163 | ce->set_next_event = rk_timer_set_next_event; | ||
| 164 | ce->set_mode = rk_timer_set_mode; | ||
| 165 | ce->irq = irq; | ||
| 166 | ce->cpumask = cpumask_of(0); | ||
| 167 | ce->rating = 250; | ||
| 168 | |||
| 169 | rk_timer_interrupt_clear(ce); | ||
| 170 | rk_timer_disable(ce); | ||
| 171 | |||
| 172 | ret = request_irq(irq, rk_timer_interrupt, IRQF_TIMER, TIMER_NAME, ce); | ||
| 173 | if (ret) { | ||
| 174 | pr_err("Failed to initialize '%s': %d\n", TIMER_NAME, ret); | ||
| 175 | return; | ||
| 176 | } | ||
| 177 | |||
| 178 | clockevents_config_and_register(ce, bc_timer.freq, 1, UINT_MAX); | ||
| 179 | } | ||
| 180 | CLOCKSOURCE_OF_DECLARE(rk_timer, "rockchip,rk3288-timer", rk_timer_init); | ||
diff --git a/drivers/clocksource/timer-marco.c b/drivers/clocksource/timer-atlas7.c index 361a789d4bee..60f9de3438b0 100644 --- a/drivers/clocksource/timer-marco.c +++ b/drivers/clocksource/timer-atlas7.c | |||
| @@ -38,7 +38,7 @@ | |||
| 38 | 38 | ||
| 39 | #define SIRFSOC_TIMER_REG_CNT 6 | 39 | #define SIRFSOC_TIMER_REG_CNT 6 |
| 40 | 40 | ||
| 41 | static unsigned long marco_timer_rate; | 41 | static unsigned long atlas7_timer_rate; |
| 42 | 42 | ||
| 43 | static const u32 sirfsoc_timer_reg_list[SIRFSOC_TIMER_REG_CNT] = { | 43 | static const u32 sirfsoc_timer_reg_list[SIRFSOC_TIMER_REG_CNT] = { |
| 44 | SIRFSOC_TIMER_WATCHDOG_EN, | 44 | SIRFSOC_TIMER_WATCHDOG_EN, |
| @@ -195,7 +195,7 @@ static int sirfsoc_local_timer_setup(struct clock_event_device *ce) | |||
| 195 | ce->rating = 200; | 195 | ce->rating = 200; |
| 196 | ce->set_mode = sirfsoc_timer_set_mode; | 196 | ce->set_mode = sirfsoc_timer_set_mode; |
| 197 | ce->set_next_event = sirfsoc_timer_set_next_event; | 197 | ce->set_next_event = sirfsoc_timer_set_next_event; |
| 198 | clockevents_calc_mult_shift(ce, marco_timer_rate, 60); | 198 | clockevents_calc_mult_shift(ce, atlas7_timer_rate, 60); |
| 199 | ce->max_delta_ns = clockevent_delta2ns(-2, ce); | 199 | ce->max_delta_ns = clockevent_delta2ns(-2, ce); |
| 200 | ce->min_delta_ns = clockevent_delta2ns(2, ce); | 200 | ce->min_delta_ns = clockevent_delta2ns(2, ce); |
| 201 | ce->cpumask = cpumask_of(cpu); | 201 | ce->cpumask = cpumask_of(cpu); |
| @@ -255,9 +255,8 @@ static void __init sirfsoc_clockevent_init(void) | |||
| 255 | } | 255 | } |
| 256 | 256 | ||
| 257 | /* initialize the kernel jiffy timer source */ | 257 | /* initialize the kernel jiffy timer source */ |
| 258 | static void __init sirfsoc_marco_timer_init(struct device_node *np) | 258 | static void __init sirfsoc_atlas7_timer_init(struct device_node *np) |
| 259 | { | 259 | { |
| 260 | u32 timer_div; | ||
| 261 | struct clk *clk; | 260 | struct clk *clk; |
| 262 | 261 | ||
| 263 | clk = of_clk_get(np, 0); | 262 | clk = of_clk_get(np, 0); |
| @@ -265,7 +264,7 @@ static void __init sirfsoc_marco_timer_init(struct device_node *np) | |||
| 265 | 264 | ||
| 266 | BUG_ON(clk_prepare_enable(clk)); | 265 | BUG_ON(clk_prepare_enable(clk)); |
| 267 | 266 | ||
| 268 | marco_timer_rate = clk_get_rate(clk); | 267 | atlas7_timer_rate = clk_get_rate(clk); |
| 269 | 268 | ||
| 270 | /* timer dividers: 0, not divided */ | 269 | /* timer dividers: 0, not divided */ |
| 271 | writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL); | 270 | writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL); |
| @@ -283,7 +282,7 @@ static void __init sirfsoc_marco_timer_init(struct device_node *np) | |||
| 283 | /* Clear all interrupts */ | 282 | /* Clear all interrupts */ |
| 284 | writel_relaxed(0xFFFF, sirfsoc_timer_base + SIRFSOC_TIMER_INTR_STATUS); | 283 | writel_relaxed(0xFFFF, sirfsoc_timer_base + SIRFSOC_TIMER_INTR_STATUS); |
| 285 | 284 | ||
| 286 | BUG_ON(clocksource_register_hz(&sirfsoc_clocksource, marco_timer_rate)); | 285 | BUG_ON(clocksource_register_hz(&sirfsoc_clocksource, atlas7_timer_rate)); |
| 287 | 286 | ||
| 288 | sirfsoc_clockevent_init(); | 287 | sirfsoc_clockevent_init(); |
| 289 | } | 288 | } |
| @@ -302,6 +301,6 @@ static void __init sirfsoc_of_timer_init(struct device_node *np) | |||
| 302 | if (!sirfsoc_timer1_irq.irq) | 301 | if (!sirfsoc_timer1_irq.irq) |
| 303 | panic("No irq passed for timer1 via DT\n"); | 302 | panic("No irq passed for timer1 via DT\n"); |
| 304 | 303 | ||
| 305 | sirfsoc_marco_timer_init(np); | 304 | sirfsoc_atlas7_timer_init(np); |
| 306 | } | 305 | } |
| 307 | CLOCKSOURCE_OF_DECLARE(sirfsoc_marco_timer, "sirf,marco-tick", sirfsoc_of_timer_init ); | 306 | CLOCKSOURCE_OF_DECLARE(sirfsoc_atlas7_timer, "sirf,atlas7-tick", sirfsoc_of_timer_init); |
diff --git a/drivers/clocksource/timer-digicolor.c b/drivers/clocksource/timer-digicolor.c new file mode 100644 index 000000000000..7f8388cfa810 --- /dev/null +++ b/drivers/clocksource/timer-digicolor.c | |||
| @@ -0,0 +1,199 @@ | |||
| 1 | /* | ||
| 2 | * Conexant Digicolor timer driver | ||
| 3 | * | ||
| 4 | * Author: Baruch Siach <baruch@tkos.co.il> | ||
| 5 | * | ||
| 6 | * Copyright (C) 2014 Paradox Innovation Ltd. | ||
| 7 | * | ||
| 8 | * Based on: | ||
| 9 | * Allwinner SoCs hstimer driver | ||
| 10 | * | ||
| 11 | * Copyright (C) 2013 Maxime Ripard | ||
| 12 | * | ||
| 13 | * Maxime Ripard <maxime.ripard@free-electrons.com> | ||
| 14 | * | ||
| 15 | * This file is licensed under the terms of the GNU General Public | ||
| 16 | * License version 2. This program is licensed "as is" without any | ||
| 17 | * warranty of any kind, whether express or implied. | ||
| 18 | */ | ||
| 19 | |||
| 20 | /* | ||
| 21 | * Conexant Digicolor SoCs have 8 configurable timers, named from "Timer A" to | ||
| 22 | * "Timer H". Timer A is the only one with watchdog support, so it is dedicated | ||
| 23 | * to the watchdog driver. This driver uses Timer B for sched_clock(), and | ||
| 24 | * Timer C for clockevents. | ||
| 25 | */ | ||
| 26 | |||
| 27 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
| 28 | |||
| 29 | #include <linux/clk.h> | ||
| 30 | #include <linux/clockchips.h> | ||
| 31 | #include <linux/interrupt.h> | ||
| 32 | #include <linux/irq.h> | ||
| 33 | #include <linux/irqreturn.h> | ||
| 34 | #include <linux/sched_clock.h> | ||
| 35 | #include <linux/of.h> | ||
| 36 | #include <linux/of_address.h> | ||
| 37 | #include <linux/of_irq.h> | ||
| 38 | |||
| 39 | enum { | ||
| 40 | TIMER_A, | ||
| 41 | TIMER_B, | ||
| 42 | TIMER_C, | ||
| 43 | TIMER_D, | ||
| 44 | TIMER_E, | ||
| 45 | TIMER_F, | ||
| 46 | TIMER_G, | ||
| 47 | TIMER_H, | ||
| 48 | }; | ||
| 49 | |||
| 50 | #define CONTROL(t) ((t)*8) | ||
| 51 | #define COUNT(t) ((t)*8 + 4) | ||
| 52 | |||
| 53 | #define CONTROL_DISABLE 0 | ||
| 54 | #define CONTROL_ENABLE BIT(0) | ||
| 55 | #define CONTROL_MODE(m) ((m) << 4) | ||
| 56 | #define CONTROL_MODE_ONESHOT CONTROL_MODE(1) | ||
| 57 | #define CONTROL_MODE_PERIODIC CONTROL_MODE(2) | ||
| 58 | |||
| 59 | struct digicolor_timer { | ||
| 60 | struct clock_event_device ce; | ||
| 61 | void __iomem *base; | ||
| 62 | u32 ticks_per_jiffy; | ||
| 63 | int timer_id; /* one of TIMER_* */ | ||
| 64 | }; | ||
| 65 | |||
| 66 | struct digicolor_timer *dc_timer(struct clock_event_device *ce) | ||
| 67 | { | ||
| 68 | return container_of(ce, struct digicolor_timer, ce); | ||
| 69 | } | ||
| 70 | |||
| 71 | static inline void dc_timer_disable(struct clock_event_device *ce) | ||
| 72 | { | ||
| 73 | struct digicolor_timer *dt = dc_timer(ce); | ||
| 74 | writeb(CONTROL_DISABLE, dt->base + CONTROL(dt->timer_id)); | ||
| 75 | } | ||
| 76 | |||
| 77 | static inline void dc_timer_enable(struct clock_event_device *ce, u32 mode) | ||
| 78 | { | ||
| 79 | struct digicolor_timer *dt = dc_timer(ce); | ||
| 80 | writeb(CONTROL_ENABLE | mode, dt->base + CONTROL(dt->timer_id)); | ||
| 81 | } | ||
| 82 | |||
| 83 | static inline void dc_timer_set_count(struct clock_event_device *ce, | ||
| 84 | unsigned long count) | ||
| 85 | { | ||
| 86 | struct digicolor_timer *dt = dc_timer(ce); | ||
| 87 | writel(count, dt->base + COUNT(dt->timer_id)); | ||
| 88 | } | ||
| 89 | |||
| 90 | static void digicolor_clkevt_mode(enum clock_event_mode mode, | ||
| 91 | struct clock_event_device *ce) | ||
| 92 | { | ||
| 93 | struct digicolor_timer *dt = dc_timer(ce); | ||
| 94 | |||
| 95 | switch (mode) { | ||
| 96 | case CLOCK_EVT_MODE_PERIODIC: | ||
| 97 | dc_timer_disable(ce); | ||
| 98 | dc_timer_set_count(ce, dt->ticks_per_jiffy); | ||
| 99 | dc_timer_enable(ce, CONTROL_MODE_PERIODIC); | ||
| 100 | break; | ||
| 101 | case CLOCK_EVT_MODE_ONESHOT: | ||
| 102 | dc_timer_disable(ce); | ||
| 103 | dc_timer_enable(ce, CONTROL_MODE_ONESHOT); | ||
| 104 | break; | ||
| 105 | case CLOCK_EVT_MODE_UNUSED: | ||
| 106 | case CLOCK_EVT_MODE_SHUTDOWN: | ||
| 107 | default: | ||
| 108 | dc_timer_disable(ce); | ||
| 109 | break; | ||
| 110 | } | ||
| 111 | } | ||
| 112 | |||
| 113 | static int digicolor_clkevt_next_event(unsigned long evt, | ||
| 114 | struct clock_event_device *ce) | ||
| 115 | { | ||
| 116 | dc_timer_disable(ce); | ||
| 117 | dc_timer_set_count(ce, evt); | ||
| 118 | dc_timer_enable(ce, CONTROL_MODE_ONESHOT); | ||
| 119 | |||
| 120 | return 0; | ||
| 121 | } | ||
| 122 | |||
| 123 | static struct digicolor_timer dc_timer_dev = { | ||
| 124 | .ce = { | ||
| 125 | .name = "digicolor_tick", | ||
| 126 | .rating = 340, | ||
| 127 | .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, | ||
| 128 | .set_mode = digicolor_clkevt_mode, | ||
| 129 | .set_next_event = digicolor_clkevt_next_event, | ||
| 130 | }, | ||
| 131 | .timer_id = TIMER_C, | ||
| 132 | }; | ||
| 133 | |||
| 134 | static irqreturn_t digicolor_timer_interrupt(int irq, void *dev_id) | ||
| 135 | { | ||
| 136 | struct clock_event_device *evt = dev_id; | ||
| 137 | |||
| 138 | evt->event_handler(evt); | ||
| 139 | |||
| 140 | return IRQ_HANDLED; | ||
| 141 | } | ||
| 142 | |||
| 143 | static u64 digicolor_timer_sched_read(void) | ||
| 144 | { | ||
| 145 | return ~readl(dc_timer_dev.base + COUNT(TIMER_B)); | ||
| 146 | } | ||
| 147 | |||
| 148 | static void __init digicolor_timer_init(struct device_node *node) | ||
| 149 | { | ||
| 150 | unsigned long rate; | ||
| 151 | struct clk *clk; | ||
| 152 | int ret, irq; | ||
| 153 | |||
| 154 | /* | ||
| 155 | * timer registers are shared with the watchdog timer; | ||
| 156 | * don't map exclusively | ||
| 157 | */ | ||
| 158 | dc_timer_dev.base = of_iomap(node, 0); | ||
| 159 | if (!dc_timer_dev.base) { | ||
| 160 | pr_err("Can't map registers"); | ||
| 161 | return; | ||
| 162 | } | ||
| 163 | |||
| 164 | irq = irq_of_parse_and_map(node, dc_timer_dev.timer_id); | ||
| 165 | if (irq <= 0) { | ||
| 166 | pr_err("Can't parse IRQ"); | ||
| 167 | return; | ||
| 168 | } | ||
| 169 | |||
| 170 | clk = of_clk_get(node, 0); | ||
| 171 | if (IS_ERR(clk)) { | ||
| 172 | pr_err("Can't get timer clock"); | ||
| 173 | return; | ||
| 174 | } | ||
| 175 | clk_prepare_enable(clk); | ||
| 176 | rate = clk_get_rate(clk); | ||
| 177 | dc_timer_dev.ticks_per_jiffy = DIV_ROUND_UP(rate, HZ); | ||
| 178 | |||
| 179 | writeb(CONTROL_DISABLE, dc_timer_dev.base + CONTROL(TIMER_B)); | ||
| 180 | writel(UINT_MAX, dc_timer_dev.base + COUNT(TIMER_B)); | ||
| 181 | writeb(CONTROL_ENABLE, dc_timer_dev.base + CONTROL(TIMER_B)); | ||
| 182 | |||
| 183 | sched_clock_register(digicolor_timer_sched_read, 32, rate); | ||
| 184 | clocksource_mmio_init(dc_timer_dev.base + COUNT(TIMER_B), node->name, | ||
| 185 | rate, 340, 32, clocksource_mmio_readl_down); | ||
| 186 | |||
| 187 | ret = request_irq(irq, digicolor_timer_interrupt, | ||
| 188 | IRQF_TIMER | IRQF_IRQPOLL, "digicolor_timerC", | ||
| 189 | &dc_timer_dev.ce); | ||
| 190 | if (ret) | ||
| 191 | pr_warn("request of timer irq %d failed (%d)\n", irq, ret); | ||
| 192 | |||
| 193 | dc_timer_dev.ce.cpumask = cpu_possible_mask; | ||
| 194 | dc_timer_dev.ce.irq = irq; | ||
| 195 | |||
| 196 | clockevents_config_and_register(&dc_timer_dev.ce, rate, 0, 0xffffffff); | ||
| 197 | } | ||
| 198 | CLOCKSOURCE_OF_DECLARE(conexant_digicolor, "cnxt,cx92755-timer", | ||
| 199 | digicolor_timer_init); | ||
diff --git a/drivers/clocksource/versatile.c b/drivers/clocksource/versatile.c index 2798e7492234..0a26d3dde6c0 100644 --- a/drivers/clocksource/versatile.c +++ b/drivers/clocksource/versatile.c | |||
| @@ -36,5 +36,7 @@ static void __init versatile_sched_clock_init(struct device_node *node) | |||
| 36 | 36 | ||
| 37 | sched_clock_register(versatile_sys_24mhz_read, 32, 24000000); | 37 | sched_clock_register(versatile_sys_24mhz_read, 32, 24000000); |
| 38 | } | 38 | } |
| 39 | CLOCKSOURCE_OF_DECLARE(versatile, "arm,vexpress-sysreg", | 39 | CLOCKSOURCE_OF_DECLARE(vexpress, "arm,vexpress-sysreg", |
| 40 | versatile_sched_clock_init); | ||
| 41 | CLOCKSOURCE_OF_DECLARE(versatile, "arm,versatile-sysreg", | ||
| 40 | versatile_sched_clock_init); | 42 | versatile_sched_clock_init); |
