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 21 struct 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 35 enum { 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 62 static 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 86 static 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 140 static 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 155 static 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 161 static 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 182 static 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 190 static 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 198 static 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 return -ENODEV; 226 227 ret = devm_request_irq(&pdev->dev, data->irq, 228 goldfish_battery_interrupt, 229 IRQF_SHARED, pdev->name, data); 230 if (ret) 231 return ret; 232 233 psy_cfg.drv_data = data; 234 235 data->ac = power_supply_register(&pdev->dev, &ac_desc, &psy_cfg); 236 if (IS_ERR(data->ac)) 237 return PTR_ERR(data->ac); 238 239 data->battery = power_supply_register(&pdev->dev, &battery_desc, 240 &psy_cfg); 241 if (IS_ERR(data->battery)) { 242 power_supply_unregister(data->ac); 243 return PTR_ERR(data->battery); 244 } 245 246 platform_set_drvdata(pdev, data); 247 248 GOLDFISH_BATTERY_WRITE(data, BATTERY_INT_ENABLE, BATTERY_INT_MASK); 249 return 0; 250 } 251 252 static int goldfish_battery_remove(struct platform_device *pdev) 253 { 254 struct goldfish_battery_data *data = platform_get_drvdata(pdev); 255 256 power_supply_unregister(data->battery); 257 power_supply_unregister(data->ac); 258 return 0; 259 } 260 261 static const struct of_device_id goldfish_battery_of_match[] = { 262 { .compatible = "google,goldfish-battery", }, 263 {}, 264 }; 265 MODULE_DEVICE_TABLE(of, goldfish_battery_of_match); 266 267 #ifdef CONFIG_ACPI 268 static const struct acpi_device_id goldfish_battery_acpi_match[] = { 269 { "GFSH0001", 0 }, 270 { }, 271 }; 272 MODULE_DEVICE_TABLE(acpi, goldfish_battery_acpi_match); 273 #endif 274 275 static struct platform_driver goldfish_battery_device = { 276 .probe = goldfish_battery_probe, 277 .remove = goldfish_battery_remove, 278 .driver = { 279 .name = "goldfish-battery", 280 .of_match_table = goldfish_battery_of_match, 281 .acpi_match_table = ACPI_PTR(goldfish_battery_acpi_match), 282 } 283 }; 284 module_platform_driver(goldfish_battery_device); 285 286 MODULE_AUTHOR("Mike Lockwood lockwood@android.com"); 287 MODULE_LICENSE("GPL"); 288 MODULE_DESCRIPTION("Battery driver for the Goldfish emulator"); 289