lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Date:	Fri, 12 Jul 2013 10:46:01 -0700
From:	Bryan Wu <cooloney@...il.com>
To:	"Kim, Milo" <Milo.Kim@...com>
Cc:	"rpurdie@...ys.net" <rpurdie@...ys.net>,
	"linux-leds@...r.kernel.org" <linux-leds@...r.kernel.org>,
	"linux-kernel@...r.kernel.org" <linux-kernel@...r.kernel.org>
Subject: Re: [PATCH re-send] leds: support new LP8501 device - another LP55xx common

On Tue, Jul 9, 2013 at 2:11 AM, Kim, Milo <Milo.Kim@...com> wrote:
> LP8501 can drive up to 9 channels like LP5523.
> LEDs can be controlled directly via the I2C and programmable engines are
> supported.
>
> LP55xx common driver
>  LP8501 is one of LP55xx family device, so LP55xx common code are used.
>  Chip specific data is defined in the structure, 'lp55xx_device_config'.
>
> Differences between LP8501 and LP5523
>  Different register layout for LED output control and others.
>  LP8501 specific feature for separate output power selection.
>  LP8501 doesn't support external clock detection.
>  Different programming engine data.
>
> LP8501 specific feature - output power selection
>  Output channels are selected by power selection - Vout or Vdd.
>  Separate power for VDD1-6 and VDD7-9 are available.
>  It is configurable in the platform data.
>  To support this feature, LP55xx DT structure and header are changed.
>  Device tree binding is updated as well.
>
> LED pattern data
>  Example pattern data is updated in the driver documentation.
>
> Signed-off-by: Milo Kim <milo.kim@...com>

This patch looks good to me, I will merge it soon.

Thanks,
-Bryan

> ---
> This patch is re-sent with additional Cc lists.
>
>  .../devicetree/bindings/leds/leds-lp55xx.txt       |   72 +++-
>  Documentation/leds/leds-lp55xx.txt                 |   30 +-
>  drivers/leds/Kconfig                               |   18 +-
>  drivers/leds/Makefile                              |    1 +
>  drivers/leds/leds-lp55xx-common.c                  |    3 +
>  drivers/leds/leds-lp8501.c                         |  410 ++++++++++++++++++++
>  include/linux/platform_data/leds-lp55xx.h          |   10 +
>  7 files changed, 537 insertions(+), 7 deletions(-)
>  create mode 100644 drivers/leds/leds-lp8501.c
>
> diff --git a/Documentation/devicetree/bindings/leds/leds-lp55xx.txt b/Documentation/devicetree/bindings/leds/leds-lp55xx.txt
> index d517688..a61727f 100644
> --- a/Documentation/devicetree/bindings/leds/leds-lp55xx.txt
> +++ b/Documentation/devicetree/bindings/leds/leds-lp55xx.txt
> @@ -1,7 +1,7 @@
>  Binding for TI/National Semiconductor LP55xx Led Drivers
>
>  Required properties:
> -- compatible: "national,lp5521" or "national,lp5523" or "ti,lp5562"
> +- compatible: "national,lp5521" or "national,lp5523" or "ti,lp5562" or "ti,lp8501"
>  - reg: I2C slave address
>  - clock-mode: Input clock mode, (0: automode, 1: internal, 2: external)
>
> @@ -11,6 +11,11 @@ Each child has own specific current settings
>
>  Optional properties:
>  - label: Used for naming LEDs
> +- pwr-sel: LP8501 specific property. Power selection for output channels.
> +         0: D1~9 are connected to VDD
> +         1: D1~6 with VDD, D7~9 with VOUT
> +         2: D1~6 with VOUT, D7~9 with VDD
> +         3: D1~9 are connected to VOUT
>
>  Alternatively, each child can have specific channel name
>  - chan-name: Name of each channel name
> @@ -145,3 +150,68 @@ lp5562@30 {
>                 max-cur = /bits/ 8 <0x60>;
>         };
>  };
> +
> +example 4) LP8501
> +9 channels are defined. The 'pwr-sel' is LP8501 specific property.
> +Others are same as LP5523.
> +
> +lp8501@32 {
> +       compatible = "ti,lp8501";
> +       reg = <0x32>;
> +       clock-mode = /bits/ 8 <2>;
> +       pwr-sel = /bits/ 8 <3>; /* D1~9 connected to VOUT */
> +
> +       chan0 {
> +               chan-name = "d1";
> +               led-cur = /bits/ 8 <0x14>;
> +               max-cur = /bits/ 8 <0x20>;
> +       };
> +
> +       chan1 {
> +               chan-name = "d2";
> +               led-cur = /bits/ 8 <0x14>;
> +               max-cur = /bits/ 8 <0x20>;
> +       };
> +
> +       chan2 {
> +               chan-name = "d3";
> +               led-cur = /bits/ 8 <0x14>;
> +               max-cur = /bits/ 8 <0x20>;
> +       };
> +
> +       chan3 {
> +               chan-name = "d4";
> +               led-cur = /bits/ 8 <0x14>;
> +               max-cur = /bits/ 8 <0x20>;
> +       };
> +
> +       chan4 {
> +               chan-name = "d5";
> +               led-cur = /bits/ 8 <0x14>;
> +               max-cur = /bits/ 8 <0x20>;
> +       };
> +
> +       chan5 {
> +               chan-name = "d6";
> +               led-cur = /bits/ 8 <0x14>;
> +               max-cur = /bits/ 8 <0x20>;
> +       };
> +
> +       chan6 {
> +               chan-name = "d7";
> +               led-cur = /bits/ 8 <0x14>;
> +               max-cur = /bits/ 8 <0x20>;
> +       };
> +
> +       chan7 {
> +               chan-name = "d8";
> +               led-cur = /bits/ 8 <0x14>;
> +               max-cur = /bits/ 8 <0x20>;
> +       };
> +
> +       chan8 {
> +               chan-name = "d9";
> +               led-cur = /bits/ 8 <0x14>;
> +               max-cur = /bits/ 8 <0x20>;
> +       };
> +};
> diff --git a/Documentation/leds/leds-lp55xx.txt b/Documentation/leds/leds-lp55xx.txt
> index eec8fa2..82713ff 100644
> --- a/Documentation/leds/leds-lp55xx.txt
> +++ b/Documentation/leds/leds-lp55xx.txt
> @@ -1,11 +1,11 @@
> -LP5521/LP5523/LP55231 Common Driver
> -===================================
> +LP5521/LP5523/LP55231/LP5562/LP8501 Common Driver
> +=================================================
>
>  Authors: Milo(Woogyom) Kim <milo.kim@...com>
>
>  Description
>  -----------
> -LP5521, LP5523/55231 and LP5562 have common features as below.
> +LP5521, LP5523/55231, LP5562 and LP8501 have common features as below.
>
>    Register access via the I2C
>    Device initialization/deinitialization
> @@ -109,6 +109,30 @@ 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.
>
> +The pattern sqeuence of LP8501 is same as LP5523.
> +However pattern data is specific.
> +Ex 1) Engine 1 is used
> +echo 1 > /sys/bus/i2c/devices/xxxx/select_engine
> +echo 1 > /sys/class/firmware/lp8501/loading
> +echo "9d0140ff7e0040007e00a001c000" > /sys/class/firmware/lp8501/data
> +echo 0 > /sys/class/firmware/lp8501/loading
> +echo 1 > /sys/bus/i2c/devices/xxxx/run_engine
> +
> +Ex 2) Engine 2 and 3 are used at the same time
> +echo 2 > /sys/bus/i2c/devices/xxxx/select_engine
> +sleep 1
> +echo 1 > /sys/class/firmware/lp8501/loading
> +echo "9d0140ff7e0040007e00a001c000" > /sys/class/firmware/lp8501/data
> +echo 0 > /sys/class/firmware/lp8501/loading
> +sleep 1
> +echo 3 > /sys/bus/i2c/devices/xxxx/select_engine
> +sleep 1
> +echo 1 > /sys/class/firmware/lp8501/loading
> +echo "9d0340ff7e0040007e00a001c000" > /sys/class/firmware/lp8501/data
> +echo 0 > /sys/class/firmware/lp8501/loading
> +sleep 1
> +echo 1 > /sys/class/leds/d1/device/run_engine
> +
>  ( 'run_engine' and 'firmware_cb' )
>  The sequence of running the program data is common.
>  But each device has own specific register addresses for commands.
> diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
> index e43402d..77329ce 100644
> --- a/drivers/leds/Kconfig
> +++ b/drivers/leds/Kconfig
> @@ -194,11 +194,11 @@ config LEDS_LP3944
>           module will be called leds-lp3944.
>
>  config LEDS_LP55XX_COMMON
> -       tristate "Common Driver for TI/National LP5521, LP5523/55231 and LP5562"
> -       depends on LEDS_LP5521 || LEDS_LP5523 || LEDS_LP5562
> +       tristate "Common Driver for TI/National LP5521/5523/55231/5562/8501"
> +       depends on LEDS_LP5521 || LEDS_LP5523 || LEDS_LP5562 || LEDS_LP8501
>         select FW_LOADER
>         help
> -         This option supports common operations for LP5521 and LP5523/55231
> +         This option supports common operations for LP5521/5523/55231/5562/8501
>           devices.
>
>  config LEDS_LP5521
> @@ -232,6 +232,18 @@ config LEDS_LP5562
>           Driver provides direct control via LED class and interface for
>           programming the engines.
>
> +config LEDS_LP8501
> +       tristate "LED Support for TI LP8501 LED driver chip"
> +       depends on LEDS_CLASS && I2C
> +       select LEDS_LP55XX_COMMON
> +       help
> +         If you say yes here you get support for TI LP8501 LED driver.
> +         It is 9 channel chip with programmable engines.
> +         Driver provides direct control via LED class and interface for
> +         programming the engines.
> +         It is similar as LP5523, but output power selection is available.
> +         And register layout and engine program schemes are different.
> +
>  config LEDS_LP8788
>         tristate "LED support for the TI LP8788 PMIC"
>         depends on LEDS_CLASS
> diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
> index ac28977..3013113 100644
> --- a/drivers/leds/Makefile
> +++ b/drivers/leds/Makefile
> @@ -27,6 +27,7 @@ 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_LP5562)              += leds-lp5562.o
> +obj-$(CONFIG_LEDS_LP8501)              += leds-lp8501.o
>  obj-$(CONFIG_LEDS_LP8788)              += leds-lp8788.o
>  obj-$(CONFIG_LEDS_TCA6507)             += leds-tca6507.o
>  obj-$(CONFIG_LEDS_CLEVO_MAIL)          += leds-clevo-mail.o
> diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c
> index c2fecd4..351825b 100644
> --- a/drivers/leds/leds-lp55xx-common.c
> +++ b/drivers/leds/leds-lp55xx-common.c
> @@ -593,6 +593,9 @@ int lp55xx_of_populate_pdata(struct device *dev, struct device_node *np)
>         of_property_read_string(np, "label", &pdata->label);
>         of_property_read_u8(np, "clock-mode", &pdata->clock_mode);
>
> +       /* LP8501 specific */
> +       of_property_read_u8(np, "pwr-sel", (u8 *)&pdata->pwr_sel);
> +
>         dev->platform_data = pdata;
>
>         return 0;
> diff --git a/drivers/leds/leds-lp8501.c b/drivers/leds/leds-lp8501.c
> new file mode 100644
> index 0000000..4573b94
> --- /dev/null
> +++ b/drivers/leds/leds-lp8501.c
> @@ -0,0 +1,410 @@
> +/*
> + * TI LP8501 9 channel LED Driver
> + *
> + * Copyright (C) 2013 Texas Instruments
> + *
> + * Author: Milo(Woogyom) Kim <milo.kim@...com>
> + *
> + * 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.
> + *
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/firmware.h>
> +#include <linux/i2c.h>
> +#include <linux/init.h>
> +#include <linux/leds.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/platform_data/leds-lp55xx.h>
> +#include <linux/slab.h>
> +
> +#include "leds-lp55xx-common.h"
> +
> +#define LP8501_PROGRAM_LENGTH          32
> +#define LP8501_MAX_LEDS                        9
> +
> +/* Registers */
> +#define LP8501_REG_ENABLE              0x00
> +#define LP8501_ENABLE                  BIT(6)
> +#define LP8501_EXEC_M                  0x3F
> +#define LP8501_EXEC_ENG1_M             0x30
> +#define LP8501_EXEC_ENG2_M             0x0C
> +#define LP8501_EXEC_ENG3_M             0x03
> +#define LP8501_RUN_ENG1                        0x20
> +#define LP8501_RUN_ENG2                        0x08
> +#define LP8501_RUN_ENG3                        0x02
> +
> +#define LP8501_REG_OP_MODE             0x01
> +#define LP8501_MODE_ENG1_M             0x30
> +#define LP8501_MODE_ENG2_M             0x0C
> +#define LP8501_MODE_ENG3_M             0x03
> +#define LP8501_LOAD_ENG1               0x10
> +#define LP8501_LOAD_ENG2               0x04
> +#define LP8501_LOAD_ENG3               0x01
> +
> +#define LP8501_REG_PWR_CONFIG          0x05
> +#define LP8501_PWR_CONFIG_M            0x03
> +
> +#define LP8501_REG_LED_PWM_BASE                0x16
> +
> +#define LP8501_REG_LED_CURRENT_BASE    0x26
> +
> +#define LP8501_REG_CONFIG              0x36
> +#define LP8501_PWM_PSAVE               BIT(7)
> +#define LP8501_AUTO_INC                        BIT(6)
> +#define LP8501_PWR_SAVE                        BIT(5)
> +#define LP8501_CP_AUTO                 0x18
> +#define LP8501_INT_CLK                 BIT(0)
> +#define LP8501_DEFAULT_CFG     \
> +       (LP8501_PWM_PSAVE | LP8501_AUTO_INC | LP8501_PWR_SAVE | LP8501_CP_AUTO)
> +
> +#define LP8501_REG_RESET               0x3D
> +#define LP8501_RESET                   0xFF
> +
> +#define LP8501_REG_PROG_PAGE_SEL       0x4F
> +#define LP8501_PAGE_ENG1               0
> +#define LP8501_PAGE_ENG2               1
> +#define LP8501_PAGE_ENG3               2
> +
> +#define LP8501_REG_PROG_MEM            0x50
> +
> +#define LP8501_ENG1_IS_LOADING(mode)   \
> +       ((mode & LP8501_MODE_ENG1_M) == LP8501_LOAD_ENG1)
> +#define LP8501_ENG2_IS_LOADING(mode)   \
> +       ((mode & LP8501_MODE_ENG2_M) == LP8501_LOAD_ENG2)
> +#define LP8501_ENG3_IS_LOADING(mode)   \
> +       ((mode & LP8501_MODE_ENG3_M) == LP8501_LOAD_ENG3)
> +
> +static inline void lp8501_wait_opmode_done(void)
> +{
> +       usleep_range(1000, 2000);
> +}
> +
> +static void lp8501_set_led_current(struct lp55xx_led *led, u8 led_current)
> +{
> +       led->led_current = led_current;
> +       lp55xx_write(led->chip, LP8501_REG_LED_CURRENT_BASE + led->chan_nr,
> +               led_current);
> +}
> +
> +static int lp8501_post_init_device(struct lp55xx_chip *chip)
> +{
> +       int ret;
> +       u8 val = LP8501_DEFAULT_CFG;
> +
> +       ret = lp55xx_write(chip, LP8501_REG_ENABLE, LP8501_ENABLE);
> +       if (ret)
> +               return ret;
> +
> +       /* Chip startup time is 500 us, 1 - 2 ms gives some margin */
> +       usleep_range(1000, 2000);
> +
> +       if (chip->pdata->clock_mode != LP55XX_CLOCK_EXT)
> +               val |= LP8501_INT_CLK;
> +
> +       ret = lp55xx_write(chip, LP8501_REG_CONFIG, val);
> +       if (ret)
> +               return ret;
> +
> +       /* Power selection for each output */
> +       return lp55xx_update_bits(chip, LP8501_REG_PWR_CONFIG,
> +                               LP8501_PWR_CONFIG_M, chip->pdata->pwr_sel);
> +}
> +
> +static void lp8501_load_engine(struct lp55xx_chip *chip)
> +{
> +       enum lp55xx_engine_index idx = chip->engine_idx;
> +       u8 mask[] = {
> +               [LP55XX_ENGINE_1] = LP8501_MODE_ENG1_M,
> +               [LP55XX_ENGINE_2] = LP8501_MODE_ENG2_M,
> +               [LP55XX_ENGINE_3] = LP8501_MODE_ENG3_M,
> +       };
> +
> +       u8 val[] = {
> +               [LP55XX_ENGINE_1] = LP8501_LOAD_ENG1,
> +               [LP55XX_ENGINE_2] = LP8501_LOAD_ENG2,
> +               [LP55XX_ENGINE_3] = LP8501_LOAD_ENG3,
> +       };
> +
> +       u8 page_sel[] = {
> +               [LP55XX_ENGINE_1] = LP8501_PAGE_ENG1,
> +               [LP55XX_ENGINE_2] = LP8501_PAGE_ENG2,
> +               [LP55XX_ENGINE_3] = LP8501_PAGE_ENG3,
> +       };
> +
> +       lp55xx_update_bits(chip, LP8501_REG_OP_MODE, mask[idx], val[idx]);
> +
> +       lp8501_wait_opmode_done();
> +
> +       lp55xx_write(chip, LP8501_REG_PROG_PAGE_SEL, page_sel[idx]);
> +}
> +
> +static void lp8501_stop_engine(struct lp55xx_chip *chip)
> +{
> +       lp55xx_write(chip, LP8501_REG_OP_MODE, 0);
> +       lp8501_wait_opmode_done();
> +}
> +
> +static void lp8501_turn_off_channels(struct lp55xx_chip *chip)
> +{
> +       int i;
> +
> +       for (i = 0; i < LP8501_MAX_LEDS; i++)
> +               lp55xx_write(chip, LP8501_REG_LED_PWM_BASE + i, 0);
> +}
> +
> +static void lp8501_run_engine(struct lp55xx_chip *chip, bool start)
> +{
> +       int ret;
> +       u8 mode;
> +       u8 exec;
> +
> +       /* stop engine */
> +       if (!start) {
> +               lp8501_stop_engine(chip);
> +               lp8501_turn_off_channels(chip);
> +               return;
> +       }
> +
> +       /*
> +        * To run the engine,
> +        * operation mode and enable register should updated at the same time
> +        */
> +
> +       ret = lp55xx_read(chip, LP8501_REG_OP_MODE, &mode);
> +       if (ret)
> +               return;
> +
> +       ret = lp55xx_read(chip, LP8501_REG_ENABLE, &exec);
> +       if (ret)
> +               return;
> +
> +       /* change operation mode to RUN only when each engine is loading */
> +       if (LP8501_ENG1_IS_LOADING(mode)) {
> +               mode = (mode & ~LP8501_MODE_ENG1_M) | LP8501_RUN_ENG1;
> +               exec = (exec & ~LP8501_EXEC_ENG1_M) | LP8501_RUN_ENG1;
> +       }
> +
> +       if (LP8501_ENG2_IS_LOADING(mode)) {
> +               mode = (mode & ~LP8501_MODE_ENG2_M) | LP8501_RUN_ENG2;
> +               exec = (exec & ~LP8501_EXEC_ENG2_M) | LP8501_RUN_ENG2;
> +       }
> +
> +       if (LP8501_ENG3_IS_LOADING(mode)) {
> +               mode = (mode & ~LP8501_MODE_ENG3_M) | LP8501_RUN_ENG3;
> +               exec = (exec & ~LP8501_EXEC_ENG3_M) | LP8501_RUN_ENG3;
> +       }
> +
> +       lp55xx_write(chip, LP8501_REG_OP_MODE, mode);
> +       lp8501_wait_opmode_done();
> +
> +       lp55xx_update_bits(chip, LP8501_REG_ENABLE, LP8501_EXEC_M, exec);
> +}
> +
> +static int lp8501_update_program_memory(struct lp55xx_chip *chip,
> +                                       const u8 *data, size_t size)
> +{
> +       u8 pattern[LP8501_PROGRAM_LENGTH] = {0};
> +       unsigned cmd;
> +       char c[3];
> +       int update_size;
> +       int nrchars;
> +       int offset = 0;
> +       int ret;
> +       int i;
> +
> +       /* clear program memory before updating */
> +       for (i = 0; i < LP8501_PROGRAM_LENGTH; i++)
> +               lp55xx_write(chip, LP8501_REG_PROG_MEM + i, 0);
> +
> +       i = 0;
> +       while ((offset < size - 1) && (i < LP8501_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;
> +
> +       update_size = i;
> +       for (i = 0; i < update_size; i++)
> +               lp55xx_write(chip, LP8501_REG_PROG_MEM + i, pattern[i]);
> +
> +       return 0;
> +
> +err:
> +       dev_err(&chip->cl->dev, "wrong pattern format\n");
> +       return -EINVAL;
> +}
> +
> +static void lp8501_firmware_loaded(struct lp55xx_chip *chip)
> +{
> +       const struct firmware *fw = chip->fw;
> +
> +       if (fw->size > LP8501_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
> +        */
> +
> +       lp8501_load_engine(chip);
> +       lp8501_update_program_memory(chip, fw->data, fw->size);
> +}
> +
> +static void lp8501_led_brightness_work(struct work_struct *work)
> +{
> +       struct lp55xx_led *led = container_of(work, struct lp55xx_led,
> +                                             brightness_work);
> +       struct lp55xx_chip *chip = led->chip;
> +
> +       mutex_lock(&chip->lock);
> +       lp55xx_write(chip, LP8501_REG_LED_PWM_BASE + led->chan_nr,
> +                    led->brightness);
> +       mutex_unlock(&chip->lock);
> +}
> +
> +/* Chip specific configurations */
> +static struct lp55xx_device_config lp8501_cfg = {
> +       .reset = {
> +               .addr = LP8501_REG_RESET,
> +               .val  = LP8501_RESET,
> +       },
> +       .enable = {
> +               .addr = LP8501_REG_ENABLE,
> +               .val  = LP8501_ENABLE,
> +       },
> +       .max_channel  = LP8501_MAX_LEDS,
> +       .post_init_device   = lp8501_post_init_device,
> +       .brightness_work_fn = lp8501_led_brightness_work,
> +       .set_led_current    = lp8501_set_led_current,
> +       .firmware_cb        = lp8501_firmware_loaded,
> +       .run_engine         = lp8501_run_engine,
> +};
> +
> +static int lp8501_probe(struct i2c_client *client,
> +                       const struct i2c_device_id *id)
> +{
> +       int ret;
> +       struct lp55xx_chip *chip;
> +       struct lp55xx_led *led;
> +       struct lp55xx_platform_data *pdata;
> +       struct device_node *np = client->dev.of_node;
> +
> +       if (!client->dev.platform_data) {
> +               if (np) {
> +                       ret = lp55xx_of_populate_pdata(&client->dev, np);
> +                       if (ret < 0)
> +                               return ret;
> +               } else {
> +                       dev_err(&client->dev, "no platform data\n");
> +                       return -EINVAL;
> +               }
> +       }
> +       pdata = client->dev.platform_data;
> +
> +       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;
> +       chip->cfg = &lp8501_cfg;
> +
> +       mutex_init(&chip->lock);
> +
> +       i2c_set_clientdata(client, led);
> +
> +       ret = lp55xx_init_device(chip);
> +       if (ret)
> +               goto err_init;
> +
> +       dev_info(&client->dev, "%s Programmable led chip found\n", id->name);
> +
> +       ret = lp55xx_register_leds(led, chip);
> +       if (ret)
> +               goto err_register_leds;
> +
> +       ret = lp55xx_register_sysfs(chip);
> +       if (ret) {
> +               dev_err(&client->dev, "registering sysfs failed\n");
> +               goto err_register_sysfs;
> +       }
> +
> +       return 0;
> +
> +err_register_sysfs:
> +       lp55xx_unregister_leds(led, chip);
> +err_register_leds:
> +       lp55xx_deinit_device(chip);
> +err_init:
> +       return ret;
> +}
> +
> +static int lp8501_remove(struct i2c_client *client)
> +{
> +       struct lp55xx_led *led = i2c_get_clientdata(client);
> +       struct lp55xx_chip *chip = led->chip;
> +
> +       lp8501_stop_engine(chip);
> +       lp55xx_unregister_sysfs(chip);
> +       lp55xx_unregister_leds(led, chip);
> +       lp55xx_deinit_device(chip);
> +
> +       return 0;
> +}
> +
> +static const struct i2c_device_id lp8501_id[] = {
> +       { "lp8501",  0 },
> +       { }
> +};
> +MODULE_DEVICE_TABLE(i2c, lp8501_id);
> +
> +#ifdef CONFIG_OF
> +static const struct of_device_id of_lp8501_leds_match[] = {
> +       { .compatible = "ti,lp8501", },
> +       {},
> +};
> +
> +MODULE_DEVICE_TABLE(of, of_lp8501_leds_match);
> +#endif
> +
> +static struct i2c_driver lp8501_driver = {
> +       .driver = {
> +               .name   = "lp8501",
> +               .of_match_table = of_match_ptr(of_lp8501_leds_match),
> +       },
> +       .probe          = lp8501_probe,
> +       .remove         = lp8501_remove,
> +       .id_table       = lp8501_id,
> +};
> +
> +module_i2c_driver(lp8501_driver);
> +
> +MODULE_DESCRIPTION("Texas Instruments LP8501 LED drvier");
> +MODULE_AUTHOR("Milo Kim");
> +MODULE_LICENSE("GPL");
> diff --git a/include/linux/platform_data/leds-lp55xx.h b/include/linux/platform_data/leds-lp55xx.h
> index 202e290..51a2ff5 100644
> --- a/include/linux/platform_data/leds-lp55xx.h
> +++ b/include/linux/platform_data/leds-lp55xx.h
> @@ -36,6 +36,13 @@ struct lp55xx_predef_pattern {
>         u8 size_b;
>  };
>
> +enum lp8501_pwr_sel {
> +       LP8501_ALL_VDD,         /* D1~9 are connected to VDD */
> +       LP8501_6VDD_3VOUT,      /* D1~6 with VDD, D7~9 with VOUT */
> +       LP8501_3VDD_6VOUT,      /* D1~6 with VOUT, D7~9 with VDD */
> +       LP8501_ALL_VOUT,        /* D1~9 are connected to VOUT */
> +};
> +
>  /*
>   * struct lp55xx_platform_data
>   * @led_config        : Configurable led class device
> @@ -67,6 +74,9 @@ struct lp55xx_platform_data {
>         /* Predefined pattern data */
>         struct lp55xx_predef_pattern *patterns;
>         unsigned int num_patterns;
> +
> +       /* LP8501 specific */
> +       enum lp8501_pwr_sel pwr_sel;
>  };
>
>  #endif /* _LEDS_LP55XX_H */
> --
> 1.7.9.5
>
>
> Best Regards,
> Milo
>
>
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ