Message ID | 20220805061629.1207-1-stefan.herbrechtsmeier-oss@weidmueller.com |
---|---|
State | Accepted |
Commit | b7e0750d8872af4f294ec46533fe5243514dcb59 |
Delegated to: | Michal Simek |
Headers | show |
Series | zynq: Convert arm twd timer to DM driver | expand |
On 8/5/22 08:16, Stefan Herbrechtsmeier wrote: > From: Stefan Herbrechtsmeier <stefan.herbrechtsmeier@weidmueller.com> > > Move arm twd timer driver from zynq to generic location. > > DM timer drivers are designed differently to original driver. Timer is > counting up and not down. > Information about clock rates are find out in timer_pre_probe() that's > why there is no need to get any additional information from DT in the > driver itself (only register offset). > > Signed-off-by: Stefan Herbrechtsmeier <stefan.herbrechtsmeier@weidmueller.com> > > --- > > arch/arm/Kconfig | 3 + > arch/arm/dts/zynq-7000.dtsi | 1 + > arch/arm/mach-zynq/Makefile | 1 - > arch/arm/mach-zynq/clk.c | 6 +- > arch/arm/mach-zynq/timer.c | 113 ---------------------------------- > drivers/timer/Kconfig | 6 ++ > drivers/timer/Makefile | 1 + > drivers/timer/arm_twd_timer.c | 108 ++++++++++++++++++++++++++++++++ > 8 files changed, 123 insertions(+), 116 deletions(-) > delete mode 100644 arch/arm/mach-zynq/timer.c > create mode 100644 drivers/timer/arm_twd_timer.c > > diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig > index 949ebb46ba..bccdc6dfd8 100644 > --- a/arch/arm/Kconfig > +++ b/arch/arm/Kconfig > @@ -1249,6 +1249,7 @@ config ARCH_VF610 > > config ARCH_ZYNQ > bool "Xilinx Zynq based platform" > + select ARM_TWD_TIMER > select CLK > select CLK_ZYNQ > select CPU_V7A > @@ -1268,7 +1269,9 @@ config ARCH_ZYNQ > select SPL_DM_SPI_FLASH if SPL > select SPL_OF_CONTROL if SPL > select SPL_SEPARATE_BSS if SPL > + select SPL_TIMER if SPL > select SUPPORT_SPL > + select TIMER > imply ARCH_EARLY_INIT_R > imply BOARD_LATE_INIT > imply CMD_CLK > diff --git a/arch/arm/dts/zynq-7000.dtsi b/arch/arm/dts/zynq-7000.dtsi > index 37155df0fd..a7ca00fb76 100644 > --- a/arch/arm/dts/zynq-7000.dtsi > +++ b/arch/arm/dts/zynq-7000.dtsi > @@ -416,6 +416,7 @@ > }; > > scutimer: timer@f8f00600 { > + u-boot,dm-pre-reloc; > interrupt-parent = <&intc>; > interrupts = <1 13 0x301>; > compatible = "arm,cortex-a9-twd-timer"; > diff --git a/arch/arm/mach-zynq/Makefile b/arch/arm/mach-zynq/Makefile > index 8737f434d9..d9b2b999e1 100644 > --- a/arch/arm/mach-zynq/Makefile > +++ b/arch/arm/mach-zynq/Makefile > @@ -6,7 +6,6 @@ > # (C) Copyright 2008 > # Guennadi Liakhovetki, DENX Software Engineering, <lg@denx.de> > > -obj-y := timer.o > obj-y += cpu.o > obj-y += ddrc.o > obj-y += slcr.o > diff --git a/arch/arm/mach-zynq/clk.c b/arch/arm/mach-zynq/clk.c > index 27f6bf2183..1945f60e08 100644 > --- a/arch/arm/mach-zynq/clk.c > +++ b/arch/arm/mach-zynq/clk.c > @@ -52,10 +52,12 @@ int set_cpu_clk_info(void) > return ret; > > rate = clk_get_rate(&clk) / 1000000; > - if (i) > + if (i) { > gd->bd->bi_ddr_freq = rate; > - else > + } else { > gd->bd->bi_arm_freq = rate; > + gd->cpu_clk = clk_get_rate(&clk); > + } > > clk_free(&clk); > } > diff --git a/arch/arm/mach-zynq/timer.c b/arch/arm/mach-zynq/timer.c > deleted file mode 100644 > index a51822a530..0000000000 > --- a/arch/arm/mach-zynq/timer.c > +++ /dev/null > @@ -1,113 +0,0 @@ > -// SPDX-License-Identifier: GPL-2.0+ > -/* > - * Copyright (C) 2017 Weidmüller Interface GmbH & Co. KG > - * Stefan Herbrechtsmeier <stefan.herbrechtsmeier@weidmueller.com> > - * > - * Copyright (C) 2012 Michal Simek <monstr@monstr.eu> > - * Copyright (C) 2011-2017 Xilinx, Inc. All rights reserved. > - * > - * (C) Copyright 2008 > - * Guennadi Liakhovetki, DENX Software Engineering, <lg@denx.de> > - * > - * (C) Copyright 2004 > - * Philippe Robin, ARM Ltd. <philippe.robin@arm.com> > - * > - * (C) Copyright 2002-2004 > - * Gary Jennejohn, DENX Software Engineering, <gj@denx.de> > - * > - * (C) Copyright 2003 > - * Texas Instruments <www.ti.com> > - * > - * (C) Copyright 2002 > - * Sysgo Real-Time Solutions, GmbH <www.elinos.com> > - * Marius Groeger <mgroeger@sysgo.de> > - * > - * (C) Copyright 2002 > - * Sysgo Real-Time Solutions, GmbH <www.elinos.com> > - * Alex Zuepke <azu@sysgo.de> > - */ > - > -#include <clk.h> > -#include <common.h> > -#include <div64.h> > -#include <dm.h> > -#include <init.h> > -#include <time.h> > -#include <malloc.h> > -#include <asm/global_data.h> > -#include <asm/io.h> > -#include <asm/arch/hardware.h> > -#include <asm/arch/clk.h> > - > -DECLARE_GLOBAL_DATA_PTR; > - > -struct scu_timer { > - u32 load; /* Timer Load Register */ > - u32 counter; /* Timer Counter Register */ > - u32 control; /* Timer Control Register */ > -}; > - > -static struct scu_timer *timer_base = > - (struct scu_timer *)ZYNQ_SCUTIMER_BASEADDR; > - > -#define SCUTIMER_CONTROL_PRESCALER_MASK 0x0000FF00 /* Prescaler */ > -#define SCUTIMER_CONTROL_PRESCALER_SHIFT 8 > -#define SCUTIMER_CONTROL_AUTO_RELOAD_MASK 0x00000002 /* Auto-reload */ > -#define SCUTIMER_CONTROL_ENABLE_MASK 0x00000001 /* Timer enable */ > - > -#define TIMER_LOAD_VAL 0xFFFFFFFF > -#define TIMER_PRESCALE 255 > - > -int timer_init(void) > -{ > - const u32 emask = SCUTIMER_CONTROL_AUTO_RELOAD_MASK | > - (TIMER_PRESCALE << SCUTIMER_CONTROL_PRESCALER_SHIFT) | > - SCUTIMER_CONTROL_ENABLE_MASK; > - > - struct udevice *dev; > - struct clk clk; > - int ret; > - > - ret = uclass_get_device_by_driver(UCLASS_CLK, > - DM_DRIVER_GET(zynq_clk), &dev); > - if (ret) > - return ret; > - > - clk.id = cpu_6or4x_clk; > - ret = clk_request(dev, &clk); > - if (ret < 0) > - return ret; > - > - gd->cpu_clk = clk_get_rate(&clk); > - > - clk_free(&clk); > - > - gd->arch.timer_rate_hz = (gd->cpu_clk / 2) / (TIMER_PRESCALE + 1); > - > - /* Load the timer counter register */ > - writel(0xFFFFFFFF, &timer_base->load); > - > - /* > - * Start the A9Timer device > - * Enable Auto reload mode, Clear prescaler control bits > - * Set prescaler value, Enable the decrementer > - */ > - clrsetbits_le32(&timer_base->control, SCUTIMER_CONTROL_PRESCALER_MASK, > - emask); > - > - /* Reset time */ > - gd->arch.lastinc = readl(&timer_base->counter) / > - (gd->arch.timer_rate_hz / CONFIG_SYS_HZ); > - gd->arch.tbl = 0; > - > - return 0; > -} > - > -/* > - * This function is derived from PowerPC code (timebase clock frequency). > - * On ARM it returns the number of timer ticks per second. > - */ > -ulong get_tbclk(void) > -{ > - return gd->arch.timer_rate_hz; > -} > diff --git a/drivers/timer/Kconfig b/drivers/timer/Kconfig > index 20b5af7e26..1e2b6566c6 100644 > --- a/drivers/timer/Kconfig > +++ b/drivers/timer/Kconfig > @@ -73,6 +73,12 @@ config ARC_TIMER > usually at least one of them exists. Either of them is supported > in U-Boot. > > +config ARM_TWD_TIMER > + bool "ARM timer watchdog (TWD) timer support" > + depends on TIMER && CLK > + help > + Select this to enable support for the ARM global timer watchdog timer. > + > config AST_TIMER > bool "Aspeed ast2400/ast2500 timer support" > depends on TIMER > diff --git a/drivers/timer/Makefile b/drivers/timer/Makefile > index d9822a5370..4db03fe09c 100644 > --- a/drivers/timer/Makefile > +++ b/drivers/timer/Makefile > @@ -6,6 +6,7 @@ obj-y += timer-uclass.o > obj-$(CONFIG_ALTERA_TIMER) += altera_timer.o > obj-$(CONFIG_ANDES_PLMT_TIMER) += andes_plmt_timer.o > obj-$(CONFIG_ARC_TIMER) += arc_timer.o > +obj-$(CONFIG_ARM_TWD_TIMER) += arm_twd_timer.o > obj-$(CONFIG_AST_TIMER) += ast_timer.o > obj-$(CONFIG_ATCPIT100_TIMER) += atcpit100_timer.o > obj-$(CONFIG_$(SPL_)ATMEL_PIT_TIMER) += atmel_pit_timer.o > diff --git a/drivers/timer/arm_twd_timer.c b/drivers/timer/arm_twd_timer.c > new file mode 100644 > index 0000000000..40ccd16587 > --- /dev/null > +++ b/drivers/timer/arm_twd_timer.c > @@ -0,0 +1,108 @@ > +// SPDX-License-Identifier: GPL-2.0+ > +/* > + * Copyright (C) 2017-2022 Weidmüller Interface GmbH & Co. KG > + * Stefan Herbrechtsmeier <stefan.herbrechtsmeier@weidmueller.com> > + * > + * Copyright (C) 2012 Michal Simek <monstr@monstr.eu> > + * Copyright (C) 2011-2017 Xilinx, Inc. All rights reserved. > + * > + * (C) Copyright 2008 > + * Guennadi Liakhovetki, DENX Software Engineering, <lg@denx.de> > + * > + * (C) Copyright 2004 > + * Philippe Robin, ARM Ltd. <philippe.robin@arm.com> > + * > + * (C) Copyright 2002-2004 > + * Gary Jennejohn, DENX Software Engineering, <gj@denx.de> > + * > + * (C) Copyright 2003 > + * Texas Instruments <www.ti.com> > + * > + * (C) Copyright 2002 > + * Sysgo Real-Time Solutions, GmbH <www.elinos.com> > + * Marius Groeger <mgroeger@sysgo.de> > + * > + * (C) Copyright 2002 > + * Sysgo Real-Time Solutions, GmbH <www.elinos.com> > + * Alex Zuepke <azu@sysgo.de> > + */ > + > +#include <common.h> > +#include <dm.h> > +#include <fdtdec.h> > +#include <timer.h> > +#include <linux/bitops.h> > + > +#include <asm/io.h> > + > +#define SCUTIMER_CONTROL_PRESCALER_MASK 0x0000FF00 /* Prescaler */ > +#define SCUTIMER_CONTROL_AUTO_RELOAD_MASK 0x00000002 /* Auto-reload */ > +#define SCUTIMER_CONTROL_ENABLE_MASK 0x00000001 /* Timer enable */ > + > +#define TIMER_LOAD_VAL 0xFFFFFFFF > + > +struct arm_twd_timer_regs { > + u32 load; /* Timer Load Register */ > + u32 counter; /* Timer Counter Register */ > + u32 control; /* Timer Control Register */ > +}; > + > +struct arm_twd_timer_priv { > + struct arm_twd_timer_regs *base; > +}; > + > +static u64 arm_twd_timer_get_count(struct udevice *dev) > +{ > + struct arm_twd_timer_priv *priv = dev_get_priv(dev); > + struct arm_twd_timer_regs *regs = priv->base; > + u32 count = TIMER_LOAD_VAL - readl(®s->counter); > + > + return timer_conv_64(count); > +} > + > +static int arm_twd_timer_probe(struct udevice *dev) > +{ > + struct arm_twd_timer_priv *priv = dev_get_priv(dev); > + struct arm_twd_timer_regs *regs; > + fdt_addr_t addr; > + > + addr = dev_read_addr(dev); > + if (addr == FDT_ADDR_T_NONE) > + return -EINVAL; > + > + priv->base = (struct arm_twd_timer_regs *)addr; > + > + regs = priv->base; > + > + /* Load the timer counter register */ > + writel(0xFFFFFFFF, ®s->load); > + > + /* > + * Start the A9Timer device > + * Enable Auto reload mode, Clear prescaler control bits > + * Set prescaler value, Enable the decrementer > + */ > + clrsetbits_le32(®s->control, SCUTIMER_CONTROL_PRESCALER_MASK, > + SCUTIMER_CONTROL_AUTO_RELOAD_MASK | > + SCUTIMER_CONTROL_ENABLE_MASK); > + > + return 0; > +} > + > +static const struct timer_ops arm_twd_timer_ops = { > + .get_count = arm_twd_timer_get_count, > +}; > + > +static const struct udevice_id arm_twd_timer_ids[] = { > + { .compatible = "arm,cortex-a9-twd-timer" }, > + {} > +}; > + > +U_BOOT_DRIVER(arm_twd_timer) = { > + .name = "arm_twd_timer", > + .id = UCLASS_TIMER, > + .of_match = arm_twd_timer_ids, > + .priv_auto = sizeof(struct arm_twd_timer_priv), > + .probe = arm_twd_timer_probe, > + .ops = &arm_twd_timer_ops, > +}; Looks good to me and also tested on HW. Aoplied. M
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 949ebb46ba..bccdc6dfd8 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1249,6 +1249,7 @@ config ARCH_VF610 config ARCH_ZYNQ bool "Xilinx Zynq based platform" + select ARM_TWD_TIMER select CLK select CLK_ZYNQ select CPU_V7A @@ -1268,7 +1269,9 @@ config ARCH_ZYNQ select SPL_DM_SPI_FLASH if SPL select SPL_OF_CONTROL if SPL select SPL_SEPARATE_BSS if SPL + select SPL_TIMER if SPL select SUPPORT_SPL + select TIMER imply ARCH_EARLY_INIT_R imply BOARD_LATE_INIT imply CMD_CLK diff --git a/arch/arm/dts/zynq-7000.dtsi b/arch/arm/dts/zynq-7000.dtsi index 37155df0fd..a7ca00fb76 100644 --- a/arch/arm/dts/zynq-7000.dtsi +++ b/arch/arm/dts/zynq-7000.dtsi @@ -416,6 +416,7 @@ }; scutimer: timer@f8f00600 { + u-boot,dm-pre-reloc; interrupt-parent = <&intc>; interrupts = <1 13 0x301>; compatible = "arm,cortex-a9-twd-timer"; diff --git a/arch/arm/mach-zynq/Makefile b/arch/arm/mach-zynq/Makefile index 8737f434d9..d9b2b999e1 100644 --- a/arch/arm/mach-zynq/Makefile +++ b/arch/arm/mach-zynq/Makefile @@ -6,7 +6,6 @@ # (C) Copyright 2008 # Guennadi Liakhovetki, DENX Software Engineering, <lg@denx.de> -obj-y := timer.o obj-y += cpu.o obj-y += ddrc.o obj-y += slcr.o diff --git a/arch/arm/mach-zynq/clk.c b/arch/arm/mach-zynq/clk.c index 27f6bf2183..1945f60e08 100644 --- a/arch/arm/mach-zynq/clk.c +++ b/arch/arm/mach-zynq/clk.c @@ -52,10 +52,12 @@ int set_cpu_clk_info(void) return ret; rate = clk_get_rate(&clk) / 1000000; - if (i) + if (i) { gd->bd->bi_ddr_freq = rate; - else + } else { gd->bd->bi_arm_freq = rate; + gd->cpu_clk = clk_get_rate(&clk); + } clk_free(&clk); } diff --git a/arch/arm/mach-zynq/timer.c b/arch/arm/mach-zynq/timer.c deleted file mode 100644 index a51822a530..0000000000 --- a/arch/arm/mach-zynq/timer.c +++ /dev/null @@ -1,113 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2017 Weidmüller Interface GmbH & Co. KG - * Stefan Herbrechtsmeier <stefan.herbrechtsmeier@weidmueller.com> - * - * Copyright (C) 2012 Michal Simek <monstr@monstr.eu> - * Copyright (C) 2011-2017 Xilinx, Inc. All rights reserved. - * - * (C) Copyright 2008 - * Guennadi Liakhovetki, DENX Software Engineering, <lg@denx.de> - * - * (C) Copyright 2004 - * Philippe Robin, ARM Ltd. <philippe.robin@arm.com> - * - * (C) Copyright 2002-2004 - * Gary Jennejohn, DENX Software Engineering, <gj@denx.de> - * - * (C) Copyright 2003 - * Texas Instruments <www.ti.com> - * - * (C) Copyright 2002 - * Sysgo Real-Time Solutions, GmbH <www.elinos.com> - * Marius Groeger <mgroeger@sysgo.de> - * - * (C) Copyright 2002 - * Sysgo Real-Time Solutions, GmbH <www.elinos.com> - * Alex Zuepke <azu@sysgo.de> - */ - -#include <clk.h> -#include <common.h> -#include <div64.h> -#include <dm.h> -#include <init.h> -#include <time.h> -#include <malloc.h> -#include <asm/global_data.h> -#include <asm/io.h> -#include <asm/arch/hardware.h> -#include <asm/arch/clk.h> - -DECLARE_GLOBAL_DATA_PTR; - -struct scu_timer { - u32 load; /* Timer Load Register */ - u32 counter; /* Timer Counter Register */ - u32 control; /* Timer Control Register */ -}; - -static struct scu_timer *timer_base = - (struct scu_timer *)ZYNQ_SCUTIMER_BASEADDR; - -#define SCUTIMER_CONTROL_PRESCALER_MASK 0x0000FF00 /* Prescaler */ -#define SCUTIMER_CONTROL_PRESCALER_SHIFT 8 -#define SCUTIMER_CONTROL_AUTO_RELOAD_MASK 0x00000002 /* Auto-reload */ -#define SCUTIMER_CONTROL_ENABLE_MASK 0x00000001 /* Timer enable */ - -#define TIMER_LOAD_VAL 0xFFFFFFFF -#define TIMER_PRESCALE 255 - -int timer_init(void) -{ - const u32 emask = SCUTIMER_CONTROL_AUTO_RELOAD_MASK | - (TIMER_PRESCALE << SCUTIMER_CONTROL_PRESCALER_SHIFT) | - SCUTIMER_CONTROL_ENABLE_MASK; - - struct udevice *dev; - struct clk clk; - int ret; - - ret = uclass_get_device_by_driver(UCLASS_CLK, - DM_DRIVER_GET(zynq_clk), &dev); - if (ret) - return ret; - - clk.id = cpu_6or4x_clk; - ret = clk_request(dev, &clk); - if (ret < 0) - return ret; - - gd->cpu_clk = clk_get_rate(&clk); - - clk_free(&clk); - - gd->arch.timer_rate_hz = (gd->cpu_clk / 2) / (TIMER_PRESCALE + 1); - - /* Load the timer counter register */ - writel(0xFFFFFFFF, &timer_base->load); - - /* - * Start the A9Timer device - * Enable Auto reload mode, Clear prescaler control bits - * Set prescaler value, Enable the decrementer - */ - clrsetbits_le32(&timer_base->control, SCUTIMER_CONTROL_PRESCALER_MASK, - emask); - - /* Reset time */ - gd->arch.lastinc = readl(&timer_base->counter) / - (gd->arch.timer_rate_hz / CONFIG_SYS_HZ); - gd->arch.tbl = 0; - - return 0; -} - -/* - * This function is derived from PowerPC code (timebase clock frequency). - * On ARM it returns the number of timer ticks per second. - */ -ulong get_tbclk(void) -{ - return gd->arch.timer_rate_hz; -} diff --git a/drivers/timer/Kconfig b/drivers/timer/Kconfig index 20b5af7e26..1e2b6566c6 100644 --- a/drivers/timer/Kconfig +++ b/drivers/timer/Kconfig @@ -73,6 +73,12 @@ config ARC_TIMER usually at least one of them exists. Either of them is supported in U-Boot. +config ARM_TWD_TIMER + bool "ARM timer watchdog (TWD) timer support" + depends on TIMER && CLK + help + Select this to enable support for the ARM global timer watchdog timer. + config AST_TIMER bool "Aspeed ast2400/ast2500 timer support" depends on TIMER diff --git a/drivers/timer/Makefile b/drivers/timer/Makefile index d9822a5370..4db03fe09c 100644 --- a/drivers/timer/Makefile +++ b/drivers/timer/Makefile @@ -6,6 +6,7 @@ obj-y += timer-uclass.o obj-$(CONFIG_ALTERA_TIMER) += altera_timer.o obj-$(CONFIG_ANDES_PLMT_TIMER) += andes_plmt_timer.o obj-$(CONFIG_ARC_TIMER) += arc_timer.o +obj-$(CONFIG_ARM_TWD_TIMER) += arm_twd_timer.o obj-$(CONFIG_AST_TIMER) += ast_timer.o obj-$(CONFIG_ATCPIT100_TIMER) += atcpit100_timer.o obj-$(CONFIG_$(SPL_)ATMEL_PIT_TIMER) += atmel_pit_timer.o diff --git a/drivers/timer/arm_twd_timer.c b/drivers/timer/arm_twd_timer.c new file mode 100644 index 0000000000..40ccd16587 --- /dev/null +++ b/drivers/timer/arm_twd_timer.c @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2017-2022 Weidmüller Interface GmbH & Co. KG + * Stefan Herbrechtsmeier <stefan.herbrechtsmeier@weidmueller.com> + * + * Copyright (C) 2012 Michal Simek <monstr@monstr.eu> + * Copyright (C) 2011-2017 Xilinx, Inc. All rights reserved. + * + * (C) Copyright 2008 + * Guennadi Liakhovetki, DENX Software Engineering, <lg@denx.de> + * + * (C) Copyright 2004 + * Philippe Robin, ARM Ltd. <philippe.robin@arm.com> + * + * (C) Copyright 2002-2004 + * Gary Jennejohn, DENX Software Engineering, <gj@denx.de> + * + * (C) Copyright 2003 + * Texas Instruments <www.ti.com> + * + * (C) Copyright 2002 + * Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Marius Groeger <mgroeger@sysgo.de> + * + * (C) Copyright 2002 + * Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Alex Zuepke <azu@sysgo.de> + */ + +#include <common.h> +#include <dm.h> +#include <fdtdec.h> +#include <timer.h> +#include <linux/bitops.h> + +#include <asm/io.h> + +#define SCUTIMER_CONTROL_PRESCALER_MASK 0x0000FF00 /* Prescaler */ +#define SCUTIMER_CONTROL_AUTO_RELOAD_MASK 0x00000002 /* Auto-reload */ +#define SCUTIMER_CONTROL_ENABLE_MASK 0x00000001 /* Timer enable */ + +#define TIMER_LOAD_VAL 0xFFFFFFFF + +struct arm_twd_timer_regs { + u32 load; /* Timer Load Register */ + u32 counter; /* Timer Counter Register */ + u32 control; /* Timer Control Register */ +}; + +struct arm_twd_timer_priv { + struct arm_twd_timer_regs *base; +}; + +static u64 arm_twd_timer_get_count(struct udevice *dev) +{ + struct arm_twd_timer_priv *priv = dev_get_priv(dev); + struct arm_twd_timer_regs *regs = priv->base; + u32 count = TIMER_LOAD_VAL - readl(®s->counter); + + return timer_conv_64(count); +} + +static int arm_twd_timer_probe(struct udevice *dev) +{ + struct arm_twd_timer_priv *priv = dev_get_priv(dev); + struct arm_twd_timer_regs *regs; + fdt_addr_t addr; + + addr = dev_read_addr(dev); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + + priv->base = (struct arm_twd_timer_regs *)addr; + + regs = priv->base; + + /* Load the timer counter register */ + writel(0xFFFFFFFF, ®s->load); + + /* + * Start the A9Timer device + * Enable Auto reload mode, Clear prescaler control bits + * Set prescaler value, Enable the decrementer + */ + clrsetbits_le32(®s->control, SCUTIMER_CONTROL_PRESCALER_MASK, + SCUTIMER_CONTROL_AUTO_RELOAD_MASK | + SCUTIMER_CONTROL_ENABLE_MASK); + + return 0; +} + +static const struct timer_ops arm_twd_timer_ops = { + .get_count = arm_twd_timer_get_count, +}; + +static const struct udevice_id arm_twd_timer_ids[] = { + { .compatible = "arm,cortex-a9-twd-timer" }, + {} +}; + +U_BOOT_DRIVER(arm_twd_timer) = { + .name = "arm_twd_timer", + .id = UCLASS_TIMER, + .of_match = arm_twd_timer_ids, + .priv_auto = sizeof(struct arm_twd_timer_priv), + .probe = arm_twd_timer_probe, + .ops = &arm_twd_timer_ops, +};