Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * LEDs driver for Analog Devices ADP5520/ADP5501 MFD PMICs
0004  *
0005  * Copyright 2009 Analog Devices Inc.
0006  *
0007  * Loosely derived from leds-da903x:
0008  * Copyright (C) 2008 Compulab, Ltd.
0009  *  Mike Rapoport <mike@compulab.co.il>
0010  *
0011  * Copyright (C) 2006-2008 Marvell International Ltd.
0012  *  Eric Miao <eric.miao@marvell.com>
0013  */
0014 
0015 #include <linux/module.h>
0016 #include <linux/kernel.h>
0017 #include <linux/platform_device.h>
0018 #include <linux/leds.h>
0019 #include <linux/mfd/adp5520.h>
0020 #include <linux/slab.h>
0021 
0022 struct adp5520_led {
0023     struct led_classdev cdev;
0024     struct device       *master;
0025     int         id;
0026     int         flags;
0027 };
0028 
0029 static int adp5520_led_set(struct led_classdev *led_cdev,
0030                enum led_brightness value)
0031 {
0032     struct adp5520_led *led;
0033 
0034     led = container_of(led_cdev, struct adp5520_led, cdev);
0035     return adp5520_write(led->master, ADP5520_LED1_CURRENT + led->id - 1,
0036              value >> 2);
0037 }
0038 
0039 static int adp5520_led_setup(struct adp5520_led *led)
0040 {
0041     struct device *dev = led->master;
0042     int flags = led->flags;
0043     int ret = 0;
0044 
0045     switch (led->id) {
0046     case FLAG_ID_ADP5520_LED1_ADP5501_LED0:
0047         ret |= adp5520_set_bits(dev, ADP5520_LED_TIME,
0048                     (flags >> ADP5520_FLAG_OFFT_SHIFT) &
0049                     ADP5520_FLAG_OFFT_MASK);
0050         ret |= adp5520_set_bits(dev, ADP5520_LED_CONTROL,
0051                     ADP5520_LED1_EN);
0052         break;
0053     case FLAG_ID_ADP5520_LED2_ADP5501_LED1:
0054         ret |= adp5520_set_bits(dev,  ADP5520_LED_TIME,
0055                     ((flags >> ADP5520_FLAG_OFFT_SHIFT) &
0056                     ADP5520_FLAG_OFFT_MASK) << 2);
0057         ret |= adp5520_clr_bits(dev, ADP5520_LED_CONTROL,
0058                      ADP5520_R3_MODE);
0059         ret |= adp5520_set_bits(dev, ADP5520_LED_CONTROL,
0060                     ADP5520_LED2_EN);
0061         break;
0062     case FLAG_ID_ADP5520_LED3_ADP5501_LED2:
0063         ret |= adp5520_set_bits(dev,  ADP5520_LED_TIME,
0064                     ((flags >> ADP5520_FLAG_OFFT_SHIFT) &
0065                     ADP5520_FLAG_OFFT_MASK) << 4);
0066         ret |= adp5520_clr_bits(dev, ADP5520_LED_CONTROL,
0067                     ADP5520_C3_MODE);
0068         ret |= adp5520_set_bits(dev, ADP5520_LED_CONTROL,
0069                     ADP5520_LED3_EN);
0070         break;
0071     }
0072 
0073     return ret;
0074 }
0075 
0076 static int adp5520_led_prepare(struct platform_device *pdev)
0077 {
0078     struct adp5520_leds_platform_data *pdata = dev_get_platdata(&pdev->dev);
0079     struct device *dev = pdev->dev.parent;
0080     int ret = 0;
0081 
0082     ret |= adp5520_write(dev, ADP5520_LED1_CURRENT, 0);
0083     ret |= adp5520_write(dev, ADP5520_LED2_CURRENT, 0);
0084     ret |= adp5520_write(dev, ADP5520_LED3_CURRENT, 0);
0085     ret |= adp5520_write(dev, ADP5520_LED_TIME, pdata->led_on_time << 6);
0086     ret |= adp5520_write(dev, ADP5520_LED_FADE, FADE_VAL(pdata->fade_in,
0087          pdata->fade_out));
0088 
0089     return ret;
0090 }
0091 
0092 static int adp5520_led_probe(struct platform_device *pdev)
0093 {
0094     struct adp5520_leds_platform_data *pdata = dev_get_platdata(&pdev->dev);
0095     struct adp5520_led *led, *led_dat;
0096     struct led_info *cur_led;
0097     int ret, i;
0098 
0099     if (pdata == NULL) {
0100         dev_err(&pdev->dev, "missing platform data\n");
0101         return -ENODEV;
0102     }
0103 
0104     if (pdata->num_leds > ADP5520_01_MAXLEDS) {
0105         dev_err(&pdev->dev, "can't handle more than %d LEDS\n",
0106                  ADP5520_01_MAXLEDS);
0107         return -EFAULT;
0108     }
0109 
0110     led = devm_kcalloc(&pdev->dev, pdata->num_leds, sizeof(*led),
0111                 GFP_KERNEL);
0112     if (!led)
0113         return -ENOMEM;
0114 
0115     ret = adp5520_led_prepare(pdev);
0116     if (ret) {
0117         dev_err(&pdev->dev, "failed to write\n");
0118         return ret;
0119     }
0120 
0121     for (i = 0; i < pdata->num_leds; ++i) {
0122         cur_led = &pdata->leds[i];
0123         led_dat = &led[i];
0124 
0125         led_dat->cdev.name = cur_led->name;
0126         led_dat->cdev.default_trigger = cur_led->default_trigger;
0127         led_dat->cdev.brightness_set_blocking = adp5520_led_set;
0128         led_dat->cdev.brightness = LED_OFF;
0129 
0130         if (cur_led->flags & ADP5520_FLAG_LED_MASK)
0131             led_dat->flags = cur_led->flags;
0132         else
0133             led_dat->flags = i + 1;
0134 
0135         led_dat->id = led_dat->flags & ADP5520_FLAG_LED_MASK;
0136 
0137         led_dat->master = pdev->dev.parent;
0138 
0139         ret = led_classdev_register(led_dat->master, &led_dat->cdev);
0140         if (ret) {
0141             dev_err(&pdev->dev, "failed to register LED %d\n",
0142                 led_dat->id);
0143             goto err;
0144         }
0145 
0146         ret = adp5520_led_setup(led_dat);
0147         if (ret) {
0148             dev_err(&pdev->dev, "failed to write\n");
0149             i++;
0150             goto err;
0151         }
0152     }
0153 
0154     platform_set_drvdata(pdev, led);
0155     return 0;
0156 
0157 err:
0158     if (i > 0) {
0159         for (i = i - 1; i >= 0; i--)
0160             led_classdev_unregister(&led[i].cdev);
0161     }
0162 
0163     return ret;
0164 }
0165 
0166 static int adp5520_led_remove(struct platform_device *pdev)
0167 {
0168     struct adp5520_leds_platform_data *pdata = dev_get_platdata(&pdev->dev);
0169     struct adp5520_led *led;
0170     int i;
0171 
0172     led = platform_get_drvdata(pdev);
0173 
0174     adp5520_clr_bits(led->master, ADP5520_LED_CONTROL,
0175          ADP5520_LED1_EN | ADP5520_LED2_EN | ADP5520_LED3_EN);
0176 
0177     for (i = 0; i < pdata->num_leds; i++) {
0178         led_classdev_unregister(&led[i].cdev);
0179     }
0180 
0181     return 0;
0182 }
0183 
0184 static struct platform_driver adp5520_led_driver = {
0185     .driver = {
0186         .name   = "adp5520-led",
0187     },
0188     .probe      = adp5520_led_probe,
0189     .remove     = adp5520_led_remove,
0190 };
0191 
0192 module_platform_driver(adp5520_led_driver);
0193 
0194 MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
0195 MODULE_DESCRIPTION("LEDS ADP5520(01) Driver");
0196 MODULE_LICENSE("GPL");
0197 MODULE_ALIAS("platform:adp5520-led");