summaryrefslogtreecommitdiff
path: root/drivers/leds
diff options
context:
space:
mode:
authorDevesh Jhunjhunwala <deveshj@codeaurora.org>2016-07-07 18:46:14 -0700
committerDevesh Jhunjhunwala <deveshj@codeaurora.org>2016-08-16 07:51:41 -0700
commit5ee78edab2bbb35684cf5162bada5f1a8cda9699 (patch)
tree31122b9a446d136bc17e346d8bb69d9100fd95d7 /drivers/leds
parente94b446eac88a43e42ecde105275d48b677ea5b3 (diff)
leds: qpnp-flash-v2: Implement max_current_query API
Update the max_current_query stub to calculate the maximum available current for flash based on the battery properties read from fuel gauge. CRs-Fixed: 1043718 Change-Id: I88fd40c449852704361ae3ed3e8223e1e9b3fe74 Signed-off-by: Devesh Jhunjhunwala <deveshj@codeaurora.org>
Diffstat (limited to 'drivers/leds')
-rw-r--r--drivers/leds/leds-qpnp-flash-v2.c151
1 files changed, 150 insertions, 1 deletions
diff --git a/drivers/leds/leds-qpnp-flash-v2.c b/drivers/leds/leds-qpnp-flash-v2.c
index 291720db72ce..4911f645f736 100644
--- a/drivers/leds/leds-qpnp-flash-v2.c
+++ b/drivers/leds/leds-qpnp-flash-v2.c
@@ -64,6 +64,7 @@
#define VPH_DROOP_DEBOUNCE_US_TO_VAL(val_us) (val_us / 8)
#define VPH_DROOP_HYST_MV_TO_VAL(val_mv) (val_mv / 25)
#define VPH_DROOP_THRESH_MV_TO_VAL(val_mv) ((val_mv / 100) - 25)
+#define VPH_DROOP_THRESH_VAL_TO_UV(val) ((val + 25) * 100000)
#define FLASH_LED_ISC_WARMUP_DELAY_SHIFT 6
#define FLASH_LED_WARMUP_DELAY_DEFAULT 2
@@ -74,6 +75,9 @@
#define FLASH_LED_VPH_DROOP_DEBOUNCE_MAX 3
#define FLASH_LED_VPH_DROOP_HYST_MAX 3
#define FLASH_LED_VPH_DROOP_THRESH_MAX 7
+#define FLASH_LED_VLED_MAX_DEFAULT_UV 3500000
+#define FLASH_LED_IBATT_OCP_THRESH_DEFAULT_UA 4500000
+#define FLASH_LED_RPARA_DEFAULT_UOHM 0
#define FLASH_LED_SAFETY_TMR_VAL_OFFSET 1
#define FLASH_LED_SAFETY_TMR_VAL_DIVISOR 10
#define FLASH_LED_SAFETY_TMR_ENABLE BIT(7)
@@ -96,6 +100,7 @@
#define FLASH_LED_DISABLE 0x00
#define FLASH_LED_SAFETY_TMR_DISABLED 0x13
#define FLASH_LED_MIN_CURRENT_MA 25
+#define FLASH_LED_MAX_TOTAL_CURRENT_MA 3750
/* notifier call chain for flash-led irqs */
static ATOMIC_NOTIFIER_HEAD(irq_notifier_list);
@@ -159,6 +164,9 @@ struct flash_led_platform_data {
int all_ramp_up_done_irq;
int all_ramp_down_done_irq;
int led_fault_irq;
+ int ibatt_ocp_threshold_ua;
+ int vled_max_uv;
+ int rpara_uohm;
u8 isc_delay;
u8 warmup_delay;
u8 current_derate_en_cfg;
@@ -364,10 +372,120 @@ out:
return rc;
}
+static int get_property_from_fg(struct qpnp_flash_led *led,
+ enum power_supply_property prop, int *val)
+{
+ int rc;
+ union power_supply_propval pval = {0, };
+
+ if (!led->bms_psy) {
+ dev_err(&led->pdev->dev, "no bms psy found\n");
+ return -EINVAL;
+ }
+
+ rc = power_supply_get_property(led->bms_psy, prop, &pval);
+ if (rc) {
+ dev_err(&led->pdev->dev,
+ "bms psy doesn't support reading prop %d rc = %d\n",
+ prop, rc);
+ return rc;
+ }
+
+ *val = pval.intval;
+ return rc;
+}
+
+#define UCONV 1000000LL
+#define MCONV 1000LL
+#define V_HEADROOM_DEFAULT 350000LL
+#define FLASH_VDIP_MARGIN 50000
+#define BOB_EFFICIENCY 900LL
+#define VIN_FLASH_MIN_UV 3300000LL
+static int qpnp_flash_led_calc_max_current(struct qpnp_flash_led *led)
+{
+ int ocv_uv, rbatt_uohm, ibat_now, rc;
+ int64_t ibat_flash_ua, avail_flash_ua, avail_flash_power_fw;
+ int64_t ibat_safe_ua, vin_flash_uv, vph_flash_uv, vph_flash_vdip;
+
+ /* RESISTANCE = esr_uohm + rslow_uohm */
+ rc = get_property_from_fg(led, POWER_SUPPLY_PROP_RESISTANCE,
+ &rbatt_uohm);
+ if (rc < 0) {
+ dev_err(&led->pdev->dev, "bms psy does not support resistance, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ /* If no battery is connected, return max possible flash current */
+ if (!rbatt_uohm)
+ return FLASH_LED_MAX_TOTAL_CURRENT_MA;
+
+ rc = get_property_from_fg(led, POWER_SUPPLY_PROP_VOLTAGE_OCV, &ocv_uv);
+ if (rc < 0) {
+ dev_err(&led->pdev->dev, "bms psy does not support OCV, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ rc = get_property_from_fg(led, POWER_SUPPLY_PROP_CURRENT_NOW,
+ &ibat_now);
+ if (rc < 0) {
+ dev_err(&led->pdev->dev, "bms psy does not support current, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ rbatt_uohm += led->pdata->rpara_uohm;
+ vph_flash_vdip =
+ VPH_DROOP_THRESH_VAL_TO_UV(led->pdata->vph_droop_threshold)
+ + FLASH_VDIP_MARGIN;
+
+ /*
+ * Calculate the maximum current that can pulled out of the battery
+ * before the battery voltage dips below a safe threshold.
+ */
+ ibat_safe_ua = div_s64((ocv_uv - vph_flash_vdip) * UCONV,
+ rbatt_uohm);
+
+ if (ibat_safe_ua <= led->pdata->ibatt_ocp_threshold_ua) {
+ /*
+ * If the calculated current is below the OCP threshold, then
+ * use it as the possible flash current.
+ */
+ ibat_flash_ua = ibat_safe_ua - ibat_now;
+ vph_flash_uv = vph_flash_vdip;
+ } else {
+ /*
+ * If the calculated current is above the OCP threshold, then
+ * use the ocp threshold instead.
+ *
+ * Any higher current will be tripping the battery OCP.
+ */
+ ibat_flash_ua = led->pdata->ibatt_ocp_threshold_ua - ibat_now;
+ vph_flash_uv = ocv_uv - div64_s64((int64_t)rbatt_uohm
+ * led->pdata->ibatt_ocp_threshold_ua, UCONV);
+ }
+ /* Calculate the input voltage of the flash module. */
+ vin_flash_uv = max((led->pdata->vled_max_uv + V_HEADROOM_DEFAULT),
+ VIN_FLASH_MIN_UV);
+ /* Calculate the available power for the flash module. */
+ avail_flash_power_fw = BOB_EFFICIENCY * vph_flash_uv * ibat_flash_ua;
+ /*
+ * Calculate the available amount of current the flash module can draw
+ * before collapsing the battery. (available power/ flash input voltage)
+ */
+ avail_flash_ua = div64_s64(avail_flash_power_fw, vin_flash_uv * MCONV);
+ dev_dbg(&led->pdev->dev,
+ "avail_iflash=%lld, ocv=%d, ibat=%d, rbatt=%d\n",
+ avail_flash_ua, ocv_uv, ibat_now, rbatt_uohm);
+ return min(FLASH_LED_MAX_TOTAL_CURRENT_MA,
+ (int)(avail_flash_ua / MCONV));
+}
+
static int qpnp_flash_led_get_max_avail_current(struct flash_switch_data *snode,
struct qpnp_flash_led *led)
{
- return 3750;
+ return qpnp_flash_led_calc_max_current(led);
}
static void qpnp_flash_led_node_set(struct flash_node_data *fnode, int value)
@@ -1204,6 +1322,37 @@ static int qpnp_flash_led_parse_common_dt(struct qpnp_flash_led *led,
return rc;
}
+ led->pdata->vled_max_uv = FLASH_LED_VLED_MAX_DEFAULT_UV;
+ rc = of_property_read_u32(node, "qcom,vled-max-uv", &val);
+ if (!rc) {
+ led->pdata->vled_max_uv = val;
+ } else if (rc != -EINVAL) {
+ dev_err(&led->pdev->dev, "Unable to parse vled_max voltage, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ led->pdata->ibatt_ocp_threshold_ua =
+ FLASH_LED_IBATT_OCP_THRESH_DEFAULT_UA;
+ rc = of_property_read_u32(node, "qcom,ibatt-ocp-threshold-ua", &val);
+ if (!rc) {
+ led->pdata->ibatt_ocp_threshold_ua = val;
+ } else if (rc != -EINVAL) {
+ dev_err(&led->pdev->dev, "Unable to parse ibatt_ocp threshold, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ led->pdata->rpara_uohm = FLASH_LED_RPARA_DEFAULT_UOHM;
+ rc = of_property_read_u32(node, "qcom,rparasitic-uohm", &val);
+ if (!rc) {
+ led->pdata->rpara_uohm = val;
+ } else if (rc != -EINVAL) {
+ dev_err(&led->pdev->dev, "Unable to parse rparasitic, rc=%d\n",
+ rc);
+ return rc;
+ }
+
led->pdata->all_ramp_up_done_irq =
of_irq_get_byname(node, "all-ramp-up-done-irq");
if (led->pdata->all_ramp_up_done_irq < 0)