278 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Power supply driver for the goldfish emulator4 *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 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 = devm_power_supply_register(&pdev->dev,236 &ac_desc,237 &psy_cfg);238 if (IS_ERR(data->ac))239 return PTR_ERR(data->ac);240 241 data->battery = devm_power_supply_register(&pdev->dev,242 &battery_desc,243 &psy_cfg);244 if (IS_ERR(data->battery))245 return PTR_ERR(data->battery);246 247 GOLDFISH_BATTERY_WRITE(data, BATTERY_INT_ENABLE, BATTERY_INT_MASK);248 return 0;249}250 251static const struct of_device_id goldfish_battery_of_match[] = {252 { .compatible = "google,goldfish-battery", },253 {},254};255MODULE_DEVICE_TABLE(of, goldfish_battery_of_match);256 257#ifdef CONFIG_ACPI258static const struct acpi_device_id goldfish_battery_acpi_match[] = {259 { "GFSH0001", 0 },260 { },261};262MODULE_DEVICE_TABLE(acpi, goldfish_battery_acpi_match);263#endif264 265static struct platform_driver goldfish_battery_device = {266 .probe = goldfish_battery_probe,267 .driver = {268 .name = "goldfish-battery",269 .of_match_table = goldfish_battery_of_match,270 .acpi_match_table = ACPI_PTR(goldfish_battery_acpi_match),271 }272};273module_platform_driver(goldfish_battery_device);274 275MODULE_AUTHOR("Mike Lockwood lockwood@android.com");276MODULE_LICENSE("GPL");277MODULE_DESCRIPTION("Battery driver for the Goldfish emulator");278