summaryrefslogtreecommitdiff
path: root/drivers/leds/leds-ns2.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/leds/leds-ns2.c')
-rw-r--r--drivers/leds/leds-ns2.c346
1 files changed, 104 insertions, 242 deletions
diff --git a/drivers/leds/leds-ns2.c b/drivers/leds/leds-ns2.c
index bd806e7c8017..1677d66d8b0e 100644
--- a/drivers/leds/leds-ns2.c
+++ b/drivers/leds/leds-ns2.c
@@ -24,25 +24,16 @@ enum ns2_led_modes {
NS_V2_LED_SATA,
};
+/*
+ * If the size of this structure or types of its members is changed,
+ * the filling of array modval in function ns2_led_register must be changed
+ * accordingly.
+ */
struct ns2_led_modval {
- enum ns2_led_modes mode;
- int cmd_level;
- int slow_level;
-};
-
-struct ns2_led {
- const char *name;
- const char *default_trigger;
- struct gpio_desc *cmd;
- struct gpio_desc *slow;
- int num_modes;
- struct ns2_led_modval *modval;
-};
-
-struct ns2_led_platform_data {
- int num_leds;
- struct ns2_led *leds;
-};
+ u32 mode;
+ u32 cmd_level;
+ u32 slow_level;
+} __packed;
/*
* The Network Space v2 dual-GPIO LED is wired to a CPLD. Three different LED
@@ -51,7 +42,7 @@ struct ns2_led_platform_data {
* for the command/slow GPIOs corresponds to a LED mode.
*/
-struct ns2_led_data {
+struct ns2_led {
struct led_classdev cdev;
struct gpio_desc *cmd;
struct gpio_desc *slow;
@@ -62,77 +53,67 @@ struct ns2_led_data {
struct ns2_led_modval *modval;
};
-static int ns2_led_get_mode(struct ns2_led_data *led_dat,
- enum ns2_led_modes *mode)
+static int ns2_led_get_mode(struct ns2_led *led, enum ns2_led_modes *mode)
{
int i;
- int ret = -EINVAL;
int cmd_level;
int slow_level;
- cmd_level = gpiod_get_value_cansleep(led_dat->cmd);
- slow_level = gpiod_get_value_cansleep(led_dat->slow);
+ cmd_level = gpiod_get_value_cansleep(led->cmd);
+ slow_level = gpiod_get_value_cansleep(led->slow);
- for (i = 0; i < led_dat->num_modes; i++) {
- if (cmd_level == led_dat->modval[i].cmd_level &&
- slow_level == led_dat->modval[i].slow_level) {
- *mode = led_dat->modval[i].mode;
- ret = 0;
- break;
+ for (i = 0; i < led->num_modes; i++) {
+ if (cmd_level == led->modval[i].cmd_level &&
+ slow_level == led->modval[i].slow_level) {
+ *mode = led->modval[i].mode;
+ return 0;
}
}
- return ret;
+ return -EINVAL;
}
-static void ns2_led_set_mode(struct ns2_led_data *led_dat,
- enum ns2_led_modes mode)
+static void ns2_led_set_mode(struct ns2_led *led, enum ns2_led_modes mode)
{
int i;
- bool found = false;
unsigned long flags;
- for (i = 0; i < led_dat->num_modes; i++)
- if (mode == led_dat->modval[i].mode) {
- found = true;
+ for (i = 0; i < led->num_modes; i++)
+ if (mode == led->modval[i].mode)
break;
- }
- if (!found)
+ if (i == led->num_modes)
return;
- write_lock_irqsave(&led_dat->rw_lock, flags);
+ write_lock_irqsave(&led->rw_lock, flags);
- if (!led_dat->can_sleep) {
- gpiod_set_value(led_dat->cmd,
- led_dat->modval[i].cmd_level);
- gpiod_set_value(led_dat->slow,
- led_dat->modval[i].slow_level);
+ if (!led->can_sleep) {
+ gpiod_set_value(led->cmd, led->modval[i].cmd_level);
+ gpiod_set_value(led->slow, led->modval[i].slow_level);
goto exit_unlock;
}
- gpiod_set_value_cansleep(led_dat->cmd, led_dat->modval[i].cmd_level);
- gpiod_set_value_cansleep(led_dat->slow, led_dat->modval[i].slow_level);
+ gpiod_set_value_cansleep(led->cmd, led->modval[i].cmd_level);
+ gpiod_set_value_cansleep(led->slow, led->modval[i].slow_level);
exit_unlock:
- write_unlock_irqrestore(&led_dat->rw_lock, flags);
+ write_unlock_irqrestore(&led->rw_lock, flags);
}
static void ns2_led_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
- struct ns2_led_data *led_dat =
- container_of(led_cdev, struct ns2_led_data, cdev);
+ struct ns2_led *led = container_of(led_cdev, struct ns2_led, cdev);
enum ns2_led_modes mode;
if (value == LED_OFF)
mode = NS_V2_LED_OFF;
- else if (led_dat->sata)
+ else if (led->sata)
mode = NS_V2_LED_SATA;
else
mode = NS_V2_LED_ON;
- ns2_led_set_mode(led_dat, mode);
+ ns2_led_set_mode(led, mode);
}
static int ns2_led_set_blocking(struct led_classdev *led_cdev,
@@ -147,8 +128,7 @@ static ssize_t ns2_led_sata_store(struct device *dev,
const char *buff, size_t count)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
- struct ns2_led_data *led_dat =
- container_of(led_cdev, struct ns2_led_data, cdev);
+ struct ns2_led *led = container_of(led_cdev, struct ns2_led, cdev);
int ret;
unsigned long enable;
@@ -158,18 +138,18 @@ static ssize_t ns2_led_sata_store(struct device *dev,
enable = !!enable;
- if (led_dat->sata == enable)
+ if (led->sata == enable)
goto exit;
- led_dat->sata = enable;
+ led->sata = enable;
if (!led_get_brightness(led_cdev))
goto exit;
if (enable)
- ns2_led_set_mode(led_dat, NS_V2_LED_SATA);
+ ns2_led_set_mode(led, NS_V2_LED_SATA);
else
- ns2_led_set_mode(led_dat, NS_V2_LED_ON);
+ ns2_led_set_mode(led, NS_V2_LED_ON);
exit:
return count;
@@ -179,10 +159,9 @@ static ssize_t ns2_led_sata_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
- struct ns2_led_data *led_dat =
- container_of(led_cdev, struct ns2_led_data, cdev);
+ struct ns2_led *led = container_of(led_cdev, struct ns2_led, cdev);
- return sprintf(buf, "%d\n", led_dat->sata);
+ return sprintf(buf, "%d\n", led->sata);
}
static DEVICE_ATTR(sata, 0644, ns2_led_sata_show, ns2_led_sata_store);
@@ -193,147 +172,94 @@ static struct attribute *ns2_led_attrs[] = {
};
ATTRIBUTE_GROUPS(ns2_led);
-static int
-create_ns2_led(struct platform_device *pdev, struct ns2_led_data *led_dat,
- const struct ns2_led *template)
+static int ns2_led_register(struct device *dev, struct fwnode_handle *node,
+ struct ns2_led *led)
{
- int ret;
+ struct led_init_data init_data = {};
+ struct ns2_led_modval *modval;
enum ns2_led_modes mode;
+ int nmodes, ret;
+
+ led->cmd = devm_fwnode_gpiod_get_index(dev, node, "cmd", 0, GPIOD_ASIS,
+ fwnode_get_name(node));
+ if (IS_ERR(led->cmd))
+ return PTR_ERR(led->cmd);
+
+ led->slow = devm_fwnode_gpiod_get_index(dev, node, "slow", 0,
+ GPIOD_ASIS,
+ fwnode_get_name(node));
+ if (IS_ERR(led->slow))
+ return PTR_ERR(led->slow);
+
+ ret = fwnode_property_count_u32(node, "modes-map");
+ if (ret < 0 || ret % 3) {
+ dev_err(dev, "Missing or malformed modes-map for %pfw\n", node);
+ return -EINVAL;
+ }
+
+ nmodes = ret / 3;
+ modval = devm_kcalloc(dev, nmodes, sizeof(*modval), GFP_KERNEL);
+ if (!modval)
+ return -ENOMEM;
+
+ fwnode_property_read_u32_array(node, "modes-map", (void *)modval,
+ nmodes * 3);
+
+ rwlock_init(&led->rw_lock);
- rwlock_init(&led_dat->rw_lock);
-
- led_dat->cdev.name = template->name;
- led_dat->cdev.default_trigger = template->default_trigger;
- led_dat->cdev.blink_set = NULL;
- led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
- led_dat->cdev.groups = ns2_led_groups;
- led_dat->cmd = template->cmd;
- led_dat->slow = template->slow;
- led_dat->can_sleep = gpiod_cansleep(led_dat->cmd) |
- gpiod_cansleep(led_dat->slow);
- if (led_dat->can_sleep)
- led_dat->cdev.brightness_set_blocking = ns2_led_set_blocking;
+ led->cdev.blink_set = NULL;
+ led->cdev.flags |= LED_CORE_SUSPENDRESUME;
+ led->cdev.groups = ns2_led_groups;
+ led->can_sleep = gpiod_cansleep(led->cmd) || gpiod_cansleep(led->slow);
+ if (led->can_sleep)
+ led->cdev.brightness_set_blocking = ns2_led_set_blocking;
else
- led_dat->cdev.brightness_set = ns2_led_set;
- led_dat->modval = template->modval;
- led_dat->num_modes = template->num_modes;
+ led->cdev.brightness_set = ns2_led_set;
+ led->num_modes = nmodes;
+ led->modval = modval;
- ret = ns2_led_get_mode(led_dat, &mode);
+ ret = ns2_led_get_mode(led, &mode);
if (ret < 0)
return ret;
/* Set LED initial state. */
- led_dat->sata = (mode == NS_V2_LED_SATA) ? 1 : 0;
- led_dat->cdev.brightness =
- (mode == NS_V2_LED_OFF) ? LED_OFF : LED_FULL;
+ led->sata = (mode == NS_V2_LED_SATA) ? 1 : 0;
+ led->cdev.brightness = (mode == NS_V2_LED_OFF) ? LED_OFF : LED_FULL;
- ret = led_classdev_register(&pdev->dev, &led_dat->cdev);
- if (ret < 0)
- return ret;
+ init_data.fwnode = node;
- return 0;
-}
+ ret = devm_led_classdev_register_ext(dev, &led->cdev, &init_data);
+ if (ret)
+ dev_err(dev, "Failed to register LED for node %pfw\n", node);
-static void delete_ns2_led(struct ns2_led_data *led_dat)
-{
- led_classdev_unregister(&led_dat->cdev);
+ return ret;
}
-#ifdef CONFIG_OF_GPIO
-/*
- * Translate OpenFirmware node properties into platform_data.
- */
-static int
-ns2_leds_get_of_pdata(struct device *dev, struct ns2_led_platform_data *pdata)
+static int ns2_led_probe(struct platform_device *pdev)
{
- struct device_node *np = dev->of_node;
- struct device_node *child;
- struct ns2_led *led, *leds;
- int ret, num_leds = 0;
+ struct device *dev = &pdev->dev;
+ struct fwnode_handle *child;
+ struct ns2_led *leds;
+ int count;
+ int ret;
- num_leds = of_get_child_count(np);
- if (!num_leds)
+ count = device_get_child_node_count(dev);
+ if (!count)
return -ENODEV;
- leds = devm_kcalloc(dev, num_leds, sizeof(struct ns2_led),
- GFP_KERNEL);
+ leds = devm_kzalloc(dev, array_size(sizeof(*leds), count), GFP_KERNEL);
if (!leds)
return -ENOMEM;
- led = leds;
- for_each_child_of_node(np, child) {
- const char *string;
- int i, num_modes;
- struct ns2_led_modval *modval;
- struct gpio_desc *gd;
-
- ret = of_property_read_string(child, "label", &string);
- led->name = (ret == 0) ? string : child->name;
-
- gd = gpiod_get_from_of_node(child, "cmd-gpio", 0,
- GPIOD_ASIS, led->name);
- if (IS_ERR(gd)) {
- ret = PTR_ERR(gd);
- goto err_node_put;
- }
- led->cmd = gd;
- gd = gpiod_get_from_of_node(child, "slow-gpio", 0,
- GPIOD_ASIS, led->name);
- if (IS_ERR(gd)) {
- ret = PTR_ERR(gd);
- goto err_node_put;
- }
- led->slow = gd;
-
- ret = of_property_read_string(child, "linux,default-trigger",
- &string);
- if (ret == 0)
- led->default_trigger = string;
-
- ret = of_property_count_u32_elems(child, "modes-map");
- if (ret < 0 || ret % 3) {
- dev_err(dev,
- "Missing or malformed modes-map property\n");
- ret = -EINVAL;
- goto err_node_put;
- }
-
- num_modes = ret / 3;
- modval = devm_kcalloc(dev,
- num_modes,
- sizeof(struct ns2_led_modval),
- GFP_KERNEL);
- if (!modval) {
- ret = -ENOMEM;
- goto err_node_put;
- }
-
- for (i = 0; i < num_modes; i++) {
- of_property_read_u32_index(child,
- "modes-map", 3 * i,
- (u32 *) &modval[i].mode);
- of_property_read_u32_index(child,
- "modes-map", 3 * i + 1,
- (u32 *) &modval[i].cmd_level);
- of_property_read_u32_index(child,
- "modes-map", 3 * i + 2,
- (u32 *) &modval[i].slow_level);
+ device_for_each_child_node(dev, child) {
+ ret = ns2_led_register(dev, child, leds++);
+ if (ret) {
+ fwnode_handle_put(child);
+ return ret;
}
-
- led->num_modes = num_modes;
- led->modval = modval;
-
- led++;
}
- pdata->leds = leds;
- pdata->num_leds = num_leds;
-
return 0;
-
-err_node_put:
- of_node_put(child);
- return ret;
}
static const struct of_device_id of_ns2_leds_match[] = {
@@ -341,76 +267,12 @@ static const struct of_device_id of_ns2_leds_match[] = {
{},
};
MODULE_DEVICE_TABLE(of, of_ns2_leds_match);
-#endif /* CONFIG_OF_GPIO */
-
-struct ns2_led_priv {
- int num_leds;
- struct ns2_led_data leds_data[];
-};
-
-static int ns2_led_probe(struct platform_device *pdev)
-{
- struct ns2_led_platform_data *pdata = dev_get_platdata(&pdev->dev);
- struct ns2_led_priv *priv;
- int i;
- int ret;
-
-#ifdef CONFIG_OF_GPIO
- if (!pdata) {
- pdata = devm_kzalloc(&pdev->dev,
- sizeof(struct ns2_led_platform_data),
- GFP_KERNEL);
- if (!pdata)
- return -ENOMEM;
-
- ret = ns2_leds_get_of_pdata(&pdev->dev, pdata);
- if (ret)
- return ret;
- }
-#else
- if (!pdata)
- return -EINVAL;
-#endif /* CONFIG_OF_GPIO */
-
- priv = devm_kzalloc(&pdev->dev, struct_size(priv, leds_data, pdata->num_leds), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
- priv->num_leds = pdata->num_leds;
-
- for (i = 0; i < priv->num_leds; i++) {
- ret = create_ns2_led(pdev, &priv->leds_data[i],
- &pdata->leds[i]);
- if (ret < 0) {
- for (i = i - 1; i >= 0; i--)
- delete_ns2_led(&priv->leds_data[i]);
- return ret;
- }
- }
-
- platform_set_drvdata(pdev, priv);
-
- return 0;
-}
-
-static int ns2_led_remove(struct platform_device *pdev)
-{
- int i;
- struct ns2_led_priv *priv;
-
- priv = platform_get_drvdata(pdev);
-
- for (i = 0; i < priv->num_leds; i++)
- delete_ns2_led(&priv->leds_data[i]);
-
- return 0;
-}
static struct platform_driver ns2_led_driver = {
.probe = ns2_led_probe,
- .remove = ns2_led_remove,
.driver = {
.name = "leds-ns2",
- .of_match_table = of_match_ptr(of_ns2_leds_match),
+ .of_match_table = of_ns2_leds_match,
},
};