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