xref: /openbmc/linux/drivers/platform/x86/compal-laptop.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
116216333SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
241b16dceSLen Brown /*-*-linux-c-*-*/
341b16dceSLen Brown 
441b16dceSLen Brown /*
541b16dceSLen Brown   Copyright (C) 2008 Cezary Jackiewicz <cezary.jackiewicz (at) gmail.com>
641b16dceSLen Brown 
741b16dceSLen Brown   based on MSI driver
841b16dceSLen Brown 
941b16dceSLen Brown   Copyright (C) 2006 Lennart Poettering <mzxreary (at) 0pointer (dot) de>
1041b16dceSLen Brown 
1141b16dceSLen Brown  */
1241b16dceSLen Brown 
1341b16dceSLen Brown /*
149be0fcb5SRoald Frederickx  * compal-laptop.c - Compal laptop support.
1541b16dceSLen Brown  *
169be0fcb5SRoald Frederickx  * This driver exports a few files in /sys/devices/platform/compal-laptop/:
179be0fcb5SRoald Frederickx  *   wake_up_XXX   Whether or not we listen to such wake up events (rw)
189be0fcb5SRoald Frederickx  *
199be0fcb5SRoald Frederickx  * In addition to these platform device attributes the driver
209be0fcb5SRoald Frederickx  * registers itself in the Linux backlight control, power_supply, rfkill
219be0fcb5SRoald Frederickx  * and hwmon subsystem and is available to userspace under:
229be0fcb5SRoald Frederickx  *
239be0fcb5SRoald Frederickx  *   /sys/class/backlight/compal-laptop/
249be0fcb5SRoald Frederickx  *   /sys/class/power_supply/compal-laptop/
259be0fcb5SRoald Frederickx  *   /sys/class/rfkill/rfkillX/
269be0fcb5SRoald Frederickx  *   /sys/class/hwmon/hwmonX/
279be0fcb5SRoald Frederickx  *
289be0fcb5SRoald Frederickx  * Notes on the power_supply battery interface:
299be0fcb5SRoald Frederickx  *   - the "minimum" design voltage is *the* design voltage
309be0fcb5SRoald Frederickx  *   - the ambient temperature is the average battery temperature
319be0fcb5SRoald Frederickx  *     and the value is an educated guess (see commented code below)
329be0fcb5SRoald Frederickx  *
3341b16dceSLen Brown  *
3441b16dceSLen Brown  * This driver might work on other laptops produced by Compal. If you
3541b16dceSLen Brown  * want to try it you can pass force=1 as argument to the module which
3641b16dceSLen Brown  * will force it to load even when the DMI data doesn't identify the
379be0fcb5SRoald Frederickx  * laptop as compatible.
389be0fcb5SRoald Frederickx  *
399be0fcb5SRoald Frederickx  * Lots of data available at:
409be0fcb5SRoald Frederickx  * http://service1.marasst.com/Compal/JHL90_91/Service%20Manual/
419be0fcb5SRoald Frederickx  * JHL90%20service%20manual-Final-0725.pdf
429be0fcb5SRoald Frederickx  *
439be0fcb5SRoald Frederickx  *
449be0fcb5SRoald Frederickx  *
459be0fcb5SRoald Frederickx  * Support for the Compal JHL90 added by Roald Frederickx
469be0fcb5SRoald Frederickx  * (roald.frederickx@gmail.com):
479be0fcb5SRoald Frederickx  * Driver got large revision. Added functionalities: backlight
489be0fcb5SRoald Frederickx  * power, wake_on_XXX, a hwmon and power_supply interface.
499be0fcb5SRoald Frederickx  *
509be0fcb5SRoald Frederickx  * In case this gets merged into the kernel source: I want to dedicate this
519be0fcb5SRoald Frederickx  * to Kasper Meerts, the awesome guy who showed me Linux and C!
5241b16dceSLen Brown  */
5341b16dceSLen Brown 
549be0fcb5SRoald Frederickx /* NOTE: currently the wake_on_XXX, hwmon and power_supply interfaces are
559be0fcb5SRoald Frederickx  * only enabled on a JHL90 board until it is verified that they work on the
569be0fcb5SRoald Frederickx  * other boards too.  See the extra_features variable. */
579be0fcb5SRoald Frederickx 
58b4a4bc0bSJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
59b4a4bc0bSJoe Perches 
6041b16dceSLen Brown #include <linux/module.h>
6141b16dceSLen Brown #include <linux/kernel.h>
6241b16dceSLen Brown #include <linux/init.h>
6341b16dceSLen Brown #include <linux/acpi.h>
6441b16dceSLen Brown #include <linux/dmi.h>
6541b16dceSLen Brown #include <linux/backlight.h>
6641b16dceSLen Brown #include <linux/platform_device.h>
67493e9143SMario Limonciello #include <linux/rfkill.h>
689be0fcb5SRoald Frederickx #include <linux/hwmon.h>
699be0fcb5SRoald Frederickx #include <linux/hwmon-sysfs.h>
709be0fcb5SRoald Frederickx #include <linux/power_supply.h>
719be0fcb5SRoald Frederickx #include <linux/fb.h>
72358fefe7SHans de Goede #include <acpi/video.h>
7341b16dceSLen Brown 
749be0fcb5SRoald Frederickx /* ======= */
759be0fcb5SRoald Frederickx /* Defines */
769be0fcb5SRoald Frederickx /* ======= */
779be0fcb5SRoald Frederickx #define DRIVER_NAME "compal-laptop"
789be0fcb5SRoald Frederickx #define DRIVER_VERSION	"0.2.7"
7941b16dceSLen Brown 
809be0fcb5SRoald Frederickx #define BACKLIGHT_LEVEL_ADDR		0xB9
819be0fcb5SRoald Frederickx #define BACKLIGHT_LEVEL_MAX		7
829be0fcb5SRoald Frederickx #define BACKLIGHT_STATE_ADDR		0x59
839be0fcb5SRoald Frederickx #define BACKLIGHT_STATE_ON_DATA		0xE1
849be0fcb5SRoald Frederickx #define BACKLIGHT_STATE_OFF_DATA	0xE2
8541b16dceSLen Brown 
869be0fcb5SRoald Frederickx #define WAKE_UP_ADDR			0xA4
879be0fcb5SRoald Frederickx #define WAKE_UP_PME			(1 << 0)
889be0fcb5SRoald Frederickx #define WAKE_UP_MODEM			(1 << 1)
899be0fcb5SRoald Frederickx #define WAKE_UP_LAN			(1 << 2)
909be0fcb5SRoald Frederickx #define WAKE_UP_WLAN			(1 << 4)
919be0fcb5SRoald Frederickx #define WAKE_UP_KEY			(1 << 6)
929be0fcb5SRoald Frederickx #define WAKE_UP_MOUSE			(1 << 7)
9341b16dceSLen Brown 
949be0fcb5SRoald Frederickx #define WIRELESS_ADDR			0xBB
959be0fcb5SRoald Frederickx #define WIRELESS_WLAN			(1 << 0)
969be0fcb5SRoald Frederickx #define WIRELESS_BT			(1 << 1)
979be0fcb5SRoald Frederickx #define WIRELESS_WLAN_EXISTS		(1 << 2)
989be0fcb5SRoald Frederickx #define WIRELESS_BT_EXISTS		(1 << 3)
999be0fcb5SRoald Frederickx #define WIRELESS_KILLSWITCH		(1 << 4)
100493e9143SMario Limonciello 
1019be0fcb5SRoald Frederickx #define PWM_ADDRESS			0x46
1029be0fcb5SRoald Frederickx #define PWM_DISABLE_ADDR		0x59
1039be0fcb5SRoald Frederickx #define PWM_DISABLE_DATA		0xA5
1049be0fcb5SRoald Frederickx #define PWM_ENABLE_ADDR			0x59
1059be0fcb5SRoald Frederickx #define PWM_ENABLE_DATA			0xA8
1069be0fcb5SRoald Frederickx 
1079be0fcb5SRoald Frederickx #define FAN_ADDRESS			0x46
1089be0fcb5SRoald Frederickx #define FAN_DATA			0x81
1099be0fcb5SRoald Frederickx #define FAN_FULL_ON_CMD			0x59 /* Doesn't seem to work. Just */
1109be0fcb5SRoald Frederickx #define FAN_FULL_ON_ENABLE		0x76 /* force the pwm signal to its */
1119be0fcb5SRoald Frederickx #define FAN_FULL_ON_DISABLE		0x77 /* maximum value instead */
1129be0fcb5SRoald Frederickx 
1139be0fcb5SRoald Frederickx #define TEMP_CPU			0xB0
1149be0fcb5SRoald Frederickx #define TEMP_CPU_LOCAL			0xB1
1159be0fcb5SRoald Frederickx #define TEMP_CPU_DTS			0xB5
1169be0fcb5SRoald Frederickx #define TEMP_NORTHBRIDGE		0xB6
1179be0fcb5SRoald Frederickx #define TEMP_VGA			0xB4
1189be0fcb5SRoald Frederickx #define TEMP_SKIN			0xB2
1199be0fcb5SRoald Frederickx 
1209be0fcb5SRoald Frederickx #define BAT_MANUFACTURER_NAME_ADDR	0x10
1219be0fcb5SRoald Frederickx #define BAT_MANUFACTURER_NAME_LEN	9
1229be0fcb5SRoald Frederickx #define BAT_MODEL_NAME_ADDR		0x19
1239be0fcb5SRoald Frederickx #define BAT_MODEL_NAME_LEN		6
1249be0fcb5SRoald Frederickx #define BAT_SERIAL_NUMBER_ADDR		0xC4
1259be0fcb5SRoald Frederickx #define BAT_SERIAL_NUMBER_LEN		5
1269be0fcb5SRoald Frederickx #define BAT_CHARGE_NOW			0xC2
1279be0fcb5SRoald Frederickx #define BAT_CHARGE_DESIGN		0xCA
1289be0fcb5SRoald Frederickx #define BAT_VOLTAGE_NOW			0xC6
1299be0fcb5SRoald Frederickx #define BAT_VOLTAGE_DESIGN		0xC8
1309be0fcb5SRoald Frederickx #define BAT_CURRENT_NOW			0xD0
1319be0fcb5SRoald Frederickx #define BAT_CURRENT_AVG			0xD2
1329be0fcb5SRoald Frederickx #define BAT_POWER			0xD4
1339be0fcb5SRoald Frederickx #define BAT_CAPACITY			0xCE
1349be0fcb5SRoald Frederickx #define BAT_TEMP			0xD6
1359be0fcb5SRoald Frederickx #define BAT_TEMP_AVG			0xD7
1369be0fcb5SRoald Frederickx #define BAT_STATUS0			0xC1
1379be0fcb5SRoald Frederickx #define BAT_STATUS1			0xF0
1389be0fcb5SRoald Frederickx #define BAT_STATUS2			0xF1
1399be0fcb5SRoald Frederickx #define BAT_STOP_CHARGE1		0xF2
1409be0fcb5SRoald Frederickx #define BAT_STOP_CHARGE2		0xF3
141d9a427ecSRoald Frederickx #define BAT_CHARGE_LIMIT		0x03
142d9a427ecSRoald Frederickx #define BAT_CHARGE_LIMIT_MAX		100
1439be0fcb5SRoald Frederickx 
1449be0fcb5SRoald Frederickx #define BAT_S0_DISCHARGE		(1 << 0)
1459be0fcb5SRoald Frederickx #define BAT_S0_DISCHRG_CRITICAL		(1 << 2)
1469be0fcb5SRoald Frederickx #define BAT_S0_LOW			(1 << 3)
1479be0fcb5SRoald Frederickx #define BAT_S0_CHARGING			(1 << 1)
1489be0fcb5SRoald Frederickx #define BAT_S0_AC			(1 << 7)
1499be0fcb5SRoald Frederickx #define BAT_S1_EXISTS			(1 << 0)
1509be0fcb5SRoald Frederickx #define BAT_S1_FULL			(1 << 1)
1519be0fcb5SRoald Frederickx #define BAT_S1_EMPTY			(1 << 2)
1529be0fcb5SRoald Frederickx #define BAT_S1_LiION_OR_NiMH		(1 << 7)
1539be0fcb5SRoald Frederickx #define BAT_S2_LOW_LOW			(1 << 0)
1549be0fcb5SRoald Frederickx #define BAT_STOP_CHRG1_BAD_CELL		(1 << 1)
1559be0fcb5SRoald Frederickx #define BAT_STOP_CHRG1_COMM_FAIL	(1 << 2)
1569be0fcb5SRoald Frederickx #define BAT_STOP_CHRG1_OVERVOLTAGE	(1 << 6)
1579be0fcb5SRoald Frederickx #define BAT_STOP_CHRG1_OVERTEMPERATURE	(1 << 7)
1589be0fcb5SRoald Frederickx 
1599be0fcb5SRoald Frederickx 
1609be0fcb5SRoald Frederickx /* ======= */
1619be0fcb5SRoald Frederickx /* Structs */
1629be0fcb5SRoald Frederickx /* ======= */
1639be0fcb5SRoald Frederickx struct compal_data{
1649be0fcb5SRoald Frederickx 	/* Fan control */
165c2be45f0SGuenter Roeck 	int pwm_enable; /* 0:full on, 1:set by pwm1, 2:control by motherboard */
1669be0fcb5SRoald Frederickx 	unsigned char curr_pwm;
1679be0fcb5SRoald Frederickx 
1689be0fcb5SRoald Frederickx 	/* Power supply */
169297d716fSKrzysztof Kozlowski 	struct power_supply *psy;
1709be0fcb5SRoald Frederickx 	struct power_supply_info psy_info;
1719be0fcb5SRoald Frederickx 	char bat_model_name[BAT_MODEL_NAME_LEN + 1];
1729be0fcb5SRoald Frederickx 	char bat_manufacturer_name[BAT_MANUFACTURER_NAME_LEN + 1];
1739be0fcb5SRoald Frederickx 	char bat_serial_number[BAT_SERIAL_NUMBER_LEN + 1];
1749be0fcb5SRoald Frederickx };
1759be0fcb5SRoald Frederickx 
1769be0fcb5SRoald Frederickx 
1779be0fcb5SRoald Frederickx /* =============== */
1789be0fcb5SRoald Frederickx /* General globals */
1799be0fcb5SRoald Frederickx /* =============== */
18090ab5ee9SRusty Russell static bool force;
18141b16dceSLen Brown module_param(force, bool, 0);
18241b16dceSLen Brown MODULE_PARM_DESC(force, "Force driver load, ignore DMI data");
18341b16dceSLen Brown 
1849be0fcb5SRoald Frederickx /* Support for the wake_on_XXX, hwmon and power_supply interface. Currently
1859be0fcb5SRoald Frederickx  * only gets enabled on a JHL90 board. Might work with the others too */
1869be0fcb5SRoald Frederickx static bool extra_features;
18741b16dceSLen Brown 
1889be0fcb5SRoald Frederickx /* Nasty stuff. For some reason the fan control is very un-linear.  I've
1899be0fcb5SRoald Frederickx  * come up with these values by looping through the possible inputs and
1909be0fcb5SRoald Frederickx  * watching the output of address 0x4F (do an ec_transaction writing 0x33
1919be0fcb5SRoald Frederickx  * into 0x4F and read a few bytes from the output, like so:
1929be0fcb5SRoald Frederickx  *	u8 writeData = 0x33;
1931cb7b1e0SThomas Renninger  *	ec_transaction(0x4F, &writeData, 1, buffer, 32);
194751516f0SLen Brown  * That address is labeled "fan1 table information" in the service manual.
1959be0fcb5SRoald Frederickx  * It should be clear which value in 'buffer' changes). This seems to be
1969be0fcb5SRoald Frederickx  * related to fan speed. It isn't a proper 'realtime' fan speed value
1979be0fcb5SRoald Frederickx  * though, because physically stopping or speeding up the fan doesn't
1989be0fcb5SRoald Frederickx  * change it. It might be the average voltage or current of the pwm output.
1999be0fcb5SRoald Frederickx  * Nevertheless, it is more fine-grained than the actual RPM reading */
2009be0fcb5SRoald Frederickx static const unsigned char pwm_lookup_table[256] = {
2019be0fcb5SRoald Frederickx 	0, 0, 0, 1, 1, 1, 2, 253, 254, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6,
2029be0fcb5SRoald Frederickx 	7, 7, 7, 8, 86, 86, 9, 9, 9, 10, 10, 10, 11, 92, 92, 12, 12, 95,
2039be0fcb5SRoald Frederickx 	13, 66, 66, 14, 14, 98, 15, 15, 15, 16, 16, 67, 17, 17, 72, 18, 70,
2049be0fcb5SRoald Frederickx 	75, 19, 90, 90, 73, 73, 73, 21, 21, 91, 91, 91, 96, 23, 94, 94, 94,
2059be0fcb5SRoald Frederickx 	94, 94, 94, 94, 94, 94, 94, 141, 141, 238, 223, 192, 139, 139, 139,
2069be0fcb5SRoald Frederickx 	139, 139, 142, 142, 142, 142, 142, 78, 78, 78, 78, 78, 76, 76, 76,
2079be0fcb5SRoald Frederickx 	76, 76, 79, 79, 79, 79, 79, 79, 79, 20, 20, 20, 20, 20, 22, 22, 22,
2089be0fcb5SRoald Frederickx 	22, 22, 24, 24, 24, 24, 24, 24, 219, 219, 219, 219, 219, 219, 219,
2099be0fcb5SRoald Frederickx 	219, 27, 27, 188, 188, 28, 28, 28, 29, 186, 186, 186, 186, 186,
2109be0fcb5SRoald Frederickx 	186, 186, 186, 186, 186, 31, 31, 31, 31, 31, 32, 32, 32, 41, 33,
2119be0fcb5SRoald Frederickx 	33, 33, 33, 33, 252, 252, 34, 34, 34, 43, 35, 35, 35, 36, 36, 38,
2129be0fcb5SRoald Frederickx 	206, 206, 206, 206, 206, 206, 206, 206, 206, 37, 37, 37, 46, 46,
2139be0fcb5SRoald Frederickx 	47, 47, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 48, 48,
2149be0fcb5SRoald Frederickx 	48, 48, 48, 40, 40, 40, 49, 42, 42, 42, 42, 42, 42, 42, 42, 44,
2159be0fcb5SRoald Frederickx 	189, 189, 189, 189, 54, 54, 45, 45, 45, 45, 45, 45, 45, 45, 251,
2169be0fcb5SRoald Frederickx 	191, 199, 199, 199, 199, 199, 215, 215, 215, 215, 187, 187, 187,
2179be0fcb5SRoald Frederickx 	187, 187, 193, 50
2189be0fcb5SRoald Frederickx };
2199be0fcb5SRoald Frederickx 
2209be0fcb5SRoald Frederickx 
2219be0fcb5SRoald Frederickx 
2229be0fcb5SRoald Frederickx 
2239be0fcb5SRoald Frederickx /* ========================= */
2249be0fcb5SRoald Frederickx /* Hardware access functions */
2259be0fcb5SRoald Frederickx /* ========================= */
2269be0fcb5SRoald Frederickx /* General access */
ec_read_u8(u8 addr)2279be0fcb5SRoald Frederickx static u8 ec_read_u8(u8 addr)
22841b16dceSLen Brown {
22909e7f280SYizhuo 	u8 value = 0;
2309be0fcb5SRoald Frederickx 	ec_read(addr, &value);
2319be0fcb5SRoald Frederickx 	return value;
2329be0fcb5SRoald Frederickx }
2339be0fcb5SRoald Frederickx 
ec_read_s8(u8 addr)2349be0fcb5SRoald Frederickx static s8 ec_read_s8(u8 addr)
2359be0fcb5SRoald Frederickx {
2369be0fcb5SRoald Frederickx 	return (s8)ec_read_u8(addr);
2379be0fcb5SRoald Frederickx }
2389be0fcb5SRoald Frederickx 
ec_read_u16(u8 addr)2399be0fcb5SRoald Frederickx static u16 ec_read_u16(u8 addr)
2409be0fcb5SRoald Frederickx {
2419be0fcb5SRoald Frederickx 	int hi, lo;
2429be0fcb5SRoald Frederickx 	lo = ec_read_u8(addr);
2439be0fcb5SRoald Frederickx 	hi = ec_read_u8(addr + 1);
2449be0fcb5SRoald Frederickx 	return (hi << 8) + lo;
2459be0fcb5SRoald Frederickx }
2469be0fcb5SRoald Frederickx 
ec_read_s16(u8 addr)2479be0fcb5SRoald Frederickx static s16 ec_read_s16(u8 addr)
2489be0fcb5SRoald Frederickx {
2499be0fcb5SRoald Frederickx 	return (s16) ec_read_u16(addr);
2509be0fcb5SRoald Frederickx }
2519be0fcb5SRoald Frederickx 
ec_read_sequence(u8 addr,u8 * buf,int len)2529be0fcb5SRoald Frederickx static void ec_read_sequence(u8 addr, u8 *buf, int len)
2539be0fcb5SRoald Frederickx {
2549be0fcb5SRoald Frederickx 	int i;
2559be0fcb5SRoald Frederickx 	for (i = 0; i < len; i++)
2569be0fcb5SRoald Frederickx 		ec_read(addr + i, buf + i);
2579be0fcb5SRoald Frederickx }
2589be0fcb5SRoald Frederickx 
2599be0fcb5SRoald Frederickx 
2609be0fcb5SRoald Frederickx /* Backlight access */
set_backlight_level(int level)2619be0fcb5SRoald Frederickx static int set_backlight_level(int level)
2629be0fcb5SRoald Frederickx {
2639be0fcb5SRoald Frederickx 	if (level < 0 || level > BACKLIGHT_LEVEL_MAX)
26441b16dceSLen Brown 		return -EINVAL;
26541b16dceSLen Brown 
2669be0fcb5SRoald Frederickx 	ec_write(BACKLIGHT_LEVEL_ADDR, level);
26741b16dceSLen Brown 
2680e4510f7SAxel Lin 	return 0;
26941b16dceSLen Brown }
27041b16dceSLen Brown 
get_backlight_level(void)2719be0fcb5SRoald Frederickx static int get_backlight_level(void)
27241b16dceSLen Brown {
2739be0fcb5SRoald Frederickx 	return (int) ec_read_u8(BACKLIGHT_LEVEL_ADDR);
27441b16dceSLen Brown }
27541b16dceSLen Brown 
set_backlight_state(bool on)2769be0fcb5SRoald Frederickx static void set_backlight_state(bool on)
2779be0fcb5SRoald Frederickx {
2789be0fcb5SRoald Frederickx 	u8 data = on ? BACKLIGHT_STATE_ON_DATA : BACKLIGHT_STATE_OFF_DATA;
2791cb7b1e0SThomas Renninger 	ec_transaction(BACKLIGHT_STATE_ADDR, &data, 1, NULL, 0);
2809be0fcb5SRoald Frederickx }
2819be0fcb5SRoald Frederickx 
2829be0fcb5SRoald Frederickx 
2839be0fcb5SRoald Frederickx /* Fan control access */
pwm_enable_control(void)2849be0fcb5SRoald Frederickx static void pwm_enable_control(void)
2859be0fcb5SRoald Frederickx {
2869be0fcb5SRoald Frederickx 	unsigned char writeData = PWM_ENABLE_DATA;
2871cb7b1e0SThomas Renninger 	ec_transaction(PWM_ENABLE_ADDR, &writeData, 1, NULL, 0);
2889be0fcb5SRoald Frederickx }
2899be0fcb5SRoald Frederickx 
pwm_disable_control(void)2909be0fcb5SRoald Frederickx static void pwm_disable_control(void)
2919be0fcb5SRoald Frederickx {
2929be0fcb5SRoald Frederickx 	unsigned char writeData = PWM_DISABLE_DATA;
2931cb7b1e0SThomas Renninger 	ec_transaction(PWM_DISABLE_ADDR, &writeData, 1, NULL, 0);
2949be0fcb5SRoald Frederickx }
2959be0fcb5SRoald Frederickx 
set_pwm(int pwm)2969be0fcb5SRoald Frederickx static void set_pwm(int pwm)
2979be0fcb5SRoald Frederickx {
2981cb7b1e0SThomas Renninger 	ec_transaction(PWM_ADDRESS, &pwm_lookup_table[pwm], 1, NULL, 0);
2999be0fcb5SRoald Frederickx }
3009be0fcb5SRoald Frederickx 
get_fan_rpm(void)3019be0fcb5SRoald Frederickx static int get_fan_rpm(void)
3029be0fcb5SRoald Frederickx {
3039be0fcb5SRoald Frederickx 	u8 value, data = FAN_DATA;
3041cb7b1e0SThomas Renninger 	ec_transaction(FAN_ADDRESS, &data, 1, &value, 1);
3059be0fcb5SRoald Frederickx 	return 100 * (int)value;
3069be0fcb5SRoald Frederickx }
3079be0fcb5SRoald Frederickx 
3089be0fcb5SRoald Frederickx 
3099be0fcb5SRoald Frederickx 
3109be0fcb5SRoald Frederickx 
3119be0fcb5SRoald Frederickx /* =================== */
3129be0fcb5SRoald Frederickx /* Interface functions */
3139be0fcb5SRoald Frederickx /* =================== */
3149be0fcb5SRoald Frederickx 
3159be0fcb5SRoald Frederickx /* Backlight interface */
bl_get_brightness(struct backlight_device * b)3169be0fcb5SRoald Frederickx static int bl_get_brightness(struct backlight_device *b)
3179be0fcb5SRoald Frederickx {
3189be0fcb5SRoald Frederickx 	return get_backlight_level();
3199be0fcb5SRoald Frederickx }
3209be0fcb5SRoald Frederickx 
bl_update_status(struct backlight_device * b)3219be0fcb5SRoald Frederickx static int bl_update_status(struct backlight_device *b)
3229be0fcb5SRoald Frederickx {
3239be0fcb5SRoald Frederickx 	int ret = set_backlight_level(b->props.brightness);
3249be0fcb5SRoald Frederickx 	if (ret)
3259be0fcb5SRoald Frederickx 		return ret;
3269be0fcb5SRoald Frederickx 
327537c7933SStephen Kitt 	set_backlight_state(!backlight_is_blank(b));
3289be0fcb5SRoald Frederickx 	return 0;
3299be0fcb5SRoald Frederickx }
3309be0fcb5SRoald Frederickx 
3319be0fcb5SRoald Frederickx static const struct backlight_ops compalbl_ops = {
3329be0fcb5SRoald Frederickx 	.get_brightness = bl_get_brightness,
3339be0fcb5SRoald Frederickx 	.update_status	= bl_update_status,
3349be0fcb5SRoald Frederickx };
3359be0fcb5SRoald Frederickx 
3369be0fcb5SRoald Frederickx 
3379be0fcb5SRoald Frederickx /* Wireless interface */
compal_rfkill_set(void * data,bool blocked)338493e9143SMario Limonciello static int compal_rfkill_set(void *data, bool blocked)
33941b16dceSLen Brown {
340493e9143SMario Limonciello 	unsigned long radio = (unsigned long) data;
3419be0fcb5SRoald Frederickx 	u8 result = ec_read_u8(WIRELESS_ADDR);
3429be0fcb5SRoald Frederickx 	u8 value;
34341b16dceSLen Brown 
344493e9143SMario Limonciello 	if (!blocked)
345493e9143SMario Limonciello 		value = (u8) (result | radio);
34641b16dceSLen Brown 	else
347493e9143SMario Limonciello 		value = (u8) (result & ~radio);
3489be0fcb5SRoald Frederickx 	ec_write(WIRELESS_ADDR, value);
34941b16dceSLen Brown 
35041b16dceSLen Brown 	return 0;
35141b16dceSLen Brown }
35241b16dceSLen Brown 
compal_rfkill_poll(struct rfkill * rfkill,void * data)353493e9143SMario Limonciello static void compal_rfkill_poll(struct rfkill *rfkill, void *data)
35441b16dceSLen Brown {
3559be0fcb5SRoald Frederickx 	u8 result = ec_read_u8(WIRELESS_ADDR);
3569be0fcb5SRoald Frederickx 	bool hw_blocked = !(result & WIRELESS_KILLSWITCH);
357493e9143SMario Limonciello 	rfkill_set_hw_state(rfkill, hw_blocked);
35841b16dceSLen Brown }
35941b16dceSLen Brown 
360493e9143SMario Limonciello static const struct rfkill_ops compal_rfkill_ops = {
361493e9143SMario Limonciello 	.poll = compal_rfkill_poll,
362493e9143SMario Limonciello 	.set_block = compal_rfkill_set,
363493e9143SMario Limonciello };
364493e9143SMario Limonciello 
365493e9143SMario Limonciello 
3669be0fcb5SRoald Frederickx /* Wake_up interface */
3679be0fcb5SRoald Frederickx #define SIMPLE_MASKED_STORE_SHOW(NAME, ADDR, MASK)			\
3689be0fcb5SRoald Frederickx static ssize_t NAME##_show(struct device *dev,				\
3699be0fcb5SRoald Frederickx 	struct device_attribute *attr, char *buf)			\
3709be0fcb5SRoald Frederickx {									\
3719be0fcb5SRoald Frederickx 	return sprintf(buf, "%d\n", ((ec_read_u8(ADDR) & MASK) != 0));	\
3729be0fcb5SRoald Frederickx }									\
3739be0fcb5SRoald Frederickx static ssize_t NAME##_store(struct device *dev,				\
3749be0fcb5SRoald Frederickx 	struct device_attribute *attr, const char *buf, size_t count)	\
3759be0fcb5SRoald Frederickx {									\
3769be0fcb5SRoald Frederickx 	int state;							\
3779be0fcb5SRoald Frederickx 	u8 old_val = ec_read_u8(ADDR);					\
3789be0fcb5SRoald Frederickx 	if (sscanf(buf, "%d", &state) != 1 || (state < 0 || state > 1))	\
3799be0fcb5SRoald Frederickx 		return -EINVAL;						\
3809be0fcb5SRoald Frederickx 	ec_write(ADDR, state ? (old_val | MASK) : (old_val & ~MASK));	\
3819be0fcb5SRoald Frederickx 	return count;							\
38241b16dceSLen Brown }
38341b16dceSLen Brown 
SIMPLE_MASKED_STORE_SHOW(wake_up_pme,WAKE_UP_ADDR,WAKE_UP_PME)3849be0fcb5SRoald Frederickx SIMPLE_MASKED_STORE_SHOW(wake_up_pme,	WAKE_UP_ADDR, WAKE_UP_PME)
3859be0fcb5SRoald Frederickx SIMPLE_MASKED_STORE_SHOW(wake_up_modem,	WAKE_UP_ADDR, WAKE_UP_MODEM)
3869be0fcb5SRoald Frederickx SIMPLE_MASKED_STORE_SHOW(wake_up_lan,	WAKE_UP_ADDR, WAKE_UP_LAN)
3879be0fcb5SRoald Frederickx SIMPLE_MASKED_STORE_SHOW(wake_up_wlan,	WAKE_UP_ADDR, WAKE_UP_WLAN)
3889be0fcb5SRoald Frederickx SIMPLE_MASKED_STORE_SHOW(wake_up_key,	WAKE_UP_ADDR, WAKE_UP_KEY)
3899be0fcb5SRoald Frederickx SIMPLE_MASKED_STORE_SHOW(wake_up_mouse,	WAKE_UP_ADDR, WAKE_UP_MOUSE)
3909be0fcb5SRoald Frederickx 
3919be0fcb5SRoald Frederickx /* Fan control interface */
3929be0fcb5SRoald Frederickx static ssize_t pwm_enable_show(struct device *dev,
3939be0fcb5SRoald Frederickx 		struct device_attribute *attr, char *buf)
3949be0fcb5SRoald Frederickx {
3959be0fcb5SRoald Frederickx 	struct compal_data *data = dev_get_drvdata(dev);
3969be0fcb5SRoald Frederickx 	return sprintf(buf, "%d\n", data->pwm_enable);
3979be0fcb5SRoald Frederickx }
3989be0fcb5SRoald Frederickx 
pwm_enable_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)3999be0fcb5SRoald Frederickx static ssize_t pwm_enable_store(struct device *dev,
4009be0fcb5SRoald Frederickx 		struct device_attribute *attr, const char *buf, size_t count)
4019be0fcb5SRoald Frederickx {
4029be0fcb5SRoald Frederickx 	struct compal_data *data = dev_get_drvdata(dev);
4039be0fcb5SRoald Frederickx 	long val;
4049be0fcb5SRoald Frederickx 	int err;
4050db7fd96SJingoo Han 
4060db7fd96SJingoo Han 	err = kstrtol(buf, 10, &val);
4079be0fcb5SRoald Frederickx 	if (err)
4089be0fcb5SRoald Frederickx 		return err;
4099be0fcb5SRoald Frederickx 	if (val < 0)
4109be0fcb5SRoald Frederickx 		return -EINVAL;
4119be0fcb5SRoald Frederickx 
4129be0fcb5SRoald Frederickx 	data->pwm_enable = val;
4139be0fcb5SRoald Frederickx 
4149be0fcb5SRoald Frederickx 	switch (val) {
4159be0fcb5SRoald Frederickx 	case 0:  /* Full speed */
4169be0fcb5SRoald Frederickx 		pwm_enable_control();
4179be0fcb5SRoald Frederickx 		set_pwm(255);
4189be0fcb5SRoald Frederickx 		break;
4199be0fcb5SRoald Frederickx 	case 1:  /* As set by pwm1 */
4209be0fcb5SRoald Frederickx 		pwm_enable_control();
4219be0fcb5SRoald Frederickx 		set_pwm(data->curr_pwm);
4229be0fcb5SRoald Frederickx 		break;
4239be0fcb5SRoald Frederickx 	default: /* Control by motherboard */
4249be0fcb5SRoald Frederickx 		pwm_disable_control();
4259be0fcb5SRoald Frederickx 		break;
4269be0fcb5SRoald Frederickx 	}
4279be0fcb5SRoald Frederickx 
4289be0fcb5SRoald Frederickx 	return count;
4299be0fcb5SRoald Frederickx }
4309be0fcb5SRoald Frederickx 
pwm_show(struct device * dev,struct device_attribute * attr,char * buf)4319be0fcb5SRoald Frederickx static ssize_t pwm_show(struct device *dev, struct device_attribute *attr,
4329be0fcb5SRoald Frederickx 		char *buf)
4339be0fcb5SRoald Frederickx {
4349be0fcb5SRoald Frederickx 	struct compal_data *data = dev_get_drvdata(dev);
4359be0fcb5SRoald Frederickx 	return sprintf(buf, "%hhu\n", data->curr_pwm);
4369be0fcb5SRoald Frederickx }
4379be0fcb5SRoald Frederickx 
pwm_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)4389be0fcb5SRoald Frederickx static ssize_t pwm_store(struct device *dev, struct device_attribute *attr,
4399be0fcb5SRoald Frederickx 		const char *buf, size_t count)
4409be0fcb5SRoald Frederickx {
4419be0fcb5SRoald Frederickx 	struct compal_data *data = dev_get_drvdata(dev);
4429be0fcb5SRoald Frederickx 	long val;
4439be0fcb5SRoald Frederickx 	int err;
4440db7fd96SJingoo Han 
4450db7fd96SJingoo Han 	err = kstrtol(buf, 10, &val);
4469be0fcb5SRoald Frederickx 	if (err)
4479be0fcb5SRoald Frederickx 		return err;
4489be0fcb5SRoald Frederickx 	if (val < 0 || val > 255)
4499be0fcb5SRoald Frederickx 		return -EINVAL;
4509be0fcb5SRoald Frederickx 
4519be0fcb5SRoald Frederickx 	data->curr_pwm = val;
4529be0fcb5SRoald Frederickx 
4539be0fcb5SRoald Frederickx 	if (data->pwm_enable != 1)
4549be0fcb5SRoald Frederickx 		return count;
4559be0fcb5SRoald Frederickx 	set_pwm(val);
4569be0fcb5SRoald Frederickx 
4579be0fcb5SRoald Frederickx 	return count;
4589be0fcb5SRoald Frederickx }
4599be0fcb5SRoald Frederickx 
fan_show(struct device * dev,struct device_attribute * attr,char * buf)4609be0fcb5SRoald Frederickx static ssize_t fan_show(struct device *dev, struct device_attribute *attr,
4619be0fcb5SRoald Frederickx 		char *buf)
4629be0fcb5SRoald Frederickx {
4639be0fcb5SRoald Frederickx 	return sprintf(buf, "%d\n", get_fan_rpm());
4649be0fcb5SRoald Frederickx }
4659be0fcb5SRoald Frederickx 
4669be0fcb5SRoald Frederickx 
4679be0fcb5SRoald Frederickx /* Temperature interface */
4689be0fcb5SRoald Frederickx #define TEMPERATURE_SHOW_TEMP_AND_LABEL(POSTFIX, ADDRESS, LABEL)	\
4699be0fcb5SRoald Frederickx static ssize_t temp_##POSTFIX(struct device *dev,			\
4709be0fcb5SRoald Frederickx 		struct device_attribute *attr, char *buf)		\
4719be0fcb5SRoald Frederickx {									\
4729be0fcb5SRoald Frederickx 	return sprintf(buf, "%d\n", 1000 * (int)ec_read_s8(ADDRESS));	\
4739be0fcb5SRoald Frederickx }									\
4749be0fcb5SRoald Frederickx static ssize_t label_##POSTFIX(struct device *dev,			\
4759be0fcb5SRoald Frederickx 		struct device_attribute *attr, char *buf)		\
4769be0fcb5SRoald Frederickx {									\
4779be0fcb5SRoald Frederickx 	return sprintf(buf, "%s\n", LABEL);				\
4789be0fcb5SRoald Frederickx }
4799be0fcb5SRoald Frederickx 
4809be0fcb5SRoald Frederickx /* Labels as in service guide */
4819be0fcb5SRoald Frederickx TEMPERATURE_SHOW_TEMP_AND_LABEL(cpu,        TEMP_CPU,        "CPU_TEMP");
4829be0fcb5SRoald Frederickx TEMPERATURE_SHOW_TEMP_AND_LABEL(cpu_local,  TEMP_CPU_LOCAL,  "CPU_TEMP_LOCAL");
4839be0fcb5SRoald Frederickx TEMPERATURE_SHOW_TEMP_AND_LABEL(cpu_DTS,    TEMP_CPU_DTS,    "CPU_DTS");
4849be0fcb5SRoald Frederickx TEMPERATURE_SHOW_TEMP_AND_LABEL(northbridge,TEMP_NORTHBRIDGE,"NorthBridge");
4859be0fcb5SRoald Frederickx TEMPERATURE_SHOW_TEMP_AND_LABEL(vga,        TEMP_VGA,        "VGA_TEMP");
4869be0fcb5SRoald Frederickx TEMPERATURE_SHOW_TEMP_AND_LABEL(SKIN,       TEMP_SKIN,       "SKIN_TEMP90");
4879be0fcb5SRoald Frederickx 
4889be0fcb5SRoald Frederickx 
4899be0fcb5SRoald Frederickx /* Power supply interface */
bat_status(void)4909be0fcb5SRoald Frederickx static int bat_status(void)
4919be0fcb5SRoald Frederickx {
4929be0fcb5SRoald Frederickx 	u8 status0 = ec_read_u8(BAT_STATUS0);
4939be0fcb5SRoald Frederickx 	u8 status1 = ec_read_u8(BAT_STATUS1);
4949be0fcb5SRoald Frederickx 
4959be0fcb5SRoald Frederickx 	if (status0 & BAT_S0_CHARGING)
4969be0fcb5SRoald Frederickx 		return POWER_SUPPLY_STATUS_CHARGING;
4979be0fcb5SRoald Frederickx 	if (status0 & BAT_S0_DISCHARGE)
4989be0fcb5SRoald Frederickx 		return POWER_SUPPLY_STATUS_DISCHARGING;
4999be0fcb5SRoald Frederickx 	if (status1 & BAT_S1_FULL)
5009be0fcb5SRoald Frederickx 		return POWER_SUPPLY_STATUS_FULL;
5019be0fcb5SRoald Frederickx 	return POWER_SUPPLY_STATUS_NOT_CHARGING;
5029be0fcb5SRoald Frederickx }
5039be0fcb5SRoald Frederickx 
bat_health(void)5049be0fcb5SRoald Frederickx static int bat_health(void)
5059be0fcb5SRoald Frederickx {
5069be0fcb5SRoald Frederickx 	u8 status = ec_read_u8(BAT_STOP_CHARGE1);
5079be0fcb5SRoald Frederickx 
5089be0fcb5SRoald Frederickx 	if (status & BAT_STOP_CHRG1_OVERTEMPERATURE)
5099be0fcb5SRoald Frederickx 		return POWER_SUPPLY_HEALTH_OVERHEAT;
5109be0fcb5SRoald Frederickx 	if (status & BAT_STOP_CHRG1_OVERVOLTAGE)
5119be0fcb5SRoald Frederickx 		return POWER_SUPPLY_HEALTH_OVERVOLTAGE;
5129be0fcb5SRoald Frederickx 	if (status & BAT_STOP_CHRG1_BAD_CELL)
5139be0fcb5SRoald Frederickx 		return POWER_SUPPLY_HEALTH_DEAD;
5149be0fcb5SRoald Frederickx 	if (status & BAT_STOP_CHRG1_COMM_FAIL)
5159be0fcb5SRoald Frederickx 		return POWER_SUPPLY_HEALTH_UNKNOWN;
5169be0fcb5SRoald Frederickx 	return POWER_SUPPLY_HEALTH_GOOD;
5179be0fcb5SRoald Frederickx }
5189be0fcb5SRoald Frederickx 
bat_is_present(void)5199be0fcb5SRoald Frederickx static int bat_is_present(void)
5209be0fcb5SRoald Frederickx {
5219be0fcb5SRoald Frederickx 	u8 status = ec_read_u8(BAT_STATUS2);
5229be0fcb5SRoald Frederickx 	return ((status & BAT_S1_EXISTS) != 0);
5239be0fcb5SRoald Frederickx }
5249be0fcb5SRoald Frederickx 
bat_technology(void)5259be0fcb5SRoald Frederickx static int bat_technology(void)
5269be0fcb5SRoald Frederickx {
5279be0fcb5SRoald Frederickx 	u8 status = ec_read_u8(BAT_STATUS1);
5289be0fcb5SRoald Frederickx 
5299be0fcb5SRoald Frederickx 	if (status & BAT_S1_LiION_OR_NiMH)
5309be0fcb5SRoald Frederickx 		return POWER_SUPPLY_TECHNOLOGY_LION;
5319be0fcb5SRoald Frederickx 	return POWER_SUPPLY_TECHNOLOGY_NiMH;
5329be0fcb5SRoald Frederickx }
5339be0fcb5SRoald Frederickx 
bat_capacity_level(void)5349be0fcb5SRoald Frederickx static int bat_capacity_level(void)
5359be0fcb5SRoald Frederickx {
5369be0fcb5SRoald Frederickx 	u8 status0 = ec_read_u8(BAT_STATUS0);
5379be0fcb5SRoald Frederickx 	u8 status1 = ec_read_u8(BAT_STATUS1);
5389be0fcb5SRoald Frederickx 	u8 status2 = ec_read_u8(BAT_STATUS2);
5399be0fcb5SRoald Frederickx 
5409be0fcb5SRoald Frederickx 	if (status0 & BAT_S0_DISCHRG_CRITICAL
5419be0fcb5SRoald Frederickx 			|| status1 & BAT_S1_EMPTY
5429be0fcb5SRoald Frederickx 			|| status2 & BAT_S2_LOW_LOW)
5439be0fcb5SRoald Frederickx 		return POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
5449be0fcb5SRoald Frederickx 	if (status0 & BAT_S0_LOW)
5459be0fcb5SRoald Frederickx 		return POWER_SUPPLY_CAPACITY_LEVEL_LOW;
5469be0fcb5SRoald Frederickx 	if (status1 & BAT_S1_FULL)
5479be0fcb5SRoald Frederickx 		return POWER_SUPPLY_CAPACITY_LEVEL_FULL;
5489be0fcb5SRoald Frederickx 	return POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
5499be0fcb5SRoald Frederickx }
5509be0fcb5SRoald Frederickx 
bat_get_property(struct power_supply * psy,enum power_supply_property psp,union power_supply_propval * val)5519be0fcb5SRoald Frederickx static int bat_get_property(struct power_supply *psy,
5529be0fcb5SRoald Frederickx 				enum power_supply_property psp,
5539be0fcb5SRoald Frederickx 				union power_supply_propval *val)
5549be0fcb5SRoald Frederickx {
555297d716fSKrzysztof Kozlowski 	struct compal_data *data = power_supply_get_drvdata(psy);
5569be0fcb5SRoald Frederickx 
5579be0fcb5SRoald Frederickx 	switch (psp) {
5589be0fcb5SRoald Frederickx 	case POWER_SUPPLY_PROP_STATUS:
5599be0fcb5SRoald Frederickx 		val->intval = bat_status();
5609be0fcb5SRoald Frederickx 		break;
5619be0fcb5SRoald Frederickx 	case POWER_SUPPLY_PROP_HEALTH:
5629be0fcb5SRoald Frederickx 		val->intval = bat_health();
5639be0fcb5SRoald Frederickx 		break;
5649be0fcb5SRoald Frederickx 	case POWER_SUPPLY_PROP_PRESENT:
5659be0fcb5SRoald Frederickx 		val->intval = bat_is_present();
5669be0fcb5SRoald Frederickx 		break;
5679be0fcb5SRoald Frederickx 	case POWER_SUPPLY_PROP_TECHNOLOGY:
5689be0fcb5SRoald Frederickx 		val->intval = bat_technology();
5699be0fcb5SRoald Frederickx 		break;
5709be0fcb5SRoald Frederickx 	case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: /* THE design voltage... */
5719be0fcb5SRoald Frederickx 		val->intval = ec_read_u16(BAT_VOLTAGE_DESIGN) * 1000;
5729be0fcb5SRoald Frederickx 		break;
5739be0fcb5SRoald Frederickx 	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
5749be0fcb5SRoald Frederickx 		val->intval = ec_read_u16(BAT_VOLTAGE_NOW) * 1000;
5759be0fcb5SRoald Frederickx 		break;
5769be0fcb5SRoald Frederickx 	case POWER_SUPPLY_PROP_CURRENT_NOW:
5779be0fcb5SRoald Frederickx 		val->intval = ec_read_s16(BAT_CURRENT_NOW) * 1000;
5789be0fcb5SRoald Frederickx 		break;
5799be0fcb5SRoald Frederickx 	case POWER_SUPPLY_PROP_CURRENT_AVG:
5809be0fcb5SRoald Frederickx 		val->intval = ec_read_s16(BAT_CURRENT_AVG) * 1000;
5819be0fcb5SRoald Frederickx 		break;
5829be0fcb5SRoald Frederickx 	case POWER_SUPPLY_PROP_POWER_NOW:
5839be0fcb5SRoald Frederickx 		val->intval = ec_read_u8(BAT_POWER) * 1000000;
5849be0fcb5SRoald Frederickx 		break;
5859be0fcb5SRoald Frederickx 	case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
5869be0fcb5SRoald Frederickx 		val->intval = ec_read_u16(BAT_CHARGE_DESIGN) * 1000;
5879be0fcb5SRoald Frederickx 		break;
5889be0fcb5SRoald Frederickx 	case POWER_SUPPLY_PROP_CHARGE_NOW:
5899be0fcb5SRoald Frederickx 		val->intval = ec_read_u16(BAT_CHARGE_NOW) * 1000;
5909be0fcb5SRoald Frederickx 		break;
591d9a427ecSRoald Frederickx 	case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
592d9a427ecSRoald Frederickx 		val->intval = ec_read_u8(BAT_CHARGE_LIMIT);
593d9a427ecSRoald Frederickx 		break;
594d9a427ecSRoald Frederickx 	case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX:
595d9a427ecSRoald Frederickx 		val->intval = BAT_CHARGE_LIMIT_MAX;
596d9a427ecSRoald Frederickx 		break;
5979be0fcb5SRoald Frederickx 	case POWER_SUPPLY_PROP_CAPACITY:
5989be0fcb5SRoald Frederickx 		val->intval = ec_read_u8(BAT_CAPACITY);
5999be0fcb5SRoald Frederickx 		break;
6009be0fcb5SRoald Frederickx 	case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
6019be0fcb5SRoald Frederickx 		val->intval = bat_capacity_level();
6029be0fcb5SRoald Frederickx 		break;
6039be0fcb5SRoald Frederickx 	/* It smees that BAT_TEMP_AVG is a (2's complement?) value showing
6049be0fcb5SRoald Frederickx 	 * the number of degrees, whereas BAT_TEMP is somewhat more
6059be0fcb5SRoald Frederickx 	 * complicated. It looks like this is a negative nember with a
6069be0fcb5SRoald Frederickx 	 * 100/256 divider and an offset of 222. Both were determined
6079be0fcb5SRoald Frederickx 	 * experimentally by comparing BAT_TEMP and BAT_TEMP_AVG. */
6089be0fcb5SRoald Frederickx 	case POWER_SUPPLY_PROP_TEMP:
6099be0fcb5SRoald Frederickx 		val->intval = ((222 - (int)ec_read_u8(BAT_TEMP)) * 1000) >> 8;
6109be0fcb5SRoald Frederickx 		break;
6119be0fcb5SRoald Frederickx 	case POWER_SUPPLY_PROP_TEMP_AMBIENT: /* Ambient, Avg, ... same thing */
6129be0fcb5SRoald Frederickx 		val->intval = ec_read_s8(BAT_TEMP_AVG) * 10;
6139be0fcb5SRoald Frederickx 		break;
6149be0fcb5SRoald Frederickx 	/* Neither the model name nor manufacturer name work for me. */
6159be0fcb5SRoald Frederickx 	case POWER_SUPPLY_PROP_MODEL_NAME:
6169be0fcb5SRoald Frederickx 		val->strval = data->bat_model_name;
6179be0fcb5SRoald Frederickx 		break;
6189be0fcb5SRoald Frederickx 	case POWER_SUPPLY_PROP_MANUFACTURER:
6199be0fcb5SRoald Frederickx 		val->strval = data->bat_manufacturer_name;
6209be0fcb5SRoald Frederickx 		break;
6219be0fcb5SRoald Frederickx 	case POWER_SUPPLY_PROP_SERIAL_NUMBER:
6229be0fcb5SRoald Frederickx 		val->strval = data->bat_serial_number;
6239be0fcb5SRoald Frederickx 		break;
6249be0fcb5SRoald Frederickx 	default:
6259be0fcb5SRoald Frederickx 		break;
6269be0fcb5SRoald Frederickx 	}
62741b16dceSLen Brown 	return 0;
62841b16dceSLen Brown }
62941b16dceSLen Brown 
bat_set_property(struct power_supply * psy,enum power_supply_property psp,const union power_supply_propval * val)630d9a427ecSRoald Frederickx static int bat_set_property(struct power_supply *psy,
631d9a427ecSRoald Frederickx 				enum power_supply_property psp,
632d9a427ecSRoald Frederickx 				const union power_supply_propval *val)
633d9a427ecSRoald Frederickx {
634d9a427ecSRoald Frederickx 	int level;
635d9a427ecSRoald Frederickx 
636d9a427ecSRoald Frederickx 	switch (psp) {
637d9a427ecSRoald Frederickx 	case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
638d9a427ecSRoald Frederickx 		level = val->intval;
639d9a427ecSRoald Frederickx 		if (level < 0 || level > BAT_CHARGE_LIMIT_MAX)
640d9a427ecSRoald Frederickx 			return -EINVAL;
641d9a427ecSRoald Frederickx 		if (ec_write(BAT_CHARGE_LIMIT, level) < 0)
642d9a427ecSRoald Frederickx 			return -EIO;
643d9a427ecSRoald Frederickx 		break;
644d9a427ecSRoald Frederickx 	default:
645d9a427ecSRoald Frederickx 		break;
646d9a427ecSRoald Frederickx 	}
647d9a427ecSRoald Frederickx 	return 0;
648d9a427ecSRoald Frederickx }
649d9a427ecSRoald Frederickx 
bat_writeable_property(struct power_supply * psy,enum power_supply_property psp)650d9a427ecSRoald Frederickx static int bat_writeable_property(struct power_supply *psy,
651d9a427ecSRoald Frederickx 				enum power_supply_property psp)
652d9a427ecSRoald Frederickx {
653d9a427ecSRoald Frederickx 	switch (psp) {
654d9a427ecSRoald Frederickx 	case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
655d9a427ecSRoald Frederickx 		return 1;
656d9a427ecSRoald Frederickx 	default:
657d9a427ecSRoald Frederickx 		return 0;
658d9a427ecSRoald Frederickx 	}
659d9a427ecSRoald Frederickx }
66041b16dceSLen Brown 
66141b16dceSLen Brown 
6629be0fcb5SRoald Frederickx 
6639be0fcb5SRoald Frederickx 
6649be0fcb5SRoald Frederickx /* ============== */
6659be0fcb5SRoald Frederickx /* Driver Globals */
6669be0fcb5SRoald Frederickx /* ============== */
667b6b996b6SJoe Perches static DEVICE_ATTR_RW(wake_up_pme);
668b6b996b6SJoe Perches static DEVICE_ATTR_RW(wake_up_modem);
669b6b996b6SJoe Perches static DEVICE_ATTR_RW(wake_up_lan);
670b6b996b6SJoe Perches static DEVICE_ATTR_RW(wake_up_wlan);
671b6b996b6SJoe Perches static DEVICE_ATTR_RW(wake_up_key);
672b6b996b6SJoe Perches static DEVICE_ATTR_RW(wake_up_mouse);
6739be0fcb5SRoald Frederickx 
6744e062581SGuenter Roeck static DEVICE_ATTR(fan1_input,  S_IRUGO, fan_show,          NULL);
6754e062581SGuenter Roeck static DEVICE_ATTR(temp1_input, S_IRUGO, temp_cpu,          NULL);
6764e062581SGuenter Roeck static DEVICE_ATTR(temp2_input, S_IRUGO, temp_cpu_local,    NULL);
6774e062581SGuenter Roeck static DEVICE_ATTR(temp3_input, S_IRUGO, temp_cpu_DTS,      NULL);
6784e062581SGuenter Roeck static DEVICE_ATTR(temp4_input, S_IRUGO, temp_northbridge,  NULL);
6794e062581SGuenter Roeck static DEVICE_ATTR(temp5_input, S_IRUGO, temp_vga,          NULL);
6804e062581SGuenter Roeck static DEVICE_ATTR(temp6_input, S_IRUGO, temp_SKIN,         NULL);
6814e062581SGuenter Roeck static DEVICE_ATTR(temp1_label, S_IRUGO, label_cpu,         NULL);
6824e062581SGuenter Roeck static DEVICE_ATTR(temp2_label, S_IRUGO, label_cpu_local,   NULL);
6834e062581SGuenter Roeck static DEVICE_ATTR(temp3_label, S_IRUGO, label_cpu_DTS,     NULL);
6844e062581SGuenter Roeck static DEVICE_ATTR(temp4_label, S_IRUGO, label_northbridge, NULL);
6854e062581SGuenter Roeck static DEVICE_ATTR(temp5_label, S_IRUGO, label_vga,         NULL);
6864e062581SGuenter Roeck static DEVICE_ATTR(temp6_label, S_IRUGO, label_SKIN,        NULL);
6874e062581SGuenter Roeck static DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, pwm_show, pwm_store);
6884e062581SGuenter Roeck static DEVICE_ATTR(pwm1_enable,
6894e062581SGuenter Roeck 		   S_IRUGO | S_IWUSR, pwm_enable_show, pwm_enable_store);
6909be0fcb5SRoald Frederickx 
691c2be45f0SGuenter Roeck static struct attribute *compal_platform_attrs[] = {
6929be0fcb5SRoald Frederickx 	&dev_attr_wake_up_pme.attr,
6939be0fcb5SRoald Frederickx 	&dev_attr_wake_up_modem.attr,
6949be0fcb5SRoald Frederickx 	&dev_attr_wake_up_lan.attr,
6959be0fcb5SRoald Frederickx 	&dev_attr_wake_up_wlan.attr,
6969be0fcb5SRoald Frederickx 	&dev_attr_wake_up_key.attr,
6979be0fcb5SRoald Frederickx 	&dev_attr_wake_up_mouse.attr,
698c2be45f0SGuenter Roeck 	NULL
699c2be45f0SGuenter Roeck };
700ab56246bSArvind Yadav static const struct attribute_group compal_platform_attr_group = {
701c2be45f0SGuenter Roeck 	.attrs = compal_platform_attrs
702c2be45f0SGuenter Roeck };
703c2be45f0SGuenter Roeck 
704c2be45f0SGuenter Roeck static struct attribute *compal_hwmon_attrs[] = {
7054e062581SGuenter Roeck 	&dev_attr_pwm1_enable.attr,
7064e062581SGuenter Roeck 	&dev_attr_pwm1.attr,
7074e062581SGuenter Roeck 	&dev_attr_fan1_input.attr,
7084e062581SGuenter Roeck 	&dev_attr_temp1_input.attr,
7094e062581SGuenter Roeck 	&dev_attr_temp2_input.attr,
7104e062581SGuenter Roeck 	&dev_attr_temp3_input.attr,
7114e062581SGuenter Roeck 	&dev_attr_temp4_input.attr,
7124e062581SGuenter Roeck 	&dev_attr_temp5_input.attr,
7134e062581SGuenter Roeck 	&dev_attr_temp6_input.attr,
7144e062581SGuenter Roeck 	&dev_attr_temp1_label.attr,
7154e062581SGuenter Roeck 	&dev_attr_temp2_label.attr,
7164e062581SGuenter Roeck 	&dev_attr_temp3_label.attr,
7174e062581SGuenter Roeck 	&dev_attr_temp4_label.attr,
7184e062581SGuenter Roeck 	&dev_attr_temp5_label.attr,
7194e062581SGuenter Roeck 	&dev_attr_temp6_label.attr,
7209be0fcb5SRoald Frederickx 	NULL
7219be0fcb5SRoald Frederickx };
722c2be45f0SGuenter Roeck ATTRIBUTE_GROUPS(compal_hwmon);
7239be0fcb5SRoald Frederickx 
7249be0fcb5SRoald Frederickx static enum power_supply_property compal_bat_properties[] = {
7259be0fcb5SRoald Frederickx 	POWER_SUPPLY_PROP_STATUS,
7269be0fcb5SRoald Frederickx 	POWER_SUPPLY_PROP_HEALTH,
7279be0fcb5SRoald Frederickx 	POWER_SUPPLY_PROP_PRESENT,
7289be0fcb5SRoald Frederickx 	POWER_SUPPLY_PROP_TECHNOLOGY,
7299be0fcb5SRoald Frederickx 	POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
7309be0fcb5SRoald Frederickx 	POWER_SUPPLY_PROP_VOLTAGE_NOW,
7319be0fcb5SRoald Frederickx 	POWER_SUPPLY_PROP_CURRENT_NOW,
7329be0fcb5SRoald Frederickx 	POWER_SUPPLY_PROP_CURRENT_AVG,
7339be0fcb5SRoald Frederickx 	POWER_SUPPLY_PROP_POWER_NOW,
7349be0fcb5SRoald Frederickx 	POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
7359be0fcb5SRoald Frederickx 	POWER_SUPPLY_PROP_CHARGE_NOW,
736d9a427ecSRoald Frederickx 	POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT,
737d9a427ecSRoald Frederickx 	POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX,
7389be0fcb5SRoald Frederickx 	POWER_SUPPLY_PROP_CAPACITY,
7399be0fcb5SRoald Frederickx 	POWER_SUPPLY_PROP_CAPACITY_LEVEL,
7409be0fcb5SRoald Frederickx 	POWER_SUPPLY_PROP_TEMP,
7419be0fcb5SRoald Frederickx 	POWER_SUPPLY_PROP_TEMP_AMBIENT,
7429be0fcb5SRoald Frederickx 	POWER_SUPPLY_PROP_MODEL_NAME,
7439be0fcb5SRoald Frederickx 	POWER_SUPPLY_PROP_MANUFACTURER,
7449be0fcb5SRoald Frederickx 	POWER_SUPPLY_PROP_SERIAL_NUMBER,
74541b16dceSLen Brown };
74641b16dceSLen Brown 
74741b16dceSLen Brown static struct backlight_device *compalbl_device;
74841b16dceSLen Brown 
7499be0fcb5SRoald Frederickx static struct platform_device *compal_device;
75041b16dceSLen Brown 
7519be0fcb5SRoald Frederickx static struct rfkill *wifi_rfkill;
7529be0fcb5SRoald Frederickx static struct rfkill *bt_rfkill;
75341b16dceSLen Brown 
7549be0fcb5SRoald Frederickx 
7559be0fcb5SRoald Frederickx 
7569be0fcb5SRoald Frederickx 
7579be0fcb5SRoald Frederickx 
7589be0fcb5SRoald Frederickx /* =================================== */
7599be0fcb5SRoald Frederickx /* Initialization & clean-up functions */
7609be0fcb5SRoald Frederickx /* =================================== */
76141b16dceSLen Brown 
dmi_check_cb(const struct dmi_system_id * id)76241b16dceSLen Brown static int dmi_check_cb(const struct dmi_system_id *id)
76341b16dceSLen Brown {
764b4a4bc0bSJoe Perches 	pr_info("Identified laptop model '%s'\n", id->ident);
7659be0fcb5SRoald Frederickx 	extra_features = false;
7660e4510f7SAxel Lin 	return 1;
7679be0fcb5SRoald Frederickx }
76841b16dceSLen Brown 
dmi_check_cb_extra(const struct dmi_system_id * id)7699be0fcb5SRoald Frederickx static int dmi_check_cb_extra(const struct dmi_system_id *id)
7709be0fcb5SRoald Frederickx {
771b4a4bc0bSJoe Perches 	pr_info("Identified laptop model '%s', enabling extra features\n",
7729be0fcb5SRoald Frederickx 		id->ident);
7739be0fcb5SRoald Frederickx 	extra_features = true;
7740e4510f7SAxel Lin 	return 1;
77541b16dceSLen Brown }
77641b16dceSLen Brown 
7776faadbbbSChristoph Hellwig static const struct dmi_system_id compal_dmi_table[] __initconst = {
77841b16dceSLen Brown 	{
77941b16dceSLen Brown 		.ident = "FL90/IFL90",
78041b16dceSLen Brown 		.matches = {
78141b16dceSLen Brown 			DMI_MATCH(DMI_BOARD_NAME, "IFL90"),
78241b16dceSLen Brown 			DMI_MATCH(DMI_BOARD_VERSION, "IFT00"),
78341b16dceSLen Brown 		},
78441b16dceSLen Brown 		.callback = dmi_check_cb
78541b16dceSLen Brown 	},
78641b16dceSLen Brown 	{
78741b16dceSLen Brown 		.ident = "FL90/IFL90",
78841b16dceSLen Brown 		.matches = {
78941b16dceSLen Brown 			DMI_MATCH(DMI_BOARD_NAME, "IFL90"),
79041b16dceSLen Brown 			DMI_MATCH(DMI_BOARD_VERSION, "REFERENCE"),
79141b16dceSLen Brown 		},
79241b16dceSLen Brown 		.callback = dmi_check_cb
79341b16dceSLen Brown 	},
79441b16dceSLen Brown 	{
79541b16dceSLen Brown 		.ident = "FL91/IFL91",
79641b16dceSLen Brown 		.matches = {
79741b16dceSLen Brown 			DMI_MATCH(DMI_BOARD_NAME, "IFL91"),
79841b16dceSLen Brown 			DMI_MATCH(DMI_BOARD_VERSION, "IFT00"),
79941b16dceSLen Brown 		},
80041b16dceSLen Brown 		.callback = dmi_check_cb
80141b16dceSLen Brown 	},
80241b16dceSLen Brown 	{
80341b16dceSLen Brown 		.ident = "FL92/JFL92",
80441b16dceSLen Brown 		.matches = {
80541b16dceSLen Brown 			DMI_MATCH(DMI_BOARD_NAME, "JFL92"),
80641b16dceSLen Brown 			DMI_MATCH(DMI_BOARD_VERSION, "IFT00"),
80741b16dceSLen Brown 		},
80841b16dceSLen Brown 		.callback = dmi_check_cb
80941b16dceSLen Brown 	},
81041b16dceSLen Brown 	{
81141b16dceSLen Brown 		.ident = "FT00/IFT00",
81241b16dceSLen Brown 		.matches = {
81341b16dceSLen Brown 			DMI_MATCH(DMI_BOARD_NAME, "IFT00"),
81441b16dceSLen Brown 			DMI_MATCH(DMI_BOARD_VERSION, "IFT00"),
81541b16dceSLen Brown 		},
81641b16dceSLen Brown 		.callback = dmi_check_cb
81741b16dceSLen Brown 	},
81834325b9dSMario Limonciello 	{
81934325b9dSMario Limonciello 		.ident = "Dell Mini 9",
82034325b9dSMario Limonciello 		.matches = {
82134325b9dSMario Limonciello 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
82234325b9dSMario Limonciello 			DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 910"),
82334325b9dSMario Limonciello 		},
82434325b9dSMario Limonciello 		.callback = dmi_check_cb
82534325b9dSMario Limonciello 	},
82634325b9dSMario Limonciello 	{
82734325b9dSMario Limonciello 		.ident = "Dell Mini 10",
82834325b9dSMario Limonciello 		.matches = {
82934325b9dSMario Limonciello 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
83034325b9dSMario Limonciello 			DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1010"),
83134325b9dSMario Limonciello 		},
83234325b9dSMario Limonciello 		.callback = dmi_check_cb
83334325b9dSMario Limonciello 	},
83434325b9dSMario Limonciello 	{
83534325b9dSMario Limonciello 		.ident = "Dell Mini 10v",
83634325b9dSMario Limonciello 		.matches = {
83734325b9dSMario Limonciello 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
83834325b9dSMario Limonciello 			DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1011"),
83934325b9dSMario Limonciello 		},
84034325b9dSMario Limonciello 		.callback = dmi_check_cb
84134325b9dSMario Limonciello 	},
84234325b9dSMario Limonciello 	{
843c3f755e3SVictor van den Elzen 		.ident = "Dell Mini 1012",
844c3f755e3SVictor van den Elzen 		.matches = {
845c3f755e3SVictor van den Elzen 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
846c3f755e3SVictor van den Elzen 			DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1012"),
847c3f755e3SVictor van den Elzen 		},
848c3f755e3SVictor van den Elzen 		.callback = dmi_check_cb
849c3f755e3SVictor van den Elzen 	},
850c3f755e3SVictor van den Elzen 	{
85134325b9dSMario Limonciello 		.ident = "Dell Inspiron 11z",
85234325b9dSMario Limonciello 		.matches = {
85334325b9dSMario Limonciello 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
85434325b9dSMario Limonciello 			DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1110"),
85534325b9dSMario Limonciello 		},
85634325b9dSMario Limonciello 		.callback = dmi_check_cb
85734325b9dSMario Limonciello 	},
85834325b9dSMario Limonciello 	{
85934325b9dSMario Limonciello 		.ident = "Dell Mini 12",
86034325b9dSMario Limonciello 		.matches = {
86134325b9dSMario Limonciello 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
86234325b9dSMario Limonciello 			DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1210"),
86334325b9dSMario Limonciello 		},
86434325b9dSMario Limonciello 		.callback = dmi_check_cb
86534325b9dSMario Limonciello 	},
8669be0fcb5SRoald Frederickx 	{
8679be0fcb5SRoald Frederickx 		.ident = "JHL90",
8689be0fcb5SRoald Frederickx 		.matches = {
8699be0fcb5SRoald Frederickx 			DMI_MATCH(DMI_BOARD_NAME, "JHL90"),
8709be0fcb5SRoald Frederickx 			DMI_MATCH(DMI_BOARD_VERSION, "REFERENCE"),
8719be0fcb5SRoald Frederickx 		},
8729be0fcb5SRoald Frederickx 		.callback = dmi_check_cb_extra
8739be0fcb5SRoald Frederickx 	},
8744a198be7SAlbert Astals Cid 	{
8754a198be7SAlbert Astals Cid 		.ident = "KHLB2",
8764a198be7SAlbert Astals Cid 		.matches = {
8774a198be7SAlbert Astals Cid 			DMI_MATCH(DMI_BOARD_NAME, "KHLB2"),
8784a198be7SAlbert Astals Cid 			DMI_MATCH(DMI_BOARD_VERSION, "REFERENCE"),
8794a198be7SAlbert Astals Cid 		},
8804a198be7SAlbert Astals Cid 		.callback = dmi_check_cb_extra
8814a198be7SAlbert Astals Cid 	},
88241b16dceSLen Brown 	{ }
88341b16dceSLen Brown };
8844585aba7SDmitry Torokhov MODULE_DEVICE_TABLE(dmi, compal_dmi_table);
88541b16dceSLen Brown 
886297d716fSKrzysztof Kozlowski static const struct power_supply_desc psy_bat_desc = {
887297d716fSKrzysztof Kozlowski 	.name		= DRIVER_NAME,
888297d716fSKrzysztof Kozlowski 	.type		= POWER_SUPPLY_TYPE_BATTERY,
889297d716fSKrzysztof Kozlowski 	.properties	= compal_bat_properties,
890297d716fSKrzysztof Kozlowski 	.num_properties	= ARRAY_SIZE(compal_bat_properties),
891297d716fSKrzysztof Kozlowski 	.get_property	= bat_get_property,
892d9a427ecSRoald Frederickx 	.set_property	= bat_set_property,
893d9a427ecSRoald Frederickx 	.property_is_writeable = bat_writeable_property,
894297d716fSKrzysztof Kozlowski };
895297d716fSKrzysztof Kozlowski 
initialize_power_supply_data(struct compal_data * data)8969be0fcb5SRoald Frederickx static void initialize_power_supply_data(struct compal_data *data)
8979be0fcb5SRoald Frederickx {
8989be0fcb5SRoald Frederickx 	ec_read_sequence(BAT_MANUFACTURER_NAME_ADDR,
8999be0fcb5SRoald Frederickx 					data->bat_manufacturer_name,
9009be0fcb5SRoald Frederickx 					BAT_MANUFACTURER_NAME_LEN);
9019be0fcb5SRoald Frederickx 	data->bat_manufacturer_name[BAT_MANUFACTURER_NAME_LEN] = 0;
9029be0fcb5SRoald Frederickx 
9039be0fcb5SRoald Frederickx 	ec_read_sequence(BAT_MODEL_NAME_ADDR,
9049be0fcb5SRoald Frederickx 					data->bat_model_name,
9059be0fcb5SRoald Frederickx 					BAT_MODEL_NAME_LEN);
9069be0fcb5SRoald Frederickx 	data->bat_model_name[BAT_MODEL_NAME_LEN] = 0;
9079be0fcb5SRoald Frederickx 
9089be0fcb5SRoald Frederickx 	scnprintf(data->bat_serial_number, BAT_SERIAL_NUMBER_LEN + 1, "%d",
9099be0fcb5SRoald Frederickx 				ec_read_u16(BAT_SERIAL_NUMBER_ADDR));
9109be0fcb5SRoald Frederickx }
9119be0fcb5SRoald Frederickx 
initialize_fan_control_data(struct compal_data * data)9129be0fcb5SRoald Frederickx static void initialize_fan_control_data(struct compal_data *data)
9139be0fcb5SRoald Frederickx {
9149be0fcb5SRoald Frederickx 	data->pwm_enable = 2; /* Keep motherboard in control for now */
9159be0fcb5SRoald Frederickx 	data->curr_pwm = 255; /* Try not to cause a CPU_on_fire exception
9169be0fcb5SRoald Frederickx 				 if we take over... */
9179be0fcb5SRoald Frederickx }
9189be0fcb5SRoald Frederickx 
setup_rfkill(void)9199be0fcb5SRoald Frederickx static int setup_rfkill(void)
9209be0fcb5SRoald Frederickx {
9219be0fcb5SRoald Frederickx 	int ret;
9229be0fcb5SRoald Frederickx 
9239be0fcb5SRoald Frederickx 	wifi_rfkill = rfkill_alloc("compal-wifi", &compal_device->dev,
9249be0fcb5SRoald Frederickx 				RFKILL_TYPE_WLAN, &compal_rfkill_ops,
9259be0fcb5SRoald Frederickx 				(void *) WIRELESS_WLAN);
9269be0fcb5SRoald Frederickx 	if (!wifi_rfkill)
9279be0fcb5SRoald Frederickx 		return -ENOMEM;
9289be0fcb5SRoald Frederickx 
9299be0fcb5SRoald Frederickx 	ret = rfkill_register(wifi_rfkill);
9309be0fcb5SRoald Frederickx 	if (ret)
9319be0fcb5SRoald Frederickx 		goto err_wifi;
9329be0fcb5SRoald Frederickx 
9339be0fcb5SRoald Frederickx 	bt_rfkill = rfkill_alloc("compal-bluetooth", &compal_device->dev,
9349be0fcb5SRoald Frederickx 				RFKILL_TYPE_BLUETOOTH, &compal_rfkill_ops,
9359be0fcb5SRoald Frederickx 				(void *) WIRELESS_BT);
9369be0fcb5SRoald Frederickx 	if (!bt_rfkill) {
9379be0fcb5SRoald Frederickx 		ret = -ENOMEM;
9389be0fcb5SRoald Frederickx 		goto err_allocate_bt;
9399be0fcb5SRoald Frederickx 	}
9409be0fcb5SRoald Frederickx 	ret = rfkill_register(bt_rfkill);
9419be0fcb5SRoald Frederickx 	if (ret)
9429be0fcb5SRoald Frederickx 		goto err_register_bt;
9439be0fcb5SRoald Frederickx 
9449be0fcb5SRoald Frederickx 	return 0;
9459be0fcb5SRoald Frederickx 
9469be0fcb5SRoald Frederickx err_register_bt:
9479be0fcb5SRoald Frederickx 	rfkill_destroy(bt_rfkill);
9489be0fcb5SRoald Frederickx 
9499be0fcb5SRoald Frederickx err_allocate_bt:
9509be0fcb5SRoald Frederickx 	rfkill_unregister(wifi_rfkill);
9519be0fcb5SRoald Frederickx 
9529be0fcb5SRoald Frederickx err_wifi:
9539be0fcb5SRoald Frederickx 	rfkill_destroy(wifi_rfkill);
9549be0fcb5SRoald Frederickx 
9559be0fcb5SRoald Frederickx 	return ret;
9569be0fcb5SRoald Frederickx }
9579be0fcb5SRoald Frederickx 
compal_probe(struct platform_device * pdev)958d443fcadSUwe Kleine-König static int compal_probe(struct platform_device *pdev)
959d443fcadSUwe Kleine-König {
960d443fcadSUwe Kleine-König 	int err;
961d443fcadSUwe Kleine-König 	struct compal_data *data;
962d443fcadSUwe Kleine-König 	struct device *hwmon_dev;
963d443fcadSUwe Kleine-König 	struct power_supply_config psy_cfg = {};
964d443fcadSUwe Kleine-König 
965d443fcadSUwe Kleine-König 	if (!extra_features)
966d443fcadSUwe Kleine-König 		return 0;
967d443fcadSUwe Kleine-König 
968d443fcadSUwe Kleine-König 	/* Fan control */
969d443fcadSUwe Kleine-König 	data = devm_kzalloc(&pdev->dev, sizeof(struct compal_data), GFP_KERNEL);
970d443fcadSUwe Kleine-König 	if (!data)
971d443fcadSUwe Kleine-König 		return -ENOMEM;
972d443fcadSUwe Kleine-König 
973d443fcadSUwe Kleine-König 	initialize_fan_control_data(data);
974d443fcadSUwe Kleine-König 
975d443fcadSUwe Kleine-König 	err = sysfs_create_group(&pdev->dev.kobj, &compal_platform_attr_group);
976d443fcadSUwe Kleine-König 	if (err)
977d443fcadSUwe Kleine-König 		return err;
978d443fcadSUwe Kleine-König 
979d443fcadSUwe Kleine-König 	hwmon_dev = devm_hwmon_device_register_with_groups(&pdev->dev,
980d443fcadSUwe Kleine-König 							   "compal", data,
981d443fcadSUwe Kleine-König 							   compal_hwmon_groups);
982d443fcadSUwe Kleine-König 	if (IS_ERR(hwmon_dev)) {
983d443fcadSUwe Kleine-König 		err = PTR_ERR(hwmon_dev);
984d443fcadSUwe Kleine-König 		goto remove;
985d443fcadSUwe Kleine-König 	}
986d443fcadSUwe Kleine-König 
987d443fcadSUwe Kleine-König 	/* Power supply */
988d443fcadSUwe Kleine-König 	initialize_power_supply_data(data);
989d443fcadSUwe Kleine-König 	psy_cfg.drv_data = data;
990d443fcadSUwe Kleine-König 	data->psy = power_supply_register(&compal_device->dev, &psy_bat_desc,
991d443fcadSUwe Kleine-König 					  &psy_cfg);
992d443fcadSUwe Kleine-König 	if (IS_ERR(data->psy)) {
993d443fcadSUwe Kleine-König 		err = PTR_ERR(data->psy);
994d443fcadSUwe Kleine-König 		goto remove;
995d443fcadSUwe Kleine-König 	}
996d443fcadSUwe Kleine-König 
997d443fcadSUwe Kleine-König 	platform_set_drvdata(pdev, data);
998d443fcadSUwe Kleine-König 
999d443fcadSUwe Kleine-König 	return 0;
1000d443fcadSUwe Kleine-König 
1001d443fcadSUwe Kleine-König remove:
1002d443fcadSUwe Kleine-König 	sysfs_remove_group(&pdev->dev.kobj, &compal_platform_attr_group);
1003d443fcadSUwe Kleine-König 	return err;
1004d443fcadSUwe Kleine-König }
1005d443fcadSUwe Kleine-König 
compal_remove(struct platform_device * pdev)1006*d1bda904SUwe Kleine-König static void compal_remove(struct platform_device *pdev)
1007d443fcadSUwe Kleine-König {
1008d443fcadSUwe Kleine-König 	struct compal_data *data;
1009d443fcadSUwe Kleine-König 
1010d443fcadSUwe Kleine-König 	if (!extra_features)
1011*d1bda904SUwe Kleine-König 		return;
1012d443fcadSUwe Kleine-König 
1013d443fcadSUwe Kleine-König 	pr_info("Unloading: resetting fan control to motherboard\n");
1014d443fcadSUwe Kleine-König 	pwm_disable_control();
1015d443fcadSUwe Kleine-König 
1016d443fcadSUwe Kleine-König 	data = platform_get_drvdata(pdev);
1017d443fcadSUwe Kleine-König 	power_supply_unregister(data->psy);
1018d443fcadSUwe Kleine-König 
1019d443fcadSUwe Kleine-König 	sysfs_remove_group(&pdev->dev.kobj, &compal_platform_attr_group);
1020d443fcadSUwe Kleine-König }
1021d443fcadSUwe Kleine-König 
1022d443fcadSUwe Kleine-König static struct platform_driver compal_driver = {
1023d443fcadSUwe Kleine-König 	.driver = {
1024d443fcadSUwe Kleine-König 		.name = DRIVER_NAME,
1025d443fcadSUwe Kleine-König 	},
1026d443fcadSUwe Kleine-König 	.probe	= compal_probe,
1027*d1bda904SUwe Kleine-König 	.remove_new = compal_remove,
1028d443fcadSUwe Kleine-König };
1029d443fcadSUwe Kleine-König 
compal_init(void)103041b16dceSLen Brown static int __init compal_init(void)
103141b16dceSLen Brown {
103241b16dceSLen Brown 	int ret;
103341b16dceSLen Brown 
10349be0fcb5SRoald Frederickx 	if (acpi_disabled) {
1035b4a4bc0bSJoe Perches 		pr_err("ACPI needs to be enabled for this driver to work!\n");
103641b16dceSLen Brown 		return -ENODEV;
10379be0fcb5SRoald Frederickx 	}
103841b16dceSLen Brown 
10399be0fcb5SRoald Frederickx 	if (!force && !dmi_check_system(compal_dmi_table)) {
1040b4a4bc0bSJoe Perches 		pr_err("Motherboard not recognized (You could try the module's force-parameter)\n");
104141b16dceSLen Brown 		return -ENODEV;
10429be0fcb5SRoald Frederickx 	}
104341b16dceSLen Brown 
1044358fefe7SHans de Goede 	if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
1045a19a6ee6SMatthew Garrett 		struct backlight_properties props;
1046a19a6ee6SMatthew Garrett 		memset(&props, 0, sizeof(struct backlight_properties));
1047bb7ca747SMatthew Garrett 		props.type = BACKLIGHT_PLATFORM;
10489be0fcb5SRoald Frederickx 		props.max_brightness = BACKLIGHT_LEVEL_MAX;
10499be0fcb5SRoald Frederickx 		compalbl_device = backlight_device_register(DRIVER_NAME,
1050a19a6ee6SMatthew Garrett 							    NULL, NULL,
1051a19a6ee6SMatthew Garrett 							    &compalbl_ops,
1052a19a6ee6SMatthew Garrett 							    &props);
105341b16dceSLen Brown 		if (IS_ERR(compalbl_device))
105441b16dceSLen Brown 			return PTR_ERR(compalbl_device);
105541b16dceSLen Brown 	}
105641b16dceSLen Brown 
105741b16dceSLen Brown 	ret = platform_driver_register(&compal_driver);
105841b16dceSLen Brown 	if (ret)
10599be0fcb5SRoald Frederickx 		goto err_backlight;
106041b16dceSLen Brown 
10618d05fc03SBarnabás Pőcze 	compal_device = platform_device_alloc(DRIVER_NAME, PLATFORM_DEVID_NONE);
106241b16dceSLen Brown 	if (!compal_device) {
106341b16dceSLen Brown 		ret = -ENOMEM;
10649be0fcb5SRoald Frederickx 		goto err_platform_driver;
106541b16dceSLen Brown 	}
106641b16dceSLen Brown 
10679be0fcb5SRoald Frederickx 	ret = platform_device_add(compal_device); /* This calls compal_probe */
106841b16dceSLen Brown 	if (ret)
10699be0fcb5SRoald Frederickx 		goto err_platform_device;
107041b16dceSLen Brown 
1071493e9143SMario Limonciello 	ret = setup_rfkill();
107241b16dceSLen Brown 	if (ret)
10739be0fcb5SRoald Frederickx 		goto err_rfkill;
107441b16dceSLen Brown 
1075b4a4bc0bSJoe Perches 	pr_info("Driver " DRIVER_VERSION " successfully loaded\n");
107641b16dceSLen Brown 	return 0;
107741b16dceSLen Brown 
10789be0fcb5SRoald Frederickx err_rfkill:
107941b16dceSLen Brown 	platform_device_del(compal_device);
108041b16dceSLen Brown 
10819be0fcb5SRoald Frederickx err_platform_device:
108241b16dceSLen Brown 	platform_device_put(compal_device);
108341b16dceSLen Brown 
10849be0fcb5SRoald Frederickx err_platform_driver:
108541b16dceSLen Brown 	platform_driver_unregister(&compal_driver);
108641b16dceSLen Brown 
10879be0fcb5SRoald Frederickx err_backlight:
108841b16dceSLen Brown 	backlight_device_unregister(compalbl_device);
108941b16dceSLen Brown 
109041b16dceSLen Brown 	return ret;
109141b16dceSLen Brown }
109241b16dceSLen Brown 
compal_cleanup(void)109341b16dceSLen Brown static void __exit compal_cleanup(void)
109441b16dceSLen Brown {
109541b16dceSLen Brown 	platform_device_unregister(compal_device);
109641b16dceSLen Brown 	platform_driver_unregister(&compal_driver);
109741b16dceSLen Brown 	backlight_device_unregister(compalbl_device);
1098493e9143SMario Limonciello 	rfkill_unregister(wifi_rfkill);
1099493e9143SMario Limonciello 	rfkill_unregister(bt_rfkill);
11009be0fcb5SRoald Frederickx 	rfkill_destroy(wifi_rfkill);
1101493e9143SMario Limonciello 	rfkill_destroy(bt_rfkill);
110241b16dceSLen Brown 
1103b4a4bc0bSJoe Perches 	pr_info("Driver unloaded\n");
110441b16dceSLen Brown }
110541b16dceSLen Brown 
110641b16dceSLen Brown module_init(compal_init);
110741b16dceSLen Brown module_exit(compal_cleanup);
110841b16dceSLen Brown 
110941b16dceSLen Brown MODULE_AUTHOR("Cezary Jackiewicz");
11109be0fcb5SRoald Frederickx MODULE_AUTHOR("Roald Frederickx (roald.frederickx@gmail.com)");
111141b16dceSLen Brown MODULE_DESCRIPTION("Compal Laptop Support");
11129be0fcb5SRoald Frederickx MODULE_VERSION(DRIVER_VERSION);
111341b16dceSLen Brown MODULE_LICENSE("GPL");
1114