162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2012-2015, 2017, 2021, The Linux Foundation. All rights reserved.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci#include <linux/bitmap.h>
662306a36Sopenharmony_ci#include <linux/delay.h>
762306a36Sopenharmony_ci#include <linux/err.h>
862306a36Sopenharmony_ci#include <linux/interrupt.h>
962306a36Sopenharmony_ci#include <linux/io.h>
1062306a36Sopenharmony_ci#include <linux/irqchip/chained_irq.h>
1162306a36Sopenharmony_ci#include <linux/irqdomain.h>
1262306a36Sopenharmony_ci#include <linux/irq.h>
1362306a36Sopenharmony_ci#include <linux/kernel.h>
1462306a36Sopenharmony_ci#include <linux/module.h>
1562306a36Sopenharmony_ci#include <linux/of.h>
1662306a36Sopenharmony_ci#include <linux/platform_device.h>
1762306a36Sopenharmony_ci#include <linux/slab.h>
1862306a36Sopenharmony_ci#include <linux/spmi.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci/* PMIC Arbiter configuration registers */
2162306a36Sopenharmony_ci#define PMIC_ARB_VERSION		0x0000
2262306a36Sopenharmony_ci#define PMIC_ARB_VERSION_V2_MIN		0x20010000
2362306a36Sopenharmony_ci#define PMIC_ARB_VERSION_V3_MIN		0x30000000
2462306a36Sopenharmony_ci#define PMIC_ARB_VERSION_V5_MIN		0x50000000
2562306a36Sopenharmony_ci#define PMIC_ARB_VERSION_V7_MIN		0x70000000
2662306a36Sopenharmony_ci#define PMIC_ARB_INT_EN			0x0004
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#define PMIC_ARB_FEATURES		0x0004
2962306a36Sopenharmony_ci#define PMIC_ARB_FEATURES_PERIPH_MASK	GENMASK(10, 0)
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#define PMIC_ARB_FEATURES1		0x0008
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci/* PMIC Arbiter channel registers offsets */
3462306a36Sopenharmony_ci#define PMIC_ARB_CMD			0x00
3562306a36Sopenharmony_ci#define PMIC_ARB_CONFIG			0x04
3662306a36Sopenharmony_ci#define PMIC_ARB_STATUS			0x08
3762306a36Sopenharmony_ci#define PMIC_ARB_WDATA0			0x10
3862306a36Sopenharmony_ci#define PMIC_ARB_WDATA1			0x14
3962306a36Sopenharmony_ci#define PMIC_ARB_RDATA0			0x18
4062306a36Sopenharmony_ci#define PMIC_ARB_RDATA1			0x1C
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci/* Mapping Table */
4362306a36Sopenharmony_ci#define SPMI_MAPPING_TABLE_REG(N)	(0x0B00 + (4 * (N)))
4462306a36Sopenharmony_ci#define SPMI_MAPPING_BIT_INDEX(X)	(((X) >> 18) & 0xF)
4562306a36Sopenharmony_ci#define SPMI_MAPPING_BIT_IS_0_FLAG(X)	(((X) >> 17) & 0x1)
4662306a36Sopenharmony_ci#define SPMI_MAPPING_BIT_IS_0_RESULT(X)	(((X) >> 9) & 0xFF)
4762306a36Sopenharmony_ci#define SPMI_MAPPING_BIT_IS_1_FLAG(X)	(((X) >> 8) & 0x1)
4862306a36Sopenharmony_ci#define SPMI_MAPPING_BIT_IS_1_RESULT(X)	(((X) >> 0) & 0xFF)
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci#define SPMI_MAPPING_TABLE_TREE_DEPTH	16	/* Maximum of 16-bits */
5162306a36Sopenharmony_ci#define PMIC_ARB_MAX_PPID		BIT(12) /* PPID is 12bit */
5262306a36Sopenharmony_ci#define PMIC_ARB_APID_VALID		BIT(15)
5362306a36Sopenharmony_ci#define PMIC_ARB_CHAN_IS_IRQ_OWNER(reg)	((reg) & BIT(24))
5462306a36Sopenharmony_ci#define INVALID_EE				0xFF
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci/* Ownership Table */
5762306a36Sopenharmony_ci#define SPMI_OWNERSHIP_PERIPH2OWNER(X)	((X) & 0x7)
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci/* Channel Status fields */
6062306a36Sopenharmony_cienum pmic_arb_chnl_status {
6162306a36Sopenharmony_ci	PMIC_ARB_STATUS_DONE	= BIT(0),
6262306a36Sopenharmony_ci	PMIC_ARB_STATUS_FAILURE	= BIT(1),
6362306a36Sopenharmony_ci	PMIC_ARB_STATUS_DENIED	= BIT(2),
6462306a36Sopenharmony_ci	PMIC_ARB_STATUS_DROPPED	= BIT(3),
6562306a36Sopenharmony_ci};
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci/* Command register fields */
6862306a36Sopenharmony_ci#define PMIC_ARB_CMD_MAX_BYTE_COUNT	8
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci/* Command Opcodes */
7162306a36Sopenharmony_cienum pmic_arb_cmd_op_code {
7262306a36Sopenharmony_ci	PMIC_ARB_OP_EXT_WRITEL = 0,
7362306a36Sopenharmony_ci	PMIC_ARB_OP_EXT_READL = 1,
7462306a36Sopenharmony_ci	PMIC_ARB_OP_EXT_WRITE = 2,
7562306a36Sopenharmony_ci	PMIC_ARB_OP_RESET = 3,
7662306a36Sopenharmony_ci	PMIC_ARB_OP_SLEEP = 4,
7762306a36Sopenharmony_ci	PMIC_ARB_OP_SHUTDOWN = 5,
7862306a36Sopenharmony_ci	PMIC_ARB_OP_WAKEUP = 6,
7962306a36Sopenharmony_ci	PMIC_ARB_OP_AUTHENTICATE = 7,
8062306a36Sopenharmony_ci	PMIC_ARB_OP_MSTR_READ = 8,
8162306a36Sopenharmony_ci	PMIC_ARB_OP_MSTR_WRITE = 9,
8262306a36Sopenharmony_ci	PMIC_ARB_OP_EXT_READ = 13,
8362306a36Sopenharmony_ci	PMIC_ARB_OP_WRITE = 14,
8462306a36Sopenharmony_ci	PMIC_ARB_OP_READ = 15,
8562306a36Sopenharmony_ci	PMIC_ARB_OP_ZERO_WRITE = 16,
8662306a36Sopenharmony_ci};
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci/*
8962306a36Sopenharmony_ci * PMIC arbiter version 5 uses different register offsets for read/write vs
9062306a36Sopenharmony_ci * observer channels.
9162306a36Sopenharmony_ci */
9262306a36Sopenharmony_cienum pmic_arb_channel {
9362306a36Sopenharmony_ci	PMIC_ARB_CHANNEL_RW,
9462306a36Sopenharmony_ci	PMIC_ARB_CHANNEL_OBS,
9562306a36Sopenharmony_ci};
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci/* Maximum number of support PMIC peripherals */
9862306a36Sopenharmony_ci#define PMIC_ARB_MAX_PERIPHS		512
9962306a36Sopenharmony_ci#define PMIC_ARB_MAX_PERIPHS_V7		1024
10062306a36Sopenharmony_ci#define PMIC_ARB_TIMEOUT_US		1000
10162306a36Sopenharmony_ci#define PMIC_ARB_MAX_TRANS_BYTES	(8)
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci#define PMIC_ARB_APID_MASK		0xFF
10462306a36Sopenharmony_ci#define PMIC_ARB_PPID_MASK		0xFFF
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci/* interrupt enable bit */
10762306a36Sopenharmony_ci#define SPMI_PIC_ACC_ENABLE_BIT		BIT(0)
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci#define spec_to_hwirq(slave_id, periph_id, irq_id, apid) \
11062306a36Sopenharmony_ci	((((slave_id) & 0xF)   << 28) | \
11162306a36Sopenharmony_ci	(((periph_id) & 0xFF)  << 20) | \
11262306a36Sopenharmony_ci	(((irq_id)    & 0x7)   << 16) | \
11362306a36Sopenharmony_ci	(((apid)      & 0x3FF) << 0))
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci#define hwirq_to_sid(hwirq)  (((hwirq) >> 28) & 0xF)
11662306a36Sopenharmony_ci#define hwirq_to_per(hwirq)  (((hwirq) >> 20) & 0xFF)
11762306a36Sopenharmony_ci#define hwirq_to_irq(hwirq)  (((hwirq) >> 16) & 0x7)
11862306a36Sopenharmony_ci#define hwirq_to_apid(hwirq) (((hwirq) >> 0)  & 0x3FF)
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistruct pmic_arb_ver_ops;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_cistruct apid_data {
12362306a36Sopenharmony_ci	u16		ppid;
12462306a36Sopenharmony_ci	u8		write_ee;
12562306a36Sopenharmony_ci	u8		irq_ee;
12662306a36Sopenharmony_ci};
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci/**
12962306a36Sopenharmony_ci * struct spmi_pmic_arb - SPMI PMIC Arbiter object
13062306a36Sopenharmony_ci *
13162306a36Sopenharmony_ci * @rd_base:		on v1 "core", on v2 "observer" register base off DT.
13262306a36Sopenharmony_ci * @wr_base:		on v1 "core", on v2 "chnls"    register base off DT.
13362306a36Sopenharmony_ci * @intr:		address of the SPMI interrupt control registers.
13462306a36Sopenharmony_ci * @cnfg:		address of the PMIC Arbiter configuration registers.
13562306a36Sopenharmony_ci * @lock:		lock to synchronize accesses.
13662306a36Sopenharmony_ci * @channel:		execution environment channel to use for accesses.
13762306a36Sopenharmony_ci * @irq:		PMIC ARB interrupt.
13862306a36Sopenharmony_ci * @ee:			the current Execution Environment
13962306a36Sopenharmony_ci * @bus_instance:	on v7: 0 = primary SPMI bus, 1 = secondary SPMI bus
14062306a36Sopenharmony_ci * @min_apid:		minimum APID (used for bounding IRQ search)
14162306a36Sopenharmony_ci * @max_apid:		maximum APID
14262306a36Sopenharmony_ci * @base_apid:		on v7: minimum APID associated with the particular SPMI
14362306a36Sopenharmony_ci *			bus instance
14462306a36Sopenharmony_ci * @apid_count:		on v5 and v7: number of APIDs associated with the
14562306a36Sopenharmony_ci *			particular SPMI bus instance
14662306a36Sopenharmony_ci * @mapping_table:	in-memory copy of PPID -> APID mapping table.
14762306a36Sopenharmony_ci * @domain:		irq domain object for PMIC IRQ domain
14862306a36Sopenharmony_ci * @spmic:		SPMI controller object
14962306a36Sopenharmony_ci * @ver_ops:		version dependent operations.
15062306a36Sopenharmony_ci * @ppid_to_apid:	in-memory copy of PPID -> APID mapping table.
15162306a36Sopenharmony_ci * @last_apid:		Highest value APID in use
15262306a36Sopenharmony_ci * @apid_data:		Table of data for all APIDs
15362306a36Sopenharmony_ci * @max_periphs:	Number of elements in apid_data[]
15462306a36Sopenharmony_ci */
15562306a36Sopenharmony_cistruct spmi_pmic_arb {
15662306a36Sopenharmony_ci	void __iomem		*rd_base;
15762306a36Sopenharmony_ci	void __iomem		*wr_base;
15862306a36Sopenharmony_ci	void __iomem		*intr;
15962306a36Sopenharmony_ci	void __iomem		*cnfg;
16062306a36Sopenharmony_ci	void __iomem		*core;
16162306a36Sopenharmony_ci	resource_size_t		core_size;
16262306a36Sopenharmony_ci	raw_spinlock_t		lock;
16362306a36Sopenharmony_ci	u8			channel;
16462306a36Sopenharmony_ci	int			irq;
16562306a36Sopenharmony_ci	u8			ee;
16662306a36Sopenharmony_ci	u32			bus_instance;
16762306a36Sopenharmony_ci	u16			min_apid;
16862306a36Sopenharmony_ci	u16			max_apid;
16962306a36Sopenharmony_ci	u16			base_apid;
17062306a36Sopenharmony_ci	int			apid_count;
17162306a36Sopenharmony_ci	u32			*mapping_table;
17262306a36Sopenharmony_ci	DECLARE_BITMAP(mapping_table_valid, PMIC_ARB_MAX_PERIPHS);
17362306a36Sopenharmony_ci	struct irq_domain	*domain;
17462306a36Sopenharmony_ci	struct spmi_controller	*spmic;
17562306a36Sopenharmony_ci	const struct pmic_arb_ver_ops *ver_ops;
17662306a36Sopenharmony_ci	u16			*ppid_to_apid;
17762306a36Sopenharmony_ci	u16			last_apid;
17862306a36Sopenharmony_ci	struct apid_data	*apid_data;
17962306a36Sopenharmony_ci	int			max_periphs;
18062306a36Sopenharmony_ci};
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci/**
18362306a36Sopenharmony_ci * struct pmic_arb_ver_ops - version dependent functionality.
18462306a36Sopenharmony_ci *
18562306a36Sopenharmony_ci * @ver_str:		version string.
18662306a36Sopenharmony_ci * @ppid_to_apid:	finds the apid for a given ppid.
18762306a36Sopenharmony_ci * @non_data_cmd:	on v1 issues an spmi non-data command.
18862306a36Sopenharmony_ci *			on v2 no HW support, returns -EOPNOTSUPP.
18962306a36Sopenharmony_ci * @offset:		on v1 offset of per-ee channel.
19062306a36Sopenharmony_ci *			on v2 offset of per-ee and per-ppid channel.
19162306a36Sopenharmony_ci * @fmt_cmd:		formats a GENI/SPMI command.
19262306a36Sopenharmony_ci * @owner_acc_status:	on v1 address of PMIC_ARB_SPMI_PIC_OWNERm_ACC_STATUSn
19362306a36Sopenharmony_ci *			on v2 address of SPMI_PIC_OWNERm_ACC_STATUSn.
19462306a36Sopenharmony_ci * @acc_enable:		on v1 address of PMIC_ARB_SPMI_PIC_ACC_ENABLEn
19562306a36Sopenharmony_ci *			on v2 address of SPMI_PIC_ACC_ENABLEn.
19662306a36Sopenharmony_ci * @irq_status:		on v1 address of PMIC_ARB_SPMI_PIC_IRQ_STATUSn
19762306a36Sopenharmony_ci *			on v2 address of SPMI_PIC_IRQ_STATUSn.
19862306a36Sopenharmony_ci * @irq_clear:		on v1 address of PMIC_ARB_SPMI_PIC_IRQ_CLEARn
19962306a36Sopenharmony_ci *			on v2 address of SPMI_PIC_IRQ_CLEARn.
20062306a36Sopenharmony_ci * @apid_map_offset:	offset of PMIC_ARB_REG_CHNLn
20162306a36Sopenharmony_ci * @apid_owner:		on v2 and later address of SPMI_PERIPHn_2OWNER_TABLE_REG
20262306a36Sopenharmony_ci */
20362306a36Sopenharmony_cistruct pmic_arb_ver_ops {
20462306a36Sopenharmony_ci	const char *ver_str;
20562306a36Sopenharmony_ci	int (*ppid_to_apid)(struct spmi_pmic_arb *pmic_arb, u16 ppid);
20662306a36Sopenharmony_ci	/* spmi commands (read_cmd, write_cmd, cmd) functionality */
20762306a36Sopenharmony_ci	int (*offset)(struct spmi_pmic_arb *pmic_arb, u8 sid, u16 addr,
20862306a36Sopenharmony_ci			enum pmic_arb_channel ch_type);
20962306a36Sopenharmony_ci	u32 (*fmt_cmd)(u8 opc, u8 sid, u16 addr, u8 bc);
21062306a36Sopenharmony_ci	int (*non_data_cmd)(struct spmi_controller *ctrl, u8 opc, u8 sid);
21162306a36Sopenharmony_ci	/* Interrupts controller functionality (offset of PIC registers) */
21262306a36Sopenharmony_ci	void __iomem *(*owner_acc_status)(struct spmi_pmic_arb *pmic_arb, u8 m,
21362306a36Sopenharmony_ci					  u16 n);
21462306a36Sopenharmony_ci	void __iomem *(*acc_enable)(struct spmi_pmic_arb *pmic_arb, u16 n);
21562306a36Sopenharmony_ci	void __iomem *(*irq_status)(struct spmi_pmic_arb *pmic_arb, u16 n);
21662306a36Sopenharmony_ci	void __iomem *(*irq_clear)(struct spmi_pmic_arb *pmic_arb, u16 n);
21762306a36Sopenharmony_ci	u32 (*apid_map_offset)(u16 n);
21862306a36Sopenharmony_ci	void __iomem *(*apid_owner)(struct spmi_pmic_arb *pmic_arb, u16 n);
21962306a36Sopenharmony_ci};
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_cistatic inline void pmic_arb_base_write(struct spmi_pmic_arb *pmic_arb,
22262306a36Sopenharmony_ci				       u32 offset, u32 val)
22362306a36Sopenharmony_ci{
22462306a36Sopenharmony_ci	writel_relaxed(val, pmic_arb->wr_base + offset);
22562306a36Sopenharmony_ci}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_cistatic inline void pmic_arb_set_rd_cmd(struct spmi_pmic_arb *pmic_arb,
22862306a36Sopenharmony_ci				       u32 offset, u32 val)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci	writel_relaxed(val, pmic_arb->rd_base + offset);
23162306a36Sopenharmony_ci}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci/**
23462306a36Sopenharmony_ci * pmic_arb_read_data: reads pmic-arb's register and copy 1..4 bytes to buf
23562306a36Sopenharmony_ci * @bc:		byte count -1. range: 0..3
23662306a36Sopenharmony_ci * @reg:	register's address
23762306a36Sopenharmony_ci * @buf:	output parameter, length must be bc + 1
23862306a36Sopenharmony_ci */
23962306a36Sopenharmony_cistatic void
24062306a36Sopenharmony_cipmic_arb_read_data(struct spmi_pmic_arb *pmic_arb, u8 *buf, u32 reg, u8 bc)
24162306a36Sopenharmony_ci{
24262306a36Sopenharmony_ci	u32 data = __raw_readl(pmic_arb->rd_base + reg);
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	memcpy(buf, &data, (bc & 3) + 1);
24562306a36Sopenharmony_ci}
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci/**
24862306a36Sopenharmony_ci * pmic_arb_write_data: write 1..4 bytes from buf to pmic-arb's register
24962306a36Sopenharmony_ci * @bc:		byte-count -1. range: 0..3.
25062306a36Sopenharmony_ci * @reg:	register's address.
25162306a36Sopenharmony_ci * @buf:	buffer to write. length must be bc + 1.
25262306a36Sopenharmony_ci */
25362306a36Sopenharmony_cistatic void pmic_arb_write_data(struct spmi_pmic_arb *pmic_arb, const u8 *buf,
25462306a36Sopenharmony_ci				u32 reg, u8 bc)
25562306a36Sopenharmony_ci{
25662306a36Sopenharmony_ci	u32 data = 0;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	memcpy(&data, buf, (bc & 3) + 1);
25962306a36Sopenharmony_ci	__raw_writel(data, pmic_arb->wr_base + reg);
26062306a36Sopenharmony_ci}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_cistatic int pmic_arb_wait_for_done(struct spmi_controller *ctrl,
26362306a36Sopenharmony_ci				  void __iomem *base, u8 sid, u16 addr,
26462306a36Sopenharmony_ci				  enum pmic_arb_channel ch_type)
26562306a36Sopenharmony_ci{
26662306a36Sopenharmony_ci	struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl);
26762306a36Sopenharmony_ci	u32 status = 0;
26862306a36Sopenharmony_ci	u32 timeout = PMIC_ARB_TIMEOUT_US;
26962306a36Sopenharmony_ci	u32 offset;
27062306a36Sopenharmony_ci	int rc;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	rc = pmic_arb->ver_ops->offset(pmic_arb, sid, addr, ch_type);
27362306a36Sopenharmony_ci	if (rc < 0)
27462306a36Sopenharmony_ci		return rc;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	offset = rc;
27762306a36Sopenharmony_ci	offset += PMIC_ARB_STATUS;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	while (timeout--) {
28062306a36Sopenharmony_ci		status = readl_relaxed(base + offset);
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci		if (status & PMIC_ARB_STATUS_DONE) {
28362306a36Sopenharmony_ci			if (status & PMIC_ARB_STATUS_DENIED) {
28462306a36Sopenharmony_ci				dev_err(&ctrl->dev, "%s: %#x %#x: transaction denied (%#x)\n",
28562306a36Sopenharmony_ci					__func__, sid, addr, status);
28662306a36Sopenharmony_ci				return -EPERM;
28762306a36Sopenharmony_ci			}
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci			if (status & PMIC_ARB_STATUS_FAILURE) {
29062306a36Sopenharmony_ci				dev_err(&ctrl->dev, "%s: %#x %#x: transaction failed (%#x)\n",
29162306a36Sopenharmony_ci					__func__, sid, addr, status);
29262306a36Sopenharmony_ci				WARN_ON(1);
29362306a36Sopenharmony_ci				return -EIO;
29462306a36Sopenharmony_ci			}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci			if (status & PMIC_ARB_STATUS_DROPPED) {
29762306a36Sopenharmony_ci				dev_err(&ctrl->dev, "%s: %#x %#x: transaction dropped (%#x)\n",
29862306a36Sopenharmony_ci					__func__, sid, addr, status);
29962306a36Sopenharmony_ci				return -EIO;
30062306a36Sopenharmony_ci			}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci			return 0;
30362306a36Sopenharmony_ci		}
30462306a36Sopenharmony_ci		udelay(1);
30562306a36Sopenharmony_ci	}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	dev_err(&ctrl->dev, "%s: %#x %#x: timeout, status %#x\n",
30862306a36Sopenharmony_ci		__func__, sid, addr, status);
30962306a36Sopenharmony_ci	return -ETIMEDOUT;
31062306a36Sopenharmony_ci}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_cistatic int
31362306a36Sopenharmony_cipmic_arb_non_data_cmd_v1(struct spmi_controller *ctrl, u8 opc, u8 sid)
31462306a36Sopenharmony_ci{
31562306a36Sopenharmony_ci	struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl);
31662306a36Sopenharmony_ci	unsigned long flags;
31762306a36Sopenharmony_ci	u32 cmd;
31862306a36Sopenharmony_ci	int rc;
31962306a36Sopenharmony_ci	u32 offset;
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	rc = pmic_arb->ver_ops->offset(pmic_arb, sid, 0, PMIC_ARB_CHANNEL_RW);
32262306a36Sopenharmony_ci	if (rc < 0)
32362306a36Sopenharmony_ci		return rc;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	offset = rc;
32662306a36Sopenharmony_ci	cmd = ((opc | 0x40) << 27) | ((sid & 0xf) << 20);
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	raw_spin_lock_irqsave(&pmic_arb->lock, flags);
32962306a36Sopenharmony_ci	pmic_arb_base_write(pmic_arb, offset + PMIC_ARB_CMD, cmd);
33062306a36Sopenharmony_ci	rc = pmic_arb_wait_for_done(ctrl, pmic_arb->wr_base, sid, 0,
33162306a36Sopenharmony_ci				    PMIC_ARB_CHANNEL_RW);
33262306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&pmic_arb->lock, flags);
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	return rc;
33562306a36Sopenharmony_ci}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_cistatic int
33862306a36Sopenharmony_cipmic_arb_non_data_cmd_v2(struct spmi_controller *ctrl, u8 opc, u8 sid)
33962306a36Sopenharmony_ci{
34062306a36Sopenharmony_ci	return -EOPNOTSUPP;
34162306a36Sopenharmony_ci}
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci/* Non-data command */
34462306a36Sopenharmony_cistatic int pmic_arb_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid)
34562306a36Sopenharmony_ci{
34662306a36Sopenharmony_ci	struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl);
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	dev_dbg(&ctrl->dev, "cmd op:0x%x sid:%d\n", opc, sid);
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	/* Check for valid non-data command */
35162306a36Sopenharmony_ci	if (opc < SPMI_CMD_RESET || opc > SPMI_CMD_WAKEUP)
35262306a36Sopenharmony_ci		return -EINVAL;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	return pmic_arb->ver_ops->non_data_cmd(ctrl, opc, sid);
35562306a36Sopenharmony_ci}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_cistatic int pmic_arb_fmt_read_cmd(struct spmi_pmic_arb *pmic_arb, u8 opc, u8 sid,
35862306a36Sopenharmony_ci				 u16 addr, size_t len, u32 *cmd, u32 *offset)
35962306a36Sopenharmony_ci{
36062306a36Sopenharmony_ci	u8 bc = len - 1;
36162306a36Sopenharmony_ci	int rc;
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	rc = pmic_arb->ver_ops->offset(pmic_arb, sid, addr,
36462306a36Sopenharmony_ci				       PMIC_ARB_CHANNEL_OBS);
36562306a36Sopenharmony_ci	if (rc < 0)
36662306a36Sopenharmony_ci		return rc;
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	*offset = rc;
36962306a36Sopenharmony_ci	if (bc >= PMIC_ARB_MAX_TRANS_BYTES) {
37062306a36Sopenharmony_ci		dev_err(&pmic_arb->spmic->dev, "pmic-arb supports 1..%d bytes per trans, but:%zu requested",
37162306a36Sopenharmony_ci			PMIC_ARB_MAX_TRANS_BYTES, len);
37262306a36Sopenharmony_ci		return  -EINVAL;
37362306a36Sopenharmony_ci	}
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	/* Check the opcode */
37662306a36Sopenharmony_ci	if (opc >= 0x60 && opc <= 0x7F)
37762306a36Sopenharmony_ci		opc = PMIC_ARB_OP_READ;
37862306a36Sopenharmony_ci	else if (opc >= 0x20 && opc <= 0x2F)
37962306a36Sopenharmony_ci		opc = PMIC_ARB_OP_EXT_READ;
38062306a36Sopenharmony_ci	else if (opc >= 0x38 && opc <= 0x3F)
38162306a36Sopenharmony_ci		opc = PMIC_ARB_OP_EXT_READL;
38262306a36Sopenharmony_ci	else
38362306a36Sopenharmony_ci		return -EINVAL;
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	*cmd = pmic_arb->ver_ops->fmt_cmd(opc, sid, addr, bc);
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	return 0;
38862306a36Sopenharmony_ci}
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_cistatic int pmic_arb_read_cmd_unlocked(struct spmi_controller *ctrl, u32 cmd,
39162306a36Sopenharmony_ci				      u32 offset, u8 sid, u16 addr, u8 *buf,
39262306a36Sopenharmony_ci				      size_t len)
39362306a36Sopenharmony_ci{
39462306a36Sopenharmony_ci	struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl);
39562306a36Sopenharmony_ci	u8 bc = len - 1;
39662306a36Sopenharmony_ci	int rc;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	pmic_arb_set_rd_cmd(pmic_arb, offset + PMIC_ARB_CMD, cmd);
39962306a36Sopenharmony_ci	rc = pmic_arb_wait_for_done(ctrl, pmic_arb->rd_base, sid, addr,
40062306a36Sopenharmony_ci				    PMIC_ARB_CHANNEL_OBS);
40162306a36Sopenharmony_ci	if (rc)
40262306a36Sopenharmony_ci		return rc;
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	pmic_arb_read_data(pmic_arb, buf, offset + PMIC_ARB_RDATA0,
40562306a36Sopenharmony_ci		     min_t(u8, bc, 3));
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	if (bc > 3)
40862306a36Sopenharmony_ci		pmic_arb_read_data(pmic_arb, buf + 4, offset + PMIC_ARB_RDATA1,
40962306a36Sopenharmony_ci					bc - 4);
41062306a36Sopenharmony_ci	return 0;
41162306a36Sopenharmony_ci}
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_cistatic int pmic_arb_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
41462306a36Sopenharmony_ci			     u16 addr, u8 *buf, size_t len)
41562306a36Sopenharmony_ci{
41662306a36Sopenharmony_ci	struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl);
41762306a36Sopenharmony_ci	unsigned long flags;
41862306a36Sopenharmony_ci	u32 cmd, offset;
41962306a36Sopenharmony_ci	int rc;
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	rc = pmic_arb_fmt_read_cmd(pmic_arb, opc, sid, addr, len, &cmd,
42262306a36Sopenharmony_ci				   &offset);
42362306a36Sopenharmony_ci	if (rc)
42462306a36Sopenharmony_ci		return rc;
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	raw_spin_lock_irqsave(&pmic_arb->lock, flags);
42762306a36Sopenharmony_ci	rc = pmic_arb_read_cmd_unlocked(ctrl, cmd, offset, sid, addr, buf, len);
42862306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&pmic_arb->lock, flags);
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	return rc;
43162306a36Sopenharmony_ci}
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_cistatic int pmic_arb_fmt_write_cmd(struct spmi_pmic_arb *pmic_arb, u8 opc,
43462306a36Sopenharmony_ci				  u8 sid, u16 addr, size_t len, u32 *cmd,
43562306a36Sopenharmony_ci				  u32 *offset)
43662306a36Sopenharmony_ci{
43762306a36Sopenharmony_ci	u8 bc = len - 1;
43862306a36Sopenharmony_ci	int rc;
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	rc = pmic_arb->ver_ops->offset(pmic_arb, sid, addr,
44162306a36Sopenharmony_ci					PMIC_ARB_CHANNEL_RW);
44262306a36Sopenharmony_ci	if (rc < 0)
44362306a36Sopenharmony_ci		return rc;
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	*offset = rc;
44662306a36Sopenharmony_ci	if (bc >= PMIC_ARB_MAX_TRANS_BYTES) {
44762306a36Sopenharmony_ci		dev_err(&pmic_arb->spmic->dev, "pmic-arb supports 1..%d bytes per trans, but:%zu requested",
44862306a36Sopenharmony_ci			PMIC_ARB_MAX_TRANS_BYTES, len);
44962306a36Sopenharmony_ci		return  -EINVAL;
45062306a36Sopenharmony_ci	}
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	/* Check the opcode */
45362306a36Sopenharmony_ci	if (opc >= 0x40 && opc <= 0x5F)
45462306a36Sopenharmony_ci		opc = PMIC_ARB_OP_WRITE;
45562306a36Sopenharmony_ci	else if (opc <= 0x0F)
45662306a36Sopenharmony_ci		opc = PMIC_ARB_OP_EXT_WRITE;
45762306a36Sopenharmony_ci	else if (opc >= 0x30 && opc <= 0x37)
45862306a36Sopenharmony_ci		opc = PMIC_ARB_OP_EXT_WRITEL;
45962306a36Sopenharmony_ci	else if (opc >= 0x80)
46062306a36Sopenharmony_ci		opc = PMIC_ARB_OP_ZERO_WRITE;
46162306a36Sopenharmony_ci	else
46262306a36Sopenharmony_ci		return -EINVAL;
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	*cmd = pmic_arb->ver_ops->fmt_cmd(opc, sid, addr, bc);
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	return 0;
46762306a36Sopenharmony_ci}
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_cistatic int pmic_arb_write_cmd_unlocked(struct spmi_controller *ctrl, u32 cmd,
47062306a36Sopenharmony_ci				      u32 offset, u8 sid, u16 addr,
47162306a36Sopenharmony_ci				      const u8 *buf, size_t len)
47262306a36Sopenharmony_ci{
47362306a36Sopenharmony_ci	struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl);
47462306a36Sopenharmony_ci	u8 bc = len - 1;
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	/* Write data to FIFOs */
47762306a36Sopenharmony_ci	pmic_arb_write_data(pmic_arb, buf, offset + PMIC_ARB_WDATA0,
47862306a36Sopenharmony_ci				min_t(u8, bc, 3));
47962306a36Sopenharmony_ci	if (bc > 3)
48062306a36Sopenharmony_ci		pmic_arb_write_data(pmic_arb, buf + 4, offset + PMIC_ARB_WDATA1,
48162306a36Sopenharmony_ci					bc - 4);
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	/* Start the transaction */
48462306a36Sopenharmony_ci	pmic_arb_base_write(pmic_arb, offset + PMIC_ARB_CMD, cmd);
48562306a36Sopenharmony_ci	return pmic_arb_wait_for_done(ctrl, pmic_arb->wr_base, sid, addr,
48662306a36Sopenharmony_ci				      PMIC_ARB_CHANNEL_RW);
48762306a36Sopenharmony_ci}
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_cistatic int pmic_arb_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
49062306a36Sopenharmony_ci			      u16 addr, const u8 *buf, size_t len)
49162306a36Sopenharmony_ci{
49262306a36Sopenharmony_ci	struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl);
49362306a36Sopenharmony_ci	unsigned long flags;
49462306a36Sopenharmony_ci	u32 cmd, offset;
49562306a36Sopenharmony_ci	int rc;
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	rc = pmic_arb_fmt_write_cmd(pmic_arb, opc, sid, addr, len, &cmd,
49862306a36Sopenharmony_ci				    &offset);
49962306a36Sopenharmony_ci	if (rc)
50062306a36Sopenharmony_ci		return rc;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	raw_spin_lock_irqsave(&pmic_arb->lock, flags);
50362306a36Sopenharmony_ci	rc = pmic_arb_write_cmd_unlocked(ctrl, cmd, offset, sid, addr, buf,
50462306a36Sopenharmony_ci					 len);
50562306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&pmic_arb->lock, flags);
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	return rc;
50862306a36Sopenharmony_ci}
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_cistatic int pmic_arb_masked_write(struct spmi_controller *ctrl, u8 sid, u16 addr,
51162306a36Sopenharmony_ci				 const u8 *buf, const u8 *mask, size_t len)
51262306a36Sopenharmony_ci{
51362306a36Sopenharmony_ci	struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl);
51462306a36Sopenharmony_ci	u32 read_cmd, read_offset, write_cmd, write_offset;
51562306a36Sopenharmony_ci	u8 temp[PMIC_ARB_MAX_TRANS_BYTES];
51662306a36Sopenharmony_ci	unsigned long flags;
51762306a36Sopenharmony_ci	int rc, i;
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	rc = pmic_arb_fmt_read_cmd(pmic_arb, SPMI_CMD_EXT_READL, sid, addr, len,
52062306a36Sopenharmony_ci				   &read_cmd, &read_offset);
52162306a36Sopenharmony_ci	if (rc)
52262306a36Sopenharmony_ci		return rc;
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	rc = pmic_arb_fmt_write_cmd(pmic_arb, SPMI_CMD_EXT_WRITEL, sid, addr,
52562306a36Sopenharmony_ci				    len, &write_cmd, &write_offset);
52662306a36Sopenharmony_ci	if (rc)
52762306a36Sopenharmony_ci		return rc;
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	raw_spin_lock_irqsave(&pmic_arb->lock, flags);
53062306a36Sopenharmony_ci	rc = pmic_arb_read_cmd_unlocked(ctrl, read_cmd, read_offset, sid, addr,
53162306a36Sopenharmony_ci					temp, len);
53262306a36Sopenharmony_ci	if (rc)
53362306a36Sopenharmony_ci		goto done;
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	for (i = 0; i < len; i++)
53662306a36Sopenharmony_ci		temp[i] = (temp[i] & ~mask[i]) | (buf[i] & mask[i]);
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	rc = pmic_arb_write_cmd_unlocked(ctrl, write_cmd, write_offset, sid,
53962306a36Sopenharmony_ci					 addr, temp, len);
54062306a36Sopenharmony_cidone:
54162306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&pmic_arb->lock, flags);
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	return rc;
54462306a36Sopenharmony_ci}
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_cienum qpnpint_regs {
54762306a36Sopenharmony_ci	QPNPINT_REG_RT_STS		= 0x10,
54862306a36Sopenharmony_ci	QPNPINT_REG_SET_TYPE		= 0x11,
54962306a36Sopenharmony_ci	QPNPINT_REG_POLARITY_HIGH	= 0x12,
55062306a36Sopenharmony_ci	QPNPINT_REG_POLARITY_LOW	= 0x13,
55162306a36Sopenharmony_ci	QPNPINT_REG_LATCHED_CLR		= 0x14,
55262306a36Sopenharmony_ci	QPNPINT_REG_EN_SET		= 0x15,
55362306a36Sopenharmony_ci	QPNPINT_REG_EN_CLR		= 0x16,
55462306a36Sopenharmony_ci	QPNPINT_REG_LATCHED_STS		= 0x18,
55562306a36Sopenharmony_ci};
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_cistruct spmi_pmic_arb_qpnpint_type {
55862306a36Sopenharmony_ci	u8 type; /* 1 -> edge */
55962306a36Sopenharmony_ci	u8 polarity_high;
56062306a36Sopenharmony_ci	u8 polarity_low;
56162306a36Sopenharmony_ci} __packed;
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci/* Simplified accessor functions for irqchip callbacks */
56462306a36Sopenharmony_cistatic void qpnpint_spmi_write(struct irq_data *d, u8 reg, void *buf,
56562306a36Sopenharmony_ci			       size_t len)
56662306a36Sopenharmony_ci{
56762306a36Sopenharmony_ci	struct spmi_pmic_arb *pmic_arb = irq_data_get_irq_chip_data(d);
56862306a36Sopenharmony_ci	u8 sid = hwirq_to_sid(d->hwirq);
56962306a36Sopenharmony_ci	u8 per = hwirq_to_per(d->hwirq);
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	if (pmic_arb_write_cmd(pmic_arb->spmic, SPMI_CMD_EXT_WRITEL, sid,
57262306a36Sopenharmony_ci			       (per << 8) + reg, buf, len))
57362306a36Sopenharmony_ci		dev_err_ratelimited(&pmic_arb->spmic->dev, "failed irqchip transaction on %x\n",
57462306a36Sopenharmony_ci				    d->irq);
57562306a36Sopenharmony_ci}
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_cistatic void qpnpint_spmi_read(struct irq_data *d, u8 reg, void *buf, size_t len)
57862306a36Sopenharmony_ci{
57962306a36Sopenharmony_ci	struct spmi_pmic_arb *pmic_arb = irq_data_get_irq_chip_data(d);
58062306a36Sopenharmony_ci	u8 sid = hwirq_to_sid(d->hwirq);
58162306a36Sopenharmony_ci	u8 per = hwirq_to_per(d->hwirq);
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	if (pmic_arb_read_cmd(pmic_arb->spmic, SPMI_CMD_EXT_READL, sid,
58462306a36Sopenharmony_ci			      (per << 8) + reg, buf, len))
58562306a36Sopenharmony_ci		dev_err_ratelimited(&pmic_arb->spmic->dev, "failed irqchip transaction on %x\n",
58662306a36Sopenharmony_ci				    d->irq);
58762306a36Sopenharmony_ci}
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_cistatic int qpnpint_spmi_masked_write(struct irq_data *d, u8 reg,
59062306a36Sopenharmony_ci				     const void *buf, const void *mask,
59162306a36Sopenharmony_ci				     size_t len)
59262306a36Sopenharmony_ci{
59362306a36Sopenharmony_ci	struct spmi_pmic_arb *pmic_arb = irq_data_get_irq_chip_data(d);
59462306a36Sopenharmony_ci	u8 sid = hwirq_to_sid(d->hwirq);
59562306a36Sopenharmony_ci	u8 per = hwirq_to_per(d->hwirq);
59662306a36Sopenharmony_ci	int rc;
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	rc = pmic_arb_masked_write(pmic_arb->spmic, sid, (per << 8) + reg, buf,
59962306a36Sopenharmony_ci				   mask, len);
60062306a36Sopenharmony_ci	if (rc)
60162306a36Sopenharmony_ci		dev_err_ratelimited(&pmic_arb->spmic->dev, "failed irqchip transaction on %x rc=%d\n",
60262306a36Sopenharmony_ci				    d->irq, rc);
60362306a36Sopenharmony_ci	return rc;
60462306a36Sopenharmony_ci}
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_cistatic void cleanup_irq(struct spmi_pmic_arb *pmic_arb, u16 apid, int id)
60762306a36Sopenharmony_ci{
60862306a36Sopenharmony_ci	u16 ppid = pmic_arb->apid_data[apid].ppid;
60962306a36Sopenharmony_ci	u8 sid = ppid >> 8;
61062306a36Sopenharmony_ci	u8 per = ppid & 0xFF;
61162306a36Sopenharmony_ci	u8 irq_mask = BIT(id);
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	dev_err_ratelimited(&pmic_arb->spmic->dev, "%s apid=%d sid=0x%x per=0x%x irq=%d\n",
61462306a36Sopenharmony_ci			__func__, apid, sid, per, id);
61562306a36Sopenharmony_ci	writel_relaxed(irq_mask, pmic_arb->ver_ops->irq_clear(pmic_arb, apid));
61662306a36Sopenharmony_ci}
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_cistatic int periph_interrupt(struct spmi_pmic_arb *pmic_arb, u16 apid)
61962306a36Sopenharmony_ci{
62062306a36Sopenharmony_ci	unsigned int irq;
62162306a36Sopenharmony_ci	u32 status, id;
62262306a36Sopenharmony_ci	int handled = 0;
62362306a36Sopenharmony_ci	u8 sid = (pmic_arb->apid_data[apid].ppid >> 8) & 0xF;
62462306a36Sopenharmony_ci	u8 per = pmic_arb->apid_data[apid].ppid & 0xFF;
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	status = readl_relaxed(pmic_arb->ver_ops->irq_status(pmic_arb, apid));
62762306a36Sopenharmony_ci	while (status) {
62862306a36Sopenharmony_ci		id = ffs(status) - 1;
62962306a36Sopenharmony_ci		status &= ~BIT(id);
63062306a36Sopenharmony_ci		irq = irq_find_mapping(pmic_arb->domain,
63162306a36Sopenharmony_ci					spec_to_hwirq(sid, per, id, apid));
63262306a36Sopenharmony_ci		if (irq == 0) {
63362306a36Sopenharmony_ci			cleanup_irq(pmic_arb, apid, id);
63462306a36Sopenharmony_ci			continue;
63562306a36Sopenharmony_ci		}
63662306a36Sopenharmony_ci		generic_handle_irq(irq);
63762306a36Sopenharmony_ci		handled++;
63862306a36Sopenharmony_ci	}
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	return handled;
64162306a36Sopenharmony_ci}
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_cistatic void pmic_arb_chained_irq(struct irq_desc *desc)
64462306a36Sopenharmony_ci{
64562306a36Sopenharmony_ci	struct spmi_pmic_arb *pmic_arb = irq_desc_get_handler_data(desc);
64662306a36Sopenharmony_ci	const struct pmic_arb_ver_ops *ver_ops = pmic_arb->ver_ops;
64762306a36Sopenharmony_ci	struct irq_chip *chip = irq_desc_get_chip(desc);
64862306a36Sopenharmony_ci	int first = pmic_arb->min_apid;
64962306a36Sopenharmony_ci	int last = pmic_arb->max_apid;
65062306a36Sopenharmony_ci	/*
65162306a36Sopenharmony_ci	 * acc_offset will be non-zero for the secondary SPMI bus instance on
65262306a36Sopenharmony_ci	 * v7 controllers.
65362306a36Sopenharmony_ci	 */
65462306a36Sopenharmony_ci	int acc_offset = pmic_arb->base_apid >> 5;
65562306a36Sopenharmony_ci	u8 ee = pmic_arb->ee;
65662306a36Sopenharmony_ci	u32 status, enable, handled = 0;
65762306a36Sopenharmony_ci	int i, id, apid;
65862306a36Sopenharmony_ci	/* status based dispatch */
65962306a36Sopenharmony_ci	bool acc_valid = false;
66062306a36Sopenharmony_ci	u32 irq_status = 0;
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	chained_irq_enter(chip, desc);
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	for (i = first >> 5; i <= last >> 5; ++i) {
66562306a36Sopenharmony_ci		status = readl_relaxed(ver_ops->owner_acc_status(pmic_arb, ee, i - acc_offset));
66662306a36Sopenharmony_ci		if (status)
66762306a36Sopenharmony_ci			acc_valid = true;
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci		while (status) {
67062306a36Sopenharmony_ci			id = ffs(status) - 1;
67162306a36Sopenharmony_ci			status &= ~BIT(id);
67262306a36Sopenharmony_ci			apid = id + i * 32;
67362306a36Sopenharmony_ci			if (apid < first || apid > last) {
67462306a36Sopenharmony_ci				WARN_ONCE(true, "spurious spmi irq received for apid=%d\n",
67562306a36Sopenharmony_ci					apid);
67662306a36Sopenharmony_ci				continue;
67762306a36Sopenharmony_ci			}
67862306a36Sopenharmony_ci			enable = readl_relaxed(
67962306a36Sopenharmony_ci					ver_ops->acc_enable(pmic_arb, apid));
68062306a36Sopenharmony_ci			if (enable & SPMI_PIC_ACC_ENABLE_BIT)
68162306a36Sopenharmony_ci				if (periph_interrupt(pmic_arb, apid) != 0)
68262306a36Sopenharmony_ci					handled++;
68362306a36Sopenharmony_ci		}
68462306a36Sopenharmony_ci	}
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	/* ACC_STATUS is empty but IRQ fired check IRQ_STATUS */
68762306a36Sopenharmony_ci	if (!acc_valid) {
68862306a36Sopenharmony_ci		for (i = first; i <= last; i++) {
68962306a36Sopenharmony_ci			/* skip if APPS is not irq owner */
69062306a36Sopenharmony_ci			if (pmic_arb->apid_data[i].irq_ee != pmic_arb->ee)
69162306a36Sopenharmony_ci				continue;
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci			irq_status = readl_relaxed(
69462306a36Sopenharmony_ci					     ver_ops->irq_status(pmic_arb, i));
69562306a36Sopenharmony_ci			if (irq_status) {
69662306a36Sopenharmony_ci				enable = readl_relaxed(
69762306a36Sopenharmony_ci					     ver_ops->acc_enable(pmic_arb, i));
69862306a36Sopenharmony_ci				if (enable & SPMI_PIC_ACC_ENABLE_BIT) {
69962306a36Sopenharmony_ci					dev_dbg(&pmic_arb->spmic->dev,
70062306a36Sopenharmony_ci						"Dispatching IRQ for apid=%d status=%x\n",
70162306a36Sopenharmony_ci						i, irq_status);
70262306a36Sopenharmony_ci					if (periph_interrupt(pmic_arb, i) != 0)
70362306a36Sopenharmony_ci						handled++;
70462306a36Sopenharmony_ci				}
70562306a36Sopenharmony_ci			}
70662306a36Sopenharmony_ci		}
70762306a36Sopenharmony_ci	}
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	if (handled == 0)
71062306a36Sopenharmony_ci		handle_bad_irq(desc);
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	chained_irq_exit(chip, desc);
71362306a36Sopenharmony_ci}
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_cistatic void qpnpint_irq_ack(struct irq_data *d)
71662306a36Sopenharmony_ci{
71762306a36Sopenharmony_ci	struct spmi_pmic_arb *pmic_arb = irq_data_get_irq_chip_data(d);
71862306a36Sopenharmony_ci	u8 irq = hwirq_to_irq(d->hwirq);
71962306a36Sopenharmony_ci	u16 apid = hwirq_to_apid(d->hwirq);
72062306a36Sopenharmony_ci	u8 data;
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	writel_relaxed(BIT(irq), pmic_arb->ver_ops->irq_clear(pmic_arb, apid));
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	data = BIT(irq);
72562306a36Sopenharmony_ci	qpnpint_spmi_write(d, QPNPINT_REG_LATCHED_CLR, &data, 1);
72662306a36Sopenharmony_ci}
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_cistatic void qpnpint_irq_mask(struct irq_data *d)
72962306a36Sopenharmony_ci{
73062306a36Sopenharmony_ci	u8 irq = hwirq_to_irq(d->hwirq);
73162306a36Sopenharmony_ci	u8 data = BIT(irq);
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci	qpnpint_spmi_write(d, QPNPINT_REG_EN_CLR, &data, 1);
73462306a36Sopenharmony_ci}
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_cistatic void qpnpint_irq_unmask(struct irq_data *d)
73762306a36Sopenharmony_ci{
73862306a36Sopenharmony_ci	struct spmi_pmic_arb *pmic_arb = irq_data_get_irq_chip_data(d);
73962306a36Sopenharmony_ci	const struct pmic_arb_ver_ops *ver_ops = pmic_arb->ver_ops;
74062306a36Sopenharmony_ci	u8 irq = hwirq_to_irq(d->hwirq);
74162306a36Sopenharmony_ci	u16 apid = hwirq_to_apid(d->hwirq);
74262306a36Sopenharmony_ci	u8 buf[2];
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	writel_relaxed(SPMI_PIC_ACC_ENABLE_BIT,
74562306a36Sopenharmony_ci			ver_ops->acc_enable(pmic_arb, apid));
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	qpnpint_spmi_read(d, QPNPINT_REG_EN_SET, &buf[0], 1);
74862306a36Sopenharmony_ci	if (!(buf[0] & BIT(irq))) {
74962306a36Sopenharmony_ci		/*
75062306a36Sopenharmony_ci		 * Since the interrupt is currently disabled, write to both the
75162306a36Sopenharmony_ci		 * LATCHED_CLR and EN_SET registers so that a spurious interrupt
75262306a36Sopenharmony_ci		 * cannot be triggered when the interrupt is enabled
75362306a36Sopenharmony_ci		 */
75462306a36Sopenharmony_ci		buf[0] = BIT(irq);
75562306a36Sopenharmony_ci		buf[1] = BIT(irq);
75662306a36Sopenharmony_ci		qpnpint_spmi_write(d, QPNPINT_REG_LATCHED_CLR, &buf, 2);
75762306a36Sopenharmony_ci	}
75862306a36Sopenharmony_ci}
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_cistatic int qpnpint_irq_set_type(struct irq_data *d, unsigned int flow_type)
76162306a36Sopenharmony_ci{
76262306a36Sopenharmony_ci	struct spmi_pmic_arb_qpnpint_type type = {0};
76362306a36Sopenharmony_ci	struct spmi_pmic_arb_qpnpint_type mask;
76462306a36Sopenharmony_ci	irq_flow_handler_t flow_handler;
76562306a36Sopenharmony_ci	u8 irq_bit = BIT(hwirq_to_irq(d->hwirq));
76662306a36Sopenharmony_ci	int rc;
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) {
76962306a36Sopenharmony_ci		type.type = irq_bit;
77062306a36Sopenharmony_ci		if (flow_type & IRQF_TRIGGER_RISING)
77162306a36Sopenharmony_ci			type.polarity_high = irq_bit;
77262306a36Sopenharmony_ci		if (flow_type & IRQF_TRIGGER_FALLING)
77362306a36Sopenharmony_ci			type.polarity_low = irq_bit;
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci		flow_handler = handle_edge_irq;
77662306a36Sopenharmony_ci	} else {
77762306a36Sopenharmony_ci		if ((flow_type & (IRQF_TRIGGER_HIGH)) &&
77862306a36Sopenharmony_ci		    (flow_type & (IRQF_TRIGGER_LOW)))
77962306a36Sopenharmony_ci			return -EINVAL;
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci		if (flow_type & IRQF_TRIGGER_HIGH)
78262306a36Sopenharmony_ci			type.polarity_high = irq_bit;
78362306a36Sopenharmony_ci		else
78462306a36Sopenharmony_ci			type.polarity_low = irq_bit;
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci		flow_handler = handle_level_irq;
78762306a36Sopenharmony_ci	}
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci	mask.type = irq_bit;
79062306a36Sopenharmony_ci	mask.polarity_high = irq_bit;
79162306a36Sopenharmony_ci	mask.polarity_low = irq_bit;
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci	rc = qpnpint_spmi_masked_write(d, QPNPINT_REG_SET_TYPE, &type, &mask,
79462306a36Sopenharmony_ci				       sizeof(type));
79562306a36Sopenharmony_ci	irq_set_handler_locked(d, flow_handler);
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci	return rc;
79862306a36Sopenharmony_ci}
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_cistatic int qpnpint_irq_set_wake(struct irq_data *d, unsigned int on)
80162306a36Sopenharmony_ci{
80262306a36Sopenharmony_ci	struct spmi_pmic_arb *pmic_arb = irq_data_get_irq_chip_data(d);
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	return irq_set_irq_wake(pmic_arb->irq, on);
80562306a36Sopenharmony_ci}
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_cistatic int qpnpint_get_irqchip_state(struct irq_data *d,
80862306a36Sopenharmony_ci				     enum irqchip_irq_state which,
80962306a36Sopenharmony_ci				     bool *state)
81062306a36Sopenharmony_ci{
81162306a36Sopenharmony_ci	u8 irq = hwirq_to_irq(d->hwirq);
81262306a36Sopenharmony_ci	u8 status = 0;
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci	if (which != IRQCHIP_STATE_LINE_LEVEL)
81562306a36Sopenharmony_ci		return -EINVAL;
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	qpnpint_spmi_read(d, QPNPINT_REG_RT_STS, &status, 1);
81862306a36Sopenharmony_ci	*state = !!(status & BIT(irq));
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	return 0;
82162306a36Sopenharmony_ci}
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_cistatic int qpnpint_irq_domain_activate(struct irq_domain *domain,
82462306a36Sopenharmony_ci				       struct irq_data *d, bool reserve)
82562306a36Sopenharmony_ci{
82662306a36Sopenharmony_ci	struct spmi_pmic_arb *pmic_arb = irq_data_get_irq_chip_data(d);
82762306a36Sopenharmony_ci	u16 periph = hwirq_to_per(d->hwirq);
82862306a36Sopenharmony_ci	u16 apid = hwirq_to_apid(d->hwirq);
82962306a36Sopenharmony_ci	u16 sid = hwirq_to_sid(d->hwirq);
83062306a36Sopenharmony_ci	u16 irq = hwirq_to_irq(d->hwirq);
83162306a36Sopenharmony_ci	u8 buf;
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	if (pmic_arb->apid_data[apid].irq_ee != pmic_arb->ee) {
83462306a36Sopenharmony_ci		dev_err(&pmic_arb->spmic->dev, "failed to xlate sid = %#x, periph = %#x, irq = %u: ee=%u but owner=%u\n",
83562306a36Sopenharmony_ci			sid, periph, irq, pmic_arb->ee,
83662306a36Sopenharmony_ci			pmic_arb->apid_data[apid].irq_ee);
83762306a36Sopenharmony_ci		return -ENODEV;
83862306a36Sopenharmony_ci	}
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	buf = BIT(irq);
84162306a36Sopenharmony_ci	qpnpint_spmi_write(d, QPNPINT_REG_EN_CLR, &buf, 1);
84262306a36Sopenharmony_ci	qpnpint_spmi_write(d, QPNPINT_REG_LATCHED_CLR, &buf, 1);
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	return 0;
84562306a36Sopenharmony_ci}
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_cistatic struct irq_chip pmic_arb_irqchip = {
84862306a36Sopenharmony_ci	.name		= "pmic_arb",
84962306a36Sopenharmony_ci	.irq_ack	= qpnpint_irq_ack,
85062306a36Sopenharmony_ci	.irq_mask	= qpnpint_irq_mask,
85162306a36Sopenharmony_ci	.irq_unmask	= qpnpint_irq_unmask,
85262306a36Sopenharmony_ci	.irq_set_type	= qpnpint_irq_set_type,
85362306a36Sopenharmony_ci	.irq_set_wake	= qpnpint_irq_set_wake,
85462306a36Sopenharmony_ci	.irq_get_irqchip_state	= qpnpint_get_irqchip_state,
85562306a36Sopenharmony_ci	.flags		= IRQCHIP_MASK_ON_SUSPEND,
85662306a36Sopenharmony_ci};
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_cistatic int qpnpint_irq_domain_translate(struct irq_domain *d,
85962306a36Sopenharmony_ci					struct irq_fwspec *fwspec,
86062306a36Sopenharmony_ci					unsigned long *out_hwirq,
86162306a36Sopenharmony_ci					unsigned int *out_type)
86262306a36Sopenharmony_ci{
86362306a36Sopenharmony_ci	struct spmi_pmic_arb *pmic_arb = d->host_data;
86462306a36Sopenharmony_ci	u32 *intspec = fwspec->param;
86562306a36Sopenharmony_ci	u16 apid, ppid;
86662306a36Sopenharmony_ci	int rc;
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	dev_dbg(&pmic_arb->spmic->dev, "intspec[0] 0x%1x intspec[1] 0x%02x intspec[2] 0x%02x\n",
86962306a36Sopenharmony_ci		intspec[0], intspec[1], intspec[2]);
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	if (irq_domain_get_of_node(d) != pmic_arb->spmic->dev.of_node)
87262306a36Sopenharmony_ci		return -EINVAL;
87362306a36Sopenharmony_ci	if (fwspec->param_count != 4)
87462306a36Sopenharmony_ci		return -EINVAL;
87562306a36Sopenharmony_ci	if (intspec[0] > 0xF || intspec[1] > 0xFF || intspec[2] > 0x7)
87662306a36Sopenharmony_ci		return -EINVAL;
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	ppid = intspec[0] << 8 | intspec[1];
87962306a36Sopenharmony_ci	rc = pmic_arb->ver_ops->ppid_to_apid(pmic_arb, ppid);
88062306a36Sopenharmony_ci	if (rc < 0) {
88162306a36Sopenharmony_ci		dev_err(&pmic_arb->spmic->dev, "failed to xlate sid = %#x, periph = %#x, irq = %u rc = %d\n",
88262306a36Sopenharmony_ci		intspec[0], intspec[1], intspec[2], rc);
88362306a36Sopenharmony_ci		return rc;
88462306a36Sopenharmony_ci	}
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	apid = rc;
88762306a36Sopenharmony_ci	/* Keep track of {max,min}_apid for bounding search during interrupt */
88862306a36Sopenharmony_ci	if (apid > pmic_arb->max_apid)
88962306a36Sopenharmony_ci		pmic_arb->max_apid = apid;
89062306a36Sopenharmony_ci	if (apid < pmic_arb->min_apid)
89162306a36Sopenharmony_ci		pmic_arb->min_apid = apid;
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci	*out_hwirq = spec_to_hwirq(intspec[0], intspec[1], intspec[2], apid);
89462306a36Sopenharmony_ci	*out_type  = intspec[3] & IRQ_TYPE_SENSE_MASK;
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci	dev_dbg(&pmic_arb->spmic->dev, "out_hwirq = %lu\n", *out_hwirq);
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	return 0;
89962306a36Sopenharmony_ci}
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_cistatic struct lock_class_key qpnpint_irq_lock_class, qpnpint_irq_request_class;
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_cistatic void qpnpint_irq_domain_map(struct spmi_pmic_arb *pmic_arb,
90462306a36Sopenharmony_ci				   struct irq_domain *domain, unsigned int virq,
90562306a36Sopenharmony_ci				   irq_hw_number_t hwirq, unsigned int type)
90662306a36Sopenharmony_ci{
90762306a36Sopenharmony_ci	irq_flow_handler_t handler;
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci	dev_dbg(&pmic_arb->spmic->dev, "virq = %u, hwirq = %lu, type = %u\n",
91062306a36Sopenharmony_ci		virq, hwirq, type);
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci	if (type & IRQ_TYPE_EDGE_BOTH)
91362306a36Sopenharmony_ci		handler = handle_edge_irq;
91462306a36Sopenharmony_ci	else
91562306a36Sopenharmony_ci		handler = handle_level_irq;
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	irq_set_lockdep_class(virq, &qpnpint_irq_lock_class,
91962306a36Sopenharmony_ci			      &qpnpint_irq_request_class);
92062306a36Sopenharmony_ci	irq_domain_set_info(domain, virq, hwirq, &pmic_arb_irqchip, pmic_arb,
92162306a36Sopenharmony_ci			    handler, NULL, NULL);
92262306a36Sopenharmony_ci}
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_cistatic int qpnpint_irq_domain_alloc(struct irq_domain *domain,
92562306a36Sopenharmony_ci				    unsigned int virq, unsigned int nr_irqs,
92662306a36Sopenharmony_ci				    void *data)
92762306a36Sopenharmony_ci{
92862306a36Sopenharmony_ci	struct spmi_pmic_arb *pmic_arb = domain->host_data;
92962306a36Sopenharmony_ci	struct irq_fwspec *fwspec = data;
93062306a36Sopenharmony_ci	irq_hw_number_t hwirq;
93162306a36Sopenharmony_ci	unsigned int type;
93262306a36Sopenharmony_ci	int ret, i;
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci	ret = qpnpint_irq_domain_translate(domain, fwspec, &hwirq, &type);
93562306a36Sopenharmony_ci	if (ret)
93662306a36Sopenharmony_ci		return ret;
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci	for (i = 0; i < nr_irqs; i++)
93962306a36Sopenharmony_ci		qpnpint_irq_domain_map(pmic_arb, domain, virq + i, hwirq + i,
94062306a36Sopenharmony_ci				       type);
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_ci	return 0;
94362306a36Sopenharmony_ci}
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_cistatic int pmic_arb_ppid_to_apid_v1(struct spmi_pmic_arb *pmic_arb, u16 ppid)
94662306a36Sopenharmony_ci{
94762306a36Sopenharmony_ci	u32 *mapping_table = pmic_arb->mapping_table;
94862306a36Sopenharmony_ci	int index = 0, i;
94962306a36Sopenharmony_ci	u16 apid_valid;
95062306a36Sopenharmony_ci	u16 apid;
95162306a36Sopenharmony_ci	u32 data;
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci	apid_valid = pmic_arb->ppid_to_apid[ppid];
95462306a36Sopenharmony_ci	if (apid_valid & PMIC_ARB_APID_VALID) {
95562306a36Sopenharmony_ci		apid = apid_valid & ~PMIC_ARB_APID_VALID;
95662306a36Sopenharmony_ci		return apid;
95762306a36Sopenharmony_ci	}
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci	for (i = 0; i < SPMI_MAPPING_TABLE_TREE_DEPTH; ++i) {
96062306a36Sopenharmony_ci		if (!test_and_set_bit(index, pmic_arb->mapping_table_valid))
96162306a36Sopenharmony_ci			mapping_table[index] = readl_relaxed(pmic_arb->cnfg +
96262306a36Sopenharmony_ci						SPMI_MAPPING_TABLE_REG(index));
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci		data = mapping_table[index];
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci		if (ppid & BIT(SPMI_MAPPING_BIT_INDEX(data))) {
96762306a36Sopenharmony_ci			if (SPMI_MAPPING_BIT_IS_1_FLAG(data)) {
96862306a36Sopenharmony_ci				index = SPMI_MAPPING_BIT_IS_1_RESULT(data);
96962306a36Sopenharmony_ci			} else {
97062306a36Sopenharmony_ci				apid = SPMI_MAPPING_BIT_IS_1_RESULT(data);
97162306a36Sopenharmony_ci				pmic_arb->ppid_to_apid[ppid]
97262306a36Sopenharmony_ci					= apid | PMIC_ARB_APID_VALID;
97362306a36Sopenharmony_ci				pmic_arb->apid_data[apid].ppid = ppid;
97462306a36Sopenharmony_ci				return apid;
97562306a36Sopenharmony_ci			}
97662306a36Sopenharmony_ci		} else {
97762306a36Sopenharmony_ci			if (SPMI_MAPPING_BIT_IS_0_FLAG(data)) {
97862306a36Sopenharmony_ci				index = SPMI_MAPPING_BIT_IS_0_RESULT(data);
97962306a36Sopenharmony_ci			} else {
98062306a36Sopenharmony_ci				apid = SPMI_MAPPING_BIT_IS_0_RESULT(data);
98162306a36Sopenharmony_ci				pmic_arb->ppid_to_apid[ppid]
98262306a36Sopenharmony_ci					= apid | PMIC_ARB_APID_VALID;
98362306a36Sopenharmony_ci				pmic_arb->apid_data[apid].ppid = ppid;
98462306a36Sopenharmony_ci				return apid;
98562306a36Sopenharmony_ci			}
98662306a36Sopenharmony_ci		}
98762306a36Sopenharmony_ci	}
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci	return -ENODEV;
99062306a36Sopenharmony_ci}
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci/* v1 offset per ee */
99362306a36Sopenharmony_cistatic int pmic_arb_offset_v1(struct spmi_pmic_arb *pmic_arb, u8 sid, u16 addr,
99462306a36Sopenharmony_ci			enum pmic_arb_channel ch_type)
99562306a36Sopenharmony_ci{
99662306a36Sopenharmony_ci	return 0x800 + 0x80 * pmic_arb->channel;
99762306a36Sopenharmony_ci}
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_cistatic u16 pmic_arb_find_apid(struct spmi_pmic_arb *pmic_arb, u16 ppid)
100062306a36Sopenharmony_ci{
100162306a36Sopenharmony_ci	struct apid_data *apidd = &pmic_arb->apid_data[pmic_arb->last_apid];
100262306a36Sopenharmony_ci	u32 regval, offset;
100362306a36Sopenharmony_ci	u16 id, apid;
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci	for (apid = pmic_arb->last_apid; ; apid++, apidd++) {
100662306a36Sopenharmony_ci		offset = pmic_arb->ver_ops->apid_map_offset(apid);
100762306a36Sopenharmony_ci		if (offset >= pmic_arb->core_size)
100862306a36Sopenharmony_ci			break;
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci		regval = readl_relaxed(pmic_arb->ver_ops->apid_owner(pmic_arb,
101162306a36Sopenharmony_ci								     apid));
101262306a36Sopenharmony_ci		apidd->irq_ee = SPMI_OWNERSHIP_PERIPH2OWNER(regval);
101362306a36Sopenharmony_ci		apidd->write_ee = apidd->irq_ee;
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci		regval = readl_relaxed(pmic_arb->core + offset);
101662306a36Sopenharmony_ci		if (!regval)
101762306a36Sopenharmony_ci			continue;
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci		id = (regval >> 8) & PMIC_ARB_PPID_MASK;
102062306a36Sopenharmony_ci		pmic_arb->ppid_to_apid[id] = apid | PMIC_ARB_APID_VALID;
102162306a36Sopenharmony_ci		apidd->ppid = id;
102262306a36Sopenharmony_ci		if (id == ppid) {
102362306a36Sopenharmony_ci			apid |= PMIC_ARB_APID_VALID;
102462306a36Sopenharmony_ci			break;
102562306a36Sopenharmony_ci		}
102662306a36Sopenharmony_ci	}
102762306a36Sopenharmony_ci	pmic_arb->last_apid = apid & ~PMIC_ARB_APID_VALID;
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci	return apid;
103062306a36Sopenharmony_ci}
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_cistatic int pmic_arb_ppid_to_apid_v2(struct spmi_pmic_arb *pmic_arb, u16 ppid)
103362306a36Sopenharmony_ci{
103462306a36Sopenharmony_ci	u16 apid_valid;
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci	apid_valid = pmic_arb->ppid_to_apid[ppid];
103762306a36Sopenharmony_ci	if (!(apid_valid & PMIC_ARB_APID_VALID))
103862306a36Sopenharmony_ci		apid_valid = pmic_arb_find_apid(pmic_arb, ppid);
103962306a36Sopenharmony_ci	if (!(apid_valid & PMIC_ARB_APID_VALID))
104062306a36Sopenharmony_ci		return -ENODEV;
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci	return apid_valid & ~PMIC_ARB_APID_VALID;
104362306a36Sopenharmony_ci}
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_cistatic int pmic_arb_read_apid_map_v5(struct spmi_pmic_arb *pmic_arb)
104662306a36Sopenharmony_ci{
104762306a36Sopenharmony_ci	struct apid_data *apidd;
104862306a36Sopenharmony_ci	struct apid_data *prev_apidd;
104962306a36Sopenharmony_ci	u16 i, apid, ppid, apid_max;
105062306a36Sopenharmony_ci	bool valid, is_irq_ee;
105162306a36Sopenharmony_ci	u32 regval, offset;
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci	/*
105462306a36Sopenharmony_ci	 * In order to allow multiple EEs to write to a single PPID in arbiter
105562306a36Sopenharmony_ci	 * version 5 and 7, there is more than one APID mapped to each PPID.
105662306a36Sopenharmony_ci	 * The owner field for each of these mappings specifies the EE which is
105762306a36Sopenharmony_ci	 * allowed to write to the APID.  The owner of the last (highest) APID
105862306a36Sopenharmony_ci	 * which has the IRQ owner bit set for a given PPID will receive
105962306a36Sopenharmony_ci	 * interrupts from the PPID.
106062306a36Sopenharmony_ci	 *
106162306a36Sopenharmony_ci	 * In arbiter version 7, the APID numbering space is divided between
106262306a36Sopenharmony_ci	 * the primary bus (0) and secondary bus (1) such that:
106362306a36Sopenharmony_ci	 * APID = 0 to N-1 are assigned to the primary bus
106462306a36Sopenharmony_ci	 * APID = N to N+M-1 are assigned to the secondary bus
106562306a36Sopenharmony_ci	 * where N = number of APIDs supported by the primary bus and
106662306a36Sopenharmony_ci	 *       M = number of APIDs supported by the secondary bus
106762306a36Sopenharmony_ci	 */
106862306a36Sopenharmony_ci	apidd = &pmic_arb->apid_data[pmic_arb->base_apid];
106962306a36Sopenharmony_ci	apid_max = pmic_arb->base_apid + pmic_arb->apid_count;
107062306a36Sopenharmony_ci	for (i = pmic_arb->base_apid; i < apid_max; i++, apidd++) {
107162306a36Sopenharmony_ci		offset = pmic_arb->ver_ops->apid_map_offset(i);
107262306a36Sopenharmony_ci		if (offset >= pmic_arb->core_size)
107362306a36Sopenharmony_ci			break;
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_ci		regval = readl_relaxed(pmic_arb->core + offset);
107662306a36Sopenharmony_ci		if (!regval)
107762306a36Sopenharmony_ci			continue;
107862306a36Sopenharmony_ci		ppid = (regval >> 8) & PMIC_ARB_PPID_MASK;
107962306a36Sopenharmony_ci		is_irq_ee = PMIC_ARB_CHAN_IS_IRQ_OWNER(regval);
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci		regval = readl_relaxed(pmic_arb->ver_ops->apid_owner(pmic_arb,
108262306a36Sopenharmony_ci								     i));
108362306a36Sopenharmony_ci		apidd->write_ee = SPMI_OWNERSHIP_PERIPH2OWNER(regval);
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci		apidd->irq_ee = is_irq_ee ? apidd->write_ee : INVALID_EE;
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_ci		valid = pmic_arb->ppid_to_apid[ppid] & PMIC_ARB_APID_VALID;
108862306a36Sopenharmony_ci		apid = pmic_arb->ppid_to_apid[ppid] & ~PMIC_ARB_APID_VALID;
108962306a36Sopenharmony_ci		prev_apidd = &pmic_arb->apid_data[apid];
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci		if (!valid || apidd->write_ee == pmic_arb->ee) {
109262306a36Sopenharmony_ci			/* First PPID mapping or one for this EE */
109362306a36Sopenharmony_ci			pmic_arb->ppid_to_apid[ppid] = i | PMIC_ARB_APID_VALID;
109462306a36Sopenharmony_ci		} else if (valid && is_irq_ee &&
109562306a36Sopenharmony_ci			   prev_apidd->write_ee == pmic_arb->ee) {
109662306a36Sopenharmony_ci			/*
109762306a36Sopenharmony_ci			 * Duplicate PPID mapping after the one for this EE;
109862306a36Sopenharmony_ci			 * override the irq owner
109962306a36Sopenharmony_ci			 */
110062306a36Sopenharmony_ci			prev_apidd->irq_ee = apidd->irq_ee;
110162306a36Sopenharmony_ci		}
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_ci		apidd->ppid = ppid;
110462306a36Sopenharmony_ci		pmic_arb->last_apid = i;
110562306a36Sopenharmony_ci	}
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_ci	/* Dump the mapping table for debug purposes. */
110862306a36Sopenharmony_ci	dev_dbg(&pmic_arb->spmic->dev, "PPID APID Write-EE IRQ-EE\n");
110962306a36Sopenharmony_ci	for (ppid = 0; ppid < PMIC_ARB_MAX_PPID; ppid++) {
111062306a36Sopenharmony_ci		apid = pmic_arb->ppid_to_apid[ppid];
111162306a36Sopenharmony_ci		if (apid & PMIC_ARB_APID_VALID) {
111262306a36Sopenharmony_ci			apid &= ~PMIC_ARB_APID_VALID;
111362306a36Sopenharmony_ci			apidd = &pmic_arb->apid_data[apid];
111462306a36Sopenharmony_ci			dev_dbg(&pmic_arb->spmic->dev, "%#03X %3u %2u %2u\n",
111562306a36Sopenharmony_ci			      ppid, apid, apidd->write_ee, apidd->irq_ee);
111662306a36Sopenharmony_ci		}
111762306a36Sopenharmony_ci	}
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_ci	return 0;
112062306a36Sopenharmony_ci}
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_cistatic int pmic_arb_ppid_to_apid_v5(struct spmi_pmic_arb *pmic_arb, u16 ppid)
112362306a36Sopenharmony_ci{
112462306a36Sopenharmony_ci	if (!(pmic_arb->ppid_to_apid[ppid] & PMIC_ARB_APID_VALID))
112562306a36Sopenharmony_ci		return -ENODEV;
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ci	return pmic_arb->ppid_to_apid[ppid] & ~PMIC_ARB_APID_VALID;
112862306a36Sopenharmony_ci}
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_ci/* v2 offset per ppid and per ee */
113162306a36Sopenharmony_cistatic int pmic_arb_offset_v2(struct spmi_pmic_arb *pmic_arb, u8 sid, u16 addr,
113262306a36Sopenharmony_ci			   enum pmic_arb_channel ch_type)
113362306a36Sopenharmony_ci{
113462306a36Sopenharmony_ci	u16 apid;
113562306a36Sopenharmony_ci	u16 ppid;
113662306a36Sopenharmony_ci	int rc;
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci	ppid = sid << 8 | ((addr >> 8) & 0xFF);
113962306a36Sopenharmony_ci	rc = pmic_arb_ppid_to_apid_v2(pmic_arb, ppid);
114062306a36Sopenharmony_ci	if (rc < 0)
114162306a36Sopenharmony_ci		return rc;
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ci	apid = rc;
114462306a36Sopenharmony_ci	return 0x1000 * pmic_arb->ee + 0x8000 * apid;
114562306a36Sopenharmony_ci}
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_ci/*
114862306a36Sopenharmony_ci * v5 offset per ee and per apid for observer channels and per apid for
114962306a36Sopenharmony_ci * read/write channels.
115062306a36Sopenharmony_ci */
115162306a36Sopenharmony_cistatic int pmic_arb_offset_v5(struct spmi_pmic_arb *pmic_arb, u8 sid, u16 addr,
115262306a36Sopenharmony_ci			   enum pmic_arb_channel ch_type)
115362306a36Sopenharmony_ci{
115462306a36Sopenharmony_ci	u16 apid;
115562306a36Sopenharmony_ci	int rc;
115662306a36Sopenharmony_ci	u32 offset = 0;
115762306a36Sopenharmony_ci	u16 ppid = (sid << 8) | (addr >> 8);
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ci	rc = pmic_arb_ppid_to_apid_v5(pmic_arb, ppid);
116062306a36Sopenharmony_ci	if (rc < 0)
116162306a36Sopenharmony_ci		return rc;
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci	apid = rc;
116462306a36Sopenharmony_ci	switch (ch_type) {
116562306a36Sopenharmony_ci	case PMIC_ARB_CHANNEL_OBS:
116662306a36Sopenharmony_ci		offset = 0x10000 * pmic_arb->ee + 0x80 * apid;
116762306a36Sopenharmony_ci		break;
116862306a36Sopenharmony_ci	case PMIC_ARB_CHANNEL_RW:
116962306a36Sopenharmony_ci		if (pmic_arb->apid_data[apid].write_ee != pmic_arb->ee) {
117062306a36Sopenharmony_ci			dev_err(&pmic_arb->spmic->dev, "disallowed SPMI write to sid=%u, addr=0x%04X\n",
117162306a36Sopenharmony_ci				sid, addr);
117262306a36Sopenharmony_ci			return -EPERM;
117362306a36Sopenharmony_ci		}
117462306a36Sopenharmony_ci		offset = 0x10000 * apid;
117562306a36Sopenharmony_ci		break;
117662306a36Sopenharmony_ci	}
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ci	return offset;
117962306a36Sopenharmony_ci}
118062306a36Sopenharmony_ci
118162306a36Sopenharmony_ci/*
118262306a36Sopenharmony_ci * v7 offset per ee and per apid for observer channels and per apid for
118362306a36Sopenharmony_ci * read/write channels.
118462306a36Sopenharmony_ci */
118562306a36Sopenharmony_cistatic int pmic_arb_offset_v7(struct spmi_pmic_arb *pmic_arb, u8 sid, u16 addr,
118662306a36Sopenharmony_ci			   enum pmic_arb_channel ch_type)
118762306a36Sopenharmony_ci{
118862306a36Sopenharmony_ci	u16 apid;
118962306a36Sopenharmony_ci	int rc;
119062306a36Sopenharmony_ci	u32 offset = 0;
119162306a36Sopenharmony_ci	u16 ppid = (sid << 8) | (addr >> 8);
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci	rc = pmic_arb->ver_ops->ppid_to_apid(pmic_arb, ppid);
119462306a36Sopenharmony_ci	if (rc < 0)
119562306a36Sopenharmony_ci		return rc;
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_ci	apid = rc;
119862306a36Sopenharmony_ci	switch (ch_type) {
119962306a36Sopenharmony_ci	case PMIC_ARB_CHANNEL_OBS:
120062306a36Sopenharmony_ci		offset = 0x8000 * pmic_arb->ee + 0x20 * apid;
120162306a36Sopenharmony_ci		break;
120262306a36Sopenharmony_ci	case PMIC_ARB_CHANNEL_RW:
120362306a36Sopenharmony_ci		if (pmic_arb->apid_data[apid].write_ee != pmic_arb->ee) {
120462306a36Sopenharmony_ci			dev_err(&pmic_arb->spmic->dev, "disallowed SPMI write to sid=%u, addr=0x%04X\n",
120562306a36Sopenharmony_ci				sid, addr);
120662306a36Sopenharmony_ci			return -EPERM;
120762306a36Sopenharmony_ci		}
120862306a36Sopenharmony_ci		offset = 0x1000 * apid;
120962306a36Sopenharmony_ci		break;
121062306a36Sopenharmony_ci	}
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci	return offset;
121362306a36Sopenharmony_ci}
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_cistatic u32 pmic_arb_fmt_cmd_v1(u8 opc, u8 sid, u16 addr, u8 bc)
121662306a36Sopenharmony_ci{
121762306a36Sopenharmony_ci	return (opc << 27) | ((sid & 0xf) << 20) | (addr << 4) | (bc & 0x7);
121862306a36Sopenharmony_ci}
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_cistatic u32 pmic_arb_fmt_cmd_v2(u8 opc, u8 sid, u16 addr, u8 bc)
122162306a36Sopenharmony_ci{
122262306a36Sopenharmony_ci	return (opc << 27) | ((addr & 0xff) << 4) | (bc & 0x7);
122362306a36Sopenharmony_ci}
122462306a36Sopenharmony_ci
122562306a36Sopenharmony_cistatic void __iomem *
122662306a36Sopenharmony_cipmic_arb_owner_acc_status_v1(struct spmi_pmic_arb *pmic_arb, u8 m, u16 n)
122762306a36Sopenharmony_ci{
122862306a36Sopenharmony_ci	return pmic_arb->intr + 0x20 * m + 0x4 * n;
122962306a36Sopenharmony_ci}
123062306a36Sopenharmony_ci
123162306a36Sopenharmony_cistatic void __iomem *
123262306a36Sopenharmony_cipmic_arb_owner_acc_status_v2(struct spmi_pmic_arb *pmic_arb, u8 m, u16 n)
123362306a36Sopenharmony_ci{
123462306a36Sopenharmony_ci	return pmic_arb->intr + 0x100000 + 0x1000 * m + 0x4 * n;
123562306a36Sopenharmony_ci}
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_cistatic void __iomem *
123862306a36Sopenharmony_cipmic_arb_owner_acc_status_v3(struct spmi_pmic_arb *pmic_arb, u8 m, u16 n)
123962306a36Sopenharmony_ci{
124062306a36Sopenharmony_ci	return pmic_arb->intr + 0x200000 + 0x1000 * m + 0x4 * n;
124162306a36Sopenharmony_ci}
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_cistatic void __iomem *
124462306a36Sopenharmony_cipmic_arb_owner_acc_status_v5(struct spmi_pmic_arb *pmic_arb, u8 m, u16 n)
124562306a36Sopenharmony_ci{
124662306a36Sopenharmony_ci	return pmic_arb->intr + 0x10000 * m + 0x4 * n;
124762306a36Sopenharmony_ci}
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_cistatic void __iomem *
125062306a36Sopenharmony_cipmic_arb_owner_acc_status_v7(struct spmi_pmic_arb *pmic_arb, u8 m, u16 n)
125162306a36Sopenharmony_ci{
125262306a36Sopenharmony_ci	return pmic_arb->intr + 0x1000 * m + 0x4 * n;
125362306a36Sopenharmony_ci}
125462306a36Sopenharmony_ci
125562306a36Sopenharmony_cistatic void __iomem *
125662306a36Sopenharmony_cipmic_arb_acc_enable_v1(struct spmi_pmic_arb *pmic_arb, u16 n)
125762306a36Sopenharmony_ci{
125862306a36Sopenharmony_ci	return pmic_arb->intr + 0x200 + 0x4 * n;
125962306a36Sopenharmony_ci}
126062306a36Sopenharmony_ci
126162306a36Sopenharmony_cistatic void __iomem *
126262306a36Sopenharmony_cipmic_arb_acc_enable_v2(struct spmi_pmic_arb *pmic_arb, u16 n)
126362306a36Sopenharmony_ci{
126462306a36Sopenharmony_ci	return pmic_arb->intr + 0x1000 * n;
126562306a36Sopenharmony_ci}
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_cistatic void __iomem *
126862306a36Sopenharmony_cipmic_arb_acc_enable_v5(struct spmi_pmic_arb *pmic_arb, u16 n)
126962306a36Sopenharmony_ci{
127062306a36Sopenharmony_ci	return pmic_arb->wr_base + 0x100 + 0x10000 * n;
127162306a36Sopenharmony_ci}
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_cistatic void __iomem *
127462306a36Sopenharmony_cipmic_arb_acc_enable_v7(struct spmi_pmic_arb *pmic_arb, u16 n)
127562306a36Sopenharmony_ci{
127662306a36Sopenharmony_ci	return pmic_arb->wr_base + 0x100 + 0x1000 * n;
127762306a36Sopenharmony_ci}
127862306a36Sopenharmony_ci
127962306a36Sopenharmony_cistatic void __iomem *
128062306a36Sopenharmony_cipmic_arb_irq_status_v1(struct spmi_pmic_arb *pmic_arb, u16 n)
128162306a36Sopenharmony_ci{
128262306a36Sopenharmony_ci	return pmic_arb->intr + 0x600 + 0x4 * n;
128362306a36Sopenharmony_ci}
128462306a36Sopenharmony_ci
128562306a36Sopenharmony_cistatic void __iomem *
128662306a36Sopenharmony_cipmic_arb_irq_status_v2(struct spmi_pmic_arb *pmic_arb, u16 n)
128762306a36Sopenharmony_ci{
128862306a36Sopenharmony_ci	return pmic_arb->intr + 0x4 + 0x1000 * n;
128962306a36Sopenharmony_ci}
129062306a36Sopenharmony_ci
129162306a36Sopenharmony_cistatic void __iomem *
129262306a36Sopenharmony_cipmic_arb_irq_status_v5(struct spmi_pmic_arb *pmic_arb, u16 n)
129362306a36Sopenharmony_ci{
129462306a36Sopenharmony_ci	return pmic_arb->wr_base + 0x104 + 0x10000 * n;
129562306a36Sopenharmony_ci}
129662306a36Sopenharmony_ci
129762306a36Sopenharmony_cistatic void __iomem *
129862306a36Sopenharmony_cipmic_arb_irq_status_v7(struct spmi_pmic_arb *pmic_arb, u16 n)
129962306a36Sopenharmony_ci{
130062306a36Sopenharmony_ci	return pmic_arb->wr_base + 0x104 + 0x1000 * n;
130162306a36Sopenharmony_ci}
130262306a36Sopenharmony_ci
130362306a36Sopenharmony_cistatic void __iomem *
130462306a36Sopenharmony_cipmic_arb_irq_clear_v1(struct spmi_pmic_arb *pmic_arb, u16 n)
130562306a36Sopenharmony_ci{
130662306a36Sopenharmony_ci	return pmic_arb->intr + 0xA00 + 0x4 * n;
130762306a36Sopenharmony_ci}
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_cistatic void __iomem *
131062306a36Sopenharmony_cipmic_arb_irq_clear_v2(struct spmi_pmic_arb *pmic_arb, u16 n)
131162306a36Sopenharmony_ci{
131262306a36Sopenharmony_ci	return pmic_arb->intr + 0x8 + 0x1000 * n;
131362306a36Sopenharmony_ci}
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_cistatic void __iomem *
131662306a36Sopenharmony_cipmic_arb_irq_clear_v5(struct spmi_pmic_arb *pmic_arb, u16 n)
131762306a36Sopenharmony_ci{
131862306a36Sopenharmony_ci	return pmic_arb->wr_base + 0x108 + 0x10000 * n;
131962306a36Sopenharmony_ci}
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_cistatic void __iomem *
132262306a36Sopenharmony_cipmic_arb_irq_clear_v7(struct spmi_pmic_arb *pmic_arb, u16 n)
132362306a36Sopenharmony_ci{
132462306a36Sopenharmony_ci	return pmic_arb->wr_base + 0x108 + 0x1000 * n;
132562306a36Sopenharmony_ci}
132662306a36Sopenharmony_ci
132762306a36Sopenharmony_cistatic u32 pmic_arb_apid_map_offset_v2(u16 n)
132862306a36Sopenharmony_ci{
132962306a36Sopenharmony_ci	return 0x800 + 0x4 * n;
133062306a36Sopenharmony_ci}
133162306a36Sopenharmony_ci
133262306a36Sopenharmony_cistatic u32 pmic_arb_apid_map_offset_v5(u16 n)
133362306a36Sopenharmony_ci{
133462306a36Sopenharmony_ci	return 0x900 + 0x4 * n;
133562306a36Sopenharmony_ci}
133662306a36Sopenharmony_ci
133762306a36Sopenharmony_cistatic u32 pmic_arb_apid_map_offset_v7(u16 n)
133862306a36Sopenharmony_ci{
133962306a36Sopenharmony_ci	return 0x2000 + 0x4 * n;
134062306a36Sopenharmony_ci}
134162306a36Sopenharmony_ci
134262306a36Sopenharmony_cistatic void __iomem *
134362306a36Sopenharmony_cipmic_arb_apid_owner_v2(struct spmi_pmic_arb *pmic_arb, u16 n)
134462306a36Sopenharmony_ci{
134562306a36Sopenharmony_ci	return pmic_arb->cnfg + 0x700 + 0x4 * n;
134662306a36Sopenharmony_ci}
134762306a36Sopenharmony_ci
134862306a36Sopenharmony_ci/*
134962306a36Sopenharmony_ci * For arbiter version 7, APID ownership table registers have independent
135062306a36Sopenharmony_ci * numbering space for each SPMI bus instance, so each is indexed starting from
135162306a36Sopenharmony_ci * 0.
135262306a36Sopenharmony_ci */
135362306a36Sopenharmony_cistatic void __iomem *
135462306a36Sopenharmony_cipmic_arb_apid_owner_v7(struct spmi_pmic_arb *pmic_arb, u16 n)
135562306a36Sopenharmony_ci{
135662306a36Sopenharmony_ci	return pmic_arb->cnfg + 0x4 * (n - pmic_arb->base_apid);
135762306a36Sopenharmony_ci}
135862306a36Sopenharmony_ci
135962306a36Sopenharmony_cistatic const struct pmic_arb_ver_ops pmic_arb_v1 = {
136062306a36Sopenharmony_ci	.ver_str		= "v1",
136162306a36Sopenharmony_ci	.ppid_to_apid		= pmic_arb_ppid_to_apid_v1,
136262306a36Sopenharmony_ci	.non_data_cmd		= pmic_arb_non_data_cmd_v1,
136362306a36Sopenharmony_ci	.offset			= pmic_arb_offset_v1,
136462306a36Sopenharmony_ci	.fmt_cmd		= pmic_arb_fmt_cmd_v1,
136562306a36Sopenharmony_ci	.owner_acc_status	= pmic_arb_owner_acc_status_v1,
136662306a36Sopenharmony_ci	.acc_enable		= pmic_arb_acc_enable_v1,
136762306a36Sopenharmony_ci	.irq_status		= pmic_arb_irq_status_v1,
136862306a36Sopenharmony_ci	.irq_clear		= pmic_arb_irq_clear_v1,
136962306a36Sopenharmony_ci	.apid_map_offset	= pmic_arb_apid_map_offset_v2,
137062306a36Sopenharmony_ci	.apid_owner		= pmic_arb_apid_owner_v2,
137162306a36Sopenharmony_ci};
137262306a36Sopenharmony_ci
137362306a36Sopenharmony_cistatic const struct pmic_arb_ver_ops pmic_arb_v2 = {
137462306a36Sopenharmony_ci	.ver_str		= "v2",
137562306a36Sopenharmony_ci	.ppid_to_apid		= pmic_arb_ppid_to_apid_v2,
137662306a36Sopenharmony_ci	.non_data_cmd		= pmic_arb_non_data_cmd_v2,
137762306a36Sopenharmony_ci	.offset			= pmic_arb_offset_v2,
137862306a36Sopenharmony_ci	.fmt_cmd		= pmic_arb_fmt_cmd_v2,
137962306a36Sopenharmony_ci	.owner_acc_status	= pmic_arb_owner_acc_status_v2,
138062306a36Sopenharmony_ci	.acc_enable		= pmic_arb_acc_enable_v2,
138162306a36Sopenharmony_ci	.irq_status		= pmic_arb_irq_status_v2,
138262306a36Sopenharmony_ci	.irq_clear		= pmic_arb_irq_clear_v2,
138362306a36Sopenharmony_ci	.apid_map_offset	= pmic_arb_apid_map_offset_v2,
138462306a36Sopenharmony_ci	.apid_owner		= pmic_arb_apid_owner_v2,
138562306a36Sopenharmony_ci};
138662306a36Sopenharmony_ci
138762306a36Sopenharmony_cistatic const struct pmic_arb_ver_ops pmic_arb_v3 = {
138862306a36Sopenharmony_ci	.ver_str		= "v3",
138962306a36Sopenharmony_ci	.ppid_to_apid		= pmic_arb_ppid_to_apid_v2,
139062306a36Sopenharmony_ci	.non_data_cmd		= pmic_arb_non_data_cmd_v2,
139162306a36Sopenharmony_ci	.offset			= pmic_arb_offset_v2,
139262306a36Sopenharmony_ci	.fmt_cmd		= pmic_arb_fmt_cmd_v2,
139362306a36Sopenharmony_ci	.owner_acc_status	= pmic_arb_owner_acc_status_v3,
139462306a36Sopenharmony_ci	.acc_enable		= pmic_arb_acc_enable_v2,
139562306a36Sopenharmony_ci	.irq_status		= pmic_arb_irq_status_v2,
139662306a36Sopenharmony_ci	.irq_clear		= pmic_arb_irq_clear_v2,
139762306a36Sopenharmony_ci	.apid_map_offset	= pmic_arb_apid_map_offset_v2,
139862306a36Sopenharmony_ci	.apid_owner		= pmic_arb_apid_owner_v2,
139962306a36Sopenharmony_ci};
140062306a36Sopenharmony_ci
140162306a36Sopenharmony_cistatic const struct pmic_arb_ver_ops pmic_arb_v5 = {
140262306a36Sopenharmony_ci	.ver_str		= "v5",
140362306a36Sopenharmony_ci	.ppid_to_apid		= pmic_arb_ppid_to_apid_v5,
140462306a36Sopenharmony_ci	.non_data_cmd		= pmic_arb_non_data_cmd_v2,
140562306a36Sopenharmony_ci	.offset			= pmic_arb_offset_v5,
140662306a36Sopenharmony_ci	.fmt_cmd		= pmic_arb_fmt_cmd_v2,
140762306a36Sopenharmony_ci	.owner_acc_status	= pmic_arb_owner_acc_status_v5,
140862306a36Sopenharmony_ci	.acc_enable		= pmic_arb_acc_enable_v5,
140962306a36Sopenharmony_ci	.irq_status		= pmic_arb_irq_status_v5,
141062306a36Sopenharmony_ci	.irq_clear		= pmic_arb_irq_clear_v5,
141162306a36Sopenharmony_ci	.apid_map_offset	= pmic_arb_apid_map_offset_v5,
141262306a36Sopenharmony_ci	.apid_owner		= pmic_arb_apid_owner_v2,
141362306a36Sopenharmony_ci};
141462306a36Sopenharmony_ci
141562306a36Sopenharmony_cistatic const struct pmic_arb_ver_ops pmic_arb_v7 = {
141662306a36Sopenharmony_ci	.ver_str		= "v7",
141762306a36Sopenharmony_ci	.ppid_to_apid		= pmic_arb_ppid_to_apid_v5,
141862306a36Sopenharmony_ci	.non_data_cmd		= pmic_arb_non_data_cmd_v2,
141962306a36Sopenharmony_ci	.offset			= pmic_arb_offset_v7,
142062306a36Sopenharmony_ci	.fmt_cmd		= pmic_arb_fmt_cmd_v2,
142162306a36Sopenharmony_ci	.owner_acc_status	= pmic_arb_owner_acc_status_v7,
142262306a36Sopenharmony_ci	.acc_enable		= pmic_arb_acc_enable_v7,
142362306a36Sopenharmony_ci	.irq_status		= pmic_arb_irq_status_v7,
142462306a36Sopenharmony_ci	.irq_clear		= pmic_arb_irq_clear_v7,
142562306a36Sopenharmony_ci	.apid_map_offset	= pmic_arb_apid_map_offset_v7,
142662306a36Sopenharmony_ci	.apid_owner		= pmic_arb_apid_owner_v7,
142762306a36Sopenharmony_ci};
142862306a36Sopenharmony_ci
142962306a36Sopenharmony_cistatic const struct irq_domain_ops pmic_arb_irq_domain_ops = {
143062306a36Sopenharmony_ci	.activate = qpnpint_irq_domain_activate,
143162306a36Sopenharmony_ci	.alloc = qpnpint_irq_domain_alloc,
143262306a36Sopenharmony_ci	.free = irq_domain_free_irqs_common,
143362306a36Sopenharmony_ci	.translate = qpnpint_irq_domain_translate,
143462306a36Sopenharmony_ci};
143562306a36Sopenharmony_ci
143662306a36Sopenharmony_cistatic int spmi_pmic_arb_probe(struct platform_device *pdev)
143762306a36Sopenharmony_ci{
143862306a36Sopenharmony_ci	struct spmi_pmic_arb *pmic_arb;
143962306a36Sopenharmony_ci	struct spmi_controller *ctrl;
144062306a36Sopenharmony_ci	struct resource *res;
144162306a36Sopenharmony_ci	void __iomem *core;
144262306a36Sopenharmony_ci	u32 *mapping_table;
144362306a36Sopenharmony_ci	u32 channel, ee, hw_ver;
144462306a36Sopenharmony_ci	int err;
144562306a36Sopenharmony_ci
144662306a36Sopenharmony_ci	ctrl = spmi_controller_alloc(&pdev->dev, sizeof(*pmic_arb));
144762306a36Sopenharmony_ci	if (!ctrl)
144862306a36Sopenharmony_ci		return -ENOMEM;
144962306a36Sopenharmony_ci
145062306a36Sopenharmony_ci	pmic_arb = spmi_controller_get_drvdata(ctrl);
145162306a36Sopenharmony_ci	pmic_arb->spmic = ctrl;
145262306a36Sopenharmony_ci
145362306a36Sopenharmony_ci	/*
145462306a36Sopenharmony_ci	 * Please don't replace this with devm_platform_ioremap_resource() or
145562306a36Sopenharmony_ci	 * devm_ioremap_resource().  These both result in a call to
145662306a36Sopenharmony_ci	 * devm_request_mem_region() which prevents multiple mappings of this
145762306a36Sopenharmony_ci	 * register address range.  SoCs with PMIC arbiter v7 may define two
145862306a36Sopenharmony_ci	 * arbiter devices, for the two physical SPMI interfaces, which  share
145962306a36Sopenharmony_ci	 * some register address ranges (i.e. "core", "obsrvr", and "chnls").
146062306a36Sopenharmony_ci	 * Ensure that both devices probe successfully by calling devm_ioremap()
146162306a36Sopenharmony_ci	 * which does not result in a devm_request_mem_region() call.
146262306a36Sopenharmony_ci	 */
146362306a36Sopenharmony_ci	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "core");
146462306a36Sopenharmony_ci	core = devm_ioremap(&ctrl->dev, res->start, resource_size(res));
146562306a36Sopenharmony_ci	if (IS_ERR(core)) {
146662306a36Sopenharmony_ci		err = PTR_ERR(core);
146762306a36Sopenharmony_ci		goto err_put_ctrl;
146862306a36Sopenharmony_ci	}
146962306a36Sopenharmony_ci
147062306a36Sopenharmony_ci	pmic_arb->core_size = resource_size(res);
147162306a36Sopenharmony_ci
147262306a36Sopenharmony_ci	pmic_arb->ppid_to_apid = devm_kcalloc(&ctrl->dev, PMIC_ARB_MAX_PPID,
147362306a36Sopenharmony_ci					      sizeof(*pmic_arb->ppid_to_apid),
147462306a36Sopenharmony_ci					      GFP_KERNEL);
147562306a36Sopenharmony_ci	if (!pmic_arb->ppid_to_apid) {
147662306a36Sopenharmony_ci		err = -ENOMEM;
147762306a36Sopenharmony_ci		goto err_put_ctrl;
147862306a36Sopenharmony_ci	}
147962306a36Sopenharmony_ci
148062306a36Sopenharmony_ci	hw_ver = readl_relaxed(core + PMIC_ARB_VERSION);
148162306a36Sopenharmony_ci
148262306a36Sopenharmony_ci	if (hw_ver < PMIC_ARB_VERSION_V2_MIN) {
148362306a36Sopenharmony_ci		pmic_arb->ver_ops = &pmic_arb_v1;
148462306a36Sopenharmony_ci		pmic_arb->wr_base = core;
148562306a36Sopenharmony_ci		pmic_arb->rd_base = core;
148662306a36Sopenharmony_ci	} else {
148762306a36Sopenharmony_ci		pmic_arb->core = core;
148862306a36Sopenharmony_ci
148962306a36Sopenharmony_ci		if (hw_ver < PMIC_ARB_VERSION_V3_MIN)
149062306a36Sopenharmony_ci			pmic_arb->ver_ops = &pmic_arb_v2;
149162306a36Sopenharmony_ci		else if (hw_ver < PMIC_ARB_VERSION_V5_MIN)
149262306a36Sopenharmony_ci			pmic_arb->ver_ops = &pmic_arb_v3;
149362306a36Sopenharmony_ci		else if (hw_ver < PMIC_ARB_VERSION_V7_MIN)
149462306a36Sopenharmony_ci			pmic_arb->ver_ops = &pmic_arb_v5;
149562306a36Sopenharmony_ci		else
149662306a36Sopenharmony_ci			pmic_arb->ver_ops = &pmic_arb_v7;
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_ci		res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
149962306a36Sopenharmony_ci						   "obsrvr");
150062306a36Sopenharmony_ci		pmic_arb->rd_base = devm_ioremap(&ctrl->dev, res->start,
150162306a36Sopenharmony_ci						 resource_size(res));
150262306a36Sopenharmony_ci		if (IS_ERR(pmic_arb->rd_base)) {
150362306a36Sopenharmony_ci			err = PTR_ERR(pmic_arb->rd_base);
150462306a36Sopenharmony_ci			goto err_put_ctrl;
150562306a36Sopenharmony_ci		}
150662306a36Sopenharmony_ci
150762306a36Sopenharmony_ci		res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
150862306a36Sopenharmony_ci						   "chnls");
150962306a36Sopenharmony_ci		pmic_arb->wr_base = devm_ioremap(&ctrl->dev, res->start,
151062306a36Sopenharmony_ci						 resource_size(res));
151162306a36Sopenharmony_ci		if (IS_ERR(pmic_arb->wr_base)) {
151262306a36Sopenharmony_ci			err = PTR_ERR(pmic_arb->wr_base);
151362306a36Sopenharmony_ci			goto err_put_ctrl;
151462306a36Sopenharmony_ci		}
151562306a36Sopenharmony_ci	}
151662306a36Sopenharmony_ci
151762306a36Sopenharmony_ci	pmic_arb->max_periphs = PMIC_ARB_MAX_PERIPHS;
151862306a36Sopenharmony_ci
151962306a36Sopenharmony_ci	if (hw_ver >= PMIC_ARB_VERSION_V7_MIN) {
152062306a36Sopenharmony_ci		pmic_arb->max_periphs = PMIC_ARB_MAX_PERIPHS_V7;
152162306a36Sopenharmony_ci		/* Optional property for v7: */
152262306a36Sopenharmony_ci		of_property_read_u32(pdev->dev.of_node, "qcom,bus-id",
152362306a36Sopenharmony_ci					&pmic_arb->bus_instance);
152462306a36Sopenharmony_ci		if (pmic_arb->bus_instance > 1) {
152562306a36Sopenharmony_ci			err = -EINVAL;
152662306a36Sopenharmony_ci			dev_err(&pdev->dev, "invalid bus instance (%u) specified\n",
152762306a36Sopenharmony_ci				pmic_arb->bus_instance);
152862306a36Sopenharmony_ci			goto err_put_ctrl;
152962306a36Sopenharmony_ci		}
153062306a36Sopenharmony_ci
153162306a36Sopenharmony_ci		if (pmic_arb->bus_instance == 0) {
153262306a36Sopenharmony_ci			pmic_arb->base_apid = 0;
153362306a36Sopenharmony_ci			pmic_arb->apid_count =
153462306a36Sopenharmony_ci				readl_relaxed(core + PMIC_ARB_FEATURES) &
153562306a36Sopenharmony_ci				PMIC_ARB_FEATURES_PERIPH_MASK;
153662306a36Sopenharmony_ci		} else {
153762306a36Sopenharmony_ci			pmic_arb->base_apid =
153862306a36Sopenharmony_ci				readl_relaxed(core + PMIC_ARB_FEATURES) &
153962306a36Sopenharmony_ci				PMIC_ARB_FEATURES_PERIPH_MASK;
154062306a36Sopenharmony_ci			pmic_arb->apid_count =
154162306a36Sopenharmony_ci				readl_relaxed(core + PMIC_ARB_FEATURES1) &
154262306a36Sopenharmony_ci				PMIC_ARB_FEATURES_PERIPH_MASK;
154362306a36Sopenharmony_ci		}
154462306a36Sopenharmony_ci
154562306a36Sopenharmony_ci		if (pmic_arb->base_apid + pmic_arb->apid_count > pmic_arb->max_periphs) {
154662306a36Sopenharmony_ci			err = -EINVAL;
154762306a36Sopenharmony_ci			dev_err(&pdev->dev, "Unsupported APID count %d detected\n",
154862306a36Sopenharmony_ci				pmic_arb->base_apid + pmic_arb->apid_count);
154962306a36Sopenharmony_ci			goto err_put_ctrl;
155062306a36Sopenharmony_ci		}
155162306a36Sopenharmony_ci	} else if (hw_ver >= PMIC_ARB_VERSION_V5_MIN) {
155262306a36Sopenharmony_ci		pmic_arb->base_apid = 0;
155362306a36Sopenharmony_ci		pmic_arb->apid_count = readl_relaxed(core + PMIC_ARB_FEATURES) &
155462306a36Sopenharmony_ci					PMIC_ARB_FEATURES_PERIPH_MASK;
155562306a36Sopenharmony_ci
155662306a36Sopenharmony_ci		if (pmic_arb->apid_count > pmic_arb->max_periphs) {
155762306a36Sopenharmony_ci			err = -EINVAL;
155862306a36Sopenharmony_ci			dev_err(&pdev->dev, "Unsupported APID count %d detected\n",
155962306a36Sopenharmony_ci				pmic_arb->apid_count);
156062306a36Sopenharmony_ci			goto err_put_ctrl;
156162306a36Sopenharmony_ci		}
156262306a36Sopenharmony_ci	}
156362306a36Sopenharmony_ci
156462306a36Sopenharmony_ci	pmic_arb->apid_data = devm_kcalloc(&ctrl->dev, pmic_arb->max_periphs,
156562306a36Sopenharmony_ci					   sizeof(*pmic_arb->apid_data),
156662306a36Sopenharmony_ci					   GFP_KERNEL);
156762306a36Sopenharmony_ci	if (!pmic_arb->apid_data) {
156862306a36Sopenharmony_ci		err = -ENOMEM;
156962306a36Sopenharmony_ci		goto err_put_ctrl;
157062306a36Sopenharmony_ci	}
157162306a36Sopenharmony_ci
157262306a36Sopenharmony_ci	dev_info(&ctrl->dev, "PMIC arbiter version %s (0x%x)\n",
157362306a36Sopenharmony_ci		 pmic_arb->ver_ops->ver_str, hw_ver);
157462306a36Sopenharmony_ci
157562306a36Sopenharmony_ci	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "intr");
157662306a36Sopenharmony_ci	pmic_arb->intr = devm_ioremap_resource(&ctrl->dev, res);
157762306a36Sopenharmony_ci	if (IS_ERR(pmic_arb->intr)) {
157862306a36Sopenharmony_ci		err = PTR_ERR(pmic_arb->intr);
157962306a36Sopenharmony_ci		goto err_put_ctrl;
158062306a36Sopenharmony_ci	}
158162306a36Sopenharmony_ci
158262306a36Sopenharmony_ci	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cnfg");
158362306a36Sopenharmony_ci	pmic_arb->cnfg = devm_ioremap_resource(&ctrl->dev, res);
158462306a36Sopenharmony_ci	if (IS_ERR(pmic_arb->cnfg)) {
158562306a36Sopenharmony_ci		err = PTR_ERR(pmic_arb->cnfg);
158662306a36Sopenharmony_ci		goto err_put_ctrl;
158762306a36Sopenharmony_ci	}
158862306a36Sopenharmony_ci
158962306a36Sopenharmony_ci	pmic_arb->irq = platform_get_irq_byname(pdev, "periph_irq");
159062306a36Sopenharmony_ci	if (pmic_arb->irq < 0) {
159162306a36Sopenharmony_ci		err = pmic_arb->irq;
159262306a36Sopenharmony_ci		goto err_put_ctrl;
159362306a36Sopenharmony_ci	}
159462306a36Sopenharmony_ci
159562306a36Sopenharmony_ci	err = of_property_read_u32(pdev->dev.of_node, "qcom,channel", &channel);
159662306a36Sopenharmony_ci	if (err) {
159762306a36Sopenharmony_ci		dev_err(&pdev->dev, "channel unspecified.\n");
159862306a36Sopenharmony_ci		goto err_put_ctrl;
159962306a36Sopenharmony_ci	}
160062306a36Sopenharmony_ci
160162306a36Sopenharmony_ci	if (channel > 5) {
160262306a36Sopenharmony_ci		dev_err(&pdev->dev, "invalid channel (%u) specified.\n",
160362306a36Sopenharmony_ci			channel);
160462306a36Sopenharmony_ci		err = -EINVAL;
160562306a36Sopenharmony_ci		goto err_put_ctrl;
160662306a36Sopenharmony_ci	}
160762306a36Sopenharmony_ci
160862306a36Sopenharmony_ci	pmic_arb->channel = channel;
160962306a36Sopenharmony_ci
161062306a36Sopenharmony_ci	err = of_property_read_u32(pdev->dev.of_node, "qcom,ee", &ee);
161162306a36Sopenharmony_ci	if (err) {
161262306a36Sopenharmony_ci		dev_err(&pdev->dev, "EE unspecified.\n");
161362306a36Sopenharmony_ci		goto err_put_ctrl;
161462306a36Sopenharmony_ci	}
161562306a36Sopenharmony_ci
161662306a36Sopenharmony_ci	if (ee > 5) {
161762306a36Sopenharmony_ci		dev_err(&pdev->dev, "invalid EE (%u) specified\n", ee);
161862306a36Sopenharmony_ci		err = -EINVAL;
161962306a36Sopenharmony_ci		goto err_put_ctrl;
162062306a36Sopenharmony_ci	}
162162306a36Sopenharmony_ci
162262306a36Sopenharmony_ci	pmic_arb->ee = ee;
162362306a36Sopenharmony_ci	mapping_table = devm_kcalloc(&ctrl->dev, pmic_arb->max_periphs,
162462306a36Sopenharmony_ci					sizeof(*mapping_table), GFP_KERNEL);
162562306a36Sopenharmony_ci	if (!mapping_table) {
162662306a36Sopenharmony_ci		err = -ENOMEM;
162762306a36Sopenharmony_ci		goto err_put_ctrl;
162862306a36Sopenharmony_ci	}
162962306a36Sopenharmony_ci
163062306a36Sopenharmony_ci	pmic_arb->mapping_table = mapping_table;
163162306a36Sopenharmony_ci	/* Initialize max_apid/min_apid to the opposite bounds, during
163262306a36Sopenharmony_ci	 * the irq domain translation, we are sure to update these */
163362306a36Sopenharmony_ci	pmic_arb->max_apid = 0;
163462306a36Sopenharmony_ci	pmic_arb->min_apid = pmic_arb->max_periphs - 1;
163562306a36Sopenharmony_ci
163662306a36Sopenharmony_ci	platform_set_drvdata(pdev, ctrl);
163762306a36Sopenharmony_ci	raw_spin_lock_init(&pmic_arb->lock);
163862306a36Sopenharmony_ci
163962306a36Sopenharmony_ci	ctrl->cmd = pmic_arb_cmd;
164062306a36Sopenharmony_ci	ctrl->read_cmd = pmic_arb_read_cmd;
164162306a36Sopenharmony_ci	ctrl->write_cmd = pmic_arb_write_cmd;
164262306a36Sopenharmony_ci
164362306a36Sopenharmony_ci	if (hw_ver >= PMIC_ARB_VERSION_V5_MIN) {
164462306a36Sopenharmony_ci		err = pmic_arb_read_apid_map_v5(pmic_arb);
164562306a36Sopenharmony_ci		if (err) {
164662306a36Sopenharmony_ci			dev_err(&pdev->dev, "could not read APID->PPID mapping table, rc= %d\n",
164762306a36Sopenharmony_ci				err);
164862306a36Sopenharmony_ci			goto err_put_ctrl;
164962306a36Sopenharmony_ci		}
165062306a36Sopenharmony_ci	}
165162306a36Sopenharmony_ci
165262306a36Sopenharmony_ci	dev_dbg(&pdev->dev, "adding irq domain\n");
165362306a36Sopenharmony_ci	pmic_arb->domain = irq_domain_add_tree(pdev->dev.of_node,
165462306a36Sopenharmony_ci					 &pmic_arb_irq_domain_ops, pmic_arb);
165562306a36Sopenharmony_ci	if (!pmic_arb->domain) {
165662306a36Sopenharmony_ci		dev_err(&pdev->dev, "unable to create irq_domain\n");
165762306a36Sopenharmony_ci		err = -ENOMEM;
165862306a36Sopenharmony_ci		goto err_put_ctrl;
165962306a36Sopenharmony_ci	}
166062306a36Sopenharmony_ci
166162306a36Sopenharmony_ci	irq_set_chained_handler_and_data(pmic_arb->irq, pmic_arb_chained_irq,
166262306a36Sopenharmony_ci					pmic_arb);
166362306a36Sopenharmony_ci	err = spmi_controller_add(ctrl);
166462306a36Sopenharmony_ci	if (err)
166562306a36Sopenharmony_ci		goto err_domain_remove;
166662306a36Sopenharmony_ci
166762306a36Sopenharmony_ci	return 0;
166862306a36Sopenharmony_ci
166962306a36Sopenharmony_cierr_domain_remove:
167062306a36Sopenharmony_ci	irq_set_chained_handler_and_data(pmic_arb->irq, NULL, NULL);
167162306a36Sopenharmony_ci	irq_domain_remove(pmic_arb->domain);
167262306a36Sopenharmony_cierr_put_ctrl:
167362306a36Sopenharmony_ci	spmi_controller_put(ctrl);
167462306a36Sopenharmony_ci	return err;
167562306a36Sopenharmony_ci}
167662306a36Sopenharmony_ci
167762306a36Sopenharmony_cistatic void spmi_pmic_arb_remove(struct platform_device *pdev)
167862306a36Sopenharmony_ci{
167962306a36Sopenharmony_ci	struct spmi_controller *ctrl = platform_get_drvdata(pdev);
168062306a36Sopenharmony_ci	struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl);
168162306a36Sopenharmony_ci	spmi_controller_remove(ctrl);
168262306a36Sopenharmony_ci	irq_set_chained_handler_and_data(pmic_arb->irq, NULL, NULL);
168362306a36Sopenharmony_ci	irq_domain_remove(pmic_arb->domain);
168462306a36Sopenharmony_ci	spmi_controller_put(ctrl);
168562306a36Sopenharmony_ci}
168662306a36Sopenharmony_ci
168762306a36Sopenharmony_cistatic const struct of_device_id spmi_pmic_arb_match_table[] = {
168862306a36Sopenharmony_ci	{ .compatible = "qcom,spmi-pmic-arb", },
168962306a36Sopenharmony_ci	{},
169062306a36Sopenharmony_ci};
169162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, spmi_pmic_arb_match_table);
169262306a36Sopenharmony_ci
169362306a36Sopenharmony_cistatic struct platform_driver spmi_pmic_arb_driver = {
169462306a36Sopenharmony_ci	.probe		= spmi_pmic_arb_probe,
169562306a36Sopenharmony_ci	.remove_new	= spmi_pmic_arb_remove,
169662306a36Sopenharmony_ci	.driver		= {
169762306a36Sopenharmony_ci		.name	= "spmi_pmic_arb",
169862306a36Sopenharmony_ci		.of_match_table = spmi_pmic_arb_match_table,
169962306a36Sopenharmony_ci	},
170062306a36Sopenharmony_ci};
170162306a36Sopenharmony_cimodule_platform_driver(spmi_pmic_arb_driver);
170262306a36Sopenharmony_ci
170362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
170462306a36Sopenharmony_ciMODULE_ALIAS("platform:spmi_pmic_arb");
1705