diff options
author | Johan Bjornstedt <johan.bjornstedt@stericsson.com> | 2012-01-18 06:44:55 -0500 |
---|---|---|
committer | Lee Jones <lee.jones@linaro.org> | 2012-12-11 03:43:51 -0500 |
commit | f8e96dff240982c1433d447bae533acc36b5cf8f (patch) | |
tree | 59c364b5fd5873bea4bd0c8fc36d9cd10a632ebb | |
parent | f5695a39833a2946479f6215f3afc4f672333713 (diff) |
ab8500_charger: Charger current step-up/down
There is no state machine in the AB to step up/down the charger
current to avoid dips and spikes on VBUS and VBAT when charging
is started. Instead this is implemented in SW.
Signed-off-by: Johan Bjornstedt <johan.bjornstedt@stericsson.com>
Signed-off-by: Mattias Wallin <mattias.wallin@stericsson.com>
Signed-off-by: Lee Jones <lee.jones@linaro.org>
Reviewed-by: Karl KOMIEROWSKI <karl.komierowski@stericsson.com>
-rw-r--r-- | drivers/power/ab8500_charger.c | 175 |
1 files changed, 135 insertions, 40 deletions
diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c index dddc9473f72a..d27dd7fec163 100644 --- a/drivers/power/ab8500_charger.c +++ b/drivers/power/ab8500_charger.c | |||
@@ -79,6 +79,9 @@ | |||
79 | /* Lowest charger voltage is 3.39V -> 0x4E */ | 79 | /* Lowest charger voltage is 3.39V -> 0x4E */ |
80 | #define LOW_VOLT_REG 0x4E | 80 | #define LOW_VOLT_REG 0x4E |
81 | 81 | ||
82 | /* Step up/down delay in us */ | ||
83 | #define STEP_UDELAY 1000 | ||
84 | |||
82 | /* UsbLineStatus register - usb types */ | 85 | /* UsbLineStatus register - usb types */ |
83 | enum ab8500_charger_link_status { | 86 | enum ab8500_charger_link_status { |
84 | USB_STAT_NOT_CONFIGURED, | 87 | USB_STAT_NOT_CONFIGURED, |
@@ -936,6 +939,88 @@ static int ab8500_charger_get_usb_cur(struct ab8500_charger *di) | |||
936 | } | 939 | } |
937 | 940 | ||
938 | /** | 941 | /** |
942 | * ab8500_charger_set_current() - set charger current | ||
943 | * @di: pointer to the ab8500_charger structure | ||
944 | * @ich: charger current, in mA | ||
945 | * @reg: select what charger register to set | ||
946 | * | ||
947 | * Set charger current. | ||
948 | * There is no state machine in the AB to step up/down the charger | ||
949 | * current to avoid dips and spikes on MAIN, VBUS and VBAT when | ||
950 | * charging is started. Instead we need to implement | ||
951 | * this charger current step-up/down here. | ||
952 | * Returns error code in case of failure else 0(on success) | ||
953 | */ | ||
954 | static int ab8500_charger_set_current(struct ab8500_charger *di, | ||
955 | int ich, int reg) | ||
956 | { | ||
957 | int ret, i; | ||
958 | int curr_index, prev_curr_index, shift_value; | ||
959 | u8 reg_value; | ||
960 | |||
961 | switch (reg) { | ||
962 | case AB8500_MCH_IPT_CURLVL_REG: | ||
963 | shift_value = MAIN_CH_INPUT_CURR_SHIFT; | ||
964 | curr_index = ab8500_current_to_regval(ich); | ||
965 | break; | ||
966 | case AB8500_USBCH_IPT_CRNTLVL_REG: | ||
967 | shift_value = VBUS_IN_CURR_LIM_SHIFT; | ||
968 | curr_index = ab8500_vbus_in_curr_to_regval(ich); | ||
969 | break; | ||
970 | case AB8500_CH_OPT_CRNTLVL_REG: | ||
971 | shift_value = 0; | ||
972 | curr_index = ab8500_current_to_regval(ich); | ||
973 | break; | ||
974 | default: | ||
975 | dev_err(di->dev, "%s current register not valid\n", __func__); | ||
976 | return -ENXIO; | ||
977 | } | ||
978 | |||
979 | if (curr_index < 0) { | ||
980 | dev_err(di->dev, "requested current limit out-of-range\n"); | ||
981 | return -ENXIO; | ||
982 | } | ||
983 | |||
984 | ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER, | ||
985 | reg, ®_value); | ||
986 | if (ret < 0) { | ||
987 | dev_err(di->dev, "%s read failed\n", __func__); | ||
988 | return ret; | ||
989 | } | ||
990 | prev_curr_index = (reg_value >> shift_value); | ||
991 | |||
992 | /* only update current if it's been changed */ | ||
993 | if (prev_curr_index == curr_index) | ||
994 | return 0; | ||
995 | |||
996 | dev_dbg(di->dev, "%s set charger current: %d mA for reg: 0x%02x\n", | ||
997 | __func__, ich, reg); | ||
998 | |||
999 | if (prev_curr_index > curr_index) { | ||
1000 | for (i = prev_curr_index - 1; i >= curr_index; i--) { | ||
1001 | ret = abx500_set_register_interruptible(di->dev, | ||
1002 | AB8500_CHARGER, reg, (u8) i << shift_value); | ||
1003 | if (ret) { | ||
1004 | dev_err(di->dev, "%s write failed\n", __func__); | ||
1005 | return ret; | ||
1006 | } | ||
1007 | usleep_range(STEP_UDELAY, STEP_UDELAY * 2); | ||
1008 | } | ||
1009 | } else { | ||
1010 | for (i = prev_curr_index + 1; i <= curr_index; i++) { | ||
1011 | ret = abx500_set_register_interruptible(di->dev, | ||
1012 | AB8500_CHARGER, reg, (u8) i << shift_value); | ||
1013 | if (ret) { | ||
1014 | dev_err(di->dev, "%s write failed\n", __func__); | ||
1015 | return ret; | ||
1016 | } | ||
1017 | usleep_range(STEP_UDELAY, STEP_UDELAY * 2); | ||
1018 | } | ||
1019 | } | ||
1020 | return ret; | ||
1021 | } | ||
1022 | |||
1023 | /** | ||
939 | * ab8500_charger_set_vbus_in_curr() - set VBUS input current limit | 1024 | * ab8500_charger_set_vbus_in_curr() - set VBUS input current limit |
940 | * @di: pointer to the ab8500_charger structure | 1025 | * @di: pointer to the ab8500_charger structure |
941 | * @ich_in: charger input current limit | 1026 | * @ich_in: charger input current limit |
@@ -946,8 +1031,6 @@ static int ab8500_charger_get_usb_cur(struct ab8500_charger *di) | |||
946 | static int ab8500_charger_set_vbus_in_curr(struct ab8500_charger *di, | 1031 | static int ab8500_charger_set_vbus_in_curr(struct ab8500_charger *di, |
947 | int ich_in) | 1032 | int ich_in) |
948 | { | 1033 | { |
949 | int ret; | ||
950 | int input_curr_index; | ||
951 | int min_value; | 1034 | int min_value; |
952 | 1035 | ||
953 | /* We should always use to lowest current limit */ | 1036 | /* We should always use to lowest current limit */ |
@@ -966,19 +1049,38 @@ static int ab8500_charger_set_vbus_in_curr(struct ab8500_charger *di, | |||
966 | break; | 1049 | break; |
967 | } | 1050 | } |
968 | 1051 | ||
969 | input_curr_index = ab8500_vbus_in_curr_to_regval(min_value); | 1052 | return ab8500_charger_set_current(di, min_value, |
970 | if (input_curr_index < 0) { | 1053 | AB8500_USBCH_IPT_CRNTLVL_REG); |
971 | dev_err(di->dev, "VBUS input current limit too high\n"); | 1054 | } |
972 | return -ENXIO; | ||
973 | } | ||
974 | 1055 | ||
975 | ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER, | 1056 | /** |
976 | AB8500_USBCH_IPT_CRNTLVL_REG, | 1057 | * ab8500_charger_set_main_in_curr() - set main charger input current |
977 | input_curr_index << VBUS_IN_CURR_LIM_SHIFT); | 1058 | * @di: pointer to the ab8500_charger structure |
978 | if (ret) | 1059 | * @ich_in: input charger current, in mA |
979 | dev_err(di->dev, "%s write failed\n", __func__); | 1060 | * |
1061 | * Set main charger input current. | ||
1062 | * Returns error code in case of failure else 0(on success) | ||
1063 | */ | ||
1064 | static int ab8500_charger_set_main_in_curr(struct ab8500_charger *di, | ||
1065 | int ich_in) | ||
1066 | { | ||
1067 | return ab8500_charger_set_current(di, ich_in, | ||
1068 | AB8500_MCH_IPT_CURLVL_REG); | ||
1069 | } | ||
980 | 1070 | ||
981 | return ret; | 1071 | /** |
1072 | * ab8500_charger_set_output_curr() - set charger output current | ||
1073 | * @di: pointer to the ab8500_charger structure | ||
1074 | * @ich_out: output charger current, in mA | ||
1075 | * | ||
1076 | * Set charger output current. | ||
1077 | * Returns error code in case of failure else 0(on success) | ||
1078 | */ | ||
1079 | static int ab8500_charger_set_output_curr(struct ab8500_charger *di, | ||
1080 | int ich_out) | ||
1081 | { | ||
1082 | return ab8500_charger_set_current(di, ich_out, | ||
1083 | AB8500_CH_OPT_CRNTLVL_REG); | ||
982 | } | 1084 | } |
983 | 1085 | ||
984 | /** | 1086 | /** |
@@ -1090,18 +1192,19 @@ static int ab8500_charger_ac_en(struct ux500_charger *charger, | |||
1090 | return ret; | 1192 | return ret; |
1091 | } | 1193 | } |
1092 | /* MainChInputCurr: current that can be drawn from the charger*/ | 1194 | /* MainChInputCurr: current that can be drawn from the charger*/ |
1093 | ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER, | 1195 | ret = ab8500_charger_set_main_in_curr(di, |
1094 | AB8500_MCH_IPT_CURLVL_REG, | 1196 | di->bat->chg_params->ac_curr_max); |
1095 | input_curr_index << MAIN_CH_INPUT_CURR_SHIFT); | ||
1096 | if (ret) { | 1197 | if (ret) { |
1097 | dev_err(di->dev, "%s write failed\n", __func__); | 1198 | dev_err(di->dev, "%s Failed to set MainChInputCurr\n", |
1199 | __func__); | ||
1098 | return ret; | 1200 | return ret; |
1099 | } | 1201 | } |
1100 | /* ChOutputCurentLevel: protected output current */ | 1202 | /* ChOutputCurentLevel: protected output current */ |
1101 | ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER, | 1203 | ret = ab8500_charger_set_output_curr(di, iset); |
1102 | AB8500_CH_OPT_CRNTLVL_REG, (u8) curr_index); | ||
1103 | if (ret) { | 1204 | if (ret) { |
1104 | dev_err(di->dev, "%s write failed\n", __func__); | 1205 | dev_err(di->dev, "%s " |
1206 | "Failed to set ChOutputCurentLevel\n", | ||
1207 | __func__); | ||
1105 | return ret; | 1208 | return ret; |
1106 | } | 1209 | } |
1107 | 1210 | ||
@@ -1158,12 +1261,11 @@ static int ab8500_charger_ac_en(struct ux500_charger *charger, | |||
1158 | return ret; | 1261 | return ret; |
1159 | } | 1262 | } |
1160 | 1263 | ||
1161 | ret = abx500_set_register_interruptible(di->dev, | 1264 | ret = ab8500_charger_set_output_curr(di, 0); |
1162 | AB8500_CHARGER, | ||
1163 | AB8500_CH_OPT_CRNTLVL_REG, CH_OP_CUR_LVL_0P1); | ||
1164 | if (ret) { | 1265 | if (ret) { |
1165 | dev_err(di->dev, | 1266 | dev_err(di->dev, "%s " |
1166 | "%s write failed\n", __func__); | 1267 | "Failed to set ChOutputCurentLevel\n", |
1268 | __func__); | ||
1167 | return ret; | 1269 | return ret; |
1168 | } | 1270 | } |
1169 | } else { | 1271 | } else { |
@@ -1266,10 +1368,11 @@ static int ab8500_charger_usb_en(struct ux500_charger *charger, | |||
1266 | return ret; | 1368 | return ret; |
1267 | } | 1369 | } |
1268 | /* ChOutputCurentLevel: protected output current */ | 1370 | /* ChOutputCurentLevel: protected output current */ |
1269 | ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER, | 1371 | ret = ab8500_charger_set_output_curr(di, ich_out); |
1270 | AB8500_CH_OPT_CRNTLVL_REG, (u8) curr_index); | ||
1271 | if (ret) { | 1372 | if (ret) { |
1272 | dev_err(di->dev, "%s write failed\n", __func__); | 1373 | dev_err(di->dev, "%s " |
1374 | "Failed to set ChOutputCurentLevel\n", | ||
1375 | __func__); | ||
1273 | return ret; | 1376 | return ret; |
1274 | } | 1377 | } |
1275 | /* Check if VBAT overshoot control should be enabled */ | 1378 | /* Check if VBAT overshoot control should be enabled */ |
@@ -1366,7 +1469,6 @@ static int ab8500_charger_update_charger_current(struct ux500_charger *charger, | |||
1366 | int ich_out) | 1469 | int ich_out) |
1367 | { | 1470 | { |
1368 | int ret; | 1471 | int ret; |
1369 | int curr_index; | ||
1370 | struct ab8500_charger *di; | 1472 | struct ab8500_charger *di; |
1371 | 1473 | ||
1372 | if (charger->psy.type == POWER_SUPPLY_TYPE_MAINS) | 1474 | if (charger->psy.type == POWER_SUPPLY_TYPE_MAINS) |
@@ -1376,18 +1478,11 @@ static int ab8500_charger_update_charger_current(struct ux500_charger *charger, | |||
1376 | else | 1478 | else |
1377 | return -ENXIO; | 1479 | return -ENXIO; |
1378 | 1480 | ||
1379 | curr_index = ab8500_current_to_regval(ich_out); | 1481 | ret = ab8500_charger_set_output_curr(di, ich_out); |
1380 | if (curr_index < 0) { | ||
1381 | dev_err(di->dev, | ||
1382 | "Charger current too high, " | ||
1383 | "charging not started\n"); | ||
1384 | return -ENXIO; | ||
1385 | } | ||
1386 | |||
1387 | ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER, | ||
1388 | AB8500_CH_OPT_CRNTLVL_REG, (u8) curr_index); | ||
1389 | if (ret) { | 1482 | if (ret) { |
1390 | dev_err(di->dev, "%s write failed\n", __func__); | 1483 | dev_err(di->dev, "%s " |
1484 | "Failed to set ChOutputCurentLevel\n", | ||
1485 | __func__); | ||
1391 | return ret; | 1486 | return ret; |
1392 | } | 1487 | } |
1393 | 1488 | ||