mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-12 15:49:17 -04:00
Merge "pinctrl: qcom: setup GPIO chip in hierarchy"
This commit is contained in:
commit
192ae22b3a
2 changed files with 139 additions and 0 deletions
|
|
@ -24,6 +24,8 @@
|
|||
#include <linux/log2.h>
|
||||
#include <linux/bitmap.h>
|
||||
|
||||
#include <linux/soc/qcom/irq.h>
|
||||
|
||||
#include "../core.h"
|
||||
#include "../pinconf.h"
|
||||
#include "pinctrl-msm.h"
|
||||
|
|
@ -46,6 +48,7 @@
|
|||
* @enabled_irqs: Bitmap of currently enabled irqs.
|
||||
* @dual_edge_irqs: Bitmap of irqs that need sw emulated dual edge
|
||||
* detection.
|
||||
* @skip_wake_irqs: Skip IRQs that are handled by wakeup interrupt contrroller
|
||||
* @soc; Reference to soc_data of platform specific data.
|
||||
* @regs: Base addresses for the TLMM tiles.
|
||||
*/
|
||||
|
|
@ -63,6 +66,7 @@ struct msm_pinctrl {
|
|||
|
||||
DECLARE_BITMAP(dual_edge_irqs, MAX_NR_GPIO);
|
||||
DECLARE_BITMAP(enabled_irqs, MAX_NR_GPIO);
|
||||
DECLARE_BITMAP(skip_wake_irqs, MAX_NR_GPIO);
|
||||
|
||||
const struct msm_pinctrl_soc_data *soc;
|
||||
void __iomem *regs[MAX_NR_TILES];
|
||||
|
|
@ -715,6 +719,12 @@ static void msm_gpio_irq_mask(struct irq_data *d)
|
|||
unsigned long flags;
|
||||
u32 val;
|
||||
|
||||
if (d->parent_data)
|
||||
irq_chip_mask_parent(d);
|
||||
|
||||
if (test_bit(d->hwirq, pctrl->skip_wake_irqs))
|
||||
return;
|
||||
|
||||
g = &pctrl->soc->groups[d->hwirq];
|
||||
|
||||
raw_spin_lock_irqsave(&pctrl->lock, flags);
|
||||
|
|
@ -759,6 +769,12 @@ static void msm_gpio_irq_clear_unmask(struct irq_data *d, bool status_clear)
|
|||
unsigned long flags;
|
||||
u32 val;
|
||||
|
||||
if (d->parent_data)
|
||||
irq_chip_unmask_parent(d);
|
||||
|
||||
if (test_bit(d->hwirq, pctrl->skip_wake_irqs))
|
||||
return;
|
||||
|
||||
g = &pctrl->soc->groups[d->hwirq];
|
||||
|
||||
raw_spin_lock_irqsave(&pctrl->lock, flags);
|
||||
|
|
@ -786,10 +802,43 @@ static void msm_gpio_irq_clear_unmask(struct irq_data *d, bool status_clear)
|
|||
|
||||
static void msm_gpio_irq_enable(struct irq_data *d)
|
||||
{
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||
struct msm_pinctrl *pctrl = gpiochip_get_data(gc);
|
||||
|
||||
/*
|
||||
* Clear the interrupt that may be pending before we enable
|
||||
* the line.
|
||||
* This is especially a problem with the GPIOs routed to the
|
||||
* PDC. These GPIOs are direct-connect interrupts to the GIC.
|
||||
* Disabling the interrupt line at the PDC does not prevent
|
||||
* the interrupt from being latched at the GIC. The state at
|
||||
* GIC needs to be cleared before enabling.
|
||||
*/
|
||||
if (d->parent_data) {
|
||||
irq_chip_set_parent_state(d, IRQCHIP_STATE_PENDING, 0);
|
||||
irq_chip_enable_parent(d);
|
||||
}
|
||||
|
||||
if (test_bit(d->hwirq, pctrl->skip_wake_irqs))
|
||||
return;
|
||||
|
||||
msm_gpio_irq_clear_unmask(d, true);
|
||||
}
|
||||
|
||||
static void msm_gpio_irq_disable(struct irq_data *d)
|
||||
{
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||
struct msm_pinctrl *pctrl = gpiochip_get_data(gc);
|
||||
|
||||
if (d->parent_data)
|
||||
irq_chip_disable_parent(d);
|
||||
|
||||
if (test_bit(d->hwirq, pctrl->skip_wake_irqs))
|
||||
return;
|
||||
|
||||
msm_gpio_irq_mask(d);
|
||||
}
|
||||
|
||||
static void msm_gpio_irq_unmask(struct irq_data *d)
|
||||
{
|
||||
msm_gpio_irq_clear_unmask(d, false);
|
||||
|
|
@ -803,6 +852,9 @@ static void msm_gpio_irq_ack(struct irq_data *d)
|
|||
unsigned long flags;
|
||||
u32 val;
|
||||
|
||||
if (test_bit(d->hwirq, pctrl->skip_wake_irqs))
|
||||
return;
|
||||
|
||||
g = &pctrl->soc->groups[d->hwirq];
|
||||
|
||||
raw_spin_lock_irqsave(&pctrl->lock, flags);
|
||||
|
|
@ -828,6 +880,12 @@ static int msm_gpio_irq_set_type(struct irq_data *d, unsigned int type)
|
|||
unsigned long flags;
|
||||
u32 val;
|
||||
|
||||
if (d->parent_data)
|
||||
irq_chip_set_type_parent(d, type);
|
||||
|
||||
if (test_bit(d->hwirq, pctrl->skip_wake_irqs))
|
||||
return 0;
|
||||
|
||||
g = &pctrl->soc->groups[d->hwirq];
|
||||
|
||||
raw_spin_lock_irqsave(&pctrl->lock, flags);
|
||||
|
|
@ -920,6 +978,15 @@ static int msm_gpio_irq_set_wake(struct irq_data *d, unsigned int on)
|
|||
struct msm_pinctrl *pctrl = gpiochip_get_data(gc);
|
||||
unsigned long flags;
|
||||
|
||||
if (d->parent_data)
|
||||
irq_chip_set_wake_parent(d, on);
|
||||
|
||||
/*
|
||||
* While they may not wake up when the TLMM is powered off,
|
||||
* some GPIOs would like to wakeup the system from suspend
|
||||
* when TLMM is powered on. To allow that, enable the GPIO
|
||||
* summary line to be wakeup capable at GIC.
|
||||
*/
|
||||
raw_spin_lock_irqsave(&pctrl->lock, flags);
|
||||
|
||||
irq_set_irq_wake(pctrl->irq, on);
|
||||
|
|
@ -998,6 +1065,30 @@ static void msm_gpio_irq_handler(struct irq_desc *desc)
|
|||
chained_irq_exit(chip, desc);
|
||||
}
|
||||
|
||||
static int msm_gpio_wakeirq(struct gpio_chip *gc,
|
||||
unsigned int child,
|
||||
unsigned int child_type,
|
||||
unsigned int *parent,
|
||||
unsigned int *parent_type)
|
||||
{
|
||||
struct msm_pinctrl *pctrl = gpiochip_get_data(gc);
|
||||
const struct msm_gpio_wakeirq_map *map;
|
||||
int i;
|
||||
|
||||
*parent = GPIO_NO_WAKE_IRQ;
|
||||
*parent_type = IRQ_TYPE_EDGE_RISING;
|
||||
|
||||
for (i = 0; i < pctrl->soc->nwakeirq_map; i++) {
|
||||
map = &pctrl->soc->wakeirq_map[i];
|
||||
if (map->gpio == child) {
|
||||
*parent = map->wakeirq;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool msm_gpio_needs_valid_mask(struct msm_pinctrl *pctrl)
|
||||
{
|
||||
if (pctrl->soc->reserved_gpios)
|
||||
|
|
@ -1012,6 +1103,7 @@ static int msm_gpio_init(struct msm_pinctrl *pctrl)
|
|||
struct gpio_irq_chip *girq;
|
||||
int ret;
|
||||
unsigned ngpio = pctrl->soc->ngpios;
|
||||
struct device_node *dn;
|
||||
|
||||
if (WARN_ON(ngpio > MAX_NR_GPIO))
|
||||
return -EINVAL;
|
||||
|
|
@ -1028,14 +1120,36 @@ static int msm_gpio_init(struct msm_pinctrl *pctrl)
|
|||
|
||||
pctrl->irq_chip.name = "msmgpio";
|
||||
pctrl->irq_chip.irq_enable = msm_gpio_irq_enable;
|
||||
pctrl->irq_chip.irq_disable = msm_gpio_irq_disable;
|
||||
pctrl->irq_chip.irq_mask = msm_gpio_irq_mask;
|
||||
pctrl->irq_chip.irq_unmask = msm_gpio_irq_unmask;
|
||||
pctrl->irq_chip.irq_ack = msm_gpio_irq_ack;
|
||||
pctrl->irq_chip.irq_eoi = irq_chip_eoi_parent;
|
||||
pctrl->irq_chip.irq_set_type = msm_gpio_irq_set_type;
|
||||
pctrl->irq_chip.irq_set_wake = msm_gpio_irq_set_wake;
|
||||
pctrl->irq_chip.irq_request_resources = msm_gpio_irq_reqres;
|
||||
pctrl->irq_chip.irq_release_resources = msm_gpio_irq_relres;
|
||||
|
||||
dn = of_parse_phandle(pctrl->dev->of_node, "wakeup-parent", 0);
|
||||
if (dn) {
|
||||
int i;
|
||||
bool skip;
|
||||
unsigned int gpio;
|
||||
|
||||
chip->irq.parent_domain = irq_find_matching_host(dn,
|
||||
DOMAIN_BUS_WAKEUP);
|
||||
of_node_put(dn);
|
||||
if (!chip->irq.parent_domain)
|
||||
return -EPROBE_DEFER;
|
||||
chip->irq.child_to_parent_hwirq = msm_gpio_wakeirq;
|
||||
|
||||
skip = irq_domain_qcom_handle_wakeup(chip->irq.parent_domain);
|
||||
for (i = 0; skip && i < pctrl->soc->nwakeirq_map; i++) {
|
||||
gpio = pctrl->soc->wakeirq_map[i].gpio;
|
||||
set_bit(gpio, pctrl->skip_wake_irqs);
|
||||
}
|
||||
}
|
||||
|
||||
girq = &chip->irq;
|
||||
girq->chip = &pctrl->irq_chip;
|
||||
girq->parent_handler = msm_gpio_irq_handler;
|
||||
|
|
@ -1074,6 +1188,16 @@ static int msm_gpio_init(struct msm_pinctrl *pctrl)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Since we are chained to the GIC using the TLMM summary line
|
||||
* and in hierarchy with the wakeup parent interrupt controller,
|
||||
* explicitly set the chained summary line. We need to do this because
|
||||
* the summary line is not routed to the wakeup parent but directly
|
||||
* to the GIC.
|
||||
*/
|
||||
gpiochip_set_chained_irqchip(chip, &pctrl->irq_chip, pctrl->irq,
|
||||
msm_gpio_irq_handler);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#define __PINCTRL_MSM_H__
|
||||
|
||||
#include <linux/pinctrl/qcom-pinctrl.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
|
||||
struct pinctrl_pin_desc;
|
||||
|
||||
|
|
@ -110,6 +111,16 @@ struct pinctrl_qup {
|
|||
u32 offset;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct msm_gpio_wakeirq_map - Map of GPIOs and their wakeup pins
|
||||
* @gpio: The GPIOs that are wakeup capable
|
||||
* @wakeirq: The interrupt at the always-on interrupt controller
|
||||
*/
|
||||
struct msm_gpio_wakeirq_map {
|
||||
unsigned int gpio;
|
||||
unsigned int wakeirq;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct msm_pinctrl_soc_data - Qualcomm pin controller driver configuration
|
||||
* @pins: An array describing all pins the pin controller affects.
|
||||
|
|
@ -120,6 +131,8 @@ struct pinctrl_qup {
|
|||
* @ngroups: The numbmer of entries in @groups.
|
||||
* @ngpio: The number of pingroups the driver should expose as GPIOs.
|
||||
* @pull_no_keeper: The SoC does not support keeper bias.
|
||||
* @wakeirq_map: The map of wakeup capable GPIOs and the pin at PDC/MPM
|
||||
* @nwakeirq_map: The number of entries in @hierarchy_map
|
||||
*/
|
||||
struct msm_pinctrl_soc_data {
|
||||
const struct pinctrl_pin_desc *pins;
|
||||
|
|
@ -135,6 +148,8 @@ struct msm_pinctrl_soc_data {
|
|||
const int *reserved_gpios;
|
||||
struct pinctrl_qup *qup_regs;
|
||||
unsigned int nqup_regs;
|
||||
const struct msm_gpio_wakeirq_map *wakeirq_map;
|
||||
unsigned int nwakeirq_map;
|
||||
};
|
||||
|
||||
extern const struct dev_pm_ops msm_pinctrl_dev_pm_ops;
|
||||
|
|
|
|||
Loading…
Reference in a new issue