root/drivers/power/supply/ipaq_micro_battery.c

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

DEFINITIONS

This source file includes following definitions.
  1. micro_battery_work
  2. get_capacity
  3. get_status
  4. micro_batt_get_property
  5. micro_ac_get_property
  6. micro_batt_probe
  7. micro_batt_remove
  8. micro_batt_suspend
  9. micro_batt_resume

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  *
   4  * h3xxx atmel micro companion support, battery subdevice
   5  * based on previous kernel 2.4 version
   6  * Author : Alessandro Gardich <gremlin@gremlin.it>
   7  * Author : Linus Walleij <linus.walleij@linaro.org>
   8  */
   9 
  10 #include <linux/module.h>
  11 #include <linux/init.h>
  12 #include <linux/platform_device.h>
  13 #include <linux/mfd/ipaq-micro.h>
  14 #include <linux/power_supply.h>
  15 #include <linux/workqueue.h>
  16 
  17 #define BATT_PERIOD 100000 /* 100 seconds in milliseconds */
  18 
  19 #define MICRO_BATT_CHEM_ALKALINE        0x01
  20 #define MICRO_BATT_CHEM_NICD            0x02
  21 #define MICRO_BATT_CHEM_NIMH            0x03
  22 #define MICRO_BATT_CHEM_LION            0x04
  23 #define MICRO_BATT_CHEM_LIPOLY          0x05
  24 #define MICRO_BATT_CHEM_NOT_INSTALLED   0x06
  25 #define MICRO_BATT_CHEM_UNKNOWN         0xff
  26 
  27 #define MICRO_BATT_STATUS_HIGH          0x01
  28 #define MICRO_BATT_STATUS_LOW           0x02
  29 #define MICRO_BATT_STATUS_CRITICAL      0x04
  30 #define MICRO_BATT_STATUS_CHARGING      0x08
  31 #define MICRO_BATT_STATUS_CHARGEMAIN    0x10
  32 #define MICRO_BATT_STATUS_DEAD          0x20 /* Battery will not charge */
  33 #define MICRO_BATT_STATUS_NOTINSTALLED  0x20 /* For expansion pack batteries */
  34 #define MICRO_BATT_STATUS_FULL          0x40 /* Battery fully charged */
  35 #define MICRO_BATT_STATUS_NOBATTERY     0x80
  36 #define MICRO_BATT_STATUS_UNKNOWN       0xff
  37 
  38 struct micro_battery {
  39         struct ipaq_micro *micro;
  40         struct workqueue_struct *wq;
  41         struct delayed_work update;
  42         u8 ac;
  43         u8 chemistry;
  44         unsigned int voltage;
  45         u16 temperature;
  46         u8 flag;
  47 };
  48 
  49 static void micro_battery_work(struct work_struct *work)
  50 {
  51         struct micro_battery *mb = container_of(work,
  52                                 struct micro_battery, update.work);
  53         struct ipaq_micro_msg msg_battery = {
  54                 .id = MSG_BATTERY,
  55         };
  56         struct ipaq_micro_msg msg_sensor = {
  57                 .id = MSG_THERMAL_SENSOR,
  58         };
  59 
  60         /* First send battery message */
  61         ipaq_micro_tx_msg_sync(mb->micro, &msg_battery);
  62         if (msg_battery.rx_len < 4)
  63                 pr_info("ERROR");
  64 
  65         /*
  66          * Returned message format:
  67          * byte 0:   0x00 = Not plugged in
  68          *           0x01 = AC adapter plugged in
  69          * byte 1:   chemistry
  70          * byte 2:   voltage LSB
  71          * byte 3:   voltage MSB
  72          * byte 4:   flags
  73          * byte 5-9: same for battery 2
  74          */
  75         mb->ac = msg_battery.rx_data[0];
  76         mb->chemistry = msg_battery.rx_data[1];
  77         mb->voltage = ((((unsigned short)msg_battery.rx_data[3] << 8) +
  78                         msg_battery.rx_data[2]) * 5000L) * 1000 / 1024;
  79         mb->flag = msg_battery.rx_data[4];
  80 
  81         if (msg_battery.rx_len == 9)
  82                 pr_debug("second battery ignored\n");
  83 
  84         /* Then read the sensor */
  85         ipaq_micro_tx_msg_sync(mb->micro, &msg_sensor);
  86         mb->temperature = msg_sensor.rx_data[1] << 8 | msg_sensor.rx_data[0];
  87 
  88         queue_delayed_work(mb->wq, &mb->update, msecs_to_jiffies(BATT_PERIOD));
  89 }
  90 
  91 static int get_capacity(struct power_supply *b)
  92 {
  93         struct micro_battery *mb = dev_get_drvdata(b->dev.parent);
  94 
  95         switch (mb->flag & 0x07) {
  96         case MICRO_BATT_STATUS_HIGH:
  97                 return 100;
  98                 break;
  99         case MICRO_BATT_STATUS_LOW:
 100                 return 50;
 101                 break;
 102         case MICRO_BATT_STATUS_CRITICAL:
 103                 return 5;
 104                 break;
 105         default:
 106                 break;
 107         }
 108         return 0;
 109 }
 110 
 111 static int get_status(struct power_supply *b)
 112 {
 113         struct micro_battery *mb = dev_get_drvdata(b->dev.parent);
 114 
 115         if (mb->flag == MICRO_BATT_STATUS_UNKNOWN)
 116                 return POWER_SUPPLY_STATUS_UNKNOWN;
 117 
 118         if (mb->flag & MICRO_BATT_STATUS_FULL)
 119                 return POWER_SUPPLY_STATUS_FULL;
 120 
 121         if ((mb->flag & MICRO_BATT_STATUS_CHARGING) ||
 122                 (mb->flag & MICRO_BATT_STATUS_CHARGEMAIN))
 123                 return POWER_SUPPLY_STATUS_CHARGING;
 124 
 125         return POWER_SUPPLY_STATUS_DISCHARGING;
 126 }
 127 
 128 static int micro_batt_get_property(struct power_supply *b,
 129                                         enum power_supply_property psp,
 130                                         union power_supply_propval *val)
 131 {
 132         struct micro_battery *mb = dev_get_drvdata(b->dev.parent);
 133 
 134         switch (psp) {
 135         case POWER_SUPPLY_PROP_TECHNOLOGY:
 136                 switch (mb->chemistry) {
 137                 case MICRO_BATT_CHEM_NICD:
 138                         val->intval = POWER_SUPPLY_TECHNOLOGY_NiCd;
 139                         break;
 140                 case MICRO_BATT_CHEM_NIMH:
 141                         val->intval = POWER_SUPPLY_TECHNOLOGY_NiMH;
 142                         break;
 143                 case MICRO_BATT_CHEM_LION:
 144                         val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
 145                         break;
 146                 case MICRO_BATT_CHEM_LIPOLY:
 147                         val->intval = POWER_SUPPLY_TECHNOLOGY_LIPO;
 148                         break;
 149                 default:
 150                         val->intval = POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
 151                         break;
 152                 };
 153                 break;
 154         case POWER_SUPPLY_PROP_STATUS:
 155                 val->intval = get_status(b);
 156                 break;
 157         case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
 158                 val->intval = 4700000;
 159                 break;
 160         case POWER_SUPPLY_PROP_CAPACITY:
 161                 val->intval = get_capacity(b);
 162                 break;
 163         case POWER_SUPPLY_PROP_TEMP:
 164                 val->intval = mb->temperature;
 165                 break;
 166         case POWER_SUPPLY_PROP_VOLTAGE_NOW:
 167                 val->intval = mb->voltage;
 168                 break;
 169         default:
 170                 return -EINVAL;
 171         };
 172 
 173         return 0;
 174 }
 175 
 176 static int micro_ac_get_property(struct power_supply *b,
 177                                  enum power_supply_property psp,
 178                                  union power_supply_propval *val)
 179 {
 180         struct micro_battery *mb = dev_get_drvdata(b->dev.parent);
 181 
 182         switch (psp) {
 183         case POWER_SUPPLY_PROP_ONLINE:
 184                 val->intval = mb->ac;
 185                 break;
 186         default:
 187                 return -EINVAL;
 188         };
 189 
 190         return 0;
 191 }
 192 
 193 static enum power_supply_property micro_batt_power_props[] = {
 194         POWER_SUPPLY_PROP_TECHNOLOGY,
 195         POWER_SUPPLY_PROP_STATUS,
 196         POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
 197         POWER_SUPPLY_PROP_CAPACITY,
 198         POWER_SUPPLY_PROP_TEMP,
 199         POWER_SUPPLY_PROP_VOLTAGE_NOW,
 200 };
 201 
 202 static const struct power_supply_desc micro_batt_power_desc = {
 203         .name                   = "main-battery",
 204         .type                   = POWER_SUPPLY_TYPE_BATTERY,
 205         .properties             = micro_batt_power_props,
 206         .num_properties         = ARRAY_SIZE(micro_batt_power_props),
 207         .get_property           = micro_batt_get_property,
 208         .use_for_apm            = 1,
 209 };
 210 
 211 static enum power_supply_property micro_ac_power_props[] = {
 212         POWER_SUPPLY_PROP_ONLINE,
 213 };
 214 
 215 static const struct power_supply_desc micro_ac_power_desc = {
 216         .name                   = "ac",
 217         .type                   = POWER_SUPPLY_TYPE_MAINS,
 218         .properties             = micro_ac_power_props,
 219         .num_properties         = ARRAY_SIZE(micro_ac_power_props),
 220         .get_property           = micro_ac_get_property,
 221 };
 222 
 223 static struct power_supply *micro_batt_power, *micro_ac_power;
 224 
 225 static int micro_batt_probe(struct platform_device *pdev)
 226 {
 227         struct micro_battery *mb;
 228         int ret;
 229 
 230         mb = devm_kzalloc(&pdev->dev, sizeof(*mb), GFP_KERNEL);
 231         if (!mb)
 232                 return -ENOMEM;
 233 
 234         mb->micro = dev_get_drvdata(pdev->dev.parent);
 235         mb->wq = alloc_workqueue("ipaq-battery-wq", WQ_MEM_RECLAIM, 0);
 236         if (!mb->wq)
 237                 return -ENOMEM;
 238 
 239         INIT_DELAYED_WORK(&mb->update, micro_battery_work);
 240         platform_set_drvdata(pdev, mb);
 241         queue_delayed_work(mb->wq, &mb->update, 1);
 242 
 243         micro_batt_power = power_supply_register(&pdev->dev,
 244                                                  &micro_batt_power_desc, NULL);
 245         if (IS_ERR(micro_batt_power)) {
 246                 ret = PTR_ERR(micro_batt_power);
 247                 goto batt_err;
 248         }
 249 
 250         micro_ac_power = power_supply_register(&pdev->dev,
 251                                                &micro_ac_power_desc, NULL);
 252         if (IS_ERR(micro_ac_power)) {
 253                 ret = PTR_ERR(micro_ac_power);
 254                 goto ac_err;
 255         }
 256 
 257         dev_info(&pdev->dev, "iPAQ micro battery driver\n");
 258         return 0;
 259 
 260 ac_err:
 261         power_supply_unregister(micro_batt_power);
 262 batt_err:
 263         cancel_delayed_work_sync(&mb->update);
 264         destroy_workqueue(mb->wq);
 265         return ret;
 266 }
 267 
 268 static int micro_batt_remove(struct platform_device *pdev)
 269 
 270 {
 271         struct micro_battery *mb = platform_get_drvdata(pdev);
 272 
 273         power_supply_unregister(micro_ac_power);
 274         power_supply_unregister(micro_batt_power);
 275         cancel_delayed_work_sync(&mb->update);
 276         destroy_workqueue(mb->wq);
 277 
 278         return 0;
 279 }
 280 
 281 static int __maybe_unused micro_batt_suspend(struct device *dev)
 282 {
 283         struct micro_battery *mb = dev_get_drvdata(dev);
 284 
 285         cancel_delayed_work_sync(&mb->update);
 286         return 0;
 287 }
 288 
 289 static int __maybe_unused micro_batt_resume(struct device *dev)
 290 {
 291         struct micro_battery *mb = dev_get_drvdata(dev);
 292 
 293         queue_delayed_work(mb->wq, &mb->update, msecs_to_jiffies(BATT_PERIOD));
 294         return 0;
 295 }
 296 
 297 static const struct dev_pm_ops micro_batt_dev_pm_ops = {
 298         SET_SYSTEM_SLEEP_PM_OPS(micro_batt_suspend, micro_batt_resume)
 299 };
 300 
 301 static struct platform_driver micro_batt_device_driver = {
 302         .driver         = {
 303                 .name   = "ipaq-micro-battery",
 304                 .pm     = &micro_batt_dev_pm_ops,
 305         },
 306         .probe          = micro_batt_probe,
 307         .remove         = micro_batt_remove,
 308 };
 309 module_platform_driver(micro_batt_device_driver);
 310 
 311 MODULE_LICENSE("GPL");
 312 MODULE_DESCRIPTION("driver for iPAQ Atmel micro battery");
 313 MODULE_ALIAS("platform:ipaq-micro-battery");

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