xref: /linux/drivers/spmi/spmi-pmic-arb.c (revision 57102ad79284facacc7a94879cf3e11452557da3)
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
3139ae93e3SKenneth Heitke #define PMIC_ARB_INT_EN			0x0004
3239ae93e3SKenneth Heitke 
33d0c6ae41SGilad Avidov /* PMIC Arbiter channel registers offsets */
34d0c6ae41SGilad Avidov #define PMIC_ARB_CMD			0x00
35d0c6ae41SGilad Avidov #define PMIC_ARB_CONFIG			0x04
36d0c6ae41SGilad Avidov #define PMIC_ARB_STATUS			0x08
37d0c6ae41SGilad Avidov #define PMIC_ARB_WDATA0			0x10
38d0c6ae41SGilad Avidov #define PMIC_ARB_WDATA1			0x14
39d0c6ae41SGilad Avidov #define PMIC_ARB_RDATA0			0x18
40d0c6ae41SGilad Avidov #define PMIC_ARB_RDATA1			0x1C
41d0c6ae41SGilad Avidov #define PMIC_ARB_REG_CHNL(N)		(0x800 + 0x4 * (N))
4239ae93e3SKenneth Heitke 
4339ae93e3SKenneth Heitke /* Mapping Table */
4439ae93e3SKenneth Heitke #define SPMI_MAPPING_TABLE_REG(N)	(0x0B00 + (4 * (N)))
4539ae93e3SKenneth Heitke #define SPMI_MAPPING_BIT_INDEX(X)	(((X) >> 18) & 0xF)
4639ae93e3SKenneth Heitke #define SPMI_MAPPING_BIT_IS_0_FLAG(X)	(((X) >> 17) & 0x1)
4739ae93e3SKenneth Heitke #define SPMI_MAPPING_BIT_IS_0_RESULT(X)	(((X) >> 9) & 0xFF)
4839ae93e3SKenneth Heitke #define SPMI_MAPPING_BIT_IS_1_FLAG(X)	(((X) >> 8) & 0x1)
4939ae93e3SKenneth Heitke #define SPMI_MAPPING_BIT_IS_1_RESULT(X)	(((X) >> 0) & 0xFF)
5039ae93e3SKenneth Heitke 
5139ae93e3SKenneth Heitke #define SPMI_MAPPING_TABLE_TREE_DEPTH	16	/* Maximum of 16-bits */
52987a9f12SStephen Boyd #define PMIC_ARB_MAX_PPID		BIT(12) /* PPID is 12bit */
53987a9f12SStephen Boyd #define PMIC_ARB_CHAN_VALID		BIT(15)
5439ae93e3SKenneth Heitke 
5539ae93e3SKenneth Heitke /* Ownership Table */
5639ae93e3SKenneth Heitke #define SPMI_OWNERSHIP_TABLE_REG(N)	(0x0700 + (4 * (N)))
5739ae93e3SKenneth Heitke #define SPMI_OWNERSHIP_PERIPH2OWNER(X)	((X) & 0x7)
5839ae93e3SKenneth Heitke 
5939ae93e3SKenneth Heitke /* Channel Status fields */
6039ae93e3SKenneth Heitke enum pmic_arb_chnl_status {
6139ae93e3SKenneth Heitke 	PMIC_ARB_STATUS_DONE	= (1 << 0),
6239ae93e3SKenneth Heitke 	PMIC_ARB_STATUS_FAILURE	= (1 << 1),
6339ae93e3SKenneth Heitke 	PMIC_ARB_STATUS_DENIED	= (1 << 2),
6439ae93e3SKenneth Heitke 	PMIC_ARB_STATUS_DROPPED	= (1 << 3),
6539ae93e3SKenneth Heitke };
6639ae93e3SKenneth Heitke 
6739ae93e3SKenneth Heitke /* Command register fields */
6839ae93e3SKenneth Heitke #define PMIC_ARB_CMD_MAX_BYTE_COUNT	8
6939ae93e3SKenneth Heitke 
7039ae93e3SKenneth Heitke /* Command Opcodes */
7139ae93e3SKenneth Heitke enum pmic_arb_cmd_op_code {
7239ae93e3SKenneth Heitke 	PMIC_ARB_OP_EXT_WRITEL = 0,
7339ae93e3SKenneth Heitke 	PMIC_ARB_OP_EXT_READL = 1,
7439ae93e3SKenneth Heitke 	PMIC_ARB_OP_EXT_WRITE = 2,
7539ae93e3SKenneth Heitke 	PMIC_ARB_OP_RESET = 3,
7639ae93e3SKenneth Heitke 	PMIC_ARB_OP_SLEEP = 4,
7739ae93e3SKenneth Heitke 	PMIC_ARB_OP_SHUTDOWN = 5,
7839ae93e3SKenneth Heitke 	PMIC_ARB_OP_WAKEUP = 6,
7939ae93e3SKenneth Heitke 	PMIC_ARB_OP_AUTHENTICATE = 7,
8039ae93e3SKenneth Heitke 	PMIC_ARB_OP_MSTR_READ = 8,
8139ae93e3SKenneth Heitke 	PMIC_ARB_OP_MSTR_WRITE = 9,
8239ae93e3SKenneth Heitke 	PMIC_ARB_OP_EXT_READ = 13,
8339ae93e3SKenneth Heitke 	PMIC_ARB_OP_WRITE = 14,
8439ae93e3SKenneth Heitke 	PMIC_ARB_OP_READ = 15,
8539ae93e3SKenneth Heitke 	PMIC_ARB_OP_ZERO_WRITE = 16,
8639ae93e3SKenneth Heitke };
8739ae93e3SKenneth Heitke 
8839ae93e3SKenneth Heitke /* Maximum number of support PMIC peripherals */
89987a9f12SStephen Boyd #define PMIC_ARB_MAX_PERIPHS		512
9039ae93e3SKenneth Heitke #define PMIC_ARB_TIMEOUT_US		100
9139ae93e3SKenneth Heitke #define PMIC_ARB_MAX_TRANS_BYTES	(8)
9239ae93e3SKenneth Heitke 
9339ae93e3SKenneth Heitke #define PMIC_ARB_APID_MASK		0xFF
9439ae93e3SKenneth Heitke #define PMIC_ARB_PPID_MASK		0xFFF
9539ae93e3SKenneth Heitke 
9639ae93e3SKenneth Heitke /* interrupt enable bit */
9739ae93e3SKenneth Heitke #define SPMI_PIC_ACC_ENABLE_BIT		BIT(0)
9839ae93e3SKenneth Heitke 
99d0c6ae41SGilad Avidov struct pmic_arb_ver_ops;
100d0c6ae41SGilad Avidov 
10139ae93e3SKenneth Heitke /**
10239ae93e3SKenneth Heitke  * spmi_pmic_arb_dev - SPMI PMIC Arbiter object
10339ae93e3SKenneth Heitke  *
104d0c6ae41SGilad Avidov  * @rd_base:		on v1 "core", on v2 "observer" register base off DT.
105d0c6ae41SGilad Avidov  * @wr_base:		on v1 "core", on v2 "chnls"    register base off DT.
10639ae93e3SKenneth Heitke  * @intr:		address of the SPMI interrupt control registers.
10739ae93e3SKenneth Heitke  * @cnfg:		address of the PMIC Arbiter configuration registers.
10839ae93e3SKenneth Heitke  * @lock:		lock to synchronize accesses.
109d0c6ae41SGilad Avidov  * @channel:		execution environment channel to use for accesses.
11067b563f1SJosh Cartwright  * @irq:		PMIC ARB interrupt.
11167b563f1SJosh Cartwright  * @ee:			the current Execution Environment
11267b563f1SJosh Cartwright  * @min_apid:		minimum APID (used for bounding IRQ search)
11367b563f1SJosh Cartwright  * @max_apid:		maximum APID
114*57102ad7SAbhijeet Dharmapurikar  * @max_periph:		maximum number of PMIC peripherals supported by HW.
11567b563f1SJosh Cartwright  * @mapping_table:	in-memory copy of PPID -> APID mapping table.
11667b563f1SJosh Cartwright  * @domain:		irq domain object for PMIC IRQ domain
11767b563f1SJosh Cartwright  * @spmic:		SPMI controller object
118d0c6ae41SGilad Avidov  * @apid_to_ppid:	in-memory copy of APID -> PPID mapping table.
119d0c6ae41SGilad Avidov  * @ver_ops:		version dependent operations.
120d0c6ae41SGilad Avidov  * @ppid_to_chan	in-memory copy of PPID -> channel (APID) mapping table.
121d0c6ae41SGilad Avidov  *			v2 only.
12239ae93e3SKenneth Heitke  */
12339ae93e3SKenneth Heitke struct spmi_pmic_arb_dev {
124d0c6ae41SGilad Avidov 	void __iomem		*rd_base;
125d0c6ae41SGilad Avidov 	void __iomem		*wr_base;
12639ae93e3SKenneth Heitke 	void __iomem		*intr;
12739ae93e3SKenneth Heitke 	void __iomem		*cnfg;
128987a9f12SStephen Boyd 	void __iomem		*core;
129987a9f12SStephen Boyd 	resource_size_t		core_size;
13039ae93e3SKenneth Heitke 	raw_spinlock_t		lock;
13139ae93e3SKenneth Heitke 	u8			channel;
13267b563f1SJosh Cartwright 	int			irq;
13367b563f1SJosh Cartwright 	u8			ee;
134987a9f12SStephen Boyd 	u16			min_apid;
135987a9f12SStephen Boyd 	u16			max_apid;
136*57102ad7SAbhijeet Dharmapurikar 	u16			max_periph;
137987a9f12SStephen Boyd 	u32			*mapping_table;
138987a9f12SStephen Boyd 	DECLARE_BITMAP(mapping_table_valid, PMIC_ARB_MAX_PERIPHS);
13967b563f1SJosh Cartwright 	struct irq_domain	*domain;
14067b563f1SJosh Cartwright 	struct spmi_controller	*spmic;
141987a9f12SStephen Boyd 	u16			*apid_to_ppid;
142d0c6ae41SGilad Avidov 	const struct pmic_arb_ver_ops *ver_ops;
143987a9f12SStephen Boyd 	u16			*ppid_to_chan;
144987a9f12SStephen Boyd 	u16			last_channel;
145*57102ad7SAbhijeet Dharmapurikar 	u8			*chan_to_owner;
146d0c6ae41SGilad Avidov };
147d0c6ae41SGilad Avidov 
148d0c6ae41SGilad Avidov /**
149d0c6ae41SGilad Avidov  * pmic_arb_ver: version dependent functionality.
150d0c6ae41SGilad Avidov  *
151*57102ad7SAbhijeet Dharmapurikar  * @mode:	access rights to specified pmic peripheral.
152d0c6ae41SGilad Avidov  * @non_data_cmd:	on v1 issues an spmi non-data command.
153d0c6ae41SGilad Avidov  *			on v2 no HW support, returns -EOPNOTSUPP.
154d0c6ae41SGilad Avidov  * @offset:		on v1 offset of per-ee channel.
155d0c6ae41SGilad Avidov  *			on v2 offset of per-ee and per-ppid channel.
156d0c6ae41SGilad Avidov  * @fmt_cmd:		formats a GENI/SPMI command.
157d0c6ae41SGilad Avidov  * @owner_acc_status:	on v1 offset of PMIC_ARB_SPMI_PIC_OWNERm_ACC_STATUSn
158d0c6ae41SGilad Avidov  *			on v2 offset of SPMI_PIC_OWNERm_ACC_STATUSn.
159d0c6ae41SGilad Avidov  * @acc_enable:		on v1 offset of PMIC_ARB_SPMI_PIC_ACC_ENABLEn
160d0c6ae41SGilad Avidov  *			on v2 offset of SPMI_PIC_ACC_ENABLEn.
161d0c6ae41SGilad Avidov  * @irq_status:		on v1 offset of PMIC_ARB_SPMI_PIC_IRQ_STATUSn
162d0c6ae41SGilad Avidov  *			on v2 offset of SPMI_PIC_IRQ_STATUSn.
163d0c6ae41SGilad Avidov  * @irq_clear:		on v1 offset of PMIC_ARB_SPMI_PIC_IRQ_CLEARn
164d0c6ae41SGilad Avidov  *			on v2 offset of SPMI_PIC_IRQ_CLEARn.
165d0c6ae41SGilad Avidov  */
166d0c6ae41SGilad Avidov struct pmic_arb_ver_ops {
167*57102ad7SAbhijeet Dharmapurikar 	int (*mode)(struct spmi_pmic_arb_dev *dev, u8 sid, u16 addr,
168*57102ad7SAbhijeet Dharmapurikar 			mode_t *mode);
169d0c6ae41SGilad Avidov 	/* spmi commands (read_cmd, write_cmd, cmd) functionality */
170987a9f12SStephen Boyd 	int (*offset)(struct spmi_pmic_arb_dev *dev, u8 sid, u16 addr,
171987a9f12SStephen Boyd 		      u32 *offset);
172d0c6ae41SGilad Avidov 	u32 (*fmt_cmd)(u8 opc, u8 sid, u16 addr, u8 bc);
173d0c6ae41SGilad Avidov 	int (*non_data_cmd)(struct spmi_controller *ctrl, u8 opc, u8 sid);
174d0c6ae41SGilad Avidov 	/* Interrupts controller functionality (offset of PIC registers) */
175d0c6ae41SGilad Avidov 	u32 (*owner_acc_status)(u8 m, u8 n);
176d0c6ae41SGilad Avidov 	u32 (*acc_enable)(u8 n);
177d0c6ae41SGilad Avidov 	u32 (*irq_status)(u8 n);
178d0c6ae41SGilad Avidov 	u32 (*irq_clear)(u8 n);
17939ae93e3SKenneth Heitke };
18039ae93e3SKenneth Heitke 
18139ae93e3SKenneth Heitke static inline void pmic_arb_base_write(struct spmi_pmic_arb_dev *dev,
18239ae93e3SKenneth Heitke 				       u32 offset, u32 val)
18339ae93e3SKenneth Heitke {
184d0c6ae41SGilad Avidov 	writel_relaxed(val, dev->wr_base + offset);
185d0c6ae41SGilad Avidov }
186d0c6ae41SGilad Avidov 
187d0c6ae41SGilad Avidov static inline void pmic_arb_set_rd_cmd(struct spmi_pmic_arb_dev *dev,
188d0c6ae41SGilad Avidov 				       u32 offset, u32 val)
189d0c6ae41SGilad Avidov {
190d0c6ae41SGilad Avidov 	writel_relaxed(val, dev->rd_base + offset);
19139ae93e3SKenneth Heitke }
19239ae93e3SKenneth Heitke 
19339ae93e3SKenneth Heitke /**
19439ae93e3SKenneth Heitke  * pa_read_data: reads pmic-arb's register and copy 1..4 bytes to buf
19539ae93e3SKenneth Heitke  * @bc:		byte count -1. range: 0..3
19639ae93e3SKenneth Heitke  * @reg:	register's address
19739ae93e3SKenneth Heitke  * @buf:	output parameter, length must be bc + 1
19839ae93e3SKenneth Heitke  */
19939ae93e3SKenneth Heitke static void pa_read_data(struct spmi_pmic_arb_dev *dev, u8 *buf, u32 reg, u8 bc)
20039ae93e3SKenneth Heitke {
201d5144796SStephen Boyd 	u32 data = __raw_readl(dev->rd_base + reg);
20239ae93e3SKenneth Heitke 	memcpy(buf, &data, (bc & 3) + 1);
20339ae93e3SKenneth Heitke }
20439ae93e3SKenneth Heitke 
20539ae93e3SKenneth Heitke /**
20639ae93e3SKenneth Heitke  * pa_write_data: write 1..4 bytes from buf to pmic-arb's register
20739ae93e3SKenneth Heitke  * @bc:		byte-count -1. range: 0..3.
20839ae93e3SKenneth Heitke  * @reg:	register's address.
20939ae93e3SKenneth Heitke  * @buf:	buffer to write. length must be bc + 1.
21039ae93e3SKenneth Heitke  */
21139ae93e3SKenneth Heitke static void
21239ae93e3SKenneth Heitke pa_write_data(struct spmi_pmic_arb_dev *dev, const u8 *buf, u32 reg, u8 bc)
21339ae93e3SKenneth Heitke {
21439ae93e3SKenneth Heitke 	u32 data = 0;
21539ae93e3SKenneth Heitke 	memcpy(&data, buf, (bc & 3) + 1);
216d5144796SStephen Boyd 	__raw_writel(data, dev->wr_base + reg);
21739ae93e3SKenneth Heitke }
21839ae93e3SKenneth Heitke 
219d0c6ae41SGilad Avidov static int pmic_arb_wait_for_done(struct spmi_controller *ctrl,
220d0c6ae41SGilad Avidov 				  void __iomem *base, u8 sid, u16 addr)
22139ae93e3SKenneth Heitke {
22239ae93e3SKenneth Heitke 	struct spmi_pmic_arb_dev *dev = spmi_controller_get_drvdata(ctrl);
22339ae93e3SKenneth Heitke 	u32 status = 0;
22439ae93e3SKenneth Heitke 	u32 timeout = PMIC_ARB_TIMEOUT_US;
225987a9f12SStephen Boyd 	u32 offset;
226987a9f12SStephen Boyd 	int rc;
227987a9f12SStephen Boyd 
228987a9f12SStephen Boyd 	rc = dev->ver_ops->offset(dev, sid, addr, &offset);
229987a9f12SStephen Boyd 	if (rc)
230987a9f12SStephen Boyd 		return rc;
231987a9f12SStephen Boyd 
232987a9f12SStephen Boyd 	offset += PMIC_ARB_STATUS;
23339ae93e3SKenneth Heitke 
23439ae93e3SKenneth Heitke 	while (timeout--) {
235d0c6ae41SGilad Avidov 		status = readl_relaxed(base + offset);
23639ae93e3SKenneth Heitke 
23739ae93e3SKenneth Heitke 		if (status & PMIC_ARB_STATUS_DONE) {
23839ae93e3SKenneth Heitke 			if (status & PMIC_ARB_STATUS_DENIED) {
23939ae93e3SKenneth Heitke 				dev_err(&ctrl->dev,
24039ae93e3SKenneth Heitke 					"%s: transaction denied (0x%x)\n",
24139ae93e3SKenneth Heitke 					__func__, status);
24239ae93e3SKenneth Heitke 				return -EPERM;
24339ae93e3SKenneth Heitke 			}
24439ae93e3SKenneth Heitke 
24539ae93e3SKenneth Heitke 			if (status & PMIC_ARB_STATUS_FAILURE) {
24639ae93e3SKenneth Heitke 				dev_err(&ctrl->dev,
24739ae93e3SKenneth Heitke 					"%s: transaction failed (0x%x)\n",
24839ae93e3SKenneth Heitke 					__func__, status);
24939ae93e3SKenneth Heitke 				return -EIO;
25039ae93e3SKenneth Heitke 			}
25139ae93e3SKenneth Heitke 
25239ae93e3SKenneth Heitke 			if (status & PMIC_ARB_STATUS_DROPPED) {
25339ae93e3SKenneth Heitke 				dev_err(&ctrl->dev,
25439ae93e3SKenneth Heitke 					"%s: transaction dropped (0x%x)\n",
25539ae93e3SKenneth Heitke 					__func__, status);
25639ae93e3SKenneth Heitke 				return -EIO;
25739ae93e3SKenneth Heitke 			}
25839ae93e3SKenneth Heitke 
25939ae93e3SKenneth Heitke 			return 0;
26039ae93e3SKenneth Heitke 		}
26139ae93e3SKenneth Heitke 		udelay(1);
26239ae93e3SKenneth Heitke 	}
26339ae93e3SKenneth Heitke 
26439ae93e3SKenneth Heitke 	dev_err(&ctrl->dev,
26539ae93e3SKenneth Heitke 		"%s: timeout, status 0x%x\n",
26639ae93e3SKenneth Heitke 		__func__, status);
26739ae93e3SKenneth Heitke 	return -ETIMEDOUT;
26839ae93e3SKenneth Heitke }
26939ae93e3SKenneth Heitke 
270d0c6ae41SGilad Avidov static int
271d0c6ae41SGilad Avidov pmic_arb_non_data_cmd_v1(struct spmi_controller *ctrl, u8 opc, u8 sid)
27239ae93e3SKenneth Heitke {
27339ae93e3SKenneth Heitke 	struct spmi_pmic_arb_dev *pmic_arb = spmi_controller_get_drvdata(ctrl);
27439ae93e3SKenneth Heitke 	unsigned long flags;
27539ae93e3SKenneth Heitke 	u32 cmd;
27639ae93e3SKenneth Heitke 	int rc;
277987a9f12SStephen Boyd 	u32 offset;
278987a9f12SStephen Boyd 
279987a9f12SStephen Boyd 	rc = pmic_arb->ver_ops->offset(pmic_arb, sid, 0, &offset);
280987a9f12SStephen Boyd 	if (rc)
281987a9f12SStephen Boyd 		return rc;
282d0c6ae41SGilad Avidov 
283d0c6ae41SGilad Avidov 	cmd = ((opc | 0x40) << 27) | ((sid & 0xf) << 20);
284d0c6ae41SGilad Avidov 
285d0c6ae41SGilad Avidov 	raw_spin_lock_irqsave(&pmic_arb->lock, flags);
286d0c6ae41SGilad Avidov 	pmic_arb_base_write(pmic_arb, offset + PMIC_ARB_CMD, cmd);
287d0c6ae41SGilad Avidov 	rc = pmic_arb_wait_for_done(ctrl, pmic_arb->wr_base, sid, 0);
288d0c6ae41SGilad Avidov 	raw_spin_unlock_irqrestore(&pmic_arb->lock, flags);
289d0c6ae41SGilad Avidov 
290d0c6ae41SGilad Avidov 	return rc;
291d0c6ae41SGilad Avidov }
292d0c6ae41SGilad Avidov 
293d0c6ae41SGilad Avidov static int
294d0c6ae41SGilad Avidov pmic_arb_non_data_cmd_v2(struct spmi_controller *ctrl, u8 opc, u8 sid)
295d0c6ae41SGilad Avidov {
296d0c6ae41SGilad Avidov 	return -EOPNOTSUPP;
297d0c6ae41SGilad Avidov }
298d0c6ae41SGilad Avidov 
299d0c6ae41SGilad Avidov /* Non-data command */
300d0c6ae41SGilad Avidov static int pmic_arb_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid)
301d0c6ae41SGilad Avidov {
302d0c6ae41SGilad Avidov 	struct spmi_pmic_arb_dev *pmic_arb = spmi_controller_get_drvdata(ctrl);
303d0c6ae41SGilad Avidov 
304d0c6ae41SGilad Avidov 	dev_dbg(&ctrl->dev, "cmd op:0x%x sid:%d\n", opc, sid);
30539ae93e3SKenneth Heitke 
30639ae93e3SKenneth Heitke 	/* Check for valid non-data command */
30739ae93e3SKenneth Heitke 	if (opc < SPMI_CMD_RESET || opc > SPMI_CMD_WAKEUP)
30839ae93e3SKenneth Heitke 		return -EINVAL;
30939ae93e3SKenneth Heitke 
310d0c6ae41SGilad Avidov 	return pmic_arb->ver_ops->non_data_cmd(ctrl, opc, sid);
31139ae93e3SKenneth Heitke }
31239ae93e3SKenneth Heitke 
31339ae93e3SKenneth Heitke static int pmic_arb_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
31439ae93e3SKenneth Heitke 			     u16 addr, u8 *buf, size_t len)
31539ae93e3SKenneth Heitke {
31639ae93e3SKenneth Heitke 	struct spmi_pmic_arb_dev *pmic_arb = spmi_controller_get_drvdata(ctrl);
31739ae93e3SKenneth Heitke 	unsigned long flags;
31839ae93e3SKenneth Heitke 	u8 bc = len - 1;
31939ae93e3SKenneth Heitke 	u32 cmd;
32039ae93e3SKenneth Heitke 	int rc;
321987a9f12SStephen Boyd 	u32 offset;
322*57102ad7SAbhijeet Dharmapurikar 	mode_t mode;
323987a9f12SStephen Boyd 
324987a9f12SStephen Boyd 	rc = pmic_arb->ver_ops->offset(pmic_arb, sid, addr, &offset);
325987a9f12SStephen Boyd 	if (rc)
326987a9f12SStephen Boyd 		return rc;
32739ae93e3SKenneth Heitke 
328*57102ad7SAbhijeet Dharmapurikar 	rc = pmic_arb->ver_ops->mode(pmic_arb, sid, addr, &mode);
329*57102ad7SAbhijeet Dharmapurikar 	if (rc)
330*57102ad7SAbhijeet Dharmapurikar 		return rc;
331*57102ad7SAbhijeet Dharmapurikar 
332*57102ad7SAbhijeet Dharmapurikar 	if (!(mode & S_IRUSR)) {
333*57102ad7SAbhijeet Dharmapurikar 		dev_err(&pmic_arb->spmic->dev,
334*57102ad7SAbhijeet Dharmapurikar 			"error: impermissible read from peripheral sid:%d addr:0x%x\n",
335*57102ad7SAbhijeet Dharmapurikar 			sid, addr);
336*57102ad7SAbhijeet Dharmapurikar 		return -EPERM;
337*57102ad7SAbhijeet Dharmapurikar 	}
338*57102ad7SAbhijeet Dharmapurikar 
33939ae93e3SKenneth Heitke 	if (bc >= PMIC_ARB_MAX_TRANS_BYTES) {
34039ae93e3SKenneth Heitke 		dev_err(&ctrl->dev,
341d0c6ae41SGilad Avidov 			"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 
356d0c6ae41SGilad Avidov 	cmd = pmic_arb->ver_ops->fmt_cmd(opc, sid, addr, bc);
35739ae93e3SKenneth Heitke 
35839ae93e3SKenneth Heitke 	raw_spin_lock_irqsave(&pmic_arb->lock, flags);
359d0c6ae41SGilad Avidov 	pmic_arb_set_rd_cmd(pmic_arb, offset + PMIC_ARB_CMD, cmd);
360d0c6ae41SGilad Avidov 	rc = pmic_arb_wait_for_done(ctrl, pmic_arb->rd_base, sid, addr);
36139ae93e3SKenneth Heitke 	if (rc)
36239ae93e3SKenneth Heitke 		goto done;
36339ae93e3SKenneth Heitke 
364d0c6ae41SGilad Avidov 	pa_read_data(pmic_arb, buf, offset + PMIC_ARB_RDATA0,
36539ae93e3SKenneth Heitke 		     min_t(u8, bc, 3));
36639ae93e3SKenneth Heitke 
36739ae93e3SKenneth Heitke 	if (bc > 3)
36839ae93e3SKenneth Heitke 		pa_read_data(pmic_arb, buf + 4,
369d0c6ae41SGilad Avidov 				offset + PMIC_ARB_RDATA1, bc - 4);
37039ae93e3SKenneth Heitke 
37139ae93e3SKenneth Heitke done:
37239ae93e3SKenneth Heitke 	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 {
37939ae93e3SKenneth Heitke 	struct spmi_pmic_arb_dev *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;
385*57102ad7SAbhijeet Dharmapurikar 	mode_t mode;
386987a9f12SStephen Boyd 
387987a9f12SStephen Boyd 	rc = pmic_arb->ver_ops->offset(pmic_arb, sid, addr, &offset);
388987a9f12SStephen Boyd 	if (rc)
389987a9f12SStephen Boyd 		return rc;
39039ae93e3SKenneth Heitke 
391*57102ad7SAbhijeet Dharmapurikar 	rc = pmic_arb->ver_ops->mode(pmic_arb, sid, addr, &mode);
392*57102ad7SAbhijeet Dharmapurikar 	if (rc)
393*57102ad7SAbhijeet Dharmapurikar 		return rc;
394*57102ad7SAbhijeet Dharmapurikar 
395*57102ad7SAbhijeet Dharmapurikar 	if (!(mode & S_IWUSR)) {
396*57102ad7SAbhijeet Dharmapurikar 		dev_err(&pmic_arb->spmic->dev,
397*57102ad7SAbhijeet Dharmapurikar 			"error: impermissible write to peripheral sid:%d addr:0x%x\n",
398*57102ad7SAbhijeet Dharmapurikar 			sid, addr);
399*57102ad7SAbhijeet Dharmapurikar 		return -EPERM;
400*57102ad7SAbhijeet Dharmapurikar 	}
401*57102ad7SAbhijeet Dharmapurikar 
40239ae93e3SKenneth Heitke 	if (bc >= PMIC_ARB_MAX_TRANS_BYTES) {
40339ae93e3SKenneth Heitke 		dev_err(&ctrl->dev,
404d0c6ae41SGilad Avidov 			"pmic-arb supports 1..%d bytes per trans, but:%zu requested",
40539ae93e3SKenneth Heitke 			PMIC_ARB_MAX_TRANS_BYTES, len);
40639ae93e3SKenneth Heitke 		return  -EINVAL;
40739ae93e3SKenneth Heitke 	}
40839ae93e3SKenneth Heitke 
40939ae93e3SKenneth Heitke 	/* Check the opcode */
41039ae93e3SKenneth Heitke 	if (opc >= 0x40 && opc <= 0x5F)
41139ae93e3SKenneth Heitke 		opc = PMIC_ARB_OP_WRITE;
41239ae93e3SKenneth Heitke 	else if (opc >= 0x00 && opc <= 0x0F)
41339ae93e3SKenneth Heitke 		opc = PMIC_ARB_OP_EXT_WRITE;
41439ae93e3SKenneth Heitke 	else if (opc >= 0x30 && opc <= 0x37)
41539ae93e3SKenneth Heitke 		opc = PMIC_ARB_OP_EXT_WRITEL;
4169b76968dSStephen Boyd 	else if (opc >= 0x80)
41739ae93e3SKenneth Heitke 		opc = PMIC_ARB_OP_ZERO_WRITE;
41839ae93e3SKenneth Heitke 	else
41939ae93e3SKenneth Heitke 		return -EINVAL;
42039ae93e3SKenneth Heitke 
421d0c6ae41SGilad Avidov 	cmd = pmic_arb->ver_ops->fmt_cmd(opc, sid, addr, bc);
42239ae93e3SKenneth Heitke 
42339ae93e3SKenneth Heitke 	/* Write data to FIFOs */
42439ae93e3SKenneth Heitke 	raw_spin_lock_irqsave(&pmic_arb->lock, flags);
425d0c6ae41SGilad Avidov 	pa_write_data(pmic_arb, buf, offset + PMIC_ARB_WDATA0,
426d0c6ae41SGilad Avidov 		      min_t(u8, bc, 3));
42739ae93e3SKenneth Heitke 	if (bc > 3)
42839ae93e3SKenneth Heitke 		pa_write_data(pmic_arb, buf + 4,
429d0c6ae41SGilad Avidov 				offset + PMIC_ARB_WDATA1, bc - 4);
43039ae93e3SKenneth Heitke 
43139ae93e3SKenneth Heitke 	/* Start the transaction */
432d0c6ae41SGilad Avidov 	pmic_arb_base_write(pmic_arb, offset + PMIC_ARB_CMD, cmd);
433d0c6ae41SGilad Avidov 	rc = pmic_arb_wait_for_done(ctrl, pmic_arb->wr_base, sid, addr);
43439ae93e3SKenneth Heitke 	raw_spin_unlock_irqrestore(&pmic_arb->lock, flags);
43539ae93e3SKenneth Heitke 
43639ae93e3SKenneth Heitke 	return rc;
43739ae93e3SKenneth Heitke }
43839ae93e3SKenneth Heitke 
43967b563f1SJosh Cartwright enum qpnpint_regs {
44067b563f1SJosh Cartwright 	QPNPINT_REG_RT_STS		= 0x10,
44167b563f1SJosh Cartwright 	QPNPINT_REG_SET_TYPE		= 0x11,
44267b563f1SJosh Cartwright 	QPNPINT_REG_POLARITY_HIGH	= 0x12,
44367b563f1SJosh Cartwright 	QPNPINT_REG_POLARITY_LOW	= 0x13,
44467b563f1SJosh Cartwright 	QPNPINT_REG_LATCHED_CLR		= 0x14,
44567b563f1SJosh Cartwright 	QPNPINT_REG_EN_SET		= 0x15,
44667b563f1SJosh Cartwright 	QPNPINT_REG_EN_CLR		= 0x16,
44767b563f1SJosh Cartwright 	QPNPINT_REG_LATCHED_STS		= 0x18,
44867b563f1SJosh Cartwright };
44967b563f1SJosh Cartwright 
45067b563f1SJosh Cartwright struct spmi_pmic_arb_qpnpint_type {
45167b563f1SJosh Cartwright 	u8 type; /* 1 -> edge */
45267b563f1SJosh Cartwright 	u8 polarity_high;
45367b563f1SJosh Cartwright 	u8 polarity_low;
45467b563f1SJosh Cartwright } __packed;
45567b563f1SJosh Cartwright 
45667b563f1SJosh Cartwright /* Simplified accessor functions for irqchip callbacks */
45767b563f1SJosh Cartwright static void qpnpint_spmi_write(struct irq_data *d, u8 reg, void *buf,
45867b563f1SJosh Cartwright 			       size_t len)
45967b563f1SJosh Cartwright {
46067b563f1SJosh Cartwright 	struct spmi_pmic_arb_dev *pa = irq_data_get_irq_chip_data(d);
46167b563f1SJosh Cartwright 	u8 sid = d->hwirq >> 24;
46267b563f1SJosh Cartwright 	u8 per = d->hwirq >> 16;
46367b563f1SJosh Cartwright 
46467b563f1SJosh Cartwright 	if (pmic_arb_write_cmd(pa->spmic, SPMI_CMD_EXT_WRITEL, sid,
46567b563f1SJosh Cartwright 			       (per << 8) + reg, buf, len))
46667b563f1SJosh Cartwright 		dev_err_ratelimited(&pa->spmic->dev,
46767b563f1SJosh Cartwright 				"failed irqchip transaction on %x\n",
46867b563f1SJosh Cartwright 				    d->irq);
46967b563f1SJosh Cartwright }
47067b563f1SJosh Cartwright 
47167b563f1SJosh Cartwright static void qpnpint_spmi_read(struct irq_data *d, u8 reg, void *buf, size_t len)
47267b563f1SJosh Cartwright {
47367b563f1SJosh Cartwright 	struct spmi_pmic_arb_dev *pa = irq_data_get_irq_chip_data(d);
47467b563f1SJosh Cartwright 	u8 sid = d->hwirq >> 24;
47567b563f1SJosh Cartwright 	u8 per = d->hwirq >> 16;
47667b563f1SJosh Cartwright 
47767b563f1SJosh Cartwright 	if (pmic_arb_read_cmd(pa->spmic, SPMI_CMD_EXT_READL, sid,
47867b563f1SJosh Cartwright 			      (per << 8) + reg, buf, len))
47967b563f1SJosh Cartwright 		dev_err_ratelimited(&pa->spmic->dev,
48067b563f1SJosh Cartwright 				"failed irqchip transaction on %x\n",
48167b563f1SJosh Cartwright 				    d->irq);
48267b563f1SJosh Cartwright }
48367b563f1SJosh Cartwright 
48467b563f1SJosh Cartwright static void periph_interrupt(struct spmi_pmic_arb_dev *pa, u8 apid)
48567b563f1SJosh Cartwright {
48667b563f1SJosh Cartwright 	unsigned int irq;
48767b563f1SJosh Cartwright 	u32 status;
48867b563f1SJosh Cartwright 	int id;
48967b563f1SJosh Cartwright 
490d0c6ae41SGilad Avidov 	status = readl_relaxed(pa->intr + pa->ver_ops->irq_status(apid));
49167b563f1SJosh Cartwright 	while (status) {
49267b563f1SJosh Cartwright 		id = ffs(status) - 1;
49367b563f1SJosh Cartwright 		status &= ~(1 << id);
49467b563f1SJosh Cartwright 		irq = irq_find_mapping(pa->domain,
49567b563f1SJosh Cartwright 				       pa->apid_to_ppid[apid] << 16
49667b563f1SJosh Cartwright 				     | id << 8
49767b563f1SJosh Cartwright 				     | apid);
49867b563f1SJosh Cartwright 		generic_handle_irq(irq);
49967b563f1SJosh Cartwright 	}
50067b563f1SJosh Cartwright }
50167b563f1SJosh Cartwright 
502bd0b9ac4SThomas Gleixner static void pmic_arb_chained_irq(struct irq_desc *desc)
50367b563f1SJosh Cartwright {
5047fe88f3cSJiang Liu 	struct spmi_pmic_arb_dev *pa = irq_desc_get_handler_data(desc);
5057fe88f3cSJiang Liu 	struct irq_chip *chip = irq_desc_get_chip(desc);
50667b563f1SJosh Cartwright 	void __iomem *intr = pa->intr;
50767b563f1SJosh Cartwright 	int first = pa->min_apid >> 5;
50867b563f1SJosh Cartwright 	int last = pa->max_apid >> 5;
50967b563f1SJosh Cartwright 	u32 status;
51067b563f1SJosh Cartwright 	int i, id;
51167b563f1SJosh Cartwright 
51267b563f1SJosh Cartwright 	chained_irq_enter(chip, desc);
51367b563f1SJosh Cartwright 
51467b563f1SJosh Cartwright 	for (i = first; i <= last; ++i) {
51567b563f1SJosh Cartwright 		status = readl_relaxed(intr +
516d0c6ae41SGilad Avidov 				      pa->ver_ops->owner_acc_status(pa->ee, i));
51767b563f1SJosh Cartwright 		while (status) {
51867b563f1SJosh Cartwright 			id = ffs(status) - 1;
51967b563f1SJosh Cartwright 			status &= ~(1 << id);
52067b563f1SJosh Cartwright 			periph_interrupt(pa, id + i * 32);
52167b563f1SJosh Cartwright 		}
52267b563f1SJosh Cartwright 	}
52367b563f1SJosh Cartwright 
52467b563f1SJosh Cartwright 	chained_irq_exit(chip, desc);
52567b563f1SJosh Cartwright }
52667b563f1SJosh Cartwright 
52767b563f1SJosh Cartwright static void qpnpint_irq_ack(struct irq_data *d)
52867b563f1SJosh Cartwright {
52967b563f1SJosh Cartwright 	struct spmi_pmic_arb_dev *pa = irq_data_get_irq_chip_data(d);
53067b563f1SJosh Cartwright 	u8 irq  = d->hwirq >> 8;
53167b563f1SJosh Cartwright 	u8 apid = d->hwirq;
53267b563f1SJosh Cartwright 	unsigned long flags;
53367b563f1SJosh Cartwright 	u8 data;
53467b563f1SJosh Cartwright 
53567b563f1SJosh Cartwright 	raw_spin_lock_irqsave(&pa->lock, flags);
536d0c6ae41SGilad Avidov 	writel_relaxed(1 << irq, pa->intr + pa->ver_ops->irq_clear(apid));
53767b563f1SJosh Cartwright 	raw_spin_unlock_irqrestore(&pa->lock, flags);
53867b563f1SJosh Cartwright 
53967b563f1SJosh Cartwright 	data = 1 << irq;
54067b563f1SJosh Cartwright 	qpnpint_spmi_write(d, QPNPINT_REG_LATCHED_CLR, &data, 1);
54167b563f1SJosh Cartwright }
54267b563f1SJosh Cartwright 
54367b563f1SJosh Cartwright static void qpnpint_irq_mask(struct irq_data *d)
54467b563f1SJosh Cartwright {
54567b563f1SJosh Cartwright 	struct spmi_pmic_arb_dev *pa = irq_data_get_irq_chip_data(d);
54667b563f1SJosh Cartwright 	u8 irq  = d->hwirq >> 8;
54767b563f1SJosh Cartwright 	u8 apid = d->hwirq;
54867b563f1SJosh Cartwright 	unsigned long flags;
54967b563f1SJosh Cartwright 	u32 status;
55067b563f1SJosh Cartwright 	u8 data;
55167b563f1SJosh Cartwright 
55267b563f1SJosh Cartwright 	raw_spin_lock_irqsave(&pa->lock, flags);
553d0c6ae41SGilad Avidov 	status = readl_relaxed(pa->intr + pa->ver_ops->acc_enable(apid));
55467b563f1SJosh Cartwright 	if (status & SPMI_PIC_ACC_ENABLE_BIT) {
55567b563f1SJosh Cartwright 		status = status & ~SPMI_PIC_ACC_ENABLE_BIT;
556d0c6ae41SGilad Avidov 		writel_relaxed(status, pa->intr +
557d0c6ae41SGilad Avidov 			       pa->ver_ops->acc_enable(apid));
55867b563f1SJosh Cartwright 	}
55967b563f1SJosh Cartwright 	raw_spin_unlock_irqrestore(&pa->lock, flags);
56067b563f1SJosh Cartwright 
56167b563f1SJosh Cartwright 	data = 1 << irq;
56267b563f1SJosh Cartwright 	qpnpint_spmi_write(d, QPNPINT_REG_EN_CLR, &data, 1);
56367b563f1SJosh Cartwright }
56467b563f1SJosh Cartwright 
56567b563f1SJosh Cartwright static void qpnpint_irq_unmask(struct irq_data *d)
56667b563f1SJosh Cartwright {
56767b563f1SJosh Cartwright 	struct spmi_pmic_arb_dev *pa = irq_data_get_irq_chip_data(d);
56867b563f1SJosh Cartwright 	u8 irq  = d->hwirq >> 8;
56967b563f1SJosh Cartwright 	u8 apid = d->hwirq;
57067b563f1SJosh Cartwright 	unsigned long flags;
57167b563f1SJosh Cartwright 	u32 status;
57267b563f1SJosh Cartwright 	u8 data;
57367b563f1SJosh Cartwright 
57467b563f1SJosh Cartwright 	raw_spin_lock_irqsave(&pa->lock, flags);
575d0c6ae41SGilad Avidov 	status = readl_relaxed(pa->intr + pa->ver_ops->acc_enable(apid));
57667b563f1SJosh Cartwright 	if (!(status & SPMI_PIC_ACC_ENABLE_BIT)) {
57767b563f1SJosh Cartwright 		writel_relaxed(status | SPMI_PIC_ACC_ENABLE_BIT,
578d0c6ae41SGilad Avidov 				pa->intr + pa->ver_ops->acc_enable(apid));
57967b563f1SJosh Cartwright 	}
58067b563f1SJosh Cartwright 	raw_spin_unlock_irqrestore(&pa->lock, flags);
58167b563f1SJosh Cartwright 
58267b563f1SJosh Cartwright 	data = 1 << irq;
58367b563f1SJosh Cartwright 	qpnpint_spmi_write(d, QPNPINT_REG_EN_SET, &data, 1);
58467b563f1SJosh Cartwright }
58567b563f1SJosh Cartwright 
58667b563f1SJosh Cartwright static void qpnpint_irq_enable(struct irq_data *d)
58767b563f1SJosh Cartwright {
58867b563f1SJosh Cartwright 	u8 irq  = d->hwirq >> 8;
58967b563f1SJosh Cartwright 	u8 data;
59067b563f1SJosh Cartwright 
59167b563f1SJosh Cartwright 	qpnpint_irq_unmask(d);
59267b563f1SJosh Cartwright 
59367b563f1SJosh Cartwright 	data = 1 << irq;
59467b563f1SJosh Cartwright 	qpnpint_spmi_write(d, QPNPINT_REG_LATCHED_CLR, &data, 1);
59567b563f1SJosh Cartwright }
59667b563f1SJosh Cartwright 
59767b563f1SJosh Cartwright static int qpnpint_irq_set_type(struct irq_data *d, unsigned int flow_type)
59867b563f1SJosh Cartwright {
59967b563f1SJosh Cartwright 	struct spmi_pmic_arb_qpnpint_type type;
60067b563f1SJosh Cartwright 	u8 irq = d->hwirq >> 8;
60167b563f1SJosh Cartwright 
60267b563f1SJosh Cartwright 	qpnpint_spmi_read(d, QPNPINT_REG_SET_TYPE, &type, sizeof(type));
60367b563f1SJosh Cartwright 
60467b563f1SJosh Cartwright 	if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) {
60567b563f1SJosh Cartwright 		type.type |= 1 << irq;
60667b563f1SJosh Cartwright 		if (flow_type & IRQF_TRIGGER_RISING)
60767b563f1SJosh Cartwright 			type.polarity_high |= 1 << irq;
60867b563f1SJosh Cartwright 		if (flow_type & IRQF_TRIGGER_FALLING)
60967b563f1SJosh Cartwright 			type.polarity_low  |= 1 << irq;
61067b563f1SJosh Cartwright 	} else {
61167b563f1SJosh Cartwright 		if ((flow_type & (IRQF_TRIGGER_HIGH)) &&
61267b563f1SJosh Cartwright 		    (flow_type & (IRQF_TRIGGER_LOW)))
61367b563f1SJosh Cartwright 			return -EINVAL;
61467b563f1SJosh Cartwright 
61567b563f1SJosh Cartwright 		type.type &= ~(1 << irq); /* level trig */
61667b563f1SJosh Cartwright 		if (flow_type & IRQF_TRIGGER_HIGH)
61767b563f1SJosh Cartwright 			type.polarity_high |= 1 << irq;
61867b563f1SJosh Cartwright 		else
61967b563f1SJosh Cartwright 			type.polarity_low  |= 1 << irq;
62067b563f1SJosh Cartwright 	}
62167b563f1SJosh Cartwright 
62267b563f1SJosh Cartwright 	qpnpint_spmi_write(d, QPNPINT_REG_SET_TYPE, &type, sizeof(type));
62367b563f1SJosh Cartwright 	return 0;
62467b563f1SJosh Cartwright }
62567b563f1SJosh Cartwright 
62660be4230SCourtney Cavin static int qpnpint_get_irqchip_state(struct irq_data *d,
62760be4230SCourtney Cavin 				     enum irqchip_irq_state which,
62860be4230SCourtney Cavin 				     bool *state)
62960be4230SCourtney Cavin {
63060be4230SCourtney Cavin 	u8 irq = d->hwirq >> 8;
63160be4230SCourtney Cavin 	u8 status = 0;
63260be4230SCourtney Cavin 
63360be4230SCourtney Cavin 	if (which != IRQCHIP_STATE_LINE_LEVEL)
63460be4230SCourtney Cavin 		return -EINVAL;
63560be4230SCourtney Cavin 
63660be4230SCourtney Cavin 	qpnpint_spmi_read(d, QPNPINT_REG_RT_STS, &status, 1);
63760be4230SCourtney Cavin 	*state = !!(status & BIT(irq));
63860be4230SCourtney Cavin 
63960be4230SCourtney Cavin 	return 0;
64060be4230SCourtney Cavin }
64160be4230SCourtney Cavin 
64267b563f1SJosh Cartwright static struct irq_chip pmic_arb_irqchip = {
64367b563f1SJosh Cartwright 	.name		= "pmic_arb",
64467b563f1SJosh Cartwright 	.irq_enable	= qpnpint_irq_enable,
64567b563f1SJosh Cartwright 	.irq_ack	= qpnpint_irq_ack,
64667b563f1SJosh Cartwright 	.irq_mask	= qpnpint_irq_mask,
64767b563f1SJosh Cartwright 	.irq_unmask	= qpnpint_irq_unmask,
64867b563f1SJosh Cartwright 	.irq_set_type	= qpnpint_irq_set_type,
64960be4230SCourtney Cavin 	.irq_get_irqchip_state	= qpnpint_get_irqchip_state,
65067b563f1SJosh Cartwright 	.flags		= IRQCHIP_MASK_ON_SUSPEND
65167b563f1SJosh Cartwright 			| IRQCHIP_SKIP_SET_WAKE,
65267b563f1SJosh Cartwright };
65367b563f1SJosh Cartwright 
65467b563f1SJosh Cartwright struct spmi_pmic_arb_irq_spec {
65567b563f1SJosh Cartwright 	unsigned slave:4;
65667b563f1SJosh Cartwright 	unsigned per:8;
65767b563f1SJosh Cartwright 	unsigned irq:3;
65867b563f1SJosh Cartwright };
65967b563f1SJosh Cartwright 
66067b563f1SJosh Cartwright static int search_mapping_table(struct spmi_pmic_arb_dev *pa,
66167b563f1SJosh Cartwright 				struct spmi_pmic_arb_irq_spec *spec,
66267b563f1SJosh Cartwright 				u8 *apid)
66367b563f1SJosh Cartwright {
66467b563f1SJosh Cartwright 	u16 ppid = spec->slave << 8 | spec->per;
66567b563f1SJosh Cartwright 	u32 *mapping_table = pa->mapping_table;
66667b563f1SJosh Cartwright 	int index = 0, i;
66767b563f1SJosh Cartwright 	u32 data;
66867b563f1SJosh Cartwright 
66967b563f1SJosh Cartwright 	for (i = 0; i < SPMI_MAPPING_TABLE_TREE_DEPTH; ++i) {
670987a9f12SStephen Boyd 		if (!test_and_set_bit(index, pa->mapping_table_valid))
671987a9f12SStephen Boyd 			mapping_table[index] = readl_relaxed(pa->cnfg +
672987a9f12SStephen Boyd 						SPMI_MAPPING_TABLE_REG(index));
673987a9f12SStephen Boyd 
67467b563f1SJosh Cartwright 		data = mapping_table[index];
67567b563f1SJosh Cartwright 
67667b563f1SJosh Cartwright 		if (ppid & (1 << SPMI_MAPPING_BIT_INDEX(data))) {
67767b563f1SJosh Cartwright 			if (SPMI_MAPPING_BIT_IS_1_FLAG(data)) {
67867b563f1SJosh Cartwright 				index = SPMI_MAPPING_BIT_IS_1_RESULT(data);
67967b563f1SJosh Cartwright 			} else {
68067b563f1SJosh Cartwright 				*apid = SPMI_MAPPING_BIT_IS_1_RESULT(data);
68167b563f1SJosh Cartwright 				return 0;
68267b563f1SJosh Cartwright 			}
68367b563f1SJosh Cartwright 		} else {
68467b563f1SJosh Cartwright 			if (SPMI_MAPPING_BIT_IS_0_FLAG(data)) {
68567b563f1SJosh Cartwright 				index = SPMI_MAPPING_BIT_IS_0_RESULT(data);
68667b563f1SJosh Cartwright 			} else {
68767b563f1SJosh Cartwright 				*apid = SPMI_MAPPING_BIT_IS_0_RESULT(data);
68867b563f1SJosh Cartwright 				return 0;
68967b563f1SJosh Cartwright 			}
69067b563f1SJosh Cartwright 		}
69167b563f1SJosh Cartwright 	}
69267b563f1SJosh Cartwright 
69367b563f1SJosh Cartwright 	return -ENODEV;
69467b563f1SJosh Cartwright }
69567b563f1SJosh Cartwright 
69667b563f1SJosh Cartwright static int qpnpint_irq_domain_dt_translate(struct irq_domain *d,
69767b563f1SJosh Cartwright 					   struct device_node *controller,
69867b563f1SJosh Cartwright 					   const u32 *intspec,
69967b563f1SJosh Cartwright 					   unsigned int intsize,
70067b563f1SJosh Cartwright 					   unsigned long *out_hwirq,
70167b563f1SJosh Cartwright 					   unsigned int *out_type)
70267b563f1SJosh Cartwright {
70367b563f1SJosh Cartwright 	struct spmi_pmic_arb_dev *pa = d->host_data;
70467b563f1SJosh Cartwright 	struct spmi_pmic_arb_irq_spec spec;
70567b563f1SJosh Cartwright 	int err;
70667b563f1SJosh Cartwright 	u8 apid;
70767b563f1SJosh Cartwright 
70867b563f1SJosh Cartwright 	dev_dbg(&pa->spmic->dev,
70967b563f1SJosh Cartwright 		"intspec[0] 0x%1x intspec[1] 0x%02x intspec[2] 0x%02x\n",
71067b563f1SJosh Cartwright 		intspec[0], intspec[1], intspec[2]);
71167b563f1SJosh Cartwright 
7125d4c9bc7SMarc Zyngier 	if (irq_domain_get_of_node(d) != controller)
71367b563f1SJosh Cartwright 		return -EINVAL;
71467b563f1SJosh Cartwright 	if (intsize != 4)
71567b563f1SJosh Cartwright 		return -EINVAL;
71667b563f1SJosh Cartwright 	if (intspec[0] > 0xF || intspec[1] > 0xFF || intspec[2] > 0x7)
71767b563f1SJosh Cartwright 		return -EINVAL;
71867b563f1SJosh Cartwright 
71967b563f1SJosh Cartwright 	spec.slave = intspec[0];
72067b563f1SJosh Cartwright 	spec.per   = intspec[1];
72167b563f1SJosh Cartwright 	spec.irq   = intspec[2];
72267b563f1SJosh Cartwright 
72367b563f1SJosh Cartwright 	err = search_mapping_table(pa, &spec, &apid);
72467b563f1SJosh Cartwright 	if (err)
72567b563f1SJosh Cartwright 		return err;
72667b563f1SJosh Cartwright 
72767b563f1SJosh Cartwright 	pa->apid_to_ppid[apid] = spec.slave << 8 | spec.per;
72867b563f1SJosh Cartwright 
72967b563f1SJosh Cartwright 	/* Keep track of {max,min}_apid for bounding search during interrupt */
73067b563f1SJosh Cartwright 	if (apid > pa->max_apid)
73167b563f1SJosh Cartwright 		pa->max_apid = apid;
73267b563f1SJosh Cartwright 	if (apid < pa->min_apid)
73367b563f1SJosh Cartwright 		pa->min_apid = apid;
73467b563f1SJosh Cartwright 
73567b563f1SJosh Cartwright 	*out_hwirq = spec.slave << 24
73667b563f1SJosh Cartwright 		   | spec.per   << 16
73767b563f1SJosh Cartwright 		   | spec.irq   << 8
73867b563f1SJosh Cartwright 		   | apid;
73967b563f1SJosh Cartwright 	*out_type  = intspec[3] & IRQ_TYPE_SENSE_MASK;
74067b563f1SJosh Cartwright 
74167b563f1SJosh Cartwright 	dev_dbg(&pa->spmic->dev, "out_hwirq = %lu\n", *out_hwirq);
74267b563f1SJosh Cartwright 
74367b563f1SJosh Cartwright 	return 0;
74467b563f1SJosh Cartwright }
74567b563f1SJosh Cartwright 
74667b563f1SJosh Cartwright static int qpnpint_irq_domain_map(struct irq_domain *d,
74767b563f1SJosh Cartwright 				  unsigned int virq,
74867b563f1SJosh Cartwright 				  irq_hw_number_t hwirq)
74967b563f1SJosh Cartwright {
75067b563f1SJosh Cartwright 	struct spmi_pmic_arb_dev *pa = d->host_data;
75167b563f1SJosh Cartwright 
75267b563f1SJosh Cartwright 	dev_dbg(&pa->spmic->dev, "virq = %u, hwirq = %lu\n", virq, hwirq);
75367b563f1SJosh Cartwright 
75467b563f1SJosh Cartwright 	irq_set_chip_and_handler(virq, &pmic_arb_irqchip, handle_level_irq);
75567b563f1SJosh Cartwright 	irq_set_chip_data(virq, d->host_data);
75667b563f1SJosh Cartwright 	irq_set_noprobe(virq);
75767b563f1SJosh Cartwright 	return 0;
75867b563f1SJosh Cartwright }
75967b563f1SJosh Cartwright 
760*57102ad7SAbhijeet Dharmapurikar static int
761*57102ad7SAbhijeet Dharmapurikar pmic_arb_mode_v1(struct spmi_pmic_arb_dev *pa, u8 sid, u16 addr, mode_t *mode)
762*57102ad7SAbhijeet Dharmapurikar {
763*57102ad7SAbhijeet Dharmapurikar 	*mode = S_IRUSR | S_IWUSR;
764*57102ad7SAbhijeet Dharmapurikar 	return 0;
765*57102ad7SAbhijeet Dharmapurikar }
766*57102ad7SAbhijeet Dharmapurikar 
767d0c6ae41SGilad Avidov /* v1 offset per ee */
768987a9f12SStephen Boyd static int
769987a9f12SStephen Boyd pmic_arb_offset_v1(struct spmi_pmic_arb_dev *pa, u8 sid, u16 addr, u32 *offset)
770d0c6ae41SGilad Avidov {
771987a9f12SStephen Boyd 	*offset = 0x800 + 0x80 * pa->channel;
772987a9f12SStephen Boyd 	return 0;
773d0c6ae41SGilad Avidov }
774d0c6ae41SGilad Avidov 
775987a9f12SStephen Boyd static u16 pmic_arb_find_chan(struct spmi_pmic_arb_dev *pa, u16 ppid)
776987a9f12SStephen Boyd {
777987a9f12SStephen Boyd 	u32 regval, offset;
778987a9f12SStephen Boyd 	u16 chan;
779987a9f12SStephen Boyd 	u16 id;
780987a9f12SStephen Boyd 
781987a9f12SStephen Boyd 	/*
782987a9f12SStephen Boyd 	 * PMIC_ARB_REG_CHNL is a table in HW mapping channel to ppid.
783987a9f12SStephen Boyd 	 * ppid_to_chan is an in-memory invert of that table.
784987a9f12SStephen Boyd 	 */
785*57102ad7SAbhijeet Dharmapurikar 	for (chan = pa->last_channel; chan < pa->max_periph; chan++) {
786*57102ad7SAbhijeet Dharmapurikar 		regval = readl_relaxed(pa->cnfg +
787*57102ad7SAbhijeet Dharmapurikar 				      SPMI_OWNERSHIP_TABLE_REG(chan));
788*57102ad7SAbhijeet Dharmapurikar 		pa->chan_to_owner[chan] = SPMI_OWNERSHIP_PERIPH2OWNER(regval);
789*57102ad7SAbhijeet Dharmapurikar 
790987a9f12SStephen Boyd 		offset = PMIC_ARB_REG_CHNL(chan);
791987a9f12SStephen Boyd 		if (offset >= pa->core_size)
792987a9f12SStephen Boyd 			break;
793987a9f12SStephen Boyd 
794987a9f12SStephen Boyd 		regval = readl_relaxed(pa->core + offset);
795987a9f12SStephen Boyd 		if (!regval)
796987a9f12SStephen Boyd 			continue;
797987a9f12SStephen Boyd 
798987a9f12SStephen Boyd 		id = (regval >> 8) & PMIC_ARB_PPID_MASK;
799987a9f12SStephen Boyd 		pa->ppid_to_chan[id] = chan | PMIC_ARB_CHAN_VALID;
800987a9f12SStephen Boyd 		if (id == ppid) {
801987a9f12SStephen Boyd 			chan |= PMIC_ARB_CHAN_VALID;
802987a9f12SStephen Boyd 			break;
803987a9f12SStephen Boyd 		}
804987a9f12SStephen Boyd 	}
805987a9f12SStephen Boyd 	pa->last_channel = chan & ~PMIC_ARB_CHAN_VALID;
806987a9f12SStephen Boyd 
807987a9f12SStephen Boyd 	return chan;
808987a9f12SStephen Boyd }
809987a9f12SStephen Boyd 
810987a9f12SStephen Boyd 
811*57102ad7SAbhijeet Dharmapurikar static int
812*57102ad7SAbhijeet Dharmapurikar pmic_arb_mode_v2(struct spmi_pmic_arb_dev *pa, u8 sid, u16 addr, mode_t *mode)
813*57102ad7SAbhijeet Dharmapurikar {
814*57102ad7SAbhijeet Dharmapurikar 	u16 ppid = (sid << 8) | (addr >> 8);
815*57102ad7SAbhijeet Dharmapurikar 	u16 chan;
816*57102ad7SAbhijeet Dharmapurikar 	u8 owner;
817*57102ad7SAbhijeet Dharmapurikar 
818*57102ad7SAbhijeet Dharmapurikar 	chan = pa->ppid_to_chan[ppid];
819*57102ad7SAbhijeet Dharmapurikar 	if (!(chan & PMIC_ARB_CHAN_VALID))
820*57102ad7SAbhijeet Dharmapurikar 		return -ENODEV;
821*57102ad7SAbhijeet Dharmapurikar 
822*57102ad7SAbhijeet Dharmapurikar 	*mode = 0;
823*57102ad7SAbhijeet Dharmapurikar 	*mode |= S_IRUSR;
824*57102ad7SAbhijeet Dharmapurikar 
825*57102ad7SAbhijeet Dharmapurikar 	chan &= ~PMIC_ARB_CHAN_VALID;
826*57102ad7SAbhijeet Dharmapurikar 	owner = pa->chan_to_owner[chan];
827*57102ad7SAbhijeet Dharmapurikar 	if (owner == pa->ee)
828*57102ad7SAbhijeet Dharmapurikar 		*mode |= S_IWUSR;
829*57102ad7SAbhijeet Dharmapurikar 	return 0;
830*57102ad7SAbhijeet Dharmapurikar }
831*57102ad7SAbhijeet Dharmapurikar 
832d0c6ae41SGilad Avidov /* v2 offset per ppid (chan) and per ee */
833987a9f12SStephen Boyd static int
834987a9f12SStephen Boyd pmic_arb_offset_v2(struct spmi_pmic_arb_dev *pa, u8 sid, u16 addr, u32 *offset)
835d0c6ae41SGilad Avidov {
836d0c6ae41SGilad Avidov 	u16 ppid = (sid << 8) | (addr >> 8);
837987a9f12SStephen Boyd 	u16 chan;
838d0c6ae41SGilad Avidov 
839987a9f12SStephen Boyd 	chan = pa->ppid_to_chan[ppid];
840987a9f12SStephen Boyd 	if (!(chan & PMIC_ARB_CHAN_VALID))
841987a9f12SStephen Boyd 		chan = pmic_arb_find_chan(pa, ppid);
842987a9f12SStephen Boyd 	if (!(chan & PMIC_ARB_CHAN_VALID))
843987a9f12SStephen Boyd 		return -ENODEV;
844987a9f12SStephen Boyd 	chan &= ~PMIC_ARB_CHAN_VALID;
845987a9f12SStephen Boyd 
846987a9f12SStephen Boyd 	*offset = 0x1000 * pa->ee + 0x8000 * chan;
847987a9f12SStephen Boyd 	return 0;
848d0c6ae41SGilad Avidov }
849d0c6ae41SGilad Avidov 
850d0c6ae41SGilad Avidov static u32 pmic_arb_fmt_cmd_v1(u8 opc, u8 sid, u16 addr, u8 bc)
851d0c6ae41SGilad Avidov {
852d0c6ae41SGilad Avidov 	return (opc << 27) | ((sid & 0xf) << 20) | (addr << 4) | (bc & 0x7);
853d0c6ae41SGilad Avidov }
854d0c6ae41SGilad Avidov 
855d0c6ae41SGilad Avidov static u32 pmic_arb_fmt_cmd_v2(u8 opc, u8 sid, u16 addr, u8 bc)
856d0c6ae41SGilad Avidov {
857d0c6ae41SGilad Avidov 	return (opc << 27) | ((addr & 0xff) << 4) | (bc & 0x7);
858d0c6ae41SGilad Avidov }
859d0c6ae41SGilad Avidov 
860d0c6ae41SGilad Avidov static u32 pmic_arb_owner_acc_status_v1(u8 m, u8 n)
861d0c6ae41SGilad Avidov {
862d0c6ae41SGilad Avidov 	return 0x20 * m + 0x4 * n;
863d0c6ae41SGilad Avidov }
864d0c6ae41SGilad Avidov 
865d0c6ae41SGilad Avidov static u32 pmic_arb_owner_acc_status_v2(u8 m, u8 n)
866d0c6ae41SGilad Avidov {
867d0c6ae41SGilad Avidov 	return 0x100000 + 0x1000 * m + 0x4 * n;
868d0c6ae41SGilad Avidov }
869d0c6ae41SGilad Avidov 
870d0c6ae41SGilad Avidov static u32 pmic_arb_acc_enable_v1(u8 n)
871d0c6ae41SGilad Avidov {
872d0c6ae41SGilad Avidov 	return 0x200 + 0x4 * n;
873d0c6ae41SGilad Avidov }
874d0c6ae41SGilad Avidov 
875d0c6ae41SGilad Avidov static u32 pmic_arb_acc_enable_v2(u8 n)
876d0c6ae41SGilad Avidov {
877d0c6ae41SGilad Avidov 	return 0x1000 * n;
878d0c6ae41SGilad Avidov }
879d0c6ae41SGilad Avidov 
880d0c6ae41SGilad Avidov static u32 pmic_arb_irq_status_v1(u8 n)
881d0c6ae41SGilad Avidov {
882d0c6ae41SGilad Avidov 	return 0x600 + 0x4 * n;
883d0c6ae41SGilad Avidov }
884d0c6ae41SGilad Avidov 
885d0c6ae41SGilad Avidov static u32 pmic_arb_irq_status_v2(u8 n)
886d0c6ae41SGilad Avidov {
887d0c6ae41SGilad Avidov 	return 0x4 + 0x1000 * n;
888d0c6ae41SGilad Avidov }
889d0c6ae41SGilad Avidov 
890d0c6ae41SGilad Avidov static u32 pmic_arb_irq_clear_v1(u8 n)
891d0c6ae41SGilad Avidov {
892d0c6ae41SGilad Avidov 	return 0xA00 + 0x4 * n;
893d0c6ae41SGilad Avidov }
894d0c6ae41SGilad Avidov 
895d0c6ae41SGilad Avidov static u32 pmic_arb_irq_clear_v2(u8 n)
896d0c6ae41SGilad Avidov {
897d0c6ae41SGilad Avidov 	return 0x8 + 0x1000 * n;
898d0c6ae41SGilad Avidov }
899d0c6ae41SGilad Avidov 
900d0c6ae41SGilad Avidov static const struct pmic_arb_ver_ops pmic_arb_v1 = {
901*57102ad7SAbhijeet Dharmapurikar 	.mode			= pmic_arb_mode_v1,
902d0c6ae41SGilad Avidov 	.non_data_cmd		= pmic_arb_non_data_cmd_v1,
903d0c6ae41SGilad Avidov 	.offset			= pmic_arb_offset_v1,
904d0c6ae41SGilad Avidov 	.fmt_cmd		= pmic_arb_fmt_cmd_v1,
905d0c6ae41SGilad Avidov 	.owner_acc_status	= pmic_arb_owner_acc_status_v1,
906d0c6ae41SGilad Avidov 	.acc_enable		= pmic_arb_acc_enable_v1,
907d0c6ae41SGilad Avidov 	.irq_status		= pmic_arb_irq_status_v1,
908d0c6ae41SGilad Avidov 	.irq_clear		= pmic_arb_irq_clear_v1,
909d0c6ae41SGilad Avidov };
910d0c6ae41SGilad Avidov 
911d0c6ae41SGilad Avidov static const struct pmic_arb_ver_ops pmic_arb_v2 = {
912*57102ad7SAbhijeet Dharmapurikar 	.mode			= pmic_arb_mode_v2,
913d0c6ae41SGilad Avidov 	.non_data_cmd		= pmic_arb_non_data_cmd_v2,
914d0c6ae41SGilad Avidov 	.offset			= pmic_arb_offset_v2,
915d0c6ae41SGilad Avidov 	.fmt_cmd		= pmic_arb_fmt_cmd_v2,
916d0c6ae41SGilad Avidov 	.owner_acc_status	= pmic_arb_owner_acc_status_v2,
917d0c6ae41SGilad Avidov 	.acc_enable		= pmic_arb_acc_enable_v2,
918d0c6ae41SGilad Avidov 	.irq_status		= pmic_arb_irq_status_v2,
919d0c6ae41SGilad Avidov 	.irq_clear		= pmic_arb_irq_clear_v2,
920d0c6ae41SGilad Avidov };
921d0c6ae41SGilad Avidov 
92267b563f1SJosh Cartwright static const struct irq_domain_ops pmic_arb_irq_domain_ops = {
92367b563f1SJosh Cartwright 	.map	= qpnpint_irq_domain_map,
92467b563f1SJosh Cartwright 	.xlate	= qpnpint_irq_domain_dt_translate,
92567b563f1SJosh Cartwright };
92667b563f1SJosh Cartwright 
92739ae93e3SKenneth Heitke static int spmi_pmic_arb_probe(struct platform_device *pdev)
92839ae93e3SKenneth Heitke {
92939ae93e3SKenneth Heitke 	struct spmi_pmic_arb_dev *pa;
93039ae93e3SKenneth Heitke 	struct spmi_controller *ctrl;
93139ae93e3SKenneth Heitke 	struct resource *res;
932d0c6ae41SGilad Avidov 	void __iomem *core;
933d0c6ae41SGilad Avidov 	u32 channel, ee, hw_ver;
934987a9f12SStephen Boyd 	int err;
935d0c6ae41SGilad Avidov 	bool is_v1;
93639ae93e3SKenneth Heitke 
93739ae93e3SKenneth Heitke 	ctrl = spmi_controller_alloc(&pdev->dev, sizeof(*pa));
93839ae93e3SKenneth Heitke 	if (!ctrl)
93939ae93e3SKenneth Heitke 		return -ENOMEM;
94039ae93e3SKenneth Heitke 
94139ae93e3SKenneth Heitke 	pa = spmi_controller_get_drvdata(ctrl);
94267b563f1SJosh Cartwright 	pa->spmic = ctrl;
94339ae93e3SKenneth Heitke 
94439ae93e3SKenneth Heitke 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "core");
945987a9f12SStephen Boyd 	pa->core_size = resource_size(res);
946*57102ad7SAbhijeet Dharmapurikar 	if (pa->core_size <= 0x800) {
947*57102ad7SAbhijeet Dharmapurikar 		dev_err(&pdev->dev, "core_size is smaller than 0x800. Failing Probe\n");
948*57102ad7SAbhijeet Dharmapurikar 		err = -EINVAL;
949*57102ad7SAbhijeet Dharmapurikar 		goto err_put_ctrl;
950*57102ad7SAbhijeet Dharmapurikar 	}
951*57102ad7SAbhijeet Dharmapurikar 
952d0c6ae41SGilad Avidov 	core = devm_ioremap_resource(&ctrl->dev, res);
953d0c6ae41SGilad Avidov 	if (IS_ERR(core)) {
954d0c6ae41SGilad Avidov 		err = PTR_ERR(core);
95539ae93e3SKenneth Heitke 		goto err_put_ctrl;
95639ae93e3SKenneth Heitke 	}
95739ae93e3SKenneth Heitke 
958d0c6ae41SGilad Avidov 	hw_ver = readl_relaxed(core + PMIC_ARB_VERSION);
959d0c6ae41SGilad Avidov 	is_v1  = (hw_ver < PMIC_ARB_VERSION_V2_MIN);
960d0c6ae41SGilad Avidov 
961d0c6ae41SGilad Avidov 	dev_info(&ctrl->dev, "PMIC Arb Version-%d (0x%x)\n", (is_v1 ? 1 : 2),
962d0c6ae41SGilad Avidov 		hw_ver);
963d0c6ae41SGilad Avidov 
964d0c6ae41SGilad Avidov 	if (is_v1) {
965d0c6ae41SGilad Avidov 		pa->ver_ops = &pmic_arb_v1;
966d0c6ae41SGilad Avidov 		pa->wr_base = core;
967d0c6ae41SGilad Avidov 		pa->rd_base = core;
968d0c6ae41SGilad Avidov 	} else {
969987a9f12SStephen Boyd 		pa->core = core;
970d0c6ae41SGilad Avidov 		pa->ver_ops = &pmic_arb_v2;
971d0c6ae41SGilad Avidov 
972*57102ad7SAbhijeet Dharmapurikar 		/* the apid to ppid table starts at PMIC_ARB_REG_CHNL(0) */
973*57102ad7SAbhijeet Dharmapurikar 		pa->max_periph =  (pa->core_size - PMIC_ARB_REG_CHNL(0)) / 4;
974*57102ad7SAbhijeet Dharmapurikar 
975d0c6ae41SGilad Avidov 		res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
976d0c6ae41SGilad Avidov 						   "obsrvr");
977d0c6ae41SGilad Avidov 		pa->rd_base = devm_ioremap_resource(&ctrl->dev, res);
978d0c6ae41SGilad Avidov 		if (IS_ERR(pa->rd_base)) {
979d0c6ae41SGilad Avidov 			err = PTR_ERR(pa->rd_base);
980d0c6ae41SGilad Avidov 			goto err_put_ctrl;
981d0c6ae41SGilad Avidov 		}
982d0c6ae41SGilad Avidov 
983d0c6ae41SGilad Avidov 		res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
984d0c6ae41SGilad Avidov 						   "chnls");
985d0c6ae41SGilad Avidov 		pa->wr_base = devm_ioremap_resource(&ctrl->dev, res);
986d0c6ae41SGilad Avidov 		if (IS_ERR(pa->wr_base)) {
987d0c6ae41SGilad Avidov 			err = PTR_ERR(pa->wr_base);
988d0c6ae41SGilad Avidov 			goto err_put_ctrl;
989d0c6ae41SGilad Avidov 		}
990d0c6ae41SGilad Avidov 
991987a9f12SStephen Boyd 		pa->ppid_to_chan = devm_kcalloc(&ctrl->dev,
992987a9f12SStephen Boyd 						PMIC_ARB_MAX_PPID,
993987a9f12SStephen Boyd 						sizeof(*pa->ppid_to_chan),
994987a9f12SStephen Boyd 						GFP_KERNEL);
995d0c6ae41SGilad Avidov 		if (!pa->ppid_to_chan) {
996d0c6ae41SGilad Avidov 			err = -ENOMEM;
997d0c6ae41SGilad Avidov 			goto err_put_ctrl;
998d0c6ae41SGilad Avidov 		}
999*57102ad7SAbhijeet Dharmapurikar 
1000*57102ad7SAbhijeet Dharmapurikar 		pa->chan_to_owner = devm_kcalloc(&ctrl->dev,
1001*57102ad7SAbhijeet Dharmapurikar 						 pa->max_periph,
1002*57102ad7SAbhijeet Dharmapurikar 						 sizeof(*pa->chan_to_owner),
1003*57102ad7SAbhijeet Dharmapurikar 						 GFP_KERNEL);
1004*57102ad7SAbhijeet Dharmapurikar 		if (!pa->chan_to_owner) {
1005*57102ad7SAbhijeet Dharmapurikar 			err = -ENOMEM;
1006*57102ad7SAbhijeet Dharmapurikar 			goto err_put_ctrl;
1007*57102ad7SAbhijeet Dharmapurikar 		}
1008d0c6ae41SGilad Avidov 	}
1009d0c6ae41SGilad Avidov 
101039ae93e3SKenneth Heitke 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "intr");
101139ae93e3SKenneth Heitke 	pa->intr = devm_ioremap_resource(&ctrl->dev, res);
101239ae93e3SKenneth Heitke 	if (IS_ERR(pa->intr)) {
101339ae93e3SKenneth Heitke 		err = PTR_ERR(pa->intr);
101439ae93e3SKenneth Heitke 		goto err_put_ctrl;
101539ae93e3SKenneth Heitke 	}
101639ae93e3SKenneth Heitke 
101739ae93e3SKenneth Heitke 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cnfg");
101839ae93e3SKenneth Heitke 	pa->cnfg = devm_ioremap_resource(&ctrl->dev, res);
101939ae93e3SKenneth Heitke 	if (IS_ERR(pa->cnfg)) {
102039ae93e3SKenneth Heitke 		err = PTR_ERR(pa->cnfg);
102139ae93e3SKenneth Heitke 		goto err_put_ctrl;
102239ae93e3SKenneth Heitke 	}
102339ae93e3SKenneth Heitke 
102467b563f1SJosh Cartwright 	pa->irq = platform_get_irq_byname(pdev, "periph_irq");
102567b563f1SJosh Cartwright 	if (pa->irq < 0) {
102667b563f1SJosh Cartwright 		err = pa->irq;
102767b563f1SJosh Cartwright 		goto err_put_ctrl;
102867b563f1SJosh Cartwright 	}
102967b563f1SJosh Cartwright 
103039ae93e3SKenneth Heitke 	err = of_property_read_u32(pdev->dev.of_node, "qcom,channel", &channel);
103139ae93e3SKenneth Heitke 	if (err) {
103239ae93e3SKenneth Heitke 		dev_err(&pdev->dev, "channel unspecified.\n");
103339ae93e3SKenneth Heitke 		goto err_put_ctrl;
103439ae93e3SKenneth Heitke 	}
103539ae93e3SKenneth Heitke 
103639ae93e3SKenneth Heitke 	if (channel > 5) {
103739ae93e3SKenneth Heitke 		dev_err(&pdev->dev, "invalid channel (%u) specified.\n",
103839ae93e3SKenneth Heitke 			channel);
1039e98cc182SChristophe JAILLET 		err = -EINVAL;
104039ae93e3SKenneth Heitke 		goto err_put_ctrl;
104139ae93e3SKenneth Heitke 	}
104239ae93e3SKenneth Heitke 
104339ae93e3SKenneth Heitke 	pa->channel = channel;
104439ae93e3SKenneth Heitke 
104567b563f1SJosh Cartwright 	err = of_property_read_u32(pdev->dev.of_node, "qcom,ee", &ee);
104667b563f1SJosh Cartwright 	if (err) {
104767b563f1SJosh Cartwright 		dev_err(&pdev->dev, "EE unspecified.\n");
104867b563f1SJosh Cartwright 		goto err_put_ctrl;
104967b563f1SJosh Cartwright 	}
105067b563f1SJosh Cartwright 
105167b563f1SJosh Cartwright 	if (ee > 5) {
105267b563f1SJosh Cartwright 		dev_err(&pdev->dev, "invalid EE (%u) specified\n", ee);
105367b563f1SJosh Cartwright 		err = -EINVAL;
105467b563f1SJosh Cartwright 		goto err_put_ctrl;
105567b563f1SJosh Cartwright 	}
105667b563f1SJosh Cartwright 
105767b563f1SJosh Cartwright 	pa->ee = ee;
105867b563f1SJosh Cartwright 
1059987a9f12SStephen Boyd 	pa->apid_to_ppid = devm_kcalloc(&ctrl->dev, PMIC_ARB_MAX_PERIPHS,
1060987a9f12SStephen Boyd 					    sizeof(*pa->apid_to_ppid),
1061987a9f12SStephen Boyd 					    GFP_KERNEL);
1062987a9f12SStephen Boyd 	if (!pa->apid_to_ppid) {
1063987a9f12SStephen Boyd 		err = -ENOMEM;
1064987a9f12SStephen Boyd 		goto err_put_ctrl;
1065987a9f12SStephen Boyd 	}
1066987a9f12SStephen Boyd 
1067987a9f12SStephen Boyd 	pa->mapping_table = devm_kcalloc(&ctrl->dev, PMIC_ARB_MAX_PERIPHS - 1,
1068987a9f12SStephen Boyd 					sizeof(*pa->mapping_table), GFP_KERNEL);
1069987a9f12SStephen Boyd 	if (!pa->mapping_table) {
1070987a9f12SStephen Boyd 		err = -ENOMEM;
1071987a9f12SStephen Boyd 		goto err_put_ctrl;
1072987a9f12SStephen Boyd 	}
107367b563f1SJosh Cartwright 
107467b563f1SJosh Cartwright 	/* Initialize max_apid/min_apid to the opposite bounds, during
107567b563f1SJosh Cartwright 	 * the irq domain translation, we are sure to update these */
107667b563f1SJosh Cartwright 	pa->max_apid = 0;
107767b563f1SJosh Cartwright 	pa->min_apid = PMIC_ARB_MAX_PERIPHS - 1;
107867b563f1SJosh Cartwright 
107939ae93e3SKenneth Heitke 	platform_set_drvdata(pdev, ctrl);
108039ae93e3SKenneth Heitke 	raw_spin_lock_init(&pa->lock);
108139ae93e3SKenneth Heitke 
108239ae93e3SKenneth Heitke 	ctrl->cmd = pmic_arb_cmd;
108339ae93e3SKenneth Heitke 	ctrl->read_cmd = pmic_arb_read_cmd;
108439ae93e3SKenneth Heitke 	ctrl->write_cmd = pmic_arb_write_cmd;
108539ae93e3SKenneth Heitke 
108667b563f1SJosh Cartwright 	dev_dbg(&pdev->dev, "adding irq domain\n");
108767b563f1SJosh Cartwright 	pa->domain = irq_domain_add_tree(pdev->dev.of_node,
108867b563f1SJosh Cartwright 					 &pmic_arb_irq_domain_ops, pa);
108967b563f1SJosh Cartwright 	if (!pa->domain) {
109067b563f1SJosh Cartwright 		dev_err(&pdev->dev, "unable to create irq_domain\n");
109167b563f1SJosh Cartwright 		err = -ENOMEM;
109267b563f1SJosh Cartwright 		goto err_put_ctrl;
109367b563f1SJosh Cartwright 	}
109467b563f1SJosh Cartwright 
1095fb68ba6dSThomas Gleixner 	irq_set_chained_handler_and_data(pa->irq, pmic_arb_chained_irq, pa);
109667b563f1SJosh Cartwright 
109739ae93e3SKenneth Heitke 	err = spmi_controller_add(ctrl);
109839ae93e3SKenneth Heitke 	if (err)
109967b563f1SJosh Cartwright 		goto err_domain_remove;
110039ae93e3SKenneth Heitke 
110139ae93e3SKenneth Heitke 	return 0;
110239ae93e3SKenneth Heitke 
110367b563f1SJosh Cartwright err_domain_remove:
1104fb68ba6dSThomas Gleixner 	irq_set_chained_handler_and_data(pa->irq, NULL, NULL);
110567b563f1SJosh Cartwright 	irq_domain_remove(pa->domain);
110639ae93e3SKenneth Heitke err_put_ctrl:
110739ae93e3SKenneth Heitke 	spmi_controller_put(ctrl);
110839ae93e3SKenneth Heitke 	return err;
110939ae93e3SKenneth Heitke }
111039ae93e3SKenneth Heitke 
111139ae93e3SKenneth Heitke static int spmi_pmic_arb_remove(struct platform_device *pdev)
111239ae93e3SKenneth Heitke {
111339ae93e3SKenneth Heitke 	struct spmi_controller *ctrl = platform_get_drvdata(pdev);
111467b563f1SJosh Cartwright 	struct spmi_pmic_arb_dev *pa = spmi_controller_get_drvdata(ctrl);
111539ae93e3SKenneth Heitke 	spmi_controller_remove(ctrl);
1116fb68ba6dSThomas Gleixner 	irq_set_chained_handler_and_data(pa->irq, NULL, NULL);
111767b563f1SJosh Cartwright 	irq_domain_remove(pa->domain);
111839ae93e3SKenneth Heitke 	spmi_controller_put(ctrl);
111939ae93e3SKenneth Heitke 	return 0;
112039ae93e3SKenneth Heitke }
112139ae93e3SKenneth Heitke 
112239ae93e3SKenneth Heitke static const struct of_device_id spmi_pmic_arb_match_table[] = {
112339ae93e3SKenneth Heitke 	{ .compatible = "qcom,spmi-pmic-arb", },
112439ae93e3SKenneth Heitke 	{},
112539ae93e3SKenneth Heitke };
112639ae93e3SKenneth Heitke MODULE_DEVICE_TABLE(of, spmi_pmic_arb_match_table);
112739ae93e3SKenneth Heitke 
112839ae93e3SKenneth Heitke static struct platform_driver spmi_pmic_arb_driver = {
112939ae93e3SKenneth Heitke 	.probe		= spmi_pmic_arb_probe,
113039ae93e3SKenneth Heitke 	.remove		= spmi_pmic_arb_remove,
113139ae93e3SKenneth Heitke 	.driver		= {
113239ae93e3SKenneth Heitke 		.name	= "spmi_pmic_arb",
113339ae93e3SKenneth Heitke 		.of_match_table = spmi_pmic_arb_match_table,
113439ae93e3SKenneth Heitke 	},
113539ae93e3SKenneth Heitke };
113639ae93e3SKenneth Heitke module_platform_driver(spmi_pmic_arb_driver);
113739ae93e3SKenneth Heitke 
113839ae93e3SKenneth Heitke MODULE_LICENSE("GPL v2");
113939ae93e3SKenneth Heitke MODULE_ALIAS("platform:spmi_pmic_arb");
1140