From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752699AbbCYJxk (ORCPT ); Wed, 25 Mar 2015 05:53:40 -0400 Received: from www.linutronix.de ([62.245.132.108]:50584 "EHLO Galois.linutronix.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752522AbbCYJwu (ORCPT ); Wed, 25 Mar 2015 05:52:50 -0400 From: Holger Dengler To: linux-kernel@vger.kernel.org Cc: Peter Mahler , Juergen Bubeck , Benedikt Spranger , Holger Dengler , Daniel Lezcano , Thomas Gleixner Subject: [PATCH 10/11] clocksource: flexcard: Add basic timestamp counter support Date: Wed, 25 Mar 2015 10:51:59 +0100 Message-Id: <1427277120-16924-11-git-send-email-dengler@linutronix.de> X-Mailer: git-send-email 2.1.4 In-Reply-To: <1427277120-16924-1-git-send-email-dengler@linutronix.de> References: <1427277120-16924-1-git-send-email-dengler@linutronix.de> X-Linutronix-Spam-Score: -1.0 X-Linutronix-Spam-Level: - X-Linutronix-Spam-Status: No , -1.0 points, 5.0 required, ALL_TRUSTED=-1,SHORTCIRCUIT=-0.0001,URIBL_BLOCKED=0.001 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Benedikt Spranger 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 Signed-off-by: Benedikt Spranger cc: Daniel Lezcano cc: Thomas Gleixner --- 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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 "); +MODULE_AUTHOR("Benedikt Spranger "); +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