regulator: Build sysfs entries with static attribute groups
authorTakashi Iwai <tiwai@suse.de>
Fri, 30 Jan 2015 19:29:31 +0000 (20:29 +0100)
committerMark Brown <broonie@kernel.org>
Mon, 2 Feb 2015 20:01:51 +0000 (20:01 +0000)
Instead of calling device_create_file() manually after the device
registration, put all in attribute groups and filter the unwanted ones
via is_visible callback.  This not only simplifies the code but also
avoids the possible race between the device registration and sysfs
registration.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Mark Brown <broonie@kernel.org>
drivers/regulator/core.c

index c2554d89d472f2d7ac470bfd5aacbdd7c55a3c57..91d79b84358b0935538424778d24838d964c0a59 100644 (file)
@@ -632,30 +632,6 @@ static ssize_t regulator_bypass_show(struct device *dev,
 static DEVICE_ATTR(bypass, 0444,
                   regulator_bypass_show, NULL);
 
-/*
- * These are the only attributes are present for all regulators.
- * Other attributes are a function of regulator functionality.
- */
-static struct attribute *regulator_dev_attrs[] = {
-       &dev_attr_name.attr,
-       &dev_attr_num_users.attr,
-       &dev_attr_type.attr,
-       NULL,
-};
-ATTRIBUTE_GROUPS(regulator_dev);
-
-static void regulator_dev_release(struct device *dev)
-{
-       struct regulator_dev *rdev = dev_get_drvdata(dev);
-       kfree(rdev);
-}
-
-static struct class regulator_class = {
-       .name = "regulator",
-       .dev_release = regulator_dev_release,
-       .dev_groups = regulator_dev_groups,
-};
-
 /* Calculate the new optimum regulator operating mode based on the new total
  * consumer load. All locks held by caller */
 static void drms_uA_update(struct regulator_dev *rdev)
@@ -3434,126 +3410,136 @@ int regulator_mode_to_status(unsigned int mode)
 }
 EXPORT_SYMBOL_GPL(regulator_mode_to_status);
 
+static struct attribute *regulator_dev_attrs[] = {
+       &dev_attr_name.attr,
+       &dev_attr_num_users.attr,
+       &dev_attr_type.attr,
+       &dev_attr_microvolts.attr,
+       &dev_attr_microamps.attr,
+       &dev_attr_opmode.attr,
+       &dev_attr_state.attr,
+       &dev_attr_status.attr,
+       &dev_attr_bypass.attr,
+       &dev_attr_requested_microamps.attr,
+       &dev_attr_min_microvolts.attr,
+       &dev_attr_max_microvolts.attr,
+       &dev_attr_min_microamps.attr,
+       &dev_attr_max_microamps.attr,
+       &dev_attr_suspend_standby_state.attr,
+       &dev_attr_suspend_mem_state.attr,
+       &dev_attr_suspend_disk_state.attr,
+       &dev_attr_suspend_standby_microvolts.attr,
+       &dev_attr_suspend_mem_microvolts.attr,
+       &dev_attr_suspend_disk_microvolts.attr,
+       &dev_attr_suspend_standby_mode.attr,
+       &dev_attr_suspend_mem_mode.attr,
+       &dev_attr_suspend_disk_mode.attr,
+       NULL
+};
+
 /*
  * To avoid cluttering sysfs (and memory) with useless state, only
  * create attributes that can be meaningfully displayed.
  */
-static int add_regulator_attributes(struct regulator_dev *rdev)
+static umode_t regulator_attr_is_visible(struct kobject *kobj,
+                                        struct attribute *attr, int idx)
 {
-       struct device *dev = &rdev->dev;
+       struct device *dev = kobj_to_dev(kobj);
+       struct regulator_dev *rdev = container_of(dev, struct regulator_dev, dev);
        const struct regulator_ops *ops = rdev->desc->ops;
-       int status = 0;
+       umode_t mode = attr->mode;
+
+       /* these three are always present */
+       if (attr == &dev_attr_name.attr ||
+           attr == &dev_attr_num_users.attr ||
+           attr == &dev_attr_type.attr)
+               return mode;
 
        /* some attributes need specific methods to be displayed */
-       if ((ops->get_voltage && ops->get_voltage(rdev) >= 0) ||
-           (ops->get_voltage_sel && ops->get_voltage_sel(rdev) >= 0) ||
-           (ops->list_voltage && ops->list_voltage(rdev, 0) >= 0) ||
-               (rdev->desc->fixed_uV && (rdev->desc->n_voltages == 1))) {
-               status = device_create_file(dev, &dev_attr_microvolts);
-               if (status < 0)
-                       return status;
-       }
-       if (ops->get_current_limit) {
-               status = device_create_file(dev, &dev_attr_microamps);
-               if (status < 0)
-                       return status;
-       }
-       if (ops->get_mode) {
-               status = device_create_file(dev, &dev_attr_opmode);
-               if (status < 0)
-                       return status;
-       }
-       if (rdev->ena_pin || ops->is_enabled) {
-               status = device_create_file(dev, &dev_attr_state);
-               if (status < 0)
-                       return status;
-       }
-       if (ops->get_status) {
-               status = device_create_file(dev, &dev_attr_status);
-               if (status < 0)
-                       return status;
-       }
-       if (ops->get_bypass) {
-               status = device_create_file(dev, &dev_attr_bypass);
-               if (status < 0)
-                       return status;
+       if (attr == &dev_attr_microvolts.attr) {
+               if ((ops->get_voltage && ops->get_voltage(rdev) >= 0) ||
+                   (ops->get_voltage_sel && ops->get_voltage_sel(rdev) >= 0) ||
+                   (ops->list_voltage && ops->list_voltage(rdev, 0) >= 0) ||
+                   (rdev->desc->fixed_uV && rdev->desc->n_voltages == 1))
+                       return mode;
+               return 0;
        }
 
+       if (attr == &dev_attr_microamps.attr)
+               return ops->get_current_limit ? mode : 0;
+
+       if (attr == &dev_attr_opmode.attr)
+               return ops->get_mode ? mode : 0;
+
+       if (attr == &dev_attr_state.attr)
+               return (rdev->ena_pin || ops->is_enabled) ? mode : 0;
+
+       if (attr == &dev_attr_status.attr)
+               return ops->get_status ? mode : 0;
+
+       if (attr == &dev_attr_bypass.attr)
+               return ops->get_bypass ? mode : 0;
+
        /* some attributes are type-specific */
-       if (rdev->desc->type == REGULATOR_CURRENT) {
-               status = device_create_file(dev, &dev_attr_requested_microamps);
-               if (status < 0)
-                       return status;
-       }
+       if (attr == &dev_attr_requested_microamps.attr)
+               return rdev->desc->type == REGULATOR_CURRENT ? mode : 0;
 
        /* all the other attributes exist to support constraints;
         * don't show them if there are no constraints, or if the
         * relevant supporting methods are missing.
         */
        if (!rdev->constraints)
-               return status;
+               return 0;
 
        /* constraints need specific supporting methods */
-       if (ops->set_voltage || ops->set_voltage_sel) {
-               status = device_create_file(dev, &dev_attr_min_microvolts);
-               if (status < 0)
-                       return status;
-               status = device_create_file(dev, &dev_attr_max_microvolts);
-               if (status < 0)
-                       return status;
-       }
-       if (ops->set_current_limit) {
-               status = device_create_file(dev, &dev_attr_min_microamps);
-               if (status < 0)
-                       return status;
-               status = device_create_file(dev, &dev_attr_max_microamps);
-               if (status < 0)
-                       return status;
-       }
-
-       status = device_create_file(dev, &dev_attr_suspend_standby_state);
-       if (status < 0)
-               return status;
-       status = device_create_file(dev, &dev_attr_suspend_mem_state);
-       if (status < 0)
-               return status;
-       status = device_create_file(dev, &dev_attr_suspend_disk_state);
-       if (status < 0)
-               return status;
+       if (attr == &dev_attr_min_microvolts.attr ||
+           attr == &dev_attr_max_microvolts.attr)
+               return (ops->set_voltage || ops->set_voltage_sel) ? mode : 0;
+
+       if (attr == &dev_attr_min_microamps.attr ||
+           attr == &dev_attr_max_microamps.attr)
+               return ops->set_current_limit ? mode : 0;
+
+       if (attr == &dev_attr_suspend_standby_state.attr ||
+           attr == &dev_attr_suspend_mem_state.attr ||
+           attr == &dev_attr_suspend_disk_state.attr)
+               return mode;
+
+       if (attr == &dev_attr_suspend_standby_microvolts.attr ||
+           attr == &dev_attr_suspend_mem_microvolts.attr ||
+           attr == &dev_attr_suspend_disk_microvolts.attr)
+               return ops->set_suspend_voltage ? mode : 0;
+
+       if (attr == &dev_attr_suspend_standby_mode.attr ||
+           attr == &dev_attr_suspend_mem_mode.attr ||
+           attr == &dev_attr_suspend_disk_mode.attr)
+               return ops->set_suspend_mode ? mode : 0;
+
+       return mode;
+}
+
+static const struct attribute_group regulator_dev_group = {
+       .attrs = regulator_dev_attrs,
+       .is_visible = regulator_attr_is_visible,
+};
+
+static const struct attribute_group *regulator_dev_groups[] = {
+       &regulator_dev_group,
+       NULL
+};
 
-       if (ops->set_suspend_voltage) {
-               status = device_create_file(dev,
-                               &dev_attr_suspend_standby_microvolts);
-               if (status < 0)
-                       return status;
-               status = device_create_file(dev,
-                               &dev_attr_suspend_mem_microvolts);
-               if (status < 0)
-                       return status;
-               status = device_create_file(dev,
-                               &dev_attr_suspend_disk_microvolts);
-               if (status < 0)
-                       return status;
-       }
-
-       if (ops->set_suspend_mode) {
-               status = device_create_file(dev,
-                               &dev_attr_suspend_standby_mode);
-               if (status < 0)
-                       return status;
-               status = device_create_file(dev,
-                               &dev_attr_suspend_mem_mode);
-               if (status < 0)
-                       return status;
-               status = device_create_file(dev,
-                               &dev_attr_suspend_disk_mode);
-               if (status < 0)
-                       return status;
-       }
-
-       return status;
+static void regulator_dev_release(struct device *dev)
+{
+       struct regulator_dev *rdev = dev_get_drvdata(dev);
+       kfree(rdev);
 }
 
+static struct class regulator_class = {
+       .name = "regulator",
+       .dev_release = regulator_dev_release,
+       .dev_groups = regulator_dev_groups,
+};
+
 static void rdev_init_debugfs(struct regulator_dev *rdev)
 {
        rdev->debugfs = debugfs_create_dir(rdev_get_name(rdev), debugfs_root);
@@ -3692,11 +3678,6 @@ regulator_register(const struct regulator_desc *regulator_desc,
        if (ret < 0)
                goto scrub;
 
-       /* add attributes supported by this regulator */
-       ret = add_regulator_attributes(rdev);
-       if (ret < 0)
-               goto scrub;
-
        if (init_data && init_data->supply_regulator)
                supply = init_data->supply_regulator;
        else if (regulator_desc->supply_name)