xref: /linux/drivers/spmi/spmi-pmic-arb.c (revision 12a9eeaebba34f4f0abe1548ecb460414e285c49)
1d0c6ae41SGilad Avidov /*
253d296b5SFenglin Wu  * Copyright (c) 2012-2015, 2017, The Linux Foundation. All rights reserved.
339ae93e3SKenneth Heitke  *
439ae93e3SKenneth Heitke  * This program is free software; you can redistribute it and/or modify
539ae93e3SKenneth Heitke  * it under the terms of the GNU General Public License version 2 and
639ae93e3SKenneth Heitke  * only version 2 as published by the Free Software Foundation.
739ae93e3SKenneth Heitke  *
839ae93e3SKenneth Heitke  * This program is distributed in the hope that it will be useful,
939ae93e3SKenneth Heitke  * but WITHOUT ANY WARRANTY; without even the implied warranty of
1039ae93e3SKenneth Heitke  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1139ae93e3SKenneth Heitke  * GNU General Public License for more details.
1239ae93e3SKenneth Heitke  */
13987a9f12SStephen Boyd #include <linux/bitmap.h>
1439ae93e3SKenneth Heitke #include <linux/delay.h>
1539ae93e3SKenneth Heitke #include <linux/err.h>
1639ae93e3SKenneth Heitke #include <linux/interrupt.h>
1739ae93e3SKenneth Heitke #include <linux/io.h>
1867b563f1SJosh Cartwright #include <linux/irqchip/chained_irq.h>
1967b563f1SJosh Cartwright #include <linux/irqdomain.h>
2067b563f1SJosh Cartwright #include <linux/irq.h>
2139ae93e3SKenneth Heitke #include <linux/kernel.h>
2239ae93e3SKenneth Heitke #include <linux/module.h>
2339ae93e3SKenneth Heitke #include <linux/of.h>
2439ae93e3SKenneth Heitke #include <linux/platform_device.h>
2539ae93e3SKenneth Heitke #include <linux/slab.h>
2639ae93e3SKenneth Heitke #include <linux/spmi.h>
2739ae93e3SKenneth Heitke 
2839ae93e3SKenneth Heitke /* PMIC Arbiter configuration registers */
2939ae93e3SKenneth Heitke #define PMIC_ARB_VERSION		0x0000
30d0c6ae41SGilad Avidov #define PMIC_ARB_VERSION_V2_MIN		0x20010000
31319f6884SAbhijeet Dharmapurikar #define PMIC_ARB_VERSION_V3_MIN		0x30000000
3240f318f0SDavid Collins #define PMIC_ARB_VERSION_V5_MIN		0x50000000
3339ae93e3SKenneth Heitke #define PMIC_ARB_INT_EN			0x0004
3439ae93e3SKenneth Heitke 
35d0c6ae41SGilad Avidov /* PMIC Arbiter channel registers offsets */
36d0c6ae41SGilad Avidov #define PMIC_ARB_CMD			0x00
37d0c6ae41SGilad Avidov #define PMIC_ARB_CONFIG			0x04
38d0c6ae41SGilad Avidov #define PMIC_ARB_STATUS			0x08
39d0c6ae41SGilad Avidov #define PMIC_ARB_WDATA0			0x10
40d0c6ae41SGilad Avidov #define PMIC_ARB_WDATA1			0x14
41d0c6ae41SGilad Avidov #define PMIC_ARB_RDATA0			0x18
42d0c6ae41SGilad Avidov #define PMIC_ARB_RDATA1			0x1C
4339ae93e3SKenneth Heitke 
4439ae93e3SKenneth Heitke /* Mapping Table */
4539ae93e3SKenneth Heitke #define SPMI_MAPPING_TABLE_REG(N)	(0x0B00 + (4 * (N)))
4639ae93e3SKenneth Heitke #define SPMI_MAPPING_BIT_INDEX(X)	(((X) >> 18) & 0xF)
4739ae93e3SKenneth Heitke #define SPMI_MAPPING_BIT_IS_0_FLAG(X)	(((X) >> 17) & 0x1)
4839ae93e3SKenneth Heitke #define SPMI_MAPPING_BIT_IS_0_RESULT(X)	(((X) >> 9) & 0xFF)
4939ae93e3SKenneth Heitke #define SPMI_MAPPING_BIT_IS_1_FLAG(X)	(((X) >> 8) & 0x1)
5039ae93e3SKenneth Heitke #define SPMI_MAPPING_BIT_IS_1_RESULT(X)	(((X) >> 0) & 0xFF)
5139ae93e3SKenneth Heitke 
5239ae93e3SKenneth Heitke #define SPMI_MAPPING_TABLE_TREE_DEPTH	16	/* Maximum of 16-bits */
53987a9f12SStephen Boyd #define PMIC_ARB_MAX_PPID		BIT(12) /* PPID is 12bit */
5402abec36SKiran Gunda #define PMIC_ARB_APID_VALID		BIT(15)
5540f318f0SDavid Collins #define PMIC_ARB_CHAN_IS_IRQ_OWNER(reg)	((reg) & BIT(24))
5640f318f0SDavid Collins #define INVALID_EE				0xFF
5739ae93e3SKenneth Heitke 
5839ae93e3SKenneth Heitke /* Ownership Table */
5939ae93e3SKenneth Heitke #define SPMI_OWNERSHIP_TABLE_REG(N)	(0x0700 + (4 * (N)))
6039ae93e3SKenneth Heitke #define SPMI_OWNERSHIP_PERIPH2OWNER(X)	((X) & 0x7)
6139ae93e3SKenneth Heitke 
6239ae93e3SKenneth Heitke /* Channel Status fields */
6339ae93e3SKenneth Heitke enum pmic_arb_chnl_status {
64111a10bfSAbhijeet Dharmapurikar 	PMIC_ARB_STATUS_DONE	= BIT(0),
65111a10bfSAbhijeet Dharmapurikar 	PMIC_ARB_STATUS_FAILURE	= BIT(1),
66111a10bfSAbhijeet Dharmapurikar 	PMIC_ARB_STATUS_DENIED	= BIT(2),
67111a10bfSAbhijeet Dharmapurikar 	PMIC_ARB_STATUS_DROPPED	= BIT(3),
6839ae93e3SKenneth Heitke };
6939ae93e3SKenneth Heitke 
7039ae93e3SKenneth Heitke /* Command register fields */
7139ae93e3SKenneth Heitke #define PMIC_ARB_CMD_MAX_BYTE_COUNT	8
7239ae93e3SKenneth Heitke 
7339ae93e3SKenneth Heitke /* Command Opcodes */
7439ae93e3SKenneth Heitke enum pmic_arb_cmd_op_code {
7539ae93e3SKenneth Heitke 	PMIC_ARB_OP_EXT_WRITEL = 0,
7639ae93e3SKenneth Heitke 	PMIC_ARB_OP_EXT_READL = 1,
7739ae93e3SKenneth Heitke 	PMIC_ARB_OP_EXT_WRITE = 2,
7839ae93e3SKenneth Heitke 	PMIC_ARB_OP_RESET = 3,
7939ae93e3SKenneth Heitke 	PMIC_ARB_OP_SLEEP = 4,
8039ae93e3SKenneth Heitke 	PMIC_ARB_OP_SHUTDOWN = 5,
8139ae93e3SKenneth Heitke 	PMIC_ARB_OP_WAKEUP = 6,
8239ae93e3SKenneth Heitke 	PMIC_ARB_OP_AUTHENTICATE = 7,
8339ae93e3SKenneth Heitke 	PMIC_ARB_OP_MSTR_READ = 8,
8439ae93e3SKenneth Heitke 	PMIC_ARB_OP_MSTR_WRITE = 9,
8539ae93e3SKenneth Heitke 	PMIC_ARB_OP_EXT_READ = 13,
8639ae93e3SKenneth Heitke 	PMIC_ARB_OP_WRITE = 14,
8739ae93e3SKenneth Heitke 	PMIC_ARB_OP_READ = 15,
8839ae93e3SKenneth Heitke 	PMIC_ARB_OP_ZERO_WRITE = 16,
8939ae93e3SKenneth Heitke };
9039ae93e3SKenneth Heitke 
9140f318f0SDavid Collins /*
9240f318f0SDavid Collins  * PMIC arbiter version 5 uses different register offsets for read/write vs
9340f318f0SDavid Collins  * observer channels.
9440f318f0SDavid Collins  */
9540f318f0SDavid Collins enum pmic_arb_channel {
9640f318f0SDavid Collins 	PMIC_ARB_CHANNEL_RW,
9740f318f0SDavid Collins 	PMIC_ARB_CHANNEL_OBS,
9840f318f0SDavid Collins };
9940f318f0SDavid Collins 
10039ae93e3SKenneth Heitke /* Maximum number of support PMIC peripherals */
101987a9f12SStephen Boyd #define PMIC_ARB_MAX_PERIPHS		512
10239ae93e3SKenneth Heitke #define PMIC_ARB_TIMEOUT_US		100
10339ae93e3SKenneth Heitke #define PMIC_ARB_MAX_TRANS_BYTES	(8)
10439ae93e3SKenneth Heitke 
10539ae93e3SKenneth Heitke #define PMIC_ARB_APID_MASK		0xFF
10639ae93e3SKenneth Heitke #define PMIC_ARB_PPID_MASK		0xFFF
10739ae93e3SKenneth Heitke 
10839ae93e3SKenneth Heitke /* interrupt enable bit */
10939ae93e3SKenneth Heitke #define SPMI_PIC_ACC_ENABLE_BIT		BIT(0)
11039ae93e3SKenneth Heitke 
11102abec36SKiran Gunda #define spec_to_hwirq(slave_id, periph_id, irq_id, apid) \
112319f6884SAbhijeet Dharmapurikar 	((((slave_id) & 0xF)   << 28) | \
113319f6884SAbhijeet Dharmapurikar 	(((periph_id) & 0xFF)  << 20) | \
114319f6884SAbhijeet Dharmapurikar 	(((irq_id)    & 0x7)   << 16) | \
115319f6884SAbhijeet Dharmapurikar 	(((apid)      & 0x1FF) << 0))
116319f6884SAbhijeet Dharmapurikar 
11702abec36SKiran Gunda #define hwirq_to_sid(hwirq)  (((hwirq) >> 28) & 0xF)
11802abec36SKiran Gunda #define hwirq_to_per(hwirq)  (((hwirq) >> 20) & 0xFF)
11902abec36SKiran Gunda #define hwirq_to_irq(hwirq)  (((hwirq) >> 16) & 0x7)
12002abec36SKiran Gunda #define hwirq_to_apid(hwirq) (((hwirq) >> 0)  & 0x1FF)
121319f6884SAbhijeet Dharmapurikar 
122d0c6ae41SGilad Avidov struct pmic_arb_ver_ops;
123d0c6ae41SGilad Avidov 
1246bc546e7SAbhijeet Dharmapurikar struct apid_data {
1256bc546e7SAbhijeet Dharmapurikar 	u16		ppid;
12640f318f0SDavid Collins 	u8		write_ee;
12740f318f0SDavid Collins 	u8		irq_ee;
1286bc546e7SAbhijeet Dharmapurikar };
1296bc546e7SAbhijeet Dharmapurikar 
13039ae93e3SKenneth Heitke /**
131111a10bfSAbhijeet Dharmapurikar  * spmi_pmic_arb - SPMI PMIC Arbiter object
13239ae93e3SKenneth Heitke  *
133d0c6ae41SGilad Avidov  * @rd_base:		on v1 "core", on v2 "observer" register base off DT.
134d0c6ae41SGilad Avidov  * @wr_base:		on v1 "core", on v2 "chnls"    register base off DT.
13539ae93e3SKenneth Heitke  * @intr:		address of the SPMI interrupt control registers.
13639ae93e3SKenneth Heitke  * @cnfg:		address of the PMIC Arbiter configuration registers.
13739ae93e3SKenneth Heitke  * @lock:		lock to synchronize accesses.
138d0c6ae41SGilad Avidov  * @channel:		execution environment channel to use for accesses.
13967b563f1SJosh Cartwright  * @irq:		PMIC ARB interrupt.
14067b563f1SJosh Cartwright  * @ee:			the current Execution Environment
14167b563f1SJosh Cartwright  * @min_apid:		minimum APID (used for bounding IRQ search)
14267b563f1SJosh Cartwright  * @max_apid:		maximum APID
14367b563f1SJosh Cartwright  * @mapping_table:	in-memory copy of PPID -> APID mapping table.
14467b563f1SJosh Cartwright  * @domain:		irq domain object for PMIC IRQ domain
14567b563f1SJosh Cartwright  * @spmic:		SPMI controller object
146d0c6ae41SGilad Avidov  * @ver_ops:		version dependent operations.
14702abec36SKiran Gunda  * @ppid_to_apid	in-memory copy of PPID -> APID mapping table.
14839ae93e3SKenneth Heitke  */
149111a10bfSAbhijeet Dharmapurikar struct spmi_pmic_arb {
150d0c6ae41SGilad Avidov 	void __iomem		*rd_base;
151d0c6ae41SGilad Avidov 	void __iomem		*wr_base;
15239ae93e3SKenneth Heitke 	void __iomem		*intr;
15339ae93e3SKenneth Heitke 	void __iomem		*cnfg;
154987a9f12SStephen Boyd 	void __iomem		*core;
155987a9f12SStephen Boyd 	resource_size_t		core_size;
15639ae93e3SKenneth Heitke 	raw_spinlock_t		lock;
15739ae93e3SKenneth Heitke 	u8			channel;
15867b563f1SJosh Cartwright 	int			irq;
15967b563f1SJosh Cartwright 	u8			ee;
160987a9f12SStephen Boyd 	u16			min_apid;
161987a9f12SStephen Boyd 	u16			max_apid;
162987a9f12SStephen Boyd 	u32			*mapping_table;
163987a9f12SStephen Boyd 	DECLARE_BITMAP(mapping_table_valid, PMIC_ARB_MAX_PERIPHS);
16467b563f1SJosh Cartwright 	struct irq_domain	*domain;
16567b563f1SJosh Cartwright 	struct spmi_controller	*spmic;
166d0c6ae41SGilad Avidov 	const struct pmic_arb_ver_ops *ver_ops;
1671ef1ce4eSAbhijeet Dharmapurikar 	u16			*ppid_to_apid;
1681ef1ce4eSAbhijeet Dharmapurikar 	u16			last_apid;
1696bc546e7SAbhijeet Dharmapurikar 	struct apid_data	apid_data[PMIC_ARB_MAX_PERIPHS];
170d0c6ae41SGilad Avidov };
171d0c6ae41SGilad Avidov 
172d0c6ae41SGilad Avidov /**
173d0c6ae41SGilad Avidov  * pmic_arb_ver: version dependent functionality.
174d0c6ae41SGilad Avidov  *
175319f6884SAbhijeet Dharmapurikar  * @ver_str:		version string.
176319f6884SAbhijeet Dharmapurikar  * @ppid_to_apid:	finds the apid for a given ppid.
177d0c6ae41SGilad Avidov  * @non_data_cmd:	on v1 issues an spmi non-data command.
178d0c6ae41SGilad Avidov  *			on v2 no HW support, returns -EOPNOTSUPP.
179d0c6ae41SGilad Avidov  * @offset:		on v1 offset of per-ee channel.
180d0c6ae41SGilad Avidov  *			on v2 offset of per-ee and per-ppid channel.
181d0c6ae41SGilad Avidov  * @fmt_cmd:		formats a GENI/SPMI command.
182e95d073cSKiran Gunda  * @owner_acc_status:	on v1 address of PMIC_ARB_SPMI_PIC_OWNERm_ACC_STATUSn
183e95d073cSKiran Gunda  *			on v2 address of SPMI_PIC_OWNERm_ACC_STATUSn.
184e95d073cSKiran Gunda  * @acc_enable:		on v1 address of PMIC_ARB_SPMI_PIC_ACC_ENABLEn
185e95d073cSKiran Gunda  *			on v2 address of SPMI_PIC_ACC_ENABLEn.
186e95d073cSKiran Gunda  * @irq_status:		on v1 address of PMIC_ARB_SPMI_PIC_IRQ_STATUSn
187e95d073cSKiran Gunda  *			on v2 address of SPMI_PIC_IRQ_STATUSn.
188e95d073cSKiran Gunda  * @irq_clear:		on v1 address of PMIC_ARB_SPMI_PIC_IRQ_CLEARn
189e95d073cSKiran Gunda  *			on v2 address of SPMI_PIC_IRQ_CLEARn.
19040f318f0SDavid Collins  * @apid_map_offset:	offset of PMIC_ARB_REG_CHNLn
191d0c6ae41SGilad Avidov  */
192d0c6ae41SGilad Avidov struct pmic_arb_ver_ops {
193319f6884SAbhijeet Dharmapurikar 	const char *ver_str;
194ff615ed9SKiran Gunda 	int (*ppid_to_apid)(struct spmi_pmic_arb *pmic_arb, u16 ppid);
195d0c6ae41SGilad Avidov 	/* spmi commands (read_cmd, write_cmd, cmd) functionality */
19640f318f0SDavid Collins 	int (*offset)(struct spmi_pmic_arb *pmic_arb, u8 sid, u16 addr,
19740f318f0SDavid Collins 			enum pmic_arb_channel ch_type);
198d0c6ae41SGilad Avidov 	u32 (*fmt_cmd)(u8 opc, u8 sid, u16 addr, u8 bc);
199d0c6ae41SGilad Avidov 	int (*non_data_cmd)(struct spmi_controller *ctrl, u8 opc, u8 sid);
200d0c6ae41SGilad Avidov 	/* Interrupts controller functionality (offset of PIC registers) */
201e95d073cSKiran Gunda 	void __iomem *(*owner_acc_status)(struct spmi_pmic_arb *pmic_arb, u8 m,
202e95d073cSKiran Gunda 					  u16 n);
203e95d073cSKiran Gunda 	void __iomem *(*acc_enable)(struct spmi_pmic_arb *pmic_arb, u16 n);
204e95d073cSKiran Gunda 	void __iomem *(*irq_status)(struct spmi_pmic_arb *pmic_arb, u16 n);
205e95d073cSKiran Gunda 	void __iomem *(*irq_clear)(struct spmi_pmic_arb *pmic_arb, u16 n);
20640f318f0SDavid Collins 	u32 (*apid_map_offset)(u16 n);
20739ae93e3SKenneth Heitke };
20839ae93e3SKenneth Heitke 
20902abec36SKiran Gunda static inline void pmic_arb_base_write(struct spmi_pmic_arb *pmic_arb,
21039ae93e3SKenneth Heitke 				       u32 offset, u32 val)
21139ae93e3SKenneth Heitke {
21202abec36SKiran Gunda 	writel_relaxed(val, pmic_arb->wr_base + offset);
213d0c6ae41SGilad Avidov }
214d0c6ae41SGilad Avidov 
21502abec36SKiran Gunda static inline void pmic_arb_set_rd_cmd(struct spmi_pmic_arb *pmic_arb,
216d0c6ae41SGilad Avidov 				       u32 offset, u32 val)
217d0c6ae41SGilad Avidov {
21802abec36SKiran Gunda 	writel_relaxed(val, pmic_arb->rd_base + offset);
21939ae93e3SKenneth Heitke }
22039ae93e3SKenneth Heitke 
22139ae93e3SKenneth Heitke /**
22202abec36SKiran Gunda  * pmic_arb_read_data: reads pmic-arb's register and copy 1..4 bytes to buf
22339ae93e3SKenneth Heitke  * @bc:		byte count -1. range: 0..3
22439ae93e3SKenneth Heitke  * @reg:	register's address
22539ae93e3SKenneth Heitke  * @buf:	output parameter, length must be bc + 1
22639ae93e3SKenneth Heitke  */
22702abec36SKiran Gunda static void
22802abec36SKiran Gunda pmic_arb_read_data(struct spmi_pmic_arb *pmic_arb, u8 *buf, u32 reg, u8 bc)
22939ae93e3SKenneth Heitke {
23002abec36SKiran Gunda 	u32 data = __raw_readl(pmic_arb->rd_base + reg);
231111a10bfSAbhijeet Dharmapurikar 
23239ae93e3SKenneth Heitke 	memcpy(buf, &data, (bc & 3) + 1);
23339ae93e3SKenneth Heitke }
23439ae93e3SKenneth Heitke 
23539ae93e3SKenneth Heitke /**
23602abec36SKiran Gunda  * pmic_arb_write_data: write 1..4 bytes from buf to pmic-arb's register
23739ae93e3SKenneth Heitke  * @bc:		byte-count -1. range: 0..3.
23839ae93e3SKenneth Heitke  * @reg:	register's address.
23939ae93e3SKenneth Heitke  * @buf:	buffer to write. length must be bc + 1.
24039ae93e3SKenneth Heitke  */
24102abec36SKiran Gunda static void pmic_arb_write_data(struct spmi_pmic_arb *pmic_arb, const u8 *buf,
24202abec36SKiran Gunda 				u32 reg, u8 bc)
24339ae93e3SKenneth Heitke {
24439ae93e3SKenneth Heitke 	u32 data = 0;
245111a10bfSAbhijeet Dharmapurikar 
24639ae93e3SKenneth Heitke 	memcpy(&data, buf, (bc & 3) + 1);
2479f7a9a44SKiran Gunda 	__raw_writel(data, pmic_arb->wr_base + reg);
24839ae93e3SKenneth Heitke }
24939ae93e3SKenneth Heitke 
250d0c6ae41SGilad Avidov static int pmic_arb_wait_for_done(struct spmi_controller *ctrl,
25140f318f0SDavid Collins 				  void __iomem *base, u8 sid, u16 addr,
25240f318f0SDavid Collins 				  enum pmic_arb_channel ch_type)
25339ae93e3SKenneth Heitke {
25402abec36SKiran Gunda 	struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl);
25539ae93e3SKenneth Heitke 	u32 status = 0;
25639ae93e3SKenneth Heitke 	u32 timeout = PMIC_ARB_TIMEOUT_US;
257987a9f12SStephen Boyd 	u32 offset;
258987a9f12SStephen Boyd 	int rc;
259987a9f12SStephen Boyd 
26040f318f0SDavid Collins 	rc = pmic_arb->ver_ops->offset(pmic_arb, sid, addr, ch_type);
261ff615ed9SKiran Gunda 	if (rc < 0)
262987a9f12SStephen Boyd 		return rc;
263987a9f12SStephen Boyd 
264ff615ed9SKiran Gunda 	offset = rc;
265987a9f12SStephen Boyd 	offset += PMIC_ARB_STATUS;
26639ae93e3SKenneth Heitke 
26739ae93e3SKenneth Heitke 	while (timeout--) {
268d0c6ae41SGilad Avidov 		status = readl_relaxed(base + offset);
26939ae93e3SKenneth Heitke 
27039ae93e3SKenneth Heitke 		if (status & PMIC_ARB_STATUS_DONE) {
27139ae93e3SKenneth Heitke 			if (status & PMIC_ARB_STATUS_DENIED) {
27202abec36SKiran Gunda 				dev_err(&ctrl->dev, "%s: transaction denied (0x%x)\n",
27339ae93e3SKenneth Heitke 					__func__, status);
27439ae93e3SKenneth Heitke 				return -EPERM;
27539ae93e3SKenneth Heitke 			}
27639ae93e3SKenneth Heitke 
27739ae93e3SKenneth Heitke 			if (status & PMIC_ARB_STATUS_FAILURE) {
27802abec36SKiran Gunda 				dev_err(&ctrl->dev, "%s: transaction failed (0x%x)\n",
27939ae93e3SKenneth Heitke 					__func__, status);
28039ae93e3SKenneth Heitke 				return -EIO;
28139ae93e3SKenneth Heitke 			}
28239ae93e3SKenneth Heitke 
28339ae93e3SKenneth Heitke 			if (status & PMIC_ARB_STATUS_DROPPED) {
28402abec36SKiran Gunda 				dev_err(&ctrl->dev, "%s: transaction dropped (0x%x)\n",
28539ae93e3SKenneth Heitke 					__func__, status);
28639ae93e3SKenneth Heitke 				return -EIO;
28739ae93e3SKenneth Heitke 			}
28839ae93e3SKenneth Heitke 
28939ae93e3SKenneth Heitke 			return 0;
29039ae93e3SKenneth Heitke 		}
29139ae93e3SKenneth Heitke 		udelay(1);
29239ae93e3SKenneth Heitke 	}
29339ae93e3SKenneth Heitke 
29402abec36SKiran Gunda 	dev_err(&ctrl->dev, "%s: timeout, status 0x%x\n",
29539ae93e3SKenneth Heitke 		__func__, status);
29639ae93e3SKenneth Heitke 	return -ETIMEDOUT;
29739ae93e3SKenneth Heitke }
29839ae93e3SKenneth Heitke 
299d0c6ae41SGilad Avidov static int
300d0c6ae41SGilad Avidov pmic_arb_non_data_cmd_v1(struct spmi_controller *ctrl, u8 opc, u8 sid)
30139ae93e3SKenneth Heitke {
30202abec36SKiran Gunda 	struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl);
30339ae93e3SKenneth Heitke 	unsigned long flags;
30439ae93e3SKenneth Heitke 	u32 cmd;
30539ae93e3SKenneth Heitke 	int rc;
306987a9f12SStephen Boyd 	u32 offset;
307987a9f12SStephen Boyd 
30840f318f0SDavid Collins 	rc = pmic_arb->ver_ops->offset(pmic_arb, sid, 0, PMIC_ARB_CHANNEL_RW);
309ff615ed9SKiran Gunda 	if (rc < 0)
310987a9f12SStephen Boyd 		return rc;
311d0c6ae41SGilad Avidov 
312ff615ed9SKiran Gunda 	offset = rc;
313d0c6ae41SGilad Avidov 	cmd = ((opc | 0x40) << 27) | ((sid & 0xf) << 20);
314d0c6ae41SGilad Avidov 
31502abec36SKiran Gunda 	raw_spin_lock_irqsave(&pmic_arb->lock, flags);
31602abec36SKiran Gunda 	pmic_arb_base_write(pmic_arb, offset + PMIC_ARB_CMD, cmd);
31740f318f0SDavid Collins 	rc = pmic_arb_wait_for_done(ctrl, pmic_arb->wr_base, sid, 0,
31840f318f0SDavid Collins 				    PMIC_ARB_CHANNEL_RW);
31902abec36SKiran Gunda 	raw_spin_unlock_irqrestore(&pmic_arb->lock, flags);
320d0c6ae41SGilad Avidov 
321d0c6ae41SGilad Avidov 	return rc;
322d0c6ae41SGilad Avidov }
323d0c6ae41SGilad Avidov 
324d0c6ae41SGilad Avidov static int
325d0c6ae41SGilad Avidov pmic_arb_non_data_cmd_v2(struct spmi_controller *ctrl, u8 opc, u8 sid)
326d0c6ae41SGilad Avidov {
327d0c6ae41SGilad Avidov 	return -EOPNOTSUPP;
328d0c6ae41SGilad Avidov }
329d0c6ae41SGilad Avidov 
330d0c6ae41SGilad Avidov /* Non-data command */
331d0c6ae41SGilad Avidov static int pmic_arb_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid)
332d0c6ae41SGilad Avidov {
33302abec36SKiran Gunda 	struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl);
334d0c6ae41SGilad Avidov 
335d0c6ae41SGilad Avidov 	dev_dbg(&ctrl->dev, "cmd op:0x%x sid:%d\n", opc, sid);
33639ae93e3SKenneth Heitke 
33739ae93e3SKenneth Heitke 	/* Check for valid non-data command */
33839ae93e3SKenneth Heitke 	if (opc < SPMI_CMD_RESET || opc > SPMI_CMD_WAKEUP)
33939ae93e3SKenneth Heitke 		return -EINVAL;
34039ae93e3SKenneth Heitke 
34102abec36SKiran Gunda 	return pmic_arb->ver_ops->non_data_cmd(ctrl, opc, sid);
34239ae93e3SKenneth Heitke }
34339ae93e3SKenneth Heitke 
34439ae93e3SKenneth Heitke static int pmic_arb_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
34539ae93e3SKenneth Heitke 			     u16 addr, u8 *buf, size_t len)
34639ae93e3SKenneth Heitke {
34702abec36SKiran Gunda 	struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl);
34839ae93e3SKenneth Heitke 	unsigned long flags;
34939ae93e3SKenneth Heitke 	u8 bc = len - 1;
35039ae93e3SKenneth Heitke 	u32 cmd;
35139ae93e3SKenneth Heitke 	int rc;
352987a9f12SStephen Boyd 	u32 offset;
353987a9f12SStephen Boyd 
35440f318f0SDavid Collins 	rc = pmic_arb->ver_ops->offset(pmic_arb, sid, addr,
35540f318f0SDavid Collins 				       PMIC_ARB_CHANNEL_OBS);
356ff615ed9SKiran Gunda 	if (rc < 0)
357987a9f12SStephen Boyd 		return rc;
35839ae93e3SKenneth Heitke 
359ff615ed9SKiran Gunda 	offset = rc;
36039ae93e3SKenneth Heitke 	if (bc >= PMIC_ARB_MAX_TRANS_BYTES) {
36102abec36SKiran Gunda 		dev_err(&ctrl->dev, "pmic-arb supports 1..%d bytes per trans, but:%zu requested",
36239ae93e3SKenneth Heitke 			PMIC_ARB_MAX_TRANS_BYTES, len);
36339ae93e3SKenneth Heitke 		return  -EINVAL;
36439ae93e3SKenneth Heitke 	}
36539ae93e3SKenneth Heitke 
36639ae93e3SKenneth Heitke 	/* Check the opcode */
36739ae93e3SKenneth Heitke 	if (opc >= 0x60 && opc <= 0x7F)
36839ae93e3SKenneth Heitke 		opc = PMIC_ARB_OP_READ;
36939ae93e3SKenneth Heitke 	else if (opc >= 0x20 && opc <= 0x2F)
37039ae93e3SKenneth Heitke 		opc = PMIC_ARB_OP_EXT_READ;
37139ae93e3SKenneth Heitke 	else if (opc >= 0x38 && opc <= 0x3F)
37239ae93e3SKenneth Heitke 		opc = PMIC_ARB_OP_EXT_READL;
37339ae93e3SKenneth Heitke 	else
37439ae93e3SKenneth Heitke 		return -EINVAL;
37539ae93e3SKenneth Heitke 
37602abec36SKiran Gunda 	cmd = pmic_arb->ver_ops->fmt_cmd(opc, sid, addr, bc);
37739ae93e3SKenneth Heitke 
37802abec36SKiran Gunda 	raw_spin_lock_irqsave(&pmic_arb->lock, flags);
37902abec36SKiran Gunda 	pmic_arb_set_rd_cmd(pmic_arb, offset + PMIC_ARB_CMD, cmd);
38040f318f0SDavid Collins 	rc = pmic_arb_wait_for_done(ctrl, pmic_arb->rd_base, sid, addr,
38140f318f0SDavid Collins 				    PMIC_ARB_CHANNEL_OBS);
38239ae93e3SKenneth Heitke 	if (rc)
38339ae93e3SKenneth Heitke 		goto done;
38439ae93e3SKenneth Heitke 
38502abec36SKiran Gunda 	pmic_arb_read_data(pmic_arb, buf, offset + PMIC_ARB_RDATA0,
38639ae93e3SKenneth Heitke 		     min_t(u8, bc, 3));
38739ae93e3SKenneth Heitke 
38839ae93e3SKenneth Heitke 	if (bc > 3)
38902abec36SKiran Gunda 		pmic_arb_read_data(pmic_arb, buf + 4, offset + PMIC_ARB_RDATA1,
39002abec36SKiran Gunda 					bc - 4);
39139ae93e3SKenneth Heitke 
39239ae93e3SKenneth Heitke done:
39302abec36SKiran Gunda 	raw_spin_unlock_irqrestore(&pmic_arb->lock, flags);
39439ae93e3SKenneth Heitke 	return rc;
39539ae93e3SKenneth Heitke }
39639ae93e3SKenneth Heitke 
39739ae93e3SKenneth Heitke static int pmic_arb_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
39839ae93e3SKenneth Heitke 			u16 addr, const u8 *buf, size_t len)
39939ae93e3SKenneth Heitke {
40002abec36SKiran Gunda 	struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl);
40139ae93e3SKenneth Heitke 	unsigned long flags;
40239ae93e3SKenneth Heitke 	u8 bc = len - 1;
40339ae93e3SKenneth Heitke 	u32 cmd;
40439ae93e3SKenneth Heitke 	int rc;
405987a9f12SStephen Boyd 	u32 offset;
406987a9f12SStephen Boyd 
40740f318f0SDavid Collins 	rc = pmic_arb->ver_ops->offset(pmic_arb, sid, addr,
40840f318f0SDavid Collins 					PMIC_ARB_CHANNEL_RW);
409ff615ed9SKiran Gunda 	if (rc < 0)
410987a9f12SStephen Boyd 		return rc;
41139ae93e3SKenneth Heitke 
412ff615ed9SKiran Gunda 	offset = rc;
41339ae93e3SKenneth Heitke 	if (bc >= PMIC_ARB_MAX_TRANS_BYTES) {
41402abec36SKiran Gunda 		dev_err(&ctrl->dev, "pmic-arb supports 1..%d bytes per trans, but:%zu requested",
41539ae93e3SKenneth Heitke 			PMIC_ARB_MAX_TRANS_BYTES, len);
41639ae93e3SKenneth Heitke 		return  -EINVAL;
41739ae93e3SKenneth Heitke 	}
41839ae93e3SKenneth Heitke 
41939ae93e3SKenneth Heitke 	/* Check the opcode */
42039ae93e3SKenneth Heitke 	if (opc >= 0x40 && opc <= 0x5F)
42139ae93e3SKenneth Heitke 		opc = PMIC_ARB_OP_WRITE;
42253d296b5SFenglin Wu 	else if (opc <= 0x0F)
42339ae93e3SKenneth Heitke 		opc = PMIC_ARB_OP_EXT_WRITE;
42439ae93e3SKenneth Heitke 	else if (opc >= 0x30 && opc <= 0x37)
42539ae93e3SKenneth Heitke 		opc = PMIC_ARB_OP_EXT_WRITEL;
4269b76968dSStephen Boyd 	else if (opc >= 0x80)
42739ae93e3SKenneth Heitke 		opc = PMIC_ARB_OP_ZERO_WRITE;
42839ae93e3SKenneth Heitke 	else
42939ae93e3SKenneth Heitke 		return -EINVAL;
43039ae93e3SKenneth Heitke 
43102abec36SKiran Gunda 	cmd = pmic_arb->ver_ops->fmt_cmd(opc, sid, addr, bc);
43239ae93e3SKenneth Heitke 
43339ae93e3SKenneth Heitke 	/* Write data to FIFOs */
43402abec36SKiran Gunda 	raw_spin_lock_irqsave(&pmic_arb->lock, flags);
43502abec36SKiran Gunda 	pmic_arb_write_data(pmic_arb, buf, offset + PMIC_ARB_WDATA0,
43602abec36SKiran Gunda 				min_t(u8, bc, 3));
43739ae93e3SKenneth Heitke 	if (bc > 3)
43802abec36SKiran Gunda 		pmic_arb_write_data(pmic_arb, buf + 4, offset + PMIC_ARB_WDATA1,
43902abec36SKiran Gunda 					bc - 4);
44039ae93e3SKenneth Heitke 
44139ae93e3SKenneth Heitke 	/* Start the transaction */
44202abec36SKiran Gunda 	pmic_arb_base_write(pmic_arb, offset + PMIC_ARB_CMD, cmd);
44340f318f0SDavid Collins 	rc = pmic_arb_wait_for_done(ctrl, pmic_arb->wr_base, sid, addr,
44440f318f0SDavid Collins 				    PMIC_ARB_CHANNEL_RW);
44502abec36SKiran Gunda 	raw_spin_unlock_irqrestore(&pmic_arb->lock, flags);
44639ae93e3SKenneth Heitke 
44739ae93e3SKenneth Heitke 	return rc;
44839ae93e3SKenneth Heitke }
44939ae93e3SKenneth Heitke 
45067b563f1SJosh Cartwright enum qpnpint_regs {
45167b563f1SJosh Cartwright 	QPNPINT_REG_RT_STS		= 0x10,
45267b563f1SJosh Cartwright 	QPNPINT_REG_SET_TYPE		= 0x11,
45367b563f1SJosh Cartwright 	QPNPINT_REG_POLARITY_HIGH	= 0x12,
45467b563f1SJosh Cartwright 	QPNPINT_REG_POLARITY_LOW	= 0x13,
45567b563f1SJosh Cartwright 	QPNPINT_REG_LATCHED_CLR		= 0x14,
45667b563f1SJosh Cartwright 	QPNPINT_REG_EN_SET		= 0x15,
45767b563f1SJosh Cartwright 	QPNPINT_REG_EN_CLR		= 0x16,
45867b563f1SJosh Cartwright 	QPNPINT_REG_LATCHED_STS		= 0x18,
45967b563f1SJosh Cartwright };
46067b563f1SJosh Cartwright 
46167b563f1SJosh Cartwright struct spmi_pmic_arb_qpnpint_type {
46267b563f1SJosh Cartwright 	u8 type; /* 1 -> edge */
46367b563f1SJosh Cartwright 	u8 polarity_high;
46467b563f1SJosh Cartwright 	u8 polarity_low;
46567b563f1SJosh Cartwright } __packed;
46667b563f1SJosh Cartwright 
46767b563f1SJosh Cartwright /* Simplified accessor functions for irqchip callbacks */
46867b563f1SJosh Cartwright static void qpnpint_spmi_write(struct irq_data *d, u8 reg, void *buf,
46967b563f1SJosh Cartwright 			       size_t len)
47067b563f1SJosh Cartwright {
47102abec36SKiran Gunda 	struct spmi_pmic_arb *pmic_arb = irq_data_get_irq_chip_data(d);
47202abec36SKiran Gunda 	u8 sid = hwirq_to_sid(d->hwirq);
47302abec36SKiran Gunda 	u8 per = hwirq_to_per(d->hwirq);
47467b563f1SJosh Cartwright 
47502abec36SKiran Gunda 	if (pmic_arb_write_cmd(pmic_arb->spmic, SPMI_CMD_EXT_WRITEL, sid,
47667b563f1SJosh Cartwright 			       (per << 8) + reg, buf, len))
47702abec36SKiran Gunda 		dev_err_ratelimited(&pmic_arb->spmic->dev, "failed irqchip transaction on %x\n",
47867b563f1SJosh Cartwright 				    d->irq);
47967b563f1SJosh Cartwright }
48067b563f1SJosh Cartwright 
48167b563f1SJosh Cartwright static void qpnpint_spmi_read(struct irq_data *d, u8 reg, void *buf, size_t len)
48267b563f1SJosh Cartwright {
48302abec36SKiran Gunda 	struct spmi_pmic_arb *pmic_arb = irq_data_get_irq_chip_data(d);
48402abec36SKiran Gunda 	u8 sid = hwirq_to_sid(d->hwirq);
48502abec36SKiran Gunda 	u8 per = hwirq_to_per(d->hwirq);
48667b563f1SJosh Cartwright 
48702abec36SKiran Gunda 	if (pmic_arb_read_cmd(pmic_arb->spmic, SPMI_CMD_EXT_READL, sid,
48867b563f1SJosh Cartwright 			      (per << 8) + reg, buf, len))
48902abec36SKiran Gunda 		dev_err_ratelimited(&pmic_arb->spmic->dev, "failed irqchip transaction on %x\n",
49067b563f1SJosh Cartwright 				    d->irq);
49167b563f1SJosh Cartwright }
49267b563f1SJosh Cartwright 
49302abec36SKiran Gunda static void cleanup_irq(struct spmi_pmic_arb *pmic_arb, u16 apid, int id)
4946bc546e7SAbhijeet Dharmapurikar {
49502abec36SKiran Gunda 	u16 ppid = pmic_arb->apid_data[apid].ppid;
4966bc546e7SAbhijeet Dharmapurikar 	u8 sid = ppid >> 8;
4976bc546e7SAbhijeet Dharmapurikar 	u8 per = ppid & 0xFF;
4986bc546e7SAbhijeet Dharmapurikar 	u8 irq_mask = BIT(id);
4996bc546e7SAbhijeet Dharmapurikar 
500e95d073cSKiran Gunda 	writel_relaxed(irq_mask, pmic_arb->ver_ops->irq_clear(pmic_arb, apid));
5016bc546e7SAbhijeet Dharmapurikar 
50202abec36SKiran Gunda 	if (pmic_arb_write_cmd(pmic_arb->spmic, SPMI_CMD_EXT_WRITEL, sid,
5036bc546e7SAbhijeet Dharmapurikar 			(per << 8) + QPNPINT_REG_LATCHED_CLR, &irq_mask, 1))
50402abec36SKiran Gunda 		dev_err_ratelimited(&pmic_arb->spmic->dev, "failed to ack irq_mask = 0x%x for ppid = %x\n",
5056bc546e7SAbhijeet Dharmapurikar 				irq_mask, ppid);
5066bc546e7SAbhijeet Dharmapurikar 
50702abec36SKiran Gunda 	if (pmic_arb_write_cmd(pmic_arb->spmic, SPMI_CMD_EXT_WRITEL, sid,
5086bc546e7SAbhijeet Dharmapurikar 			       (per << 8) + QPNPINT_REG_EN_CLR, &irq_mask, 1))
50902abec36SKiran Gunda 		dev_err_ratelimited(&pmic_arb->spmic->dev, "failed to ack irq_mask = 0x%x for ppid = %x\n",
5106bc546e7SAbhijeet Dharmapurikar 				irq_mask, ppid);
5116bc546e7SAbhijeet Dharmapurikar }
5126bc546e7SAbhijeet Dharmapurikar 
51302abec36SKiran Gunda static void periph_interrupt(struct spmi_pmic_arb *pmic_arb, u16 apid)
51467b563f1SJosh Cartwright {
51567b563f1SJosh Cartwright 	unsigned int irq;
51667b563f1SJosh Cartwright 	u32 status;
51767b563f1SJosh Cartwright 	int id;
51802abec36SKiran Gunda 	u8 sid = (pmic_arb->apid_data[apid].ppid >> 8) & 0xF;
51902abec36SKiran Gunda 	u8 per = pmic_arb->apid_data[apid].ppid & 0xFF;
52067b563f1SJosh Cartwright 
521e95d073cSKiran Gunda 	status = readl_relaxed(pmic_arb->ver_ops->irq_status(pmic_arb, apid));
52267b563f1SJosh Cartwright 	while (status) {
52367b563f1SJosh Cartwright 		id = ffs(status) - 1;
524111a10bfSAbhijeet Dharmapurikar 		status &= ~BIT(id);
52502abec36SKiran Gunda 		irq = irq_find_mapping(pmic_arb->domain,
52602abec36SKiran Gunda 					spec_to_hwirq(sid, per, id, apid));
5276bc546e7SAbhijeet Dharmapurikar 		if (irq == 0) {
52802abec36SKiran Gunda 			cleanup_irq(pmic_arb, apid, id);
5296bc546e7SAbhijeet Dharmapurikar 			continue;
5306bc546e7SAbhijeet Dharmapurikar 		}
53167b563f1SJosh Cartwright 		generic_handle_irq(irq);
53267b563f1SJosh Cartwright 	}
53367b563f1SJosh Cartwright }
53467b563f1SJosh Cartwright 
535bd0b9ac4SThomas Gleixner static void pmic_arb_chained_irq(struct irq_desc *desc)
53667b563f1SJosh Cartwright {
53702abec36SKiran Gunda 	struct spmi_pmic_arb *pmic_arb = irq_desc_get_handler_data(desc);
538e95d073cSKiran Gunda 	const struct pmic_arb_ver_ops *ver_ops = pmic_arb->ver_ops;
5397fe88f3cSJiang Liu 	struct irq_chip *chip = irq_desc_get_chip(desc);
54002abec36SKiran Gunda 	int first = pmic_arb->min_apid >> 5;
54102abec36SKiran Gunda 	int last = pmic_arb->max_apid >> 5;
542e95d073cSKiran Gunda 	u8 ee = pmic_arb->ee;
543472eaf8bSAbhijeet Dharmapurikar 	u32 status, enable;
544472eaf8bSAbhijeet Dharmapurikar 	int i, id, apid;
54567b563f1SJosh Cartwright 
54667b563f1SJosh Cartwright 	chained_irq_enter(chip, desc);
54767b563f1SJosh Cartwright 
54867b563f1SJosh Cartwright 	for (i = first; i <= last; ++i) {
549e95d073cSKiran Gunda 		status = readl_relaxed(
550e95d073cSKiran Gunda 				ver_ops->owner_acc_status(pmic_arb, ee, i));
55167b563f1SJosh Cartwright 		while (status) {
55267b563f1SJosh Cartwright 			id = ffs(status) - 1;
553111a10bfSAbhijeet Dharmapurikar 			status &= ~BIT(id);
554472eaf8bSAbhijeet Dharmapurikar 			apid = id + i * 32;
555e95d073cSKiran Gunda 			enable = readl_relaxed(
556e95d073cSKiran Gunda 					ver_ops->acc_enable(pmic_arb, apid));
557472eaf8bSAbhijeet Dharmapurikar 			if (enable & SPMI_PIC_ACC_ENABLE_BIT)
55802abec36SKiran Gunda 				periph_interrupt(pmic_arb, apid);
55967b563f1SJosh Cartwright 		}
56067b563f1SJosh Cartwright 	}
56167b563f1SJosh Cartwright 
56267b563f1SJosh Cartwright 	chained_irq_exit(chip, desc);
56367b563f1SJosh Cartwright }
56467b563f1SJosh Cartwright 
56567b563f1SJosh Cartwright static void qpnpint_irq_ack(struct irq_data *d)
56667b563f1SJosh Cartwright {
56702abec36SKiran Gunda 	struct spmi_pmic_arb *pmic_arb = irq_data_get_irq_chip_data(d);
56802abec36SKiran Gunda 	u8 irq = hwirq_to_irq(d->hwirq);
56902abec36SKiran Gunda 	u16 apid = hwirq_to_apid(d->hwirq);
57067b563f1SJosh Cartwright 	u8 data;
57167b563f1SJosh Cartwright 
572e95d073cSKiran Gunda 	writel_relaxed(BIT(irq), pmic_arb->ver_ops->irq_clear(pmic_arb, apid));
57367b563f1SJosh Cartwright 
574111a10bfSAbhijeet Dharmapurikar 	data = BIT(irq);
57567b563f1SJosh Cartwright 	qpnpint_spmi_write(d, QPNPINT_REG_LATCHED_CLR, &data, 1);
57667b563f1SJosh Cartwright }
57767b563f1SJosh Cartwright 
57867b563f1SJosh Cartwright static void qpnpint_irq_mask(struct irq_data *d)
57967b563f1SJosh Cartwright {
58002abec36SKiran Gunda 	u8 irq = hwirq_to_irq(d->hwirq);
5816bc546e7SAbhijeet Dharmapurikar 	u8 data = BIT(irq);
58267b563f1SJosh Cartwright 
58367b563f1SJosh Cartwright 	qpnpint_spmi_write(d, QPNPINT_REG_EN_CLR, &data, 1);
58467b563f1SJosh Cartwright }
58567b563f1SJosh Cartwright 
58667b563f1SJosh Cartwright static void qpnpint_irq_unmask(struct irq_data *d)
58767b563f1SJosh Cartwright {
58802abec36SKiran Gunda 	struct spmi_pmic_arb *pmic_arb = irq_data_get_irq_chip_data(d);
589e95d073cSKiran Gunda 	const struct pmic_arb_ver_ops *ver_ops = pmic_arb->ver_ops;
59002abec36SKiran Gunda 	u8 irq = hwirq_to_irq(d->hwirq);
59102abec36SKiran Gunda 	u16 apid = hwirq_to_apid(d->hwirq);
592cee0fad7SAbhijeet Dharmapurikar 	u8 buf[2];
59367b563f1SJosh Cartwright 
5946bc546e7SAbhijeet Dharmapurikar 	writel_relaxed(SPMI_PIC_ACC_ENABLE_BIT,
595e95d073cSKiran Gunda 			ver_ops->acc_enable(pmic_arb, apid));
59667b563f1SJosh Cartwright 
597cee0fad7SAbhijeet Dharmapurikar 	qpnpint_spmi_read(d, QPNPINT_REG_EN_SET, &buf[0], 1);
598cee0fad7SAbhijeet Dharmapurikar 	if (!(buf[0] & BIT(irq))) {
599cee0fad7SAbhijeet Dharmapurikar 		/*
600cee0fad7SAbhijeet Dharmapurikar 		 * Since the interrupt is currently disabled, write to both the
601cee0fad7SAbhijeet Dharmapurikar 		 * LATCHED_CLR and EN_SET registers so that a spurious interrupt
602cee0fad7SAbhijeet Dharmapurikar 		 * cannot be triggered when the interrupt is enabled
603cee0fad7SAbhijeet Dharmapurikar 		 */
604cee0fad7SAbhijeet Dharmapurikar 		buf[0] = BIT(irq);
605cee0fad7SAbhijeet Dharmapurikar 		buf[1] = BIT(irq);
606cee0fad7SAbhijeet Dharmapurikar 		qpnpint_spmi_write(d, QPNPINT_REG_LATCHED_CLR, &buf, 2);
607cee0fad7SAbhijeet Dharmapurikar 	}
60867b563f1SJosh Cartwright }
60967b563f1SJosh Cartwright 
61067b563f1SJosh Cartwright static int qpnpint_irq_set_type(struct irq_data *d, unsigned int flow_type)
61167b563f1SJosh Cartwright {
61267b563f1SJosh Cartwright 	struct spmi_pmic_arb_qpnpint_type type;
613325255bcSKiran Gunda 	irq_flow_handler_t flow_handler;
61402abec36SKiran Gunda 	u8 irq = hwirq_to_irq(d->hwirq);
61567b563f1SJosh Cartwright 
61667b563f1SJosh Cartwright 	qpnpint_spmi_read(d, QPNPINT_REG_SET_TYPE, &type, sizeof(type));
61767b563f1SJosh Cartwright 
61867b563f1SJosh Cartwright 	if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) {
619325255bcSKiran Gunda 		type.type |= BIT(irq);
62067b563f1SJosh Cartwright 		if (flow_type & IRQF_TRIGGER_RISING)
621325255bcSKiran Gunda 			type.polarity_high |= BIT(irq);
62267b563f1SJosh Cartwright 		if (flow_type & IRQF_TRIGGER_FALLING)
623325255bcSKiran Gunda 			type.polarity_low  |= BIT(irq);
624325255bcSKiran Gunda 
625325255bcSKiran Gunda 		flow_handler = handle_edge_irq;
62667b563f1SJosh Cartwright 	} else {
62767b563f1SJosh Cartwright 		if ((flow_type & (IRQF_TRIGGER_HIGH)) &&
62867b563f1SJosh Cartwright 		    (flow_type & (IRQF_TRIGGER_LOW)))
62967b563f1SJosh Cartwright 			return -EINVAL;
63067b563f1SJosh Cartwright 
631325255bcSKiran Gunda 		type.type &= ~BIT(irq); /* level trig */
63267b563f1SJosh Cartwright 		if (flow_type & IRQF_TRIGGER_HIGH)
633325255bcSKiran Gunda 			type.polarity_high |= BIT(irq);
63467b563f1SJosh Cartwright 		else
635325255bcSKiran Gunda 			type.polarity_low  |= BIT(irq);
636325255bcSKiran Gunda 
637325255bcSKiran Gunda 		flow_handler = handle_level_irq;
63867b563f1SJosh Cartwright 	}
63967b563f1SJosh Cartwright 
64067b563f1SJosh Cartwright 	qpnpint_spmi_write(d, QPNPINT_REG_SET_TYPE, &type, sizeof(type));
641325255bcSKiran Gunda 	irq_set_handler_locked(d, flow_handler);
6425f9b2ea3SAbhijeet Dharmapurikar 
64367b563f1SJosh Cartwright 	return 0;
64467b563f1SJosh Cartwright }
64567b563f1SJosh Cartwright 
646cdeef07aSKiran Gunda static int qpnpint_irq_set_wake(struct irq_data *d, unsigned int on)
647cdeef07aSKiran Gunda {
648cdeef07aSKiran Gunda 	struct spmi_pmic_arb *pmic_arb = irq_data_get_irq_chip_data(d);
649cdeef07aSKiran Gunda 
650cdeef07aSKiran Gunda 	return irq_set_irq_wake(pmic_arb->irq, on);
651cdeef07aSKiran Gunda }
652cdeef07aSKiran Gunda 
65360be4230SCourtney Cavin static int qpnpint_get_irqchip_state(struct irq_data *d,
65460be4230SCourtney Cavin 				     enum irqchip_irq_state which,
65560be4230SCourtney Cavin 				     bool *state)
65660be4230SCourtney Cavin {
65702abec36SKiran Gunda 	u8 irq = hwirq_to_irq(d->hwirq);
65860be4230SCourtney Cavin 	u8 status = 0;
65960be4230SCourtney Cavin 
66060be4230SCourtney Cavin 	if (which != IRQCHIP_STATE_LINE_LEVEL)
66160be4230SCourtney Cavin 		return -EINVAL;
66260be4230SCourtney Cavin 
66360be4230SCourtney Cavin 	qpnpint_spmi_read(d, QPNPINT_REG_RT_STS, &status, 1);
66460be4230SCourtney Cavin 	*state = !!(status & BIT(irq));
66560be4230SCourtney Cavin 
66660be4230SCourtney Cavin 	return 0;
66760be4230SCourtney Cavin }
66860be4230SCourtney Cavin 
669*12a9eeaeSBrian Masney static int qpnpint_irq_domain_activate(struct irq_domain *domain,
670*12a9eeaeSBrian Masney 				       struct irq_data *d, bool reserve)
6712fb4f258SKiran Gunda {
6722fb4f258SKiran Gunda 	struct spmi_pmic_arb *pmic_arb = irq_data_get_irq_chip_data(d);
6732fb4f258SKiran Gunda 	u16 periph = hwirq_to_per(d->hwirq);
6742fb4f258SKiran Gunda 	u16 apid = hwirq_to_apid(d->hwirq);
6752fb4f258SKiran Gunda 	u16 sid = hwirq_to_sid(d->hwirq);
6762fb4f258SKiran Gunda 	u16 irq = hwirq_to_irq(d->hwirq);
6772fb4f258SKiran Gunda 
6782fb4f258SKiran Gunda 	if (pmic_arb->apid_data[apid].irq_ee != pmic_arb->ee) {
6792fb4f258SKiran Gunda 		dev_err(&pmic_arb->spmic->dev, "failed to xlate sid = %#x, periph = %#x, irq = %u: ee=%u but owner=%u\n",
6802fb4f258SKiran Gunda 			sid, periph, irq, pmic_arb->ee,
6812fb4f258SKiran Gunda 			pmic_arb->apid_data[apid].irq_ee);
6822fb4f258SKiran Gunda 		return -ENODEV;
6832fb4f258SKiran Gunda 	}
6842fb4f258SKiran Gunda 
6852fb4f258SKiran Gunda 	return 0;
6862fb4f258SKiran Gunda }
6872fb4f258SKiran Gunda 
68867b563f1SJosh Cartwright static struct irq_chip pmic_arb_irqchip = {
68967b563f1SJosh Cartwright 	.name		= "pmic_arb",
69067b563f1SJosh Cartwright 	.irq_ack	= qpnpint_irq_ack,
69167b563f1SJosh Cartwright 	.irq_mask	= qpnpint_irq_mask,
69267b563f1SJosh Cartwright 	.irq_unmask	= qpnpint_irq_unmask,
69367b563f1SJosh Cartwright 	.irq_set_type	= qpnpint_irq_set_type,
694cdeef07aSKiran Gunda 	.irq_set_wake	= qpnpint_irq_set_wake,
69560be4230SCourtney Cavin 	.irq_get_irqchip_state	= qpnpint_get_irqchip_state,
696cdeef07aSKiran Gunda 	.flags		= IRQCHIP_MASK_ON_SUSPEND,
69767b563f1SJosh Cartwright };
69867b563f1SJosh Cartwright 
699*12a9eeaeSBrian Masney static int qpnpint_irq_domain_translate(struct irq_domain *d,
700*12a9eeaeSBrian Masney 					struct irq_fwspec *fwspec,
70167b563f1SJosh Cartwright 					unsigned long *out_hwirq,
70267b563f1SJosh Cartwright 					unsigned int *out_type)
70367b563f1SJosh Cartwright {
70402abec36SKiran Gunda 	struct spmi_pmic_arb *pmic_arb = d->host_data;
705*12a9eeaeSBrian Masney 	u32 *intspec = fwspec->param;
706ff615ed9SKiran Gunda 	u16 apid, ppid;
7077f1d4e58SAbhijeet Dharmapurikar 	int rc;
70867b563f1SJosh Cartwright 
70902abec36SKiran Gunda 	dev_dbg(&pmic_arb->spmic->dev, "intspec[0] 0x%1x intspec[1] 0x%02x intspec[2] 0x%02x\n",
71067b563f1SJosh Cartwright 		intspec[0], intspec[1], intspec[2]);
71167b563f1SJosh Cartwright 
712*12a9eeaeSBrian Masney 	if (irq_domain_get_of_node(d) != pmic_arb->spmic->dev.of_node)
71367b563f1SJosh Cartwright 		return -EINVAL;
714*12a9eeaeSBrian Masney 	if (fwspec->param_count != 4)
71567b563f1SJosh Cartwright 		return -EINVAL;
71667b563f1SJosh Cartwright 	if (intspec[0] > 0xF || intspec[1] > 0xFF || intspec[2] > 0x7)
71767b563f1SJosh Cartwright 		return -EINVAL;
71867b563f1SJosh Cartwright 
719ff615ed9SKiran Gunda 	ppid = intspec[0] << 8 | intspec[1];
720ff615ed9SKiran Gunda 	rc = pmic_arb->ver_ops->ppid_to_apid(pmic_arb, ppid);
7217f1d4e58SAbhijeet Dharmapurikar 	if (rc < 0) {
72240f318f0SDavid Collins 		dev_err(&pmic_arb->spmic->dev, "failed to xlate sid = %#x, periph = %#x, irq = %u rc = %d\n",
7237f1d4e58SAbhijeet Dharmapurikar 		intspec[0], intspec[1], intspec[2], rc);
7247f1d4e58SAbhijeet Dharmapurikar 		return rc;
7257f1d4e58SAbhijeet Dharmapurikar 	}
72667b563f1SJosh Cartwright 
727ff615ed9SKiran Gunda 	apid = rc;
72867b563f1SJosh Cartwright 	/* Keep track of {max,min}_apid for bounding search during interrupt */
72902abec36SKiran Gunda 	if (apid > pmic_arb->max_apid)
73002abec36SKiran Gunda 		pmic_arb->max_apid = apid;
73102abec36SKiran Gunda 	if (apid < pmic_arb->min_apid)
73202abec36SKiran Gunda 		pmic_arb->min_apid = apid;
73367b563f1SJosh Cartwright 
73402abec36SKiran Gunda 	*out_hwirq = spec_to_hwirq(intspec[0], intspec[1], intspec[2], apid);
73567b563f1SJosh Cartwright 	*out_type  = intspec[3] & IRQ_TYPE_SENSE_MASK;
73667b563f1SJosh Cartwright 
73702abec36SKiran Gunda 	dev_dbg(&pmic_arb->spmic->dev, "out_hwirq = %lu\n", *out_hwirq);
73867b563f1SJosh Cartwright 
73967b563f1SJosh Cartwright 	return 0;
74067b563f1SJosh Cartwright }
74167b563f1SJosh Cartwright 
742*12a9eeaeSBrian Masney 
743*12a9eeaeSBrian Masney static void qpnpint_irq_domain_map(struct spmi_pmic_arb *pmic_arb,
744*12a9eeaeSBrian Masney 				   struct irq_domain *domain, unsigned int virq,
745*12a9eeaeSBrian Masney 				   irq_hw_number_t hwirq, unsigned int type)
74667b563f1SJosh Cartwright {
747*12a9eeaeSBrian Masney 	irq_flow_handler_t handler;
74867b563f1SJosh Cartwright 
749*12a9eeaeSBrian Masney 	dev_dbg(&pmic_arb->spmic->dev, "virq = %u, hwirq = %lu, type = %u\n",
750*12a9eeaeSBrian Masney 		virq, hwirq, type);
75167b563f1SJosh Cartwright 
752*12a9eeaeSBrian Masney 	if (type & IRQ_TYPE_EDGE_BOTH)
753*12a9eeaeSBrian Masney 		handler = handle_edge_irq;
754*12a9eeaeSBrian Masney 	else
755*12a9eeaeSBrian Masney 		handler = handle_level_irq;
756*12a9eeaeSBrian Masney 
757*12a9eeaeSBrian Masney 	irq_domain_set_info(domain, virq, hwirq, &pmic_arb_irqchip, pmic_arb,
758*12a9eeaeSBrian Masney 			    handler, NULL, NULL);
759*12a9eeaeSBrian Masney }
760*12a9eeaeSBrian Masney 
761*12a9eeaeSBrian Masney static int qpnpint_irq_domain_alloc(struct irq_domain *domain,
762*12a9eeaeSBrian Masney 				    unsigned int virq, unsigned int nr_irqs,
763*12a9eeaeSBrian Masney 				    void *data)
764*12a9eeaeSBrian Masney {
765*12a9eeaeSBrian Masney 	struct spmi_pmic_arb *pmic_arb = domain->host_data;
766*12a9eeaeSBrian Masney 	struct irq_fwspec *fwspec = data;
767*12a9eeaeSBrian Masney 	irq_hw_number_t hwirq;
768*12a9eeaeSBrian Masney 	unsigned int type;
769*12a9eeaeSBrian Masney 	int ret, i;
770*12a9eeaeSBrian Masney 
771*12a9eeaeSBrian Masney 	ret = qpnpint_irq_domain_translate(domain, fwspec, &hwirq, &type);
772*12a9eeaeSBrian Masney 	if (ret)
773*12a9eeaeSBrian Masney 		return ret;
774*12a9eeaeSBrian Masney 
775*12a9eeaeSBrian Masney 	for (i = 0; i < nr_irqs; i++)
776*12a9eeaeSBrian Masney 		qpnpint_irq_domain_map(pmic_arb, domain, virq + i, hwirq + i,
777*12a9eeaeSBrian Masney 				       type);
778*12a9eeaeSBrian Masney 
77967b563f1SJosh Cartwright 	return 0;
78067b563f1SJosh Cartwright }
78167b563f1SJosh Cartwright 
782ff615ed9SKiran Gunda static int pmic_arb_ppid_to_apid_v1(struct spmi_pmic_arb *pmic_arb, u16 ppid)
7837f1d4e58SAbhijeet Dharmapurikar {
78402abec36SKiran Gunda 	u32 *mapping_table = pmic_arb->mapping_table;
7857f1d4e58SAbhijeet Dharmapurikar 	int index = 0, i;
7867f1d4e58SAbhijeet Dharmapurikar 	u16 apid_valid;
787ff615ed9SKiran Gunda 	u16 apid;
7887f1d4e58SAbhijeet Dharmapurikar 	u32 data;
7897f1d4e58SAbhijeet Dharmapurikar 
79002abec36SKiran Gunda 	apid_valid = pmic_arb->ppid_to_apid[ppid];
79102abec36SKiran Gunda 	if (apid_valid & PMIC_ARB_APID_VALID) {
792ff615ed9SKiran Gunda 		apid = apid_valid & ~PMIC_ARB_APID_VALID;
793ff615ed9SKiran Gunda 		return apid;
7947f1d4e58SAbhijeet Dharmapurikar 	}
7957f1d4e58SAbhijeet Dharmapurikar 
7967f1d4e58SAbhijeet Dharmapurikar 	for (i = 0; i < SPMI_MAPPING_TABLE_TREE_DEPTH; ++i) {
79702abec36SKiran Gunda 		if (!test_and_set_bit(index, pmic_arb->mapping_table_valid))
79802abec36SKiran Gunda 			mapping_table[index] = readl_relaxed(pmic_arb->cnfg +
7997f1d4e58SAbhijeet Dharmapurikar 						SPMI_MAPPING_TABLE_REG(index));
8007f1d4e58SAbhijeet Dharmapurikar 
8017f1d4e58SAbhijeet Dharmapurikar 		data = mapping_table[index];
8027f1d4e58SAbhijeet Dharmapurikar 
8037f1d4e58SAbhijeet Dharmapurikar 		if (ppid & BIT(SPMI_MAPPING_BIT_INDEX(data))) {
8047f1d4e58SAbhijeet Dharmapurikar 			if (SPMI_MAPPING_BIT_IS_1_FLAG(data)) {
8057f1d4e58SAbhijeet Dharmapurikar 				index = SPMI_MAPPING_BIT_IS_1_RESULT(data);
8067f1d4e58SAbhijeet Dharmapurikar 			} else {
807ff615ed9SKiran Gunda 				apid = SPMI_MAPPING_BIT_IS_1_RESULT(data);
80802abec36SKiran Gunda 				pmic_arb->ppid_to_apid[ppid]
809ff615ed9SKiran Gunda 					= apid | PMIC_ARB_APID_VALID;
810ff615ed9SKiran Gunda 				pmic_arb->apid_data[apid].ppid = ppid;
811ff615ed9SKiran Gunda 				return apid;
8127f1d4e58SAbhijeet Dharmapurikar 			}
8137f1d4e58SAbhijeet Dharmapurikar 		} else {
8147f1d4e58SAbhijeet Dharmapurikar 			if (SPMI_MAPPING_BIT_IS_0_FLAG(data)) {
8157f1d4e58SAbhijeet Dharmapurikar 				index = SPMI_MAPPING_BIT_IS_0_RESULT(data);
8167f1d4e58SAbhijeet Dharmapurikar 			} else {
817ff615ed9SKiran Gunda 				apid = SPMI_MAPPING_BIT_IS_0_RESULT(data);
81802abec36SKiran Gunda 				pmic_arb->ppid_to_apid[ppid]
819ff615ed9SKiran Gunda 					= apid | PMIC_ARB_APID_VALID;
820ff615ed9SKiran Gunda 				pmic_arb->apid_data[apid].ppid = ppid;
821ff615ed9SKiran Gunda 				return apid;
8227f1d4e58SAbhijeet Dharmapurikar 			}
8237f1d4e58SAbhijeet Dharmapurikar 		}
8247f1d4e58SAbhijeet Dharmapurikar 	}
8257f1d4e58SAbhijeet Dharmapurikar 
8267f1d4e58SAbhijeet Dharmapurikar 	return -ENODEV;
8277f1d4e58SAbhijeet Dharmapurikar }
8287f1d4e58SAbhijeet Dharmapurikar 
829d0c6ae41SGilad Avidov /* v1 offset per ee */
83040f318f0SDavid Collins static int pmic_arb_offset_v1(struct spmi_pmic_arb *pmic_arb, u8 sid, u16 addr,
83140f318f0SDavid Collins 			enum pmic_arb_channel ch_type)
832d0c6ae41SGilad Avidov {
833ff615ed9SKiran Gunda 	return 0x800 + 0x80 * pmic_arb->channel;
834d0c6ae41SGilad Avidov }
835d0c6ae41SGilad Avidov 
83602abec36SKiran Gunda static u16 pmic_arb_find_apid(struct spmi_pmic_arb *pmic_arb, u16 ppid)
837987a9f12SStephen Boyd {
838f2f31564SKiran Gunda 	struct apid_data *apidd = &pmic_arb->apid_data[pmic_arb->last_apid];
839987a9f12SStephen Boyd 	u32 regval, offset;
840f2f31564SKiran Gunda 	u16 id, apid;
841987a9f12SStephen Boyd 
842f2f31564SKiran Gunda 	for (apid = pmic_arb->last_apid; ; apid++, apidd++) {
84340f318f0SDavid Collins 		offset = pmic_arb->ver_ops->apid_map_offset(apid);
84402abec36SKiran Gunda 		if (offset >= pmic_arb->core_size)
845987a9f12SStephen Boyd 			break;
846987a9f12SStephen Boyd 
84702abec36SKiran Gunda 		regval = readl_relaxed(pmic_arb->cnfg +
848b319b592SKiran Gunda 				      SPMI_OWNERSHIP_TABLE_REG(apid));
84940f318f0SDavid Collins 		apidd->irq_ee = SPMI_OWNERSHIP_PERIPH2OWNER(regval);
85040f318f0SDavid Collins 		apidd->write_ee = apidd->irq_ee;
851b319b592SKiran Gunda 
85202abec36SKiran Gunda 		regval = readl_relaxed(pmic_arb->core + offset);
853987a9f12SStephen Boyd 		if (!regval)
854987a9f12SStephen Boyd 			continue;
855987a9f12SStephen Boyd 
856987a9f12SStephen Boyd 		id = (regval >> 8) & PMIC_ARB_PPID_MASK;
85702abec36SKiran Gunda 		pmic_arb->ppid_to_apid[id] = apid | PMIC_ARB_APID_VALID;
858f2f31564SKiran Gunda 		apidd->ppid = id;
859987a9f12SStephen Boyd 		if (id == ppid) {
86002abec36SKiran Gunda 			apid |= PMIC_ARB_APID_VALID;
861987a9f12SStephen Boyd 			break;
862987a9f12SStephen Boyd 		}
863987a9f12SStephen Boyd 	}
86402abec36SKiran Gunda 	pmic_arb->last_apid = apid & ~PMIC_ARB_APID_VALID;
865987a9f12SStephen Boyd 
8661ef1ce4eSAbhijeet Dharmapurikar 	return apid;
867987a9f12SStephen Boyd }
868987a9f12SStephen Boyd 
869ff615ed9SKiran Gunda static int pmic_arb_ppid_to_apid_v2(struct spmi_pmic_arb *pmic_arb, u16 ppid)
87057102ad7SAbhijeet Dharmapurikar {
8717f1d4e58SAbhijeet Dharmapurikar 	u16 apid_valid;
87257102ad7SAbhijeet Dharmapurikar 
87302abec36SKiran Gunda 	apid_valid = pmic_arb->ppid_to_apid[ppid];
87402abec36SKiran Gunda 	if (!(apid_valid & PMIC_ARB_APID_VALID))
87502abec36SKiran Gunda 		apid_valid = pmic_arb_find_apid(pmic_arb, ppid);
87602abec36SKiran Gunda 	if (!(apid_valid & PMIC_ARB_APID_VALID))
87757102ad7SAbhijeet Dharmapurikar 		return -ENODEV;
87857102ad7SAbhijeet Dharmapurikar 
879ff615ed9SKiran Gunda 	return apid_valid & ~PMIC_ARB_APID_VALID;
8807f1d4e58SAbhijeet Dharmapurikar }
8817f1d4e58SAbhijeet Dharmapurikar 
88240f318f0SDavid Collins static int pmic_arb_read_apid_map_v5(struct spmi_pmic_arb *pmic_arb)
88340f318f0SDavid Collins {
88440f318f0SDavid Collins 	struct apid_data *apidd = pmic_arb->apid_data;
88540f318f0SDavid Collins 	struct apid_data *prev_apidd;
88640f318f0SDavid Collins 	u16 i, apid, ppid;
88740f318f0SDavid Collins 	bool valid, is_irq_ee;
88840f318f0SDavid Collins 	u32 regval, offset;
88940f318f0SDavid Collins 
89040f318f0SDavid Collins 	/*
89140f318f0SDavid Collins 	 * In order to allow multiple EEs to write to a single PPID in arbiter
89240f318f0SDavid Collins 	 * version 5, there is more than one APID mapped to each PPID.
89340f318f0SDavid Collins 	 * The owner field for each of these mappings specifies the EE which is
89440f318f0SDavid Collins 	 * allowed to write to the APID.  The owner of the last (highest) APID
89540f318f0SDavid Collins 	 * for a given PPID will receive interrupts from the PPID.
89640f318f0SDavid Collins 	 */
89740f318f0SDavid Collins 	for (i = 0; ; i++, apidd++) {
89840f318f0SDavid Collins 		offset = pmic_arb->ver_ops->apid_map_offset(i);
89940f318f0SDavid Collins 		if (offset >= pmic_arb->core_size)
90040f318f0SDavid Collins 			break;
90140f318f0SDavid Collins 
90240f318f0SDavid Collins 		regval = readl_relaxed(pmic_arb->core + offset);
90340f318f0SDavid Collins 		if (!regval)
90440f318f0SDavid Collins 			continue;
90540f318f0SDavid Collins 		ppid = (regval >> 8) & PMIC_ARB_PPID_MASK;
90640f318f0SDavid Collins 		is_irq_ee = PMIC_ARB_CHAN_IS_IRQ_OWNER(regval);
90740f318f0SDavid Collins 
90840f318f0SDavid Collins 		regval = readl_relaxed(pmic_arb->cnfg +
90940f318f0SDavid Collins 				      SPMI_OWNERSHIP_TABLE_REG(i));
91040f318f0SDavid Collins 		apidd->write_ee = SPMI_OWNERSHIP_PERIPH2OWNER(regval);
91140f318f0SDavid Collins 
91240f318f0SDavid Collins 		apidd->irq_ee = is_irq_ee ? apidd->write_ee : INVALID_EE;
91340f318f0SDavid Collins 
91440f318f0SDavid Collins 		valid = pmic_arb->ppid_to_apid[ppid] & PMIC_ARB_APID_VALID;
91540f318f0SDavid Collins 		apid = pmic_arb->ppid_to_apid[ppid] & ~PMIC_ARB_APID_VALID;
91640f318f0SDavid Collins 		prev_apidd = &pmic_arb->apid_data[apid];
91740f318f0SDavid Collins 
91840f318f0SDavid Collins 		if (valid && is_irq_ee &&
91940f318f0SDavid Collins 				prev_apidd->write_ee == pmic_arb->ee) {
92040f318f0SDavid Collins 			/*
92140f318f0SDavid Collins 			 * Duplicate PPID mapping after the one for this EE;
92240f318f0SDavid Collins 			 * override the irq owner
92340f318f0SDavid Collins 			 */
92440f318f0SDavid Collins 			prev_apidd->irq_ee = apidd->irq_ee;
92540f318f0SDavid Collins 		} else if (!valid || is_irq_ee) {
92640f318f0SDavid Collins 			/* First PPID mapping or duplicate for another EE */
92740f318f0SDavid Collins 			pmic_arb->ppid_to_apid[ppid] = i | PMIC_ARB_APID_VALID;
92840f318f0SDavid Collins 		}
92940f318f0SDavid Collins 
93040f318f0SDavid Collins 		apidd->ppid = ppid;
93140f318f0SDavid Collins 		pmic_arb->last_apid = i;
93240f318f0SDavid Collins 	}
93340f318f0SDavid Collins 
93440f318f0SDavid Collins 	/* Dump the mapping table for debug purposes. */
93540f318f0SDavid Collins 	dev_dbg(&pmic_arb->spmic->dev, "PPID APID Write-EE IRQ-EE\n");
93640f318f0SDavid Collins 	for (ppid = 0; ppid < PMIC_ARB_MAX_PPID; ppid++) {
93740f318f0SDavid Collins 		apid = pmic_arb->ppid_to_apid[ppid];
93840f318f0SDavid Collins 		if (apid & PMIC_ARB_APID_VALID) {
93940f318f0SDavid Collins 			apid &= ~PMIC_ARB_APID_VALID;
94040f318f0SDavid Collins 			apidd = &pmic_arb->apid_data[apid];
94140f318f0SDavid Collins 			dev_dbg(&pmic_arb->spmic->dev, "%#03X %3u %2u %2u\n",
94240f318f0SDavid Collins 			      ppid, apid, apidd->write_ee, apidd->irq_ee);
94340f318f0SDavid Collins 		}
94440f318f0SDavid Collins 	}
94540f318f0SDavid Collins 
94640f318f0SDavid Collins 	return 0;
94740f318f0SDavid Collins }
94840f318f0SDavid Collins 
94940f318f0SDavid Collins static int pmic_arb_ppid_to_apid_v5(struct spmi_pmic_arb *pmic_arb, u16 ppid)
95040f318f0SDavid Collins {
95140f318f0SDavid Collins 	if (!(pmic_arb->ppid_to_apid[ppid] & PMIC_ARB_APID_VALID))
95240f318f0SDavid Collins 		return -ENODEV;
95340f318f0SDavid Collins 
95440f318f0SDavid Collins 	return pmic_arb->ppid_to_apid[ppid] & ~PMIC_ARB_APID_VALID;
95540f318f0SDavid Collins }
95640f318f0SDavid Collins 
9571ef1ce4eSAbhijeet Dharmapurikar /* v2 offset per ppid and per ee */
95840f318f0SDavid Collins static int pmic_arb_offset_v2(struct spmi_pmic_arb *pmic_arb, u8 sid, u16 addr,
95940f318f0SDavid Collins 			   enum pmic_arb_channel ch_type)
960d0c6ae41SGilad Avidov {
961319f6884SAbhijeet Dharmapurikar 	u16 apid;
962ff615ed9SKiran Gunda 	u16 ppid;
9637f1d4e58SAbhijeet Dharmapurikar 	int rc;
964d0c6ae41SGilad Avidov 
965ff615ed9SKiran Gunda 	ppid = sid << 8 | ((addr >> 8) & 0xFF);
966ff615ed9SKiran Gunda 	rc = pmic_arb_ppid_to_apid_v2(pmic_arb, ppid);
9677f1d4e58SAbhijeet Dharmapurikar 	if (rc < 0)
9687f1d4e58SAbhijeet Dharmapurikar 		return rc;
969987a9f12SStephen Boyd 
970ff615ed9SKiran Gunda 	apid = rc;
971ff615ed9SKiran Gunda 	return 0x1000 * pmic_arb->ee + 0x8000 * apid;
972d0c6ae41SGilad Avidov }
973d0c6ae41SGilad Avidov 
97440f318f0SDavid Collins /*
97540f318f0SDavid Collins  * v5 offset per ee and per apid for observer channels and per apid for
97640f318f0SDavid Collins  * read/write channels.
97740f318f0SDavid Collins  */
97840f318f0SDavid Collins static int pmic_arb_offset_v5(struct spmi_pmic_arb *pmic_arb, u8 sid, u16 addr,
97940f318f0SDavid Collins 			   enum pmic_arb_channel ch_type)
98040f318f0SDavid Collins {
98140f318f0SDavid Collins 	u16 apid;
98240f318f0SDavid Collins 	int rc;
98340f318f0SDavid Collins 	u32 offset = 0;
98440f318f0SDavid Collins 	u16 ppid = (sid << 8) | (addr >> 8);
98540f318f0SDavid Collins 
98640f318f0SDavid Collins 	rc = pmic_arb_ppid_to_apid_v5(pmic_arb, ppid);
98740f318f0SDavid Collins 	if (rc < 0)
98840f318f0SDavid Collins 		return rc;
98940f318f0SDavid Collins 
99040f318f0SDavid Collins 	apid = rc;
99140f318f0SDavid Collins 	switch (ch_type) {
99240f318f0SDavid Collins 	case PMIC_ARB_CHANNEL_OBS:
99340f318f0SDavid Collins 		offset = 0x10000 * pmic_arb->ee + 0x80 * apid;
99440f318f0SDavid Collins 		break;
99540f318f0SDavid Collins 	case PMIC_ARB_CHANNEL_RW:
99640f318f0SDavid Collins 		offset = 0x10000 * apid;
99740f318f0SDavid Collins 		break;
99840f318f0SDavid Collins 	}
99940f318f0SDavid Collins 
100040f318f0SDavid Collins 	return offset;
100140f318f0SDavid Collins }
100240f318f0SDavid Collins 
1003d0c6ae41SGilad Avidov static u32 pmic_arb_fmt_cmd_v1(u8 opc, u8 sid, u16 addr, u8 bc)
1004d0c6ae41SGilad Avidov {
1005d0c6ae41SGilad Avidov 	return (opc << 27) | ((sid & 0xf) << 20) | (addr << 4) | (bc & 0x7);
1006d0c6ae41SGilad Avidov }
1007d0c6ae41SGilad Avidov 
1008d0c6ae41SGilad Avidov static u32 pmic_arb_fmt_cmd_v2(u8 opc, u8 sid, u16 addr, u8 bc)
1009d0c6ae41SGilad Avidov {
1010d0c6ae41SGilad Avidov 	return (opc << 27) | ((addr & 0xff) << 4) | (bc & 0x7);
1011d0c6ae41SGilad Avidov }
1012d0c6ae41SGilad Avidov 
1013e95d073cSKiran Gunda static void __iomem *
1014e95d073cSKiran Gunda pmic_arb_owner_acc_status_v1(struct spmi_pmic_arb *pmic_arb, u8 m, u16 n)
1015d0c6ae41SGilad Avidov {
1016e95d073cSKiran Gunda 	return pmic_arb->intr + 0x20 * m + 0x4 * n;
1017d0c6ae41SGilad Avidov }
1018d0c6ae41SGilad Avidov 
1019e95d073cSKiran Gunda static void __iomem *
1020e95d073cSKiran Gunda pmic_arb_owner_acc_status_v2(struct spmi_pmic_arb *pmic_arb, u8 m, u16 n)
1021d0c6ae41SGilad Avidov {
1022e95d073cSKiran Gunda 	return pmic_arb->intr + 0x100000 + 0x1000 * m + 0x4 * n;
1023d0c6ae41SGilad Avidov }
1024d0c6ae41SGilad Avidov 
1025e95d073cSKiran Gunda static void __iomem *
1026e95d073cSKiran Gunda pmic_arb_owner_acc_status_v3(struct spmi_pmic_arb *pmic_arb, u8 m, u16 n)
1027319f6884SAbhijeet Dharmapurikar {
1028e95d073cSKiran Gunda 	return pmic_arb->intr + 0x200000 + 0x1000 * m + 0x4 * n;
1029319f6884SAbhijeet Dharmapurikar }
1030319f6884SAbhijeet Dharmapurikar 
1031e95d073cSKiran Gunda static void __iomem *
103240f318f0SDavid Collins pmic_arb_owner_acc_status_v5(struct spmi_pmic_arb *pmic_arb, u8 m, u16 n)
103340f318f0SDavid Collins {
103440f318f0SDavid Collins 	return pmic_arb->intr + 0x10000 * m + 0x4 * n;
103540f318f0SDavid Collins }
103640f318f0SDavid Collins 
103740f318f0SDavid Collins static void __iomem *
1038e95d073cSKiran Gunda pmic_arb_acc_enable_v1(struct spmi_pmic_arb *pmic_arb, u16 n)
1039d0c6ae41SGilad Avidov {
1040e95d073cSKiran Gunda 	return pmic_arb->intr + 0x200 + 0x4 * n;
1041d0c6ae41SGilad Avidov }
1042d0c6ae41SGilad Avidov 
1043e95d073cSKiran Gunda static void __iomem *
1044e95d073cSKiran Gunda pmic_arb_acc_enable_v2(struct spmi_pmic_arb *pmic_arb, u16 n)
1045d0c6ae41SGilad Avidov {
1046e95d073cSKiran Gunda 	return pmic_arb->intr + 0x1000 * n;
1047d0c6ae41SGilad Avidov }
1048d0c6ae41SGilad Avidov 
1049e95d073cSKiran Gunda static void __iomem *
105040f318f0SDavid Collins pmic_arb_acc_enable_v5(struct spmi_pmic_arb *pmic_arb, u16 n)
105140f318f0SDavid Collins {
105240f318f0SDavid Collins 	return pmic_arb->wr_base + 0x100 + 0x10000 * n;
105340f318f0SDavid Collins }
105440f318f0SDavid Collins 
105540f318f0SDavid Collins static void __iomem *
1056e95d073cSKiran Gunda pmic_arb_irq_status_v1(struct spmi_pmic_arb *pmic_arb, u16 n)
1057d0c6ae41SGilad Avidov {
1058e95d073cSKiran Gunda 	return pmic_arb->intr + 0x600 + 0x4 * n;
1059d0c6ae41SGilad Avidov }
1060d0c6ae41SGilad Avidov 
1061e95d073cSKiran Gunda static void __iomem *
1062e95d073cSKiran Gunda pmic_arb_irq_status_v2(struct spmi_pmic_arb *pmic_arb, u16 n)
1063d0c6ae41SGilad Avidov {
1064e95d073cSKiran Gunda 	return pmic_arb->intr + 0x4 + 0x1000 * n;
1065d0c6ae41SGilad Avidov }
1066d0c6ae41SGilad Avidov 
1067e95d073cSKiran Gunda static void __iomem *
106840f318f0SDavid Collins pmic_arb_irq_status_v5(struct spmi_pmic_arb *pmic_arb, u16 n)
106940f318f0SDavid Collins {
107040f318f0SDavid Collins 	return pmic_arb->wr_base + 0x104 + 0x10000 * n;
107140f318f0SDavid Collins }
107240f318f0SDavid Collins 
107340f318f0SDavid Collins static void __iomem *
1074e95d073cSKiran Gunda pmic_arb_irq_clear_v1(struct spmi_pmic_arb *pmic_arb, u16 n)
1075d0c6ae41SGilad Avidov {
1076e95d073cSKiran Gunda 	return pmic_arb->intr + 0xA00 + 0x4 * n;
1077d0c6ae41SGilad Avidov }
1078d0c6ae41SGilad Avidov 
1079e95d073cSKiran Gunda static void __iomem *
1080e95d073cSKiran Gunda pmic_arb_irq_clear_v2(struct spmi_pmic_arb *pmic_arb, u16 n)
1081d0c6ae41SGilad Avidov {
1082e95d073cSKiran Gunda 	return pmic_arb->intr + 0x8 + 0x1000 * n;
1083d0c6ae41SGilad Avidov }
1084d0c6ae41SGilad Avidov 
108540f318f0SDavid Collins static void __iomem *
108640f318f0SDavid Collins pmic_arb_irq_clear_v5(struct spmi_pmic_arb *pmic_arb, u16 n)
108740f318f0SDavid Collins {
108840f318f0SDavid Collins 	return pmic_arb->wr_base + 0x108 + 0x10000 * n;
108940f318f0SDavid Collins }
109040f318f0SDavid Collins 
109140f318f0SDavid Collins static u32 pmic_arb_apid_map_offset_v2(u16 n)
109240f318f0SDavid Collins {
109340f318f0SDavid Collins 	return 0x800 + 0x4 * n;
109440f318f0SDavid Collins }
109540f318f0SDavid Collins 
109640f318f0SDavid Collins static u32 pmic_arb_apid_map_offset_v5(u16 n)
109740f318f0SDavid Collins {
109840f318f0SDavid Collins 	return 0x900 + 0x4 * n;
109940f318f0SDavid Collins }
110040f318f0SDavid Collins 
1101d0c6ae41SGilad Avidov static const struct pmic_arb_ver_ops pmic_arb_v1 = {
1102319f6884SAbhijeet Dharmapurikar 	.ver_str		= "v1",
11037f1d4e58SAbhijeet Dharmapurikar 	.ppid_to_apid		= pmic_arb_ppid_to_apid_v1,
1104d0c6ae41SGilad Avidov 	.non_data_cmd		= pmic_arb_non_data_cmd_v1,
1105d0c6ae41SGilad Avidov 	.offset			= pmic_arb_offset_v1,
1106d0c6ae41SGilad Avidov 	.fmt_cmd		= pmic_arb_fmt_cmd_v1,
1107d0c6ae41SGilad Avidov 	.owner_acc_status	= pmic_arb_owner_acc_status_v1,
1108d0c6ae41SGilad Avidov 	.acc_enable		= pmic_arb_acc_enable_v1,
1109d0c6ae41SGilad Avidov 	.irq_status		= pmic_arb_irq_status_v1,
1110d0c6ae41SGilad Avidov 	.irq_clear		= pmic_arb_irq_clear_v1,
111140f318f0SDavid Collins 	.apid_map_offset	= pmic_arb_apid_map_offset_v2,
1112d0c6ae41SGilad Avidov };
1113d0c6ae41SGilad Avidov 
1114d0c6ae41SGilad Avidov static const struct pmic_arb_ver_ops pmic_arb_v2 = {
1115319f6884SAbhijeet Dharmapurikar 	.ver_str		= "v2",
11167f1d4e58SAbhijeet Dharmapurikar 	.ppid_to_apid		= pmic_arb_ppid_to_apid_v2,
1117d0c6ae41SGilad Avidov 	.non_data_cmd		= pmic_arb_non_data_cmd_v2,
1118d0c6ae41SGilad Avidov 	.offset			= pmic_arb_offset_v2,
1119d0c6ae41SGilad Avidov 	.fmt_cmd		= pmic_arb_fmt_cmd_v2,
1120d0c6ae41SGilad Avidov 	.owner_acc_status	= pmic_arb_owner_acc_status_v2,
1121d0c6ae41SGilad Avidov 	.acc_enable		= pmic_arb_acc_enable_v2,
1122d0c6ae41SGilad Avidov 	.irq_status		= pmic_arb_irq_status_v2,
1123d0c6ae41SGilad Avidov 	.irq_clear		= pmic_arb_irq_clear_v2,
112440f318f0SDavid Collins 	.apid_map_offset	= pmic_arb_apid_map_offset_v2,
1125d0c6ae41SGilad Avidov };
1126d0c6ae41SGilad Avidov 
1127319f6884SAbhijeet Dharmapurikar static const struct pmic_arb_ver_ops pmic_arb_v3 = {
1128319f6884SAbhijeet Dharmapurikar 	.ver_str		= "v3",
1129319f6884SAbhijeet Dharmapurikar 	.ppid_to_apid		= pmic_arb_ppid_to_apid_v2,
1130319f6884SAbhijeet Dharmapurikar 	.non_data_cmd		= pmic_arb_non_data_cmd_v2,
1131319f6884SAbhijeet Dharmapurikar 	.offset			= pmic_arb_offset_v2,
1132319f6884SAbhijeet Dharmapurikar 	.fmt_cmd		= pmic_arb_fmt_cmd_v2,
1133319f6884SAbhijeet Dharmapurikar 	.owner_acc_status	= pmic_arb_owner_acc_status_v3,
1134319f6884SAbhijeet Dharmapurikar 	.acc_enable		= pmic_arb_acc_enable_v2,
1135319f6884SAbhijeet Dharmapurikar 	.irq_status		= pmic_arb_irq_status_v2,
1136319f6884SAbhijeet Dharmapurikar 	.irq_clear		= pmic_arb_irq_clear_v2,
113740f318f0SDavid Collins 	.apid_map_offset	= pmic_arb_apid_map_offset_v2,
113840f318f0SDavid Collins };
113940f318f0SDavid Collins 
114040f318f0SDavid Collins static const struct pmic_arb_ver_ops pmic_arb_v5 = {
114140f318f0SDavid Collins 	.ver_str		= "v5",
114240f318f0SDavid Collins 	.ppid_to_apid		= pmic_arb_ppid_to_apid_v5,
114340f318f0SDavid Collins 	.non_data_cmd		= pmic_arb_non_data_cmd_v2,
114440f318f0SDavid Collins 	.offset			= pmic_arb_offset_v5,
114540f318f0SDavid Collins 	.fmt_cmd		= pmic_arb_fmt_cmd_v2,
114640f318f0SDavid Collins 	.owner_acc_status	= pmic_arb_owner_acc_status_v5,
114740f318f0SDavid Collins 	.acc_enable		= pmic_arb_acc_enable_v5,
114840f318f0SDavid Collins 	.irq_status		= pmic_arb_irq_status_v5,
114940f318f0SDavid Collins 	.irq_clear		= pmic_arb_irq_clear_v5,
115040f318f0SDavid Collins 	.apid_map_offset	= pmic_arb_apid_map_offset_v5,
1151319f6884SAbhijeet Dharmapurikar };
1152319f6884SAbhijeet Dharmapurikar 
115367b563f1SJosh Cartwright static const struct irq_domain_ops pmic_arb_irq_domain_ops = {
1154*12a9eeaeSBrian Masney 	.activate = qpnpint_irq_domain_activate,
1155*12a9eeaeSBrian Masney 	.alloc = qpnpint_irq_domain_alloc,
1156*12a9eeaeSBrian Masney 	.free = irq_domain_free_irqs_common,
1157*12a9eeaeSBrian Masney 	.translate = qpnpint_irq_domain_translate,
115867b563f1SJosh Cartwright };
115967b563f1SJosh Cartwright 
116039ae93e3SKenneth Heitke static int spmi_pmic_arb_probe(struct platform_device *pdev)
116139ae93e3SKenneth Heitke {
116202abec36SKiran Gunda 	struct spmi_pmic_arb *pmic_arb;
116339ae93e3SKenneth Heitke 	struct spmi_controller *ctrl;
116439ae93e3SKenneth Heitke 	struct resource *res;
1165d0c6ae41SGilad Avidov 	void __iomem *core;
11664788e613SKiran Gunda 	u32 *mapping_table;
1167d0c6ae41SGilad Avidov 	u32 channel, ee, hw_ver;
1168987a9f12SStephen Boyd 	int err;
116939ae93e3SKenneth Heitke 
117002abec36SKiran Gunda 	ctrl = spmi_controller_alloc(&pdev->dev, sizeof(*pmic_arb));
117139ae93e3SKenneth Heitke 	if (!ctrl)
117239ae93e3SKenneth Heitke 		return -ENOMEM;
117339ae93e3SKenneth Heitke 
117402abec36SKiran Gunda 	pmic_arb = spmi_controller_get_drvdata(ctrl);
117502abec36SKiran Gunda 	pmic_arb->spmic = ctrl;
117639ae93e3SKenneth Heitke 
117739ae93e3SKenneth Heitke 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "core");
1178d0c6ae41SGilad Avidov 	core = devm_ioremap_resource(&ctrl->dev, res);
1179d0c6ae41SGilad Avidov 	if (IS_ERR(core)) {
1180d0c6ae41SGilad Avidov 		err = PTR_ERR(core);
118139ae93e3SKenneth Heitke 		goto err_put_ctrl;
118239ae93e3SKenneth Heitke 	}
118339ae93e3SKenneth Heitke 
1184000e1a43SKiran Gunda 	pmic_arb->core_size = resource_size(res);
1185000e1a43SKiran Gunda 
118602abec36SKiran Gunda 	pmic_arb->ppid_to_apid = devm_kcalloc(&ctrl->dev, PMIC_ARB_MAX_PPID,
118702abec36SKiran Gunda 					      sizeof(*pmic_arb->ppid_to_apid),
118802abec36SKiran Gunda 					      GFP_KERNEL);
118902abec36SKiran Gunda 	if (!pmic_arb->ppid_to_apid) {
1190eba9718eSStephen Boyd 		err = -ENOMEM;
1191eba9718eSStephen Boyd 		goto err_put_ctrl;
1192eba9718eSStephen Boyd 	}
1193eba9718eSStephen Boyd 
1194d0c6ae41SGilad Avidov 	hw_ver = readl_relaxed(core + PMIC_ARB_VERSION);
1195d0c6ae41SGilad Avidov 
1196319f6884SAbhijeet Dharmapurikar 	if (hw_ver < PMIC_ARB_VERSION_V2_MIN) {
119702abec36SKiran Gunda 		pmic_arb->ver_ops = &pmic_arb_v1;
119802abec36SKiran Gunda 		pmic_arb->wr_base = core;
119902abec36SKiran Gunda 		pmic_arb->rd_base = core;
1200d0c6ae41SGilad Avidov 	} else {
120102abec36SKiran Gunda 		pmic_arb->core = core;
1202319f6884SAbhijeet Dharmapurikar 
1203319f6884SAbhijeet Dharmapurikar 		if (hw_ver < PMIC_ARB_VERSION_V3_MIN)
120402abec36SKiran Gunda 			pmic_arb->ver_ops = &pmic_arb_v2;
120540f318f0SDavid Collins 		else if (hw_ver < PMIC_ARB_VERSION_V5_MIN)
120602abec36SKiran Gunda 			pmic_arb->ver_ops = &pmic_arb_v3;
120740f318f0SDavid Collins 		else
120840f318f0SDavid Collins 			pmic_arb->ver_ops = &pmic_arb_v5;
1209d0c6ae41SGilad Avidov 
1210d0c6ae41SGilad Avidov 		res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
1211d0c6ae41SGilad Avidov 						   "obsrvr");
121202abec36SKiran Gunda 		pmic_arb->rd_base = devm_ioremap_resource(&ctrl->dev, res);
121302abec36SKiran Gunda 		if (IS_ERR(pmic_arb->rd_base)) {
121402abec36SKiran Gunda 			err = PTR_ERR(pmic_arb->rd_base);
1215d0c6ae41SGilad Avidov 			goto err_put_ctrl;
1216d0c6ae41SGilad Avidov 		}
1217d0c6ae41SGilad Avidov 
1218d0c6ae41SGilad Avidov 		res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
1219d0c6ae41SGilad Avidov 						   "chnls");
122002abec36SKiran Gunda 		pmic_arb->wr_base = devm_ioremap_resource(&ctrl->dev, res);
122102abec36SKiran Gunda 		if (IS_ERR(pmic_arb->wr_base)) {
122202abec36SKiran Gunda 			err = PTR_ERR(pmic_arb->wr_base);
1223d0c6ae41SGilad Avidov 			goto err_put_ctrl;
1224d0c6ae41SGilad Avidov 		}
1225d0c6ae41SGilad Avidov 	}
1226d0c6ae41SGilad Avidov 
1227319f6884SAbhijeet Dharmapurikar 	dev_info(&ctrl->dev, "PMIC arbiter version %s (0x%x)\n",
122802abec36SKiran Gunda 		 pmic_arb->ver_ops->ver_str, hw_ver);
1229319f6884SAbhijeet Dharmapurikar 
123039ae93e3SKenneth Heitke 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "intr");
123102abec36SKiran Gunda 	pmic_arb->intr = devm_ioremap_resource(&ctrl->dev, res);
123202abec36SKiran Gunda 	if (IS_ERR(pmic_arb->intr)) {
123302abec36SKiran Gunda 		err = PTR_ERR(pmic_arb->intr);
123439ae93e3SKenneth Heitke 		goto err_put_ctrl;
123539ae93e3SKenneth Heitke 	}
123639ae93e3SKenneth Heitke 
123739ae93e3SKenneth Heitke 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cnfg");
123802abec36SKiran Gunda 	pmic_arb->cnfg = devm_ioremap_resource(&ctrl->dev, res);
123902abec36SKiran Gunda 	if (IS_ERR(pmic_arb->cnfg)) {
124002abec36SKiran Gunda 		err = PTR_ERR(pmic_arb->cnfg);
124139ae93e3SKenneth Heitke 		goto err_put_ctrl;
124239ae93e3SKenneth Heitke 	}
124339ae93e3SKenneth Heitke 
124402abec36SKiran Gunda 	pmic_arb->irq = platform_get_irq_byname(pdev, "periph_irq");
124502abec36SKiran Gunda 	if (pmic_arb->irq < 0) {
124602abec36SKiran Gunda 		err = pmic_arb->irq;
124767b563f1SJosh Cartwright 		goto err_put_ctrl;
124867b563f1SJosh Cartwright 	}
124967b563f1SJosh Cartwright 
125039ae93e3SKenneth Heitke 	err = of_property_read_u32(pdev->dev.of_node, "qcom,channel", &channel);
125139ae93e3SKenneth Heitke 	if (err) {
125239ae93e3SKenneth Heitke 		dev_err(&pdev->dev, "channel unspecified.\n");
125339ae93e3SKenneth Heitke 		goto err_put_ctrl;
125439ae93e3SKenneth Heitke 	}
125539ae93e3SKenneth Heitke 
125639ae93e3SKenneth Heitke 	if (channel > 5) {
125739ae93e3SKenneth Heitke 		dev_err(&pdev->dev, "invalid channel (%u) specified.\n",
125839ae93e3SKenneth Heitke 			channel);
1259e98cc182SChristophe JAILLET 		err = -EINVAL;
126039ae93e3SKenneth Heitke 		goto err_put_ctrl;
126139ae93e3SKenneth Heitke 	}
126239ae93e3SKenneth Heitke 
126302abec36SKiran Gunda 	pmic_arb->channel = channel;
126439ae93e3SKenneth Heitke 
126567b563f1SJosh Cartwright 	err = of_property_read_u32(pdev->dev.of_node, "qcom,ee", &ee);
126667b563f1SJosh Cartwright 	if (err) {
126767b563f1SJosh Cartwright 		dev_err(&pdev->dev, "EE unspecified.\n");
126867b563f1SJosh Cartwright 		goto err_put_ctrl;
126967b563f1SJosh Cartwright 	}
127067b563f1SJosh Cartwright 
127167b563f1SJosh Cartwright 	if (ee > 5) {
127267b563f1SJosh Cartwright 		dev_err(&pdev->dev, "invalid EE (%u) specified\n", ee);
127367b563f1SJosh Cartwright 		err = -EINVAL;
127467b563f1SJosh Cartwright 		goto err_put_ctrl;
127567b563f1SJosh Cartwright 	}
127667b563f1SJosh Cartwright 
127702abec36SKiran Gunda 	pmic_arb->ee = ee;
12784788e613SKiran Gunda 	mapping_table = devm_kcalloc(&ctrl->dev, PMIC_ARB_MAX_PERIPHS,
12794788e613SKiran Gunda 					sizeof(*mapping_table), GFP_KERNEL);
12804788e613SKiran Gunda 	if (!mapping_table) {
1281987a9f12SStephen Boyd 		err = -ENOMEM;
1282987a9f12SStephen Boyd 		goto err_put_ctrl;
1283987a9f12SStephen Boyd 	}
128467b563f1SJosh Cartwright 
12854788e613SKiran Gunda 	pmic_arb->mapping_table = mapping_table;
128667b563f1SJosh Cartwright 	/* Initialize max_apid/min_apid to the opposite bounds, during
128767b563f1SJosh Cartwright 	 * the irq domain translation, we are sure to update these */
128802abec36SKiran Gunda 	pmic_arb->max_apid = 0;
128902abec36SKiran Gunda 	pmic_arb->min_apid = PMIC_ARB_MAX_PERIPHS - 1;
129067b563f1SJosh Cartwright 
129139ae93e3SKenneth Heitke 	platform_set_drvdata(pdev, ctrl);
129202abec36SKiran Gunda 	raw_spin_lock_init(&pmic_arb->lock);
129339ae93e3SKenneth Heitke 
129439ae93e3SKenneth Heitke 	ctrl->cmd = pmic_arb_cmd;
129539ae93e3SKenneth Heitke 	ctrl->read_cmd = pmic_arb_read_cmd;
129639ae93e3SKenneth Heitke 	ctrl->write_cmd = pmic_arb_write_cmd;
129739ae93e3SKenneth Heitke 
129840f318f0SDavid Collins 	if (hw_ver >= PMIC_ARB_VERSION_V5_MIN) {
129940f318f0SDavid Collins 		err = pmic_arb_read_apid_map_v5(pmic_arb);
130040f318f0SDavid Collins 		if (err) {
130140f318f0SDavid Collins 			dev_err(&pdev->dev, "could not read APID->PPID mapping table, rc= %d\n",
130240f318f0SDavid Collins 				err);
130340f318f0SDavid Collins 			goto err_put_ctrl;
130440f318f0SDavid Collins 		}
130540f318f0SDavid Collins 	}
130640f318f0SDavid Collins 
130767b563f1SJosh Cartwright 	dev_dbg(&pdev->dev, "adding irq domain\n");
130802abec36SKiran Gunda 	pmic_arb->domain = irq_domain_add_tree(pdev->dev.of_node,
130902abec36SKiran Gunda 					 &pmic_arb_irq_domain_ops, pmic_arb);
131002abec36SKiran Gunda 	if (!pmic_arb->domain) {
131167b563f1SJosh Cartwright 		dev_err(&pdev->dev, "unable to create irq_domain\n");
131267b563f1SJosh Cartwright 		err = -ENOMEM;
131367b563f1SJosh Cartwright 		goto err_put_ctrl;
131467b563f1SJosh Cartwright 	}
131567b563f1SJosh Cartwright 
131602abec36SKiran Gunda 	irq_set_chained_handler_and_data(pmic_arb->irq, pmic_arb_chained_irq,
131702abec36SKiran Gunda 					pmic_arb);
131839ae93e3SKenneth Heitke 	err = spmi_controller_add(ctrl);
131939ae93e3SKenneth Heitke 	if (err)
132067b563f1SJosh Cartwright 		goto err_domain_remove;
132139ae93e3SKenneth Heitke 
132239ae93e3SKenneth Heitke 	return 0;
132339ae93e3SKenneth Heitke 
132467b563f1SJosh Cartwright err_domain_remove:
132502abec36SKiran Gunda 	irq_set_chained_handler_and_data(pmic_arb->irq, NULL, NULL);
132602abec36SKiran Gunda 	irq_domain_remove(pmic_arb->domain);
132739ae93e3SKenneth Heitke err_put_ctrl:
132839ae93e3SKenneth Heitke 	spmi_controller_put(ctrl);
132939ae93e3SKenneth Heitke 	return err;
133039ae93e3SKenneth Heitke }
133139ae93e3SKenneth Heitke 
133239ae93e3SKenneth Heitke static int spmi_pmic_arb_remove(struct platform_device *pdev)
133339ae93e3SKenneth Heitke {
133439ae93e3SKenneth Heitke 	struct spmi_controller *ctrl = platform_get_drvdata(pdev);
133502abec36SKiran Gunda 	struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl);
133639ae93e3SKenneth Heitke 	spmi_controller_remove(ctrl);
133702abec36SKiran Gunda 	irq_set_chained_handler_and_data(pmic_arb->irq, NULL, NULL);
133802abec36SKiran Gunda 	irq_domain_remove(pmic_arb->domain);
133939ae93e3SKenneth Heitke 	spmi_controller_put(ctrl);
134039ae93e3SKenneth Heitke 	return 0;
134139ae93e3SKenneth Heitke }
134239ae93e3SKenneth Heitke 
134339ae93e3SKenneth Heitke static const struct of_device_id spmi_pmic_arb_match_table[] = {
134439ae93e3SKenneth Heitke 	{ .compatible = "qcom,spmi-pmic-arb", },
134539ae93e3SKenneth Heitke 	{},
134639ae93e3SKenneth Heitke };
134739ae93e3SKenneth Heitke MODULE_DEVICE_TABLE(of, spmi_pmic_arb_match_table);
134839ae93e3SKenneth Heitke 
134939ae93e3SKenneth Heitke static struct platform_driver spmi_pmic_arb_driver = {
135039ae93e3SKenneth Heitke 	.probe		= spmi_pmic_arb_probe,
135139ae93e3SKenneth Heitke 	.remove		= spmi_pmic_arb_remove,
135239ae93e3SKenneth Heitke 	.driver		= {
135339ae93e3SKenneth Heitke 		.name	= "spmi_pmic_arb",
135439ae93e3SKenneth Heitke 		.of_match_table = spmi_pmic_arb_match_table,
135539ae93e3SKenneth Heitke 	},
135639ae93e3SKenneth Heitke };
135739ae93e3SKenneth Heitke module_platform_driver(spmi_pmic_arb_driver);
135839ae93e3SKenneth Heitke 
135939ae93e3SKenneth Heitke MODULE_LICENSE("GPL v2");
136039ae93e3SKenneth Heitke MODULE_ALIAS("platform:spmi_pmic_arb");
1361