Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v4.9-rc4 272 lines 5.3 kB view raw
1/* 2 * LED driver for WM8350 driven LEDS. 3 * 4 * Copyright(C) 2007, 2008 Wolfson Microelectronics PLC. 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 2 as 8 * published by the Free Software Foundation. 9 * 10 */ 11 12#include <linux/kernel.h> 13#include <linux/platform_device.h> 14#include <linux/leds.h> 15#include <linux/err.h> 16#include <linux/mfd/wm8350/pmic.h> 17#include <linux/regulator/consumer.h> 18#include <linux/slab.h> 19#include <linux/module.h> 20 21/* Microamps */ 22static const int isink_cur[] = { 23 4, 24 5, 25 6, 26 7, 27 8, 28 10, 29 11, 30 14, 31 16, 32 19, 33 23, 34 27, 35 32, 36 39, 37 46, 38 54, 39 65, 40 77, 41 92, 42 109, 43 130, 44 154, 45 183, 46 218, 47 259, 48 308, 49 367, 50 436, 51 518, 52 616, 53 733, 54 872, 55 1037, 56 1233, 57 1466, 58 1744, 59 2073, 60 2466, 61 2933, 62 3487, 63 4147, 64 4932, 65 5865, 66 6975, 67 8294, 68 9864, 69 11730, 70 13949, 71 16589, 72 19728, 73 23460, 74 27899, 75 33178, 76 39455, 77 46920, 78 55798, 79 66355, 80 78910, 81 93840, 82 111596, 83 132710, 84 157820, 85 187681, 86 223191 87}; 88 89#define to_wm8350_led(led_cdev) \ 90 container_of(led_cdev, struct wm8350_led, cdev) 91 92static int wm8350_led_enable(struct wm8350_led *led) 93{ 94 int ret = 0; 95 96 if (led->enabled) 97 return ret; 98 99 ret = regulator_enable(led->isink); 100 if (ret != 0) { 101 dev_err(led->cdev.dev, "Failed to enable ISINK: %d\n", ret); 102 return ret; 103 } 104 105 ret = regulator_enable(led->dcdc); 106 if (ret != 0) { 107 dev_err(led->cdev.dev, "Failed to enable DCDC: %d\n", ret); 108 regulator_disable(led->isink); 109 return ret; 110 } 111 112 led->enabled = 1; 113 114 return ret; 115} 116 117static int wm8350_led_disable(struct wm8350_led *led) 118{ 119 int ret = 0; 120 121 if (!led->enabled) 122 return ret; 123 124 ret = regulator_disable(led->dcdc); 125 if (ret != 0) { 126 dev_err(led->cdev.dev, "Failed to disable DCDC: %d\n", ret); 127 return ret; 128 } 129 130 ret = regulator_disable(led->isink); 131 if (ret != 0) { 132 dev_err(led->cdev.dev, "Failed to disable ISINK: %d\n", ret); 133 ret = regulator_enable(led->dcdc); 134 if (ret != 0) 135 dev_err(led->cdev.dev, "Failed to reenable DCDC: %d\n", 136 ret); 137 return ret; 138 } 139 140 led->enabled = 0; 141 142 return ret; 143} 144 145static int wm8350_led_set(struct led_classdev *led_cdev, 146 enum led_brightness value) 147{ 148 struct wm8350_led *led = to_wm8350_led(led_cdev); 149 unsigned long flags; 150 int ret; 151 int uA; 152 153 led->value = value; 154 155 spin_lock_irqsave(&led->value_lock, flags); 156 157 if (led->value == LED_OFF) { 158 spin_unlock_irqrestore(&led->value_lock, flags); 159 return wm8350_led_disable(led); 160 } 161 162 /* This scales linearly into the index of valid current 163 * settings which results in a linear scaling of perceived 164 * brightness due to the non-linear current settings provided 165 * by the hardware. 166 */ 167 uA = (led->max_uA_index * led->value) / LED_FULL; 168 spin_unlock_irqrestore(&led->value_lock, flags); 169 BUG_ON(uA >= ARRAY_SIZE(isink_cur)); 170 171 ret = regulator_set_current_limit(led->isink, isink_cur[uA], 172 isink_cur[uA]); 173 if (ret != 0) { 174 dev_err(led->cdev.dev, "Failed to set %duA: %d\n", 175 isink_cur[uA], ret); 176 return ret; 177 } 178 179 return wm8350_led_enable(led); 180} 181 182static void wm8350_led_shutdown(struct platform_device *pdev) 183{ 184 struct wm8350_led *led = platform_get_drvdata(pdev); 185 186 led->value = LED_OFF; 187 wm8350_led_disable(led); 188} 189 190static int wm8350_led_probe(struct platform_device *pdev) 191{ 192 struct regulator *isink, *dcdc; 193 struct wm8350_led *led; 194 struct wm8350_led_platform_data *pdata = dev_get_platdata(&pdev->dev); 195 int i; 196 197 if (pdata == NULL) { 198 dev_err(&pdev->dev, "no platform data\n"); 199 return -ENODEV; 200 } 201 202 if (pdata->max_uA < isink_cur[0]) { 203 dev_err(&pdev->dev, "Invalid maximum current %duA\n", 204 pdata->max_uA); 205 return -EINVAL; 206 } 207 208 isink = devm_regulator_get(&pdev->dev, "led_isink"); 209 if (IS_ERR(isink)) { 210 dev_err(&pdev->dev, "%s: can't get ISINK\n", __func__); 211 return PTR_ERR(isink); 212 } 213 214 dcdc = devm_regulator_get(&pdev->dev, "led_vcc"); 215 if (IS_ERR(dcdc)) { 216 dev_err(&pdev->dev, "%s: can't get DCDC\n", __func__); 217 return PTR_ERR(dcdc); 218 } 219 220 led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL); 221 if (led == NULL) 222 return -ENOMEM; 223 224 led->cdev.brightness_set_blocking = wm8350_led_set; 225 led->cdev.default_trigger = pdata->default_trigger; 226 led->cdev.name = pdata->name; 227 led->cdev.flags |= LED_CORE_SUSPENDRESUME; 228 led->enabled = regulator_is_enabled(isink); 229 led->isink = isink; 230 led->dcdc = dcdc; 231 232 for (i = 0; i < ARRAY_SIZE(isink_cur) - 1; i++) 233 if (isink_cur[i] >= pdata->max_uA) 234 break; 235 led->max_uA_index = i; 236 if (pdata->max_uA != isink_cur[i]) 237 dev_warn(&pdev->dev, 238 "Maximum current %duA is not directly supported," 239 " check platform data\n", 240 pdata->max_uA); 241 242 spin_lock_init(&led->value_lock); 243 led->value = LED_OFF; 244 platform_set_drvdata(pdev, led); 245 246 return led_classdev_register(&pdev->dev, &led->cdev); 247} 248 249static int wm8350_led_remove(struct platform_device *pdev) 250{ 251 struct wm8350_led *led = platform_get_drvdata(pdev); 252 253 led_classdev_unregister(&led->cdev); 254 wm8350_led_disable(led); 255 return 0; 256} 257 258static struct platform_driver wm8350_led_driver = { 259 .driver = { 260 .name = "wm8350-led", 261 }, 262 .probe = wm8350_led_probe, 263 .remove = wm8350_led_remove, 264 .shutdown = wm8350_led_shutdown, 265}; 266 267module_platform_driver(wm8350_led_driver); 268 269MODULE_AUTHOR("Mark Brown"); 270MODULE_DESCRIPTION("WM8350 LED driver"); 271MODULE_LICENSE("GPL"); 272MODULE_ALIAS("platform:wm8350-led");