Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*-*-linux-c-*-*/
0003 
0004 /*
0005   Copyright (C) 2008 Cezary Jackiewicz <cezary.jackiewicz (at) gmail.com>
0006 
0007   based on MSI driver
0008 
0009   Copyright (C) 2006 Lennart Poettering <mzxreary (at) 0pointer (dot) de>
0010 
0011  */
0012 
0013 /*
0014  * compal-laptop.c - Compal laptop support.
0015  *
0016  * This driver exports a few files in /sys/devices/platform/compal-laptop/:
0017  *   wake_up_XXX   Whether or not we listen to such wake up events (rw)
0018  *
0019  * In addition to these platform device attributes the driver
0020  * registers itself in the Linux backlight control, power_supply, rfkill
0021  * and hwmon subsystem and is available to userspace under:
0022  *
0023  *   /sys/class/backlight/compal-laptop/
0024  *   /sys/class/power_supply/compal-laptop/
0025  *   /sys/class/rfkill/rfkillX/
0026  *   /sys/class/hwmon/hwmonX/
0027  *
0028  * Notes on the power_supply battery interface:
0029  *   - the "minimum" design voltage is *the* design voltage
0030  *   - the ambient temperature is the average battery temperature
0031  *     and the value is an educated guess (see commented code below)
0032  *
0033  *
0034  * This driver might work on other laptops produced by Compal. If you
0035  * want to try it you can pass force=1 as argument to the module which
0036  * will force it to load even when the DMI data doesn't identify the
0037  * laptop as compatible.
0038  *
0039  * Lots of data available at:
0040  * http://service1.marasst.com/Compal/JHL90_91/Service%20Manual/
0041  * JHL90%20service%20manual-Final-0725.pdf
0042  *
0043  *
0044  *
0045  * Support for the Compal JHL90 added by Roald Frederickx
0046  * (roald.frederickx@gmail.com):
0047  * Driver got large revision. Added functionalities: backlight
0048  * power, wake_on_XXX, a hwmon and power_supply interface.
0049  *
0050  * In case this gets merged into the kernel source: I want to dedicate this
0051  * to Kasper Meerts, the awesome guy who showed me Linux and C!
0052  */
0053 
0054 /* NOTE: currently the wake_on_XXX, hwmon and power_supply interfaces are
0055  * only enabled on a JHL90 board until it is verified that they work on the
0056  * other boards too.  See the extra_features variable. */
0057 
0058 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0059 
0060 #include <linux/module.h>
0061 #include <linux/kernel.h>
0062 #include <linux/init.h>
0063 #include <linux/acpi.h>
0064 #include <linux/dmi.h>
0065 #include <linux/backlight.h>
0066 #include <linux/platform_device.h>
0067 #include <linux/rfkill.h>
0068 #include <linux/hwmon.h>
0069 #include <linux/hwmon-sysfs.h>
0070 #include <linux/power_supply.h>
0071 #include <linux/fb.h>
0072 #include <acpi/video.h>
0073 
0074 /* ======= */
0075 /* Defines */
0076 /* ======= */
0077 #define DRIVER_NAME "compal-laptop"
0078 #define DRIVER_VERSION  "0.2.7"
0079 
0080 #define BACKLIGHT_LEVEL_ADDR        0xB9
0081 #define BACKLIGHT_LEVEL_MAX     7
0082 #define BACKLIGHT_STATE_ADDR        0x59
0083 #define BACKLIGHT_STATE_ON_DATA     0xE1
0084 #define BACKLIGHT_STATE_OFF_DATA    0xE2
0085 
0086 #define WAKE_UP_ADDR            0xA4
0087 #define WAKE_UP_PME         (1 << 0)
0088 #define WAKE_UP_MODEM           (1 << 1)
0089 #define WAKE_UP_LAN         (1 << 2)
0090 #define WAKE_UP_WLAN            (1 << 4)
0091 #define WAKE_UP_KEY         (1 << 6)
0092 #define WAKE_UP_MOUSE           (1 << 7)
0093 
0094 #define WIRELESS_ADDR           0xBB
0095 #define WIRELESS_WLAN           (1 << 0)
0096 #define WIRELESS_BT         (1 << 1)
0097 #define WIRELESS_WLAN_EXISTS        (1 << 2)
0098 #define WIRELESS_BT_EXISTS      (1 << 3)
0099 #define WIRELESS_KILLSWITCH     (1 << 4)
0100 
0101 #define PWM_ADDRESS         0x46
0102 #define PWM_DISABLE_ADDR        0x59
0103 #define PWM_DISABLE_DATA        0xA5
0104 #define PWM_ENABLE_ADDR         0x59
0105 #define PWM_ENABLE_DATA         0xA8
0106 
0107 #define FAN_ADDRESS         0x46
0108 #define FAN_DATA            0x81
0109 #define FAN_FULL_ON_CMD         0x59 /* Doesn't seem to work. Just */
0110 #define FAN_FULL_ON_ENABLE      0x76 /* force the pwm signal to its */
0111 #define FAN_FULL_ON_DISABLE     0x77 /* maximum value instead */
0112 
0113 #define TEMP_CPU            0xB0
0114 #define TEMP_CPU_LOCAL          0xB1
0115 #define TEMP_CPU_DTS            0xB5
0116 #define TEMP_NORTHBRIDGE        0xB6
0117 #define TEMP_VGA            0xB4
0118 #define TEMP_SKIN           0xB2
0119 
0120 #define BAT_MANUFACTURER_NAME_ADDR  0x10
0121 #define BAT_MANUFACTURER_NAME_LEN   9
0122 #define BAT_MODEL_NAME_ADDR     0x19
0123 #define BAT_MODEL_NAME_LEN      6
0124 #define BAT_SERIAL_NUMBER_ADDR      0xC4
0125 #define BAT_SERIAL_NUMBER_LEN       5
0126 #define BAT_CHARGE_NOW          0xC2
0127 #define BAT_CHARGE_DESIGN       0xCA
0128 #define BAT_VOLTAGE_NOW         0xC6
0129 #define BAT_VOLTAGE_DESIGN      0xC8
0130 #define BAT_CURRENT_NOW         0xD0
0131 #define BAT_CURRENT_AVG         0xD2
0132 #define BAT_POWER           0xD4
0133 #define BAT_CAPACITY            0xCE
0134 #define BAT_TEMP            0xD6
0135 #define BAT_TEMP_AVG            0xD7
0136 #define BAT_STATUS0         0xC1
0137 #define BAT_STATUS1         0xF0
0138 #define BAT_STATUS2         0xF1
0139 #define BAT_STOP_CHARGE1        0xF2
0140 #define BAT_STOP_CHARGE2        0xF3
0141 #define BAT_CHARGE_LIMIT        0x03
0142 #define BAT_CHARGE_LIMIT_MAX        100
0143 
0144 #define BAT_S0_DISCHARGE        (1 << 0)
0145 #define BAT_S0_DISCHRG_CRITICAL     (1 << 2)
0146 #define BAT_S0_LOW          (1 << 3)
0147 #define BAT_S0_CHARGING         (1 << 1)
0148 #define BAT_S0_AC           (1 << 7)
0149 #define BAT_S1_EXISTS           (1 << 0)
0150 #define BAT_S1_FULL         (1 << 1)
0151 #define BAT_S1_EMPTY            (1 << 2)
0152 #define BAT_S1_LiION_OR_NiMH        (1 << 7)
0153 #define BAT_S2_LOW_LOW          (1 << 0)
0154 #define BAT_STOP_CHRG1_BAD_CELL     (1 << 1)
0155 #define BAT_STOP_CHRG1_COMM_FAIL    (1 << 2)
0156 #define BAT_STOP_CHRG1_OVERVOLTAGE  (1 << 6)
0157 #define BAT_STOP_CHRG1_OVERTEMPERATURE  (1 << 7)
0158 
0159 
0160 /* ======= */
0161 /* Structs */
0162 /* ======= */
0163 struct compal_data{
0164     /* Fan control */
0165     int pwm_enable; /* 0:full on, 1:set by pwm1, 2:control by motherboard */
0166     unsigned char curr_pwm;
0167 
0168     /* Power supply */
0169     struct power_supply *psy;
0170     struct power_supply_info psy_info;
0171     char bat_model_name[BAT_MODEL_NAME_LEN + 1];
0172     char bat_manufacturer_name[BAT_MANUFACTURER_NAME_LEN + 1];
0173     char bat_serial_number[BAT_SERIAL_NUMBER_LEN + 1];
0174 };
0175 
0176 
0177 /* =============== */
0178 /* General globals */
0179 /* =============== */
0180 static bool force;
0181 module_param(force, bool, 0);
0182 MODULE_PARM_DESC(force, "Force driver load, ignore DMI data");
0183 
0184 /* Support for the wake_on_XXX, hwmon and power_supply interface. Currently
0185  * only gets enabled on a JHL90 board. Might work with the others too */
0186 static bool extra_features;
0187 
0188 /* Nasty stuff. For some reason the fan control is very un-linear.  I've
0189  * come up with these values by looping through the possible inputs and
0190  * watching the output of address 0x4F (do an ec_transaction writing 0x33
0191  * into 0x4F and read a few bytes from the output, like so:
0192  *  u8 writeData = 0x33;
0193  *  ec_transaction(0x4F, &writeData, 1, buffer, 32);
0194  * That address is labeled "fan1 table information" in the service manual.
0195  * It should be clear which value in 'buffer' changes). This seems to be
0196  * related to fan speed. It isn't a proper 'realtime' fan speed value
0197  * though, because physically stopping or speeding up the fan doesn't
0198  * change it. It might be the average voltage or current of the pwm output.
0199  * Nevertheless, it is more fine-grained than the actual RPM reading */
0200 static const unsigned char pwm_lookup_table[256] = {
0201     0, 0, 0, 1, 1, 1, 2, 253, 254, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6,
0202     7, 7, 7, 8, 86, 86, 9, 9, 9, 10, 10, 10, 11, 92, 92, 12, 12, 95,
0203     13, 66, 66, 14, 14, 98, 15, 15, 15, 16, 16, 67, 17, 17, 72, 18, 70,
0204     75, 19, 90, 90, 73, 73, 73, 21, 21, 91, 91, 91, 96, 23, 94, 94, 94,
0205     94, 94, 94, 94, 94, 94, 94, 141, 141, 238, 223, 192, 139, 139, 139,
0206     139, 139, 142, 142, 142, 142, 142, 78, 78, 78, 78, 78, 76, 76, 76,
0207     76, 76, 79, 79, 79, 79, 79, 79, 79, 20, 20, 20, 20, 20, 22, 22, 22,
0208     22, 22, 24, 24, 24, 24, 24, 24, 219, 219, 219, 219, 219, 219, 219,
0209     219, 27, 27, 188, 188, 28, 28, 28, 29, 186, 186, 186, 186, 186,
0210     186, 186, 186, 186, 186, 31, 31, 31, 31, 31, 32, 32, 32, 41, 33,
0211     33, 33, 33, 33, 252, 252, 34, 34, 34, 43, 35, 35, 35, 36, 36, 38,
0212     206, 206, 206, 206, 206, 206, 206, 206, 206, 37, 37, 37, 46, 46,
0213     47, 47, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 48, 48,
0214     48, 48, 48, 40, 40, 40, 49, 42, 42, 42, 42, 42, 42, 42, 42, 44,
0215     189, 189, 189, 189, 54, 54, 45, 45, 45, 45, 45, 45, 45, 45, 251,
0216     191, 199, 199, 199, 199, 199, 215, 215, 215, 215, 187, 187, 187,
0217     187, 187, 193, 50
0218 };
0219 
0220 
0221 
0222 
0223 /* ========================= */
0224 /* Hardware access functions */
0225 /* ========================= */
0226 /* General access */
0227 static u8 ec_read_u8(u8 addr)
0228 {
0229     u8 value = 0;
0230     ec_read(addr, &value);
0231     return value;
0232 }
0233 
0234 static s8 ec_read_s8(u8 addr)
0235 {
0236     return (s8)ec_read_u8(addr);
0237 }
0238 
0239 static u16 ec_read_u16(u8 addr)
0240 {
0241     int hi, lo;
0242     lo = ec_read_u8(addr);
0243     hi = ec_read_u8(addr + 1);
0244     return (hi << 8) + lo;
0245 }
0246 
0247 static s16 ec_read_s16(u8 addr)
0248 {
0249     return (s16) ec_read_u16(addr);
0250 }
0251 
0252 static void ec_read_sequence(u8 addr, u8 *buf, int len)
0253 {
0254     int i;
0255     for (i = 0; i < len; i++)
0256         ec_read(addr + i, buf + i);
0257 }
0258 
0259 
0260 /* Backlight access */
0261 static int set_backlight_level(int level)
0262 {
0263     if (level < 0 || level > BACKLIGHT_LEVEL_MAX)
0264         return -EINVAL;
0265 
0266     ec_write(BACKLIGHT_LEVEL_ADDR, level);
0267 
0268     return 0;
0269 }
0270 
0271 static int get_backlight_level(void)
0272 {
0273     return (int) ec_read_u8(BACKLIGHT_LEVEL_ADDR);
0274 }
0275 
0276 static void set_backlight_state(bool on)
0277 {
0278     u8 data = on ? BACKLIGHT_STATE_ON_DATA : BACKLIGHT_STATE_OFF_DATA;
0279     ec_transaction(BACKLIGHT_STATE_ADDR, &data, 1, NULL, 0);
0280 }
0281 
0282 
0283 /* Fan control access */
0284 static void pwm_enable_control(void)
0285 {
0286     unsigned char writeData = PWM_ENABLE_DATA;
0287     ec_transaction(PWM_ENABLE_ADDR, &writeData, 1, NULL, 0);
0288 }
0289 
0290 static void pwm_disable_control(void)
0291 {
0292     unsigned char writeData = PWM_DISABLE_DATA;
0293     ec_transaction(PWM_DISABLE_ADDR, &writeData, 1, NULL, 0);
0294 }
0295 
0296 static void set_pwm(int pwm)
0297 {
0298     ec_transaction(PWM_ADDRESS, &pwm_lookup_table[pwm], 1, NULL, 0);
0299 }
0300 
0301 static int get_fan_rpm(void)
0302 {
0303     u8 value, data = FAN_DATA;
0304     ec_transaction(FAN_ADDRESS, &data, 1, &value, 1);
0305     return 100 * (int)value;
0306 }
0307 
0308 
0309 
0310 
0311 /* =================== */
0312 /* Interface functions */
0313 /* =================== */
0314 
0315 /* Backlight interface */
0316 static int bl_get_brightness(struct backlight_device *b)
0317 {
0318     return get_backlight_level();
0319 }
0320 
0321 static int bl_update_status(struct backlight_device *b)
0322 {
0323     int ret = set_backlight_level(b->props.brightness);
0324     if (ret)
0325         return ret;
0326 
0327     set_backlight_state(!backlight_is_blank(b));
0328     return 0;
0329 }
0330 
0331 static const struct backlight_ops compalbl_ops = {
0332     .get_brightness = bl_get_brightness,
0333     .update_status  = bl_update_status,
0334 };
0335 
0336 
0337 /* Wireless interface */
0338 static int compal_rfkill_set(void *data, bool blocked)
0339 {
0340     unsigned long radio = (unsigned long) data;
0341     u8 result = ec_read_u8(WIRELESS_ADDR);
0342     u8 value;
0343 
0344     if (!blocked)
0345         value = (u8) (result | radio);
0346     else
0347         value = (u8) (result & ~radio);
0348     ec_write(WIRELESS_ADDR, value);
0349 
0350     return 0;
0351 }
0352 
0353 static void compal_rfkill_poll(struct rfkill *rfkill, void *data)
0354 {
0355     u8 result = ec_read_u8(WIRELESS_ADDR);
0356     bool hw_blocked = !(result & WIRELESS_KILLSWITCH);
0357     rfkill_set_hw_state(rfkill, hw_blocked);
0358 }
0359 
0360 static const struct rfkill_ops compal_rfkill_ops = {
0361     .poll = compal_rfkill_poll,
0362     .set_block = compal_rfkill_set,
0363 };
0364 
0365 
0366 /* Wake_up interface */
0367 #define SIMPLE_MASKED_STORE_SHOW(NAME, ADDR, MASK)          \
0368 static ssize_t NAME##_show(struct device *dev,              \
0369     struct device_attribute *attr, char *buf)           \
0370 {                                   \
0371     return sprintf(buf, "%d\n", ((ec_read_u8(ADDR) & MASK) != 0));  \
0372 }                                   \
0373 static ssize_t NAME##_store(struct device *dev,             \
0374     struct device_attribute *attr, const char *buf, size_t count)   \
0375 {                                   \
0376     int state;                          \
0377     u8 old_val = ec_read_u8(ADDR);                  \
0378     if (sscanf(buf, "%d", &state) != 1 || (state < 0 || state > 1)) \
0379         return -EINVAL;                     \
0380     ec_write(ADDR, state ? (old_val | MASK) : (old_val & ~MASK));   \
0381     return count;                           \
0382 }
0383 
0384 SIMPLE_MASKED_STORE_SHOW(wake_up_pme,   WAKE_UP_ADDR, WAKE_UP_PME)
0385 SIMPLE_MASKED_STORE_SHOW(wake_up_modem, WAKE_UP_ADDR, WAKE_UP_MODEM)
0386 SIMPLE_MASKED_STORE_SHOW(wake_up_lan,   WAKE_UP_ADDR, WAKE_UP_LAN)
0387 SIMPLE_MASKED_STORE_SHOW(wake_up_wlan,  WAKE_UP_ADDR, WAKE_UP_WLAN)
0388 SIMPLE_MASKED_STORE_SHOW(wake_up_key,   WAKE_UP_ADDR, WAKE_UP_KEY)
0389 SIMPLE_MASKED_STORE_SHOW(wake_up_mouse, WAKE_UP_ADDR, WAKE_UP_MOUSE)
0390 
0391 /* Fan control interface */
0392 static ssize_t pwm_enable_show(struct device *dev,
0393         struct device_attribute *attr, char *buf)
0394 {
0395     struct compal_data *data = dev_get_drvdata(dev);
0396     return sprintf(buf, "%d\n", data->pwm_enable);
0397 }
0398 
0399 static ssize_t pwm_enable_store(struct device *dev,
0400         struct device_attribute *attr, const char *buf, size_t count)
0401 {
0402     struct compal_data *data = dev_get_drvdata(dev);
0403     long val;
0404     int err;
0405 
0406     err = kstrtol(buf, 10, &val);
0407     if (err)
0408         return err;
0409     if (val < 0)
0410         return -EINVAL;
0411 
0412     data->pwm_enable = val;
0413 
0414     switch (val) {
0415     case 0:  /* Full speed */
0416         pwm_enable_control();
0417         set_pwm(255);
0418         break;
0419     case 1:  /* As set by pwm1 */
0420         pwm_enable_control();
0421         set_pwm(data->curr_pwm);
0422         break;
0423     default: /* Control by motherboard */
0424         pwm_disable_control();
0425         break;
0426     }
0427 
0428     return count;
0429 }
0430 
0431 static ssize_t pwm_show(struct device *dev, struct device_attribute *attr,
0432         char *buf)
0433 {
0434     struct compal_data *data = dev_get_drvdata(dev);
0435     return sprintf(buf, "%hhu\n", data->curr_pwm);
0436 }
0437 
0438 static ssize_t pwm_store(struct device *dev, struct device_attribute *attr,
0439         const char *buf, size_t count)
0440 {
0441     struct compal_data *data = dev_get_drvdata(dev);
0442     long val;
0443     int err;
0444 
0445     err = kstrtol(buf, 10, &val);
0446     if (err)
0447         return err;
0448     if (val < 0 || val > 255)
0449         return -EINVAL;
0450 
0451     data->curr_pwm = val;
0452 
0453     if (data->pwm_enable != 1)
0454         return count;
0455     set_pwm(val);
0456 
0457     return count;
0458 }
0459 
0460 static ssize_t fan_show(struct device *dev, struct device_attribute *attr,
0461         char *buf)
0462 {
0463     return sprintf(buf, "%d\n", get_fan_rpm());
0464 }
0465 
0466 
0467 /* Temperature interface */
0468 #define TEMPERATURE_SHOW_TEMP_AND_LABEL(POSTFIX, ADDRESS, LABEL)    \
0469 static ssize_t temp_##POSTFIX(struct device *dev,           \
0470         struct device_attribute *attr, char *buf)       \
0471 {                                   \
0472     return sprintf(buf, "%d\n", 1000 * (int)ec_read_s8(ADDRESS));   \
0473 }                                   \
0474 static ssize_t label_##POSTFIX(struct device *dev,          \
0475         struct device_attribute *attr, char *buf)       \
0476 {                                   \
0477     return sprintf(buf, "%s\n", LABEL);             \
0478 }
0479 
0480 /* Labels as in service guide */
0481 TEMPERATURE_SHOW_TEMP_AND_LABEL(cpu,        TEMP_CPU,        "CPU_TEMP");
0482 TEMPERATURE_SHOW_TEMP_AND_LABEL(cpu_local,  TEMP_CPU_LOCAL,  "CPU_TEMP_LOCAL");
0483 TEMPERATURE_SHOW_TEMP_AND_LABEL(cpu_DTS,    TEMP_CPU_DTS,    "CPU_DTS");
0484 TEMPERATURE_SHOW_TEMP_AND_LABEL(northbridge,TEMP_NORTHBRIDGE,"NorthBridge");
0485 TEMPERATURE_SHOW_TEMP_AND_LABEL(vga,        TEMP_VGA,        "VGA_TEMP");
0486 TEMPERATURE_SHOW_TEMP_AND_LABEL(SKIN,       TEMP_SKIN,       "SKIN_TEMP90");
0487 
0488 
0489 /* Power supply interface */
0490 static int bat_status(void)
0491 {
0492     u8 status0 = ec_read_u8(BAT_STATUS0);
0493     u8 status1 = ec_read_u8(BAT_STATUS1);
0494 
0495     if (status0 & BAT_S0_CHARGING)
0496         return POWER_SUPPLY_STATUS_CHARGING;
0497     if (status0 & BAT_S0_DISCHARGE)
0498         return POWER_SUPPLY_STATUS_DISCHARGING;
0499     if (status1 & BAT_S1_FULL)
0500         return POWER_SUPPLY_STATUS_FULL;
0501     return POWER_SUPPLY_STATUS_NOT_CHARGING;
0502 }
0503 
0504 static int bat_health(void)
0505 {
0506     u8 status = ec_read_u8(BAT_STOP_CHARGE1);
0507 
0508     if (status & BAT_STOP_CHRG1_OVERTEMPERATURE)
0509         return POWER_SUPPLY_HEALTH_OVERHEAT;
0510     if (status & BAT_STOP_CHRG1_OVERVOLTAGE)
0511         return POWER_SUPPLY_HEALTH_OVERVOLTAGE;
0512     if (status & BAT_STOP_CHRG1_BAD_CELL)
0513         return POWER_SUPPLY_HEALTH_DEAD;
0514     if (status & BAT_STOP_CHRG1_COMM_FAIL)
0515         return POWER_SUPPLY_HEALTH_UNKNOWN;
0516     return POWER_SUPPLY_HEALTH_GOOD;
0517 }
0518 
0519 static int bat_is_present(void)
0520 {
0521     u8 status = ec_read_u8(BAT_STATUS2);
0522     return ((status & BAT_S1_EXISTS) != 0);
0523 }
0524 
0525 static int bat_technology(void)
0526 {
0527     u8 status = ec_read_u8(BAT_STATUS1);
0528 
0529     if (status & BAT_S1_LiION_OR_NiMH)
0530         return POWER_SUPPLY_TECHNOLOGY_LION;
0531     return POWER_SUPPLY_TECHNOLOGY_NiMH;
0532 }
0533 
0534 static int bat_capacity_level(void)
0535 {
0536     u8 status0 = ec_read_u8(BAT_STATUS0);
0537     u8 status1 = ec_read_u8(BAT_STATUS1);
0538     u8 status2 = ec_read_u8(BAT_STATUS2);
0539 
0540     if (status0 & BAT_S0_DISCHRG_CRITICAL
0541             || status1 & BAT_S1_EMPTY
0542             || status2 & BAT_S2_LOW_LOW)
0543         return POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
0544     if (status0 & BAT_S0_LOW)
0545         return POWER_SUPPLY_CAPACITY_LEVEL_LOW;
0546     if (status1 & BAT_S1_FULL)
0547         return POWER_SUPPLY_CAPACITY_LEVEL_FULL;
0548     return POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
0549 }
0550 
0551 static int bat_get_property(struct power_supply *psy,
0552                 enum power_supply_property psp,
0553                 union power_supply_propval *val)
0554 {
0555     struct compal_data *data = power_supply_get_drvdata(psy);
0556 
0557     switch (psp) {
0558     case POWER_SUPPLY_PROP_STATUS:
0559         val->intval = bat_status();
0560         break;
0561     case POWER_SUPPLY_PROP_HEALTH:
0562         val->intval = bat_health();
0563         break;
0564     case POWER_SUPPLY_PROP_PRESENT:
0565         val->intval = bat_is_present();
0566         break;
0567     case POWER_SUPPLY_PROP_TECHNOLOGY:
0568         val->intval = bat_technology();
0569         break;
0570     case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: /* THE design voltage... */
0571         val->intval = ec_read_u16(BAT_VOLTAGE_DESIGN) * 1000;
0572         break;
0573     case POWER_SUPPLY_PROP_VOLTAGE_NOW:
0574         val->intval = ec_read_u16(BAT_VOLTAGE_NOW) * 1000;
0575         break;
0576     case POWER_SUPPLY_PROP_CURRENT_NOW:
0577         val->intval = ec_read_s16(BAT_CURRENT_NOW) * 1000;
0578         break;
0579     case POWER_SUPPLY_PROP_CURRENT_AVG:
0580         val->intval = ec_read_s16(BAT_CURRENT_AVG) * 1000;
0581         break;
0582     case POWER_SUPPLY_PROP_POWER_NOW:
0583         val->intval = ec_read_u8(BAT_POWER) * 1000000;
0584         break;
0585     case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
0586         val->intval = ec_read_u16(BAT_CHARGE_DESIGN) * 1000;
0587         break;
0588     case POWER_SUPPLY_PROP_CHARGE_NOW:
0589         val->intval = ec_read_u16(BAT_CHARGE_NOW) * 1000;
0590         break;
0591     case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
0592         val->intval = ec_read_u8(BAT_CHARGE_LIMIT);
0593         break;
0594     case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX:
0595         val->intval = BAT_CHARGE_LIMIT_MAX;
0596         break;
0597     case POWER_SUPPLY_PROP_CAPACITY:
0598         val->intval = ec_read_u8(BAT_CAPACITY);
0599         break;
0600     case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
0601         val->intval = bat_capacity_level();
0602         break;
0603     /* It smees that BAT_TEMP_AVG is a (2's complement?) value showing
0604      * the number of degrees, whereas BAT_TEMP is somewhat more
0605      * complicated. It looks like this is a negative nember with a
0606      * 100/256 divider and an offset of 222. Both were determined
0607      * experimentally by comparing BAT_TEMP and BAT_TEMP_AVG. */
0608     case POWER_SUPPLY_PROP_TEMP:
0609         val->intval = ((222 - (int)ec_read_u8(BAT_TEMP)) * 1000) >> 8;
0610         break;
0611     case POWER_SUPPLY_PROP_TEMP_AMBIENT: /* Ambient, Avg, ... same thing */
0612         val->intval = ec_read_s8(BAT_TEMP_AVG) * 10;
0613         break;
0614     /* Neither the model name nor manufacturer name work for me. */
0615     case POWER_SUPPLY_PROP_MODEL_NAME:
0616         val->strval = data->bat_model_name;
0617         break;
0618     case POWER_SUPPLY_PROP_MANUFACTURER:
0619         val->strval = data->bat_manufacturer_name;
0620         break;
0621     case POWER_SUPPLY_PROP_SERIAL_NUMBER:
0622         val->strval = data->bat_serial_number;
0623         break;
0624     default:
0625         break;
0626     }
0627     return 0;
0628 }
0629 
0630 static int bat_set_property(struct power_supply *psy,
0631                 enum power_supply_property psp,
0632                 const union power_supply_propval *val)
0633 {
0634     int level;
0635 
0636     switch (psp) {
0637     case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
0638         level = val->intval;
0639         if (level < 0 || level > BAT_CHARGE_LIMIT_MAX)
0640             return -EINVAL;
0641         if (ec_write(BAT_CHARGE_LIMIT, level) < 0)
0642             return -EIO;
0643         break;
0644     default:
0645         break;
0646     }
0647     return 0;
0648 }
0649 
0650 static int bat_writeable_property(struct power_supply *psy,
0651                 enum power_supply_property psp)
0652 {
0653     switch (psp) {
0654     case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
0655         return 1;
0656     default:
0657         return 0;
0658     }
0659 }
0660 
0661 
0662 
0663 
0664 /* ============== */
0665 /* Driver Globals */
0666 /* ============== */
0667 static DEVICE_ATTR_RW(wake_up_pme);
0668 static DEVICE_ATTR_RW(wake_up_modem);
0669 static DEVICE_ATTR_RW(wake_up_lan);
0670 static DEVICE_ATTR_RW(wake_up_wlan);
0671 static DEVICE_ATTR_RW(wake_up_key);
0672 static DEVICE_ATTR_RW(wake_up_mouse);
0673 
0674 static DEVICE_ATTR(fan1_input,  S_IRUGO, fan_show,          NULL);
0675 static DEVICE_ATTR(temp1_input, S_IRUGO, temp_cpu,          NULL);
0676 static DEVICE_ATTR(temp2_input, S_IRUGO, temp_cpu_local,    NULL);
0677 static DEVICE_ATTR(temp3_input, S_IRUGO, temp_cpu_DTS,      NULL);
0678 static DEVICE_ATTR(temp4_input, S_IRUGO, temp_northbridge,  NULL);
0679 static DEVICE_ATTR(temp5_input, S_IRUGO, temp_vga,          NULL);
0680 static DEVICE_ATTR(temp6_input, S_IRUGO, temp_SKIN,         NULL);
0681 static DEVICE_ATTR(temp1_label, S_IRUGO, label_cpu,         NULL);
0682 static DEVICE_ATTR(temp2_label, S_IRUGO, label_cpu_local,   NULL);
0683 static DEVICE_ATTR(temp3_label, S_IRUGO, label_cpu_DTS,     NULL);
0684 static DEVICE_ATTR(temp4_label, S_IRUGO, label_northbridge, NULL);
0685 static DEVICE_ATTR(temp5_label, S_IRUGO, label_vga,         NULL);
0686 static DEVICE_ATTR(temp6_label, S_IRUGO, label_SKIN,        NULL);
0687 static DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, pwm_show, pwm_store);
0688 static DEVICE_ATTR(pwm1_enable,
0689            S_IRUGO | S_IWUSR, pwm_enable_show, pwm_enable_store);
0690 
0691 static struct attribute *compal_platform_attrs[] = {
0692     &dev_attr_wake_up_pme.attr,
0693     &dev_attr_wake_up_modem.attr,
0694     &dev_attr_wake_up_lan.attr,
0695     &dev_attr_wake_up_wlan.attr,
0696     &dev_attr_wake_up_key.attr,
0697     &dev_attr_wake_up_mouse.attr,
0698     NULL
0699 };
0700 static const struct attribute_group compal_platform_attr_group = {
0701     .attrs = compal_platform_attrs
0702 };
0703 
0704 static struct attribute *compal_hwmon_attrs[] = {
0705     &dev_attr_pwm1_enable.attr,
0706     &dev_attr_pwm1.attr,
0707     &dev_attr_fan1_input.attr,
0708     &dev_attr_temp1_input.attr,
0709     &dev_attr_temp2_input.attr,
0710     &dev_attr_temp3_input.attr,
0711     &dev_attr_temp4_input.attr,
0712     &dev_attr_temp5_input.attr,
0713     &dev_attr_temp6_input.attr,
0714     &dev_attr_temp1_label.attr,
0715     &dev_attr_temp2_label.attr,
0716     &dev_attr_temp3_label.attr,
0717     &dev_attr_temp4_label.attr,
0718     &dev_attr_temp5_label.attr,
0719     &dev_attr_temp6_label.attr,
0720     NULL
0721 };
0722 ATTRIBUTE_GROUPS(compal_hwmon);
0723 
0724 static int compal_probe(struct platform_device *);
0725 static int compal_remove(struct platform_device *);
0726 static struct platform_driver compal_driver = {
0727     .driver = {
0728         .name = DRIVER_NAME,
0729     },
0730     .probe  = compal_probe,
0731     .remove = compal_remove,
0732 };
0733 
0734 static enum power_supply_property compal_bat_properties[] = {
0735     POWER_SUPPLY_PROP_STATUS,
0736     POWER_SUPPLY_PROP_HEALTH,
0737     POWER_SUPPLY_PROP_PRESENT,
0738     POWER_SUPPLY_PROP_TECHNOLOGY,
0739     POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
0740     POWER_SUPPLY_PROP_VOLTAGE_NOW,
0741     POWER_SUPPLY_PROP_CURRENT_NOW,
0742     POWER_SUPPLY_PROP_CURRENT_AVG,
0743     POWER_SUPPLY_PROP_POWER_NOW,
0744     POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
0745     POWER_SUPPLY_PROP_CHARGE_NOW,
0746     POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT,
0747     POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX,
0748     POWER_SUPPLY_PROP_CAPACITY,
0749     POWER_SUPPLY_PROP_CAPACITY_LEVEL,
0750     POWER_SUPPLY_PROP_TEMP,
0751     POWER_SUPPLY_PROP_TEMP_AMBIENT,
0752     POWER_SUPPLY_PROP_MODEL_NAME,
0753     POWER_SUPPLY_PROP_MANUFACTURER,
0754     POWER_SUPPLY_PROP_SERIAL_NUMBER,
0755 };
0756 
0757 static struct backlight_device *compalbl_device;
0758 
0759 static struct platform_device *compal_device;
0760 
0761 static struct rfkill *wifi_rfkill;
0762 static struct rfkill *bt_rfkill;
0763 
0764 
0765 
0766 
0767 
0768 /* =================================== */
0769 /* Initialization & clean-up functions */
0770 /* =================================== */
0771 
0772 static int dmi_check_cb(const struct dmi_system_id *id)
0773 {
0774     pr_info("Identified laptop model '%s'\n", id->ident);
0775     extra_features = false;
0776     return 1;
0777 }
0778 
0779 static int dmi_check_cb_extra(const struct dmi_system_id *id)
0780 {
0781     pr_info("Identified laptop model '%s', enabling extra features\n",
0782         id->ident);
0783     extra_features = true;
0784     return 1;
0785 }
0786 
0787 static const struct dmi_system_id compal_dmi_table[] __initconst = {
0788     {
0789         .ident = "FL90/IFL90",
0790         .matches = {
0791             DMI_MATCH(DMI_BOARD_NAME, "IFL90"),
0792             DMI_MATCH(DMI_BOARD_VERSION, "IFT00"),
0793         },
0794         .callback = dmi_check_cb
0795     },
0796     {
0797         .ident = "FL90/IFL90",
0798         .matches = {
0799             DMI_MATCH(DMI_BOARD_NAME, "IFL90"),
0800             DMI_MATCH(DMI_BOARD_VERSION, "REFERENCE"),
0801         },
0802         .callback = dmi_check_cb
0803     },
0804     {
0805         .ident = "FL91/IFL91",
0806         .matches = {
0807             DMI_MATCH(DMI_BOARD_NAME, "IFL91"),
0808             DMI_MATCH(DMI_BOARD_VERSION, "IFT00"),
0809         },
0810         .callback = dmi_check_cb
0811     },
0812     {
0813         .ident = "FL92/JFL92",
0814         .matches = {
0815             DMI_MATCH(DMI_BOARD_NAME, "JFL92"),
0816             DMI_MATCH(DMI_BOARD_VERSION, "IFT00"),
0817         },
0818         .callback = dmi_check_cb
0819     },
0820     {
0821         .ident = "FT00/IFT00",
0822         .matches = {
0823             DMI_MATCH(DMI_BOARD_NAME, "IFT00"),
0824             DMI_MATCH(DMI_BOARD_VERSION, "IFT00"),
0825         },
0826         .callback = dmi_check_cb
0827     },
0828     {
0829         .ident = "Dell Mini 9",
0830         .matches = {
0831             DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
0832             DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 910"),
0833         },
0834         .callback = dmi_check_cb
0835     },
0836     {
0837         .ident = "Dell Mini 10",
0838         .matches = {
0839             DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
0840             DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1010"),
0841         },
0842         .callback = dmi_check_cb
0843     },
0844     {
0845         .ident = "Dell Mini 10v",
0846         .matches = {
0847             DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
0848             DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1011"),
0849         },
0850         .callback = dmi_check_cb
0851     },
0852     {
0853         .ident = "Dell Mini 1012",
0854         .matches = {
0855             DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
0856             DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1012"),
0857         },
0858         .callback = dmi_check_cb
0859     },
0860     {
0861         .ident = "Dell Inspiron 11z",
0862         .matches = {
0863             DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
0864             DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1110"),
0865         },
0866         .callback = dmi_check_cb
0867     },
0868     {
0869         .ident = "Dell Mini 12",
0870         .matches = {
0871             DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
0872             DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1210"),
0873         },
0874         .callback = dmi_check_cb
0875     },
0876     {
0877         .ident = "JHL90",
0878         .matches = {
0879             DMI_MATCH(DMI_BOARD_NAME, "JHL90"),
0880             DMI_MATCH(DMI_BOARD_VERSION, "REFERENCE"),
0881         },
0882         .callback = dmi_check_cb_extra
0883     },
0884     {
0885         .ident = "KHLB2",
0886         .matches = {
0887             DMI_MATCH(DMI_BOARD_NAME, "KHLB2"),
0888             DMI_MATCH(DMI_BOARD_VERSION, "REFERENCE"),
0889         },
0890         .callback = dmi_check_cb_extra
0891     },
0892     { }
0893 };
0894 MODULE_DEVICE_TABLE(dmi, compal_dmi_table);
0895 
0896 static const struct power_supply_desc psy_bat_desc = {
0897     .name       = DRIVER_NAME,
0898     .type       = POWER_SUPPLY_TYPE_BATTERY,
0899     .properties = compal_bat_properties,
0900     .num_properties = ARRAY_SIZE(compal_bat_properties),
0901     .get_property   = bat_get_property,
0902     .set_property   = bat_set_property,
0903     .property_is_writeable = bat_writeable_property,
0904 };
0905 
0906 static void initialize_power_supply_data(struct compal_data *data)
0907 {
0908     ec_read_sequence(BAT_MANUFACTURER_NAME_ADDR,
0909                     data->bat_manufacturer_name,
0910                     BAT_MANUFACTURER_NAME_LEN);
0911     data->bat_manufacturer_name[BAT_MANUFACTURER_NAME_LEN] = 0;
0912 
0913     ec_read_sequence(BAT_MODEL_NAME_ADDR,
0914                     data->bat_model_name,
0915                     BAT_MODEL_NAME_LEN);
0916     data->bat_model_name[BAT_MODEL_NAME_LEN] = 0;
0917 
0918     scnprintf(data->bat_serial_number, BAT_SERIAL_NUMBER_LEN + 1, "%d",
0919                 ec_read_u16(BAT_SERIAL_NUMBER_ADDR));
0920 }
0921 
0922 static void initialize_fan_control_data(struct compal_data *data)
0923 {
0924     data->pwm_enable = 2; /* Keep motherboard in control for now */
0925     data->curr_pwm = 255; /* Try not to cause a CPU_on_fire exception
0926                  if we take over... */
0927 }
0928 
0929 static int setup_rfkill(void)
0930 {
0931     int ret;
0932 
0933     wifi_rfkill = rfkill_alloc("compal-wifi", &compal_device->dev,
0934                 RFKILL_TYPE_WLAN, &compal_rfkill_ops,
0935                 (void *) WIRELESS_WLAN);
0936     if (!wifi_rfkill)
0937         return -ENOMEM;
0938 
0939     ret = rfkill_register(wifi_rfkill);
0940     if (ret)
0941         goto err_wifi;
0942 
0943     bt_rfkill = rfkill_alloc("compal-bluetooth", &compal_device->dev,
0944                 RFKILL_TYPE_BLUETOOTH, &compal_rfkill_ops,
0945                 (void *) WIRELESS_BT);
0946     if (!bt_rfkill) {
0947         ret = -ENOMEM;
0948         goto err_allocate_bt;
0949     }
0950     ret = rfkill_register(bt_rfkill);
0951     if (ret)
0952         goto err_register_bt;
0953 
0954     return 0;
0955 
0956 err_register_bt:
0957     rfkill_destroy(bt_rfkill);
0958 
0959 err_allocate_bt:
0960     rfkill_unregister(wifi_rfkill);
0961 
0962 err_wifi:
0963     rfkill_destroy(wifi_rfkill);
0964 
0965     return ret;
0966 }
0967 
0968 static int __init compal_init(void)
0969 {
0970     int ret;
0971 
0972     if (acpi_disabled) {
0973         pr_err("ACPI needs to be enabled for this driver to work!\n");
0974         return -ENODEV;
0975     }
0976 
0977     if (!force && !dmi_check_system(compal_dmi_table)) {
0978         pr_err("Motherboard not recognized (You could try the module's force-parameter)\n");
0979         return -ENODEV;
0980     }
0981 
0982     if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
0983         struct backlight_properties props;
0984         memset(&props, 0, sizeof(struct backlight_properties));
0985         props.type = BACKLIGHT_PLATFORM;
0986         props.max_brightness = BACKLIGHT_LEVEL_MAX;
0987         compalbl_device = backlight_device_register(DRIVER_NAME,
0988                                 NULL, NULL,
0989                                 &compalbl_ops,
0990                                 &props);
0991         if (IS_ERR(compalbl_device))
0992             return PTR_ERR(compalbl_device);
0993     }
0994 
0995     ret = platform_driver_register(&compal_driver);
0996     if (ret)
0997         goto err_backlight;
0998 
0999     compal_device = platform_device_alloc(DRIVER_NAME, -1);
1000     if (!compal_device) {
1001         ret = -ENOMEM;
1002         goto err_platform_driver;
1003     }
1004 
1005     ret = platform_device_add(compal_device); /* This calls compal_probe */
1006     if (ret)
1007         goto err_platform_device;
1008 
1009     ret = setup_rfkill();
1010     if (ret)
1011         goto err_rfkill;
1012 
1013     pr_info("Driver " DRIVER_VERSION " successfully loaded\n");
1014     return 0;
1015 
1016 err_rfkill:
1017     platform_device_del(compal_device);
1018 
1019 err_platform_device:
1020     platform_device_put(compal_device);
1021 
1022 err_platform_driver:
1023     platform_driver_unregister(&compal_driver);
1024 
1025 err_backlight:
1026     backlight_device_unregister(compalbl_device);
1027 
1028     return ret;
1029 }
1030 
1031 static int compal_probe(struct platform_device *pdev)
1032 {
1033     int err;
1034     struct compal_data *data;
1035     struct device *hwmon_dev;
1036     struct power_supply_config psy_cfg = {};
1037 
1038     if (!extra_features)
1039         return 0;
1040 
1041     /* Fan control */
1042     data = devm_kzalloc(&pdev->dev, sizeof(struct compal_data), GFP_KERNEL);
1043     if (!data)
1044         return -ENOMEM;
1045 
1046     initialize_fan_control_data(data);
1047 
1048     err = sysfs_create_group(&pdev->dev.kobj, &compal_platform_attr_group);
1049     if (err)
1050         return err;
1051 
1052     hwmon_dev = devm_hwmon_device_register_with_groups(&pdev->dev,
1053                                "compal", data,
1054                                compal_hwmon_groups);
1055     if (IS_ERR(hwmon_dev)) {
1056         err = PTR_ERR(hwmon_dev);
1057         goto remove;
1058     }
1059 
1060     /* Power supply */
1061     initialize_power_supply_data(data);
1062     psy_cfg.drv_data = data;
1063     data->psy = power_supply_register(&compal_device->dev, &psy_bat_desc,
1064                       &psy_cfg);
1065     if (IS_ERR(data->psy)) {
1066         err = PTR_ERR(data->psy);
1067         goto remove;
1068     }
1069 
1070     platform_set_drvdata(pdev, data);
1071 
1072     return 0;
1073 
1074 remove:
1075     sysfs_remove_group(&pdev->dev.kobj, &compal_platform_attr_group);
1076     return err;
1077 }
1078 
1079 static void __exit compal_cleanup(void)
1080 {
1081     platform_device_unregister(compal_device);
1082     platform_driver_unregister(&compal_driver);
1083     backlight_device_unregister(compalbl_device);
1084     rfkill_unregister(wifi_rfkill);
1085     rfkill_unregister(bt_rfkill);
1086     rfkill_destroy(wifi_rfkill);
1087     rfkill_destroy(bt_rfkill);
1088 
1089     pr_info("Driver unloaded\n");
1090 }
1091 
1092 static int compal_remove(struct platform_device *pdev)
1093 {
1094     struct compal_data *data;
1095 
1096     if (!extra_features)
1097         return 0;
1098 
1099     pr_info("Unloading: resetting fan control to motherboard\n");
1100     pwm_disable_control();
1101 
1102     data = platform_get_drvdata(pdev);
1103     power_supply_unregister(data->psy);
1104 
1105     sysfs_remove_group(&pdev->dev.kobj, &compal_platform_attr_group);
1106 
1107     return 0;
1108 }
1109 
1110 
1111 module_init(compal_init);
1112 module_exit(compal_cleanup);
1113 
1114 MODULE_AUTHOR("Cezary Jackiewicz");
1115 MODULE_AUTHOR("Roald Frederickx (roald.frederickx@gmail.com)");
1116 MODULE_DESCRIPTION("Compal Laptop Support");
1117 MODULE_VERSION(DRIVER_VERSION);
1118 MODULE_LICENSE("GPL");