xref: /linux/drivers/counter/interrupt-cnt.c (revision edbafe65eef2b58625db1e113fbbfb1fe10c0291)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2021 Pengutronix, Oleksij Rempel <kernel@pengutronix.de>
4  */
5 
6 #include <linux/cleanup.h>
7 #include <linux/counter.h>
8 #include <linux/gpio/consumer.h>
9 #include <linux/interrupt.h>
10 #include <linux/irq.h>
11 #include <linux/module.h>
12 #include <linux/mutex.h>
13 #include <linux/platform_device.h>
14 #include <linux/types.h>
15 
16 #define INTERRUPT_CNT_NAME "interrupt-cnt"
17 
18 struct interrupt_cnt_priv {
19 	atomic_long_t count;
20 	struct gpio_desc *gpio;
21 	int irq;
22 	bool enabled;
23 	struct mutex lock;
24 	struct counter_signal signals;
25 	struct counter_synapse synapses;
26 	struct counter_count cnts;
27 };
28 
29 static irqreturn_t interrupt_cnt_isr(int irq, void *dev_id)
30 {
31 	struct counter_device *counter = dev_id;
32 	struct interrupt_cnt_priv *priv = counter_priv(counter);
33 
34 	atomic_long_inc(&priv->count);
35 
36 	counter_push_event(counter, COUNTER_EVENT_CHANGE_OF_STATE, 0);
37 
38 	return IRQ_HANDLED;
39 }
40 
41 static int interrupt_cnt_enable_read(struct counter_device *counter,
42 				     struct counter_count *count, u8 *enable)
43 {
44 	struct interrupt_cnt_priv *priv = counter_priv(counter);
45 
46 	guard(mutex)(&priv->lock);
47 
48 	*enable = priv->enabled;
49 
50 	return 0;
51 }
52 
53 static int interrupt_cnt_enable_write(struct counter_device *counter,
54 				      struct counter_count *count, u8 enable)
55 {
56 	struct interrupt_cnt_priv *priv = counter_priv(counter);
57 
58 	guard(mutex)(&priv->lock);
59 
60 	if (priv->enabled == enable)
61 		return 0;
62 
63 	if (enable) {
64 		priv->enabled = true;
65 		enable_irq(priv->irq);
66 	} else {
67 		disable_irq(priv->irq);
68 		priv->enabled = false;
69 	}
70 
71 	return 0;
72 }
73 
74 static struct counter_comp interrupt_cnt_ext[] = {
75 	COUNTER_COMP_ENABLE(interrupt_cnt_enable_read,
76 			    interrupt_cnt_enable_write),
77 };
78 
79 static const enum counter_synapse_action interrupt_cnt_synapse_actions[] = {
80 	COUNTER_SYNAPSE_ACTION_RISING_EDGE,
81 };
82 
83 static int interrupt_cnt_action_read(struct counter_device *counter,
84 				     struct counter_count *count,
85 				     struct counter_synapse *synapse,
86 				     enum counter_synapse_action *action)
87 {
88 	*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
89 
90 	return 0;
91 }
92 
93 static int interrupt_cnt_read(struct counter_device *counter,
94 			      struct counter_count *count, u64 *val)
95 {
96 	struct interrupt_cnt_priv *priv = counter_priv(counter);
97 
98 	*val = atomic_long_read(&priv->count);
99 
100 	return 0;
101 }
102 
103 static int interrupt_cnt_write(struct counter_device *counter,
104 			       struct counter_count *count, const u64 val)
105 {
106 	struct interrupt_cnt_priv *priv = counter_priv(counter);
107 
108 	if (val != (typeof(priv->count.counter))val)
109 		return -ERANGE;
110 
111 	atomic_long_set(&priv->count, val);
112 
113 	return 0;
114 }
115 
116 static const enum counter_function interrupt_cnt_functions[] = {
117 	COUNTER_FUNCTION_INCREASE,
118 };
119 
120 static int interrupt_cnt_function_read(struct counter_device *counter,
121 				       struct counter_count *count,
122 				       enum counter_function *function)
123 {
124 	*function = COUNTER_FUNCTION_INCREASE;
125 
126 	return 0;
127 }
128 
129 static int interrupt_cnt_signal_read(struct counter_device *counter,
130 				     struct counter_signal *signal,
131 				     enum counter_signal_level *level)
132 {
133 	struct interrupt_cnt_priv *priv = counter_priv(counter);
134 	int ret;
135 
136 	if (!priv->gpio)
137 		return -EINVAL;
138 
139 	ret = gpiod_get_value(priv->gpio);
140 	if (ret < 0)
141 		return ret;
142 
143 	*level = ret ? COUNTER_SIGNAL_LEVEL_HIGH : COUNTER_SIGNAL_LEVEL_LOW;
144 
145 	return 0;
146 }
147 
148 static int interrupt_cnt_watch_validate(struct counter_device *counter,
149 					const struct counter_watch *watch)
150 {
151 	if (watch->channel != 0 ||
152 	    watch->event != COUNTER_EVENT_CHANGE_OF_STATE)
153 		return -EINVAL;
154 
155 	return 0;
156 }
157 
158 static const struct counter_ops interrupt_cnt_ops = {
159 	.action_read = interrupt_cnt_action_read,
160 	.count_read = interrupt_cnt_read,
161 	.count_write = interrupt_cnt_write,
162 	.function_read = interrupt_cnt_function_read,
163 	.signal_read  = interrupt_cnt_signal_read,
164 	.watch_validate  = interrupt_cnt_watch_validate,
165 };
166 
167 static int interrupt_cnt_probe(struct platform_device *pdev)
168 {
169 	struct device *dev = &pdev->dev;
170 	struct counter_device *counter;
171 	struct interrupt_cnt_priv *priv;
172 	int ret;
173 
174 	counter = devm_counter_alloc(dev, sizeof(*priv));
175 	if (!counter)
176 		return -ENOMEM;
177 	priv = counter_priv(counter);
178 
179 	priv->irq = platform_get_irq_optional(pdev,  0);
180 	if (priv->irq == -ENXIO)
181 		priv->irq = 0;
182 	else if (priv->irq < 0)
183 		return dev_err_probe(dev, priv->irq, "failed to get IRQ\n");
184 
185 	priv->gpio = devm_gpiod_get_optional(dev, NULL, GPIOD_IN);
186 	if (IS_ERR(priv->gpio))
187 		return dev_err_probe(dev, PTR_ERR(priv->gpio), "failed to get GPIO\n");
188 
189 	if (!priv->irq && !priv->gpio) {
190 		dev_err(dev, "IRQ and GPIO are not found. At least one source should be provided\n");
191 		return -ENODEV;
192 	}
193 
194 	if (!priv->irq) {
195 		int irq = gpiod_to_irq(priv->gpio);
196 
197 		if (irq < 0)
198 			return dev_err_probe(dev, irq, "failed to get IRQ from GPIO\n");
199 
200 		priv->irq = irq;
201 	}
202 
203 	priv->signals.name = devm_kasprintf(dev, GFP_KERNEL, "IRQ %d",
204 					    priv->irq);
205 	if (!priv->signals.name)
206 		return -ENOMEM;
207 
208 	counter->signals = &priv->signals;
209 	counter->num_signals = 1;
210 
211 	priv->synapses.actions_list = interrupt_cnt_synapse_actions;
212 	priv->synapses.num_actions = ARRAY_SIZE(interrupt_cnt_synapse_actions);
213 	priv->synapses.signal = &priv->signals;
214 
215 	priv->cnts.name = "Channel 0 Count";
216 	priv->cnts.functions_list = interrupt_cnt_functions;
217 	priv->cnts.num_functions = ARRAY_SIZE(interrupt_cnt_functions);
218 	priv->cnts.synapses = &priv->synapses;
219 	priv->cnts.num_synapses = 1;
220 	priv->cnts.ext = interrupt_cnt_ext;
221 	priv->cnts.num_ext = ARRAY_SIZE(interrupt_cnt_ext);
222 
223 	counter->name = dev_name(dev);
224 	counter->parent = dev;
225 	counter->ops = &interrupt_cnt_ops;
226 	counter->counts = &priv->cnts;
227 	counter->num_counts = 1;
228 
229 	irq_set_status_flags(priv->irq, IRQ_NOAUTOEN);
230 	ret = devm_request_irq(dev, priv->irq, interrupt_cnt_isr,
231 			       IRQF_TRIGGER_RISING, dev_name(dev), counter);
232 	if (ret)
233 		return ret;
234 
235 	ret = devm_mutex_init(dev, &priv->lock);
236 	if (ret)
237 		return ret;
238 
239 	ret = devm_counter_add(dev, counter);
240 	if (ret < 0)
241 		return dev_err_probe(dev, ret, "Failed to add counter\n");
242 
243 	return 0;
244 }
245 
246 static const struct of_device_id interrupt_cnt_of_match[] = {
247 	{ .compatible = "interrupt-counter", },
248 	{}
249 };
250 MODULE_DEVICE_TABLE(of, interrupt_cnt_of_match);
251 
252 static struct platform_driver interrupt_cnt_driver = {
253 	.probe = interrupt_cnt_probe,
254 	.driver = {
255 		.name = INTERRUPT_CNT_NAME,
256 		.of_match_table = interrupt_cnt_of_match,
257 	},
258 };
259 module_platform_driver(interrupt_cnt_driver);
260 
261 MODULE_ALIAS("platform:interrupt-counter");
262 MODULE_AUTHOR("Oleksij Rempel <o.rempel@pengutronix.de>");
263 MODULE_DESCRIPTION("Interrupt counter driver");
264 MODULE_LICENSE("GPL v2");
265 MODULE_IMPORT_NS("COUNTER");
266