1*8c0984e5SSebastian Reichel /* 2*8c0984e5SSebastian Reichel * Battery and Power Management code for the Sharp SL-5x00 3*8c0984e5SSebastian Reichel * 4*8c0984e5SSebastian Reichel * Copyright (C) 2009 Thomas Kunze 5*8c0984e5SSebastian Reichel * 6*8c0984e5SSebastian Reichel * based on tosa_battery.c 7*8c0984e5SSebastian Reichel * 8*8c0984e5SSebastian Reichel * This program is free software; you can redistribute it and/or modify 9*8c0984e5SSebastian Reichel * it under the terms of the GNU General Public License version 2 as 10*8c0984e5SSebastian Reichel * published by the Free Software Foundation. 11*8c0984e5SSebastian Reichel * 12*8c0984e5SSebastian Reichel */ 13*8c0984e5SSebastian Reichel #include <linux/kernel.h> 14*8c0984e5SSebastian Reichel #include <linux/module.h> 15*8c0984e5SSebastian Reichel #include <linux/power_supply.h> 16*8c0984e5SSebastian Reichel #include <linux/delay.h> 17*8c0984e5SSebastian Reichel #include <linux/spinlock.h> 18*8c0984e5SSebastian Reichel #include <linux/interrupt.h> 19*8c0984e5SSebastian Reichel #include <linux/gpio.h> 20*8c0984e5SSebastian Reichel #include <linux/mfd/ucb1x00.h> 21*8c0984e5SSebastian Reichel 22*8c0984e5SSebastian Reichel #include <asm/mach/sharpsl_param.h> 23*8c0984e5SSebastian Reichel #include <asm/mach-types.h> 24*8c0984e5SSebastian Reichel #include <mach/collie.h> 25*8c0984e5SSebastian Reichel 26*8c0984e5SSebastian Reichel static DEFINE_MUTEX(bat_lock); /* protects gpio pins */ 27*8c0984e5SSebastian Reichel static struct work_struct bat_work; 28*8c0984e5SSebastian Reichel static struct ucb1x00 *ucb; 29*8c0984e5SSebastian Reichel 30*8c0984e5SSebastian Reichel struct collie_bat { 31*8c0984e5SSebastian Reichel int status; 32*8c0984e5SSebastian Reichel struct power_supply *psy; 33*8c0984e5SSebastian Reichel int full_chrg; 34*8c0984e5SSebastian Reichel 35*8c0984e5SSebastian Reichel struct mutex work_lock; /* protects data */ 36*8c0984e5SSebastian Reichel 37*8c0984e5SSebastian Reichel bool (*is_present)(struct collie_bat *bat); 38*8c0984e5SSebastian Reichel int gpio_full; 39*8c0984e5SSebastian Reichel int gpio_charge_on; 40*8c0984e5SSebastian Reichel 41*8c0984e5SSebastian Reichel int technology; 42*8c0984e5SSebastian Reichel 43*8c0984e5SSebastian Reichel int gpio_bat; 44*8c0984e5SSebastian Reichel int adc_bat; 45*8c0984e5SSebastian Reichel int adc_bat_divider; 46*8c0984e5SSebastian Reichel int bat_max; 47*8c0984e5SSebastian Reichel int bat_min; 48*8c0984e5SSebastian Reichel 49*8c0984e5SSebastian Reichel int gpio_temp; 50*8c0984e5SSebastian Reichel int adc_temp; 51*8c0984e5SSebastian Reichel int adc_temp_divider; 52*8c0984e5SSebastian Reichel }; 53*8c0984e5SSebastian Reichel 54*8c0984e5SSebastian Reichel static struct collie_bat collie_bat_main; 55*8c0984e5SSebastian Reichel 56*8c0984e5SSebastian Reichel static unsigned long collie_read_bat(struct collie_bat *bat) 57*8c0984e5SSebastian Reichel { 58*8c0984e5SSebastian Reichel unsigned long value = 0; 59*8c0984e5SSebastian Reichel 60*8c0984e5SSebastian Reichel if (bat->gpio_bat < 0 || bat->adc_bat < 0) 61*8c0984e5SSebastian Reichel return 0; 62*8c0984e5SSebastian Reichel mutex_lock(&bat_lock); 63*8c0984e5SSebastian Reichel gpio_set_value(bat->gpio_bat, 1); 64*8c0984e5SSebastian Reichel msleep(5); 65*8c0984e5SSebastian Reichel ucb1x00_adc_enable(ucb); 66*8c0984e5SSebastian Reichel value = ucb1x00_adc_read(ucb, bat->adc_bat, UCB_SYNC); 67*8c0984e5SSebastian Reichel ucb1x00_adc_disable(ucb); 68*8c0984e5SSebastian Reichel gpio_set_value(bat->gpio_bat, 0); 69*8c0984e5SSebastian Reichel mutex_unlock(&bat_lock); 70*8c0984e5SSebastian Reichel value = value * 1000000 / bat->adc_bat_divider; 71*8c0984e5SSebastian Reichel 72*8c0984e5SSebastian Reichel return value; 73*8c0984e5SSebastian Reichel } 74*8c0984e5SSebastian Reichel 75*8c0984e5SSebastian Reichel static unsigned long collie_read_temp(struct collie_bat *bat) 76*8c0984e5SSebastian Reichel { 77*8c0984e5SSebastian Reichel unsigned long value = 0; 78*8c0984e5SSebastian Reichel if (bat->gpio_temp < 0 || bat->adc_temp < 0) 79*8c0984e5SSebastian Reichel return 0; 80*8c0984e5SSebastian Reichel 81*8c0984e5SSebastian Reichel mutex_lock(&bat_lock); 82*8c0984e5SSebastian Reichel gpio_set_value(bat->gpio_temp, 1); 83*8c0984e5SSebastian Reichel msleep(5); 84*8c0984e5SSebastian Reichel ucb1x00_adc_enable(ucb); 85*8c0984e5SSebastian Reichel value = ucb1x00_adc_read(ucb, bat->adc_temp, UCB_SYNC); 86*8c0984e5SSebastian Reichel ucb1x00_adc_disable(ucb); 87*8c0984e5SSebastian Reichel gpio_set_value(bat->gpio_temp, 0); 88*8c0984e5SSebastian Reichel mutex_unlock(&bat_lock); 89*8c0984e5SSebastian Reichel 90*8c0984e5SSebastian Reichel value = value * 10000 / bat->adc_temp_divider; 91*8c0984e5SSebastian Reichel 92*8c0984e5SSebastian Reichel return value; 93*8c0984e5SSebastian Reichel } 94*8c0984e5SSebastian Reichel 95*8c0984e5SSebastian Reichel static int collie_bat_get_property(struct power_supply *psy, 96*8c0984e5SSebastian Reichel enum power_supply_property psp, 97*8c0984e5SSebastian Reichel union power_supply_propval *val) 98*8c0984e5SSebastian Reichel { 99*8c0984e5SSebastian Reichel int ret = 0; 100*8c0984e5SSebastian Reichel struct collie_bat *bat = power_supply_get_drvdata(psy); 101*8c0984e5SSebastian Reichel 102*8c0984e5SSebastian Reichel if (bat->is_present && !bat->is_present(bat) 103*8c0984e5SSebastian Reichel && psp != POWER_SUPPLY_PROP_PRESENT) { 104*8c0984e5SSebastian Reichel return -ENODEV; 105*8c0984e5SSebastian Reichel } 106*8c0984e5SSebastian Reichel 107*8c0984e5SSebastian Reichel switch (psp) { 108*8c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_STATUS: 109*8c0984e5SSebastian Reichel val->intval = bat->status; 110*8c0984e5SSebastian Reichel break; 111*8c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_TECHNOLOGY: 112*8c0984e5SSebastian Reichel val->intval = bat->technology; 113*8c0984e5SSebastian Reichel break; 114*8c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_VOLTAGE_NOW: 115*8c0984e5SSebastian Reichel val->intval = collie_read_bat(bat); 116*8c0984e5SSebastian Reichel break; 117*8c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_VOLTAGE_MAX: 118*8c0984e5SSebastian Reichel if (bat->full_chrg == -1) 119*8c0984e5SSebastian Reichel val->intval = bat->bat_max; 120*8c0984e5SSebastian Reichel else 121*8c0984e5SSebastian Reichel val->intval = bat->full_chrg; 122*8c0984e5SSebastian Reichel break; 123*8c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: 124*8c0984e5SSebastian Reichel val->intval = bat->bat_max; 125*8c0984e5SSebastian Reichel break; 126*8c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: 127*8c0984e5SSebastian Reichel val->intval = bat->bat_min; 128*8c0984e5SSebastian Reichel break; 129*8c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_TEMP: 130*8c0984e5SSebastian Reichel val->intval = collie_read_temp(bat); 131*8c0984e5SSebastian Reichel break; 132*8c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_PRESENT: 133*8c0984e5SSebastian Reichel val->intval = bat->is_present ? bat->is_present(bat) : 1; 134*8c0984e5SSebastian Reichel break; 135*8c0984e5SSebastian Reichel default: 136*8c0984e5SSebastian Reichel ret = -EINVAL; 137*8c0984e5SSebastian Reichel break; 138*8c0984e5SSebastian Reichel } 139*8c0984e5SSebastian Reichel return ret; 140*8c0984e5SSebastian Reichel } 141*8c0984e5SSebastian Reichel 142*8c0984e5SSebastian Reichel static void collie_bat_external_power_changed(struct power_supply *psy) 143*8c0984e5SSebastian Reichel { 144*8c0984e5SSebastian Reichel schedule_work(&bat_work); 145*8c0984e5SSebastian Reichel } 146*8c0984e5SSebastian Reichel 147*8c0984e5SSebastian Reichel static irqreturn_t collie_bat_gpio_isr(int irq, void *data) 148*8c0984e5SSebastian Reichel { 149*8c0984e5SSebastian Reichel pr_info("collie_bat_gpio irq\n"); 150*8c0984e5SSebastian Reichel schedule_work(&bat_work); 151*8c0984e5SSebastian Reichel return IRQ_HANDLED; 152*8c0984e5SSebastian Reichel } 153*8c0984e5SSebastian Reichel 154*8c0984e5SSebastian Reichel static void collie_bat_update(struct collie_bat *bat) 155*8c0984e5SSebastian Reichel { 156*8c0984e5SSebastian Reichel int old; 157*8c0984e5SSebastian Reichel struct power_supply *psy = bat->psy; 158*8c0984e5SSebastian Reichel 159*8c0984e5SSebastian Reichel mutex_lock(&bat->work_lock); 160*8c0984e5SSebastian Reichel 161*8c0984e5SSebastian Reichel old = bat->status; 162*8c0984e5SSebastian Reichel 163*8c0984e5SSebastian Reichel if (bat->is_present && !bat->is_present(bat)) { 164*8c0984e5SSebastian Reichel printk(KERN_NOTICE "%s not present\n", psy->desc->name); 165*8c0984e5SSebastian Reichel bat->status = POWER_SUPPLY_STATUS_UNKNOWN; 166*8c0984e5SSebastian Reichel bat->full_chrg = -1; 167*8c0984e5SSebastian Reichel } else if (power_supply_am_i_supplied(psy)) { 168*8c0984e5SSebastian Reichel if (bat->status == POWER_SUPPLY_STATUS_DISCHARGING) { 169*8c0984e5SSebastian Reichel gpio_set_value(bat->gpio_charge_on, 1); 170*8c0984e5SSebastian Reichel mdelay(15); 171*8c0984e5SSebastian Reichel } 172*8c0984e5SSebastian Reichel 173*8c0984e5SSebastian Reichel if (gpio_get_value(bat->gpio_full)) { 174*8c0984e5SSebastian Reichel if (old == POWER_SUPPLY_STATUS_CHARGING || 175*8c0984e5SSebastian Reichel bat->full_chrg == -1) 176*8c0984e5SSebastian Reichel bat->full_chrg = collie_read_bat(bat); 177*8c0984e5SSebastian Reichel 178*8c0984e5SSebastian Reichel gpio_set_value(bat->gpio_charge_on, 0); 179*8c0984e5SSebastian Reichel bat->status = POWER_SUPPLY_STATUS_FULL; 180*8c0984e5SSebastian Reichel } else { 181*8c0984e5SSebastian Reichel gpio_set_value(bat->gpio_charge_on, 1); 182*8c0984e5SSebastian Reichel bat->status = POWER_SUPPLY_STATUS_CHARGING; 183*8c0984e5SSebastian Reichel } 184*8c0984e5SSebastian Reichel } else { 185*8c0984e5SSebastian Reichel gpio_set_value(bat->gpio_charge_on, 0); 186*8c0984e5SSebastian Reichel bat->status = POWER_SUPPLY_STATUS_DISCHARGING; 187*8c0984e5SSebastian Reichel } 188*8c0984e5SSebastian Reichel 189*8c0984e5SSebastian Reichel if (old != bat->status) 190*8c0984e5SSebastian Reichel power_supply_changed(psy); 191*8c0984e5SSebastian Reichel 192*8c0984e5SSebastian Reichel mutex_unlock(&bat->work_lock); 193*8c0984e5SSebastian Reichel } 194*8c0984e5SSebastian Reichel 195*8c0984e5SSebastian Reichel static void collie_bat_work(struct work_struct *work) 196*8c0984e5SSebastian Reichel { 197*8c0984e5SSebastian Reichel collie_bat_update(&collie_bat_main); 198*8c0984e5SSebastian Reichel } 199*8c0984e5SSebastian Reichel 200*8c0984e5SSebastian Reichel 201*8c0984e5SSebastian Reichel static enum power_supply_property collie_bat_main_props[] = { 202*8c0984e5SSebastian Reichel POWER_SUPPLY_PROP_STATUS, 203*8c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TECHNOLOGY, 204*8c0984e5SSebastian Reichel POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, 205*8c0984e5SSebastian Reichel POWER_SUPPLY_PROP_VOLTAGE_NOW, 206*8c0984e5SSebastian Reichel POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, 207*8c0984e5SSebastian Reichel POWER_SUPPLY_PROP_VOLTAGE_MAX, 208*8c0984e5SSebastian Reichel POWER_SUPPLY_PROP_PRESENT, 209*8c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TEMP, 210*8c0984e5SSebastian Reichel }; 211*8c0984e5SSebastian Reichel 212*8c0984e5SSebastian Reichel static enum power_supply_property collie_bat_bu_props[] = { 213*8c0984e5SSebastian Reichel POWER_SUPPLY_PROP_STATUS, 214*8c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TECHNOLOGY, 215*8c0984e5SSebastian Reichel POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, 216*8c0984e5SSebastian Reichel POWER_SUPPLY_PROP_VOLTAGE_NOW, 217*8c0984e5SSebastian Reichel POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, 218*8c0984e5SSebastian Reichel POWER_SUPPLY_PROP_VOLTAGE_MAX, 219*8c0984e5SSebastian Reichel POWER_SUPPLY_PROP_PRESENT, 220*8c0984e5SSebastian Reichel }; 221*8c0984e5SSebastian Reichel 222*8c0984e5SSebastian Reichel static const struct power_supply_desc collie_bat_main_desc = { 223*8c0984e5SSebastian Reichel .name = "main-battery", 224*8c0984e5SSebastian Reichel .type = POWER_SUPPLY_TYPE_BATTERY, 225*8c0984e5SSebastian Reichel .properties = collie_bat_main_props, 226*8c0984e5SSebastian Reichel .num_properties = ARRAY_SIZE(collie_bat_main_props), 227*8c0984e5SSebastian Reichel .get_property = collie_bat_get_property, 228*8c0984e5SSebastian Reichel .external_power_changed = collie_bat_external_power_changed, 229*8c0984e5SSebastian Reichel .use_for_apm = 1, 230*8c0984e5SSebastian Reichel }; 231*8c0984e5SSebastian Reichel 232*8c0984e5SSebastian Reichel static struct collie_bat collie_bat_main = { 233*8c0984e5SSebastian Reichel .status = POWER_SUPPLY_STATUS_DISCHARGING, 234*8c0984e5SSebastian Reichel .full_chrg = -1, 235*8c0984e5SSebastian Reichel .psy = NULL, 236*8c0984e5SSebastian Reichel 237*8c0984e5SSebastian Reichel .gpio_full = COLLIE_GPIO_CO, 238*8c0984e5SSebastian Reichel .gpio_charge_on = COLLIE_GPIO_CHARGE_ON, 239*8c0984e5SSebastian Reichel 240*8c0984e5SSebastian Reichel .technology = POWER_SUPPLY_TECHNOLOGY_LIPO, 241*8c0984e5SSebastian Reichel 242*8c0984e5SSebastian Reichel .gpio_bat = COLLIE_GPIO_MBAT_ON, 243*8c0984e5SSebastian Reichel .adc_bat = UCB_ADC_INP_AD1, 244*8c0984e5SSebastian Reichel .adc_bat_divider = 155, 245*8c0984e5SSebastian Reichel .bat_max = 4310000, 246*8c0984e5SSebastian Reichel .bat_min = 1551 * 1000000 / 414, 247*8c0984e5SSebastian Reichel 248*8c0984e5SSebastian Reichel .gpio_temp = COLLIE_GPIO_TMP_ON, 249*8c0984e5SSebastian Reichel .adc_temp = UCB_ADC_INP_AD0, 250*8c0984e5SSebastian Reichel .adc_temp_divider = 10000, 251*8c0984e5SSebastian Reichel }; 252*8c0984e5SSebastian Reichel 253*8c0984e5SSebastian Reichel static const struct power_supply_desc collie_bat_bu_desc = { 254*8c0984e5SSebastian Reichel .name = "backup-battery", 255*8c0984e5SSebastian Reichel .type = POWER_SUPPLY_TYPE_BATTERY, 256*8c0984e5SSebastian Reichel .properties = collie_bat_bu_props, 257*8c0984e5SSebastian Reichel .num_properties = ARRAY_SIZE(collie_bat_bu_props), 258*8c0984e5SSebastian Reichel .get_property = collie_bat_get_property, 259*8c0984e5SSebastian Reichel .external_power_changed = collie_bat_external_power_changed, 260*8c0984e5SSebastian Reichel }; 261*8c0984e5SSebastian Reichel 262*8c0984e5SSebastian Reichel static struct collie_bat collie_bat_bu = { 263*8c0984e5SSebastian Reichel .status = POWER_SUPPLY_STATUS_UNKNOWN, 264*8c0984e5SSebastian Reichel .full_chrg = -1, 265*8c0984e5SSebastian Reichel .psy = NULL, 266*8c0984e5SSebastian Reichel 267*8c0984e5SSebastian Reichel .gpio_full = -1, 268*8c0984e5SSebastian Reichel .gpio_charge_on = -1, 269*8c0984e5SSebastian Reichel 270*8c0984e5SSebastian Reichel .technology = POWER_SUPPLY_TECHNOLOGY_LiMn, 271*8c0984e5SSebastian Reichel 272*8c0984e5SSebastian Reichel .gpio_bat = COLLIE_GPIO_BBAT_ON, 273*8c0984e5SSebastian Reichel .adc_bat = UCB_ADC_INP_AD1, 274*8c0984e5SSebastian Reichel .adc_bat_divider = 155, 275*8c0984e5SSebastian Reichel .bat_max = 3000000, 276*8c0984e5SSebastian Reichel .bat_min = 1900000, 277*8c0984e5SSebastian Reichel 278*8c0984e5SSebastian Reichel .gpio_temp = -1, 279*8c0984e5SSebastian Reichel .adc_temp = -1, 280*8c0984e5SSebastian Reichel .adc_temp_divider = -1, 281*8c0984e5SSebastian Reichel }; 282*8c0984e5SSebastian Reichel 283*8c0984e5SSebastian Reichel static struct gpio collie_batt_gpios[] = { 284*8c0984e5SSebastian Reichel { COLLIE_GPIO_CO, GPIOF_IN, "main battery full" }, 285*8c0984e5SSebastian Reichel { COLLIE_GPIO_MAIN_BAT_LOW, GPIOF_IN, "main battery low" }, 286*8c0984e5SSebastian Reichel { COLLIE_GPIO_CHARGE_ON, GPIOF_OUT_INIT_LOW, "main charge on" }, 287*8c0984e5SSebastian Reichel { COLLIE_GPIO_MBAT_ON, GPIOF_OUT_INIT_LOW, "main battery" }, 288*8c0984e5SSebastian Reichel { COLLIE_GPIO_TMP_ON, GPIOF_OUT_INIT_LOW, "main battery temp" }, 289*8c0984e5SSebastian Reichel { COLLIE_GPIO_BBAT_ON, GPIOF_OUT_INIT_LOW, "backup battery" }, 290*8c0984e5SSebastian Reichel }; 291*8c0984e5SSebastian Reichel 292*8c0984e5SSebastian Reichel #ifdef CONFIG_PM 293*8c0984e5SSebastian Reichel static int wakeup_enabled; 294*8c0984e5SSebastian Reichel 295*8c0984e5SSebastian Reichel static int collie_bat_suspend(struct ucb1x00_dev *dev) 296*8c0984e5SSebastian Reichel { 297*8c0984e5SSebastian Reichel /* flush all pending status updates */ 298*8c0984e5SSebastian Reichel flush_work(&bat_work); 299*8c0984e5SSebastian Reichel 300*8c0984e5SSebastian Reichel if (device_may_wakeup(&dev->ucb->dev) && 301*8c0984e5SSebastian Reichel collie_bat_main.status == POWER_SUPPLY_STATUS_CHARGING) 302*8c0984e5SSebastian Reichel wakeup_enabled = !enable_irq_wake(gpio_to_irq(COLLIE_GPIO_CO)); 303*8c0984e5SSebastian Reichel else 304*8c0984e5SSebastian Reichel wakeup_enabled = 0; 305*8c0984e5SSebastian Reichel 306*8c0984e5SSebastian Reichel return 0; 307*8c0984e5SSebastian Reichel } 308*8c0984e5SSebastian Reichel 309*8c0984e5SSebastian Reichel static int collie_bat_resume(struct ucb1x00_dev *dev) 310*8c0984e5SSebastian Reichel { 311*8c0984e5SSebastian Reichel if (wakeup_enabled) 312*8c0984e5SSebastian Reichel disable_irq_wake(gpio_to_irq(COLLIE_GPIO_CO)); 313*8c0984e5SSebastian Reichel 314*8c0984e5SSebastian Reichel /* things may have changed while we were away */ 315*8c0984e5SSebastian Reichel schedule_work(&bat_work); 316*8c0984e5SSebastian Reichel return 0; 317*8c0984e5SSebastian Reichel } 318*8c0984e5SSebastian Reichel #else 319*8c0984e5SSebastian Reichel #define collie_bat_suspend NULL 320*8c0984e5SSebastian Reichel #define collie_bat_resume NULL 321*8c0984e5SSebastian Reichel #endif 322*8c0984e5SSebastian Reichel 323*8c0984e5SSebastian Reichel static int collie_bat_probe(struct ucb1x00_dev *dev) 324*8c0984e5SSebastian Reichel { 325*8c0984e5SSebastian Reichel int ret; 326*8c0984e5SSebastian Reichel struct power_supply_config psy_main_cfg = {}, psy_bu_cfg = {}; 327*8c0984e5SSebastian Reichel 328*8c0984e5SSebastian Reichel if (!machine_is_collie()) 329*8c0984e5SSebastian Reichel return -ENODEV; 330*8c0984e5SSebastian Reichel 331*8c0984e5SSebastian Reichel ucb = dev->ucb; 332*8c0984e5SSebastian Reichel 333*8c0984e5SSebastian Reichel ret = gpio_request_array(collie_batt_gpios, 334*8c0984e5SSebastian Reichel ARRAY_SIZE(collie_batt_gpios)); 335*8c0984e5SSebastian Reichel if (ret) 336*8c0984e5SSebastian Reichel return ret; 337*8c0984e5SSebastian Reichel 338*8c0984e5SSebastian Reichel mutex_init(&collie_bat_main.work_lock); 339*8c0984e5SSebastian Reichel 340*8c0984e5SSebastian Reichel INIT_WORK(&bat_work, collie_bat_work); 341*8c0984e5SSebastian Reichel 342*8c0984e5SSebastian Reichel psy_main_cfg.drv_data = &collie_bat_main; 343*8c0984e5SSebastian Reichel collie_bat_main.psy = power_supply_register(&dev->ucb->dev, 344*8c0984e5SSebastian Reichel &collie_bat_main_desc, 345*8c0984e5SSebastian Reichel &psy_main_cfg); 346*8c0984e5SSebastian Reichel if (IS_ERR(collie_bat_main.psy)) { 347*8c0984e5SSebastian Reichel ret = PTR_ERR(collie_bat_main.psy); 348*8c0984e5SSebastian Reichel goto err_psy_reg_main; 349*8c0984e5SSebastian Reichel } 350*8c0984e5SSebastian Reichel 351*8c0984e5SSebastian Reichel psy_bu_cfg.drv_data = &collie_bat_bu; 352*8c0984e5SSebastian Reichel collie_bat_bu.psy = power_supply_register(&dev->ucb->dev, 353*8c0984e5SSebastian Reichel &collie_bat_bu_desc, 354*8c0984e5SSebastian Reichel &psy_bu_cfg); 355*8c0984e5SSebastian Reichel if (IS_ERR(collie_bat_bu.psy)) { 356*8c0984e5SSebastian Reichel ret = PTR_ERR(collie_bat_bu.psy); 357*8c0984e5SSebastian Reichel goto err_psy_reg_bu; 358*8c0984e5SSebastian Reichel } 359*8c0984e5SSebastian Reichel 360*8c0984e5SSebastian Reichel ret = request_irq(gpio_to_irq(COLLIE_GPIO_CO), 361*8c0984e5SSebastian Reichel collie_bat_gpio_isr, 362*8c0984e5SSebastian Reichel IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, 363*8c0984e5SSebastian Reichel "main full", &collie_bat_main); 364*8c0984e5SSebastian Reichel if (ret) 365*8c0984e5SSebastian Reichel goto err_irq; 366*8c0984e5SSebastian Reichel 367*8c0984e5SSebastian Reichel device_init_wakeup(&ucb->dev, 1); 368*8c0984e5SSebastian Reichel schedule_work(&bat_work); 369*8c0984e5SSebastian Reichel 370*8c0984e5SSebastian Reichel return 0; 371*8c0984e5SSebastian Reichel 372*8c0984e5SSebastian Reichel err_irq: 373*8c0984e5SSebastian Reichel power_supply_unregister(collie_bat_bu.psy); 374*8c0984e5SSebastian Reichel err_psy_reg_bu: 375*8c0984e5SSebastian Reichel power_supply_unregister(collie_bat_main.psy); 376*8c0984e5SSebastian Reichel err_psy_reg_main: 377*8c0984e5SSebastian Reichel 378*8c0984e5SSebastian Reichel /* see comment in collie_bat_remove */ 379*8c0984e5SSebastian Reichel cancel_work_sync(&bat_work); 380*8c0984e5SSebastian Reichel gpio_free_array(collie_batt_gpios, ARRAY_SIZE(collie_batt_gpios)); 381*8c0984e5SSebastian Reichel return ret; 382*8c0984e5SSebastian Reichel } 383*8c0984e5SSebastian Reichel 384*8c0984e5SSebastian Reichel static void collie_bat_remove(struct ucb1x00_dev *dev) 385*8c0984e5SSebastian Reichel { 386*8c0984e5SSebastian Reichel free_irq(gpio_to_irq(COLLIE_GPIO_CO), &collie_bat_main); 387*8c0984e5SSebastian Reichel 388*8c0984e5SSebastian Reichel power_supply_unregister(collie_bat_bu.psy); 389*8c0984e5SSebastian Reichel power_supply_unregister(collie_bat_main.psy); 390*8c0984e5SSebastian Reichel 391*8c0984e5SSebastian Reichel /* 392*8c0984e5SSebastian Reichel * Now cancel the bat_work. We won't get any more schedules, 393*8c0984e5SSebastian Reichel * since all sources (isr and external_power_changed) are 394*8c0984e5SSebastian Reichel * unregistered now. 395*8c0984e5SSebastian Reichel */ 396*8c0984e5SSebastian Reichel cancel_work_sync(&bat_work); 397*8c0984e5SSebastian Reichel gpio_free_array(collie_batt_gpios, ARRAY_SIZE(collie_batt_gpios)); 398*8c0984e5SSebastian Reichel } 399*8c0984e5SSebastian Reichel 400*8c0984e5SSebastian Reichel static struct ucb1x00_driver collie_bat_driver = { 401*8c0984e5SSebastian Reichel .add = collie_bat_probe, 402*8c0984e5SSebastian Reichel .remove = collie_bat_remove, 403*8c0984e5SSebastian Reichel .suspend = collie_bat_suspend, 404*8c0984e5SSebastian Reichel .resume = collie_bat_resume, 405*8c0984e5SSebastian Reichel }; 406*8c0984e5SSebastian Reichel 407*8c0984e5SSebastian Reichel static int __init collie_bat_init(void) 408*8c0984e5SSebastian Reichel { 409*8c0984e5SSebastian Reichel return ucb1x00_register_driver(&collie_bat_driver); 410*8c0984e5SSebastian Reichel } 411*8c0984e5SSebastian Reichel 412*8c0984e5SSebastian Reichel static void __exit collie_bat_exit(void) 413*8c0984e5SSebastian Reichel { 414*8c0984e5SSebastian Reichel ucb1x00_unregister_driver(&collie_bat_driver); 415*8c0984e5SSebastian Reichel } 416*8c0984e5SSebastian Reichel 417*8c0984e5SSebastian Reichel module_init(collie_bat_init); 418*8c0984e5SSebastian Reichel module_exit(collie_bat_exit); 419*8c0984e5SSebastian Reichel 420*8c0984e5SSebastian Reichel MODULE_LICENSE("GPL"); 421*8c0984e5SSebastian Reichel MODULE_AUTHOR("Thomas Kunze"); 422*8c0984e5SSebastian Reichel MODULE_DESCRIPTION("Collie battery driver"); 423