summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSeung-Woo Kim <sw0312.kim@samsung.com>2020-06-11 11:39:01 +0900
committerSeung-Woo Kim <sw0312.kim@samsung.com>2020-06-11 11:39:13 +0900
commit865f7cc8cdb26f1572d36058a5a00e1a6e253389 (patch)
treec5307d7e75b9a0713e2e2ef73c1ef1335d222792
parent7d599670f9864fc54e953f1a9ac3a0767968bfca (diff)
downloadlinux-exynos-865f7cc8cdb26f1572d36058a5a00e1a6e253389.tar.gz
linux-exynos-865f7cc8cdb26f1572d36058a5a00e1a6e253389.tar.bz2
linux-exynos-865f7cc8cdb26f1572d36058a5a00e1a6e253389.zip
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 <jaewon02.kim@samsung.com> Signed-off-by: Seung-Woo Kim <sw0312.kim@samsung.com>
-rw-r--r--drivers/leds/Kconfig7
-rw-r--r--drivers/leds/Makefile1
-rw-r--r--drivers/leds/leds-max77843.c254
-rw-r--r--drivers/mfd/max77843.c3
-rw-r--r--include/linux/mfd/max77843-private.h33
5 files changed, 298 insertions, 0 deletions
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 52ea34e337cd..2e08f837d8d1 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 35980450db9b..a13ef06cc79e 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 000000000000..4637cd491ebd
--- /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 <jaewon02.kim@samsung.com>
+ *
+ * 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 <linux/err.h>
+#include <linux/module.h>
+#include <linux/leds.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/max77693-common.h>
+#include <linux/mfd/max77843-private.h>
+
+#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 <jaewon02.kim@samsung.com>");
+MODULE_DESCRIPTION("Maxim MAX77843 LED driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/max77843.c b/drivers/mfd/max77843.c
index dc5caeaaa6a1..21c61d82fe80 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 b8908bf8d315..4f7934d68af3 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)