summaryrefslogtreecommitdiffstats
path: root/drivers/pinctrl/intel/pinctrl-intel.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pinctrl/intel/pinctrl-intel.c')
-rw-r--r--drivers/pinctrl/intel/pinctrl-intel.c171
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;