brintos

brintos / linux-shallow public Read only

0
0
Text · 13.9 KiB · ddac836 Raw
540 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Silergy SY7802 flash LED driver with an I2C interface4 *5 * Copyright 2024 André Apitzsch <git@apitzsch.eu>6 */7 8#include <linux/gpio/consumer.h>9#include <linux/i2c.h>10#include <linux/kernel.h>11#include <linux/led-class-flash.h>12#include <linux/module.h>13#include <linux/mutex.h>14#include <linux/regmap.h>15#include <linux/regulator/consumer.h>16 17#define SY7802_MAX_LEDS 218#define SY7802_LED_JOINT 219 20#define SY7802_REG_ENABLE		0x1021#define SY7802_REG_TORCH_BRIGHTNESS	0xa022#define SY7802_REG_FLASH_BRIGHTNESS	0xb023#define SY7802_REG_FLASH_DURATION	0xc024#define SY7802_REG_FLAGS		0xd025#define SY7802_REG_CONFIG_1		0xe026#define SY7802_REG_CONFIG_2		0xf027#define SY7802_REG_VIN_MONITOR		0x8028#define SY7802_REG_LAST_FLASH		0x8129#define SY7802_REG_VLED_MONITOR		0x3030#define SY7802_REG_ADC_DELAY		0x3131#define SY7802_REG_DEV_ID		0xff32 33#define SY7802_MODE_OFF		034#define SY7802_MODE_TORCH	235#define SY7802_MODE_FLASH	336#define SY7802_MODE_MASK	GENMASK(1, 0)37 38#define SY7802_LEDS_SHIFT	339#define SY7802_LEDS_MASK(_id)	(BIT(_id) << SY7802_LEDS_SHIFT)40#define SY7802_LEDS_MASK_ALL	(SY7802_LEDS_MASK(0) | SY7802_LEDS_MASK(1))41 42#define SY7802_TORCH_CURRENT_SHIFT	343#define SY7802_TORCH_CURRENT_MASK(_id) \44	(GENMASK(2, 0) << (SY7802_TORCH_CURRENT_SHIFT * (_id)))45#define SY7802_TORCH_CURRENT_MASK_ALL \46	(SY7802_TORCH_CURRENT_MASK(0) | SY7802_TORCH_CURRENT_MASK(1))47 48#define SY7802_FLASH_CURRENT_SHIFT	449#define SY7802_FLASH_CURRENT_MASK(_id) \50	(GENMASK(3, 0) << (SY7802_FLASH_CURRENT_SHIFT * (_id)))51#define SY7802_FLASH_CURRENT_MASK_ALL \52	(SY7802_FLASH_CURRENT_MASK(0) | SY7802_FLASH_CURRENT_MASK(1))53 54#define SY7802_TIMEOUT_DEFAULT_US	512000U55#define SY7802_TIMEOUT_MIN_US		32000U56#define SY7802_TIMEOUT_MAX_US		1024000U57#define SY7802_TIMEOUT_STEPSIZE_US	32000U58 59#define SY7802_TORCH_BRIGHTNESS_MAX 860 61#define SY7802_FLASH_BRIGHTNESS_DEFAULT	1462#define SY7802_FLASH_BRIGHTNESS_MIN	063#define SY7802_FLASH_BRIGHTNESS_MAX	1564#define SY7802_FLASH_BRIGHTNESS_STEP	165 66#define SY7802_FLAG_TIMEOUT			BIT(0)67#define SY7802_FLAG_THERMAL_SHUTDOWN		BIT(1)68#define SY7802_FLAG_LED_FAULT			BIT(2)69#define SY7802_FLAG_TX1_INTERRUPT		BIT(3)70#define SY7802_FLAG_TX2_INTERRUPT		BIT(4)71#define SY7802_FLAG_LED_THERMAL_FAULT		BIT(5)72#define SY7802_FLAG_FLASH_INPUT_VOLTAGE_LOW	BIT(6)73#define SY7802_FLAG_INPUT_VOLTAGE_LOW		BIT(7)74 75#define SY7802_CHIP_ID	0x5176 77static const struct reg_default sy7802_regmap_defs[] = {78	{ SY7802_REG_ENABLE, SY7802_LEDS_MASK_ALL },79	{ SY7802_REG_TORCH_BRIGHTNESS, 0x92 },80	{ SY7802_REG_FLASH_BRIGHTNESS, SY7802_FLASH_BRIGHTNESS_DEFAULT |81		SY7802_FLASH_BRIGHTNESS_DEFAULT << SY7802_FLASH_CURRENT_SHIFT },82	{ SY7802_REG_FLASH_DURATION, 0x6f },83	{ SY7802_REG_FLAGS, 0x0 },84	{ SY7802_REG_CONFIG_1, 0x68 },85	{ SY7802_REG_CONFIG_2, 0xf0 },86};87 88struct sy7802_led {89	struct led_classdev_flash flash;90	struct sy7802 *chip;91	u8 led_id;92};93 94struct sy7802 {95	struct device *dev;96	struct regmap *regmap;97	struct mutex mutex;98 99	struct gpio_desc *enable_gpio;100	struct regulator *vin_regulator;101 102	unsigned int fled_strobe_used;103	unsigned int fled_torch_used;104	unsigned int leds_active;105	int num_leds;106	struct sy7802_led leds[] __counted_by(num_leds);107};108 109static int sy7802_torch_brightness_set(struct led_classdev *lcdev, enum led_brightness brightness)110{111	struct sy7802_led *led = container_of(lcdev, struct sy7802_led, flash.led_cdev);112	struct sy7802 *chip = led->chip;113	u32 fled_torch_used_tmp;114	u32 led_enable_mask;115	u32 enable_mask;116	u32 torch_mask;117	u32 val;118	int ret;119 120	mutex_lock(&chip->mutex);121 122	if (chip->fled_strobe_used) {123		dev_warn(chip->dev, "Cannot set torch brightness whilst strobe is enabled\n");124		ret = -EBUSY;125		goto unlock;126	}127 128	if (brightness)129		fled_torch_used_tmp = chip->fled_torch_used | BIT(led->led_id);130	else131		fled_torch_used_tmp = chip->fled_torch_used & ~BIT(led->led_id);132 133	led_enable_mask = led->led_id == SY7802_LED_JOINT ?134			  SY7802_LEDS_MASK_ALL :135			  SY7802_LEDS_MASK(led->led_id);136 137	val = brightness ? led_enable_mask : SY7802_MODE_OFF;138	if (fled_torch_used_tmp)139		val |= SY7802_MODE_TORCH;140 141	/* Disable torch to apply brightness */142	ret = regmap_update_bits(chip->regmap, SY7802_REG_ENABLE, SY7802_MODE_MASK,143				 SY7802_MODE_OFF);144	if (ret)145		goto unlock;146 147	torch_mask = led->led_id == SY7802_LED_JOINT ?148		     SY7802_TORCH_CURRENT_MASK_ALL :149		     SY7802_TORCH_CURRENT_MASK(led->led_id);150 151	/* Register expects brightness between 0 and MAX_BRIGHTNESS - 1 */152	if (brightness)153		brightness -= 1;154 155	brightness |= (brightness << SY7802_TORCH_CURRENT_SHIFT);156 157	ret = regmap_update_bits(chip->regmap, SY7802_REG_TORCH_BRIGHTNESS, torch_mask, brightness);158	if (ret)159		goto unlock;160 161	enable_mask = SY7802_MODE_MASK | led_enable_mask;162	ret = regmap_update_bits(chip->regmap, SY7802_REG_ENABLE, enable_mask, val);163	if (ret)164		goto unlock;165 166	chip->fled_torch_used = fled_torch_used_tmp;167 168unlock:169	mutex_unlock(&chip->mutex);170	return ret;171}172 173static int sy7802_flash_brightness_set(struct led_classdev_flash *fl_cdev, u32 brightness)174{175	struct sy7802_led *led = container_of(fl_cdev, struct sy7802_led, flash);176	struct led_flash_setting *s = &fl_cdev->brightness;177	u32 val = (brightness - s->min) / s->step;178	struct sy7802 *chip = led->chip;179	u32 flash_mask;180	int ret;181 182	val |= (val << SY7802_FLASH_CURRENT_SHIFT);183	flash_mask = led->led_id == SY7802_LED_JOINT ?184		     SY7802_FLASH_CURRENT_MASK_ALL :185		     SY7802_FLASH_CURRENT_MASK(led->led_id);186 187	mutex_lock(&chip->mutex);188	ret = regmap_update_bits(chip->regmap, SY7802_REG_FLASH_BRIGHTNESS, flash_mask, val);189	mutex_unlock(&chip->mutex);190 191	return ret;192}193 194static int sy7802_strobe_set(struct led_classdev_flash *fl_cdev, bool state)195{196	struct sy7802_led *led = container_of(fl_cdev, struct sy7802_led, flash);197	struct sy7802 *chip = led->chip;198	u32 fled_strobe_used_tmp;199	u32 led_enable_mask;200	u32 enable_mask;201	u32 val;202	int ret;203 204	mutex_lock(&chip->mutex);205 206	if (chip->fled_torch_used) {207		dev_warn(chip->dev, "Cannot set strobe brightness whilst torch is enabled\n");208		ret = -EBUSY;209		goto unlock;210	}211 212	if (state)213		fled_strobe_used_tmp = chip->fled_strobe_used | BIT(led->led_id);214	else215		fled_strobe_used_tmp = chip->fled_strobe_used & ~BIT(led->led_id);216 217	led_enable_mask = led->led_id == SY7802_LED_JOINT ?218			  SY7802_LEDS_MASK_ALL :219			  SY7802_LEDS_MASK(led->led_id);220 221	val = state ? led_enable_mask : SY7802_MODE_OFF;222	if (fled_strobe_used_tmp)223		val |= SY7802_MODE_FLASH;224 225	enable_mask = SY7802_MODE_MASK | led_enable_mask;226	ret = regmap_update_bits(chip->regmap, SY7802_REG_ENABLE, enable_mask, val);227 228	if (ret)229		goto unlock;230 231	chip->fled_strobe_used = fled_strobe_used_tmp;232 233unlock:234	mutex_unlock(&chip->mutex);235	return ret;236}237 238static int sy7802_strobe_get(struct led_classdev_flash *fl_cdev, bool *state)239{240	struct sy7802_led *led = container_of(fl_cdev, struct sy7802_led, flash);241	struct sy7802 *chip = led->chip;242 243	mutex_lock(&chip->mutex);244	*state = !!(chip->fled_strobe_used & BIT(led->led_id));245	mutex_unlock(&chip->mutex);246 247	return 0;248}249 250static int sy7802_timeout_set(struct led_classdev_flash *fl_cdev, u32 timeout)251{252	struct sy7802_led *led = container_of(fl_cdev, struct sy7802_led, flash);253	struct led_flash_setting *s = &fl_cdev->timeout;254	u32 val = (timeout - s->min) / s->step;255	struct sy7802 *chip = led->chip;256 257	return regmap_write(chip->regmap, SY7802_REG_FLASH_DURATION, val);258}259 260static int sy7802_fault_get(struct led_classdev_flash *fl_cdev, u32 *fault)261{262	struct sy7802_led *led = container_of(fl_cdev, struct sy7802_led, flash);263	struct sy7802 *chip = led->chip;264	u32 val, led_faults = 0;265	int ret;266 267	/* NOTE: reading register clears fault status */268	ret = regmap_read(chip->regmap, SY7802_REG_FLAGS, &val);269	if (ret)270		return ret;271 272	if (val & (SY7802_FLAG_FLASH_INPUT_VOLTAGE_LOW | SY7802_FLAG_INPUT_VOLTAGE_LOW))273		led_faults |= LED_FAULT_INPUT_VOLTAGE;274 275	if (val & SY7802_FLAG_THERMAL_SHUTDOWN)276		led_faults |= LED_FAULT_OVER_TEMPERATURE;277 278	if (val & SY7802_FLAG_TIMEOUT)279		led_faults |= LED_FAULT_TIMEOUT;280 281	*fault = led_faults;282	return 0;283}284 285static const struct led_flash_ops sy7802_flash_ops = {286	.flash_brightness_set = sy7802_flash_brightness_set,287	.strobe_set = sy7802_strobe_set,288	.strobe_get = sy7802_strobe_get,289	.timeout_set = sy7802_timeout_set,290	.fault_get = sy7802_fault_get,291};292 293static void sy7802_init_flash_brightness(struct led_classdev_flash *fl_cdev)294{295	struct led_flash_setting *s;296 297	/* Init flash brightness setting */298	s = &fl_cdev->brightness;299	s->min = SY7802_FLASH_BRIGHTNESS_MIN;300	s->max = SY7802_FLASH_BRIGHTNESS_MAX;301	s->step = SY7802_FLASH_BRIGHTNESS_STEP;302	s->val = SY7802_FLASH_BRIGHTNESS_DEFAULT;303}304 305static void sy7802_init_flash_timeout(struct led_classdev_flash *fl_cdev)306{307	struct led_flash_setting *s;308 309	/* Init flash timeout setting */310	s = &fl_cdev->timeout;311	s->min = SY7802_TIMEOUT_MIN_US;312	s->max = SY7802_TIMEOUT_MAX_US;313	s->step = SY7802_TIMEOUT_STEPSIZE_US;314	s->val = SY7802_TIMEOUT_DEFAULT_US;315}316 317static int sy7802_led_register(struct device *dev, struct sy7802_led *led,318			       struct device_node *np)319{320	struct led_init_data init_data = {};321	int ret;322 323	init_data.fwnode = of_fwnode_handle(np);324 325	ret = devm_led_classdev_flash_register_ext(dev, &led->flash, &init_data);326	if (ret) {327		dev_err(dev, "Couldn't register flash %d\n", led->led_id);328		return ret;329	}330 331	return 0;332}333 334static int sy7802_init_flash_properties(struct device *dev, struct sy7802_led *led,335					struct device_node *np)336{337	struct led_classdev_flash *flash = &led->flash;338	struct led_classdev *lcdev = &flash->led_cdev;339	u32 sources[SY7802_MAX_LEDS];340	int i, num, ret;341 342	num = of_property_count_u32_elems(np, "led-sources");343	if (num < 1) {344		dev_err(dev, "Not specified or wrong number of led-sources\n");345		return -EINVAL;346	}347 348	ret = of_property_read_u32_array(np, "led-sources", sources, num);349	if (ret)350		return ret;351 352	for (i = 0; i < num; i++) {353		if (sources[i] >= SY7802_MAX_LEDS)354			return -EINVAL;355		if (led->chip->leds_active & BIT(sources[i]))356			return -EINVAL;357		led->chip->leds_active |= BIT(sources[i]);358	}359 360	/* If both channels are specified in 'led-sources', joint flash output mode is used */361	led->led_id = num == 2 ? SY7802_LED_JOINT : sources[0];362 363	lcdev->max_brightness = SY7802_TORCH_BRIGHTNESS_MAX;364	lcdev->brightness_set_blocking = sy7802_torch_brightness_set;365	lcdev->flags |= LED_DEV_CAP_FLASH;366 367	flash->ops = &sy7802_flash_ops;368 369	sy7802_init_flash_brightness(flash);370	sy7802_init_flash_timeout(flash);371 372	return 0;373}374 375static int sy7802_chip_check(struct sy7802 *chip)376{377	struct device *dev = chip->dev;378	u32 chipid;379	int ret;380 381	ret = regmap_read(chip->regmap, SY7802_REG_DEV_ID, &chipid);382	if (ret)383		return dev_err_probe(dev, ret, "Failed to read chip ID\n");384 385	if (chipid != SY7802_CHIP_ID)386		return dev_err_probe(dev, -ENODEV, "Unsupported chip detected: %x\n", chipid);387 388	return 0;389}390 391static void sy7802_enable(struct sy7802 *chip)392{393	gpiod_set_value_cansleep(chip->enable_gpio, 1);394	usleep_range(200, 300);395}396 397static void sy7802_disable(struct sy7802 *chip)398{399	gpiod_set_value_cansleep(chip->enable_gpio, 0);400}401 402static int sy7802_probe_dt(struct sy7802 *chip)403{404	struct device_node *np = dev_of_node(chip->dev);405	int child_num;406	int ret;407 408	regmap_write(chip->regmap, SY7802_REG_ENABLE, SY7802_MODE_OFF);409	regmap_write(chip->regmap, SY7802_REG_TORCH_BRIGHTNESS, LED_OFF);410 411	child_num = 0;412	for_each_available_child_of_node_scoped(np, child) {413		struct sy7802_led *led = chip->leds + child_num;414 415		led->chip = chip;416		led->led_id = child_num;417 418		ret = sy7802_init_flash_properties(chip->dev, led, child);419		if (ret)420			return ret;421 422		ret = sy7802_led_register(chip->dev, led, child);423		if (ret)424			return ret;425 426		child_num++;427	}428	return 0;429}430 431static void sy7802_chip_disable_action(void *data)432{433	struct sy7802 *chip = data;434 435	sy7802_disable(chip);436}437 438static void sy7802_regulator_disable_action(void *data)439{440	struct sy7802 *chip = data;441 442	regulator_disable(chip->vin_regulator);443}444 445static const struct regmap_config sy7802_regmap_config = {446	.reg_bits = 8,447	.val_bits = 8,448	.max_register = 0xff,449	.cache_type = REGCACHE_MAPLE,450	.reg_defaults = sy7802_regmap_defs,451	.num_reg_defaults = ARRAY_SIZE(sy7802_regmap_defs),452};453 454static int sy7802_probe(struct i2c_client *client)455{456	struct device *dev = &client->dev;457	struct sy7802 *chip;458	size_t count;459	int ret;460 461	count = device_get_child_node_count(dev);462	if (!count || count > SY7802_MAX_LEDS)463		return dev_err_probe(dev, -EINVAL, "Invalid amount of LED nodes %zu\n", count);464 465	chip = devm_kzalloc(dev, struct_size(chip, leds, count), GFP_KERNEL);466	if (!chip)467		return -ENOMEM;468 469	chip->num_leds = count;470 471	chip->dev = dev;472	i2c_set_clientdata(client, chip);473 474	chip->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);475	ret = PTR_ERR_OR_ZERO(chip->enable_gpio);476	if (ret)477		return dev_err_probe(dev, ret, "Failed to request enable gpio\n");478 479	chip->vin_regulator = devm_regulator_get(dev, "vin");480	ret = PTR_ERR_OR_ZERO(chip->vin_regulator);481	if (ret)482		return dev_err_probe(dev, ret, "Failed to request regulator\n");483 484	ret = regulator_enable(chip->vin_regulator);485	if (ret)486		return dev_err_probe(dev, ret, "Failed to enable regulator\n");487 488	ret = devm_add_action_or_reset(dev, sy7802_regulator_disable_action, chip);489	if (ret)490		return ret;491 492	ret = devm_mutex_init(dev, &chip->mutex);493	if (ret)494		return ret;495 496	mutex_lock(&chip->mutex);497 498	chip->regmap = devm_regmap_init_i2c(client, &sy7802_regmap_config);499	if (IS_ERR(chip->regmap)) {500		ret = PTR_ERR(chip->regmap);501		dev_err_probe(dev, ret, "Failed to allocate register map\n");502		goto error;503	}504 505	ret = sy7802_probe_dt(chip);506	if (ret < 0)507		goto error;508 509	sy7802_enable(chip);510 511	ret = devm_add_action_or_reset(dev, sy7802_chip_disable_action, chip);512	if (ret)513		goto error;514 515	ret = sy7802_chip_check(chip);516 517error:518	mutex_unlock(&chip->mutex);519	return ret;520}521 522static const struct of_device_id __maybe_unused sy7802_leds_match[] = {523	{ .compatible = "silergy,sy7802", },524	{}525};526MODULE_DEVICE_TABLE(of, sy7802_leds_match);527 528static struct i2c_driver sy7802_driver = {529	.driver = {530		.name = "sy7802",531		.of_match_table = of_match_ptr(sy7802_leds_match),532	},533	.probe = sy7802_probe,534};535module_i2c_driver(sy7802_driver);536 537MODULE_AUTHOR("André Apitzsch <git@apitzsch.eu>");538MODULE_DESCRIPTION("Silergy SY7802 flash LED driver");539MODULE_LICENSE("GPL");540