Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 192ae22b authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "pinctrl: qcom: setup GPIO chip in hierarchy"

parents ea48d885 1ec1dc87
Loading
Loading
Loading
Loading
+124 −0
Original line number Diff line number Diff line
@@ -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;
}

+15 −0
Original line number Diff line number Diff line
@@ -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;