Merge "leds: qti-flash: Add maximum available flash current prediction"

This commit is contained in:
qctecmdr 2020-02-28 09:29:38 -08:00 • committed by Gerrit - the friendly Code Review server
commit 41dfbf027b

View file

@ -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,