leds: add support for MAX77843 led control driver 15/235915/1
authorSeung-Woo Kim <sw0312.kim@samsung.com>
Thu, 11 Jun 2020 02:39:01 +0000 (11:39 +0900)
committerSeung-Woo Kim <sw0312.kim@samsung.com>
Thu, 11 Jun 2020 02:39:13 +0000 (11:39 +0900)
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>
drivers/leds/Kconfig
drivers/leds/Makefile
drivers/leds/leds-max77843.c [new file with mode: 0644]
drivers/mfd/max77843.c
include/linux/mfd/max77843-private.h

index 52ea34e..2e08f83 100644 (file)
@@ -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
index 3598045..a13ef06 100644 (file)
@@ -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 (file)
index 0000000..4637cd4
--- /dev/null
@@ -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");
index dc5caea..21c61d8 100644 (file)
@@ -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",
        },
 };
 
index b8908bf..4f7934d 100644 (file)
@@ -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)