diff options
| author | Chanwoo Choi <cw00.choi@samsung.com> | 2012-07-12 02:03:25 -0400 |
|---|---|---|
| committer | Anton Vorontsov <anton.vorontsov@linaro.org> | 2012-07-13 21:31:13 -0400 |
| commit | bee737bccb03ebd27f2d52706e9aed2fa2c8dcc4 (patch) | |
| tree | bf652913262e9e0504324c090b5893c58c936396 | |
| parent | 85a392d47cac8fa9258c5609a7b02adade961076 (diff) | |
charger-manager: Use EXTCON Subsystem to detect charger cables for charging
This patch support that charger-manager use EXTCON(External Connector)
Subsystem to detect the state of charger cables for enabling or disabling
charger(regulator) and select the charger cable for charging among
a number of external cable according to policy of H/W board.
Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
Signed-off-by: Myungjoo Ham <myungjoo.ham@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Signed-off-by: Anton Vorontsov <anton.vorontsov@linaro.org>
| -rw-r--r-- | drivers/power/charger-manager.c | 137 | ||||
| -rw-r--r-- | include/linux/power/charger-manager.h | 59 |
2 files changed, 179 insertions, 17 deletions
diff --git a/drivers/power/charger-manager.c b/drivers/power/charger-manager.c index 86935ec18954..d1e99e7957d2 100644 --- a/drivers/power/charger-manager.c +++ b/drivers/power/charger-manager.c | |||
| @@ -271,16 +271,13 @@ static int try_charger_enable(struct charger_manager *cm, bool enable) | |||
| 271 | if (enable) { | 271 | if (enable) { |
| 272 | if (cm->emergency_stop) | 272 | if (cm->emergency_stop) |
| 273 | return -EAGAIN; | 273 | return -EAGAIN; |
| 274 | err = regulator_bulk_enable(desc->num_charger_regulators, | 274 | for (i = 0 ; i < desc->num_charger_regulators ; i++) |
| 275 | desc->charger_regulators); | 275 | regulator_enable(desc->charger_regulators[i].consumer); |
| 276 | } else { | 276 | } else { |
| 277 | /* | 277 | /* |
| 278 | * Abnormal battery state - Stop charging forcibly, | 278 | * Abnormal battery state - Stop charging forcibly, |
| 279 | * even if charger was enabled at the other places | 279 | * even if charger was enabled at the other places |
| 280 | */ | 280 | */ |
| 281 | err = regulator_bulk_disable(desc->num_charger_regulators, | ||
| 282 | desc->charger_regulators); | ||
| 283 | |||
| 284 | for (i = 0; i < desc->num_charger_regulators; i++) { | 281 | for (i = 0; i < desc->num_charger_regulators; i++) { |
| 285 | if (regulator_is_enabled( | 282 | if (regulator_is_enabled( |
| 286 | desc->charger_regulators[i].consumer)) { | 283 | desc->charger_regulators[i].consumer)) { |
| @@ -288,7 +285,7 @@ static int try_charger_enable(struct charger_manager *cm, bool enable) | |||
| 288 | desc->charger_regulators[i].consumer); | 285 | desc->charger_regulators[i].consumer); |
| 289 | dev_warn(cm->dev, | 286 | dev_warn(cm->dev, |
| 290 | "Disable regulator(%s) forcibly.\n", | 287 | "Disable regulator(%s) forcibly.\n", |
| 291 | desc->charger_regulators[i].supply); | 288 | desc->charger_regulators[i].regulator_name); |
| 292 | } | 289 | } |
| 293 | } | 290 | } |
| 294 | } | 291 | } |
| @@ -994,11 +991,77 @@ int setup_charger_manager(struct charger_global_desc *gd) | |||
| 994 | } | 991 | } |
| 995 | EXPORT_SYMBOL_GPL(setup_charger_manager); | 992 | EXPORT_SYMBOL_GPL(setup_charger_manager); |
| 996 | 993 | ||
| 994 | /** | ||
| 995 | * charger_extcon_work - enable/diable charger according to the state | ||
| 996 | * of charger cable | ||
| 997 | * | ||
| 998 | * @work: work_struct of the function charger_extcon_work. | ||
| 999 | */ | ||
| 1000 | static void charger_extcon_work(struct work_struct *work) | ||
| 1001 | { | ||
| 1002 | struct charger_cable *cable = | ||
| 1003 | container_of(work, struct charger_cable, wq); | ||
| 1004 | |||
| 1005 | try_charger_enable(cable->cm, cable->attached); | ||
| 1006 | } | ||
| 1007 | |||
| 1008 | /** | ||
| 1009 | * charger_extcon_notifier - receive the state of charger cable | ||
| 1010 | * when registered cable is attached or detached. | ||
| 1011 | * | ||
| 1012 | * @self: the notifier block of the charger_extcon_notifier. | ||
| 1013 | * @event: the cable state. | ||
| 1014 | * @ptr: the data pointer of notifier block. | ||
| 1015 | */ | ||
| 1016 | static int charger_extcon_notifier(struct notifier_block *self, | ||
| 1017 | unsigned long event, void *ptr) | ||
| 1018 | { | ||
| 1019 | struct charger_cable *cable = | ||
| 1020 | container_of(self, struct charger_cable, nb); | ||
| 1021 | |||
| 1022 | cable->attached = event; | ||
| 1023 | schedule_work(&cable->wq); | ||
| 1024 | |||
| 1025 | return NOTIFY_DONE; | ||
| 1026 | } | ||
| 1027 | |||
| 1028 | /** | ||
| 1029 | * charger_extcon_init - register external connector to use it | ||
| 1030 | * as the charger cable | ||
| 1031 | * | ||
| 1032 | * @cm: the Charger Manager representing the battery. | ||
| 1033 | * @cable: the Charger cable representing the external connector. | ||
| 1034 | */ | ||
| 1035 | static int charger_extcon_init(struct charger_manager *cm, | ||
| 1036 | struct charger_cable *cable) | ||
| 1037 | { | ||
| 1038 | int ret = 0; | ||
| 1039 | |||
| 1040 | /* | ||
| 1041 | * Charger manager use Extcon framework to identify | ||
| 1042 | * the charger cable among various external connector | ||
| 1043 | * cable (e.g., TA, USB, MHL, Dock). | ||
| 1044 | */ | ||
| 1045 | INIT_WORK(&cable->wq, charger_extcon_work); | ||
| 1046 | cable->nb.notifier_call = charger_extcon_notifier; | ||
| 1047 | ret = extcon_register_interest(&cable->extcon_dev, | ||
| 1048 | cable->extcon_name, cable->name, &cable->nb); | ||
| 1049 | if (ret < 0) { | ||
| 1050 | pr_info("Cannot register extcon_dev for %s(cable: %s).\n", | ||
| 1051 | cable->extcon_name, | ||
| 1052 | cable->name); | ||
| 1053 | ret = -EINVAL; | ||
| 1054 | } | ||
| 1055 | |||
| 1056 | return ret; | ||
| 1057 | } | ||
| 1058 | |||
| 997 | static int charger_manager_probe(struct platform_device *pdev) | 1059 | static int charger_manager_probe(struct platform_device *pdev) |
| 998 | { | 1060 | { |
| 999 | struct charger_desc *desc = dev_get_platdata(&pdev->dev); | 1061 | struct charger_desc *desc = dev_get_platdata(&pdev->dev); |
| 1000 | struct charger_manager *cm; | 1062 | struct charger_manager *cm; |
| 1001 | int ret = 0, i = 0; | 1063 | int ret = 0, i = 0; |
| 1064 | int j = 0; | ||
| 1002 | union power_supply_propval val; | 1065 | union power_supply_propval val; |
| 1003 | 1066 | ||
| 1004 | if (g_desc && !rtc_dev && g_desc->rtc_name) { | 1067 | if (g_desc && !rtc_dev && g_desc->rtc_name) { |
| @@ -1167,11 +1230,31 @@ static int charger_manager_probe(struct platform_device *pdev) | |||
| 1167 | goto err_register; | 1230 | goto err_register; |
| 1168 | } | 1231 | } |
| 1169 | 1232 | ||
| 1170 | ret = regulator_bulk_get(&pdev->dev, desc->num_charger_regulators, | 1233 | for (i = 0 ; i < desc->num_charger_regulators ; i++) { |
| 1171 | desc->charger_regulators); | 1234 | struct charger_regulator *charger |
| 1172 | if (ret) { | 1235 | = &desc->charger_regulators[i]; |
| 1173 | dev_err(&pdev->dev, "Cannot get charger regulators.\n"); | 1236 | |
| 1174 | goto err_bulk_get; | 1237 | charger->consumer = regulator_get(&pdev->dev, |
| 1238 | charger->regulator_name); | ||
| 1239 | if (charger->consumer == NULL) { | ||
| 1240 | dev_err(&pdev->dev, "Cannot find charger(%s)n", | ||
| 1241 | charger->regulator_name); | ||
| 1242 | ret = -EINVAL; | ||
| 1243 | goto err_chg_get; | ||
| 1244 | } | ||
| 1245 | |||
| 1246 | for (j = 0 ; j < charger->num_cables ; j++) { | ||
| 1247 | struct charger_cable *cable = &charger->cables[j]; | ||
| 1248 | |||
| 1249 | ret = charger_extcon_init(cm, cable); | ||
| 1250 | if (ret < 0) { | ||
| 1251 | dev_err(&pdev->dev, "Cannot find charger(%s)n", | ||
| 1252 | charger->regulator_name); | ||
| 1253 | goto err_extcon; | ||
| 1254 | } | ||
| 1255 | cable->charger = charger; | ||
| 1256 | cable->cm = cm; | ||
| 1257 | } | ||
| 1175 | } | 1258 | } |
| 1176 | 1259 | ||
| 1177 | ret = try_charger_enable(cm, true); | 1260 | ret = try_charger_enable(cm, true); |
| @@ -1197,9 +1280,19 @@ static int charger_manager_probe(struct platform_device *pdev) | |||
| 1197 | return 0; | 1280 | return 0; |
| 1198 | 1281 | ||
| 1199 | err_chg_enable: | 1282 | err_chg_enable: |
| 1200 | regulator_bulk_free(desc->num_charger_regulators, | 1283 | err_extcon: |
| 1201 | desc->charger_regulators); | 1284 | for (i = 0 ; i < desc->num_charger_regulators ; i++) { |
| 1202 | err_bulk_get: | 1285 | struct charger_regulator *charger |
| 1286 | = &desc->charger_regulators[i]; | ||
| 1287 | for (j = 0 ; j < charger->num_cables ; j++) { | ||
| 1288 | struct charger_cable *cable = &charger->cables[j]; | ||
| 1289 | extcon_unregister_interest(&cable->extcon_dev); | ||
| 1290 | } | ||
| 1291 | } | ||
| 1292 | err_chg_get: | ||
| 1293 | for (i = 0 ; i < desc->num_charger_regulators ; i++) | ||
| 1294 | regulator_put(desc->charger_regulators[i].consumer); | ||
| 1295 | |||
| 1203 | power_supply_unregister(&cm->charger_psy); | 1296 | power_supply_unregister(&cm->charger_psy); |
| 1204 | err_register: | 1297 | err_register: |
| 1205 | kfree(cm->charger_psy.properties); | 1298 | kfree(cm->charger_psy.properties); |
| @@ -1218,6 +1311,8 @@ static int __devexit charger_manager_remove(struct platform_device *pdev) | |||
| 1218 | { | 1311 | { |
| 1219 | struct charger_manager *cm = platform_get_drvdata(pdev); | 1312 | struct charger_manager *cm = platform_get_drvdata(pdev); |
| 1220 | struct charger_desc *desc = cm->desc; | 1313 | struct charger_desc *desc = cm->desc; |
| 1314 | int i = 0; | ||
| 1315 | int j = 0; | ||
| 1221 | 1316 | ||
| 1222 | /* Remove from the list */ | 1317 | /* Remove from the list */ |
| 1223 | mutex_lock(&cm_list_mtx); | 1318 | mutex_lock(&cm_list_mtx); |
| @@ -1229,8 +1324,18 @@ static int __devexit charger_manager_remove(struct platform_device *pdev) | |||
| 1229 | if (delayed_work_pending(&cm_monitor_work)) | 1324 | if (delayed_work_pending(&cm_monitor_work)) |
| 1230 | cancel_delayed_work_sync(&cm_monitor_work); | 1325 | cancel_delayed_work_sync(&cm_monitor_work); |
| 1231 | 1326 | ||
| 1232 | regulator_bulk_free(desc->num_charger_regulators, | 1327 | for (i = 0 ; i < desc->num_charger_regulators ; i++) { |
| 1233 | desc->charger_regulators); | 1328 | struct charger_regulator *charger |
| 1329 | = &desc->charger_regulators[i]; | ||
| 1330 | for (j = 0 ; j < charger->num_cables ; j++) { | ||
| 1331 | struct charger_cable *cable = &charger->cables[j]; | ||
| 1332 | extcon_unregister_interest(&cable->extcon_dev); | ||
| 1333 | } | ||
| 1334 | } | ||
| 1335 | |||
| 1336 | for (i = 0 ; i < desc->num_charger_regulators ; i++) | ||
| 1337 | regulator_put(desc->charger_regulators[i].consumer); | ||
| 1338 | |||
| 1234 | power_supply_unregister(&cm->charger_psy); | 1339 | power_supply_unregister(&cm->charger_psy); |
| 1235 | 1340 | ||
| 1236 | try_charger_enable(cm, false); | 1341 | try_charger_enable(cm, false); |
diff --git a/include/linux/power/charger-manager.h b/include/linux/power/charger-manager.h index 241065c9ce51..6cb9fbc9a153 100644 --- a/include/linux/power/charger-manager.h +++ b/include/linux/power/charger-manager.h | |||
| @@ -16,6 +16,7 @@ | |||
| 16 | #define _CHARGER_MANAGER_H | 16 | #define _CHARGER_MANAGER_H |
| 17 | 17 | ||
| 18 | #include <linux/power_supply.h> | 18 | #include <linux/power_supply.h> |
| 19 | #include <linux/extcon.h> | ||
| 19 | 20 | ||
| 20 | enum data_source { | 21 | enum data_source { |
| 21 | CM_BATTERY_PRESENT, | 22 | CM_BATTERY_PRESENT, |
| @@ -65,6 +66,62 @@ struct charger_global_desc { | |||
| 65 | }; | 66 | }; |
| 66 | 67 | ||
| 67 | /** | 68 | /** |
| 69 | * struct charger_cable | ||
| 70 | * @extcon_name: the name of extcon device. | ||
| 71 | * @name: the name of charger cable(external connector). | ||
| 72 | * @extcon_dev: the extcon device. | ||
| 73 | * @wq: the workqueue to control charger according to the state of | ||
| 74 | * charger cable. If charger cable is attached, enable charger. | ||
| 75 | * But if charger cable is detached, disable charger. | ||
| 76 | * @nb: the notifier block to receive changed state from EXTCON | ||
| 77 | * (External Connector) when charger cable is attached/detached. | ||
| 78 | * @attached: the state of charger cable. | ||
| 79 | * true: the charger cable is attached | ||
| 80 | * false: the charger cable is detached | ||
| 81 | * @charger: the instance of struct charger_regulator. | ||
| 82 | * @cm: the Charger Manager representing the battery. | ||
| 83 | */ | ||
| 84 | struct charger_cable { | ||
| 85 | const char *extcon_name; | ||
| 86 | const char *name; | ||
| 87 | |||
| 88 | /* The charger-manager use Exton framework*/ | ||
| 89 | struct extcon_specific_cable_nb extcon_dev; | ||
| 90 | struct work_struct wq; | ||
| 91 | struct notifier_block nb; | ||
| 92 | |||
| 93 | /* The state of charger cable */ | ||
| 94 | bool attached; | ||
| 95 | |||
| 96 | struct charger_regulator *charger; | ||
| 97 | struct charger_manager *cm; | ||
| 98 | }; | ||
| 99 | |||
| 100 | /** | ||
| 101 | * struct charger_regulator | ||
| 102 | * @regulator_name: the name of regulator for using charger. | ||
| 103 | * @consumer: the regulator consumer for the charger. | ||
| 104 | * @cables: | ||
| 105 | * the array of charger cables to enable/disable charger | ||
| 106 | * and set current limit according to constratint data of | ||
| 107 | * struct charger_cable if only charger cable included | ||
| 108 | * in the array of charger cables is attached/detached. | ||
| 109 | * @num_cables: the number of charger cables. | ||
| 110 | */ | ||
| 111 | struct charger_regulator { | ||
| 112 | /* The name of regulator for charging */ | ||
| 113 | const char *regulator_name; | ||
| 114 | struct regulator *consumer; | ||
| 115 | |||
| 116 | /* | ||
| 117 | * Store constraint information related to current limit, | ||
| 118 | * each cable have different condition for charging. | ||
| 119 | */ | ||
| 120 | struct charger_cable *cables; | ||
| 121 | int num_cables; | ||
| 122 | }; | ||
| 123 | |||
| 124 | /** | ||
| 68 | * struct charger_desc | 125 | * struct charger_desc |
| 69 | * @psy_name: the name of power-supply-class for charger manager | 126 | * @psy_name: the name of power-supply-class for charger manager |
| 70 | * @polling_mode: | 127 | * @polling_mode: |
| @@ -109,7 +166,7 @@ struct charger_desc { | |||
| 109 | char **psy_charger_stat; | 166 | char **psy_charger_stat; |
| 110 | 167 | ||
| 111 | int num_charger_regulators; | 168 | int num_charger_regulators; |
| 112 | struct regulator_bulk_data *charger_regulators; | 169 | struct charger_regulator *charger_regulators; |
| 113 | 170 | ||
| 114 | char *psy_fuel_gauge; | 171 | char *psy_fuel_gauge; |
| 115 | 172 | ||
