brintos

brintos / linux-shallow public Read only

0
0
Text · 12.2 KiB · abf6b96 Raw
495 lines · c
1// SPDX-License-Identifier: GPL-2.02// Flash and torch driver for Texas Instruments LM3601X LED3// Flash driver chip family4// Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/5 6#include <linux/delay.h>7#include <linux/i2c.h>8#include <linux/leds.h>9#include <linux/led-class-flash.h>10#include <linux/module.h>11#include <linux/regmap.h>12#include <linux/slab.h>13 14#define LM3601X_LED_IR		0x015#define LM3601X_LED_TORCH	0x116 17/* Registers */18#define LM3601X_ENABLE_REG	0x0119#define LM3601X_CFG_REG		0x0220#define LM3601X_LED_FLASH_REG	0x0321#define LM3601X_LED_TORCH_REG	0x0422#define LM3601X_FLAGS_REG	0x0523#define LM3601X_DEV_ID_REG	0x0624 25#define LM3601X_SW_RESET	BIT(7)26 27/* Enable Mode bits */28#define LM3601X_MODE_STANDBY	0x0029#define LM3601X_MODE_IR_DRV	BIT(0)30#define LM3601X_MODE_TORCH	BIT(1)31#define LM3601X_MODE_STROBE	(BIT(0) | BIT(1))32#define LM3601X_STRB_EN		BIT(2)33#define LM3601X_STRB_EDGE_TRIG	BIT(3)34#define LM3601X_IVFM_EN		BIT(4)35 36#define LM36010_BOOST_LIMIT_28	BIT(5)37#define LM36010_BOOST_FREQ_4MHZ	BIT(6)38#define LM36010_BOOST_MODE_PASS	BIT(7)39 40/* Flag Mask */41#define LM3601X_FLASH_TIME_OUT	BIT(0)42#define LM3601X_UVLO_FAULT	BIT(1)43#define LM3601X_THERM_SHUTDOWN	BIT(2)44#define LM3601X_THERM_CURR	BIT(3)45#define LM36010_CURR_LIMIT	BIT(4)46#define LM3601X_SHORT_FAULT	BIT(5)47#define LM3601X_IVFM_TRIP	BIT(6)48#define LM36010_OVP_FAULT	BIT(7)49 50#define LM3601X_MAX_TORCH_I_UA	37600051#define LM3601X_MIN_TORCH_I_UA	240052#define LM3601X_TORCH_REG_DIV	296553 54#define LM3601X_MAX_STROBE_I_UA	150000055#define LM3601X_MIN_STROBE_I_UA	1100056#define LM3601X_STROBE_REG_DIV	1180057 58#define LM3601X_TIMEOUT_MASK	0x1e59#define LM3601X_ENABLE_MASK	(LM3601X_MODE_IR_DRV | LM3601X_MODE_TORCH)60 61#define LM3601X_LOWER_STEP_US	4000062#define LM3601X_UPPER_STEP_US	20000063#define LM3601X_MIN_TIMEOUT_US	4000064#define LM3601X_MAX_TIMEOUT_US	160000065#define LM3601X_TIMEOUT_XOVER_US 40000066 67enum lm3601x_type {68	CHIP_LM36010 = 0,69	CHIP_LM36011,70};71 72/**73 * struct lm3601x_led - private lm3601x LED data74 * @fled_cdev: flash LED class device pointer75 * @client: Pointer to the I2C client76 * @regmap: Devices register map77 * @lock: Lock for reading/writing the device78 * @flash_timeout: the timeout for the flash79 * @last_flag: last known flags register value80 * @torch_current_max: maximum current for the torch81 * @flash_current_max: maximum current for the flash82 * @max_flash_timeout: maximum timeout for the flash83 * @led_mode: The mode to enable either IR or Torch84 */85struct lm3601x_led {86	struct led_classdev_flash fled_cdev;87	struct i2c_client *client;88	struct regmap *regmap;89	struct mutex lock;90 91	unsigned int flash_timeout;92	unsigned int last_flag;93 94	u32 torch_current_max;95	u32 flash_current_max;96	u32 max_flash_timeout;97 98	u32 led_mode;99};100 101static const struct reg_default lm3601x_regmap_defs[] = {102	{ LM3601X_ENABLE_REG, 0x20 },103	{ LM3601X_CFG_REG, 0x15 },104	{ LM3601X_LED_FLASH_REG, 0x00 },105	{ LM3601X_LED_TORCH_REG, 0x00 },106};107 108static bool lm3601x_volatile_reg(struct device *dev, unsigned int reg)109{110	switch (reg) {111	case LM3601X_FLAGS_REG:112		return true;113	default:114		return false;115	}116}117 118static const struct regmap_config lm3601x_regmap = {119	.reg_bits = 8,120	.val_bits = 8,121 122	.max_register = LM3601X_DEV_ID_REG,123	.reg_defaults = lm3601x_regmap_defs,124	.num_reg_defaults = ARRAY_SIZE(lm3601x_regmap_defs),125	.cache_type = REGCACHE_MAPLE,126	.volatile_reg = lm3601x_volatile_reg,127};128 129static struct lm3601x_led *fled_cdev_to_led(struct led_classdev_flash *fled_cdev)130{131	return container_of(fled_cdev, struct lm3601x_led, fled_cdev);132}133 134static int lm3601x_read_faults(struct lm3601x_led *led)135{136	int flags_val;137	int ret;138 139	ret = regmap_read(led->regmap, LM3601X_FLAGS_REG, &flags_val);140	if (ret < 0)141		return -EIO;142 143	led->last_flag = 0;144 145	if (flags_val & LM36010_OVP_FAULT)146		led->last_flag |= LED_FAULT_OVER_VOLTAGE;147 148	if (flags_val & (LM3601X_THERM_SHUTDOWN | LM3601X_THERM_CURR))149		led->last_flag |= LED_FAULT_OVER_TEMPERATURE;150 151	if (flags_val & LM3601X_SHORT_FAULT)152		led->last_flag |= LED_FAULT_SHORT_CIRCUIT;153 154	if (flags_val & LM36010_CURR_LIMIT)155		led->last_flag |= LED_FAULT_OVER_CURRENT;156 157	if (flags_val & LM3601X_UVLO_FAULT)158		led->last_flag |= LED_FAULT_UNDER_VOLTAGE;159 160	if (flags_val & LM3601X_IVFM_TRIP)161		led->last_flag |= LED_FAULT_INPUT_VOLTAGE;162 163	if (flags_val & LM3601X_THERM_SHUTDOWN)164		led->last_flag |= LED_FAULT_LED_OVER_TEMPERATURE;165 166	return led->last_flag;167}168 169static int lm3601x_brightness_set(struct led_classdev *cdev,170					enum led_brightness brightness)171{172	struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(cdev);173	struct lm3601x_led *led = fled_cdev_to_led(fled_cdev);174	int ret, led_mode_val;175 176	mutex_lock(&led->lock);177 178	ret = lm3601x_read_faults(led);179	if (ret < 0)180		goto out;181 182	if (led->led_mode == LM3601X_LED_TORCH)183		led_mode_val = LM3601X_MODE_TORCH;184	else185		led_mode_val = LM3601X_MODE_IR_DRV;186 187	if (brightness == LED_OFF) {188		ret = regmap_update_bits(led->regmap, LM3601X_ENABLE_REG,189					led_mode_val, LED_OFF);190		goto out;191	}192 193	ret = regmap_write(led->regmap, LM3601X_LED_TORCH_REG, brightness - 1);194	if (ret < 0)195		goto out;196 197	ret = regmap_update_bits(led->regmap, LM3601X_ENABLE_REG,198				LM3601X_MODE_TORCH | LM3601X_MODE_IR_DRV,199				led_mode_val);200out:201	mutex_unlock(&led->lock);202	return ret;203}204 205static int lm3601x_strobe_set(struct led_classdev_flash *fled_cdev,206				bool state)207{208	struct lm3601x_led *led = fled_cdev_to_led(fled_cdev);209	int timeout_reg_val;210	int current_timeout;211	int ret;212 213	mutex_lock(&led->lock);214 215	ret = regmap_read(led->regmap, LM3601X_CFG_REG, &current_timeout);216	if (ret < 0)217		goto out;218 219	if (led->flash_timeout >= LM3601X_TIMEOUT_XOVER_US)220		timeout_reg_val = led->flash_timeout / LM3601X_UPPER_STEP_US + 0x07;221	else222		timeout_reg_val = led->flash_timeout / LM3601X_LOWER_STEP_US - 0x01;223 224	if (led->flash_timeout != current_timeout)225		ret = regmap_update_bits(led->regmap, LM3601X_CFG_REG,226					LM3601X_TIMEOUT_MASK, timeout_reg_val);227 228	if (state)229		ret = regmap_update_bits(led->regmap, LM3601X_ENABLE_REG,230					LM3601X_MODE_TORCH | LM3601X_MODE_IR_DRV,231					LM3601X_MODE_STROBE);232	else233		ret = regmap_update_bits(led->regmap, LM3601X_ENABLE_REG,234					LM3601X_MODE_STROBE, LED_OFF);235 236	ret = lm3601x_read_faults(led);237out:238	mutex_unlock(&led->lock);239	return ret;240}241 242static int lm3601x_flash_brightness_set(struct led_classdev_flash *fled_cdev,243					u32 brightness)244{245	struct lm3601x_led *led = fled_cdev_to_led(fled_cdev);246	u8 brightness_val;247	int ret;248 249	mutex_lock(&led->lock);250	ret = lm3601x_read_faults(led);251	if (ret < 0)252		goto out;253 254	if (brightness == LED_OFF) {255		ret = regmap_update_bits(led->regmap, LM3601X_ENABLE_REG,256					LM3601X_MODE_STROBE, LED_OFF);257		goto out;258	}259 260	brightness_val = brightness / LM3601X_STROBE_REG_DIV;261 262	ret = regmap_write(led->regmap, LM3601X_LED_FLASH_REG, brightness_val);263out:264	mutex_unlock(&led->lock);265	return ret;266}267 268static int lm3601x_flash_timeout_set(struct led_classdev_flash *fled_cdev,269				u32 timeout)270{271	struct lm3601x_led *led = fled_cdev_to_led(fled_cdev);272 273	mutex_lock(&led->lock);274 275	led->flash_timeout = timeout;276 277	mutex_unlock(&led->lock);278 279	return 0;280}281 282static int lm3601x_strobe_get(struct led_classdev_flash *fled_cdev, bool *state)283{284	struct lm3601x_led *led = fled_cdev_to_led(fled_cdev);285	int strobe_state;286	int ret;287 288	mutex_lock(&led->lock);289 290	ret = regmap_read(led->regmap, LM3601X_ENABLE_REG, &strobe_state);291	if (ret < 0)292		goto out;293 294	*state = strobe_state & LM3601X_MODE_STROBE;295 296out:297	mutex_unlock(&led->lock);298	return ret;299}300 301static int lm3601x_flash_fault_get(struct led_classdev_flash *fled_cdev,302				u32 *fault)303{304	struct lm3601x_led *led = fled_cdev_to_led(fled_cdev);305 306	lm3601x_read_faults(led);307 308	*fault = led->last_flag;309 310	return 0;311}312 313static const struct led_flash_ops flash_ops = {314	.flash_brightness_set	= lm3601x_flash_brightness_set,315	.strobe_set		= lm3601x_strobe_set,316	.strobe_get		= lm3601x_strobe_get,317	.timeout_set		= lm3601x_flash_timeout_set,318	.fault_get		= lm3601x_flash_fault_get,319};320 321static int lm3601x_register_leds(struct lm3601x_led *led,322				 struct fwnode_handle *fwnode)323{324	struct led_classdev *led_cdev;325	struct led_flash_setting *setting;326	struct led_init_data init_data = {};327 328	led->fled_cdev.ops = &flash_ops;329 330	setting = &led->fled_cdev.timeout;331	setting->min = LM3601X_MIN_TIMEOUT_US;332	setting->max = led->max_flash_timeout;333	setting->step = LM3601X_LOWER_STEP_US;334	setting->val = led->max_flash_timeout;335 336	setting = &led->fled_cdev.brightness;337	setting->min = LM3601X_MIN_STROBE_I_UA;338	setting->max = led->flash_current_max;339	setting->step = LM3601X_TORCH_REG_DIV;340	setting->val = led->flash_current_max;341 342	led_cdev = &led->fled_cdev.led_cdev;343	led_cdev->brightness_set_blocking = lm3601x_brightness_set;344	led_cdev->max_brightness =345			DIV_ROUND_UP(led->torch_current_max - LM3601X_MIN_TORCH_I_UA + 1,346				     LM3601X_TORCH_REG_DIV);347	led_cdev->flags |= LED_DEV_CAP_FLASH;348 349	init_data.fwnode = fwnode;350	init_data.devicename = led->client->name;351	init_data.default_label = (led->led_mode == LM3601X_LED_TORCH) ?352					"torch" : "infrared";353	return devm_led_classdev_flash_register_ext(&led->client->dev,354						&led->fled_cdev, &init_data);355}356 357static int lm3601x_parse_node(struct lm3601x_led *led,358			      struct fwnode_handle **fwnode)359{360	struct fwnode_handle *child = NULL;361	int ret = -ENODEV;362 363	child = device_get_next_child_node(&led->client->dev, child);364	if (!child) {365		dev_err(&led->client->dev, "No LED Child node\n");366		return ret;367	}368 369	ret = fwnode_property_read_u32(child, "reg", &led->led_mode);370	if (ret) {371		dev_err(&led->client->dev, "reg DT property missing\n");372		goto out_err;373	}374 375	if (led->led_mode > LM3601X_LED_TORCH ||376	    led->led_mode < LM3601X_LED_IR) {377		dev_warn(&led->client->dev, "Invalid led mode requested\n");378		ret = -EINVAL;379		goto out_err;380	}381 382	ret = fwnode_property_read_u32(child, "led-max-microamp",383					&led->torch_current_max);384	if (ret) {385		dev_warn(&led->client->dev,386			"led-max-microamp DT property missing\n");387		goto out_err;388	}389 390	if (led->torch_current_max > LM3601X_MAX_TORCH_I_UA) {391		dev_warn(&led->client->dev,392			 "Max torch current set too high (%d vs %d)\n",393			 led->torch_current_max,394			 LM3601X_MAX_TORCH_I_UA);395		led->torch_current_max = LM3601X_MAX_TORCH_I_UA;396	}397 398	ret = fwnode_property_read_u32(child, "flash-max-microamp",399				&led->flash_current_max);400	if (ret) {401		dev_warn(&led->client->dev,402			 "flash-max-microamp DT property missing\n");403		goto out_err;404	}405 406	ret = fwnode_property_read_u32(child, "flash-max-timeout-us",407				&led->max_flash_timeout);408	if (ret) {409		dev_warn(&led->client->dev,410			 "flash-max-timeout-us DT property missing\n");411		goto out_err;412	}413 414	*fwnode = child;415 416out_err:417	fwnode_handle_put(child);418	return ret;419}420 421static int lm3601x_probe(struct i2c_client *client)422{423	struct lm3601x_led *led;424	struct fwnode_handle *fwnode;425	int ret;426 427	led = devm_kzalloc(&client->dev, sizeof(*led), GFP_KERNEL);428	if (!led)429		return -ENOMEM;430 431	led->client = client;432	i2c_set_clientdata(client, led);433 434	ret = lm3601x_parse_node(led, &fwnode);435	if (ret)436		return -ENODEV;437 438	led->regmap = devm_regmap_init_i2c(client, &lm3601x_regmap);439	if (IS_ERR(led->regmap)) {440		ret = PTR_ERR(led->regmap);441		dev_err(&client->dev,442			"Failed to allocate register map: %d\n", ret);443		return ret;444	}445 446	ret = regmap_write(led->regmap, LM3601X_DEV_ID_REG, LM3601X_SW_RESET);447	if (ret)448		dev_warn(&client->dev, "Failed to reset the LED controller\n");449 450	mutex_init(&led->lock);451 452	return lm3601x_register_leds(led, fwnode);453}454 455static void lm3601x_remove(struct i2c_client *client)456{457	struct lm3601x_led *led = i2c_get_clientdata(client);458	int ret;459 460	ret = regmap_update_bits(led->regmap, LM3601X_ENABLE_REG,461				 LM3601X_ENABLE_MASK, LM3601X_MODE_STANDBY);462	if (ret)463		dev_warn(&client->dev,464			 "Failed to put into standby (%pe)\n", ERR_PTR(ret));465}466 467static const struct i2c_device_id lm3601x_id[] = {468	{ "LM36010", CHIP_LM36010 },469	{ "LM36011", CHIP_LM36011 },470	{ }471};472MODULE_DEVICE_TABLE(i2c, lm3601x_id);473 474static const struct of_device_id of_lm3601x_leds_match[] = {475	{ .compatible = "ti,lm36010", },476	{ .compatible = "ti,lm36011", },477	{ }478};479MODULE_DEVICE_TABLE(of, of_lm3601x_leds_match);480 481static struct i2c_driver lm3601x_i2c_driver = {482	.driver = {483		.name = "lm3601x",484		.of_match_table = of_lm3601x_leds_match,485	},486	.probe = lm3601x_probe,487	.remove = lm3601x_remove,488	.id_table = lm3601x_id,489};490module_i2c_driver(lm3601x_i2c_driver);491 492MODULE_DESCRIPTION("Texas Instruments Flash Lighting driver for LM3601X");493MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");494MODULE_LICENSE("GPL v2");495