162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Device driver for irqs in HISI PMIC IC 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2013 Linaro Ltd. 662306a36Sopenharmony_ci * Copyright (c) 2011 Hisilicon. 762306a36Sopenharmony_ci * Copyright (c) 2020-2021 Huawei Technologies Co., Ltd. 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/bitops.h> 1162306a36Sopenharmony_ci#include <linux/interrupt.h> 1262306a36Sopenharmony_ci#include <linux/irq.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/of_gpio.h> 1562306a36Sopenharmony_ci#include <linux/platform_device.h> 1662306a36Sopenharmony_ci#include <linux/slab.h> 1762306a36Sopenharmony_ci#include <linux/irqdomain.h> 1862306a36Sopenharmony_ci#include <linux/regmap.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistruct hi6421v600_irq { 2162306a36Sopenharmony_ci struct device *dev; 2262306a36Sopenharmony_ci struct irq_domain *domain; 2362306a36Sopenharmony_ci int irq; 2462306a36Sopenharmony_ci unsigned int *irqs; 2562306a36Sopenharmony_ci struct regmap *regmap; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci /* Protect IRQ mask changes */ 2862306a36Sopenharmony_ci spinlock_t lock; 2962306a36Sopenharmony_ci}; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cienum hi6421v600_irq_list { 3262306a36Sopenharmony_ci OTMP = 0, 3362306a36Sopenharmony_ci VBUS_CONNECT, 3462306a36Sopenharmony_ci VBUS_DISCONNECT, 3562306a36Sopenharmony_ci ALARMON_R, 3662306a36Sopenharmony_ci HOLD_6S, 3762306a36Sopenharmony_ci HOLD_1S, 3862306a36Sopenharmony_ci POWERKEY_UP, 3962306a36Sopenharmony_ci POWERKEY_DOWN, 4062306a36Sopenharmony_ci OCP_SCP_R, 4162306a36Sopenharmony_ci COUL_R, 4262306a36Sopenharmony_ci SIM0_HPD_R, 4362306a36Sopenharmony_ci SIM0_HPD_F, 4462306a36Sopenharmony_ci SIM1_HPD_R, 4562306a36Sopenharmony_ci SIM1_HPD_F, 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci PMIC_IRQ_LIST_MAX 4862306a36Sopenharmony_ci}; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci#define HISI_IRQ_BANK_SIZE 2 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci/* 5362306a36Sopenharmony_ci * IRQ number for the power key button and mask for both UP and DOWN IRQs 5462306a36Sopenharmony_ci */ 5562306a36Sopenharmony_ci#define HISI_POWERKEY_IRQ_NUM 0 5662306a36Sopenharmony_ci#define HISI_IRQ_POWERKEY_UP_DOWN (BIT(POWERKEY_DOWN) | BIT(POWERKEY_UP)) 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/* 5962306a36Sopenharmony_ci * Registers for IRQ address and IRQ mask bits 6062306a36Sopenharmony_ci * 6162306a36Sopenharmony_ci * Please notice that we need to regmap a larger region, as other 6262306a36Sopenharmony_ci * registers are used by the irqs. 6362306a36Sopenharmony_ci * See drivers/irq/hi6421-irq.c. 6462306a36Sopenharmony_ci */ 6562306a36Sopenharmony_ci#define SOC_PMIC_IRQ_MASK_0_ADDR 0x0202 6662306a36Sopenharmony_ci#define SOC_PMIC_IRQ0_ADDR 0x0212 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci/* 6962306a36Sopenharmony_ci * The IRQs are mapped as: 7062306a36Sopenharmony_ci * 7162306a36Sopenharmony_ci * ====================== ============= ============ ===== 7262306a36Sopenharmony_ci * IRQ MASK REGISTER IRQ REGISTER BIT 7362306a36Sopenharmony_ci * ====================== ============= ============ ===== 7462306a36Sopenharmony_ci * OTMP 0x0202 0x212 bit 0 7562306a36Sopenharmony_ci * VBUS_CONNECT 0x0202 0x212 bit 1 7662306a36Sopenharmony_ci * VBUS_DISCONNECT 0x0202 0x212 bit 2 7762306a36Sopenharmony_ci * ALARMON_R 0x0202 0x212 bit 3 7862306a36Sopenharmony_ci * HOLD_6S 0x0202 0x212 bit 4 7962306a36Sopenharmony_ci * HOLD_1S 0x0202 0x212 bit 5 8062306a36Sopenharmony_ci * POWERKEY_UP 0x0202 0x212 bit 6 8162306a36Sopenharmony_ci * POWERKEY_DOWN 0x0202 0x212 bit 7 8262306a36Sopenharmony_ci * 8362306a36Sopenharmony_ci * OCP_SCP_R 0x0203 0x213 bit 0 8462306a36Sopenharmony_ci * COUL_R 0x0203 0x213 bit 1 8562306a36Sopenharmony_ci * SIM0_HPD_R 0x0203 0x213 bit 2 8662306a36Sopenharmony_ci * SIM0_HPD_F 0x0203 0x213 bit 3 8762306a36Sopenharmony_ci * SIM1_HPD_R 0x0203 0x213 bit 4 8862306a36Sopenharmony_ci * SIM1_HPD_F 0x0203 0x213 bit 5 8962306a36Sopenharmony_ci * ====================== ============= ============ ===== 9062306a36Sopenharmony_ci * 9162306a36Sopenharmony_ci * Each mask register contains 8 bits. The ancillary macros below 9262306a36Sopenharmony_ci * convert a number from 0 to 14 into a register address and a bit mask 9362306a36Sopenharmony_ci */ 9462306a36Sopenharmony_ci#define HISI_IRQ_MASK_REG(irq_data) (SOC_PMIC_IRQ_MASK_0_ADDR + \ 9562306a36Sopenharmony_ci (irqd_to_hwirq(irq_data) / BITS_PER_BYTE)) 9662306a36Sopenharmony_ci#define HISI_IRQ_MASK_BIT(irq_data) BIT(irqd_to_hwirq(irq_data) & (BITS_PER_BYTE - 1)) 9762306a36Sopenharmony_ci#define HISI_8BITS_MASK 0xff 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic irqreturn_t hi6421v600_irq_handler(int irq, void *__priv) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci struct hi6421v600_irq *priv = __priv; 10262306a36Sopenharmony_ci unsigned long pending; 10362306a36Sopenharmony_ci unsigned int in; 10462306a36Sopenharmony_ci int i, offset; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci for (i = 0; i < HISI_IRQ_BANK_SIZE; i++) { 10762306a36Sopenharmony_ci regmap_read(priv->regmap, SOC_PMIC_IRQ0_ADDR + i, &in); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci /* Mark pending IRQs as handled */ 11062306a36Sopenharmony_ci regmap_write(priv->regmap, SOC_PMIC_IRQ0_ADDR + i, in); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci pending = in & HISI_8BITS_MASK; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci if (i == HISI_POWERKEY_IRQ_NUM && 11562306a36Sopenharmony_ci (pending & HISI_IRQ_POWERKEY_UP_DOWN) == HISI_IRQ_POWERKEY_UP_DOWN) { 11662306a36Sopenharmony_ci /* 11762306a36Sopenharmony_ci * If both powerkey down and up IRQs are received, 11862306a36Sopenharmony_ci * handle them at the right order 11962306a36Sopenharmony_ci */ 12062306a36Sopenharmony_ci generic_handle_irq_safe(priv->irqs[POWERKEY_DOWN]); 12162306a36Sopenharmony_ci generic_handle_irq_safe(priv->irqs[POWERKEY_UP]); 12262306a36Sopenharmony_ci pending &= ~HISI_IRQ_POWERKEY_UP_DOWN; 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci if (!pending) 12662306a36Sopenharmony_ci continue; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci for_each_set_bit(offset, &pending, BITS_PER_BYTE) { 12962306a36Sopenharmony_ci generic_handle_irq_safe(priv->irqs[offset + i * BITS_PER_BYTE]); 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci return IRQ_HANDLED; 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic void hi6421v600_irq_mask(struct irq_data *d) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci struct hi6421v600_irq *priv = irq_data_get_irq_chip_data(d); 13962306a36Sopenharmony_ci unsigned long flags; 14062306a36Sopenharmony_ci unsigned int data; 14162306a36Sopenharmony_ci u32 offset; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci offset = HISI_IRQ_MASK_REG(d); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci regmap_read(priv->regmap, offset, &data); 14862306a36Sopenharmony_ci data |= HISI_IRQ_MASK_BIT(d); 14962306a36Sopenharmony_ci regmap_write(priv->regmap, offset, data); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic void hi6421v600_irq_unmask(struct irq_data *d) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci struct hi6421v600_irq *priv = irq_data_get_irq_chip_data(d); 15762306a36Sopenharmony_ci u32 data, offset; 15862306a36Sopenharmony_ci unsigned long flags; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci offset = HISI_IRQ_MASK_REG(d); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci regmap_read(priv->regmap, offset, &data); 16562306a36Sopenharmony_ci data &= ~HISI_IRQ_MASK_BIT(d); 16662306a36Sopenharmony_ci regmap_write(priv->regmap, offset, data); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic struct irq_chip hi6421v600_pmu_irqchip = { 17262306a36Sopenharmony_ci .name = "hi6421v600-irq", 17362306a36Sopenharmony_ci .irq_mask = hi6421v600_irq_mask, 17462306a36Sopenharmony_ci .irq_unmask = hi6421v600_irq_unmask, 17562306a36Sopenharmony_ci .irq_disable = hi6421v600_irq_mask, 17662306a36Sopenharmony_ci .irq_enable = hi6421v600_irq_unmask, 17762306a36Sopenharmony_ci}; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistatic int hi6421v600_irq_map(struct irq_domain *d, unsigned int virq, 18062306a36Sopenharmony_ci irq_hw_number_t hw) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci struct hi6421v600_irq *priv = d->host_data; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci irq_set_chip_and_handler_name(virq, &hi6421v600_pmu_irqchip, 18562306a36Sopenharmony_ci handle_simple_irq, "hi6421v600"); 18662306a36Sopenharmony_ci irq_set_chip_data(virq, priv); 18762306a36Sopenharmony_ci irq_set_irq_type(virq, IRQ_TYPE_NONE); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci return 0; 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic const struct irq_domain_ops hi6421v600_domain_ops = { 19362306a36Sopenharmony_ci .map = hi6421v600_irq_map, 19462306a36Sopenharmony_ci .xlate = irq_domain_xlate_twocell, 19562306a36Sopenharmony_ci}; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic void hi6421v600_irq_init(struct hi6421v600_irq *priv) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci int i; 20062306a36Sopenharmony_ci unsigned int pending; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci /* Mask all IRQs */ 20362306a36Sopenharmony_ci for (i = 0; i < HISI_IRQ_BANK_SIZE; i++) 20462306a36Sopenharmony_ci regmap_write(priv->regmap, SOC_PMIC_IRQ_MASK_0_ADDR + i, 20562306a36Sopenharmony_ci HISI_8BITS_MASK); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci /* Mark all IRQs as handled */ 20862306a36Sopenharmony_ci for (i = 0; i < HISI_IRQ_BANK_SIZE; i++) { 20962306a36Sopenharmony_ci regmap_read(priv->regmap, SOC_PMIC_IRQ0_ADDR + i, &pending); 21062306a36Sopenharmony_ci regmap_write(priv->regmap, SOC_PMIC_IRQ0_ADDR + i, 21162306a36Sopenharmony_ci HISI_8BITS_MASK); 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cistatic int hi6421v600_irq_probe(struct platform_device *pdev) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci struct device *pmic_dev = pdev->dev.parent; 21862306a36Sopenharmony_ci struct device_node *np = pmic_dev->of_node; 21962306a36Sopenharmony_ci struct platform_device *pmic_pdev; 22062306a36Sopenharmony_ci struct device *dev = &pdev->dev; 22162306a36Sopenharmony_ci struct hi6421v600_irq *priv; 22262306a36Sopenharmony_ci struct regmap *regmap; 22362306a36Sopenharmony_ci unsigned int virq; 22462306a36Sopenharmony_ci int i, ret; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci /* 22762306a36Sopenharmony_ci * This driver is meant to be called by hi6421-spmi-core, 22862306a36Sopenharmony_ci * which should first set drvdata. If this doesn't happen, hit 22962306a36Sopenharmony_ci * a warn on and return. 23062306a36Sopenharmony_ci */ 23162306a36Sopenharmony_ci regmap = dev_get_drvdata(pmic_dev); 23262306a36Sopenharmony_ci if (WARN_ON(!regmap)) 23362306a36Sopenharmony_ci return -ENODEV; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 23662306a36Sopenharmony_ci if (!priv) 23762306a36Sopenharmony_ci return -ENOMEM; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci priv->dev = dev; 24062306a36Sopenharmony_ci priv->regmap = regmap; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci spin_lock_init(&priv->lock); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci pmic_pdev = container_of(pmic_dev, struct platform_device, dev); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci priv->irq = platform_get_irq(pmic_pdev, 0); 24762306a36Sopenharmony_ci if (priv->irq < 0) 24862306a36Sopenharmony_ci return priv->irq; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci platform_set_drvdata(pdev, priv); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci hi6421v600_irq_init(priv); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci priv->irqs = devm_kzalloc(dev, PMIC_IRQ_LIST_MAX * sizeof(int), GFP_KERNEL); 25562306a36Sopenharmony_ci if (!priv->irqs) 25662306a36Sopenharmony_ci return -ENOMEM; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci priv->domain = irq_domain_add_simple(np, PMIC_IRQ_LIST_MAX, 0, 25962306a36Sopenharmony_ci &hi6421v600_domain_ops, priv); 26062306a36Sopenharmony_ci if (!priv->domain) { 26162306a36Sopenharmony_ci dev_err(dev, "Failed to create IRQ domain\n"); 26262306a36Sopenharmony_ci return -ENODEV; 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci for (i = 0; i < PMIC_IRQ_LIST_MAX; i++) { 26662306a36Sopenharmony_ci virq = irq_create_mapping(priv->domain, i); 26762306a36Sopenharmony_ci if (!virq) { 26862306a36Sopenharmony_ci dev_err(dev, "Failed to map H/W IRQ\n"); 26962306a36Sopenharmony_ci return -ENODEV; 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci priv->irqs[i] = virq; 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci ret = devm_request_threaded_irq(dev, 27562306a36Sopenharmony_ci priv->irq, hi6421v600_irq_handler, 27662306a36Sopenharmony_ci NULL, 27762306a36Sopenharmony_ci IRQF_TRIGGER_LOW | IRQF_SHARED | IRQF_NO_SUSPEND, 27862306a36Sopenharmony_ci "pmic", priv); 27962306a36Sopenharmony_ci if (ret < 0) { 28062306a36Sopenharmony_ci dev_err(dev, "Failed to start IRQ handling thread: error %d\n", 28162306a36Sopenharmony_ci ret); 28262306a36Sopenharmony_ci return ret; 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci return 0; 28662306a36Sopenharmony_ci} 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_cistatic const struct platform_device_id hi6421v600_irq_table[] = { 28962306a36Sopenharmony_ci { .name = "hi6421v600-irq" }, 29062306a36Sopenharmony_ci {}, 29162306a36Sopenharmony_ci}; 29262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(platform, hi6421v600_irq_table); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_cistatic struct platform_driver hi6421v600_irq_driver = { 29562306a36Sopenharmony_ci .id_table = hi6421v600_irq_table, 29662306a36Sopenharmony_ci .driver = { 29762306a36Sopenharmony_ci .name = "hi6421v600-irq", 29862306a36Sopenharmony_ci }, 29962306a36Sopenharmony_ci .probe = hi6421v600_irq_probe, 30062306a36Sopenharmony_ci}; 30162306a36Sopenharmony_cimodule_platform_driver(hi6421v600_irq_driver); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ciMODULE_DESCRIPTION("HiSilicon Hi6421v600 IRQ driver"); 30462306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 305