From 9ea89402e25edafb6ad8ec92848d12c1d5d3969f Mon Sep 17 00:00:00 2001 From: Cheng-Yi Chiang Date: Mon, 4 Aug 2014 13:47:45 +0200 Subject: sbs-battery: export manufacturer and model name to sysfs This CL supports two power_supply_property items for smart battery: POWER_SUPPLY_PROP_MANUFACTURER and POWER_SUPPLY_PROP_MODEL_NAME such that battery information 'manufacturer' and 'model_name' can be exported to sysfs. Signed-off-by: Cheng-Yi Chiang Reviewed-by: Olof Johansson Signed-off-by: Javier Martinez Canillas Signed-off-by: Sebastian Reichel diff --git a/drivers/power/sbs-battery.c b/drivers/power/sbs-battery.c index b5f2a76..08feb38 100644 --- a/drivers/power/sbs-battery.c +++ b/drivers/power/sbs-battery.c @@ -49,6 +49,8 @@ enum { REG_DESIGN_CAPACITY, REG_DESIGN_CAPACITY_CHARGE, REG_DESIGN_VOLTAGE, + REG_MANUFACTURER, + REG_MODEL_NAME, }; /* Battery Mode defines */ @@ -68,6 +70,7 @@ enum sbs_battery_mode { #define BATTERY_FULL_CHARGED 0x20 #define BATTERY_FULL_DISCHARGED 0x10 +/* min_value and max_value are only valid for numerical data */ #define SBS_DATA(_psp, _addr, _min_value, _max_value) { \ .psp = _psp, \ .addr = _addr, \ @@ -115,6 +118,11 @@ static const struct chip_data { SBS_DATA(POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, 0x19, 0, 65535), [REG_SERIAL_NUMBER] = SBS_DATA(POWER_SUPPLY_PROP_SERIAL_NUMBER, 0x1C, 0, 65535), + /* Properties of type `const char *' */ + [REG_MANUFACTURER] = + SBS_DATA(POWER_SUPPLY_PROP_MANUFACTURER, 0x20, 0, 65535), + [REG_MODEL_NAME] = + SBS_DATA(POWER_SUPPLY_PROP_MODEL_NAME, 0x21, 0, 65535) }; static enum power_supply_property sbs_properties[] = { @@ -137,6 +145,9 @@ static enum power_supply_property sbs_properties[] = { POWER_SUPPLY_PROP_CHARGE_NOW, POWER_SUPPLY_PROP_CHARGE_FULL, POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + /* Properties of type `const char *' */ + POWER_SUPPLY_PROP_MANUFACTURER, + POWER_SUPPLY_PROP_MODEL_NAME }; struct sbs_info { @@ -153,6 +164,9 @@ struct sbs_info { int ignore_changes; }; +static char model_name[I2C_SMBUS_BLOCK_MAX + 1]; +static char manufacturer[I2C_SMBUS_BLOCK_MAX + 1]; + static int sbs_read_word_data(struct i2c_client *client, u8 address) { struct sbs_info *chip = i2c_get_clientdata(client); @@ -179,6 +193,74 @@ static int sbs_read_word_data(struct i2c_client *client, u8 address) return le16_to_cpu(ret); } +static int sbs_read_string_data(struct i2c_client *client, u8 address, + char *values) +{ + struct sbs_info *chip = i2c_get_clientdata(client); + s32 ret = 0, block_length = 0; + int retries_length = 1, retries_block = 1; + u8 block_buffer[I2C_SMBUS_BLOCK_MAX + 1]; + + if (chip->pdata) { + retries_length = max(chip->pdata->i2c_retry_count + 1, 1); + retries_block = max(chip->pdata->i2c_retry_count + 1, 1); + } + + /* Adapter needs to support these two functions */ + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_I2C_BLOCK)){ + return -ENODEV; + } + + /* Get the length of block data */ + while (retries_length > 0) { + ret = i2c_smbus_read_byte_data(client, address); + if (ret >= 0) + break; + retries_length--; + } + + if (ret < 0) { + dev_dbg(&client->dev, + "%s: i2c read at address 0x%x failed\n", + __func__, address); + return ret; + } + + /* block_length does not include NULL terminator */ + block_length = ret; + if (block_length > I2C_SMBUS_BLOCK_MAX) { + dev_err(&client->dev, + "%s: Returned block_length is longer than 0x%x\n", + __func__, I2C_SMBUS_BLOCK_MAX); + return -EINVAL; + } + + /* Get the block data */ + while (retries_block > 0) { + ret = i2c_smbus_read_i2c_block_data( + client, address, + block_length + 1, block_buffer); + if (ret >= 0) + break; + retries_block--; + } + + if (ret < 0) { + dev_dbg(&client->dev, + "%s: i2c read at address 0x%x failed\n", + __func__, address); + return ret; + } + + /* block_buffer[0] == block_length */ + memcpy(values, block_buffer + 1, block_length); + values[block_length] = '\0'; + + return le16_to_cpu(ret); +} + static int sbs_write_word_data(struct i2c_client *client, u8 address, u16 value) { @@ -318,6 +400,19 @@ static int sbs_get_battery_property(struct i2c_client *client, return 0; } +static int sbs_get_battery_string_property(struct i2c_client *client, + int reg_offset, enum power_supply_property psp, char *val) +{ + s32 ret; + + ret = sbs_read_string_data(client, sbs_data[reg_offset].addr, val); + + if (ret < 0) + return ret; + + return 0; +} + static void sbs_unit_adjustment(struct i2c_client *client, enum power_supply_property psp, union power_supply_propval *val) { @@ -505,6 +600,26 @@ static int sbs_get_property(struct power_supply *psy, ret = sbs_get_battery_property(client, ret, psp, val); break; + case POWER_SUPPLY_PROP_MODEL_NAME: + ret = sbs_get_property_index(client, psp); + if (ret < 0) + break; + + ret = sbs_get_battery_string_property(client, ret, psp, + model_name); + val->strval = model_name; + break; + + case POWER_SUPPLY_PROP_MANUFACTURER: + ret = sbs_get_property_index(client, psp); + if (ret < 0) + break; + + ret = sbs_get_battery_string_property(client, ret, psp, + manufacturer); + val->strval = manufacturer; + break; + default: dev_err(&client->dev, "%s: INVALID property\n", __func__); -- cgit v0.10.2 From 4495b0adfb65a8eb9a5255b831f2160b6e583f3d Mon Sep 17 00:00:00 2001 From: Simon Que Date: Mon, 4 Aug 2014 13:47:46 +0200 Subject: sbs-battery: add min design voltage to sbs-battery sbs-battery has a max design voltage but not a min design voltage field. The SBS spec only has one design voltage: http://www.sbs-forum.org/specs/sbdat110.pdf Currently this is being used for max design voltage. This patch uses it for min design voltage as well. Signed-off-by: Simon Que Reviewed-by: Simon Glass Reviewed-by: Todd Broch Signed-off-by: Javier Martinez Canillas Signed-off-by: Sebastian Reichel diff --git a/drivers/power/sbs-battery.c b/drivers/power/sbs-battery.c index 08feb38..c7b7b40 100644 --- a/drivers/power/sbs-battery.c +++ b/drivers/power/sbs-battery.c @@ -48,7 +48,8 @@ enum { REG_FULL_CHARGE_CAPACITY_CHARGE, REG_DESIGN_CAPACITY, REG_DESIGN_CAPACITY_CHARGE, - REG_DESIGN_VOLTAGE, + REG_DESIGN_VOLTAGE_MIN, + REG_DESIGN_VOLTAGE_MAX, REG_MANUFACTURER, REG_MODEL_NAME, }; @@ -114,7 +115,9 @@ static const struct chip_data { SBS_DATA(POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, 0x18, 0, 65535), [REG_DESIGN_CAPACITY_CHARGE] = SBS_DATA(POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, 0x18, 0, 65535), - [REG_DESIGN_VOLTAGE] = + [REG_DESIGN_VOLTAGE_MIN] = + SBS_DATA(POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, 0x19, 0, 65535), + [REG_DESIGN_VOLTAGE_MAX] = SBS_DATA(POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, 0x19, 0, 65535), [REG_SERIAL_NUMBER] = SBS_DATA(POWER_SUPPLY_PROP_SERIAL_NUMBER, 0x1C, 0, 65535), @@ -138,6 +141,7 @@ static enum power_supply_property sbs_properties[] = { POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, POWER_SUPPLY_PROP_SERIAL_NUMBER, + POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, POWER_SUPPLY_PROP_ENERGY_NOW, POWER_SUPPLY_PROP_ENERGY_FULL, @@ -431,6 +435,7 @@ static void sbs_unit_adjustment(struct i2c_client *client, break; case POWER_SUPPLY_PROP_VOLTAGE_NOW: + case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: case POWER_SUPPLY_PROP_CURRENT_NOW: case POWER_SUPPLY_PROP_CHARGE_NOW: @@ -592,6 +597,7 @@ static int sbs_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_TEMP: case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG: + case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: ret = sbs_get_property_index(client, psp); if (ret < 0) -- cgit v0.10.2 From d3ed534cca703b2aaeee9277a5b8063ae6eab1d1 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 24 Aug 2014 19:36:29 -0500 Subject: power/reset: xgene-reset: Fix prototype of xgene_restart() The xgene-reset driver uses xgene_restart() as arm_pm_restart() but that function should take an enum reset_type as the first argument rather than a char. Fix this; the paramter is not referenced in the implementation. Signed-off-by: Mark Brown Signed-off-by: Sebastian Reichel diff --git a/drivers/power/reset/xgene-reboot.c b/drivers/power/reset/xgene-reboot.c index ecd55f8..6b49be6 100644 --- a/drivers/power/reset/xgene-reboot.c +++ b/drivers/power/reset/xgene-reboot.c @@ -40,7 +40,7 @@ struct xgene_reboot_context { static struct xgene_reboot_context *xgene_restart_ctx; -static void xgene_restart(char str, const char *cmd) +static void xgene_restart(enum reboot_mode mode, const char *cmd) { struct xgene_reboot_context *ctx = xgene_restart_ctx; unsigned long timeout; -- cgit v0.10.2 From c6738d06a363ac7010a5c3b54e2af1957852cc45 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Tue, 26 Aug 2014 13:41:38 +0900 Subject: power: charger-manager: Fix checking of wrong return type This patch fix minor issue about checking wrong return type. The of_cm_parse_desc() return ERR_PTR(errnor number) when some error happen in this function. But, charger_manager_probe() has only checked whether desc is NULL or not. If of_cm_parse_desc() returns ERR_PTR(-ENOMEM), desc isn't NULL but desc is (void *)(-ENOMEM). Althouhg some error happen for parsing DT, charger_manager_probe() can't detect error of desc instance. Signed-off-by: Chanwoo Choi Acked-by: Myungjoo Ham Signed-off-by: Sebastian Reichel diff --git a/drivers/power/charger-manager.c b/drivers/power/charger-manager.c index 9e4dab4..a10fb57 100644 --- a/drivers/power/charger-manager.c +++ b/drivers/power/charger-manager.c @@ -1677,7 +1677,7 @@ static int charger_manager_probe(struct platform_device *pdev) } } - if (!desc) { + if (IS_ERR(desc)) { dev_err(&pdev->dev, "No platform data (desc) found\n"); return -ENODEV; } -- cgit v0.10.2 From b1022e247872dc87a6ffd6f9aa6a92e0b67cdf00 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Tue, 26 Aug 2014 13:41:39 +0900 Subject: power: charger-manager: Check charging state right after completed initialization This patch check the charging state after completed initialization of charger- manager and update current charging state. If charger-manager never check and update current charging state, charger-manager would have the mismatch issue between real state of cable connection and the charging state of charger-manager until first polling time of charger-manager. Signed-off-by: Chanwoo Choi Acked-by: Myungjoo Ham Signed-off-by: Sebastian Reichel diff --git a/drivers/power/charger-manager.c b/drivers/power/charger-manager.c index a10fb57..4e560f7 100644 --- a/drivers/power/charger-manager.c +++ b/drivers/power/charger-manager.c @@ -1839,6 +1839,13 @@ static int charger_manager_probe(struct platform_device *pdev) device_init_wakeup(&pdev->dev, true); device_set_wakeup_capable(&pdev->dev, false); + /* + * Charger-manager have to check the charging state right after + * tialization of charger-manager and then update current charging + * state. + */ + cm_monitor(); + schedule_work(&setup_polling); return 0; -- cgit v0.10.2 From a8adcc9012d8502e06ba7b3f966bad8f2c58edc3 Mon Sep 17 00:00:00 2001 From: Ramakrishna Pallala Date: Wed, 27 Aug 2014 23:44:08 +0530 Subject: power_supply: Add boot and calibration attributes Usually PMIC's come with coulomb counting mechanism which can be used to implement a Fuel Gauginig solution in Software itself. One of key input to these SW Fuel Gauge solutioons is the boot up parameters like boot voltage and boot current. This patch adds the VOLTAGE_BOOT and CURRENT_BOOT power supply attributes to report bootup voltage and current. This patch also adds CALIBRATE power supply attribute which useful is for calibrating the battery/coulomb counter. Signed-off-by: Ramakrishna Pallala Signed-off-by: Sebastian Reichel diff --git a/Documentation/power/power_supply_class.txt b/Documentation/power/power_supply_class.txt index 48cff88..82dacc0 100644 --- a/Documentation/power/power_supply_class.txt +++ b/Documentation/power/power_supply_class.txt @@ -101,6 +101,10 @@ VOLTAGE_MAX, VOLTAGE_MIN - same as _DESIGN voltage values except that these ones should be used if hardware could only guess (measure and retain) the thresholds of a given power supply. +VOLTAGE_BOOT - Reports the voltage measured during boot + +CURRENT_BOOT - Reports the current measured during boot + CHARGE_FULL_DESIGN, CHARGE_EMPTY_DESIGN - design charge values, when battery considered full/empty. @@ -123,6 +127,8 @@ the current drawn from a charging source. CHARGE_TERM_CURRENT - Charge termination current used to detect the end of charge condition. +CALIBRATE - battery or coulomb counter calibration status + CONSTANT_CHARGE_VOLTAGE - constant charge voltage programmed by charger. CONSTANT_CHARGE_VOLTAGE_MAX - maximum charge voltage supported by the power supply object. diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c index 750a202..7e4726d 100644 --- a/drivers/power/power_supply_sysfs.c +++ b/drivers/power/power_supply_sysfs.c @@ -149,9 +149,11 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(voltage_now), POWER_SUPPLY_ATTR(voltage_avg), POWER_SUPPLY_ATTR(voltage_ocv), + POWER_SUPPLY_ATTR(voltage_boot), POWER_SUPPLY_ATTR(current_max), POWER_SUPPLY_ATTR(current_now), POWER_SUPPLY_ATTR(current_avg), + POWER_SUPPLY_ATTR(current_boot), POWER_SUPPLY_ATTR(power_now), POWER_SUPPLY_ATTR(power_avg), POWER_SUPPLY_ATTR(charge_full_design), @@ -193,6 +195,7 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(type), POWER_SUPPLY_ATTR(scope), POWER_SUPPLY_ATTR(charge_term_current), + POWER_SUPPLY_ATTR(calibrate), /* Properties of type `const char *' */ POWER_SUPPLY_ATTR(model_name), POWER_SUPPLY_ATTR(manufacturer), diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index f3dea41..de59a28 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -102,9 +102,11 @@ enum power_supply_property { POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_VOLTAGE_AVG, POWER_SUPPLY_PROP_VOLTAGE_OCV, + POWER_SUPPLY_PROP_VOLTAGE_BOOT, POWER_SUPPLY_PROP_CURRENT_MAX, POWER_SUPPLY_PROP_CURRENT_NOW, POWER_SUPPLY_PROP_CURRENT_AVG, + POWER_SUPPLY_PROP_CURRENT_BOOT, POWER_SUPPLY_PROP_POWER_NOW, POWER_SUPPLY_PROP_POWER_AVG, POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, @@ -146,6 +148,7 @@ enum power_supply_property { POWER_SUPPLY_PROP_TYPE, /* use power_supply.type instead */ POWER_SUPPLY_PROP_SCOPE, POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT, + POWER_SUPPLY_PROP_CALIBRATE, /* Properties of type `const char *' */ POWER_SUPPLY_PROP_MODEL_NAME, POWER_SUPPLY_PROP_MANUFACTURER, @@ -291,6 +294,7 @@ static inline bool power_supply_is_amp_property(enum power_supply_property psp) case POWER_SUPPLY_PROP_CURRENT_MAX: case POWER_SUPPLY_PROP_CURRENT_NOW: case POWER_SUPPLY_PROP_CURRENT_AVG: + case POWER_SUPPLY_PROP_CURRENT_BOOT: return 1; default: break; @@ -315,6 +319,7 @@ static inline bool power_supply_is_watt_property(enum power_supply_property psp) case POWER_SUPPLY_PROP_VOLTAGE_NOW: case POWER_SUPPLY_PROP_VOLTAGE_AVG: case POWER_SUPPLY_PROP_VOLTAGE_OCV: + case POWER_SUPPLY_PROP_VOLTAGE_BOOT: case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: case POWER_SUPPLY_PROP_POWER_NOW: -- cgit v0.10.2 From 86515b7de86443775cf5e821e52df4c0adf528ab Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Fri, 29 Aug 2014 12:45:27 +0900 Subject: power: charger-manager: Remove casting the return value which is a void pointer Casting the return value which is a void pointer is redundant. The conversion from void pointer to any other pointer type is guaranteed by the C programming language. Signed-off-by: Jingoo Han Signed-off-by: Sebastian Reichel diff --git a/drivers/power/charger-manager.c b/drivers/power/charger-manager.c index 4e560f7..c1ed3c9 100644 --- a/drivers/power/charger-manager.c +++ b/drivers/power/charger-manager.c @@ -1656,7 +1656,7 @@ 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); + return dev_get_platdata(&pdev->dev); } static int charger_manager_probe(struct platform_device *pdev) -- cgit v0.10.2 From 8f5a37cb28fce189f3d6802ade98a116f59a47bf Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 4 Sep 2014 17:31:22 +0530 Subject: power-supply: Don't over-allocate memory for "supplied-from" array In routine power_supply_check_supplies(), 'cnt' is counting the number of supplies passed in "power-supplies" field of a node. The value of 'cnt' will always be one more than the number of supplies after the do-while loop ends. And so we need to allocate memory for 'cnt - 1' char pointers. But we are allocating memory for 'cnt' instead. Fix this by not over-allocating memory. Signed-off-by: Viresh Kumar Signed-off-by: Sebastian Reichel diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c index 078afd6..10f0b57 100644 --- a/drivers/power/power_supply_core.c +++ b/drivers/power/power_supply_core.c @@ -234,7 +234,7 @@ static int power_supply_check_supplies(struct power_supply *psy) return -ENOMEM; } - *psy->supplied_from = devm_kzalloc(psy->dev, sizeof(char *) * cnt, + *psy->supplied_from = devm_kzalloc(psy->dev, sizeof(char *) * (cnt - 1), GFP_KERNEL); if (!*psy->supplied_from) { dev_err(psy->dev, "Couldn't allocate memory for supply list\n"); -- cgit v0.10.2 From f9c85486c4ae8fd56770340bd9c16a61127c96e1 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 4 Sep 2014 17:31:23 +0530 Subject: power-supply: Return early if "power-supplies" property isn't valid If power-supply's DT node doesn't have a valid "power-supplies" entry, then power_supply_check_supplies() should return early instead of trying to allocate memory for "supplied_from" array. Signed-off-by: Viresh Kumar Signed-off-by: Sebastian Reichel diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c index 10f0b57..414384a 100644 --- a/drivers/power/power_supply_core.c +++ b/drivers/power/power_supply_core.c @@ -226,6 +226,10 @@ static int power_supply_check_supplies(struct power_supply *psy) of_node_put(np); } while (np); + /* Missing valid "power-supplies" entries */ + if (cnt == 1) + return 0; + /* All supplies found, allocate char ** array for filling */ psy->supplied_from = devm_kzalloc(psy->dev, sizeof(psy->supplied_from), GFP_KERNEL); -- cgit v0.10.2 From d8755dcf6b8107a31f4a573b6d7a4e414dd42415 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 4 Sep 2014 17:31:24 +0530 Subject: Documentation: Charger Manager: Fix spelling mistakes 'unnecessary' was wrongly spelled as 'unncessary', also it should have been 'unnecessarily'. Signed-off-by: Viresh Kumar Signed-off-by: Sebastian Reichel diff --git a/Documentation/power/charger-manager.txt b/Documentation/power/charger-manager.txt index b4f7f4b..9ff1105 100644 --- a/Documentation/power/charger-manager.txt +++ b/Documentation/power/charger-manager.txt @@ -29,7 +29,7 @@ Charger Manager supports the following: While the battery is being charged and the system is in suspend-to-RAM, we may need to monitor the battery health by looking at the ambient or battery temperature. We can accomplish this by waking up the system - periodically. However, such a method wakes up devices unncessary for + periodically. However, such a method wakes up devices unnecessarily for monitoring the battery health and tasks, and user processes that are supposed to be kept suspended. That, in turn, incurs unnecessary power consumption and slow down charging process. Or even, such peak power -- cgit v0.10.2 From 3c5f8853469d3e549799808b9bf639b5d32751f0 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 4 Sep 2014 17:31:25 +0530 Subject: power-supply: Forward declare structs together power_supply.h requires to forward declare few structures. One of them is done at the top of the file and other one just before it is used. Declare them together for better readability. Signed-off-by: Viresh Kumar Signed-off-by: Sebastian Reichel diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index de59a28..3ed0496 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -18,8 +18,6 @@ #include #include -struct device; - /* * All voltages, currents, charges, energies, time and temperatures in uV, * µA, µAh, µWh, seconds and tenths of degree Celsius unless otherwise @@ -175,6 +173,7 @@ union power_supply_propval { const char *strval; }; +struct device; struct device_node; struct power_supply { -- cgit v0.10.2 From e80cf421432bab7327891e7e8afe027fafb4c27b Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 4 Sep 2014 17:31:26 +0530 Subject: power-supply: Drop unnecessary typecasts Typecast from 'void *' to any other pointer type falls under implicit typecasts category and so doesn't require explicit typecasts. Drop them. Signed-off-by: Viresh Kumar Signed-off-by: Sebastian Reichel diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c index 414384a..71d00ec 100644 --- a/drivers/power/power_supply_core.c +++ b/drivers/power/power_supply_core.c @@ -58,7 +58,7 @@ static bool __power_supply_is_supplied_by(struct power_supply *supplier, static int __power_supply_changed_work(struct device *dev, void *data) { - struct power_supply *psy = (struct power_supply *)data; + struct power_supply *psy = data; struct power_supply *pst = dev_get_drvdata(dev); if (__power_supply_is_supplied_by(psy, pst)) { @@ -119,7 +119,7 @@ EXPORT_SYMBOL_GPL(power_supply_changed); static int __power_supply_populate_supplied_from(struct device *dev, void *data) { - struct power_supply *psy = (struct power_supply *)data; + struct power_supply *psy = data; struct power_supply *epsy = dev_get_drvdata(dev); struct device_node *np; int i = 0; @@ -158,7 +158,7 @@ static int power_supply_populate_supplied_from(struct power_supply *psy) static int __power_supply_find_supply_from_node(struct device *dev, void *data) { - struct device_node *np = (struct device_node *)data; + struct device_node *np = data; struct power_supply *epsy = dev_get_drvdata(dev); /* return error breaks out of class_for_each_device loop */ @@ -257,7 +257,7 @@ static inline int power_supply_check_supplies(struct power_supply *psy) static int __power_supply_am_i_supplied(struct device *dev, void *data) { union power_supply_propval ret = {0,}; - struct power_supply *psy = (struct power_supply *)data; + struct power_supply *psy = data; struct power_supply *epsy = dev_get_drvdata(dev); if (__power_supply_is_supplied_by(epsy, psy)) -- cgit v0.10.2 From a0f93b4268d903430d7e21110f10be0919daf3b2 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 4 Sep 2014 17:31:27 +0530 Subject: power-supply: Use 'break' instead of 'continue' to end loop In few routines, we need to end the do-while loop when no more "power-supplies" are available. Currently we are doing 'continue' which will make the 'while(np)' conditional statement run again. Skip this by doing a 'break' instead. Signed-off-by: Viresh Kumar Signed-off-by: Sebastian Reichel diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c index 71d00ec..7657335 100644 --- a/drivers/power/power_supply_core.c +++ b/drivers/power/power_supply_core.c @@ -127,7 +127,7 @@ static int __power_supply_populate_supplied_from(struct device *dev, do { np = of_parse_phandle(psy->of_node, "power-supplies", i++); if (!np) - continue; + break; if (np == epsy->of_node) { dev_info(psy->dev, "%s: Found supply : %s\n", @@ -215,7 +215,7 @@ static int power_supply_check_supplies(struct power_supply *psy) np = of_parse_phandle(psy->of_node, "power-supplies", cnt++); if (!np) - continue; + break; ret = power_supply_find_supply_from_node(np); if (ret) { -- cgit v0.10.2 From 8468b029a2871b1f0e372a9354bddeac6b03af3b Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 4 Sep 2014 17:31:28 +0530 Subject: power-supply: Rearrange code to remove duplicate lines of_node_put() was called twice in power_supply_check_supplies() whereas a single call will also work. Rearrange code a bit to make that feasible. Signed-off-by: Viresh Kumar Signed-off-by: Sebastian Reichel diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c index 7657335..8a86cd1 100644 --- a/drivers/power/power_supply_core.c +++ b/drivers/power/power_supply_core.c @@ -218,12 +218,12 @@ static int power_supply_check_supplies(struct power_supply *psy) break; ret = power_supply_find_supply_from_node(np); + of_node_put(np); + if (ret) { dev_dbg(psy->dev, "Failed to find supply, defer!\n"); - of_node_put(np); return -EPROBE_DEFER; } - of_node_put(np); } while (np); /* Missing valid "power-supplies" entries */ -- cgit v0.10.2 From f5b89affe2b2e6a6092f4228baf08a6dd59bfc61 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 4 Sep 2014 17:31:29 +0530 Subject: power-supply: Propagate error returned by power_supply_find_supply_from_node() Callers of power_supply_find_supply_from_node(), i.e. power_supply_check_supplies(), must propagate the errors returned by it instead of returning their own. Signed-off-by: Viresh Kumar Signed-off-by: Sebastian Reichel diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c index 8a86cd1..ab1cf8b 100644 --- a/drivers/power/power_supply_core.c +++ b/drivers/power/power_supply_core.c @@ -221,8 +221,8 @@ static int power_supply_check_supplies(struct power_supply *psy) of_node_put(np); if (ret) { - dev_dbg(psy->dev, "Failed to find supply, defer!\n"); - return -EPROBE_DEFER; + dev_dbg(psy->dev, "Failed to find supply!\n"); + return ret; } } while (np); -- cgit v0.10.2 From 585b008743b5a14d93e3d506729c73978edc8da7 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 4 Sep 2014 17:31:30 +0530 Subject: power-supply: Don't return -EINVAL from __power_supply_find_supply_from_node() We need to stop 'class_for_each_device' loop when a supply matches with the of-node. In order to achieve this we currently return -EINVAL from __power_supply_populate_supplied_from() on successful match. class_for_each_device() is free to return similar errors in other cases as well and so the choice of return value here isn't particularly great. This commit isn't removing the Hack but making it more elegant by returning '1' instead. Also power_supply_find_supply_from_node() can return errors other than -EPROBE_DEFER now if class_for_each_device() fails. Signed-off-by: Viresh Kumar Signed-off-by: Sebastian Reichel diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c index ab1cf8b..55140eb 100644 --- a/drivers/power/power_supply_core.c +++ b/drivers/power/power_supply_core.c @@ -161,9 +161,9 @@ static int __power_supply_find_supply_from_node(struct device *dev, struct device_node *np = data; struct power_supply *epsy = dev_get_drvdata(dev); - /* return error breaks out of class_for_each_device loop */ + /* returning non-zero breaks out of class_for_each_device loop */ if (epsy->of_node == np) - return -EINVAL; + return 1; return 0; } @@ -186,15 +186,19 @@ static int power_supply_find_supply_from_node(struct device_node *supply_node) return -EPROBE_DEFER; /* - * We have to treat the return value as inverted, because if - * we return error on not found, then it won't continue looking. - * So we trick it by returning error on success to stop looking - * once the matching device is found. + * class_for_each_device() either returns its own errors or values + * returned by __power_supply_find_supply_from_node(). + * + * __power_supply_find_supply_from_node() will return 0 (no match) + * or 1 (match). + * + * We return 0 if class_for_each_device() returned 1, -EPROBE_DEFER if + * it returned 0, or error as returned by it. */ error = class_for_each_device(power_supply_class, NULL, supply_node, __power_supply_find_supply_from_node); - return error ? 0 : -EPROBE_DEFER; + return error ? (error == 1 ? 0 : error) : -EPROBE_DEFER; } static int power_supply_check_supplies(struct power_supply *psy) -- cgit v0.10.2 From 1c42a389eaa0fddca6e6d9625e65ff62c9b90e80 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 4 Sep 2014 17:31:31 +0530 Subject: power-supply: Drop useless 'if (ret.intval)' statements There is no need to check the value of ret.intval before returning it, as we will be returning zero explicitly when ret.intval is zero. So essentially we will end up returning value of ret.intval as it is. Drop the unnecessary 'if' statements. Signed-off-by: Viresh Kumar Signed-off-by: Sebastian Reichel diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c index 55140eb..bcff7fd 100644 --- a/drivers/power/power_supply_core.c +++ b/drivers/power/power_supply_core.c @@ -265,10 +265,8 @@ static int __power_supply_am_i_supplied(struct device *dev, void *data) struct power_supply *epsy = dev_get_drvdata(dev); if (__power_supply_is_supplied_by(epsy, psy)) - if (!epsy->get_property(epsy, POWER_SUPPLY_PROP_ONLINE, &ret)) { - if (ret.intval) - return ret.intval; - } + if (!epsy->get_property(epsy, POWER_SUPPLY_PROP_ONLINE, &ret)) + return ret.intval; return 0; } @@ -293,12 +291,10 @@ static int __power_supply_is_system_supplied(struct device *dev, void *data) unsigned int *count = data; (*count)++; - if (psy->type != POWER_SUPPLY_TYPE_BATTERY) { - if (psy->get_property(psy, POWER_SUPPLY_PROP_ONLINE, &ret)) - return 0; - if (ret.intval) + if (psy->type != POWER_SUPPLY_TYPE_BATTERY) + if (!psy->get_property(psy, POWER_SUPPLY_PROP_ONLINE, &ret)) return ret.intval; - } + return 0; } -- cgit v0.10.2 From 061f3806bbe4d8e86dddf101f75f49c38d3f1669 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 4 Sep 2014 17:31:32 +0530 Subject: power-supply: Mark 'if' blocks in power_supply_changed_work() with 'likely' The 'if' statements in power_supply_changed_work() are mostly there for taking care of races and normally they will always evaluate to true. Optimize them for fast execution with 'likely' statements. Also there is need to have better comments in code to mention about the races clearly. Get them in place. Cc: Zoran Markovic Signed-off-by: Viresh Kumar Signed-off-by: Sebastian Reichel diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c index bcff7fd..26518c8 100644 --- a/drivers/power/power_supply_core.c +++ b/drivers/power/power_supply_core.c @@ -78,7 +78,14 @@ static void power_supply_changed_work(struct work_struct *work) dev_dbg(psy->dev, "%s\n", __func__); spin_lock_irqsave(&psy->changed_lock, flags); - if (psy->changed) { + /* + * Check 'changed' here to avoid issues due to race between + * power_supply_changed() and this routine. In worst case + * power_supply_changed() can be called again just before we take above + * lock. During the first call of this routine we will mark 'changed' as + * false and it will stay false for the next call as well. + */ + if (likely(psy->changed)) { psy->changed = false; spin_unlock_irqrestore(&psy->changed_lock, flags); class_for_each_device(power_supply_class, NULL, psy, @@ -89,12 +96,13 @@ static void power_supply_changed_work(struct work_struct *work) kobject_uevent(&psy->dev->kobj, KOBJ_CHANGE); spin_lock_irqsave(&psy->changed_lock, flags); } + /* - * Dependent power supplies (e.g. battery) may have changed state - * as a result of this event, so poll again and hold the - * wakeup_source until all events are processed. + * Hold the wakeup_source until all events are processed. + * power_supply_changed() might have called again and have set 'changed' + * to true. */ - if (!psy->changed) + if (likely(!psy->changed)) pm_relax(psy->dev); spin_unlock_irqrestore(&psy->changed_lock, flags); } -- cgit v0.10.2 From 9d2410c79b5b2dd741648de26ad52ffd2ce3dc01 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 4 Sep 2014 17:31:33 +0530 Subject: power-supply: Use PTR_ERR_OR_ZERO() routine At multiple places we are doing exactly what PTR_ERR_OR_ZERO() does. And so that routine can be reused instead of increasing lines of code here. Signed-off-by: Viresh Kumar Signed-off-by: Sebastian Reichel diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c index 26518c8..376464e 100644 --- a/drivers/power/power_supply_core.c +++ b/drivers/power/power_supply_core.c @@ -435,9 +435,7 @@ static int psy_register_thermal(struct power_supply *psy) if (psy->properties[i] == POWER_SUPPLY_PROP_TEMP) { psy->tzd = thermal_zone_device_register(psy->name, 0, 0, psy, &psy_tzd_ops, NULL, 0, 0); - if (IS_ERR(psy->tzd)) - return PTR_ERR(psy->tzd); - break; + return PTR_ERR_OR_ZERO(psy->tzd); } } return 0; @@ -515,9 +513,7 @@ static int psy_register_cooler(struct power_supply *psy) psy->tcd = thermal_cooling_device_register( (char *)psy->name, psy, &psy_tcd_ops); - if (IS_ERR(psy->tcd)) - return PTR_ERR(psy->tcd); - break; + return PTR_ERR_OR_ZERO(psy->tcd); } } return 0; -- cgit v0.10.2 From 73b4a087ba4c0d0d52519769320fa684185c563e Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 4 Sep 2014 17:31:34 +0530 Subject: power-supply: Check for failures only when we can fail In power_supply_show_property() routine, we call ->get_property() conditionally and should check for failure in that case only. There is no point comparing 'ret' for errors when 'ret' is surely zero. Signed-off-by: Viresh Kumar Signed-off-by: Sebastian Reichel diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c index 7e4726d..62653f5 100644 --- a/drivers/power/power_supply_sysfs.c +++ b/drivers/power/power_supply_sysfs.c @@ -73,19 +73,20 @@ static ssize_t power_supply_show_property(struct device *dev, const ptrdiff_t off = attr - power_supply_attrs; union power_supply_propval value; - if (off == POWER_SUPPLY_PROP_TYPE) + if (off == POWER_SUPPLY_PROP_TYPE) { value.intval = psy->type; - else + } else { ret = psy->get_property(psy, off, &value); - if (ret < 0) { - if (ret == -ENODATA) - dev_dbg(dev, "driver has no data for `%s' property\n", - attr->attr.name); - else if (ret != -ENODEV) - dev_err(dev, "driver failed to report `%s' property: %zd\n", - attr->attr.name, ret); - return ret; + if (ret < 0) { + if (ret == -ENODATA) + dev_dbg(dev, "driver has no data for `%s' property\n", + attr->attr.name); + else if (ret != -ENODEV) + dev_err(dev, "driver failed to report `%s' property: %zd\n", + attr->attr.name, ret); + return ret; + } } if (off == POWER_SUPPLY_PROP_STATUS) -- cgit v0.10.2 From 464069cae95f71ba670c7e3a90d919b18fb48d66 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 4 Sep 2014 17:31:35 +0530 Subject: power-supply: Avoid unnecessary 'goto' statements Using 'goto' statements for freeing resources on failures is a good choice as it makes code very clean, and reduces the chances of human errors. Though in most cases compiler may take care of this. But adding unnecessary 'goto' statements wouldn't make anything better. Code becomes less readable actually. Signed-off-by: Viresh Kumar Signed-off-by: Sebastian Reichel diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c index 376464e..81177e2 100644 --- a/drivers/power/power_supply_core.c +++ b/drivers/power/power_supply_core.c @@ -599,7 +599,7 @@ static int __power_supply_register(struct device *parent, power_supply_changed(psy); - goto success; + return 0; create_triggers_failed: psy_unregister_cooler(psy); @@ -612,7 +612,6 @@ wakeup_init_failed: check_supplies_failed: dev_set_name_failed: put_device(dev); -success: return rc; } diff --git a/drivers/power/power_supply_leds.c b/drivers/power/power_supply_leds.c index 995f966..effa093 100644 --- a/drivers/power/power_supply_leds.c +++ b/drivers/power/power_supply_leds.c @@ -57,8 +57,6 @@ static void power_supply_update_bat_leds(struct power_supply *psy) static int power_supply_create_bat_triggers(struct power_supply *psy) { - int rc = 0; - psy->charging_full_trig_name = kasprintf(GFP_KERNEL, "%s-charging-or-full", psy->name); if (!psy->charging_full_trig_name) @@ -87,7 +85,7 @@ static int power_supply_create_bat_triggers(struct power_supply *psy) led_trigger_register_simple(psy->charging_blink_full_solid_trig_name, &psy->charging_blink_full_solid_trig); - goto success; + return 0; charging_blink_full_solid_failed: kfree(psy->full_trig_name); @@ -96,9 +94,7 @@ full_failed: charging_failed: kfree(psy->charging_full_trig_name); charging_full_failed: - rc = -ENOMEM; -success: - return rc; + return -ENOMEM; } static void power_supply_remove_bat_triggers(struct power_supply *psy) @@ -132,20 +128,13 @@ static void power_supply_update_gen_leds(struct power_supply *psy) static int power_supply_create_gen_triggers(struct power_supply *psy) { - int rc = 0; - psy->online_trig_name = kasprintf(GFP_KERNEL, "%s-online", psy->name); if (!psy->online_trig_name) - goto online_failed; + return -ENOMEM; led_trigger_register_simple(psy->online_trig_name, &psy->online_trig); - goto success; - -online_failed: - rc = -ENOMEM; -success: - return rc; + return 0; } static void power_supply_remove_gen_triggers(struct power_supply *psy) -- cgit v0.10.2 From 7195c505c2e8e2dd372f7b1737f2a788e74889fb Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 4 Sep 2014 17:31:36 +0530 Subject: power_supply: Don't iterate over devices to return -EPROBE_DEFER This piece of code was added so that we return -EPROBE_DEFER when no devices are registered. But even if class_for_each_device() returns 0, we are going to return -EPROBE_DEFER only. And so this code isn't required at all. Remove it. Signed-off-by: Viresh Kumar Signed-off-by: Sebastian Reichel diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c index 81177e2..6cb7fe5 100644 --- a/drivers/power/power_supply_core.c +++ b/drivers/power/power_supply_core.c @@ -179,19 +179,6 @@ static int __power_supply_find_supply_from_node(struct device *dev, static int power_supply_find_supply_from_node(struct device_node *supply_node) { int error; - struct device *dev; - struct class_dev_iter iter; - - /* - * Use iterator to see if any other device is registered. - * This is required since class_for_each_device returns 0 - * if there are no devices registered. - */ - class_dev_iter_init(&iter, power_supply_class, NULL, NULL); - dev = class_dev_iter_next(&iter); - - if (!dev) - return -EPROBE_DEFER; /* * class_for_each_device() either returns its own errors or values -- cgit v0.10.2 From 6647156c00cc70c1e93161c3cf178071b3381bbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Moll?= Date: Fri, 8 Aug 2014 13:12:17 +0000 Subject: power: reset: add LTC2952 poweroff driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds a driver for the LTC2952, an external power control chip, which signals the OS to shut down. Additionally this driver lets the kernel power down the board. Signed-off-by: René Moll Signed-off-by: Sebastian Reichel diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index ca41523..7615639 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -51,6 +51,13 @@ config POWER_RESET_MSM help Power off and restart support for Qualcomm boards. +config POWER_RESET_LTC2952 + bool "LTC2952 PowerPath power-off driver" + depends on OF_GPIO && POWER_RESET + help + This driver supports an external powerdown trigger and board power + down via the LTC2952. Bindings are made in the device tree. + config POWER_RESET_QNAP bool "QNAP power-off driver" depends on OF_GPIO && POWER_RESET && PLAT_ORION diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile index a42e70e..5d94352 100644 --- a/drivers/power/reset/Makefile +++ b/drivers/power/reset/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_POWER_RESET_BRCMSTB) += brcmstb-reboot.o obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o obj-$(CONFIG_POWER_RESET_HISI) += hisi-reboot.o obj-$(CONFIG_POWER_RESET_MSM) += msm-poweroff.o +obj-$(CONFIG_POWER_RESET_LTC2952) += ltc2952-poweroff.o obj-$(CONFIG_POWER_RESET_QNAP) += qnap-poweroff.o obj-$(CONFIG_POWER_RESET_RESTART) += restart-poweroff.o obj-$(CONFIG_POWER_RESET_SUN6I) += sun6i-reboot.o diff --git a/drivers/power/reset/ltc2952-poweroff.c b/drivers/power/reset/ltc2952-poweroff.c new file mode 100644 index 0000000..116a1ce --- /dev/null +++ b/drivers/power/reset/ltc2952-poweroff.c @@ -0,0 +1,386 @@ +/* + * LTC2952 (PowerPath) driver + * + * Copyright (C) 2014, Xsens Technologies BV + * Maintainer: René Moll + * + * 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 the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * ---------------------------------------- + * - Description + * ---------------------------------------- + * + * This driver is to be used with an external PowerPath Controller (LTC2952). + * Its function is to determine when a external shut down is triggered + * and react by properly shutting down the system. + * + * This driver expects a device tree with a ltc2952 entry for pin mapping. + * + * ---------------------------------------- + * - GPIO + * ---------------------------------------- + * + * The following GPIOs are used: + * - trigger (input) + * A level change indicates the shut-down trigger. If it's state reverts + * within the time-out defined by trigger_delay, the shut down is not + * executed. + * + * - watchdog (output) + * Once a shut down is triggered, the driver will toggle this signal, + * with an internal (wde_interval) to stall the hardware shut down. + * + * - kill (output) + * The last action during shut down is triggering this signalling, such + * that the PowerPath Control will power down the hardware. + * + * ---------------------------------------- + * - Interrupts + * ---------------------------------------- + * + * The driver requires a non-shared, edge-triggered interrupt on the trigger + * GPIO. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct ltc2952_poweroff_data { + struct hrtimer timer_trigger; + struct hrtimer timer_wde; + + ktime_t trigger_delay; + ktime_t wde_interval; + + struct device *dev; + + unsigned int virq; + + /** + * 0: trigger + * 1: watchdog + * 2: kill + */ + struct gpio_desc *gpio[3]; +}; + +static int ltc2952_poweroff_panic; +static struct ltc2952_poweroff_data *ltc2952_data; + +#define POWERPATH_IO_TRIGGER 0 +#define POWERPATH_IO_WATCHDOG 1 +#define POWERPATH_IO_KILL 2 + +/** + * ltc2952_poweroff_timer_wde - Timer callback + * Toggles the watchdog reset signal each wde_interval + * + * @timer: corresponding timer + * + * Returns HRTIMER_RESTART for an infinite loop which will only stop when the + * machine actually shuts down + */ +static enum hrtimer_restart ltc2952_poweroff_timer_wde(struct hrtimer *timer) +{ + ktime_t now; + int state; + unsigned long overruns; + + if (ltc2952_poweroff_panic) + return HRTIMER_NORESTART; + + state = gpiod_get_value(ltc2952_data->gpio[POWERPATH_IO_WATCHDOG]); + gpiod_set_value(ltc2952_data->gpio[POWERPATH_IO_WATCHDOG], !state); + + now = hrtimer_cb_get_time(timer); + overruns = hrtimer_forward(timer, now, ltc2952_data->wde_interval); + + return HRTIMER_RESTART; +} + +static enum hrtimer_restart ltc2952_poweroff_timer_trigger( + struct hrtimer *timer) +{ + int ret; + + ret = hrtimer_start(<c2952_data->timer_wde, + ltc2952_data->wde_interval, HRTIMER_MODE_REL); + + if (ret) { + dev_err(ltc2952_data->dev, "unable to start the timer\n"); + /* + * The device will not toggle the watchdog reset, + * thus shut down is only safe if the PowerPath controller + * has a long enough time-off before triggering a hardware + * power-off. + * + * Only sending a warning as the system will power-off anyway + */ + } + + dev_info(ltc2952_data->dev, "executing shutdown\n"); + + orderly_poweroff(true); + + return HRTIMER_NORESTART; +} + +/** + * ltc2952_poweroff_handler - Interrupt handler + * Triggered each time the trigger signal changes state and (de)activates a + * time-out (timer_trigger). Once the time-out is actually reached the shut + * down is executed. + * + * @irq: IRQ number + * @dev_id: pointer to the main data structure + */ +static irqreturn_t ltc2952_poweroff_handler(int irq, void *dev_id) +{ + int ret; + struct ltc2952_poweroff_data *data = dev_id; + + if (ltc2952_poweroff_panic) + goto irq_ok; + + if (hrtimer_active(&data->timer_wde)) { + /* shutdown is already triggered, nothing to do any more */ + goto irq_ok; + } + + if (!hrtimer_active(&data->timer_trigger)) { + ret = hrtimer_start(&data->timer_trigger, data->trigger_delay, + HRTIMER_MODE_REL); + + if (ret) + dev_err(data->dev, "unable to start the wait timer\n"); + } else { + ret = hrtimer_cancel(&data->timer_trigger); + /* omitting return value check, timer should have been valid */ + } + +irq_ok: + return IRQ_HANDLED; +} + +static void ltc2952_poweroff_kill(void) +{ + gpiod_set_value(ltc2952_data->gpio[POWERPATH_IO_KILL], 1); +} + +static int ltc2952_poweroff_suspend(struct platform_device *pdev, + pm_message_t state) +{ + return -ENOSYS; +} + +static int ltc2952_poweroff_resume(struct platform_device *pdev) +{ + return -ENOSYS; +} + +static void ltc2952_poweroff_default(struct ltc2952_poweroff_data *data) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(data->gpio); i++) + data->gpio[i] = NULL; + + data->wde_interval = ktime_set(0, 300L*1E6L); + data->trigger_delay = ktime_set(2, 500L*1E6L); + + hrtimer_init(&data->timer_trigger, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + data->timer_trigger.function = <c2952_poweroff_timer_trigger; + + hrtimer_init(&data->timer_wde, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + data->timer_wde.function = <c2952_poweroff_timer_wde; +} + +static int ltc2952_poweroff_init(struct platform_device *pdev) +{ + int ret, virq; + unsigned int i; + struct ltc2952_poweroff_data *data; + + static char *name[] = { + "trigger", + "watchdog", + "kill", + NULL + }; + + data = ltc2952_data; + ltc2952_poweroff_default(ltc2952_data); + + for (i = 0; i < ARRAY_SIZE(ltc2952_data->gpio); i++) { + ltc2952_data->gpio[i] = gpiod_get(&pdev->dev, name[i]); + + if (IS_ERR(ltc2952_data->gpio[i])) { + ret = PTR_ERR(ltc2952_data->gpio[i]); + dev_err(&pdev->dev, + "unable to claim the following gpio: %s\n", + name[i]); + goto err_io; + } + } + + ret = gpiod_direction_output( + ltc2952_data->gpio[POWERPATH_IO_WATCHDOG], 0); + if (ret) { + dev_err(&pdev->dev, "unable to use watchdog-gpio as output\n"); + goto err_io; + } + + ret = gpiod_direction_output(ltc2952_data->gpio[POWERPATH_IO_KILL], 0); + if (ret) { + dev_err(&pdev->dev, "unable to use kill-gpio as output\n"); + goto err_io; + } + + virq = gpiod_to_irq(ltc2952_data->gpio[POWERPATH_IO_TRIGGER]); + if (virq < 0) { + dev_err(&pdev->dev, "cannot map GPIO as interrupt"); + goto err_io; + } + + ltc2952_data->virq = virq; + ret = request_irq(virq, + ltc2952_poweroff_handler, + (IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING), + "ltc2952-poweroff", + ltc2952_data + ); + + if (ret) { + dev_err(&pdev->dev, "cannot configure an interrupt handler\n"); + goto err_io; + } + + return 0; + +err_io: + for (i = 0; i < ARRAY_SIZE(ltc2952_data->gpio); i++) + if (ltc2952_data->gpio[i]) + gpiod_put(ltc2952_data->gpio[i]); + + return ret; +} + +static int ltc2952_poweroff_probe(struct platform_device *pdev) +{ + int ret; + + if (pm_power_off) { + dev_err(&pdev->dev, "pm_power_off already registered"); + return -EBUSY; + } + + ltc2952_data = kzalloc(sizeof(*ltc2952_data), GFP_KERNEL); + if (!ltc2952_data) + return -ENOMEM; + + ltc2952_data->dev = &pdev->dev; + + ret = ltc2952_poweroff_init(pdev); + if (ret) + goto err; + + pm_power_off = <c2952_poweroff_kill; + + dev_info(&pdev->dev, "probe successful\n"); + + return 0; + +err: + kfree(ltc2952_data); + return ret; +} + +static int ltc2952_poweroff_remove(struct platform_device *pdev) +{ + unsigned int i; + + pm_power_off = NULL; + + if (ltc2952_data) { + free_irq(ltc2952_data->virq, ltc2952_data); + + for (i = 0; i < ARRAY_SIZE(ltc2952_data->gpio); i++) + gpiod_put(ltc2952_data->gpio[i]); + + kfree(ltc2952_data); + } + + return 0; +} + +static const struct of_device_id of_ltc2952_poweroff_match[] = { + { .compatible = "lltc,ltc2952"}, + {}, +}; +MODULE_DEVICE_TABLE(of, of_ltc2952_poweroff_match); + +static struct platform_driver ltc2952_poweroff_driver = { + .probe = ltc2952_poweroff_probe, + .remove = ltc2952_poweroff_remove, + .driver = { + .name = "ltc2952-poweroff", + .owner = THIS_MODULE, + .of_match_table = of_ltc2952_poweroff_match, + }, + .suspend = ltc2952_poweroff_suspend, + .resume = ltc2952_poweroff_resume, +}; + +static int ltc2952_poweroff_notify_panic(struct notifier_block *nb, + unsigned long code, void *unused) +{ + ltc2952_poweroff_panic = 1; + return NOTIFY_DONE; +} + +static struct notifier_block ltc2952_poweroff_panic_nb = { + .notifier_call = ltc2952_poweroff_notify_panic, +}; + +static int __init ltc2952_poweroff_platform_init(void) +{ + ltc2952_poweroff_panic = 0; + + atomic_notifier_chain_register(&panic_notifier_list, + <c2952_poweroff_panic_nb); + + return platform_driver_register(<c2952_poweroff_driver); +} + +static void __exit ltc2952_poweroff_platform_exit(void) +{ + atomic_notifier_chain_unregister(&panic_notifier_list, + <c2952_poweroff_panic_nb); + + platform_driver_unregister(<c2952_poweroff_driver); +} + +module_init(ltc2952_poweroff_platform_init); +module_exit(ltc2952_poweroff_platform_exit); + +MODULE_AUTHOR("René Moll "); +MODULE_DESCRIPTION("LTC PowerPath power-off driver"); +MODULE_LICENSE("GPL v2"); -- cgit v0.10.2 From a4b7aef6b066bda215566cf4af3455a6e36c381d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Moll?= Date: Fri, 8 Aug 2014 13:12:22 +0000 Subject: Documentation: DT: Add LTC2952 poweroff bindings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit LTC2952 is an external power control chip, which signals the OS to shut down. This patch documents the DT binding for the chip. Signed-off-by: René Moll Signed-off-by: Sebastian Reichel diff --git a/Documentation/devicetree/bindings/power/reset/ltc2952-poweroff.txt b/Documentation/devicetree/bindings/power/reset/ltc2952-poweroff.txt new file mode 100644 index 0000000..0c94c63 --- /dev/null +++ b/Documentation/devicetree/bindings/power/reset/ltc2952-poweroff.txt @@ -0,0 +1,26 @@ +Binding for the LTC2952 PowerPath controller + +This chip is used to externally trigger a system shut down. Once the trigger has +been sent, the chips' watchdog has to be reset to gracefully shut down. +If the Linux systems decides to shut down it powers off the platform via the +kill signal. + +Required properties: + +- compatible: Must contain: "lltc,ltc2952" +- trigger-gpios: phandle + gpio-specifier for the GPIO connected to the + chip's trigger line +- watchdog-gpios: phandle + gpio-specifier for the GPIO connected to the + chip's watchdog line +- kill-gpios: phandle + gpio-specifier for the GPIO connected to the + chip's kill line + +Example: + +ltc2952 { + compatible = "lltc,ltc2952"; + + trigger-gpios = <&gpio0 1 GPIO_ACTIVE_LOW>; + watchdog-gpios = <&gpio1 2 GPIO_ACTIVE_HIGH>; + kill-gpios = <&gpio0 2 GPIO_ACTIVE_LOW>; +}; -- cgit v0.10.2 From 25cc24c200dcba21bd1f1a59a01741185062dc0e Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 12 Sep 2014 08:53:53 +0200 Subject: mfd: max14577: Add defines for MAX77836 charger Prepare for adding support for MAX77836 charger to the max14577 charger driver by adding necessary new defines and prefixes to existing ones. The MAX77836 uses slightly different values for ChgTyp field of STATUS2 register. On the MAX14577 value of 0x6 is reserved and 0x7 dead battery. On the MAX77836 the opposite: - 0x6 means special charger, - 0x7 is reserved. Regardless of these differences use one common enum max14577_muic_charger_type. Signed-off-by: Krzysztof Kozlowski Cc: Kyungmin Park Acked-by: Lee Jones Acked-by: Andrew Morton diff --git a/include/linux/mfd/max14577-private.h b/include/linux/mfd/max14577-private.h index 4992536..d6f3216 100644 --- a/include/linux/mfd/max14577-private.h +++ b/include/linux/mfd/max14577-private.h @@ -72,15 +72,33 @@ enum max14577_muic_reg { MAX14577_MUIC_REG_END, }; +/* + * Combined charger types for max14577 and max77836. + * + * On max14577 three lower bits map to STATUS2/CHGTYP field. + * However the max77836 has different two last values of STATUS2/CHGTYP. + * To indicate the difference enum has two additional values for max77836. + * These values are just a register value bitwise OR with 0x8. + */ enum max14577_muic_charger_type { - MAX14577_CHARGER_TYPE_NONE = 0, - MAX14577_CHARGER_TYPE_USB, - MAX14577_CHARGER_TYPE_DOWNSTREAM_PORT, - MAX14577_CHARGER_TYPE_DEDICATED_CHG, - MAX14577_CHARGER_TYPE_SPECIAL_500MA, - MAX14577_CHARGER_TYPE_SPECIAL_1A, - MAX14577_CHARGER_TYPE_RESERVED, - MAX14577_CHARGER_TYPE_DEAD_BATTERY = 7, + MAX14577_CHARGER_TYPE_NONE = 0x0, + MAX14577_CHARGER_TYPE_USB = 0x1, + MAX14577_CHARGER_TYPE_DOWNSTREAM_PORT = 0x2, + MAX14577_CHARGER_TYPE_DEDICATED_CHG = 0x3, + MAX14577_CHARGER_TYPE_SPECIAL_500MA = 0x4, + /* Special 1A or 2A charger */ + MAX14577_CHARGER_TYPE_SPECIAL_1A = 0x5, + /* max14577: reserved, used on max77836 */ + MAX14577_CHARGER_TYPE_RESERVED = 0x6, + /* max14577: dead-battery charing with maximum current 100mA */ + MAX14577_CHARGER_TYPE_DEAD_BATTERY = 0x7, + /* + * max77836: special charger (bias on D+/D-), + * matches register value of 0x6 + */ + MAX77836_CHARGER_TYPE_SPECIAL_BIAS = 0xe, + /* max77836: reserved, register value 0x7 */ + MAX77836_CHARGER_TYPE_RESERVED = 0xf, }; /* MAX14577 interrupts */ @@ -121,13 +139,15 @@ enum max14577_muic_charger_type { #define STATUS2_CHGTYP_SHIFT 0 #define STATUS2_CHGDETRUN_SHIFT 3 #define STATUS2_DCDTMR_SHIFT 4 -#define STATUS2_DBCHG_SHIFT 5 +#define MAX14577_STATUS2_DBCHG_SHIFT 5 +#define MAX77836_STATUS2_DXOVP_SHIFT 5 #define STATUS2_VBVOLT_SHIFT 6 #define MAX77836_STATUS2_VIDRM_SHIFT 7 #define STATUS2_CHGTYP_MASK (0x7 << STATUS2_CHGTYP_SHIFT) #define STATUS2_CHGDETRUN_MASK BIT(STATUS2_CHGDETRUN_SHIFT) #define STATUS2_DCDTMR_MASK BIT(STATUS2_DCDTMR_SHIFT) -#define STATUS2_DBCHG_MASK BIT(STATUS2_DBCHG_SHIFT) +#define MAX14577_STATUS2_DBCHG_MASK BIT(MAX14577_STATUS2_DBCHG_SHIFT) +#define MAX77836_STATUS2_DXOVP_MASK BIT(MAX77836_STATUS2_DXOVP_SHIFT) #define STATUS2_VBVOLT_MASK BIT(STATUS2_VBVOLT_SHIFT) #define MAX77836_STATUS2_VIDRM_MASK BIT(MAX77836_STATUS2_VIDRM_SHIFT) @@ -177,9 +197,11 @@ enum max14577_muic_charger_type { #define CTRL3_JIGSET_SHIFT 0 #define CTRL3_BOOTSET_SHIFT 2 #define CTRL3_ADCDBSET_SHIFT 4 +#define CTRL3_WBTH_SHIFT 6 #define CTRL3_JIGSET_MASK (0x3 << CTRL3_JIGSET_SHIFT) #define CTRL3_BOOTSET_MASK (0x3 << CTRL3_BOOTSET_SHIFT) #define CTRL3_ADCDBSET_MASK (0x3 << CTRL3_ADCDBSET_SHIFT) +#define CTRL3_WBTH_MASK (0x3 << CTRL3_WBTH_SHIFT) /* Slave addr = 0x4A: Charger */ enum max14577_charger_reg { @@ -210,16 +232,20 @@ enum max14577_charger_reg { #define CDETCTRL1_CHGTYPMAN_SHIFT 1 #define CDETCTRL1_DCDEN_SHIFT 2 #define CDETCTRL1_DCD2SCT_SHIFT 3 -#define CDETCTRL1_DCHKTM_SHIFT 4 -#define CDETCTRL1_DBEXIT_SHIFT 5 +#define MAX14577_CDETCTRL1_DCHKTM_SHIFT 4 +#define MAX77836_CDETCTRL1_CDLY_SHIFT 4 +#define MAX14577_CDETCTRL1_DBEXIT_SHIFT 5 +#define MAX77836_CDETCTRL1_DCDCPL_SHIFT 5 #define CDETCTRL1_DBIDLE_SHIFT 6 #define CDETCTRL1_CDPDET_SHIFT 7 #define CDETCTRL1_CHGDETEN_MASK BIT(CDETCTRL1_CHGDETEN_SHIFT) #define CDETCTRL1_CHGTYPMAN_MASK BIT(CDETCTRL1_CHGTYPMAN_SHIFT) #define CDETCTRL1_DCDEN_MASK BIT(CDETCTRL1_DCDEN_SHIFT) #define CDETCTRL1_DCD2SCT_MASK BIT(CDETCTRL1_DCD2SCT_SHIFT) -#define CDETCTRL1_DCHKTM_MASK BIT(CDETCTRL1_DCHKTM_SHIFT) -#define CDETCTRL1_DBEXIT_MASK BIT(CDETCTRL1_DBEXIT_SHIFT) +#define MAX14577_CDETCTRL1_DCHKTM_MASK BIT(MAX14577_CDETCTRL1_DCHKTM_SHIFT) +#define MAX77836_CDETCTRL1_CDDLY_MASK BIT(MAX77836_CDETCTRL1_CDDLY_SHIFT) +#define MAX14577_CDETCTRL1_DBEXIT_MASK BIT(MAX14577_CDETCTRL1_DBEXIT_SHIFT) +#define MAX77836_CDETCTRL1_DCDCPL_MASK BIT(MAX77836_CDETCTRL1_DCDCPL_SHIFT) #define CDETCTRL1_DBIDLE_MASK BIT(CDETCTRL1_DBIDLE_SHIFT) #define CDETCTRL1_CDPDET_MASK BIT(CDETCTRL1_CDPDET_SHIFT) -- cgit v0.10.2 From 4476767cff44737f8965b34af0f375ac49f7fafa Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 12 Sep 2014 08:53:54 +0200 Subject: mfd: max14577: Map charger device to its own of_node Add a "maxim,max14577-charger" of_compatible to the mfd_cell so the MFD child device (the charger) will have its own of_node set. This will be used by the max14577 charger driver in next patches to obtain battery configuration from DTS. Signed-off-by: Krzysztof Kozlowski Acked-by: Andrew Morton Signed-off-by: Lee Jones diff --git a/drivers/mfd/max14577.c b/drivers/mfd/max14577.c index 4a5e885..6599407 100644 --- a/drivers/mfd/max14577.c +++ b/drivers/mfd/max14577.c @@ -35,7 +35,10 @@ static const struct mfd_cell max14577_devs[] = { .name = "max14577-regulator", .of_compatible = "maxim,max14577-regulator", }, - { .name = "max14577-charger", }, + { + .name = "max14577-charger", + .of_compatible = "maxim,max14577-charger", + }, }; static const struct mfd_cell max77836_devs[] = { -- cgit v0.10.2 From 3682a8ee87f9107253e51733f42da10160ce41e3 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 12 Sep 2014 08:53:55 +0200 Subject: charger: max14577: Add support for MAX77836 charger Add support for MAX77836 charger to the max14577 driver. The MAX77836 charger is almost the same as 14577 model except: - No dead-battery detection; - Support for special charger (like in MAX77693); - Support for DX over-voltage protection (like in MAX77693); - Lower values of charging current (two times lower current for slow/fast charge, much lower EOC current); - Slightly different values in ChgTyp field of STATUS2 register. On MAX14577 0x6 is reserved and 0x7 dead battery. On the MAX77836 the 0x6 means special charger and 0x7 is reserved. Regardless of these differences the driver maps them to one enum max14577_muic_charger_type. Signed-off-by: Krzysztof Kozlowski Acked-by: Andrew Morton Signed-off-by: Lee Jones diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 73cfcdf..69fa8a9 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -325,11 +325,11 @@ config CHARGER_MANAGER with help of suspend_again support. config CHARGER_MAX14577 - tristate "Maxim MAX14577 MUIC battery charger driver" + tristate "Maxim MAX14577/77836 battery charger driver" depends on MFD_MAX14577 help Say Y to enable support for the battery charger control sysfs and - platform data of MAX14577 MUICs. + platform data of MAX14577/77836 MUICs. config CHARGER_MAX8997 tristate "Maxim MAX8997/MAX8966 PMIC battery charger driver" diff --git a/drivers/power/max14577_charger.c b/drivers/power/max14577_charger.c index fad2a75..1935305 100644 --- a/drivers/power/max14577_charger.c +++ b/drivers/power/max14577_charger.c @@ -1,7 +1,7 @@ /* - * Battery charger driver for the Maxim 14577 + * max14577_charger.c - Battery charger driver for the Maxim 14577/77836 * - * Copyright (C) 2013 Samsung Electronics + * Copyright (C) 2013,2014 Samsung Electronics * Krzysztof Kozlowski * * This program is free software; you can redistribute it and/or modify @@ -25,10 +25,35 @@ struct max14577_charger { struct max14577 *max14577; struct power_supply charger; - unsigned int charging_state; - unsigned int battery_state; + unsigned int charging_state; + unsigned int battery_state; }; +/* + * Helper function for mapping values of STATUS2/CHGTYP register on max14577 + * and max77836 chipsets to enum maxim_muic_charger_type. + */ +static enum max14577_muic_charger_type maxim_get_charger_type( + enum maxim_device_type dev_type, u8 val) { + switch (val) { + case MAX14577_CHARGER_TYPE_NONE: + case MAX14577_CHARGER_TYPE_USB: + case MAX14577_CHARGER_TYPE_DOWNSTREAM_PORT: + case MAX14577_CHARGER_TYPE_DEDICATED_CHG: + case MAX14577_CHARGER_TYPE_SPECIAL_500MA: + case MAX14577_CHARGER_TYPE_SPECIAL_1A: + return val; + case MAX14577_CHARGER_TYPE_DEAD_BATTERY: + case MAX14577_CHARGER_TYPE_RESERVED: + if (dev_type == MAXIM_DEVICE_TYPE_MAX77836) + val |= 0x8; + return val; + default: + WARN_ONCE(1, "max14577: Unsupported chgtyp register value 0x%02x", val); + return val; + } +} + static int max14577_get_charger_state(struct max14577_charger *chg) { struct regmap *rmap = chg->max14577->regmap; @@ -89,19 +114,23 @@ static int max14577_get_online(struct max14577_charger *chg) { struct regmap *rmap = chg->max14577->regmap; u8 reg_data; + enum max14577_muic_charger_type chg_type; max14577_read_reg(rmap, MAX14577_MUIC_REG_STATUS2, ®_data); reg_data = ((reg_data & STATUS2_CHGTYP_MASK) >> STATUS2_CHGTYP_SHIFT); - switch (reg_data) { + chg_type = maxim_get_charger_type(chg->max14577->dev_type, reg_data); + switch (chg_type) { case MAX14577_CHARGER_TYPE_USB: case MAX14577_CHARGER_TYPE_DEDICATED_CHG: case MAX14577_CHARGER_TYPE_SPECIAL_500MA: case MAX14577_CHARGER_TYPE_SPECIAL_1A: case MAX14577_CHARGER_TYPE_DEAD_BATTERY: + case MAX77836_CHARGER_TYPE_SPECIAL_BIAS: return 1; case MAX14577_CHARGER_TYPE_NONE: case MAX14577_CHARGER_TYPE_DOWNSTREAM_PORT: case MAX14577_CHARGER_TYPE_RESERVED: + case MAX77836_CHARGER_TYPE_RESERVED: default: return 0; } @@ -118,10 +147,12 @@ static int max14577_get_battery_health(struct max14577_charger *chg) struct regmap *rmap = chg->max14577->regmap; int state = POWER_SUPPLY_HEALTH_GOOD; u8 reg_data; + enum max14577_muic_charger_type chg_type; max14577_read_reg(rmap, MAX14577_MUIC_REG_STATUS2, ®_data); reg_data = ((reg_data & STATUS2_CHGTYP_MASK) >> STATUS2_CHGTYP_SHIFT); - if (reg_data == MAX14577_CHARGER_TYPE_DEAD_BATTERY) { + chg_type = maxim_get_charger_type(chg->max14577->dev_type, reg_data); + if (chg_type == MAX14577_CHARGER_TYPE_DEAD_BATTERY) { state = POWER_SUPPLY_HEALTH_DEAD; goto state_set; } @@ -167,7 +198,7 @@ static void max14577_charger_reg_init(struct max14577_charger *chg) CDETCTRL1_CHGDETEN_MASK | CDETCTRL1_CHGTYPMAN_MASK, reg_data); - /* Battery Fast-Charge Timer, from SM-V700: 6hrs */ + /* Battery Fast-Charge Timer, set to: 6hrs */ reg_data = 0x3 << CHGCTRL1_TCHW_SHIFT; max14577_write_reg(rmap, MAX14577_REG_CHGCTRL1, reg_data); @@ -179,19 +210,22 @@ static void max14577_charger_reg_init(struct max14577_charger *chg) reg_data |= 0x1 << CHGCTRL2_MBCHOSTEN_SHIFT; max14577_write_reg(rmap, MAX14577_REG_CHGCTRL2, reg_data); - /* Battery-Charger Constant Voltage (CV) Mode, from SM-V700: 4.35V */ + /* Battery-Charger Constant Voltage (CV) Mode, set to: 4.35V */ reg_data = 0xf << CHGCTRL3_MBCCVWRC_SHIFT; max14577_write_reg(rmap, MAX14577_REG_CHGCTRL3, reg_data); /* - * Fast Battery-Charge Current Low, default 200-950mA - * Fast Battery-Charge Current High, from SM-V700: 450mA + * Fast Battery-Charge Current Low, + * default 200-950mA (max14577) / 100-475mA (max77836) + * + * Fast Battery-Charge Current High, + * set to 450mA (max14577) / 225mA (max77836) */ reg_data = 0x1 << CHGCTRL4_MBCICHWRCL_SHIFT; reg_data |= 0x5 << CHGCTRL4_MBCICHWRCH_SHIFT; max14577_write_reg(rmap, MAX14577_REG_CHGCTRL4, reg_data); - /* End-of-Charge Current, from SM-V700: 50mA */ + /* End-of-Charge Current, set to 50mA (max14577) / 7.5mA (max77836) */ reg_data = 0x0 << CHGCTRL5_EOCS_SHIFT; max14577_write_reg(rmap, MAX14577_REG_CHGCTRL5, reg_data); @@ -199,7 +233,7 @@ static void max14577_charger_reg_init(struct max14577_charger *chg) reg_data = 0x0 << CHGCTRL6_AUTOSTOP_SHIFT; max14577_write_reg(rmap, MAX14577_REG_CHGCTRL6, reg_data); - /* Overvoltage-Protection Threshold, from SM-V700: 6.5V */ + /* Overvoltage-Protection Threshold, set to 6.5V */ reg_data = 0x2 << CHGCTRL7_OTPCGHCVS_SHIFT; max14577_write_reg(rmap, MAX14577_REG_CHGCTRL7, reg_data); } @@ -215,7 +249,11 @@ static enum power_supply_property max14577_charger_props[] = { POWER_SUPPLY_PROP_MANUFACTURER, }; -static const char *model_name = "MAX14577"; +static const char * const model_names[] = { + [MAXIM_DEVICE_TYPE_UNKNOWN] = "MAX14577-like", + [MAXIM_DEVICE_TYPE_MAX14577] = "MAX14577", + [MAXIM_DEVICE_TYPE_MAX77836] = "MAX77836", +}; static const char *manufacturer = "Maxim Integrated"; static int max14577_charger_get_property(struct power_supply *psy, @@ -244,7 +282,8 @@ static int max14577_charger_get_property(struct power_supply *psy, val->intval = max14577_get_online(chg); break; case POWER_SUPPLY_PROP_MODEL_NAME: - val->strval = model_name; + BUILD_BUG_ON(ARRAY_SIZE(model_names) != MAXIM_DEVICE_TYPE_NUM); + val->strval = model_names[chg->max14577->dev_type]; break; case POWER_SUPPLY_PROP_MANUFACTURER: val->strval = manufacturer; @@ -296,6 +335,13 @@ static int max14577_charger_remove(struct platform_device *pdev) return 0; } +static const struct platform_device_id max14577_charger_id[] = { + { "max14577-charger", MAXIM_DEVICE_TYPE_MAX14577, }, + { "max77836-charger", MAXIM_DEVICE_TYPE_MAX77836, }, + { } +}; +MODULE_DEVICE_TABLE(platform, max14577_charger_id); + static struct platform_driver max14577_charger_driver = { .driver = { .owner = THIS_MODULE, @@ -303,9 +349,10 @@ static struct platform_driver max14577_charger_driver = { }, .probe = max14577_charger_probe, .remove = max14577_charger_remove, + .id_table = max14577_charger_id, }; module_platform_driver(max14577_charger_driver); MODULE_AUTHOR("Krzysztof Kozlowski "); -MODULE_DESCRIPTION("MAXIM 14577 charger driver"); +MODULE_DESCRIPTION("Maxim 14577/77836 charger driver"); MODULE_LICENSE("GPL"); -- cgit v0.10.2 From b8f139f68f2099b7f8b4ef470a1e53210e3aa025 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 12 Sep 2014 08:53:56 +0200 Subject: regulator/mfd: max14577: Export symbols for calculating charger current This patch prepares for changing the max14577 charger driver to allow configuring battery-dependent settings from DTS. The patch moves from regulator driver to MFD core driver and exports: - function for calculating register value for charger's current; - table of limits for chargers (MAX14577, MAX77836). Previously they were used only by the max14577 regulator driver. In next patch the charger driver will use them as well. Exporting them will reduce unnecessary code duplication. Signed-off-by: Krzysztof Kozlowski Acked-by: Mark Brown Acked-by: Andrew Morton Signed-off-by: Lee Jones diff --git a/drivers/mfd/max14577.c b/drivers/mfd/max14577.c index 6599407..b8af263 100644 --- a/drivers/mfd/max14577.c +++ b/drivers/mfd/max14577.c @@ -26,6 +26,87 @@ #include #include +/* + * Table of valid charger currents for different Maxim chipsets. + * It is placed here because it is used by both charger and regulator driver. + */ +const struct maxim_charger_current maxim_charger_currents[] = { + [MAXIM_DEVICE_TYPE_UNKNOWN] = { 0, 0, 0, 0 }, + [MAXIM_DEVICE_TYPE_MAX14577] = { + .min = MAX14577_CHARGER_CURRENT_LIMIT_MIN, + .high_start = MAX14577_CHARGER_CURRENT_LIMIT_HIGH_START, + .high_step = MAX14577_CHARGER_CURRENT_LIMIT_HIGH_STEP, + .max = MAX14577_CHARGER_CURRENT_LIMIT_MAX, + }, + [MAXIM_DEVICE_TYPE_MAX77836] = { + .min = MAX77836_CHARGER_CURRENT_LIMIT_MIN, + .high_start = MAX77836_CHARGER_CURRENT_LIMIT_HIGH_START, + .high_step = MAX77836_CHARGER_CURRENT_LIMIT_HIGH_STEP, + .max = MAX77836_CHARGER_CURRENT_LIMIT_MAX, + }, +}; +EXPORT_SYMBOL_GPL(maxim_charger_currents); + +/* + * maxim_charger_calc_reg_current - Calculate register value for current + * @limits: constraints for charger, matching the MBCICHWRC register + * @min_ua: minimal requested current, micro Amps + * @max_ua: maximum requested current, micro Amps + * @dst: destination to store calculated register value + * + * Calculates the value of MBCICHWRC (Fast Battery Charge Current) register + * for given current and stores it under pointed 'dst'. The stored value + * combines low bit (MBCICHWRCL) and high bits (MBCICHWRCH). It is also + * properly shifted. + * + * The calculated register value matches the current which: + * - is always between ; + * - is always less or equal to max_ua; + * - is the highest possible value; + * - may be lower than min_ua. + * + * On success returns 0. On error returns -EINVAL (requested min/max current + * is outside of given charger limits) and 'dst' is not set. + */ +int maxim_charger_calc_reg_current(const struct maxim_charger_current *limits, + unsigned int min_ua, unsigned int max_ua, u8 *dst) +{ + unsigned int current_bits = 0xf; + + if (min_ua > max_ua) + return -EINVAL; + + if (min_ua > limits->max || max_ua < limits->min) + return -EINVAL; + + if (max_ua < limits->high_start) { + /* + * Less than high_start, so set the minimal current + * (turn Low Bit off, 0 as high bits). + */ + *dst = 0x0; + return 0; + } + + /* max_ua is in range: , cut it to limits.max */ + max_ua = min(limits->max, max_ua); + max_ua -= limits->high_start; + /* + * There is no risk of overflow 'max_ua' here because: + * - max_ua >= limits.high_start + * - BUILD_BUG checks that 'limits' are: max >= high_start + high_step + */ + current_bits = max_ua / limits->high_step; + + /* Turn Low Bit on (use range ) ... */ + *dst = 0x1 << CHGCTRL4_MBCICHWRCL_SHIFT; + /* and set proper High Bits */ + *dst |= current_bits << CHGCTRL4_MBCICHWRCH_SHIFT; + + return 0; +} +EXPORT_SYMBOL_GPL(maxim_charger_calc_reg_current); + static const struct mfd_cell max14577_devs[] = { { .name = "max14577-muic", @@ -466,6 +547,20 @@ static int __init max14577_i2c_init(void) BUILD_BUG_ON(ARRAY_SIZE(max14577_i2c_id) != MAXIM_DEVICE_TYPE_NUM); BUILD_BUG_ON(ARRAY_SIZE(max14577_dt_match) != MAXIM_DEVICE_TYPE_NUM); + /* Valid charger current values must be provided for each chipset */ + BUILD_BUG_ON(ARRAY_SIZE(maxim_charger_currents) != MAXIM_DEVICE_TYPE_NUM); + + /* Check for valid values for charger */ + BUILD_BUG_ON(MAX14577_CHARGER_CURRENT_LIMIT_HIGH_START + + MAX14577_CHARGER_CURRENT_LIMIT_HIGH_STEP * 0xf != + MAX14577_CHARGER_CURRENT_LIMIT_MAX); + BUILD_BUG_ON(MAX14577_CHARGER_CURRENT_LIMIT_HIGH_STEP == 0); + + BUILD_BUG_ON(MAX77836_CHARGER_CURRENT_LIMIT_HIGH_START + + MAX77836_CHARGER_CURRENT_LIMIT_HIGH_STEP * 0xf != + MAX77836_CHARGER_CURRENT_LIMIT_MAX); + BUILD_BUG_ON(MAX77836_CHARGER_CURRENT_LIMIT_HIGH_STEP == 0); + return i2c_add_driver(&max14577_i2c_driver); } subsys_initcall(max14577_i2c_init); diff --git a/drivers/regulator/max14577.c b/drivers/regulator/max14577.c index 5d9c605..0ff5a20 100644 --- a/drivers/regulator/max14577.c +++ b/drivers/regulator/max14577.c @@ -22,42 +22,6 @@ #include #include -/* - * Valid limits of current for max14577 and max77836 chargers. - * They must correspond to MBCICHWRCL and MBCICHWRCH fields in CHGCTRL4 - * register for given chipset. - */ -struct maxim_charger_current { - /* Minimal current, set in CHGCTRL4/MBCICHWRCL, uA */ - unsigned int min; - /* - * Minimal current when high setting is active, - * set in CHGCTRL4/MBCICHWRCH, uA - */ - unsigned int high_start; - /* Value of one step in high setting, uA */ - unsigned int high_step; - /* Maximum current of high setting, uA */ - unsigned int max; -}; - -/* Table of valid charger currents for different Maxim chipsets */ -static const struct maxim_charger_current maxim_charger_currents[] = { - [MAXIM_DEVICE_TYPE_UNKNOWN] = { 0, 0, 0, 0 }, - [MAXIM_DEVICE_TYPE_MAX14577] = { - .min = MAX14577_REGULATOR_CURRENT_LIMIT_MIN, - .high_start = MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_START, - .high_step = MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_STEP, - .max = MAX14577_REGULATOR_CURRENT_LIMIT_MAX, - }, - [MAXIM_DEVICE_TYPE_MAX77836] = { - .min = MAX77836_REGULATOR_CURRENT_LIMIT_MIN, - .high_start = MAX77836_REGULATOR_CURRENT_LIMIT_HIGH_START, - .high_step = MAX77836_REGULATOR_CURRENT_LIMIT_HIGH_STEP, - .max = MAX77836_REGULATOR_CURRENT_LIMIT_MAX, - }, -}; - static int max14577_reg_is_enabled(struct regulator_dev *rdev) { int rid = rdev_get_id(rdev); @@ -103,8 +67,8 @@ static int max14577_reg_get_current_limit(struct regulator_dev *rdev) static int max14577_reg_set_current_limit(struct regulator_dev *rdev, int min_uA, int max_uA) { - int i, current_bits = 0xf; u8 reg_data; + int ret; struct max14577 *max14577 = rdev_get_drvdata(rdev); const struct maxim_charger_current *limits = &maxim_charger_currents[max14577->dev_type]; @@ -112,35 +76,9 @@ static int max14577_reg_set_current_limit(struct regulator_dev *rdev, if (rdev_get_id(rdev) != MAX14577_CHARGER) return -EINVAL; - if (min_uA > limits->max || max_uA < limits->min) - return -EINVAL; - - if (max_uA < limits->high_start) { - /* - * Less than high_start, - * so set the minimal current (turn only Low Bit off) - */ - u8 reg_data = 0x0 << CHGCTRL4_MBCICHWRCL_SHIFT; - return max14577_update_reg(rdev->regmap, - MAX14577_CHG_REG_CHG_CTRL4, - CHGCTRL4_MBCICHWRCL_MASK, reg_data); - } - - /* - * max_uA is in range: , so search for - * valid current starting from maximum current. - */ - for (i = limits->max; i >= limits->high_start; i -= limits->high_step) { - if (i <= max_uA) - break; - current_bits--; - } - BUG_ON(current_bits < 0); /* Cannot happen */ - - /* Turn Low Bit on (use range high_start-max)... */ - reg_data = 0x1 << CHGCTRL4_MBCICHWRCL_SHIFT; - /* and set proper High Bits */ - reg_data |= current_bits << CHGCTRL4_MBCICHWRCH_SHIFT; + ret = maxim_charger_calc_reg_current(limits, min_uA, max_uA, ®_data); + if (ret) + return ret; return max14577_update_reg(rdev->regmap, MAX14577_CHG_REG_CHG_CTRL4, CHGCTRL4_MBCICHWRCL_MASK | CHGCTRL4_MBCICHWRCH_MASK, @@ -442,16 +380,6 @@ static struct platform_driver max14577_regulator_driver = { static int __init max14577_regulator_init(void) { - /* Check for valid values for charger */ - BUILD_BUG_ON(MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_START + - MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_STEP * 0xf != - MAX14577_REGULATOR_CURRENT_LIMIT_MAX); - BUILD_BUG_ON(MAX77836_REGULATOR_CURRENT_LIMIT_HIGH_START + - MAX77836_REGULATOR_CURRENT_LIMIT_HIGH_STEP * 0xf != - MAX77836_REGULATOR_CURRENT_LIMIT_MAX); - /* Valid charger current values must be provided for each chipset */ - BUILD_BUG_ON(ARRAY_SIZE(maxim_charger_currents) != MAXIM_DEVICE_TYPE_NUM); - BUILD_BUG_ON(ARRAY_SIZE(max14577_supported_regulators) != MAX14577_REGULATOR_NUM); BUILD_BUG_ON(ARRAY_SIZE(max77836_supported_regulators) != MAX77836_REGULATOR_NUM); diff --git a/include/linux/mfd/max14577-private.h b/include/linux/mfd/max14577-private.h index d6f3216..7d51483 100644 --- a/include/linux/mfd/max14577-private.h +++ b/include/linux/mfd/max14577-private.h @@ -281,17 +281,17 @@ enum max14577_charger_reg { #define CHGCTRL7_OTPCGHCVS_SHIFT 0 #define CHGCTRL7_OTPCGHCVS_MASK (0x3 << CHGCTRL7_OTPCGHCVS_SHIFT) -/* MAX14577 regulator current limits (as in CHGCTRL4 register), uA */ -#define MAX14577_REGULATOR_CURRENT_LIMIT_MIN 90000 -#define MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_START 200000 -#define MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_STEP 50000 -#define MAX14577_REGULATOR_CURRENT_LIMIT_MAX 950000 - -/* MAX77836 regulator current limits (as in CHGCTRL4 register), uA */ -#define MAX77836_REGULATOR_CURRENT_LIMIT_MIN 45000 -#define MAX77836_REGULATOR_CURRENT_LIMIT_HIGH_START 100000 -#define MAX77836_REGULATOR_CURRENT_LIMIT_HIGH_STEP 25000 -#define MAX77836_REGULATOR_CURRENT_LIMIT_MAX 475000 +/* MAX14577 charger current limits (as in CHGCTRL4 register), uA */ +#define MAX14577_CHARGER_CURRENT_LIMIT_MIN 90000U +#define MAX14577_CHARGER_CURRENT_LIMIT_HIGH_START 200000U +#define MAX14577_CHARGER_CURRENT_LIMIT_HIGH_STEP 50000U +#define MAX14577_CHARGER_CURRENT_LIMIT_MAX 950000U + +/* MAX77836 charger current limits (as in CHGCTRL4 register), uA */ +#define MAX77836_CHARGER_CURRENT_LIMIT_MIN 45000U +#define MAX77836_CHARGER_CURRENT_LIMIT_HIGH_START 100000U +#define MAX77836_CHARGER_CURRENT_LIMIT_HIGH_STEP 25000U +#define MAX77836_CHARGER_CURRENT_LIMIT_MAX 475000U /* MAX14577 regulator SFOUT LDO voltage, fixed, uV */ #define MAX14577_REGULATOR_SAFEOUT_VOLTAGE 4900000 diff --git a/include/linux/mfd/max14577.h b/include/linux/mfd/max14577.h index c83fbed..3c098d5 100644 --- a/include/linux/mfd/max14577.h +++ b/include/linux/mfd/max14577.h @@ -74,4 +74,27 @@ struct max14577_platform_data { struct max14577_regulator_platform_data *regulators; }; +/* + * Valid limits of current for max14577 and max77836 chargers. + * They must correspond to MBCICHWRCL and MBCICHWRCH fields in CHGCTRL4 + * register for given chipset. + */ +struct maxim_charger_current { + /* Minimal current, set in CHGCTRL4/MBCICHWRCL, uA */ + unsigned int min; + /* + * Minimal current when high setting is active, + * set in CHGCTRL4/MBCICHWRCH, uA + */ + unsigned int high_start; + /* Value of one step in high setting, uA */ + unsigned int high_step; + /* Maximum current of high setting, uA */ + unsigned int max; +}; + +extern const struct maxim_charger_current maxim_charger_currents[]; +extern int maxim_charger_calc_reg_current(const struct maxim_charger_current *limits, + unsigned int min_ua, unsigned int max_ua, u8 *dst); + #endif /* __MAX14577_H__ */ -- cgit v0.10.2 From e30110e9c96f48aea01abc3e6dfadb369cbafec3 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 12 Sep 2014 08:53:57 +0200 Subject: charger: max14577: Configure battery-dependent settings from DTS and sysfs Remove hard-coded values for: - Fast Charge current, - End Of Charge current, - Fast Charge timer, - Overvoltage Protection Threshold, - Battery Constant Voltage, and use DTS or sysfs to configure them. This allows using the max14577 charger driver with different batteries. Now the charger driver requires valid configuration data from DTS. In case of wrong configuration data it fails during probe. The fast charge timer is configured through sysfs entry. Signed-off-by: Krzysztof Kozlowski Acked-by: Andrew Morton Signed-off-by: Lee Jones diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 69fa8a9..04e1d2f 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -327,6 +327,7 @@ config CHARGER_MANAGER config CHARGER_MAX14577 tristate "Maxim MAX14577/77836 battery charger driver" depends on MFD_MAX14577 + select SYSFS help Say Y to enable support for the battery charger control sysfs and platform data of MAX14577/77836 MUICs. diff --git a/drivers/power/max14577_charger.c b/drivers/power/max14577_charger.c index 1935305..0a2bc72 100644 --- a/drivers/power/max14577_charger.c +++ b/drivers/power/max14577_charger.c @@ -19,6 +19,7 @@ #include #include #include +#include struct max14577_charger { struct device *dev; @@ -27,6 +28,8 @@ struct max14577_charger { unsigned int charging_state; unsigned int battery_state; + + struct max14577_charger_platform_data *pdata; }; /* @@ -178,15 +181,131 @@ static int max14577_get_present(struct max14577_charger *chg) return 1; } +static int max14577_set_fast_charge_timer(struct max14577_charger *chg, + unsigned long hours) +{ + u8 reg_data; + + switch (hours) { + case 5 ... 7: + reg_data = hours - 3; + break; + case 0: + /* Disable */ + reg_data = 0x7; + break; + default: + dev_err(chg->dev, "Wrong value for Fast-Charge Timer: %lu\n", + hours); + return -EINVAL; + } + reg_data <<= CHGCTRL1_TCHW_SHIFT; + + return max14577_update_reg(chg->max14577->regmap, + MAX14577_REG_CHGCTRL1, CHGCTRL1_TCHW_MASK, reg_data); +} + +static int max14577_init_constant_voltage(struct max14577_charger *chg, + unsigned int uvolt) +{ + u8 reg_data; + + if (uvolt < MAXIM_CHARGER_CONSTANT_VOLTAGE_MIN || + uvolt > MAXIM_CHARGER_CONSTANT_VOLTAGE_MAX) + return -EINVAL; + + if (uvolt == 4200000) + reg_data = 0x0; + else if (uvolt == MAXIM_CHARGER_CONSTANT_VOLTAGE_MAX) + reg_data = 0x1f; + else if (uvolt <= 4280000) { + unsigned int val = uvolt; + + val -= MAXIM_CHARGER_CONSTANT_VOLTAGE_MIN; + val /= MAXIM_CHARGER_CONSTANT_VOLTAGE_STEP; + if (uvolt <= 4180000) + reg_data = 0x1 + val; + else + reg_data = val; /* Fix for gap between 4.18V and 4.22V */ + } else + return -EINVAL; + + reg_data <<= CHGCTRL3_MBCCVWRC_SHIFT; + + return max14577_write_reg(chg->max14577->regmap, + MAX14577_CHG_REG_CHG_CTRL3, reg_data); +} + +static int max14577_init_eoc(struct max14577_charger *chg, + unsigned int uamp) +{ + unsigned int current_bits = 0xf; + u8 reg_data; + + switch (chg->max14577->dev_type) { + case MAXIM_DEVICE_TYPE_MAX77836: + if (uamp < 5000) + return -EINVAL; /* Requested current is too low */ + + if (uamp >= 7500 && uamp < 10000) + current_bits = 0x0; + else if (uamp <= 50000) { + /* <5000, 7499> and <10000, 50000> */ + current_bits = uamp / 5000; + } else { + uamp = min(uamp, 100000U) - 50000U; + current_bits = 0xa + uamp / 10000; + } + break; + + case MAXIM_DEVICE_TYPE_MAX14577: + default: + if (uamp < MAX14577_CHARGER_EOC_CURRENT_LIMIT_MIN) + return -EINVAL; /* Requested current is too low */ + + uamp = min(uamp, MAX14577_CHARGER_EOC_CURRENT_LIMIT_MAX); + uamp -= MAX14577_CHARGER_EOC_CURRENT_LIMIT_MIN; + current_bits = uamp / MAX14577_CHARGER_EOC_CURRENT_LIMIT_STEP; + break; + } + + reg_data = current_bits << CHGCTRL5_EOCS_SHIFT; + + return max14577_update_reg(chg->max14577->regmap, + MAX14577_CHG_REG_CHG_CTRL5, CHGCTRL5_EOCS_MASK, + reg_data); +} + +static int max14577_init_fast_charge(struct max14577_charger *chg, + unsigned int uamp) +{ + u8 reg_data; + int ret; + const struct maxim_charger_current *limits = + &maxim_charger_currents[chg->max14577->dev_type]; + + ret = maxim_charger_calc_reg_current(limits, uamp, uamp, ®_data); + if (ret) { + dev_err(chg->dev, "Wrong value for fast charge: %u\n", uamp); + return ret; + } + + return max14577_update_reg(chg->max14577->regmap, + MAX14577_CHG_REG_CHG_CTRL4, + CHGCTRL4_MBCICHWRCL_MASK | CHGCTRL4_MBCICHWRCH_MASK, + reg_data); +} + /* * Sets charger registers to proper and safe default values. * Some of these values are equal to defaults in MAX14577E * data sheet but there are minor differences. */ -static void max14577_charger_reg_init(struct max14577_charger *chg) +static int max14577_charger_reg_init(struct max14577_charger *chg) { struct regmap *rmap = chg->max14577->regmap; u8 reg_data; + int ret; /* * Charger-Type Manual Detection, default off (set CHGTYPMAN to 0) @@ -198,10 +317,6 @@ static void max14577_charger_reg_init(struct max14577_charger *chg) CDETCTRL1_CHGDETEN_MASK | CDETCTRL1_CHGTYPMAN_MASK, reg_data); - /* Battery Fast-Charge Timer, set to: 6hrs */ - reg_data = 0x3 << CHGCTRL1_TCHW_SHIFT; - max14577_write_reg(rmap, MAX14577_REG_CHGCTRL1, reg_data); - /* * Wall-Adapter Rapid Charge, default on * Battery-Charger, default on @@ -210,32 +325,46 @@ static void max14577_charger_reg_init(struct max14577_charger *chg) reg_data |= 0x1 << CHGCTRL2_MBCHOSTEN_SHIFT; max14577_write_reg(rmap, MAX14577_REG_CHGCTRL2, reg_data); - /* Battery-Charger Constant Voltage (CV) Mode, set to: 4.35V */ - reg_data = 0xf << CHGCTRL3_MBCCVWRC_SHIFT; - max14577_write_reg(rmap, MAX14577_REG_CHGCTRL3, reg_data); - - /* - * Fast Battery-Charge Current Low, - * default 200-950mA (max14577) / 100-475mA (max77836) - * - * Fast Battery-Charge Current High, - * set to 450mA (max14577) / 225mA (max77836) - */ - reg_data = 0x1 << CHGCTRL4_MBCICHWRCL_SHIFT; - reg_data |= 0x5 << CHGCTRL4_MBCICHWRCH_SHIFT; - max14577_write_reg(rmap, MAX14577_REG_CHGCTRL4, reg_data); - - /* End-of-Charge Current, set to 50mA (max14577) / 7.5mA (max77836) */ - reg_data = 0x0 << CHGCTRL5_EOCS_SHIFT; - max14577_write_reg(rmap, MAX14577_REG_CHGCTRL5, reg_data); - /* Auto Charging Stop, default off */ reg_data = 0x0 << CHGCTRL6_AUTOSTOP_SHIFT; max14577_write_reg(rmap, MAX14577_REG_CHGCTRL6, reg_data); - /* Overvoltage-Protection Threshold, set to 6.5V */ - reg_data = 0x2 << CHGCTRL7_OTPCGHCVS_SHIFT; + ret = max14577_init_constant_voltage(chg, chg->pdata->constant_uvolt); + if (ret) + return ret; + + ret = max14577_init_eoc(chg, chg->pdata->eoc_uamp); + if (ret) + return ret; + + ret = max14577_init_fast_charge(chg, chg->pdata->fast_charge_uamp); + if (ret) + return ret; + + ret = max14577_set_fast_charge_timer(chg, + MAXIM_CHARGER_FAST_CHARGE_TIMER_DEFAULT); + if (ret) + return ret; + + /* Initialize Overvoltage-Protection Threshold */ + switch (chg->pdata->ovp_uvolt) { + case 7500000: + reg_data = 0x0; + break; + case 6000000: + case 6500000: + case 7000000: + reg_data = 0x1 + (chg->pdata->ovp_uvolt - 6000000) / 500000; + break; + default: + dev_err(chg->dev, "Wrong value for OVP: %u\n", + chg->pdata->ovp_uvolt); + return -EINVAL; + } + reg_data <<= CHGCTRL7_OTPCGHCVS_SHIFT; max14577_write_reg(rmap, MAX14577_REG_CHGCTRL7, reg_data); + + return 0; } /* Support property from charger */ @@ -295,6 +424,110 @@ static int max14577_charger_get_property(struct power_supply *psy, return ret; } +#ifdef CONFIG_OF +static struct max14577_charger_platform_data *max14577_charger_dt_init( + struct platform_device *pdev) +{ + struct max14577_charger_platform_data *pdata; + struct device_node *np = pdev->dev.of_node; + int ret; + + if (!np) { + dev_err(&pdev->dev, "No charger OF node\n"); + return ERR_PTR(-EINVAL); + } + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return ERR_PTR(-ENOMEM); + + ret = of_property_read_u32(np, "maxim,constant-uvolt", + &pdata->constant_uvolt); + if (ret) { + dev_err(&pdev->dev, "Cannot parse maxim,constant-uvolt field from DT\n"); + return ERR_PTR(ret); + } + + ret = of_property_read_u32(np, "maxim,fast-charge-uamp", + &pdata->fast_charge_uamp); + if (ret) { + dev_err(&pdev->dev, "Cannot parse maxim,fast-charge-uamp field from DT\n"); + return ERR_PTR(ret); + } + + ret = of_property_read_u32(np, "maxim,eoc-uamp", &pdata->eoc_uamp); + if (ret) { + dev_err(&pdev->dev, "Cannot parse maxim,eoc-uamp field from DT\n"); + return ERR_PTR(ret); + } + + ret = of_property_read_u32(np, "maxim,ovp-uvolt", &pdata->ovp_uvolt); + if (ret) { + dev_err(&pdev->dev, "Cannot parse maxim,ovp-uvolt field from DT\n"); + return ERR_PTR(ret); + } + + return pdata; +} +#else /* CONFIG_OF */ +static struct max14577_charger_platform_data *max14577_charger_dt_init( + struct platform_device *pdev) +{ + return NULL; +} +#endif /* CONFIG_OF */ + +static ssize_t show_fast_charge_timer(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct max14577_charger *chg = dev_get_drvdata(dev); + u8 reg_data; + int ret; + unsigned int val; + + ret = max14577_read_reg(chg->max14577->regmap, MAX14577_REG_CHGCTRL1, + ®_data); + if (ret) + return ret; + + reg_data &= CHGCTRL1_TCHW_MASK; + reg_data >>= CHGCTRL1_TCHW_SHIFT; + switch (reg_data) { + case 0x2 ... 0x4: + val = reg_data + 3; + break; + case 0x7: + val = 0; + break; + default: + val = 5; + break; + } + + return scnprintf(buf, PAGE_SIZE, "%u\n", val); +} + +static ssize_t store_fast_charge_timer(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct max14577_charger *chg = dev_get_drvdata(dev); + unsigned long val; + int ret; + + ret = kstrtoul(buf, 10, &val); + if (ret) + return ret; + + ret = max14577_set_fast_charge_timer(chg, val); + if (ret) + return ret; + + return count; +} + +static DEVICE_ATTR(fast_charge_timer, S_IRUGO | S_IWUSR, + show_fast_charge_timer, store_fast_charge_timer); + static int max14577_charger_probe(struct platform_device *pdev) { struct max14577_charger *chg; @@ -309,7 +542,13 @@ static int max14577_charger_probe(struct platform_device *pdev) chg->dev = &pdev->dev; chg->max14577 = max14577; - max14577_charger_reg_init(chg); + chg->pdata = max14577_charger_dt_init(pdev); + if (IS_ERR_OR_NULL(chg->pdata)) + return PTR_ERR(chg->pdata); + + ret = max14577_charger_reg_init(chg); + if (ret) + return ret; chg->charger.name = "max14577-charger", chg->charger.type = POWER_SUPPLY_TYPE_BATTERY, @@ -317,19 +556,35 @@ static int max14577_charger_probe(struct platform_device *pdev) chg->charger.num_properties = ARRAY_SIZE(max14577_charger_props), chg->charger.get_property = max14577_charger_get_property, + ret = device_create_file(&pdev->dev, &dev_attr_fast_charge_timer); + if (ret) { + dev_err(&pdev->dev, "failed: create sysfs entry\n"); + return ret; + } + ret = power_supply_register(&pdev->dev, &chg->charger); if (ret) { dev_err(&pdev->dev, "failed: power supply register\n"); - return ret; + goto err; } + /* Check for valid values for charger */ + BUILD_BUG_ON(MAX14577_CHARGER_EOC_CURRENT_LIMIT_MIN + + MAX14577_CHARGER_EOC_CURRENT_LIMIT_STEP * 0xf != + MAX14577_CHARGER_EOC_CURRENT_LIMIT_MAX); return 0; + +err: + device_remove_file(&pdev->dev, &dev_attr_fast_charge_timer); + + return ret; } static int max14577_charger_remove(struct platform_device *pdev) { struct max14577_charger *chg = platform_get_drvdata(pdev); + device_remove_file(&pdev->dev, &dev_attr_fast_charge_timer); power_supply_unregister(&chg->charger); return 0; diff --git a/include/linux/mfd/max14577-private.h b/include/linux/mfd/max14577-private.h index 7d51483..f01c1fa 100644 --- a/include/linux/mfd/max14577-private.h +++ b/include/linux/mfd/max14577-private.h @@ -293,6 +293,25 @@ enum max14577_charger_reg { #define MAX77836_CHARGER_CURRENT_LIMIT_HIGH_STEP 25000U #define MAX77836_CHARGER_CURRENT_LIMIT_MAX 475000U +/* + * MAX14577 charger End-Of-Charge current limits + * (as in CHGCTRL5 register), uA + */ +#define MAX14577_CHARGER_EOC_CURRENT_LIMIT_MIN 50000U +#define MAX14577_CHARGER_EOC_CURRENT_LIMIT_STEP 10000U +#define MAX14577_CHARGER_EOC_CURRENT_LIMIT_MAX 200000U + +/* + * MAX14577/MAX77836 Battery Constant Voltage + * (as in CHGCTRL3 register), uV + */ +#define MAXIM_CHARGER_CONSTANT_VOLTAGE_MIN 4000000U +#define MAXIM_CHARGER_CONSTANT_VOLTAGE_STEP 20000U +#define MAXIM_CHARGER_CONSTANT_VOLTAGE_MAX 4350000U + +/* Default value for fast charge timer, in hours */ +#define MAXIM_CHARGER_FAST_CHARGE_TIMER_DEFAULT 5 + /* MAX14577 regulator SFOUT LDO voltage, fixed, uV */ #define MAX14577_REGULATOR_SAFEOUT_VOLTAGE 4900000 diff --git a/include/linux/mfd/max14577.h b/include/linux/mfd/max14577.h index 3c098d5..ccfaf95 100644 --- a/include/linux/mfd/max14577.h +++ b/include/linux/mfd/max14577.h @@ -54,6 +54,13 @@ struct max14577_regulator_platform_data { struct device_node *of_node; }; +struct max14577_charger_platform_data { + u32 constant_uvolt; + u32 fast_charge_uamp; + u32 eoc_uamp; + u32 ovp_uvolt; +}; + /* * MAX14577 MFD platform data */ -- cgit v0.10.2 From 2c33e9296202cd11bf2e2f801b69ffba0953748a Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 12 Sep 2014 08:53:58 +0200 Subject: power: max17040: Add ID for MAX77836 Fuel Gauge block MAX77836 has the same Fuel Gauge as MAX17040/17048. The max17040 driver can be safely re-used. The patch adds MAX77836 device to the array of i2c_device_id. Additionally it removes the id associated with MAX17040 device as the value is not used. Signed-off-by: Krzysztof Kozlowski Acked-by: Andrew Morton Signed-off-by: Lee Jones diff --git a/drivers/power/max17040_battery.c b/drivers/power/max17040_battery.c index 0fbac86..14d4470 100644 --- a/drivers/power/max17040_battery.c +++ b/drivers/power/max17040_battery.c @@ -277,7 +277,8 @@ static SIMPLE_DEV_PM_OPS(max17040_pm_ops, max17040_suspend, max17040_resume); #endif /* CONFIG_PM_SLEEP */ static const struct i2c_device_id max17040_id[] = { - { "max17040", 0 }, + { "max17040" }, + { "max77836-battery" }, { } }; MODULE_DEVICE_TABLE(i2c, max17040_id); -- cgit v0.10.2 From 8d70d68d7a1b3082ca5a3808be18103a83ae348d Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 12 Sep 2014 08:53:59 +0200 Subject: devicetree: mfd: max14577: Add device tree bindings document Add document describing device tree bindings for MAX14577 MFD drivers: MFD core, extcon, regulator and charger. Both MAX14577 and MAX77836 chipsets are documented. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Tomasz Figa Acked-by: Andrew Morton Signed-off-by: Lee Jones diff --git a/Documentation/devicetree/bindings/mfd/max14577.txt b/Documentation/devicetree/bindings/mfd/max14577.txt new file mode 100644 index 0000000..236264c --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/max14577.txt @@ -0,0 +1,146 @@ +Maxim MAX14577/77836 Multi-Function Device + +MAX14577 is a Multi-Function Device with Micro-USB Interface Circuit, Li+ +Battery Charger and SFOUT LDO output for powering USB devices. It is +interfaced to host controller using I2C. + +MAX77836 additionally contains PMIC (with two LDO regulators) and Fuel Gauge. + + +Required properties: +- compatible : Must be "maxim,max14577" or "maxim,max77836". +- reg : I2C slave address for the max14577 chip (0x25 for max14577/max77836) +- interrupts : IRQ line for the chip. +- interrupt-parent : The parent interrupt controller. + + +Required nodes: + - charger : + Node for configuring the charger driver. + Required properties: + - compatible : "maxim,max14577-charger" + or "maxim,max77836-charger" + - maxim,fast-charge-uamp : Current in uA for Fast Charge; + Valid values: + - for max14577: 90000 - 950000; + - for max77836: 45000 - 475000; + - maxim,eoc-uamp : Current in uA for End-Of-Charge mode; + Valid values: + - for max14577: 50000 - 200000; + - for max77836: 5000 - 100000; + - maxim,ovp-uvolt : OverVoltage Protection Threshold in uV; + In an overvoltage condition, INT asserts and charging + stops. Valid values: + - 6000000, 6500000, 7000000, 7500000; + - maxim,constant-uvolt : Battery Constant Voltage in uV; + Valid values: + - 4000000 - 4280000 (step by 20000); + - 4350000; + + +Optional nodes: +- max14577-muic/max77836-muic : + Node used only by extcon consumers. + Required properties: + - compatible : "maxim,max14577-muic" or "maxim,max77836-muic" + +- regulators : + Required properties: + - compatible : "maxim,max14577-regulator" + or "maxim,max77836-regulator" + + May contain a sub-node per regulator from the list below. Each + sub-node should contain the constraints and initialization information + for that regulator. See regulator.txt for a description of standard + properties for these sub-nodes. + + List of valid regulator names: + - for max14577: CHARGER, SAFEOUT. + - for max77836: CHARGER, SAFEOUT, LDO1, LDO2. + + The SAFEOUT is a fixed voltage regulator so there is no need to specify + voltages for it. + + +Example: + +#include + +max14577@25 { + compatible = "maxim,max14577"; + reg = <0x25>; + interrupt-parent = <&gpx1>; + interrupts = <5 IRQ_TYPE_NONE>; + + muic: max14577-muic { + compatible = "maxim,max14577-muic"; + }; + + regulators { + compatible = "maxim,max14577-regulator"; + + SAFEOUT { + regulator-name = "SAFEOUT"; + }; + CHARGER { + regulator-name = "CHARGER"; + regulator-min-microamp = <90000>; + regulator-max-microamp = <950000>; + regulator-boot-on; + }; + }; + + charger { + compatible = "maxim,max14577-charger"; + + maxim,constant-uvolt = <4350000>; + maxim,fast-charge-uamp = <450000>; + maxim,eoc-uamp = <50000>; + maxim,ovp-uvolt = <6500000>; + }; +}; + + +max77836@25 { + compatible = "maxim,max77836"; + reg = <0x25>; + interrupt-parent = <&gpx1>; + interrupts = <5 IRQ_TYPE_NONE>; + + muic: max77836-muic { + compatible = "maxim,max77836-muic"; + }; + + regulators { + compatible = "maxim,max77836-regulator"; + + SAFEOUT { + regulator-name = "SAFEOUT"; + }; + CHARGER { + regulator-name = "CHARGER"; + regulator-min-microamp = <90000>; + regulator-max-microamp = <950000>; + regulator-boot-on; + }; + LDO1 { + regulator-name = "LDO1"; + regulator-min-microvolt = <2700000>; + regulator-max-microvolt = <2700000>; + }; + LDO2 { + regulator-name = "LDO2"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <3950000>; + }; + }; + + charger { + compatible = "maxim,max77836-charger"; + + maxim,constant-uvolt = <4350000>; + maxim,fast-charge-uamp = <225000>; + maxim,eoc-uamp = <7500>; + maxim,ovp-uvolt = <6500000>; + }; +}; -- cgit v0.10.2 From d6cc1f5824cbca392d099f3bb0c441efd9e54de9 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 12 Sep 2014 08:54:00 +0200 Subject: Documentation: charger: max14577: Document exported sysfs entry Document the 'fast charge timer' setting exported by max14577 driver through sysfs entry. Signed-off-by: Krzysztof Kozlowski Acked-by: Andrew Morton Signed-off-by: Lee Jones diff --git a/Documentation/ABI/testing/sysfs-class-power b/Documentation/ABI/testing/sysfs-class-power index 78c7bac..ad4fb90 100644 --- a/Documentation/ABI/testing/sysfs-class-power +++ b/Documentation/ABI/testing/sysfs-class-power @@ -18,3 +18,17 @@ Description: This file is writeable and can be used to set the assumed battery 'full level'. As batteries age, this value has to be amended over time. + +What: /sys/class/power_supply/max14577-charger/device/fast_charge_timer +Date: July 2014 +KernelVersion: 3.18.0 +Contact: Krzysztof Kozlowski +Description: + This entry shows and sets the maximum time the max14577 + charger operates in fast-charge mode. When the timer expires + the device will terminate fast-charge mode (charging current + will drop to 0 A) and will trigger interrupt. + + Valid values: + - 5, 6 or 7 (hours), + - 0: disabled. -- cgit v0.10.2 From 628ef02c56e515430dd8d8439126dd0ecb8ce8bb Mon Sep 17 00:00:00 2001 From: Puthikorn Voravootivat Date: Tue, 9 Sep 2014 12:20:35 -0700 Subject: bq27x00_battery: Add support to bq27742 Add support to bq27742 in bq27x00 driver. bq27742 register addresses are mostly mostly the same as bq27500 addresses with minor differences. Signed-off-by: Puthikorn Voravootivat Reviewed-by: Gwendal Grignou Reviewed-by: Rhyland Klein Reviewed-by: Benson Leung Signed-off-by: Sebastian Reichel diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c index e10763e..5b9f0ea 100644 --- a/drivers/power/bq27x00_battery.c +++ b/drivers/power/bq27x00_battery.c @@ -23,6 +23,7 @@ * http://focus.ti.com/docs/prod/folders/print/bq27000.html * http://focus.ti.com/docs/prod/folders/print/bq27500.html * http://www.ti.com/product/bq27425-g1 + * http://www.ti.com/product/BQ27742-G1 */ #include @@ -71,6 +72,8 @@ #define BQ27500_FLAG_FC BIT(9) #define BQ27500_FLAG_OTC BIT(15) +#define BQ27742_POWER_AVG 0x76 + /* bq27425 register addresses are same as bq27x00 addresses minus 4 */ #define BQ27425_REG_OFFSET 0x04 #define BQ27425_REG_SOC 0x18 /* Register address plus offset */ @@ -83,7 +86,7 @@ struct bq27x00_access_methods { int (*read)(struct bq27x00_device_info *di, u8 reg, bool single); }; -enum bq27x00_chip { BQ27000, BQ27500, BQ27425}; +enum bq27x00_chip { BQ27000, BQ27500, BQ27425, BQ27742}; struct bq27x00_reg_cache { int temperature; @@ -152,6 +155,24 @@ static enum power_supply_property bq27425_battery_props[] = { POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, }; +static enum power_supply_property bq27742_battery_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_CAPACITY_LEVEL, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_CHARGE_FULL, + POWER_SUPPLY_PROP_CHARGE_NOW, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + POWER_SUPPLY_PROP_CYCLE_COUNT, + POWER_SUPPLY_PROP_POWER_AVG, + POWER_SUPPLY_PROP_HEALTH, +}; + static unsigned int poll_interval = 360; module_param(poll_interval, uint, 0644); MODULE_PARM_DESC(poll_interval, "battery poll interval in seconds - " \ @@ -176,7 +197,7 @@ static inline int bq27x00_read(struct bq27x00_device_info *di, u8 reg, */ static bool bq27xxx_is_chip_version_higher(struct bq27x00_device_info *di) { - if (di->chip == BQ27425 || di->chip == BQ27500) + if (di->chip == BQ27425 || di->chip == BQ27500 || di->chip == BQ27742) return true; return false; } @@ -189,7 +210,7 @@ static int bq27x00_battery_read_rsoc(struct bq27x00_device_info *di) { int rsoc; - if (di->chip == BQ27500) + if (di->chip == BQ27500 || di->chip == BQ27742) rsoc = bq27x00_read(di, BQ27500_REG_SOC, false); else if (di->chip == BQ27425) rsoc = bq27x00_read(di, BQ27425_REG_SOC, false); @@ -414,6 +435,7 @@ static void bq27x00_update(struct bq27x00_device_info *di) struct bq27x00_reg_cache cache = {0, }; bool is_bq27500 = di->chip == BQ27500; bool is_bq27425 = di->chip == BQ27425; + bool is_bq27742 = di->chip == BQ27742; cache.flags = bq27x00_read(di, BQ27x00_REG_FLAGS, !is_bq27500); if ((cache.flags & 0xff) == 0xff) @@ -432,7 +454,11 @@ static void bq27x00_update(struct bq27x00_device_info *di) cache.health = -ENODATA; } else { cache.capacity = bq27x00_battery_read_rsoc(di); - if (!is_bq27425) { + if (is_bq27742) + cache.time_to_empty = + bq27x00_battery_read_time(di, + BQ27x00_REG_TTE); + else if (!is_bq27425) { cache.energy = bq27x00_battery_read_energy(di); cache.time_to_empty = bq27x00_battery_read_time(di, @@ -444,14 +470,22 @@ static void bq27x00_update(struct bq27x00_device_info *di) bq27x00_battery_read_time(di, BQ27x00_REG_TTF); } - cache.charge_full = bq27x00_battery_read_lmd(di); + if (!is_bq27742) + cache.charge_full = + bq27x00_battery_read_lmd(di); cache.health = bq27x00_battery_read_health(di); } cache.temperature = bq27x00_battery_read_temperature(di); if (!is_bq27425) cache.cycle_count = bq27x00_battery_read_cyct(di); - cache.power_avg = - bq27x00_battery_read_pwr_avg(di, BQ27x00_POWER_AVG); + if (is_bq27742) + cache.power_avg = + bq27x00_battery_read_pwr_avg(di, + BQ27742_POWER_AVG); + else + cache.power_avg = + bq27x00_battery_read_pwr_avg(di, + BQ27x00_POWER_AVG); /* We only have to read charge design full once */ if (di->charge_design_full <= 0) @@ -702,6 +736,9 @@ static int bq27x00_powersupply_init(struct bq27x00_device_info *di) if (di->chip == BQ27425) { di->bat.properties = bq27425_battery_props; di->bat.num_properties = ARRAY_SIZE(bq27425_battery_props); + } else if (di->chip == BQ27742) { + di->bat.properties = bq27742_battery_props; + di->bat.num_properties = ARRAY_SIZE(bq27742_battery_props); } else { di->bat.properties = bq27x00_battery_props; di->bat.num_properties = ARRAY_SIZE(bq27x00_battery_props); @@ -858,6 +895,7 @@ static const struct i2c_device_id bq27x00_id[] = { { "bq27200", BQ27000 }, /* bq27200 is same as bq27000, but with i2c */ { "bq27500", BQ27500 }, { "bq27425", BQ27425 }, + { "bq27742", BQ27742 }, {}, }; MODULE_DEVICE_TABLE(i2c, bq27x00_id); -- cgit v0.10.2 From 920ac5be91bc447c5ef82f457207a169aa79c5f6 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 16 Sep 2014 18:10:40 +0200 Subject: power: max8925: Fix NULL ptr dereference on memory allocation failure Check the return value of devm_kzalloc() to fix possible NULL pointer dereference and properly exit the probe() on memory allocation failure. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Sebastian Reichel diff --git a/drivers/power/max8925_power.c b/drivers/power/max8925_power.c index b4513f2..0cf2378 100644 --- a/drivers/power/max8925_power.c +++ b/drivers/power/max8925_power.c @@ -452,13 +452,14 @@ max8925_power_dt_init(struct platform_device *pdev) pdata = devm_kzalloc(&pdev->dev, sizeof(struct max8925_power_pdata), GFP_KERNEL); + if (!pdata) + goto ret; of_property_read_u32(np, "topoff-threshold", &topoff_threshold); of_property_read_u32(np, "batt-detect", &batt_detect); of_property_read_u32(np, "fast-charge", &fast_charge); of_property_read_u32(np, "no-insert-detect", &no_insert_detect); of_property_read_u32(np, "no-temp-support", &no_temp_support); - of_node_put(np); pdata->batt_detect = batt_detect; pdata->fast_charge = fast_charge; @@ -466,6 +467,8 @@ max8925_power_dt_init(struct platform_device *pdev) pdata->no_insert_detect = no_insert_detect; pdata->no_temp_support = no_temp_support; +ret: + of_node_put(np); return pdata; } #else -- cgit v0.10.2 From ddd26dff757d08d4eb309a28bf2a02372387e71f Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 16 Sep 2014 18:10:41 +0200 Subject: power: max8925: Use of_get_child_by_name Use of_get_child_by_name to obtain reference to charger node instead of of_find_node_by_name which can walk outside of the parent node. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Sebastian Reichel diff --git a/drivers/power/max8925_power.c b/drivers/power/max8925_power.c index 0cf2378..a6d45ee 100644 --- a/drivers/power/max8925_power.c +++ b/drivers/power/max8925_power.c @@ -443,7 +443,7 @@ max8925_power_dt_init(struct platform_device *pdev) if (!nproot) return pdev->dev.platform_data; - np = of_find_node_by_name(nproot, "charger"); + np = of_get_child_by_name(nproot, "charger"); if (!np) { dev_err(&pdev->dev, "failed to find charger node\n"); return NULL; -- cgit v0.10.2 From 4d96fb1ec81118c6406fe6d3670f172b2faaedf3 Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Tue, 23 Sep 2014 22:42:16 +0200 Subject: power: gpio-charger: do not use gpio value directly Some gpio implementations return interesting values for gpio_get_value when the value is not 0 - as seen on a imx6sl board. Therefore do not use the value returned from gpio_get_value directly but simply check for 0 or not 0. Signed-off-by: Heiko Stuebner Reviewed-by: Doug Anderson Tested-by: Doug Anderson Signed-off-by: Sebastian Reichel diff --git a/drivers/power/gpio-charger.c b/drivers/power/gpio-charger.c index a0024b2..7536933 100644 --- a/drivers/power/gpio-charger.c +++ b/drivers/power/gpio-charger.c @@ -55,7 +55,7 @@ static int gpio_charger_get_property(struct power_supply *psy, switch (psp) { case POWER_SUPPLY_PROP_ONLINE: - val->intval = gpio_get_value_cansleep(pdata->gpio); + val->intval = !!gpio_get_value_cansleep(pdata->gpio); val->intval ^= pdata->gpio_active_low; break; default: -- cgit v0.10.2 From 6a91e854442cf950d9cb806891667d16c4a41d88 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 25 Sep 2014 09:05:31 +0200 Subject: power: max14577: Fix circular config SYSFS dependency Make the max14577 charger driver depending on SYSFS instead selecting it. This fixes warning on x86_64 with allmodconfig: scripts/kconfig/conf --allmodconfig Kconfig fs/sysfs/Kconfig:1:error: recursive dependency detected! fs/sysfs/Kconfig:1: symbol SYSFS is selected by CHARGER_MAX14577 drivers/power/Kconfig:327: symbol CHARGER_MAX14577 depends on POWER_SUPPLY drivers/power/Kconfig:1: symbol POWER_SUPPLY is selected by HID_SONY drivers/hid/Kconfig:638: symbol HID_SONY depends on NEW_LEDS drivers/leds/Kconfig:8: symbol NEW_LEDS is selected by SENSORS_APPLESMC drivers/hwmon/Kconfig:299: symbol SENSORS_APPLESMC depends on HWMON drivers/hwmon/Kconfig:5: symbol HWMON is selected by EEEPC_LAPTOP drivers/platform/x86/Kconfig:496: symbol EEEPC_LAPTOP depends on HOTPLUG_PCI drivers/pci/hotplug/Kconfig:5: symbol HOTPLUG_PCI depends on SYSFS Signed-off-by: Krzysztof Kozlowski Reported-by: Stephen Rothwell Signed-off-by: Sebastian Reichel diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 04e1d2f..0108c2a 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -327,7 +327,7 @@ config CHARGER_MANAGER config CHARGER_MAX14577 tristate "Maxim MAX14577/77836 battery charger driver" depends on MFD_MAX14577 - select SYSFS + depends on SYSFS help Say Y to enable support for the battery charger control sysfs and platform data of MAX14577/77836 MUICs. -- cgit v0.10.2 From 661a88860274e059fdb744dfaa98c045db7b5d1d Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 26 Sep 2014 13:27:03 +0200 Subject: power: charger-manager: Fix NULL pointer exception with missing cm-fuel-gauge NULL pointer exception happens during charger-manager probe if 'cm-fuel-gauge' property is not present. [ 2.448536] Unable to handle kernel NULL pointer dereference at virtual address 00000000 [ 2.456572] pgd = c0004000 [ 2.459217] [00000000] *pgd=00000000 [ 2.462759] Internal error: Oops: 5 [#1] PREEMPT SMP ARM [ 2.468047] Modules linked in: [ 2.471089] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 3.17.0-rc6-00251-ge44cf96cd525-dirty #969 [ 2.479765] task: ea890000 ti: ea87a000 task.ti: ea87a000 [ 2.485161] PC is at strcmp+0x4/0x30 [ 2.488719] LR is at power_supply_match_device_by_name+0x10/0x1c [ 2.494695] pc : [] lr : [] psr: a0000113 [ 2.494695] sp : ea87bde0 ip : 00000000 fp : eaa97010 [ 2.506150] r10: 00000004 r9 : ea97269c r8 : ea3bbfd0 [ 2.511360] r7 : eaa97000 r6 : c030fe28 r5 : 00000000 r4 : ea3b0000 [ 2.517869] r3 : 0000006d r2 : 00000000 r1 : 00000000 r0 : c057c195 [ 2.524381] Flags: NzCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment kernel [ 2.531671] Control: 10c5387d Table: 4000404a DAC: 00000015 [ 2.537399] Process swapper/0 (pid: 1, stack limit = 0xea87a240) [ 2.543388] Stack: (0xea87bde0 to 0xea87c000) [ 2.547733] bde0: ea3b0210 c026b1c8 eaa97010 eaa97000 eaa97010 eabb60a8 ea3b0210 00000000 [ 2.555891] be00: 00000008 ea2db210 ea1a3410 c030fee0 ea3bbf90 c03138fc c068969c c013526c [ 2.564050] be20: eaa040c0 00000000 c068969c 00000000 eaa040c0 ea2da300 00000002 00000000 [ 2.572208] be40: 00000001 ea2da3c0 00000000 00000001 00000000 eaa97010 c068969c 00000000 [ 2.580367] be60: 00000000 c068969c 00000000 00000002 00000000 c026b71c c026b6f0 eaa97010 [ 2.588527] be80: c0e82530 c026a330 00000000 eaa97010 c068969c eaa97044 00000000 c061df50 [ 2.596686] bea0: ea87a000 c026a4dc 00000000 c068969c c026a448 c0268b5c ea8054a8 eaa8fd50 [ 2.604845] bec0: c068969c ea2db180 c06801f8 c0269b18 c0590f68 c068969c c0656c98 c068969c [ 2.613004] bee0: c0656c98 ea3bbe40 c06988c0 c026aaf0 00000000 c0656c98 c0656c98 c00088a4 [ 2.621163] bf00: 00000000 c0055f48 00000000 00000004 00000000 ea890000 c05dbc54 c062c178 [ 2.629323] bf20: c0603518 c005f674 00000001 ea87a000 eb7ff83b c0476440 00000091 c003d41c [ 2.637482] bf40: c05db344 00000007 eb7ff858 00000007 c065a76c c0647d24 00000007 c062c170 [ 2.645642] bf60: c06988c0 00000091 c062c178 c0603518 00000000 c0603cc4 00000007 00000007 [ 2.653801] bf80: c0603518 c0c0c0c0 00000000 c0453948 00000000 00000000 00000000 00000000 [ 2.661959] bfa0: 00000000 c0453950 00000000 c000e728 00000000 00000000 00000000 00000000 [ 2.670118] bfc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 [ 2.678277] bfe0: 00000000 00000000 00000000 00000000 00000013 00000000 c0c0c0c0 c0c0c0c0 [ 2.686454] [] (strcmp) from [] (power_supply_match_device_by_name+0x10/0x1c) [ 2.695303] [] (power_supply_match_device_by_name) from [] (class_find_device+0x54/0xac) [ 2.705106] [] (class_find_device) from [] (power_supply_get_by_name+0x1c/0x30) [ 2.714137] [] (power_supply_get_by_name) from [] (charger_manager_probe+0x3d8/0xe58) [ 2.723683] [] (charger_manager_probe) from [] (platform_drv_probe+0x2c/0x5c) [ 2.732532] [] (platform_drv_probe) from [] (driver_probe_device+0x10c/0x224) [ 2.741384] [] (driver_probe_device) from [] (__driver_attach+0x94/0x98) [ 2.749813] [] (__driver_attach) from [] (bus_for_each_dev+0x54/0x88) [ 2.757969] [] (bus_for_each_dev) from [] (bus_add_driver+0xd4/0x1d0) [ 2.766123] [] (bus_add_driver) from [] (driver_register+0x78/0xf4) [ 2.774110] [] (driver_register) from [] (do_one_initcall+0x80/0x1bc) [ 2.782276] [] (do_one_initcall) from [] (kernel_init_freeable+0x100/0x1cc) [ 2.790952] [] (kernel_init_freeable) from [] (kernel_init+0x8/0xec) [ 2.799029] [] (kernel_init) from [] (ret_from_fork+0x14/0x2c) [ 2.806572] Code: e12fff1e e1a03000 eafffff7 e4d03001 (e4d12001) [ 2.812832] ---[ end trace 7f12556111b9e7ef ]--- Signed-off-by: Krzysztof Kozlowski Cc: Fixes: 856ee6115e2d ("charger-manager: Support deivce tree in charger manager driver") Signed-off-by: Sebastian Reichel diff --git a/drivers/power/charger-manager.c b/drivers/power/charger-manager.c index c1ed3c9..7098a1c 100644 --- a/drivers/power/charger-manager.c +++ b/drivers/power/charger-manager.c @@ -1720,6 +1720,11 @@ static int charger_manager_probe(struct platform_device *pdev) return -EINVAL; } + if (!desc->psy_fuel_gauge) { + dev_err(&pdev->dev, "No fuel gauge power supply defined\n"); + return -EINVAL; + } + /* Counting index only */ while (desc->psy_charger_stat[i]) i++; -- cgit v0.10.2 From f0745f3696e88b4055c6229e1306a78fbe503066 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 22 Jul 2014 15:52:28 +0100 Subject: power: reset: Add restart functionality for STiH41x platforms This driver adds the restart functionality for STiH415 and STiH416 platforms from STMicroelectronics. This driver registers an arm_pm_restart function to reset the platform. Signed-off-by: Christophe Kerello Signed-off-by: Lee Jones Signed-off-by: Sebastian Reichel diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index 7615639..88603b6 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -82,6 +82,12 @@ config POWER_RESET_SUN6I help Reboot support for the Allwinner A31 SoCs. +config POWER_RESET_ST + bool "ST restart power-off driver" + depends on POWER_RESET && ARCH_STI + help + Power off and reset support for STMicroelectronics boards. + config POWER_RESET_VEXPRESS bool "ARM Versatile Express power-off and reset driver" depends on ARM || ARM64 diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile index 5d94352..06f106b 100644 --- a/drivers/power/reset/Makefile +++ b/drivers/power/reset/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_POWER_RESET_LTC2952) += ltc2952-poweroff.o obj-$(CONFIG_POWER_RESET_QNAP) += qnap-poweroff.o obj-$(CONFIG_POWER_RESET_RESTART) += restart-poweroff.o obj-$(CONFIG_POWER_RESET_SUN6I) += sun6i-reboot.o +obj-$(CONFIG_POWER_RESET_ST) += st-poweroff.o obj-$(CONFIG_POWER_RESET_VEXPRESS) += vexpress-poweroff.o obj-$(CONFIG_POWER_RESET_XGENE) += xgene-reboot.o obj-$(CONFIG_POWER_RESET_KEYSTONE) += keystone-reset.o diff --git a/drivers/power/reset/st-poweroff.c b/drivers/power/reset/st-poweroff.c new file mode 100644 index 0000000..a0acf25 --- /dev/null +++ b/drivers/power/reset/st-poweroff.c @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2014 STMicroelectronics + * + * Power off Restart driver, used in STMicroelectronics devices. + * + * Author: Christophe Kerello + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#include + +struct reset_syscfg { + struct regmap *regmap; + /* syscfg used for reset */ + unsigned int offset_rst; + unsigned int mask_rst; + /* syscfg used for unmask the reset */ + unsigned int offset_rst_msk; + unsigned int mask_rst_msk; +}; + +/* STiH415 */ +#define STIH415_SYSCFG_11 0x2c +#define STIH415_SYSCFG_15 0x3c + +static struct reset_syscfg stih415_reset = { + .offset_rst = STIH415_SYSCFG_11, + .mask_rst = BIT(0), + .offset_rst_msk = STIH415_SYSCFG_15, + .mask_rst_msk = BIT(0) +}; + +/* STiH416 */ +#define STIH416_SYSCFG_500 0x7d0 +#define STIH416_SYSCFG_504 0x7e0 + +static struct reset_syscfg stih416_reset = { + .offset_rst = STIH416_SYSCFG_500, + .mask_rst = BIT(0), + .offset_rst_msk = STIH416_SYSCFG_504, + .mask_rst_msk = BIT(0) +}; + +/* STiH407 */ +#define STIH407_SYSCFG_4000 0x0 +#define STIH407_SYSCFG_4008 0x20 + +static struct reset_syscfg stih407_reset = { + .offset_rst = STIH407_SYSCFG_4000, + .mask_rst = BIT(0), + .offset_rst_msk = STIH407_SYSCFG_4008, + .mask_rst_msk = BIT(0) +}; + +/* STiD127 */ +#define STID127_SYSCFG_700 0x0 +#define STID127_SYSCFG_773 0x124 + +static struct reset_syscfg stid127_reset = { + .offset_rst = STID127_SYSCFG_773, + .mask_rst = BIT(0), + .offset_rst_msk = STID127_SYSCFG_700, + .mask_rst_msk = BIT(8) +}; + +static struct reset_syscfg *st_restart_syscfg; + +static void st_restart(enum reboot_mode reboot_mode, const char *cmd) +{ + /* reset syscfg updated */ + regmap_update_bits(st_restart_syscfg->regmap, + st_restart_syscfg->offset_rst, + st_restart_syscfg->mask_rst, + 0); + + /* unmask the reset */ + regmap_update_bits(st_restart_syscfg->regmap, + st_restart_syscfg->offset_rst_msk, + st_restart_syscfg->mask_rst_msk, + 0); +} + +static struct of_device_id st_reset_of_match[] = { + { + .compatible = "st,stih415-restart", + .data = (void *)&stih415_reset, + }, { + .compatible = "st,stih416-restart", + .data = (void *)&stih416_reset, + }, { + .compatible = "st,stih407-restart", + .data = (void *)&stih407_reset, + }, { + .compatible = "st,stid127-restart", + .data = (void *)&stid127_reset, + }, + {} +}; + +static int st_reset_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + const struct of_device_id *match; + struct device *dev = &pdev->dev; + + match = of_match_device(st_reset_of_match, dev); + if (!match) + return -ENODEV; + + st_restart_syscfg = (struct reset_syscfg *)match->data; + + st_restart_syscfg->regmap = + syscon_regmap_lookup_by_phandle(np, "st,syscfg"); + if (IS_ERR(st_restart_syscfg->regmap)) { + dev_err(dev, "No syscfg phandle specified\n"); + return PTR_ERR(st_restart_syscfg->regmap); + } + + arm_pm_restart = st_restart; + + return 0; +} + +static struct platform_driver st_reset_driver = { + .probe = st_reset_probe, + .driver = { + .name = "st_reset", + .of_match_table = st_reset_of_match, + }, +}; + +static int __init st_reset_init(void) +{ + return platform_driver_register(&st_reset_driver); +} + +device_initcall(st_reset_init); + +MODULE_AUTHOR("Christophe Kerello "); +MODULE_DESCRIPTION("STMicroelectronics Power off Restart driver"); +MODULE_LICENSE("GPL v2"); -- cgit v0.10.2 From b08fec2262c8dbb349fe9456586cf0724407e5ee Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 22 Jul 2014 15:52:29 +0100 Subject: power: reset: st: Provide DT bindings for ST's Power Reset driver Signed-off-by: Christophe Kerello Signed-off-by: Lee Jones Signed-off-by: Sebastian Reichel diff --git a/Documentation/devicetree/bindings/power/reset/st-reset.txt b/Documentation/devicetree/bindings/power/reset/st-reset.txt new file mode 100644 index 0000000..809af54 --- /dev/null +++ b/Documentation/devicetree/bindings/power/reset/st-reset.txt @@ -0,0 +1,11 @@ +*Device-Tree bindings for ST SW reset functionality + +Required properties: +- compatible: should be "st,-restart". +- st,syscfg: should be a phandle of the syscfg node. + +Example node: + restart { + compatible = "st,stih416-restart"; + st,syscfg = <&syscfg_sbc>; + }; -- cgit v0.10.2 From 371bb20d6927f204ef5a7887ecddb06b2501c0d9 Mon Sep 17 00:00:00 2001 From: David Riley Date: Wed, 27 Aug 2014 12:23:51 -0700 Subject: power: Add simple gpio-restart driver This driver registers a restart handler to set a GPIO line high/low to reset a board based on devicetree bindings. Signed-off-by: David Riley Reviewed-by: Guenter Roeck Signed-off-by: Sebastian Reichel diff --git a/Documentation/devicetree/bindings/gpio/gpio-restart.txt b/Documentation/devicetree/bindings/gpio/gpio-restart.txt new file mode 100644 index 0000000..af3701b --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/gpio-restart.txt @@ -0,0 +1,54 @@ +Drive a GPIO line that can be used to restart the system from a restart +handler. + +This binding supports level and edge triggered reset. At driver load +time, the driver will request the given gpio line and install a restart +handler. If the optional properties 'open-source' is not found, the GPIO line +will be driven in the inactive state. Otherwise its not driven until +the restart is initiated. + +When the system is restarted, the restart handler will be invoked in +priority order. The gpio is configured as an output, and driven active, +triggering a level triggered reset condition. This will also cause an +inactive->active edge condition, triggering positive edge triggered +reset. After a delay specified by active-delay, the GPIO is set to +inactive, thus causing an active->inactive edge, triggering negative edge +triggered reset. After a delay specified by inactive-delay, the GPIO +is driven active again. After a delay specified by wait-delay, the +restart handler completes allowing other restart handlers to be attempted. + +Required properties: +- compatible : should be "gpio-restart". +- gpios : The GPIO to set high/low, see "gpios property" in + Documentation/devicetree/bindings/gpio/gpio.txt. If the pin should be + low to reset the board set it to "Active Low", otherwise set + gpio to "Active High". + +Optional properties: +- open-source : Treat the GPIO as being open source and defer driving + it to when the restart is initiated. If this optional property is not + specified, the GPIO is initialized as an output in its inactive state. +- priority : A priority ranging from 0 to 255 (default 128) according to + the following guidelines: + 0: Restart handler of last resort, with limited restart + capabilities + 128: Default restart handler; use if no other restart handler is + expected to be available, and/or if restart functionality is + sufficient to restart the entire system + 255: Highest priority restart handler, will preempt all other + restart handlers +- active-delay: Delay (default 100) to wait after driving gpio active [ms] +- inactive-delay: Delay (default 100) to wait after driving gpio inactive [ms] +- wait-delay: Delay (default 3000) to wait after completing restart + sequence [ms] + +Examples: + +gpio-restart { + compatible = "gpio-restart"; + gpios = <&gpio 4 0>; + priority = <128>; + active-delay = <100>; + inactive-delay = <100>; + wait-delay = <3000>; +}; diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index 88603b6..a178e9c 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -39,6 +39,14 @@ config POWER_RESET_GPIO If your board needs a GPIO high/low to power down, say Y and create a binding in your devicetree. +config POWER_RESET_GPIO_RESTART + bool "GPIO restart driver" + depends on OF_GPIO && POWER_RESET + help + This driver supports restarting your board via a GPIO line. + If your board needs a GPIO high/low to restart, say Y and + create a binding in your devicetree. + config POWER_RESET_HISI bool "Hisilicon power-off driver" depends on POWER_RESET && ARCH_HISI diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile index 06f106b..ab8cd34 100644 --- a/drivers/power/reset/Makefile +++ b/drivers/power/reset/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_POWER_RESET_AS3722) += as3722-poweroff.o obj-$(CONFIG_POWER_RESET_AXXIA) += axxia-reset.o obj-$(CONFIG_POWER_RESET_BRCMSTB) += brcmstb-reboot.o obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o +obj-$(CONFIG_POWER_RESET_GPIO_RESTART) += gpio-restart.o obj-$(CONFIG_POWER_RESET_HISI) += hisi-reboot.o obj-$(CONFIG_POWER_RESET_MSM) += msm-poweroff.o obj-$(CONFIG_POWER_RESET_LTC2952) += ltc2952-poweroff.o diff --git a/drivers/power/reset/gpio-restart.c b/drivers/power/reset/gpio-restart.c new file mode 100644 index 0000000..a76829b --- /dev/null +++ b/drivers/power/reset/gpio-restart.c @@ -0,0 +1,149 @@ +/* + * Toggles a GPIO pin to restart a device + * + * Copyright (C) 2014 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Based on the gpio-poweroff driver. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +struct gpio_restart { + struct gpio_desc *reset_gpio; + struct notifier_block restart_handler; + u32 active_delay_ms; + u32 inactive_delay_ms; + u32 wait_delay_ms; +}; + +static int gpio_restart_notify(struct notifier_block *this, + unsigned long mode, void *cmd) +{ + struct gpio_restart *gpio_restart = + container_of(this, struct gpio_restart, restart_handler); + + /* drive it active, also inactive->active edge */ + gpiod_direction_output(gpio_restart->reset_gpio, 1); + mdelay(gpio_restart->active_delay_ms); + + /* drive inactive, also active->inactive edge */ + gpiod_set_value(gpio_restart->reset_gpio, 0); + mdelay(gpio_restart->inactive_delay_ms); + + /* drive it active, also inactive->active edge */ + gpiod_set_value(gpio_restart->reset_gpio, 1); + + /* give it some time */ + mdelay(gpio_restart->wait_delay_ms); + + WARN_ON(1); + + return NOTIFY_DONE; +} + +static int gpio_restart_probe(struct platform_device *pdev) +{ + struct gpio_restart *gpio_restart; + bool open_source = false; + u32 property; + int ret; + + gpio_restart = devm_kzalloc(&pdev->dev, sizeof(*gpio_restart), + GFP_KERNEL); + if (!gpio_restart) + return -ENOMEM; + + open_source = of_property_read_bool(pdev->dev.of_node, "open-source"); + + gpio_restart->reset_gpio = devm_gpiod_get(&pdev->dev, NULL, + open_source ? GPIOD_IN : GPIOD_OUT_LOW); + if (IS_ERR(gpio_restart->reset_gpio)) { + dev_err(&pdev->dev, "Could net get reset GPIO\n"); + return PTR_ERR(gpio_restart->reset_gpio); + } + + gpio_restart->restart_handler.notifier_call = gpio_restart_notify; + gpio_restart->restart_handler.priority = 128; + gpio_restart->active_delay_ms = 100; + gpio_restart->inactive_delay_ms = 100; + gpio_restart->wait_delay_ms = 3000; + + ret = of_property_read_u32(pdev->dev.of_node, "priority", &property); + if (!ret) { + if (property > 255) + dev_err(&pdev->dev, "Invalid priority property: %u\n", + property); + else + gpio_restart->restart_handler.priority = property; + } + + of_property_read_u32(pdev->dev.of_node, "active-delay", + &gpio_restart->active_delay_ms); + of_property_read_u32(pdev->dev.of_node, "inactive-delay", + &gpio_restart->inactive_delay_ms); + of_property_read_u32(pdev->dev.of_node, "wait-delay", + &gpio_restart->wait_delay_ms); + + platform_set_drvdata(pdev, gpio_restart); + + ret = register_restart_handler(&gpio_restart->restart_handler); + if (ret) { + dev_err(&pdev->dev, "%s: cannot register restart handler, %d\n", + __func__, ret); + return -ENODEV; + } + + return 0; +} + +static int gpio_restart_remove(struct platform_device *pdev) +{ + struct gpio_restart *gpio_restart = platform_get_drvdata(pdev); + int ret; + + ret = unregister_restart_handler(&gpio_restart->restart_handler); + if (ret) { + dev_err(&pdev->dev, + "%s: cannot unregister restart handler, %d\n", + __func__, ret); + return -ENODEV; + } + + return 0; +} + +static const struct of_device_id of_gpio_restart_match[] = { + { .compatible = "gpio-restart", }, + {}, +}; + +static struct platform_driver gpio_restart_driver = { + .probe = gpio_restart_probe, + .remove = gpio_restart_remove, + .driver = { + .name = "restart-gpio", + .owner = THIS_MODULE, + .of_match_table = of_gpio_restart_match, + }, +}; + +module_platform_driver(gpio_restart_driver); + +MODULE_AUTHOR("David Riley "); +MODULE_DESCRIPTION("GPIO restart driver"); +MODULE_LICENSE("GPL"); -- cgit v0.10.2 From 18a702e0de9879d5c0225a09f494443f0b91a0cc Mon Sep 17 00:00:00 2001 From: Pramod Gurav Date: Thu, 25 Sep 2014 17:03:51 +0530 Subject: power: reset: use restart_notifier mechanism for msm-poweroff This change replaces use of arm_pm_restart with recently introduced reset mechanism in Linux kernel called restart_notifier. Choosing priority 128, which is default priority, as according to documentation, this mechanism is sufficient to restart the entire system. Cc: Guenter Roeck Cc: Josh Cartwright Cc: Sebastian Reichel Cc: Dmitry Eremin-Solenikov Cc: David Woodhouse Cc: Stephen Boyd Cc: linux-pm@vger.kernel.org Reviewed-by: Guenter Roeck Reviewed-by: Stephen Boyd Signed-off-by: Pramod Gurav Signed-off-by: Sebastian Reichel diff --git a/drivers/power/reset/msm-poweroff.c b/drivers/power/reset/msm-poweroff.c index 774f9a3..4702efd 100644 --- a/drivers/power/reset/msm-poweroff.c +++ b/drivers/power/reset/msm-poweroff.c @@ -20,21 +20,27 @@ #include #include #include - -#include +#include static void __iomem *msm_ps_hold; - -static void do_msm_restart(enum reboot_mode reboot_mode, const char *cmd) +static int do_msm_restart(struct notifier_block *nb, unsigned long action, + void *data) { writel(0, msm_ps_hold); mdelay(10000); + + return NOTIFY_DONE; } +static struct notifier_block restart_nb = { + .notifier_call = do_msm_restart, + .priority = 128, +}; + static void do_msm_poweroff(void) { /* TODO: Add poweroff capability */ - do_msm_restart(REBOOT_HARD, NULL); + do_msm_restart(&restart_nb, 0, NULL); } static int msm_restart_probe(struct platform_device *pdev) @@ -47,8 +53,10 @@ static int msm_restart_probe(struct platform_device *pdev) if (IS_ERR(msm_ps_hold)) return PTR_ERR(msm_ps_hold); + register_restart_handler(&restart_nb); + pm_power_off = do_msm_poweroff; - arm_pm_restart = do_msm_restart; + return 0; } -- cgit v0.10.2 From a3c0c3e79066c78b890c078998c8c6bd577f1d21 Mon Sep 17 00:00:00 2001 From: Puthikorn Voravootivat Date: Tue, 30 Sep 2014 11:18:56 -0700 Subject: bq27x00_battery: Fix flag reading for bq27742 This patch fix the following issues. - Flag for bq27742 is 2 bytes contracy to 1 byte for older hardware - Don't read FLAG_CI as bq27742 does not have it - Use Battery full capacity register as last measure discharge Signed-off-by: Puthikorn Voravootivat Reviewed-by: Benson Leung Signed-off-by: Sebastian Reichel diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c index 5b9f0ea..e3bacfe 100644 --- a/drivers/power/bq27x00_battery.c +++ b/drivers/power/bq27x00_battery.c @@ -254,9 +254,11 @@ static inline int bq27x00_battery_read_nac(struct bq27x00_device_info *di) { int flags; bool is_bq27500 = di->chip == BQ27500; + bool is_bq27742 = di->chip == BQ27742; bool is_higher = bq27xxx_is_chip_version_higher(di); + bool flags_1b = !(is_bq27500 || is_bq27742); - flags = bq27x00_read(di, BQ27x00_REG_FLAGS, !is_bq27500); + flags = bq27x00_read(di, BQ27x00_REG_FLAGS, flags_1b); if (flags >= 0 && !is_higher && (flags & BQ27000_FLAG_CI)) return -ENODATA; @@ -436,13 +438,14 @@ static void bq27x00_update(struct bq27x00_device_info *di) bool is_bq27500 = di->chip == BQ27500; bool is_bq27425 = di->chip == BQ27425; bool is_bq27742 = di->chip == BQ27742; + bool flags_1b = !(is_bq27500 || is_bq27742); - cache.flags = bq27x00_read(di, BQ27x00_REG_FLAGS, !is_bq27500); + cache.flags = bq27x00_read(di, BQ27x00_REG_FLAGS, flags_1b); if ((cache.flags & 0xff) == 0xff) /* read error */ cache.flags = -1; if (cache.flags >= 0) { - if (!is_bq27500 && !is_bq27425 + if (!is_bq27500 && !is_bq27425 && !is_bq27742 && (cache.flags & BQ27000_FLAG_CI)) { dev_info(di->dev, "battery is not calibrated! ignoring capacity values\n"); cache.capacity = -ENODATA; @@ -470,9 +473,7 @@ static void bq27x00_update(struct bq27x00_device_info *di) bq27x00_battery_read_time(di, BQ27x00_REG_TTF); } - if (!is_bq27742) - cache.charge_full = - bq27x00_battery_read_lmd(di); + cache.charge_full = bq27x00_battery_read_lmd(di); cache.health = bq27x00_battery_read_health(di); } cache.temperature = bq27x00_battery_read_temperature(di); -- cgit v0.10.2 From 09fb07bcaf529a21612fbebd1297d8c5dd1abf1b Mon Sep 17 00:00:00 2001 From: Feng Kan Date: Tue, 30 Sep 2014 16:25:03 -0700 Subject: power: reset: Add generic SYSCON register mapped reset Add a generic SYSCON register mapped reset mechanism. Signed-off-by: Feng Kan Signed-off-by: Sebastian Reichel diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index a178e9c..addb26a 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -118,3 +118,8 @@ config POWER_RESET_KEYSTONE help Reboot support for the KEYSTONE SoCs. +config POWER_RESET_SYSCON + bool "Generic SYSCON regmap reset driver" + depends on POWER_RESET && MFD_SYSCON && OF + help + Reboot support for generic SYSCON mapped register reset. diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile index ab8cd34..61ead27 100644 --- a/drivers/power/reset/Makefile +++ b/drivers/power/reset/Makefile @@ -13,3 +13,4 @@ obj-$(CONFIG_POWER_RESET_ST) += st-poweroff.o obj-$(CONFIG_POWER_RESET_VEXPRESS) += vexpress-poweroff.o obj-$(CONFIG_POWER_RESET_XGENE) += xgene-reboot.o obj-$(CONFIG_POWER_RESET_KEYSTONE) += keystone-reset.o +obj-$(CONFIG_POWER_RESET_SYSCON) += syscon-reboot.o diff --git a/drivers/power/reset/syscon-reboot.c b/drivers/power/reset/syscon-reboot.c new file mode 100644 index 0000000..948e0ee --- /dev/null +++ b/drivers/power/reset/syscon-reboot.c @@ -0,0 +1,96 @@ +/* + * Generic Syscon Reboot Driver + * + * Copyright (c) 2013, Applied Micro Circuits Corporation + * Author: Feng Kan + * + * 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 the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +struct syscon_reboot_context { + struct regmap *map; + u32 offset; + u32 mask; + struct notifier_block restart_handler; +}; + +static struct syscon_reboot_context *syscon_reboot_ctx; + +static int syscon_restart_handle(struct notifier_block *this, + unsigned long mode, void *cmd) +{ + struct syscon_reboot_context *ctx = syscon_reboot_ctx; + unsigned long timeout; + + /* Issue the reboot */ + if (ctx->map) + regmap_write(ctx->map, ctx->offset, ctx->mask); + + timeout = jiffies + HZ; + while (time_before(jiffies, timeout)) + cpu_relax(); + + pr_emerg("Unable to restart system\n"); + return NOTIFY_DONE; +} + +static int syscon_reboot_probe(struct platform_device *pdev) +{ + struct syscon_reboot_context *ctx; + struct device *dev = &pdev->dev; + int err; + + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + ctx->map = syscon_regmap_lookup_by_phandle(dev->of_node, "regmap"); + if (IS_ERR(ctx->map)) + return PTR_ERR(ctx->map); + + if (of_property_read_u32(pdev->dev.of_node, "offset", &ctx->offset)) + return -EINVAL; + + if (of_property_read_u32(pdev->dev.of_node, "mask", &ctx->mask)) + return -EINVAL; + + ctx->restart_handler.notifier_call = syscon_restart_handle; + ctx->restart_handler.priority = 128; + err = register_restart_handler(&ctx->restart_handler); + if (err) + dev_err(dev, "can't register restart notifier (err=%d)\n", err); + + syscon_reboot_ctx = ctx; + + return 0; +} + +static struct of_device_id syscon_reboot_of_match[] = { + { .compatible = "syscon-reboot" }, + {} +}; + +static struct platform_driver syscon_reboot_driver = { + .probe = syscon_reboot_probe, + .driver = { + .name = "syscon-reboot", + .of_match_table = syscon_reboot_of_match, + }, +}; +module_platform_driver(syscon_reboot_driver); -- cgit v0.10.2 From a7ba749e01461a3802d22157fc0e6c4ecbe0dd32 Mon Sep 17 00:00:00 2001 From: Feng Kan Date: Tue, 30 Sep 2014 16:25:04 -0700 Subject: Documentation: power: reset: Add documentation for generic SYSCON reboot driver Add documentation for generic SYSCON reboot driver. Signed-off-by: Feng Kan Signed-off-by: Sebastian Reichel diff --git a/Documentation/devicetree/bindings/power/reset/syscon-reboot.txt b/Documentation/devicetree/bindings/power/reset/syscon-reboot.txt new file mode 100644 index 0000000..1190631 --- /dev/null +++ b/Documentation/devicetree/bindings/power/reset/syscon-reboot.txt @@ -0,0 +1,23 @@ +Generic SYSCON mapped register reset driver + +This is a generic reset driver using syscon to map the reset register. +The reset is generally performed with a write to the reset register +defined by the register map pointed by syscon reference plus the offset +with the mask defined in the reboot node. + +Required properties: +- compatible: should contain "syscon-reboot" +- regmap: this is phandle to the register map node +- offset: offset in the register map for the reboot register (in bytes) +- mask: the reset value written to the reboot register (32 bit access) + +Default will be little endian mode, 32 bit access only. + +Examples: + + reboot { + compatible = "syscon-reboot"; + regmap = <®mapnode>; + offset = <0x0>; + mask = <0x1>; + }; -- cgit v0.10.2 From afaebbdbd48ada5ead707d6a90ce4b604e1d77d4 Mon Sep 17 00:00:00 2001 From: Feng Kan Date: Thu, 2 Oct 2014 11:24:15 -0700 Subject: power: reset: corrections for simple syscon reboot driver This patch is to fix some bugs in reboot driver. Which includes auto selection of the MFD_SYSCON for the driver, use of container to locate restart handler, correction of the count down failure timer and ordering of the header file. Signed-off-by: Feng Kan Reviewed-by: Guenter Roeck [ sre: return err instead of 0 in syscon_reboot_probe() ] Signed-off-by: Sebastian Reichel diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index addb26a..3b451e1 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -120,6 +120,7 @@ config POWER_RESET_KEYSTONE config POWER_RESET_SYSCON bool "Generic SYSCON regmap reset driver" - depends on POWER_RESET && MFD_SYSCON && OF + depends on POWER_RESET && OF + select MFD_SYSCON help Reboot support for generic SYSCON mapped register reset. diff --git a/drivers/power/reset/syscon-reboot.c b/drivers/power/reset/syscon-reboot.c index 948e0ee..815b901 100644 --- a/drivers/power/reset/syscon-reboot.c +++ b/drivers/power/reset/syscon-reboot.c @@ -14,14 +14,15 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +#include #include -#include -#include -#include #include #include -#include +#include +#include +#include #include +#include struct syscon_reboot_context { struct regmap *map; @@ -30,21 +31,17 @@ struct syscon_reboot_context { struct notifier_block restart_handler; }; -static struct syscon_reboot_context *syscon_reboot_ctx; - static int syscon_restart_handle(struct notifier_block *this, unsigned long mode, void *cmd) { - struct syscon_reboot_context *ctx = syscon_reboot_ctx; - unsigned long timeout; + struct syscon_reboot_context *ctx = + container_of(this, struct syscon_reboot_context, + restart_handler); /* Issue the reboot */ - if (ctx->map) - regmap_write(ctx->map, ctx->offset, ctx->mask); + regmap_write(ctx->map, ctx->offset, ctx->mask); - timeout = jiffies + HZ; - while (time_before(jiffies, timeout)) - cpu_relax(); + mdelay(1000); pr_emerg("Unable to restart system\n"); return NOTIFY_DONE; @@ -76,9 +73,7 @@ static int syscon_reboot_probe(struct platform_device *pdev) if (err) dev_err(dev, "can't register restart notifier (err=%d)\n", err); - syscon_reboot_ctx = ctx; - - return 0; + return err; } static struct of_device_id syscon_reboot_of_match[] = { -- cgit v0.10.2 From c8c5ebcc5e33a0ae65b6b1cba8d257d423f25da0 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 2 Oct 2014 14:12:34 +0200 Subject: Documentation: charger: max14577: Update the date of introducing ABI Update the date of introducing max14577 charger's ABI (fast_charge_timer sysfs entry) to approximate date of kernel release which actually introduces this. The old date came from previous driver submissions. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Sebastian Reichel diff --git a/Documentation/ABI/testing/sysfs-class-power b/Documentation/ABI/testing/sysfs-class-power index ad4fb90..909e760 100644 --- a/Documentation/ABI/testing/sysfs-class-power +++ b/Documentation/ABI/testing/sysfs-class-power @@ -20,7 +20,7 @@ Description: amended over time. What: /sys/class/power_supply/max14577-charger/device/fast_charge_timer -Date: July 2014 +Date: October 2014 KernelVersion: 3.18.0 Contact: Krzysztof Kozlowski Description: -- cgit v0.10.2 From 7881c64716f3a7d60b325ed0ad4d15f49b474a43 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 4 Oct 2014 16:31:13 -0700 Subject: power: ab8500_fg: Fix build warning Fix drivers/power/ab8500_fg.c: In function 'ab8500_fg_probe': drivers/power/ab8500_fg.c:2989:27: warning: 'i' may be used uninitialized in this function drivers/power/ab8500_fg.c:2972:15: note: 'i' was declared here which actually points to a real bug. Signed-off-by: Guenter Roeck Signed-off-by: Sebastian Reichel diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c index 3cb4178..217da4b 100644 --- a/drivers/power/ab8500_fg.c +++ b/drivers/power/ab8500_fg.c @@ -2969,7 +2969,7 @@ static struct device_attribute ab8505_fg_sysfs_psy_attrs[] = { static int ab8500_fg_sysfs_psy_create_attrs(struct device *dev) { - unsigned int i, j; + unsigned int i; struct power_supply *psy = dev_get_drvdata(dev); struct ab8500_fg *di; @@ -2978,14 +2978,15 @@ static int ab8500_fg_sysfs_psy_create_attrs(struct device *dev) if (((is_ab8505(di->parent) || is_ab9540(di->parent)) && abx500_get_chip_id(dev->parent) >= AB8500_CUT2P0) || is_ab8540(di->parent)) { - for (j = 0; j < ARRAY_SIZE(ab8505_fg_sysfs_psy_attrs); j++) - if (device_create_file(dev, &ab8505_fg_sysfs_psy_attrs[j])) + for (i = 0; i < ARRAY_SIZE(ab8505_fg_sysfs_psy_attrs); i++) + if (device_create_file(dev, + &ab8505_fg_sysfs_psy_attrs[i])) goto sysfs_psy_create_attrs_failed_ab8505; } return 0; sysfs_psy_create_attrs_failed_ab8505: dev_err(dev, "Failed creating sysfs psy attrs for ab8505.\n"); - while (j--) + while (i--) device_remove_file(dev, &ab8505_fg_sysfs_psy_attrs[i]); return -EIO; -- cgit v0.10.2