diff options
author | Lee Jones <lee.jones@linaro.org> | 2013-02-15 05:58:58 -0500 |
---|---|---|
committer | Lee Jones <lee.jones@linaro.org> | 2013-03-06 23:35:58 -0500 |
commit | 4d3b4aa58ac9e18029c4d0259630414cffd3ba76 (patch) | |
tree | bdf4b1ede008d25d894f54c6d74486d394096d9a /drivers/power | |
parent | 9b7f50e3ea9a98f518fc4077f8bebc96717acff5 (diff) |
abx500-chargalg: Add charging current step interface
To prevent overheating, provide differnt steps of charging current
interface to allow thermal mitigation. This will provide possibility
to reduce gradually the charging current.
Signed-off-by: Lee Jones <lee.jones@linaro.org>
Diffstat (limited to 'drivers/power')
-rw-r--r-- | drivers/power/abx500_chargalg.c | 231 |
1 files changed, 155 insertions, 76 deletions
diff --git a/drivers/power/abx500_chargalg.c b/drivers/power/abx500_chargalg.c index 7e4bc011dd8f..9863e423602c 100644 --- a/drivers/power/abx500_chargalg.c +++ b/drivers/power/abx500_chargalg.c | |||
@@ -47,6 +47,9 @@ | |||
47 | /* Plus margin for the low battery threshold */ | 47 | /* Plus margin for the low battery threshold */ |
48 | #define BAT_PLUS_MARGIN (100) | 48 | #define BAT_PLUS_MARGIN (100) |
49 | 49 | ||
50 | #define CHARGALG_CURR_STEP_LOW 0 | ||
51 | #define CHARGALG_CURR_STEP_HIGH 100 | ||
52 | |||
50 | #define to_abx500_chargalg_device_info(x) container_of((x), \ | 53 | #define to_abx500_chargalg_device_info(x) container_of((x), \ |
51 | struct abx500_chargalg, chargalg_psy); | 54 | struct abx500_chargalg, chargalg_psy); |
52 | 55 | ||
@@ -80,6 +83,11 @@ struct abx500_chargalg_suspension_status { | |||
80 | bool usb_suspended; | 83 | bool usb_suspended; |
81 | }; | 84 | }; |
82 | 85 | ||
86 | struct abx500_chargalg_current_step_status { | ||
87 | bool curr_step_change; | ||
88 | int curr_step; | ||
89 | }; | ||
90 | |||
83 | struct abx500_chargalg_battery_data { | 91 | struct abx500_chargalg_battery_data { |
84 | int temp; | 92 | int temp; |
85 | int volt; | 93 | int volt; |
@@ -220,6 +228,7 @@ enum maxim_ret { | |||
220 | * @batt_data: data of the battery | 228 | * @batt_data: data of the battery |
221 | * @susp_status: current charger suspension status | 229 | * @susp_status: current charger suspension status |
222 | * @bm: Platform specific battery management information | 230 | * @bm: Platform specific battery management information |
231 | * @curr_status: Current step status for over-current protection | ||
223 | * @parent: pointer to the struct abx500 | 232 | * @parent: pointer to the struct abx500 |
224 | * @chargalg_psy: structure that holds the battery properties exposed by | 233 | * @chargalg_psy: structure that holds the battery properties exposed by |
225 | * the charging algorithm | 234 | * the charging algorithm |
@@ -245,6 +254,7 @@ struct abx500_chargalg { | |||
245 | struct abx500_chargalg_battery_data batt_data; | 254 | struct abx500_chargalg_battery_data batt_data; |
246 | struct abx500_chargalg_suspension_status susp_status; | 255 | struct abx500_chargalg_suspension_status susp_status; |
247 | struct ab8500 *parent; | 256 | struct ab8500 *parent; |
257 | struct abx500_chargalg_current_step_status curr_status; | ||
248 | struct abx500_bm_data *bm; | 258 | struct abx500_bm_data *bm; |
249 | struct power_supply chargalg_psy; | 259 | struct power_supply chargalg_psy; |
250 | struct ux500_charger *ac_chg; | 260 | struct ux500_charger *ac_chg; |
@@ -268,6 +278,12 @@ static enum power_supply_property abx500_chargalg_props[] = { | |||
268 | POWER_SUPPLY_PROP_HEALTH, | 278 | POWER_SUPPLY_PROP_HEALTH, |
269 | }; | 279 | }; |
270 | 280 | ||
281 | struct abx500_chargalg_sysfs_entry { | ||
282 | struct attribute attr; | ||
283 | ssize_t (*show)(struct abx500_chargalg *, char *); | ||
284 | ssize_t (*store)(struct abx500_chargalg *, const char *, size_t); | ||
285 | }; | ||
286 | |||
271 | /** | 287 | /** |
272 | * abx500_chargalg_safety_timer_expired() - Expiration of the safety timer | 288 | * abx500_chargalg_safety_timer_expired() - Expiration of the safety timer |
273 | * @timer: pointer to the hrtimer structure | 289 | * @timer: pointer to the hrtimer structure |
@@ -402,6 +418,22 @@ static int abx500_chargalg_check_charger_connection(struct abx500_chargalg *di) | |||
402 | } | 418 | } |
403 | 419 | ||
404 | /** | 420 | /** |
421 | * abx500_chargalg_check_current_step_status() - Check charging current | ||
422 | * step status. | ||
423 | * @di: pointer to the abx500_chargalg structure | ||
424 | * | ||
425 | * This function will check if there is a change in the charging current step | ||
426 | * and change charge state accordingly. | ||
427 | */ | ||
428 | static void abx500_chargalg_check_current_step_status | ||
429 | (struct abx500_chargalg *di) | ||
430 | { | ||
431 | if (di->curr_status.curr_step_change) | ||
432 | abx500_chargalg_state_to(di, STATE_NORMAL_INIT); | ||
433 | di->curr_status.curr_step_change = false; | ||
434 | } | ||
435 | |||
436 | /** | ||
405 | * abx500_chargalg_start_safety_timer() - Start charging safety timer | 437 | * abx500_chargalg_start_safety_timer() - Start charging safety timer |
406 | * @di: pointer to the abx500_chargalg structure | 438 | * @di: pointer to the abx500_chargalg structure |
407 | * | 439 | * |
@@ -1300,6 +1332,7 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di) | |||
1300 | { | 1332 | { |
1301 | int charger_status; | 1333 | int charger_status; |
1302 | int ret; | 1334 | int ret; |
1335 | int curr_step_lvl; | ||
1303 | 1336 | ||
1304 | /* Collect data from all power_supply class devices */ | 1337 | /* Collect data from all power_supply class devices */ |
1305 | class_for_each_device(power_supply_class, NULL, | 1338 | class_for_each_device(power_supply_class, NULL, |
@@ -1310,6 +1343,7 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di) | |||
1310 | abx500_chargalg_check_charger_voltage(di); | 1343 | abx500_chargalg_check_charger_voltage(di); |
1311 | 1344 | ||
1312 | charger_status = abx500_chargalg_check_charger_connection(di); | 1345 | charger_status = abx500_chargalg_check_charger_connection(di); |
1346 | abx500_chargalg_check_current_step_status(di); | ||
1313 | 1347 | ||
1314 | if (is_ab8500(di->parent)) { | 1348 | if (is_ab8500(di->parent)) { |
1315 | ret = abx500_chargalg_check_charger_enable(di); | 1349 | ret = abx500_chargalg_check_charger_enable(di); |
@@ -1523,9 +1557,18 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di) | |||
1523 | } | 1557 | } |
1524 | } | 1558 | } |
1525 | 1559 | ||
1526 | abx500_chargalg_start_charging(di, | 1560 | if (di->curr_status.curr_step == CHARGALG_CURR_STEP_LOW) |
1527 | di->bm->bat_type[di->bm->batt_id].normal_vol_lvl, | 1561 | abx500_chargalg_stop_charging(di); |
1528 | di->bm->bat_type[di->bm->batt_id].normal_cur_lvl); | 1562 | else { |
1563 | curr_step_lvl = di->bm->bat_type[ | ||
1564 | di->bm->batt_id].normal_cur_lvl | ||
1565 | * di->curr_status.curr_step | ||
1566 | / CHARGALG_CURR_STEP_HIGH; | ||
1567 | abx500_chargalg_start_charging(di, | ||
1568 | di->bm->bat_type[di->bm->batt_id] | ||
1569 | .normal_vol_lvl, curr_step_lvl); | ||
1570 | } | ||
1571 | |||
1529 | abx500_chargalg_state_to(di, STATE_NORMAL); | 1572 | abx500_chargalg_state_to(di, STATE_NORMAL); |
1530 | abx500_chargalg_start_safety_timer(di); | 1573 | abx500_chargalg_start_safety_timer(di); |
1531 | abx500_chargalg_stop_maintenance_timer(di); | 1574 | abx500_chargalg_stop_maintenance_timer(di); |
@@ -1767,99 +1810,134 @@ static int abx500_chargalg_get_property(struct power_supply *psy, | |||
1767 | 1810 | ||
1768 | /* Exposure to the sysfs interface */ | 1811 | /* Exposure to the sysfs interface */ |
1769 | 1812 | ||
1770 | /** | 1813 | static ssize_t abx500_chargalg_curr_step_show(struct abx500_chargalg *di, |
1771 | * abx500_chargalg_sysfs_show() - sysfs show operations | 1814 | char *buf) |
1772 | * @kobj: pointer to the struct kobject | ||
1773 | * @attr: pointer to the struct attribute | ||
1774 | * @buf: buffer that holds the parameter to send to userspace | ||
1775 | * | ||
1776 | * Returns a buffer to be displayed in user space | ||
1777 | */ | ||
1778 | static ssize_t abx500_chargalg_sysfs_show(struct kobject *kobj, | ||
1779 | struct attribute *attr, char *buf) | ||
1780 | { | 1815 | { |
1781 | struct abx500_chargalg *di = container_of(kobj, | 1816 | return sprintf(buf, "%d\n", di->curr_status.curr_step); |
1782 | struct abx500_chargalg, chargalg_kobject); | 1817 | } |
1818 | |||
1819 | static ssize_t abx500_chargalg_curr_step_store(struct abx500_chargalg *di, | ||
1820 | const char *buf, size_t length) | ||
1821 | { | ||
1822 | long int param; | ||
1823 | int ret; | ||
1824 | |||
1825 | ret = kstrtol(buf, 10, ¶m); | ||
1826 | if (ret < 0) | ||
1827 | return ret; | ||
1783 | 1828 | ||
1829 | di->curr_status.curr_step = param; | ||
1830 | if (di->curr_status.curr_step >= CHARGALG_CURR_STEP_LOW && | ||
1831 | di->curr_status.curr_step <= CHARGALG_CURR_STEP_HIGH) { | ||
1832 | di->curr_status.curr_step_change = true; | ||
1833 | queue_work(di->chargalg_wq, &di->chargalg_work); | ||
1834 | } else | ||
1835 | dev_info(di->dev, "Wrong current step\n" | ||
1836 | "Enter 0. Disable AC/USB Charging\n" | ||
1837 | "1--100. Set AC/USB charging current step\n" | ||
1838 | "100. Enable AC/USB Charging\n"); | ||
1839 | |||
1840 | return strlen(buf); | ||
1841 | } | ||
1842 | |||
1843 | |||
1844 | static ssize_t abx500_chargalg_en_show(struct abx500_chargalg *di, | ||
1845 | char *buf) | ||
1846 | { | ||
1784 | return sprintf(buf, "%d\n", | 1847 | return sprintf(buf, "%d\n", |
1785 | di->susp_status.ac_suspended && | 1848 | di->susp_status.ac_suspended && |
1786 | di->susp_status.usb_suspended); | 1849 | di->susp_status.usb_suspended); |
1787 | } | 1850 | } |
1788 | 1851 | ||
1789 | /** | 1852 | static ssize_t abx500_chargalg_en_store(struct abx500_chargalg *di, |
1790 | * abx500_chargalg_sysfs_charger() - sysfs store operations | 1853 | const char *buf, size_t length) |
1791 | * @kobj: pointer to the struct kobject | ||
1792 | * @attr: pointer to the struct attribute | ||
1793 | * @buf: buffer that holds the parameter passed from userspace | ||
1794 | * @length: length of the parameter passed | ||
1795 | * | ||
1796 | * Returns length of the buffer(input taken from user space) on success | ||
1797 | * else error code on failure | ||
1798 | * The operation to be performed on passing the parameters from the user space. | ||
1799 | */ | ||
1800 | static ssize_t abx500_chargalg_sysfs_charger(struct kobject *kobj, | ||
1801 | struct attribute *attr, const char *buf, size_t length) | ||
1802 | { | 1854 | { |
1803 | struct abx500_chargalg *di = container_of(kobj, | ||
1804 | struct abx500_chargalg, chargalg_kobject); | ||
1805 | long int param; | 1855 | long int param; |
1806 | int ac_usb; | 1856 | int ac_usb; |
1807 | int ret; | 1857 | int ret; |
1808 | char entry = *attr->name; | ||
1809 | 1858 | ||
1810 | switch (entry) { | 1859 | ret = kstrtol(buf, 10, ¶m); |
1811 | case 'c': | 1860 | if (ret < 0) |
1812 | ret = strict_strtol(buf, 10, ¶m); | 1861 | return ret; |
1813 | if (ret < 0) | 1862 | |
1814 | return ret; | 1863 | ac_usb = param; |
1815 | 1864 | switch (ac_usb) { | |
1816 | ac_usb = param; | 1865 | case 0: |
1817 | switch (ac_usb) { | 1866 | /* Disable charging */ |
1818 | case 0: | 1867 | di->susp_status.ac_suspended = true; |
1819 | /* Disable charging */ | 1868 | di->susp_status.usb_suspended = true; |
1820 | di->susp_status.ac_suspended = true; | 1869 | di->susp_status.suspended_change = true; |
1821 | di->susp_status.usb_suspended = true; | 1870 | /* Trigger a state change */ |
1822 | di->susp_status.suspended_change = true; | 1871 | queue_work(di->chargalg_wq, |
1823 | /* Trigger a state change */ | 1872 | &di->chargalg_work); |
1824 | queue_work(di->chargalg_wq, | 1873 | break; |
1825 | &di->chargalg_work); | 1874 | case 1: |
1826 | break; | 1875 | /* Enable AC Charging */ |
1827 | case 1: | 1876 | di->susp_status.ac_suspended = false; |
1828 | /* Enable AC Charging */ | 1877 | di->susp_status.suspended_change = true; |
1829 | di->susp_status.ac_suspended = false; | 1878 | /* Trigger a state change */ |
1830 | di->susp_status.suspended_change = true; | 1879 | queue_work(di->chargalg_wq, |
1831 | /* Trigger a state change */ | 1880 | &di->chargalg_work); |
1832 | queue_work(di->chargalg_wq, | ||
1833 | &di->chargalg_work); | ||
1834 | break; | ||
1835 | case 2: | ||
1836 | /* Enable USB charging */ | ||
1837 | di->susp_status.usb_suspended = false; | ||
1838 | di->susp_status.suspended_change = true; | ||
1839 | /* Trigger a state change */ | ||
1840 | queue_work(di->chargalg_wq, | ||
1841 | &di->chargalg_work); | ||
1842 | break; | ||
1843 | default: | ||
1844 | dev_info(di->dev, "Wrong input\n" | ||
1845 | "Enter 0. Disable AC/USB Charging\n" | ||
1846 | "1. Enable AC charging\n" | ||
1847 | "2. Enable USB Charging\n"); | ||
1848 | }; | ||
1849 | break; | 1881 | break; |
1882 | case 2: | ||
1883 | /* Enable USB charging */ | ||
1884 | di->susp_status.usb_suspended = false; | ||
1885 | di->susp_status.suspended_change = true; | ||
1886 | /* Trigger a state change */ | ||
1887 | queue_work(di->chargalg_wq, | ||
1888 | &di->chargalg_work); | ||
1889 | break; | ||
1890 | default: | ||
1891 | dev_info(di->dev, "Wrong input\n" | ||
1892 | "Enter 0. Disable AC/USB Charging\n" | ||
1893 | "1. Enable AC charging\n" | ||
1894 | "2. Enable USB Charging\n"); | ||
1850 | }; | 1895 | }; |
1851 | return strlen(buf); | 1896 | return strlen(buf); |
1852 | } | 1897 | } |
1853 | 1898 | ||
1854 | static struct attribute abx500_chargalg_en_charger = \ | 1899 | static struct abx500_chargalg_sysfs_entry abx500_chargalg_en_charger = |
1900 | __ATTR(chargalg, 0644, abx500_chargalg_en_show, | ||
1901 | abx500_chargalg_en_store); | ||
1902 | |||
1903 | static struct abx500_chargalg_sysfs_entry abx500_chargalg_curr_step = | ||
1904 | __ATTR(chargalg_curr_step, 0644, abx500_chargalg_curr_step_show, | ||
1905 | abx500_chargalg_curr_step_store); | ||
1906 | |||
1907 | static ssize_t abx500_chargalg_sysfs_show(struct kobject *kobj, | ||
1908 | struct attribute *attr, char *buf) | ||
1909 | { | ||
1910 | struct abx500_chargalg_sysfs_entry *entry = container_of(attr, | ||
1911 | struct abx500_chargalg_sysfs_entry, attr); | ||
1912 | |||
1913 | struct abx500_chargalg *di = container_of(kobj, | ||
1914 | struct abx500_chargalg, chargalg_kobject); | ||
1915 | |||
1916 | if (!entry->show) | ||
1917 | return -EIO; | ||
1918 | |||
1919 | return entry->show(di, buf); | ||
1920 | } | ||
1921 | |||
1922 | static ssize_t abx500_chargalg_sysfs_charger(struct kobject *kobj, | ||
1923 | struct attribute *attr, const char *buf, size_t length) | ||
1855 | { | 1924 | { |
1856 | .name = "chargalg", | 1925 | struct abx500_chargalg_sysfs_entry *entry = container_of(attr, |
1857 | .mode = S_IRUGO | S_IWUSR, | 1926 | struct abx500_chargalg_sysfs_entry, attr); |
1858 | }; | 1927 | |
1928 | struct abx500_chargalg *di = container_of(kobj, | ||
1929 | struct abx500_chargalg, chargalg_kobject); | ||
1930 | |||
1931 | if (!entry->store) | ||
1932 | return -EIO; | ||
1933 | |||
1934 | return entry->store(di, buf, length); | ||
1935 | } | ||
1859 | 1936 | ||
1860 | static struct attribute *abx500_chargalg_chg[] = { | 1937 | static struct attribute *abx500_chargalg_chg[] = { |
1861 | &abx500_chargalg_en_charger, | 1938 | &abx500_chargalg_en_charger.attr, |
1862 | NULL | 1939 | &abx500_chargalg_curr_step.attr, |
1940 | NULL, | ||
1863 | }; | 1941 | }; |
1864 | 1942 | ||
1865 | static const struct sysfs_ops abx500_chargalg_sysfs_ops = { | 1943 | static const struct sysfs_ops abx500_chargalg_sysfs_ops = { |
@@ -2052,6 +2130,7 @@ static int abx500_chargalg_probe(struct platform_device *pdev) | |||
2052 | dev_err(di->dev, "failed to create sysfs entry\n"); | 2130 | dev_err(di->dev, "failed to create sysfs entry\n"); |
2053 | goto free_psy; | 2131 | goto free_psy; |
2054 | } | 2132 | } |
2133 | di->curr_status.curr_step = CHARGALG_CURR_STEP_HIGH; | ||
2055 | 2134 | ||
2056 | /* Run the charging algorithm */ | 2135 | /* Run the charging algorithm */ |
2057 | queue_delayed_work(di->chargalg_wq, &di->chargalg_periodic_work, 0); | 2136 | queue_delayed_work(di->chargalg_wq, &di->chargalg_periodic_work, 0); |