From fcb81b750bf03aa8ea16fd0ba45c0c2927a43d8c Mon Sep 17 00:00:00 2001 From: Oliver Rohe Date: Wed, 9 Sep 2020 11:21:34 +0200 Subject: [PATCH] arch: arm: add wsysinit --- arch/arm/configs/am3xxx_pfc_generic_defconfig | 1 + arch/arm/mach-omap2/Kconfig | 5 + arch/arm/mach-omap2/Makefile | 2 + arch/arm/mach-omap2/wsysinit.c | 346 ++++++++++++++++++ 4 files changed, 354 insertions(+) create mode 100644 arch/arm/mach-omap2/wsysinit.c diff --git a/arch/arm/configs/am3xxx_pfc_generic_defconfig b/arch/arm/configs/am3xxx_pfc_generic_defconfig index f190968ecff3..dbe7f862eba4 100644 --- a/arch/arm/configs/am3xxx_pfc_generic_defconfig +++ b/arch/arm/configs/am3xxx_pfc_generic_defconfig @@ -26,6 +26,7 @@ CONFIG_OMAP_RESET_CLOCKS=y CONFIG_ARCH_OMAP3=y CONFIG_ARCH_OMAP4=y CONFIG_SOC_AM33XX=y +CONFIG_WAGO_SYSTEM_BASED_STARTUP=y # CONFIG_SOC_OMAP3430 is not set # CONFIG_SOC_TI81XX is not set # CONFIG_MACH_OMAP3517EVM is not set diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig index 821727eefd5a..88987f1ae0ec 100644 --- a/arch/arm/mach-omap2/Kconfig +++ b/arch/arm/mach-omap2/Kconfig @@ -120,6 +120,11 @@ config ARCH_OMAP2PLUS help Systems based on OMAP2, OMAP3, OMAP4 or OMAP5 +config WAGO_SYSTEM_BASED_STARTUP + bool "WAGO System based startup code" + help + Enable WAGO System based startup code + config OMAP_INTERCONNECT_BARRIER bool select ARM_HEAVY_MB diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile index daf21127c82f..4de2268c4a49 100644 --- a/arch/arm/mach-omap2/Makefile +++ b/arch/arm/mach-omap2/Makefile @@ -226,3 +226,5 @@ targets += pm-asm-offsets.s clean-files += pm-asm-offsets.h obj-$(CONFIG_OMAP_IOMMU) += omap-iommu.o + +obj-$(CONFIG_WAGO_SYSTEM_BASED_STARTUP) += wsysinit.o diff --git a/arch/arm/mach-omap2/wsysinit.c b/arch/arm/mach-omap2/wsysinit.c new file mode 100644 index 000000000000..5b2eddbaf157 --- /dev/null +++ b/arch/arm/mach-omap2/wsysinit.c @@ -0,0 +1,346 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * WAGO System Init Functions + * + * Copyright (C) 2014 WAGO Automation + * + * Author: Heinrich Toews + */ +#undef DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "soc.h" + +#define DEVICE_NAME "wsysinit-drv" + +#define RESET_TIME_QUECTEL_UC20_G 200 + +struct wsysinit_settings { + bool dp_reset; + bool modem_reset; + int dp_gpio_rst; + int modem_gpio_rst; + bool dp_alow; + const char *tty_service; + const char *tty_rs232_485; + const char *modem; + int drvvbus_gpio; + const char *board_variant; + u32 adjtimex_tick; + u32 adjtimex_frequency; +}; + +static struct wsysinit_settings wsysinitset = { 0,}; + +static struct of_device_id wsysinit_dt_ids[] = { + { .compatible = "wago,sysinit" }, + { } +}; +MODULE_DEVICE_TABLE(of, wsysinit_dt_ids); + +static int dp_trig_reset(struct wsysinit_settings *ps) +{ + /* check if value is valid */ + if (ps == NULL || !gpio_is_valid(ps->dp_gpio_rst)) + return -EINVAL; + + gpiod_set_value_cansleep(gpio_to_desc(ps->dp_gpio_rst), 1); + mdelay(100); + gpiod_set_value_cansleep(gpio_to_desc(ps->dp_gpio_rst), 0); + + pr_info("DPx resetted (pin is active %s)!\n", ps->dp_alow ? "low" : "high"); + + return 0; +} + +static int modem_reset(struct wsysinit_settings *ps) +{ + if (ps == NULL || !gpio_is_valid(ps->modem_gpio_rst)) + return -EINVAL; + + gpiod_set_value_cansleep(gpio_to_desc(ps->modem_gpio_rst), 1); + mdelay(RESET_TIME_QUECTEL_UC20_G); + gpiod_set_value_cansleep(gpio_to_desc(ps->modem_gpio_rst), 0); + + return 0; +} + +static ssize_t dp_sysfs_trig_reset(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + u32 val = simple_strtoul(buf, NULL, 10); + + /* check if value is valid */ + if (val != 1) + return -EINVAL; + + dp_trig_reset(&wsysinitset); + + return count; +} + +static ssize_t modem_sysfs_reset(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + u32 val = simple_strtoul(buf, NULL, 10); + + if (val != 1) + return -EINVAL; + + /* Return value omitted, because is of no value for the user of the command */ + (void)modem_reset(&wsysinitset); + + return count; +} + +static int wsysinit_init_probe_dt(struct platform_device *pdev) +{ + int ret; + struct device_node *np = pdev->dev.of_node; + enum of_gpio_flags flags; + struct __kernel_timex tmx = {0}; + + wsysinitset.dp_reset = of_property_read_bool(np, "dp,reset"); + wsysinitset.dp_gpio_rst = of_get_named_gpio_flags(np, "dp,gpio-rst", 0, &flags); + if (wsysinitset.dp_gpio_rst == -EPROBE_DEFER) + return -EPROBE_DEFER; + + if (wsysinitset.dp_gpio_rst >= 0) { + int rqflags = 0; + + wsysinitset.dp_alow = (flags & OF_GPIO_ACTIVE_LOW) ? 1 : 0; + + if (wsysinitset.dp_alow) /* FLAG_ACTIVE_LOW ?? */ + rqflags |= GPIOF_ACTIVE_LOW | GPIOF_OUT_INIT_LOW; + else + rqflags |= GPIOF_OUT_INIT_HIGH; + + ret = devm_gpio_request_one(&pdev->dev, wsysinitset.dp_gpio_rst, + rqflags | GPIOF_EXPORT_DIR_FIXED, /* */ + "Profibus Reset Pin"); + if (ret < 0) { + dev_err(&pdev->dev, "failed to claim DP reset pin\n"); + return ret; + } + + if (wsysinitset.dp_reset) + dp_trig_reset(&wsysinitset); + + } else if (wsysinitset.dp_reset) { + dev_err(&pdev->dev, "failed to find DP gpio reset signal!\n"); + return -1; + } + + wsysinitset.modem_reset = of_property_read_bool(np, "modem,reset"); + wsysinitset.modem_gpio_rst = of_get_named_gpio_flags(np, "modem,gpio-rst", 0, &flags); + if (wsysinitset.modem_gpio_rst == -EPROBE_DEFER) + return -EPROBE_DEFER; + + if (wsysinitset.modem_gpio_rst >= 0) { + ret = devm_gpio_request_one(&pdev->dev, wsysinitset.modem_gpio_rst, + GPIOF_EXPORT_DIR_FIXED, + "Modem Reset Pin"); + if (ret < 0) { + dev_err(&pdev->dev, "failed to claim modem reset pin\n"); + return ret; + } + + if (wsysinitset.modem_reset) + modem_reset(&wsysinitset); + + /* devm_gpio_free(&pdev->dev, wsysinitset.dp_gpio_rst); */ + } else if (wsysinitset.modem_reset) { + dev_err(&pdev->dev, "failed to find modem reset signal!\n"); + return -1; + } + + if (of_property_read_string(np, "modem,type", &wsysinitset.modem)) + wsysinitset.modem = NULL; + + if (of_property_read_string(np, "tty,service", &wsysinitset.tty_service)) + wsysinitset.tty_service = NULL; + + if (of_property_read_string(np, "tty,rs232-485", &wsysinitset.tty_rs232_485)) + wsysinitset.tty_rs232_485 = NULL; + + if (of_property_read_string(np, "board,variant", &wsysinitset.board_variant)) + wsysinitset.board_variant = NULL; + + if (!of_property_read_u32(np, "adjtimex,tick", &wsysinitset.adjtimex_tick)) { + tmx.modes |= ADJ_TICK; + tmx.tick = (__kernel_long_t) wsysinitset.adjtimex_tick; + } + + if (!of_property_read_u32(np, "adjtimex,frequency", &wsysinitset.adjtimex_frequency)) { + tmx.modes |= ADJ_FREQUENCY; + tmx.freq = (__kernel_long_t) wsysinitset.adjtimex_frequency; + } + + if (tmx.modes) { + do_adjtimex(&tmx); + + pr_debug("%s: adjtimex: tick %u, frequency %u\n", __func__, + wsysinitset.adjtimex_tick, wsysinitset.adjtimex_frequency); + } + + return 0; +} + +/* WSYSINIT Specific Kernel Parameters */ + +char *__wsysinit_bootversion = NULL; +core_param(bootversion, __wsysinit_bootversion, charp, 0); +EXPORT_SYMBOL(__wsysinit_bootversion); + +/* WSYSINIT SYSFS Init */ + +static dev_t wsysinit_sysfs_dev; +struct class* wsysinit_sysfs_class = NULL; +struct device* wsysinit_sysfs_device = NULL; +EXPORT_SYMBOL(wsysinit_sysfs_class); +EXPORT_SYMBOL(wsysinit_sysfs_device); + +ssize_t wsysinit_sysfs_bootversion_show(struct device* dev, struct device_attribute* attr, char* buf) +{ + if (__wsysinit_bootversion) + sprintf (buf, "%s\n", __wsysinit_bootversion); + else + sprintf (buf, "unknown\n"); + return strlen(buf); +} + +ssize_t wsysinit_sysfs_tty_service_show(struct device* dev, struct device_attribute* attr, char* buf) +{ + if (wsysinitset.tty_service) + sprintf(buf, "%s\n", wsysinitset.tty_service); + else + sprintf(buf, "%s\n", "unknown"); + + return strlen(buf); +} + +ssize_t wsysinit_sysfs_tty_rs232_485_show(struct device* dev, struct device_attribute* attr, char* buf) +{ + if (wsysinitset.tty_rs232_485) + sprintf(buf, "%s\n", wsysinitset.tty_rs232_485); + else + sprintf(buf, "%s\n", "unknown"); + + return strlen(buf); +} + +ssize_t wsysinit_sysfs_board_variant_show(struct device* dev, struct device_attribute* attr, char* buf) +{ + if (wsysinitset.board_variant) + sprintf(buf, "%s\n", wsysinitset.board_variant); + else + sprintf(buf, "%s\n", "unknown"); + + return strlen(buf); +} + +DEVICE_ATTR ( bootversion , 0444, wsysinit_sysfs_bootversion_show, NULL); +DEVICE_ATTR ( tty_service , 0444, wsysinit_sysfs_tty_service_show, NULL); +DEVICE_ATTR ( tty_rs232_485, 0444, wsysinit_sysfs_tty_rs232_485_show, NULL); +DEVICE_ATTR ( dp_trig_reset, 0200, NULL, dp_sysfs_trig_reset); +DEVICE_ATTR ( board_variant, 0444, wsysinit_sysfs_board_variant_show, NULL); +DEVICE_ATTR ( modem_reset, 0200, NULL, modem_sysfs_reset); + +static void wsysinit_sysfs_init(void) +{ + device_create_file(wsysinit_sysfs_device, &dev_attr_bootversion); + device_create_file(wsysinit_sysfs_device, &dev_attr_dp_trig_reset); + device_create_file(wsysinit_sysfs_device, &dev_attr_board_variant); + + if (wsysinitset.modem) + device_create_file(wsysinit_sysfs_device, &dev_attr_modem_reset); + + if (wsysinitset.tty_service) + device_create_file(wsysinit_sysfs_device, &dev_attr_tty_service); + + if (wsysinitset.tty_rs232_485) + device_create_file(wsysinit_sysfs_device, &dev_attr_tty_rs232_485); +} + +static int wsysinit_init_probe(struct platform_device *pdev) +{ + int rc; + + if (!pdev->dev.of_node) { + dev_err(&pdev->dev, "WSYSINIT Init: No DT node found!\n"); + return -1; + } + + rc = wsysinit_init_probe_dt(pdev); + if (rc < 0) { + dev_err(&pdev->dev, "failed to probe DT parameters\n"); + return rc; + } + + wsysinit_sysfs_init(); + + pr_info("WAGO WSYSINIT Init: %s probed.\n", pdev->dev.of_node->name); + + return 0; +} + +static int wsysinit_init_remove(struct platform_device *pdev) +{ + return 0; +} + +static struct platform_driver wsysinit_init_driver = { + .probe = wsysinit_init_probe, + .remove = wsysinit_init_remove, + .driver = { + .name = DEVICE_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(wsysinit_dt_ids), + }, +}; + +static __init int wsysinit_init(void) +{ + pr_info("WSYSINIT: create sysfs entries\n"); + + wsysinit_sysfs_class = class_create("wago"); + if (IS_ERR(wsysinit_sysfs_class)) { + + pr_err("%s: class_create: error %li\n", + __func__, PTR_ERR(wsysinit_sysfs_class)); + unregister_chrdev_region(wsysinit_sysfs_dev, 1); + return -1; + } + + wsysinit_sysfs_device = device_create (wsysinit_sysfs_class, NULL, + wsysinit_sysfs_dev, NULL, "system"); + + return platform_driver_register(&wsysinit_init_driver); +} + +static __exit void wsysinit_exit(void) +{ + platform_driver_unregister(&wsysinit_init_driver); +} + +omap_postcore_initcall(wsysinit_init); +module_exit(wsysinit_exit);