summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMihaela Martinas <Mihaela.Martinas@freescale.com>2015-09-17 18:52:09 +0300
committerDong Aisheng <aisheng.dong@nxp.com>2019-11-25 15:46:39 +0800
commitca4e90d8191cf5aa23ca4f2edad4d3347fb9a44c (patch)
treef0484fbf11c4178d1bee8a29c53b32582643cc23
parentfbc2f711235aa7a618acd6c569d8aac2acd78bb0 (diff)
pinctrl: Add pinmuxing driver for S32V234
Add basic support for S32V234 SIUL2 pin controller, based on i.MX model. Definitions of MSCR PADs, which are used for I/O (MSCR < 512), were added. Definitions of IMCR (input only) PADs (MSCR >= 512), corresponding to specific peripherals like ENET, I2C, UART or uSDHC are going to be added along with the drivers which need them. Add configurations for specific S32 SoC selection and pinmuxing driver. Signed-off-by: Mihaela Martinas <Mihaela.Martinas@freescale.com> Signed-off-by: Costin Carabas <costin.carabas@nxp.com> Signed-off-by: Nica Dan <dan.nica@nxp.com> Signed-off-by: Ghennadi Procopciuc <Ghennadi.Procopciuc@nxp.com> Signed-off-by: Matthew Nunez <matthew.nunez@nxp.com> Signed-off-by: Stefan-Gabriel Mirea <stefan-gabriel.mirea@nxp.com>
-rw-r--r--arch/arm64/Kconfig.platforms8
-rw-r--r--drivers/pinctrl/freescale/Kconfig13
-rw-r--r--drivers/pinctrl/freescale/Makefile2
-rw-r--r--drivers/pinctrl/freescale/pinctrl-s32v-core.c526
-rw-r--r--drivers/pinctrl/freescale/pinctrl-s32v.h72
-rw-r--r--drivers/pinctrl/freescale/pinctrl-s32v234.c389
6 files changed, 1010 insertions, 0 deletions
diff --git a/arch/arm64/Kconfig.platforms b/arch/arm64/Kconfig.platforms
index 17f1c34ec750..dfee554cc774 100644
--- a/arch/arm64/Kconfig.platforms
+++ b/arch/arm64/Kconfig.platforms
@@ -214,9 +214,17 @@ config ARCH_ROCKCHIP
config ARCH_S32
bool "NXP S32 SoC Family"
+ select PINCTRL
help
This enables support for the NXP S32 family of processors.
+if ARCH_S32
+menu "S32 SOC selection"
+ config SOC_S32V234
+ bool "S32V234 SOC"
+endmenu
+endif
+
config ARCH_SEATTLE
bool "AMD Seattle SoC Family"
help
diff --git a/drivers/pinctrl/freescale/Kconfig b/drivers/pinctrl/freescale/Kconfig
index 5f4058033ec6..01bc57662ac3 100644
--- a/drivers/pinctrl/freescale/Kconfig
+++ b/drivers/pinctrl/freescale/Kconfig
@@ -177,3 +177,16 @@ config PINCTRL_IMX23
config PINCTRL_IMX28
bool
select PINCTRL_MXS
+
+config PINCTRL_S32V_CORE
+ bool "Core driver for the S32V pin controller"
+ help
+ Say Y here to enable the S32V pin controller
+
+config PINCTRL_S32V234
+ bool "Freescale S32V234 pinctrl driver"
+ depends on SOC_S32V234
+ select PINCTRL_IMX
+ select PINCTRL_S32V_CORE
+ help
+ Say Y here to enable the Freescale S32V234 pinctrl driver
diff --git a/drivers/pinctrl/freescale/Makefile b/drivers/pinctrl/freescale/Makefile
index 78e9140c13e3..5ec6e6f9f959 100644
--- a/drivers/pinctrl/freescale/Makefile
+++ b/drivers/pinctrl/freescale/Makefile
@@ -28,3 +28,5 @@ obj-$(CONFIG_PINCTRL_MXS) += pinctrl-mxs.o
obj-$(CONFIG_PINCTRL_IMX23) += pinctrl-imx23.o
obj-$(CONFIG_PINCTRL_IMX25) += pinctrl-imx25.o
obj-$(CONFIG_PINCTRL_IMX28) += pinctrl-imx28.o
+obj-$(CONFIG_PINCTRL_S32V234) += pinctrl-s32v234.o
+obj-$(CONFIG_PINCTRL_S32V_CORE) += pinctrl-s32v-core.o
diff --git a/drivers/pinctrl/freescale/pinctrl-s32v-core.c b/drivers/pinctrl/freescale/pinctrl-s32v-core.c
new file mode 100644
index 000000000000..75d0adc2be25
--- /dev/null
+++ b/drivers/pinctrl/freescale/pinctrl-s32v-core.c
@@ -0,0 +1,526 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Core driver for the S32V pin controller
+ *
+ * Copyright (C) 2015-2016 Freescale Semiconductor, Inc.
+ * Copyright (C) 2017 NXP
+ *
+ * Based on pinctrl-imx.c:
+ * Author: Dong Aisheng <dong.aisheng@linaro.org>
+ * Copyright (C) 2012 Freescale Semiconductor, Inc.
+ * Copyright (C) 2012 Linaro Ltd.
+ */
+
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/pinctrl/machine.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/slab.h>
+
+#include "../core.h"
+#include "pinctrl-s32v.h"
+
+/**
+ * @dev: a pointer back to containing device
+ * @base: the offset to the controller in virtual memory
+ */
+struct s32v_pinctrl {
+ struct device *dev;
+ struct pinctrl_dev *pctl;
+ void __iomem *base;
+ const struct s32v_pinctrl_soc_info *info;
+};
+
+static const char *pin_get_name_from_info(struct s32v_pinctrl_soc_info *info,
+ const unsigned int pin_id)
+{
+ int i;
+
+ for (i = 0; i < info->npins; i++) {
+ if (info->pins[i].number == pin_id)
+ return info->pins[i].name;
+ }
+
+ return NULL;
+}
+
+static inline const struct s32v_pin_group *s32v_pinctrl_find_group_by_name(
+ const struct s32v_pinctrl_soc_info *info,
+ const char *name)
+{
+ const struct s32v_pin_group *grp = NULL;
+ unsigned int i;
+
+ for (i = 0; i < info->ngroups; i++) {
+ if (!strcmp(info->groups[i].name, name)) {
+ grp = &info->groups[i];
+ break;
+ }
+ }
+
+ return grp;
+}
+
+static int s32v_get_groups_count(struct pinctrl_dev *pctldev)
+{
+ struct s32v_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev);
+ const struct s32v_pinctrl_soc_info *info = ipctl->info;
+
+ return info->ngroups;
+}
+
+static const char *s32v_get_group_name(struct pinctrl_dev *pctldev,
+ unsigned int selector)
+{
+ struct s32v_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev);
+ const struct s32v_pinctrl_soc_info *info = ipctl->info;
+
+ return info->groups[selector].name;
+}
+
+static int s32v_get_group_pins(struct pinctrl_dev *pctldev,
+ unsigned int selector, const unsigned int **pins,
+ unsigned int *npins)
+{
+ struct s32v_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev);
+ const struct s32v_pinctrl_soc_info *info = ipctl->info;
+
+ if (selector >= info->ngroups)
+ return -EINVAL;
+
+ *pins = info->groups[selector].pin_ids;
+ *npins = info->groups[selector].npins;
+
+ return 0;
+}
+
+static void s32v_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s,
+ unsigned int offset)
+{
+ seq_printf(s, "%s", dev_name(pctldev->dev));
+}
+
+static int s32v_dt_node_to_map(struct pinctrl_dev *pctldev,
+ struct device_node *np,
+ struct pinctrl_map **map, unsigned int *num_maps)
+{
+ struct s32v_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev);
+ const struct s32v_pinctrl_soc_info *info = ipctl->info;
+ const struct s32v_pin_group *grp;
+ struct pinctrl_map *new_map;
+ struct device_node *parent;
+ int map_num = 1;
+ int i, j;
+
+ /*
+ * first find the group of this node and check if we need create
+ * config maps for pins
+ */
+ grp = s32v_pinctrl_find_group_by_name(info, np->name);
+ if (!grp) {
+ dev_err(info->dev, "unable to find group for node %s\n",
+ np->name);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < grp->npins; i++)
+ map_num++;
+
+ new_map = kmalloc_array(map_num, sizeof(struct pinctrl_map),
+ GFP_KERNEL);
+ if (!new_map)
+ return -ENOMEM;
+
+ *map = new_map;
+ *num_maps = map_num;
+
+ /* create mux map */
+ parent = of_get_parent(np);
+ if (!parent) {
+ kfree(new_map);
+ return -EINVAL;
+ }
+ new_map[0].type = PIN_MAP_TYPE_MUX_GROUP;
+ new_map[0].data.mux.function = parent->name;
+ new_map[0].data.mux.group = np->name;
+ of_node_put(parent);
+
+ /* create config map */
+ new_map++;
+ for (i = j = 0; i < grp->npins; i++) {
+ new_map[j].type = PIN_MAP_TYPE_CONFIGS_PIN;
+ new_map[j].data.configs.group_or_pin =
+ pin_get_name(pctldev, grp->pins[i].pin_id);
+ new_map[j].data.configs.configs = &grp->pins[i].config;
+ new_map[j].data.configs.num_configs = 1;
+ j++;
+ }
+
+ dev_dbg(pctldev->dev, "maps: function %s group %s num %d\n",
+ (*map)->data.mux.function, (*map)->data.mux.group, map_num);
+
+ return 0;
+}
+
+static void s32v_dt_free_map(struct pinctrl_dev *pctldev,
+ struct pinctrl_map *map, unsigned int num_maps)
+{
+ kfree(map);
+}
+
+static const struct pinctrl_ops s32v_pctrl_ops = {
+ .get_groups_count = s32v_get_groups_count,
+ .get_group_name = s32v_get_group_name,
+ .get_group_pins = s32v_get_group_pins,
+ .pin_dbg_show = s32v_pin_dbg_show,
+ .dt_node_to_map = s32v_dt_node_to_map,
+ .dt_free_map = s32v_dt_free_map,
+
+};
+
+static int s32v_pmx_set(struct pinctrl_dev *pctldev, unsigned int selector,
+ unsigned int group)
+{
+ struct s32v_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev);
+ const struct s32v_pinctrl_soc_info *info = ipctl->info;
+ unsigned int npins, pin_id;
+ int i;
+ struct s32v_pin_group *grp;
+
+ /*
+ * Configure the mux mode for each pin in the group for a specific
+ * function.
+ */
+ grp = &info->groups[group];
+ npins = grp->npins;
+
+ dev_dbg(ipctl->dev, "enable function %s group %s\n",
+ info->functions[selector].name, grp->name);
+
+ for (i = 0; i < npins; i++) {
+ struct s32v_pin *pin = &grp->pins[i];
+
+ pin_id = pin->pin_id;
+
+ writel(pin->config, ipctl->base + S32V_PAD_CONFIG(pin_id));
+ dev_dbg(ipctl->dev, "write: offset 0x%x val %lu\n",
+ S32V_PAD_CONFIG(pin_id), pin->config);
+ }
+
+ return 0;
+}
+
+static int s32v_pmx_get_funcs_count(struct pinctrl_dev *pctldev)
+{
+ struct s32v_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev);
+ const struct s32v_pinctrl_soc_info *info = ipctl->info;
+
+ return info->nfunctions;
+}
+
+static const char *s32v_pmx_get_func_name(struct pinctrl_dev *pctldev,
+ unsigned int selector)
+{
+ struct s32v_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev);
+ const struct s32v_pinctrl_soc_info *info = ipctl->info;
+
+ return info->functions[selector].name;
+}
+
+static int s32v_pmx_get_groups(struct pinctrl_dev *pctldev,
+ unsigned int selector,
+ const char * const **groups,
+ unsigned int * const num_groups)
+{
+ struct s32v_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev);
+ const struct s32v_pinctrl_soc_info *info = ipctl->info;
+
+ *groups = info->functions[selector].groups;
+ *num_groups = info->functions[selector].num_groups;
+
+ return 0;
+}
+
+static const struct pinmux_ops s32v_pmx_ops = {
+ .get_functions_count = s32v_pmx_get_funcs_count,
+ .get_function_name = s32v_pmx_get_func_name,
+ .get_function_groups = s32v_pmx_get_groups,
+ .set_mux = s32v_pmx_set,
+};
+
+static int s32v_pinconf_get(struct pinctrl_dev *pctldev,
+ unsigned int pin_id, unsigned long *config)
+{
+ struct s32v_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev);
+
+ *config = readl(ipctl->base + S32V_PAD_CONFIG(pin_id));
+
+ return 0;
+}
+
+static int s32v_pinconf_set(struct pinctrl_dev *pctldev,
+ unsigned int pin_id, unsigned long *configs,
+ unsigned int num_configs)
+{
+ struct s32v_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev);
+ int i;
+
+ dev_dbg(ipctl->dev, "pinconf set pin %s\n",
+ pin_get_name(pctldev, pin_id));
+
+ for (i = 0; i < num_configs; i++) {
+ writel(configs[i], ipctl->base + S32V_PAD_CONFIG(pin_id));
+ dev_dbg(ipctl->dev, "write: offset 0x%x val 0x%lx\n",
+ S32V_PAD_CONFIG(pin_id), configs[i]);
+ } /* for each config */
+
+ return 0;
+}
+
+static void s32v_pinconf_dbg_show(struct pinctrl_dev *pctldev,
+ struct seq_file *s, unsigned int pin_id)
+{
+ struct s32v_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev);
+ unsigned long config;
+
+ config = readl(ipctl->base + S32V_PAD_CONFIG(pin_id));
+ seq_printf(s, "0x%lx", config);
+}
+
+static void s32v_pinconf_group_dbg_show(struct pinctrl_dev *pctldev,
+ struct seq_file *s, unsigned int group)
+{
+ struct s32v_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev);
+ const struct s32v_pinctrl_soc_info *info = ipctl->info;
+ struct s32v_pin_group *grp;
+ unsigned long config;
+ const char *name;
+ int i, ret;
+
+ if (group > info->ngroups)
+ return;
+
+ seq_puts(s, "\n");
+ grp = &info->groups[group];
+ for (i = 0; i < grp->npins; i++) {
+ struct s32v_pin *pin = &grp->pins[i];
+
+ name = pin_get_name(pctldev, pin->pin_id);
+ ret = s32v_pinconf_get(pctldev, pin->pin_id, &config);
+ if (ret)
+ return;
+ seq_printf(s, "%s: 0x%lx", name, config);
+ }
+}
+
+static const struct pinconf_ops s32v_pinconf_ops = {
+ .pin_config_get = s32v_pinconf_get,
+ .pin_config_set = s32v_pinconf_set,
+ .pin_config_dbg_show = s32v_pinconf_dbg_show,
+ .pin_config_group_dbg_show = s32v_pinconf_group_dbg_show,
+};
+
+static struct pinctrl_desc s32v_pinctrl_desc = {
+ .pctlops = &s32v_pctrl_ops,
+ .pmxops = &s32v_pmx_ops,
+ .confops = &s32v_pinconf_ops,
+ .owner = THIS_MODULE,
+};
+
+/*
+ * Each pin represented in fsl,pins consists of 5 u32 PIN_FUNC_ID and
+ * 1 u32 CONFIG, so 24 types in total for each pin.
+ */
+#define FSL_PIN_SIZE 24
+#define SHARE_FSL_PIN_SIZE 20
+
+static int s32v_pinctrl_parse_groups(struct device_node *np,
+ struct s32v_pin_group *grp,
+ struct s32v_pinctrl_soc_info *info,
+ u32 index)
+{
+ int size, i;
+ const __be32 *list;
+
+ dev_dbg(info->dev, "group(%d): %s\n", index, np->name);
+
+ /* Initialise group */
+ grp->name = np->name;
+
+ /*
+ * the binding format is fsl,pins = <PIN CONFIG>,
+ * do sanity check and calculate pins number
+ */
+ list = of_get_property(np, "fsl,pins", &size);
+ if (!list) {
+ dev_err(info->dev, "no fsl,pins property in node %s\n",
+ np->full_name);
+ return -EINVAL;
+ }
+
+ /* we do not check return since it's safe node passed down */
+ if (!size || size % S32V_PIN_SIZE) {
+ dev_err(info->dev, "Invalid fsl,pins property in node %s\n",
+ np->full_name);
+ return -EINVAL;
+ }
+
+ grp->npins = size / S32V_PIN_SIZE;
+ grp->pins = devm_kzalloc(info->dev,
+ grp->npins * sizeof(struct s32v_pin),
+ GFP_KERNEL);
+ grp->pin_ids = devm_kzalloc(info->dev,
+ grp->npins * sizeof(unsigned int),
+ GFP_KERNEL);
+ if (!grp->pins || !grp->pin_ids)
+ return -ENOMEM;
+
+ for (i = 0; i < grp->npins; i++) {
+ struct s32v_pin *pin = &grp->pins[i];
+
+ pin->pin_id = be32_to_cpu(*list++);
+ pin->config = be32_to_cpu(*list++);
+ grp->pin_ids[i] = grp->pins[i].pin_id;
+
+ dev_dbg(info->dev, "%s: 0x%08lx",
+ pin_get_name_from_info(info, pin->pin_id), pin->config);
+ }
+
+ return 0;
+}
+
+static int s32v_pinctrl_parse_functions(struct device_node *np,
+ struct s32v_pinctrl_soc_info *info,
+ u32 index)
+{
+ struct device_node *child;
+ struct s32v_pmx_func *func;
+ struct s32v_pin_group *grp;
+ static u32 grp_index;
+ u32 i = 0;
+
+ dev_dbg(info->dev, "parse function(%d): %s\n", index, np->name);
+
+ func = &info->functions[index];
+
+ /* Initialise function */
+ func->name = np->name;
+ func->num_groups = of_get_child_count(np);
+ if (func->num_groups == 0) {
+ dev_err(info->dev, "no groups defined in %s\n", np->full_name);
+ return -EINVAL;
+ }
+ func->groups = devm_kzalloc(info->dev,
+ func->num_groups * sizeof(char *),
+ GFP_KERNEL);
+
+ for_each_child_of_node(np, child) {
+ func->groups[i] = child->name;
+ grp = &info->groups[grp_index++];
+ s32v_pinctrl_parse_groups(child, grp, info, i++);
+ }
+
+ return 0;
+}
+
+static int s32v_pinctrl_probe_dt(struct platform_device *pdev,
+ struct s32v_pinctrl_soc_info *info)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct device_node *child;
+ u32 nfuncs = 0;
+ u32 i = 0;
+
+ if (!np)
+ return -ENODEV;
+
+ nfuncs = of_get_child_count(np);
+ if (nfuncs <= 0) {
+ dev_err(&pdev->dev, "no functions defined\n");
+ return -EINVAL;
+ }
+
+ info->nfunctions = nfuncs;
+ info->functions = devm_kzalloc(&pdev->dev,
+ nfuncs * sizeof(struct s32v_pmx_func),
+ GFP_KERNEL);
+ if (!info->functions)
+ return -ENOMEM;
+
+ info->ngroups = 0;
+ for_each_child_of_node(np, child)
+ info->ngroups += of_get_child_count(child);
+ info->groups = devm_kzalloc(&pdev->dev, info->ngroups *
+ sizeof(struct s32v_pin_group),
+ GFP_KERNEL);
+ if (!info->groups)
+ return -ENOMEM;
+
+ for_each_child_of_node(np, child)
+ s32v_pinctrl_parse_functions(child, info, i++);
+
+ return 0;
+}
+
+int s32v_pinctrl_probe(struct platform_device *pdev,
+ struct s32v_pinctrl_soc_info *info)
+{
+ struct s32v_pinctrl *ipctl;
+ struct resource *res;
+ int ret;
+
+ if (!info || !info->pins || !info->npins) {
+ dev_err(&pdev->dev, "wrong pinctrl info\n");
+ return -EINVAL;
+ }
+ info->dev = &pdev->dev;
+
+ /* Create state holders etc for this driver */
+ ipctl = devm_kzalloc(&pdev->dev, sizeof(*ipctl), GFP_KERNEL);
+ if (!ipctl)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ ipctl->base = devm_ioremap_resource(&pdev->dev, res);
+
+ if (IS_ERR(ipctl->base))
+ return PTR_ERR(ipctl->base);
+
+ s32v_pinctrl_desc.name = dev_name(&pdev->dev);
+ s32v_pinctrl_desc.pins = info->pins;
+ s32v_pinctrl_desc.npins = info->npins;
+
+ ret = s32v_pinctrl_probe_dt(pdev, info);
+ if (ret) {
+ dev_err(&pdev->dev, "fail to probe dt properties\n");
+ return ret;
+ }
+
+ ipctl->info = info;
+ ipctl->dev = info->dev;
+ platform_set_drvdata(pdev, ipctl);
+ ipctl->pctl = pinctrl_register(&s32v_pinctrl_desc, &pdev->dev, ipctl);
+ if (!ipctl->pctl) {
+ dev_err(&pdev->dev, "could not register s32 pinctrl driver\n");
+ return -EINVAL;
+ }
+
+ dev_info(&pdev->dev, "initialized s32 pinctrl driver\n");
+
+ return 0;
+}
+
+int s32v_pinctrl_remove(struct platform_device *pdev)
+{
+ struct s32v_pinctrl *ipctl = platform_get_drvdata(pdev);
+
+ pinctrl_unregister(ipctl->pctl);
+
+ return 0;
+}
diff --git a/drivers/pinctrl/freescale/pinctrl-s32v.h b/drivers/pinctrl/freescale/pinctrl-s32v.h
new file mode 100644
index 000000000000..f231607bbc5b
--- /dev/null
+++ b/drivers/pinctrl/freescale/pinctrl-s32v.h
@@ -0,0 +1,72 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * S32V pinmux core definitions
+ *
+ * Copyright (C) 2017 NXP
+ * Copyright (C) 2015-2016 Freescale Semiconductor, Inc.
+ * Copyright (C) 2012 Linaro Ltd.
+ *
+ * Based on pinctrl-imx.h, by Dong Aisheng <dong.aisheng@linaro.org>
+ */
+
+#ifndef __DRIVERS_PINCTRL_S32V_H
+#define __DRIVERS_PINCTRL_S32V_H
+
+struct platform_device;
+
+/**
+ * struct s32v_pin - describes a single S32V pin
+ * @pin_id: the pin_id of this pin
+ * @config: the config for this pin.
+ */
+struct s32v_pin {
+ unsigned int pin_id;
+ unsigned long config;
+};
+
+/**
+ * struct s32v_pin_group - describes an S32V pin group
+ * @name: the name of this specific pin group
+ * @npins: the number of pins in this group array, i.e. the number of
+ * elements in .pins so we can iterate over that array
+ * @pin_ids: array of pin_ids. pinctrl forces us to maintain such an array
+ * @pins: array of pins
+ */
+struct s32v_pin_group {
+ const char *name;
+ unsigned int npins;
+ unsigned int *pin_ids;
+ struct s32v_pin *pins;
+};
+
+/**
+ * struct s32v_pmx_func - describes S32V pinmux functions
+ * @name: the name of this specific function
+ * @groups: corresponding pin groups
+ * @num_groups: the number of groups
+ */
+struct s32v_pmx_func {
+ const char *name;
+ const char **groups;
+ unsigned int num_groups;
+};
+
+struct s32v_pinctrl_soc_info {
+ struct device *dev;
+ const struct pinctrl_pin_desc *pins;
+ unsigned int npins;
+ struct s32v_pin_group *groups;
+ unsigned int ngroups;
+ struct s32v_pmx_func *functions;
+ unsigned int nfunctions;
+ unsigned int flags;
+};
+
+#define S32V_PINCTRL_PIN(pin) PINCTRL_PIN(pin, #pin)
+#define S32V_PAD_CONFIG(idx) (0x240 + (idx) * 4)
+#define S32V_PIN_SIZE (8)
+
+int s32v_pinctrl_probe(struct platform_device *pdev,
+ struct s32v_pinctrl_soc_info *info);
+int s32v_pinctrl_remove(struct platform_device *pdev);
+#endif /* __DRIVERS_PINCTRL_S32V_H */
diff --git a/drivers/pinctrl/freescale/pinctrl-s32v234.c b/drivers/pinctrl/freescale/pinctrl-s32v234.c
new file mode 100644
index 000000000000..959302ed4113
--- /dev/null
+++ b/drivers/pinctrl/freescale/pinctrl-s32v234.c
@@ -0,0 +1,389 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * s32v234 pinctrl driver based on imx pinmux and pinconf core
+ *
+ * Copyright 2015-2016 Freescale Semiconductor, Inc.
+ * Copyright 2017 NXP
+ */
+
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/pinctrl/pinctrl.h>
+
+#include "pinctrl-s32v.h"
+
+enum s32v234_pins {
+ S32V234_MSCR_PA0 = 0,
+ S32V234_MSCR_PA1 = 1,
+ S32V234_MSCR_PA2 = 2,
+ S32V234_MSCR_PA3 = 3,
+ S32V234_MSCR_PA4 = 4,
+ S32V234_MSCR_PA5 = 5,
+ S32V234_MSCR_PA6 = 6,
+ S32V234_MSCR_PA7 = 7,
+ S32V234_MSCR_PA8 = 8,
+ S32V234_MSCR_PA9 = 9,
+ S32V234_MSCR_PA10 = 10,
+ S32V234_MSCR_PA11 = 11,
+ S32V234_MSCR_PA12 = 12,
+ S32V234_MSCR_PA13 = 13,
+ S32V234_MSCR_PA14 = 14,
+ S32V234_MSCR_PA15 = 15,
+ S32V234_MSCR_PB0 = 16,
+ S32V234_MSCR_PB1 = 17,
+ S32V234_MSCR_PB2 = 18,
+ S32V234_MSCR_PB3 = 19,
+ S32V234_MSCR_PB4 = 20,
+ S32V234_MSCR_PB5 = 21,
+ S32V234_MSCR_PB6 = 22,
+ S32V234_MSCR_PB7 = 23,
+ S32V234_MSCR_PB8 = 24,
+ S32V234_MSCR_PB9 = 25,
+ S32V234_MSCR_PB10 = 26,
+ S32V234_MSCR_PB11 = 27,
+ S32V234_MSCR_PB12 = 28,
+ S32V234_MSCR_PB13 = 29,
+ S32V234_MSCR_PB14 = 30,
+ S32V234_MSCR_PB15 = 31,
+ S32V234_MSCR_PC0 = 32,
+ S32V234_MSCR_PC1 = 33,
+ S32V234_MSCR_PC2 = 34,
+ S32V234_MSCR_PC3 = 35,
+ S32V234_MSCR_PC4 = 36,
+ S32V234_MSCR_PC5 = 37,
+ S32V234_MSCR_PC6 = 38,
+ S32V234_MSCR_PC7 = 39,
+ S32V234_MSCR_PC8 = 40,
+ S32V234_MSCR_PC9 = 41,
+ S32V234_MSCR_PC10 = 42,
+ S32V234_MSCR_PC11 = 43,
+ S32V234_MSCR_PC12 = 44,
+ S32V234_MSCR_PC13 = 45,
+ S32V234_MSCR_PC14 = 46,
+ S32V234_MSCR_PC15 = 47,
+ S32V234_MSCR_PD0 = 48,
+ S32V234_MSCR_PD1 = 49,
+ S32V234_MSCR_PD2 = 50,
+ S32V234_MSCR_PD3 = 51,
+ S32V234_MSCR_PD4 = 52,
+ S32V234_MSCR_PD5 = 53,
+ S32V234_MSCR_PD6 = 54,
+ S32V234_MSCR_PD7 = 55,
+ S32V234_MSCR_PD8 = 56,
+ S32V234_MSCR_PD9 = 57,
+ S32V234_MSCR_PD10 = 58,
+ S32V234_MSCR_PD11 = 59,
+ S32V234_MSCR_PD12 = 60,
+ S32V234_MSCR_PD13 = 61,
+ S32V234_MSCR_PD14 = 62,
+ S32V234_MSCR_PD15 = 63,
+ S32V234_MSCR_PE0 = 64,
+ S32V234_MSCR_PE1 = 65,
+ S32V234_MSCR_PE2 = 66,
+ S32V234_MSCR_PE3 = 67,
+ S32V234_MSCR_PE4 = 68,
+ S32V234_MSCR_PE5 = 69,
+ S32V234_MSCR_PE6 = 70,
+ S32V234_MSCR_PE7 = 71,
+ S32V234_MSCR_PE8 = 72,
+ S32V234_MSCR_PE9 = 73,
+ S32V234_MSCR_PE10 = 74,
+ S32V234_MSCR_PE11 = 75,
+ S32V234_MSCR_PE12 = 76,
+ S32V234_MSCR_PE13 = 77,
+ S32V234_MSCR_PE14 = 78,
+ S32V234_MSCR_PE15 = 79,
+ S32V234_MSCR_PF0 = 80,
+ S32V234_MSCR_PF1 = 81,
+ S32V234_MSCR_PF2 = 82,
+ S32V234_MSCR_PF3 = 83,
+ S32V234_MSCR_PF4 = 84,
+ S32V234_MSCR_PF5 = 85,
+ S32V234_MSCR_PF6 = 86,
+ S32V234_MSCR_PF7 = 87,
+ S32V234_MSCR_PF8 = 88,
+ S32V234_MSCR_PF9 = 89,
+ S32V234_MSCR_PF10 = 90,
+ S32V234_MSCR_PF11 = 91,
+ S32V234_MSCR_PF12 = 92,
+ S32V234_MSCR_PF13 = 93,
+ S32V234_MSCR_PF14 = 94,
+ S32V234_MSCR_PF15 = 95,
+ S32V234_MSCR_PG0 = 96,
+ S32V234_MSCR_PG1 = 97,
+ S32V234_MSCR_PG2 = 98,
+ S32V234_MSCR_PG3 = 99,
+ S32V234_MSCR_PG4 = 100,
+ S32V234_MSCR_PG5 = 101,
+ S32V234_MSCR_PG6 = 102,
+ S32V234_MSCR_PG7 = 103,
+ S32V234_MSCR_PG8 = 104,
+ S32V234_MSCR_PG9 = 105,
+ S32V234_MSCR_PG10 = 106,
+ S32V234_MSCR_PG11 = 107,
+ S32V234_MSCR_PG12 = 108,
+ S32V234_MSCR_PG13 = 109,
+ S32V234_MSCR_PG14 = 110,
+ S32V234_MSCR_PG15 = 111,
+ S32V234_MSCR_PH0 = 112,
+ S32V234_MSCR_PH1 = 113,
+ S32V234_MSCR_PH2 = 114,
+ S32V234_MSCR_PH3 = 115,
+ S32V234_MSCR_PH4 = 116,
+ S32V234_MSCR_PH5 = 117,
+ S32V234_MSCR_PH6 = 118,
+ S32V234_MSCR_PH7 = 119,
+ S32V234_MSCR_PH8 = 120,
+ S32V234_MSCR_PH9 = 121,
+ S32V234_MSCR_PH10 = 122,
+ S32V234_MSCR_PH11 = 123,
+ S32V234_MSCR_PH12 = 124,
+ S32V234_MSCR_PH13 = 125,
+ S32V234_MSCR_PH14 = 126,
+ S32V234_MSCR_PH15 = 127,
+ S32V234_MSCR_PJ0 = 128,
+ S32V234_MSCR_PJ1 = 129,
+ S32V234_MSCR_PJ2 = 130,
+ S32V234_MSCR_PJ3 = 131,
+ S32V234_MSCR_PJ4 = 132,
+ S32V234_MSCR_PJ5 = 133,
+ S32V234_MSCR_PJ6 = 134,
+ S32V234_MSCR_PJ7 = 135,
+ S32V234_MSCR_PJ8 = 136,
+ S32V234_MSCR_PJ9 = 137,
+ S32V234_MSCR_PJ10 = 138,
+ S32V234_MSCR_PJ11 = 139,
+ S32V234_MSCR_PJ12 = 140,
+ S32V234_MSCR_PJ13 = 141,
+ S32V234_MSCR_PJ14 = 142,
+ S32V234_MSCR_PJ15 = 143,
+ S32V234_MSCR_PK0 = 144,
+ S32V234_MSCR_PK1 = 145,
+ S32V234_MSCR_PK2 = 146,
+ S32V234_MSCR_PK3 = 147,
+ S32V234_MSCR_PK4 = 148,
+ S32V234_MSCR_PK5 = 149,
+ S32V234_MSCR_PK6 = 150,
+ S32V234_MSCR_PK7 = 151,
+ S32V234_MSCR_PK8 = 152,
+ S32V234_MSCR_PK9 = 153,
+ S32V234_MSCR_PK10 = 154,
+ S32V234_MSCR_PK11 = 155,
+ S32V234_MSCR_PK12 = 156,
+ S32V234_MSCR_PK13 = 157,
+ S32V234_MSCR_PK14 = 158,
+ S32V234_MSCR_PK15 = 159,
+ S32V234_MSCR_PL0 = 160,
+ S32V234_MSCR_PL1 = 161,
+ S32V234_MSCR_PL2 = 162,
+ S32V234_MSCR_PL3 = 163,
+ S32V234_MSCR_PL4 = 164,
+ S32V234_MSCR_PL5 = 165,
+ S32V234_MSCR_PL8 = 166,
+};
+
+/* Pad names for the pinmux subsystem */
+static const struct pinctrl_pin_desc s32v234_pinctrl_pads[] = {
+ S32V_PINCTRL_PIN(S32V234_MSCR_PA0),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PA1),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PA2),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PA3),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PA4),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PA5),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PA6),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PA7),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PA8),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PA9),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PA10),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PA11),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PA12),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PA13),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PA14),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PA15),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PB0),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PB1),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PB2),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PB3),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PB4),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PB5),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PB6),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PB7),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PB8),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PB9),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PB10),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PB11),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PB12),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PB13),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PB14),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PB15),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PC0),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PC1),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PC2),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PC3),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PC4),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PC5),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PC6),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PC7),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PC8),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PC9),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PC10),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PC11),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PC12),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PC13),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PC14),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PC15),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PD0),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PD1),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PD2),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PD3),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PD4),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PD5),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PD6),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PD7),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PD8),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PD9),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PD10),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PD11),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PD12),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PD13),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PD14),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PD15),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PE0),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PE1),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PE2),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PE3),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PE4),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PE5),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PE6),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PE7),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PE8),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PE9),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PE10),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PE11),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PE12),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PE13),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PE14),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PE15),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PF0),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PF1),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PF2),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PF3),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PF4),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PF5),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PF6),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PF7),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PF8),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PF9),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PF10),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PF11),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PF12),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PF13),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PF14),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PF15),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PG0),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PG1),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PG2),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PG3),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PG4),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PG5),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PG6),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PG7),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PG8),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PG9),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PG10),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PG11),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PG12),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PG13),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PG14),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PG15),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PH0),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PH1),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PH2),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PH3),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PH4),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PH5),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PH6),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PH7),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PH8),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PH9),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PH10),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PH11),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PH12),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PH13),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PH14),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PH15),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PJ0),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PJ1),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PJ2),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PJ3),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PJ4),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PJ5),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PJ6),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PJ7),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PJ8),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PJ9),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PJ10),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PJ11),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PJ12),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PJ13),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PJ14),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PJ15),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PK0),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PK1),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PK2),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PK3),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PK4),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PK5),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PK6),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PK7),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PK8),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PK9),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PK10),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PK11),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PK12),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PK13),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PK14),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PK15),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PL0),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PL1),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PL2),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PL3),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PL4),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PL5),
+ S32V_PINCTRL_PIN(S32V234_MSCR_PL8),
+};
+
+static struct s32v_pinctrl_soc_info s32v234_pinctrl_info = {
+ .pins = s32v234_pinctrl_pads,
+ .npins = ARRAY_SIZE(s32v234_pinctrl_pads),
+};
+
+static const struct of_device_id s32v234_pinctrl_of_match[] = {
+ { .compatible = "fsl,s32v234-siul2", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, s32v234_pinctrl_of_match);
+
+static int s32v234_pinctrl_probe(struct platform_device *pdev)
+{
+ return s32v_pinctrl_probe(pdev, &s32v234_pinctrl_info);
+}
+
+static struct platform_driver s32v234_pinctrl_driver = {
+ .driver = {
+ .name = "s32v234-siul2",
+ .owner = THIS_MODULE,
+ .of_match_table = s32v234_pinctrl_of_match,
+ },
+ .probe = s32v234_pinctrl_probe,
+ .remove = s32v_pinctrl_remove,
+};
+
+module_platform_driver(s32v234_pinctrl_driver);
+
+MODULE_DESCRIPTION("Freescale S32V234 pinctrl driver");
+MODULE_LICENSE("GPL v2");