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

Commit e6f61cc0 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "pinctrl: qcom: tlmm: Add support for PDC muxes"

parents 11f2cc4d 892c01c3
Loading
Loading
Loading
Loading
+153 −41
Original line number Diff line number Diff line
/*
 * Copyright (c) 2013, Sony Mobile Communications AB.
 * Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
 * Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -70,6 +70,7 @@ struct msm_pinctrl {

	const struct msm_pinctrl_soc_data *soc;
	void __iomem *regs;
	void __iomem *pdc_regs;
};

static int msm_get_groups_count(struct pinctrl_dev *pctldev)
@@ -777,6 +778,102 @@ static struct irq_chip msm_gpio_irq_chip = {
	.irq_set_wake   = msm_gpio_irq_set_wake,
};

static struct irq_chip msm_dirconn_irq_chip;

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);

	chained_irq_enter(chip, desc);
	generic_handle_irq(irqd->irq);
	chained_irq_exit(chip, desc);
}

static void setup_pdc_gpio(struct irq_domain *domain,
			unsigned int parent_irq, unsigned int gpio)
{
	int irq;

	if (gpio != 0) {
		irq = irq_find_mapping(domain, gpio);
		irq_set_parent(irq, parent_irq);
		irq_set_chip(irq, &msm_dirconn_irq_chip);
		irq_set_handler_data(parent_irq, irq_get_irq_data(irq));
	}

	__irq_set_handler(parent_irq, msm_gpio_dirconn_handler, false, NULL);
}

static void request_dc_interrupt(struct irq_domain *domain,
			struct irq_domain *parent, irq_hw_number_t hwirq,
			unsigned int gpio)
{
	struct irq_fwspec fwspec;
	unsigned int parent_irq;

	fwspec.fwnode = parent->fwnode;
	fwspec.param[0] = 0; /* SPI */
	fwspec.param[1] = hwirq;
	fwspec.param[2] = IRQ_TYPE_NONE;
	fwspec.param_count = 3;

	parent_irq = irq_create_fwspec_mapping(&fwspec);

	setup_pdc_gpio(domain, parent_irq, gpio);
}

/**
 * gpio_muxed_to_pdc: Mux the GPIO to a PDC IRQ
 *
 * @pdc_domain: the PDC's domain
 * @d: the GPIO's IRQ data
 *
 * Find a free PDC port for the GPIO and map the GPIO's mux information to the
 * PDC registers; so the GPIO can be used a wakeup source.
 */
static void gpio_muxed_to_pdc(struct irq_domain *pdc_domain, struct irq_data *d)
{
	int i, j;
	unsigned int mux;
	struct irq_desc *desc = irq_data_to_desc(d);
	struct irq_data *parent_data = irq_get_irq_data(desc->parent_irq);
	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
	unsigned int gpio = d->hwirq;
	struct msm_pinctrl *pctrl;
	unsigned int irq;

	if (!gc || !parent_data)
		return;

	pctrl = gpiochip_get_data(gc);

	for (i = 0; i < pctrl->soc->n_gpio_mux_in; i++) {
		if (gpio != pctrl->soc->gpio_mux_in[i].gpio)
			continue;
		mux = pctrl->soc->gpio_mux_in[i].mux;
		for (j = 0; j < pctrl->soc->n_pdc_mux_out; j++) {
			struct msm_pdc_mux_output *pdc_out =
						&pctrl->soc->pdc_mux_out[j];

			if (pdc_out->mux == mux)
				break;
			if (pdc_out->mux)
				continue;
			pdc_out->mux = gpio;
			irq = irq_find_mapping(pdc_domain, pdc_out->hwirq + 32);
			/* setup the IRQ parent for the GPIO */
			setup_pdc_gpio(pctrl->chip.irqdomain, irq, gpio);
			/* program pdc select grp register */
			writel_relaxed((mux & 0x3F), pctrl->pdc_regs +
				(0x14 * j));
			break;
		}
		/* We have no more PDC port available */
		WARN_ON(j == pctrl->soc->n_pdc_mux_out);
	}
}

static bool is_gpio_dual_edge(struct irq_data *d, irq_hw_number_t *dir_conn_irq)
{
	struct irq_desc *desc = irq_data_to_desc(d);
@@ -797,6 +894,17 @@ static bool is_gpio_dual_edge(struct irq_data *d, irq_hw_number_t *dir_conn_irq)
			return true;
		}
	}

	for (i = 0; i < pctrl->soc->n_pdc_mux_out; i++) {
		struct msm_pdc_mux_output *dir_conn =
					&pctrl->soc->pdc_mux_out[i];

		if (dir_conn->mux == d->hwirq && (dir_conn->hwirq + 32)
				!= parent_data->hwirq) {
			*dir_conn_irq = dir_conn->hwirq + 32;
			return true;
		}
	}
	return false;
}

@@ -814,9 +922,12 @@ static void msm_dirconn_irq_mask(struct irq_data *d)
			irq_get_irq_data(irq_find_mapping(parent_data->domain,
						dir_conn_irq));

		if (dir_conn_data && dir_conn_data->chip->irq_mask)
		if (!dir_conn_data)
			return;
		if (dir_conn_data->chip->irq_mask)
			dir_conn_data->chip->irq_mask(dir_conn_data);
	}

	if (parent_data->chip->irq_mask)
		parent_data->chip->irq_mask(parent_data);
}
@@ -835,7 +946,9 @@ static void msm_dirconn_irq_unmask(struct irq_data *d)
			irq_get_irq_data(irq_find_mapping(parent_data->domain,
						dir_conn_irq));

		if (dir_conn_data && dir_conn_data->chip->irq_unmask)
		if (!dir_conn_data)
			return;
		if (dir_conn_data->chip->irq_unmask)
			dir_conn_data->chip->irq_unmask(dir_conn_data);
	}
	if (parent_data->chip->irq_unmask)
@@ -1103,57 +1216,53 @@ static void msm_gpio_irq_handler(struct irq_desc *desc)
	chained_irq_exit(chip, desc);
}

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);

	chained_irq_enter(chip, desc);
	generic_handle_irq(irqd->irq);
	chained_irq_exit(chip, desc);
}

static void msm_gpio_setup_dir_connects(struct msm_pinctrl *pctrl)
{
	struct device_node *parent_node;
	struct irq_domain *parent_domain;
	struct irq_fwspec fwspec;
	struct irq_domain *pdc_domain;
	unsigned int i;

	parent_node = of_irq_find_parent(pctrl->dev->of_node);

	if (!parent_node)
		return;

	parent_domain = irq_find_host(parent_node);
	if (!parent_domain)
	pdc_domain = irq_find_host(parent_node);
	if (!pdc_domain)
		return;

	fwspec.fwnode = parent_domain->fwnode;
	for (i = 0; i < pctrl->soc->n_dir_conns; i++) {
		const struct msm_dir_conn *dirconn = &pctrl->soc->dir_conn[i];
		unsigned int parent_irq;
		int irq;

		fwspec.param[0] = 0; /* SPI */
		fwspec.param[1] = dirconn->hwirq;
		fwspec.param[2] = IRQ_TYPE_NONE;
		fwspec.param_count = 3;
		parent_irq = irq_create_fwspec_mapping(&fwspec);
		request_dc_interrupt(pctrl->chip.irqdomain, pdc_domain,
					dirconn->hwirq, dirconn->gpio);
	}

		if (dirconn->gpio != 0) {
			irq = irq_find_mapping(pctrl->chip.irqdomain,
					dirconn->gpio);
	for (i = 0; i < pctrl->soc->n_pdc_mux_out; i++) {
		struct msm_pdc_mux_output *pdc_out =
					&pctrl->soc->pdc_mux_out[i];

			irq_set_parent(irq, parent_irq);
			irq_set_chip(irq, &msm_dirconn_irq_chip);
			__irq_set_handler(parent_irq, msm_gpio_dirconn_handler,
				false, NULL);
			irq_set_handler_data(parent_irq, irq_get_irq_data(irq));
		} else {
			__irq_set_handler(parent_irq, msm_gpio_dirconn_handler,
				false, NULL);
		request_dc_interrupt(pctrl->chip.irqdomain, pdc_domain,
					pdc_out->hwirq, 0);
	}

	/*
	 * Statically choose the GPIOs for mapping to PDC. Dynamic mux mapping
	 * is very difficult.
	 */
	for (i = 0; i < pctrl->soc->n_pdc_mux_out; i++) {
		unsigned int irq;
		struct irq_data *d;
		struct msm_gpio_mux_input *gpio_in =
					&pctrl->soc->gpio_mux_in[i];
		if (!gpio_in->init)
			continue;

		irq = irq_find_mapping(pctrl->chip.irqdomain, gpio_in->gpio);
		d = irq_get_irq_data(irq);
		if (!d)
			continue;

		gpio_muxed_to_pdc(pdc_domain, d);
	}
}

@@ -1263,6 +1372,9 @@ int msm_pinctrl_probe(struct platform_device *pdev,
	if (IS_ERR(pctrl->regs))
		return PTR_ERR(pctrl->regs);

	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
	pctrl->pdc_regs = devm_ioremap_resource(&pdev->dev, res);

	msm_pinctrl_setup_pm_reset(pctrl);

	pctrl->irq = platform_get_irq(pdev, 0);
+35 −3
Original line number Diff line number Diff line
/*
 * Copyright (c) 2013, Sony Mobile Communications AB.
 * Copyright (c) 2018, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -99,13 +100,35 @@ struct msm_pingroup {
	unsigned intr_detection_bit:5;
	unsigned intr_detection_width:5;
	unsigned dir_conn_en_bit:8;
}
};

/**
 * struct msm_gpio_mux_input - Map GPIO to Mux pin
 * @mux::	The mux pin to which the GPIO is connected to
 * @gpio:	GPIO pin number
 * @init:	Setup PDC connection at probe
 */
struct msm_gpio_mux_input {
	unsigned int mux;
	unsigned int gpio;
	bool init;
};

/**
 * struct msm_pdc_mux_output - GPIO mux pin to PDC port
 * @mux:	GPIO mux pin number
 * @hwirq:	The PDC port (hwirq) that GPIO is connected to
 */
struct msm_pdc_mux_output {
	unsigned int mux;
	irq_hw_number_t hwirq;
};

/**
 * struct msm_dir_conn - Direct GPIO connect configuration
 * @gpio:	GPIO pin number
 * @hwirq:	The GIC interrupt that the pin is connected to
 */;
 */
struct msm_dir_conn {
	unsigned int gpio;
	irq_hw_number_t hwirq;
@@ -122,8 +145,12 @@ struct msm_dir_conn {
 * @ngpio:      The number of pingroups the driver should expose as GPIOs.
 * @dir_conn:   An array describing all the pins directly connected to GIC.
 * @ndirconns:  The number of pins directly connected to GIC
 * @dir_conn_offsets:   Direct connect register offsets for each tile.
 * @dir_conn_irq_base:  Direct connect interrupt base register for kpss.
 * @gpio_mux_in:	Map of GPIO pin to the hwirq.
 * @n_gpioc_mux_in:	The number of entries in @pdc_mux_in.
 * @pdc_mux_out:	Map of GPIO mux to PDC port.
 * @n_pdc_mux_out:	The number of entries in @pdc_mux_out.
 * @n_pdc_offset:	The offset for the PDC mux pins
 */
struct msm_pinctrl_soc_data {
	const struct pinctrl_pin_desc *pins;
@@ -136,6 +163,11 @@ struct msm_pinctrl_soc_data {
	const struct msm_dir_conn *dir_conn;
	unsigned int n_dir_conns;
	unsigned int dir_conn_irq_base;
	struct msm_pdc_mux_output *pdc_mux_out;
	unsigned int n_pdc_mux_out;
	struct msm_gpio_mux_input *gpio_mux_in;
	unsigned int n_gpio_mux_in;
	unsigned int n_pdc_mux_offset;
};

int msm_pinctrl_probe(struct platform_device *pdev,