1/*-*-linux-c-*-*/
2
3/*
4  Copyright (C) 2008 Cezary Jackiewicz <cezary.jackiewicz (at) gmail.com>
5
6  based on MSI driver
7
8  Copyright (C) 2006 Lennart Poettering <mzxreary (at) 0pointer (dot) de>
9
10  This program is free software; you can redistribute it and/or modify
11  it under the terms of the GNU General Public License as published by
12  the Free Software Foundation; either version 2 of the License, or
13  (at your option) any later version.
14
15  This program is distributed in the hope that it will be useful, but
16  WITHOUT ANY WARRANTY; without even the implied warranty of
17  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	See the GNU
18  General Public License for more details.
19
20  You should have received a copy of the GNU General Public License
21  along with this program; if not, write to the Free Software
22  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23  02110-1301, USA.
24 */
25
26/*
27 * compal-laptop.c - Compal laptop support.
28 *
29 * This driver exports a few files in /sys/devices/platform/compal-laptop/:
30 *   wake_up_XXX   Whether or not we listen to such wake up events (rw)
31 *
32 * In addition to these platform device attributes the driver
33 * registers itself in the Linux backlight control, power_supply, rfkill
34 * and hwmon subsystem and is available to userspace under:
35 *
36 *   /sys/class/backlight/compal-laptop/
37 *   /sys/class/power_supply/compal-laptop/
38 *   /sys/class/rfkill/rfkillX/
39 *   /sys/class/hwmon/hwmonX/
40 *
41 * Notes on the power_supply battery interface:
42 *   - the "minimum" design voltage is *the* design voltage
43 *   - the ambient temperature is the average battery temperature
44 *     and the value is an educated guess (see commented code below)
45 *
46 *
47 * This driver might work on other laptops produced by Compal. If you
48 * want to try it you can pass force=1 as argument to the module which
49 * will force it to load even when the DMI data doesn't identify the
50 * laptop as compatible.
51 *
52 * Lots of data available at:
53 * http://service1.marasst.com/Compal/JHL90_91/Service%20Manual/
54 * JHL90%20service%20manual-Final-0725.pdf
55 *
56 *
57 *
58 * Support for the Compal JHL90 added by Roald Frederickx
59 * (roald.frederickx@gmail.com):
60 * Driver got large revision. Added functionalities: backlight
61 * power, wake_on_XXX, a hwmon and power_supply interface.
62 *
63 * In case this gets merged into the kernel source: I want to dedicate this
64 * to Kasper Meerts, the awesome guy who showed me Linux and C!
65 */
66
67/* NOTE: currently the wake_on_XXX, hwmon and power_supply interfaces are
68 * only enabled on a JHL90 board until it is verified that they work on the
69 * other boards too.  See the extra_features variable. */
70
71#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
72
73#include <linux/module.h>
74#include <linux/kernel.h>
75#include <linux/init.h>
76#include <linux/acpi.h>
77#include <linux/dmi.h>
78#include <linux/backlight.h>
79#include <linux/platform_device.h>
80#include <linux/rfkill.h>
81#include <linux/hwmon.h>
82#include <linux/hwmon-sysfs.h>
83#include <linux/power_supply.h>
84#include <linux/fb.h>
85
86
87/* ======= */
88/* Defines */
89/* ======= */
90#define DRIVER_NAME "compal-laptop"
91#define DRIVER_VERSION	"0.2.7"
92
93#define BACKLIGHT_LEVEL_ADDR		0xB9
94#define BACKLIGHT_LEVEL_MAX		7
95#define BACKLIGHT_STATE_ADDR		0x59
96#define BACKLIGHT_STATE_ON_DATA		0xE1
97#define BACKLIGHT_STATE_OFF_DATA	0xE2
98
99#define WAKE_UP_ADDR			0xA4
100#define WAKE_UP_PME			(1 << 0)
101#define WAKE_UP_MODEM			(1 << 1)
102#define WAKE_UP_LAN			(1 << 2)
103#define WAKE_UP_WLAN			(1 << 4)
104#define WAKE_UP_KEY			(1 << 6)
105#define WAKE_UP_MOUSE			(1 << 7)
106
107#define WIRELESS_ADDR			0xBB
108#define WIRELESS_WLAN			(1 << 0)
109#define WIRELESS_BT			(1 << 1)
110#define WIRELESS_WLAN_EXISTS		(1 << 2)
111#define WIRELESS_BT_EXISTS		(1 << 3)
112#define WIRELESS_KILLSWITCH		(1 << 4)
113
114#define PWM_ADDRESS			0x46
115#define PWM_DISABLE_ADDR		0x59
116#define PWM_DISABLE_DATA		0xA5
117#define PWM_ENABLE_ADDR			0x59
118#define PWM_ENABLE_DATA			0xA8
119
120#define FAN_ADDRESS			0x46
121#define FAN_DATA			0x81
122#define FAN_FULL_ON_CMD			0x59 /* Doesn't seem to work. Just */
123#define FAN_FULL_ON_ENABLE		0x76 /* force the pwm signal to its */
124#define FAN_FULL_ON_DISABLE		0x77 /* maximum value instead */
125
126#define TEMP_CPU			0xB0
127#define TEMP_CPU_LOCAL			0xB1
128#define TEMP_CPU_DTS			0xB5
129#define TEMP_NORTHBRIDGE		0xB6
130#define TEMP_VGA			0xB4
131#define TEMP_SKIN			0xB2
132
133#define BAT_MANUFACTURER_NAME_ADDR	0x10
134#define BAT_MANUFACTURER_NAME_LEN	9
135#define BAT_MODEL_NAME_ADDR		0x19
136#define BAT_MODEL_NAME_LEN		6
137#define BAT_SERIAL_NUMBER_ADDR		0xC4
138#define BAT_SERIAL_NUMBER_LEN		5
139#define BAT_CHARGE_NOW			0xC2
140#define BAT_CHARGE_DESIGN		0xCA
141#define BAT_VOLTAGE_NOW			0xC6
142#define BAT_VOLTAGE_DESIGN		0xC8
143#define BAT_CURRENT_NOW			0xD0
144#define BAT_CURRENT_AVG			0xD2
145#define BAT_POWER			0xD4
146#define BAT_CAPACITY			0xCE
147#define BAT_TEMP			0xD6
148#define BAT_TEMP_AVG			0xD7
149#define BAT_STATUS0			0xC1
150#define BAT_STATUS1			0xF0
151#define BAT_STATUS2			0xF1
152#define BAT_STOP_CHARGE1		0xF2
153#define BAT_STOP_CHARGE2		0xF3
154
155#define BAT_S0_DISCHARGE		(1 << 0)
156#define BAT_S0_DISCHRG_CRITICAL		(1 << 2)
157#define BAT_S0_LOW			(1 << 3)
158#define BAT_S0_CHARGING			(1 << 1)
159#define BAT_S0_AC			(1 << 7)
160#define BAT_S1_EXISTS			(1 << 0)
161#define BAT_S1_FULL			(1 << 1)
162#define BAT_S1_EMPTY			(1 << 2)
163#define BAT_S1_LiION_OR_NiMH		(1 << 7)
164#define BAT_S2_LOW_LOW			(1 << 0)
165#define BAT_STOP_CHRG1_BAD_CELL		(1 << 1)
166#define BAT_STOP_CHRG1_COMM_FAIL	(1 << 2)
167#define BAT_STOP_CHRG1_OVERVOLTAGE	(1 << 6)
168#define BAT_STOP_CHRG1_OVERTEMPERATURE	(1 << 7)
169
170
171/* ======= */
172/* Structs */
173/* ======= */
174struct compal_data{
175	/* Fan control */
176	int pwm_enable; /* 0:full on, 1:set by pwm1, 2:control by motherboard */
177	unsigned char curr_pwm;
178
179	/* Power supply */
180	struct power_supply *psy;
181	struct power_supply_info psy_info;
182	char bat_model_name[BAT_MODEL_NAME_LEN + 1];
183	char bat_manufacturer_name[BAT_MANUFACTURER_NAME_LEN + 1];
184	char bat_serial_number[BAT_SERIAL_NUMBER_LEN + 1];
185};
186
187
188/* =============== */
189/* General globals */
190/* =============== */
191static bool force;
192module_param(force, bool, 0);
193MODULE_PARM_DESC(force, "Force driver load, ignore DMI data");
194
195/* Support for the wake_on_XXX, hwmon and power_supply interface. Currently
196 * only gets enabled on a JHL90 board. Might work with the others too */
197static bool extra_features;
198
199/* Nasty stuff. For some reason the fan control is very un-linear.  I've
200 * come up with these values by looping through the possible inputs and
201 * watching the output of address 0x4F (do an ec_transaction writing 0x33
202 * into 0x4F and read a few bytes from the output, like so:
203 *	u8 writeData = 0x33;
204 *	ec_transaction(0x4F, &writeData, 1, buffer, 32);
205 * That address is labeled "fan1 table information" in the service manual.
206 * It should be clear which value in 'buffer' changes). This seems to be
207 * related to fan speed. It isn't a proper 'realtime' fan speed value
208 * though, because physically stopping or speeding up the fan doesn't
209 * change it. It might be the average voltage or current of the pwm output.
210 * Nevertheless, it is more fine-grained than the actual RPM reading */
211static const unsigned char pwm_lookup_table[256] = {
212	0, 0, 0, 1, 1, 1, 2, 253, 254, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6,
213	7, 7, 7, 8, 86, 86, 9, 9, 9, 10, 10, 10, 11, 92, 92, 12, 12, 95,
214	13, 66, 66, 14, 14, 98, 15, 15, 15, 16, 16, 67, 17, 17, 72, 18, 70,
215	75, 19, 90, 90, 73, 73, 73, 21, 21, 91, 91, 91, 96, 23, 94, 94, 94,
216	94, 94, 94, 94, 94, 94, 94, 141, 141, 238, 223, 192, 139, 139, 139,
217	139, 139, 142, 142, 142, 142, 142, 78, 78, 78, 78, 78, 76, 76, 76,
218	76, 76, 79, 79, 79, 79, 79, 79, 79, 20, 20, 20, 20, 20, 22, 22, 22,
219	22, 22, 24, 24, 24, 24, 24, 24, 219, 219, 219, 219, 219, 219, 219,
220	219, 27, 27, 188, 188, 28, 28, 28, 29, 186, 186, 186, 186, 186,
221	186, 186, 186, 186, 186, 31, 31, 31, 31, 31, 32, 32, 32, 41, 33,
222	33, 33, 33, 33, 252, 252, 34, 34, 34, 43, 35, 35, 35, 36, 36, 38,
223	206, 206, 206, 206, 206, 206, 206, 206, 206, 37, 37, 37, 46, 46,
224	47, 47, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 48, 48,
225	48, 48, 48, 40, 40, 40, 49, 42, 42, 42, 42, 42, 42, 42, 42, 44,
226	189, 189, 189, 189, 54, 54, 45, 45, 45, 45, 45, 45, 45, 45, 251,
227	191, 199, 199, 199, 199, 199, 215, 215, 215, 215, 187, 187, 187,
228	187, 187, 193, 50
229};
230
231
232
233
234/* ========================= */
235/* Hardware access functions */
236/* ========================= */
237/* General access */
238static u8 ec_read_u8(u8 addr)
239{
240	u8 value;
241	ec_read(addr, &value);
242	return value;
243}
244
245static s8 ec_read_s8(u8 addr)
246{
247	return (s8)ec_read_u8(addr);
248}
249
250static u16 ec_read_u16(u8 addr)
251{
252	int hi, lo;
253	lo = ec_read_u8(addr);
254	hi = ec_read_u8(addr + 1);
255	return (hi << 8) + lo;
256}
257
258static s16 ec_read_s16(u8 addr)
259{
260	return (s16) ec_read_u16(addr);
261}
262
263static void ec_read_sequence(u8 addr, u8 *buf, int len)
264{
265	int i;
266	for (i = 0; i < len; i++)
267		ec_read(addr + i, buf + i);
268}
269
270
271/* Backlight access */
272static int set_backlight_level(int level)
273{
274	if (level < 0 || level > BACKLIGHT_LEVEL_MAX)
275		return -EINVAL;
276
277	ec_write(BACKLIGHT_LEVEL_ADDR, level);
278
279	return 0;
280}
281
282static int get_backlight_level(void)
283{
284	return (int) ec_read_u8(BACKLIGHT_LEVEL_ADDR);
285}
286
287static void set_backlight_state(bool on)
288{
289	u8 data = on ? BACKLIGHT_STATE_ON_DATA : BACKLIGHT_STATE_OFF_DATA;
290	ec_transaction(BACKLIGHT_STATE_ADDR, &data, 1, NULL, 0);
291}
292
293
294/* Fan control access */
295static void pwm_enable_control(void)
296{
297	unsigned char writeData = PWM_ENABLE_DATA;
298	ec_transaction(PWM_ENABLE_ADDR, &writeData, 1, NULL, 0);
299}
300
301static void pwm_disable_control(void)
302{
303	unsigned char writeData = PWM_DISABLE_DATA;
304	ec_transaction(PWM_DISABLE_ADDR, &writeData, 1, NULL, 0);
305}
306
307static void set_pwm(int pwm)
308{
309	ec_transaction(PWM_ADDRESS, &pwm_lookup_table[pwm], 1, NULL, 0);
310}
311
312static int get_fan_rpm(void)
313{
314	u8 value, data = FAN_DATA;
315	ec_transaction(FAN_ADDRESS, &data, 1, &value, 1);
316	return 100 * (int)value;
317}
318
319
320
321
322/* =================== */
323/* Interface functions */
324/* =================== */
325
326/* Backlight interface */
327static int bl_get_brightness(struct backlight_device *b)
328{
329	return get_backlight_level();
330}
331
332static int bl_update_status(struct backlight_device *b)
333{
334	int ret = set_backlight_level(b->props.brightness);
335	if (ret)
336		return ret;
337
338	set_backlight_state((b->props.power == FB_BLANK_UNBLANK)
339		&&    !(b->props.state & BL_CORE_SUSPENDED)
340		&&    !(b->props.state & BL_CORE_FBBLANK));
341	return 0;
342}
343
344static const struct backlight_ops compalbl_ops = {
345	.get_brightness = bl_get_brightness,
346	.update_status	= bl_update_status,
347};
348
349
350/* Wireless interface */
351static int compal_rfkill_set(void *data, bool blocked)
352{
353	unsigned long radio = (unsigned long) data;
354	u8 result = ec_read_u8(WIRELESS_ADDR);
355	u8 value;
356
357	if (!blocked)
358		value = (u8) (result | radio);
359	else
360		value = (u8) (result & ~radio);
361	ec_write(WIRELESS_ADDR, value);
362
363	return 0;
364}
365
366static void compal_rfkill_poll(struct rfkill *rfkill, void *data)
367{
368	u8 result = ec_read_u8(WIRELESS_ADDR);
369	bool hw_blocked = !(result & WIRELESS_KILLSWITCH);
370	rfkill_set_hw_state(rfkill, hw_blocked);
371}
372
373static const struct rfkill_ops compal_rfkill_ops = {
374	.poll = compal_rfkill_poll,
375	.set_block = compal_rfkill_set,
376};
377
378
379/* Wake_up interface */
380#define SIMPLE_MASKED_STORE_SHOW(NAME, ADDR, MASK)			\
381static ssize_t NAME##_show(struct device *dev,				\
382	struct device_attribute *attr, char *buf)			\
383{									\
384	return sprintf(buf, "%d\n", ((ec_read_u8(ADDR) & MASK) != 0));	\
385}									\
386static ssize_t NAME##_store(struct device *dev,				\
387	struct device_attribute *attr, const char *buf, size_t count)	\
388{									\
389	int state;							\
390	u8 old_val = ec_read_u8(ADDR);					\
391	if (sscanf(buf, "%d", &state) != 1 || (state < 0 || state > 1))	\
392		return -EINVAL;						\
393	ec_write(ADDR, state ? (old_val | MASK) : (old_val & ~MASK));	\
394	return count;							\
395}
396
397SIMPLE_MASKED_STORE_SHOW(wake_up_pme,	WAKE_UP_ADDR, WAKE_UP_PME)
398SIMPLE_MASKED_STORE_SHOW(wake_up_modem,	WAKE_UP_ADDR, WAKE_UP_MODEM)
399SIMPLE_MASKED_STORE_SHOW(wake_up_lan,	WAKE_UP_ADDR, WAKE_UP_LAN)
400SIMPLE_MASKED_STORE_SHOW(wake_up_wlan,	WAKE_UP_ADDR, WAKE_UP_WLAN)
401SIMPLE_MASKED_STORE_SHOW(wake_up_key,	WAKE_UP_ADDR, WAKE_UP_KEY)
402SIMPLE_MASKED_STORE_SHOW(wake_up_mouse,	WAKE_UP_ADDR, WAKE_UP_MOUSE)
403
404/* Fan control interface */
405static ssize_t pwm_enable_show(struct device *dev,
406		struct device_attribute *attr, char *buf)
407{
408	struct compal_data *data = dev_get_drvdata(dev);
409	return sprintf(buf, "%d\n", data->pwm_enable);
410}
411
412static ssize_t pwm_enable_store(struct device *dev,
413		struct device_attribute *attr, const char *buf, size_t count)
414{
415	struct compal_data *data = dev_get_drvdata(dev);
416	long val;
417	int err;
418
419	err = kstrtol(buf, 10, &val);
420	if (err)
421		return err;
422	if (val < 0)
423		return -EINVAL;
424
425	data->pwm_enable = val;
426
427	switch (val) {
428	case 0:  /* Full speed */
429		pwm_enable_control();
430		set_pwm(255);
431		break;
432	case 1:  /* As set by pwm1 */
433		pwm_enable_control();
434		set_pwm(data->curr_pwm);
435		break;
436	default: /* Control by motherboard */
437		pwm_disable_control();
438		break;
439	}
440
441	return count;
442}
443
444static ssize_t pwm_show(struct device *dev, struct device_attribute *attr,
445		char *buf)
446{
447	struct compal_data *data = dev_get_drvdata(dev);
448	return sprintf(buf, "%hhu\n", data->curr_pwm);
449}
450
451static ssize_t pwm_store(struct device *dev, struct device_attribute *attr,
452		const char *buf, size_t count)
453{
454	struct compal_data *data = dev_get_drvdata(dev);
455	long val;
456	int err;
457
458	err = kstrtol(buf, 10, &val);
459	if (err)
460		return err;
461	if (val < 0 || val > 255)
462		return -EINVAL;
463
464	data->curr_pwm = val;
465
466	if (data->pwm_enable != 1)
467		return count;
468	set_pwm(val);
469
470	return count;
471}
472
473static ssize_t fan_show(struct device *dev, struct device_attribute *attr,
474		char *buf)
475{
476	return sprintf(buf, "%d\n", get_fan_rpm());
477}
478
479
480/* Temperature interface */
481#define TEMPERATURE_SHOW_TEMP_AND_LABEL(POSTFIX, ADDRESS, LABEL)	\
482static ssize_t temp_##POSTFIX(struct device *dev,			\
483		struct device_attribute *attr, char *buf)		\
484{									\
485	return sprintf(buf, "%d\n", 1000 * (int)ec_read_s8(ADDRESS));	\
486}									\
487static ssize_t label_##POSTFIX(struct device *dev,			\
488		struct device_attribute *attr, char *buf)		\
489{									\
490	return sprintf(buf, "%s\n", LABEL);				\
491}
492
493/* Labels as in service guide */
494TEMPERATURE_SHOW_TEMP_AND_LABEL(cpu,        TEMP_CPU,        "CPU_TEMP");
495TEMPERATURE_SHOW_TEMP_AND_LABEL(cpu_local,  TEMP_CPU_LOCAL,  "CPU_TEMP_LOCAL");
496TEMPERATURE_SHOW_TEMP_AND_LABEL(cpu_DTS,    TEMP_CPU_DTS,    "CPU_DTS");
497TEMPERATURE_SHOW_TEMP_AND_LABEL(northbridge,TEMP_NORTHBRIDGE,"NorthBridge");
498TEMPERATURE_SHOW_TEMP_AND_LABEL(vga,        TEMP_VGA,        "VGA_TEMP");
499TEMPERATURE_SHOW_TEMP_AND_LABEL(SKIN,       TEMP_SKIN,       "SKIN_TEMP90");
500
501
502/* Power supply interface */
503static int bat_status(void)
504{
505	u8 status0 = ec_read_u8(BAT_STATUS0);
506	u8 status1 = ec_read_u8(BAT_STATUS1);
507
508	if (status0 & BAT_S0_CHARGING)
509		return POWER_SUPPLY_STATUS_CHARGING;
510	if (status0 & BAT_S0_DISCHARGE)
511		return POWER_SUPPLY_STATUS_DISCHARGING;
512	if (status1 & BAT_S1_FULL)
513		return POWER_SUPPLY_STATUS_FULL;
514	return POWER_SUPPLY_STATUS_NOT_CHARGING;
515}
516
517static int bat_health(void)
518{
519	u8 status = ec_read_u8(BAT_STOP_CHARGE1);
520
521	if (status & BAT_STOP_CHRG1_OVERTEMPERATURE)
522		return POWER_SUPPLY_HEALTH_OVERHEAT;
523	if (status & BAT_STOP_CHRG1_OVERVOLTAGE)
524		return POWER_SUPPLY_HEALTH_OVERVOLTAGE;
525	if (status & BAT_STOP_CHRG1_BAD_CELL)
526		return POWER_SUPPLY_HEALTH_DEAD;
527	if (status & BAT_STOP_CHRG1_COMM_FAIL)
528		return POWER_SUPPLY_HEALTH_UNKNOWN;
529	return POWER_SUPPLY_HEALTH_GOOD;
530}
531
532static int bat_is_present(void)
533{
534	u8 status = ec_read_u8(BAT_STATUS2);
535	return ((status & BAT_S1_EXISTS) != 0);
536}
537
538static int bat_technology(void)
539{
540	u8 status = ec_read_u8(BAT_STATUS1);
541
542	if (status & BAT_S1_LiION_OR_NiMH)
543		return POWER_SUPPLY_TECHNOLOGY_LION;
544	return POWER_SUPPLY_TECHNOLOGY_NiMH;
545}
546
547static int bat_capacity_level(void)
548{
549	u8 status0 = ec_read_u8(BAT_STATUS0);
550	u8 status1 = ec_read_u8(BAT_STATUS1);
551	u8 status2 = ec_read_u8(BAT_STATUS2);
552
553	if (status0 & BAT_S0_DISCHRG_CRITICAL
554			|| status1 & BAT_S1_EMPTY
555			|| status2 & BAT_S2_LOW_LOW)
556		return POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
557	if (status0 & BAT_S0_LOW)
558		return POWER_SUPPLY_CAPACITY_LEVEL_LOW;
559	if (status1 & BAT_S1_FULL)
560		return POWER_SUPPLY_CAPACITY_LEVEL_FULL;
561	return POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
562}
563
564static int bat_get_property(struct power_supply *psy,
565				enum power_supply_property psp,
566				union power_supply_propval *val)
567{
568	struct compal_data *data = power_supply_get_drvdata(psy);
569
570	switch (psp) {
571	case POWER_SUPPLY_PROP_STATUS:
572		val->intval = bat_status();
573		break;
574	case POWER_SUPPLY_PROP_HEALTH:
575		val->intval = bat_health();
576		break;
577	case POWER_SUPPLY_PROP_PRESENT:
578		val->intval = bat_is_present();
579		break;
580	case POWER_SUPPLY_PROP_TECHNOLOGY:
581		val->intval = bat_technology();
582		break;
583	case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: /* THE design voltage... */
584		val->intval = ec_read_u16(BAT_VOLTAGE_DESIGN) * 1000;
585		break;
586	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
587		val->intval = ec_read_u16(BAT_VOLTAGE_NOW) * 1000;
588		break;
589	case POWER_SUPPLY_PROP_CURRENT_NOW:
590		val->intval = ec_read_s16(BAT_CURRENT_NOW) * 1000;
591		break;
592	case POWER_SUPPLY_PROP_CURRENT_AVG:
593		val->intval = ec_read_s16(BAT_CURRENT_AVG) * 1000;
594		break;
595	case POWER_SUPPLY_PROP_POWER_NOW:
596		val->intval = ec_read_u8(BAT_POWER) * 1000000;
597		break;
598	case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
599		val->intval = ec_read_u16(BAT_CHARGE_DESIGN) * 1000;
600		break;
601	case POWER_SUPPLY_PROP_CHARGE_NOW:
602		val->intval = ec_read_u16(BAT_CHARGE_NOW) * 1000;
603		break;
604	case POWER_SUPPLY_PROP_CAPACITY:
605		val->intval = ec_read_u8(BAT_CAPACITY);
606		break;
607	case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
608		val->intval = bat_capacity_level();
609		break;
610	/* It smees that BAT_TEMP_AVG is a (2's complement?) value showing
611	 * the number of degrees, whereas BAT_TEMP is somewhat more
612	 * complicated. It looks like this is a negative nember with a
613	 * 100/256 divider and an offset of 222. Both were determined
614	 * experimentally by comparing BAT_TEMP and BAT_TEMP_AVG. */
615	case POWER_SUPPLY_PROP_TEMP:
616		val->intval = ((222 - (int)ec_read_u8(BAT_TEMP)) * 1000) >> 8;
617		break;
618	case POWER_SUPPLY_PROP_TEMP_AMBIENT: /* Ambient, Avg, ... same thing */
619		val->intval = ec_read_s8(BAT_TEMP_AVG) * 10;
620		break;
621	/* Neither the model name nor manufacturer name work for me. */
622	case POWER_SUPPLY_PROP_MODEL_NAME:
623		val->strval = data->bat_model_name;
624		break;
625	case POWER_SUPPLY_PROP_MANUFACTURER:
626		val->strval = data->bat_manufacturer_name;
627		break;
628	case POWER_SUPPLY_PROP_SERIAL_NUMBER:
629		val->strval = data->bat_serial_number;
630		break;
631	default:
632		break;
633	}
634	return 0;
635}
636
637
638
639
640
641/* ============== */
642/* Driver Globals */
643/* ============== */
644static DEVICE_ATTR(wake_up_pme,
645		0644, wake_up_pme_show,		wake_up_pme_store);
646static DEVICE_ATTR(wake_up_modem,
647		0644, wake_up_modem_show,	wake_up_modem_store);
648static DEVICE_ATTR(wake_up_lan,
649		0644, wake_up_lan_show,	wake_up_lan_store);
650static DEVICE_ATTR(wake_up_wlan,
651		0644, wake_up_wlan_show,	wake_up_wlan_store);
652static DEVICE_ATTR(wake_up_key,
653		0644, wake_up_key_show,	wake_up_key_store);
654static DEVICE_ATTR(wake_up_mouse,
655		0644, wake_up_mouse_show,	wake_up_mouse_store);
656
657static DEVICE_ATTR(fan1_input,  S_IRUGO, fan_show,          NULL);
658static DEVICE_ATTR(temp1_input, S_IRUGO, temp_cpu,          NULL);
659static DEVICE_ATTR(temp2_input, S_IRUGO, temp_cpu_local,    NULL);
660static DEVICE_ATTR(temp3_input, S_IRUGO, temp_cpu_DTS,      NULL);
661static DEVICE_ATTR(temp4_input, S_IRUGO, temp_northbridge,  NULL);
662static DEVICE_ATTR(temp5_input, S_IRUGO, temp_vga,          NULL);
663static DEVICE_ATTR(temp6_input, S_IRUGO, temp_SKIN,         NULL);
664static DEVICE_ATTR(temp1_label, S_IRUGO, label_cpu,         NULL);
665static DEVICE_ATTR(temp2_label, S_IRUGO, label_cpu_local,   NULL);
666static DEVICE_ATTR(temp3_label, S_IRUGO, label_cpu_DTS,     NULL);
667static DEVICE_ATTR(temp4_label, S_IRUGO, label_northbridge, NULL);
668static DEVICE_ATTR(temp5_label, S_IRUGO, label_vga,         NULL);
669static DEVICE_ATTR(temp6_label, S_IRUGO, label_SKIN,        NULL);
670static DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, pwm_show, pwm_store);
671static DEVICE_ATTR(pwm1_enable,
672		   S_IRUGO | S_IWUSR, pwm_enable_show, pwm_enable_store);
673
674static struct attribute *compal_platform_attrs[] = {
675	&dev_attr_wake_up_pme.attr,
676	&dev_attr_wake_up_modem.attr,
677	&dev_attr_wake_up_lan.attr,
678	&dev_attr_wake_up_wlan.attr,
679	&dev_attr_wake_up_key.attr,
680	&dev_attr_wake_up_mouse.attr,
681	NULL
682};
683static struct attribute_group compal_platform_attr_group = {
684	.attrs = compal_platform_attrs
685};
686
687static struct attribute *compal_hwmon_attrs[] = {
688	&dev_attr_pwm1_enable.attr,
689	&dev_attr_pwm1.attr,
690	&dev_attr_fan1_input.attr,
691	&dev_attr_temp1_input.attr,
692	&dev_attr_temp2_input.attr,
693	&dev_attr_temp3_input.attr,
694	&dev_attr_temp4_input.attr,
695	&dev_attr_temp5_input.attr,
696	&dev_attr_temp6_input.attr,
697	&dev_attr_temp1_label.attr,
698	&dev_attr_temp2_label.attr,
699	&dev_attr_temp3_label.attr,
700	&dev_attr_temp4_label.attr,
701	&dev_attr_temp5_label.attr,
702	&dev_attr_temp6_label.attr,
703	NULL
704};
705ATTRIBUTE_GROUPS(compal_hwmon);
706
707static int compal_probe(struct platform_device *);
708static int compal_remove(struct platform_device *);
709static struct platform_driver compal_driver = {
710	.driver = {
711		.name = DRIVER_NAME,
712	},
713	.probe	= compal_probe,
714	.remove	= compal_remove,
715};
716
717static enum power_supply_property compal_bat_properties[] = {
718	POWER_SUPPLY_PROP_STATUS,
719	POWER_SUPPLY_PROP_HEALTH,
720	POWER_SUPPLY_PROP_PRESENT,
721	POWER_SUPPLY_PROP_TECHNOLOGY,
722	POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
723	POWER_SUPPLY_PROP_VOLTAGE_NOW,
724	POWER_SUPPLY_PROP_CURRENT_NOW,
725	POWER_SUPPLY_PROP_CURRENT_AVG,
726	POWER_SUPPLY_PROP_POWER_NOW,
727	POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
728	POWER_SUPPLY_PROP_CHARGE_NOW,
729	POWER_SUPPLY_PROP_CAPACITY,
730	POWER_SUPPLY_PROP_CAPACITY_LEVEL,
731	POWER_SUPPLY_PROP_TEMP,
732	POWER_SUPPLY_PROP_TEMP_AMBIENT,
733	POWER_SUPPLY_PROP_MODEL_NAME,
734	POWER_SUPPLY_PROP_MANUFACTURER,
735	POWER_SUPPLY_PROP_SERIAL_NUMBER,
736};
737
738static struct backlight_device *compalbl_device;
739
740static struct platform_device *compal_device;
741
742static struct rfkill *wifi_rfkill;
743static struct rfkill *bt_rfkill;
744
745
746
747
748
749/* =================================== */
750/* Initialization & clean-up functions */
751/* =================================== */
752
753static int dmi_check_cb(const struct dmi_system_id *id)
754{
755	pr_info("Identified laptop model '%s'\n", id->ident);
756	extra_features = false;
757	return 1;
758}
759
760static int dmi_check_cb_extra(const struct dmi_system_id *id)
761{
762	pr_info("Identified laptop model '%s', enabling extra features\n",
763		id->ident);
764	extra_features = true;
765	return 1;
766}
767
768static struct dmi_system_id __initdata compal_dmi_table[] = {
769	{
770		.ident = "FL90/IFL90",
771		.matches = {
772			DMI_MATCH(DMI_BOARD_NAME, "IFL90"),
773			DMI_MATCH(DMI_BOARD_VERSION, "IFT00"),
774		},
775		.callback = dmi_check_cb
776	},
777	{
778		.ident = "FL90/IFL90",
779		.matches = {
780			DMI_MATCH(DMI_BOARD_NAME, "IFL90"),
781			DMI_MATCH(DMI_BOARD_VERSION, "REFERENCE"),
782		},
783		.callback = dmi_check_cb
784	},
785	{
786		.ident = "FL91/IFL91",
787		.matches = {
788			DMI_MATCH(DMI_BOARD_NAME, "IFL91"),
789			DMI_MATCH(DMI_BOARD_VERSION, "IFT00"),
790		},
791		.callback = dmi_check_cb
792	},
793	{
794		.ident = "FL92/JFL92",
795		.matches = {
796			DMI_MATCH(DMI_BOARD_NAME, "JFL92"),
797			DMI_MATCH(DMI_BOARD_VERSION, "IFT00"),
798		},
799		.callback = dmi_check_cb
800	},
801	{
802		.ident = "FT00/IFT00",
803		.matches = {
804			DMI_MATCH(DMI_BOARD_NAME, "IFT00"),
805			DMI_MATCH(DMI_BOARD_VERSION, "IFT00"),
806		},
807		.callback = dmi_check_cb
808	},
809	{
810		.ident = "Dell Mini 9",
811		.matches = {
812			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
813			DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 910"),
814		},
815		.callback = dmi_check_cb
816	},
817	{
818		.ident = "Dell Mini 10",
819		.matches = {
820			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
821			DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1010"),
822		},
823		.callback = dmi_check_cb
824	},
825	{
826		.ident = "Dell Mini 10v",
827		.matches = {
828			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
829			DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1011"),
830		},
831		.callback = dmi_check_cb
832	},
833	{
834		.ident = "Dell Mini 1012",
835		.matches = {
836			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
837			DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1012"),
838		},
839		.callback = dmi_check_cb
840	},
841	{
842		.ident = "Dell Inspiron 11z",
843		.matches = {
844			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
845			DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1110"),
846		},
847		.callback = dmi_check_cb
848	},
849	{
850		.ident = "Dell Mini 12",
851		.matches = {
852			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
853			DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1210"),
854		},
855		.callback = dmi_check_cb
856	},
857	{
858		.ident = "JHL90",
859		.matches = {
860			DMI_MATCH(DMI_BOARD_NAME, "JHL90"),
861			DMI_MATCH(DMI_BOARD_VERSION, "REFERENCE"),
862		},
863		.callback = dmi_check_cb_extra
864	},
865	{
866		.ident = "KHLB2",
867		.matches = {
868			DMI_MATCH(DMI_BOARD_NAME, "KHLB2"),
869			DMI_MATCH(DMI_BOARD_VERSION, "REFERENCE"),
870		},
871		.callback = dmi_check_cb_extra
872	},
873	{ }
874};
875MODULE_DEVICE_TABLE(dmi, compal_dmi_table);
876
877static const struct power_supply_desc psy_bat_desc = {
878	.name		= DRIVER_NAME,
879	.type		= POWER_SUPPLY_TYPE_BATTERY,
880	.properties	= compal_bat_properties,
881	.num_properties	= ARRAY_SIZE(compal_bat_properties),
882	.get_property	= bat_get_property,
883};
884
885static void initialize_power_supply_data(struct compal_data *data)
886{
887
888	ec_read_sequence(BAT_MANUFACTURER_NAME_ADDR,
889					data->bat_manufacturer_name,
890					BAT_MANUFACTURER_NAME_LEN);
891	data->bat_manufacturer_name[BAT_MANUFACTURER_NAME_LEN] = 0;
892
893	ec_read_sequence(BAT_MODEL_NAME_ADDR,
894					data->bat_model_name,
895					BAT_MODEL_NAME_LEN);
896	data->bat_model_name[BAT_MODEL_NAME_LEN] = 0;
897
898	scnprintf(data->bat_serial_number, BAT_SERIAL_NUMBER_LEN + 1, "%d",
899				ec_read_u16(BAT_SERIAL_NUMBER_ADDR));
900}
901
902static void initialize_fan_control_data(struct compal_data *data)
903{
904	data->pwm_enable = 2; /* Keep motherboard in control for now */
905	data->curr_pwm = 255; /* Try not to cause a CPU_on_fire exception
906				 if we take over... */
907}
908
909static int setup_rfkill(void)
910{
911	int ret;
912
913	wifi_rfkill = rfkill_alloc("compal-wifi", &compal_device->dev,
914				RFKILL_TYPE_WLAN, &compal_rfkill_ops,
915				(void *) WIRELESS_WLAN);
916	if (!wifi_rfkill)
917		return -ENOMEM;
918
919	ret = rfkill_register(wifi_rfkill);
920	if (ret)
921		goto err_wifi;
922
923	bt_rfkill = rfkill_alloc("compal-bluetooth", &compal_device->dev,
924				RFKILL_TYPE_BLUETOOTH, &compal_rfkill_ops,
925				(void *) WIRELESS_BT);
926	if (!bt_rfkill) {
927		ret = -ENOMEM;
928		goto err_allocate_bt;
929	}
930	ret = rfkill_register(bt_rfkill);
931	if (ret)
932		goto err_register_bt;
933
934	return 0;
935
936err_register_bt:
937	rfkill_destroy(bt_rfkill);
938
939err_allocate_bt:
940	rfkill_unregister(wifi_rfkill);
941
942err_wifi:
943	rfkill_destroy(wifi_rfkill);
944
945	return ret;
946}
947
948static int __init compal_init(void)
949{
950	int ret;
951
952	if (acpi_disabled) {
953		pr_err("ACPI needs to be enabled for this driver to work!\n");
954		return -ENODEV;
955	}
956
957	if (!force && !dmi_check_system(compal_dmi_table)) {
958		pr_err("Motherboard not recognized (You could try the module's force-parameter)\n");
959		return -ENODEV;
960	}
961
962	if (!acpi_video_backlight_support()) {
963		struct backlight_properties props;
964		memset(&props, 0, sizeof(struct backlight_properties));
965		props.type = BACKLIGHT_PLATFORM;
966		props.max_brightness = BACKLIGHT_LEVEL_MAX;
967		compalbl_device = backlight_device_register(DRIVER_NAME,
968							    NULL, NULL,
969							    &compalbl_ops,
970							    &props);
971		if (IS_ERR(compalbl_device))
972			return PTR_ERR(compalbl_device);
973	}
974
975	ret = platform_driver_register(&compal_driver);
976	if (ret)
977		goto err_backlight;
978
979	compal_device = platform_device_alloc(DRIVER_NAME, -1);
980	if (!compal_device) {
981		ret = -ENOMEM;
982		goto err_platform_driver;
983	}
984
985	ret = platform_device_add(compal_device); /* This calls compal_probe */
986	if (ret)
987		goto err_platform_device;
988
989	ret = setup_rfkill();
990	if (ret)
991		goto err_rfkill;
992
993	pr_info("Driver " DRIVER_VERSION " successfully loaded\n");
994	return 0;
995
996err_rfkill:
997	platform_device_del(compal_device);
998
999err_platform_device:
1000	platform_device_put(compal_device);
1001
1002err_platform_driver:
1003	platform_driver_unregister(&compal_driver);
1004
1005err_backlight:
1006	backlight_device_unregister(compalbl_device);
1007
1008	return ret;
1009}
1010
1011static int compal_probe(struct platform_device *pdev)
1012{
1013	int err;
1014	struct compal_data *data;
1015	struct device *hwmon_dev;
1016	struct power_supply_config psy_cfg = {};
1017
1018	if (!extra_features)
1019		return 0;
1020
1021	/* Fan control */
1022	data = devm_kzalloc(&pdev->dev, sizeof(struct compal_data), GFP_KERNEL);
1023	if (!data)
1024		return -ENOMEM;
1025
1026	initialize_fan_control_data(data);
1027
1028	err = sysfs_create_group(&pdev->dev.kobj, &compal_platform_attr_group);
1029	if (err)
1030		return err;
1031
1032	hwmon_dev = devm_hwmon_device_register_with_groups(&pdev->dev,
1033							   "compal", data,
1034							   compal_hwmon_groups);
1035	if (IS_ERR(hwmon_dev)) {
1036		err = PTR_ERR(hwmon_dev);
1037		goto remove;
1038	}
1039
1040	/* Power supply */
1041	initialize_power_supply_data(data);
1042	psy_cfg.drv_data = data;
1043	data->psy = power_supply_register(&compal_device->dev, &psy_bat_desc,
1044					  &psy_cfg);
1045	if (IS_ERR(data->psy)) {
1046		err = PTR_ERR(data->psy);
1047		goto remove;
1048	}
1049
1050	platform_set_drvdata(pdev, data);
1051
1052	return 0;
1053
1054remove:
1055	sysfs_remove_group(&pdev->dev.kobj, &compal_platform_attr_group);
1056	return err;
1057}
1058
1059static void __exit compal_cleanup(void)
1060{
1061	platform_device_unregister(compal_device);
1062	platform_driver_unregister(&compal_driver);
1063	backlight_device_unregister(compalbl_device);
1064	rfkill_unregister(wifi_rfkill);
1065	rfkill_unregister(bt_rfkill);
1066	rfkill_destroy(wifi_rfkill);
1067	rfkill_destroy(bt_rfkill);
1068
1069	pr_info("Driver unloaded\n");
1070}
1071
1072static int compal_remove(struct platform_device *pdev)
1073{
1074	struct compal_data *data;
1075
1076	if (!extra_features)
1077		return 0;
1078
1079	pr_info("Unloading: resetting fan control to motherboard\n");
1080	pwm_disable_control();
1081
1082	data = platform_get_drvdata(pdev);
1083	power_supply_unregister(data->psy);
1084
1085	sysfs_remove_group(&pdev->dev.kobj, &compal_platform_attr_group);
1086
1087	return 0;
1088}
1089
1090
1091module_init(compal_init);
1092module_exit(compal_cleanup);
1093
1094MODULE_AUTHOR("Cezary Jackiewicz");
1095MODULE_AUTHOR("Roald Frederickx (roald.frederickx@gmail.com)");
1096MODULE_DESCRIPTION("Compal Laptop Support");
1097MODULE_VERSION(DRIVER_VERSION);
1098MODULE_LICENSE("GPL");
1099