linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: nick.hawkins@hpe.com
To: verdun@hpe.com, nick@hpe.com, joel@jms.id.au, arnd@arndb.de
Cc: Nick Hawkins <nick.hawkins@hpe.com>,
	Daniel Lezcano <daniel.lezcano@linaro.org>,
	Thomas Gleixner <tglx@linutronix.de>,
	linux-kernel@vger.kernel.org
Subject: [PATCH v4 04/11] clocksource/drivers: Add HPE GXP timer
Date: Wed, 20 Apr 2022 10:01:48 -0500	[thread overview]
Message-ID: <20220420150156.47405-4-nick.hawkins@hpe.com> (raw)
In-Reply-To: <20220420150156.47405-1-nick.hawkins@hpe.com>

From: Nick Hawkins <nick.hawkins@hpe.com>

Add support for the HPE GXP SOC timer. The GXP supports several
different kinds of timers but for the purpose of this driver there
is only support for the General Timer. The timer has a 1us
resolution and is 32 bits. The timer also creates a child watchdog
device as the register region is the same based on previous review
feedback.

Signed-off-by: Nick Hawkins <nick.hawkins@hpe.com>

---
v2:
*Made watchdog a child of timer as they share the same register
region
*Fixed watchdog init timeout call
*Fixed variable usage u32/u64
*Removed Read Once
*fixed error that should have been debug
---
 drivers/clocksource/Kconfig     |   8 ++
 drivers/clocksource/Makefile    |   1 +
 drivers/clocksource/timer-gxp.c | 183 ++++++++++++++++++++++++++++++++
 3 files changed, 192 insertions(+)
 create mode 100644 drivers/clocksource/timer-gxp.c

diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index cfb8ea0df3b1..716117f21f95 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -617,6 +617,14 @@ config CLKSRC_ST_LPC
 	  Enable this option to use the Low Power controller timer
 	  as clocksource.
 
+config GXP_TIMER
+	bool "GXP timer driver" if COMPILE_TEST
+	depends on ARCH_HPE
+	default y
+	help
+	  Provides a driver for the timer control found on HPE
+	  GXP SOCs. This is required for all GXP SOCs.
+
 config ATCPIT100_TIMER
 	bool "ATCPIT100 timer driver"
 	depends on NDS32 || COMPILE_TEST
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index fa5f624eadb6..35cc32fe6c6a 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -89,3 +89,4 @@ obj-$(CONFIG_GX6605S_TIMER)		+= timer-gx6605s.o
 obj-$(CONFIG_HYPERV_TIMER)		+= hyperv_timer.o
 obj-$(CONFIG_MICROCHIP_PIT64B)		+= timer-microchip-pit64b.o
 obj-$(CONFIG_MSC313E_TIMER)		+= timer-msc313e.o
+obj-$(CONFIG_GXP_TIMER)			+= timer-gxp.o
diff --git a/drivers/clocksource/timer-gxp.c b/drivers/clocksource/timer-gxp.c
new file mode 100644
index 000000000000..3fe5be2f94c7
--- /dev/null
+++ b/drivers/clocksource/timer-gxp.c
@@ -0,0 +1,183 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2022 Hewlett-Packard Enterprise Development Company, L.P.*/
+
+#include <linux/clk.h>
+#include <linux/clockchips.h>
+#include <linux/clocksource.h>
+#include <linux/interrupt.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/sched_clock.h>
+
+#define TIMER0_FREQ	1000000
+#define GXP_TIMER_CNT_OFS 0x00
+#define GXP_TIMESTAMP_OFS 0x08
+#define GXP_TIMER_CTRL_OFS 0x14
+
+/*TCS Stands for Timer Control/Status: these are masks to be used in*/
+/* the Timer Count Registers */
+#define MASK_TCS_ENABLE	0x01
+#define MASK_TCS_PERIOD	0x02
+#define MASK_TCS_RELOAD	0x04
+#define MASK_TCS_TC	0x80
+
+struct gxp_timer {
+	void __iomem *counter;
+	void __iomem *control;
+	struct clock_event_device evt;
+};
+
+static struct gxp_timer *local_gxp_timer;
+
+static void __iomem *system_clock __read_mostly;
+
+static inline struct gxp_timer *to_gxp_timer(struct clock_event_device *evt_dev)
+{
+	return container_of(evt_dev, struct gxp_timer, evt);
+}
+
+static u64 notrace gxp_sched_read(void)
+{
+	return readl_relaxed(system_clock);
+}
+
+static int gxp_time_set_next_event(unsigned long event,	struct clock_event_device *evt_dev)
+{
+	struct gxp_timer *timer = to_gxp_timer(evt_dev);
+
+	/* Stop counting and disable interrupt before updating */
+	writeb_relaxed(MASK_TCS_TC, timer->control);
+	writel_relaxed(event, timer->counter);
+	writeb_relaxed(MASK_TCS_TC | MASK_TCS_ENABLE, timer->control);
+
+	return 0;
+}
+
+static irqreturn_t gxp_timer_interrupt(int irq, void *dev_id)
+{
+	struct gxp_timer *timer = (struct gxp_timer *)dev_id;
+
+	if (!(readb_relaxed(timer->control) & MASK_TCS_TC))
+		return IRQ_NONE;
+
+	writeb_relaxed(MASK_TCS_TC, timer->control);
+
+	timer->evt.event_handler(&timer->evt);
+
+	return IRQ_HANDLED;
+}
+
+static int __init gxp_timer_init(struct device_node *node)
+{
+	void __iomem *base;
+	struct clk *clk;
+	u32 freq;
+	int ret, irq;
+	struct gxp_timer *gxp_timer;
+
+	base = of_iomap(node, 0);
+	if (!base) {
+		pr_err("Can't remap timer base register");
+		ret = -ENXIO;
+		return ret;
+	}
+
+	/*Set the offset to the clock register*/
+	system_clock = base + GXP_TIMESTAMP_OFS;
+
+	clk = of_clk_get(node, 0);
+	if (IS_ERR(clk)) {
+		pr_err("%pOFn clock not found: %d\n", node, (int)PTR_ERR(clk));
+		ret = -EIO;
+		goto err_iounmap;
+	}
+
+	ret = clk_prepare_enable(clk);
+
+	freq = clk_get_rate(clk);
+
+	sched_clock_register(gxp_sched_read, 32, freq);
+	clocksource_mmio_init(system_clock, node->name, freq,
+			      300, 32, clocksource_mmio_readl_up);
+
+	irq = irq_of_parse_and_map(node, 0);
+	if (irq <= 0) {
+		ret = -EINVAL;
+		pr_err("GXP Timer Can't parse IRQ %d", irq);
+		goto err_iounmap;
+	}
+
+	gxp_timer = kzalloc(sizeof(*gxp_timer), GFP_KERNEL);
+	if (!gxp_timer) {
+		ret = -ENOMEM;
+		goto err_iounmap;
+	}
+
+	gxp_timer->counter = base + GXP_TIMER_CNT_OFS;
+	gxp_timer->control = base + GXP_TIMER_CTRL_OFS;
+	gxp_timer->evt.name = node->name;
+	gxp_timer->evt.rating = 300;
+	gxp_timer->evt.features = CLOCK_EVT_FEAT_ONESHOT;
+	gxp_timer->evt.set_next_event = gxp_time_set_next_event;
+	gxp_timer->evt.cpumask = cpumask_of(0);
+
+	local_gxp_timer = gxp_timer;
+
+	ret = request_irq(irq, gxp_timer_interrupt, IRQF_TIMER | IRQF_SHARED,
+			  node->name, gxp_timer);
+	if (ret) {
+		pr_err("%s: request_irq() failed %pe\n", "GXP Timer Tick", ERR_PTR(ret));
+		goto err_iounmap;
+	}
+
+	clockevents_config_and_register(&gxp_timer->evt, TIMER0_FREQ,
+					0xf, 0xffffffff);
+
+	pr_debug("gxp: system timer (irq = %d)\n", irq);
+	return 0;
+
+err_iounmap:
+	iounmap(system_clock);
+	iounmap(base);
+	return ret;
+}
+
+static struct platform_device gxp_watchdog_device = {
+	.name = "gxp-wdt",
+	.id = -1,
+};
+
+/*
+ * This probe gets called after the timer is already up and running. This will create
+ * the watchdog device as a child since the registers are shared.
+ */
+
+static int gxp_timer_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+
+	/* Pass the base address (counter) as platform data and nothing else */
+	gxp_watchdog_device.dev.platform_data = local_gxp_timer->counter;
+	gxp_watchdog_device.dev.parent = dev;
+	return platform_device_register(&gxp_watchdog_device);
+}
+
+static const struct of_device_id gxp_timer_of_match[] = {
+	{ .compatible = "hpe,gxp-timer", },
+	{},
+};
+
+static struct platform_driver gxp_timer_driver = {
+	.probe  = gxp_timer_probe,
+	.driver = {
+		.name = "gxp-timer",
+		.of_match_table = gxp_timer_of_match,
+		.suppress_bind_attrs = true,
+	},
+};
+
+builtin_platform_driver(gxp_timer_driver);
+
+TIMER_OF_DECLARE(gxp, "hpe,gxp-timer", gxp_timer_init);
-- 
2.17.1


  parent reply	other threads:[~2022-04-20 15:00 UTC|newest]

Thread overview: 19+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-04-20 15:01 [PATCH v4 01/11] arch: arm: mach-hpe: Introduce the HPE GXP architecture nick.hawkins
2022-04-20 15:01 ` [PATCH v4 02/11] arch: arm: configs: multi_v7_defconfig nick.hawkins
2022-04-20 15:01 ` [PATCH v4 03/11] drivers: wdt: Introduce HPE GXP SoC Watchdog nick.hawkins
2022-04-20 15:52   ` Guenter Roeck
2022-04-20 17:06     ` Hawkins, Nick
2022-04-20 15:01 ` nick.hawkins [this message]
2022-04-20 15:01 ` [PATCH v4 05/11] dt-bindings: timer: Add HPE GXP Timer Binding nick.hawkins
2022-04-20 15:01 ` [PATCH v4 06/11] dt-bindings: watchdog: Add HPE GXP Watchdog timer binding nick.hawkins
2022-04-20 15:53   ` Guenter Roeck
2022-04-20 16:08     ` Hawkins, Nick
2022-04-20 16:36       ` Guenter Roeck
2022-04-20 17:15         ` Hawkins, Nick
2022-04-20 15:01 ` [PATCH v4 07/11] dt-bindings: arm: Add HPE GXP Binding nick.hawkins
2022-04-20 15:01 ` [PATCH v4 08/11] dt-bindings: usb: generic-echi: Add HPE GXP echi binding nick.hawkins
2022-04-20 15:01 ` [PATCH v4 09/11] dt-bindings: usb: generic-ochi: Add HPE GXP ochi binding nick.hawkins
2022-04-20 17:02   ` Arnd Bergmann
2022-04-20 17:12     ` Hawkins, Nick
2022-04-20 15:01 ` [PATCH v4 10/11] arch: arm: boot: dts: Introduce HPE GXP Device tree nick.hawkins
2022-04-20 15:01 ` [PATCH v4 11/11] maintainers: Introduce HPE GXP Architecture nick.hawkins

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20220420150156.47405-4-nick.hawkins@hpe.com \
    --to=nick.hawkins@hpe.com \
    --cc=arnd@arndb.de \
    --cc=daniel.lezcano@linaro.org \
    --cc=joel@jms.id.au \
    --cc=linux-kernel@vger.kernel.org \
    --cc=nick@hpe.com \
    --cc=tglx@linutronix.de \
    --cc=verdun@hpe.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).