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