2222#include <linux/io.h>
2323#include <linux/rfkill.h>
2424#include <linux/power_supply.h>
25+ #include <linux/sysfs.h>
2526#include <linux/acpi.h>
2627#include <linux/mm.h>
2728#include <linux/i8042.h>
2829#include <linux/debugfs.h>
2930#include <linux/seq_file.h>
31+ #include <acpi/battery.h>
3032#include <acpi/video.h>
3133#include "dell-rbtn.h"
3234#include "dell-smbios.h"
@@ -99,6 +101,20 @@ static bool force_rfkill;
99101static bool micmute_led_registered ;
100102static bool mute_led_registered ;
101103
104+ struct battery_mode_info {
105+ int token ;
106+ const char * label ;
107+ };
108+
109+ static const struct battery_mode_info battery_modes [] = {
110+ { BAT_PRI_AC_MODE_TOKEN , "Trickle" },
111+ { BAT_EXPRESS_MODE_TOKEN , "Fast" },
112+ { BAT_STANDARD_MODE_TOKEN , "Standard" },
113+ { BAT_ADAPTIVE_MODE_TOKEN , "Adaptive" },
114+ { BAT_CUSTOM_MODE_TOKEN , "Custom" },
115+ };
116+ static u32 battery_supported_modes ;
117+
102118module_param (force_rfkill , bool , 0444 );
103119MODULE_PARM_DESC (force_rfkill , "enable rfkill on non whitelisted models" );
104120
@@ -353,6 +369,32 @@ static const struct dmi_system_id dell_quirks[] __initconst = {
353369 { }
354370};
355371
372+ /* -1 is a sentinel value, telling us to use token->value */
373+ #define USE_TVAL ((u32) -1)
374+ static int dell_send_request_for_tokenid (struct calling_interface_buffer * buffer ,
375+ u16 class , u16 select , u16 tokenid ,
376+ u32 val )
377+ {
378+ struct calling_interface_token * token ;
379+
380+ token = dell_smbios_find_token (tokenid );
381+ if (!token )
382+ return - ENODEV ;
383+
384+ if (val == USE_TVAL )
385+ val = token -> value ;
386+
387+ dell_fill_request (buffer , token -> location , val , 0 , 0 );
388+ return dell_send_request (buffer , class , select );
389+ }
390+
391+ static inline int dell_set_std_token_value (struct calling_interface_buffer * buffer ,
392+ u16 tokenid , u32 value )
393+ {
394+ return dell_send_request_for_tokenid (buffer , CLASS_TOKEN_WRITE ,
395+ SELECT_TOKEN_STD , tokenid , value );
396+ }
397+
356398/*
357399 * Derived from information in smbios-wireless-ctl:
358400 *
@@ -2183,6 +2225,271 @@ static struct led_classdev mute_led_cdev = {
21832225 .default_trigger = "audio-mute" ,
21842226};
21852227
2228+ static int dell_battery_set_mode (const u16 tokenid )
2229+ {
2230+ struct calling_interface_buffer buffer ;
2231+
2232+ return dell_set_std_token_value (& buffer , tokenid , USE_TVAL );
2233+ }
2234+
2235+ static int dell_battery_read (const u16 tokenid )
2236+ {
2237+ struct calling_interface_buffer buffer ;
2238+ int err ;
2239+
2240+ err = dell_send_request_for_tokenid (& buffer , CLASS_TOKEN_READ ,
2241+ SELECT_TOKEN_STD , tokenid , 0 );
2242+ if (err )
2243+ return err ;
2244+
2245+ if (buffer .output [1 ] > INT_MAX )
2246+ return - EIO ;
2247+
2248+ return buffer .output [1 ];
2249+ }
2250+
2251+ static bool dell_battery_mode_is_active (const u16 tokenid )
2252+ {
2253+ struct calling_interface_token * token ;
2254+ int ret ;
2255+
2256+ ret = dell_battery_read (tokenid );
2257+ if (ret < 0 )
2258+ return false;
2259+
2260+ token = dell_smbios_find_token (tokenid );
2261+ /* token's already verified by dell_battery_read() */
2262+
2263+ return token -> value == (u16 ) ret ;
2264+ }
2265+
2266+ /*
2267+ * The rules: the minimum start charging value is 50%. The maximum
2268+ * start charging value is 95%. The minimum end charging value is
2269+ * 55%. The maximum end charging value is 100%. And finally, there
2270+ * has to be at least a 5% difference between start & end values.
2271+ */
2272+ #define CHARGE_START_MIN 50
2273+ #define CHARGE_START_MAX 95
2274+ #define CHARGE_END_MIN 55
2275+ #define CHARGE_END_MAX 100
2276+ #define CHARGE_MIN_DIFF 5
2277+
2278+ static int dell_battery_set_custom_charge_start (int start )
2279+ {
2280+ struct calling_interface_buffer buffer ;
2281+ int end ;
2282+
2283+ start = clamp (start , CHARGE_START_MIN , CHARGE_START_MAX );
2284+ end = dell_battery_read (BAT_CUSTOM_CHARGE_END );
2285+ if (end < 0 )
2286+ return end ;
2287+ if ((end - start ) < CHARGE_MIN_DIFF )
2288+ start = end - CHARGE_MIN_DIFF ;
2289+
2290+ return dell_set_std_token_value (& buffer , BAT_CUSTOM_CHARGE_START ,
2291+ start );
2292+ }
2293+
2294+ static int dell_battery_set_custom_charge_end (int end )
2295+ {
2296+ struct calling_interface_buffer buffer ;
2297+ int start ;
2298+
2299+ end = clamp (end , CHARGE_END_MIN , CHARGE_END_MAX );
2300+ start = dell_battery_read (BAT_CUSTOM_CHARGE_START );
2301+ if (start < 0 )
2302+ return start ;
2303+ if ((end - start ) < CHARGE_MIN_DIFF )
2304+ end = start + CHARGE_MIN_DIFF ;
2305+
2306+ return dell_set_std_token_value (& buffer , BAT_CUSTOM_CHARGE_END , end );
2307+ }
2308+
2309+ static ssize_t charge_types_show (struct device * dev ,
2310+ struct device_attribute * attr ,
2311+ char * buf )
2312+ {
2313+ ssize_t count = 0 ;
2314+ int i ;
2315+
2316+ for (i = 0 ; i < ARRAY_SIZE (battery_modes ); i ++ ) {
2317+ bool active ;
2318+
2319+ if (!(battery_supported_modes & BIT (i )))
2320+ continue ;
2321+
2322+ active = dell_battery_mode_is_active (battery_modes [i ].token );
2323+ count += sysfs_emit_at (buf , count , active ? "[%s] " : "%s " ,
2324+ battery_modes [i ].label );
2325+ }
2326+
2327+ /* convert the last space to a newline */
2328+ if (count > 0 )
2329+ count -- ;
2330+ count += sysfs_emit_at (buf , count , "\n" );
2331+
2332+ return count ;
2333+ }
2334+
2335+ static ssize_t charge_types_store (struct device * dev ,
2336+ struct device_attribute * attr ,
2337+ const char * buf , size_t size )
2338+ {
2339+ bool matched = false;
2340+ int err , i ;
2341+
2342+ for (i = 0 ; i < ARRAY_SIZE (battery_modes ); i ++ ) {
2343+ if (!(battery_supported_modes & BIT (i )))
2344+ continue ;
2345+
2346+ if (sysfs_streq (battery_modes [i ].label , buf )) {
2347+ matched = true;
2348+ break ;
2349+ }
2350+ }
2351+ if (!matched )
2352+ return - EINVAL ;
2353+
2354+ err = dell_battery_set_mode (battery_modes [i ].token );
2355+ if (err )
2356+ return err ;
2357+
2358+ return size ;
2359+ }
2360+
2361+ static ssize_t charge_control_start_threshold_show (struct device * dev ,
2362+ struct device_attribute * attr ,
2363+ char * buf )
2364+ {
2365+ int start ;
2366+
2367+ start = dell_battery_read (BAT_CUSTOM_CHARGE_START );
2368+ if (start < 0 )
2369+ return start ;
2370+
2371+ if (start > CHARGE_START_MAX )
2372+ return - EIO ;
2373+
2374+ return sysfs_emit (buf , "%d\n" , start );
2375+ }
2376+
2377+ static ssize_t charge_control_start_threshold_store (struct device * dev ,
2378+ struct device_attribute * attr ,
2379+ const char * buf , size_t size )
2380+ {
2381+ int ret , start ;
2382+
2383+ ret = kstrtoint (buf , 10 , & start );
2384+ if (ret )
2385+ return ret ;
2386+ if (start < 0 || start > 100 )
2387+ return - EINVAL ;
2388+
2389+ ret = dell_battery_set_custom_charge_start (start );
2390+ if (ret )
2391+ return ret ;
2392+
2393+ return size ;
2394+ }
2395+
2396+ static ssize_t charge_control_end_threshold_show (struct device * dev ,
2397+ struct device_attribute * attr ,
2398+ char * buf )
2399+ {
2400+ int end ;
2401+
2402+ end = dell_battery_read (BAT_CUSTOM_CHARGE_END );
2403+ if (end < 0 )
2404+ return end ;
2405+
2406+ if (end > CHARGE_END_MAX )
2407+ return - EIO ;
2408+
2409+ return sysfs_emit (buf , "%d\n" , end );
2410+ }
2411+
2412+ static ssize_t charge_control_end_threshold_store (struct device * dev ,
2413+ struct device_attribute * attr ,
2414+ const char * buf , size_t size )
2415+ {
2416+ int ret , end ;
2417+
2418+ ret = kstrtouint (buf , 10 , & end );
2419+ if (ret )
2420+ return ret ;
2421+ if (end < 0 || end > 100 )
2422+ return - EINVAL ;
2423+
2424+ ret = dell_battery_set_custom_charge_end (end );
2425+ if (ret )
2426+ return ret ;
2427+
2428+ return size ;
2429+ }
2430+
2431+ static DEVICE_ATTR_RW (charge_control_start_threshold );
2432+ static DEVICE_ATTR_RW (charge_control_end_threshold );
2433+ static DEVICE_ATTR_RW (charge_types );
2434+
2435+ static struct attribute * dell_battery_attrs [] = {
2436+ & dev_attr_charge_control_start_threshold .attr ,
2437+ & dev_attr_charge_control_end_threshold .attr ,
2438+ & dev_attr_charge_types .attr ,
2439+ NULL ,
2440+ };
2441+ ATTRIBUTE_GROUPS (dell_battery );
2442+
2443+ static int dell_battery_add (struct power_supply * battery ,
2444+ struct acpi_battery_hook * hook )
2445+ {
2446+ /* this currently only supports the primary battery */
2447+ if (strcmp (battery -> desc -> name , "BAT0" ) != 0 )
2448+ return - ENODEV ;
2449+
2450+ return device_add_groups (& battery -> dev , dell_battery_groups );
2451+ }
2452+
2453+ static int dell_battery_remove (struct power_supply * battery ,
2454+ struct acpi_battery_hook * hook )
2455+ {
2456+ device_remove_groups (& battery -> dev , dell_battery_groups );
2457+ return 0 ;
2458+ }
2459+
2460+ static struct acpi_battery_hook dell_battery_hook = {
2461+ .add_battery = dell_battery_add ,
2462+ .remove_battery = dell_battery_remove ,
2463+ .name = "Dell Primary Battery Extension" ,
2464+ };
2465+
2466+ static u32 __init battery_get_supported_modes (void )
2467+ {
2468+ u32 modes = 0 ;
2469+ int i ;
2470+
2471+ for (i = 0 ; i < ARRAY_SIZE (battery_modes ); i ++ ) {
2472+ if (dell_smbios_find_token (battery_modes [i ].token ))
2473+ modes |= BIT (i );
2474+ }
2475+
2476+ return modes ;
2477+ }
2478+
2479+ static void __init dell_battery_init (struct device * dev )
2480+ {
2481+ battery_supported_modes = battery_get_supported_modes ();
2482+
2483+ if (battery_supported_modes != 0 )
2484+ battery_hook_register (& dell_battery_hook );
2485+ }
2486+
2487+ static void dell_battery_exit (void )
2488+ {
2489+ if (battery_supported_modes != 0 )
2490+ battery_hook_unregister (& dell_battery_hook );
2491+ }
2492+
21862493static int __init dell_init (void )
21872494{
21882495 struct calling_interface_token * token ;
@@ -2219,6 +2526,7 @@ static int __init dell_init(void)
22192526 touchpad_led_init (& platform_device -> dev );
22202527
22212528 kbd_led_init (& platform_device -> dev );
2529+ dell_battery_init (& platform_device -> dev );
22222530
22232531 dell_laptop_dir = debugfs_create_dir ("dell_laptop" , NULL );
22242532 debugfs_create_file ("rfkill" , 0444 , dell_laptop_dir , NULL ,
@@ -2293,6 +2601,7 @@ static int __init dell_init(void)
22932601 if (mute_led_registered )
22942602 led_classdev_unregister (& mute_led_cdev );
22952603fail_led :
2604+ dell_battery_exit ();
22962605 dell_cleanup_rfkill ();
22972606fail_rfkill :
22982607 platform_device_del (platform_device );
@@ -2311,6 +2620,7 @@ static void __exit dell_exit(void)
23112620 if (quirks && quirks -> touchpad_led )
23122621 touchpad_led_exit ();
23132622 kbd_led_exit ();
2623+ dell_battery_exit ();
23142624 backlight_device_unregister (dell_backlight_device );
23152625 if (micmute_led_registered )
23162626 led_classdev_unregister (& micmute_led_cdev );
0 commit comments