1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * LED Flash class interface 4 * 5 * Copyright (C) 2015 Samsung Electronics Co., Ltd. 6 * Author: Jacek Anaszewski <j.anaszewski@samsung.com> 7 */ 8 9 #include <linux/device.h> 10 #include <linux/init.h> 11 #include <linux/led-class-flash.h> 12 #include <linux/leds.h> 13 #include <linux/module.h> 14 #include <linux/slab.h> 15 16 #define has_flash_op(fled_cdev, op) \ 17 (fled_cdev && fled_cdev->ops->op) 18 19 #define call_flash_op(fled_cdev, op, args...) \ 20 ((has_flash_op(fled_cdev, op)) ? \ 21 (fled_cdev->ops->op(fled_cdev, args)) : \ 22 -EINVAL) 23 24 static const char * const led_flash_fault_names[] = { 25 "led-over-voltage", 26 "flash-timeout-exceeded", 27 "controller-over-temperature", 28 "controller-short-circuit", 29 "led-power-supply-over-current", 30 "indicator-led-fault", 31 "led-under-voltage", 32 "controller-under-voltage", 33 "led-over-temperature", 34 }; 35 36 static ssize_t flash_brightness_store(struct device *dev, 37 struct device_attribute *attr, const char *buf, size_t size) 38 { 39 struct led_classdev *led_cdev = dev_get_drvdata(dev); 40 struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); 41 unsigned long state; 42 ssize_t ret; 43 44 mutex_lock(&led_cdev->led_access); 45 46 if (led_sysfs_is_disabled(led_cdev)) { 47 ret = -EBUSY; 48 goto unlock; 49 } 50 51 ret = kstrtoul(buf, 10, &state); 52 if (ret) 53 goto unlock; 54 55 ret = led_set_flash_brightness(fled_cdev, state); 56 if (ret < 0) 57 goto unlock; 58 59 ret = size; 60 unlock: 61 mutex_unlock(&led_cdev->led_access); 62 return ret; 63 } 64 65 static ssize_t flash_brightness_show(struct device *dev, 66 struct device_attribute *attr, char *buf) 67 { 68 struct led_classdev *led_cdev = dev_get_drvdata(dev); 69 struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); 70 71 /* no lock needed for this */ 72 led_update_flash_brightness(fled_cdev); 73 74 return sprintf(buf, "%u\n", fled_cdev->brightness.val); 75 } 76 static DEVICE_ATTR_RW(flash_brightness); 77 78 static ssize_t max_flash_brightness_show(struct device *dev, 79 struct device_attribute *attr, char *buf) 80 { 81 struct led_classdev *led_cdev = dev_get_drvdata(dev); 82 struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); 83 84 return sprintf(buf, "%u\n", fled_cdev->brightness.max); 85 } 86 static DEVICE_ATTR_RO(max_flash_brightness); 87 88 static ssize_t flash_strobe_store(struct device *dev, 89 struct device_attribute *attr, const char *buf, size_t size) 90 { 91 struct led_classdev *led_cdev = dev_get_drvdata(dev); 92 struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); 93 unsigned long state; 94 ssize_t ret = -EBUSY; 95 96 mutex_lock(&led_cdev->led_access); 97 98 if (led_sysfs_is_disabled(led_cdev)) 99 goto unlock; 100 101 ret = kstrtoul(buf, 10, &state); 102 if (ret) 103 goto unlock; 104 105 if (state > 1) { 106 ret = -EINVAL; 107 goto unlock; 108 } 109 110 ret = led_set_flash_strobe(fled_cdev, state); 111 if (ret < 0) 112 goto unlock; 113 ret = size; 114 unlock: 115 mutex_unlock(&led_cdev->led_access); 116 return ret; 117 } 118 119 static ssize_t flash_strobe_show(struct device *dev, 120 struct device_attribute *attr, char *buf) 121 { 122 struct led_classdev *led_cdev = dev_get_drvdata(dev); 123 struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); 124 bool state; 125 int ret; 126 127 /* no lock needed for this */ 128 ret = led_get_flash_strobe(fled_cdev, &state); 129 if (ret < 0) 130 return ret; 131 132 return sprintf(buf, "%u\n", state); 133 } 134 static DEVICE_ATTR_RW(flash_strobe); 135 136 static ssize_t flash_timeout_store(struct device *dev, 137 struct device_attribute *attr, const char *buf, size_t size) 138 { 139 struct led_classdev *led_cdev = dev_get_drvdata(dev); 140 struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); 141 unsigned long flash_timeout; 142 ssize_t ret; 143 144 mutex_lock(&led_cdev->led_access); 145 146 if (led_sysfs_is_disabled(led_cdev)) { 147 ret = -EBUSY; 148 goto unlock; 149 } 150 151 ret = kstrtoul(buf, 10, &flash_timeout); 152 if (ret) 153 goto unlock; 154 155 ret = led_set_flash_timeout(fled_cdev, flash_timeout); 156 if (ret < 0) 157 goto unlock; 158 159 ret = size; 160 unlock: 161 mutex_unlock(&led_cdev->led_access); 162 return ret; 163 } 164 165 static ssize_t flash_timeout_show(struct device *dev, 166 struct device_attribute *attr, char *buf) 167 { 168 struct led_classdev *led_cdev = dev_get_drvdata(dev); 169 struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); 170 171 return sprintf(buf, "%u\n", fled_cdev->timeout.val); 172 } 173 static DEVICE_ATTR_RW(flash_timeout); 174 175 static ssize_t max_flash_timeout_show(struct device *dev, 176 struct device_attribute *attr, char *buf) 177 { 178 struct led_classdev *led_cdev = dev_get_drvdata(dev); 179 struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); 180 181 return sprintf(buf, "%u\n", fled_cdev->timeout.max); 182 } 183 static DEVICE_ATTR_RO(max_flash_timeout); 184 185 static ssize_t flash_fault_show(struct device *dev, 186 struct device_attribute *attr, char *buf) 187 { 188 struct led_classdev *led_cdev = dev_get_drvdata(dev); 189 struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); 190 u32 fault, mask = 0x1; 191 char *pbuf = buf; 192 int i, ret, buf_len; 193 194 ret = led_get_flash_fault(fled_cdev, &fault); 195 if (ret < 0) 196 return -EINVAL; 197 198 *buf = '\0'; 199 200 for (i = 0; i < LED_NUM_FLASH_FAULTS; ++i) { 201 if (fault & mask) { 202 buf_len = sprintf(pbuf, "%s ", 203 led_flash_fault_names[i]); 204 pbuf += buf_len; 205 } 206 mask <<= 1; 207 } 208 209 return strlen(strcat(buf, "\n")); 210 } 211 static DEVICE_ATTR_RO(flash_fault); 212 213 static struct attribute *led_flash_strobe_attrs[] = { 214 &dev_attr_flash_strobe.attr, 215 NULL, 216 }; 217 218 static struct attribute *led_flash_timeout_attrs[] = { 219 &dev_attr_flash_timeout.attr, 220 &dev_attr_max_flash_timeout.attr, 221 NULL, 222 }; 223 224 static struct attribute *led_flash_brightness_attrs[] = { 225 &dev_attr_flash_brightness.attr, 226 &dev_attr_max_flash_brightness.attr, 227 NULL, 228 }; 229 230 static struct attribute *led_flash_fault_attrs[] = { 231 &dev_attr_flash_fault.attr, 232 NULL, 233 }; 234 235 static const struct attribute_group led_flash_strobe_group = { 236 .attrs = led_flash_strobe_attrs, 237 }; 238 239 static const struct attribute_group led_flash_timeout_group = { 240 .attrs = led_flash_timeout_attrs, 241 }; 242 243 static const struct attribute_group led_flash_brightness_group = { 244 .attrs = led_flash_brightness_attrs, 245 }; 246 247 static const struct attribute_group led_flash_fault_group = { 248 .attrs = led_flash_fault_attrs, 249 }; 250 251 static void led_flash_resume(struct led_classdev *led_cdev) 252 { 253 struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); 254 255 call_flash_op(fled_cdev, flash_brightness_set, 256 fled_cdev->brightness.val); 257 call_flash_op(fled_cdev, timeout_set, fled_cdev->timeout.val); 258 } 259 260 static void led_flash_init_sysfs_groups(struct led_classdev_flash *fled_cdev) 261 { 262 struct led_classdev *led_cdev = &fled_cdev->led_cdev; 263 const struct led_flash_ops *ops = fled_cdev->ops; 264 const struct attribute_group **flash_groups = fled_cdev->sysfs_groups; 265 266 int num_sysfs_groups = 0; 267 268 flash_groups[num_sysfs_groups++] = &led_flash_strobe_group; 269 270 if (ops->flash_brightness_set) 271 flash_groups[num_sysfs_groups++] = &led_flash_brightness_group; 272 273 if (ops->timeout_set) 274 flash_groups[num_sysfs_groups++] = &led_flash_timeout_group; 275 276 if (ops->fault_get) 277 flash_groups[num_sysfs_groups++] = &led_flash_fault_group; 278 279 led_cdev->groups = flash_groups; 280 } 281 282 int led_classdev_flash_register_ext(struct device *parent, 283 struct led_classdev_flash *fled_cdev, 284 struct led_init_data *init_data) 285 { 286 struct led_classdev *led_cdev; 287 const struct led_flash_ops *ops; 288 int ret; 289 290 if (!fled_cdev) 291 return -EINVAL; 292 293 led_cdev = &fled_cdev->led_cdev; 294 295 if (led_cdev->flags & LED_DEV_CAP_FLASH) { 296 if (!led_cdev->brightness_set_blocking) 297 return -EINVAL; 298 299 ops = fled_cdev->ops; 300 if (!ops || !ops->strobe_set) 301 return -EINVAL; 302 303 led_cdev->flash_resume = led_flash_resume; 304 305 /* Select the sysfs attributes to be created for the device */ 306 led_flash_init_sysfs_groups(fled_cdev); 307 } 308 309 /* Register led class device */ 310 ret = led_classdev_register_ext(parent, led_cdev, init_data); 311 if (ret < 0) 312 return ret; 313 314 return 0; 315 } 316 EXPORT_SYMBOL_GPL(led_classdev_flash_register_ext); 317 318 void led_classdev_flash_unregister(struct led_classdev_flash *fled_cdev) 319 { 320 if (!fled_cdev) 321 return; 322 323 led_classdev_unregister(&fled_cdev->led_cdev); 324 } 325 EXPORT_SYMBOL_GPL(led_classdev_flash_unregister); 326 327 static void devm_led_classdev_flash_release(struct device *dev, void *res) 328 { 329 led_classdev_flash_unregister(*(struct led_classdev_flash **)res); 330 } 331 332 int devm_led_classdev_flash_register_ext(struct device *parent, 333 struct led_classdev_flash *fled_cdev, 334 struct led_init_data *init_data) 335 { 336 struct led_classdev_flash **dr; 337 int ret; 338 339 dr = devres_alloc(devm_led_classdev_flash_release, sizeof(*dr), 340 GFP_KERNEL); 341 if (!dr) 342 return -ENOMEM; 343 344 ret = led_classdev_flash_register_ext(parent, fled_cdev, init_data); 345 if (ret) { 346 devres_free(dr); 347 return ret; 348 } 349 350 *dr = fled_cdev; 351 devres_add(parent, dr); 352 353 return 0; 354 } 355 EXPORT_SYMBOL_GPL(devm_led_classdev_flash_register_ext); 356 357 static int devm_led_classdev_flash_match(struct device *dev, 358 void *res, void *data) 359 { 360 struct led_classdev_flash **p = res; 361 362 if (WARN_ON(!p || !*p)) 363 return 0; 364 365 return *p == data; 366 } 367 368 void devm_led_classdev_flash_unregister(struct device *dev, 369 struct led_classdev_flash *fled_cdev) 370 { 371 WARN_ON(devres_release(dev, 372 devm_led_classdev_flash_release, 373 devm_led_classdev_flash_match, fled_cdev)); 374 } 375 EXPORT_SYMBOL_GPL(devm_led_classdev_flash_unregister); 376 377 static void led_clamp_align(struct led_flash_setting *s) 378 { 379 u32 v, offset; 380 381 v = s->val + s->step / 2; 382 v = clamp(v, s->min, s->max); 383 offset = v - s->min; 384 offset = s->step * (offset / s->step); 385 s->val = s->min + offset; 386 } 387 388 int led_set_flash_timeout(struct led_classdev_flash *fled_cdev, u32 timeout) 389 { 390 struct led_classdev *led_cdev = &fled_cdev->led_cdev; 391 struct led_flash_setting *s = &fled_cdev->timeout; 392 393 s->val = timeout; 394 led_clamp_align(s); 395 396 if (!(led_cdev->flags & LED_SUSPENDED)) 397 return call_flash_op(fled_cdev, timeout_set, s->val); 398 399 return 0; 400 } 401 EXPORT_SYMBOL_GPL(led_set_flash_timeout); 402 403 int led_get_flash_fault(struct led_classdev_flash *fled_cdev, u32 *fault) 404 { 405 return call_flash_op(fled_cdev, fault_get, fault); 406 } 407 EXPORT_SYMBOL_GPL(led_get_flash_fault); 408 409 int led_set_flash_brightness(struct led_classdev_flash *fled_cdev, 410 u32 brightness) 411 { 412 struct led_classdev *led_cdev = &fled_cdev->led_cdev; 413 struct led_flash_setting *s = &fled_cdev->brightness; 414 415 s->val = brightness; 416 led_clamp_align(s); 417 418 if (!(led_cdev->flags & LED_SUSPENDED)) 419 return call_flash_op(fled_cdev, flash_brightness_set, s->val); 420 421 return 0; 422 } 423 EXPORT_SYMBOL_GPL(led_set_flash_brightness); 424 425 int led_update_flash_brightness(struct led_classdev_flash *fled_cdev) 426 { 427 struct led_flash_setting *s = &fled_cdev->brightness; 428 u32 brightness; 429 430 if (has_flash_op(fled_cdev, flash_brightness_get)) { 431 int ret = call_flash_op(fled_cdev, flash_brightness_get, 432 &brightness); 433 if (ret < 0) 434 return ret; 435 436 s->val = brightness; 437 } 438 439 return 0; 440 } 441 EXPORT_SYMBOL_GPL(led_update_flash_brightness); 442 443 MODULE_AUTHOR("Jacek Anaszewski <j.anaszewski@samsung.com>"); 444 MODULE_DESCRIPTION("LED Flash class interface"); 445 MODULE_LICENSE("GPL v2"); 446