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
);
69 irqd
->enabled
[data
->hwirq
] = 1;
72 static void lp8788_irq_disable(struct irq_data
*data
)
74 struct lp8788_irq_data
*irqd
= irq_data_get_irq_chip_data(data
);
75 irqd
->enabled
[data
->hwirq
] = 0;
78 static void lp8788_irq_bus_lock(struct irq_data
*data
)
80 struct lp8788_irq_data
*irqd
= irq_data_get_irq_chip_data(data
);
82 mutex_lock(&irqd
->irq_lock
);
85 static void lp8788_irq_bus_sync_unlock(struct irq_data
*data
)
87 struct lp8788_irq_data
*irqd
= irq_data_get_irq_chip_data(data
);
88 enum lp8788_int_id irq
= data
->hwirq
;
91 addr
= _irq_to_enable_addr(irq
);
92 mask
= _irq_to_mask(irq
);
93 val
= _irq_to_val(irq
, irqd
->enabled
[irq
]);
95 lp8788_update_bits(irqd
->lp
, addr
, mask
, val
);
97 mutex_unlock(&irqd
->irq_lock
);
100 static struct irq_chip lp8788_irq_chip
= {
102 .irq_enable
= lp8788_irq_enable
,
103 .irq_disable
= lp8788_irq_disable
,
104 .irq_bus_lock
= lp8788_irq_bus_lock
,
105 .irq_bus_sync_unlock
= lp8788_irq_bus_sync_unlock
,
108 static irqreturn_t
lp8788_irq_handler(int irq
, void *ptr
)
110 struct lp8788_irq_data
*irqd
= ptr
;
111 struct lp8788
*lp
= irqd
->lp
;
112 u8 status
[NUM_REGS
], addr
, mask
;
116 if (lp8788_read_multi_bytes(lp
, LP8788_INT_1
, status
, NUM_REGS
))
119 for (i
= 0 ; i
< LP8788_INT_MAX
; i
++) {
120 addr
= _irq_to_addr(i
);
121 mask
= _irq_to_mask(i
);
123 /* reporting only if the irq is enabled */
124 if (status
[addr
] & mask
) {
125 handle_nested_irq(irq_find_mapping(irqd
->domain
, i
));
130 return handled
? IRQ_HANDLED
: IRQ_NONE
;
133 static int lp8788_irq_map(struct irq_domain
*d
, unsigned int virq
,
134 irq_hw_number_t hwirq
)
136 struct lp8788_irq_data
*irqd
= d
->host_data
;
137 struct irq_chip
*chip
= &lp8788_irq_chip
;
139 irq_set_chip_data(virq
, irqd
);
140 irq_set_chip_and_handler(virq
, chip
, handle_edge_irq
);
141 irq_set_nested_thread(virq
, 1);
144 set_irq_flags(virq
, IRQF_VALID
);
146 irq_set_noprobe(virq
);
152 static struct irq_domain_ops lp8788_domain_ops
= {
153 .map
= lp8788_irq_map
,
156 int lp8788_irq_init(struct lp8788
*lp
, int irq
)
158 struct lp8788_irq_data
*irqd
;
162 dev_warn(lp
->dev
, "invalid irq number: %d\n", irq
);
166 irqd
= devm_kzalloc(lp
->dev
, sizeof(*irqd
), GFP_KERNEL
);
171 irqd
->domain
= irq_domain_add_linear(lp
->dev
->of_node
, LP8788_INT_MAX
,
172 &lp8788_domain_ops
, irqd
);
174 dev_err(lp
->dev
, "failed to add irq domain err\n");
178 lp
->irqdm
= irqd
->domain
;
179 mutex_init(&irqd
->irq_lock
);
181 ret
= request_threaded_irq(irq
, NULL
, lp8788_irq_handler
,
182 IRQF_TRIGGER_FALLING
| IRQF_ONESHOT
,
185 dev_err(lp
->dev
, "failed to create a thread for IRQ_N\n");
194 void lp8788_irq_exit(struct lp8788
*lp
)
197 free_irq(lp
->irq
, lp
->irqdm
);