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