[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <1427277120-16924-11-git-send-email-dengler@linutronix.de>
Date: Wed, 25 Mar 2015 10:51:59 +0100
From: Holger Dengler <dengler@...utronix.de>
To: linux-kernel@...r.kernel.org
Cc: Peter Mahler <mahler@...ug.com>, Juergen Bubeck <bubeck@...ug.com>,
Benedikt Spranger <b.spranger@...utronix.de>,
Holger Dengler <dengler@...utronix.de>,
Daniel Lezcano <daniel.lezcano@...aro.org>,
Thomas Gleixner <tglx@...utronix.de>
Subject: [PATCH 10/11] clocksource: flexcard: Add basic timestamp counter support
From: Benedikt Spranger <b.spranger@...utronix.de>
The Eberspaecher Flexcard PMC II offers a Flexray network synchronized
counter with a selectable resolution of 1us, 100ns or 10ns. Add basic
support for the timestamp counter with 1us resolution, which is the
standard Flexray bus resolution.
Signed-off-by: Holger Dengler <dengler@...utronix.de>
Signed-off-by: Benedikt Spranger <b.spranger@...utronix.de>
cc: Daniel Lezcano <daniel.lezcano@...aro.org>
cc: Thomas Gleixner <tglx@...utronix.de>
---
drivers/clocksource/Kconfig | 11 ++
drivers/clocksource/Makefile | 1 +
drivers/clocksource/flexcard-time.c | 235 ++++++++++++++++++++++++++++++++++++
include/linux/mfd/flexcard.h | 6 +
4 files changed, 253 insertions(+)
create mode 100644 drivers/clocksource/flexcard-time.c
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index 68161f7..a6b9308 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -250,4 +250,15 @@ config CLKSRC_PXA
help
This enables OST0 support available on PXA and SA-11x0
platforms.
+
+config CLKSRC_FLEXCARD
+ tristate "Support for Flexcard clock function"
+ depends on MFD_FLEXCARD
+ select CLKSRC_MMIO
+ help
+ This is the clocksource function driver for the
+ Eberspaecher Flexcard PMC II carrier board. The
+ Flexcard provide a Flexray synchronized counter
+ configurable at 1, 10 or 100MHz.
+
endmenu
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index 752d5c7..8bbc657 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -51,3 +51,4 @@ obj-$(CONFIG_ARCH_INTEGRATOR_AP) += timer-integrator-ap.o
obj-$(CONFIG_CLKSRC_VERSATILE) += versatile.o
obj-$(CONFIG_CLKSRC_MIPS_GIC) += mips-gic-timer.o
obj-$(CONFIG_ASM9260_TIMER) += asm9260_timer.o
+obj-$(CONFIG_CLKSRC_FLEXCARD) += flexcard-time.o
diff --git a/drivers/clocksource/flexcard-time.c b/drivers/clocksource/flexcard-time.c
new file mode 100644
index 0000000..f2efb53
--- /dev/null
+++ b/drivers/clocksource/flexcard-time.c
@@ -0,0 +1,235 @@
+/*
+ * Eberspaecher Flexcard PMC II - clocksource driver
+ *
+ * Copyright (c) 2014,2015 Linutronix GmbH
+ * Author: Holger Dengler
+ * Benedikt Spranger
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/platform_device.h>
+#include <linux/posix-clock.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/flexcard.h>
+
+#define MAX_CLOCKS 16
+#define CLKSEL_OFF 0x10
+
+static dev_t flexcard_clk_devt;
+static struct class *flexcard_clk_class;
+
+struct flexcard_clk {
+ struct posix_clock clock;
+ dev_t devid;
+ struct device *dev;
+ void __iomem *ts64;
+ void __iomem *reset;
+};
+
+static int flexcard_clk_getres(struct posix_clock *pc, struct timespec *tp)
+{
+ tp->tv_sec = 0;
+ tp->tv_nsec = 1000;
+
+ return 0;
+}
+
+static int flexcard_clk_gettime(struct posix_clock *pc, struct timespec *tp)
+{
+ struct flexcard_clk *clk = container_of(pc, struct flexcard_clk, clock);
+ u64 now;
+ u32 upper, rem;
+
+retry:
+ upper = readl(clk->ts64);
+ now = ((u64) upper << 32) | readl(clk->ts64 + 4);
+ if (upper != readl(clk->ts64))
+ goto retry;
+
+ tp->tv_sec = div_u64_rem(now, clk->desc.freq, &rem);
+ tp->tv_nsec = rem * 1000;
+
+ return 0;
+}
+
+static int flexcard_clk_settime(struct posix_clock *pc,
+ const struct timespec *tp)
+{
+ struct flexcard_clk *clk = container_of(pc, struct flexcard_clk, clock);
+
+ /* The FlexCard clock could only be reset to 0 and not set */
+ if (tp->tv_sec || tp->tv_nsec)
+ return -EINVAL;
+
+ writel(FLEXCARD_RST_TS, clk->reset);
+
+ return 0;
+}
+
+static struct posix_clock_operations flexcard_clk_ops = {
+ .owner = THIS_MODULE,
+ .clock_getres = flexcard_clk_getres,
+ .clock_gettime = flexcard_clk_gettime,
+ .clock_settime = flexcard_clk_settime,
+};
+
+static int flexcard_clksrc_iomap(struct platform_device *pdev)
+{
+ struct flexcard_clk *clk = platform_get_drvdata(pdev);
+ struct resource *res;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENXIO;
+
+ clk->ts64 = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (!clk->ts64)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!res)
+ return -ENXIO;
+
+ clk->reset = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (!clk->reset)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int flexcard_clksrc_probe(struct platform_device *pdev)
+{
+ const struct mfd_cell *cell;
+ struct flexcard_clk *clk;
+ int major, ret;
+
+ cell = mfd_get_cell(pdev);
+ if (!cell)
+ return -ENODEV;
+
+ if (cell->id >= MAX_CLOCKS) {
+ dev_err(&pdev->dev, "all flexcard posix clocks in use\n");
+ return -EBUSY;
+ }
+
+ clk = devm_kzalloc(&pdev->dev, sizeof(*clk), GFP_KERNEL);
+ if (!clk)
+ return -ENOMEM;
+
+ major = MAJOR(flexcard_clk_devt);
+ platform_set_drvdata(pdev, clk);
+
+ ret = flexcard_clksrc_iomap(pdev);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to map resources: %d\n", ret);
+ goto out;
+ }
+
+ clk->devid = MKDEV(major, cell->id);
+ clk->clock.ops = flexcard_clk_ops;
+
+ writel(FLEXCARD_CLK_1MHZ, clk->ts64 + CLKSEL_OFF);
+ writel(FLEXCARD_RST_TS, clk->reset);
+
+ clk->dev = device_create(flexcard_clk_class, &pdev->dev, clk->devid,
+ clk, "flexcard_clk%d", cell->id);
+ if (IS_ERR(clk->dev)) {
+ ret = PTR_ERR(clk->dev);
+ goto out;
+ }
+
+ ret = posix_clock_register(&clk->clock, clk->devid);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "failed to register flexcard posix clock: %d\n", ret);
+ goto out_destroy;
+ }
+
+ dev_info(&pdev->dev, "flexcard posix clock %d registered", cell->id);
+
+ return 0;
+
+out_destroy:
+ device_destroy(flexcard_clk_class, clk->devid);
+out:
+ return ret;
+}
+
+static int flexcard_clksrc_remove(struct platform_device *pdev)
+{
+ struct flexcard_clk *clk = platform_get_drvdata(pdev);
+
+ posix_clock_unregister(&clk->clock);
+ device_destroy(flexcard_clk_class, clk->devid);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static const struct platform_device_id flexcard_clksrc_id_table[] = {
+ { "flexcard-clksrc", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, flexcard_clksrc_id_table);
+
+static struct platform_driver flexcard_clksrc_driver = {
+ .probe = flexcard_clksrc_probe,
+ .remove = flexcard_clksrc_remove,
+ .driver = {
+ .name = "flexcard-clksrc",
+ },
+ .id_table = flexcard_clksrc_id_table,
+};
+
+static int __init flexcard_clksrc_init(void)
+{
+ int ret;
+
+ flexcard_clk_class = class_create(THIS_MODULE, "flexcard_clk");
+ if (IS_ERR(flexcard_clk_class)) {
+ pr_err("flexcard_clk: failed to allocate class\n");
+ return PTR_ERR(flexcard_clk_class);
+ }
+
+ ret = alloc_chrdev_region(&flexcard_clk_devt, 0, MAX_CLOCKS,
+ "flexcard_clk");
+ if (ret < 0) {
+ pr_err("failed to allocate device region\n");
+ goto out;
+ }
+
+ ret = platform_driver_register(&flexcard_clksrc_driver);
+ if (ret < 0)
+ goto out_unregister;
+
+ return 0;
+
+out_unregister:
+ unregister_chrdev_region(flexcard_clk_devt, MAX_CLOCKS);
+out:
+ class_destroy(flexcard_clk_class);
+
+ return ret;
+}
+
+static void __exit flexcard_clksrc_exit(void)
+{
+ platform_driver_unregister(&flexcard_clksrc_driver);
+ unregister_chrdev_region(flexcard_clk_devt, MAX_CLOCKS);
+ class_destroy(flexcard_clk_class);
+}
+
+MODULE_AUTHOR("Holger Dengler <dengler@...utronix.de>");
+MODULE_AUTHOR("Benedikt Spranger <b.spranger@...utronix.de>");
+MODULE_DESCRIPTION("Eberspaecher Flexcard PMC II clocksource driver");
+MODULE_LICENSE("GPL v2");
+
+module_init(flexcard_clksrc_init);
+module_exit(flexcard_clksrc_exit);
diff --git a/include/linux/mfd/flexcard.h b/include/linux/mfd/flexcard.h
index 964d78c..d7ecf3d 100644
--- a/include/linux/mfd/flexcard.h
+++ b/include/linux/mfd/flexcard.h
@@ -19,8 +19,14 @@
#define FLEXCARD_FR_OFFSET 0x4000
#define FLEXCARD_FR_SIZE 0x2000
+#define FLEXCARD_RST_TS 0x8000
+
#define FLEXCARD_MAX_NAME 16
+#define FLEXCARD_CLK_1MHZ 0
+#define FLEXCARD_CLK_10MHZ 1
+#define FLEXCARD_CLK_100MHZ 2
+
struct flexcard_device {
raw_spinlock_t irq_lock;
struct pci_dev *pdev;
--
2.1.4
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Powered by blists - more mailing lists