summaryrefslogtreecommitdiffstats
path: root/drivers/leds/leds-max77650.c
blob: 6b74ce9cac12118be314e9f6564c76f1bb36b62d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
// SPDX-License-Identifier: GPL-2.0
//
// Copyright (C) 2018 BayLibre SAS
// Author: Bartosz Golaszewski <bgolaszewski@baylibre.com>
//
// LED driver for MAXIM 77650/77651 charger/power-supply.

#include <linux/i2c.h>
#include <linux/leds.h>
#include <linux/mfd/max77650.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>

#define MAX77650_LED_NUM_LEDS		3

#define MAX77650_LED_A_BASE		0x40
#define MAX77650_LED_B_BASE		0x43

#define MAX77650_LED_BR_MASK		GENMASK(4, 0)
#define MAX77650_LED_EN_MASK		GENMASK(7, 6)

#define MAX77650_LED_MAX_BRIGHTNESS	MAX77650_LED_BR_MASK

/* Enable EN_LED_MSTR. */
#define MAX77650_LED_TOP_DEFAULT	BIT(0)

#define MAX77650_LED_ENABLE		GENMASK(7, 6)
#define MAX77650_LED_DISABLE		0x00

#define MAX77650_LED_A_DEFAULT		MAX77650_LED_DISABLE
/* 100% on duty */
#define MAX77650_LED_B_DEFAULT		GENMASK(3, 0)

struct max77650_led {
	struct led_classdev cdev;
	struct regmap *map;
	unsigned int regA;
	unsigned int regB;
};

static struct max77650_led *max77650_to_led(struct led_classdev *cdev)
{
	return container_of(cdev, struct max77650_led, cdev);
}

static int max77650_led_brightness_set(struct led_classdev *cdev,
				       enum led_brightness brightness)
{
	struct max77650_led *led = max77650_to_led(cdev);
	int val, mask;

	mask = MAX77650_LED_BR_MASK | MAX77650_LED_EN_MASK;

	if (brightness == LED_OFF)
		val = MAX77650_LED_DISABLE;
	else
		val = MAX77650_LED_ENABLE | brightness;

	return regmap_update_bits(led->map, led->regA, mask, val);
}

static int max77650_led_probe(struct platform_device *pdev)
{
	struct device_node *of_node, *child;
	struct max77650_led *leds, *led;
	struct device *parent;
	struct device *dev;
	struct regmap *map;
	const char *label;
	int rv, num_leds;
	u32 reg;

	dev = &pdev->dev;
	parent = dev->parent;
	of_node = dev->of_node;

	if (!of_node)
		return -ENODEV;

	leds = devm_kcalloc(dev, sizeof(*leds),
			    MAX77650_LED_NUM_LEDS, GFP_KERNEL);
	if (!leds)
		return -ENOMEM;

	map = dev_get_regmap(dev->parent, NULL);
	if (!map)
		return -ENODEV;

	num_leds = of_get_child_count(of_node);
	if (!num_leds || num_leds > MAX77650_LED_NUM_LEDS)
		return -ENODEV;

	for_each_child_of_node(of_node, child) {
		rv = of_property_read_u32(child, "reg", &reg);
		if (rv || reg >= MAX77650_LED_NUM_LEDS)
			return -EINVAL;

		led = &leds[reg];
		led->map = map;
		led->regA = MAX77650_LED_A_BASE + reg;
		led->regB = MAX77650_LED_B_BASE + reg;
		led->cdev.brightness_set_blocking = max77650_led_brightness_set;
		led->cdev.max_brightness = MAX77650_LED_MAX_BRIGHTNESS;

		label = of_get_property(child, "label", NULL);
		if (!label) {
			led->cdev.name = "max77650::";
		} else {
			led->cdev.name = devm_kasprintf(dev, GFP_KERNEL,
							"max77650:%s", label);
			if (!led->cdev.name)
				return -ENOMEM;
		}

		of_property_read_string(child, "linux,default-trigger",
					&led->cdev.default_trigger);

		rv = devm_of_led_classdev_register(dev, child, &led->cdev);
		if (rv)
			return rv;

		rv = regmap_write(map, led->regA, MAX77650_LED_A_DEFAULT);
		if (rv)
			return rv;

		rv = regmap_write(map, led->regB, MAX77650_LED_B_DEFAULT);
		if (rv)
			return rv;
	}

	return regmap_write(map,
			    MAX77650_REG_CNFG_LED_TOP,
			    MAX77650_LED_TOP_DEFAULT);
}

static struct platform_driver max77650_led_driver = {
	.driver = {
		.name = "max77650-led",
	},
	.probe = max77650_led_probe,
};
module_platform_driver(max77650_led_driver);

MODULE_DESCRIPTION("MAXIM 77650/77651 LED driver");
MODULE_AUTHOR("Bartosz Golaszewski <bgolaszewski@baylibre.com>");
MODULE_LICENSE("GPL v2");
OpenPOWER on IntegriCloud