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