root/drivers/misc/hi6421v600-irq.c
// SPDX-License-Identifier: GPL-2.0
/*
 * Device driver for irqs in HISI PMIC IC
 *
 * Copyright (c) 2013 Linaro Ltd.
 * Copyright (c) 2011 Hisilicon.
 * Copyright (c) 2020-2021 Huawei Technologies Co., Ltd.
 */

#include <linux/bitops.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/irqdomain.h>
#include <linux/regmap.h>

struct hi6421v600_irq {
        struct device           *dev;
        struct irq_domain       *domain;
        int                     irq;
        unsigned int            *irqs;
        struct regmap           *regmap;

        /* Protect IRQ mask changes */
        spinlock_t              lock;
};

enum hi6421v600_irq_list {
        OTMP = 0,
        VBUS_CONNECT,
        VBUS_DISCONNECT,
        ALARMON_R,
        HOLD_6S,
        HOLD_1S,
        POWERKEY_UP,
        POWERKEY_DOWN,
        OCP_SCP_R,
        COUL_R,
        SIM0_HPD_R,
        SIM0_HPD_F,
        SIM1_HPD_R,
        SIM1_HPD_F,

        PMIC_IRQ_LIST_MAX
};

#define HISI_IRQ_BANK_SIZE              2

/*
 * IRQ number for the power key button and mask for both UP and DOWN IRQs
 */
#define HISI_POWERKEY_IRQ_NUM           0
#define HISI_IRQ_POWERKEY_UP_DOWN       (BIT(POWERKEY_DOWN) | BIT(POWERKEY_UP))

/*
 * Registers for IRQ address and IRQ mask bits
 *
 * Please notice that we need to regmap a larger region, as other
 * registers are used by the irqs.
 * See drivers/irq/hi6421-irq.c.
 */
#define SOC_PMIC_IRQ_MASK_0_ADDR        0x0202
#define SOC_PMIC_IRQ0_ADDR              0x0212

/*
 * The IRQs are mapped as:
 *
 *      ======================  =============   ============    =====
 *      IRQ                     MASK REGISTER   IRQ REGISTER    BIT
 *      ======================  =============   ============    =====
 *      OTMP                    0x0202          0x212           bit 0
 *      VBUS_CONNECT            0x0202          0x212           bit 1
 *      VBUS_DISCONNECT         0x0202          0x212           bit 2
 *      ALARMON_R               0x0202          0x212           bit 3
 *      HOLD_6S                 0x0202          0x212           bit 4
 *      HOLD_1S                 0x0202          0x212           bit 5
 *      POWERKEY_UP             0x0202          0x212           bit 6
 *      POWERKEY_DOWN           0x0202          0x212           bit 7
 *
 *      OCP_SCP_R               0x0203          0x213           bit 0
 *      COUL_R                  0x0203          0x213           bit 1
 *      SIM0_HPD_R              0x0203          0x213           bit 2
 *      SIM0_HPD_F              0x0203          0x213           bit 3
 *      SIM1_HPD_R              0x0203          0x213           bit 4
 *      SIM1_HPD_F              0x0203          0x213           bit 5
 *      ======================  =============   ============    =====
 *
 * Each mask register contains 8 bits. The ancillary macros below
 * convert a number from 0 to 14 into a register address and a bit mask
 */
#define HISI_IRQ_MASK_REG(irq_data)     (SOC_PMIC_IRQ_MASK_0_ADDR + \
                                         (irqd_to_hwirq(irq_data) / BITS_PER_BYTE))
#define HISI_IRQ_MASK_BIT(irq_data)     BIT(irqd_to_hwirq(irq_data) & (BITS_PER_BYTE - 1))
#define HISI_8BITS_MASK                 0xff

static irqreturn_t hi6421v600_irq_handler(int irq, void *__priv)
{
        struct hi6421v600_irq *priv = __priv;
        unsigned long pending;
        unsigned int in;
        int i, offset;

        for (i = 0; i < HISI_IRQ_BANK_SIZE; i++) {
                regmap_read(priv->regmap, SOC_PMIC_IRQ0_ADDR + i, &in);

                /* Mark pending IRQs as handled */
                regmap_write(priv->regmap, SOC_PMIC_IRQ0_ADDR + i, in);

                pending = in & HISI_8BITS_MASK;

                if (i == HISI_POWERKEY_IRQ_NUM &&
                    (pending & HISI_IRQ_POWERKEY_UP_DOWN) == HISI_IRQ_POWERKEY_UP_DOWN) {
                        /*
                         * If both powerkey down and up IRQs are received,
                         * handle them at the right order
                         */
                        generic_handle_irq_safe(priv->irqs[POWERKEY_DOWN]);
                        generic_handle_irq_safe(priv->irqs[POWERKEY_UP]);
                        pending &= ~HISI_IRQ_POWERKEY_UP_DOWN;
                }

                if (!pending)
                        continue;

                for_each_set_bit(offset, &pending, BITS_PER_BYTE) {
                        generic_handle_irq_safe(priv->irqs[offset + i * BITS_PER_BYTE]);
                }
        }

        return IRQ_HANDLED;
}

static void hi6421v600_irq_mask(struct irq_data *d)
{
        struct hi6421v600_irq *priv = irq_data_get_irq_chip_data(d);
        unsigned long flags;
        unsigned int data;
        u32 offset;

        offset = HISI_IRQ_MASK_REG(d);

        spin_lock_irqsave(&priv->lock, flags);

        regmap_read(priv->regmap, offset, &data);
        data |= HISI_IRQ_MASK_BIT(d);
        regmap_write(priv->regmap, offset, data);

        spin_unlock_irqrestore(&priv->lock, flags);
}

static void hi6421v600_irq_unmask(struct irq_data *d)
{
        struct hi6421v600_irq *priv = irq_data_get_irq_chip_data(d);
        u32 data, offset;
        unsigned long flags;

        offset = HISI_IRQ_MASK_REG(d);

        spin_lock_irqsave(&priv->lock, flags);

        regmap_read(priv->regmap, offset, &data);
        data &= ~HISI_IRQ_MASK_BIT(d);
        regmap_write(priv->regmap, offset, data);

        spin_unlock_irqrestore(&priv->lock, flags);
}

static struct irq_chip hi6421v600_pmu_irqchip = {
        .name           = "hi6421v600-irq",
        .irq_mask       = hi6421v600_irq_mask,
        .irq_unmask     = hi6421v600_irq_unmask,
        .irq_disable    = hi6421v600_irq_mask,
        .irq_enable     = hi6421v600_irq_unmask,
};

static int hi6421v600_irq_map(struct irq_domain *d, unsigned int virq,
                              irq_hw_number_t hw)
{
        struct hi6421v600_irq *priv = d->host_data;

        irq_set_chip_and_handler_name(virq, &hi6421v600_pmu_irqchip,
                                      handle_simple_irq, "hi6421v600");
        irq_set_chip_data(virq, priv);
        irq_set_irq_type(virq, IRQ_TYPE_NONE);

        return 0;
}

static const struct irq_domain_ops hi6421v600_domain_ops = {
        .map    = hi6421v600_irq_map,
        .xlate  = irq_domain_xlate_twocell,
};

static void hi6421v600_irq_init(struct hi6421v600_irq *priv)
{
        int i;
        unsigned int pending;

        /* Mask all IRQs */
        for (i = 0; i < HISI_IRQ_BANK_SIZE; i++)
                regmap_write(priv->regmap, SOC_PMIC_IRQ_MASK_0_ADDR + i,
                             HISI_8BITS_MASK);

        /* Mark all IRQs as handled */
        for (i = 0; i < HISI_IRQ_BANK_SIZE; i++) {
                regmap_read(priv->regmap, SOC_PMIC_IRQ0_ADDR + i, &pending);
                regmap_write(priv->regmap, SOC_PMIC_IRQ0_ADDR + i,
                             HISI_8BITS_MASK);
        }
}

static int hi6421v600_irq_probe(struct platform_device *pdev)
{
        struct device *pmic_dev = pdev->dev.parent;
        struct platform_device *pmic_pdev;
        struct device *dev = &pdev->dev;
        struct hi6421v600_irq *priv;
        struct regmap *regmap;
        unsigned int virq;
        int i, ret;

        /*
         * This driver is meant to be called by hi6421-spmi-core,
         * which should first set drvdata. If this doesn't happen, hit
         * a warn on and return.
         */
        regmap = dev_get_drvdata(pmic_dev);
        if (WARN_ON(!regmap))
                return -ENODEV;

        priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
        if (!priv)
                return -ENOMEM;

        priv->dev = dev;
        priv->regmap = regmap;

        spin_lock_init(&priv->lock);

        pmic_pdev = container_of(pmic_dev, struct platform_device, dev);

        priv->irq = platform_get_irq(pmic_pdev, 0);
        if (priv->irq < 0)
                return priv->irq;

        platform_set_drvdata(pdev, priv);

        hi6421v600_irq_init(priv);

        priv->irqs = devm_kzalloc(dev, PMIC_IRQ_LIST_MAX * sizeof(int), GFP_KERNEL);
        if (!priv->irqs)
                return -ENOMEM;

        priv->domain = irq_domain_create_simple(dev_fwnode(pmic_dev), PMIC_IRQ_LIST_MAX, 0,
                                                &hi6421v600_domain_ops, priv);
        if (!priv->domain) {
                dev_err(dev, "Failed to create IRQ domain\n");
                return -ENODEV;
        }

        for (i = 0; i < PMIC_IRQ_LIST_MAX; i++) {
                virq = irq_create_mapping(priv->domain, i);
                if (!virq) {
                        dev_err(dev, "Failed to map H/W IRQ\n");
                        return -ENODEV;
                }
                priv->irqs[i] = virq;
        }

        ret = devm_request_threaded_irq(dev,
                                        priv->irq, hi6421v600_irq_handler,
                                        NULL,
                                        IRQF_TRIGGER_LOW | IRQF_SHARED | IRQF_NO_SUSPEND,
                                        "pmic", priv);
        if (ret < 0) {
                dev_err(dev, "Failed to start IRQ handling thread: error %d\n",
                        ret);
                return ret;
        }

        return 0;
}

static const struct platform_device_id hi6421v600_irq_table[] = {
        { .name = "hi6421v600-irq" },
        {},
};
MODULE_DEVICE_TABLE(platform, hi6421v600_irq_table);

static struct platform_driver hi6421v600_irq_driver = {
        .id_table = hi6421v600_irq_table,
        .driver = {
                .name = "hi6421v600-irq",
        },
        .probe  = hi6421v600_irq_probe,
};
module_platform_driver(hi6421v600_irq_driver);

MODULE_DESCRIPTION("HiSilicon Hi6421v600 IRQ driver");
MODULE_LICENSE("GPL v2");