[7/8,v2] clocksource/drivers/fttmr010: Merge Moxa into FTTMR010

Message ID 20170518201705.28790-7-linus.walleij@linaro.org
State New
Headers show
Series
  • [1/8,v2] clocksource/drivers/fttmr010: Fix the clock handling
Related show

Commit Message

Linus Walleij May 18, 2017, 8:17 p.m.
This merges the Moxa Art timer driver into the Faraday FTTMR010
driver and replaces all Kconfig symbols to use the Faraday
driver instead. We are now so similar that the drivers can
be merged by just adding a few lines to the Faraday timer.

Differences:

- The Faraday driver explicitly sets the counter to count
  upwards for the clocksource, removing the need for the
  clocksource core to invert the value.

- The Faraday driver also handles sched_clock()

On the Aspeed, the counter can only count downwards, so support
the timers in downward-counting mode as well, and flag the
Aspeed to use this mode. This mode was tested on the Gemini so
I have high hopes that it'll work fine on the Aspeed as well.

After this we have one driver for all three SoCs and a generic
Faraday FTTMR010 timer driver, which is nice.

Cc: Joel Stanley <joel@jms.id.au>
Cc: Jonas Jensen <jonas.jensen@gmail.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>

---
ChangeLog v1->v2:
- As it appears that the Aspeed timers can only count downwards,
  augment the code to deal with downward counting clockevent,
  clock source and sched_clock(), and flag the Aspeed for this
  mode.

ARM SoC folks: please ACK this so it can be merged with in the
clocksource subsystem once it works.

Joel: it would be super if you can test this. If you have some
vendor tree or similar that actually indicates where the
up/down counter bits are it's even better, but I'm hoping that
this half-assed guesswork will JustWork(TM) (yeah, famous
last words, sorry...)
---
 arch/arm/mach-aspeed/Kconfig         |   2 +-
 arch/arm/mach-moxart/Kconfig         |   2 +-
 drivers/clocksource/Kconfig          |   7 -
 drivers/clocksource/Makefile         |   1 -
 drivers/clocksource/moxart_timer.c   | 256 -----------------------------------
 drivers/clocksource/timer-fttmr010.c | 143 ++++++++++++++-----
 6 files changed, 108 insertions(+), 303 deletions(-)
 delete mode 100644 drivers/clocksource/moxart_timer.c

-- 
2.9.3

Comments

Joel Stanley May 19, 2017, 5:14 a.m. | #1
On Fri, May 19, 2017 at 4:17 AM, Linus Walleij <linus.walleij@linaro.org> wrote:
> This merges the Moxa Art timer driver into the Faraday FTTMR010

> driver and replaces all Kconfig symbols to use the Faraday

> driver instead. We are now so similar that the drivers can

> be merged by just adding a few lines to the Faraday timer.

>

> Differences:

>

> - The Faraday driver explicitly sets the counter to count

>   upwards for the clocksource, removing the need for the

>   clocksource core to invert the value.

>

> - The Faraday driver also handles sched_clock()

>

> On the Aspeed, the counter can only count downwards, so support

> the timers in downward-counting mode as well, and flag the

> Aspeed to use this mode. This mode was tested on the Gemini so

> I have high hopes that it'll work fine on the Aspeed as well.

>

> After this we have one driver for all three SoCs and a generic

> Faraday FTTMR010 timer driver, which is nice.

>

> Cc: Joel Stanley <joel@jms.id.au>

> Cc: Jonas Jensen <jonas.jensen@gmail.com>

> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>

> ---

> ChangeLog v1->v2:

> - As it appears that the Aspeed timers can only count downwards,

>   augment the code to deal with downward counting clockevent,

>   clock source and sched_clock(), and flag the Aspeed for this

>   mode.

>

> ARM SoC folks: please ACK this so it can be merged with in the

> clocksource subsystem once it works.

>

> Joel: it would be super if you can test this. If you have some

> vendor tree or similar that actually indicates where the

> up/down counter bits are it's even better, but I'm hoping that

> this half-assed guesswork will JustWork(TM) (yeah, famous

> last words, sorry...)


I gave it a spin on the ast2500-evb and it worked for me.

I also gave it a read through and it looks good. I'd lowercase the
shouty FTTMR010-TIMER message that pops up during boot, but it's no
biggie.

Reviewed-by: Joel Stanley <joel@jms.id.au>

Tested-by: Joel Stanley <joel@jms.id.au>


Cheers,

Joel
Jonas Jensen May 19, 2017, 1:23 p.m. | #2
On 18 May 2017 at 22:17, Linus Walleij <linus.walleij@linaro.org> wrote:
> ChangeLog v1->v2:

> - As it appears that the Aspeed timers can only count downwards,

>   augment the code to deal with downward counting clockevent,

>   clock source and sched_clock(), and flag the Aspeed for this

>   mode.


Adding another successful spin from me using v2 of the complete series [1].

[1] https://bitbucket.org/Kasreyn/linux-next/commits/78456248df9eeb48c963b574f50c7268713ef0b1
Uncompressing Linux... done, booting the kernel.
[    0.000000] Booting Linux on physical CPU 0x0
[    0.000000] Linux version 4.12.0-rc1-next-20170518-00010-g7845624
(i@ildjarn) (gcc version 4.9.1 (crosstool-NG 1.20.0) ) #4486 PREEMPT
Fri May 19 14:04:54 CEST 2017
[    0.000000] CPU: FA526 [66015261] revision 1 (ARMv4), cr=0000397f
[    0.000000] CPU: VIVT data cache, VIVT instruction cache
[    0.000000] OF: fdt: Machine model: MOXA UC-7112-LX
[    0.000000] bootconsole [earlycon0] enabled
[    0.000000] Memory policy: Data cache writeback
[    0.000000] Built 1 zonelists in Zone order, mobility grouping on.
Total pages: 8128
[    0.000000] Kernel command line: console=ttyS0,115200n8 earlyprintk
root=/dev/mmcblk0p1 rw rootwait
[    0.000000] PID hash table entries: 128 (order: -3, 512 bytes)
[    0.000000] Dentry cache hash table entries: 4096 (order: 2, 16384 bytes)
[    0.000000] Inode-cache hash table entries: 2048 (order: 1, 8192 bytes)
[    0.000000] Memory: 19132K/32768K available (3453K kernel code,
157K rwdata, 728K rodata, 192K init, 8587K bss, 13636K reserved, 0K
cma-reserved)
[    0.000000] Virtual kernel memory layout:
[    0.000000]     vector  : 0xffff0000 - 0xffff1000   (   4 kB)
[    0.000000]     fixmap  : 0xffc00000 - 0xfff00000   (3072 kB)
[    0.000000]     vmalloc : 0xc2800000 - 0xff800000   ( 976 MB)
[    0.000000]     lowmem  : 0xc0000000 - 0xc2000000   (  32 MB)
[    0.000000]       .text : 0xc0008000 - 0xc0367910   (3455 kB)
[    0.000000]       .init : 0xc043a000 - 0xc046a000   ( 192 kB)
[    0.000000]       .data : 0xc046a000 - 0xc0491560   ( 158 kB)
[    0.000000]        .bss : 0xc0495cdc - 0xc0cf8c4c   (8588 kB)
[    0.000000] SLUB: HWalign=32, Order=0-3, MinObjects=0, CPUs=1, Nodes=1
[    0.000000] Running RCU self tests
[    0.000000] Preemptible hierarchical RCU implementation.
[    0.000000] RCU lockdep checking is enabled.
[    0.000000] NR_IRQS:16 nr_irqs:16 16
[    0.000000] clocksource: FTTMR010-TIMER2: mask: 0xffffffff
max_cycles: 0xffffffff, max_idle_ns: 39817925974 ns
[    0.000089] sched_clock: 32 bits at 48MHz, resolution 20ns, wraps
every 44739242997ns
[    0.009440] Lock dependency validator: Copyright (c) 2006 Red Hat,
Inc., Ingo Molnar
[    0.017790] ... MAX_LOCKDEP_SUBCLASSES:  8
[    0.024511] ... MAX_LOCK_DEPTH:          48
[    0.029422] ... MAX_LOCKDEP_KEYS:        8191
[    0.034102] ... CLASSHASH_SIZE:          4096
[    0.039125] ... MAX_LOCKDEP_ENTRIES:     32768
[    0.043886] ... MAX_LOCKDEP_CHAINS:      65536
[    0.048627] ... CHAINHASH_SIZE:          32768
[    0.053753]  memory used by lock dependency info: 5167 kB
[    0.059859]  per task-struct memory footprint: 1536 bytes
[    0.072501] kmemleak: Kernel memory leak detector disabled
[    0.090759] kmemleak: Early log buffer exceeded (1229), please
increase DEBUG_KMEMLEAK_EARLY_LOG_SIZE
[    0.100997] Calibrating delay loop... 143.76 BogoMIPS (lpj=718848)
[    0.175633] pid_max: default: 4096 minimum: 301
[    0.183379] Mount-cache hash table entries: 1024 (order: 0, 4096 bytes)
[    0.191120] Mountpoint-cache hash table entries: 1024 (order: 0, 4096 bytes)
[    0.217285] CPU: Testing write buffer coherency: ok
[    0.249546] Setting up static identity map for 0x8200 - 0x8248
[    0.261082] Hierarchical SRCU implementation.
[    0.329338] devtmpfs: initialized
[    0.409206] DMA-API: preallocated 4096 debug entries
[    0.414554] DMA-API: debugging enabled by kernel config
[    0.445030] kworker/u2:0 (14) used greatest stack depth: 6224 bytes left
[    0.455985] clocksource: jiffies: mask: 0xffffffff max_cycles:
0xffffffff, max_idle_ns: 19112604462750000 ns
[    0.467884] futex hash table entries: 16 (order: -3, 704 bytes)
[    0.497799] NET: Registered protocol family 16
[    0.517950] DMA: preallocated 256 KiB pool for atomic coherent allocations
[    0.557803] kworker/u2:1 (17) used greatest stack depth: 5856 bytes left
[    1.316981] clocksource: Switched to clocksource FTTMR010-TIMER2
[    1.415454] NET: Registered protocol family 2
[    1.440312] TCP established hash table entries: 1024 (order: 0, 4096 bytes)
[    1.449574] TCP bind hash table entries: 1024 (order: 3, 36864 bytes)
[    1.460123] TCP: Hash tables configured (established 1024 bind 1024)
[    1.471541] UDP hash table entries: 128 (order: 1, 10240 bytes)
[    1.479360] UDP-Lite hash table entries: 128 (order: 1, 10240 bytes)
[    1.493097] NET: Registered protocol family 1
[    1.545526] workingset: timestamp_bits=30 max_order=13 bucket_order=0
[    2.049607] jffs2: version 2.2. (NAND) © 2001-2006 Red Hat, Inc.
[    2.186448] io scheduler noop registered
[    2.199676] io scheduler cfq registered (default)
[    2.204813] io scheduler mq-deadline registered
[    2.212844] io scheduler kyber registered
[    2.273164] ftgpio010-gpio 98700000.gpio: FTGPIO010 @c289e000 registered
[    2.311358] Serial: 8250/16550 driver, 1 ports, IRQ sharing enabled
[    2.392108] console [ttyS0] disabled
[    2.403351] 98200000.uart: ttyS0 at MMIO 0x98200000 (irq = 21,
base_baud = 921600) is a 16550A
[    2.417906] console [ttyS0] enabled
[    2.417906] console [ttyS0] enabled
[    2.425654] bootconsole [earlycon0] disabled
[    2.425654] bootconsole [earlycon0] disabled
[    2.508660] 80000000.flash: Found 1 x16 devices at 0x0 in 16-bit
bank. Manufacturer ID 0x000089 Chip ID 0x000018
[    2.520534] Intel/Sharp Extended Query Table at 0x0031
[    2.525990] Intel/Sharp Extended Query Table at 0x0031
[    2.531858] Using buffer write method
[    2.535736] cfi_cmdset_0001: Erase suspend on write enabled
[    2.543134] 4 ofpart partitions found on MTD device 80000000.flash
[    2.550035] Creating 4 MTD partitions on "80000000.flash":
[    2.555941] 0x000000000000-0x000000040000 : "bootloader"
[    2.563134] random: fast init done
[    2.634452] 0x000000040000-0x000000200000 : "linux kernel"
[    2.686438] 0x000000200000-0x000000a00000 : "root filesystem"
[    2.744918] 0x000000a00000-0x000001000000 : "user filesystem"
[    3.460849] libphy: MOXA ART Ethernet MII: probed
[    4.147623] libphy: MOXA ART Ethernet MII: probed
[    4.194338] libphy: Fixed MDIO Bus: probed
[    4.316628] moxart-rtc 90000000.soc:rtc: rtc core: registered
90000000.soc:rtc as rtc0
[    4.396418] sdhci: Secure Digital Host Controller Interface driver
[    4.406095] sdhci: Copyright(c) Pierre Ossman
[    4.500912] sdhci-pltfm: SDHCI platform and OF driver helper
[    4.563980] NET: Registered protocol family 17
[    4.575489] mmc0: new SD card at address e624
[    4.662702] mmcblk0: mmc0:e624 SD02G 1.84 GiB
[    4.722683]  mmcblk0: p1
[    4.733139] console [netcon0] enabled
[    4.748281] netconsole: network logging started
[    4.778193] moxart-rtc 90000000.soc:rtc: setting system clock to
2017-05-19 12:59:09 UTC (1495198749)
[    4.835127] EXT4-fs (mmcblk0p1): mounting ext3 file system using
the ext4 subsystem
[    6.235992] EXT4-fs (mmcblk0p1): recovery complete
[    6.254832] EXT4-fs (mmcblk0p1): mounted filesystem with ordered
data mode. Opts: (null)
[    6.265796] VFS: Mounted root (ext3 filesystem) on device 179:1.
[    6.373316] devtmpfs: mounted
[    6.410728] Freeing unused kernel memory: 192K
[    6.415430] This architecture does not have kernel memory protection.
INIT: version 2.88 booting


Tested-by: Jonas Jensen <jonas.jensen@gmail.com>



   Jonas
Linus Walleij May 19, 2017, 1:58 p.m. | #3
On Thu, May 18, 2017 at 10:17 PM, Linus Walleij
<linus.walleij@linaro.org> wrote:

> This merges the Moxa Art timer driver into the Faraday FTTMR010

> driver and replaces all Kconfig symbols to use the Faraday

> driver instead. We are now so similar that the drivers can

> be merged by just adding a few lines to the Faraday timer.


OK Daniel, the series is tested clear by Joel and Jonas,
feel free to merge these patches up to and including this #7
when you feel they look good.

Patch #8 I will send to the ARM SoC people pronto.

Yours,
Linus Walleij
Daniel Lezcano May 25, 2017, 6:33 p.m. | #4
On 19/05/2017 15:58, Linus Walleij wrote:
> On Thu, May 18, 2017 at 10:17 PM, Linus Walleij

> <linus.walleij@linaro.org> wrote:

> 

>> This merges the Moxa Art timer driver into the Faraday FTTMR010

>> driver and replaces all Kconfig symbols to use the Faraday

>> driver instead. We are now so similar that the drivers can

>> be merged by just adding a few lines to the Faraday timer.

> 

> OK Daniel, the series is tested clear by Joel and Jonas,

> feel free to merge these patches up to and including this #7

> when you feel they look good.

> 

> Patch #8 I will send to the ARM SoC people pronto.


Hi Linus,

patches 1-7 is applied for 4.13.

I assumed V1 Rob's ack applies also for the V2.

Fixed a trivial conflict with the Kconfig.

Thanks!

  -- Daniel

-- 
 <http://www.linaro.org/> Linaro.org │ Open source software for ARM SoCs

Follow Linaro:  <http://www.facebook.com/pages/Linaro> Facebook |
<http://twitter.com/#!/linaroorg> Twitter |
<http://www.linaro.org/linaro-blog/> Blog

Patch hide | download patch | download mbox

diff --git a/arch/arm/mach-aspeed/Kconfig b/arch/arm/mach-aspeed/Kconfig
index f3f8c5c658db..2d5570e6e186 100644
--- a/arch/arm/mach-aspeed/Kconfig
+++ b/arch/arm/mach-aspeed/Kconfig
@@ -4,7 +4,7 @@  menuconfig ARCH_ASPEED
 	select SRAM
 	select WATCHDOG
 	select ASPEED_WATCHDOG
-	select MOXART_TIMER
+	select FTTMR010_TIMER
 	select MFD_SYSCON
 	select PINCTRL
 	help
diff --git a/arch/arm/mach-moxart/Kconfig b/arch/arm/mach-moxart/Kconfig
index 70db2abf6163..a4a91f9a3301 100644
--- a/arch/arm/mach-moxart/Kconfig
+++ b/arch/arm/mach-moxart/Kconfig
@@ -4,7 +4,7 @@  menuconfig ARCH_MOXART
 	select CPU_FA526
 	select ARM_DMA_MEM_BUFFERABLE
 	select FARADAY_FTINTC010
-	select MOXART_TIMER
+	select FTTMR010_TIMER
 	select GPIOLIB
 	select PHYLIB if NETDEVICES
 	help
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index 545d541ae20e..1b22ade4c8f1 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -188,13 +188,6 @@  config ATLAS7_TIMER
 	help
 	  Enables support for the Atlas7 timer.
 
-config MOXART_TIMER
-	bool "Moxart timer driver" if COMPILE_TEST
-	depends on GENERIC_CLOCKEVENTS
-	select CLKSRC_MMIO
-	help
-	  Enables support for the Moxart timer.
-
 config MXS_TIMER
 	bool "Mxs timer driver" if COMPILE_TEST
 	depends on GENERIC_CLOCKEVENTS
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index 2b5b56a6f00f..cf0c30b6ec1f 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -26,7 +26,6 @@  obj-$(CONFIG_ORION_TIMER)	+= time-orion.o
 obj-$(CONFIG_BCM2835_TIMER)	+= bcm2835_timer.o
 obj-$(CONFIG_CLPS711X_TIMER)	+= clps711x-timer.o
 obj-$(CONFIG_ATLAS7_TIMER)	+= timer-atlas7.o
-obj-$(CONFIG_MOXART_TIMER)	+= moxart_timer.o
 obj-$(CONFIG_MXS_TIMER)		+= mxs_timer.o
 obj-$(CONFIG_CLKSRC_PXA)	+= pxa_timer.o
 obj-$(CONFIG_PRIMA2_TIMER)	+= timer-prima2.o
diff --git a/drivers/clocksource/moxart_timer.c b/drivers/clocksource/moxart_timer.c
deleted file mode 100644
index 7f3430654fbd..000000000000
--- a/drivers/clocksource/moxart_timer.c
+++ /dev/null
@@ -1,256 +0,0 @@ 
-/*
- * MOXA ART SoCs timer handling.
- *
- * Copyright (C) 2013 Jonas Jensen
- *
- * Jonas Jensen <jonas.jensen@gmail.com>
- *
- * This file is licensed under the terms of the GNU General Public
- * License version 2.  This program is licensed "as is" without any
- * warranty of any kind, whether express or implied.
- */
-
-#include <linux/clk.h>
-#include <linux/clockchips.h>
-#include <linux/interrupt.h>
-#include <linux/irq.h>
-#include <linux/irqreturn.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/of_irq.h>
-#include <linux/io.h>
-#include <linux/clocksource.h>
-#include <linux/bitops.h>
-#include <linux/slab.h>
-
-#define TIMER1_BASE		0x00
-#define TIMER2_BASE		0x10
-#define TIMER3_BASE		0x20
-
-#define REG_COUNT		0x0 /* writable */
-#define REG_LOAD		0x4
-#define REG_MATCH1		0x8
-#define REG_MATCH2		0xC
-
-#define TIMER_CR		0x30
-#define TIMER_INTR_STATE	0x34
-#define TIMER_INTR_MASK		0x38
-
-/*
- * Moxart TIMER_CR flags:
- *
- * MOXART_CR_*_CLOCK	0: PCLK, 1: EXT1CLK
- * MOXART_CR_*_INT	overflow interrupt enable bit
- */
-#define MOXART_CR_1_ENABLE	BIT(0)
-#define MOXART_CR_1_CLOCK	BIT(1)
-#define MOXART_CR_1_INT	BIT(2)
-#define MOXART_CR_2_ENABLE	BIT(3)
-#define MOXART_CR_2_CLOCK	BIT(4)
-#define MOXART_CR_2_INT	BIT(5)
-#define MOXART_CR_3_ENABLE	BIT(6)
-#define MOXART_CR_3_CLOCK	BIT(7)
-#define MOXART_CR_3_INT	BIT(8)
-#define MOXART_CR_COUNT_UP	BIT(9)
-
-#define MOXART_TIMER1_ENABLE	(MOXART_CR_2_ENABLE | MOXART_CR_1_ENABLE)
-#define MOXART_TIMER1_DISABLE	(MOXART_CR_2_ENABLE)
-
-/*
- * The ASpeed variant of the IP block has a different layout
- * for the control register
- */
-#define ASPEED_CR_1_ENABLE	BIT(0)
-#define ASPEED_CR_1_CLOCK	BIT(1)
-#define ASPEED_CR_1_INT		BIT(2)
-#define ASPEED_CR_2_ENABLE	BIT(4)
-#define ASPEED_CR_2_CLOCK	BIT(5)
-#define ASPEED_CR_2_INT		BIT(6)
-#define ASPEED_CR_3_ENABLE	BIT(8)
-#define ASPEED_CR_3_CLOCK	BIT(9)
-#define ASPEED_CR_3_INT		BIT(10)
-
-#define ASPEED_TIMER1_ENABLE   (ASPEED_CR_2_ENABLE | ASPEED_CR_1_ENABLE)
-#define ASPEED_TIMER1_DISABLE  (ASPEED_CR_2_ENABLE)
-
-struct moxart_timer {
-	void __iomem *base;
-	unsigned int t1_disable_val;
-	unsigned int t1_enable_val;
-	unsigned int count_per_tick;
-	struct clock_event_device clkevt;
-};
-
-static inline struct moxart_timer *to_moxart(struct clock_event_device *evt)
-{
-	return container_of(evt, struct moxart_timer, clkevt);
-}
-
-static inline void moxart_disable(struct clock_event_device *evt)
-{
-	struct moxart_timer *timer = to_moxart(evt);
-
-	writel(timer->t1_disable_val, timer->base + TIMER_CR);
-}
-
-static inline void moxart_enable(struct clock_event_device *evt)
-{
-	struct moxart_timer *timer = to_moxart(evt);
-
-	writel(timer->t1_enable_val, timer->base + TIMER_CR);
-}
-
-static int moxart_shutdown(struct clock_event_device *evt)
-{
-	moxart_disable(evt);
-	return 0;
-}
-
-static int moxart_set_oneshot(struct clock_event_device *evt)
-{
-	moxart_disable(evt);
-	writel(~0, to_moxart(evt)->base + TIMER1_BASE + REG_LOAD);
-	return 0;
-}
-
-static int moxart_set_periodic(struct clock_event_device *evt)
-{
-	struct moxart_timer *timer = to_moxart(evt);
-
-	moxart_disable(evt);
-	writel(timer->count_per_tick, timer->base + TIMER1_BASE + REG_LOAD);
-	writel(0, timer->base + TIMER1_BASE + REG_MATCH1);
-	moxart_enable(evt);
-	return 0;
-}
-
-static int moxart_clkevt_next_event(unsigned long cycles,
-				    struct clock_event_device *evt)
-{
-	struct moxart_timer *timer = to_moxart(evt);
-	u32 u;
-
-	moxart_disable(evt);
-
-	u = readl(timer->base + TIMER1_BASE + REG_COUNT) - cycles;
-	writel(u, timer->base + TIMER1_BASE + REG_MATCH1);
-
-	moxart_enable(evt);
-
-	return 0;
-}
-
-static irqreturn_t moxart_timer_interrupt(int irq, void *dev_id)
-{
-	struct clock_event_device *evt = dev_id;
-	evt->event_handler(evt);
-	return IRQ_HANDLED;
-}
-
-static int __init moxart_timer_init(struct device_node *node)
-{
-	int ret, irq;
-	unsigned long pclk;
-	struct clk *clk;
-	struct moxart_timer *timer;
-
-	timer = kzalloc(sizeof(*timer), GFP_KERNEL);
-	if (!timer)
-		return -ENOMEM;
-
-	timer->base = of_iomap(node, 0);
-	if (!timer->base) {
-		pr_err("%s: of_iomap failed\n", node->full_name);
-		ret = -ENXIO;
-		goto out_free;
-	}
-
-	irq = irq_of_parse_and_map(node, 0);
-	if (irq <= 0) {
-		pr_err("%s: irq_of_parse_and_map failed\n", node->full_name);
-		ret = -EINVAL;
-		goto out_unmap;
-	}
-
-	clk = of_clk_get(node, 0);
-	if (IS_ERR(clk))  {
-		pr_err("%s: of_clk_get failed\n", node->full_name);
-		ret = PTR_ERR(clk);
-		goto out_unmap;
-	}
-
-	pclk = clk_get_rate(clk);
-
-	if (of_device_is_compatible(node, "moxa,moxart-timer")) {
-		timer->t1_enable_val = MOXART_TIMER1_ENABLE;
-		timer->t1_disable_val = MOXART_TIMER1_DISABLE;
-	} else if (of_device_is_compatible(node, "aspeed,ast2400-timer")) {
-		timer->t1_enable_val = ASPEED_TIMER1_ENABLE;
-		timer->t1_disable_val = ASPEED_TIMER1_DISABLE;
-	} else {
-		pr_err("%s: unknown platform\n", node->full_name);
-		ret = -EINVAL;
-		goto out_unmap;
-	}
-
-	timer->count_per_tick = DIV_ROUND_CLOSEST(pclk, HZ);
-
-	timer->clkevt.name = node->name;
-	timer->clkevt.rating = 200;
-	timer->clkevt.features = CLOCK_EVT_FEAT_PERIODIC |
-					CLOCK_EVT_FEAT_ONESHOT;
-	timer->clkevt.set_state_shutdown = moxart_shutdown;
-	timer->clkevt.set_state_periodic = moxart_set_periodic;
-	timer->clkevt.set_state_oneshot = moxart_set_oneshot;
-	timer->clkevt.tick_resume = moxart_set_oneshot;
-	timer->clkevt.set_next_event = moxart_clkevt_next_event;
-	timer->clkevt.cpumask = cpumask_of(0);
-	timer->clkevt.irq = irq;
-
-	ret = clocksource_mmio_init(timer->base + TIMER2_BASE + REG_COUNT,
-				    "moxart_timer", pclk, 200, 32,
-				    clocksource_mmio_readl_down);
-	if (ret) {
-		pr_err("%s: clocksource_mmio_init failed\n", node->full_name);
-		goto out_unmap;
-	}
-
-	ret = request_irq(irq, moxart_timer_interrupt, IRQF_TIMER,
-			  node->name, &timer->clkevt);
-	if (ret) {
-		pr_err("%s: setup_irq failed\n", node->full_name);
-		goto out_unmap;
-	}
-
-	/* Clear match registers */
-	writel(0, timer->base + TIMER1_BASE + REG_MATCH1);
-	writel(0, timer->base + TIMER1_BASE + REG_MATCH2);
-	writel(0, timer->base + TIMER2_BASE + REG_MATCH1);
-	writel(0, timer->base + TIMER2_BASE + REG_MATCH2);
-
-	/*
-	 * Start timer 2 rolling as our main wall clock source, keep timer 1
-	 * disabled
-	 */
-	writel(0, timer->base + TIMER_CR);
-	writel(~0, timer->base + TIMER2_BASE + REG_LOAD);
-	writel(timer->t1_disable_val, timer->base + TIMER_CR);
-
-	/*
-	 * documentation is not publicly available:
-	 * min_delta / max_delta obtained by trial-and-error,
-	 * max_delta 0xfffffffe should be ok because count
-	 * register size is u32
-	 */
-	clockevents_config_and_register(&timer->clkevt, pclk, 0x4, 0xfffffffe);
-
-	return 0;
-
-out_unmap:
-	iounmap(timer->base);
-out_free:
-	kfree(timer);
-	return ret;
-}
-CLOCKSOURCE_OF_DECLARE(moxart, "moxa,moxart-timer", moxart_timer_init);
-CLOCKSOURCE_OF_DECLARE(aspeed, "aspeed,ast2400-timer", moxart_timer_init);
diff --git a/drivers/clocksource/timer-fttmr010.c b/drivers/clocksource/timer-fttmr010.c
index 2d915d1455ab..f8801507a687 100644
--- a/drivers/clocksource/timer-fttmr010.c
+++ b/drivers/clocksource/timer-fttmr010.c
@@ -50,6 +50,20 @@ 
 #define TIMER_2_CR_UPDOWN	BIT(10)
 #define TIMER_3_CR_UPDOWN	BIT(11)
 
+/*
+ * The Aspeed AST2400 moves bits around in the control register
+ * and lacks bits for setting the timer to count upwards.
+ */
+#define TIMER_1_CR_ASPEED_ENABLE	BIT(0)
+#define TIMER_1_CR_ASPEED_CLOCK		BIT(1)
+#define TIMER_1_CR_ASPEED_INT		BIT(2)
+#define TIMER_2_CR_ASPEED_ENABLE	BIT(4)
+#define TIMER_2_CR_ASPEED_CLOCK		BIT(5)
+#define TIMER_2_CR_ASPEED_INT		BIT(6)
+#define TIMER_3_CR_ASPEED_ENABLE	BIT(8)
+#define TIMER_3_CR_ASPEED_CLOCK		BIT(9)
+#define TIMER_3_CR_ASPEED_INT		BIT(10)
+
 #define TIMER_1_INT_MATCH1	BIT(0)
 #define TIMER_1_INT_MATCH2	BIT(1)
 #define TIMER_1_INT_OVERFLOW	BIT(2)
@@ -64,6 +78,8 @@ 
 struct fttmr010 {
 	void __iomem *base;
 	unsigned int tick_rate;
+	bool count_down;
+	u32 t1_enable_val;
 	struct clock_event_device clkevt;
 };
 
@@ -77,6 +93,8 @@  static inline struct fttmr010 *to_fttmr010(struct clock_event_device *evt)
 
 static u64 notrace fttmr010_read_sched_clock(void)
 {
+	if (local_fttmr->count_down)
+		return ~readl(local_fttmr->base + TIMER2_COUNT);
 	return readl(local_fttmr->base + TIMER2_COUNT);
 }
 
@@ -86,11 +104,23 @@  static int fttmr010_timer_set_next_event(unsigned long cycles,
 	struct fttmr010 *fttmr010 = to_fttmr010(evt);
 	u32 cr;
 
-	/* Setup the match register */
+	/* Stop */
+	cr = readl(fttmr010->base + TIMER_CR);
+	cr &= ~fttmr010->t1_enable_val;
+	writel(cr, fttmr010->base + TIMER_CR);
+
+	/* Setup the match register forward/backward in time */
 	cr = readl(fttmr010->base + TIMER1_COUNT);
-	writel(cr + cycles, fttmr010->base + TIMER1_MATCH1);
-	if (readl(fttmr010->base + TIMER1_COUNT) - cr > cycles)
-		return -ETIME;
+	if (fttmr010->count_down)
+		cr -= cycles;
+	else
+		cr += cycles;
+	writel(cr, fttmr010->base + TIMER1_MATCH1);
+
+	/* Start */
+	cr = readl(fttmr010->base + TIMER_CR);
+	cr |= fttmr010->t1_enable_val;
+	writel(cr, fttmr010->base + TIMER_CR);
 
 	return 0;
 }
@@ -100,9 +130,9 @@  static int fttmr010_timer_shutdown(struct clock_event_device *evt)
 	struct fttmr010 *fttmr010 = to_fttmr010(evt);
 	u32 cr;
 
-	/* Stop timer and interrupt. */
+	/* Stop */
 	cr = readl(fttmr010->base + TIMER_CR);
-	cr &= ~(TIMER_1_CR_ENABLE | TIMER_1_CR_INT);
+	cr &= ~fttmr010->t1_enable_val;
 	writel(cr, fttmr010->base + TIMER_CR);
 
 	return 0;
@@ -113,14 +143,17 @@  static int fttmr010_timer_set_oneshot(struct clock_event_device *evt)
 	struct fttmr010 *fttmr010 = to_fttmr010(evt);
 	u32 cr;
 
-	/* Stop timer and interrupt. */
+	/* Stop */
 	cr = readl(fttmr010->base + TIMER_CR);
-	cr &= ~(TIMER_1_CR_ENABLE | TIMER_1_CR_INT);
+	cr &= ~fttmr010->t1_enable_val;
 	writel(cr, fttmr010->base + TIMER_CR);
 
-	/* Setup counter start from 0 */
+	/* Setup counter start from 0 or ~0 */
 	writel(0, fttmr010->base + TIMER1_COUNT);
-	writel(0, fttmr010->base + TIMER1_LOAD);
+	if (fttmr010->count_down)
+		writel(~0, fttmr010->base + TIMER1_LOAD);
+	else
+		writel(0, fttmr010->base + TIMER1_LOAD);
 
 	/* Enable interrupt */
 	cr = readl(fttmr010->base + TIMER_INTR_MASK);
@@ -128,11 +161,6 @@  static int fttmr010_timer_set_oneshot(struct clock_event_device *evt)
 	cr |= TIMER_1_INT_MATCH1;
 	writel(cr, fttmr010->base + TIMER_INTR_MASK);
 
-	/* Start the timer */
-	cr = readl(fttmr010->base + TIMER_CR);
-	cr |= TIMER_1_CR_ENABLE;
-	writel(cr, fttmr010->base + TIMER_CR);
-
 	return 0;
 }
 
@@ -142,26 +170,30 @@  static int fttmr010_timer_set_periodic(struct clock_event_device *evt)
 	u32 period = DIV_ROUND_CLOSEST(fttmr010->tick_rate, HZ);
 	u32 cr;
 
-	/* Stop timer and interrupt */
+	/* Stop */
 	cr = readl(fttmr010->base + TIMER_CR);
-	cr &= ~(TIMER_1_CR_ENABLE | TIMER_1_CR_INT);
+	cr &= ~fttmr010->t1_enable_val;
 	writel(cr, fttmr010->base + TIMER_CR);
 
-	/* Setup timer to fire at 1/HT intervals. */
-	cr = 0xffffffff - (period - 1);
-	writel(cr, fttmr010->base + TIMER1_COUNT);
-	writel(cr, fttmr010->base + TIMER1_LOAD);
-
-	/* enable interrupt on overflow */
-	cr = readl(fttmr010->base + TIMER_INTR_MASK);
-	cr &= ~(TIMER_1_INT_MATCH1 | TIMER_1_INT_MATCH2);
-	cr |= TIMER_1_INT_OVERFLOW;
-	writel(cr, fttmr010->base + TIMER_INTR_MASK);
+	/* Setup timer to fire at 1/HZ intervals. */
+	if (fttmr010->count_down) {
+		writel(period, fttmr010->base + TIMER1_LOAD);
+		writel(0, fttmr010->base + TIMER1_MATCH1);
+	} else {
+		cr = 0xffffffff - (period - 1);
+		writel(cr, fttmr010->base + TIMER1_COUNT);
+		writel(cr, fttmr010->base + TIMER1_LOAD);
+
+		/* Enable interrupt on overflow */
+		cr = readl(fttmr010->base + TIMER_INTR_MASK);
+		cr &= ~(TIMER_1_INT_MATCH1 | TIMER_1_INT_MATCH2);
+		cr |= TIMER_1_INT_OVERFLOW;
+		writel(cr, fttmr010->base + TIMER_INTR_MASK);
+	}
 
 	/* Start the timer */
 	cr = readl(fttmr010->base + TIMER_CR);
-	cr |= TIMER_1_CR_ENABLE;
-	cr |= TIMER_1_CR_INT;
+	cr |= fttmr010->t1_enable_val;
 	writel(cr, fttmr010->base + TIMER_CR);
 
 	return 0;
@@ -181,9 +213,11 @@  static irqreturn_t fttmr010_timer_interrupt(int irq, void *dev_id)
 static int __init fttmr010_timer_init(struct device_node *np)
 {
 	struct fttmr010 *fttmr010;
+	bool is_ast2400;
 	int irq;
 	struct clk *clk;
 	int ret;
+	u32 val;
 
 	/*
 	 * These implementations require a clock reference.
@@ -223,13 +257,37 @@  static int __init fttmr010_timer_init(struct device_node *np)
 	}
 
 	/*
+	 * The Aspeed AST2400 moves bits around in the control register,
+	 * otherwise it works the same.
+	 */
+	is_ast2400 = of_device_is_compatible(np, "aspeed,ast2400-timer");
+	if (is_ast2400) {
+		fttmr010->t1_enable_val = TIMER_1_CR_ASPEED_ENABLE |
+			TIMER_1_CR_ASPEED_INT;
+		/* Downward not available */
+		fttmr010->count_down = true;
+	} else {
+		fttmr010->t1_enable_val = TIMER_1_CR_ENABLE | TIMER_1_CR_INT;
+	}
+
+	/*
 	 * Reset the interrupt mask and status
 	 */
 	writel(TIMER_INT_ALL_MASK, fttmr010->base + TIMER_INTR_MASK);
 	writel(0, fttmr010->base + TIMER_INTR_STATE);
-	/* Enable timer 1 count up, timer 2 count up */
-	writel((TIMER_1_CR_UPDOWN | TIMER_2_CR_ENABLE | TIMER_2_CR_UPDOWN),
-	       fttmr010->base + TIMER_CR);
+
+	/*
+	 * Enable timer 1 count up, timer 2 count up, except on Aspeed,
+	 * where everything just counts down.
+	 */
+	if (is_ast2400)
+		val = TIMER_2_CR_ASPEED_ENABLE;
+	else {
+		val = TIMER_2_CR_ENABLE;
+		if (!fttmr010->count_down)
+			val |= TIMER_1_CR_UPDOWN | TIMER_2_CR_UPDOWN;
+	}
+	writel(val, fttmr010->base + TIMER_CR);
 
 	/*
 	 * Setup free-running clocksource timer (interrupts
@@ -237,13 +295,22 @@  static int __init fttmr010_timer_init(struct device_node *np)
 	 */
 	local_fttmr = fttmr010;
 	writel(0, fttmr010->base + TIMER2_COUNT);
-	writel(0, fttmr010->base + TIMER2_LOAD);
 	writel(0, fttmr010->base + TIMER2_MATCH1);
 	writel(0, fttmr010->base + TIMER2_MATCH2);
-	clocksource_mmio_init(fttmr010->base + TIMER2_COUNT,
-			      "FTTMR010-TIMER2",
-			      fttmr010->tick_rate,
-			      300, 32, clocksource_mmio_readl_up);
+
+	if (fttmr010->count_down) {
+		writel(~0, fttmr010->base + TIMER2_LOAD);
+		clocksource_mmio_init(fttmr010->base + TIMER2_COUNT,
+				      "FTTMR010-TIMER2",
+				      fttmr010->tick_rate,
+				      300, 32, clocksource_mmio_readl_down);
+	} else {
+		writel(0, fttmr010->base + TIMER2_LOAD);
+		clocksource_mmio_init(fttmr010->base + TIMER2_COUNT,
+				      "FTTMR010-TIMER2",
+				      fttmr010->tick_rate,
+				      300, 32, clocksource_mmio_readl_up);
+	}
 	sched_clock_register(fttmr010_read_sched_clock, 32,
 			     fttmr010->tick_rate);
 
@@ -290,3 +357,5 @@  static int __init fttmr010_timer_init(struct device_node *np)
 }
 CLOCKSOURCE_OF_DECLARE(fttmr010, "faraday,fttmr010", fttmr010_timer_init);
 CLOCKSOURCE_OF_DECLARE(gemini, "cortina,gemini-timer", fttmr010_timer_init);
+CLOCKSOURCE_OF_DECLARE(moxart, "moxa,moxart-timer", fttmr010_timer_init);
+CLOCKSOURCE_OF_DECLARE(aspeed, "aspeed,ast2400-timer", fttmr010_timer_init);