mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-10-29 23:53:32 +00:00
pinctrl: intel: Allow custom GPIO base for pad groups
Currently we always have direct mapping between GPIO numbers and the hardware pin numbers. However, there are cases where that's not the case anymore (more about this in the next patch). Instead we need to be able to specify custom GPIO base for certain pad groups. To support this, add a new field (gpio_base) to the pad group structure and update the core Intel pinctrl driver to handle this accordingly. Passing 0 as gpio_base will use direct mapping so the existing drivers do not need to be modified. Passing -1 excludes the whole pad group from having GPIO mapping. Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com> Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
This commit is contained in:
parent
03c4749dd6
commit
a60eac3239
2 changed files with 120 additions and 39 deletions
|
@ -806,22 +806,63 @@ static const struct gpio_chip intel_gpio_chip = {
|
|||
.set_config = gpiochip_generic_config,
|
||||
};
|
||||
|
||||
/**
|
||||
* intel_gpio_to_pin() - Translate from GPIO offset to pin number
|
||||
* @pctrl: Pinctrl structure
|
||||
* @offset: GPIO offset from gpiolib
|
||||
* @commmunity: Community is filled here if not %NULL
|
||||
* @padgrp: Pad group is filled here if not %NULL
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
static int intel_gpio_to_pin(struct intel_pinctrl *pctrl, unsigned offset,
|
||||
const struct intel_community **community,
|
||||
const struct intel_padgroup **padgrp)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < pctrl->ncommunities; i++) {
|
||||
const struct intel_community *comm = &pctrl->communities[i];
|
||||
int j;
|
||||
|
||||
for (j = 0; j < comm->ngpps; j++) {
|
||||
const struct intel_padgroup *pgrp = &comm->gpps[j];
|
||||
|
||||
if (pgrp->gpio_base < 0)
|
||||
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 -EINVAL;
|
||||
}
|
||||
|
||||
static void intel_gpio_irq_ack(struct irq_data *d)
|
||||
{
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||
struct intel_pinctrl *pctrl = gpiochip_get_data(gc);
|
||||
const struct intel_community *community;
|
||||
unsigned pin = irqd_to_hwirq(d);
|
||||
const struct intel_padgroup *padgrp;
|
||||
int pin;
|
||||
|
||||
community = intel_get_community(pctrl, pin);
|
||||
if (community) {
|
||||
const struct intel_padgroup *padgrp;
|
||||
pin = intel_gpio_to_pin(pctrl, irqd_to_hwirq(d), &community, &padgrp);
|
||||
if (pin >= 0) {
|
||||
unsigned gpp, gpp_offset, is_offset;
|
||||
|
||||
padgrp = intel_community_get_padgroup(community, pin);
|
||||
if (!padgrp)
|
||||
return;
|
||||
|
||||
gpp = padgrp->reg_num;
|
||||
gpp_offset = padgroup_offset(padgrp, pin);
|
||||
is_offset = community->is_offset + gpp * 4;
|
||||
|
@ -837,19 +878,15 @@ static void intel_gpio_irq_enable(struct irq_data *d)
|
|||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||
struct intel_pinctrl *pctrl = gpiochip_get_data(gc);
|
||||
const struct intel_community *community;
|
||||
unsigned pin = irqd_to_hwirq(d);
|
||||
const struct intel_padgroup *padgrp;
|
||||
int pin;
|
||||
|
||||
community = intel_get_community(pctrl, pin);
|
||||
if (community) {
|
||||
const struct intel_padgroup *padgrp;
|
||||
pin = intel_gpio_to_pin(pctrl, irqd_to_hwirq(d), &community, &padgrp);
|
||||
if (pin >= 0) {
|
||||
unsigned gpp, gpp_offset, is_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);
|
||||
is_offset = community->is_offset + gpp * 4;
|
||||
|
@ -870,20 +907,16 @@ static void intel_gpio_irq_mask_unmask(struct irq_data *d, 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;
|
||||
unsigned pin = irqd_to_hwirq(d);
|
||||
const struct intel_padgroup *padgrp;
|
||||
int pin;
|
||||
|
||||
community = intel_get_community(pctrl, pin);
|
||||
if (community) {
|
||||
const struct intel_padgroup *padgrp;
|
||||
pin = intel_gpio_to_pin(pctrl, irqd_to_hwirq(d), &community, &padgrp);
|
||||
if (pin >= 0) {
|
||||
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);
|
||||
|
||||
|
@ -914,7 +947,7 @@ static int intel_gpio_irq_type(struct irq_data *d, unsigned type)
|
|||
{
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||
struct intel_pinctrl *pctrl = gpiochip_get_data(gc);
|
||||
unsigned pin = irqd_to_hwirq(d);
|
||||
unsigned pin = intel_gpio_to_pin(pctrl, irqd_to_hwirq(d), NULL, NULL);
|
||||
unsigned long flags;
|
||||
void __iomem *reg;
|
||||
u32 value;
|
||||
|
@ -969,7 +1002,7 @@ static int intel_gpio_irq_wake(struct irq_data *d, unsigned int on)
|
|||
{
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||
struct intel_pinctrl *pctrl = gpiochip_get_data(gc);
|
||||
unsigned pin = irqd_to_hwirq(d);
|
||||
unsigned pin = intel_gpio_to_pin(pctrl, irqd_to_hwirq(d), NULL, NULL);
|
||||
|
||||
if (on)
|
||||
enable_irq_wake(pctrl->irq);
|
||||
|
@ -1000,14 +1033,10 @@ static irqreturn_t intel_gpio_community_irq_handler(struct intel_pinctrl *pctrl,
|
|||
pending &= enabled;
|
||||
|
||||
for_each_set_bit(gpp_offset, &pending, padgrp->size) {
|
||||
unsigned padno, irq;
|
||||
|
||||
padno = padgrp->base - community->pin_base + gpp_offset;
|
||||
if (padno >= community->npins)
|
||||
break;
|
||||
unsigned irq;
|
||||
|
||||
irq = irq_find_mapping(gc->irq.domain,
|
||||
community->pin_base + padno);
|
||||
padgrp->gpio_base + gpp_offset);
|
||||
generic_handle_irq(irq);
|
||||
|
||||
ret |= IRQ_HANDLED;
|
||||
|
@ -1044,13 +1073,56 @@ static struct irq_chip intel_gpio_irqchip = {
|
|||
.flags = IRQCHIP_MASK_ON_SUSPEND,
|
||||
};
|
||||
|
||||
static int intel_gpio_add_pin_ranges(struct intel_pinctrl *pctrl,
|
||||
const struct intel_community *community)
|
||||
{
|
||||
int ret, i;
|
||||
|
||||
for (i = 0; i < community->ngpps; i++) {
|
||||
const struct intel_padgroup *gpp = &community->gpps[i];
|
||||
|
||||
if (gpp->gpio_base < 0)
|
||||
continue;
|
||||
|
||||
ret = gpiochip_add_pin_range(&pctrl->chip, dev_name(pctrl->dev),
|
||||
gpp->gpio_base, gpp->base,
|
||||
gpp->size);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned intel_gpio_ngpio(const struct intel_pinctrl *pctrl)
|
||||
{
|
||||
const struct intel_community *community;
|
||||
unsigned 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 < 0)
|
||||
continue;
|
||||
|
||||
if (gpp->gpio_base + gpp->size > ngpio)
|
||||
ngpio = gpp->gpio_base + gpp->size;
|
||||
}
|
||||
}
|
||||
|
||||
return ngpio;
|
||||
}
|
||||
|
||||
static int intel_gpio_probe(struct intel_pinctrl *pctrl, int irq)
|
||||
{
|
||||
int ret;
|
||||
int ret, i;
|
||||
|
||||
pctrl->chip = intel_gpio_chip;
|
||||
|
||||
pctrl->chip.ngpio = pctrl->soc->npins;
|
||||
pctrl->chip.ngpio = intel_gpio_ngpio(pctrl);
|
||||
pctrl->chip.label = dev_name(pctrl->dev);
|
||||
pctrl->chip.parent = pctrl->dev;
|
||||
pctrl->chip.base = -1;
|
||||
|
@ -1062,11 +1134,14 @@ static int intel_gpio_probe(struct intel_pinctrl *pctrl, int irq)
|
|||
return ret;
|
||||
}
|
||||
|
||||
ret = gpiochip_add_pin_range(&pctrl->chip, dev_name(pctrl->dev),
|
||||
0, 0, pctrl->soc->npins);
|
||||
if (ret) {
|
||||
dev_err(pctrl->dev, "failed to add GPIO pin range\n");
|
||||
return ret;
|
||||
for (i = 0; i < pctrl->ncommunities; i++) {
|
||||
struct intel_community *community = &pctrl->communities[i];
|
||||
|
||||
ret = intel_gpio_add_pin_ranges(pctrl, community);
|
||||
if (ret) {
|
||||
dev_err(pctrl->dev, "failed to add GPIO pin range\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1126,6 +1201,9 @@ static int intel_pinctrl_add_padgroups(struct intel_pinctrl *pctrl,
|
|||
if (gpps[i].size > 32)
|
||||
return -EINVAL;
|
||||
|
||||
if (!gpps[i].gpio_base)
|
||||
gpps[i].gpio_base = gpps[i].base;
|
||||
|
||||
gpps[i].padown_num = padown_num;
|
||||
|
||||
/*
|
||||
|
|
|
@ -51,6 +51,8 @@ struct intel_function {
|
|||
* @reg_num: GPI_IS register number
|
||||
* @base: Starting pin of this group
|
||||
* @size: Size of this group (maximum is 32).
|
||||
* @gpio_base: Starting GPIO base of this group (%0 if matches with @base,
|
||||
* and %-1 if no GPIO mapping should be created)
|
||||
* @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
|
||||
|
@ -60,6 +62,7 @@ struct intel_padgroup {
|
|||
unsigned reg_num;
|
||||
unsigned base;
|
||||
unsigned size;
|
||||
int gpio_base;
|
||||
unsigned padown_num;
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue