lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:	Tue, 13 Aug 2013 11:08:40 +0200
From:	Benedikt Spranger <b.spranger@...utronix.de>
To:	netdev@...r.kernel.org
Cc:	Alexander Frank <Alexander.Frank@...rspaecher.com>,
	Sebastian Andrzej Siewior <bigeasy@...utronix.de>,
	Benedikt Spranger <b.spranger@...utronix.de>,
	Daniel Lezcano <daniel.lezcano@...aro.org>,
	John Stultz <john.stultz@...aro.org>,
	Thomas Gleixner <tglx@...utronix.de>,
	Holger Dengler <dengler@...utronix.de>
Subject: [PATCH 5/7] clocksource: Add flexcard support

This patch adds the a clocksource and a clock event device which is
available on the FlexCard.

Cc: Daniel Lezcano <daniel.lezcano@...aro.org>
Cc: John Stultz <john.stultz@...aro.org>
Cc: Thomas Gleixner <tglx@...utronix.de>
Signed-off-by: Benedikt Spranger <b.spranger@...utronix.de>
Signed-off-by: Holger Dengler <dengler@...utronix.de>
---
 drivers/clocksource/Kconfig        |  13 ++
 drivers/clocksource/Makefile       |   2 +
 drivers/clocksource/flexcard_clk.c | 297 +++++++++++++++++++++++++++++++++++++
 drivers/clocksource/flexcard_evt.c | 161 ++++++++++++++++++++
 drivers/mfd/Kconfig                |   2 +
 5 files changed, 475 insertions(+)
 create mode 100644 drivers/clocksource/flexcard_clk.c
 create mode 100644 drivers/clocksource/flexcard_evt.c

diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index b7b9b04..0dd5ccf 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -70,6 +70,19 @@ config CLKSRC_DBX500_PRCMU_SCHED_CLOCK
 	help
 	  Use the always on PRCMU Timer as sched_clock
 
+config FLEXCARD_CLK
+	tristate
+	select CLKSRC_MMIO
+	depends on MFD_EBEL_FLEXCARD
+	help
+	  Support for Eberspächer Flexcard clock function.
+
+config FLEXCARD_EVT
+	tristate
+	depends on MFD_EBEL_FLEXCARD
+	help
+	  Support for Eberspächer Flexcard timer function.
+
 config ARM_ARCH_TIMER
 	bool
 	select CLKSRC_OF if OF
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index 8b00c5c..329e65a 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -12,6 +12,8 @@ obj-$(CONFIG_CLKBLD_I8253)	+= i8253.o
 obj-$(CONFIG_CLKSRC_MMIO)	+= mmio.o
 obj-$(CONFIG_DW_APB_TIMER)	+= dw_apb_timer.o
 obj-$(CONFIG_DW_APB_TIMER_OF)	+= dw_apb_timer_of.o
+obj-$(CONFIG_FLEXCARD_CLK)	+= flexcard_clk.o
+obj-$(CONFIG_FLEXCARD_EVT)	+= flexcard_evt.o
 obj-$(CONFIG_CLKSRC_NOMADIK_MTU)	+= nomadik-mtu.o
 obj-$(CONFIG_CLKSRC_DBX500_PRCMU)	+= clksrc-dbx500-prcmu.o
 obj-$(CONFIG_ARMADA_370_XP_TIMER)	+= time-armada-370-xp.o
diff --git a/drivers/clocksource/flexcard_clk.c b/drivers/clocksource/flexcard_clk.c
new file mode 100644
index 0000000..09f5ecb
--- /dev/null
+++ b/drivers/clocksource/flexcard_clk.c
@@ -0,0 +1,297 @@
+/*
+ * Copyright 2012 Eberspächer Electronics GmbH & Co. KG. All Rights Reserved.
+ */
+
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/posix-clock.h>
+#include <linux/slab.h>
+#include <linux/flexcard.h>
+#include <linux/io.h>
+#include <linux/export.h>
+#include <linux/module.h>
+
+#define FC_MAX_CLOCKS	16
+#define FC_CLKSEL_OFF	0x10
+
+static DECLARE_BITMAP(fc_clocks_map, FC_MAX_CLOCKS);
+static DEFINE_MUTEX(fc_clocks_mutex);
+static dev_t fc_devt;
+static struct class *fc_class;
+
+struct fc_clock {
+	struct posix_clock clock;
+	struct fc_clksrc src;
+	struct device *dev;
+	void __iomem *reg;
+	unsigned long ovr;
+	dev_t devid;
+	int index;
+	u64 old;
+};
+
+static int fc_clock_gettime(struct posix_clock *pc, struct timespec *tp)
+{
+	struct fc_clock *clk = container_of(pc, struct fc_clock, clock);
+	u64 val;
+	u64 now;
+
+	now = (u64)readl(clk->reg) << 32;
+	now |= readl(clk->reg + 0x4);
+
+	if (now < clk->old)
+		clk->ovr++;
+	clk->old = now;
+
+	val = clk->ovr * 0xffffffff + now;
+
+	tp->tv_nsec = do_div(val, clk->src.freq) * clk->src.mul;
+	tp->tv_sec = val;
+
+	return 0;
+}
+
+static int fc_clock_getres(struct posix_clock *pc, struct timespec *tp)
+{
+	struct fc_clock *clk = container_of(pc, struct fc_clock, clock);
+	tp->tv_sec = 0;
+	tp->tv_nsec = clk->src.mul;
+	return 0;
+}
+
+static int fc_clock_settime(struct posix_clock *pc, const struct timespec *tp)
+{
+	struct fc_clock *clk = container_of(pc, struct fc_clock, clock);
+
+	/* The FlexCard clock could only be reset to 0 and not set */
+	if (tp->tv_sec || tp->tv_nsec)
+		return -EINVAL;
+
+	clk->ovr = 0;
+	clk->old = 0;
+
+	/* Reset Timestamp Bit 15 (RST_TS), 0x700-0x5BC=0x144 (FC_RESET) */
+	writel(FC_RST_TS, clk->reg - FC_FC_RESET_OFF);
+
+	return 0;
+}
+
+static long fc_clock_ioctl(struct posix_clock *pc, unsigned int cmd,
+		    unsigned long arg)
+{
+	struct fc_clock *clk = container_of(pc, struct fc_clock, clock);
+	struct fc_clksrc clk_src;
+	int ret = 0;
+
+	switch (cmd) {
+	case FCGCLKSRC:
+		memset(&clk_src, 0, sizeof(clk_src));
+		clk_src.type = clk->src.type;
+		clk_src.freq = clk->src.freq;
+
+		if (copy_to_user((void __user *)arg, &clk_src,
+				 sizeof(clk_src)))
+			ret = -EFAULT;
+		break;
+	case FCSCLKSRC:
+		if (copy_from_user(&clk_src, (void __user *)arg,
+				   sizeof(clk_src))) {
+			ret = -EFAULT;
+			break;
+		}
+
+		switch (clk_src.type) {
+		case FC_CLK_INTERNAL_1MHZ:
+			clk->src.freq = 1000000;
+			clk->src.mul = NSEC_PER_USEC;
+			break;
+
+		case FC_CLK_INTERNAL_10MHZ:
+			clk->src.freq = 10000000;
+			clk->src.mul = 100;
+			break;
+
+		case FC_CLK_INTERNAL_100MHZ:
+			clk->src.freq = 100000000;
+			clk->src.mul = 10;
+			break;
+
+		case FC_CLK_INTERNAL_TRIGGER1:
+		case FC_CLK_INTERNAL_TRIGGER2:
+			if (clk_src.freq < 100000 ||
+			    clk_src.freq > NSEC_PER_SEC) {
+				ret = -EINVAL;
+				break;
+			}
+			clk->src.freq = clk_src.freq;
+			clk->src.mul = NSEC_PER_SEC/clk_src.freq;
+
+			break;
+		default:
+			ret = -EINVAL;
+		}
+
+		clk->src.type = clk_src.type;
+		writel(clk_src.type, clk->reg + FC_CLKSEL_OFF);
+		break;
+	default:
+		ret = -ENOTTY;
+		break;
+	}
+	return ret;
+}
+
+static struct posix_clock_operations fc_clock_ops = {
+	.owner		= THIS_MODULE,
+	.clock_gettime	= fc_clock_gettime,
+	.clock_getres	= fc_clock_getres,
+	.clock_settime	= fc_clock_settime,
+	.ioctl		= fc_clock_ioctl,
+};
+
+static int fc_clksrc_probe(struct platform_device *pdev)
+{
+	struct fc_clock *clk;
+	struct resource *res;
+	int index, major, ret = -ENXIO;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "failed to get I/O memory\n");
+		goto out;
+	}
+
+	mutex_lock(&fc_clocks_mutex);
+	major = MAJOR(fc_devt);
+	ret = -EBUSY;
+	index = find_first_zero_bit(fc_clocks_map, FC_MAX_CLOCKS);
+	if (index >= FC_MAX_CLOCKS) {
+		dev_err(&pdev->dev, "failed to request clock\n");
+		goto out_unlock;
+	}
+	set_bit(index, fc_clocks_map);
+
+	clk = kzalloc(sizeof(struct fc_clock), GFP_KERNEL);
+	if (!clk) {
+		dev_err(&pdev->dev, "out of memory\n");
+		goto out_clear;
+	}
+
+	clk->reg = ioremap_nocache(res->start, resource_size(res));
+	if (!clk->reg) {
+		dev_err(&pdev->dev, "failed to remap I/O memory\n");
+		goto out_free;
+	}
+
+	clk->clock.ops = fc_clock_ops;
+	clk->devid = MKDEV(major, index);
+	clk->index = index;
+
+	clk->src.type = FC_CLK_INTERNAL_1MHZ;
+	clk->src.freq = 1000000;
+	clk->src.mul = NSEC_PER_USEC;
+	writel(clk->src.type, clk->reg + FC_CLKSEL_OFF);
+
+	clk->dev = device_create(fc_class, &pdev->dev, clk->devid, clk,
+				 "fc_clk%d", clk->index);
+	if (IS_ERR(clk->dev))
+		goto out_unmap;
+
+	platform_set_drvdata(pdev, clk);
+
+	ret = posix_clock_register(&clk->clock, clk->devid);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to create posix clock\n");
+		goto out_destroy;
+	}
+
+	mutex_unlock(&fc_clocks_mutex);
+
+	dev_info(&pdev->dev, "Flexcard clocksource registered");
+	return 0;
+
+out_destroy:
+	device_destroy(fc_class, clk->devid);
+out_unmap:
+	iounmap(clk->reg);
+out_free:
+	kfree(clk);
+out_clear:
+	clear_bit(index, fc_clocks_map);
+out_unlock:
+	mutex_unlock(&fc_clocks_mutex);
+out:
+	return ret;
+}
+
+static int fc_clksrc_remove(struct platform_device *pdev)
+{
+	struct fc_clock *clk = platform_get_drvdata(pdev);
+
+	mutex_lock(&fc_clocks_mutex);
+	posix_clock_unregister(&clk->clock);
+	device_destroy(fc_class, clk->devid);
+	iounmap(clk->reg);
+	clear_bit(clk->index, fc_clocks_map);
+	platform_set_drvdata(pdev, NULL);
+	kfree(clk);
+	mutex_unlock(&fc_clocks_mutex);
+
+	return 0;
+}
+
+static struct platform_driver fc_clksrc_driver = {
+	.probe		= fc_clksrc_probe,
+	.remove		= fc_clksrc_remove,
+	.driver		= {
+		.name	= "flexcard-clksrc",
+		.owner	= THIS_MODULE,
+	}
+};
+MODULE_ALIAS("platform:flexcard-clksrc");
+
+static int __init fc_clock_init(void)
+{
+	int ret;
+
+	fc_class = class_create(THIS_MODULE, "fc_clk");
+	if (IS_ERR(fc_class)) {
+		pr_err("fc_clk: failed to allocate class\n");
+		return PTR_ERR(fc_class);
+	}
+
+	ret = alloc_chrdev_region(&fc_devt, 0, FC_MAX_CLOCKS,
+				  "flexcard_clk");
+	if (ret < 0) {
+		pr_err("failed to allocate device region\n");
+		goto out;
+	}
+
+	ret = platform_driver_register(&fc_clksrc_driver);
+	if (ret < 0)
+		goto out_unregister;
+
+	return 0;
+
+out_unregister:
+	unregister_chrdev_region(fc_devt, FC_MAX_CLOCKS);
+out:
+	class_destroy(fc_class);
+	return ret;
+}
+module_init(fc_clock_init);
+
+static void __exit fc_clock_exit(void)
+{
+	platform_driver_unregister(&fc_clksrc_driver);
+	unregister_chrdev_region(fc_devt, FC_MAX_CLOCKS);
+	class_destroy(fc_class);
+}
+module_exit(fc_clock_exit);
+
+MODULE_DESCRIPTION("Flexcard Clock Function Driver");
+MODULE_AUTHOR("Benedikt Spranger <b.spranger@...utronix.de>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/clocksource/flexcard_evt.c b/drivers/clocksource/flexcard_evt.c
new file mode 100644
index 0000000..2e11c35
--- /dev/null
+++ b/drivers/clocksource/flexcard_evt.c
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2012 Eberspächer Electronics GmbH & Co. KG. All Rights Reserved.
+ */
+
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/clocksource.h>
+#include <linux/clockchips.h>
+#include <linux/slab.h>
+#include <linux/flexcard.h>
+#include <linux/module.h>
+
+struct fc_clkevt_device {
+	struct clock_event_device	clkevt;
+	void __iomem			*tirqir;
+	bool				oneshot;
+};
+
+static struct fc_clkevt_device *to_tc_clkevt(struct clock_event_device *clkevt)
+{
+	return container_of(clkevt, struct fc_clkevt_device, clkevt);
+}
+
+static void flexcard_mode(enum clock_event_mode mode,
+			  struct clock_event_device *evt)
+{
+	struct fc_clkevt_device *dev = to_tc_clkevt(evt);
+	void __iomem *tirqir = dev->tirqir;
+
+	switch (mode) {
+	case CLOCK_EVT_MODE_PERIODIC:
+		dev->oneshot = false;
+		writel(1000, tirqir);
+		break;
+
+	case CLOCK_EVT_MODE_SHUTDOWN:
+	case CLOCK_EVT_MODE_UNUSED:
+		writel(0, tirqir);
+		break;
+
+	case CLOCK_EVT_MODE_ONESHOT:
+		writel(0, tirqir);
+		dev->oneshot = true;
+		break;
+	case CLOCK_EVT_MODE_RESUME:
+		/* Nothing to do here */
+		break;
+	}
+}
+
+static int flexcard_next_event(unsigned long delta,
+			       struct clock_event_device *evt)
+{
+	struct fc_clkevt_device *dev = to_tc_clkevt(evt);
+	void __iomem *tirqir = dev->tirqir;
+
+	writel(delta, tirqir);
+
+	return 0;
+}
+
+static const struct fc_clkevt_device clkevt_templ = {
+	.clkevt = {
+		.name		= "flexcard_clkevt",
+		.features	= CLOCK_EVT_FEAT_PERIODIC |
+				  CLOCK_EVT_FEAT_ONESHOT,
+		.rating		= 300,
+		.set_next_event	= flexcard_next_event,
+		.set_mode       = flexcard_mode,
+	},
+};
+
+static irqreturn_t flexcard_evt_handler(int irq, void *dev_id)
+{
+	struct fc_clkevt_device *dev = dev_id;
+	void __iomem *tirqir = dev->tirqir;
+
+	if (dev->oneshot)
+		writel(0, tirqir);
+
+	dev->clkevt.event_handler(&dev->clkevt);
+
+	return IRQ_HANDLED;
+}
+
+static int flexcard_clkevt_probe(struct platform_device *pdev)
+{
+	struct fc_clkevt_device *clkevt = NULL;
+	struct resource *res;
+	int irq, ret = -ENXIO;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(&pdev->dev, "failed to get IRQ number\n");
+		goto out;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "failed to get I/O memory\n");
+		goto out;
+	}
+
+	if (!request_mem_region(res->start, resource_size(res), pdev->name)) {
+		dev_err(&pdev->dev, "failed to request memory region\n");
+		goto out;
+	}
+
+	clkevt = kmalloc(sizeof(*clkevt), GFP_KERNEL);
+	if (!clkevt)
+		goto out_release;
+	memcpy(clkevt, &clkevt_templ, sizeof(*clkevt));
+
+	clkevt->tirqir = ioremap_nocache(res->start, resource_size(res));
+	if (!clkevt->tirqir) {
+		dev_err(&pdev->dev, "failed to remap I/O memory\n");
+		goto out_release;
+	}
+
+	ret = request_irq(irq, flexcard_evt_handler, IRQF_TIMER,
+			  "flexcard_timer", clkevt);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to request IRQ\n");
+		goto out_unmap;
+	}
+
+	clockevents_config_and_register(&clkevt->clkevt, 1000000,
+				       0xF, 0xFFFFFF);
+	dev_info(&pdev->dev, "Flexcard clockevent registered");
+	return 0;
+
+out_unmap:
+	iounmap(clkevt->tirqir);
+out_release:
+	release_mem_region(res->start, resource_size(res));
+	kfree(clkevt);
+out:
+	return ret;
+}
+
+static struct platform_driver flexcard_clkevt_driver = {
+	.probe		= flexcard_clkevt_probe,
+	.driver		= {
+		.name	= "flexcard-clkevt",
+		.owner	= THIS_MODULE,
+	}
+};
+MODULE_ALIAS("platform:flexcard-clkevt");
+
+static int __init flexcard_evt_init(void)
+{
+	return platform_driver_register(&flexcard_clkevt_driver);
+}
+module_init(flexcard_evt_init);
+
+MODULE_DESCRIPTION("Flexcard Timer Function Driver");
+MODULE_AUTHOR("Benedikt Spranger <b.spranger@...utronix.de>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index f847d52..12c60e7 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -226,6 +226,8 @@ config MFD_INTEL_MSIC
 config MFD_EBEL_FLEXCARD
 	tristate "Support for the Eberspächer Electronic Flexcard"
 	select MFD_CORE
+	select FLEXCARD_CLK
+	select FLEXCARD_EVT
 	select UIO
 	select UIO_PDRV
 	select UIO_PDRV_GENIRQ
-- 
1.8.4.rc2

--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Powered by blists - more mailing lists