1 /* 2 * hwmon.c - part of lm_sensors, Linux kernel modules for hardware monitoring 3 * 4 * This file defines the sysfs class "hwmon", for use by sensors drivers. 5 * 6 * Copyright (C) 2005 Mark M. Hoffman <mhoffman@lightlink.com> 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; version 2 of the License. 11 */ 12 13 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 14 15 #include <linux/module.h> 16 #include <linux/device.h> 17 #include <linux/err.h> 18 #include <linux/slab.h> 19 #include <linux/kdev_t.h> 20 #include <linux/idr.h> 21 #include <linux/hwmon.h> 22 #include <linux/gfp.h> 23 #include <linux/spinlock.h> 24 #include <linux/pci.h> 25 26 #define HWMON_ID_PREFIX "hwmon" 27 #define HWMON_ID_FORMAT HWMON_ID_PREFIX "%d" 28 29 struct hwmon_device { 30 const char *name; 31 struct device dev; 32 }; 33 #define to_hwmon_device(d) container_of(d, struct hwmon_device, dev) 34 35 static ssize_t 36 show_name(struct device *dev, struct device_attribute *attr, char *buf) 37 { 38 return sprintf(buf, "%s\n", to_hwmon_device(dev)->name); 39 } 40 static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); 41 42 static struct attribute *hwmon_dev_attrs[] = { 43 &dev_attr_name.attr, 44 NULL 45 }; 46 47 static umode_t hwmon_dev_name_is_visible(struct kobject *kobj, 48 struct attribute *attr, int n) 49 { 50 struct device *dev = container_of(kobj, struct device, kobj); 51 52 if (to_hwmon_device(dev)->name == NULL) 53 return 0; 54 55 return attr->mode; 56 } 57 58 static struct attribute_group hwmon_dev_attr_group = { 59 .attrs = hwmon_dev_attrs, 60 .is_visible = hwmon_dev_name_is_visible, 61 }; 62 63 static const struct attribute_group *hwmon_dev_attr_groups[] = { 64 &hwmon_dev_attr_group, 65 NULL 66 }; 67 68 static void hwmon_dev_release(struct device *dev) 69 { 70 kfree(to_hwmon_device(dev)); 71 } 72 73 static struct class hwmon_class = { 74 .name = "hwmon", 75 .owner = THIS_MODULE, 76 .dev_groups = hwmon_dev_attr_groups, 77 .dev_release = hwmon_dev_release, 78 }; 79 80 static DEFINE_IDA(hwmon_ida); 81 82 /** 83 * hwmon_device_register_with_groups - register w/ hwmon 84 * @dev: the parent device 85 * @name: hwmon name attribute 86 * @drvdata: driver data to attach to created device 87 * @groups: List of attribute groups to create 88 * 89 * hwmon_device_unregister() must be called when the device is no 90 * longer needed. 91 * 92 * Returns the pointer to the new device. 93 */ 94 struct device * 95 hwmon_device_register_with_groups(struct device *dev, const char *name, 96 void *drvdata, 97 const struct attribute_group **groups) 98 { 99 struct hwmon_device *hwdev; 100 int err, id; 101 102 id = ida_simple_get(&hwmon_ida, 0, 0, GFP_KERNEL); 103 if (id < 0) 104 return ERR_PTR(id); 105 106 hwdev = kzalloc(sizeof(*hwdev), GFP_KERNEL); 107 if (hwdev == NULL) { 108 err = -ENOMEM; 109 goto ida_remove; 110 } 111 112 hwdev->name = name; 113 hwdev->dev.class = &hwmon_class; 114 hwdev->dev.parent = dev; 115 hwdev->dev.groups = groups; 116 hwdev->dev.of_node = dev ? dev->of_node : NULL; 117 dev_set_drvdata(&hwdev->dev, drvdata); 118 dev_set_name(&hwdev->dev, HWMON_ID_FORMAT, id); 119 err = device_register(&hwdev->dev); 120 if (err) 121 goto free; 122 123 return &hwdev->dev; 124 125 free: 126 kfree(hwdev); 127 ida_remove: 128 ida_simple_remove(&hwmon_ida, id); 129 return ERR_PTR(err); 130 } 131 EXPORT_SYMBOL_GPL(hwmon_device_register_with_groups); 132 133 /** 134 * hwmon_device_register - register w/ hwmon 135 * @dev: the device to register 136 * 137 * hwmon_device_unregister() must be called when the device is no 138 * longer needed. 139 * 140 * Returns the pointer to the new device. 141 */ 142 struct device *hwmon_device_register(struct device *dev) 143 { 144 return hwmon_device_register_with_groups(dev, NULL, NULL, NULL); 145 } 146 EXPORT_SYMBOL_GPL(hwmon_device_register); 147 148 /** 149 * hwmon_device_unregister - removes the previously registered class device 150 * 151 * @dev: the class device to destroy 152 */ 153 void hwmon_device_unregister(struct device *dev) 154 { 155 int id; 156 157 if (likely(sscanf(dev_name(dev), HWMON_ID_FORMAT, &id) == 1)) { 158 device_unregister(dev); 159 ida_simple_remove(&hwmon_ida, id); 160 } else 161 dev_dbg(dev->parent, 162 "hwmon_device_unregister() failed: bad class ID!\n"); 163 } 164 EXPORT_SYMBOL_GPL(hwmon_device_unregister); 165 166 static void devm_hwmon_release(struct device *dev, void *res) 167 { 168 struct device *hwdev = *(struct device **)res; 169 170 hwmon_device_unregister(hwdev); 171 } 172 173 /** 174 * devm_hwmon_device_register_with_groups - register w/ hwmon 175 * @dev: the parent device 176 * @name: hwmon name attribute 177 * @drvdata: driver data to attach to created device 178 * @groups: List of attribute groups to create 179 * 180 * Returns the pointer to the new device. The new device is automatically 181 * unregistered with the parent device. 182 */ 183 struct device * 184 devm_hwmon_device_register_with_groups(struct device *dev, const char *name, 185 void *drvdata, 186 const struct attribute_group **groups) 187 { 188 struct device **ptr, *hwdev; 189 190 if (!dev) 191 return ERR_PTR(-EINVAL); 192 193 ptr = devres_alloc(devm_hwmon_release, sizeof(*ptr), GFP_KERNEL); 194 if (!ptr) 195 return ERR_PTR(-ENOMEM); 196 197 hwdev = hwmon_device_register_with_groups(dev, name, drvdata, groups); 198 if (IS_ERR(hwdev)) 199 goto error; 200 201 *ptr = hwdev; 202 devres_add(dev, ptr); 203 return hwdev; 204 205 error: 206 devres_free(ptr); 207 return hwdev; 208 } 209 EXPORT_SYMBOL_GPL(devm_hwmon_device_register_with_groups); 210 211 static int devm_hwmon_match(struct device *dev, void *res, void *data) 212 { 213 struct device **hwdev = res; 214 215 return *hwdev == data; 216 } 217 218 /** 219 * devm_hwmon_device_unregister - removes a previously registered hwmon device 220 * 221 * @dev: the parent device of the device to unregister 222 */ 223 void devm_hwmon_device_unregister(struct device *dev) 224 { 225 WARN_ON(devres_release(dev, devm_hwmon_release, devm_hwmon_match, dev)); 226 } 227 EXPORT_SYMBOL_GPL(devm_hwmon_device_unregister); 228 229 static void __init hwmon_pci_quirks(void) 230 { 231 #if defined CONFIG_X86 && defined CONFIG_PCI 232 struct pci_dev *sb; 233 u16 base; 234 u8 enable; 235 236 /* Open access to 0x295-0x296 on MSI MS-7031 */ 237 sb = pci_get_device(PCI_VENDOR_ID_ATI, 0x436c, NULL); 238 if (sb) { 239 if (sb->subsystem_vendor == 0x1462 && /* MSI */ 240 sb->subsystem_device == 0x0031) { /* MS-7031 */ 241 pci_read_config_byte(sb, 0x48, &enable); 242 pci_read_config_word(sb, 0x64, &base); 243 244 if (base == 0 && !(enable & BIT(2))) { 245 dev_info(&sb->dev, 246 "Opening wide generic port at 0x295\n"); 247 pci_write_config_word(sb, 0x64, 0x295); 248 pci_write_config_byte(sb, 0x48, 249 enable | BIT(2)); 250 } 251 } 252 pci_dev_put(sb); 253 } 254 #endif 255 } 256 257 static int __init hwmon_init(void) 258 { 259 int err; 260 261 hwmon_pci_quirks(); 262 263 err = class_register(&hwmon_class); 264 if (err) { 265 pr_err("couldn't register hwmon sysfs class\n"); 266 return err; 267 } 268 return 0; 269 } 270 271 static void __exit hwmon_exit(void) 272 { 273 class_unregister(&hwmon_class); 274 } 275 276 subsys_initcall(hwmon_init); 277 module_exit(hwmon_exit); 278 279 MODULE_AUTHOR("Mark M. Hoffman <mhoffman@lightlink.com>"); 280 MODULE_DESCRIPTION("hardware monitoring sysfs/class support"); 281 MODULE_LICENSE("GPL"); 282 283