1*8c0984e5SSebastian Reichel /* 2*8c0984e5SSebastian Reichel * Power supply driver for the goldfish emulator 3*8c0984e5SSebastian Reichel * 4*8c0984e5SSebastian Reichel * Copyright (C) 2008 Google, Inc. 5*8c0984e5SSebastian Reichel * Copyright (C) 2012 Intel, Inc. 6*8c0984e5SSebastian Reichel * Copyright (C) 2013 Intel, Inc. 7*8c0984e5SSebastian Reichel * Author: Mike Lockwood <lockwood@android.com> 8*8c0984e5SSebastian Reichel * 9*8c0984e5SSebastian Reichel * This software is licensed under the terms of the GNU General Public 10*8c0984e5SSebastian Reichel * License version 2, as published by the Free Software Foundation, and 11*8c0984e5SSebastian Reichel * may be copied, distributed, and modified under those terms. 12*8c0984e5SSebastian Reichel * 13*8c0984e5SSebastian Reichel * This program is distributed in the hope that it will be useful, 14*8c0984e5SSebastian Reichel * but WITHOUT ANY WARRANTY; without even the implied warranty of 15*8c0984e5SSebastian Reichel * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16*8c0984e5SSebastian Reichel * GNU General Public License for more details. 17*8c0984e5SSebastian Reichel */ 18*8c0984e5SSebastian Reichel 19*8c0984e5SSebastian Reichel #include <linux/module.h> 20*8c0984e5SSebastian Reichel #include <linux/err.h> 21*8c0984e5SSebastian Reichel #include <linux/platform_device.h> 22*8c0984e5SSebastian Reichel #include <linux/power_supply.h> 23*8c0984e5SSebastian Reichel #include <linux/types.h> 24*8c0984e5SSebastian Reichel #include <linux/pci.h> 25*8c0984e5SSebastian Reichel #include <linux/interrupt.h> 26*8c0984e5SSebastian Reichel #include <linux/io.h> 27*8c0984e5SSebastian Reichel #include <linux/acpi.h> 28*8c0984e5SSebastian Reichel 29*8c0984e5SSebastian Reichel struct goldfish_battery_data { 30*8c0984e5SSebastian Reichel void __iomem *reg_base; 31*8c0984e5SSebastian Reichel int irq; 32*8c0984e5SSebastian Reichel spinlock_t lock; 33*8c0984e5SSebastian Reichel 34*8c0984e5SSebastian Reichel struct power_supply *battery; 35*8c0984e5SSebastian Reichel struct power_supply *ac; 36*8c0984e5SSebastian Reichel }; 37*8c0984e5SSebastian Reichel 38*8c0984e5SSebastian Reichel #define GOLDFISH_BATTERY_READ(data, addr) \ 39*8c0984e5SSebastian Reichel (readl(data->reg_base + addr)) 40*8c0984e5SSebastian Reichel #define GOLDFISH_BATTERY_WRITE(data, addr, x) \ 41*8c0984e5SSebastian Reichel (writel(x, data->reg_base + addr)) 42*8c0984e5SSebastian Reichel 43*8c0984e5SSebastian Reichel /* 44*8c0984e5SSebastian Reichel * Temporary variable used between goldfish_battery_probe() and 45*8c0984e5SSebastian Reichel * goldfish_battery_open(). 46*8c0984e5SSebastian Reichel */ 47*8c0984e5SSebastian Reichel static struct goldfish_battery_data *battery_data; 48*8c0984e5SSebastian Reichel 49*8c0984e5SSebastian Reichel enum { 50*8c0984e5SSebastian Reichel /* status register */ 51*8c0984e5SSebastian Reichel BATTERY_INT_STATUS = 0x00, 52*8c0984e5SSebastian Reichel /* set this to enable IRQ */ 53*8c0984e5SSebastian Reichel BATTERY_INT_ENABLE = 0x04, 54*8c0984e5SSebastian Reichel 55*8c0984e5SSebastian Reichel BATTERY_AC_ONLINE = 0x08, 56*8c0984e5SSebastian Reichel BATTERY_STATUS = 0x0C, 57*8c0984e5SSebastian Reichel BATTERY_HEALTH = 0x10, 58*8c0984e5SSebastian Reichel BATTERY_PRESENT = 0x14, 59*8c0984e5SSebastian Reichel BATTERY_CAPACITY = 0x18, 60*8c0984e5SSebastian Reichel 61*8c0984e5SSebastian Reichel BATTERY_STATUS_CHANGED = 1U << 0, 62*8c0984e5SSebastian Reichel AC_STATUS_CHANGED = 1U << 1, 63*8c0984e5SSebastian Reichel BATTERY_INT_MASK = BATTERY_STATUS_CHANGED | AC_STATUS_CHANGED, 64*8c0984e5SSebastian Reichel }; 65*8c0984e5SSebastian Reichel 66*8c0984e5SSebastian Reichel 67*8c0984e5SSebastian Reichel static int goldfish_ac_get_property(struct power_supply *psy, 68*8c0984e5SSebastian Reichel enum power_supply_property psp, 69*8c0984e5SSebastian Reichel union power_supply_propval *val) 70*8c0984e5SSebastian Reichel { 71*8c0984e5SSebastian Reichel struct goldfish_battery_data *data = power_supply_get_drvdata(psy); 72*8c0984e5SSebastian Reichel int ret = 0; 73*8c0984e5SSebastian Reichel 74*8c0984e5SSebastian Reichel switch (psp) { 75*8c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_ONLINE: 76*8c0984e5SSebastian Reichel val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_AC_ONLINE); 77*8c0984e5SSebastian Reichel break; 78*8c0984e5SSebastian Reichel default: 79*8c0984e5SSebastian Reichel ret = -EINVAL; 80*8c0984e5SSebastian Reichel break; 81*8c0984e5SSebastian Reichel } 82*8c0984e5SSebastian Reichel return ret; 83*8c0984e5SSebastian Reichel } 84*8c0984e5SSebastian Reichel 85*8c0984e5SSebastian Reichel static int goldfish_battery_get_property(struct power_supply *psy, 86*8c0984e5SSebastian Reichel enum power_supply_property psp, 87*8c0984e5SSebastian Reichel union power_supply_propval *val) 88*8c0984e5SSebastian Reichel { 89*8c0984e5SSebastian Reichel struct goldfish_battery_data *data = power_supply_get_drvdata(psy); 90*8c0984e5SSebastian Reichel int ret = 0; 91*8c0984e5SSebastian Reichel 92*8c0984e5SSebastian Reichel switch (psp) { 93*8c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_STATUS: 94*8c0984e5SSebastian Reichel val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_STATUS); 95*8c0984e5SSebastian Reichel break; 96*8c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_HEALTH: 97*8c0984e5SSebastian Reichel val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_HEALTH); 98*8c0984e5SSebastian Reichel break; 99*8c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_PRESENT: 100*8c0984e5SSebastian Reichel val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_PRESENT); 101*8c0984e5SSebastian Reichel break; 102*8c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_TECHNOLOGY: 103*8c0984e5SSebastian Reichel val->intval = POWER_SUPPLY_TECHNOLOGY_LION; 104*8c0984e5SSebastian Reichel break; 105*8c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_CAPACITY: 106*8c0984e5SSebastian Reichel val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_CAPACITY); 107*8c0984e5SSebastian Reichel break; 108*8c0984e5SSebastian Reichel default: 109*8c0984e5SSebastian Reichel ret = -EINVAL; 110*8c0984e5SSebastian Reichel break; 111*8c0984e5SSebastian Reichel } 112*8c0984e5SSebastian Reichel 113*8c0984e5SSebastian Reichel return ret; 114*8c0984e5SSebastian Reichel } 115*8c0984e5SSebastian Reichel 116*8c0984e5SSebastian Reichel static enum power_supply_property goldfish_battery_props[] = { 117*8c0984e5SSebastian Reichel POWER_SUPPLY_PROP_STATUS, 118*8c0984e5SSebastian Reichel POWER_SUPPLY_PROP_HEALTH, 119*8c0984e5SSebastian Reichel POWER_SUPPLY_PROP_PRESENT, 120*8c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TECHNOLOGY, 121*8c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CAPACITY, 122*8c0984e5SSebastian Reichel }; 123*8c0984e5SSebastian Reichel 124*8c0984e5SSebastian Reichel static enum power_supply_property goldfish_ac_props[] = { 125*8c0984e5SSebastian Reichel POWER_SUPPLY_PROP_ONLINE, 126*8c0984e5SSebastian Reichel }; 127*8c0984e5SSebastian Reichel 128*8c0984e5SSebastian Reichel static irqreturn_t goldfish_battery_interrupt(int irq, void *dev_id) 129*8c0984e5SSebastian Reichel { 130*8c0984e5SSebastian Reichel unsigned long irq_flags; 131*8c0984e5SSebastian Reichel struct goldfish_battery_data *data = dev_id; 132*8c0984e5SSebastian Reichel uint32_t status; 133*8c0984e5SSebastian Reichel 134*8c0984e5SSebastian Reichel spin_lock_irqsave(&data->lock, irq_flags); 135*8c0984e5SSebastian Reichel 136*8c0984e5SSebastian Reichel /* read status flags, which will clear the interrupt */ 137*8c0984e5SSebastian Reichel status = GOLDFISH_BATTERY_READ(data, BATTERY_INT_STATUS); 138*8c0984e5SSebastian Reichel status &= BATTERY_INT_MASK; 139*8c0984e5SSebastian Reichel 140*8c0984e5SSebastian Reichel if (status & BATTERY_STATUS_CHANGED) 141*8c0984e5SSebastian Reichel power_supply_changed(data->battery); 142*8c0984e5SSebastian Reichel if (status & AC_STATUS_CHANGED) 143*8c0984e5SSebastian Reichel power_supply_changed(data->ac); 144*8c0984e5SSebastian Reichel 145*8c0984e5SSebastian Reichel spin_unlock_irqrestore(&data->lock, irq_flags); 146*8c0984e5SSebastian Reichel return status ? IRQ_HANDLED : IRQ_NONE; 147*8c0984e5SSebastian Reichel } 148*8c0984e5SSebastian Reichel 149*8c0984e5SSebastian Reichel static const struct power_supply_desc battery_desc = { 150*8c0984e5SSebastian Reichel .properties = goldfish_battery_props, 151*8c0984e5SSebastian Reichel .num_properties = ARRAY_SIZE(goldfish_battery_props), 152*8c0984e5SSebastian Reichel .get_property = goldfish_battery_get_property, 153*8c0984e5SSebastian Reichel .name = "battery", 154*8c0984e5SSebastian Reichel .type = POWER_SUPPLY_TYPE_BATTERY, 155*8c0984e5SSebastian Reichel }; 156*8c0984e5SSebastian Reichel 157*8c0984e5SSebastian Reichel static const struct power_supply_desc ac_desc = { 158*8c0984e5SSebastian Reichel .properties = goldfish_ac_props, 159*8c0984e5SSebastian Reichel .num_properties = ARRAY_SIZE(goldfish_ac_props), 160*8c0984e5SSebastian Reichel .get_property = goldfish_ac_get_property, 161*8c0984e5SSebastian Reichel .name = "ac", 162*8c0984e5SSebastian Reichel .type = POWER_SUPPLY_TYPE_MAINS, 163*8c0984e5SSebastian Reichel }; 164*8c0984e5SSebastian Reichel 165*8c0984e5SSebastian Reichel static int goldfish_battery_probe(struct platform_device *pdev) 166*8c0984e5SSebastian Reichel { 167*8c0984e5SSebastian Reichel int ret; 168*8c0984e5SSebastian Reichel struct resource *r; 169*8c0984e5SSebastian Reichel struct goldfish_battery_data *data; 170*8c0984e5SSebastian Reichel struct power_supply_config psy_cfg = {}; 171*8c0984e5SSebastian Reichel 172*8c0984e5SSebastian Reichel data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); 173*8c0984e5SSebastian Reichel if (data == NULL) 174*8c0984e5SSebastian Reichel return -ENOMEM; 175*8c0984e5SSebastian Reichel 176*8c0984e5SSebastian Reichel spin_lock_init(&data->lock); 177*8c0984e5SSebastian Reichel 178*8c0984e5SSebastian Reichel r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 179*8c0984e5SSebastian Reichel if (r == NULL) { 180*8c0984e5SSebastian Reichel dev_err(&pdev->dev, "platform_get_resource failed\n"); 181*8c0984e5SSebastian Reichel return -ENODEV; 182*8c0984e5SSebastian Reichel } 183*8c0984e5SSebastian Reichel 184*8c0984e5SSebastian Reichel data->reg_base = devm_ioremap(&pdev->dev, r->start, resource_size(r)); 185*8c0984e5SSebastian Reichel if (data->reg_base == NULL) { 186*8c0984e5SSebastian Reichel dev_err(&pdev->dev, "unable to remap MMIO\n"); 187*8c0984e5SSebastian Reichel return -ENOMEM; 188*8c0984e5SSebastian Reichel } 189*8c0984e5SSebastian Reichel 190*8c0984e5SSebastian Reichel data->irq = platform_get_irq(pdev, 0); 191*8c0984e5SSebastian Reichel if (data->irq < 0) { 192*8c0984e5SSebastian Reichel dev_err(&pdev->dev, "platform_get_irq failed\n"); 193*8c0984e5SSebastian Reichel return -ENODEV; 194*8c0984e5SSebastian Reichel } 195*8c0984e5SSebastian Reichel 196*8c0984e5SSebastian Reichel ret = devm_request_irq(&pdev->dev, data->irq, goldfish_battery_interrupt, 197*8c0984e5SSebastian Reichel IRQF_SHARED, pdev->name, data); 198*8c0984e5SSebastian Reichel if (ret) 199*8c0984e5SSebastian Reichel return ret; 200*8c0984e5SSebastian Reichel 201*8c0984e5SSebastian Reichel psy_cfg.drv_data = data; 202*8c0984e5SSebastian Reichel 203*8c0984e5SSebastian Reichel data->ac = power_supply_register(&pdev->dev, &ac_desc, &psy_cfg); 204*8c0984e5SSebastian Reichel if (IS_ERR(data->ac)) 205*8c0984e5SSebastian Reichel return PTR_ERR(data->ac); 206*8c0984e5SSebastian Reichel 207*8c0984e5SSebastian Reichel data->battery = power_supply_register(&pdev->dev, &battery_desc, 208*8c0984e5SSebastian Reichel &psy_cfg); 209*8c0984e5SSebastian Reichel if (IS_ERR(data->battery)) { 210*8c0984e5SSebastian Reichel power_supply_unregister(data->ac); 211*8c0984e5SSebastian Reichel return PTR_ERR(data->battery); 212*8c0984e5SSebastian Reichel } 213*8c0984e5SSebastian Reichel 214*8c0984e5SSebastian Reichel platform_set_drvdata(pdev, data); 215*8c0984e5SSebastian Reichel battery_data = data; 216*8c0984e5SSebastian Reichel 217*8c0984e5SSebastian Reichel GOLDFISH_BATTERY_WRITE(data, BATTERY_INT_ENABLE, BATTERY_INT_MASK); 218*8c0984e5SSebastian Reichel return 0; 219*8c0984e5SSebastian Reichel } 220*8c0984e5SSebastian Reichel 221*8c0984e5SSebastian Reichel static int goldfish_battery_remove(struct platform_device *pdev) 222*8c0984e5SSebastian Reichel { 223*8c0984e5SSebastian Reichel struct goldfish_battery_data *data = platform_get_drvdata(pdev); 224*8c0984e5SSebastian Reichel 225*8c0984e5SSebastian Reichel power_supply_unregister(data->battery); 226*8c0984e5SSebastian Reichel power_supply_unregister(data->ac); 227*8c0984e5SSebastian Reichel battery_data = NULL; 228*8c0984e5SSebastian Reichel return 0; 229*8c0984e5SSebastian Reichel } 230*8c0984e5SSebastian Reichel 231*8c0984e5SSebastian Reichel static const struct of_device_id goldfish_battery_of_match[] = { 232*8c0984e5SSebastian Reichel { .compatible = "google,goldfish-battery", }, 233*8c0984e5SSebastian Reichel {}, 234*8c0984e5SSebastian Reichel }; 235*8c0984e5SSebastian Reichel MODULE_DEVICE_TABLE(of, goldfish_battery_of_match); 236*8c0984e5SSebastian Reichel 237*8c0984e5SSebastian Reichel static const struct acpi_device_id goldfish_battery_acpi_match[] = { 238*8c0984e5SSebastian Reichel { "GFSH0001", 0 }, 239*8c0984e5SSebastian Reichel { }, 240*8c0984e5SSebastian Reichel }; 241*8c0984e5SSebastian Reichel MODULE_DEVICE_TABLE(acpi, goldfish_battery_acpi_match); 242*8c0984e5SSebastian Reichel 243*8c0984e5SSebastian Reichel static struct platform_driver goldfish_battery_device = { 244*8c0984e5SSebastian Reichel .probe = goldfish_battery_probe, 245*8c0984e5SSebastian Reichel .remove = goldfish_battery_remove, 246*8c0984e5SSebastian Reichel .driver = { 247*8c0984e5SSebastian Reichel .name = "goldfish-battery", 248*8c0984e5SSebastian Reichel .of_match_table = goldfish_battery_of_match, 249*8c0984e5SSebastian Reichel .acpi_match_table = ACPI_PTR(goldfish_battery_acpi_match), 250*8c0984e5SSebastian Reichel } 251*8c0984e5SSebastian Reichel }; 252*8c0984e5SSebastian Reichel module_platform_driver(goldfish_battery_device); 253*8c0984e5SSebastian Reichel 254*8c0984e5SSebastian Reichel MODULE_AUTHOR("Mike Lockwood lockwood@android.com"); 255*8c0984e5SSebastian Reichel MODULE_LICENSE("GPL"); 256*8c0984e5SSebastian Reichel MODULE_DESCRIPTION("Battery driver for the Goldfish emulator"); 257