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