1aaec1a0fSWilliam Breathitt Gray // SPDX-License-Identifier: GPL-2.0
2aaec1a0fSWilliam Breathitt Gray /*
3aaec1a0fSWilliam Breathitt Gray  * Generic Counter interface
4aaec1a0fSWilliam Breathitt Gray  * Copyright (C) 2020 William Breathitt Gray
5aaec1a0fSWilliam Breathitt Gray  */
6b6c50affSWilliam Breathitt Gray #include <linux/cdev.h>
7aaec1a0fSWilliam Breathitt Gray #include <linux/counter.h>
8aaec1a0fSWilliam Breathitt Gray #include <linux/device.h>
9b6c50affSWilliam Breathitt Gray #include <linux/device/bus.h>
10aaec1a0fSWilliam Breathitt Gray #include <linux/export.h>
11b6c50affSWilliam Breathitt Gray #include <linux/fs.h>
12aaec1a0fSWilliam Breathitt Gray #include <linux/gfp.h>
13aaec1a0fSWilliam Breathitt Gray #include <linux/idr.h>
14aaec1a0fSWilliam Breathitt Gray #include <linux/init.h>
15b6c50affSWilliam Breathitt Gray #include <linux/kdev_t.h>
16aaec1a0fSWilliam Breathitt Gray #include <linux/module.h>
17b6c50affSWilliam Breathitt Gray #include <linux/mutex.h>
18c18e2760SUwe Kleine-König #include <linux/slab.h>
19b6c50affSWilliam Breathitt Gray #include <linux/types.h>
20b6c50affSWilliam Breathitt Gray #include <linux/wait.h>
21aaec1a0fSWilliam Breathitt Gray 
22b6c50affSWilliam Breathitt Gray #include "counter-chrdev.h"
23aaec1a0fSWilliam Breathitt Gray #include "counter-sysfs.h"
24aaec1a0fSWilliam Breathitt Gray 
254da08477SWilliam Breathitt Gray #define COUNTER_NAME	"counter"
264da08477SWilliam Breathitt Gray 
27aaec1a0fSWilliam Breathitt Gray /* Provides a unique ID for each counter device */
28aaec1a0fSWilliam Breathitt Gray static DEFINE_IDA(counter_ida);
29aaec1a0fSWilliam Breathitt Gray 
30c18e2760SUwe Kleine-König struct counter_device_allochelper {
31c18e2760SUwe Kleine-König 	struct counter_device counter;
32c18e2760SUwe Kleine-König 
33c18e2760SUwe Kleine-König 	/*
34c18e2760SUwe Kleine-König 	 * This is cache line aligned to ensure private data behaves like if it
35c18e2760SUwe Kleine-König 	 * were kmalloced separately.
36c18e2760SUwe Kleine-König 	 */
37c18e2760SUwe Kleine-König 	unsigned long privdata[] ____cacheline_aligned;
38c18e2760SUwe Kleine-König };
39c18e2760SUwe Kleine-König 
counter_device_release(struct device * dev)40aaec1a0fSWilliam Breathitt Gray static void counter_device_release(struct device *dev)
41aaec1a0fSWilliam Breathitt Gray {
42b56346ddSUwe Kleine-König 	struct counter_device *const counter =
43b56346ddSUwe Kleine-König 		container_of(dev, struct counter_device, dev);
44b6c50affSWilliam Breathitt Gray 
45b6c50affSWilliam Breathitt Gray 	counter_chrdev_remove(counter);
46aaec1a0fSWilliam Breathitt Gray 	ida_free(&counter_ida, dev->id);
47c18e2760SUwe Kleine-König 
48c18e2760SUwe Kleine-König 	kfree(container_of(counter, struct counter_device_allochelper, counter));
49aaec1a0fSWilliam Breathitt Gray }
50aaec1a0fSWilliam Breathitt Gray 
51aaec1a0fSWilliam Breathitt Gray static struct device_type counter_device_type = {
52aaec1a0fSWilliam Breathitt Gray 	.name = "counter_device",
53aaec1a0fSWilliam Breathitt Gray 	.release = counter_device_release,
54aaec1a0fSWilliam Breathitt Gray };
55aaec1a0fSWilliam Breathitt Gray 
56aaec1a0fSWilliam Breathitt Gray static struct bus_type counter_bus_type = {
57aaec1a0fSWilliam Breathitt Gray 	.name = "counter",
58aaec1a0fSWilliam Breathitt Gray 	.dev_name = "counter",
59aaec1a0fSWilliam Breathitt Gray };
60aaec1a0fSWilliam Breathitt Gray 
61b6c50affSWilliam Breathitt Gray static dev_t counter_devt;
62b6c50affSWilliam Breathitt Gray 
63aaec1a0fSWilliam Breathitt Gray /**
645207fb2fSUwe Kleine-König  * counter_priv - access counter device private data
655207fb2fSUwe Kleine-König  * @counter: counter device
665207fb2fSUwe Kleine-König  *
675207fb2fSUwe Kleine-König  * Get the counter device private data
685207fb2fSUwe Kleine-König  */
counter_priv(const struct counter_device * const counter)695207fb2fSUwe Kleine-König void *counter_priv(const struct counter_device *const counter)
705207fb2fSUwe Kleine-König {
71c18e2760SUwe Kleine-König 	struct counter_device_allochelper *ch =
72c18e2760SUwe Kleine-König 		container_of(counter, struct counter_device_allochelper, counter);
73c18e2760SUwe Kleine-König 
74c18e2760SUwe Kleine-König 	return &ch->privdata;
75c18e2760SUwe Kleine-König }
76*3216e551SWilliam Breathitt Gray EXPORT_SYMBOL_NS_GPL(counter_priv, COUNTER);
775207fb2fSUwe Kleine-König 
785207fb2fSUwe Kleine-König /**
79c18e2760SUwe Kleine-König  * counter_alloc - allocate a counter_device
80c18e2760SUwe Kleine-König  * @sizeof_priv: size of the driver private data
81c18e2760SUwe Kleine-König  *
82c18e2760SUwe Kleine-König  * This is part one of counter registration. The structure is allocated
83c18e2760SUwe Kleine-König  * dynamically to ensure the right lifetime for the embedded struct device.
84c18e2760SUwe Kleine-König  *
85c18e2760SUwe Kleine-König  * If this succeeds, call counter_put() to get rid of the counter_device again.
86c18e2760SUwe Kleine-König  */
counter_alloc(size_t sizeof_priv)87c18e2760SUwe Kleine-König struct counter_device *counter_alloc(size_t sizeof_priv)
88c18e2760SUwe Kleine-König {
89c18e2760SUwe Kleine-König 	struct counter_device_allochelper *ch;
90c18e2760SUwe Kleine-König 	struct counter_device *counter;
91c18e2760SUwe Kleine-König 	struct device *dev;
92c18e2760SUwe Kleine-König 	int err;
93c18e2760SUwe Kleine-König 
94c18e2760SUwe Kleine-König 	ch = kzalloc(sizeof(*ch) + sizeof_priv, GFP_KERNEL);
95fc55e63eSDan Carpenter 	if (!ch)
96fc55e63eSDan Carpenter 		return NULL;
97c18e2760SUwe Kleine-König 
98c18e2760SUwe Kleine-König 	counter = &ch->counter;
99c18e2760SUwe Kleine-König 	dev = &counter->dev;
100c18e2760SUwe Kleine-König 
101c18e2760SUwe Kleine-König 	/* Acquire unique ID */
102c18e2760SUwe Kleine-König 	err = ida_alloc(&counter_ida, GFP_KERNEL);
103c18e2760SUwe Kleine-König 	if (err < 0)
104c18e2760SUwe Kleine-König 		goto err_ida_alloc;
105c18e2760SUwe Kleine-König 	dev->id = err;
106c18e2760SUwe Kleine-König 
107c18e2760SUwe Kleine-König 	mutex_init(&counter->ops_exist_lock);
108c18e2760SUwe Kleine-König 	dev->type = &counter_device_type;
109c18e2760SUwe Kleine-König 	dev->bus = &counter_bus_type;
110c18e2760SUwe Kleine-König 	dev->devt = MKDEV(MAJOR(counter_devt), dev->id);
111c18e2760SUwe Kleine-König 
112c18e2760SUwe Kleine-König 	err = counter_chrdev_add(counter);
113c18e2760SUwe Kleine-König 	if (err < 0)
114c18e2760SUwe Kleine-König 		goto err_chrdev_add;
115c18e2760SUwe Kleine-König 
116c18e2760SUwe Kleine-König 	device_initialize(dev);
117c18e2760SUwe Kleine-König 
1184da08477SWilliam Breathitt Gray 	err = dev_set_name(dev, COUNTER_NAME "%d", dev->id);
1194da08477SWilliam Breathitt Gray 	if (err)
1204da08477SWilliam Breathitt Gray 		goto err_dev_set_name;
1214da08477SWilliam Breathitt Gray 
122c18e2760SUwe Kleine-König 	return counter;
123c18e2760SUwe Kleine-König 
1244da08477SWilliam Breathitt Gray err_dev_set_name:
1254da08477SWilliam Breathitt Gray 
1264da08477SWilliam Breathitt Gray 	counter_chrdev_remove(counter);
127c18e2760SUwe Kleine-König err_chrdev_add:
128c18e2760SUwe Kleine-König 
129c18e2760SUwe Kleine-König 	ida_free(&counter_ida, dev->id);
130c18e2760SUwe Kleine-König err_ida_alloc:
131c18e2760SUwe Kleine-König 
132c18e2760SUwe Kleine-König 	kfree(ch);
133c18e2760SUwe Kleine-König 
134fc55e63eSDan Carpenter 	return NULL;
135c18e2760SUwe Kleine-König }
136*3216e551SWilliam Breathitt Gray EXPORT_SYMBOL_NS_GPL(counter_alloc, COUNTER);
137c18e2760SUwe Kleine-König 
counter_put(struct counter_device * counter)138c18e2760SUwe Kleine-König void counter_put(struct counter_device *counter)
139c18e2760SUwe Kleine-König {
140c18e2760SUwe Kleine-König 	put_device(&counter->dev);
141c18e2760SUwe Kleine-König }
142*3216e551SWilliam Breathitt Gray EXPORT_SYMBOL_NS_GPL(counter_put, COUNTER);
143c18e2760SUwe Kleine-König 
144c18e2760SUwe Kleine-König /**
145c18e2760SUwe Kleine-König  * counter_add - complete registration of a counter
146c18e2760SUwe Kleine-König  * @counter: the counter to add
147c18e2760SUwe Kleine-König  *
148c18e2760SUwe Kleine-König  * This is part two of counter registration.
149c18e2760SUwe Kleine-König  *
150c18e2760SUwe Kleine-König  * If this succeeds, call counter_unregister() to get rid of the counter_device again.
151c18e2760SUwe Kleine-König  */
counter_add(struct counter_device * counter)152c18e2760SUwe Kleine-König int counter_add(struct counter_device *counter)
153c18e2760SUwe Kleine-König {
154c18e2760SUwe Kleine-König 	int err;
155c18e2760SUwe Kleine-König 	struct device *dev = &counter->dev;
156c18e2760SUwe Kleine-König 
157c18e2760SUwe Kleine-König 	if (counter->parent) {
158c18e2760SUwe Kleine-König 		dev->parent = counter->parent;
159c18e2760SUwe Kleine-König 		dev->of_node = counter->parent->of_node;
160c18e2760SUwe Kleine-König 	}
161c18e2760SUwe Kleine-König 
162c18e2760SUwe Kleine-König 	err = counter_sysfs_add(counter);
163c18e2760SUwe Kleine-König 	if (err < 0)
164c18e2760SUwe Kleine-König 		return err;
165c18e2760SUwe Kleine-König 
166c18e2760SUwe Kleine-König 	/* implies device_add(dev) */
167c18e2760SUwe Kleine-König 	return cdev_device_add(&counter->chrdev, dev);
168c18e2760SUwe Kleine-König }
169*3216e551SWilliam Breathitt Gray EXPORT_SYMBOL_NS_GPL(counter_add, COUNTER);
170c18e2760SUwe Kleine-König 
171c18e2760SUwe Kleine-König /**
172aaec1a0fSWilliam Breathitt Gray  * counter_unregister - unregister Counter from the system
173aaec1a0fSWilliam Breathitt Gray  * @counter:	pointer to Counter to unregister
174aaec1a0fSWilliam Breathitt Gray  *
175aaec1a0fSWilliam Breathitt Gray  * The Counter is unregistered from the system.
176aaec1a0fSWilliam Breathitt Gray  */
counter_unregister(struct counter_device * const counter)177aaec1a0fSWilliam Breathitt Gray void counter_unregister(struct counter_device *const counter)
178aaec1a0fSWilliam Breathitt Gray {
179aaec1a0fSWilliam Breathitt Gray 	if (!counter)
180aaec1a0fSWilliam Breathitt Gray 		return;
181aaec1a0fSWilliam Breathitt Gray 
182b6c50affSWilliam Breathitt Gray 	cdev_device_del(&counter->chrdev, &counter->dev);
183b6c50affSWilliam Breathitt Gray 
184b6c50affSWilliam Breathitt Gray 	mutex_lock(&counter->ops_exist_lock);
185b6c50affSWilliam Breathitt Gray 
186b6c50affSWilliam Breathitt Gray 	counter->ops = NULL;
187b6c50affSWilliam Breathitt Gray 	wake_up(&counter->events_wait);
188b6c50affSWilliam Breathitt Gray 
189b6c50affSWilliam Breathitt Gray 	mutex_unlock(&counter->ops_exist_lock);
190aaec1a0fSWilliam Breathitt Gray }
191*3216e551SWilliam Breathitt Gray EXPORT_SYMBOL_NS_GPL(counter_unregister, COUNTER);
192aaec1a0fSWilliam Breathitt Gray 
devm_counter_release(void * counter)193aaec1a0fSWilliam Breathitt Gray static void devm_counter_release(void *counter)
194aaec1a0fSWilliam Breathitt Gray {
195aaec1a0fSWilliam Breathitt Gray 	counter_unregister(counter);
196aaec1a0fSWilliam Breathitt Gray }
197aaec1a0fSWilliam Breathitt Gray 
devm_counter_put(void * counter)198c18e2760SUwe Kleine-König static void devm_counter_put(void *counter)
199c18e2760SUwe Kleine-König {
200c18e2760SUwe Kleine-König 	counter_put(counter);
201c18e2760SUwe Kleine-König }
202c18e2760SUwe Kleine-König 
203c18e2760SUwe Kleine-König /**
204c18e2760SUwe Kleine-König  * devm_counter_alloc - allocate a counter_device
205c18e2760SUwe Kleine-König  * @dev: the device to register the release callback for
206c18e2760SUwe Kleine-König  * @sizeof_priv: size of the driver private data
207c18e2760SUwe Kleine-König  *
208c18e2760SUwe Kleine-König  * This is the device managed version of counter_add(). It registers a cleanup
209c18e2760SUwe Kleine-König  * callback to care for calling counter_put().
210c18e2760SUwe Kleine-König  */
devm_counter_alloc(struct device * dev,size_t sizeof_priv)211c18e2760SUwe Kleine-König struct counter_device *devm_counter_alloc(struct device *dev, size_t sizeof_priv)
212c18e2760SUwe Kleine-König {
213c18e2760SUwe Kleine-König 	struct counter_device *counter;
214c18e2760SUwe Kleine-König 	int err;
215c18e2760SUwe Kleine-König 
216c18e2760SUwe Kleine-König 	counter = counter_alloc(sizeof_priv);
217fc55e63eSDan Carpenter 	if (!counter)
218fc55e63eSDan Carpenter 		return NULL;
219c18e2760SUwe Kleine-König 
220c18e2760SUwe Kleine-König 	err = devm_add_action_or_reset(dev, devm_counter_put, counter);
221c18e2760SUwe Kleine-König 	if (err < 0)
222fc55e63eSDan Carpenter 		return NULL;
223c18e2760SUwe Kleine-König 
224c18e2760SUwe Kleine-König 	return counter;
225c18e2760SUwe Kleine-König }
226*3216e551SWilliam Breathitt Gray EXPORT_SYMBOL_NS_GPL(devm_counter_alloc, COUNTER);
227c18e2760SUwe Kleine-König 
228c18e2760SUwe Kleine-König /**
229c18e2760SUwe Kleine-König  * devm_counter_add - complete registration of a counter
230c18e2760SUwe Kleine-König  * @dev: the device to register the release callback for
231c18e2760SUwe Kleine-König  * @counter: the counter to add
232c18e2760SUwe Kleine-König  *
233c18e2760SUwe Kleine-König  * This is the device managed version of counter_add(). It registers a cleanup
234c18e2760SUwe Kleine-König  * callback to care for calling counter_unregister().
235c18e2760SUwe Kleine-König  */
devm_counter_add(struct device * dev,struct counter_device * const counter)236c18e2760SUwe Kleine-König int devm_counter_add(struct device *dev,
237c18e2760SUwe Kleine-König 		     struct counter_device *const counter)
238c18e2760SUwe Kleine-König {
239c18e2760SUwe Kleine-König 	int err;
240c18e2760SUwe Kleine-König 
241c18e2760SUwe Kleine-König 	err = counter_add(counter);
242c18e2760SUwe Kleine-König 	if (err < 0)
243c18e2760SUwe Kleine-König 		return err;
244c18e2760SUwe Kleine-König 
245c18e2760SUwe Kleine-König 	return devm_add_action_or_reset(dev, devm_counter_release, counter);
246c18e2760SUwe Kleine-König }
247*3216e551SWilliam Breathitt Gray EXPORT_SYMBOL_NS_GPL(devm_counter_add, COUNTER);
248c18e2760SUwe Kleine-König 
249b6c50affSWilliam Breathitt Gray #define COUNTER_DEV_MAX 256
250b6c50affSWilliam Breathitt Gray 
counter_init(void)251aaec1a0fSWilliam Breathitt Gray static int __init counter_init(void)
252aaec1a0fSWilliam Breathitt Gray {
253b6c50affSWilliam Breathitt Gray 	int err;
254b6c50affSWilliam Breathitt Gray 
255b6c50affSWilliam Breathitt Gray 	err = bus_register(&counter_bus_type);
256b6c50affSWilliam Breathitt Gray 	if (err < 0)
257b6c50affSWilliam Breathitt Gray 		return err;
258b6c50affSWilliam Breathitt Gray 
2594da08477SWilliam Breathitt Gray 	err = alloc_chrdev_region(&counter_devt, 0, COUNTER_DEV_MAX,
2604da08477SWilliam Breathitt Gray 				  COUNTER_NAME);
261b6c50affSWilliam Breathitt Gray 	if (err < 0)
262b6c50affSWilliam Breathitt Gray 		goto err_unregister_bus;
263b6c50affSWilliam Breathitt Gray 
264b6c50affSWilliam Breathitt Gray 	return 0;
265b6c50affSWilliam Breathitt Gray 
266b6c50affSWilliam Breathitt Gray err_unregister_bus:
267b6c50affSWilliam Breathitt Gray 	bus_unregister(&counter_bus_type);
268b6c50affSWilliam Breathitt Gray 	return err;
269aaec1a0fSWilliam Breathitt Gray }
270aaec1a0fSWilliam Breathitt Gray 
counter_exit(void)271aaec1a0fSWilliam Breathitt Gray static void __exit counter_exit(void)
272aaec1a0fSWilliam Breathitt Gray {
273b6c50affSWilliam Breathitt Gray 	unregister_chrdev_region(counter_devt, COUNTER_DEV_MAX);
274aaec1a0fSWilliam Breathitt Gray 	bus_unregister(&counter_bus_type);
275aaec1a0fSWilliam Breathitt Gray }
276aaec1a0fSWilliam Breathitt Gray 
277aaec1a0fSWilliam Breathitt Gray subsys_initcall(counter_init);
278aaec1a0fSWilliam Breathitt Gray module_exit(counter_exit);
279aaec1a0fSWilliam Breathitt Gray 
280aaec1a0fSWilliam Breathitt Gray MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
281aaec1a0fSWilliam Breathitt Gray MODULE_DESCRIPTION("Generic Counter interface");
282aaec1a0fSWilliam Breathitt Gray MODULE_LICENSE("GPL v2");
283