Re: [PATCH] leds: pca963x: add runtime pm support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Matt,

You shouldn't define your own pm ops in the driver - it is already
accomplished in drivers/leds/led-class.c:

static int __init leds_init(void)
{
        leds_class = class_create(THIS_MODULE, "leds");
        if (IS_ERR(leds_class))
                return PTR_ERR(leds_class);
        leds_class->pm = &leds_class_dev_pm_ops; <--------------
        leds_class->dev_groups = led_groups;
        return 0;
}

LED class drivers shouldn't use pm_runtime API directly, but rely
on the pm ops defined for the class instead.

Every call to led_classdev_register() results in a call to

device_create_with_groups(leds_class, parent, 0,
                          led_cdev, led_cdev->groups, "%s", name);

As you see led_class is passed in the first argument. Beneath it results
in adding a device to the PM core's list of active devices, with pm ops
defined for the leds_class.

Any suspend/resume event results in calling relevant pm op for each
registered LED class device, not per LED class driver. LED class
define generic pm ops for the whole LED subsystem:

void led_classdev_suspend(struct led_classdev *led_cdev)
{
        led_cdev->flags |= LED_SUSPENDED;
        led_set_brightness_nopm(led_cdev, 0);
}

void led_classdev_resume(struct led_classdev *led_cdev)
{
        led_set_brightness_nopm(led_cdev, led_cdev->brightness);

        if (led_cdev->flash_resume)
                led_cdev->flash_resume(led_cdev);

        led_cdev->flags &= ~LED_SUSPENDED;
}

The only thing a LED class driver needs to to know is the number
of LEDs with brightness greater than 0. If it drops to 0, then it
should put the device in a power down mode. If the number is greater
then 0 it should power the device up.


On 10/18/2016 05:17 PM, Matt Ranostay wrote:
Allow chip to enter low power state when no LEDs are being lit or in
blink mode.

Cc: Tony Lindgren <tony@xxxxxxxxxxx>
Cc: Jacek Anaszewski <j.anaszewski@xxxxxxxxxxx>
Signed-off-by: Matt Ranostay <matt@ranostay.consulting>
---
 drivers/leds/leds-pca963x.c | 47 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 47 insertions(+)

diff --git a/drivers/leds/leds-pca963x.c b/drivers/leds/leds-pca963x.c
index 407eba11e187..a6b954b31fcd 100644
--- a/drivers/leds/leds-pca963x.c
+++ b/drivers/leds/leds-pca963x.c
@@ -35,6 +35,7 @@
 #include <linux/slab.h>
 #include <linux/of.h>
 #include <linux/platform_data/leds-pca963x.h>
+#include <linux/pm_runtime.h>

 /* LED select registers determine the source that drives LED outputs */
 #define PCA963X_LED_OFF		0x0	/* LED driver off */
@@ -108,6 +109,7 @@ struct pca963x_led {
 	struct pca963x *chip;
 	struct led_classdev led_cdev;
 	int led_num; /* 0 .. 15 potentially */
+	enum led_brightness brightness;
 	char name[32];
 	u8 gdc;
 	u8 gfrq;
@@ -116,6 +118,8 @@ struct pca963x_led {
 static int pca963x_brightness(struct pca963x_led *pca963x,
 			       enum led_brightness brightness)
 {
+	struct i2c_client *client = pca963x->chip->client;
+	enum led_brightness prev_brightness = pca963x->brightness;
 	u8 ledout_addr = pca963x->chip->chipdef->ledout_base
 		+ (pca963x->led_num / 4);
 	u8 ledout;
@@ -123,8 +127,12 @@ static int pca963x_brightness(struct pca963x_led *pca963x,
 	u8 mask = 0x3 << shift;
 	int ret;

+	if (brightness == prev_brightness)
+		return 0;
+
 	mutex_lock(&pca963x->chip->mutex);
 	ledout = i2c_smbus_read_byte_data(pca963x->chip->client, ledout_addr);
+
 	switch (brightness) {
 	case LED_FULL:
 		ret = i2c_smbus_write_byte_data(pca963x->chip->client,
@@ -132,6 +140,9 @@ static int pca963x_brightness(struct pca963x_led *pca963x,
 			(ledout & ~mask) | (PCA963X_LED_ON << shift));
 		break;
 	case LED_OFF:
+		ret = pm_runtime_put(&client->dev);
+		if (ret < 0)
+			goto unlock;

Here you should check the number of the LEDs with brightness > 0
instead. If the number is 0 then you should call
pca963x_runtime_suspend().

 		ret = i2c_smbus_write_byte_data(pca963x->chip->client,
 			ledout_addr, ledout & ~mask);
 		break;
@@ -146,7 +157,15 @@ static int pca963x_brightness(struct pca963x_led *pca963x,
 			(ledout & ~mask) | (PCA963X_LED_PWM << shift));
 		break;
 	}
+
+	if (prev_brightness == LED_OFF) {
+		ret = pm_runtime_get_sync(&client->dev);

Similarly here pca963x_runtime_resume() should be called.
Of course a counter for the LEDs with brightness greater than 0
should be added and maintained.

+		if (ret < 0)
+			goto unlock;
+	}
 unlock:
+	pca963x->brightness = brightness;
+
 	mutex_unlock(&pca963x->chip->mutex);
 	return ret;
 }
@@ -350,6 +369,7 @@ static int pca963x_probe(struct i2c_client *client,
 		return -ENOMEM;

 	i2c_set_clientdata(client, pca963x_chip);
+	dev_set_drvdata(&client->dev, client);

 	mutex_init(&pca963x_chip->mutex);
 	pca963x_chip->chipdef = chip;
@@ -402,6 +422,12 @@ static int pca963x_probe(struct i2c_client *client,
 			i2c_smbus_write_byte_data(client, PCA963X_MODE2, 0x05);
 	}

+	err = pm_runtime_set_active(&client->dev);
+	if (err)
+		goto exit;
+	pm_runtime_enable(&client->dev);
+	pm_runtime_idle(&client->dev);
+
 	return 0;

 exit:
@@ -416,16 +442,37 @@ static int pca963x_remove(struct i2c_client *client)
 	struct pca963x *pca963x = i2c_get_clientdata(client);
 	int i;

+	pm_runtime_disable(&client->dev);
+
 	for (i = 0; i < pca963x->chipdef->n_leds; i++)
 		led_classdev_unregister(&pca963x->leds[i].led_cdev);

 	return 0;
 }

+static int pca963x_runtime_suspend(struct device *dev)
+{
+	struct i2c_client *client = dev_get_drvdata(dev);
+
+	return i2c_smbus_write_byte_data(client, PCA963X_MODE1, BIT(4));
+}
+
+static int pca963x_runtime_resume(struct device *dev)
+{
+	struct i2c_client *client = dev_get_drvdata(dev);
+
+	return i2c_smbus_write_byte_data(client, PCA963X_MODE1, 0x00);
+}
+
+const static struct dev_pm_ops pca963x_pms = {
+	SET_RUNTIME_PM_OPS(pca963x_runtime_suspend, pca963x_runtime_resume, NULL)
+};
+
 static struct i2c_driver pca963x_driver = {
 	.driver = {
 		.name	= "leds-pca963x",
 		.of_match_table = of_match_ptr(of_pca963x_match),
+		.pm	= &pca963x_pms,
 	},
 	.probe	= pca963x_probe,
 	.remove	= pca963x_remove,



--
Best regards,
Jacek Anaszewski
--
To unsubscribe from this list: send the line "unsubscribe linux-leds" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux OMAP]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]

  Powered by Linux