103f613f0SLubomir Rintel // SPDX-License-Identifier: BSD-2-Clause OR GPL-2.0-or-later 203f613f0SLubomir Rintel /* 303f613f0SLubomir Rintel * Dell Wyse 3020 a.k.a. "Ariel" Embedded Controller LED Driver 403f613f0SLubomir Rintel * 503f613f0SLubomir Rintel * Copyright (C) 2020 Lubomir Rintel 603f613f0SLubomir Rintel */ 703f613f0SLubomir Rintel 803f613f0SLubomir Rintel #include <linux/module.h> 903f613f0SLubomir Rintel #include <linux/leds.h> 1003f613f0SLubomir Rintel #include <linux/regmap.h> 1103f613f0SLubomir Rintel #include <linux/of_platform.h> 1203f613f0SLubomir Rintel 1303f613f0SLubomir Rintel enum ec_index { 1403f613f0SLubomir Rintel EC_BLUE_LED = 0x01, 1503f613f0SLubomir Rintel EC_AMBER_LED = 0x02, 1603f613f0SLubomir Rintel EC_GREEN_LED = 0x03, 1703f613f0SLubomir Rintel }; 1803f613f0SLubomir Rintel 1903f613f0SLubomir Rintel enum { 2003f613f0SLubomir Rintel EC_LED_OFF = 0x00, 2103f613f0SLubomir Rintel EC_LED_STILL = 0x01, 2203f613f0SLubomir Rintel EC_LED_FADE = 0x02, 2303f613f0SLubomir Rintel EC_LED_BLINK = 0x03, 2403f613f0SLubomir Rintel }; 2503f613f0SLubomir Rintel 2603f613f0SLubomir Rintel struct ariel_led { 2703f613f0SLubomir Rintel struct regmap *ec_ram; 2803f613f0SLubomir Rintel enum ec_index ec_index; 2903f613f0SLubomir Rintel struct led_classdev led_cdev; 3003f613f0SLubomir Rintel }; 3103f613f0SLubomir Rintel 3203f613f0SLubomir Rintel #define led_cdev_to_ariel_led(c) container_of(c, struct ariel_led, led_cdev) 3303f613f0SLubomir Rintel 3403f613f0SLubomir Rintel static enum led_brightness ariel_led_get(struct led_classdev *led_cdev) 3503f613f0SLubomir Rintel { 3603f613f0SLubomir Rintel struct ariel_led *led = led_cdev_to_ariel_led(led_cdev); 3703f613f0SLubomir Rintel unsigned int led_status = 0; 3803f613f0SLubomir Rintel 3903f613f0SLubomir Rintel if (regmap_read(led->ec_ram, led->ec_index, &led_status)) 4003f613f0SLubomir Rintel return LED_OFF; 4103f613f0SLubomir Rintel 4203f613f0SLubomir Rintel if (led_status == EC_LED_STILL) 4303f613f0SLubomir Rintel return LED_FULL; 4403f613f0SLubomir Rintel else 4503f613f0SLubomir Rintel return LED_OFF; 4603f613f0SLubomir Rintel } 4703f613f0SLubomir Rintel 4803f613f0SLubomir Rintel static void ariel_led_set(struct led_classdev *led_cdev, 4903f613f0SLubomir Rintel enum led_brightness brightness) 5003f613f0SLubomir Rintel { 5103f613f0SLubomir Rintel struct ariel_led *led = led_cdev_to_ariel_led(led_cdev); 5203f613f0SLubomir Rintel 5303f613f0SLubomir Rintel if (brightness == LED_OFF) 5403f613f0SLubomir Rintel regmap_write(led->ec_ram, led->ec_index, EC_LED_OFF); 5503f613f0SLubomir Rintel else 5603f613f0SLubomir Rintel regmap_write(led->ec_ram, led->ec_index, EC_LED_STILL); 5703f613f0SLubomir Rintel } 5803f613f0SLubomir Rintel 5903f613f0SLubomir Rintel static int ariel_blink_set(struct led_classdev *led_cdev, 6003f613f0SLubomir Rintel unsigned long *delay_on, unsigned long *delay_off) 6103f613f0SLubomir Rintel { 6203f613f0SLubomir Rintel struct ariel_led *led = led_cdev_to_ariel_led(led_cdev); 6303f613f0SLubomir Rintel 6403f613f0SLubomir Rintel if (*delay_on == 0 && *delay_off == 0) 6503f613f0SLubomir Rintel return -EINVAL; 6603f613f0SLubomir Rintel 6703f613f0SLubomir Rintel if (*delay_on == 0) { 6803f613f0SLubomir Rintel regmap_write(led->ec_ram, led->ec_index, EC_LED_OFF); 6903f613f0SLubomir Rintel } else if (*delay_off == 0) { 7003f613f0SLubomir Rintel regmap_write(led->ec_ram, led->ec_index, EC_LED_STILL); 7103f613f0SLubomir Rintel } else { 7203f613f0SLubomir Rintel *delay_on = 500; 7303f613f0SLubomir Rintel *delay_off = 500; 7403f613f0SLubomir Rintel regmap_write(led->ec_ram, led->ec_index, EC_LED_BLINK); 7503f613f0SLubomir Rintel } 7603f613f0SLubomir Rintel 7703f613f0SLubomir Rintel return 0; 7803f613f0SLubomir Rintel } 7903f613f0SLubomir Rintel 8003f613f0SLubomir Rintel #define NLEDS 3 8103f613f0SLubomir Rintel 8203f613f0SLubomir Rintel static int ariel_led_probe(struct platform_device *pdev) 8303f613f0SLubomir Rintel { 8403f613f0SLubomir Rintel struct device *dev = &pdev->dev; 8503f613f0SLubomir Rintel struct ariel_led *leds; 8603f613f0SLubomir Rintel struct regmap *ec_ram; 8703f613f0SLubomir Rintel int ret; 8803f613f0SLubomir Rintel int i; 8903f613f0SLubomir Rintel 9003f613f0SLubomir Rintel ec_ram = dev_get_regmap(dev->parent, "ec_ram"); 9103f613f0SLubomir Rintel if (!ec_ram) 9203f613f0SLubomir Rintel return -ENODEV; 9303f613f0SLubomir Rintel 9403f613f0SLubomir Rintel leds = devm_kcalloc(dev, NLEDS, sizeof(*leds), GFP_KERNEL); 9503f613f0SLubomir Rintel if (!leds) 9603f613f0SLubomir Rintel return -ENOMEM; 9703f613f0SLubomir Rintel 9803f613f0SLubomir Rintel leds[0].ec_index = EC_BLUE_LED; 99*47854d2dSZheng Yongjun leds[0].led_cdev.name = "blue:power"; 10003f613f0SLubomir Rintel leds[0].led_cdev.default_trigger = "default-on"; 10103f613f0SLubomir Rintel 10203f613f0SLubomir Rintel leds[1].ec_index = EC_AMBER_LED; 103*47854d2dSZheng Yongjun leds[1].led_cdev.name = "amber:status"; 10403f613f0SLubomir Rintel 10503f613f0SLubomir Rintel leds[2].ec_index = EC_GREEN_LED; 106*47854d2dSZheng Yongjun leds[2].led_cdev.name = "green:status"; 10703f613f0SLubomir Rintel leds[2].led_cdev.default_trigger = "default-on"; 10803f613f0SLubomir Rintel 10903f613f0SLubomir Rintel for (i = 0; i < NLEDS; i++) { 11003f613f0SLubomir Rintel leds[i].ec_ram = ec_ram; 11103f613f0SLubomir Rintel leds[i].led_cdev.brightness_get = ariel_led_get; 11203f613f0SLubomir Rintel leds[i].led_cdev.brightness_set = ariel_led_set; 11303f613f0SLubomir Rintel leds[i].led_cdev.blink_set = ariel_blink_set; 11403f613f0SLubomir Rintel 11503f613f0SLubomir Rintel ret = devm_led_classdev_register(dev, &leds[i].led_cdev); 11603f613f0SLubomir Rintel if (ret) 11703f613f0SLubomir Rintel return ret; 11803f613f0SLubomir Rintel } 11903f613f0SLubomir Rintel 12003f613f0SLubomir Rintel return 0; 12103f613f0SLubomir Rintel } 12203f613f0SLubomir Rintel 12303f613f0SLubomir Rintel static struct platform_driver ariel_led_driver = { 12403f613f0SLubomir Rintel .probe = ariel_led_probe, 12503f613f0SLubomir Rintel .driver = { 12603f613f0SLubomir Rintel .name = "dell-wyse-ariel-led", 12703f613f0SLubomir Rintel }, 12803f613f0SLubomir Rintel }; 12903f613f0SLubomir Rintel module_platform_driver(ariel_led_driver); 13003f613f0SLubomir Rintel 13103f613f0SLubomir Rintel MODULE_AUTHOR("Lubomir Rintel <lkundrak@v3.sk>"); 13203f613f0SLubomir Rintel MODULE_DESCRIPTION("Dell Wyse 3020 Status LEDs Driver"); 13303f613f0SLubomir Rintel MODULE_LICENSE("Dual BSD/GPL"); 134