162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2011, Code Aurora Forum. All rights reserved. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#define pr_fmt(fmt) "%s: " fmt, __func__ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/kernel.h> 962306a36Sopenharmony_ci#include <linux/interrupt.h> 1062306a36Sopenharmony_ci#include <linux/irqchip/chained_irq.h> 1162306a36Sopenharmony_ci#include <linux/irq.h> 1262306a36Sopenharmony_ci#include <linux/irqdomain.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/platform_device.h> 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci#include <linux/err.h> 1762306a36Sopenharmony_ci#include <linux/ssbi.h> 1862306a36Sopenharmony_ci#include <linux/regmap.h> 1962306a36Sopenharmony_ci#include <linux/of_platform.h> 2062306a36Sopenharmony_ci#include <linux/mfd/core.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define SSBI_REG_ADDR_IRQ_BASE 0x1BB 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define SSBI_REG_ADDR_IRQ_ROOT (SSBI_REG_ADDR_IRQ_BASE + 0) 2562306a36Sopenharmony_ci#define SSBI_REG_ADDR_IRQ_M_STATUS1 (SSBI_REG_ADDR_IRQ_BASE + 1) 2662306a36Sopenharmony_ci#define SSBI_REG_ADDR_IRQ_M_STATUS2 (SSBI_REG_ADDR_IRQ_BASE + 2) 2762306a36Sopenharmony_ci#define SSBI_REG_ADDR_IRQ_M_STATUS3 (SSBI_REG_ADDR_IRQ_BASE + 3) 2862306a36Sopenharmony_ci#define SSBI_REG_ADDR_IRQ_M_STATUS4 (SSBI_REG_ADDR_IRQ_BASE + 4) 2962306a36Sopenharmony_ci#define SSBI_REG_ADDR_IRQ_BLK_SEL (SSBI_REG_ADDR_IRQ_BASE + 5) 3062306a36Sopenharmony_ci#define SSBI_REG_ADDR_IRQ_IT_STATUS (SSBI_REG_ADDR_IRQ_BASE + 6) 3162306a36Sopenharmony_ci#define SSBI_REG_ADDR_IRQ_CONFIG (SSBI_REG_ADDR_IRQ_BASE + 7) 3262306a36Sopenharmony_ci#define SSBI_REG_ADDR_IRQ_RT_STATUS (SSBI_REG_ADDR_IRQ_BASE + 8) 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#define PM8821_SSBI_REG_ADDR_IRQ_BASE 0x100 3562306a36Sopenharmony_ci#define PM8821_SSBI_REG_ADDR_IRQ_MASTER0 (PM8821_SSBI_REG_ADDR_IRQ_BASE + 0x30) 3662306a36Sopenharmony_ci#define PM8821_SSBI_REG_ADDR_IRQ_MASTER1 (PM8821_SSBI_REG_ADDR_IRQ_BASE + 0xb0) 3762306a36Sopenharmony_ci#define PM8821_SSBI_REG(m, b, offset) \ 3862306a36Sopenharmony_ci ((m == 0) ? \ 3962306a36Sopenharmony_ci (PM8821_SSBI_REG_ADDR_IRQ_MASTER0 + b + offset) : \ 4062306a36Sopenharmony_ci (PM8821_SSBI_REG_ADDR_IRQ_MASTER1 + b + offset)) 4162306a36Sopenharmony_ci#define PM8821_SSBI_ADDR_IRQ_ROOT(m, b) PM8821_SSBI_REG(m, b, 0x0) 4262306a36Sopenharmony_ci#define PM8821_SSBI_ADDR_IRQ_CLEAR(m, b) PM8821_SSBI_REG(m, b, 0x01) 4362306a36Sopenharmony_ci#define PM8821_SSBI_ADDR_IRQ_MASK(m, b) PM8821_SSBI_REG(m, b, 0x08) 4462306a36Sopenharmony_ci#define PM8821_SSBI_ADDR_IRQ_RT_STATUS(m, b) PM8821_SSBI_REG(m, b, 0x0f) 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci#define PM8821_BLOCKS_PER_MASTER 7 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci#define PM_IRQF_LVL_SEL 0x01 /* level select */ 4962306a36Sopenharmony_ci#define PM_IRQF_MASK_FE 0x02 /* mask falling edge */ 5062306a36Sopenharmony_ci#define PM_IRQF_MASK_RE 0x04 /* mask rising edge */ 5162306a36Sopenharmony_ci#define PM_IRQF_CLR 0x08 /* clear interrupt */ 5262306a36Sopenharmony_ci#define PM_IRQF_BITS_MASK 0x70 5362306a36Sopenharmony_ci#define PM_IRQF_BITS_SHIFT 4 5462306a36Sopenharmony_ci#define PM_IRQF_WRITE 0x80 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci#define PM_IRQF_MASK_ALL (PM_IRQF_MASK_FE | \ 5762306a36Sopenharmony_ci PM_IRQF_MASK_RE) 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci#define REG_HWREV 0x002 /* PMIC4 revision */ 6062306a36Sopenharmony_ci#define REG_HWREV_2 0x0E8 /* PMIC4 revision 2 */ 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci#define PM8XXX_NR_IRQS 256 6362306a36Sopenharmony_ci#define PM8821_NR_IRQS 112 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistruct pm_irq_data { 6662306a36Sopenharmony_ci int num_irqs; 6762306a36Sopenharmony_ci struct irq_chip *irq_chip; 6862306a36Sopenharmony_ci irq_handler_t irq_handler; 6962306a36Sopenharmony_ci}; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistruct pm_irq_chip { 7262306a36Sopenharmony_ci struct regmap *regmap; 7362306a36Sopenharmony_ci spinlock_t pm_irq_lock; 7462306a36Sopenharmony_ci struct irq_domain *irqdomain; 7562306a36Sopenharmony_ci unsigned int num_blocks; 7662306a36Sopenharmony_ci unsigned int num_masters; 7762306a36Sopenharmony_ci const struct pm_irq_data *pm_irq_data; 7862306a36Sopenharmony_ci /* MUST BE AT THE END OF THIS STRUCT */ 7962306a36Sopenharmony_ci u8 config[]; 8062306a36Sopenharmony_ci}; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic int pm8xxx_read_block_irq(struct pm_irq_chip *chip, unsigned int bp, 8362306a36Sopenharmony_ci unsigned int *ip) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci int rc; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci spin_lock(&chip->pm_irq_lock); 8862306a36Sopenharmony_ci rc = regmap_write(chip->regmap, SSBI_REG_ADDR_IRQ_BLK_SEL, bp); 8962306a36Sopenharmony_ci if (rc) { 9062306a36Sopenharmony_ci pr_err("Failed Selecting Block %d rc=%d\n", bp, rc); 9162306a36Sopenharmony_ci goto bail; 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci rc = regmap_read(chip->regmap, SSBI_REG_ADDR_IRQ_IT_STATUS, ip); 9562306a36Sopenharmony_ci if (rc) 9662306a36Sopenharmony_ci pr_err("Failed Reading Status rc=%d\n", rc); 9762306a36Sopenharmony_cibail: 9862306a36Sopenharmony_ci spin_unlock(&chip->pm_irq_lock); 9962306a36Sopenharmony_ci return rc; 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic int 10362306a36Sopenharmony_cipm8xxx_config_irq(struct pm_irq_chip *chip, unsigned int bp, unsigned int cp) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci int rc; 10662306a36Sopenharmony_ci unsigned long flags; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci spin_lock_irqsave(&chip->pm_irq_lock, flags); 10962306a36Sopenharmony_ci rc = regmap_write(chip->regmap, SSBI_REG_ADDR_IRQ_BLK_SEL, bp); 11062306a36Sopenharmony_ci if (rc) { 11162306a36Sopenharmony_ci pr_err("Failed Selecting Block %d rc=%d\n", bp, rc); 11262306a36Sopenharmony_ci goto bail; 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci cp |= PM_IRQF_WRITE; 11662306a36Sopenharmony_ci rc = regmap_write(chip->regmap, SSBI_REG_ADDR_IRQ_CONFIG, cp); 11762306a36Sopenharmony_ci if (rc) 11862306a36Sopenharmony_ci pr_err("Failed Configuring IRQ rc=%d\n", rc); 11962306a36Sopenharmony_cibail: 12062306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->pm_irq_lock, flags); 12162306a36Sopenharmony_ci return rc; 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic int pm8xxx_irq_block_handler(struct pm_irq_chip *chip, int block) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci int pmirq, i, ret = 0; 12762306a36Sopenharmony_ci unsigned int bits; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci ret = pm8xxx_read_block_irq(chip, block, &bits); 13062306a36Sopenharmony_ci if (ret) { 13162306a36Sopenharmony_ci pr_err("Failed reading %d block ret=%d", block, ret); 13262306a36Sopenharmony_ci return ret; 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci if (!bits) { 13562306a36Sopenharmony_ci pr_err("block bit set in master but no irqs: %d", block); 13662306a36Sopenharmony_ci return 0; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci /* Check IRQ bits */ 14062306a36Sopenharmony_ci for (i = 0; i < 8; i++) { 14162306a36Sopenharmony_ci if (bits & (1 << i)) { 14262306a36Sopenharmony_ci pmirq = block * 8 + i; 14362306a36Sopenharmony_ci generic_handle_domain_irq(chip->irqdomain, pmirq); 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci return 0; 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistatic int pm8xxx_irq_master_handler(struct pm_irq_chip *chip, int master) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci unsigned int blockbits; 15262306a36Sopenharmony_ci int block_number, i, ret = 0; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci ret = regmap_read(chip->regmap, SSBI_REG_ADDR_IRQ_M_STATUS1 + master, 15562306a36Sopenharmony_ci &blockbits); 15662306a36Sopenharmony_ci if (ret) { 15762306a36Sopenharmony_ci pr_err("Failed to read master %d ret=%d\n", master, ret); 15862306a36Sopenharmony_ci return ret; 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci if (!blockbits) { 16162306a36Sopenharmony_ci pr_err("master bit set in root but no blocks: %d", master); 16262306a36Sopenharmony_ci return 0; 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci for (i = 0; i < 8; i++) 16662306a36Sopenharmony_ci if (blockbits & (1 << i)) { 16762306a36Sopenharmony_ci block_number = master * 8 + i; /* block # */ 16862306a36Sopenharmony_ci ret |= pm8xxx_irq_block_handler(chip, block_number); 16962306a36Sopenharmony_ci } 17062306a36Sopenharmony_ci return ret; 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic irqreturn_t pm8xxx_irq_handler(int irq, void *data) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci struct pm_irq_chip *chip = data; 17662306a36Sopenharmony_ci unsigned int root; 17762306a36Sopenharmony_ci int i, ret, masters = 0; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci ret = regmap_read(chip->regmap, SSBI_REG_ADDR_IRQ_ROOT, &root); 18062306a36Sopenharmony_ci if (ret) { 18162306a36Sopenharmony_ci pr_err("Can't read root status ret=%d\n", ret); 18262306a36Sopenharmony_ci return IRQ_NONE; 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci /* on pm8xxx series masters start from bit 1 of the root */ 18662306a36Sopenharmony_ci masters = root >> 1; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci /* Read allowed masters for blocks. */ 18962306a36Sopenharmony_ci for (i = 0; i < chip->num_masters; i++) 19062306a36Sopenharmony_ci if (masters & (1 << i)) 19162306a36Sopenharmony_ci pm8xxx_irq_master_handler(chip, i); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci return IRQ_HANDLED; 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_cistatic void pm8821_irq_block_handler(struct pm_irq_chip *chip, 19762306a36Sopenharmony_ci int master, int block) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci int pmirq, i, ret; 20062306a36Sopenharmony_ci unsigned int bits; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci ret = regmap_read(chip->regmap, 20362306a36Sopenharmony_ci PM8821_SSBI_ADDR_IRQ_ROOT(master, block), &bits); 20462306a36Sopenharmony_ci if (ret) { 20562306a36Sopenharmony_ci pr_err("Reading block %d failed ret=%d", block, ret); 20662306a36Sopenharmony_ci return; 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci /* Convert block offset to global block number */ 21062306a36Sopenharmony_ci block += (master * PM8821_BLOCKS_PER_MASTER) - 1; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci /* Check IRQ bits */ 21362306a36Sopenharmony_ci for (i = 0; i < 8; i++) { 21462306a36Sopenharmony_ci if (bits & BIT(i)) { 21562306a36Sopenharmony_ci pmirq = block * 8 + i; 21662306a36Sopenharmony_ci generic_handle_domain_irq(chip->irqdomain, pmirq); 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci} 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_cistatic inline void pm8821_irq_master_handler(struct pm_irq_chip *chip, 22262306a36Sopenharmony_ci int master, u8 master_val) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci int block; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci for (block = 1; block < 8; block++) 22762306a36Sopenharmony_ci if (master_val & BIT(block)) 22862306a36Sopenharmony_ci pm8821_irq_block_handler(chip, master, block); 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_cistatic irqreturn_t pm8821_irq_handler(int irq, void *data) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci struct pm_irq_chip *chip = data; 23462306a36Sopenharmony_ci unsigned int master; 23562306a36Sopenharmony_ci int ret; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci ret = regmap_read(chip->regmap, 23862306a36Sopenharmony_ci PM8821_SSBI_REG_ADDR_IRQ_MASTER0, &master); 23962306a36Sopenharmony_ci if (ret) { 24062306a36Sopenharmony_ci pr_err("Failed to read master 0 ret=%d\n", ret); 24162306a36Sopenharmony_ci return IRQ_NONE; 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci /* bits 1 through 7 marks the first 7 blocks in master 0 */ 24562306a36Sopenharmony_ci if (master & GENMASK(7, 1)) 24662306a36Sopenharmony_ci pm8821_irq_master_handler(chip, 0, master); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci /* bit 0 marks if master 1 contains any bits */ 24962306a36Sopenharmony_ci if (!(master & BIT(0))) 25062306a36Sopenharmony_ci return IRQ_NONE; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci ret = regmap_read(chip->regmap, 25362306a36Sopenharmony_ci PM8821_SSBI_REG_ADDR_IRQ_MASTER1, &master); 25462306a36Sopenharmony_ci if (ret) { 25562306a36Sopenharmony_ci pr_err("Failed to read master 1 ret=%d\n", ret); 25662306a36Sopenharmony_ci return IRQ_NONE; 25762306a36Sopenharmony_ci } 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci pm8821_irq_master_handler(chip, 1, master); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci return IRQ_HANDLED; 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_cistatic void pm8xxx_irq_mask_ack(struct irq_data *d) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d); 26762306a36Sopenharmony_ci unsigned int pmirq = irqd_to_hwirq(d); 26862306a36Sopenharmony_ci u8 block, config; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci block = pmirq / 8; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci config = chip->config[pmirq] | PM_IRQF_MASK_ALL | PM_IRQF_CLR; 27362306a36Sopenharmony_ci pm8xxx_config_irq(chip, block, config); 27462306a36Sopenharmony_ci} 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_cistatic void pm8xxx_irq_unmask(struct irq_data *d) 27762306a36Sopenharmony_ci{ 27862306a36Sopenharmony_ci struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d); 27962306a36Sopenharmony_ci unsigned int pmirq = irqd_to_hwirq(d); 28062306a36Sopenharmony_ci u8 block, config; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci block = pmirq / 8; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci config = chip->config[pmirq]; 28562306a36Sopenharmony_ci pm8xxx_config_irq(chip, block, config); 28662306a36Sopenharmony_ci} 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_cistatic int pm8xxx_irq_set_type(struct irq_data *d, unsigned int flow_type) 28962306a36Sopenharmony_ci{ 29062306a36Sopenharmony_ci struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d); 29162306a36Sopenharmony_ci unsigned int pmirq = irqd_to_hwirq(d); 29262306a36Sopenharmony_ci int irq_bit; 29362306a36Sopenharmony_ci u8 block, config; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci block = pmirq / 8; 29662306a36Sopenharmony_ci irq_bit = pmirq % 8; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci chip->config[pmirq] = (irq_bit << PM_IRQF_BITS_SHIFT) 29962306a36Sopenharmony_ci | PM_IRQF_MASK_ALL; 30062306a36Sopenharmony_ci if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) { 30162306a36Sopenharmony_ci if (flow_type & IRQF_TRIGGER_RISING) 30262306a36Sopenharmony_ci chip->config[pmirq] &= ~PM_IRQF_MASK_RE; 30362306a36Sopenharmony_ci if (flow_type & IRQF_TRIGGER_FALLING) 30462306a36Sopenharmony_ci chip->config[pmirq] &= ~PM_IRQF_MASK_FE; 30562306a36Sopenharmony_ci } else { 30662306a36Sopenharmony_ci chip->config[pmirq] |= PM_IRQF_LVL_SEL; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci if (flow_type & IRQF_TRIGGER_HIGH) 30962306a36Sopenharmony_ci chip->config[pmirq] &= ~PM_IRQF_MASK_RE; 31062306a36Sopenharmony_ci else 31162306a36Sopenharmony_ci chip->config[pmirq] &= ~PM_IRQF_MASK_FE; 31262306a36Sopenharmony_ci } 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci config = chip->config[pmirq] | PM_IRQF_CLR; 31562306a36Sopenharmony_ci return pm8xxx_config_irq(chip, block, config); 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_cistatic int pm8xxx_irq_get_irqchip_state(struct irq_data *d, 31962306a36Sopenharmony_ci enum irqchip_irq_state which, 32062306a36Sopenharmony_ci bool *state) 32162306a36Sopenharmony_ci{ 32262306a36Sopenharmony_ci struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d); 32362306a36Sopenharmony_ci unsigned int pmirq = irqd_to_hwirq(d); 32462306a36Sopenharmony_ci unsigned int bits; 32562306a36Sopenharmony_ci unsigned long flags; 32662306a36Sopenharmony_ci int irq_bit; 32762306a36Sopenharmony_ci u8 block; 32862306a36Sopenharmony_ci int rc; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci if (which != IRQCHIP_STATE_LINE_LEVEL) 33162306a36Sopenharmony_ci return -EINVAL; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci block = pmirq / 8; 33462306a36Sopenharmony_ci irq_bit = pmirq % 8; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci spin_lock_irqsave(&chip->pm_irq_lock, flags); 33762306a36Sopenharmony_ci rc = regmap_write(chip->regmap, SSBI_REG_ADDR_IRQ_BLK_SEL, block); 33862306a36Sopenharmony_ci if (rc) { 33962306a36Sopenharmony_ci pr_err("Failed Selecting Block %d rc=%d\n", block, rc); 34062306a36Sopenharmony_ci goto bail; 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci rc = regmap_read(chip->regmap, SSBI_REG_ADDR_IRQ_RT_STATUS, &bits); 34462306a36Sopenharmony_ci if (rc) { 34562306a36Sopenharmony_ci pr_err("Failed Reading Status rc=%d\n", rc); 34662306a36Sopenharmony_ci goto bail; 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci *state = !!(bits & BIT(irq_bit)); 35062306a36Sopenharmony_cibail: 35162306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->pm_irq_lock, flags); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci return rc; 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_cistatic struct irq_chip pm8xxx_irq_chip = { 35762306a36Sopenharmony_ci .name = "pm8xxx", 35862306a36Sopenharmony_ci .irq_mask_ack = pm8xxx_irq_mask_ack, 35962306a36Sopenharmony_ci .irq_unmask = pm8xxx_irq_unmask, 36062306a36Sopenharmony_ci .irq_set_type = pm8xxx_irq_set_type, 36162306a36Sopenharmony_ci .irq_get_irqchip_state = pm8xxx_irq_get_irqchip_state, 36262306a36Sopenharmony_ci .flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE, 36362306a36Sopenharmony_ci}; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_cistatic void pm8xxx_irq_domain_map(struct pm_irq_chip *chip, 36662306a36Sopenharmony_ci struct irq_domain *domain, unsigned int irq, 36762306a36Sopenharmony_ci irq_hw_number_t hwirq, unsigned int type) 36862306a36Sopenharmony_ci{ 36962306a36Sopenharmony_ci irq_domain_set_info(domain, irq, hwirq, chip->pm_irq_data->irq_chip, 37062306a36Sopenharmony_ci chip, handle_level_irq, NULL, NULL); 37162306a36Sopenharmony_ci irq_set_noprobe(irq); 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_cistatic int pm8xxx_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, 37562306a36Sopenharmony_ci unsigned int nr_irqs, void *data) 37662306a36Sopenharmony_ci{ 37762306a36Sopenharmony_ci struct pm_irq_chip *chip = domain->host_data; 37862306a36Sopenharmony_ci struct irq_fwspec *fwspec = data; 37962306a36Sopenharmony_ci irq_hw_number_t hwirq; 38062306a36Sopenharmony_ci unsigned int type; 38162306a36Sopenharmony_ci int ret, i; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci ret = irq_domain_translate_twocell(domain, fwspec, &hwirq, &type); 38462306a36Sopenharmony_ci if (ret) 38562306a36Sopenharmony_ci return ret; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci for (i = 0; i < nr_irqs; i++) 38862306a36Sopenharmony_ci pm8xxx_irq_domain_map(chip, domain, virq + i, hwirq + i, type); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci return 0; 39162306a36Sopenharmony_ci} 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_cistatic const struct irq_domain_ops pm8xxx_irq_domain_ops = { 39462306a36Sopenharmony_ci .alloc = pm8xxx_irq_domain_alloc, 39562306a36Sopenharmony_ci .free = irq_domain_free_irqs_common, 39662306a36Sopenharmony_ci .translate = irq_domain_translate_twocell, 39762306a36Sopenharmony_ci}; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_cistatic void pm8821_irq_mask_ack(struct irq_data *d) 40062306a36Sopenharmony_ci{ 40162306a36Sopenharmony_ci struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d); 40262306a36Sopenharmony_ci unsigned int pmirq = irqd_to_hwirq(d); 40362306a36Sopenharmony_ci u8 block, master; 40462306a36Sopenharmony_ci int irq_bit, rc; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci block = pmirq / 8; 40762306a36Sopenharmony_ci master = block / PM8821_BLOCKS_PER_MASTER; 40862306a36Sopenharmony_ci irq_bit = pmirq % 8; 40962306a36Sopenharmony_ci block %= PM8821_BLOCKS_PER_MASTER; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci rc = regmap_update_bits(chip->regmap, 41262306a36Sopenharmony_ci PM8821_SSBI_ADDR_IRQ_MASK(master, block), 41362306a36Sopenharmony_ci BIT(irq_bit), BIT(irq_bit)); 41462306a36Sopenharmony_ci if (rc) { 41562306a36Sopenharmony_ci pr_err("Failed to mask IRQ:%d rc=%d\n", pmirq, rc); 41662306a36Sopenharmony_ci return; 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci rc = regmap_update_bits(chip->regmap, 42062306a36Sopenharmony_ci PM8821_SSBI_ADDR_IRQ_CLEAR(master, block), 42162306a36Sopenharmony_ci BIT(irq_bit), BIT(irq_bit)); 42262306a36Sopenharmony_ci if (rc) 42362306a36Sopenharmony_ci pr_err("Failed to CLEAR IRQ:%d rc=%d\n", pmirq, rc); 42462306a36Sopenharmony_ci} 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_cistatic void pm8821_irq_unmask(struct irq_data *d) 42762306a36Sopenharmony_ci{ 42862306a36Sopenharmony_ci struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d); 42962306a36Sopenharmony_ci unsigned int pmirq = irqd_to_hwirq(d); 43062306a36Sopenharmony_ci int irq_bit, rc; 43162306a36Sopenharmony_ci u8 block, master; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci block = pmirq / 8; 43462306a36Sopenharmony_ci master = block / PM8821_BLOCKS_PER_MASTER; 43562306a36Sopenharmony_ci irq_bit = pmirq % 8; 43662306a36Sopenharmony_ci block %= PM8821_BLOCKS_PER_MASTER; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci rc = regmap_update_bits(chip->regmap, 43962306a36Sopenharmony_ci PM8821_SSBI_ADDR_IRQ_MASK(master, block), 44062306a36Sopenharmony_ci BIT(irq_bit), ~BIT(irq_bit)); 44162306a36Sopenharmony_ci if (rc) 44262306a36Sopenharmony_ci pr_err("Failed to read/write unmask IRQ:%d rc=%d\n", pmirq, rc); 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci} 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_cistatic int pm8821_irq_get_irqchip_state(struct irq_data *d, 44762306a36Sopenharmony_ci enum irqchip_irq_state which, 44862306a36Sopenharmony_ci bool *state) 44962306a36Sopenharmony_ci{ 45062306a36Sopenharmony_ci struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d); 45162306a36Sopenharmony_ci int rc, pmirq = irqd_to_hwirq(d); 45262306a36Sopenharmony_ci u8 block, irq_bit, master; 45362306a36Sopenharmony_ci unsigned int bits; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci block = pmirq / 8; 45662306a36Sopenharmony_ci master = block / PM8821_BLOCKS_PER_MASTER; 45762306a36Sopenharmony_ci irq_bit = pmirq % 8; 45862306a36Sopenharmony_ci block %= PM8821_BLOCKS_PER_MASTER; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci rc = regmap_read(chip->regmap, 46162306a36Sopenharmony_ci PM8821_SSBI_ADDR_IRQ_RT_STATUS(master, block), &bits); 46262306a36Sopenharmony_ci if (rc) { 46362306a36Sopenharmony_ci pr_err("Reading Status of IRQ %d failed rc=%d\n", pmirq, rc); 46462306a36Sopenharmony_ci return rc; 46562306a36Sopenharmony_ci } 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci *state = !!(bits & BIT(irq_bit)); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci return rc; 47062306a36Sopenharmony_ci} 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_cistatic struct irq_chip pm8821_irq_chip = { 47362306a36Sopenharmony_ci .name = "pm8821", 47462306a36Sopenharmony_ci .irq_mask_ack = pm8821_irq_mask_ack, 47562306a36Sopenharmony_ci .irq_unmask = pm8821_irq_unmask, 47662306a36Sopenharmony_ci .irq_get_irqchip_state = pm8821_irq_get_irqchip_state, 47762306a36Sopenharmony_ci .flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE, 47862306a36Sopenharmony_ci}; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_cistatic const struct regmap_config ssbi_regmap_config = { 48162306a36Sopenharmony_ci .reg_bits = 16, 48262306a36Sopenharmony_ci .val_bits = 8, 48362306a36Sopenharmony_ci .max_register = 0x3ff, 48462306a36Sopenharmony_ci .fast_io = true, 48562306a36Sopenharmony_ci .reg_read = ssbi_reg_read, 48662306a36Sopenharmony_ci .reg_write = ssbi_reg_write 48762306a36Sopenharmony_ci}; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_cistatic const struct pm_irq_data pm8xxx_data = { 49062306a36Sopenharmony_ci .num_irqs = PM8XXX_NR_IRQS, 49162306a36Sopenharmony_ci .irq_chip = &pm8xxx_irq_chip, 49262306a36Sopenharmony_ci .irq_handler = pm8xxx_irq_handler, 49362306a36Sopenharmony_ci}; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_cistatic const struct pm_irq_data pm8821_data = { 49662306a36Sopenharmony_ci .num_irqs = PM8821_NR_IRQS, 49762306a36Sopenharmony_ci .irq_chip = &pm8821_irq_chip, 49862306a36Sopenharmony_ci .irq_handler = pm8821_irq_handler, 49962306a36Sopenharmony_ci}; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_cistatic const struct of_device_id pm8xxx_id_table[] = { 50262306a36Sopenharmony_ci { .compatible = "qcom,pm8058", .data = &pm8xxx_data}, 50362306a36Sopenharmony_ci { .compatible = "qcom,pm8821", .data = &pm8821_data}, 50462306a36Sopenharmony_ci { .compatible = "qcom,pm8921", .data = &pm8xxx_data}, 50562306a36Sopenharmony_ci { } 50662306a36Sopenharmony_ci}; 50762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, pm8xxx_id_table); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_cistatic int pm8xxx_probe(struct platform_device *pdev) 51062306a36Sopenharmony_ci{ 51162306a36Sopenharmony_ci const struct pm_irq_data *data; 51262306a36Sopenharmony_ci struct regmap *regmap; 51362306a36Sopenharmony_ci int irq, rc; 51462306a36Sopenharmony_ci unsigned int val; 51562306a36Sopenharmony_ci struct pm_irq_chip *chip; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci data = of_device_get_match_data(&pdev->dev); 51862306a36Sopenharmony_ci if (!data) { 51962306a36Sopenharmony_ci dev_err(&pdev->dev, "No matching driver data found\n"); 52062306a36Sopenharmony_ci return -EINVAL; 52162306a36Sopenharmony_ci } 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci irq = platform_get_irq(pdev, 0); 52462306a36Sopenharmony_ci if (irq < 0) 52562306a36Sopenharmony_ci return irq; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci regmap = devm_regmap_init(&pdev->dev, NULL, pdev->dev.parent, 52862306a36Sopenharmony_ci &ssbi_regmap_config); 52962306a36Sopenharmony_ci if (IS_ERR(regmap)) 53062306a36Sopenharmony_ci return PTR_ERR(regmap); 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci /* Read PMIC chip revision */ 53362306a36Sopenharmony_ci rc = regmap_read(regmap, REG_HWREV, &val); 53462306a36Sopenharmony_ci if (rc) { 53562306a36Sopenharmony_ci pr_err("Failed to read hw rev reg %d:rc=%d\n", REG_HWREV, rc); 53662306a36Sopenharmony_ci return rc; 53762306a36Sopenharmony_ci } 53862306a36Sopenharmony_ci pr_info("PMIC revision 1: %02X\n", val); 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci /* Read PMIC chip revision 2 */ 54162306a36Sopenharmony_ci rc = regmap_read(regmap, REG_HWREV_2, &val); 54262306a36Sopenharmony_ci if (rc) { 54362306a36Sopenharmony_ci pr_err("Failed to read hw rev 2 reg %d:rc=%d\n", 54462306a36Sopenharmony_ci REG_HWREV_2, rc); 54562306a36Sopenharmony_ci return rc; 54662306a36Sopenharmony_ci } 54762306a36Sopenharmony_ci pr_info("PMIC revision 2: %02X\n", val); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci chip = devm_kzalloc(&pdev->dev, 55062306a36Sopenharmony_ci struct_size(chip, config, data->num_irqs), 55162306a36Sopenharmony_ci GFP_KERNEL); 55262306a36Sopenharmony_ci if (!chip) 55362306a36Sopenharmony_ci return -ENOMEM; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci platform_set_drvdata(pdev, chip); 55662306a36Sopenharmony_ci chip->regmap = regmap; 55762306a36Sopenharmony_ci chip->num_blocks = DIV_ROUND_UP(data->num_irqs, 8); 55862306a36Sopenharmony_ci chip->num_masters = DIV_ROUND_UP(chip->num_blocks, 8); 55962306a36Sopenharmony_ci chip->pm_irq_data = data; 56062306a36Sopenharmony_ci spin_lock_init(&chip->pm_irq_lock); 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci chip->irqdomain = irq_domain_add_linear(pdev->dev.of_node, 56362306a36Sopenharmony_ci data->num_irqs, 56462306a36Sopenharmony_ci &pm8xxx_irq_domain_ops, 56562306a36Sopenharmony_ci chip); 56662306a36Sopenharmony_ci if (!chip->irqdomain) 56762306a36Sopenharmony_ci return -ENODEV; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci rc = devm_request_irq(&pdev->dev, irq, data->irq_handler, 0, dev_name(&pdev->dev), chip); 57062306a36Sopenharmony_ci if (rc) 57162306a36Sopenharmony_ci return rc; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci irq_set_irq_wake(irq, 1); 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci rc = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); 57662306a36Sopenharmony_ci if (rc) 57762306a36Sopenharmony_ci irq_domain_remove(chip->irqdomain); 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci return rc; 58062306a36Sopenharmony_ci} 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_cistatic int pm8xxx_remove_child(struct device *dev, void *unused) 58362306a36Sopenharmony_ci{ 58462306a36Sopenharmony_ci platform_device_unregister(to_platform_device(dev)); 58562306a36Sopenharmony_ci return 0; 58662306a36Sopenharmony_ci} 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_cistatic int pm8xxx_remove(struct platform_device *pdev) 58962306a36Sopenharmony_ci{ 59062306a36Sopenharmony_ci struct pm_irq_chip *chip = platform_get_drvdata(pdev); 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci device_for_each_child(&pdev->dev, NULL, pm8xxx_remove_child); 59362306a36Sopenharmony_ci irq_domain_remove(chip->irqdomain); 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci return 0; 59662306a36Sopenharmony_ci} 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_cistatic struct platform_driver pm8xxx_driver = { 59962306a36Sopenharmony_ci .probe = pm8xxx_probe, 60062306a36Sopenharmony_ci .remove = pm8xxx_remove, 60162306a36Sopenharmony_ci .driver = { 60262306a36Sopenharmony_ci .name = "pm8xxx-core", 60362306a36Sopenharmony_ci .of_match_table = pm8xxx_id_table, 60462306a36Sopenharmony_ci }, 60562306a36Sopenharmony_ci}; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_cistatic int __init pm8xxx_init(void) 60862306a36Sopenharmony_ci{ 60962306a36Sopenharmony_ci return platform_driver_register(&pm8xxx_driver); 61062306a36Sopenharmony_ci} 61162306a36Sopenharmony_cisubsys_initcall(pm8xxx_init); 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_cistatic void __exit pm8xxx_exit(void) 61462306a36Sopenharmony_ci{ 61562306a36Sopenharmony_ci platform_driver_unregister(&pm8xxx_driver); 61662306a36Sopenharmony_ci} 61762306a36Sopenharmony_cimodule_exit(pm8xxx_exit); 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 62062306a36Sopenharmony_ciMODULE_DESCRIPTION("PMIC 8xxx core driver"); 62162306a36Sopenharmony_ciMODULE_VERSION("1.0"); 62262306a36Sopenharmony_ciMODULE_ALIAS("platform:pm8xxx-core"); 623