From d36240d26025bec95f3499e2401a56db98d9f01c Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Tue, 19 Nov 2013 11:18:03 +0100 Subject: power_supply: Add power_supply notifier MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds a notifier chain to the power_supply, this helps drivers in other subsystem to listen to changes in power supply subsystem. This would help to take some actions in those drivers on changing the power supply properties. One such scenario is to increase/decrease system performance based on the battery capacity/voltage. Another scenario is to adjust the h/w peak current detection voltage/current thresholds based on battery voltage/capacity. The notifier helps drivers to listen to changes in power_suppy susbystem without polling the power_supply properties Signed-off-by: Jenny TC Signed-off-by: Pali Rohár Acked-by: Jenny TC Signed-off-by: Anton Vorontsov --- include/linux/power_supply.h | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'include/linux') diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index 5c2600630dc9..3e28fe188d17 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -16,6 +16,7 @@ #include #include #include +#include struct device; @@ -158,6 +159,10 @@ enum power_supply_type { POWER_SUPPLY_TYPE_USB_ACA, /* Accessory Charger Adapters */ }; +enum power_supply_notifier_events { + PSY_EVENT_PROP_CHANGED, +}; + union power_supply_propval { int intval; const char *strval; @@ -235,6 +240,9 @@ struct power_supply_info { int use_for_apm; }; +extern struct atomic_notifier_head power_supply_notifier; +extern int power_supply_reg_notifier(struct notifier_block *nb); +extern void power_supply_unreg_notifier(struct notifier_block *nb); extern struct power_supply *power_supply_get_by_name(const char *name); extern void power_supply_changed(struct power_supply *psy); extern int power_supply_am_i_supplied(struct power_supply *psy); -- cgit v1.2.3 From 5c49a6256bed52f639ed70d252b1c91d1ab899d6 Mon Sep 17 00:00:00 2001 From: Jonghwa Lee Date: Wed, 18 Dec 2013 15:42:34 +0900 Subject: charger-manager: Modify the way of checking battery's temperature Charger-manager driver used to check battery temperature through the callback function passed by platform data. Unfortunatley, without that pre-defined callback function, charger-manager can't get battery's temperature at all. Also passing callback function through platform data ruins DT support for charger-manager. This patch mondifies charger-manager driver to get temperature of battery without pre-defined callback function. Now, charger-manager can use either of battery thermometer in fuel-gauge and ouside of battery. It uses thermal framework interface for outer thermometer if thermal fw is enabled. Otherwise, it tries to use fuel-gauge's through the power supply interface. Signed-off-by: Jonghwa Lee Signed-off-by: Myungjoo Ham Signed-off-by: Anton Vorontsov --- drivers/power/charger-manager.c | 161 +++++++++++++++++++++++++--------- include/linux/power/charger-manager.h | 24 +++-- 2 files changed, 136 insertions(+), 49 deletions(-) (limited to 'include/linux') diff --git a/drivers/power/charger-manager.c b/drivers/power/charger-manager.c index 7287c0efd6bf..6b68d158e3c1 100644 --- a/drivers/power/charger-manager.c +++ b/drivers/power/charger-manager.c @@ -25,12 +25,22 @@ #include #include #include +#include + +/* + * Default termperature threshold for charging. + * Every temperature units are in tenth of centigrade. + */ +#define CM_DEFAULT_RECHARGE_TEMP_DIFF 50 +#define CM_DEFAULT_CHARGE_TEMP_MAX 500 static const char * const default_event_names[] = { [CM_EVENT_UNKNOWN] = "Unknown", [CM_EVENT_BATT_FULL] = "Battery Full", [CM_EVENT_BATT_IN] = "Battery Inserted", [CM_EVENT_BATT_OUT] = "Battery Pulled Out", + [CM_EVENT_BATT_OVERHEAT] = "Battery Overheat", + [CM_EVENT_BATT_COLD] = "Battery Cold", [CM_EVENT_EXT_PWR_IN_OUT] = "External Power Attach/Detach", [CM_EVENT_CHG_START_STOP] = "Charging Start/Stop", [CM_EVENT_OTHERS] = "Other battery events" @@ -540,6 +550,60 @@ static int check_charging_duration(struct charger_manager *cm) return ret; } +static int cm_get_battery_temperature(struct charger_manager *cm, + int *temp) +{ + int ret; + + if (!cm->desc->measure_battery_temp) + return -ENODEV; + +#ifdef CONFIG_THERMAL + ret = thermal_zone_get_temp(cm->tzd_batt, (unsigned long *)temp); + if (!ret) + /* Calibrate temperature unit */ + *temp /= 100; +#else + ret = cm->fuel_gauge->get_property(cm->fuel_gauge, + POWER_SUPPLY_PROP_TEMP, + (union power_supply_propval *)temp); +#endif + return ret; +} + +static int cm_check_thermal_status(struct charger_manager *cm) +{ + struct charger_desc *desc = cm->desc; + int temp, upper_limit, lower_limit; + int ret = 0; + + ret = cm_get_battery_temperature(cm, &temp); + if (ret) { + /* FIXME: + * No information of battery temperature might + * occur hazadous result. We have to handle it + * depending on battery type. + */ + dev_err(cm->dev, "Failed to get battery temperature\n"); + return 0; + } + + upper_limit = desc->temp_max; + lower_limit = desc->temp_min; + + if (cm->emergency_stop) { + upper_limit -= desc->temp_diff; + lower_limit += desc->temp_diff; + } + + if (temp > upper_limit) + ret = CM_EVENT_BATT_OVERHEAT; + else if (temp < lower_limit) + ret = CM_EVENT_BATT_COLD; + + return ret; +} + /** * _cm_monitor - Monitor the temperature and return true for exceptions. * @cm: the Charger Manager representing the battery. @@ -549,28 +613,22 @@ static int check_charging_duration(struct charger_manager *cm) */ static bool _cm_monitor(struct charger_manager *cm) { - struct charger_desc *desc = cm->desc; - int temp = desc->temperature_out_of_range(&cm->last_temp_mC); + int temp_alrt; - dev_dbg(cm->dev, "monitoring (%2.2d.%3.3dC)\n", - cm->last_temp_mC / 1000, cm->last_temp_mC % 1000); + temp_alrt = cm_check_thermal_status(cm); /* It has been stopped already */ - if (temp && cm->emergency_stop) + if (temp_alrt && cm->emergency_stop) return false; /* * Check temperature whether overheat or cold. * If temperature is out of range normal state, stop charging. */ - if (temp) { - cm->emergency_stop = temp; - if (!try_charger_enable(cm, false)) { - if (temp > 0) - uevent_notify(cm, "OVERHEAT"); - else - uevent_notify(cm, "COLD"); - } + if (temp_alrt) { + cm->emergency_stop = temp_alrt; + if (!try_charger_enable(cm, false)) + uevent_notify(cm, default_event_names[temp_alrt]); /* * Check whole charging duration and discharing duration @@ -802,21 +860,8 @@ static int charger_get_property(struct power_supply *psy, POWER_SUPPLY_PROP_CURRENT_NOW, val); break; case POWER_SUPPLY_PROP_TEMP: - /* in thenth of centigrade */ - if (cm->last_temp_mC == INT_MIN) - desc->temperature_out_of_range(&cm->last_temp_mC); - val->intval = cm->last_temp_mC / 100; - if (!desc->measure_battery_temp) - ret = -ENODEV; - break; case POWER_SUPPLY_PROP_TEMP_AMBIENT: - /* in thenth of centigrade */ - if (cm->last_temp_mC == INT_MIN) - desc->temperature_out_of_range(&cm->last_temp_mC); - val->intval = cm->last_temp_mC / 100; - if (desc->measure_battery_temp) - ret = -ENODEV; - break; + return cm_get_battery_temperature(cm, &val->intval); case POWER_SUPPLY_PROP_CAPACITY: if (!cm->fuel_gauge) { ret = -ENODEV; @@ -1439,6 +1484,50 @@ err: return ret; } +static int cm_init_thermal_data(struct charger_manager *cm) +{ + struct charger_desc *desc = cm->desc; + union power_supply_propval val; + int ret; + + /* Verify whether fuel gauge provides battery temperature */ + ret = cm->fuel_gauge->get_property(cm->fuel_gauge, + POWER_SUPPLY_PROP_TEMP, &val); + + if (!ret) { + cm->charger_psy.properties[cm->charger_psy.num_properties] = + POWER_SUPPLY_PROP_TEMP; + cm->charger_psy.num_properties++; + cm->desc->measure_battery_temp = true; + } +#ifdef CONFIG_THERMAL + cm->tzd_batt = cm->fuel_gauge->tzd; + + if (ret && desc->thermal_zone) { + cm->tzd_batt = + thermal_zone_get_zone_by_name(desc->thermal_zone); + if (IS_ERR(cm->tzd_batt)) + return PTR_ERR(cm->tzd_batt); + + /* Use external thermometer */ + cm->charger_psy.properties[cm->charger_psy.num_properties] = + POWER_SUPPLY_PROP_TEMP_AMBIENT; + cm->charger_psy.num_properties++; + cm->desc->measure_battery_temp = true; + ret = 0; + } +#endif + if (cm->desc->measure_battery_temp) { + /* NOTICE : Default allowable minimum charge temperature is 0 */ + if (!desc->temp_max) + desc->temp_max = CM_DEFAULT_CHARGE_TEMP_MAX; + if (!desc->temp_diff) + desc->temp_diff = CM_DEFAULT_RECHARGE_TEMP_DIFF; + } + + return ret; +} + static int charger_manager_probe(struct platform_device *pdev) { struct charger_desc *desc = dev_get_platdata(&pdev->dev); @@ -1470,7 +1559,6 @@ static int charger_manager_probe(struct platform_device *pdev) /* Basic Values. Unspecified are Null or 0 */ cm->dev = &pdev->dev; cm->desc = desc; - cm->last_temp_mC = INT_MIN; /* denotes "unmeasured, yet" */ /* * The following two do not need to be errors. @@ -1533,11 +1621,6 @@ static int charger_manager_probe(struct platform_device *pdev) return -EINVAL; } - if (!desc->temperature_out_of_range) { - dev_err(&pdev->dev, "there is no temperature_out_of_range\n"); - return -EINVAL; - } - if (!desc->charging_max_duration_ms || !desc->discharging_max_duration_ms) { dev_info(&pdev->dev, "Cannot limit charging duration checking mechanism to prevent overcharge/overheat and control discharging duration\n"); @@ -1583,14 +1666,10 @@ static int charger_manager_probe(struct platform_device *pdev) cm->charger_psy.num_properties++; } - if (desc->measure_battery_temp) { - cm->charger_psy.properties[cm->charger_psy.num_properties] = - POWER_SUPPLY_PROP_TEMP; - cm->charger_psy.num_properties++; - } else { - cm->charger_psy.properties[cm->charger_psy.num_properties] = - POWER_SUPPLY_PROP_TEMP_AMBIENT; - cm->charger_psy.num_properties++; + ret = cm_init_thermal_data(cm); + if (ret) { + dev_err(&pdev->dev, "Failed to initialize thermal data\n"); + cm->desc->measure_battery_temp = false; } INIT_DELAYED_WORK(&cm->fullbatt_vchk_work, fullbatt_vchk); diff --git a/include/linux/power/charger-manager.h b/include/linux/power/charger-manager.h index 0e86840eb603..9aec0293f204 100644 --- a/include/linux/power/charger-manager.h +++ b/include/linux/power/charger-manager.h @@ -37,6 +37,8 @@ enum cm_event_types { CM_EVENT_BATT_FULL, CM_EVENT_BATT_IN, CM_EVENT_BATT_OUT, + CM_EVENT_BATT_OVERHEAT, + CM_EVENT_BATT_COLD, CM_EVENT_EXT_PWR_IN_OUT, CM_EVENT_CHG_START_STOP, CM_EVENT_OTHERS, @@ -173,11 +175,10 @@ struct charger_regulator { * @num_charger_regulator: the number of entries in charger_regulators * @charger_regulators: array of charger regulators * @psy_fuel_gauge: the name of power-supply for fuel gauge - * @temperature_out_of_range: - * Determine whether the status is overheat or cold or normal. - * return_value > 0: overheat - * return_value == 0: normal - * return_value < 0: cold + * @thermal_zone : the name of thermal zone for battery + * @temp_min : Minimum battery temperature for charging. + * @temp_max : Maximum battery temperature for charging. + * @temp_diff : Temperature diffential to restart charging. * @measure_battery_temp: * true: measure battery temperature * false: measure ambient temperature @@ -210,7 +211,12 @@ struct charger_desc { char *psy_fuel_gauge; - int (*temperature_out_of_range)(int *mC); + char *thermal_zone; + + int temp_min; + int temp_max; + int temp_diff; + bool measure_battery_temp; u64 charging_max_duration_ms; @@ -226,13 +232,13 @@ struct charger_desc { * @desc: instance of charger_desc * @fuel_gauge: power_supply for fuel gauge * @charger_stat: array of power_supply for chargers + * @tzd_batt : thermal zone device for battery * @charger_enabled: the state of charger * @fullbatt_vchk_jiffies_at: * jiffies at the time full battery check will occur. * @fullbatt_vchk_work: work queue for full battery check * @emergency_stop: * When setting true, stop charging - * @last_temp_mC: the measured temperature in milli-Celsius * @psy_name_buf: the name of power-supply-class for charger manager * @charger_psy: power_supply for charger manager * @status_save_ext_pwr_inserted: @@ -250,13 +256,15 @@ struct charger_manager { struct power_supply *fuel_gauge; struct power_supply **charger_stat; +#ifdef CONFIG_THERMAL + struct thermal_zone_device *tzd_batt; +#endif bool charger_enabled; unsigned long fullbatt_vchk_jiffies_at; struct delayed_work fullbatt_vchk_work; int emergency_stop; - int last_temp_mC; char psy_name_buf[PSY_NAME_MAX + 1]; struct power_supply charger_psy; -- cgit v1.2.3 From 856ee6115e2de98cb83389ce18116e6d5b90e817 Mon Sep 17 00:00:00 2001 From: Jonghwa Lee Date: Wed, 18 Dec 2013 15:42:35 +0900 Subject: charger-manager: Support deivce tree in charger manager driver Charger-manager can parse charger_desc data from devicetree which is used to register charger manager. Signed-off-by: Jonghwa Lee Signed-off-by: Myungjoo Ham Signed-off-by: Anton Vorontsov --- .../bindings/power_supply/charger-manager.txt | 81 ++++++++++++ drivers/power/charger-manager.c | 138 ++++++++++++++++++++- include/linux/power/charger-manager.h | 12 +- 3 files changed, 222 insertions(+), 9 deletions(-) create mode 100644 Documentation/devicetree/bindings/power_supply/charger-manager.txt (limited to 'include/linux') diff --git a/Documentation/devicetree/bindings/power_supply/charger-manager.txt b/Documentation/devicetree/bindings/power_supply/charger-manager.txt new file mode 100644 index 000000000000..2b33750e3db2 --- /dev/null +++ b/Documentation/devicetree/bindings/power_supply/charger-manager.txt @@ -0,0 +1,81 @@ +charger-manager bindings +~~~~~~~~~~~~~~~~~~~~~~~~ + +Required properties : + - compatible : "charger-manager" + - <>-supply : for regulator consumer + - cm-num-chargers : number of chargers + - cm-chargers : name of chargers + - cm-fuel-gauge : name of battery fuel gauge + - subnode : + - cm-regulator-name : name of charger regulator + - subnode : + - cm-cable-name : name of charger cable + - cm-cable-extcon : name of extcon dev +(optional) - cm-cable-min : minimum current of cable +(optional) - cm-cable-max : maximum current of cable + +Optional properties : + - cm-name : charger manager's name (default : "battery") + - cm-poll-mode : polling mode (enum polling_modes) + - cm-poll-interval : polling interval + - cm-battery-stat : battery status (enum data_source) + - cm-fullbatt-* : data for full battery checking + - cm-thermal-zone : name of external thermometer's thermal zone + - cm-battery-* : threshold battery temperature for charging + -cold : critical cold temperature of battery for charging + -cold-in-minus : flag that cold temerature is in minus degree + -hot : critical hot temperature of battery for charging + -temp-diff : temperature difference to allow recharging + - cm-dis/charging-max = limits of charging duration + +Example : + charger-manager@0 { + compatible = "charger-manager"; + chg-reg-supply = <&charger_regulator>; + + cm-name = "battery"; + /* Always polling ON : 30s */ + cm-poll-mode = <1>; + cm-poll-interval = <30000>; + + cm-fullbatt-vchkdrop-ms = <30000>; + cm-fullbatt-vchkdrop-volt = <150000>; + cm-fullbatt-soc = <100>; + + cm-battery-stat = <3>; + + cm-num-chargers = <3>; + cm-chargers = "charger0", "charger1", "charger2"; + + cm-fuel-gauge = "fuelgauge0"; + + cm-thermal-zone = "thermal_zone.1" + /* in deci centigrade */ + cm-battery-cold = <50>; + cm-battery-cold-in-minus; + cm-battery-hot = <800>; + cm-battery-temp-diff = <100>; + + /* Allow charging for 5hr */ + cm-charging-max = <18000000>; + /* Allow discharging for 2hr */ + cm-discharging-max = <7200000>; + + regulator@0 { + cm-regulator-name = "chg-reg"; + cable@0 { + cm-cable-name = "USB"; + cm-cable-extcon = "extcon-dev.0"; + cm-cable-min = <475000>; + cm-cable-max = <500000>; + }; + cable@1 { + cm-cable-name = "TA"; + cm-cable-extcon = "extcon-dev.0"; + cm-cable-min = <650000>; + cm-cable-max = <675000>; + }; + }; + + }; diff --git a/drivers/power/charger-manager.c b/drivers/power/charger-manager.c index 6b68d158e3c1..ec5c140a52b8 100644 --- a/drivers/power/charger-manager.c +++ b/drivers/power/charger-manager.c @@ -25,6 +25,7 @@ #include #include #include +#include #include /* @@ -528,7 +529,7 @@ static int check_charging_duration(struct charger_manager *cm) duration = curr - cm->charging_start_time; if (duration > desc->charging_max_duration_ms) { - dev_info(cm->dev, "Charging duration exceed %lldms\n", + dev_info(cm->dev, "Charging duration exceed %ums\n", desc->charging_max_duration_ms); uevent_notify(cm, "Discharging"); try_charger_enable(cm, false); @@ -539,7 +540,7 @@ static int check_charging_duration(struct charger_manager *cm) if (duration > desc->charging_max_duration_ms && is_ext_pwr_online(cm)) { - dev_info(cm->dev, "Discharging duration exceed %lldms\n", + dev_info(cm->dev, "Discharging duration exceed %ums\n", desc->discharging_max_duration_ms); uevent_notify(cm, "Recharging"); try_charger_enable(cm, true); @@ -1528,9 +1529,139 @@ static int cm_init_thermal_data(struct charger_manager *cm) return ret; } +static struct of_device_id charger_manager_match[] = { + { + .compatible = "charger-manager", + }, + {}, +}; + +struct charger_desc *of_cm_parse_desc(struct device *dev) +{ + struct charger_desc *desc; + struct device_node *np = dev->of_node; + u32 poll_mode = CM_POLL_DISABLE; + u32 battery_stat = CM_NO_BATTERY; + int num_chgs = 0; + + desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL); + if (!desc) + return ERR_PTR(-ENOMEM); + + of_property_read_string(np, "cm-name", &desc->psy_name); + + of_property_read_u32(np, "cm-poll-mode", &poll_mode); + desc->polling_mode = poll_mode; + + of_property_read_u32(np, "cm-poll-interval", + &desc->polling_interval_ms); + + of_property_read_u32(np, "cm-fullbatt-vchkdrop-ms", + &desc->fullbatt_vchkdrop_ms); + of_property_read_u32(np, "cm-fullbatt-vchkdrop-volt", + &desc->fullbatt_vchkdrop_uV); + of_property_read_u32(np, "cm-fullbatt-voltage", &desc->fullbatt_uV); + of_property_read_u32(np, "cm-fullbatt-soc", &desc->fullbatt_soc); + of_property_read_u32(np, "cm-fullbatt-capacity", + &desc->fullbatt_full_capacity); + + of_property_read_u32(np, "cm-battery-stat", &battery_stat); + desc->battery_present = battery_stat; + + /* chargers */ + of_property_read_u32(np, "cm-num-chargers", &num_chgs); + if (num_chgs) { + /* Allocate empty bin at the tail of array */ + desc->psy_charger_stat = devm_kzalloc(dev, sizeof(char *) + * (num_chgs + 1), GFP_KERNEL); + if (desc->psy_charger_stat) { + int i; + for (i = 0; i < num_chgs; i++) + of_property_read_string_index(np, "cm-chargers", + i, &desc->psy_charger_stat[i]); + } else { + return ERR_PTR(-ENOMEM); + } + } + + of_property_read_string(np, "cm-fuel-gauge", &desc->psy_fuel_gauge); + + of_property_read_string(np, "cm-thermal-zone", &desc->thermal_zone); + + of_property_read_u32(np, "cm-battery-cold", &desc->temp_min); + if (of_get_property(np, "cm-battery-cold-in-minus", NULL)) + desc->temp_min *= -1; + of_property_read_u32(np, "cm-battery-hot", &desc->temp_max); + of_property_read_u32(np, "cm-battery-temp-diff", &desc->temp_diff); + + of_property_read_u32(np, "cm-charging-max", + &desc->charging_max_duration_ms); + of_property_read_u32(np, "cm-discharging-max", + &desc->discharging_max_duration_ms); + + /* battery charger regualtors */ + desc->num_charger_regulators = of_get_child_count(np); + if (desc->num_charger_regulators) { + struct charger_regulator *chg_regs; + struct device_node *child; + + chg_regs = devm_kzalloc(dev, sizeof(*chg_regs) + * desc->num_charger_regulators, + GFP_KERNEL); + if (!chg_regs) + return ERR_PTR(-ENOMEM); + + desc->charger_regulators = chg_regs; + + for_each_child_of_node(np, child) { + struct charger_cable *cables; + struct device_node *_child; + + of_property_read_string(child, "cm-regulator-name", + &chg_regs->regulator_name); + + /* charger cables */ + chg_regs->num_cables = of_get_child_count(child); + if (chg_regs->num_cables) { + cables = devm_kzalloc(dev, sizeof(*cables) + * chg_regs->num_cables, + GFP_KERNEL); + if (!cables) + return ERR_PTR(-ENOMEM); + + chg_regs->cables = cables; + + for_each_child_of_node(child, _child) { + of_property_read_string(_child, + "cm-cable-name", &cables->name); + of_property_read_string(_child, + "cm-cable-extcon", + &cables->extcon_name); + of_property_read_u32(_child, + "cm-cable-min", + &cables->min_uA); + of_property_read_u32(_child, + "cm-cable-max", + &cables->max_uA); + cables++; + } + } + chg_regs++; + } + } + return desc; +} + +static inline struct charger_desc *cm_get_drv_data(struct platform_device *pdev) +{ + if (pdev->dev.of_node) + return of_cm_parse_desc(&pdev->dev); + return (struct charger_desc *)dev_get_platdata(&pdev->dev); +} + static int charger_manager_probe(struct platform_device *pdev) { - struct charger_desc *desc = dev_get_platdata(&pdev->dev); + struct charger_desc *desc = cm_get_drv_data(pdev); struct charger_manager *cm; int ret = 0, i = 0; int j = 0; @@ -1887,6 +2018,7 @@ static struct platform_driver charger_manager_driver = { .name = "charger-manager", .owner = THIS_MODULE, .pm = &charger_manager_pm, + .of_match_table = charger_manager_match, }, .probe = charger_manager_probe, .remove = charger_manager_remove, diff --git a/include/linux/power/charger-manager.h b/include/linux/power/charger-manager.h index 9aec0293f204..07e7945a1ff2 100644 --- a/include/linux/power/charger-manager.h +++ b/include/linux/power/charger-manager.h @@ -191,7 +191,7 @@ struct charger_regulator { * max_duration_ms', cm start charging. */ struct charger_desc { - char *psy_name; + const char *psy_name; enum polling_modes polling_mode; unsigned int polling_interval_ms; @@ -204,14 +204,14 @@ struct charger_desc { enum data_source battery_present; - char **psy_charger_stat; + const char **psy_charger_stat; int num_charger_regulators; struct charger_regulator *charger_regulators; - char *psy_fuel_gauge; + const char *psy_fuel_gauge; - char *thermal_zone; + const char *thermal_zone; int temp_min; int temp_max; @@ -219,8 +219,8 @@ struct charger_desc { bool measure_battery_temp; - u64 charging_max_duration_ms; - u64 discharging_max_duration_ms; + u32 charging_max_duration_ms; + u32 discharging_max_duration_ms; }; #define PSY_NAME_MAX 30 -- cgit v1.2.3 From 32260308b4cafcd71b1c0aac35675dc68120c33d Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Tue, 19 Nov 2013 11:18:04 +0100 Subject: bq2415x_charger: Use power_supply notifier for automode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch removing set_mode_hook function from board data and replacing it with new string variable of notifier power supply device. After this change it is possible to add DT support because driver does not need specific board function anymore. Only static data and name of power supply device is required. Signed-off-by: Pali Rohár Reviewed-by: Pavel Machek Signed-off-by: Anton Vorontsov --- drivers/power/bq2415x_charger.c | 77 +++++++++++++++++++++++++++-------- include/linux/power/bq2415x_charger.h | 48 +++------------------- 2 files changed, 65 insertions(+), 60 deletions(-) (limited to 'include/linux') diff --git a/drivers/power/bq2415x_charger.c b/drivers/power/bq2415x_charger.c index df893dd1447d..0bece7026056 100644 --- a/drivers/power/bq2415x_charger.c +++ b/drivers/power/bq2415x_charger.c @@ -1,7 +1,7 @@ /* * bq2415x charger driver * - * Copyright (C) 2011-2012 Pali Rohár + * Copyright (C) 2011-2013 Pali Rohár * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -170,6 +170,7 @@ struct bq2415x_device { struct bq2415x_platform_data init_data; struct power_supply charger; struct delayed_work work; + struct notifier_block nb; enum bq2415x_mode reported_mode;/* mode reported by hook function */ enum bq2415x_mode mode; /* current configured mode */ enum bq2415x_chip chip; @@ -795,24 +796,53 @@ static int bq2415x_set_mode(struct bq2415x_device *bq, enum bq2415x_mode mode) } -/* hook function called by other driver which set reported mode */ -static void bq2415x_hook_function(enum bq2415x_mode mode, void *data) +static int bq2415x_notifier_call(struct notifier_block *nb, + unsigned long val, void *v) { - struct bq2415x_device *bq = data; + struct bq2415x_device *bq = + container_of(nb, struct bq2415x_device, nb); + struct power_supply *psy = v; + enum bq2415x_mode mode; + union power_supply_propval prop; + int ret; + int mA; - if (!bq) - return; + if (val != PSY_EVENT_PROP_CHANGED) + return NOTIFY_OK; + + if (strcmp(psy->name, bq->init_data.notify_device) != 0) + return NOTIFY_OK; + + dev_dbg(bq->dev, "notifier call was called\n"); + + ret = psy->get_property(psy, POWER_SUPPLY_PROP_CURRENT_MAX, &prop); + if (ret != 0) + return NOTIFY_OK; + + mA = prop.intval; + + if (mA == 0) + mode = BQ2415X_MODE_OFF; + else if (mA < 500) + mode = BQ2415X_MODE_NONE; + else if (mA < 1800) + mode = BQ2415X_MODE_HOST_CHARGER; + else + mode = BQ2415X_MODE_DEDICATED_CHARGER; + + if (bq->reported_mode == mode) + return NOTIFY_OK; - dev_dbg(bq->dev, "hook function was called\n"); bq->reported_mode = mode; /* if automode is not enabled do not tell about reported_mode */ if (bq->automode < 1) - return; + return NOTIFY_OK; sysfs_notify(&bq->charger.dev->kobj, NULL, "reported_mode"); bq2415x_set_mode(bq, bq->reported_mode); + return NOTIFY_OK; } /**** timer functions ****/ @@ -1512,6 +1542,7 @@ static int bq2415x_probe(struct i2c_client *client, int num; char *name; struct bq2415x_device *bq; + struct power_supply *psy; if (!client->dev.platform_data) { dev_err(&client->dev, "platform data not set\n"); @@ -1573,16 +1604,27 @@ static int bq2415x_probe(struct i2c_client *client, goto error_4; } - if (bq->init_data.set_mode_hook) { - if (bq->init_data.set_mode_hook( - bq2415x_hook_function, bq)) { - bq->automode = 1; + if (bq->init_data.notify_device) { + bq->nb.notifier_call = bq2415x_notifier_call; + ret = power_supply_reg_notifier(&bq->nb); + if (ret) { + dev_err(bq->dev, "failed to reg notifier: %d\n", ret); + goto error_5; + } + psy = power_supply_get_by_name(bq->init_data.notify_device); + if (psy) { + /* Query for initial reported_mode and set it */ + bq2415x_notifier_call(&bq->nb, + PSY_EVENT_PROP_CHANGED, psy); bq2415x_set_mode(bq, bq->reported_mode); - dev_info(bq->dev, "automode enabled\n"); } else { - bq->automode = -1; - dev_info(bq->dev, "automode failed\n"); + dev_info(bq->dev, "notifier power supply device (%s) " + "for automode is not registred yet... " + "automode will not work without that device\n", + bq->init_data.notify_device); } + bq->automode = 1; + dev_info(bq->dev, "automode enabled\n"); } else { bq->automode = -1; dev_info(bq->dev, "automode not supported\n"); @@ -1594,6 +1636,7 @@ static int bq2415x_probe(struct i2c_client *client, dev_info(bq->dev, "driver registered\n"); return 0; +error_5: error_4: bq2415x_sysfs_exit(bq); error_3: @@ -1614,8 +1657,8 @@ static int bq2415x_remove(struct i2c_client *client) { struct bq2415x_device *bq = i2c_get_clientdata(client); - if (bq->init_data.set_mode_hook) - bq->init_data.set_mode_hook(NULL, NULL); + if (bq->init_data.notify_device) + power_supply_unreg_notifier(&bq->nb); bq2415x_sysfs_exit(bq); bq2415x_power_supply_exit(bq); diff --git a/include/linux/power/bq2415x_charger.h b/include/linux/power/bq2415x_charger.h index 8dcc0f46fc0a..50762af8b834 100644 --- a/include/linux/power/bq2415x_charger.h +++ b/include/linux/power/bq2415x_charger.h @@ -1,7 +1,7 @@ /* * bq2415x charger driver * - * Copyright (C) 2011-2012 Pali Rohár + * Copyright (C) 2011-2013 Pali Rohár * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -31,46 +31,9 @@ * termination current. It it is less or equal to zero, configuring charge * and termination current will not be possible. * - * Function set_mode_hook is needed for automode (setting correct current - * limit when charger is connected/disconnected or setting boost mode). - * When is NULL, automode function is disabled. When is not NULL, it must - * have this prototype: - * - * int (*set_mode_hook)( - * void (*hook)(enum bq2415x_mode mode, void *data), - * void *data) - * - * hook is hook function (see below) and data is pointer to driver private - * data - * - * bq2415x driver will call it as: - * - * platform_data->set_mode_hook(bq2415x_hook_function, bq2415x_device); - * - * Board/platform function set_mode_hook return non zero value when hook - * function was successful registered. Platform code should call that hook - * function (which get from pointer, with data) every time when charger - * was connected/disconnected or require to enable boost mode. bq2415x - * driver then will set correct current limit, enable/disable charger or - * boost mode. - * - * Hook function has this prototype: - * - * void hook(enum bq2415x_mode mode, void *data); - * - * mode is bq2415x mode (charger or boost) - * data is pointer to driver private data (which get from - * set_charger_type_hook) - * - * When bq driver is being unloaded, it call function: - * - * platform_data->set_mode_hook(NULL, NULL); - * - * (hook function and driver private data are NULL) - * - * After that board/platform code must not call driver hook function! It - * is possible that pointer to hook function will not be valid and calling - * will cause undefined result. + * For automode support is needed to provide name of power supply device + * in value notify_device. Device driver must immediately report property + * POWER_SUPPLY_PROP_CURRENT_MAX when current changed. */ /* Supported modes with maximal current limit */ @@ -89,8 +52,7 @@ struct bq2415x_platform_data { int charge_current; /* mA */ int termination_current; /* mA */ int resistor_sense; /* m ohm */ - int (*set_mode_hook)(void (*hook)(enum bq2415x_mode mode, void *data), - void *data); + const char *notify_device; /* name */ }; #endif -- cgit v1.2.3 From abce97708a9b5ba897ad94fa289804d8af8d3ea9 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Sun, 24 Nov 2013 17:49:29 +0100 Subject: power_supply: Add power_supply_get_by_phandle Add method to get power supply by device tree phandle. Signed-off-by: Sebastian Reichel Reviewed-by: Pavel Machek Signed-off-by: Anton Vorontsov --- drivers/power/power_supply_core.c | 26 ++++++++++++++++++++++++++ include/linux/power_supply.h | 8 ++++++++ 2 files changed, 34 insertions(+) (limited to 'include/linux') diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c index 6aac23cc6566..26606641fe44 100644 --- a/drivers/power/power_supply_core.c +++ b/drivers/power/power_supply_core.c @@ -341,6 +341,32 @@ struct power_supply *power_supply_get_by_name(const char *name) } EXPORT_SYMBOL_GPL(power_supply_get_by_name); +#ifdef CONFIG_OF +static int power_supply_match_device_node(struct device *dev, const void *data) +{ + return dev->parent && dev->parent->of_node == data; +} + +struct power_supply *power_supply_get_by_phandle(struct device_node *np, + const char *property) +{ + struct device_node *power_supply_np; + struct device *dev; + + power_supply_np = of_parse_phandle(np, property, 0); + if (!power_supply_np) + return ERR_PTR(-ENODEV); + + dev = class_find_device(power_supply_class, NULL, power_supply_np, + power_supply_match_device_node); + + of_node_put(power_supply_np); + + return dev ? dev_get_drvdata(dev) : NULL; +} +EXPORT_SYMBOL_GPL(power_supply_get_by_phandle); +#endif /* CONFIG_OF */ + int power_supply_powers(struct power_supply *psy, struct device *dev) { return sysfs_create_link(&psy->dev->kobj, &dev->kobj, "powers"); diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index 3e28fe188d17..c9dc4e09854c 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -244,6 +244,14 @@ extern struct atomic_notifier_head power_supply_notifier; extern int power_supply_reg_notifier(struct notifier_block *nb); extern void power_supply_unreg_notifier(struct notifier_block *nb); extern struct power_supply *power_supply_get_by_name(const char *name); +#ifdef CONFIG_OF +extern struct power_supply *power_supply_get_by_phandle(struct device_node *np, + const char *property); +#else /* !CONFIG_OF */ +static inline struct power_supply * +power_supply_get_by_phandle(struct device_node *np, const char *property) +{ return NULL; } +#endif /* CONFIG_OF */ extern void power_supply_changed(struct power_supply *psy); extern int power_supply_am_i_supplied(struct power_supply *psy); extern int power_supply_set_battery_charged(struct power_supply *psy); -- cgit v1.2.3 From 34a109610e2a78b56be22fa314054c09669075eb Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Sun, 1 Dec 2013 02:00:10 +0100 Subject: isp1704_charger: Add DT support This patch introduces device tree support to the isp1704 charger driver. Adding support involved moving the handling of the enable GPIO from board code into the driver. Signed-off-by: Sebastian Reichel Signed-off-by: Anton Vorontsov --- drivers/power/isp1704_charger.c | 54 ++++++++++++++++++++++++++++++++--- include/linux/power/isp1704_charger.h | 1 + 2 files changed, 51 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/drivers/power/isp1704_charger.c b/drivers/power/isp1704_charger.c index 1bb3a91b1acc..80edb7d8cb54 100644 --- a/drivers/power/isp1704_charger.c +++ b/drivers/power/isp1704_charger.c @@ -29,6 +29,8 @@ #include #include #include +#include +#include #include #include @@ -88,6 +90,8 @@ static void isp1704_charger_set_power(struct isp1704_charger *isp, bool on) if (board && board->set_power) board->set_power(on); + else if (board) + gpio_set_value(board->enable_gpio, on); } /* @@ -400,12 +404,47 @@ static int isp1704_charger_probe(struct platform_device *pdev) struct isp1704_charger *isp; int ret = -ENODEV; + struct isp1704_charger_data *pdata = dev_get_platdata(&pdev->dev); + struct device_node *np = pdev->dev.of_node; + + if (np) { + int gpio = of_get_named_gpio(np, "nxp,enable-gpio", 0); + + if (gpio < 0) + return gpio; + + pdata = devm_kzalloc(&pdev->dev, + sizeof(struct isp1704_charger_data), GFP_KERNEL); + pdata->enable_gpio = gpio; + + dev_info(&pdev->dev, "init gpio %d\n", pdata->enable_gpio); + + ret = devm_gpio_request_one(&pdev->dev, pdata->enable_gpio, + GPIOF_OUT_INIT_HIGH, "isp1704_reset"); + if (ret) + goto fail0; + } + + if (!pdata) { + dev_err(&pdev->dev, "missing platform data!\n"); + return -ENODEV; + } + + isp = devm_kzalloc(&pdev->dev, sizeof(*isp), GFP_KERNEL); if (!isp) return -ENOMEM; - isp->phy = usb_get_phy(USB_PHY_TYPE_USB2); - if (IS_ERR_OR_NULL(isp->phy)) + if (np) + isp->phy = devm_usb_get_phy_by_phandle(&pdev->dev, "usb-phy", 0); + else + isp->phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2); + + if (IS_ERR(isp->phy)) { + ret = PTR_ERR(isp->phy); + goto fail0; + } + if (!isp->phy) goto fail0; isp->dev = &pdev->dev; @@ -464,7 +503,6 @@ fail2: power_supply_unregister(&isp->psy); fail1: isp1704_charger_set_power(isp, 0); - usb_put_phy(isp->phy); fail0: dev_err(&pdev->dev, "failed to register isp1704 with error %d\n", ret); @@ -477,15 +515,23 @@ static int isp1704_charger_remove(struct platform_device *pdev) usb_unregister_notifier(isp->phy, &isp->nb); power_supply_unregister(&isp->psy); - usb_put_phy(isp->phy); isp1704_charger_set_power(isp, 0); return 0; } +#ifdef CONFIG_OF +static const struct of_device_id omap_isp1704_of_match[] = { + { .compatible = "nxp,isp1704", }, + {}, +}; +MODULE_DEVICE_TABLE(of, omap_isp1704_of_match); +#endif + static struct platform_driver isp1704_charger_driver = { .driver = { .name = "isp1704_charger", + .of_match_table = of_match_ptr(omap_isp1704_of_match), }, .probe = isp1704_charger_probe, .remove = isp1704_charger_remove, diff --git a/include/linux/power/isp1704_charger.h b/include/linux/power/isp1704_charger.h index 68096a6aa2d7..0105d9e7af85 100644 --- a/include/linux/power/isp1704_charger.h +++ b/include/linux/power/isp1704_charger.h @@ -24,6 +24,7 @@ struct isp1704_charger_data { void (*set_power)(bool on); + int enable_gpio; }; #endif -- cgit v1.2.3