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 #include <linux/acpi.h> 28 29 struct goldfish_battery_data { 30 void __iomem *reg_base; 31 int irq; 32 spinlock_t lock; 33 34 struct power_supply *battery; 35 struct power_supply *ac; 36 }; 37 38 #define GOLDFISH_BATTERY_READ(data, addr) \ 39 (readl(data->reg_base + addr)) 40 #define GOLDFISH_BATTERY_WRITE(data, addr, x) \ 41 (writel(x, data->reg_base + addr)) 42 43 /* 44 * Temporary variable used between goldfish_battery_probe() and 45 * goldfish_battery_open(). 46 */ 47 static struct goldfish_battery_data *battery_data; 48 49 enum { 50 /* status register */ 51 BATTERY_INT_STATUS = 0x00, 52 /* set this to enable IRQ */ 53 BATTERY_INT_ENABLE = 0x04, 54 55 BATTERY_AC_ONLINE = 0x08, 56 BATTERY_STATUS = 0x0C, 57 BATTERY_HEALTH = 0x10, 58 BATTERY_PRESENT = 0x14, 59 BATTERY_CAPACITY = 0x18, 60 61 BATTERY_STATUS_CHANGED = 1U << 0, 62 AC_STATUS_CHANGED = 1U << 1, 63 BATTERY_INT_MASK = BATTERY_STATUS_CHANGED | AC_STATUS_CHANGED, 64 }; 65 66 67 static int goldfish_ac_get_property(struct power_supply *psy, 68 enum power_supply_property psp, 69 union power_supply_propval *val) 70 { 71 struct goldfish_battery_data *data = power_supply_get_drvdata(psy); 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 85 static 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 = power_supply_get_drvdata(psy); 90 int ret = 0; 91 92 switch (psp) { 93 case POWER_SUPPLY_PROP_STATUS: 94 val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_STATUS); 95 break; 96 case POWER_SUPPLY_PROP_HEALTH: 97 val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_HEALTH); 98 break; 99 case POWER_SUPPLY_PROP_PRESENT: 100 val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_PRESENT); 101 break; 102 case POWER_SUPPLY_PROP_TECHNOLOGY: 103 val->intval = POWER_SUPPLY_TECHNOLOGY_LION; 104 break; 105 case POWER_SUPPLY_PROP_CAPACITY: 106 val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_CAPACITY); 107 break; 108 default: 109 ret = -EINVAL; 110 break; 111 } 112 113 return ret; 114 } 115 116 static enum power_supply_property goldfish_battery_props[] = { 117 POWER_SUPPLY_PROP_STATUS, 118 POWER_SUPPLY_PROP_HEALTH, 119 POWER_SUPPLY_PROP_PRESENT, 120 POWER_SUPPLY_PROP_TECHNOLOGY, 121 POWER_SUPPLY_PROP_CAPACITY, 122 }; 123 124 static enum power_supply_property goldfish_ac_props[] = { 125 POWER_SUPPLY_PROP_ONLINE, 126 }; 127 128 static irqreturn_t goldfish_battery_interrupt(int irq, void *dev_id) 129 { 130 unsigned long irq_flags; 131 struct goldfish_battery_data *data = dev_id; 132 uint32_t status; 133 134 spin_lock_irqsave(&data->lock, irq_flags); 135 136 /* read status flags, which will clear the interrupt */ 137 status = GOLDFISH_BATTERY_READ(data, BATTERY_INT_STATUS); 138 status &= BATTERY_INT_MASK; 139 140 if (status & BATTERY_STATUS_CHANGED) 141 power_supply_changed(data->battery); 142 if (status & AC_STATUS_CHANGED) 143 power_supply_changed(data->ac); 144 145 spin_unlock_irqrestore(&data->lock, irq_flags); 146 return status ? IRQ_HANDLED : IRQ_NONE; 147 } 148 149 static const struct power_supply_desc battery_desc = { 150 .properties = goldfish_battery_props, 151 .num_properties = ARRAY_SIZE(goldfish_battery_props), 152 .get_property = goldfish_battery_get_property, 153 .name = "battery", 154 .type = POWER_SUPPLY_TYPE_BATTERY, 155 }; 156 157 static const struct power_supply_desc ac_desc = { 158 .properties = goldfish_ac_props, 159 .num_properties = ARRAY_SIZE(goldfish_ac_props), 160 .get_property = goldfish_ac_get_property, 161 .name = "ac", 162 .type = POWER_SUPPLY_TYPE_MAINS, 163 }; 164 165 static int goldfish_battery_probe(struct platform_device *pdev) 166 { 167 int ret; 168 struct resource *r; 169 struct goldfish_battery_data *data; 170 struct power_supply_config psy_cfg = {}; 171 172 data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); 173 if (data == NULL) 174 return -ENOMEM; 175 176 spin_lock_init(&data->lock); 177 178 r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 179 if (r == NULL) { 180 dev_err(&pdev->dev, "platform_get_resource failed\n"); 181 return -ENODEV; 182 } 183 184 data->reg_base = devm_ioremap(&pdev->dev, r->start, resource_size(r)); 185 if (data->reg_base == NULL) { 186 dev_err(&pdev->dev, "unable to remap MMIO\n"); 187 return -ENOMEM; 188 } 189 190 data->irq = platform_get_irq(pdev, 0); 191 if (data->irq < 0) { 192 dev_err(&pdev->dev, "platform_get_irq failed\n"); 193 return -ENODEV; 194 } 195 196 ret = devm_request_irq(&pdev->dev, data->irq, goldfish_battery_interrupt, 197 IRQF_SHARED, pdev->name, data); 198 if (ret) 199 return ret; 200 201 psy_cfg.drv_data = data; 202 203 data->ac = power_supply_register(&pdev->dev, &ac_desc, &psy_cfg); 204 if (IS_ERR(data->ac)) 205 return PTR_ERR(data->ac); 206 207 data->battery = power_supply_register(&pdev->dev, &battery_desc, 208 &psy_cfg); 209 if (IS_ERR(data->battery)) { 210 power_supply_unregister(data->ac); 211 return PTR_ERR(data->battery); 212 } 213 214 platform_set_drvdata(pdev, data); 215 battery_data = data; 216 217 GOLDFISH_BATTERY_WRITE(data, BATTERY_INT_ENABLE, BATTERY_INT_MASK); 218 return 0; 219 } 220 221 static int goldfish_battery_remove(struct platform_device *pdev) 222 { 223 struct goldfish_battery_data *data = platform_get_drvdata(pdev); 224 225 power_supply_unregister(data->battery); 226 power_supply_unregister(data->ac); 227 battery_data = NULL; 228 return 0; 229 } 230 231 static const struct of_device_id goldfish_battery_of_match[] = { 232 { .compatible = "google,goldfish-battery", }, 233 {}, 234 }; 235 MODULE_DEVICE_TABLE(of, goldfish_battery_of_match); 236 237 static const struct acpi_device_id goldfish_battery_acpi_match[] = { 238 { "GFSH0001", 0 }, 239 { }, 240 }; 241 MODULE_DEVICE_TABLE(acpi, goldfish_battery_acpi_match); 242 243 static struct platform_driver goldfish_battery_device = { 244 .probe = goldfish_battery_probe, 245 .remove = goldfish_battery_remove, 246 .driver = { 247 .name = "goldfish-battery", 248 .of_match_table = goldfish_battery_of_match, 249 .acpi_match_table = ACPI_PTR(goldfish_battery_acpi_match), 250 } 251 }; 252 module_platform_driver(goldfish_battery_device); 253 254 MODULE_AUTHOR("Mike Lockwood lockwood@android.com"); 255 MODULE_LICENSE("GPL"); 256 MODULE_DESCRIPTION("Battery driver for the Goldfish emulator"); 257