162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// Copyright (c) 2019 MediaTek Inc. 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci#include <linux/interrupt.h> 662306a36Sopenharmony_ci#include <linux/irq.h> 762306a36Sopenharmony_ci#include <linux/irqdomain.h> 862306a36Sopenharmony_ci#include <linux/module.h> 962306a36Sopenharmony_ci#include <linux/platform_device.h> 1062306a36Sopenharmony_ci#include <linux/regmap.h> 1162306a36Sopenharmony_ci#include <linux/suspend.h> 1262306a36Sopenharmony_ci#include <linux/mfd/mt6323/core.h> 1362306a36Sopenharmony_ci#include <linux/mfd/mt6323/registers.h> 1462306a36Sopenharmony_ci#include <linux/mfd/mt6331/core.h> 1562306a36Sopenharmony_ci#include <linux/mfd/mt6331/registers.h> 1662306a36Sopenharmony_ci#include <linux/mfd/mt6397/core.h> 1762306a36Sopenharmony_ci#include <linux/mfd/mt6397/registers.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistatic void mt6397_irq_lock(struct irq_data *data) 2062306a36Sopenharmony_ci{ 2162306a36Sopenharmony_ci struct mt6397_chip *mt6397 = irq_data_get_irq_chip_data(data); 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci mutex_lock(&mt6397->irqlock); 2462306a36Sopenharmony_ci} 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic void mt6397_irq_sync_unlock(struct irq_data *data) 2762306a36Sopenharmony_ci{ 2862306a36Sopenharmony_ci struct mt6397_chip *mt6397 = irq_data_get_irq_chip_data(data); 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci regmap_write(mt6397->regmap, mt6397->int_con[0], 3162306a36Sopenharmony_ci mt6397->irq_masks_cur[0]); 3262306a36Sopenharmony_ci regmap_write(mt6397->regmap, mt6397->int_con[1], 3362306a36Sopenharmony_ci mt6397->irq_masks_cur[1]); 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci mutex_unlock(&mt6397->irqlock); 3662306a36Sopenharmony_ci} 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic void mt6397_irq_disable(struct irq_data *data) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci struct mt6397_chip *mt6397 = irq_data_get_irq_chip_data(data); 4162306a36Sopenharmony_ci int shift = data->hwirq & 0xf; 4262306a36Sopenharmony_ci int reg = data->hwirq >> 4; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci mt6397->irq_masks_cur[reg] &= ~BIT(shift); 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic void mt6397_irq_enable(struct irq_data *data) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci struct mt6397_chip *mt6397 = irq_data_get_irq_chip_data(data); 5062306a36Sopenharmony_ci int shift = data->hwirq & 0xf; 5162306a36Sopenharmony_ci int reg = data->hwirq >> 4; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci mt6397->irq_masks_cur[reg] |= BIT(shift); 5462306a36Sopenharmony_ci} 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic int mt6397_irq_set_wake(struct irq_data *irq_data, unsigned int on) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci struct mt6397_chip *mt6397 = irq_data_get_irq_chip_data(irq_data); 5962306a36Sopenharmony_ci int shift = irq_data->hwirq & 0xf; 6062306a36Sopenharmony_ci int reg = irq_data->hwirq >> 4; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci if (on) 6362306a36Sopenharmony_ci mt6397->wake_mask[reg] |= BIT(shift); 6462306a36Sopenharmony_ci else 6562306a36Sopenharmony_ci mt6397->wake_mask[reg] &= ~BIT(shift); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci return 0; 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic struct irq_chip mt6397_irq_chip = { 7162306a36Sopenharmony_ci .name = "mt6397-irq", 7262306a36Sopenharmony_ci .irq_bus_lock = mt6397_irq_lock, 7362306a36Sopenharmony_ci .irq_bus_sync_unlock = mt6397_irq_sync_unlock, 7462306a36Sopenharmony_ci .irq_enable = mt6397_irq_enable, 7562306a36Sopenharmony_ci .irq_disable = mt6397_irq_disable, 7662306a36Sopenharmony_ci .irq_set_wake = pm_sleep_ptr(mt6397_irq_set_wake), 7762306a36Sopenharmony_ci}; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic void mt6397_irq_handle_reg(struct mt6397_chip *mt6397, int reg, 8062306a36Sopenharmony_ci int irqbase) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci unsigned int status = 0; 8362306a36Sopenharmony_ci int i, irq, ret; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci ret = regmap_read(mt6397->regmap, reg, &status); 8662306a36Sopenharmony_ci if (ret) { 8762306a36Sopenharmony_ci dev_err(mt6397->dev, "Failed to read irq status: %d\n", ret); 8862306a36Sopenharmony_ci return; 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci for (i = 0; i < 16; i++) { 9262306a36Sopenharmony_ci if (status & BIT(i)) { 9362306a36Sopenharmony_ci irq = irq_find_mapping(mt6397->irq_domain, irqbase + i); 9462306a36Sopenharmony_ci if (irq) 9562306a36Sopenharmony_ci handle_nested_irq(irq); 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci regmap_write(mt6397->regmap, reg, status); 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic irqreturn_t mt6397_irq_thread(int irq, void *data) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci struct mt6397_chip *mt6397 = data; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci mt6397_irq_handle_reg(mt6397, mt6397->int_status[0], 0); 10762306a36Sopenharmony_ci mt6397_irq_handle_reg(mt6397, mt6397->int_status[1], 16); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci return IRQ_HANDLED; 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic int mt6397_irq_domain_map(struct irq_domain *d, unsigned int irq, 11362306a36Sopenharmony_ci irq_hw_number_t hw) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci struct mt6397_chip *mt6397 = d->host_data; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci irq_set_chip_data(irq, mt6397); 11862306a36Sopenharmony_ci irq_set_chip_and_handler(irq, &mt6397_irq_chip, handle_level_irq); 11962306a36Sopenharmony_ci irq_set_nested_thread(irq, 1); 12062306a36Sopenharmony_ci irq_set_noprobe(irq); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci return 0; 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic const struct irq_domain_ops mt6397_irq_domain_ops = { 12662306a36Sopenharmony_ci .map = mt6397_irq_domain_map, 12762306a36Sopenharmony_ci}; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic int mt6397_irq_pm_notifier(struct notifier_block *notifier, 13062306a36Sopenharmony_ci unsigned long pm_event, void *unused) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci struct mt6397_chip *chip = 13362306a36Sopenharmony_ci container_of(notifier, struct mt6397_chip, pm_nb); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci switch (pm_event) { 13662306a36Sopenharmony_ci case PM_SUSPEND_PREPARE: 13762306a36Sopenharmony_ci regmap_write(chip->regmap, 13862306a36Sopenharmony_ci chip->int_con[0], chip->wake_mask[0]); 13962306a36Sopenharmony_ci regmap_write(chip->regmap, 14062306a36Sopenharmony_ci chip->int_con[1], chip->wake_mask[1]); 14162306a36Sopenharmony_ci enable_irq_wake(chip->irq); 14262306a36Sopenharmony_ci break; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci case PM_POST_SUSPEND: 14562306a36Sopenharmony_ci regmap_write(chip->regmap, 14662306a36Sopenharmony_ci chip->int_con[0], chip->irq_masks_cur[0]); 14762306a36Sopenharmony_ci regmap_write(chip->regmap, 14862306a36Sopenharmony_ci chip->int_con[1], chip->irq_masks_cur[1]); 14962306a36Sopenharmony_ci disable_irq_wake(chip->irq); 15062306a36Sopenharmony_ci break; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci default: 15362306a36Sopenharmony_ci break; 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci return NOTIFY_DONE; 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ciint mt6397_irq_init(struct mt6397_chip *chip) 16062306a36Sopenharmony_ci{ 16162306a36Sopenharmony_ci int ret; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci mutex_init(&chip->irqlock); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci switch (chip->chip_id) { 16662306a36Sopenharmony_ci case MT6323_CHIP_ID: 16762306a36Sopenharmony_ci chip->int_con[0] = MT6323_INT_CON0; 16862306a36Sopenharmony_ci chip->int_con[1] = MT6323_INT_CON1; 16962306a36Sopenharmony_ci chip->int_status[0] = MT6323_INT_STATUS0; 17062306a36Sopenharmony_ci chip->int_status[1] = MT6323_INT_STATUS1; 17162306a36Sopenharmony_ci break; 17262306a36Sopenharmony_ci case MT6331_CHIP_ID: 17362306a36Sopenharmony_ci chip->int_con[0] = MT6331_INT_CON0; 17462306a36Sopenharmony_ci chip->int_con[1] = MT6331_INT_CON1; 17562306a36Sopenharmony_ci chip->int_status[0] = MT6331_INT_STATUS_CON0; 17662306a36Sopenharmony_ci chip->int_status[1] = MT6331_INT_STATUS_CON1; 17762306a36Sopenharmony_ci break; 17862306a36Sopenharmony_ci case MT6391_CHIP_ID: 17962306a36Sopenharmony_ci case MT6397_CHIP_ID: 18062306a36Sopenharmony_ci chip->int_con[0] = MT6397_INT_CON0; 18162306a36Sopenharmony_ci chip->int_con[1] = MT6397_INT_CON1; 18262306a36Sopenharmony_ci chip->int_status[0] = MT6397_INT_STATUS0; 18362306a36Sopenharmony_ci chip->int_status[1] = MT6397_INT_STATUS1; 18462306a36Sopenharmony_ci break; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci default: 18762306a36Sopenharmony_ci dev_err(chip->dev, "unsupported chip: 0x%x\n", chip->chip_id); 18862306a36Sopenharmony_ci return -ENODEV; 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci /* Mask all interrupt sources */ 19262306a36Sopenharmony_ci regmap_write(chip->regmap, chip->int_con[0], 0x0); 19362306a36Sopenharmony_ci regmap_write(chip->regmap, chip->int_con[1], 0x0); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci chip->pm_nb.notifier_call = mt6397_irq_pm_notifier; 19662306a36Sopenharmony_ci chip->irq_domain = irq_domain_add_linear(chip->dev->of_node, 19762306a36Sopenharmony_ci MT6397_IRQ_NR, 19862306a36Sopenharmony_ci &mt6397_irq_domain_ops, 19962306a36Sopenharmony_ci chip); 20062306a36Sopenharmony_ci if (!chip->irq_domain) { 20162306a36Sopenharmony_ci dev_err(chip->dev, "could not create irq domain\n"); 20262306a36Sopenharmony_ci return -ENOMEM; 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci ret = devm_request_threaded_irq(chip->dev, chip->irq, NULL, 20662306a36Sopenharmony_ci mt6397_irq_thread, IRQF_ONESHOT, 20762306a36Sopenharmony_ci "mt6397-pmic", chip); 20862306a36Sopenharmony_ci if (ret) { 20962306a36Sopenharmony_ci dev_err(chip->dev, "failed to register irq=%d; err: %d\n", 21062306a36Sopenharmony_ci chip->irq, ret); 21162306a36Sopenharmony_ci return ret; 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci register_pm_notifier(&chip->pm_nb); 21562306a36Sopenharmony_ci return 0; 21662306a36Sopenharmony_ci} 217