linux/drivers/platform/x86/compal-laptop.c
<<
>>
Prefs
   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