0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #include <linux/init.h>
0011 #include <linux/kernel.h>
0012 #include <linux/module.h>
0013 #include <linux/platform_device.h>
0014 #include <linux/power_supply.h>
0015 #include <linux/wm97xx.h>
0016 #include <linux/spinlock.h>
0017 #include <linux/interrupt.h>
0018 #include <linux/gpio/consumer.h>
0019 #include <linux/irq.h>
0020 #include <linux/slab.h>
0021
0022 static struct work_struct bat_work;
0023 static struct gpio_desc *charge_gpiod;
0024 static DEFINE_MUTEX(work_lock);
0025 static int bat_status = POWER_SUPPLY_STATUS_UNKNOWN;
0026 static enum power_supply_property *prop;
0027
0028 static unsigned long wm97xx_read_bat(struct power_supply *bat_ps)
0029 {
0030 struct wm97xx_batt_pdata *pdata = power_supply_get_drvdata(bat_ps);
0031
0032 return wm97xx_read_aux_adc(dev_get_drvdata(bat_ps->dev.parent),
0033 pdata->batt_aux) * pdata->batt_mult /
0034 pdata->batt_div;
0035 }
0036
0037 static unsigned long wm97xx_read_temp(struct power_supply *bat_ps)
0038 {
0039 struct wm97xx_batt_pdata *pdata = power_supply_get_drvdata(bat_ps);
0040
0041 return wm97xx_read_aux_adc(dev_get_drvdata(bat_ps->dev.parent),
0042 pdata->temp_aux) * pdata->temp_mult /
0043 pdata->temp_div;
0044 }
0045
0046 static int wm97xx_bat_get_property(struct power_supply *bat_ps,
0047 enum power_supply_property psp,
0048 union power_supply_propval *val)
0049 {
0050 struct wm97xx_batt_pdata *pdata = power_supply_get_drvdata(bat_ps);
0051
0052 switch (psp) {
0053 case POWER_SUPPLY_PROP_STATUS:
0054 val->intval = bat_status;
0055 break;
0056 case POWER_SUPPLY_PROP_TECHNOLOGY:
0057 val->intval = pdata->batt_tech;
0058 break;
0059 case POWER_SUPPLY_PROP_VOLTAGE_NOW:
0060 if (pdata->batt_aux >= 0)
0061 val->intval = wm97xx_read_bat(bat_ps);
0062 else
0063 return -EINVAL;
0064 break;
0065 case POWER_SUPPLY_PROP_TEMP:
0066 if (pdata->temp_aux >= 0)
0067 val->intval = wm97xx_read_temp(bat_ps);
0068 else
0069 return -EINVAL;
0070 break;
0071 case POWER_SUPPLY_PROP_VOLTAGE_MAX:
0072 if (pdata->max_voltage >= 0)
0073 val->intval = pdata->max_voltage;
0074 else
0075 return -EINVAL;
0076 break;
0077 case POWER_SUPPLY_PROP_VOLTAGE_MIN:
0078 if (pdata->min_voltage >= 0)
0079 val->intval = pdata->min_voltage;
0080 else
0081 return -EINVAL;
0082 break;
0083 case POWER_SUPPLY_PROP_PRESENT:
0084 val->intval = 1;
0085 break;
0086 default:
0087 return -EINVAL;
0088 }
0089 return 0;
0090 }
0091
0092 static void wm97xx_bat_external_power_changed(struct power_supply *bat_ps)
0093 {
0094 schedule_work(&bat_work);
0095 }
0096
0097 static void wm97xx_bat_update(struct power_supply *bat_ps)
0098 {
0099 int old_status = bat_status;
0100
0101 mutex_lock(&work_lock);
0102
0103 bat_status = (charge_gpiod) ?
0104 (gpiod_get_value(charge_gpiod) ?
0105 POWER_SUPPLY_STATUS_DISCHARGING :
0106 POWER_SUPPLY_STATUS_CHARGING) :
0107 POWER_SUPPLY_STATUS_UNKNOWN;
0108
0109 if (old_status != bat_status) {
0110 pr_debug("%s: %i -> %i\n", bat_ps->desc->name, old_status,
0111 bat_status);
0112 power_supply_changed(bat_ps);
0113 }
0114
0115 mutex_unlock(&work_lock);
0116 }
0117
0118 static struct power_supply *bat_psy;
0119 static struct power_supply_desc bat_psy_desc = {
0120 .type = POWER_SUPPLY_TYPE_BATTERY,
0121 .get_property = wm97xx_bat_get_property,
0122 .external_power_changed = wm97xx_bat_external_power_changed,
0123 .use_for_apm = 1,
0124 };
0125
0126 static void wm97xx_bat_work(struct work_struct *work)
0127 {
0128 wm97xx_bat_update(bat_psy);
0129 }
0130
0131 static irqreturn_t wm97xx_chrg_irq(int irq, void *data)
0132 {
0133 schedule_work(&bat_work);
0134 return IRQ_HANDLED;
0135 }
0136
0137 #ifdef CONFIG_PM
0138 static int wm97xx_bat_suspend(struct device *dev)
0139 {
0140 flush_work(&bat_work);
0141 return 0;
0142 }
0143
0144 static int wm97xx_bat_resume(struct device *dev)
0145 {
0146 schedule_work(&bat_work);
0147 return 0;
0148 }
0149
0150 static const struct dev_pm_ops wm97xx_bat_pm_ops = {
0151 .suspend = wm97xx_bat_suspend,
0152 .resume = wm97xx_bat_resume,
0153 };
0154 #endif
0155
0156 static int wm97xx_bat_probe(struct platform_device *dev)
0157 {
0158 int ret = 0;
0159 int props = 1;
0160 int i = 0;
0161 struct wm97xx_batt_pdata *pdata = dev->dev.platform_data;
0162 struct power_supply_config cfg = {};
0163
0164 if (!pdata) {
0165 dev_err(&dev->dev, "No platform data supplied\n");
0166 return -EINVAL;
0167 }
0168
0169 cfg.drv_data = pdata;
0170
0171 if (dev->id != -1)
0172 return -EINVAL;
0173
0174 charge_gpiod = devm_gpiod_get_optional(&dev->dev, NULL, GPIOD_IN);
0175 if (IS_ERR(charge_gpiod))
0176 return dev_err_probe(&dev->dev,
0177 PTR_ERR(charge_gpiod),
0178 "failed to get charge GPIO\n");
0179 if (charge_gpiod) {
0180 gpiod_set_consumer_name(charge_gpiod, "BATT CHRG");
0181 ret = request_irq(gpiod_to_irq(charge_gpiod),
0182 wm97xx_chrg_irq, 0,
0183 "AC Detect", dev);
0184 if (ret)
0185 return dev_err_probe(&dev->dev, ret,
0186 "failed to request GPIO irq\n");
0187 props++;
0188 }
0189
0190 if (pdata->batt_tech >= 0)
0191 props++;
0192 if (pdata->temp_aux >= 0)
0193 props++;
0194 if (pdata->batt_aux >= 0)
0195 props++;
0196 if (pdata->max_voltage >= 0)
0197 props++;
0198 if (pdata->min_voltage >= 0)
0199 props++;
0200
0201 prop = kcalloc(props, sizeof(*prop), GFP_KERNEL);
0202 if (!prop) {
0203 ret = -ENOMEM;
0204 goto err3;
0205 }
0206
0207 prop[i++] = POWER_SUPPLY_PROP_PRESENT;
0208 if (charge_gpiod)
0209 prop[i++] = POWER_SUPPLY_PROP_STATUS;
0210 if (pdata->batt_tech >= 0)
0211 prop[i++] = POWER_SUPPLY_PROP_TECHNOLOGY;
0212 if (pdata->temp_aux >= 0)
0213 prop[i++] = POWER_SUPPLY_PROP_TEMP;
0214 if (pdata->batt_aux >= 0)
0215 prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_NOW;
0216 if (pdata->max_voltage >= 0)
0217 prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_MAX;
0218 if (pdata->min_voltage >= 0)
0219 prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_MIN;
0220
0221 INIT_WORK(&bat_work, wm97xx_bat_work);
0222
0223 if (!pdata->batt_name) {
0224 dev_info(&dev->dev, "Please consider setting proper battery "
0225 "name in platform definition file, falling "
0226 "back to name \"wm97xx-batt\"\n");
0227 bat_psy_desc.name = "wm97xx-batt";
0228 } else
0229 bat_psy_desc.name = pdata->batt_name;
0230
0231 bat_psy_desc.properties = prop;
0232 bat_psy_desc.num_properties = props;
0233
0234 bat_psy = power_supply_register(&dev->dev, &bat_psy_desc, &cfg);
0235 if (!IS_ERR(bat_psy)) {
0236 schedule_work(&bat_work);
0237 } else {
0238 ret = PTR_ERR(bat_psy);
0239 goto err4;
0240 }
0241
0242 return 0;
0243 err4:
0244 kfree(prop);
0245 err3:
0246 if (charge_gpiod)
0247 free_irq(gpiod_to_irq(charge_gpiod), dev);
0248 return ret;
0249 }
0250
0251 static int wm97xx_bat_remove(struct platform_device *dev)
0252 {
0253 if (charge_gpiod)
0254 free_irq(gpiod_to_irq(charge_gpiod), dev);
0255 cancel_work_sync(&bat_work);
0256 power_supply_unregister(bat_psy);
0257 kfree(prop);
0258 return 0;
0259 }
0260
0261 static struct platform_driver wm97xx_bat_driver = {
0262 .driver = {
0263 .name = "wm97xx-battery",
0264 #ifdef CONFIG_PM
0265 .pm = &wm97xx_bat_pm_ops,
0266 #endif
0267 },
0268 .probe = wm97xx_bat_probe,
0269 .remove = wm97xx_bat_remove,
0270 };
0271
0272 module_platform_driver(wm97xx_bat_driver);
0273
0274 MODULE_LICENSE("GPL");
0275 MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>");
0276 MODULE_DESCRIPTION("WM97xx battery driver");