162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#define pr_fmt(fmt) "%s: " fmt, __func__
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/kernel.h>
962306a36Sopenharmony_ci#include <linux/interrupt.h>
1062306a36Sopenharmony_ci#include <linux/irqchip/chained_irq.h>
1162306a36Sopenharmony_ci#include <linux/irq.h>
1262306a36Sopenharmony_ci#include <linux/irqdomain.h>
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/platform_device.h>
1562306a36Sopenharmony_ci#include <linux/slab.h>
1662306a36Sopenharmony_ci#include <linux/err.h>
1762306a36Sopenharmony_ci#include <linux/ssbi.h>
1862306a36Sopenharmony_ci#include <linux/regmap.h>
1962306a36Sopenharmony_ci#include <linux/of_platform.h>
2062306a36Sopenharmony_ci#include <linux/mfd/core.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#define	SSBI_REG_ADDR_IRQ_BASE		0x1BB
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#define	SSBI_REG_ADDR_IRQ_ROOT		(SSBI_REG_ADDR_IRQ_BASE + 0)
2562306a36Sopenharmony_ci#define	SSBI_REG_ADDR_IRQ_M_STATUS1	(SSBI_REG_ADDR_IRQ_BASE + 1)
2662306a36Sopenharmony_ci#define	SSBI_REG_ADDR_IRQ_M_STATUS2	(SSBI_REG_ADDR_IRQ_BASE + 2)
2762306a36Sopenharmony_ci#define	SSBI_REG_ADDR_IRQ_M_STATUS3	(SSBI_REG_ADDR_IRQ_BASE + 3)
2862306a36Sopenharmony_ci#define	SSBI_REG_ADDR_IRQ_M_STATUS4	(SSBI_REG_ADDR_IRQ_BASE + 4)
2962306a36Sopenharmony_ci#define	SSBI_REG_ADDR_IRQ_BLK_SEL	(SSBI_REG_ADDR_IRQ_BASE + 5)
3062306a36Sopenharmony_ci#define	SSBI_REG_ADDR_IRQ_IT_STATUS	(SSBI_REG_ADDR_IRQ_BASE + 6)
3162306a36Sopenharmony_ci#define	SSBI_REG_ADDR_IRQ_CONFIG	(SSBI_REG_ADDR_IRQ_BASE + 7)
3262306a36Sopenharmony_ci#define	SSBI_REG_ADDR_IRQ_RT_STATUS	(SSBI_REG_ADDR_IRQ_BASE + 8)
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#define	PM8821_SSBI_REG_ADDR_IRQ_BASE	0x100
3562306a36Sopenharmony_ci#define	PM8821_SSBI_REG_ADDR_IRQ_MASTER0 (PM8821_SSBI_REG_ADDR_IRQ_BASE + 0x30)
3662306a36Sopenharmony_ci#define	PM8821_SSBI_REG_ADDR_IRQ_MASTER1 (PM8821_SSBI_REG_ADDR_IRQ_BASE + 0xb0)
3762306a36Sopenharmony_ci#define	PM8821_SSBI_REG(m, b, offset) \
3862306a36Sopenharmony_ci			((m == 0) ? \
3962306a36Sopenharmony_ci			(PM8821_SSBI_REG_ADDR_IRQ_MASTER0 + b + offset) : \
4062306a36Sopenharmony_ci			(PM8821_SSBI_REG_ADDR_IRQ_MASTER1 + b + offset))
4162306a36Sopenharmony_ci#define	PM8821_SSBI_ADDR_IRQ_ROOT(m, b)		PM8821_SSBI_REG(m, b, 0x0)
4262306a36Sopenharmony_ci#define	PM8821_SSBI_ADDR_IRQ_CLEAR(m, b)	PM8821_SSBI_REG(m, b, 0x01)
4362306a36Sopenharmony_ci#define	PM8821_SSBI_ADDR_IRQ_MASK(m, b)		PM8821_SSBI_REG(m, b, 0x08)
4462306a36Sopenharmony_ci#define	PM8821_SSBI_ADDR_IRQ_RT_STATUS(m, b)	PM8821_SSBI_REG(m, b, 0x0f)
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci#define	PM8821_BLOCKS_PER_MASTER	7
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci#define	PM_IRQF_LVL_SEL			0x01	/* level select */
4962306a36Sopenharmony_ci#define	PM_IRQF_MASK_FE			0x02	/* mask falling edge */
5062306a36Sopenharmony_ci#define	PM_IRQF_MASK_RE			0x04	/* mask rising edge */
5162306a36Sopenharmony_ci#define	PM_IRQF_CLR			0x08	/* clear interrupt */
5262306a36Sopenharmony_ci#define	PM_IRQF_BITS_MASK		0x70
5362306a36Sopenharmony_ci#define	PM_IRQF_BITS_SHIFT		4
5462306a36Sopenharmony_ci#define	PM_IRQF_WRITE			0x80
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci#define	PM_IRQF_MASK_ALL		(PM_IRQF_MASK_FE | \
5762306a36Sopenharmony_ci					PM_IRQF_MASK_RE)
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci#define REG_HWREV		0x002  /* PMIC4 revision */
6062306a36Sopenharmony_ci#define REG_HWREV_2		0x0E8  /* PMIC4 revision 2 */
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci#define PM8XXX_NR_IRQS		256
6362306a36Sopenharmony_ci#define PM8821_NR_IRQS		112
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistruct pm_irq_data {
6662306a36Sopenharmony_ci	int num_irqs;
6762306a36Sopenharmony_ci	struct irq_chip *irq_chip;
6862306a36Sopenharmony_ci	irq_handler_t irq_handler;
6962306a36Sopenharmony_ci};
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistruct pm_irq_chip {
7262306a36Sopenharmony_ci	struct regmap		*regmap;
7362306a36Sopenharmony_ci	spinlock_t		pm_irq_lock;
7462306a36Sopenharmony_ci	struct irq_domain	*irqdomain;
7562306a36Sopenharmony_ci	unsigned int		num_blocks;
7662306a36Sopenharmony_ci	unsigned int		num_masters;
7762306a36Sopenharmony_ci	const struct pm_irq_data *pm_irq_data;
7862306a36Sopenharmony_ci	/* MUST BE AT THE END OF THIS STRUCT */
7962306a36Sopenharmony_ci	u8			config[];
8062306a36Sopenharmony_ci};
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cistatic int pm8xxx_read_block_irq(struct pm_irq_chip *chip, unsigned int bp,
8362306a36Sopenharmony_ci				 unsigned int *ip)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	int	rc;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	spin_lock(&chip->pm_irq_lock);
8862306a36Sopenharmony_ci	rc = regmap_write(chip->regmap, SSBI_REG_ADDR_IRQ_BLK_SEL, bp);
8962306a36Sopenharmony_ci	if (rc) {
9062306a36Sopenharmony_ci		pr_err("Failed Selecting Block %d rc=%d\n", bp, rc);
9162306a36Sopenharmony_ci		goto bail;
9262306a36Sopenharmony_ci	}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	rc = regmap_read(chip->regmap, SSBI_REG_ADDR_IRQ_IT_STATUS, ip);
9562306a36Sopenharmony_ci	if (rc)
9662306a36Sopenharmony_ci		pr_err("Failed Reading Status rc=%d\n", rc);
9762306a36Sopenharmony_cibail:
9862306a36Sopenharmony_ci	spin_unlock(&chip->pm_irq_lock);
9962306a36Sopenharmony_ci	return rc;
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistatic int
10362306a36Sopenharmony_cipm8xxx_config_irq(struct pm_irq_chip *chip, unsigned int bp, unsigned int cp)
10462306a36Sopenharmony_ci{
10562306a36Sopenharmony_ci	int	rc;
10662306a36Sopenharmony_ci	unsigned long flags;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	spin_lock_irqsave(&chip->pm_irq_lock, flags);
10962306a36Sopenharmony_ci	rc = regmap_write(chip->regmap, SSBI_REG_ADDR_IRQ_BLK_SEL, bp);
11062306a36Sopenharmony_ci	if (rc) {
11162306a36Sopenharmony_ci		pr_err("Failed Selecting Block %d rc=%d\n", bp, rc);
11262306a36Sopenharmony_ci		goto bail;
11362306a36Sopenharmony_ci	}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	cp |= PM_IRQF_WRITE;
11662306a36Sopenharmony_ci	rc = regmap_write(chip->regmap, SSBI_REG_ADDR_IRQ_CONFIG, cp);
11762306a36Sopenharmony_ci	if (rc)
11862306a36Sopenharmony_ci		pr_err("Failed Configuring IRQ rc=%d\n", rc);
11962306a36Sopenharmony_cibail:
12062306a36Sopenharmony_ci	spin_unlock_irqrestore(&chip->pm_irq_lock, flags);
12162306a36Sopenharmony_ci	return rc;
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cistatic int pm8xxx_irq_block_handler(struct pm_irq_chip *chip, int block)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	int pmirq, i, ret = 0;
12762306a36Sopenharmony_ci	unsigned int bits;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	ret = pm8xxx_read_block_irq(chip, block, &bits);
13062306a36Sopenharmony_ci	if (ret) {
13162306a36Sopenharmony_ci		pr_err("Failed reading %d block ret=%d", block, ret);
13262306a36Sopenharmony_ci		return ret;
13362306a36Sopenharmony_ci	}
13462306a36Sopenharmony_ci	if (!bits) {
13562306a36Sopenharmony_ci		pr_err("block bit set in master but no irqs: %d", block);
13662306a36Sopenharmony_ci		return 0;
13762306a36Sopenharmony_ci	}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	/* Check IRQ bits */
14062306a36Sopenharmony_ci	for (i = 0; i < 8; i++) {
14162306a36Sopenharmony_ci		if (bits & (1 << i)) {
14262306a36Sopenharmony_ci			pmirq = block * 8 + i;
14362306a36Sopenharmony_ci			generic_handle_domain_irq(chip->irqdomain, pmirq);
14462306a36Sopenharmony_ci		}
14562306a36Sopenharmony_ci	}
14662306a36Sopenharmony_ci	return 0;
14762306a36Sopenharmony_ci}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_cistatic int pm8xxx_irq_master_handler(struct pm_irq_chip *chip, int master)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	unsigned int blockbits;
15262306a36Sopenharmony_ci	int block_number, i, ret = 0;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	ret = regmap_read(chip->regmap, SSBI_REG_ADDR_IRQ_M_STATUS1 + master,
15562306a36Sopenharmony_ci			  &blockbits);
15662306a36Sopenharmony_ci	if (ret) {
15762306a36Sopenharmony_ci		pr_err("Failed to read master %d ret=%d\n", master, ret);
15862306a36Sopenharmony_ci		return ret;
15962306a36Sopenharmony_ci	}
16062306a36Sopenharmony_ci	if (!blockbits) {
16162306a36Sopenharmony_ci		pr_err("master bit set in root but no blocks: %d", master);
16262306a36Sopenharmony_ci		return 0;
16362306a36Sopenharmony_ci	}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	for (i = 0; i < 8; i++)
16662306a36Sopenharmony_ci		if (blockbits & (1 << i)) {
16762306a36Sopenharmony_ci			block_number = master * 8 + i;	/* block # */
16862306a36Sopenharmony_ci			ret |= pm8xxx_irq_block_handler(chip, block_number);
16962306a36Sopenharmony_ci		}
17062306a36Sopenharmony_ci	return ret;
17162306a36Sopenharmony_ci}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_cistatic irqreturn_t pm8xxx_irq_handler(int irq, void *data)
17462306a36Sopenharmony_ci{
17562306a36Sopenharmony_ci	struct pm_irq_chip *chip = data;
17662306a36Sopenharmony_ci	unsigned int root;
17762306a36Sopenharmony_ci	int	i, ret, masters = 0;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	ret = regmap_read(chip->regmap, SSBI_REG_ADDR_IRQ_ROOT, &root);
18062306a36Sopenharmony_ci	if (ret) {
18162306a36Sopenharmony_ci		pr_err("Can't read root status ret=%d\n", ret);
18262306a36Sopenharmony_ci		return IRQ_NONE;
18362306a36Sopenharmony_ci	}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	/* on pm8xxx series masters start from bit 1 of the root */
18662306a36Sopenharmony_ci	masters = root >> 1;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	/* Read allowed masters for blocks. */
18962306a36Sopenharmony_ci	for (i = 0; i < chip->num_masters; i++)
19062306a36Sopenharmony_ci		if (masters & (1 << i))
19162306a36Sopenharmony_ci			pm8xxx_irq_master_handler(chip, i);
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	return IRQ_HANDLED;
19462306a36Sopenharmony_ci}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_cistatic void pm8821_irq_block_handler(struct pm_irq_chip *chip,
19762306a36Sopenharmony_ci				     int master, int block)
19862306a36Sopenharmony_ci{
19962306a36Sopenharmony_ci	int pmirq, i, ret;
20062306a36Sopenharmony_ci	unsigned int bits;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	ret = regmap_read(chip->regmap,
20362306a36Sopenharmony_ci			  PM8821_SSBI_ADDR_IRQ_ROOT(master, block), &bits);
20462306a36Sopenharmony_ci	if (ret) {
20562306a36Sopenharmony_ci		pr_err("Reading block %d failed ret=%d", block, ret);
20662306a36Sopenharmony_ci		return;
20762306a36Sopenharmony_ci	}
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	/* Convert block offset to global block number */
21062306a36Sopenharmony_ci	block += (master * PM8821_BLOCKS_PER_MASTER) - 1;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	/* Check IRQ bits */
21362306a36Sopenharmony_ci	for (i = 0; i < 8; i++) {
21462306a36Sopenharmony_ci		if (bits & BIT(i)) {
21562306a36Sopenharmony_ci			pmirq = block * 8 + i;
21662306a36Sopenharmony_ci			generic_handle_domain_irq(chip->irqdomain, pmirq);
21762306a36Sopenharmony_ci		}
21862306a36Sopenharmony_ci	}
21962306a36Sopenharmony_ci}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_cistatic inline void pm8821_irq_master_handler(struct pm_irq_chip *chip,
22262306a36Sopenharmony_ci					     int master, u8 master_val)
22362306a36Sopenharmony_ci{
22462306a36Sopenharmony_ci	int block;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	for (block = 1; block < 8; block++)
22762306a36Sopenharmony_ci		if (master_val & BIT(block))
22862306a36Sopenharmony_ci			pm8821_irq_block_handler(chip, master, block);
22962306a36Sopenharmony_ci}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_cistatic irqreturn_t pm8821_irq_handler(int irq, void *data)
23262306a36Sopenharmony_ci{
23362306a36Sopenharmony_ci	struct pm_irq_chip *chip = data;
23462306a36Sopenharmony_ci	unsigned int master;
23562306a36Sopenharmony_ci	int ret;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	ret = regmap_read(chip->regmap,
23862306a36Sopenharmony_ci			  PM8821_SSBI_REG_ADDR_IRQ_MASTER0, &master);
23962306a36Sopenharmony_ci	if (ret) {
24062306a36Sopenharmony_ci		pr_err("Failed to read master 0 ret=%d\n", ret);
24162306a36Sopenharmony_ci		return IRQ_NONE;
24262306a36Sopenharmony_ci	}
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	/* bits 1 through 7 marks the first 7 blocks in master 0 */
24562306a36Sopenharmony_ci	if (master & GENMASK(7, 1))
24662306a36Sopenharmony_ci		pm8821_irq_master_handler(chip, 0, master);
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	/* bit 0 marks if master 1 contains any bits */
24962306a36Sopenharmony_ci	if (!(master & BIT(0)))
25062306a36Sopenharmony_ci		return IRQ_NONE;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	ret = regmap_read(chip->regmap,
25362306a36Sopenharmony_ci			  PM8821_SSBI_REG_ADDR_IRQ_MASTER1, &master);
25462306a36Sopenharmony_ci	if (ret) {
25562306a36Sopenharmony_ci		pr_err("Failed to read master 1 ret=%d\n", ret);
25662306a36Sopenharmony_ci		return IRQ_NONE;
25762306a36Sopenharmony_ci	}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	pm8821_irq_master_handler(chip, 1, master);
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	return IRQ_HANDLED;
26262306a36Sopenharmony_ci}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_cistatic void pm8xxx_irq_mask_ack(struct irq_data *d)
26562306a36Sopenharmony_ci{
26662306a36Sopenharmony_ci	struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
26762306a36Sopenharmony_ci	unsigned int pmirq = irqd_to_hwirq(d);
26862306a36Sopenharmony_ci	u8	block, config;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	block = pmirq / 8;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	config = chip->config[pmirq] | PM_IRQF_MASK_ALL | PM_IRQF_CLR;
27362306a36Sopenharmony_ci	pm8xxx_config_irq(chip, block, config);
27462306a36Sopenharmony_ci}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_cistatic void pm8xxx_irq_unmask(struct irq_data *d)
27762306a36Sopenharmony_ci{
27862306a36Sopenharmony_ci	struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
27962306a36Sopenharmony_ci	unsigned int pmirq = irqd_to_hwirq(d);
28062306a36Sopenharmony_ci	u8	block, config;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	block = pmirq / 8;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	config = chip->config[pmirq];
28562306a36Sopenharmony_ci	pm8xxx_config_irq(chip, block, config);
28662306a36Sopenharmony_ci}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_cistatic int pm8xxx_irq_set_type(struct irq_data *d, unsigned int flow_type)
28962306a36Sopenharmony_ci{
29062306a36Sopenharmony_ci	struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
29162306a36Sopenharmony_ci	unsigned int pmirq = irqd_to_hwirq(d);
29262306a36Sopenharmony_ci	int irq_bit;
29362306a36Sopenharmony_ci	u8 block, config;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	block = pmirq / 8;
29662306a36Sopenharmony_ci	irq_bit  = pmirq % 8;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	chip->config[pmirq] = (irq_bit << PM_IRQF_BITS_SHIFT)
29962306a36Sopenharmony_ci							| PM_IRQF_MASK_ALL;
30062306a36Sopenharmony_ci	if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) {
30162306a36Sopenharmony_ci		if (flow_type & IRQF_TRIGGER_RISING)
30262306a36Sopenharmony_ci			chip->config[pmirq] &= ~PM_IRQF_MASK_RE;
30362306a36Sopenharmony_ci		if (flow_type & IRQF_TRIGGER_FALLING)
30462306a36Sopenharmony_ci			chip->config[pmirq] &= ~PM_IRQF_MASK_FE;
30562306a36Sopenharmony_ci	} else {
30662306a36Sopenharmony_ci		chip->config[pmirq] |= PM_IRQF_LVL_SEL;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci		if (flow_type & IRQF_TRIGGER_HIGH)
30962306a36Sopenharmony_ci			chip->config[pmirq] &= ~PM_IRQF_MASK_RE;
31062306a36Sopenharmony_ci		else
31162306a36Sopenharmony_ci			chip->config[pmirq] &= ~PM_IRQF_MASK_FE;
31262306a36Sopenharmony_ci	}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	config = chip->config[pmirq] | PM_IRQF_CLR;
31562306a36Sopenharmony_ci	return pm8xxx_config_irq(chip, block, config);
31662306a36Sopenharmony_ci}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_cistatic int pm8xxx_irq_get_irqchip_state(struct irq_data *d,
31962306a36Sopenharmony_ci					enum irqchip_irq_state which,
32062306a36Sopenharmony_ci					bool *state)
32162306a36Sopenharmony_ci{
32262306a36Sopenharmony_ci	struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
32362306a36Sopenharmony_ci	unsigned int pmirq = irqd_to_hwirq(d);
32462306a36Sopenharmony_ci	unsigned int bits;
32562306a36Sopenharmony_ci	unsigned long flags;
32662306a36Sopenharmony_ci	int irq_bit;
32762306a36Sopenharmony_ci	u8 block;
32862306a36Sopenharmony_ci	int rc;
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	if (which != IRQCHIP_STATE_LINE_LEVEL)
33162306a36Sopenharmony_ci		return -EINVAL;
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	block = pmirq / 8;
33462306a36Sopenharmony_ci	irq_bit = pmirq % 8;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	spin_lock_irqsave(&chip->pm_irq_lock, flags);
33762306a36Sopenharmony_ci	rc = regmap_write(chip->regmap, SSBI_REG_ADDR_IRQ_BLK_SEL, block);
33862306a36Sopenharmony_ci	if (rc) {
33962306a36Sopenharmony_ci		pr_err("Failed Selecting Block %d rc=%d\n", block, rc);
34062306a36Sopenharmony_ci		goto bail;
34162306a36Sopenharmony_ci	}
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	rc = regmap_read(chip->regmap, SSBI_REG_ADDR_IRQ_RT_STATUS, &bits);
34462306a36Sopenharmony_ci	if (rc) {
34562306a36Sopenharmony_ci		pr_err("Failed Reading Status rc=%d\n", rc);
34662306a36Sopenharmony_ci		goto bail;
34762306a36Sopenharmony_ci	}
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	*state = !!(bits & BIT(irq_bit));
35062306a36Sopenharmony_cibail:
35162306a36Sopenharmony_ci	spin_unlock_irqrestore(&chip->pm_irq_lock, flags);
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	return rc;
35462306a36Sopenharmony_ci}
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_cistatic struct irq_chip pm8xxx_irq_chip = {
35762306a36Sopenharmony_ci	.name		= "pm8xxx",
35862306a36Sopenharmony_ci	.irq_mask_ack	= pm8xxx_irq_mask_ack,
35962306a36Sopenharmony_ci	.irq_unmask	= pm8xxx_irq_unmask,
36062306a36Sopenharmony_ci	.irq_set_type	= pm8xxx_irq_set_type,
36162306a36Sopenharmony_ci	.irq_get_irqchip_state = pm8xxx_irq_get_irqchip_state,
36262306a36Sopenharmony_ci	.flags		= IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE,
36362306a36Sopenharmony_ci};
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_cistatic void pm8xxx_irq_domain_map(struct pm_irq_chip *chip,
36662306a36Sopenharmony_ci				  struct irq_domain *domain, unsigned int irq,
36762306a36Sopenharmony_ci				  irq_hw_number_t hwirq, unsigned int type)
36862306a36Sopenharmony_ci{
36962306a36Sopenharmony_ci	irq_domain_set_info(domain, irq, hwirq, chip->pm_irq_data->irq_chip,
37062306a36Sopenharmony_ci			    chip, handle_level_irq, NULL, NULL);
37162306a36Sopenharmony_ci	irq_set_noprobe(irq);
37262306a36Sopenharmony_ci}
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_cistatic int pm8xxx_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
37562306a36Sopenharmony_ci				   unsigned int nr_irqs, void *data)
37662306a36Sopenharmony_ci{
37762306a36Sopenharmony_ci	struct pm_irq_chip *chip = domain->host_data;
37862306a36Sopenharmony_ci	struct irq_fwspec *fwspec = data;
37962306a36Sopenharmony_ci	irq_hw_number_t hwirq;
38062306a36Sopenharmony_ci	unsigned int type;
38162306a36Sopenharmony_ci	int ret, i;
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	ret = irq_domain_translate_twocell(domain, fwspec, &hwirq, &type);
38462306a36Sopenharmony_ci	if (ret)
38562306a36Sopenharmony_ci		return ret;
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	for (i = 0; i < nr_irqs; i++)
38862306a36Sopenharmony_ci		pm8xxx_irq_domain_map(chip, domain, virq + i, hwirq + i, type);
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	return 0;
39162306a36Sopenharmony_ci}
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_cistatic const struct irq_domain_ops pm8xxx_irq_domain_ops = {
39462306a36Sopenharmony_ci	.alloc = pm8xxx_irq_domain_alloc,
39562306a36Sopenharmony_ci	.free = irq_domain_free_irqs_common,
39662306a36Sopenharmony_ci	.translate = irq_domain_translate_twocell,
39762306a36Sopenharmony_ci};
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_cistatic void pm8821_irq_mask_ack(struct irq_data *d)
40062306a36Sopenharmony_ci{
40162306a36Sopenharmony_ci	struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
40262306a36Sopenharmony_ci	unsigned int pmirq = irqd_to_hwirq(d);
40362306a36Sopenharmony_ci	u8 block, master;
40462306a36Sopenharmony_ci	int irq_bit, rc;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	block = pmirq / 8;
40762306a36Sopenharmony_ci	master = block / PM8821_BLOCKS_PER_MASTER;
40862306a36Sopenharmony_ci	irq_bit = pmirq % 8;
40962306a36Sopenharmony_ci	block %= PM8821_BLOCKS_PER_MASTER;
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	rc = regmap_update_bits(chip->regmap,
41262306a36Sopenharmony_ci				PM8821_SSBI_ADDR_IRQ_MASK(master, block),
41362306a36Sopenharmony_ci				BIT(irq_bit), BIT(irq_bit));
41462306a36Sopenharmony_ci	if (rc) {
41562306a36Sopenharmony_ci		pr_err("Failed to mask IRQ:%d rc=%d\n", pmirq, rc);
41662306a36Sopenharmony_ci		return;
41762306a36Sopenharmony_ci	}
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	rc = regmap_update_bits(chip->regmap,
42062306a36Sopenharmony_ci				PM8821_SSBI_ADDR_IRQ_CLEAR(master, block),
42162306a36Sopenharmony_ci				BIT(irq_bit), BIT(irq_bit));
42262306a36Sopenharmony_ci	if (rc)
42362306a36Sopenharmony_ci		pr_err("Failed to CLEAR IRQ:%d rc=%d\n", pmirq, rc);
42462306a36Sopenharmony_ci}
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_cistatic void pm8821_irq_unmask(struct irq_data *d)
42762306a36Sopenharmony_ci{
42862306a36Sopenharmony_ci	struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
42962306a36Sopenharmony_ci	unsigned int pmirq = irqd_to_hwirq(d);
43062306a36Sopenharmony_ci	int irq_bit, rc;
43162306a36Sopenharmony_ci	u8 block, master;
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	block = pmirq / 8;
43462306a36Sopenharmony_ci	master = block / PM8821_BLOCKS_PER_MASTER;
43562306a36Sopenharmony_ci	irq_bit = pmirq % 8;
43662306a36Sopenharmony_ci	block %= PM8821_BLOCKS_PER_MASTER;
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	rc = regmap_update_bits(chip->regmap,
43962306a36Sopenharmony_ci				PM8821_SSBI_ADDR_IRQ_MASK(master, block),
44062306a36Sopenharmony_ci				BIT(irq_bit), ~BIT(irq_bit));
44162306a36Sopenharmony_ci	if (rc)
44262306a36Sopenharmony_ci		pr_err("Failed to read/write unmask IRQ:%d rc=%d\n", pmirq, rc);
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci}
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_cistatic int pm8821_irq_get_irqchip_state(struct irq_data *d,
44762306a36Sopenharmony_ci					enum irqchip_irq_state which,
44862306a36Sopenharmony_ci					bool *state)
44962306a36Sopenharmony_ci{
45062306a36Sopenharmony_ci	struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
45162306a36Sopenharmony_ci	int rc, pmirq = irqd_to_hwirq(d);
45262306a36Sopenharmony_ci	u8 block, irq_bit, master;
45362306a36Sopenharmony_ci	unsigned int bits;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	block = pmirq / 8;
45662306a36Sopenharmony_ci	master = block / PM8821_BLOCKS_PER_MASTER;
45762306a36Sopenharmony_ci	irq_bit = pmirq % 8;
45862306a36Sopenharmony_ci	block %= PM8821_BLOCKS_PER_MASTER;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	rc = regmap_read(chip->regmap,
46162306a36Sopenharmony_ci		PM8821_SSBI_ADDR_IRQ_RT_STATUS(master, block), &bits);
46262306a36Sopenharmony_ci	if (rc) {
46362306a36Sopenharmony_ci		pr_err("Reading Status of IRQ %d failed rc=%d\n", pmirq, rc);
46462306a36Sopenharmony_ci		return rc;
46562306a36Sopenharmony_ci	}
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	*state = !!(bits & BIT(irq_bit));
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	return rc;
47062306a36Sopenharmony_ci}
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_cistatic struct irq_chip pm8821_irq_chip = {
47362306a36Sopenharmony_ci	.name		= "pm8821",
47462306a36Sopenharmony_ci	.irq_mask_ack	= pm8821_irq_mask_ack,
47562306a36Sopenharmony_ci	.irq_unmask	= pm8821_irq_unmask,
47662306a36Sopenharmony_ci	.irq_get_irqchip_state = pm8821_irq_get_irqchip_state,
47762306a36Sopenharmony_ci	.flags		= IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE,
47862306a36Sopenharmony_ci};
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_cistatic const struct regmap_config ssbi_regmap_config = {
48162306a36Sopenharmony_ci	.reg_bits = 16,
48262306a36Sopenharmony_ci	.val_bits = 8,
48362306a36Sopenharmony_ci	.max_register = 0x3ff,
48462306a36Sopenharmony_ci	.fast_io = true,
48562306a36Sopenharmony_ci	.reg_read = ssbi_reg_read,
48662306a36Sopenharmony_ci	.reg_write = ssbi_reg_write
48762306a36Sopenharmony_ci};
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_cistatic const struct pm_irq_data pm8xxx_data = {
49062306a36Sopenharmony_ci	.num_irqs = PM8XXX_NR_IRQS,
49162306a36Sopenharmony_ci	.irq_chip = &pm8xxx_irq_chip,
49262306a36Sopenharmony_ci	.irq_handler = pm8xxx_irq_handler,
49362306a36Sopenharmony_ci};
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_cistatic const struct pm_irq_data pm8821_data = {
49662306a36Sopenharmony_ci	.num_irqs = PM8821_NR_IRQS,
49762306a36Sopenharmony_ci	.irq_chip = &pm8821_irq_chip,
49862306a36Sopenharmony_ci	.irq_handler = pm8821_irq_handler,
49962306a36Sopenharmony_ci};
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_cistatic const struct of_device_id pm8xxx_id_table[] = {
50262306a36Sopenharmony_ci	{ .compatible = "qcom,pm8058", .data = &pm8xxx_data},
50362306a36Sopenharmony_ci	{ .compatible = "qcom,pm8821", .data = &pm8821_data},
50462306a36Sopenharmony_ci	{ .compatible = "qcom,pm8921", .data = &pm8xxx_data},
50562306a36Sopenharmony_ci	{ }
50662306a36Sopenharmony_ci};
50762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, pm8xxx_id_table);
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_cistatic int pm8xxx_probe(struct platform_device *pdev)
51062306a36Sopenharmony_ci{
51162306a36Sopenharmony_ci	const struct pm_irq_data *data;
51262306a36Sopenharmony_ci	struct regmap *regmap;
51362306a36Sopenharmony_ci	int irq, rc;
51462306a36Sopenharmony_ci	unsigned int val;
51562306a36Sopenharmony_ci	struct pm_irq_chip *chip;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	data = of_device_get_match_data(&pdev->dev);
51862306a36Sopenharmony_ci	if (!data) {
51962306a36Sopenharmony_ci		dev_err(&pdev->dev, "No matching driver data found\n");
52062306a36Sopenharmony_ci		return -EINVAL;
52162306a36Sopenharmony_ci	}
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
52462306a36Sopenharmony_ci	if (irq < 0)
52562306a36Sopenharmony_ci		return irq;
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	regmap = devm_regmap_init(&pdev->dev, NULL, pdev->dev.parent,
52862306a36Sopenharmony_ci				  &ssbi_regmap_config);
52962306a36Sopenharmony_ci	if (IS_ERR(regmap))
53062306a36Sopenharmony_ci		return PTR_ERR(regmap);
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	/* Read PMIC chip revision */
53362306a36Sopenharmony_ci	rc = regmap_read(regmap, REG_HWREV, &val);
53462306a36Sopenharmony_ci	if (rc) {
53562306a36Sopenharmony_ci		pr_err("Failed to read hw rev reg %d:rc=%d\n", REG_HWREV, rc);
53662306a36Sopenharmony_ci		return rc;
53762306a36Sopenharmony_ci	}
53862306a36Sopenharmony_ci	pr_info("PMIC revision 1: %02X\n", val);
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	/* Read PMIC chip revision 2 */
54162306a36Sopenharmony_ci	rc = regmap_read(regmap, REG_HWREV_2, &val);
54262306a36Sopenharmony_ci	if (rc) {
54362306a36Sopenharmony_ci		pr_err("Failed to read hw rev 2 reg %d:rc=%d\n",
54462306a36Sopenharmony_ci			REG_HWREV_2, rc);
54562306a36Sopenharmony_ci		return rc;
54662306a36Sopenharmony_ci	}
54762306a36Sopenharmony_ci	pr_info("PMIC revision 2: %02X\n", val);
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	chip = devm_kzalloc(&pdev->dev,
55062306a36Sopenharmony_ci			    struct_size(chip, config, data->num_irqs),
55162306a36Sopenharmony_ci			    GFP_KERNEL);
55262306a36Sopenharmony_ci	if (!chip)
55362306a36Sopenharmony_ci		return -ENOMEM;
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	platform_set_drvdata(pdev, chip);
55662306a36Sopenharmony_ci	chip->regmap = regmap;
55762306a36Sopenharmony_ci	chip->num_blocks = DIV_ROUND_UP(data->num_irqs, 8);
55862306a36Sopenharmony_ci	chip->num_masters = DIV_ROUND_UP(chip->num_blocks, 8);
55962306a36Sopenharmony_ci	chip->pm_irq_data = data;
56062306a36Sopenharmony_ci	spin_lock_init(&chip->pm_irq_lock);
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	chip->irqdomain = irq_domain_add_linear(pdev->dev.of_node,
56362306a36Sopenharmony_ci						data->num_irqs,
56462306a36Sopenharmony_ci						&pm8xxx_irq_domain_ops,
56562306a36Sopenharmony_ci						chip);
56662306a36Sopenharmony_ci	if (!chip->irqdomain)
56762306a36Sopenharmony_ci		return -ENODEV;
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	rc = devm_request_irq(&pdev->dev, irq, data->irq_handler, 0, dev_name(&pdev->dev), chip);
57062306a36Sopenharmony_ci	if (rc)
57162306a36Sopenharmony_ci		return rc;
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	irq_set_irq_wake(irq, 1);
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	rc = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
57662306a36Sopenharmony_ci	if (rc)
57762306a36Sopenharmony_ci		irq_domain_remove(chip->irqdomain);
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	return rc;
58062306a36Sopenharmony_ci}
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_cistatic int pm8xxx_remove_child(struct device *dev, void *unused)
58362306a36Sopenharmony_ci{
58462306a36Sopenharmony_ci	platform_device_unregister(to_platform_device(dev));
58562306a36Sopenharmony_ci	return 0;
58662306a36Sopenharmony_ci}
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_cistatic int pm8xxx_remove(struct platform_device *pdev)
58962306a36Sopenharmony_ci{
59062306a36Sopenharmony_ci	struct pm_irq_chip *chip = platform_get_drvdata(pdev);
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	device_for_each_child(&pdev->dev, NULL, pm8xxx_remove_child);
59362306a36Sopenharmony_ci	irq_domain_remove(chip->irqdomain);
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	return 0;
59662306a36Sopenharmony_ci}
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_cistatic struct platform_driver pm8xxx_driver = {
59962306a36Sopenharmony_ci	.probe		= pm8xxx_probe,
60062306a36Sopenharmony_ci	.remove		= pm8xxx_remove,
60162306a36Sopenharmony_ci	.driver		= {
60262306a36Sopenharmony_ci		.name	= "pm8xxx-core",
60362306a36Sopenharmony_ci		.of_match_table = pm8xxx_id_table,
60462306a36Sopenharmony_ci	},
60562306a36Sopenharmony_ci};
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_cistatic int __init pm8xxx_init(void)
60862306a36Sopenharmony_ci{
60962306a36Sopenharmony_ci	return platform_driver_register(&pm8xxx_driver);
61062306a36Sopenharmony_ci}
61162306a36Sopenharmony_cisubsys_initcall(pm8xxx_init);
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_cistatic void __exit pm8xxx_exit(void)
61462306a36Sopenharmony_ci{
61562306a36Sopenharmony_ci	platform_driver_unregister(&pm8xxx_driver);
61662306a36Sopenharmony_ci}
61762306a36Sopenharmony_cimodule_exit(pm8xxx_exit);
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
62062306a36Sopenharmony_ciMODULE_DESCRIPTION("PMIC 8xxx core driver");
62162306a36Sopenharmony_ciMODULE_VERSION("1.0");
62262306a36Sopenharmony_ciMODULE_ALIAS("platform:pm8xxx-core");
623