summaryrefslogtreecommitdiffstats
path: root/drivers/clocksource
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2018-11-09 10:55:05 +0100
committerSascha Hauer <s.hauer@pengutronix.de>2018-11-09 10:55:05 +0100
commit227426f16163d3721fee82650980aae0eef01375 (patch)
treea64101906c15b814ec942468e88875ce8160e45d /drivers/clocksource
parentc024f90f5dd5604d3f82dbbc055fd88a69d59187 (diff)
parentac7324a0ed12a67fc836b383388f2562d32e55ed (diff)
downloadbarebox-227426f16163d3721fee82650980aae0eef01375.tar.gz
barebox-227426f16163d3721fee82650980aae0eef01375.tar.xz
Merge branch 'for-next/net-switch-mv88e6xxx'
Diffstat (limited to 'drivers/clocksource')
-rw-r--r--drivers/clocksource/Kconfig10
-rw-r--r--drivers/clocksource/Makefile2
-rw-r--r--drivers/clocksource/arm_global_timer.c113
-rw-r--r--drivers/clocksource/timer-imx-gpt.c192
4 files changed, 317 insertions, 0 deletions
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index 3d63f7ff16..3caf72503a 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -1,3 +1,6 @@
+config ARCH_HAS_IMX_GPT
+ bool
+
config AMBA_SP804
bool
depends on ARM_AMBA
@@ -70,3 +73,10 @@ config CLOCKSOURCE_ARMV8_TIMER
bool
default y
depends on ARM && CPU_64v8
+
+config CLOCKSOURCE_ARM_GLOBAL_TIMER
+ bool
+ depends on ARM && CPU_V7
+config CLOCKSOURCE_IMX_GPT
+ def_bool y
+ depends on ARCH_HAS_IMX_GPT
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index ea33fff502..ce4d74137a 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -12,3 +12,5 @@ obj-$(CONFIG_CLOCKSOURCE_UEMD) += uemd.o
obj-$(CONFIG_CLOCKSOURCE_ROCKCHIP)+= rk_timer.o
obj-$(CONFIG_CLOCKSOURCE_ATMEL_PIT) += timer-atmel-pit.o
obj-$(CONFIG_CLOCKSOURCE_ARMV8_TIMER) += armv8-timer.o
+obj-$(CONFIG_CLOCKSOURCE_ARM_GLOBAL_TIMER) += arm_global_timer.o
+obj-$(CONFIG_CLOCKSOURCE_IMX_GPT) += timer-imx-gpt.o
diff --git a/drivers/clocksource/arm_global_timer.c b/drivers/clocksource/arm_global_timer.c
new file mode 100644
index 0000000000..44e3a3c762
--- /dev/null
+++ b/drivers/clocksource/arm_global_timer.c
@@ -0,0 +1,113 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Clocksource driver for generic Cortex A9 timer block
+ *
+ * Copyright (C) 2018 Zodiac Inflight Innovations
+ * Author: Andrey Smirnov <andrew.smirnov@gmail.com>
+ *
+ * based on corresponding driver from Linux kernel with the following
+ * copyright:
+ *
+ * drivers/clocksource/arm_global_timer.c
+ *
+ * Copyright (C) 2013 STMicroelectronics (R&D) Limited.
+ * Author: Stuart Menefy <stuart.menefy@st.com>
+ * Author: Srinivas Kandagatla <srinivas.kandagatla@st.com>
+ */
+#include <common.h>
+#include <init.h>
+#include <clock.h>
+#include <linux/clk.h>
+#include <io.h>
+#include <asm/system.h>
+
+#define GT_COUNTER0 0x00
+#define GT_COUNTER1 0x04
+
+#define GT_CONTROL 0x08
+#define GT_CONTROL_TIMER_ENABLE BIT(0) /* this bit is NOT banked */
+
+static void __iomem *gt_base;
+
+/*
+ * To get the value from the Global Timer Counter register proceed as follows:
+ * 1. Read the upper 32-bit timer counter register
+ * 2. Read the lower 32-bit timer counter register
+ * 3. Read the upper 32-bit timer counter register again. If the value is
+ * different to the 32-bit upper value read previously, go back to step 2.
+ * Otherwise the 64-bit timer counter value is correct.
+ */
+static uint64_t arm_global_clocksource_read(void)
+{
+ uint64_t counter;
+ uint32_t lower;
+ uint32_t upper, old_upper;
+
+ upper = readl(gt_base + GT_COUNTER1);
+ do {
+ old_upper = upper;
+ lower = readl(gt_base + GT_COUNTER0);
+ upper = readl(gt_base + GT_COUNTER1);
+ } while (upper != old_upper);
+
+ counter = upper;
+ counter <<= 32;
+ counter |= lower;
+ return counter;
+}
+
+static struct clocksource cs = {
+ .read = arm_global_clocksource_read,
+ .mask = CLOCKSOURCE_MASK(64),
+ .shift = 0,
+};
+
+static int arm_global_timer_probe(struct device_d *dev)
+{
+ struct resource *iores;
+ struct clk *clk;
+ int ret;
+
+ iores = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(iores))
+ return PTR_ERR(iores);
+
+ clk = clk_get(dev, NULL);
+ if (IS_ERR(clk)) {
+ ret = PTR_ERR(clk);
+ dev_err(dev, "clock not found: %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_enable(clk);
+ if (ret) {
+ dev_err(dev, "clock failed to enable: %d\n", ret);
+ return ret;
+ }
+
+ gt_base = IOMEM(iores->start);
+
+ cs.mult = clocksource_hz2mult(clk_get_rate(clk), cs.shift);
+
+ writel(0, gt_base + GT_CONTROL);
+ writel(0, gt_base + GT_COUNTER0);
+ writel(0, gt_base + GT_COUNTER1);
+ /* enables timer on all the cores */
+ writel(GT_CONTROL_TIMER_ENABLE, gt_base + GT_CONTROL);
+
+ return init_clock(&cs);
+}
+
+static struct of_device_id arm_global_timer_dt_ids[] = {
+ { .compatible = "arm,cortex-a9-global-timer", },
+ { }
+};
+
+static struct driver_d arm_global_timer_driver = {
+ .name = "arm-global-timer",
+ .probe = arm_global_timer_probe,
+ .of_compatible = DRV_OF_COMPAT(arm_global_timer_dt_ids),
+};
+postcore_platform_driver(arm_global_timer_driver);
+
diff --git a/drivers/clocksource/timer-imx-gpt.c b/drivers/clocksource/timer-imx-gpt.c
new file mode 100644
index 0000000000..4d6c6c2b0b
--- /dev/null
+++ b/drivers/clocksource/timer-imx-gpt.c
@@ -0,0 +1,192 @@
+/*
+ * (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>
+ *
+ * (C) Copyright 2002
+ * Gary Jennejohn, DENX Software Engineering, <gj@denx.de>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <common.h>
+#include <init.h>
+#include <clock.h>
+#include <errno.h>
+#include <of.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <notifier.h>
+#include <io.h>
+
+/* Part 1: Registers */
+# define GPT_TCTL 0x00
+# define GPT_TPRER 0x04
+
+/* Part 2: Bitfields */
+#define TCTL_SWR (1 << 15) /* Software reset */
+#define IMX1_TCTL_FRR (1 << 8) /* Freerun / restart */
+#define IMX31_TCTL_FRR (1 << 9) /* Freerun / restart */
+#define IMX1_TCTL_CLKSOURCE_PER (1 << 1) /* Clock source bit position */
+#define IMX31_TCTL_CLKSOURCE_IPG (1 << 6) /* Clock source bit position */
+#define IMX31_TCTL_CLKSOURCE_PER (2 << 6) /* Clock source bit position */
+#define TCTL_TEN (1 << 0) /* Timer enable */
+
+static struct clk *clk_gpt;
+
+struct imx_gpt_regs {
+ unsigned int tcn;
+ uint32_t tctl_val;
+};
+
+static struct imx_gpt_regs regs_imx1 = {
+ .tcn = 0x10,
+ .tctl_val = IMX1_TCTL_FRR | IMX1_TCTL_CLKSOURCE_PER | TCTL_TEN,
+};
+
+static struct imx_gpt_regs regs_imx31 = {
+ .tcn = 0x24,
+ .tctl_val = IMX31_TCTL_FRR | IMX31_TCTL_CLKSOURCE_PER | TCTL_TEN,
+};
+
+static struct imx_gpt_regs *regs;
+static void __iomem *timer_base;
+
+static uint64_t imx_clocksource_read(void)
+{
+ return readl(timer_base + regs->tcn);
+}
+
+static struct clocksource cs = {
+ .read = imx_clocksource_read,
+ .mask = CLOCKSOURCE_MASK(32),
+ .shift = 10,
+};
+
+static int imx_clocksource_clock_change(struct notifier_block *nb, unsigned long event, void *data)
+{
+ cs.mult = clocksource_hz2mult(clk_get_rate(clk_gpt), cs.shift);
+ return 0;
+}
+
+static struct notifier_block imx_clock_notifier = {
+ .notifier_call = imx_clocksource_clock_change,
+};
+
+static int imx_gpt_probe(struct device_d *dev)
+{
+ struct resource *iores;
+ int i;
+ int ret;
+ unsigned long rate;
+
+ /* one timer is enough */
+ if (timer_base)
+ return 0;
+
+ ret = dev_get_drvdata(dev, (const void **)&regs);
+ if (ret)
+ return ret;
+
+ iores = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(iores))
+ return PTR_ERR(iores);
+ timer_base = IOMEM(iores->start);
+
+ /* setup GP Timer 1 */
+ writel(TCTL_SWR, timer_base + GPT_TCTL);
+
+ for (i = 0; i < 100; i++)
+ writel(0, timer_base + GPT_TCTL); /* We have no udelay by now */
+
+ clk_gpt = clk_get(dev, "per");
+ if (IS_ERR(clk_gpt)) {
+ rate = 20000000;
+ dev_err(dev, "failed to get clock, assume %lu Hz\n", rate);
+ } else {
+ rate = clk_get_rate(clk_gpt);
+ if (!rate) {
+ dev_err(dev, "clock reports rate == 0\n");
+ return -EIO;
+ }
+ }
+
+ writel(0, timer_base + GPT_TPRER);
+ writel(regs->tctl_val, timer_base + GPT_TCTL);
+
+ cs.mult = clocksource_hz2mult(rate, cs.shift);
+
+ init_clock(&cs);
+
+ clock_register_client(&imx_clock_notifier);
+
+ return 0;
+}
+
+static __maybe_unused struct of_device_id imx_gpt_dt_ids[] = {
+ {
+ .compatible = "fsl,imx1-gpt",
+ .data = &regs_imx1,
+ }, {
+ .compatible = "fsl,imx21-gpt",
+ .data = &regs_imx1,
+ }, {
+ .compatible = "fsl,imx31-gpt",
+ .data = &regs_imx31,
+ }, {
+ .compatible = "fsl,imx6q-gpt",
+ .data = &regs_imx31,
+ }, {
+ .compatible = "fsl,imx6dl-gpt",
+ .data = &regs_imx31,
+ }, {
+ .compatible = "fsl,imx6ul-gpt",
+ .data = &regs_imx31,
+ }, {
+ .compatible = "fsl,imx7d-gpt",
+ .data = &regs_imx31,
+ }, {
+ /* sentinel */
+ }
+};
+
+static struct platform_device_id imx_gpt_ids[] = {
+ {
+ .name = "imx1-gpt",
+ .driver_data = (unsigned long)&regs_imx1,
+ }, {
+ .name = "imx31-gpt",
+ .driver_data = (unsigned long)&regs_imx31,
+ }, {
+ /* sentinel */
+ },
+};
+
+static struct driver_d imx_gpt_driver = {
+ .name = "imx-gpt",
+ .probe = imx_gpt_probe,
+ .of_compatible = DRV_OF_COMPAT(imx_gpt_dt_ids),
+ .id_table = imx_gpt_ids,
+};
+
+static int imx_gpt_init(void)
+{
+ return platform_driver_register(&imx_gpt_driver);
+}
+postcore_initcall(imx_gpt_init);