From 865f7cc8cdb26f1572d36058a5a00e1a6e253389 Mon Sep 17 00:00:00 2001 From: Seung-Woo Kim Date: Thu, 11 Jun 2020 11:39:01 +0900 Subject: [PATCH] leds: add support for MAX77843 led control driver Maxim max77843 has 4-channel led controller on max77843 multi function device. Add max77843 led driver for the device. Note: this ports below commits in 4.1 kernel in tizen_5.0 branch: commit dd11bc28be60 ("mfd: max77843: Add led of_compatible in mfd_cell") commit ab013630b285 ("leds: add support for MAX77843 led control driver") Change-Id: I48c778addba06d1070284e4fc6b8e3ca500c089a Signed-off-by: Jaewon Kim Signed-off-by: Seung-Woo Kim --- drivers/leds/Kconfig | 7 + drivers/leds/Makefile | 1 + drivers/leds/leds-max77843.c | 254 +++++++++++++++++++++++++++++++++++ drivers/mfd/max77843.c | 3 + include/linux/mfd/max77843-private.h | 33 +++++ 5 files changed, 298 insertions(+) create mode 100644 drivers/leds/leds-max77843.c diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 52ea34e..2e08f83 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -569,6 +569,13 @@ config LEDS_MAX77693 multifunction device. It has build in control for two leds in flash and torch mode. +config LEDS_MAX77843 + tristate "LED support for MAX77843 PMIC" + depends on LEDS_CLASS && MFD_MAX77843 + help + This option enables support for on-chip 4-channel LED driver + on MAXIM MAX77843 PMIC. + config LEDS_MAX8997 tristate "LED support for MAX8997 PMIC" depends on LEDS_CLASS && MFD_MAX8997 diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 3598045..a13ef06 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -60,6 +60,7 @@ obj-$(CONFIG_LEDS_NS2) += leds-ns2.o obj-$(CONFIG_LEDS_NETXBIG) += leds-netxbig.o obj-$(CONFIG_LEDS_ASIC3) += leds-asic3.o obj-$(CONFIG_LEDS_MAX77693) += leds-max77693.o +obj-$(CONFIG_LEDS_MAX77843) += leds-max77843.o obj-$(CONFIG_LEDS_MAX8997) += leds-max8997.o obj-$(CONFIG_LEDS_LM355x) += leds-lm355x.o obj-$(CONFIG_LEDS_BLINKM) += leds-blinkm.o diff --git a/drivers/leds/leds-max77843.c b/drivers/leds/leds-max77843.c new file mode 100644 index 0000000..4637cd4 --- /dev/null +++ b/drivers/leds/leds-max77843.c @@ -0,0 +1,254 @@ +/* + * max77843.c - LED class driver for Maxim MAX77843 + * + * Copyright (C) 2015 Samsung Electronics + * Author: Jaewon Kim + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX77843_MAX_BRIGHTNESS 0xFF + +enum max77843_leds { + MAX77843_LED0 = 0, + MAX77843_LED1, + MAX77843_LED2, + MAX77843_LED3, + + MAX77843_LED_NUM, +}; + +struct max77843_led_info { + struct led_classdev cdev; + const char *color; + u8 channel; + bool active; +}; + +struct max77843_led { + struct max77693_dev *max77843; + struct regmap *regmap_led; + struct device *dev; + struct max77843_led_info led_info[4]; +}; + +static struct max77843_led *cdev_to_led(struct led_classdev *cdev) +{ + struct max77843_led_info *led_info = container_of(cdev, + struct max77843_led_info, cdev); + + return container_of(led_info, struct max77843_led, + led_info[led_info->channel]); +} + +static void max77843_led_brightness_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + + struct max77843_led *led = cdev_to_led(led_cdev); + struct max77843_led_info *led_info = container_of(led_cdev, + struct max77843_led_info, cdev); + u8 channel = led_info->channel; + + switch (channel) { + case MAX77843_LED0: + regmap_write(led->regmap_led, + MAX77843_LED_REG_LED0BRT, value); + if (value == 0) + regmap_update_bits(led->regmap_led, + MAX77843_LED_REG_LEDEN, + MAX77843_LED_LED0EN_MASK, + OFF << LED0EN_SHIFT); + else + regmap_update_bits(led->regmap_led, + MAX77843_LED_REG_LEDEN, + MAX77843_LED_LED0EN_MASK, + CONSTANT << LED0EN_SHIFT); + + break; + case MAX77843_LED1: + regmap_write(led->regmap_led, + MAX77843_LED_REG_LED1BRT, value); + if (value == 0) + regmap_update_bits(led->regmap_led, + MAX77843_LED_REG_LEDEN, + MAX77843_LED_LED1EN_MASK, + OFF << LED1EN_SHIFT); + else + regmap_update_bits(led->regmap_led, + MAX77843_LED_REG_LEDEN, + MAX77843_LED_LED1EN_MASK, + CONSTANT << LED1EN_SHIFT); + break; + case MAX77843_LED2: + regmap_write(led->regmap_led, + MAX77843_LED_REG_LED2BRT, value); + if (value == 0) + regmap_update_bits(led->regmap_led, + MAX77843_LED_REG_LEDEN, + MAX77843_LED_LED2EN_MASK, + OFF << LED2EN_SHIFT); + else + regmap_update_bits(led->regmap_led, + MAX77843_LED_REG_LEDEN, + MAX77843_LED_LED2EN_MASK, + CONSTANT << LED2EN_SHIFT); + break; + case MAX77843_LED3: + regmap_write(led->regmap_led, + MAX77843_LED_REG_LED3BRT, value); + if (value == 0) + regmap_update_bits(led->regmap_led, + MAX77843_LED_REG_LEDEN, + MAX77843_LED_LED3EN_MASK, + OFF << LED3EN_SHIFT); + else + regmap_update_bits(led->regmap_led, + MAX77843_LED_REG_LEDEN, + MAX77843_LED_LED3EN_MASK, + CONSTANT << LED3EN_SHIFT); + break; + } +} + +static ssize_t max77843_led_show_color(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct max77843_led_info *led_info = container_of(cdev, + struct max77843_led_info, cdev); + + return sprintf(buf, "%s\n", led_info->color); +} + +static DEVICE_ATTR(color, 0644, max77843_led_show_color, NULL); + +static struct attribute *max77843_attrs[] = { + &dev_attr_color.attr, + NULL +}; +ATTRIBUTE_GROUPS(max77843); + +static int max77843_led_initialize(struct max77843_led_info *led_info, + const char *name, const char *color, enum max77843_leds id) +{ + struct led_classdev *cdev = &led_info->cdev; + struct max77843_led *led; + int ret; + + led_info->channel = id; + led_info->active = true; + led_info->color = color; + led = cdev_to_led(cdev); + + cdev->name = name; + cdev->brightness = 0; + cdev->max_brightness = MAX77843_MAX_BRIGHTNESS; + cdev->brightness_set = max77843_led_brightness_set; + cdev->groups = max77843_groups; + + ret = led_classdev_register(led->dev, cdev); + if (ret < 0) + return ret; + + return 0; +} + +static int max77843_led_probe(struct platform_device *pdev) +{ + struct max77693_dev *max77843 = dev_get_drvdata(pdev->dev.parent); + struct max77843_led *led; + struct device_node *child; + int i, ret; + + led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL); + if (!led) + return -ENOMEM; + + led->max77843 = max77843; + led->regmap_led = max77843->regmap; + led->dev = &pdev->dev; + + for_each_available_child_of_node(pdev->dev.of_node, child) { + int channel; + const char *label; + const char *color; + + ret = of_property_read_u32(child, "channel", &channel); + if (ret) { + dev_err(&pdev->dev, "failed to parse channel\n"); + of_node_put(child); + goto err_init; + } + + ret = of_property_read_string(child, "label", &label); + if (ret) { + dev_err(&pdev->dev, "failed to parse lable\n"); + of_node_put(child); + goto err_init; + } + + ret = of_property_read_string(child, "color", &color); + if (ret) { + dev_err(&pdev->dev, "failed to parse color\n"); + of_node_put(child); + goto err_init; + } + + ret = max77843_led_initialize(&led->led_info[channel], + label, color, channel); + if (ret < 0) { + dev_err(&pdev->dev, "failed to initialize leds\n"); + goto err_init; + } + } + + platform_set_drvdata(pdev, led); + + return 0; + +err_init: + for (i = 0; i < MAX77843_LED_NUM; i++) + if (led->led_info[i].active) + led_classdev_unregister(&led->led_info[i].cdev); + + return ret; +} + +static int max77843_led_remove(struct platform_device *pdev) +{ + struct max77843_led *led = platform_get_drvdata(pdev); + int i; + + for (i = 0; i < MAX77843_LED_NUM; i++) + if (led->led_info[i].active) + led_classdev_unregister(&led->led_info[i].cdev); + + return 0; +} + +static struct platform_driver max77843_led_driver = { + .driver = { + .name = "max77843-led", + }, + .probe = max77843_led_probe, + .remove = max77843_led_remove, +}; + +module_platform_driver(max77843_led_driver); + +MODULE_AUTHOR("Jaewon Kim "); +MODULE_DESCRIPTION("Maxim MAX77843 LED driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/max77843.c b/drivers/mfd/max77843.c index dc5caea..21c61d8 100644 --- a/drivers/mfd/max77843.c +++ b/drivers/mfd/max77843.c @@ -38,6 +38,9 @@ static const struct mfd_cell max77843_devs[] = { }, { .name = "max77843-haptic", .of_compatible = "maxim,max77843-haptic", + }, { + .name = "max77843-led", + .of_compatible = "maxim,max77843-led", }, }; diff --git a/include/linux/mfd/max77843-private.h b/include/linux/mfd/max77843-private.h index b8908bf..4f7934d 100644 --- a/include/linux/mfd/max77843-private.h +++ b/include/linux/mfd/max77843-private.h @@ -202,6 +202,39 @@ enum max77843_irq_muic { #define MAX77843_MCONFIG_MEN_MASK BIT(MCONFIG_MEN_SHIFT) #define MAX77843_MCONFIG_PDIV_MASK (0x3 << MCONFIG_PDIV_SHIFT) +#define LED0EN_SHIFT 0 +#define LED1EN_SHIFT 2 +#define LED2EN_SHIFT 4 +#define LED3EN_SHIFT 6 + +#define MAX77843_LED_LED0EN_MASK (0x3 << LED0EN_SHIFT) +#define MAX77843_LED_LED1EN_MASK (0x3 << LED1EN_SHIFT) +#define MAX77843_LED_LED2EN_MASK (0x3 << LED2EN_SHIFT) +#define MAX77843_LED_LED3EN_MASK (0x3 << LED3EN_SHIFT) + +#define OFF 0x0 +#define CONSTANT 0x1 +#define BLINK 0x2 + +#define MAX77843_LED_LED0EN_CONSTANT (CONSTANT << LED0EN_SHIFT) +#define MAX77843_LED_LED1EN_CONSTANT (CONSTANT << LED1EN_SHIFT) +#define MAX77843_LED_LED2EN_CONSTANT (CONSTANT << LED2EN_SHIFT) +#define MAX77843_LED_LED3EN_CONSTANT (CONSTANT << LED3EN_SHIFT) + +/* MAX77843 LEDBLNK register */ +#define LEDTON1_SHIFT 4 +#define LEDTOFF1_SHIFT 0 + +#define MAX77843_LEDBLNK_LEDTON1_MASK (0xF << LEDTON1_SHIFT) +#define MAX77843_LEDBLNK_LEDTOFF1_MASK (0xF << LEDTOFF1_SHIFT) + +/* MAX77843 LEDRAMP register */ +#define RMPUP1_SHIFT 4 +#define RMPDN1_SHIFT 0 + +#define MAX77843_LEDRAMP_RAMPUP1_MASK (0xF << RMPUP1_SHIFT) +#define MAX77843_LEDRAMP_RAMPDN1_MASK (0xF << RMPDN1_SHIFT) + /* Max77843 charger insterrupts */ #define MAX77843_CHG_BYP_I BIT(0) #define MAX77843_CHG_BATP_I BIT(2) -- 2.7.4