From 0c596a7ebb51b515b2d59a77b8cd37c5e98b1aa5 Mon Sep 17 00:00:00 2001 From: Marek Belisko Date: Fri, 30 Nov 2012 14:00:49 -0800 Subject: leds/tca6507: Add support for devicetree. Support added only for leds (not for gpio's). (cooloney@gmail.com: fix 2 building errors) Signed-off-by: Marek Belisko Signed-off-by: Bryan Wu --- drivers/leds/leds-tca6507.c | 76 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 72 insertions(+), 4 deletions(-) diff --git a/drivers/leds/leds-tca6507.c b/drivers/leds/leds-tca6507.c index b26a63bae16..220fc7fbf1f 100644 --- a/drivers/leds/leds-tca6507.c +++ b/drivers/leds/leds-tca6507.c @@ -667,8 +667,72 @@ static void tca6507_remove_gpio(struct tca6507_chip *tca) } #endif /* CONFIG_GPIOLIB */ +#ifdef CONFIG_OF +static struct tca6507_platform_data * +tca6507_led_dt_init(struct i2c_client *client) +{ + struct device_node *np = client->dev.of_node, *child; + struct tca6507_platform_data *pdata; + struct led_info *tca_leds; + int count = 0; + + for_each_child_of_node(np, child) + count++; + if (!count) + return ERR_PTR(-ENODEV); + + if (count > NUM_LEDS) + return ERR_PTR(-ENODEV); + + tca_leds = devm_kzalloc(&client->dev, + sizeof(struct led_info) * count, GFP_KERNEL); + if (!tca_leds) + return ERR_PTR(-ENOMEM); + + for_each_child_of_node(np, child) { + struct led_info led; + u32 reg; + int ret; + + led.name = + of_get_property(child, "label", NULL) ? : child->name; + led.default_trigger = + of_get_property(child, "linux,default-trigger", NULL); + + ret = of_property_read_u32(child, "reg", ®); + if (ret != 0) + continue; + + tca_leds[reg] = led; + } + pdata = devm_kzalloc(&client->dev, + sizeof(struct tca6507_platform_data), GFP_KERNEL); + if (!pdata) + return ERR_PTR(-ENOMEM); + + pdata->leds.leds = tca_leds; + pdata->leds.num_leds = count; + + return pdata; +} + +static const struct of_device_id of_tca6507_leds_match[] = { + { .compatible = "ti,tca6507", }, + {}, +}; + +#else +static struct tca6507_platform_data * +tca6507_led_dt_init(struct i2c_client *client) +{ + return ERR_PTR(-ENODEV); +} + +#define of_tca6507_leds_match NULL +#endif + static int tca6507_probe(struct i2c_client *client, - const struct i2c_device_id *id) + const struct i2c_device_id *id) { struct tca6507_chip *tca; struct i2c_adapter *adapter; @@ -683,9 +747,12 @@ static int tca6507_probe(struct i2c_client *client, return -EIO; if (!pdata || pdata->leds.num_leds != NUM_LEDS) { - dev_err(&client->dev, "Need %d entries in platform-data list\n", - NUM_LEDS); - return -ENODEV; + pdata = tca6507_led_dt_init(client); + if (IS_ERR(pdata)) { + dev_err(&client->dev, "Need %d entries in platform-data list\n", + NUM_LEDS); + return PTR_ERR(pdata); + } } tca = devm_kzalloc(&client->dev, sizeof(*tca), GFP_KERNEL); if (!tca) @@ -750,6 +817,7 @@ static struct i2c_driver tca6507_driver = { .driver = { .name = "leds-tca6507", .owner = THIS_MODULE, + .of_match_table = of_tca6507_leds_match, }, .probe = tca6507_probe, .remove = tca6507_remove, -- cgit v1.2.3 From 60c681a92a1bebcfccf37f446fa4e1b8f36ccbb1 Mon Sep 17 00:00:00 2001 From: Marek Belisko Date: Fri, 30 Nov 2012 14:00:50 -0800 Subject: leds/tca6507: DT: Add documentation for tca6507 devicetree bindings. Signed-off-by: Marek Belisko Signed-off-by: Bryan Wu --- Documentation/devicetree/bindings/leds/tca6507.txt | 33 ++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 Documentation/devicetree/bindings/leds/tca6507.txt diff --git a/Documentation/devicetree/bindings/leds/tca6507.txt b/Documentation/devicetree/bindings/leds/tca6507.txt new file mode 100644 index 00000000000..2b6693b972f --- /dev/null +++ b/Documentation/devicetree/bindings/leds/tca6507.txt @@ -0,0 +1,33 @@ +LEDs conected to tca6507 + +Required properties: +- compatible : should be : "ti,tca6507". + +Each led is represented as a sub-node of the ti,tca6507 device. + +LED sub-node properties: +- label : (optional) see Documentation/devicetree/bindings/leds/common.txt +- reg : number of LED line (could be from 0 to 6) +- linux,default-trigger : (optional) + see Documentation/devicetree/bindings/leds/common.txt + +Examples: + +tca6507@45 { + compatible = "ti,tca6507"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x45>; + + led0: red-aux@0 { + label = "red:aux"; + reg = <0x0>; + }; + + led1: green-aux@1 { + label = "green:aux"; + reg = <0x5>; + linux,default-trigger = "default-on"; + }; +}; + -- cgit v1.2.3 From 8614fb46637a993258146022b0140d553132a0b7 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 25 Dec 2012 02:16:54 -0800 Subject: leds: pca9532: Convert to devm_input_allocate_device() Signed-off-by: Axel Lin Signed-off-by: Bryan Wu --- drivers/leds/leds-pca9532.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/leds/leds-pca9532.c b/drivers/leds/leds-pca9532.c index cee8a5b483a..43d08df6e8b 100644 --- a/drivers/leds/leds-pca9532.c +++ b/drivers/leds/leds-pca9532.c @@ -311,7 +311,6 @@ static int pca9532_destroy_devices(struct pca9532_data *data, int n_devs) break; case PCA9532_TYPE_N2100_BEEP: if (data->idev != NULL) { - input_unregister_device(data->idev); cancel_work_sync(&data->work); data->idev = NULL; } @@ -382,7 +381,7 @@ static int pca9532_configure(struct i2c_client *client, BUG_ON(data->idev); led->state = PCA9532_PWM1; pca9532_setled(led); - data->idev = input_allocate_device(); + data->idev = devm_input_allocate_device(&client->dev); if (data->idev == NULL) { err = -ENOMEM; goto exit; @@ -401,7 +400,6 @@ static int pca9532_configure(struct i2c_client *client, INIT_WORK(&data->work, pca9532_input_work); err = input_register_device(data->idev); if (err) { - input_free_device(data->idev); cancel_work_sync(&data->work); data->idev = NULL; goto exit; -- cgit v1.2.3 From 9ea6cdac9ba5fbc65adde4dc6a3cbee1206508df Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 21 Dec 2012 01:43:55 -0800 Subject: leds: leds-pwm: Convert to use devm_get_pwm Update the driver to use the new API for requesting pwm so we can take advantage of the pwm_lookup table to find the correct pwm to be used for the LED functionality. Signed-off-by: Peter Ujfalusi Signed-off-by: Bryan Wu --- drivers/leds/leds-pwm.c | 19 ++++++------------- include/linux/leds_pwm.h | 2 +- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/drivers/leds/leds-pwm.c b/drivers/leds/leds-pwm.c index 2157524f277..351257c2a7b 100644 --- a/drivers/leds/leds-pwm.c +++ b/drivers/leds/leds-pwm.c @@ -67,12 +67,11 @@ static int led_pwm_probe(struct platform_device *pdev) cur_led = &pdata->leds[i]; led_dat = &leds_data[i]; - led_dat->pwm = pwm_request(cur_led->pwm_id, - cur_led->name); + led_dat->pwm = devm_pwm_get(&pdev->dev, cur_led->name); if (IS_ERR(led_dat->pwm)) { ret = PTR_ERR(led_dat->pwm); - dev_err(&pdev->dev, "unable to request PWM %d\n", - cur_led->pwm_id); + dev_err(&pdev->dev, "unable to request PWM for %s\n", + cur_led->name); goto err; } @@ -86,10 +85,8 @@ static int led_pwm_probe(struct platform_device *pdev) led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; ret = led_classdev_register(&pdev->dev, &led_dat->cdev); - if (ret < 0) { - pwm_free(led_dat->pwm); + if (ret < 0) goto err; - } } platform_set_drvdata(pdev, leds_data); @@ -98,10 +95,8 @@ static int led_pwm_probe(struct platform_device *pdev) err: if (i > 0) { - for (i = i - 1; i >= 0; i--) { + for (i = i - 1; i >= 0; i--) led_classdev_unregister(&leds_data[i].cdev); - pwm_free(leds_data[i].pwm); - } } return ret; @@ -115,10 +110,8 @@ static int led_pwm_remove(struct platform_device *pdev) leds_data = platform_get_drvdata(pdev); - for (i = 0; i < pdata->num_leds; i++) { + for (i = 0; i < pdata->num_leds; i++) led_classdev_unregister(&leds_data[i].cdev); - pwm_free(leds_data[i].pwm); - } return 0; } diff --git a/include/linux/leds_pwm.h b/include/linux/leds_pwm.h index 33a07116748..a65e9646e4b 100644 --- a/include/linux/leds_pwm.h +++ b/include/linux/leds_pwm.h @@ -7,7 +7,7 @@ struct led_pwm { const char *name; const char *default_trigger; - unsigned pwm_id; + unsigned pwm_id __deprecated; u8 active_low; unsigned max_brightness; unsigned pwm_period_ns; -- cgit v1.2.3 From 0f86815ad0a2c0c70cbe0ecc2f80571726285461 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 21 Dec 2012 01:43:56 -0800 Subject: leds: leds-pwm: Preparing the driver for device tree support In order to be able to add device tree support for leds-pwm driver we need to rearrange the data structures used by the drivers. Signed-off-by: Peter Ujfalusi Signed-off-by: Bryan Wu --- drivers/leds/leds-pwm.c | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/drivers/leds/leds-pwm.c b/drivers/leds/leds-pwm.c index 351257c2a7b..c767837522f 100644 --- a/drivers/leds/leds-pwm.c +++ b/drivers/leds/leds-pwm.c @@ -30,6 +30,11 @@ struct led_pwm_data { unsigned int period; }; +struct led_pwm_priv { + int num_leds; + struct led_pwm_data leds[0]; +}; + static void led_pwm_set(struct led_classdev *led_cdev, enum led_brightness brightness) { @@ -47,25 +52,29 @@ static void led_pwm_set(struct led_classdev *led_cdev, } } +static inline size_t sizeof_pwm_leds_priv(int num_leds) +{ + return sizeof(struct led_pwm_priv) + + (sizeof(struct led_pwm_data) * num_leds); +} + static int led_pwm_probe(struct platform_device *pdev) { struct led_pwm_platform_data *pdata = pdev->dev.platform_data; - struct led_pwm *cur_led; - struct led_pwm_data *leds_data, *led_dat; + struct led_pwm_priv *priv; int i, ret = 0; if (!pdata) return -EBUSY; - leds_data = devm_kzalloc(&pdev->dev, - sizeof(struct led_pwm_data) * pdata->num_leds, - GFP_KERNEL); - if (!leds_data) + priv = devm_kzalloc(&pdev->dev, sizeof_pwm_leds_priv(pdata->num_leds), + GFP_KERNEL); + if (!priv) return -ENOMEM; for (i = 0; i < pdata->num_leds; i++) { - cur_led = &pdata->leds[i]; - led_dat = &leds_data[i]; + struct led_pwm *cur_led = &pdata->leds[i]; + struct led_pwm_data *led_dat = &priv->leds[i]; led_dat->pwm = devm_pwm_get(&pdev->dev, cur_led->name); if (IS_ERR(led_dat->pwm)) { @@ -88,15 +97,16 @@ static int led_pwm_probe(struct platform_device *pdev) if (ret < 0) goto err; } + priv->num_leds = pdata->num_leds; - platform_set_drvdata(pdev, leds_data); + platform_set_drvdata(pdev, priv); return 0; err: if (i > 0) { for (i = i - 1; i >= 0; i--) - led_classdev_unregister(&leds_data[i].cdev); + led_classdev_unregister(&priv->leds[i].cdev); } return ret; @@ -104,14 +114,11 @@ err: static int led_pwm_remove(struct platform_device *pdev) { + struct led_pwm_priv *priv = platform_get_drvdata(pdev); int i; - struct led_pwm_platform_data *pdata = pdev->dev.platform_data; - struct led_pwm_data *leds_data; - - leds_data = platform_get_drvdata(pdev); - for (i = 0; i < pdata->num_leds; i++) - led_classdev_unregister(&leds_data[i].cdev); + for (i = 0; i < priv->num_leds; i++) + led_classdev_unregister(&priv->leds[i].cdev); return 0; } -- cgit v1.2.3 From d4c0c4705bef1134339b038872ece3705a2783e0 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 21 Dec 2012 01:43:57 -0800 Subject: pwm: Correct parameter name in header for *pwm_get() functions To synchronize the header file definition and the actual code. In the code the consumer parameter is named as con_id, change the header file and replace consumer -> con_id in the parameter list. Signed-off-by: Peter Ujfalusi Ackedy-by: Thierry Reding Signed-off-by: Bryan Wu --- include/linux/pwm.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/linux/pwm.h b/include/linux/pwm.h index 6d661f32e0e..cc908a5396f 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -174,10 +174,10 @@ struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip, struct pwm_device *of_pwm_xlate_with_flags(struct pwm_chip *pc, const struct of_phandle_args *args); -struct pwm_device *pwm_get(struct device *dev, const char *consumer); +struct pwm_device *pwm_get(struct device *dev, const char *con_id); void pwm_put(struct pwm_device *pwm); -struct pwm_device *devm_pwm_get(struct device *dev, const char *consumer); +struct pwm_device *devm_pwm_get(struct device *dev, const char *con_id); void devm_pwm_put(struct device *dev, struct pwm_device *pwm); #else static inline int pwm_set_chip_data(struct pwm_device *pwm, void *data) -- cgit v1.2.3 From 8eb9612799605a7988d1c97cdc5980a5b8f04c56 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 21 Dec 2012 01:43:58 -0800 Subject: pwm: core: Rename of_pwm_request() to of_pwm_get() and export it Allow client driver to use of_pwm_get() to get the PWM they need. This is needed for drivers which handle more than one PWM separately, like leds-pwm driver, which have: pwmleds { compatible = "pwm-leds"; kpad { label = "omap4::keypad"; pwms = <&twl_pwm 0 7812500>; max-brightness = <127>; }; charging { label = "omap4:green:chrg"; pwms = <&twl_pwmled 0 7812500>; max-brightness = <255>; }; }; in the dts files. Signed-off-by: Peter Ujfalusi Acked-by: Thierry Reding Signed-off-by: Bryan Wu --- drivers/pwm/core.c | 8 ++++---- include/linux/pwm.h | 7 +++++++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 903138b1884..3cb741dc203 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -471,7 +471,7 @@ static struct pwm_chip *of_node_to_pwmchip(struct device_node *np) } /** - * of_pwm_request() - request a PWM via the PWM framework + * of_pwm_get() - request a PWM via the PWM framework * @np: device node to get the PWM from * @con_id: consumer name * @@ -486,8 +486,7 @@ static struct pwm_chip *of_node_to_pwmchip(struct device_node *np) * becomes mandatory for devices that look up the PWM device via the con_id * parameter. */ -static struct pwm_device *of_pwm_request(struct device_node *np, - const char *con_id) +struct pwm_device *of_pwm_get(struct device_node *np, const char *con_id) { struct pwm_device *pwm = NULL; struct of_phandle_args args; @@ -545,6 +544,7 @@ put: return pwm; } +EXPORT_SYMBOL_GPL(of_pwm_get); /** * pwm_add_table() - register PWM device consumers @@ -587,7 +587,7 @@ struct pwm_device *pwm_get(struct device *dev, const char *con_id) /* look up via DT first */ if (IS_ENABLED(CONFIG_OF) && dev && dev->of_node) - return of_pwm_request(dev->of_node, con_id); + return of_pwm_get(dev->of_node, con_id); /* * We look up the provider in the static table typically provided by diff --git a/include/linux/pwm.h b/include/linux/pwm.h index cc908a5396f..76a1959f2b2 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -175,6 +175,7 @@ struct pwm_device *of_pwm_xlate_with_flags(struct pwm_chip *pc, const struct of_phandle_args *args); struct pwm_device *pwm_get(struct device *dev, const char *con_id); +struct pwm_device *of_pwm_get(struct device_node *np, const char *con_id); void pwm_put(struct pwm_device *pwm); struct pwm_device *devm_pwm_get(struct device *dev, const char *con_id); @@ -213,6 +214,12 @@ static inline struct pwm_device *pwm_get(struct device *dev, return ERR_PTR(-ENODEV); } +static inline struct pwm_device *of_pwm_get(struct device_node *np, + const char *con_id) +{ + return ERR_PTR(-ENODEV); +} + static inline void pwm_put(struct pwm_device *pwm) { } -- cgit v1.2.3 From 261a5edd3ac77ecb4b33310a1dd1ed8d656f0569 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 21 Dec 2012 01:43:59 -0800 Subject: pwm: Add devm_of_pwm_get() as exported API for users When booted with DT users can use devm version of of_pwm_get() to benefit from automatic resource release. Signed-off-by: Peter Ujfalusi Acked-by: Thierry Reding Signed-off-by: Bryan Wu --- drivers/pwm/core.c | 30 ++++++++++++++++++++++++++++++ include/linux/pwm.h | 9 +++++++++ 2 files changed, 39 insertions(+) diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 3cb741dc203..4a13da48fef 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -708,6 +708,36 @@ struct pwm_device *devm_pwm_get(struct device *dev, const char *con_id) } EXPORT_SYMBOL_GPL(devm_pwm_get); +/** + * devm_of_pwm_get() - resource managed of_pwm_get() + * @dev: device for PWM consumer + * @np: device node to get the PWM from + * @con_id: consumer name + * + * This function performs like of_pwm_get() but the acquired PWM device will + * automatically be released on driver detach. + */ +struct pwm_device *devm_of_pwm_get(struct device *dev, struct device_node *np, + const char *con_id) +{ + struct pwm_device **ptr, *pwm; + + ptr = devres_alloc(devm_pwm_release, sizeof(**ptr), GFP_KERNEL); + if (!ptr) + return ERR_PTR(-ENOMEM); + + pwm = of_pwm_get(np, con_id); + if (!IS_ERR(pwm)) { + *ptr = pwm; + devres_add(dev, ptr); + } else { + devres_free(ptr); + } + + return pwm; +} +EXPORT_SYMBOL_GPL(devm_of_pwm_get); + static int devm_pwm_match(struct device *dev, void *res, void *data) { struct pwm_device **p = res; diff --git a/include/linux/pwm.h b/include/linux/pwm.h index 76a1959f2b2..70655a205b7 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -179,6 +179,8 @@ struct pwm_device *of_pwm_get(struct device_node *np, const char *con_id); void pwm_put(struct pwm_device *pwm); struct pwm_device *devm_pwm_get(struct device *dev, const char *con_id); +struct pwm_device *devm_of_pwm_get(struct device *dev, struct device_node *np, + const char *con_id); void devm_pwm_put(struct device *dev, struct pwm_device *pwm); #else static inline int pwm_set_chip_data(struct pwm_device *pwm, void *data) @@ -230,6 +232,13 @@ static inline struct pwm_device *devm_pwm_get(struct device *dev, return ERR_PTR(-ENODEV); } +static inline struct pwm_device *devm_of_pwm_get(struct device *dev, + struct device_node *np, + const char *con_id) +{ + return ERR_PTR(-ENODEV); +} + static inline void devm_pwm_put(struct device *dev, struct pwm_device *pwm) { } -- cgit v1.2.3 From 8a66a579083a20172a46e74d175a57621dccae0a Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 21 Dec 2012 01:44:00 -0800 Subject: leds: leds-pwm: Simplify cleanup code The code looks more nicer if we use: while (i--) instead: if (i > 0) for (i = i - 1; i >= 0; i--) Signed-off-by: Peter Ujfalusi Signed-off-by: Bryan Wu --- drivers/leds/leds-pwm.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/leds/leds-pwm.c b/drivers/leds/leds-pwm.c index c767837522f..46f4e44c6c6 100644 --- a/drivers/leds/leds-pwm.c +++ b/drivers/leds/leds-pwm.c @@ -104,10 +104,8 @@ static int led_pwm_probe(struct platform_device *pdev) return 0; err: - if (i > 0) { - for (i = i - 1; i >= 0; i--) - led_classdev_unregister(&priv->leds[i].cdev); - } + while (i--) + led_classdev_unregister(&priv->leds[i].cdev); return ret; } -- cgit v1.2.3 From 08541cbcc7386fa78efb454a92ddbfb1a2859cf9 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 21 Dec 2012 01:44:01 -0800 Subject: leds: leds-pwm: Add device tree bindings The DT binding for the pwm-leds devices are similar to the gpio-leds type. LEDs are represented as sub-nodes of the pwm-leds device. The code for handling the DT boot is based on the code found in the leds-gpio driver and adapted to use PWMs instead of GPIOs. To avoid having custom cleanup code in case of DT boot the newly created devm_of_pwm_get() API is used to get the correct PWM instance. For usage see: Documentation/devicetree/bindings/leds/leds-pwm.txt Signed-off-by: Peter Ujfalusi Acked-by: Grant Likely Signed-off-by: Bryan Wu --- .../devicetree/bindings/leds/leds-pwm.txt | 48 +++++++++ drivers/leds/leds-pwm.c | 112 +++++++++++++++++---- 2 files changed, 140 insertions(+), 20 deletions(-) create mode 100644 Documentation/devicetree/bindings/leds/leds-pwm.txt diff --git a/Documentation/devicetree/bindings/leds/leds-pwm.txt b/Documentation/devicetree/bindings/leds/leds-pwm.txt new file mode 100644 index 00000000000..7297107cf83 --- /dev/null +++ b/Documentation/devicetree/bindings/leds/leds-pwm.txt @@ -0,0 +1,48 @@ +LED connected to PWM + +Required properties: +- compatible : should be "pwm-leds". + +Each LED is represented as a sub-node of the pwm-leds device. Each +node's name represents the name of the corresponding LED. + +LED sub-node properties: +- pwms : PWM property to point to the PWM device (phandle)/port (id) and to + specify the period time to be used: <&phandle id period_ns>; +- pwm-names : (optional) Name to be used by the PWM subsystem for the PWM device + For the pwms and pwm-names property please refer to: + Documentation/devicetree/bindings/pwm/pwm.txt +- max-brightness : Maximum brightness possible for the LED +- label : (optional) + see Documentation/devicetree/bindings/leds/common.txt +- linux,default-trigger : (optional) + see Documentation/devicetree/bindings/leds/common.txt + +Example: + +twl_pwm: pwm { + /* provides two PWMs (id 0, 1 for PWM1 and PWM2) */ + compatible = "ti,twl6030-pwm"; + #pwm-cells = <2>; +}; + +twl_pwmled: pwmled { + /* provides one PWM (id 0 for Charing indicator LED) */ + compatible = "ti,twl6030-pwmled"; + #pwm-cells = <2>; +}; + +pwmleds { + compatible = "pwm-leds"; + kpad { + label = "omap4::keypad"; + pwms = <&twl_pwm 0 7812500>; + max-brightness = <127>; + }; + + charging { + label = "omap4:green:chrg"; + pwms = <&twl_pwmled 0 7812500>; + max-brightness = <255>; + }; +}; diff --git a/drivers/leds/leds-pwm.c b/drivers/leds/leds-pwm.c index 46f4e44c6c6..a1ea5f6a8d3 100644 --- a/drivers/leds/leds-pwm.c +++ b/drivers/leds/leds-pwm.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -58,46 +59,110 @@ static inline size_t sizeof_pwm_leds_priv(int num_leds) (sizeof(struct led_pwm_data) * num_leds); } -static int led_pwm_probe(struct platform_device *pdev) +static struct led_pwm_priv *led_pwm_create_of(struct platform_device *pdev) { - struct led_pwm_platform_data *pdata = pdev->dev.platform_data; + struct device_node *node = pdev->dev.of_node; + struct device_node *child; struct led_pwm_priv *priv; - int i, ret = 0; + int count, ret; - if (!pdata) - return -EBUSY; + /* count LEDs in this device, so we know how much to allocate */ + count = of_get_child_count(node); + if (!count) + return NULL; - priv = devm_kzalloc(&pdev->dev, sizeof_pwm_leds_priv(pdata->num_leds), + priv = devm_kzalloc(&pdev->dev, sizeof_pwm_leds_priv(count), GFP_KERNEL); if (!priv) - return -ENOMEM; + return NULL; - for (i = 0; i < pdata->num_leds; i++) { - struct led_pwm *cur_led = &pdata->leds[i]; - struct led_pwm_data *led_dat = &priv->leds[i]; + for_each_child_of_node(node, child) { + struct led_pwm_data *led_dat = &priv->leds[priv->num_leds]; - led_dat->pwm = devm_pwm_get(&pdev->dev, cur_led->name); + led_dat->cdev.name = of_get_property(child, "label", + NULL) ? : child->name; + + led_dat->pwm = devm_of_pwm_get(&pdev->dev, child, NULL); if (IS_ERR(led_dat->pwm)) { - ret = PTR_ERR(led_dat->pwm); dev_err(&pdev->dev, "unable to request PWM for %s\n", - cur_led->name); + led_dat->cdev.name); goto err; } + /* Get the period from PWM core when n*/ + led_dat->period = pwm_get_period(led_dat->pwm); + + led_dat->cdev.default_trigger = of_get_property(child, + "linux,default-trigger", NULL); + of_property_read_u32(child, "max-brightness", + &led_dat->cdev.max_brightness); - led_dat->cdev.name = cur_led->name; - led_dat->cdev.default_trigger = cur_led->default_trigger; - led_dat->active_low = cur_led->active_low; - led_dat->period = cur_led->pwm_period_ns; led_dat->cdev.brightness_set = led_pwm_set; led_dat->cdev.brightness = LED_OFF; - led_dat->cdev.max_brightness = cur_led->max_brightness; led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; ret = led_classdev_register(&pdev->dev, &led_dat->cdev); - if (ret < 0) + if (ret < 0) { + dev_err(&pdev->dev, "failed to register for %s\n", + led_dat->cdev.name); + of_node_put(child); goto err; + } + priv->num_leds++; + } + + return priv; +err: + while (priv->num_leds--) + led_classdev_unregister(&priv->leds[priv->num_leds].cdev); + + return NULL; +} + +static int led_pwm_probe(struct platform_device *pdev) +{ + struct led_pwm_platform_data *pdata = pdev->dev.platform_data; + struct led_pwm_priv *priv; + int i, ret = 0; + + if (pdata && pdata->num_leds) { + priv = devm_kzalloc(&pdev->dev, + sizeof_pwm_leds_priv(pdata->num_leds), + GFP_KERNEL); + if (!priv) + return -ENOMEM; + + for (i = 0; i < pdata->num_leds; i++) { + struct led_pwm *cur_led = &pdata->leds[i]; + struct led_pwm_data *led_dat = &priv->leds[i]; + + led_dat->pwm = devm_pwm_get(&pdev->dev, cur_led->name); + if (IS_ERR(led_dat->pwm)) { + ret = PTR_ERR(led_dat->pwm); + dev_err(&pdev->dev, + "unable to request PWM for %s\n", + cur_led->name); + goto err; + } + + led_dat->cdev.name = cur_led->name; + led_dat->cdev.default_trigger = cur_led->default_trigger; + led_dat->active_low = cur_led->active_low; + led_dat->period = cur_led->pwm_period_ns; + led_dat->cdev.brightness_set = led_pwm_set; + led_dat->cdev.brightness = LED_OFF; + led_dat->cdev.max_brightness = cur_led->max_brightness; + led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; + + ret = led_classdev_register(&pdev->dev, &led_dat->cdev); + if (ret < 0) + goto err; + } + priv->num_leds = pdata->num_leds; + } else { + priv = led_pwm_create_of(pdev); + if (!priv) + return -ENODEV; } - priv->num_leds = pdata->num_leds; platform_set_drvdata(pdev, priv); @@ -121,12 +186,19 @@ static int led_pwm_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id of_pwm_leds_match[] = { + { .compatible = "pwm-leds", }, + {}, +}; +MODULE_DEVICE_TABLE(of, of_pwm_leds_match); + static struct platform_driver led_pwm_driver = { .probe = led_pwm_probe, .remove = led_pwm_remove, .driver = { .name = "leds_pwm", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(of_pwm_leds_match), }, }; -- cgit v1.2.3 From a1932edf8baaf2b09ad72b119c1f9749ad4397f2 Mon Sep 17 00:00:00 2001 From: "Kim, Milo" Date: Wed, 2 Jan 2013 22:28:12 -0800 Subject: leds-lp8788: fix a parent device in _probe() The lp8788-keyled is a platform driver of lp8788-mfd. The platform device is allocated when mfd_add_devices() is called in lp8788-mfd. On the other hand, 'lp->dev' is the i2c client device. Therefore, this 'platform_device' is a proper parent device in case of resource managed mem alloc, registering led device and device kernel messages. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp8788.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/leds/leds-lp8788.c b/drivers/leds/leds-lp8788.c index 4353942c5fd..7c2cb384e7a 100644 --- a/drivers/leds/leds-lp8788.c +++ b/drivers/leds/leds-lp8788.c @@ -130,9 +130,10 @@ static int lp8788_led_probe(struct platform_device *pdev) struct lp8788 *lp = dev_get_drvdata(pdev->dev.parent); struct lp8788_led_platform_data *led_pdata; struct lp8788_led *led; + struct device *dev = &pdev->dev; int ret; - led = devm_kzalloc(lp->dev, sizeof(struct lp8788_led), GFP_KERNEL); + led = devm_kzalloc(dev, sizeof(struct lp8788_led), GFP_KERNEL); if (!led) return -ENOMEM; @@ -154,13 +155,13 @@ static int lp8788_led_probe(struct platform_device *pdev) ret = lp8788_led_init_device(led, led_pdata); if (ret) { - dev_err(lp->dev, "led init device err: %d\n", ret); + dev_err(dev, "led init device err: %d\n", ret); return ret; } - ret = led_classdev_register(lp->dev, &led->led_dev); + ret = led_classdev_register(dev, &led->led_dev); if (ret) { - dev_err(lp->dev, "led register err: %d\n", ret); + dev_err(dev, "led register err: %d\n", ret); return ret; } -- cgit v1.2.3 From 5e3b7c6b1cb1a579a65fa77c7f785d94013025be Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 19 Dec 2012 16:59:43 -0800 Subject: leds: lm3530: Ensure drvdata->enable has correct status if regulator_disable fails Add lm3530_led_enable() and lm3530_led_disable() helper functions. This ensures setting drvdata->enable to correct status if regulator_disable fails. Signed-off-by: Axel Lin Acked-by: Shreshtha Kumar SAHU Acked-by: Milo Kim Tested-by: Milo Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lm3530.c | 58 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 40 insertions(+), 18 deletions(-) diff --git a/drivers/leds/leds-lm3530.c b/drivers/leds/leds-lm3530.c index 21414548383..a036a19040f 100644 --- a/drivers/leds/leds-lm3530.c +++ b/drivers/leds/leds-lm3530.c @@ -187,6 +187,40 @@ static void lm3530_als_configure(struct lm3530_platform_data *pdata, (pdata->als2_resistor_sel << LM3530_ALS2_IMP_SHIFT); } +static int lm3530_led_enable(struct lm3530_data *drvdata) +{ + int ret; + + if (drvdata->enable) + return 0; + + ret = regulator_enable(drvdata->regulator); + if (ret) { + dev_err(drvdata->led_dev.dev, "Failed to enable vin:%d\n", ret); + return ret; + } + + drvdata->enable = true; + return 0; +} + +static void lm3530_led_disable(struct lm3530_data *drvdata) +{ + int ret; + + if (!drvdata->enable) + return; + + ret = regulator_disable(drvdata->regulator); + if (ret) { + dev_err(drvdata->led_dev.dev, "Failed to disable vin:%d\n", + ret); + return; + } + + drvdata->enable = false; +} + static int lm3530_init_registers(struct lm3530_data *drvdata) { int ret = 0; @@ -245,15 +279,9 @@ static int lm3530_init_registers(struct lm3530_data *drvdata) reg_val[12] = LM3530_DEF_ZT_3; /* LM3530_ALS_Z3T_REG */ reg_val[13] = LM3530_DEF_ZT_4; /* LM3530_ALS_Z4T_REG */ - if (!drvdata->enable) { - ret = regulator_enable(drvdata->regulator); - if (ret) { - dev_err(&drvdata->client->dev, - "Enable regulator failed\n"); - return ret; - } - drvdata->enable = true; - } + ret = lm3530_led_enable(drvdata); + if (ret) + return ret; for (i = 0; i < LM3530_REG_MAX; i++) { /* do not update brightness register when pwm mode */ @@ -305,13 +333,8 @@ static void lm3530_brightness_set(struct led_classdev *led_cdev, else drvdata->brightness = brt_val; - if (brt_val == 0) { - err = regulator_disable(drvdata->regulator); - if (err) - dev_err(&drvdata->client->dev, - "Disable regulator failed\n"); - drvdata->enable = false; - } + if (brt_val == 0) + lm3530_led_disable(drvdata); break; case LM3530_BL_MODE_ALS: break; @@ -458,8 +481,7 @@ static int lm3530_remove(struct i2c_client *client) device_remove_file(drvdata->led_dev.dev, &dev_attr_mode); - if (drvdata->enable) - regulator_disable(drvdata->regulator); + lm3530_led_disable(drvdata); led_classdev_unregister(&drvdata->led_dev); return 0; } -- cgit v1.2.3 From 4d7983324507ff23ddf0b6e513864d4eca7a62f1 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 10 Jan 2013 20:03:38 -0800 Subject: leds: renesas-tpu: Improve the readability to pick the lowest acceptable rate I spent a few minutes to understand why the code catching the mismatch case by checking if k is 0 or not. And the code using "k - 1" as array index is unusual. This patch checks acceptable rate from the lowest rate, and then we don't need to subtract k by 1. This change improves the readability. Signed-off-by: Axel Lin Signed-off-by: Bryan Wu --- drivers/leds/leds-renesas-tpu.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/leds/leds-renesas-tpu.c b/drivers/leds/leds-renesas-tpu.c index e0fff1ca592..d3c2b7e68fb 100644 --- a/drivers/leds/leds-renesas-tpu.c +++ b/drivers/leds/leds-renesas-tpu.c @@ -133,24 +133,24 @@ static int r_tpu_enable(struct r_tpu_priv *p, enum led_brightness brightness) rate = clk_get_rate(p->clk); /* pick the lowest acceptable rate */ - for (k = 0; k < ARRAY_SIZE(prescaler); k++) - if ((rate / prescaler[k]) < p->min_rate) + for (k = ARRAY_SIZE(prescaler) - 1; k >= 0; k--) + if ((rate / prescaler[k]) >= p->min_rate) break; - if (!k) { + if (k < 0) { dev_err(&p->pdev->dev, "clock rate mismatch\n"); goto err0; } dev_dbg(&p->pdev->dev, "rate = %lu, prescaler %u\n", - rate, prescaler[k - 1]); + rate, prescaler[k]); /* clear TCNT on TGRB match, count on rising edge, set prescaler */ - r_tpu_write(p, TCR, 0x0040 | (k - 1)); + r_tpu_write(p, TCR, 0x0040 | k); /* output 0 until TGRA, output 1 until TGRB */ r_tpu_write(p, TIOR, 0x0002); - rate /= prescaler[k - 1] * p->refresh_rate; + rate /= prescaler[k] * p->refresh_rate; r_tpu_write(p, TGRB, rate); dev_dbg(&p->pdev->dev, "TRGB = 0x%04lx\n", rate); -- cgit v1.2.3 From 0c0d9067ac6ff1326f24ad6e711fe88893785ca9 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 21 Jan 2013 21:56:27 -0800 Subject: leds: lm355x: rename devAttr to avoid CamelCase Fixed the checkpatch warning as below: WARNING: Avoid CamelCase: Rename devAttr to attr. Signed-off-by: Jingoo Han Signed-off-by: Bryan Wu --- drivers/leds/leds-lm355x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/leds/leds-lm355x.c b/drivers/leds/leds-lm355x.c index 65d79284c48..4117235ba61 100644 --- a/drivers/leds/leds-lm355x.c +++ b/drivers/leds/leds-lm355x.c @@ -380,7 +380,7 @@ static void lm355x_indicator_brightness_set(struct led_classdev *cdev, /* indicator pattern only for lm3556*/ static ssize_t lm3556_indicator_pattern_store(struct device *dev, - struct device_attribute *devAttr, + struct device_attribute *attr, const char *buf, size_t size) { ssize_t ret; -- cgit v1.2.3 From 85b4b756f1dda11556ead50ea884b8f1040f8c66 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 21 Jan 2013 21:57:20 -0800 Subject: leds: lm3642: rename devAttr to avoid CamelCase Fixed the checkpatch warning as below: WARNING: Avoid CamelCase: Rename devAttr to attr. Signed-off-by: Jingoo Han Signed-off-by: Bryan Wu --- drivers/leds/leds-lm3642.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/leds/leds-lm3642.c b/drivers/leds/leds-lm3642.c index 07b3dde9061..9f428d9dfe9 100644 --- a/drivers/leds/leds-lm3642.c +++ b/drivers/leds/leds-lm3642.c @@ -176,7 +176,7 @@ out: /* torch pin config for lm3642*/ static ssize_t lm3642_torch_pin_store(struct device *dev, - struct device_attribute *devAttr, + struct device_attribute *attr, const char *buf, size_t size) { ssize_t ret; @@ -233,7 +233,7 @@ static void lm3642_torch_brightness_set(struct led_classdev *cdev, /* strobe pin config for lm3642*/ static ssize_t lm3642_strobe_pin_store(struct device *dev, - struct device_attribute *devAttr, + struct device_attribute *attr, const char *buf, size_t size) { ssize_t ret; -- cgit v1.2.3 From 962363cedae26c99201b015c6f0f342313b629e4 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 21 Jan 2013 21:58:07 -0800 Subject: leds: pca9532: fix suspect code indent for conditional statements Fixed the checkpatch warning as below: WARNING: suspect code indent for conditional statements (8, 8) Signed-off-by: Jingoo Han Signed-off-by: Bryan Wu --- drivers/leds/leds-pca9532.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/leds/leds-pca9532.c b/drivers/leds/leds-pca9532.c index 43d08df6e8b..0c597bdd23f 100644 --- a/drivers/leds/leds-pca9532.c +++ b/drivers/leds/leds-pca9532.c @@ -186,7 +186,7 @@ static int pca9532_set_blink(struct led_classdev *led_cdev, int err = 0; if (*delay_on == 0 && *delay_off == 0) { - /* led subsystem ask us for a blink rate */ + /* led subsystem ask us for a blink rate */ *delay_on = 1000; *delay_off = 1000; } -- cgit v1.2.3 From d7789430de0865a55496213d6af2a870069c4dac Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 21 Jan 2013 21:58:51 -0800 Subject: leds: leds-ss4200: use DEFINE_PCI_DEVICE_TABLE This macro is used to create a struct pci_device_id array. Signed-off-by: Jingoo Han Signed-off-by: Bryan Wu --- drivers/leds/leds-ss4200.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/leds/leds-ss4200.c b/drivers/leds/leds-ss4200.c index ec9b287ecfb..64e204e714f 100644 --- a/drivers/leds/leds-ss4200.c +++ b/drivers/leds/leds-ss4200.c @@ -63,8 +63,7 @@ MODULE_LICENSE("GPL"); /* * PCI ID of the Intel ICH7 LPC Device within which the GPIO block lives. */ -static const struct pci_device_id ich7_lpc_pci_id[] = -{ +static DEFINE_PCI_DEVICE_TABLE(ich7_lpc_pci_id) = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_0) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_1) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_30) }, -- cgit v1.2.3 From 8a4529a38d38f53657769b942f6fc5d34f2c64b5 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 21 Jan 2013 21:59:29 -0800 Subject: leds: wm831x: add missing const Fixed the checkpatch warning as below: WARNING: static const char * array should probably be static const char * const Signed-off-by: Jingoo Han Signed-off-by: Bryan Wu --- drivers/leds/leds-wm831x-status.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/leds/leds-wm831x-status.c b/drivers/leds/leds-wm831x-status.c index 74a24cf897c..6bd5c679d87 100644 --- a/drivers/leds/leds-wm831x-status.c +++ b/drivers/leds/leds-wm831x-status.c @@ -157,7 +157,7 @@ static int wm831x_status_blink_set(struct led_classdev *led_cdev, return ret; } -static const char *led_src_texts[] = { +static const char * const led_src_texts[] = { "otp", "power", "charger", -- cgit v1.2.3 From 944f7b1dedb859f76a88c8d34ce23a90bf6285a0 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 17:49:46 +0900 Subject: leds-lp55xx: clean up init_device() in lp5521/5523 To make _probe() simple, device initialization code is moved to _init_device() at each driver. This patch is a preceding step for lp55xx common driver architecture. leds-lp5521: When 'lp5521_init_device()' gets failed, error handling should be 'fail1' rather than 'fail2'. fail1: releasing platform resource and return code fail2: releasing allocated LED devices with handling 'fail1' The 'lp5521_init_device()' is called before creating LED devices. Thus, 'goto fail1' is proper error handler of this function. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 102 +++++++++++++++++++++++++-------------------- drivers/leds/leds-lp5523.c | 47 ++++++++++++--------- 2 files changed, 84 insertions(+), 65 deletions(-) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index cb8a5220200..24f439f78bd 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -687,6 +687,59 @@ static void lp5521_unregister_sysfs(struct i2c_client *client) &lp5521_led_attribute_group); } +static int lp5521_init_device(struct lp5521_chip *chip) +{ + struct lp5521_platform_data *pdata = chip->pdata; + struct i2c_client *client = chip->client; + int ret; + u8 buf; + + if (pdata->setup_resources) { + ret = pdata->setup_resources(); + if (ret < 0) + return ret; + } + + if (pdata->enable) { + pdata->enable(0); + usleep_range(1000, 2000); /* Keep enable down at least 1ms */ + pdata->enable(1); + usleep_range(1000, 2000); /* 500us abs min. */ + } + + lp5521_write(client, LP5521_REG_RESET, 0xff); + usleep_range(10000, 20000); /* + * Exact value is not available. 10 - 20ms + * appears to be enough for reset. + */ + + /* + * Make sure that the chip is reset by reading back the r channel + * current reg. This is dummy read is required on some platforms - + * otherwise further access to the R G B channels in the + * LP5521_REG_ENABLE register will not have any effect - strange! + */ + ret = lp5521_read(client, LP5521_REG_R_CURRENT, &buf); + if (ret) { + dev_err(&client->dev, "error in resetting chip\n"); + return ret; + } + if (buf != LP5521_REG_R_CURR_DEFAULT) { + dev_err(&client->dev, + "unexpected data in register (expected 0x%x got 0x%x)\n", + LP5521_REG_R_CURR_DEFAULT, buf); + ret = -EINVAL; + return ret; + } + usleep_range(10000, 20000); + + ret = lp5521_detect(client); + if (ret) + dev_err(&client->dev, "Chip not found\n"); + + return ret; +} + static int lp5521_init_led(struct lp5521_led *led, struct i2c_client *client, int chan, struct lp5521_platform_data *pdata) @@ -742,7 +795,6 @@ static int lp5521_probe(struct i2c_client *client, struct lp5521_chip *chip; struct lp5521_platform_data *pdata; int ret, i, led; - u8 buf; chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); if (!chip) @@ -762,51 +814,9 @@ static int lp5521_probe(struct i2c_client *client, chip->pdata = pdata; - if (pdata->setup_resources) { - ret = pdata->setup_resources(); - if (ret < 0) - return ret; - } - - if (pdata->enable) { - pdata->enable(0); - usleep_range(1000, 2000); /* Keep enable down at least 1ms */ - pdata->enable(1); - usleep_range(1000, 2000); /* 500us abs min. */ - } - - lp5521_write(client, LP5521_REG_RESET, 0xff); - usleep_range(10000, 20000); /* - * Exact value is not available. 10 - 20ms - * appears to be enough for reset. - */ - - /* - * Make sure that the chip is reset by reading back the r channel - * current reg. This is dummy read is required on some platforms - - * otherwise further access to the R G B channels in the - * LP5521_REG_ENABLE register will not have any effect - strange! - */ - ret = lp5521_read(client, LP5521_REG_R_CURRENT, &buf); - if (ret) { - dev_err(&client->dev, "error in resetting chip\n"); - goto fail2; - } - if (buf != LP5521_REG_R_CURR_DEFAULT) { - dev_err(&client->dev, - "unexpected data in register (expected 0x%x got 0x%x)\n", - LP5521_REG_R_CURR_DEFAULT, buf); - ret = -EINVAL; - goto fail2; - } - usleep_range(10000, 20000); - - ret = lp5521_detect(client); - - if (ret) { - dev_err(&client->dev, "Chip not found\n"); - goto fail2; - } + ret = lp5521_init_device(chip); + if (ret) + goto fail1; dev_info(&client->dev, "%s programmable led chip found\n", id->name); diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 7f5be8948cd..491ea725bb0 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -896,6 +896,33 @@ static int lp5523_init_led(struct lp5523_led *led, struct device *dev, return 0; } +static int lp5523_init_device(struct lp5523_chip *chip) +{ + struct lp5523_platform_data *pdata = chip->pdata; + struct i2c_client *client = chip->client; + int ret; + + if (pdata->setup_resources) { + ret = pdata->setup_resources(); + if (ret < 0) + return ret; + } + + if (pdata->enable) { + pdata->enable(0); + usleep_range(1000, 2000); /* Keep enable down at least 1ms */ + pdata->enable(1); + usleep_range(1000, 2000); /* 500us abs min. */ + } + + lp5523_write(client, LP5523_REG_RESET, 0xff); + usleep_range(10000, 20000); /* + * Exact value is not available. 10 - 20ms + * appears to be enough for reset. + */ + return lp5523_detect(client); +} + static int lp5523_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -921,25 +948,7 @@ static int lp5523_probe(struct i2c_client *client, chip->pdata = pdata; - if (pdata->setup_resources) { - ret = pdata->setup_resources(); - if (ret < 0) - return ret; - } - - if (pdata->enable) { - pdata->enable(0); - usleep_range(1000, 2000); /* Keep enable down at least 1ms */ - pdata->enable(1); - usleep_range(1000, 2000); /* 500us abs min. */ - } - - lp5523_write(client, LP5523_REG_RESET, 0xff); - usleep_range(10000, 20000); /* - * Exact value is not available. 10 - 20ms - * appears to be enough for reset. - */ - ret = lp5523_detect(client); + ret = lp5523_init_device(chip); if (ret) goto fail1; -- cgit v1.2.3 From 1a9914855d2868112257dbc5771ddc6ebc9b2cab Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 17:50:36 +0900 Subject: leds-lp55xx: clean up deinit_device() in lp5521/5523 Device de-initialization code is moved to _deinit_device() at each driver. This patch is a preceding step for lp55xx common driver architecture. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 20 ++++++++++++-------- drivers/leds/leds-lp5523.c | 20 ++++++++++++-------- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index 24f439f78bd..fd963fe0a94 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -740,6 +740,16 @@ static int lp5521_init_device(struct lp5521_chip *chip) return ret; } +static void lp5521_deinit_device(struct lp5521_chip *chip) +{ + struct lp5521_platform_data *pdata = chip->pdata; + + if (pdata->enable) + pdata->enable(0); + if (pdata->release_resources) + pdata->release_resources(); +} + static int lp5521_init_led(struct lp5521_led *led, struct i2c_client *client, int chan, struct lp5521_platform_data *pdata) @@ -865,10 +875,7 @@ fail2: cancel_work_sync(&chip->leds[i].brightness_work); } fail1: - if (pdata->enable) - pdata->enable(0); - if (pdata->release_resources) - pdata->release_resources(); + lp5521_deinit_device(chip); return ret; } @@ -885,10 +892,7 @@ static int lp5521_remove(struct i2c_client *client) cancel_work_sync(&chip->leds[i].brightness_work); } - if (chip->pdata->enable) - chip->pdata->enable(0); - if (chip->pdata->release_resources) - chip->pdata->release_resources(); + lp5521_deinit_device(chip); return 0; } diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 491ea725bb0..ddb482aebe1 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -923,6 +923,16 @@ static int lp5523_init_device(struct lp5523_chip *chip) return lp5523_detect(client); } +static void lp5523_deinit_device(struct lp5523_chip *chip) +{ + struct lp5523_platform_data *pdata = chip->pdata; + + if (pdata->enable) + pdata->enable(0); + if (pdata->release_resources) + pdata->release_resources(); +} + static int lp5523_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -1009,10 +1019,7 @@ fail2: flush_work(&chip->leds[i].brightness_work); } fail1: - if (pdata->enable) - pdata->enable(0); - if (pdata->release_resources) - pdata->release_resources(); + lp5523_deinit_device(chip); return ret; } @@ -1031,10 +1038,7 @@ static int lp5523_remove(struct i2c_client *client) flush_work(&chip->leds[i].brightness_work); } - if (chip->pdata->enable) - chip->pdata->enable(0); - if (chip->pdata->release_resources) - chip->pdata->release_resources(); + lp5523_deinit_device(chip); return 0; } -- cgit v1.2.3 From f652480802b636f86e194f9680347676b655d856 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 17:53:40 +0900 Subject: leds-lp55xx: clean up init leds in lp5521/5523 To make LED initialization code simple, new function, _register_leds() is added at each driver. This patch is a preceding step for lp55xx common driver architecture. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 69 ++++++++++++++++++++++++++----------------- drivers/leds/leds-lp5523.c | 73 ++++++++++++++++++++++++++++------------------ 2 files changed, 86 insertions(+), 56 deletions(-) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index fd963fe0a94..f4cd0fe67fe 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -799,12 +799,50 @@ static int lp5521_init_led(struct lp5521_led *led, return 0; } +static int lp5521_register_leds(struct lp5521_chip *chip) +{ + struct lp5521_platform_data *pdata = chip->pdata; + struct i2c_client *client = chip->client; + int i; + int led; + int ret; + + /* Initialize leds */ + chip->num_channels = pdata->num_channels; + chip->num_leds = 0; + led = 0; + for (i = 0; i < pdata->num_channels; i++) { + /* Do not initialize channels that are not connected */ + if (pdata->led_config[i].led_current == 0) + continue; + + ret = lp5521_init_led(&chip->leds[led], client, i, pdata); + if (ret) { + dev_err(&client->dev, "error initializing leds\n"); + return ret; + } + chip->num_leds++; + + chip->leds[led].id = led; + /* Set initial LED current */ + lp5521_set_led_current(chip, led, + chip->leds[led].led_current); + + INIT_WORK(&(chip->leds[led].brightness_work), + lp5521_led_brightness_work); + + led++; + } + + return 0; +} + static int lp5521_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct lp5521_chip *chip; struct lp5521_platform_data *pdata; - int ret, i, led; + int ret, i; chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); if (!chip) @@ -836,32 +874,9 @@ static int lp5521_probe(struct i2c_client *client, goto fail1; } - /* Initialize leds */ - chip->num_channels = pdata->num_channels; - chip->num_leds = 0; - led = 0; - for (i = 0; i < pdata->num_channels; i++) { - /* Do not initialize channels that are not connected */ - if (pdata->led_config[i].led_current == 0) - continue; - - ret = lp5521_init_led(&chip->leds[led], client, i, pdata); - if (ret) { - dev_err(&client->dev, "error initializing leds\n"); - goto fail2; - } - chip->num_leds++; - - chip->leds[led].id = led; - /* Set initial LED current */ - lp5521_set_led_current(chip, led, - chip->leds[led].led_current); - - INIT_WORK(&(chip->leds[led].brightness_work), - lp5521_led_brightness_work); - - led++; - } + ret = lp5521_register_leds(chip); + if (ret) + goto fail2; ret = lp5521_register_sysfs(client); if (ret) { diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index ddb482aebe1..f5e89328981 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -896,6 +896,46 @@ static int lp5523_init_led(struct lp5523_led *led, struct device *dev, return 0; } +static int lp5523_register_leds(struct lp5523_chip *chip, const char *name) +{ + struct lp5523_platform_data *pdata = chip->pdata; + struct i2c_client *client = chip->client; + int i; + int led; + int ret; + + /* Initialize leds */ + chip->num_channels = pdata->num_channels; + chip->num_leds = 0; + led = 0; + for (i = 0; i < pdata->num_channels; i++) { + /* Do not initialize channels that are not connected */ + if (pdata->led_config[i].led_current == 0) + continue; + + INIT_WORK(&chip->leds[led].brightness_work, + lp5523_led_brightness_work); + + ret = lp5523_init_led(&chip->leds[led], &client->dev, i, pdata, + name); + if (ret) { + dev_err(&client->dev, "error initializing leds\n"); + return ret; + } + chip->num_leds++; + + chip->leds[led].id = led; + /* Set LED current */ + lp5523_write(client, + LP5523_REG_LED_CURRENT_BASE + chip->leds[led].chan_nr, + chip->leds[led].led_current); + + led++; + } + + return 0; +} + static int lp5523_init_device(struct lp5523_chip *chip) { struct lp5523_platform_data *pdata = chip->pdata; @@ -938,7 +978,7 @@ static int lp5523_probe(struct i2c_client *client, { struct lp5523_chip *chip; struct lp5523_platform_data *pdata; - int ret, i, led; + int ret, i; chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); if (!chip) @@ -978,34 +1018,9 @@ static int lp5523_probe(struct i2c_client *client, goto fail1; } - /* Initialize leds */ - chip->num_channels = pdata->num_channels; - chip->num_leds = 0; - led = 0; - for (i = 0; i < pdata->num_channels; i++) { - /* Do not initialize channels that are not connected */ - if (pdata->led_config[i].led_current == 0) - continue; - - INIT_WORK(&chip->leds[led].brightness_work, - lp5523_led_brightness_work); - - ret = lp5523_init_led(&chip->leds[led], &client->dev, i, pdata, - id->name); - if (ret) { - dev_err(&client->dev, "error initializing leds\n"); - goto fail2; - } - chip->num_leds++; - - chip->leds[led].id = led; - /* Set LED current */ - lp5523_write(client, - LP5523_REG_LED_CURRENT_BASE + chip->leds[led].chan_nr, - chip->leds[led].led_current); - - led++; - } + ret = lp5523_register_leds(chip, id->name); + if (ret) + goto fail2; ret = lp5523_register_sysfs(client); if (ret) { -- cgit v1.2.3 From 1904f83d568dba794be9de1311bafb5a4424812a Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 17:56:23 +0900 Subject: leds-lp55xx: clean up deinit leds in lp5521/5523 To make LED unregistration code simple, new function, _unregister_leds() is added in each driver. This patch is a preceding step for lp55xx common driver architecture. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 23 +++++++++++++---------- drivers/leds/leds-lp5523.c | 21 ++++++++++++--------- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index f4cd0fe67fe..ec1ffe6316c 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -837,12 +837,22 @@ static int lp5521_register_leds(struct lp5521_chip *chip) return 0; } +static void lp5521_unregister_leds(struct lp5521_chip *chip) +{ + int i; + + for (i = 0; i < chip->num_leds; i++) { + led_classdev_unregister(&chip->leds[i].cdev); + cancel_work_sync(&chip->leds[i].brightness_work); + } +} + static int lp5521_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct lp5521_chip *chip; struct lp5521_platform_data *pdata; - int ret, i; + int ret; chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); if (!chip) @@ -885,10 +895,7 @@ static int lp5521_probe(struct i2c_client *client, } return ret; fail2: - for (i = 0; i < chip->num_leds; i++) { - led_classdev_unregister(&chip->leds[i].cdev); - cancel_work_sync(&chip->leds[i].brightness_work); - } + lp5521_unregister_leds(chip); fail1: lp5521_deinit_device(chip); return ret; @@ -897,15 +904,11 @@ fail1: static int lp5521_remove(struct i2c_client *client) { struct lp5521_chip *chip = i2c_get_clientdata(client); - int i; lp5521_run_led_pattern(PATTERN_OFF, chip); lp5521_unregister_sysfs(client); - for (i = 0; i < chip->num_leds; i++) { - led_classdev_unregister(&chip->leds[i].cdev); - cancel_work_sync(&chip->leds[i].brightness_work); - } + lp5521_unregister_leds(chip); lp5521_deinit_device(chip); return 0; diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index f5e89328981..2fc19bbddb7 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -936,6 +936,16 @@ static int lp5523_register_leds(struct lp5523_chip *chip, const char *name) return 0; } +static void lp5523_unregister_leds(struct lp5523_chip *chip) +{ + int i; + + for (i = 0; i < chip->num_leds; i++) { + led_classdev_unregister(&chip->leds[i].cdev); + flush_work(&chip->leds[i].brightness_work); + } +} + static int lp5523_init_device(struct lp5523_chip *chip) { struct lp5523_platform_data *pdata = chip->pdata; @@ -1029,10 +1039,7 @@ static int lp5523_probe(struct i2c_client *client, } return ret; fail2: - for (i = 0; i < chip->num_leds; i++) { - led_classdev_unregister(&chip->leds[i].cdev); - flush_work(&chip->leds[i].brightness_work); - } + lp5523_unregister_leds(chip); fail1: lp5523_deinit_device(chip); return ret; @@ -1041,17 +1048,13 @@ fail1: static int lp5523_remove(struct i2c_client *client) { struct lp5523_chip *chip = i2c_get_clientdata(client); - int i; /* Disable engine mode */ lp5523_write(client, LP5523_REG_OP_MODE, LP5523_CMD_DISABLED); lp5523_unregister_sysfs(client); - for (i = 0; i < chip->num_leds; i++) { - led_classdev_unregister(&chip->leds[i].cdev); - flush_work(&chip->leds[i].brightness_work); - } + lp5523_unregister_leds(chip); lp5523_deinit_device(chip); return 0; -- cgit v1.2.3 From 86eb7748cef00faa3eaefc8fc450ed30281a09e7 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 17:57:02 +0900 Subject: leds-lp55xx: add device reset function in lp5521/5523 Use explicit each driver function rather than raw command. These function will be merged into the lp55xx common driver. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 10 +++++++++- drivers/leds/leds-lp5523.c | 10 +++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index ec1ffe6316c..ec89ed64100 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -687,6 +687,13 @@ static void lp5521_unregister_sysfs(struct i2c_client *client) &lp5521_led_attribute_group); } +static void lp5521_reset_device(struct lp5521_chip *chip) +{ + struct i2c_client *client = chip->client; + + lp5521_write(client, LP5521_REG_RESET, 0xff); +} + static int lp5521_init_device(struct lp5521_chip *chip) { struct lp5521_platform_data *pdata = chip->pdata; @@ -707,7 +714,8 @@ static int lp5521_init_device(struct lp5521_chip *chip) usleep_range(1000, 2000); /* 500us abs min. */ } - lp5521_write(client, LP5521_REG_RESET, 0xff); + lp5521_reset_device(chip); + usleep_range(10000, 20000); /* * Exact value is not available. 10 - 20ms * appears to be enough for reset. diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 2fc19bbddb7..cac492b0abf 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -946,6 +946,13 @@ static void lp5523_unregister_leds(struct lp5523_chip *chip) } } +static void lp5523_reset_device(struct lp5523_chip *chip) +{ + struct i2c_client *client = chip->client; + + lp5523_write(client, LP5523_REG_RESET, 0xff); +} + static int lp5523_init_device(struct lp5523_chip *chip) { struct lp5523_platform_data *pdata = chip->pdata; @@ -965,7 +972,8 @@ static int lp5523_init_device(struct lp5523_chip *chip) usleep_range(1000, 2000); /* 500us abs min. */ } - lp5523_write(client, LP5523_REG_RESET, 0xff); + lp5523_reset_device(chip); + usleep_range(10000, 20000); /* * Exact value is not available. 10 - 20ms * appears to be enough for reset. -- cgit v1.2.3 From f6c64c6fc8d793b414446f1a655a37b7bfce68e3 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 17:58:01 +0900 Subject: leds-lp55xx: do chip specific configuration on device init Chip specific function is configured when the device is initialized. So _configure() is moved to each device init function. If chip configuration gets failed, the device is de-initialized in each _init_device(), not probe(). For compile error fix, function type declarations are added. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 26 +++++++++++++++++--------- drivers/leds/leds-lp5523.c | 26 +++++++++++++++++++------- 2 files changed, 36 insertions(+), 16 deletions(-) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index ec89ed64100..e042a094a07 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -694,6 +694,7 @@ static void lp5521_reset_device(struct lp5521_chip *chip) lp5521_write(client, LP5521_REG_RESET, 0xff); } +static void lp5521_deinit_device(struct lp5521_chip *chip); static int lp5521_init_device(struct lp5521_chip *chip) { struct lp5521_platform_data *pdata = chip->pdata; @@ -742,9 +743,22 @@ static int lp5521_init_device(struct lp5521_chip *chip) usleep_range(10000, 20000); ret = lp5521_detect(client); - if (ret) + if (ret) { dev_err(&client->dev, "Chip not found\n"); + goto err; + } + + ret = lp5521_configure(client); + if (ret < 0) { + dev_err(&client->dev, "error configuring chip\n"); + goto err_config; + } + return 0; + +err_config: + lp5521_deinit_device(chip); +err: return ret; } @@ -882,16 +896,10 @@ static int lp5521_probe(struct i2c_client *client, ret = lp5521_init_device(chip); if (ret) - goto fail1; + goto err_init; dev_info(&client->dev, "%s programmable led chip found\n", id->name); - ret = lp5521_configure(client); - if (ret < 0) { - dev_err(&client->dev, "error configuring chip\n"); - goto fail1; - } - ret = lp5521_register_leds(chip); if (ret) goto fail2; @@ -904,8 +912,8 @@ static int lp5521_probe(struct i2c_client *client, return ret; fail2: lp5521_unregister_leds(chip); -fail1: lp5521_deinit_device(chip); +err_init: return ret; } diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index cac492b0abf..fefe27c3f37 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -953,6 +953,7 @@ static void lp5523_reset_device(struct lp5523_chip *chip) lp5523_write(client, LP5523_REG_RESET, 0xff); } +static void lp5523_deinit_device(struct lp5523_chip *chip); static int lp5523_init_device(struct lp5523_chip *chip) { struct lp5523_platform_data *pdata = chip->pdata; @@ -978,7 +979,22 @@ static int lp5523_init_device(struct lp5523_chip *chip) * Exact value is not available. 10 - 20ms * appears to be enough for reset. */ - return lp5523_detect(client); + ret = lp5523_detect(client); + if (ret) + goto err; + + ret = lp5523_configure(client); + if (ret < 0) { + dev_err(&client->dev, "error configuring chip\n"); + goto err_config; + } + + return 0; + +err_config: + lp5523_deinit_device(chip); +err: + return ret; } static void lp5523_deinit_device(struct lp5523_chip *chip) @@ -1018,7 +1034,7 @@ static int lp5523_probe(struct i2c_client *client, ret = lp5523_init_device(chip); if (ret) - goto fail1; + goto err_init; dev_info(&client->dev, "%s Programmable led chip found\n", id->name); @@ -1030,11 +1046,6 @@ static int lp5523_probe(struct i2c_client *client, goto fail1; } } - ret = lp5523_configure(client); - if (ret < 0) { - dev_err(&client->dev, "error configuring chip\n"); - goto fail1; - } ret = lp5523_register_leds(chip, id->name); if (ret) @@ -1050,6 +1061,7 @@ fail2: lp5523_unregister_leds(chip); fail1: lp5523_deinit_device(chip); +err_init: return ret; } -- cgit v1.2.3 From c93d08fa75020835741c7b1d0523ff854e8acde1 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 18:01:23 +0900 Subject: leds-lp55xx: add new common driver for lp5521/5523 This patch supports basic common driver code for LP5521, LP5523/55231 devices. ( Driver Structure Data ) lp55xx_led and lp55xx_chip In lp55xx common driver, two different data structure is used. o lp55xx_led control multi output LED channels such as led current, channel index. o lp55xx_chip general chip control such like the I2C and platform data. For example, LP5521 has maximum 3 LED channels. LP5523/55231 has 9 output channels. lp55xx_chip for LP5521 ... lp55xx_led #1 lp55xx_led #2 lp55xx_led #3 lp55xx_chip for LP5523 ... lp55xx_led #1 lp55xx_led #2 . . lp55xx_led #9 ( Platform Data ) LP5521 and LP5523/55231 have own specific platform data. However, this data can be handled with just one platform data structure. The lp55xx platform data is declared in the header. This structure is derived from leds-lp5521.h and leds-lp5523.h Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/Kconfig | 9 ++++ drivers/leds/Makefile | 1 + drivers/leds/leds-lp55xx-common.c | 59 +++++++++++++++++++++ drivers/leds/leds-lp55xx-common.h | 61 ++++++++++++++++++++++ include/linux/platform_data/leds-lp55xx.h | 87 +++++++++++++++++++++++++++++++ 5 files changed, 217 insertions(+) create mode 100644 drivers/leds/leds-lp55xx-common.c create mode 100644 drivers/leds/leds-lp55xx-common.h create mode 100644 include/linux/platform_data/leds-lp55xx.h diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index b58bc8a14b9..3d7822b3498 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -193,9 +193,17 @@ config LEDS_LP3944 To compile this driver as a module, choose M here: the module will be called leds-lp3944. +config LEDS_LP55XX_COMMON + tristate "Common Driver for TI/National LP5521 and LP5523/55231" + depends on LEDS_LP5521 || LEDS_LP5523 + help + This option supports common operations for LP5521 and LP5523/55231 + devices. + config LEDS_LP5521 tristate "LED Support for N.S. LP5521 LED driver chip" depends on LEDS_CLASS && I2C + select LEDS_LP55XX_COMMON help If you say yes here you get support for the National Semiconductor LP5521 LED driver. It is 3 channel chip with programmable engines. @@ -205,6 +213,7 @@ config LEDS_LP5521 config LEDS_LP5523 tristate "LED Support for TI/National LP5523/55231 LED driver chip" depends on LEDS_CLASS && I2C + select LEDS_LP55XX_COMMON help If you say yes here you get support for TI/National Semiconductor LP5523/55231 LED driver. diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 3fb9641b619..215e7e3b617 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_LEDS_PCA9532) += leds-pca9532.o obj-$(CONFIG_LEDS_GPIO_REGISTER) += leds-gpio-register.o obj-$(CONFIG_LEDS_GPIO) += leds-gpio.o obj-$(CONFIG_LEDS_LP3944) += leds-lp3944.o +obj-$(CONFIG_LEDS_LP55XX_COMMON) += leds-lp55xx-common.o obj-$(CONFIG_LEDS_LP5521) += leds-lp5521.o obj-$(CONFIG_LEDS_LP5523) += leds-lp5523.o obj-$(CONFIG_LEDS_LP8788) += leds-lp8788.o diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c new file mode 100644 index 00000000000..1c716ecfa81 --- /dev/null +++ b/drivers/leds/leds-lp55xx-common.c @@ -0,0 +1,59 @@ +/* + * LP5521/LP5523/LP55231 Common Driver + * + * Copyright 2012 Texas Instruments + * + * Author: Milo(Woogyom) Kim + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Derived from leds-lp5521.c, leds-lp5523.c + */ + +#include +#include +#include +#include + +#include "leds-lp55xx-common.h" + +int lp55xx_write(struct lp55xx_chip *chip, u8 reg, u8 val) +{ + return i2c_smbus_write_byte_data(chip->cl, reg, val); +} +EXPORT_SYMBOL_GPL(lp55xx_write); + +int lp55xx_read(struct lp55xx_chip *chip, u8 reg, u8 *val) +{ + s32 ret; + + ret = i2c_smbus_read_byte_data(chip->cl, reg); + if (ret < 0) + return ret; + + *val = ret; + return 0; +} +EXPORT_SYMBOL_GPL(lp55xx_read); + +int lp55xx_update_bits(struct lp55xx_chip *chip, u8 reg, u8 mask, u8 val) +{ + int ret; + u8 tmp; + + ret = lp55xx_read(chip, reg, &tmp); + if (ret) + return ret; + + tmp &= ~mask; + tmp |= val & mask; + + return lp55xx_write(chip, reg, tmp); +} +EXPORT_SYMBOL_GPL(lp55xx_update_bits); + +MODULE_AUTHOR("Milo Kim "); +MODULE_DESCRIPTION("LP55xx Common Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/leds/leds-lp55xx-common.h b/drivers/leds/leds-lp55xx-common.h new file mode 100644 index 00000000000..369cb9c91f1 --- /dev/null +++ b/drivers/leds/leds-lp55xx-common.h @@ -0,0 +1,61 @@ +/* + * LP55XX Common Driver Header + * + * Copyright (C) 2012 Texas Instruments + * + * Author: Milo(Woogyom) Kim + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * Derived from leds-lp5521.c, leds-lp5523.c + */ + +#ifndef _LEDS_LP55XX_COMMON_H +#define _LEDS_LP55XX_COMMON_H + +struct lp55xx_led; +struct lp55xx_chip; + +/* + * struct lp55xx_chip + * @cl : I2C communication for access registers + * @pdata : Platform specific data + * @lock : Lock for user-space interface + * @num_leds : Number of registered LEDs + */ +struct lp55xx_chip { + struct i2c_client *cl; + struct lp55xx_platform_data *pdata; + struct mutex lock; /* lock for user-space interface */ + int num_leds; +}; + +/* + * struct lp55xx_led + * @chan_nr : Channel number + * @cdev : LED class device + * @led_current : Current setting at each led channel + * @max_current : Maximun current at each led channel + * @brightness_work : Workqueue for brightness control + * @brightness : Brightness value + * @chip : The lp55xx chip data + */ +struct lp55xx_led { + int chan_nr; + struct led_classdev cdev; + u8 led_current; + u8 max_current; + struct work_struct brightness_work; + u8 brightness; + struct lp55xx_chip *chip; +}; + +/* register access */ +extern int lp55xx_write(struct lp55xx_chip *chip, u8 reg, u8 val); +extern int lp55xx_read(struct lp55xx_chip *chip, u8 reg, u8 *val); +extern int lp55xx_update_bits(struct lp55xx_chip *chip, u8 reg, + u8 mask, u8 val); + +#endif /* _LEDS_LP55XX_COMMON_H */ diff --git a/include/linux/platform_data/leds-lp55xx.h b/include/linux/platform_data/leds-lp55xx.h new file mode 100644 index 00000000000..1509570d5a3 --- /dev/null +++ b/include/linux/platform_data/leds-lp55xx.h @@ -0,0 +1,87 @@ +/* + * LP55XX Platform Data Header + * + * Copyright (C) 2012 Texas Instruments + * + * Author: Milo(Woogyom) Kim + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * Derived from leds-lp5521.h, leds-lp5523.h + */ + +#ifndef _LEDS_LP55XX_H +#define _LEDS_LP55XX_H + +/* Clock configuration */ +#define LP55XX_CLOCK_AUTO 0 +#define LP55XX_CLOCK_INT 1 +#define LP55XX_CLOCK_EXT 2 + +/* Bits in LP5521 CONFIG register. 'update_config' in lp55xx_platform_data */ +#define LP5521_PWM_HF 0x40 /* PWM: 0 = 256Hz, 1 = 558Hz */ +#define LP5521_PWRSAVE_EN 0x20 /* 1 = Power save mode */ +#define LP5521_CP_MODE_OFF 0 /* Charge pump (CP) off */ +#define LP5521_CP_MODE_BYPASS 8 /* CP forced to bypass mode */ +#define LP5521_CP_MODE_1X5 0x10 /* CP forced to 1.5x mode */ +#define LP5521_CP_MODE_AUTO 0x18 /* Automatic mode selection */ +#define LP5521_R_TO_BATT 4 /* R out: 0 = CP, 1 = Vbat */ +#define LP5521_CLK_SRC_EXT 0 /* Ext-clk source (CLK_32K) */ +#define LP5521_CLK_INT 1 /* Internal clock */ +#define LP5521_CLK_AUTO 2 /* Automatic clock selection */ + +struct lp55xx_led_config { + const char *name; + u8 chan_nr; + u8 led_current; /* mA x10, 0 if led is not connected */ + u8 max_current; +}; + +struct lp55xx_predef_pattern { + u8 *r; + u8 *g; + u8 *b; + u8 size_r; + u8 size_g; + u8 size_b; +}; + +/* + * struct lp55xx_platform_data + * @led_config : Configurable led class device + * @num_channels : Number of LED channels + * @label : Used for naming LEDs + * @clock_mode : Input clock mode. LP55XX_CLOCK_AUTO or _INT or _EXT + * @setup_resources : Platform specific function before enabling the chip + * @release_resources : Platform specific function after disabling the chip + * @enable : EN pin control by platform side + * @patterns : Predefined pattern data for RGB channels + * @num_patterns : Number of patterns + * @update_config : Value of CONFIG register + */ +struct lp55xx_platform_data { + + /* LED channel configuration */ + struct lp55xx_led_config *led_config; + u8 num_channels; + const char *label; + + /* Clock configuration */ + u8 clock_mode; + + /* Platform specific functions */ + int (*setup_resources)(void); + void (*release_resources)(void); + void (*enable)(bool state); + + /* Predefined pattern data */ + struct lp55xx_predef_pattern *patterns; + unsigned int num_patterns; + + /* _CONFIG register */ + u8 update_config; +}; + +#endif /* _LEDS_LP55XX_H */ -- cgit v1.2.3 From 945c700746cbfa3375bf88123c2cf6c210f4cc2c Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 18:02:26 +0900 Subject: leds-lp55xx: replace name of data structure Change the name of chip data structure and platform data. This patch is a preceding step for cleaning up lp5521/5523 probe and remove. These data will be replaced with new lp55xx common data structures in next patch. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 36 ++++++++++++++++++------------------ drivers/leds/leds-lp5523.c | 38 +++++++++++++++++++------------------- 2 files changed, 37 insertions(+), 37 deletions(-) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index e042a094a07..8ef8f44bf86 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -872,35 +872,35 @@ static void lp5521_unregister_leds(struct lp5521_chip *chip) static int lp5521_probe(struct i2c_client *client, const struct i2c_device_id *id) { - struct lp5521_chip *chip; - struct lp5521_platform_data *pdata; + struct lp5521_chip *old_chip; + struct lp5521_platform_data *old_pdata; int ret; - chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); - if (!chip) + old_chip = devm_kzalloc(&client->dev, sizeof(*old_chip), GFP_KERNEL); + if (!old_chip) return -ENOMEM; - i2c_set_clientdata(client, chip); - chip->client = client; + i2c_set_clientdata(client, old_chip); + old_chip->client = client; - pdata = client->dev.platform_data; + old_pdata = client->dev.platform_data; - if (!pdata) { + if (!old_pdata) { dev_err(&client->dev, "no platform data\n"); return -EINVAL; } - mutex_init(&chip->lock); + mutex_init(&old_chip->lock); - chip->pdata = pdata; + old_chip->pdata = old_pdata; - ret = lp5521_init_device(chip); + ret = lp5521_init_device(old_chip); if (ret) goto err_init; dev_info(&client->dev, "%s programmable led chip found\n", id->name); - ret = lp5521_register_leds(chip); + ret = lp5521_register_leds(old_chip); if (ret) goto fail2; @@ -911,22 +911,22 @@ static int lp5521_probe(struct i2c_client *client, } return ret; fail2: - lp5521_unregister_leds(chip); - lp5521_deinit_device(chip); + lp5521_unregister_leds(old_chip); + lp5521_deinit_device(old_chip); err_init: return ret; } static int lp5521_remove(struct i2c_client *client) { - struct lp5521_chip *chip = i2c_get_clientdata(client); + struct lp5521_chip *old_chip = i2c_get_clientdata(client); - lp5521_run_led_pattern(PATTERN_OFF, chip); + lp5521_run_led_pattern(PATTERN_OFF, old_chip); lp5521_unregister_sysfs(client); - lp5521_unregister_leds(chip); + lp5521_unregister_leds(old_chip); - lp5521_deinit_device(chip); + lp5521_deinit_device(old_chip); return 0; } diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index fefe27c3f37..49b976271d9 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -1010,44 +1010,44 @@ static void lp5523_deinit_device(struct lp5523_chip *chip) static int lp5523_probe(struct i2c_client *client, const struct i2c_device_id *id) { - struct lp5523_chip *chip; - struct lp5523_platform_data *pdata; + struct lp5523_chip *old_chip; + struct lp5523_platform_data *old_pdata; int ret, i; - chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); - if (!chip) + old_chip = devm_kzalloc(&client->dev, sizeof(*old_chip), GFP_KERNEL); + if (!old_chip) return -ENOMEM; - i2c_set_clientdata(client, chip); - chip->client = client; + i2c_set_clientdata(client, old_chip); + old_chip->client = client; - pdata = client->dev.platform_data; + old_pdata = client->dev.platform_data; - if (!pdata) { + if (!old_pdata) { dev_err(&client->dev, "no platform data\n"); return -EINVAL; } - mutex_init(&chip->lock); + mutex_init(&old_chip->lock); - chip->pdata = pdata; + old_chip->pdata = old_pdata; - ret = lp5523_init_device(chip); + ret = lp5523_init_device(old_chip); if (ret) goto err_init; dev_info(&client->dev, "%s Programmable led chip found\n", id->name); /* Initialize engines */ - for (i = 0; i < ARRAY_SIZE(chip->engines); i++) { - ret = lp5523_init_engine(&chip->engines[i], i + 1); + for (i = 0; i < ARRAY_SIZE(old_chip->engines); i++) { + ret = lp5523_init_engine(&old_chip->engines[i], i + 1); if (ret) { dev_err(&client->dev, "error initializing engine\n"); goto fail1; } } - ret = lp5523_register_leds(chip, id->name); + ret = lp5523_register_leds(old_chip, id->name); if (ret) goto fail2; @@ -1058,25 +1058,25 @@ static int lp5523_probe(struct i2c_client *client, } return ret; fail2: - lp5523_unregister_leds(chip); + lp5523_unregister_leds(old_chip); fail1: - lp5523_deinit_device(chip); + lp5523_deinit_device(old_chip); err_init: return ret; } static int lp5523_remove(struct i2c_client *client) { - struct lp5523_chip *chip = i2c_get_clientdata(client); + struct lp5523_chip *old_chip = i2c_get_clientdata(client); /* Disable engine mode */ lp5523_write(client, LP5523_REG_OP_MODE, LP5523_CMD_DISABLED); lp5523_unregister_sysfs(client); - lp5523_unregister_leds(chip); + lp5523_unregister_leds(old_chip); - lp5523_deinit_device(chip); + lp5523_deinit_device(old_chip); return 0; } -- cgit v1.2.3 From 6a0c9a47963cc72c68713923ead60d1e72e7136c Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 18:03:08 +0900 Subject: leds-lp55xx: use common lp55xx data structure in _probe() LP5521 and LP5523 data structures have common features. Use common lp55xx data structures rather than chip specific data. Legacy code in probe is replaced with this new data structures. lp55xx_chip : Common data between lp5521_chip and lp5523_chip lp55xx_led : Common LED structure between lp5521_led and lp5523_led lp55xx_platform_data : Common platform data between lp5521_platform_data and lp5523_platform_data Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 36 ++++++++++++++++++++++-------------- drivers/leds/leds-lp5523.c | 36 ++++++++++++++++++++++-------------- 2 files changed, 44 insertions(+), 28 deletions(-) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index 8ef8f44bf86..1eab1557a61 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -34,6 +34,9 @@ #include #include #include +#include + +#include "leds-lp55xx-common.h" #define LP5521_PROGRAM_LENGTH 32 /* in bytes */ @@ -872,27 +875,32 @@ static void lp5521_unregister_leds(struct lp5521_chip *chip) static int lp5521_probe(struct i2c_client *client, const struct i2c_device_id *id) { - struct lp5521_chip *old_chip; - struct lp5521_platform_data *old_pdata; + struct lp5521_chip *old_chip = NULL; int ret; + struct lp55xx_chip *chip; + struct lp55xx_led *led; + struct lp55xx_platform_data *pdata = client->dev.platform_data; - old_chip = devm_kzalloc(&client->dev, sizeof(*old_chip), GFP_KERNEL); - if (!old_chip) - return -ENOMEM; - - i2c_set_clientdata(client, old_chip); - old_chip->client = client; - - old_pdata = client->dev.platform_data; - - if (!old_pdata) { + if (!pdata) { dev_err(&client->dev, "no platform data\n"); return -EINVAL; } - mutex_init(&old_chip->lock); + chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + led = devm_kzalloc(&client->dev, + sizeof(*led) * pdata->num_channels, GFP_KERNEL); + if (!led) + return -ENOMEM; + + chip->cl = client; + chip->pdata = pdata; + + mutex_init(&chip->lock); - old_chip->pdata = old_pdata; + i2c_set_clientdata(client, led); ret = lp5521_init_device(old_chip); if (ret) diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 49b976271d9..71bda3670ff 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -34,6 +34,9 @@ #include #include #include +#include + +#include "leds-lp55xx-common.h" #define LP5523_REG_ENABLE 0x00 #define LP5523_REG_OP_MODE 0x01 @@ -1010,27 +1013,32 @@ static void lp5523_deinit_device(struct lp5523_chip *chip) static int lp5523_probe(struct i2c_client *client, const struct i2c_device_id *id) { - struct lp5523_chip *old_chip; - struct lp5523_platform_data *old_pdata; + struct lp5523_chip *old_chip = NULL; int ret, i; + struct lp55xx_chip *chip; + struct lp55xx_led *led; + struct lp55xx_platform_data *pdata = client->dev.platform_data; - old_chip = devm_kzalloc(&client->dev, sizeof(*old_chip), GFP_KERNEL); - if (!old_chip) - return -ENOMEM; - - i2c_set_clientdata(client, old_chip); - old_chip->client = client; - - old_pdata = client->dev.platform_data; - - if (!old_pdata) { + if (!pdata) { dev_err(&client->dev, "no platform data\n"); return -EINVAL; } - mutex_init(&old_chip->lock); + chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + led = devm_kzalloc(&client->dev, + sizeof(*led) * pdata->num_channels, GFP_KERNEL); + if (!led) + return -ENOMEM; + + chip->cl = client; + chip->pdata = pdata; + + mutex_init(&chip->lock); - old_chip->pdata = old_pdata; + i2c_set_clientdata(client, led); ret = lp5523_init_device(old_chip); if (ret) -- cgit v1.2.3 From 9448217403462c4b17bc56690a0348a0c02e5ba2 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 18:03:55 +0900 Subject: leds-lp5521: clean up lp5521_configure() This patch is a preceding step for making common lp55xx init function. LP5521_REG_R_CURRENT register code moved: Chip specific code moved from lp5521_init_device() to lp5521_configure(). Remove engine init function: LP5521 has internal program engines which are used for running LED patterns. (blinking, ramp up/down and other emotional visual effects) Engine initialization is done by reset command in lp5521_init_device(). Remove this duplicate code. Return code: Do not use 'OR' arithmetic for the result. If some error occus, just return it. Enable latency: Use explicit named function, lp5521_wait_enable_done(). According to the datasheet, 500us is guaranteed time. Thus wait time is changed from 1000us to 500us. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 78 ++++++++++++++++++++++------------------------ 1 file changed, 38 insertions(+), 40 deletions(-) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index 1eab1557a61..341a41030fd 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -125,6 +125,12 @@ struct lp5521_chip { u8 num_leds; }; +static inline void lp5521_wait_enable_done(void) +{ + /* it takes more 488 us to update ENABLE register */ + usleep_range(500, 600); +} + static inline struct lp5521_led *cdev_to_led(struct led_classdev *cdev) { return container_of(cdev, struct lp5521_led, cdev); @@ -229,43 +235,56 @@ static int lp5521_set_led_current(struct lp5521_chip *chip, int led, u8 curr) curr); } -static void lp5521_init_engine(struct lp5521_chip *chip) -{ - int i; - for (i = 0; i < ARRAY_SIZE(chip->engines); i++) { - chip->engines[i].id = i + 1; - chip->engines[i].engine_mask = LP5521_ENG_MASK_BASE >> (i * 2); - chip->engines[i].prog_page = i; - } -} - static int lp5521_configure(struct i2c_client *client) { struct lp5521_chip *chip = i2c_get_clientdata(client); int ret; u8 cfg; + u8 val; - lp5521_init_engine(chip); + /* + * Make sure that the chip is reset by reading back the r channel + * current reg. This is dummy read is required on some platforms - + * otherwise further access to the R G B channels in the + * LP5521_REG_ENABLE register will not have any effect - strange! + */ + ret = lp5521_read(client, LP5521_REG_R_CURRENT, &val); + if (ret) { + dev_err(&client->dev, "error in resetting chip\n"); + return ret; + } + if (val != LP5521_REG_R_CURR_DEFAULT) { + dev_err(&client->dev, + "unexpected data in register (expected 0x%x got 0x%x)\n", + LP5521_REG_R_CURR_DEFAULT, val); + ret = -EINVAL; + return ret; + } + usleep_range(10000, 20000); /* Set all PWMs to direct control mode */ ret = lp5521_write(client, LP5521_REG_OP_MODE, LP5521_CMD_DIRECT); cfg = chip->pdata->update_config ? : (LP5521_PWRSAVE_EN | LP5521_CP_MODE_AUTO | LP5521_R_TO_BATT); - ret |= lp5521_write(client, LP5521_REG_CONFIG, cfg); + ret = lp5521_write(client, LP5521_REG_CONFIG, cfg); + if (ret) + return ret; /* Initialize all channels PWM to zero -> leds off */ - ret |= lp5521_write(client, LP5521_REG_R_PWM, 0); - ret |= lp5521_write(client, LP5521_REG_G_PWM, 0); - ret |= lp5521_write(client, LP5521_REG_B_PWM, 0); + lp5521_write(client, LP5521_REG_R_PWM, 0); + lp5521_write(client, LP5521_REG_G_PWM, 0); + lp5521_write(client, LP5521_REG_B_PWM, 0); /* Set engines are set to run state when OP_MODE enables engines */ - ret |= lp5521_write(client, LP5521_REG_ENABLE, + ret = lp5521_write(client, LP5521_REG_ENABLE, LP5521_ENABLE_RUN_PROGRAM); - /* enable takes 500us. 1 - 2 ms leaves some margin */ - usleep_range(1000, 2000); + if (ret) + return ret; - return ret; + lp5521_wait_enable_done(); + + return 0; } static int lp5521_run_selftest(struct lp5521_chip *chip, char *buf) @@ -703,7 +722,6 @@ static int lp5521_init_device(struct lp5521_chip *chip) struct lp5521_platform_data *pdata = chip->pdata; struct i2c_client *client = chip->client; int ret; - u8 buf; if (pdata->setup_resources) { ret = pdata->setup_resources(); @@ -725,26 +743,6 @@ static int lp5521_init_device(struct lp5521_chip *chip) * appears to be enough for reset. */ - /* - * Make sure that the chip is reset by reading back the r channel - * current reg. This is dummy read is required on some platforms - - * otherwise further access to the R G B channels in the - * LP5521_REG_ENABLE register will not have any effect - strange! - */ - ret = lp5521_read(client, LP5521_REG_R_CURRENT, &buf); - if (ret) { - dev_err(&client->dev, "error in resetting chip\n"); - return ret; - } - if (buf != LP5521_REG_R_CURR_DEFAULT) { - dev_err(&client->dev, - "unexpected data in register (expected 0x%x got 0x%x)\n", - LP5521_REG_R_CURR_DEFAULT, buf); - ret = -EINVAL; - return ret; - } - usleep_range(10000, 20000); - ret = lp5521_detect(client); if (ret) { dev_err(&client->dev, "Chip not found\n"); -- cgit v1.2.3 From 632418bf65503405df3f9a6a1616f5a95f91db85 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 18:04:50 +0900 Subject: leds-lp5523: clean up lp5523_configure() This patch is a preceding step for making common lp55xx init function. Return code: Do not use 'OR' arithmetic for the result. If some error occurs, just return it. Remove engine verification code: To check whether internal engine works or not, many lines of code are executed. However, this job is unnecessary during the chip initialization because the engine usage is not mandatory but optional function. LED engines are enabled when specific LED pattern is loaded. Therefore, this verification code is removed. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5523.c | 67 +++++++--------------------------------------- 1 file changed, 9 insertions(+), 58 deletions(-) diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 71bda3670ff..2ca41c5af71 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -199,77 +199,28 @@ static int lp5523_detect(struct i2c_client *client) static int lp5523_configure(struct i2c_client *client) { - struct lp5523_chip *chip = i2c_get_clientdata(client); int ret = 0; - u8 status; - /* one pattern per engine setting led mux start and stop addresses */ - static const u8 pattern[][LP5523_PROGRAM_LENGTH] = { - { 0x9c, 0x30, 0x9c, 0xb0, 0x9d, 0x80, 0xd8, 0x00, 0}, - { 0x9c, 0x40, 0x9c, 0xc0, 0x9d, 0x80, 0xd8, 0x00, 0}, - { 0x9c, 0x50, 0x9c, 0xd0, 0x9d, 0x80, 0xd8, 0x00, 0}, - }; + ret = lp5523_write(client, LP5523_REG_ENABLE, LP5523_ENABLE); + if (ret) + return ret; - ret |= lp5523_write(client, LP5523_REG_ENABLE, LP5523_ENABLE); /* Chip startup time is 500 us, 1 - 2 ms gives some margin */ usleep_range(1000, 2000); - ret |= lp5523_write(client, LP5523_REG_CONFIG, + ret = lp5523_write(client, LP5523_REG_CONFIG, LP5523_AUTO_INC | LP5523_PWR_SAVE | LP5523_CP_AUTO | LP5523_AUTO_CLK | LP5523_PWM_PWR_SAVE); + if (ret) + return ret; /* turn on all leds */ - ret |= lp5523_write(client, LP5523_REG_ENABLE_LEDS_MSB, 0x01); - ret |= lp5523_write(client, LP5523_REG_ENABLE_LEDS_LSB, 0xff); - - /* hardcode 32 bytes of memory for each engine from program memory */ - ret |= lp5523_write(client, LP5523_REG_CH1_PROG_START, 0x00); - ret |= lp5523_write(client, LP5523_REG_CH2_PROG_START, 0x10); - ret |= lp5523_write(client, LP5523_REG_CH3_PROG_START, 0x20); - - /* write led mux address space for each channel */ - ret |= lp5523_load_program(&chip->engines[0], pattern[0]); - ret |= lp5523_load_program(&chip->engines[1], pattern[1]); - ret |= lp5523_load_program(&chip->engines[2], pattern[2]); - - if (ret) { - dev_err(&client->dev, "could not load mux programs\n"); - return -1; - } - - /* set all engines exec state and mode to run 00101010 */ - ret |= lp5523_write(client, LP5523_REG_ENABLE, - (LP5523_CMD_RUN | LP5523_ENABLE)); - - ret |= lp5523_write(client, LP5523_REG_OP_MODE, LP5523_CMD_RUN); - - if (ret) { - dev_err(&client->dev, "could not start mux programs\n"); - return -1; - } - - /* Let the programs run for couple of ms and check the engine status */ - usleep_range(3000, 6000); - ret = lp5523_read(client, LP5523_REG_STATUS, &status); - if (ret < 0) + ret = lp5523_write(client, LP5523_REG_ENABLE_LEDS_MSB, 0x01); + if (ret) return ret; - status &= LP5523_ENG_STATUS_MASK; - - if (status == LP5523_ENG_STATUS_MASK) { - dev_dbg(&client->dev, "all engines configured\n"); - } else { - dev_info(&client->dev, "status == %x\n", status); - dev_err(&client->dev, "cound not configure LED engine\n"); - return -1; - } - - dev_info(&client->dev, "disabling engines\n"); - - ret |= lp5523_write(client, LP5523_REG_OP_MODE, LP5523_CMD_DISABLED); - - return ret; + return lp5523_write(client, LP5523_REG_ENABLE_LEDS_LSB, 0xff); } static int lp5523_set_engine_mode(struct lp5523_engine *engine, u8 mode) -- cgit v1.2.3 From a85908dd7799e4fa242812ce27a8f774c721d1fb Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 18:07:20 +0900 Subject: leds-lp55xx: use lp55xx common init function - platform data LP5521/5523 platform data functions are moved to lp55xx common driver. New init function, lp55xx_init_device() is created. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 14 -------------- drivers/leds/leds-lp5523.c | 14 -------------- drivers/leds/leds-lp55xx-common.c | 34 ++++++++++++++++++++++++++++++++++ drivers/leds/leds-lp55xx-common.h | 3 +++ 4 files changed, 37 insertions(+), 28 deletions(-) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index 341a41030fd..124ce80fa11 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -719,23 +719,9 @@ static void lp5521_reset_device(struct lp5521_chip *chip) static void lp5521_deinit_device(struct lp5521_chip *chip); static int lp5521_init_device(struct lp5521_chip *chip) { - struct lp5521_platform_data *pdata = chip->pdata; struct i2c_client *client = chip->client; int ret; - if (pdata->setup_resources) { - ret = pdata->setup_resources(); - if (ret < 0) - return ret; - } - - if (pdata->enable) { - pdata->enable(0); - usleep_range(1000, 2000); /* Keep enable down at least 1ms */ - pdata->enable(1); - usleep_range(1000, 2000); /* 500us abs min. */ - } - lp5521_reset_device(chip); usleep_range(10000, 20000); /* diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 2ca41c5af71..8e602047ce3 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -910,23 +910,9 @@ static void lp5523_reset_device(struct lp5523_chip *chip) static void lp5523_deinit_device(struct lp5523_chip *chip); static int lp5523_init_device(struct lp5523_chip *chip) { - struct lp5523_platform_data *pdata = chip->pdata; struct i2c_client *client = chip->client; int ret; - if (pdata->setup_resources) { - ret = pdata->setup_resources(); - if (ret < 0) - return ret; - } - - if (pdata->enable) { - pdata->enable(0); - usleep_range(1000, 2000); /* Keep enable down at least 1ms */ - pdata->enable(1); - usleep_range(1000, 2000); /* 500us abs min. */ - } - lp5523_reset_device(chip); usleep_range(10000, 20000); /* diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index 1c716ecfa81..05a854c0d9b 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -12,6 +12,7 @@ * Derived from leds-lp5521.c, leds-lp5523.c */ +#include #include #include #include @@ -54,6 +55,39 @@ int lp55xx_update_bits(struct lp55xx_chip *chip, u8 reg, u8 mask, u8 val) } EXPORT_SYMBOL_GPL(lp55xx_update_bits); +int lp55xx_init_device(struct lp55xx_chip *chip) +{ + struct lp55xx_platform_data *pdata; + struct device *dev = &chip->cl->dev; + int ret = 0; + + WARN_ON(!chip); + + pdata = chip->pdata; + + if (!pdata) + return -EINVAL; + + if (pdata->setup_resources) { + ret = pdata->setup_resources(); + if (ret < 0) { + dev_err(dev, "setup resoure err: %d\n", ret); + goto err; + } + } + + if (pdata->enable) { + pdata->enable(0); + usleep_range(1000, 2000); /* Keep enable down at least 1ms */ + pdata->enable(1); + usleep_range(1000, 2000); /* 500us abs min. */ + } + +err: + return ret; +} +EXPORT_SYMBOL_GPL(lp55xx_init_device); + MODULE_AUTHOR("Milo Kim "); MODULE_DESCRIPTION("LP55xx Common Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/leds/leds-lp55xx-common.h b/drivers/leds/leds-lp55xx-common.h index 369cb9c91f1..09d1882ce58 100644 --- a/drivers/leds/leds-lp55xx-common.h +++ b/drivers/leds/leds-lp55xx-common.h @@ -58,4 +58,7 @@ extern int lp55xx_read(struct lp55xx_chip *chip, u8 reg, u8 *val); extern int lp55xx_update_bits(struct lp55xx_chip *chip, u8 reg, u8 mask, u8 val); +/* common device init functions */ +extern int lp55xx_init_device(struct lp55xx_chip *chip); + #endif /* _LEDS_LP55XX_COMMON_H */ -- cgit v1.2.3 From 48068d5de16c23c256c085b2cd3ff03bec393900 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 18:08:49 +0900 Subject: leds-lp55xx: use lp55xx common init function - reset LP5521/5523 reset device functions are moved to lp55xx common driver. Value of register address and value are chip dependent. Those are configured in each driver. In init function, reset command is executed. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 26 ++++++++++++-------------- drivers/leds/leds-lp5523.c | 23 ++++++++++------------- drivers/leds/leds-lp55xx-common.c | 22 +++++++++++++++++++++- drivers/leds/leds-lp55xx-common.h | 20 ++++++++++++++++++++ 4 files changed, 63 insertions(+), 28 deletions(-) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index 124ce80fa11..e1f1dfcd154 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -98,6 +98,9 @@ /* Pattern Mode */ #define PATTERN_OFF 0 +/* Reset register value */ +#define LP5521_RESET 0xFF + struct lp5521_engine { int id; u8 mode; @@ -709,26 +712,12 @@ static void lp5521_unregister_sysfs(struct i2c_client *client) &lp5521_led_attribute_group); } -static void lp5521_reset_device(struct lp5521_chip *chip) -{ - struct i2c_client *client = chip->client; - - lp5521_write(client, LP5521_REG_RESET, 0xff); -} - static void lp5521_deinit_device(struct lp5521_chip *chip); static int lp5521_init_device(struct lp5521_chip *chip) { struct i2c_client *client = chip->client; int ret; - lp5521_reset_device(chip); - - usleep_range(10000, 20000); /* - * Exact value is not available. 10 - 20ms - * appears to be enough for reset. - */ - ret = lp5521_detect(client); if (ret) { dev_err(&client->dev, "Chip not found\n"); @@ -856,6 +845,14 @@ static void lp5521_unregister_leds(struct lp5521_chip *chip) } } +/* Chip specific configurations */ +static struct lp55xx_device_config lp5521_cfg = { + .reset = { + .addr = LP5521_REG_RESET, + .val = LP5521_RESET, + }, +}; + static int lp5521_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -881,6 +878,7 @@ static int lp5521_probe(struct i2c_client *client, chip->cl = client; chip->pdata = pdata; + chip->cfg = &lp5521_cfg; mutex_init(&chip->lock); diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 8e602047ce3..00547783db7 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -87,6 +87,7 @@ #define LP5523_AUTO_CLK 0x02 #define LP5523_EN_LEDTEST 0x80 #define LP5523_LEDTEST_DONE 0x80 +#define LP5523_RESET 0xFF #define LP5523_DEFAULT_CURRENT 50 /* microAmps */ #define LP5523_PROGRAM_LENGTH 32 /* in bytes */ @@ -900,25 +901,12 @@ static void lp5523_unregister_leds(struct lp5523_chip *chip) } } -static void lp5523_reset_device(struct lp5523_chip *chip) -{ - struct i2c_client *client = chip->client; - - lp5523_write(client, LP5523_REG_RESET, 0xff); -} - static void lp5523_deinit_device(struct lp5523_chip *chip); static int lp5523_init_device(struct lp5523_chip *chip) { struct i2c_client *client = chip->client; int ret; - lp5523_reset_device(chip); - - usleep_range(10000, 20000); /* - * Exact value is not available. 10 - 20ms - * appears to be enough for reset. - */ ret = lp5523_detect(client); if (ret) goto err; @@ -947,6 +935,14 @@ static void lp5523_deinit_device(struct lp5523_chip *chip) pdata->release_resources(); } +/* Chip specific configurations */ +static struct lp55xx_device_config lp5523_cfg = { + .reset = { + .addr = LP5523_REG_RESET, + .val = LP5523_RESET, + }, +}; + static int lp5523_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -972,6 +968,7 @@ static int lp5523_probe(struct i2c_client *client, chip->cl = client; chip->pdata = pdata; + chip->cfg = &lp5523_cfg; mutex_init(&chip->lock); diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index 05a854c0d9b..bbf2bbf0380 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -20,6 +20,16 @@ #include "leds-lp55xx-common.h" +static void lp55xx_reset_device(struct lp55xx_chip *chip) +{ + struct lp55xx_device_config *cfg = chip->cfg; + u8 addr = cfg->reset.addr; + u8 val = cfg->reset.val; + + /* no error checking here because no ACK from the device after reset */ + lp55xx_write(chip, addr, val); +} + int lp55xx_write(struct lp55xx_chip *chip, u8 reg, u8 val) { return i2c_smbus_write_byte_data(chip->cl, reg, val); @@ -58,14 +68,16 @@ EXPORT_SYMBOL_GPL(lp55xx_update_bits); int lp55xx_init_device(struct lp55xx_chip *chip) { struct lp55xx_platform_data *pdata; + struct lp55xx_device_config *cfg; struct device *dev = &chip->cl->dev; int ret = 0; WARN_ON(!chip); pdata = chip->pdata; + cfg = chip->cfg; - if (!pdata) + if (!pdata || !cfg) return -EINVAL; if (pdata->setup_resources) { @@ -83,6 +95,14 @@ int lp55xx_init_device(struct lp55xx_chip *chip) usleep_range(1000, 2000); /* 500us abs min. */ } + lp55xx_reset_device(chip); + + /* + * Exact value is not available. 10 - 20ms + * appears to be enough for reset. + */ + usleep_range(10000, 20000); + err: return ret; } diff --git a/drivers/leds/leds-lp55xx-common.h b/drivers/leds/leds-lp55xx-common.h index 09d1882ce58..a73ee0b9a0b 100644 --- a/drivers/leds/leds-lp55xx-common.h +++ b/drivers/leds/leds-lp55xx-common.h @@ -18,18 +18,38 @@ struct lp55xx_led; struct lp55xx_chip; +/* + * struct lp55xx_reg + * @addr : Register address + * @val : Register value + */ +struct lp55xx_reg { + u8 addr; + u8 val; +}; + +/* + * struct lp55xx_device_config + * @reset : Chip specific reset command + */ +struct lp55xx_device_config { + const struct lp55xx_reg reset; +}; + /* * struct lp55xx_chip * @cl : I2C communication for access registers * @pdata : Platform specific data * @lock : Lock for user-space interface * @num_leds : Number of registered LEDs + * @cfg : Device specific configuration data */ struct lp55xx_chip { struct i2c_client *cl; struct lp55xx_platform_data *pdata; struct mutex lock; /* lock for user-space interface */ int num_leds; + struct lp55xx_device_config *cfg; }; /* -- cgit v1.2.3 From e3a700d8aae190e09fb06abe0ddd2e172a682508 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 18:09:56 +0900 Subject: leds-lp55xx: use lp55xx common init function - detect LP5521/5523 chip detection functions are replaced with lp55xx common function, lp55xx_detect_device(). Chip dependent address and values are configurable in each driver. In init function, chip detection is executed. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 31 ++++--------------------------- drivers/leds/leds-lp5523.c | 26 ++++---------------------- drivers/leds/leds-lp55xx-common.c | 29 +++++++++++++++++++++++++++++ drivers/leds/leds-lp55xx-common.h | 2 ++ 4 files changed, 39 insertions(+), 49 deletions(-) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index e1f1dfcd154..0e27f7eb5d0 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -328,26 +328,6 @@ static void lp5521_led_brightness_work(struct work_struct *work) mutex_unlock(&chip->lock); } -/* Detect the chip by setting its ENABLE register and reading it back. */ -static int lp5521_detect(struct i2c_client *client) -{ - int ret; - u8 buf; - - ret = lp5521_write(client, LP5521_REG_ENABLE, LP5521_ENABLE_DEFAULT); - if (ret) - return ret; - /* enable takes 500us. 1 - 2 ms leaves some margin */ - usleep_range(1000, 2000); - ret = lp5521_read(client, LP5521_REG_ENABLE, &buf); - if (ret) - return ret; - if (buf != LP5521_ENABLE_DEFAULT) - return -ENODEV; - - return 0; -} - /* Set engine mode and create appropriate sysfs attributes, if required. */ static int lp5521_set_mode(struct lp5521_engine *engine, u8 mode) { @@ -718,12 +698,6 @@ static int lp5521_init_device(struct lp5521_chip *chip) struct i2c_client *client = chip->client; int ret; - ret = lp5521_detect(client); - if (ret) { - dev_err(&client->dev, "Chip not found\n"); - goto err; - } - ret = lp5521_configure(client); if (ret < 0) { dev_err(&client->dev, "error configuring chip\n"); @@ -734,7 +708,6 @@ static int lp5521_init_device(struct lp5521_chip *chip) err_config: lp5521_deinit_device(chip); -err: return ret; } @@ -851,6 +824,10 @@ static struct lp55xx_device_config lp5521_cfg = { .addr = LP5521_REG_RESET, .val = LP5521_RESET, }, + .enable = { + .addr = LP5521_REG_ENABLE, + .val = LP5521_ENABLE_DEFAULT, + }, }; static int lp5521_probe(struct i2c_client *client, diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 00547783db7..b3698e85d51 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -181,23 +181,6 @@ static int lp5523_read(struct i2c_client *client, u8 reg, u8 *buf) return 0; } -static int lp5523_detect(struct i2c_client *client) -{ - int ret; - u8 buf; - - ret = lp5523_write(client, LP5523_REG_ENABLE, LP5523_ENABLE); - if (ret) - return ret; - ret = lp5523_read(client, LP5523_REG_ENABLE, &buf); - if (ret) - return ret; - if (buf == 0x40) - return 0; - else - return -ENODEV; -} - static int lp5523_configure(struct i2c_client *client) { int ret = 0; @@ -907,10 +890,6 @@ static int lp5523_init_device(struct lp5523_chip *chip) struct i2c_client *client = chip->client; int ret; - ret = lp5523_detect(client); - if (ret) - goto err; - ret = lp5523_configure(client); if (ret < 0) { dev_err(&client->dev, "error configuring chip\n"); @@ -921,7 +900,6 @@ static int lp5523_init_device(struct lp5523_chip *chip) err_config: lp5523_deinit_device(chip); -err: return ret; } @@ -941,6 +919,10 @@ static struct lp55xx_device_config lp5523_cfg = { .addr = LP5523_REG_RESET, .val = LP5523_RESET, }, + .enable = { + .addr = LP5523_REG_ENABLE, + .val = LP5523_ENABLE, + }, }; static int lp5523_probe(struct i2c_client *client, diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index bbf2bbf0380..6fede0b9671 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -30,6 +30,29 @@ static void lp55xx_reset_device(struct lp55xx_chip *chip) lp55xx_write(chip, addr, val); } +static int lp55xx_detect_device(struct lp55xx_chip *chip) +{ + struct lp55xx_device_config *cfg = chip->cfg; + u8 addr = cfg->enable.addr; + u8 val = cfg->enable.val; + int ret; + + ret = lp55xx_write(chip, addr, val); + if (ret) + return ret; + + usleep_range(1000, 2000); + + ret = lp55xx_read(chip, addr, &val); + if (ret) + return ret; + + if (val != cfg->enable.val) + return -ENODEV; + + return 0; +} + int lp55xx_write(struct lp55xx_chip *chip, u8 reg, u8 val) { return i2c_smbus_write_byte_data(chip->cl, reg, val); @@ -103,6 +126,12 @@ int lp55xx_init_device(struct lp55xx_chip *chip) */ usleep_range(10000, 20000); + ret = lp55xx_detect_device(chip); + if (ret) { + dev_err(dev, "device detection err: %d\n", ret); + goto err; + } + err: return ret; } diff --git a/drivers/leds/leds-lp55xx-common.h b/drivers/leds/leds-lp55xx-common.h index a73ee0b9a0b..81753012ba2 100644 --- a/drivers/leds/leds-lp55xx-common.h +++ b/drivers/leds/leds-lp55xx-common.h @@ -31,9 +31,11 @@ struct lp55xx_reg { /* * struct lp55xx_device_config * @reset : Chip specific reset command + * @enable : Chip specific enable command */ struct lp55xx_device_config { const struct lp55xx_reg reset; + const struct lp55xx_reg enable; }; /* -- cgit v1.2.3 From ffbdccdbbaee814963a09d25b1cc598cfe131366 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 18:57:36 +0900 Subject: leds-lp55xx: use lp55xx common init function - post int LP5521/5523 chip configuration is replaced with lp55xx common function, lp55xx_post_init_device(). Name change: lp5521/5523_configure() to lp5521/5523_post_init_device() These are called in init function. Register access function Argument type is changed from 'i2c_client' to 'lp55xx_chip'. Use exported R/W functions of lp55xx common driver. Temporary variables in lp5521/5523_init_device() These functions will be removed but temporary variables are needed for blocking build warnings - incompatible pointer. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 29 ++++++++++++++--------------- drivers/leds/leds-lp5523.c | 16 +++++++++------- drivers/leds/leds-lp55xx-common.c | 15 +++++++++++++++ drivers/leds/leds-lp55xx-common.h | 4 ++++ 4 files changed, 42 insertions(+), 22 deletions(-) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index 0e27f7eb5d0..faab44900c2 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -238,11 +238,9 @@ static int lp5521_set_led_current(struct lp5521_chip *chip, int led, u8 curr) curr); } -static int lp5521_configure(struct i2c_client *client) +static int lp5521_post_init_device(struct lp55xx_chip *chip) { - struct lp5521_chip *chip = i2c_get_clientdata(client); int ret; - u8 cfg; u8 val; /* @@ -251,13 +249,13 @@ static int lp5521_configure(struct i2c_client *client) * otherwise further access to the R G B channels in the * LP5521_REG_ENABLE register will not have any effect - strange! */ - ret = lp5521_read(client, LP5521_REG_R_CURRENT, &val); + ret = lp55xx_read(chip, LP5521_REG_R_CURRENT, &val); if (ret) { - dev_err(&client->dev, "error in resetting chip\n"); + dev_err(&chip->cl->dev, "error in resetting chip\n"); return ret; } if (val != LP5521_REG_R_CURR_DEFAULT) { - dev_err(&client->dev, + dev_err(&chip->cl->dev, "unexpected data in register (expected 0x%x got 0x%x)\n", LP5521_REG_R_CURR_DEFAULT, val); ret = -EINVAL; @@ -266,22 +264,21 @@ static int lp5521_configure(struct i2c_client *client) usleep_range(10000, 20000); /* Set all PWMs to direct control mode */ - ret = lp5521_write(client, LP5521_REG_OP_MODE, LP5521_CMD_DIRECT); + ret = lp55xx_write(chip, LP5521_REG_OP_MODE, LP5521_CMD_DIRECT); - cfg = chip->pdata->update_config ? + val = chip->pdata->update_config ? : (LP5521_PWRSAVE_EN | LP5521_CP_MODE_AUTO | LP5521_R_TO_BATT); - ret = lp5521_write(client, LP5521_REG_CONFIG, cfg); + ret = lp55xx_write(chip, LP5521_REG_CONFIG, val); if (ret) return ret; /* Initialize all channels PWM to zero -> leds off */ - lp5521_write(client, LP5521_REG_R_PWM, 0); - lp5521_write(client, LP5521_REG_G_PWM, 0); - lp5521_write(client, LP5521_REG_B_PWM, 0); + lp55xx_write(chip, LP5521_REG_R_PWM, 0); + lp55xx_write(chip, LP5521_REG_G_PWM, 0); + lp55xx_write(chip, LP5521_REG_B_PWM, 0); /* Set engines are set to run state when OP_MODE enables engines */ - ret = lp5521_write(client, LP5521_REG_ENABLE, - LP5521_ENABLE_RUN_PROGRAM); + ret = lp55xx_write(chip, LP5521_REG_ENABLE, LP5521_ENABLE_RUN_PROGRAM); if (ret) return ret; @@ -696,9 +693,10 @@ static void lp5521_deinit_device(struct lp5521_chip *chip); static int lp5521_init_device(struct lp5521_chip *chip) { struct i2c_client *client = chip->client; + struct lp55xx_chip *temp; int ret; - ret = lp5521_configure(client); + ret = lp5521_post_init_device(temp); if (ret < 0) { dev_err(&client->dev, "error configuring chip\n"); goto err_config; @@ -828,6 +826,7 @@ static struct lp55xx_device_config lp5521_cfg = { .addr = LP5521_REG_ENABLE, .val = LP5521_ENABLE_DEFAULT, }, + .post_init_device = lp5521_post_init_device, }; static int lp5521_probe(struct i2c_client *client, diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index b3698e85d51..110565b066e 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -181,18 +181,18 @@ static int lp5523_read(struct i2c_client *client, u8 reg, u8 *buf) return 0; } -static int lp5523_configure(struct i2c_client *client) +static int lp5523_post_init_device(struct lp55xx_chip *chip) { - int ret = 0; + int ret; - ret = lp5523_write(client, LP5523_REG_ENABLE, LP5523_ENABLE); + ret = lp55xx_write(chip, LP5523_REG_ENABLE, LP5523_ENABLE); if (ret) return ret; /* Chip startup time is 500 us, 1 - 2 ms gives some margin */ usleep_range(1000, 2000); - ret = lp5523_write(client, LP5523_REG_CONFIG, + ret = lp55xx_write(chip, LP5523_REG_CONFIG, LP5523_AUTO_INC | LP5523_PWR_SAVE | LP5523_CP_AUTO | LP5523_AUTO_CLK | LP5523_PWM_PWR_SAVE); @@ -200,11 +200,11 @@ static int lp5523_configure(struct i2c_client *client) return ret; /* turn on all leds */ - ret = lp5523_write(client, LP5523_REG_ENABLE_LEDS_MSB, 0x01); + ret = lp55xx_write(chip, LP5523_REG_ENABLE_LEDS_MSB, 0x01); if (ret) return ret; - return lp5523_write(client, LP5523_REG_ENABLE_LEDS_LSB, 0xff); + return lp55xx_write(chip, LP5523_REG_ENABLE_LEDS_LSB, 0xff); } static int lp5523_set_engine_mode(struct lp5523_engine *engine, u8 mode) @@ -888,9 +888,10 @@ static void lp5523_deinit_device(struct lp5523_chip *chip); static int lp5523_init_device(struct lp5523_chip *chip) { struct i2c_client *client = chip->client; + struct lp55xx_chip *temp; int ret; - ret = lp5523_configure(client); + ret = lp5523_post_init_device(temp); if (ret < 0) { dev_err(&client->dev, "error configuring chip\n"); goto err_config; @@ -923,6 +924,7 @@ static struct lp55xx_device_config lp5523_cfg = { .addr = LP5523_REG_ENABLE, .val = LP5523_ENABLE, }, + .post_init_device = lp5523_post_init_device, }; static int lp5523_probe(struct i2c_client *client, diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index 6fede0b9671..74beb363b78 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -53,6 +53,16 @@ static int lp55xx_detect_device(struct lp55xx_chip *chip) return 0; } +static int lp55xx_post_init_device(struct lp55xx_chip *chip) +{ + struct lp55xx_device_config *cfg = chip->cfg; + + if (!cfg->post_init_device) + return 0; + + return cfg->post_init_device(chip); +} + int lp55xx_write(struct lp55xx_chip *chip, u8 reg, u8 val) { return i2c_smbus_write_byte_data(chip->cl, reg, val); @@ -132,6 +142,11 @@ int lp55xx_init_device(struct lp55xx_chip *chip) goto err; } + /* chip specific initialization */ + ret = lp55xx_post_init_device(chip); + + return 0; + err: return ret; } diff --git a/drivers/leds/leds-lp55xx-common.h b/drivers/leds/leds-lp55xx-common.h index 81753012ba2..ffedc7723d8 100644 --- a/drivers/leds/leds-lp55xx-common.h +++ b/drivers/leds/leds-lp55xx-common.h @@ -32,10 +32,14 @@ struct lp55xx_reg { * struct lp55xx_device_config * @reset : Chip specific reset command * @enable : Chip specific enable command + * @post_init_device : Chip specific initialization code */ struct lp55xx_device_config { const struct lp55xx_reg reset; const struct lp55xx_reg enable; + + /* define if the device has specific initialization process */ + int (*post_init_device) (struct lp55xx_chip *chip); }; /* -- cgit v1.2.3 From 22ebeb488b3dbbb64b81146b366551107ae34af8 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 18:58:35 +0900 Subject: leds-lp55xx: clean up init function lp5521/5523_init_device() are replaced with lp55xx common function, lp55xx_init_device(). Error handler in init_device: deinit function are matched with 'err_post_init' section in lp55xx_init_device(). Remove LP5523 engine intialization code: Engine functionality is not mandatory but optional. Moreover engine initialization is done internally with device reset command. Therefore, this code is unnecessary. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 22 +------------------ drivers/leds/leds-lp5523.c | 46 ++------------------------------------- drivers/leds/leds-lp55xx-common.c | 9 ++++++++ 3 files changed, 12 insertions(+), 65 deletions(-) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index faab44900c2..74dc208fb99 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -689,26 +689,6 @@ static void lp5521_unregister_sysfs(struct i2c_client *client) &lp5521_led_attribute_group); } -static void lp5521_deinit_device(struct lp5521_chip *chip); -static int lp5521_init_device(struct lp5521_chip *chip) -{ - struct i2c_client *client = chip->client; - struct lp55xx_chip *temp; - int ret; - - ret = lp5521_post_init_device(temp); - if (ret < 0) { - dev_err(&client->dev, "error configuring chip\n"); - goto err_config; - } - - return 0; - -err_config: - lp5521_deinit_device(chip); - return ret; -} - static void lp5521_deinit_device(struct lp5521_chip *chip) { struct lp5521_platform_data *pdata = chip->pdata; @@ -860,7 +840,7 @@ static int lp5521_probe(struct i2c_client *client, i2c_set_clientdata(client, led); - ret = lp5521_init_device(old_chip); + ret = lp55xx_init_device(chip); if (ret) goto err_init; diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 110565b066e..80b7fb4a3ad 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -773,18 +773,6 @@ static void lp5523_set_mode(struct lp5523_engine *engine, u8 mode) /*--------------------------------------------------------------*/ /* Probe, Attach, Remove */ /*--------------------------------------------------------------*/ -static int __init lp5523_init_engine(struct lp5523_engine *engine, int id) -{ - if (id < 1 || id > LP5523_ENGINES) - return -1; - engine->id = id; - engine->engine_mask = LP5523_ENG_MASK_BASE >> SHIFT_MASK(id); - engine->prog_page = id - 1; - engine->mux_page = id + 2; - - return 0; -} - static int lp5523_init_led(struct lp5523_led *led, struct device *dev, int chan, struct lp5523_platform_data *pdata, const char *chip_name) @@ -884,26 +872,6 @@ static void lp5523_unregister_leds(struct lp5523_chip *chip) } } -static void lp5523_deinit_device(struct lp5523_chip *chip); -static int lp5523_init_device(struct lp5523_chip *chip) -{ - struct i2c_client *client = chip->client; - struct lp55xx_chip *temp; - int ret; - - ret = lp5523_post_init_device(temp); - if (ret < 0) { - dev_err(&client->dev, "error configuring chip\n"); - goto err_config; - } - - return 0; - -err_config: - lp5523_deinit_device(chip); - return ret; -} - static void lp5523_deinit_device(struct lp5523_chip *chip) { struct lp5523_platform_data *pdata = chip->pdata; @@ -931,7 +899,7 @@ static int lp5523_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct lp5523_chip *old_chip = NULL; - int ret, i; + int ret; struct lp55xx_chip *chip; struct lp55xx_led *led; struct lp55xx_platform_data *pdata = client->dev.platform_data; @@ -958,21 +926,12 @@ static int lp5523_probe(struct i2c_client *client, i2c_set_clientdata(client, led); - ret = lp5523_init_device(old_chip); + ret = lp55xx_init_device(chip); if (ret) goto err_init; dev_info(&client->dev, "%s Programmable led chip found\n", id->name); - /* Initialize engines */ - for (i = 0; i < ARRAY_SIZE(old_chip->engines); i++) { - ret = lp5523_init_engine(&old_chip->engines[i], i + 1); - if (ret) { - dev_err(&client->dev, "error initializing engine\n"); - goto fail1; - } - } - ret = lp5523_register_leds(old_chip, id->name); if (ret) goto fail2; @@ -985,7 +944,6 @@ static int lp5523_probe(struct i2c_client *client, return ret; fail2: lp5523_unregister_leds(old_chip); -fail1: lp5523_deinit_device(old_chip); err_init: return ret; diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index 74beb363b78..c06745f160c 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -144,9 +144,18 @@ int lp55xx_init_device(struct lp55xx_chip *chip) /* chip specific initialization */ ret = lp55xx_post_init_device(chip); + if (ret) { + dev_err(dev, "post init device err: %d\n", ret); + goto err_post_init; + } return 0; +err_post_init: + if (pdata->enable) + pdata->enable(0); + if (pdata->release_resources) + pdata->release_resources(); err: return ret; } -- cgit v1.2.3 From 6ce6176263393dd80b9a537c1e1462b8529f240b Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:03:02 +0900 Subject: leds-lp55xx: use lp55xx common deinit function Two separate de-init functions are merged into one common function. And it is used in err_post_init of lp55xx_init_device(). Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 16 ++++------------ drivers/leds/leds-lp5523.c | 16 ++++------------ drivers/leds/leds-lp55xx-common.c | 15 ++++++++++++--- drivers/leds/leds-lp55xx-common.h | 3 ++- 4 files changed, 22 insertions(+), 28 deletions(-) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index 74dc208fb99..dd4526e168f 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -689,16 +689,6 @@ static void lp5521_unregister_sysfs(struct i2c_client *client) &lp5521_led_attribute_group); } -static void lp5521_deinit_device(struct lp5521_chip *chip) -{ - struct lp5521_platform_data *pdata = chip->pdata; - - if (pdata->enable) - pdata->enable(0); - if (pdata->release_resources) - pdata->release_resources(); -} - static int lp5521_init_led(struct lp5521_led *led, struct i2c_client *client, int chan, struct lp5521_platform_data *pdata) @@ -858,7 +848,7 @@ static int lp5521_probe(struct i2c_client *client, return ret; fail2: lp5521_unregister_leds(old_chip); - lp5521_deinit_device(old_chip); + lp55xx_deinit_device(chip); err_init: return ret; } @@ -866,13 +856,15 @@ err_init: static int lp5521_remove(struct i2c_client *client) { struct lp5521_chip *old_chip = i2c_get_clientdata(client); + struct lp55xx_led *led = i2c_get_clientdata(client); + struct lp55xx_chip *chip = led->chip; lp5521_run_led_pattern(PATTERN_OFF, old_chip); lp5521_unregister_sysfs(client); lp5521_unregister_leds(old_chip); + lp55xx_deinit_device(chip); - lp5521_deinit_device(old_chip); return 0; } diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 80b7fb4a3ad..3f506e3d498 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -872,16 +872,6 @@ static void lp5523_unregister_leds(struct lp5523_chip *chip) } } -static void lp5523_deinit_device(struct lp5523_chip *chip) -{ - struct lp5523_platform_data *pdata = chip->pdata; - - if (pdata->enable) - pdata->enable(0); - if (pdata->release_resources) - pdata->release_resources(); -} - /* Chip specific configurations */ static struct lp55xx_device_config lp5523_cfg = { .reset = { @@ -944,7 +934,7 @@ static int lp5523_probe(struct i2c_client *client, return ret; fail2: lp5523_unregister_leds(old_chip); - lp5523_deinit_device(old_chip); + lp55xx_deinit_device(chip); err_init: return ret; } @@ -952,6 +942,8 @@ err_init: static int lp5523_remove(struct i2c_client *client) { struct lp5523_chip *old_chip = i2c_get_clientdata(client); + struct lp55xx_led *led = i2c_get_clientdata(client); + struct lp55xx_chip *chip = led->chip; /* Disable engine mode */ lp5523_write(client, LP5523_REG_OP_MODE, LP5523_CMD_DISABLED); @@ -959,8 +951,8 @@ static int lp5523_remove(struct i2c_client *client) lp5523_unregister_sysfs(client); lp5523_unregister_leds(old_chip); + lp55xx_deinit_device(chip); - lp5523_deinit_device(old_chip); return 0; } diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index c06745f160c..bcabf2cb949 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -152,14 +152,23 @@ int lp55xx_init_device(struct lp55xx_chip *chip) return 0; err_post_init: + lp55xx_deinit_device(chip); +err: + return ret; +} +EXPORT_SYMBOL_GPL(lp55xx_init_device); + +void lp55xx_deinit_device(struct lp55xx_chip *chip) +{ + struct lp55xx_platform_data *pdata = chip->pdata; + if (pdata->enable) pdata->enable(0); + if (pdata->release_resources) pdata->release_resources(); -err: - return ret; } -EXPORT_SYMBOL_GPL(lp55xx_init_device); +EXPORT_SYMBOL_GPL(lp55xx_deinit_device); MODULE_AUTHOR("Milo Kim "); MODULE_DESCRIPTION("LP55xx Common Driver"); diff --git a/drivers/leds/leds-lp55xx-common.h b/drivers/leds/leds-lp55xx-common.h index ffedc7723d8..908b00a56b7 100644 --- a/drivers/leds/leds-lp55xx-common.h +++ b/drivers/leds/leds-lp55xx-common.h @@ -84,7 +84,8 @@ extern int lp55xx_read(struct lp55xx_chip *chip, u8 reg, u8 *val); extern int lp55xx_update_bits(struct lp55xx_chip *chip, u8 reg, u8 mask, u8 val); -/* common device init functions */ +/* common device init/deinit functions */ extern int lp55xx_init_device(struct lp55xx_chip *chip); +extern void lp55xx_deinit_device(struct lp55xx_chip *chip); #endif /* _LEDS_LP55XX_COMMON_H */ -- cgit v1.2.3 From 9e9b3db1b2f725bacaf1b7e8708a0c78265bde97 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:06:27 +0900 Subject: leds-lp55xx: use lp55xx common led registration function LED class devices are registered in lp5521_register_leds() and lp5523_register_leds(). Two separate functions are merged into consolidated lp55xx function, lp55xx_register_leds(). Error handling fix: Unregistering LEDS are handled in lp55xx_register_leds() when LED registration failure occurs. So each driver error handler is changed to 'err_register_leds' Chip dependency: 'brightness_work_fn' and 'set_led_current' To make the structure abstract, both functions are configured in each driver. Those functions should be done by each driver because register control is chip-dependant work. lp55xx_init_led: skeleton Will be filled in next patch Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 43 +++--------------------------- drivers/leds/leds-lp5523.c | 45 +++----------------------------- drivers/leds/leds-lp55xx-common.c | 55 +++++++++++++++++++++++++++++++++++++++ drivers/leds/leds-lp55xx-common.h | 11 ++++++++ 4 files changed, 72 insertions(+), 82 deletions(-) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index dd4526e168f..dc58f4106d0 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -738,44 +738,6 @@ static int lp5521_init_led(struct lp5521_led *led, return 0; } -static int lp5521_register_leds(struct lp5521_chip *chip) -{ - struct lp5521_platform_data *pdata = chip->pdata; - struct i2c_client *client = chip->client; - int i; - int led; - int ret; - - /* Initialize leds */ - chip->num_channels = pdata->num_channels; - chip->num_leds = 0; - led = 0; - for (i = 0; i < pdata->num_channels; i++) { - /* Do not initialize channels that are not connected */ - if (pdata->led_config[i].led_current == 0) - continue; - - ret = lp5521_init_led(&chip->leds[led], client, i, pdata); - if (ret) { - dev_err(&client->dev, "error initializing leds\n"); - return ret; - } - chip->num_leds++; - - chip->leds[led].id = led; - /* Set initial LED current */ - lp5521_set_led_current(chip, led, - chip->leds[led].led_current); - - INIT_WORK(&(chip->leds[led].brightness_work), - lp5521_led_brightness_work); - - led++; - } - - return 0; -} - static void lp5521_unregister_leds(struct lp5521_chip *chip) { int i; @@ -836,9 +798,9 @@ static int lp5521_probe(struct i2c_client *client, dev_info(&client->dev, "%s programmable led chip found\n", id->name); - ret = lp5521_register_leds(old_chip); + ret = lp55xx_register_leds(led, chip); if (ret) - goto fail2; + goto err_register_leds; ret = lp5521_register_sysfs(client); if (ret) { @@ -848,6 +810,7 @@ static int lp5521_probe(struct i2c_client *client, return ret; fail2: lp5521_unregister_leds(old_chip); +err_register_leds: lp55xx_deinit_device(chip); err_init: return ret; diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 3f506e3d498..41ac502ff82 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -822,46 +822,6 @@ static int lp5523_init_led(struct lp5523_led *led, struct device *dev, return 0; } -static int lp5523_register_leds(struct lp5523_chip *chip, const char *name) -{ - struct lp5523_platform_data *pdata = chip->pdata; - struct i2c_client *client = chip->client; - int i; - int led; - int ret; - - /* Initialize leds */ - chip->num_channels = pdata->num_channels; - chip->num_leds = 0; - led = 0; - for (i = 0; i < pdata->num_channels; i++) { - /* Do not initialize channels that are not connected */ - if (pdata->led_config[i].led_current == 0) - continue; - - INIT_WORK(&chip->leds[led].brightness_work, - lp5523_led_brightness_work); - - ret = lp5523_init_led(&chip->leds[led], &client->dev, i, pdata, - name); - if (ret) { - dev_err(&client->dev, "error initializing leds\n"); - return ret; - } - chip->num_leds++; - - chip->leds[led].id = led; - /* Set LED current */ - lp5523_write(client, - LP5523_REG_LED_CURRENT_BASE + chip->leds[led].chan_nr, - chip->leds[led].led_current); - - led++; - } - - return 0; -} - static void lp5523_unregister_leds(struct lp5523_chip *chip) { int i; @@ -922,9 +882,9 @@ static int lp5523_probe(struct i2c_client *client, dev_info(&client->dev, "%s Programmable led chip found\n", id->name); - ret = lp5523_register_leds(old_chip, id->name); + ret = lp55xx_register_leds(led, chip); if (ret) - goto fail2; + goto err_register_leds; ret = lp5523_register_sysfs(client); if (ret) { @@ -934,6 +894,7 @@ static int lp5523_probe(struct i2c_client *client, return ret; fail2: lp5523_unregister_leds(old_chip); +err_register_leds: lp55xx_deinit_device(chip); err_init: return ret; diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index bcabf2cb949..1fab1d1c450 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -63,6 +63,12 @@ static int lp55xx_post_init_device(struct lp55xx_chip *chip) return cfg->post_init_device(chip); } +static int lp55xx_init_led(struct lp55xx_led *led, + struct lp55xx_chip *chip, int chan) +{ + return 0; +} + int lp55xx_write(struct lp55xx_chip *chip, u8 reg, u8 val) { return i2c_smbus_write_byte_data(chip->cl, reg, val); @@ -170,6 +176,55 @@ void lp55xx_deinit_device(struct lp55xx_chip *chip) } EXPORT_SYMBOL_GPL(lp55xx_deinit_device); +int lp55xx_register_leds(struct lp55xx_led *led, struct lp55xx_chip *chip) +{ + struct lp55xx_platform_data *pdata = chip->pdata; + struct lp55xx_device_config *cfg = chip->cfg; + int num_channels = pdata->num_channels; + struct lp55xx_led *each; + u8 led_current; + int ret; + int i; + + if (!cfg->brightness_work_fn) { + dev_err(&chip->cl->dev, "empty brightness configuration\n"); + return -EINVAL; + } + + for (i = 0; i < num_channels; i++) { + + /* do not initialize channels that are not connected */ + if (pdata->led_config[i].led_current == 0) + continue; + + led_current = pdata->led_config[i].led_current; + each = led + i; + ret = lp55xx_init_led(each, chip, i); + if (ret) + goto err_init_led; + + INIT_WORK(&each->brightness_work, cfg->brightness_work_fn); + + chip->num_leds++; + each->chip = chip; + + /* setting led current at each channel */ + if (cfg->set_led_current) + cfg->set_led_current(each, led_current); + } + + return 0; + +err_init_led: + for (i = 0; i < chip->num_leds; i++) { + each = led + i; + led_classdev_unregister(&each->cdev); + flush_work(&each->brightness_work); + } + return ret; +} +EXPORT_SYMBOL_GPL(lp55xx_register_leds); + MODULE_AUTHOR("Milo Kim "); MODULE_DESCRIPTION("LP55xx Common Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/leds/leds-lp55xx-common.h b/drivers/leds/leds-lp55xx-common.h index 908b00a56b7..219780a2d4e 100644 --- a/drivers/leds/leds-lp55xx-common.h +++ b/drivers/leds/leds-lp55xx-common.h @@ -33,6 +33,8 @@ struct lp55xx_reg { * @reset : Chip specific reset command * @enable : Chip specific enable command * @post_init_device : Chip specific initialization code + * @brightness_work_fn : Brightness work function + * @set_led_current : LED current set function */ struct lp55xx_device_config { const struct lp55xx_reg reset; @@ -40,6 +42,12 @@ struct lp55xx_device_config { /* define if the device has specific initialization process */ int (*post_init_device) (struct lp55xx_chip *chip); + + /* access brightness register */ + void (*brightness_work_fn)(struct work_struct *work); + + /* current setting function */ + void (*set_led_current) (struct lp55xx_led *led, u8 led_current); }; /* @@ -88,4 +96,7 @@ extern int lp55xx_update_bits(struct lp55xx_chip *chip, u8 reg, extern int lp55xx_init_device(struct lp55xx_chip *chip); extern void lp55xx_deinit_device(struct lp55xx_chip *chip); +/* common LED class device functions */ +extern int lp55xx_register_leds(struct lp55xx_led *led, + struct lp55xx_chip *chip); #endif /* _LEDS_LP55XX_COMMON_H */ -- cgit v1.2.3 From 0e2023463a3c9412728cb2c36c79aca0bb731cc8 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:07:34 +0900 Subject: leds-lp55xx: use lp55xx_init_led() common function lp5521_init_led() and lp5523_init_led() are replaced with one common function, lp55xx_init_led(). Max channels is configurable, so it's used in lp55xx_init_led(). 'LP5523_LEDS' are changed to 'LP5523_MAX_LEDS'. lp55xx_set_brightness, lp55xx_led_attributes: skeleton Will be filled in next patches. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 50 +---------------------------- drivers/leds/leds-lp5523.c | 62 +++++------------------------------- drivers/leds/leds-lp55xx-common.c | 66 +++++++++++++++++++++++++++++++++++++++ drivers/leds/leds-lp55xx-common.h | 2 ++ 4 files changed, 76 insertions(+), 104 deletions(-) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index dc58f4106d0..bda03049fb3 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -689,55 +689,6 @@ static void lp5521_unregister_sysfs(struct i2c_client *client) &lp5521_led_attribute_group); } -static int lp5521_init_led(struct lp5521_led *led, - struct i2c_client *client, - int chan, struct lp5521_platform_data *pdata) -{ - struct device *dev = &client->dev; - char name[32]; - int res; - - if (chan >= LP5521_MAX_LEDS) - return -EINVAL; - - if (pdata->led_config[chan].led_current == 0) - return 0; - - led->led_current = pdata->led_config[chan].led_current; - led->max_current = pdata->led_config[chan].max_current; - led->chan_nr = pdata->led_config[chan].chan_nr; - - if (led->chan_nr >= LP5521_MAX_LEDS) { - dev_err(dev, "Use channel numbers between 0 and %d\n", - LP5521_MAX_LEDS - 1); - return -EINVAL; - } - - led->cdev.brightness_set = lp5521_set_brightness; - if (pdata->led_config[chan].name) { - led->cdev.name = pdata->led_config[chan].name; - } else { - snprintf(name, sizeof(name), "%s:channel%d", - pdata->label ?: client->name, chan); - led->cdev.name = name; - } - - res = led_classdev_register(dev, &led->cdev); - if (res < 0) { - dev_err(dev, "couldn't register led on channel %d\n", chan); - return res; - } - - res = sysfs_create_group(&led->cdev.dev->kobj, - &lp5521_led_attribute_group); - if (res < 0) { - dev_err(dev, "couldn't register current attribute\n"); - led_classdev_unregister(&led->cdev); - return res; - } - return 0; -} - static void lp5521_unregister_leds(struct lp5521_chip *chip) { int i; @@ -758,6 +709,7 @@ static struct lp55xx_device_config lp5521_cfg = { .addr = LP5521_REG_ENABLE, .val = LP5521_ENABLE_DEFAULT, }, + .max_channel = LP5521_MAX_LEDS, .post_init_device = lp5521_post_init_device, }; diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 41ac502ff82..ca2f8134909 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -94,7 +94,7 @@ #define LP5523_PROGRAM_PAGES 6 #define LP5523_ADC_SHORTCIRC_LIM 80 -#define LP5523_LEDS 9 +#define LP5523_MAX_LEDS 9 #define LP5523_ENGINES 3 #define LP5523_ENG_MASK_BASE 0x30 /* 00110000 */ @@ -136,7 +136,7 @@ struct lp5523_chip { struct mutex lock; /* Serialize control */ struct i2c_client *client; struct lp5523_engine engines[LP5523_ENGINES]; - struct lp5523_led leds[LP5523_LEDS]; + struct lp5523_led leds[LP5523_MAX_LEDS]; struct lp5523_platform_data *pdata; u8 num_channels; u8 num_leds; @@ -285,7 +285,7 @@ static int lp5523_mux_parse(const char *buf, u16 *mux, size_t len) int i; u16 tmp_mux = 0; - len = min_t(int, len, LP5523_LEDS); + len = min_t(int, len, LP5523_MAX_LEDS); for (i = 0; i < len; i++) { switch (buf[i]) { case '1': @@ -308,7 +308,7 @@ static int lp5523_mux_parse(const char *buf, u16 *mux, size_t len) static void lp5523_mux_to_array(u16 led_mux, char *array) { int i, pos = 0; - for (i = 0; i < LP5523_LEDS; i++) + for (i = 0; i < LP5523_MAX_LEDS; i++) pos += sprintf(array + pos, "%x", LED_ACTIVE(led_mux, i)); array[pos] = '\0'; @@ -324,7 +324,7 @@ static ssize_t show_engine_leds(struct device *dev, { struct i2c_client *client = to_i2c_client(dev); struct lp5523_chip *chip = i2c_get_clientdata(client); - char mux[LP5523_LEDS + 1]; + char mux[LP5523_MAX_LEDS + 1]; lp5523_mux_to_array(chip->engines[nr - 1].led_mux, mux); @@ -417,7 +417,7 @@ static ssize_t lp5523_selftest(struct device *dev, vdd--; /* There may be some fluctuation in measurement */ - for (i = 0; i < LP5523_LEDS; i++) { + for (i = 0; i < LP5523_MAX_LEDS; i++) { /* Skip non-existing channels */ if (chip->pdata->led_config[i].led_current == 0) continue; @@ -773,55 +773,6 @@ static void lp5523_set_mode(struct lp5523_engine *engine, u8 mode) /*--------------------------------------------------------------*/ /* Probe, Attach, Remove */ /*--------------------------------------------------------------*/ -static int lp5523_init_led(struct lp5523_led *led, struct device *dev, - int chan, struct lp5523_platform_data *pdata, - const char *chip_name) -{ - char name[32]; - int res; - - if (chan >= LP5523_LEDS) - return -EINVAL; - - if (pdata->led_config[chan].led_current) { - led->led_current = pdata->led_config[chan].led_current; - led->max_current = pdata->led_config[chan].max_current; - led->chan_nr = pdata->led_config[chan].chan_nr; - - if (led->chan_nr >= LP5523_LEDS) { - dev_err(dev, "Use channel numbers between 0 and %d\n", - LP5523_LEDS - 1); - return -EINVAL; - } - - if (pdata->led_config[chan].name) { - led->cdev.name = pdata->led_config[chan].name; - } else { - snprintf(name, sizeof(name), "%s:channel%d", - pdata->label ? : chip_name, chan); - led->cdev.name = name; - } - - led->cdev.brightness_set = lp5523_set_brightness; - res = led_classdev_register(dev, &led->cdev); - if (res < 0) { - dev_err(dev, "couldn't register led on channel %d\n", - chan); - return res; - } - res = sysfs_create_group(&led->cdev.dev->kobj, - &lp5523_led_attribute_group); - if (res < 0) { - dev_err(dev, "couldn't register current attribute\n"); - led_classdev_unregister(&led->cdev); - return res; - } - } else { - led->led_current = 0; - } - return 0; -} - static void lp5523_unregister_leds(struct lp5523_chip *chip) { int i; @@ -842,6 +793,7 @@ static struct lp55xx_device_config lp5523_cfg = { .addr = LP5523_REG_ENABLE, .val = LP5523_ENABLE, }, + .max_channel = LP5523_MAX_LEDS, .post_init_device = lp5523_post_init_device, }; diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index 1fab1d1c450..75ab1c3c03e 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -63,9 +63,75 @@ static int lp55xx_post_init_device(struct lp55xx_chip *chip) return cfg->post_init_device(chip); } +static struct attribute *lp55xx_led_attributes[] = { + NULL, +}; + +static struct attribute_group lp55xx_led_attr_group = { + .attrs = lp55xx_led_attributes +}; + +static void lp55xx_set_brightness(struct led_classdev *cdev, + enum led_brightness brightness) +{ +} + static int lp55xx_init_led(struct lp55xx_led *led, struct lp55xx_chip *chip, int chan) { + struct lp55xx_platform_data *pdata = chip->pdata; + struct lp55xx_device_config *cfg = chip->cfg; + struct device *dev = &chip->cl->dev; + char name[32]; + int ret; + int max_channel = cfg->max_channel; + + if (chan >= max_channel) { + dev_err(dev, "invalid channel: %d / %d\n", chan, max_channel); + return -EINVAL; + } + + if (pdata->led_config[chan].led_current == 0) + return 0; + + led->led_current = pdata->led_config[chan].led_current; + led->max_current = pdata->led_config[chan].max_current; + led->chan_nr = pdata->led_config[chan].chan_nr; + + if (led->chan_nr >= max_channel) { + dev_err(dev, "Use channel numbers between 0 and %d\n", + max_channel - 1); + return -EINVAL; + } + + led->cdev.brightness_set = lp55xx_set_brightness; + + if (pdata->led_config[chan].name) { + led->cdev.name = pdata->led_config[chan].name; + } else { + snprintf(name, sizeof(name), "%s:channel%d", + pdata->label ? : chip->cl->name, chan); + led->cdev.name = name; + } + + /* + * register led class device for each channel and + * add device attributes + */ + + ret = led_classdev_register(dev, &led->cdev); + if (ret) { + dev_err(dev, "led register err: %d\n", ret); + return ret; + } + + ret = sysfs_create_group(&led->cdev.dev->kobj, &lp55xx_led_attr_group); + if (ret) { + dev_err(dev, "led sysfs err: %d\n", ret); + led_classdev_unregister(&led->cdev); + return ret; + } + return 0; } diff --git a/drivers/leds/leds-lp55xx-common.h b/drivers/leds/leds-lp55xx-common.h index 219780a2d4e..70d2bdf54b8 100644 --- a/drivers/leds/leds-lp55xx-common.h +++ b/drivers/leds/leds-lp55xx-common.h @@ -32,6 +32,7 @@ struct lp55xx_reg { * struct lp55xx_device_config * @reset : Chip specific reset command * @enable : Chip specific enable command + * @max_channel : Maximum number of channels * @post_init_device : Chip specific initialization code * @brightness_work_fn : Brightness work function * @set_led_current : LED current set function @@ -39,6 +40,7 @@ struct lp55xx_reg { struct lp55xx_device_config { const struct lp55xx_reg reset; const struct lp55xx_reg enable; + const int max_channel; /* define if the device has specific initialization process */ int (*post_init_device) (struct lp55xx_chip *chip); -- cgit v1.2.3 From a6e4679a09a0a2bcfa63074272fc9fb2a40f11ad Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:08:40 +0900 Subject: leds-lp55xx: use lp55xx_set_brightness() lp5521_set_brightness() and lp5523_set_brightness() are replaced with common function, lp55xx_set_brightness(). This function is invoked when the brightness of each LED channel is updated. LP5521 and LP5523 have different register address for the brightness control, so this work is done by chip specific brightness_work_fn(). lp5521/5523_led_brightness_work(): use lp55xx_led and lp55xx_chip data structure. use lp55xx write function. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 19 ++++--------------- drivers/leds/leds-lp5523.c | 23 ++++------------------- drivers/leds/leds-lp55xx-common.c | 9 +++++++++ 3 files changed, 17 insertions(+), 34 deletions(-) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index bda03049fb3..6efbb7ec0e2 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -151,8 +151,6 @@ static inline struct lp5521_chip *led_to_lp5521(struct lp5521_led *led) leds[led->id]); } -static void lp5521_led_brightness_work(struct work_struct *work); - static inline int lp5521_write(struct i2c_client *client, u8 reg, u8 value) { return i2c_smbus_write_byte_data(client, reg, value); @@ -303,24 +301,14 @@ static int lp5521_run_selftest(struct lp5521_chip *chip, char *buf) return 0; } -static void lp5521_set_brightness(struct led_classdev *cdev, - enum led_brightness brightness) -{ - struct lp5521_led *led = cdev_to_led(cdev); - led->brightness = (u8)brightness; - schedule_work(&led->brightness_work); -} - static void lp5521_led_brightness_work(struct work_struct *work) { - struct lp5521_led *led = container_of(work, - struct lp5521_led, + struct lp55xx_led *led = container_of(work, struct lp55xx_led, brightness_work); - struct lp5521_chip *chip = led_to_lp5521(led); - struct i2c_client *client = chip->client; + struct lp55xx_chip *chip = led->chip; mutex_lock(&chip->lock); - lp5521_write(client, LP5521_REG_LED_PWM_BASE + led->chan_nr, + lp55xx_write(chip, LP5521_REG_LED_PWM_BASE + led->chan_nr, led->brightness); mutex_unlock(&chip->lock); } @@ -711,6 +699,7 @@ static struct lp55xx_device_config lp5521_cfg = { }, .max_channel = LP5521_MAX_LEDS, .post_init_device = lp5521_post_init_device, + .brightness_work_fn = lp5521_led_brightness_work, }; static int lp5521_probe(struct i2c_client *client, diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index ca2f8134909..43db2429616 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -163,8 +163,6 @@ static void lp5523_set_mode(struct lp5523_engine *engine, u8 mode); static int lp5523_set_engine_mode(struct lp5523_engine *engine, u8 mode); static int lp5523_load_program(struct lp5523_engine *engine, const u8 *pattern); -static void lp5523_led_brightness_work(struct work_struct *work); - static int lp5523_write(struct i2c_client *client, u8 reg, u8 value) { return i2c_smbus_write_byte_data(client, reg, value); @@ -468,29 +466,15 @@ release_lock: return pos; } -static void lp5523_set_brightness(struct led_classdev *cdev, - enum led_brightness brightness) -{ - struct lp5523_led *led = cdev_to_led(cdev); - - led->brightness = (u8)brightness; - - schedule_work(&led->brightness_work); -} - static void lp5523_led_brightness_work(struct work_struct *work) { - struct lp5523_led *led = container_of(work, - struct lp5523_led, + struct lp55xx_led *led = container_of(work, struct lp55xx_led, brightness_work); - struct lp5523_chip *chip = led_to_lp5523(led); - struct i2c_client *client = chip->client; + struct lp55xx_chip *chip = led->chip; mutex_lock(&chip->lock); - - lp5523_write(client, LP5523_REG_LED_PWM_BASE + led->chan_nr, + lp55xx_write(chip, LP5523_REG_LED_PWM_BASE + led->chan_nr, led->brightness); - mutex_unlock(&chip->lock); } @@ -795,6 +779,7 @@ static struct lp55xx_device_config lp5523_cfg = { }, .max_channel = LP5523_MAX_LEDS, .post_init_device = lp5523_post_init_device, + .brightness_work_fn = lp5523_led_brightness_work, }; static int lp5523_probe(struct i2c_client *client, diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index 75ab1c3c03e..8244d78447f 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -20,6 +20,11 @@ #include "leds-lp55xx-common.h" +static struct lp55xx_led *cdev_to_lp55xx_led(struct led_classdev *cdev) +{ + return container_of(cdev, struct lp55xx_led, cdev); +} + static void lp55xx_reset_device(struct lp55xx_chip *chip) { struct lp55xx_device_config *cfg = chip->cfg; @@ -74,6 +79,10 @@ static struct attribute_group lp55xx_led_attr_group = { static void lp55xx_set_brightness(struct led_classdev *cdev, enum led_brightness brightness) { + struct lp55xx_led *led = cdev_to_lp55xx_led(cdev); + + led->brightness = (u8)brightness; + schedule_work(&led->brightness_work); } static int lp55xx_init_led(struct lp55xx_led *led, -- cgit v1.2.3 From a96bfa135ddc8045166fc6311ce4d21bfcb8d13d Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:09:32 +0900 Subject: leds-lp55xx: provide common LED current setting LED current is configurable via the sysfs. Max current is a read-only attribute. These attributes code can be shared in lp55xx common driver. Device attributes: 'led_current' and 'max_current' move to lp55xx common driver Replaced functions: show_max_current() => lp55xx_show_max_current() show_current() => lp55xx_show_current() store_current() => lp55xx_store_current() LED setting function: set_led_current() Current registers are device specific, so configurable function is added in each driver. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 74 +++++---------------------------------- drivers/leds/leds-lp5523.c | 69 +++++------------------------------- drivers/leds/leds-lp55xx-common.c | 53 ++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 127 deletions(-) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index 6efbb7ec0e2..7133af824b5 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -134,6 +134,13 @@ static inline void lp5521_wait_enable_done(void) usleep_range(500, 600); } +static void lp5521_set_led_current(struct lp55xx_led *led, u8 led_current) +{ + led->led_current = led_current; + lp55xx_write(led->chip, LP5521_REG_LED_CURRENT_BASE + led->chan_nr, + led_current); +} + static inline struct lp5521_led *cdev_to_led(struct led_classdev *cdev) { return container_of(cdev, struct lp5521_led, cdev); @@ -229,13 +236,6 @@ static int lp5521_load_program(struct lp5521_engine *eng, const u8 *pattern) return lp5521_write(client, LP5521_REG_OP_MODE, mode); } -static int lp5521_set_led_current(struct lp5521_chip *chip, int led, u8 curr) -{ - return lp5521_write(chip->client, - LP5521_REG_LED_CURRENT_BASE + chip->leds[led].chan_nr, - curr); -} - static int lp5521_post_init_device(struct lp55xx_chip *chip) { int ret; @@ -462,54 +462,6 @@ store_mode(1) store_mode(2) store_mode(3) -static ssize_t show_max_current(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct led_classdev *led_cdev = dev_get_drvdata(dev); - struct lp5521_led *led = cdev_to_led(led_cdev); - - return sprintf(buf, "%d\n", led->max_current); -} - -static ssize_t show_current(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct led_classdev *led_cdev = dev_get_drvdata(dev); - struct lp5521_led *led = cdev_to_led(led_cdev); - - return sprintf(buf, "%d\n", led->led_current); -} - -static ssize_t store_current(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len) -{ - struct led_classdev *led_cdev = dev_get_drvdata(dev); - struct lp5521_led *led = cdev_to_led(led_cdev); - struct lp5521_chip *chip = led_to_lp5521(led); - ssize_t ret; - unsigned long curr; - - if (kstrtoul(buf, 0, &curr)) - return -EINVAL; - - if (curr > led->max_current) - return -EINVAL; - - mutex_lock(&chip->lock); - ret = lp5521_set_led_current(chip, led->id, curr); - mutex_unlock(&chip->lock); - - if (ret < 0) - return ret; - - led->led_current = (u8)curr; - - return len; -} - static ssize_t lp5521_selftest(struct device *dev, struct device_attribute *attr, char *buf) @@ -615,18 +567,7 @@ static ssize_t store_led_pattern(struct device *dev, return len; } -/* led class device attributes */ -static DEVICE_ATTR(led_current, S_IRUGO | S_IWUSR, show_current, store_current); -static DEVICE_ATTR(max_current, S_IRUGO , show_max_current, NULL); - -static struct attribute *lp5521_led_attributes[] = { - &dev_attr_led_current.attr, - &dev_attr_max_current.attr, - NULL, -}; - static struct attribute_group lp5521_led_attribute_group = { - .attrs = lp5521_led_attributes }; /* device attributes */ @@ -700,6 +641,7 @@ static struct lp55xx_device_config lp5521_cfg = { .max_channel = LP5521_MAX_LEDS, .post_init_device = lp5521_post_init_device, .brightness_work_fn = lp5521_led_brightness_work, + .set_led_current = lp5521_set_led_current, }; static int lp5521_probe(struct i2c_client *client, diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 43db2429616..dfb33542056 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -142,6 +142,13 @@ struct lp5523_chip { u8 num_leds; }; +static void lp5523_set_led_current(struct lp55xx_led *led, u8 led_current) +{ + led->led_current = led_current; + lp55xx_write(led->chip, LP5523_REG_LED_CURRENT_BASE + led->chan_nr, + led_current); +} + static inline struct lp5523_led *cdev_to_led(struct led_classdev *cdev) { return container_of(cdev, struct lp5523_led, cdev); @@ -602,68 +609,7 @@ store_mode(1) store_mode(2) store_mode(3) -static ssize_t show_max_current(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct led_classdev *led_cdev = dev_get_drvdata(dev); - struct lp5523_led *led = cdev_to_led(led_cdev); - - return sprintf(buf, "%d\n", led->max_current); -} - -static ssize_t show_current(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct led_classdev *led_cdev = dev_get_drvdata(dev); - struct lp5523_led *led = cdev_to_led(led_cdev); - - return sprintf(buf, "%d\n", led->led_current); -} - -static ssize_t store_current(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len) -{ - struct led_classdev *led_cdev = dev_get_drvdata(dev); - struct lp5523_led *led = cdev_to_led(led_cdev); - struct lp5523_chip *chip = led_to_lp5523(led); - ssize_t ret; - unsigned long curr; - - if (kstrtoul(buf, 0, &curr)) - return -EINVAL; - - if (curr > led->max_current) - return -EINVAL; - - mutex_lock(&chip->lock); - ret = lp5523_write(chip->client, - LP5523_REG_LED_CURRENT_BASE + led->chan_nr, - (u8)curr); - mutex_unlock(&chip->lock); - - if (ret < 0) - return ret; - - led->led_current = (u8)curr; - - return len; -} - -/* led class device attributes */ -static DEVICE_ATTR(led_current, S_IRUGO | S_IWUSR, show_current, store_current); -static DEVICE_ATTR(max_current, S_IRUGO , show_max_current, NULL); - -static struct attribute *lp5523_led_attributes[] = { - &dev_attr_led_current.attr, - &dev_attr_max_current.attr, - NULL, -}; - static struct attribute_group lp5523_led_attribute_group = { - .attrs = lp5523_led_attributes }; /* device attributes */ @@ -780,6 +726,7 @@ static struct lp55xx_device_config lp5523_cfg = { .max_channel = LP5523_MAX_LEDS, .post_init_device = lp5523_post_init_device, .brightness_work_fn = lp5523_led_brightness_work, + .set_led_current = lp5523_set_led_current, }; static int lp5523_probe(struct i2c_client *client, diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index 8244d78447f..6b3d03709f5 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -25,6 +25,11 @@ static struct lp55xx_led *cdev_to_lp55xx_led(struct led_classdev *cdev) return container_of(cdev, struct lp55xx_led, cdev); } +static struct lp55xx_led *dev_to_lp55xx_led(struct device *dev) +{ + return cdev_to_lp55xx_led(dev_get_drvdata(dev)); +} + static void lp55xx_reset_device(struct lp55xx_chip *chip) { struct lp55xx_device_config *cfg = chip->cfg; @@ -68,7 +73,55 @@ static int lp55xx_post_init_device(struct lp55xx_chip *chip) return cfg->post_init_device(chip); } +static ssize_t lp55xx_show_current(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct lp55xx_led *led = dev_to_lp55xx_led(dev); + + return sprintf(buf, "%d\n", led->led_current); +} + +static ssize_t lp55xx_store_current(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct lp55xx_led *led = dev_to_lp55xx_led(dev); + struct lp55xx_chip *chip = led->chip; + unsigned long curr; + + if (kstrtoul(buf, 0, &curr)) + return -EINVAL; + + if (curr > led->max_current) + return -EINVAL; + + if (!chip->cfg->set_led_current) + return len; + + mutex_lock(&chip->lock); + chip->cfg->set_led_current(led, (u8)curr); + mutex_unlock(&chip->lock); + + return len; +} + +static ssize_t lp55xx_show_max_current(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct lp55xx_led *led = dev_to_lp55xx_led(dev); + + return sprintf(buf, "%d\n", led->max_current); +} + +static DEVICE_ATTR(led_current, S_IRUGO | S_IWUSR, lp55xx_show_current, + lp55xx_store_current); +static DEVICE_ATTR(max_current, S_IRUGO , lp55xx_show_max_current, NULL); + static struct attribute *lp55xx_led_attributes[] = { + &dev_attr_led_current.attr, + &dev_attr_max_current.attr, NULL, }; -- cgit v1.2.3 From c3a68ebfcd22abc186f2328149732c801449b297 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:11:18 +0900 Subject: leds-lp55xx: use lp55xx_unregister_leds() To unregister led class devices and sysfs attributes, LP5521 and LP5523 have each driver function. This patch makes both drivers simple using common driver function, lp55xx_unregister_leds(). And some unused variables are removed. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 24 ++---------------------- drivers/leds/leds-lp5523.c | 25 ++----------------------- drivers/leds/leds-lp55xx-common.c | 16 ++++++++++++++++ drivers/leds/leds-lp55xx-common.h | 2 ++ 4 files changed, 22 insertions(+), 45 deletions(-) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index 7133af824b5..cec252eae71 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -567,9 +567,6 @@ static ssize_t store_led_pattern(struct device *dev, return len; } -static struct attribute_group lp5521_led_attribute_group = { -}; - /* device attributes */ static DEVICE_ATTR(engine1_mode, S_IRUGO | S_IWUSR, show_engine1_mode, store_engine1_mode); @@ -607,25 +604,9 @@ static int lp5521_register_sysfs(struct i2c_client *client) static void lp5521_unregister_sysfs(struct i2c_client *client) { - struct lp5521_chip *chip = i2c_get_clientdata(client); struct device *dev = &client->dev; - int i; sysfs_remove_group(&dev->kobj, &lp5521_group); - - for (i = 0; i < chip->num_leds; i++) - sysfs_remove_group(&chip->leds[i].cdev.dev->kobj, - &lp5521_led_attribute_group); -} - -static void lp5521_unregister_leds(struct lp5521_chip *chip) -{ - int i; - - for (i = 0; i < chip->num_leds; i++) { - led_classdev_unregister(&chip->leds[i].cdev); - cancel_work_sync(&chip->leds[i].brightness_work); - } } /* Chip specific configurations */ @@ -647,7 +628,6 @@ static struct lp55xx_device_config lp5521_cfg = { static int lp5521_probe(struct i2c_client *client, const struct i2c_device_id *id) { - struct lp5521_chip *old_chip = NULL; int ret; struct lp55xx_chip *chip; struct lp55xx_led *led; @@ -692,7 +672,7 @@ static int lp5521_probe(struct i2c_client *client, } return ret; fail2: - lp5521_unregister_leds(old_chip); + lp55xx_unregister_leds(led, chip); err_register_leds: lp55xx_deinit_device(chip); err_init: @@ -708,7 +688,7 @@ static int lp5521_remove(struct i2c_client *client) lp5521_run_led_pattern(PATTERN_OFF, old_chip); lp5521_unregister_sysfs(client); - lp5521_unregister_leds(old_chip); + lp55xx_unregister_leds(led, chip); lp55xx_deinit_device(chip); return 0; diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index dfb33542056..70630156eff 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -609,9 +609,6 @@ store_mode(1) store_mode(2) store_mode(3) -static struct attribute_group lp5523_led_attribute_group = { -}; - /* device attributes */ static DEVICE_ATTR(engine1_mode, S_IRUGO | S_IWUSR, show_engine1_mode, store_engine1_mode); @@ -662,15 +659,9 @@ static int lp5523_register_sysfs(struct i2c_client *client) static void lp5523_unregister_sysfs(struct i2c_client *client) { - struct lp5523_chip *chip = i2c_get_clientdata(client); struct device *dev = &client->dev; - int i; sysfs_remove_group(&dev->kobj, &lp5523_group); - - for (i = 0; i < chip->num_leds; i++) - sysfs_remove_group(&chip->leds[i].cdev.dev->kobj, - &lp5523_led_attribute_group); } /*--------------------------------------------------------------*/ @@ -703,16 +694,6 @@ static void lp5523_set_mode(struct lp5523_engine *engine, u8 mode) /*--------------------------------------------------------------*/ /* Probe, Attach, Remove */ /*--------------------------------------------------------------*/ -static void lp5523_unregister_leds(struct lp5523_chip *chip) -{ - int i; - - for (i = 0; i < chip->num_leds; i++) { - led_classdev_unregister(&chip->leds[i].cdev); - flush_work(&chip->leds[i].brightness_work); - } -} - /* Chip specific configurations */ static struct lp55xx_device_config lp5523_cfg = { .reset = { @@ -732,7 +713,6 @@ static struct lp55xx_device_config lp5523_cfg = { static int lp5523_probe(struct i2c_client *client, const struct i2c_device_id *id) { - struct lp5523_chip *old_chip = NULL; int ret; struct lp55xx_chip *chip; struct lp55xx_led *led; @@ -777,7 +757,7 @@ static int lp5523_probe(struct i2c_client *client, } return ret; fail2: - lp5523_unregister_leds(old_chip); + lp55xx_unregister_leds(led, chip); err_register_leds: lp55xx_deinit_device(chip); err_init: @@ -786,7 +766,6 @@ err_init: static int lp5523_remove(struct i2c_client *client) { - struct lp5523_chip *old_chip = i2c_get_clientdata(client); struct lp55xx_led *led = i2c_get_clientdata(client); struct lp55xx_chip *chip = led->chip; @@ -795,7 +774,7 @@ static int lp5523_remove(struct i2c_client *client) lp5523_unregister_sysfs(client); - lp5523_unregister_leds(old_chip); + lp55xx_unregister_leds(led, chip); lp55xx_deinit_device(chip); return 0; diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index 6b3d03709f5..dcd64f5285e 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -353,6 +353,22 @@ err_init_led: } EXPORT_SYMBOL_GPL(lp55xx_register_leds); +void lp55xx_unregister_leds(struct lp55xx_led *led, struct lp55xx_chip *chip) +{ + int i; + struct lp55xx_led *each; + struct kobject *kobj; + + for (i = 0; i < chip->num_leds; i++) { + each = led + i; + kobj = &led->cdev.dev->kobj; + sysfs_remove_group(kobj, &lp55xx_led_attr_group); + led_classdev_unregister(&each->cdev); + flush_work(&each->brightness_work); + } +} +EXPORT_SYMBOL_GPL(lp55xx_unregister_leds); + MODULE_AUTHOR("Milo Kim "); MODULE_DESCRIPTION("LP55xx Common Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/leds/leds-lp55xx-common.h b/drivers/leds/leds-lp55xx-common.h index 70d2bdf54b8..32d96828cbc 100644 --- a/drivers/leds/leds-lp55xx-common.h +++ b/drivers/leds/leds-lp55xx-common.h @@ -101,4 +101,6 @@ extern void lp55xx_deinit_device(struct lp55xx_chip *chip); /* common LED class device functions */ extern int lp55xx_register_leds(struct lp55xx_led *led, struct lp55xx_chip *chip); +extern void lp55xx_unregister_leds(struct lp55xx_led *led, + struct lp55xx_chip *chip); #endif /* _LEDS_LP55XX_COMMON_H */ -- cgit v1.2.3 From d9067d28461cb2e817cacb84c727959cbd57d247 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:12:47 +0900 Subject: leds-lp55xx: fix error condition in lp55xx_register_leds() Use lp55xx_unregister_leds() rather than duplicate code. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp55xx-common.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index dcd64f5285e..cd19027895e 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -344,11 +344,7 @@ int lp55xx_register_leds(struct lp55xx_led *led, struct lp55xx_chip *chip) return 0; err_init_led: - for (i = 0; i < chip->num_leds; i++) { - each = led + i; - led_classdev_unregister(&each->cdev); - flush_work(&each->brightness_work); - } + lp55xx_unregister_leds(led, chip); return ret; } EXPORT_SYMBOL_GPL(lp55xx_register_leds); -- cgit v1.2.3 From b3b6f8119d752c969c6394314dc7ab80e6611111 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:15:27 +0900 Subject: leds-lp55xx: add new lp55xx_register_sysfs() for the firmware interface LP55xx family chips have internal program memory which run various patterns. Using this memory, LEDs continue on blinking/dimming without continuous I2C commands. That means the I2C HOST can be entered into sleep once the memory is updated. An application can get hex data from a file and write them into the program memory through the I2C. This is general firwmare interface. This patch is the initial step for adding the firmware interface. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp55xx-common.c | 16 ++++++++++++++++ drivers/leds/leds-lp55xx-common.h | 4 ++++ 2 files changed, 20 insertions(+) diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index cd19027895e..98407ca45e4 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -197,6 +197,14 @@ static int lp55xx_init_led(struct lp55xx_led *led, return 0; } +static struct attribute *lp55xx_engine_attributes[] = { + NULL, +}; + +static const struct attribute_group lp55xx_engine_attr_group = { + .attrs = lp55xx_engine_attributes, +}; + int lp55xx_write(struct lp55xx_chip *chip, u8 reg, u8 val) { return i2c_smbus_write_byte_data(chip->cl, reg, val); @@ -365,6 +373,14 @@ void lp55xx_unregister_leds(struct lp55xx_led *led, struct lp55xx_chip *chip) } EXPORT_SYMBOL_GPL(lp55xx_unregister_leds); +int lp55xx_register_sysfs(struct lp55xx_chip *chip) +{ + struct device *dev = &chip->cl->dev; + + return sysfs_create_group(&dev->kobj, &lp55xx_engine_attr_group); +} +EXPORT_SYMBOL_GPL(lp55xx_register_sysfs); + MODULE_AUTHOR("Milo Kim "); MODULE_DESCRIPTION("LP55xx Common Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/leds/leds-lp55xx-common.h b/drivers/leds/leds-lp55xx-common.h index 32d96828cbc..d0be837643f 100644 --- a/drivers/leds/leds-lp55xx-common.h +++ b/drivers/leds/leds-lp55xx-common.h @@ -103,4 +103,8 @@ extern int lp55xx_register_leds(struct lp55xx_led *led, struct lp55xx_chip *chip); extern void lp55xx_unregister_leds(struct lp55xx_led *led, struct lp55xx_chip *chip); + +/* common device attributes functions */ +extern int lp55xx_register_sysfs(struct lp55xx_chip *chip); + #endif /* _LEDS_LP55XX_COMMON_H */ -- cgit v1.2.3 From 10c06d178df11b0b2b746321a80ea14241997127 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:17:20 +0900 Subject: leds-lp55xx: support firmware interface This patch provides additional device attributes which enable loading the firmware. ('select_engine' and 'run_engine') To run a LED pattern, two parts of driver should be enabled. Common features : lp55xx-common =============================== Firmware interface for loading LED patterns Chip specific features : leds-lp5521, leds-lp5523 ================================================= Register addresses for loading firmware data Register addresses for running selected engine Pattern programming sequence ============================ LP55xx chips have three program engines. To load and run a LED pattern, the programming sequence is as follows. (1) Select an engine number (1/2/3) (2) Set engine mode to load (3) Write pattern data into selected area (4) Set engine mode to run This sequence is almost same as the firmware interface. (1) Select an engine number : 'select_engine' dev attribute (2) Mode change to load : 'loading' of firmware class (3) Write pattern data into selected area : 'data' of firmware class (4) Mode change to run : 'run_engine' dev attribute (1) and (4) are device specific features which provide callback functions (2) and (3) are common features. For example, echo 1 or 2 or 3 > /sys/bus/i2c/devices/xxxx/select_engine echo 1 > /sys/class/firmware/lp5521/loading echo "4000600040FF6000" > /sys/class/firmware/lp5521/data echo 0 > /sys/class/firmware/lp5521/loading echo 1 > /sys/bus/i2c/devices/xxxx/run_engine As soon as 'loading' is set to 0, registered callback is called. Inside the callback, the selected engine is loaded and memory is updated. To run programmed pattern, 'run_engine' attribute should be enabled. Device specific data structure ============================== o Firmware callback load selected engine and update program memory o Run engine change the engine mode o 'engine_idx' and firmware data, 'fw' Those are used in the driver internally with callback functions Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/Kconfig | 1 + drivers/leds/leds-lp55xx-common.c | 117 ++++++++++++++++++++++++++++++++++++++ drivers/leds/leds-lp55xx-common.h | 19 +++++++ 3 files changed, 137 insertions(+) diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 3d7822b3498..fc680a9ed84 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -196,6 +196,7 @@ config LEDS_LP3944 config LEDS_LP55XX_COMMON tristate "Common Driver for TI/National LP5521 and LP5523/55231" depends on LEDS_LP5521 || LEDS_LP5523 + select FW_LOADER help This option supports common operations for LP5521 and LP5523/55231 devices. diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index 98407ca45e4..578902ab604 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -13,6 +13,7 @@ */ #include +#include #include #include #include @@ -197,7 +198,123 @@ static int lp55xx_init_led(struct lp55xx_led *led, return 0; } +static void lp55xx_firmware_loaded(const struct firmware *fw, void *context) +{ + struct lp55xx_chip *chip = context; + struct device *dev = &chip->cl->dev; + + if (!fw) { + dev_err(dev, "firmware request failed\n"); + goto out; + } + + /* handling firmware data is chip dependent */ + mutex_lock(&chip->lock); + + chip->fw = fw; + if (chip->cfg->firmware_cb) + chip->cfg->firmware_cb(chip); + + mutex_unlock(&chip->lock); + +out: + /* firmware should be released for other channel use */ + release_firmware(chip->fw); +} + +static int lp55xx_request_firmware(struct lp55xx_chip *chip) +{ + const char *name = chip->cl->name; + struct device *dev = &chip->cl->dev; + + return request_firmware_nowait(THIS_MODULE, true, name, dev, + GFP_KERNEL, chip, lp55xx_firmware_loaded); +} + +static ssize_t lp55xx_show_engine_select(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev)); + struct lp55xx_chip *chip = led->chip; + + return sprintf(buf, "%d\n", chip->engine_idx); +} + +static ssize_t lp55xx_store_engine_select(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev)); + struct lp55xx_chip *chip = led->chip; + unsigned long val; + int ret; + + if (kstrtoul(buf, 0, &val)) + return -EINVAL; + + /* select the engine to be run */ + + switch (val) { + case LP55XX_ENGINE_1: + case LP55XX_ENGINE_2: + case LP55XX_ENGINE_3: + mutex_lock(&chip->lock); + chip->engine_idx = val; + ret = lp55xx_request_firmware(chip); + mutex_unlock(&chip->lock); + break; + default: + dev_err(dev, "%lu: invalid engine index. (1, 2, 3)\n", val); + return -EINVAL; + } + + if (ret) { + dev_err(dev, "request firmware err: %d\n", ret); + return ret; + } + + return len; +} + +static inline void lp55xx_run_engine(struct lp55xx_chip *chip, bool start) +{ + if (chip->cfg->run_engine) + chip->cfg->run_engine(chip, start); +} + +static ssize_t lp55xx_store_engine_run(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev)); + struct lp55xx_chip *chip = led->chip; + unsigned long val; + + if (kstrtoul(buf, 0, &val)) + return -EINVAL; + + /* run or stop the selected engine */ + + if (val <= 0) { + lp55xx_run_engine(chip, false); + return len; + } + + mutex_lock(&chip->lock); + lp55xx_run_engine(chip, true); + mutex_unlock(&chip->lock); + + return len; +} + +static DEVICE_ATTR(select_engine, S_IRUGO | S_IWUSR, + lp55xx_show_engine_select, lp55xx_store_engine_select); +static DEVICE_ATTR(run_engine, S_IWUSR, NULL, lp55xx_store_engine_run); + static struct attribute *lp55xx_engine_attributes[] = { + &dev_attr_select_engine.attr, + &dev_attr_run_engine.attr, NULL, }; diff --git a/drivers/leds/leds-lp55xx-common.h b/drivers/leds/leds-lp55xx-common.h index d0be837643f..8473abf9830 100644 --- a/drivers/leds/leds-lp55xx-common.h +++ b/drivers/leds/leds-lp55xx-common.h @@ -15,6 +15,13 @@ #ifndef _LEDS_LP55XX_COMMON_H #define _LEDS_LP55XX_COMMON_H +enum lp55xx_engine_index { + LP55XX_ENGINE_INVALID, + LP55XX_ENGINE_1, + LP55XX_ENGINE_2, + LP55XX_ENGINE_3, +}; + struct lp55xx_led; struct lp55xx_chip; @@ -36,6 +43,8 @@ struct lp55xx_reg { * @post_init_device : Chip specific initialization code * @brightness_work_fn : Brightness work function * @set_led_current : LED current set function + * @firmware_cb : Call function when the firmware is loaded + * @run_engine : Run internal engine for pattern */ struct lp55xx_device_config { const struct lp55xx_reg reset; @@ -50,6 +59,12 @@ struct lp55xx_device_config { /* current setting function */ void (*set_led_current) (struct lp55xx_led *led, u8 led_current); + + /* access program memory when the firmware is loaded */ + void (*firmware_cb)(struct lp55xx_chip *chip); + + /* used for running firmware LED patterns */ + void (*run_engine) (struct lp55xx_chip *chip, bool start); }; /* @@ -59,6 +74,8 @@ struct lp55xx_device_config { * @lock : Lock for user-space interface * @num_leds : Number of registered LEDs * @cfg : Device specific configuration data + * @engine_idx : Selected engine number + * @fw : Firmware data for running a LED pattern */ struct lp55xx_chip { struct i2c_client *cl; @@ -66,6 +83,8 @@ struct lp55xx_chip { struct mutex lock; /* lock for user-space interface */ int num_leds; struct lp55xx_device_config *cfg; + enum lp55xx_engine_index engine_idx; + const struct firmware *fw; }; /* -- cgit v1.2.3 From 9ce7cb170f97f83a78dc948bf7d25690f15e1328 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:18:10 +0900 Subject: leds-lp5521: use generic firmware interface LP55xx common driver provides generic firmware interface for running a LED pattern. LP5521 and LP5523 have many device attributes for running patterns. This patch cleans up those complex code. Removed device attributes: engine1_mode engine2_mode engine3_mode engine1_load engine2_load engine3_load led_pattern All device attributes and functions are replaced with two callback functions, 'firmware_cb' and 'run_engine'. New engine functions: lp5521_load/stop/run_engine(), lp5521_update_program_memory() and lp5521_wait_opmode_done() Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 414 ++++++++++++++++++--------------------------- 1 file changed, 163 insertions(+), 251 deletions(-) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index cec252eae71..89371e065c1 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -35,6 +35,7 @@ #include #include #include +#include #include "leds-lp55xx-common.h" @@ -101,12 +102,28 @@ /* Reset register value */ #define LP5521_RESET 0xFF -struct lp5521_engine { - int id; - u8 mode; - u8 prog_page; - u8 engine_mask; -}; +/* Program Memory Operations */ +#define LP5521_MODE_R_M 0x30 /* Operation Mode Register */ +#define LP5521_MODE_G_M 0x0C +#define LP5521_MODE_B_M 0x03 +#define LP5521_LOAD_R 0x10 +#define LP5521_LOAD_G 0x04 +#define LP5521_LOAD_B 0x01 + +#define LP5521_R_IS_LOADING(mode) \ + ((mode & LP5521_MODE_R_M) == LP5521_LOAD_R) +#define LP5521_G_IS_LOADING(mode) \ + ((mode & LP5521_MODE_G_M) == LP5521_LOAD_G) +#define LP5521_B_IS_LOADING(mode) \ + ((mode & LP5521_MODE_B_M) == LP5521_LOAD_B) + +#define LP5521_EXEC_R_M 0x30 /* Enable Register */ +#define LP5521_EXEC_G_M 0x0C +#define LP5521_EXEC_B_M 0x03 +#define LP5521_EXEC_M 0x3F +#define LP5521_RUN_R 0x20 +#define LP5521_RUN_G 0x08 +#define LP5521_RUN_B 0x02 struct lp5521_led { int id; @@ -122,12 +139,17 @@ struct lp5521_chip { struct lp5521_platform_data *pdata; struct mutex lock; /* Serialize control */ struct i2c_client *client; - struct lp5521_engine engines[LP5521_MAX_ENGINES]; struct lp5521_led leds[LP5521_MAX_LEDS]; u8 num_channels; u8 num_leds; }; +static inline void lp5521_wait_opmode_done(void) +{ + /* operation mode change needs to be longer than 153 us */ + usleep_range(200, 300); +} + static inline void lp5521_wait_enable_done(void) { /* it takes more 488 us to update ENABLE register */ @@ -141,23 +163,6 @@ static void lp5521_set_led_current(struct lp55xx_led *led, u8 led_current) led_current); } -static inline struct lp5521_led *cdev_to_led(struct led_classdev *cdev) -{ - return container_of(cdev, struct lp5521_led, cdev); -} - -static inline struct lp5521_chip *engine_to_lp5521(struct lp5521_engine *engine) -{ - return container_of(engine, struct lp5521_chip, - engines[engine->id - 1]); -} - -static inline struct lp5521_chip *led_to_lp5521(struct lp5521_led *led) -{ - return container_of(led, struct lp5521_chip, - leds[led->id]); -} - static inline int lp5521_write(struct i2c_client *client, u8 reg, u8 value) { return i2c_smbus_write_byte_data(client, reg, value); @@ -175,65 +180,153 @@ static int lp5521_read(struct i2c_client *client, u8 reg, u8 *buf) return 0; } -static int lp5521_set_engine_mode(struct lp5521_engine *engine, u8 mode) +static void lp5521_load_engine(struct lp55xx_chip *chip) { - struct lp5521_chip *chip = engine_to_lp5521(engine); - struct i2c_client *client = chip->client; - int ret; - u8 engine_state; + enum lp55xx_engine_index idx = chip->engine_idx; + u8 mask[] = { + [LP55XX_ENGINE_1] = LP5521_MODE_R_M, + [LP55XX_ENGINE_2] = LP5521_MODE_G_M, + [LP55XX_ENGINE_3] = LP5521_MODE_B_M, + }; - /* Only transition between RUN and DIRECT mode are handled here */ - if (mode == LP5521_CMD_LOAD) - return 0; + u8 val[] = { + [LP55XX_ENGINE_1] = LP5521_LOAD_R, + [LP55XX_ENGINE_2] = LP5521_LOAD_G, + [LP55XX_ENGINE_3] = LP5521_LOAD_B, + }; - if (mode == LP5521_CMD_DISABLED) - mode = LP5521_CMD_DIRECT; + lp55xx_update_bits(chip, LP5521_REG_OP_MODE, mask[idx], val[idx]); - ret = lp5521_read(client, LP5521_REG_OP_MODE, &engine_state); - if (ret < 0) - return ret; + lp5521_wait_opmode_done(); +} - /* set mode only for this engine */ - engine_state &= ~(engine->engine_mask); - mode &= engine->engine_mask; - engine_state |= mode; - return lp5521_write(client, LP5521_REG_OP_MODE, engine_state); +static void lp5521_stop_engine(struct lp55xx_chip *chip) +{ + lp55xx_write(chip, LP5521_REG_OP_MODE, 0); + lp5521_wait_opmode_done(); } -static int lp5521_load_program(struct lp5521_engine *eng, const u8 *pattern) +static void lp5521_run_engine(struct lp55xx_chip *chip, bool start) { - struct lp5521_chip *chip = engine_to_lp5521(eng); - struct i2c_client *client = chip->client; int ret; - int addr; u8 mode; + u8 exec; - /* move current engine to direct mode and remember the state */ - ret = lp5521_set_engine_mode(eng, LP5521_CMD_DIRECT); + /* stop engine */ + if (!start) { + lp5521_stop_engine(chip); + lp55xx_write(chip, LP5521_REG_OP_MODE, LP5521_CMD_DIRECT); + lp5521_wait_opmode_done(); + return; + } + + /* + * To run the engine, + * operation mode and enable register should updated at the same time + */ + + ret = lp55xx_read(chip, LP5521_REG_OP_MODE, &mode); if (ret) - return ret; + return; - /* Mode change requires min 500 us delay. 1 - 2 ms with margin */ - usleep_range(1000, 2000); - ret = lp5521_read(client, LP5521_REG_OP_MODE, &mode); + ret = lp55xx_read(chip, LP5521_REG_ENABLE, &exec); if (ret) - return ret; + return; + + /* change operation mode to RUN only when each engine is loading */ + if (LP5521_R_IS_LOADING(mode)) { + mode = (mode & ~LP5521_MODE_R_M) | LP5521_RUN_R; + exec = (exec & ~LP5521_EXEC_R_M) | LP5521_RUN_R; + } + + if (LP5521_G_IS_LOADING(mode)) { + mode = (mode & ~LP5521_MODE_G_M) | LP5521_RUN_G; + exec = (exec & ~LP5521_EXEC_G_M) | LP5521_RUN_G; + } + + if (LP5521_B_IS_LOADING(mode)) { + mode = (mode & ~LP5521_MODE_B_M) | LP5521_RUN_B; + exec = (exec & ~LP5521_EXEC_B_M) | LP5521_RUN_B; + } + + lp55xx_write(chip, LP5521_REG_OP_MODE, mode); + lp5521_wait_opmode_done(); - /* For loading, all the engines to load mode */ - lp5521_write(client, LP5521_REG_OP_MODE, LP5521_CMD_DIRECT); - /* Mode change requires min 500 us delay. 1 - 2 ms with margin */ - usleep_range(1000, 2000); - lp5521_write(client, LP5521_REG_OP_MODE, LP5521_CMD_LOAD); - /* Mode change requires min 500 us delay. 1 - 2 ms with margin */ - usleep_range(1000, 2000); - - addr = LP5521_PROG_MEM_BASE + eng->prog_page * LP5521_PROG_MEM_SIZE; - i2c_smbus_write_i2c_block_data(client, - addr, - LP5521_PROG_MEM_SIZE, - pattern); - - return lp5521_write(client, LP5521_REG_OP_MODE, mode); + lp55xx_update_bits(chip, LP5521_REG_ENABLE, LP5521_EXEC_M, exec); + lp5521_wait_enable_done(); +} + +static int lp5521_update_program_memory(struct lp55xx_chip *chip, + const u8 *data, size_t size) +{ + enum lp55xx_engine_index idx = chip->engine_idx; + u8 pattern[LP5521_PROGRAM_LENGTH] = {0}; + u8 addr[] = { + [LP55XX_ENGINE_1] = LP5521_REG_R_PROG_MEM, + [LP55XX_ENGINE_2] = LP5521_REG_G_PROG_MEM, + [LP55XX_ENGINE_3] = LP5521_REG_B_PROG_MEM, + }; + unsigned cmd; + char c[3]; + int program_size; + int nrchars; + int offset = 0; + int ret; + int i; + + /* clear program memory before updating */ + for (i = 0; i < LP5521_PROGRAM_LENGTH; i++) + lp55xx_write(chip, addr[idx] + i, 0); + + i = 0; + while ((offset < size - 1) && (i < LP5521_PROGRAM_LENGTH)) { + /* separate sscanfs because length is working only for %s */ + ret = sscanf(data + offset, "%2s%n ", c, &nrchars); + if (ret != 1) + goto err; + + ret = sscanf(c, "%2x", &cmd); + if (ret != 1) + goto err; + + pattern[i] = (u8)cmd; + offset += nrchars; + i++; + } + + /* Each instruction is 16bit long. Check that length is even */ + if (i % 2) + goto err; + + program_size = i; + for (i = 0; i < program_size; i++) + lp55xx_write(chip, addr[idx] + i, pattern[i]); + + return 0; + +err: + dev_err(&chip->cl->dev, "wrong pattern format\n"); + return -EINVAL; +} + +static void lp5521_firmware_loaded(struct lp55xx_chip *chip) +{ + const struct firmware *fw = chip->fw; + + if (fw->size > LP5521_PROGRAM_LENGTH) { + dev_err(&chip->cl->dev, "firmware data size overflow: %zu\n", + fw->size); + return; + } + + /* + * Program momery sequence + * 1) set engine mode to "LOAD" + * 2) write firmware data into program memory + */ + + lp5521_load_engine(chip); + lp5521_update_program_memory(chip, fw->data, fw->size); } static int lp5521_post_init_device(struct lp55xx_chip *chip) @@ -313,155 +406,6 @@ static void lp5521_led_brightness_work(struct work_struct *work) mutex_unlock(&chip->lock); } -/* Set engine mode and create appropriate sysfs attributes, if required. */ -static int lp5521_set_mode(struct lp5521_engine *engine, u8 mode) -{ - int ret = 0; - - /* if in that mode already do nothing, except for run */ - if (mode == engine->mode && mode != LP5521_CMD_RUN) - return 0; - - if (mode == LP5521_CMD_RUN) { - ret = lp5521_set_engine_mode(engine, LP5521_CMD_RUN); - } else if (mode == LP5521_CMD_LOAD) { - lp5521_set_engine_mode(engine, LP5521_CMD_DISABLED); - lp5521_set_engine_mode(engine, LP5521_CMD_LOAD); - } else if (mode == LP5521_CMD_DISABLED) { - lp5521_set_engine_mode(engine, LP5521_CMD_DISABLED); - } - - engine->mode = mode; - - return ret; -} - -static int lp5521_do_store_load(struct lp5521_engine *engine, - const char *buf, size_t len) -{ - struct lp5521_chip *chip = engine_to_lp5521(engine); - struct i2c_client *client = chip->client; - int ret, nrchars, offset = 0, i = 0; - char c[3]; - unsigned cmd; - u8 pattern[LP5521_PROGRAM_LENGTH] = {0}; - - while ((offset < len - 1) && (i < LP5521_PROGRAM_LENGTH)) { - /* separate sscanfs because length is working only for %s */ - ret = sscanf(buf + offset, "%2s%n ", c, &nrchars); - if (ret != 2) - goto fail; - ret = sscanf(c, "%2x", &cmd); - if (ret != 1) - goto fail; - pattern[i] = (u8)cmd; - - offset += nrchars; - i++; - } - - /* Each instruction is 16bit long. Check that length is even */ - if (i % 2) - goto fail; - - mutex_lock(&chip->lock); - if (engine->mode == LP5521_CMD_LOAD) - ret = lp5521_load_program(engine, pattern); - else - ret = -EINVAL; - mutex_unlock(&chip->lock); - - if (ret) { - dev_err(&client->dev, "failed loading pattern\n"); - return ret; - } - - return len; -fail: - dev_err(&client->dev, "wrong pattern format\n"); - return -EINVAL; -} - -static ssize_t store_engine_load(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lp5521_chip *chip = i2c_get_clientdata(client); - return lp5521_do_store_load(&chip->engines[nr - 1], buf, len); -} - -#define store_load(nr) \ -static ssize_t store_engine##nr##_load(struct device *dev, \ - struct device_attribute *attr, \ - const char *buf, size_t len) \ -{ \ - return store_engine_load(dev, attr, buf, len, nr); \ -} -store_load(1) -store_load(2) -store_load(3) - -static ssize_t show_engine_mode(struct device *dev, - struct device_attribute *attr, - char *buf, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lp5521_chip *chip = i2c_get_clientdata(client); - switch (chip->engines[nr - 1].mode) { - case LP5521_CMD_RUN: - return sprintf(buf, "run\n"); - case LP5521_CMD_LOAD: - return sprintf(buf, "load\n"); - case LP5521_CMD_DISABLED: - return sprintf(buf, "disabled\n"); - default: - return sprintf(buf, "disabled\n"); - } -} - -#define show_mode(nr) \ -static ssize_t show_engine##nr##_mode(struct device *dev, \ - struct device_attribute *attr, \ - char *buf) \ -{ \ - return show_engine_mode(dev, attr, buf, nr); \ -} -show_mode(1) -show_mode(2) -show_mode(3) - -static ssize_t store_engine_mode(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lp5521_chip *chip = i2c_get_clientdata(client); - struct lp5521_engine *engine = &chip->engines[nr - 1]; - mutex_lock(&chip->lock); - - if (!strncmp(buf, "run", 3)) - lp5521_set_mode(engine, LP5521_CMD_RUN); - else if (!strncmp(buf, "load", 4)) - lp5521_set_mode(engine, LP5521_CMD_LOAD); - else if (!strncmp(buf, "disabled", 8)) - lp5521_set_mode(engine, LP5521_CMD_DISABLED); - - mutex_unlock(&chip->lock); - return len; -} - -#define store_mode(nr) \ -static ssize_t store_engine##nr##_mode(struct device *dev, \ - struct device_attribute *attr, \ - const char *buf, size_t len) \ -{ \ - return store_engine_mode(dev, attr, buf, len, nr); \ -} -store_mode(1) -store_mode(2) -store_mode(3) - static ssize_t lp5521_selftest(struct device *dev, struct device_attribute *attr, char *buf) @@ -550,45 +494,11 @@ static void lp5521_run_led_pattern(int mode, struct lp5521_chip *chip) } } -static ssize_t store_led_pattern(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len) -{ - struct lp5521_chip *chip = i2c_get_clientdata(to_i2c_client(dev)); - unsigned long val; - int ret; - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - lp5521_run_led_pattern(val, chip); - - return len; -} - /* device attributes */ -static DEVICE_ATTR(engine1_mode, S_IRUGO | S_IWUSR, - show_engine1_mode, store_engine1_mode); -static DEVICE_ATTR(engine2_mode, S_IRUGO | S_IWUSR, - show_engine2_mode, store_engine2_mode); -static DEVICE_ATTR(engine3_mode, S_IRUGO | S_IWUSR, - show_engine3_mode, store_engine3_mode); -static DEVICE_ATTR(engine1_load, S_IWUSR, NULL, store_engine1_load); -static DEVICE_ATTR(engine2_load, S_IWUSR, NULL, store_engine2_load); -static DEVICE_ATTR(engine3_load, S_IWUSR, NULL, store_engine3_load); static DEVICE_ATTR(selftest, S_IRUGO, lp5521_selftest, NULL); -static DEVICE_ATTR(led_pattern, S_IWUSR, NULL, store_led_pattern); static struct attribute *lp5521_attributes[] = { - &dev_attr_engine1_mode.attr, - &dev_attr_engine2_mode.attr, - &dev_attr_engine3_mode.attr, &dev_attr_selftest.attr, - &dev_attr_engine1_load.attr, - &dev_attr_engine2_load.attr, - &dev_attr_engine3_load.attr, - &dev_attr_led_pattern.attr, NULL }; @@ -623,6 +533,8 @@ static struct lp55xx_device_config lp5521_cfg = { .post_init_device = lp5521_post_init_device, .brightness_work_fn = lp5521_led_brightness_work, .set_led_current = lp5521_set_led_current, + .firmware_cb = lp5521_firmware_loaded, + .run_engine = lp5521_run_engine, }; static int lp5521_probe(struct i2c_client *client, -- cgit v1.2.3 From db6eaf8388a413a5ee1b4547ce78506b9c6456b0 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:18:51 +0900 Subject: leds-lp5523: use generic firmware interface LP55xx common driver provides generic firmware interface for running a LED pattern. LP5521 and LP5523 have many device attributes for running patterns. This patch cleans up those complex code. Removed device attributes: engine1_mode engine2_mode engine3_mode engine1_load engine2_load engine3_load engine1_leds engine2_leds engine3_leds All device attributes and functions are replaced with two callback functions, 'firmware_cb' and 'run_engine'. New engine functions: lp5523_load/stop/run_engine(), lp5523_update_program_memory() and lp5523_wait_opmode_done() Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5523.c | 503 ++++++++++++++------------------------------- 1 file changed, 157 insertions(+), 346 deletions(-) diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 70630156eff..4b1ad9013a5 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -35,6 +35,7 @@ #include #include #include +#include #include "leds-lp55xx-common.h" @@ -108,20 +109,39 @@ #define LED_ACTIVE(mux, led) (!!(mux & (0x0001 << led))) #define SHIFT_MASK(id) (((id) - 1) * 2) +/* Memory Page Selection */ +#define LP5523_PAGE_ENG1 0 +#define LP5523_PAGE_ENG2 1 +#define LP5523_PAGE_ENG3 2 + +/* Program Memory Operations */ +#define LP5523_MODE_ENG1_M 0x30 /* Operation Mode Register */ +#define LP5523_MODE_ENG2_M 0x0C +#define LP5523_MODE_ENG3_M 0x03 +#define LP5523_LOAD_ENG1 0x10 +#define LP5523_LOAD_ENG2 0x04 +#define LP5523_LOAD_ENG3 0x01 + +#define LP5523_ENG1_IS_LOADING(mode) \ + ((mode & LP5523_MODE_ENG1_M) == LP5523_LOAD_ENG1) +#define LP5523_ENG2_IS_LOADING(mode) \ + ((mode & LP5523_MODE_ENG2_M) == LP5523_LOAD_ENG2) +#define LP5523_ENG3_IS_LOADING(mode) \ + ((mode & LP5523_MODE_ENG3_M) == LP5523_LOAD_ENG3) + +#define LP5523_EXEC_ENG1_M 0x30 /* Enable Register */ +#define LP5523_EXEC_ENG2_M 0x0C +#define LP5523_EXEC_ENG3_M 0x03 +#define LP5523_EXEC_M 0x3F +#define LP5523_RUN_ENG1 0x20 +#define LP5523_RUN_ENG2 0x08 +#define LP5523_RUN_ENG3 0x02 + enum lp5523_chip_id { LP5523, LP55231, }; -struct lp5523_engine { - int id; - u8 mode; - u8 prog_page; - u8 mux_page; - u16 led_mux; - u8 engine_mask; -}; - struct lp5523_led { int id; u8 chan_nr; @@ -135,13 +155,17 @@ struct lp5523_led { struct lp5523_chip { struct mutex lock; /* Serialize control */ struct i2c_client *client; - struct lp5523_engine engines[LP5523_ENGINES]; struct lp5523_led leds[LP5523_MAX_LEDS]; struct lp5523_platform_data *pdata; u8 num_channels; u8 num_leds; }; +static inline void lp5523_wait_opmode_done(void) +{ + usleep_range(1000, 2000); +} + static void lp5523_set_led_current(struct lp55xx_led *led, u8 led_current) { led->led_current = led_current; @@ -149,27 +173,6 @@ static void lp5523_set_led_current(struct lp55xx_led *led, u8 led_current) led_current); } -static inline struct lp5523_led *cdev_to_led(struct led_classdev *cdev) -{ - return container_of(cdev, struct lp5523_led, cdev); -} - -static inline struct lp5523_chip *engine_to_lp5523(struct lp5523_engine *engine) -{ - return container_of(engine, struct lp5523_chip, - engines[engine->id - 1]); -} - -static inline struct lp5523_chip *led_to_lp5523(struct lp5523_led *led) -{ - return container_of(led, struct lp5523_chip, - leds[led->id]); -} - -static void lp5523_set_mode(struct lp5523_engine *engine, u8 mode); -static int lp5523_set_engine_mode(struct lp5523_engine *engine, u8 mode); -static int lp5523_load_program(struct lp5523_engine *engine, const u8 *pattern); - static int lp5523_write(struct i2c_client *client, u8 reg, u8 value) { return i2c_smbus_write_byte_data(client, reg, value); @@ -212,177 +215,162 @@ static int lp5523_post_init_device(struct lp55xx_chip *chip) return lp55xx_write(chip, LP5523_REG_ENABLE_LEDS_LSB, 0xff); } -static int lp5523_set_engine_mode(struct lp5523_engine *engine, u8 mode) +static void lp5523_load_engine(struct lp55xx_chip *chip) { - struct lp5523_chip *chip = engine_to_lp5523(engine); - struct i2c_client *client = chip->client; - int ret; - u8 engine_state; - - ret = lp5523_read(client, LP5523_REG_OP_MODE, &engine_state); - if (ret) - goto fail; - - engine_state &= ~(engine->engine_mask); - - /* set mode only for this engine */ - mode &= engine->engine_mask; - - engine_state |= mode; - - ret |= lp5523_write(client, LP5523_REG_OP_MODE, engine_state); -fail: - return ret; + enum lp55xx_engine_index idx = chip->engine_idx; + u8 mask[] = { + [LP55XX_ENGINE_1] = LP5523_MODE_ENG1_M, + [LP55XX_ENGINE_2] = LP5523_MODE_ENG2_M, + [LP55XX_ENGINE_3] = LP5523_MODE_ENG3_M, + }; + + u8 val[] = { + [LP55XX_ENGINE_1] = LP5523_LOAD_ENG1, + [LP55XX_ENGINE_2] = LP5523_LOAD_ENG2, + [LP55XX_ENGINE_3] = LP5523_LOAD_ENG3, + }; + + u8 page_sel[] = { + [LP55XX_ENGINE_1] = LP5523_PAGE_ENG1, + [LP55XX_ENGINE_2] = LP5523_PAGE_ENG2, + [LP55XX_ENGINE_3] = LP5523_PAGE_ENG3, + }; + + lp55xx_update_bits(chip, LP5523_REG_OP_MODE, mask[idx], val[idx]); + + lp5523_wait_opmode_done(); + + lp55xx_write(chip, LP5523_REG_PROG_PAGE_SEL, page_sel[idx]); } -static int lp5523_load_mux(struct lp5523_engine *engine, u16 mux) +static void lp5523_stop_engine(struct lp55xx_chip *chip) { - struct lp5523_chip *chip = engine_to_lp5523(engine); - struct i2c_client *client = chip->client; - int ret = 0; - - ret |= lp5523_set_engine_mode(engine, LP5523_CMD_LOAD); + lp55xx_write(chip, LP5523_REG_OP_MODE, 0); + lp5523_wait_opmode_done(); +} - ret |= lp5523_write(client, LP5523_REG_PROG_PAGE_SEL, engine->mux_page); - ret |= lp5523_write(client, LP5523_REG_PROG_MEM, - (u8)(mux >> 8)); - ret |= lp5523_write(client, LP5523_REG_PROG_MEM + 1, (u8)(mux)); - engine->led_mux = mux; +static void lp5523_turn_off_channels(struct lp55xx_chip *chip) +{ + int i; - return ret; + for (i = 0; i < LP5523_MAX_LEDS; i++) + lp55xx_write(chip, LP5523_REG_LED_PWM_BASE + i, 0); } -static int lp5523_load_program(struct lp5523_engine *engine, const u8 *pattern) +static void lp5523_run_engine(struct lp55xx_chip *chip, bool start) { - struct lp5523_chip *chip = engine_to_lp5523(engine); - struct i2c_client *client = chip->client; + int ret; + u8 mode; + u8 exec; - int ret = 0; + /* stop engine */ + if (!start) { + lp5523_stop_engine(chip); + lp5523_turn_off_channels(chip); + return; + } - ret |= lp5523_set_engine_mode(engine, LP5523_CMD_LOAD); + /* + * To run the engine, + * operation mode and enable register should updated at the same time + */ - ret |= lp5523_write(client, LP5523_REG_PROG_PAGE_SEL, - engine->prog_page); - ret |= i2c_smbus_write_i2c_block_data(client, LP5523_REG_PROG_MEM, - LP5523_PROGRAM_LENGTH, pattern); + ret = lp55xx_read(chip, LP5523_REG_OP_MODE, &mode); + if (ret) + return; - return ret; -} + ret = lp55xx_read(chip, LP5523_REG_ENABLE, &exec); + if (ret) + return; -static int lp5523_run_program(struct lp5523_engine *engine) -{ - struct lp5523_chip *chip = engine_to_lp5523(engine); - struct i2c_client *client = chip->client; - int ret; + /* change operation mode to RUN only when each engine is loading */ + if (LP5523_ENG1_IS_LOADING(mode)) { + mode = (mode & ~LP5523_MODE_ENG1_M) | LP5523_RUN_ENG1; + exec = (exec & ~LP5523_EXEC_ENG1_M) | LP5523_RUN_ENG1; + } - ret = lp5523_write(client, LP5523_REG_ENABLE, - LP5523_CMD_RUN | LP5523_ENABLE); - if (ret) - goto fail; + if (LP5523_ENG2_IS_LOADING(mode)) { + mode = (mode & ~LP5523_MODE_ENG2_M) | LP5523_RUN_ENG2; + exec = (exec & ~LP5523_EXEC_ENG2_M) | LP5523_RUN_ENG2; + } - ret = lp5523_set_engine_mode(engine, LP5523_CMD_RUN); -fail: - return ret; + if (LP5523_ENG3_IS_LOADING(mode)) { + mode = (mode & ~LP5523_MODE_ENG3_M) | LP5523_RUN_ENG3; + exec = (exec & ~LP5523_EXEC_ENG3_M) | LP5523_RUN_ENG3; + } + + lp55xx_write(chip, LP5523_REG_OP_MODE, mode); + lp5523_wait_opmode_done(); + + lp55xx_update_bits(chip, LP5523_REG_ENABLE, LP5523_EXEC_M, exec); } -static int lp5523_mux_parse(const char *buf, u16 *mux, size_t len) +static int lp5523_update_program_memory(struct lp55xx_chip *chip, + const u8 *data, size_t size) { + u8 pattern[LP5523_PROGRAM_LENGTH] = {0}; + unsigned cmd; + char c[3]; + int update_size; + int nrchars; + int offset = 0; + int ret; int i; - u16 tmp_mux = 0; - - len = min_t(int, len, LP5523_MAX_LEDS); - for (i = 0; i < len; i++) { - switch (buf[i]) { - case '1': - tmp_mux |= (1 << i); - break; - case '0': - break; - case '\n': - i = len; - break; - default: - return -1; - } - } - *mux = tmp_mux; - return 0; -} + /* clear program memory before updating */ + for (i = 0; i < LP5523_PROGRAM_LENGTH; i++) + lp55xx_write(chip, LP5523_REG_PROG_MEM + i, 0); -static void lp5523_mux_to_array(u16 led_mux, char *array) -{ - int i, pos = 0; - for (i = 0; i < LP5523_MAX_LEDS; i++) - pos += sprintf(array + pos, "%x", LED_ACTIVE(led_mux, i)); + i = 0; + while ((offset < size - 1) && (i < LP5523_PROGRAM_LENGTH)) { + /* separate sscanfs because length is working only for %s */ + ret = sscanf(data + offset, "%2s%n ", c, &nrchars); + if (ret != 1) + goto err; - array[pos] = '\0'; -} + ret = sscanf(c, "%2x", &cmd); + if (ret != 1) + goto err; -/*--------------------------------------------------------------*/ -/* Sysfs interface */ -/*--------------------------------------------------------------*/ + pattern[i] = (u8)cmd; + offset += nrchars; + i++; + } -static ssize_t show_engine_leds(struct device *dev, - struct device_attribute *attr, - char *buf, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lp5523_chip *chip = i2c_get_clientdata(client); - char mux[LP5523_MAX_LEDS + 1]; + /* Each instruction is 16bit long. Check that length is even */ + if (i % 2) + goto err; - lp5523_mux_to_array(chip->engines[nr - 1].led_mux, mux); + update_size = i; + for (i = 0; i < update_size; i++) + lp55xx_write(chip, LP5523_REG_PROG_MEM + i, pattern[i]); - return sprintf(buf, "%s\n", mux); -} + return 0; -#define show_leds(nr) \ -static ssize_t show_engine##nr##_leds(struct device *dev, \ - struct device_attribute *attr, \ - char *buf) \ -{ \ - return show_engine_leds(dev, attr, buf, nr); \ +err: + dev_err(&chip->cl->dev, "wrong pattern format\n"); + return -EINVAL; } -show_leds(1) -show_leds(2) -show_leds(3) -static ssize_t store_engine_leds(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len, int nr) +static void lp5523_firmware_loaded(struct lp55xx_chip *chip) { - struct i2c_client *client = to_i2c_client(dev); - struct lp5523_chip *chip = i2c_get_clientdata(client); - u16 mux = 0; - ssize_t ret; - - if (lp5523_mux_parse(buf, &mux, len)) - return -EINVAL; + const struct firmware *fw = chip->fw; - mutex_lock(&chip->lock); - ret = -EINVAL; - if (chip->engines[nr - 1].mode != LP5523_CMD_LOAD) - goto leave; - - if (lp5523_load_mux(&chip->engines[nr - 1], mux)) - goto leave; + if (fw->size > LP5523_PROGRAM_LENGTH) { + dev_err(&chip->cl->dev, "firmware data size overflow: %zu\n", + fw->size); + return; + } - ret = len; -leave: - mutex_unlock(&chip->lock); - return ret; -} + /* + * Program momery sequence + * 1) set engine mode to "LOAD" + * 2) write firmware data into program memory + */ -#define store_leds(nr) \ -static ssize_t store_engine##nr##_leds(struct device *dev, \ - struct device_attribute *attr, \ - const char *buf, size_t len) \ -{ \ - return store_engine_leds(dev, attr, buf, len, nr); \ + lp5523_load_engine(chip); + lp5523_update_program_memory(chip, fw->data, fw->size); } -store_leds(1) -store_leds(2) -store_leds(3) static ssize_t lp5523_selftest(struct device *dev, struct device_attribute *attr, @@ -485,159 +473,10 @@ static void lp5523_led_brightness_work(struct work_struct *work) mutex_unlock(&chip->lock); } -static int lp5523_do_store_load(struct lp5523_engine *engine, - const char *buf, size_t len) -{ - struct lp5523_chip *chip = engine_to_lp5523(engine); - struct i2c_client *client = chip->client; - int ret, nrchars, offset = 0, i = 0; - char c[3]; - unsigned cmd; - u8 pattern[LP5523_PROGRAM_LENGTH] = {0}; - - if (engine->mode != LP5523_CMD_LOAD) - return -EINVAL; - - while ((offset < len - 1) && (i < LP5523_PROGRAM_LENGTH)) { - /* separate sscanfs because length is working only for %s */ - ret = sscanf(buf + offset, "%2s%n ", c, &nrchars); - ret = sscanf(c, "%2x", &cmd); - if (ret != 1) - goto fail; - pattern[i] = (u8)cmd; - - offset += nrchars; - i++; - } - - /* Each instruction is 16bit long. Check that length is even */ - if (i % 2) - goto fail; - - mutex_lock(&chip->lock); - ret = lp5523_load_program(engine, pattern); - mutex_unlock(&chip->lock); - - if (ret) { - dev_err(&client->dev, "failed loading pattern\n"); - return ret; - } - - return len; -fail: - dev_err(&client->dev, "wrong pattern format\n"); - return -EINVAL; -} - -static ssize_t store_engine_load(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lp5523_chip *chip = i2c_get_clientdata(client); - return lp5523_do_store_load(&chip->engines[nr - 1], buf, len); -} - -#define store_load(nr) \ -static ssize_t store_engine##nr##_load(struct device *dev, \ - struct device_attribute *attr, \ - const char *buf, size_t len) \ -{ \ - return store_engine_load(dev, attr, buf, len, nr); \ -} -store_load(1) -store_load(2) -store_load(3) - -static ssize_t show_engine_mode(struct device *dev, - struct device_attribute *attr, - char *buf, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lp5523_chip *chip = i2c_get_clientdata(client); - switch (chip->engines[nr - 1].mode) { - case LP5523_CMD_RUN: - return sprintf(buf, "run\n"); - case LP5523_CMD_LOAD: - return sprintf(buf, "load\n"); - case LP5523_CMD_DISABLED: - return sprintf(buf, "disabled\n"); - default: - return sprintf(buf, "disabled\n"); - } -} - -#define show_mode(nr) \ -static ssize_t show_engine##nr##_mode(struct device *dev, \ - struct device_attribute *attr, \ - char *buf) \ -{ \ - return show_engine_mode(dev, attr, buf, nr); \ -} -show_mode(1) -show_mode(2) -show_mode(3) - -static ssize_t store_engine_mode(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lp5523_chip *chip = i2c_get_clientdata(client); - struct lp5523_engine *engine = &chip->engines[nr - 1]; - mutex_lock(&chip->lock); - - if (!strncmp(buf, "run", 3)) - lp5523_set_mode(engine, LP5523_CMD_RUN); - else if (!strncmp(buf, "load", 4)) - lp5523_set_mode(engine, LP5523_CMD_LOAD); - else if (!strncmp(buf, "disabled", 8)) - lp5523_set_mode(engine, LP5523_CMD_DISABLED); - - mutex_unlock(&chip->lock); - return len; -} - -#define store_mode(nr) \ -static ssize_t store_engine##nr##_mode(struct device *dev, \ - struct device_attribute *attr, \ - const char *buf, size_t len) \ -{ \ - return store_engine_mode(dev, attr, buf, len, nr); \ -} -store_mode(1) -store_mode(2) -store_mode(3) - -/* device attributes */ -static DEVICE_ATTR(engine1_mode, S_IRUGO | S_IWUSR, - show_engine1_mode, store_engine1_mode); -static DEVICE_ATTR(engine2_mode, S_IRUGO | S_IWUSR, - show_engine2_mode, store_engine2_mode); -static DEVICE_ATTR(engine3_mode, S_IRUGO | S_IWUSR, - show_engine3_mode, store_engine3_mode); -static DEVICE_ATTR(engine1_leds, S_IRUGO | S_IWUSR, - show_engine1_leds, store_engine1_leds); -static DEVICE_ATTR(engine2_leds, S_IRUGO | S_IWUSR, - show_engine2_leds, store_engine2_leds); -static DEVICE_ATTR(engine3_leds, S_IRUGO | S_IWUSR, - show_engine3_leds, store_engine3_leds); -static DEVICE_ATTR(engine1_load, S_IWUSR, NULL, store_engine1_load); -static DEVICE_ATTR(engine2_load, S_IWUSR, NULL, store_engine2_load); -static DEVICE_ATTR(engine3_load, S_IWUSR, NULL, store_engine3_load); static DEVICE_ATTR(selftest, S_IRUGO, lp5523_selftest, NULL); static struct attribute *lp5523_attributes[] = { - &dev_attr_engine1_mode.attr, - &dev_attr_engine2_mode.attr, - &dev_attr_engine3_mode.attr, &dev_attr_selftest.attr, - &dev_attr_engine1_load.attr, - &dev_attr_engine1_leds.attr, - &dev_attr_engine2_load.attr, - &dev_attr_engine2_leds.attr, - &dev_attr_engine3_load.attr, - &dev_attr_engine3_leds.attr, NULL, }; @@ -664,36 +503,6 @@ static void lp5523_unregister_sysfs(struct i2c_client *client) sysfs_remove_group(&dev->kobj, &lp5523_group); } -/*--------------------------------------------------------------*/ -/* Set chip operating mode */ -/*--------------------------------------------------------------*/ -static void lp5523_set_mode(struct lp5523_engine *engine, u8 mode) -{ - /* if in that mode already do nothing, except for run */ - if (mode == engine->mode && mode != LP5523_CMD_RUN) - return; - - switch (mode) { - case LP5523_CMD_RUN: - lp5523_run_program(engine); - break; - case LP5523_CMD_LOAD: - lp5523_set_engine_mode(engine, LP5523_CMD_DISABLED); - lp5523_set_engine_mode(engine, LP5523_CMD_LOAD); - break; - case LP5523_CMD_DISABLED: - lp5523_set_engine_mode(engine, LP5523_CMD_DISABLED); - break; - default: - return; - } - - engine->mode = mode; -} - -/*--------------------------------------------------------------*/ -/* Probe, Attach, Remove */ -/*--------------------------------------------------------------*/ /* Chip specific configurations */ static struct lp55xx_device_config lp5523_cfg = { .reset = { @@ -708,6 +517,8 @@ static struct lp55xx_device_config lp5523_cfg = { .post_init_device = lp5523_post_init_device, .brightness_work_fn = lp5523_led_brightness_work, .set_led_current = lp5523_set_led_current, + .firmware_cb = lp5523_firmware_loaded, + .run_engine = lp5523_run_engine, }; static int lp5523_probe(struct i2c_client *client, -- cgit v1.2.3 From 240085e255cd2818aff2ccde3066b7db1f29076a Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:20:01 +0900 Subject: leds-lp55xx: support device specific attributes To support device specific attributes, new common driver function is added. Eventually those are created on registering the sysfs with common dev attrs. Furthermore, this patch makes adding device attributes simple in each driver. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp55xx-common.c | 13 ++++++++++++- drivers/leds/leds-lp55xx-common.h | 4 ++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index 578902ab604..9638ad4dc63 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -493,8 +493,19 @@ EXPORT_SYMBOL_GPL(lp55xx_unregister_leds); int lp55xx_register_sysfs(struct lp55xx_chip *chip) { struct device *dev = &chip->cl->dev; + struct lp55xx_device_config *cfg = chip->cfg; + int ret; + + if (!cfg->run_engine || !cfg->firmware_cb) + goto dev_specific_attrs; + + ret = sysfs_create_group(&dev->kobj, &lp55xx_engine_attr_group); + if (ret) + return ret; - return sysfs_create_group(&dev->kobj, &lp55xx_engine_attr_group); +dev_specific_attrs: + return cfg->dev_attr_group ? + sysfs_create_group(&dev->kobj, cfg->dev_attr_group) : 0; } EXPORT_SYMBOL_GPL(lp55xx_register_sysfs); diff --git a/drivers/leds/leds-lp55xx-common.h b/drivers/leds/leds-lp55xx-common.h index 8473abf9830..64eb78da1c4 100644 --- a/drivers/leds/leds-lp55xx-common.h +++ b/drivers/leds/leds-lp55xx-common.h @@ -45,6 +45,7 @@ struct lp55xx_reg { * @set_led_current : LED current set function * @firmware_cb : Call function when the firmware is loaded * @run_engine : Run internal engine for pattern + * @dev_attr_group : Device specific attributes */ struct lp55xx_device_config { const struct lp55xx_reg reset; @@ -65,6 +66,9 @@ struct lp55xx_device_config { /* used for running firmware LED patterns */ void (*run_engine) (struct lp55xx_chip *chip, bool start); + + /* additional device specific attributes */ + const struct attribute_group *dev_attr_group; }; /* -- cgit v1.2.3 From e73c0ce6beaa71bee39b2d11bff0253be84c71a9 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:20:45 +0900 Subject: leds-lp55xx: use common device attribute driver function lp5521/5523_register_sysfs() are replaced with lp55xx common driver function, lp55xx_register_sysfs(). Chip specific device attributes are configurable using 'dev_attr_group'. Error condition name is changed: use specific error condition, 'err_register_sysfs' rather than unclear name, 'fail2'. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 17 +++++++---------- drivers/leds/leds-lp5523.c | 23 +++++++---------------- 2 files changed, 14 insertions(+), 26 deletions(-) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index 89371e065c1..abc33139e6f 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -506,12 +506,6 @@ static const struct attribute_group lp5521_group = { .attrs = lp5521_attributes, }; -static int lp5521_register_sysfs(struct i2c_client *client) -{ - struct device *dev = &client->dev; - return sysfs_create_group(&dev->kobj, &lp5521_group); -} - static void lp5521_unregister_sysfs(struct i2c_client *client) { struct device *dev = &client->dev; @@ -535,6 +529,7 @@ static struct lp55xx_device_config lp5521_cfg = { .set_led_current = lp5521_set_led_current, .firmware_cb = lp5521_firmware_loaded, .run_engine = lp5521_run_engine, + .dev_attr_group = &lp5521_group, }; static int lp5521_probe(struct i2c_client *client, @@ -577,13 +572,15 @@ static int lp5521_probe(struct i2c_client *client, if (ret) goto err_register_leds; - ret = lp5521_register_sysfs(client); + ret = lp55xx_register_sysfs(chip); if (ret) { dev_err(&client->dev, "registering sysfs failed\n"); - goto fail2; + goto err_register_sysfs; } - return ret; -fail2: + + return 0; + +err_register_sysfs: lp55xx_unregister_leds(led, chip); err_register_leds: lp55xx_deinit_device(chip); diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 4b1ad9013a5..4192a1ec606 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -484,18 +484,6 @@ static const struct attribute_group lp5523_group = { .attrs = lp5523_attributes, }; -static int lp5523_register_sysfs(struct i2c_client *client) -{ - struct device *dev = &client->dev; - int ret; - - ret = sysfs_create_group(&dev->kobj, &lp5523_group); - if (ret < 0) - return ret; - - return 0; -} - static void lp5523_unregister_sysfs(struct i2c_client *client) { struct device *dev = &client->dev; @@ -519,6 +507,7 @@ static struct lp55xx_device_config lp5523_cfg = { .set_led_current = lp5523_set_led_current, .firmware_cb = lp5523_firmware_loaded, .run_engine = lp5523_run_engine, + .dev_attr_group = &lp5523_group, }; static int lp5523_probe(struct i2c_client *client, @@ -561,13 +550,15 @@ static int lp5523_probe(struct i2c_client *client, if (ret) goto err_register_leds; - ret = lp5523_register_sysfs(client); + ret = lp55xx_register_sysfs(chip); if (ret) { dev_err(&client->dev, "registering sysfs failed\n"); - goto fail2; + goto err_register_sysfs; } - return ret; -fail2: + + return 0; + +err_register_sysfs: lp55xx_unregister_leds(led, chip); err_register_leds: lp55xx_deinit_device(chip); -- cgit v1.2.3 From 9ca3bd8022d76a0d1b386cedcecaf49004a58644 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:21:43 +0900 Subject: leds-lp55xx: code refactoring on selftest function LP5521 and LP5523 have a selftest function which is run via the sysfs. Use lp55xx driver data and R/W functions rather than lp5521/5523 private data and functions. Additionally, if-statements are changed for code simplicity. Unused functions, lp5521/5523_read() are removed. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 30 ++++++++++---------------- drivers/leds/leds-lp5523.c | 53 +++++++++++++++++----------------------------- 2 files changed, 31 insertions(+), 52 deletions(-) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index abc33139e6f..1f6d9c7eb4a 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -168,18 +168,6 @@ static inline int lp5521_write(struct i2c_client *client, u8 reg, u8 value) return i2c_smbus_write_byte_data(client, reg, value); } -static int lp5521_read(struct i2c_client *client, u8 reg, u8 *buf) -{ - s32 ret; - - ret = i2c_smbus_read_byte_data(client, reg); - if (ret < 0) - return ret; - - *buf = ret; - return 0; -} - static void lp5521_load_engine(struct lp55xx_chip *chip) { enum lp55xx_engine_index idx = chip->engine_idx; @@ -378,19 +366,23 @@ static int lp5521_post_init_device(struct lp55xx_chip *chip) return 0; } -static int lp5521_run_selftest(struct lp5521_chip *chip, char *buf) +static int lp5521_run_selftest(struct lp55xx_chip *chip, char *buf) { + struct lp55xx_platform_data *pdata = chip->pdata; int ret; u8 status; - ret = lp5521_read(chip->client, LP5521_REG_STATUS, &status); + ret = lp55xx_read(chip, LP5521_REG_STATUS, &status); if (ret < 0) return ret; + if (pdata->clock_mode != LP55XX_CLOCK_EXT) + return 0; + /* Check that ext clock is really in use if requested */ - if (chip->pdata && chip->pdata->clock_mode == LP5521_CLOCK_EXT) - if ((status & LP5521_EXT_CLK_USED) == 0) - return -EIO; + if ((status & LP5521_EXT_CLK_USED) == 0) + return -EIO; + return 0; } @@ -410,8 +402,8 @@ static ssize_t lp5521_selftest(struct device *dev, struct device_attribute *attr, char *buf) { - struct i2c_client *client = to_i2c_client(dev); - struct lp5521_chip *chip = i2c_get_clientdata(client); + struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev)); + struct lp55xx_chip *chip = led->chip; int ret; mutex_lock(&chip->lock); diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 4192a1ec606..577059934f5 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -178,17 +178,6 @@ static int lp5523_write(struct i2c_client *client, u8 reg, u8 value) return i2c_smbus_write_byte_data(client, reg, value); } -static int lp5523_read(struct i2c_client *client, u8 reg, u8 *buf) -{ - s32 ret = i2c_smbus_read_byte_data(client, reg); - - if (ret < 0) - return ret; - - *buf = ret; - return 0; -} - static int lp5523_post_init_device(struct lp55xx_chip *chip) { int ret; @@ -376,35 +365,35 @@ static ssize_t lp5523_selftest(struct device *dev, struct device_attribute *attr, char *buf) { - struct i2c_client *client = to_i2c_client(dev); - struct lp5523_chip *chip = i2c_get_clientdata(client); + struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev)); + struct lp55xx_chip *chip = led->chip; + struct lp55xx_platform_data *pdata = chip->pdata; int i, ret, pos = 0; - int led = 0; u8 status, adc, vdd; mutex_lock(&chip->lock); - ret = lp5523_read(chip->client, LP5523_REG_STATUS, &status); + ret = lp55xx_read(chip, LP5523_REG_STATUS, &status); if (ret < 0) goto fail; /* Check that ext clock is really in use if requested */ - if ((chip->pdata) && (chip->pdata->clock_mode == LP5523_CLOCK_EXT)) + if (pdata->clock_mode == LP55XX_CLOCK_EXT) { if ((status & LP5523_EXT_CLK_USED) == 0) goto fail; + } /* Measure VDD (i.e. VBAT) first (channel 16 corresponds to VDD) */ - lp5523_write(chip->client, LP5523_REG_LED_TEST_CTRL, - LP5523_EN_LEDTEST | 16); + lp55xx_write(chip, LP5523_REG_LED_TEST_CTRL, LP5523_EN_LEDTEST | 16); usleep_range(3000, 6000); /* ADC conversion time is typically 2.7 ms */ - ret = lp5523_read(chip->client, LP5523_REG_STATUS, &status); + ret = lp55xx_read(chip, LP5523_REG_STATUS, &status); if (ret < 0) goto fail; if (!(status & LP5523_LEDTEST_DONE)) usleep_range(3000, 6000); /* Was not ready. Wait little bit */ - ret = lp5523_read(chip->client, LP5523_REG_LED_TEST_ADC, &vdd); + ret = lp55xx_read(chip, LP5523_REG_LED_TEST_ADC, &vdd); if (ret < 0) goto fail; @@ -412,41 +401,39 @@ static ssize_t lp5523_selftest(struct device *dev, for (i = 0; i < LP5523_MAX_LEDS; i++) { /* Skip non-existing channels */ - if (chip->pdata->led_config[i].led_current == 0) + if (pdata->led_config[i].led_current == 0) continue; /* Set default current */ - lp5523_write(chip->client, - LP5523_REG_LED_CURRENT_BASE + i, - chip->pdata->led_config[i].led_current); + lp55xx_write(chip, LP5523_REG_LED_CURRENT_BASE + i, + pdata->led_config[i].led_current); - lp5523_write(chip->client, LP5523_REG_LED_PWM_BASE + i, 0xff); + lp55xx_write(chip, LP5523_REG_LED_PWM_BASE + i, 0xff); /* let current stabilize 2 - 4ms before measurements start */ usleep_range(2000, 4000); - lp5523_write(chip->client, - LP5523_REG_LED_TEST_CTRL, + lp55xx_write(chip, LP5523_REG_LED_TEST_CTRL, LP5523_EN_LEDTEST | i); /* ADC conversion time is 2.7 ms typically */ usleep_range(3000, 6000); - ret = lp5523_read(chip->client, LP5523_REG_STATUS, &status); + ret = lp55xx_read(chip, LP5523_REG_STATUS, &status); if (ret < 0) goto fail; if (!(status & LP5523_LEDTEST_DONE)) usleep_range(3000, 6000);/* Was not ready. Wait. */ - ret = lp5523_read(chip->client, LP5523_REG_LED_TEST_ADC, &adc); + + ret = lp55xx_read(chip, LP5523_REG_LED_TEST_ADC, &adc); if (ret < 0) goto fail; if (adc >= vdd || adc < LP5523_ADC_SHORTCIRC_LIM) pos += sprintf(buf + pos, "LED %d FAIL\n", i); - lp5523_write(chip->client, LP5523_REG_LED_PWM_BASE + i, 0x00); + lp55xx_write(chip, LP5523_REG_LED_PWM_BASE + i, 0x00); /* Restore current */ - lp5523_write(chip->client, - LP5523_REG_LED_CURRENT_BASE + i, - chip->leds[led].led_current); + lp55xx_write(chip, LP5523_REG_LED_CURRENT_BASE + i, + led->led_current); led++; } if (pos == 0) -- cgit v1.2.3 From ba6fa84651ff9a609e0ceb8265e3335ab6ed656d Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:23:04 +0900 Subject: leds-lp55xx: add new function for removing device attribtues lp55xx_unregister_sysfs() is used for removing lp55xx device attributes. Chip specific and engine attributes are removed on unloading the driver. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp55xx-common.c | 12 ++++++++++++ drivers/leds/leds-lp55xx-common.h | 1 + 2 files changed, 13 insertions(+) diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index 9638ad4dc63..782ab84fe65 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -509,6 +509,18 @@ dev_specific_attrs: } EXPORT_SYMBOL_GPL(lp55xx_register_sysfs); +void lp55xx_unregister_sysfs(struct lp55xx_chip *chip) +{ + struct device *dev = &chip->cl->dev; + struct lp55xx_device_config *cfg = chip->cfg; + + if (cfg->dev_attr_group) + sysfs_remove_group(&dev->kobj, cfg->dev_attr_group); + + sysfs_remove_group(&dev->kobj, &lp55xx_engine_attr_group); +} +EXPORT_SYMBOL_GPL(lp55xx_unregister_sysfs); + MODULE_AUTHOR("Milo Kim "); MODULE_DESCRIPTION("LP55xx Common Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/leds/leds-lp55xx-common.h b/drivers/leds/leds-lp55xx-common.h index 64eb78da1c4..ece4761a130 100644 --- a/drivers/leds/leds-lp55xx-common.h +++ b/drivers/leds/leds-lp55xx-common.h @@ -129,5 +129,6 @@ extern void lp55xx_unregister_leds(struct lp55xx_led *led, /* common device attributes functions */ extern int lp55xx_register_sysfs(struct lp55xx_chip *chip); +extern void lp55xx_unregister_sysfs(struct lp55xx_chip *chip); #endif /* _LEDS_LP55XX_COMMON_H */ -- cgit v1.2.3 From 87cc4bde2a97cd8acccf34f333d0980dc5c2aa8a Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:23:51 +0900 Subject: leds-lp55xx: clean up _remove() Replace lp5521/5523_unregister_sysfs() with lp55xx_unregister_sysfs(). On unloading the driver, running engines should be stopped. Use explicit driver function, lp5521/5523_stop_engine(). Unused functions are removed. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 79 ++-------------------------------------------- drivers/leds/leds-lp5523.c | 19 ++--------- 2 files changed, 4 insertions(+), 94 deletions(-) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index 1f6d9c7eb4a..46721c3a8e8 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -412,36 +412,6 @@ static ssize_t lp5521_selftest(struct device *dev, return sprintf(buf, "%s\n", ret ? "FAIL" : "OK"); } -static void lp5521_clear_program_memory(struct i2c_client *cl) -{ - int i; - u8 rgb_mem[] = { - LP5521_REG_R_PROG_MEM, - LP5521_REG_G_PROG_MEM, - LP5521_REG_B_PROG_MEM, - }; - - for (i = 0; i < ARRAY_SIZE(rgb_mem); i++) { - lp5521_write(cl, rgb_mem[i], 0); - lp5521_write(cl, rgb_mem[i] + 1, 0); - } -} - -static void lp5521_write_program_memory(struct i2c_client *cl, - u8 base, u8 *rgb, int size) -{ - int i; - - if (!rgb || size <= 0) - return; - - for (i = 0; i < size; i++) - lp5521_write(cl, base + i, *(rgb + i)); - - lp5521_write(cl, base + i, 0); - lp5521_write(cl, base + i + 1, 0); -} - static inline struct lp5521_led_pattern *lp5521_get_pattern (struct lp5521_chip *chip, u8 offset) { @@ -450,42 +420,6 @@ static inline struct lp5521_led_pattern *lp5521_get_pattern return ptn; } -static void lp5521_run_led_pattern(int mode, struct lp5521_chip *chip) -{ - struct lp5521_led_pattern *ptn; - struct i2c_client *cl = chip->client; - int num_patterns = chip->pdata->num_patterns; - - if (mode > num_patterns || !(chip->pdata->patterns)) - return; - - if (mode == PATTERN_OFF) { - lp5521_write(cl, LP5521_REG_ENABLE, LP5521_ENABLE_DEFAULT); - usleep_range(1000, 2000); - lp5521_write(cl, LP5521_REG_OP_MODE, LP5521_CMD_DIRECT); - } else { - ptn = lp5521_get_pattern(chip, mode); - if (!ptn) - return; - - lp5521_write(cl, LP5521_REG_OP_MODE, LP5521_CMD_LOAD); - usleep_range(1000, 2000); - - lp5521_clear_program_memory(cl); - - lp5521_write_program_memory(cl, LP5521_REG_R_PROG_MEM, - ptn->r, ptn->size_r); - lp5521_write_program_memory(cl, LP5521_REG_G_PROG_MEM, - ptn->g, ptn->size_g); - lp5521_write_program_memory(cl, LP5521_REG_B_PROG_MEM, - ptn->b, ptn->size_b); - - lp5521_write(cl, LP5521_REG_OP_MODE, LP5521_CMD_RUN); - usleep_range(1000, 2000); - lp5521_write(cl, LP5521_REG_ENABLE, LP5521_ENABLE_RUN_PROGRAM); - } -} - /* device attributes */ static DEVICE_ATTR(selftest, S_IRUGO, lp5521_selftest, NULL); @@ -498,13 +432,6 @@ static const struct attribute_group lp5521_group = { .attrs = lp5521_attributes, }; -static void lp5521_unregister_sysfs(struct i2c_client *client) -{ - struct device *dev = &client->dev; - - sysfs_remove_group(&dev->kobj, &lp5521_group); -} - /* Chip specific configurations */ static struct lp55xx_device_config lp5521_cfg = { .reset = { @@ -582,13 +509,11 @@ err_init: static int lp5521_remove(struct i2c_client *client) { - struct lp5521_chip *old_chip = i2c_get_clientdata(client); struct lp55xx_led *led = i2c_get_clientdata(client); struct lp55xx_chip *chip = led->chip; - lp5521_run_led_pattern(PATTERN_OFF, old_chip); - lp5521_unregister_sysfs(client); - + lp5521_stop_engine(chip); + lp55xx_unregister_sysfs(chip); lp55xx_unregister_leds(led, chip); lp55xx_deinit_device(chip); diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 577059934f5..cf587c1b2c4 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -173,11 +173,6 @@ static void lp5523_set_led_current(struct lp55xx_led *led, u8 led_current) led_current); } -static int lp5523_write(struct i2c_client *client, u8 reg, u8 value) -{ - return i2c_smbus_write_byte_data(client, reg, value); -} - static int lp5523_post_init_device(struct lp55xx_chip *chip) { int ret; @@ -471,13 +466,6 @@ static const struct attribute_group lp5523_group = { .attrs = lp5523_attributes, }; -static void lp5523_unregister_sysfs(struct i2c_client *client) -{ - struct device *dev = &client->dev; - - sysfs_remove_group(&dev->kobj, &lp5523_group); -} - /* Chip specific configurations */ static struct lp55xx_device_config lp5523_cfg = { .reset = { @@ -558,11 +546,8 @@ static int lp5523_remove(struct i2c_client *client) struct lp55xx_led *led = i2c_get_clientdata(client); struct lp55xx_chip *chip = led->chip; - /* Disable engine mode */ - lp5523_write(client, LP5523_REG_OP_MODE, LP5523_CMD_DISABLED); - - lp5523_unregister_sysfs(client); - + lp5523_stop_engine(chip); + lp55xx_unregister_sysfs(chip); lp55xx_unregister_leds(led, chip); lp55xx_deinit_device(chip); -- cgit v1.2.3 From 93ca4093adb757d5140071e72b2e9bfbb519b6c1 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:24:37 +0900 Subject: leds-lp55xx: clean up unused data and functions Old data structures and I2C function are not used any more. Each driver uses the lp55xx common data and functions. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 32 -------------------------------- drivers/leds/leds-lp5523.c | 19 ------------------- 2 files changed, 51 deletions(-) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index 46721c3a8e8..f05eb6e31d5 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -125,25 +125,6 @@ #define LP5521_RUN_G 0x08 #define LP5521_RUN_B 0x02 -struct lp5521_led { - int id; - u8 chan_nr; - u8 led_current; - u8 max_current; - struct led_classdev cdev; - struct work_struct brightness_work; - u8 brightness; -}; - -struct lp5521_chip { - struct lp5521_platform_data *pdata; - struct mutex lock; /* Serialize control */ - struct i2c_client *client; - struct lp5521_led leds[LP5521_MAX_LEDS]; - u8 num_channels; - u8 num_leds; -}; - static inline void lp5521_wait_opmode_done(void) { /* operation mode change needs to be longer than 153 us */ @@ -163,11 +144,6 @@ static void lp5521_set_led_current(struct lp55xx_led *led, u8 led_current) led_current); } -static inline int lp5521_write(struct i2c_client *client, u8 reg, u8 value) -{ - return i2c_smbus_write_byte_data(client, reg, value); -} - static void lp5521_load_engine(struct lp55xx_chip *chip) { enum lp55xx_engine_index idx = chip->engine_idx; @@ -412,14 +388,6 @@ static ssize_t lp5521_selftest(struct device *dev, return sprintf(buf, "%s\n", ret ? "FAIL" : "OK"); } -static inline struct lp5521_led_pattern *lp5521_get_pattern - (struct lp5521_chip *chip, u8 offset) -{ - struct lp5521_led_pattern *ptn; - ptn = chip->pdata->patterns + (offset - 1); - return ptn; -} - /* device attributes */ static DEVICE_ATTR(selftest, S_IRUGO, lp5521_selftest, NULL); diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index cf587c1b2c4..b14bde2db24 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -142,25 +142,6 @@ enum lp5523_chip_id { LP55231, }; -struct lp5523_led { - int id; - u8 chan_nr; - u8 led_current; - u8 max_current; - struct led_classdev cdev; - struct work_struct brightness_work; - u8 brightness; -}; - -struct lp5523_chip { - struct mutex lock; /* Serialize control */ - struct i2c_client *client; - struct lp5523_led leds[LP5523_MAX_LEDS]; - struct lp5523_platform_data *pdata; - u8 num_channels; - u8 num_leds; -}; - static inline void lp5523_wait_opmode_done(void) { usleep_range(1000, 2000); -- cgit v1.2.3 From 12f022d27bcdd606527ae1b3c9ad20cc8c90ce98 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:25:26 +0900 Subject: leds-lp55xx: clean up definitions Remove unused definitions and change hex values to capital letters Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 26 +++------------------ drivers/leds/leds-lp5523.c | 56 ++++++++-------------------------------------- 2 files changed, 12 insertions(+), 70 deletions(-) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index f05eb6e31d5..b2a0d877cdc 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -39,18 +39,9 @@ #include "leds-lp55xx-common.h" -#define LP5521_PROGRAM_LENGTH 32 /* in bytes */ - -#define LP5521_MAX_LEDS 3 /* Maximum number of LEDs */ -#define LP5521_MAX_ENGINES 3 /* Maximum number of engines */ - -#define LP5521_ENG_MASK_BASE 0x30 /* 00110000 */ -#define LP5521_ENG_STATUS_MASK 0x07 /* 00000111 */ - -#define LP5521_CMD_LOAD 0x15 /* 00010101 */ -#define LP5521_CMD_RUN 0x2a /* 00101010 */ -#define LP5521_CMD_DIRECT 0x3f /* 00111111 */ -#define LP5521_CMD_DISABLED 0x00 /* 00000000 */ +#define LP5521_PROGRAM_LENGTH 32 +#define LP5521_MAX_LEDS 3 +#define LP5521_CMD_DIRECT 0x3F /* Registers */ #define LP5521_REG_ENABLE 0x00 @@ -62,22 +53,14 @@ #define LP5521_REG_G_CURRENT 0x06 #define LP5521_REG_B_CURRENT 0x07 #define LP5521_REG_CONFIG 0x08 -#define LP5521_REG_R_CHANNEL_PC 0x09 -#define LP5521_REG_G_CHANNEL_PC 0x0A -#define LP5521_REG_B_CHANNEL_PC 0x0B #define LP5521_REG_STATUS 0x0C #define LP5521_REG_RESET 0x0D -#define LP5521_REG_GPO 0x0E #define LP5521_REG_R_PROG_MEM 0x10 #define LP5521_REG_G_PROG_MEM 0x30 #define LP5521_REG_B_PROG_MEM 0x50 -#define LP5521_PROG_MEM_BASE LP5521_REG_R_PROG_MEM -#define LP5521_PROG_MEM_SIZE 0x20 - /* Base register to set LED current */ #define LP5521_REG_LED_CURRENT_BASE LP5521_REG_R_CURRENT - /* Base register to set the brightness */ #define LP5521_REG_LED_PWM_BASE LP5521_REG_R_PWM @@ -96,9 +79,6 @@ /* default R channel current register value */ #define LP5521_REG_R_CURR_DEFAULT 0xAF -/* Pattern Mode */ -#define PATTERN_OFF 0 - /* Reset register value */ #define LP5521_RESET 0xFF diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index b14bde2db24..e145ce984b1 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -39,76 +39,38 @@ #include "leds-lp55xx-common.h" +#define LP5523_PROGRAM_LENGTH 32 +#define LP5523_MAX_LEDS 9 + +/* Registers */ #define LP5523_REG_ENABLE 0x00 #define LP5523_REG_OP_MODE 0x01 -#define LP5523_REG_RATIOMETRIC_MSB 0x02 -#define LP5523_REG_RATIOMETRIC_LSB 0x03 #define LP5523_REG_ENABLE_LEDS_MSB 0x04 #define LP5523_REG_ENABLE_LEDS_LSB 0x05 -#define LP5523_REG_LED_CNTRL_BASE 0x06 #define LP5523_REG_LED_PWM_BASE 0x16 #define LP5523_REG_LED_CURRENT_BASE 0x26 #define LP5523_REG_CONFIG 0x36 -#define LP5523_REG_CHANNEL1_PC 0x37 -#define LP5523_REG_CHANNEL2_PC 0x38 -#define LP5523_REG_CHANNEL3_PC 0x39 -#define LP5523_REG_STATUS 0x3a -#define LP5523_REG_GPO 0x3b -#define LP5523_REG_VARIABLE 0x3c -#define LP5523_REG_RESET 0x3d -#define LP5523_REG_TEMP_CTRL 0x3e -#define LP5523_REG_TEMP_READ 0x3f -#define LP5523_REG_TEMP_WRITE 0x40 +#define LP5523_REG_STATUS 0x3A +#define LP5523_REG_RESET 0x3D #define LP5523_REG_LED_TEST_CTRL 0x41 #define LP5523_REG_LED_TEST_ADC 0x42 -#define LP5523_REG_ENG1_VARIABLE 0x45 -#define LP5523_REG_ENG2_VARIABLE 0x46 -#define LP5523_REG_ENG3_VARIABLE 0x47 -#define LP5523_REG_MASTER_FADER1 0x48 -#define LP5523_REG_MASTER_FADER2 0x49 -#define LP5523_REG_MASTER_FADER3 0x4a -#define LP5523_REG_CH1_PROG_START 0x4c -#define LP5523_REG_CH2_PROG_START 0x4d -#define LP5523_REG_CH3_PROG_START 0x4e -#define LP5523_REG_PROG_PAGE_SEL 0x4f +#define LP5523_REG_PROG_PAGE_SEL 0x4F #define LP5523_REG_PROG_MEM 0x50 -#define LP5523_CMD_LOAD 0x15 /* 00010101 */ -#define LP5523_CMD_RUN 0x2a /* 00101010 */ -#define LP5523_CMD_DISABLED 0x00 /* 00000000 */ - +/* Bit description in registers */ #define LP5523_ENABLE 0x40 #define LP5523_AUTO_INC 0x40 #define LP5523_PWR_SAVE 0x20 #define LP5523_PWM_PWR_SAVE 0x04 -#define LP5523_CP_1 0x08 -#define LP5523_CP_1_5 0x10 #define LP5523_CP_AUTO 0x18 -#define LP5523_INT_CLK 0x01 #define LP5523_AUTO_CLK 0x02 + #define LP5523_EN_LEDTEST 0x80 #define LP5523_LEDTEST_DONE 0x80 #define LP5523_RESET 0xFF - -#define LP5523_DEFAULT_CURRENT 50 /* microAmps */ -#define LP5523_PROGRAM_LENGTH 32 /* in bytes */ -#define LP5523_PROGRAM_PAGES 6 #define LP5523_ADC_SHORTCIRC_LIM 80 - -#define LP5523_MAX_LEDS 9 -#define LP5523_ENGINES 3 - -#define LP5523_ENG_MASK_BASE 0x30 /* 00110000 */ - -#define LP5523_ENG_STATUS_MASK 0x07 /* 00000111 */ - -#define LP5523_IRQ_FLAGS IRQF_TRIGGER_FALLING - #define LP5523_EXT_CLK_USED 0x08 -#define LED_ACTIVE(mux, led) (!!(mux & (0x0001 << led))) -#define SHIFT_MASK(id) (((id) - 1) * 2) - /* Memory Page Selection */ #define LP5523_PAGE_ENG1 0 #define LP5523_PAGE_ENG2 1 -- cgit v1.2.3 From 79bcc10b8cf3db99958743ecb174e7637e1dd932 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:26:14 +0900 Subject: leds-lp55xx: clean up headers Remove unused headers and sort them alphabetically Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 19 ++++++------------- drivers/leds/leds-lp5523.c | 19 ++++++------------- 2 files changed, 12 insertions(+), 26 deletions(-) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index b2a0d877cdc..3b6dcb0be7e 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -20,22 +20,15 @@ * 02110-1301 USA */ -#include -#include -#include -#include -#include -#include #include -#include -#include -#include +#include +#include +#include #include -#include -#include -#include +#include +#include #include -#include +#include #include "leds-lp55xx-common.h" diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index e145ce984b1..d44c001e5e7 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -20,22 +20,15 @@ * 02110-1301 USA */ -#include -#include -#include -#include -#include -#include #include -#include -#include -#include +#include +#include +#include #include -#include -#include -#include +#include +#include #include -#include +#include #include "leds-lp55xx-common.h" -- cgit v1.2.3 From df4094d24e6328824a2dfe8e6f641bff9a484d68 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:26:59 +0900 Subject: leds-lp5521/5523: use new lp55xx common header The LP55xx common driver provides a new header, leds-lp55xx.h. This driver enables removing duplicate code for both drivers and making coherent driver structure. LP5521 and LP5523/55231 platform data were merged into one common file. Therefore, the LP5521/5523 platform code need to be fixed. This patch has been already acked. For ux500: https://lkml.org/lkml/2012/10/11/417 Acked-by: Linus Walleij For omap: https://lkml.org/lkml/2012/10/11/334 Acked-by: Tony Lindgren Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- arch/arm/mach-omap2/board-rx51-peripherals.c | 8 +-- arch/arm/mach-ux500/board-mop500.c | 14 +++--- include/linux/leds-lp5521.h | 73 ---------------------------- include/linux/leds-lp5523.h | 49 ------------------- 4 files changed, 11 insertions(+), 133 deletions(-) delete mode 100644 include/linux/leds-lp5521.h delete mode 100644 include/linux/leds-lp5523.h diff --git a/arch/arm/mach-omap2/board-rx51-peripherals.c b/arch/arm/mach-omap2/board-rx51-peripherals.c index cf07e289b4e..1a2c4db13e3 100644 --- a/arch/arm/mach-omap2/board-rx51-peripherals.c +++ b/arch/arm/mach-omap2/board-rx51-peripherals.c @@ -40,7 +40,7 @@ #include #include #include -#include +#include #include <../drivers/staging/iio/light/tsl2563.h> #include @@ -160,7 +160,7 @@ static struct tsl2563_platform_data rx51_tsl2563_platform_data = { #endif #if defined(CONFIG_LEDS_LP5523) || defined(CONFIG_LEDS_LP5523_MODULE) -static struct lp5523_led_config rx51_lp5523_led_config[] = { +static struct lp55xx_led_config rx51_lp5523_led_config[] = { { .chan_nr = 0, .led_current = 50, @@ -207,10 +207,10 @@ static void rx51_lp5523_enable(bool state) gpio_set_value(RX51_LP5523_CHIP_EN_GPIO, !!state); } -static struct lp5523_platform_data rx51_lp5523_platform_data = { +static struct lp55xx_platform_data rx51_lp5523_platform_data = { .led_config = rx51_lp5523_led_config, .num_channels = ARRAY_SIZE(rx51_lp5523_led_config), - .clock_mode = LP5523_CLOCK_AUTO, + .clock_mode = LP55XX_CLOCK_AUTO, .setup_resources = rx51_lp5523_setup, .release_resources = rx51_lp5523_release, .enable = rx51_lp5523_enable, diff --git a/arch/arm/mach-ux500/board-mop500.c b/arch/arm/mach-ux500/board-mop500.c index d453522edb0..b04684e16e2 100644 --- a/arch/arm/mach-ux500/board-mop500.c +++ b/arch/arm/mach-ux500/board-mop500.c @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include #include #include @@ -320,7 +320,7 @@ static struct tc3589x_platform_data mop500_tc35892_data = { .irq_base = MOP500_EGPIO_IRQ_BASE, }; -static struct lp5521_led_config lp5521_pri_led[] = { +static struct lp55xx_led_config lp5521_pri_led[] = { [0] = { .chan_nr = 0, .led_current = 0x2f, @@ -338,14 +338,14 @@ static struct lp5521_led_config lp5521_pri_led[] = { }, }; -static struct lp5521_platform_data __initdata lp5521_pri_data = { +static struct lp55xx_platform_data __initdata lp5521_pri_data = { .label = "lp5521_pri", .led_config = &lp5521_pri_led[0], .num_channels = 3, - .clock_mode = LP5521_CLOCK_EXT, + .clock_mode = LP55XX_CLOCK_EXT, }; -static struct lp5521_led_config lp5521_sec_led[] = { +static struct lp55xx_led_config lp5521_sec_led[] = { [0] = { .chan_nr = 0, .led_current = 0x2f, @@ -363,11 +363,11 @@ static struct lp5521_led_config lp5521_sec_led[] = { }, }; -static struct lp5521_platform_data __initdata lp5521_sec_data = { +static struct lp55xx_platform_data __initdata lp5521_sec_data = { .label = "lp5521_sec", .led_config = &lp5521_sec_led[0], .num_channels = 3, - .clock_mode = LP5521_CLOCK_EXT, + .clock_mode = LP55XX_CLOCK_EXT, }; static struct i2c_board_info __initdata mop500_i2c0_devices[] = { diff --git a/include/linux/leds-lp5521.h b/include/linux/leds-lp5521.h deleted file mode 100644 index 3f071ec019b..00000000000 --- a/include/linux/leds-lp5521.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * LP5521 LED chip driver. - * - * Copyright (C) 2010 Nokia Corporation - * - * Contact: Samu Onkalo - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - */ - -#ifndef __LINUX_LP5521_H -#define __LINUX_LP5521_H - -/* See Documentation/leds/leds-lp5521.txt */ - -struct lp5521_led_config { - char *name; - u8 chan_nr; - u8 led_current; /* mA x10, 0 if led is not connected */ - u8 max_current; -}; - -struct lp5521_led_pattern { - u8 *r; - u8 *g; - u8 *b; - u8 size_r; - u8 size_g; - u8 size_b; -}; - -#define LP5521_CLOCK_AUTO 0 -#define LP5521_CLOCK_INT 1 -#define LP5521_CLOCK_EXT 2 - -/* Bits in CONFIG register */ -#define LP5521_PWM_HF 0x40 /* PWM: 0 = 256Hz, 1 = 558Hz */ -#define LP5521_PWRSAVE_EN 0x20 /* 1 = Power save mode */ -#define LP5521_CP_MODE_OFF 0 /* Charge pump (CP) off */ -#define LP5521_CP_MODE_BYPASS 8 /* CP forced to bypass mode */ -#define LP5521_CP_MODE_1X5 0x10 /* CP forced to 1.5x mode */ -#define LP5521_CP_MODE_AUTO 0x18 /* Automatic mode selection */ -#define LP5521_R_TO_BATT 4 /* R out: 0 = CP, 1 = Vbat */ -#define LP5521_CLK_SRC_EXT 0 /* Ext-clk source (CLK_32K) */ -#define LP5521_CLK_INT 1 /* Internal clock */ -#define LP5521_CLK_AUTO 2 /* Automatic clock selection */ - -struct lp5521_platform_data { - struct lp5521_led_config *led_config; - u8 num_channels; - u8 clock_mode; - int (*setup_resources)(void); - void (*release_resources)(void); - void (*enable)(bool state); - const char *label; - u8 update_config; - struct lp5521_led_pattern *patterns; - int num_patterns; -}; - -#endif /* __LINUX_LP5521_H */ diff --git a/include/linux/leds-lp5523.h b/include/linux/leds-lp5523.h deleted file mode 100644 index 727877fb406..00000000000 --- a/include/linux/leds-lp5523.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * LP5523 LED Driver - * - * Copyright (C) 2010 Nokia Corporation - * - * Contact: Samu Onkalo - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - */ - -#ifndef __LINUX_LP5523_H -#define __LINUX_LP5523_H - -/* See Documentation/leds/leds-lp5523.txt */ - -struct lp5523_led_config { - const char *name; - u8 chan_nr; - u8 led_current; /* mA x10, 0 if led is not connected */ - u8 max_current; -}; - -#define LP5523_CLOCK_AUTO 0 -#define LP5523_CLOCK_INT 1 -#define LP5523_CLOCK_EXT 2 - -struct lp5523_platform_data { - struct lp5523_led_config *led_config; - u8 num_channels; - u8 clock_mode; - int (*setup_resources)(void); - void (*release_resources)(void); - void (*enable)(bool state); - const char *label; -}; - -#endif /* __LINUX_LP5523_H */ -- cgit v1.2.3 From a2387cb9f6974fede9bf2d731a78ca158acb5424 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:28:00 +0900 Subject: leds-lp5521/5523: add author and copyright description Now LP5521 and LP5523 drivers are based on new lp55xx structure. So the author and copyrights are updated. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 3 +++ drivers/leds/leds-lp5523.c | 3 +++ 2 files changed, 6 insertions(+) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index 3b6dcb0be7e..1001347ba70 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -2,8 +2,10 @@ * LP5521 LED chip driver. * * Copyright (C) 2010 Nokia Corporation + * Copyright (C) 2012 Texas Instruments * * Contact: Samu Onkalo + * Milo(Woogyom) Kim * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -479,5 +481,6 @@ static struct i2c_driver lp5521_driver = { module_i2c_driver(lp5521_driver); MODULE_AUTHOR("Mathias Nyman, Yuri Zaporozhets, Samu Onkalo"); +MODULE_AUTHOR("Milo Kim "); MODULE_DESCRIPTION("LP5521 LED engine"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index d44c001e5e7..229f734040a 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -2,8 +2,10 @@ * lp5523.c - LP5523 LED Driver * * Copyright (C) 2010 Nokia Corporation + * Copyright (C) 2012 Texas Instruments * * Contact: Samu Onkalo + * Milo(Woogyom) Kim * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -510,5 +512,6 @@ static struct i2c_driver lp5523_driver = { module_i2c_driver(lp5523_driver); MODULE_AUTHOR("Mathias Nyman "); +MODULE_AUTHOR("Milo Kim "); MODULE_DESCRIPTION("LP5523 LED engine"); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 109b833071b44a4a6f5dc56385025543ed15a500 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:28:44 +0900 Subject: leds-lp55xx: fix problem on removing LED attributes LP55XX common device attributes, 'led_current' and 'max_current' are created while loading the driver. Those are LED device attributes which are removed automatically on releasing led class devices - led_classdev_unregister(). Therefore, this duplicate code should be removed. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp55xx-common.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index 782ab84fe65..d9eb8415742 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -478,12 +478,9 @@ void lp55xx_unregister_leds(struct lp55xx_led *led, struct lp55xx_chip *chip) { int i; struct lp55xx_led *each; - struct kobject *kobj; for (i = 0; i < chip->num_leds; i++) { each = led + i; - kobj = &led->cdev.dev->kobj; - sysfs_remove_group(kobj, &lp55xx_led_attr_group); led_classdev_unregister(&each->cdev); flush_work(&each->brightness_work); } -- cgit v1.2.3 From c0285f8ca8b8a4a1830b6071d6680bde447ff51c Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 20:22:48 +0900 Subject: Documentation: leds: update LP55xx family devices Update changed platform data information. Add leds-lp55xx.txt which includes the firmware interface description. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- Documentation/leds/00-INDEX | 2 + Documentation/leds/leds-lp5521.txt | 63 +++----------------- Documentation/leds/leds-lp5523.txt | 27 ++------- Documentation/leds/leds-lp55xx.txt | 118 +++++++++++++++++++++++++++++++++++++ 4 files changed, 132 insertions(+), 78 deletions(-) create mode 100644 Documentation/leds/leds-lp55xx.txt diff --git a/Documentation/leds/00-INDEX b/Documentation/leds/00-INDEX index 5fefe374892..5246090ef15 100644 --- a/Documentation/leds/00-INDEX +++ b/Documentation/leds/00-INDEX @@ -6,5 +6,7 @@ leds-lp5521.txt - notes on how to use the leds-lp5521 driver. leds-lp5523.txt - notes on how to use the leds-lp5523 driver. +leds-lp55xx.txt + - description about lp55xx common driver. leds-lm3556.txt - notes on how to use the leds-lm3556 driver. diff --git a/Documentation/leds/leds-lp5521.txt b/Documentation/leds/leds-lp5521.txt index 0e542ab3d4a..270f5719633 100644 --- a/Documentation/leds/leds-lp5521.txt +++ b/Documentation/leds/leds-lp5521.txt @@ -17,19 +17,8 @@ lp5521:channelx, where x is 0 .. 2 All three channels can be also controlled using the engine micro programs. More details of the instructions can be found from the public data sheet. -Control interface for the engines: -x is 1 .. 3 -enginex_mode : disabled, load, run -enginex_load : store program (visible only in engine load mode) - -Example (start to blink the channel 2 led): -cd /sys/class/leds/lp5521:channel2/device -echo "load" > engine3_mode -echo "037f4d0003ff6000" > engine3_load -echo "run" > engine3_mode - -stop the engine: -echo "disabled" > engine3_mode +LP5521 has the internal program memory for running various LED patterns. +For the details, please refer to 'firmware' section in leds-lp55xx.txt sysfs contains a selftest entry. The test communicates with the chip and checks that @@ -47,7 +36,7 @@ The name of each channel can be configurable. If the name field is not defined, the default name will be set to 'xxxx:channelN' (XXXX : pdata->label or i2c client name, N : channel number) -static struct lp5521_led_config lp5521_led_config[] = { +static struct lp55xx_led_config lp5521_led_config[] = { { .name = "red", .chan_nr = 0, @@ -81,10 +70,10 @@ static void lp5521_enable(bool state) /* Control of chip enable signal */ } -static struct lp5521_platform_data lp5521_platform_data = { +static struct lp55xx_platform_data lp5521_platform_data = { .led_config = lp5521_led_config, .num_channels = ARRAY_SIZE(lp5521_led_config), - .clock_mode = LP5521_CLOCK_EXT, + .clock_mode = LP55XX_CLOCK_EXT, .setup_resources = lp5521_setup, .release_resources = lp5521_release, .enable = lp5521_enable, @@ -105,47 +94,9 @@ example of update_config : LP5521_CP_MODE_AUTO | LP5521_R_TO_BATT | \ LP5521_CLK_INT) -static struct lp5521_platform_data lp5521_pdata = { +static struct lp55xx_platform_data lp5521_pdata = { .led_config = lp5521_led_config, .num_channels = ARRAY_SIZE(lp5521_led_config), - .clock_mode = LP5521_CLOCK_INT, + .clock_mode = LP55XX_CLOCK_INT, .update_config = LP5521_CONFIGS, }; - -LED patterns : LP5521 has autonomous operation without external control. -Pattern data can be defined in the platform data. - -example of led pattern data : - -/* RGB(50,5,0) 500ms on, 500ms off, infinite loop */ -static u8 pattern_red[] = { - 0x40, 0x32, 0x60, 0x00, 0x40, 0x00, 0x60, 0x00, - }; - -static u8 pattern_green[] = { - 0x40, 0x05, 0x60, 0x00, 0x40, 0x00, 0x60, 0x00, - }; - -static struct lp5521_led_pattern board_led_patterns[] = { - { - .r = pattern_red, - .g = pattern_green, - .size_r = ARRAY_SIZE(pattern_red), - .size_g = ARRAY_SIZE(pattern_green), - }, -}; - -static struct lp5521_platform_data lp5521_platform_data = { - .led_config = lp5521_led_config, - .num_channels = ARRAY_SIZE(lp5521_led_config), - .clock_mode = LP5521_CLOCK_EXT, - .patterns = board_led_patterns, - .num_patterns = ARRAY_SIZE(board_led_patterns), -}; - -Then predefined led pattern(s) can be executed via the sysfs. -To start the pattern #1, -# echo 1 > /sys/bus/i2c/devices/xxxx/led_pattern -(xxxx : i2c bus & slave address) -To end the pattern, -# echo 0 > /sys/bus/i2c/devices/xxxx/led_pattern diff --git a/Documentation/leds/leds-lp5523.txt b/Documentation/leds/leds-lp5523.txt index c2743f59f9a..899fdad509f 100644 --- a/Documentation/leds/leds-lp5523.txt +++ b/Documentation/leds/leds-lp5523.txt @@ -27,25 +27,8 @@ c) Default If both fields are NULL, 'lp5523' is used by default. /sys/class/leds/lp5523:channelN (N: 0 ~ 8) -The chip provides 3 engines. Each engine can control channels without -interaction from the main CPU. Details of the micro engine code can be found -from the public data sheet. Leds can be muxed to different channels. - -Control interface for the engines: -x is 1 .. 3 -enginex_mode : disabled, load, run -enginex_load : microcode load (visible only in load mode) -enginex_leds : led mux control (visible only in load mode) - -cd /sys/class/leds/lp5523:channel2/device -echo "load" > engine3_mode -echo "9d80400004ff05ff437f0000" > engine3_load -echo "111111111" > engine3_leds -echo "run" > engine3_mode - -sysfs contains a selftest entry. It measures each channel -voltage level and checks if it looks reasonable. If the level is too high, -the led is missing; if the level is too low, there is a short circuit. +LP5523 has the internal program memory for running various LED patterns. +For the details, please refer to 'firmware' section in leds-lp55xx.txt Selftest uses always the current from the platform data. @@ -58,7 +41,7 @@ Example platform data: Note - chan_nr can have values between 0 and 8. -static struct lp5523_led_config lp5523_led_config[] = { +static struct lp55xx_led_config lp5523_led_config[] = { { .name = "D1", .chan_nr = 0, @@ -88,10 +71,10 @@ static void lp5523_enable(bool state) /* Control chip enable signal */ } -static struct lp5523_platform_data lp5523_platform_data = { +static struct lp55xx_platform_data lp5523_platform_data = { .led_config = lp5523_led_config, .num_channels = ARRAY_SIZE(lp5523_led_config), - .clock_mode = LP5523_CLOCK_EXT, + .clock_mode = LP55XX_CLOCK_EXT, .setup_resources = lp5523_setup, .release_resources = lp5523_release, .enable = lp5523_enable, diff --git a/Documentation/leds/leds-lp55xx.txt b/Documentation/leds/leds-lp55xx.txt new file mode 100644 index 00000000000..ced41868d2d --- /dev/null +++ b/Documentation/leds/leds-lp55xx.txt @@ -0,0 +1,118 @@ +LP5521/LP5523/LP55231 Common Driver +=================================== + +Authors: Milo(Woogyom) Kim + +Description +----------- +LP5521, LP5523/55231 have common features as below. + + Register access via the I2C + Device initialization/deinitialization + Create LED class devices for multiple output channels + Device attributes for user-space interface + Program memory for running LED patterns + +The LP55xx common driver provides these features using exported functions. + lp55xx_init_device() / lp55xx_deinit_device() + lp55xx_register_leds() / lp55xx_unregister_leds() + lp55xx_regsister_sysfs() / lp55xx_unregister_sysfs() + +( Driver Structure Data ) + +In lp55xx common driver, two different data structure is used. + +o lp55xx_led + control multi output LED channels such as led current, channel index. +o lp55xx_chip + general chip control such like the I2C and platform data. + +For example, LP5521 has maximum 3 LED channels. +LP5523/55231 has 9 output channels. + +lp55xx_chip for LP5521 ... lp55xx_led #1 + lp55xx_led #2 + lp55xx_led #3 + +lp55xx_chip for LP5523 ... lp55xx_led #1 + lp55xx_led #2 + . + . + lp55xx_led #9 + +( Chip Dependent Code ) + +To support device specific configurations, special structure +'lpxx_device_config' is used. + + Maximum number of channels + Reset command, chip enable command + Chip specific initialization + Brightness control register access + Setting LED output current + Program memory address access for running patterns + Additional device specific attributes + +( Firmware Interface ) + +LP55xx family devices have the internal program memory for running +various LED patterns. +This pattern data is saved as a file in the user-land or +hex byte string is written into the memory through the I2C. +LP55xx common driver supports the firmware interface. + +LP55xx chips have three program engines. +To load and run the pattern, the programming sequence is following. + (1) Select an engine number (1/2/3) + (2) Mode change to load + (3) Write pattern data into selected area + (4) Mode change to run + +The LP55xx common driver provides simple interfaces as below. +select_engine : Select which engine is used for running program +run_engine : Start program which is loaded via the firmware interface +firmware : Load program data + +For example, run blinking pattern in engine #1 of LP5521 +echo 1 > /sys/bus/i2c/devices/xxxx/select_engine +echo 1 > /sys/class/firmware/lp5521/loading +echo "4000600040FF6000" > /sys/class/firmware/lp5521/data +echo 0 > /sys/class/firmware/lp5521/loading +echo 1 > /sys/bus/i2c/devices/xxxx/run_engine + +For example, run blinking pattern in engine #3 of LP55231 +echo 3 > /sys/bus/i2c/devices/xxxx/select_engine +echo 1 > /sys/class/firmware/lp55231/loading +echo "9d0740ff7e0040007e00a0010000" > /sys/class/firmware/lp55231/data +echo 0 > /sys/class/firmware/lp55231/loading +echo 1 > /sys/bus/i2c/devices/xxxx/run_engine + +To start blinking patterns in engine #2 and #3 simultaneously, +for idx in 2 3 +do + echo $idx > /sys/class/leds/red/device/select_engine + sleep 0.1 + echo 1 > /sys/class/firmware/lp5521/loading + echo "4000600040FF6000" > /sys/class/firmware/lp5521/data + echo 0 > /sys/class/firmware/lp5521/loading +done +echo 1 > /sys/class/leds/red/device/run_engine + +Here is another example for LP5523. +echo 2 > /sys/bus/i2c/devices/xxxx/select_engine +echo 1 > /sys/class/firmware/lp5523/loading +echo "9d80400004ff05ff437f0000" > /sys/class/firmware/lp5523/data +echo 0 > /sys/class/firmware/lp5523/loading +echo 1 > /sys/bus/i2c/devices/xxxx/run_engine + +As soon as 'loading' is set to 0, registered callback is called. +Inside the callback, the selected engine is loaded and memory is updated. +To run programmed pattern, 'run_engine' attribute should be enabled. + +( 'run_engine' and 'firmware_cb' ) +The sequence of running the program data is common. +But each device has own specific register addresses for commands. +To support this, 'run_engine' and 'firmware_cb' are configurable in each driver. +run_engine : Control the selected engine +firmware_cb : The callback function after loading the firmware is done. + Chip specific commands for loading and updating program memory. -- cgit v1.2.3 From ad0ba85fab7da9e634c9da4f96315c8d0b9febcb Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 23 Jan 2013 00:35:04 -0800 Subject: leds: leds-pwm: make it depend on PWM and not HAVE_PWM The correct dependency for the leds-pwm is PWM and not HAVE_PWM since PWM drivers now have their own subsystem. Signed-off-by: Peter Ujfalusi Signed-off-by: Bryan Wu --- drivers/leds/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index fc680a9ed84..78b354f3212 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -320,7 +320,7 @@ config LEDS_DAC124S085 config LEDS_PWM tristate "PWM driven LED Support" depends on LEDS_CLASS - depends on HAVE_PWM + depends on PWM help This option enables support for pwm driven LEDs -- cgit v1.2.3 From ef754e88e35f86d9704f79ac8dace8c66f367164 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sun, 27 Jan 2013 01:14:14 -0800 Subject: leds: tca6507: Use of_get_child_count() Signed-off-by: Axel Lin Signed-off-by: Bryan Wu --- drivers/leds/leds-tca6507.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/drivers/leds/leds-tca6507.c b/drivers/leds/leds-tca6507.c index 220fc7fbf1f..070ba0741b2 100644 --- a/drivers/leds/leds-tca6507.c +++ b/drivers/leds/leds-tca6507.c @@ -674,14 +674,10 @@ tca6507_led_dt_init(struct i2c_client *client) struct device_node *np = client->dev.of_node, *child; struct tca6507_platform_data *pdata; struct led_info *tca_leds; - int count = 0; + int count; - for_each_child_of_node(np, child) - count++; - if (!count) - return ERR_PTR(-ENODEV); - - if (count > NUM_LEDS) + count = of_get_child_count(np); + if (!count || count > NUM_LEDS) return ERR_PTR(-ENODEV); tca_leds = devm_kzalloc(&client->dev, -- cgit v1.2.3 From 61d4eb2724283e85b37ed2fe13390366d7a6db74 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sun, 27 Jan 2013 04:56:57 -0800 Subject: leds: 88pm860x: Add missing of_node_put() of_find_node_by_name() returns a node pointer with refcount incremented, use of_node_put() on it when done. of_find_node_by_name() will call of_node_put() against from parameter, thus we also need to call of_node_get(from) before calling of_find_node_by_name(). Signed-off-by: Axel Lin Signed-off-by: Bryan Wu --- drivers/leds/leds-88pm860x.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/leds/leds-88pm860x.c b/drivers/leds/leds-88pm860x.c index 6be2edd4117..f5b9ea31579 100644 --- a/drivers/leds/leds-88pm860x.c +++ b/drivers/leds/leds-88pm860x.c @@ -128,8 +128,10 @@ static void pm860x_led_set(struct led_classdev *cdev, static int pm860x_led_dt_init(struct platform_device *pdev, struct pm860x_led *data) { - struct device_node *nproot = pdev->dev.parent->of_node, *np; + struct device_node *nproot, *np; int iset = 0; + + nproot = of_node_get(pdev->dev.parent->of_node); if (!nproot) return -ENODEV; nproot = of_find_node_by_name(nproot, "leds"); @@ -145,6 +147,7 @@ static int pm860x_led_dt_init(struct platform_device *pdev, break; } } + of_node_put(nproot); return 0; } #else -- cgit v1.2.3 From 4b07c5d5123f76487c61cf9dc3f987d0b8c88a94 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 4 Feb 2013 18:01:21 -0800 Subject: leds: leds-sunfire: use dev_err()/pr_err() instead of printk() Fixed the checkpatch errors and warnings as below: ERROR: spaces required around that '=' (ctx:VxW) WARNING: Prefer netdev_err(netdev, ... then dev_err(dev, ... then pr_err(... to printk(KERN_ERR ... Signed-off-by: Jingoo Han Signed-off-by: Bryan Wu --- drivers/leds/leds-sunfire.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/drivers/leds/leds-sunfire.c b/drivers/leds/leds-sunfire.c index 07ff5a3a6ce..89792990088 100644 --- a/drivers/leds/leds-sunfire.c +++ b/drivers/leds/leds-sunfire.c @@ -3,6 +3,8 @@ * Copyright (C) 2008 David S. Miller */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -14,9 +16,6 @@ #include #include -#define DRIVER_NAME "leds-sunfire" -#define PFX DRIVER_NAME ": " - MODULE_AUTHOR("David S. Miller (davem@davemloft.net)"); MODULE_DESCRIPTION("Sun Fire LED driver"); MODULE_LICENSE("GPL"); @@ -130,14 +129,14 @@ static int sunfire_led_generic_probe(struct platform_device *pdev, int i, err; if (pdev->num_resources != 1) { - printk(KERN_ERR PFX "Wrong number of resources %d, should be 1\n", + dev_err(&pdev->dev, "Wrong number of resources %d, should be 1\n", pdev->num_resources); return -EINVAL; } p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL); if (!p) { - printk(KERN_ERR PFX "Could not allocate struct sunfire_drvdata\n"); + dev_err(&pdev->dev, "Could not allocate struct sunfire_drvdata\n"); return -ENOMEM; } @@ -152,7 +151,7 @@ static int sunfire_led_generic_probe(struct platform_device *pdev, err = led_classdev_register(&pdev->dev, lp); if (err) { - printk(KERN_ERR PFX "Could not register %s LED\n", + dev_err(&pdev->dev, "Could not register %s LED\n", lp->name); for (i--; i >= 0; i--) led_classdev_unregister(&p->leds[i].led_cdev); @@ -188,7 +187,7 @@ static struct led_type clockboard_led_types[NUM_LEDS_PER_BOARD] = { { .name = "clockboard-right", .handler = clockboard_right_set, - .default_trigger= "heartbeat", + .default_trigger = "heartbeat", }, }; @@ -209,7 +208,7 @@ static struct led_type fhc_led_types[NUM_LEDS_PER_BOARD] = { { .name = "fhc-right", .handler = fhc_right_set, - .default_trigger= "heartbeat", + .default_trigger = "heartbeat", }, }; @@ -244,13 +243,13 @@ static int __init sunfire_leds_init(void) int err = platform_driver_register(&sunfire_clockboard_led_driver); if (err) { - printk(KERN_ERR PFX "Could not register clock board LED driver\n"); + pr_err("Could not register clock board LED driver\n"); return err; } err = platform_driver_register(&sunfire_fhc_led_driver); if (err) { - printk(KERN_ERR PFX "Could not register FHC LED driver\n"); + pr_err("Could not register FHC LED driver\n"); platform_driver_unregister(&sunfire_clockboard_led_driver); } -- cgit v1.2.3