Browse Source

timer: dw-apb: Add Designware APB timer driver

Add timer driver for the Designware APB Timer IP. This is present
for example on the Altera SoCFPGA chips.

Signed-off-by: Marek Vasut <marex@denx.de>
Cc: Chin Liang See <chin.liang.see@intel.com>
Cc: Dinh Nguyen <dinguyen@kernel.org>
Cc: Ley Foon Tan <ley.foon.tan@intel.com>
Marek Vasut 6 years ago
parent
commit
66011a0883
3 changed files with 98 additions and 0 deletions
  1. 7 0
      drivers/timer/Kconfig
  2. 1 0
      drivers/timer/Makefile
  3. 90 0
      drivers/timer/dw-apb-timer.c

+ 7 - 0
drivers/timer/Kconfig

@@ -59,6 +59,13 @@ config CADENCE_TTC_TIMER
 	  Enables support for the cadence ttc driver. This driver is present
 	  Enables support for the cadence ttc driver. This driver is present
 	  on Xilinx Zynq and ZynqMP SoCs.
 	  on Xilinx Zynq and ZynqMP SoCs.
 
 
+config DESIGNWARE_APB_TIMER
+	bool "Designware APB Timer"
+	depends on TIMER
+	help
+	  Enables support for the Designware APB Timer driver. This timer is
+	  present on Altera SoCFPGA SoCs.
+
 config SANDBOX_TIMER
 config SANDBOX_TIMER
 	bool "Sandbox timer support"
 	bool "Sandbox timer support"
 	depends on SANDBOX && TIMER
 	depends on SANDBOX && TIMER

+ 1 - 0
drivers/timer/Makefile

@@ -5,6 +5,7 @@
 obj-y += timer-uclass.o
 obj-y += timer-uclass.o
 obj-$(CONFIG_ALTERA_TIMER)	+= altera_timer.o
 obj-$(CONFIG_ALTERA_TIMER)	+= altera_timer.o
 obj-$(CONFIG_CADENCE_TTC_TIMER)	+= cadence-ttc.o
 obj-$(CONFIG_CADENCE_TTC_TIMER)	+= cadence-ttc.o
+obj-$(CONFIG_DESIGNWARE_APB_TIMER)	+= dw-apb-timer.o
 obj-$(CONFIG_SANDBOX_TIMER)	+= sandbox_timer.o
 obj-$(CONFIG_SANDBOX_TIMER)	+= sandbox_timer.o
 obj-$(CONFIG_X86_TSC_TIMER)	+= tsc_timer.o
 obj-$(CONFIG_X86_TSC_TIMER)	+= tsc_timer.o
 obj-$(CONFIG_OMAP_TIMER)	+= omap-timer.o
 obj-$(CONFIG_OMAP_TIMER)	+= omap-timer.o

+ 90 - 0
drivers/timer/dw-apb-timer.c

@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Designware APB Timer driver
+ *
+ * Copyright (C) 2018 Marek Vasut <marex@denx.de>
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <clk.h>
+#include <timer.h>
+
+#include <asm/io.h>
+#include <asm/arch/timer.h>
+
+#define DW_APB_LOAD_VAL		0x0
+#define DW_APB_CURR_VAL		0x4
+#define DW_APB_CTRL		0x8
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct dw_apb_timer_priv {
+	fdt_addr_t	regs;
+};
+
+static int dw_apb_timer_get_count(struct udevice *dev, u64 *count)
+{
+	struct dw_apb_timer_priv *priv = dev_get_priv(dev);
+
+	/*
+	 * The DW APB counter counts down, but this function
+	 * requires the count to be incrementing. Invert the
+	 * result.
+	 */
+	*count = ~readl(priv->regs + DW_APB_CURR_VAL);
+
+	return 0;
+}
+
+static int dw_apb_timer_probe(struct udevice *dev)
+{
+	struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+	struct dw_apb_timer_priv *priv = dev_get_priv(dev);
+	struct clk clk;
+	int ret;
+
+	ret = clk_get_by_index(dev, 0, &clk);
+	if (ret)
+		return ret;
+
+	uc_priv->clock_rate = clk_get_rate(&clk);
+
+	clk_free(&clk);
+
+	/* init timer */
+	writel(0xffffffff, priv->regs + DW_APB_LOAD_VAL);
+	writel(0xffffffff, priv->regs + DW_APB_CURR_VAL);
+	setbits_le32(priv->regs + DW_APB_CTRL, 0x3);
+
+	return 0;
+}
+
+static int dw_apb_timer_ofdata_to_platdata(struct udevice *dev)
+{
+	struct dw_apb_timer_priv *priv = dev_get_priv(dev);
+
+	priv->regs = dev_read_addr(dev);
+
+	return 0;
+}
+
+static const struct timer_ops dw_apb_timer_ops = {
+	.get_count	= dw_apb_timer_get_count,
+};
+
+static const struct udevice_id dw_apb_timer_ids[] = {
+	{ .compatible = "snps,dw-apb-timer" },
+	{}
+};
+
+U_BOOT_DRIVER(dw_apb_timer) = {
+	.name		= "dw_apb_timer",
+	.id		= UCLASS_TIMER,
+	.ops		= &dw_apb_timer_ops,
+	.probe		= dw_apb_timer_probe,
+	.flags		= DM_FLAG_PRE_RELOC,
+	.of_match	= dw_apb_timer_ids,
+	.ofdata_to_platdata = dw_apb_timer_ofdata_to_platdata,
+	.priv_auto_alloc_size = sizeof(struct dw_apb_timer_priv),
+};