diff --git a/drivers/pinctrl/intel/pinctrl-intel.c b/drivers/pinctrl/intel/pinctrl-intel.c
index 592b465e981e..dd2e2ee7fc92 100644
--- a/drivers/pinctrl/intel/pinctrl-intel.c
+++ b/drivers/pinctrl/intel/pinctrl-intel.c
@@ -117,6 +117,7 @@ struct intel_pinctrl {
 };
 
 #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 pin)
@@ -135,6 +136,22 @@ static struct intel_community *intel_get_community(struct intel_pinctrl *pctrl,
 	return NULL;
 }
 
+static const struct intel_padgroup *
+intel_community_get_padgroup(const struct intel_community *community,
+			     unsigned pin)
+{
+	int i;
+
+	for (i = 0; i < community->ngpps; i++) {
+		const struct intel_padgroup *padgrp = &community->gpps[i];
+
+		if (pin >= padgrp->base && pin < padgrp->base + padgrp->size)
+			return padgrp;
+	}
+
+	return NULL;
+}
+
 static void __iomem *intel_get_padcfg(struct intel_pinctrl *pctrl, unsigned pin,
 				      unsigned reg)
 {
@@ -158,7 +175,8 @@ static void __iomem *intel_get_padcfg(struct intel_pinctrl *pctrl, unsigned pin,
 static bool intel_pad_owned_by_host(struct intel_pinctrl *pctrl, unsigned pin)
 {
 	const struct intel_community *community;
-	unsigned padno, gpp, offset, group;
+	const struct intel_padgroup *padgrp;
+	unsigned gpp, offset, gpp_offset;
 	void __iomem *padown;
 
 	community = intel_get_community(pctrl, pin);
@@ -167,19 +185,23 @@ static bool intel_pad_owned_by_host(struct intel_pinctrl *pctrl, unsigned pin)
 	if (!community->padown_offset)
 		return true;
 
-	padno = pin_to_padno(community, pin);
-	group = padno / community->gpp_size;
-	gpp = PADOWN_GPP(padno % community->gpp_size);
-	offset = community->padown_offset + 0x10 * group + gpp * 4;
+	padgrp = intel_community_get_padgroup(community, pin);
+	if (!padgrp)
+		return false;
+
+	gpp_offset = padgroup_offset(padgrp, pin);
+	gpp = PADOWN_GPP(gpp_offset);
+	offset = community->padown_offset + padgrp->padown_num * 4 + gpp * 4;
 	padown = community->regs + offset;
 
-	return !(readl(padown) & PADOWN_MASK(padno));
+	return !(readl(padown) & PADOWN_MASK(gpp_offset));
 }
 
 static bool intel_pad_acpi_mode(struct intel_pinctrl *pctrl, unsigned pin)
 {
 	const struct intel_community *community;
-	unsigned padno, gpp, offset;
+	const struct intel_padgroup *padgrp;
+	unsigned offset, gpp_offset;
 	void __iomem *hostown;
 
 	community = intel_get_community(pctrl, pin);
@@ -188,18 +210,22 @@ static bool intel_pad_acpi_mode(struct intel_pinctrl *pctrl, unsigned pin)
 	if (!community->hostown_offset)
 		return false;
 
-	padno = pin_to_padno(community, pin);
-	gpp = padno / community->gpp_size;
-	offset = community->hostown_offset + gpp * 4;
+	padgrp = intel_community_get_padgroup(community, pin);
+	if (!padgrp)
+		return true;
+
+	gpp_offset = padgroup_offset(padgrp, pin);
+	offset = community->hostown_offset + padgrp->reg_num * 4;
 	hostown = community->regs + offset;
 
-	return !(readl(hostown) & BIT(padno % community->gpp_size));
+	return !(readl(hostown) & BIT(gpp_offset));
 }
 
 static bool intel_pad_locked(struct intel_pinctrl *pctrl, unsigned pin)
 {
 	struct intel_community *community;
-	unsigned padno, gpp, offset;
+	const struct intel_padgroup *padgrp;
+	unsigned offset, gpp_offset;
 	u32 value;
 
 	community = intel_get_community(pctrl, pin);
@@ -208,22 +234,25 @@ static bool intel_pad_locked(struct intel_pinctrl *pctrl, unsigned pin)
 	if (!community->padcfglock_offset)
 		return false;
 
-	padno = pin_to_padno(community, pin);
-	gpp = padno / community->gpp_size;
+	padgrp = intel_community_get_padgroup(community, pin);
+	if (!padgrp)
+		return true;
+
+	gpp_offset = padgroup_offset(padgrp, pin);
 
 	/*
 	 * If PADCFGLOCK and PADCFGLOCKTX bits are both clear for this pad,
 	 * the pad is considered unlocked. Any other case means that it is
 	 * either fully or partially locked and we don't touch it.
 	 */
-	offset = community->padcfglock_offset + gpp * 8;
+	offset = community->padcfglock_offset + padgrp->reg_num * 8;
 	value = readl(community->regs + offset);
-	if (value & BIT(pin % community->gpp_size))
+	if (value & BIT(gpp_offset))
 		return true;
 
-	offset = community->padcfglock_offset + 4 + gpp * 8;
+	offset = community->padcfglock_offset + 4 + padgrp->reg_num * 8;
 	value = readl(community->regs + offset);
-	if (value & BIT(pin % community->gpp_size))
+	if (value & BIT(gpp_offset))
 		return true;
 
 	return false;
@@ -777,18 +806,22 @@ static void intel_gpio_irq_ack(struct irq_data *d)
 	const struct intel_community *community;
 	unsigned pin = irqd_to_hwirq(d);
 
-	raw_spin_lock(&pctrl->lock);
-
 	community = intel_get_community(pctrl, pin);
 	if (community) {
-		unsigned padno = pin_to_padno(community, pin);
-		unsigned gpp_offset = padno % community->gpp_size;
-		unsigned gpp = padno / community->gpp_size;
+		const struct intel_padgroup *padgrp;
+		unsigned gpp, gpp_offset;
+
+		padgrp = intel_community_get_padgroup(community, pin);
+		if (!padgrp)
+			return;
 
+		gpp = padgrp->reg_num;
+		gpp_offset = padgroup_offset(padgrp, pin);
+
+		raw_spin_lock(&pctrl->lock);
 		writel(BIT(gpp_offset), community->regs + GPI_IS + gpp * 4);
+		raw_spin_unlock(&pctrl->lock);
 	}
-
-	raw_spin_unlock(&pctrl->lock);
 }
 
 static void intel_gpio_irq_enable(struct irq_data *d)
@@ -797,27 +830,30 @@ static void intel_gpio_irq_enable(struct irq_data *d)
 	struct intel_pinctrl *pctrl = gpiochip_get_data(gc);
 	const struct intel_community *community;
 	unsigned pin = irqd_to_hwirq(d);
-	unsigned long flags;
-
-	raw_spin_lock_irqsave(&pctrl->lock, flags);
 
 	community = intel_get_community(pctrl, pin);
 	if (community) {
-		unsigned padno = pin_to_padno(community, pin);
-		unsigned gpp_size = community->gpp_size;
-		unsigned gpp_offset = padno % gpp_size;
-		unsigned gpp = padno / gpp_size;
+		const struct intel_padgroup *padgrp;
+		unsigned gpp, gpp_offset;
+		unsigned long flags;
 		u32 value;
 
+		padgrp = intel_community_get_padgroup(community, pin);
+		if (!padgrp)
+			return;
+
+		gpp = padgrp->reg_num;
+		gpp_offset = padgroup_offset(padgrp, pin);
+
+		raw_spin_lock_irqsave(&pctrl->lock, flags);
 		/* Clear interrupt status first to avoid unexpected interrupt */
 		writel(BIT(gpp_offset), community->regs + GPI_IS + gpp * 4);
 
 		value = readl(community->regs + community->ie_offset + gpp * 4);
 		value |= BIT(gpp_offset);
 		writel(value, community->regs + community->ie_offset + gpp * 4);
+		raw_spin_unlock_irqrestore(&pctrl->lock, flags);
 	}
-
-	raw_spin_unlock_irqrestore(&pctrl->lock, flags);
 }
 
 static void intel_gpio_irq_mask_unmask(struct irq_data *d, bool mask)
@@ -826,28 +862,33 @@ static void intel_gpio_irq_mask_unmask(struct irq_data *d, bool mask)
 	struct intel_pinctrl *pctrl = gpiochip_get_data(gc);
 	const struct intel_community *community;
 	unsigned pin = irqd_to_hwirq(d);
-	unsigned long flags;
-
-	raw_spin_lock_irqsave(&pctrl->lock, flags);
 
 	community = intel_get_community(pctrl, pin);
 	if (community) {
-		unsigned padno = pin_to_padno(community, pin);
-		unsigned gpp_offset = padno % community->gpp_size;
-		unsigned gpp = padno / community->gpp_size;
+		const struct intel_padgroup *padgrp;
+		unsigned gpp, gpp_offset;
+		unsigned long flags;
 		void __iomem *reg;
 		u32 value;
 
+		padgrp = intel_community_get_padgroup(community, pin);
+		if (!padgrp)
+			return;
+
+		gpp = padgrp->reg_num;
+		gpp_offset = padgroup_offset(padgrp, pin);
+
 		reg = community->regs + community->ie_offset + gpp * 4;
+
+		raw_spin_lock_irqsave(&pctrl->lock, flags);
 		value = readl(reg);
 		if (mask)
 			value &= ~BIT(gpp_offset);
 		else
 			value |= BIT(gpp_offset);
 		writel(value, reg);
+		raw_spin_unlock_irqrestore(&pctrl->lock, flags);
 	}
-
-	raw_spin_unlock_irqrestore(&pctrl->lock, flags);
 }
 
 static void intel_gpio_irq_mask(struct irq_data *d)
@@ -938,23 +979,20 @@ static irqreturn_t intel_gpio_community_irq_handler(struct intel_pinctrl *pctrl,
 	int gpp;
 
 	for (gpp = 0; gpp < community->ngpps; gpp++) {
+		const struct intel_padgroup *padgrp = &community->gpps[gpp];
 		unsigned long pending, enabled, gpp_offset;
 
-		pending = readl(community->regs + GPI_IS + gpp * 4);
+		pending = readl(community->regs + GPI_IS + padgrp->reg_num * 4);
 		enabled = readl(community->regs + community->ie_offset +
-				gpp * 4);
+				padgrp->reg_num * 4);
 
 		/* Only interrupts that are enabled */
 		pending &= enabled;
 
-		for_each_set_bit(gpp_offset, &pending, community->gpp_size) {
+		for_each_set_bit(gpp_offset, &pending, padgrp->size) {
 			unsigned padno, irq;
 
-			/*
-			 * The last group in community can have less pins
-			 * than NPADS_IN_GPP.
-			 */
-			padno = gpp_offset + gpp * community->gpp_size;
+			padno = padgrp->base - community->pin_base + gpp_offset;
 			if (padno >= community->npins)
 				break;
 
@@ -1045,6 +1083,51 @@ static int intel_gpio_probe(struct intel_pinctrl *pctrl, int irq)
 	return 0;
 }
 
+static int intel_pinctrl_add_padgroups(struct intel_pinctrl *pctrl,
+				       struct intel_community *community)
+{
+	struct intel_padgroup *gpps;
+	unsigned npins = community->npins;
+	unsigned padown_num = 0;
+	size_t ngpps, i;
+
+	if (community->gpps)
+		ngpps = community->ngpps;
+	else
+		ngpps = DIV_ROUND_UP(community->npins, community->gpp_size);
+
+	gpps = devm_kcalloc(pctrl->dev, ngpps, sizeof(*gpps), GFP_KERNEL);
+	if (!gpps)
+		return -ENOMEM;
+
+	for (i = 0; i < ngpps; i++) {
+		if (community->gpps) {
+			gpps[i] = community->gpps[i];
+		} else {
+			gpps[i].reg_num = i;
+			gpps[i].base = community->pin_base +
+				       i * community->gpp_size;
+			gpps[i].size = min(community->gpp_size, npins);
+			npins -= gpps[i].size;
+		}
+
+		if (gpps[i].size > 32)
+			return -EINVAL;
+
+		gpps[i].padown_num = padown_num;
+
+		if (community->padown_fixed)
+			padown_num += ALIGN(DIV_ROUND_UP(gpps[i].size, 8), 4);
+		else
+			padown_num += DIV_ROUND_UP(gpps[i].size, 8);
+	}
+
+	community->ngpps = ngpps;
+	community->gpps = gpps;
+
+	return 0;
+}
+
 static int intel_pinctrl_pm_init(struct intel_pinctrl *pctrl)
 {
 #ifdef CONFIG_PM_SLEEP
@@ -1142,8 +1225,10 @@ int intel_pinctrl_probe(struct platform_device *pdev,
 
 		community->regs = regs;
 		community->pad_regs = regs + padbar;
-		community->ngpps = DIV_ROUND_UP(community->npins,
-						community->gpp_size);
+
+		ret = intel_pinctrl_add_padgroups(pctrl, community);
+		if (ret)
+			return ret;
 	}
 
 	irq = platform_get_irq(pdev, 0);
diff --git a/drivers/pinctrl/intel/pinctrl-intel.h b/drivers/pinctrl/intel/pinctrl-intel.h
index fe9521f345b5..fc7b3dc15eec 100644
--- a/drivers/pinctrl/intel/pinctrl-intel.h
+++ b/drivers/pinctrl/intel/pinctrl-intel.h
@@ -44,10 +44,29 @@ struct intel_function {
 };
 
 /**
+ * struct intel_padgroup - Hardware pad group information
+ * @reg_num: GPI_IS register number
+ * @base: Starting pin of this group
+ * @size: Size of this group (maximum is 32).
+ * @padown_num: PAD_OWN register number (assigned by the core driver)
+ *
+ * If pad groups of a community are not the same size, use this structure
+ * to specify them.
+ */
+struct intel_padgroup {
+	unsigned reg_num;
+	unsigned base;
+	unsigned size;
+	unsigned padown_num;
+};
+
+/**
  * struct intel_community - Intel pin community description
  * @barno: MMIO BAR number where registers for this community reside
  * @padown_offset: Register offset of PAD_OWN register from @regs. If %0
  *                 then there is no support for owner.
+ * @padown_fixed: True if PAD_OWN registers for a pad group always takes 4
+ *                registers, even if the pad group is smaller.
  * @padcfglock_offset: Register offset of PADCFGLOCK from @regs. If %0 then
  *                     locking is not supported.
  * @hostown_offset: Register offset of HOSTSW_OWN from @regs. If %0 then it
@@ -56,17 +75,24 @@ struct intel_function {
  * @ie_offset: Register offset of GPI_IE from @regs.
  * @pin_base: Starting pin of pins in this community
  * @gpp_size: Maximum number of pads in each group, such as PADCFGLOCK,
- *            HOSTSW_OWN,  GPI_IS, GPI_IE, etc.
+ *            HOSTSW_OWN,  GPI_IS, GPI_IE, etc. Used when @gpps is %NULL.
  * @npins: Number of pins in this community
  * @features: Additional features supported by the hardware
+ * @gpps: Pad groups if the controller has variable size pad groups
+ * @ngpps: Number of pad groups in this community
  * @regs: Community specific common registers (reserved for core driver)
  * @pad_regs: Community specific pad registers (reserved for core driver)
- * @ngpps: Number of groups (hw groups) in this community (reserved for
- *         core driver)
+ *
+ * Most Intel GPIO host controllers this driver supports each pad group is
+ * of equal size (except the last one). In that case the driver can just
+ * fill in @gpp_size field and let the core driver to handle the rest. If
+ * the controller has pad groups of variable size the client driver can
+ * pass custom @gpps and @ngpps instead.
  */
 struct intel_community {
 	unsigned barno;
 	unsigned padown_offset;
+	bool padown_fixed;
 	unsigned padcfglock_offset;
 	unsigned hostown_offset;
 	unsigned ie_offset;
@@ -74,9 +100,11 @@ struct intel_community {
 	unsigned gpp_size;
 	size_t npins;
 	unsigned features;
+	const struct intel_padgroup *gpps;
+	size_t ngpps;
+	/* Reserved for the core driver */
 	void __iomem *regs;
 	void __iomem *pad_regs;
-	size_t ngpps;
 };
 
 /* Additional features supported by the hardware */
diff --git a/drivers/pinctrl/intel/pinctrl-sunrisepoint.c b/drivers/pinctrl/intel/pinctrl-sunrisepoint.c
index 9877526c0807..cdcc02d2384b 100644
--- a/drivers/pinctrl/intel/pinctrl-sunrisepoint.c
+++ b/drivers/pinctrl/intel/pinctrl-sunrisepoint.c
@@ -27,6 +27,7 @@
 	{						\
 		.barno = (b),				\
 		.padown_offset = SPT_PAD_OWN,		\
+		.padown_fixed = true,			\
 		.padcfglock_offset = SPT_PADCFGLOCK,	\
 		.hostown_offset = SPT_HOSTSW_OWN,	\
 		.ie_offset = SPT_GPI_IE,		\
