diff options
author | Dmitry Torokhov | 2013-03-18 03:40:50 +0100 |
---|---|---|
committer | Dmitry Torokhov | 2013-03-18 03:40:50 +0100 |
commit | 688d794c4c3f8b08c814381ee2edd3ede5856056 (patch) | |
tree | ef680add71e2a9588d07d8b594edbc1b5cd127d7 /drivers/leds | |
parent | USB: cdc-acm - blacklist IMS PCU device (diff) | |
parent | Linux 3.9-rc3 (diff) | |
download | kernel-qcow2-linux-688d794c4c3f8b08c814381ee2edd3ede5856056.tar.gz kernel-qcow2-linux-688d794c4c3f8b08c814381ee2edd3ede5856056.tar.xz kernel-qcow2-linux-688d794c4c3f8b08c814381ee2edd3ede5856056.zip |
Merge tag 'v3.9-rc3' into next
Merge with mainline to bring in module_platform_driver_probe() and
devm_ioremap_resource().
Diffstat (limited to 'drivers/leds')
50 files changed, 1755 insertions, 1842 deletions
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index f508defc0d96..ec50824c02ec 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -154,7 +154,7 @@ config LEDS_HP6XX config LEDS_PCA9532 tristate "LED driver for PCA9532 dimmer" depends on LEDS_CLASS - depends on I2C && INPUT && EXPERIMENTAL + depends on I2C && INPUT help This option enables support for NXP pca9532 LED controller. It is generally only useful @@ -193,9 +193,18 @@ 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 + select FW_LOADER + 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 +214,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. @@ -310,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 @@ -379,7 +389,9 @@ config LEDS_NS2 tristate "LED support for Network Space v2 GPIO LEDs" depends on LEDS_CLASS depends on MACH_NETSPACE_V2 || MACH_INETSPACE_V2 || \ - MACH_NETSPACE_MAX_V2 || MACH_D2NET_V2 + MACH_NETSPACE_MAX_V2 || MACH_D2NET_V2 || \ + MACH_NETSPACE_V2_DT || MACH_INETSPACE_V2_DT || \ + MACH_NETSPACE_MAX_V2_DT || MACH_NETSPACE_MINI_V2_DT default y help This option enable support for the dual-GPIO LED found on the diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 3fb9641b6194..215e7e3b6173 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/led-class.c b/drivers/leds/led-class.c index 48cce18e9d6d..a20752f562bc 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c @@ -211,7 +211,7 @@ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev) led_trigger_set_default(led_cdev); #endif - printk(KERN_DEBUG "Registered led device: %s\n", + dev_dbg(parent, "Registered led device: %s\n", led_cdev->name); return 0; diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c index 262eb4193710..3c972b2f9893 100644 --- a/drivers/leds/led-triggers.c +++ b/drivers/leds/led-triggers.c @@ -166,6 +166,19 @@ void led_trigger_set_default(struct led_classdev *led_cdev) } EXPORT_SYMBOL_GPL(led_trigger_set_default); +void led_trigger_rename_static(const char *name, struct led_trigger *trig) +{ + /* new name must be on a temporary string to prevent races */ + BUG_ON(name == trig->name); + + down_write(&triggers_list_lock); + /* this assumes that trig->name was originaly allocated to + * non constant storage */ + strcpy((char *)trig->name, name); + up_write(&triggers_list_lock); +} +EXPORT_SYMBOL_GPL(led_trigger_rename_static); + /* LED Trigger Interface */ int led_trigger_register(struct led_trigger *trig) @@ -300,13 +313,13 @@ void led_trigger_register_simple(const char *name, struct led_trigger **tp) if (err < 0) { kfree(trig); trig = NULL; - printk(KERN_WARNING "LED trigger %s failed to register" - " (%d)\n", name, err); + pr_warn("LED trigger %s failed to register (%d)\n", + name, err); } - } else - printk(KERN_WARNING "LED trigger %s failed to register" - " (no memory)\n", name); - + } else { + pr_warn("LED trigger %s failed to register (no memory)\n", + name); + } *tp = trig; } EXPORT_SYMBOL_GPL(led_trigger_register_simple); diff --git a/drivers/leds/leds-88pm860x.c b/drivers/leds/leds-88pm860x.c index b7e8cc0957fc..f5b9ea315790 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 @@ -165,15 +168,13 @@ static int pm860x_led_probe(struct platform_device *pdev) res = platform_get_resource_byname(pdev, IORESOURCE_REG, "control"); if (!res) { dev_err(&pdev->dev, "No REG resource for control\n"); - ret = -ENXIO; - goto out; + return -ENXIO; } data->reg_control = res->start; res = platform_get_resource_byname(pdev, IORESOURCE_REG, "blink"); if (!res) { dev_err(&pdev->dev, "No REG resource for blink\n"); - ret = -ENXIO; - goto out; + return -ENXIO; } data->reg_blink = res->start; memset(data->name, 0, MFD_NAME_SIZE); @@ -224,9 +225,6 @@ static int pm860x_led_probe(struct platform_device *pdev) } pm860x_led_set(&data->cdev, 0); return 0; -out: - devm_kfree(&pdev->dev, data); - return ret; } static int pm860x_led_remove(struct platform_device *pdev) diff --git a/drivers/leds/leds-adp5520.c b/drivers/leds/leds-adp5520.c index aa56a867693a..e8072abe76e5 100644 --- a/drivers/leds/leds-adp5520.c +++ b/drivers/leds/leds-adp5520.c @@ -5,10 +5,10 @@ * * Loosely derived from leds-da903x: * Copyright (C) 2008 Compulab, Ltd. - * Mike Rapoport <mike@compulab.co.il> + * Mike Rapoport <mike@compulab.co.il> * * Copyright (C) 2006-2008 Marvell International Ltd. - * Eric Miao <eric.miao@marvell.com> + * Eric Miao <eric.miao@marvell.com> * * Licensed under the GPL-2 or later. */ @@ -85,7 +85,7 @@ static int adp5520_led_setup(struct adp5520_led *led) return ret; } -static int __devinit adp5520_led_prepare(struct platform_device *pdev) +static int adp5520_led_prepare(struct platform_device *pdev) { struct adp5520_leds_platform_data *pdata = pdev->dev.platform_data; struct device *dev = pdev->dev.parent; @@ -101,7 +101,7 @@ static int __devinit adp5520_led_prepare(struct platform_device *pdev) return ret; } -static int __devinit adp5520_led_probe(struct platform_device *pdev) +static int adp5520_led_probe(struct platform_device *pdev) { struct adp5520_leds_platform_data *pdata = pdev->dev.platform_data; struct adp5520_led *led, *led_dat; @@ -183,7 +183,7 @@ err: return ret; } -static int __devexit adp5520_led_remove(struct platform_device *pdev) +static int adp5520_led_remove(struct platform_device *pdev) { struct adp5520_leds_platform_data *pdata = pdev->dev.platform_data; struct adp5520_led *led; @@ -208,7 +208,7 @@ static struct platform_driver adp5520_led_driver = { .owner = THIS_MODULE, }, .probe = adp5520_led_probe, - .remove = __devexit_p(adp5520_led_remove), + .remove = adp5520_led_remove, }; module_platform_driver(adp5520_led_driver); diff --git a/drivers/leds/leds-asic3.c b/drivers/leds/leds-asic3.c index 5de74ff90dcf..b474745e001b 100644 --- a/drivers/leds/leds-asic3.c +++ b/drivers/leds/leds-asic3.c @@ -92,7 +92,7 @@ static int blink_set(struct led_classdev *cdev, return 0; } -static int __devinit asic3_led_probe(struct platform_device *pdev) +static int asic3_led_probe(struct platform_device *pdev) { struct asic3_led *led = pdev->dev.platform_data; int ret; @@ -125,7 +125,7 @@ out: return ret; } -static int __devexit asic3_led_remove(struct platform_device *pdev) +static int asic3_led_remove(struct platform_device *pdev) { struct asic3_led *led = pdev->dev.platform_data; @@ -167,7 +167,7 @@ static const struct dev_pm_ops asic3_led_pm_ops = { static struct platform_driver asic3_led_driver = { .probe = asic3_led_probe, - .remove = __devexit_p(asic3_led_remove), + .remove = asic3_led_remove, .driver = { .name = "leds-asic3", .owner = THIS_MODULE, diff --git a/drivers/leds/leds-atmel-pwm.c b/drivers/leds/leds-atmel-pwm.c index 45430632faab..386773532d95 100644 --- a/drivers/leds/leds-atmel-pwm.c +++ b/drivers/leds/leds-atmel-pwm.c @@ -35,7 +35,7 @@ static void pwmled_brightness(struct led_classdev *cdev, enum led_brightness b) * NOTE: we reuse the platform_data structure of GPIO leds, * but repurpose its "gpio" number as a PWM channel number. */ -static int __devinit pwmled_probe(struct platform_device *pdev) +static int pwmled_probe(struct platform_device *pdev) { const struct gpio_led_platform_data *pdata; struct pwmled *leds; diff --git a/drivers/leds/leds-bd2802.c b/drivers/leds/leds-bd2802.c index 89ca6a2a19d1..851517030cc1 100644 --- a/drivers/leds/leds-bd2802.c +++ b/drivers/leds/leds-bd2802.c @@ -26,8 +26,8 @@ #define BD2802_LED_OFFSET 0xa #define BD2802_COLOR_OFFSET 0x3 -#define BD2802_REG_CLKSETUP 0x00 -#define BD2802_REG_CONTROL 0x01 +#define BD2802_REG_CLKSETUP 0x00 +#define BD2802_REG_CONTROL 0x01 #define BD2802_REG_HOURSETUP 0x02 #define BD2802_REG_CURRENT1SETUP 0x03 #define BD2802_REG_CURRENT2SETUP 0x04 @@ -93,7 +93,7 @@ struct bd2802_led { * In ADF mode, user can set registers of BD2802GU directly, * therefore BD2802GU doesn't enter reset state. */ - int adf_on; + int adf_on; enum led_ids led_id; enum led_colors color; @@ -328,7 +328,7 @@ static ssize_t bd2802_store_reg##reg_addr(struct device *dev, \ int ret; \ if (!count) \ return -EINVAL; \ - ret = strict_strtoul(buf, 16, &val); \ + ret = kstrtoul(buf, 16, &val); \ if (ret) \ return ret; \ down_write(&led->rwsem); \ @@ -492,7 +492,7 @@ static ssize_t bd2802_store_##attr_name(struct device *dev, \ int ret; \ if (!count) \ return -EINVAL; \ - ret = strict_strtoul(buf, 16, &val); \ + ret = kstrtoul(buf, 16, &val); \ if (ret) \ return ret; \ down_write(&led->rwsem); \ @@ -670,7 +670,7 @@ static void bd2802_unregister_led_classdev(struct bd2802_led *led) led_classdev_unregister(&led->cdev_led1r); } -static int __devinit bd2802_probe(struct i2c_client *client, +static int bd2802_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct bd2802_led *led; diff --git a/drivers/leds/leds-blinkm.c b/drivers/leds/leds-blinkm.c index f7c3d7f1ec52..a502678cc7f5 100644 --- a/drivers/leds/leds-blinkm.c +++ b/drivers/leds/leds-blinkm.c @@ -632,7 +632,7 @@ static int blinkm_detect(struct i2c_client *client, struct i2c_board_info *info) return 0; } -static int __devinit blinkm_probe(struct i2c_client *client, +static int blinkm_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct blinkm_data *data; @@ -743,7 +743,7 @@ exit: return err; } -static int __devexit blinkm_remove(struct i2c_client *client) +static int blinkm_remove(struct i2c_client *client) { struct blinkm_data *data = i2c_get_clientdata(client); int ret = 0; @@ -801,7 +801,7 @@ static struct i2c_driver blinkm_driver = { .name = "blinkm", }, .probe = blinkm_probe, - .remove = __devexit_p(blinkm_remove), + .remove = blinkm_remove, .id_table = blinkm_id, .detect = blinkm_detect, .address_list = normal_i2c, diff --git a/drivers/leds/leds-clevo-mail.c b/drivers/leds/leds-clevo-mail.c index e024b0b1c3b1..6a8405df76a3 100644 --- a/drivers/leds/leds-clevo-mail.c +++ b/drivers/leds/leds-clevo-mail.c @@ -1,3 +1,4 @@ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/module.h> @@ -26,7 +27,7 @@ static struct platform_device *pdev; static int __init clevo_mail_led_dmi_callback(const struct dmi_system_id *id) { - printk(KERN_INFO KBUILD_MODNAME ": '%s' found\n", id->ident); + pr_info("'%s' found\n", id->ident); return 1; } @@ -135,8 +136,7 @@ static int clevo_mail_led_blink(struct led_classdev *led_cdev, status = 0; } else { - printk(KERN_DEBUG KBUILD_MODNAME - ": clevo_mail_led_blink(..., %lu, %lu)," + pr_debug("clevo_mail_led_blink(..., %lu, %lu)," " returning -EINVAL (unsupported)\n", *delay_on, *delay_off); } @@ -153,7 +153,7 @@ static struct led_classdev clevo_mail_led = { .flags = LED_CORE_SUSPENDRESUME, }; -static int __devinit clevo_mail_led_probe(struct platform_device *pdev) +static int clevo_mail_led_probe(struct platform_device *pdev) { return led_classdev_register(&pdev->dev, &clevo_mail_led); } @@ -183,7 +183,7 @@ static int __init clevo_mail_led_init(void) count = dmi_check_system(clevo_mail_led_dmi_table); } else { count = 1; - printk(KERN_ERR KBUILD_MODNAME ": Skipping DMI detection. " + pr_err("Skipping DMI detection. " "If the driver works on your hardware please " "report model and the output of dmidecode in tracker " "at http://sourceforge.net/projects/clevo-mailled/\n"); @@ -197,8 +197,7 @@ static int __init clevo_mail_led_init(void) error = platform_driver_probe(&clevo_mail_led_driver, clevo_mail_led_probe); if (error) { - printk(KERN_ERR KBUILD_MODNAME - ": Can't probe platform driver\n"); + pr_err("Can't probe platform driver\n"); platform_device_unregister(pdev); } } else diff --git a/drivers/leds/leds-cobalt-qube.c b/drivers/leds/leds-cobalt-qube.c index 6a8725cc7b4d..8abcb66db01c 100644 --- a/drivers/leds/leds-cobalt-qube.c +++ b/drivers/leds/leds-cobalt-qube.c @@ -34,7 +34,7 @@ static struct led_classdev qube_front_led = { .default_trigger = "default-on", }; -static int __devinit cobalt_qube_led_probe(struct platform_device *pdev) +static int cobalt_qube_led_probe(struct platform_device *pdev) { struct resource *res; int retval; @@ -43,7 +43,7 @@ static int __devinit cobalt_qube_led_probe(struct platform_device *pdev) if (!res) return -EBUSY; - led_port = ioremap(res->start, resource_size(res)); + led_port = devm_ioremap(&pdev->dev, res->start, resource_size(res)); if (!led_port) return -ENOMEM; @@ -52,32 +52,29 @@ static int __devinit cobalt_qube_led_probe(struct platform_device *pdev) retval = led_classdev_register(&pdev->dev, &qube_front_led); if (retval) - goto err_iounmap; + goto err_null; return 0; -err_iounmap: - iounmap(led_port); +err_null: led_port = NULL; return retval; } -static int __devexit cobalt_qube_led_remove(struct platform_device *pdev) +static int cobalt_qube_led_remove(struct platform_device *pdev) { led_classdev_unregister(&qube_front_led); - if (led_port) { - iounmap(led_port); + if (led_port) led_port = NULL; - } return 0; } static struct platform_driver cobalt_qube_led_driver = { .probe = cobalt_qube_led_probe, - .remove = __devexit_p(cobalt_qube_led_remove), + .remove = cobalt_qube_led_remove, .driver = { .name = "cobalt-qube-leds", .owner = THIS_MODULE, diff --git a/drivers/leds/leds-cobalt-raq.c b/drivers/leds/leds-cobalt-raq.c index aac1c073fe7b..001088b31373 100644 --- a/drivers/leds/leds-cobalt-raq.c +++ b/drivers/leds/leds-cobalt-raq.c @@ -76,7 +76,7 @@ static struct led_classdev raq_power_off_led = { .default_trigger = "power-off", }; -static int __devinit cobalt_raq_led_probe(struct platform_device *pdev) +static int cobalt_raq_led_probe(struct platform_device *pdev) { struct resource *res; int retval; @@ -85,13 +85,13 @@ static int __devinit cobalt_raq_led_probe(struct platform_device *pdev) if (!res) return -EBUSY; - led_port = ioremap(res->start, resource_size(res)); + led_port = devm_ioremap(&pdev->dev, res->start, resource_size(res)); if (!led_port) return -ENOMEM; retval = led_classdev_register(&pdev->dev, &raq_power_off_led); if (retval) - goto err_iounmap; + goto err_null; retval = led_classdev_register(&pdev->dev, &raq_web_led); if (retval) @@ -102,29 +102,26 @@ static int __devinit cobalt_raq_led_probe(struct platform_device *pdev) err_unregister: led_classdev_unregister(&raq_power_off_led); -err_iounmap: - iounmap(led_port); +err_null: led_port = NULL; return retval; } -static int __devexit cobalt_raq_led_remove(struct platform_device *pdev) +static int cobalt_raq_led_remove(struct platform_device *pdev) { led_classdev_unregister(&raq_power_off_led); led_classdev_unregister(&raq_web_led); - if (led_port) { - iounmap(led_port); + if (led_port) led_port = NULL; - } return 0; } static struct platform_driver cobalt_raq_led_driver = { .probe = cobalt_raq_led_probe, - .remove = __devexit_p(cobalt_raq_led_remove), + .remove = cobalt_raq_led_remove, .driver = { .name = "cobalt-raq-leds", .owner = THIS_MODULE, diff --git a/drivers/leds/leds-da903x.c b/drivers/leds/leds-da903x.c index cc77c9d92615..c263a21db829 100644 --- a/drivers/leds/leds-da903x.c +++ b/drivers/leds/leds-da903x.c @@ -2,10 +2,10 @@ * LEDs driver for Dialog Semiconductor DA9030/DA9034 * * Copyright (C) 2008 Compulab, Ltd. - * Mike Rapoport <mike@compulab.co.il> + * Mike Rapoport <mike@compulab.co.il> * * Copyright (C) 2006-2008 Marvell International Ltd. - * Eric Miao <eric.miao@marvell.com> + * Eric Miao <eric.miao@marvell.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 @@ -85,13 +85,13 @@ static void da903x_led_set(struct led_classdev *led_cdev, enum led_brightness value) { struct da903x_led *led; - + led = container_of(led_cdev, struct da903x_led, cdev); led->new_brightness = value; schedule_work(&led->work); } -static int __devinit da903x_led_probe(struct platform_device *pdev) +static int da903x_led_probe(struct platform_device *pdev) { struct led_info *pdata = pdev->dev.platform_data; struct da903x_led *led; @@ -136,7 +136,7 @@ static int __devinit da903x_led_probe(struct platform_device *pdev) return 0; } -static int __devexit da903x_led_remove(struct platform_device *pdev) +static int da903x_led_remove(struct platform_device *pdev) { struct da903x_led *led = platform_get_drvdata(pdev); @@ -150,13 +150,13 @@ static struct platform_driver da903x_led_driver = { .owner = THIS_MODULE, }, .probe = da903x_led_probe, - .remove = __devexit_p(da903x_led_remove), + .remove = da903x_led_remove, }; module_platform_driver(da903x_led_driver); MODULE_DESCRIPTION("LEDs driver for Dialog Semiconductor DA9030/DA9034"); -MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>" - "Mike Rapoport <mike@compulab.co.il>"); +MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>"); +MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:da903x-led"); diff --git a/drivers/leds/leds-da9052.c b/drivers/leds/leds-da9052.c index 58a5244c437e..efec43344e9f 100644 --- a/drivers/leds/leds-da9052.c +++ b/drivers/leds/leds-da9052.c @@ -102,7 +102,7 @@ static int da9052_configure_leds(struct da9052 *da9052) return error; } -static int __devinit da9052_led_probe(struct platform_device *pdev) +static int da9052_led_probe(struct platform_device *pdev) { struct da9052_pdata *pdata; struct da9052 *da9052; @@ -176,7 +176,7 @@ err: return error; } -static int __devexit da9052_led_remove(struct platform_device *pdev) +static int da9052_led_remove(struct platform_device *pdev) { struct da9052_led *led = platform_get_drvdata(pdev); struct da9052_pdata *pdata; @@ -204,7 +204,7 @@ static struct platform_driver da9052_led_driver = { .owner = THIS_MODULE, }, .probe = da9052_led_probe, - .remove = __devexit_p(da9052_led_remove), + .remove = da9052_led_remove, }; module_platform_driver(da9052_led_driver); diff --git a/drivers/leds/leds-fsg.c b/drivers/leds/leds-fsg.c index b9053fa6e253..b4d5a44cc41b 100644 --- a/drivers/leds/leds-fsg.c +++ b/drivers/leds/leds-fsg.c @@ -20,8 +20,8 @@ #include <linux/platform_device.h> #include <linux/leds.h> #include <linux/module.h> +#include <linux/io.h> #include <mach/hardware.h> -#include <asm/io.h> #define FSG_LED_WLAN_BIT 0 #define FSG_LED_WAN_BIT 1 @@ -149,11 +149,10 @@ static int fsg_led_probe(struct platform_device *pdev) int ret; /* Map the LED chip select address space */ - latch_address = (unsigned short *) ioremap(IXP4XX_EXP_BUS_BASE(2), 512); - if (!latch_address) { - ret = -ENOMEM; - goto failremap; - } + latch_address = (unsigned short *) devm_ioremap(&pdev->dev, + IXP4XX_EXP_BUS_BASE(2), 512); + if (!latch_address) + return -ENOMEM; latch_value = 0xffff; *latch_address = latch_value; @@ -195,8 +194,6 @@ static int fsg_led_probe(struct platform_device *pdev) failwan: led_classdev_unregister(&fsg_wlan_led); failwlan: - iounmap(latch_address); - failremap: return ret; } @@ -210,8 +207,6 @@ static int fsg_led_remove(struct platform_device *pdev) led_classdev_unregister(&fsg_sync_led); led_classdev_unregister(&fsg_ring_led); - iounmap(latch_address); - return 0; } diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index 087d1e66f4f7..a0d931bcb37c 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c @@ -21,6 +21,7 @@ #include <linux/workqueue.h> #include <linux/module.h> #include <linux/pinctrl/consumer.h> +#include <linux/err.h> struct gpio_led_data { struct led_classdev cdev; @@ -91,7 +92,7 @@ static int gpio_blink_set(struct led_classdev *led_cdev, delay_on, delay_off); } -static int __devinit create_gpio_led(const struct gpio_led *template, +static int create_gpio_led(const struct gpio_led *template, struct gpio_led_data *led_dat, struct device *parent, int (*blink_set)(unsigned, int, unsigned long *, unsigned long *)) { @@ -101,15 +102,11 @@ static int __devinit create_gpio_led(const struct gpio_led *template, /* skip leds that aren't available */ if (!gpio_is_valid(template->gpio)) { - printk(KERN_INFO "Skipping unavailable LED gpio %d (%s)\n", + dev_info(parent, "Skipping unavailable LED gpio %d (%s)\n", template->gpio, template->name); return 0; } - ret = gpio_request(template->gpio, template->name); - if (ret < 0) - return ret; - led_dat->cdev.name = template->name; led_dat->cdev.default_trigger = template->default_trigger; led_dat->gpio = template->gpio; @@ -129,20 +126,20 @@ static int __devinit create_gpio_led(const struct gpio_led *template, if (!template->retain_state_suspended) led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; - ret = gpio_direction_output(led_dat->gpio, led_dat->active_low ^ state); + ret = devm_gpio_request_one(parent, template->gpio, + (led_dat->active_low ^ state) ? + GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW, + template->name); if (ret < 0) - goto err; - + return ret; + INIT_WORK(&led_dat->work, gpio_led_work); ret = led_classdev_register(parent, &led_dat->cdev); if (ret < 0) - goto err; + return ret; return 0; -err: - gpio_free(led_dat->gpio); - return ret; } static void delete_gpio_led(struct gpio_led_data *led) @@ -151,7 +148,6 @@ static void delete_gpio_led(struct gpio_led_data *led) return; led_classdev_unregister(&led->cdev); cancel_work_sync(&led->work); - gpio_free(led->gpio); } struct gpio_leds_priv { @@ -167,7 +163,7 @@ static inline int sizeof_gpio_leds_priv(int num_leds) /* Code to create from OpenFirmware platform devices */ #ifdef CONFIG_OF_GPIO -static struct gpio_leds_priv * __devinit gpio_leds_create_of(struct platform_device *pdev) +static struct gpio_leds_priv *gpio_leds_create_of(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node, *child; struct gpio_leds_priv *priv; @@ -176,12 +172,16 @@ static struct gpio_leds_priv * __devinit gpio_leds_create_of(struct platform_dev /* count LEDs in this device, so we know how much to allocate */ count = of_get_child_count(np); if (!count) - return NULL; + return ERR_PTR(-ENODEV); + + for_each_child_of_node(np, child) + if (of_get_gpio(child, 0) == -EPROBE_DEFER) + return ERR_PTR(-EPROBE_DEFER); priv = devm_kzalloc(&pdev->dev, sizeof_gpio_leds_priv(count), GFP_KERNEL); if (!priv) - return NULL; + return ERR_PTR(-ENOMEM); for_each_child_of_node(np, child) { struct gpio_led led = {}; @@ -216,7 +216,7 @@ static struct gpio_leds_priv * __devinit gpio_leds_create_of(struct platform_dev err: for (count = priv->num_leds - 2; count >= 0; count--) delete_gpio_led(&priv->leds[count]); - return NULL; + return ERR_PTR(-ENODEV); } static const struct of_device_id of_gpio_leds_match[] = { @@ -224,14 +224,14 @@ static const struct of_device_id of_gpio_leds_match[] = { {}, }; #else /* CONFIG_OF_GPIO */ -static struct gpio_leds_priv * __devinit gpio_leds_create_of(struct platform_device *pdev) +static struct gpio_leds_priv *gpio_leds_create_of(struct platform_device *pdev) { - return NULL; + return ERR_PTR(-ENODEV); } #endif /* CONFIG_OF_GPIO */ -static int __devinit gpio_led_probe(struct platform_device *pdev) +static int gpio_led_probe(struct platform_device *pdev) { struct gpio_led_platform_data *pdata = pdev->dev.platform_data; struct gpio_leds_priv *priv; @@ -264,8 +264,8 @@ static int __devinit gpio_led_probe(struct platform_device *pdev) } } else { priv = gpio_leds_create_of(pdev); - if (!priv) - return -ENODEV; + if (IS_ERR(priv)) + return PTR_ERR(priv); } platform_set_drvdata(pdev, priv); @@ -273,7 +273,7 @@ static int __devinit gpio_led_probe(struct platform_device *pdev) return 0; } -static int __devexit gpio_led_remove(struct platform_device *pdev) +static int gpio_led_remove(struct platform_device *pdev) { struct gpio_leds_priv *priv = platform_get_drvdata(pdev); int i; @@ -288,7 +288,7 @@ static int __devexit gpio_led_remove(struct platform_device *pdev) static struct platform_driver gpio_led_driver = { .probe = gpio_led_probe, - .remove = __devexit_p(gpio_led_remove), + .remove = gpio_led_remove, .driver = { .name = "leds-gpio", .owner = THIS_MODULE, diff --git a/drivers/leds/leds-lm3530.c b/drivers/leds/leds-lm3530.c index b26306f6724d..a036a19040fe 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; @@ -377,7 +400,7 @@ static ssize_t lm3530_mode_set(struct device *dev, struct device_attribute } static DEVICE_ATTR(mode, 0644, lm3530_mode_get, lm3530_mode_set); -static int __devinit lm3530_probe(struct i2c_client *client, +static int lm3530_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct lm3530_platform_data *pdata = client->dev.platform_data; @@ -452,14 +475,13 @@ err_create_file: return err; } -static int __devexit lm3530_remove(struct i2c_client *client) +static int lm3530_remove(struct i2c_client *client) { struct lm3530_data *drvdata = i2c_get_clientdata(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; } @@ -472,7 +494,7 @@ MODULE_DEVICE_TABLE(i2c, lm3530_id); static struct i2c_driver lm3530_i2c_driver = { .probe = lm3530_probe, - .remove = __devexit_p(lm3530_remove), + .remove = lm3530_remove, .id_table = lm3530_id, .driver = { .name = LM3530_NAME, diff --git a/drivers/leds/leds-lm3533.c b/drivers/leds/leds-lm3533.c index f6837b99908c..bbf24d038a7f 100644 --- a/drivers/leds/leds-lm3533.c +++ b/drivers/leds/leds-lm3533.c @@ -646,7 +646,7 @@ static struct attribute_group lm3533_led_attribute_group = { .attrs = lm3533_led_attributes }; -static int __devinit lm3533_led_setup(struct lm3533_led *led, +static int lm3533_led_setup(struct lm3533_led *led, struct lm3533_led_platform_data *pdata) { int ret; @@ -658,7 +658,7 @@ static int __devinit lm3533_led_setup(struct lm3533_led *led, return lm3533_ctrlbank_set_pwm(&led->cb, pdata->pwm); } -static int __devinit lm3533_led_probe(struct platform_device *pdev) +static int lm3533_led_probe(struct platform_device *pdev) { struct lm3533 *lm3533; struct lm3533_led_platform_data *pdata; @@ -742,7 +742,7 @@ err_unregister: return ret; } -static int __devexit lm3533_led_remove(struct platform_device *pdev) +static int lm3533_led_remove(struct platform_device *pdev) { struct lm3533_led *led = platform_get_drvdata(pdev); @@ -774,7 +774,7 @@ static struct platform_driver lm3533_led_driver = { .owner = THIS_MODULE, }, .probe = lm3533_led_probe, - .remove = __devexit_p(lm3533_led_remove), + .remove = lm3533_led_remove, .shutdown = lm3533_led_shutdown, }; module_platform_driver(lm3533_led_driver); diff --git a/drivers/leds/leds-lm355x.c b/drivers/leds/leds-lm355x.c index 065ec015d67a..4117235ba618 100644 --- a/drivers/leds/leds-lm355x.c +++ b/drivers/leds/leds-lm355x.c @@ -168,7 +168,7 @@ static char lm355x_name[][I2C_NAME_SIZE] = { }; /* chip initialize */ -static int __devinit lm355x_chip_init(struct lm355x_chip_data *chip) +static int lm355x_chip_init(struct lm355x_chip_data *chip) { int ret; unsigned int reg_val; @@ -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; @@ -408,10 +408,10 @@ static ssize_t lm3556_indicator_pattern_store(struct device *dev, return size; out: dev_err(chip->dev, "%s:i2c access fail to register\n", __func__); - return size; + return ret; } -static DEVICE_ATTR(pattern, 0666, NULL, lm3556_indicator_pattern_store); +static DEVICE_ATTR(pattern, S_IWUSR, NULL, lm3556_indicator_pattern_store); static const struct regmap_config lm355x_regmap = { .reg_bits = 8, @@ -420,7 +420,7 @@ static const struct regmap_config lm355x_regmap = { }; /* module initialize */ -static int __devinit lm355x_probe(struct i2c_client *client, +static int lm355x_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct lm355x_platform_data *pdata = client->dev.platform_data; @@ -526,7 +526,7 @@ err_out: return err; } -static int __devexit lm355x_remove(struct i2c_client *client) +static int lm355x_remove(struct i2c_client *client) { struct lm355x_chip_data *chip = i2c_get_clientdata(client); struct lm355x_reg_data *preg = chip->regs; @@ -560,7 +560,7 @@ static struct i2c_driver lm355x_i2c_driver = { .pm = NULL, }, .probe = lm355x_probe, - .remove = __devexit_p(lm355x_remove), + .remove = lm355x_remove, .id_table = lm355x_id, }; diff --git a/drivers/leds/leds-lm3642.c b/drivers/leds/leds-lm3642.c index 3285006e9888..9f428d9dfe91 100644 --- a/drivers/leds/leds-lm3642.c +++ b/drivers/leds/leds-lm3642.c @@ -93,7 +93,7 @@ struct lm3642_chip_data { }; /* chip initialize */ -static int __devinit lm3642_chip_init(struct lm3642_chip_data *chip) +static int lm3642_chip_init(struct lm3642_chip_data *chip) { int ret; struct lm3642_platform_data *pdata = chip->pdata; @@ -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; @@ -201,13 +201,13 @@ static ssize_t lm3642_torch_pin_store(struct device *dev, return size; out: dev_err(chip->dev, "%s:i2c access fail to register\n", __func__); - return size; + return ret; out_strtoint: dev_err(chip->dev, "%s: fail to change str to int\n", __func__); - return size; + return ret; } -static DEVICE_ATTR(torch_pin, 0666, NULL, lm3642_torch_pin_store); +static DEVICE_ATTR(torch_pin, S_IWUSR, NULL, lm3642_torch_pin_store); static void lm3642_deferred_torch_brightness_set(struct work_struct *work) { @@ -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; @@ -258,13 +258,13 @@ static ssize_t lm3642_strobe_pin_store(struct device *dev, return size; out: dev_err(chip->dev, "%s:i2c access fail to register\n", __func__); - return size; + return ret; out_strtoint: dev_err(chip->dev, "%s: fail to change str to int\n", __func__); - return size; + return ret; } -static DEVICE_ATTR(strobe_pin, 0666, NULL, lm3642_strobe_pin_store); +static DEVICE_ATTR(strobe_pin, S_IWUSR, NULL, lm3642_strobe_pin_store); static void lm3642_deferred_strobe_brightness_set(struct work_struct *work) { @@ -313,7 +313,7 @@ static const struct regmap_config lm3642_regmap = { .max_register = REG_MAX, }; -static int __devinit lm3642_probe(struct i2c_client *client, +static int lm3642_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct lm3642_platform_data *pdata = client->dev.platform_data; @@ -420,7 +420,7 @@ err_out: return err; } -static int __devexit lm3642_remove(struct i2c_client *client) +static int lm3642_remove(struct i2c_client *client) { struct lm3642_chip_data *chip = i2c_get_clientdata(client); @@ -450,7 +450,7 @@ static struct i2c_driver lm3642_i2c_driver = { .pm = NULL, }, .probe = lm3642_probe, - .remove = __devexit_p(lm3642_remove), + .remove = lm3642_remove, .id_table = lm3642_id, }; diff --git a/drivers/leds/leds-lp3944.c b/drivers/leds/leds-lp3944.c index c298f7d9f535..0c4386e656c1 100644 --- a/drivers/leds/leds-lp3944.c +++ b/drivers/leds/leds-lp3944.c @@ -86,7 +86,7 @@ static int lp3944_reg_read(struct i2c_client *client, u8 reg, u8 *value) tmp = i2c_smbus_read_byte_data(client, reg); if (tmp < 0) - return -EINVAL; + return tmp; *value = tmp; @@ -374,7 +374,7 @@ exit: return err; } -static int __devinit lp3944_probe(struct i2c_client *client, +static int lp3944_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct lp3944_platform_data *lp3944_pdata = client->dev.platform_data; @@ -411,7 +411,7 @@ static int __devinit lp3944_probe(struct i2c_client *client, return 0; } -static int __devexit lp3944_remove(struct i2c_client *client) +static int lp3944_remove(struct i2c_client *client) { struct lp3944_platform_data *pdata = client->dev.platform_data; struct lp3944_data *data = i2c_get_clientdata(client); @@ -446,7 +446,7 @@ static struct i2c_driver lp3944_driver = { .name = "lp3944", }, .probe = lp3944_probe, - .remove = __devexit_p(lp3944_remove), + .remove = lp3944_remove, .id_table = lp3944_id, }; diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index 2064aefedc07..1001347ba70b 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 <samu.p.onkalo@nokia.com> + * Milo(Woogyom) Kim <milo.kim@ti.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -20,33 +22,21 @@ * 02110-1301 USA */ -#include <linux/module.h> -#include <linux/init.h> -#include <linux/i2c.h> -#include <linux/mutex.h> -#include <linux/gpio.h> -#include <linux/interrupt.h> #include <linux/delay.h> -#include <linux/ctype.h> -#include <linux/spinlock.h> -#include <linux/wait.h> +#include <linux/firmware.h> +#include <linux/i2c.h> +#include <linux/init.h> #include <linux/leds.h> -#include <linux/leds-lp5521.h> -#include <linux/workqueue.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/platform_data/leds-lp55xx.h> #include <linux/slab.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 */ +#include "leds-lp55xx-common.h" -#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 @@ -58,22 +48,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 @@ -92,440 +74,287 @@ /* 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 -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 -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; -}; +#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) -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; -}; +#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 -static inline struct lp5521_led *cdev_to_led(struct led_classdev *cdev) +static inline void lp5521_wait_opmode_done(void) { - return container_of(cdev, struct lp5521_led, cdev); + /* operation mode change needs to be longer than 153 us */ + usleep_range(200, 300); } -static inline struct lp5521_chip *engine_to_lp5521(struct lp5521_engine *engine) +static inline void lp5521_wait_enable_done(void) { - return container_of(engine, struct lp5521_chip, - engines[engine->id - 1]); + /* it takes more 488 us to update ENABLE register */ + usleep_range(500, 600); } -static inline struct lp5521_chip *led_to_lp5521(struct lp5521_led *led) +static void lp5521_set_led_current(struct lp55xx_led *led, u8 led_current) { - return container_of(led, struct lp5521_chip, - leds[led->id]); + led->led_current = led_current; + lp55xx_write(led->chip, LP5521_REG_LED_CURRENT_BASE + led->chan_nr, + led_current); } -static void lp5521_led_brightness_work(struct work_struct *work); - -static inline int lp5521_write(struct i2c_client *client, u8 reg, u8 value) +static void lp5521_load_engine(struct lp55xx_chip *chip) { - return i2c_smbus_write_byte_data(client, reg, value); -} + 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, + }; -static int lp5521_read(struct i2c_client *client, u8 reg, u8 *buf) -{ - s32 ret; + u8 val[] = { + [LP55XX_ENGINE_1] = LP5521_LOAD_R, + [LP55XX_ENGINE_2] = LP5521_LOAD_G, + [LP55XX_ENGINE_3] = LP5521_LOAD_B, + }; - ret = i2c_smbus_read_byte_data(client, reg); - if (ret < 0) - return -EIO; + lp55xx_update_bits(chip, LP5521_REG_OP_MODE, mask[idx], val[idx]); - *buf = ret; - return 0; + lp5521_wait_opmode_done(); } -static int lp5521_set_engine_mode(struct lp5521_engine *engine, u8 mode) +static void lp5521_stop_engine(struct lp55xx_chip *chip) { - struct lp5521_chip *chip = engine_to_lp5521(engine); - struct i2c_client *client = chip->client; - int ret; - u8 engine_state; - - /* Only transition between RUN and DIRECT mode are handled here */ - if (mode == LP5521_CMD_LOAD) - return 0; - - if (mode == LP5521_CMD_DISABLED) - mode = LP5521_CMD_DIRECT; - - ret = lp5521_read(client, LP5521_REG_OP_MODE, &engine_state); - if (ret < 0) - return ret; - - /* 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); + 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); - if (ret) - return ret; - - /* 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); - if (ret) - return ret; - - /* 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); -} - -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 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; + /* stop engine */ + if (!start) { + lp5521_stop_engine(chip); + lp55xx_write(chip, LP5521_REG_OP_MODE, LP5521_CMD_DIRECT); + lp5521_wait_opmode_done(); + return; } -} - -static int lp5521_configure(struct i2c_client *client) -{ - struct lp5521_chip *chip = i2c_get_clientdata(client); - int ret; - u8 cfg; - - lp5521_init_engine(chip); - - /* 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); - - /* 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); - - /* Set engines are set to run state when OP_MODE enables engines */ - ret |= lp5521_write(client, LP5521_REG_ENABLE, - LP5521_ENABLE_RUN_PROGRAM); - /* enable takes 500us. 1 - 2 ms leaves some margin */ - usleep_range(1000, 2000); - - return ret; -} - -static int lp5521_run_selftest(struct lp5521_chip *chip, char *buf) -{ - int ret; - u8 status; - - ret = lp5521_read(chip->client, LP5521_REG_STATUS, &status); - if (ret < 0) - return ret; - - /* 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; - 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, - brightness_work); - struct lp5521_chip *chip = led_to_lp5521(led); - struct i2c_client *client = chip->client; - - mutex_lock(&chip->lock); - lp5521_write(client, LP5521_REG_LED_PWM_BASE + led->chan_nr, - led->brightness); - 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; + /* + * To run the engine, + * operation mode and enable register should updated at the same time + */ - 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); + ret = lp55xx_read(chip, LP5521_REG_OP_MODE, &mode); if (ret) - return ret; - if (buf != LP5521_ENABLE_DEFAULT) - return -ENODEV; + return; - return 0; -} + ret = lp55xx_read(chip, LP5521_REG_ENABLE, &exec); + if (ret) + return; -/* Set engine mode and create appropriate sysfs attributes, if required. */ -static int lp5521_set_mode(struct lp5521_engine *engine, u8 mode) -{ - int ret = 0; + /* 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 in that mode already do nothing, except for run */ - if (mode == engine->mode && mode != LP5521_CMD_RUN) - return 0; + 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 (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); + if (LP5521_B_IS_LOADING(mode)) { + mode = (mode & ~LP5521_MODE_B_M) | LP5521_RUN_B; + exec = (exec & ~LP5521_EXEC_B_M) | LP5521_RUN_B; } - engine->mode = mode; + lp55xx_write(chip, LP5521_REG_OP_MODE, mode); + lp5521_wait_opmode_done(); - return ret; + lp55xx_update_bits(chip, LP5521_REG_ENABLE, LP5521_EXEC_M, exec); + lp5521_wait_enable_done(); } -static int lp5521_do_store_load(struct lp5521_engine *engine, - const char *buf, size_t len) +static int lp5521_update_program_memory(struct lp55xx_chip *chip, + const u8 *data, size_t size) { - 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; + 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; - while ((offset < len - 1) && (i < LP5521_PROGRAM_LENGTH)) { + /* 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(buf + offset, "%2s%n ", c, &nrchars); - if (ret != 2) - goto fail; + ret = sscanf(data + offset, "%2s%n ", c, &nrchars); + if (ret != 1) + goto err; + ret = sscanf(c, "%2x", &cmd); if (ret != 1) - goto fail; - pattern[i] = (u8)cmd; + goto err; + pattern[i] = (u8)cmd; offset += nrchars; i++; } /* Each instruction is 16bit long. Check that length is even */ if (i % 2) - goto fail; + goto err; - mutex_lock(&chip->lock); - if (engine->mode == LP5521_CMD_LOAD) - ret = lp5521_load_program(engine, pattern); - else - ret = -EINVAL; - mutex_unlock(&chip->lock); + program_size = i; + for (i = 0; i < program_size; i++) + lp55xx_write(chip, addr[idx] + i, pattern[i]); - if (ret) { - dev_err(&client->dev, "failed loading pattern\n"); - return ret; - } + return 0; - return len; -fail: - dev_err(&client->dev, "wrong pattern format\n"); +err: + dev_err(&chip->cl->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) +static void lp5521_firmware_loaded(struct lp55xx_chip *chip) { - 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); -} + const struct firmware *fw = chip->fw; -#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"); + if (fw->size > LP5521_PROGRAM_LENGTH) { + dev_err(&chip->cl->dev, "firmware data size overflow: %zu\n", + fw->size); + return; } -} -#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); \ + /* + * 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); } -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) +static int lp5521_post_init_device(struct lp55xx_chip *chip) { - 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); + int ret; + u8 val; - 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); + /* + * 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 = lp55xx_read(chip, LP5521_REG_R_CURRENT, &val); + if (ret) { + dev_err(&chip->cl->dev, "error in resetting chip\n"); + return ret; + } + if (val != LP5521_REG_R_CURR_DEFAULT) { + dev_err(&chip->cl->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); - mutex_unlock(&chip->lock); - return len; -} + /* Set all PWMs to direct control mode */ + ret = lp55xx_write(chip, LP5521_REG_OP_MODE, LP5521_CMD_DIRECT); -#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) + val = chip->pdata->update_config ? + : (LP5521_PWRSAVE_EN | LP5521_CP_MODE_AUTO | LP5521_R_TO_BATT); + ret = lp55xx_write(chip, LP5521_REG_CONFIG, val); + if (ret) + return ret; -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); + /* Initialize all channels PWM to zero -> leds off */ + lp55xx_write(chip, LP5521_REG_R_PWM, 0); + lp55xx_write(chip, LP5521_REG_G_PWM, 0); + lp55xx_write(chip, LP5521_REG_B_PWM, 0); - return sprintf(buf, "%d\n", led->max_current); -} + /* Set engines are set to run state when OP_MODE enables engines */ + ret = lp55xx_write(chip, LP5521_REG_ENABLE, LP5521_ENABLE_RUN_PROGRAM); + if (ret) + return ret; -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); + lp5521_wait_enable_done(); - return sprintf(buf, "%d\n", led->led_current); + return 0; } -static ssize_t store_current(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len) +static int lp5521_run_selftest(struct lp55xx_chip *chip, char *buf) { - 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; + struct lp55xx_platform_data *pdata = chip->pdata; + int ret; + u8 status; - if (kstrtoul(buf, 0, &curr)) - return -EINVAL; + ret = lp55xx_read(chip, LP5521_REG_STATUS, &status); + if (ret < 0) + return ret; - if (curr > led->max_current) - return -EINVAL; + if (pdata->clock_mode != LP55XX_CLOCK_EXT) + return 0; - mutex_lock(&chip->lock); - ret = lp5521_set_led_current(chip, led->id, curr); - mutex_unlock(&chip->lock); + /* Check that ext clock is really in use if requested */ + if ((status & LP5521_EXT_CLK_USED) == 0) + return -EIO; - if (ret < 0) - return ret; + return 0; +} - led->led_current = (u8)curr; +static void lp5521_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; - return len; + mutex_lock(&chip->lock); + lp55xx_write(chip, LP5521_REG_LED_PWM_BASE + led->chan_nr, + led->brightness); + mutex_unlock(&chip->lock); } 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); @@ -534,133 +363,11 @@ 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) -{ - struct lp5521_led_pattern *ptn; - ptn = chip->pdata->patterns + (offset - 1); - 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); - } -} - -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 = strict_strtoul(buf, 16, &val); - if (ret) - return ret; - - lp5521_run_led_pattern(val, chip); - - 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 */ -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 }; @@ -668,210 +375,91 @@ 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 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); -} +/* Chip specific configurations */ +static struct lp55xx_device_config lp5521_cfg = { + .reset = { + .addr = LP5521_REG_RESET, + .val = LP5521_RESET, + }, + .enable = { + .addr = LP5521_REG_ENABLE, + .val = LP5521_ENABLE_DEFAULT, + }, + .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, + .firmware_cb = lp5521_firmware_loaded, + .run_engine = lp5521_run_engine, + .dev_attr_group = &lp5521_group, +}; -static int __devinit lp5521_init_led(struct lp5521_led *led, - struct i2c_client *client, - int chan, struct lp5521_platform_data *pdata) +static int lp5521_probe(struct i2c_client *client, + const struct i2c_device_id *id) { - 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; + int ret; + struct lp55xx_chip *chip; + struct lp55xx_led *led; + struct lp55xx_platform_data *pdata = client->dev.platform_data; - if (led->chan_nr >= LP5521_MAX_LEDS) { - dev_err(dev, "Use channel numbers between 0 and %d\n", - LP5521_MAX_LEDS - 1); + if (!pdata) { + dev_err(&client->dev, "no platform data\n"); 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 int __devinit 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; - u8 buf; - chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); if (!chip) return -ENOMEM; - i2c_set_clientdata(client, chip); - chip->client = client; - - pdata = client->dev.platform_data; + led = devm_kzalloc(&client->dev, + sizeof(*led) * pdata->num_channels, GFP_KERNEL); + if (!led) + return -ENOMEM; - if (!pdata) { - dev_err(&client->dev, "no platform data\n"); - return -EINVAL; - } + chip->cl = client; + chip->pdata = pdata; + chip->cfg = &lp5521_cfg; mutex_init(&chip->lock); - 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. - */ + i2c_set_clientdata(client, led); - /* - * 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 || buf != LP5521_REG_R_CURR_DEFAULT) { - dev_err(&client->dev, "error in resetting chip\n"); - goto fail2; - } - usleep_range(10000, 20000); - - ret = lp5521_detect(client); - - if (ret) { - dev_err(&client->dev, "Chip not found\n"); - goto fail2; - } + ret = lp55xx_init_device(chip); + if (ret) + 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; - } - - /* 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 = lp55xx_register_leds(led, chip); + 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: - for (i = 0; i < chip->num_leds; i++) { - led_classdev_unregister(&chip->leds[i].cdev); - cancel_work_sync(&chip->leds[i].brightness_work); - } -fail1: - if (pdata->enable) - pdata->enable(0); - if (pdata->release_resources) - pdata->release_resources(); + + return 0; + +err_register_sysfs: + lp55xx_unregister_leds(led, chip); +err_register_leds: + lp55xx_deinit_device(chip); +err_init: return ret; } -static int __devexit lp5521_remove(struct i2c_client *client) +static int lp5521_remove(struct i2c_client *client) { - struct lp5521_chip *chip = i2c_get_clientdata(client); - int i; + struct lp55xx_led *led = i2c_get_clientdata(client); + struct lp55xx_chip *chip = led->chip; - 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_stop_engine(chip); + lp55xx_unregister_sysfs(chip); + lp55xx_unregister_leds(led, chip); + lp55xx_deinit_device(chip); - if (chip->pdata->enable) - chip->pdata->enable(0); - if (chip->pdata->release_resources) - chip->pdata->release_resources(); return 0; } @@ -886,12 +474,13 @@ static struct i2c_driver lp5521_driver = { .name = "lp5521", }, .probe = lp5521_probe, - .remove = __devexit_p(lp5521_remove), + .remove = lp5521_remove, .id_table = lp5521_id, }; module_i2c_driver(lp5521_driver); MODULE_AUTHOR("Mathias Nyman, Yuri Zaporozhets, Samu Onkalo"); +MODULE_AUTHOR("Milo Kim <milo.kim@ti.com>"); MODULE_DESCRIPTION("LP5521 LED engine"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 97994ffdc014..229f734040af 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 <samu.p.onkalo@nokia.com> + * Milo(Woogyom) Kim <milo.kim@ti.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -20,488 +22,351 @@ * 02110-1301 USA */ -#include <linux/module.h> -#include <linux/init.h> -#include <linux/i2c.h> -#include <linux/mutex.h> -#include <linux/gpio.h> -#include <linux/interrupt.h> #include <linux/delay.h> -#include <linux/ctype.h> -#include <linux/spinlock.h> -#include <linux/wait.h> +#include <linux/firmware.h> +#include <linux/i2c.h> +#include <linux/init.h> #include <linux/leds.h> -#include <linux/leds-lp5523.h> -#include <linux/workqueue.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 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_DEFAULT_CURRENT 50 /* microAmps */ -#define LP5523_PROGRAM_LENGTH 32 /* in bytes */ -#define LP5523_PROGRAM_PAGES 6 +#define LP5523_RESET 0xFF #define LP5523_ADC_SHORTCIRC_LIM 80 - -#define LP5523_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 +#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; - 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_engine engines[LP5523_ENGINES]; - struct lp5523_led leds[LP5523_LEDS]; - struct lp5523_platform_data *pdata; - u8 num_channels; - u8 num_leds; -}; - -static inline struct lp5523_led *cdev_to_led(struct led_classdev *cdev) +static inline void lp5523_wait_opmode_done(void) { - 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 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); + usleep_range(1000, 2000); } -static int lp5523_read(struct i2c_client *client, u8 reg, u8 *buf) +static void lp5523_set_led_current(struct lp55xx_led *led, u8 led_current) { - s32 ret = i2c_smbus_read_byte_data(client, reg); - - if (ret < 0) - return -EIO; - - *buf = ret; - return 0; + led->led_current = led_current; + lp55xx_write(led->chip, LP5523_REG_LED_CURRENT_BASE + led->chan_nr, + led_current); } -static int lp5523_detect(struct i2c_client *client) +static int lp5523_post_init_device(struct lp55xx_chip *chip) { int ret; - u8 buf; - ret = lp5523_write(client, LP5523_REG_ENABLE, LP5523_ENABLE); + ret = lp55xx_write(chip, 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) -{ - 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); /* 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); + 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); - lp5523_read(client, LP5523_REG_STATUS, &status); - 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); + ret = lp55xx_write(chip, LP5523_REG_ENABLE_LEDS_MSB, 0x01); + if (ret) + return ret; - return ret; + 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; + 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, + }; - ret = lp5523_read(client, LP5523_REG_OP_MODE, &engine_state); - if (ret) - goto fail; + u8 val[] = { + [LP55XX_ENGINE_1] = LP5523_LOAD_ENG1, + [LP55XX_ENGINE_2] = LP5523_LOAD_ENG2, + [LP55XX_ENGINE_3] = LP5523_LOAD_ENG3, + }; - engine_state &= ~(engine->engine_mask); + u8 page_sel[] = { + [LP55XX_ENGINE_1] = LP5523_PAGE_ENG1, + [LP55XX_ENGINE_2] = LP5523_PAGE_ENG2, + [LP55XX_ENGINE_3] = LP5523_PAGE_ENG3, + }; - /* set mode only for this engine */ - mode &= engine->engine_mask; + lp55xx_update_bits(chip, LP5523_REG_OP_MODE, mask[idx], val[idx]); - engine_state |= mode; + lp5523_wait_opmode_done(); - ret |= lp5523_write(client, LP5523_REG_OP_MODE, engine_state); -fail: - return ret; + 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_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_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_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; + const struct firmware *fw = chip->fw; - if (lp5523_mux_parse(buf, &mux, len)) - return -EINVAL; - - mutex_lock(&chip->lock); - ret = -EINVAL; - if (chip->engines[nr - 1].mode != LP5523_CMD_LOAD) - goto leave; + if (fw->size > LP5523_PROGRAM_LENGTH) { + dev_err(&chip->cl->dev, "firmware data size overflow: %zu\n", + fw->size); + return; + } - if (lp5523_load_mux(&chip->engines[nr - 1], mux)) - goto leave; + /* + * Program momery sequence + * 1) set engine mode to "LOAD" + * 2) write firmware data into program memory + */ - ret = len; -leave: - mutex_unlock(&chip->lock); - return ret; + lp5523_load_engine(chip); + lp5523_update_program_memory(chip, fw->data, fw->size); } -#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); \ -} -store_leds(1) -store_leds(2) -store_leds(3) - 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; + 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) + 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) @@ -516,249 +381,22 @@ 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); -} - -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) - -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 (strict_strtoul(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 */ -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, }; @@ -766,252 +404,91 @@ 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 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); -} - -/*--------------------------------------------------------------*/ -/* 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 */ -/*--------------------------------------------------------------*/ -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; -} +/* Chip specific configurations */ +static struct lp55xx_device_config lp5523_cfg = { + .reset = { + .addr = LP5523_REG_RESET, + .val = LP5523_RESET, + }, + .enable = { + .addr = LP5523_REG_ENABLE, + .val = LP5523_ENABLE, + }, + .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, + .firmware_cb = lp5523_firmware_loaded, + .run_engine = lp5523_run_engine, + .dev_attr_group = &lp5523_group, +}; -static int __devinit lp5523_init_led(struct lp5523_led *led, struct device *dev, - int chan, struct lp5523_platform_data *pdata, - const char *chip_name) +static int lp5523_probe(struct i2c_client *client, + const struct i2c_device_id *id) { - char name[32]; - int res; + int ret; + struct lp55xx_chip *chip; + struct lp55xx_led *led; + struct lp55xx_platform_data *pdata = client->dev.platform_data; - if (chan >= LP5523_LEDS) + if (!pdata) { + dev_err(&client->dev, "no platform data\n"); 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 int __devinit lp5523_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct lp5523_chip *chip; - struct lp5523_platform_data *pdata; - int ret, i, led; chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); if (!chip) return -ENOMEM; - i2c_set_clientdata(client, chip); - chip->client = client; + led = devm_kzalloc(&client->dev, + sizeof(*led) * pdata->num_channels, GFP_KERNEL); + if (!led) + return -ENOMEM; - pdata = client->dev.platform_data; - - if (!pdata) { - dev_err(&client->dev, "no platform data\n"); - return -EINVAL; - } + chip->cl = client; + chip->pdata = pdata; + chip->cfg = &lp5523_cfg; mutex_init(&chip->lock); - chip->pdata = pdata; - - if (pdata->setup_resources) { - ret = pdata->setup_resources(); - if (ret < 0) - return ret; - } + i2c_set_clientdata(client, led); - 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 = lp55xx_init_device(chip); if (ret) - goto fail1; + 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); - if (ret) { - dev_err(&client->dev, "error initializing engine\n"); - goto fail1; - } - } - ret = lp5523_configure(client); - if (ret < 0) { - dev_err(&client->dev, "error configuring chip\n"); - 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 = lp55xx_register_leds(led, chip); + 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: - for (i = 0; i < chip->num_leds; i++) { - led_classdev_unregister(&chip->leds[i].cdev); - flush_work(&chip->leds[i].brightness_work); - } -fail1: - if (pdata->enable) - pdata->enable(0); - if (pdata->release_resources) - pdata->release_resources(); + + return 0; + +err_register_sysfs: + lp55xx_unregister_leds(led, chip); +err_register_leds: + lp55xx_deinit_device(chip); +err_init: return ret; } 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); + struct lp55xx_led *led = i2c_get_clientdata(client); + struct lp55xx_chip *chip = led->chip; - 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_stop_engine(chip); + lp55xx_unregister_sysfs(chip); + lp55xx_unregister_leds(led, chip); + lp55xx_deinit_device(chip); - if (chip->pdata->enable) - chip->pdata->enable(0); - if (chip->pdata->release_resources) - chip->pdata->release_resources(); return 0; } @@ -1035,5 +512,6 @@ static struct i2c_driver lp5523_driver = { module_i2c_driver(lp5523_driver); MODULE_AUTHOR("Mathias Nyman <mathias.nyman@nokia.com>"); +MODULE_AUTHOR("Milo Kim <milo.kim@ti.com>"); MODULE_DESCRIPTION("LP5523 LED engine"); MODULE_LICENSE("GPL"); diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c new file mode 100644 index 000000000000..d9eb84157423 --- /dev/null +++ b/drivers/leds/leds-lp55xx-common.c @@ -0,0 +1,523 @@ +/* + * LP5521/LP5523/LP55231 Common Driver + * + * Copyright 2012 Texas Instruments + * + * Author: Milo(Woogyom) Kim <milo.kim@ti.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. + * + * Derived from leds-lp5521.c, leds-lp5523.c + */ + +#include <linux/delay.h> +#include <linux/firmware.h> +#include <linux/i2c.h> +#include <linux/leds.h> +#include <linux/module.h> +#include <linux/platform_data/leds-lp55xx.h> + +#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 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; + 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); +} + +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; +} + +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); +} + +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, +}; + +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) +{ + 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, + 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; +} + +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, +}; + +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); +} +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); + +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 || !cfg) + 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. */ + } + + lp55xx_reset_device(chip); + + /* + * Exact value is not available. 10 - 20ms + * appears to be enough for reset. + */ + usleep_range(10000, 20000); + + ret = lp55xx_detect_device(chip); + if (ret) { + dev_err(dev, "device detection err: %d\n", ret); + goto err; + } + + /* 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: + 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(); +} +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: + lp55xx_unregister_leds(led, chip); + return ret; +} +EXPORT_SYMBOL_GPL(lp55xx_register_leds); + +void lp55xx_unregister_leds(struct lp55xx_led *led, struct lp55xx_chip *chip) +{ + int i; + struct lp55xx_led *each; + + for (i = 0; i < chip->num_leds; i++) { + each = led + i; + led_classdev_unregister(&each->cdev); + flush_work(&each->brightness_work); + } +} +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; + +dev_specific_attrs: + return cfg->dev_attr_group ? + sysfs_create_group(&dev->kobj, cfg->dev_attr_group) : 0; +} +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 <milo.kim@ti.com>"); +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 000000000000..ece4761a1302 --- /dev/null +++ b/drivers/leds/leds-lp55xx-common.h @@ -0,0 +1,134 @@ +/* + * LP55XX Common Driver Header + * + * Copyright (C) 2012 Texas Instruments + * + * Author: Milo(Woogyom) Kim <milo.kim@ti.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. + * + * Derived from leds-lp5521.c, leds-lp5523.c + */ + +#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; + +/* + * 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 + * @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 + * @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; + 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); + + /* 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); + + /* 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); + + /* additional device specific attributes */ + const struct attribute_group *dev_attr_group; +}; + +/* + * 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 + * @engine_idx : Selected engine number + * @fw : Firmware data for running a LED pattern + */ +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; + enum lp55xx_engine_index engine_idx; + const struct firmware *fw; +}; + +/* + * 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); + +/* common device init/deinit functions */ +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); +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); +extern void lp55xx_unregister_sysfs(struct lp55xx_chip *chip); + +#endif /* _LEDS_LP55XX_COMMON_H */ diff --git a/drivers/leds/leds-lp8788.c b/drivers/leds/leds-lp8788.c index 64009a176651..7c2cb384e7ae 100644 --- a/drivers/leds/leds-lp8788.c +++ b/drivers/leds/leds-lp8788.c @@ -125,14 +125,15 @@ static void lp8788_brightness_set(struct led_classdev *led_cdev, schedule_work(&led->work); } -static __devinit int lp8788_led_probe(struct platform_device *pdev) +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,20 +155,20 @@ static __devinit 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; } return 0; } -static int __devexit lp8788_led_remove(struct platform_device *pdev) +static int lp8788_led_remove(struct platform_device *pdev) { struct lp8788_led *led = platform_get_drvdata(pdev); @@ -179,7 +180,7 @@ static int __devexit lp8788_led_remove(struct platform_device *pdev) static struct platform_driver lp8788_led_driver = { .probe = lp8788_led_probe, - .remove = __devexit_p(lp8788_led_remove), + .remove = lp8788_led_remove, .driver = { .name = LP8788_DEV_KEYLED, .owner = THIS_MODULE, diff --git a/drivers/leds/leds-lt3593.c b/drivers/leds/leds-lt3593.c index 09a732217f6d..c9b9e1fec587 100644 --- a/drivers/leds/leds-lt3593.c +++ b/drivers/leds/leds-lt3593.c @@ -82,22 +82,18 @@ static void lt3593_led_set(struct led_classdev *led_cdev, schedule_work(&led_dat->work); } -static int __devinit create_lt3593_led(const struct gpio_led *template, +static int create_lt3593_led(const struct gpio_led *template, struct lt3593_led_data *led_dat, struct device *parent) { int ret, state; /* skip leds on GPIOs that aren't available */ if (!gpio_is_valid(template->gpio)) { - printk(KERN_INFO "%s: skipping unavailable LT3593 LED at gpio %d (%s)\n", + dev_info(parent, "%s: skipping unavailable LT3593 LED at gpio %d (%s)\n", KBUILD_MODNAME, template->gpio, template->name); return 0; } - ret = gpio_request(template->gpio, template->name); - if (ret < 0) - return ret; - led_dat->cdev.name = template->name; led_dat->cdev.default_trigger = template->default_trigger; led_dat->gpio = template->gpio; @@ -110,24 +106,21 @@ static int __devinit create_lt3593_led(const struct gpio_led *template, if (!template->retain_state_suspended) led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; - ret = gpio_direction_output(led_dat->gpio, state); + ret = devm_gpio_request_one(parent, template->gpio, + GPIOF_DIR_OUT | state, template->name); if (ret < 0) - goto err; + return ret; INIT_WORK(&led_dat->work, lt3593_led_work); ret = led_classdev_register(parent, &led_dat->cdev); if (ret < 0) - goto err; + return ret; - printk(KERN_INFO "%s: registered LT3593 LED '%s' at GPIO %d\n", + dev_info(parent, "%s: registered LT3593 LED '%s' at GPIO %d\n", KBUILD_MODNAME, template->name, template->gpio); return 0; - -err: - gpio_free(led_dat->gpio); - return ret; } static void delete_lt3593_led(struct lt3593_led_data *led) @@ -137,10 +130,9 @@ static void delete_lt3593_led(struct lt3593_led_data *led) led_classdev_unregister(&led->cdev); cancel_work_sync(&led->work); - gpio_free(led->gpio); } -static int __devinit lt3593_led_probe(struct platform_device *pdev) +static int lt3593_led_probe(struct platform_device *pdev) { struct gpio_led_platform_data *pdata = pdev->dev.platform_data; struct lt3593_led_data *leds_data; @@ -173,7 +165,7 @@ err: return ret; } -static int __devexit lt3593_led_remove(struct platform_device *pdev) +static int lt3593_led_remove(struct platform_device *pdev) { int i; struct gpio_led_platform_data *pdata = pdev->dev.platform_data; @@ -189,7 +181,7 @@ static int __devexit lt3593_led_remove(struct platform_device *pdev) static struct platform_driver lt3593_led_driver = { .probe = lt3593_led_probe, - .remove = __devexit_p(lt3593_led_remove), + .remove = lt3593_led_remove, .driver = { .name = "leds-lt3593", .owner = THIS_MODULE, diff --git a/drivers/leds/leds-max8997.c b/drivers/leds/leds-max8997.c index 569e36de37df..f449a8bdddc7 100644 --- a/drivers/leds/leds-max8997.c +++ b/drivers/leds/leds-max8997.c @@ -229,7 +229,7 @@ static ssize_t max8997_led_store_mode(struct device *dev, static DEVICE_ATTR(mode, 0644, max8997_led_show_mode, max8997_led_store_mode); -static int __devinit max8997_led_probe(struct platform_device *pdev) +static int max8997_led_probe(struct platform_device *pdev) { struct max8997_dev *iodev = dev_get_drvdata(pdev->dev.parent); struct max8997_platform_data *pdata = dev_get_platdata(iodev->dev); @@ -292,7 +292,7 @@ static int __devinit max8997_led_probe(struct platform_device *pdev) return 0; } -static int __devexit max8997_led_remove(struct platform_device *pdev) +static int max8997_led_remove(struct platform_device *pdev) { struct max8997_led *led = platform_get_drvdata(pdev); @@ -308,7 +308,7 @@ static struct platform_driver max8997_led_driver = { .owner = THIS_MODULE, }, .probe = max8997_led_probe, - .remove = __devexit_p(max8997_led_remove), + .remove = max8997_led_remove, }; module_platform_driver(max8997_led_driver); diff --git a/drivers/leds/leds-mc13783.c b/drivers/leds/leds-mc13783.c index 2a5d43400677..e942adaa7504 100644 --- a/drivers/leds/leds-mc13783.c +++ b/drivers/leds/leds-mc13783.c @@ -128,7 +128,7 @@ static void mc13783_led_set(struct led_classdev *led_cdev, schedule_work(&led->work); } -static int __devinit mc13783_led_setup(struct mc13783_led *led, int max_current) +static int mc13783_led_setup(struct mc13783_led *led, int max_current) { int shift = 0; int mask = 0; @@ -181,7 +181,7 @@ static int __devinit mc13783_led_setup(struct mc13783_led *led, int max_current) return ret; } -static int __devinit mc13783_leds_prepare(struct platform_device *pdev) +static int mc13783_leds_prepare(struct platform_device *pdev) { struct mc13xxx_leds_platform_data *pdata = dev_get_platdata(&pdev->dev); struct mc13xxx *dev = dev_get_drvdata(pdev->dev.parent); @@ -262,7 +262,7 @@ out: return ret; } -static int __devinit mc13783_led_probe(struct platform_device *pdev) +static int mc13783_led_probe(struct platform_device *pdev) { struct mc13xxx_leds_platform_data *pdata = dev_get_platdata(&pdev->dev); struct mc13xxx_led_platform_data *led_cur; @@ -348,7 +348,7 @@ err_register: return ret; } -static int __devexit mc13783_led_remove(struct platform_device *pdev) +static int mc13783_led_remove(struct platform_device *pdev) { struct mc13xxx_leds_platform_data *pdata = dev_get_platdata(&pdev->dev); struct mc13783_led *led = platform_get_drvdata(pdev); @@ -381,7 +381,7 @@ static struct platform_driver mc13783_led_driver = { .owner = THIS_MODULE, }, .probe = mc13783_led_probe, - .remove = __devexit_p(mc13783_led_remove), + .remove = mc13783_led_remove, }; module_platform_driver(mc13783_led_driver); diff --git a/drivers/leds/leds-net48xx.c b/drivers/leds/leds-net48xx.c index f117f7326c5b..27d06c528246 100644 --- a/drivers/leds/leds-net48xx.c +++ b/drivers/leds/leds-net48xx.c @@ -15,7 +15,7 @@ #include <linux/platform_device.h> #include <linux/leds.h> #include <linux/err.h> -#include <asm/io.h> +#include <linux/io.h> #include <linux/nsc_gpio.h> #include <linux/scx200_gpio.h> #include <linux/module.h> diff --git a/drivers/leds/leds-netxbig.c b/drivers/leds/leds-netxbig.c index 461bbf9b33fa..c61c5ebcc08e 100644 --- a/drivers/leds/leds-netxbig.c +++ b/drivers/leds/leds-netxbig.c @@ -71,7 +71,7 @@ static void gpio_ext_set_value(struct netxbig_gpio_ext *gpio_ext, spin_unlock_irqrestore(&gpio_ext_lock, flags); } -static int __devinit gpio_ext_init(struct netxbig_gpio_ext *gpio_ext) +static int gpio_ext_init(struct netxbig_gpio_ext *gpio_ext) { int err; int i; @@ -243,7 +243,7 @@ static ssize_t netxbig_led_sata_store(struct device *dev, int mode_val; int ret; - ret = strict_strtoul(buff, 10, &enable); + ret = kstrtoul(buff, 10, &enable); if (ret < 0) return ret; @@ -301,7 +301,7 @@ static void delete_netxbig_led(struct netxbig_led_data *led_dat) led_classdev_unregister(&led_dat->cdev); } -static int __devinit +static int create_netxbig_led(struct platform_device *pdev, struct netxbig_led_data *led_dat, const struct netxbig_led *template) @@ -352,7 +352,7 @@ create_netxbig_led(struct platform_device *pdev, return ret; } -static int __devinit netxbig_led_probe(struct platform_device *pdev) +static int netxbig_led_probe(struct platform_device *pdev) { struct netxbig_led_platform_data *pdata = pdev->dev.platform_data; struct netxbig_led_data *leds_data; @@ -389,7 +389,7 @@ err_free_leds: return ret; } -static int __devexit netxbig_led_remove(struct platform_device *pdev) +static int netxbig_led_remove(struct platform_device *pdev) { struct netxbig_led_platform_data *pdata = pdev->dev.platform_data; struct netxbig_led_data *leds_data; @@ -407,7 +407,7 @@ static int __devexit netxbig_led_remove(struct platform_device *pdev) static struct platform_driver netxbig_led_driver = { .probe = netxbig_led_probe, - .remove = __devexit_p(netxbig_led_remove), + .remove = netxbig_led_remove, .driver = { .name = "leds-netxbig", .owner = THIS_MODULE, diff --git a/drivers/leds/leds-ns2.c b/drivers/leds/leds-ns2.c index d176ec83f5d9..d978171c25b4 100644 --- a/drivers/leds/leds-ns2.c +++ b/drivers/leds/leds-ns2.c @@ -30,6 +30,7 @@ #include <linux/leds.h> #include <linux/module.h> #include <linux/platform_data/leds-kirkwood-ns2.h> +#include <linux/of_gpio.h> /* * The Network Space v2 dual-GPIO LED is wired to a CPLD and can blink in @@ -149,7 +150,7 @@ static ssize_t ns2_led_sata_store(struct device *dev, unsigned long enable; enum ns2_led_modes mode; - ret = strict_strtoul(buff, 10, &enable); + ret = kstrtoul(buff, 10, &enable); if (ret < 0) return ret; @@ -184,36 +185,29 @@ static ssize_t ns2_led_sata_show(struct device *dev, static DEVICE_ATTR(sata, 0644, ns2_led_sata_show, ns2_led_sata_store); -static int __devinit +static int create_ns2_led(struct platform_device *pdev, struct ns2_led_data *led_dat, const struct ns2_led *template) { int ret; enum ns2_led_modes mode; - ret = gpio_request(template->cmd, template->name); - if (ret == 0) { - ret = gpio_direction_output(template->cmd, - gpio_get_value(template->cmd)); - if (ret) - gpio_free(template->cmd); - } + ret = devm_gpio_request_one(&pdev->dev, template->cmd, + GPIOF_DIR_OUT | gpio_get_value(template->cmd), + template->name); if (ret) { dev_err(&pdev->dev, "%s: failed to setup command GPIO\n", template->name); + return ret; } - ret = gpio_request(template->slow, template->name); - if (ret == 0) { - ret = gpio_direction_output(template->slow, - gpio_get_value(template->slow)); - if (ret) - gpio_free(template->slow); - } + ret = devm_gpio_request_one(&pdev->dev, template->slow, + GPIOF_DIR_OUT | gpio_get_value(template->slow), + template->name); if (ret) { dev_err(&pdev->dev, "%s: failed to setup slow GPIO\n", template->name); - goto err_free_cmd; + return ret; } rwlock_init(&led_dat->rw_lock); @@ -228,7 +222,7 @@ create_ns2_led(struct platform_device *pdev, struct ns2_led_data *led_dat, ret = ns2_led_get_mode(led_dat, &mode); if (ret < 0) - goto err_free_slow; + return ret; /* Set LED initial state. */ led_dat->sata = (mode == NS_V2_LED_SATA) ? 1 : 0; @@ -237,7 +231,7 @@ create_ns2_led(struct platform_device *pdev, struct ns2_led_data *led_dat, ret = led_classdev_register(&pdev->dev, &led_dat->cdev); if (ret < 0) - goto err_free_slow; + return ret; ret = device_create_file(led_dat->cdev.dev, &dev_attr_sata); if (ret < 0) @@ -247,11 +241,6 @@ create_ns2_led(struct platform_device *pdev, struct ns2_led_data *led_dat, err_free_cdev: led_classdev_unregister(&led_dat->cdev); -err_free_slow: - gpio_free(led_dat->slow); -err_free_cmd: - gpio_free(led_dat->cmd); - return ret; } @@ -259,22 +248,90 @@ static void delete_ns2_led(struct ns2_led_data *led_dat) { device_remove_file(led_dat->cdev.dev, &dev_attr_sata); led_classdev_unregister(&led_dat->cdev); - gpio_free(led_dat->cmd); - gpio_free(led_dat->slow); } -static int __devinit ns2_led_probe(struct platform_device *pdev) +#ifdef CONFIG_OF_GPIO +/* + * Translate OpenFirmware node properties into platform_data. + */ +static int +ns2_leds_get_of_pdata(struct device *dev, struct ns2_led_platform_data *pdata) +{ + struct device_node *np = dev->of_node; + struct device_node *child; + struct ns2_led *leds; + int num_leds = 0; + int i = 0; + + num_leds = of_get_child_count(np); + if (!num_leds) + return -ENODEV; + + leds = devm_kzalloc(dev, num_leds * sizeof(struct ns2_led), + GFP_KERNEL); + if (!leds) + return -ENOMEM; + + for_each_child_of_node(np, child) { + const char *string; + int ret; + + ret = of_get_named_gpio(child, "cmd-gpio", 0); + if (ret < 0) + return ret; + leds[i].cmd = ret; + ret = of_get_named_gpio(child, "slow-gpio", 0); + if (ret < 0) + return ret; + leds[i].slow = ret; + ret = of_property_read_string(child, "label", &string); + leds[i].name = (ret == 0) ? string : child->name; + ret = of_property_read_string(child, "linux,default-trigger", + &string); + if (ret == 0) + leds[i].default_trigger = string; + + i++; + } + + pdata->leds = leds; + pdata->num_leds = num_leds; + + return 0; +} + +static const struct of_device_id of_ns2_leds_match[] = { + { .compatible = "lacie,ns2-leds", }, + {}, +}; +#endif /* CONFIG_OF_GPIO */ + +static int ns2_led_probe(struct platform_device *pdev) { struct ns2_led_platform_data *pdata = pdev->dev.platform_data; struct ns2_led_data *leds_data; int i; int ret; +#ifdef CONFIG_OF_GPIO + if (!pdata) { + pdata = devm_kzalloc(&pdev->dev, + sizeof(struct ns2_led_platform_data), + GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + ret = ns2_leds_get_of_pdata(&pdev->dev, pdata); + if (ret) + return ret; + } +#else if (!pdata) return -EINVAL; +#endif /* CONFIG_OF_GPIO */ leds_data = devm_kzalloc(&pdev->dev, sizeof(struct ns2_led_data) * - pdata->num_leds, GFP_KERNEL); + pdata->num_leds, GFP_KERNEL); if (!leds_data) return -ENOMEM; @@ -292,7 +349,7 @@ static int __devinit ns2_led_probe(struct platform_device *pdev) return 0; } -static int __devexit ns2_led_remove(struct platform_device *pdev) +static int ns2_led_remove(struct platform_device *pdev) { int i; struct ns2_led_platform_data *pdata = pdev->dev.platform_data; @@ -310,10 +367,11 @@ static int __devexit ns2_led_remove(struct platform_device *pdev) static struct platform_driver ns2_led_driver = { .probe = ns2_led_probe, - .remove = __devexit_p(ns2_led_remove), + .remove = ns2_led_remove, .driver = { - .name = "leds-ns2", - .owner = THIS_MODULE, + .name = "leds-ns2", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(of_ns2_leds_match), }, }; diff --git a/drivers/leds/leds-ot200.c b/drivers/leds/leds-ot200.c index c4646825a620..ee14662ed5ce 100644 --- a/drivers/leds/leds-ot200.c +++ b/drivers/leds/leds-ot200.c @@ -115,7 +115,7 @@ static void ot200_led_brightness_set(struct led_classdev *led_cdev, spin_unlock_irqrestore(&value_lock, flags); } -static int __devinit ot200_led_probe(struct platform_device *pdev) +static int ot200_led_probe(struct platform_device *pdev) { int i; int ret; @@ -144,7 +144,7 @@ err: return ret; } -static int __devexit ot200_led_remove(struct platform_device *pdev) +static int ot200_led_remove(struct platform_device *pdev) { int i; @@ -156,7 +156,7 @@ static int __devexit ot200_led_remove(struct platform_device *pdev) static struct platform_driver ot200_led_driver = { .probe = ot200_led_probe, - .remove = __devexit_p(ot200_led_remove), + .remove = ot200_led_remove, .driver = { .name = "leds-ot200", .owner = THIS_MODULE, diff --git a/drivers/leds/leds-pca9532.c b/drivers/leds/leds-pca9532.c index cee8a5b483ac..0c597bdd23f9 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; } @@ -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; diff --git a/drivers/leds/leds-pca955x.c b/drivers/leds/leds-pca955x.c index aef3cf0432fe..edf485b773c8 100644 --- a/drivers/leds/leds-pca955x.c +++ b/drivers/leds/leds-pca955x.c @@ -255,7 +255,7 @@ static void pca955x_led_set(struct led_classdev *led_cdev, enum led_brightness v schedule_work(&pca955x->work); } -static int __devinit pca955x_probe(struct i2c_client *client, +static int pca955x_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct pca955x *pca955x; @@ -277,7 +277,7 @@ static int __devinit pca955x_probe(struct i2c_client *client, return -ENODEV; } - printk(KERN_INFO "leds-pca955x: Using %s %d-bit LED driver at " + dev_info(&client->dev, "leds-pca955x: Using %s %d-bit LED driver at " "slave address 0x%02x\n", id->name, chip->bits, client->addr); @@ -363,7 +363,7 @@ exit: return err; } -static int __devexit pca955x_remove(struct i2c_client *client) +static int pca955x_remove(struct i2c_client *client) { struct pca955x *pca955x = i2c_get_clientdata(client); int i; @@ -382,7 +382,7 @@ static struct i2c_driver pca955x_driver = { .owner = THIS_MODULE, }, .probe = pca955x_probe, - .remove = __devexit_p(pca955x_remove), + .remove = pca955x_remove, .id_table = pca955x_id, }; diff --git a/drivers/leds/leds-pca9633.c b/drivers/leds/leds-pca9633.c index 2f2f9c43535d..9aae5679ffb2 100644 --- a/drivers/leds/leds-pca9633.c +++ b/drivers/leds/leds-pca9633.c @@ -93,7 +93,7 @@ static void pca9633_led_set(struct led_classdev *led_cdev, schedule_work(&pca9633->work); } -static int __devinit pca9633_probe(struct i2c_client *client, +static int pca9633_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct pca9633_led *pca9633; @@ -164,7 +164,7 @@ exit: return err; } -static int __devexit pca9633_remove(struct i2c_client *client) +static int pca9633_remove(struct i2c_client *client) { struct pca9633_led *pca9633 = i2c_get_clientdata(client); int i; @@ -183,7 +183,7 @@ static struct i2c_driver pca9633_driver = { .owner = THIS_MODULE, }, .probe = pca9633_probe, - .remove = __devexit_p(pca9633_remove), + .remove = pca9633_remove, .id_table = pca9633_id, }; diff --git a/drivers/leds/leds-pwm.c b/drivers/leds/leds-pwm.c index f2e44c719437..a1ea5f6a8d39 100644 --- a/drivers/leds/leds-pwm.c +++ b/drivers/leds/leds-pwm.c @@ -16,6 +16,7 @@ #include <linux/kernel.h> #include <linux/init.h> #include <linux/platform_device.h> +#include <linux/of_platform.h> #include <linux/fb.h> #include <linux/leds.h> #include <linux/err.h> @@ -26,10 +27,15 @@ struct led_pwm_data { struct led_classdev cdev; struct pwm_device *pwm; - unsigned int active_low; + unsigned int active_low; 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,88 +53,152 @@ static void led_pwm_set(struct led_classdev *led_cdev, } } -static int led_pwm_probe(struct platform_device *pdev) +static inline size_t sizeof_pwm_leds_priv(int num_leds) { - struct led_pwm_platform_data *pdata = pdev->dev.platform_data; - struct led_pwm *cur_led; - struct led_pwm_data *leds_data, *led_dat; - int i, ret = 0; + return sizeof(struct led_pwm_priv) + + (sizeof(struct led_pwm_data) * num_leds); +} + +static struct led_pwm_priv *led_pwm_create_of(struct platform_device *pdev) +{ + struct device_node *node = pdev->dev.of_node; + struct device_node *child; + struct led_pwm_priv *priv; + 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; - leds_data = devm_kzalloc(&pdev->dev, - sizeof(struct led_pwm_data) * pdata->num_leds, - GFP_KERNEL); - if (!leds_data) - return -ENOMEM; + priv = devm_kzalloc(&pdev->dev, sizeof_pwm_leds_priv(count), + GFP_KERNEL); + if (!priv) + return NULL; - for (i = 0; i < pdata->num_leds; i++) { - cur_led = &pdata->leds[i]; - led_dat = &leds_data[i]; + for_each_child_of_node(node, child) { + struct led_pwm_data *led_dat = &priv->leds[priv->num_leds]; - led_dat->pwm = pwm_request(cur_led->pwm_id, - 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 %d\n", - cur_led->pwm_id); + dev_err(&pdev->dev, "unable to request PWM for %s\n", + 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) { - pwm_free(led_dat->pwm); + dev_err(&pdev->dev, "failed to register for %s\n", + led_dat->cdev.name); + of_node_put(child); goto err; } + priv->num_leds++; } - platform_set_drvdata(pdev, leds_data); + return priv; +err: + while (priv->num_leds--) + led_classdev_unregister(&priv->leds[priv->num_leds].cdev); - return 0; + return NULL; +} -err: - if (i > 0) { - for (i = i - 1; i >= 0; i--) { - led_classdev_unregister(&leds_data[i].cdev); - pwm_free(leds_data[i].pwm); +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; } + platform_set_drvdata(pdev, priv); + + return 0; + +err: + while (i--) + led_classdev_unregister(&priv->leds[i].cdev); + return ret; } -static int __devexit led_pwm_remove(struct platform_device *pdev) +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); - pwm_free(leds_data[i].pwm); - } + for (i = 0; i < priv->num_leds; i++) + led_classdev_unregister(&priv->leds[i].cdev); 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 = __devexit_p(led_pwm_remove), + .remove = led_pwm_remove, .driver = { .name = "leds_pwm", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(of_pwm_leds_match), }, }; diff --git a/drivers/leds/leds-rb532.c b/drivers/leds/leds-rb532.c index a7815b6cd856..2e746d257b02 100644 --- a/drivers/leds/leds-rb532.c +++ b/drivers/leds/leds-rb532.c @@ -16,7 +16,7 @@ #include <asm/mach-rc32434/rb.h> static void rb532_led_set(struct led_classdev *cdev, - enum led_brightness brightness) + enum led_brightness brightness) { if (brightness) set_latch_u5(LO_ULED, 0); @@ -37,12 +37,12 @@ static struct led_classdev rb532_uled = { .default_trigger = "nand-disk", }; -static int __devinit rb532_led_probe(struct platform_device *pdev) +static int rb532_led_probe(struct platform_device *pdev) { return led_classdev_register(&pdev->dev, &rb532_uled); } -static int __devexit rb532_led_remove(struct platform_device *pdev) +static int rb532_led_remove(struct platform_device *pdev) { led_classdev_unregister(&rb532_uled); return 0; @@ -50,7 +50,7 @@ static int __devexit rb532_led_remove(struct platform_device *pdev) static struct platform_driver rb532_led_driver = { .probe = rb532_led_probe, - .remove = __devexit_p(rb532_led_remove), + .remove = rb532_led_remove, .driver = { .name = "rb532-led", .owner = THIS_MODULE, diff --git a/drivers/leds/leds-regulator.c b/drivers/leds/leds-regulator.c index 25d382d60fa9..4253a9b03dbf 100644 --- a/drivers/leds/leds-regulator.c +++ b/drivers/leds/leds-regulator.c @@ -140,7 +140,7 @@ static void regulator_led_brightness_set(struct led_classdev *led_cdev, schedule_work(&led->work); } -static int __devinit regulator_led_probe(struct platform_device *pdev) +static int regulator_led_probe(struct platform_device *pdev) { struct led_regulator_platform_data *pdata = pdev->dev.platform_data; struct regulator_led *led; @@ -206,7 +206,7 @@ err_vcc: return ret; } -static int __devexit regulator_led_remove(struct platform_device *pdev) +static int regulator_led_remove(struct platform_device *pdev) { struct regulator_led *led = platform_get_drvdata(pdev); @@ -223,7 +223,7 @@ static struct platform_driver regulator_led_driver = { .owner = THIS_MODULE, }, .probe = regulator_led_probe, - .remove = __devexit_p(regulator_led_remove), + .remove = regulator_led_remove, }; module_platform_driver(regulator_led_driver); diff --git a/drivers/leds/leds-renesas-tpu.c b/drivers/leds/leds-renesas-tpu.c index 771ea067e680..d3c2b7e68fbc 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); @@ -204,10 +204,10 @@ static void r_tpu_set_pin(struct r_tpu_priv *p, enum r_tpu_pin new_state, if (p->pin_state == R_TPU_PIN_GPIO_FN) gpio_free(cfg->pin_gpio_fn); - if (new_state == R_TPU_PIN_GPIO) { - gpio_request(cfg->pin_gpio, cfg->name); - gpio_direction_output(cfg->pin_gpio, !!brightness); - } + if (new_state == R_TPU_PIN_GPIO) + gpio_request_one(cfg->pin_gpio, GPIOF_DIR_OUT | !!brightness, + cfg->name); + if (new_state == R_TPU_PIN_GPIO_FN) gpio_request(cfg->pin_gpio_fn, cfg->name); @@ -238,7 +238,7 @@ static void r_tpu_set_brightness(struct led_classdev *ldev, schedule_work(&p->work); } -static int __devinit r_tpu_probe(struct platform_device *pdev) +static int r_tpu_probe(struct platform_device *pdev) { struct led_renesas_tpu_config *cfg = pdev->dev.platform_data; struct r_tpu_priv *p; @@ -263,18 +263,18 @@ static int __devinit r_tpu_probe(struct platform_device *pdev) } /* map memory, let mapbase point to our channel */ - p->mapbase = ioremap_nocache(res->start, resource_size(res)); + p->mapbase = devm_ioremap_nocache(&pdev->dev, res->start, + resource_size(res)); if (p->mapbase == NULL) { dev_err(&pdev->dev, "failed to remap I/O memory\n"); return -ENXIO; } /* get hold of clock */ - p->clk = clk_get(&pdev->dev, NULL); + p->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(p->clk)) { dev_err(&pdev->dev, "cannot get clock\n"); - ret = PTR_ERR(p->clk); - goto err0; + return PTR_ERR(p->clk); } p->pdev = pdev; @@ -293,7 +293,7 @@ static int __devinit r_tpu_probe(struct platform_device *pdev) p->ldev.flags |= LED_CORE_SUSPENDRESUME; ret = led_classdev_register(&pdev->dev, &p->ldev); if (ret < 0) - goto err1; + goto err0; /* max_brightness may be updated by the LED core code */ p->min_rate = p->ldev.max_brightness * p->refresh_rate; @@ -301,15 +301,12 @@ static int __devinit r_tpu_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); return 0; - err1: - r_tpu_set_pin(p, R_TPU_PIN_UNUSED, LED_OFF); - clk_put(p->clk); err0: - iounmap(p->mapbase); + r_tpu_set_pin(p, R_TPU_PIN_UNUSED, LED_OFF); return ret; } -static int __devexit r_tpu_remove(struct platform_device *pdev) +static int r_tpu_remove(struct platform_device *pdev) { struct r_tpu_priv *p = platform_get_drvdata(pdev); @@ -320,15 +317,13 @@ static int __devexit r_tpu_remove(struct platform_device *pdev) r_tpu_set_pin(p, R_TPU_PIN_UNUSED, LED_OFF); pm_runtime_disable(&pdev->dev); - clk_put(p->clk); - iounmap(p->mapbase); return 0; } static struct platform_driver r_tpu_device_driver = { .probe = r_tpu_probe, - .remove = __devexit_p(r_tpu_remove), + .remove = r_tpu_remove, .driver = { .name = "leds-renesas-tpu", } diff --git a/drivers/leds/leds-ss4200.c b/drivers/leds/leds-ss4200.c index 57371e1485ab..64e204e714f6 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) }, @@ -263,7 +262,7 @@ static int nasgpio_led_set_blink(struct led_classdev *led_cdev, * already taken care of this, but we will do so in a non destructive manner * so that we have what we need whether the BIOS did it or not. */ -static int __devinit ich7_gpio_init(struct device *dev) +static int ich7_gpio_init(struct device *dev) { int i; u32 config_data = 0; @@ -342,7 +341,7 @@ static void ich7_lpc_cleanup(struct device *dev) * so we can retrive the required operational information and prepare the GPIO. */ static struct pci_dev *nas_gpio_pci_dev; -static int __devinit ich7_lpc_probe(struct pci_dev *dev, +static int ich7_lpc_probe(struct pci_dev *dev, const struct pci_device_id *id) { int status; @@ -459,7 +458,7 @@ static ssize_t nas_led_blink_store(struct device *dev, struct led_classdev *led = dev_get_drvdata(dev); unsigned long blink_state; - ret = strict_strtoul(buf, 10, &blink_state); + ret = kstrtoul(buf, 10, &blink_state); if (ret) return ret; diff --git a/drivers/leds/leds-sunfire.c b/drivers/leds/leds-sunfire.c index 134d9a4b34f1..89792990088d 100644 --- a/drivers/leds/leds-sunfire.c +++ b/drivers/leds/leds-sunfire.c @@ -3,6 +3,8 @@ * Copyright (C) 2008 David S. Miller <davem@davemloft.net> */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> @@ -14,9 +16,6 @@ #include <asm/fhc.h> #include <asm/upa.h> -#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"); @@ -123,21 +122,21 @@ struct sunfire_drvdata { struct sunfire_led leds[NUM_LEDS_PER_BOARD]; }; -static int __devinit sunfire_led_generic_probe(struct platform_device *pdev, +static int sunfire_led_generic_probe(struct platform_device *pdev, struct led_type *types) { struct sunfire_drvdata *p; 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 __devinit 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); @@ -165,7 +164,7 @@ static int __devinit sunfire_led_generic_probe(struct platform_device *pdev, return 0; } -static int __devexit sunfire_led_generic_remove(struct platform_device *pdev) +static int sunfire_led_generic_remove(struct platform_device *pdev) { struct sunfire_drvdata *p = dev_get_drvdata(&pdev->dev); int i; @@ -188,11 +187,11 @@ static struct led_type clockboard_led_types[NUM_LEDS_PER_BOARD] = { { .name = "clockboard-right", .handler = clockboard_right_set, - .default_trigger= "heartbeat", + .default_trigger = "heartbeat", }, }; -static int __devinit sunfire_clockboard_led_probe(struct platform_device *pdev) +static int sunfire_clockboard_led_probe(struct platform_device *pdev) { return sunfire_led_generic_probe(pdev, clockboard_led_types); } @@ -209,11 +208,11 @@ static struct led_type fhc_led_types[NUM_LEDS_PER_BOARD] = { { .name = "fhc-right", .handler = fhc_right_set, - .default_trigger= "heartbeat", + .default_trigger = "heartbeat", }, }; -static int __devinit sunfire_fhc_led_probe(struct platform_device *pdev) +static int sunfire_fhc_led_probe(struct platform_device *pdev) { return sunfire_led_generic_probe(pdev, fhc_led_types); } @@ -223,7 +222,7 @@ MODULE_ALIAS("platform:sunfire-fhc-leds"); static struct platform_driver sunfire_clockboard_led_driver = { .probe = sunfire_clockboard_led_probe, - .remove = __devexit_p(sunfire_led_generic_remove), + .remove = sunfire_led_generic_remove, .driver = { .name = "sunfire-clockboard-leds", .owner = THIS_MODULE, @@ -232,7 +231,7 @@ static struct platform_driver sunfire_clockboard_led_driver = { static struct platform_driver sunfire_fhc_led_driver = { .probe = sunfire_fhc_led_probe, - .remove = __devexit_p(sunfire_led_generic_remove), + .remove = sunfire_led_generic_remove, .driver = { .name = "sunfire-fhc-leds", .owner = THIS_MODULE, @@ -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); } diff --git a/drivers/leds/leds-tca6507.c b/drivers/leds/leds-tca6507.c index dabcf7ae8d0f..070ba0741b21 100644 --- a/drivers/leds/leds-tca6507.c +++ b/drivers/leds/leds-tca6507.c @@ -667,8 +667,68 @@ static void tca6507_remove_gpio(struct tca6507_chip *tca) } #endif /* CONFIG_GPIOLIB */ -static int __devinit tca6507_probe(struct i2c_client *client, - const struct i2c_device_id *id) +#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; + + count = of_get_child_count(np); + if (!count || 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) { struct tca6507_chip *tca; struct i2c_adapter *adapter; @@ -683,9 +743,12 @@ static int __devinit 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) @@ -730,7 +793,7 @@ exit: return err; } -static int __devexit tca6507_remove(struct i2c_client *client) +static int tca6507_remove(struct i2c_client *client) { int i; struct tca6507_chip *tca = i2c_get_clientdata(client); @@ -750,9 +813,10 @@ static struct i2c_driver tca6507_driver = { .driver = { .name = "leds-tca6507", .owner = THIS_MODULE, + .of_match_table = of_tca6507_leds_match, }, .probe = tca6507_probe, - .remove = __devexit_p(tca6507_remove), + .remove = tca6507_remove, .id_table = tca6507_id, }; diff --git a/drivers/leds/leds-wm831x-status.c b/drivers/leds/leds-wm831x-status.c index 74a24cf897c3..6bd5c679d877 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", diff --git a/drivers/leds/leds-wm8350.c b/drivers/leds/leds-wm8350.c index 88f23f845595..ed15157c8f6c 100644 --- a/drivers/leds/leds-wm8350.c +++ b/drivers/leds/leds-wm8350.c @@ -216,13 +216,13 @@ static int wm8350_led_probe(struct platform_device *pdev) isink = devm_regulator_get(&pdev->dev, "led_isink"); if (IS_ERR(isink)) { - printk(KERN_ERR "%s: can't get ISINK\n", __func__); + dev_err(&pdev->dev, "%s: can't get ISINK\n", __func__); return PTR_ERR(isink); } dcdc = devm_regulator_get(&pdev->dev, "led_vcc"); if (IS_ERR(dcdc)) { - printk(KERN_ERR "%s: can't get DCDC\n", __func__); + dev_err(&pdev->dev, "%s: can't get DCDC\n", __func__); return PTR_ERR(dcdc); } diff --git a/drivers/leds/leds-wrap.c b/drivers/leds/leds-wrap.c index 6e21e654bb02..b358cc05eff5 100644 --- a/drivers/leds/leds-wrap.c +++ b/drivers/leds/leds-wrap.c @@ -15,7 +15,7 @@ #include <linux/platform_device.h> #include <linux/leds.h> #include <linux/err.h> -#include <asm/io.h> +#include <linux/io.h> #include <linux/scx200_gpio.h> #include <linux/module.h> diff --git a/drivers/leds/ledtrig-backlight.c b/drivers/leds/ledtrig-backlight.c index b941685f2227..027a2b15d7d8 100644 --- a/drivers/leds/ledtrig-backlight.c +++ b/drivers/leds/ledtrig-backlight.c @@ -40,7 +40,7 @@ static int fb_notifier_callback(struct notifier_block *p, int new_status = *blank ? BLANK : UNBLANK; switch (event) { - case FB_EVENT_BLANK : + case FB_EVENT_BLANK: if (new_status == n->old_status) break; @@ -76,7 +76,7 @@ static ssize_t bl_trig_invert_store(struct device *dev, unsigned long invert; int ret; - ret = strict_strtoul(buf, 10, &invert); + ret = kstrtoul(buf, 10, &invert); if (ret < 0) return ret; diff --git a/drivers/leds/ledtrig-cpu.c b/drivers/leds/ledtrig-cpu.c index b312056da14d..4239b3955ff0 100644 --- a/drivers/leds/ledtrig-cpu.c +++ b/drivers/leds/ledtrig-cpu.c @@ -33,8 +33,6 @@ struct led_trigger_cpu { char name[MAX_NAME_LEN]; struct led_trigger *_trig; - struct mutex lock; - int lock_is_inited; }; static DEFINE_PER_CPU(struct led_trigger_cpu, cpu_trig); @@ -50,12 +48,6 @@ void ledtrig_cpu(enum cpu_led_event ledevt) { struct led_trigger_cpu *trig = &__get_cpu_var(cpu_trig); - /* mutex lock should be initialized before calling mutex_call() */ - if (!trig->lock_is_inited) - return; - - mutex_lock(&trig->lock); - /* Locate the correct CPU LED */ switch (ledevt) { case CPU_LED_IDLE_END: @@ -75,8 +67,6 @@ void ledtrig_cpu(enum cpu_led_event ledevt) /* Will leave the LED as it is */ break; } - - mutex_unlock(&trig->lock); } EXPORT_SYMBOL(ledtrig_cpu); @@ -117,14 +107,9 @@ static int __init ledtrig_cpu_init(void) for_each_possible_cpu(cpu) { struct led_trigger_cpu *trig = &per_cpu(cpu_trig, cpu); - mutex_init(&trig->lock); - snprintf(trig->name, MAX_NAME_LEN, "cpu%d", cpu); - mutex_lock(&trig->lock); led_trigger_register_simple(trig->name, &trig->_trig); - trig->lock_is_inited = 1; - mutex_unlock(&trig->lock); } register_syscore_ops(&ledtrig_cpu_syscore_ops); @@ -142,15 +127,9 @@ static void __exit ledtrig_cpu_exit(void) for_each_possible_cpu(cpu) { struct led_trigger_cpu *trig = &per_cpu(cpu_trig, cpu); - mutex_lock(&trig->lock); - led_trigger_unregister_simple(trig->_trig); trig->_trig = NULL; memset(trig->name, 0, MAX_NAME_LEN); - trig->lock_is_inited = 0; - - mutex_unlock(&trig->lock); - mutex_destroy(&trig->lock); } unregister_syscore_ops(&ledtrig_cpu_syscore_ops); diff --git a/drivers/leds/ledtrig-gpio.c b/drivers/leds/ledtrig-gpio.c index ba215dc42f98..72e3ebfc281f 100644 --- a/drivers/leds/ledtrig-gpio.c +++ b/drivers/leds/ledtrig-gpio.c @@ -110,7 +110,7 @@ static ssize_t gpio_trig_inverted_store(struct device *dev, unsigned long inverted; int ret; - ret = strict_strtoul(buf, 10, &inverted); + ret = kstrtoul(buf, 10, &inverted); if (ret < 0) return ret; |