summaryrefslogtreecommitdiff
path: root/drivers/pinctrl
diff options
context:
space:
mode:
authorStefan Wahren <wahrenst@gmx.net>2024-05-03 08:27:45 +0200
committerLinus Walleij <linus.walleij@linaro.org>2024-05-06 08:14:01 +0200
commit8ff05989b44e1a8f7d2bbe67320990ebc2fbb5e5 (patch)
tree0c2b02adf784d5c602479e0c01b3454926a9cd4b /drivers/pinctrl
parent4ad4f6e2fd0a44e0aa03c7f220ac79abfe866fbf (diff)
pinctrl: bcm2835: Make pin freeing behavior configurable
Until now after a bcm2835 pin was freed its pinmux was set to GPIO_IN. So in case it was configured as GPIO_OUT before the configured output level also get lost. As long as GPIO sysfs was used this wasn't actually a problem because the pins and their possible output level were kept by sysfs. Since more and more Raspberry Pi users start using libgpiod they are confused about this behavior. So make the pin freeing behavior of GPIO_OUT configurable via module parameter. In case pinctrl-bcm2835.persist_gpio_outputs is set to 1, the output level is kept. This patch based on the downstream work of Phil Elwell. Link: https://github.com/raspberrypi/linux/pull/6117 Signed-off-by: Stefan Wahren <wahrenst@gmx.net> Message-ID: <20240503062745.11298-1-wahrenst@gmx.net> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Diffstat (limited to 'drivers/pinctrl')
-rw-r--r--drivers/pinctrl/bcm/pinctrl-bcm2835.c19
1 files changed, 15 insertions, 4 deletions
diff --git a/drivers/pinctrl/bcm/pinctrl-bcm2835.c b/drivers/pinctrl/bcm/pinctrl-bcm2835.c
index f5a9372d43bd..7178a38475cc 100644
--- a/drivers/pinctrl/bcm/pinctrl-bcm2835.c
+++ b/drivers/pinctrl/bcm/pinctrl-bcm2835.c
@@ -244,6 +244,10 @@ static const char * const irq_type_names[] = {
[IRQ_TYPE_LEVEL_LOW] = "level-low",
};
+static bool persist_gpio_outputs;
+module_param(persist_gpio_outputs, bool, 0644);
+MODULE_PARM_DESC(persist_gpio_outputs, "Enable GPIO_OUT persistence when pin is freed");
+
static inline u32 bcm2835_gpio_rd(struct bcm2835_pinctrl *pc, unsigned reg)
{
return readl(pc->base + reg);
@@ -926,6 +930,13 @@ static int bcm2835_pmx_free(struct pinctrl_dev *pctldev,
unsigned offset)
{
struct bcm2835_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
+ enum bcm2835_fsel fsel = bcm2835_pinctrl_fsel_get(pc, offset);
+
+ if (fsel == BCM2835_FSEL_GPIO_IN)
+ return 0;
+
+ if (persist_gpio_outputs && fsel == BCM2835_FSEL_GPIO_OUT)
+ return 0;
/* disable by setting to GPIO_IN */
bcm2835_pinctrl_fsel_set(pc, offset, BCM2835_FSEL_GPIO_IN);
@@ -970,10 +981,7 @@ static void bcm2835_pmx_gpio_disable_free(struct pinctrl_dev *pctldev,
struct pinctrl_gpio_range *range,
unsigned offset)
{
- struct bcm2835_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
-
- /* disable by setting to GPIO_IN */
- bcm2835_pinctrl_fsel_set(pc, offset, BCM2835_FSEL_GPIO_IN);
+ bcm2835_pmx_free(pctldev, offset);
}
static int bcm2835_pmx_gpio_set_direction(struct pinctrl_dev *pctldev,
@@ -1419,6 +1427,9 @@ static int bcm2835_pinctrl_probe(struct platform_device *pdev)
goto out_remove;
}
+ dev_info(dev, "GPIO_OUT persistence: %s\n",
+ persist_gpio_outputs ? "yes" : "no");
+
return 0;
out_remove: