diff options
author | Marco Chiappero <marco@absence.it> | 2012-05-19 09:35:50 -0400 |
---|---|---|
committer | Matthew Garrett <mjg@redhat.com> | 2012-05-31 14:29:35 -0400 |
commit | 967145a030a86cba29fe090acd2b55b56568359e (patch) | |
tree | 953d210a7f7db723268b2b0afaf6131d5d43aea2 /drivers/platform/x86 | |
parent | ae188715ac3215f33ea6a8b829529225bf67deaa (diff) |
sony-laptop: support battery care functions
Allows limiting the maximum battery charge level to a selectable value
(100%, 80% and 50%).
[malattia@linux.it: group function specific variables in a struct, use
kstrtoul. Allow 0 to 100 values into sysfs files rounding to the actual
limit.]
Signed-off-by: Marco Chiappero <marco@absence.it>
Signed-off-by: Mattia Dongili <malattia@linux.it>
Signed-off-by: Matthew Garrett <mjg@redhat.com>
Diffstat (limited to 'drivers/platform/x86')
-rw-r--r-- | drivers/platform/x86/sony-laptop.c | 178 |
1 files changed, 178 insertions, 0 deletions
diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 89e5cf9b1914..0ac186e9abdc 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c | |||
@@ -144,6 +144,10 @@ static void sony_nc_kbd_backlight_resume(void); | |||
144 | static void sony_nc_kbd_backlight_setup(struct platform_device *pd); | 144 | static void sony_nc_kbd_backlight_setup(struct platform_device *pd); |
145 | static int sony_nc_kbd_backlight_cleanup(struct platform_device *pd); | 145 | static int sony_nc_kbd_backlight_cleanup(struct platform_device *pd); |
146 | 146 | ||
147 | static int sony_nc_battery_care_setup(struct platform_device *pd, | ||
148 | unsigned int handle); | ||
149 | static void sony_nc_battery_care_cleanup(struct platform_device *pd); | ||
150 | |||
147 | enum sony_nc_rfkill { | 151 | enum sony_nc_rfkill { |
148 | SONY_WIFI, | 152 | SONY_WIFI, |
149 | SONY_BLUETOOTH, | 153 | SONY_BLUETOOTH, |
@@ -1270,6 +1274,14 @@ static void sony_nc_function_setup(struct acpi_device *device, | |||
1270 | /* setup hotkeys */ | 1274 | /* setup hotkeys */ |
1271 | sony_call_snc_handle(handle, 0x100, &result); | 1275 | sony_call_snc_handle(handle, 0x100, &result); |
1272 | break; | 1276 | break; |
1277 | case 0x0115: | ||
1278 | case 0x0136: | ||
1279 | case 0x013f: | ||
1280 | result = sony_nc_battery_care_setup(pf_device, handle); | ||
1281 | if (result) | ||
1282 | pr_err("couldn't set up battery care function (%d)\n", | ||
1283 | result); | ||
1284 | break; | ||
1273 | case 0x0124: | 1285 | case 0x0124: |
1274 | case 0x0135: | 1286 | case 0x0135: |
1275 | sony_nc_rfkill_setup(device); | 1287 | sony_nc_rfkill_setup(device); |
@@ -1306,6 +1318,11 @@ static void sony_nc_function_cleanup(struct platform_device *pd) | |||
1306 | continue; | 1318 | continue; |
1307 | 1319 | ||
1308 | switch (handle) { | 1320 | switch (handle) { |
1321 | case 0x0115: | ||
1322 | case 0x0136: | ||
1323 | case 0x013f: | ||
1324 | sony_nc_battery_care_cleanup(pd); | ||
1325 | break; | ||
1309 | case 0x0124: | 1326 | case 0x0124: |
1310 | case 0x0135: | 1327 | case 0x0135: |
1311 | sony_nc_rfkill_cleanup(); | 1328 | sony_nc_rfkill_cleanup(); |
@@ -1745,6 +1762,167 @@ static void sony_nc_kbd_backlight_resume(void) | |||
1745 | &ignore); | 1762 | &ignore); |
1746 | } | 1763 | } |
1747 | 1764 | ||
1765 | struct battery_care_control { | ||
1766 | struct device_attribute attrs[2]; | ||
1767 | unsigned int handle; | ||
1768 | }; | ||
1769 | static struct battery_care_control *bcare_ctl; | ||
1770 | |||
1771 | static ssize_t sony_nc_battery_care_limit_store(struct device *dev, | ||
1772 | struct device_attribute *attr, | ||
1773 | const char *buffer, size_t count) | ||
1774 | { | ||
1775 | unsigned int result, cmd; | ||
1776 | unsigned long value; | ||
1777 | |||
1778 | if (count > 31) | ||
1779 | return -EINVAL; | ||
1780 | |||
1781 | if (kstrtoul(buffer, 10, &value)) | ||
1782 | return -EINVAL; | ||
1783 | |||
1784 | /* limit values (2 bits): | ||
1785 | * 00 - none | ||
1786 | * 01 - 80% | ||
1787 | * 10 - 50% | ||
1788 | * 11 - 100% | ||
1789 | * | ||
1790 | * bit 0: 0 disable BCL, 1 enable BCL | ||
1791 | * bit 1: 1 tell to store the battery limit (see bits 6,7) too | ||
1792 | * bits 2,3: reserved | ||
1793 | * bits 4,5: store the limit into the EC | ||
1794 | * bits 6,7: store the limit into the battery | ||
1795 | */ | ||
1796 | |||
1797 | /* | ||
1798 | * handle 0x0115 should allow storing on battery too; | ||
1799 | * handle 0x0136 same as 0x0115 + health status; | ||
1800 | * handle 0x013f, same as 0x0136 but no storing on the battery | ||
1801 | * | ||
1802 | * Store only inside the EC for now, regardless the handle number | ||
1803 | */ | ||
1804 | if (value == 0) | ||
1805 | /* disable limits */ | ||
1806 | cmd = 0x0; | ||
1807 | |||
1808 | else if (value <= 50) | ||
1809 | cmd = 0x21; | ||
1810 | |||
1811 | else if (value <= 80) | ||
1812 | cmd = 0x11; | ||
1813 | |||
1814 | else if (value <= 100) | ||
1815 | cmd = 0x31; | ||
1816 | |||
1817 | else | ||
1818 | return -EINVAL; | ||
1819 | |||
1820 | if (sony_call_snc_handle(bcare_ctl->handle, (cmd << 0x10) | 0x0100, | ||
1821 | &result)) | ||
1822 | return -EIO; | ||
1823 | |||
1824 | return count; | ||
1825 | } | ||
1826 | |||
1827 | static ssize_t sony_nc_battery_care_limit_show(struct device *dev, | ||
1828 | struct device_attribute *attr, char *buffer) | ||
1829 | { | ||
1830 | unsigned int result, status; | ||
1831 | |||
1832 | if (sony_call_snc_handle(bcare_ctl->handle, 0x0000, &result)) | ||
1833 | return -EIO; | ||
1834 | |||
1835 | status = (result & 0x01) ? ((result & 0x30) >> 0x04) : 0; | ||
1836 | switch (status) { | ||
1837 | case 1: | ||
1838 | status = 80; | ||
1839 | break; | ||
1840 | case 2: | ||
1841 | status = 50; | ||
1842 | break; | ||
1843 | case 3: | ||
1844 | status = 100; | ||
1845 | break; | ||
1846 | default: | ||
1847 | status = 0; | ||
1848 | break; | ||
1849 | } | ||
1850 | |||
1851 | return snprintf(buffer, PAGE_SIZE, "%d\n", status); | ||
1852 | } | ||
1853 | |||
1854 | static ssize_t sony_nc_battery_care_health_show(struct device *dev, | ||
1855 | struct device_attribute *attr, char *buffer) | ||
1856 | { | ||
1857 | ssize_t count = 0; | ||
1858 | unsigned int health; | ||
1859 | |||
1860 | if (sony_call_snc_handle(bcare_ctl->handle, 0x0200, &health)) | ||
1861 | return -EIO; | ||
1862 | |||
1863 | count = snprintf(buffer, PAGE_SIZE, "%d\n", health & 0xff); | ||
1864 | |||
1865 | return count; | ||
1866 | } | ||
1867 | |||
1868 | static int sony_nc_battery_care_setup(struct platform_device *pd, | ||
1869 | unsigned int handle) | ||
1870 | { | ||
1871 | int ret = 0; | ||
1872 | |||
1873 | bcare_ctl = kzalloc(sizeof(struct battery_care_control), GFP_KERNEL); | ||
1874 | if (!bcare_ctl) | ||
1875 | return -ENOMEM; | ||
1876 | |||
1877 | bcare_ctl->handle = handle; | ||
1878 | |||
1879 | sysfs_attr_init(&bcare_ctl->attrs[0].attr); | ||
1880 | bcare_ctl->attrs[0].attr.name = "battery_care_limiter"; | ||
1881 | bcare_ctl->attrs[0].attr.mode = S_IRUGO | S_IWUSR; | ||
1882 | bcare_ctl->attrs[0].show = sony_nc_battery_care_limit_show; | ||
1883 | bcare_ctl->attrs[0].store = sony_nc_battery_care_limit_store; | ||
1884 | |||
1885 | ret = device_create_file(&pd->dev, &bcare_ctl->attrs[0]); | ||
1886 | if (ret) | ||
1887 | goto outkzalloc; | ||
1888 | |||
1889 | /* 0x0115 is for models with no health reporting capability */ | ||
1890 | if (handle == 0x0115) | ||
1891 | return 0; | ||
1892 | |||
1893 | sysfs_attr_init(&bcare_ctl->attrs[1].attr); | ||
1894 | bcare_ctl->attrs[1].attr.name = "battery_care_health"; | ||
1895 | bcare_ctl->attrs[1].attr.mode = S_IRUGO; | ||
1896 | bcare_ctl->attrs[1].show = sony_nc_battery_care_health_show; | ||
1897 | |||
1898 | ret = device_create_file(&pd->dev, &bcare_ctl->attrs[1]); | ||
1899 | if (ret) | ||
1900 | goto outlimiter; | ||
1901 | |||
1902 | return 0; | ||
1903 | |||
1904 | outlimiter: | ||
1905 | device_remove_file(&pd->dev, &bcare_ctl->attrs[0]); | ||
1906 | |||
1907 | outkzalloc: | ||
1908 | kfree(bcare_ctl); | ||
1909 | bcare_ctl = NULL; | ||
1910 | |||
1911 | return ret; | ||
1912 | } | ||
1913 | |||
1914 | static void sony_nc_battery_care_cleanup(struct platform_device *pd) | ||
1915 | { | ||
1916 | if (bcare_ctl) { | ||
1917 | device_remove_file(&pd->dev, &bcare_ctl->attrs[0]); | ||
1918 | if (bcare_ctl->handle != 0x0115) | ||
1919 | device_remove_file(&pd->dev, &bcare_ctl->attrs[1]); | ||
1920 | |||
1921 | kfree(bcare_ctl); | ||
1922 | bcare_ctl = NULL; | ||
1923 | } | ||
1924 | } | ||
1925 | |||
1748 | static void sony_nc_backlight_ng_read_limits(int handle, | 1926 | static void sony_nc_backlight_ng_read_limits(int handle, |
1749 | struct sony_backlight_props *props) | 1927 | struct sony_backlight_props *props) |
1750 | { | 1928 | { |