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 = container_of(psy, 71 struct goldfish_battery_data, ac); 72 int ret = 0; 73 74 switch (psp) { 75 case POWER_SUPPLY_PROP_ONLINE: 76 val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_AC_ONLINE); 77 break; 78 default: 79 ret = -EINVAL; 80 break; 81 } 82 return ret; 83} 84 85static int goldfish_battery_get_property(struct power_supply *psy, 86 enum power_supply_property psp, 87 union power_supply_propval *val) 88{ 89 struct goldfish_battery_data *data = container_of(psy, 90 struct goldfish_battery_data, battery); 91 int ret = 0; 92 93 switch (psp) { 94 case POWER_SUPPLY_PROP_STATUS: 95 val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_STATUS); 96 break; 97 case POWER_SUPPLY_PROP_HEALTH: 98 val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_HEALTH); 99 break; 100 case POWER_SUPPLY_PROP_PRESENT: 101 val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_PRESENT); 102 break; 103 case POWER_SUPPLY_PROP_TECHNOLOGY: 104 val->intval = POWER_SUPPLY_TECHNOLOGY_LION; 105 break; 106 case POWER_SUPPLY_PROP_CAPACITY: 107 val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_CAPACITY); 108 break; 109 default: 110 ret = -EINVAL; 111 break; 112 } 113 114 return ret; 115} 116 117static enum power_supply_property goldfish_battery_props[] = { 118 POWER_SUPPLY_PROP_STATUS, 119 POWER_SUPPLY_PROP_HEALTH, 120 POWER_SUPPLY_PROP_PRESENT, 121 POWER_SUPPLY_PROP_TECHNOLOGY, 122 POWER_SUPPLY_PROP_CAPACITY, 123}; 124 125static enum power_supply_property goldfish_ac_props[] = { 126 POWER_SUPPLY_PROP_ONLINE, 127}; 128 129static irqreturn_t goldfish_battery_interrupt(int irq, void *dev_id) 130{ 131 unsigned long irq_flags; 132 struct goldfish_battery_data *data = dev_id; 133 uint32_t status; 134 135 spin_lock_irqsave(&data->lock, irq_flags); 136 137 /* read status flags, which will clear the interrupt */ 138 status = GOLDFISH_BATTERY_READ(data, BATTERY_INT_STATUS); 139 status &= BATTERY_INT_MASK; 140 141 if (status & BATTERY_STATUS_CHANGED) 142 power_supply_changed(&data->battery); 143 if (status & AC_STATUS_CHANGED) 144 power_supply_changed(&data->ac); 145 146 spin_unlock_irqrestore(&data->lock, irq_flags); 147 return status ? IRQ_HANDLED : IRQ_NONE; 148} 149 150 151static int goldfish_battery_probe(struct platform_device *pdev) 152{ 153 int ret; 154 struct resource *r; 155 struct goldfish_battery_data *data; 156 157 data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); 158 if (data == NULL) 159 return -ENOMEM; 160 161 spin_lock_init(&data->lock); 162 163 data->battery.properties = goldfish_battery_props; 164 data->battery.num_properties = ARRAY_SIZE(goldfish_battery_props); 165 data->battery.get_property = goldfish_battery_get_property; 166 data->battery.name = "battery"; 167 data->battery.type = POWER_SUPPLY_TYPE_BATTERY; 168 169 data->ac.properties = goldfish_ac_props; 170 data->ac.num_properties = ARRAY_SIZE(goldfish_ac_props); 171 data->ac.get_property = goldfish_ac_get_property; 172 data->ac.name = "ac"; 173 data->ac.type = POWER_SUPPLY_TYPE_MAINS; 174 175 r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 176 if (r == NULL) { 177 dev_err(&pdev->dev, "platform_get_resource failed\n"); 178 return -ENODEV; 179 } 180 181 data->reg_base = devm_ioremap(&pdev->dev, r->start, resource_size(r)); 182 if (data->reg_base == NULL) { 183 dev_err(&pdev->dev, "unable to remap MMIO\n"); 184 return -ENOMEM; 185 } 186 187 data->irq = platform_get_irq(pdev, 0); 188 if (data->irq < 0) { 189 dev_err(&pdev->dev, "platform_get_irq failed\n"); 190 return -ENODEV; 191 } 192 193 ret = devm_request_irq(&pdev->dev, data->irq, goldfish_battery_interrupt, 194 IRQF_SHARED, pdev->name, data); 195 if (ret) 196 return ret; 197 198 ret = power_supply_register(&pdev->dev, &data->ac); 199 if (ret) 200 return ret; 201 202 ret = power_supply_register(&pdev->dev, &data->battery); 203 if (ret) { 204 power_supply_unregister(&data->ac); 205 return ret; 206 } 207 208 platform_set_drvdata(pdev, data); 209 battery_data = data; 210 211 GOLDFISH_BATTERY_WRITE(data, BATTERY_INT_ENABLE, BATTERY_INT_MASK); 212 return 0; 213} 214 215static int goldfish_battery_remove(struct platform_device *pdev) 216{ 217 struct goldfish_battery_data *data = platform_get_drvdata(pdev); 218 219 power_supply_unregister(&data->battery); 220 power_supply_unregister(&data->ac); 221 battery_data = NULL; 222 return 0; 223} 224 225static struct platform_driver goldfish_battery_device = { 226 .probe = goldfish_battery_probe, 227 .remove = goldfish_battery_remove, 228 .driver = { 229 .name = "goldfish-battery" 230 } 231}; 232module_platform_driver(goldfish_battery_device); 233 234MODULE_AUTHOR("Mike Lockwood lockwood@android.com"); 235MODULE_LICENSE("GPL"); 236MODULE_DESCRIPTION("Battery driver for the Goldfish emulator"); 237