Received: by 2002:a05:6a10:f347:0:0:0:0 with SMTP id d7csp346289pxu; Fri, 11 Dec 2020 03:55:04 -0800 (PST) X-Google-Smtp-Source: ABdhPJz7MBQq+ykZgxzm+kzMtjAm4wiMikN6uJ2leGLe4LwkfX8SPks+BmiP5TelkGew2k6mP2/1 X-Received: by 2002:aa7:d6c9:: with SMTP id x9mr11037942edr.96.1607687704509; Fri, 11 Dec 2020 03:55:04 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1607687704; cv=none; d=google.com; s=arc-20160816; b=YTP61+TPDI32gGdxgHX7afD4hE5jVRr2f9lMlUm7dzdj1IHfy1Zxva2wPUInWbI7Eb UFqjhOeZwu82wVumMSlfhgzE0UHLYSqSn6BC+YAvP6f3mSDILEB0htNzIz8mAIlaJvfy OZkKis1GV44Z4VMjGy+gpgU7Q/SKKuVeqyzSsF3nCulWdNQbwj24ICCp8iVEuiwMXJAw UdeAjnLj2JfaIaq8wrusaqmwkZocuYKszsSyhIWZ5jolrh4d0Vd+m9Ed5M/W1Ft4Wk3+ YKVUQ8NtiwSD2+N2DSnE2X+84eM5DhRYFnUKMQykWOx5MC3mBPngRMBlXTnvRGsfrkkC Gwbg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :message-id:date:subject:cc:to:from:dkim-signature; bh=7mhKza1Cax/u42moL2BVtPYkbdV4UBxjh48EyphNhVU=; b=rUVCcbJEM1/NESYlKzNVjpd4Fqe/WrP/+4FOXKS91xasCxAM2imnPx7wutFzswfvpJ 6e0+ACGbL50G+OYtEcQV2TJYH3CFmygvdGfj1ou31FbywgwUhe486+hLyhSKgQSK8W9t 0ctcZEsHaXs85aySw0//MKclK+m35S/Fu7mVikv1KNkv6/HG3ahmLGoxtHbu/PdxvDlK bb9vWuiQPA0hW5SpW8qq62awpH45in4IiFXhcN6zUamPQs4ZtgFAYLw4iKP4sF8lIcWp neyxVBWZqZCL9akO60Mz3Algr7LnFtr0WPU0/HQRW8bsE2XzDLL/bbwOdSTGfTWLqpL6 FGaA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@beagleboard-org.20150623.gappssmtp.com header.s=20150623 header.b=BZaANn+W; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id l61si4368933ede.106.2020.12.11.03.54.40; Fri, 11 Dec 2020 03:55:04 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; dkim=pass header.i=@beagleboard-org.20150623.gappssmtp.com header.s=20150623 header.b=BZaANn+W; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2405396AbgLKEbO (ORCPT + 99 others); Thu, 10 Dec 2020 23:31:14 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:37388 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2405377AbgLKEaw (ORCPT ); Thu, 10 Dec 2020 23:30:52 -0500 Received: from mail-ej1-x641.google.com (mail-ej1-x641.google.com [IPv6:2a00:1450:4864:20::641]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 20F21C0613CF for ; Thu, 10 Dec 2020 20:30:12 -0800 (PST) Received: by mail-ej1-x641.google.com with SMTP id d17so10537632ejy.9 for ; Thu, 10 Dec 2020 20:30:12 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=beagleboard-org.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=7mhKza1Cax/u42moL2BVtPYkbdV4UBxjh48EyphNhVU=; b=BZaANn+WakBOTmPUNY8qL1KIrb5KJ1veI1O0KRHiVUwHj9yaYQ+MuQk7IWDp8B7K00 viqTKdzcvd6trw13q6NnTCAvcss6ewUMDdv8TObErfbCkfO6qlzqBZ/OYAl+FJKSPJgH KlNyrh6UHw8Z4aWFiX8wV7OnO3+jZ0zxCfJHKtvgYrFK5Q42ig0WPieZ44w4tMSsr3nZ /vlJXWq/bvKPbUN4wyEAf/ynXDVgdDOvfp/Jkp6QwrDK8+R+Ks5ym6d3Av3aRUYZ5ACj 8qXkgAtX2ThE9hS+s5nE6MtM6AUhdMdzQhE5OnNK1kl6DoXJWa2JCINkh+sJCTgxNdoj 4nng== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=7mhKza1Cax/u42moL2BVtPYkbdV4UBxjh48EyphNhVU=; b=hk6WYCagLGHpzfoEpBhhIPkPPgB9y7x1ARwz0o9X5OXAHZ6cmPOyjwmFX0KrU72mjK baGjtbr9Z/ett9JF5B3h6ahuFO6vhSn70hIAylBC4qGMVzbF85u+4KxgIseMaSXnjN0h y18jhY8tU+WrzZbNusVRbrkEkD6TGMoQBRfnswahTtCDUDbETPgSANH0EqfhoEjBHDRM hMUXfcVP06ImVVfGMc6mXs5XhgxPbcqp0BAx/YdeCAmrJD9NPrPOPEIlZoDG3cX9k0at gJLDYzuh6T8C3jOoVkgVfWh0k+SJa9ulaU8mtFkxDVolh6hZpyp3arcSusx+7c6sK2MX f+Lw== X-Gm-Message-State: AOAM531PdPtRxtXEJR0aBC0RuGafaRmhgo30BEIoG9+LqFNjSuPiauRY 5SdLpEBF0fPrPfDpwEE+kLLJsw== X-Received: by 2002:a17:907:214d:: with SMTP id rk13mr9500297ejb.501.1607661010805; Thu, 10 Dec 2020 20:30:10 -0800 (PST) Received: from x1.hsd1.or.comcast.net ([143.244.37.163]) by smtp.gmail.com with ESMTPSA id b19sm6453163edx.47.2020.12.10.20.30.07 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 10 Dec 2020 20:30:09 -0800 (PST) From: Drew Fustini To: linux-gpio@vger.kernel.org, linux-kernel@vger.kernel.org Cc: Drew Fustini , Pantelis Antoniou , Linus Walleij , Tony Lindgren Subject: [RFC PATCH] pinctrl: add helper to expose pinctrl state in debugfs Date: Thu, 10 Dec 2020 20:26:29 -0800 Message-Id: <20201211042625.129255-1-drew@beagleboard.org> X-Mailer: git-send-email 2.25.1 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org BeagleBoard.org [0] currently uses an out-of-tree driver called bone-pinmux-helper [1] developed by Pantelis Antoniou [2] back in 2013. The driver assists users of our BeagleBone and PocketBeagle boards in rapid prototyping by allowing them to change at run-time between defined set of pinctrl states [3] for each pin on the expansion connectors [4]. This is achieved by exposing a 'state' file in sysfs for each pin which is used by our 'config-pin' utility [5]. Our goal is to eliminate all out-of-tree drivers for BeagleBoard.org boards and thus I have been working to replace bone-pinmux-helper with a new driver that could be acceptable upstream. My understanding is that debugfs, unlike sysfs, could be the appropriate mechanism to expose such functionality. Here is an example of how pin P9.14 on the BeagleBone Black expansion connector [6] would be represented in device tree: &am33xx_pinmux { /* P9_14 (ZCZ ball U14) gpio1_18 */ P9_14_default_pin: pinmux_P9_14_default_pin { pinctrl-single,pins = < AM33XX_IOPAD(0x0848, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; P9_14_gpio_pin: pinmux_P9_14_gpio_pin { pinctrl-single,pins = < AM33XX_IOPAD(0x0848, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; }; P9_14_gpio_pu_pin: pinmux_P9_14_gpio_pu_pin { pinctrl-single,pins = < AM33XX_IOPAD(0x0848, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; }; P9_14_gpio_pd_pin: pinmux_P9_14_gpio_pd_pin { pinctrl-single,pins = < AM33XX_IOPAD(0x0848, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; }; P9_14_gpio_input_pin: pinmux_P9_14_gpio_input_pin { pinctrl-single,pins = < AM33XX_IOPAD(0x0848, PIN_INPUT | MUX_MODE7) >; }; P9_14_pwm_pin: pinmux_P9_14_pwm_pin { pinctrl-single,pins = < AM33XX_IOPAD(0x0848, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE6) >; }; }; &ocp { /* P9_14 (ZCZ ball U14) */ P9_14_pinmux { compatible = "pinctrl,state-helper"; status = "okay"; pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "gpio_input", "pwm"; pinctrl-0 = <&P9_14_default_pin>; pinctrl-1 = <&P9_14_gpio_pin>; pinctrl-2 = <&P9_14_gpio_pu_pin>; pinctrl-3 = <&P9_14_gpio_pd_pin>; pinctrl-4 = <&P9_14_gpio_input_pin>; pinctrl-5 = <&P9_14_pwm_pin>; }; }; I used the compatible string "pinctrl,state-helper" but would appreciate advice on how to best name this. Should I create a new vendor prefix? The P9_14_pinmux entry would cause pinctrl-state-helper to be probed. The driver would create the corresponding pinctrl state file in debugfs for the pin. Here is an example of how the state can be read and written from userspace: root@beaglebone:~# cat /sys/kernel/debug/ocp\:P9_14_pinmux/state default root@beaglebone:~# echo pwm > /sys/kernel/debug/ocp\:P9_14_pinmux/state root@beaglebone:~# cat /sys/kernel/debug/ocp\:P9_14_pinmux/state pwm I would very much appreciate feedback on both this general concept, and also specific areas in which the code should be changed to be acceptable upstream. Thank you! Drew [0] http://beagleboard.org/latest-images [1] https://github.com/beagleboard/linux/blob/5.4/drivers/misc/cape/beaglebone/bone-pinmux-helper.c [2] https://github.com/RobertCNelson/linux-dev/blob/master/patches/drivers/ti/gpio/0001-BeagleBone-pinmux-helper.patch [3] https://github.com/beagleboard/BeagleBoard-DeviceTrees/blob/v5.4.x-ti-overlays/src/arm/am335x-bone-common-univ.dtsi#L2084 [4] https://github.com/beagleboard/beaglebone-black/wiki/System-Reference-Manual#section-7-1 [5] https://github.com/beagleboard/bb.org-overlays/blob/master/tools/beaglebone-universal-io/config-pin [6] https://beagleboard.org/Support/bone101/#headers Cc: Pantelis Antoniou Cc: Linus Walleij Cc: Tony Lindgren Signed-off-by: Drew Fustini --- drivers/pinctrl/Kconfig | 10 ++ drivers/pinctrl/Makefile | 1 + drivers/pinctrl/pinctrl-state-helper.c | 233 +++++++++++++++++++++++++ 3 files changed, 244 insertions(+) create mode 100644 drivers/pinctrl/pinctrl-state-helper.c diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index 8828613c4e0e..4faed5c8c83b 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -255,6 +255,16 @@ config PINCTRL_SINGLE help This selects the device tree based generic pinctrl driver. +config PINCTRL_STATE_HELPER + tristate "Helper to expose pinctrl state to debugfs" + depends on OF + depends on HAS_IOMEM + select GENERIC_PINCTRL_GROUPS + select GENERIC_PINMUX_FUNCTIONS + select GENERIC_PINCONF + help + This selects the device tree based generic pinctrl driver. + config PINCTRL_SIRF bool "CSR SiRFprimaII pin controller driver" depends on ARCH_SIRF diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index 1731b2154df9..156c356dbd3f 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -34,6 +34,7 @@ obj-$(CONFIG_PINCTRL_RZA1) += pinctrl-rza1.o obj-$(CONFIG_PINCTRL_RZA2) += pinctrl-rza2.o obj-$(CONFIG_PINCTRL_RZN1) += pinctrl-rzn1.o obj-$(CONFIG_PINCTRL_SINGLE) += pinctrl-single.o +obj-$(CONFIG_PINCTRL_STATE_HELPER) += pinctrl-state-helper.o obj-$(CONFIG_PINCTRL_SIRF) += sirf/ obj-$(CONFIG_PINCTRL_SX150X) += pinctrl-sx150x.o obj-$(CONFIG_ARCH_TEGRA) += tegra/ diff --git a/drivers/pinctrl/pinctrl-state-helper.c b/drivers/pinctrl/pinctrl-state-helper.c new file mode 100644 index 000000000000..d11edb9ee9b4 --- /dev/null +++ b/drivers/pinctrl/pinctrl-state-helper.c @@ -0,0 +1,233 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Pinmux state helper driver + * + * Copyright (C) 2013 Pantelis Antoniou + * Copyright (C) 2020 Drew Fustini + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "core.h" +#include "devicetree.h" +#include "pinconf.h" +#include "pinmux.h" + +#define DRIVER_NAME "pinctrl_state_helper" + +struct pinctrl_state_helper_priv { + unsigned int offset; + struct device *dev; + struct pinctrl *pinctrl; + char selected_state_name[64]; +}; + +static ssize_t pinctrl_state_read(struct file *file, + char __user *usr_buf, + size_t size, loff_t *ppos) +{ + int cnt; + char buf[64]; + struct pinctrl_state_helper_priv *priv; + struct seq_file *sfile; + char *state_name; + + sfile = file->private_data; + priv = sfile->private; + state_name = priv->selected_state_name; + if (state_name == NULL || strlen(state_name) == 0) + state_name = "none"; + + if (*ppos != 0) + return 0; + + cnt = snprintf(buf, sizeof(buf), "%s\n", state_name); + + return simple_read_from_buffer(usr_buf, size, ppos, buf, cnt); +} + +static ssize_t pinctrl_state_write(struct file *file, const char __user *ubuf, size_t cnt, loff_t *ppos) +{ + int err; + char *s; + char state_name[64]; + struct seq_file *sfile; + struct pinctrl *p; + struct pinctrl_state *state; + struct pinctrl_state_helper_priv *priv; + + if (cnt > 63) { + pr_debug("%s: cnt TRUNCATED to 63", __func__); + cnt = 63; + } + + if (copy_from_user(state_name, ubuf, cnt)) { + pr_debug("%s: return -EFAULT", __func__); + return -EFAULT; + } + + state_name[cnt] = '\0'; + s = strchr(state_name, '\n'); + if (s != NULL) + *s = '\0'; + + sfile = file->private_data; + priv = sfile->private; + strncpy(priv->selected_state_name, state_name, 64); + + p = devm_pinctrl_get(priv->dev); + + state = pinctrl_lookup_state(p, state_name); + + if (!IS_ERR(state)) { + err = pinctrl_select_state(p, state); + if (err != 0) { + pr_err("Failed to select state %s\n", state_name); + return -EINVAL; + } + } else { + err = PTR_ERR_OR_ZERO(state); + pr_err("Failed to find state %s err=%d\n", state_name, err); + return -EINVAL; + } + + if (*ppos != 0) { + pr_err("%s: ppos not zero, return -EINVAL", __func__); + return -EINVAL; + } + + return cnt; +} + +static int pinctrl_state_open(struct inode *inode, struct file *file) +{ + return single_open(file, NULL, inode->i_private); +} + +static const struct file_operations pinctrl_state_ops = { + .owner = THIS_MODULE, + .open = pinctrl_state_open, + .read = pinctrl_state_read, + .write = pinctrl_state_write, + .llseek = no_llseek, + .release = single_release, +}; + +static int pinctrl_state_helper_probe(struct platform_device *pdev) +{ + struct pinctrl_state *state; + char *state_name = "default"; + struct pinctrl_state_helper_priv *priv; + struct device *dev = &pdev->dev; + int err; + struct dentry *pin_dentry; + struct dentry *helper_dir; + struct dentry *parent = NULL; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (priv == NULL) { + dev_err(&pdev->dev, "Failed to allocate priv\n"); + err = -ENOMEM; + goto err_no_mem; + } + + state_name = devm_kzalloc(&pdev->dev, strlen(PINCTRL_STATE_DEFAULT) + 1, + GFP_KERNEL); + if (state_name == NULL) { + dev_err(dev, "Failed to allocate state name\n"); + err = -ENOMEM; + goto err_no_state_mem; + } + strcpy(priv->selected_state_name, PINCTRL_STATE_DEFAULT); + platform_set_drvdata(pdev, priv); + + priv->pinctrl = devm_pinctrl_get(dev); + if (IS_ERR(priv->pinctrl)) { + dev_err(dev, "Failed to get pinctrl\n"); + err = PTR_ERR_OR_ZERO(priv->pinctrl); + goto err_no_pinctrl; + } + + if (err != 0) { + state = pinctrl_lookup_state(priv->pinctrl, + priv->selected_state_name); + if (!IS_ERR(state)) { + err = pinctrl_select_state(priv->pinctrl, state); + if (err != 0) { + dev_err(dev, "Failed to select default state\n"); + goto err_no_state; + } + } else { + *priv->selected_state_name = '\0'; + } + } + + helper_dir = debugfs_create_dir(dev_name(dev), parent); + priv->dev = dev; + pin_dentry = debugfs_create_file("state", 0600, helper_dir, priv, &pinctrl_state_ops); + + return 0; + +err_no_state: + devm_pinctrl_put(priv->pinctrl); +err_no_pinctrl: + devm_kfree(dev, priv->selected_state_name); +err_no_state_mem: + devm_kfree(dev, priv); +err_no_mem: + return err; +} + +static int pinctrl_state_helper_remove(struct platform_device *pdev) +{ + struct pinctrl_state_helper_priv *priv; + + priv = platform_get_drvdata(pdev); + devm_pinctrl_put(priv->pinctrl); + return 0; +} + +static const struct of_device_id helper_of_match[] = { + { + .compatible = "pinctrl,state-helper", + }, + { }, +}; +MODULE_DEVICE_TABLE(of, helper_of_match); + +static struct platform_driver pinctrl_state_helper_driver = { + .probe = pinctrl_state_helper_probe, + .remove = pinctrl_state_helper_remove, + .driver = { + .name = "pinctrl_state_helper", + .owner = THIS_MODULE, + .of_match_table = helper_of_match, + }, +}; + +module_platform_driver(pinctrl_state_helper_driver); + +MODULE_AUTHOR("Drew Fustini "); +MODULE_DESCRIPTION("One-register-per-pin type device tree based pinctrl driver"); +MODULE_LICENSE("GPL v2"); -- 2.25.1