xref: /linux/drivers/spmi/spmi-pmic-arb.c (revision 03ab8e6297acd1bc0eedaa050e2a1635c576fd11)
197fb5e8dSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2d0c6ae41SGilad Avidov /*
3d19db80aSSubbaraman Narayanamurthy  * Copyright (c) 2012-2015, 2017, 2021, The Linux Foundation. All rights reserved.
439ae93e3SKenneth Heitke  */
5987a9f12SStephen Boyd #include <linux/bitmap.h>
639ae93e3SKenneth Heitke #include <linux/delay.h>
739ae93e3SKenneth Heitke #include <linux/err.h>
839ae93e3SKenneth Heitke #include <linux/interrupt.h>
939ae93e3SKenneth Heitke #include <linux/io.h>
1067b563f1SJosh Cartwright #include <linux/irqchip/chained_irq.h>
1167b563f1SJosh Cartwright #include <linux/irqdomain.h>
1267b563f1SJosh Cartwright #include <linux/irq.h>
1339ae93e3SKenneth Heitke #include <linux/kernel.h>
1439ae93e3SKenneth Heitke #include <linux/module.h>
1539ae93e3SKenneth Heitke #include <linux/of.h>
1639ae93e3SKenneth Heitke #include <linux/platform_device.h>
1739ae93e3SKenneth Heitke #include <linux/slab.h>
1839ae93e3SKenneth Heitke #include <linux/spmi.h>
1939ae93e3SKenneth Heitke 
2039ae93e3SKenneth Heitke /* PMIC Arbiter configuration registers */
2139ae93e3SKenneth Heitke #define PMIC_ARB_VERSION		0x0000
22d0c6ae41SGilad Avidov #define PMIC_ARB_VERSION_V2_MIN		0x20010000
23319f6884SAbhijeet Dharmapurikar #define PMIC_ARB_VERSION_V3_MIN		0x30000000
2440f318f0SDavid Collins #define PMIC_ARB_VERSION_V5_MIN		0x50000000
2539ae93e3SKenneth Heitke #define PMIC_ARB_INT_EN			0x0004
2639ae93e3SKenneth Heitke 
27d0c6ae41SGilad Avidov /* PMIC Arbiter channel registers offsets */
28d0c6ae41SGilad Avidov #define PMIC_ARB_CMD			0x00
29d0c6ae41SGilad Avidov #define PMIC_ARB_CONFIG			0x04
30d0c6ae41SGilad Avidov #define PMIC_ARB_STATUS			0x08
31d0c6ae41SGilad Avidov #define PMIC_ARB_WDATA0			0x10
32d0c6ae41SGilad Avidov #define PMIC_ARB_WDATA1			0x14
33d0c6ae41SGilad Avidov #define PMIC_ARB_RDATA0			0x18
34d0c6ae41SGilad Avidov #define PMIC_ARB_RDATA1			0x1C
3539ae93e3SKenneth Heitke 
3639ae93e3SKenneth Heitke /* Mapping Table */
3739ae93e3SKenneth Heitke #define SPMI_MAPPING_TABLE_REG(N)	(0x0B00 + (4 * (N)))
3839ae93e3SKenneth Heitke #define SPMI_MAPPING_BIT_INDEX(X)	(((X) >> 18) & 0xF)
3939ae93e3SKenneth Heitke #define SPMI_MAPPING_BIT_IS_0_FLAG(X)	(((X) >> 17) & 0x1)
4039ae93e3SKenneth Heitke #define SPMI_MAPPING_BIT_IS_0_RESULT(X)	(((X) >> 9) & 0xFF)
4139ae93e3SKenneth Heitke #define SPMI_MAPPING_BIT_IS_1_FLAG(X)	(((X) >> 8) & 0x1)
4239ae93e3SKenneth Heitke #define SPMI_MAPPING_BIT_IS_1_RESULT(X)	(((X) >> 0) & 0xFF)
4339ae93e3SKenneth Heitke 
4439ae93e3SKenneth Heitke #define SPMI_MAPPING_TABLE_TREE_DEPTH	16	/* Maximum of 16-bits */
45987a9f12SStephen Boyd #define PMIC_ARB_MAX_PPID		BIT(12) /* PPID is 12bit */
4602abec36SKiran Gunda #define PMIC_ARB_APID_VALID		BIT(15)
4740f318f0SDavid Collins #define PMIC_ARB_CHAN_IS_IRQ_OWNER(reg)	((reg) & BIT(24))
4840f318f0SDavid Collins #define INVALID_EE				0xFF
4939ae93e3SKenneth Heitke 
5039ae93e3SKenneth Heitke /* Ownership Table */
5139ae93e3SKenneth Heitke #define SPMI_OWNERSHIP_TABLE_REG(N)	(0x0700 + (4 * (N)))
5239ae93e3SKenneth Heitke #define SPMI_OWNERSHIP_PERIPH2OWNER(X)	((X) & 0x7)
5339ae93e3SKenneth Heitke 
5439ae93e3SKenneth Heitke /* Channel Status fields */
5539ae93e3SKenneth Heitke enum pmic_arb_chnl_status {
56111a10bfSAbhijeet Dharmapurikar 	PMIC_ARB_STATUS_DONE	= BIT(0),
57111a10bfSAbhijeet Dharmapurikar 	PMIC_ARB_STATUS_FAILURE	= BIT(1),
58111a10bfSAbhijeet Dharmapurikar 	PMIC_ARB_STATUS_DENIED	= BIT(2),
59111a10bfSAbhijeet Dharmapurikar 	PMIC_ARB_STATUS_DROPPED	= BIT(3),
6039ae93e3SKenneth Heitke };
6139ae93e3SKenneth Heitke 
6239ae93e3SKenneth Heitke /* Command register fields */
6339ae93e3SKenneth Heitke #define PMIC_ARB_CMD_MAX_BYTE_COUNT	8
6439ae93e3SKenneth Heitke 
6539ae93e3SKenneth Heitke /* Command Opcodes */
6639ae93e3SKenneth Heitke enum pmic_arb_cmd_op_code {
6739ae93e3SKenneth Heitke 	PMIC_ARB_OP_EXT_WRITEL = 0,
6839ae93e3SKenneth Heitke 	PMIC_ARB_OP_EXT_READL = 1,
6939ae93e3SKenneth Heitke 	PMIC_ARB_OP_EXT_WRITE = 2,
7039ae93e3SKenneth Heitke 	PMIC_ARB_OP_RESET = 3,
7139ae93e3SKenneth Heitke 	PMIC_ARB_OP_SLEEP = 4,
7239ae93e3SKenneth Heitke 	PMIC_ARB_OP_SHUTDOWN = 5,
7339ae93e3SKenneth Heitke 	PMIC_ARB_OP_WAKEUP = 6,
7439ae93e3SKenneth Heitke 	PMIC_ARB_OP_AUTHENTICATE = 7,
7539ae93e3SKenneth Heitke 	PMIC_ARB_OP_MSTR_READ = 8,
7639ae93e3SKenneth Heitke 	PMIC_ARB_OP_MSTR_WRITE = 9,
7739ae93e3SKenneth Heitke 	PMIC_ARB_OP_EXT_READ = 13,
7839ae93e3SKenneth Heitke 	PMIC_ARB_OP_WRITE = 14,
7939ae93e3SKenneth Heitke 	PMIC_ARB_OP_READ = 15,
8039ae93e3SKenneth Heitke 	PMIC_ARB_OP_ZERO_WRITE = 16,
8139ae93e3SKenneth Heitke };
8239ae93e3SKenneth Heitke 
8340f318f0SDavid Collins /*
8440f318f0SDavid Collins  * PMIC arbiter version 5 uses different register offsets for read/write vs
8540f318f0SDavid Collins  * observer channels.
8640f318f0SDavid Collins  */
8740f318f0SDavid Collins enum pmic_arb_channel {
8840f318f0SDavid Collins 	PMIC_ARB_CHANNEL_RW,
8940f318f0SDavid Collins 	PMIC_ARB_CHANNEL_OBS,
9040f318f0SDavid Collins };
9140f318f0SDavid Collins 
9239ae93e3SKenneth Heitke /* Maximum number of support PMIC peripherals */
93987a9f12SStephen Boyd #define PMIC_ARB_MAX_PERIPHS		512
9439ae93e3SKenneth Heitke #define PMIC_ARB_TIMEOUT_US		100
9539ae93e3SKenneth Heitke #define PMIC_ARB_MAX_TRANS_BYTES	(8)
9639ae93e3SKenneth Heitke 
9739ae93e3SKenneth Heitke #define PMIC_ARB_APID_MASK		0xFF
9839ae93e3SKenneth Heitke #define PMIC_ARB_PPID_MASK		0xFFF
9939ae93e3SKenneth Heitke 
10039ae93e3SKenneth Heitke /* interrupt enable bit */
10139ae93e3SKenneth Heitke #define SPMI_PIC_ACC_ENABLE_BIT		BIT(0)
10239ae93e3SKenneth Heitke 
10302abec36SKiran Gunda #define spec_to_hwirq(slave_id, periph_id, irq_id, apid) \
104319f6884SAbhijeet Dharmapurikar 	((((slave_id) & 0xF)   << 28) | \
105319f6884SAbhijeet Dharmapurikar 	(((periph_id) & 0xFF)  << 20) | \
106319f6884SAbhijeet Dharmapurikar 	(((irq_id)    & 0x7)   << 16) | \
107319f6884SAbhijeet Dharmapurikar 	(((apid)      & 0x1FF) << 0))
108319f6884SAbhijeet Dharmapurikar 
10902abec36SKiran Gunda #define hwirq_to_sid(hwirq)  (((hwirq) >> 28) & 0xF)
11002abec36SKiran Gunda #define hwirq_to_per(hwirq)  (((hwirq) >> 20) & 0xFF)
11102abec36SKiran Gunda #define hwirq_to_irq(hwirq)  (((hwirq) >> 16) & 0x7)
11202abec36SKiran Gunda #define hwirq_to_apid(hwirq) (((hwirq) >> 0)  & 0x1FF)
113319f6884SAbhijeet Dharmapurikar 
114d0c6ae41SGilad Avidov struct pmic_arb_ver_ops;
115d0c6ae41SGilad Avidov 
1166bc546e7SAbhijeet Dharmapurikar struct apid_data {
1176bc546e7SAbhijeet Dharmapurikar 	u16		ppid;
11840f318f0SDavid Collins 	u8		write_ee;
11940f318f0SDavid Collins 	u8		irq_ee;
1206bc546e7SAbhijeet Dharmapurikar };
1216bc546e7SAbhijeet Dharmapurikar 
12239ae93e3SKenneth Heitke /**
123111a10bfSAbhijeet Dharmapurikar  * spmi_pmic_arb - SPMI PMIC Arbiter object
12439ae93e3SKenneth Heitke  *
125d0c6ae41SGilad Avidov  * @rd_base:		on v1 "core", on v2 "observer" register base off DT.
126d0c6ae41SGilad Avidov  * @wr_base:		on v1 "core", on v2 "chnls"    register base off DT.
12739ae93e3SKenneth Heitke  * @intr:		address of the SPMI interrupt control registers.
12839ae93e3SKenneth Heitke  * @cnfg:		address of the PMIC Arbiter configuration registers.
12939ae93e3SKenneth Heitke  * @lock:		lock to synchronize accesses.
130d0c6ae41SGilad Avidov  * @channel:		execution environment channel to use for accesses.
13167b563f1SJosh Cartwright  * @irq:		PMIC ARB interrupt.
13267b563f1SJosh Cartwright  * @ee:			the current Execution Environment
13367b563f1SJosh Cartwright  * @min_apid:		minimum APID (used for bounding IRQ search)
13467b563f1SJosh Cartwright  * @max_apid:		maximum APID
13567b563f1SJosh Cartwright  * @mapping_table:	in-memory copy of PPID -> APID mapping table.
13667b563f1SJosh Cartwright  * @domain:		irq domain object for PMIC IRQ domain
13767b563f1SJosh Cartwright  * @spmic:		SPMI controller object
138d0c6ae41SGilad Avidov  * @ver_ops:		version dependent operations.
13902abec36SKiran Gunda  * @ppid_to_apid	in-memory copy of PPID -> APID mapping table.
14039ae93e3SKenneth Heitke  */
141111a10bfSAbhijeet Dharmapurikar struct spmi_pmic_arb {
142d0c6ae41SGilad Avidov 	void __iomem		*rd_base;
143d0c6ae41SGilad Avidov 	void __iomem		*wr_base;
14439ae93e3SKenneth Heitke 	void __iomem		*intr;
14539ae93e3SKenneth Heitke 	void __iomem		*cnfg;
146987a9f12SStephen Boyd 	void __iomem		*core;
147987a9f12SStephen Boyd 	resource_size_t		core_size;
14839ae93e3SKenneth Heitke 	raw_spinlock_t		lock;
14939ae93e3SKenneth Heitke 	u8			channel;
15067b563f1SJosh Cartwright 	int			irq;
15167b563f1SJosh Cartwright 	u8			ee;
152987a9f12SStephen Boyd 	u16			min_apid;
153987a9f12SStephen Boyd 	u16			max_apid;
154987a9f12SStephen Boyd 	u32			*mapping_table;
155987a9f12SStephen Boyd 	DECLARE_BITMAP(mapping_table_valid, PMIC_ARB_MAX_PERIPHS);
15667b563f1SJosh Cartwright 	struct irq_domain	*domain;
15767b563f1SJosh Cartwright 	struct spmi_controller	*spmic;
158d0c6ae41SGilad Avidov 	const struct pmic_arb_ver_ops *ver_ops;
1591ef1ce4eSAbhijeet Dharmapurikar 	u16			*ppid_to_apid;
1601ef1ce4eSAbhijeet Dharmapurikar 	u16			last_apid;
1616bc546e7SAbhijeet Dharmapurikar 	struct apid_data	apid_data[PMIC_ARB_MAX_PERIPHS];
162d0c6ae41SGilad Avidov };
163d0c6ae41SGilad Avidov 
164d0c6ae41SGilad Avidov /**
165d0c6ae41SGilad Avidov  * pmic_arb_ver: version dependent functionality.
166d0c6ae41SGilad Avidov  *
167319f6884SAbhijeet Dharmapurikar  * @ver_str:		version string.
168319f6884SAbhijeet Dharmapurikar  * @ppid_to_apid:	finds the apid for a given ppid.
169d0c6ae41SGilad Avidov  * @non_data_cmd:	on v1 issues an spmi non-data command.
170d0c6ae41SGilad Avidov  *			on v2 no HW support, returns -EOPNOTSUPP.
171d0c6ae41SGilad Avidov  * @offset:		on v1 offset of per-ee channel.
172d0c6ae41SGilad Avidov  *			on v2 offset of per-ee and per-ppid channel.
173d0c6ae41SGilad Avidov  * @fmt_cmd:		formats a GENI/SPMI command.
174e95d073cSKiran Gunda  * @owner_acc_status:	on v1 address of PMIC_ARB_SPMI_PIC_OWNERm_ACC_STATUSn
175e95d073cSKiran Gunda  *			on v2 address of SPMI_PIC_OWNERm_ACC_STATUSn.
176e95d073cSKiran Gunda  * @acc_enable:		on v1 address of PMIC_ARB_SPMI_PIC_ACC_ENABLEn
177e95d073cSKiran Gunda  *			on v2 address of SPMI_PIC_ACC_ENABLEn.
178e95d073cSKiran Gunda  * @irq_status:		on v1 address of PMIC_ARB_SPMI_PIC_IRQ_STATUSn
179e95d073cSKiran Gunda  *			on v2 address of SPMI_PIC_IRQ_STATUSn.
180e95d073cSKiran Gunda  * @irq_clear:		on v1 address of PMIC_ARB_SPMI_PIC_IRQ_CLEARn
181e95d073cSKiran Gunda  *			on v2 address of SPMI_PIC_IRQ_CLEARn.
18240f318f0SDavid Collins  * @apid_map_offset:	offset of PMIC_ARB_REG_CHNLn
183d0c6ae41SGilad Avidov  */
184d0c6ae41SGilad Avidov struct pmic_arb_ver_ops {
185319f6884SAbhijeet Dharmapurikar 	const char *ver_str;
186ff615ed9SKiran Gunda 	int (*ppid_to_apid)(struct spmi_pmic_arb *pmic_arb, u16 ppid);
187d0c6ae41SGilad Avidov 	/* spmi commands (read_cmd, write_cmd, cmd) functionality */
18840f318f0SDavid Collins 	int (*offset)(struct spmi_pmic_arb *pmic_arb, u8 sid, u16 addr,
18940f318f0SDavid Collins 			enum pmic_arb_channel ch_type);
190d0c6ae41SGilad Avidov 	u32 (*fmt_cmd)(u8 opc, u8 sid, u16 addr, u8 bc);
191d0c6ae41SGilad Avidov 	int (*non_data_cmd)(struct spmi_controller *ctrl, u8 opc, u8 sid);
192d0c6ae41SGilad Avidov 	/* Interrupts controller functionality (offset of PIC registers) */
193e95d073cSKiran Gunda 	void __iomem *(*owner_acc_status)(struct spmi_pmic_arb *pmic_arb, u8 m,
194e95d073cSKiran Gunda 					  u16 n);
195e95d073cSKiran Gunda 	void __iomem *(*acc_enable)(struct spmi_pmic_arb *pmic_arb, u16 n);
196e95d073cSKiran Gunda 	void __iomem *(*irq_status)(struct spmi_pmic_arb *pmic_arb, u16 n);
197e95d073cSKiran Gunda 	void __iomem *(*irq_clear)(struct spmi_pmic_arb *pmic_arb, u16 n);
19840f318f0SDavid Collins 	u32 (*apid_map_offset)(u16 n);
19939ae93e3SKenneth Heitke };
20039ae93e3SKenneth Heitke 
20102abec36SKiran Gunda static inline void pmic_arb_base_write(struct spmi_pmic_arb *pmic_arb,
20239ae93e3SKenneth Heitke 				       u32 offset, u32 val)
20339ae93e3SKenneth Heitke {
20402abec36SKiran Gunda 	writel_relaxed(val, pmic_arb->wr_base + offset);
205d0c6ae41SGilad Avidov }
206d0c6ae41SGilad Avidov 
20702abec36SKiran Gunda static inline void pmic_arb_set_rd_cmd(struct spmi_pmic_arb *pmic_arb,
208d0c6ae41SGilad Avidov 				       u32 offset, u32 val)
209d0c6ae41SGilad Avidov {
21002abec36SKiran Gunda 	writel_relaxed(val, pmic_arb->rd_base + offset);
21139ae93e3SKenneth Heitke }
21239ae93e3SKenneth Heitke 
21339ae93e3SKenneth Heitke /**
21402abec36SKiran Gunda  * pmic_arb_read_data: reads pmic-arb's register and copy 1..4 bytes to buf
21539ae93e3SKenneth Heitke  * @bc:		byte count -1. range: 0..3
21639ae93e3SKenneth Heitke  * @reg:	register's address
21739ae93e3SKenneth Heitke  * @buf:	output parameter, length must be bc + 1
21839ae93e3SKenneth Heitke  */
21902abec36SKiran Gunda static void
22002abec36SKiran Gunda pmic_arb_read_data(struct spmi_pmic_arb *pmic_arb, u8 *buf, u32 reg, u8 bc)
22139ae93e3SKenneth Heitke {
22202abec36SKiran Gunda 	u32 data = __raw_readl(pmic_arb->rd_base + reg);
223111a10bfSAbhijeet Dharmapurikar 
22439ae93e3SKenneth Heitke 	memcpy(buf, &data, (bc & 3) + 1);
22539ae93e3SKenneth Heitke }
22639ae93e3SKenneth Heitke 
22739ae93e3SKenneth Heitke /**
22802abec36SKiran Gunda  * pmic_arb_write_data: write 1..4 bytes from buf to pmic-arb's register
22939ae93e3SKenneth Heitke  * @bc:		byte-count -1. range: 0..3.
23039ae93e3SKenneth Heitke  * @reg:	register's address.
23139ae93e3SKenneth Heitke  * @buf:	buffer to write. length must be bc + 1.
23239ae93e3SKenneth Heitke  */
23302abec36SKiran Gunda static void pmic_arb_write_data(struct spmi_pmic_arb *pmic_arb, const u8 *buf,
23402abec36SKiran Gunda 				u32 reg, u8 bc)
23539ae93e3SKenneth Heitke {
23639ae93e3SKenneth Heitke 	u32 data = 0;
237111a10bfSAbhijeet Dharmapurikar 
23839ae93e3SKenneth Heitke 	memcpy(&data, buf, (bc & 3) + 1);
2399f7a9a44SKiran Gunda 	__raw_writel(data, pmic_arb->wr_base + reg);
24039ae93e3SKenneth Heitke }
24139ae93e3SKenneth Heitke 
242d0c6ae41SGilad Avidov static int pmic_arb_wait_for_done(struct spmi_controller *ctrl,
24340f318f0SDavid Collins 				  void __iomem *base, u8 sid, u16 addr,
24440f318f0SDavid Collins 				  enum pmic_arb_channel ch_type)
24539ae93e3SKenneth Heitke {
24602abec36SKiran Gunda 	struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl);
24739ae93e3SKenneth Heitke 	u32 status = 0;
24839ae93e3SKenneth Heitke 	u32 timeout = PMIC_ARB_TIMEOUT_US;
249987a9f12SStephen Boyd 	u32 offset;
250987a9f12SStephen Boyd 	int rc;
251987a9f12SStephen Boyd 
25240f318f0SDavid Collins 	rc = pmic_arb->ver_ops->offset(pmic_arb, sid, addr, ch_type);
253ff615ed9SKiran Gunda 	if (rc < 0)
254987a9f12SStephen Boyd 		return rc;
255987a9f12SStephen Boyd 
256ff615ed9SKiran Gunda 	offset = rc;
257987a9f12SStephen Boyd 	offset += PMIC_ARB_STATUS;
25839ae93e3SKenneth Heitke 
25939ae93e3SKenneth Heitke 	while (timeout--) {
260d0c6ae41SGilad Avidov 		status = readl_relaxed(base + offset);
26139ae93e3SKenneth Heitke 
26239ae93e3SKenneth Heitke 		if (status & PMIC_ARB_STATUS_DONE) {
26339ae93e3SKenneth Heitke 			if (status & PMIC_ARB_STATUS_DENIED) {
264b56ca501SStephen Boyd 				dev_err(&ctrl->dev, "%s: %#x %#x: transaction denied (%#x)\n",
265b56ca501SStephen Boyd 					__func__, sid, addr, status);
26639ae93e3SKenneth Heitke 				return -EPERM;
26739ae93e3SKenneth Heitke 			}
26839ae93e3SKenneth Heitke 
26939ae93e3SKenneth Heitke 			if (status & PMIC_ARB_STATUS_FAILURE) {
270b56ca501SStephen Boyd 				dev_err(&ctrl->dev, "%s: %#x %#x: transaction failed (%#x)\n",
271b56ca501SStephen Boyd 					__func__, sid, addr, status);
272b56ca501SStephen Boyd 				WARN_ON(1);
27339ae93e3SKenneth Heitke 				return -EIO;
27439ae93e3SKenneth Heitke 			}
27539ae93e3SKenneth Heitke 
27639ae93e3SKenneth Heitke 			if (status & PMIC_ARB_STATUS_DROPPED) {
277b56ca501SStephen Boyd 				dev_err(&ctrl->dev, "%s: %#x %#x: transaction dropped (%#x)\n",
278b56ca501SStephen Boyd 					__func__, sid, addr, status);
27939ae93e3SKenneth Heitke 				return -EIO;
28039ae93e3SKenneth Heitke 			}
28139ae93e3SKenneth Heitke 
28239ae93e3SKenneth Heitke 			return 0;
28339ae93e3SKenneth Heitke 		}
28439ae93e3SKenneth Heitke 		udelay(1);
28539ae93e3SKenneth Heitke 	}
28639ae93e3SKenneth Heitke 
287b56ca501SStephen Boyd 	dev_err(&ctrl->dev, "%s: %#x %#x: timeout, status %#x\n",
288b56ca501SStephen Boyd 		__func__, sid, addr, status);
28939ae93e3SKenneth Heitke 	return -ETIMEDOUT;
29039ae93e3SKenneth Heitke }
29139ae93e3SKenneth Heitke 
292d0c6ae41SGilad Avidov static int
293d0c6ae41SGilad Avidov pmic_arb_non_data_cmd_v1(struct spmi_controller *ctrl, u8 opc, u8 sid)
29439ae93e3SKenneth Heitke {
29502abec36SKiran Gunda 	struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl);
29639ae93e3SKenneth Heitke 	unsigned long flags;
29739ae93e3SKenneth Heitke 	u32 cmd;
29839ae93e3SKenneth Heitke 	int rc;
299987a9f12SStephen Boyd 	u32 offset;
300987a9f12SStephen Boyd 
30140f318f0SDavid Collins 	rc = pmic_arb->ver_ops->offset(pmic_arb, sid, 0, PMIC_ARB_CHANNEL_RW);
302ff615ed9SKiran Gunda 	if (rc < 0)
303987a9f12SStephen Boyd 		return rc;
304d0c6ae41SGilad Avidov 
305ff615ed9SKiran Gunda 	offset = rc;
306d0c6ae41SGilad Avidov 	cmd = ((opc | 0x40) << 27) | ((sid & 0xf) << 20);
307d0c6ae41SGilad Avidov 
30802abec36SKiran Gunda 	raw_spin_lock_irqsave(&pmic_arb->lock, flags);
30902abec36SKiran Gunda 	pmic_arb_base_write(pmic_arb, offset + PMIC_ARB_CMD, cmd);
31040f318f0SDavid Collins 	rc = pmic_arb_wait_for_done(ctrl, pmic_arb->wr_base, sid, 0,
31140f318f0SDavid Collins 				    PMIC_ARB_CHANNEL_RW);
31202abec36SKiran Gunda 	raw_spin_unlock_irqrestore(&pmic_arb->lock, flags);
313d0c6ae41SGilad Avidov 
314d0c6ae41SGilad Avidov 	return rc;
315d0c6ae41SGilad Avidov }
316d0c6ae41SGilad Avidov 
317d0c6ae41SGilad Avidov static int
318d0c6ae41SGilad Avidov pmic_arb_non_data_cmd_v2(struct spmi_controller *ctrl, u8 opc, u8 sid)
319d0c6ae41SGilad Avidov {
320d0c6ae41SGilad Avidov 	return -EOPNOTSUPP;
321d0c6ae41SGilad Avidov }
322d0c6ae41SGilad Avidov 
323d0c6ae41SGilad Avidov /* Non-data command */
324d0c6ae41SGilad Avidov static int pmic_arb_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid)
325d0c6ae41SGilad Avidov {
32602abec36SKiran Gunda 	struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl);
327d0c6ae41SGilad Avidov 
328d0c6ae41SGilad Avidov 	dev_dbg(&ctrl->dev, "cmd op:0x%x sid:%d\n", opc, sid);
32939ae93e3SKenneth Heitke 
33039ae93e3SKenneth Heitke 	/* Check for valid non-data command */
33139ae93e3SKenneth Heitke 	if (opc < SPMI_CMD_RESET || opc > SPMI_CMD_WAKEUP)
33239ae93e3SKenneth Heitke 		return -EINVAL;
33339ae93e3SKenneth Heitke 
33402abec36SKiran Gunda 	return pmic_arb->ver_ops->non_data_cmd(ctrl, opc, sid);
33539ae93e3SKenneth Heitke }
33639ae93e3SKenneth Heitke 
3371b18af40SDavid Collins static int pmic_arb_fmt_read_cmd(struct spmi_pmic_arb *pmic_arb, u8 opc, u8 sid,
3381b18af40SDavid Collins 				 u16 addr, size_t len, u32 *cmd, u32 *offset)
33939ae93e3SKenneth Heitke {
34039ae93e3SKenneth Heitke 	u8 bc = len - 1;
34139ae93e3SKenneth Heitke 	int rc;
342987a9f12SStephen Boyd 
34340f318f0SDavid Collins 	rc = pmic_arb->ver_ops->offset(pmic_arb, sid, addr,
34440f318f0SDavid Collins 				       PMIC_ARB_CHANNEL_OBS);
345ff615ed9SKiran Gunda 	if (rc < 0)
346987a9f12SStephen Boyd 		return rc;
34739ae93e3SKenneth Heitke 
3481b18af40SDavid Collins 	*offset = rc;
34939ae93e3SKenneth Heitke 	if (bc >= PMIC_ARB_MAX_TRANS_BYTES) {
3501b18af40SDavid Collins 		dev_err(&pmic_arb->spmic->dev, "pmic-arb supports 1..%d bytes per trans, but:%zu requested",
35139ae93e3SKenneth Heitke 			PMIC_ARB_MAX_TRANS_BYTES, len);
35239ae93e3SKenneth Heitke 		return  -EINVAL;
35339ae93e3SKenneth Heitke 	}
35439ae93e3SKenneth Heitke 
35539ae93e3SKenneth Heitke 	/* Check the opcode */
35639ae93e3SKenneth Heitke 	if (opc >= 0x60 && opc <= 0x7F)
35739ae93e3SKenneth Heitke 		opc = PMIC_ARB_OP_READ;
35839ae93e3SKenneth Heitke 	else if (opc >= 0x20 && opc <= 0x2F)
35939ae93e3SKenneth Heitke 		opc = PMIC_ARB_OP_EXT_READ;
36039ae93e3SKenneth Heitke 	else if (opc >= 0x38 && opc <= 0x3F)
36139ae93e3SKenneth Heitke 		opc = PMIC_ARB_OP_EXT_READL;
36239ae93e3SKenneth Heitke 	else
36339ae93e3SKenneth Heitke 		return -EINVAL;
36439ae93e3SKenneth Heitke 
3651b18af40SDavid Collins 	*cmd = pmic_arb->ver_ops->fmt_cmd(opc, sid, addr, bc);
36639ae93e3SKenneth Heitke 
3671b18af40SDavid Collins 	return 0;
3681b18af40SDavid Collins }
3691b18af40SDavid Collins 
3701b18af40SDavid Collins static int pmic_arb_read_cmd_unlocked(struct spmi_controller *ctrl, u32 cmd,
3711b18af40SDavid Collins 				      u32 offset, u8 sid, u16 addr, u8 *buf,
3721b18af40SDavid Collins 				      size_t len)
3731b18af40SDavid Collins {
3741b18af40SDavid Collins 	struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl);
3751b18af40SDavid Collins 	u8 bc = len - 1;
3761b18af40SDavid Collins 	int rc;
3771b18af40SDavid Collins 
37802abec36SKiran Gunda 	pmic_arb_set_rd_cmd(pmic_arb, offset + PMIC_ARB_CMD, cmd);
37940f318f0SDavid Collins 	rc = pmic_arb_wait_for_done(ctrl, pmic_arb->rd_base, sid, addr,
38040f318f0SDavid Collins 				    PMIC_ARB_CHANNEL_OBS);
38139ae93e3SKenneth Heitke 	if (rc)
3821b18af40SDavid Collins 		return rc;
38339ae93e3SKenneth Heitke 
38402abec36SKiran Gunda 	pmic_arb_read_data(pmic_arb, buf, offset + PMIC_ARB_RDATA0,
38539ae93e3SKenneth Heitke 		     min_t(u8, bc, 3));
38639ae93e3SKenneth Heitke 
38739ae93e3SKenneth Heitke 	if (bc > 3)
38802abec36SKiran Gunda 		pmic_arb_read_data(pmic_arb, buf + 4, offset + PMIC_ARB_RDATA1,
38902abec36SKiran Gunda 					bc - 4);
3901b18af40SDavid Collins 	return 0;
39139ae93e3SKenneth Heitke }
39239ae93e3SKenneth Heitke 
3931b18af40SDavid Collins static int pmic_arb_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
3941b18af40SDavid Collins 			     u16 addr, u8 *buf, size_t len)
39539ae93e3SKenneth Heitke {
39602abec36SKiran Gunda 	struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl);
39739ae93e3SKenneth Heitke 	unsigned long flags;
3981b18af40SDavid Collins 	u32 cmd, offset;
39939ae93e3SKenneth Heitke 	int rc;
4001b18af40SDavid Collins 
4011b18af40SDavid Collins 	rc = pmic_arb_fmt_read_cmd(pmic_arb, opc, sid, addr, len, &cmd,
4021b18af40SDavid Collins 				   &offset);
4031b18af40SDavid Collins 	if (rc)
4041b18af40SDavid Collins 		return rc;
4051b18af40SDavid Collins 
4061b18af40SDavid Collins 	raw_spin_lock_irqsave(&pmic_arb->lock, flags);
4071b18af40SDavid Collins 	rc = pmic_arb_read_cmd_unlocked(ctrl, cmd, offset, sid, addr, buf, len);
4081b18af40SDavid Collins 	raw_spin_unlock_irqrestore(&pmic_arb->lock, flags);
4091b18af40SDavid Collins 
4101b18af40SDavid Collins 	return rc;
4111b18af40SDavid Collins }
4121b18af40SDavid Collins 
4131b18af40SDavid Collins static int pmic_arb_fmt_write_cmd(struct spmi_pmic_arb *pmic_arb, u8 opc,
4141b18af40SDavid Collins 				  u8 sid, u16 addr, size_t len, u32 *cmd,
4151b18af40SDavid Collins 				  u32 *offset)
4161b18af40SDavid Collins {
4171b18af40SDavid Collins 	u8 bc = len - 1;
4181b18af40SDavid Collins 	int rc;
419987a9f12SStephen Boyd 
42040f318f0SDavid Collins 	rc = pmic_arb->ver_ops->offset(pmic_arb, sid, addr,
42140f318f0SDavid Collins 					PMIC_ARB_CHANNEL_RW);
422ff615ed9SKiran Gunda 	if (rc < 0)
423987a9f12SStephen Boyd 		return rc;
42439ae93e3SKenneth Heitke 
4251b18af40SDavid Collins 	*offset = rc;
42639ae93e3SKenneth Heitke 	if (bc >= PMIC_ARB_MAX_TRANS_BYTES) {
4271b18af40SDavid Collins 		dev_err(&pmic_arb->spmic->dev, "pmic-arb supports 1..%d bytes per trans, but:%zu requested",
42839ae93e3SKenneth Heitke 			PMIC_ARB_MAX_TRANS_BYTES, len);
42939ae93e3SKenneth Heitke 		return  -EINVAL;
43039ae93e3SKenneth Heitke 	}
43139ae93e3SKenneth Heitke 
43239ae93e3SKenneth Heitke 	/* Check the opcode */
43339ae93e3SKenneth Heitke 	if (opc >= 0x40 && opc <= 0x5F)
43439ae93e3SKenneth Heitke 		opc = PMIC_ARB_OP_WRITE;
43553d296b5SFenglin Wu 	else if (opc <= 0x0F)
43639ae93e3SKenneth Heitke 		opc = PMIC_ARB_OP_EXT_WRITE;
43739ae93e3SKenneth Heitke 	else if (opc >= 0x30 && opc <= 0x37)
43839ae93e3SKenneth Heitke 		opc = PMIC_ARB_OP_EXT_WRITEL;
4399b76968dSStephen Boyd 	else if (opc >= 0x80)
44039ae93e3SKenneth Heitke 		opc = PMIC_ARB_OP_ZERO_WRITE;
44139ae93e3SKenneth Heitke 	else
44239ae93e3SKenneth Heitke 		return -EINVAL;
44339ae93e3SKenneth Heitke 
4441b18af40SDavid Collins 	*cmd = pmic_arb->ver_ops->fmt_cmd(opc, sid, addr, bc);
4451b18af40SDavid Collins 
4461b18af40SDavid Collins 	return 0;
4471b18af40SDavid Collins }
4481b18af40SDavid Collins 
4491b18af40SDavid Collins static int pmic_arb_write_cmd_unlocked(struct spmi_controller *ctrl, u32 cmd,
4501b18af40SDavid Collins 				      u32 offset, u8 sid, u16 addr,
4511b18af40SDavid Collins 				      const u8 *buf, size_t len)
4521b18af40SDavid Collins {
4531b18af40SDavid Collins 	struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl);
4541b18af40SDavid Collins 	u8 bc = len - 1;
45539ae93e3SKenneth Heitke 
45639ae93e3SKenneth Heitke 	/* Write data to FIFOs */
45702abec36SKiran Gunda 	pmic_arb_write_data(pmic_arb, buf, offset + PMIC_ARB_WDATA0,
45802abec36SKiran Gunda 				min_t(u8, bc, 3));
45939ae93e3SKenneth Heitke 	if (bc > 3)
46002abec36SKiran Gunda 		pmic_arb_write_data(pmic_arb, buf + 4, offset + PMIC_ARB_WDATA1,
46102abec36SKiran Gunda 					bc - 4);
46239ae93e3SKenneth Heitke 
46339ae93e3SKenneth Heitke 	/* Start the transaction */
46402abec36SKiran Gunda 	pmic_arb_base_write(pmic_arb, offset + PMIC_ARB_CMD, cmd);
4651b18af40SDavid Collins 	return pmic_arb_wait_for_done(ctrl, pmic_arb->wr_base, sid, addr,
46640f318f0SDavid Collins 				      PMIC_ARB_CHANNEL_RW);
4671b18af40SDavid Collins }
4681b18af40SDavid Collins 
4691b18af40SDavid Collins static int pmic_arb_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
4701b18af40SDavid Collins 			      u16 addr, const u8 *buf, size_t len)
4711b18af40SDavid Collins {
4721b18af40SDavid Collins 	struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl);
4731b18af40SDavid Collins 	unsigned long flags;
4741b18af40SDavid Collins 	u32 cmd, offset;
4751b18af40SDavid Collins 	int rc;
4761b18af40SDavid Collins 
4771b18af40SDavid Collins 	rc = pmic_arb_fmt_write_cmd(pmic_arb, opc, sid, addr, len, &cmd,
4781b18af40SDavid Collins 				    &offset);
4791b18af40SDavid Collins 	if (rc)
4801b18af40SDavid Collins 		return rc;
4811b18af40SDavid Collins 
4821b18af40SDavid Collins 	raw_spin_lock_irqsave(&pmic_arb->lock, flags);
4831b18af40SDavid Collins 	rc = pmic_arb_write_cmd_unlocked(ctrl, cmd, offset, sid, addr, buf,
4841b18af40SDavid Collins 					 len);
4851b18af40SDavid Collins 	raw_spin_unlock_irqrestore(&pmic_arb->lock, flags);
4861b18af40SDavid Collins 
4871b18af40SDavid Collins 	return rc;
4881b18af40SDavid Collins }
4891b18af40SDavid Collins 
4901b18af40SDavid Collins static int pmic_arb_masked_write(struct spmi_controller *ctrl, u8 sid, u16 addr,
4911b18af40SDavid Collins 				 const u8 *buf, const u8 *mask, size_t len)
4921b18af40SDavid Collins {
4931b18af40SDavid Collins 	struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl);
4941b18af40SDavid Collins 	u32 read_cmd, read_offset, write_cmd, write_offset;
4951b18af40SDavid Collins 	u8 temp[PMIC_ARB_MAX_TRANS_BYTES];
4961b18af40SDavid Collins 	unsigned long flags;
4971b18af40SDavid Collins 	int rc, i;
4981b18af40SDavid Collins 
4991b18af40SDavid Collins 	rc = pmic_arb_fmt_read_cmd(pmic_arb, SPMI_CMD_EXT_READL, sid, addr, len,
5001b18af40SDavid Collins 				   &read_cmd, &read_offset);
5011b18af40SDavid Collins 	if (rc)
5021b18af40SDavid Collins 		return rc;
5031b18af40SDavid Collins 
5041b18af40SDavid Collins 	rc = pmic_arb_fmt_write_cmd(pmic_arb, SPMI_CMD_EXT_WRITEL, sid, addr,
5051b18af40SDavid Collins 				    len, &write_cmd, &write_offset);
5061b18af40SDavid Collins 	if (rc)
5071b18af40SDavid Collins 		return rc;
5081b18af40SDavid Collins 
5091b18af40SDavid Collins 	raw_spin_lock_irqsave(&pmic_arb->lock, flags);
5101b18af40SDavid Collins 	rc = pmic_arb_read_cmd_unlocked(ctrl, read_cmd, read_offset, sid, addr,
5111b18af40SDavid Collins 					temp, len);
5121b18af40SDavid Collins 	if (rc)
5131b18af40SDavid Collins 		goto done;
5141b18af40SDavid Collins 
5151b18af40SDavid Collins 	for (i = 0; i < len; i++)
5161b18af40SDavid Collins 		temp[i] = (temp[i] & ~mask[i]) | (buf[i] & mask[i]);
5171b18af40SDavid Collins 
5181b18af40SDavid Collins 	rc = pmic_arb_write_cmd_unlocked(ctrl, write_cmd, write_offset, sid,
5191b18af40SDavid Collins 					 addr, temp, len);
5201b18af40SDavid Collins done:
52102abec36SKiran Gunda 	raw_spin_unlock_irqrestore(&pmic_arb->lock, flags);
52239ae93e3SKenneth Heitke 
52339ae93e3SKenneth Heitke 	return rc;
52439ae93e3SKenneth Heitke }
52539ae93e3SKenneth Heitke 
52667b563f1SJosh Cartwright enum qpnpint_regs {
52767b563f1SJosh Cartwright 	QPNPINT_REG_RT_STS		= 0x10,
52867b563f1SJosh Cartwright 	QPNPINT_REG_SET_TYPE		= 0x11,
52967b563f1SJosh Cartwright 	QPNPINT_REG_POLARITY_HIGH	= 0x12,
53067b563f1SJosh Cartwright 	QPNPINT_REG_POLARITY_LOW	= 0x13,
53167b563f1SJosh Cartwright 	QPNPINT_REG_LATCHED_CLR		= 0x14,
53267b563f1SJosh Cartwright 	QPNPINT_REG_EN_SET		= 0x15,
53367b563f1SJosh Cartwright 	QPNPINT_REG_EN_CLR		= 0x16,
53467b563f1SJosh Cartwright 	QPNPINT_REG_LATCHED_STS		= 0x18,
53567b563f1SJosh Cartwright };
53667b563f1SJosh Cartwright 
53767b563f1SJosh Cartwright struct spmi_pmic_arb_qpnpint_type {
53867b563f1SJosh Cartwright 	u8 type; /* 1 -> edge */
53967b563f1SJosh Cartwright 	u8 polarity_high;
54067b563f1SJosh Cartwright 	u8 polarity_low;
54167b563f1SJosh Cartwright } __packed;
54267b563f1SJosh Cartwright 
54367b563f1SJosh Cartwright /* Simplified accessor functions for irqchip callbacks */
54467b563f1SJosh Cartwright static void qpnpint_spmi_write(struct irq_data *d, u8 reg, void *buf,
54567b563f1SJosh Cartwright 			       size_t len)
54667b563f1SJosh Cartwright {
54702abec36SKiran Gunda 	struct spmi_pmic_arb *pmic_arb = irq_data_get_irq_chip_data(d);
54802abec36SKiran Gunda 	u8 sid = hwirq_to_sid(d->hwirq);
54902abec36SKiran Gunda 	u8 per = hwirq_to_per(d->hwirq);
55067b563f1SJosh Cartwright 
55102abec36SKiran Gunda 	if (pmic_arb_write_cmd(pmic_arb->spmic, SPMI_CMD_EXT_WRITEL, sid,
55267b563f1SJosh Cartwright 			       (per << 8) + reg, buf, len))
55302abec36SKiran Gunda 		dev_err_ratelimited(&pmic_arb->spmic->dev, "failed irqchip transaction on %x\n",
55467b563f1SJosh Cartwright 				    d->irq);
55567b563f1SJosh Cartwright }
55667b563f1SJosh Cartwright 
55767b563f1SJosh Cartwright static void qpnpint_spmi_read(struct irq_data *d, u8 reg, void *buf, size_t len)
55867b563f1SJosh Cartwright {
55902abec36SKiran Gunda 	struct spmi_pmic_arb *pmic_arb = irq_data_get_irq_chip_data(d);
56002abec36SKiran Gunda 	u8 sid = hwirq_to_sid(d->hwirq);
56102abec36SKiran Gunda 	u8 per = hwirq_to_per(d->hwirq);
56267b563f1SJosh Cartwright 
56302abec36SKiran Gunda 	if (pmic_arb_read_cmd(pmic_arb->spmic, SPMI_CMD_EXT_READL, sid,
56467b563f1SJosh Cartwright 			      (per << 8) + reg, buf, len))
56502abec36SKiran Gunda 		dev_err_ratelimited(&pmic_arb->spmic->dev, "failed irqchip transaction on %x\n",
56667b563f1SJosh Cartwright 				    d->irq);
56767b563f1SJosh Cartwright }
56867b563f1SJosh Cartwright 
5691b18af40SDavid Collins static int qpnpint_spmi_masked_write(struct irq_data *d, u8 reg,
5701b18af40SDavid Collins 				     const void *buf, const void *mask,
5711b18af40SDavid Collins 				     size_t len)
5721b18af40SDavid Collins {
5731b18af40SDavid Collins 	struct spmi_pmic_arb *pmic_arb = irq_data_get_irq_chip_data(d);
5741b18af40SDavid Collins 	u8 sid = hwirq_to_sid(d->hwirq);
5751b18af40SDavid Collins 	u8 per = hwirq_to_per(d->hwirq);
5761b18af40SDavid Collins 	int rc;
5771b18af40SDavid Collins 
5781b18af40SDavid Collins 	rc = pmic_arb_masked_write(pmic_arb->spmic, sid, (per << 8) + reg, buf,
5791b18af40SDavid Collins 				   mask, len);
5801b18af40SDavid Collins 	if (rc)
5811b18af40SDavid Collins 		dev_err_ratelimited(&pmic_arb->spmic->dev, "failed irqchip transaction on %x rc=%d\n",
5821b18af40SDavid Collins 				    d->irq, rc);
5831b18af40SDavid Collins 	return rc;
5841b18af40SDavid Collins }
5851b18af40SDavid Collins 
58602abec36SKiran Gunda static void cleanup_irq(struct spmi_pmic_arb *pmic_arb, u16 apid, int id)
5876bc546e7SAbhijeet Dharmapurikar {
58802abec36SKiran Gunda 	u16 ppid = pmic_arb->apid_data[apid].ppid;
5896bc546e7SAbhijeet Dharmapurikar 	u8 sid = ppid >> 8;
5906bc546e7SAbhijeet Dharmapurikar 	u8 per = ppid & 0xFF;
5916bc546e7SAbhijeet Dharmapurikar 	u8 irq_mask = BIT(id);
5926bc546e7SAbhijeet Dharmapurikar 
593e95d073cSKiran Gunda 	writel_relaxed(irq_mask, pmic_arb->ver_ops->irq_clear(pmic_arb, apid));
5946bc546e7SAbhijeet Dharmapurikar 
59502abec36SKiran Gunda 	if (pmic_arb_write_cmd(pmic_arb->spmic, SPMI_CMD_EXT_WRITEL, sid,
5966bc546e7SAbhijeet Dharmapurikar 			(per << 8) + QPNPINT_REG_LATCHED_CLR, &irq_mask, 1))
59702abec36SKiran Gunda 		dev_err_ratelimited(&pmic_arb->spmic->dev, "failed to ack irq_mask = 0x%x for ppid = %x\n",
5986bc546e7SAbhijeet Dharmapurikar 				irq_mask, ppid);
5996bc546e7SAbhijeet Dharmapurikar 
60002abec36SKiran Gunda 	if (pmic_arb_write_cmd(pmic_arb->spmic, SPMI_CMD_EXT_WRITEL, sid,
6016bc546e7SAbhijeet Dharmapurikar 			       (per << 8) + QPNPINT_REG_EN_CLR, &irq_mask, 1))
60202abec36SKiran Gunda 		dev_err_ratelimited(&pmic_arb->spmic->dev, "failed to ack irq_mask = 0x%x for ppid = %x\n",
6036bc546e7SAbhijeet Dharmapurikar 				irq_mask, ppid);
6046bc546e7SAbhijeet Dharmapurikar }
6056bc546e7SAbhijeet Dharmapurikar 
60602abec36SKiran Gunda static void periph_interrupt(struct spmi_pmic_arb *pmic_arb, u16 apid)
60767b563f1SJosh Cartwright {
60867b563f1SJosh Cartwright 	unsigned int irq;
609d19db80aSSubbaraman Narayanamurthy 	u32 status, id;
61002abec36SKiran Gunda 	u8 sid = (pmic_arb->apid_data[apid].ppid >> 8) & 0xF;
61102abec36SKiran Gunda 	u8 per = pmic_arb->apid_data[apid].ppid & 0xFF;
61267b563f1SJosh Cartwright 
613e95d073cSKiran Gunda 	status = readl_relaxed(pmic_arb->ver_ops->irq_status(pmic_arb, apid));
61467b563f1SJosh Cartwright 	while (status) {
61567b563f1SJosh Cartwright 		id = ffs(status) - 1;
616111a10bfSAbhijeet Dharmapurikar 		status &= ~BIT(id);
61702abec36SKiran Gunda 		irq = irq_find_mapping(pmic_arb->domain,
61802abec36SKiran Gunda 					spec_to_hwirq(sid, per, id, apid));
6196bc546e7SAbhijeet Dharmapurikar 		if (irq == 0) {
62002abec36SKiran Gunda 			cleanup_irq(pmic_arb, apid, id);
6216bc546e7SAbhijeet Dharmapurikar 			continue;
6226bc546e7SAbhijeet Dharmapurikar 		}
62367b563f1SJosh Cartwright 		generic_handle_irq(irq);
62467b563f1SJosh Cartwright 	}
62567b563f1SJosh Cartwright }
62667b563f1SJosh Cartwright 
627bd0b9ac4SThomas Gleixner static void pmic_arb_chained_irq(struct irq_desc *desc)
62867b563f1SJosh Cartwright {
62902abec36SKiran Gunda 	struct spmi_pmic_arb *pmic_arb = irq_desc_get_handler_data(desc);
630e95d073cSKiran Gunda 	const struct pmic_arb_ver_ops *ver_ops = pmic_arb->ver_ops;
6317fe88f3cSJiang Liu 	struct irq_chip *chip = irq_desc_get_chip(desc);
63202abec36SKiran Gunda 	int first = pmic_arb->min_apid >> 5;
63302abec36SKiran Gunda 	int last = pmic_arb->max_apid >> 5;
634e95d073cSKiran Gunda 	u8 ee = pmic_arb->ee;
635472eaf8bSAbhijeet Dharmapurikar 	u32 status, enable;
636472eaf8bSAbhijeet Dharmapurikar 	int i, id, apid;
63767b563f1SJosh Cartwright 
63867b563f1SJosh Cartwright 	chained_irq_enter(chip, desc);
63967b563f1SJosh Cartwright 
64067b563f1SJosh Cartwright 	for (i = first; i <= last; ++i) {
641e95d073cSKiran Gunda 		status = readl_relaxed(
642e95d073cSKiran Gunda 				ver_ops->owner_acc_status(pmic_arb, ee, i));
64367b563f1SJosh Cartwright 		while (status) {
64467b563f1SJosh Cartwright 			id = ffs(status) - 1;
645111a10bfSAbhijeet Dharmapurikar 			status &= ~BIT(id);
646472eaf8bSAbhijeet Dharmapurikar 			apid = id + i * 32;
647e95d073cSKiran Gunda 			enable = readl_relaxed(
648e95d073cSKiran Gunda 					ver_ops->acc_enable(pmic_arb, apid));
649472eaf8bSAbhijeet Dharmapurikar 			if (enable & SPMI_PIC_ACC_ENABLE_BIT)
65002abec36SKiran Gunda 				periph_interrupt(pmic_arb, apid);
65167b563f1SJosh Cartwright 		}
65267b563f1SJosh Cartwright 	}
65367b563f1SJosh Cartwright 
65467b563f1SJosh Cartwright 	chained_irq_exit(chip, desc);
65567b563f1SJosh Cartwright }
65667b563f1SJosh Cartwright 
65767b563f1SJosh Cartwright static void qpnpint_irq_ack(struct irq_data *d)
65867b563f1SJosh Cartwright {
65902abec36SKiran Gunda 	struct spmi_pmic_arb *pmic_arb = irq_data_get_irq_chip_data(d);
66002abec36SKiran Gunda 	u8 irq = hwirq_to_irq(d->hwirq);
66102abec36SKiran Gunda 	u16 apid = hwirq_to_apid(d->hwirq);
66267b563f1SJosh Cartwright 	u8 data;
66367b563f1SJosh Cartwright 
664e95d073cSKiran Gunda 	writel_relaxed(BIT(irq), pmic_arb->ver_ops->irq_clear(pmic_arb, apid));
66567b563f1SJosh Cartwright 
666111a10bfSAbhijeet Dharmapurikar 	data = BIT(irq);
66767b563f1SJosh Cartwright 	qpnpint_spmi_write(d, QPNPINT_REG_LATCHED_CLR, &data, 1);
66867b563f1SJosh Cartwright }
66967b563f1SJosh Cartwright 
67067b563f1SJosh Cartwright static void qpnpint_irq_mask(struct irq_data *d)
67167b563f1SJosh Cartwright {
67202abec36SKiran Gunda 	u8 irq = hwirq_to_irq(d->hwirq);
6736bc546e7SAbhijeet Dharmapurikar 	u8 data = BIT(irq);
67467b563f1SJosh Cartwright 
67567b563f1SJosh Cartwright 	qpnpint_spmi_write(d, QPNPINT_REG_EN_CLR, &data, 1);
67667b563f1SJosh Cartwright }
67767b563f1SJosh Cartwright 
67867b563f1SJosh Cartwright static void qpnpint_irq_unmask(struct irq_data *d)
67967b563f1SJosh Cartwright {
68002abec36SKiran Gunda 	struct spmi_pmic_arb *pmic_arb = irq_data_get_irq_chip_data(d);
681e95d073cSKiran Gunda 	const struct pmic_arb_ver_ops *ver_ops = pmic_arb->ver_ops;
68202abec36SKiran Gunda 	u8 irq = hwirq_to_irq(d->hwirq);
68302abec36SKiran Gunda 	u16 apid = hwirq_to_apid(d->hwirq);
684cee0fad7SAbhijeet Dharmapurikar 	u8 buf[2];
68567b563f1SJosh Cartwright 
6866bc546e7SAbhijeet Dharmapurikar 	writel_relaxed(SPMI_PIC_ACC_ENABLE_BIT,
687e95d073cSKiran Gunda 			ver_ops->acc_enable(pmic_arb, apid));
68867b563f1SJosh Cartwright 
689cee0fad7SAbhijeet Dharmapurikar 	qpnpint_spmi_read(d, QPNPINT_REG_EN_SET, &buf[0], 1);
690cee0fad7SAbhijeet Dharmapurikar 	if (!(buf[0] & BIT(irq))) {
691cee0fad7SAbhijeet Dharmapurikar 		/*
692cee0fad7SAbhijeet Dharmapurikar 		 * Since the interrupt is currently disabled, write to both the
693cee0fad7SAbhijeet Dharmapurikar 		 * LATCHED_CLR and EN_SET registers so that a spurious interrupt
694cee0fad7SAbhijeet Dharmapurikar 		 * cannot be triggered when the interrupt is enabled
695cee0fad7SAbhijeet Dharmapurikar 		 */
696cee0fad7SAbhijeet Dharmapurikar 		buf[0] = BIT(irq);
697cee0fad7SAbhijeet Dharmapurikar 		buf[1] = BIT(irq);
698cee0fad7SAbhijeet Dharmapurikar 		qpnpint_spmi_write(d, QPNPINT_REG_LATCHED_CLR, &buf, 2);
699cee0fad7SAbhijeet Dharmapurikar 	}
70067b563f1SJosh Cartwright }
70167b563f1SJosh Cartwright 
70267b563f1SJosh Cartwright static int qpnpint_irq_set_type(struct irq_data *d, unsigned int flow_type)
70367b563f1SJosh Cartwright {
7041b18af40SDavid Collins 	struct spmi_pmic_arb_qpnpint_type type = {0};
7051b18af40SDavid Collins 	struct spmi_pmic_arb_qpnpint_type mask;
706325255bcSKiran Gunda 	irq_flow_handler_t flow_handler;
7071b18af40SDavid Collins 	u8 irq_bit = BIT(hwirq_to_irq(d->hwirq));
7081b18af40SDavid Collins 	int rc;
70967b563f1SJosh Cartwright 
71067b563f1SJosh Cartwright 	if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) {
7111b18af40SDavid Collins 		type.type = irq_bit;
71267b563f1SJosh Cartwright 		if (flow_type & IRQF_TRIGGER_RISING)
7131b18af40SDavid Collins 			type.polarity_high = irq_bit;
71467b563f1SJosh Cartwright 		if (flow_type & IRQF_TRIGGER_FALLING)
7151b18af40SDavid Collins 			type.polarity_low = irq_bit;
716325255bcSKiran Gunda 
717325255bcSKiran Gunda 		flow_handler = handle_edge_irq;
71867b563f1SJosh Cartwright 	} else {
71967b563f1SJosh Cartwright 		if ((flow_type & (IRQF_TRIGGER_HIGH)) &&
72067b563f1SJosh Cartwright 		    (flow_type & (IRQF_TRIGGER_LOW)))
72167b563f1SJosh Cartwright 			return -EINVAL;
72267b563f1SJosh Cartwright 
72367b563f1SJosh Cartwright 		if (flow_type & IRQF_TRIGGER_HIGH)
7241b18af40SDavid Collins 			type.polarity_high = irq_bit;
72567b563f1SJosh Cartwright 		else
7261b18af40SDavid Collins 			type.polarity_low = irq_bit;
727325255bcSKiran Gunda 
728325255bcSKiran Gunda 		flow_handler = handle_level_irq;
72967b563f1SJosh Cartwright 	}
73067b563f1SJosh Cartwright 
7311b18af40SDavid Collins 	mask.type = irq_bit;
7321b18af40SDavid Collins 	mask.polarity_high = irq_bit;
7331b18af40SDavid Collins 	mask.polarity_low = irq_bit;
7341b18af40SDavid Collins 
7351b18af40SDavid Collins 	rc = qpnpint_spmi_masked_write(d, QPNPINT_REG_SET_TYPE, &type, &mask,
7361b18af40SDavid Collins 				       sizeof(type));
737325255bcSKiran Gunda 	irq_set_handler_locked(d, flow_handler);
7385f9b2ea3SAbhijeet Dharmapurikar 
7391b18af40SDavid Collins 	return rc;
74067b563f1SJosh Cartwright }
74167b563f1SJosh Cartwright 
742cdeef07aSKiran Gunda static int qpnpint_irq_set_wake(struct irq_data *d, unsigned int on)
743cdeef07aSKiran Gunda {
744cdeef07aSKiran Gunda 	struct spmi_pmic_arb *pmic_arb = irq_data_get_irq_chip_data(d);
745cdeef07aSKiran Gunda 
746cdeef07aSKiran Gunda 	return irq_set_irq_wake(pmic_arb->irq, on);
747cdeef07aSKiran Gunda }
748cdeef07aSKiran Gunda 
74960be4230SCourtney Cavin static int qpnpint_get_irqchip_state(struct irq_data *d,
75060be4230SCourtney Cavin 				     enum irqchip_irq_state which,
75160be4230SCourtney Cavin 				     bool *state)
75260be4230SCourtney Cavin {
75302abec36SKiran Gunda 	u8 irq = hwirq_to_irq(d->hwirq);
75460be4230SCourtney Cavin 	u8 status = 0;
75560be4230SCourtney Cavin 
75660be4230SCourtney Cavin 	if (which != IRQCHIP_STATE_LINE_LEVEL)
75760be4230SCourtney Cavin 		return -EINVAL;
75860be4230SCourtney Cavin 
75960be4230SCourtney Cavin 	qpnpint_spmi_read(d, QPNPINT_REG_RT_STS, &status, 1);
76060be4230SCourtney Cavin 	*state = !!(status & BIT(irq));
76160be4230SCourtney Cavin 
76260be4230SCourtney Cavin 	return 0;
76360be4230SCourtney Cavin }
76460be4230SCourtney Cavin 
76512a9eeaeSBrian Masney static int qpnpint_irq_domain_activate(struct irq_domain *domain,
76612a9eeaeSBrian Masney 				       struct irq_data *d, bool reserve)
7672fb4f258SKiran Gunda {
7682fb4f258SKiran Gunda 	struct spmi_pmic_arb *pmic_arb = irq_data_get_irq_chip_data(d);
7692fb4f258SKiran Gunda 	u16 periph = hwirq_to_per(d->hwirq);
7702fb4f258SKiran Gunda 	u16 apid = hwirq_to_apid(d->hwirq);
7712fb4f258SKiran Gunda 	u16 sid = hwirq_to_sid(d->hwirq);
7722fb4f258SKiran Gunda 	u16 irq = hwirq_to_irq(d->hwirq);
7732fb4f258SKiran Gunda 
7742fb4f258SKiran Gunda 	if (pmic_arb->apid_data[apid].irq_ee != pmic_arb->ee) {
7752fb4f258SKiran Gunda 		dev_err(&pmic_arb->spmic->dev, "failed to xlate sid = %#x, periph = %#x, irq = %u: ee=%u but owner=%u\n",
7762fb4f258SKiran Gunda 			sid, periph, irq, pmic_arb->ee,
7772fb4f258SKiran Gunda 			pmic_arb->apid_data[apid].irq_ee);
7782fb4f258SKiran Gunda 		return -ENODEV;
7792fb4f258SKiran Gunda 	}
7802fb4f258SKiran Gunda 
7812fb4f258SKiran Gunda 	return 0;
7822fb4f258SKiran Gunda }
7832fb4f258SKiran Gunda 
78467b563f1SJosh Cartwright static struct irq_chip pmic_arb_irqchip = {
78567b563f1SJosh Cartwright 	.name		= "pmic_arb",
78667b563f1SJosh Cartwright 	.irq_ack	= qpnpint_irq_ack,
78767b563f1SJosh Cartwright 	.irq_mask	= qpnpint_irq_mask,
78867b563f1SJosh Cartwright 	.irq_unmask	= qpnpint_irq_unmask,
78967b563f1SJosh Cartwright 	.irq_set_type	= qpnpint_irq_set_type,
790cdeef07aSKiran Gunda 	.irq_set_wake	= qpnpint_irq_set_wake,
79160be4230SCourtney Cavin 	.irq_get_irqchip_state	= qpnpint_get_irqchip_state,
792cdeef07aSKiran Gunda 	.flags		= IRQCHIP_MASK_ON_SUSPEND,
79367b563f1SJosh Cartwright };
79467b563f1SJosh Cartwright 
79512a9eeaeSBrian Masney static int qpnpint_irq_domain_translate(struct irq_domain *d,
79612a9eeaeSBrian Masney 					struct irq_fwspec *fwspec,
79767b563f1SJosh Cartwright 					unsigned long *out_hwirq,
79867b563f1SJosh Cartwright 					unsigned int *out_type)
79967b563f1SJosh Cartwright {
80002abec36SKiran Gunda 	struct spmi_pmic_arb *pmic_arb = d->host_data;
80112a9eeaeSBrian Masney 	u32 *intspec = fwspec->param;
802ff615ed9SKiran Gunda 	u16 apid, ppid;
8037f1d4e58SAbhijeet Dharmapurikar 	int rc;
80467b563f1SJosh Cartwright 
80502abec36SKiran Gunda 	dev_dbg(&pmic_arb->spmic->dev, "intspec[0] 0x%1x intspec[1] 0x%02x intspec[2] 0x%02x\n",
80667b563f1SJosh Cartwright 		intspec[0], intspec[1], intspec[2]);
80767b563f1SJosh Cartwright 
80812a9eeaeSBrian Masney 	if (irq_domain_get_of_node(d) != pmic_arb->spmic->dev.of_node)
80967b563f1SJosh Cartwright 		return -EINVAL;
81012a9eeaeSBrian Masney 	if (fwspec->param_count != 4)
81167b563f1SJosh Cartwright 		return -EINVAL;
81267b563f1SJosh Cartwright 	if (intspec[0] > 0xF || intspec[1] > 0xFF || intspec[2] > 0x7)
81367b563f1SJosh Cartwright 		return -EINVAL;
81467b563f1SJosh Cartwright 
815ff615ed9SKiran Gunda 	ppid = intspec[0] << 8 | intspec[1];
816ff615ed9SKiran Gunda 	rc = pmic_arb->ver_ops->ppid_to_apid(pmic_arb, ppid);
8177f1d4e58SAbhijeet Dharmapurikar 	if (rc < 0) {
81840f318f0SDavid Collins 		dev_err(&pmic_arb->spmic->dev, "failed to xlate sid = %#x, periph = %#x, irq = %u rc = %d\n",
8197f1d4e58SAbhijeet Dharmapurikar 		intspec[0], intspec[1], intspec[2], rc);
8207f1d4e58SAbhijeet Dharmapurikar 		return rc;
8217f1d4e58SAbhijeet Dharmapurikar 	}
82267b563f1SJosh Cartwright 
823ff615ed9SKiran Gunda 	apid = rc;
82467b563f1SJosh Cartwright 	/* Keep track of {max,min}_apid for bounding search during interrupt */
82502abec36SKiran Gunda 	if (apid > pmic_arb->max_apid)
82602abec36SKiran Gunda 		pmic_arb->max_apid = apid;
82702abec36SKiran Gunda 	if (apid < pmic_arb->min_apid)
82802abec36SKiran Gunda 		pmic_arb->min_apid = apid;
82967b563f1SJosh Cartwright 
83002abec36SKiran Gunda 	*out_hwirq = spec_to_hwirq(intspec[0], intspec[1], intspec[2], apid);
83167b563f1SJosh Cartwright 	*out_type  = intspec[3] & IRQ_TYPE_SENSE_MASK;
83267b563f1SJosh Cartwright 
83302abec36SKiran Gunda 	dev_dbg(&pmic_arb->spmic->dev, "out_hwirq = %lu\n", *out_hwirq);
83467b563f1SJosh Cartwright 
83567b563f1SJosh Cartwright 	return 0;
83667b563f1SJosh Cartwright }
83767b563f1SJosh Cartwright 
8382d5a2f91SStephen Boyd static struct lock_class_key qpnpint_irq_lock_class, qpnpint_irq_request_class;
83912a9eeaeSBrian Masney 
84025655c75SBrian Masney static void qpnpint_irq_domain_map(struct spmi_pmic_arb *pmic_arb,
84112a9eeaeSBrian Masney 				   struct irq_domain *domain, unsigned int virq,
84212a9eeaeSBrian Masney 				   irq_hw_number_t hwirq, unsigned int type)
84367b563f1SJosh Cartwright {
84412a9eeaeSBrian Masney 	irq_flow_handler_t handler;
84567b563f1SJosh Cartwright 
84612a9eeaeSBrian Masney 	dev_dbg(&pmic_arb->spmic->dev, "virq = %u, hwirq = %lu, type = %u\n",
84712a9eeaeSBrian Masney 		virq, hwirq, type);
84867b563f1SJosh Cartwright 
84912a9eeaeSBrian Masney 	if (type & IRQ_TYPE_EDGE_BOTH)
85012a9eeaeSBrian Masney 		handler = handle_edge_irq;
851135ef21aSBrian Masney 	else
85225655c75SBrian Masney 		handler = handle_level_irq;
85312a9eeaeSBrian Masney 
8542d5a2f91SStephen Boyd 
8552d5a2f91SStephen Boyd 	irq_set_lockdep_class(virq, &qpnpint_irq_lock_class,
8562d5a2f91SStephen Boyd 			      &qpnpint_irq_request_class);
85712a9eeaeSBrian Masney 	irq_domain_set_info(domain, virq, hwirq, &pmic_arb_irqchip, pmic_arb,
85812a9eeaeSBrian Masney 			    handler, NULL, NULL);
85912a9eeaeSBrian Masney }
86012a9eeaeSBrian Masney 
86112a9eeaeSBrian Masney static int qpnpint_irq_domain_alloc(struct irq_domain *domain,
86212a9eeaeSBrian Masney 				    unsigned int virq, unsigned int nr_irqs,
86312a9eeaeSBrian Masney 				    void *data)
86412a9eeaeSBrian Masney {
86512a9eeaeSBrian Masney 	struct spmi_pmic_arb *pmic_arb = domain->host_data;
86612a9eeaeSBrian Masney 	struct irq_fwspec *fwspec = data;
86712a9eeaeSBrian Masney 	irq_hw_number_t hwirq;
86812a9eeaeSBrian Masney 	unsigned int type;
86912a9eeaeSBrian Masney 	int ret, i;
87012a9eeaeSBrian Masney 
87112a9eeaeSBrian Masney 	ret = qpnpint_irq_domain_translate(domain, fwspec, &hwirq, &type);
87212a9eeaeSBrian Masney 	if (ret)
87312a9eeaeSBrian Masney 		return ret;
87412a9eeaeSBrian Masney 
87525655c75SBrian Masney 	for (i = 0; i < nr_irqs; i++)
87625655c75SBrian Masney 		qpnpint_irq_domain_map(pmic_arb, domain, virq + i, hwirq + i,
87725655c75SBrian Masney 				       type);
87812a9eeaeSBrian Masney 
87967b563f1SJosh Cartwright 	return 0;
88067b563f1SJosh Cartwright }
88167b563f1SJosh Cartwright 
882ff615ed9SKiran Gunda static int pmic_arb_ppid_to_apid_v1(struct spmi_pmic_arb *pmic_arb, u16 ppid)
8837f1d4e58SAbhijeet Dharmapurikar {
88402abec36SKiran Gunda 	u32 *mapping_table = pmic_arb->mapping_table;
8857f1d4e58SAbhijeet Dharmapurikar 	int index = 0, i;
8867f1d4e58SAbhijeet Dharmapurikar 	u16 apid_valid;
887ff615ed9SKiran Gunda 	u16 apid;
8887f1d4e58SAbhijeet Dharmapurikar 	u32 data;
8897f1d4e58SAbhijeet Dharmapurikar 
89002abec36SKiran Gunda 	apid_valid = pmic_arb->ppid_to_apid[ppid];
89102abec36SKiran Gunda 	if (apid_valid & PMIC_ARB_APID_VALID) {
892ff615ed9SKiran Gunda 		apid = apid_valid & ~PMIC_ARB_APID_VALID;
893ff615ed9SKiran Gunda 		return apid;
8947f1d4e58SAbhijeet Dharmapurikar 	}
8957f1d4e58SAbhijeet Dharmapurikar 
8967f1d4e58SAbhijeet Dharmapurikar 	for (i = 0; i < SPMI_MAPPING_TABLE_TREE_DEPTH; ++i) {
89702abec36SKiran Gunda 		if (!test_and_set_bit(index, pmic_arb->mapping_table_valid))
89802abec36SKiran Gunda 			mapping_table[index] = readl_relaxed(pmic_arb->cnfg +
8997f1d4e58SAbhijeet Dharmapurikar 						SPMI_MAPPING_TABLE_REG(index));
9007f1d4e58SAbhijeet Dharmapurikar 
9017f1d4e58SAbhijeet Dharmapurikar 		data = mapping_table[index];
9027f1d4e58SAbhijeet Dharmapurikar 
9037f1d4e58SAbhijeet Dharmapurikar 		if (ppid & BIT(SPMI_MAPPING_BIT_INDEX(data))) {
9047f1d4e58SAbhijeet Dharmapurikar 			if (SPMI_MAPPING_BIT_IS_1_FLAG(data)) {
9057f1d4e58SAbhijeet Dharmapurikar 				index = SPMI_MAPPING_BIT_IS_1_RESULT(data);
9067f1d4e58SAbhijeet Dharmapurikar 			} else {
907ff615ed9SKiran Gunda 				apid = SPMI_MAPPING_BIT_IS_1_RESULT(data);
90802abec36SKiran Gunda 				pmic_arb->ppid_to_apid[ppid]
909ff615ed9SKiran Gunda 					= apid | PMIC_ARB_APID_VALID;
910ff615ed9SKiran Gunda 				pmic_arb->apid_data[apid].ppid = ppid;
911ff615ed9SKiran Gunda 				return apid;
9127f1d4e58SAbhijeet Dharmapurikar 			}
9137f1d4e58SAbhijeet Dharmapurikar 		} else {
9147f1d4e58SAbhijeet Dharmapurikar 			if (SPMI_MAPPING_BIT_IS_0_FLAG(data)) {
9157f1d4e58SAbhijeet Dharmapurikar 				index = SPMI_MAPPING_BIT_IS_0_RESULT(data);
9167f1d4e58SAbhijeet Dharmapurikar 			} else {
917ff615ed9SKiran Gunda 				apid = SPMI_MAPPING_BIT_IS_0_RESULT(data);
91802abec36SKiran Gunda 				pmic_arb->ppid_to_apid[ppid]
919ff615ed9SKiran Gunda 					= apid | PMIC_ARB_APID_VALID;
920ff615ed9SKiran Gunda 				pmic_arb->apid_data[apid].ppid = ppid;
921ff615ed9SKiran Gunda 				return apid;
9227f1d4e58SAbhijeet Dharmapurikar 			}
9237f1d4e58SAbhijeet Dharmapurikar 		}
9247f1d4e58SAbhijeet Dharmapurikar 	}
9257f1d4e58SAbhijeet Dharmapurikar 
9267f1d4e58SAbhijeet Dharmapurikar 	return -ENODEV;
9277f1d4e58SAbhijeet Dharmapurikar }
9287f1d4e58SAbhijeet Dharmapurikar 
929d0c6ae41SGilad Avidov /* v1 offset per ee */
93040f318f0SDavid Collins static int pmic_arb_offset_v1(struct spmi_pmic_arb *pmic_arb, u8 sid, u16 addr,
93140f318f0SDavid Collins 			enum pmic_arb_channel ch_type)
932d0c6ae41SGilad Avidov {
933ff615ed9SKiran Gunda 	return 0x800 + 0x80 * pmic_arb->channel;
934d0c6ae41SGilad Avidov }
935d0c6ae41SGilad Avidov 
93602abec36SKiran Gunda static u16 pmic_arb_find_apid(struct spmi_pmic_arb *pmic_arb, u16 ppid)
937987a9f12SStephen Boyd {
938f2f31564SKiran Gunda 	struct apid_data *apidd = &pmic_arb->apid_data[pmic_arb->last_apid];
939987a9f12SStephen Boyd 	u32 regval, offset;
940f2f31564SKiran Gunda 	u16 id, apid;
941987a9f12SStephen Boyd 
942f2f31564SKiran Gunda 	for (apid = pmic_arb->last_apid; ; apid++, apidd++) {
94340f318f0SDavid Collins 		offset = pmic_arb->ver_ops->apid_map_offset(apid);
94402abec36SKiran Gunda 		if (offset >= pmic_arb->core_size)
945987a9f12SStephen Boyd 			break;
946987a9f12SStephen Boyd 
94702abec36SKiran Gunda 		regval = readl_relaxed(pmic_arb->cnfg +
948b319b592SKiran Gunda 				      SPMI_OWNERSHIP_TABLE_REG(apid));
94940f318f0SDavid Collins 		apidd->irq_ee = SPMI_OWNERSHIP_PERIPH2OWNER(regval);
95040f318f0SDavid Collins 		apidd->write_ee = apidd->irq_ee;
951b319b592SKiran Gunda 
95202abec36SKiran Gunda 		regval = readl_relaxed(pmic_arb->core + offset);
953987a9f12SStephen Boyd 		if (!regval)
954987a9f12SStephen Boyd 			continue;
955987a9f12SStephen Boyd 
956987a9f12SStephen Boyd 		id = (regval >> 8) & PMIC_ARB_PPID_MASK;
95702abec36SKiran Gunda 		pmic_arb->ppid_to_apid[id] = apid | PMIC_ARB_APID_VALID;
958f2f31564SKiran Gunda 		apidd->ppid = id;
959987a9f12SStephen Boyd 		if (id == ppid) {
96002abec36SKiran Gunda 			apid |= PMIC_ARB_APID_VALID;
961987a9f12SStephen Boyd 			break;
962987a9f12SStephen Boyd 		}
963987a9f12SStephen Boyd 	}
96402abec36SKiran Gunda 	pmic_arb->last_apid = apid & ~PMIC_ARB_APID_VALID;
965987a9f12SStephen Boyd 
9661ef1ce4eSAbhijeet Dharmapurikar 	return apid;
967987a9f12SStephen Boyd }
968987a9f12SStephen Boyd 
969ff615ed9SKiran Gunda static int pmic_arb_ppid_to_apid_v2(struct spmi_pmic_arb *pmic_arb, u16 ppid)
97057102ad7SAbhijeet Dharmapurikar {
9717f1d4e58SAbhijeet Dharmapurikar 	u16 apid_valid;
97257102ad7SAbhijeet Dharmapurikar 
97302abec36SKiran Gunda 	apid_valid = pmic_arb->ppid_to_apid[ppid];
97402abec36SKiran Gunda 	if (!(apid_valid & PMIC_ARB_APID_VALID))
97502abec36SKiran Gunda 		apid_valid = pmic_arb_find_apid(pmic_arb, ppid);
97602abec36SKiran Gunda 	if (!(apid_valid & PMIC_ARB_APID_VALID))
97757102ad7SAbhijeet Dharmapurikar 		return -ENODEV;
97857102ad7SAbhijeet Dharmapurikar 
979ff615ed9SKiran Gunda 	return apid_valid & ~PMIC_ARB_APID_VALID;
9807f1d4e58SAbhijeet Dharmapurikar }
9817f1d4e58SAbhijeet Dharmapurikar 
98240f318f0SDavid Collins static int pmic_arb_read_apid_map_v5(struct spmi_pmic_arb *pmic_arb)
98340f318f0SDavid Collins {
98440f318f0SDavid Collins 	struct apid_data *apidd = pmic_arb->apid_data;
98540f318f0SDavid Collins 	struct apid_data *prev_apidd;
98640f318f0SDavid Collins 	u16 i, apid, ppid;
98740f318f0SDavid Collins 	bool valid, is_irq_ee;
98840f318f0SDavid Collins 	u32 regval, offset;
98940f318f0SDavid Collins 
99040f318f0SDavid Collins 	/*
99140f318f0SDavid Collins 	 * In order to allow multiple EEs to write to a single PPID in arbiter
99240f318f0SDavid Collins 	 * version 5, there is more than one APID mapped to each PPID.
99340f318f0SDavid Collins 	 * The owner field for each of these mappings specifies the EE which is
99440f318f0SDavid Collins 	 * allowed to write to the APID.  The owner of the last (highest) APID
99540f318f0SDavid Collins 	 * for a given PPID will receive interrupts from the PPID.
99640f318f0SDavid Collins 	 */
99740f318f0SDavid Collins 	for (i = 0; ; i++, apidd++) {
99840f318f0SDavid Collins 		offset = pmic_arb->ver_ops->apid_map_offset(i);
99940f318f0SDavid Collins 		if (offset >= pmic_arb->core_size)
100040f318f0SDavid Collins 			break;
100140f318f0SDavid Collins 
100240f318f0SDavid Collins 		regval = readl_relaxed(pmic_arb->core + offset);
100340f318f0SDavid Collins 		if (!regval)
100440f318f0SDavid Collins 			continue;
100540f318f0SDavid Collins 		ppid = (regval >> 8) & PMIC_ARB_PPID_MASK;
100640f318f0SDavid Collins 		is_irq_ee = PMIC_ARB_CHAN_IS_IRQ_OWNER(regval);
100740f318f0SDavid Collins 
100840f318f0SDavid Collins 		regval = readl_relaxed(pmic_arb->cnfg +
100940f318f0SDavid Collins 				      SPMI_OWNERSHIP_TABLE_REG(i));
101040f318f0SDavid Collins 		apidd->write_ee = SPMI_OWNERSHIP_PERIPH2OWNER(regval);
101140f318f0SDavid Collins 
101240f318f0SDavid Collins 		apidd->irq_ee = is_irq_ee ? apidd->write_ee : INVALID_EE;
101340f318f0SDavid Collins 
101440f318f0SDavid Collins 		valid = pmic_arb->ppid_to_apid[ppid] & PMIC_ARB_APID_VALID;
101540f318f0SDavid Collins 		apid = pmic_arb->ppid_to_apid[ppid] & ~PMIC_ARB_APID_VALID;
101640f318f0SDavid Collins 		prev_apidd = &pmic_arb->apid_data[apid];
101740f318f0SDavid Collins 
101840f318f0SDavid Collins 		if (valid && is_irq_ee &&
101940f318f0SDavid Collins 				prev_apidd->write_ee == pmic_arb->ee) {
102040f318f0SDavid Collins 			/*
102140f318f0SDavid Collins 			 * Duplicate PPID mapping after the one for this EE;
102240f318f0SDavid Collins 			 * override the irq owner
102340f318f0SDavid Collins 			 */
102440f318f0SDavid Collins 			prev_apidd->irq_ee = apidd->irq_ee;
102540f318f0SDavid Collins 		} else if (!valid || is_irq_ee) {
102640f318f0SDavid Collins 			/* First PPID mapping or duplicate for another EE */
102740f318f0SDavid Collins 			pmic_arb->ppid_to_apid[ppid] = i | PMIC_ARB_APID_VALID;
102840f318f0SDavid Collins 		}
102940f318f0SDavid Collins 
103040f318f0SDavid Collins 		apidd->ppid = ppid;
103140f318f0SDavid Collins 		pmic_arb->last_apid = i;
103240f318f0SDavid Collins 	}
103340f318f0SDavid Collins 
103440f318f0SDavid Collins 	/* Dump the mapping table for debug purposes. */
103540f318f0SDavid Collins 	dev_dbg(&pmic_arb->spmic->dev, "PPID APID Write-EE IRQ-EE\n");
103640f318f0SDavid Collins 	for (ppid = 0; ppid < PMIC_ARB_MAX_PPID; ppid++) {
103740f318f0SDavid Collins 		apid = pmic_arb->ppid_to_apid[ppid];
103840f318f0SDavid Collins 		if (apid & PMIC_ARB_APID_VALID) {
103940f318f0SDavid Collins 			apid &= ~PMIC_ARB_APID_VALID;
104040f318f0SDavid Collins 			apidd = &pmic_arb->apid_data[apid];
104140f318f0SDavid Collins 			dev_dbg(&pmic_arb->spmic->dev, "%#03X %3u %2u %2u\n",
104240f318f0SDavid Collins 			      ppid, apid, apidd->write_ee, apidd->irq_ee);
104340f318f0SDavid Collins 		}
104440f318f0SDavid Collins 	}
104540f318f0SDavid Collins 
104640f318f0SDavid Collins 	return 0;
104740f318f0SDavid Collins }
104840f318f0SDavid Collins 
104940f318f0SDavid Collins static int pmic_arb_ppid_to_apid_v5(struct spmi_pmic_arb *pmic_arb, u16 ppid)
105040f318f0SDavid Collins {
105140f318f0SDavid Collins 	if (!(pmic_arb->ppid_to_apid[ppid] & PMIC_ARB_APID_VALID))
105240f318f0SDavid Collins 		return -ENODEV;
105340f318f0SDavid Collins 
105440f318f0SDavid Collins 	return pmic_arb->ppid_to_apid[ppid] & ~PMIC_ARB_APID_VALID;
105540f318f0SDavid Collins }
105640f318f0SDavid Collins 
10571ef1ce4eSAbhijeet Dharmapurikar /* v2 offset per ppid and per ee */
105840f318f0SDavid Collins static int pmic_arb_offset_v2(struct spmi_pmic_arb *pmic_arb, u8 sid, u16 addr,
105940f318f0SDavid Collins 			   enum pmic_arb_channel ch_type)
1060d0c6ae41SGilad Avidov {
1061319f6884SAbhijeet Dharmapurikar 	u16 apid;
1062ff615ed9SKiran Gunda 	u16 ppid;
10637f1d4e58SAbhijeet Dharmapurikar 	int rc;
1064d0c6ae41SGilad Avidov 
1065ff615ed9SKiran Gunda 	ppid = sid << 8 | ((addr >> 8) & 0xFF);
1066ff615ed9SKiran Gunda 	rc = pmic_arb_ppid_to_apid_v2(pmic_arb, ppid);
10677f1d4e58SAbhijeet Dharmapurikar 	if (rc < 0)
10687f1d4e58SAbhijeet Dharmapurikar 		return rc;
1069987a9f12SStephen Boyd 
1070ff615ed9SKiran Gunda 	apid = rc;
1071ff615ed9SKiran Gunda 	return 0x1000 * pmic_arb->ee + 0x8000 * apid;
1072d0c6ae41SGilad Avidov }
1073d0c6ae41SGilad Avidov 
107440f318f0SDavid Collins /*
107540f318f0SDavid Collins  * v5 offset per ee and per apid for observer channels and per apid for
107640f318f0SDavid Collins  * read/write channels.
107740f318f0SDavid Collins  */
107840f318f0SDavid Collins static int pmic_arb_offset_v5(struct spmi_pmic_arb *pmic_arb, u8 sid, u16 addr,
107940f318f0SDavid Collins 			   enum pmic_arb_channel ch_type)
108040f318f0SDavid Collins {
108140f318f0SDavid Collins 	u16 apid;
108240f318f0SDavid Collins 	int rc;
108340f318f0SDavid Collins 	u32 offset = 0;
108440f318f0SDavid Collins 	u16 ppid = (sid << 8) | (addr >> 8);
108540f318f0SDavid Collins 
108640f318f0SDavid Collins 	rc = pmic_arb_ppid_to_apid_v5(pmic_arb, ppid);
108740f318f0SDavid Collins 	if (rc < 0)
108840f318f0SDavid Collins 		return rc;
108940f318f0SDavid Collins 
109040f318f0SDavid Collins 	apid = rc;
109140f318f0SDavid Collins 	switch (ch_type) {
109240f318f0SDavid Collins 	case PMIC_ARB_CHANNEL_OBS:
109340f318f0SDavid Collins 		offset = 0x10000 * pmic_arb->ee + 0x80 * apid;
109440f318f0SDavid Collins 		break;
109540f318f0SDavid Collins 	case PMIC_ARB_CHANNEL_RW:
109640f318f0SDavid Collins 		offset = 0x10000 * apid;
109740f318f0SDavid Collins 		break;
109840f318f0SDavid Collins 	}
109940f318f0SDavid Collins 
110040f318f0SDavid Collins 	return offset;
110140f318f0SDavid Collins }
110240f318f0SDavid Collins 
1103d0c6ae41SGilad Avidov static u32 pmic_arb_fmt_cmd_v1(u8 opc, u8 sid, u16 addr, u8 bc)
1104d0c6ae41SGilad Avidov {
1105d0c6ae41SGilad Avidov 	return (opc << 27) | ((sid & 0xf) << 20) | (addr << 4) | (bc & 0x7);
1106d0c6ae41SGilad Avidov }
1107d0c6ae41SGilad Avidov 
1108d0c6ae41SGilad Avidov static u32 pmic_arb_fmt_cmd_v2(u8 opc, u8 sid, u16 addr, u8 bc)
1109d0c6ae41SGilad Avidov {
1110d0c6ae41SGilad Avidov 	return (opc << 27) | ((addr & 0xff) << 4) | (bc & 0x7);
1111d0c6ae41SGilad Avidov }
1112d0c6ae41SGilad Avidov 
1113e95d073cSKiran Gunda static void __iomem *
1114e95d073cSKiran Gunda pmic_arb_owner_acc_status_v1(struct spmi_pmic_arb *pmic_arb, u8 m, u16 n)
1115d0c6ae41SGilad Avidov {
1116e95d073cSKiran Gunda 	return pmic_arb->intr + 0x20 * m + 0x4 * n;
1117d0c6ae41SGilad Avidov }
1118d0c6ae41SGilad Avidov 
1119e95d073cSKiran Gunda static void __iomem *
1120e95d073cSKiran Gunda pmic_arb_owner_acc_status_v2(struct spmi_pmic_arb *pmic_arb, u8 m, u16 n)
1121d0c6ae41SGilad Avidov {
1122e95d073cSKiran Gunda 	return pmic_arb->intr + 0x100000 + 0x1000 * m + 0x4 * n;
1123d0c6ae41SGilad Avidov }
1124d0c6ae41SGilad Avidov 
1125e95d073cSKiran Gunda static void __iomem *
1126e95d073cSKiran Gunda pmic_arb_owner_acc_status_v3(struct spmi_pmic_arb *pmic_arb, u8 m, u16 n)
1127319f6884SAbhijeet Dharmapurikar {
1128e95d073cSKiran Gunda 	return pmic_arb->intr + 0x200000 + 0x1000 * m + 0x4 * n;
1129319f6884SAbhijeet Dharmapurikar }
1130319f6884SAbhijeet Dharmapurikar 
1131e95d073cSKiran Gunda static void __iomem *
113240f318f0SDavid Collins pmic_arb_owner_acc_status_v5(struct spmi_pmic_arb *pmic_arb, u8 m, u16 n)
113340f318f0SDavid Collins {
113440f318f0SDavid Collins 	return pmic_arb->intr + 0x10000 * m + 0x4 * n;
113540f318f0SDavid Collins }
113640f318f0SDavid Collins 
113740f318f0SDavid Collins static void __iomem *
1138e95d073cSKiran Gunda pmic_arb_acc_enable_v1(struct spmi_pmic_arb *pmic_arb, u16 n)
1139d0c6ae41SGilad Avidov {
1140e95d073cSKiran Gunda 	return pmic_arb->intr + 0x200 + 0x4 * n;
1141d0c6ae41SGilad Avidov }
1142d0c6ae41SGilad Avidov 
1143e95d073cSKiran Gunda static void __iomem *
1144e95d073cSKiran Gunda pmic_arb_acc_enable_v2(struct spmi_pmic_arb *pmic_arb, u16 n)
1145d0c6ae41SGilad Avidov {
1146e95d073cSKiran Gunda 	return pmic_arb->intr + 0x1000 * n;
1147d0c6ae41SGilad Avidov }
1148d0c6ae41SGilad Avidov 
1149e95d073cSKiran Gunda static void __iomem *
115040f318f0SDavid Collins pmic_arb_acc_enable_v5(struct spmi_pmic_arb *pmic_arb, u16 n)
115140f318f0SDavid Collins {
115240f318f0SDavid Collins 	return pmic_arb->wr_base + 0x100 + 0x10000 * n;
115340f318f0SDavid Collins }
115440f318f0SDavid Collins 
115540f318f0SDavid Collins static void __iomem *
1156e95d073cSKiran Gunda pmic_arb_irq_status_v1(struct spmi_pmic_arb *pmic_arb, u16 n)
1157d0c6ae41SGilad Avidov {
1158e95d073cSKiran Gunda 	return pmic_arb->intr + 0x600 + 0x4 * n;
1159d0c6ae41SGilad Avidov }
1160d0c6ae41SGilad Avidov 
1161e95d073cSKiran Gunda static void __iomem *
1162e95d073cSKiran Gunda pmic_arb_irq_status_v2(struct spmi_pmic_arb *pmic_arb, u16 n)
1163d0c6ae41SGilad Avidov {
1164e95d073cSKiran Gunda 	return pmic_arb->intr + 0x4 + 0x1000 * n;
1165d0c6ae41SGilad Avidov }
1166d0c6ae41SGilad Avidov 
1167e95d073cSKiran Gunda static void __iomem *
116840f318f0SDavid Collins pmic_arb_irq_status_v5(struct spmi_pmic_arb *pmic_arb, u16 n)
116940f318f0SDavid Collins {
117040f318f0SDavid Collins 	return pmic_arb->wr_base + 0x104 + 0x10000 * n;
117140f318f0SDavid Collins }
117240f318f0SDavid Collins 
117340f318f0SDavid Collins static void __iomem *
1174e95d073cSKiran Gunda pmic_arb_irq_clear_v1(struct spmi_pmic_arb *pmic_arb, u16 n)
1175d0c6ae41SGilad Avidov {
1176e95d073cSKiran Gunda 	return pmic_arb->intr + 0xA00 + 0x4 * n;
1177d0c6ae41SGilad Avidov }
1178d0c6ae41SGilad Avidov 
1179e95d073cSKiran Gunda static void __iomem *
1180e95d073cSKiran Gunda pmic_arb_irq_clear_v2(struct spmi_pmic_arb *pmic_arb, u16 n)
1181d0c6ae41SGilad Avidov {
1182e95d073cSKiran Gunda 	return pmic_arb->intr + 0x8 + 0x1000 * n;
1183d0c6ae41SGilad Avidov }
1184d0c6ae41SGilad Avidov 
118540f318f0SDavid Collins static void __iomem *
118640f318f0SDavid Collins pmic_arb_irq_clear_v5(struct spmi_pmic_arb *pmic_arb, u16 n)
118740f318f0SDavid Collins {
118840f318f0SDavid Collins 	return pmic_arb->wr_base + 0x108 + 0x10000 * n;
118940f318f0SDavid Collins }
119040f318f0SDavid Collins 
119140f318f0SDavid Collins static u32 pmic_arb_apid_map_offset_v2(u16 n)
119240f318f0SDavid Collins {
119340f318f0SDavid Collins 	return 0x800 + 0x4 * n;
119440f318f0SDavid Collins }
119540f318f0SDavid Collins 
119640f318f0SDavid Collins static u32 pmic_arb_apid_map_offset_v5(u16 n)
119740f318f0SDavid Collins {
119840f318f0SDavid Collins 	return 0x900 + 0x4 * n;
119940f318f0SDavid Collins }
120040f318f0SDavid Collins 
1201d0c6ae41SGilad Avidov static const struct pmic_arb_ver_ops pmic_arb_v1 = {
1202319f6884SAbhijeet Dharmapurikar 	.ver_str		= "v1",
12037f1d4e58SAbhijeet Dharmapurikar 	.ppid_to_apid		= pmic_arb_ppid_to_apid_v1,
1204d0c6ae41SGilad Avidov 	.non_data_cmd		= pmic_arb_non_data_cmd_v1,
1205d0c6ae41SGilad Avidov 	.offset			= pmic_arb_offset_v1,
1206d0c6ae41SGilad Avidov 	.fmt_cmd		= pmic_arb_fmt_cmd_v1,
1207d0c6ae41SGilad Avidov 	.owner_acc_status	= pmic_arb_owner_acc_status_v1,
1208d0c6ae41SGilad Avidov 	.acc_enable		= pmic_arb_acc_enable_v1,
1209d0c6ae41SGilad Avidov 	.irq_status		= pmic_arb_irq_status_v1,
1210d0c6ae41SGilad Avidov 	.irq_clear		= pmic_arb_irq_clear_v1,
121140f318f0SDavid Collins 	.apid_map_offset	= pmic_arb_apid_map_offset_v2,
1212d0c6ae41SGilad Avidov };
1213d0c6ae41SGilad Avidov 
1214d0c6ae41SGilad Avidov static const struct pmic_arb_ver_ops pmic_arb_v2 = {
1215319f6884SAbhijeet Dharmapurikar 	.ver_str		= "v2",
12167f1d4e58SAbhijeet Dharmapurikar 	.ppid_to_apid		= pmic_arb_ppid_to_apid_v2,
1217d0c6ae41SGilad Avidov 	.non_data_cmd		= pmic_arb_non_data_cmd_v2,
1218d0c6ae41SGilad Avidov 	.offset			= pmic_arb_offset_v2,
1219d0c6ae41SGilad Avidov 	.fmt_cmd		= pmic_arb_fmt_cmd_v2,
1220d0c6ae41SGilad Avidov 	.owner_acc_status	= pmic_arb_owner_acc_status_v2,
1221d0c6ae41SGilad Avidov 	.acc_enable		= pmic_arb_acc_enable_v2,
1222d0c6ae41SGilad Avidov 	.irq_status		= pmic_arb_irq_status_v2,
1223d0c6ae41SGilad Avidov 	.irq_clear		= pmic_arb_irq_clear_v2,
122440f318f0SDavid Collins 	.apid_map_offset	= pmic_arb_apid_map_offset_v2,
1225d0c6ae41SGilad Avidov };
1226d0c6ae41SGilad Avidov 
1227319f6884SAbhijeet Dharmapurikar static const struct pmic_arb_ver_ops pmic_arb_v3 = {
1228319f6884SAbhijeet Dharmapurikar 	.ver_str		= "v3",
1229319f6884SAbhijeet Dharmapurikar 	.ppid_to_apid		= pmic_arb_ppid_to_apid_v2,
1230319f6884SAbhijeet Dharmapurikar 	.non_data_cmd		= pmic_arb_non_data_cmd_v2,
1231319f6884SAbhijeet Dharmapurikar 	.offset			= pmic_arb_offset_v2,
1232319f6884SAbhijeet Dharmapurikar 	.fmt_cmd		= pmic_arb_fmt_cmd_v2,
1233319f6884SAbhijeet Dharmapurikar 	.owner_acc_status	= pmic_arb_owner_acc_status_v3,
1234319f6884SAbhijeet Dharmapurikar 	.acc_enable		= pmic_arb_acc_enable_v2,
1235319f6884SAbhijeet Dharmapurikar 	.irq_status		= pmic_arb_irq_status_v2,
1236319f6884SAbhijeet Dharmapurikar 	.irq_clear		= pmic_arb_irq_clear_v2,
123740f318f0SDavid Collins 	.apid_map_offset	= pmic_arb_apid_map_offset_v2,
123840f318f0SDavid Collins };
123940f318f0SDavid Collins 
124040f318f0SDavid Collins static const struct pmic_arb_ver_ops pmic_arb_v5 = {
124140f318f0SDavid Collins 	.ver_str		= "v5",
124240f318f0SDavid Collins 	.ppid_to_apid		= pmic_arb_ppid_to_apid_v5,
124340f318f0SDavid Collins 	.non_data_cmd		= pmic_arb_non_data_cmd_v2,
124440f318f0SDavid Collins 	.offset			= pmic_arb_offset_v5,
124540f318f0SDavid Collins 	.fmt_cmd		= pmic_arb_fmt_cmd_v2,
124640f318f0SDavid Collins 	.owner_acc_status	= pmic_arb_owner_acc_status_v5,
124740f318f0SDavid Collins 	.acc_enable		= pmic_arb_acc_enable_v5,
124840f318f0SDavid Collins 	.irq_status		= pmic_arb_irq_status_v5,
124940f318f0SDavid Collins 	.irq_clear		= pmic_arb_irq_clear_v5,
125040f318f0SDavid Collins 	.apid_map_offset	= pmic_arb_apid_map_offset_v5,
1251319f6884SAbhijeet Dharmapurikar };
1252319f6884SAbhijeet Dharmapurikar 
125367b563f1SJosh Cartwright static const struct irq_domain_ops pmic_arb_irq_domain_ops = {
125412a9eeaeSBrian Masney 	.activate = qpnpint_irq_domain_activate,
125512a9eeaeSBrian Masney 	.alloc = qpnpint_irq_domain_alloc,
125612a9eeaeSBrian Masney 	.free = irq_domain_free_irqs_common,
125712a9eeaeSBrian Masney 	.translate = qpnpint_irq_domain_translate,
125867b563f1SJosh Cartwright };
125967b563f1SJosh Cartwright 
126039ae93e3SKenneth Heitke static int spmi_pmic_arb_probe(struct platform_device *pdev)
126139ae93e3SKenneth Heitke {
126202abec36SKiran Gunda 	struct spmi_pmic_arb *pmic_arb;
126339ae93e3SKenneth Heitke 	struct spmi_controller *ctrl;
126439ae93e3SKenneth Heitke 	struct resource *res;
1265d0c6ae41SGilad Avidov 	void __iomem *core;
12664788e613SKiran Gunda 	u32 *mapping_table;
1267d0c6ae41SGilad Avidov 	u32 channel, ee, hw_ver;
1268987a9f12SStephen Boyd 	int err;
126939ae93e3SKenneth Heitke 
127002abec36SKiran Gunda 	ctrl = spmi_controller_alloc(&pdev->dev, sizeof(*pmic_arb));
127139ae93e3SKenneth Heitke 	if (!ctrl)
127239ae93e3SKenneth Heitke 		return -ENOMEM;
127339ae93e3SKenneth Heitke 
127402abec36SKiran Gunda 	pmic_arb = spmi_controller_get_drvdata(ctrl);
127502abec36SKiran Gunda 	pmic_arb->spmic = ctrl;
127639ae93e3SKenneth Heitke 
127739ae93e3SKenneth Heitke 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "core");
1278d0c6ae41SGilad Avidov 	core = devm_ioremap_resource(&ctrl->dev, res);
1279d0c6ae41SGilad Avidov 	if (IS_ERR(core)) {
1280d0c6ae41SGilad Avidov 		err = PTR_ERR(core);
128139ae93e3SKenneth Heitke 		goto err_put_ctrl;
128239ae93e3SKenneth Heitke 	}
128339ae93e3SKenneth Heitke 
1284000e1a43SKiran Gunda 	pmic_arb->core_size = resource_size(res);
1285000e1a43SKiran Gunda 
128602abec36SKiran Gunda 	pmic_arb->ppid_to_apid = devm_kcalloc(&ctrl->dev, PMIC_ARB_MAX_PPID,
128702abec36SKiran Gunda 					      sizeof(*pmic_arb->ppid_to_apid),
128802abec36SKiran Gunda 					      GFP_KERNEL);
128902abec36SKiran Gunda 	if (!pmic_arb->ppid_to_apid) {
1290eba9718eSStephen Boyd 		err = -ENOMEM;
1291eba9718eSStephen Boyd 		goto err_put_ctrl;
1292eba9718eSStephen Boyd 	}
1293eba9718eSStephen Boyd 
1294d0c6ae41SGilad Avidov 	hw_ver = readl_relaxed(core + PMIC_ARB_VERSION);
1295d0c6ae41SGilad Avidov 
1296319f6884SAbhijeet Dharmapurikar 	if (hw_ver < PMIC_ARB_VERSION_V2_MIN) {
129702abec36SKiran Gunda 		pmic_arb->ver_ops = &pmic_arb_v1;
129802abec36SKiran Gunda 		pmic_arb->wr_base = core;
129902abec36SKiran Gunda 		pmic_arb->rd_base = core;
1300d0c6ae41SGilad Avidov 	} else {
130102abec36SKiran Gunda 		pmic_arb->core = core;
1302319f6884SAbhijeet Dharmapurikar 
1303319f6884SAbhijeet Dharmapurikar 		if (hw_ver < PMIC_ARB_VERSION_V3_MIN)
130402abec36SKiran Gunda 			pmic_arb->ver_ops = &pmic_arb_v2;
130540f318f0SDavid Collins 		else if (hw_ver < PMIC_ARB_VERSION_V5_MIN)
130602abec36SKiran Gunda 			pmic_arb->ver_ops = &pmic_arb_v3;
130740f318f0SDavid Collins 		else
130840f318f0SDavid Collins 			pmic_arb->ver_ops = &pmic_arb_v5;
1309d0c6ae41SGilad Avidov 
1310d0c6ae41SGilad Avidov 		res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
1311d0c6ae41SGilad Avidov 						   "obsrvr");
131202abec36SKiran Gunda 		pmic_arb->rd_base = devm_ioremap_resource(&ctrl->dev, res);
131302abec36SKiran Gunda 		if (IS_ERR(pmic_arb->rd_base)) {
131402abec36SKiran Gunda 			err = PTR_ERR(pmic_arb->rd_base);
1315d0c6ae41SGilad Avidov 			goto err_put_ctrl;
1316d0c6ae41SGilad Avidov 		}
1317d0c6ae41SGilad Avidov 
1318d0c6ae41SGilad Avidov 		res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
1319d0c6ae41SGilad Avidov 						   "chnls");
132002abec36SKiran Gunda 		pmic_arb->wr_base = devm_ioremap_resource(&ctrl->dev, res);
132102abec36SKiran Gunda 		if (IS_ERR(pmic_arb->wr_base)) {
132202abec36SKiran Gunda 			err = PTR_ERR(pmic_arb->wr_base);
1323d0c6ae41SGilad Avidov 			goto err_put_ctrl;
1324d0c6ae41SGilad Avidov 		}
1325d0c6ae41SGilad Avidov 	}
1326d0c6ae41SGilad Avidov 
1327319f6884SAbhijeet Dharmapurikar 	dev_info(&ctrl->dev, "PMIC arbiter version %s (0x%x)\n",
132802abec36SKiran Gunda 		 pmic_arb->ver_ops->ver_str, hw_ver);
1329319f6884SAbhijeet Dharmapurikar 
133039ae93e3SKenneth Heitke 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "intr");
133102abec36SKiran Gunda 	pmic_arb->intr = devm_ioremap_resource(&ctrl->dev, res);
133202abec36SKiran Gunda 	if (IS_ERR(pmic_arb->intr)) {
133302abec36SKiran Gunda 		err = PTR_ERR(pmic_arb->intr);
133439ae93e3SKenneth Heitke 		goto err_put_ctrl;
133539ae93e3SKenneth Heitke 	}
133639ae93e3SKenneth Heitke 
133739ae93e3SKenneth Heitke 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cnfg");
133802abec36SKiran Gunda 	pmic_arb->cnfg = devm_ioremap_resource(&ctrl->dev, res);
133902abec36SKiran Gunda 	if (IS_ERR(pmic_arb->cnfg)) {
134002abec36SKiran Gunda 		err = PTR_ERR(pmic_arb->cnfg);
134139ae93e3SKenneth Heitke 		goto err_put_ctrl;
134239ae93e3SKenneth Heitke 	}
134339ae93e3SKenneth Heitke 
134402abec36SKiran Gunda 	pmic_arb->irq = platform_get_irq_byname(pdev, "periph_irq");
134502abec36SKiran Gunda 	if (pmic_arb->irq < 0) {
134602abec36SKiran Gunda 		err = pmic_arb->irq;
134767b563f1SJosh Cartwright 		goto err_put_ctrl;
134867b563f1SJosh Cartwright 	}
134967b563f1SJosh Cartwright 
135039ae93e3SKenneth Heitke 	err = of_property_read_u32(pdev->dev.of_node, "qcom,channel", &channel);
135139ae93e3SKenneth Heitke 	if (err) {
135239ae93e3SKenneth Heitke 		dev_err(&pdev->dev, "channel unspecified.\n");
135339ae93e3SKenneth Heitke 		goto err_put_ctrl;
135439ae93e3SKenneth Heitke 	}
135539ae93e3SKenneth Heitke 
135639ae93e3SKenneth Heitke 	if (channel > 5) {
135739ae93e3SKenneth Heitke 		dev_err(&pdev->dev, "invalid channel (%u) specified.\n",
135839ae93e3SKenneth Heitke 			channel);
1359e98cc182SChristophe JAILLET 		err = -EINVAL;
136039ae93e3SKenneth Heitke 		goto err_put_ctrl;
136139ae93e3SKenneth Heitke 	}
136239ae93e3SKenneth Heitke 
136302abec36SKiran Gunda 	pmic_arb->channel = channel;
136439ae93e3SKenneth Heitke 
136567b563f1SJosh Cartwright 	err = of_property_read_u32(pdev->dev.of_node, "qcom,ee", &ee);
136667b563f1SJosh Cartwright 	if (err) {
136767b563f1SJosh Cartwright 		dev_err(&pdev->dev, "EE unspecified.\n");
136867b563f1SJosh Cartwright 		goto err_put_ctrl;
136967b563f1SJosh Cartwright 	}
137067b563f1SJosh Cartwright 
137167b563f1SJosh Cartwright 	if (ee > 5) {
137267b563f1SJosh Cartwright 		dev_err(&pdev->dev, "invalid EE (%u) specified\n", ee);
137367b563f1SJosh Cartwright 		err = -EINVAL;
137467b563f1SJosh Cartwright 		goto err_put_ctrl;
137567b563f1SJosh Cartwright 	}
137667b563f1SJosh Cartwright 
137702abec36SKiran Gunda 	pmic_arb->ee = ee;
13784788e613SKiran Gunda 	mapping_table = devm_kcalloc(&ctrl->dev, PMIC_ARB_MAX_PERIPHS,
13794788e613SKiran Gunda 					sizeof(*mapping_table), GFP_KERNEL);
13804788e613SKiran Gunda 	if (!mapping_table) {
1381987a9f12SStephen Boyd 		err = -ENOMEM;
1382987a9f12SStephen Boyd 		goto err_put_ctrl;
1383987a9f12SStephen Boyd 	}
138467b563f1SJosh Cartwright 
13854788e613SKiran Gunda 	pmic_arb->mapping_table = mapping_table;
138667b563f1SJosh Cartwright 	/* Initialize max_apid/min_apid to the opposite bounds, during
138767b563f1SJosh Cartwright 	 * the irq domain translation, we are sure to update these */
138802abec36SKiran Gunda 	pmic_arb->max_apid = 0;
138902abec36SKiran Gunda 	pmic_arb->min_apid = PMIC_ARB_MAX_PERIPHS - 1;
139067b563f1SJosh Cartwright 
139139ae93e3SKenneth Heitke 	platform_set_drvdata(pdev, ctrl);
139202abec36SKiran Gunda 	raw_spin_lock_init(&pmic_arb->lock);
139339ae93e3SKenneth Heitke 
139439ae93e3SKenneth Heitke 	ctrl->cmd = pmic_arb_cmd;
139539ae93e3SKenneth Heitke 	ctrl->read_cmd = pmic_arb_read_cmd;
139639ae93e3SKenneth Heitke 	ctrl->write_cmd = pmic_arb_write_cmd;
139739ae93e3SKenneth Heitke 
139840f318f0SDavid Collins 	if (hw_ver >= PMIC_ARB_VERSION_V5_MIN) {
139940f318f0SDavid Collins 		err = pmic_arb_read_apid_map_v5(pmic_arb);
140040f318f0SDavid Collins 		if (err) {
140140f318f0SDavid Collins 			dev_err(&pdev->dev, "could not read APID->PPID mapping table, rc= %d\n",
140240f318f0SDavid Collins 				err);
140340f318f0SDavid Collins 			goto err_put_ctrl;
140440f318f0SDavid Collins 		}
140540f318f0SDavid Collins 	}
140640f318f0SDavid Collins 
140767b563f1SJosh Cartwright 	dev_dbg(&pdev->dev, "adding irq domain\n");
140802abec36SKiran Gunda 	pmic_arb->domain = irq_domain_add_tree(pdev->dev.of_node,
140902abec36SKiran Gunda 					 &pmic_arb_irq_domain_ops, pmic_arb);
141002abec36SKiran Gunda 	if (!pmic_arb->domain) {
141167b563f1SJosh Cartwright 		dev_err(&pdev->dev, "unable to create irq_domain\n");
141267b563f1SJosh Cartwright 		err = -ENOMEM;
141367b563f1SJosh Cartwright 		goto err_put_ctrl;
141467b563f1SJosh Cartwright 	}
141567b563f1SJosh Cartwright 
141602abec36SKiran Gunda 	irq_set_chained_handler_and_data(pmic_arb->irq, pmic_arb_chained_irq,
141702abec36SKiran Gunda 					pmic_arb);
141839ae93e3SKenneth Heitke 	err = spmi_controller_add(ctrl);
141939ae93e3SKenneth Heitke 	if (err)
142067b563f1SJosh Cartwright 		goto err_domain_remove;
142139ae93e3SKenneth Heitke 
142239ae93e3SKenneth Heitke 	return 0;
142339ae93e3SKenneth Heitke 
142467b563f1SJosh Cartwright err_domain_remove:
142502abec36SKiran Gunda 	irq_set_chained_handler_and_data(pmic_arb->irq, NULL, NULL);
142602abec36SKiran Gunda 	irq_domain_remove(pmic_arb->domain);
142739ae93e3SKenneth Heitke err_put_ctrl:
142839ae93e3SKenneth Heitke 	spmi_controller_put(ctrl);
142939ae93e3SKenneth Heitke 	return err;
143039ae93e3SKenneth Heitke }
143139ae93e3SKenneth Heitke 
143239ae93e3SKenneth Heitke static int spmi_pmic_arb_remove(struct platform_device *pdev)
143339ae93e3SKenneth Heitke {
143439ae93e3SKenneth Heitke 	struct spmi_controller *ctrl = platform_get_drvdata(pdev);
143502abec36SKiran Gunda 	struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl);
143639ae93e3SKenneth Heitke 	spmi_controller_remove(ctrl);
143702abec36SKiran Gunda 	irq_set_chained_handler_and_data(pmic_arb->irq, NULL, NULL);
143802abec36SKiran Gunda 	irq_domain_remove(pmic_arb->domain);
143939ae93e3SKenneth Heitke 	spmi_controller_put(ctrl);
144039ae93e3SKenneth Heitke 	return 0;
144139ae93e3SKenneth Heitke }
144239ae93e3SKenneth Heitke 
144339ae93e3SKenneth Heitke static const struct of_device_id spmi_pmic_arb_match_table[] = {
144439ae93e3SKenneth Heitke 	{ .compatible = "qcom,spmi-pmic-arb", },
144539ae93e3SKenneth Heitke 	{},
144639ae93e3SKenneth Heitke };
144739ae93e3SKenneth Heitke MODULE_DEVICE_TABLE(of, spmi_pmic_arb_match_table);
144839ae93e3SKenneth Heitke 
144939ae93e3SKenneth Heitke static struct platform_driver spmi_pmic_arb_driver = {
145039ae93e3SKenneth Heitke 	.probe		= spmi_pmic_arb_probe,
145139ae93e3SKenneth Heitke 	.remove		= spmi_pmic_arb_remove,
145239ae93e3SKenneth Heitke 	.driver		= {
145339ae93e3SKenneth Heitke 		.name	= "spmi_pmic_arb",
145439ae93e3SKenneth Heitke 		.of_match_table = spmi_pmic_arb_match_table,
145539ae93e3SKenneth Heitke 	},
145639ae93e3SKenneth Heitke };
145739ae93e3SKenneth Heitke module_platform_driver(spmi_pmic_arb_driver);
145839ae93e3SKenneth Heitke 
145939ae93e3SKenneth Heitke MODULE_LICENSE("GPL v2");
146039ae93e3SKenneth Heitke MODULE_ALIAS("platform:spmi_pmic_arb");
1461