xref: /openbmc/linux/drivers/leds/leds-pm8058.c (revision 8957261c)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /* Copyright (c) 2010, 2011, 2016 The Linux Foundation. All rights reserved.
3  */
4 #include <linux/leds.h>
5 #include <linux/module.h>
6 #include <linux/of.h>
7 #include <linux/platform_device.h>
8 #include <linux/pm.h>
9 #include <linux/regmap.h>
10 
11 #define PM8058_LED_TYPE_COMMON	0x00
12 #define PM8058_LED_TYPE_KEYPAD	0x01
13 #define PM8058_LED_TYPE_FLASH	0x02
14 
15 #define PM8058_LED_TYPE_COMMON_MASK	0xf8
16 #define PM8058_LED_TYPE_KEYPAD_MASK	0xf0
17 #define PM8058_LED_TYPE_COMMON_SHIFT	3
18 #define PM8058_LED_TYPE_KEYPAD_SHIFT	4
19 
20 struct pm8058_led {
21 	struct regmap *map;
22 	u32 reg;
23 	u32 ledtype;
24 	struct led_classdev cdev;
25 };
26 
27 static void pm8058_led_set(struct led_classdev *cled,
28 	enum led_brightness value)
29 {
30 	struct pm8058_led *led;
31 	int ret = 0;
32 	unsigned int mask = 0;
33 	unsigned int val = 0;
34 
35 	led = container_of(cled, struct pm8058_led, cdev);
36 	switch (led->ledtype) {
37 	case PM8058_LED_TYPE_COMMON:
38 		mask = PM8058_LED_TYPE_COMMON_MASK;
39 		val = value << PM8058_LED_TYPE_COMMON_SHIFT;
40 		break;
41 	case PM8058_LED_TYPE_KEYPAD:
42 	case PM8058_LED_TYPE_FLASH:
43 		mask = PM8058_LED_TYPE_KEYPAD_MASK;
44 		val = value << PM8058_LED_TYPE_KEYPAD_SHIFT;
45 		break;
46 	default:
47 		break;
48 	}
49 
50 	ret = regmap_update_bits(led->map, led->reg, mask, val);
51 	if (ret)
52 		pr_err("Failed to set LED brightness\n");
53 }
54 
55 static enum led_brightness pm8058_led_get(struct led_classdev *cled)
56 {
57 	struct pm8058_led *led;
58 	int ret;
59 	unsigned int val;
60 
61 	led = container_of(cled, struct pm8058_led, cdev);
62 
63 	ret = regmap_read(led->map, led->reg, &val);
64 	if (ret) {
65 		pr_err("Failed to get LED brightness\n");
66 		return LED_OFF;
67 	}
68 
69 	switch (led->ledtype) {
70 	case PM8058_LED_TYPE_COMMON:
71 		val &= PM8058_LED_TYPE_COMMON_MASK;
72 		val >>= PM8058_LED_TYPE_COMMON_SHIFT;
73 		break;
74 	case PM8058_LED_TYPE_KEYPAD:
75 	case PM8058_LED_TYPE_FLASH:
76 		val &= PM8058_LED_TYPE_KEYPAD_MASK;
77 		val >>= PM8058_LED_TYPE_KEYPAD_SHIFT;
78 		break;
79 	default:
80 		val = LED_OFF;
81 		break;
82 	}
83 
84 	return val;
85 }
86 
87 static int pm8058_led_probe(struct platform_device *pdev)
88 {
89 	struct led_init_data init_data = {};
90 	struct device *dev = &pdev->dev;
91 	struct pm8058_led *led;
92 	struct device_node *np;
93 	int ret;
94 	struct regmap *map;
95 	enum led_brightness maxbright;
96 	enum led_default_state state;
97 
98 	led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);
99 	if (!led)
100 		return -ENOMEM;
101 
102 	led->ledtype = (u32)(unsigned long)of_device_get_match_data(dev);
103 
104 	map = dev_get_regmap(dev->parent, NULL);
105 	if (!map) {
106 		dev_err(dev, "Parent regmap unavailable.\n");
107 		return -ENXIO;
108 	}
109 	led->map = map;
110 
111 	np = dev_of_node(dev);
112 
113 	ret = of_property_read_u32(np, "reg", &led->reg);
114 	if (ret) {
115 		dev_err(dev, "no register offset specified\n");
116 		return -EINVAL;
117 	}
118 
119 	led->cdev.brightness_set = pm8058_led_set;
120 	led->cdev.brightness_get = pm8058_led_get;
121 	if (led->ledtype == PM8058_LED_TYPE_COMMON)
122 		maxbright = 31; /* 5 bits */
123 	else
124 		maxbright = 15; /* 4 bits */
125 	led->cdev.max_brightness = maxbright;
126 
127 	init_data.fwnode = of_fwnode_handle(np);
128 
129 	state = led_init_default_state_get(init_data.fwnode);
130 	switch (state) {
131 	case LEDS_DEFSTATE_ON:
132 		led->cdev.brightness = maxbright;
133 		pm8058_led_set(&led->cdev, maxbright);
134 		break;
135 	case LEDS_DEFSTATE_KEEP:
136 		led->cdev.brightness = pm8058_led_get(&led->cdev);
137 		break;
138 	default:
139 		led->cdev.brightness = LED_OFF;
140 		pm8058_led_set(&led->cdev, LED_OFF);
141 	}
142 
143 	if (led->ledtype == PM8058_LED_TYPE_KEYPAD ||
144 	    led->ledtype == PM8058_LED_TYPE_FLASH)
145 		led->cdev.flags	= LED_CORE_SUSPENDRESUME;
146 
147 	ret = devm_led_classdev_register_ext(dev, &led->cdev, &init_data);
148 	if (ret)
149 		dev_err(dev, "Failed to register LED for %pOF\n", np);
150 
151 	return ret;
152 }
153 
154 static const struct of_device_id pm8058_leds_id_table[] = {
155 	{
156 		.compatible = "qcom,pm8058-led",
157 		.data = (void *)PM8058_LED_TYPE_COMMON
158 	},
159 	{
160 		.compatible = "qcom,pm8058-keypad-led",
161 		.data = (void *)PM8058_LED_TYPE_KEYPAD
162 	},
163 	{
164 		.compatible = "qcom,pm8058-flash-led",
165 		.data = (void *)PM8058_LED_TYPE_FLASH
166 	},
167 	{ },
168 };
169 MODULE_DEVICE_TABLE(of, pm8058_leds_id_table);
170 
171 static struct platform_driver pm8058_led_driver = {
172 	.probe		= pm8058_led_probe,
173 	.driver		= {
174 		.name	= "pm8058-leds",
175 		.of_match_table = pm8058_leds_id_table,
176 	},
177 };
178 module_platform_driver(pm8058_led_driver);
179 
180 MODULE_DESCRIPTION("PM8058 LEDs driver");
181 MODULE_LICENSE("GPL v2");
182 MODULE_ALIAS("platform:pm8058-leds");
183