blob: 12bbf36a52092d054d6fdcc587efaab39d7d3795 [file] [log] [blame]
Vatsal Bucha485ec2c2018-07-09 19:34:44 +05301/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#include <linux/kernel.h>
14#include <linux/module.h>
15#include <linux/irq.h>
16#include <linux/of.h>
17#include <linux/of_irq.h>
18#include <linux/slab.h>
19#include <linux/irqdomain.h>
20#include <linux/regmap.h>
21#include <asoc/wcd-irq.h>
22
23static int wcd_map_irq(struct wcd_irq_info *irq_info, int irq)
24{
25 if (!irq_info) {
26 pr_err("%s: Null IRQ handle\n", __func__);
27 return -EINVAL;
28 }
29 return regmap_irq_get_virq(irq_info->irq_chip, irq);
30}
31
32/**
33 * wcd_request_irq: Request a thread handler for the given IRQ
34 * @irq_info: pointer to IRQ info structure
35 * @irq: irq number
36 * @name: name for the IRQ thread
37 * @handler: irq handler
38 * @data: data pointer
39 *
40 * Returns 0 on success or error on failure
41 */
42int wcd_request_irq(struct wcd_irq_info *irq_info, int irq, const char *name,
43 irq_handler_t handler, void *data)
44{
45 if (!irq_info) {
46 pr_err("%s: Null IRQ handle\n", __func__);
47 return -EINVAL;
48 }
49 irq = wcd_map_irq(irq_info, irq);
50 if (irq < 0)
51 return irq;
52
53 return request_threaded_irq(irq, NULL, handler,
54 IRQF_ONESHOT | IRQF_TRIGGER_RISING,
55 name, data);
56}
57EXPORT_SYMBOL(wcd_request_irq);
58
59/**
60 * wcd_free_irq: Free the IRQ resources allocated during request_irq
61 * @irq_info: pointer to IRQ info structure
62 * @irq: irq number
63 * @data: data pointer
64 */
65void wcd_free_irq(struct wcd_irq_info *irq_info, int irq, void *data)
66{
67 if (!irq_info) {
68 pr_err("%s: Null IRQ handle\n", __func__);
69 return;
70 }
71
72 irq = wcd_map_irq(irq_info, irq);
73 if (irq < 0)
74 return;
75
76 free_irq(irq, data);
77}
78EXPORT_SYMBOL(wcd_free_irq);
79
80/**
81 * wcd_enable_irq: Enable the given IRQ
82 * @irq_info: pointer to IRQ info structure
83 * @irq: irq number
84 */
85void wcd_enable_irq(struct wcd_irq_info *irq_info, int irq)
86{
87 if (!irq_info)
88 pr_err("%s: Null IRQ handle\n", __func__);
89 else
90 enable_irq(wcd_map_irq(irq_info, irq));
91}
92EXPORT_SYMBOL(wcd_enable_irq);
93
94/**
95 * wcd_disable_irq: Disable the given IRQ
96 * @irq_info: pointer to IRQ info structure
97 * @irq: irq number
98 */
99void wcd_disable_irq(struct wcd_irq_info *irq_info, int irq)
100{
101 if (!irq_info)
102 pr_err("%s: Null IRQ handle\n", __func__);
103 else
104 disable_irq(wcd_map_irq(irq_info, irq));
105}
106EXPORT_SYMBOL(wcd_disable_irq);
107
108static void wcd_irq_chip_disable(struct irq_data *data)
109{
110}
111
112static void wcd_irq_chip_enable(struct irq_data *data)
113{
114}
115
116static struct irq_chip wcd_irq_chip = {
117 .name = NULL,
118 .irq_disable = wcd_irq_chip_disable,
119 .irq_enable = wcd_irq_chip_enable,
120};
121
122static struct lock_class_key wcd_irq_lock_class;
123
124static int wcd_irq_chip_map(struct irq_domain *irqd, unsigned int virq,
125 irq_hw_number_t hw)
126{
127 irq_set_chip_and_handler(virq, &wcd_irq_chip, handle_simple_irq);
128 irq_set_lockdep_class(virq, &wcd_irq_lock_class);
129 irq_set_nested_thread(virq, 1);
130 irq_set_noprobe(virq);
131
132 return 0;
133}
134
135static const struct irq_domain_ops wcd_domain_ops = {
136 .map = wcd_irq_chip_map,
137};
138
139/**
140 * wcd_irq_init: Initializes IRQ module
141 * @irq_info: pointer to IRQ info structure
142 *
143 * Returns 0 on success or error on failure
144 */
145int wcd_irq_init(struct wcd_irq_info *irq_info, struct irq_domain **virq)
146{
147 int ret = 0;
148
149 if (!irq_info) {
150 pr_err("%s: Null IRQ handle\n", __func__);
151 return -EINVAL;
152 }
153
154 wcd_irq_chip.name = irq_info->codec_name;
155
156 *virq = irq_domain_add_linear(NULL, 1, &wcd_domain_ops, NULL);
157 if (!(*virq)) {
158 pr_err("%s: Failed to add IRQ domain\n", __func__);
159 return -EINVAL;
160 }
161
162 ret = devm_regmap_add_irq_chip(irq_info->dev, irq_info->regmap,
163 irq_create_mapping(*virq, 0),
164 IRQF_ONESHOT, 0, irq_info->wcd_regmap_irq_chip,
165 &irq_info->irq_chip);
166 if (ret)
167 pr_err("%s: Failed to add IRQs: %d\n",
168 __func__, ret);
169
170 return ret;
171}
172EXPORT_SYMBOL(wcd_irq_init);
173
174/**
175 * wcd_irq_exit: Uninitialize regmap IRQ and free IRQ resources
176 * @irq_info: pointer to IRQ info structure
177 *
178 * Returns 0 on success or error on failure
179 */
180int wcd_irq_exit(struct wcd_irq_info *irq_info, struct irq_domain *virq)
181{
182 if (!irq_info) {
183 pr_err("%s: Null pointer handle\n", __func__);
184 return -EINVAL;
185 }
186
187 regmap_del_irq_chip(irq_find_mapping(virq, 0), irq_info->irq_chip);
188
189 return 0;
190}
191EXPORT_SYMBOL(wcd_irq_exit);