162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Power supply driver for the goldfish emulator 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2008 Google, Inc. 662306a36Sopenharmony_ci * Copyright (C) 2012 Intel, Inc. 762306a36Sopenharmony_ci * Copyright (C) 2013 Intel, Inc. 862306a36Sopenharmony_ci * Author: Mike Lockwood <lockwood@android.com> 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/err.h> 1362306a36Sopenharmony_ci#include <linux/platform_device.h> 1462306a36Sopenharmony_ci#include <linux/power_supply.h> 1562306a36Sopenharmony_ci#include <linux/types.h> 1662306a36Sopenharmony_ci#include <linux/pci.h> 1762306a36Sopenharmony_ci#include <linux/interrupt.h> 1862306a36Sopenharmony_ci#include <linux/io.h> 1962306a36Sopenharmony_ci#include <linux/acpi.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistruct goldfish_battery_data { 2262306a36Sopenharmony_ci void __iomem *reg_base; 2362306a36Sopenharmony_ci int irq; 2462306a36Sopenharmony_ci spinlock_t lock; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci struct power_supply *battery; 2762306a36Sopenharmony_ci struct power_supply *ac; 2862306a36Sopenharmony_ci}; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define GOLDFISH_BATTERY_READ(data, addr) \ 3162306a36Sopenharmony_ci (readl(data->reg_base + addr)) 3262306a36Sopenharmony_ci#define GOLDFISH_BATTERY_WRITE(data, addr, x) \ 3362306a36Sopenharmony_ci (writel(x, data->reg_base + addr)) 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cienum { 3662306a36Sopenharmony_ci /* status register */ 3762306a36Sopenharmony_ci BATTERY_INT_STATUS = 0x00, 3862306a36Sopenharmony_ci /* set this to enable IRQ */ 3962306a36Sopenharmony_ci BATTERY_INT_ENABLE = 0x04, 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci BATTERY_AC_ONLINE = 0x08, 4262306a36Sopenharmony_ci BATTERY_STATUS = 0x0C, 4362306a36Sopenharmony_ci BATTERY_HEALTH = 0x10, 4462306a36Sopenharmony_ci BATTERY_PRESENT = 0x14, 4562306a36Sopenharmony_ci BATTERY_CAPACITY = 0x18, 4662306a36Sopenharmony_ci BATTERY_VOLTAGE = 0x1C, 4762306a36Sopenharmony_ci BATTERY_TEMP = 0x20, 4862306a36Sopenharmony_ci BATTERY_CHARGE_COUNTER = 0x24, 4962306a36Sopenharmony_ci BATTERY_VOLTAGE_MAX = 0x28, 5062306a36Sopenharmony_ci BATTERY_CURRENT_MAX = 0x2C, 5162306a36Sopenharmony_ci BATTERY_CURRENT_NOW = 0x30, 5262306a36Sopenharmony_ci BATTERY_CURRENT_AVG = 0x34, 5362306a36Sopenharmony_ci BATTERY_CHARGE_FULL_UAH = 0x38, 5462306a36Sopenharmony_ci BATTERY_CYCLE_COUNT = 0x40, 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci BATTERY_STATUS_CHANGED = 1U << 0, 5762306a36Sopenharmony_ci AC_STATUS_CHANGED = 1U << 1, 5862306a36Sopenharmony_ci BATTERY_INT_MASK = BATTERY_STATUS_CHANGED | AC_STATUS_CHANGED, 5962306a36Sopenharmony_ci}; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistatic int goldfish_ac_get_property(struct power_supply *psy, 6362306a36Sopenharmony_ci enum power_supply_property psp, 6462306a36Sopenharmony_ci union power_supply_propval *val) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci struct goldfish_battery_data *data = power_supply_get_drvdata(psy); 6762306a36Sopenharmony_ci int ret = 0; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci switch (psp) { 7062306a36Sopenharmony_ci case POWER_SUPPLY_PROP_ONLINE: 7162306a36Sopenharmony_ci val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_AC_ONLINE); 7262306a36Sopenharmony_ci break; 7362306a36Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_MAX: 7462306a36Sopenharmony_ci val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_VOLTAGE_MAX); 7562306a36Sopenharmony_ci break; 7662306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CURRENT_MAX: 7762306a36Sopenharmony_ci val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_CURRENT_MAX); 7862306a36Sopenharmony_ci break; 7962306a36Sopenharmony_ci default: 8062306a36Sopenharmony_ci ret = -EINVAL; 8162306a36Sopenharmony_ci break; 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci return ret; 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic int goldfish_battery_get_property(struct power_supply *psy, 8762306a36Sopenharmony_ci enum power_supply_property psp, 8862306a36Sopenharmony_ci union power_supply_propval *val) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci struct goldfish_battery_data *data = power_supply_get_drvdata(psy); 9162306a36Sopenharmony_ci int ret = 0; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci switch (psp) { 9462306a36Sopenharmony_ci case POWER_SUPPLY_PROP_STATUS: 9562306a36Sopenharmony_ci val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_STATUS); 9662306a36Sopenharmony_ci break; 9762306a36Sopenharmony_ci case POWER_SUPPLY_PROP_HEALTH: 9862306a36Sopenharmony_ci val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_HEALTH); 9962306a36Sopenharmony_ci break; 10062306a36Sopenharmony_ci case POWER_SUPPLY_PROP_PRESENT: 10162306a36Sopenharmony_ci val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_PRESENT); 10262306a36Sopenharmony_ci break; 10362306a36Sopenharmony_ci case POWER_SUPPLY_PROP_TECHNOLOGY: 10462306a36Sopenharmony_ci val->intval = POWER_SUPPLY_TECHNOLOGY_LION; 10562306a36Sopenharmony_ci break; 10662306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CAPACITY: 10762306a36Sopenharmony_ci val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_CAPACITY); 10862306a36Sopenharmony_ci break; 10962306a36Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_NOW: 11062306a36Sopenharmony_ci val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_VOLTAGE); 11162306a36Sopenharmony_ci break; 11262306a36Sopenharmony_ci case POWER_SUPPLY_PROP_TEMP: 11362306a36Sopenharmony_ci val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_TEMP); 11462306a36Sopenharmony_ci break; 11562306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CHARGE_COUNTER: 11662306a36Sopenharmony_ci val->intval = GOLDFISH_BATTERY_READ(data, 11762306a36Sopenharmony_ci BATTERY_CHARGE_COUNTER); 11862306a36Sopenharmony_ci break; 11962306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CURRENT_NOW: 12062306a36Sopenharmony_ci val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_CURRENT_NOW); 12162306a36Sopenharmony_ci break; 12262306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CURRENT_AVG: 12362306a36Sopenharmony_ci val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_CURRENT_AVG); 12462306a36Sopenharmony_ci break; 12562306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CHARGE_FULL: 12662306a36Sopenharmony_ci val->intval = GOLDFISH_BATTERY_READ(data, 12762306a36Sopenharmony_ci BATTERY_CHARGE_FULL_UAH); 12862306a36Sopenharmony_ci break; 12962306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CYCLE_COUNT: 13062306a36Sopenharmony_ci val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_CYCLE_COUNT); 13162306a36Sopenharmony_ci break; 13262306a36Sopenharmony_ci default: 13362306a36Sopenharmony_ci ret = -EINVAL; 13462306a36Sopenharmony_ci break; 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci return ret; 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic enum power_supply_property goldfish_battery_props[] = { 14162306a36Sopenharmony_ci POWER_SUPPLY_PROP_STATUS, 14262306a36Sopenharmony_ci POWER_SUPPLY_PROP_HEALTH, 14362306a36Sopenharmony_ci POWER_SUPPLY_PROP_PRESENT, 14462306a36Sopenharmony_ci POWER_SUPPLY_PROP_TECHNOLOGY, 14562306a36Sopenharmony_ci POWER_SUPPLY_PROP_CAPACITY, 14662306a36Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_NOW, 14762306a36Sopenharmony_ci POWER_SUPPLY_PROP_TEMP, 14862306a36Sopenharmony_ci POWER_SUPPLY_PROP_CHARGE_COUNTER, 14962306a36Sopenharmony_ci POWER_SUPPLY_PROP_CURRENT_NOW, 15062306a36Sopenharmony_ci POWER_SUPPLY_PROP_CURRENT_AVG, 15162306a36Sopenharmony_ci POWER_SUPPLY_PROP_CHARGE_FULL, 15262306a36Sopenharmony_ci POWER_SUPPLY_PROP_CYCLE_COUNT, 15362306a36Sopenharmony_ci}; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic enum power_supply_property goldfish_ac_props[] = { 15662306a36Sopenharmony_ci POWER_SUPPLY_PROP_ONLINE, 15762306a36Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_MAX, 15862306a36Sopenharmony_ci POWER_SUPPLY_PROP_CURRENT_MAX, 15962306a36Sopenharmony_ci}; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistatic irqreturn_t goldfish_battery_interrupt(int irq, void *dev_id) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci unsigned long irq_flags; 16462306a36Sopenharmony_ci struct goldfish_battery_data *data = dev_id; 16562306a36Sopenharmony_ci uint32_t status; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci spin_lock_irqsave(&data->lock, irq_flags); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci /* read status flags, which will clear the interrupt */ 17062306a36Sopenharmony_ci status = GOLDFISH_BATTERY_READ(data, BATTERY_INT_STATUS); 17162306a36Sopenharmony_ci status &= BATTERY_INT_MASK; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci if (status & BATTERY_STATUS_CHANGED) 17462306a36Sopenharmony_ci power_supply_changed(data->battery); 17562306a36Sopenharmony_ci if (status & AC_STATUS_CHANGED) 17662306a36Sopenharmony_ci power_supply_changed(data->ac); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci spin_unlock_irqrestore(&data->lock, irq_flags); 17962306a36Sopenharmony_ci return status ? IRQ_HANDLED : IRQ_NONE; 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic const struct power_supply_desc battery_desc = { 18362306a36Sopenharmony_ci .properties = goldfish_battery_props, 18462306a36Sopenharmony_ci .num_properties = ARRAY_SIZE(goldfish_battery_props), 18562306a36Sopenharmony_ci .get_property = goldfish_battery_get_property, 18662306a36Sopenharmony_ci .name = "battery", 18762306a36Sopenharmony_ci .type = POWER_SUPPLY_TYPE_BATTERY, 18862306a36Sopenharmony_ci}; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic const struct power_supply_desc ac_desc = { 19162306a36Sopenharmony_ci .properties = goldfish_ac_props, 19262306a36Sopenharmony_ci .num_properties = ARRAY_SIZE(goldfish_ac_props), 19362306a36Sopenharmony_ci .get_property = goldfish_ac_get_property, 19462306a36Sopenharmony_ci .name = "ac", 19562306a36Sopenharmony_ci .type = POWER_SUPPLY_TYPE_MAINS, 19662306a36Sopenharmony_ci}; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_cistatic int goldfish_battery_probe(struct platform_device *pdev) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci int ret; 20162306a36Sopenharmony_ci struct resource *r; 20262306a36Sopenharmony_ci struct goldfish_battery_data *data; 20362306a36Sopenharmony_ci struct power_supply_config psy_cfg = {}; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); 20662306a36Sopenharmony_ci if (data == NULL) 20762306a36Sopenharmony_ci return -ENOMEM; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci spin_lock_init(&data->lock); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 21262306a36Sopenharmony_ci if (r == NULL) { 21362306a36Sopenharmony_ci dev_err(&pdev->dev, "platform_get_resource failed\n"); 21462306a36Sopenharmony_ci return -ENODEV; 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci data->reg_base = devm_ioremap(&pdev->dev, r->start, resource_size(r)); 21862306a36Sopenharmony_ci if (data->reg_base == NULL) { 21962306a36Sopenharmony_ci dev_err(&pdev->dev, "unable to remap MMIO\n"); 22062306a36Sopenharmony_ci return -ENOMEM; 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci data->irq = platform_get_irq(pdev, 0); 22462306a36Sopenharmony_ci if (data->irq < 0) 22562306a36Sopenharmony_ci return -ENODEV; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci ret = devm_request_irq(&pdev->dev, data->irq, 22862306a36Sopenharmony_ci goldfish_battery_interrupt, 22962306a36Sopenharmony_ci IRQF_SHARED, pdev->name, data); 23062306a36Sopenharmony_ci if (ret) 23162306a36Sopenharmony_ci return ret; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci psy_cfg.drv_data = data; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci data->ac = power_supply_register(&pdev->dev, &ac_desc, &psy_cfg); 23662306a36Sopenharmony_ci if (IS_ERR(data->ac)) 23762306a36Sopenharmony_ci return PTR_ERR(data->ac); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci data->battery = power_supply_register(&pdev->dev, &battery_desc, 24062306a36Sopenharmony_ci &psy_cfg); 24162306a36Sopenharmony_ci if (IS_ERR(data->battery)) { 24262306a36Sopenharmony_ci power_supply_unregister(data->ac); 24362306a36Sopenharmony_ci return PTR_ERR(data->battery); 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci platform_set_drvdata(pdev, data); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci GOLDFISH_BATTERY_WRITE(data, BATTERY_INT_ENABLE, BATTERY_INT_MASK); 24962306a36Sopenharmony_ci return 0; 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_cistatic int goldfish_battery_remove(struct platform_device *pdev) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci struct goldfish_battery_data *data = platform_get_drvdata(pdev); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci power_supply_unregister(data->battery); 25762306a36Sopenharmony_ci power_supply_unregister(data->ac); 25862306a36Sopenharmony_ci return 0; 25962306a36Sopenharmony_ci} 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_cistatic const struct of_device_id goldfish_battery_of_match[] = { 26262306a36Sopenharmony_ci { .compatible = "google,goldfish-battery", }, 26362306a36Sopenharmony_ci {}, 26462306a36Sopenharmony_ci}; 26562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, goldfish_battery_of_match); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci#ifdef CONFIG_ACPI 26862306a36Sopenharmony_cistatic const struct acpi_device_id goldfish_battery_acpi_match[] = { 26962306a36Sopenharmony_ci { "GFSH0001", 0 }, 27062306a36Sopenharmony_ci { }, 27162306a36Sopenharmony_ci}; 27262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, goldfish_battery_acpi_match); 27362306a36Sopenharmony_ci#endif 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_cistatic struct platform_driver goldfish_battery_device = { 27662306a36Sopenharmony_ci .probe = goldfish_battery_probe, 27762306a36Sopenharmony_ci .remove = goldfish_battery_remove, 27862306a36Sopenharmony_ci .driver = { 27962306a36Sopenharmony_ci .name = "goldfish-battery", 28062306a36Sopenharmony_ci .of_match_table = goldfish_battery_of_match, 28162306a36Sopenharmony_ci .acpi_match_table = ACPI_PTR(goldfish_battery_acpi_match), 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci}; 28462306a36Sopenharmony_cimodule_platform_driver(goldfish_battery_device); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ciMODULE_AUTHOR("Mike Lockwood lockwood@android.com"); 28762306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 28862306a36Sopenharmony_ciMODULE_DESCRIPTION("Battery driver for the Goldfish emulator"); 289