diff options
Diffstat (limited to 'drivers/pinctrl/bcm/pinctrl-bcm2835.c')
| -rw-r--r-- | drivers/pinctrl/bcm/pinctrl-bcm2835.c | 110 |
1 files changed, 92 insertions, 18 deletions
diff --git a/drivers/pinctrl/bcm/pinctrl-bcm2835.c b/drivers/pinctrl/bcm/pinctrl-bcm2835.c index 1489191a213f..c165674c5b4d 100644 --- a/drivers/pinctrl/bcm/pinctrl-bcm2835.c +++ b/drivers/pinctrl/bcm/pinctrl-bcm2835.c @@ -34,6 +34,7 @@ #include <linux/seq_file.h> #include <linux/slab.h> #include <linux/spinlock.h> +#include <linux/string_choices.h> #include <linux/types.h> #include <dt-bindings/pinctrl/bcm2835.h> @@ -244,6 +245,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, 0444); +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); @@ -341,21 +346,24 @@ static int bcm2835_gpio_get_direction(struct gpio_chip *chip, unsigned int offse struct bcm2835_pinctrl *pc = gpiochip_get_data(chip); enum bcm2835_fsel fsel = bcm2835_pinctrl_fsel_get(pc, offset); - /* Alternative function doesn't clearly provide a direction */ - if (fsel > BCM2835_FSEL_GPIO_OUT) - return -EINVAL; - - if (fsel == BCM2835_FSEL_GPIO_IN) - return GPIO_LINE_DIRECTION_IN; + if (fsel == BCM2835_FSEL_GPIO_OUT) + return GPIO_LINE_DIRECTION_OUT; - return GPIO_LINE_DIRECTION_OUT; + /* + * Alternative function doesn't clearly provide a direction. Default + * to INPUT. + */ + return GPIO_LINE_DIRECTION_IN; } -static void bcm2835_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +static int bcm2835_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) { struct bcm2835_pinctrl *pc = gpiochip_get_data(chip); bcm2835_gpio_set_bit(pc, value ? GPSET0 : GPCLR0, offset); + + return 0; } static int bcm2835_gpio_direction_output(struct gpio_chip *chip, @@ -748,7 +756,7 @@ static void bcm2835_pctl_pin_dbg_show(struct pinctrl_dev *pctldev, int irq = irq_find_mapping(chip->irq.domain, offset); seq_printf(s, "function %s in %s; irq %d (%s)", - fname, value ? "hi" : "lo", + fname, str_hi_lo(value), irq, irq_type_names[pc->irq_type[offset]]); } @@ -926,6 +934,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 +985,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, @@ -1003,8 +1015,27 @@ static const struct pinmux_ops bcm2835_pmx_ops = { static int bcm2835_pinconf_get(struct pinctrl_dev *pctldev, unsigned pin, unsigned long *config) { - /* No way to read back config in HW */ - return -ENOTSUPP; + enum pin_config_param param = pinconf_to_config_param(*config); + struct bcm2835_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev); + enum bcm2835_fsel fsel = bcm2835_pinctrl_fsel_get(pc, pin); + u32 val; + + /* No way to read back bias config in HW */ + + switch (param) { + case PIN_CONFIG_LEVEL: + if (fsel != BCM2835_FSEL_GPIO_OUT) + return -EINVAL; + + val = bcm2835_gpio_get_bit(pc, GPLEV0, pin); + *config = pinconf_to_config_packed(param, val); + break; + + default: + return -ENOTSUPP; + } + + return 0; } static void bcm2835_pull_config_set(struct bcm2835_pinctrl *pc, @@ -1060,7 +1091,7 @@ static int bcm2835_pinconf_set(struct pinctrl_dev *pctldev, break; /* Set output-high or output-low */ - case PIN_CONFIG_OUTPUT: + case PIN_CONFIG_LEVEL: bcm2835_gpio_set_bit(pc, arg ? GPSET0 : GPCLR0, pin); break; @@ -1079,6 +1110,45 @@ static const struct pinconf_ops bcm2835_pinconf_ops = { .pin_config_set = bcm2835_pinconf_set, }; +static int bcm2711_pinconf_get(struct pinctrl_dev *pctldev, unsigned pin, + unsigned long *config) +{ + struct bcm2835_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev); + enum pin_config_param param = pinconf_to_config_param(*config); + u32 offset, shift, val; + + offset = PUD_2711_REG_OFFSET(pin); + shift = PUD_2711_REG_SHIFT(pin); + val = bcm2835_gpio_rd(pc, GP_GPIO_PUP_PDN_CNTRL_REG0 + (offset * 4)); + + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + if (((val >> shift) & PUD_2711_MASK) != BCM2711_PULL_NONE) + return -EINVAL; + + break; + + case PIN_CONFIG_BIAS_PULL_UP: + if (((val >> shift) & PUD_2711_MASK) != BCM2711_PULL_UP) + return -EINVAL; + + *config = pinconf_to_config_packed(param, 50000); + break; + + case PIN_CONFIG_BIAS_PULL_DOWN: + if (((val >> shift) & PUD_2711_MASK) != BCM2711_PULL_DOWN) + return -EINVAL; + + *config = pinconf_to_config_packed(param, 50000); + break; + + default: + return bcm2835_pinconf_get(pctldev, pin, config); + } + + return 0; +} + static void bcm2711_pull_config_set(struct bcm2835_pinctrl *pc, unsigned int pin, unsigned int arg) { @@ -1132,7 +1202,7 @@ static int bcm2711_pinconf_set(struct pinctrl_dev *pctldev, break; /* Set output-high or output-low */ - case PIN_CONFIG_OUTPUT: + case PIN_CONFIG_LEVEL: bcm2835_gpio_set_bit(pc, arg ? GPSET0 : GPCLR0, pin); break; @@ -1146,7 +1216,7 @@ static int bcm2711_pinconf_set(struct pinctrl_dev *pctldev, static const struct pinconf_ops bcm2711_pinconf_ops = { .is_generic = true, - .pin_config_get = bcm2835_pinconf_get, + .pin_config_get = bcm2711_pinconf_get, .pin_config_set = bcm2711_pinconf_set, }; @@ -1213,6 +1283,7 @@ static const struct of_device_id bcm2835_pinctrl_match[] = { }, {} }; +MODULE_DEVICE_TABLE(of, bcm2835_pinctrl_match); static int bcm2835_pinctrl_probe(struct platform_device *pdev) { @@ -1361,6 +1432,9 @@ static int bcm2835_pinctrl_probe(struct platform_device *pdev) goto out_remove; } + dev_info(dev, "GPIO_OUT persistence: %s\n", + str_yes_no(persist_gpio_outputs)); + return 0; out_remove: |
