Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * TI LP8788 MFD - interrupt handler
0004  *
0005  * Copyright 2012 Texas Instruments
0006  *
0007  * Author: Milo(Woogyom) Kim <milo.kim@ti.com>
0008  */
0009 
0010 #include <linux/delay.h>
0011 #include <linux/err.h>
0012 #include <linux/interrupt.h>
0013 #include <linux/irq.h>
0014 #include <linux/irqdomain.h>
0015 #include <linux/device.h>
0016 #include <linux/mfd/lp8788.h>
0017 #include <linux/module.h>
0018 #include <linux/slab.h>
0019 
0020 /* register address */
0021 #define LP8788_INT_1            0x00
0022 #define LP8788_INTEN_1          0x03
0023 
0024 #define BASE_INTEN_ADDR         LP8788_INTEN_1
0025 #define SIZE_REG            8
0026 #define NUM_REGS            3
0027 
0028 /*
0029  * struct lp8788_irq_data
0030  * @lp               : used for accessing to lp8788 registers
0031  * @irq_lock         : mutex for enabling/disabling the interrupt
0032  * @domain           : IRQ domain for handling nested interrupt
0033  * @enabled          : status of enabled interrupt
0034  */
0035 struct lp8788_irq_data {
0036     struct lp8788 *lp;
0037     struct mutex irq_lock;
0038     struct irq_domain *domain;
0039     int enabled[LP8788_INT_MAX];
0040 };
0041 
0042 static inline u8 _irq_to_addr(enum lp8788_int_id id)
0043 {
0044     return id / SIZE_REG;
0045 }
0046 
0047 static inline u8 _irq_to_enable_addr(enum lp8788_int_id id)
0048 {
0049     return _irq_to_addr(id) + BASE_INTEN_ADDR;
0050 }
0051 
0052 static inline u8 _irq_to_mask(enum lp8788_int_id id)
0053 {
0054     return 1 << (id % SIZE_REG);
0055 }
0056 
0057 static inline u8 _irq_to_val(enum lp8788_int_id id, int enable)
0058 {
0059     return enable << (id % SIZE_REG);
0060 }
0061 
0062 static void lp8788_irq_enable(struct irq_data *data)
0063 {
0064     struct lp8788_irq_data *irqd = irq_data_get_irq_chip_data(data);
0065 
0066     irqd->enabled[data->hwirq] = 1;
0067 }
0068 
0069 static void lp8788_irq_disable(struct irq_data *data)
0070 {
0071     struct lp8788_irq_data *irqd = irq_data_get_irq_chip_data(data);
0072 
0073     irqd->enabled[data->hwirq] = 0;
0074 }
0075 
0076 static void lp8788_irq_bus_lock(struct irq_data *data)
0077 {
0078     struct lp8788_irq_data *irqd = irq_data_get_irq_chip_data(data);
0079 
0080     mutex_lock(&irqd->irq_lock);
0081 }
0082 
0083 static void lp8788_irq_bus_sync_unlock(struct irq_data *data)
0084 {
0085     struct lp8788_irq_data *irqd = irq_data_get_irq_chip_data(data);
0086     enum lp8788_int_id irq = data->hwirq;
0087     u8 addr, mask, val;
0088 
0089     addr = _irq_to_enable_addr(irq);
0090     mask = _irq_to_mask(irq);
0091     val = _irq_to_val(irq, irqd->enabled[irq]);
0092 
0093     lp8788_update_bits(irqd->lp, addr, mask, val);
0094 
0095     mutex_unlock(&irqd->irq_lock);
0096 }
0097 
0098 static struct irq_chip lp8788_irq_chip = {
0099     .name           = "lp8788",
0100     .irq_enable     = lp8788_irq_enable,
0101     .irq_disable        = lp8788_irq_disable,
0102     .irq_bus_lock       = lp8788_irq_bus_lock,
0103     .irq_bus_sync_unlock    = lp8788_irq_bus_sync_unlock,
0104 };
0105 
0106 static irqreturn_t lp8788_irq_handler(int irq, void *ptr)
0107 {
0108     struct lp8788_irq_data *irqd = ptr;
0109     struct lp8788 *lp = irqd->lp;
0110     u8 status[NUM_REGS], addr, mask;
0111     bool handled = false;
0112     int i;
0113 
0114     if (lp8788_read_multi_bytes(lp, LP8788_INT_1, status, NUM_REGS))
0115         return IRQ_NONE;
0116 
0117     for (i = 0 ; i < LP8788_INT_MAX ; i++) {
0118         addr = _irq_to_addr(i);
0119         mask = _irq_to_mask(i);
0120 
0121         /* reporting only if the irq is enabled */
0122         if (status[addr] & mask) {
0123             handle_nested_irq(irq_find_mapping(irqd->domain, i));
0124             handled = true;
0125         }
0126     }
0127 
0128     return handled ? IRQ_HANDLED : IRQ_NONE;
0129 }
0130 
0131 static int lp8788_irq_map(struct irq_domain *d, unsigned int virq,
0132             irq_hw_number_t hwirq)
0133 {
0134     struct lp8788_irq_data *irqd = d->host_data;
0135     struct irq_chip *chip = &lp8788_irq_chip;
0136 
0137     irq_set_chip_data(virq, irqd);
0138     irq_set_chip_and_handler(virq, chip, handle_edge_irq);
0139     irq_set_nested_thread(virq, 1);
0140     irq_set_noprobe(virq);
0141 
0142     return 0;
0143 }
0144 
0145 static const struct irq_domain_ops lp8788_domain_ops = {
0146     .map = lp8788_irq_map,
0147 };
0148 
0149 int lp8788_irq_init(struct lp8788 *lp, int irq)
0150 {
0151     struct lp8788_irq_data *irqd;
0152     int ret;
0153 
0154     if (irq <= 0) {
0155         dev_warn(lp->dev, "invalid irq number: %d\n", irq);
0156         return 0;
0157     }
0158 
0159     irqd = devm_kzalloc(lp->dev, sizeof(*irqd), GFP_KERNEL);
0160     if (!irqd)
0161         return -ENOMEM;
0162 
0163     irqd->lp = lp;
0164     irqd->domain = irq_domain_add_linear(lp->dev->of_node, LP8788_INT_MAX,
0165                     &lp8788_domain_ops, irqd);
0166     if (!irqd->domain) {
0167         dev_err(lp->dev, "failed to add irq domain err\n");
0168         return -EINVAL;
0169     }
0170 
0171     lp->irqdm = irqd->domain;
0172     mutex_init(&irqd->irq_lock);
0173 
0174     ret = request_threaded_irq(irq, NULL, lp8788_irq_handler,
0175                 IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
0176                 "lp8788-irq", irqd);
0177     if (ret) {
0178         dev_err(lp->dev, "failed to create a thread for IRQ_N\n");
0179         return ret;
0180     }
0181 
0182     lp->irq = irq;
0183 
0184     return 0;
0185 }
0186 
0187 void lp8788_irq_exit(struct lp8788 *lp)
0188 {
0189     if (lp->irq)
0190         free_irq(lp->irq, lp->irqdm);
0191 }