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