Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * LED driver for WM8350 driven LEDS.
0004  *
0005  * Copyright(C) 2007, 2008 Wolfson Microelectronics PLC.
0006  */
0007 
0008 #include <linux/kernel.h>
0009 #include <linux/platform_device.h>
0010 #include <linux/leds.h>
0011 #include <linux/err.h>
0012 #include <linux/mfd/wm8350/pmic.h>
0013 #include <linux/regulator/consumer.h>
0014 #include <linux/slab.h>
0015 #include <linux/module.h>
0016 
0017 /* Microamps */
0018 static const int isink_cur[] = {
0019     4,
0020     5,
0021     6,
0022     7,
0023     8,
0024     10,
0025     11,
0026     14,
0027     16,
0028     19,
0029     23,
0030     27,
0031     32,
0032     39,
0033     46,
0034     54,
0035     65,
0036     77,
0037     92,
0038     109,
0039     130,
0040     154,
0041     183,
0042     218,
0043     259,
0044     308,
0045     367,
0046     436,
0047     518,
0048     616,
0049     733,
0050     872,
0051     1037,
0052     1233,
0053     1466,
0054     1744,
0055     2073,
0056     2466,
0057     2933,
0058     3487,
0059     4147,
0060     4932,
0061     5865,
0062     6975,
0063     8294,
0064     9864,
0065     11730,
0066     13949,
0067     16589,
0068     19728,
0069     23460,
0070     27899,
0071     33178,
0072     39455,
0073     46920,
0074     55798,
0075     66355,
0076     78910,
0077     93840,
0078     111596,
0079     132710,
0080     157820,
0081     187681,
0082     223191
0083 };
0084 
0085 #define to_wm8350_led(led_cdev) \
0086     container_of(led_cdev, struct wm8350_led, cdev)
0087 
0088 static int wm8350_led_enable(struct wm8350_led *led)
0089 {
0090     int ret = 0;
0091 
0092     if (led->enabled)
0093         return ret;
0094 
0095     ret = regulator_enable(led->isink);
0096     if (ret != 0) {
0097         dev_err(led->cdev.dev, "Failed to enable ISINK: %d\n", ret);
0098         return ret;
0099     }
0100 
0101     ret = regulator_enable(led->dcdc);
0102     if (ret != 0) {
0103         dev_err(led->cdev.dev, "Failed to enable DCDC: %d\n", ret);
0104         regulator_disable(led->isink);
0105         return ret;
0106     }
0107 
0108     led->enabled = 1;
0109 
0110     return ret;
0111 }
0112 
0113 static int wm8350_led_disable(struct wm8350_led *led)
0114 {
0115     int ret = 0;
0116 
0117     if (!led->enabled)
0118         return ret;
0119 
0120     ret = regulator_disable(led->dcdc);
0121     if (ret != 0) {
0122         dev_err(led->cdev.dev, "Failed to disable DCDC: %d\n", ret);
0123         return ret;
0124     }
0125 
0126     ret = regulator_disable(led->isink);
0127     if (ret != 0) {
0128         dev_err(led->cdev.dev, "Failed to disable ISINK: %d\n", ret);
0129         ret = regulator_enable(led->dcdc);
0130         if (ret != 0)
0131             dev_err(led->cdev.dev, "Failed to reenable DCDC: %d\n",
0132                 ret);
0133         return ret;
0134     }
0135 
0136     led->enabled = 0;
0137 
0138     return ret;
0139 }
0140 
0141 static int wm8350_led_set(struct led_classdev *led_cdev,
0142                enum led_brightness value)
0143 {
0144     struct wm8350_led *led = to_wm8350_led(led_cdev);
0145     unsigned long flags;
0146     int ret;
0147     int uA;
0148 
0149     led->value = value;
0150 
0151     spin_lock_irqsave(&led->value_lock, flags);
0152 
0153     if (led->value == LED_OFF) {
0154         spin_unlock_irqrestore(&led->value_lock, flags);
0155         return wm8350_led_disable(led);
0156     }
0157 
0158     /* This scales linearly into the index of valid current
0159      * settings which results in a linear scaling of perceived
0160      * brightness due to the non-linear current settings provided
0161      * by the hardware.
0162      */
0163     uA = (led->max_uA_index * led->value) / LED_FULL;
0164     spin_unlock_irqrestore(&led->value_lock, flags);
0165     BUG_ON(uA >= ARRAY_SIZE(isink_cur));
0166 
0167     ret = regulator_set_current_limit(led->isink, isink_cur[uA],
0168                       isink_cur[uA]);
0169     if (ret != 0) {
0170         dev_err(led->cdev.dev, "Failed to set %duA: %d\n",
0171             isink_cur[uA], ret);
0172         return ret;
0173     }
0174 
0175     return wm8350_led_enable(led);
0176 }
0177 
0178 static void wm8350_led_shutdown(struct platform_device *pdev)
0179 {
0180     struct wm8350_led *led = platform_get_drvdata(pdev);
0181 
0182     led->value = LED_OFF;
0183     wm8350_led_disable(led);
0184 }
0185 
0186 static int wm8350_led_probe(struct platform_device *pdev)
0187 {
0188     struct regulator *isink, *dcdc;
0189     struct wm8350_led *led;
0190     struct wm8350_led_platform_data *pdata = dev_get_platdata(&pdev->dev);
0191     int i;
0192 
0193     if (pdata == NULL) {
0194         dev_err(&pdev->dev, "no platform data\n");
0195         return -ENODEV;
0196     }
0197 
0198     if (pdata->max_uA < isink_cur[0]) {
0199         dev_err(&pdev->dev, "Invalid maximum current %duA\n",
0200             pdata->max_uA);
0201         return -EINVAL;
0202     }
0203 
0204     isink = devm_regulator_get(&pdev->dev, "led_isink");
0205     if (IS_ERR(isink)) {
0206         dev_err(&pdev->dev, "%s: can't get ISINK\n", __func__);
0207         return PTR_ERR(isink);
0208     }
0209 
0210     dcdc = devm_regulator_get(&pdev->dev, "led_vcc");
0211     if (IS_ERR(dcdc)) {
0212         dev_err(&pdev->dev, "%s: can't get DCDC\n", __func__);
0213         return PTR_ERR(dcdc);
0214     }
0215 
0216     led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL);
0217     if (led == NULL)
0218         return -ENOMEM;
0219 
0220     led->cdev.brightness_set_blocking = wm8350_led_set;
0221     led->cdev.default_trigger = pdata->default_trigger;
0222     led->cdev.name = pdata->name;
0223     led->cdev.flags |= LED_CORE_SUSPENDRESUME;
0224     led->enabled = regulator_is_enabled(isink);
0225     led->isink = isink;
0226     led->dcdc = dcdc;
0227 
0228     for (i = 0; i < ARRAY_SIZE(isink_cur) - 1; i++)
0229         if (isink_cur[i] >= pdata->max_uA)
0230             break;
0231     led->max_uA_index = i;
0232     if (pdata->max_uA != isink_cur[i])
0233         dev_warn(&pdev->dev,
0234              "Maximum current %duA is not directly supported,"
0235              " check platform data\n",
0236              pdata->max_uA);
0237 
0238     spin_lock_init(&led->value_lock);
0239     led->value = LED_OFF;
0240     platform_set_drvdata(pdev, led);
0241 
0242     return led_classdev_register(&pdev->dev, &led->cdev);
0243 }
0244 
0245 static int wm8350_led_remove(struct platform_device *pdev)
0246 {
0247     struct wm8350_led *led = platform_get_drvdata(pdev);
0248 
0249     led_classdev_unregister(&led->cdev);
0250     wm8350_led_disable(led);
0251     return 0;
0252 }
0253 
0254 static struct platform_driver wm8350_led_driver = {
0255     .driver = {
0256            .name = "wm8350-led",
0257            },
0258     .probe = wm8350_led_probe,
0259     .remove = wm8350_led_remove,
0260     .shutdown = wm8350_led_shutdown,
0261 };
0262 
0263 module_platform_driver(wm8350_led_driver);
0264 
0265 MODULE_AUTHOR("Mark Brown");
0266 MODULE_DESCRIPTION("WM8350 LED driver");
0267 MODULE_LICENSE("GPL");
0268 MODULE_ALIAS("platform:wm8350-led");