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