Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/patches/2196235/?format=api
{ "id": 2196235, "url": "http://patchwork.ozlabs.org/api/patches/2196235/?format=api", "web_url": "http://patchwork.ozlabs.org/project/linux-aspeed/patch/20260213-pinctrl-single-bit-v1-2-c60f2fb80efb@aspeedtech.com/", "project": { "id": 57, "url": "http://patchwork.ozlabs.org/api/projects/57/?format=api", "name": "Linux ASPEED SoC development", "link_name": "linux-aspeed", "list_id": "linux-aspeed.lists.ozlabs.org", "list_email": "linux-aspeed@lists.ozlabs.org", "web_url": "", "scm_url": "", "webscm_url": "", "list_archive_url": "", "list_archive_url_format": "", "commit_url_format": "" }, "msgid": "<20260213-pinctrl-single-bit-v1-2-c60f2fb80efb@aspeedtech.com>", "list_archive_url": null, "date": "2026-02-13T08:17:43", "name": "[RFC,2/2] pinctrl: add syscon-backed packed-field pin controller driver", "commit_ref": null, "pull_url": null, "state": "new", "archived": false, "hash": "739ebe5c96473149030b7080d78a0cc4b80e0c98", "submitter": { "id": 80235, "url": "http://patchwork.ozlabs.org/api/people/80235/?format=api", "name": "Billy Tsai", "email": "billy_tsai@aspeedtech.com" }, "delegate": null, "mbox": "http://patchwork.ozlabs.org/project/linux-aspeed/patch/20260213-pinctrl-single-bit-v1-2-c60f2fb80efb@aspeedtech.com/mbox/", "series": [ { "id": 492066, "url": "http://patchwork.ozlabs.org/api/series/492066/?format=api", "web_url": "http://patchwork.ozlabs.org/project/linux-aspeed/list/?series=492066", "date": "2026-02-13T08:17:43", "name": "pinctrl: add syscon-backed packed-field pinctrl driver and DT bindings", "version": 1, "mbox": "http://patchwork.ozlabs.org/series/492066/mbox/" } ], "comments": "http://patchwork.ozlabs.org/api/patches/2196235/comments/", "check": "pending", "checks": "http://patchwork.ozlabs.org/api/patches/2196235/checks/", "tags": {}, "related": [], "headers": { "Return-Path": "\n <linux-aspeed+bounces-3516-incoming=patchwork.ozlabs.org@lists.ozlabs.org>", "X-Original-To": [ "incoming@patchwork.ozlabs.org", "linux-aspeed@lists.ozlabs.org" ], "Delivered-To": "patchwork-incoming@legolas.ozlabs.org", "Authentication-Results": [ "legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=lists.ozlabs.org\n (client-ip=112.213.38.117; helo=lists.ozlabs.org;\n envelope-from=linux-aspeed+bounces-3516-incoming=patchwork.ozlabs.org@lists.ozlabs.org;\n receiver=patchwork.ozlabs.org)", "lists.ozlabs.org;\n arc=none smtp.remote-ip=211.20.114.72", "lists.ozlabs.org;\n dmarc=pass (p=quarantine dis=none) header.from=aspeedtech.com", "lists.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=aspeedtech.com\n (client-ip=211.20.114.72; helo=twmbx01.aspeed.com;\n envelope-from=billy_tsai@aspeedtech.com; receiver=lists.ozlabs.org)" ], "Received": [ "from lists.ozlabs.org (lists.ozlabs.org [112.213.38.117])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519 server-signature ECDSA (secp384r1 raw public key)\n server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4fC4mr47mHz1xxM\n\tfor <incoming@patchwork.ozlabs.org>; Fri, 13 Feb 2026 19:18:19 +1100 (AEDT)", "from boromir.ozlabs.org (localhost [127.0.0.1])\n\tby lists.ozlabs.org (Postfix) with ESMTP id 4fC4mp3Ts7z2ydq;\n\tFri, 13 Feb 2026 19:18:18 +1100 (AEDT)", "from TWMBX01.aspeed.com (mail.aspeedtech.com [211.20.114.72])\n\t(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))\n\t(No client certificate requested)\n\tby lists.ozlabs.org (Postfix) with ESMTPS id 4fC4mm4lRGz2xdY\n\tfor <linux-aspeed@lists.ozlabs.org>; Fri, 13 Feb 2026 19:18:16 +1100 (AEDT)", "from TWMBX01.aspeed.com (192.168.0.62) by TWMBX01.aspeed.com\n (192.168.0.62) with Microsoft SMTP Server (version=TLS1_2,\n cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1748.10; Fri, 13 Feb\n 2026 16:17:55 +0800", "from [127.0.1.1] (192.168.10.13) by TWMBX01.aspeed.com\n (192.168.0.62) with Microsoft SMTP Server id 15.2.1748.10 via Frontend\n Transport; Fri, 13 Feb 2026 16:17:55 +0800" ], "ARC-Seal": "i=1; a=rsa-sha256; d=lists.ozlabs.org; s=201707; t=1770970698;\n\tcv=none;\n b=an5OWLloYxViqC3gtb2rEhikmm7nARUJ6iOvUXRSyQODARVN5WNm9n3PlFMKCvqpt0GmrXSJEm6MUYayT5V5XnhQfnqaHGIU1PLqgHmOtM1AlXrvDnEpv2rQzYG4e3f6Cr4DqUVQqfP3EIiFVoWiDDYQjSiepe6HGAUuGCqwRbhXA8CcRvcLkQLPPcBiAsN8YeWqmuPyBz6xQPeFQ6lhEj11T/2nhvr6x6a++7pjfQrqGVD+F7kdDRo1lYlD/jRF9+XCQNVWWb/+z4SVsij7oORS7sFilq8WL29Q2X8JyHKFV5ZVDjlGIO6P1Xe1+7zSRquCe3M7V4PXwK8XX8Rnlg==", "ARC-Message-Signature": "i=1; a=rsa-sha256; d=lists.ozlabs.org; s=201707;\n\tt=1770970698; c=relaxed/relaxed;\n\tbh=4v8hvAA8vuWAeKtYgVy6iuLiViA4Q2yNvVNSL7FdBTc=;\n\th=From:Date:Subject:MIME-Version:Content-Type:Message-ID:References:\n\t In-Reply-To:To:CC;\n b=lBo2E+SUGeSTgjMx7NwK1lDLlEw+B4ELMXbu9YqEYF9DQ9L1WgUEZo/DEZfqxWJflMdY7WT1ERDF0vcrvoYrN2MC6WBhgNhg7ewJ2LIJVvw8jm49Rpa1pPPfY96ACiF3erjVAnk7rObASOSTrxBvhOQupMdMYpv117QbnXfcFNIfsSk+VCyQ3+tg5R3dv7iQCyAhz0cUAXVCiCLEqIFbrUv/lfpkt7D1dMP7h56dYRvFiEzN+/OfEsJcDgyCa1b/SV/y/9KybM9AWS6nZo+W2x/UXLvYeRWirGHxoMmguc1VjBqB5cY/rccej1an5vI8QoIlEisAhUVGfs5zK6c+ug==", "ARC-Authentication-Results": "i=1; lists.ozlabs.org;\n dmarc=pass (p=quarantine dis=none) header.from=aspeedtech.com;\n spf=pass (client-ip=211.20.114.72; helo=twmbx01.aspeed.com;\n envelope-from=billy_tsai@aspeedtech.com;\n receiver=lists.ozlabs.org) smtp.mailfrom=aspeedtech.com", "From": "Billy Tsai <billy_tsai@aspeedtech.com>", "Date": "Fri, 13 Feb 2026 16:17:43 +0800", "Subject": "[PATCH RFC 2/2] pinctrl: add syscon-backed packed-field pin\n controller driver", "X-Mailing-List": "linux-aspeed@lists.ozlabs.org", "List-Id": "<linux-aspeed.lists.ozlabs.org>", "List-Help": "<mailto:linux-aspeed+help@lists.ozlabs.org>", "List-Owner": "<mailto:linux-aspeed+owner@lists.ozlabs.org>", "List-Post": "<mailto:linux-aspeed@lists.ozlabs.org>", "List-Archive": "<https://lore.kernel.org/linux-aspeed/>,\n <https://lists.ozlabs.org/pipermail/linux-aspeed/>", "List-Subscribe": "<mailto:linux-aspeed+subscribe@lists.ozlabs.org>,\n <mailto:linux-aspeed+subscribe-digest@lists.ozlabs.org>,\n <mailto:linux-aspeed+subscribe-nomail@lists.ozlabs.org>", "List-Unsubscribe": "<mailto:linux-aspeed+unsubscribe@lists.ozlabs.org>", "Precedence": "list", "MIME-Version": "1.0", "Content-Type": "text/plain; charset=\"utf-8\"", "Content-Transfer-Encoding": "7bit", "Message-ID": "<20260213-pinctrl-single-bit-v1-2-c60f2fb80efb@aspeedtech.com>", "References": "<20260213-pinctrl-single-bit-v1-0-c60f2fb80efb@aspeedtech.com>", "In-Reply-To": "<20260213-pinctrl-single-bit-v1-0-c60f2fb80efb@aspeedtech.com>", "To": "Linus Walleij <linusw@kernel.org>, Tony Lindgren <tony@atomide.com>, \"Rob\n Herring\" <robh@kernel.org>, Krzysztof Kozlowski <krzk+dt@kernel.org>, \"Conor\n Dooley\" <conor+dt@kernel.org>, Joel Stanley <joel@jms.id.au>, Andrew Jeffery\n\t<andrew@codeconstruct.com.au>, Bartosz Golaszewski <brgl@kernel.org>", "CC": "<patrickw3@meta.com>, <linux-gpio@vger.kernel.org>,\n\t<devicetree@vger.kernel.org>, <linux-kernel@vger.kernel.org>,\n\t<linux-arm-kernel@lists.infradead.org>, <linux-aspeed@lists.ozlabs.org>,\n\t<BMC-SW@aspeedtech.com>, Billy Tsai <billy_tsai@aspeedtech.com>", "X-Mailer": "b4 0.14.3", "X-Developer-Signature": "v=1; a=ed25519-sha256; t=1770970675; l=35175;\n i=billy_tsai@aspeedtech.com; s=20251118; h=from:subject:message-id;\n bh=x34CwRDps5hRNuSH3yeJ1AdDZ44/2EIE/QI4SaohkYM=;\n b=XErTtKtgMuGBkdkoOuIhvm2ITwM9IjXPMURcEML7XCdd6j7W6UpD64NYbHPuIrdgX+ew6Vl4g\n KMabIRqUbjlBJbSU/ZOLbxS91jeWtBhqxQLugx3Fc0/CcBYpo+dpRnP", "X-Developer-Key": "i=billy_tsai@aspeedtech.com; a=ed25519;\n pk=/A8qvgZ6CPfnwKgT6/+k+nvXOkN477MshEGJvVdzeeQ=", "X-Spam-Status": "No, score=0.0 required=5.0 tests=SPF_HELO_FAIL,SPF_PASS\n\tautolearn=disabled version=4.0.1", "X-Spam-Checker-Version": "SpamAssassin 4.0.1 (2024-03-25) on lists.ozlabs.org" }, "content": "Introduce a new pinctrl driver for controllers where pinmux and/or pin\nconfiguration fields are represented as fixed-width packed fields within\nshared registers.\n\nThe driver is designed to be used as a child of a syscon node and\nobtains a regmap from the parent, avoiding direct MMIO ownership and\nallowing shared access to SCU-style register blocks.\n\nThis driver is derived from pinctrl-single but reworked as a standalone\nimplementation focused on packed-field controllers.\n\nSigned-off-by: Billy Tsai <billy_tsai@aspeedtech.com>\n---\n drivers/pinctrl/Kconfig | 13 +\n drivers/pinctrl/Makefile | 1 +\n drivers/pinctrl/pinctrl-packed.c | 1168 ++++++++++++++++++++++++++++++++++++++\n 3 files changed, 1182 insertions(+)", "diff": "diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig\nindex 7b9f792acb0e..d1fd6a2e767c 100644\n--- a/drivers/pinctrl/Kconfig\n+++ b/drivers/pinctrl/Kconfig\n@@ -443,6 +443,19 @@ config PINCTRL_OCELOT\n \n \t If conpiled as a module, the module name will be pinctrl-ocelot.\n \n+config PINCTRL_PACKED\n+\ttristate \"Packed-field type device tree based pinctrl driver\"\n+\tdepends on OF\n+\tdepends on HAS_IOMEM\n+\tselect GENERIC_PINCTRL_GROUPS\n+\tselect GENERIC_PINMUX_FUNCTIONS\n+\tselect GENERIC_PINCONF\n+\thelp\n+\t This selects the device tree based generic pinctrl driver with\n+\t fixed-width packed-field register layout.\n+\t The driver is derived from pinctrl-single but reworked as a standalone\n+\t implementation focused on packed-field controllers.\n+\n config PINCTRL_PALMAS\n \ttristate \"Pinctrl driver for the PALMAS Series MFD devices\"\n \tdepends on OF && MFD_PALMAS\ndiff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile\nindex be5200c23e60..fd71da9901b9 100644\n--- a/drivers/pinctrl/Makefile\n+++ b/drivers/pinctrl/Makefile\n@@ -45,6 +45,7 @@ obj-$(CONFIG_PINCTRL_MCP23S08)\t+= pinctrl-mcp23s08.o\n obj-$(CONFIG_PINCTRL_MICROCHIP_SGPIO)\t+= pinctrl-microchip-sgpio.o\n obj-$(CONFIG_PINCTRL_MLXBF3)\t+= pinctrl-mlxbf3.o\n obj-$(CONFIG_PINCTRL_OCELOT)\t+= pinctrl-ocelot.o\n+obj-$(CONFIG_PINCTRL_PACKED)\t+= pinctrl-packed.o\n obj-$(CONFIG_PINCTRL_PALMAS)\t+= pinctrl-palmas.o\n obj-$(CONFIG_PINCTRL_PEF2256)\t+= pinctrl-pef2256.o\n obj-$(CONFIG_PINCTRL_PIC32)\t+= pinctrl-pic32.o\ndiff --git a/drivers/pinctrl/pinctrl-packed.c b/drivers/pinctrl/pinctrl-packed.c\nnew file mode 100644\nindex 000000000000..cfb43894cfaa\n--- /dev/null\n+++ b/drivers/pinctrl/pinctrl-packed.c\n@@ -0,0 +1,1168 @@\n+// SPDX-License-Identifier: GPL-2.0-or-later\n+/*\n+ * Generic device tree based pinctrl driver for fixed-width\n+ * packed-field pin controllers\n+ *\n+ * Copyright (C) 2026 ASPEED Technology Inc.\n+ *\n+ * This driver targets controllers where pinmux and/or pinconf fields\n+ * are arranged as fixed-width fields packed sequentially within\n+ * shared registers.\n+ *\n+ * Derived from drivers/pinctrl/pinctrl-single.c\n+ *\n+ * Original author:\n+ * Tony Lindgren <tony@atomide.com>\n+ *\n+ * Reworked for packed-field / syscon-based pin controllers:\n+ * Billy Tsai <billy_tsai@aspeedtech.com>\n+ */\n+\n+#include <linux/err.h>\n+#include <linux/init.h>\n+#include <linux/io.h>\n+#include <linux/list.h>\n+#include <linux/mfd/syscon.h>\n+#include <linux/module.h>\n+#include <linux/of.h>\n+#include <linux/of_address.h>\n+#include <linux/platform_device.h>\n+#include <linux/regmap.h>\n+#include <linux/seq_file.h>\n+#include <linux/slab.h>\n+\n+#include <linux/pinctrl/pinconf-generic.h>\n+#include <linux/pinctrl/pinconf.h>\n+#include <linux/pinctrl/pinctrl.h>\n+#include <linux/pinctrl/pinmux.h>\n+\n+#include \"core.h\"\n+#include \"devicetree.h\"\n+#include \"pinconf.h\"\n+#include \"pinmux.h\"\n+\n+#define DRIVER_NAME\t\t\t\"pinctrl-packed\"\n+\n+/**\n+ * struct pcp_func_vals - mux function register offset and value pair\n+ * @reg:\tregister offset\n+ * @val:\tregister value\n+ * @mask:\tmask\n+ */\n+struct pcp_func_vals {\n+\tunsigned int reg;\n+\tunsigned int val;\n+\tunsigned int mask;\n+};\n+\n+/**\n+ * struct pcp_conf_vals - pinconf parameter, pinconf register offset\n+ * and value, enable, disable, mask\n+ * @param:\tconfig parameter\n+ * @val:\tuser input bits in the pinconf register\n+ * @enable:\tenable bits in the pinconf register\n+ * @disable:\tdisable bits in the pinconf register\n+ * @mask:\tmask bits in the register value\n+ */\n+struct pcp_conf_vals {\n+\tenum pin_config_param param;\n+\tunsigned int val;\n+\tunsigned int enable;\n+\tunsigned int disable;\n+\tunsigned int mask;\n+};\n+\n+/**\n+ * struct pcp_conf_type - pinconf property name, pinconf param pair\n+ * @name:\tproperty name in DTS file\n+ * @param:\tconfig parameter\n+ */\n+struct pcp_conf_type {\n+\tconst char *name;\n+\tenum pin_config_param param;\n+};\n+\n+/**\n+ * struct pcp_function - pinctrl function\n+ * @name:\tpinctrl function name\n+ * @vals:\tregister and vals array\n+ * @nvals:\tnumber of entries in vals array\n+ * @conf:\tarray of pin configurations\n+ * @nconfs:\tnumber of pin configurations available\n+ * @node:\tlist node\n+ */\n+struct pcp_function {\n+\tconst char *name;\n+\tstruct pcp_func_vals *vals;\n+\tunsigned int nvals;\n+\tstruct pcp_conf_vals *conf;\n+\tint nconfs;\n+\tstruct list_head node;\n+};\n+\n+/**\n+ * struct pcp_gpiofunc_range - pin ranges with same mux value of gpio function\n+ * @offset:\toffset base of pins\n+ * @npins:\tnumber pins with the same mux value of gpio function\n+ * @gpiofunc:\tmux value of gpio function\n+ * @node:\tlist node\n+ */\n+struct pcp_gpiofunc_range {\n+\tunsigned int offset;\n+\tunsigned int npins;\n+\tunsigned int gpiofunc;\n+\tstruct list_head node;\n+};\n+\n+/**\n+ * struct pcp_data - wrapper for data needed by pinctrl framework\n+ * @pa:\t\tpindesc array\n+ * @cur:\tindex to current element\n+ *\n+ * REVISIT: We should be able to drop this eventually by adding\n+ * support for registering pins individually in the pinctrl\n+ * framework for those drivers that don't need a static array.\n+ */\n+struct pcp_data {\n+\tstruct pinctrl_pin_desc *pa;\n+\tint cur;\n+};\n+\n+/**\n+ * struct pcp_soc_data - SoC specific settings\n+ * @flags:\tinitial SoC specific PCP_FEAT_xxx values\n+ */\n+struct pcp_soc_data {\n+\tunsigned int flags;\n+};\n+\n+/**\n+ * struct pcp_device - pinctrl device instance\n+ * @regmap:\tregmap for the controller\n+ * @base_offset: offset from the regmap base\n+ * @size:\tsize of the area\n+ * @dev:\tdevice entry\n+ * @np:\t\tdevice tree node\n+ * @pctl:\tpin controller device\n+ * @flags:\tmask of PCP_FEAT_xxx values\n+ * @mutex:\tmutex protecting the lists\n+ * @width:\tbits per mux register\n+ * @fmask:\tfunction register mask\n+ * @bits_per_pin: number of bits per pin\n+ * @pins:\tphysical pins on the SoC\n+ * @gpiofuncs:\tlist of gpio functions\n+ * @desc:\tpin controller descriptor\n+ */\n+struct pcp_device {\n+\tstruct regmap *regmap;\n+\tunsigned int base_offset;\n+\tunsigned int size;\n+\tstruct device *dev;\n+\tstruct device_node *np;\n+\tstruct pinctrl_dev *pctl;\n+\tunsigned int flags;\n+#define PCP_FEAT_PINCONF\t(1 << 0)\n+\tstruct mutex mutex;\n+\tunsigned int width;\n+\tunsigned int fmask;\n+\tunsigned int bits_per_pin;\n+\tstruct pcp_data pins;\n+\tstruct list_head gpiofuncs;\n+\tstruct pinctrl_desc desc;\n+};\n+\n+#define PCP_HAS_PINCONF\t\t(pcp->flags & PCP_FEAT_PINCONF)\n+\n+static int pcp_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin,\n+\t\t\t unsigned long *config);\n+static int pcp_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin,\n+\t\t\t unsigned long *configs, unsigned int num_configs);\n+\n+static enum pin_config_param pcp_bias[] = {\n+\tPIN_CONFIG_BIAS_PULL_DOWN,\n+\tPIN_CONFIG_BIAS_PULL_UP,\n+};\n+\n+static inline void pcp_writel(unsigned int val, struct pcp_device *pcp,\n+\t\t\t unsigned int reg)\n+{\n+\tregmap_write(pcp->regmap, reg, val);\n+}\n+\n+static inline unsigned int pcp_readl(struct pcp_device *pcp, unsigned int reg)\n+{\n+\tunsigned int val;\n+\n+\tregmap_read(pcp->regmap, reg, &val);\n+\treturn val;\n+}\n+\n+static unsigned int pcp_pin_reg_offset_get(struct pcp_device *pcp,\n+\t\t\t\t\t unsigned int pin)\n+{\n+\tunsigned int mux_bytes = pcp->width / BITS_PER_BYTE;\n+\tunsigned int pin_offset_bytes;\n+\n+\tpin_offset_bytes = (pcp->bits_per_pin * pin) / BITS_PER_BYTE;\n+\treturn (pin_offset_bytes / mux_bytes) * mux_bytes;\n+}\n+\n+static unsigned int pcp_pin_shift_reg_get(struct pcp_device *pcp,\n+\t\t\t\t\t unsigned int pin)\n+{\n+\treturn (pin % (pcp->width / pcp->bits_per_pin)) * pcp->bits_per_pin;\n+}\n+\n+static void pcp_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s,\n+\t\t\t unsigned int pin)\n+{\n+\tunsigned int val, offset;\n+\tstruct pcp_device *pcp;\n+\n+\tpcp = pinctrl_dev_get_drvdata(pctldev);\n+\n+\toffset = pcp_pin_reg_offset_get(pcp, pin);\n+\tval = pcp_readl(pcp, pcp->base_offset + offset);\n+\n+\tval &= pcp->fmask << pcp_pin_shift_reg_get(pcp, pin);\n+\n+\tseq_printf(s, \"%08x %08x %s \", pcp->base_offset + offset, val, DRIVER_NAME);\n+}\n+\n+static void pcp_dt_free_map(struct pinctrl_dev *pctldev, struct pinctrl_map *map,\n+\t\t\t unsigned int num_maps)\n+{\n+\tstruct pcp_device *pcp;\n+\n+\tpcp = pinctrl_dev_get_drvdata(pctldev);\n+\tdevm_kfree(pcp->dev, map);\n+}\n+\n+static int pcp_dt_node_to_map(struct pinctrl_dev *pctldev,\n+\t\t\t struct device_node *np_config,\n+\t\t\t struct pinctrl_map **map, unsigned int *num_maps);\n+\n+static const struct pinctrl_ops pcp_pinctrl_ops = {\n+\t.get_groups_count = pinctrl_generic_get_group_count,\n+\t.get_group_name = pinctrl_generic_get_group_name,\n+\t.get_group_pins = pinctrl_generic_get_group_pins,\n+\t.pin_dbg_show = pcp_pin_dbg_show,\n+\t.dt_node_to_map = pcp_dt_node_to_map,\n+\t.dt_free_map = pcp_dt_free_map,\n+};\n+\n+static int pcp_get_function(struct pinctrl_dev *pctldev, unsigned int pin,\n+\t\t\t struct pcp_function **func)\n+{\n+\tstruct pcp_device *pcp = pinctrl_dev_get_drvdata(pctldev);\n+\tstruct pin_desc *pdesc = pin_desc_get(pctldev, pin);\n+\tconst struct pinctrl_setting_mux *setting;\n+\tconst struct function_desc *function;\n+\tunsigned int fselector;\n+\n+\t/* If pin is not described in DTS & enabled, mux_setting is NULL. */\n+\tsetting = pdesc->mux_setting;\n+\tif (!setting)\n+\t\treturn -EOPNOTSUPP;\n+\tfselector = setting->func;\n+\tfunction = pinmux_generic_get_function(pctldev, fselector);\n+\tif (!function)\n+\t\treturn -EINVAL;\n+\t*func = function->data;\n+\tif (!(*func)) {\n+\t\tdev_err(pcp->dev, \"%s could not find function%i\\n\",\n+\t\t\t__func__, fselector);\n+\t\treturn -EOPNOTSUPP;\n+\t}\n+\treturn 0;\n+}\n+\n+static int pcp_set_mux(struct pinctrl_dev *pctldev, unsigned int fselector,\n+\t\t unsigned int group)\n+{\n+\tconst struct function_desc *function;\n+\tstruct pcp_function *func;\n+\tstruct pcp_device *pcp;\n+\tint i;\n+\n+\tpcp = pinctrl_dev_get_drvdata(pctldev);\n+\t/* If function mask is null, needn't enable it. */\n+\tif (!pcp->fmask)\n+\t\treturn 0;\n+\tfunction = pinmux_generic_get_function(pctldev, fselector);\n+\tif (!function)\n+\t\treturn -EINVAL;\n+\tfunc = function->data;\n+\tif (!func)\n+\t\treturn -EINVAL;\n+\n+\tdev_dbg(pcp->dev, \"enabling %s function%i\\n\",\n+\t\tfunc->name, fselector);\n+\n+\tfor (i = 0; i < func->nvals; i++) {\n+\t\tstruct pcp_func_vals *vals;\n+\n+\t\tvals = &func->vals[i];\n+\t\tregmap_update_bits(pcp->regmap, vals->reg, vals->mask, vals->val);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int pcp_request_gpio(struct pinctrl_dev *pctldev,\n+\t\t\t struct pinctrl_gpio_range *range, unsigned int pin)\n+{\n+\tstruct pcp_device *pcp = pinctrl_dev_get_drvdata(pctldev);\n+\tstruct pcp_gpiofunc_range *frange = NULL;\n+\tstruct list_head *pos, *tmp;\n+\tint pin_shift;\n+\n+\t/* If function mask is null, return directly. */\n+\tif (!pcp->fmask)\n+\t\treturn -EOPNOTSUPP;\n+\n+\tlist_for_each_safe(pos, tmp, &pcp->gpiofuncs) {\n+\t\tu32 offset;\n+\n+\t\tfrange = list_entry(pos, struct pcp_gpiofunc_range, node);\n+\t\tif (pin >= frange->offset + frange->npins\n+\t\t\t|| pin < frange->offset)\n+\t\t\tcontinue;\n+\n+\t\toffset = pcp_pin_reg_offset_get(pcp, pin);\n+\n+\t\tpin_shift = pcp_pin_shift_reg_get(pcp, pin);\n+\n+\t\tregmap_update_bits(pcp->regmap, pcp->base_offset + offset,\n+\t\t\t\t\tpcp->fmask << pin_shift,\n+\t\t\t\t\tfrange->gpiofunc << pin_shift);\n+\t\tbreak;\n+\t}\n+\treturn 0;\n+}\n+\n+static const struct pinmux_ops pcp_pinmux_ops = {\n+\t.get_functions_count = pinmux_generic_get_function_count,\n+\t.get_function_name = pinmux_generic_get_function_name,\n+\t.get_function_groups = pinmux_generic_get_function_groups,\n+\t.set_mux = pcp_set_mux,\n+\t.gpio_request_enable = pcp_request_gpio,\n+};\n+\n+/* Clear BIAS value */\n+static void pcp_pinconf_clear_bias(struct pinctrl_dev *pctldev, unsigned int pin)\n+{\n+\tunsigned long config;\n+\tint i;\n+\n+\tfor (i = 0; i < ARRAY_SIZE(pcp_bias); i++) {\n+\t\tconfig = pinconf_to_config_packed(pcp_bias[i], 0);\n+\t\tpcp_pinconf_set(pctldev, pin, &config, 1);\n+\t}\n+}\n+\n+/*\n+ * Check whether PIN_CONFIG_BIAS_DISABLE is valid.\n+ * It's depend on that PULL_DOWN & PULL_UP configs are all invalid.\n+ */\n+static bool pcp_pinconf_bias_disable(struct pinctrl_dev *pctldev, unsigned int pin)\n+{\n+\tunsigned long config;\n+\tint i;\n+\n+\tfor (i = 0; i < ARRAY_SIZE(pcp_bias); i++) {\n+\t\tconfig = pinconf_to_config_packed(pcp_bias[i], 0);\n+\t\tif (!pcp_pinconf_get(pctldev, pin, &config))\n+\t\t\treturn false;\n+\t}\n+\treturn true;\n+}\n+\n+static int pcp_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin,\n+\t\t\t unsigned long *config)\n+{\n+\tstruct pcp_device *pcp = pinctrl_dev_get_drvdata(pctldev);\n+\tunsigned int offset, data, i, j;\n+\tenum pin_config_param param;\n+\tstruct pcp_function *func;\n+\tint ret;\n+\n+\toffset = 0;\n+\tdata = 0;\n+\tret = pcp_get_function(pctldev, pin, &func);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tfor (i = 0; i < func->nconfs; i++) {\n+\t\tparam = pinconf_to_config_param(*config);\n+\t\tif (param == PIN_CONFIG_BIAS_DISABLE) {\n+\t\t\tif (pcp_pinconf_bias_disable(pctldev, pin)) {\n+\t\t\t\t*config = 0;\n+\t\t\t\treturn 0;\n+\t\t\t} else {\n+\t\t\t\treturn -EOPNOTSUPP;\n+\t\t\t}\n+\t\t} else if (param != func->conf[i].param) {\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\toffset = pcp_pin_reg_offset_get(pcp, pin);\n+\t\tdata = pcp_readl(pcp, pcp->base_offset + offset) & func->conf[i].mask;\n+\t\tswitch (func->conf[i].param) {\n+\t\t/* 4 parameters */\n+\t\tcase PIN_CONFIG_BIAS_PULL_DOWN:\n+\t\tcase PIN_CONFIG_BIAS_PULL_UP:\n+\t\tcase PIN_CONFIG_INPUT_SCHMITT_ENABLE:\n+\t\t\tif ((data != func->conf[i].enable) ||\n+\t\t\t (data == func->conf[i].disable))\n+\t\t\t\treturn -EOPNOTSUPP;\n+\t\t\t*config = 0;\n+\t\t\tbreak;\n+\t\t/* 2 parameters */\n+\t\tcase PIN_CONFIG_INPUT_SCHMITT:\n+\t\t\tfor (j = 0; j < func->nconfs; j++) {\n+\t\t\t\tswitch (func->conf[j].param) {\n+\t\t\t\tcase PIN_CONFIG_INPUT_SCHMITT_ENABLE:\n+\t\t\t\t\tif (data != func->conf[j].enable)\n+\t\t\t\t\t\treturn -EOPNOTSUPP;\n+\t\t\t\t\tbreak;\n+\t\t\t\tdefault:\n+\t\t\t\t\tbreak;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t\t*config = data;\n+\t\t\tbreak;\n+\t\tcase PIN_CONFIG_DRIVE_STRENGTH:\n+\t\tcase PIN_CONFIG_SLEW_RATE:\n+\t\tcase PIN_CONFIG_MODE_LOW_POWER:\n+\t\tcase PIN_CONFIG_INPUT_ENABLE:\n+\t\tdefault:\n+\t\t\t*config = data;\n+\t\t\tbreak;\n+\t\t}\n+\t\treturn 0;\n+\t}\n+\treturn -EOPNOTSUPP;\n+}\n+\n+static int pcp_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin,\n+\t\t\t unsigned long *configs, unsigned int num_configs)\n+{\n+\tstruct pcp_device *pcp = pinctrl_dev_get_drvdata(pctldev);\n+\tunsigned int offset, shift, i, data;\n+\tenum pin_config_param param;\n+\tstruct pcp_function *func;\n+\tint j, ret;\n+\tu32 arg;\n+\n+\toffset = 0;\n+\tshift = 0;\n+\tret = pcp_get_function(pctldev, pin, &func);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tfor (j = 0; j < num_configs; j++) {\n+\t\tparam = pinconf_to_config_param(configs[j]);\n+\n+\t\t/* BIAS_DISABLE has no entry in the func->conf table */\n+\t\tif (param == PIN_CONFIG_BIAS_DISABLE) {\n+\t\t\t/* This just disables all bias entries */\n+\t\t\tpcp_pinconf_clear_bias(pctldev, pin);\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tfor (i = 0; i < func->nconfs; i++) {\n+\t\t\tif (param != func->conf[i].param)\n+\t\t\t\tcontinue;\n+\n+\t\t\t/* Use the same offset mapping as pinmux (handles bit-per-mux) */\n+\t\t\toffset = pcp_pin_reg_offset_get(pcp, pin);\n+\t\t\tdata = pcp_readl(pcp, pcp->base_offset + offset);\n+\t\t\targ = pinconf_to_config_argument(configs[j]);\n+\t\t\tswitch (param) {\n+\t\t\t/* 2 parameters */\n+\t\t\tcase PIN_CONFIG_INPUT_SCHMITT:\n+\t\t\tcase PIN_CONFIG_DRIVE_STRENGTH:\n+\t\t\tcase PIN_CONFIG_SLEW_RATE:\n+\t\t\tcase PIN_CONFIG_MODE_LOW_POWER:\n+\t\t\tcase PIN_CONFIG_INPUT_ENABLE:\n+\t\t\t\tshift = ffs(func->conf[i].mask) - 1;\n+\t\t\t\tdata &= ~func->conf[i].mask;\n+\t\t\t\tdata |= (arg << shift) & func->conf[i].mask;\n+\t\t\t\tbreak;\n+\t\t\t/* 4 parameters */\n+\t\t\tcase PIN_CONFIG_BIAS_PULL_DOWN:\n+\t\t\tcase PIN_CONFIG_BIAS_PULL_UP:\n+\t\t\t\tif (arg) {\n+\t\t\t\t\tpcp_pinconf_clear_bias(pctldev, pin);\n+\t\t\t\t\tdata = pcp_readl(pcp, pcp->base_offset + offset);\n+\t\t\t\t}\n+\t\t\t\tfallthrough;\n+\t\t\tcase PIN_CONFIG_INPUT_SCHMITT_ENABLE:\n+\t\t\t\tdata &= ~func->conf[i].mask;\n+\t\t\t\tif (arg)\n+\t\t\t\t\tdata |= func->conf[i].enable;\n+\t\t\t\telse\n+\t\t\t\t\tdata |= func->conf[i].disable;\n+\t\t\t\tbreak;\n+\t\t\tdefault:\n+\t\t\t\treturn -EOPNOTSUPP;\n+\t\t\t}\n+\t\t\tpcp_writel(data, pcp, pcp->base_offset + offset);\n+\n+\t\t\tbreak;\n+\t\t}\n+\t\tif (i >= func->nconfs)\n+\t\t\treturn -EOPNOTSUPP;\n+\t} /* for each config */\n+\n+\treturn 0;\n+}\n+\n+static int pcp_pinconf_group_get(struct pinctrl_dev *pctldev,\n+\t\t\t\t unsigned int group, unsigned long *config)\n+{\n+\tconst unsigned int *pins;\n+\tunsigned int npins, old;\n+\tint i, ret;\n+\n+\told = 0;\n+\tret = pinctrl_generic_get_group_pins(pctldev, group, &pins, &npins);\n+\tif (ret)\n+\t\treturn ret;\n+\tfor (i = 0; i < npins; i++) {\n+\t\tif (pcp_pinconf_get(pctldev, pins[i], config))\n+\t\t\treturn -EOPNOTSUPP;\n+\t\t/* configs do not match between two pins */\n+\t\tif (i && (old != *config))\n+\t\t\treturn -EOPNOTSUPP;\n+\t\told = *config;\n+\t}\n+\treturn 0;\n+}\n+\n+static int pcp_pinconf_group_set(struct pinctrl_dev *pctldev, unsigned int group,\n+\t\t\t\t unsigned long *configs, unsigned int num_configs)\n+{\n+\tconst unsigned int *pins;\n+\tunsigned int npins;\n+\tint i, ret;\n+\n+\tret = pinctrl_generic_get_group_pins(pctldev, group, &pins, &npins);\n+\tif (ret)\n+\t\treturn ret;\n+\tfor (i = 0; i < npins; i++) {\n+\t\tif (pcp_pinconf_set(pctldev, pins[i], configs, num_configs))\n+\t\t\treturn -EOPNOTSUPP;\n+\t}\n+\treturn 0;\n+}\n+\n+static void pcp_pinconf_config_dbg_show(struct pinctrl_dev *pctldev,\n+\t\t\t\t\tstruct seq_file *s, unsigned long config)\n+{\n+\tpinconf_generic_dump_config(pctldev, s, config);\n+}\n+\n+static const struct pinconf_ops pcp_pinconf_ops = {\n+\t.pin_config_get = pcp_pinconf_get,\n+\t.pin_config_set = pcp_pinconf_set,\n+\t.pin_config_group_get = pcp_pinconf_group_get,\n+\t.pin_config_group_set = pcp_pinconf_group_set,\n+\t.pin_config_config_dbg_show = pcp_pinconf_config_dbg_show,\n+\t.is_generic = true,\n+};\n+\n+/**\n+ * pcp_add_pin() - add a pin to the static per controller pin array\n+ * @pcp: pcp driver instance\n+ * @offset: register offset from base\n+ */\n+static int pcp_add_pin(struct pcp_device *pcp, unsigned int offset)\n+{\n+\tstruct pinctrl_pin_desc *pin;\n+\tint i;\n+\n+\ti = pcp->pins.cur;\n+\tif (i >= pcp->desc.npins) {\n+\t\tdev_err(pcp->dev, \"too many pins, max %i\\n\",\n+\t\t\tpcp->desc.npins);\n+\t\treturn -ENOMEM;\n+\t}\n+\n+\tpin = &pcp->pins.pa[i];\n+\tpin->number = i;\n+\tpcp->pins.cur++;\n+\n+\treturn i;\n+}\n+\n+/**\n+ * pcp_allocate_pin_table() - adds all the pins for the pinctrl driver\n+ * @pcp: pcp driver instance\n+ *\n+ * If your hardware needs holes in the address space, then just set\n+ * up multiple driver instances.\n+ */\n+static int pcp_allocate_pin_table(struct pcp_device *pcp)\n+{\n+\tint nr_pins, i;\n+\n+\tif (!pcp->fmask) {\n+\t\tdev_err(pcp->dev, \"invalid function mask\\n\");\n+\t\treturn -EINVAL;\n+\t}\n+\tpcp->bits_per_pin = fls(pcp->fmask);\n+\tnr_pins = (pcp->size * BITS_PER_BYTE) / pcp->bits_per_pin;\n+\n+\tdev_dbg(pcp->dev, \"allocating %i pins\\n\", nr_pins);\n+\tpcp->pins.pa = devm_kcalloc(pcp->dev,\n+\t\t\t\tnr_pins, sizeof(*pcp->pins.pa),\n+\t\t\t\tGFP_KERNEL);\n+\tif (!pcp->pins.pa)\n+\t\treturn -ENOMEM;\n+\n+\tpcp->desc.pins = pcp->pins.pa;\n+\tpcp->desc.npins = nr_pins;\n+\n+\tfor (i = 0; i < pcp->desc.npins; i++) {\n+\t\tunsigned int offset;\n+\t\tint res;\n+\n+\t\toffset = pcp_pin_reg_offset_get(pcp, i);\n+\t\tres = pcp_add_pin(pcp, offset);\n+\t\tif (res < 0) {\n+\t\t\tdev_err(pcp->dev, \"error adding pins: %i\\n\", res);\n+\t\t\treturn res;\n+\t\t}\n+\t}\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * pcp_add_function() - adds a new function to the function list\n+ * @pcp: pcp driver instance\n+ * @fcn: new function allocated\n+ * @name: name of the function\n+ * @vals: array of mux register value pairs used by the function\n+ * @nvals: number of mux register value pairs\n+ * @pgnames: array of pingroup names for the function\n+ * @npgnames: number of pingroup names\n+ *\n+ * Caller must take care of locking.\n+ */\n+static int pcp_add_function(struct pcp_device *pcp, struct pcp_function **fcn,\n+\t\t\t const char *name, struct pcp_func_vals *vals,\n+\t\t\t unsigned int nvals, const char **pgnames,\n+\t\t\t unsigned int npgnames)\n+{\n+\tstruct pcp_function *function;\n+\tint selector;\n+\n+\tfunction = devm_kzalloc(pcp->dev, sizeof(*function), GFP_KERNEL);\n+\tif (!function)\n+\t\treturn -ENOMEM;\n+\n+\tfunction->vals = vals;\n+\tfunction->nvals = nvals;\n+\tfunction->name = name;\n+\n+\tselector = pinmux_generic_add_function(pcp->pctl, name,\n+\t\t\t\t\t pgnames, npgnames,\n+\t\t\t\t\t function);\n+\tif (selector < 0) {\n+\t\tdevm_kfree(pcp->dev, function);\n+\t\t*fcn = NULL;\n+\t} else {\n+\t\t*fcn = function;\n+\t}\n+\n+\treturn selector;\n+}\n+\n+/*\n+ * check whether data matches enable bits or disable bits\n+ * Return value: 1 for matching enable bits, 0 for matching disable bits,\n+ * and negative value for matching failure.\n+ */\n+static int pcp_config_match(unsigned int data, unsigned int enable,\n+\t\t\t unsigned int disable)\n+{\n+\tint ret = -EINVAL;\n+\n+\tif (data == enable)\n+\t\tret = 1;\n+\telse if (data == disable)\n+\t\tret = 0;\n+\treturn ret;\n+}\n+\n+static void add_config(struct pcp_conf_vals **conf, enum pin_config_param param,\n+\t\t unsigned int value, unsigned int enable,\n+\t\t unsigned int disable, unsigned int mask)\n+{\n+\t(*conf)->param = param;\n+\t(*conf)->val = value;\n+\t(*conf)->enable = enable;\n+\t(*conf)->disable = disable;\n+\t(*conf)->mask = mask;\n+\t(*conf)++;\n+}\n+\n+static void add_setting(unsigned long **setting, enum pin_config_param param,\n+\t\t\tunsigned int arg)\n+{\n+\t**setting = pinconf_to_config_packed(param, arg);\n+\t(*setting)++;\n+}\n+\n+/* add pinconf setting with 2 parameters */\n+static void pcp_add_conf2(struct pcp_device *pcp, struct device_node *np,\n+\t\t\t const char *name, enum pin_config_param param,\n+\t\t\t struct pcp_conf_vals **conf, unsigned long **settings)\n+{\n+\tunsigned int value[2], shift;\n+\tint ret;\n+\n+\tret = of_property_read_u32_array(np, name, value, 2);\n+\tif (ret)\n+\t\treturn;\n+\t/* set value & mask */\n+\tvalue[0] &= value[1];\n+\tshift = ffs(value[1]) - 1;\n+\t/* skip enable & disable */\n+\tadd_config(conf, param, value[0], 0, 0, value[1]);\n+\tadd_setting(settings, param, value[0] >> shift);\n+}\n+\n+/* add pinconf setting with 4 parameters */\n+static void pcp_add_conf4(struct pcp_device *pcp, struct device_node *np,\n+\t\t\t const char *name, enum pin_config_param param,\n+\t\t\t struct pcp_conf_vals **conf, unsigned long **settings)\n+{\n+\tunsigned int value[4];\n+\tint ret;\n+\n+\t/* value to set, enable, disable, mask */\n+\tret = of_property_read_u32_array(np, name, value, 4);\n+\tif (ret)\n+\t\treturn;\n+\tif (!value[3]) {\n+\t\tdev_err(pcp->dev, \"mask field of the property can't be 0\\n\");\n+\t\treturn;\n+\t}\n+\tvalue[0] &= value[3];\n+\tvalue[1] &= value[3];\n+\tvalue[2] &= value[3];\n+\tret = pcp_config_match(value[0], value[1], value[2]);\n+\tif (ret < 0)\n+\t\tdev_dbg(pcp->dev, \"failed to match enable or disable bits\\n\");\n+\tadd_config(conf, param, value[0], value[1], value[2], value[3]);\n+\tadd_setting(settings, param, ret);\n+}\n+\n+static int pcp_parse_pinconf(struct pcp_device *pcp, struct device_node *np,\n+\t\t\t struct pcp_function *func, struct pinctrl_map **map)\n+\n+{\n+\tstatic const struct pcp_conf_type prop2[] = {\n+\t\t{ \"pinctrl-packed,drive-strength\", PIN_CONFIG_DRIVE_STRENGTH, },\n+\t\t{ \"pinctrl-packed,slew-rate\", PIN_CONFIG_SLEW_RATE, },\n+\t\t{ \"pinctrl-packed,input-enable\", PIN_CONFIG_INPUT_ENABLE, },\n+\t\t{ \"pinctrl-packed,input-schmitt\", PIN_CONFIG_INPUT_SCHMITT, },\n+\t\t{ \"pinctrl-packed,low-power-mode\", PIN_CONFIG_MODE_LOW_POWER, },\n+\t};\n+\tstatic const struct pcp_conf_type prop4[] = {\n+\t\t{ \"pinctrl-packed,bias-pullup\", PIN_CONFIG_BIAS_PULL_UP, },\n+\t\t{ \"pinctrl-packed,bias-pulldown\", PIN_CONFIG_BIAS_PULL_DOWN, },\n+\t\t{ \"pinctrl-packed,input-schmitt-enable\",\n+\t\t\tPIN_CONFIG_INPUT_SCHMITT_ENABLE, },\n+\t};\n+\n+\tunsigned long *settings = NULL, *s = NULL;\n+\tstruct pcp_conf_vals *conf = NULL;\n+\tstruct pinctrl_map *m = *map;\n+\tint i = 0, nconfs = 0;\n+\n+\t/* If pinconf isn't supported, don't parse properties in below. */\n+\tif (!PCP_HAS_PINCONF)\n+\t\treturn -EOPNOTSUPP;\n+\n+\t/* calculate how many properties are supported in the current node */\n+\tfor (i = 0; i < ARRAY_SIZE(prop2); i++) {\n+\t\tif (of_property_present(np, prop2[i].name))\n+\t\t\tnconfs++;\n+\t}\n+\tfor (i = 0; i < ARRAY_SIZE(prop4); i++) {\n+\t\tif (of_property_present(np, prop4[i].name))\n+\t\t\tnconfs++;\n+\t}\n+\tif (!nconfs)\n+\t\treturn -EOPNOTSUPP;\n+\n+\tfunc->conf = devm_kcalloc(pcp->dev,\n+\t\t\t\t nconfs, sizeof(struct pcp_conf_vals),\n+\t\t\t\t GFP_KERNEL);\n+\tif (!func->conf)\n+\t\treturn -ENOMEM;\n+\tfunc->nconfs = nconfs;\n+\tconf = &(func->conf[0]);\n+\tm++;\n+\tsettings = devm_kcalloc(pcp->dev, nconfs, sizeof(unsigned long),\n+\t\t\t\tGFP_KERNEL);\n+\tif (!settings)\n+\t\treturn -ENOMEM;\n+\ts = &settings[0];\n+\n+\tfor (i = 0; i < ARRAY_SIZE(prop2); i++)\n+\t\tpcp_add_conf2(pcp, np, prop2[i].name, prop2[i].param,\n+\t\t\t &conf, &s);\n+\tfor (i = 0; i < ARRAY_SIZE(prop4); i++)\n+\t\tpcp_add_conf4(pcp, np, prop4[i].name, prop4[i].param,\n+\t\t\t &conf, &s);\n+\tm->type = PIN_MAP_TYPE_CONFIGS_GROUP;\n+\tm->data.configs.group_or_pin = np->name;\n+\tm->data.configs.configs = settings;\n+\tm->data.configs.num_configs = nconfs;\n+\treturn 0;\n+}\n+\n+/**\n+ * pcp_parse_one_pinctrl_entry() - parses a device tree mux entry\n+ * @pcp: pinctrl driver instance\n+ * @np: device node of the mux entry\n+ * @map: map entry\n+ * @num_maps: number of map\n+ * @pgnames: pingroup names\n+ *\n+ * Note that this binding currently supports only sets of one register + value.\n+ *\n+ * Also note that this driver tries to avoid understanding pin and function\n+ * names because of the extra bloat they would cause especially in the case of\n+ * a large number of pins. This driver just sets what is specified for the board\n+ * in the .dts file. Further user space debugging tools can be developed to\n+ * decipher the pin and function names using debugfs.\n+ *\n+ * If you are concerned about the boot time, set up the static pins in\n+ * the bootloader, and only set up selected pins as device tree entries.\n+ */\n+static int pcp_parse_one_pinctrl_entry(struct pcp_device *pcp,\n+\t\t\t\t struct device_node *np,\n+\t\t\t\t struct pinctrl_map **map,\n+\t\t\t\t unsigned int *num_maps,\n+\t\t\t\t const char **pgnames)\n+{\n+\tint rows, *pins, found, res, i, fsel, gsel;\n+\tconst char *name = \"pinctrl-packed,pins\";\n+\tstruct pcp_function *function = NULL;\n+\tstruct pcp_func_vals *vals;\n+\n+\tres = -ENOMEM;\n+\tfound = 0;\n+\trows = pinctrl_count_index_with_args(np, name);\n+\tif (rows <= 0) {\n+\t\tdev_err(pcp->dev, \"Invalid number of rows: %d\\n\", rows);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tvals = devm_kcalloc(pcp->dev, rows, sizeof(*vals), GFP_KERNEL);\n+\tif (!vals)\n+\t\treturn -ENOMEM;\n+\n+\tpins = devm_kcalloc(pcp->dev, rows, sizeof(*pins), GFP_KERNEL);\n+\tif (!pins)\n+\t\tgoto free_vals;\n+\n+\tfor (i = 0; i < rows; i++) {\n+\t\tstruct of_phandle_args pinctrl_spec;\n+\t\tunsigned int offset;\n+\t\tunsigned int pin_index, func_sel;\n+\t\tunsigned int shift, mask, val;\n+\n+\t\tres = pinctrl_parse_index_with_args(np, name, i, &pinctrl_spec);\n+\t\tif (res)\n+\t\t\treturn res;\n+\n+\t\t/* Expect <pin_index func_sel> for bit-per-mux users. */\n+\t\tif (pinctrl_spec.args_count < 2) {\n+\t\t\tdev_err(pcp->dev,\n+\t\t\t\t\"invalid args_count for bit-per-mux spec: %i\\n\",\n+\t\t\t\tpinctrl_spec.args_count);\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\tpin_index = pinctrl_spec.args[0];\n+\t\tfunc_sel = pinctrl_spec.args[1];\n+\n+\t\tif (pin_index >= pcp->desc.npins) {\n+\t\t\tdev_err(pcp->dev,\n+\t\t\t\t\"pin index out of range for %pOFn: %u (npins %u)\\n\",\n+\t\t\t\tnp, pin_index, pcp->desc.npins);\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\toffset = pcp_pin_reg_offset_get(pcp, pin_index);\n+\t\tshift = pcp_pin_shift_reg_get(pcp, pin_index);\n+\n+\t\tmask = pcp->fmask << shift;\n+\t\tval = (func_sel << shift) & mask;\n+\n+\t\tvals[found].reg = pcp->base_offset + offset;\n+\t\tvals[found].val = val;\n+\t\tvals[found].mask = mask;\n+\n+\t\tdev_dbg(pcp->dev,\n+\t\t\t \"%pOFn pin: %u offset: 0x%x func: 0x%x val: 0x%x mask: 0x%x\\n\",\n+\t\t\t pinctrl_spec.np, pin_index, offset,\n+\t\t\t func_sel, val, mask);\n+\n+\t\tpins[found++] = pin_index;\n+\t}\n+\n+\tpgnames[0] = np->name;\n+\tmutex_lock(&pcp->mutex);\n+\tfsel = pcp_add_function(pcp, &function, np->name, vals, found,\n+\t\t\t\tpgnames, 1);\n+\tif (fsel < 0) {\n+\t\tres = fsel;\n+\t\tgoto free_pins;\n+\t}\n+\n+\tgsel = pinctrl_generic_add_group(pcp->pctl, np->name, pins, found, pcp);\n+\tif (gsel < 0) {\n+\t\tres = gsel;\n+\t\tgoto free_function;\n+\t}\n+\n+\t(*map)->type = PIN_MAP_TYPE_MUX_GROUP;\n+\t(*map)->data.mux.group = np->name;\n+\t(*map)->data.mux.function = np->name;\n+\n+\tif (PCP_HAS_PINCONF && function) {\n+\t\tres = pcp_parse_pinconf(pcp, np, function, map);\n+\t\tif (res == 0)\n+\t\t\t*num_maps = 2;\n+\t\telse if (res == -EOPNOTSUPP)\n+\t\t\t*num_maps = 1;\n+\t\telse\n+\t\t\tgoto free_pingroups;\n+\t} else {\n+\t\t*num_maps = 1;\n+\t}\n+\tmutex_unlock(&pcp->mutex);\n+\n+\treturn 0;\n+\n+free_pingroups:\n+\tpinctrl_generic_remove_group(pcp->pctl, gsel);\n+\t*num_maps = 1;\n+free_function:\n+\tpinmux_generic_remove_function(pcp->pctl, fsel);\n+free_pins:\n+\tmutex_unlock(&pcp->mutex);\n+\tdevm_kfree(pcp->dev, pins);\n+\n+free_vals:\n+\tdevm_kfree(pcp->dev, vals);\n+\n+\treturn res;\n+}\n+\n+/**\n+ * pcp_dt_node_to_map() - allocates and parses pinctrl maps\n+ * @pctldev: pinctrl instance\n+ * @np_config: device tree pinmux entry\n+ * @map: array of map entries\n+ * @num_maps: number of maps\n+ */\n+static int pcp_dt_node_to_map(struct pinctrl_dev *pctldev,\n+\t\t\t struct device_node *np_config,\n+\t\t\t struct pinctrl_map **map, unsigned int *num_maps)\n+{\n+\tstruct pcp_device *pcp;\n+\tconst char **pgnames;\n+\tint ret;\n+\n+\tpcp = pinctrl_dev_get_drvdata(pctldev);\n+\t/* create 2 maps. One is for pinmux, and the other is for pinconf. */\n+\t*map = devm_kcalloc(pcp->dev, 2, sizeof(**map), GFP_KERNEL);\n+\tif (!*map)\n+\t\treturn -ENOMEM;\n+\n+\t*num_maps = 0;\n+\n+\tpgnames = devm_kzalloc(pcp->dev, sizeof(*pgnames), GFP_KERNEL);\n+\tif (!pgnames) {\n+\t\tret = -ENOMEM;\n+\t\tgoto free_map;\n+\t}\n+\n+\tif (of_find_property(np_config, \"pinctrl-packed,pins\", NULL)) {\n+\t\tret = pcp_parse_one_pinctrl_entry(pcp, np_config, map, num_maps,\n+\t\t\t\t\t\t pgnames);\n+\t} else {\n+\t\tret = -EINVAL;\n+\t}\n+\n+\tif (ret < 0) {\n+\t\tdev_err(pcp->dev, \"no pins entries for %pOFn\\n\", np_config);\n+\t\tgoto free_pgnames;\n+\t}\n+\n+\treturn 0;\n+\n+free_pgnames:\n+\tdevm_kfree(pcp->dev, pgnames);\n+free_map:\n+\tdevm_kfree(pcp->dev, *map);\n+\treturn ret;\n+}\n+\n+static int pcp_add_gpio_func(struct device_node *node, struct pcp_device *pcp)\n+{\n+\tconst char *cellname = \"#pinctrl-packed,gpio-range-cells\";\n+\tconst char *propname = \"pinctrl-packed,gpio-range\";\n+\tstruct pcp_gpiofunc_range *range;\n+\tstruct of_phandle_args gpiospec;\n+\tint ret, i;\n+\n+\tfor (i = 0; ; i++) {\n+\t\tret = of_parse_phandle_with_args(node, propname, cellname,\n+\t\t\t\t\t\t i, &gpiospec);\n+\t\t/* Do not treat it as error. Only treat it as end condition. */\n+\t\tif (ret) {\n+\t\t\tret = 0;\n+\t\t\tbreak;\n+\t\t}\n+\t\trange = devm_kzalloc(pcp->dev, sizeof(*range), GFP_KERNEL);\n+\t\tif (!range) {\n+\t\t\tret = -ENOMEM;\n+\t\t\tbreak;\n+\t\t}\n+\t\trange->offset = gpiospec.args[0];\n+\t\trange->npins = gpiospec.args[1];\n+\t\trange->gpiofunc = gpiospec.args[2];\n+\t\tmutex_lock(&pcp->mutex);\n+\t\tlist_add_tail(&range->node, &pcp->gpiofuncs);\n+\t\tmutex_unlock(&pcp->mutex);\n+\t}\n+\treturn ret;\n+}\n+\n+static int pcp_probe(struct platform_device *pdev)\n+{\n+\tstruct device_node *np = pdev->dev.of_node;\n+\tstruct resource res, parent_res;\n+\tconst struct pcp_soc_data *soc;\n+\tstruct pcp_device *pcp;\n+\tint ret;\n+\n+\tsoc = of_device_get_match_data(&pdev->dev);\n+\tif (WARN_ON(!soc))\n+\t\treturn -EINVAL;\n+\n+\tpcp = devm_kzalloc(&pdev->dev, sizeof(*pcp), GFP_KERNEL);\n+\tif (!pcp)\n+\t\treturn -ENOMEM;\n+\n+\tpcp->dev = &pdev->dev;\n+\tpcp->np = np;\n+\tmutex_init(&pcp->mutex);\n+\tINIT_LIST_HEAD(&pcp->gpiofuncs);\n+\tpcp->flags = soc->flags;\n+\n+\tpcp->regmap = syscon_node_to_regmap(np->parent);\n+\tif (IS_ERR(pcp->regmap)) {\n+\t\tdev_err(pcp->dev, \"could not get regmap from parent\\n\");\n+\t\treturn PTR_ERR(pcp->regmap);\n+\t}\n+\tpcp->width = regmap_get_reg_stride(pcp->regmap) * BITS_PER_BYTE;\n+\n+\tret = of_address_to_resource(np, 0, &res);\n+\tif (ret) {\n+\t\tdev_err(pcp->dev, \"could not get resource from node\\n\");\n+\t\treturn ret;\n+\t}\n+\n+\tret = of_address_to_resource(np->parent, 0, &parent_res);\n+\tif (ret) {\n+\t\tdev_err(pcp->dev,\n+\t\t\t\"could not get resource from parent syscon\\n\");\n+\t\treturn ret;\n+\t}\n+\n+\tpcp->base_offset = res.start - parent_res.start;\n+\tpcp->size = resource_size(&res);\n+\n+\tret = of_property_read_u32(np, \"pinctrl-packed,function-mask\",\n+\t\t\t\t &pcp->fmask);\n+\tif (ret) {\n+\t\tdev_err(pcp->dev, \"function mask property not specified\\n\");\n+\t\treturn ret;\n+\t}\n+\tplatform_set_drvdata(pdev, pcp);\n+\n+\tpcp->desc.name = dev_name(&pdev->dev);\n+\tpcp->desc.pctlops = &pcp_pinctrl_ops;\n+\tpcp->desc.pmxops = &pcp_pinmux_ops;\n+\tif (PCP_HAS_PINCONF)\n+\t\tpcp->desc.confops = &pcp_pinconf_ops;\n+\tpcp->desc.owner = THIS_MODULE;\n+\n+\tret = pcp_allocate_pin_table(pcp);\n+\tif (ret < 0) {\n+\t\tdev_err(pcp->dev, \"failed to allocate pin table\\n\");\n+\t\treturn ret;\n+\t}\n+\n+\tret = devm_pinctrl_register_and_init(pcp->dev, &pcp->desc, pcp, &pcp->pctl);\n+\tif (ret) {\n+\t\tdev_err(pcp->dev, \"could not register packed pinctrl driver\\n\");\n+\t\treturn ret;\n+\t}\n+\n+\tret = pcp_add_gpio_func(np, pcp);\n+\tif (ret < 0) {\n+\t\tdev_err(pcp->dev, \"failed to add gpio functions\\n\");\n+\t\treturn ret;\n+\t}\n+\n+\tdev_info(pcp->dev, \"%i pins registered\\n\", pcp->desc.npins);\n+\n+\tret = pinctrl_enable(pcp->pctl);\n+\tif (ret) {\n+\t\tdev_err(pcp->dev, \"failed to enable pinctrl\\n\");\n+\t\treturn ret;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static const struct pcp_soc_data pinctrl_data = {\n+};\n+static const struct pcp_soc_data pinconf_data = {\n+\t.flags = PCP_FEAT_PINCONF,\n+};\n+\n+static const struct of_device_id pcp_of_match[] = {\n+\t{ .compatible = \"pinctrl-packed\", .data = &pinctrl_data },\n+\t{ .compatible = \"pinconf-packed\", .data = &pinconf_data },\n+\t{ },\n+};\n+MODULE_DEVICE_TABLE(of, pcp_of_match);\n+\n+static struct platform_driver pcp_driver = {\n+\t.probe\t\t= pcp_probe,\n+\t.driver = {\n+\t\t.name\t\t= DRIVER_NAME,\n+\t\t.of_match_table\t= pcp_of_match,\n+\t},\n+};\n+\n+module_platform_driver(pcp_driver);\n+\n+MODULE_AUTHOR(\"Tony Lindgren <tony@atomide.com>\");\n+MODULE_AUTHOR(\"Billy Tsai <billy_tsai@aspeedtech.com>\");\n+MODULE_DESCRIPTION(\"packed-field type device tree based pinctrl driver\");\n+MODULE_LICENSE(\"GPL\");\n", "prefixes": [ "RFC", "2/2" ] }