power: supply: rn5t618: Add input current limit
authorAndreas Kemnade <andreas@kemnade.info>
Fri, 28 May 2021 21:30:06 +0000 (23:30 +0200)
committerSebastian Reichel <sebastian.reichel@collabora.com>
Thu, 3 Jun 2021 17:31:23 +0000 (19:31 +0200)
Adds properties for setting the maximum current to USB, ADP and Battery
supplies. USB and ADP limits are reset to OTP values upon replugging.

Signed-off-by: Andreas Kemnade <andreas@kemnade.info>
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
drivers/power/supply/rn5t618_power.c

index f8f00f6..8190619 100644 (file)
 
 #define FG_ENABLE 1
 
+/*
+ * Formula seems accurate for battery current, but for USB current around 70mA
+ * per step was seen on Kobo Clara HD but all sources show the same formula
+ * also fur USB current. To avoid accidentially unwanted high currents we stick
+ * to that formula
+ */
+#define TO_CUR_REG(x) ((x) / 100000 - 1)
+#define FROM_CUR_REG(x) ((((x) & 0x1f) + 1) * 100000)
+#define CHG_MIN_CUR 100000
+#define CHG_MAX_CUR 1800000
+#define ADP_MAX_CUR 2500000
+#define USB_MAX_CUR 1400000
+
+
 struct rn5t618_power_info {
        struct rn5t618 *rn5t618;
        struct platform_device *pdev;
@@ -61,12 +75,16 @@ static enum power_supply_usb_type rn5t618_usb_types[] = {
 };
 
 static enum power_supply_property rn5t618_usb_props[] = {
+       /* input current limit is not very accurate */
+       POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
        POWER_SUPPLY_PROP_STATUS,
        POWER_SUPPLY_PROP_USB_TYPE,
        POWER_SUPPLY_PROP_ONLINE,
 };
 
 static enum power_supply_property rn5t618_adp_props[] = {
+       /* input current limit is not very accurate */
+       POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
        POWER_SUPPLY_PROP_STATUS,
        POWER_SUPPLY_PROP_ONLINE,
 };
@@ -82,6 +100,7 @@ static enum power_supply_property rn5t618_battery_props[] = {
        POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
        POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
        POWER_SUPPLY_PROP_TECHNOLOGY,
+       POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT,
        POWER_SUPPLY_PROP_CHARGE_FULL,
        POWER_SUPPLY_PROP_CHARGE_NOW,
 };
@@ -271,6 +290,36 @@ static int rn5t618_battery_ttf(struct rn5t618_power_info *info,
        return 0;
 }
 
+static int rn5t618_battery_set_current_limit(struct rn5t618_power_info *info,
+                               const union power_supply_propval *val)
+{
+       if (val->intval < CHG_MIN_CUR)
+               return -EINVAL;
+
+       if (val->intval >= CHG_MAX_CUR)
+               return -EINVAL;
+
+       return regmap_update_bits(info->rn5t618->regmap,
+                                 RN5T618_CHGISET,
+                                 0x1F, TO_CUR_REG(val->intval));
+}
+
+static int rn5t618_battery_get_current_limit(struct rn5t618_power_info *info,
+                                            union power_supply_propval *val)
+{
+       unsigned int regval;
+       int ret;
+
+       ret = regmap_read(info->rn5t618->regmap, RN5T618_CHGISET,
+                         &regval);
+       if (ret < 0)
+               return ret;
+
+       val->intval = FROM_CUR_REG(regval);
+
+       return 0;
+}
+
 static int rn5t618_battery_charge_full(struct rn5t618_power_info *info,
                                       union power_supply_propval *val)
 {
@@ -336,6 +385,9 @@ static int rn5t618_battery_get_property(struct power_supply *psy,
        case POWER_SUPPLY_PROP_TECHNOLOGY:
                val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
                break;
+       case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
+               ret = rn5t618_battery_get_current_limit(info, val);
+               break;
        case POWER_SUPPLY_PROP_CHARGE_FULL:
                ret = rn5t618_battery_charge_full(info, val);
                break;
@@ -349,12 +401,38 @@ static int rn5t618_battery_get_property(struct power_supply *psy,
        return ret;
 }
 
+static int rn5t618_battery_set_property(struct power_supply *psy,
+                                       enum power_supply_property psp,
+                                       const union power_supply_propval *val)
+{
+       struct rn5t618_power_info *info = power_supply_get_drvdata(psy);
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
+               return rn5t618_battery_set_current_limit(info, val);
+       default:
+               return -EINVAL;
+       }
+}
+
+static int rn5t618_battery_property_is_writeable(struct power_supply *psy,
+                                               enum power_supply_property psp)
+{
+       switch (psp) {
+       case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
+               return true;
+       default:
+               return false;
+       }
+}
+
 static int rn5t618_adp_get_property(struct power_supply *psy,
                                    enum power_supply_property psp,
                                    union power_supply_propval *val)
 {
        struct rn5t618_power_info *info = power_supply_get_drvdata(psy);
        unsigned int chgstate;
+       unsigned int regval;
        bool online;
        int ret;
 
@@ -378,6 +456,14 @@ static int rn5t618_adp_get_property(struct power_supply *psy,
                        val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
 
                break;
+       case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
+               ret = regmap_read(info->rn5t618->regmap,
+                                 RN5T618_REGISET1, &regval);
+               if (ret < 0)
+                       return ret;
+
+               val->intval = FROM_CUR_REG(regval);
+               break;
        default:
                return -EINVAL;
        }
@@ -385,6 +471,45 @@ static int rn5t618_adp_get_property(struct power_supply *psy,
        return 0;
 }
 
+static int rn5t618_adp_set_property(struct power_supply *psy,
+                                   enum power_supply_property psp,
+                                   const union power_supply_propval *val)
+{
+       struct rn5t618_power_info *info = power_supply_get_drvdata(psy);
+       int ret;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
+               if (val->intval > ADP_MAX_CUR)
+                       return -EINVAL;
+
+               if (val->intval < CHG_MIN_CUR)
+                       return -EINVAL;
+
+               ret = regmap_write(info->rn5t618->regmap, RN5T618_REGISET1,
+                                  TO_CUR_REG(val->intval));
+               if (ret < 0)
+                       return ret;
+
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int rn5t618_adp_property_is_writeable(struct power_supply *psy,
+                                            enum power_supply_property psp)
+{
+       switch (psp) {
+       case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
+               return true;
+       default:
+               return false;
+       }
+}
+
 static int rc5t619_usb_get_type(struct rn5t618_power_info *info,
                                union power_supply_propval *val)
 {
@@ -418,6 +543,7 @@ static int rn5t618_usb_get_property(struct power_supply *psy,
 {
        struct rn5t618_power_info *info = power_supply_get_drvdata(psy);
        unsigned int chgstate;
+       unsigned int regval;
        bool online;
        int ret;
 
@@ -446,6 +572,23 @@ static int rn5t618_usb_get_property(struct power_supply *psy,
                        return -ENODATA;
 
                return rc5t619_usb_get_type(info, val);
+       case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
+               ret = regmap_read(info->rn5t618->regmap, RN5T618_CHGCTL1,
+                                 &regval);
+               if (ret < 0)
+                       return ret;
+
+               val->intval = 0;
+               if (regval & 2) {
+                       ret = regmap_read(info->rn5t618->regmap,
+                                         RN5T618_REGISET2,
+                                         &regval);
+                       if (ret < 0)
+                               return ret;
+
+                       val->intval = FROM_CUR_REG(regval);
+               }
+               break;
        default:
                return -EINVAL;
        }
@@ -453,12 +596,53 @@ static int rn5t618_usb_get_property(struct power_supply *psy,
        return 0;
 }
 
+static int rn5t618_usb_set_property(struct power_supply *psy,
+                                   enum power_supply_property psp,
+                                   const union power_supply_propval *val)
+{
+       struct rn5t618_power_info *info = power_supply_get_drvdata(psy);
+       int ret;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
+               if (val->intval > USB_MAX_CUR)
+                       return -EINVAL;
+
+               if (val->intval < CHG_MIN_CUR)
+                       return -EINVAL;
+
+               ret = regmap_write(info->rn5t618->regmap, RN5T618_REGISET2,
+                                  0xE0 | TO_CUR_REG(val->intval));
+               if (ret < 0)
+                       return ret;
+
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int rn5t618_usb_property_is_writeable(struct power_supply *psy,
+                                            enum power_supply_property psp)
+{
+       switch (psp) {
+       case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
+               return true;
+       default:
+               return false;
+       }
+}
+
 static const struct power_supply_desc rn5t618_battery_desc = {
        .name                   = "rn5t618-battery",
        .type                   = POWER_SUPPLY_TYPE_BATTERY,
        .properties             = rn5t618_battery_props,
        .num_properties         = ARRAY_SIZE(rn5t618_battery_props),
        .get_property           = rn5t618_battery_get_property,
+       .set_property           = rn5t618_battery_set_property,
+       .property_is_writeable  = rn5t618_battery_property_is_writeable,
 };
 
 static const struct power_supply_desc rn5t618_adp_desc = {
@@ -467,6 +651,8 @@ static const struct power_supply_desc rn5t618_adp_desc = {
        .properties             = rn5t618_adp_props,
        .num_properties         = ARRAY_SIZE(rn5t618_adp_props),
        .get_property           = rn5t618_adp_get_property,
+       .set_property           = rn5t618_adp_set_property,
+       .property_is_writeable  = rn5t618_adp_property_is_writeable,
 };
 
 static const struct power_supply_desc rn5t618_usb_desc = {
@@ -477,6 +663,8 @@ static const struct power_supply_desc rn5t618_usb_desc = {
        .properties             = rn5t618_usb_props,
        .num_properties         = ARRAY_SIZE(rn5t618_usb_props),
        .get_property           = rn5t618_usb_get_property,
+       .set_property           = rn5t618_usb_set_property,
+       .property_is_writeable  = rn5t618_usb_property_is_writeable,
 };
 
 static irqreturn_t rn5t618_charger_irq(int irq, void *data)