This is support for the running time meter(RTM) found on the Data Modul embedded boards. Signed-off-by: Zahari Doychev <zahari.doychev@xxxxxxxxx> --- drivers/staging/dmec/Kconfig | 10 ++- drivers/staging/dmec/Makefile | 1 +- drivers/staging/dmec/rtm-dmec.c | 203 +++++++++++++++++++++++++++++++++- 3 files changed, 214 insertions(+), 0 deletions(-) create mode 100644 drivers/staging/dmec/rtm-dmec.c diff --git a/drivers/staging/dmec/Kconfig b/drivers/staging/dmec/Kconfig index eddf0bb..f6f4146 100644 --- a/drivers/staging/dmec/Kconfig +++ b/drivers/staging/dmec/Kconfig @@ -38,3 +38,13 @@ config WDT_DMEC To compile this driver as a module, say M here: the module will be called wdt-dmec + +config RTM_DMEC + tristate "Data Modul RTM" + depends on MFD_DMEC + help + Say Y to enable support for a running time meter(RTM) on a Data Modul + embedded controllers. + + To compile this driver as a module, say M here: the module will be + called wdt-dmec diff --git a/drivers/staging/dmec/Makefile b/drivers/staging/dmec/Makefile index 8b363cc..b55d56f 100644 --- a/drivers/staging/dmec/Makefile +++ b/drivers/staging/dmec/Makefile @@ -2,3 +2,4 @@ obj-$(CONFIG_MFD_DMEC) += dmec-core.o obj-$(CONFIG_I2C_DMEC) += i2c-dmec.o obj-$(CONFIG_GPIO_DMEC) += gpio-dmec.o obj-$(CONFIG_WDT_DMEC) += wdt-dmec.o +obj-$(CONFIG_RTM_DMEC) += rtm-dmec.o diff --git a/drivers/staging/dmec/rtm-dmec.c b/drivers/staging/dmec/rtm-dmec.c new file mode 100644 index 0000000..cd32536 --- /dev/null +++ b/drivers/staging/dmec/rtm-dmec.c @@ -0,0 +1,203 @@ +/* + * Running time meter for Data Modul AG Embedded Controller + * + * Copyright (C) 2016 Data Modul AG + * + * Author: Sebastian Wezel <sebastian@xxxxxxxxxxxxxxxx> + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/mutex.h> + +#include "dmec.h" + +#define DMEC_RTM_RTM_OFFSET 0x60 +#define DMEC_RTM_RTMREV_OFFSET 0x63 +#define DMEC_RTM_OOSRTM_OFFSET 0x64 +#define DMEC_RTM_BTCNT_OFFSET 0x68 +#define DMEC_RTM_BBCTNT_OFFSET 0x6c + +static DEFINE_MUTEX(rtm_lock); +static struct regmap *regmap; +static bool enable_reset; + +static unsigned int dmec_rtm_get_three_byte(unsigned int reg, + struct device *dev) +{ + unsigned int low = 0, mid = 0, high = 0, val = 0; + + regmap_read(regmap, reg, &low); + regmap_read(regmap, reg + 1, &mid); + regmap_read(regmap, reg + 2, &high); + + val = ((high << 16) | (mid << 8) | (low & 0x0000ff)); + return val; +} + +static ssize_t dmec_rtm_time_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned int val = 0; + + val = dmec_rtm_get_three_byte(DMEC_RTM_RTM_OFFSET, dev); + return scnprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t dmec_rtm_time_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + unsigned int low = 0, mid = 0, high = 0, higher = 0; + long val = 0, ret = -EPERM; + int reg = DMEC_RTM_RTM_OFFSET; + + mutex_lock(&rtm_lock); + if (enable_reset && !kstrtol(buf, 10, &val)) { + higher = (val >> 24) & 0xff; + high = (val >> 16) & 0xff; + mid = (val >> 8) & 0xff; + low = val & 0xff; + + regmap_write(regmap, reg, low); + regmap_write(regmap, reg + 1, mid); + regmap_write(regmap, reg + 2, high); + regmap_write(regmap, reg + 3, higher); + enable_reset = false; + ret = count; + } + mutex_unlock(&rtm_lock); + + return ret; +} + +static ssize_t dmec_rtm_version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned int val = 0; + + regmap_read(regmap, DMEC_RTM_RTMREV_OFFSET, &val); + return scnprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t dmec_rtm_out_of_spec_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned int val = 0; + + val = dmec_rtm_get_three_byte(DMEC_RTM_OOSRTM_OFFSET, dev); + return scnprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t dmec_rtm_boot_count_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned int val = 0; + + val = dmec_rtm_get_three_byte(DMEC_RTM_BTCNT_OFFSET, dev); + return scnprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t dmec_rtm_bios_boot_count_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned int val = 0; + + val = dmec_rtm_get_three_byte(DMEC_RTM_BBCTNT_OFFSET, dev); + return scnprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t dmec_rtm_enable_reset_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%d\n", enable_reset); +} + +static ssize_t dmec_rtm_enable_reset_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + long val = 0; + + mutex_lock(&rtm_lock); + if (kstrtol(buf, 10, &val) != 0) + enable_reset = false; + + /* The enable should only work if the value is 1 */ + if (val == 1) + enable_reset = true; + mutex_unlock(&rtm_lock); + return count; +} + +static DEVICE_ATTR(rtm_time, 0664, dmec_rtm_time_show, dmec_rtm_time_store); +static DEVICE_ATTR(rtm_version, 0444, dmec_rtm_version_show, NULL); +static DEVICE_ATTR(rtm_out_of_spec, 0444, dmec_rtm_out_of_spec_show, NULL); +static DEVICE_ATTR(rtm_boot_count, 0444, dmec_rtm_boot_count_show, NULL); +static DEVICE_ATTR(rtm_bios_boot_count, 0444, dmec_rtm_bios_boot_count_show, + NULL); +static DEVICE_ATTR(rtm_enable_reset, 0664, dmec_rtm_enable_reset_show, + dmec_rtm_enable_reset_store); + +static struct attribute *rtm_attribute[] = { + &dev_attr_rtm_time.attr, + &dev_attr_rtm_version.attr, + &dev_attr_rtm_out_of_spec.attr, + &dev_attr_rtm_boot_count.attr, + &dev_attr_rtm_bios_boot_count.attr, + &dev_attr_rtm_enable_reset.attr, + NULL +}; + +static const struct attribute_group rtm_attr_group = { + .attrs = rtm_attribute, +}; + +static int dmec_rtm_probe(struct platform_device *pdev) +{ + int ret; + + regmap = dmec_get_regmap(pdev->dev.parent); + enable_reset = 0; + + ret = sysfs_create_group(&pdev->dev.kobj, &rtm_attr_group); + if (ret) + return ret; + + return ret; +} + +static int dmec_rtm_remove(struct platform_device *pdev) +{ + sysfs_remove_group(&pdev->dev.kobj, &rtm_attr_group); + + return 0; +} + +static struct platform_driver dmec_rtm_driver = { + .driver = { + .name = "dmec-rtm", + .owner = THIS_MODULE, + }, + .probe = dmec_rtm_probe, + .remove = dmec_rtm_remove, +}; + +module_platform_driver(dmec_rtm_driver); + +MODULE_DESCRIPTION("dmec rtm driver"); +MODULE_AUTHOR("Sebastian Wezel <sebastian@xxxxxxxxxxxxxxxx>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:dmec-rtm"); -- git-series 0.8.10 -- To unsubscribe from this list: send the line "unsubscribe linux-gpio" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html