summaryrefslogtreecommitdiff
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.c937
1 files changed, 530 insertions, 407 deletions
diff --git a/drivers/pinctrl/intel/pinctrl-intel.c b/drivers/pinctrl/intel/pinctrl-intel.c
index 85750974d182..cf9db8ac0f42 100644
--- a/drivers/pinctrl/intel/pinctrl-intel.c
+++ b/drivers/pinctrl/intel/pinctrl-intel.c
@@ -8,18 +8,25 @@
*/
#include <linux/acpi.h>
+#include <linux/cleanup.h>
+#include <linux/export.h>
#include <linux/gpio/driver.h>
#include <linux/interrupt.h>
#include <linux/log2.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/property.h>
+#include <linux/seq_file.h>
+#include <linux/string_helpers.h>
#include <linux/time.h>
-#include <linux/pinctrl/pinctrl.h>
-#include <linux/pinctrl/pinmux.h>
+#include <linux/pinctrl/consumer.h>
#include <linux/pinctrl/pinconf.h>
#include <linux/pinctrl/pinconf-generic.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+
+#include <linux/platform_data/x86/pwm-lpss.h>
#include "../core.h"
#include "pinctrl-intel.h"
@@ -46,14 +53,15 @@
#define PADOWN_MASK(p) (GENMASK(3, 0) << PADOWN_SHIFT(p))
#define PADOWN_GPP(p) ((p) / 8)
+#define PWMC 0x204
+
/* Offset from pad_regs */
#define PADCFG0 0x000
-#define PADCFG0_RXEVCFG_SHIFT 25
#define PADCFG0_RXEVCFG_MASK GENMASK(26, 25)
-#define PADCFG0_RXEVCFG_LEVEL 0
-#define PADCFG0_RXEVCFG_EDGE 1
-#define PADCFG0_RXEVCFG_DISABLED 2
-#define PADCFG0_RXEVCFG_EDGE_BOTH 3
+#define PADCFG0_RXEVCFG_LEVEL (0 << 25)
+#define PADCFG0_RXEVCFG_EDGE (1 << 25)
+#define PADCFG0_RXEVCFG_DISABLED (2 << 25)
+#define PADCFG0_RXEVCFG_EDGE_BOTH (3 << 25)
#define PADCFG0_PREGFRXSEL BIT(24)
#define PADCFG0_RXINV BIT(23)
#define PADCFG0_GPIROUTIOXAPIC BIT(20)
@@ -63,6 +71,12 @@
#define PADCFG0_PMODE_SHIFT 10
#define PADCFG0_PMODE_MASK GENMASK(13, 10)
#define PADCFG0_PMODE_GPIO 0
+#define PADCFG0_GPIODIS_SHIFT 8
+#define PADCFG0_GPIODIS_MASK GENMASK(9, 8)
+#define PADCFG0_GPIODIS_NONE 0
+#define PADCFG0_GPIODIS_OUTPUT 1
+#define PADCFG0_GPIODIS_INPUT 2
+#define PADCFG0_GPIODIS_FULL 3
#define PADCFG0_GPIORXDIS BIT(9)
#define PADCFG0_GPIOTXDIS BIT(8)
#define PADCFG0_GPIORXSTATE BIT(1)
@@ -72,15 +86,30 @@
#define PADCFG1_TERM_UP BIT(13)
#define PADCFG1_TERM_SHIFT 10
#define PADCFG1_TERM_MASK GENMASK(12, 10)
+/*
+ * Bit 0 Bit 1 Bit 2 Value, Ohms
+ *
+ * 0 0 0 -
+ * 0 0 1 20000
+ * 0 1 0 5000
+ * 0 1 1 ~4000
+ * 1 0 0 1000 (if supported)
+ * 1 0 1 ~952 (if supported)
+ * 1 1 0 ~833 (if supported)
+ * 1 1 1 ~800 (if supported)
+ */
#define PADCFG1_TERM_20K BIT(2)
#define PADCFG1_TERM_5K BIT(1)
+#define PADCFG1_TERM_4K (BIT(2) | BIT(1))
#define PADCFG1_TERM_1K BIT(0)
+#define PADCFG1_TERM_952 (BIT(2) | BIT(0))
#define PADCFG1_TERM_833 (BIT(1) | BIT(0))
+#define PADCFG1_TERM_800 (BIT(2) | BIT(1) | BIT(0))
#define PADCFG2 0x008
-#define PADCFG2_DEBEN BIT(0)
#define PADCFG2_DEBOUNCE_SHIFT 1
#define PADCFG2_DEBOUNCE_MASK GENMASK(4, 1)
+#define PADCFG2_DEBEN BIT(0)
#define DEBOUNCE_PERIOD_NSEC 31250
@@ -98,14 +127,30 @@ struct intel_community_context {
#define pin_to_padno(c, p) ((p) - (c)->pin_base)
#define padgroup_offset(g, p) ((p) - (g)->base)
-static struct intel_community *intel_get_community(struct intel_pinctrl *pctrl,
- unsigned int pin)
+#define for_each_intel_pin_community(pctrl, community) \
+ for (unsigned int __ci = 0; \
+ __ci < pctrl->ncommunities && (community = &pctrl->communities[__ci]); \
+ __ci++) \
+
+#define for_each_intel_community_pad_group(community, grp) \
+ for (unsigned int __gi = 0; \
+ __gi < community->ngpps && (grp = &community->gpps[__gi]); \
+ __gi++) \
+
+#define for_each_intel_pad_group(pctrl, community, grp) \
+ for_each_intel_pin_community(pctrl, community) \
+ for_each_intel_community_pad_group(community, grp)
+
+#define for_each_intel_gpio_group(pctrl, community, grp) \
+ for_each_intel_pad_group(pctrl, community, grp) \
+ if (grp->gpio_base == INTEL_GPIO_BASE_NOMAP) {} else
+
+const struct intel_community *intel_get_community(const struct intel_pinctrl *pctrl,
+ unsigned int pin)
{
- struct intel_community *community;
- int i;
+ const struct intel_community *community;
- for (i = 0; i < pctrl->ncommunities; i++) {
- community = &pctrl->communities[i];
+ for_each_intel_pin_community(pctrl, community) {
if (pin >= community->pin_base &&
pin < community->pin_base + community->npins)
return community;
@@ -114,16 +159,15 @@ static struct intel_community *intel_get_community(struct intel_pinctrl *pctrl,
dev_warn(pctrl->dev, "failed to find community for pin %u\n", pin);
return NULL;
}
+EXPORT_SYMBOL_NS_GPL(intel_get_community, "PINCTRL_INTEL");
static const struct intel_padgroup *
intel_community_get_padgroup(const struct intel_community *community,
unsigned int pin)
{
- int i;
-
- for (i = 0; i < community->ngpps; i++) {
- const struct intel_padgroup *padgrp = &community->gpps[i];
+ const struct intel_padgroup *padgrp;
+ for_each_intel_community_pad_group(community, padgrp) {
if (pin >= padgrp->base && pin < padgrp->base + padgrp->size)
return padgrp;
}
@@ -151,7 +195,7 @@ static void __iomem *intel_get_padcfg(struct intel_pinctrl *pctrl,
return community->pad_regs + reg + padno * nregs * 4;
}
-static bool intel_pad_owned_by_host(struct intel_pinctrl *pctrl, unsigned int pin)
+static bool intel_pad_owned_by_host(const struct intel_pinctrl *pctrl, unsigned int pin)
{
const struct intel_community *community;
const struct intel_padgroup *padgrp;
@@ -176,7 +220,7 @@ static bool intel_pad_owned_by_host(struct intel_pinctrl *pctrl, unsigned int pi
return !(readl(padown) & PADOWN_MASK(gpp_offset));
}
-static bool intel_pad_acpi_mode(struct intel_pinctrl *pctrl, unsigned int pin)
+static bool intel_pad_acpi_mode(const struct intel_pinctrl *pctrl, unsigned int pin)
{
const struct intel_community *community;
const struct intel_padgroup *padgrp;
@@ -202,7 +246,6 @@ static bool intel_pad_acpi_mode(struct intel_pinctrl *pctrl, unsigned int pin)
/**
* enum - Locking variants of the pad configuration
- *
* @PAD_UNLOCKED: pad is fully controlled by the configuration registers
* @PAD_LOCKED: pad configuration registers, except TX state, are locked
* @PAD_LOCKED_TX: pad configuration TX state is locked
@@ -219,9 +262,9 @@ enum {
PAD_LOCKED_FULL = PAD_LOCKED | PAD_LOCKED_TX,
};
-static int intel_pad_locked(struct intel_pinctrl *pctrl, unsigned int pin)
+static int intel_pad_locked(const struct intel_pinctrl *pctrl, unsigned int pin)
{
- struct intel_community *community;
+ const struct intel_community *community;
const struct intel_padgroup *padgrp;
unsigned int offset, gpp_offset;
u32 value;
@@ -257,40 +300,42 @@ static int intel_pad_locked(struct intel_pinctrl *pctrl, unsigned int pin)
return ret;
}
-static bool intel_pad_is_unlocked(struct intel_pinctrl *pctrl, unsigned int pin)
+static bool intel_pad_is_unlocked(const struct intel_pinctrl *pctrl, unsigned int pin)
{
return (intel_pad_locked(pctrl, pin) & PAD_LOCKED) == PAD_UNLOCKED;
}
-static bool intel_pad_usable(struct intel_pinctrl *pctrl, unsigned int pin)
+static bool intel_pad_usable(const struct intel_pinctrl *pctrl, unsigned int pin)
{
return intel_pad_owned_by_host(pctrl, pin) && intel_pad_is_unlocked(pctrl, pin);
}
-static int intel_get_groups_count(struct pinctrl_dev *pctldev)
+int intel_get_groups_count(struct pinctrl_dev *pctldev)
{
- struct intel_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
+ const struct intel_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
return pctrl->soc->ngroups;
}
+EXPORT_SYMBOL_NS_GPL(intel_get_groups_count, "PINCTRL_INTEL");
-static const char *intel_get_group_name(struct pinctrl_dev *pctldev,
- unsigned int group)
+const char *intel_get_group_name(struct pinctrl_dev *pctldev, unsigned int group)
{
- struct intel_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
+ const struct intel_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
- return pctrl->soc->groups[group].name;
+ return pctrl->soc->groups[group].grp.name;
}
+EXPORT_SYMBOL_NS_GPL(intel_get_group_name, "PINCTRL_INTEL");
-static int intel_get_group_pins(struct pinctrl_dev *pctldev, unsigned int group,
- const unsigned int **pins, unsigned int *npins)
+int intel_get_group_pins(struct pinctrl_dev *pctldev, unsigned int group,
+ const unsigned int **pins, unsigned int *npins)
{
- struct intel_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
+ const struct intel_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
- *pins = pctrl->soc->groups[group].pins;
- *npins = pctrl->soc->groups[group].npins;
+ *pins = pctrl->soc->groups[group].grp.pins;
+ *npins = pctrl->soc->groups[group].grp.npins;
return 0;
}
+EXPORT_SYMBOL_NS_GPL(intel_get_group_pins, "PINCTRL_INTEL");
static void intel_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s,
unsigned int pin)
@@ -350,95 +395,126 @@ static const struct pinctrl_ops intel_pinctrl_ops = {
.pin_dbg_show = intel_pin_dbg_show,
};
-static int intel_get_functions_count(struct pinctrl_dev *pctldev)
+int intel_get_functions_count(struct pinctrl_dev *pctldev)
{
- struct intel_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
+ const struct intel_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
return pctrl->soc->nfunctions;
}
+EXPORT_SYMBOL_NS_GPL(intel_get_functions_count, "PINCTRL_INTEL");
-static const char *intel_get_function_name(struct pinctrl_dev *pctldev,
- unsigned int function)
+const char *intel_get_function_name(struct pinctrl_dev *pctldev, unsigned int function)
{
- struct intel_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
+ const struct intel_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
- return pctrl->soc->functions[function].name;
+ return pctrl->soc->functions[function].func.name;
}
+EXPORT_SYMBOL_NS_GPL(intel_get_function_name, "PINCTRL_INTEL");
-static int intel_get_function_groups(struct pinctrl_dev *pctldev,
- unsigned int function,
- const char * const **groups,
- unsigned int * const ngroups)
+int intel_get_function_groups(struct pinctrl_dev *pctldev, unsigned int function,
+ const char * const **groups, unsigned int * const ngroups)
{
- struct intel_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
+ const struct intel_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
- *groups = pctrl->soc->functions[function].groups;
- *ngroups = pctrl->soc->functions[function].ngroups;
+ *groups = pctrl->soc->functions[function].func.groups;
+ *ngroups = pctrl->soc->functions[function].func.ngroups;
return 0;
}
+EXPORT_SYMBOL_NS_GPL(intel_get_function_groups, "PINCTRL_INTEL");
static int intel_pinmux_set_mux(struct pinctrl_dev *pctldev,
unsigned int function, unsigned int group)
{
struct intel_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
const struct intel_pingroup *grp = &pctrl->soc->groups[group];
- unsigned long flags;
int i;
- raw_spin_lock_irqsave(&pctrl->lock, flags);
+ guard(raw_spinlock_irqsave)(&pctrl->lock);
/*
* All pins in the groups needs to be accessible and writable
* before we can enable the mux for this group.
*/
- for (i = 0; i < grp->npins; i++) {
- if (!intel_pad_usable(pctrl, grp->pins[i])) {
- raw_spin_unlock_irqrestore(&pctrl->lock, flags);
+ for (i = 0; i < grp->grp.npins; i++) {
+ if (!intel_pad_usable(pctrl, grp->grp.pins[i]))
return -EBUSY;
- }
}
/* Now enable the mux setting for each pin in the group */
- for (i = 0; i < grp->npins; i++) {
+ for (i = 0; i < grp->grp.npins; i++) {
void __iomem *padcfg0;
- u32 value;
+ u32 value, pmode;
- padcfg0 = intel_get_padcfg(pctrl, grp->pins[i], PADCFG0);
- value = readl(padcfg0);
+ padcfg0 = intel_get_padcfg(pctrl, grp->grp.pins[i], PADCFG0);
+ value = readl(padcfg0);
value &= ~PADCFG0_PMODE_MASK;
if (grp->modes)
- value |= grp->modes[i] << PADCFG0_PMODE_SHIFT;
+ pmode = grp->modes[i];
else
- value |= grp->mode << PADCFG0_PMODE_SHIFT;
+ pmode = grp->mode;
+ value |= pmode << PADCFG0_PMODE_SHIFT;
writel(value, padcfg0);
}
- raw_spin_unlock_irqrestore(&pctrl->lock, flags);
-
return 0;
}
-static void __intel_gpio_set_direction(void __iomem *padcfg0, bool input)
+/**
+ * enum - Possible pad physical connections
+ * @PAD_CONNECT_NONE: pad is fully disconnected
+ * @PAD_CONNECT_INPUT: pad is in input only mode
+ * @PAD_CONNECT_OUTPUT: pad is in output only mode
+ * @PAD_CONNECT_FULL: pad is fully connected
+ */
+enum {
+ PAD_CONNECT_NONE = 0,
+ PAD_CONNECT_INPUT = 1,
+ PAD_CONNECT_OUTPUT = 2,
+ PAD_CONNECT_FULL = PAD_CONNECT_INPUT | PAD_CONNECT_OUTPUT,
+};
+
+static int __intel_gpio_get_direction(u32 value)
{
- u32 value;
+ switch ((value & PADCFG0_GPIODIS_MASK) >> PADCFG0_GPIODIS_SHIFT) {
+ case PADCFG0_GPIODIS_FULL:
+ return PAD_CONNECT_NONE;
+ case PADCFG0_GPIODIS_OUTPUT:
+ return PAD_CONNECT_INPUT;
+ case PADCFG0_GPIODIS_INPUT:
+ return PAD_CONNECT_OUTPUT;
+ case PADCFG0_GPIODIS_NONE:
+ return PAD_CONNECT_FULL;
+ default:
+ return -ENOTSUPP;
+ };
+}
- value = readl(padcfg0);
- if (input) {
+static u32 __intel_gpio_set_direction(u32 value, bool input, bool output)
+{
+ if (input)
value &= ~PADCFG0_GPIORXDIS;
- value |= PADCFG0_GPIOTXDIS;
- } else {
- value &= ~PADCFG0_GPIOTXDIS;
+ else
value |= PADCFG0_GPIORXDIS;
- }
- writel(value, padcfg0);
+
+ if (output)
+ value &= ~PADCFG0_GPIOTXDIS;
+ else
+ value |= PADCFG0_GPIOTXDIS;
+
+ return value;
+}
+
+static int __intel_gpio_get_gpio_mode(u32 value)
+{
+ return (value & PADCFG0_PMODE_MASK) >> PADCFG0_PMODE_SHIFT;
}
static int intel_gpio_get_gpio_mode(void __iomem *padcfg0)
{
- return (readl(padcfg0) & PADCFG0_PMODE_MASK) >> PADCFG0_PMODE_SHIFT;
+ return __intel_gpio_get_gpio_mode(readl(padcfg0));
}
static void intel_gpio_set_gpio_mode(void __iomem *padcfg0)
@@ -451,9 +527,8 @@ static void intel_gpio_set_gpio_mode(void __iomem *padcfg0)
value &= ~PADCFG0_PMODE_MASK;
value |= PADCFG0_PMODE_GPIO;
- /* Disable input and output buffers */
- value |= PADCFG0_GPIORXDIS;
- value |= PADCFG0_GPIOTXDIS;
+ /* Disable TX buffer and enable RX (this will be input) */
+ value = __intel_gpio_set_direction(value, true, false);
/* Disable SCI/SMI/NMI generation */
value &= ~(PADCFG0_GPIROUTIOXAPIC | PADCFG0_GPIROUTSCI);
@@ -468,21 +543,16 @@ static int intel_gpio_request_enable(struct pinctrl_dev *pctldev,
{
struct intel_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
void __iomem *padcfg0;
- unsigned long flags;
padcfg0 = intel_get_padcfg(pctrl, pin, PADCFG0);
- raw_spin_lock_irqsave(&pctrl->lock, flags);
+ guard(raw_spinlock_irqsave)(&pctrl->lock);
- if (!intel_pad_owned_by_host(pctrl, pin)) {
- raw_spin_unlock_irqrestore(&pctrl->lock, flags);
+ if (!intel_pad_owned_by_host(pctrl, pin))
return -EBUSY;
- }
- if (!intel_pad_is_unlocked(pctrl, pin)) {
- raw_spin_unlock_irqrestore(&pctrl->lock, flags);
+ if (!intel_pad_is_unlocked(pctrl, pin))
return 0;
- }
/*
* If pin is already configured in GPIO mode, we assume that
@@ -490,18 +560,11 @@ static int intel_gpio_request_enable(struct pinctrl_dev *pctldev,
* potential glitches on the pin. Otherwise, for the pin in
* alternative mode, consumer has to supply respective flags.
*/
- if (intel_gpio_get_gpio_mode(padcfg0) == PADCFG0_PMODE_GPIO) {
- raw_spin_unlock_irqrestore(&pctrl->lock, flags);
+ if (intel_gpio_get_gpio_mode(padcfg0) == PADCFG0_PMODE_GPIO)
return 0;
- }
intel_gpio_set_gpio_mode(padcfg0);
- /* Disable TX buffer and enable RX (this will be input) */
- __intel_gpio_set_direction(padcfg0, true);
-
- raw_spin_unlock_irqrestore(&pctrl->lock, flags);
-
return 0;
}
@@ -511,13 +574,18 @@ static int intel_gpio_set_direction(struct pinctrl_dev *pctldev,
{
struct intel_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
void __iomem *padcfg0;
- unsigned long flags;
+ u32 value;
padcfg0 = intel_get_padcfg(pctrl, pin, PADCFG0);
- raw_spin_lock_irqsave(&pctrl->lock, flags);
- __intel_gpio_set_direction(padcfg0, input);
- raw_spin_unlock_irqrestore(&pctrl->lock, flags);
+ guard(raw_spinlock_irqsave)(&pctrl->lock);
+
+ value = readl(padcfg0);
+ if (input)
+ value = __intel_gpio_set_direction(value, true, false);
+ else
+ value = __intel_gpio_set_direction(value, false, true);
+ writel(value, padcfg0);
return 0;
}
@@ -534,17 +602,13 @@ static const struct pinmux_ops intel_pinmux_ops = {
static int intel_config_get_pull(struct intel_pinctrl *pctrl, unsigned int pin,
enum pin_config_param param, u32 *arg)
{
- const struct intel_community *community;
void __iomem *padcfg1;
- unsigned long flags;
u32 value, term;
- community = intel_get_community(pctrl, pin);
padcfg1 = intel_get_padcfg(pctrl, pin, PADCFG1);
- raw_spin_lock_irqsave(&pctrl->lock, flags);
- value = readl(padcfg1);
- raw_spin_unlock_irqrestore(&pctrl->lock, flags);
+ scoped_guard(raw_spinlock_irqsave, &pctrl->lock)
+ value = readl(padcfg1);
term = (value & PADCFG1_TERM_MASK) >> PADCFG1_TERM_SHIFT;
@@ -565,6 +629,9 @@ static int intel_config_get_pull(struct intel_pinctrl *pctrl, unsigned int pin,
case PADCFG1_TERM_1K:
*arg = 1000;
break;
+ case PADCFG1_TERM_4K:
+ *arg = 4000;
+ break;
case PADCFG1_TERM_5K:
*arg = 5000;
break;
@@ -575,7 +642,9 @@ static int intel_config_get_pull(struct intel_pinctrl *pctrl, unsigned int pin,
break;
- case PIN_CONFIG_BIAS_PULL_DOWN:
+ case PIN_CONFIG_BIAS_PULL_DOWN: {
+ const struct intel_community *community = intel_get_community(pctrl, pin);
+
if (!term || value & PADCFG1_TERM_UP)
return -EINVAL;
@@ -590,6 +659,9 @@ static int intel_config_get_pull(struct intel_pinctrl *pctrl, unsigned int pin,
return -EINVAL;
*arg = 1000;
break;
+ case PADCFG1_TERM_4K:
+ *arg = 4000;
+ break;
case PADCFG1_TERM_5K:
*arg = 5000;
break;
@@ -599,6 +671,7 @@ static int intel_config_get_pull(struct intel_pinctrl *pctrl, unsigned int pin,
}
break;
+ }
default:
return -EINVAL;
@@ -607,11 +680,27 @@ static int intel_config_get_pull(struct intel_pinctrl *pctrl, unsigned int pin,
return 0;
}
+static int intel_config_get_high_impedance(struct intel_pinctrl *pctrl, unsigned int pin,
+ enum pin_config_param param, u32 *arg)
+{
+ void __iomem *padcfg0;
+ u32 value;
+
+ padcfg0 = intel_get_padcfg(pctrl, pin, PADCFG0);
+
+ scoped_guard(raw_spinlock_irqsave, &pctrl->lock)
+ value = readl(padcfg0);
+
+ if (__intel_gpio_get_direction(value) != PAD_CONNECT_NONE)
+ return -EINVAL;
+
+ return 0;
+}
+
static int intel_config_get_debounce(struct intel_pinctrl *pctrl, unsigned int pin,
enum pin_config_param param, u32 *arg)
{
void __iomem *padcfg2;
- unsigned long flags;
unsigned long v;
u32 value2;
@@ -619,9 +708,9 @@ static int intel_config_get_debounce(struct intel_pinctrl *pctrl, unsigned int p
if (!padcfg2)
return -ENOTSUPP;
- raw_spin_lock_irqsave(&pctrl->lock, flags);
- value2 = readl(padcfg2);
- raw_spin_unlock_irqrestore(&pctrl->lock, flags);
+ scoped_guard(raw_spinlock_irqsave, &pctrl->lock)
+ value2 = readl(padcfg2);
+
if (!(value2 & PADCFG2_DEBEN))
return -EINVAL;
@@ -651,6 +740,12 @@ static int intel_config_get(struct pinctrl_dev *pctldev, unsigned int pin,
return ret;
break;
+ case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
+ ret = intel_config_get_high_impedance(pctrl, pin, param, &arg);
+ if (ret)
+ return ret;
+ break;
+
case PIN_CONFIG_INPUT_DEBOUNCE:
ret = intel_config_get_debounce(pctrl, pin, param, &arg);
if (ret)
@@ -670,101 +765,113 @@ static int intel_config_set_pull(struct intel_pinctrl *pctrl, unsigned int pin,
{
unsigned int param = pinconf_to_config_param(config);
unsigned int arg = pinconf_to_config_argument(config);
- const struct intel_community *community;
+ u32 term = 0, up = 0, value;
void __iomem *padcfg1;
- unsigned long flags;
- int ret = 0;
- u32 value;
-
- community = intel_get_community(pctrl, pin);
- padcfg1 = intel_get_padcfg(pctrl, pin, PADCFG1);
-
- raw_spin_lock_irqsave(&pctrl->lock, flags);
-
- value = readl(padcfg1);
switch (param) {
case PIN_CONFIG_BIAS_DISABLE:
- value &= ~(PADCFG1_TERM_MASK | PADCFG1_TERM_UP);
break;
case PIN_CONFIG_BIAS_PULL_UP:
- value &= ~PADCFG1_TERM_MASK;
-
- value |= PADCFG1_TERM_UP;
-
- /* Set default strength value in case none is given */
- if (arg == 1)
- arg = 5000;
-
switch (arg) {
case 20000:
- value |= PADCFG1_TERM_20K << PADCFG1_TERM_SHIFT;
+ term = PADCFG1_TERM_20K;
break;
+ case 1: /* Set default strength value in case none is given */
case 5000:
- value |= PADCFG1_TERM_5K << PADCFG1_TERM_SHIFT;
+ term = PADCFG1_TERM_5K;
+ break;
+ case 4000:
+ term = PADCFG1_TERM_4K;
break;
case 1000:
- value |= PADCFG1_TERM_1K << PADCFG1_TERM_SHIFT;
+ term = PADCFG1_TERM_1K;
break;
case 833:
- value |= PADCFG1_TERM_833 << PADCFG1_TERM_SHIFT;
+ term = PADCFG1_TERM_833;
break;
default:
- ret = -EINVAL;
+ return -EINVAL;
}
+ up = PADCFG1_TERM_UP;
break;
- case PIN_CONFIG_BIAS_PULL_DOWN:
- value &= ~(PADCFG1_TERM_UP | PADCFG1_TERM_MASK);
-
- /* Set default strength value in case none is given */
- if (arg == 1)
- arg = 5000;
+ case PIN_CONFIG_BIAS_PULL_DOWN: {
+ const struct intel_community *community = intel_get_community(pctrl, pin);
switch (arg) {
case 20000:
- value |= PADCFG1_TERM_20K << PADCFG1_TERM_SHIFT;
+ term = PADCFG1_TERM_20K;
break;
+ case 1: /* Set default strength value in case none is given */
case 5000:
- value |= PADCFG1_TERM_5K << PADCFG1_TERM_SHIFT;
+ term = PADCFG1_TERM_5K;
+ break;
+ case 4000:
+ term = PADCFG1_TERM_4K;
break;
case 1000:
- if (!(community->features & PINCTRL_FEATURE_1K_PD)) {
- ret = -EINVAL;
- break;
- }
- value |= PADCFG1_TERM_1K << PADCFG1_TERM_SHIFT;
+ if (!(community->features & PINCTRL_FEATURE_1K_PD))
+ return -EINVAL;
+ term = PADCFG1_TERM_1K;
break;
case 833:
- if (!(community->features & PINCTRL_FEATURE_1K_PD)) {
- ret = -EINVAL;
- break;
- }
- value |= PADCFG1_TERM_833 << PADCFG1_TERM_SHIFT;
+ if (!(community->features & PINCTRL_FEATURE_1K_PD))
+ return -EINVAL;
+ term = PADCFG1_TERM_833;
break;
default:
- ret = -EINVAL;
+ return -EINVAL;
}
break;
}
- if (!ret)
- writel(value, padcfg1);
+ default:
+ return -EINVAL;
+ }
- raw_spin_unlock_irqrestore(&pctrl->lock, flags);
+ padcfg1 = intel_get_padcfg(pctrl, pin, PADCFG1);
- return ret;
+ guard(raw_spinlock_irqsave)(&pctrl->lock);
+
+ value = readl(padcfg1);
+ value = (value & ~PADCFG1_TERM_MASK) | (term << PADCFG1_TERM_SHIFT);
+ value = (value & ~PADCFG1_TERM_UP) | up;
+ writel(value, padcfg1);
+
+ return 0;
+}
+
+static void intel_gpio_set_high_impedance(struct intel_pinctrl *pctrl, unsigned int pin)
+{
+ void __iomem *padcfg0;
+ u32 value;
+
+ padcfg0 = intel_get_padcfg(pctrl, pin, PADCFG0);
+
+ guard(raw_spinlock_irqsave)(&pctrl->lock);
+
+ value = readl(padcfg0);
+ value = __intel_gpio_set_direction(value, false, false);
+ writel(value, padcfg0);
}
static int intel_config_set_debounce(struct intel_pinctrl *pctrl,
unsigned int pin, unsigned int debounce)
{
void __iomem *padcfg0, *padcfg2;
- unsigned long flags;
u32 value0, value2;
+ unsigned long v;
+
+ if (debounce) {
+ v = order_base_2(debounce * NSEC_PER_USEC / DEBOUNCE_PERIOD_NSEC);
+ if (v < 3 || v > 15)
+ return -EINVAL;
+ } else {
+ v = 0;
+ }
padcfg2 = intel_get_padcfg(pctrl, pin, PADCFG2);
if (!padcfg2)
@@ -772,35 +879,25 @@ static int intel_config_set_debounce(struct intel_pinctrl *pctrl,
padcfg0 = intel_get_padcfg(pctrl, pin, PADCFG0);
- raw_spin_lock_irqsave(&pctrl->lock, flags);
+ guard(raw_spinlock_irqsave)(&pctrl->lock);
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 * NSEC_PER_USEC / DEBOUNCE_PERIOD_NSEC);
- if (v < 3 || v > 15) {
- raw_spin_unlock_irqrestore(&pctrl->lock, flags);
- return -EINVAL;
- }
-
+ value2 = (value2 & ~PADCFG2_DEBOUNCE_MASK) | (v << PADCFG2_DEBOUNCE_SHIFT);
+ if (v) {
/* Enable glitch filter and debouncer */
value0 |= PADCFG0_PREGFRXSEL;
- value2 |= v << PADCFG2_DEBOUNCE_SHIFT;
value2 |= PADCFG2_DEBEN;
+ } else {
+ /* Disable glitch filter and debouncer */
+ value0 &= ~PADCFG0_PREGFRXSEL;
+ value2 &= ~PADCFG2_DEBEN;
}
writel(value0, padcfg0);
writel(value2, padcfg2);
- raw_spin_unlock_irqrestore(&pctrl->lock, flags);
-
return 0;
}
@@ -823,6 +920,10 @@ static int intel_config_set(struct pinctrl_dev *pctldev, unsigned int pin,
return ret;
break;
+ case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
+ intel_gpio_set_high_impedance(pctrl, pin);
+ break;
+
case PIN_CONFIG_INPUT_DEBOUNCE:
ret = intel_config_set_debounce(pctrl, pin,
pinconf_to_config_argument(configs[i]));
@@ -861,35 +962,25 @@ static const struct pinctrl_desc intel_pinctrl_desc = {
* When coming through gpiolib irqchip, the GPIO offset is not
* automatically translated to pinctrl pin number. This function can be
* used to find out the corresponding pinctrl pin.
+ *
+ * Return: a pin number and pointers to the community and pad group, which
+ * the pin belongs to, or negative error code if translation can't be done.
*/
-static int intel_gpio_to_pin(struct intel_pinctrl *pctrl, unsigned int offset,
+static int intel_gpio_to_pin(const struct intel_pinctrl *pctrl, unsigned int offset,
const struct intel_community **community,
const struct intel_padgroup **padgrp)
{
- int i;
+ const struct intel_community *comm;
+ const struct intel_padgroup *grp;
- for (i = 0; i < pctrl->ncommunities; i++) {
- const struct intel_community *comm = &pctrl->communities[i];
- int j;
+ for_each_intel_gpio_group(pctrl, comm, grp) {
+ if (offset >= grp->gpio_base && offset < grp->gpio_base + grp->size) {
+ if (community)
+ *community = comm;
+ if (padgrp)
+ *padgrp = grp;
- for (j = 0; j < comm->ngpps; j++) {
- const struct intel_padgroup *pgrp = &comm->gpps[j];
-
- if (pgrp->gpio_base == INTEL_GPIO_BASE_NOMAP)
- continue;
-
- if (offset >= pgrp->gpio_base &&
- offset < pgrp->gpio_base + pgrp->size) {
- int pin;
-
- pin = pgrp->base + offset - pgrp->gpio_base;
- if (community)
- *community = comm;
- if (padgrp)
- *padgrp = pgrp;
-
- return pin;
- }
+ return grp->base + offset - grp->gpio_base;
}
}
@@ -902,8 +993,10 @@ static int intel_gpio_to_pin(struct intel_pinctrl *pctrl, unsigned int offset,
* @pin: pin number
*
* Translate the pin number of pinctrl to GPIO offset
+ *
+ * Return: a GPIO offset, or negative error code if translation can't be done.
*/
-static __maybe_unused int intel_pin_to_gpio(struct intel_pinctrl *pctrl, int pin)
+static int intel_pin_to_gpio(const struct intel_pinctrl *pctrl, int pin)
{
const struct intel_community *community;
const struct intel_padgroup *padgrp;
@@ -935,43 +1028,43 @@ static int intel_gpio_get(struct gpio_chip *chip, unsigned int offset)
return -EINVAL;
padcfg0 = readl(reg);
- if (!(padcfg0 & PADCFG0_GPIOTXDIS))
+ if (__intel_gpio_get_direction(padcfg0) & PAD_CONNECT_OUTPUT)
return !!(padcfg0 & PADCFG0_GPIOTXSTATE);
return !!(padcfg0 & PADCFG0_GPIORXSTATE);
}
-static void intel_gpio_set(struct gpio_chip *chip, unsigned int offset,
- int value)
+static int intel_gpio_set(struct gpio_chip *chip, unsigned int offset,
+ int value)
{
struct intel_pinctrl *pctrl = gpiochip_get_data(chip);
- unsigned long flags;
void __iomem *reg;
u32 padcfg0;
int pin;
pin = intel_gpio_to_pin(pctrl, offset, NULL, NULL);
if (pin < 0)
- return;
+ return -EINVAL;
reg = intel_get_padcfg(pctrl, pin, PADCFG0);
if (!reg)
- return;
+ return -EINVAL;
+
+ guard(raw_spinlock_irqsave)(&pctrl->lock);
- raw_spin_lock_irqsave(&pctrl->lock, flags);
padcfg0 = readl(reg);
if (value)
padcfg0 |= PADCFG0_GPIOTXSTATE;
else
padcfg0 &= ~PADCFG0_GPIOTXSTATE;
writel(padcfg0, reg);
- raw_spin_unlock_irqrestore(&pctrl->lock, flags);
+
+ return 0;
}
static int intel_gpio_get_direction(struct gpio_chip *chip, unsigned int offset)
{
struct intel_pinctrl *pctrl = gpiochip_get_data(chip);
- unsigned long flags;
void __iomem *reg;
u32 padcfg0;
int pin;
@@ -984,28 +1077,33 @@ static int intel_gpio_get_direction(struct gpio_chip *chip, unsigned int offset)
if (!reg)
return -EINVAL;
- raw_spin_lock_irqsave(&pctrl->lock, flags);
- padcfg0 = readl(reg);
- raw_spin_unlock_irqrestore(&pctrl->lock, flags);
+ scoped_guard(raw_spinlock_irqsave, &pctrl->lock)
+ padcfg0 = readl(reg);
+
if (padcfg0 & PADCFG0_PMODE_MASK)
return -EINVAL;
- if (padcfg0 & PADCFG0_GPIOTXDIS)
- return GPIO_LINE_DIRECTION_IN;
+ if (__intel_gpio_get_direction(padcfg0) & PAD_CONNECT_OUTPUT)
+ return GPIO_LINE_DIRECTION_OUT;
- return GPIO_LINE_DIRECTION_OUT;
+ return GPIO_LINE_DIRECTION_IN;
}
static int intel_gpio_direction_input(struct gpio_chip *chip, unsigned int offset)
{
- return pinctrl_gpio_direction_input(chip->base + offset);
+ return pinctrl_gpio_direction_input(chip, offset);
}
static int intel_gpio_direction_output(struct gpio_chip *chip, unsigned int offset,
int value)
{
- intel_gpio_set(chip, offset, value);
- return pinctrl_gpio_direction_output(chip->base + offset);
+ int ret;
+
+ ret = intel_gpio_set(chip, offset, value);
+ if (ret)
+ return ret;
+
+ return pinctrl_gpio_direction_output(chip, offset);
}
static const struct gpio_chip intel_gpio_chip = {
@@ -1030,30 +1128,30 @@ static void intel_gpio_irq_ack(struct irq_data *d)
pin = intel_gpio_to_pin(pctrl, irqd_to_hwirq(d), &community, &padgrp);
if (pin >= 0) {
- unsigned int gpp, gpp_offset, is_offset;
+ unsigned int gpp, gpp_offset;
+ void __iomem *is;
gpp = padgrp->reg_num;
gpp_offset = padgroup_offset(padgrp, pin);
- is_offset = community->is_offset + gpp * 4;
- raw_spin_lock(&pctrl->lock);
- writel(BIT(gpp_offset), community->regs + is_offset);
- raw_spin_unlock(&pctrl->lock);
+ is = community->regs + community->is_offset + gpp * 4;
+
+ guard(raw_spinlock)(&pctrl->lock);
+
+ writel(BIT(gpp_offset), is);
}
}
-static void intel_gpio_irq_mask_unmask(struct irq_data *d, bool mask)
+static void intel_gpio_irq_mask_unmask(struct gpio_chip *gc, irq_hw_number_t hwirq, bool mask)
{
- struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct intel_pinctrl *pctrl = gpiochip_get_data(gc);
const struct intel_community *community;
const struct intel_padgroup *padgrp;
int pin;
- pin = intel_gpio_to_pin(pctrl, irqd_to_hwirq(d), &community, &padgrp);
+ pin = intel_gpio_to_pin(pctrl, hwirq, &community, &padgrp);
if (pin >= 0) {
unsigned int gpp, gpp_offset;
- unsigned long flags;
void __iomem *reg, *is;
u32 value;
@@ -1063,7 +1161,7 @@ static void intel_gpio_irq_mask_unmask(struct irq_data *d, bool mask)
reg = community->regs + community->ie_offset + gpp * 4;
is = community->regs + community->is_offset + gpp * 4;
- raw_spin_lock_irqsave(&pctrl->lock, flags);
+ guard(raw_spinlock_irqsave)(&pctrl->lock);
/* Clear interrupt status first to avoid unexpected interrupt */
writel(BIT(gpp_offset), is);
@@ -1074,18 +1172,25 @@ static void intel_gpio_irq_mask_unmask(struct irq_data *d, bool mask)
else
value |= BIT(gpp_offset);
writel(value, reg);
- raw_spin_unlock_irqrestore(&pctrl->lock, flags);
}
}
static void intel_gpio_irq_mask(struct irq_data *d)
{
- intel_gpio_irq_mask_unmask(d, true);
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ irq_hw_number_t hwirq = irqd_to_hwirq(d);
+
+ intel_gpio_irq_mask_unmask(gc, hwirq, true);
+ gpiochip_disable_irq(gc, hwirq);
}
static void intel_gpio_irq_unmask(struct irq_data *d)
{
- intel_gpio_irq_mask_unmask(d, false);
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ irq_hw_number_t hwirq = irqd_to_hwirq(d);
+
+ gpiochip_enable_irq(gc, hwirq);
+ intel_gpio_irq_mask_unmask(gc, hwirq, false);
}
static int intel_gpio_irq_type(struct irq_data *d, unsigned int type)
@@ -1093,9 +1198,8 @@ static int intel_gpio_irq_type(struct irq_data *d, unsigned int type)
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct intel_pinctrl *pctrl = gpiochip_get_data(gc);
unsigned int pin = intel_gpio_to_pin(pctrl, irqd_to_hwirq(d), NULL, NULL);
- unsigned long flags;
+ u32 rxevcfg, rxinv, value;
void __iomem *reg;
- u32 value;
reg = intel_get_padcfg(pctrl, pin, PADCFG0);
if (!reg)
@@ -1111,31 +1215,32 @@ static int intel_gpio_irq_type(struct irq_data *d, unsigned int type)
return -EPERM;
}
- raw_spin_lock_irqsave(&pctrl->lock, flags);
-
- intel_gpio_set_gpio_mode(reg);
-
- /* Disable TX buffer and enable RX (this will be input) */
- __intel_gpio_set_direction(reg, true);
-
- value = readl(reg);
-
- value &= ~(PADCFG0_RXEVCFG_MASK | PADCFG0_RXINV);
-
if ((type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH) {
- value |= PADCFG0_RXEVCFG_EDGE_BOTH << PADCFG0_RXEVCFG_SHIFT;
+ rxevcfg = PADCFG0_RXEVCFG_EDGE_BOTH;
} else if (type & IRQ_TYPE_EDGE_FALLING) {
- value |= PADCFG0_RXEVCFG_EDGE << PADCFG0_RXEVCFG_SHIFT;
- value |= PADCFG0_RXINV;
+ rxevcfg = PADCFG0_RXEVCFG_EDGE;
} else if (type & IRQ_TYPE_EDGE_RISING) {
- value |= PADCFG0_RXEVCFG_EDGE << PADCFG0_RXEVCFG_SHIFT;
+ rxevcfg = PADCFG0_RXEVCFG_EDGE;
} else if (type & IRQ_TYPE_LEVEL_MASK) {
- if (type & IRQ_TYPE_LEVEL_LOW)
- value |= PADCFG0_RXINV;
+ rxevcfg = PADCFG0_RXEVCFG_LEVEL;
} else {
- value |= PADCFG0_RXEVCFG_DISABLED << PADCFG0_RXEVCFG_SHIFT;
+ rxevcfg = PADCFG0_RXEVCFG_DISABLED;
}
+ if (type == IRQ_TYPE_EDGE_FALLING || type == IRQ_TYPE_LEVEL_LOW)
+ rxinv = PADCFG0_RXINV;
+ else
+ rxinv = 0;
+
+ guard(raw_spinlock_irqsave)(&pctrl->lock);
+
+ intel_gpio_set_gpio_mode(reg);
+
+ value = readl(reg);
+
+ value = (value & ~PADCFG0_RXEVCFG_MASK) | rxevcfg;
+ value = (value & ~PADCFG0_RXINV) | rxinv;
+
writel(value, reg);
if (type & IRQ_TYPE_EDGE_BOTH)
@@ -1143,8 +1248,6 @@ static int intel_gpio_irq_type(struct irq_data *d, unsigned int type)
else if (type & IRQ_TYPE_LEVEL_MASK)
irq_set_handler_locked(d, handle_level_irq);
- raw_spin_unlock_irqrestore(&pctrl->lock, flags);
-
return 0;
}
@@ -1159,119 +1262,126 @@ static int intel_gpio_irq_wake(struct irq_data *d, unsigned int on)
else
disable_irq_wake(pctrl->irq);
- dev_dbg(pctrl->dev, "%sable wake for pin %u\n", on ? "en" : "dis", pin);
+ dev_dbg(pctrl->dev, "%s wake for pin %u\n", str_enable_disable(on), pin);
return 0;
}
-static int intel_gpio_community_irq_handler(struct intel_pinctrl *pctrl,
- const struct intel_community *community)
+static const struct irq_chip intel_gpio_irq_chip = {
+ .name = "intel-gpio",
+ .irq_ack = intel_gpio_irq_ack,
+ .irq_mask = intel_gpio_irq_mask,
+ .irq_unmask = intel_gpio_irq_unmask,
+ .irq_set_type = intel_gpio_irq_type,
+ .irq_set_wake = intel_gpio_irq_wake,
+ .flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_IMMUTABLE,
+ GPIOCHIP_IRQ_RESOURCE_HELPERS,
+};
+
+static irqreturn_t intel_gpio_irq(int irq, void *data)
{
- struct gpio_chip *gc = &pctrl->chip;
- unsigned int gpp;
+ const struct intel_community *community;
+ const struct intel_padgroup *padgrp;
+ struct intel_pinctrl *pctrl = data;
int ret = 0;
- for (gpp = 0; gpp < community->ngpps; gpp++) {
- const struct intel_padgroup *padgrp = &community->gpps[gpp];
- unsigned long pending, enabled, gpp_offset;
+ /* Need to check all communities for pending interrupts */
+ for_each_intel_pad_group(pctrl, community, padgrp) {
+ struct gpio_chip *gc = &pctrl->chip;
+ unsigned long pending, enabled;
+ unsigned int gpp, gpp_offset;
+ void __iomem *reg, *is;
- raw_spin_lock(&pctrl->lock);
+ gpp = padgrp->reg_num;
- pending = readl(community->regs + community->is_offset +
- padgrp->reg_num * 4);
- enabled = readl(community->regs + community->ie_offset +
- padgrp->reg_num * 4);
+ reg = community->regs + community->ie_offset + gpp * 4;
+ is = community->regs + community->is_offset + gpp * 4;
- raw_spin_unlock(&pctrl->lock);
+ scoped_guard(raw_spinlock, &pctrl->lock) {
+ pending = readl(is);
+ enabled = readl(reg);
+ }
/* Only interrupts that are enabled */
pending &= enabled;
- for_each_set_bit(gpp_offset, &pending, padgrp->size) {
- unsigned int irq;
-
- irq = irq_find_mapping(gc->irq.domain,
- padgrp->gpio_base + gpp_offset);
- generic_handle_irq(irq);
- }
+ for_each_set_bit(gpp_offset, &pending, padgrp->size)
+ generic_handle_domain_irq(gc->irq.domain, padgrp->gpio_base + gpp_offset);
ret += pending ? 1 : 0;
}
- return ret;
+ return IRQ_RETVAL(ret);
}
-static irqreturn_t intel_gpio_irq(int irq, void *data)
+static void intel_gpio_irq_init(struct intel_pinctrl *pctrl)
{
const struct intel_community *community;
- struct intel_pinctrl *pctrl = data;
- unsigned int i;
- int ret = 0;
- /* Need to check all communities for pending interrupts */
- for (i = 0; i < pctrl->ncommunities; i++) {
- community = &pctrl->communities[i];
- ret += intel_gpio_community_irq_handler(pctrl, community);
- }
+ for_each_intel_pin_community(pctrl, community) {
+ void __iomem *reg, *is;
+ unsigned int gpp;
- return IRQ_RETVAL(ret);
+ for (gpp = 0; gpp < community->ngpps; gpp++) {
+ reg = community->regs + community->ie_offset + gpp * 4;
+ is = community->regs + community->is_offset + gpp * 4;
+
+ /* Mask and clear all interrupts */
+ writel(0, reg);
+ writel(0xffff, is);
+ }
+ }
}
-static int intel_gpio_add_community_ranges(struct intel_pinctrl *pctrl,
- const struct intel_community *community)
+static int intel_gpio_irq_init_hw(struct gpio_chip *gc)
{
- int ret = 0, i;
-
- for (i = 0; i < community->ngpps; i++) {
- const struct intel_padgroup *gpp = &community->gpps[i];
-
- if (gpp->gpio_base == INTEL_GPIO_BASE_NOMAP)
- continue;
+ struct intel_pinctrl *pctrl = gpiochip_get_data(gc);
- ret = gpiochip_add_pin_range(&pctrl->chip, dev_name(pctrl->dev),
- gpp->gpio_base, gpp->base,
- gpp->size);
- if (ret)
- return ret;
- }
+ /*
+ * Make sure the interrupt lines are in a proper state before
+ * further configuration.
+ */
+ intel_gpio_irq_init(pctrl);
- return ret;
+ return 0;
}
-static int intel_gpio_add_pin_ranges(struct gpio_chip *gc)
+/**
+ * intel_gpio_add_pin_ranges - add GPIO pin ranges for all groups in all communities
+ * @gc: GPIO chip structure
+ *
+ * This function iterates over all communities and all groups and adds the respective
+ * GPIO pin ranges, so the GPIO library will correctly map a GPIO offset to a pin number.
+ *
+ * Return: 0, or negative error code if range can't be added.
+ */
+int intel_gpio_add_pin_ranges(struct gpio_chip *gc)
{
struct intel_pinctrl *pctrl = gpiochip_get_data(gc);
- int ret, i;
-
- for (i = 0; i < pctrl->ncommunities; i++) {
- struct intel_community *community = &pctrl->communities[i];
+ const struct intel_community *community;
+ const struct intel_padgroup *grp;
+ int ret;
- ret = intel_gpio_add_community_ranges(pctrl, community);
- if (ret) {
- dev_err(pctrl->dev, "failed to add GPIO pin range\n");
- return ret;
- }
+ for_each_intel_gpio_group(pctrl, community, grp) {
+ ret = gpiochip_add_pin_range(&pctrl->chip, dev_name(pctrl->dev),
+ grp->gpio_base, grp->base,
+ grp->size);
+ if (ret)
+ return dev_err_probe(pctrl->dev, ret, "failed to add GPIO pin range\n");
}
return 0;
}
+EXPORT_SYMBOL_NS_GPL(intel_gpio_add_pin_ranges, "PINCTRL_INTEL");
static unsigned int intel_gpio_ngpio(const struct intel_pinctrl *pctrl)
{
const struct intel_community *community;
+ const struct intel_padgroup *grp;
unsigned int ngpio = 0;
- int i, j;
-
- for (i = 0; i < pctrl->ncommunities; i++) {
- community = &pctrl->communities[i];
- for (j = 0; j < community->ngpps; j++) {
- const struct intel_padgroup *gpp = &community->gpps[j];
- if (gpp->gpio_base == INTEL_GPIO_BASE_NOMAP)
- continue;
-
- if (gpp->gpio_base + gpp->size > ngpio)
- ngpio = gpp->gpio_base + gpp->size;
- }
+ for_each_intel_gpio_group(pctrl, community, grp) {
+ if (grp->gpio_base + grp->size > ngpio)
+ ngpio = grp->gpio_base + grp->size;
}
return ngpio;
@@ -1292,15 +1402,6 @@ static int intel_gpio_probe(struct intel_pinctrl *pctrl, int irq)
pctrl->chip.add_pin_ranges = intel_gpio_add_pin_ranges;
pctrl->irq = irq;
- /* Setup IRQ chip */
- pctrl->irqchip.name = dev_name(pctrl->dev);
- pctrl->irqchip.irq_ack = intel_gpio_irq_ack;
- pctrl->irqchip.irq_mask = intel_gpio_irq_mask;
- pctrl->irqchip.irq_unmask = intel_gpio_irq_unmask;
- pctrl->irqchip.irq_set_type = intel_gpio_irq_type;
- pctrl->irqchip.irq_set_wake = intel_gpio_irq_wake;
- pctrl->irqchip.flags = IRQCHIP_MASK_ON_SUSPEND;
-
/*
* On some platforms several GPIO controllers share the same interrupt
* line.
@@ -1308,24 +1409,22 @@ static int intel_gpio_probe(struct intel_pinctrl *pctrl, int irq)
ret = devm_request_irq(pctrl->dev, irq, intel_gpio_irq,
IRQF_SHARED | IRQF_NO_THREAD,
dev_name(pctrl->dev), pctrl);
- if (ret) {
- dev_err(pctrl->dev, "failed to request interrupt\n");
- return ret;
- }
+ if (ret)
+ return dev_err_probe(pctrl->dev, ret, "failed to request interrupt\n");
+ /* Setup IRQ chip */
girq = &pctrl->chip.irq;
- girq->chip = &pctrl->irqchip;
+ gpio_irq_chip_set_chip(girq, &intel_gpio_irq_chip);
/* This will let us handle the IRQ in the driver */
girq->parent_handler = NULL;
girq->num_parents = 0;
girq->default_type = IRQ_TYPE_NONE;
girq->handler = handle_bad_irq;
+ girq->init_hw = intel_gpio_irq_init_hw;
ret = devm_gpiochip_add_data(pctrl->dev, &pctrl->chip, pctrl);
- if (ret) {
- dev_err(pctrl->dev, "failed to register gpiochip\n");
- return ret;
- }
+ if (ret)
+ return dev_err_probe(pctrl->dev, ret, "failed to register gpiochip\n");
return 0;
}
@@ -1344,7 +1443,7 @@ static int intel_pinctrl_add_padgroups_by_gpps(struct intel_pinctrl *pctrl,
for (i = 0; i < ngpps; i++) {
gpps[i] = community->gpps[i];
- if (gpps[i].size > 32)
+ if (gpps[i].size > INTEL_PINCTRL_MAX_GPP_SIZE)
return -EINVAL;
/* Special treatment for GPIO base */
@@ -1362,7 +1461,7 @@ static int intel_pinctrl_add_padgroups_by_gpps(struct intel_pinctrl *pctrl,
}
gpps[i].padown_num = padown_num;
- padown_num += DIV_ROUND_UP(gpps[i].size * 4, 32);
+ padown_num += DIV_ROUND_UP(gpps[i].size * 4, INTEL_PINCTRL_MAX_GPP_SIZE);
}
community->gpps = gpps;
@@ -1378,7 +1477,7 @@ static int intel_pinctrl_add_padgroups_by_size(struct intel_pinctrl *pctrl,
unsigned int padown_num = 0;
size_t i, ngpps = DIV_ROUND_UP(npins, community->gpp_size);
- if (community->gpp_size > 32)
+ if (community->gpp_size > INTEL_PINCTRL_MAX_GPP_SIZE)
return -EINVAL;
gpps = devm_kcalloc(pctrl->dev, ngpps, sizeof(*gpps), GFP_KERNEL);
@@ -1396,14 +1495,7 @@ static int intel_pinctrl_add_padgroups_by_size(struct intel_pinctrl *pctrl,
gpps[i].gpio_base = gpps[i].base;
gpps[i].padown_num = padown_num;
- /*
- * In older hardware the number of padown registers per
- * group is fixed regardless of the group size.
- */
- if (community->gpp_num_padown_regs)
- padown_num += community->gpp_num_padown_regs;
- else
- padown_num += DIV_ROUND_UP(gpps[i].size * 4, 32);
+ padown_num += community->gpp_num_padown_regs;
}
community->ngpps = ngpps;
@@ -1456,17 +1548,38 @@ static int intel_pinctrl_pm_init(struct intel_pinctrl *pctrl)
return 0;
}
-static int intel_pinctrl_probe(struct platform_device *pdev,
- const struct intel_pinctrl_soc_data *soc_data)
+static int intel_pinctrl_probe_pwm(struct intel_pinctrl *pctrl,
+ struct intel_community *community)
{
+ static const struct pwm_lpss_boardinfo info = {
+ .clk_rate = 19200000,
+ .npwm = 1,
+ .base_unit_bits = 22,
+ };
+ struct pwm_chip *chip;
+
+ if (!(community->features & PINCTRL_FEATURE_PWM))
+ return 0;
+
+ if (!IS_REACHABLE(CONFIG_PWM_LPSS))
+ return 0;
+
+ chip = devm_pwm_lpss_probe(pctrl->dev, community->regs + PWMC, &info);
+ return PTR_ERR_OR_ZERO(chip);
+}
+
+int intel_pinctrl_probe(struct platform_device *pdev,
+ const struct intel_pinctrl_soc_data *soc_data)
+{
+ struct device *dev = &pdev->dev;
struct intel_pinctrl *pctrl;
int i, ret, irq;
- pctrl = devm_kzalloc(&pdev->dev, sizeof(*pctrl), GFP_KERNEL);
+ pctrl = devm_kzalloc(dev, sizeof(*pctrl), GFP_KERNEL);
if (!pctrl)
return -ENOMEM;
- pctrl->dev = &pdev->dev;
+ pctrl->dev = dev;
pctrl->soc = soc_data;
raw_spin_lock_init(&pctrl->lock);
@@ -1475,8 +1588,8 @@ static int intel_pinctrl_probe(struct platform_device *pdev,
* to the registers.
*/
pctrl->ncommunities = pctrl->soc->ncommunities;
- pctrl->communities = devm_kcalloc(&pdev->dev, pctrl->ncommunities,
- sizeof(*pctrl->communities), GFP_KERNEL);
+ pctrl->communities = devm_kmemdup_array(dev, pctrl->soc->communities, pctrl->ncommunities,
+ sizeof(*pctrl->soc->communities), GFP_KERNEL);
if (!pctrl->communities)
return -ENOMEM;
@@ -1486,8 +1599,6 @@ static int intel_pinctrl_probe(struct platform_device *pdev,
u32 offset;
u32 value;
- *community = pctrl->soc->communities[i];
-
regs = devm_platform_ioremap_resource(pdev, community->barno);
if (IS_ERR(regs))
return PTR_ERR(regs);
@@ -1527,7 +1638,7 @@ static int intel_pinctrl_probe(struct platform_device *pdev,
offset = (value & CAPLIST_NEXT_MASK) >> CAPLIST_NEXT_SHIFT;
} while (offset);
- dev_dbg(&pdev->dev, "Community%d features: %#08x\n", i, community->features);
+ dev_dbg(dev, "Community%d features: %#08x\n", i, community->features);
/* Read offset of the pad configuration registers */
offset = readl(regs + PADBAR);
@@ -1541,6 +1652,10 @@ static int intel_pinctrl_probe(struct platform_device *pdev,
ret = intel_pinctrl_add_padgroups_by_size(pctrl, community);
if (ret)
return ret;
+
+ ret = intel_pinctrl_probe_pwm(pctrl, community);
+ if (ret)
+ return ret;
}
irq = platform_get_irq(pdev, 0);
@@ -1552,16 +1667,13 @@ static int intel_pinctrl_probe(struct platform_device *pdev,
return ret;
pctrl->pctldesc = intel_pinctrl_desc;
- pctrl->pctldesc.name = dev_name(&pdev->dev);
+ pctrl->pctldesc.name = dev_name(dev);
pctrl->pctldesc.pins = pctrl->soc->pins;
pctrl->pctldesc.npins = pctrl->soc->npins;
- pctrl->pctldev = devm_pinctrl_register(&pdev->dev, &pctrl->pctldesc,
- pctrl);
- if (IS_ERR(pctrl->pctldev)) {
- dev_err(&pdev->dev, "failed to register pinctrl driver\n");
- return PTR_ERR(pctrl->pctldev);
- }
+ pctrl->pctldev = devm_pinctrl_register(dev, &pctrl->pctldesc, pctrl);
+ if (IS_ERR(pctrl->pctldev))
+ return dev_err_probe(dev, PTR_ERR(pctrl->pctldev), "failed to register pinctrl\n");
ret = intel_gpio_probe(pctrl, irq);
if (ret)
@@ -1571,6 +1683,7 @@ static int intel_pinctrl_probe(struct platform_device *pdev,
return 0;
}
+EXPORT_SYMBOL_NS_GPL(intel_pinctrl_probe, "PINCTRL_INTEL");
int intel_pinctrl_probe_by_hid(struct platform_device *pdev)
{
@@ -1582,7 +1695,7 @@ int intel_pinctrl_probe_by_hid(struct platform_device *pdev)
return intel_pinctrl_probe(pdev, data);
}
-EXPORT_SYMBOL_GPL(intel_pinctrl_probe_by_hid);
+EXPORT_SYMBOL_NS_GPL(intel_pinctrl_probe_by_hid, "PINCTRL_INTEL");
int intel_pinctrl_probe_by_uid(struct platform_device *pdev)
{
@@ -1594,26 +1707,24 @@ int intel_pinctrl_probe_by_uid(struct platform_device *pdev)
return intel_pinctrl_probe(pdev, data);
}
-EXPORT_SYMBOL_GPL(intel_pinctrl_probe_by_uid);
+EXPORT_SYMBOL_NS_GPL(intel_pinctrl_probe_by_uid, "PINCTRL_INTEL");
const struct intel_pinctrl_soc_data *intel_pinctrl_get_soc_data(struct platform_device *pdev)
{
- const struct intel_pinctrl_soc_data *data = NULL;
- const struct intel_pinctrl_soc_data **table;
- struct acpi_device *adev;
- unsigned int i;
+ const struct intel_pinctrl_soc_data * const *table;
+ const struct intel_pinctrl_soc_data *data;
+ struct device *dev = &pdev->dev;
- adev = ACPI_COMPANION(&pdev->dev);
- if (adev) {
- const void *match = device_get_match_data(&pdev->dev);
+ table = device_get_match_data(dev);
+ if (table) {
+ struct acpi_device *adev = ACPI_COMPANION(dev);
+ unsigned int i;
- table = (const struct intel_pinctrl_soc_data **)match;
for (i = 0; table[i]; i++) {
- if (!strcmp(adev->pnp.unique_id, table[i]->uid)) {
- data = table[i];
+ if (acpi_dev_uid_match(adev, table[i]->uid))
break;
- }
}
+ data = table[i];
} else {
const struct platform_device_id *id;
@@ -1621,18 +1732,25 @@ const struct intel_pinctrl_soc_data *intel_pinctrl_get_soc_data(struct platform_
if (!id)
return ERR_PTR(-ENODEV);
- table = (const struct intel_pinctrl_soc_data **)id->driver_data;
+ table = (const struct intel_pinctrl_soc_data * const *)id->driver_data;
data = table[pdev->id];
}
return data ?: ERR_PTR(-ENODATA);
}
-EXPORT_SYMBOL_GPL(intel_pinctrl_get_soc_data);
+EXPORT_SYMBOL_NS_GPL(intel_pinctrl_get_soc_data, "PINCTRL_INTEL");
+
+static bool __intel_gpio_is_direct_irq(u32 value)
+{
+ return (value & PADCFG0_GPIROUTIOXAPIC) &&
+ (__intel_gpio_get_direction(value) == PAD_CONNECT_INPUT) &&
+ (__intel_gpio_get_gpio_mode(value) == PADCFG0_PMODE_GPIO);
+}
-#ifdef CONFIG_PM_SLEEP
static bool intel_pinctrl_should_save(struct intel_pinctrl *pctrl, unsigned int pin)
{
const struct pin_desc *pd = pin_desc_get(pctrl->pctldev, pin);
+ u32 value;
if (!pd || !intel_pad_usable(pctrl, pin))
return false;
@@ -1647,10 +1765,28 @@ static bool intel_pinctrl_should_save(struct intel_pinctrl *pctrl, unsigned int
gpiochip_line_is_irq(&pctrl->chip, intel_pin_to_gpio(pctrl, pin)))
return true;
+ /*
+ * The firmware on some systems may configure GPIO pins to be
+ * an interrupt source in so called "direct IRQ" mode. In such
+ * cases the GPIO controller driver has no idea if those pins
+ * are being used or not. At the same time, there is a known bug
+ * in the firmwares that don't restore the pin settings correctly
+ * after suspend, i.e. by an unknown reason the Rx value becomes
+ * inverted.
+ *
+ * Hence, let's save and restore the pins that are configured
+ * as GPIOs in the input mode with GPIROUTIOXAPIC bit set.
+ *
+ * See https://bugzilla.kernel.org/show_bug.cgi?id=214749.
+ */
+ value = readl(intel_get_padcfg(pctrl, pin, PADCFG0));
+ if (__intel_gpio_is_direct_irq(value))
+ return true;
+
return false;
}
-int intel_pinctrl_suspend_noirq(struct device *dev)
+static int intel_pinctrl_suspend_noirq(struct device *dev)
{
struct intel_pinctrl *pctrl = dev_get_drvdata(dev);
struct intel_community_context *communities;
@@ -1693,27 +1829,6 @@ int intel_pinctrl_suspend_noirq(struct device *dev)
return 0;
}
-EXPORT_SYMBOL_GPL(intel_pinctrl_suspend_noirq);
-
-static void intel_gpio_irq_init(struct intel_pinctrl *pctrl)
-{
- size_t i;
-
- for (i = 0; i < pctrl->ncommunities; i++) {
- const struct intel_community *community;
- void __iomem *base;
- unsigned int gpp;
-
- community = &pctrl->communities[i];
- base = community->regs;
-
- for (gpp = 0; gpp < community->ngpps; gpp++) {
- /* Mask and clear all interrupts */
- writel(0, base + community->ie_offset + gpp * 4);
- writel(0xffff, base + community->is_offset + gpp * 4);
- }
- }
-}
static bool intel_gpio_update_reg(void __iomem *reg, u32 mask, u32 value)
{
@@ -1780,7 +1895,7 @@ static void intel_restore_padcfg(struct intel_pinctrl *pctrl, unsigned int pin,
dev_dbg(dev, "restored pin %u padcfg%u %#08x\n", pin, n, readl(padcfg));
}
-int intel_pinctrl_resume_noirq(struct device *dev)
+static int intel_pinctrl_resume_noirq(struct device *dev)
{
struct intel_pinctrl *pctrl = dev_get_drvdata(dev);
const struct intel_community_context *communities;
@@ -1794,7 +1909,12 @@ int intel_pinctrl_resume_noirq(struct device *dev)
for (i = 0; i < pctrl->soc->npins; i++) {
const struct pinctrl_pin_desc *desc = &pctrl->soc->pins[i];
- if (!intel_pinctrl_should_save(pctrl, desc->number))
+ if (!(intel_pinctrl_should_save(pctrl, desc->number) ||
+ /*
+ * If the firmware mangled the register contents too much,
+ * check the saved value for the Direct IRQ mode.
+ */
+ __intel_gpio_is_direct_irq(pads[i].padcfg0)))
continue;
intel_restore_padcfg(pctrl, desc->number, PADCFG0, pads[i].padcfg0);
@@ -1819,10 +1939,13 @@ int intel_pinctrl_resume_noirq(struct device *dev)
return 0;
}
-EXPORT_SYMBOL_GPL(intel_pinctrl_resume_noirq);
-#endif
+
+EXPORT_NS_GPL_DEV_SLEEP_PM_OPS(intel_pinctrl_pm_ops, PINCTRL_INTEL) = {
+ NOIRQ_SYSTEM_SLEEP_PM_OPS(intel_pinctrl_suspend_noirq, intel_pinctrl_resume_noirq)
+};
MODULE_AUTHOR("Mathias Nyman <mathias.nyman@linux.intel.com>");
MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>");
MODULE_DESCRIPTION("Intel pinctrl/GPIO core driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS("PWM_LPSS");