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

Commit 7a1eafa3 authored by Maulik Shah's avatar Maulik Shah
Browse files

pinctrl: qcom: Use TLMM and PDC for dual edge wakeup interrupts



Although PDC has the capability to route falling edge interrupt to
GIC by inverting the input signal, falling edge of the interrupts marked
as dual edge type is routed as is. This causes GIC to ignore to falling
edge of these interrupts when the system is active. During APSS system
sleep once PDC starts monitoring these interrupts, PDC always replays a
rising edge irrespective of which edge woke the APSS from sleep.  So,
the issue does not happen during the system sleep.

To address the issue when system is active, route the falling_edge
of these interrupts through one of the direct connect interrupts
available for APSS. The original GIC interrupt is configured as dual
edge at PDC, rising edge at GIC (same as earlier, this does not change).

This solution requires additional programming of 2 registers:

TLMM_<ZONE>_DIR_CONN_INTRn_CFG_HMSS - program GPIO_SEL with the gpio
number and set POLAIRTY to 0 for inverting the falling edge

TLMM_GPIO_INTR_CFGn - set DIR_CONN_EN bit to 1 for enabling direct
connect for gpio.

Change-Id: Iaa9dfe391ef4942cd8b12fd1e7d8f9c238947af2
Signed-off-by: default avatarArchana Sathyakumar <asathyak@codeaurora.org>
Signed-off-by: default avatarMaulik Shah <mkshah@codeaurora.org>
parent 5f551b33
Loading
Loading
Loading
Loading
+210 −6
Original line number Diff line number Diff line
@@ -45,6 +45,7 @@
 * @chip:           gpiochip handle.
 * @restart_nb:     restart notifier block.
 * @irq:            parent irq for the TLMM irq_chip.
 * @n_dir_conns:    The number of pins directly connected to GIC.
 * @lock:           Spinlock to protect register resources as well
 *                  as msm_pinctrl data structures.
 * @enabled_irqs:   Bitmap of currently enabled irqs.
@@ -63,6 +64,7 @@ struct msm_pinctrl {

	struct irq_chip irq_chip;
	int irq;
	int n_dir_conns;

	raw_spinlock_t lock;

@@ -713,16 +715,45 @@ static void msm_gpio_update_dual_edge_pos(struct msm_pinctrl *pctrl,
		val, val2);
}

static bool is_gpio_dual_edge(struct irq_data *d, irq_hw_number_t *irq)
{
	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
	struct msm_pinctrl *pctrl = gpiochip_get_data(gc);
	struct msm_dir_conn *dc;
	int i;

	for (i = pctrl->n_dir_conns; i > 0; i--) {
		dc = &pctrl->soc->dir_conn[i];

		if (dc->gpio == d->hwirq) {
			*irq = dc->irq;
			return true;
		}
	}

	return false;
}

static void msm_gpio_irq_mask(struct irq_data *d)
{
	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
	struct msm_pinctrl *pctrl = gpiochip_get_data(gc);
	const struct msm_pingroup *g;
	unsigned long flags;
	struct irq_data *dir_conn_data;
	irq_hw_number_t dir_conn_irq = 0;
	u32 val;

	if (d->parent_data)
	if (d->parent_data) {
		if (is_gpio_dual_edge(d, &dir_conn_irq)) {
			dir_conn_data = irq_get_irq_data(dir_conn_irq);
			if (!dir_conn_data)
				return;

			dir_conn_data->chip->irq_mask(dir_conn_data);
		}
		irq_chip_mask_parent(d);
	}

	if (test_bit(d->hwirq, pctrl->skip_wake_irqs))
		return;
@@ -768,11 +799,21 @@ static void msm_gpio_irq_clear_unmask(struct irq_data *d, bool status_clear)
	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
	struct msm_pinctrl *pctrl = gpiochip_get_data(gc);
	const struct msm_pingroup *g;
	struct irq_data *dir_conn_data;
	irq_hw_number_t dir_conn_irq = 0;
	unsigned long flags;
	u32 val;

	if (d->parent_data)
	if (d->parent_data) {
		if (is_gpio_dual_edge(d, &dir_conn_irq)) {
			dir_conn_data = irq_get_irq_data(dir_conn_irq);
			if (!dir_conn_data)
				return;

			dir_conn_data->chip->irq_unmask(dir_conn_data);
		}
		irq_chip_unmask_parent(d);
	}

	if (test_bit(d->hwirq, pctrl->skip_wake_irqs))
		return;
@@ -806,6 +847,8 @@ 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);
	struct irq_data *dir_conn_data;
	irq_hw_number_t dir_conn_irq = 0;

	/*
	 * Clear the interrupt that may be pending before we enable
@@ -817,6 +860,15 @@ static void msm_gpio_irq_enable(struct irq_data *d)
	 * GIC needs to be cleared before enabling.
	 */
	if (d->parent_data) {
		if (is_gpio_dual_edge(d, &dir_conn_irq)) {
			dir_conn_data = irq_get_irq_data(dir_conn_irq);
			if (!dir_conn_data)
				return;

			irq_set_irqchip_state(dir_conn_irq,
					IRQCHIP_STATE_PENDING, 0);
			dir_conn_data->chip->irq_unmask(dir_conn_data);
		}
		irq_chip_set_parent_state(d, IRQCHIP_STATE_PENDING, 0);
		irq_chip_enable_parent(d);
	}
@@ -831,9 +883,19 @@ 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);
	struct irq_data *dir_conn_data;
	irq_hw_number_t dir_conn_irq = 0;

	if (d->parent_data)
	if (d->parent_data) {
		if (is_gpio_dual_edge(d, &dir_conn_irq)) {
			dir_conn_data = irq_get_irq_data(dir_conn_irq);
			if (!dir_conn_data)
				return;

			dir_conn_data->chip->irq_mask(dir_conn_data);
		}
		irq_chip_disable_parent(d);
	}

	if (test_bit(d->hwirq, pctrl->skip_wake_irqs))
		return;
@@ -874,16 +936,147 @@ static void msm_gpio_irq_ack(struct irq_data *d)
	raw_spin_unlock_irqrestore(&pctrl->lock, flags);
}

static void msm_dirconn_cfg_reg(struct irq_data *d, u32 offset)
{
	u32 val;
	const struct msm_pingroup *g;
	unsigned long flags;
	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
	struct msm_pinctrl *pctrl = gpiochip_get_data(gc);

	raw_spin_lock_irqsave(&pctrl->lock, flags);
	g = &pctrl->soc->groups[d->hwirq];

	val = (d->hwirq) & 0xFF;

	writel_relaxed(val, pctrl->regs[g->tile] + g->dir_conn_reg
		       + (offset * 4));

	val = msm_readl_intr_cfg(pctrl, g);
	val |= BIT(g->dir_conn_en_bit);

	msm_writel_intr_cfg(val, pctrl, g);
	raw_spin_unlock_irqrestore(&pctrl->lock, flags);
}

static void msm_dirconn_uncfg_reg(struct irq_data *d, u32 offset)
{
	u32 val = 0;
	const struct msm_pingroup *g;
	unsigned long flags;
	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
	struct msm_pinctrl *pctrl = gpiochip_get_data(gc);

	raw_spin_lock_irqsave(&pctrl->lock, flags);
	g = &pctrl->soc->groups[d->hwirq];

	writel_relaxed(val, pctrl->regs + g->dir_conn_reg + (offset * 4));
	val = readl_relaxed(pctrl->regs + g->intr_cfg_reg);
	val &= ~BIT(g->dir_conn_en_bit);
	writel_relaxed(val, pctrl->regs + g->intr_cfg_reg);
	raw_spin_unlock_irqrestore(&pctrl->lock, flags);
}

static int select_dir_conn_mux(struct irq_data *d, irq_hw_number_t *irq,
			       bool add)
{
	struct msm_dir_conn *dc = NULL;
	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
	struct msm_pinctrl *pctrl = gpiochip_get_data(gc);
	int i, n_dir_conns = pctrl->n_dir_conns;

	for (i = n_dir_conns; i > 0; i--) {
		dc = &pctrl->soc->dir_conn[i];
		if (dc->gpio == d->hwirq && !add) {
			*irq = dc->irq;
			dc->gpio = -1;
			return n_dir_conns - i;
		}

		if (dc->gpio == -1 && add) {
			dc->gpio = (int)d->hwirq;
			*irq = dc->irq;
			return n_dir_conns - i;
		}
	}

	pr_err("%s: No direct connects selected for interrupt %lu\n",
				__func__, d->hwirq);
	return -EBUSY;
}

static void msm_gpio_dirconn_handler(struct irq_desc *desc)
{
	struct irq_data *irqd = irq_desc_get_handler_data(desc);
	struct irq_chip *chip = irq_desc_get_chip(desc);

	if (!irqd)
		return;

	chained_irq_enter(chip, desc);
	generic_handle_irq(irqd->irq);
	chained_irq_exit(chip, desc);
	irq_set_irqchip_state(irq_desc_get_irq_data(desc)->irq,
			      IRQCHIP_STATE_ACTIVE, 0);
}

static void add_dirconn_tlmm(struct irq_data *d, struct msm_pinctrl *pctrl)
{
	struct irq_data *dir_conn_data = NULL;
	int offset = 0;
	irq_hw_number_t irq = 0;

	offset = select_dir_conn_mux(d, &irq, true);
	if (offset < 0)
		return;

	msm_dirconn_cfg_reg(d, offset);
	irq_set_handler_data(irq, d);
	dir_conn_data = irq_get_irq_data(irq);

	if (!dir_conn_data)
		return;

	dir_conn_data->chip->irq_unmask(dir_conn_data);
}

static void remove_dirconn_tlmm(struct irq_data *d, irq_hw_number_t irq)
{
	struct irq_data *dir_conn_data = NULL;
	int offset = 0;

	offset = select_dir_conn_mux(d, &irq, false);
	if (offset < 0)
		return;

	msm_dirconn_uncfg_reg(d, offset);
	irq_set_handler_data(irq, NULL);
	dir_conn_data = irq_get_irq_data(irq);

	if (!dir_conn_data)
		return;

	dir_conn_data->chip->irq_mask(dir_conn_data);
}

static int msm_gpio_irq_set_type(struct irq_data *d, unsigned int type)
{
	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
	struct msm_pinctrl *pctrl = gpiochip_get_data(gc);
	const struct msm_pingroup *g;
	irq_hw_number_t irq = 0;
	unsigned long flags;
	u32 val;

	if (d->parent_data)
	if (d->parent_data) {
		if (pctrl->n_dir_conns > 0) {
			if (type == IRQ_TYPE_EDGE_BOTH)
				add_dirconn_tlmm(d, pctrl);
			else if (is_gpio_dual_edge(d, &irq))
				remove_dirconn_tlmm(d, irq);
		}
		irq_chip_set_type_parent(d, type);
	}

	if (test_bit(d->hwirq, pctrl->skip_wake_irqs))
		return 0;
@@ -1309,8 +1502,7 @@ int msm_pinctrl_probe(struct platform_device *pdev,
{
	struct msm_pinctrl *pctrl;
	struct resource *res;
	int ret;
	int i;
	int ret, i, num_irq, irq;

	msm_pinctrl_data = pctrl = devm_kzalloc(&pdev->dev, sizeof(*pctrl),
						GFP_KERNEL);
@@ -1362,6 +1554,18 @@ int msm_pinctrl_probe(struct platform_device *pdev,
	if (ret)
		return ret;

	num_irq = platform_irq_count(pdev);

	for (i = 1; i < num_irq; i++) {
		struct msm_dir_conn *dc = &soc_data->dir_conn[i];

		irq = platform_get_irq(pdev, i);
		dc->irq = irq;
		__irq_set_handler(irq, msm_gpio_dirconn_handler, false, NULL);
		irq_set_irq_type(irq, IRQ_TYPE_EDGE_RISING);
		pctrl->n_dir_conns++;
	}

	platform_set_drvdata(pdev, pctrl);

	dev_dbg(&pdev->dev, "Probed Qualcomm pinctrl driver\n");
+16 −0
Original line number Diff line number Diff line
@@ -38,6 +38,7 @@ struct msm_function {
 * @intr_status_reg:      Offset of the register holding the status bits for this group.
 * @intr_target_reg:      Offset of the register specifying routing of the interrupts
 *                        from this group.
 * @dir_conn_reg:         Offset of the register hmss setup in tile.
 * @mux_bit:              Offset in @ctl_reg for the pinmux function selection.
 * @pull_bit:             Offset in @ctl_reg for the bias configuration.
 * @drv_bit:              Offset in @ctl_reg for the drive strength configuration.
@@ -56,6 +57,7 @@ struct msm_function {
 * @intr_detection_width: Number of bits used for specifying interrupt type,
 *                        Should be 2 for SoCs that can detect both edges in hardware,
 *                        otherwise 1.
 * @dir_conn_en_bit:      Offset in @intr_cfg_reg for direct connect enable bit
 * @wake_reg:             Offset of the WAKEUP_INT_EN register from base tile
 * @wake_bit:             Bit number for the corresponding gpio
 */
@@ -72,6 +74,7 @@ struct msm_pingroup {
	u32 intr_cfg_reg;
	u32 intr_status_reg;
	u32 intr_target_reg;
	u32 dir_conn_reg;

	unsigned int tile:2;

@@ -96,11 +99,22 @@ struct msm_pingroup {
	unsigned intr_polarity_bit:5;
	unsigned intr_detection_bit:5;
	unsigned intr_detection_width:5;
	unsigned dir_conn_en_bit:8;

	u32 wake_reg;
	unsigned int wake_bit;
};

/**
 * struct msm_dir_conn - TLMM Direct GPIO connect configuration
 * @gpio:	GPIO pin number
 * @irq:	The GIC interrupt that the pin is connected to
 */
struct msm_dir_conn {
	int gpio;
	int irq;
};

/*
 * struct pinctrl_qup - Qup mode configuration
 * @mode:	Qup i3c mode
@@ -133,6 +147,7 @@ struct msm_gpio_wakeirq_map {
 * @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
 * @dir_conn:       An array describing all the pins directly connected to GIC.
 */
struct msm_pinctrl_soc_data {
	const struct pinctrl_pin_desc *pins;
@@ -150,6 +165,7 @@ struct msm_pinctrl_soc_data {
	unsigned int nwakeirq_map;
	struct pinctrl_qup *qup_regs;
	unsigned int nqup_regs;
	struct msm_dir_conn *dir_conn;
};

extern const struct dev_pm_ops msm_pinctrl_dev_pm_ops;
+33 −0
Original line number Diff line number Diff line
@@ -22,6 +22,11 @@ enum {
	WEST
};

#define HMSS_WEST	0x031BB000
#define HMSS_EAST	0x035B7000
#define HMSS_NORTH	0x039BC000
#define HMSS_SOUTH	0x03DBE000

#define FUNCTION(fname)					\
	[msm_mux_##fname] = {				\
		.name = #fname,				\
@@ -53,6 +58,10 @@ enum {
		.intr_status_reg = 0x1000 * id + 0xc,	\
		.intr_target_reg = 0x1000 * id + 0x8,	\
		.tile = _tile,			\
		.dir_conn_reg = _tile == WEST ? HMSS_WEST : \
				_tile == EAST ? HMSS_EAST : \
				_tile == NORTH ? HMSS_NORTH : \
				HMSS_SOUTH, \
		.mux_bit = 2,			\
		.pull_bit = 0,			\
		.drv_bit = 6,			\
@@ -69,6 +78,7 @@ enum {
		.intr_polarity_bit = 1,		\
		.intr_detection_bit = 2,	\
		.intr_detection_width = 2,	\
		.dir_conn_en_bit = 8,		\
	}

#define SDC_QDSD_PINGROUP(pg_name, ctl, pull, drv)	\
@@ -1502,6 +1512,26 @@ static const struct msm_pingroup sm8150_groups[] = {
	[178] = SDC_QDSD_PINGROUP(sdc2_data, 0xB2000, 9, 0),
};

static const struct msm_gpio_wakeirq_map sm8150_pdc_map[] = {
	{ 3, 31 }, { 5, 32 }, { 8, 33 }, { 9, 34 }, { 10, 100 },
	{ 24, 37 }, { 26, 38 }, { 27, 41 }, { 28, 42 }, { 30, 39 },
	{ 37, 44 }, { 38, 30 }, { 39, 118 }, { 41, 47 }, { 42, 48 },
	{ 47, 49 }, { 48, 51 }, { 49, 53 }, { 50, 52 }, { 51, 116 },
	{ 54, 55 }, { 55, 56 }, { 56, 57 }, { 58, 58 }, { 60, 60 },
	{ 68, 62 }, { 70, 63 }, { 76, 71 }, { 77, 66 }, { 81, 64 },
	{ 86, 67 }, { 87, 84 }, { 88, 117 }, { 90, 69 }, { 91, 70 },
	{ 95, 72 }, { 96, 73 }, { 97, 74 }, { 101, 40 }, { 103, 77 },
	{ 108, 79 }, { 112, 80 }, { 113, 81 }, { 114, 82 }, { 117, 85 },
	{ 119, 87 }, { 120, 88 }, { 121, 89 }, { 122, 90 }, { 123, 91 },
	{ 125, 93 }, { 129, 94 }, { 132, 105 }, { 133, 83 }, { 134, 36 },
	{ 142, 103 }, { 144, 115 }, { 147, 102 }, { 150, 107 }, { 152, 108 },
};

static struct msm_dir_conn sm8150_dir_conn[] = {
	  {-1, 0}, {-1, 0}, {-1, 0}, {-1, 0}, {-1, 0},
	  {-1, 0}, {-1, 0}, {-1, 0}, {-1, 0}
};

static const struct msm_pinctrl_soc_data sm8150_pinctrl = {
	.pins = sm8150_pins,
	.npins = ARRAY_SIZE(sm8150_pins),
@@ -1512,6 +1542,9 @@ static const struct msm_pinctrl_soc_data sm8150_pinctrl = {
	.ngpios = 176,
	.tiles = sm8150_tiles,
	.ntiles = ARRAY_SIZE(sm8150_tiles),
	.wakeirq_map = sm8150_pdc_map,
	.nwakeirq_map = ARRAY_SIZE(sm8150_pdc_map),
	.dir_conn = sm8150_dir_conn,
};

static int sm8150_pinctrl_probe(struct platform_device *pdev)