goldfish_battery.c (7745B)
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Power supply driver for the goldfish emulator 4 * 5 * Copyright (C) 2008 Google, Inc. 6 * Copyright (C) 2012 Intel, Inc. 7 * Copyright (C) 2013 Intel, Inc. 8 * Author: Mike Lockwood <lockwood@android.com> 9 */ 10 11#include <linux/module.h> 12#include <linux/err.h> 13#include <linux/platform_device.h> 14#include <linux/power_supply.h> 15#include <linux/types.h> 16#include <linux/pci.h> 17#include <linux/interrupt.h> 18#include <linux/io.h> 19#include <linux/acpi.h> 20 21struct goldfish_battery_data { 22 void __iomem *reg_base; 23 int irq; 24 spinlock_t lock; 25 26 struct power_supply *battery; 27 struct power_supply *ac; 28}; 29 30#define GOLDFISH_BATTERY_READ(data, addr) \ 31 (readl(data->reg_base + addr)) 32#define GOLDFISH_BATTERY_WRITE(data, addr, x) \ 33 (writel(x, data->reg_base + addr)) 34 35enum { 36 /* status register */ 37 BATTERY_INT_STATUS = 0x00, 38 /* set this to enable IRQ */ 39 BATTERY_INT_ENABLE = 0x04, 40 41 BATTERY_AC_ONLINE = 0x08, 42 BATTERY_STATUS = 0x0C, 43 BATTERY_HEALTH = 0x10, 44 BATTERY_PRESENT = 0x14, 45 BATTERY_CAPACITY = 0x18, 46 BATTERY_VOLTAGE = 0x1C, 47 BATTERY_TEMP = 0x20, 48 BATTERY_CHARGE_COUNTER = 0x24, 49 BATTERY_VOLTAGE_MAX = 0x28, 50 BATTERY_CURRENT_MAX = 0x2C, 51 BATTERY_CURRENT_NOW = 0x30, 52 BATTERY_CURRENT_AVG = 0x34, 53 BATTERY_CHARGE_FULL_UAH = 0x38, 54 BATTERY_CYCLE_COUNT = 0x40, 55 56 BATTERY_STATUS_CHANGED = 1U << 0, 57 AC_STATUS_CHANGED = 1U << 1, 58 BATTERY_INT_MASK = BATTERY_STATUS_CHANGED | AC_STATUS_CHANGED, 59}; 60 61 62static int goldfish_ac_get_property(struct power_supply *psy, 63 enum power_supply_property psp, 64 union power_supply_propval *val) 65{ 66 struct goldfish_battery_data *data = power_supply_get_drvdata(psy); 67 int ret = 0; 68 69 switch (psp) { 70 case POWER_SUPPLY_PROP_ONLINE: 71 val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_AC_ONLINE); 72 break; 73 case POWER_SUPPLY_PROP_VOLTAGE_MAX: 74 val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_VOLTAGE_MAX); 75 break; 76 case POWER_SUPPLY_PROP_CURRENT_MAX: 77 val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_CURRENT_MAX); 78 break; 79 default: 80 ret = -EINVAL; 81 break; 82 } 83 return ret; 84} 85 86static int goldfish_battery_get_property(struct power_supply *psy, 87 enum power_supply_property psp, 88 union power_supply_propval *val) 89{ 90 struct goldfish_battery_data *data = power_supply_get_drvdata(psy); 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 case POWER_SUPPLY_PROP_VOLTAGE_NOW: 110 val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_VOLTAGE); 111 break; 112 case POWER_SUPPLY_PROP_TEMP: 113 val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_TEMP); 114 break; 115 case POWER_SUPPLY_PROP_CHARGE_COUNTER: 116 val->intval = GOLDFISH_BATTERY_READ(data, 117 BATTERY_CHARGE_COUNTER); 118 break; 119 case POWER_SUPPLY_PROP_CURRENT_NOW: 120 val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_CURRENT_NOW); 121 break; 122 case POWER_SUPPLY_PROP_CURRENT_AVG: 123 val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_CURRENT_AVG); 124 break; 125 case POWER_SUPPLY_PROP_CHARGE_FULL: 126 val->intval = GOLDFISH_BATTERY_READ(data, 127 BATTERY_CHARGE_FULL_UAH); 128 break; 129 case POWER_SUPPLY_PROP_CYCLE_COUNT: 130 val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_CYCLE_COUNT); 131 break; 132 default: 133 ret = -EINVAL; 134 break; 135 } 136 137 return ret; 138} 139 140static enum power_supply_property goldfish_battery_props[] = { 141 POWER_SUPPLY_PROP_STATUS, 142 POWER_SUPPLY_PROP_HEALTH, 143 POWER_SUPPLY_PROP_PRESENT, 144 POWER_SUPPLY_PROP_TECHNOLOGY, 145 POWER_SUPPLY_PROP_CAPACITY, 146 POWER_SUPPLY_PROP_VOLTAGE_NOW, 147 POWER_SUPPLY_PROP_TEMP, 148 POWER_SUPPLY_PROP_CHARGE_COUNTER, 149 POWER_SUPPLY_PROP_CURRENT_NOW, 150 POWER_SUPPLY_PROP_CURRENT_AVG, 151 POWER_SUPPLY_PROP_CHARGE_FULL, 152 POWER_SUPPLY_PROP_CYCLE_COUNT, 153}; 154 155static enum power_supply_property goldfish_ac_props[] = { 156 POWER_SUPPLY_PROP_ONLINE, 157 POWER_SUPPLY_PROP_VOLTAGE_MAX, 158 POWER_SUPPLY_PROP_CURRENT_MAX, 159}; 160 161static irqreturn_t goldfish_battery_interrupt(int irq, void *dev_id) 162{ 163 unsigned long irq_flags; 164 struct goldfish_battery_data *data = dev_id; 165 uint32_t status; 166 167 spin_lock_irqsave(&data->lock, irq_flags); 168 169 /* read status flags, which will clear the interrupt */ 170 status = GOLDFISH_BATTERY_READ(data, BATTERY_INT_STATUS); 171 status &= BATTERY_INT_MASK; 172 173 if (status & BATTERY_STATUS_CHANGED) 174 power_supply_changed(data->battery); 175 if (status & AC_STATUS_CHANGED) 176 power_supply_changed(data->ac); 177 178 spin_unlock_irqrestore(&data->lock, irq_flags); 179 return status ? IRQ_HANDLED : IRQ_NONE; 180} 181 182static const struct power_supply_desc battery_desc = { 183 .properties = goldfish_battery_props, 184 .num_properties = ARRAY_SIZE(goldfish_battery_props), 185 .get_property = goldfish_battery_get_property, 186 .name = "battery", 187 .type = POWER_SUPPLY_TYPE_BATTERY, 188}; 189 190static const struct power_supply_desc ac_desc = { 191 .properties = goldfish_ac_props, 192 .num_properties = ARRAY_SIZE(goldfish_ac_props), 193 .get_property = goldfish_ac_get_property, 194 .name = "ac", 195 .type = POWER_SUPPLY_TYPE_MAINS, 196}; 197 198static int goldfish_battery_probe(struct platform_device *pdev) 199{ 200 int ret; 201 struct resource *r; 202 struct goldfish_battery_data *data; 203 struct power_supply_config psy_cfg = {}; 204 205 data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); 206 if (data == NULL) 207 return -ENOMEM; 208 209 spin_lock_init(&data->lock); 210 211 r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 212 if (r == NULL) { 213 dev_err(&pdev->dev, "platform_get_resource failed\n"); 214 return -ENODEV; 215 } 216 217 data->reg_base = devm_ioremap(&pdev->dev, r->start, resource_size(r)); 218 if (data->reg_base == NULL) { 219 dev_err(&pdev->dev, "unable to remap MMIO\n"); 220 return -ENOMEM; 221 } 222 223 data->irq = platform_get_irq(pdev, 0); 224 if (data->irq < 0) { 225 dev_err(&pdev->dev, "platform_get_irq failed\n"); 226 return -ENODEV; 227 } 228 229 ret = devm_request_irq(&pdev->dev, data->irq, 230 goldfish_battery_interrupt, 231 IRQF_SHARED, pdev->name, data); 232 if (ret) 233 return ret; 234 235 psy_cfg.drv_data = data; 236 237 data->ac = power_supply_register(&pdev->dev, &ac_desc, &psy_cfg); 238 if (IS_ERR(data->ac)) 239 return PTR_ERR(data->ac); 240 241 data->battery = power_supply_register(&pdev->dev, &battery_desc, 242 &psy_cfg); 243 if (IS_ERR(data->battery)) { 244 power_supply_unregister(data->ac); 245 return PTR_ERR(data->battery); 246 } 247 248 platform_set_drvdata(pdev, data); 249 250 GOLDFISH_BATTERY_WRITE(data, BATTERY_INT_ENABLE, BATTERY_INT_MASK); 251 return 0; 252} 253 254static int goldfish_battery_remove(struct platform_device *pdev) 255{ 256 struct goldfish_battery_data *data = platform_get_drvdata(pdev); 257 258 power_supply_unregister(data->battery); 259 power_supply_unregister(data->ac); 260 return 0; 261} 262 263static const struct of_device_id goldfish_battery_of_match[] = { 264 { .compatible = "google,goldfish-battery", }, 265 {}, 266}; 267MODULE_DEVICE_TABLE(of, goldfish_battery_of_match); 268 269#ifdef CONFIG_ACPI 270static const struct acpi_device_id goldfish_battery_acpi_match[] = { 271 { "GFSH0001", 0 }, 272 { }, 273}; 274MODULE_DEVICE_TABLE(acpi, goldfish_battery_acpi_match); 275#endif 276 277static struct platform_driver goldfish_battery_device = { 278 .probe = goldfish_battery_probe, 279 .remove = goldfish_battery_remove, 280 .driver = { 281 .name = "goldfish-battery", 282 .of_match_table = goldfish_battery_of_match, 283 .acpi_match_table = ACPI_PTR(goldfish_battery_acpi_match), 284 } 285}; 286module_platform_driver(goldfish_battery_device); 287 288MODULE_AUTHOR("Mike Lockwood lockwood@android.com"); 289MODULE_LICENSE("GPL"); 290MODULE_DESCRIPTION("Battery driver for the Goldfish emulator");