18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2013, Sony Mobile Communications AB.
48c2ecf20Sopenharmony_ci * Copyright (c) 2013, The Linux Foundation. All rights reserved.
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/delay.h>
88c2ecf20Sopenharmony_ci#include <linux/err.h>
98c2ecf20Sopenharmony_ci#include <linux/io.h>
108c2ecf20Sopenharmony_ci#include <linux/module.h>
118c2ecf20Sopenharmony_ci#include <linux/of.h>
128c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
138c2ecf20Sopenharmony_ci#include <linux/pinctrl/machine.h>
148c2ecf20Sopenharmony_ci#include <linux/pinctrl/pinctrl.h>
158c2ecf20Sopenharmony_ci#include <linux/pinctrl/pinmux.h>
168c2ecf20Sopenharmony_ci#include <linux/pinctrl/pinconf.h>
178c2ecf20Sopenharmony_ci#include <linux/pinctrl/pinconf-generic.h>
188c2ecf20Sopenharmony_ci#include <linux/slab.h>
198c2ecf20Sopenharmony_ci#include <linux/gpio/driver.h>
208c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
218c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
228c2ecf20Sopenharmony_ci#include <linux/reboot.h>
238c2ecf20Sopenharmony_ci#include <linux/pm.h>
248c2ecf20Sopenharmony_ci#include <linux/log2.h>
258c2ecf20Sopenharmony_ci#include <linux/qcom_scm.h>
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#include <linux/soc/qcom/irq.h>
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#include "../core.h"
308c2ecf20Sopenharmony_ci#include "../pinconf.h"
318c2ecf20Sopenharmony_ci#include "pinctrl-msm.h"
328c2ecf20Sopenharmony_ci#include "../pinctrl-utils.h"
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#define MAX_NR_GPIO 300
358c2ecf20Sopenharmony_ci#define MAX_NR_TILES 4
368c2ecf20Sopenharmony_ci#define PS_HOLD_OFFSET 0x820
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci/**
398c2ecf20Sopenharmony_ci * struct msm_pinctrl - state for a pinctrl-msm device
408c2ecf20Sopenharmony_ci * @dev:            device handle.
418c2ecf20Sopenharmony_ci * @pctrl:          pinctrl handle.
428c2ecf20Sopenharmony_ci * @chip:           gpiochip handle.
438c2ecf20Sopenharmony_ci * @desc:           pin controller descriptor
448c2ecf20Sopenharmony_ci * @restart_nb:     restart notifier block.
458c2ecf20Sopenharmony_ci * @irq_chip:       irq chip information
468c2ecf20Sopenharmony_ci * @irq:            parent irq for the TLMM irq_chip.
478c2ecf20Sopenharmony_ci * @intr_target_use_scm: route irq to application cpu using scm calls
488c2ecf20Sopenharmony_ci * @lock:           Spinlock to protect register resources as well
498c2ecf20Sopenharmony_ci *                  as msm_pinctrl data structures.
508c2ecf20Sopenharmony_ci * @enabled_irqs:   Bitmap of currently enabled irqs.
518c2ecf20Sopenharmony_ci * @dual_edge_irqs: Bitmap of irqs that need sw emulated dual edge
528c2ecf20Sopenharmony_ci *                  detection.
538c2ecf20Sopenharmony_ci * @skip_wake_irqs: Skip IRQs that are handled by wakeup interrupt controller
548c2ecf20Sopenharmony_ci * @disabled_for_mux: These IRQs were disabled because we muxed away.
558c2ecf20Sopenharmony_ci * @soc:            Reference to soc_data of platform specific data.
568c2ecf20Sopenharmony_ci * @regs:           Base addresses for the TLMM tiles.
578c2ecf20Sopenharmony_ci * @phys_base:      Physical base address
588c2ecf20Sopenharmony_ci */
598c2ecf20Sopenharmony_cistruct msm_pinctrl {
608c2ecf20Sopenharmony_ci	struct device *dev;
618c2ecf20Sopenharmony_ci	struct pinctrl_dev *pctrl;
628c2ecf20Sopenharmony_ci	struct gpio_chip chip;
638c2ecf20Sopenharmony_ci	struct pinctrl_desc desc;
648c2ecf20Sopenharmony_ci	struct notifier_block restart_nb;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	struct irq_chip irq_chip;
678c2ecf20Sopenharmony_ci	int irq;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	bool intr_target_use_scm;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	raw_spinlock_t lock;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	DECLARE_BITMAP(dual_edge_irqs, MAX_NR_GPIO);
748c2ecf20Sopenharmony_ci	DECLARE_BITMAP(enabled_irqs, MAX_NR_GPIO);
758c2ecf20Sopenharmony_ci	DECLARE_BITMAP(skip_wake_irqs, MAX_NR_GPIO);
768c2ecf20Sopenharmony_ci	DECLARE_BITMAP(disabled_for_mux, MAX_NR_GPIO);
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	const struct msm_pinctrl_soc_data *soc;
798c2ecf20Sopenharmony_ci	void __iomem *regs[MAX_NR_TILES];
808c2ecf20Sopenharmony_ci	u32 phys_base[MAX_NR_TILES];
818c2ecf20Sopenharmony_ci};
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci#define MSM_ACCESSOR(name) \
848c2ecf20Sopenharmony_cistatic u32 msm_readl_##name(struct msm_pinctrl *pctrl, \
858c2ecf20Sopenharmony_ci			    const struct msm_pingroup *g) \
868c2ecf20Sopenharmony_ci{ \
878c2ecf20Sopenharmony_ci	return readl(pctrl->regs[g->tile] + g->name##_reg); \
888c2ecf20Sopenharmony_ci} \
898c2ecf20Sopenharmony_cistatic void msm_writel_##name(u32 val, struct msm_pinctrl *pctrl, \
908c2ecf20Sopenharmony_ci			      const struct msm_pingroup *g) \
918c2ecf20Sopenharmony_ci{ \
928c2ecf20Sopenharmony_ci	writel(val, pctrl->regs[g->tile] + g->name##_reg); \
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ciMSM_ACCESSOR(ctl)
968c2ecf20Sopenharmony_ciMSM_ACCESSOR(io)
978c2ecf20Sopenharmony_ciMSM_ACCESSOR(intr_cfg)
988c2ecf20Sopenharmony_ciMSM_ACCESSOR(intr_status)
998c2ecf20Sopenharmony_ciMSM_ACCESSOR(intr_target)
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_cistatic void msm_ack_intr_status(struct msm_pinctrl *pctrl,
1028c2ecf20Sopenharmony_ci				const struct msm_pingroup *g)
1038c2ecf20Sopenharmony_ci{
1048c2ecf20Sopenharmony_ci	u32 val = g->intr_ack_high ? BIT(g->intr_status_bit) : 0;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	msm_writel_intr_status(val, pctrl, g);
1078c2ecf20Sopenharmony_ci}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_cistatic int msm_get_groups_count(struct pinctrl_dev *pctldev)
1108c2ecf20Sopenharmony_ci{
1118c2ecf20Sopenharmony_ci	struct msm_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	return pctrl->soc->ngroups;
1148c2ecf20Sopenharmony_ci}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_cistatic const char *msm_get_group_name(struct pinctrl_dev *pctldev,
1178c2ecf20Sopenharmony_ci				      unsigned group)
1188c2ecf20Sopenharmony_ci{
1198c2ecf20Sopenharmony_ci	struct msm_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	return pctrl->soc->groups[group].name;
1228c2ecf20Sopenharmony_ci}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_cistatic int msm_get_group_pins(struct pinctrl_dev *pctldev,
1258c2ecf20Sopenharmony_ci			      unsigned group,
1268c2ecf20Sopenharmony_ci			      const unsigned **pins,
1278c2ecf20Sopenharmony_ci			      unsigned *num_pins)
1288c2ecf20Sopenharmony_ci{
1298c2ecf20Sopenharmony_ci	struct msm_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	*pins = pctrl->soc->groups[group].pins;
1328c2ecf20Sopenharmony_ci	*num_pins = pctrl->soc->groups[group].npins;
1338c2ecf20Sopenharmony_ci	return 0;
1348c2ecf20Sopenharmony_ci}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_cistatic const struct pinctrl_ops msm_pinctrl_ops = {
1378c2ecf20Sopenharmony_ci	.get_groups_count	= msm_get_groups_count,
1388c2ecf20Sopenharmony_ci	.get_group_name		= msm_get_group_name,
1398c2ecf20Sopenharmony_ci	.get_group_pins		= msm_get_group_pins,
1408c2ecf20Sopenharmony_ci	.dt_node_to_map		= pinconf_generic_dt_node_to_map_group,
1418c2ecf20Sopenharmony_ci	.dt_free_map		= pinctrl_utils_free_map,
1428c2ecf20Sopenharmony_ci};
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_cistatic int msm_pinmux_request(struct pinctrl_dev *pctldev, unsigned offset)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	struct msm_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
1478c2ecf20Sopenharmony_ci	struct gpio_chip *chip = &pctrl->chip;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	return gpiochip_line_is_valid(chip, offset) ? 0 : -EINVAL;
1508c2ecf20Sopenharmony_ci}
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_cistatic int msm_get_functions_count(struct pinctrl_dev *pctldev)
1538c2ecf20Sopenharmony_ci{
1548c2ecf20Sopenharmony_ci	struct msm_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	return pctrl->soc->nfunctions;
1578c2ecf20Sopenharmony_ci}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_cistatic const char *msm_get_function_name(struct pinctrl_dev *pctldev,
1608c2ecf20Sopenharmony_ci					 unsigned function)
1618c2ecf20Sopenharmony_ci{
1628c2ecf20Sopenharmony_ci	struct msm_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	return pctrl->soc->functions[function].name;
1658c2ecf20Sopenharmony_ci}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_cistatic int msm_get_function_groups(struct pinctrl_dev *pctldev,
1688c2ecf20Sopenharmony_ci				   unsigned function,
1698c2ecf20Sopenharmony_ci				   const char * const **groups,
1708c2ecf20Sopenharmony_ci				   unsigned * const num_groups)
1718c2ecf20Sopenharmony_ci{
1728c2ecf20Sopenharmony_ci	struct msm_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	*groups = pctrl->soc->functions[function].groups;
1758c2ecf20Sopenharmony_ci	*num_groups = pctrl->soc->functions[function].ngroups;
1768c2ecf20Sopenharmony_ci	return 0;
1778c2ecf20Sopenharmony_ci}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_cistatic int msm_pinmux_set_mux(struct pinctrl_dev *pctldev,
1808c2ecf20Sopenharmony_ci			      unsigned function,
1818c2ecf20Sopenharmony_ci			      unsigned group)
1828c2ecf20Sopenharmony_ci{
1838c2ecf20Sopenharmony_ci	struct msm_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
1848c2ecf20Sopenharmony_ci	struct gpio_chip *gc = &pctrl->chip;
1858c2ecf20Sopenharmony_ci	unsigned int irq = irq_find_mapping(gc->irq.domain, group);
1868c2ecf20Sopenharmony_ci	struct irq_data *d = irq_get_irq_data(irq);
1878c2ecf20Sopenharmony_ci	unsigned int gpio_func = pctrl->soc->gpio_func;
1888c2ecf20Sopenharmony_ci	const struct msm_pingroup *g;
1898c2ecf20Sopenharmony_ci	unsigned long flags;
1908c2ecf20Sopenharmony_ci	u32 val, mask;
1918c2ecf20Sopenharmony_ci	int i;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	g = &pctrl->soc->groups[group];
1948c2ecf20Sopenharmony_ci	mask = GENMASK(g->mux_bit + order_base_2(g->nfuncs) - 1, g->mux_bit);
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	for (i = 0; i < g->nfuncs; i++) {
1978c2ecf20Sopenharmony_ci		if (g->funcs[i] == function)
1988c2ecf20Sopenharmony_ci			break;
1998c2ecf20Sopenharmony_ci	}
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	if (WARN_ON(i == g->nfuncs))
2028c2ecf20Sopenharmony_ci		return -EINVAL;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	/*
2058c2ecf20Sopenharmony_ci	 * If an GPIO interrupt is setup on this pin then we need special
2068c2ecf20Sopenharmony_ci	 * handling.  Specifically interrupt detection logic will still see
2078c2ecf20Sopenharmony_ci	 * the pin twiddle even when we're muxed away.
2088c2ecf20Sopenharmony_ci	 *
2098c2ecf20Sopenharmony_ci	 * When we see a pin with an interrupt setup on it then we'll disable
2108c2ecf20Sopenharmony_ci	 * (mask) interrupts on it when we mux away until we mux back.  Note
2118c2ecf20Sopenharmony_ci	 * that disable_irq() refcounts and interrupts are disabled as long as
2128c2ecf20Sopenharmony_ci	 * at least one disable_irq() has been called.
2138c2ecf20Sopenharmony_ci	 */
2148c2ecf20Sopenharmony_ci	if (d && i != gpio_func &&
2158c2ecf20Sopenharmony_ci	    !test_and_set_bit(d->hwirq, pctrl->disabled_for_mux))
2168c2ecf20Sopenharmony_ci		disable_irq(irq);
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&pctrl->lock, flags);
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	val = msm_readl_ctl(pctrl, g);
2218c2ecf20Sopenharmony_ci	val &= ~mask;
2228c2ecf20Sopenharmony_ci	val |= i << g->mux_bit;
2238c2ecf20Sopenharmony_ci	msm_writel_ctl(val, pctrl, g);
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&pctrl->lock, flags);
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	if (d && i == gpio_func &&
2288c2ecf20Sopenharmony_ci	    test_and_clear_bit(d->hwirq, pctrl->disabled_for_mux)) {
2298c2ecf20Sopenharmony_ci		/*
2308c2ecf20Sopenharmony_ci		 * Clear interrupts detected while not GPIO since we only
2318c2ecf20Sopenharmony_ci		 * masked things.
2328c2ecf20Sopenharmony_ci		 */
2338c2ecf20Sopenharmony_ci		if (d->parent_data && test_bit(d->hwirq, pctrl->skip_wake_irqs))
2348c2ecf20Sopenharmony_ci			irq_chip_set_parent_state(d, IRQCHIP_STATE_PENDING, false);
2358c2ecf20Sopenharmony_ci		else
2368c2ecf20Sopenharmony_ci			msm_ack_intr_status(pctrl, g);
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci		enable_irq(irq);
2398c2ecf20Sopenharmony_ci	}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	return 0;
2428c2ecf20Sopenharmony_ci}
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_cistatic int msm_pinmux_request_gpio(struct pinctrl_dev *pctldev,
2458c2ecf20Sopenharmony_ci				   struct pinctrl_gpio_range *range,
2468c2ecf20Sopenharmony_ci				   unsigned offset)
2478c2ecf20Sopenharmony_ci{
2488c2ecf20Sopenharmony_ci	struct msm_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
2498c2ecf20Sopenharmony_ci	const struct msm_pingroup *g = &pctrl->soc->groups[offset];
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	/* No funcs? Probably ACPI so can't do anything here */
2528c2ecf20Sopenharmony_ci	if (!g->nfuncs)
2538c2ecf20Sopenharmony_ci		return 0;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	return msm_pinmux_set_mux(pctldev, g->funcs[pctrl->soc->gpio_func], offset);
2568c2ecf20Sopenharmony_ci}
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_cistatic const struct pinmux_ops msm_pinmux_ops = {
2598c2ecf20Sopenharmony_ci	.request		= msm_pinmux_request,
2608c2ecf20Sopenharmony_ci	.get_functions_count	= msm_get_functions_count,
2618c2ecf20Sopenharmony_ci	.get_function_name	= msm_get_function_name,
2628c2ecf20Sopenharmony_ci	.get_function_groups	= msm_get_function_groups,
2638c2ecf20Sopenharmony_ci	.gpio_request_enable	= msm_pinmux_request_gpio,
2648c2ecf20Sopenharmony_ci	.set_mux		= msm_pinmux_set_mux,
2658c2ecf20Sopenharmony_ci};
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_cistatic int msm_config_reg(struct msm_pinctrl *pctrl,
2688c2ecf20Sopenharmony_ci			  const struct msm_pingroup *g,
2698c2ecf20Sopenharmony_ci			  unsigned param,
2708c2ecf20Sopenharmony_ci			  unsigned *mask,
2718c2ecf20Sopenharmony_ci			  unsigned *bit)
2728c2ecf20Sopenharmony_ci{
2738c2ecf20Sopenharmony_ci	switch (param) {
2748c2ecf20Sopenharmony_ci	case PIN_CONFIG_BIAS_DISABLE:
2758c2ecf20Sopenharmony_ci	case PIN_CONFIG_BIAS_PULL_DOWN:
2768c2ecf20Sopenharmony_ci	case PIN_CONFIG_BIAS_BUS_HOLD:
2778c2ecf20Sopenharmony_ci	case PIN_CONFIG_BIAS_PULL_UP:
2788c2ecf20Sopenharmony_ci		*bit = g->pull_bit;
2798c2ecf20Sopenharmony_ci		*mask = 3;
2808c2ecf20Sopenharmony_ci		break;
2818c2ecf20Sopenharmony_ci	case PIN_CONFIG_DRIVE_OPEN_DRAIN:
2828c2ecf20Sopenharmony_ci		*bit = g->od_bit;
2838c2ecf20Sopenharmony_ci		*mask = 1;
2848c2ecf20Sopenharmony_ci		break;
2858c2ecf20Sopenharmony_ci	case PIN_CONFIG_DRIVE_STRENGTH:
2868c2ecf20Sopenharmony_ci		*bit = g->drv_bit;
2878c2ecf20Sopenharmony_ci		*mask = 7;
2888c2ecf20Sopenharmony_ci		break;
2898c2ecf20Sopenharmony_ci	case PIN_CONFIG_OUTPUT:
2908c2ecf20Sopenharmony_ci	case PIN_CONFIG_INPUT_ENABLE:
2918c2ecf20Sopenharmony_ci		*bit = g->oe_bit;
2928c2ecf20Sopenharmony_ci		*mask = 1;
2938c2ecf20Sopenharmony_ci		break;
2948c2ecf20Sopenharmony_ci	default:
2958c2ecf20Sopenharmony_ci		return -ENOTSUPP;
2968c2ecf20Sopenharmony_ci	}
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	return 0;
2998c2ecf20Sopenharmony_ci}
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci#define MSM_NO_PULL		0
3028c2ecf20Sopenharmony_ci#define MSM_PULL_DOWN		1
3038c2ecf20Sopenharmony_ci#define MSM_KEEPER		2
3048c2ecf20Sopenharmony_ci#define MSM_PULL_UP_NO_KEEPER	2
3058c2ecf20Sopenharmony_ci#define MSM_PULL_UP		3
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_cistatic unsigned msm_regval_to_drive(u32 val)
3088c2ecf20Sopenharmony_ci{
3098c2ecf20Sopenharmony_ci	return (val + 1) * 2;
3108c2ecf20Sopenharmony_ci}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_cistatic int msm_config_group_get(struct pinctrl_dev *pctldev,
3138c2ecf20Sopenharmony_ci				unsigned int group,
3148c2ecf20Sopenharmony_ci				unsigned long *config)
3158c2ecf20Sopenharmony_ci{
3168c2ecf20Sopenharmony_ci	const struct msm_pingroup *g;
3178c2ecf20Sopenharmony_ci	struct msm_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
3188c2ecf20Sopenharmony_ci	unsigned param = pinconf_to_config_param(*config);
3198c2ecf20Sopenharmony_ci	unsigned mask;
3208c2ecf20Sopenharmony_ci	unsigned arg;
3218c2ecf20Sopenharmony_ci	unsigned bit;
3228c2ecf20Sopenharmony_ci	int ret;
3238c2ecf20Sopenharmony_ci	u32 val;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	g = &pctrl->soc->groups[group];
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	ret = msm_config_reg(pctrl, g, param, &mask, &bit);
3288c2ecf20Sopenharmony_ci	if (ret < 0)
3298c2ecf20Sopenharmony_ci		return ret;
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	val = msm_readl_ctl(pctrl, g);
3328c2ecf20Sopenharmony_ci	arg = (val >> bit) & mask;
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	/* Convert register value to pinconf value */
3358c2ecf20Sopenharmony_ci	switch (param) {
3368c2ecf20Sopenharmony_ci	case PIN_CONFIG_BIAS_DISABLE:
3378c2ecf20Sopenharmony_ci		if (arg != MSM_NO_PULL)
3388c2ecf20Sopenharmony_ci			return -EINVAL;
3398c2ecf20Sopenharmony_ci		arg = 1;
3408c2ecf20Sopenharmony_ci		break;
3418c2ecf20Sopenharmony_ci	case PIN_CONFIG_BIAS_PULL_DOWN:
3428c2ecf20Sopenharmony_ci		if (arg != MSM_PULL_DOWN)
3438c2ecf20Sopenharmony_ci			return -EINVAL;
3448c2ecf20Sopenharmony_ci		arg = 1;
3458c2ecf20Sopenharmony_ci		break;
3468c2ecf20Sopenharmony_ci	case PIN_CONFIG_BIAS_BUS_HOLD:
3478c2ecf20Sopenharmony_ci		if (pctrl->soc->pull_no_keeper)
3488c2ecf20Sopenharmony_ci			return -ENOTSUPP;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci		if (arg != MSM_KEEPER)
3518c2ecf20Sopenharmony_ci			return -EINVAL;
3528c2ecf20Sopenharmony_ci		arg = 1;
3538c2ecf20Sopenharmony_ci		break;
3548c2ecf20Sopenharmony_ci	case PIN_CONFIG_BIAS_PULL_UP:
3558c2ecf20Sopenharmony_ci		if (pctrl->soc->pull_no_keeper)
3568c2ecf20Sopenharmony_ci			arg = arg == MSM_PULL_UP_NO_KEEPER;
3578c2ecf20Sopenharmony_ci		else
3588c2ecf20Sopenharmony_ci			arg = arg == MSM_PULL_UP;
3598c2ecf20Sopenharmony_ci		if (!arg)
3608c2ecf20Sopenharmony_ci			return -EINVAL;
3618c2ecf20Sopenharmony_ci		break;
3628c2ecf20Sopenharmony_ci	case PIN_CONFIG_DRIVE_OPEN_DRAIN:
3638c2ecf20Sopenharmony_ci		/* Pin is not open-drain */
3648c2ecf20Sopenharmony_ci		if (!arg)
3658c2ecf20Sopenharmony_ci			return -EINVAL;
3668c2ecf20Sopenharmony_ci		arg = 1;
3678c2ecf20Sopenharmony_ci		break;
3688c2ecf20Sopenharmony_ci	case PIN_CONFIG_DRIVE_STRENGTH:
3698c2ecf20Sopenharmony_ci		arg = msm_regval_to_drive(arg);
3708c2ecf20Sopenharmony_ci		break;
3718c2ecf20Sopenharmony_ci	case PIN_CONFIG_OUTPUT:
3728c2ecf20Sopenharmony_ci		/* Pin is not output */
3738c2ecf20Sopenharmony_ci		if (!arg)
3748c2ecf20Sopenharmony_ci			return -EINVAL;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci		val = msm_readl_io(pctrl, g);
3778c2ecf20Sopenharmony_ci		arg = !!(val & BIT(g->in_bit));
3788c2ecf20Sopenharmony_ci		break;
3798c2ecf20Sopenharmony_ci	case PIN_CONFIG_INPUT_ENABLE:
3808c2ecf20Sopenharmony_ci		/* Pin is output */
3818c2ecf20Sopenharmony_ci		if (arg)
3828c2ecf20Sopenharmony_ci			return -EINVAL;
3838c2ecf20Sopenharmony_ci		arg = 1;
3848c2ecf20Sopenharmony_ci		break;
3858c2ecf20Sopenharmony_ci	default:
3868c2ecf20Sopenharmony_ci		return -ENOTSUPP;
3878c2ecf20Sopenharmony_ci	}
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	*config = pinconf_to_config_packed(param, arg);
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	return 0;
3928c2ecf20Sopenharmony_ci}
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_cistatic int msm_config_group_set(struct pinctrl_dev *pctldev,
3958c2ecf20Sopenharmony_ci				unsigned group,
3968c2ecf20Sopenharmony_ci				unsigned long *configs,
3978c2ecf20Sopenharmony_ci				unsigned num_configs)
3988c2ecf20Sopenharmony_ci{
3998c2ecf20Sopenharmony_ci	const struct msm_pingroup *g;
4008c2ecf20Sopenharmony_ci	struct msm_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
4018c2ecf20Sopenharmony_ci	unsigned long flags;
4028c2ecf20Sopenharmony_ci	unsigned param;
4038c2ecf20Sopenharmony_ci	unsigned mask;
4048c2ecf20Sopenharmony_ci	unsigned arg;
4058c2ecf20Sopenharmony_ci	unsigned bit;
4068c2ecf20Sopenharmony_ci	int ret;
4078c2ecf20Sopenharmony_ci	u32 val;
4088c2ecf20Sopenharmony_ci	int i;
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	g = &pctrl->soc->groups[group];
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	for (i = 0; i < num_configs; i++) {
4138c2ecf20Sopenharmony_ci		param = pinconf_to_config_param(configs[i]);
4148c2ecf20Sopenharmony_ci		arg = pinconf_to_config_argument(configs[i]);
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci		ret = msm_config_reg(pctrl, g, param, &mask, &bit);
4178c2ecf20Sopenharmony_ci		if (ret < 0)
4188c2ecf20Sopenharmony_ci			return ret;
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci		/* Convert pinconf values to register values */
4218c2ecf20Sopenharmony_ci		switch (param) {
4228c2ecf20Sopenharmony_ci		case PIN_CONFIG_BIAS_DISABLE:
4238c2ecf20Sopenharmony_ci			arg = MSM_NO_PULL;
4248c2ecf20Sopenharmony_ci			break;
4258c2ecf20Sopenharmony_ci		case PIN_CONFIG_BIAS_PULL_DOWN:
4268c2ecf20Sopenharmony_ci			arg = MSM_PULL_DOWN;
4278c2ecf20Sopenharmony_ci			break;
4288c2ecf20Sopenharmony_ci		case PIN_CONFIG_BIAS_BUS_HOLD:
4298c2ecf20Sopenharmony_ci			if (pctrl->soc->pull_no_keeper)
4308c2ecf20Sopenharmony_ci				return -ENOTSUPP;
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci			arg = MSM_KEEPER;
4338c2ecf20Sopenharmony_ci			break;
4348c2ecf20Sopenharmony_ci		case PIN_CONFIG_BIAS_PULL_UP:
4358c2ecf20Sopenharmony_ci			if (pctrl->soc->pull_no_keeper)
4368c2ecf20Sopenharmony_ci				arg = MSM_PULL_UP_NO_KEEPER;
4378c2ecf20Sopenharmony_ci			else
4388c2ecf20Sopenharmony_ci				arg = MSM_PULL_UP;
4398c2ecf20Sopenharmony_ci			break;
4408c2ecf20Sopenharmony_ci		case PIN_CONFIG_DRIVE_OPEN_DRAIN:
4418c2ecf20Sopenharmony_ci			arg = 1;
4428c2ecf20Sopenharmony_ci			break;
4438c2ecf20Sopenharmony_ci		case PIN_CONFIG_DRIVE_STRENGTH:
4448c2ecf20Sopenharmony_ci			/* Check for invalid values */
4458c2ecf20Sopenharmony_ci			if (arg > 16 || arg < 2 || (arg % 2) != 0)
4468c2ecf20Sopenharmony_ci				arg = -1;
4478c2ecf20Sopenharmony_ci			else
4488c2ecf20Sopenharmony_ci				arg = (arg / 2) - 1;
4498c2ecf20Sopenharmony_ci			break;
4508c2ecf20Sopenharmony_ci		case PIN_CONFIG_OUTPUT:
4518c2ecf20Sopenharmony_ci			/* set output value */
4528c2ecf20Sopenharmony_ci			raw_spin_lock_irqsave(&pctrl->lock, flags);
4538c2ecf20Sopenharmony_ci			val = msm_readl_io(pctrl, g);
4548c2ecf20Sopenharmony_ci			if (arg)
4558c2ecf20Sopenharmony_ci				val |= BIT(g->out_bit);
4568c2ecf20Sopenharmony_ci			else
4578c2ecf20Sopenharmony_ci				val &= ~BIT(g->out_bit);
4588c2ecf20Sopenharmony_ci			msm_writel_io(val, pctrl, g);
4598c2ecf20Sopenharmony_ci			raw_spin_unlock_irqrestore(&pctrl->lock, flags);
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci			/* enable output */
4628c2ecf20Sopenharmony_ci			arg = 1;
4638c2ecf20Sopenharmony_ci			break;
4648c2ecf20Sopenharmony_ci		case PIN_CONFIG_INPUT_ENABLE:
4658c2ecf20Sopenharmony_ci			/* disable output */
4668c2ecf20Sopenharmony_ci			arg = 0;
4678c2ecf20Sopenharmony_ci			break;
4688c2ecf20Sopenharmony_ci		default:
4698c2ecf20Sopenharmony_ci			dev_err(pctrl->dev, "Unsupported config parameter: %x\n",
4708c2ecf20Sopenharmony_ci				param);
4718c2ecf20Sopenharmony_ci			return -EINVAL;
4728c2ecf20Sopenharmony_ci		}
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci		/* Range-check user-supplied value */
4758c2ecf20Sopenharmony_ci		if (arg & ~mask) {
4768c2ecf20Sopenharmony_ci			dev_err(pctrl->dev, "config %x: %x is invalid\n", param, arg);
4778c2ecf20Sopenharmony_ci			return -EINVAL;
4788c2ecf20Sopenharmony_ci		}
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci		raw_spin_lock_irqsave(&pctrl->lock, flags);
4818c2ecf20Sopenharmony_ci		val = msm_readl_ctl(pctrl, g);
4828c2ecf20Sopenharmony_ci		val &= ~(mask << bit);
4838c2ecf20Sopenharmony_ci		val |= arg << bit;
4848c2ecf20Sopenharmony_ci		msm_writel_ctl(val, pctrl, g);
4858c2ecf20Sopenharmony_ci		raw_spin_unlock_irqrestore(&pctrl->lock, flags);
4868c2ecf20Sopenharmony_ci	}
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	return 0;
4898c2ecf20Sopenharmony_ci}
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_cistatic const struct pinconf_ops msm_pinconf_ops = {
4928c2ecf20Sopenharmony_ci	.is_generic		= true,
4938c2ecf20Sopenharmony_ci	.pin_config_group_get	= msm_config_group_get,
4948c2ecf20Sopenharmony_ci	.pin_config_group_set	= msm_config_group_set,
4958c2ecf20Sopenharmony_ci};
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_cistatic int msm_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
4988c2ecf20Sopenharmony_ci{
4998c2ecf20Sopenharmony_ci	const struct msm_pingroup *g;
5008c2ecf20Sopenharmony_ci	struct msm_pinctrl *pctrl = gpiochip_get_data(chip);
5018c2ecf20Sopenharmony_ci	unsigned long flags;
5028c2ecf20Sopenharmony_ci	u32 val;
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	g = &pctrl->soc->groups[offset];
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&pctrl->lock, flags);
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	val = msm_readl_ctl(pctrl, g);
5098c2ecf20Sopenharmony_ci	val &= ~BIT(g->oe_bit);
5108c2ecf20Sopenharmony_ci	msm_writel_ctl(val, pctrl, g);
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&pctrl->lock, flags);
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	return 0;
5158c2ecf20Sopenharmony_ci}
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_cistatic int msm_gpio_direction_output(struct gpio_chip *chip, unsigned offset, int value)
5188c2ecf20Sopenharmony_ci{
5198c2ecf20Sopenharmony_ci	const struct msm_pingroup *g;
5208c2ecf20Sopenharmony_ci	struct msm_pinctrl *pctrl = gpiochip_get_data(chip);
5218c2ecf20Sopenharmony_ci	unsigned long flags;
5228c2ecf20Sopenharmony_ci	u32 val;
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	g = &pctrl->soc->groups[offset];
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&pctrl->lock, flags);
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	val = msm_readl_io(pctrl, g);
5298c2ecf20Sopenharmony_ci	if (value)
5308c2ecf20Sopenharmony_ci		val |= BIT(g->out_bit);
5318c2ecf20Sopenharmony_ci	else
5328c2ecf20Sopenharmony_ci		val &= ~BIT(g->out_bit);
5338c2ecf20Sopenharmony_ci	msm_writel_io(val, pctrl, g);
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci	val = msm_readl_ctl(pctrl, g);
5368c2ecf20Sopenharmony_ci	val |= BIT(g->oe_bit);
5378c2ecf20Sopenharmony_ci	msm_writel_ctl(val, pctrl, g);
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&pctrl->lock, flags);
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci	return 0;
5428c2ecf20Sopenharmony_ci}
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_cistatic int msm_gpio_get_direction(struct gpio_chip *chip, unsigned int offset)
5458c2ecf20Sopenharmony_ci{
5468c2ecf20Sopenharmony_ci	struct msm_pinctrl *pctrl = gpiochip_get_data(chip);
5478c2ecf20Sopenharmony_ci	const struct msm_pingroup *g;
5488c2ecf20Sopenharmony_ci	u32 val;
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	g = &pctrl->soc->groups[offset];
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	val = msm_readl_ctl(pctrl, g);
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci	return val & BIT(g->oe_bit) ? GPIO_LINE_DIRECTION_OUT :
5558c2ecf20Sopenharmony_ci				      GPIO_LINE_DIRECTION_IN;
5568c2ecf20Sopenharmony_ci}
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_cistatic int msm_gpio_get(struct gpio_chip *chip, unsigned offset)
5598c2ecf20Sopenharmony_ci{
5608c2ecf20Sopenharmony_ci	const struct msm_pingroup *g;
5618c2ecf20Sopenharmony_ci	struct msm_pinctrl *pctrl = gpiochip_get_data(chip);
5628c2ecf20Sopenharmony_ci	u32 val;
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci	g = &pctrl->soc->groups[offset];
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	val = msm_readl_io(pctrl, g);
5678c2ecf20Sopenharmony_ci	return !!(val & BIT(g->in_bit));
5688c2ecf20Sopenharmony_ci}
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_cistatic void msm_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
5718c2ecf20Sopenharmony_ci{
5728c2ecf20Sopenharmony_ci	const struct msm_pingroup *g;
5738c2ecf20Sopenharmony_ci	struct msm_pinctrl *pctrl = gpiochip_get_data(chip);
5748c2ecf20Sopenharmony_ci	unsigned long flags;
5758c2ecf20Sopenharmony_ci	u32 val;
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci	g = &pctrl->soc->groups[offset];
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&pctrl->lock, flags);
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci	val = msm_readl_io(pctrl, g);
5828c2ecf20Sopenharmony_ci	if (value)
5838c2ecf20Sopenharmony_ci		val |= BIT(g->out_bit);
5848c2ecf20Sopenharmony_ci	else
5858c2ecf20Sopenharmony_ci		val &= ~BIT(g->out_bit);
5868c2ecf20Sopenharmony_ci	msm_writel_io(val, pctrl, g);
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&pctrl->lock, flags);
5898c2ecf20Sopenharmony_ci}
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS
5928c2ecf20Sopenharmony_ci#include <linux/seq_file.h>
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_cistatic void msm_gpio_dbg_show_one(struct seq_file *s,
5958c2ecf20Sopenharmony_ci				  struct pinctrl_dev *pctldev,
5968c2ecf20Sopenharmony_ci				  struct gpio_chip *chip,
5978c2ecf20Sopenharmony_ci				  unsigned offset,
5988c2ecf20Sopenharmony_ci				  unsigned gpio)
5998c2ecf20Sopenharmony_ci{
6008c2ecf20Sopenharmony_ci	const struct msm_pingroup *g;
6018c2ecf20Sopenharmony_ci	struct msm_pinctrl *pctrl = gpiochip_get_data(chip);
6028c2ecf20Sopenharmony_ci	unsigned func;
6038c2ecf20Sopenharmony_ci	int is_out;
6048c2ecf20Sopenharmony_ci	int drive;
6058c2ecf20Sopenharmony_ci	int pull;
6068c2ecf20Sopenharmony_ci	int val;
6078c2ecf20Sopenharmony_ci	u32 ctl_reg, io_reg;
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci	static const char * const pulls_keeper[] = {
6108c2ecf20Sopenharmony_ci		"no pull",
6118c2ecf20Sopenharmony_ci		"pull down",
6128c2ecf20Sopenharmony_ci		"keeper",
6138c2ecf20Sopenharmony_ci		"pull up"
6148c2ecf20Sopenharmony_ci	};
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci	static const char * const pulls_no_keeper[] = {
6178c2ecf20Sopenharmony_ci		"no pull",
6188c2ecf20Sopenharmony_ci		"pull down",
6198c2ecf20Sopenharmony_ci		"pull up",
6208c2ecf20Sopenharmony_ci	};
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	if (!gpiochip_line_is_valid(chip, offset))
6238c2ecf20Sopenharmony_ci		return;
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci	g = &pctrl->soc->groups[offset];
6268c2ecf20Sopenharmony_ci	ctl_reg = msm_readl_ctl(pctrl, g);
6278c2ecf20Sopenharmony_ci	io_reg = msm_readl_io(pctrl, g);
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci	is_out = !!(ctl_reg & BIT(g->oe_bit));
6308c2ecf20Sopenharmony_ci	func = (ctl_reg >> g->mux_bit) & 7;
6318c2ecf20Sopenharmony_ci	drive = (ctl_reg >> g->drv_bit) & 7;
6328c2ecf20Sopenharmony_ci	pull = (ctl_reg >> g->pull_bit) & 3;
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ci	if (is_out)
6358c2ecf20Sopenharmony_ci		val = !!(io_reg & BIT(g->out_bit));
6368c2ecf20Sopenharmony_ci	else
6378c2ecf20Sopenharmony_ci		val = !!(io_reg & BIT(g->in_bit));
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci	seq_printf(s, " %-8s: %-3s", g->name, is_out ? "out" : "in");
6408c2ecf20Sopenharmony_ci	seq_printf(s, " %-4s func%d", val ? "high" : "low", func);
6418c2ecf20Sopenharmony_ci	seq_printf(s, " %dmA", msm_regval_to_drive(drive));
6428c2ecf20Sopenharmony_ci	if (pctrl->soc->pull_no_keeper)
6438c2ecf20Sopenharmony_ci		seq_printf(s, " %s", pulls_no_keeper[pull]);
6448c2ecf20Sopenharmony_ci	else
6458c2ecf20Sopenharmony_ci		seq_printf(s, " %s", pulls_keeper[pull]);
6468c2ecf20Sopenharmony_ci	seq_puts(s, "\n");
6478c2ecf20Sopenharmony_ci}
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_cistatic void msm_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
6508c2ecf20Sopenharmony_ci{
6518c2ecf20Sopenharmony_ci	unsigned gpio = chip->base;
6528c2ecf20Sopenharmony_ci	unsigned i;
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci	for (i = 0; i < chip->ngpio; i++, gpio++)
6558c2ecf20Sopenharmony_ci		msm_gpio_dbg_show_one(s, NULL, chip, i, gpio);
6568c2ecf20Sopenharmony_ci}
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci#else
6598c2ecf20Sopenharmony_ci#define msm_gpio_dbg_show NULL
6608c2ecf20Sopenharmony_ci#endif
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_cistatic int msm_gpio_init_valid_mask(struct gpio_chip *gc,
6638c2ecf20Sopenharmony_ci				    unsigned long *valid_mask,
6648c2ecf20Sopenharmony_ci				    unsigned int ngpios)
6658c2ecf20Sopenharmony_ci{
6668c2ecf20Sopenharmony_ci	struct msm_pinctrl *pctrl = gpiochip_get_data(gc);
6678c2ecf20Sopenharmony_ci	int ret;
6688c2ecf20Sopenharmony_ci	unsigned int len, i;
6698c2ecf20Sopenharmony_ci	const int *reserved = pctrl->soc->reserved_gpios;
6708c2ecf20Sopenharmony_ci	u16 *tmp;
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci	/* Driver provided reserved list overrides DT and ACPI */
6738c2ecf20Sopenharmony_ci	if (reserved) {
6748c2ecf20Sopenharmony_ci		bitmap_fill(valid_mask, ngpios);
6758c2ecf20Sopenharmony_ci		for (i = 0; reserved[i] >= 0; i++) {
6768c2ecf20Sopenharmony_ci			if (i >= ngpios || reserved[i] >= ngpios) {
6778c2ecf20Sopenharmony_ci				dev_err(pctrl->dev, "invalid list of reserved GPIOs\n");
6788c2ecf20Sopenharmony_ci				return -EINVAL;
6798c2ecf20Sopenharmony_ci			}
6808c2ecf20Sopenharmony_ci			clear_bit(reserved[i], valid_mask);
6818c2ecf20Sopenharmony_ci		}
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci		return 0;
6848c2ecf20Sopenharmony_ci	}
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci	/* The number of GPIOs in the ACPI tables */
6878c2ecf20Sopenharmony_ci	len = ret = device_property_count_u16(pctrl->dev, "gpios");
6888c2ecf20Sopenharmony_ci	if (ret < 0)
6898c2ecf20Sopenharmony_ci		return 0;
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci	if (ret > ngpios)
6928c2ecf20Sopenharmony_ci		return -EINVAL;
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_ci	tmp = kmalloc_array(len, sizeof(*tmp), GFP_KERNEL);
6958c2ecf20Sopenharmony_ci	if (!tmp)
6968c2ecf20Sopenharmony_ci		return -ENOMEM;
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	ret = device_property_read_u16_array(pctrl->dev, "gpios", tmp, len);
6998c2ecf20Sopenharmony_ci	if (ret < 0) {
7008c2ecf20Sopenharmony_ci		dev_err(pctrl->dev, "could not read list of GPIOs\n");
7018c2ecf20Sopenharmony_ci		goto out;
7028c2ecf20Sopenharmony_ci	}
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci	bitmap_zero(valid_mask, ngpios);
7058c2ecf20Sopenharmony_ci	for (i = 0; i < len; i++)
7068c2ecf20Sopenharmony_ci		set_bit(tmp[i], valid_mask);
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ciout:
7098c2ecf20Sopenharmony_ci	kfree(tmp);
7108c2ecf20Sopenharmony_ci	return ret;
7118c2ecf20Sopenharmony_ci}
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_cistatic const struct gpio_chip msm_gpio_template = {
7148c2ecf20Sopenharmony_ci	.direction_input  = msm_gpio_direction_input,
7158c2ecf20Sopenharmony_ci	.direction_output = msm_gpio_direction_output,
7168c2ecf20Sopenharmony_ci	.get_direction    = msm_gpio_get_direction,
7178c2ecf20Sopenharmony_ci	.get              = msm_gpio_get,
7188c2ecf20Sopenharmony_ci	.set              = msm_gpio_set,
7198c2ecf20Sopenharmony_ci	.request          = gpiochip_generic_request,
7208c2ecf20Sopenharmony_ci	.free             = gpiochip_generic_free,
7218c2ecf20Sopenharmony_ci	.dbg_show         = msm_gpio_dbg_show,
7228c2ecf20Sopenharmony_ci};
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci/* For dual-edge interrupts in software, since some hardware has no
7258c2ecf20Sopenharmony_ci * such support:
7268c2ecf20Sopenharmony_ci *
7278c2ecf20Sopenharmony_ci * At appropriate moments, this function may be called to flip the polarity
7288c2ecf20Sopenharmony_ci * settings of both-edge irq lines to try and catch the next edge.
7298c2ecf20Sopenharmony_ci *
7308c2ecf20Sopenharmony_ci * The attempt is considered successful if:
7318c2ecf20Sopenharmony_ci * - the status bit goes high, indicating that an edge was caught, or
7328c2ecf20Sopenharmony_ci * - the input value of the gpio doesn't change during the attempt.
7338c2ecf20Sopenharmony_ci * If the value changes twice during the process, that would cause the first
7348c2ecf20Sopenharmony_ci * test to fail but would force the second, as two opposite
7358c2ecf20Sopenharmony_ci * transitions would cause a detection no matter the polarity setting.
7368c2ecf20Sopenharmony_ci *
7378c2ecf20Sopenharmony_ci * The do-loop tries to sledge-hammer closed the timing hole between
7388c2ecf20Sopenharmony_ci * the initial value-read and the polarity-write - if the line value changes
7398c2ecf20Sopenharmony_ci * during that window, an interrupt is lost, the new polarity setting is
7408c2ecf20Sopenharmony_ci * incorrect, and the first success test will fail, causing a retry.
7418c2ecf20Sopenharmony_ci *
7428c2ecf20Sopenharmony_ci * Algorithm comes from Google's msmgpio driver.
7438c2ecf20Sopenharmony_ci */
7448c2ecf20Sopenharmony_cistatic void msm_gpio_update_dual_edge_pos(struct msm_pinctrl *pctrl,
7458c2ecf20Sopenharmony_ci					  const struct msm_pingroup *g,
7468c2ecf20Sopenharmony_ci					  struct irq_data *d)
7478c2ecf20Sopenharmony_ci{
7488c2ecf20Sopenharmony_ci	int loop_limit = 100;
7498c2ecf20Sopenharmony_ci	unsigned val, val2, intstat;
7508c2ecf20Sopenharmony_ci	unsigned pol;
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci	do {
7538c2ecf20Sopenharmony_ci		val = msm_readl_io(pctrl, g) & BIT(g->in_bit);
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci		pol = msm_readl_intr_cfg(pctrl, g);
7568c2ecf20Sopenharmony_ci		pol ^= BIT(g->intr_polarity_bit);
7578c2ecf20Sopenharmony_ci		msm_writel_intr_cfg(pol, pctrl, g);
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci		val2 = msm_readl_io(pctrl, g) & BIT(g->in_bit);
7608c2ecf20Sopenharmony_ci		intstat = msm_readl_intr_status(pctrl, g);
7618c2ecf20Sopenharmony_ci		if (intstat || (val == val2))
7628c2ecf20Sopenharmony_ci			return;
7638c2ecf20Sopenharmony_ci	} while (loop_limit-- > 0);
7648c2ecf20Sopenharmony_ci	dev_err(pctrl->dev, "dual-edge irq failed to stabilize, %#08x != %#08x\n",
7658c2ecf20Sopenharmony_ci		val, val2);
7668c2ecf20Sopenharmony_ci}
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_cistatic void msm_gpio_irq_mask(struct irq_data *d)
7698c2ecf20Sopenharmony_ci{
7708c2ecf20Sopenharmony_ci	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
7718c2ecf20Sopenharmony_ci	struct msm_pinctrl *pctrl = gpiochip_get_data(gc);
7728c2ecf20Sopenharmony_ci	const struct msm_pingroup *g;
7738c2ecf20Sopenharmony_ci	unsigned long flags;
7748c2ecf20Sopenharmony_ci	u32 val;
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci	if (d->parent_data)
7778c2ecf20Sopenharmony_ci		irq_chip_mask_parent(d);
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci	if (test_bit(d->hwirq, pctrl->skip_wake_irqs))
7808c2ecf20Sopenharmony_ci		return;
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci	g = &pctrl->soc->groups[d->hwirq];
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&pctrl->lock, flags);
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_ci	val = msm_readl_intr_cfg(pctrl, g);
7878c2ecf20Sopenharmony_ci	/*
7888c2ecf20Sopenharmony_ci	 * There are two bits that control interrupt forwarding to the CPU. The
7898c2ecf20Sopenharmony_ci	 * RAW_STATUS_EN bit causes the level or edge sensed on the line to be
7908c2ecf20Sopenharmony_ci	 * latched into the interrupt status register when the hardware detects
7918c2ecf20Sopenharmony_ci	 * an irq that it's configured for (either edge for edge type or level
7928c2ecf20Sopenharmony_ci	 * for level type irq). The 'non-raw' status enable bit causes the
7938c2ecf20Sopenharmony_ci	 * hardware to assert the summary interrupt to the CPU if the latched
7948c2ecf20Sopenharmony_ci	 * status bit is set. There's a bug though, the edge detection logic
7958c2ecf20Sopenharmony_ci	 * seems to have a problem where toggling the RAW_STATUS_EN bit may
7968c2ecf20Sopenharmony_ci	 * cause the status bit to latch spuriously when there isn't any edge
7978c2ecf20Sopenharmony_ci	 * so we can't touch that bit for edge type irqs and we have to keep
7988c2ecf20Sopenharmony_ci	 * the bit set anyway so that edges are latched while the line is masked.
7998c2ecf20Sopenharmony_ci	 *
8008c2ecf20Sopenharmony_ci	 * To make matters more complicated, leaving the RAW_STATUS_EN bit
8018c2ecf20Sopenharmony_ci	 * enabled all the time causes level interrupts to re-latch into the
8028c2ecf20Sopenharmony_ci	 * status register because the level is still present on the line after
8038c2ecf20Sopenharmony_ci	 * we ack it. We clear the raw status enable bit during mask here and
8048c2ecf20Sopenharmony_ci	 * set the bit on unmask so the interrupt can't latch into the hardware
8058c2ecf20Sopenharmony_ci	 * while it's masked.
8068c2ecf20Sopenharmony_ci	 */
8078c2ecf20Sopenharmony_ci	if (irqd_get_trigger_type(d) & IRQ_TYPE_LEVEL_MASK)
8088c2ecf20Sopenharmony_ci		val &= ~BIT(g->intr_raw_status_bit);
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_ci	val &= ~BIT(g->intr_enable_bit);
8118c2ecf20Sopenharmony_ci	msm_writel_intr_cfg(val, pctrl, g);
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ci	clear_bit(d->hwirq, pctrl->enabled_irqs);
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&pctrl->lock, flags);
8168c2ecf20Sopenharmony_ci}
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_cistatic void msm_gpio_irq_unmask(struct irq_data *d)
8198c2ecf20Sopenharmony_ci{
8208c2ecf20Sopenharmony_ci	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
8218c2ecf20Sopenharmony_ci	struct msm_pinctrl *pctrl = gpiochip_get_data(gc);
8228c2ecf20Sopenharmony_ci	const struct msm_pingroup *g;
8238c2ecf20Sopenharmony_ci	unsigned long flags;
8248c2ecf20Sopenharmony_ci	u32 val;
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_ci	if (d->parent_data)
8278c2ecf20Sopenharmony_ci		irq_chip_unmask_parent(d);
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_ci	if (test_bit(d->hwirq, pctrl->skip_wake_irqs))
8308c2ecf20Sopenharmony_ci		return;
8318c2ecf20Sopenharmony_ci
8328c2ecf20Sopenharmony_ci	g = &pctrl->soc->groups[d->hwirq];
8338c2ecf20Sopenharmony_ci
8348c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&pctrl->lock, flags);
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_ci	val = msm_readl_intr_cfg(pctrl, g);
8378c2ecf20Sopenharmony_ci	val |= BIT(g->intr_raw_status_bit);
8388c2ecf20Sopenharmony_ci	val |= BIT(g->intr_enable_bit);
8398c2ecf20Sopenharmony_ci	msm_writel_intr_cfg(val, pctrl, g);
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_ci	set_bit(d->hwirq, pctrl->enabled_irqs);
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&pctrl->lock, flags);
8448c2ecf20Sopenharmony_ci}
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_cistatic void msm_gpio_irq_enable(struct irq_data *d)
8478c2ecf20Sopenharmony_ci{
8488c2ecf20Sopenharmony_ci	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
8498c2ecf20Sopenharmony_ci	struct msm_pinctrl *pctrl = gpiochip_get_data(gc);
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci	if (d->parent_data)
8528c2ecf20Sopenharmony_ci		irq_chip_enable_parent(d);
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_ci	if (!test_bit(d->hwirq, pctrl->skip_wake_irqs))
8558c2ecf20Sopenharmony_ci		msm_gpio_irq_unmask(d);
8568c2ecf20Sopenharmony_ci}
8578c2ecf20Sopenharmony_ci
8588c2ecf20Sopenharmony_cistatic void msm_gpio_irq_disable(struct irq_data *d)
8598c2ecf20Sopenharmony_ci{
8608c2ecf20Sopenharmony_ci	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
8618c2ecf20Sopenharmony_ci	struct msm_pinctrl *pctrl = gpiochip_get_data(gc);
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_ci	if (d->parent_data)
8648c2ecf20Sopenharmony_ci		irq_chip_disable_parent(d);
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci	if (!test_bit(d->hwirq, pctrl->skip_wake_irqs))
8678c2ecf20Sopenharmony_ci		msm_gpio_irq_mask(d);
8688c2ecf20Sopenharmony_ci}
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci/**
8718c2ecf20Sopenharmony_ci * msm_gpio_update_dual_edge_parent() - Prime next edge for IRQs handled by parent.
8728c2ecf20Sopenharmony_ci * @d: The irq dta.
8738c2ecf20Sopenharmony_ci *
8748c2ecf20Sopenharmony_ci * This is much like msm_gpio_update_dual_edge_pos() but for IRQs that are
8758c2ecf20Sopenharmony_ci * normally handled by the parent irqchip.  The logic here is slightly
8768c2ecf20Sopenharmony_ci * different due to what's easy to do with our parent, but in principle it's
8778c2ecf20Sopenharmony_ci * the same.
8788c2ecf20Sopenharmony_ci */
8798c2ecf20Sopenharmony_cistatic void msm_gpio_update_dual_edge_parent(struct irq_data *d)
8808c2ecf20Sopenharmony_ci{
8818c2ecf20Sopenharmony_ci	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
8828c2ecf20Sopenharmony_ci	struct msm_pinctrl *pctrl = gpiochip_get_data(gc);
8838c2ecf20Sopenharmony_ci	const struct msm_pingroup *g = &pctrl->soc->groups[d->hwirq];
8848c2ecf20Sopenharmony_ci	int loop_limit = 100;
8858c2ecf20Sopenharmony_ci	unsigned int val;
8868c2ecf20Sopenharmony_ci	unsigned int type;
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_ci	/* Read the value and make a guess about what edge we need to catch */
8898c2ecf20Sopenharmony_ci	val = msm_readl_io(pctrl, g) & BIT(g->in_bit);
8908c2ecf20Sopenharmony_ci	type = val ? IRQ_TYPE_EDGE_FALLING : IRQ_TYPE_EDGE_RISING;
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_ci	do {
8938c2ecf20Sopenharmony_ci		/* Set the parent to catch the next edge */
8948c2ecf20Sopenharmony_ci		irq_chip_set_type_parent(d, type);
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ci		/*
8978c2ecf20Sopenharmony_ci		 * Possibly the line changed between when we last read "val"
8988c2ecf20Sopenharmony_ci		 * (and decided what edge we needed) and when set the edge.
8998c2ecf20Sopenharmony_ci		 * If the value didn't change (or changed and then changed
9008c2ecf20Sopenharmony_ci		 * back) then we're done.
9018c2ecf20Sopenharmony_ci		 */
9028c2ecf20Sopenharmony_ci		val = msm_readl_io(pctrl, g) & BIT(g->in_bit);
9038c2ecf20Sopenharmony_ci		if (type == IRQ_TYPE_EDGE_RISING) {
9048c2ecf20Sopenharmony_ci			if (!val)
9058c2ecf20Sopenharmony_ci				return;
9068c2ecf20Sopenharmony_ci			type = IRQ_TYPE_EDGE_FALLING;
9078c2ecf20Sopenharmony_ci		} else if (type == IRQ_TYPE_EDGE_FALLING) {
9088c2ecf20Sopenharmony_ci			if (val)
9098c2ecf20Sopenharmony_ci				return;
9108c2ecf20Sopenharmony_ci			type = IRQ_TYPE_EDGE_RISING;
9118c2ecf20Sopenharmony_ci		}
9128c2ecf20Sopenharmony_ci	} while (loop_limit-- > 0);
9138c2ecf20Sopenharmony_ci	dev_warn_once(pctrl->dev, "dual-edge irq failed to stabilize\n");
9148c2ecf20Sopenharmony_ci}
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_cistatic void msm_gpio_irq_ack(struct irq_data *d)
9178c2ecf20Sopenharmony_ci{
9188c2ecf20Sopenharmony_ci	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
9198c2ecf20Sopenharmony_ci	struct msm_pinctrl *pctrl = gpiochip_get_data(gc);
9208c2ecf20Sopenharmony_ci	const struct msm_pingroup *g;
9218c2ecf20Sopenharmony_ci	unsigned long flags;
9228c2ecf20Sopenharmony_ci
9238c2ecf20Sopenharmony_ci	if (test_bit(d->hwirq, pctrl->skip_wake_irqs)) {
9248c2ecf20Sopenharmony_ci		if (test_bit(d->hwirq, pctrl->dual_edge_irqs))
9258c2ecf20Sopenharmony_ci			msm_gpio_update_dual_edge_parent(d);
9268c2ecf20Sopenharmony_ci		return;
9278c2ecf20Sopenharmony_ci	}
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_ci	g = &pctrl->soc->groups[d->hwirq];
9308c2ecf20Sopenharmony_ci
9318c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&pctrl->lock, flags);
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_ci	msm_ack_intr_status(pctrl, g);
9348c2ecf20Sopenharmony_ci
9358c2ecf20Sopenharmony_ci	if (test_bit(d->hwirq, pctrl->dual_edge_irqs))
9368c2ecf20Sopenharmony_ci		msm_gpio_update_dual_edge_pos(pctrl, g, d);
9378c2ecf20Sopenharmony_ci
9388c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&pctrl->lock, flags);
9398c2ecf20Sopenharmony_ci}
9408c2ecf20Sopenharmony_ci
9418c2ecf20Sopenharmony_cistatic bool msm_gpio_needs_dual_edge_parent_workaround(struct irq_data *d,
9428c2ecf20Sopenharmony_ci						       unsigned int type)
9438c2ecf20Sopenharmony_ci{
9448c2ecf20Sopenharmony_ci	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
9458c2ecf20Sopenharmony_ci	struct msm_pinctrl *pctrl = gpiochip_get_data(gc);
9468c2ecf20Sopenharmony_ci
9478c2ecf20Sopenharmony_ci	return type == IRQ_TYPE_EDGE_BOTH &&
9488c2ecf20Sopenharmony_ci	       pctrl->soc->wakeirq_dual_edge_errata && d->parent_data &&
9498c2ecf20Sopenharmony_ci	       test_bit(d->hwirq, pctrl->skip_wake_irqs);
9508c2ecf20Sopenharmony_ci}
9518c2ecf20Sopenharmony_ci
9528c2ecf20Sopenharmony_cistatic int msm_gpio_irq_set_type(struct irq_data *d, unsigned int type)
9538c2ecf20Sopenharmony_ci{
9548c2ecf20Sopenharmony_ci	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
9558c2ecf20Sopenharmony_ci	struct msm_pinctrl *pctrl = gpiochip_get_data(gc);
9568c2ecf20Sopenharmony_ci	const struct msm_pingroup *g;
9578c2ecf20Sopenharmony_ci	unsigned long flags;
9588c2ecf20Sopenharmony_ci	bool was_enabled;
9598c2ecf20Sopenharmony_ci	u32 val;
9608c2ecf20Sopenharmony_ci
9618c2ecf20Sopenharmony_ci	if (msm_gpio_needs_dual_edge_parent_workaround(d, type)) {
9628c2ecf20Sopenharmony_ci		set_bit(d->hwirq, pctrl->dual_edge_irqs);
9638c2ecf20Sopenharmony_ci		irq_set_handler_locked(d, handle_fasteoi_ack_irq);
9648c2ecf20Sopenharmony_ci		msm_gpio_update_dual_edge_parent(d);
9658c2ecf20Sopenharmony_ci		return 0;
9668c2ecf20Sopenharmony_ci	}
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_ci	if (d->parent_data)
9698c2ecf20Sopenharmony_ci		irq_chip_set_type_parent(d, type);
9708c2ecf20Sopenharmony_ci
9718c2ecf20Sopenharmony_ci	if (test_bit(d->hwirq, pctrl->skip_wake_irqs)) {
9728c2ecf20Sopenharmony_ci		clear_bit(d->hwirq, pctrl->dual_edge_irqs);
9738c2ecf20Sopenharmony_ci		irq_set_handler_locked(d, handle_fasteoi_irq);
9748c2ecf20Sopenharmony_ci		return 0;
9758c2ecf20Sopenharmony_ci	}
9768c2ecf20Sopenharmony_ci
9778c2ecf20Sopenharmony_ci	g = &pctrl->soc->groups[d->hwirq];
9788c2ecf20Sopenharmony_ci
9798c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&pctrl->lock, flags);
9808c2ecf20Sopenharmony_ci
9818c2ecf20Sopenharmony_ci	/*
9828c2ecf20Sopenharmony_ci	 * For hw without possibility of detecting both edges
9838c2ecf20Sopenharmony_ci	 */
9848c2ecf20Sopenharmony_ci	if (g->intr_detection_width == 1 && type == IRQ_TYPE_EDGE_BOTH)
9858c2ecf20Sopenharmony_ci		set_bit(d->hwirq, pctrl->dual_edge_irqs);
9868c2ecf20Sopenharmony_ci	else
9878c2ecf20Sopenharmony_ci		clear_bit(d->hwirq, pctrl->dual_edge_irqs);
9888c2ecf20Sopenharmony_ci
9898c2ecf20Sopenharmony_ci	/* Route interrupts to application cpu.
9908c2ecf20Sopenharmony_ci	 * With intr_target_use_scm interrupts are routed to
9918c2ecf20Sopenharmony_ci	 * application cpu using scm calls.
9928c2ecf20Sopenharmony_ci	 */
9938c2ecf20Sopenharmony_ci	if (pctrl->intr_target_use_scm) {
9948c2ecf20Sopenharmony_ci		u32 addr = pctrl->phys_base[0] + g->intr_target_reg;
9958c2ecf20Sopenharmony_ci		int ret;
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_ci		qcom_scm_io_readl(addr, &val);
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_ci		val &= ~(7 << g->intr_target_bit);
10008c2ecf20Sopenharmony_ci		val |= g->intr_target_kpss_val << g->intr_target_bit;
10018c2ecf20Sopenharmony_ci
10028c2ecf20Sopenharmony_ci		ret = qcom_scm_io_writel(addr, val);
10038c2ecf20Sopenharmony_ci		if (ret)
10048c2ecf20Sopenharmony_ci			dev_err(pctrl->dev,
10058c2ecf20Sopenharmony_ci				"Failed routing %lu interrupt to Apps proc",
10068c2ecf20Sopenharmony_ci				d->hwirq);
10078c2ecf20Sopenharmony_ci	} else {
10088c2ecf20Sopenharmony_ci		val = msm_readl_intr_target(pctrl, g);
10098c2ecf20Sopenharmony_ci		val &= ~(7 << g->intr_target_bit);
10108c2ecf20Sopenharmony_ci		val |= g->intr_target_kpss_val << g->intr_target_bit;
10118c2ecf20Sopenharmony_ci		msm_writel_intr_target(val, pctrl, g);
10128c2ecf20Sopenharmony_ci	}
10138c2ecf20Sopenharmony_ci
10148c2ecf20Sopenharmony_ci	/* Update configuration for gpio.
10158c2ecf20Sopenharmony_ci	 * RAW_STATUS_EN is left on for all gpio irqs. Due to the
10168c2ecf20Sopenharmony_ci	 * internal circuitry of TLMM, toggling the RAW_STATUS
10178c2ecf20Sopenharmony_ci	 * could cause the INTR_STATUS to be set for EDGE interrupts.
10188c2ecf20Sopenharmony_ci	 */
10198c2ecf20Sopenharmony_ci	val = msm_readl_intr_cfg(pctrl, g);
10208c2ecf20Sopenharmony_ci	was_enabled = val & BIT(g->intr_raw_status_bit);
10218c2ecf20Sopenharmony_ci	val |= BIT(g->intr_raw_status_bit);
10228c2ecf20Sopenharmony_ci	if (g->intr_detection_width == 2) {
10238c2ecf20Sopenharmony_ci		val &= ~(3 << g->intr_detection_bit);
10248c2ecf20Sopenharmony_ci		val &= ~(1 << g->intr_polarity_bit);
10258c2ecf20Sopenharmony_ci		switch (type) {
10268c2ecf20Sopenharmony_ci		case IRQ_TYPE_EDGE_RISING:
10278c2ecf20Sopenharmony_ci			val |= 1 << g->intr_detection_bit;
10288c2ecf20Sopenharmony_ci			val |= BIT(g->intr_polarity_bit);
10298c2ecf20Sopenharmony_ci			break;
10308c2ecf20Sopenharmony_ci		case IRQ_TYPE_EDGE_FALLING:
10318c2ecf20Sopenharmony_ci			val |= 2 << g->intr_detection_bit;
10328c2ecf20Sopenharmony_ci			val |= BIT(g->intr_polarity_bit);
10338c2ecf20Sopenharmony_ci			break;
10348c2ecf20Sopenharmony_ci		case IRQ_TYPE_EDGE_BOTH:
10358c2ecf20Sopenharmony_ci			val |= 3 << g->intr_detection_bit;
10368c2ecf20Sopenharmony_ci			val |= BIT(g->intr_polarity_bit);
10378c2ecf20Sopenharmony_ci			break;
10388c2ecf20Sopenharmony_ci		case IRQ_TYPE_LEVEL_LOW:
10398c2ecf20Sopenharmony_ci			break;
10408c2ecf20Sopenharmony_ci		case IRQ_TYPE_LEVEL_HIGH:
10418c2ecf20Sopenharmony_ci			val |= BIT(g->intr_polarity_bit);
10428c2ecf20Sopenharmony_ci			break;
10438c2ecf20Sopenharmony_ci		}
10448c2ecf20Sopenharmony_ci	} else if (g->intr_detection_width == 1) {
10458c2ecf20Sopenharmony_ci		val &= ~(1 << g->intr_detection_bit);
10468c2ecf20Sopenharmony_ci		val &= ~(1 << g->intr_polarity_bit);
10478c2ecf20Sopenharmony_ci		switch (type) {
10488c2ecf20Sopenharmony_ci		case IRQ_TYPE_EDGE_RISING:
10498c2ecf20Sopenharmony_ci			val |= BIT(g->intr_detection_bit);
10508c2ecf20Sopenharmony_ci			val |= BIT(g->intr_polarity_bit);
10518c2ecf20Sopenharmony_ci			break;
10528c2ecf20Sopenharmony_ci		case IRQ_TYPE_EDGE_FALLING:
10538c2ecf20Sopenharmony_ci			val |= BIT(g->intr_detection_bit);
10548c2ecf20Sopenharmony_ci			break;
10558c2ecf20Sopenharmony_ci		case IRQ_TYPE_EDGE_BOTH:
10568c2ecf20Sopenharmony_ci			val |= BIT(g->intr_detection_bit);
10578c2ecf20Sopenharmony_ci			val |= BIT(g->intr_polarity_bit);
10588c2ecf20Sopenharmony_ci			break;
10598c2ecf20Sopenharmony_ci		case IRQ_TYPE_LEVEL_LOW:
10608c2ecf20Sopenharmony_ci			break;
10618c2ecf20Sopenharmony_ci		case IRQ_TYPE_LEVEL_HIGH:
10628c2ecf20Sopenharmony_ci			val |= BIT(g->intr_polarity_bit);
10638c2ecf20Sopenharmony_ci			break;
10648c2ecf20Sopenharmony_ci		}
10658c2ecf20Sopenharmony_ci	} else {
10668c2ecf20Sopenharmony_ci		BUG();
10678c2ecf20Sopenharmony_ci	}
10688c2ecf20Sopenharmony_ci	msm_writel_intr_cfg(val, pctrl, g);
10698c2ecf20Sopenharmony_ci
10708c2ecf20Sopenharmony_ci	/*
10718c2ecf20Sopenharmony_ci	 * The first time we set RAW_STATUS_EN it could trigger an interrupt.
10728c2ecf20Sopenharmony_ci	 * Clear the interrupt.  This is safe because we have
10738c2ecf20Sopenharmony_ci	 * IRQCHIP_SET_TYPE_MASKED.
10748c2ecf20Sopenharmony_ci	 */
10758c2ecf20Sopenharmony_ci	if (!was_enabled)
10768c2ecf20Sopenharmony_ci		msm_ack_intr_status(pctrl, g);
10778c2ecf20Sopenharmony_ci
10788c2ecf20Sopenharmony_ci	if (test_bit(d->hwirq, pctrl->dual_edge_irqs))
10798c2ecf20Sopenharmony_ci		msm_gpio_update_dual_edge_pos(pctrl, g, d);
10808c2ecf20Sopenharmony_ci
10818c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&pctrl->lock, flags);
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_ci	if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))
10848c2ecf20Sopenharmony_ci		irq_set_handler_locked(d, handle_level_irq);
10858c2ecf20Sopenharmony_ci	else if (type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING))
10868c2ecf20Sopenharmony_ci		irq_set_handler_locked(d, handle_edge_irq);
10878c2ecf20Sopenharmony_ci
10888c2ecf20Sopenharmony_ci	return 0;
10898c2ecf20Sopenharmony_ci}
10908c2ecf20Sopenharmony_ci
10918c2ecf20Sopenharmony_cistatic int msm_gpio_irq_set_wake(struct irq_data *d, unsigned int on)
10928c2ecf20Sopenharmony_ci{
10938c2ecf20Sopenharmony_ci	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
10948c2ecf20Sopenharmony_ci	struct msm_pinctrl *pctrl = gpiochip_get_data(gc);
10958c2ecf20Sopenharmony_ci
10968c2ecf20Sopenharmony_ci	/*
10978c2ecf20Sopenharmony_ci	 * While they may not wake up when the TLMM is powered off,
10988c2ecf20Sopenharmony_ci	 * some GPIOs would like to wakeup the system from suspend
10998c2ecf20Sopenharmony_ci	 * when TLMM is powered on. To allow that, enable the GPIO
11008c2ecf20Sopenharmony_ci	 * summary line to be wakeup capable at GIC.
11018c2ecf20Sopenharmony_ci	 */
11028c2ecf20Sopenharmony_ci	if (d->parent_data && test_bit(d->hwirq, pctrl->skip_wake_irqs))
11038c2ecf20Sopenharmony_ci		return irq_chip_set_wake_parent(d, on);
11048c2ecf20Sopenharmony_ci
11058c2ecf20Sopenharmony_ci	return irq_set_irq_wake(pctrl->irq, on);
11068c2ecf20Sopenharmony_ci}
11078c2ecf20Sopenharmony_ci
11088c2ecf20Sopenharmony_cistatic int msm_gpio_irq_reqres(struct irq_data *d)
11098c2ecf20Sopenharmony_ci{
11108c2ecf20Sopenharmony_ci	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
11118c2ecf20Sopenharmony_ci	struct msm_pinctrl *pctrl = gpiochip_get_data(gc);
11128c2ecf20Sopenharmony_ci	int ret;
11138c2ecf20Sopenharmony_ci
11148c2ecf20Sopenharmony_ci	if (!try_module_get(gc->owner))
11158c2ecf20Sopenharmony_ci		return -ENODEV;
11168c2ecf20Sopenharmony_ci
11178c2ecf20Sopenharmony_ci	ret = msm_pinmux_request_gpio(pctrl->pctrl, NULL, d->hwirq);
11188c2ecf20Sopenharmony_ci	if (ret)
11198c2ecf20Sopenharmony_ci		goto out;
11208c2ecf20Sopenharmony_ci	msm_gpio_direction_input(gc, d->hwirq);
11218c2ecf20Sopenharmony_ci
11228c2ecf20Sopenharmony_ci	if (gpiochip_lock_as_irq(gc, d->hwirq)) {
11238c2ecf20Sopenharmony_ci		dev_err(gc->parent,
11248c2ecf20Sopenharmony_ci			"unable to lock HW IRQ %lu for IRQ\n",
11258c2ecf20Sopenharmony_ci			d->hwirq);
11268c2ecf20Sopenharmony_ci		ret = -EINVAL;
11278c2ecf20Sopenharmony_ci		goto out;
11288c2ecf20Sopenharmony_ci	}
11298c2ecf20Sopenharmony_ci
11308c2ecf20Sopenharmony_ci	/*
11318c2ecf20Sopenharmony_ci	 * The disable / clear-enable workaround we do in msm_pinmux_set_mux()
11328c2ecf20Sopenharmony_ci	 * only works if disable is not lazy since we only clear any bogus
11338c2ecf20Sopenharmony_ci	 * interrupt in hardware. Explicitly mark the interrupt as UNLAZY.
11348c2ecf20Sopenharmony_ci	 */
11358c2ecf20Sopenharmony_ci	irq_set_status_flags(d->irq, IRQ_DISABLE_UNLAZY);
11368c2ecf20Sopenharmony_ci
11378c2ecf20Sopenharmony_ci	return 0;
11388c2ecf20Sopenharmony_ciout:
11398c2ecf20Sopenharmony_ci	module_put(gc->owner);
11408c2ecf20Sopenharmony_ci	return ret;
11418c2ecf20Sopenharmony_ci}
11428c2ecf20Sopenharmony_ci
11438c2ecf20Sopenharmony_cistatic void msm_gpio_irq_relres(struct irq_data *d)
11448c2ecf20Sopenharmony_ci{
11458c2ecf20Sopenharmony_ci	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
11468c2ecf20Sopenharmony_ci
11478c2ecf20Sopenharmony_ci	gpiochip_unlock_as_irq(gc, d->hwirq);
11488c2ecf20Sopenharmony_ci	module_put(gc->owner);
11498c2ecf20Sopenharmony_ci}
11508c2ecf20Sopenharmony_ci
11518c2ecf20Sopenharmony_cistatic int msm_gpio_irq_set_affinity(struct irq_data *d,
11528c2ecf20Sopenharmony_ci				const struct cpumask *dest, bool force)
11538c2ecf20Sopenharmony_ci{
11548c2ecf20Sopenharmony_ci	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
11558c2ecf20Sopenharmony_ci	struct msm_pinctrl *pctrl = gpiochip_get_data(gc);
11568c2ecf20Sopenharmony_ci
11578c2ecf20Sopenharmony_ci	if (d->parent_data && test_bit(d->hwirq, pctrl->skip_wake_irqs))
11588c2ecf20Sopenharmony_ci		return irq_chip_set_affinity_parent(d, dest, force);
11598c2ecf20Sopenharmony_ci
11608c2ecf20Sopenharmony_ci	return 0;
11618c2ecf20Sopenharmony_ci}
11628c2ecf20Sopenharmony_ci
11638c2ecf20Sopenharmony_cistatic int msm_gpio_irq_set_vcpu_affinity(struct irq_data *d, void *vcpu_info)
11648c2ecf20Sopenharmony_ci{
11658c2ecf20Sopenharmony_ci	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
11668c2ecf20Sopenharmony_ci	struct msm_pinctrl *pctrl = gpiochip_get_data(gc);
11678c2ecf20Sopenharmony_ci
11688c2ecf20Sopenharmony_ci	if (d->parent_data && test_bit(d->hwirq, pctrl->skip_wake_irqs))
11698c2ecf20Sopenharmony_ci		return irq_chip_set_vcpu_affinity_parent(d, vcpu_info);
11708c2ecf20Sopenharmony_ci
11718c2ecf20Sopenharmony_ci	return 0;
11728c2ecf20Sopenharmony_ci}
11738c2ecf20Sopenharmony_ci
11748c2ecf20Sopenharmony_cistatic void msm_gpio_irq_handler(struct irq_desc *desc)
11758c2ecf20Sopenharmony_ci{
11768c2ecf20Sopenharmony_ci	struct gpio_chip *gc = irq_desc_get_handler_data(desc);
11778c2ecf20Sopenharmony_ci	const struct msm_pingroup *g;
11788c2ecf20Sopenharmony_ci	struct msm_pinctrl *pctrl = gpiochip_get_data(gc);
11798c2ecf20Sopenharmony_ci	struct irq_chip *chip = irq_desc_get_chip(desc);
11808c2ecf20Sopenharmony_ci	int irq_pin;
11818c2ecf20Sopenharmony_ci	int handled = 0;
11828c2ecf20Sopenharmony_ci	u32 val;
11838c2ecf20Sopenharmony_ci	int i;
11848c2ecf20Sopenharmony_ci
11858c2ecf20Sopenharmony_ci	chained_irq_enter(chip, desc);
11868c2ecf20Sopenharmony_ci
11878c2ecf20Sopenharmony_ci	/*
11888c2ecf20Sopenharmony_ci	 * Each pin has it's own IRQ status register, so use
11898c2ecf20Sopenharmony_ci	 * enabled_irq bitmap to limit the number of reads.
11908c2ecf20Sopenharmony_ci	 */
11918c2ecf20Sopenharmony_ci	for_each_set_bit(i, pctrl->enabled_irqs, pctrl->chip.ngpio) {
11928c2ecf20Sopenharmony_ci		g = &pctrl->soc->groups[i];
11938c2ecf20Sopenharmony_ci		val = msm_readl_intr_status(pctrl, g);
11948c2ecf20Sopenharmony_ci		if (val & BIT(g->intr_status_bit)) {
11958c2ecf20Sopenharmony_ci			irq_pin = irq_find_mapping(gc->irq.domain, i);
11968c2ecf20Sopenharmony_ci			generic_handle_irq(irq_pin);
11978c2ecf20Sopenharmony_ci			handled++;
11988c2ecf20Sopenharmony_ci		}
11998c2ecf20Sopenharmony_ci	}
12008c2ecf20Sopenharmony_ci
12018c2ecf20Sopenharmony_ci	/* No interrupts were flagged */
12028c2ecf20Sopenharmony_ci	if (handled == 0)
12038c2ecf20Sopenharmony_ci		handle_bad_irq(desc);
12048c2ecf20Sopenharmony_ci
12058c2ecf20Sopenharmony_ci	chained_irq_exit(chip, desc);
12068c2ecf20Sopenharmony_ci}
12078c2ecf20Sopenharmony_ci
12088c2ecf20Sopenharmony_cistatic int msm_gpio_wakeirq(struct gpio_chip *gc,
12098c2ecf20Sopenharmony_ci			    unsigned int child,
12108c2ecf20Sopenharmony_ci			    unsigned int child_type,
12118c2ecf20Sopenharmony_ci			    unsigned int *parent,
12128c2ecf20Sopenharmony_ci			    unsigned int *parent_type)
12138c2ecf20Sopenharmony_ci{
12148c2ecf20Sopenharmony_ci	struct msm_pinctrl *pctrl = gpiochip_get_data(gc);
12158c2ecf20Sopenharmony_ci	const struct msm_gpio_wakeirq_map *map;
12168c2ecf20Sopenharmony_ci	int i;
12178c2ecf20Sopenharmony_ci
12188c2ecf20Sopenharmony_ci	*parent = GPIO_NO_WAKE_IRQ;
12198c2ecf20Sopenharmony_ci	*parent_type = IRQ_TYPE_EDGE_RISING;
12208c2ecf20Sopenharmony_ci
12218c2ecf20Sopenharmony_ci	for (i = 0; i < pctrl->soc->nwakeirq_map; i++) {
12228c2ecf20Sopenharmony_ci		map = &pctrl->soc->wakeirq_map[i];
12238c2ecf20Sopenharmony_ci		if (map->gpio == child) {
12248c2ecf20Sopenharmony_ci			*parent = map->wakeirq;
12258c2ecf20Sopenharmony_ci			break;
12268c2ecf20Sopenharmony_ci		}
12278c2ecf20Sopenharmony_ci	}
12288c2ecf20Sopenharmony_ci
12298c2ecf20Sopenharmony_ci	return 0;
12308c2ecf20Sopenharmony_ci}
12318c2ecf20Sopenharmony_ci
12328c2ecf20Sopenharmony_cistatic bool msm_gpio_needs_valid_mask(struct msm_pinctrl *pctrl)
12338c2ecf20Sopenharmony_ci{
12348c2ecf20Sopenharmony_ci	if (pctrl->soc->reserved_gpios)
12358c2ecf20Sopenharmony_ci		return true;
12368c2ecf20Sopenharmony_ci
12378c2ecf20Sopenharmony_ci	return device_property_count_u16(pctrl->dev, "gpios") > 0;
12388c2ecf20Sopenharmony_ci}
12398c2ecf20Sopenharmony_ci
12408c2ecf20Sopenharmony_cistatic int msm_gpio_init(struct msm_pinctrl *pctrl)
12418c2ecf20Sopenharmony_ci{
12428c2ecf20Sopenharmony_ci	struct gpio_chip *chip;
12438c2ecf20Sopenharmony_ci	struct gpio_irq_chip *girq;
12448c2ecf20Sopenharmony_ci	int i, ret;
12458c2ecf20Sopenharmony_ci	unsigned gpio, ngpio = pctrl->soc->ngpios;
12468c2ecf20Sopenharmony_ci	struct device_node *np;
12478c2ecf20Sopenharmony_ci	bool skip;
12488c2ecf20Sopenharmony_ci
12498c2ecf20Sopenharmony_ci	if (WARN_ON(ngpio > MAX_NR_GPIO))
12508c2ecf20Sopenharmony_ci		return -EINVAL;
12518c2ecf20Sopenharmony_ci
12528c2ecf20Sopenharmony_ci	chip = &pctrl->chip;
12538c2ecf20Sopenharmony_ci	chip->base = -1;
12548c2ecf20Sopenharmony_ci	chip->ngpio = ngpio;
12558c2ecf20Sopenharmony_ci	chip->label = dev_name(pctrl->dev);
12568c2ecf20Sopenharmony_ci	chip->parent = pctrl->dev;
12578c2ecf20Sopenharmony_ci	chip->owner = THIS_MODULE;
12588c2ecf20Sopenharmony_ci	chip->of_node = pctrl->dev->of_node;
12598c2ecf20Sopenharmony_ci	if (msm_gpio_needs_valid_mask(pctrl))
12608c2ecf20Sopenharmony_ci		chip->init_valid_mask = msm_gpio_init_valid_mask;
12618c2ecf20Sopenharmony_ci
12628c2ecf20Sopenharmony_ci	pctrl->irq_chip.name = "msmgpio";
12638c2ecf20Sopenharmony_ci	pctrl->irq_chip.irq_enable = msm_gpio_irq_enable;
12648c2ecf20Sopenharmony_ci	pctrl->irq_chip.irq_disable = msm_gpio_irq_disable;
12658c2ecf20Sopenharmony_ci	pctrl->irq_chip.irq_mask = msm_gpio_irq_mask;
12668c2ecf20Sopenharmony_ci	pctrl->irq_chip.irq_unmask = msm_gpio_irq_unmask;
12678c2ecf20Sopenharmony_ci	pctrl->irq_chip.irq_ack = msm_gpio_irq_ack;
12688c2ecf20Sopenharmony_ci	pctrl->irq_chip.irq_set_type = msm_gpio_irq_set_type;
12698c2ecf20Sopenharmony_ci	pctrl->irq_chip.irq_set_wake = msm_gpio_irq_set_wake;
12708c2ecf20Sopenharmony_ci	pctrl->irq_chip.irq_request_resources = msm_gpio_irq_reqres;
12718c2ecf20Sopenharmony_ci	pctrl->irq_chip.irq_release_resources = msm_gpio_irq_relres;
12728c2ecf20Sopenharmony_ci	pctrl->irq_chip.irq_set_affinity = msm_gpio_irq_set_affinity;
12738c2ecf20Sopenharmony_ci	pctrl->irq_chip.irq_set_vcpu_affinity = msm_gpio_irq_set_vcpu_affinity;
12748c2ecf20Sopenharmony_ci	pctrl->irq_chip.flags = IRQCHIP_MASK_ON_SUSPEND |
12758c2ecf20Sopenharmony_ci				IRQCHIP_SET_TYPE_MASKED |
12768c2ecf20Sopenharmony_ci				IRQCHIP_ENABLE_WAKEUP_ON_SUSPEND;
12778c2ecf20Sopenharmony_ci
12788c2ecf20Sopenharmony_ci	np = of_parse_phandle(pctrl->dev->of_node, "wakeup-parent", 0);
12798c2ecf20Sopenharmony_ci	if (np) {
12808c2ecf20Sopenharmony_ci		chip->irq.parent_domain = irq_find_matching_host(np,
12818c2ecf20Sopenharmony_ci						 DOMAIN_BUS_WAKEUP);
12828c2ecf20Sopenharmony_ci		of_node_put(np);
12838c2ecf20Sopenharmony_ci		if (!chip->irq.parent_domain)
12848c2ecf20Sopenharmony_ci			return -EPROBE_DEFER;
12858c2ecf20Sopenharmony_ci		chip->irq.child_to_parent_hwirq = msm_gpio_wakeirq;
12868c2ecf20Sopenharmony_ci		pctrl->irq_chip.irq_eoi = irq_chip_eoi_parent;
12878c2ecf20Sopenharmony_ci		/*
12888c2ecf20Sopenharmony_ci		 * Let's skip handling the GPIOs, if the parent irqchip
12898c2ecf20Sopenharmony_ci		 * is handling the direct connect IRQ of the GPIO.
12908c2ecf20Sopenharmony_ci		 */
12918c2ecf20Sopenharmony_ci		skip = irq_domain_qcom_handle_wakeup(chip->irq.parent_domain);
12928c2ecf20Sopenharmony_ci		for (i = 0; skip && i < pctrl->soc->nwakeirq_map; i++) {
12938c2ecf20Sopenharmony_ci			gpio = pctrl->soc->wakeirq_map[i].gpio;
12948c2ecf20Sopenharmony_ci			set_bit(gpio, pctrl->skip_wake_irqs);
12958c2ecf20Sopenharmony_ci		}
12968c2ecf20Sopenharmony_ci	}
12978c2ecf20Sopenharmony_ci
12988c2ecf20Sopenharmony_ci	girq = &chip->irq;
12998c2ecf20Sopenharmony_ci	girq->chip = &pctrl->irq_chip;
13008c2ecf20Sopenharmony_ci	girq->parent_handler = msm_gpio_irq_handler;
13018c2ecf20Sopenharmony_ci	girq->fwnode = pctrl->dev->fwnode;
13028c2ecf20Sopenharmony_ci	girq->num_parents = 1;
13038c2ecf20Sopenharmony_ci	girq->parents = devm_kcalloc(pctrl->dev, 1, sizeof(*girq->parents),
13048c2ecf20Sopenharmony_ci				     GFP_KERNEL);
13058c2ecf20Sopenharmony_ci	if (!girq->parents)
13068c2ecf20Sopenharmony_ci		return -ENOMEM;
13078c2ecf20Sopenharmony_ci	girq->default_type = IRQ_TYPE_NONE;
13088c2ecf20Sopenharmony_ci	girq->handler = handle_bad_irq;
13098c2ecf20Sopenharmony_ci	girq->parents[0] = pctrl->irq;
13108c2ecf20Sopenharmony_ci
13118c2ecf20Sopenharmony_ci	ret = gpiochip_add_data(&pctrl->chip, pctrl);
13128c2ecf20Sopenharmony_ci	if (ret) {
13138c2ecf20Sopenharmony_ci		dev_err(pctrl->dev, "Failed register gpiochip\n");
13148c2ecf20Sopenharmony_ci		return ret;
13158c2ecf20Sopenharmony_ci	}
13168c2ecf20Sopenharmony_ci
13178c2ecf20Sopenharmony_ci	/*
13188c2ecf20Sopenharmony_ci	 * For DeviceTree-supported systems, the gpio core checks the
13198c2ecf20Sopenharmony_ci	 * pinctrl's device node for the "gpio-ranges" property.
13208c2ecf20Sopenharmony_ci	 * If it is present, it takes care of adding the pin ranges
13218c2ecf20Sopenharmony_ci	 * for the driver. In this case the driver can skip ahead.
13228c2ecf20Sopenharmony_ci	 *
13238c2ecf20Sopenharmony_ci	 * In order to remain compatible with older, existing DeviceTree
13248c2ecf20Sopenharmony_ci	 * files which don't set the "gpio-ranges" property or systems that
13258c2ecf20Sopenharmony_ci	 * utilize ACPI the driver has to call gpiochip_add_pin_range().
13268c2ecf20Sopenharmony_ci	 */
13278c2ecf20Sopenharmony_ci	if (!of_property_read_bool(pctrl->dev->of_node, "gpio-ranges")) {
13288c2ecf20Sopenharmony_ci		ret = gpiochip_add_pin_range(&pctrl->chip,
13298c2ecf20Sopenharmony_ci			dev_name(pctrl->dev), 0, 0, chip->ngpio);
13308c2ecf20Sopenharmony_ci		if (ret) {
13318c2ecf20Sopenharmony_ci			dev_err(pctrl->dev, "Failed to add pin range\n");
13328c2ecf20Sopenharmony_ci			gpiochip_remove(&pctrl->chip);
13338c2ecf20Sopenharmony_ci			return ret;
13348c2ecf20Sopenharmony_ci		}
13358c2ecf20Sopenharmony_ci	}
13368c2ecf20Sopenharmony_ci
13378c2ecf20Sopenharmony_ci	return 0;
13388c2ecf20Sopenharmony_ci}
13398c2ecf20Sopenharmony_ci
13408c2ecf20Sopenharmony_cistatic int msm_ps_hold_restart(struct notifier_block *nb, unsigned long action,
13418c2ecf20Sopenharmony_ci			       void *data)
13428c2ecf20Sopenharmony_ci{
13438c2ecf20Sopenharmony_ci	struct msm_pinctrl *pctrl = container_of(nb, struct msm_pinctrl, restart_nb);
13448c2ecf20Sopenharmony_ci
13458c2ecf20Sopenharmony_ci	writel(0, pctrl->regs[0] + PS_HOLD_OFFSET);
13468c2ecf20Sopenharmony_ci	mdelay(1000);
13478c2ecf20Sopenharmony_ci	return NOTIFY_DONE;
13488c2ecf20Sopenharmony_ci}
13498c2ecf20Sopenharmony_ci
13508c2ecf20Sopenharmony_cistatic struct msm_pinctrl *poweroff_pctrl;
13518c2ecf20Sopenharmony_ci
13528c2ecf20Sopenharmony_cistatic void msm_ps_hold_poweroff(void)
13538c2ecf20Sopenharmony_ci{
13548c2ecf20Sopenharmony_ci	msm_ps_hold_restart(&poweroff_pctrl->restart_nb, 0, NULL);
13558c2ecf20Sopenharmony_ci}
13568c2ecf20Sopenharmony_ci
13578c2ecf20Sopenharmony_cistatic void msm_pinctrl_setup_pm_reset(struct msm_pinctrl *pctrl)
13588c2ecf20Sopenharmony_ci{
13598c2ecf20Sopenharmony_ci	int i;
13608c2ecf20Sopenharmony_ci	const struct msm_function *func = pctrl->soc->functions;
13618c2ecf20Sopenharmony_ci
13628c2ecf20Sopenharmony_ci	for (i = 0; i < pctrl->soc->nfunctions; i++)
13638c2ecf20Sopenharmony_ci		if (!strcmp(func[i].name, "ps_hold")) {
13648c2ecf20Sopenharmony_ci			pctrl->restart_nb.notifier_call = msm_ps_hold_restart;
13658c2ecf20Sopenharmony_ci			pctrl->restart_nb.priority = 128;
13668c2ecf20Sopenharmony_ci			if (register_restart_handler(&pctrl->restart_nb))
13678c2ecf20Sopenharmony_ci				dev_err(pctrl->dev,
13688c2ecf20Sopenharmony_ci					"failed to setup restart handler.\n");
13698c2ecf20Sopenharmony_ci			poweroff_pctrl = pctrl;
13708c2ecf20Sopenharmony_ci			pm_power_off = msm_ps_hold_poweroff;
13718c2ecf20Sopenharmony_ci			break;
13728c2ecf20Sopenharmony_ci		}
13738c2ecf20Sopenharmony_ci}
13748c2ecf20Sopenharmony_ci
13758c2ecf20Sopenharmony_cistatic __maybe_unused int msm_pinctrl_suspend(struct device *dev)
13768c2ecf20Sopenharmony_ci{
13778c2ecf20Sopenharmony_ci	struct msm_pinctrl *pctrl = dev_get_drvdata(dev);
13788c2ecf20Sopenharmony_ci
13798c2ecf20Sopenharmony_ci	return pinctrl_force_sleep(pctrl->pctrl);
13808c2ecf20Sopenharmony_ci}
13818c2ecf20Sopenharmony_ci
13828c2ecf20Sopenharmony_cistatic __maybe_unused int msm_pinctrl_resume(struct device *dev)
13838c2ecf20Sopenharmony_ci{
13848c2ecf20Sopenharmony_ci	struct msm_pinctrl *pctrl = dev_get_drvdata(dev);
13858c2ecf20Sopenharmony_ci
13868c2ecf20Sopenharmony_ci	return pinctrl_force_default(pctrl->pctrl);
13878c2ecf20Sopenharmony_ci}
13888c2ecf20Sopenharmony_ci
13898c2ecf20Sopenharmony_ciSIMPLE_DEV_PM_OPS(msm_pinctrl_dev_pm_ops, msm_pinctrl_suspend,
13908c2ecf20Sopenharmony_ci		  msm_pinctrl_resume);
13918c2ecf20Sopenharmony_ci
13928c2ecf20Sopenharmony_ciEXPORT_SYMBOL(msm_pinctrl_dev_pm_ops);
13938c2ecf20Sopenharmony_ci
13948c2ecf20Sopenharmony_ciint msm_pinctrl_probe(struct platform_device *pdev,
13958c2ecf20Sopenharmony_ci		      const struct msm_pinctrl_soc_data *soc_data)
13968c2ecf20Sopenharmony_ci{
13978c2ecf20Sopenharmony_ci	struct msm_pinctrl *pctrl;
13988c2ecf20Sopenharmony_ci	struct resource *res;
13998c2ecf20Sopenharmony_ci	int ret;
14008c2ecf20Sopenharmony_ci	int i;
14018c2ecf20Sopenharmony_ci
14028c2ecf20Sopenharmony_ci	pctrl = devm_kzalloc(&pdev->dev, sizeof(*pctrl), GFP_KERNEL);
14038c2ecf20Sopenharmony_ci	if (!pctrl)
14048c2ecf20Sopenharmony_ci		return -ENOMEM;
14058c2ecf20Sopenharmony_ci
14068c2ecf20Sopenharmony_ci	pctrl->dev = &pdev->dev;
14078c2ecf20Sopenharmony_ci	pctrl->soc = soc_data;
14088c2ecf20Sopenharmony_ci	pctrl->chip = msm_gpio_template;
14098c2ecf20Sopenharmony_ci	pctrl->intr_target_use_scm = of_device_is_compatible(
14108c2ecf20Sopenharmony_ci					pctrl->dev->of_node,
14118c2ecf20Sopenharmony_ci					"qcom,ipq8064-pinctrl");
14128c2ecf20Sopenharmony_ci
14138c2ecf20Sopenharmony_ci	raw_spin_lock_init(&pctrl->lock);
14148c2ecf20Sopenharmony_ci
14158c2ecf20Sopenharmony_ci	if (soc_data->tiles) {
14168c2ecf20Sopenharmony_ci		for (i = 0; i < soc_data->ntiles; i++) {
14178c2ecf20Sopenharmony_ci			res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
14188c2ecf20Sopenharmony_ci							   soc_data->tiles[i]);
14198c2ecf20Sopenharmony_ci			pctrl->regs[i] = devm_ioremap_resource(&pdev->dev, res);
14208c2ecf20Sopenharmony_ci			if (IS_ERR(pctrl->regs[i]))
14218c2ecf20Sopenharmony_ci				return PTR_ERR(pctrl->regs[i]);
14228c2ecf20Sopenharmony_ci		}
14238c2ecf20Sopenharmony_ci	} else {
14248c2ecf20Sopenharmony_ci		res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
14258c2ecf20Sopenharmony_ci		pctrl->regs[0] = devm_ioremap_resource(&pdev->dev, res);
14268c2ecf20Sopenharmony_ci		if (IS_ERR(pctrl->regs[0]))
14278c2ecf20Sopenharmony_ci			return PTR_ERR(pctrl->regs[0]);
14288c2ecf20Sopenharmony_ci
14298c2ecf20Sopenharmony_ci		pctrl->phys_base[0] = res->start;
14308c2ecf20Sopenharmony_ci	}
14318c2ecf20Sopenharmony_ci
14328c2ecf20Sopenharmony_ci	msm_pinctrl_setup_pm_reset(pctrl);
14338c2ecf20Sopenharmony_ci
14348c2ecf20Sopenharmony_ci	pctrl->irq = platform_get_irq(pdev, 0);
14358c2ecf20Sopenharmony_ci	if (pctrl->irq < 0)
14368c2ecf20Sopenharmony_ci		return pctrl->irq;
14378c2ecf20Sopenharmony_ci
14388c2ecf20Sopenharmony_ci	pctrl->desc.owner = THIS_MODULE;
14398c2ecf20Sopenharmony_ci	pctrl->desc.pctlops = &msm_pinctrl_ops;
14408c2ecf20Sopenharmony_ci	pctrl->desc.pmxops = &msm_pinmux_ops;
14418c2ecf20Sopenharmony_ci	pctrl->desc.confops = &msm_pinconf_ops;
14428c2ecf20Sopenharmony_ci	pctrl->desc.name = dev_name(&pdev->dev);
14438c2ecf20Sopenharmony_ci	pctrl->desc.pins = pctrl->soc->pins;
14448c2ecf20Sopenharmony_ci	pctrl->desc.npins = pctrl->soc->npins;
14458c2ecf20Sopenharmony_ci
14468c2ecf20Sopenharmony_ci	pctrl->pctrl = devm_pinctrl_register(&pdev->dev, &pctrl->desc, pctrl);
14478c2ecf20Sopenharmony_ci	if (IS_ERR(pctrl->pctrl)) {
14488c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Couldn't register pinctrl driver\n");
14498c2ecf20Sopenharmony_ci		return PTR_ERR(pctrl->pctrl);
14508c2ecf20Sopenharmony_ci	}
14518c2ecf20Sopenharmony_ci
14528c2ecf20Sopenharmony_ci	ret = msm_gpio_init(pctrl);
14538c2ecf20Sopenharmony_ci	if (ret)
14548c2ecf20Sopenharmony_ci		return ret;
14558c2ecf20Sopenharmony_ci
14568c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, pctrl);
14578c2ecf20Sopenharmony_ci
14588c2ecf20Sopenharmony_ci	dev_dbg(&pdev->dev, "Probed Qualcomm pinctrl driver\n");
14598c2ecf20Sopenharmony_ci
14608c2ecf20Sopenharmony_ci	return 0;
14618c2ecf20Sopenharmony_ci}
14628c2ecf20Sopenharmony_ciEXPORT_SYMBOL(msm_pinctrl_probe);
14638c2ecf20Sopenharmony_ci
14648c2ecf20Sopenharmony_ciint msm_pinctrl_remove(struct platform_device *pdev)
14658c2ecf20Sopenharmony_ci{
14668c2ecf20Sopenharmony_ci	struct msm_pinctrl *pctrl = platform_get_drvdata(pdev);
14678c2ecf20Sopenharmony_ci
14688c2ecf20Sopenharmony_ci	gpiochip_remove(&pctrl->chip);
14698c2ecf20Sopenharmony_ci
14708c2ecf20Sopenharmony_ci	unregister_restart_handler(&pctrl->restart_nb);
14718c2ecf20Sopenharmony_ci
14728c2ecf20Sopenharmony_ci	return 0;
14738c2ecf20Sopenharmony_ci}
14748c2ecf20Sopenharmony_ciEXPORT_SYMBOL(msm_pinctrl_remove);
14758c2ecf20Sopenharmony_ci
1476