xref: /linux/drivers/spmi/spmi-pmic-arb.c (revision 325255bc9554504a2545284655d4fd8e74c06c22)
1d0c6ae41SGilad Avidov /*
2d0c6ae41SGilad Avidov  * Copyright (c) 2012-2015, 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
3239ae93e3SKenneth Heitke #define PMIC_ARB_INT_EN			0x0004
3339ae93e3SKenneth Heitke 
34d0c6ae41SGilad Avidov /* PMIC Arbiter channel registers offsets */
35d0c6ae41SGilad Avidov #define PMIC_ARB_CMD			0x00
36d0c6ae41SGilad Avidov #define PMIC_ARB_CONFIG			0x04
37d0c6ae41SGilad Avidov #define PMIC_ARB_STATUS			0x08
38d0c6ae41SGilad Avidov #define PMIC_ARB_WDATA0			0x10
39d0c6ae41SGilad Avidov #define PMIC_ARB_WDATA1			0x14
40d0c6ae41SGilad Avidov #define PMIC_ARB_RDATA0			0x18
41d0c6ae41SGilad Avidov #define PMIC_ARB_RDATA1			0x1C
4202abec36SKiran Gunda #define PMIC_ARB_REG_APID(N)		(0x800 + 0x4 * (N))
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)
5539ae93e3SKenneth Heitke 
5639ae93e3SKenneth Heitke /* Ownership Table */
5739ae93e3SKenneth Heitke #define SPMI_OWNERSHIP_TABLE_REG(N)	(0x0700 + (4 * (N)))
5839ae93e3SKenneth Heitke #define SPMI_OWNERSHIP_PERIPH2OWNER(X)	((X) & 0x7)
5939ae93e3SKenneth Heitke 
6039ae93e3SKenneth Heitke /* Channel Status fields */
6139ae93e3SKenneth Heitke enum pmic_arb_chnl_status {
62111a10bfSAbhijeet Dharmapurikar 	PMIC_ARB_STATUS_DONE	= BIT(0),
63111a10bfSAbhijeet Dharmapurikar 	PMIC_ARB_STATUS_FAILURE	= BIT(1),
64111a10bfSAbhijeet Dharmapurikar 	PMIC_ARB_STATUS_DENIED	= BIT(2),
65111a10bfSAbhijeet Dharmapurikar 	PMIC_ARB_STATUS_DROPPED	= BIT(3),
6639ae93e3SKenneth Heitke };
6739ae93e3SKenneth Heitke 
6839ae93e3SKenneth Heitke /* Command register fields */
6939ae93e3SKenneth Heitke #define PMIC_ARB_CMD_MAX_BYTE_COUNT	8
7039ae93e3SKenneth Heitke 
7139ae93e3SKenneth Heitke /* Command Opcodes */
7239ae93e3SKenneth Heitke enum pmic_arb_cmd_op_code {
7339ae93e3SKenneth Heitke 	PMIC_ARB_OP_EXT_WRITEL = 0,
7439ae93e3SKenneth Heitke 	PMIC_ARB_OP_EXT_READL = 1,
7539ae93e3SKenneth Heitke 	PMIC_ARB_OP_EXT_WRITE = 2,
7639ae93e3SKenneth Heitke 	PMIC_ARB_OP_RESET = 3,
7739ae93e3SKenneth Heitke 	PMIC_ARB_OP_SLEEP = 4,
7839ae93e3SKenneth Heitke 	PMIC_ARB_OP_SHUTDOWN = 5,
7939ae93e3SKenneth Heitke 	PMIC_ARB_OP_WAKEUP = 6,
8039ae93e3SKenneth Heitke 	PMIC_ARB_OP_AUTHENTICATE = 7,
8139ae93e3SKenneth Heitke 	PMIC_ARB_OP_MSTR_READ = 8,
8239ae93e3SKenneth Heitke 	PMIC_ARB_OP_MSTR_WRITE = 9,
8339ae93e3SKenneth Heitke 	PMIC_ARB_OP_EXT_READ = 13,
8439ae93e3SKenneth Heitke 	PMIC_ARB_OP_WRITE = 14,
8539ae93e3SKenneth Heitke 	PMIC_ARB_OP_READ = 15,
8639ae93e3SKenneth Heitke 	PMIC_ARB_OP_ZERO_WRITE = 16,
8739ae93e3SKenneth Heitke };
8839ae93e3SKenneth Heitke 
8939ae93e3SKenneth Heitke /* Maximum number of support PMIC peripherals */
90987a9f12SStephen Boyd #define PMIC_ARB_MAX_PERIPHS		512
9139ae93e3SKenneth Heitke #define PMIC_ARB_TIMEOUT_US		100
9239ae93e3SKenneth Heitke #define PMIC_ARB_MAX_TRANS_BYTES	(8)
9339ae93e3SKenneth Heitke 
9439ae93e3SKenneth Heitke #define PMIC_ARB_APID_MASK		0xFF
9539ae93e3SKenneth Heitke #define PMIC_ARB_PPID_MASK		0xFFF
9639ae93e3SKenneth Heitke 
9739ae93e3SKenneth Heitke /* interrupt enable bit */
9839ae93e3SKenneth Heitke #define SPMI_PIC_ACC_ENABLE_BIT		BIT(0)
9939ae93e3SKenneth Heitke 
10002abec36SKiran Gunda #define spec_to_hwirq(slave_id, periph_id, irq_id, apid) \
101319f6884SAbhijeet Dharmapurikar 	((((slave_id) & 0xF)   << 28) | \
102319f6884SAbhijeet Dharmapurikar 	(((periph_id) & 0xFF)  << 20) | \
103319f6884SAbhijeet Dharmapurikar 	(((irq_id)    & 0x7)   << 16) | \
104319f6884SAbhijeet Dharmapurikar 	(((apid)      & 0x1FF) << 0))
105319f6884SAbhijeet Dharmapurikar 
10602abec36SKiran Gunda #define hwirq_to_sid(hwirq)  (((hwirq) >> 28) & 0xF)
10702abec36SKiran Gunda #define hwirq_to_per(hwirq)  (((hwirq) >> 20) & 0xFF)
10802abec36SKiran Gunda #define hwirq_to_irq(hwirq)  (((hwirq) >> 16) & 0x7)
10902abec36SKiran Gunda #define hwirq_to_apid(hwirq) (((hwirq) >> 0)  & 0x1FF)
110319f6884SAbhijeet Dharmapurikar 
111d0c6ae41SGilad Avidov struct pmic_arb_ver_ops;
112d0c6ae41SGilad Avidov 
1136bc546e7SAbhijeet Dharmapurikar struct apid_data {
1146bc546e7SAbhijeet Dharmapurikar 	u16		ppid;
1156bc546e7SAbhijeet Dharmapurikar 	u8		owner;
1166bc546e7SAbhijeet Dharmapurikar };
1176bc546e7SAbhijeet Dharmapurikar 
11839ae93e3SKenneth Heitke /**
119111a10bfSAbhijeet Dharmapurikar  * spmi_pmic_arb - SPMI PMIC Arbiter object
12039ae93e3SKenneth Heitke  *
121d0c6ae41SGilad Avidov  * @rd_base:		on v1 "core", on v2 "observer" register base off DT.
122d0c6ae41SGilad Avidov  * @wr_base:		on v1 "core", on v2 "chnls"    register base off DT.
12339ae93e3SKenneth Heitke  * @intr:		address of the SPMI interrupt control registers.
12439ae93e3SKenneth Heitke  * @cnfg:		address of the PMIC Arbiter configuration registers.
12539ae93e3SKenneth Heitke  * @lock:		lock to synchronize accesses.
126d0c6ae41SGilad Avidov  * @channel:		execution environment channel to use for accesses.
12767b563f1SJosh Cartwright  * @irq:		PMIC ARB interrupt.
12867b563f1SJosh Cartwright  * @ee:			the current Execution Environment
12967b563f1SJosh Cartwright  * @min_apid:		minimum APID (used for bounding IRQ search)
13067b563f1SJosh Cartwright  * @max_apid:		maximum APID
13167b563f1SJosh Cartwright  * @mapping_table:	in-memory copy of PPID -> APID mapping table.
13267b563f1SJosh Cartwright  * @domain:		irq domain object for PMIC IRQ domain
13367b563f1SJosh Cartwright  * @spmic:		SPMI controller object
134d0c6ae41SGilad Avidov  * @ver_ops:		version dependent operations.
13502abec36SKiran Gunda  * @ppid_to_apid	in-memory copy of PPID -> APID mapping table.
13639ae93e3SKenneth Heitke  */
137111a10bfSAbhijeet Dharmapurikar struct spmi_pmic_arb {
138d0c6ae41SGilad Avidov 	void __iomem		*rd_base;
139d0c6ae41SGilad Avidov 	void __iomem		*wr_base;
14039ae93e3SKenneth Heitke 	void __iomem		*intr;
14139ae93e3SKenneth Heitke 	void __iomem		*cnfg;
142987a9f12SStephen Boyd 	void __iomem		*core;
143987a9f12SStephen Boyd 	resource_size_t		core_size;
14439ae93e3SKenneth Heitke 	raw_spinlock_t		lock;
14539ae93e3SKenneth Heitke 	u8			channel;
14667b563f1SJosh Cartwright 	int			irq;
14767b563f1SJosh Cartwright 	u8			ee;
148987a9f12SStephen Boyd 	u16			min_apid;
149987a9f12SStephen Boyd 	u16			max_apid;
150987a9f12SStephen Boyd 	u32			*mapping_table;
151987a9f12SStephen Boyd 	DECLARE_BITMAP(mapping_table_valid, PMIC_ARB_MAX_PERIPHS);
15267b563f1SJosh Cartwright 	struct irq_domain	*domain;
15367b563f1SJosh Cartwright 	struct spmi_controller	*spmic;
154d0c6ae41SGilad Avidov 	const struct pmic_arb_ver_ops *ver_ops;
1551ef1ce4eSAbhijeet Dharmapurikar 	u16			*ppid_to_apid;
1561ef1ce4eSAbhijeet Dharmapurikar 	u16			last_apid;
1576bc546e7SAbhijeet Dharmapurikar 	struct apid_data	apid_data[PMIC_ARB_MAX_PERIPHS];
158d0c6ae41SGilad Avidov };
159d0c6ae41SGilad Avidov 
160d0c6ae41SGilad Avidov /**
161d0c6ae41SGilad Avidov  * pmic_arb_ver: version dependent functionality.
162d0c6ae41SGilad Avidov  *
163319f6884SAbhijeet Dharmapurikar  * @ver_str:		version string.
164319f6884SAbhijeet Dharmapurikar  * @ppid_to_apid:	finds the apid for a given ppid.
165d0c6ae41SGilad Avidov  * @non_data_cmd:	on v1 issues an spmi non-data command.
166d0c6ae41SGilad Avidov  *			on v2 no HW support, returns -EOPNOTSUPP.
167d0c6ae41SGilad Avidov  * @offset:		on v1 offset of per-ee channel.
168d0c6ae41SGilad Avidov  *			on v2 offset of per-ee and per-ppid channel.
169d0c6ae41SGilad Avidov  * @fmt_cmd:		formats a GENI/SPMI command.
170d0c6ae41SGilad Avidov  * @owner_acc_status:	on v1 offset of PMIC_ARB_SPMI_PIC_OWNERm_ACC_STATUSn
171d0c6ae41SGilad Avidov  *			on v2 offset of SPMI_PIC_OWNERm_ACC_STATUSn.
172d0c6ae41SGilad Avidov  * @acc_enable:		on v1 offset of PMIC_ARB_SPMI_PIC_ACC_ENABLEn
173d0c6ae41SGilad Avidov  *			on v2 offset of SPMI_PIC_ACC_ENABLEn.
174d0c6ae41SGilad Avidov  * @irq_status:		on v1 offset of PMIC_ARB_SPMI_PIC_IRQ_STATUSn
175d0c6ae41SGilad Avidov  *			on v2 offset of SPMI_PIC_IRQ_STATUSn.
176d0c6ae41SGilad Avidov  * @irq_clear:		on v1 offset of PMIC_ARB_SPMI_PIC_IRQ_CLEARn
177d0c6ae41SGilad Avidov  *			on v2 offset of SPMI_PIC_IRQ_CLEARn.
178d0c6ae41SGilad Avidov  */
179d0c6ae41SGilad Avidov struct pmic_arb_ver_ops {
180319f6884SAbhijeet Dharmapurikar 	const char *ver_str;
18102abec36SKiran Gunda 	int (*ppid_to_apid)(struct spmi_pmic_arb *pmic_arb, u8 sid, u16 addr,
182319f6884SAbhijeet Dharmapurikar 			u16 *apid);
183d0c6ae41SGilad Avidov 	/* spmi commands (read_cmd, write_cmd, cmd) functionality */
18402abec36SKiran Gunda 	int (*offset)(struct spmi_pmic_arb *pmic_arb, u8 sid, u16 addr,
185987a9f12SStephen Boyd 		      u32 *offset);
186d0c6ae41SGilad Avidov 	u32 (*fmt_cmd)(u8 opc, u8 sid, u16 addr, u8 bc);
187d0c6ae41SGilad Avidov 	int (*non_data_cmd)(struct spmi_controller *ctrl, u8 opc, u8 sid);
188d0c6ae41SGilad Avidov 	/* Interrupts controller functionality (offset of PIC registers) */
189319f6884SAbhijeet Dharmapurikar 	u32 (*owner_acc_status)(u8 m, u16 n);
190319f6884SAbhijeet Dharmapurikar 	u32 (*acc_enable)(u16 n);
191319f6884SAbhijeet Dharmapurikar 	u32 (*irq_status)(u16 n);
192319f6884SAbhijeet Dharmapurikar 	u32 (*irq_clear)(u16 n);
19339ae93e3SKenneth Heitke };
19439ae93e3SKenneth Heitke 
19502abec36SKiran Gunda static inline void pmic_arb_base_write(struct spmi_pmic_arb *pmic_arb,
19639ae93e3SKenneth Heitke 				       u32 offset, u32 val)
19739ae93e3SKenneth Heitke {
19802abec36SKiran Gunda 	writel_relaxed(val, pmic_arb->wr_base + offset);
199d0c6ae41SGilad Avidov }
200d0c6ae41SGilad Avidov 
20102abec36SKiran Gunda static inline void pmic_arb_set_rd_cmd(struct spmi_pmic_arb *pmic_arb,
202d0c6ae41SGilad Avidov 				       u32 offset, u32 val)
203d0c6ae41SGilad Avidov {
20402abec36SKiran Gunda 	writel_relaxed(val, pmic_arb->rd_base + offset);
20539ae93e3SKenneth Heitke }
20639ae93e3SKenneth Heitke 
20739ae93e3SKenneth Heitke /**
20802abec36SKiran Gunda  * pmic_arb_read_data: reads pmic-arb's register and copy 1..4 bytes to buf
20939ae93e3SKenneth Heitke  * @bc:		byte count -1. range: 0..3
21039ae93e3SKenneth Heitke  * @reg:	register's address
21139ae93e3SKenneth Heitke  * @buf:	output parameter, length must be bc + 1
21239ae93e3SKenneth Heitke  */
21302abec36SKiran Gunda static void
21402abec36SKiran Gunda pmic_arb_read_data(struct spmi_pmic_arb *pmic_arb, u8 *buf, u32 reg, u8 bc)
21539ae93e3SKenneth Heitke {
21602abec36SKiran Gunda 	u32 data = __raw_readl(pmic_arb->rd_base + reg);
217111a10bfSAbhijeet Dharmapurikar 
21839ae93e3SKenneth Heitke 	memcpy(buf, &data, (bc & 3) + 1);
21939ae93e3SKenneth Heitke }
22039ae93e3SKenneth Heitke 
22139ae93e3SKenneth Heitke /**
22202abec36SKiran Gunda  * pmic_arb_write_data: write 1..4 bytes from buf to pmic-arb's register
22339ae93e3SKenneth Heitke  * @bc:		byte-count -1. range: 0..3.
22439ae93e3SKenneth Heitke  * @reg:	register's address.
22539ae93e3SKenneth Heitke  * @buf:	buffer to write. length must be bc + 1.
22639ae93e3SKenneth Heitke  */
22702abec36SKiran Gunda static void pmic_arb_write_data(struct spmi_pmic_arb *pmic_arb, const u8 *buf,
22802abec36SKiran Gunda 				u32 reg, u8 bc)
22939ae93e3SKenneth Heitke {
23039ae93e3SKenneth Heitke 	u32 data = 0;
231111a10bfSAbhijeet Dharmapurikar 
23239ae93e3SKenneth Heitke 	memcpy(&data, buf, (bc & 3) + 1);
23302abec36SKiran Gunda 	pmic_arb_base_write(pmic_arb, reg, data);
23439ae93e3SKenneth Heitke }
23539ae93e3SKenneth Heitke 
236d0c6ae41SGilad Avidov static int pmic_arb_wait_for_done(struct spmi_controller *ctrl,
237d0c6ae41SGilad Avidov 				  void __iomem *base, u8 sid, u16 addr)
23839ae93e3SKenneth Heitke {
23902abec36SKiran Gunda 	struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl);
24039ae93e3SKenneth Heitke 	u32 status = 0;
24139ae93e3SKenneth Heitke 	u32 timeout = PMIC_ARB_TIMEOUT_US;
242987a9f12SStephen Boyd 	u32 offset;
243987a9f12SStephen Boyd 	int rc;
244987a9f12SStephen Boyd 
24502abec36SKiran Gunda 	rc = pmic_arb->ver_ops->offset(pmic_arb, sid, addr, &offset);
246987a9f12SStephen Boyd 	if (rc)
247987a9f12SStephen Boyd 		return rc;
248987a9f12SStephen Boyd 
249987a9f12SStephen Boyd 	offset += PMIC_ARB_STATUS;
25039ae93e3SKenneth Heitke 
25139ae93e3SKenneth Heitke 	while (timeout--) {
252d0c6ae41SGilad Avidov 		status = readl_relaxed(base + offset);
25339ae93e3SKenneth Heitke 
25439ae93e3SKenneth Heitke 		if (status & PMIC_ARB_STATUS_DONE) {
25539ae93e3SKenneth Heitke 			if (status & PMIC_ARB_STATUS_DENIED) {
25602abec36SKiran Gunda 				dev_err(&ctrl->dev, "%s: transaction denied (0x%x)\n",
25739ae93e3SKenneth Heitke 					__func__, status);
25839ae93e3SKenneth Heitke 				return -EPERM;
25939ae93e3SKenneth Heitke 			}
26039ae93e3SKenneth Heitke 
26139ae93e3SKenneth Heitke 			if (status & PMIC_ARB_STATUS_FAILURE) {
26202abec36SKiran Gunda 				dev_err(&ctrl->dev, "%s: transaction failed (0x%x)\n",
26339ae93e3SKenneth Heitke 					__func__, status);
26439ae93e3SKenneth Heitke 				return -EIO;
26539ae93e3SKenneth Heitke 			}
26639ae93e3SKenneth Heitke 
26739ae93e3SKenneth Heitke 			if (status & PMIC_ARB_STATUS_DROPPED) {
26802abec36SKiran Gunda 				dev_err(&ctrl->dev, "%s: transaction dropped (0x%x)\n",
26939ae93e3SKenneth Heitke 					__func__, status);
27039ae93e3SKenneth Heitke 				return -EIO;
27139ae93e3SKenneth Heitke 			}
27239ae93e3SKenneth Heitke 
27339ae93e3SKenneth Heitke 			return 0;
27439ae93e3SKenneth Heitke 		}
27539ae93e3SKenneth Heitke 		udelay(1);
27639ae93e3SKenneth Heitke 	}
27739ae93e3SKenneth Heitke 
27802abec36SKiran Gunda 	dev_err(&ctrl->dev, "%s: timeout, status 0x%x\n",
27939ae93e3SKenneth Heitke 		__func__, status);
28039ae93e3SKenneth Heitke 	return -ETIMEDOUT;
28139ae93e3SKenneth Heitke }
28239ae93e3SKenneth Heitke 
283d0c6ae41SGilad Avidov static int
284d0c6ae41SGilad Avidov pmic_arb_non_data_cmd_v1(struct spmi_controller *ctrl, u8 opc, u8 sid)
28539ae93e3SKenneth Heitke {
28602abec36SKiran Gunda 	struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl);
28739ae93e3SKenneth Heitke 	unsigned long flags;
28839ae93e3SKenneth Heitke 	u32 cmd;
28939ae93e3SKenneth Heitke 	int rc;
290987a9f12SStephen Boyd 	u32 offset;
291987a9f12SStephen Boyd 
29202abec36SKiran Gunda 	rc = pmic_arb->ver_ops->offset(pmic_arb, sid, 0, &offset);
293987a9f12SStephen Boyd 	if (rc)
294987a9f12SStephen Boyd 		return rc;
295d0c6ae41SGilad Avidov 
296d0c6ae41SGilad Avidov 	cmd = ((opc | 0x40) << 27) | ((sid & 0xf) << 20);
297d0c6ae41SGilad Avidov 
29802abec36SKiran Gunda 	raw_spin_lock_irqsave(&pmic_arb->lock, flags);
29902abec36SKiran Gunda 	pmic_arb_base_write(pmic_arb, offset + PMIC_ARB_CMD, cmd);
30002abec36SKiran Gunda 	rc = pmic_arb_wait_for_done(ctrl, pmic_arb->wr_base, sid, 0);
30102abec36SKiran Gunda 	raw_spin_unlock_irqrestore(&pmic_arb->lock, flags);
302d0c6ae41SGilad Avidov 
303d0c6ae41SGilad Avidov 	return rc;
304d0c6ae41SGilad Avidov }
305d0c6ae41SGilad Avidov 
306d0c6ae41SGilad Avidov static int
307d0c6ae41SGilad Avidov pmic_arb_non_data_cmd_v2(struct spmi_controller *ctrl, u8 opc, u8 sid)
308d0c6ae41SGilad Avidov {
309d0c6ae41SGilad Avidov 	return -EOPNOTSUPP;
310d0c6ae41SGilad Avidov }
311d0c6ae41SGilad Avidov 
312d0c6ae41SGilad Avidov /* Non-data command */
313d0c6ae41SGilad Avidov static int pmic_arb_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid)
314d0c6ae41SGilad Avidov {
31502abec36SKiran Gunda 	struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl);
316d0c6ae41SGilad Avidov 
317d0c6ae41SGilad Avidov 	dev_dbg(&ctrl->dev, "cmd op:0x%x sid:%d\n", opc, sid);
31839ae93e3SKenneth Heitke 
31939ae93e3SKenneth Heitke 	/* Check for valid non-data command */
32039ae93e3SKenneth Heitke 	if (opc < SPMI_CMD_RESET || opc > SPMI_CMD_WAKEUP)
32139ae93e3SKenneth Heitke 		return -EINVAL;
32239ae93e3SKenneth Heitke 
32302abec36SKiran Gunda 	return pmic_arb->ver_ops->non_data_cmd(ctrl, opc, sid);
32439ae93e3SKenneth Heitke }
32539ae93e3SKenneth Heitke 
32639ae93e3SKenneth Heitke static int pmic_arb_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
32739ae93e3SKenneth Heitke 			     u16 addr, u8 *buf, size_t len)
32839ae93e3SKenneth Heitke {
32902abec36SKiran Gunda 	struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl);
33039ae93e3SKenneth Heitke 	unsigned long flags;
33139ae93e3SKenneth Heitke 	u8 bc = len - 1;
33239ae93e3SKenneth Heitke 	u32 cmd;
33339ae93e3SKenneth Heitke 	int rc;
334987a9f12SStephen Boyd 	u32 offset;
335987a9f12SStephen Boyd 
33602abec36SKiran Gunda 	rc = pmic_arb->ver_ops->offset(pmic_arb, sid, addr, &offset);
337987a9f12SStephen Boyd 	if (rc)
338987a9f12SStephen Boyd 		return rc;
33939ae93e3SKenneth Heitke 
34039ae93e3SKenneth Heitke 	if (bc >= PMIC_ARB_MAX_TRANS_BYTES) {
34102abec36SKiran Gunda 		dev_err(&ctrl->dev, "pmic-arb supports 1..%d bytes per trans, but:%zu requested",
34239ae93e3SKenneth Heitke 			PMIC_ARB_MAX_TRANS_BYTES, len);
34339ae93e3SKenneth Heitke 		return  -EINVAL;
34439ae93e3SKenneth Heitke 	}
34539ae93e3SKenneth Heitke 
34639ae93e3SKenneth Heitke 	/* Check the opcode */
34739ae93e3SKenneth Heitke 	if (opc >= 0x60 && opc <= 0x7F)
34839ae93e3SKenneth Heitke 		opc = PMIC_ARB_OP_READ;
34939ae93e3SKenneth Heitke 	else if (opc >= 0x20 && opc <= 0x2F)
35039ae93e3SKenneth Heitke 		opc = PMIC_ARB_OP_EXT_READ;
35139ae93e3SKenneth Heitke 	else if (opc >= 0x38 && opc <= 0x3F)
35239ae93e3SKenneth Heitke 		opc = PMIC_ARB_OP_EXT_READL;
35339ae93e3SKenneth Heitke 	else
35439ae93e3SKenneth Heitke 		return -EINVAL;
35539ae93e3SKenneth Heitke 
35602abec36SKiran Gunda 	cmd = pmic_arb->ver_ops->fmt_cmd(opc, sid, addr, bc);
35739ae93e3SKenneth Heitke 
35802abec36SKiran Gunda 	raw_spin_lock_irqsave(&pmic_arb->lock, flags);
35902abec36SKiran Gunda 	pmic_arb_set_rd_cmd(pmic_arb, offset + PMIC_ARB_CMD, cmd);
36002abec36SKiran Gunda 	rc = pmic_arb_wait_for_done(ctrl, pmic_arb->rd_base, sid, addr);
36139ae93e3SKenneth Heitke 	if (rc)
36239ae93e3SKenneth Heitke 		goto done;
36339ae93e3SKenneth Heitke 
36402abec36SKiran Gunda 	pmic_arb_read_data(pmic_arb, buf, offset + PMIC_ARB_RDATA0,
36539ae93e3SKenneth Heitke 		     min_t(u8, bc, 3));
36639ae93e3SKenneth Heitke 
36739ae93e3SKenneth Heitke 	if (bc > 3)
36802abec36SKiran Gunda 		pmic_arb_read_data(pmic_arb, buf + 4, offset + PMIC_ARB_RDATA1,
36902abec36SKiran Gunda 					bc - 4);
37039ae93e3SKenneth Heitke 
37139ae93e3SKenneth Heitke done:
37202abec36SKiran Gunda 	raw_spin_unlock_irqrestore(&pmic_arb->lock, flags);
37339ae93e3SKenneth Heitke 	return rc;
37439ae93e3SKenneth Heitke }
37539ae93e3SKenneth Heitke 
37639ae93e3SKenneth Heitke static int pmic_arb_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
37739ae93e3SKenneth Heitke 			      u16 addr, const u8 *buf, size_t len)
37839ae93e3SKenneth Heitke {
37902abec36SKiran Gunda 	struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl);
38039ae93e3SKenneth Heitke 	unsigned long flags;
38139ae93e3SKenneth Heitke 	u8 bc = len - 1;
38239ae93e3SKenneth Heitke 	u32 cmd;
38339ae93e3SKenneth Heitke 	int rc;
384987a9f12SStephen Boyd 	u32 offset;
385987a9f12SStephen Boyd 
38602abec36SKiran Gunda 	rc = pmic_arb->ver_ops->offset(pmic_arb, sid, addr, &offset);
387987a9f12SStephen Boyd 	if (rc)
388987a9f12SStephen Boyd 		return rc;
38939ae93e3SKenneth Heitke 
39039ae93e3SKenneth Heitke 	if (bc >= PMIC_ARB_MAX_TRANS_BYTES) {
39102abec36SKiran Gunda 		dev_err(&ctrl->dev, "pmic-arb supports 1..%d bytes per trans, but:%zu requested",
39239ae93e3SKenneth Heitke 			PMIC_ARB_MAX_TRANS_BYTES, len);
39339ae93e3SKenneth Heitke 		return  -EINVAL;
39439ae93e3SKenneth Heitke 	}
39539ae93e3SKenneth Heitke 
39639ae93e3SKenneth Heitke 	/* Check the opcode */
39739ae93e3SKenneth Heitke 	if (opc >= 0x40 && opc <= 0x5F)
39839ae93e3SKenneth Heitke 		opc = PMIC_ARB_OP_WRITE;
39939ae93e3SKenneth Heitke 	else if (opc >= 0x00 && opc <= 0x0F)
40039ae93e3SKenneth Heitke 		opc = PMIC_ARB_OP_EXT_WRITE;
40139ae93e3SKenneth Heitke 	else if (opc >= 0x30 && opc <= 0x37)
40239ae93e3SKenneth Heitke 		opc = PMIC_ARB_OP_EXT_WRITEL;
4039b76968dSStephen Boyd 	else if (opc >= 0x80)
40439ae93e3SKenneth Heitke 		opc = PMIC_ARB_OP_ZERO_WRITE;
40539ae93e3SKenneth Heitke 	else
40639ae93e3SKenneth Heitke 		return -EINVAL;
40739ae93e3SKenneth Heitke 
40802abec36SKiran Gunda 	cmd = pmic_arb->ver_ops->fmt_cmd(opc, sid, addr, bc);
40939ae93e3SKenneth Heitke 
41039ae93e3SKenneth Heitke 	/* Write data to FIFOs */
41102abec36SKiran Gunda 	raw_spin_lock_irqsave(&pmic_arb->lock, flags);
41202abec36SKiran Gunda 	pmic_arb_write_data(pmic_arb, buf, offset + PMIC_ARB_WDATA0,
41302abec36SKiran Gunda 				min_t(u8, bc, 3));
41439ae93e3SKenneth Heitke 	if (bc > 3)
41502abec36SKiran Gunda 		pmic_arb_write_data(pmic_arb, buf + 4, offset + PMIC_ARB_WDATA1,
41602abec36SKiran Gunda 					bc - 4);
41739ae93e3SKenneth Heitke 
41839ae93e3SKenneth Heitke 	/* Start the transaction */
41902abec36SKiran Gunda 	pmic_arb_base_write(pmic_arb, offset + PMIC_ARB_CMD, cmd);
42002abec36SKiran Gunda 	rc = pmic_arb_wait_for_done(ctrl, pmic_arb->wr_base, sid, addr);
42102abec36SKiran Gunda 	raw_spin_unlock_irqrestore(&pmic_arb->lock, flags);
42239ae93e3SKenneth Heitke 
42339ae93e3SKenneth Heitke 	return rc;
42439ae93e3SKenneth Heitke }
42539ae93e3SKenneth Heitke 
42667b563f1SJosh Cartwright enum qpnpint_regs {
42767b563f1SJosh Cartwright 	QPNPINT_REG_RT_STS		= 0x10,
42867b563f1SJosh Cartwright 	QPNPINT_REG_SET_TYPE		= 0x11,
42967b563f1SJosh Cartwright 	QPNPINT_REG_POLARITY_HIGH	= 0x12,
43067b563f1SJosh Cartwright 	QPNPINT_REG_POLARITY_LOW	= 0x13,
43167b563f1SJosh Cartwright 	QPNPINT_REG_LATCHED_CLR		= 0x14,
43267b563f1SJosh Cartwright 	QPNPINT_REG_EN_SET		= 0x15,
43367b563f1SJosh Cartwright 	QPNPINT_REG_EN_CLR		= 0x16,
43467b563f1SJosh Cartwright 	QPNPINT_REG_LATCHED_STS		= 0x18,
43567b563f1SJosh Cartwright };
43667b563f1SJosh Cartwright 
43767b563f1SJosh Cartwright struct spmi_pmic_arb_qpnpint_type {
43867b563f1SJosh Cartwright 	u8 type; /* 1 -> edge */
43967b563f1SJosh Cartwright 	u8 polarity_high;
44067b563f1SJosh Cartwright 	u8 polarity_low;
44167b563f1SJosh Cartwright } __packed;
44267b563f1SJosh Cartwright 
44367b563f1SJosh Cartwright /* Simplified accessor functions for irqchip callbacks */
44467b563f1SJosh Cartwright static void qpnpint_spmi_write(struct irq_data *d, u8 reg, void *buf,
44567b563f1SJosh Cartwright 			       size_t len)
44667b563f1SJosh Cartwright {
44702abec36SKiran Gunda 	struct spmi_pmic_arb *pmic_arb = irq_data_get_irq_chip_data(d);
44802abec36SKiran Gunda 	u8 sid = hwirq_to_sid(d->hwirq);
44902abec36SKiran Gunda 	u8 per = hwirq_to_per(d->hwirq);
45067b563f1SJosh Cartwright 
45102abec36SKiran Gunda 	if (pmic_arb_write_cmd(pmic_arb->spmic, SPMI_CMD_EXT_WRITEL, sid,
45267b563f1SJosh Cartwright 			       (per << 8) + reg, buf, len))
45302abec36SKiran Gunda 		dev_err_ratelimited(&pmic_arb->spmic->dev, "failed irqchip transaction on %x\n",
45467b563f1SJosh Cartwright 				    d->irq);
45567b563f1SJosh Cartwright }
45667b563f1SJosh Cartwright 
45767b563f1SJosh Cartwright static void qpnpint_spmi_read(struct irq_data *d, u8 reg, void *buf, size_t len)
45867b563f1SJosh Cartwright {
45902abec36SKiran Gunda 	struct spmi_pmic_arb *pmic_arb = irq_data_get_irq_chip_data(d);
46002abec36SKiran Gunda 	u8 sid = hwirq_to_sid(d->hwirq);
46102abec36SKiran Gunda 	u8 per = hwirq_to_per(d->hwirq);
46267b563f1SJosh Cartwright 
46302abec36SKiran Gunda 	if (pmic_arb_read_cmd(pmic_arb->spmic, SPMI_CMD_EXT_READL, sid,
46467b563f1SJosh Cartwright 			      (per << 8) + reg, buf, len))
46502abec36SKiran Gunda 		dev_err_ratelimited(&pmic_arb->spmic->dev, "failed irqchip transaction on %x\n",
46667b563f1SJosh Cartwright 				    d->irq);
46767b563f1SJosh Cartwright }
46867b563f1SJosh Cartwright 
46902abec36SKiran Gunda static void cleanup_irq(struct spmi_pmic_arb *pmic_arb, u16 apid, int id)
4706bc546e7SAbhijeet Dharmapurikar {
47102abec36SKiran Gunda 	u16 ppid = pmic_arb->apid_data[apid].ppid;
4726bc546e7SAbhijeet Dharmapurikar 	u8 sid = ppid >> 8;
4736bc546e7SAbhijeet Dharmapurikar 	u8 per = ppid & 0xFF;
4746bc546e7SAbhijeet Dharmapurikar 	u8 irq_mask = BIT(id);
4756bc546e7SAbhijeet Dharmapurikar 
47602abec36SKiran Gunda 	writel_relaxed(irq_mask, pmic_arb->intr +
47702abec36SKiran Gunda 				pmic_arb->ver_ops->irq_clear(apid));
4786bc546e7SAbhijeet Dharmapurikar 
47902abec36SKiran Gunda 	if (pmic_arb_write_cmd(pmic_arb->spmic, SPMI_CMD_EXT_WRITEL, sid,
4806bc546e7SAbhijeet Dharmapurikar 			(per << 8) + QPNPINT_REG_LATCHED_CLR, &irq_mask, 1))
48102abec36SKiran Gunda 		dev_err_ratelimited(&pmic_arb->spmic->dev, "failed to ack irq_mask = 0x%x for ppid = %x\n",
4826bc546e7SAbhijeet Dharmapurikar 				irq_mask, ppid);
4836bc546e7SAbhijeet Dharmapurikar 
48402abec36SKiran Gunda 	if (pmic_arb_write_cmd(pmic_arb->spmic, SPMI_CMD_EXT_WRITEL, sid,
4856bc546e7SAbhijeet Dharmapurikar 			       (per << 8) + QPNPINT_REG_EN_CLR, &irq_mask, 1))
48602abec36SKiran Gunda 		dev_err_ratelimited(&pmic_arb->spmic->dev, "failed to ack irq_mask = 0x%x for ppid = %x\n",
4876bc546e7SAbhijeet Dharmapurikar 				irq_mask, ppid);
4886bc546e7SAbhijeet Dharmapurikar }
4896bc546e7SAbhijeet Dharmapurikar 
49002abec36SKiran Gunda static void periph_interrupt(struct spmi_pmic_arb *pmic_arb, u16 apid)
49167b563f1SJosh Cartwright {
49267b563f1SJosh Cartwright 	unsigned int irq;
49367b563f1SJosh Cartwright 	u32 status;
49467b563f1SJosh Cartwright 	int id;
49502abec36SKiran Gunda 	u8 sid = (pmic_arb->apid_data[apid].ppid >> 8) & 0xF;
49602abec36SKiran Gunda 	u8 per = pmic_arb->apid_data[apid].ppid & 0xFF;
49767b563f1SJosh Cartwright 
49802abec36SKiran Gunda 	status = readl_relaxed(pmic_arb->intr +
49902abec36SKiran Gunda 				pmic_arb->ver_ops->irq_status(apid));
50067b563f1SJosh Cartwright 	while (status) {
50167b563f1SJosh Cartwright 		id = ffs(status) - 1;
502111a10bfSAbhijeet Dharmapurikar 		status &= ~BIT(id);
50302abec36SKiran Gunda 		irq = irq_find_mapping(pmic_arb->domain,
50402abec36SKiran Gunda 					spec_to_hwirq(sid, per, id, apid));
5056bc546e7SAbhijeet Dharmapurikar 		if (irq == 0) {
50602abec36SKiran Gunda 			cleanup_irq(pmic_arb, apid, id);
5076bc546e7SAbhijeet Dharmapurikar 			continue;
5086bc546e7SAbhijeet Dharmapurikar 		}
50967b563f1SJosh Cartwright 		generic_handle_irq(irq);
51067b563f1SJosh Cartwright 	}
51167b563f1SJosh Cartwright }
51267b563f1SJosh Cartwright 
513bd0b9ac4SThomas Gleixner static void pmic_arb_chained_irq(struct irq_desc *desc)
51467b563f1SJosh Cartwright {
51502abec36SKiran Gunda 	struct spmi_pmic_arb *pmic_arb = irq_desc_get_handler_data(desc);
5167fe88f3cSJiang Liu 	struct irq_chip *chip = irq_desc_get_chip(desc);
51702abec36SKiran Gunda 	void __iomem *intr = pmic_arb->intr;
51802abec36SKiran Gunda 	int first = pmic_arb->min_apid >> 5;
51902abec36SKiran Gunda 	int last = pmic_arb->max_apid >> 5;
520472eaf8bSAbhijeet Dharmapurikar 	u32 status, enable;
521472eaf8bSAbhijeet Dharmapurikar 	int i, id, apid;
52267b563f1SJosh Cartwright 
52367b563f1SJosh Cartwright 	chained_irq_enter(chip, desc);
52467b563f1SJosh Cartwright 
52567b563f1SJosh Cartwright 	for (i = first; i <= last; ++i) {
52667b563f1SJosh Cartwright 		status = readl_relaxed(intr +
52702abec36SKiran Gunda 			 pmic_arb->ver_ops->owner_acc_status(pmic_arb->ee, i));
52867b563f1SJosh Cartwright 		while (status) {
52967b563f1SJosh Cartwright 			id = ffs(status) - 1;
530111a10bfSAbhijeet Dharmapurikar 			status &= ~BIT(id);
531472eaf8bSAbhijeet Dharmapurikar 			apid = id + i * 32;
532472eaf8bSAbhijeet Dharmapurikar 			enable = readl_relaxed(intr +
53302abec36SKiran Gunda 					pmic_arb->ver_ops->acc_enable(apid));
534472eaf8bSAbhijeet Dharmapurikar 			if (enable & SPMI_PIC_ACC_ENABLE_BIT)
53502abec36SKiran Gunda 				periph_interrupt(pmic_arb, apid);
53667b563f1SJosh Cartwright 		}
53767b563f1SJosh Cartwright 	}
53867b563f1SJosh Cartwright 
53967b563f1SJosh Cartwright 	chained_irq_exit(chip, desc);
54067b563f1SJosh Cartwright }
54167b563f1SJosh Cartwright 
54267b563f1SJosh Cartwright static void qpnpint_irq_ack(struct irq_data *d)
54367b563f1SJosh Cartwright {
54402abec36SKiran Gunda 	struct spmi_pmic_arb *pmic_arb = irq_data_get_irq_chip_data(d);
54502abec36SKiran Gunda 	u8 irq = hwirq_to_irq(d->hwirq);
54602abec36SKiran Gunda 	u16 apid = hwirq_to_apid(d->hwirq);
54767b563f1SJosh Cartwright 	u8 data;
54867b563f1SJosh Cartwright 
54902abec36SKiran Gunda 	writel_relaxed(BIT(irq), pmic_arb->intr +
55002abec36SKiran Gunda 				pmic_arb->ver_ops->irq_clear(apid));
55167b563f1SJosh Cartwright 
552111a10bfSAbhijeet Dharmapurikar 	data = BIT(irq);
55367b563f1SJosh Cartwright 	qpnpint_spmi_write(d, QPNPINT_REG_LATCHED_CLR, &data, 1);
55467b563f1SJosh Cartwright }
55567b563f1SJosh Cartwright 
55667b563f1SJosh Cartwright static void qpnpint_irq_mask(struct irq_data *d)
55767b563f1SJosh Cartwright {
55802abec36SKiran Gunda 	u8 irq = hwirq_to_irq(d->hwirq);
5596bc546e7SAbhijeet Dharmapurikar 	u8 data = BIT(irq);
56067b563f1SJosh Cartwright 
56167b563f1SJosh Cartwright 	qpnpint_spmi_write(d, QPNPINT_REG_EN_CLR, &data, 1);
56267b563f1SJosh Cartwright }
56367b563f1SJosh Cartwright 
56467b563f1SJosh Cartwright static void qpnpint_irq_unmask(struct irq_data *d)
56567b563f1SJosh Cartwright {
56602abec36SKiran Gunda 	struct spmi_pmic_arb *pmic_arb = irq_data_get_irq_chip_data(d);
56702abec36SKiran Gunda 	u8 irq = hwirq_to_irq(d->hwirq);
56802abec36SKiran Gunda 	u16 apid = hwirq_to_apid(d->hwirq);
569cee0fad7SAbhijeet Dharmapurikar 	u8 buf[2];
57067b563f1SJosh Cartwright 
5716bc546e7SAbhijeet Dharmapurikar 	writel_relaxed(SPMI_PIC_ACC_ENABLE_BIT,
57202abec36SKiran Gunda 		pmic_arb->intr + pmic_arb->ver_ops->acc_enable(apid));
57367b563f1SJosh Cartwright 
574cee0fad7SAbhijeet Dharmapurikar 	qpnpint_spmi_read(d, QPNPINT_REG_EN_SET, &buf[0], 1);
575cee0fad7SAbhijeet Dharmapurikar 	if (!(buf[0] & BIT(irq))) {
576cee0fad7SAbhijeet Dharmapurikar 		/*
577cee0fad7SAbhijeet Dharmapurikar 		 * Since the interrupt is currently disabled, write to both the
578cee0fad7SAbhijeet Dharmapurikar 		 * LATCHED_CLR and EN_SET registers so that a spurious interrupt
579cee0fad7SAbhijeet Dharmapurikar 		 * cannot be triggered when the interrupt is enabled
580cee0fad7SAbhijeet Dharmapurikar 		 */
581cee0fad7SAbhijeet Dharmapurikar 		buf[0] = BIT(irq);
582cee0fad7SAbhijeet Dharmapurikar 		buf[1] = BIT(irq);
583cee0fad7SAbhijeet Dharmapurikar 		qpnpint_spmi_write(d, QPNPINT_REG_LATCHED_CLR, &buf, 2);
584cee0fad7SAbhijeet Dharmapurikar 	}
58567b563f1SJosh Cartwright }
58667b563f1SJosh Cartwright 
58767b563f1SJosh Cartwright static int qpnpint_irq_set_type(struct irq_data *d, unsigned int flow_type)
58867b563f1SJosh Cartwright {
58967b563f1SJosh Cartwright 	struct spmi_pmic_arb_qpnpint_type type;
590*325255bcSKiran Gunda 	irq_flow_handler_t flow_handler;
59102abec36SKiran Gunda 	u8 irq = hwirq_to_irq(d->hwirq);
59267b563f1SJosh Cartwright 
59367b563f1SJosh Cartwright 	qpnpint_spmi_read(d, QPNPINT_REG_SET_TYPE, &type, sizeof(type));
59467b563f1SJosh Cartwright 
59567b563f1SJosh Cartwright 	if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) {
596*325255bcSKiran Gunda 		type.type |= BIT(irq);
59767b563f1SJosh Cartwright 		if (flow_type & IRQF_TRIGGER_RISING)
598*325255bcSKiran Gunda 			type.polarity_high |= BIT(irq);
59967b563f1SJosh Cartwright 		if (flow_type & IRQF_TRIGGER_FALLING)
600*325255bcSKiran Gunda 			type.polarity_low  |= BIT(irq);
601*325255bcSKiran Gunda 
602*325255bcSKiran Gunda 		flow_handler = handle_edge_irq;
60367b563f1SJosh Cartwright 	} else {
60467b563f1SJosh Cartwright 		if ((flow_type & (IRQF_TRIGGER_HIGH)) &&
60567b563f1SJosh Cartwright 		    (flow_type & (IRQF_TRIGGER_LOW)))
60667b563f1SJosh Cartwright 			return -EINVAL;
60767b563f1SJosh Cartwright 
608*325255bcSKiran Gunda 		type.type &= ~BIT(irq); /* level trig */
60967b563f1SJosh Cartwright 		if (flow_type & IRQF_TRIGGER_HIGH)
610*325255bcSKiran Gunda 			type.polarity_high |= BIT(irq);
61167b563f1SJosh Cartwright 		else
612*325255bcSKiran Gunda 			type.polarity_low  |= BIT(irq);
613*325255bcSKiran Gunda 
614*325255bcSKiran Gunda 		flow_handler = handle_level_irq;
61567b563f1SJosh Cartwright 	}
61667b563f1SJosh Cartwright 
61767b563f1SJosh Cartwright 	qpnpint_spmi_write(d, QPNPINT_REG_SET_TYPE, &type, sizeof(type));
618*325255bcSKiran Gunda 	irq_set_handler_locked(d, flow_handler);
6195f9b2ea3SAbhijeet Dharmapurikar 
62067b563f1SJosh Cartwright 	return 0;
62167b563f1SJosh Cartwright }
62267b563f1SJosh Cartwright 
62360be4230SCourtney Cavin static int qpnpint_get_irqchip_state(struct irq_data *d,
62460be4230SCourtney Cavin 				     enum irqchip_irq_state which,
62560be4230SCourtney Cavin 				     bool *state)
62660be4230SCourtney Cavin {
62702abec36SKiran Gunda 	u8 irq = hwirq_to_irq(d->hwirq);
62860be4230SCourtney Cavin 	u8 status = 0;
62960be4230SCourtney Cavin 
63060be4230SCourtney Cavin 	if (which != IRQCHIP_STATE_LINE_LEVEL)
63160be4230SCourtney Cavin 		return -EINVAL;
63260be4230SCourtney Cavin 
63360be4230SCourtney Cavin 	qpnpint_spmi_read(d, QPNPINT_REG_RT_STS, &status, 1);
63460be4230SCourtney Cavin 	*state = !!(status & BIT(irq));
63560be4230SCourtney Cavin 
63660be4230SCourtney Cavin 	return 0;
63760be4230SCourtney Cavin }
63860be4230SCourtney Cavin 
63967b563f1SJosh Cartwright static struct irq_chip pmic_arb_irqchip = {
64067b563f1SJosh Cartwright 	.name		= "pmic_arb",
64167b563f1SJosh Cartwright 	.irq_ack	= qpnpint_irq_ack,
64267b563f1SJosh Cartwright 	.irq_mask	= qpnpint_irq_mask,
64367b563f1SJosh Cartwright 	.irq_unmask	= qpnpint_irq_unmask,
64467b563f1SJosh Cartwright 	.irq_set_type	= qpnpint_irq_set_type,
64560be4230SCourtney Cavin 	.irq_get_irqchip_state	= qpnpint_get_irqchip_state,
64667b563f1SJosh Cartwright 	.flags		= IRQCHIP_MASK_ON_SUSPEND
64767b563f1SJosh Cartwright 			| IRQCHIP_SKIP_SET_WAKE,
64867b563f1SJosh Cartwright };
64967b563f1SJosh Cartwright 
65067b563f1SJosh Cartwright static int qpnpint_irq_domain_dt_translate(struct irq_domain *d,
65167b563f1SJosh Cartwright 					   struct device_node *controller,
65267b563f1SJosh Cartwright 					   const u32 *intspec,
65367b563f1SJosh Cartwright 					   unsigned int intsize,
65467b563f1SJosh Cartwright 					   unsigned long *out_hwirq,
65567b563f1SJosh Cartwright 					   unsigned int *out_type)
65667b563f1SJosh Cartwright {
65702abec36SKiran Gunda 	struct spmi_pmic_arb *pmic_arb = d->host_data;
6587f1d4e58SAbhijeet Dharmapurikar 	int rc;
659319f6884SAbhijeet Dharmapurikar 	u16 apid;
66067b563f1SJosh Cartwright 
66102abec36SKiran Gunda 	dev_dbg(&pmic_arb->spmic->dev, "intspec[0] 0x%1x intspec[1] 0x%02x intspec[2] 0x%02x\n",
66267b563f1SJosh Cartwright 		intspec[0], intspec[1], intspec[2]);
66367b563f1SJosh Cartwright 
6645d4c9bc7SMarc Zyngier 	if (irq_domain_get_of_node(d) != controller)
66567b563f1SJosh Cartwright 		return -EINVAL;
66667b563f1SJosh Cartwright 	if (intsize != 4)
66767b563f1SJosh Cartwright 		return -EINVAL;
66867b563f1SJosh Cartwright 	if (intspec[0] > 0xF || intspec[1] > 0xFF || intspec[2] > 0x7)
66967b563f1SJosh Cartwright 		return -EINVAL;
67067b563f1SJosh Cartwright 
67102abec36SKiran Gunda 	rc = pmic_arb->ver_ops->ppid_to_apid(pmic_arb, intspec[0],
6727f1d4e58SAbhijeet Dharmapurikar 					    (intspec[1] << 8), &apid);
6737f1d4e58SAbhijeet Dharmapurikar 	if (rc < 0) {
67402abec36SKiran Gunda 		dev_err(&pmic_arb->spmic->dev, "failed to xlate sid = 0x%x, periph = 0x%x, irq = %x rc = %d\n",
6757f1d4e58SAbhijeet Dharmapurikar 		intspec[0], intspec[1], intspec[2], rc);
6767f1d4e58SAbhijeet Dharmapurikar 		return rc;
6777f1d4e58SAbhijeet Dharmapurikar 	}
67867b563f1SJosh Cartwright 
67967b563f1SJosh Cartwright 	/* Keep track of {max,min}_apid for bounding search during interrupt */
68002abec36SKiran Gunda 	if (apid > pmic_arb->max_apid)
68102abec36SKiran Gunda 		pmic_arb->max_apid = apid;
68202abec36SKiran Gunda 	if (apid < pmic_arb->min_apid)
68302abec36SKiran Gunda 		pmic_arb->min_apid = apid;
68467b563f1SJosh Cartwright 
68502abec36SKiran Gunda 	*out_hwirq = spec_to_hwirq(intspec[0], intspec[1], intspec[2], apid);
68667b563f1SJosh Cartwright 	*out_type  = intspec[3] & IRQ_TYPE_SENSE_MASK;
68767b563f1SJosh Cartwright 
68802abec36SKiran Gunda 	dev_dbg(&pmic_arb->spmic->dev, "out_hwirq = %lu\n", *out_hwirq);
68967b563f1SJosh Cartwright 
69067b563f1SJosh Cartwright 	return 0;
69167b563f1SJosh Cartwright }
69267b563f1SJosh Cartwright 
69367b563f1SJosh Cartwright static int qpnpint_irq_domain_map(struct irq_domain *d,
69467b563f1SJosh Cartwright 				  unsigned int virq,
69567b563f1SJosh Cartwright 				  irq_hw_number_t hwirq)
69667b563f1SJosh Cartwright {
69702abec36SKiran Gunda 	struct spmi_pmic_arb *pmic_arb = d->host_data;
69867b563f1SJosh Cartwright 
69902abec36SKiran Gunda 	dev_dbg(&pmic_arb->spmic->dev, "virq = %u, hwirq = %lu\n", virq, hwirq);
70067b563f1SJosh Cartwright 
70167b563f1SJosh Cartwright 	irq_set_chip_and_handler(virq, &pmic_arb_irqchip, handle_level_irq);
70267b563f1SJosh Cartwright 	irq_set_chip_data(virq, d->host_data);
70367b563f1SJosh Cartwright 	irq_set_noprobe(virq);
70467b563f1SJosh Cartwright 	return 0;
70567b563f1SJosh Cartwright }
70667b563f1SJosh Cartwright 
70702abec36SKiran Gunda static int pmic_arb_ppid_to_apid_v1(struct spmi_pmic_arb *pmic_arb, u8 sid,
70802abec36SKiran Gunda 				    u16 addr, u16 *apid)
7097f1d4e58SAbhijeet Dharmapurikar {
7107f1d4e58SAbhijeet Dharmapurikar 	u16 ppid = sid << 8 | ((addr >> 8) & 0xFF);
71102abec36SKiran Gunda 	u32 *mapping_table = pmic_arb->mapping_table;
7127f1d4e58SAbhijeet Dharmapurikar 	int index = 0, i;
7137f1d4e58SAbhijeet Dharmapurikar 	u16 apid_valid;
7147f1d4e58SAbhijeet Dharmapurikar 	u32 data;
7157f1d4e58SAbhijeet Dharmapurikar 
71602abec36SKiran Gunda 	apid_valid = pmic_arb->ppid_to_apid[ppid];
71702abec36SKiran Gunda 	if (apid_valid & PMIC_ARB_APID_VALID) {
71802abec36SKiran Gunda 		*apid = apid_valid & ~PMIC_ARB_APID_VALID;
7197f1d4e58SAbhijeet Dharmapurikar 		return 0;
7207f1d4e58SAbhijeet Dharmapurikar 	}
7217f1d4e58SAbhijeet Dharmapurikar 
7227f1d4e58SAbhijeet Dharmapurikar 	for (i = 0; i < SPMI_MAPPING_TABLE_TREE_DEPTH; ++i) {
72302abec36SKiran Gunda 		if (!test_and_set_bit(index, pmic_arb->mapping_table_valid))
72402abec36SKiran Gunda 			mapping_table[index] = readl_relaxed(pmic_arb->cnfg +
7257f1d4e58SAbhijeet Dharmapurikar 						SPMI_MAPPING_TABLE_REG(index));
7267f1d4e58SAbhijeet Dharmapurikar 
7277f1d4e58SAbhijeet Dharmapurikar 		data = mapping_table[index];
7287f1d4e58SAbhijeet Dharmapurikar 
7297f1d4e58SAbhijeet Dharmapurikar 		if (ppid & BIT(SPMI_MAPPING_BIT_INDEX(data))) {
7307f1d4e58SAbhijeet Dharmapurikar 			if (SPMI_MAPPING_BIT_IS_1_FLAG(data)) {
7317f1d4e58SAbhijeet Dharmapurikar 				index = SPMI_MAPPING_BIT_IS_1_RESULT(data);
7327f1d4e58SAbhijeet Dharmapurikar 			} else {
7337f1d4e58SAbhijeet Dharmapurikar 				*apid = SPMI_MAPPING_BIT_IS_1_RESULT(data);
73402abec36SKiran Gunda 				pmic_arb->ppid_to_apid[ppid]
73502abec36SKiran Gunda 					= *apid | PMIC_ARB_APID_VALID;
73602abec36SKiran Gunda 				pmic_arb->apid_data[*apid].ppid = ppid;
7377f1d4e58SAbhijeet Dharmapurikar 				return 0;
7387f1d4e58SAbhijeet Dharmapurikar 			}
7397f1d4e58SAbhijeet Dharmapurikar 		} else {
7407f1d4e58SAbhijeet Dharmapurikar 			if (SPMI_MAPPING_BIT_IS_0_FLAG(data)) {
7417f1d4e58SAbhijeet Dharmapurikar 				index = SPMI_MAPPING_BIT_IS_0_RESULT(data);
7427f1d4e58SAbhijeet Dharmapurikar 			} else {
7437f1d4e58SAbhijeet Dharmapurikar 				*apid = SPMI_MAPPING_BIT_IS_0_RESULT(data);
74402abec36SKiran Gunda 				pmic_arb->ppid_to_apid[ppid]
74502abec36SKiran Gunda 					= *apid | PMIC_ARB_APID_VALID;
74602abec36SKiran Gunda 				pmic_arb->apid_data[*apid].ppid = ppid;
7477f1d4e58SAbhijeet Dharmapurikar 				return 0;
7487f1d4e58SAbhijeet Dharmapurikar 			}
7497f1d4e58SAbhijeet Dharmapurikar 		}
7507f1d4e58SAbhijeet Dharmapurikar 	}
7517f1d4e58SAbhijeet Dharmapurikar 
7527f1d4e58SAbhijeet Dharmapurikar 	return -ENODEV;
7537f1d4e58SAbhijeet Dharmapurikar }
7547f1d4e58SAbhijeet Dharmapurikar 
755d0c6ae41SGilad Avidov /* v1 offset per ee */
75602abec36SKiran Gunda static int pmic_arb_offset_v1(struct spmi_pmic_arb *pmic_arb, u8 sid, u16 addr,
75702abec36SKiran Gunda 				u32 *offset)
758d0c6ae41SGilad Avidov {
75902abec36SKiran Gunda 	*offset = 0x800 + 0x80 * pmic_arb->channel;
760987a9f12SStephen Boyd 	return 0;
761d0c6ae41SGilad Avidov }
762d0c6ae41SGilad Avidov 
76302abec36SKiran Gunda static u16 pmic_arb_find_apid(struct spmi_pmic_arb *pmic_arb, u16 ppid)
764987a9f12SStephen Boyd {
765f2f31564SKiran Gunda 	struct apid_data *apidd = &pmic_arb->apid_data[pmic_arb->last_apid];
766987a9f12SStephen Boyd 	u32 regval, offset;
767f2f31564SKiran Gunda 	u16 id, apid;
768987a9f12SStephen Boyd 
769987a9f12SStephen Boyd 	/*
770f2f31564SKiran Gunda 	 * PMIC_ARB_REG_APID is a table in HW mapping apid to ppid.
7711ef1ce4eSAbhijeet Dharmapurikar 	 * ppid_to_apid is an in-memory invert of that table.
772987a9f12SStephen Boyd 	 */
773f2f31564SKiran Gunda 	for (apid = pmic_arb->last_apid; ; apid++, apidd++) {
77402abec36SKiran Gunda 		offset = PMIC_ARB_REG_APID(apid);
77502abec36SKiran Gunda 		if (offset >= pmic_arb->core_size)
776987a9f12SStephen Boyd 			break;
777987a9f12SStephen Boyd 
77802abec36SKiran Gunda 		regval = readl_relaxed(pmic_arb->cnfg +
779b319b592SKiran Gunda 				      SPMI_OWNERSHIP_TABLE_REG(apid));
780f2f31564SKiran Gunda 		apidd->owner = SPMI_OWNERSHIP_PERIPH2OWNER(regval);
781b319b592SKiran Gunda 
78202abec36SKiran Gunda 		regval = readl_relaxed(pmic_arb->core + offset);
783987a9f12SStephen Boyd 		if (!regval)
784987a9f12SStephen Boyd 			continue;
785987a9f12SStephen Boyd 
786987a9f12SStephen Boyd 		id = (regval >> 8) & PMIC_ARB_PPID_MASK;
78702abec36SKiran Gunda 		pmic_arb->ppid_to_apid[id] = apid | PMIC_ARB_APID_VALID;
788f2f31564SKiran Gunda 		apidd->ppid = id;
789987a9f12SStephen Boyd 		if (id == ppid) {
79002abec36SKiran Gunda 			apid |= PMIC_ARB_APID_VALID;
791987a9f12SStephen Boyd 			break;
792987a9f12SStephen Boyd 		}
793987a9f12SStephen Boyd 	}
79402abec36SKiran Gunda 	pmic_arb->last_apid = apid & ~PMIC_ARB_APID_VALID;
795987a9f12SStephen Boyd 
7961ef1ce4eSAbhijeet Dharmapurikar 	return apid;
797987a9f12SStephen Boyd }
798987a9f12SStephen Boyd 
79902abec36SKiran Gunda static int pmic_arb_ppid_to_apid_v2(struct spmi_pmic_arb *pmic_arb, u8 sid,
80002abec36SKiran Gunda 				    u16 addr, u16 *apid)
80157102ad7SAbhijeet Dharmapurikar {
80257102ad7SAbhijeet Dharmapurikar 	u16 ppid = (sid << 8) | (addr >> 8);
8037f1d4e58SAbhijeet Dharmapurikar 	u16 apid_valid;
80457102ad7SAbhijeet Dharmapurikar 
80502abec36SKiran Gunda 	apid_valid = pmic_arb->ppid_to_apid[ppid];
80602abec36SKiran Gunda 	if (!(apid_valid & PMIC_ARB_APID_VALID))
80702abec36SKiran Gunda 		apid_valid = pmic_arb_find_apid(pmic_arb, ppid);
80802abec36SKiran Gunda 	if (!(apid_valid & PMIC_ARB_APID_VALID))
80957102ad7SAbhijeet Dharmapurikar 		return -ENODEV;
81057102ad7SAbhijeet Dharmapurikar 
81102abec36SKiran Gunda 	*apid = apid_valid & ~PMIC_ARB_APID_VALID;
8127f1d4e58SAbhijeet Dharmapurikar 	return 0;
8137f1d4e58SAbhijeet Dharmapurikar }
8147f1d4e58SAbhijeet Dharmapurikar 
8151ef1ce4eSAbhijeet Dharmapurikar /* v2 offset per ppid and per ee */
81602abec36SKiran Gunda static int pmic_arb_offset_v2(struct spmi_pmic_arb *pmic_arb, u8 sid, u16 addr,
81702abec36SKiran Gunda 				u32 *offset)
818d0c6ae41SGilad Avidov {
819319f6884SAbhijeet Dharmapurikar 	u16 apid;
8207f1d4e58SAbhijeet Dharmapurikar 	int rc;
821d0c6ae41SGilad Avidov 
82202abec36SKiran Gunda 	rc = pmic_arb_ppid_to_apid_v2(pmic_arb, sid, addr, &apid);
8237f1d4e58SAbhijeet Dharmapurikar 	if (rc < 0)
8247f1d4e58SAbhijeet Dharmapurikar 		return rc;
825987a9f12SStephen Boyd 
82602abec36SKiran Gunda 	*offset = 0x1000 * pmic_arb->ee + 0x8000 * apid;
827987a9f12SStephen Boyd 	return 0;
828d0c6ae41SGilad Avidov }
829d0c6ae41SGilad Avidov 
830d0c6ae41SGilad Avidov static u32 pmic_arb_fmt_cmd_v1(u8 opc, u8 sid, u16 addr, u8 bc)
831d0c6ae41SGilad Avidov {
832d0c6ae41SGilad Avidov 	return (opc << 27) | ((sid & 0xf) << 20) | (addr << 4) | (bc & 0x7);
833d0c6ae41SGilad Avidov }
834d0c6ae41SGilad Avidov 
835d0c6ae41SGilad Avidov static u32 pmic_arb_fmt_cmd_v2(u8 opc, u8 sid, u16 addr, u8 bc)
836d0c6ae41SGilad Avidov {
837d0c6ae41SGilad Avidov 	return (opc << 27) | ((addr & 0xff) << 4) | (bc & 0x7);
838d0c6ae41SGilad Avidov }
839d0c6ae41SGilad Avidov 
840319f6884SAbhijeet Dharmapurikar static u32 pmic_arb_owner_acc_status_v1(u8 m, u16 n)
841d0c6ae41SGilad Avidov {
842d0c6ae41SGilad Avidov 	return 0x20 * m + 0x4 * n;
843d0c6ae41SGilad Avidov }
844d0c6ae41SGilad Avidov 
845319f6884SAbhijeet Dharmapurikar static u32 pmic_arb_owner_acc_status_v2(u8 m, u16 n)
846d0c6ae41SGilad Avidov {
847d0c6ae41SGilad Avidov 	return 0x100000 + 0x1000 * m + 0x4 * n;
848d0c6ae41SGilad Avidov }
849d0c6ae41SGilad Avidov 
850319f6884SAbhijeet Dharmapurikar static u32 pmic_arb_owner_acc_status_v3(u8 m, u16 n)
851319f6884SAbhijeet Dharmapurikar {
852319f6884SAbhijeet Dharmapurikar 	return 0x200000 + 0x1000 * m + 0x4 * n;
853319f6884SAbhijeet Dharmapurikar }
854319f6884SAbhijeet Dharmapurikar 
855319f6884SAbhijeet Dharmapurikar static u32 pmic_arb_acc_enable_v1(u16 n)
856d0c6ae41SGilad Avidov {
857d0c6ae41SGilad Avidov 	return 0x200 + 0x4 * n;
858d0c6ae41SGilad Avidov }
859d0c6ae41SGilad Avidov 
860319f6884SAbhijeet Dharmapurikar static u32 pmic_arb_acc_enable_v2(u16 n)
861d0c6ae41SGilad Avidov {
862d0c6ae41SGilad Avidov 	return 0x1000 * n;
863d0c6ae41SGilad Avidov }
864d0c6ae41SGilad Avidov 
865319f6884SAbhijeet Dharmapurikar static u32 pmic_arb_irq_status_v1(u16 n)
866d0c6ae41SGilad Avidov {
867d0c6ae41SGilad Avidov 	return 0x600 + 0x4 * n;
868d0c6ae41SGilad Avidov }
869d0c6ae41SGilad Avidov 
870319f6884SAbhijeet Dharmapurikar static u32 pmic_arb_irq_status_v2(u16 n)
871d0c6ae41SGilad Avidov {
872d0c6ae41SGilad Avidov 	return 0x4 + 0x1000 * n;
873d0c6ae41SGilad Avidov }
874d0c6ae41SGilad Avidov 
875319f6884SAbhijeet Dharmapurikar static u32 pmic_arb_irq_clear_v1(u16 n)
876d0c6ae41SGilad Avidov {
877d0c6ae41SGilad Avidov 	return 0xA00 + 0x4 * n;
878d0c6ae41SGilad Avidov }
879d0c6ae41SGilad Avidov 
880319f6884SAbhijeet Dharmapurikar static u32 pmic_arb_irq_clear_v2(u16 n)
881d0c6ae41SGilad Avidov {
882d0c6ae41SGilad Avidov 	return 0x8 + 0x1000 * n;
883d0c6ae41SGilad Avidov }
884d0c6ae41SGilad Avidov 
885d0c6ae41SGilad Avidov static const struct pmic_arb_ver_ops pmic_arb_v1 = {
886319f6884SAbhijeet Dharmapurikar 	.ver_str		= "v1",
8877f1d4e58SAbhijeet Dharmapurikar 	.ppid_to_apid		= pmic_arb_ppid_to_apid_v1,
888d0c6ae41SGilad Avidov 	.non_data_cmd		= pmic_arb_non_data_cmd_v1,
889d0c6ae41SGilad Avidov 	.offset			= pmic_arb_offset_v1,
890d0c6ae41SGilad Avidov 	.fmt_cmd		= pmic_arb_fmt_cmd_v1,
891d0c6ae41SGilad Avidov 	.owner_acc_status	= pmic_arb_owner_acc_status_v1,
892d0c6ae41SGilad Avidov 	.acc_enable		= pmic_arb_acc_enable_v1,
893d0c6ae41SGilad Avidov 	.irq_status		= pmic_arb_irq_status_v1,
894d0c6ae41SGilad Avidov 	.irq_clear		= pmic_arb_irq_clear_v1,
895d0c6ae41SGilad Avidov };
896d0c6ae41SGilad Avidov 
897d0c6ae41SGilad Avidov static const struct pmic_arb_ver_ops pmic_arb_v2 = {
898319f6884SAbhijeet Dharmapurikar 	.ver_str		= "v2",
8997f1d4e58SAbhijeet Dharmapurikar 	.ppid_to_apid		= pmic_arb_ppid_to_apid_v2,
900d0c6ae41SGilad Avidov 	.non_data_cmd		= pmic_arb_non_data_cmd_v2,
901d0c6ae41SGilad Avidov 	.offset			= pmic_arb_offset_v2,
902d0c6ae41SGilad Avidov 	.fmt_cmd		= pmic_arb_fmt_cmd_v2,
903d0c6ae41SGilad Avidov 	.owner_acc_status	= pmic_arb_owner_acc_status_v2,
904d0c6ae41SGilad Avidov 	.acc_enable		= pmic_arb_acc_enable_v2,
905d0c6ae41SGilad Avidov 	.irq_status		= pmic_arb_irq_status_v2,
906d0c6ae41SGilad Avidov 	.irq_clear		= pmic_arb_irq_clear_v2,
907d0c6ae41SGilad Avidov };
908d0c6ae41SGilad Avidov 
909319f6884SAbhijeet Dharmapurikar static const struct pmic_arb_ver_ops pmic_arb_v3 = {
910319f6884SAbhijeet Dharmapurikar 	.ver_str		= "v3",
911319f6884SAbhijeet Dharmapurikar 	.ppid_to_apid		= pmic_arb_ppid_to_apid_v2,
912319f6884SAbhijeet Dharmapurikar 	.non_data_cmd		= pmic_arb_non_data_cmd_v2,
913319f6884SAbhijeet Dharmapurikar 	.offset			= pmic_arb_offset_v2,
914319f6884SAbhijeet Dharmapurikar 	.fmt_cmd		= pmic_arb_fmt_cmd_v2,
915319f6884SAbhijeet Dharmapurikar 	.owner_acc_status	= pmic_arb_owner_acc_status_v3,
916319f6884SAbhijeet Dharmapurikar 	.acc_enable		= pmic_arb_acc_enable_v2,
917319f6884SAbhijeet Dharmapurikar 	.irq_status		= pmic_arb_irq_status_v2,
918319f6884SAbhijeet Dharmapurikar 	.irq_clear		= pmic_arb_irq_clear_v2,
919319f6884SAbhijeet Dharmapurikar };
920319f6884SAbhijeet Dharmapurikar 
92167b563f1SJosh Cartwright static const struct irq_domain_ops pmic_arb_irq_domain_ops = {
92267b563f1SJosh Cartwright 	.map	= qpnpint_irq_domain_map,
92367b563f1SJosh Cartwright 	.xlate	= qpnpint_irq_domain_dt_translate,
92467b563f1SJosh Cartwright };
92567b563f1SJosh Cartwright 
92639ae93e3SKenneth Heitke static int spmi_pmic_arb_probe(struct platform_device *pdev)
92739ae93e3SKenneth Heitke {
92802abec36SKiran Gunda 	struct spmi_pmic_arb *pmic_arb;
92939ae93e3SKenneth Heitke 	struct spmi_controller *ctrl;
93039ae93e3SKenneth Heitke 	struct resource *res;
931d0c6ae41SGilad Avidov 	void __iomem *core;
932d0c6ae41SGilad Avidov 	u32 channel, ee, hw_ver;
933987a9f12SStephen Boyd 	int err;
93439ae93e3SKenneth Heitke 
93502abec36SKiran Gunda 	ctrl = spmi_controller_alloc(&pdev->dev, sizeof(*pmic_arb));
93639ae93e3SKenneth Heitke 	if (!ctrl)
93739ae93e3SKenneth Heitke 		return -ENOMEM;
93839ae93e3SKenneth Heitke 
93902abec36SKiran Gunda 	pmic_arb = spmi_controller_get_drvdata(ctrl);
94002abec36SKiran Gunda 	pmic_arb->spmic = ctrl;
94139ae93e3SKenneth Heitke 
94239ae93e3SKenneth Heitke 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "core");
94302abec36SKiran Gunda 	pmic_arb->core_size = resource_size(res);
94457102ad7SAbhijeet Dharmapurikar 
945d0c6ae41SGilad Avidov 	core = devm_ioremap_resource(&ctrl->dev, res);
946d0c6ae41SGilad Avidov 	if (IS_ERR(core)) {
947d0c6ae41SGilad Avidov 		err = PTR_ERR(core);
94839ae93e3SKenneth Heitke 		goto err_put_ctrl;
94939ae93e3SKenneth Heitke 	}
95039ae93e3SKenneth Heitke 
95102abec36SKiran Gunda 	pmic_arb->ppid_to_apid = devm_kcalloc(&ctrl->dev, PMIC_ARB_MAX_PPID,
95202abec36SKiran Gunda 					      sizeof(*pmic_arb->ppid_to_apid),
95302abec36SKiran Gunda 					      GFP_KERNEL);
95402abec36SKiran Gunda 	if (!pmic_arb->ppid_to_apid) {
955eba9718eSStephen Boyd 		err = -ENOMEM;
956eba9718eSStephen Boyd 		goto err_put_ctrl;
957eba9718eSStephen Boyd 	}
958eba9718eSStephen Boyd 
959d0c6ae41SGilad Avidov 	hw_ver = readl_relaxed(core + PMIC_ARB_VERSION);
960d0c6ae41SGilad Avidov 
961319f6884SAbhijeet Dharmapurikar 	if (hw_ver < PMIC_ARB_VERSION_V2_MIN) {
96202abec36SKiran Gunda 		pmic_arb->ver_ops = &pmic_arb_v1;
96302abec36SKiran Gunda 		pmic_arb->wr_base = core;
96402abec36SKiran Gunda 		pmic_arb->rd_base = core;
965d0c6ae41SGilad Avidov 	} else {
96602abec36SKiran Gunda 		pmic_arb->core = core;
967319f6884SAbhijeet Dharmapurikar 
968319f6884SAbhijeet Dharmapurikar 		if (hw_ver < PMIC_ARB_VERSION_V3_MIN)
96902abec36SKiran Gunda 			pmic_arb->ver_ops = &pmic_arb_v2;
970319f6884SAbhijeet Dharmapurikar 		else
97102abec36SKiran Gunda 			pmic_arb->ver_ops = &pmic_arb_v3;
972d0c6ae41SGilad Avidov 
973d0c6ae41SGilad Avidov 		res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
974d0c6ae41SGilad Avidov 						   "obsrvr");
97502abec36SKiran Gunda 		pmic_arb->rd_base = devm_ioremap_resource(&ctrl->dev, res);
97602abec36SKiran Gunda 		if (IS_ERR(pmic_arb->rd_base)) {
97702abec36SKiran Gunda 			err = PTR_ERR(pmic_arb->rd_base);
978d0c6ae41SGilad Avidov 			goto err_put_ctrl;
979d0c6ae41SGilad Avidov 		}
980d0c6ae41SGilad Avidov 
981d0c6ae41SGilad Avidov 		res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
982d0c6ae41SGilad Avidov 						   "chnls");
98302abec36SKiran Gunda 		pmic_arb->wr_base = devm_ioremap_resource(&ctrl->dev, res);
98402abec36SKiran Gunda 		if (IS_ERR(pmic_arb->wr_base)) {
98502abec36SKiran Gunda 			err = PTR_ERR(pmic_arb->wr_base);
986d0c6ae41SGilad Avidov 			goto err_put_ctrl;
987d0c6ae41SGilad Avidov 		}
988d0c6ae41SGilad Avidov 	}
989d0c6ae41SGilad Avidov 
990319f6884SAbhijeet Dharmapurikar 	dev_info(&ctrl->dev, "PMIC arbiter version %s (0x%x)\n",
99102abec36SKiran Gunda 		 pmic_arb->ver_ops->ver_str, hw_ver);
992319f6884SAbhijeet Dharmapurikar 
99339ae93e3SKenneth Heitke 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "intr");
99402abec36SKiran Gunda 	pmic_arb->intr = devm_ioremap_resource(&ctrl->dev, res);
99502abec36SKiran Gunda 	if (IS_ERR(pmic_arb->intr)) {
99602abec36SKiran Gunda 		err = PTR_ERR(pmic_arb->intr);
99739ae93e3SKenneth Heitke 		goto err_put_ctrl;
99839ae93e3SKenneth Heitke 	}
99939ae93e3SKenneth Heitke 
100039ae93e3SKenneth Heitke 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cnfg");
100102abec36SKiran Gunda 	pmic_arb->cnfg = devm_ioremap_resource(&ctrl->dev, res);
100202abec36SKiran Gunda 	if (IS_ERR(pmic_arb->cnfg)) {
100302abec36SKiran Gunda 		err = PTR_ERR(pmic_arb->cnfg);
100439ae93e3SKenneth Heitke 		goto err_put_ctrl;
100539ae93e3SKenneth Heitke 	}
100639ae93e3SKenneth Heitke 
100702abec36SKiran Gunda 	pmic_arb->irq = platform_get_irq_byname(pdev, "periph_irq");
100802abec36SKiran Gunda 	if (pmic_arb->irq < 0) {
100902abec36SKiran Gunda 		err = pmic_arb->irq;
101067b563f1SJosh Cartwright 		goto err_put_ctrl;
101167b563f1SJosh Cartwright 	}
101267b563f1SJosh Cartwright 
101339ae93e3SKenneth Heitke 	err = of_property_read_u32(pdev->dev.of_node, "qcom,channel", &channel);
101439ae93e3SKenneth Heitke 	if (err) {
101539ae93e3SKenneth Heitke 		dev_err(&pdev->dev, "channel unspecified.\n");
101639ae93e3SKenneth Heitke 		goto err_put_ctrl;
101739ae93e3SKenneth Heitke 	}
101839ae93e3SKenneth Heitke 
101939ae93e3SKenneth Heitke 	if (channel > 5) {
102039ae93e3SKenneth Heitke 		dev_err(&pdev->dev, "invalid channel (%u) specified.\n",
102139ae93e3SKenneth Heitke 			channel);
1022e98cc182SChristophe JAILLET 		err = -EINVAL;
102339ae93e3SKenneth Heitke 		goto err_put_ctrl;
102439ae93e3SKenneth Heitke 	}
102539ae93e3SKenneth Heitke 
102602abec36SKiran Gunda 	pmic_arb->channel = channel;
102739ae93e3SKenneth Heitke 
102867b563f1SJosh Cartwright 	err = of_property_read_u32(pdev->dev.of_node, "qcom,ee", &ee);
102967b563f1SJosh Cartwright 	if (err) {
103067b563f1SJosh Cartwright 		dev_err(&pdev->dev, "EE unspecified.\n");
103167b563f1SJosh Cartwright 		goto err_put_ctrl;
103267b563f1SJosh Cartwright 	}
103367b563f1SJosh Cartwright 
103467b563f1SJosh Cartwright 	if (ee > 5) {
103567b563f1SJosh Cartwright 		dev_err(&pdev->dev, "invalid EE (%u) specified\n", ee);
103667b563f1SJosh Cartwright 		err = -EINVAL;
103767b563f1SJosh Cartwright 		goto err_put_ctrl;
103867b563f1SJosh Cartwright 	}
103967b563f1SJosh Cartwright 
104002abec36SKiran Gunda 	pmic_arb->ee = ee;
104167b563f1SJosh Cartwright 
104202abec36SKiran Gunda 	pmic_arb->mapping_table = devm_kcalloc(&ctrl->dev,
104302abec36SKiran Gunda 					PMIC_ARB_MAX_PERIPHS - 1,
104402abec36SKiran Gunda 					sizeof(*pmic_arb->mapping_table),
104502abec36SKiran Gunda 					GFP_KERNEL);
104602abec36SKiran Gunda 	if (!pmic_arb->mapping_table) {
1047987a9f12SStephen Boyd 		err = -ENOMEM;
1048987a9f12SStephen Boyd 		goto err_put_ctrl;
1049987a9f12SStephen Boyd 	}
105067b563f1SJosh Cartwright 
105167b563f1SJosh Cartwright 	/* Initialize max_apid/min_apid to the opposite bounds, during
105267b563f1SJosh Cartwright 	 * the irq domain translation, we are sure to update these */
105302abec36SKiran Gunda 	pmic_arb->max_apid = 0;
105402abec36SKiran Gunda 	pmic_arb->min_apid = PMIC_ARB_MAX_PERIPHS - 1;
105567b563f1SJosh Cartwright 
105639ae93e3SKenneth Heitke 	platform_set_drvdata(pdev, ctrl);
105702abec36SKiran Gunda 	raw_spin_lock_init(&pmic_arb->lock);
105839ae93e3SKenneth Heitke 
105939ae93e3SKenneth Heitke 	ctrl->cmd = pmic_arb_cmd;
106039ae93e3SKenneth Heitke 	ctrl->read_cmd = pmic_arb_read_cmd;
106139ae93e3SKenneth Heitke 	ctrl->write_cmd = pmic_arb_write_cmd;
106239ae93e3SKenneth Heitke 
106367b563f1SJosh Cartwright 	dev_dbg(&pdev->dev, "adding irq domain\n");
106402abec36SKiran Gunda 	pmic_arb->domain = irq_domain_add_tree(pdev->dev.of_node,
106502abec36SKiran Gunda 					 &pmic_arb_irq_domain_ops, pmic_arb);
106602abec36SKiran Gunda 	if (!pmic_arb->domain) {
106767b563f1SJosh Cartwright 		dev_err(&pdev->dev, "unable to create irq_domain\n");
106867b563f1SJosh Cartwright 		err = -ENOMEM;
106967b563f1SJosh Cartwright 		goto err_put_ctrl;
107067b563f1SJosh Cartwright 	}
107167b563f1SJosh Cartwright 
107202abec36SKiran Gunda 	irq_set_chained_handler_and_data(pmic_arb->irq, pmic_arb_chained_irq,
107302abec36SKiran Gunda 					pmic_arb);
107402abec36SKiran Gunda 	enable_irq_wake(pmic_arb->irq);
107567b563f1SJosh Cartwright 
107639ae93e3SKenneth Heitke 	err = spmi_controller_add(ctrl);
107739ae93e3SKenneth Heitke 	if (err)
107867b563f1SJosh Cartwright 		goto err_domain_remove;
107939ae93e3SKenneth Heitke 
108039ae93e3SKenneth Heitke 	return 0;
108139ae93e3SKenneth Heitke 
108267b563f1SJosh Cartwright err_domain_remove:
108302abec36SKiran Gunda 	irq_set_chained_handler_and_data(pmic_arb->irq, NULL, NULL);
108402abec36SKiran Gunda 	irq_domain_remove(pmic_arb->domain);
108539ae93e3SKenneth Heitke err_put_ctrl:
108639ae93e3SKenneth Heitke 	spmi_controller_put(ctrl);
108739ae93e3SKenneth Heitke 	return err;
108839ae93e3SKenneth Heitke }
108939ae93e3SKenneth Heitke 
109039ae93e3SKenneth Heitke static int spmi_pmic_arb_remove(struct platform_device *pdev)
109139ae93e3SKenneth Heitke {
109239ae93e3SKenneth Heitke 	struct spmi_controller *ctrl = platform_get_drvdata(pdev);
109302abec36SKiran Gunda 	struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl);
109439ae93e3SKenneth Heitke 	spmi_controller_remove(ctrl);
109502abec36SKiran Gunda 	irq_set_chained_handler_and_data(pmic_arb->irq, NULL, NULL);
109602abec36SKiran Gunda 	irq_domain_remove(pmic_arb->domain);
109739ae93e3SKenneth Heitke 	spmi_controller_put(ctrl);
109839ae93e3SKenneth Heitke 	return 0;
109939ae93e3SKenneth Heitke }
110039ae93e3SKenneth Heitke 
110139ae93e3SKenneth Heitke static const struct of_device_id spmi_pmic_arb_match_table[] = {
110239ae93e3SKenneth Heitke 	{ .compatible = "qcom,spmi-pmic-arb", },
110339ae93e3SKenneth Heitke 	{},
110439ae93e3SKenneth Heitke };
110539ae93e3SKenneth Heitke MODULE_DEVICE_TABLE(of, spmi_pmic_arb_match_table);
110639ae93e3SKenneth Heitke 
110739ae93e3SKenneth Heitke static struct platform_driver spmi_pmic_arb_driver = {
110839ae93e3SKenneth Heitke 	.probe		= spmi_pmic_arb_probe,
110939ae93e3SKenneth Heitke 	.remove		= spmi_pmic_arb_remove,
111039ae93e3SKenneth Heitke 	.driver		= {
111139ae93e3SKenneth Heitke 		.name	= "spmi_pmic_arb",
111239ae93e3SKenneth Heitke 		.of_match_table = spmi_pmic_arb_match_table,
111339ae93e3SKenneth Heitke 	},
111439ae93e3SKenneth Heitke };
111539ae93e3SKenneth Heitke module_platform_driver(spmi_pmic_arb_driver);
111639ae93e3SKenneth Heitke 
111739ae93e3SKenneth Heitke MODULE_LICENSE("GPL v2");
111839ae93e3SKenneth Heitke MODULE_ALIAS("platform:spmi_pmic_arb");
1119