162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2012-2015, 2017, 2021, The Linux Foundation. All rights reserved. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci#include <linux/bitmap.h> 662306a36Sopenharmony_ci#include <linux/delay.h> 762306a36Sopenharmony_ci#include <linux/err.h> 862306a36Sopenharmony_ci#include <linux/interrupt.h> 962306a36Sopenharmony_ci#include <linux/io.h> 1062306a36Sopenharmony_ci#include <linux/irqchip/chained_irq.h> 1162306a36Sopenharmony_ci#include <linux/irqdomain.h> 1262306a36Sopenharmony_ci#include <linux/irq.h> 1362306a36Sopenharmony_ci#include <linux/kernel.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/of.h> 1662306a36Sopenharmony_ci#include <linux/platform_device.h> 1762306a36Sopenharmony_ci#include <linux/slab.h> 1862306a36Sopenharmony_ci#include <linux/spmi.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci/* PMIC Arbiter configuration registers */ 2162306a36Sopenharmony_ci#define PMIC_ARB_VERSION 0x0000 2262306a36Sopenharmony_ci#define PMIC_ARB_VERSION_V2_MIN 0x20010000 2362306a36Sopenharmony_ci#define PMIC_ARB_VERSION_V3_MIN 0x30000000 2462306a36Sopenharmony_ci#define PMIC_ARB_VERSION_V5_MIN 0x50000000 2562306a36Sopenharmony_ci#define PMIC_ARB_VERSION_V7_MIN 0x70000000 2662306a36Sopenharmony_ci#define PMIC_ARB_INT_EN 0x0004 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#define PMIC_ARB_FEATURES 0x0004 2962306a36Sopenharmony_ci#define PMIC_ARB_FEATURES_PERIPH_MASK GENMASK(10, 0) 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#define PMIC_ARB_FEATURES1 0x0008 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* PMIC Arbiter channel registers offsets */ 3462306a36Sopenharmony_ci#define PMIC_ARB_CMD 0x00 3562306a36Sopenharmony_ci#define PMIC_ARB_CONFIG 0x04 3662306a36Sopenharmony_ci#define PMIC_ARB_STATUS 0x08 3762306a36Sopenharmony_ci#define PMIC_ARB_WDATA0 0x10 3862306a36Sopenharmony_ci#define PMIC_ARB_WDATA1 0x14 3962306a36Sopenharmony_ci#define PMIC_ARB_RDATA0 0x18 4062306a36Sopenharmony_ci#define PMIC_ARB_RDATA1 0x1C 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci/* Mapping Table */ 4362306a36Sopenharmony_ci#define SPMI_MAPPING_TABLE_REG(N) (0x0B00 + (4 * (N))) 4462306a36Sopenharmony_ci#define SPMI_MAPPING_BIT_INDEX(X) (((X) >> 18) & 0xF) 4562306a36Sopenharmony_ci#define SPMI_MAPPING_BIT_IS_0_FLAG(X) (((X) >> 17) & 0x1) 4662306a36Sopenharmony_ci#define SPMI_MAPPING_BIT_IS_0_RESULT(X) (((X) >> 9) & 0xFF) 4762306a36Sopenharmony_ci#define SPMI_MAPPING_BIT_IS_1_FLAG(X) (((X) >> 8) & 0x1) 4862306a36Sopenharmony_ci#define SPMI_MAPPING_BIT_IS_1_RESULT(X) (((X) >> 0) & 0xFF) 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci#define SPMI_MAPPING_TABLE_TREE_DEPTH 16 /* Maximum of 16-bits */ 5162306a36Sopenharmony_ci#define PMIC_ARB_MAX_PPID BIT(12) /* PPID is 12bit */ 5262306a36Sopenharmony_ci#define PMIC_ARB_APID_VALID BIT(15) 5362306a36Sopenharmony_ci#define PMIC_ARB_CHAN_IS_IRQ_OWNER(reg) ((reg) & BIT(24)) 5462306a36Sopenharmony_ci#define INVALID_EE 0xFF 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci/* Ownership Table */ 5762306a36Sopenharmony_ci#define SPMI_OWNERSHIP_PERIPH2OWNER(X) ((X) & 0x7) 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci/* Channel Status fields */ 6062306a36Sopenharmony_cienum pmic_arb_chnl_status { 6162306a36Sopenharmony_ci PMIC_ARB_STATUS_DONE = BIT(0), 6262306a36Sopenharmony_ci PMIC_ARB_STATUS_FAILURE = BIT(1), 6362306a36Sopenharmony_ci PMIC_ARB_STATUS_DENIED = BIT(2), 6462306a36Sopenharmony_ci PMIC_ARB_STATUS_DROPPED = BIT(3), 6562306a36Sopenharmony_ci}; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci/* Command register fields */ 6862306a36Sopenharmony_ci#define PMIC_ARB_CMD_MAX_BYTE_COUNT 8 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci/* Command Opcodes */ 7162306a36Sopenharmony_cienum pmic_arb_cmd_op_code { 7262306a36Sopenharmony_ci PMIC_ARB_OP_EXT_WRITEL = 0, 7362306a36Sopenharmony_ci PMIC_ARB_OP_EXT_READL = 1, 7462306a36Sopenharmony_ci PMIC_ARB_OP_EXT_WRITE = 2, 7562306a36Sopenharmony_ci PMIC_ARB_OP_RESET = 3, 7662306a36Sopenharmony_ci PMIC_ARB_OP_SLEEP = 4, 7762306a36Sopenharmony_ci PMIC_ARB_OP_SHUTDOWN = 5, 7862306a36Sopenharmony_ci PMIC_ARB_OP_WAKEUP = 6, 7962306a36Sopenharmony_ci PMIC_ARB_OP_AUTHENTICATE = 7, 8062306a36Sopenharmony_ci PMIC_ARB_OP_MSTR_READ = 8, 8162306a36Sopenharmony_ci PMIC_ARB_OP_MSTR_WRITE = 9, 8262306a36Sopenharmony_ci PMIC_ARB_OP_EXT_READ = 13, 8362306a36Sopenharmony_ci PMIC_ARB_OP_WRITE = 14, 8462306a36Sopenharmony_ci PMIC_ARB_OP_READ = 15, 8562306a36Sopenharmony_ci PMIC_ARB_OP_ZERO_WRITE = 16, 8662306a36Sopenharmony_ci}; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci/* 8962306a36Sopenharmony_ci * PMIC arbiter version 5 uses different register offsets for read/write vs 9062306a36Sopenharmony_ci * observer channels. 9162306a36Sopenharmony_ci */ 9262306a36Sopenharmony_cienum pmic_arb_channel { 9362306a36Sopenharmony_ci PMIC_ARB_CHANNEL_RW, 9462306a36Sopenharmony_ci PMIC_ARB_CHANNEL_OBS, 9562306a36Sopenharmony_ci}; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci/* Maximum number of support PMIC peripherals */ 9862306a36Sopenharmony_ci#define PMIC_ARB_MAX_PERIPHS 512 9962306a36Sopenharmony_ci#define PMIC_ARB_MAX_PERIPHS_V7 1024 10062306a36Sopenharmony_ci#define PMIC_ARB_TIMEOUT_US 1000 10162306a36Sopenharmony_ci#define PMIC_ARB_MAX_TRANS_BYTES (8) 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci#define PMIC_ARB_APID_MASK 0xFF 10462306a36Sopenharmony_ci#define PMIC_ARB_PPID_MASK 0xFFF 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci/* interrupt enable bit */ 10762306a36Sopenharmony_ci#define SPMI_PIC_ACC_ENABLE_BIT BIT(0) 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci#define spec_to_hwirq(slave_id, periph_id, irq_id, apid) \ 11062306a36Sopenharmony_ci ((((slave_id) & 0xF) << 28) | \ 11162306a36Sopenharmony_ci (((periph_id) & 0xFF) << 20) | \ 11262306a36Sopenharmony_ci (((irq_id) & 0x7) << 16) | \ 11362306a36Sopenharmony_ci (((apid) & 0x3FF) << 0)) 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci#define hwirq_to_sid(hwirq) (((hwirq) >> 28) & 0xF) 11662306a36Sopenharmony_ci#define hwirq_to_per(hwirq) (((hwirq) >> 20) & 0xFF) 11762306a36Sopenharmony_ci#define hwirq_to_irq(hwirq) (((hwirq) >> 16) & 0x7) 11862306a36Sopenharmony_ci#define hwirq_to_apid(hwirq) (((hwirq) >> 0) & 0x3FF) 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistruct pmic_arb_ver_ops; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistruct apid_data { 12362306a36Sopenharmony_ci u16 ppid; 12462306a36Sopenharmony_ci u8 write_ee; 12562306a36Sopenharmony_ci u8 irq_ee; 12662306a36Sopenharmony_ci}; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci/** 12962306a36Sopenharmony_ci * struct spmi_pmic_arb - SPMI PMIC Arbiter object 13062306a36Sopenharmony_ci * 13162306a36Sopenharmony_ci * @rd_base: on v1 "core", on v2 "observer" register base off DT. 13262306a36Sopenharmony_ci * @wr_base: on v1 "core", on v2 "chnls" register base off DT. 13362306a36Sopenharmony_ci * @intr: address of the SPMI interrupt control registers. 13462306a36Sopenharmony_ci * @cnfg: address of the PMIC Arbiter configuration registers. 13562306a36Sopenharmony_ci * @lock: lock to synchronize accesses. 13662306a36Sopenharmony_ci * @channel: execution environment channel to use for accesses. 13762306a36Sopenharmony_ci * @irq: PMIC ARB interrupt. 13862306a36Sopenharmony_ci * @ee: the current Execution Environment 13962306a36Sopenharmony_ci * @bus_instance: on v7: 0 = primary SPMI bus, 1 = secondary SPMI bus 14062306a36Sopenharmony_ci * @min_apid: minimum APID (used for bounding IRQ search) 14162306a36Sopenharmony_ci * @max_apid: maximum APID 14262306a36Sopenharmony_ci * @base_apid: on v7: minimum APID associated with the particular SPMI 14362306a36Sopenharmony_ci * bus instance 14462306a36Sopenharmony_ci * @apid_count: on v5 and v7: number of APIDs associated with the 14562306a36Sopenharmony_ci * particular SPMI bus instance 14662306a36Sopenharmony_ci * @mapping_table: in-memory copy of PPID -> APID mapping table. 14762306a36Sopenharmony_ci * @domain: irq domain object for PMIC IRQ domain 14862306a36Sopenharmony_ci * @spmic: SPMI controller object 14962306a36Sopenharmony_ci * @ver_ops: version dependent operations. 15062306a36Sopenharmony_ci * @ppid_to_apid: in-memory copy of PPID -> APID mapping table. 15162306a36Sopenharmony_ci * @last_apid: Highest value APID in use 15262306a36Sopenharmony_ci * @apid_data: Table of data for all APIDs 15362306a36Sopenharmony_ci * @max_periphs: Number of elements in apid_data[] 15462306a36Sopenharmony_ci */ 15562306a36Sopenharmony_cistruct spmi_pmic_arb { 15662306a36Sopenharmony_ci void __iomem *rd_base; 15762306a36Sopenharmony_ci void __iomem *wr_base; 15862306a36Sopenharmony_ci void __iomem *intr; 15962306a36Sopenharmony_ci void __iomem *cnfg; 16062306a36Sopenharmony_ci void __iomem *core; 16162306a36Sopenharmony_ci resource_size_t core_size; 16262306a36Sopenharmony_ci raw_spinlock_t lock; 16362306a36Sopenharmony_ci u8 channel; 16462306a36Sopenharmony_ci int irq; 16562306a36Sopenharmony_ci u8 ee; 16662306a36Sopenharmony_ci u32 bus_instance; 16762306a36Sopenharmony_ci u16 min_apid; 16862306a36Sopenharmony_ci u16 max_apid; 16962306a36Sopenharmony_ci u16 base_apid; 17062306a36Sopenharmony_ci int apid_count; 17162306a36Sopenharmony_ci u32 *mapping_table; 17262306a36Sopenharmony_ci DECLARE_BITMAP(mapping_table_valid, PMIC_ARB_MAX_PERIPHS); 17362306a36Sopenharmony_ci struct irq_domain *domain; 17462306a36Sopenharmony_ci struct spmi_controller *spmic; 17562306a36Sopenharmony_ci const struct pmic_arb_ver_ops *ver_ops; 17662306a36Sopenharmony_ci u16 *ppid_to_apid; 17762306a36Sopenharmony_ci u16 last_apid; 17862306a36Sopenharmony_ci struct apid_data *apid_data; 17962306a36Sopenharmony_ci int max_periphs; 18062306a36Sopenharmony_ci}; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci/** 18362306a36Sopenharmony_ci * struct pmic_arb_ver_ops - version dependent functionality. 18462306a36Sopenharmony_ci * 18562306a36Sopenharmony_ci * @ver_str: version string. 18662306a36Sopenharmony_ci * @ppid_to_apid: finds the apid for a given ppid. 18762306a36Sopenharmony_ci * @non_data_cmd: on v1 issues an spmi non-data command. 18862306a36Sopenharmony_ci * on v2 no HW support, returns -EOPNOTSUPP. 18962306a36Sopenharmony_ci * @offset: on v1 offset of per-ee channel. 19062306a36Sopenharmony_ci * on v2 offset of per-ee and per-ppid channel. 19162306a36Sopenharmony_ci * @fmt_cmd: formats a GENI/SPMI command. 19262306a36Sopenharmony_ci * @owner_acc_status: on v1 address of PMIC_ARB_SPMI_PIC_OWNERm_ACC_STATUSn 19362306a36Sopenharmony_ci * on v2 address of SPMI_PIC_OWNERm_ACC_STATUSn. 19462306a36Sopenharmony_ci * @acc_enable: on v1 address of PMIC_ARB_SPMI_PIC_ACC_ENABLEn 19562306a36Sopenharmony_ci * on v2 address of SPMI_PIC_ACC_ENABLEn. 19662306a36Sopenharmony_ci * @irq_status: on v1 address of PMIC_ARB_SPMI_PIC_IRQ_STATUSn 19762306a36Sopenharmony_ci * on v2 address of SPMI_PIC_IRQ_STATUSn. 19862306a36Sopenharmony_ci * @irq_clear: on v1 address of PMIC_ARB_SPMI_PIC_IRQ_CLEARn 19962306a36Sopenharmony_ci * on v2 address of SPMI_PIC_IRQ_CLEARn. 20062306a36Sopenharmony_ci * @apid_map_offset: offset of PMIC_ARB_REG_CHNLn 20162306a36Sopenharmony_ci * @apid_owner: on v2 and later address of SPMI_PERIPHn_2OWNER_TABLE_REG 20262306a36Sopenharmony_ci */ 20362306a36Sopenharmony_cistruct pmic_arb_ver_ops { 20462306a36Sopenharmony_ci const char *ver_str; 20562306a36Sopenharmony_ci int (*ppid_to_apid)(struct spmi_pmic_arb *pmic_arb, u16 ppid); 20662306a36Sopenharmony_ci /* spmi commands (read_cmd, write_cmd, cmd) functionality */ 20762306a36Sopenharmony_ci int (*offset)(struct spmi_pmic_arb *pmic_arb, u8 sid, u16 addr, 20862306a36Sopenharmony_ci enum pmic_arb_channel ch_type); 20962306a36Sopenharmony_ci u32 (*fmt_cmd)(u8 opc, u8 sid, u16 addr, u8 bc); 21062306a36Sopenharmony_ci int (*non_data_cmd)(struct spmi_controller *ctrl, u8 opc, u8 sid); 21162306a36Sopenharmony_ci /* Interrupts controller functionality (offset of PIC registers) */ 21262306a36Sopenharmony_ci void __iomem *(*owner_acc_status)(struct spmi_pmic_arb *pmic_arb, u8 m, 21362306a36Sopenharmony_ci u16 n); 21462306a36Sopenharmony_ci void __iomem *(*acc_enable)(struct spmi_pmic_arb *pmic_arb, u16 n); 21562306a36Sopenharmony_ci void __iomem *(*irq_status)(struct spmi_pmic_arb *pmic_arb, u16 n); 21662306a36Sopenharmony_ci void __iomem *(*irq_clear)(struct spmi_pmic_arb *pmic_arb, u16 n); 21762306a36Sopenharmony_ci u32 (*apid_map_offset)(u16 n); 21862306a36Sopenharmony_ci void __iomem *(*apid_owner)(struct spmi_pmic_arb *pmic_arb, u16 n); 21962306a36Sopenharmony_ci}; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_cistatic inline void pmic_arb_base_write(struct spmi_pmic_arb *pmic_arb, 22262306a36Sopenharmony_ci u32 offset, u32 val) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci writel_relaxed(val, pmic_arb->wr_base + offset); 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_cistatic inline void pmic_arb_set_rd_cmd(struct spmi_pmic_arb *pmic_arb, 22862306a36Sopenharmony_ci u32 offset, u32 val) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci writel_relaxed(val, pmic_arb->rd_base + offset); 23162306a36Sopenharmony_ci} 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci/** 23462306a36Sopenharmony_ci * pmic_arb_read_data: reads pmic-arb's register and copy 1..4 bytes to buf 23562306a36Sopenharmony_ci * @bc: byte count -1. range: 0..3 23662306a36Sopenharmony_ci * @reg: register's address 23762306a36Sopenharmony_ci * @buf: output parameter, length must be bc + 1 23862306a36Sopenharmony_ci */ 23962306a36Sopenharmony_cistatic void 24062306a36Sopenharmony_cipmic_arb_read_data(struct spmi_pmic_arb *pmic_arb, u8 *buf, u32 reg, u8 bc) 24162306a36Sopenharmony_ci{ 24262306a36Sopenharmony_ci u32 data = __raw_readl(pmic_arb->rd_base + reg); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci memcpy(buf, &data, (bc & 3) + 1); 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci/** 24862306a36Sopenharmony_ci * pmic_arb_write_data: write 1..4 bytes from buf to pmic-arb's register 24962306a36Sopenharmony_ci * @bc: byte-count -1. range: 0..3. 25062306a36Sopenharmony_ci * @reg: register's address. 25162306a36Sopenharmony_ci * @buf: buffer to write. length must be bc + 1. 25262306a36Sopenharmony_ci */ 25362306a36Sopenharmony_cistatic void pmic_arb_write_data(struct spmi_pmic_arb *pmic_arb, const u8 *buf, 25462306a36Sopenharmony_ci u32 reg, u8 bc) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci u32 data = 0; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci memcpy(&data, buf, (bc & 3) + 1); 25962306a36Sopenharmony_ci __raw_writel(data, pmic_arb->wr_base + reg); 26062306a36Sopenharmony_ci} 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_cistatic int pmic_arb_wait_for_done(struct spmi_controller *ctrl, 26362306a36Sopenharmony_ci void __iomem *base, u8 sid, u16 addr, 26462306a36Sopenharmony_ci enum pmic_arb_channel ch_type) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl); 26762306a36Sopenharmony_ci u32 status = 0; 26862306a36Sopenharmony_ci u32 timeout = PMIC_ARB_TIMEOUT_US; 26962306a36Sopenharmony_ci u32 offset; 27062306a36Sopenharmony_ci int rc; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci rc = pmic_arb->ver_ops->offset(pmic_arb, sid, addr, ch_type); 27362306a36Sopenharmony_ci if (rc < 0) 27462306a36Sopenharmony_ci return rc; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci offset = rc; 27762306a36Sopenharmony_ci offset += PMIC_ARB_STATUS; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci while (timeout--) { 28062306a36Sopenharmony_ci status = readl_relaxed(base + offset); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci if (status & PMIC_ARB_STATUS_DONE) { 28362306a36Sopenharmony_ci if (status & PMIC_ARB_STATUS_DENIED) { 28462306a36Sopenharmony_ci dev_err(&ctrl->dev, "%s: %#x %#x: transaction denied (%#x)\n", 28562306a36Sopenharmony_ci __func__, sid, addr, status); 28662306a36Sopenharmony_ci return -EPERM; 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci if (status & PMIC_ARB_STATUS_FAILURE) { 29062306a36Sopenharmony_ci dev_err(&ctrl->dev, "%s: %#x %#x: transaction failed (%#x)\n", 29162306a36Sopenharmony_ci __func__, sid, addr, status); 29262306a36Sopenharmony_ci WARN_ON(1); 29362306a36Sopenharmony_ci return -EIO; 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci if (status & PMIC_ARB_STATUS_DROPPED) { 29762306a36Sopenharmony_ci dev_err(&ctrl->dev, "%s: %#x %#x: transaction dropped (%#x)\n", 29862306a36Sopenharmony_ci __func__, sid, addr, status); 29962306a36Sopenharmony_ci return -EIO; 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci return 0; 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci udelay(1); 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci dev_err(&ctrl->dev, "%s: %#x %#x: timeout, status %#x\n", 30862306a36Sopenharmony_ci __func__, sid, addr, status); 30962306a36Sopenharmony_ci return -ETIMEDOUT; 31062306a36Sopenharmony_ci} 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_cistatic int 31362306a36Sopenharmony_cipmic_arb_non_data_cmd_v1(struct spmi_controller *ctrl, u8 opc, u8 sid) 31462306a36Sopenharmony_ci{ 31562306a36Sopenharmony_ci struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl); 31662306a36Sopenharmony_ci unsigned long flags; 31762306a36Sopenharmony_ci u32 cmd; 31862306a36Sopenharmony_ci int rc; 31962306a36Sopenharmony_ci u32 offset; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci rc = pmic_arb->ver_ops->offset(pmic_arb, sid, 0, PMIC_ARB_CHANNEL_RW); 32262306a36Sopenharmony_ci if (rc < 0) 32362306a36Sopenharmony_ci return rc; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci offset = rc; 32662306a36Sopenharmony_ci cmd = ((opc | 0x40) << 27) | ((sid & 0xf) << 20); 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci raw_spin_lock_irqsave(&pmic_arb->lock, flags); 32962306a36Sopenharmony_ci pmic_arb_base_write(pmic_arb, offset + PMIC_ARB_CMD, cmd); 33062306a36Sopenharmony_ci rc = pmic_arb_wait_for_done(ctrl, pmic_arb->wr_base, sid, 0, 33162306a36Sopenharmony_ci PMIC_ARB_CHANNEL_RW); 33262306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&pmic_arb->lock, flags); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci return rc; 33562306a36Sopenharmony_ci} 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_cistatic int 33862306a36Sopenharmony_cipmic_arb_non_data_cmd_v2(struct spmi_controller *ctrl, u8 opc, u8 sid) 33962306a36Sopenharmony_ci{ 34062306a36Sopenharmony_ci return -EOPNOTSUPP; 34162306a36Sopenharmony_ci} 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci/* Non-data command */ 34462306a36Sopenharmony_cistatic int pmic_arb_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid) 34562306a36Sopenharmony_ci{ 34662306a36Sopenharmony_ci struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl); 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci dev_dbg(&ctrl->dev, "cmd op:0x%x sid:%d\n", opc, sid); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci /* Check for valid non-data command */ 35162306a36Sopenharmony_ci if (opc < SPMI_CMD_RESET || opc > SPMI_CMD_WAKEUP) 35262306a36Sopenharmony_ci return -EINVAL; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci return pmic_arb->ver_ops->non_data_cmd(ctrl, opc, sid); 35562306a36Sopenharmony_ci} 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_cistatic int pmic_arb_fmt_read_cmd(struct spmi_pmic_arb *pmic_arb, u8 opc, u8 sid, 35862306a36Sopenharmony_ci u16 addr, size_t len, u32 *cmd, u32 *offset) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci u8 bc = len - 1; 36162306a36Sopenharmony_ci int rc; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci rc = pmic_arb->ver_ops->offset(pmic_arb, sid, addr, 36462306a36Sopenharmony_ci PMIC_ARB_CHANNEL_OBS); 36562306a36Sopenharmony_ci if (rc < 0) 36662306a36Sopenharmony_ci return rc; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci *offset = rc; 36962306a36Sopenharmony_ci if (bc >= PMIC_ARB_MAX_TRANS_BYTES) { 37062306a36Sopenharmony_ci dev_err(&pmic_arb->spmic->dev, "pmic-arb supports 1..%d bytes per trans, but:%zu requested", 37162306a36Sopenharmony_ci PMIC_ARB_MAX_TRANS_BYTES, len); 37262306a36Sopenharmony_ci return -EINVAL; 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci /* Check the opcode */ 37662306a36Sopenharmony_ci if (opc >= 0x60 && opc <= 0x7F) 37762306a36Sopenharmony_ci opc = PMIC_ARB_OP_READ; 37862306a36Sopenharmony_ci else if (opc >= 0x20 && opc <= 0x2F) 37962306a36Sopenharmony_ci opc = PMIC_ARB_OP_EXT_READ; 38062306a36Sopenharmony_ci else if (opc >= 0x38 && opc <= 0x3F) 38162306a36Sopenharmony_ci opc = PMIC_ARB_OP_EXT_READL; 38262306a36Sopenharmony_ci else 38362306a36Sopenharmony_ci return -EINVAL; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci *cmd = pmic_arb->ver_ops->fmt_cmd(opc, sid, addr, bc); 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci return 0; 38862306a36Sopenharmony_ci} 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_cistatic int pmic_arb_read_cmd_unlocked(struct spmi_controller *ctrl, u32 cmd, 39162306a36Sopenharmony_ci u32 offset, u8 sid, u16 addr, u8 *buf, 39262306a36Sopenharmony_ci size_t len) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl); 39562306a36Sopenharmony_ci u8 bc = len - 1; 39662306a36Sopenharmony_ci int rc; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci pmic_arb_set_rd_cmd(pmic_arb, offset + PMIC_ARB_CMD, cmd); 39962306a36Sopenharmony_ci rc = pmic_arb_wait_for_done(ctrl, pmic_arb->rd_base, sid, addr, 40062306a36Sopenharmony_ci PMIC_ARB_CHANNEL_OBS); 40162306a36Sopenharmony_ci if (rc) 40262306a36Sopenharmony_ci return rc; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci pmic_arb_read_data(pmic_arb, buf, offset + PMIC_ARB_RDATA0, 40562306a36Sopenharmony_ci min_t(u8, bc, 3)); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci if (bc > 3) 40862306a36Sopenharmony_ci pmic_arb_read_data(pmic_arb, buf + 4, offset + PMIC_ARB_RDATA1, 40962306a36Sopenharmony_ci bc - 4); 41062306a36Sopenharmony_ci return 0; 41162306a36Sopenharmony_ci} 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_cistatic int pmic_arb_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, 41462306a36Sopenharmony_ci u16 addr, u8 *buf, size_t len) 41562306a36Sopenharmony_ci{ 41662306a36Sopenharmony_ci struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl); 41762306a36Sopenharmony_ci unsigned long flags; 41862306a36Sopenharmony_ci u32 cmd, offset; 41962306a36Sopenharmony_ci int rc; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci rc = pmic_arb_fmt_read_cmd(pmic_arb, opc, sid, addr, len, &cmd, 42262306a36Sopenharmony_ci &offset); 42362306a36Sopenharmony_ci if (rc) 42462306a36Sopenharmony_ci return rc; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci raw_spin_lock_irqsave(&pmic_arb->lock, flags); 42762306a36Sopenharmony_ci rc = pmic_arb_read_cmd_unlocked(ctrl, cmd, offset, sid, addr, buf, len); 42862306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&pmic_arb->lock, flags); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci return rc; 43162306a36Sopenharmony_ci} 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_cistatic int pmic_arb_fmt_write_cmd(struct spmi_pmic_arb *pmic_arb, u8 opc, 43462306a36Sopenharmony_ci u8 sid, u16 addr, size_t len, u32 *cmd, 43562306a36Sopenharmony_ci u32 *offset) 43662306a36Sopenharmony_ci{ 43762306a36Sopenharmony_ci u8 bc = len - 1; 43862306a36Sopenharmony_ci int rc; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci rc = pmic_arb->ver_ops->offset(pmic_arb, sid, addr, 44162306a36Sopenharmony_ci PMIC_ARB_CHANNEL_RW); 44262306a36Sopenharmony_ci if (rc < 0) 44362306a36Sopenharmony_ci return rc; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci *offset = rc; 44662306a36Sopenharmony_ci if (bc >= PMIC_ARB_MAX_TRANS_BYTES) { 44762306a36Sopenharmony_ci dev_err(&pmic_arb->spmic->dev, "pmic-arb supports 1..%d bytes per trans, but:%zu requested", 44862306a36Sopenharmony_ci PMIC_ARB_MAX_TRANS_BYTES, len); 44962306a36Sopenharmony_ci return -EINVAL; 45062306a36Sopenharmony_ci } 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci /* Check the opcode */ 45362306a36Sopenharmony_ci if (opc >= 0x40 && opc <= 0x5F) 45462306a36Sopenharmony_ci opc = PMIC_ARB_OP_WRITE; 45562306a36Sopenharmony_ci else if (opc <= 0x0F) 45662306a36Sopenharmony_ci opc = PMIC_ARB_OP_EXT_WRITE; 45762306a36Sopenharmony_ci else if (opc >= 0x30 && opc <= 0x37) 45862306a36Sopenharmony_ci opc = PMIC_ARB_OP_EXT_WRITEL; 45962306a36Sopenharmony_ci else if (opc >= 0x80) 46062306a36Sopenharmony_ci opc = PMIC_ARB_OP_ZERO_WRITE; 46162306a36Sopenharmony_ci else 46262306a36Sopenharmony_ci return -EINVAL; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci *cmd = pmic_arb->ver_ops->fmt_cmd(opc, sid, addr, bc); 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci return 0; 46762306a36Sopenharmony_ci} 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_cistatic int pmic_arb_write_cmd_unlocked(struct spmi_controller *ctrl, u32 cmd, 47062306a36Sopenharmony_ci u32 offset, u8 sid, u16 addr, 47162306a36Sopenharmony_ci const u8 *buf, size_t len) 47262306a36Sopenharmony_ci{ 47362306a36Sopenharmony_ci struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl); 47462306a36Sopenharmony_ci u8 bc = len - 1; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci /* Write data to FIFOs */ 47762306a36Sopenharmony_ci pmic_arb_write_data(pmic_arb, buf, offset + PMIC_ARB_WDATA0, 47862306a36Sopenharmony_ci min_t(u8, bc, 3)); 47962306a36Sopenharmony_ci if (bc > 3) 48062306a36Sopenharmony_ci pmic_arb_write_data(pmic_arb, buf + 4, offset + PMIC_ARB_WDATA1, 48162306a36Sopenharmony_ci bc - 4); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci /* Start the transaction */ 48462306a36Sopenharmony_ci pmic_arb_base_write(pmic_arb, offset + PMIC_ARB_CMD, cmd); 48562306a36Sopenharmony_ci return pmic_arb_wait_for_done(ctrl, pmic_arb->wr_base, sid, addr, 48662306a36Sopenharmony_ci PMIC_ARB_CHANNEL_RW); 48762306a36Sopenharmony_ci} 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_cistatic int pmic_arb_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, 49062306a36Sopenharmony_ci u16 addr, const u8 *buf, size_t len) 49162306a36Sopenharmony_ci{ 49262306a36Sopenharmony_ci struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl); 49362306a36Sopenharmony_ci unsigned long flags; 49462306a36Sopenharmony_ci u32 cmd, offset; 49562306a36Sopenharmony_ci int rc; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci rc = pmic_arb_fmt_write_cmd(pmic_arb, opc, sid, addr, len, &cmd, 49862306a36Sopenharmony_ci &offset); 49962306a36Sopenharmony_ci if (rc) 50062306a36Sopenharmony_ci return rc; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci raw_spin_lock_irqsave(&pmic_arb->lock, flags); 50362306a36Sopenharmony_ci rc = pmic_arb_write_cmd_unlocked(ctrl, cmd, offset, sid, addr, buf, 50462306a36Sopenharmony_ci len); 50562306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&pmic_arb->lock, flags); 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci return rc; 50862306a36Sopenharmony_ci} 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_cistatic int pmic_arb_masked_write(struct spmi_controller *ctrl, u8 sid, u16 addr, 51162306a36Sopenharmony_ci const u8 *buf, const u8 *mask, size_t len) 51262306a36Sopenharmony_ci{ 51362306a36Sopenharmony_ci struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl); 51462306a36Sopenharmony_ci u32 read_cmd, read_offset, write_cmd, write_offset; 51562306a36Sopenharmony_ci u8 temp[PMIC_ARB_MAX_TRANS_BYTES]; 51662306a36Sopenharmony_ci unsigned long flags; 51762306a36Sopenharmony_ci int rc, i; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci rc = pmic_arb_fmt_read_cmd(pmic_arb, SPMI_CMD_EXT_READL, sid, addr, len, 52062306a36Sopenharmony_ci &read_cmd, &read_offset); 52162306a36Sopenharmony_ci if (rc) 52262306a36Sopenharmony_ci return rc; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci rc = pmic_arb_fmt_write_cmd(pmic_arb, SPMI_CMD_EXT_WRITEL, sid, addr, 52562306a36Sopenharmony_ci len, &write_cmd, &write_offset); 52662306a36Sopenharmony_ci if (rc) 52762306a36Sopenharmony_ci return rc; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci raw_spin_lock_irqsave(&pmic_arb->lock, flags); 53062306a36Sopenharmony_ci rc = pmic_arb_read_cmd_unlocked(ctrl, read_cmd, read_offset, sid, addr, 53162306a36Sopenharmony_ci temp, len); 53262306a36Sopenharmony_ci if (rc) 53362306a36Sopenharmony_ci goto done; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci for (i = 0; i < len; i++) 53662306a36Sopenharmony_ci temp[i] = (temp[i] & ~mask[i]) | (buf[i] & mask[i]); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci rc = pmic_arb_write_cmd_unlocked(ctrl, write_cmd, write_offset, sid, 53962306a36Sopenharmony_ci addr, temp, len); 54062306a36Sopenharmony_cidone: 54162306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&pmic_arb->lock, flags); 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci return rc; 54462306a36Sopenharmony_ci} 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_cienum qpnpint_regs { 54762306a36Sopenharmony_ci QPNPINT_REG_RT_STS = 0x10, 54862306a36Sopenharmony_ci QPNPINT_REG_SET_TYPE = 0x11, 54962306a36Sopenharmony_ci QPNPINT_REG_POLARITY_HIGH = 0x12, 55062306a36Sopenharmony_ci QPNPINT_REG_POLARITY_LOW = 0x13, 55162306a36Sopenharmony_ci QPNPINT_REG_LATCHED_CLR = 0x14, 55262306a36Sopenharmony_ci QPNPINT_REG_EN_SET = 0x15, 55362306a36Sopenharmony_ci QPNPINT_REG_EN_CLR = 0x16, 55462306a36Sopenharmony_ci QPNPINT_REG_LATCHED_STS = 0x18, 55562306a36Sopenharmony_ci}; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_cistruct spmi_pmic_arb_qpnpint_type { 55862306a36Sopenharmony_ci u8 type; /* 1 -> edge */ 55962306a36Sopenharmony_ci u8 polarity_high; 56062306a36Sopenharmony_ci u8 polarity_low; 56162306a36Sopenharmony_ci} __packed; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci/* Simplified accessor functions for irqchip callbacks */ 56462306a36Sopenharmony_cistatic void qpnpint_spmi_write(struct irq_data *d, u8 reg, void *buf, 56562306a36Sopenharmony_ci size_t len) 56662306a36Sopenharmony_ci{ 56762306a36Sopenharmony_ci struct spmi_pmic_arb *pmic_arb = irq_data_get_irq_chip_data(d); 56862306a36Sopenharmony_ci u8 sid = hwirq_to_sid(d->hwirq); 56962306a36Sopenharmony_ci u8 per = hwirq_to_per(d->hwirq); 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci if (pmic_arb_write_cmd(pmic_arb->spmic, SPMI_CMD_EXT_WRITEL, sid, 57262306a36Sopenharmony_ci (per << 8) + reg, buf, len)) 57362306a36Sopenharmony_ci dev_err_ratelimited(&pmic_arb->spmic->dev, "failed irqchip transaction on %x\n", 57462306a36Sopenharmony_ci d->irq); 57562306a36Sopenharmony_ci} 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_cistatic void qpnpint_spmi_read(struct irq_data *d, u8 reg, void *buf, size_t len) 57862306a36Sopenharmony_ci{ 57962306a36Sopenharmony_ci struct spmi_pmic_arb *pmic_arb = irq_data_get_irq_chip_data(d); 58062306a36Sopenharmony_ci u8 sid = hwirq_to_sid(d->hwirq); 58162306a36Sopenharmony_ci u8 per = hwirq_to_per(d->hwirq); 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci if (pmic_arb_read_cmd(pmic_arb->spmic, SPMI_CMD_EXT_READL, sid, 58462306a36Sopenharmony_ci (per << 8) + reg, buf, len)) 58562306a36Sopenharmony_ci dev_err_ratelimited(&pmic_arb->spmic->dev, "failed irqchip transaction on %x\n", 58662306a36Sopenharmony_ci d->irq); 58762306a36Sopenharmony_ci} 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_cistatic int qpnpint_spmi_masked_write(struct irq_data *d, u8 reg, 59062306a36Sopenharmony_ci const void *buf, const void *mask, 59162306a36Sopenharmony_ci size_t len) 59262306a36Sopenharmony_ci{ 59362306a36Sopenharmony_ci struct spmi_pmic_arb *pmic_arb = irq_data_get_irq_chip_data(d); 59462306a36Sopenharmony_ci u8 sid = hwirq_to_sid(d->hwirq); 59562306a36Sopenharmony_ci u8 per = hwirq_to_per(d->hwirq); 59662306a36Sopenharmony_ci int rc; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci rc = pmic_arb_masked_write(pmic_arb->spmic, sid, (per << 8) + reg, buf, 59962306a36Sopenharmony_ci mask, len); 60062306a36Sopenharmony_ci if (rc) 60162306a36Sopenharmony_ci dev_err_ratelimited(&pmic_arb->spmic->dev, "failed irqchip transaction on %x rc=%d\n", 60262306a36Sopenharmony_ci d->irq, rc); 60362306a36Sopenharmony_ci return rc; 60462306a36Sopenharmony_ci} 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_cistatic void cleanup_irq(struct spmi_pmic_arb *pmic_arb, u16 apid, int id) 60762306a36Sopenharmony_ci{ 60862306a36Sopenharmony_ci u16 ppid = pmic_arb->apid_data[apid].ppid; 60962306a36Sopenharmony_ci u8 sid = ppid >> 8; 61062306a36Sopenharmony_ci u8 per = ppid & 0xFF; 61162306a36Sopenharmony_ci u8 irq_mask = BIT(id); 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci dev_err_ratelimited(&pmic_arb->spmic->dev, "%s apid=%d sid=0x%x per=0x%x irq=%d\n", 61462306a36Sopenharmony_ci __func__, apid, sid, per, id); 61562306a36Sopenharmony_ci writel_relaxed(irq_mask, pmic_arb->ver_ops->irq_clear(pmic_arb, apid)); 61662306a36Sopenharmony_ci} 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_cistatic int periph_interrupt(struct spmi_pmic_arb *pmic_arb, u16 apid) 61962306a36Sopenharmony_ci{ 62062306a36Sopenharmony_ci unsigned int irq; 62162306a36Sopenharmony_ci u32 status, id; 62262306a36Sopenharmony_ci int handled = 0; 62362306a36Sopenharmony_ci u8 sid = (pmic_arb->apid_data[apid].ppid >> 8) & 0xF; 62462306a36Sopenharmony_ci u8 per = pmic_arb->apid_data[apid].ppid & 0xFF; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci status = readl_relaxed(pmic_arb->ver_ops->irq_status(pmic_arb, apid)); 62762306a36Sopenharmony_ci while (status) { 62862306a36Sopenharmony_ci id = ffs(status) - 1; 62962306a36Sopenharmony_ci status &= ~BIT(id); 63062306a36Sopenharmony_ci irq = irq_find_mapping(pmic_arb->domain, 63162306a36Sopenharmony_ci spec_to_hwirq(sid, per, id, apid)); 63262306a36Sopenharmony_ci if (irq == 0) { 63362306a36Sopenharmony_ci cleanup_irq(pmic_arb, apid, id); 63462306a36Sopenharmony_ci continue; 63562306a36Sopenharmony_ci } 63662306a36Sopenharmony_ci generic_handle_irq(irq); 63762306a36Sopenharmony_ci handled++; 63862306a36Sopenharmony_ci } 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci return handled; 64162306a36Sopenharmony_ci} 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_cistatic void pmic_arb_chained_irq(struct irq_desc *desc) 64462306a36Sopenharmony_ci{ 64562306a36Sopenharmony_ci struct spmi_pmic_arb *pmic_arb = irq_desc_get_handler_data(desc); 64662306a36Sopenharmony_ci const struct pmic_arb_ver_ops *ver_ops = pmic_arb->ver_ops; 64762306a36Sopenharmony_ci struct irq_chip *chip = irq_desc_get_chip(desc); 64862306a36Sopenharmony_ci int first = pmic_arb->min_apid; 64962306a36Sopenharmony_ci int last = pmic_arb->max_apid; 65062306a36Sopenharmony_ci /* 65162306a36Sopenharmony_ci * acc_offset will be non-zero for the secondary SPMI bus instance on 65262306a36Sopenharmony_ci * v7 controllers. 65362306a36Sopenharmony_ci */ 65462306a36Sopenharmony_ci int acc_offset = pmic_arb->base_apid >> 5; 65562306a36Sopenharmony_ci u8 ee = pmic_arb->ee; 65662306a36Sopenharmony_ci u32 status, enable, handled = 0; 65762306a36Sopenharmony_ci int i, id, apid; 65862306a36Sopenharmony_ci /* status based dispatch */ 65962306a36Sopenharmony_ci bool acc_valid = false; 66062306a36Sopenharmony_ci u32 irq_status = 0; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci chained_irq_enter(chip, desc); 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci for (i = first >> 5; i <= last >> 5; ++i) { 66562306a36Sopenharmony_ci status = readl_relaxed(ver_ops->owner_acc_status(pmic_arb, ee, i - acc_offset)); 66662306a36Sopenharmony_ci if (status) 66762306a36Sopenharmony_ci acc_valid = true; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci while (status) { 67062306a36Sopenharmony_ci id = ffs(status) - 1; 67162306a36Sopenharmony_ci status &= ~BIT(id); 67262306a36Sopenharmony_ci apid = id + i * 32; 67362306a36Sopenharmony_ci if (apid < first || apid > last) { 67462306a36Sopenharmony_ci WARN_ONCE(true, "spurious spmi irq received for apid=%d\n", 67562306a36Sopenharmony_ci apid); 67662306a36Sopenharmony_ci continue; 67762306a36Sopenharmony_ci } 67862306a36Sopenharmony_ci enable = readl_relaxed( 67962306a36Sopenharmony_ci ver_ops->acc_enable(pmic_arb, apid)); 68062306a36Sopenharmony_ci if (enable & SPMI_PIC_ACC_ENABLE_BIT) 68162306a36Sopenharmony_ci if (periph_interrupt(pmic_arb, apid) != 0) 68262306a36Sopenharmony_ci handled++; 68362306a36Sopenharmony_ci } 68462306a36Sopenharmony_ci } 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci /* ACC_STATUS is empty but IRQ fired check IRQ_STATUS */ 68762306a36Sopenharmony_ci if (!acc_valid) { 68862306a36Sopenharmony_ci for (i = first; i <= last; i++) { 68962306a36Sopenharmony_ci /* skip if APPS is not irq owner */ 69062306a36Sopenharmony_ci if (pmic_arb->apid_data[i].irq_ee != pmic_arb->ee) 69162306a36Sopenharmony_ci continue; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci irq_status = readl_relaxed( 69462306a36Sopenharmony_ci ver_ops->irq_status(pmic_arb, i)); 69562306a36Sopenharmony_ci if (irq_status) { 69662306a36Sopenharmony_ci enable = readl_relaxed( 69762306a36Sopenharmony_ci ver_ops->acc_enable(pmic_arb, i)); 69862306a36Sopenharmony_ci if (enable & SPMI_PIC_ACC_ENABLE_BIT) { 69962306a36Sopenharmony_ci dev_dbg(&pmic_arb->spmic->dev, 70062306a36Sopenharmony_ci "Dispatching IRQ for apid=%d status=%x\n", 70162306a36Sopenharmony_ci i, irq_status); 70262306a36Sopenharmony_ci if (periph_interrupt(pmic_arb, i) != 0) 70362306a36Sopenharmony_ci handled++; 70462306a36Sopenharmony_ci } 70562306a36Sopenharmony_ci } 70662306a36Sopenharmony_ci } 70762306a36Sopenharmony_ci } 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci if (handled == 0) 71062306a36Sopenharmony_ci handle_bad_irq(desc); 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci chained_irq_exit(chip, desc); 71362306a36Sopenharmony_ci} 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_cistatic void qpnpint_irq_ack(struct irq_data *d) 71662306a36Sopenharmony_ci{ 71762306a36Sopenharmony_ci struct spmi_pmic_arb *pmic_arb = irq_data_get_irq_chip_data(d); 71862306a36Sopenharmony_ci u8 irq = hwirq_to_irq(d->hwirq); 71962306a36Sopenharmony_ci u16 apid = hwirq_to_apid(d->hwirq); 72062306a36Sopenharmony_ci u8 data; 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci writel_relaxed(BIT(irq), pmic_arb->ver_ops->irq_clear(pmic_arb, apid)); 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci data = BIT(irq); 72562306a36Sopenharmony_ci qpnpint_spmi_write(d, QPNPINT_REG_LATCHED_CLR, &data, 1); 72662306a36Sopenharmony_ci} 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_cistatic void qpnpint_irq_mask(struct irq_data *d) 72962306a36Sopenharmony_ci{ 73062306a36Sopenharmony_ci u8 irq = hwirq_to_irq(d->hwirq); 73162306a36Sopenharmony_ci u8 data = BIT(irq); 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci qpnpint_spmi_write(d, QPNPINT_REG_EN_CLR, &data, 1); 73462306a36Sopenharmony_ci} 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_cistatic void qpnpint_irq_unmask(struct irq_data *d) 73762306a36Sopenharmony_ci{ 73862306a36Sopenharmony_ci struct spmi_pmic_arb *pmic_arb = irq_data_get_irq_chip_data(d); 73962306a36Sopenharmony_ci const struct pmic_arb_ver_ops *ver_ops = pmic_arb->ver_ops; 74062306a36Sopenharmony_ci u8 irq = hwirq_to_irq(d->hwirq); 74162306a36Sopenharmony_ci u16 apid = hwirq_to_apid(d->hwirq); 74262306a36Sopenharmony_ci u8 buf[2]; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci writel_relaxed(SPMI_PIC_ACC_ENABLE_BIT, 74562306a36Sopenharmony_ci ver_ops->acc_enable(pmic_arb, apid)); 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci qpnpint_spmi_read(d, QPNPINT_REG_EN_SET, &buf[0], 1); 74862306a36Sopenharmony_ci if (!(buf[0] & BIT(irq))) { 74962306a36Sopenharmony_ci /* 75062306a36Sopenharmony_ci * Since the interrupt is currently disabled, write to both the 75162306a36Sopenharmony_ci * LATCHED_CLR and EN_SET registers so that a spurious interrupt 75262306a36Sopenharmony_ci * cannot be triggered when the interrupt is enabled 75362306a36Sopenharmony_ci */ 75462306a36Sopenharmony_ci buf[0] = BIT(irq); 75562306a36Sopenharmony_ci buf[1] = BIT(irq); 75662306a36Sopenharmony_ci qpnpint_spmi_write(d, QPNPINT_REG_LATCHED_CLR, &buf, 2); 75762306a36Sopenharmony_ci } 75862306a36Sopenharmony_ci} 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_cistatic int qpnpint_irq_set_type(struct irq_data *d, unsigned int flow_type) 76162306a36Sopenharmony_ci{ 76262306a36Sopenharmony_ci struct spmi_pmic_arb_qpnpint_type type = {0}; 76362306a36Sopenharmony_ci struct spmi_pmic_arb_qpnpint_type mask; 76462306a36Sopenharmony_ci irq_flow_handler_t flow_handler; 76562306a36Sopenharmony_ci u8 irq_bit = BIT(hwirq_to_irq(d->hwirq)); 76662306a36Sopenharmony_ci int rc; 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) { 76962306a36Sopenharmony_ci type.type = irq_bit; 77062306a36Sopenharmony_ci if (flow_type & IRQF_TRIGGER_RISING) 77162306a36Sopenharmony_ci type.polarity_high = irq_bit; 77262306a36Sopenharmony_ci if (flow_type & IRQF_TRIGGER_FALLING) 77362306a36Sopenharmony_ci type.polarity_low = irq_bit; 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci flow_handler = handle_edge_irq; 77662306a36Sopenharmony_ci } else { 77762306a36Sopenharmony_ci if ((flow_type & (IRQF_TRIGGER_HIGH)) && 77862306a36Sopenharmony_ci (flow_type & (IRQF_TRIGGER_LOW))) 77962306a36Sopenharmony_ci return -EINVAL; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci if (flow_type & IRQF_TRIGGER_HIGH) 78262306a36Sopenharmony_ci type.polarity_high = irq_bit; 78362306a36Sopenharmony_ci else 78462306a36Sopenharmony_ci type.polarity_low = irq_bit; 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci flow_handler = handle_level_irq; 78762306a36Sopenharmony_ci } 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci mask.type = irq_bit; 79062306a36Sopenharmony_ci mask.polarity_high = irq_bit; 79162306a36Sopenharmony_ci mask.polarity_low = irq_bit; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci rc = qpnpint_spmi_masked_write(d, QPNPINT_REG_SET_TYPE, &type, &mask, 79462306a36Sopenharmony_ci sizeof(type)); 79562306a36Sopenharmony_ci irq_set_handler_locked(d, flow_handler); 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci return rc; 79862306a36Sopenharmony_ci} 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_cistatic int qpnpint_irq_set_wake(struct irq_data *d, unsigned int on) 80162306a36Sopenharmony_ci{ 80262306a36Sopenharmony_ci struct spmi_pmic_arb *pmic_arb = irq_data_get_irq_chip_data(d); 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci return irq_set_irq_wake(pmic_arb->irq, on); 80562306a36Sopenharmony_ci} 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_cistatic int qpnpint_get_irqchip_state(struct irq_data *d, 80862306a36Sopenharmony_ci enum irqchip_irq_state which, 80962306a36Sopenharmony_ci bool *state) 81062306a36Sopenharmony_ci{ 81162306a36Sopenharmony_ci u8 irq = hwirq_to_irq(d->hwirq); 81262306a36Sopenharmony_ci u8 status = 0; 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci if (which != IRQCHIP_STATE_LINE_LEVEL) 81562306a36Sopenharmony_ci return -EINVAL; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci qpnpint_spmi_read(d, QPNPINT_REG_RT_STS, &status, 1); 81862306a36Sopenharmony_ci *state = !!(status & BIT(irq)); 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci return 0; 82162306a36Sopenharmony_ci} 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_cistatic int qpnpint_irq_domain_activate(struct irq_domain *domain, 82462306a36Sopenharmony_ci struct irq_data *d, bool reserve) 82562306a36Sopenharmony_ci{ 82662306a36Sopenharmony_ci struct spmi_pmic_arb *pmic_arb = irq_data_get_irq_chip_data(d); 82762306a36Sopenharmony_ci u16 periph = hwirq_to_per(d->hwirq); 82862306a36Sopenharmony_ci u16 apid = hwirq_to_apid(d->hwirq); 82962306a36Sopenharmony_ci u16 sid = hwirq_to_sid(d->hwirq); 83062306a36Sopenharmony_ci u16 irq = hwirq_to_irq(d->hwirq); 83162306a36Sopenharmony_ci u8 buf; 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci if (pmic_arb->apid_data[apid].irq_ee != pmic_arb->ee) { 83462306a36Sopenharmony_ci dev_err(&pmic_arb->spmic->dev, "failed to xlate sid = %#x, periph = %#x, irq = %u: ee=%u but owner=%u\n", 83562306a36Sopenharmony_ci sid, periph, irq, pmic_arb->ee, 83662306a36Sopenharmony_ci pmic_arb->apid_data[apid].irq_ee); 83762306a36Sopenharmony_ci return -ENODEV; 83862306a36Sopenharmony_ci } 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci buf = BIT(irq); 84162306a36Sopenharmony_ci qpnpint_spmi_write(d, QPNPINT_REG_EN_CLR, &buf, 1); 84262306a36Sopenharmony_ci qpnpint_spmi_write(d, QPNPINT_REG_LATCHED_CLR, &buf, 1); 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci return 0; 84562306a36Sopenharmony_ci} 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_cistatic struct irq_chip pmic_arb_irqchip = { 84862306a36Sopenharmony_ci .name = "pmic_arb", 84962306a36Sopenharmony_ci .irq_ack = qpnpint_irq_ack, 85062306a36Sopenharmony_ci .irq_mask = qpnpint_irq_mask, 85162306a36Sopenharmony_ci .irq_unmask = qpnpint_irq_unmask, 85262306a36Sopenharmony_ci .irq_set_type = qpnpint_irq_set_type, 85362306a36Sopenharmony_ci .irq_set_wake = qpnpint_irq_set_wake, 85462306a36Sopenharmony_ci .irq_get_irqchip_state = qpnpint_get_irqchip_state, 85562306a36Sopenharmony_ci .flags = IRQCHIP_MASK_ON_SUSPEND, 85662306a36Sopenharmony_ci}; 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_cistatic int qpnpint_irq_domain_translate(struct irq_domain *d, 85962306a36Sopenharmony_ci struct irq_fwspec *fwspec, 86062306a36Sopenharmony_ci unsigned long *out_hwirq, 86162306a36Sopenharmony_ci unsigned int *out_type) 86262306a36Sopenharmony_ci{ 86362306a36Sopenharmony_ci struct spmi_pmic_arb *pmic_arb = d->host_data; 86462306a36Sopenharmony_ci u32 *intspec = fwspec->param; 86562306a36Sopenharmony_ci u16 apid, ppid; 86662306a36Sopenharmony_ci int rc; 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci dev_dbg(&pmic_arb->spmic->dev, "intspec[0] 0x%1x intspec[1] 0x%02x intspec[2] 0x%02x\n", 86962306a36Sopenharmony_ci intspec[0], intspec[1], intspec[2]); 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci if (irq_domain_get_of_node(d) != pmic_arb->spmic->dev.of_node) 87262306a36Sopenharmony_ci return -EINVAL; 87362306a36Sopenharmony_ci if (fwspec->param_count != 4) 87462306a36Sopenharmony_ci return -EINVAL; 87562306a36Sopenharmony_ci if (intspec[0] > 0xF || intspec[1] > 0xFF || intspec[2] > 0x7) 87662306a36Sopenharmony_ci return -EINVAL; 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci ppid = intspec[0] << 8 | intspec[1]; 87962306a36Sopenharmony_ci rc = pmic_arb->ver_ops->ppid_to_apid(pmic_arb, ppid); 88062306a36Sopenharmony_ci if (rc < 0) { 88162306a36Sopenharmony_ci dev_err(&pmic_arb->spmic->dev, "failed to xlate sid = %#x, periph = %#x, irq = %u rc = %d\n", 88262306a36Sopenharmony_ci intspec[0], intspec[1], intspec[2], rc); 88362306a36Sopenharmony_ci return rc; 88462306a36Sopenharmony_ci } 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci apid = rc; 88762306a36Sopenharmony_ci /* Keep track of {max,min}_apid for bounding search during interrupt */ 88862306a36Sopenharmony_ci if (apid > pmic_arb->max_apid) 88962306a36Sopenharmony_ci pmic_arb->max_apid = apid; 89062306a36Sopenharmony_ci if (apid < pmic_arb->min_apid) 89162306a36Sopenharmony_ci pmic_arb->min_apid = apid; 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci *out_hwirq = spec_to_hwirq(intspec[0], intspec[1], intspec[2], apid); 89462306a36Sopenharmony_ci *out_type = intspec[3] & IRQ_TYPE_SENSE_MASK; 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci dev_dbg(&pmic_arb->spmic->dev, "out_hwirq = %lu\n", *out_hwirq); 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci return 0; 89962306a36Sopenharmony_ci} 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_cistatic struct lock_class_key qpnpint_irq_lock_class, qpnpint_irq_request_class; 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_cistatic void qpnpint_irq_domain_map(struct spmi_pmic_arb *pmic_arb, 90462306a36Sopenharmony_ci struct irq_domain *domain, unsigned int virq, 90562306a36Sopenharmony_ci irq_hw_number_t hwirq, unsigned int type) 90662306a36Sopenharmony_ci{ 90762306a36Sopenharmony_ci irq_flow_handler_t handler; 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci dev_dbg(&pmic_arb->spmic->dev, "virq = %u, hwirq = %lu, type = %u\n", 91062306a36Sopenharmony_ci virq, hwirq, type); 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci if (type & IRQ_TYPE_EDGE_BOTH) 91362306a36Sopenharmony_ci handler = handle_edge_irq; 91462306a36Sopenharmony_ci else 91562306a36Sopenharmony_ci handler = handle_level_irq; 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci irq_set_lockdep_class(virq, &qpnpint_irq_lock_class, 91962306a36Sopenharmony_ci &qpnpint_irq_request_class); 92062306a36Sopenharmony_ci irq_domain_set_info(domain, virq, hwirq, &pmic_arb_irqchip, pmic_arb, 92162306a36Sopenharmony_ci handler, NULL, NULL); 92262306a36Sopenharmony_ci} 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_cistatic int qpnpint_irq_domain_alloc(struct irq_domain *domain, 92562306a36Sopenharmony_ci unsigned int virq, unsigned int nr_irqs, 92662306a36Sopenharmony_ci void *data) 92762306a36Sopenharmony_ci{ 92862306a36Sopenharmony_ci struct spmi_pmic_arb *pmic_arb = domain->host_data; 92962306a36Sopenharmony_ci struct irq_fwspec *fwspec = data; 93062306a36Sopenharmony_ci irq_hw_number_t hwirq; 93162306a36Sopenharmony_ci unsigned int type; 93262306a36Sopenharmony_ci int ret, i; 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci ret = qpnpint_irq_domain_translate(domain, fwspec, &hwirq, &type); 93562306a36Sopenharmony_ci if (ret) 93662306a36Sopenharmony_ci return ret; 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci for (i = 0; i < nr_irqs; i++) 93962306a36Sopenharmony_ci qpnpint_irq_domain_map(pmic_arb, domain, virq + i, hwirq + i, 94062306a36Sopenharmony_ci type); 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci return 0; 94362306a36Sopenharmony_ci} 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_cistatic int pmic_arb_ppid_to_apid_v1(struct spmi_pmic_arb *pmic_arb, u16 ppid) 94662306a36Sopenharmony_ci{ 94762306a36Sopenharmony_ci u32 *mapping_table = pmic_arb->mapping_table; 94862306a36Sopenharmony_ci int index = 0, i; 94962306a36Sopenharmony_ci u16 apid_valid; 95062306a36Sopenharmony_ci u16 apid; 95162306a36Sopenharmony_ci u32 data; 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci apid_valid = pmic_arb->ppid_to_apid[ppid]; 95462306a36Sopenharmony_ci if (apid_valid & PMIC_ARB_APID_VALID) { 95562306a36Sopenharmony_ci apid = apid_valid & ~PMIC_ARB_APID_VALID; 95662306a36Sopenharmony_ci return apid; 95762306a36Sopenharmony_ci } 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci for (i = 0; i < SPMI_MAPPING_TABLE_TREE_DEPTH; ++i) { 96062306a36Sopenharmony_ci if (!test_and_set_bit(index, pmic_arb->mapping_table_valid)) 96162306a36Sopenharmony_ci mapping_table[index] = readl_relaxed(pmic_arb->cnfg + 96262306a36Sopenharmony_ci SPMI_MAPPING_TABLE_REG(index)); 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci data = mapping_table[index]; 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci if (ppid & BIT(SPMI_MAPPING_BIT_INDEX(data))) { 96762306a36Sopenharmony_ci if (SPMI_MAPPING_BIT_IS_1_FLAG(data)) { 96862306a36Sopenharmony_ci index = SPMI_MAPPING_BIT_IS_1_RESULT(data); 96962306a36Sopenharmony_ci } else { 97062306a36Sopenharmony_ci apid = SPMI_MAPPING_BIT_IS_1_RESULT(data); 97162306a36Sopenharmony_ci pmic_arb->ppid_to_apid[ppid] 97262306a36Sopenharmony_ci = apid | PMIC_ARB_APID_VALID; 97362306a36Sopenharmony_ci pmic_arb->apid_data[apid].ppid = ppid; 97462306a36Sopenharmony_ci return apid; 97562306a36Sopenharmony_ci } 97662306a36Sopenharmony_ci } else { 97762306a36Sopenharmony_ci if (SPMI_MAPPING_BIT_IS_0_FLAG(data)) { 97862306a36Sopenharmony_ci index = SPMI_MAPPING_BIT_IS_0_RESULT(data); 97962306a36Sopenharmony_ci } else { 98062306a36Sopenharmony_ci apid = SPMI_MAPPING_BIT_IS_0_RESULT(data); 98162306a36Sopenharmony_ci pmic_arb->ppid_to_apid[ppid] 98262306a36Sopenharmony_ci = apid | PMIC_ARB_APID_VALID; 98362306a36Sopenharmony_ci pmic_arb->apid_data[apid].ppid = ppid; 98462306a36Sopenharmony_ci return apid; 98562306a36Sopenharmony_ci } 98662306a36Sopenharmony_ci } 98762306a36Sopenharmony_ci } 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci return -ENODEV; 99062306a36Sopenharmony_ci} 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci/* v1 offset per ee */ 99362306a36Sopenharmony_cistatic int pmic_arb_offset_v1(struct spmi_pmic_arb *pmic_arb, u8 sid, u16 addr, 99462306a36Sopenharmony_ci enum pmic_arb_channel ch_type) 99562306a36Sopenharmony_ci{ 99662306a36Sopenharmony_ci return 0x800 + 0x80 * pmic_arb->channel; 99762306a36Sopenharmony_ci} 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_cistatic u16 pmic_arb_find_apid(struct spmi_pmic_arb *pmic_arb, u16 ppid) 100062306a36Sopenharmony_ci{ 100162306a36Sopenharmony_ci struct apid_data *apidd = &pmic_arb->apid_data[pmic_arb->last_apid]; 100262306a36Sopenharmony_ci u32 regval, offset; 100362306a36Sopenharmony_ci u16 id, apid; 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci for (apid = pmic_arb->last_apid; ; apid++, apidd++) { 100662306a36Sopenharmony_ci offset = pmic_arb->ver_ops->apid_map_offset(apid); 100762306a36Sopenharmony_ci if (offset >= pmic_arb->core_size) 100862306a36Sopenharmony_ci break; 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci regval = readl_relaxed(pmic_arb->ver_ops->apid_owner(pmic_arb, 101162306a36Sopenharmony_ci apid)); 101262306a36Sopenharmony_ci apidd->irq_ee = SPMI_OWNERSHIP_PERIPH2OWNER(regval); 101362306a36Sopenharmony_ci apidd->write_ee = apidd->irq_ee; 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci regval = readl_relaxed(pmic_arb->core + offset); 101662306a36Sopenharmony_ci if (!regval) 101762306a36Sopenharmony_ci continue; 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci id = (regval >> 8) & PMIC_ARB_PPID_MASK; 102062306a36Sopenharmony_ci pmic_arb->ppid_to_apid[id] = apid | PMIC_ARB_APID_VALID; 102162306a36Sopenharmony_ci apidd->ppid = id; 102262306a36Sopenharmony_ci if (id == ppid) { 102362306a36Sopenharmony_ci apid |= PMIC_ARB_APID_VALID; 102462306a36Sopenharmony_ci break; 102562306a36Sopenharmony_ci } 102662306a36Sopenharmony_ci } 102762306a36Sopenharmony_ci pmic_arb->last_apid = apid & ~PMIC_ARB_APID_VALID; 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci return apid; 103062306a36Sopenharmony_ci} 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_cistatic int pmic_arb_ppid_to_apid_v2(struct spmi_pmic_arb *pmic_arb, u16 ppid) 103362306a36Sopenharmony_ci{ 103462306a36Sopenharmony_ci u16 apid_valid; 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci apid_valid = pmic_arb->ppid_to_apid[ppid]; 103762306a36Sopenharmony_ci if (!(apid_valid & PMIC_ARB_APID_VALID)) 103862306a36Sopenharmony_ci apid_valid = pmic_arb_find_apid(pmic_arb, ppid); 103962306a36Sopenharmony_ci if (!(apid_valid & PMIC_ARB_APID_VALID)) 104062306a36Sopenharmony_ci return -ENODEV; 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci return apid_valid & ~PMIC_ARB_APID_VALID; 104362306a36Sopenharmony_ci} 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_cistatic int pmic_arb_read_apid_map_v5(struct spmi_pmic_arb *pmic_arb) 104662306a36Sopenharmony_ci{ 104762306a36Sopenharmony_ci struct apid_data *apidd; 104862306a36Sopenharmony_ci struct apid_data *prev_apidd; 104962306a36Sopenharmony_ci u16 i, apid, ppid, apid_max; 105062306a36Sopenharmony_ci bool valid, is_irq_ee; 105162306a36Sopenharmony_ci u32 regval, offset; 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci /* 105462306a36Sopenharmony_ci * In order to allow multiple EEs to write to a single PPID in arbiter 105562306a36Sopenharmony_ci * version 5 and 7, there is more than one APID mapped to each PPID. 105662306a36Sopenharmony_ci * The owner field for each of these mappings specifies the EE which is 105762306a36Sopenharmony_ci * allowed to write to the APID. The owner of the last (highest) APID 105862306a36Sopenharmony_ci * which has the IRQ owner bit set for a given PPID will receive 105962306a36Sopenharmony_ci * interrupts from the PPID. 106062306a36Sopenharmony_ci * 106162306a36Sopenharmony_ci * In arbiter version 7, the APID numbering space is divided between 106262306a36Sopenharmony_ci * the primary bus (0) and secondary bus (1) such that: 106362306a36Sopenharmony_ci * APID = 0 to N-1 are assigned to the primary bus 106462306a36Sopenharmony_ci * APID = N to N+M-1 are assigned to the secondary bus 106562306a36Sopenharmony_ci * where N = number of APIDs supported by the primary bus and 106662306a36Sopenharmony_ci * M = number of APIDs supported by the secondary bus 106762306a36Sopenharmony_ci */ 106862306a36Sopenharmony_ci apidd = &pmic_arb->apid_data[pmic_arb->base_apid]; 106962306a36Sopenharmony_ci apid_max = pmic_arb->base_apid + pmic_arb->apid_count; 107062306a36Sopenharmony_ci for (i = pmic_arb->base_apid; i < apid_max; i++, apidd++) { 107162306a36Sopenharmony_ci offset = pmic_arb->ver_ops->apid_map_offset(i); 107262306a36Sopenharmony_ci if (offset >= pmic_arb->core_size) 107362306a36Sopenharmony_ci break; 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci regval = readl_relaxed(pmic_arb->core + offset); 107662306a36Sopenharmony_ci if (!regval) 107762306a36Sopenharmony_ci continue; 107862306a36Sopenharmony_ci ppid = (regval >> 8) & PMIC_ARB_PPID_MASK; 107962306a36Sopenharmony_ci is_irq_ee = PMIC_ARB_CHAN_IS_IRQ_OWNER(regval); 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci regval = readl_relaxed(pmic_arb->ver_ops->apid_owner(pmic_arb, 108262306a36Sopenharmony_ci i)); 108362306a36Sopenharmony_ci apidd->write_ee = SPMI_OWNERSHIP_PERIPH2OWNER(regval); 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci apidd->irq_ee = is_irq_ee ? apidd->write_ee : INVALID_EE; 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci valid = pmic_arb->ppid_to_apid[ppid] & PMIC_ARB_APID_VALID; 108862306a36Sopenharmony_ci apid = pmic_arb->ppid_to_apid[ppid] & ~PMIC_ARB_APID_VALID; 108962306a36Sopenharmony_ci prev_apidd = &pmic_arb->apid_data[apid]; 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci if (!valid || apidd->write_ee == pmic_arb->ee) { 109262306a36Sopenharmony_ci /* First PPID mapping or one for this EE */ 109362306a36Sopenharmony_ci pmic_arb->ppid_to_apid[ppid] = i | PMIC_ARB_APID_VALID; 109462306a36Sopenharmony_ci } else if (valid && is_irq_ee && 109562306a36Sopenharmony_ci prev_apidd->write_ee == pmic_arb->ee) { 109662306a36Sopenharmony_ci /* 109762306a36Sopenharmony_ci * Duplicate PPID mapping after the one for this EE; 109862306a36Sopenharmony_ci * override the irq owner 109962306a36Sopenharmony_ci */ 110062306a36Sopenharmony_ci prev_apidd->irq_ee = apidd->irq_ee; 110162306a36Sopenharmony_ci } 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci apidd->ppid = ppid; 110462306a36Sopenharmony_ci pmic_arb->last_apid = i; 110562306a36Sopenharmony_ci } 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci /* Dump the mapping table for debug purposes. */ 110862306a36Sopenharmony_ci dev_dbg(&pmic_arb->spmic->dev, "PPID APID Write-EE IRQ-EE\n"); 110962306a36Sopenharmony_ci for (ppid = 0; ppid < PMIC_ARB_MAX_PPID; ppid++) { 111062306a36Sopenharmony_ci apid = pmic_arb->ppid_to_apid[ppid]; 111162306a36Sopenharmony_ci if (apid & PMIC_ARB_APID_VALID) { 111262306a36Sopenharmony_ci apid &= ~PMIC_ARB_APID_VALID; 111362306a36Sopenharmony_ci apidd = &pmic_arb->apid_data[apid]; 111462306a36Sopenharmony_ci dev_dbg(&pmic_arb->spmic->dev, "%#03X %3u %2u %2u\n", 111562306a36Sopenharmony_ci ppid, apid, apidd->write_ee, apidd->irq_ee); 111662306a36Sopenharmony_ci } 111762306a36Sopenharmony_ci } 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci return 0; 112062306a36Sopenharmony_ci} 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_cistatic int pmic_arb_ppid_to_apid_v5(struct spmi_pmic_arb *pmic_arb, u16 ppid) 112362306a36Sopenharmony_ci{ 112462306a36Sopenharmony_ci if (!(pmic_arb->ppid_to_apid[ppid] & PMIC_ARB_APID_VALID)) 112562306a36Sopenharmony_ci return -ENODEV; 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci return pmic_arb->ppid_to_apid[ppid] & ~PMIC_ARB_APID_VALID; 112862306a36Sopenharmony_ci} 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ci/* v2 offset per ppid and per ee */ 113162306a36Sopenharmony_cistatic int pmic_arb_offset_v2(struct spmi_pmic_arb *pmic_arb, u8 sid, u16 addr, 113262306a36Sopenharmony_ci enum pmic_arb_channel ch_type) 113362306a36Sopenharmony_ci{ 113462306a36Sopenharmony_ci u16 apid; 113562306a36Sopenharmony_ci u16 ppid; 113662306a36Sopenharmony_ci int rc; 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci ppid = sid << 8 | ((addr >> 8) & 0xFF); 113962306a36Sopenharmony_ci rc = pmic_arb_ppid_to_apid_v2(pmic_arb, ppid); 114062306a36Sopenharmony_ci if (rc < 0) 114162306a36Sopenharmony_ci return rc; 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci apid = rc; 114462306a36Sopenharmony_ci return 0x1000 * pmic_arb->ee + 0x8000 * apid; 114562306a36Sopenharmony_ci} 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ci/* 114862306a36Sopenharmony_ci * v5 offset per ee and per apid for observer channels and per apid for 114962306a36Sopenharmony_ci * read/write channels. 115062306a36Sopenharmony_ci */ 115162306a36Sopenharmony_cistatic int pmic_arb_offset_v5(struct spmi_pmic_arb *pmic_arb, u8 sid, u16 addr, 115262306a36Sopenharmony_ci enum pmic_arb_channel ch_type) 115362306a36Sopenharmony_ci{ 115462306a36Sopenharmony_ci u16 apid; 115562306a36Sopenharmony_ci int rc; 115662306a36Sopenharmony_ci u32 offset = 0; 115762306a36Sopenharmony_ci u16 ppid = (sid << 8) | (addr >> 8); 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci rc = pmic_arb_ppid_to_apid_v5(pmic_arb, ppid); 116062306a36Sopenharmony_ci if (rc < 0) 116162306a36Sopenharmony_ci return rc; 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci apid = rc; 116462306a36Sopenharmony_ci switch (ch_type) { 116562306a36Sopenharmony_ci case PMIC_ARB_CHANNEL_OBS: 116662306a36Sopenharmony_ci offset = 0x10000 * pmic_arb->ee + 0x80 * apid; 116762306a36Sopenharmony_ci break; 116862306a36Sopenharmony_ci case PMIC_ARB_CHANNEL_RW: 116962306a36Sopenharmony_ci if (pmic_arb->apid_data[apid].write_ee != pmic_arb->ee) { 117062306a36Sopenharmony_ci dev_err(&pmic_arb->spmic->dev, "disallowed SPMI write to sid=%u, addr=0x%04X\n", 117162306a36Sopenharmony_ci sid, addr); 117262306a36Sopenharmony_ci return -EPERM; 117362306a36Sopenharmony_ci } 117462306a36Sopenharmony_ci offset = 0x10000 * apid; 117562306a36Sopenharmony_ci break; 117662306a36Sopenharmony_ci } 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci return offset; 117962306a36Sopenharmony_ci} 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci/* 118262306a36Sopenharmony_ci * v7 offset per ee and per apid for observer channels and per apid for 118362306a36Sopenharmony_ci * read/write channels. 118462306a36Sopenharmony_ci */ 118562306a36Sopenharmony_cistatic int pmic_arb_offset_v7(struct spmi_pmic_arb *pmic_arb, u8 sid, u16 addr, 118662306a36Sopenharmony_ci enum pmic_arb_channel ch_type) 118762306a36Sopenharmony_ci{ 118862306a36Sopenharmony_ci u16 apid; 118962306a36Sopenharmony_ci int rc; 119062306a36Sopenharmony_ci u32 offset = 0; 119162306a36Sopenharmony_ci u16 ppid = (sid << 8) | (addr >> 8); 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci rc = pmic_arb->ver_ops->ppid_to_apid(pmic_arb, ppid); 119462306a36Sopenharmony_ci if (rc < 0) 119562306a36Sopenharmony_ci return rc; 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci apid = rc; 119862306a36Sopenharmony_ci switch (ch_type) { 119962306a36Sopenharmony_ci case PMIC_ARB_CHANNEL_OBS: 120062306a36Sopenharmony_ci offset = 0x8000 * pmic_arb->ee + 0x20 * apid; 120162306a36Sopenharmony_ci break; 120262306a36Sopenharmony_ci case PMIC_ARB_CHANNEL_RW: 120362306a36Sopenharmony_ci if (pmic_arb->apid_data[apid].write_ee != pmic_arb->ee) { 120462306a36Sopenharmony_ci dev_err(&pmic_arb->spmic->dev, "disallowed SPMI write to sid=%u, addr=0x%04X\n", 120562306a36Sopenharmony_ci sid, addr); 120662306a36Sopenharmony_ci return -EPERM; 120762306a36Sopenharmony_ci } 120862306a36Sopenharmony_ci offset = 0x1000 * apid; 120962306a36Sopenharmony_ci break; 121062306a36Sopenharmony_ci } 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci return offset; 121362306a36Sopenharmony_ci} 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_cistatic u32 pmic_arb_fmt_cmd_v1(u8 opc, u8 sid, u16 addr, u8 bc) 121662306a36Sopenharmony_ci{ 121762306a36Sopenharmony_ci return (opc << 27) | ((sid & 0xf) << 20) | (addr << 4) | (bc & 0x7); 121862306a36Sopenharmony_ci} 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_cistatic u32 pmic_arb_fmt_cmd_v2(u8 opc, u8 sid, u16 addr, u8 bc) 122162306a36Sopenharmony_ci{ 122262306a36Sopenharmony_ci return (opc << 27) | ((addr & 0xff) << 4) | (bc & 0x7); 122362306a36Sopenharmony_ci} 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_cistatic void __iomem * 122662306a36Sopenharmony_cipmic_arb_owner_acc_status_v1(struct spmi_pmic_arb *pmic_arb, u8 m, u16 n) 122762306a36Sopenharmony_ci{ 122862306a36Sopenharmony_ci return pmic_arb->intr + 0x20 * m + 0x4 * n; 122962306a36Sopenharmony_ci} 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_cistatic void __iomem * 123262306a36Sopenharmony_cipmic_arb_owner_acc_status_v2(struct spmi_pmic_arb *pmic_arb, u8 m, u16 n) 123362306a36Sopenharmony_ci{ 123462306a36Sopenharmony_ci return pmic_arb->intr + 0x100000 + 0x1000 * m + 0x4 * n; 123562306a36Sopenharmony_ci} 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_cistatic void __iomem * 123862306a36Sopenharmony_cipmic_arb_owner_acc_status_v3(struct spmi_pmic_arb *pmic_arb, u8 m, u16 n) 123962306a36Sopenharmony_ci{ 124062306a36Sopenharmony_ci return pmic_arb->intr + 0x200000 + 0x1000 * m + 0x4 * n; 124162306a36Sopenharmony_ci} 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_cistatic void __iomem * 124462306a36Sopenharmony_cipmic_arb_owner_acc_status_v5(struct spmi_pmic_arb *pmic_arb, u8 m, u16 n) 124562306a36Sopenharmony_ci{ 124662306a36Sopenharmony_ci return pmic_arb->intr + 0x10000 * m + 0x4 * n; 124762306a36Sopenharmony_ci} 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_cistatic void __iomem * 125062306a36Sopenharmony_cipmic_arb_owner_acc_status_v7(struct spmi_pmic_arb *pmic_arb, u8 m, u16 n) 125162306a36Sopenharmony_ci{ 125262306a36Sopenharmony_ci return pmic_arb->intr + 0x1000 * m + 0x4 * n; 125362306a36Sopenharmony_ci} 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_cistatic void __iomem * 125662306a36Sopenharmony_cipmic_arb_acc_enable_v1(struct spmi_pmic_arb *pmic_arb, u16 n) 125762306a36Sopenharmony_ci{ 125862306a36Sopenharmony_ci return pmic_arb->intr + 0x200 + 0x4 * n; 125962306a36Sopenharmony_ci} 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_cistatic void __iomem * 126262306a36Sopenharmony_cipmic_arb_acc_enable_v2(struct spmi_pmic_arb *pmic_arb, u16 n) 126362306a36Sopenharmony_ci{ 126462306a36Sopenharmony_ci return pmic_arb->intr + 0x1000 * n; 126562306a36Sopenharmony_ci} 126662306a36Sopenharmony_ci 126762306a36Sopenharmony_cistatic void __iomem * 126862306a36Sopenharmony_cipmic_arb_acc_enable_v5(struct spmi_pmic_arb *pmic_arb, u16 n) 126962306a36Sopenharmony_ci{ 127062306a36Sopenharmony_ci return pmic_arb->wr_base + 0x100 + 0x10000 * n; 127162306a36Sopenharmony_ci} 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_cistatic void __iomem * 127462306a36Sopenharmony_cipmic_arb_acc_enable_v7(struct spmi_pmic_arb *pmic_arb, u16 n) 127562306a36Sopenharmony_ci{ 127662306a36Sopenharmony_ci return pmic_arb->wr_base + 0x100 + 0x1000 * n; 127762306a36Sopenharmony_ci} 127862306a36Sopenharmony_ci 127962306a36Sopenharmony_cistatic void __iomem * 128062306a36Sopenharmony_cipmic_arb_irq_status_v1(struct spmi_pmic_arb *pmic_arb, u16 n) 128162306a36Sopenharmony_ci{ 128262306a36Sopenharmony_ci return pmic_arb->intr + 0x600 + 0x4 * n; 128362306a36Sopenharmony_ci} 128462306a36Sopenharmony_ci 128562306a36Sopenharmony_cistatic void __iomem * 128662306a36Sopenharmony_cipmic_arb_irq_status_v2(struct spmi_pmic_arb *pmic_arb, u16 n) 128762306a36Sopenharmony_ci{ 128862306a36Sopenharmony_ci return pmic_arb->intr + 0x4 + 0x1000 * n; 128962306a36Sopenharmony_ci} 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_cistatic void __iomem * 129262306a36Sopenharmony_cipmic_arb_irq_status_v5(struct spmi_pmic_arb *pmic_arb, u16 n) 129362306a36Sopenharmony_ci{ 129462306a36Sopenharmony_ci return pmic_arb->wr_base + 0x104 + 0x10000 * n; 129562306a36Sopenharmony_ci} 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_cistatic void __iomem * 129862306a36Sopenharmony_cipmic_arb_irq_status_v7(struct spmi_pmic_arb *pmic_arb, u16 n) 129962306a36Sopenharmony_ci{ 130062306a36Sopenharmony_ci return pmic_arb->wr_base + 0x104 + 0x1000 * n; 130162306a36Sopenharmony_ci} 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_cistatic void __iomem * 130462306a36Sopenharmony_cipmic_arb_irq_clear_v1(struct spmi_pmic_arb *pmic_arb, u16 n) 130562306a36Sopenharmony_ci{ 130662306a36Sopenharmony_ci return pmic_arb->intr + 0xA00 + 0x4 * n; 130762306a36Sopenharmony_ci} 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_cistatic void __iomem * 131062306a36Sopenharmony_cipmic_arb_irq_clear_v2(struct spmi_pmic_arb *pmic_arb, u16 n) 131162306a36Sopenharmony_ci{ 131262306a36Sopenharmony_ci return pmic_arb->intr + 0x8 + 0x1000 * n; 131362306a36Sopenharmony_ci} 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_cistatic void __iomem * 131662306a36Sopenharmony_cipmic_arb_irq_clear_v5(struct spmi_pmic_arb *pmic_arb, u16 n) 131762306a36Sopenharmony_ci{ 131862306a36Sopenharmony_ci return pmic_arb->wr_base + 0x108 + 0x10000 * n; 131962306a36Sopenharmony_ci} 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_cistatic void __iomem * 132262306a36Sopenharmony_cipmic_arb_irq_clear_v7(struct spmi_pmic_arb *pmic_arb, u16 n) 132362306a36Sopenharmony_ci{ 132462306a36Sopenharmony_ci return pmic_arb->wr_base + 0x108 + 0x1000 * n; 132562306a36Sopenharmony_ci} 132662306a36Sopenharmony_ci 132762306a36Sopenharmony_cistatic u32 pmic_arb_apid_map_offset_v2(u16 n) 132862306a36Sopenharmony_ci{ 132962306a36Sopenharmony_ci return 0x800 + 0x4 * n; 133062306a36Sopenharmony_ci} 133162306a36Sopenharmony_ci 133262306a36Sopenharmony_cistatic u32 pmic_arb_apid_map_offset_v5(u16 n) 133362306a36Sopenharmony_ci{ 133462306a36Sopenharmony_ci return 0x900 + 0x4 * n; 133562306a36Sopenharmony_ci} 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_cistatic u32 pmic_arb_apid_map_offset_v7(u16 n) 133862306a36Sopenharmony_ci{ 133962306a36Sopenharmony_ci return 0x2000 + 0x4 * n; 134062306a36Sopenharmony_ci} 134162306a36Sopenharmony_ci 134262306a36Sopenharmony_cistatic void __iomem * 134362306a36Sopenharmony_cipmic_arb_apid_owner_v2(struct spmi_pmic_arb *pmic_arb, u16 n) 134462306a36Sopenharmony_ci{ 134562306a36Sopenharmony_ci return pmic_arb->cnfg + 0x700 + 0x4 * n; 134662306a36Sopenharmony_ci} 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci/* 134962306a36Sopenharmony_ci * For arbiter version 7, APID ownership table registers have independent 135062306a36Sopenharmony_ci * numbering space for each SPMI bus instance, so each is indexed starting from 135162306a36Sopenharmony_ci * 0. 135262306a36Sopenharmony_ci */ 135362306a36Sopenharmony_cistatic void __iomem * 135462306a36Sopenharmony_cipmic_arb_apid_owner_v7(struct spmi_pmic_arb *pmic_arb, u16 n) 135562306a36Sopenharmony_ci{ 135662306a36Sopenharmony_ci return pmic_arb->cnfg + 0x4 * (n - pmic_arb->base_apid); 135762306a36Sopenharmony_ci} 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_cistatic const struct pmic_arb_ver_ops pmic_arb_v1 = { 136062306a36Sopenharmony_ci .ver_str = "v1", 136162306a36Sopenharmony_ci .ppid_to_apid = pmic_arb_ppid_to_apid_v1, 136262306a36Sopenharmony_ci .non_data_cmd = pmic_arb_non_data_cmd_v1, 136362306a36Sopenharmony_ci .offset = pmic_arb_offset_v1, 136462306a36Sopenharmony_ci .fmt_cmd = pmic_arb_fmt_cmd_v1, 136562306a36Sopenharmony_ci .owner_acc_status = pmic_arb_owner_acc_status_v1, 136662306a36Sopenharmony_ci .acc_enable = pmic_arb_acc_enable_v1, 136762306a36Sopenharmony_ci .irq_status = pmic_arb_irq_status_v1, 136862306a36Sopenharmony_ci .irq_clear = pmic_arb_irq_clear_v1, 136962306a36Sopenharmony_ci .apid_map_offset = pmic_arb_apid_map_offset_v2, 137062306a36Sopenharmony_ci .apid_owner = pmic_arb_apid_owner_v2, 137162306a36Sopenharmony_ci}; 137262306a36Sopenharmony_ci 137362306a36Sopenharmony_cistatic const struct pmic_arb_ver_ops pmic_arb_v2 = { 137462306a36Sopenharmony_ci .ver_str = "v2", 137562306a36Sopenharmony_ci .ppid_to_apid = pmic_arb_ppid_to_apid_v2, 137662306a36Sopenharmony_ci .non_data_cmd = pmic_arb_non_data_cmd_v2, 137762306a36Sopenharmony_ci .offset = pmic_arb_offset_v2, 137862306a36Sopenharmony_ci .fmt_cmd = pmic_arb_fmt_cmd_v2, 137962306a36Sopenharmony_ci .owner_acc_status = pmic_arb_owner_acc_status_v2, 138062306a36Sopenharmony_ci .acc_enable = pmic_arb_acc_enable_v2, 138162306a36Sopenharmony_ci .irq_status = pmic_arb_irq_status_v2, 138262306a36Sopenharmony_ci .irq_clear = pmic_arb_irq_clear_v2, 138362306a36Sopenharmony_ci .apid_map_offset = pmic_arb_apid_map_offset_v2, 138462306a36Sopenharmony_ci .apid_owner = pmic_arb_apid_owner_v2, 138562306a36Sopenharmony_ci}; 138662306a36Sopenharmony_ci 138762306a36Sopenharmony_cistatic const struct pmic_arb_ver_ops pmic_arb_v3 = { 138862306a36Sopenharmony_ci .ver_str = "v3", 138962306a36Sopenharmony_ci .ppid_to_apid = pmic_arb_ppid_to_apid_v2, 139062306a36Sopenharmony_ci .non_data_cmd = pmic_arb_non_data_cmd_v2, 139162306a36Sopenharmony_ci .offset = pmic_arb_offset_v2, 139262306a36Sopenharmony_ci .fmt_cmd = pmic_arb_fmt_cmd_v2, 139362306a36Sopenharmony_ci .owner_acc_status = pmic_arb_owner_acc_status_v3, 139462306a36Sopenharmony_ci .acc_enable = pmic_arb_acc_enable_v2, 139562306a36Sopenharmony_ci .irq_status = pmic_arb_irq_status_v2, 139662306a36Sopenharmony_ci .irq_clear = pmic_arb_irq_clear_v2, 139762306a36Sopenharmony_ci .apid_map_offset = pmic_arb_apid_map_offset_v2, 139862306a36Sopenharmony_ci .apid_owner = pmic_arb_apid_owner_v2, 139962306a36Sopenharmony_ci}; 140062306a36Sopenharmony_ci 140162306a36Sopenharmony_cistatic const struct pmic_arb_ver_ops pmic_arb_v5 = { 140262306a36Sopenharmony_ci .ver_str = "v5", 140362306a36Sopenharmony_ci .ppid_to_apid = pmic_arb_ppid_to_apid_v5, 140462306a36Sopenharmony_ci .non_data_cmd = pmic_arb_non_data_cmd_v2, 140562306a36Sopenharmony_ci .offset = pmic_arb_offset_v5, 140662306a36Sopenharmony_ci .fmt_cmd = pmic_arb_fmt_cmd_v2, 140762306a36Sopenharmony_ci .owner_acc_status = pmic_arb_owner_acc_status_v5, 140862306a36Sopenharmony_ci .acc_enable = pmic_arb_acc_enable_v5, 140962306a36Sopenharmony_ci .irq_status = pmic_arb_irq_status_v5, 141062306a36Sopenharmony_ci .irq_clear = pmic_arb_irq_clear_v5, 141162306a36Sopenharmony_ci .apid_map_offset = pmic_arb_apid_map_offset_v5, 141262306a36Sopenharmony_ci .apid_owner = pmic_arb_apid_owner_v2, 141362306a36Sopenharmony_ci}; 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_cistatic const struct pmic_arb_ver_ops pmic_arb_v7 = { 141662306a36Sopenharmony_ci .ver_str = "v7", 141762306a36Sopenharmony_ci .ppid_to_apid = pmic_arb_ppid_to_apid_v5, 141862306a36Sopenharmony_ci .non_data_cmd = pmic_arb_non_data_cmd_v2, 141962306a36Sopenharmony_ci .offset = pmic_arb_offset_v7, 142062306a36Sopenharmony_ci .fmt_cmd = pmic_arb_fmt_cmd_v2, 142162306a36Sopenharmony_ci .owner_acc_status = pmic_arb_owner_acc_status_v7, 142262306a36Sopenharmony_ci .acc_enable = pmic_arb_acc_enable_v7, 142362306a36Sopenharmony_ci .irq_status = pmic_arb_irq_status_v7, 142462306a36Sopenharmony_ci .irq_clear = pmic_arb_irq_clear_v7, 142562306a36Sopenharmony_ci .apid_map_offset = pmic_arb_apid_map_offset_v7, 142662306a36Sopenharmony_ci .apid_owner = pmic_arb_apid_owner_v7, 142762306a36Sopenharmony_ci}; 142862306a36Sopenharmony_ci 142962306a36Sopenharmony_cistatic const struct irq_domain_ops pmic_arb_irq_domain_ops = { 143062306a36Sopenharmony_ci .activate = qpnpint_irq_domain_activate, 143162306a36Sopenharmony_ci .alloc = qpnpint_irq_domain_alloc, 143262306a36Sopenharmony_ci .free = irq_domain_free_irqs_common, 143362306a36Sopenharmony_ci .translate = qpnpint_irq_domain_translate, 143462306a36Sopenharmony_ci}; 143562306a36Sopenharmony_ci 143662306a36Sopenharmony_cistatic int spmi_pmic_arb_probe(struct platform_device *pdev) 143762306a36Sopenharmony_ci{ 143862306a36Sopenharmony_ci struct spmi_pmic_arb *pmic_arb; 143962306a36Sopenharmony_ci struct spmi_controller *ctrl; 144062306a36Sopenharmony_ci struct resource *res; 144162306a36Sopenharmony_ci void __iomem *core; 144262306a36Sopenharmony_ci u32 *mapping_table; 144362306a36Sopenharmony_ci u32 channel, ee, hw_ver; 144462306a36Sopenharmony_ci int err; 144562306a36Sopenharmony_ci 144662306a36Sopenharmony_ci ctrl = spmi_controller_alloc(&pdev->dev, sizeof(*pmic_arb)); 144762306a36Sopenharmony_ci if (!ctrl) 144862306a36Sopenharmony_ci return -ENOMEM; 144962306a36Sopenharmony_ci 145062306a36Sopenharmony_ci pmic_arb = spmi_controller_get_drvdata(ctrl); 145162306a36Sopenharmony_ci pmic_arb->spmic = ctrl; 145262306a36Sopenharmony_ci 145362306a36Sopenharmony_ci /* 145462306a36Sopenharmony_ci * Please don't replace this with devm_platform_ioremap_resource() or 145562306a36Sopenharmony_ci * devm_ioremap_resource(). These both result in a call to 145662306a36Sopenharmony_ci * devm_request_mem_region() which prevents multiple mappings of this 145762306a36Sopenharmony_ci * register address range. SoCs with PMIC arbiter v7 may define two 145862306a36Sopenharmony_ci * arbiter devices, for the two physical SPMI interfaces, which share 145962306a36Sopenharmony_ci * some register address ranges (i.e. "core", "obsrvr", and "chnls"). 146062306a36Sopenharmony_ci * Ensure that both devices probe successfully by calling devm_ioremap() 146162306a36Sopenharmony_ci * which does not result in a devm_request_mem_region() call. 146262306a36Sopenharmony_ci */ 146362306a36Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "core"); 146462306a36Sopenharmony_ci core = devm_ioremap(&ctrl->dev, res->start, resource_size(res)); 146562306a36Sopenharmony_ci if (IS_ERR(core)) { 146662306a36Sopenharmony_ci err = PTR_ERR(core); 146762306a36Sopenharmony_ci goto err_put_ctrl; 146862306a36Sopenharmony_ci } 146962306a36Sopenharmony_ci 147062306a36Sopenharmony_ci pmic_arb->core_size = resource_size(res); 147162306a36Sopenharmony_ci 147262306a36Sopenharmony_ci pmic_arb->ppid_to_apid = devm_kcalloc(&ctrl->dev, PMIC_ARB_MAX_PPID, 147362306a36Sopenharmony_ci sizeof(*pmic_arb->ppid_to_apid), 147462306a36Sopenharmony_ci GFP_KERNEL); 147562306a36Sopenharmony_ci if (!pmic_arb->ppid_to_apid) { 147662306a36Sopenharmony_ci err = -ENOMEM; 147762306a36Sopenharmony_ci goto err_put_ctrl; 147862306a36Sopenharmony_ci } 147962306a36Sopenharmony_ci 148062306a36Sopenharmony_ci hw_ver = readl_relaxed(core + PMIC_ARB_VERSION); 148162306a36Sopenharmony_ci 148262306a36Sopenharmony_ci if (hw_ver < PMIC_ARB_VERSION_V2_MIN) { 148362306a36Sopenharmony_ci pmic_arb->ver_ops = &pmic_arb_v1; 148462306a36Sopenharmony_ci pmic_arb->wr_base = core; 148562306a36Sopenharmony_ci pmic_arb->rd_base = core; 148662306a36Sopenharmony_ci } else { 148762306a36Sopenharmony_ci pmic_arb->core = core; 148862306a36Sopenharmony_ci 148962306a36Sopenharmony_ci if (hw_ver < PMIC_ARB_VERSION_V3_MIN) 149062306a36Sopenharmony_ci pmic_arb->ver_ops = &pmic_arb_v2; 149162306a36Sopenharmony_ci else if (hw_ver < PMIC_ARB_VERSION_V5_MIN) 149262306a36Sopenharmony_ci pmic_arb->ver_ops = &pmic_arb_v3; 149362306a36Sopenharmony_ci else if (hw_ver < PMIC_ARB_VERSION_V7_MIN) 149462306a36Sopenharmony_ci pmic_arb->ver_ops = &pmic_arb_v5; 149562306a36Sopenharmony_ci else 149662306a36Sopenharmony_ci pmic_arb->ver_ops = &pmic_arb_v7; 149762306a36Sopenharmony_ci 149862306a36Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, 149962306a36Sopenharmony_ci "obsrvr"); 150062306a36Sopenharmony_ci pmic_arb->rd_base = devm_ioremap(&ctrl->dev, res->start, 150162306a36Sopenharmony_ci resource_size(res)); 150262306a36Sopenharmony_ci if (IS_ERR(pmic_arb->rd_base)) { 150362306a36Sopenharmony_ci err = PTR_ERR(pmic_arb->rd_base); 150462306a36Sopenharmony_ci goto err_put_ctrl; 150562306a36Sopenharmony_ci } 150662306a36Sopenharmony_ci 150762306a36Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, 150862306a36Sopenharmony_ci "chnls"); 150962306a36Sopenharmony_ci pmic_arb->wr_base = devm_ioremap(&ctrl->dev, res->start, 151062306a36Sopenharmony_ci resource_size(res)); 151162306a36Sopenharmony_ci if (IS_ERR(pmic_arb->wr_base)) { 151262306a36Sopenharmony_ci err = PTR_ERR(pmic_arb->wr_base); 151362306a36Sopenharmony_ci goto err_put_ctrl; 151462306a36Sopenharmony_ci } 151562306a36Sopenharmony_ci } 151662306a36Sopenharmony_ci 151762306a36Sopenharmony_ci pmic_arb->max_periphs = PMIC_ARB_MAX_PERIPHS; 151862306a36Sopenharmony_ci 151962306a36Sopenharmony_ci if (hw_ver >= PMIC_ARB_VERSION_V7_MIN) { 152062306a36Sopenharmony_ci pmic_arb->max_periphs = PMIC_ARB_MAX_PERIPHS_V7; 152162306a36Sopenharmony_ci /* Optional property for v7: */ 152262306a36Sopenharmony_ci of_property_read_u32(pdev->dev.of_node, "qcom,bus-id", 152362306a36Sopenharmony_ci &pmic_arb->bus_instance); 152462306a36Sopenharmony_ci if (pmic_arb->bus_instance > 1) { 152562306a36Sopenharmony_ci err = -EINVAL; 152662306a36Sopenharmony_ci dev_err(&pdev->dev, "invalid bus instance (%u) specified\n", 152762306a36Sopenharmony_ci pmic_arb->bus_instance); 152862306a36Sopenharmony_ci goto err_put_ctrl; 152962306a36Sopenharmony_ci } 153062306a36Sopenharmony_ci 153162306a36Sopenharmony_ci if (pmic_arb->bus_instance == 0) { 153262306a36Sopenharmony_ci pmic_arb->base_apid = 0; 153362306a36Sopenharmony_ci pmic_arb->apid_count = 153462306a36Sopenharmony_ci readl_relaxed(core + PMIC_ARB_FEATURES) & 153562306a36Sopenharmony_ci PMIC_ARB_FEATURES_PERIPH_MASK; 153662306a36Sopenharmony_ci } else { 153762306a36Sopenharmony_ci pmic_arb->base_apid = 153862306a36Sopenharmony_ci readl_relaxed(core + PMIC_ARB_FEATURES) & 153962306a36Sopenharmony_ci PMIC_ARB_FEATURES_PERIPH_MASK; 154062306a36Sopenharmony_ci pmic_arb->apid_count = 154162306a36Sopenharmony_ci readl_relaxed(core + PMIC_ARB_FEATURES1) & 154262306a36Sopenharmony_ci PMIC_ARB_FEATURES_PERIPH_MASK; 154362306a36Sopenharmony_ci } 154462306a36Sopenharmony_ci 154562306a36Sopenharmony_ci if (pmic_arb->base_apid + pmic_arb->apid_count > pmic_arb->max_periphs) { 154662306a36Sopenharmony_ci err = -EINVAL; 154762306a36Sopenharmony_ci dev_err(&pdev->dev, "Unsupported APID count %d detected\n", 154862306a36Sopenharmony_ci pmic_arb->base_apid + pmic_arb->apid_count); 154962306a36Sopenharmony_ci goto err_put_ctrl; 155062306a36Sopenharmony_ci } 155162306a36Sopenharmony_ci } else if (hw_ver >= PMIC_ARB_VERSION_V5_MIN) { 155262306a36Sopenharmony_ci pmic_arb->base_apid = 0; 155362306a36Sopenharmony_ci pmic_arb->apid_count = readl_relaxed(core + PMIC_ARB_FEATURES) & 155462306a36Sopenharmony_ci PMIC_ARB_FEATURES_PERIPH_MASK; 155562306a36Sopenharmony_ci 155662306a36Sopenharmony_ci if (pmic_arb->apid_count > pmic_arb->max_periphs) { 155762306a36Sopenharmony_ci err = -EINVAL; 155862306a36Sopenharmony_ci dev_err(&pdev->dev, "Unsupported APID count %d detected\n", 155962306a36Sopenharmony_ci pmic_arb->apid_count); 156062306a36Sopenharmony_ci goto err_put_ctrl; 156162306a36Sopenharmony_ci } 156262306a36Sopenharmony_ci } 156362306a36Sopenharmony_ci 156462306a36Sopenharmony_ci pmic_arb->apid_data = devm_kcalloc(&ctrl->dev, pmic_arb->max_periphs, 156562306a36Sopenharmony_ci sizeof(*pmic_arb->apid_data), 156662306a36Sopenharmony_ci GFP_KERNEL); 156762306a36Sopenharmony_ci if (!pmic_arb->apid_data) { 156862306a36Sopenharmony_ci err = -ENOMEM; 156962306a36Sopenharmony_ci goto err_put_ctrl; 157062306a36Sopenharmony_ci } 157162306a36Sopenharmony_ci 157262306a36Sopenharmony_ci dev_info(&ctrl->dev, "PMIC arbiter version %s (0x%x)\n", 157362306a36Sopenharmony_ci pmic_arb->ver_ops->ver_str, hw_ver); 157462306a36Sopenharmony_ci 157562306a36Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "intr"); 157662306a36Sopenharmony_ci pmic_arb->intr = devm_ioremap_resource(&ctrl->dev, res); 157762306a36Sopenharmony_ci if (IS_ERR(pmic_arb->intr)) { 157862306a36Sopenharmony_ci err = PTR_ERR(pmic_arb->intr); 157962306a36Sopenharmony_ci goto err_put_ctrl; 158062306a36Sopenharmony_ci } 158162306a36Sopenharmony_ci 158262306a36Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cnfg"); 158362306a36Sopenharmony_ci pmic_arb->cnfg = devm_ioremap_resource(&ctrl->dev, res); 158462306a36Sopenharmony_ci if (IS_ERR(pmic_arb->cnfg)) { 158562306a36Sopenharmony_ci err = PTR_ERR(pmic_arb->cnfg); 158662306a36Sopenharmony_ci goto err_put_ctrl; 158762306a36Sopenharmony_ci } 158862306a36Sopenharmony_ci 158962306a36Sopenharmony_ci pmic_arb->irq = platform_get_irq_byname(pdev, "periph_irq"); 159062306a36Sopenharmony_ci if (pmic_arb->irq < 0) { 159162306a36Sopenharmony_ci err = pmic_arb->irq; 159262306a36Sopenharmony_ci goto err_put_ctrl; 159362306a36Sopenharmony_ci } 159462306a36Sopenharmony_ci 159562306a36Sopenharmony_ci err = of_property_read_u32(pdev->dev.of_node, "qcom,channel", &channel); 159662306a36Sopenharmony_ci if (err) { 159762306a36Sopenharmony_ci dev_err(&pdev->dev, "channel unspecified.\n"); 159862306a36Sopenharmony_ci goto err_put_ctrl; 159962306a36Sopenharmony_ci } 160062306a36Sopenharmony_ci 160162306a36Sopenharmony_ci if (channel > 5) { 160262306a36Sopenharmony_ci dev_err(&pdev->dev, "invalid channel (%u) specified.\n", 160362306a36Sopenharmony_ci channel); 160462306a36Sopenharmony_ci err = -EINVAL; 160562306a36Sopenharmony_ci goto err_put_ctrl; 160662306a36Sopenharmony_ci } 160762306a36Sopenharmony_ci 160862306a36Sopenharmony_ci pmic_arb->channel = channel; 160962306a36Sopenharmony_ci 161062306a36Sopenharmony_ci err = of_property_read_u32(pdev->dev.of_node, "qcom,ee", &ee); 161162306a36Sopenharmony_ci if (err) { 161262306a36Sopenharmony_ci dev_err(&pdev->dev, "EE unspecified.\n"); 161362306a36Sopenharmony_ci goto err_put_ctrl; 161462306a36Sopenharmony_ci } 161562306a36Sopenharmony_ci 161662306a36Sopenharmony_ci if (ee > 5) { 161762306a36Sopenharmony_ci dev_err(&pdev->dev, "invalid EE (%u) specified\n", ee); 161862306a36Sopenharmony_ci err = -EINVAL; 161962306a36Sopenharmony_ci goto err_put_ctrl; 162062306a36Sopenharmony_ci } 162162306a36Sopenharmony_ci 162262306a36Sopenharmony_ci pmic_arb->ee = ee; 162362306a36Sopenharmony_ci mapping_table = devm_kcalloc(&ctrl->dev, pmic_arb->max_periphs, 162462306a36Sopenharmony_ci sizeof(*mapping_table), GFP_KERNEL); 162562306a36Sopenharmony_ci if (!mapping_table) { 162662306a36Sopenharmony_ci err = -ENOMEM; 162762306a36Sopenharmony_ci goto err_put_ctrl; 162862306a36Sopenharmony_ci } 162962306a36Sopenharmony_ci 163062306a36Sopenharmony_ci pmic_arb->mapping_table = mapping_table; 163162306a36Sopenharmony_ci /* Initialize max_apid/min_apid to the opposite bounds, during 163262306a36Sopenharmony_ci * the irq domain translation, we are sure to update these */ 163362306a36Sopenharmony_ci pmic_arb->max_apid = 0; 163462306a36Sopenharmony_ci pmic_arb->min_apid = pmic_arb->max_periphs - 1; 163562306a36Sopenharmony_ci 163662306a36Sopenharmony_ci platform_set_drvdata(pdev, ctrl); 163762306a36Sopenharmony_ci raw_spin_lock_init(&pmic_arb->lock); 163862306a36Sopenharmony_ci 163962306a36Sopenharmony_ci ctrl->cmd = pmic_arb_cmd; 164062306a36Sopenharmony_ci ctrl->read_cmd = pmic_arb_read_cmd; 164162306a36Sopenharmony_ci ctrl->write_cmd = pmic_arb_write_cmd; 164262306a36Sopenharmony_ci 164362306a36Sopenharmony_ci if (hw_ver >= PMIC_ARB_VERSION_V5_MIN) { 164462306a36Sopenharmony_ci err = pmic_arb_read_apid_map_v5(pmic_arb); 164562306a36Sopenharmony_ci if (err) { 164662306a36Sopenharmony_ci dev_err(&pdev->dev, "could not read APID->PPID mapping table, rc= %d\n", 164762306a36Sopenharmony_ci err); 164862306a36Sopenharmony_ci goto err_put_ctrl; 164962306a36Sopenharmony_ci } 165062306a36Sopenharmony_ci } 165162306a36Sopenharmony_ci 165262306a36Sopenharmony_ci dev_dbg(&pdev->dev, "adding irq domain\n"); 165362306a36Sopenharmony_ci pmic_arb->domain = irq_domain_add_tree(pdev->dev.of_node, 165462306a36Sopenharmony_ci &pmic_arb_irq_domain_ops, pmic_arb); 165562306a36Sopenharmony_ci if (!pmic_arb->domain) { 165662306a36Sopenharmony_ci dev_err(&pdev->dev, "unable to create irq_domain\n"); 165762306a36Sopenharmony_ci err = -ENOMEM; 165862306a36Sopenharmony_ci goto err_put_ctrl; 165962306a36Sopenharmony_ci } 166062306a36Sopenharmony_ci 166162306a36Sopenharmony_ci irq_set_chained_handler_and_data(pmic_arb->irq, pmic_arb_chained_irq, 166262306a36Sopenharmony_ci pmic_arb); 166362306a36Sopenharmony_ci err = spmi_controller_add(ctrl); 166462306a36Sopenharmony_ci if (err) 166562306a36Sopenharmony_ci goto err_domain_remove; 166662306a36Sopenharmony_ci 166762306a36Sopenharmony_ci return 0; 166862306a36Sopenharmony_ci 166962306a36Sopenharmony_cierr_domain_remove: 167062306a36Sopenharmony_ci irq_set_chained_handler_and_data(pmic_arb->irq, NULL, NULL); 167162306a36Sopenharmony_ci irq_domain_remove(pmic_arb->domain); 167262306a36Sopenharmony_cierr_put_ctrl: 167362306a36Sopenharmony_ci spmi_controller_put(ctrl); 167462306a36Sopenharmony_ci return err; 167562306a36Sopenharmony_ci} 167662306a36Sopenharmony_ci 167762306a36Sopenharmony_cistatic void spmi_pmic_arb_remove(struct platform_device *pdev) 167862306a36Sopenharmony_ci{ 167962306a36Sopenharmony_ci struct spmi_controller *ctrl = platform_get_drvdata(pdev); 168062306a36Sopenharmony_ci struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl); 168162306a36Sopenharmony_ci spmi_controller_remove(ctrl); 168262306a36Sopenharmony_ci irq_set_chained_handler_and_data(pmic_arb->irq, NULL, NULL); 168362306a36Sopenharmony_ci irq_domain_remove(pmic_arb->domain); 168462306a36Sopenharmony_ci spmi_controller_put(ctrl); 168562306a36Sopenharmony_ci} 168662306a36Sopenharmony_ci 168762306a36Sopenharmony_cistatic const struct of_device_id spmi_pmic_arb_match_table[] = { 168862306a36Sopenharmony_ci { .compatible = "qcom,spmi-pmic-arb", }, 168962306a36Sopenharmony_ci {}, 169062306a36Sopenharmony_ci}; 169162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, spmi_pmic_arb_match_table); 169262306a36Sopenharmony_ci 169362306a36Sopenharmony_cistatic struct platform_driver spmi_pmic_arb_driver = { 169462306a36Sopenharmony_ci .probe = spmi_pmic_arb_probe, 169562306a36Sopenharmony_ci .remove_new = spmi_pmic_arb_remove, 169662306a36Sopenharmony_ci .driver = { 169762306a36Sopenharmony_ci .name = "spmi_pmic_arb", 169862306a36Sopenharmony_ci .of_match_table = spmi_pmic_arb_match_table, 169962306a36Sopenharmony_ci }, 170062306a36Sopenharmony_ci}; 170162306a36Sopenharmony_cimodule_platform_driver(spmi_pmic_arb_driver); 170262306a36Sopenharmony_ci 170362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 170462306a36Sopenharmony_ciMODULE_ALIAS("platform:spmi_pmic_arb"); 1705