xref: /openbmc/linux/drivers/leds/leds-bd2606mvv.c (revision 8325642d)
1*8325642dSAndreas Kemnade // SPDX-License-Identifier: GPL-2.0-only
2*8325642dSAndreas Kemnade /*
3*8325642dSAndreas Kemnade  * Copyright (C) 2023 Andreas Kemnade
4*8325642dSAndreas Kemnade  *
5*8325642dSAndreas Kemnade  * Datasheet:
6*8325642dSAndreas Kemnade  * https://fscdn.rohm.com/en/products/databook/datasheet/ic/power/led_driver/bd2606mvv_1-e.pdf
7*8325642dSAndreas Kemnade  *
8*8325642dSAndreas Kemnade  * If LED brightness cannot be controlled independently due to shared
9*8325642dSAndreas Kemnade  * brightness registers, max_brightness is set to 1 and only on/off
10*8325642dSAndreas Kemnade  * is possible for the affected LED pair.
11*8325642dSAndreas Kemnade  */
12*8325642dSAndreas Kemnade 
13*8325642dSAndreas Kemnade #include <linux/i2c.h>
14*8325642dSAndreas Kemnade #include <linux/leds.h>
15*8325642dSAndreas Kemnade #include <linux/module.h>
16*8325642dSAndreas Kemnade #include <linux/mod_devicetable.h>
17*8325642dSAndreas Kemnade #include <linux/property.h>
18*8325642dSAndreas Kemnade #include <linux/regmap.h>
19*8325642dSAndreas Kemnade #include <linux/slab.h>
20*8325642dSAndreas Kemnade 
21*8325642dSAndreas Kemnade #define BD2606_MAX_LEDS 6
22*8325642dSAndreas Kemnade #define BD2606_MAX_BRIGHTNESS 63
23*8325642dSAndreas Kemnade #define BD2606_REG_PWRCNT 3
24*8325642dSAndreas Kemnade #define ldev_to_led(c)	container_of(c, struct bd2606mvv_led, ldev)
25*8325642dSAndreas Kemnade 
26*8325642dSAndreas Kemnade struct bd2606mvv_led {
27*8325642dSAndreas Kemnade 	unsigned int led_no;
28*8325642dSAndreas Kemnade 	struct led_classdev ldev;
29*8325642dSAndreas Kemnade 	struct bd2606mvv_priv *priv;
30*8325642dSAndreas Kemnade };
31*8325642dSAndreas Kemnade 
32*8325642dSAndreas Kemnade struct bd2606mvv_priv {
33*8325642dSAndreas Kemnade 	struct bd2606mvv_led leds[BD2606_MAX_LEDS];
34*8325642dSAndreas Kemnade 	struct regmap *regmap;
35*8325642dSAndreas Kemnade };
36*8325642dSAndreas Kemnade 
37*8325642dSAndreas Kemnade static int
38*8325642dSAndreas Kemnade bd2606mvv_brightness_set(struct led_classdev *led_cdev,
39*8325642dSAndreas Kemnade 		      enum led_brightness brightness)
40*8325642dSAndreas Kemnade {
41*8325642dSAndreas Kemnade 	struct bd2606mvv_led *led = ldev_to_led(led_cdev);
42*8325642dSAndreas Kemnade 	struct bd2606mvv_priv *priv = led->priv;
43*8325642dSAndreas Kemnade 	int err;
44*8325642dSAndreas Kemnade 
45*8325642dSAndreas Kemnade 	if (brightness == 0)
46*8325642dSAndreas Kemnade 		return regmap_update_bits(priv->regmap,
47*8325642dSAndreas Kemnade 					  BD2606_REG_PWRCNT,
48*8325642dSAndreas Kemnade 					  1 << led->led_no,
49*8325642dSAndreas Kemnade 					  0);
50*8325642dSAndreas Kemnade 
51*8325642dSAndreas Kemnade 	/* shared brightness register */
52*8325642dSAndreas Kemnade 	err = regmap_write(priv->regmap, led->led_no / 2,
53*8325642dSAndreas Kemnade 			   led_cdev->max_brightness == 1 ?
54*8325642dSAndreas Kemnade 			   BD2606_MAX_BRIGHTNESS : brightness);
55*8325642dSAndreas Kemnade 	if (err)
56*8325642dSAndreas Kemnade 		return err;
57*8325642dSAndreas Kemnade 
58*8325642dSAndreas Kemnade 	return regmap_update_bits(priv->regmap,
59*8325642dSAndreas Kemnade 				  BD2606_REG_PWRCNT,
60*8325642dSAndreas Kemnade 				  1 << led->led_no,
61*8325642dSAndreas Kemnade 				  1 << led->led_no);
62*8325642dSAndreas Kemnade }
63*8325642dSAndreas Kemnade 
64*8325642dSAndreas Kemnade static const struct regmap_config bd2606mvv_regmap = {
65*8325642dSAndreas Kemnade 	.reg_bits = 8,
66*8325642dSAndreas Kemnade 	.val_bits = 8,
67*8325642dSAndreas Kemnade 	.max_register = 0x3,
68*8325642dSAndreas Kemnade };
69*8325642dSAndreas Kemnade 
70*8325642dSAndreas Kemnade static int bd2606mvv_probe(struct i2c_client *client)
71*8325642dSAndreas Kemnade {
72*8325642dSAndreas Kemnade 	struct fwnode_handle *np, *child;
73*8325642dSAndreas Kemnade 	struct device *dev = &client->dev;
74*8325642dSAndreas Kemnade 	struct bd2606mvv_priv *priv;
75*8325642dSAndreas Kemnade 	struct fwnode_handle *led_fwnodes[BD2606_MAX_LEDS] = { 0 };
76*8325642dSAndreas Kemnade 	int active_pairs[BD2606_MAX_LEDS / 2] = { 0 };
77*8325642dSAndreas Kemnade 	int err, reg;
78*8325642dSAndreas Kemnade 	int i;
79*8325642dSAndreas Kemnade 
80*8325642dSAndreas Kemnade 	np = dev_fwnode(dev);
81*8325642dSAndreas Kemnade 	if (!np)
82*8325642dSAndreas Kemnade 		return -ENODEV;
83*8325642dSAndreas Kemnade 
84*8325642dSAndreas Kemnade 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
85*8325642dSAndreas Kemnade 	if (!priv)
86*8325642dSAndreas Kemnade 		return -ENOMEM;
87*8325642dSAndreas Kemnade 
88*8325642dSAndreas Kemnade 	priv->regmap = devm_regmap_init_i2c(client, &bd2606mvv_regmap);
89*8325642dSAndreas Kemnade 	if (IS_ERR(priv->regmap)) {
90*8325642dSAndreas Kemnade 		err = PTR_ERR(priv->regmap);
91*8325642dSAndreas Kemnade 		dev_err(dev, "Failed to allocate register map: %d\n", err);
92*8325642dSAndreas Kemnade 		return err;
93*8325642dSAndreas Kemnade 	}
94*8325642dSAndreas Kemnade 
95*8325642dSAndreas Kemnade 	i2c_set_clientdata(client, priv);
96*8325642dSAndreas Kemnade 
97*8325642dSAndreas Kemnade 	fwnode_for_each_available_child_node(np, child) {
98*8325642dSAndreas Kemnade 		struct bd2606mvv_led *led;
99*8325642dSAndreas Kemnade 
100*8325642dSAndreas Kemnade 		err = fwnode_property_read_u32(child, "reg", &reg);
101*8325642dSAndreas Kemnade 		if (err) {
102*8325642dSAndreas Kemnade 			fwnode_handle_put(child);
103*8325642dSAndreas Kemnade 			return err;
104*8325642dSAndreas Kemnade 		}
105*8325642dSAndreas Kemnade 		if (reg < 0 || reg >= BD2606_MAX_LEDS || led_fwnodes[reg]) {
106*8325642dSAndreas Kemnade 			fwnode_handle_put(child);
107*8325642dSAndreas Kemnade 			return -EINVAL;
108*8325642dSAndreas Kemnade 		}
109*8325642dSAndreas Kemnade 		led = &priv->leds[reg];
110*8325642dSAndreas Kemnade 		led_fwnodes[reg] = child;
111*8325642dSAndreas Kemnade 		active_pairs[reg / 2]++;
112*8325642dSAndreas Kemnade 		led->priv = priv;
113*8325642dSAndreas Kemnade 		led->led_no = reg;
114*8325642dSAndreas Kemnade 		led->ldev.brightness_set_blocking = bd2606mvv_brightness_set;
115*8325642dSAndreas Kemnade 		led->ldev.max_brightness = BD2606_MAX_BRIGHTNESS;
116*8325642dSAndreas Kemnade 	}
117*8325642dSAndreas Kemnade 
118*8325642dSAndreas Kemnade 	for (i = 0; i < BD2606_MAX_LEDS; i++) {
119*8325642dSAndreas Kemnade 		struct led_init_data init_data = {};
120*8325642dSAndreas Kemnade 
121*8325642dSAndreas Kemnade 		if (!led_fwnodes[i])
122*8325642dSAndreas Kemnade 			continue;
123*8325642dSAndreas Kemnade 
124*8325642dSAndreas Kemnade 		init_data.fwnode = led_fwnodes[i];
125*8325642dSAndreas Kemnade 		/* Check whether brightness can be independently adjusted. */
126*8325642dSAndreas Kemnade 		if (active_pairs[i / 2] == 2)
127*8325642dSAndreas Kemnade 			priv->leds[i].ldev.max_brightness = 1;
128*8325642dSAndreas Kemnade 
129*8325642dSAndreas Kemnade 		err = devm_led_classdev_register_ext(dev,
130*8325642dSAndreas Kemnade 						     &priv->leds[i].ldev,
131*8325642dSAndreas Kemnade 						     &init_data);
132*8325642dSAndreas Kemnade 		if (err < 0) {
133*8325642dSAndreas Kemnade 			fwnode_handle_put(child);
134*8325642dSAndreas Kemnade 			return dev_err_probe(dev, err,
135*8325642dSAndreas Kemnade 					     "couldn't register LED %s\n",
136*8325642dSAndreas Kemnade 					     priv->leds[i].ldev.name);
137*8325642dSAndreas Kemnade 		}
138*8325642dSAndreas Kemnade 	}
139*8325642dSAndreas Kemnade 	return 0;
140*8325642dSAndreas Kemnade }
141*8325642dSAndreas Kemnade 
142*8325642dSAndreas Kemnade static const struct of_device_id __maybe_unused of_bd2606mvv_leds_match[] = {
143*8325642dSAndreas Kemnade 	{ .compatible = "rohm,bd2606mvv", },
144*8325642dSAndreas Kemnade 	{},
145*8325642dSAndreas Kemnade };
146*8325642dSAndreas Kemnade MODULE_DEVICE_TABLE(of, of_bd2606mvv_leds_match);
147*8325642dSAndreas Kemnade 
148*8325642dSAndreas Kemnade static struct i2c_driver bd2606mvv_driver = {
149*8325642dSAndreas Kemnade 	.driver   = {
150*8325642dSAndreas Kemnade 		.name    = "leds-bd2606mvv",
151*8325642dSAndreas Kemnade 		.of_match_table = of_match_ptr(of_bd2606mvv_leds_match),
152*8325642dSAndreas Kemnade 	},
153*8325642dSAndreas Kemnade 	.probe_new = bd2606mvv_probe,
154*8325642dSAndreas Kemnade };
155*8325642dSAndreas Kemnade 
156*8325642dSAndreas Kemnade module_i2c_driver(bd2606mvv_driver);
157*8325642dSAndreas Kemnade 
158*8325642dSAndreas Kemnade MODULE_AUTHOR("Andreas Kemnade <andreas@kemnade.info>");
159*8325642dSAndreas Kemnade MODULE_DESCRIPTION("BD2606 LED driver");
160*8325642dSAndreas Kemnade MODULE_LICENSE("GPL");
161