1 // SPDX-License-Identifier: BSD-2-Clause OR GPL-2.0-or-later 2 /* 3 * Dell Wyse 3020 a.k.a. "Ariel" Embedded Controller LED Driver 4 * 5 * Copyright (C) 2020 Lubomir Rintel 6 */ 7 8 #include <linux/module.h> 9 #include <linux/leds.h> 10 #include <linux/regmap.h> 11 #include <linux/of_platform.h> 12 13 enum ec_index { 14 EC_BLUE_LED = 0x01, 15 EC_AMBER_LED = 0x02, 16 EC_GREEN_LED = 0x03, 17 }; 18 19 enum { 20 EC_LED_OFF = 0x00, 21 EC_LED_STILL = 0x01, 22 EC_LED_FADE = 0x02, 23 EC_LED_BLINK = 0x03, 24 }; 25 26 struct ariel_led { 27 struct regmap *ec_ram; 28 enum ec_index ec_index; 29 struct led_classdev led_cdev; 30 }; 31 32 #define led_cdev_to_ariel_led(c) container_of(c, struct ariel_led, led_cdev) 33 34 static enum led_brightness ariel_led_get(struct led_classdev *led_cdev) 35 { 36 struct ariel_led *led = led_cdev_to_ariel_led(led_cdev); 37 unsigned int led_status = 0; 38 39 if (regmap_read(led->ec_ram, led->ec_index, &led_status)) 40 return LED_OFF; 41 42 if (led_status == EC_LED_STILL) 43 return LED_FULL; 44 else 45 return LED_OFF; 46 } 47 48 static void ariel_led_set(struct led_classdev *led_cdev, 49 enum led_brightness brightness) 50 { 51 struct ariel_led *led = led_cdev_to_ariel_led(led_cdev); 52 53 if (brightness == LED_OFF) 54 regmap_write(led->ec_ram, led->ec_index, EC_LED_OFF); 55 else 56 regmap_write(led->ec_ram, led->ec_index, EC_LED_STILL); 57 } 58 59 static int ariel_blink_set(struct led_classdev *led_cdev, 60 unsigned long *delay_on, unsigned long *delay_off) 61 { 62 struct ariel_led *led = led_cdev_to_ariel_led(led_cdev); 63 64 if (*delay_on == 0 && *delay_off == 0) 65 return -EINVAL; 66 67 if (*delay_on == 0) { 68 regmap_write(led->ec_ram, led->ec_index, EC_LED_OFF); 69 } else if (*delay_off == 0) { 70 regmap_write(led->ec_ram, led->ec_index, EC_LED_STILL); 71 } else { 72 *delay_on = 500; 73 *delay_off = 500; 74 regmap_write(led->ec_ram, led->ec_index, EC_LED_BLINK); 75 } 76 77 return 0; 78 } 79 80 #define NLEDS 3 81 82 static int ariel_led_probe(struct platform_device *pdev) 83 { 84 struct device *dev = &pdev->dev; 85 struct ariel_led *leds; 86 struct regmap *ec_ram; 87 int ret; 88 int i; 89 90 ec_ram = dev_get_regmap(dev->parent, "ec_ram"); 91 if (!ec_ram) 92 return -ENODEV; 93 94 leds = devm_kcalloc(dev, NLEDS, sizeof(*leds), GFP_KERNEL); 95 if (!leds) 96 return -ENOMEM; 97 98 leds[0].ec_index = EC_BLUE_LED; 99 leds[0].led_cdev.name = "blue:power"; 100 leds[0].led_cdev.default_trigger = "default-on"; 101 102 leds[1].ec_index = EC_AMBER_LED; 103 leds[1].led_cdev.name = "amber:status"; 104 105 leds[2].ec_index = EC_GREEN_LED; 106 leds[2].led_cdev.name = "green:status"; 107 leds[2].led_cdev.default_trigger = "default-on"; 108 109 for (i = 0; i < NLEDS; i++) { 110 leds[i].ec_ram = ec_ram; 111 leds[i].led_cdev.brightness_get = ariel_led_get; 112 leds[i].led_cdev.brightness_set = ariel_led_set; 113 leds[i].led_cdev.blink_set = ariel_blink_set; 114 115 ret = devm_led_classdev_register(dev, &leds[i].led_cdev); 116 if (ret) 117 return ret; 118 } 119 120 return 0; 121 } 122 123 static struct platform_driver ariel_led_driver = { 124 .probe = ariel_led_probe, 125 .driver = { 126 .name = "dell-wyse-ariel-led", 127 }, 128 }; 129 module_platform_driver(ariel_led_driver); 130 131 MODULE_AUTHOR("Lubomir Rintel <lkundrak@v3.sk>"); 132 MODULE_DESCRIPTION("Dell Wyse 3020 Status LEDs Driver"); 133 MODULE_LICENSE("Dual BSD/GPL"); 134