2 * TI LP8788 MFD - interrupt handler
4 * Copyright 2012 Texas Instruments
6 * Author: Milo(Woogyom) Kim <milo.kim@ti.com>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
14 #include <linux/delay.h>
15 #include <linux/err.h>
16 #include <linux/interrupt.h>
17 #include <linux/irq.h>
18 #include <linux/irqdomain.h>
19 #include <linux/device.h>
20 #include <linux/mfd/lp8788.h>
21 #include <linux/module.h>
22 #include <linux/slab.h>
24 /* register address */
25 #define LP8788_INT_1 0x00
26 #define LP8788_INTEN_1 0x03
28 #define BASE_INTEN_ADDR LP8788_INTEN_1
33 * struct lp8788_irq_data
34 * @lp : used for accessing to lp8788 registers
35 * @irq_lock : mutex for enabling/disabling the interrupt
36 * @domain : IRQ domain for handling nested interrupt
37 * @enabled : status of enabled interrupt
39 struct lp8788_irq_data
{
41 struct mutex irq_lock
;
42 struct irq_domain
*domain
;
43 int enabled
[LP8788_INT_MAX
];
46 static inline u8
_irq_to_addr(enum lp8788_int_id id
)
51 static inline u8
_irq_to_enable_addr(enum lp8788_int_id id
)
53 return _irq_to_addr(id
) + BASE_INTEN_ADDR
;
56 static inline u8
_irq_to_mask(enum lp8788_int_id id
)
58 return 1 << (id
% SIZE_REG
);
61 static inline u8
_irq_to_val(enum lp8788_int_id id
, int enable
)
63 return enable
<< (id
% SIZE_REG
);
66 static void lp8788_irq_enable(struct irq_data
*data
)
68 struct lp8788_irq_data
*irqd
= irq_data_get_irq_chip_data(data
);
70 irqd
->enabled
[data
->hwirq
] = 1;
73 static void lp8788_irq_disable(struct irq_data
*data
)
75 struct lp8788_irq_data
*irqd
= irq_data_get_irq_chip_data(data
);
77 irqd
->enabled
[data
->hwirq
] = 0;
80 static void lp8788_irq_bus_lock(struct irq_data
*data
)
82 struct lp8788_irq_data
*irqd
= irq_data_get_irq_chip_data(data
);
84 mutex_lock(&irqd
->irq_lock
);
87 static void lp8788_irq_bus_sync_unlock(struct irq_data
*data
)
89 struct lp8788_irq_data
*irqd
= irq_data_get_irq_chip_data(data
);
90 enum lp8788_int_id irq
= data
->hwirq
;
93 addr
= _irq_to_enable_addr(irq
);
94 mask
= _irq_to_mask(irq
);
95 val
= _irq_to_val(irq
, irqd
->enabled
[irq
]);
97 lp8788_update_bits(irqd
->lp
, addr
, mask
, val
);
99 mutex_unlock(&irqd
->irq_lock
);
102 static struct irq_chip lp8788_irq_chip
= {
104 .irq_enable
= lp8788_irq_enable
,
105 .irq_disable
= lp8788_irq_disable
,
106 .irq_bus_lock
= lp8788_irq_bus_lock
,
107 .irq_bus_sync_unlock
= lp8788_irq_bus_sync_unlock
,
110 static irqreturn_t
lp8788_irq_handler(int irq
, void *ptr
)
112 struct lp8788_irq_data
*irqd
= ptr
;
113 struct lp8788
*lp
= irqd
->lp
;
114 u8 status
[NUM_REGS
], addr
, mask
;
118 if (lp8788_read_multi_bytes(lp
, LP8788_INT_1
, status
, NUM_REGS
))
121 for (i
= 0 ; i
< LP8788_INT_MAX
; i
++) {
122 addr
= _irq_to_addr(i
);
123 mask
= _irq_to_mask(i
);
125 /* reporting only if the irq is enabled */
126 if (status
[addr
] & mask
) {
127 handle_nested_irq(irq_find_mapping(irqd
->domain
, i
));
132 return handled
? IRQ_HANDLED
: IRQ_NONE
;
135 static int lp8788_irq_map(struct irq_domain
*d
, unsigned int virq
,
136 irq_hw_number_t hwirq
)
138 struct lp8788_irq_data
*irqd
= d
->host_data
;
139 struct irq_chip
*chip
= &lp8788_irq_chip
;
141 irq_set_chip_data(virq
, irqd
);
142 irq_set_chip_and_handler(virq
, chip
, handle_edge_irq
);
143 irq_set_nested_thread(virq
, 1);
144 irq_set_noprobe(virq
);
149 static const struct irq_domain_ops lp8788_domain_ops
= {
150 .map
= lp8788_irq_map
,
153 int lp8788_irq_init(struct lp8788
*lp
, int irq
)
155 struct lp8788_irq_data
*irqd
;
159 dev_warn(lp
->dev
, "invalid irq number: %d\n", irq
);
163 irqd
= devm_kzalloc(lp
->dev
, sizeof(*irqd
), GFP_KERNEL
);
168 irqd
->domain
= irq_domain_add_linear(lp
->dev
->of_node
, LP8788_INT_MAX
,
169 &lp8788_domain_ops
, irqd
);
171 dev_err(lp
->dev
, "failed to add irq domain err\n");
175 lp
->irqdm
= irqd
->domain
;
176 mutex_init(&irqd
->irq_lock
);
178 ret
= request_threaded_irq(irq
, NULL
, lp8788_irq_handler
,
179 IRQF_TRIGGER_FALLING
| IRQF_ONESHOT
,
182 dev_err(lp
->dev
, "failed to create a thread for IRQ_N\n");
191 void lp8788_irq_exit(struct lp8788
*lp
)
194 free_irq(lp
->irq
, lp
->irqdm
);