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