162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Device driver for irqs in HISI PMIC IC
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2013 Linaro Ltd.
662306a36Sopenharmony_ci * Copyright (c) 2011 Hisilicon.
762306a36Sopenharmony_ci * Copyright (c) 2020-2021 Huawei Technologies Co., Ltd.
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/bitops.h>
1162306a36Sopenharmony_ci#include <linux/interrupt.h>
1262306a36Sopenharmony_ci#include <linux/irq.h>
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/of_gpio.h>
1562306a36Sopenharmony_ci#include <linux/platform_device.h>
1662306a36Sopenharmony_ci#include <linux/slab.h>
1762306a36Sopenharmony_ci#include <linux/irqdomain.h>
1862306a36Sopenharmony_ci#include <linux/regmap.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_cistruct hi6421v600_irq {
2162306a36Sopenharmony_ci	struct device		*dev;
2262306a36Sopenharmony_ci	struct irq_domain	*domain;
2362306a36Sopenharmony_ci	int			irq;
2462306a36Sopenharmony_ci	unsigned int		*irqs;
2562306a36Sopenharmony_ci	struct regmap		*regmap;
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci	/* Protect IRQ mask changes */
2862306a36Sopenharmony_ci	spinlock_t		lock;
2962306a36Sopenharmony_ci};
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cienum hi6421v600_irq_list {
3262306a36Sopenharmony_ci	OTMP = 0,
3362306a36Sopenharmony_ci	VBUS_CONNECT,
3462306a36Sopenharmony_ci	VBUS_DISCONNECT,
3562306a36Sopenharmony_ci	ALARMON_R,
3662306a36Sopenharmony_ci	HOLD_6S,
3762306a36Sopenharmony_ci	HOLD_1S,
3862306a36Sopenharmony_ci	POWERKEY_UP,
3962306a36Sopenharmony_ci	POWERKEY_DOWN,
4062306a36Sopenharmony_ci	OCP_SCP_R,
4162306a36Sopenharmony_ci	COUL_R,
4262306a36Sopenharmony_ci	SIM0_HPD_R,
4362306a36Sopenharmony_ci	SIM0_HPD_F,
4462306a36Sopenharmony_ci	SIM1_HPD_R,
4562306a36Sopenharmony_ci	SIM1_HPD_F,
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	PMIC_IRQ_LIST_MAX
4862306a36Sopenharmony_ci};
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci#define HISI_IRQ_BANK_SIZE		2
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci/*
5362306a36Sopenharmony_ci * IRQ number for the power key button and mask for both UP and DOWN IRQs
5462306a36Sopenharmony_ci */
5562306a36Sopenharmony_ci#define HISI_POWERKEY_IRQ_NUM		0
5662306a36Sopenharmony_ci#define HISI_IRQ_POWERKEY_UP_DOWN	(BIT(POWERKEY_DOWN) | BIT(POWERKEY_UP))
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci/*
5962306a36Sopenharmony_ci * Registers for IRQ address and IRQ mask bits
6062306a36Sopenharmony_ci *
6162306a36Sopenharmony_ci * Please notice that we need to regmap a larger region, as other
6262306a36Sopenharmony_ci * registers are used by the irqs.
6362306a36Sopenharmony_ci * See drivers/irq/hi6421-irq.c.
6462306a36Sopenharmony_ci */
6562306a36Sopenharmony_ci#define SOC_PMIC_IRQ_MASK_0_ADDR	0x0202
6662306a36Sopenharmony_ci#define SOC_PMIC_IRQ0_ADDR		0x0212
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci/*
6962306a36Sopenharmony_ci * The IRQs are mapped as:
7062306a36Sopenharmony_ci *
7162306a36Sopenharmony_ci *	======================  =============   ============	=====
7262306a36Sopenharmony_ci *	IRQ			MASK REGISTER	IRQ REGISTER	BIT
7362306a36Sopenharmony_ci *	======================  =============   ============	=====
7462306a36Sopenharmony_ci *	OTMP			0x0202		0x212		bit 0
7562306a36Sopenharmony_ci *	VBUS_CONNECT		0x0202		0x212		bit 1
7662306a36Sopenharmony_ci *	VBUS_DISCONNECT		0x0202		0x212		bit 2
7762306a36Sopenharmony_ci *	ALARMON_R		0x0202		0x212		bit 3
7862306a36Sopenharmony_ci *	HOLD_6S			0x0202		0x212		bit 4
7962306a36Sopenharmony_ci *	HOLD_1S			0x0202		0x212		bit 5
8062306a36Sopenharmony_ci *	POWERKEY_UP		0x0202		0x212		bit 6
8162306a36Sopenharmony_ci *	POWERKEY_DOWN		0x0202		0x212		bit 7
8262306a36Sopenharmony_ci *
8362306a36Sopenharmony_ci *	OCP_SCP_R		0x0203		0x213		bit 0
8462306a36Sopenharmony_ci *	COUL_R			0x0203		0x213		bit 1
8562306a36Sopenharmony_ci *	SIM0_HPD_R		0x0203		0x213		bit 2
8662306a36Sopenharmony_ci *	SIM0_HPD_F		0x0203		0x213		bit 3
8762306a36Sopenharmony_ci *	SIM1_HPD_R		0x0203		0x213		bit 4
8862306a36Sopenharmony_ci *	SIM1_HPD_F		0x0203		0x213		bit 5
8962306a36Sopenharmony_ci *	======================  =============   ============	=====
9062306a36Sopenharmony_ci *
9162306a36Sopenharmony_ci * Each mask register contains 8 bits. The ancillary macros below
9262306a36Sopenharmony_ci * convert a number from 0 to 14 into a register address and a bit mask
9362306a36Sopenharmony_ci */
9462306a36Sopenharmony_ci#define HISI_IRQ_MASK_REG(irq_data)	(SOC_PMIC_IRQ_MASK_0_ADDR + \
9562306a36Sopenharmony_ci					 (irqd_to_hwirq(irq_data) / BITS_PER_BYTE))
9662306a36Sopenharmony_ci#define HISI_IRQ_MASK_BIT(irq_data)	BIT(irqd_to_hwirq(irq_data) & (BITS_PER_BYTE - 1))
9762306a36Sopenharmony_ci#define HISI_8BITS_MASK			0xff
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cistatic irqreturn_t hi6421v600_irq_handler(int irq, void *__priv)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	struct hi6421v600_irq *priv = __priv;
10262306a36Sopenharmony_ci	unsigned long pending;
10362306a36Sopenharmony_ci	unsigned int in;
10462306a36Sopenharmony_ci	int i, offset;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	for (i = 0; i < HISI_IRQ_BANK_SIZE; i++) {
10762306a36Sopenharmony_ci		regmap_read(priv->regmap, SOC_PMIC_IRQ0_ADDR + i, &in);
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci		/* Mark pending IRQs as handled */
11062306a36Sopenharmony_ci		regmap_write(priv->regmap, SOC_PMIC_IRQ0_ADDR + i, in);
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci		pending = in & HISI_8BITS_MASK;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci		if (i == HISI_POWERKEY_IRQ_NUM &&
11562306a36Sopenharmony_ci		    (pending & HISI_IRQ_POWERKEY_UP_DOWN) == HISI_IRQ_POWERKEY_UP_DOWN) {
11662306a36Sopenharmony_ci			/*
11762306a36Sopenharmony_ci			 * If both powerkey down and up IRQs are received,
11862306a36Sopenharmony_ci			 * handle them at the right order
11962306a36Sopenharmony_ci			 */
12062306a36Sopenharmony_ci			generic_handle_irq_safe(priv->irqs[POWERKEY_DOWN]);
12162306a36Sopenharmony_ci			generic_handle_irq_safe(priv->irqs[POWERKEY_UP]);
12262306a36Sopenharmony_ci			pending &= ~HISI_IRQ_POWERKEY_UP_DOWN;
12362306a36Sopenharmony_ci		}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci		if (!pending)
12662306a36Sopenharmony_ci			continue;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci		for_each_set_bit(offset, &pending, BITS_PER_BYTE) {
12962306a36Sopenharmony_ci			generic_handle_irq_safe(priv->irqs[offset + i * BITS_PER_BYTE]);
13062306a36Sopenharmony_ci		}
13162306a36Sopenharmony_ci	}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	return IRQ_HANDLED;
13462306a36Sopenharmony_ci}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_cistatic void hi6421v600_irq_mask(struct irq_data *d)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	struct hi6421v600_irq *priv = irq_data_get_irq_chip_data(d);
13962306a36Sopenharmony_ci	unsigned long flags;
14062306a36Sopenharmony_ci	unsigned int data;
14162306a36Sopenharmony_ci	u32 offset;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	offset = HISI_IRQ_MASK_REG(d);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	spin_lock_irqsave(&priv->lock, flags);
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	regmap_read(priv->regmap, offset, &data);
14862306a36Sopenharmony_ci	data |= HISI_IRQ_MASK_BIT(d);
14962306a36Sopenharmony_ci	regmap_write(priv->regmap, offset, data);
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, flags);
15262306a36Sopenharmony_ci}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_cistatic void hi6421v600_irq_unmask(struct irq_data *d)
15562306a36Sopenharmony_ci{
15662306a36Sopenharmony_ci	struct hi6421v600_irq *priv = irq_data_get_irq_chip_data(d);
15762306a36Sopenharmony_ci	u32 data, offset;
15862306a36Sopenharmony_ci	unsigned long flags;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	offset = HISI_IRQ_MASK_REG(d);
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	spin_lock_irqsave(&priv->lock, flags);
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	regmap_read(priv->regmap, offset, &data);
16562306a36Sopenharmony_ci	data &= ~HISI_IRQ_MASK_BIT(d);
16662306a36Sopenharmony_ci	regmap_write(priv->regmap, offset, data);
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, flags);
16962306a36Sopenharmony_ci}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_cistatic struct irq_chip hi6421v600_pmu_irqchip = {
17262306a36Sopenharmony_ci	.name		= "hi6421v600-irq",
17362306a36Sopenharmony_ci	.irq_mask	= hi6421v600_irq_mask,
17462306a36Sopenharmony_ci	.irq_unmask	= hi6421v600_irq_unmask,
17562306a36Sopenharmony_ci	.irq_disable	= hi6421v600_irq_mask,
17662306a36Sopenharmony_ci	.irq_enable	= hi6421v600_irq_unmask,
17762306a36Sopenharmony_ci};
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_cistatic int hi6421v600_irq_map(struct irq_domain *d, unsigned int virq,
18062306a36Sopenharmony_ci			      irq_hw_number_t hw)
18162306a36Sopenharmony_ci{
18262306a36Sopenharmony_ci	struct hi6421v600_irq *priv = d->host_data;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	irq_set_chip_and_handler_name(virq, &hi6421v600_pmu_irqchip,
18562306a36Sopenharmony_ci				      handle_simple_irq, "hi6421v600");
18662306a36Sopenharmony_ci	irq_set_chip_data(virq, priv);
18762306a36Sopenharmony_ci	irq_set_irq_type(virq, IRQ_TYPE_NONE);
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	return 0;
19062306a36Sopenharmony_ci}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_cistatic const struct irq_domain_ops hi6421v600_domain_ops = {
19362306a36Sopenharmony_ci	.map	= hi6421v600_irq_map,
19462306a36Sopenharmony_ci	.xlate	= irq_domain_xlate_twocell,
19562306a36Sopenharmony_ci};
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cistatic void hi6421v600_irq_init(struct hi6421v600_irq *priv)
19862306a36Sopenharmony_ci{
19962306a36Sopenharmony_ci	int i;
20062306a36Sopenharmony_ci	unsigned int pending;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	/* Mask all IRQs */
20362306a36Sopenharmony_ci	for (i = 0; i < HISI_IRQ_BANK_SIZE; i++)
20462306a36Sopenharmony_ci		regmap_write(priv->regmap, SOC_PMIC_IRQ_MASK_0_ADDR + i,
20562306a36Sopenharmony_ci			     HISI_8BITS_MASK);
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	/* Mark all IRQs as handled */
20862306a36Sopenharmony_ci	for (i = 0; i < HISI_IRQ_BANK_SIZE; i++) {
20962306a36Sopenharmony_ci		regmap_read(priv->regmap, SOC_PMIC_IRQ0_ADDR + i, &pending);
21062306a36Sopenharmony_ci		regmap_write(priv->regmap, SOC_PMIC_IRQ0_ADDR + i,
21162306a36Sopenharmony_ci			     HISI_8BITS_MASK);
21262306a36Sopenharmony_ci	}
21362306a36Sopenharmony_ci}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_cistatic int hi6421v600_irq_probe(struct platform_device *pdev)
21662306a36Sopenharmony_ci{
21762306a36Sopenharmony_ci	struct device *pmic_dev = pdev->dev.parent;
21862306a36Sopenharmony_ci	struct device_node *np = pmic_dev->of_node;
21962306a36Sopenharmony_ci	struct platform_device *pmic_pdev;
22062306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
22162306a36Sopenharmony_ci	struct hi6421v600_irq *priv;
22262306a36Sopenharmony_ci	struct regmap *regmap;
22362306a36Sopenharmony_ci	unsigned int virq;
22462306a36Sopenharmony_ci	int i, ret;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	/*
22762306a36Sopenharmony_ci	 * This driver is meant to be called by hi6421-spmi-core,
22862306a36Sopenharmony_ci	 * which should first set drvdata. If this doesn't happen, hit
22962306a36Sopenharmony_ci	 * a warn on and return.
23062306a36Sopenharmony_ci	 */
23162306a36Sopenharmony_ci	regmap = dev_get_drvdata(pmic_dev);
23262306a36Sopenharmony_ci	if (WARN_ON(!regmap))
23362306a36Sopenharmony_ci		return -ENODEV;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
23662306a36Sopenharmony_ci	if (!priv)
23762306a36Sopenharmony_ci		return -ENOMEM;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	priv->dev = dev;
24062306a36Sopenharmony_ci	priv->regmap = regmap;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	spin_lock_init(&priv->lock);
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	pmic_pdev = container_of(pmic_dev, struct platform_device, dev);
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	priv->irq = platform_get_irq(pmic_pdev, 0);
24762306a36Sopenharmony_ci	if (priv->irq < 0)
24862306a36Sopenharmony_ci		return priv->irq;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	platform_set_drvdata(pdev, priv);
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	hi6421v600_irq_init(priv);
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	priv->irqs = devm_kzalloc(dev, PMIC_IRQ_LIST_MAX * sizeof(int), GFP_KERNEL);
25562306a36Sopenharmony_ci	if (!priv->irqs)
25662306a36Sopenharmony_ci		return -ENOMEM;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	priv->domain = irq_domain_add_simple(np, PMIC_IRQ_LIST_MAX, 0,
25962306a36Sopenharmony_ci					     &hi6421v600_domain_ops, priv);
26062306a36Sopenharmony_ci	if (!priv->domain) {
26162306a36Sopenharmony_ci		dev_err(dev, "Failed to create IRQ domain\n");
26262306a36Sopenharmony_ci		return -ENODEV;
26362306a36Sopenharmony_ci	}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	for (i = 0; i < PMIC_IRQ_LIST_MAX; i++) {
26662306a36Sopenharmony_ci		virq = irq_create_mapping(priv->domain, i);
26762306a36Sopenharmony_ci		if (!virq) {
26862306a36Sopenharmony_ci			dev_err(dev, "Failed to map H/W IRQ\n");
26962306a36Sopenharmony_ci			return -ENODEV;
27062306a36Sopenharmony_ci		}
27162306a36Sopenharmony_ci		priv->irqs[i] = virq;
27262306a36Sopenharmony_ci	}
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	ret = devm_request_threaded_irq(dev,
27562306a36Sopenharmony_ci					priv->irq, hi6421v600_irq_handler,
27662306a36Sopenharmony_ci					NULL,
27762306a36Sopenharmony_ci					IRQF_TRIGGER_LOW | IRQF_SHARED | IRQF_NO_SUSPEND,
27862306a36Sopenharmony_ci					"pmic", priv);
27962306a36Sopenharmony_ci	if (ret < 0) {
28062306a36Sopenharmony_ci		dev_err(dev, "Failed to start IRQ handling thread: error %d\n",
28162306a36Sopenharmony_ci			ret);
28262306a36Sopenharmony_ci		return ret;
28362306a36Sopenharmony_ci	}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	return 0;
28662306a36Sopenharmony_ci}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_cistatic const struct platform_device_id hi6421v600_irq_table[] = {
28962306a36Sopenharmony_ci	{ .name = "hi6421v600-irq" },
29062306a36Sopenharmony_ci	{},
29162306a36Sopenharmony_ci};
29262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(platform, hi6421v600_irq_table);
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_cistatic struct platform_driver hi6421v600_irq_driver = {
29562306a36Sopenharmony_ci	.id_table = hi6421v600_irq_table,
29662306a36Sopenharmony_ci	.driver = {
29762306a36Sopenharmony_ci		.name = "hi6421v600-irq",
29862306a36Sopenharmony_ci	},
29962306a36Sopenharmony_ci	.probe	= hi6421v600_irq_probe,
30062306a36Sopenharmony_ci};
30162306a36Sopenharmony_cimodule_platform_driver(hi6421v600_irq_driver);
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ciMODULE_DESCRIPTION("HiSilicon Hi6421v600 IRQ driver");
30462306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
305