diff options
Diffstat (limited to 'drivers/pinctrl/intel/pinctrl-intel.c')
-rw-r--r-- | drivers/pinctrl/intel/pinctrl-intel.c | 171 |
1 files changed, 150 insertions, 21 deletions
diff --git a/drivers/pinctrl/intel/pinctrl-intel.c b/drivers/pinctrl/intel/pinctrl-intel.c index 6df35dcb29ae..592b465e981e 100644 --- a/drivers/pinctrl/intel/pinctrl-intel.c +++ b/drivers/pinctrl/intel/pinctrl-intel.c @@ -13,6 +13,7 @@ #include <linux/module.h> #include <linux/interrupt.h> #include <linux/gpio/driver.h> +#include <linux/log2.h> #include <linux/platform_device.h> #include <linux/pinctrl/pinctrl.h> #include <linux/pinctrl/pinmux.h> @@ -23,6 +24,10 @@ #include "pinctrl-intel.h" /* Offset from regs */ +#define REVID 0x000 +#define REVID_SHIFT 16 +#define REVID_MASK GENMASK(31, 16) + #define PADBAR 0x00c #define GPI_IS 0x100 #define GPI_GPE_STS 0x140 @@ -41,6 +46,7 @@ #define PADCFG0_RXEVCFG_EDGE 1 #define PADCFG0_RXEVCFG_DISABLED 2 #define PADCFG0_RXEVCFG_EDGE_BOTH 3 +#define PADCFG0_PREGFRXSEL BIT(24) #define PADCFG0_RXINV BIT(23) #define PADCFG0_GPIROUTIOXAPIC BIT(20) #define PADCFG0_GPIROUTSCI BIT(19) @@ -62,9 +68,17 @@ #define PADCFG1_TERM_5K 2 #define PADCFG1_TERM_1K 1 +#define PADCFG2 0x008 +#define PADCFG2_DEBEN BIT(0) +#define PADCFG2_DEBOUNCE_SHIFT 1 +#define PADCFG2_DEBOUNCE_MASK GENMASK(4, 1) + +#define DEBOUNCE_PERIOD 31250 /* ns */ + struct intel_pad_context { u32 padcfg0; u32 padcfg1; + u32 padcfg2; }; struct intel_community_context { @@ -126,13 +140,19 @@ static void __iomem *intel_get_padcfg(struct intel_pinctrl *pctrl, unsigned pin, { const struct intel_community *community; unsigned padno; + size_t nregs; community = intel_get_community(pctrl, pin); if (!community) return NULL; padno = pin_to_padno(community, pin); - return community->pad_regs + reg + padno * 8; + nregs = (community->features & PINCTRL_FEATURE_DEBOUNCE) ? 4 : 2; + + if (reg == PADCFG2 && !(community->features & PINCTRL_FEATURE_DEBOUNCE)) + return NULL; + + return community->pad_regs + reg + padno * nregs * 4; } static bool intel_pad_owned_by_host(struct intel_pinctrl *pctrl, unsigned pin) @@ -244,6 +264,7 @@ static void intel_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s, unsigned pin) { struct intel_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + void __iomem *padcfg; u32 cfg0, cfg1, mode; bool locked, acpi; @@ -263,6 +284,11 @@ static void intel_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s, seq_printf(s, "0x%08x 0x%08x", cfg0, cfg1); + /* Dump the additional PADCFG registers if available */ + padcfg = intel_get_padcfg(pctrl, pin, PADCFG2); + if (padcfg) + seq_printf(s, " 0x%08x", readl(padcfg)); + locked = intel_pad_locked(pctrl, pin); acpi = intel_pad_acpi_mode(pctrl, pin); @@ -432,12 +458,14 @@ static int intel_config_get(struct pinctrl_dev *pctldev, unsigned pin, { struct intel_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); enum pin_config_param param = pinconf_to_config_param(*config); + const struct intel_community *community; u32 value, term; - u16 arg = 0; + u32 arg = 0; if (!intel_pad_owned_by_host(pctrl, pin)) return -ENOTSUPP; + community = intel_get_community(pctrl, pin); value = readl(intel_get_padcfg(pctrl, pin, PADCFG1)); term = (value & PADCFG1_TERM_MASK) >> PADCFG1_TERM_SHIFT; @@ -473,6 +501,11 @@ static int intel_config_get(struct pinctrl_dev *pctldev, unsigned pin, return -EINVAL; switch (term) { + case PADCFG1_TERM_1K: + if (!(community->features & PINCTRL_FEATURE_1K_PD)) + return -EINVAL; + arg = 1000; + break; case PADCFG1_TERM_5K: arg = 5000; break; @@ -483,6 +516,24 @@ static int intel_config_get(struct pinctrl_dev *pctldev, unsigned pin, break; + case PIN_CONFIG_INPUT_DEBOUNCE: { + void __iomem *padcfg2; + u32 v; + + padcfg2 = intel_get_padcfg(pctrl, pin, PADCFG2); + if (!padcfg2) + return -ENOTSUPP; + + v = readl(padcfg2); + if (!(v & PADCFG2_DEBEN)) + return -EINVAL; + + v = (v & PADCFG2_DEBOUNCE_MASK) >> PADCFG2_DEBOUNCE_SHIFT; + arg = BIT(v) * DEBOUNCE_PERIOD / 1000; + + break; + } + default: return -ENOTSUPP; } @@ -496,6 +547,7 @@ static int intel_config_set_pull(struct intel_pinctrl *pctrl, unsigned pin, { unsigned param = pinconf_to_config_param(config); unsigned arg = pinconf_to_config_argument(config); + const struct intel_community *community; void __iomem *padcfg1; unsigned long flags; int ret = 0; @@ -503,6 +555,7 @@ static int intel_config_set_pull(struct intel_pinctrl *pctrl, unsigned pin, raw_spin_lock_irqsave(&pctrl->lock, flags); + community = intel_get_community(pctrl, pin); padcfg1 = intel_get_padcfg(pctrl, pin, PADCFG1); value = readl(padcfg1); @@ -545,6 +598,13 @@ static int intel_config_set_pull(struct intel_pinctrl *pctrl, unsigned pin, case 5000: value |= PADCFG1_TERM_5K << PADCFG1_TERM_SHIFT; break; + case 1000: + if (!(community->features & PINCTRL_FEATURE_1K_PD)) { + ret = -EINVAL; + break; + } + value |= PADCFG1_TERM_1K << PADCFG1_TERM_SHIFT; + break; default: ret = -EINVAL; } @@ -560,6 +620,53 @@ static int intel_config_set_pull(struct intel_pinctrl *pctrl, unsigned pin, return ret; } +static int intel_config_set_debounce(struct intel_pinctrl *pctrl, unsigned pin, + unsigned debounce) +{ + void __iomem *padcfg0, *padcfg2; + unsigned long flags; + u32 value0, value2; + int ret = 0; + + padcfg2 = intel_get_padcfg(pctrl, pin, PADCFG2); + if (!padcfg2) + return -ENOTSUPP; + + padcfg0 = intel_get_padcfg(pctrl, pin, PADCFG0); + + raw_spin_lock_irqsave(&pctrl->lock, flags); + + value0 = readl(padcfg0); + value2 = readl(padcfg2); + + /* Disable glitch filter and debouncer */ + value0 &= ~PADCFG0_PREGFRXSEL; + value2 &= ~(PADCFG2_DEBEN | PADCFG2_DEBOUNCE_MASK); + + if (debounce) { + unsigned long v; + + v = order_base_2(debounce * 1000 / DEBOUNCE_PERIOD); + if (v < 3 || v > 15) { + ret = -EINVAL; + goto exit_unlock; + } else { + /* Enable glitch filter and debouncer */ + value0 |= PADCFG0_PREGFRXSEL; + value2 |= v << PADCFG2_DEBOUNCE_SHIFT; + value2 |= PADCFG2_DEBEN; + } + } + + writel(value0, padcfg0); + writel(value2, padcfg2); + +exit_unlock: + raw_spin_unlock_irqrestore(&pctrl->lock, flags); + + return ret; +} + static int intel_config_set(struct pinctrl_dev *pctldev, unsigned pin, unsigned long *configs, unsigned nconfigs) { @@ -579,6 +686,13 @@ static int intel_config_set(struct pinctrl_dev *pctldev, unsigned pin, return ret; break; + case PIN_CONFIG_INPUT_DEBOUNCE: + ret = intel_config_set_debounce(pctrl, pin, + pinconf_to_config_argument(configs[i])); + if (ret) + return ret; + break; + default: return -ENOTSUPP; } @@ -653,6 +767,7 @@ static const struct gpio_chip intel_gpio_chip = { .direction_output = intel_gpio_direction_output, .get = intel_gpio_get, .set = intel_gpio_set, + .set_config = gpiochip_generic_config, }; static void intel_gpio_irq_ack(struct irq_data *d) @@ -892,7 +1007,7 @@ static int intel_gpio_probe(struct intel_pinctrl *pctrl, int irq) pctrl->chip.base = -1; pctrl->irq = irq; - ret = gpiochip_add_data(&pctrl->chip, pctrl); + ret = devm_gpiochip_add_data(pctrl->dev, &pctrl->chip, pctrl); if (ret) { dev_err(pctrl->dev, "failed to register gpiochip\n"); return ret; @@ -902,7 +1017,7 @@ static int intel_gpio_probe(struct intel_pinctrl *pctrl, int irq) 0, 0, pctrl->soc->npins); if (ret) { dev_err(pctrl->dev, "failed to add GPIO pin range\n"); - goto fail; + return ret; } /* @@ -915,24 +1030,19 @@ static int intel_gpio_probe(struct intel_pinctrl *pctrl, int irq) dev_name(pctrl->dev), pctrl); if (ret) { dev_err(pctrl->dev, "failed to request interrupt\n"); - goto fail; + return ret; } ret = gpiochip_irqchip_add(&pctrl->chip, &intel_gpio_irqchip, 0, handle_bad_irq, IRQ_TYPE_NONE); if (ret) { dev_err(pctrl->dev, "failed to add irqchip\n"); - goto fail; + return ret; } gpiochip_set_chained_irqchip(&pctrl->chip, &intel_gpio_irqchip, irq, NULL); return 0; - -fail: - gpiochip_remove(&pctrl->chip); - - return ret; } static int intel_pinctrl_pm_init(struct intel_pinctrl *pctrl) @@ -1013,6 +1123,20 @@ int intel_pinctrl_probe(struct platform_device *pdev, if (IS_ERR(regs)) return PTR_ERR(regs); + /* + * Determine community features based on the revision if + * not specified already. + */ + if (!community->features) { + u32 rev; + + rev = (readl(regs + REVID) & REVID_MASK) >> REVID_SHIFT; + if (rev >= 0x94) { + community->features |= PINCTRL_FEATURE_DEBOUNCE; + community->features |= PINCTRL_FEATURE_1K_PD; + } + } + /* Read offset of the pad configuration registers */ padbar = readl(regs + PADBAR); @@ -1054,16 +1178,6 @@ int intel_pinctrl_probe(struct platform_device *pdev, } EXPORT_SYMBOL_GPL(intel_pinctrl_probe); -int intel_pinctrl_remove(struct platform_device *pdev) -{ - struct intel_pinctrl *pctrl = platform_get_drvdata(pdev); - - gpiochip_remove(&pctrl->chip); - - return 0; -} -EXPORT_SYMBOL_GPL(intel_pinctrl_remove); - #ifdef CONFIG_PM_SLEEP static bool intel_pinctrl_should_save(struct intel_pinctrl *pctrl, unsigned pin) { @@ -1096,6 +1210,7 @@ int intel_pinctrl_suspend(struct device *dev) pads = pctrl->context.pads; for (i = 0; i < pctrl->soc->npins; i++) { const struct pinctrl_pin_desc *desc = &pctrl->soc->pins[i]; + void __iomem *padcfg; u32 val; if (!intel_pinctrl_should_save(pctrl, desc->number)) @@ -1105,6 +1220,10 @@ int intel_pinctrl_suspend(struct device *dev) pads[i].padcfg0 = val & ~PADCFG0_GPIORXSTATE; val = readl(intel_get_padcfg(pctrl, desc->number, PADCFG1)); pads[i].padcfg1 = val; + + padcfg = intel_get_padcfg(pctrl, desc->number, PADCFG2); + if (padcfg) + pads[i].padcfg2 = readl(padcfg); } communities = pctrl->context.communities; @@ -1177,6 +1296,16 @@ int intel_pinctrl_resume(struct device *dev) dev_dbg(dev, "restored pin %u padcfg1 %#08x\n", desc->number, readl(padcfg)); } + + padcfg = intel_get_padcfg(pctrl, desc->number, PADCFG2); + if (padcfg) { + val = readl(padcfg); + if (val != pads[i].padcfg2) { + writel(pads[i].padcfg2, padcfg); + dev_dbg(dev, "restored pin %u padcfg2 %#08x\n", + desc->number, readl(padcfg)); + } + } } communities = pctrl->context.communities; |