blob: d4890a6dd6a483c1422748a13f7b09cad4f39f86 [file] [log] [blame]
Meng Wang43bbb872018-12-10 12:32:05 +08001// SPDX-License-Identifier: GPL-2.0-only
Sudheer Papothi594cedb2019-08-15 15:30:24 +05302/* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
Vatsal Bucha485ec2c2018-07-09 19:34:44 +05303 */
4
5#include <linux/kernel.h>
6#include <linux/module.h>
7#include <linux/irq.h>
8#include <linux/of.h>
9#include <linux/of_irq.h>
10#include <linux/slab.h>
11#include <linux/irqdomain.h>
12#include <linux/regmap.h>
13#include <asoc/wcd-irq.h>
14
15static int wcd_map_irq(struct wcd_irq_info *irq_info, int irq)
16{
17 if (!irq_info) {
18 pr_err("%s: Null IRQ handle\n", __func__);
19 return -EINVAL;
20 }
21 return regmap_irq_get_virq(irq_info->irq_chip, irq);
22}
23
24/**
25 * wcd_request_irq: Request a thread handler for the given IRQ
26 * @irq_info: pointer to IRQ info structure
27 * @irq: irq number
28 * @name: name for the IRQ thread
29 * @handler: irq handler
30 * @data: data pointer
31 *
32 * Returns 0 on success or error on failure
33 */
34int wcd_request_irq(struct wcd_irq_info *irq_info, int irq, const char *name,
35 irq_handler_t handler, void *data)
36{
37 if (!irq_info) {
38 pr_err("%s: Null IRQ handle\n", __func__);
39 return -EINVAL;
40 }
41 irq = wcd_map_irq(irq_info, irq);
42 if (irq < 0)
43 return irq;
44
45 return request_threaded_irq(irq, NULL, handler,
46 IRQF_ONESHOT | IRQF_TRIGGER_RISING,
47 name, data);
48}
49EXPORT_SYMBOL(wcd_request_irq);
50
51/**
52 * wcd_free_irq: Free the IRQ resources allocated during request_irq
53 * @irq_info: pointer to IRQ info structure
54 * @irq: irq number
55 * @data: data pointer
56 */
57void wcd_free_irq(struct wcd_irq_info *irq_info, int irq, void *data)
58{
59 if (!irq_info) {
60 pr_err("%s: Null IRQ handle\n", __func__);
61 return;
62 }
63
64 irq = wcd_map_irq(irq_info, irq);
65 if (irq < 0)
66 return;
67
68 free_irq(irq, data);
69}
70EXPORT_SYMBOL(wcd_free_irq);
71
72/**
73 * wcd_enable_irq: Enable the given IRQ
74 * @irq_info: pointer to IRQ info structure
75 * @irq: irq number
76 */
77void wcd_enable_irq(struct wcd_irq_info *irq_info, int irq)
78{
79 if (!irq_info)
80 pr_err("%s: Null IRQ handle\n", __func__);
81 else
82 enable_irq(wcd_map_irq(irq_info, irq));
83}
84EXPORT_SYMBOL(wcd_enable_irq);
85
86/**
87 * wcd_disable_irq: Disable the given IRQ
88 * @irq_info: pointer to IRQ info structure
89 * @irq: irq number
90 */
91void wcd_disable_irq(struct wcd_irq_info *irq_info, int irq)
92{
93 if (!irq_info)
94 pr_err("%s: Null IRQ handle\n", __func__);
95 else
Ramprasad Katkam6af0c152018-09-20 18:01:39 +053096 disable_irq_nosync(wcd_map_irq(irq_info, irq));
Vatsal Bucha485ec2c2018-07-09 19:34:44 +053097}
98EXPORT_SYMBOL(wcd_disable_irq);
99
100static void wcd_irq_chip_disable(struct irq_data *data)
101{
102}
103
104static void wcd_irq_chip_enable(struct irq_data *data)
105{
106}
107
108static struct irq_chip wcd_irq_chip = {
109 .name = NULL,
110 .irq_disable = wcd_irq_chip_disable,
111 .irq_enable = wcd_irq_chip_enable,
112};
113
114static struct lock_class_key wcd_irq_lock_class;
Meng Wang15c825d2018-09-06 10:49:18 +0800115static struct lock_class_key wcd_irq_lock_requested_class;
Vatsal Bucha485ec2c2018-07-09 19:34:44 +0530116
117static int wcd_irq_chip_map(struct irq_domain *irqd, unsigned int virq,
118 irq_hw_number_t hw)
119{
120 irq_set_chip_and_handler(virq, &wcd_irq_chip, handle_simple_irq);
Meng Wang15c825d2018-09-06 10:49:18 +0800121 irq_set_lockdep_class(virq, &wcd_irq_lock_class,
122 &wcd_irq_lock_requested_class);
Vatsal Bucha485ec2c2018-07-09 19:34:44 +0530123 irq_set_nested_thread(virq, 1);
124 irq_set_noprobe(virq);
125
126 return 0;
127}
128
129static const struct irq_domain_ops wcd_domain_ops = {
130 .map = wcd_irq_chip_map,
131};
132
133/**
134 * wcd_irq_init: Initializes IRQ module
135 * @irq_info: pointer to IRQ info structure
136 *
137 * Returns 0 on success or error on failure
138 */
139int wcd_irq_init(struct wcd_irq_info *irq_info, struct irq_domain **virq)
140{
141 int ret = 0;
142
143 if (!irq_info) {
144 pr_err("%s: Null IRQ handle\n", __func__);
145 return -EINVAL;
146 }
147
148 wcd_irq_chip.name = irq_info->codec_name;
149
150 *virq = irq_domain_add_linear(NULL, 1, &wcd_domain_ops, NULL);
151 if (!(*virq)) {
152 pr_err("%s: Failed to add IRQ domain\n", __func__);
153 return -EINVAL;
154 }
155
156 ret = devm_regmap_add_irq_chip(irq_info->dev, irq_info->regmap,
157 irq_create_mapping(*virq, 0),
158 IRQF_ONESHOT, 0, irq_info->wcd_regmap_irq_chip,
159 &irq_info->irq_chip);
160 if (ret)
161 pr_err("%s: Failed to add IRQs: %d\n",
162 __func__, ret);
163
164 return ret;
165}
166EXPORT_SYMBOL(wcd_irq_init);
167
168/**
169 * wcd_irq_exit: Uninitialize regmap IRQ and free IRQ resources
170 * @irq_info: pointer to IRQ info structure
171 *
172 * Returns 0 on success or error on failure
173 */
174int wcd_irq_exit(struct wcd_irq_info *irq_info, struct irq_domain *virq)
175{
176 if (!irq_info) {
177 pr_err("%s: Null pointer handle\n", __func__);
178 return -EINVAL;
179 }
180
Sudheer Papothi594cedb2019-08-15 15:30:24 +0530181 devm_regmap_del_irq_chip(irq_info->dev, irq_find_mapping(virq, 0),
182 irq_info->irq_chip);
Vatsal Bucha485ec2c2018-07-09 19:34:44 +0530183
184 return 0;
185}
186EXPORT_SYMBOL(wcd_irq_exit);