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