From 7f32d37009974c52323335cf8d118384fbce3572 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 20 Nov 2019 21:31:11 +0200 Subject: [PATCH] pinctrl: lynxpoint: Add pin control operations Add implementation for: - pin control, group information retrieval: count, name and pins - pin muxing: - function information (count, name and groups) - mux setting - GPIO control (enable, disable, set direction) - pin configuration: - pull disable, up and down - any other option is treated as not supported. Reviewed-by: Linus Walleij Signed-off-by: Andy Shevchenko Acked-by: Mika Westerberg --- drivers/pinctrl/intel/pinctrl-lynxpoint.c | 315 +++++++++++++++++++++++++++++- 1 file changed, 314 insertions(+), 1 deletion(-) diff --git a/drivers/pinctrl/intel/pinctrl-lynxpoint.c b/drivers/pinctrl/intel/pinctrl-lynxpoint.c index 5a8c77c..c209def 100644 --- a/drivers/pinctrl/intel/pinctrl-lynxpoint.c +++ b/drivers/pinctrl/intel/pinctrl-lynxpoint.c @@ -146,6 +146,7 @@ static const struct intel_pinctrl_soc_data lptlp_soc_data = { /* Bitmapped register offsets */ #define LP_ACPI_OWNED 0x00 /* Bitmap, set by bios, 0: pin reserved for ACPI */ +#define LP_IRQ2IOXAPIC 0x10 /* Bitmap, set by bios, 1: pin routed to IOxAPIC */ #define LP_GC 0x7C /* set APIC IRQ to IRQ14 or IRQ15 for all pins */ #define LP_INT_STAT 0x80 #define LP_INT_ENABLE 0x90 @@ -166,7 +167,10 @@ static const struct intel_pinctrl_soc_data lptlp_soc_data = { /* LP_CONFIG2 reg bits */ #define GPINDIS_BIT BIT(2) /* disable input sensing */ -#define GPIWP_BIT (BIT(0) | BIT(1)) /* weak pull options */ +#define GPIWP_MASK GENMASK(1, 0) /* weak pull options */ +#define GPIWP_NONE 0 /* none */ +#define GPIWP_DOWN 1 /* weak pull down */ +#define GPIWP_UP 2 /* weak pull up */ /* * Lynxpoint gpios are controlled through both bitmapped registers and @@ -195,6 +199,8 @@ static const struct intel_pinctrl_soc_data lptlp_soc_data = { * ... * LP94_CONFIG1 (gpio 94) ... * LP94_CONFIG2 (gpio 94) ... + * + * IOxAPIC redirection map applies only for gpio 8-10, 13-14, 45-55. */ static struct intel_community *lp_get_community(struct intel_pinctrl *lg, @@ -246,6 +252,308 @@ static bool lp_gpio_acpi_use(struct intel_pinctrl *lg, unsigned int pin) return !(ioread32(acpi_use) & BIT(pin % 32)); } +static bool lp_gpio_ioxapic_use(struct gpio_chip *chip, unsigned int offset) +{ + void __iomem *ioxapic_use = lp_gpio_reg(chip, offset, LP_IRQ2IOXAPIC); + u32 value; + + value = ioread32(ioxapic_use); + + if (offset >= 8 && offset <= 10) + return !!(value & BIT(offset - 8 + 0)); + if (offset >= 13 && offset <= 14) + return !!(value & BIT(offset - 13 + 3)); + if (offset >= 45 && offset <= 55) + return !!(value & BIT(offset - 45 + 5)); + + return false; +} + +static int lp_get_groups_count(struct pinctrl_dev *pctldev) +{ + struct intel_pinctrl *lg = pinctrl_dev_get_drvdata(pctldev); + + return lg->soc->ngroups; +} + +static const char *lp_get_group_name(struct pinctrl_dev *pctldev, + unsigned int selector) +{ + struct intel_pinctrl *lg = pinctrl_dev_get_drvdata(pctldev); + + return lg->soc->groups[selector].name; +} + +static int lp_get_group_pins(struct pinctrl_dev *pctldev, + unsigned int selector, + const unsigned int **pins, + unsigned int *num_pins) +{ + struct intel_pinctrl *lg = pinctrl_dev_get_drvdata(pctldev); + + *pins = lg->soc->groups[selector].pins; + *num_pins = lg->soc->groups[selector].npins; + + return 0; +} + +static const struct pinctrl_ops lptlp_pinctrl_ops = { + .get_groups_count = lp_get_groups_count, + .get_group_name = lp_get_group_name, + .get_group_pins = lp_get_group_pins, +}; + +static int lp_get_functions_count(struct pinctrl_dev *pctldev) +{ + struct intel_pinctrl *lg = pinctrl_dev_get_drvdata(pctldev); + + return lg->soc->nfunctions; +} + +static const char *lp_get_function_name(struct pinctrl_dev *pctldev, + unsigned int selector) +{ + struct intel_pinctrl *lg = pinctrl_dev_get_drvdata(pctldev); + + return lg->soc->functions[selector].name; +} + +static int lp_get_function_groups(struct pinctrl_dev *pctldev, + unsigned int selector, + const char * const **groups, + unsigned int *num_groups) +{ + struct intel_pinctrl *lg = pinctrl_dev_get_drvdata(pctldev); + + *groups = lg->soc->functions[selector].groups; + *num_groups = lg->soc->functions[selector].ngroups; + + return 0; +} + +static int lp_pinmux_set_mux(struct pinctrl_dev *pctldev, + unsigned int function, unsigned int group) +{ + struct intel_pinctrl *lg = pinctrl_dev_get_drvdata(pctldev); + const struct intel_pingroup *grp = &lg->soc->groups[group]; + unsigned long flags; + int i; + + raw_spin_lock_irqsave(&lg->lock, flags); + + /* Now enable the mux setting for each pin in the group */ + for (i = 0; i < grp->npins; i++) { + void __iomem *reg = lp_gpio_reg(&lg->chip, grp->pins[i], LP_CONFIG1); + u32 value; + + value = ioread32(reg); + + value &= ~USE_SEL_MASK; + if (grp->modes) + value |= grp->modes[i]; + else + value |= grp->mode; + + iowrite32(value, reg); + } + + raw_spin_unlock_irqrestore(&lg->lock, flags); + + return 0; +} + +static int lp_gpio_request_enable(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned int pin) +{ + struct intel_pinctrl *lg = pinctrl_dev_get_drvdata(pctldev); + void __iomem *reg = lp_gpio_reg(&lg->chip, pin, LP_CONFIG1); + void __iomem *conf2 = lp_gpio_reg(&lg->chip, pin, LP_CONFIG2); + unsigned long flags; + u32 value; + + pm_runtime_get(lg->dev); + + raw_spin_lock_irqsave(&lg->lock, flags); + + /* + * Reconfigure pin to GPIO mode if needed and issue a warning, + * since we expect firmware to configure it properly. + */ + value = ioread32(reg); + if ((value & USE_SEL_MASK) != USE_SEL_GPIO) { + iowrite32((value & USE_SEL_MASK) | USE_SEL_GPIO, reg); + dev_warn(lg->dev, FW_BUG "pin %u forcibly reconfigured as GPIO\n", pin); + } + + /* Enable input sensing */ + iowrite32(ioread32(conf2) & ~GPINDIS_BIT, conf2); + + raw_spin_unlock_irqrestore(&lg->lock, flags); + + return 0; +} + +static void lp_gpio_disable_free(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned int pin) +{ + struct intel_pinctrl *lg = pinctrl_dev_get_drvdata(pctldev); + void __iomem *conf2 = lp_gpio_reg(&lg->chip, pin, LP_CONFIG2); + unsigned long flags; + + raw_spin_lock_irqsave(&lg->lock, flags); + + /* Disable input sensing */ + iowrite32(ioread32(conf2) | GPINDIS_BIT, conf2); + + raw_spin_unlock_irqrestore(&lg->lock, flags); + + pm_runtime_put(lg->dev); +} + +static int lp_gpio_set_direction(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned int pin, bool input) +{ + struct intel_pinctrl *lg = pinctrl_dev_get_drvdata(pctldev); + void __iomem *reg = lp_gpio_reg(&lg->chip, pin, LP_CONFIG1); + unsigned long flags; + u32 value; + + raw_spin_lock_irqsave(&lg->lock, flags); + + value = ioread32(reg); + value &= ~DIR_BIT; + if (input) { + value |= DIR_BIT; + } else { + /* + * Before making any direction modifications, do a check if GPIO + * is set for direct IRQ. On Lynxpoint, setting GPIO to output + * does not make sense, so let's at least warn the caller before + * they shoot themselves in the foot. + */ + WARN(lp_gpio_ioxapic_use(&lg->chip, pin), + "Potential Error: Setting GPIO to output with IOxAPIC redirection"); + } + iowrite32(value, reg); + + raw_spin_unlock_irqrestore(&lg->lock, flags); + + return 0; +} + +static const struct pinmux_ops lptlp_pinmux_ops = { + .get_functions_count = lp_get_functions_count, + .get_function_name = lp_get_function_name, + .get_function_groups = lp_get_function_groups, + .set_mux = lp_pinmux_set_mux, + .gpio_request_enable = lp_gpio_request_enable, + .gpio_disable_free = lp_gpio_disable_free, + .gpio_set_direction = lp_gpio_set_direction, +}; + +static int lp_pin_config_get(struct pinctrl_dev *pctldev, unsigned int pin, + unsigned long *config) +{ + struct intel_pinctrl *lg = pinctrl_dev_get_drvdata(pctldev); + void __iomem *conf2 = lp_gpio_reg(&lg->chip, pin, LP_CONFIG2); + enum pin_config_param param = pinconf_to_config_param(*config); + unsigned long flags; + u32 value, pull; + u16 arg = 0; + + raw_spin_lock_irqsave(&lg->lock, flags); + value = ioread32(conf2); + raw_spin_unlock_irqrestore(&lg->lock, flags); + + pull = value & GPIWP_MASK; + + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + if (pull) + return -EINVAL; + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + if (pull != GPIWP_DOWN) + return -EINVAL; + + arg = 1; + break; + case PIN_CONFIG_BIAS_PULL_UP: + if (pull != GPIWP_UP) + return -EINVAL; + + arg = 1; + break; + default: + return -ENOTSUPP; + } + + *config = pinconf_to_config_packed(param, arg); + + return 0; +} + +static int lp_pin_config_set(struct pinctrl_dev *pctldev, unsigned int pin, + unsigned long *configs, unsigned int num_configs) +{ + struct intel_pinctrl *lg = pinctrl_dev_get_drvdata(pctldev); + void __iomem *conf2 = lp_gpio_reg(&lg->chip, pin, LP_CONFIG2); + enum pin_config_param param; + unsigned long flags; + int i, ret = 0; + u32 value; + + raw_spin_lock_irqsave(&lg->lock, flags); + + value = ioread32(conf2); + + for (i = 0; i < num_configs; i++) { + param = pinconf_to_config_param(configs[i]); + + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + value &= ~GPIWP_MASK; + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + value &= ~GPIWP_MASK; + value |= GPIWP_DOWN; + break; + case PIN_CONFIG_BIAS_PULL_UP: + value &= ~GPIWP_MASK; + value |= GPIWP_UP; + break; + default: + ret = -ENOTSUPP; + } + + if (ret) + break; + } + + if (!ret) + iowrite32(value, conf2); + + raw_spin_unlock_irqrestore(&lg->lock, flags); + + return ret; +} + +static const struct pinconf_ops lptlp_pinconf_ops = { + .is_generic = true, + .pin_config_get = lp_pin_config_get, + .pin_config_set = lp_pin_config_set, +}; + +static const struct pinctrl_desc lptlp_pinctrl_desc = { + .pctlops = &lptlp_pinctrl_ops, + .pmxops = &lptlp_pinmux_ops, + .confops = &lptlp_pinconf_ops, + .owner = THIS_MODULE, +}; + static int lp_gpio_request(struct gpio_chip *chip, unsigned int offset) { struct intel_pinctrl *lg = gpiochip_get_data(chip); @@ -525,6 +833,11 @@ static int lp_gpio_probe(struct platform_device *pdev) if (!lg->communities) return -ENOMEM; + lg->pctldesc = lptlp_pinctrl_desc; + lg->pctldesc.name = dev_name(dev); + lg->pctldesc.pins = lg->soc->pins; + lg->pctldesc.npins = lg->soc->npins; + platform_set_drvdata(pdev, lg); io_rc = platform_get_resource(pdev, IORESOURCE_IO, 0); -- 2.7.4