xref: /openbmc/linux/drivers/leds/leds-an30259a.c (revision ee1cd5048959de496cd005c50b137212a5b62062)
12d00f35cSSimon Shields // SPDX-License-Identifier: GPL-2.0+
22d00f35cSSimon Shields //
32d00f35cSSimon Shields // Driver for Panasonic AN30259A 3-channel LED driver
42d00f35cSSimon Shields //
52d00f35cSSimon Shields // Copyright (c) 2018 Simon Shields <simon@lineageos.org>
62d00f35cSSimon Shields //
72d00f35cSSimon Shields // Datasheet:
82d00f35cSSimon Shields // https://www.alliedelec.com/m/d/a9d2b3ee87c2d1a535a41dd747b1c247.pdf
92d00f35cSSimon Shields 
102d00f35cSSimon Shields #include <linux/i2c.h>
112d00f35cSSimon Shields #include <linux/leds.h>
122d00f35cSSimon Shields #include <linux/module.h>
132d00f35cSSimon Shields #include <linux/mutex.h>
142d00f35cSSimon Shields #include <linux/of.h>
152d00f35cSSimon Shields #include <linux/regmap.h>
162d00f35cSSimon Shields 
172d00f35cSSimon Shields #define AN30259A_MAX_LEDS 3
182d00f35cSSimon Shields 
192d00f35cSSimon Shields #define AN30259A_REG_SRESET 0x00
202d00f35cSSimon Shields #define AN30259A_LED_SRESET BIT(0)
212d00f35cSSimon Shields 
222d00f35cSSimon Shields /* LED power registers */
232d00f35cSSimon Shields #define AN30259A_REG_LED_ON 0x01
242d00f35cSSimon Shields #define AN30259A_LED_EN(x) BIT((x) - 1)
252d00f35cSSimon Shields #define AN30259A_LED_SLOPE(x) BIT(((x) - 1) + 4)
262d00f35cSSimon Shields 
272d00f35cSSimon Shields #define AN30259A_REG_LEDCC(x) (0x03 + ((x) - 1))
282d00f35cSSimon Shields 
292d00f35cSSimon Shields /* slope control registers */
302d00f35cSSimon Shields #define AN30259A_REG_SLOPE(x) (0x06 + ((x) - 1))
312d00f35cSSimon Shields #define AN30259A_LED_SLOPETIME1(x) (x)
322d00f35cSSimon Shields #define AN30259A_LED_SLOPETIME2(x) ((x) << 4)
332d00f35cSSimon Shields 
342d00f35cSSimon Shields #define AN30259A_REG_LEDCNT1(x) (0x09 + (4 * ((x) - 1)))
352d00f35cSSimon Shields #define AN30259A_LED_DUTYMAX(x) ((x) << 4)
362d00f35cSSimon Shields #define AN30259A_LED_DUTYMID(x) (x)
372d00f35cSSimon Shields 
382d00f35cSSimon Shields #define AN30259A_REG_LEDCNT2(x) (0x0A + (4 * ((x) - 1)))
392d00f35cSSimon Shields #define AN30259A_LED_DELAY(x) ((x) << 4)
402d00f35cSSimon Shields #define AN30259A_LED_DUTYMIN(x) (x)
412d00f35cSSimon Shields 
422d00f35cSSimon Shields /* detention time control (length of each slope step) */
432d00f35cSSimon Shields #define AN30259A_REG_LEDCNT3(x) (0x0B + (4 * ((x) - 1)))
442d00f35cSSimon Shields #define AN30259A_LED_DT1(x) (x)
452d00f35cSSimon Shields #define AN30259A_LED_DT2(x) ((x) << 4)
462d00f35cSSimon Shields 
472d00f35cSSimon Shields #define AN30259A_REG_LEDCNT4(x) (0x0C + (4 * ((x) - 1)))
482d00f35cSSimon Shields #define AN30259A_LED_DT3(x) (x)
492d00f35cSSimon Shields #define AN30259A_LED_DT4(x) ((x) << 4)
502d00f35cSSimon Shields 
512d00f35cSSimon Shields #define AN30259A_REG_MAX 0x14
522d00f35cSSimon Shields 
532d00f35cSSimon Shields #define AN30259A_BLINK_MAX_TIME 7500 /* ms */
542d00f35cSSimon Shields #define AN30259A_SLOPE_RESOLUTION 500 /* ms */
552d00f35cSSimon Shields 
561817208eSJacek Anaszewski #define AN30259A_NAME "an30259a"
571817208eSJacek Anaszewski 
582d00f35cSSimon Shields struct an30259a;
592d00f35cSSimon Shields 
602d00f35cSSimon Shields struct an30259a_led {
612d00f35cSSimon Shields 	struct an30259a *chip;
621817208eSJacek Anaszewski 	struct fwnode_handle *fwnode;
632d00f35cSSimon Shields 	struct led_classdev cdev;
642d00f35cSSimon Shields 	u32 num;
655ff422a7SAndy Shevchenko 	enum led_default_state default_state;
662d00f35cSSimon Shields 	bool sloping;
672d00f35cSSimon Shields };
682d00f35cSSimon Shields 
692d00f35cSSimon Shields struct an30259a {
702d00f35cSSimon Shields 	struct mutex mutex; /* held when writing to registers */
712d00f35cSSimon Shields 	struct i2c_client *client;
722d00f35cSSimon Shields 	struct an30259a_led leds[AN30259A_MAX_LEDS];
732d00f35cSSimon Shields 	struct regmap *regmap;
742d00f35cSSimon Shields 	int num_leds;
752d00f35cSSimon Shields };
762d00f35cSSimon Shields 
an30259a_brightness_set(struct led_classdev * cdev,enum led_brightness brightness)772d00f35cSSimon Shields static int an30259a_brightness_set(struct led_classdev *cdev,
782d00f35cSSimon Shields 				   enum led_brightness brightness)
792d00f35cSSimon Shields {
802d00f35cSSimon Shields 	struct an30259a_led *led;
812d00f35cSSimon Shields 	int ret;
822d00f35cSSimon Shields 	unsigned int led_on;
832d00f35cSSimon Shields 
842d00f35cSSimon Shields 	led = container_of(cdev, struct an30259a_led, cdev);
852d00f35cSSimon Shields 	mutex_lock(&led->chip->mutex);
862d00f35cSSimon Shields 
872d00f35cSSimon Shields 	ret = regmap_read(led->chip->regmap, AN30259A_REG_LED_ON, &led_on);
882d00f35cSSimon Shields 	if (ret)
892d00f35cSSimon Shields 		goto error;
902d00f35cSSimon Shields 
912d00f35cSSimon Shields 	switch (brightness) {
922d00f35cSSimon Shields 	case LED_OFF:
932d00f35cSSimon Shields 		led_on &= ~AN30259A_LED_EN(led->num);
942d00f35cSSimon Shields 		led_on &= ~AN30259A_LED_SLOPE(led->num);
952d00f35cSSimon Shields 		led->sloping = false;
962d00f35cSSimon Shields 		break;
972d00f35cSSimon Shields 	default:
982d00f35cSSimon Shields 		led_on |= AN30259A_LED_EN(led->num);
992d00f35cSSimon Shields 		if (led->sloping)
1002d00f35cSSimon Shields 			led_on |= AN30259A_LED_SLOPE(led->num);
1012d00f35cSSimon Shields 		ret = regmap_write(led->chip->regmap,
1022d00f35cSSimon Shields 				   AN30259A_REG_LEDCNT1(led->num),
1032d00f35cSSimon Shields 				   AN30259A_LED_DUTYMAX(0xf) |
1042d00f35cSSimon Shields 				   AN30259A_LED_DUTYMID(0xf));
1052d00f35cSSimon Shields 		if (ret)
1062d00f35cSSimon Shields 			goto error;
1072d00f35cSSimon Shields 		break;
1082d00f35cSSimon Shields 	}
1092d00f35cSSimon Shields 
1102d00f35cSSimon Shields 	ret = regmap_write(led->chip->regmap, AN30259A_REG_LED_ON, led_on);
1112d00f35cSSimon Shields 	if (ret)
1122d00f35cSSimon Shields 		goto error;
1132d00f35cSSimon Shields 
1142d00f35cSSimon Shields 	ret = regmap_write(led->chip->regmap, AN30259A_REG_LEDCC(led->num),
1152d00f35cSSimon Shields 			   brightness);
1162d00f35cSSimon Shields 
1172d00f35cSSimon Shields error:
1182d00f35cSSimon Shields 	mutex_unlock(&led->chip->mutex);
1192d00f35cSSimon Shields 
1202d00f35cSSimon Shields 	return ret;
1212d00f35cSSimon Shields }
1222d00f35cSSimon Shields 
an30259a_blink_set(struct led_classdev * cdev,unsigned long * delay_off,unsigned long * delay_on)1232d00f35cSSimon Shields static int an30259a_blink_set(struct led_classdev *cdev,
1242d00f35cSSimon Shields 			      unsigned long *delay_off, unsigned long *delay_on)
1252d00f35cSSimon Shields {
1262d00f35cSSimon Shields 	struct an30259a_led *led;
1272d00f35cSSimon Shields 	int ret, num;
1282d00f35cSSimon Shields 	unsigned int led_on;
1292d00f35cSSimon Shields 	unsigned long off = *delay_off, on = *delay_on;
1302d00f35cSSimon Shields 
1312d00f35cSSimon Shields 	led = container_of(cdev, struct an30259a_led, cdev);
1322d00f35cSSimon Shields 
1332d00f35cSSimon Shields 	mutex_lock(&led->chip->mutex);
1342d00f35cSSimon Shields 	num = led->num;
1352d00f35cSSimon Shields 
1362d00f35cSSimon Shields 	/* slope time can only be a multiple of 500ms. */
1372d00f35cSSimon Shields 	if (off % AN30259A_SLOPE_RESOLUTION || on % AN30259A_SLOPE_RESOLUTION) {
1382d00f35cSSimon Shields 		ret = -EINVAL;
1392d00f35cSSimon Shields 		goto error;
1402d00f35cSSimon Shields 	}
1412d00f35cSSimon Shields 
1422d00f35cSSimon Shields 	/* up to a maximum of 7500ms. */
1432d00f35cSSimon Shields 	if (off > AN30259A_BLINK_MAX_TIME || on > AN30259A_BLINK_MAX_TIME) {
1442d00f35cSSimon Shields 		ret = -EINVAL;
1452d00f35cSSimon Shields 		goto error;
1462d00f35cSSimon Shields 	}
1472d00f35cSSimon Shields 
1482d00f35cSSimon Shields 	/* if no blink specified, default to 1 Hz. */
1492d00f35cSSimon Shields 	if (!off && !on) {
1502d00f35cSSimon Shields 		*delay_off = off = 500;
1512d00f35cSSimon Shields 		*delay_on = on = 500;
1522d00f35cSSimon Shields 	}
1532d00f35cSSimon Shields 
1542d00f35cSSimon Shields 	/* convert into values the HW will understand. */
1552d00f35cSSimon Shields 	off /= AN30259A_SLOPE_RESOLUTION;
1562d00f35cSSimon Shields 	on /= AN30259A_SLOPE_RESOLUTION;
1572d00f35cSSimon Shields 
1582d00f35cSSimon Shields 	/* duty min should be zero (=off), delay should be zero. */
1592d00f35cSSimon Shields 	ret = regmap_write(led->chip->regmap, AN30259A_REG_LEDCNT2(num),
1602d00f35cSSimon Shields 			   AN30259A_LED_DELAY(0) | AN30259A_LED_DUTYMIN(0));
1612d00f35cSSimon Shields 	if (ret)
1622d00f35cSSimon Shields 		goto error;
1632d00f35cSSimon Shields 
1642d00f35cSSimon Shields 	/* reset detention time (no "breathing" effect). */
1652d00f35cSSimon Shields 	ret = regmap_write(led->chip->regmap, AN30259A_REG_LEDCNT3(num),
1662d00f35cSSimon Shields 			   AN30259A_LED_DT1(0) | AN30259A_LED_DT2(0));
1672d00f35cSSimon Shields 	if (ret)
1682d00f35cSSimon Shields 		goto error;
1692d00f35cSSimon Shields 	ret = regmap_write(led->chip->regmap, AN30259A_REG_LEDCNT4(num),
1702d00f35cSSimon Shields 			   AN30259A_LED_DT3(0) | AN30259A_LED_DT4(0));
1712d00f35cSSimon Shields 	if (ret)
1722d00f35cSSimon Shields 		goto error;
1732d00f35cSSimon Shields 
1742d00f35cSSimon Shields 	/* slope time controls on/off cycle length. */
1752d00f35cSSimon Shields 	ret = regmap_write(led->chip->regmap, AN30259A_REG_SLOPE(num),
1762d00f35cSSimon Shields 			   AN30259A_LED_SLOPETIME1(off) |
1772d00f35cSSimon Shields 			   AN30259A_LED_SLOPETIME2(on));
1782d00f35cSSimon Shields 	if (ret)
1792d00f35cSSimon Shields 		goto error;
1802d00f35cSSimon Shields 
1812d00f35cSSimon Shields 	/* Finally, enable slope mode. */
1822d00f35cSSimon Shields 	ret = regmap_read(led->chip->regmap, AN30259A_REG_LED_ON, &led_on);
1832d00f35cSSimon Shields 	if (ret)
1842d00f35cSSimon Shields 		goto error;
1852d00f35cSSimon Shields 
1862d00f35cSSimon Shields 	led_on |= AN30259A_LED_SLOPE(num) | AN30259A_LED_EN(led->num);
1872d00f35cSSimon Shields 
1882d00f35cSSimon Shields 	ret = regmap_write(led->chip->regmap, AN30259A_REG_LED_ON, led_on);
1892d00f35cSSimon Shields 
1902d00f35cSSimon Shields 	if (!ret)
1912d00f35cSSimon Shields 		led->sloping = true;
1922d00f35cSSimon Shields error:
1932d00f35cSSimon Shields 	mutex_unlock(&led->chip->mutex);
1942d00f35cSSimon Shields 
1952d00f35cSSimon Shields 	return ret;
1962d00f35cSSimon Shields }
1972d00f35cSSimon Shields 
an30259a_dt_init(struct i2c_client * client,struct an30259a * chip)1982d00f35cSSimon Shields static int an30259a_dt_init(struct i2c_client *client,
1992d00f35cSSimon Shields 			    struct an30259a *chip)
2002d00f35cSSimon Shields {
2018853c95eSMarek Behún 	struct device_node *np = dev_of_node(&client->dev), *child;
2022d00f35cSSimon Shields 	int count, ret;
2032d00f35cSSimon Shields 	int i = 0;
2042d00f35cSSimon Shields 	struct an30259a_led *led;
2052d00f35cSSimon Shields 
20699a013c8SMarek Behún 	count = of_get_available_child_count(np);
2072d00f35cSSimon Shields 	if (!count || count > AN30259A_MAX_LEDS)
2082d00f35cSSimon Shields 		return -EINVAL;
2092d00f35cSSimon Shields 
2102d00f35cSSimon Shields 	for_each_available_child_of_node(np, child) {
2112d00f35cSSimon Shields 		u32 source;
2122d00f35cSSimon Shields 
2132d00f35cSSimon Shields 		ret = of_property_read_u32(child, "reg", &source);
2142d00f35cSSimon Shields 		if (ret != 0 || !source || source > AN30259A_MAX_LEDS) {
2152d00f35cSSimon Shields 			dev_err(&client->dev, "Couldn't read LED address: %d\n",
2162d00f35cSSimon Shields 				ret);
2172d00f35cSSimon Shields 			count--;
2182d00f35cSSimon Shields 			continue;
2192d00f35cSSimon Shields 		}
2202d00f35cSSimon Shields 
2212d00f35cSSimon Shields 		led = &chip->leds[i];
2222d00f35cSSimon Shields 
2232d00f35cSSimon Shields 		led->num = source;
2242d00f35cSSimon Shields 		led->chip = chip;
2251817208eSJacek Anaszewski 		led->fwnode = of_fwnode_handle(child);
2265ff422a7SAndy Shevchenko 		led->default_state = led_init_default_state_get(led->fwnode);
2272d00f35cSSimon Shields 
2282d00f35cSSimon Shields 		i++;
2292d00f35cSSimon Shields 	}
2302d00f35cSSimon Shields 
2312d00f35cSSimon Shields 	if (!count)
2322d00f35cSSimon Shields 		return -EINVAL;
2332d00f35cSSimon Shields 
2342d00f35cSSimon Shields 	chip->num_leds = i;
2352d00f35cSSimon Shields 
2362d00f35cSSimon Shields 	return 0;
2372d00f35cSSimon Shields }
2382d00f35cSSimon Shields 
2392d00f35cSSimon Shields static const struct regmap_config an30259a_regmap_config = {
2402d00f35cSSimon Shields 	.reg_bits = 8,
2412d00f35cSSimon Shields 	.val_bits = 8,
2422d00f35cSSimon Shields 	.max_register = AN30259A_REG_MAX,
2432d00f35cSSimon Shields };
2442d00f35cSSimon Shields 
an30259a_init_default_state(struct an30259a_led * led)2452d00f35cSSimon Shields static void an30259a_init_default_state(struct an30259a_led *led)
2462d00f35cSSimon Shields {
2472d00f35cSSimon Shields 	struct an30259a *chip = led->chip;
2482d00f35cSSimon Shields 	int led_on, err;
2492d00f35cSSimon Shields 
2502d00f35cSSimon Shields 	switch (led->default_state) {
2515ff422a7SAndy Shevchenko 	case LEDS_DEFSTATE_ON:
2522d00f35cSSimon Shields 		led->cdev.brightness = LED_FULL;
2532d00f35cSSimon Shields 		break;
2545ff422a7SAndy Shevchenko 	case LEDS_DEFSTATE_KEEP:
2552d00f35cSSimon Shields 		err = regmap_read(chip->regmap, AN30259A_REG_LED_ON, &led_on);
2562d00f35cSSimon Shields 		if (err)
2572d00f35cSSimon Shields 			break;
2582d00f35cSSimon Shields 
2592d00f35cSSimon Shields 		if (!(led_on & AN30259A_LED_EN(led->num))) {
2602d00f35cSSimon Shields 			led->cdev.brightness = LED_OFF;
2612d00f35cSSimon Shields 			break;
2622d00f35cSSimon Shields 		}
2632d00f35cSSimon Shields 		regmap_read(chip->regmap, AN30259A_REG_LEDCC(led->num),
2642d00f35cSSimon Shields 			    &led->cdev.brightness);
2652d00f35cSSimon Shields 		break;
2662d00f35cSSimon Shields 	default:
2672d00f35cSSimon Shields 		led->cdev.brightness = LED_OFF;
2682d00f35cSSimon Shields 	}
2692d00f35cSSimon Shields 
2702d00f35cSSimon Shields 	an30259a_brightness_set(&led->cdev, led->cdev.brightness);
2712d00f35cSSimon Shields }
2722d00f35cSSimon Shields 
an30259a_probe(struct i2c_client * client)2732d00f35cSSimon Shields static int an30259a_probe(struct i2c_client *client)
2742d00f35cSSimon Shields {
2752d00f35cSSimon Shields 	struct an30259a *chip;
2762d00f35cSSimon Shields 	int i, err;
2772d00f35cSSimon Shields 
2782d00f35cSSimon Shields 	chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
2792d00f35cSSimon Shields 	if (!chip)
2802d00f35cSSimon Shields 		return -ENOMEM;
2812d00f35cSSimon Shields 
2822d00f35cSSimon Shields 	err = an30259a_dt_init(client, chip);
2832d00f35cSSimon Shields 	if (err < 0)
2842d00f35cSSimon Shields 		return err;
2852d00f35cSSimon Shields 
286*3ead19aaSGeorge Stark 	err = devm_mutex_init(&client->dev, &chip->mutex);
287*3ead19aaSGeorge Stark 	if (err)
288*3ead19aaSGeorge Stark 		return err;
289*3ead19aaSGeorge Stark 
2902d00f35cSSimon Shields 	chip->client = client;
2912d00f35cSSimon Shields 	i2c_set_clientdata(client, chip);
2922d00f35cSSimon Shields 
2932d00f35cSSimon Shields 	chip->regmap = devm_regmap_init_i2c(client, &an30259a_regmap_config);
2942d00f35cSSimon Shields 
295fc7b5028SChuhong Yuan 	if (IS_ERR(chip->regmap)) {
296fc7b5028SChuhong Yuan 		err = PTR_ERR(chip->regmap);
297fc7b5028SChuhong Yuan 		dev_err(&client->dev, "Failed to allocate register map: %d\n",
298fc7b5028SChuhong Yuan 			err);
299fc7b5028SChuhong Yuan 		goto exit;
300fc7b5028SChuhong Yuan 	}
301fc7b5028SChuhong Yuan 
3022d00f35cSSimon Shields 	for (i = 0; i < chip->num_leds; i++) {
3031817208eSJacek Anaszewski 		struct led_init_data init_data = {};
3041817208eSJacek Anaszewski 
3052d00f35cSSimon Shields 		an30259a_init_default_state(&chip->leds[i]);
3062d00f35cSSimon Shields 		chip->leds[i].cdev.brightness_set_blocking =
3072d00f35cSSimon Shields 			an30259a_brightness_set;
3082d00f35cSSimon Shields 		chip->leds[i].cdev.blink_set = an30259a_blink_set;
3092d00f35cSSimon Shields 
3101817208eSJacek Anaszewski 		init_data.fwnode = chip->leds[i].fwnode;
3111817208eSJacek Anaszewski 		init_data.devicename = AN30259A_NAME;
3121817208eSJacek Anaszewski 		init_data.default_label = ":";
3131817208eSJacek Anaszewski 
3141817208eSJacek Anaszewski 		err = devm_led_classdev_register_ext(&client->dev,
3151817208eSJacek Anaszewski 						 &chip->leds[i].cdev,
3161817208eSJacek Anaszewski 						 &init_data);
3172d00f35cSSimon Shields 		if (err < 0)
3182d00f35cSSimon Shields 			goto exit;
3192d00f35cSSimon Shields 	}
3202d00f35cSSimon Shields 	return 0;
3212d00f35cSSimon Shields 
3222d00f35cSSimon Shields exit:
3232d00f35cSSimon Shields 	return err;
3242d00f35cSSimon Shields }
3252d00f35cSSimon Shields 
3262d00f35cSSimon Shields static const struct of_device_id an30259a_match_table[] = {
3272d00f35cSSimon Shields 	{ .compatible = "panasonic,an30259a", },
3282d00f35cSSimon Shields 	{ /* sentinel */ },
3292d00f35cSSimon Shields };
3302d00f35cSSimon Shields 
3312d00f35cSSimon Shields MODULE_DEVICE_TABLE(of, an30259a_match_table);
3322d00f35cSSimon Shields 
3332d00f35cSSimon Shields static const struct i2c_device_id an30259a_id[] = {
3342d00f35cSSimon Shields 	{ "an30259a", 0 },
3352d00f35cSSimon Shields 	{ /* sentinel */ },
3362d00f35cSSimon Shields };
3372d00f35cSSimon Shields MODULE_DEVICE_TABLE(i2c, an30259a_id);
3382d00f35cSSimon Shields 
3392d00f35cSSimon Shields static struct i2c_driver an30259a_driver = {
3402d00f35cSSimon Shields 	.driver = {
341f3b357c2SChristophe JAILLET 		.name = "leds-an30259a",
3423d590af8SZhu Wang 		.of_match_table = an30259a_match_table,
3432d00f35cSSimon Shields 	},
344d9ff8a8eSUwe Kleine-König 	.probe = an30259a_probe,
3452d00f35cSSimon Shields 	.id_table = an30259a_id,
3462d00f35cSSimon Shields };
3472d00f35cSSimon Shields 
3482d00f35cSSimon Shields module_i2c_driver(an30259a_driver);
3492d00f35cSSimon Shields 
3502d00f35cSSimon Shields MODULE_AUTHOR("Simon Shields <simon@lineageos.org>");
351f3b357c2SChristophe JAILLET MODULE_DESCRIPTION("AN30259A LED driver");
3522d00f35cSSimon Shields MODULE_LICENSE("GPL v2");
353