Loading drivers/leds/leds-qti-flash.c +305 −11 Original line number Diff line number Diff line Loading @@ -15,13 +15,21 @@ #include <linux/of_gpio.h> #include <linux/of_irq.h> #include <linux/platform_device.h> #include <linux/power_supply.h> #include <linux/regmap.h> #include <linux/soc/qcom/battery_charger.h> #include "leds.h" #define FLASH_LED_REVISION1 0x00 #define FLASH_LED_PERIPH_SUBTYPE 0x05 #define FLASH_LED_STATUS1 0x06 #define FLASH_LED_STATUS2 0x07 #define FLASH_LED_OTST1_STATUS BIT(5) #define FLASH_LED_OTST2_STATUS BIT(4) #define FLASH_LED_VPH_PWR_LOW BIT(0) #define FLASH_INT_RT_STS 0x10 Loading Loading @@ -59,6 +67,17 @@ #define FLASH_LED_ENABLE(id) BIT(id) #define FLASH_LED_DISABLE 0 #define FLASH_LED_MITIGATION_SEL 0x63 #define FLASH_LED_PREEMPTIVE_LMH_MASK GENMASK(1, 0) #define FLASH_LED_LMH_MITIGATION_SW 0x2 #define FLASH_LED_THERMAL_OTST2_CFG1 0x78 #define FLASH_LED_THERMAL_OTST1_CFG1 0x7A #define FLASH_LED_THERMAL_THRSH_MASK GENMASK(2, 0) #define FLASH_LED_V1_OTST1_THRSH_MIN 0x13 #define FLASH_LED_V2_OTST1_THRSH_MIN 0x10 #define FLASH_LED_OTST2_THRSH_MIN 0x30 #define MAX_IRES_LEVELS 2 #define IRES_12P5_MAX_CURR_MA 1500 #define IRES_5P0_MAX_CURR_MA 640 Loading @@ -67,6 +86,13 @@ #define IRES_5P0_UA 5000 #define IRES_DEFAULT_UA IRES_12P5_UA #define MAX_FLASH_CURRENT_MA 2000 #define IBATT_OCP_THRESH_DEFAULT_UA 4500000 #define FLASH_THERMAL_LEVELS 2 #define OTST1_IDX 0 #define OTST2_IDX 1 #define OTST1_CURR_LIM_MA 200 #define OTST2_CURR_LIM_MA 500 #define VLED_MAX_DEFAULT_UV 3500000 enum flash_led_type { FLASH_LED_TYPE_UNKNOWN, Loading @@ -74,12 +100,19 @@ enum flash_led_type { FLASH_LED_TYPE_TORCH, }; enum flash_led_revision { FLASH_LED_REVISION_2P0 = 1, }; enum flash_led_subtype { FLASH_LED_4_CHAN = 0x7, }; enum strobe_type { SW_STROBE = 0, HW_STROBE, }; /* Configurations for each individual flash or torch device */ struct flash_node_data { struct qti_flash_led *led; struct led_classdev_flash fdev; Loading Loading @@ -112,6 +145,7 @@ struct flash_switch_data { * @regmap : Pointer for regmap structure * @fnode : Pointer for array of child LED devices * @snode : Pointer for array of child switch devices * @batt_psy : Pointer for battery power supply * @lock : Spinlock to be used for critical section * @num_fnodes : Number of flash/torch nodes defined in device tree * @num_snodes : Number of switch nodes defined in device tree Loading @@ -119,15 +153,21 @@ struct flash_switch_data { * @all_ramp_up_done_irq : IRQ number for all ramp up interrupt * @all_ramp_down_done_irq : IRQ number for all ramp down interrupt * @led_fault_irq : IRQ number for LED fault interrupt * @max_current : Maximum current available for flash * @thermal_derate_current : Thermal derating current limits * @base : Base address of the flash LED module * @revision : Revision of the flash LED module * @subtype : Peripheral subtype of the flash LED module * @max_channels : Maximum number of channels supported by flash module * @ref_count : Reference count used to enable/disable flash LED * @trigger_lmh : Flag to enable lmh mitigation */ struct qti_flash_led { struct platform_device *pdev; struct regmap *regmap; struct flash_node_data *fnode; struct flash_switch_data *snode; struct power_supply *batt_psy; spinlock_t lock; u32 num_fnodes; u32 num_snodes; Loading @@ -136,9 +176,22 @@ struct qti_flash_led { int all_ramp_down_done_irq; int led_fault_irq; int max_current; int thermal_derate_current[FLASH_THERMAL_LEVELS]; u16 base; u8 revision; u8 subtype; u8 max_channels; u8 ref_count; bool trigger_lmh; }; struct flash_current_headroom { u16 current_ma; u16 headroom_mv; }; static const struct flash_current_headroom pm8350c_map[4] = { {750, 200}, {1000, 250}, {1250, 300}, {1500, 400}, }; static const u32 flash_led_max_ires_values[MAX_IRES_LEVELS] = { Loading Loading @@ -586,15 +639,214 @@ static struct led_classdev *trigger_to_lcdev(struct led_trigger *trig) return NULL; } static ssize_t qti_flash_led_max_current_show(struct device *dev, struct device_attribute *attr, char *buf) #define UCONV 1000000LL #define MCONV 1000LL #define VIN_FLASH_MIN_UV 3300000LL #define BOB_EFFICIENCY 900LL #define VFLASH_DIP_MARGIN_UV 50000 #define VOLTAGE_HDRM_DEFAULT_MV 400 #define VDIP_THRESH_DEFAULT_UV 2800000LL static int qti_flash_led_get_voltage_headroom( struct qti_flash_led *led) { struct flash_switch_data *snode; struct led_classdev *led_cdev = dev_get_drvdata(dev); static const struct flash_current_headroom *hdrm_map; int i, j, voltage_hdrm_mv = 0, voltage_hdrm_max = 0; u32 map_size = 0; snode = container_of(led_cdev, struct flash_switch_data, cdev); if (led->subtype == FLASH_LED_4_CHAN) { hdrm_map = pm8350c_map; map_size = ARRAY_SIZE(pm8350c_map); } return scnprintf(buf, PAGE_SIZE, "%d\n", snode->led->max_current); for (i = 0; i < led->num_fnodes; i++) { if (!led->fnode[i].configured) continue; voltage_hdrm_mv = VOLTAGE_HDRM_DEFAULT_MV; for (j = 0; j < map_size; j++) { if (led->fnode[i].current_ma <= hdrm_map[j].current_ma) voltage_hdrm_mv = hdrm_map[j].headroom_mv; } voltage_hdrm_max = max(voltage_hdrm_max, voltage_hdrm_mv); } if (!voltage_hdrm_max) voltage_hdrm_max = VOLTAGE_HDRM_DEFAULT_MV; return voltage_hdrm_max; } static int qti_flash_led_calc_max_avail_current( struct qti_flash_led *led, int *max_current_ma) { int rc; int rbatt_uohm, ocv_uv, ibatt_now_ua, voltage_hdrm_mv; int64_t ibatt_safe_ua, i_flash_ua, i_avail_ua, vflash_vdip, vph_flash_uv, vin_flash_uv, p_flash_fw; union power_supply_propval prop = {}; rc = qti_battery_charger_get_prop("battery", BATTERY_RESISTANCE, &rbatt_uohm); if (rc < 0) { pr_err("Failed to get battery resistance, rc=%d\n", rc); return rc; } if (!rbatt_uohm) { *max_current_ma = MAX_FLASH_CURRENT_MA; return 0; } if (!led->batt_psy) led->batt_psy = power_supply_get_by_name("battery"); if (!led->batt_psy) { pr_err("Failed to get battery power supply, rc=%d\n", rc); return -EINVAL; } rc = power_supply_get_property(led->batt_psy, POWER_SUPPLY_PROP_VOLTAGE_OCV, &prop); if (rc < 0) { pr_err("Failed to get battery OCV, rc=%d\n", rc); return rc; } ocv_uv = prop.intval; rc = power_supply_get_property(led->batt_psy, POWER_SUPPLY_PROP_CURRENT_NOW, &prop); if (rc < 0) { pr_err("Failed to get battery current, rc=%d\n", rc); return rc; } /* Battery power supply returns -ve value for discharging */ ibatt_now_ua = -(prop.intval); voltage_hdrm_mv = qti_flash_led_get_voltage_headroom(led); vflash_vdip = VDIP_THRESH_DEFAULT_UV; if (!led->trigger_lmh) { rc = qti_flash_led_masked_write(led, FLASH_LED_MITIGATION_SEL, FLASH_LED_PREEMPTIVE_LMH_MASK, FLASH_LED_LMH_MITIGATION_SW); if (rc < 0) { pr_err("Failed to enable LMH mitigation, rc=%d\n", rc); return rc; } /* Wait for lmh mitigation to take effect */ udelay(100); led->trigger_lmh = true; return qti_flash_led_calc_max_avail_current(led, max_current_ma); } ibatt_safe_ua = DIV_ROUND_CLOSEST((ocv_uv - (vflash_vdip + VFLASH_DIP_MARGIN_UV)) * UCONV, rbatt_uohm); if (ibatt_safe_ua < IBATT_OCP_THRESH_DEFAULT_UA) { i_flash_ua = ibatt_safe_ua - ibatt_now_ua; vph_flash_uv = vflash_vdip + VFLASH_DIP_MARGIN_UV; } else { i_flash_ua = IBATT_OCP_THRESH_DEFAULT_UA - ibatt_now_ua; vph_flash_uv = ocv_uv - DIV_ROUND_CLOSEST((int64_t)rbatt_uohm * IBATT_OCP_THRESH_DEFAULT_UA, UCONV); } vin_flash_uv = max(VLED_MAX_DEFAULT_UV + (voltage_hdrm_mv * MCONV), VIN_FLASH_MIN_UV); p_flash_fw = BOB_EFFICIENCY * vph_flash_uv * i_flash_ua; i_avail_ua = DIV_ROUND_CLOSEST(p_flash_fw, (vin_flash_uv * MCONV)); *max_current_ma = min(MAX_FLASH_CURRENT_MA, (int)(DIV_ROUND_CLOSEST(i_avail_ua, MCONV))); pr_debug("rbatt_uohm=%d ocv_uv=%d ibatt_now_ua=%d i_avail_ua=%lld\n", rbatt_uohm, ocv_uv, ibatt_now_ua, i_avail_ua); return 0; } static int qti_flash_led_calc_thermal_current( struct qti_flash_led *led, int *thermal_current_limit) { int rc; u8 otst_status, thrsh_min = 0; if (led->subtype == FLASH_LED_4_CHAN) { thrsh_min = FLASH_LED_V1_OTST1_THRSH_MIN; if (led->revision == FLASH_LED_REVISION_2P0) thrsh_min = FLASH_LED_V2_OTST1_THRSH_MIN; } rc = qti_flash_led_masked_write(led, FLASH_LED_THERMAL_OTST1_CFG1, FLASH_LED_THERMAL_THRSH_MASK, thrsh_min); if (rc < 0) return rc; rc = qti_flash_led_masked_write(led, FLASH_LED_THERMAL_OTST2_CFG1, FLASH_LED_THERMAL_THRSH_MASK, FLASH_LED_OTST2_THRSH_MIN); if (rc < 0) return rc; /* Check THERMAL OTST status */ rc = qti_flash_led_read(led, FLASH_LED_STATUS2, &otst_status, 1); if (rc < 0) return rc; if (otst_status & FLASH_LED_OTST1_STATUS) *thermal_current_limit = led->thermal_derate_current[OTST1_IDX]; else if (otst_status & FLASH_LED_OTST2_STATUS) *thermal_current_limit = led->thermal_derate_current[OTST2_IDX]; pr_debug("thermal_current_limit=%d\n", *thermal_current_limit); return 0; } static int qti_flash_led_get_max_avail_current( struct qti_flash_led *led, int *max_current_ma) { int thermal_current_limit = 0, rc; led->trigger_lmh = false; rc = qti_flash_led_calc_max_avail_current(led, max_current_ma); if (rc < 0) { pr_err("Failed to calculate max avail current, rc=%d\n", rc); return rc; } rc = qti_flash_led_calc_thermal_current(led, &thermal_current_limit); if (rc < 0) { pr_err("Failed to calculate thermal current limit, rc=%d\n", rc); return rc; } if (thermal_current_limit) *max_current_ma = min(*max_current_ma, thermal_current_limit); led->max_current = *max_current_ma; pr_debug("max_current_ma=%d\n", *max_current_ma); return 0; } int qti_flash_led_prepare(struct led_trigger *trig, int options, Loading @@ -602,6 +854,7 @@ int qti_flash_led_prepare(struct led_trigger *trig, int options, { struct led_classdev *led_cdev; struct flash_switch_data *snode; int rc = 0; if (!trig) { pr_err("Invalid led_trigger\n"); Loading @@ -617,13 +870,36 @@ int qti_flash_led_prepare(struct led_trigger *trig, int options, snode = container_of(led_cdev, struct flash_switch_data, cdev); if (options & QUERY_MAX_AVAIL_CURRENT) { *max_current = snode->led->max_current; rc = qti_flash_led_get_max_avail_current(snode->led, max_current); if (rc < 0) { pr_err("Failed to query max avail current, rc=%d\n", rc); return rc; } } return 0; } EXPORT_SYMBOL(qti_flash_led_prepare); static ssize_t qti_flash_led_max_current_show(struct device *dev, struct device_attribute *attr, char *buf) { struct flash_switch_data *snode; struct led_classdev *led_cdev = dev_get_drvdata(dev); int rc; snode = container_of(led_cdev, struct flash_switch_data, cdev); rc = qti_flash_led_get_max_avail_current(snode->led, &snode->led->max_current); if (rc < 0) { pr_err("Failed to get max current, rc=%d\n", rc); return -EINVAL; } EXPORT_SYMBOL(qti_flash_led_prepare); return scnprintf(buf, PAGE_SIZE, "%d\n", snode->led->max_current); } static struct device_attribute qti_flash_led_attrs[] = { __ATTR(max_current, 0664, qti_flash_led_max_current_show, NULL), Loading Loading @@ -728,6 +1004,18 @@ static int qti_flash_led_setup(struct qti_flash_led *led) int rc = 0, i, addr_offset; u8 val, mask; rc = qti_flash_led_read(led, FLASH_LED_REVISION1, &val, 1); if (rc < 0) return rc; led->revision = val; rc = qti_flash_led_read(led, FLASH_LED_PERIPH_SUBTYPE, &val, 1); if (rc < 0) return rc; led->subtype = val; for (i = 0; i < led->num_fnodes; i++) { addr_offset = led->fnode[i].id; rc = qti_flash_led_masked_write(led, Loading @@ -749,6 +1037,9 @@ static int qti_flash_led_setup(struct qti_flash_led *led) led->max_current = MAX_FLASH_CURRENT_MA; led->thermal_derate_current[OTST1_IDX] = OTST1_CURR_LIM_MA; led->thermal_derate_current[OTST2_IDX] = OTST2_CURR_LIM_MA; return rc; } Loading Loading @@ -1231,6 +1522,9 @@ static int qti_flash_led_remove(struct platform_device *pdev) struct qti_flash_led *led = dev_get_drvdata(&pdev->dev); int i, j; if (led->batt_psy) power_supply_put(led->batt_psy); for (i = 0; (i < led->num_snodes); i++) { for (j = 0; j < ARRAY_SIZE(qti_flash_led_attrs); j++) sysfs_remove_file(&led->snode[i].cdev.dev->kobj, Loading Loading
drivers/leds/leds-qti-flash.c +305 −11 Original line number Diff line number Diff line Loading @@ -15,13 +15,21 @@ #include <linux/of_gpio.h> #include <linux/of_irq.h> #include <linux/platform_device.h> #include <linux/power_supply.h> #include <linux/regmap.h> #include <linux/soc/qcom/battery_charger.h> #include "leds.h" #define FLASH_LED_REVISION1 0x00 #define FLASH_LED_PERIPH_SUBTYPE 0x05 #define FLASH_LED_STATUS1 0x06 #define FLASH_LED_STATUS2 0x07 #define FLASH_LED_OTST1_STATUS BIT(5) #define FLASH_LED_OTST2_STATUS BIT(4) #define FLASH_LED_VPH_PWR_LOW BIT(0) #define FLASH_INT_RT_STS 0x10 Loading Loading @@ -59,6 +67,17 @@ #define FLASH_LED_ENABLE(id) BIT(id) #define FLASH_LED_DISABLE 0 #define FLASH_LED_MITIGATION_SEL 0x63 #define FLASH_LED_PREEMPTIVE_LMH_MASK GENMASK(1, 0) #define FLASH_LED_LMH_MITIGATION_SW 0x2 #define FLASH_LED_THERMAL_OTST2_CFG1 0x78 #define FLASH_LED_THERMAL_OTST1_CFG1 0x7A #define FLASH_LED_THERMAL_THRSH_MASK GENMASK(2, 0) #define FLASH_LED_V1_OTST1_THRSH_MIN 0x13 #define FLASH_LED_V2_OTST1_THRSH_MIN 0x10 #define FLASH_LED_OTST2_THRSH_MIN 0x30 #define MAX_IRES_LEVELS 2 #define IRES_12P5_MAX_CURR_MA 1500 #define IRES_5P0_MAX_CURR_MA 640 Loading @@ -67,6 +86,13 @@ #define IRES_5P0_UA 5000 #define IRES_DEFAULT_UA IRES_12P5_UA #define MAX_FLASH_CURRENT_MA 2000 #define IBATT_OCP_THRESH_DEFAULT_UA 4500000 #define FLASH_THERMAL_LEVELS 2 #define OTST1_IDX 0 #define OTST2_IDX 1 #define OTST1_CURR_LIM_MA 200 #define OTST2_CURR_LIM_MA 500 #define VLED_MAX_DEFAULT_UV 3500000 enum flash_led_type { FLASH_LED_TYPE_UNKNOWN, Loading @@ -74,12 +100,19 @@ enum flash_led_type { FLASH_LED_TYPE_TORCH, }; enum flash_led_revision { FLASH_LED_REVISION_2P0 = 1, }; enum flash_led_subtype { FLASH_LED_4_CHAN = 0x7, }; enum strobe_type { SW_STROBE = 0, HW_STROBE, }; /* Configurations for each individual flash or torch device */ struct flash_node_data { struct qti_flash_led *led; struct led_classdev_flash fdev; Loading Loading @@ -112,6 +145,7 @@ struct flash_switch_data { * @regmap : Pointer for regmap structure * @fnode : Pointer for array of child LED devices * @snode : Pointer for array of child switch devices * @batt_psy : Pointer for battery power supply * @lock : Spinlock to be used for critical section * @num_fnodes : Number of flash/torch nodes defined in device tree * @num_snodes : Number of switch nodes defined in device tree Loading @@ -119,15 +153,21 @@ struct flash_switch_data { * @all_ramp_up_done_irq : IRQ number for all ramp up interrupt * @all_ramp_down_done_irq : IRQ number for all ramp down interrupt * @led_fault_irq : IRQ number for LED fault interrupt * @max_current : Maximum current available for flash * @thermal_derate_current : Thermal derating current limits * @base : Base address of the flash LED module * @revision : Revision of the flash LED module * @subtype : Peripheral subtype of the flash LED module * @max_channels : Maximum number of channels supported by flash module * @ref_count : Reference count used to enable/disable flash LED * @trigger_lmh : Flag to enable lmh mitigation */ struct qti_flash_led { struct platform_device *pdev; struct regmap *regmap; struct flash_node_data *fnode; struct flash_switch_data *snode; struct power_supply *batt_psy; spinlock_t lock; u32 num_fnodes; u32 num_snodes; Loading @@ -136,9 +176,22 @@ struct qti_flash_led { int all_ramp_down_done_irq; int led_fault_irq; int max_current; int thermal_derate_current[FLASH_THERMAL_LEVELS]; u16 base; u8 revision; u8 subtype; u8 max_channels; u8 ref_count; bool trigger_lmh; }; struct flash_current_headroom { u16 current_ma; u16 headroom_mv; }; static const struct flash_current_headroom pm8350c_map[4] = { {750, 200}, {1000, 250}, {1250, 300}, {1500, 400}, }; static const u32 flash_led_max_ires_values[MAX_IRES_LEVELS] = { Loading Loading @@ -586,15 +639,214 @@ static struct led_classdev *trigger_to_lcdev(struct led_trigger *trig) return NULL; } static ssize_t qti_flash_led_max_current_show(struct device *dev, struct device_attribute *attr, char *buf) #define UCONV 1000000LL #define MCONV 1000LL #define VIN_FLASH_MIN_UV 3300000LL #define BOB_EFFICIENCY 900LL #define VFLASH_DIP_MARGIN_UV 50000 #define VOLTAGE_HDRM_DEFAULT_MV 400 #define VDIP_THRESH_DEFAULT_UV 2800000LL static int qti_flash_led_get_voltage_headroom( struct qti_flash_led *led) { struct flash_switch_data *snode; struct led_classdev *led_cdev = dev_get_drvdata(dev); static const struct flash_current_headroom *hdrm_map; int i, j, voltage_hdrm_mv = 0, voltage_hdrm_max = 0; u32 map_size = 0; snode = container_of(led_cdev, struct flash_switch_data, cdev); if (led->subtype == FLASH_LED_4_CHAN) { hdrm_map = pm8350c_map; map_size = ARRAY_SIZE(pm8350c_map); } return scnprintf(buf, PAGE_SIZE, "%d\n", snode->led->max_current); for (i = 0; i < led->num_fnodes; i++) { if (!led->fnode[i].configured) continue; voltage_hdrm_mv = VOLTAGE_HDRM_DEFAULT_MV; for (j = 0; j < map_size; j++) { if (led->fnode[i].current_ma <= hdrm_map[j].current_ma) voltage_hdrm_mv = hdrm_map[j].headroom_mv; } voltage_hdrm_max = max(voltage_hdrm_max, voltage_hdrm_mv); } if (!voltage_hdrm_max) voltage_hdrm_max = VOLTAGE_HDRM_DEFAULT_MV; return voltage_hdrm_max; } static int qti_flash_led_calc_max_avail_current( struct qti_flash_led *led, int *max_current_ma) { int rc; int rbatt_uohm, ocv_uv, ibatt_now_ua, voltage_hdrm_mv; int64_t ibatt_safe_ua, i_flash_ua, i_avail_ua, vflash_vdip, vph_flash_uv, vin_flash_uv, p_flash_fw; union power_supply_propval prop = {}; rc = qti_battery_charger_get_prop("battery", BATTERY_RESISTANCE, &rbatt_uohm); if (rc < 0) { pr_err("Failed to get battery resistance, rc=%d\n", rc); return rc; } if (!rbatt_uohm) { *max_current_ma = MAX_FLASH_CURRENT_MA; return 0; } if (!led->batt_psy) led->batt_psy = power_supply_get_by_name("battery"); if (!led->batt_psy) { pr_err("Failed to get battery power supply, rc=%d\n", rc); return -EINVAL; } rc = power_supply_get_property(led->batt_psy, POWER_SUPPLY_PROP_VOLTAGE_OCV, &prop); if (rc < 0) { pr_err("Failed to get battery OCV, rc=%d\n", rc); return rc; } ocv_uv = prop.intval; rc = power_supply_get_property(led->batt_psy, POWER_SUPPLY_PROP_CURRENT_NOW, &prop); if (rc < 0) { pr_err("Failed to get battery current, rc=%d\n", rc); return rc; } /* Battery power supply returns -ve value for discharging */ ibatt_now_ua = -(prop.intval); voltage_hdrm_mv = qti_flash_led_get_voltage_headroom(led); vflash_vdip = VDIP_THRESH_DEFAULT_UV; if (!led->trigger_lmh) { rc = qti_flash_led_masked_write(led, FLASH_LED_MITIGATION_SEL, FLASH_LED_PREEMPTIVE_LMH_MASK, FLASH_LED_LMH_MITIGATION_SW); if (rc < 0) { pr_err("Failed to enable LMH mitigation, rc=%d\n", rc); return rc; } /* Wait for lmh mitigation to take effect */ udelay(100); led->trigger_lmh = true; return qti_flash_led_calc_max_avail_current(led, max_current_ma); } ibatt_safe_ua = DIV_ROUND_CLOSEST((ocv_uv - (vflash_vdip + VFLASH_DIP_MARGIN_UV)) * UCONV, rbatt_uohm); if (ibatt_safe_ua < IBATT_OCP_THRESH_DEFAULT_UA) { i_flash_ua = ibatt_safe_ua - ibatt_now_ua; vph_flash_uv = vflash_vdip + VFLASH_DIP_MARGIN_UV; } else { i_flash_ua = IBATT_OCP_THRESH_DEFAULT_UA - ibatt_now_ua; vph_flash_uv = ocv_uv - DIV_ROUND_CLOSEST((int64_t)rbatt_uohm * IBATT_OCP_THRESH_DEFAULT_UA, UCONV); } vin_flash_uv = max(VLED_MAX_DEFAULT_UV + (voltage_hdrm_mv * MCONV), VIN_FLASH_MIN_UV); p_flash_fw = BOB_EFFICIENCY * vph_flash_uv * i_flash_ua; i_avail_ua = DIV_ROUND_CLOSEST(p_flash_fw, (vin_flash_uv * MCONV)); *max_current_ma = min(MAX_FLASH_CURRENT_MA, (int)(DIV_ROUND_CLOSEST(i_avail_ua, MCONV))); pr_debug("rbatt_uohm=%d ocv_uv=%d ibatt_now_ua=%d i_avail_ua=%lld\n", rbatt_uohm, ocv_uv, ibatt_now_ua, i_avail_ua); return 0; } static int qti_flash_led_calc_thermal_current( struct qti_flash_led *led, int *thermal_current_limit) { int rc; u8 otst_status, thrsh_min = 0; if (led->subtype == FLASH_LED_4_CHAN) { thrsh_min = FLASH_LED_V1_OTST1_THRSH_MIN; if (led->revision == FLASH_LED_REVISION_2P0) thrsh_min = FLASH_LED_V2_OTST1_THRSH_MIN; } rc = qti_flash_led_masked_write(led, FLASH_LED_THERMAL_OTST1_CFG1, FLASH_LED_THERMAL_THRSH_MASK, thrsh_min); if (rc < 0) return rc; rc = qti_flash_led_masked_write(led, FLASH_LED_THERMAL_OTST2_CFG1, FLASH_LED_THERMAL_THRSH_MASK, FLASH_LED_OTST2_THRSH_MIN); if (rc < 0) return rc; /* Check THERMAL OTST status */ rc = qti_flash_led_read(led, FLASH_LED_STATUS2, &otst_status, 1); if (rc < 0) return rc; if (otst_status & FLASH_LED_OTST1_STATUS) *thermal_current_limit = led->thermal_derate_current[OTST1_IDX]; else if (otst_status & FLASH_LED_OTST2_STATUS) *thermal_current_limit = led->thermal_derate_current[OTST2_IDX]; pr_debug("thermal_current_limit=%d\n", *thermal_current_limit); return 0; } static int qti_flash_led_get_max_avail_current( struct qti_flash_led *led, int *max_current_ma) { int thermal_current_limit = 0, rc; led->trigger_lmh = false; rc = qti_flash_led_calc_max_avail_current(led, max_current_ma); if (rc < 0) { pr_err("Failed to calculate max avail current, rc=%d\n", rc); return rc; } rc = qti_flash_led_calc_thermal_current(led, &thermal_current_limit); if (rc < 0) { pr_err("Failed to calculate thermal current limit, rc=%d\n", rc); return rc; } if (thermal_current_limit) *max_current_ma = min(*max_current_ma, thermal_current_limit); led->max_current = *max_current_ma; pr_debug("max_current_ma=%d\n", *max_current_ma); return 0; } int qti_flash_led_prepare(struct led_trigger *trig, int options, Loading @@ -602,6 +854,7 @@ int qti_flash_led_prepare(struct led_trigger *trig, int options, { struct led_classdev *led_cdev; struct flash_switch_data *snode; int rc = 0; if (!trig) { pr_err("Invalid led_trigger\n"); Loading @@ -617,13 +870,36 @@ int qti_flash_led_prepare(struct led_trigger *trig, int options, snode = container_of(led_cdev, struct flash_switch_data, cdev); if (options & QUERY_MAX_AVAIL_CURRENT) { *max_current = snode->led->max_current; rc = qti_flash_led_get_max_avail_current(snode->led, max_current); if (rc < 0) { pr_err("Failed to query max avail current, rc=%d\n", rc); return rc; } } return 0; } EXPORT_SYMBOL(qti_flash_led_prepare); static ssize_t qti_flash_led_max_current_show(struct device *dev, struct device_attribute *attr, char *buf) { struct flash_switch_data *snode; struct led_classdev *led_cdev = dev_get_drvdata(dev); int rc; snode = container_of(led_cdev, struct flash_switch_data, cdev); rc = qti_flash_led_get_max_avail_current(snode->led, &snode->led->max_current); if (rc < 0) { pr_err("Failed to get max current, rc=%d\n", rc); return -EINVAL; } EXPORT_SYMBOL(qti_flash_led_prepare); return scnprintf(buf, PAGE_SIZE, "%d\n", snode->led->max_current); } static struct device_attribute qti_flash_led_attrs[] = { __ATTR(max_current, 0664, qti_flash_led_max_current_show, NULL), Loading Loading @@ -728,6 +1004,18 @@ static int qti_flash_led_setup(struct qti_flash_led *led) int rc = 0, i, addr_offset; u8 val, mask; rc = qti_flash_led_read(led, FLASH_LED_REVISION1, &val, 1); if (rc < 0) return rc; led->revision = val; rc = qti_flash_led_read(led, FLASH_LED_PERIPH_SUBTYPE, &val, 1); if (rc < 0) return rc; led->subtype = val; for (i = 0; i < led->num_fnodes; i++) { addr_offset = led->fnode[i].id; rc = qti_flash_led_masked_write(led, Loading @@ -749,6 +1037,9 @@ static int qti_flash_led_setup(struct qti_flash_led *led) led->max_current = MAX_FLASH_CURRENT_MA; led->thermal_derate_current[OTST1_IDX] = OTST1_CURR_LIM_MA; led->thermal_derate_current[OTST2_IDX] = OTST2_CURR_LIM_MA; return rc; } Loading Loading @@ -1231,6 +1522,9 @@ static int qti_flash_led_remove(struct platform_device *pdev) struct qti_flash_led *led = dev_get_drvdata(&pdev->dev); int i, j; if (led->batt_psy) power_supply_put(led->batt_psy); for (i = 0; (i < led->num_snodes); i++) { for (j = 0; j < ARRAY_SIZE(qti_flash_led_attrs); j++) sysfs_remove_file(&led->snode[i].cdev.dev->kobj, Loading