1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Generic Counter interface
4  * Copyright (C) 2020 William Breathitt Gray
5  */
6 #include <linux/counter.h>
7 #include <linux/device.h>
8 #include <linux/export.h>
9 #include <linux/gfp.h>
10 #include <linux/idr.h>
11 #include <linux/init.h>
12 #include <linux/module.h>
13 
14 #include "counter-sysfs.h"
15 
16 /* Provides a unique ID for each counter device */
17 static DEFINE_IDA(counter_ida);
18 
19 static void counter_device_release(struct device *dev)
20 {
21 	ida_free(&counter_ida, dev->id);
22 }
23 
24 static struct device_type counter_device_type = {
25 	.name = "counter_device",
26 	.release = counter_device_release,
27 };
28 
29 static struct bus_type counter_bus_type = {
30 	.name = "counter",
31 	.dev_name = "counter",
32 };
33 
34 /**
35  * counter_register - register Counter to the system
36  * @counter:	pointer to Counter to register
37  *
38  * This function registers a Counter to the system. A sysfs "counter" directory
39  * will be created and populated with sysfs attributes correlating with the
40  * Counter Signals, Synapses, and Counts respectively.
41  */
42 int counter_register(struct counter_device *const counter)
43 {
44 	struct device *const dev = &counter->dev;
45 	int id;
46 	int err;
47 
48 	/* Acquire unique ID */
49 	id = ida_alloc(&counter_ida, GFP_KERNEL);
50 	if (id < 0)
51 		return id;
52 
53 	/* Configure device structure for Counter */
54 	dev->id = id;
55 	dev->type = &counter_device_type;
56 	dev->bus = &counter_bus_type;
57 	if (counter->parent) {
58 		dev->parent = counter->parent;
59 		dev->of_node = counter->parent->of_node;
60 	}
61 	device_initialize(dev);
62 	dev_set_drvdata(dev, counter);
63 
64 	/* Add Counter sysfs attributes */
65 	err = counter_sysfs_add(counter);
66 	if (err < 0)
67 		goto err_free_id;
68 
69 	/* Add device to system */
70 	err = device_add(dev);
71 	if (err < 0)
72 		goto err_free_id;
73 
74 	return 0;
75 
76 err_free_id:
77 	put_device(dev);
78 	return err;
79 }
80 EXPORT_SYMBOL_GPL(counter_register);
81 
82 /**
83  * counter_unregister - unregister Counter from the system
84  * @counter:	pointer to Counter to unregister
85  *
86  * The Counter is unregistered from the system.
87  */
88 void counter_unregister(struct counter_device *const counter)
89 {
90 	if (!counter)
91 		return;
92 
93 	device_unregister(&counter->dev);
94 }
95 EXPORT_SYMBOL_GPL(counter_unregister);
96 
97 static void devm_counter_release(void *counter)
98 {
99 	counter_unregister(counter);
100 }
101 
102 /**
103  * devm_counter_register - Resource-managed counter_register
104  * @dev:	device to allocate counter_device for
105  * @counter:	pointer to Counter to register
106  *
107  * Managed counter_register. The Counter registered with this function is
108  * automatically unregistered on driver detach. This function calls
109  * counter_register internally. Refer to that function for more information.
110  *
111  * RETURNS:
112  * 0 on success, negative error number on failure.
113  */
114 int devm_counter_register(struct device *dev,
115 			  struct counter_device *const counter)
116 {
117 	int err;
118 
119 	err = counter_register(counter);
120 	if (err < 0)
121 		return err;
122 
123 	return devm_add_action_or_reset(dev, devm_counter_release, counter);
124 }
125 EXPORT_SYMBOL_GPL(devm_counter_register);
126 
127 static int __init counter_init(void)
128 {
129 	return bus_register(&counter_bus_type);
130 }
131 
132 static void __exit counter_exit(void)
133 {
134 	bus_unregister(&counter_bus_type);
135 }
136 
137 subsys_initcall(counter_init);
138 module_exit(counter_exit);
139 
140 MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
141 MODULE_DESCRIPTION("Generic Counter interface");
142 MODULE_LICENSE("GPL v2");
143