mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-09 05:39:54 -04:00
Merge "leds: qti-flash: Add maximum available flash current prediction"
This commit is contained in:
commit
41dfbf027b
1 changed files with 305 additions and 11 deletions
|
|
@ -15,14 +15,22 @@
|
|||
#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_VPH_PWR_LOW BIT(0)
|
||||
#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
|
||||
#define FLASH_LED_FAULT_RT_STS BIT(0)
|
||||
|
|
@ -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
|
||||
|
|
@ -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,
|
||||
|
|
@ -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;
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
|
|
@ -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] = {
|
||||
|
|
@ -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,
|
||||
|
|
@ -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");
|
||||
|
|
@ -617,14 +870,37 @@ 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;
|
||||
return 0;
|
||||
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 -EINVAL;
|
||||
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;
|
||||
}
|
||||
|
||||
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),
|
||||
};
|
||||
|
|
@ -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,
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -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…
Reference in a new issue