[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <1376384922-8519-7-git-send-email-b.spranger@linutronix.de>
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