root/drivers/staging/nvec/nvec_power.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. nvec_power_notifier
  2. get_bat_mfg_data
  3. nvec_power_bat_notifier
  4. nvec_power_get_property
  5. nvec_battery_get_property
  6. nvec_power_poll
  7. nvec_power_probe
  8. nvec_power_remove

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  * nvec_power: power supply driver for a NVIDIA compliant embedded controller
   4  *
   5  * Copyright (C) 2011 The AC100 Kernel Team <ac100@lists.launchpad.net>
   6  *
   7  * Authors:  Ilya Petrov <ilya.muromec@gmail.com>
   8  *           Marc Dietrich <marvin24@gmx.de>
   9  */
  10 
  11 #include <linux/module.h>
  12 #include <linux/platform_device.h>
  13 #include <linux/err.h>
  14 #include <linux/power_supply.h>
  15 #include <linux/slab.h>
  16 #include <linux/workqueue.h>
  17 #include <linux/delay.h>
  18 
  19 #include "nvec.h"
  20 
  21 #define GET_SYSTEM_STATUS 0x00
  22 
  23 struct nvec_power {
  24         struct notifier_block notifier;
  25         struct delayed_work poller;
  26         struct nvec_chip *nvec;
  27         int on;
  28         int bat_present;
  29         int bat_status;
  30         int bat_voltage_now;
  31         int bat_current_now;
  32         int bat_current_avg;
  33         int time_remain;
  34         int charge_full_design;
  35         int charge_last_full;
  36         int critical_capacity;
  37         int capacity_remain;
  38         int bat_temperature;
  39         int bat_cap;
  40         int bat_type_enum;
  41         char bat_manu[30];
  42         char bat_model[30];
  43         char bat_type[30];
  44 };
  45 
  46 enum {
  47         SLOT_STATUS,
  48         VOLTAGE,
  49         TIME_REMAINING,
  50         CURRENT,
  51         AVERAGE_CURRENT,
  52         AVERAGING_TIME_INTERVAL,
  53         CAPACITY_REMAINING,
  54         LAST_FULL_CHARGE_CAPACITY,
  55         DESIGN_CAPACITY,
  56         CRITICAL_CAPACITY,
  57         TEMPERATURE,
  58         MANUFACTURER,
  59         MODEL,
  60         TYPE,
  61 };
  62 
  63 enum {
  64         AC,
  65         BAT,
  66 };
  67 
  68 struct bat_response {
  69         u8 event_type;
  70         u8 length;
  71         u8 sub_type;
  72         u8 status;
  73         /* payload */
  74         union {
  75                 char plc[30];
  76                 u16 plu;
  77                 s16 pls;
  78         };
  79 };
  80 
  81 static struct power_supply *nvec_bat_psy;
  82 static struct power_supply *nvec_psy;
  83 
  84 static int nvec_power_notifier(struct notifier_block *nb,
  85                                unsigned long event_type, void *data)
  86 {
  87         struct nvec_power *power =
  88             container_of(nb, struct nvec_power, notifier);
  89         struct bat_response *res = data;
  90 
  91         if (event_type != NVEC_SYS)
  92                 return NOTIFY_DONE;
  93 
  94         if (res->sub_type == 0) {
  95                 if (power->on != res->plu) {
  96                         power->on = res->plu;
  97                         power_supply_changed(nvec_psy);
  98                 }
  99                 return NOTIFY_STOP;
 100         }
 101         return NOTIFY_OK;
 102 }
 103 
 104 static const int bat_init[] = {
 105         LAST_FULL_CHARGE_CAPACITY, DESIGN_CAPACITY, CRITICAL_CAPACITY,
 106         MANUFACTURER, MODEL, TYPE,
 107 };
 108 
 109 static void get_bat_mfg_data(struct nvec_power *power)
 110 {
 111         int i;
 112         char buf[] = { NVEC_BAT, SLOT_STATUS };
 113 
 114         for (i = 0; i < ARRAY_SIZE(bat_init); i++) {
 115                 buf[1] = bat_init[i];
 116                 nvec_write_async(power->nvec, buf, 2);
 117         }
 118 }
 119 
 120 static int nvec_power_bat_notifier(struct notifier_block *nb,
 121                                    unsigned long event_type, void *data)
 122 {
 123         struct nvec_power *power =
 124             container_of(nb, struct nvec_power, notifier);
 125         struct bat_response *res = data;
 126         int status_changed = 0;
 127 
 128         if (event_type != NVEC_BAT)
 129                 return NOTIFY_DONE;
 130 
 131         switch (res->sub_type) {
 132         case SLOT_STATUS:
 133                 if (res->plc[0] & 1) {
 134                         if (power->bat_present == 0) {
 135                                 status_changed = 1;
 136                                 get_bat_mfg_data(power);
 137                         }
 138 
 139                         power->bat_present = 1;
 140 
 141                         switch ((res->plc[0] >> 1) & 3) {
 142                         case 0:
 143                                 power->bat_status =
 144                                     POWER_SUPPLY_STATUS_NOT_CHARGING;
 145                                 break;
 146                         case 1:
 147                                 power->bat_status =
 148                                     POWER_SUPPLY_STATUS_CHARGING;
 149                                 break;
 150                         case 2:
 151                                 power->bat_status =
 152                                     POWER_SUPPLY_STATUS_DISCHARGING;
 153                                 break;
 154                         default:
 155                                 power->bat_status = POWER_SUPPLY_STATUS_UNKNOWN;
 156                         }
 157                 } else {
 158                         if (power->bat_present == 1)
 159                                 status_changed = 1;
 160 
 161                         power->bat_present = 0;
 162                         power->bat_status = POWER_SUPPLY_STATUS_UNKNOWN;
 163                 }
 164                 power->bat_cap = res->plc[1];
 165                 if (status_changed)
 166                         power_supply_changed(nvec_bat_psy);
 167                 break;
 168         case VOLTAGE:
 169                 power->bat_voltage_now = res->plu * 1000;
 170                 break;
 171         case TIME_REMAINING:
 172                 power->time_remain = res->plu * 3600;
 173                 break;
 174         case CURRENT:
 175                 power->bat_current_now = res->pls * 1000;
 176                 break;
 177         case AVERAGE_CURRENT:
 178                 power->bat_current_avg = res->pls * 1000;
 179                 break;
 180         case CAPACITY_REMAINING:
 181                 power->capacity_remain = res->plu * 1000;
 182                 break;
 183         case LAST_FULL_CHARGE_CAPACITY:
 184                 power->charge_last_full = res->plu * 1000;
 185                 break;
 186         case DESIGN_CAPACITY:
 187                 power->charge_full_design = res->plu * 1000;
 188                 break;
 189         case CRITICAL_CAPACITY:
 190                 power->critical_capacity = res->plu * 1000;
 191                 break;
 192         case TEMPERATURE:
 193                 power->bat_temperature = res->plu - 2732;
 194                 break;
 195         case MANUFACTURER:
 196                 memcpy(power->bat_manu, &res->plc, res->length - 2);
 197                 power->bat_model[res->length - 2] = '\0';
 198                 break;
 199         case MODEL:
 200                 memcpy(power->bat_model, &res->plc, res->length - 2);
 201                 power->bat_model[res->length - 2] = '\0';
 202                 break;
 203         case TYPE:
 204                 memcpy(power->bat_type, &res->plc, res->length - 2);
 205                 power->bat_type[res->length - 2] = '\0';
 206                 /*
 207                  * This differs a little from the spec fill in more if you find
 208                  * some.
 209                  */
 210                 if (!strncmp(power->bat_type, "Li", 30))
 211                         power->bat_type_enum = POWER_SUPPLY_TECHNOLOGY_LION;
 212                 else
 213                         power->bat_type_enum = POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
 214                 break;
 215         default:
 216                 return NOTIFY_STOP;
 217         }
 218 
 219         return NOTIFY_STOP;
 220 }
 221 
 222 static int nvec_power_get_property(struct power_supply *psy,
 223                                    enum power_supply_property psp,
 224                                    union power_supply_propval *val)
 225 {
 226         struct nvec_power *power = dev_get_drvdata(psy->dev.parent);
 227 
 228         switch (psp) {
 229         case POWER_SUPPLY_PROP_ONLINE:
 230                 val->intval = power->on;
 231                 break;
 232         default:
 233                 return -EINVAL;
 234         }
 235         return 0;
 236 }
 237 
 238 static int nvec_battery_get_property(struct power_supply *psy,
 239                                      enum power_supply_property psp,
 240                                      union power_supply_propval *val)
 241 {
 242         struct nvec_power *power = dev_get_drvdata(psy->dev.parent);
 243 
 244         switch (psp) {
 245         case POWER_SUPPLY_PROP_STATUS:
 246                 val->intval = power->bat_status;
 247                 break;
 248         case POWER_SUPPLY_PROP_CAPACITY:
 249                 val->intval = power->bat_cap;
 250                 break;
 251         case POWER_SUPPLY_PROP_PRESENT:
 252                 val->intval = power->bat_present;
 253                 break;
 254         case POWER_SUPPLY_PROP_VOLTAGE_NOW:
 255                 val->intval = power->bat_voltage_now;
 256                 break;
 257         case POWER_SUPPLY_PROP_CURRENT_NOW:
 258                 val->intval = power->bat_current_now;
 259                 break;
 260         case POWER_SUPPLY_PROP_CURRENT_AVG:
 261                 val->intval = power->bat_current_avg;
 262                 break;
 263         case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
 264                 val->intval = power->time_remain;
 265                 break;
 266         case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
 267                 val->intval = power->charge_full_design;
 268                 break;
 269         case POWER_SUPPLY_PROP_CHARGE_FULL:
 270                 val->intval = power->charge_last_full;
 271                 break;
 272         case POWER_SUPPLY_PROP_CHARGE_EMPTY:
 273                 val->intval = power->critical_capacity;
 274                 break;
 275         case POWER_SUPPLY_PROP_CHARGE_NOW:
 276                 val->intval = power->capacity_remain;
 277                 break;
 278         case POWER_SUPPLY_PROP_TEMP:
 279                 val->intval = power->bat_temperature;
 280                 break;
 281         case POWER_SUPPLY_PROP_MANUFACTURER:
 282                 val->strval = power->bat_manu;
 283                 break;
 284         case POWER_SUPPLY_PROP_MODEL_NAME:
 285                 val->strval = power->bat_model;
 286                 break;
 287         case POWER_SUPPLY_PROP_TECHNOLOGY:
 288                 val->intval = power->bat_type_enum;
 289                 break;
 290         default:
 291                 return -EINVAL;
 292         }
 293         return 0;
 294 }
 295 
 296 static enum power_supply_property nvec_power_props[] = {
 297         POWER_SUPPLY_PROP_ONLINE,
 298 };
 299 
 300 static enum power_supply_property nvec_battery_props[] = {
 301         POWER_SUPPLY_PROP_STATUS,
 302         POWER_SUPPLY_PROP_PRESENT,
 303         POWER_SUPPLY_PROP_CAPACITY,
 304         POWER_SUPPLY_PROP_VOLTAGE_NOW,
 305         POWER_SUPPLY_PROP_CURRENT_NOW,
 306 #ifdef EC_FULL_DIAG
 307         POWER_SUPPLY_PROP_CURRENT_AVG,
 308         POWER_SUPPLY_PROP_TEMP,
 309         POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
 310 #endif
 311         POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
 312         POWER_SUPPLY_PROP_CHARGE_FULL,
 313         POWER_SUPPLY_PROP_CHARGE_EMPTY,
 314         POWER_SUPPLY_PROP_CHARGE_NOW,
 315         POWER_SUPPLY_PROP_MANUFACTURER,
 316         POWER_SUPPLY_PROP_MODEL_NAME,
 317         POWER_SUPPLY_PROP_TECHNOLOGY,
 318 };
 319 
 320 static char *nvec_power_supplied_to[] = {
 321         "battery",
 322 };
 323 
 324 static const struct power_supply_desc nvec_bat_psy_desc = {
 325         .name = "battery",
 326         .type = POWER_SUPPLY_TYPE_BATTERY,
 327         .properties = nvec_battery_props,
 328         .num_properties = ARRAY_SIZE(nvec_battery_props),
 329         .get_property = nvec_battery_get_property,
 330 };
 331 
 332 static const struct power_supply_desc nvec_psy_desc = {
 333         .name = "ac",
 334         .type = POWER_SUPPLY_TYPE_MAINS,
 335         .properties = nvec_power_props,
 336         .num_properties = ARRAY_SIZE(nvec_power_props),
 337         .get_property = nvec_power_get_property,
 338 };
 339 
 340 static int counter;
 341 static int const bat_iter[] = {
 342         SLOT_STATUS, VOLTAGE, CURRENT, CAPACITY_REMAINING,
 343 #ifdef EC_FULL_DIAG
 344         AVERAGE_CURRENT, TEMPERATURE, TIME_REMAINING,
 345 #endif
 346 };
 347 
 348 static void nvec_power_poll(struct work_struct *work)
 349 {
 350         char buf[] = { NVEC_SYS, GET_SYSTEM_STATUS };
 351         struct nvec_power *power = container_of(work, struct nvec_power,
 352                                                 poller.work);
 353 
 354         if (counter >= ARRAY_SIZE(bat_iter))
 355                 counter = 0;
 356 
 357         /* AC status via sys req */
 358         nvec_write_async(power->nvec, buf, 2);
 359         msleep(100);
 360 
 361         /*
 362          * Select a battery request function via round robin doing it all at
 363          * once seems to overload the power supply.
 364          */
 365         buf[0] = NVEC_BAT;
 366         buf[1] = bat_iter[counter++];
 367         nvec_write_async(power->nvec, buf, 2);
 368 
 369         schedule_delayed_work(to_delayed_work(work), msecs_to_jiffies(5000));
 370 };
 371 
 372 static int nvec_power_probe(struct platform_device *pdev)
 373 {
 374         struct power_supply **psy;
 375         const struct power_supply_desc *psy_desc;
 376         struct nvec_power *power;
 377         struct nvec_chip *nvec = dev_get_drvdata(pdev->dev.parent);
 378         struct power_supply_config psy_cfg = {};
 379 
 380         power = devm_kzalloc(&pdev->dev, sizeof(struct nvec_power), GFP_NOWAIT);
 381         if (!power)
 382                 return -ENOMEM;
 383 
 384         dev_set_drvdata(&pdev->dev, power);
 385         power->nvec = nvec;
 386 
 387         switch (pdev->id) {
 388         case AC:
 389                 psy = &nvec_psy;
 390                 psy_desc = &nvec_psy_desc;
 391                 psy_cfg.supplied_to = nvec_power_supplied_to;
 392                 psy_cfg.num_supplicants = ARRAY_SIZE(nvec_power_supplied_to);
 393 
 394                 power->notifier.notifier_call = nvec_power_notifier;
 395 
 396                 INIT_DELAYED_WORK(&power->poller, nvec_power_poll);
 397                 schedule_delayed_work(&power->poller, msecs_to_jiffies(5000));
 398                 break;
 399         case BAT:
 400                 psy = &nvec_bat_psy;
 401                 psy_desc = &nvec_bat_psy_desc;
 402 
 403                 power->notifier.notifier_call = nvec_power_bat_notifier;
 404                 break;
 405         default:
 406                 return -ENODEV;
 407         }
 408 
 409         nvec_register_notifier(nvec, &power->notifier, NVEC_SYS);
 410 
 411         if (pdev->id == BAT)
 412                 get_bat_mfg_data(power);
 413 
 414         *psy = power_supply_register(&pdev->dev, psy_desc, &psy_cfg);
 415 
 416         return PTR_ERR_OR_ZERO(*psy);
 417 }
 418 
 419 static int nvec_power_remove(struct platform_device *pdev)
 420 {
 421         struct nvec_power *power = platform_get_drvdata(pdev);
 422 
 423         cancel_delayed_work_sync(&power->poller);
 424         nvec_unregister_notifier(power->nvec, &power->notifier);
 425         switch (pdev->id) {
 426         case AC:
 427                 power_supply_unregister(nvec_psy);
 428                 break;
 429         case BAT:
 430                 power_supply_unregister(nvec_bat_psy);
 431         }
 432 
 433         return 0;
 434 }
 435 
 436 static struct platform_driver nvec_power_driver = {
 437         .probe = nvec_power_probe,
 438         .remove = nvec_power_remove,
 439         .driver = {
 440                    .name = "nvec-power",
 441         }
 442 };
 443 
 444 module_platform_driver(nvec_power_driver);
 445 
 446 MODULE_AUTHOR("Ilya Petrov <ilya.muromec@gmail.com>");
 447 MODULE_LICENSE("GPL");
 448 MODULE_DESCRIPTION("NVEC battery and AC driver");
 449 MODULE_ALIAS("platform:nvec-power");

/* [<][>][^][v][top][bottom][index][help] */