162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * LP5521/LP5523/LP55231/LP5562 Common Driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright 2012 Texas Instruments
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Author: Milo(Woogyom) Kim <milo.kim@ti.com>
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * Derived from leds-lp5521.c, leds-lp5523.c
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/clk.h>
1362306a36Sopenharmony_ci#include <linux/delay.h>
1462306a36Sopenharmony_ci#include <linux/firmware.h>
1562306a36Sopenharmony_ci#include <linux/i2c.h>
1662306a36Sopenharmony_ci#include <linux/leds.h>
1762306a36Sopenharmony_ci#include <linux/module.h>
1862306a36Sopenharmony_ci#include <linux/platform_data/leds-lp55xx.h>
1962306a36Sopenharmony_ci#include <linux/slab.h>
2062306a36Sopenharmony_ci#include <linux/gpio/consumer.h>
2162306a36Sopenharmony_ci#include <dt-bindings/leds/leds-lp55xx.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#include "leds-lp55xx-common.h"
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci/* External clock rate */
2662306a36Sopenharmony_ci#define LP55XX_CLK_32K			32768
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistatic struct lp55xx_led *cdev_to_lp55xx_led(struct led_classdev *cdev)
2962306a36Sopenharmony_ci{
3062306a36Sopenharmony_ci	return container_of(cdev, struct lp55xx_led, cdev);
3162306a36Sopenharmony_ci}
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic struct lp55xx_led *dev_to_lp55xx_led(struct device *dev)
3462306a36Sopenharmony_ci{
3562306a36Sopenharmony_ci	return cdev_to_lp55xx_led(dev_get_drvdata(dev));
3662306a36Sopenharmony_ci}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic struct lp55xx_led *mcled_cdev_to_led(struct led_classdev_mc *mc_cdev)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	return container_of(mc_cdev, struct lp55xx_led, mc_cdev);
4162306a36Sopenharmony_ci}
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic void lp55xx_reset_device(struct lp55xx_chip *chip)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	struct lp55xx_device_config *cfg = chip->cfg;
4662306a36Sopenharmony_ci	u8 addr = cfg->reset.addr;
4762306a36Sopenharmony_ci	u8 val  = cfg->reset.val;
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	/* no error checking here because no ACK from the device after reset */
5062306a36Sopenharmony_ci	lp55xx_write(chip, addr, val);
5162306a36Sopenharmony_ci}
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cistatic int lp55xx_detect_device(struct lp55xx_chip *chip)
5462306a36Sopenharmony_ci{
5562306a36Sopenharmony_ci	struct lp55xx_device_config *cfg = chip->cfg;
5662306a36Sopenharmony_ci	u8 addr = cfg->enable.addr;
5762306a36Sopenharmony_ci	u8 val  = cfg->enable.val;
5862306a36Sopenharmony_ci	int ret;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	ret = lp55xx_write(chip, addr, val);
6162306a36Sopenharmony_ci	if (ret)
6262306a36Sopenharmony_ci		return ret;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	usleep_range(1000, 2000);
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	ret = lp55xx_read(chip, addr, &val);
6762306a36Sopenharmony_ci	if (ret)
6862306a36Sopenharmony_ci		return ret;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	if (val != cfg->enable.val)
7162306a36Sopenharmony_ci		return -ENODEV;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	return 0;
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic int lp55xx_post_init_device(struct lp55xx_chip *chip)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	struct lp55xx_device_config *cfg = chip->cfg;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	if (!cfg->post_init_device)
8162306a36Sopenharmony_ci		return 0;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	return cfg->post_init_device(chip);
8462306a36Sopenharmony_ci}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cistatic ssize_t led_current_show(struct device *dev,
8762306a36Sopenharmony_ci			    struct device_attribute *attr,
8862306a36Sopenharmony_ci			    char *buf)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	struct lp55xx_led *led = dev_to_lp55xx_led(dev);
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	return sysfs_emit(buf, "%d\n", led->led_current);
9362306a36Sopenharmony_ci}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistatic ssize_t led_current_store(struct device *dev,
9662306a36Sopenharmony_ci			     struct device_attribute *attr,
9762306a36Sopenharmony_ci			     const char *buf, size_t len)
9862306a36Sopenharmony_ci{
9962306a36Sopenharmony_ci	struct lp55xx_led *led = dev_to_lp55xx_led(dev);
10062306a36Sopenharmony_ci	struct lp55xx_chip *chip = led->chip;
10162306a36Sopenharmony_ci	unsigned long curr;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	if (kstrtoul(buf, 0, &curr))
10462306a36Sopenharmony_ci		return -EINVAL;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	if (curr > led->max_current)
10762306a36Sopenharmony_ci		return -EINVAL;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	if (!chip->cfg->set_led_current)
11062306a36Sopenharmony_ci		return len;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	mutex_lock(&chip->lock);
11362306a36Sopenharmony_ci	chip->cfg->set_led_current(led, (u8)curr);
11462306a36Sopenharmony_ci	mutex_unlock(&chip->lock);
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	return len;
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_cistatic ssize_t max_current_show(struct device *dev,
12062306a36Sopenharmony_ci			    struct device_attribute *attr,
12162306a36Sopenharmony_ci			    char *buf)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	struct lp55xx_led *led = dev_to_lp55xx_led(dev);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	return sysfs_emit(buf, "%d\n", led->max_current);
12662306a36Sopenharmony_ci}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_cistatic DEVICE_ATTR_RW(led_current);
12962306a36Sopenharmony_cistatic DEVICE_ATTR_RO(max_current);
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_cistatic struct attribute *lp55xx_led_attrs[] = {
13262306a36Sopenharmony_ci	&dev_attr_led_current.attr,
13362306a36Sopenharmony_ci	&dev_attr_max_current.attr,
13462306a36Sopenharmony_ci	NULL,
13562306a36Sopenharmony_ci};
13662306a36Sopenharmony_ciATTRIBUTE_GROUPS(lp55xx_led);
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_cistatic int lp55xx_set_mc_brightness(struct led_classdev *cdev,
13962306a36Sopenharmony_ci				    enum led_brightness brightness)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	struct led_classdev_mc *mc_dev = lcdev_to_mccdev(cdev);
14262306a36Sopenharmony_ci	struct lp55xx_led *led = mcled_cdev_to_led(mc_dev);
14362306a36Sopenharmony_ci	struct lp55xx_device_config *cfg = led->chip->cfg;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	led_mc_calc_color_components(&led->mc_cdev, brightness);
14662306a36Sopenharmony_ci	return cfg->multicolor_brightness_fn(led);
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_cistatic int lp55xx_set_brightness(struct led_classdev *cdev,
15162306a36Sopenharmony_ci			     enum led_brightness brightness)
15262306a36Sopenharmony_ci{
15362306a36Sopenharmony_ci	struct lp55xx_led *led = cdev_to_lp55xx_led(cdev);
15462306a36Sopenharmony_ci	struct lp55xx_device_config *cfg = led->chip->cfg;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	led->brightness = (u8)brightness;
15762306a36Sopenharmony_ci	return cfg->brightness_fn(led);
15862306a36Sopenharmony_ci}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_cistatic int lp55xx_init_led(struct lp55xx_led *led,
16162306a36Sopenharmony_ci			struct lp55xx_chip *chip, int chan)
16262306a36Sopenharmony_ci{
16362306a36Sopenharmony_ci	struct lp55xx_platform_data *pdata = chip->pdata;
16462306a36Sopenharmony_ci	struct lp55xx_device_config *cfg = chip->cfg;
16562306a36Sopenharmony_ci	struct device *dev = &chip->cl->dev;
16662306a36Sopenharmony_ci	int max_channel = cfg->max_channel;
16762306a36Sopenharmony_ci	struct mc_subled *mc_led_info;
16862306a36Sopenharmony_ci	struct led_classdev *led_cdev;
16962306a36Sopenharmony_ci	char name[32];
17062306a36Sopenharmony_ci	int i;
17162306a36Sopenharmony_ci	int ret;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	if (chan >= max_channel) {
17462306a36Sopenharmony_ci		dev_err(dev, "invalid channel: %d / %d\n", chan, max_channel);
17562306a36Sopenharmony_ci		return -EINVAL;
17662306a36Sopenharmony_ci	}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	if (pdata->led_config[chan].led_current == 0)
17962306a36Sopenharmony_ci		return 0;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	if (pdata->led_config[chan].name) {
18262306a36Sopenharmony_ci		led->cdev.name = pdata->led_config[chan].name;
18362306a36Sopenharmony_ci	} else {
18462306a36Sopenharmony_ci		snprintf(name, sizeof(name), "%s:channel%d",
18562306a36Sopenharmony_ci			pdata->label ? : chip->cl->name, chan);
18662306a36Sopenharmony_ci		led->cdev.name = name;
18762306a36Sopenharmony_ci	}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	if (pdata->led_config[chan].num_colors > 1) {
19062306a36Sopenharmony_ci		mc_led_info = devm_kcalloc(dev,
19162306a36Sopenharmony_ci					   pdata->led_config[chan].num_colors,
19262306a36Sopenharmony_ci					   sizeof(*mc_led_info), GFP_KERNEL);
19362306a36Sopenharmony_ci		if (!mc_led_info)
19462306a36Sopenharmony_ci			return -ENOMEM;
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci		led_cdev = &led->mc_cdev.led_cdev;
19762306a36Sopenharmony_ci		led_cdev->name = led->cdev.name;
19862306a36Sopenharmony_ci		led_cdev->brightness_set_blocking = lp55xx_set_mc_brightness;
19962306a36Sopenharmony_ci		led->mc_cdev.num_colors = pdata->led_config[chan].num_colors;
20062306a36Sopenharmony_ci		for (i = 0; i < led->mc_cdev.num_colors; i++) {
20162306a36Sopenharmony_ci			mc_led_info[i].color_index =
20262306a36Sopenharmony_ci				pdata->led_config[chan].color_id[i];
20362306a36Sopenharmony_ci			mc_led_info[i].channel =
20462306a36Sopenharmony_ci					pdata->led_config[chan].output_num[i];
20562306a36Sopenharmony_ci		}
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci		led->mc_cdev.subled_info = mc_led_info;
20862306a36Sopenharmony_ci	} else {
20962306a36Sopenharmony_ci		led->cdev.brightness_set_blocking = lp55xx_set_brightness;
21062306a36Sopenharmony_ci	}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	led->cdev.groups = lp55xx_led_groups;
21362306a36Sopenharmony_ci	led->cdev.default_trigger = pdata->led_config[chan].default_trigger;
21462306a36Sopenharmony_ci	led->led_current = pdata->led_config[chan].led_current;
21562306a36Sopenharmony_ci	led->max_current = pdata->led_config[chan].max_current;
21662306a36Sopenharmony_ci	led->chan_nr = pdata->led_config[chan].chan_nr;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	if (led->chan_nr >= max_channel) {
21962306a36Sopenharmony_ci		dev_err(dev, "Use channel numbers between 0 and %d\n",
22062306a36Sopenharmony_ci			max_channel - 1);
22162306a36Sopenharmony_ci		return -EINVAL;
22262306a36Sopenharmony_ci	}
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	if (pdata->led_config[chan].num_colors > 1)
22562306a36Sopenharmony_ci		ret = devm_led_classdev_multicolor_register(dev, &led->mc_cdev);
22662306a36Sopenharmony_ci	else
22762306a36Sopenharmony_ci		ret = devm_led_classdev_register(dev, &led->cdev);
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	if (ret) {
23062306a36Sopenharmony_ci		dev_err(dev, "led register err: %d\n", ret);
23162306a36Sopenharmony_ci		return ret;
23262306a36Sopenharmony_ci	}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	return 0;
23562306a36Sopenharmony_ci}
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_cistatic void lp55xx_firmware_loaded(const struct firmware *fw, void *context)
23862306a36Sopenharmony_ci{
23962306a36Sopenharmony_ci	struct lp55xx_chip *chip = context;
24062306a36Sopenharmony_ci	struct device *dev = &chip->cl->dev;
24162306a36Sopenharmony_ci	enum lp55xx_engine_index idx = chip->engine_idx;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	if (!fw) {
24462306a36Sopenharmony_ci		dev_err(dev, "firmware request failed\n");
24562306a36Sopenharmony_ci		return;
24662306a36Sopenharmony_ci	}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	/* handling firmware data is chip dependent */
24962306a36Sopenharmony_ci	mutex_lock(&chip->lock);
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	chip->engines[idx - 1].mode = LP55XX_ENGINE_LOAD;
25262306a36Sopenharmony_ci	chip->fw = fw;
25362306a36Sopenharmony_ci	if (chip->cfg->firmware_cb)
25462306a36Sopenharmony_ci		chip->cfg->firmware_cb(chip);
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	mutex_unlock(&chip->lock);
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	/* firmware should be released for other channel use */
25962306a36Sopenharmony_ci	release_firmware(chip->fw);
26062306a36Sopenharmony_ci	chip->fw = NULL;
26162306a36Sopenharmony_ci}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_cistatic int lp55xx_request_firmware(struct lp55xx_chip *chip)
26462306a36Sopenharmony_ci{
26562306a36Sopenharmony_ci	const char *name = chip->cl->name;
26662306a36Sopenharmony_ci	struct device *dev = &chip->cl->dev;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	return request_firmware_nowait(THIS_MODULE, false, name, dev,
26962306a36Sopenharmony_ci				GFP_KERNEL, chip, lp55xx_firmware_loaded);
27062306a36Sopenharmony_ci}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_cistatic ssize_t select_engine_show(struct device *dev,
27362306a36Sopenharmony_ci			    struct device_attribute *attr,
27462306a36Sopenharmony_ci			    char *buf)
27562306a36Sopenharmony_ci{
27662306a36Sopenharmony_ci	struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
27762306a36Sopenharmony_ci	struct lp55xx_chip *chip = led->chip;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	return sprintf(buf, "%d\n", chip->engine_idx);
28062306a36Sopenharmony_ci}
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_cistatic ssize_t select_engine_store(struct device *dev,
28362306a36Sopenharmony_ci			     struct device_attribute *attr,
28462306a36Sopenharmony_ci			     const char *buf, size_t len)
28562306a36Sopenharmony_ci{
28662306a36Sopenharmony_ci	struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
28762306a36Sopenharmony_ci	struct lp55xx_chip *chip = led->chip;
28862306a36Sopenharmony_ci	unsigned long val;
28962306a36Sopenharmony_ci	int ret;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	if (kstrtoul(buf, 0, &val))
29262306a36Sopenharmony_ci		return -EINVAL;
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	/* select the engine to be run */
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	switch (val) {
29762306a36Sopenharmony_ci	case LP55XX_ENGINE_1:
29862306a36Sopenharmony_ci	case LP55XX_ENGINE_2:
29962306a36Sopenharmony_ci	case LP55XX_ENGINE_3:
30062306a36Sopenharmony_ci		mutex_lock(&chip->lock);
30162306a36Sopenharmony_ci		chip->engine_idx = val;
30262306a36Sopenharmony_ci		ret = lp55xx_request_firmware(chip);
30362306a36Sopenharmony_ci		mutex_unlock(&chip->lock);
30462306a36Sopenharmony_ci		break;
30562306a36Sopenharmony_ci	default:
30662306a36Sopenharmony_ci		dev_err(dev, "%lu: invalid engine index. (1, 2, 3)\n", val);
30762306a36Sopenharmony_ci		return -EINVAL;
30862306a36Sopenharmony_ci	}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	if (ret) {
31162306a36Sopenharmony_ci		dev_err(dev, "request firmware err: %d\n", ret);
31262306a36Sopenharmony_ci		return ret;
31362306a36Sopenharmony_ci	}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	return len;
31662306a36Sopenharmony_ci}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_cistatic inline void lp55xx_run_engine(struct lp55xx_chip *chip, bool start)
31962306a36Sopenharmony_ci{
32062306a36Sopenharmony_ci	if (chip->cfg->run_engine)
32162306a36Sopenharmony_ci		chip->cfg->run_engine(chip, start);
32262306a36Sopenharmony_ci}
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_cistatic ssize_t run_engine_store(struct device *dev,
32562306a36Sopenharmony_ci			     struct device_attribute *attr,
32662306a36Sopenharmony_ci			     const char *buf, size_t len)
32762306a36Sopenharmony_ci{
32862306a36Sopenharmony_ci	struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
32962306a36Sopenharmony_ci	struct lp55xx_chip *chip = led->chip;
33062306a36Sopenharmony_ci	unsigned long val;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	if (kstrtoul(buf, 0, &val))
33362306a36Sopenharmony_ci		return -EINVAL;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	/* run or stop the selected engine */
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	if (val <= 0) {
33862306a36Sopenharmony_ci		lp55xx_run_engine(chip, false);
33962306a36Sopenharmony_ci		return len;
34062306a36Sopenharmony_ci	}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	mutex_lock(&chip->lock);
34362306a36Sopenharmony_ci	lp55xx_run_engine(chip, true);
34462306a36Sopenharmony_ci	mutex_unlock(&chip->lock);
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	return len;
34762306a36Sopenharmony_ci}
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_cistatic DEVICE_ATTR_RW(select_engine);
35062306a36Sopenharmony_cistatic DEVICE_ATTR_WO(run_engine);
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_cistatic struct attribute *lp55xx_engine_attributes[] = {
35362306a36Sopenharmony_ci	&dev_attr_select_engine.attr,
35462306a36Sopenharmony_ci	&dev_attr_run_engine.attr,
35562306a36Sopenharmony_ci	NULL,
35662306a36Sopenharmony_ci};
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_cistatic const struct attribute_group lp55xx_engine_attr_group = {
35962306a36Sopenharmony_ci	.attrs = lp55xx_engine_attributes,
36062306a36Sopenharmony_ci};
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ciint lp55xx_write(struct lp55xx_chip *chip, u8 reg, u8 val)
36362306a36Sopenharmony_ci{
36462306a36Sopenharmony_ci	return i2c_smbus_write_byte_data(chip->cl, reg, val);
36562306a36Sopenharmony_ci}
36662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(lp55xx_write);
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ciint lp55xx_read(struct lp55xx_chip *chip, u8 reg, u8 *val)
36962306a36Sopenharmony_ci{
37062306a36Sopenharmony_ci	s32 ret;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	ret = i2c_smbus_read_byte_data(chip->cl, reg);
37362306a36Sopenharmony_ci	if (ret < 0)
37462306a36Sopenharmony_ci		return ret;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	*val = ret;
37762306a36Sopenharmony_ci	return 0;
37862306a36Sopenharmony_ci}
37962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(lp55xx_read);
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ciint lp55xx_update_bits(struct lp55xx_chip *chip, u8 reg, u8 mask, u8 val)
38262306a36Sopenharmony_ci{
38362306a36Sopenharmony_ci	int ret;
38462306a36Sopenharmony_ci	u8 tmp;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	ret = lp55xx_read(chip, reg, &tmp);
38762306a36Sopenharmony_ci	if (ret)
38862306a36Sopenharmony_ci		return ret;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	tmp &= ~mask;
39162306a36Sopenharmony_ci	tmp |= val & mask;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	return lp55xx_write(chip, reg, tmp);
39462306a36Sopenharmony_ci}
39562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(lp55xx_update_bits);
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_cibool lp55xx_is_extclk_used(struct lp55xx_chip *chip)
39862306a36Sopenharmony_ci{
39962306a36Sopenharmony_ci	struct clk *clk;
40062306a36Sopenharmony_ci	int err;
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	clk = devm_clk_get(&chip->cl->dev, "32k_clk");
40362306a36Sopenharmony_ci	if (IS_ERR(clk))
40462306a36Sopenharmony_ci		goto use_internal_clk;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	err = clk_prepare_enable(clk);
40762306a36Sopenharmony_ci	if (err)
40862306a36Sopenharmony_ci		goto use_internal_clk;
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	if (clk_get_rate(clk) != LP55XX_CLK_32K) {
41162306a36Sopenharmony_ci		clk_disable_unprepare(clk);
41262306a36Sopenharmony_ci		goto use_internal_clk;
41362306a36Sopenharmony_ci	}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	dev_info(&chip->cl->dev, "%dHz external clock used\n",	LP55XX_CLK_32K);
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	chip->clk = clk;
41862306a36Sopenharmony_ci	return true;
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ciuse_internal_clk:
42162306a36Sopenharmony_ci	dev_info(&chip->cl->dev, "internal clock used\n");
42262306a36Sopenharmony_ci	return false;
42362306a36Sopenharmony_ci}
42462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(lp55xx_is_extclk_used);
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ciint lp55xx_init_device(struct lp55xx_chip *chip)
42762306a36Sopenharmony_ci{
42862306a36Sopenharmony_ci	struct lp55xx_platform_data *pdata;
42962306a36Sopenharmony_ci	struct lp55xx_device_config *cfg;
43062306a36Sopenharmony_ci	struct device *dev = &chip->cl->dev;
43162306a36Sopenharmony_ci	int ret = 0;
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	WARN_ON(!chip);
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	pdata = chip->pdata;
43662306a36Sopenharmony_ci	cfg = chip->cfg;
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	if (!pdata || !cfg)
43962306a36Sopenharmony_ci		return -EINVAL;
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	if (pdata->enable_gpiod) {
44262306a36Sopenharmony_ci		gpiod_direction_output(pdata->enable_gpiod, 0);
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci		gpiod_set_consumer_name(pdata->enable_gpiod, "LP55xx enable");
44562306a36Sopenharmony_ci		gpiod_set_value(pdata->enable_gpiod, 0);
44662306a36Sopenharmony_ci		usleep_range(1000, 2000); /* Keep enable down at least 1ms */
44762306a36Sopenharmony_ci		gpiod_set_value(pdata->enable_gpiod, 1);
44862306a36Sopenharmony_ci		usleep_range(1000, 2000); /* 500us abs min. */
44962306a36Sopenharmony_ci	}
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	lp55xx_reset_device(chip);
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	/*
45462306a36Sopenharmony_ci	 * Exact value is not available. 10 - 20ms
45562306a36Sopenharmony_ci	 * appears to be enough for reset.
45662306a36Sopenharmony_ci	 */
45762306a36Sopenharmony_ci	usleep_range(10000, 20000);
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	ret = lp55xx_detect_device(chip);
46062306a36Sopenharmony_ci	if (ret) {
46162306a36Sopenharmony_ci		dev_err(dev, "device detection err: %d\n", ret);
46262306a36Sopenharmony_ci		goto err;
46362306a36Sopenharmony_ci	}
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	/* chip specific initialization */
46662306a36Sopenharmony_ci	ret = lp55xx_post_init_device(chip);
46762306a36Sopenharmony_ci	if (ret) {
46862306a36Sopenharmony_ci		dev_err(dev, "post init device err: %d\n", ret);
46962306a36Sopenharmony_ci		goto err_post_init;
47062306a36Sopenharmony_ci	}
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	return 0;
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_cierr_post_init:
47562306a36Sopenharmony_ci	lp55xx_deinit_device(chip);
47662306a36Sopenharmony_cierr:
47762306a36Sopenharmony_ci	return ret;
47862306a36Sopenharmony_ci}
47962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(lp55xx_init_device);
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_civoid lp55xx_deinit_device(struct lp55xx_chip *chip)
48262306a36Sopenharmony_ci{
48362306a36Sopenharmony_ci	struct lp55xx_platform_data *pdata = chip->pdata;
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	if (chip->clk)
48662306a36Sopenharmony_ci		clk_disable_unprepare(chip->clk);
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	if (pdata->enable_gpiod)
48962306a36Sopenharmony_ci		gpiod_set_value(pdata->enable_gpiod, 0);
49062306a36Sopenharmony_ci}
49162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(lp55xx_deinit_device);
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ciint lp55xx_register_leds(struct lp55xx_led *led, struct lp55xx_chip *chip)
49462306a36Sopenharmony_ci{
49562306a36Sopenharmony_ci	struct lp55xx_platform_data *pdata = chip->pdata;
49662306a36Sopenharmony_ci	struct lp55xx_device_config *cfg = chip->cfg;
49762306a36Sopenharmony_ci	int num_channels = pdata->num_channels;
49862306a36Sopenharmony_ci	struct lp55xx_led *each;
49962306a36Sopenharmony_ci	u8 led_current;
50062306a36Sopenharmony_ci	int ret;
50162306a36Sopenharmony_ci	int i;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	if (!cfg->brightness_fn) {
50462306a36Sopenharmony_ci		dev_err(&chip->cl->dev, "empty brightness configuration\n");
50562306a36Sopenharmony_ci		return -EINVAL;
50662306a36Sopenharmony_ci	}
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	for (i = 0; i < num_channels; i++) {
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci		/* do not initialize channels that are not connected */
51162306a36Sopenharmony_ci		if (pdata->led_config[i].led_current == 0)
51262306a36Sopenharmony_ci			continue;
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci		led_current = pdata->led_config[i].led_current;
51562306a36Sopenharmony_ci		each = led + i;
51662306a36Sopenharmony_ci		ret = lp55xx_init_led(each, chip, i);
51762306a36Sopenharmony_ci		if (ret)
51862306a36Sopenharmony_ci			goto err_init_led;
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci		chip->num_leds++;
52162306a36Sopenharmony_ci		each->chip = chip;
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci		/* setting led current at each channel */
52462306a36Sopenharmony_ci		if (cfg->set_led_current)
52562306a36Sopenharmony_ci			cfg->set_led_current(each, led_current);
52662306a36Sopenharmony_ci	}
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	return 0;
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_cierr_init_led:
53162306a36Sopenharmony_ci	return ret;
53262306a36Sopenharmony_ci}
53362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(lp55xx_register_leds);
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ciint lp55xx_register_sysfs(struct lp55xx_chip *chip)
53662306a36Sopenharmony_ci{
53762306a36Sopenharmony_ci	struct device *dev = &chip->cl->dev;
53862306a36Sopenharmony_ci	struct lp55xx_device_config *cfg = chip->cfg;
53962306a36Sopenharmony_ci	int ret;
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	if (!cfg->run_engine || !cfg->firmware_cb)
54262306a36Sopenharmony_ci		goto dev_specific_attrs;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	ret = sysfs_create_group(&dev->kobj, &lp55xx_engine_attr_group);
54562306a36Sopenharmony_ci	if (ret)
54662306a36Sopenharmony_ci		return ret;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_cidev_specific_attrs:
54962306a36Sopenharmony_ci	return cfg->dev_attr_group ?
55062306a36Sopenharmony_ci		sysfs_create_group(&dev->kobj, cfg->dev_attr_group) : 0;
55162306a36Sopenharmony_ci}
55262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(lp55xx_register_sysfs);
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_civoid lp55xx_unregister_sysfs(struct lp55xx_chip *chip)
55562306a36Sopenharmony_ci{
55662306a36Sopenharmony_ci	struct device *dev = &chip->cl->dev;
55762306a36Sopenharmony_ci	struct lp55xx_device_config *cfg = chip->cfg;
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	if (cfg->dev_attr_group)
56062306a36Sopenharmony_ci		sysfs_remove_group(&dev->kobj, cfg->dev_attr_group);
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	sysfs_remove_group(&dev->kobj, &lp55xx_engine_attr_group);
56362306a36Sopenharmony_ci}
56462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(lp55xx_unregister_sysfs);
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_cistatic int lp55xx_parse_common_child(struct device_node *np,
56762306a36Sopenharmony_ci				     struct lp55xx_led_config *cfg,
56862306a36Sopenharmony_ci				     int led_number, int *chan_nr)
56962306a36Sopenharmony_ci{
57062306a36Sopenharmony_ci	int ret;
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	of_property_read_string(np, "chan-name",
57362306a36Sopenharmony_ci				&cfg[led_number].name);
57462306a36Sopenharmony_ci	of_property_read_u8(np, "led-cur",
57562306a36Sopenharmony_ci			    &cfg[led_number].led_current);
57662306a36Sopenharmony_ci	of_property_read_u8(np, "max-cur",
57762306a36Sopenharmony_ci			    &cfg[led_number].max_current);
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	ret = of_property_read_u32(np, "reg", chan_nr);
58062306a36Sopenharmony_ci	if (ret)
58162306a36Sopenharmony_ci		return ret;
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	if (*chan_nr < 0 || *chan_nr > cfg->max_channel)
58462306a36Sopenharmony_ci		return -EINVAL;
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	return 0;
58762306a36Sopenharmony_ci}
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_cistatic int lp55xx_parse_multi_led_child(struct device_node *child,
59062306a36Sopenharmony_ci					 struct lp55xx_led_config *cfg,
59162306a36Sopenharmony_ci					 int child_number, int color_number)
59262306a36Sopenharmony_ci{
59362306a36Sopenharmony_ci	int chan_nr, color_id, ret;
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	ret = lp55xx_parse_common_child(child, cfg, child_number, &chan_nr);
59662306a36Sopenharmony_ci	if (ret)
59762306a36Sopenharmony_ci		return ret;
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	ret = of_property_read_u32(child, "color", &color_id);
60062306a36Sopenharmony_ci	if (ret)
60162306a36Sopenharmony_ci		return ret;
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	cfg[child_number].color_id[color_number] = color_id;
60462306a36Sopenharmony_ci	cfg[child_number].output_num[color_number] = chan_nr;
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	return 0;
60762306a36Sopenharmony_ci}
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_cistatic int lp55xx_parse_multi_led(struct device_node *np,
61062306a36Sopenharmony_ci				  struct lp55xx_led_config *cfg,
61162306a36Sopenharmony_ci				  int child_number)
61262306a36Sopenharmony_ci{
61362306a36Sopenharmony_ci	struct device_node *child;
61462306a36Sopenharmony_ci	int num_colors = 0, ret;
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	for_each_available_child_of_node(np, child) {
61762306a36Sopenharmony_ci		ret = lp55xx_parse_multi_led_child(child, cfg, child_number,
61862306a36Sopenharmony_ci						   num_colors);
61962306a36Sopenharmony_ci		if (ret) {
62062306a36Sopenharmony_ci			of_node_put(child);
62162306a36Sopenharmony_ci			return ret;
62262306a36Sopenharmony_ci		}
62362306a36Sopenharmony_ci		num_colors++;
62462306a36Sopenharmony_ci	}
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	cfg[child_number].num_colors = num_colors;
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	return 0;
62962306a36Sopenharmony_ci}
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_cistatic int lp55xx_parse_logical_led(struct device_node *np,
63262306a36Sopenharmony_ci				   struct lp55xx_led_config *cfg,
63362306a36Sopenharmony_ci				   int child_number)
63462306a36Sopenharmony_ci{
63562306a36Sopenharmony_ci	int led_color, ret;
63662306a36Sopenharmony_ci	int chan_nr = 0;
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	cfg[child_number].default_trigger =
63962306a36Sopenharmony_ci		of_get_property(np, "linux,default-trigger", NULL);
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	ret = of_property_read_u32(np, "color", &led_color);
64262306a36Sopenharmony_ci	if (ret)
64362306a36Sopenharmony_ci		return ret;
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	if (led_color == LED_COLOR_ID_RGB)
64662306a36Sopenharmony_ci		return lp55xx_parse_multi_led(np, cfg, child_number);
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci	ret =  lp55xx_parse_common_child(np, cfg, child_number, &chan_nr);
64962306a36Sopenharmony_ci	if (ret < 0)
65062306a36Sopenharmony_ci		return ret;
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci	cfg[child_number].chan_nr = chan_nr;
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	return ret;
65562306a36Sopenharmony_ci}
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_cistruct lp55xx_platform_data *lp55xx_of_populate_pdata(struct device *dev,
65862306a36Sopenharmony_ci						      struct device_node *np,
65962306a36Sopenharmony_ci						      struct lp55xx_chip *chip)
66062306a36Sopenharmony_ci{
66162306a36Sopenharmony_ci	struct device_node *child;
66262306a36Sopenharmony_ci	struct lp55xx_platform_data *pdata;
66362306a36Sopenharmony_ci	struct lp55xx_led_config *cfg;
66462306a36Sopenharmony_ci	int num_channels;
66562306a36Sopenharmony_ci	int i = 0;
66662306a36Sopenharmony_ci	int ret;
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
66962306a36Sopenharmony_ci	if (!pdata)
67062306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	num_channels = of_get_available_child_count(np);
67362306a36Sopenharmony_ci	if (num_channels == 0) {
67462306a36Sopenharmony_ci		dev_err(dev, "no LED channels\n");
67562306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
67662306a36Sopenharmony_ci	}
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	cfg = devm_kcalloc(dev, num_channels, sizeof(*cfg), GFP_KERNEL);
67962306a36Sopenharmony_ci	if (!cfg)
68062306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	pdata->led_config = &cfg[0];
68362306a36Sopenharmony_ci	pdata->num_channels = num_channels;
68462306a36Sopenharmony_ci	cfg->max_channel = chip->cfg->max_channel;
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	for_each_available_child_of_node(np, child) {
68762306a36Sopenharmony_ci		ret = lp55xx_parse_logical_led(child, cfg, i);
68862306a36Sopenharmony_ci		if (ret) {
68962306a36Sopenharmony_ci			of_node_put(child);
69062306a36Sopenharmony_ci			return ERR_PTR(-EINVAL);
69162306a36Sopenharmony_ci		}
69262306a36Sopenharmony_ci		i++;
69362306a36Sopenharmony_ci	}
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	if (of_property_read_u32(np, "ti,charge-pump-mode", &pdata->charge_pump_mode))
69662306a36Sopenharmony_ci		pdata->charge_pump_mode = LP55XX_CP_AUTO;
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	if (pdata->charge_pump_mode > LP55XX_CP_AUTO) {
69962306a36Sopenharmony_ci		dev_err(dev, "invalid charge pump mode %d\n", pdata->charge_pump_mode);
70062306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
70162306a36Sopenharmony_ci	}
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	of_property_read_string(np, "label", &pdata->label);
70462306a36Sopenharmony_ci	of_property_read_u8(np, "clock-mode", &pdata->clock_mode);
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	pdata->enable_gpiod = devm_gpiod_get_optional(dev, "enable",
70762306a36Sopenharmony_ci						      GPIOD_ASIS);
70862306a36Sopenharmony_ci	if (IS_ERR(pdata->enable_gpiod))
70962306a36Sopenharmony_ci		return ERR_CAST(pdata->enable_gpiod);
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	/* LP8501 specific */
71262306a36Sopenharmony_ci	of_property_read_u8(np, "pwr-sel", (u8 *)&pdata->pwr_sel);
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	return pdata;
71562306a36Sopenharmony_ci}
71662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(lp55xx_of_populate_pdata);
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ciMODULE_AUTHOR("Milo Kim <milo.kim@ti.com>");
71962306a36Sopenharmony_ciMODULE_DESCRIPTION("LP55xx Common Driver");
72062306a36Sopenharmony_ciMODULE_LICENSE("GPL");
721