161fa67a4SLinus Walleij // SPDX-License-Identifier: GPL-2.0
261fa67a4SLinus Walleij // Flash and torch driver for Texas Instruments LM3601X LED
361fa67a4SLinus Walleij // Flash driver chip family
461fa67a4SLinus Walleij // Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/
561fa67a4SLinus Walleij
661fa67a4SLinus Walleij #include <linux/delay.h>
761fa67a4SLinus Walleij #include <linux/i2c.h>
861fa67a4SLinus Walleij #include <linux/leds.h>
961fa67a4SLinus Walleij #include <linux/led-class-flash.h>
1061fa67a4SLinus Walleij #include <linux/module.h>
1161fa67a4SLinus Walleij #include <linux/regmap.h>
1261fa67a4SLinus Walleij #include <linux/slab.h>
1361fa67a4SLinus Walleij
1461fa67a4SLinus Walleij #define LM3601X_LED_IR 0x0
1561fa67a4SLinus Walleij #define LM3601X_LED_TORCH 0x1
1661fa67a4SLinus Walleij
1761fa67a4SLinus Walleij /* Registers */
1861fa67a4SLinus Walleij #define LM3601X_ENABLE_REG 0x01
1961fa67a4SLinus Walleij #define LM3601X_CFG_REG 0x02
2061fa67a4SLinus Walleij #define LM3601X_LED_FLASH_REG 0x03
2161fa67a4SLinus Walleij #define LM3601X_LED_TORCH_REG 0x04
2261fa67a4SLinus Walleij #define LM3601X_FLAGS_REG 0x05
2361fa67a4SLinus Walleij #define LM3601X_DEV_ID_REG 0x06
2461fa67a4SLinus Walleij
2561fa67a4SLinus Walleij #define LM3601X_SW_RESET BIT(7)
2661fa67a4SLinus Walleij
2761fa67a4SLinus Walleij /* Enable Mode bits */
2861fa67a4SLinus Walleij #define LM3601X_MODE_STANDBY 0x00
2961fa67a4SLinus Walleij #define LM3601X_MODE_IR_DRV BIT(0)
3061fa67a4SLinus Walleij #define LM3601X_MODE_TORCH BIT(1)
3161fa67a4SLinus Walleij #define LM3601X_MODE_STROBE (BIT(0) | BIT(1))
3261fa67a4SLinus Walleij #define LM3601X_STRB_EN BIT(2)
3361fa67a4SLinus Walleij #define LM3601X_STRB_EDGE_TRIG BIT(3)
3461fa67a4SLinus Walleij #define LM3601X_IVFM_EN BIT(4)
3561fa67a4SLinus Walleij
3661fa67a4SLinus Walleij #define LM36010_BOOST_LIMIT_28 BIT(5)
3761fa67a4SLinus Walleij #define LM36010_BOOST_FREQ_4MHZ BIT(6)
3861fa67a4SLinus Walleij #define LM36010_BOOST_MODE_PASS BIT(7)
3961fa67a4SLinus Walleij
4061fa67a4SLinus Walleij /* Flag Mask */
4161fa67a4SLinus Walleij #define LM3601X_FLASH_TIME_OUT BIT(0)
4261fa67a4SLinus Walleij #define LM3601X_UVLO_FAULT BIT(1)
4361fa67a4SLinus Walleij #define LM3601X_THERM_SHUTDOWN BIT(2)
4461fa67a4SLinus Walleij #define LM3601X_THERM_CURR BIT(3)
4561fa67a4SLinus Walleij #define LM36010_CURR_LIMIT BIT(4)
4661fa67a4SLinus Walleij #define LM3601X_SHORT_FAULT BIT(5)
4761fa67a4SLinus Walleij #define LM3601X_IVFM_TRIP BIT(6)
4861fa67a4SLinus Walleij #define LM36010_OVP_FAULT BIT(7)
4961fa67a4SLinus Walleij
5061fa67a4SLinus Walleij #define LM3601X_MAX_TORCH_I_UA 376000
5161fa67a4SLinus Walleij #define LM3601X_MIN_TORCH_I_UA 2400
5261fa67a4SLinus Walleij #define LM3601X_TORCH_REG_DIV 2965
5361fa67a4SLinus Walleij
5461fa67a4SLinus Walleij #define LM3601X_MAX_STROBE_I_UA 1500000
5561fa67a4SLinus Walleij #define LM3601X_MIN_STROBE_I_UA 11000
5661fa67a4SLinus Walleij #define LM3601X_STROBE_REG_DIV 11800
5761fa67a4SLinus Walleij
5861fa67a4SLinus Walleij #define LM3601X_TIMEOUT_MASK 0x1e
5961fa67a4SLinus Walleij #define LM3601X_ENABLE_MASK (LM3601X_MODE_IR_DRV | LM3601X_MODE_TORCH)
6061fa67a4SLinus Walleij
6161fa67a4SLinus Walleij #define LM3601X_LOWER_STEP_US 40000
6261fa67a4SLinus Walleij #define LM3601X_UPPER_STEP_US 200000
6361fa67a4SLinus Walleij #define LM3601X_MIN_TIMEOUT_US 40000
6461fa67a4SLinus Walleij #define LM3601X_MAX_TIMEOUT_US 1600000
6561fa67a4SLinus Walleij #define LM3601X_TIMEOUT_XOVER_US 400000
6661fa67a4SLinus Walleij
6761fa67a4SLinus Walleij enum lm3601x_type {
6861fa67a4SLinus Walleij CHIP_LM36010 = 0,
6961fa67a4SLinus Walleij CHIP_LM36011,
7061fa67a4SLinus Walleij };
7161fa67a4SLinus Walleij
7261fa67a4SLinus Walleij /**
7361fa67a4SLinus Walleij * struct lm3601x_led -
7461fa67a4SLinus Walleij * @fled_cdev: flash LED class device pointer
7561fa67a4SLinus Walleij * @client: Pointer to the I2C client
7661fa67a4SLinus Walleij * @regmap: Devices register map
7761fa67a4SLinus Walleij * @lock: Lock for reading/writing the device
7861fa67a4SLinus Walleij * @led_name: LED label for the Torch or IR LED
7961fa67a4SLinus Walleij * @flash_timeout: the timeout for the flash
8061fa67a4SLinus Walleij * @last_flag: last known flags register value
8161fa67a4SLinus Walleij * @torch_current_max: maximum current for the torch
8261fa67a4SLinus Walleij * @flash_current_max: maximum current for the flash
8361fa67a4SLinus Walleij * @max_flash_timeout: maximum timeout for the flash
8461fa67a4SLinus Walleij * @led_mode: The mode to enable either IR or Torch
8561fa67a4SLinus Walleij */
8661fa67a4SLinus Walleij struct lm3601x_led {
8761fa67a4SLinus Walleij struct led_classdev_flash fled_cdev;
8861fa67a4SLinus Walleij struct i2c_client *client;
8961fa67a4SLinus Walleij struct regmap *regmap;
9061fa67a4SLinus Walleij struct mutex lock;
9161fa67a4SLinus Walleij
9261fa67a4SLinus Walleij unsigned int flash_timeout;
9361fa67a4SLinus Walleij unsigned int last_flag;
9461fa67a4SLinus Walleij
9561fa67a4SLinus Walleij u32 torch_current_max;
9661fa67a4SLinus Walleij u32 flash_current_max;
9761fa67a4SLinus Walleij u32 max_flash_timeout;
9861fa67a4SLinus Walleij
9961fa67a4SLinus Walleij u32 led_mode;
10061fa67a4SLinus Walleij };
10161fa67a4SLinus Walleij
10261fa67a4SLinus Walleij static const struct reg_default lm3601x_regmap_defs[] = {
10361fa67a4SLinus Walleij { LM3601X_ENABLE_REG, 0x20 },
10461fa67a4SLinus Walleij { LM3601X_CFG_REG, 0x15 },
10561fa67a4SLinus Walleij { LM3601X_LED_FLASH_REG, 0x00 },
10661fa67a4SLinus Walleij { LM3601X_LED_TORCH_REG, 0x00 },
10761fa67a4SLinus Walleij };
10861fa67a4SLinus Walleij
lm3601x_volatile_reg(struct device * dev,unsigned int reg)10961fa67a4SLinus Walleij static bool lm3601x_volatile_reg(struct device *dev, unsigned int reg)
11061fa67a4SLinus Walleij {
11161fa67a4SLinus Walleij switch (reg) {
11261fa67a4SLinus Walleij case LM3601X_FLAGS_REG:
11361fa67a4SLinus Walleij return true;
11461fa67a4SLinus Walleij default:
11561fa67a4SLinus Walleij return false;
11661fa67a4SLinus Walleij }
11761fa67a4SLinus Walleij }
11861fa67a4SLinus Walleij
11961fa67a4SLinus Walleij static const struct regmap_config lm3601x_regmap = {
12061fa67a4SLinus Walleij .reg_bits = 8,
12161fa67a4SLinus Walleij .val_bits = 8,
12261fa67a4SLinus Walleij
12361fa67a4SLinus Walleij .max_register = LM3601X_DEV_ID_REG,
12461fa67a4SLinus Walleij .reg_defaults = lm3601x_regmap_defs,
12561fa67a4SLinus Walleij .num_reg_defaults = ARRAY_SIZE(lm3601x_regmap_defs),
12661fa67a4SLinus Walleij .cache_type = REGCACHE_RBTREE,
12761fa67a4SLinus Walleij .volatile_reg = lm3601x_volatile_reg,
12861fa67a4SLinus Walleij };
12961fa67a4SLinus Walleij
fled_cdev_to_led(struct led_classdev_flash * fled_cdev)13061fa67a4SLinus Walleij static struct lm3601x_led *fled_cdev_to_led(struct led_classdev_flash *fled_cdev)
13161fa67a4SLinus Walleij {
13261fa67a4SLinus Walleij return container_of(fled_cdev, struct lm3601x_led, fled_cdev);
13361fa67a4SLinus Walleij }
13461fa67a4SLinus Walleij
lm3601x_read_faults(struct lm3601x_led * led)13561fa67a4SLinus Walleij static int lm3601x_read_faults(struct lm3601x_led *led)
13661fa67a4SLinus Walleij {
13761fa67a4SLinus Walleij int flags_val;
13861fa67a4SLinus Walleij int ret;
13961fa67a4SLinus Walleij
14061fa67a4SLinus Walleij ret = regmap_read(led->regmap, LM3601X_FLAGS_REG, &flags_val);
14161fa67a4SLinus Walleij if (ret < 0)
14261fa67a4SLinus Walleij return -EIO;
14361fa67a4SLinus Walleij
14461fa67a4SLinus Walleij led->last_flag = 0;
14561fa67a4SLinus Walleij
14661fa67a4SLinus Walleij if (flags_val & LM36010_OVP_FAULT)
14761fa67a4SLinus Walleij led->last_flag |= LED_FAULT_OVER_VOLTAGE;
14861fa67a4SLinus Walleij
14961fa67a4SLinus Walleij if (flags_val & (LM3601X_THERM_SHUTDOWN | LM3601X_THERM_CURR))
15061fa67a4SLinus Walleij led->last_flag |= LED_FAULT_OVER_TEMPERATURE;
15161fa67a4SLinus Walleij
15261fa67a4SLinus Walleij if (flags_val & LM3601X_SHORT_FAULT)
15361fa67a4SLinus Walleij led->last_flag |= LED_FAULT_SHORT_CIRCUIT;
15461fa67a4SLinus Walleij
15561fa67a4SLinus Walleij if (flags_val & LM36010_CURR_LIMIT)
15661fa67a4SLinus Walleij led->last_flag |= LED_FAULT_OVER_CURRENT;
15761fa67a4SLinus Walleij
15861fa67a4SLinus Walleij if (flags_val & LM3601X_UVLO_FAULT)
15961fa67a4SLinus Walleij led->last_flag |= LED_FAULT_UNDER_VOLTAGE;
16061fa67a4SLinus Walleij
16161fa67a4SLinus Walleij if (flags_val & LM3601X_IVFM_TRIP)
16261fa67a4SLinus Walleij led->last_flag |= LED_FAULT_INPUT_VOLTAGE;
16361fa67a4SLinus Walleij
16461fa67a4SLinus Walleij if (flags_val & LM3601X_THERM_SHUTDOWN)
16561fa67a4SLinus Walleij led->last_flag |= LED_FAULT_LED_OVER_TEMPERATURE;
16661fa67a4SLinus Walleij
16761fa67a4SLinus Walleij return led->last_flag;
16861fa67a4SLinus Walleij }
16961fa67a4SLinus Walleij
lm3601x_brightness_set(struct led_classdev * cdev,enum led_brightness brightness)17061fa67a4SLinus Walleij static int lm3601x_brightness_set(struct led_classdev *cdev,
17161fa67a4SLinus Walleij enum led_brightness brightness)
17261fa67a4SLinus Walleij {
17361fa67a4SLinus Walleij struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(cdev);
17461fa67a4SLinus Walleij struct lm3601x_led *led = fled_cdev_to_led(fled_cdev);
17561fa67a4SLinus Walleij int ret, led_mode_val;
17661fa67a4SLinus Walleij
17761fa67a4SLinus Walleij mutex_lock(&led->lock);
17861fa67a4SLinus Walleij
17961fa67a4SLinus Walleij ret = lm3601x_read_faults(led);
18061fa67a4SLinus Walleij if (ret < 0)
18161fa67a4SLinus Walleij goto out;
18261fa67a4SLinus Walleij
18361fa67a4SLinus Walleij if (led->led_mode == LM3601X_LED_TORCH)
18461fa67a4SLinus Walleij led_mode_val = LM3601X_MODE_TORCH;
18561fa67a4SLinus Walleij else
18661fa67a4SLinus Walleij led_mode_val = LM3601X_MODE_IR_DRV;
18761fa67a4SLinus Walleij
18861fa67a4SLinus Walleij if (brightness == LED_OFF) {
18961fa67a4SLinus Walleij ret = regmap_update_bits(led->regmap, LM3601X_ENABLE_REG,
19061fa67a4SLinus Walleij led_mode_val, LED_OFF);
19161fa67a4SLinus Walleij goto out;
19261fa67a4SLinus Walleij }
19361fa67a4SLinus Walleij
19461fa67a4SLinus Walleij ret = regmap_write(led->regmap, LM3601X_LED_TORCH_REG, brightness);
19561fa67a4SLinus Walleij if (ret < 0)
19661fa67a4SLinus Walleij goto out;
19761fa67a4SLinus Walleij
19861fa67a4SLinus Walleij ret = regmap_update_bits(led->regmap, LM3601X_ENABLE_REG,
19961fa67a4SLinus Walleij LM3601X_MODE_TORCH | LM3601X_MODE_IR_DRV,
20061fa67a4SLinus Walleij led_mode_val);
20161fa67a4SLinus Walleij out:
20261fa67a4SLinus Walleij mutex_unlock(&led->lock);
20361fa67a4SLinus Walleij return ret;
20461fa67a4SLinus Walleij }
20561fa67a4SLinus Walleij
lm3601x_strobe_set(struct led_classdev_flash * fled_cdev,bool state)20661fa67a4SLinus Walleij static int lm3601x_strobe_set(struct led_classdev_flash *fled_cdev,
20761fa67a4SLinus Walleij bool state)
20861fa67a4SLinus Walleij {
20961fa67a4SLinus Walleij struct lm3601x_led *led = fled_cdev_to_led(fled_cdev);
21061fa67a4SLinus Walleij int timeout_reg_val;
21161fa67a4SLinus Walleij int current_timeout;
21261fa67a4SLinus Walleij int ret;
21361fa67a4SLinus Walleij
21461fa67a4SLinus Walleij mutex_lock(&led->lock);
21561fa67a4SLinus Walleij
21661fa67a4SLinus Walleij ret = regmap_read(led->regmap, LM3601X_CFG_REG, ¤t_timeout);
21761fa67a4SLinus Walleij if (ret < 0)
21861fa67a4SLinus Walleij goto out;
21961fa67a4SLinus Walleij
22061fa67a4SLinus Walleij if (led->flash_timeout >= LM3601X_TIMEOUT_XOVER_US)
22161fa67a4SLinus Walleij timeout_reg_val = led->flash_timeout / LM3601X_UPPER_STEP_US + 0x07;
22261fa67a4SLinus Walleij else
22361fa67a4SLinus Walleij timeout_reg_val = led->flash_timeout / LM3601X_LOWER_STEP_US - 0x01;
22461fa67a4SLinus Walleij
22561fa67a4SLinus Walleij if (led->flash_timeout != current_timeout)
22661fa67a4SLinus Walleij ret = regmap_update_bits(led->regmap, LM3601X_CFG_REG,
22761fa67a4SLinus Walleij LM3601X_TIMEOUT_MASK, timeout_reg_val);
22861fa67a4SLinus Walleij
22961fa67a4SLinus Walleij if (state)
23061fa67a4SLinus Walleij ret = regmap_update_bits(led->regmap, LM3601X_ENABLE_REG,
23161fa67a4SLinus Walleij LM3601X_MODE_TORCH | LM3601X_MODE_IR_DRV,
23261fa67a4SLinus Walleij LM3601X_MODE_STROBE);
23361fa67a4SLinus Walleij else
23461fa67a4SLinus Walleij ret = regmap_update_bits(led->regmap, LM3601X_ENABLE_REG,
23561fa67a4SLinus Walleij LM3601X_MODE_STROBE, LED_OFF);
23661fa67a4SLinus Walleij
23761fa67a4SLinus Walleij ret = lm3601x_read_faults(led);
23861fa67a4SLinus Walleij out:
23961fa67a4SLinus Walleij mutex_unlock(&led->lock);
24061fa67a4SLinus Walleij return ret;
24161fa67a4SLinus Walleij }
24261fa67a4SLinus Walleij
lm3601x_flash_brightness_set(struct led_classdev_flash * fled_cdev,u32 brightness)24361fa67a4SLinus Walleij static int lm3601x_flash_brightness_set(struct led_classdev_flash *fled_cdev,
24461fa67a4SLinus Walleij u32 brightness)
24561fa67a4SLinus Walleij {
24661fa67a4SLinus Walleij struct lm3601x_led *led = fled_cdev_to_led(fled_cdev);
24761fa67a4SLinus Walleij u8 brightness_val;
24861fa67a4SLinus Walleij int ret;
24961fa67a4SLinus Walleij
25061fa67a4SLinus Walleij mutex_lock(&led->lock);
25161fa67a4SLinus Walleij ret = lm3601x_read_faults(led);
25261fa67a4SLinus Walleij if (ret < 0)
25361fa67a4SLinus Walleij goto out;
25461fa67a4SLinus Walleij
25561fa67a4SLinus Walleij if (brightness == LED_OFF) {
25661fa67a4SLinus Walleij ret = regmap_update_bits(led->regmap, LM3601X_ENABLE_REG,
25761fa67a4SLinus Walleij LM3601X_MODE_STROBE, LED_OFF);
25861fa67a4SLinus Walleij goto out;
25961fa67a4SLinus Walleij }
26061fa67a4SLinus Walleij
26161fa67a4SLinus Walleij brightness_val = brightness / LM3601X_STROBE_REG_DIV;
26261fa67a4SLinus Walleij
26361fa67a4SLinus Walleij ret = regmap_write(led->regmap, LM3601X_LED_FLASH_REG, brightness_val);
26461fa67a4SLinus Walleij out:
26561fa67a4SLinus Walleij mutex_unlock(&led->lock);
26661fa67a4SLinus Walleij return ret;
26761fa67a4SLinus Walleij }
26861fa67a4SLinus Walleij
lm3601x_flash_timeout_set(struct led_classdev_flash * fled_cdev,u32 timeout)26961fa67a4SLinus Walleij static int lm3601x_flash_timeout_set(struct led_classdev_flash *fled_cdev,
27061fa67a4SLinus Walleij u32 timeout)
27161fa67a4SLinus Walleij {
27261fa67a4SLinus Walleij struct lm3601x_led *led = fled_cdev_to_led(fled_cdev);
27361fa67a4SLinus Walleij
27461fa67a4SLinus Walleij mutex_lock(&led->lock);
27561fa67a4SLinus Walleij
27661fa67a4SLinus Walleij led->flash_timeout = timeout;
27761fa67a4SLinus Walleij
27861fa67a4SLinus Walleij mutex_unlock(&led->lock);
27961fa67a4SLinus Walleij
28061fa67a4SLinus Walleij return 0;
28161fa67a4SLinus Walleij }
28261fa67a4SLinus Walleij
lm3601x_strobe_get(struct led_classdev_flash * fled_cdev,bool * state)28361fa67a4SLinus Walleij static int lm3601x_strobe_get(struct led_classdev_flash *fled_cdev, bool *state)
28461fa67a4SLinus Walleij {
28561fa67a4SLinus Walleij struct lm3601x_led *led = fled_cdev_to_led(fled_cdev);
28661fa67a4SLinus Walleij int strobe_state;
28761fa67a4SLinus Walleij int ret;
28861fa67a4SLinus Walleij
28961fa67a4SLinus Walleij mutex_lock(&led->lock);
29061fa67a4SLinus Walleij
29161fa67a4SLinus Walleij ret = regmap_read(led->regmap, LM3601X_ENABLE_REG, &strobe_state);
29261fa67a4SLinus Walleij if (ret < 0)
29361fa67a4SLinus Walleij goto out;
29461fa67a4SLinus Walleij
29561fa67a4SLinus Walleij *state = strobe_state & LM3601X_MODE_STROBE;
29661fa67a4SLinus Walleij
29761fa67a4SLinus Walleij out:
29861fa67a4SLinus Walleij mutex_unlock(&led->lock);
29961fa67a4SLinus Walleij return ret;
30061fa67a4SLinus Walleij }
30161fa67a4SLinus Walleij
lm3601x_flash_fault_get(struct led_classdev_flash * fled_cdev,u32 * fault)30261fa67a4SLinus Walleij static int lm3601x_flash_fault_get(struct led_classdev_flash *fled_cdev,
30361fa67a4SLinus Walleij u32 *fault)
30461fa67a4SLinus Walleij {
30561fa67a4SLinus Walleij struct lm3601x_led *led = fled_cdev_to_led(fled_cdev);
30661fa67a4SLinus Walleij
30761fa67a4SLinus Walleij lm3601x_read_faults(led);
30861fa67a4SLinus Walleij
30961fa67a4SLinus Walleij *fault = led->last_flag;
31061fa67a4SLinus Walleij
31161fa67a4SLinus Walleij return 0;
31261fa67a4SLinus Walleij }
31361fa67a4SLinus Walleij
31461fa67a4SLinus Walleij static const struct led_flash_ops flash_ops = {
31561fa67a4SLinus Walleij .flash_brightness_set = lm3601x_flash_brightness_set,
31661fa67a4SLinus Walleij .strobe_set = lm3601x_strobe_set,
31761fa67a4SLinus Walleij .strobe_get = lm3601x_strobe_get,
31861fa67a4SLinus Walleij .timeout_set = lm3601x_flash_timeout_set,
31961fa67a4SLinus Walleij .fault_get = lm3601x_flash_fault_get,
32061fa67a4SLinus Walleij };
32161fa67a4SLinus Walleij
lm3601x_register_leds(struct lm3601x_led * led,struct fwnode_handle * fwnode)32261fa67a4SLinus Walleij static int lm3601x_register_leds(struct lm3601x_led *led,
32361fa67a4SLinus Walleij struct fwnode_handle *fwnode)
32461fa67a4SLinus Walleij {
32561fa67a4SLinus Walleij struct led_classdev *led_cdev;
32661fa67a4SLinus Walleij struct led_flash_setting *setting;
32761fa67a4SLinus Walleij struct led_init_data init_data = {};
32861fa67a4SLinus Walleij
32961fa67a4SLinus Walleij led->fled_cdev.ops = &flash_ops;
33061fa67a4SLinus Walleij
33161fa67a4SLinus Walleij setting = &led->fled_cdev.timeout;
33261fa67a4SLinus Walleij setting->min = LM3601X_MIN_TIMEOUT_US;
33361fa67a4SLinus Walleij setting->max = led->max_flash_timeout;
33461fa67a4SLinus Walleij setting->step = LM3601X_LOWER_STEP_US;
33561fa67a4SLinus Walleij setting->val = led->max_flash_timeout;
33661fa67a4SLinus Walleij
33761fa67a4SLinus Walleij setting = &led->fled_cdev.brightness;
33861fa67a4SLinus Walleij setting->min = LM3601X_MIN_STROBE_I_UA;
33961fa67a4SLinus Walleij setting->max = led->flash_current_max;
34061fa67a4SLinus Walleij setting->step = LM3601X_TORCH_REG_DIV;
34161fa67a4SLinus Walleij setting->val = led->flash_current_max;
34261fa67a4SLinus Walleij
34361fa67a4SLinus Walleij led_cdev = &led->fled_cdev.led_cdev;
34461fa67a4SLinus Walleij led_cdev->brightness_set_blocking = lm3601x_brightness_set;
34561fa67a4SLinus Walleij led_cdev->max_brightness = DIV_ROUND_UP(led->torch_current_max,
34661fa67a4SLinus Walleij LM3601X_TORCH_REG_DIV);
34761fa67a4SLinus Walleij led_cdev->flags |= LED_DEV_CAP_FLASH;
34861fa67a4SLinus Walleij
34961fa67a4SLinus Walleij init_data.fwnode = fwnode;
35061fa67a4SLinus Walleij init_data.devicename = led->client->name;
35161fa67a4SLinus Walleij init_data.default_label = (led->led_mode == LM3601X_LED_TORCH) ?
35261fa67a4SLinus Walleij "torch" : "infrared";
35361fa67a4SLinus Walleij return devm_led_classdev_flash_register_ext(&led->client->dev,
35461fa67a4SLinus Walleij &led->fled_cdev, &init_data);
35561fa67a4SLinus Walleij }
35661fa67a4SLinus Walleij
lm3601x_parse_node(struct lm3601x_led * led,struct fwnode_handle ** fwnode)35761fa67a4SLinus Walleij static int lm3601x_parse_node(struct lm3601x_led *led,
35861fa67a4SLinus Walleij struct fwnode_handle **fwnode)
35961fa67a4SLinus Walleij {
36061fa67a4SLinus Walleij struct fwnode_handle *child = NULL;
36161fa67a4SLinus Walleij int ret = -ENODEV;
36261fa67a4SLinus Walleij
36361fa67a4SLinus Walleij child = device_get_next_child_node(&led->client->dev, child);
36461fa67a4SLinus Walleij if (!child) {
36561fa67a4SLinus Walleij dev_err(&led->client->dev, "No LED Child node\n");
36661fa67a4SLinus Walleij return ret;
36761fa67a4SLinus Walleij }
36861fa67a4SLinus Walleij
36961fa67a4SLinus Walleij ret = fwnode_property_read_u32(child, "reg", &led->led_mode);
37061fa67a4SLinus Walleij if (ret) {
37161fa67a4SLinus Walleij dev_err(&led->client->dev, "reg DT property missing\n");
37261fa67a4SLinus Walleij goto out_err;
37361fa67a4SLinus Walleij }
37461fa67a4SLinus Walleij
37561fa67a4SLinus Walleij if (led->led_mode > LM3601X_LED_TORCH ||
37661fa67a4SLinus Walleij led->led_mode < LM3601X_LED_IR) {
37761fa67a4SLinus Walleij dev_warn(&led->client->dev, "Invalid led mode requested\n");
37861fa67a4SLinus Walleij ret = -EINVAL;
37961fa67a4SLinus Walleij goto out_err;
38061fa67a4SLinus Walleij }
38161fa67a4SLinus Walleij
38261fa67a4SLinus Walleij ret = fwnode_property_read_u32(child, "led-max-microamp",
38361fa67a4SLinus Walleij &led->torch_current_max);
38461fa67a4SLinus Walleij if (ret) {
38561fa67a4SLinus Walleij dev_warn(&led->client->dev,
38661fa67a4SLinus Walleij "led-max-microamp DT property missing\n");
38761fa67a4SLinus Walleij goto out_err;
38861fa67a4SLinus Walleij }
38961fa67a4SLinus Walleij
39061fa67a4SLinus Walleij ret = fwnode_property_read_u32(child, "flash-max-microamp",
39161fa67a4SLinus Walleij &led->flash_current_max);
39261fa67a4SLinus Walleij if (ret) {
39361fa67a4SLinus Walleij dev_warn(&led->client->dev,
39461fa67a4SLinus Walleij "flash-max-microamp DT property missing\n");
39561fa67a4SLinus Walleij goto out_err;
39661fa67a4SLinus Walleij }
39761fa67a4SLinus Walleij
39861fa67a4SLinus Walleij ret = fwnode_property_read_u32(child, "flash-max-timeout-us",
39961fa67a4SLinus Walleij &led->max_flash_timeout);
40061fa67a4SLinus Walleij if (ret) {
40161fa67a4SLinus Walleij dev_warn(&led->client->dev,
40261fa67a4SLinus Walleij "flash-max-timeout-us DT property missing\n");
40361fa67a4SLinus Walleij goto out_err;
40461fa67a4SLinus Walleij }
40561fa67a4SLinus Walleij
40661fa67a4SLinus Walleij *fwnode = child;
40761fa67a4SLinus Walleij
40861fa67a4SLinus Walleij out_err:
40961fa67a4SLinus Walleij fwnode_handle_put(child);
41061fa67a4SLinus Walleij return ret;
41161fa67a4SLinus Walleij }
41261fa67a4SLinus Walleij
lm3601x_probe(struct i2c_client * client)41361fa67a4SLinus Walleij static int lm3601x_probe(struct i2c_client *client)
41461fa67a4SLinus Walleij {
41561fa67a4SLinus Walleij struct lm3601x_led *led;
41661fa67a4SLinus Walleij struct fwnode_handle *fwnode;
41761fa67a4SLinus Walleij int ret;
41861fa67a4SLinus Walleij
41961fa67a4SLinus Walleij led = devm_kzalloc(&client->dev, sizeof(*led), GFP_KERNEL);
42061fa67a4SLinus Walleij if (!led)
42161fa67a4SLinus Walleij return -ENOMEM;
42261fa67a4SLinus Walleij
42361fa67a4SLinus Walleij led->client = client;
42461fa67a4SLinus Walleij i2c_set_clientdata(client, led);
42561fa67a4SLinus Walleij
42661fa67a4SLinus Walleij ret = lm3601x_parse_node(led, &fwnode);
42761fa67a4SLinus Walleij if (ret)
42861fa67a4SLinus Walleij return -ENODEV;
42961fa67a4SLinus Walleij
43061fa67a4SLinus Walleij led->regmap = devm_regmap_init_i2c(client, &lm3601x_regmap);
43161fa67a4SLinus Walleij if (IS_ERR(led->regmap)) {
43261fa67a4SLinus Walleij ret = PTR_ERR(led->regmap);
43361fa67a4SLinus Walleij dev_err(&client->dev,
43461fa67a4SLinus Walleij "Failed to allocate register map: %d\n", ret);
43561fa67a4SLinus Walleij return ret;
43661fa67a4SLinus Walleij }
43761fa67a4SLinus Walleij
43861fa67a4SLinus Walleij mutex_init(&led->lock);
43961fa67a4SLinus Walleij
44061fa67a4SLinus Walleij return lm3601x_register_leds(led, fwnode);
44161fa67a4SLinus Walleij }
44261fa67a4SLinus Walleij
lm3601x_remove(struct i2c_client * client)443ed5c2f5fSUwe Kleine-König static void lm3601x_remove(struct i2c_client *client)
44461fa67a4SLinus Walleij {
44561fa67a4SLinus Walleij struct lm3601x_led *led = i2c_get_clientdata(client);
44622a23436SUwe Kleine-König int ret;
44761fa67a4SLinus Walleij
44822a23436SUwe Kleine-König ret = regmap_update_bits(led->regmap, LM3601X_ENABLE_REG,
44922a23436SUwe Kleine-König LM3601X_ENABLE_MASK, LM3601X_MODE_STANDBY);
45022a23436SUwe Kleine-König if (ret)
45122a23436SUwe Kleine-König dev_warn(&client->dev,
45222a23436SUwe Kleine-König "Failed to put into standby (%pe)\n", ERR_PTR(ret));
45361fa67a4SLinus Walleij }
45461fa67a4SLinus Walleij
45561fa67a4SLinus Walleij static const struct i2c_device_id lm3601x_id[] = {
45661fa67a4SLinus Walleij { "LM36010", CHIP_LM36010 },
45761fa67a4SLinus Walleij { "LM36011", CHIP_LM36011 },
45861fa67a4SLinus Walleij { }
45961fa67a4SLinus Walleij };
46061fa67a4SLinus Walleij MODULE_DEVICE_TABLE(i2c, lm3601x_id);
46161fa67a4SLinus Walleij
46261fa67a4SLinus Walleij static const struct of_device_id of_lm3601x_leds_match[] = {
46361fa67a4SLinus Walleij { .compatible = "ti,lm36010", },
46461fa67a4SLinus Walleij { .compatible = "ti,lm36011", },
46561fa67a4SLinus Walleij { }
46661fa67a4SLinus Walleij };
46761fa67a4SLinus Walleij MODULE_DEVICE_TABLE(of, of_lm3601x_leds_match);
46861fa67a4SLinus Walleij
46961fa67a4SLinus Walleij static struct i2c_driver lm3601x_i2c_driver = {
47061fa67a4SLinus Walleij .driver = {
47161fa67a4SLinus Walleij .name = "lm3601x",
47261fa67a4SLinus Walleij .of_match_table = of_lm3601x_leds_match,
47361fa67a4SLinus Walleij },
474*d9ff8a8eSUwe Kleine-König .probe = lm3601x_probe,
47561fa67a4SLinus Walleij .remove = lm3601x_remove,
47661fa67a4SLinus Walleij .id_table = lm3601x_id,
47761fa67a4SLinus Walleij };
47861fa67a4SLinus Walleij module_i2c_driver(lm3601x_i2c_driver);
47961fa67a4SLinus Walleij
48061fa67a4SLinus Walleij MODULE_DESCRIPTION("Texas Instruments Flash Lighting driver for LM3601X");
48161fa67a4SLinus Walleij MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
48261fa67a4SLinus Walleij MODULE_LICENSE("GPL v2");
483