These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / leds / leds-syscon.c
index 6896e2d..b88900d 100644 (file)
@@ -20,6 +20,7 @@
  * MA 02111-1307 USA
  */
 #include <linux/io.h>
+#include <linux/module.h>
 #include <linux/of_device.h>
 #include <linux/of_address.h>
 #include <linux/platform_device.h>
@@ -66,102 +67,101 @@ static void syscon_led_set(struct led_classdev *led_cdev,
                dev_err(sled->cdev.dev, "error updating LED status\n");
 }
 
-static int __init syscon_leds_spawn(struct device_node *np,
-                                   struct device *dev,
-                                   struct regmap *map)
+static int syscon_led_probe(struct platform_device *pdev)
 {
-       struct device_node *child;
+       struct device *dev = &pdev->dev;
+       struct device_node *np = dev->of_node;
+       struct device *parent;
+       struct regmap *map;
+       struct syscon_led *sled;
+       const char *state;
        int ret;
 
-       for_each_available_child_of_node(np, child) {
-               struct syscon_led *sled;
-               const char *state;
-
-               /* Only check for register-bit-leds */
-               if (of_property_match_string(child, "compatible",
-                                            "register-bit-led") < 0)
-                       continue;
-
-               sled = devm_kzalloc(dev, sizeof(*sled), GFP_KERNEL);
-               if (!sled)
-                       return -ENOMEM;
-
-               sled->map = map;
-
-               if (of_property_read_u32(child, "offset", &sled->offset))
-                       return -EINVAL;
-               if (of_property_read_u32(child, "mask", &sled->mask))
-                       return -EINVAL;
-               sled->cdev.name =
-                       of_get_property(child, "label", NULL) ? : child->name;
-               sled->cdev.default_trigger =
-                       of_get_property(child, "linux,default-trigger", NULL);
-
-               state = of_get_property(child, "default-state", NULL);
-               if (state) {
-                       if (!strcmp(state, "keep")) {
-                               u32 val;
-
-                               ret = regmap_read(map, sled->offset, &val);
-                               if (ret < 0)
-                                       return ret;
-                               sled->state = !!(val & sled->mask);
-                       } else if (!strcmp(state, "on")) {
-                               sled->state = true;
-                               ret = regmap_update_bits(map, sled->offset,
-                                                        sled->mask,
-                                                        sled->mask);
-                               if (ret < 0)
-                                       return ret;
-                       } else {
-                               sled->state = false;
-                               ret = regmap_update_bits(map, sled->offset,
-                                                        sled->mask, 0);
-                               if (ret < 0)
-                                       return ret;
-                       }
+       parent = dev->parent;
+       if (!parent) {
+               dev_err(dev, "no parent for syscon LED\n");
+               return -ENODEV;
+       }
+       map = syscon_node_to_regmap(parent->of_node);
+       if (IS_ERR(map)) {
+               dev_err(dev, "no regmap for syscon LED parent\n");
+               return PTR_ERR(map);
+       }
+
+       sled = devm_kzalloc(dev, sizeof(*sled), GFP_KERNEL);
+       if (!sled)
+               return -ENOMEM;
+
+       sled->map = map;
+
+       if (of_property_read_u32(np, "offset", &sled->offset))
+               return -EINVAL;
+       if (of_property_read_u32(np, "mask", &sled->mask))
+               return -EINVAL;
+       sled->cdev.name =
+               of_get_property(np, "label", NULL) ? : np->name;
+       sled->cdev.default_trigger =
+               of_get_property(np, "linux,default-trigger", NULL);
+
+       state = of_get_property(np, "default-state", NULL);
+       if (state) {
+               if (!strcmp(state, "keep")) {
+                       u32 val;
+
+                       ret = regmap_read(map, sled->offset, &val);
+                       if (ret < 0)
+                               return ret;
+                       sled->state = !!(val & sled->mask);
+               } else if (!strcmp(state, "on")) {
+                       sled->state = true;
+                       ret = regmap_update_bits(map, sled->offset,
+                                                sled->mask,
+                                                sled->mask);
+                       if (ret < 0)
+                               return ret;
+               } else {
+                       sled->state = false;
+                       ret = regmap_update_bits(map, sled->offset,
+                                                sled->mask, 0);
+                       if (ret < 0)
+                               return ret;
                }
-               sled->cdev.brightness_set = syscon_led_set;
+       }
+       sled->cdev.brightness_set = syscon_led_set;
 
-               ret = led_classdev_register(dev, &sled->cdev);
-               if (ret < 0)
-                       return ret;
+       ret = led_classdev_register(dev, &sled->cdev);
+       if (ret < 0)
+               return ret;
+
+       platform_set_drvdata(pdev, sled);
+       dev_info(dev, "registered LED %s\n", sled->cdev.name);
 
-               dev_info(dev, "registered LED %s\n", sled->cdev.name);
-       }
        return 0;
 }
 
-static int __init syscon_leds_init(void)
+static int syscon_led_remove(struct platform_device *pdev)
 {
-       struct device_node *np;
-
-       for_each_of_allnodes(np) {
-               struct platform_device *pdev;
-               struct regmap *map;
-               int ret;
+       struct syscon_led *sled = platform_get_drvdata(pdev);
 
-               if (!of_device_is_compatible(np, "syscon"))
-                       continue;
+       led_classdev_unregister(&sled->cdev);
+       /* Turn it off */
+       regmap_update_bits(sled->map, sled->offset, sled->mask, 0);
+       return 0;
+}
 
-               map = syscon_node_to_regmap(np);
-               if (IS_ERR(map)) {
-                       pr_err("error getting regmap for syscon LEDs\n");
-                       continue;
-               }
+static const struct of_device_id of_syscon_leds_match[] = {
+       { .compatible = "register-bit-led", },
+       {},
+};
 
-               /*
-                * If the map is there, the device should be there, we allocate
-                * memory on the syscon device's behalf here.
-                */
-               pdev = of_find_device_by_node(np);
-               if (!pdev)
-                       return -ENODEV;
-               ret = syscon_leds_spawn(np, &pdev->dev, map);
-               if (ret)
-                       dev_err(&pdev->dev, "could not spawn syscon LEDs\n");
-       }
+MODULE_DEVICE_TABLE(of, of_syscon_leds_match);
 
-       return 0;
-}
-device_initcall(syscon_leds_init);
+static struct platform_driver syscon_led_driver = {
+       .probe          = syscon_led_probe,
+       .remove         = syscon_led_remove,
+       .driver         = {
+               .name   = "leds-syscon",
+               .of_match_table = of_syscon_leds_match,
+       },
+};
+module_platform_driver(syscon_led_driver);